Bladeren bron

feat: 新版本系统通知

qindao 1 jaar geleden
bovenliggende
commit
f8c10c7f54

+ 2 - 1
.env.development

@@ -19,7 +19,8 @@ VITE_DROP_CONSOLE = true
 # VITE_PROXY=[["/skyeye-admin-api","http://192.168.14.68/skyeye-admin-api"],[],["/eye_api_bak","http://192.168.14.68/eye_api"],["/push_stream_host","http://192.168.14.68/push_stream_host"],["/skyeye-login","http://192.168.14.68/skyeye-login"],["/ws_api_bak","ws://192.168.14.68/ws_api_bak"]]
 # VITE_PROXY=[["/skyeye-admin-api","http://192.168.13.68/skyeye-admin-api"],[],["/eye_api_bak","http://192.168.13.68/eye_api"],["/push_stream_host","http://192.168.13.68/push_stream_host"],["/skyeye-login","http://192.168.13.68/skyeye-login"],["/ws_api_bak","ws://192.168.13.68/ws_api_bak"]]
 # 中建材 staff
-VITE_PROXY=[["/skyeye-admin-api","http://192.168.13.68:70/skyeye-admin-api"],["/eye_api_bak","http://192.168.13.68:70/eye_api"],["/push_stream_host","http://192.168.13.68:70/push_stream_host"],["/skyeye-login","http://192.168.13.68:70/skyeye-login"],["/ws_api_bak","ws://192.168.13.68:70/ws_api_bak"]]
+# VITE_PROXY=[["/skyeye-admin-api","http://192.168.13.68:70/skyeye-admin-api"],["/eye_api_bak","http://192.168.13.68:70/eye_api"],["/push_stream_host","http://192.168.13.68:70/push_stream_host"],["/skyeye-login","http://192.168.13.68:70/skyeye-login"],["/ws_api_bak","ws://192.168.13.68:70/ws_api_bak"]]
+VITE_PROXY=[["/skyeye-admin-api","http://192.168.13.68:70/skyeye-admin-api"],["/eye_api_bak","http://192.168.22.233:8800/eye_api"],["/push_stream_host","http://192.168.13.68:70/push_stream_host"],["/skyeye-login","http://192.168.13.68:70/skyeye-login"],["/ws_api_bak","ws://192.168.13.68:70/ws_api_bak"]]
 # VITE_PROXY=[["/skyeye-admin-api","http://192.168.13.68/skyeye-admin-api"],[],["/eye_api_bak","http://192.168.13.68/eye_api"],["/push_stream_host","http://192.168.13.68/push_stream_host"],["/skyeye-login","http://192.168.13.68/skyeye-login"],["/ws_api_bak","ws://192.168.13.68/ws_api_bak"],["/skyeye-file-upload","http://192.168.13.68/skyeye-file-upload"]]
 # VITE_PROXY=[["/skyeye-admin-api","http://192.168.22.163:8800/api"],[],["/eye_api_bak","http://192.168.22.163:8800/api"],["/push_stream_host","http://192.168.13.68/push_stream_host"],["/skyeye-login","http://192.168.13.68/skyeye-login"],["/ws_api_bak","ws://192.168.13.68/ws_api_bak"]]
 #VITE_PROXY=[["/skyeye-admin-api","http://192.168.22.121:8800/api"],["/eye_api_bak","http://192.168.22.121:8800/api"],["/push_stream_host","http://192.168.13.68/push_stream_host"],["/skyeye-login","http://192.168.13.68/skyeye-login"],["/ws_api_bak","ws://192.168.13.68/ws_api_bak"],["/skyeye-file-upload","http://192.168.13.68/skyeye-file-upload"]]

+ 10 - 9
src/api/message/sysnotion-config.ts

@@ -1,13 +1,14 @@
 import { http } from '@/utils/http/axios';
-export interface queryReportConfigListParams {
-  content?: string;
-  title?: string;
-  pushChannel?: number[];
-  recipientType?: number;
-  userGroupList?: number[];
-  customUserList?: number[];
-}
-export function queryReportConfigList(params: queryReportConfigListParams) {
+import { RuleFormProps } from '@/views/message/sysnotion-config/type';
+// export interface queryReportConfigListParams {
+//   content?: string;
+//   title?: string;
+//   pushChannel?: number[];
+//   recipientType?: number;
+//   userGroupList?: number[];
+//   customUserList?: number[];
+// }
+export function addSystemMessage(params: RuleFormProps) {
   return http.request({
     url: '/systemMessage/addSystemMessage',
     method: 'post',

+ 5 - 5
src/views/message/components/PushObject.vue

@@ -217,11 +217,11 @@
     userInfo.value = false;
   };
   const formatCustomUserList = (customList: customUserList[]): SelectedFilterPersonInfo[] => {
-    return customList.map((item) => ({
-      id: item.userId,
-      staffNo: item.userNumber,
-      nickname: item.userNickname,
-    }));
+     return customList.map((item) => ({
+        id: item.userId,
+        staffNo: item.userNumber,
+        nickname: item.userNickname,
+      }));
   };
   defineExpose({
     submitForm,

+ 22 - 0
src/views/message/constant.ts

@@ -3,6 +3,7 @@ export const typeName = [
   { value: 2, label: '平台访问统计' },
   { value: 3, label: '人员访问数据' },
 ];
+
 export const statisticTypeName = [
   { value: 1, label: '周报' },
   { value: 2, label: '月报' },
@@ -10,21 +11,42 @@ export const statisticTypeName = [
   { value: 4, label: '年报' },
   { value: 5, label: '自定义' },
 ];
+
 export const messageTypeName = [
   { value: 1, label: '报表消息' },
   { value: 2, label: '报警消息' },
   { value: 3, label: '系统消息' },
 ];
+
+// 推送渠道:1-蓝信 2-平台 4-APP主页 5-PC主页
 export const pushChannelName = [
+  { value: 4, label: 'APP主页' },
+  { value: 5, label: 'PC主页' },
   { value: 1, label: '蓝信' },
   { value: 2, label: '平台' },
 ];
+
 export const recipientTypeName = [
   { value: 1, label: '全员' },
   { value: 2, label: '分组' },
   { value: 3, label: '自定义' },
 ];
+
 export const statusName = [
   { value: 0, label: '已推送' },
   { value: 1, label: '未推送' },
 ];
+
+export const messageTypeOptions = [
+  {value: 1, label: 'Banner样式'},
+  {value: 2, label: '文本样式'},
+]
+
+export const contentTypeOptinos = [
+  {value: 1, label: '富文本'},
+  {value: 2, label: '链接跳转'},
+]
+
+export const title = '本系统进行了重大升级,请查看详细内容';
+
+export const content = '尊敬的用户:\n    我们计划于2024年9月5日进行平台系统升级,以提升服务性能和用户体验,升级期间,平台将暂时不可用,预计停机时间为4小时,从上午2:00至6:00。请您提前做好相关安排,以避免不便,感谢您的理解与支持。如有疑问,请联系客服支持团队。\n敬请留意。\n天眼团队';

+ 123 - 419
src/views/message/sysnotion-config/SysnotionConfig.vue

@@ -9,146 +9,21 @@
     </div>
     <div class="content">
       <div class="left">
-        <el-form
-          ref="ruleFormRef"
-          style="max-width: 600px"
-          label-width="auto"
-          :model="ruleForm"
-          :label-position="labelPosition"
-          class="el-form-outer"
-        >
-          <el-form-item
-            label="消息标题"
-            prop="title"
-            :rules="[{ required: true, message: '请输入消息标题' }]"
-          >
-            <el-input
-              v-model="ruleForm.title"
-              placeholder="请输入20字以内的消息标题"
-              maxlength="20"
-              show-word-limit
-              :disabled="isDisabled"
-            />
-          </el-form-item>
-          <el-form-item label="消息内容" prop="content" class="transprant">
-            <el-input
-              v-model="ruleForm.content"
-              placeholder="请输入500字以内的消息内容"
-              type="textarea"
-              :rows="5"
-              maxlength="500"
-              show-word-limit
-              :disabled="isDisabled"
-            />
-          </el-form-item>
-          <el-form-item
-            label="推送渠道"
-            prop="channel"
-            :rules="[{ required: true, message: '请选择推送渠道' }]"
-          >
-            <el-checkbox
-              v-model="ruleForm.channel"
-              v-for="item in pushChannelName"
-              :key="item.value"
-              :value="item.value"
-              :label="item.label"
-              :disabled="isDisabled"
-            />
-          </el-form-item>
-          <PushObject
-            ref="childFromRef"
-            :recipientType="ruleForm.object.recipientType"
-            :userGroupList="ruleForm.object.userGroupList"
-            :customUserList="ruleForm.object.customUserList"
-            :disabled="isDisabled"
-          />
-          <el-form-item label="操作人" prop="operator" class="transprant">
-            <el-input v-model="ruleForm.operator" :disabled="true" />
-          </el-form-item>
-        </el-form>
+        <!-- 基本配置 -->
+        <BasicInfo ref="basicInfoRef" :data-soure="ruleForm" :is-disabled="isDisabled"/>
+        <!-- 内容配置区域 -->
+        <contentConfig ref="contentConfigRef" :data-soure="ruleForm" :is-disabled="isDisabled"/>
+        <!-- 按钮区域 -->
         <div class="btns" v-if="!isDisabled">
           <div style="position: absolute; right: 0; bottom: 0">
-            <el-button @click="refresh()">重置</el-button>
-            <el-button type="primary" @click="submitForm()">确定</el-button>
+            <el-button @click="onCancel">取消</el-button>
+            <el-button @click="onSave">暂存</el-button>
+            <el-button type="primary" @click="submitForm">确定</el-button>
           </div>
         </div>
       </div>
-      <div class="right">
-        <div class="top-head">
-          实时效果
-          <el-tooltip
-            effect="dark"
-            content="实例样式仅供参考,最终展示以线上为准"
-            placement="top-start"
-          >
-            <img src="@/assets/icons/info.png" />
-          </el-tooltip>
-        </div>
-        <div class="tabs">
-          <el-tabs v-model="activeName">
-            <el-tab-pane label="平台侧" name="platform" class="platform">
-              <div class="tabs-content">
-                <div class="title">
-                  <div class="vertical"></div>
-                  <span>卡片页:</span>
-                </div>
-                <div class="card">
-                  <div class="card-title"
-                    >系统公告<a>去查看<img src="@/assets/icons/arrow.png" /></a
-                  ></div>
-                  <div class="card-content">
-                    <p style="font-size: 12px; color: #969799">2024年6月25日 16:45:06</p>
-                    <p style="margin-top: 5px; font-size: 13px; color: #646566">
-                      {{ ruleForm.title || title }}
-                    </p>
-                  </div>
-                </div>
-              </div>
-              <div class="tabs-content">
-                <div class="title">
-                  <div class="vertical"></div>
-                  <span>详情页:</span>
-                </div>
-                <div class="info">
-                  <div class="info-title"> {{ ruleForm.title || title }} </div>
-                  <div class="info-content">
-                    <span>{{ ruleForm.content || content }}</span>
-                  </div>
-                </div>
-              </div>
-            </el-tab-pane>
-            <el-tab-pane label="蓝信侧" name="lanxin" class="lanxin">
-              <div class="tabs-content">
-                <div class="title">
-                  <div class="vertical"></div>
-                  <span>卡片页:</span>
-                </div>
-                <div class="card">
-                  <div class="card-title">系统公告</div>
-                  <div class="card-content">
-                    <p style="margin-top: 5px; font-size: 13px; color: #646566">
-                      {{ ruleForm.title || title }}
-                    </p>
-                  </div>
-                  <img src="@/assets/icons/link_icon.png" />
-                </div>
-              </div>
-              <div class="tabs-content">
-                <div class="title">
-                  <div class="vertical"></div>
-                  <span>详情页:</span>
-                </div>
-                <div class="info">
-                  <div class="info-title"> {{ ruleForm.title || title }} </div>
-                  <div class="info-content">
-                    <span>{{ ruleForm.content || content }}</span>
-                  </div>
-                </div>
-              </div>
-            </el-tab-pane>
-          </el-tabs>
-        </div>
-      </div>
+      <!-- 实时预览 -->
+      <RightCard :rule-form="ruleForm"/>
     </div>
   </div>
 </template>
@@ -157,85 +32,41 @@
   import { useRoute, useRouter } from 'vue-router';
   import { ref, reactive, onMounted } from 'vue';
   import { storeToRefs } from 'pinia';
-  import { debounce } from 'lodash-es';
-  import { ElMessage } from 'element-plus';
+  import { ElMessage, ElMessageBox } from 'element-plus';
   import { useUserStore } from '@/store/modules/user';
-  import { pushChannelName } from '../constant';
-  import type { FormProps } from 'element-plus';
-  import PushObject from '../components/PushObject.vue';
-  import type { FormInstance } from 'element-plus';
   import {
-    queryReportConfigListParams,
-    queryReportConfigList,
+    addSystemMessage,
     confirmReportConfig,
     viewSystemMessage,
   } from '@/api/message/sysnotion-config';
-  import { ObjectFrom } from './type';
-  const title = ref<string>('本系统进行了重大升级,请查看详细内容');
-  const content = ref<string>(
-    '尊敬的用户:\n    我们计划于2024年9月5日进行平台系统升级,以提升服务性能和用户体验,升级期间,平台将暂时不可用,预计停机时间为4小时,从上午2:00至6:00。请您提前做好相关安排,以避免不便,感谢您的理解与支持。如有疑问,请联系客服支持团队。\n敬请留意。\n天眼团队',
-  );
+  import { RuleFormProps, MessageTypeEnum } from './type';
+  import BasicInfo from './compontents/BasicInfo.vue';
+  import ContentConfig from './compontents/ContentConfig.vue';
+  import RightCard from './compontents/RightCard.vue';
+
   const isDisabled = ref<boolean>(false);
-  const ruleFormRef = ref<FormInstance>();
-  const childFromRef = ref();
-  const validate = ref<boolean>();
   const useUser = useUserStore();
   const { info } = storeToRefs(useUser);
-  const labelPosition = ref<FormProps['labelPosition']>('left');
-  interface RuleForm {
-    title: string;
-    content: string;
-    channel: number[];
-    object: ObjectFrom;
-    operator: string;
-  }
-  const ruleForm = reactive<RuleForm>({
+  const basicInfoRef = ref<InstanceType<typeof BasicInfo>>()
+  const contentConfigRef = ref<InstanceType<typeof ContentConfig>>()
+  const ruleForm = reactive<RuleFormProps>({
+    messageType: MessageTypeEnum.BANNER,
+    bannerUrl: '',
     title: '',
+    pushChannel: [],
+    recipientType: {},
+    expirationTime: '',
+    introduction: '',
+    contentType: 1,
     content: '',
-    channel: [],
-    object: {},
     operator: info.value.nickname,
   });
-  const activeName = ref('platform');
-  const debounceEmit = debounce((params) => {
-    queryReportConfigList(params)
-      .then((res) => {
-        confirmReportConfig(res).then(() => {
-          ElMessage({
-            message: '下发成功!',
-            type: 'success',
-          });
-          router.back();
-        });
-      })
-      .catch((e) => console.error(e));
-  }, 500);
-  const submitForm = () => {
-    const childValue = childFromRef.value!.getChildValue();
-    childFromRef.value!.submitForm().then((res) => {
-      validate.value = res;
-    });
-    ruleFormRef.value!.validate((valid) => {
-      if (validate.value && valid) {
-        const params: queryReportConfigListParams = {
-          content: ruleForm.content,
-          title: ruleForm.title,
-          pushChannel: ruleForm.channel.map((item) => item),
-          recipientType: childValue.recipientType,
-          userGroupList: childValue.userGroupList,
-          customUserList: childValue.customUserList,
-        };
-        debounceEmit(params);
-      } else {
-        console.log('下发失败');
-      }
-    });
-  };
-  const refresh = () => {
-    if (!ruleFormRef.value) return;
-    ruleFormRef.value.resetFields();
-    childFromRef.value.refreshForm();
+
+   // 原始数据副本
+  let originalData: RuleFormProps = {
+    ...ruleForm,
   };
+
   const router = useRouter();
   const rollback = () => {
     router.back();
@@ -248,17 +79,103 @@
       viewSystemMessage(Number(sysId)).then((res) => {
         ruleForm.title = res.title;
         ruleForm.content = res.content ? res.content : ' ';
-        ruleForm.channel = res.pushChannel;
-        ruleForm.object.recipientType = res.recipientType;
+        ruleForm.pushChannel = res.pushChannel;
+        ruleForm.recipientType.recipientType = res.recipientType;
         if (res.recipientType === 2) {
-          ruleForm.object.userGroupList = res.userGroupList;
+          ruleForm.recipientType.userGroupList = res.userGroupList;
         }
         if (res.recipientType === 3) {
-          ruleForm.object.customUserList = res.customUserList;
+          ruleForm.recipientType.customUserList = res.customUserList;
         }
       });
     }
   });
+
+// 取消
+const onCancel = () => {
+   // 比对数据
+  const changes = compareData(ruleForm, originalData);
+  console.log('Object.keys(changes): ', Object.keys(changes));
+
+  // ElMessageBox.confirm(
+  //   '您对系统通知的额操作尚未保存,请问是否暂存?',
+  //   '提示',
+  //   {
+  //     confirmButtonText: '暂存',
+  //     cancelButtonText: '取消',
+  //     type: 'warning',
+  //   }
+  // )
+  //   .then(() => {
+  //     ElMessage({
+  //       type: 'success',
+  //       message: '暂存成功',
+  //     })
+  //   })
+  //   .catch(() => {
+  //     ElMessage({
+  //       type: 'info',
+  //       message: '取消暂存',
+  //     })
+  //   })
+}
+
+// 暂存
+const onSave = async() => {
+  // to save dada
+  const baseInfoData = await basicInfoRef.value?.validate();
+  const contentConfigData = contentConfigRef.value?.buildFormdata();
+  console.log('contentConfigData: ', contentConfigData);
+  console.log('baseInfoDara: ', baseInfoData);
+  
+  const {messageType, title,pushChannel, expirationTime, bannerUrl, recipientType} = baseInfoData
+  const params = {
+    ...contentConfigData,
+    messageType,
+    title,
+    pushChannel,
+    expirationTime,
+    bannerUrl,
+    recipientType: recipientType.recipientType,
+    customUserList: recipientType.customUserList,
+    userGroupList: recipientType.userGroupList,
+    contentUrl: '',
+    
+  }
+  delete params.operator
+  addSystemMessage(params).then((res) => {
+    if (res) {
+      ElMessage({
+        message: '暂存成功!',
+        type: 'success',
+      });
+    }
+});
+  }
+
+  const submitForm = () => {
+    confirmReportConfig(1).then(() => {
+      ElMessage({
+        message: '下发成功!',
+        type: 'success',
+      });
+      router.back();
+    });
+  };
+
+    // 比对方法
+  const compareData = (newData: RuleFormProps, oldData: RuleFormProps) => {
+    const diff: Partial<
+      Omit<RuleFormProps, 'content'>
+    > = {};
+    for (const key in newData) {
+      if (newData[key as keyof RuleFormProps] !== oldData[key as keyof RuleFormProps]) {
+        diff[key as keyof RuleFormProps] = newData[key as keyof RuleFormProps];
+      }
+    }
+    return diff;
+  };
+
 </script>
 
 <style lang="scss" scoped>
@@ -318,220 +235,7 @@
           position: relative;
         }
       }
-      .right {
-        width: 380px;
-        height: 100%;
-        padding: 20px 9px 0 20px;
-        .top-head {
-          display: flex;
-          gap: 7px;
-          align-items: center;
-          width: 100%;
-          height: 22px;
-          font-weight: 600;
-          font-size: 14px;
-          color: rgba(0, 0, 0, 0.85);
-          line-height: 22px;
-          img {
-            cursor: pointer;
-          }
-        }
-        .tabs {
-          margin-top: 14px;
-          overflow-y: auto;
-          :deep(.el-tabs__header) {
-            margin: 0 0 22px;
-          }
-          :deep(.is-top) {
-            font-weight: 550;
-            font-size: 14px;
-            line-height: 22px;
-          }
-          :deep(.el-tabs__nav-wrap::after) {
-            height: 0px;
-          }
-          .platform {
-            display: flex;
-            flex-direction: column;
-            gap: 24px;
-            .tabs-content {
-              width: 100%;
-              height: auto;
-              max-height: calc(100vh - 450px);
-              .title {
-                display: flex;
-                align-items: center;
-                margin-bottom: 12px;
-                gap: 18px;
-                width: 100%;
-                height: 31px;
-                .vertical {
-                  width: 4px;
-                  height: 12px;
-                  background: #1777ff;
-                  border-radius: 3px;
-                }
-                span {
-                  font-weight: 400;
-                  font-size: 14px;
-                  color: #303133;
-                  line-height: 20px;
-                }
-              }
-              .card {
-                width: 100%;
-                height: 124px;
-                padding: 10px 14px 0 12px;
-                background: #ffffff;
-                border-radius: 4px;
-                border: 1px solid rgba(0, 0, 0, 0.06);
-                .card-title {
-                  display: flex;
-                  justify-content: space-between;
-                  font-weight: 600;
-                  font-size: 15px;
-                  color: #646566;
-                  line-height: 20px;
-                  a {
-                    font-size: 14px;
-                    display: flex;
-                    gap: 8px;
-                    cursor: default;
-                  }
-                }
-                .card-content {
-                  margin-top: 15px;
-                  font-weight: 400;
-                  line-height: 20px;
-                }
-              }
-              .info {
-                width: 100%;
-                max-height: calc(100vh - 450px - 32px);
-                background: #ffffff;
-                border-radius: 4px;
-                border: 1px solid rgba(0, 0, 0, 0.06);
-                .info-title {
-                  display: flex;
-                  justify-content: center;
-                  align-items: center;
-                  width: 100%;
-                  height: 41px;
-                  font-weight: 600;
-                  font-size: 14px;
-                  color: rgba(0, 0, 0, 0.85);
-                  line-height: 22px;
-                  border-bottom: 1px solid rgba(0, 0, 0, 0.06);
-                }
-                .info-content {
-                  width: 100%;
-                  max-height: calc(100vh - 450px - 32px - 55px);
-                  overflow-y: auto;
-                  padding: 12px 7px 12px 12px;
-                  font-weight: 400;
-                  font-size: 14px;
-                  color: #646566;
-                  line-height: 22px;
-                  word-wrap: break-word;
-                  white-space: pre-wrap;
-                }
-              }
-            }
-          }
-          .lanxin {
-            display: flex;
-            flex-direction: column;
-            gap: 24px;
-            .tabs-content {
-              width: 100%;
-              height: auto;
-              max-height: calc(100vh - 400px);
-              .title {
-                display: flex;
-                align-items: center;
-                margin-bottom: 12px;
-                gap: 18px;
-                width: 100%;
-                height: 31px;
-                .vertical {
-                  width: 4px;
-                  height: 12px;
-                  background: #1777ff;
-                  border-radius: 3px;
-                }
-                span {
-                  font-weight: 400;
-                  font-size: 14px;
-                  color: #303133;
-                  line-height: 20px;
-                }
-              }
-              .card {
-                width: 100%;
-                height: 79px;
-                padding: 10px 14px 0 12px;
-                background: #ffffff;
-                border-radius: 4px;
-                border: 1px solid rgba(0, 0, 0, 0.06);
-                position: relative;
-                .card-title {
-                  display: flex;
-                  justify-content: space-between;
-                  font-weight: 600;
-                  font-size: 15px;
-                  color: #646566;
-                  line-height: 20px;
-                  a {
-                    display: flex;
-                    gap: 8px;
-                  }
-                }
-                .card-content {
-                  margin-top: 15px;
-                  font-weight: 400;
-                  line-height: 20px;
-                }
-                img {
-                  position: absolute;
-                  top: 10px;
-                  right: 17px;
-                }
-              }
-              .info {
-                width: 100%;
-                max-height: calc(100vh - 400px - 50px);
-                background: #ffffff;
-                border-radius: 4px;
-                border: 1px solid rgba(0, 0, 0, 0.06);
-                .info-title {
-                  display: flex;
-                  justify-content: center;
-                  align-items: center;
-                  width: 100%;
-                  height: 41px;
-                  font-weight: 600;
-                  font-size: 14px;
-                  color: rgba(0, 0, 0, 0.85);
-                  line-height: 22px;
-                  border-bottom: 1px solid rgba(0, 0, 0, 0.06);
-                }
-                .info-content {
-                  width: 100%;
-                  max-height: calc(100vh - 400px - 32px - 41px);
-                  overflow-y: auto;
-                  padding: 12px 7px 12px 12px;
-                  font-weight: 400;
-                  font-size: 14px;
-                  color: #646566;
-                  line-height: 22px;
-                  word-wrap: break-word;
-                  white-space: pre-wrap;
-                }
-              }
-            }
-          }
-        }
-      }
+      
     }
   }
 </style>

+ 191 - 0
src/views/message/sysnotion-config/compontents/BasicInfo.vue

@@ -0,0 +1,191 @@
+<!--
+ * @since: 2024-12-30
+ * BasicInfo.vue
+-->
+<template>
+  <CardLayout title="基础配置" :isShowWraning="false" :mandatory="false">
+    <el-form
+      ref="ruleFormRef"
+      style="max-width: 600px"
+      label-width="auto"
+      :model="ruleForm"
+      :rules="formRules"
+      :label-position="labelPosition"
+      class="el-form-outer"
+    >
+      <el-form-item
+        label="消息样式: "
+        prop="messageType"
+      >
+        <el-radio-group v-model="ruleForm.messageType" :disabled="isDisabled">
+        <el-radio :value="item.value"   v-for="item in messageTypeOptions" :key="item.value">{{ item.       label }}   </el-radio>
+  </el-radio-group>
+      </el-form-item>
+       <el-form-item
+        label="消息标题: "
+        prop="title"
+      >
+        <el-input
+          v-model="ruleForm.title"
+          placeholder="请输入20字以内的消息标题"
+          maxlength="20"
+          show-word-limit
+          :disabled="isDisabled"
+        />
+      </el-form-item>
+      <el-form-item
+        label="Banner图片: "
+        prop="bannerUrl"
+        v-if="ruleForm.messageType === MessageTypeEnum.BANNER"
+      >
+        <el-upload
+          class="avatar-uploader"
+          :action="actionUrl"
+          :show-file-list="false"
+          :on-success="handleAvatarSuccess"
+          :before-upload="beforeAvatarUpload"
+        >
+          <img v-if="ruleForm.bannerUrl" :src="ruleForm.bannerUrl" class="avatar" />
+          <el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon>
+        </el-upload>
+      </el-form-item>
+      <el-form-item
+        label="推送渠道: "
+        prop="pushChannel"
+      >
+        <el-checkbox
+          v-model="ruleForm.pushChannel"
+          v-for="item in pushChannelName"
+          :key="item.value"
+          :value="item.value"
+          :label="item.label"
+          :disabled="isDisabled"
+        />
+      </el-form-item>
+      <el-form-item
+        label="失效时间: "
+        prop="expirationTime"
+      >
+         <el-date-picker
+          v-model="ruleForm.expirationTime"
+          type="datetime"
+          placeholder="请选择失效时间"
+          format="YYYY/MM/DD hh:mm:ss"
+          value-format="YYYY-MM-DD hh:mm:ss"
+          :disabled="isDisabled"
+        />
+      </el-form-item>
+      <PushObject
+        ref="childFromRef"
+        :recipientType="ruleForm.recipientType.recipientType"
+        :userGroupList="ruleForm.recipientType.userGroupList"
+        :customUserList="ruleForm.recipientType.customUserList"
+        :disabled="isDisabled"
+      />
+    </el-form>
+  </CardLayout>
+</template>
+
+<script setup lang="ts">
+  import { ref, watch,computed } from 'vue';
+  import type { FormInstance, FormProps, FormRules, UploadProps } from 'element-plus';
+  import { ElMessage } from 'element-plus'
+  import { Plus } from '@element-plus/icons-vue'
+  import PushObject from '../../components/PushObject.vue';
+  import CardLayout from './CardLayout.vue';  
+  import { RuleFormProps, MessageTypeEnum } from '../type';
+  import { messageTypeOptions, pushChannelName } from '../../constant';
+  import urlJoin from 'url-join';
+  import { useGlobSetting } from '@/hooks/setting';
+  const { urlPrefix } = useGlobSetting();
+  interface Props {
+    dataSoure: RuleFormProps,
+    isDisabled: boolean,
+  }
+  const props = defineProps<Props>()
+  const labelPosition = ref<FormProps['labelPosition']>('left');
+  const childFromRef = ref<InstanceType<typeof PushObject>>();
+  const actionUrl = computed(() => {
+    return urlJoin(urlPrefix!, `/skyeye-file-upload/upload`);
+  });
+
+  /**
+   * 表单相关操作 
+   */
+  type Rule = Omit<RuleFormProps, 'introduction' | 'contentType' | 'content' | 'operator' >;
+  const formRules: FormRules<Rule> = {
+    messageType: [{ required: true, trigger: 'change', message: '请选择消息样式' }],
+    title: [{ required: true, trigger: 'change', message: '请输入消息标题' }],
+    pushChannel: [{ required: true, trigger: 'change', message: '请选择推送渠道' }],
+    recipientType: [{ required: true, trigger: 'change', message: '请选择推送对象' }],
+    bannerUrl: [{ required: true, trigger: 'change', message: '请选择banner图片' }],
+  };
+  const ruleForm = ref<Rule>({
+    title: '',
+    messageType: MessageTypeEnum.BANNER,
+    pushChannel: [],
+    recipientType: {
+      recipientType: 0,
+      userGroupList:[],
+      customUserList: [],
+    },
+    expirationTime: '',
+    bannerUrl:''
+  })
+
+  watch(
+    () => props.dataSoure,
+    (value) => {
+      if (value) {
+        ruleForm.value = {...value}
+      }
+    }, {
+    immediate: true
+  })
+
+  const ruleFormRef = ref<FormInstance>();
+  const isValidate = ref<boolean>();
+  const validate = async() => {
+    if (!ruleFormRef.value) return;
+    try {
+      const isSuccess = await ruleFormRef.value.validate();
+      if (isSuccess) {
+        const childValue = childFromRef.value!.getChildValue();
+        childFromRef.value!.submitForm().then((res) => {
+          isValidate.value = res
+        });
+        if (childValue) {
+          ruleForm.value.recipientType = { ...childValue }
+        } 
+        return ruleForm.value
+      } else {
+
+      }
+     
+    } catch (error) {
+      console.log('error: ', error);
+    }
+  };
+
+  const handleAvatarSuccess: UploadProps['onSuccess'] = (
+    response,
+    uploadFile
+  ) => {
+    ruleForm.value.bannerUrl = URL.createObjectURL(uploadFile.raw!)
+  }
+
+  const beforeAvatarUpload: UploadProps['beforeUpload'] = (rawFile) => {
+    if (rawFile.type !== 'image/jpeg') {
+      ElMessage.error('Avatar picture must be JPG format!')
+      return false
+    } else if (rawFile.size / 1024 / 1024 > 2) {
+      ElMessage.error('Avatar picture size can not exceed 2MB!')
+      return false
+    }
+    return true
+  }
+
+  defineExpose({validate})
+</script>
+
+<style scoped lang="scss"></style>

+ 92 - 0
src/views/message/sysnotion-config/compontents/CardLayout.vue

@@ -0,0 +1,92 @@
+<!--
+ * @since: 2024-12-30
+ * CardLayout.vue
+-->
+<template>
+  <div class="layout">
+    <section class="header" v-if="title">
+      <header class="card-title">
+        {{ title }}
+        <span class="mandatory" v-if="mandatory">*</span>
+      </header>
+      <slot name="actions"></slot>
+    </section>
+    <slot></slot>
+
+    <div class="flex items-center cursor-pointer new-action w-[70px]" v-if="!isViewMode && showAddNewButton" @click="$emit('handleAddNew')">
+      <el-button type="primary" :icon="Plus" circle size="small" />
+      <span style="margin-left: 10px; padding-top: 2px">新增</span>
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+  import { inject } from 'vue';
+  import { Plus } from '@element-plus/icons-vue';
+
+  interface LayoutSetting {
+    title?: string;
+    mandatory?: boolean;
+    showAddNewButton?: boolean;
+    isShowWraning?: boolean;
+  }
+
+  withDefaults(defineProps<LayoutSetting>(), {
+    mandatory: false,
+    showAddNewButton: false,
+    isShowWraning: false,
+  });
+
+  const isViewMode = inject('isViewMode', false);
+
+  defineEmits<{
+    (event: 'handleAddNew'): void;
+  }>();
+</script>
+
+<style scoped lang="scss">
+  .layout {
+    margin-bottom: 20px;
+    border-bottom: 4px solid #d3eafe;
+  }
+
+  .layout:last-child {
+    border: none;
+  }
+
+  .header {
+    margin-bottom: 20px;
+    padding-right: 20px;
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+
+    .card-title {
+      height: 20px;
+      line-height: 20px;
+      font-size: 16px;
+      font-weight: bold;
+
+      &::before {
+        content: ' ';
+        display: inline;
+        padding: 2px;
+        margin-right: 10px;
+        background-color: #409eff;
+        height: 80%;
+      }
+    }
+  }
+
+  .mandatory {
+    color: var(--el-color-danger);
+  }
+
+  .new-action {
+    margin-bottom: 20px;
+  }
+
+  .warningTip {
+    margin-left: 10px;
+  }
+</style>

+ 216 - 0
src/views/message/sysnotion-config/compontents/ContentConfig.vue

@@ -0,0 +1,216 @@
+<!--
+ * @since: 2024-12-30
+ * ContentConfig.vue
+-->
+<template>
+  <div class="container"></div>
+  <card-layout title="内容配置" :isShowWraning="false" :mandatory="false">
+     <el-form
+      ref="ruleFormRef"
+      label-width="auto"
+      :model="ruleForm"
+      :label-position="labelPosition"
+      class="el-form-outer"
+    >
+    <el-form-item label="简介内容" prop="introduction" class="transprant">
+        <el-input
+          v-model="ruleForm.introduction"
+          placeholder="请输入500字以内的简介内容"
+          type="textarea"
+          :rows="5"
+          maxlength="500"
+          show-word-limit
+          :disabled="isDisabled"
+        />
+      </el-form-item>
+   
+      <el-form-item label="详情内容" prop="contentType" class="transprant">
+        <el-radio-group v-model="ruleForm.contentType" :disabled="isDisabled">
+          <el-radio :value="item.value"   v-for="item in contentTypeOptinos" :key="item.value">{{ item.  label }}</el-radio>
+        </el-radio-group>
+      </el-form-item>
+       <el-form-item label=" " prop="contentUrl" class="transprant" v-if="ruleForm.contentType === ContentTypeEnum.LINK">
+        <el-input
+          v-model="ruleForm.contentUrl"
+          placeholder="请将链接地址粘贴到此处"
+          :disabled="isDisabled"
+        />
+      </el-form-item>
+      <el-form-item label="  " prop="content"  v-if="ruleForm.contentType === ContentTypeEnum.RICHTEXT">
+      <div style="border: 1px solid #ccc">
+        <Toolbar
+          style="border-bottom: 1px solid #ccc"
+          :editor="editorRef"
+          :defaultConfig="toolbarConfig"
+        />
+        <Editor
+          style="height: 500px; overflow-y: hidden;"
+          v-model="valueHtml"
+          :defaultConfig="editorConfig"
+          @onCreated="handleCreated"
+          @onChange="handleChange"
+          @onDestroyed="handleDestroyed"
+          @onFocus="handleFocus"
+          @onBlur="handleBlur"
+        />
+      </div>
+      </el-form-item>
+      <el-form-item label="操作人" prop="operator" class="transprant">
+        <el-input v-model="ruleForm.operator" :disabled="true" />
+      </el-form-item>
+    </el-form>
+  </card-layout>
+</template>
+
+<script setup lang="ts">
+  import { onBeforeUnmount, ref, shallowRef, onMounted, watch } from 'vue'
+  import type { FormProps } from 'element-plus';
+  import '@wangeditor/editor/dist/css/style.css' // 引入 css
+  import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
+  import { type IEditorConfig } from '@wangeditor/editor-for-vue'
+  import { RuleFormProps, ContentTypeEnum } from './../type';
+  import { contentTypeOptinos } from '../../constant';
+  import CardLayout from './CardLayout.vue';
+
+  interface Props {
+    dataSoure: RuleFormProps,
+    isDisabled: boolean
+  }
+  const props = defineProps<Props>()
+  const labelPosition = ref<FormProps['labelPosition']>('left');
+
+   /**
+   * 表单相关操作 
+   */
+  type Rule = Pick<RuleFormProps, 'introduction' | 'contentType' | 'content' | 'operator' | 'contentUrl'>;
+  const ruleForm = ref<Rule>({
+    introduction: '',
+    contentType: 1,
+    content: '',
+    operator: '',
+    contentUrl: ''
+  })
+
+  watch(() => props.dataSoure, (value) => {
+    if (value) {
+      const { introduction, contentType, content, operator, contentUrl } = value;
+      ruleForm.value = {
+        introduction,
+        contentType,
+        content,
+        operator,
+        contentUrl
+      }
+      console.log(' ruleForm.value: ',  ruleForm.value);
+    }
+  }, {
+  immediate: true
+  })
+
+  /********************* 富文本区域配置与方法 **********************/
+  // 编辑器实例,必须用 shallowRef
+  const editorRef = shallowRef()
+  // 内容 HTML
+  const valueHtml = ref()
+  // 排除工具栏选项
+  const toolbarConfig = {
+    excludeKeys: [
+      "insertTable", // 插入表格
+      "deleteTable", // 删除表格
+      'insertVideo',
+      'uploadVideo',
+      "codeBlock", // 代码块
+      "emotion", // 表情
+    ]
+  }   
+const editorConfig: Partial<IEditorConfig> = {
+  placeholder: '请输入内容...', MENU_CONF: {}}
+  editorConfig.MENU_CONF['uploadImage'] = {
+    // 上传图片的配置
+    server: '/skyeye-file-upload/upload', // form-data fieldName ,默认值 'wangeditor-uploaded-image'
+    fieldName: 'file',
+    // 单个文件的最大体积限制,默认为 2M
+    maxFileSize: 1 * 1024 * 1024, // 1M
+    // 最多可上传几个文件,默认为 100
+    maxNumberOfFiles: 10,
+    // 选择文件时的类型限制,默认为 ['image/*'] 。如不想限制,则设置为 []
+    // allowedFileTypes: ['image/*'],
+    // 自定义上传参数,例如传递验证的 token 等。参数会被添加到 formData 中,一起上传到服务端。
+    meta: {
+        type: 1,
+        targetId: 5
+    },
+    // 将 meta 拼接到 url 参数中,默认 false
+    metaWithUrl: false,
+    // 自定义增加 http  header
+    headers: {
+      Accept: 'application/json, text/plain, */*',
+      // ContentType: 'application/json;charset=UTF-8'
+    },
+    // 跨域是否传递 cookie ,默认为 false
+    withCredentials: true,
+    // 超时时间,默认为 10 秒
+    timeout: 10 * 1000, // 5 秒
+    // 上传之前触发
+    onBeforeUpload(file) {
+      console.log('file: ', file);
+      // file 选中的文件,格式如 { key: file }
+      return file
+      // 可以 return
+      // 1. return file 或者 new 一个 file ,接下来将上传
+      // 2. return false ,不上传这个 file
+    },
+    // 上传进度的回调函数
+    onProgress(progress: number) {
+      // progress 是 0-100 的数字
+      console.log('progress', progress)
+    },
+    // 单个文件上传成功之后
+    onSuccess(file: File, res: any) {
+      console.log(`${file.name} 上传成功`, res)
+    },
+    // 单个文件上传失败
+    onFailed(file: File, res: any) {
+      console.log(`${file.name} 上传失败`, res)
+    },
+    // 上传错误,或者触发 timeout 超时
+    onError(file: File, err: any, res: any) {
+      console.log(`${file.name} 上传出错`, err, res)
+    },
+  }
+
+  // 执行 createEditor 组件销毁时,也及时销毁编辑器
+  onBeforeUnmount(() => {
+    const editor = editorRef.value
+    if (editor == null) return
+    editor.destroy()
+  })
+
+  const handleCreated = (editor) => {
+    editorRef.value = editor
+  }
+  const handleChange = (editor) => {
+    // console.log('change:', editor.children)
+  }
+  const handleDestroyed = (editor) => {
+    console.log('destroyed', editor)
+  }
+  const handleFocus = (editor) => {
+    // console.log('focus', editor)
+  }
+  const handleBlur = (editor) => {
+    console.log('blur', editor)
+  }
+
+  const buildFormdata = () => {
+    return {
+      ...ruleForm.value,
+      content: valueHtml.value,
+    }
+  }
+
+  defineExpose({ buildFormdata })
+
+</script>
+
+<style scoped lang="scss"></style>

+ 311 - 0
src/views/message/sysnotion-config/compontents/RightCard.vue

@@ -0,0 +1,311 @@
+<!--
+ * @since: 2024-12-30
+ * RightCard.vue
+-->
+<template>
+   <div class="right">
+        <div class="top-head">
+          实时效果
+          <el-tooltip
+            effect="dark"
+            content="实例样式仅供参考,最终展示以线上为准"
+            placement="top-start"
+          >
+            <img src="@/assets/icons/info.png" />
+          </el-tooltip>
+        </div>
+        <div class="tabs">
+          <el-tabs v-model="activeName">
+            <el-tab-pane label="平台侧" name="platform" class="platform">
+              <div class="tabs-content">
+                <div class="title">
+                  <div class="vertical"></div>
+                  <span>卡片页:</span>
+                </div>
+                <div class="card">
+                  <div class="card-title"
+                    >系统公告<a>去查看<img src="@/assets/icons/arrow.png" /></a
+                  ></div>
+                  <div class="card-content">
+                    <p style="font-size: 12px; color: #969799">2024年6月25日 16:45:06</p>
+                    <p style="margin-top: 5px; font-size: 13px; color: #646566">
+                      {{ ruleForm.title || title }}
+                    </p>
+                  </div>
+                </div>
+              </div>
+              <div class="tabs-content">
+                <div class="title">
+                  <div class="vertical"></div>
+                  <span>详情页:</span>
+                </div>
+                <div class="info">
+                  <div class="info-title"> {{ ruleForm.title || title }} </div>
+                  <div class="info-content">
+                    <span>{{ ruleForm.introductio || content }}</span>
+                  </div>
+                </div>
+              </div>
+            </el-tab-pane>
+            <el-tab-pane label="蓝信侧" name="lanxin" class="lanxin">
+              <div class="tabs-content">
+                <div class="title">
+                  <div class="vertical"></div>
+                  <span>卡片页:</span>
+                </div>
+                <div class="card">
+                  <div class="card-title">系统公告</div>
+                  <div class="card-content">
+                    <p style="margin-top: 5px; font-size: 13px; color: #646566">
+                      {{ ruleForm.title || title }}
+                    </p>
+                  </div>
+                  <img src="@/assets/icons/link_icon.png" />
+                </div>
+              </div>
+              <div class="tabs-content">
+                <div class="title">
+                  <div class="vertical"></div>
+                  <span>详情页:</span>
+                </div>
+                <div class="info">
+                  <div class="info-title"> {{ ruleForm.title || title }} </div>
+                  <div class="info-content">
+                    <span>{{ ruleForm.introductio || content }}</span>
+                  </div>
+                </div>
+              </div>
+            </el-tab-pane>
+          </el-tabs>
+        </div>
+      </div>
+</template>
+
+<script setup lang="ts">
+import { ref, reactive, onMounted } from 'vue';
+import { RuleFormProps } from '../type';
+import { title, content } from '../../constant';
+
+const activeName = ref('platform');
+interface Props {
+  ruleForm: RuleFormProps
+}
+defineProps<Props>()
+</script>
+
+<style scoped lang="scss">
+.right {
+        width: 380px;
+        height: 100%;
+        padding: 20px 9px 0 20px;
+        .top-head {
+          display: flex;
+          gap: 7px;
+          align-items: center;
+          width: 100%;
+          height: 22px;
+          font-weight: 600;
+          font-size: 14px;
+          color: rgba(0, 0, 0, 0.85);
+          line-height: 22px;
+          img {
+            cursor: pointer;
+          }
+        }
+        .tabs {
+          margin-top: 14px;
+          overflow-y: auto;
+          :deep(.el-tabs__header) {
+            margin: 0 0 22px;
+          }
+          :deep(.is-top) {
+            font-weight: 550;
+            font-size: 14px;
+            line-height: 22px;
+          }
+          :deep(.el-tabs__nav-wrap::after) {
+            height: 0px;
+          }
+          .platform {
+            display: flex;
+            flex-direction: column;
+            gap: 24px;
+            .tabs-content {
+              width: 100%;
+              height: auto;
+              max-height: calc(100vh - 450px);
+              .title {
+                display: flex;
+                align-items: center;
+                margin-bottom: 12px;
+                gap: 18px;
+                width: 100%;
+                height: 31px;
+                .vertical {
+                  width: 4px;
+                  height: 12px;
+                  background: #1777ff;
+                  border-radius: 3px;
+                }
+                span {
+                  font-weight: 400;
+                  font-size: 14px;
+                  color: #303133;
+                  line-height: 20px;
+                }
+              }
+              .card {
+                width: 100%;
+                height: 124px;
+                padding: 10px 14px 0 12px;
+                background: #ffffff;
+                border-radius: 4px;
+                border: 1px solid rgba(0, 0, 0, 0.06);
+                .card-title {
+                  display: flex;
+                  justify-content: space-between;
+                  font-weight: 600;
+                  font-size: 15px;
+                  color: #646566;
+                  line-height: 20px;
+                  a {
+                    font-size: 14px;
+                    display: flex;
+                    gap: 8px;
+                    cursor: default;
+                  }
+                }
+                .card-content {
+                  margin-top: 15px;
+                  font-weight: 400;
+                  line-height: 20px;
+                }
+              }
+              .info {
+                width: 100%;
+                max-height: calc(100vh - 450px - 32px);
+                background: #ffffff;
+                border-radius: 4px;
+                border: 1px solid rgba(0, 0, 0, 0.06);
+                .info-title {
+                  display: flex;
+                  justify-content: center;
+                  align-items: center;
+                  width: 100%;
+                  height: 41px;
+                  font-weight: 600;
+                  font-size: 14px;
+                  color: rgba(0, 0, 0, 0.85);
+                  line-height: 22px;
+                  border-bottom: 1px solid rgba(0, 0, 0, 0.06);
+                }
+                .info-content {
+                  width: 100%;
+                  max-height: calc(100vh - 450px - 32px - 55px);
+                  overflow-y: auto;
+                  padding: 12px 7px 12px 12px;
+                  font-weight: 400;
+                  font-size: 14px;
+                  color: #646566;
+                  line-height: 22px;
+                  word-wrap: break-word;
+                  white-space: pre-wrap;
+                }
+              }
+            }
+          }
+          .lanxin {
+            display: flex;
+            flex-direction: column;
+            gap: 24px;
+            .tabs-content {
+              width: 100%;
+              height: auto;
+              max-height: calc(100vh - 400px);
+              .title {
+                display: flex;
+                align-items: center;
+                margin-bottom: 12px;
+                gap: 18px;
+                width: 100%;
+                height: 31px;
+                .vertical {
+                  width: 4px;
+                  height: 12px;
+                  background: #1777ff;
+                  border-radius: 3px;
+                }
+                span {
+                  font-weight: 400;
+                  font-size: 14px;
+                  color: #303133;
+                  line-height: 20px;
+                }
+              }
+              .card {
+                width: 100%;
+                height: 79px;
+                padding: 10px 14px 0 12px;
+                background: #ffffff;
+                border-radius: 4px;
+                border: 1px solid rgba(0, 0, 0, 0.06);
+                position: relative;
+                .card-title {
+                  display: flex;
+                  justify-content: space-between;
+                  font-weight: 600;
+                  font-size: 15px;
+                  color: #646566;
+                  line-height: 20px;
+                  a {
+                    display: flex;
+                    gap: 8px;
+                  }
+                }
+                .card-content {
+                  margin-top: 15px;
+                  font-weight: 400;
+                  line-height: 20px;
+                }
+                img {
+                  position: absolute;
+                  top: 10px;
+                  right: 17px;
+                }
+              }
+              .info {
+                width: 100%;
+                max-height: calc(100vh - 400px - 50px);
+                background: #ffffff;
+                border-radius: 4px;
+                border: 1px solid rgba(0, 0, 0, 0.06);
+                .info-title {
+                  display: flex;
+                  justify-content: center;
+                  align-items: center;
+                  width: 100%;
+                  height: 41px;
+                  font-weight: 600;
+                  font-size: 14px;
+                  color: rgba(0, 0, 0, 0.85);
+                  line-height: 22px;
+                  border-bottom: 1px solid rgba(0, 0, 0, 0.06);
+                }
+                .info-content {
+                  width: 100%;
+                  max-height: calc(100vh - 400px - 32px - 41px);
+                  overflow-y: auto;
+                  padding: 12px 7px 12px 12px;
+                  font-weight: 400;
+                  font-size: 14px;
+                  color: #646566;
+                  line-height: 22px;
+                  word-wrap: break-word;
+                  white-space: pre-wrap;
+                }
+              }
+            }
+          }
+        }
+      }
+</style>

+ 24 - 0
src/views/message/sysnotion-config/type.ts

@@ -18,4 +18,28 @@ export interface ObjectFrom {
     recipientType?: number;
     userGroupList?: GroupList[];
     customUserList?: UserList[];
+}
+
+export interface RuleFormProps {
+  messageType: number;
+  title: string;
+  pushChannel: number[];
+  recipientType: ObjectFrom;
+  expirationTime: string;
+  introduction?: string;
+  contentType?: number;
+  content?: string;
+  operator: string;
+  bannerUrl?: string;
+  contentUrl?: string;
+}
+
+export enum ContentTypeEnum {
+  RICHTEXT = 1,
+  LINK = 2,
+}
+
+export enum MessageTypeEnum {
+  BANNER = 1,
+  TEXT = 2
 }