Commit dec40da981ad0b97b0a2235f89ec97abb96bc449

Authored by 1400859700@qq.com
2 parents 37a80607 8484ee22

fix:所有字段验证失效问题

Showing 58 changed files with 2405 additions and 905 deletions

Too many changes to show.

To preserve performance only 58 of 99 files are displayed.

... ... @@ -6,12 +6,18 @@ VITE_PUBLIC_PATH = /
6 6
7 7 # Cross-domain proxy, you can configure multiple
8 8 # Please note that no line breaks
9   -# VITE_PROXY = [["/api","http://192.168.10.118:8080/api"],["/upload","http://192.168.10.116:3300/upload"]]
10   -VITE_PROXY = [["/api","http://101.133.234.90:8080/api"],["/upload","http://192.168.10.116:3300/upload"]]
11   -# VITE_PROXY=[["/api","https://vvbin.cn/test"]]
  9 +
  10 +# 本地
  11 +# VITE_PROXY = [["/api","http://localhost:8080/api"]]
  12 +
  13 +# 线上
  14 +VITE_PROXY = [["/api","http://101.133.234.90:8080/api"]]
  15 +
  16 +# 实时数据的ws地址
  17 +VITE_WEB_SOCKET = ws://101.133.234.90:8080/api/ws/plugins/telemetry?token=
12 18
13 19 # Delete console
14   -VITE_DROP_CONSOLE = false
  20 +VITE_DROP_CONSOLE = true
15 21
16 22 # Basic interface address SPA
17 23 VITE_GLOB_API_URL=/api
... ...
... ... @@ -10,26 +10,29 @@ VITE_DROP_CONSOLE = true
10 10 # Whether to enable gzip or brotli compression
11 11 # Optional: gzip | brotli | none
12 12 # If you need multiple forms, you can use `,` to separate
13   -VITE_BUILD_COMPRESS = 'none'
  13 +VITE_BUILD_COMPRESS = 'gzip'
14 14
15 15 # Whether to delete origin files when using compress, default false
16 16 VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE = false
17 17
18 18 # Basic interface address SPA
19   -VITE_GLOB_API_URL=/api
  19 +VITE_GLOB_API_URL=http://localhost:8080/api
20 20
21 21 # File upload address, optional
22 22 # It can be forwarded by nginx or write the actual address directly
23   -VITE_GLOB_UPLOAD_URL=/upload
  23 +VITE_GLOB_UPLOAD_URL=http://localhost:8080/upload
24 24
25 25 # Interface prefix
26   -VITE_GLOB_API_URL_PREFIX=/v1
  26 +VITE_GLOB_API_URL_PREFIX=/yt
27 27
28 28 # Whether to enable image compression
29   -VITE_USE_IMAGEMIN= true
  29 +VITE_USE_IMAGEMIN= false
30 30
31 31 # use pwa
32 32 VITE_USE_PWA = false
33 33
34 34 # Is it compatible with older browsers
35   -VITE_LEGACY = false
  35 +VITE_LEGACY = true
  36 +
  37 +# 实时数据的ws地址
  38 +VITE_WEB_SOCKET = ws://101.133.234.90:8080/api/ws/plugins/telemetry?token=
... ...
1   -import { BasicPageParams, BasicFetchResult } from '/@/api/model/baseModel';
2   -/**
3   - * @description: Request list interface parameters
4   - */
5   -export type MessageConfigParams = BasicPageParams & MessageParams;
6   -
7   -export type MessageParams = {
8   - status?: number;
9   - messageType?: string;
10   -};
11   -
12   -export interface MessageConfig {
13   - id: string;
14   - configName: string;
15   - messageType: string;
16   - platformType: string;
17   - config: ConfigParams;
18   - createTime: string;
19   - updateTime: string;
20   - status: number;
21   -}
22   -export interface ConfigParams {
23   - host: string;
24   - port: number;
25   - username: string;
26   - password: string;
27   - accessKeyId: string;
28   - accessKeySecret: string;
29   -}
30   -
31   -/**
32   - * @description: Request list return value
33   - */
34   -export type MessageConfigResultModel = BasicFetchResult<MessageConfig>;
35   -
36   -export type MessageConfigResult = MessageConfig;
... ... @@ -30,3 +30,14 @@ export const getDeviceDataKeys = (id: string) => {
30 30 }
31 31 );
32 32 };
  33 +// 获取设备状态,在线 or 离线时间
  34 +export const getDeviceActiveTime = (entityId: string) => {
  35 + return defHttp.get(
  36 + {
  37 + url: `/plugins/telemetry/DEVICE/${entityId}/values/attributes?keys=active`,
  38 + },
  39 + {
  40 + joinPrefix: false,
  41 + }
  42 + );
  43 +};
... ...
  1 +import { BasicPageParams } from './../model/baseModel';
  2 +import { defHttp } from '/@/utils/http/axios';
  3 +enum HomeEnum {
  4 + home = '/homepage/left/top',
  5 + TenantExpireTimeList = '/homepage/right',
  6 + EntitiesQueryFind = '/entitiesQuery/find',
  7 +}
  8 +
  9 +export const getHomeData = () => {
  10 + return defHttp.get({
  11 + url: HomeEnum.home,
  12 + });
  13 +};
  14 +
  15 +// 获取即将过期租户列表
  16 +export const getTenantExpireTimeList = (params: BasicPageParams) => {
  17 + return defHttp.get({
  18 + url: HomeEnum.TenantExpireTimeList,
  19 + params,
  20 + });
  21 +};
  22 +
  23 +// 获取entities实体ID
  24 +export const getEntitiesId = () => {
  25 + return defHttp.post(
  26 + {
  27 + url: HomeEnum.EntitiesQueryFind,
  28 + data: {
  29 + entityFilter: {
  30 + type: 'apiUsageState',
  31 + resolveMultiple: false,
  32 + },
  33 + pageLink: {
  34 + pageSize: 1,
  35 + page: 0,
  36 + sortOrder: {
  37 + key: {
  38 + type: 'ENTITY_FIELD',
  39 + key: 'createdTime',
  40 + },
  41 + direction: 'DESC',
  42 + },
  43 + },
  44 + entityFields: [
  45 + {
  46 + type: 'ENTITY_FIELD',
  47 + key: 'name',
  48 + },
  49 + {
  50 + type: 'ENTITY_FIELD',
  51 + key: 'label',
  52 + },
  53 + {
  54 + type: 'ENTITY_FIELD',
  55 + key: 'additionalInfo',
  56 + },
  57 + ],
  58 + },
  59 + },
  60 + {
  61 + joinPrefix: false,
  62 + }
  63 + );
  64 +};
... ...
1   -import { defHttp } from '/@/utils/http/axios';
2   -
3   -enum Api {
4   - // The address does not exist
5   - Error = '/error',
6   -}
7   -
8   -/**
9   - * @description: Trigger ajax error
10   - */
11   -
12   -export const fireErrorApi = () => defHttp.get({ url: Api.Error });
1   -import { defHttp } from '/@/utils/http/axios';
2   -
3   -enum Api {
4   - TREE_OPTIONS_LIST = '/tree/getDemoOptions',
5   -}
6   -
7   -/**
8   - * @description: Get sample options value
9   - */
10   -export const treeOptionsListApi = (params?: Recordable) =>
11   - defHttp.get<Recordable[]>({ url: Api.TREE_OPTIONS_LIST, params });
1   -import { UploadApiResult } from './model/uploadModel';
  1 +import { FileUploadResponse } from './model/uploadModel';
2 2 import { IPutPersonal } from './model/index';
3 3 import { defHttp } from '/@/utils/http/axios';
4   -import { UploadFileParams } from '/#/axios';
5 4
6 5 enum API {
7   - BaseUploadUrl = '/api/yt/oss/upload',
  6 + BaseUploadUrl = '/oss/upload',
8 7 PutPersonalUrl = '/user/center',
9 8 GetPersonalUrl = '/user/',
10 9 }
11   -/**
12   - * @description: Upload interface
13   - */
14   -export const uploadApi = (
15   - params: UploadFileParams,
16   - onUploadProgress: (progressEvent: ProgressEvent) => void
17   -) => {
18   - return defHttp.uploadFile<UploadApiResult>(
19   - {
20   - url: API.BaseUploadUrl,
21   - onUploadProgress,
22   - },
23   - params
24   - );
  10 +export const uploadApi = (file) => {
  11 + return defHttp.post<FileUploadResponse>({ url: API.BaseUploadUrl, params: file });
25 12 };
26 13
27 14 export const personalGet = (id: string) => {
... ...
1   -export interface UploadApiResult {
2   - message: string;
3   - code: number;
4   - url: string;
  1 +export interface FileUploadResponse {
  2 + fileName: string;
  3 + fileDownloadUri: string;
  4 + fileType: string;
  5 + size: number;
  6 + fileStaticUri: string;
5 7 }
... ...
... ... @@ -17,7 +17,7 @@ enum Api {
17 17 export const getMenuList = () => {
18 18 const userStore = useUserStore();
19 19 let url = Api.GetMenuList;
20   - if (userStore.getRoleList.find((v) => v == RoleEnum.ROLE_SYS_ADMIN)) {
  20 + if (userStore.getRoleList.find((v) => v == RoleEnum.SYS_ADMIN)) {
21 21 url = Api.SysAdminMenuList;
22 22 }
23 23 return defHttp.get<getMenuListResultModel>({ url });
... ...
... ... @@ -4,13 +4,14 @@
4 4 -->
5 5
6 6 <template>
7   - <div class="anticon" :class="getAppLogoClass" @click="goHome">
8   - <img :src="getLogo" />
  7 + <div class="application" :class="getAppLogoClass">
  8 + <img v-if="getLogo" :src="getLogo" />
  9 + <img v-else src="/src/assets/images/logo.png" />
9 10 <span
10 11 class="ml-2 md:opacity-100"
11 12 :class="getTitleClass"
12 13 v-show="showTitle"
13   - style="white-space: nowrap"
  14 + style="white-space: nowrap; font-size: small"
14 15 >
15 16 {{ getTitle }}
16 17 </span>
... ... @@ -19,10 +20,8 @@
19 20 <script lang="ts" setup>
20 21 import { computed, unref } from 'vue';
21 22 import { useGlobSetting } from '/@/hooks/setting';
22   - import { useGo } from '/@/hooks/web/usePage';
23 23 import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
24 24 import { useDesign } from '/@/hooks/web/useDesign';
25   - import { PageEnum } from '/@/enums/pageEnum';
26 25 import { useUserStore } from '/@/store/modules/user';
27 26 const props = defineProps({
28 27 /**
... ... @@ -43,7 +42,6 @@
43 42 const { getCollapsedShowTitle } = useMenuSetting();
44 43 const userStore = useUserStore();
45 44 const { title } = useGlobSetting();
46   - const go = useGo();
47 45
48 46 const getAppLogoClass = computed(() => [
49 47 prefixCls,
... ... @@ -56,12 +54,8 @@
56 54 'xs:opacity-0': !props.alwaysShowTitle,
57 55 },
58 56 ]);
59   -
60   - function goHome() {
61   - go(userStore.getUserInfo.homePath || PageEnum.BASE_HOME);
62   - }
63 57 const getLogo = computed(() => {
64   - return userStore.platInfo?.logo ?? '/src/assets/images/logo.png';
  58 + return userStore.platInfo?.logo;
65 59 });
66 60 const getTitle = computed(() => {
67 61 // 设置icon
... ... @@ -82,7 +76,6 @@
82 76 padding-left: 7px;
83 77 cursor: pointer;
84 78 transition: all 0.2s ease;
85   -
86 79 &.light {
87 80 border-bottom: 1px solid @border-color-base;
88 81 }
... ...
1 1 export enum RoleEnum {
2   - ROLE_SYS_ADMIN = 'SYS_ADMIN',
3   - ROLE_TENANT_ADMIN = 'TENANT_ADMIN',
4   - ROLE_NORMAL_USER = 'CUSTOMER_USER',
5   - ROLE_PLATFORM_ADMIN = 'PLATFORM_ADMIN',
  2 + SYS_ADMIN = 'SYS_ADMIN',
  3 + PLATFORM_ADMIN = 'PLATFORM_ADMIN',
  4 + TENANT_ADMIN = 'TENANT_ADMIN',
  5 + CUSTOMER_USER = 'CUSTOMER_USER',
  6 +}
  7 +
  8 +export function isAdmin(role: string) {
  9 + if (role === RoleEnum.SYS_ADMIN || role === RoleEnum.PLATFORM_ADMIN) {
  10 + return true;
  11 + } else if (role === RoleEnum.TENANT_ADMIN || role === RoleEnum.CUSTOMER_USER) {
  12 + return false;
  13 + }
6 14 }
... ...
  1 +import { ref, computed } from 'vue';
  2 +import { useMessage } from '/@/hooks/web/useMessage';
  3 +/**
  4 + *
  5 + * @param deleteFn 要删除的API接口方法
  6 + * @param handleSuccess 刷新表格的方法
  7 + * @returns {
  8 + * hasBatchDelete: 是否可以删除
  9 + * selectionOptions 表格复选框配置项
  10 + * handleDeleteOrBatchDelete 删除方法,适用单一删除和批量删除。参数为null为批量删除
  11 + * }
  12 + *
  13 + */
  14 +export interface selectionOptions {
  15 + rowKey: string;
  16 + clickToRowSelect: boolean;
  17 + rowSelection: {
  18 + onChange: (selectedRowKeys: string[]) => void;
  19 + type: 'radio' | 'checkbox';
  20 + };
  21 +}
  22 +export const useBatchDelete = (
  23 + deleteFn: (deleteIds: string[]) => Promise<void>,
  24 + handleSuccess: () => void
  25 +) => {
  26 + const { createMessage } = useMessage();
  27 + const selectedRowIds = ref<string[]>([]);
  28 + const hasBatchDelete = computed(() => selectedRowIds.value.length <= 0);
  29 + // 复选框事件
  30 + const onSelectRowChange = (selectedRowKeys: string[]) => {
  31 + selectedRowIds.value = selectedRowKeys;
  32 + };
  33 + const handleDeleteOrBatchDelete = async (record: Recordable | null) => {
  34 + if (record) {
  35 + try {
  36 + await deleteFn([record.id]);
  37 + createMessage.success('删除联系人成功');
  38 + handleSuccess();
  39 + } catch (e) {
  40 + createMessage.error('删除失败');
  41 + }
  42 + } else {
  43 + try {
  44 + await deleteFn(selectedRowIds.value);
  45 + createMessage.success('批量删除联系人成功');
  46 + selectedRowIds.value = [];
  47 + handleSuccess();
  48 + } catch (e) {
  49 + createMessage.info('删除失败');
  50 + }
  51 + }
  52 + };
  53 + const selectionOptions: selectionOptions = {
  54 + rowKey: 'id',
  55 + clickToRowSelect: false,
  56 + rowSelection: {
  57 + onChange: onSelectRowChange,
  58 + type: 'checkbox',
  59 + },
  60 + };
  61 + return { hasBatchDelete, selectionOptions, handleDeleteOrBatchDelete };
  62 +};
... ...
... ... @@ -78,6 +78,7 @@
78 78
79 79 .ant-badge {
80 80 font-size: 18px;
  81 + display: none;
81 82
82 83 .ant-badge-multiple-words {
83 84 padding: 0 4px;
... ...
1 1 import { FormSchema } from '/@/components/Table';
  2 +import { phoneRule, emailRule } from '/@/utils/rules';
2 3
3 4 export const formSchema: FormSchema[] = [
4 5 {
5   - field: 'nickName',
  6 + field: 'realName',
6 7 label: '用户昵称',
7 8 colProps: { span: 13 },
8 9 required: true,
... ... @@ -20,6 +21,7 @@ export const formSchema: FormSchema[] = [
20 21 componentProps: {
21 22 placeholder: '请输入手机号码',
22 23 },
  24 + rules: phoneRule,
23 25 },
24 26 {
25 27 field: 'email',
... ... @@ -30,5 +32,6 @@ export const formSchema: FormSchema[] = [
30 32 componentProps: {
31 33 placeholder: '请输入邮箱',
32 34 },
  35 + rules: emailRule,
33 36 },
34 37 ];
... ...
1 1 <template>
2 2 <BasicModal
3 3 :useWrapper="true"
4   - width="80vw"
  4 + width="82vw"
5 5 :height="compHeight"
6 6 v-bind="$attrs"
7 7 @register="registerModal"
... ... @@ -22,15 +22,34 @@
22 22 ><p style="font-size: 17px; margin-top: 7px; margin-left: 10px">个人信息</p></div
23 23 >
24 24 <div class="change-avatar" style="text-align: center">
25   - <div class="mb-2">头像</div>
26   - <CropperAvatar
27   - :uploadApi="uploadApi"
28   - :value="avatar"
29   - btnText="更换头像"
30   - :btnProps="{ preIcon: 'ant-design:cloud-upload-outlined' }"
31   - @change="updateAvatar"
32   - width="150"
33   - />
  25 + <div class="mb-2" style="font-weight: 700">个人头像</div>
  26 + <Upload
  27 + style="width: 20vw"
  28 + name="avatar"
  29 + list-type="picture-card"
  30 + class="avatar-uploader"
  31 + :show-upload-list="false"
  32 + :customRequest="customUploadqrcodePic"
  33 + :before-upload="beforeUploadqrcodePic"
  34 + >
  35 + <img
  36 + style="text-align: center; border-radius: 50%; width: 10vw; height: 15vh"
  37 + v-if="peresonalPic"
  38 + :src="peresonalPic"
  39 + alt="avatar"
  40 + />
  41 + <div v-else>
  42 + <div style="margin-top: 30px">
  43 + <PlusOutlined style="font-size: 30px" />
  44 + </div>
  45 + <div
  46 + class="ant-upload-text flex"
  47 + style="width: 280px; height: 130px; align-items: center"
  48 + >
  49 + 支持.PNG、.JPG、.SVG格式,建议尺寸为300px × 300px(及以上),大小不超过2M。</div
  50 + >
  51 + </div>
  52 + </Upload>
34 53 </div>
35 54 <Description
36 55 class="mt-8"
... ... @@ -66,13 +85,13 @@
66 85 import { BasicForm, useForm } from '/@/components/Form/index';
67 86 import { formSchema } from './config';
68 87 import { Description, DescItem, useDescription } from '/@/components/Description/index';
69   - import { CropperAvatar } from '/@/components/Cropper';
70   - import defaultImage from '/@/assets/images/logo.png';
71 88 import { uploadApi, personalPut } from '/@/api/personal/index';
72 89 import { useMessage } from '/@/hooks/web/useMessage';
73 90 import { USER_INFO_KEY } from '/@/enums/cacheEnum';
74 91 import { getAuthCache } from '/@/utils/auth';
75   - import { useUserStore } from '/@/store/modules/user';
  92 + import { Upload } from 'ant-design-vue';
  93 + import { PlusOutlined } from '@ant-design/icons-vue';
  94 + import type { FileItem } from '/@/components/Upload/src/typing';
76 95
77 96 const schema: DescItem[] = [
78 97 {
... ... @@ -102,68 +121,91 @@
102 121 ];
103 122 export default defineComponent({
104 123 name: 'index',
105   - components: { BasicModal, BasicForm, Description, CropperAvatar },
106   - setup() {
107   - const userStore = useUserStore();
  124 + components: { BasicModal, BasicForm, Description, Upload, PlusOutlined },
  125 + emits: ['refreshPersonl', 'register'],
  126 + setup(_, { emit }) {
108 127 const userInfo = getAuthCache(USER_INFO_KEY);
109 128 const { createMessage } = useMessage();
110 129 const getPersonalValue: any = ref({});
111 130 const getPersonalDetailValue: any = ref({});
112   - const avatarUrl: any = ref('');
  131 + const updatePersonalData: any = ref({});
113 132 const getBackendV: any = ref({});
114 133 const [registerDesc] = useDescription({
115 134 title: '个人详情',
116 135 schema: schema,
117 136 });
118 137
119   - const [registerModal, { closeModal }] = useModalInner();
120   - const [registerForm, { validate, resetFields }] = useForm({
  138 + const peresonalPic = ref();
  139 +
  140 + const customUploadqrcodePic = async ({ file }) => {
  141 + if (beforeUploadqrcodePic(file)) {
  142 + const formData = new FormData();
  143 + formData.append('file', file);
  144 + const response = await uploadApi(formData);
  145 + if (response.fileStaticUri) {
  146 + peresonalPic.value = response.fileStaticUri;
  147 + }
  148 + }
  149 + };
  150 +
  151 + const beforeUploadqrcodePic = (file: FileItem) => {
  152 + const isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png';
  153 + if (!isJpgOrPng) {
  154 + createMessage.error('只能上传图片文件!');
  155 + }
  156 + const isLt2M = (file.size as number) / 1024 / 1024 < 2;
  157 + if (!isLt2M) {
  158 + createMessage.error('图片大小不能超过2MB!');
  159 + }
  160 + return isJpgOrPng && isLt2M;
  161 + };
  162 +
  163 + const [registerForm, { validate, resetFields, setFieldsValue }] = useForm({
121 164 showActionButtonGroup: false,
122 165 schemas: formSchema,
123 166 });
124   - const avatar = computed(() => {
125   - const { avatar } = userStore.getUserInfo;
126   - return avatar || defaultImage;
  167 +
  168 + const [registerModal, { closeModal }] = useModalInner(async (data) => {
  169 + (peresonalPic.value = data.userInfo.avatar),
  170 + setFieldsValue({
  171 + realName: data.userInfo.realName,
  172 + phoneNumber: data.userInfo.phoneNumber,
  173 + email: data.userInfo.email,
  174 + });
  175 + if (data.userInfo) {
  176 + getPersonalDetailValue.value = data.userInfo;
  177 + }
  178 + if (Object.keys(updatePersonalData.value).length != 0) {
  179 + getPersonalDetailValue.value = updatePersonalData.value;
  180 + peresonalPic.value = updatePersonalData.value.avatar;
  181 + setFieldsValue({
  182 + realName: updatePersonalData.value.realName,
  183 + phoneNumber: updatePersonalData.value.phoneNumber,
  184 + email: updatePersonalData.value.email,
  185 + });
  186 + }
127 187 });
128 188 const handleSubmit = async () => {
129   - // console.log(userStore.getUserInfo);
130 189 const getUserInfo = await userInfo;
131   - // console.log(getUserInfo);
132 190 getPersonalValue.value = await validate();
133 191 getPersonalValue.value.id = getUserInfo.userId;
134 192 getPersonalValue.value.username = getBackendV.value.username;
135   - getPersonalValue.value.avatar = avatarUrl.value;
136   - await personalPut(getPersonalValue.value);
  193 + getPersonalValue.value.avatar = peresonalPic.value;
  194 + const data = await personalPut(getPersonalValue.value);
  195 + updatePersonalData.value = data;
137 196 createMessage.success('修改成功');
138 197 closeModal();
139 198 resetFields();
  199 + emit('refreshPersonl', updatePersonalData.value);
140 200 };
141   - const updateAvatar = async (v) => {
142   - avatarUrl.value = v.data.fileStaticUri;
143   - // console.log(avatarUrl.value);
144   - // await personalPut({ avatar: v });
145   - };
146   - const getPersonalDetail = async () => {
147   - try {
148   - const getUserInfo = await userInfo;
149   - getPersonalDetailValue.value = getUserInfo;
150   - } catch (e) {
151   - return e;
152   - }
153   - };
154   - getPersonalDetail();
155   -
156   - // onMounted(async () => {
157   - // getPersonalDetail();
158   - // });
159 201 const compHeight = computed(() => {
160 202 return 1000;
161 203 });
162 204 return {
163   - uploadApi,
  205 + peresonalPic,
  206 + beforeUploadqrcodePic,
  207 + customUploadqrcodePic,
164 208 compHeight,
165   - updateAvatar,
166   - avatar,
167 209 handleSubmit,
168 210 getPersonalDetailValue,
169 211 registerDesc,
... ... @@ -174,4 +216,22 @@
174 216 },
175 217 });
176 218 </script>
177   -<style lang="less"></style>
  219 +<style scoped lang="less">
  220 + .change-avatar {
  221 + /deep/ .ant-upload-select-picture-card {
  222 + display: inherit;
  223 + float: none;
  224 + width: 10vw;
  225 + height: 17vh;
  226 + margin-right: 8px;
  227 + margin-bottom: 8px;
  228 + text-align: center;
  229 + vertical-align: top;
  230 + background-color: #fafafa;
  231 + border: 1px dashed #d9d9d9;
  232 + border-radius: 50%;
  233 + cursor: pointer;
  234 + transition: border-color 0.3s ease;
  235 + }
  236 + }
  237 +</style>
... ...
1 1 <template>
2 2 <Dropdown placement="bottomLeft" :overlayClassName="`${prefixCls}-dropdown-overlay`">
3 3 <span :class="[prefixCls, `${prefixCls}--${theme}`]" class="flex">
4   - <img :class="`${prefixCls}__header`" :src="getUserInfo.avatar" />
  4 + <img
  5 + :class="`${prefixCls}__header`"
  6 + :src="refreshPersonlData.avatar ? refreshPersonlData.avatar : getUserInfo.avatar"
  7 + />
5 8 <span :class="`${prefixCls}__info hidden md:block`">
6 9 <span :class="`${prefixCls}__name `" class="truncate">
7   - {{ getUserInfo.realName }}
  10 + {{ refreshPersonlData.realName ? refreshPersonlData.realName : getUserInfo.realName }}
8 11 </span>
9 12 </span>
10 13 </span>
... ... @@ -12,12 +15,10 @@
12 15 <template #overlay>
13 16 <Menu @click="handleMenuClick">
14 17 <MenuItem
15   - key="doc"
16   - :text="t('layout.header.dropdownItemDoc')"
  18 + key="personal"
  19 + :text="t('layout.header.dropdownItemPersonal')"
17 20 icon="ion:document-text-outline"
18   - v-if="getShowDoc"
19 21 />
20   - <MenuDivider v-if="getShowDoc" />
21 22 <MenuItem
22 23 v-if="getUseLockPage"
23 24 key="lock"
... ... @@ -29,22 +30,21 @@
29 30 :text="t('layout.header.dropdownItemLoginOut')"
30 31 icon="ion:power-outline"
31 32 />
32   - <MenuItem
33   - key="personal"
34   - :text="t('layout.header.dropdownItemPersonal')"
35   - icon="ion:build-outlined"
36   - />
37 33 </Menu>
38 34 </template>
39 35 </Dropdown>
40 36 <LockAction @register="register" />
41   - <PersonalChild @register="registerPersonal" />
  37 + <PersonalChild
  38 + @refreshPersonl="refreshPersonlFunc"
  39 + ref="personalRef"
  40 + @register="registerPersonal"
  41 + />
42 42 </template>
43 43 <script lang="ts">
44 44 // components
45 45 import { Dropdown, Menu } from 'ant-design-vue';
46 46
47   - import { defineComponent, computed } from 'vue';
  47 + import { defineComponent, computed, getCurrentInstance, ref, reactive } from 'vue';
48 48
49 49 import { DOC_URL } from '/@/settings/siteSetting';
50 50
... ... @@ -53,13 +53,12 @@
53 53 import { useI18n } from '/@/hooks/web/useI18n';
54 54 import { useDesign } from '/@/hooks/web/useDesign';
55 55 import { useModal } from '/@/components/Modal';
56   -
57 56 import headerImg from '/@/assets/images/header.jpg';
58 57 import { propTypes } from '/@/utils/propTypes';
59 58 import { openWindow } from '/@/utils';
60   -
61 59 import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent';
62   -
  60 + import { USER_INFO_KEY } from '/@/enums/cacheEnum';
  61 + import { getAuthCache } from '/@/utils/auth';
63 62 type MenuEvent = 'logout' | 'doc' | 'lock' | 'personal';
64 63
65 64 export default defineComponent({
... ... @@ -68,7 +67,6 @@
68 67 Dropdown,
69 68 Menu,
70 69 MenuItem: createAsyncComponent(() => import('./DropMenuItem.vue')),
71   - MenuDivider: Menu.Divider,
72 70 LockAction: createAsyncComponent(() => import('../lock/LockModal.vue')),
73 71 PersonalChild: createAsyncComponent(() => import('../personal/index.vue')),
74 72 },
... ... @@ -76,14 +74,21 @@
76 74 theme: propTypes.oneOf(['dark', 'light']),
77 75 },
78 76 setup() {
  77 + const refreshPersonlData = reactive({
  78 + avatar: '',
  79 + realName: '',
  80 + });
  81 + const userInfo = getAuthCache(USER_INFO_KEY);
  82 + const { proxy } = getCurrentInstance();
  83 + const personalRef = ref(null);
79 84 const { prefixCls } = useDesign('header-user-dropdown');
80 85 const { t } = useI18n();
81 86 const { getShowDoc, getUseLockPage } = useHeaderSetting();
82 87 const userStore = useUserStore();
83 88
84 89 const getUserInfo = computed(() => {
85   - const { realName = '', avatar, desc } = userStore.getUserInfo || {};
86   - return { realName, avatar: avatar || headerImg, desc };
  90 + const { realName = '', avatar } = userStore.getUserInfo || {};
  91 + return { realName, avatar: avatar || headerImg };
87 92 });
88 93
89 94 const [register, { openModal }] = useModal();
... ... @@ -122,11 +127,21 @@
122 127
123 128 const openPersonalFunc = () => {
124 129 setTimeout(() => {
125   - openModalPersonal(true);
  130 + openModalPersonal(true, {
  131 + userInfo,
  132 + });
126 133 }, 10);
127 134 };
128 135
  136 + const refreshPersonlFunc = (v) => {
  137 + refreshPersonlData.avatar = v.avatar;
  138 + refreshPersonlData.realName = v.realName;
  139 + };
  140 +
129 141 return {
  142 + refreshPersonlData,
  143 + refreshPersonlFunc,
  144 + personalRef,
130 145 registerPersonal,
131 146 openPersonalFunc,
132 147 prefixCls,
... ...
... ... @@ -21,43 +21,18 @@
21 21 </transition>
22 22 </template>
23 23 </RouterView>
24   - <!-- <BasicModal
25   - @register="register"
26   - v-bind="$attrs"
27   - :mask="true"
28   - :showCancelBtn="false"
29   - :showOkBtn="false"
30   - :canFullscreen="false"
31   - :closable="false"
32   - :maskStyle="maskColor"
33   - :height="600"
34   - :width="1500"
35   - :maskClosable="false"
36   - title="请您修改初始密码"
37   - :helpMessage="['请您修改初始密码']"
38   - :keyboard="false"
39   - >
40   - <PasswordDialog />
41   - </BasicModal> -->
  24 +
42 25 <FrameLayout v-if="getCanEmbedIFramePage" />
43 26 </template>
44 27
45 28 <script lang="ts">
46   - import { computed, defineComponent, unref, onMounted } from 'vue';
47   - // import PasswordDialog from '/@/views/system/password/index.vue';
48   -
  29 + import { computed, defineComponent, unref } from 'vue';
49 30 import FrameLayout from '/@/layouts/iframe/index.vue';
50   -
51 31 import { useRootSetting } from '/@/hooks/setting/useRootSetting';
52   -
53 32 import { useTransitionSetting } from '/@/hooks/setting/useTransitionSetting';
54 33 import { useMultipleTabSetting } from '/@/hooks/setting/useMultipleTabSetting';
55 34 import { getTransitionName } from './transition';
56   -
57 35 import { useMultipleTabStore } from '/@/store/modules/multipleTab';
58   - // import { BasicModal, useModal } from '/@/components/Modal';
59   - // import { USER_INFO_KEY } from '/@/enums/cacheEnum';
60   - // import { getAuthCache } from '/@/utils/auth';
61 36 export default defineComponent({
62 37 name: 'PageLayout',
63 38 components: { FrameLayout },
... ... @@ -78,17 +53,6 @@
78 53 return tabStore.getCachedTabList;
79 54 });
80 55
81   - // const [register, { openModal }] = useModal();
82   - // const maskColor = ref({ backgroundColor: 'grey' });
83   - // const statusModel = ref(false);
84   - onMounted(() => {
85   - // const userInfo = getAuthCache(USER_INFO_KEY);
86   - // if (userInfo.needSetPwd == true) {
87   - // statusModel.value = true;
88   - // openModal(statusModel.value);
89   - // }
90   - });
91   -
92 56 return {
93 57 getTransitionName,
94 58 openCache,
... ... @@ -96,8 +60,6 @@
96 60 getBasicTransition,
97 61 getCaches,
98 62 getCanEmbedIFramePage,
99   - // register,
100   - // maskColor,
101 63 };
102 64 },
103 65 });
... ...
... ... @@ -12,7 +12,7 @@ export default {
12 12 networkExceptionMsg:
13 13 'Please check if your network connection is normal! The network is abnormal',
14 14
15   - errMsg401: 'no permission',
  15 + errMsg401: '',
16 16 errMsg403: 'not authorized',
17 17 errMsg404: 'the resource was not found!',
18 18 errMsg405: 'Network request error, request method not allowed!',
... ...
... ... @@ -10,8 +10,7 @@ export default {
10 10 apiRequestFailed: '请求出错,请稍候重试',
11 11 networkException: '网络异常',
12 12 networkExceptionMsg: '网络异常,请检查您的网络连接是否正常!',
13   -
14   - errMsg401: '没有权限!',
  13 + errMsg401: '',
15 14 errMsg403: '未授权',
16 15 errMsg404: '未找到该资源!',
17 16 errMsg405: '网络请求错误,请求方法未允许!',
... ...
... ... @@ -14,13 +14,7 @@ import { setupStore } from '/@/store';
14 14 import { setupGlobDirectives } from '/@/directives';
15 15 import { setupI18n } from '/@/locales/setupI18n';
16 16 import { registerGlobComp } from '/@/components/registerGlobComp';
17   -// Do not introduce on-demand in local development?
18   -// In the local development for introduce on-demand, the number of browser requests will increase by about 20%.
19   -// Which may slow down the browser refresh.
20   -// Therefore, all are introduced in local development, and only introduced on demand in the production environment
21   -if (import.meta.env.DEV) {
22   - import('ant-design-vue/dist/antd.less');
23   -}
  17 +import 'ant-design-vue/dist/antd.less';
24 18
25 19 async function bootstrap() {
26 20 const app = createApp(App);
... ...
... ... @@ -129,7 +129,6 @@ export const useUserStore = defineStore({
129 129 try {
130 130 const { goHome = true, mode, ...loginParams } = params;
131 131 const data = await loginApi(loginParams, mode);
132   - console.log(data);
133 132 return this.process(data, goHome);
134 133 } catch (error) {
135 134 return Promise.reject(error);
... ...
... ... @@ -10,7 +10,6 @@ import { SessionTimeoutProcessingEnum } from '/@/enums/appEnum';
10 10 const { createMessage, createErrorModal } = useMessage();
11 11 const error = createMessage.error!;
12 12 const stp = projectSetting.sessionTimeoutProcessing;
13   -
14 13 export function checkStatus(
15 14 status: number,
16 15 msg: string,
... ... @@ -19,7 +18,6 @@ export function checkStatus(
19 18 const { t } = useI18n();
20 19 const userStore = useUserStoreWithOut();
21 20 let errMessage = '';
22   -
23 21 switch (status) {
24 22 case 400:
25 23 errMessage = `${msg}`;
... ...
... ... @@ -10,58 +10,17 @@ import { useGlobSetting } from '/@/hooks/setting';
10 10 import { useMessage } from '/@/hooks/web/useMessage';
11 11 import { RequestEnum, ContentTypeEnum } from '/@/enums/httpEnum';
12 12 import { isString } from '/@/utils/is';
13   -import { getJwtToken, getAuthCache } from '/@/utils/auth';
  13 +import { getJwtToken } from '/@/utils/auth';
14 14 import { setObjToUrlParams, deepMerge } from '/@/utils';
15 15 import { useErrorLogStoreWithOut } from '/@/store/modules/errorLog';
16 16 import { useI18n } from '/@/hooks/web/useI18n';
17 17 import { joinTimestamp, formatRequestDate } from './helper';
18 18 import { PageEnum } from '/@/enums/pageEnum';
19   -import { REFRESH_TOKEN_KEY } from '/@/enums/cacheEnum';
20 19 import { router } from '/@/router';
21 20
22   -// import { useUserStore } from '/@/store/modules/user';
23   -// const userStore = useUserStore();
24   -// console.log(userStore.userInfo);
25   -
26   -// YUNTENG IOT__DEVELOPMENT__2.7.1__COMMON__LOCAL__KEY__
27   -
28   -function timestampToTime(timestamp) {
29   - const date = new Date(timestamp); //时间戳为10位需*1000,时间戳为13位的话不需乘1000
30   - const Y = date.getFullYear() + '-';
31   - const M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-';
32   - const D = date.getDate() + ' ';
33   - const h = date.getHours() + ':';
34   - const m = date.getMinutes() + ':';
35   - const s = date.getSeconds();
36   - return Y + M + D + h + m + s;
37   -}
38   -
39   -function convertToDate() {
40   - const date = new Date();
41   - const y = date.getFullYear();
42   - let m = date.getMonth() + 1;
43   - let d = date.getDate();
44   - let h = date.getHours();
45   - let min = date.getMinutes();
46   - let s = date.getSeconds();
47   - m = m < 10 ? '0' + m : m; //月小于10,加0
48   - d = d < 10 ? '0' + d : d; //day小于10,加0
49   - h = h < 10 ? '0' + h : h;
50   - min = min < 10 ? '0' + min : min;
51   - s = s < 10 ? '0' + s : s;
52   - return y + '-' + m + '-' + d + ' ' + h + ':' + min + ':' + s;
53   -}
54   -
55 21 const globSetting = useGlobSetting();
56 22 const urlPrefix = globSetting.urlPrefix;
57 23 const { createMessage, createErrorModal } = useMessage();
58   -const getJwtTokenInfo = getAuthCache(REFRESH_TOKEN_KEY);
59   -const getExiper = window.localStorage.getItem(
60   - 'UNDEFINED__DEVELOPMENT__2.7.1__COMMON__LOCAL__KEY__'
61   -);
62   -const getExiperValue = JSON.parse(getExiper);
63   -const expireTime = timestampToTime(getExiperValue.expire);
64   -const nowTime = convertToDate();
65 24
66 25 /**
67 26 * @description: 数据处理,方便区分多种处理方式
... ... @@ -162,32 +121,13 @@ const transform: AxiosTransform = {
162 121 const msg: string = response?.data?.msg ?? '';
163 122 const err: string = error?.toString?.() ?? '';
164 123 let errMessage = '';
165   -
166 124 try {
167   - if (response.data.code === '401' || response.data.msg === 'tenant has expired') {
168   - if (expireTime < nowTime) {
169   - // console.log('过期');
170   - createMessage.error('token已经过期,请退回登录');
171   - } else {
172   - // console.log('未过期');
173   - }
174   - if (getJwtTokenInfo) {
175   - if (PageEnum.BASE_LOGIN) {
176   - router.push(PageEnum.BASE_LOGIN);
177   - }
178   - }
179   - // router.beforeEach((to, from, next) => {
180   - // if (getJwtTokenInfo) {
181   - // if (to.path !== '/login') {
182   - // // doRefresh();
183   - // next({ path: '/' });
184   - // }
185   - // }
186   - // });
187   - } else {
188   - // doRefresh();
  125 + console.log(response.data);
  126 + if (response.data.status == '401' || response.data.message == '"Authentication failed"') {
  127 + window.localStorage.clear();
  128 + window.sessionStorage.clear();
  129 + router.push(PageEnum.BASE_HOME);
189 130 }
190   -
191 131 if (code === 'ECONNABORTED' && message.indexOf('timeout') !== -1) {
192 132 errMessage = t('sys.api.apiTimeoutMessage');
193 133 }
... ...
... ... @@ -60,3 +60,171 @@ export const EmailRegexp = /^[a-zA-Z0-9_.-]+@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*\.[a
60 60
61 61 // 手机号正则
62 62 export const PhoneRegexp = /^[1][3,4,5,6,7,8,9][0-9]{9}$/;
  63 +
  64 +//站内通知
  65 +export const NotificationTitleMaxLength: Rule[] = [
  66 + {
  67 + required: true,
  68 + validator: (_, value: string) => {
  69 + if (String(value).length > 50) {
  70 + return Promise.reject('标题长度不超过200字');
  71 + }
  72 + return Promise.resolve();
  73 + },
  74 + validateTrigger: 'blur',
  75 + },
  76 +];
  77 +
  78 +export const NotificationContentMaxLength: Rule[] = [
  79 + {
  80 + required: true,
  81 + validator: (_, value: string) => {
  82 + if (String(value).length > 50) {
  83 + return Promise.reject('内容长度不超过50字');
  84 + }
  85 + return Promise.resolve();
  86 + },
  87 + validateTrigger: 'blur',
  88 + },
  89 +];
  90 +
  91 +export const DeviceNameMaxLength: Rule[] = [
  92 + {
  93 + required: true,
  94 + validator: (_, value: string) => {
  95 + if (String(value).length > 30) {
  96 + return Promise.reject('设备名称长度不超过30字');
  97 + }
  98 + return Promise.resolve();
  99 + },
  100 + validateTrigger: 'blur',
  101 + },
  102 +];
  103 +
  104 +export const DeviceProfileIdMaxLength: Rule[] = [
  105 + {
  106 + required: true,
  107 + validator: (_, value: string) => {
  108 + if (String(value).length > 36) {
  109 + return Promise.reject('设备配置长度不超过36字');
  110 + }
  111 + return Promise.resolve();
  112 + },
  113 + validateTrigger: 'blur',
  114 + },
  115 +];
  116 +
  117 +export const DeviceOrgIdMaxLength: Rule[] = [
  118 + {
  119 + required: true,
  120 + validator: (_, value: string) => {
  121 + if (String(value).length > 36) {
  122 + return Promise.reject('组织长度不超过36字');
  123 + }
  124 + return Promise.resolve();
  125 + },
  126 + validateTrigger: 'blur',
  127 + },
  128 +];
  129 +
  130 +export const DeviceLabelMaxLength: Rule[] = [
  131 + {
  132 + required: true,
  133 + validator: (_, value: string) => {
  134 + if (String(value).length > 255) {
  135 + return Promise.reject('设备标签不超过255字');
  136 + }
  137 + return Promise.resolve();
  138 + },
  139 + validateTrigger: 'blur',
  140 + },
  141 +];
  142 +export const DeviceDescriptionlMaxLength: Rule[] = [
  143 + {
  144 + required: true,
  145 + validator: (_, value: string) => {
  146 + if (String(value).length > 500) {
  147 + return Promise.reject('备注不超过500字');
  148 + }
  149 + return Promise.resolve();
  150 + },
  151 + validateTrigger: 'blur',
  152 + },
  153 +];
  154 +export const DeviceIdMaxLength: Rule[] = [
  155 + {
  156 + required: true,
  157 + validator: (_, value: string) => {
  158 + if (String(value).length > 36) {
  159 + return Promise.reject('id不超过36字');
  160 + }
  161 + return Promise.resolve();
  162 + },
  163 + validateTrigger: 'blur',
  164 + },
  165 +];
  166 +
  167 +export const DeviceTenantIdMaxLength: Rule[] = [
  168 + {
  169 + required: true,
  170 + validator: (_, value: string) => {
  171 + if (String(value).length > 36) {
  172 + return Promise.reject('租户Code不超过36字');
  173 + }
  174 + return Promise.resolve();
  175 + },
  176 + validateTrigger: 'blur',
  177 + },
  178 +];
  179 +
  180 +export const DeviceTbDeviceIdMaxLength: Rule[] = [
  181 + {
  182 + required: true,
  183 + validator: (_, value: string) => {
  184 + if (String(value).length > 36) {
  185 + return Promise.reject('tbDeviceId不超过36字');
  186 + }
  187 + return Promise.resolve();
  188 + },
  189 + validateTrigger: 'blur',
  190 + },
  191 +];
  192 +
  193 +export const DeviceUserNameMaxLength: Rule[] = [
  194 + {
  195 + required: true,
  196 + validator: (_, value: string) => {
  197 + if (String(value).length > 50) {
  198 + return Promise.reject('用户名长度不超过50字');
  199 + }
  200 + return Promise.resolve();
  201 + },
  202 + validateTrigger: 'blur',
  203 + },
  204 +];
  205 +
  206 +export const DeviceQueryUserNameMaxLength: Rule[] = [
  207 + {
  208 + required: true,
  209 + validator: (_, value: string) => {
  210 + if (String(value).length > 50) {
  211 + return Promise.reject('设备名称长度不超过50字');
  212 + }
  213 + return Promise.resolve();
  214 + },
  215 + validateTrigger: 'blur',
  216 + },
  217 +];
  218 +
  219 +export const DeviceProfileQueryUserNameMaxLength: Rule[] = [
  220 + {
  221 + required: true,
  222 + validator: (_, value: string) => {
  223 + if (String(value).length > 255) {
  224 + return Promise.reject('配置名称长度不超过255字');
  225 + }
  226 + return Promise.resolve();
  227 + },
  228 + validateTrigger: 'blur',
  229 + },
  230 +];
... ...
... ... @@ -33,6 +33,23 @@ export const alarmSearchSchemas: FormSchema[] = [
33 33 label: '告警类型',
34 34 component: 'Input',
35 35 colProps: { span: 6 },
  36 + componentProps: {
  37 + maxLength: 255,
  38 + placeholder: '请输入告警类型',
  39 + },
  40 + dynamicRules: () => {
  41 + return [
  42 + {
  43 + required: false,
  44 + validator: (_, value) => {
  45 + if (String(value).length > 255) {
  46 + return Promise.reject('字数不超过255个字');
  47 + }
  48 + return Promise.resolve();
  49 + },
  50 + },
  51 + ];
  52 + },
36 53 },
37 54 {
38 55 field: 'endTime',
... ...
... ... @@ -51,7 +51,6 @@
51 51 severity: alarmLevel(data.severity),
52 52 status: statusType(data.status),
53 53 });
54   - console.log(data.status);
55 54 alarmStatus.value = data.status;
56 55 alarmId.value = data.id;
57 56 });
... ...
... ... @@ -41,6 +41,7 @@ export const formSchema: FormSchema[] = [
41 41 label: '',
42 42 component: 'Input',
43 43 componentProps: {
  44 + maxLength: 255,
44 45 placeholder: '请输入设备名称',
45 46 },
46 47 },
... ...
... ... @@ -34,8 +34,16 @@
34 34 :canFullscreen="false"
35 35 >
36 36 <BasicForm @register="registerForm" />
37   - <div ref="chartRef" :style="{ height: '600px', width }"></div>
  37 + <Alert
  38 + v-if="!isNull"
  39 + message="当前时间节点暂无历史数据"
  40 + description="请尝试选择其他时间段查询历史数据"
  41 + type="warning"
  42 + show-icon
  43 + />
  44 + <div v-show="isNull" ref="chartRef" :style="{ height: '600px', width }"></div>
38 45 </BasicModal>
  46 + <DeviceDetailDrawer @register="registerDetailDrawer" />
39 47 </div>
40 48 </template>
41 49 <script lang="ts">
... ... @@ -44,22 +52,30 @@
44 52 import { formSchema, columns } from './config.data';
45 53 import { BasicTable, useTable } from '/@/components/Table';
46 54 import { devicePage } from '/@/api/alarm/contact/alarmContact';
47   - import { Tag } from 'ant-design-vue';
  55 + import { Tag, Alert } from 'ant-design-vue';
48 56 import { DeviceState } from '/@/api/device/model/deviceModel';
49 57 import { BAI_DU_MAP_URL } from '/@/utils/fnUtils';
50 58 import { useModal, BasicModal } from '/@/components/Modal';
51 59 import { BasicForm, useForm } from '/@/components/Form';
52 60 import { schemas } from './config.data';
53 61 import { useECharts } from '/@/hooks/web/useECharts';
54   - import { getDeviceHistoryInfo, getDeviceDataKeys } from '/@/api/alarm/position';
  62 + import {
  63 + getDeviceHistoryInfo,
  64 + getDeviceDataKeys,
  65 + getDeviceActiveTime,
  66 + } from '/@/api/alarm/position';
  67 + import { useDrawer } from '/@/components/Drawer';
  68 + import DeviceDetailDrawer from '/@/views/device/manage/cpns/modal/DeviceDetailDrawer.vue';
55 69 import moment from 'moment';
56 70 export default defineComponent({
57 71 name: 'BaiduMap',
58 72 components: {
59 73 BasicTable,
60 74 Tag,
  75 + Alert,
61 76 BasicModal,
62 77 BasicForm,
  78 + DeviceDetailDrawer,
63 79 },
64 80 props: {
65 81 width: {
... ... @@ -74,7 +90,10 @@
74 90 setup() {
75 91 const wrapRef = ref<HTMLDivElement | null>(null);
76 92 const { toPromise } = useScript({ src: BAI_DU_MAP_URL });
77   - const entityId = ref('');
  93 + const [registerDetailDrawer, { openDrawer }] = useDrawer();
  94 +
  95 + let entityId = '';
  96 + let globalRecord: any = {};
78 97 async function initMap() {
79 98 await toPromise();
80 99 await nextTick();
... ... @@ -101,13 +120,14 @@
101 120 },
102 121 });
103 122 // 点击表格某一行触发
104   - const deviceRowClick = (record) => {
105   - entityId.value = record.tbDeviceId;
  123 + const deviceRowClick = async (record) => {
  124 + entityId = record.tbDeviceId;
  125 + globalRecord = record;
106 126 const BMap = (window as any).BMap;
107 127 const wrapEl = unref(wrapRef);
108 128 const map = new BMap.Map(wrapEl);
109 129 if (record.deviceInfo.address) {
110   - const { name, organizationDTO, updateTime, deviceState, deviceProfile } = record;
  130 + const { name, organizationDTO, deviceState, deviceProfile } = record;
111 131 const { longitude, latitude, address } = record.deviceInfo;
112 132 const point = new BMap.Point(longitude, latitude);
113 133 let options = {
... ... @@ -117,6 +137,10 @@
117 137 map.centerAndZoom(point, 15);
118 138 map.enableScrollWheelZoom(true);
119 139 // 创建信息窗口对象
  140 + const res = await getDeviceActiveTime(entityId);
  141 +
  142 + let { value: activeStatus, lastUpdateTs } = res[0];
  143 + lastUpdateTs = moment(lastUpdateTs).format('YYYY-MM-DD HH:mm:ss');
120 144 let infoWindow = new BMap.InfoWindow(
121 145 `
122 146 <div style="display:flex;justify-content:space-between; margin:20px 0px;">
... ... @@ -132,10 +156,9 @@
132 156 <div>所属组织:${organizationDTO.name}</div>
133 157 <div style="margin-top:6px;">接入协议:${deviceProfile.transportType}</div>
134 158 <div style="margin-top:6px;">设备位置:${address}</div>
135   - <div style="margin-top:6px;">下线时间:${updateTime}</div>
136   - <div style="display:flex;justify-content:space-between; margin-top:10px">
137   - <button style="color:#fff;background-color:#409eff;padding:4px; border-radius:4px;">设备信息</button>
138   - <button style="color:#fff;background-color:#409eff;padding:4px; border-radius:4px;">报警记录</button>
  159 + <div style="margin-top:6px;">${activeStatus ? '在' : '离'}线时间:${lastUpdateTs}</div>
  160 + <div style="display:flex;justify-content:end; margin-top:10px">
  161 + <button onclick="openDeviceInfoDrawer()" style="margin-right:10px;color:#fff;background-color:#409eff;padding:4px; border-radius:4px;">设备信息</button>
139 162 <button onclick="openHistoryModal()" style="color:#fff;background-color:#409eff;padding:4px; border-radius:4px;">历史数据</button>
140 163 </div>
141 164 `,
... ... @@ -185,13 +208,20 @@
185 208 endTs = Date.now();
186 209 // 发送请求
187 210 const res = await getDeviceHistoryInfo({
188   - entityId: entityId.value,
  211 + entityId,
189 212 keys: keys.join(),
190 213 startTs,
191 214 endTs,
192 215 interval,
193 216 agg,
194 217 });
  218 + // 判断对象是否为空
  219 + if (Object.keys(res).length === 0) {
  220 + isNull.value = false;
  221 + return;
  222 + } else {
  223 + isNull.value = true;
  224 + }
195 225 // 处理数据
196 226 for (const key in res) {
197 227 for (const item1 of res[key]) {
... ... @@ -250,24 +280,38 @@
250 280
251 281 const chartRef = ref<HTMLDivElement | null>(null);
252 282 const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>);
253   -
  283 + const isNull = ref(true);
  284 + // 设备信息
  285 + const openDeviceInfoDrawer = async () => {
  286 + const { id, tbDeviceId } = globalRecord;
  287 + openDrawer(true, {
  288 + id,
  289 + tbDeviceId,
  290 + });
  291 + };
254 292 const openHistoryModal = async () => {
255 293 openModal(true);
256   -
257 294 // 收集参数
258 295 const dataArray: any[] = [];
259 296 const startTs = Date.now() - 86400000; //最近一天
260 297 const endTs = Date.now();
261 298 // 发送请求
262   - keys = await getDeviceDataKeys(entityId.value);
  299 + keys = await getDeviceDataKeys(entityId);
263 300 const res = await getDeviceHistoryInfo({
264   - entityId: entityId.value,
  301 + entityId,
265 302 keys: keys.join(),
266 303 startTs,
267 304 endTs,
268 305 interval: 7200000, //间隔两小时
269 306 agg: 'AVG',
270 307 });
  308 + // 判断对象是否为空
  309 + if (Object.keys(res).length === 0) {
  310 + isNull.value = false;
  311 + return;
  312 + } else {
  313 + isNull.value = true;
  314 + }
271 315 // 处理数据
272 316 for (const key in res) {
273 317 for (const item1 of res[key]) {
... ... @@ -327,12 +371,14 @@
327 371 agg: 'AVG',
328 372 });
329 373 };
  374 +
330 375 const cancelHistoryModal = () => {
331 376 resetFields();
332 377 setOptions({});
333 378 };
334 379 onMounted(() => {
335 380 initMap();
  381 + (window as any).openDeviceInfoDrawer = openDeviceInfoDrawer;
336 382 (window as any).openHistoryModal = openHistoryModal;
337 383 });
338 384 return {
... ... @@ -343,7 +389,9 @@
343 389 registerModal,
344 390 registerForm,
345 391 chartRef,
  392 + isNull,
346 393 cancelHistoryModal,
  394 + registerDetailDrawer,
347 395 };
348 396 },
349 397 });
... ...
... ... @@ -56,6 +56,7 @@ export const searchFormSchema: FormSchema[] = [
56 56 component: 'Input',
57 57 colProps: { span: 6 },
58 58 componentProps: {
  59 + maxLength: 36,
59 60 placeholder: '请输入联系人姓名',
60 61 },
61 62 },
... ... @@ -70,6 +71,7 @@ export const formSchema: FormSchema[] = [
70 71 component: 'Input',
71 72 componentProps: {
72 73 placeholder: '请输入联系人姓名',
  74 + maxLength: 255,
73 75 },
74 76 },
75 77 {
... ... @@ -108,6 +110,7 @@ export const formSchema: FormSchema[] = [
108 110 component: 'Input',
109 111 componentProps: {
110 112 placeholder: '请输入微信号',
  113 + maxLength: 255,
111 114 },
112 115 },
113 116 {
... ... @@ -115,7 +118,8 @@ export const formSchema: FormSchema[] = [
115 118 label: '备注',
116 119 component: 'InputTextArea',
117 120 componentProps: {
118   - placeholder: '',
  121 + placeholder: '请输入备注',
  122 + maxLength: 255,
119 123 },
120 124 },
121 125 {
... ... @@ -123,5 +127,8 @@ export const formSchema: FormSchema[] = [
123 127 label: '',
124 128 component: 'Input',
125 129 show: false,
  130 + componentProps: {
  131 + maxLength: 36,
  132 + },
126 133 },
127 134 ];
... ...
  1 +<template>
  2 + 123
  3 + <div ref="chartRef" :style="{ height, width }"></div>
  4 +</template>
  5 +<script lang="ts" setup>
  6 + import { ref, Ref, withDefaults } from 'vue';
  7 + import { useECharts } from '/@/hooks/web/useECharts';
  8 +
  9 + interface Props {
  10 + width?: string;
  11 + height?: string;
  12 + }
  13 + withDefaults(defineProps<Props>(), {
  14 + width: '100%',
  15 + height: '280px',
  16 + });
  17 +
  18 + const chartRef = ref<HTMLDivElement | null>(null);
  19 + const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>);
  20 +</script>
... ...
1 1 <template>
2   - <div class="md:flex justify-between">
3   - <template v-for="(item, index) in growCardList" :key="item.title">
4   - <div
5   - class="growCardItem md:w-1/3 w-full !md:mt-0 !mt-4 bg-white"
6   - :class="index === 0 ? '!md:ml-0' : '!md:ml-4'"
  2 + <div class="md:flex">
  3 + <Card size="small" class="md:w-1/3 w-full !md:mt-0 !mt-4 !md:mr-4">
  4 + <div class="flex" style="height: 100px">
  5 + <div class="mr-4"
  6 + ><img src="/src/assets/images/device-count.png" style="width: 5rem; height: 5rem"
  7 + /></div>
  8 + <div class="flex-auto">
  9 + <div class="flex justify-between" style="align-items: center">
  10 + <div style="font-size: 1.625rem; color: #333">{{
  11 + growCardList?.deviceInfo?.sumCount
  12 + }}</div>
  13 + <img src="/src/assets/images/tip.png" style="width: 1.4rem; height: 1.4rem" />
  14 + </div>
  15 + <div> 设备数(个) </div>
  16 + <div class="flex mt-2">
  17 + <div class="flex mr-1" style="align-items: center; font-size: 0.75rem"
  18 + ><img src="/src/assets/images/online.png" class="mr-1" />
  19 + <span>在线{{ growCardList?.deviceInfo?.onLine }}</span>
  20 + </div>
  21 + <div class="flex mr-1" style="align-items: center; font-size: 0.75rem">
  22 + <img src="/src/assets/images/offline.png" class="mr-1" />
  23 + <span> 离线{{ growCardList?.deviceInfo?.offLine }} </span>
  24 + </div>
  25 + <div class="flex mr-1" style="align-items: center; font-size: 0.75rem">
  26 + <img src="/src/assets/images/inactive.png" class="mr-1" />
  27 + <span> 未激活{{ growCardList?.deviceInfo?.inActive }} </span>
  28 + </div>
  29 + </div>
  30 + </div>
  31 + </div>
  32 + <div class="ml-2 pt-4" style="border-top: 2px solid #f0f2f5">
  33 + 今日新增 {{ growCardList?.deviceInfo?.todayAdd }}</div
  34 + >
  35 + </Card>
  36 + <Card size="small" class="md:w-1/3 w-full !md:mt-0 !mt-4 !md:mr-4">
  37 + <div class="flex" style="height: 100px">
  38 + <div class="mr-4">
  39 + <img
  40 + v-if="!isAdmin(role)"
  41 + src="/src/assets/images/alarm-count.png"
  42 + style="width: 5rem; height: 5rem"
  43 + />
  44 + <img v-else src="/src/assets/images/zh.png" style="width: 5rem; height: 5rem" />
  45 + </div>
  46 + <div class="flex-auto">
  47 + <div class="flex justify-between" style="align-items: center">
  48 + <div v-if="!isAdmin(role)" style="font-size: 1.625rem; color: #333">{{
  49 + growCardList?.alarmInfo?.sumCount
  50 + }}</div>
  51 + <div style="font-size: 1.625rem; color: #333">{{
  52 + growCardList?.tenantInfo?.sumCount
  53 + }}</div>
  54 + <img src="/src/assets/images/tip.png" style="width: 1.4rem; height: 1.4rem" />
  55 + </div>
  56 + <div> {{ !isAdmin(role) ? `${currentMonth}月告警数(条)` : '租户总量(个)' }}</div>
  57 + </div>
  58 + </div>
  59 + <div v-if="!isAdmin(role)" class="ml-2 pt-4" style="border-top: 2px solid #f0f2f5">
  60 + 今日新增 {{ growCardList?.alarmInfo?.todayAdd }}</div
  61 + >
  62 + <div v-else class="ml-2 pt-4" style="border-top: 2px solid #f0f2f5">
  63 + 今日新增 {{ growCardList?.tenantInfo?.todayAdd }}</div
7 64 >
8   - <div
9   - class="
10   - growCardItem-top
11   - border border-solid border-t-0 border-r-0 border-l-0 border-b-1
12   - dark:border-#ccc
13   - light:border-#F2F2F5
14   - "
15   - >
16   - <img :src="item.imgUrl" style="width: 5rem; height: 5rem" />
17   - <div class="growCardItem-right">
18   - <div class="flex justify-between ml-3">
19   - <div style="font-size: 1.625rem; color: #333">{{ item.value }}</div>
20   - <img src="../../../../assets/images/tip.png" style="width: 1.4rem; height: 1.4rem" />
  65 + </Card>
  66 + <Card size="small" class="md:w-1/3 w-full !md:mt-0 !mt-4">
  67 + <div class="flex" style="height: 100px">
  68 + <div class="mr-4"
  69 + ><img
  70 + v-if="!isAdmin(role)"
  71 + src="/src/assets/images/msg-count.png"
  72 + style="width: 5rem; height: 5rem"
  73 + /><img v-else src="/src/assets/images/kf.png" style="width: 5rem; height: 5rem" />
  74 + </div>
  75 + <div v-if="!isAdmin(role)" style="display: flex; align-items: center">
  76 + <div>
  77 + <div class="flex" style="align-items: center">
  78 + {{ `${currentMonth}月数据点(条)` }}
  79 + <span style="font-size: 1.625rem; color: #333">
  80 + {{ growCardList?.messageInfo?.dataPointsCount }}</span
  81 + >
21 82 </div>
22   - <div class="ml-3">{{ item.title }}</div>
23   - <div class="ml-1.5 mt-3 flex flex-nowrap" style="width: 15rem" v-if="item.offLine">
24   - <div class="count">
25   - <img
26   - src="../../../../assets/images/online.png"
27   - style="width: 0.6rem; height: 0.6rem"
28   - class="mr-1"
29   - />
30   - 在线 {{ item.onLine }}
31   - </div>
32   - <div class="count">
33   - <img
34   - src="../../../../assets/images/offline.png"
35   - style="width: 0.6rem; height: 0.6rem"
36   - class="mr-1"
37   - />
38   - 离线 {{ item.offLine }}
39   - </div>
40   - <div class="count">
41   - <img
42   - src="../../../../assets/images/inactive.png"
43   - style="width: 0.6rem; height: 0.6rem"
44   - class="mr-1"
45   - />
46   - 未激活 {{ item.inactive }}
47   - </div>
  83 + <div class="flex" style="align-items: center">
  84 + {{ `${currentMonth}月消息量(条)` }}
  85 + <span style="font-size: 1.625rem; color: #333">
  86 + {{ growCardList?.messageInfo?.messageCount }}</span
  87 + >
48 88 </div>
49 89 </div>
50 90 </div>
51   - <div class="growCardItem-bottom"> 今日新增 {{ item.newDay }}</div>
  91 + <div class="flex-auto" v-else>
  92 + <div class="flex justify-between" style="align-items: center">
  93 + <div style="font-size: 1.625rem; color: #333">{{
  94 + growCardList?.customerInfo?.sumCount
  95 + }}</div>
  96 + <img src="/src/assets/images/tip.png" style="width: 1.4rem; height: 1.4rem" />
  97 + </div>
  98 + <div>客户总量(个)</div>
  99 + </div>
  100 + </div>
  101 + <div v-if="!isAdmin(role)" class="ml-2 pt-4" style="border-top: 2px solid #f0f2f5">
  102 + 今日新增
  103 + <span class="ml-8">数据点 ({{ growCardList?.messageInfo?.todayDataPointsAdd }})</span>
  104 + <span class="ml-8">消息量 ({{ growCardList?.messageInfo?.todayMessageAdd }})</span>
52 105 </div>
53   - </template>
  106 + <div v-else class="ml-2 pt-4" style="border-top: 2px solid #f0f2f5">
  107 + 今日新增 {{ growCardList?.customerInfo?.todayAdd }}</div
  108 + >
  109 + </Card>
54 110 </div>
55 111 </template>
56 112 <script lang="ts" setup>
57   - import { growCardList } from '../data';
58   -</script>
59   -
60   -<style scoped lang="less">
61   - .growCardItem {
62   - height: 11.187rem;
63   - color: #666;
64   - .growCardItem-top {
65   - display: flex;
66   - margin: 1.25rem;
67   - padding-bottom: 0.625rem;
68   - .growCardItem-right {
69   - width: 18.75rem;
70   - .count {
71   - display: flex;
72   - font-size: 0.75rem;
73   - align-items: center;
74   - margin-left: 0.5rem;
75   - }
76   - }
77   - }
78   - .growCardItem-bottom {
79   - margin-left: 1.25rem;
80   - }
  113 + import { ref, onMounted, computed } from 'vue';
  114 + import { Card } from 'ant-design-vue';
  115 + import { getHomeData } from '/@/api/dashboard';
  116 + import { isAdmin } from '/@/enums/roleEnum';
  117 + defineProps<{
  118 + role: string;
  119 + }>();
  120 + defineExpose({
  121 + isAdmin,
  122 + });
  123 + interface CardList {
  124 + deviceInfo: {
  125 + sumCount: number;
  126 + onLine: number;
  127 + offLine: number;
  128 + inActive: number;
  129 + todayAdd: number;
  130 + };
  131 + tenantInfo?: { sumCount: number; todayAdd: number };
  132 + customerInfo?: { sumCount: number; todayAdd: number };
  133 + alarmInfo?: {
  134 + sumCount: number;
  135 + todayAdd: number;
  136 + };
  137 + messageInfo?: {
  138 + dataPointsCount: number;
  139 + messageCount: number;
  140 + todayDataPointsAdd: number;
  141 + todayMessageAdd: number;
  142 + };
81 143 }
82   -</style>
  144 + const growCardList = ref<CardList>();
  145 + onMounted(async () => {
  146 + const res = await getHomeData();
  147 + growCardList.value = res;
  148 + });
  149 + // 获取当前月份0-11 +1
  150 + const currentMonth = computed(() => {
  151 + return new Date().getMonth() + 1;
  152 + });
  153 +</script>
... ...
1 1 <template>
2   - <Card title="帮助文档">
3   - <div>
4   - <template v-for="item in helpDoc" :key="item.title">
5   - <AnchorLink v-bind="item" />
6   - </template>
7   - </div>
8   - <Card
9   - :tab-list="tabListTitle"
10   - v-bind="$attrs"
11   - :active-tab-key="activeKey"
12   - :bordered="false"
13   - @tabChange="onTabChange"
14   - :bodyStyle="{ padding: 0 }"
15   - >
16   - <div v-if="activeKey === 'tab1'">
17   - <List item-layout="horizontal" :dataSource="dataSource">
18   - <template #renderItem="{ item }">
19   - <ListItem>
20   - <ListItemMeta>
21   - <template #avatar>
22   - <Avatar src="https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png" />
23   - </template>
24   - <template #description>
25   - <span
26   - @click="go('/stationnotification/mynotification')"
27   - class="cursor-pointer noticeTitle"
28   - >{{ item.sysNotice.title }}
29   - </span>
30   - </template>
31   - <template #title>
32   - <span>{{ item.user.realName }}</span>
  2 + <div>
  3 + <Card title="帮助文档" v-if="!isAdmin(role)">
  4 + <div>
  5 + <template v-for="item in helpDoc" :key="item.title">
  6 + <AnchorLink v-bind="item" />
  7 + </template>
  8 + </div>
  9 + <Card
  10 + v-if="!isAdmin(role)"
  11 + :tab-list="tabListTitle"
  12 + v-bind="$attrs"
  13 + :active-tab-key="activeKey"
  14 + :bordered="false"
  15 + @tabChange="onTabChange"
  16 + :bodyStyle="{ padding: 0 }"
  17 + >
  18 + <div v-if="activeKey === 'tab1'">
  19 + <List item-layout="horizontal" :dataSource="dataSource">
  20 + <template #renderItem="{ item }">
  21 + <ListItem>
  22 + <ListItemMeta>
  23 + <template #avatar>
  24 + <Avatar
  25 + src="https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png"
  26 + />
  27 + </template>
  28 + <template #description>
  29 + <span
  30 + class="cursor-pointer noticeTitle"
  31 + @click="go('/stationnotification/mynotification')"
  32 + >{{ item.sysNotice.title }}
  33 + </span>
  34 + </template>
  35 + <template #title>
  36 + <span>{{ item.user.realName }}</span>
  37 + </template>
  38 + </ListItemMeta>
  39 + <template #extra>
  40 + <Time :value="item.sysNotice.senderDate" />
33 41 </template>
34   - </ListItemMeta>
35   - <template #extra>
36   - <Time :value="item.sysNotice.senderDate" />
37   - </template>
38   - </ListItem>
39   - </template>
40   - </List>
41   - <Card hoverable title="联系我们" :bordered="false">
42   - <template #cover>
43   - <img :src="getQrCode" alt="" style="width: 150px; height: 150px; margin: 50px auto" />
44   - </template>
45   - <CardMeta>
46   - <template #description>
47   - <p>联系人: {{ getContacts }}</p>
48   - <p>联系电话: {{ getTel }}</p>
49   - <p>联系地址: {{ getAddress }} </p>
  42 + </ListItem>
50 43 </template>
51   - </CardMeta>
52   - </Card>
53   - </div>
  44 + </List>
  45 + <Card hoverable title="联系我们" :bordered="false">
  46 + <template #cover>
  47 + <img :src="getQrCode" alt="" style="width: 150px; height: 150px; margin: 50px auto" />
  48 + </template>
  49 + <CardMeta>
  50 + <template #description>
  51 + <p>联系人: {{ getContacts }}</p>
  52 + <p>联系电话: {{ getTel }}</p>
  53 + <p>联系地址: {{ getAddress }} </p>
  54 + </template>
  55 + </CardMeta>
  56 + </Card>
  57 + </div>
  58 + </Card>
  59 + </Card>
  60 +
  61 + <Card v-if="isAdmin(role)">
  62 + <Descriptions title="租户消息量TOP10" :column="1">
  63 + <template v-for="(item, index) in 10" :key="index">
  64 + <DescriptionsItem>
  65 + <span
  66 + class="mr-2"
  67 + style="
  68 + width: 1.25rem;
  69 + height: 1.25rem;
  70 + border: 1px solid;
  71 + color: #0b55f1;
  72 + border-radius: 50%;
  73 + display: flex;
  74 + align-items: center;
  75 + justify-content: center;
  76 + "
  77 + :style="{
  78 + color:
  79 + index === 0
  80 + ? '#f0a16e'
  81 + : index === 1
  82 + ? '#868585'
  83 + : index === 2
  84 + ? '#e78739'
  85 + : '#4e84f5',
  86 + backgroundColor:
  87 + index === 0 ? '#fed36a' : index === 1 ? '#CBCAC9' : index === 2 ? '#F1B889' : '',
  88 + borderColor:
  89 + index === 0
  90 + ? '#fdee7d'
  91 + : index === 1
  92 + ? '#e6e6e5'
  93 + : index === 2
  94 + ? '#f8c296'
  95 + : '#0b55f1;',
  96 + }"
  97 + >{{ index + 1 }}</span
  98 + >兰州天兆猪业</DescriptionsItem
  99 + >
  100 + </template>
  101 + </Descriptions>
  102 + </Card>
  103 + <Card v-if="isAdmin(role)">
  104 + <BasicTable @register="registerTable" />
54 105 </Card>
55   - </Card>
  106 + </div>
56 107 </template>
57 108
58 109 <script lang="ts">
59 110 import { defineComponent, ref, computed, onMounted } from 'vue';
60   - import { Card, AnchorLink, List, ListItem, ListItemMeta, Avatar, CardMeta } from 'ant-design-vue';
  111 + import { Card, Anchor, List, Avatar, Descriptions } from 'ant-design-vue';
61 112 import { useUserStore } from '/@/store/modules/user';
62 113 import { getEnterPriseDetail } from '/@/api/oem';
63 114 import { notifyMyGetrPageApi } from '/@/api/stationnotification/stationnotifyApi';
64 115 import { Time } from '/@/components/Time';
65 116 import { useGo } from '/@/hooks/web/usePage';
  117 + import { BasicTable, useTable } from '/@/components/Table';
  118 + import { columns } from './props';
  119 + import { isAdmin } from '/@/enums/roleEnum';
  120 + import { getTenantExpireTimeList } from '/@/api/dashboard';
66 121 export default defineComponent({
67 122 components: {
68 123 Card,
69   - AnchorLink,
  124 + CardMeta: Card.Meta,
  125 + AnchorLink: Anchor.Link,
70 126 List,
71   - ListItem,
72   - ListItemMeta,
  127 + ListItem: List.Item,
  128 + ListItemMeta: List.Item.Meta,
  129 + Descriptions,
  130 + DescriptionsItem: Descriptions.Item,
73 131 Avatar,
74 132 Time,
75   - CardMeta,
  133 + BasicTable,
76 134 },
77   - setup() {
78   - onMounted(async () => {
79   - const res = await getEnterPriseDetail();
80   - userStore.setEnterPriseInfo(res);
81   - });
  135 + props: {
  136 + role: {
  137 + type: String,
  138 + required: true,
  139 + },
  140 + },
  141 + setup(props) {
  142 + // 通知数据
  143 + const dataSource = ref([]);
  144 + const go = useGo();
  145 +
82 146 const helpDoc = ref([
83 147 {
84 148 title: '如何接入设备?',
... ... @@ -109,6 +173,18 @@
109 173 activeKey.value = key;
110 174 };
111 175
  176 + const [registerTable, { redoHeight }] = useTable({
  177 + api: getTenantExpireTimeList,
  178 + title: '本月即将过期租户',
  179 + showIndexColumn: false,
  180 + useSearchForm: false,
  181 + columns,
  182 + fetchSetting: {
  183 + listField: 'expireTenant.items',
  184 + totalField: 'expireTenant.total',
  185 + },
  186 + });
  187 +
112 188 const userStore = useUserStore();
113 189 const getContacts = computed(() => {
114 190 return userStore.enterPriseInfo?.contacts;
... ... @@ -122,32 +198,35 @@
122 198 const getQrCode = computed(() => {
123 199 return userStore.enterPriseInfo?.qrCode;
124 200 });
125   -
126   - // 通知数据
127   - const dataSource = ref([]);
128   - const go = useGo();
129 201 onMounted(async () => {
130   - const res = await notifyMyGetrPageApi({ page: 1, pageSize: 5 });
131   - dataSource.value = res.items;
  202 + if (isAdmin(props.role)) return;
  203 +
  204 + const res = await getEnterPriseDetail();
  205 + const notice = await notifyMyGetrPageApi({ page: 1, pageSize: 5 });
  206 + userStore.setEnterPriseInfo(res);
  207 + dataSource.value = notice.items;
132 208 });
133 209
134 210 return {
135 211 activeKey,
136 212 tabListTitle,
137   - onTabChange,
138 213 helpDoc,
139 214 getQrCode,
140 215 getContacts,
141 216 getAddress,
142 217 getTel,
143 218 dataSource,
  219 + onTabChange,
144 220 go,
  221 + registerTable,
  222 + isAdmin,
  223 + redoHeight,
145 224 };
146 225 },
147 226 });
148 227 </script>
149 228
150   -<style lang="less" scoped>
  229 +<style scoped>
151 230 .noticeTitle:hover {
152 231 border-bottom: 1px solid #ccc;
153 232 }
... ...
... ... @@ -4,59 +4,347 @@
4 4 v-bind="$attrs"
5 5 :active-tab-key="activeKey"
6 6 @tabChange="onTabChange"
  7 + v-if="!isAdmin(role)"
7 8 >
8 9 <template #tabBarExtraContent>
9 10 <div class="extra-date">
10   - <template v-for="(item, index) in dateList" :key="item">
11   - <span @click="changeDate(index)" :class="{ active: index === activeIndex }">{{
12   - item
13   - }}</span>
  11 + <template v-for="(item, index) in dateList" :key="item.value">
  12 + <span
  13 + @click="quickQueryDate(index, item.value)"
  14 + :class="{ active: index === activeIndex }"
  15 + >{{ item.label }}</span
  16 + >
14 17 </template>
15   - <DatePicker @change="onDateChange" />
  18 + <DatePicker @change="onDateChange" v-model:value="dateValue" />
16 19 </div>
17 20 </template>
18   - <div v-if="activeKey === 'tab1'">
19   - <p class="center">告警数</p>
20   - <VisitAnalysis />
  21 + <div v-show="activeKey === '1'">
  22 + <!-- 折线图 -->
  23 + <VisitAnalysis :alarmList="state.alarmList" />
21 24 </div>
22   - <div v-else>
23   - <p class="center">消息数</p>
24   - <VisitAnalysisBar />
  25 + <div v-show="activeKey === '2'">
  26 + <!-- 柱形图 -->
  27 + <VisitAnalysisBar :dataPointList="state.dataPointList" :messageList="state.messageList" />
25 28 </div>
26 29 </Card>
  30 + <Card v-bind="$attrs" v-if="isAdmin(role)" title="租户趋势">
  31 + <TenantTrend />
  32 + </Card>
  33 + <Card v-bind="$attrs" v-if="isAdmin(role)" title="客户趋势">
  34 + <CustomerTrend />
  35 + </Card>
27 36 </template>
28 37 <script lang="ts" setup>
29   - import { ref } from 'vue';
  38 + import { ref, reactive } from 'vue';
30 39 import { Card, DatePicker } from 'ant-design-vue';
31 40 import VisitAnalysis from './VisitAnalysis.vue';
32 41 import VisitAnalysisBar from './VisitAnalysisBar.vue';
33   -
34   - const activeKey = ref('tab1');
35   -
  42 + import { isAdmin } from '/@/enums/roleEnum';
  43 + import { useWebSocket } from '@vueuse/core';
  44 + import { getAuthCache } from '/@/utils/auth';
  45 + import CustomerTrend from './CustomerTrend.vue';
  46 + import TenantTrend from './TenantTrend.vue';
  47 + import { JWT_TOKEN_KEY } from '/@/enums/cacheEnum';
  48 + import { formatToDateTime } from '/@/utils/dateUtil';
  49 + import { getEntitiesId } from '/@/api/dashboard/index';
  50 + defineExpose({
  51 + isAdmin,
  52 + });
  53 + const props = defineProps<{
  54 + role: string;
  55 + }>();
  56 + const activeKey = ref('1');
  57 + let entityId = null;
  58 + // 图表tab切换选项卡
36 59 const tabListTitle = [
37 60 {
38   - key: 'tab1',
  61 + key: '1',
39 62 tab: '告警数统计',
40 63 },
41 64 {
42   - key: 'tab2',
  65 + key: '2',
43 66 tab: '消息量统计',
44 67 },
45 68 ];
46   - const dateList = ref(['1小时', '1天', '7天', '30天']);
47   - const activeIndex = ref(0);
48   - function onTabChange(key) {
  69 + // 快速选择日期
  70 + const activeIndex = ref(3);
  71 + const dateValue = ref();
  72 + const dateList = ref([
  73 + { label: '1小时', value: 3600000 },
  74 + { label: '1天', value: 86400000 },
  75 + { label: '7天', value: 604800000 },
  76 + { label: '30天', value: 2592000000 },
  77 + ]);
  78 + // web Socket
  79 + const token: string = getAuthCache(JWT_TOKEN_KEY);
  80 + const state = reactive({
  81 + server: `${import.meta.env.VITE_WEB_SOCKET}${token}`,
  82 + alarmList: new Array<[number, string]>(),
  83 + alarmItem: new Array<[number, string]>(),
  84 + dataPointList: new Array<[number, string]>(),
  85 + messageList: new Array<[number, string]>(),
  86 + dataPoint: new Array<[number, string]>(),
  87 + MsgCount: new Array<[number, string]>(),
  88 + });
  89 + const { send, close } = useWebSocket(state.server, {
  90 + async onConnected() {
  91 + if (isAdmin(props.role)) return;
  92 + const res = await getEntitiesId();
  93 + entityId = res.data[0].entityId;
  94 + const sendValue = JSON.stringify({
  95 + entityDataCmds: [
  96 + {
  97 + query: {
  98 + entityFilter: {
  99 + type: 'singleEntity',
  100 + singleEntity: entityId,
  101 + },
  102 + pageLink: {
  103 + pageSize: 1024,
  104 + page: 0,
  105 + sortOrder: {
  106 + key: {
  107 + type: 'ENTITY_FIELD',
  108 + key: 'createdTime',
  109 + },
  110 + direction: 'DESC',
  111 + },
  112 + },
  113 + entityFields: [
  114 + {
  115 + type: 'ENTITY_FIELD',
  116 + key: 'name',
  117 + },
  118 + {
  119 + type: 'ENTITY_FIELD',
  120 + key: 'label',
  121 + },
  122 + {
  123 + type: 'ENTITY_FIELD',
  124 + key: 'additionalInfo',
  125 + },
  126 + ],
  127 + latestValues: [
  128 + {
  129 + type: 'TIME_SERIES',
  130 + key: 'createdAlarmsCountHourly',
  131 + },
  132 + ],
  133 + },
  134 + cmdId: activeKey.value,
  135 + },
  136 + ],
  137 + });
  138 + const sendValue1 = JSON.stringify({
  139 + entityDataCmds: [
  140 + {
  141 + cmdId: activeKey.value,
  142 + historyCmd: {
  143 + keys: ['createdAlarmsCountHourly'],
  144 + startTs: Date.now() - 2592000000,
  145 + endTs: Date.now(),
  146 + interval: 86400000,
  147 + agg: 'COUNT',
  148 + },
  149 + },
  150 + ],
  151 + });
  152 + send(sendValue);
  153 + send(sendValue1);
  154 + console.log('建立连接了');
  155 + },
  156 + onMessage(_, e) {
  157 + const { data, update } = JSON.parse(e.data);
  158 + console.log('来新消息了', data, update);
  159 + if (activeKey.value === '1') {
  160 + if (data) {
  161 + const { createdAlarmsCountHourly } = data.data[0].latest.TIME_SERIES;
  162 + state.alarmItem = [createdAlarmsCountHourly.ts, createdAlarmsCountHourly.value];
  163 + state.alarmList.push([createdAlarmsCountHourly.ts, createdAlarmsCountHourly.value]);
  164 + }
  165 + if (update) {
  166 + const { createdAlarmsCountHourly } = update[0].timeseries;
  167 + const newArray: any = [];
  168 + for (const item of createdAlarmsCountHourly) {
  169 + newArray.push([item.ts, item.value]);
  170 + }
  171 + state.alarmList = newArray;
  172 + }
  173 + } else {
  174 + if (data) {
  175 + const { transportDataPointsCountHourly, transportMsgCountHourly } =
  176 + data.data[0].latest.TIME_SERIES;
  177 + state.dataPoint = [
  178 + transportDataPointsCountHourly.ts,
  179 + transportDataPointsCountHourly.value,
  180 + ];
  181 + state.MsgCount = [
  182 + transportDataPointsCountHourly.ts,
  183 + transportDataPointsCountHourly.value,
  184 + ];
  185 + state.dataPointList.push([
  186 + transportDataPointsCountHourly.ts,
  187 + transportDataPointsCountHourly.value,
  188 + ]);
  189 + state.messageList.push([transportMsgCountHourly.ts, transportMsgCountHourly.value]);
  190 + }
  191 + if (update) {
  192 + const { transportDataPointsCountHourly, transportMsgCountHourly } = update[0].timeseries;
  193 + const newArray: any[] = [];
  194 + const newArray1: any[] = [];
  195 + for (const item of transportDataPointsCountHourly) {
  196 + newArray.push([item.ts, item.value]);
  197 + }
  198 + for (const item of transportMsgCountHourly) {
  199 + newArray1.push([item.ts, item.value]);
  200 + }
  201 + state.dataPointList = newArray;
  202 + state.messageList = newArray1;
  203 + }
  204 + }
  205 + },
  206 + onDisconnected() {
  207 + console.log('断开连接了');
  208 + close();
  209 + },
  210 + });
  211 +
  212 + // 切换tab页
  213 + function onTabChange(key: string) {
49 214 activeKey.value = key;
  215 + activeIndex.value = 3;
  216 + dateValue.value = '';
  217 + const sendValue = JSON.stringify({
  218 + entityDataCmds: [
  219 + {
  220 + cmdId: activeKey.value,
  221 + historyCmd: {
  222 + keys:
  223 + activeKey.value === '1'
  224 + ? ['createdAlarmsCountHourly']
  225 + : ['transportMsgCountHourly', 'transportDataPointsCountHourly'],
  226 + startTs: Date.now() - 2592000000,
  227 + endTs: Date.now(),
  228 + interval: 86400000,
  229 + agg: 'COUNT',
  230 + },
  231 + },
  232 + ],
  233 + });
  234 + if (key === '2') {
  235 + const sendMessageValue = JSON.stringify({
  236 + entityDataCmds: [
  237 + {
  238 + query: {
  239 + entityFilter: {
  240 + type: 'singleEntity',
  241 + singleEntity: entityId,
  242 + },
  243 + pageLink: {
  244 + pageSize: 1024,
  245 + page: 0,
  246 + sortOrder: {
  247 + key: {
  248 + type: 'ENTITY_FIELD',
  249 + key: 'createdTime',
  250 + },
  251 + direction: 'DESC',
  252 + },
  253 + },
  254 + entityFields: [
  255 + {
  256 + type: 'ENTITY_FIELD',
  257 + key: 'name',
  258 + },
  259 + {
  260 + type: 'ENTITY_FIELD',
  261 + key: 'label',
  262 + },
  263 + {
  264 + type: 'ENTITY_FIELD',
  265 + key: 'additionalInfo',
  266 + },
  267 + ],
  268 + latestValues: [
  269 + {
  270 + type: 'TIME_SERIES',
  271 + key: 'transportMsgCountHourly',
  272 + },
  273 + {
  274 + type: 'TIME_SERIES',
  275 + key: 'transportDataPointsCountHourly',
  276 + },
  277 + ],
  278 + },
  279 + cmdId: activeKey.value,
  280 + },
  281 + ],
  282 + });
  283 + send(sendMessageValue);
  284 + }
  285 + send(sendValue);
50 286 }
51   - function onDateChange(date, dateString) {
52   - console.log(date, dateString);
  287 + // 选择日期
  288 + function onDateChange(_, dateString) {
  289 + activeIndex.value = -1;
  290 + const dateTime = Number(formatToDateTime(dateString, 'x'));
  291 + // 动态发送ws数据
  292 + const sendValue = JSON.stringify({
  293 + entityDataCmds: [
  294 + {
  295 + cmdId: activeKey.value,
  296 + historyCmd: {
  297 + keys:
  298 + activeKey.value === '1'
  299 + ? ['createdAlarmsCountHourly']
  300 + : ['transportMsgCountHourly', 'transportDataPointsCountHourly'],
  301 + startTs: dateTime,
  302 + endTs: dateTime + 86400000,
  303 + interval: 7200000,
  304 + limit: 12,
  305 + agg: 'COUNT',
  306 + },
  307 + },
  308 + ],
  309 + });
  310 + send(sendValue);
53 311 }
54   - function changeDate(index: number) {
  312 + // 快速选择时间
  313 + function quickQueryDate(index: number, value: number) {
  314 + if (activeIndex.value === index) return;
55 315 activeIndex.value = index;
  316 + dateValue.value = '';
  317 + let interval = 300000;
  318 + if (value === 86400000) {
  319 + interval = 7200000;
  320 + } else if (value === 604800000 || value === 2592000000) {
  321 + interval = 86400000;
  322 + }
  323 + // 动态发送ws数据
  324 + const sendValue = JSON.stringify({
  325 + entityDataCmds: [
  326 + {
  327 + cmdId: activeKey.value,
  328 + historyCmd: {
  329 + keys:
  330 + activeKey.value === '1'
  331 + ? ['createdAlarmsCountHourly']
  332 + : ['transportMsgCountHourly', 'transportDataPointsCountHourly'],
  333 + startTs: Date.now() - value,
  334 + endTs: Date.now(),
  335 + interval,
  336 + agg: 'COUNT',
  337 + },
  338 + },
  339 + ],
  340 + });
  341 + send(sendValue);
  342 +
  343 + console.log(JSON.parse(sendValue), '----interval', state.alarmList);
56 344 }
57 345 </script>
58 346
59   -<style scoped lang="less">
  347 +<style lang="less">
60 348 .center {
61 349 display: flex;
62 350 justify-content: center;
... ...
  1 +<template>
  2 + 123
  3 + <div ref="chartRef" :style="{ height, width }"></div>
  4 +</template>
  5 +<script lang="ts" setup>
  6 + import { ref, Ref, withDefaults } from 'vue';
  7 + import { useECharts } from '/@/hooks/web/useECharts';
  8 +
  9 + interface Props {
  10 + width?: string;
  11 + height?: string;
  12 + }
  13 + withDefaults(defineProps<Props>(), {
  14 + width: '100%',
  15 + height: '280px',
  16 + });
  17 +
  18 + const chartRef = ref<HTMLDivElement | null>(null);
  19 + const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>);
  20 +</script>
... ...
1 1 <template>
2   - <div ref="chartRef" :style="{ height, width }"></div>
  2 + <div>
  3 + <p class="center">告警数</p>
  4 + <div ref="chartRef" :style="{ height, width }" v-show="alarmList.length"></div>
  5 + <div v-show="!alarmList.length">暂无数据</div>
  6 + </div>
3 7 </template>
4 8 <script lang="ts" setup>
5   - import { onMounted, ref, Ref } from 'vue';
  9 + import { onMounted, ref, Ref, withDefaults, watch } from 'vue';
6 10 import { useECharts } from '/@/hooks/web/useECharts';
7   - import { basicProps } from './props';
8 11
9   - defineProps({
10   - ...basicProps,
  12 + interface Props {
  13 + width?: string;
  14 + height?: string;
  15 + alarmList: [number, string][];
  16 + }
  17 + const props = withDefaults(defineProps<Props>(), {
  18 + width: '100%',
  19 + height: '280px',
  20 + alarmList: () => [],
11 21 });
  22 +
12 23 const chartRef = ref<HTMLDivElement | null>(null);
13 24 const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>);
14 25
... ... @@ -16,79 +27,60 @@
16 27 setOptions({
17 28 tooltip: {
18 29 trigger: 'axis',
19   - axisPointer: {
20   - lineStyle: {
21   - width: 1,
22   - color: '#019680',
23   - },
24   - },
25 30 },
  31 + grid: {
  32 + left: '3%',
  33 + right: '4%',
  34 + bottom: '3%',
  35 + containLabel: true,
  36 + },
  37 +
26 38 xAxis: {
27   - type: 'category',
28   - boundaryGap: false,
29   - data: [
30   - '6:00',
31   - '7:00',
32   - '8:00',
33   - '9:00',
34   - '10:00',
35   - '11:00',
36   - '12:00',
37   - '13:00',
38   - '14:00',
39   - '15:00',
40   - '16:00',
41   - '17:00',
42   - '18:00',
43   - '19:00',
44   - '20:00',
45   - '21:00',
46   - '22:00',
47   - '23:00',
48   - ],
49   - splitLine: {
50   - show: true,
51   - lineStyle: {
52   - width: 1,
53   - type: 'solid',
54   - color: 'rgba(226,226,226,0.5)',
55   - },
56   - },
57   - axisTick: {
58   - show: false,
59   - },
  39 + type: 'time',
  40 + },
  41 + yAxis: {
  42 + type: 'value',
60 43 },
61   - yAxis: [
62   - {
63   - type: 'value',
64   - max: 80000,
65   - splitNumber: 4,
66   - axisTick: {
67   - show: false,
68   - },
69   - splitArea: {
70   - show: true,
71   - areaStyle: {
72   - color: ['rgba(255,255,255,0.2)', 'rgba(226,226,226,0.2)'],
73   - },
74   - },
75   - },
76   - ],
77   - grid: { left: '1%', right: '1%', top: '2 %', bottom: 0, containLabel: true },
78 44 series: [
79 45 {
80   - smooth: true,
81   - data: [
82   - 111, 222, 4000, 18000, 33333, 55555, 66666, 33333, 14000, 36000, 66666, 44444, 22222,
83   - 11111, 4000, 2000, 500, 333, 222, 111,
84   - ],
85   - type: 'line',
86   - areaStyle: {},
87   - itemStyle: {
88   - color: '#5ab1ef',
89   - },
  46 + name: '告警数',
  47 + type: 'bar',
  48 + stack: 'Total',
  49 + data: props.alarmList,
90 50 },
91 51 ],
92 52 });
93 53 });
  54 + watch(
  55 + () => props.alarmList,
  56 + (newValue) => {
  57 + console.log(newValue);
  58 + setOptions({
  59 + tooltip: {
  60 + trigger: 'axis',
  61 + },
  62 + grid: {
  63 + left: '3%',
  64 + right: '4%',
  65 + bottom: '3%',
  66 + containLabel: true,
  67 + },
  68 +
  69 + xAxis: {
  70 + type: 'time',
  71 + },
  72 + yAxis: {
  73 + type: 'value',
  74 + },
  75 + series: [
  76 + {
  77 + name: '告警数',
  78 + type: 'bar',
  79 + stack: 'Total',
  80 + data: newValue,
  81 + },
  82 + ],
  83 + });
  84 + }
  85 + );
94 86 </script>
... ...
1 1 <template>
2   - <div ref="chartRef" :style="{ height, width }"></div>
  2 + <div>
  3 + <p class="center">消息量</p>
  4 + <div ref="chartRef" :style="{ height, width }" v-show="dataPointList.length"></div>
  5 + <div v-show="!dataPointList.length">暂无数据</div>
  6 + </div>
3 7 </template>
4 8 <script lang="ts" setup>
5   - import { onMounted, ref, Ref } from 'vue';
  9 + import { ref, Ref, watch, withDefaults } from 'vue';
6 10 import { useECharts } from '/@/hooks/web/useECharts';
7   - import { basicProps } from './props';
8   -
9   - defineProps({
10   - ...basicProps,
  11 + type DataItem = [number, string];
  12 + interface Props {
  13 + width?: string;
  14 + height?: string;
  15 + dataPointList: DataItem[];
  16 + messageList: DataItem[];
  17 + }
  18 + const props = withDefaults(defineProps<Props>(), {
  19 + width: '100%',
  20 + height: '280px',
  21 + dataPointList: () => [],
  22 + messageList: () => [],
11 23 });
12   -
13 24 const chartRef = ref<HTMLDivElement | null>(null);
14 25 const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>);
15   - onMounted(() => {
16   - setOptions({
17   - tooltip: {
18   - trigger: 'axis',
19   - axisPointer: {
20   - lineStyle: {
21   - width: 1,
22   - color: '#019680',
  26 + watch(
  27 + () => [props.dataPointList, props.messageList],
  28 + ([newValue, newValue1]) => {
  29 + // 计算总量
  30 + let dataPointTotal = 0;
  31 + let messageTotal = 0;
  32 + for (const item of newValue) {
  33 + dataPointTotal += Number(item[1]);
  34 + }
  35 + for (const item of newValue1) {
  36 + messageTotal += Number(item[1]);
  37 + }
  38 + setOptions({
  39 + tooltip: {
  40 + trigger: 'axis',
  41 + axisPointer: {
  42 + type: 'shadow',
23 43 },
24 44 },
25   - },
26   - grid: { left: '1%', right: '1%', top: '2 %', bottom: 0, containLabel: true },
27   - xAxis: {
28   - type: 'category',
29   - data: [
30   - '1月',
31   - '2月',
32   - '3月',
33   - '4月',
34   - '5月',
35   - '6月',
36   - '7月',
37   - '8月',
38   - '9月',
39   - '10月',
40   - '11月',
41   - '12月',
42   - ],
43   - },
44   - yAxis: {
45   - type: 'value',
46   - max: 8000,
47   - splitNumber: 4,
48   - },
49   - series: [
50   - {
51   - data: [3000, 2000, 3333, 5000, 3200, 4200, 3200, 2100, 3000, 5100, 6000, 3200, 4800],
52   - type: 'bar',
53   - barMaxWidth: 80,
  45 + xAxis: {
  46 + type: 'time',
54 47 },
55   - ],
56   - });
57   - });
  48 + legend: {
  49 + data: ['传输数据点', '传输消息量'],
  50 + left: '5%',
  51 + orient: 'vertical',
  52 + formatter: (name) => {
  53 + return name === '传输数据点' ? `${name} ${dataPointTotal}` : `${name} ${messageTotal}`;
  54 + },
  55 + },
  56 +
  57 + yAxis: {},
  58 + grid: {
  59 + left: '3%',
  60 + right: '4%',
  61 + bottom: '3%',
  62 + containLabel: true,
  63 + },
  64 + series: [
  65 + {
  66 + name: '传输数据点',
  67 + type: 'bar',
  68 + stack: 'total',
  69 + data: newValue,
  70 + color: '#9fe080',
  71 + },
  72 + {
  73 + name: '传输消息量',
  74 + type: 'bar',
  75 + stack: 'total',
  76 + data: newValue1,
  77 + color: '#5c7bd9',
  78 + },
  79 + ],
  80 + });
  81 + }
  82 + );
58 83 </script>
... ...
1 1 import { PropType } from 'vue';
2   -
  2 +import type { BasicColumn } from '/@/components/Table';
3 3 export interface BasicProps {
4 4 width: string;
5 5 height: string;
... ... @@ -14,3 +14,16 @@ export const basicProps = {
14 14 default: '280px',
15 15 },
16 16 };
  17 +
  18 +export const columns: BasicColumn[] = [
  19 + {
  20 + title: '租户名称',
  21 + dataIndex: 'name',
  22 + width: 120,
  23 + },
  24 + {
  25 + title: '过期时间',
  26 + dataIndex: 'tenantExpireTime',
  27 + width: 120,
  28 + },
  29 +];
... ...
1   -export interface GrowCardItem {
2   - imgUrl: string;
3   - title: string;
4   - value: string;
5   - onLine?: number;
6   - offLine?: number;
7   - inactive?: number;
8   - newDay: string;
9   -}
10   -
11   -export const growCardList: GrowCardItem[] = [
12   - {
13   - imgUrl: '/src/assets/images/device-count.png',
14   - title: '设备数(个)',
15   - value: '10,000',
16   - onLine: 2000,
17   - offLine: 3000,
18   - inactive: 4000,
19   - newDay: '123,45',
20   - },
21   - {
22   - imgUrl: '/src/assets/images/alarm-count.png',
23   - title: '11月告警数(条)',
24   - value: '11,000',
25   - newDay: '167,45',
26   - },
27   - {
28   - imgUrl: '/src/assets/images/msg-count.png',
29   - title: '11月消息量(条)',
30   - value: '12,000',
31   - newDay: '198,45',
32   - },
33   -];
1 1 <template>
2 2 <div class="p-4 md:flex">
3 3 <div class="md:w-7/10 w-full !mr-4 enter-y">
4   - <GrowCard :loading="loading" class="enter-y" />
5   - <SiteAnalysis class="!my-4 enter-y" :loading="loading" />
6   - <div class="md:flex enter-y">
  4 + <GrowCard :loading="loading" class="enter-y" :role="role" />
  5 + <SiteAnalysis class="!my-4 enter-y" :loading="loading" :role="role" />
  6 + <div class="md:flex enter-y" v-if="!isAdmin(role)">
7 7 <Card title="核心流程指南" style="width: 100%">
8   - <img alt="核心流程指南" src="../../../assets/images/flow.png" />
  8 + <img alt="核心流程指南" src="/src/assets/images/flow.png" />
9 9 </Card>
10 10 </div>
11 11 </div>
12 12 <div class="md:w-3/10 w-full enter-y">
13   - <HelpDoc />
  13 + <HelpDoc :role="role" />
14 14 </div>
15 15 </div>
16 16 </template>
... ... @@ -20,8 +20,19 @@
20 20 import SiteAnalysis from './components/SiteAnalysis.vue';
21 21 import { Card } from 'ant-design-vue';
22 22 import HelpDoc from './components/HelpDoc.vue';
  23 + import { USER_INFO_KEY } from '/@/enums/cacheEnum';
  24 + import { getAuthCache } from '/@/utils/auth';
  25 + import { isAdmin } from '/@/enums/roleEnum';
  26 + defineExpose({
  27 + isAdmin,
  28 + });
23 29
  30 + const userInfo: any = getAuthCache(USER_INFO_KEY);
  31 + const role: string = userInfo.roles[0];
24 32 const loading = ref(true);
  33 +
  34 + console.log(role);
  35 +
25 36 setTimeout(() => {
26 37 loading.value = false;
27 38 }, 1500);
... ...
... ... @@ -3,6 +3,7 @@ import { findDictItemByCode } from '/@/api/system/dict';
3 3 import { deviceProfile } from '/@/api/device/deviceManager';
4 4 import { getOrganizationList } from '/@/api/system/system';
5 5 import { copyTransFun } from '/@/utils/fnUtils';
  6 +
6 7 // 第一步的表单
7 8 export const step1Schemas: FormSchema[] = [
8 9 {
... ... @@ -17,6 +18,7 @@ export const step1Schemas: FormSchema[] = [
17 18 required: true,
18 19 component: 'Input',
19 20 componentProps: {
  21 + placeholder: '设备名称',
20 22 maxLength: 30,
21 23 },
22 24 },
... ... @@ -26,6 +28,7 @@ export const step1Schemas: FormSchema[] = [
26 28 required: true,
27 29 component: 'ApiSelect',
28 30 componentProps: {
  31 + placeholder: '设备类型',
29 32 api: findDictItemByCode,
30 33 params: {
31 34 dictCode: 'device_type',
... ... @@ -64,6 +67,20 @@ export const step1Schemas: FormSchema[] = [
64 67 component: 'Input',
65 68 componentProps: {
66 69 maxLength: 255,
  70 + placeholder: '请输入设备标签',
  71 + },
  72 + dynamicRules: () => {
  73 + return [
  74 + {
  75 + required: false,
  76 + validator: (_, value) => {
  77 + if (String(value).length > 255) {
  78 + return Promise.reject('字数不超过255个字');
  79 + }
  80 + return Promise.resolve();
  81 + },
  82 + },
  83 + ];
67 84 },
68 85 },
69 86 {
... ... @@ -76,24 +93,92 @@ export const step1Schemas: FormSchema[] = [
76 93 field: 'description',
77 94 label: '备注',
78 95 component: 'InputTextArea',
  96 + componentProps: {
  97 + maxLength: 500,
  98 + placeholder: '请输入备注',
  99 + },
  100 + dynamicRules: () => {
  101 + return [
  102 + {
  103 + required: false,
  104 + validator: (_, value) => {
  105 + if (String(value).length > 500) {
  106 + return Promise.reject('字数不超过500个字');
  107 + }
  108 + return Promise.resolve();
  109 + },
  110 + },
  111 + ];
  112 + },
79 113 },
80 114 {
81 115 field: 'id',
82 116 label: 'id',
83 117 component: 'Input',
84 118 show: false,
  119 + componentProps: {
  120 + maxLength: 36,
  121 + placeholder: '请输入id',
  122 + },
  123 + dynamicRules: () => {
  124 + return [
  125 + {
  126 + required: false,
  127 + validator: (_, value) => {
  128 + if (String(value).length > 36) {
  129 + return Promise.reject('字数不超过36个字');
  130 + }
  131 + return Promise.resolve();
  132 + },
  133 + },
  134 + ];
  135 + },
85 136 },
86 137 {
87 138 field: 'tenantId',
88 139 label: '租户Code',
89 140 component: 'Input',
90 141 show: false,
  142 + componentProps: {
  143 + maxLength: 36,
  144 + placeholder: '请输入租户Code',
  145 + },
  146 + dynamicRules: () => {
  147 + return [
  148 + {
  149 + required: false,
  150 + validator: (_, value) => {
  151 + if (String(value).length > 36) {
  152 + return Promise.reject('字数不超过36个字');
  153 + }
  154 + return Promise.resolve();
  155 + },
  156 + },
  157 + ];
  158 + },
91 159 },
92 160 {
93 161 field: 'tbDeviceId',
94 162 label: 'tbDeviceId',
95 163 component: 'Input',
96 164 show: false,
  165 + componentProps: {
  166 + maxLength: 36,
  167 + placeholder: '请输入tbDeviceId',
  168 + },
  169 + dynamicRules: () => {
  170 + return [
  171 + {
  172 + required: false,
  173 + validator: (_, value) => {
  174 + if (String(value).length > 36) {
  175 + return Promise.reject('字数不超过36个字');
  176 + }
  177 + return Promise.resolve();
  178 + },
  179 + },
  180 + ];
  181 + },
97 182 },
98 183 ];
99 184
... ... @@ -244,6 +329,10 @@ export const step2Schemas: FormSchema[] = [
244 329 field: 'credentialsId',
245 330 required: true,
246 331 ifShow: false,
  332 + componentProps: {
  333 + maxLength: 36,
  334 + placeholder: '请输入访问令牌',
  335 + },
247 336 },
248 337 {
249 338 label: 'RSA公钥',
... ... @@ -251,6 +340,10 @@ export const step2Schemas: FormSchema[] = [
251 340 field: 'publicKey',
252 341 required: true,
253 342 ifShow: false,
  343 + componentProps: {
  344 + maxLength: 36,
  345 + placeholder: '请输入RSA公钥',
  346 + },
254 347 },
255 348 {
256 349 label: '客户端ID',
... ... @@ -258,6 +351,10 @@ export const step2Schemas: FormSchema[] = [
258 351 field: 'clientId',
259 352 required: true,
260 353 ifShow: false,
  354 + componentProps: {
  355 + maxLength: 36,
  356 + placeholder: '请输入客户端ID',
  357 + },
261 358 },
262 359 {
263 360 label: '用户名',
... ... @@ -265,11 +362,32 @@ export const step2Schemas: FormSchema[] = [
265 362 field: 'username',
266 363 required: true,
267 364 ifShow: false,
  365 + componentProps: {
  366 + maxLength: 255,
  367 + placeholder: '请输入用户名',
  368 + },
268 369 },
269 370 {
270 371 label: '密码',
271 372 component: 'InputPassword',
272 373 field: 'password',
  374 + componentProps: {
  375 + maxLength: 36,
  376 + placeholder: '请输入密码',
  377 + },
  378 + dynamicRules: () => {
  379 + return [
  380 + {
  381 + required: false,
  382 + validator: (_, value) => {
  383 + if (String(value).length > 36) {
  384 + return Promise.reject('字数不超过36个字');
  385 + }
  386 + return Promise.resolve();
  387 + },
  388 + },
  389 + ];
  390 + },
273 391 ifShow: false,
274 392 },
275 393 ];
... ... @@ -409,6 +527,10 @@ export const TokenSchemas: FormSchema[] = [
409 527 field: 'credentialsId',
410 528 required: true,
411 529 ifShow: false,
  530 + componentProps: {
  531 + maxLength: 36,
  532 + placeholder: '请输入访问令牌',
  533 + },
412 534 },
413 535 {
414 536 label: 'RSA公钥',
... ... @@ -416,6 +538,10 @@ export const TokenSchemas: FormSchema[] = [
416 538 field: 'publicKey',
417 539 required: true,
418 540 ifShow: false,
  541 + componentProps: {
  542 + maxLength: 36,
  543 + placeholder: '请输入RSA公钥',
  544 + },
419 545 },
420 546 {
421 547 label: '客户端ID',
... ... @@ -423,6 +549,10 @@ export const TokenSchemas: FormSchema[] = [
423 549 field: 'clientId',
424 550 required: true,
425 551 ifShow: false,
  552 + componentProps: {
  553 + maxLength: 36,
  554 + placeholder: '请输入客户端ID',
  555 + },
426 556 },
427 557 {
428 558 label: '用户名',
... ... @@ -430,23 +560,78 @@ export const TokenSchemas: FormSchema[] = [
430 560 field: 'username',
431 561 required: true,
432 562 ifShow: false,
  563 + componentProps: {
  564 + maxLength: 255,
  565 + placeholder: '请输入用户名',
  566 + },
433 567 },
434 568 {
435 569 label: '密码',
436 570 component: 'InputPassword',
437 571 field: 'password',
438 572 ifShow: false,
  573 + componentProps: {
  574 + maxLength: 36,
  575 + placeholder: '请输入密码',
  576 + },
  577 + dynamicRules: () => {
  578 + return [
  579 + {
  580 + required: false,
  581 + validator: (_, value) => {
  582 + if (String(value).length > 36) {
  583 + return Promise.reject('字数不超过36个字');
  584 + }
  585 + return Promise.resolve();
  586 + },
  587 + },
  588 + ];
  589 + },
439 590 },
440 591 {
441 592 label: 'id',
442 593 component: 'Input',
443 594 field: 'id',
444 595 show: false,
  596 + componentProps: {
  597 + maxLength: 36,
  598 + placeholder: '请输入id',
  599 + },
  600 + dynamicRules: () => {
  601 + return [
  602 + {
  603 + required: false,
  604 + validator: (_, value) => {
  605 + if (String(value).length > 36) {
  606 + return Promise.reject('字数不超过36个字');
  607 + }
  608 + return Promise.resolve();
  609 + },
  610 + },
  611 + ];
  612 + },
445 613 },
446 614 {
447 615 label: 'tbDeviceId',
448 616 component: 'Input',
449 617 field: 'tbDeviceId',
450 618 show: false,
  619 + componentProps: {
  620 + maxLength: 36,
  621 + placeholder: '请输入tbDeviceId',
  622 + },
  623 + dynamicRules: () => {
  624 + return [
  625 + {
  626 + required: false,
  627 + validator: (_, value) => {
  628 + if (String(value).length > 36) {
  629 + return Promise.reject('字数不超过36个字');
  630 + }
  631 + return Promise.resolve();
  632 + },
  633 + },
  634 + ];
  635 + },
451 636 },
452 637 ];
... ...
1 1 import { formatToDateTime } from '/@/utils/dateUtil';
2 2 import { FormSchema } from '/@/components/Form';
3 3 import { BasicColumn } from '/@/components/Table';
  4 +
4 5 import { DeviceTypeEnum } from '/@/api/device/model/deviceModel';
5 6
6 7 export const columns: BasicColumn[] = [
... ... @@ -8,25 +9,20 @@ export const columns: BasicColumn[] = [
8 9 title: '设备名称',
9 10 dataIndex: 'name',
10 11 width: 120,
11   - key: 'name',
12 12 },
13 13 {
14 14 title: '设备标签',
15 15 dataIndex: 'label',
16 16 width: 100,
17   - key: 'label',
18 17 },
19 18 {
20 19 title: '设备配置',
21 20 dataIndex: 'deviceProfile.name',
22 21 width: 160,
23   - key: 'deviceProfile.name',
24 22 },
25   -
26 23 {
27 24 title: '设备类型',
28 25 dataIndex: 'deviceType',
29   - key: 'deviceType',
30 26 customRender({ text }) {
31 27 return text === DeviceTypeEnum.GATEWAY
32 28 ? '网关设备'
... ... @@ -39,7 +35,6 @@ export const columns: BasicColumn[] = [
39 35 title: '描述',
40 36 dataIndex: 'description',
41 37 width: 180,
42   - key: 'description',
43 38 },
44 39 ];
45 40 // 实时数据表格
... ... @@ -95,6 +90,9 @@ export const alarmSearchSchemas: FormSchema[] = [
95 90 label: '告警类型',
96 91 component: 'Input',
97 92 colProps: { span: 6 },
  93 + componentProps: {
  94 + maxLength: 36,
  95 + },
98 96 },
99 97 {
100 98 field: 'endTime',
... ... @@ -208,6 +206,9 @@ export const alarmSchemasForm: FormSchema[] = [
208 206 field: 'details',
209 207 label: '详情',
210 208 component: 'InputTextArea',
  209 + componentProps: {
  210 + maxLength: 255,
  211 + },
211 212 },
212 213 ];
213 214
... ... @@ -218,12 +219,18 @@ export const childDeviceSchemas: FormSchema[] = [
218 219 label: '设备配置',
219 220 component: 'Select',
220 221 colProps: { span: 12 },
  222 + componentProps: {
  223 + maxLength: 255,
  224 + },
221 225 },
222 226 {
223 227 field: 'icon',
224 228 label: '设备名称',
225 229 component: 'Input',
226 230 colProps: { span: 12 },
  231 + componentProps: {
  232 + maxLength: 255,
  233 + },
227 234 },
228 235 ];
229 236 export const childDeviceColumns: BasicColumn[] = [
... ...
  1 +import { formatToDate } from '/@/utils/dateUtil';
1 2 import { BasicColumn } from '/@/components/Table';
2 3 import { FormSchema } from '/@/components/Table';
3 4 import { DeviceTypeEnum, DeviceState } from '/@/api/device/model/deviceModel';
  5 +
4 6 // 表格列数据
5 7 export const columns: BasicColumn[] = [
6 8 {
... ... @@ -19,6 +21,7 @@ export const columns: BasicColumn[] = [
19 21 dataIndex: 'deviceProfile.name',
20 22 width: 160,
21 23 slots: { customRender: 'deviceProfile' },
  24 + ellipsis: true,
22 25 },
23 26
24 27 {
... ... @@ -36,10 +39,16 @@ export const columns: BasicColumn[] = [
36 39 width: 120,
37 40 slots: { customRender: 'deviceState' },
38 41 },
39   -
40 42 {
41 43 title: '最后连接时间',
42   - dataIndex: 'lastConnectTime',
  44 + dataIndex: 'lastOnlineTime',
  45 + format: (text) => formatToDate(text, 'YYYY-MM-DD HH:mm:ss'),
  46 + width: 180,
  47 + },
  48 + {
  49 + title: '最后断开时间',
  50 + dataIndex: 'lastOfflineTime',
  51 + format: (text) => formatToDate(text, 'YYYY-MM-DD HH:mm:ss'),
43 52 width: 180,
44 53 },
45 54 ];
... ... @@ -76,6 +85,23 @@ export const searchFormSchema: FormSchema[] = [
76 85 field: 'name',
77 86 label: '设备名称',
78 87 component: 'Input',
79   - colProps: { span: 6 },
  88 + colProps: { span: 7 },
  89 + componentProps: {
  90 + maxLength: 255,
  91 + placeholder: '请输入设备名称',
  92 + },
  93 + dynamicRules: () => {
  94 + return [
  95 + {
  96 + required: false,
  97 + validator: (_, value) => {
  98 + if (String(value).length > 255) {
  99 + return Promise.reject('字数不超过255个字');
  100 + }
  101 + return Promise.resolve();
  102 + },
  103 + },
  104 + ];
  105 + },
80 106 },
81 107 ];
... ...
... ... @@ -6,7 +6,7 @@
6 6 :destroyOnClose="true"
7 7 @close="closeDrawer"
8 8 :title="deviceDetail.name"
9   - width="78%"
  9 + width="70%"
10 10 >
11 11 <Tabs v-model:activeKey="activeKey" :size="size" type="card">
12 12 <TabPane key="1" tab="详情"
... ... @@ -26,7 +26,7 @@
26 26 import { defineComponent, ref } from 'vue';
27 27 import { BasicDrawer, useDrawerInner } from '/@/components/Drawer';
28 28
29   - import { Tabs, TabPane } from 'ant-design-vue';
  29 + import { Tabs } from 'ant-design-vue';
30 30 import Detail from '../tabs/Detail.vue';
31 31 import RealTimeData from '../tabs/RealTimeData.vue';
32 32 import Alarm from '../tabs/Alarm.vue';
... ... @@ -37,13 +37,12 @@
37 37 components: {
38 38 BasicDrawer,
39 39 Tabs,
40   - TabPane,
  40 + TabPane: Tabs.TabPane,
41 41 Detail,
42 42 RealTimeData,
43 43 Alarm,
44 44 ChildDevice,
45 45 },
46   -
47 46 emits: ['reload', 'register'],
48 47 setup() {
49 48 const activeKey = ref('1');
... ...
... ... @@ -39,9 +39,10 @@
39 39 import { createOrEditDevice } from '/@/api/device/deviceManager';
40 40 import DeviceStep1 from '../step/DeviceStep1.vue';
41 41 import DeviceStep2 from '../step/DeviceStep2.vue';
42   - import { Steps, Step } from 'ant-design-vue';
  42 + import { Steps } from 'ant-design-vue';
43 43 import { useMessage } from '/@/hooks/web/useMessage';
44 44 import { credentialTypeEnum } from '../../config/data';
  45 +
45 46 export default defineComponent({
46 47 name: 'DeviceModal',
47 48 components: {
... ... @@ -49,7 +50,7 @@
49 50 DeviceStep1,
50 51 DeviceStep2,
51 52 Steps,
52   - Step,
  53 + Step: Steps.Step,
53 54 },
54 55 props: {
55 56 userData: { type: Object },
... ...
... ... @@ -44,6 +44,13 @@
44 44 labelWidth: 120,
45 45 schemas: alarmSearchSchemas,
46 46 },
  47 + showTableSetting: true,
  48 + tableSetting: {
  49 + redo: true,
  50 + size: false,
  51 + setting: false,
  52 + fullScreen: false,
  53 + },
47 54 useSearchForm: true,
48 55 bordered: true,
49 56 showIndexColumn: false,
... ...
... ... @@ -10,8 +10,8 @@
10 10 bordered
11 11 :columns="columns"
12 12 :data-source="[deviceDetail]"
  13 + :rowKey="(_, index) => index"
13 14 :pagination="false"
14   - rowKey="tbDeviceId"
15 15 style="width: 800px"
16 16 />
17 17 </div>
... ... @@ -23,7 +23,7 @@
23 23 </div>
24 24 <div v-if="deviceDetail?.deviceInfo?.address" class="mt-4">
25 25 <p>设备位置</p>
26   - <div ref="wrapRef" style="height: 400px; width: 90%"></div>
  26 + <div ref="wrapRef" style="height: 400px; width: 100%"></div>
27 27 </div>
28 28 </div>
29 29 </template>
... ...
... ... @@ -24,7 +24,7 @@
24 24 setup(props) {
25 25 const token: string = getAuthCache(JWT_TOKEN_KEY);
26 26 const state = reactive({
27   - server: `ws://101.133.234.90:8080/api/ws/plugins/telemetry?token=${token}`,
  27 + server: `${import.meta.env.VITE_WEB_SOCKET}${token}`,
28 28 sendValue: JSON.stringify({
29 29 attrSubCmds: [],
30 30 tsSubCmds: [
... ...
... ... @@ -8,7 +8,7 @@
8 8 @cancel="handleCancel"
9 9 >
10 10 <div class="step-form-form">
11   - <a-steps :current="current">
  11 + <a-steps :current="current" @change="handleChange">
12 12 <a-step title="设备配置" />
13 13 <a-step title="传输配置" />
14 14 <a-step title="告警配置" />
... ... @@ -34,10 +34,9 @@
34 34 @next="handleStep3Next"
35 35 @redo="handleRedo"
36 36 /></div>
37   -
38 37 <div v-show="current === 3">
39   - <DeviceProfileStep4 ref="DeviceProfileStep4Ref" @prev="handleStepPrev"
40   - /></div>
  38 + <DeviceProfileStep4 ref="DeviceProfileStep4Ref" @prev="handleStepPrev" />
  39 + </div>
41 40 </div>
42 41 </BasicModal>
43 42 </template>
... ... @@ -86,13 +85,21 @@
86 85 const current = ref(0);
87 86 const isUpdate = ref(true);
88 87 const getTitle = computed(() => (!unref(isUpdate) ? '新增设备配置' : '编辑设备配置'));
  88 + const handleChange = (v) => {
  89 + console.log(v);
  90 + };
89 91 const [register, { closeModal }] = useModalInner(async (data) => {
90 92 isUpdate.value = !!data?.isUpdate;
91 93 if (!unref(isUpdate)) {
92 94 current.value = 0;
  95 + proxy.$refs.DeviceProfileStep3Ref.clearProfileDataFunc();
  96 + proxy.$refs.DeviceProfileStep3Ref.addAlarmRule();
  97 + // proxy.$refs.DeviceProfileStep4Ref.customResetAndFunc();
  98 +
93 99 switch (current.value) {
94 100 case 0:
95 101 proxy.$refs.DeviceProfileStep1Ref.customResetFunc();
  102 + proxy.$refs.DeviceProfileStep1Ref.resetIconFunc();
96 103 break;
97 104 case 1:
98 105 proxy.$refs.DeviceProfileStep2Ref.customResetAndFunc();
... ... @@ -109,32 +116,15 @@
109 116 }
110 117 if (unref(isUpdate)) {
111 118 current.value = 0;
  119 + proxy.$refs.DeviceProfileStep3Ref.clearProfileDataFunc();
  120 + proxy.$refs.DeviceProfileStep3Ref.addAlarmRule();
112 121 postEditId.value = data.record.id;
113 122 const getBackendData = await deviceConfigGetDetail(postEditId.value);
114 123 editEchoData.value = { ...getBackendData };
115   - console.log(editEchoData.value);
116 124 switch (current.value) {
117 125 case 0:
118 126 proxy.$refs.DeviceProfileStep1Ref.resetFieldsFunc(editEchoData.value);
119   - break;
120   - case 1:
121   - proxy.$refs.DeviceProfileStep2Ref.resetFieldsFunc({
122   - transportType: editEchoData.value.profileData.transportConfiguration.type,
123   - });
124   - break;
125   - case 2:
126   - proxy.$refs.DeviceProfileStep3Ref.retryRegisterFormFunc({
127   - alarmType: editEchoData.value.profileData?.alarms[0].alarmType,
128   - });
129   - proxy.$refs.DeviceProfileStep3Ref.retryRegisterFormHighSettingmFunc(
130   - editEchoData.value
131   - );
132   - proxy.$refs.DeviceProfileStep3Ref.retryRegisterFormCreateAlarmFunc(
133   - editEchoData.value
134   - );
135   - break;
136   - case 3:
137   - proxy.$refs.DeviceProfileStep4Ref.resetFieldsFunc(editEchoData.value);
  127 + proxy.$refs.DeviceProfileStep1Ref.editIconFunc(editEchoData.value.icon);
138 128 break;
139 129 }
140 130 }
... ... @@ -142,18 +132,132 @@
142 132 function handleStepPrev() {
143 133 current.value--;
144 134 }
145   - function handleStepNext1(v) {
  135 + function handleStepNext1(v, v1) {
  136 + console.log(v, v1);
146 137 current.value++;
147 138 getStepOneData.value = v;
  139 + getStepOneData.value.icon = v1;
  140 + console.log(getStepOneData.value);
  141 + if (unref(isUpdate)) {
  142 + proxy.$refs.DeviceProfileStep2Ref.resetFieldsFunc({
  143 + transportType: editEchoData.value.profileData.transportConfiguration.type,
  144 + });
  145 + } else {
  146 + // proxy.$refs.DeviceProfileStep2Ref.customResetAndFunc();
  147 + }
148 148 }
149 149 function handleStep2Next(v) {
150 150 current.value++;
  151 +
151 152 getStepTwoData.value = v;
152   - proxy.$refs.DeviceProfileStep3Ref.addAlarmRule();
  153 + if (unref(isUpdate)) {
  154 + proxy.$refs.DeviceProfileStep3Ref.retryRegisterFormFunc({
  155 + alarmType: editEchoData.value.profileData.alarms[0].alarmType,
  156 + });
  157 + proxy.$refs.DeviceProfileStep3Ref.retryRegisterFormHighSettingmFunc({
  158 + propagate: editEchoData.value.profileData.alarms[0].propagate,
  159 + propagateRelationTypes:
  160 + editEchoData.value.profileData?.alarms[0].propagateRelationTypes,
  161 + });
  162 + const getKey = Object.keys(editEchoData.value.profileData?.alarms[0].createRules);
  163 + proxy.$refs.DeviceProfileStep3Ref.retryRegisterFormCreateAlarmFunc({
  164 + default: getKey[0],
  165 + });
  166 + const findDay = [
  167 + { label: '等于', value: 'EQUAL' },
  168 + { label: '不等于', value: 'NOT_EQUAL' },
  169 + { label: '开始于', value: 'STARTS_WITH' },
  170 + { label: '结束于', value: 'ENDS_WITH' },
  171 + { label: '包含', value: 'CONTAINS' },
  172 + { label: '不包含', value: 'NOT_CONTAINS' },
  173 + { label: '等于', value: 'EQUAL' },
  174 + { label: '不等于', value: 'NOT_EQUAL' },
  175 + { label: '大于', value: 'GREATER' },
  176 + { label: '小于', value: 'LESS' },
  177 + { label: '大于或等于', value: 'GREATER_OR_EQUAL' },
  178 + { label: '小于或等于', value: 'LESS_OR_EQUAL' },
  179 + ];
  180 + const findRuleByValue = findDay.find((f) => {
  181 + if (
  182 + f.value ==
  183 + editEchoData.value.profileData?.alarms[0].createRules[getKey[0]].condition
  184 + .condition[0].predicate.operation
  185 + ) {
  186 + return f.label;
  187 + }
  188 + });
  189 + const findClearRuleByValue = findDay.find((f) => {
  190 + if (
  191 + f.value ==
  192 + editEchoData.value.profileData?.alarms[0].clearRule.condition.condition[0].predicate
  193 + .operation
  194 + ) {
  195 + return f.label;
  196 + }
  197 + });
  198 + proxy.$refs.DeviceProfileStep3Ref.retryRulesFormDataFunc(
  199 + `
  200 + 键名:${
  201 + editEchoData.value.profileData?.alarms[0].createRules[getKey[0]].condition
  202 + .condition[0].key.key
  203 + }...操作:${findRuleByValue?.label}...值:${
  204 + editEchoData.value.profileData?.alarms[0].createRules[getKey[0]].condition
  205 + .condition[0].predicate.value.defaultValue
  206 + }
  207 + `
  208 + );
  209 + proxy.$refs.DeviceProfileStep3Ref.retryEnableFormDataFunc(`始终启用`);
  210 + proxy.$refs.DeviceProfileStep3Ref.retryTemplateFormDataFunc(
  211 + `
  212 + 报警详细信息:${
  213 + editEchoData.value.profileData?.alarms[0].createRules[getKey[0]].alarmDetails
  214 + }
  215 + `
  216 + );
  217 + //清除报警
  218 + proxy.$refs.DeviceProfileStep3Ref.retryRulesClearFormDataFunc(
  219 + `
  220 + 键名:${editEchoData.value.profileData?.alarms[0].clearRule.condition.condition[0].key.key}...操作:${findClearRuleByValue?.label}...值:${editEchoData.value.profileData?.alarms[0].clearRule.condition.condition[0].predicate.value.defaultValue}
  221 + `
  222 + );
  223 + proxy.$refs.DeviceProfileStep3Ref.retryEnableClearFormDataFunc(`始终启用`);
  224 + proxy.$refs.DeviceProfileStep3Ref.retryTemplateClearFormDataFunc(
  225 + `
  226 + 报警详细信息:${editEchoData.value.profileData?.alarms[0].clearRule.alarmDetails}
  227 + `
  228 + );
  229 + } else {
  230 + proxy.$refs.DeviceProfileStep3Ref.resetRegisterFormFunc();
  231 + proxy.$refs.DeviceProfileStep3Ref.resetRegisterFormHighSettingmFunc();
  232 + proxy.$refs.DeviceProfileStep3Ref.resetRegisterFormCreateAlarmFunc();
  233 + proxy.$refs.DeviceProfileStep3Ref.resetRulesFormDataFunc();
  234 + proxy.$refs.DeviceProfileStep3Ref.resetEnableFormDataFunc();
  235 + proxy.$refs.DeviceProfileStep3Ref.resetTemplateFormDataFunc();
  236 + proxy.$refs.DeviceProfileStep3Ref.resetRulesClearFormDataFunc();
  237 + proxy.$refs.DeviceProfileStep3Ref.resetEnableClearFormDataFunc();
  238 + proxy.$refs.DeviceProfileStep3Ref.resetTemplateClearFormDataFunc();
  239 + }
153 240 }
154 241 function handleStep3Next(v) {
155 242 current.value++;
156 243 getStepThreeData.value = v;
  244 + try {
  245 + if (unref(isUpdate)) {
  246 + setTimeout(() => {
  247 + proxy.$refs.DeviceProfileStep4Ref.resetFieldsFunc({
  248 + alarmContactId: editEchoData.value.alarmProfile.alarmContactId,
  249 + messageMode: editEchoData.value.alarmProfile.messageMode,
  250 + });
  251 + }, 1000);
  252 + }
  253 + // if (!unref(isUpdate)) {
  254 + // setTimeout(() => {
  255 + // proxy.$refs.DeviceProfileStep4Ref.customResetAndFunc();
  256 + // }, 1000);
  257 + // }
  258 + } catch (e) {
  259 + return e;
  260 + }
157 261 }
158 262 function handleRedo() {
159 263 current.value = 0;
... ... @@ -197,6 +301,7 @@
197 301 return;
198 302 };
199 303 return {
  304 + handleChange,
200 305 DeviceProfileStep2Ref,
201 306 DeviceProfileStep3Ref,
202 307 DeviceProfileStep4Ref,
... ...
... ... @@ -2,8 +2,9 @@ import { BasicColumn } from '/@/components/Table';
2 2 import { FormSchema } from '/@/components/Table';
3 3 import { findDictItemByCode } from '/@/api/system/dict';
4 4 import { MessageEnum } from '/@/enums/messageEnum';
5   -import { getOrganizationList } from '/@/api/system/system';
6   -import { copyTransFun } from '/@/utils/fnUtils';
  5 +// import { getOrganizationList } from '/@/api/system/system';
  6 +// import { copyTransFun } from '/@/utils/fnUtils';
  7 +import { numberRule } from '/@/utils/rules';
7 8
8 9 export const columns: BasicColumn[] = [
9 10 {
... ... @@ -30,23 +31,27 @@ export const columns: BasicColumn[] = [
30 31
31 32 export const searchFormSchema: FormSchema[] = [
32 33 {
33   - field: 'organizationId',
34   - label: '请选择组织',
35   - component: 'ApiTreeSelect',
36   - colProps: { span: 6 },
37   - componentProps: {
38   - api: async () => {
39   - const data = await getOrganizationList();
40   - copyTransFun(data as any as any[]);
41   - return data;
42   - },
43   - },
44   - },
45   - {
46 34 field: 'name',
47 35 label: '配置名称',
48 36 component: 'Input',
49 37 colProps: { span: 8 },
  38 + componentProps: {
  39 + maxLength: 255,
  40 + placeholder: '请输入配置名称',
  41 + },
  42 + dynamicRules: () => {
  43 + return [
  44 + {
  45 + required: false,
  46 + validator: (_, value) => {
  47 + if (String(value).length > 255) {
  48 + return Promise.reject('字数不超过255个字');
  49 + }
  50 + return Promise.resolve();
  51 + },
  52 + },
  53 + ];
  54 + },
50 55 },
51 56 ];
52 57
... ... @@ -63,6 +68,10 @@ export const formSchema: FormSchema[] = [
63 68 label: '配置名称',
64 69 required: true,
65 70 component: 'Input',
  71 + componentProps: {
  72 + maxLength: 255,
  73 + placeholder: '请输入配置名称',
  74 + },
66 75 },
67 76 {
68 77 field: 'messageType',
... ... @@ -98,6 +107,11 @@ export const formSchema: FormSchema[] = [
98 107 label: 'accessKeyId',
99 108 required: true,
100 109 component: 'Input',
  110 + componentProps: {
  111 + maxLength: 36,
  112 + placeholder: '请输入accessKeyId',
  113 + },
  114 +
101 115 ifShow: ({ values }) => isMessage(Reflect.get(values, 'messageType')),
102 116 },
103 117 {
... ... @@ -105,6 +119,11 @@ export const formSchema: FormSchema[] = [
105 119 label: 'accessKeySecret',
106 120 required: true,
107 121 component: 'Input',
  122 + componentProps: {
  123 + maxLength: 36,
  124 + placeholder: '请输入accessKeySecret',
  125 + },
  126 +
108 127 ifShow: ({ values }) => isMessage(Reflect.get(values, 'messageType')),
109 128 },
110 129 {
... ... @@ -113,6 +132,11 @@ export const formSchema: FormSchema[] = [
113 132 defaultValue: 'smtp.163.com',
114 133 required: true,
115 134 component: 'Input',
  135 + componentProps: {
  136 + maxLength: 36,
  137 + placeholder: '请输入accessKeySecret',
  138 + },
  139 +
116 140 ifShow: ({ values }) => isEmail(Reflect.get(values, 'messageType')),
117 141 },
118 142 {
... ... @@ -121,6 +145,12 @@ export const formSchema: FormSchema[] = [
121 145 defaultValue: 25,
122 146 required: true,
123 147 component: 'InputNumber',
  148 + rules: numberRule,
  149 + componentProps: {
  150 + maxLength: 36,
  151 + placeholder: '请输入端口',
  152 + },
  153 +
124 154 ifShow: ({ values }) => isEmail(Reflect.get(values, 'messageType')),
125 155 },
126 156 {
... ... @@ -128,6 +158,11 @@ export const formSchema: FormSchema[] = [
128 158 label: '用户名',
129 159 required: true,
130 160 component: 'Input',
  161 + componentProps: {
  162 + maxLength: 255,
  163 + placeholder: '请输入用户名',
  164 + },
  165 +
131 166 ifShow: ({ values }) => isEmail(Reflect.get(values, 'messageType')),
132 167 },
133 168 {
... ... @@ -142,12 +177,46 @@ export const formSchema: FormSchema[] = [
142 177 label: '消息配置',
143 178 component: 'Input',
144 179 show: false,
  180 + componentProps: {
  181 + maxLength: 255,
  182 + placeholder: '请输入消息配置',
  183 + },
  184 + dynamicRules: () => {
  185 + return [
  186 + {
  187 + required: false,
  188 + validator: (_, value) => {
  189 + if (String(value).length > 255) {
  190 + return Promise.reject('字数不超过255个字');
  191 + }
  192 + return Promise.resolve();
  193 + },
  194 + },
  195 + ];
  196 + },
145 197 },
146 198 {
147 199 field: 'id',
148 200 label: '主键',
149 201 component: 'Input',
150 202 show: false,
  203 + componentProps: {
  204 + maxLength: 36,
  205 + placeholder: '请输入主键',
  206 + },
  207 + dynamicRules: () => {
  208 + return [
  209 + {
  210 + required: false,
  211 + validator: (_, value) => {
  212 + if (String(value).length > 36) {
  213 + return Promise.reject('字数不超过36个字');
  214 + }
  215 + return Promise.resolve();
  216 + },
  217 + },
  218 + ];
  219 + },
151 220 },
152 221 {
153 222 field: 'status',
... ... @@ -165,5 +234,22 @@ export const formSchema: FormSchema[] = [
165 234 label: '备注',
166 235 field: 'remark',
167 236 component: 'InputTextArea',
  237 + componentProps: {
  238 + maxLength: 255,
  239 + placeholder: '请输入备注',
  240 + },
  241 + dynamicRules: () => {
  242 + return [
  243 + {
  244 + required: false,
  245 + validator: (_, value) => {
  246 + if (String(value).length > 255) {
  247 + return Promise.reject('字数不超过255个字');
  248 + }
  249 + return Promise.resolve();
  250 + },
  251 + },
  252 + ];
  253 + },
168 254 },
169 255 ];
... ...
... ... @@ -21,65 +21,69 @@
21 21 /></TabPane>
22 22 <TabPane key="3" tab="报警规则">
23 23 <div style="padding-top: 10px">
24   - <div style="border-radius: 10px; border: 1px solid grey">
25   - <BasicForm
26   - :showSubmitButton="false"
27   - :showResetButton="false"
28   - @register="registerStep3Schemas"
29   - />
30   - <BasicForm
31   - :showSubmitButton="false"
32   - :showResetButton="false"
33   - @register="registerStep3HighSetting"
34   - />
35   - <BasicForm
36   - :showSubmitButton="false"
37   - :showResetButton="false"
38   - @register="registerStep3CreateAlarm"
39   - />
40   - <BasicForm
41   - :showSubmitButton="false"
42   - :showResetButton="false"
43   - @register="registerStep3RuleAlarm"
44   - />
45   - <BasicForm
46   - :showSubmitButton="false"
47   - :showResetButton="false"
48   - @register="registerStep3Condition"
49   - />
50   - <BasicForm
51   - :showSubmitButton="false"
52   - :showResetButton="false"
53   - @register="registerStep3Enable"
54   - />
55   - <BasicForm
56   - :showSubmitButton="false"
57   - :showResetButton="false"
58   - @register="registerStep3TemplateDetail"
59   - />
  24 + <div style="border-radius: 10px; border: 1px solid #f0f0f0">
  25 + <div style="margin-left: 15px">
  26 + <BasicForm
  27 + :showSubmitButton="false"
  28 + :showResetButton="false"
  29 + @register="registerStep3Schemas"
  30 + />
  31 + <BasicForm
  32 + :showSubmitButton="false"
  33 + :showResetButton="false"
  34 + @register="registerStep3HighSetting"
  35 + />
  36 + <BasicForm
  37 + :showSubmitButton="false"
  38 + :showResetButton="false"
  39 + @register="registerStep3CreateAlarm"
  40 + />
  41 + <BasicForm
  42 + :showSubmitButton="false"
  43 + :showResetButton="false"
  44 + @register="registerStep3RuleAlarm"
  45 + />
  46 + <BasicForm
  47 + :showSubmitButton="false"
  48 + :showResetButton="false"
  49 + @register="registerStep3Condition"
  50 + />
  51 + <BasicForm
  52 + :showSubmitButton="false"
  53 + :showResetButton="false"
  54 + @register="registerStep3Enable"
  55 + />
  56 + <BasicForm
  57 + :showSubmitButton="false"
  58 + :showResetButton="false"
  59 + @register="registerStep3TemplateDetail"
  60 + />
  61 + </div>
60 62 </div>
61   - <div style="border-radius: 10px; border: 1px solid grey; margin-top: 15px">
  63 + <div style="border-radius: 10px; border: 1px solid #f0f0f0; margin-top: 15px">
62 64 <p>清除报警规则</p>
63   - <BasicForm
64   - :showSubmitButton="false"
65   - :showResetButton="false"
66   - @register="registerStep3ClearRuleAlarm"
67   - />
68   - <BasicForm
69   - :showSubmitButton="false"
70   - :showResetButton="false"
71   - @register="registerStep3ClearCondition"
72   - />
73   - <BasicForm
74   - :showSubmitButton="false"
75   - :showResetButton="false"
76   - @register="registerStep3ClearEnable"
77   - />
78   - <BasicForm
79   - :showSubmitButton="false"
80   - :showResetButton="false"
81   - @register="registerStep3ClearTemplateDetail"
82   - />
  65 + <div style="margin-left: 15px">
  66 + <BasicForm
  67 + :showSubmitButton="false"
  68 + :showResetButton="false"
  69 + @register="registerStep3ClearRuleAlarm"
  70 + />
  71 + <BasicForm
  72 + :showSubmitButton="false"
  73 + :showResetButton="false"
  74 + @register="registerStep3ClearCondition"
  75 + />
  76 + <BasicForm
  77 + :showSubmitButton="false"
  78 + :showResetButton="false"
  79 + @register="registerStep3ClearEnable"
  80 + />
  81 + <BasicForm
  82 + :showSubmitButton="false"
  83 + :showResetButton="false"
  84 + @register="registerStep3ClearTemplateDetail"
  85 + />
  86 + </div>
83 87 </div>
84 88 </div>
85 89 </TabPane>
... ... @@ -93,7 +97,7 @@
93 97 <script lang="ts">
94 98 import { defineComponent, ref, computed, watch } from 'vue';
95 99 import { BasicModal, useModalInner } from '/@/components/Modal';
96   - import { Tabs, TabPane } from 'ant-design-vue';
  100 + import { Tabs } from 'ant-design-vue';
97 101 import { deviceConfigGetDetail } from '/@/api/device/deviceConfigApi';
98 102 import { BasicForm, useForm } from '/@/components/Form/index';
99 103 import {
... ... @@ -116,14 +120,14 @@
116 120
117 121 export default defineComponent({
118 122 name: 'ConfigDrawer',
119   - components: { Tabs, TabPane, BasicModal, BasicForm },
  123 + components: { Tabs, TabPane: Tabs.TabPane, BasicModal, BasicForm },
120 124 emits: ['success', 'register'],
121 125 setup() {
122 126 const activeKey = ref('1');
123 127 const isUpdate = ref(true);
124 128 const descInfo: any = ref(null);
125 129 const dataInfo: any = ref('');
126   - const [registerDetail, { setFieldsValue: setRegisterDetail }] = useForm({
  130 + const [registerDetail, { resetFields, setFieldsValue: setRegisterDetail }] = useForm({
127 131 schemas: step1Schemas,
128 132 actionColOptions: {
129 133 span: 24,
... ... @@ -219,8 +223,10 @@
219 223 const [register] = useModalInner(async (data) => {
220 224 activeKey.value = '1';
221 225 isUpdate.value = !!data?.isUpdate;
222   - descInfo.value = await deviceConfigGetDetail(data.record.id);
  226 + const getV = await deviceConfigGetDetail(data.record.id);
  227 + descInfo.value = getV;
223 228 try {
  229 + resetFields();
224 230 await setRegisterDetail({ ...descInfo.value });
225 231 } catch (e) {
226 232 return e;
... ... @@ -229,84 +235,90 @@
229 235 const handleChange = (v) => {
230 236 try {
231 237 switch (v) {
232   - case '1':
233   - setRegisterDetail({ ...descInfo.value });
234   - break;
  238 + // case '1':
  239 + // setRegisterDetail({ ...descInfo.value });
  240 + // break;
235 241 case '2':
236 242 setRegisterTrans({
237 243 transportType: descInfo.value.profileData?.transportConfiguration.type,
238 244 });
239 245 break;
240 246 case '3':
241   - setRegisterStep3Schemas({
242   - alarmType: descInfo.value.profileData?.alarms[0].alarmType,
243   - });
244   - setRegisterStep3HighSetting({
245   - propagate: descInfo.value.profileData?.alarms[0].propagate,
246   - propagateRelationTypes:
247   - descInfo.value.profileData?.alarms[0].propagateRelationTypes,
248   - });
249   - const getKey = Object.keys(descInfo.value.profileData?.alarms[0].createRules);
250   - setRegisterStep3CreateAlarm({
251   - default: getKey[0],
252   - });
253   - setRegisterStep3RuleAlarm({
254   - type: descInfo.value.profileData?.alarms[0].createRules[getKey[0]].condition
255   - .condition[0].key.type,
256   - key1: descInfo.value.profileData?.alarms[0].createRules[getKey[0]].condition
257   - .condition[0].key.key,
258   - type1:
259   - descInfo.value.profileData?.alarms[0].createRules[getKey[0]].condition
260   - .condition[0].valueType,
261   - value1:
262   - descInfo.value.profileData?.alarms[0].createRules[getKey[0]].condition
263   - .condition[0].predicate.value.defaultValue,
264   - operation:
265   - descInfo.value.profileData?.alarms[0].createRules[getKey[0]].condition
266   - .condition[0].predicate.operation,
267   - });
268   - setRegisterStep3Condition({
269   - conditionType:
270   - descInfo.value.profileData?.alarms[0].createRules[getKey[0]].condition.spec.type,
271   - });
272   - setRegisterStep3Enable({
273   - schedule:
274   - descInfo.value.profileData?.alarms[0].createRules[getKey[0]].schedule.type,
275   - });
276   - setRegisterStep3TemplateDetail({
277   - alarmDetails:
278   - descInfo.value.profileData?.alarms[0].createRules[getKey[0]].alarmDetails,
279   - });
280   - //清楚报警
281   - setRegisterStep3ClearRuleAlarm({
282   - type: descInfo.value.profileData?.alarms[0].clearRule.condition.condition[0].key
283   - .type,
284   - key1: descInfo.value.profileData?.alarms[0].clearRule.condition.condition[0].key
285   - .key,
286   - type1:
287   - descInfo.value.profileData?.alarms[0].clearRule.condition.condition[0].valueType,
288   - value1:
289   - descInfo.value.profileData?.alarms[0].clearRule.condition.condition[0].predicate
290   - .value.defaultValue,
291   - operation:
292   - descInfo.value.profileData?.alarms[0].clearRule.condition.condition[0].predicate
293   - .operation,
294   - });
295   - setRegisterStep3ClearCondition({
296   - conditionType: descInfo.value.profileData?.alarms[0].clearRule.condition.spec.type,
297   - });
298   - setRegisterStep3ClearEnable({
299   - schedule: descInfo.value.profileData?.alarms[0].clearRule.schedule.type,
300   - });
301   - setRegisterStep3ClearTemplateDetail({
302   - alarmDetails: descInfo.value.profileData?.alarms[0].clearRule.alarmDetails,
303   - });
304   - break;
  247 + setTimeout(() => {
  248 + setRegisterStep3Schemas({
  249 + alarmType: descInfo.value.profileData?.alarms[0]?.alarmType,
  250 + });
  251 + setRegisterStep3HighSetting({
  252 + propagate: descInfo.value.profileData?.alarms[0]?.propagate,
  253 + propagateRelationTypes:
  254 + descInfo.value.profileData?.alarms[0]?.propagateRelationTypes,
  255 + });
  256 + const getKey = Object.keys(descInfo.value.profileData?.alarms[0]?.createRules);
  257 + setRegisterStep3CreateAlarm({
  258 + default: getKey[0],
  259 + });
  260 + setRegisterStep3RuleAlarm({
  261 + type: descInfo.value.profileData?.alarms[0].createRules[getKey[0]].condition
  262 + .condition[0].key.type,
  263 + key1: descInfo.value.profileData?.alarms[0].createRules[getKey[0]].condition
  264 + .condition[0].key.key,
  265 + type1:
  266 + descInfo.value.profileData?.alarms[0].createRules[getKey[0]].condition
  267 + .condition[0].valueType,
  268 + value1:
  269 + descInfo.value.profileData?.alarms[0].createRules[getKey[0]].condition
  270 + .condition[0].predicate.value.defaultValue,
  271 + operation:
  272 + descInfo.value.profileData?.alarms[0].createRules[getKey[0]].condition
  273 + .condition[0].predicate.operation,
  274 + });
  275 + setRegisterStep3Condition({
  276 + conditionType:
  277 + descInfo.value.profileData?.alarms[0].createRules[getKey[0]].condition.spec
  278 + .type,
  279 + });
  280 + setRegisterStep3Enable({
  281 + schedule:
  282 + descInfo.value.profileData?.alarms[0].createRules[getKey[0]].schedule.type,
  283 + });
  284 + setRegisterStep3TemplateDetail({
  285 + alarmDetails:
  286 + descInfo.value.profileData?.alarms[0].createRules[getKey[0]].alarmDetails,
  287 + });
  288 + //清除报警
  289 + setRegisterStep3ClearRuleAlarm({
  290 + type: descInfo.value.profileData?.alarms[0].clearRule.condition.condition[0].key
  291 + .type,
  292 + key1: descInfo.value.profileData?.alarms[0].clearRule.condition.condition[0].key
  293 + .key,
  294 + type1:
  295 + descInfo.value.profileData?.alarms[0].clearRule.condition.condition[0]
  296 + .valueType,
  297 + value1:
  298 + descInfo.value.profileData?.alarms[0].clearRule.condition.condition[0].predicate
  299 + .value.defaultValue,
  300 + operation:
  301 + descInfo.value.profileData?.alarms[0].clearRule.condition.condition[0].predicate
  302 + .operation,
  303 + });
  304 + setRegisterStep3ClearCondition({
  305 + conditionType:
  306 + descInfo.value.profileData?.alarms[0].clearRule.condition.spec.type,
  307 + });
  308 + setRegisterStep3ClearEnable({
  309 + schedule: descInfo.value.profileData?.alarms[0].clearRule.schedule.type,
  310 + });
  311 + setRegisterStep3ClearTemplateDetail({
  312 + alarmDetails: descInfo.value.profileData?.alarms[0].clearRule.alarmDetails,
  313 + });
  314 + }, 1000);
305 315 case '4':
306   - setRegisterContact({
307   - alarmContactId: descInfo.value.alarmProfile.alarmContactId,
308   - messageMode: descInfo.value.alarmProfile.messageMode,
309   - });
  316 + setTimeout(() => {
  317 + setRegisterContact({
  318 + alarmContactId: descInfo.value?.alarmProfile?.alarmContactId,
  319 + messageMode: descInfo.value?.alarmProfile?.messageMode,
  320 + });
  321 + }, 1000);
310 322 break;
311 323 }
312 324 } catch (e) {
... ...
... ... @@ -45,12 +45,12 @@
45 45 </BasicTable>
46 46 <DeviceProfileModal v-if="isJudgeStatus" @register="registerModal" @success="handleSuccess" />
47 47 <DeviceConfigDetail @register="registerModalDetail" @success="handleSuccess" />
48   - <ExpExcelModal @register="register1" @success="defaultHeader" />
  48 + <!-- <ExpExcelModal @register="register1" @success="defaultHeader" /> -->
49 49 </div>
50 50 </template>
51 51 <script lang="ts">
52 52 import { defineComponent, ref, reactive } from 'vue';
53   - import { BasicTable, useTable, TableAction } from '/@/components/Table';
  53 + import { BasicTable, useTable, TableAction, BasicColumn } from '/@/components/Table';
54 54 import { columns, searchFormSchema } from './device.profile.data';
55 55 import { useMessage } from '/@/hooks/web/useMessage';
56 56 import { deviceConfigGetQuery, deviceConfigDelete } from '/@/api/device/deviceConfigApi';
... ... @@ -58,7 +58,7 @@
58 58 import DeviceProfileModal from '/@/views/device/profile/DeviceProfileModal.vue';
59 59 import DeviceConfigDetail from '/@/views/device/profile/deviceConfigDetail.vue';
60 60 import { ImpExcel, ExcelData } from '/@/components/Excel';
61   - import { jsonToSheetXlsx, ExportModalResult } from '/@/components/Excel';
  61 + // import { jsonToSheetXlsx, ExportModalResult } from '/@/components/Excel';
62 62
63 63 export default defineComponent({
64 64 name: 'DeviceProfileManagement',
... ... @@ -156,23 +156,23 @@
156 156 isJudgeStatus.value = false;
157 157 }
158 158
159   - function defaultHeader({ filename, bookType }: ExportModalResult) {
160   - // 默认Object.keys(data[0])作为header
161   - jsonToSheetXlsx({
162   - data,
163   - filename,
164   - write2excelOpts: {
165   - bookType,
166   - },
167   - });
168   - }
  159 + // function defaultHeader({ filename, bookType }: ExportModalResult) {
  160 + // // 默认Object.keys(data[0])作为header
  161 + // jsonToSheetXlsx({
  162 + // data,
  163 + // filename,
  164 + // write2excelOpts: {
  165 + // bookType,
  166 + // },
  167 + // });
  168 + // }
169 169
170   - const [register1, { openModal: openModalExcel }] = useModal();
  170 + // const [register1, { openModal: openModalExcel }] = useModal();
171 171 const handleExport = (record: Recordable) => {
172 172 console.log(record);
173   - setTimeout(() => {
174   - openModalExcel();
175   - }, 50);
  173 + // setTimeout(() => {
  174 + // openModalExcel();
  175 + // }, 50);
176 176 };
177 177 function handleImport() {
178 178 console.log('record');
... ... @@ -182,8 +182,8 @@
182 182 }
183 183 return {
184 184 registerModalDetail,
185   - register1,
186   - defaultHeader,
  185 + // register1,
  186 + // defaultHeader,
187 187 useSelectionChange,
188 188 handleTableDel,
189 189 isJudgeStatus,
... ...
1 1 <template>
2 2 <div class="step1">
3 3 <div class="step1-form">
  4 + <div
  5 + style="
  6 + width: 12vw;
  7 + height: 24vh;
  8 + margin-left: 25px;
  9 + display: flex;
  10 + justify-content: space-between;
  11 + align-items: center;
  12 + "
  13 + >
  14 + <div style="width: 4vw; height: 24vh">请上传图片</div>
  15 + <div style="width: 8vw; height: 24vh">
  16 + <Upload
  17 + style="width: 20vw"
  18 + name="avatar"
  19 + list-type="picture-card"
  20 + class="avatar-uploader"
  21 + :show-upload-list="false"
  22 + :customRequest="customUploadqrcodePic"
  23 + :before-upload="beforeUploadqrcodePic"
  24 + >
  25 + <img
  26 + style="text-align: center; border-radius: 50%; width: 10vw; height: 15vh"
  27 + v-if="peresonalPic"
  28 + :src="peresonalPic"
  29 + alt="avatar"
  30 + />
  31 + <div v-else>
  32 + <div style="margin-top: 30px">
  33 + <PlusOutlined style="font-size: 30px" />
  34 + </div>
  35 + <div
  36 + class="ant-upload-text flex"
  37 + style="width: 280px; height: 130px; align-items: center"
  38 + >
  39 + 支持.PNG、.JPG、.JPEG格式,建议大小不超过2M。</div
  40 + >
  41 + </div>
  42 + </Upload>
  43 + </div>
  44 + </div>
4 45 <BasicForm @register="register" />
5 46 </div>
6 47 </div>
7 48 </template>
8 49 <script lang="ts">
9   - import { defineComponent } from 'vue';
  50 + import { defineComponent, ref } from 'vue';
10 51 import { BasicForm, useForm } from '/@/components/Form';
11 52 import { step1Schemas } from './data';
12 53 import { Select, Input, Divider } from 'ant-design-vue';
  54 + import { uploadApi } from '/@/api/personal/index';
  55 + import { Upload } from 'ant-design-vue';
  56 + import { PlusOutlined } from '@ant-design/icons-vue';
  57 + import { useMessage } from '/@/hooks/web/useMessage';
  58 + import type { FileItem } from '/@/components/Upload/src/typing';
13 59
14 60 export default defineComponent({
15 61 components: {
... ... @@ -18,9 +64,13 @@
18 64 [Input.name]: Input,
19 65 [Input.Group.name]: Input.Group,
20 66 [Divider.name]: Divider,
  67 + Upload,
  68 + PlusOutlined,
21 69 },
22 70 emits: ['next', 'resetFunc'],
23 71 setup(_, { emit }) {
  72 + const { createMessage } = useMessage();
  73 +
24 74 const [register, { validate, setFieldsValue, resetFields }] = useForm({
25 75 labelWidth: 100,
26 76 schemas: step1Schemas,
... ... @@ -36,16 +86,59 @@
36 86 const resetFieldsFunc = (v) => {
37 87 setFieldsValue(v);
38 88 };
  89 + const peresonalPic = ref();
  90 +
  91 + const resetIconFunc = () => {
  92 + peresonalPic.value = '';
  93 + };
  94 +
  95 + const editIconFunc = (v) => {
  96 + peresonalPic.value = v;
  97 + };
  98 +
  99 + const customUploadqrcodePic = async ({ file }) => {
  100 + if (beforeUploadqrcodePic(file)) {
  101 + const formData = new FormData();
  102 + formData.append('file', file);
  103 + const response = await uploadApi(formData);
  104 + if (response.fileStaticUri) {
  105 + peresonalPic.value = response.fileStaticUri;
  106 + }
  107 + }
  108 + };
  109 +
  110 + const beforeUploadqrcodePic = (file: FileItem) => {
  111 + const isJpgOrPng =
  112 + file.type === 'image/jpeg' || file.type === 'image/png' || file.type === 'image/jpg';
  113 + if (!isJpgOrPng) {
  114 + createMessage.error('只能上传图片文件!');
  115 + }
  116 + const isLt2M = (file.size as number) / 1024 / 1024 < 2;
  117 + if (!isLt2M) {
  118 + createMessage.error('图片大小不能超过5MB!');
  119 + }
  120 + return isJpgOrPng && isLt2M;
  121 + };
39 122 async function customSubmitFunc() {
40 123 try {
41 124 const values = await validate();
42   - emit('next', values);
  125 + emit('next', values, peresonalPic.value);
43 126 } catch (error) {}
44 127 }
45 128 const customResetFunc = async () => {
46 129 await resetFields();
47 130 };
48   - return { register, resetFieldsFunc, customResetFunc };
  131 + return {
  132 + editIconFunc,
  133 + resetIconFunc,
  134 + register,
  135 + resetFieldsFunc,
  136 + customResetFunc,
  137 + uploadApi,
  138 + peresonalPic,
  139 + beforeUploadqrcodePic,
  140 + customUploadqrcodePic,
  141 + };
49 142 },
50 143 });
51 144 </script>
... ...
... ... @@ -31,7 +31,7 @@
31 31 <template v-for="(childItem, createIndex) in item.alarms" :key="childItem.id">
32 32 <div class="aic" style="border: 1px solid #bfbfbf">
33 33 <div class="w-3/4">
34   - <div style="margin-left: -30px; margin-top: 20px"
  34 + <div style="margin-left: -33px; margin-top: 20px"
35 35 ><BasicForm @register="registerFormCreateAlarm" />
36 36 </div>
37 37 <div style="margin-left: 5px; margin-top: -50px">
... ... @@ -78,7 +78,7 @@
78 78 <div style="height: 20px"></div>
79 79 <p>清除报警规则</p>
80 80 <template
81   - v-for="(childClearItem, clearIndexItem) in item.alarms[clearIndex].clearRule"
  81 + v-for="(childClearItem, clearIndexItem) in item.clearRule"
82 82 :key="childClearItem.id"
83 83 >
84 84 <div class="aic mb-1" style="border: 1px solid #bfbfbf">
... ... @@ -222,12 +222,20 @@
222 222 const ruleClearTemplateData: any = ref(null);
223 223 const enableClearTemplateData: any = ref(null);
224 224 const detailClearTemplateData: any = ref(null);
  225 + const scheduleCustomValue: any = ref({});
  226 + const scheduleCustomClearValue: any = ref({});
225 227 const clearIndex = ref(-1);
  228 + const getSchduleCustomValue: any = ref([]);
  229 + const getSchduleClearCustomValue: any = ref([]);
226 230 //告警列表
227 231 let profileData = ref<IProfileData[]>([]);
228 232 const log = (e) => {
229 233 console.log(e);
230 234 };
  235 + //编辑清空操作
  236 + const clearProfileDataFunc = () => {
  237 + unref(profileData).splice(0, 1);
  238 + };
231 239 //删除告警配置
232 240 const deleteAlarmRule = (index: number) => {
233 241 unref(profileData).splice(index, 1);
... ... @@ -254,26 +262,27 @@
254 262 id: Date.now() + Math.random() + '',
255 263 alarmType: '',
256 264 createRules: {},
257   - clearRule: [
258   - {
259   - id: Date.now() + Math.random() + '',
260   - alarmDetails: '',
261   - dashboardId: {
262   - id: '',
263   - entityType: '',
264   - },
265   - propagate: '',
266   - propagateRelationTypes: [''],
267   - schedule: {
268   - type: 'string',
269   - },
270   - condition: {},
271   - },
272   - ],
  265 +
273 266 propagate: false,
274 267 propagateRelationTypes: [''],
275 268 },
276 269 ],
  270 + clearRule: [
  271 + {
  272 + id: Date.now() + Math.random() + '',
  273 + alarmDetails: '',
  274 + dashboardId: {
  275 + id: '',
  276 + entityType: '',
  277 + },
  278 + propagate: '',
  279 + propagateRelationTypes: [''],
  280 + schedule: {
  281 + type: 'string',
  282 + },
  283 + condition: {},
  284 + },
  285 + ],
277 286 });
278 287 };
279 288 //TODO Mobile dashboard:
... ... @@ -342,6 +351,25 @@
342 351 const resetRegisterFormCreateAlarmFunc = () => {
343 352 resetRegisterFormCreateAlarm();
344 353 };
  354 + const resetRulesFormDataFunc = () => {
  355 + ruleTemplateData.value = ``;
  356 + };
  357 + const resetEnableFormDataFunc = () => {
  358 + enableTemplateData.value = ``;
  359 + };
  360 + const resetTemplateFormDataFunc = () => {
  361 + detailTemplateData.value = ``;
  362 + };
  363 + const resetRulesClearFormDataFunc = () => {
  364 + ruleClearTemplateData.value = ``;
  365 + };
  366 + const resetEnableClearFormDataFunc = () => {
  367 + enableClearTemplateData.value = ``;
  368 + };
  369 + const resetTemplateClearFormDataFunc = () => {
  370 + detailClearTemplateData.value = ``;
  371 + };
  372 +
345 373 //回显表单数据
346 374 const retryRegisterFormFunc = (v) => {
347 375 setRegisterForm(v);
... ... @@ -352,6 +380,25 @@
352 380 const retryRegisterFormCreateAlarmFunc = (v) => {
353 381 setRegisterFormCreateAlarm(v);
354 382 };
  383 + const retryRulesFormDataFunc = (v) => {
  384 + ruleTemplateData.value = v;
  385 + };
  386 + const retryEnableFormDataFunc = (v) => {
  387 + enableTemplateData.value = v;
  388 + };
  389 + const retryTemplateFormDataFunc = (v) => {
  390 + detailTemplateData.value = v;
  391 + };
  392 + const retryRulesClearFormDataFunc = (v) => {
  393 + ruleClearTemplateData.value = v;
  394 + };
  395 + const retryEnableClearFormDataFunc = (v) => {
  396 + enableClearTemplateData.value = v;
  397 + };
  398 + const retryTemplateClearFormDataFunc = (v) => {
  399 + detailClearTemplateData.value = v;
  400 + };
  401 +
355 402 const tempValue1: string = ref<string>('');
356 403 // 添加‘创建条件’
357 404 const addCreateRole = (index: number) => {
... ... @@ -400,27 +447,26 @@
400 447 };
401 448 },
402 449 });
403   -
404 450 unref(profileData)[index].alarms.push({
405 451 id: Date.now() + Math.random() + '',
406 452 alarmType: '',
407 453 createRules: {},
408   - clearRule: [
409   - {
410   - id: Date.now() + Math.random() + '',
411   - alarmDetails: '',
412   - dashboardId: {
413   - id: '',
414   - entityType: '',
415   - },
416   - propagate: '',
417   - propagateRelationTypes: [''],
418   - schedule: {
419   - type: 'string',
420   - },
421   - condition: {},
422   - },
423   - ],
  454 + // clearRule: [
  455 + // {
  456 + // id: Date.now() + Math.random() + '',
  457 + // alarmDetails: '',
  458 + // dashboardId: {
  459 + // id: '',
  460 + // entityType: '',
  461 + // },
  462 + // propagate: '',
  463 + // propagateRelationTypes: [''],
  464 + // schedule: {
  465 + // type: 'string',
  466 + // },
  467 + // condition: {},
  468 + // },
  469 + // ],
424 470 propagate: false,
425 471 propagateRelationTypes: [''],
426 472 });
... ... @@ -486,12 +532,23 @@
486 532 console.log(e);
487 533 }
488 534 });
  535 + const findDayCustomByValue = findDay.map((f, i) => {
  536 + try {
  537 + if (f.value == v.daysOfWeek1[i]) {
  538 + return f.label;
  539 + }
  540 + } catch (e) {
  541 + console.log(e);
  542 + }
  543 + });
489 544 enableTemplateData.value =
490   - v.startsOn == undefined
  545 + v.schedule == 'ANY_TIME'
491 546 ? `始终启用`
492   - : `
  547 + : v.schedule == 'SPECIFIC_TIME'
  548 + ? `
493 549 开始时间:${v.startsOn},结束时间:${v.endsOn},天数:${findDayByValue}
494   - `;
  550 + `
  551 + : `天数:${findDayCustomByValue},开始时间: ${v.startsOn1},结束时间:${v.endsOn1}`;
495 552 };
496 553 //规则条件
497 554 const getAllFieldsRuleFunc = (v, v1) => {
... ... @@ -516,7 +573,7 @@
516 573 }
517 574 });
518 575 ruleTemplateData.value = `
519   - 键名:${v.key1}...操作:${findRuleByValue?.label}...值:${v.value1}
  576 + 键名:${v.key1} 操作:${findRuleByValue?.label} 值:${v.value1}
520 577 `;
521 578
522 579 ruleLastObj.value = v1;
... ... @@ -607,11 +664,23 @@
607 664 console.log(e);
608 665 }
609 666 });
  667 + const findDayCustomByValue = findDay.map((f, i) => {
  668 + try {
  669 + if (f.value == v.daysOfWeek1[i]) {
  670 + return f.label;
  671 + }
  672 + } catch (e) {
  673 + console.log(e);
  674 + }
  675 + });
610 676 enableClearTemplateData.value =
611   - v.startsOn == undefined
  677 + v.schedule == 'ANY_TIME'
612 678 ? `始终启用`
613   - : `开始时间:${v.startsOn},结束时间:${v.endsOn},天数:${findDayByValue}
614   - `;
  679 + : v.schedule == 'SPECIFIC_TIME'
  680 + ? `
  681 + 开始时间:${v.startsOn},结束时间:${v.endsOn},天数:${findDayByValue}
  682 + `
  683 + : `天数:${findDayCustomByValue},开始时间: ${v.startsOn1},结束时间:${v.endsOn1}`;
615 684 };
616 685 //规则条件
617 686 const getAllClearFieldsRuleFunc = (v, v1) => {
... ... @@ -636,7 +705,7 @@
636 705 }
637 706 });
638 707 ruleClearTemplateData.value = `
639   - 键名:${v.key1}...操作:${findRuleByValue?.label}...值:${v.value1}
  708 + 键名:${v.key1} 操作:${findRuleByValue?.label} 值:${v.value1}
640 709 `;
641 710
642 711 ruleLastObj.value = v1;
... ... @@ -677,7 +746,7 @@
677 746 };
678 747 Object.assign(addClearitionalObj.value, getValueConditon);
679 748 };
680   - //用于生成uuid
  749 + //生成uuid
681 750 function generateUUID() {
682 751 let d = new Date().getTime();
683 752 if (window.performance && typeof window.performance.now === 'function') {
... ... @@ -691,28 +760,111 @@
691 760 return uuid;
692 761 }
693 762 const handleFormStep3toStep4Next = async () => {
  763 + console.log(enableObj.value);
694 764 try {
  765 + if (enableObj.value.schedule == 'CUSTOM') {
  766 + for (let i in enableObj.value) {
  767 + console.log(enableObj.value[i]);
  768 + console.log(i);
  769 + // let o = {};
  770 + // if(enableObj.value[i]=='1')
  771 + // o[i] = enableObj.value[i];
  772 + // getSchduleCustomValue.value.push(o);
  773 + // getSchduleCustomValue.value.push(enableObj.value[i]);
  774 + }
  775 + console.log(getSchduleCustomValue.value);
  776 + // switch (enableObj.value.daysOfWeek1[0]) {
  777 + // case '1':
  778 + // getSchduleCustomValue.value.push({
  779 + // enabled: true,
  780 + // dayOfWeek: enableObj.value.daysOfWeek1[0],
  781 + // startsOn: enableObj.value.startsOn1,
  782 + // endsOn: enableObj.value.endsOn1,
  783 + // });
  784 + // break;
  785 + // case '2':
  786 + // getSchduleCustomValue.value.push({
  787 + // enabled: true,
  788 + // dayOfWeek: enableObj.value.daysOfWeek2[0],
  789 + // startsOn: enableObj.value.startsOn2,
  790 + // endsOn: enableObj.value.endsOn2,
  791 + // });
  792 + // break;
  793 + // case '3':
  794 + // getSchduleCustomValue.value.push({
  795 + // enabled: true,
  796 + // dayOfWeek: enableObj.value.daysOfWeek3[0],
  797 + // startsOn: enableObj.value.startsOn3,
  798 + // endsOn: enableObj.value.endsOn3,
  799 + // });
  800 + // break;
  801 + // }
  802 + scheduleCustomValue.value = {
  803 + type: enableObj.value.schedule,
  804 + timezone: enableObj.value.timezone,
  805 + items: getSchduleCustomValue.value,
  806 + };
  807 + }
  808 + //清除报警规则---报警日程表
  809 + if (enableClearObj.value.schedule == 'CUSTOM') {
  810 + switch (enableClearObj.value.daysOfWeek1[0]) {
  811 + case '1':
  812 + getSchduleClearCustomValue.value.push({
  813 + enabled: true,
  814 + dayOfWeek: enableClearObj.value.daysOfWeek1[0],
  815 + startsOn: enableClearObj.value.startsOn1,
  816 + endsOn: enableClearObj.value.endsOn1,
  817 + });
  818 + break;
  819 + case '2':
  820 + getSchduleClearCustomValue.value.push({
  821 + enabled: true,
  822 + dayOfWeek: enableClearObj.value.daysOfWeek2[0],
  823 + startsOn: enableClearObj.value.startsOn2,
  824 + endsOn: enableClearObj.value.endsOn2,
  825 + });
  826 + break;
  827 + case '3':
  828 + getSchduleClearCustomValue.value.push({
  829 + enabled: true,
  830 + dayOfWeek: enableClearObj.value.daysOfWeek3[0],
  831 + startsOn: enableClearObj.value.startsOn3,
  832 + endsOn: enableClearObj.value.endsOn3,
  833 + });
  834 + break;
  835 + }
  836 + scheduleCustomClearValue.value = {
  837 + type: enableClearObj.value.schedule,
  838 + timezone: enableClearObj.value.timezone,
  839 + items: getSchduleClearCustomValue.value,
  840 + };
  841 + }
695 842 const scheduleClearValue = {
696 843 type: enableClearObj.value.schedule,
697 844 daysOfWeek: enableClearObj.value.daysOfWeek,
698   - // endsOn: enableClearObj.value.endsOn,
699   - // startsOn: enableClearObj.value.startOn,
  845 + endsOn: enableClearObj.value.endsOn,
  846 + startsOn: enableClearObj.value.startOn,
700 847 timezone: enableClearObj.value.timezone,
701 848 };
702 849 const getClearSchedule = {
703   - schedule: scheduleClearValue,
  850 + schedule:
  851 + enableClearObj.value.schedule == 'CUSTOM'
  852 + ? scheduleCustomClearValue.value
  853 + : scheduleClearValue,
704 854 };
705 855 const getClearAdditionalProp = Object.assign({}, detailClearObj.value, getClearSchedule);
706 856 const scheduleValue = {
707 857 type: enableObj.value.schedule,
708 858 daysOfWeek: enableObj.value.daysOfWeek,
709   - // endsOn: enableObj.value.endsOn,
710   - // startsOn: enableObj.value.startOn,
  859 + endsOn: enableObj.value.endsOn,
  860 + startsOn: enableObj.value.startOn,
711 861 timezone: enableObj.value.timezone,
712 862 };
713 863 const getSchedule = {
714   - schedule: scheduleValue,
  864 + schedule:
  865 + enableObj.value.schedule == 'CUSTOM' ? scheduleCustomValue.value : scheduleValue,
715 866 };
  867 +
716 868 const getAdditionalProp = Object.assign({}, detailObj.value, getSchedule);
717 869 const getScheduleAndAlarmDetails = Object.assign(
718 870 {},
... ... @@ -746,7 +898,7 @@
746 898 console.log(valueRegisterFormCreateAlarm);
747 899 const getValueRegisterFormHighSetting = {
748 900 propagate: valueRegisterFormHighSetting?.propagate,
749   - propagateRelationTypes: [valueRegisterFormHighSetting?.propagateRelationTypes],
  901 + propagateRelationTypes: [valueRegisterFormHighSetting?.propagateRelationTypes].flat(1),
750 902 };
751 903 Object.assign(
752 904 emptyObj.value,
... ... @@ -756,7 +908,9 @@
756 908 getClearRulesAllObj,
757 909 objectId
758 910 );
759   - alarmss.value.push(emptyObj.value);
  911 + if (alarmss.value.length == 0) {
  912 + alarmss.value.push(emptyObj.value);
  913 + }
760 914 const getAlarms = {
761 915 alarms: alarmss.value,
762 916 };
... ... @@ -795,7 +949,11 @@
795 949 isRuleAlarmRuleConditions.value = 4;
796 950 setTimeout(() => {
797 951 openModal4(true);
798   - proxy.$refs.getChildData1.resetDataFunc();
  952 + try {
  953 + proxy.$refs.getChildData1.resetDataFunc();
  954 + } catch (e) {
  955 + return e;
  956 + }
799 957 }, 50);
800 958 };
801 959 const handleOpenClearEnableRule = () => {
... ... @@ -814,6 +972,19 @@
814 972 };
815 973
816 974 return {
  975 + resetEnableClearFormDataFunc,
  976 + resetTemplateClearFormDataFunc,
  977 + resetRulesClearFormDataFunc,
  978 + resetEnableFormDataFunc,
  979 + resetTemplateFormDataFunc,
  980 + resetRulesFormDataFunc,
  981 + retryEnableClearFormDataFunc,
  982 + retryTemplateClearFormDataFunc,
  983 + retryRulesClearFormDataFunc,
  984 + retryEnableFormDataFunc,
  985 + retryTemplateFormDataFunc,
  986 + retryRulesFormDataFunc,
  987 + clearProfileDataFunc,
817 988 clearIndex,
818 989 retryRegisterFormFunc,
819 990 retryRegisterFormHighSettingmFunc,
... ...
... ... @@ -99,7 +99,7 @@ export const formSchema: FormSchema[] = [
99 99 {
100 100 field: 'conditionType',
101 101 label: '条件类型',
102   - colProps: { span: 24 },
  102 + colProps: { span: 13 },
103 103 component: 'Select',
104 104 componentProps: {
105 105 placeholder: '请选择报警日程表',
... ... @@ -177,11 +177,28 @@ export const formSchema: FormSchema[] = [
177 177 {
178 178 field: 'defaultValue',
179 179 label: '持续时间值',
180   - colProps: { span: 24 },
  180 + colProps: { span: 13 },
181 181 component: 'Input',
182 182 componentProps: {
  183 + maxLength: 16,
183 184 placeholder: '请输入持续时间值(请输入数字)',
184 185 },
  186 + dynamicRules: () => {
  187 + return [
  188 + {
  189 + validator: (_, value) => {
  190 + if (!value) {
  191 + return Promise.reject('持续时间值不能为空');
  192 + }
  193 + const pwdRegex = new RegExp(/-?\d+/);
  194 + if (!pwdRegex.test(value)) {
  195 + return Promise.reject('只能为数字,且最长不超过16位');
  196 + }
  197 + return Promise.resolve();
  198 + },
  199 + },
  200 + ];
  201 + },
185 202 ifShow: ({ values }) => isWenDu(Reflect.get(values, 'conditionType')),
186 203 show: ({ values }) => {
187 204 return !values.field5;
... ... @@ -210,7 +227,7 @@ export const formSchema: FormSchema[] = [
210 227 {
211 228 field: 'unit',
212 229 label: '时间单位',
213   - colProps: { span: 24 },
  230 + colProps: { span: 13 },
214 231 component: 'Select',
215 232 componentProps: {
216 233 placeholder: '请选择时间单位',
... ... @@ -226,12 +243,28 @@ export const formSchema: FormSchema[] = [
226 243 {
227 244 field: 'defaultValue',
228 245 label: '事件计数值必填',
229   - colProps: { span: 24 },
  246 + colProps: { span: 13 },
230 247 component: 'Input',
231 248 componentProps: {
  249 + maxLength: 2147483637,
232 250 placeholder: '请输入事件计数值(应在1到2147483637之间)',
233 251 },
234   - rules: [{ message: '事件计数应在1到2147483637之间', trigger: 'blur' }],
  252 + dynamicRules: () => {
  253 + return [
  254 + {
  255 + validator: (_, value) => {
  256 + if (!value) {
  257 + return Promise.reject('事件计数不能为空');
  258 + }
  259 + const pwdRegex = new RegExp(/-?\d+/);
  260 + if (!pwdRegex.test(value)) {
  261 + return Promise.reject('只能为数字,且最长不超过16位');
  262 + }
  263 + return Promise.resolve();
  264 + },
  265 + },
  266 + ];
  267 + },
235 268 ifShow: ({ values }) => isTimeAll(Reflect.get(values, 'conditionType')),
236 269 show: ({ values }) => {
237 270 return !values.field6;
... ... @@ -240,6 +273,7 @@ export const formSchema: FormSchema[] = [
240 273 {
241 274 field: 'inherit',
242 275 label: '',
  276 + colProps: { span: 13 },
243 277 component: 'Checkbox',
244 278 renderComponentContent: 'Inherit from owner',
245 279 ifShow: ({ values }) =>
... ...