Commit 0b3b21b771f8f320e6c820183d6a7793a00446eb

Authored by loveumiko
2 parents 3da1f60b d336bc7a

Merge branch 'main_dev' of http://git.yunteng.com/yunteng/thingskit-front into f…

…eat/device-new-protocol
Showing 35 changed files with 424 additions and 133 deletions
... ... @@ -33,6 +33,7 @@
33 33 import { openWindow } from '/@/utils';
34 34
35 35 import { useOpenKeys } from './useOpenKeys';
  36 + import { useMenuActiveFix } from '/@/hooks/business/useMenuActiveFix';
36 37 export default defineComponent({
37 38 name: 'SimpleMenu',
38 39 components: {
... ... @@ -120,7 +121,9 @@
120 121 isClickGo.value = false;
121 122 return;
122 123 }
123   - const path = (route || unref(currentRoute)).path;
  124 +
  125 + let { flag, path } = useMenuActiveFix(route || unref(currentRoute));
  126 + path = flag ? path : (route || unref(currentRoute)).path;
124 127
125 128 menuState.activeName = path;
126 129
... ...
... ... @@ -29,7 +29,6 @@ export function useOpenKeys(
29 29 return;
30 30 }
31 31 const keys = getAllParentPath(menuList, path);
32   -
33 32 if (!unref(accordion)) {
34 33 menuState.openNames = uniq([...menuState.openNames, ...keys]);
35 34 } else {
... ...
  1 +import { RouteLocationNormalizedLoaded } from 'vue-router';
  2 +
  3 +const menuMap = new Map();
  4 +
  5 +menuMap.set('/visual/board/detail/:boardId/:boardName?', '/visual/board');
  6 +
  7 +export const useMenuActiveFix = (route: RouteLocationNormalizedLoaded) => {
  8 + let flag = false;
  9 + let path;
  10 +
  11 + const matchPath = route.matched.map((item) => item.path);
  12 + const needFixMenus: string[] = Array.from(menuMap.keys());
  13 +
  14 + for (const item of matchPath) {
  15 + for (const menu of needFixMenus) {
  16 + if (menu === item) {
  17 + flag = true;
  18 + path = menuMap.get(menu);
  19 + break;
  20 + }
  21 + }
  22 + }
  23 +
  24 + return {
  25 + flag,
  26 + path,
  27 + };
  28 +};
... ...
... ... @@ -19,6 +19,7 @@
19 19 import { useRootSetting } from '/@/hooks/setting/useRootSetting';
20 20 import { useAppInject } from '/@/hooks/web/useAppInject';
21 21 import { useDesign } from '/@/hooks/web/useDesign';
  22 + import { OUTSIDE_LINK_PREFIX } from '/@/router/helper/routeHelper';
22 23
23 24 export default defineComponent({
24 25 name: 'LayoutMenu',
... ... @@ -113,6 +114,11 @@
113 114 */
114 115
115 116 function handleMenuClick(path: string) {
  117 + if (path.startsWith(OUTSIDE_LINK_PREFIX)) {
  118 + path = path.replace(OUTSIDE_LINK_PREFIX, '');
  119 + window.open(path);
  120 + return;
  121 + }
116 122 go(path);
117 123 }
118 124
... ...
... ... @@ -6,6 +6,7 @@ import { cloneDeep, omit } from 'lodash-es';
6 6 import { warn } from '/@/utils/log';
7 7 import { createRouter, createWebHashHistory } from 'vue-router';
8 8
  9 +export const OUTSIDE_LINK_PREFIX = '/outside/link';
9 10 export type LayoutMapKey = 'LAYOUT';
10 11 const IFRAME = () => import('/@/views/sys/iframe/FrameBlank.vue');
11 12
... ... @@ -81,6 +82,9 @@ export function transformObjToRoute<T = AppRouteModule>(routeList: AppRouteModul
81 82 route.meta = meta;
82 83 }
83 84 }
  85 + if (!route.path.startsWith('/') && route.meta.isLink) {
  86 + route.path = `${OUTSIDE_LINK_PREFIX}${route.path}`;
  87 + }
84 88 route.children && asyncImportRoute(route.children);
85 89 });
86 90 return routeList as unknown as T[];
... ...
... ... @@ -37,13 +37,21 @@
37 37 </a-button>
38 38 </template>
39 39 <template #status="{ record }">
40   - <Switch
41   - :checked="record.status === 1"
42   - :loading="record.pendingStatus"
43   - checkedChildren="启用"
44   - unCheckedChildren="禁用"
45   - @change="(checked:boolean)=>statusChange(checked,record)"
46   - />
  40 + <Authority value="api:yt:alarm:profile:status">
  41 + <Switch
  42 + :checked="record.status === 1"
  43 + :loading="record.pendingStatus"
  44 + checkedChildren="启用"
  45 + unCheckedChildren="禁用"
  46 + @change="(checked:boolean)=>statusChange(checked,record)"
  47 + />
  48 + </Authority>
  49 + <Tag
  50 + v-if="!hasPermission('api:yt:alarm:profile:status')"
  51 + :color="record.status ? 'green' : 'red'"
  52 + >
  53 + {{ record.status ? '启用' : '禁用' }}
  54 + </Tag>
47 55 </template>
48 56 <template #action="{ record }">
49 57 <TableAction
... ... @@ -88,7 +96,7 @@
88 96 import { useResetOrganizationTree, OrganizationIdTree } from '/@/views/common/organizationIdTree';
89 97 import { deleteAlarmConfig, queryAlarmConfig } from '/@/api/alarm/config/alarmConfig';
90 98 import { searchFormSchema, columns } from './config.data';
91   - import { Modal, Popconfirm } from 'ant-design-vue';
  99 + import { Modal, Popconfirm, Tag } from 'ant-design-vue';
92 100 import { JsonPreview } from '/@/components/CodeEditor';
93 101 import { findDictItemByCode } from '/@/api/system/dict';
94 102 import { alarmContactGetPage } from '/@/api/device/deviceConfigApi';
... ... @@ -97,6 +105,7 @@
97 105 import { putAlarmConfigStatus } from '/@/api/alarm/config/alarmConfig';
98 106 import { useMessage } from '/@/hooks/web/useMessage';
99 107 import { Authority } from '/@/components/Authority';
  108 + import { usePermission } from '/@/hooks/web/usePermission';
100 109
101 110 export default defineComponent({
102 111 components: {
... ... @@ -108,8 +117,10 @@
108 117 Switch,
109 118 Authority,
110 119 Popconfirm,
  120 + Tag,
111 121 },
112 122 setup() {
  123 + const { hasPermission } = usePermission();
113 124 const searchInfo = reactive<Recordable>({});
114 125 const { organizationIdTreeRef, resetFn } = useResetOrganizationTree(searchInfo);
115 126 // 刷新
... ... @@ -250,6 +261,7 @@
250 261 showAlarmContact,
251 262 showMessageMode,
252 263 statusChange,
  264 + hasPermission,
253 265 };
254 266 },
255 267 });
... ...
... ... @@ -17,7 +17,8 @@ export enum ConfigurationPermission {
17 17 UPDATE = 'api:yt:dataview:center:update',
18 18 DELETE = 'api:yt:dataview:center:delete',
19 19 SHARE = 'api:yt:dataview:center:share',
20   - DESIGN = 'api:yt:dataview:center:get_configuration_info:design',
  20 + // DESIGN = 'api:yt:dataview:center:get_configuration_info:design',
  21 + DESIGN = 'api:yt:dataview:center:get_dataview_info:design',
21 22 PREVIEW = 'api:yt:dataview:center:get_configuration_info:preview',
22 23 PUBLISH = 'api:yt:dataview:center:publish',
23 24 // CANCEL_PUBLISH = 'api:yt:dataview:center:cancel_publish',
... ...
... ... @@ -280,6 +280,7 @@
280 280 </Tooltip>
281 281 <Tooltip v-if="!isCustomerUser" title="设计">
282 282 <AuthIcon
  283 + :auth="ConfigurationPermission.DESIGN"
283 284 :disabled="item.state === 1"
284 285 icon="ant-design:edit-outlined"
285 286 @click="handleDesign(item)"
... ...
... ... @@ -169,12 +169,12 @@ export const alarmColumns: BasicColumn[] = [
169 169 {
170 170 title: '告警时间',
171 171 dataIndex: 'createdTime',
172   - width: 120,
  172 + width: 180,
173 173 },
174 174 {
175 175 title: '告警设备',
176 176 dataIndex: 'deviceName',
177   - width: 100,
  177 + width: 120,
178 178 },
179 179 {
180 180 title: '告警场景',
... ... @@ -184,14 +184,20 @@ export const alarmColumns: BasicColumn[] = [
184 184 {
185 185 title: '告警级别',
186 186 dataIndex: 'severity',
187   - width: 160,
  187 + width: 90,
188 188 format: (text) => alarmLevel(text),
189 189 },
190 190 {
  191 + title: '告警详情',
  192 + dataIndex: 'details',
  193 + slots: { customRender: 'details' },
  194 + width: 160,
  195 + },
  196 + {
191 197 title: '状态',
192 198 dataIndex: 'status',
193 199 format: (text) => statusType(text),
194   - width: 160,
  200 + width: 100,
195 201 },
196 202 ];
197 203
... ... @@ -263,14 +269,6 @@ export const alarmSchemasForm: FormSchema[] = [
263 269 disabled: true,
264 270 },
265 271 },
266   - {
267   - field: 'details',
268   - label: '详情',
269   - component: 'InputTextArea',
270   - componentProps: {
271   - maxLength: 255,
272   - },
273   - },
274 272 ];
275 273 // 子设备
276 274 export const childDeviceSchemas: FormSchema[] = [
... ...
... ... @@ -54,10 +54,6 @@
54 54 await resetFields();
55 55 await setFieldsValue({
56 56 ...data,
57   - details: JSON.stringify(data?.details?.data).slice(
58   - 1,
59   - JSON.stringify(data?.details?.data).length - 1
60   - ),
61 57 severity: alarmLevel(data.severity),
62 58 status: statusType(data.status),
63 59 });
... ...
... ... @@ -145,6 +145,8 @@
145 145 // !!!此处需要删除地图的属性,否则会报堆栈溢出的错误 Uncaught RangeError: Maximum call stack size exceeded
146 146 Reflect.deleteProperty(DeviceStep1Ref.value.positionState, 'map');
147 147 Reflect.deleteProperty(DeviceStep1Ref.value.positionState, 'marker');
  148 + Reflect.deleteProperty(DeviceStep1Ref.value.devicePositionState, 'map');
  149 + Reflect.deleteProperty(DeviceStep1Ref.value.devicePositionState, 'marker');
148 150 setModalProps({
149 151 confirmLoading: true,
150 152 });
... ... @@ -156,7 +158,7 @@
156 158 customerId: currentDeviceData.customerId,
157 159 deviceInfo: {
158 160 avatar: DeviceStep1Ref.value?.devicePic,
159   - ...DeviceStep1Ref.value?.positionState,
  161 + ...DeviceStep1Ref.value?.devicePositionState,
160 162 },
161 163 };
162 164 validateNameLength(stepRecord.name);
... ... @@ -168,7 +170,7 @@
168 170 sn: stepRecord.name,
169 171 deviceInfo: {
170 172 avatar: DeviceStep1Ref.value?.devicePic,
171   - ...DeviceStep1Ref.value?.positionState,
  173 + ...DeviceStep1Ref.value?.devicePositionState,
172 174 },
173 175 deviceToken:
174 176 unref(current) === 0 || !unref(stepState).addAgree
... ...
... ... @@ -46,7 +46,7 @@
46 46 </div>
47 47 </template>
48 48 <template #deviceAddress>
49   - <Input disabled v-model:value="positionState.address">
  49 + <Input disabled v-model:value="devicePositionState.address">
50 50 <template #addonAfter>
51 51 <EnvironmentTwoTone @click="selectPosition" />
52 52 </template>
... ... @@ -128,6 +128,7 @@
128 128 import { useDrawer } from '/@/components/Drawer';
129 129 import DeptDrawer from '/@/views/system/organization/OrganizationDrawer.vue';
130 130 import { TaskTypeEnum } from '/@/views/task/center/config';
  131 + import { toRaw } from 'vue';
131 132
132 133 export default defineComponent({
133 134 components: {
... ... @@ -239,8 +240,14 @@
239 240 const selectPosition = () => {
240 241 visible.value = true;
241 242 if (!positionState.longitude) {
242   - positionState.longitude = '104.04666605565338';
243   - positionState.latitude = '30.543516387560476';
  243 + positionState.longitude = '104.05326410962411';
  244 + positionState.latitude = '30.54855093076791';
  245 +
  246 + // 根据经纬度获取详细位置
  247 + if (positionState.longitude && positionState.latitude) {
  248 + var pt = new BMap.Point(positionState.longitude, positionState.latitude);
  249 + getAddrByPoint(pt);
  250 + }
244 251 initMap(positionState.longitude, positionState.latitude);
245 252 } else {
246 253 initMap(positionState.longitude, positionState.latitude);
... ... @@ -261,6 +268,15 @@
261 268 map: null,
262 269 marker: null,
263 270 });
  271 +
  272 + const devicePositionState = ref({
  273 + longitude: '',
  274 + latitude: '',
  275 + address: '',
  276 + map: null,
  277 + marker: null,
  278 + });
  279 +
264 280 /**
265 281 * 逆地址解析函数(根据坐标点获取详细地址)
266 282 * @param {Object} point 百度地图坐标点,必传
... ... @@ -371,6 +387,7 @@
371 387 // 确定选择的位置
372 388 const handleOk = () => {
373 389 visible.value = false;
  390 + devicePositionState.value = { ...toRaw(positionState) };
374 391 };
375 392 // 取消选择位置
376 393 const handleCancel = () => {
... ... @@ -385,6 +402,7 @@
385 402 positionState.longitude = deviceInfo.longitude;
386 403 positionState.latitude = deviceInfo.latitude;
387 404 positionState.address = deviceInfo.address;
  405 + devicePositionState.value = { ...toRaw(positionState) };
388 406 devicePic.value = deviceInfo.avatar;
389 407 setFieldsValue({
390 408 ...data,
... ... @@ -418,11 +436,13 @@
418 436 function parentResetDevicePic(deviceInfo) {
419 437 devicePic.value = deviceInfo.avatar;
420 438 }
  439 +
421 440 // 父组件重置位置
422 441 function parentResetPositionState() {
423 442 for (let key in positionState) {
424 443 positionState[key] = '';
425 444 }
  445 + devicePositionState.value = { ...toRaw(positionState) };
426 446 }
427 447 // 禁用设备类型
428 448 function disabledDeviceType(disabled: boolean) {
... ... @@ -474,6 +494,7 @@
474 494 handleOpenOrgDrawer,
475 495 handleSuccess,
476 496 handleTreeOrg,
  497 + devicePositionState,
477 498 };
478 499 },
479 500 });
... ...
... ... @@ -12,17 +12,31 @@
12 12 ]"
13 13 />
14 14 </template>
  15 + <template #details="{ record }">
  16 + <a-button type="link" class="ml-2" @click="handleViewAlarmDetails(record)">
  17 + 查看告警详情
  18 + </a-button>
  19 + </template>
15 20 </BasicTable>
16 21 <AlarmDetailModal @register="registerDetailModal" @success="handleSuccess" />
17 22 </div>
18 23 </template>
19 24 <script lang="ts">
20   - import { defineComponent } from 'vue';
  25 + import { defineComponent, h, nextTick } from 'vue';
21 26 import { BasicTable, useTable, TableAction } from '/@/components/Table';
22 27 import { alarmColumns, alarmSearchSchemas } from '../../config/detail.config';
23 28 import { getDeviceAlarm } from '/@/api/device/deviceManager';
24 29 import AlarmDetailModal from '../modal/AlarmDetailModal.vue';
25 30 import { useModal } from '/@/components/Modal';
  31 + import { Modal } from 'ant-design-vue';
  32 + import { JsonPreview } from '/@/components/CodeEditor';
  33 + import { getDeviceDetail } from '/@/api/device/deviceManager';
  34 + import { getAttribute } from '/@/api/ruleengine/ruleengineApi';
  35 + import {
  36 + operationNumber_OR_TIME,
  37 + operationString,
  38 + operationBoolean,
  39 + } from '/@/views/rule/linkedge/config/formatData';
26 40 export default defineComponent({
27 41 name: 'DeviceManagement',
28 42 components: {
... ... @@ -66,11 +80,85 @@
66 80 const handleSuccess = () => {
67 81 reload();
68 82 };
  83 + const handleViewAlarmDetails = async (record: Recordable) => {
  84 + await nextTick();
  85 + const { details } = record;
  86 + const deviceIdKeys = Object.keys(details);
  87 + const detailObject = deviceIdKeys.map((key) => ({ label: key, value: details[key] }));
  88 + const dataFormat = await handleAlarmDetailFormat(deviceIdKeys);
  89 + const dataFormats = detailObject.reduce((acc: any, curr: any) => {
  90 + dataFormat.forEach((item) => {
  91 + if (item.tbDeviceId === curr.label) {
  92 + const findName = item.attribute.find(
  93 + (item) => item.identifier === curr.value.key
  94 + )?.name;
  95 + const findLogin = [
  96 + ...operationNumber_OR_TIME,
  97 + ...operationString,
  98 + ...operationBoolean,
  99 + ].find((item) => item.value === curr.value.logic)?.symbol;
  100 + const findAttribute = item.attribute.find(
  101 + (findItem) => findItem.identifier === curr.value.key
  102 + );
  103 + const value = {
  104 + ['触发属性']: findName,
  105 + ['触发条件']: `${findLogin}${curr.value.logicValue}`,
  106 + ['触发值']: `${curr.value.realValue}${findAttribute.detail?.dataType?.specs?.unit?.key}`,
  107 + };
  108 + const data = {
  109 + [item.name]: value,
  110 + };
  111 + acc.push(data);
  112 + }
  113 + });
  114 + return [...acc];
  115 + }, []);
  116 + const objectFormat = dataFormats.reduce((acc: any, curr: any) => {
  117 + return {
  118 + ...acc,
  119 + ...curr,
  120 + };
  121 + }, {});
  122 + Modal.info({
  123 + title: '告警详情',
  124 + width: 600,
  125 + centered: true,
  126 + maskClosable: true,
  127 + content: h(JsonPreview, { data: JSON.parse(JSON.stringify(objectFormat)) }),
  128 + });
  129 + };
  130 + const handleAlarmDetailFormat = async (keys: string[]) => {
  131 + const temp: any = [];
  132 + for (let item of keys) {
  133 + if (item === 'key' || item === 'data') return; //旧数据则终止
  134 + const deviceDetailRes = await getDeviceDetail(item);
  135 + const { deviceProfileId } = deviceDetailRes;
  136 + if (!deviceProfileId) return;
  137 + const attributeRes = await getAttribute(deviceProfileId);
  138 + const dataFormat: any = handleDataFormat(deviceDetailRes, attributeRes);
  139 + temp.push(dataFormat);
  140 + }
  141 + return temp;
  142 + };
  143 + const handleDataFormat = (deviceDetail: any, attributes: any) => {
  144 + const { name, tbDeviceId } = deviceDetail;
  145 + const attribute = attributes.map((item) => ({
  146 + identifier: item.identifier,
  147 + name: item.name,
  148 + detail: item.detail,
  149 + }));
  150 + return {
  151 + name,
  152 + tbDeviceId,
  153 + attribute,
  154 + };
  155 + };
69 156 return {
70 157 registerTable,
71 158 registerDetailModal,
72 159 handleDetail,
73 160 handleSuccess,
  161 + handleViewAlarmDetails,
74 162 };
75 163 },
76 164 });
... ...
... ... @@ -52,7 +52,9 @@
52 52 <div class="mt-4" v-if="!isCustomer">
53 53 <a-button type="primary" class="mr-4" @click="copyTbDeviceId">复制设备ID</a-button>
54 54 <a-button type="primary" class="mr-4" @click="copyDeviceToken">复制访问令牌</a-button>
55   - <a-button type="primary" class="mr-4" @click="manageDeviceToken">管理设备凭证</a-button>
  55 + <Authority value="api:yt:device:equipment">
  56 + <a-button type="primary" class="mr-4" @click="manageDeviceToken">管理设备凭证</a-button>
  57 + </Authority>
56 58 <ManageDeviceTokenModal @register="registerModal" />
57 59 </div>
58 60 <div class="mt-4">
... ... @@ -84,10 +86,10 @@
84 86 import { DeviceTypeEnum } from '/@/api/device/model/deviceModel';
85 87 import { useAuthDeviceDetail } from '../../hook/useAuthDeviceDetail';
86 88 import { useClipboard } from '@vueuse/core';
87   -
88 89 import wz from '/@/assets/images/wz.png';
89 90 import { useAsyncQueue } from '../../../localtion/useAsyncQueue';
90 91 import locationImage from '/@/assets/icons/location.svg';
  92 + import { Authority } from '/@/components/Authority';
91 93
92 94 export default defineComponent({
93 95 components: {
... ... @@ -98,6 +100,7 @@
98 100 BasicModal,
99 101 Tooltip,
100 102 Empty,
  103 + Authority,
101 104 },
102 105 props: {
103 106 deviceDetail: {
... ...
... ... @@ -5,7 +5,9 @@
5 5 >
6 6 <template #toolbar>
7 7 <Space>
8   - <Button type="primary" @click="openModal(true)">命令下发</Button>
  8 + <Authority value="api:yt:device:rpc">
  9 + <Button type="primary" @click="openModal(true)">命令下发</Button>
  10 + </Authority>
9 11 </Space>
10 12 </template>
11 13 <template #recordContent="{ record }">
... ... @@ -43,6 +45,7 @@
43 45 import { DeviceRecord } from '/@/api/device/model/deviceModel';
44 46 import { BasicModal, useModal } from '/@/components/Modal';
45 47 import CommandIssuance from '../CommandIssuance.vue';
  48 + import { Authority } from '/@/components/Authority';
46 49
47 50 const props = defineProps({
48 51 fromId: {
... ...
... ... @@ -24,14 +24,16 @@
24 24 <Authority value="api:yt:device:import">
25 25 <Button type="primary" @click="handleBatchImport">导入</Button>
26 26 </Authority>
27   - <a-button
28   - v-if="authBtn(role)"
29   - type="primary"
30   - @click="handleBatchAssign"
31   - :disabled="!isExistOption"
32   - >
33   - 批量分配
34   - </a-button>
  27 + <Authority value="api:yt:device:assign">
  28 + <a-button
  29 + v-if="authBtn(role)"
  30 + type="primary"
  31 + @click="handleBatchAssign"
  32 + :disabled="!isExistOption"
  33 + >
  34 + 批量分配
  35 + </a-button>
  36 + </Authority>
35 37 </template>
36 38 <template #img="{ record }">
37 39 <TableImg
... ... @@ -134,6 +136,7 @@
134 136 label: '分配客户',
135 137 icon: 'mdi:account-arrow-right',
136 138 ifShow: authBtn(role),
  139 + auth: 'api:yt:device:assign',
137 140 onClick: handleDispatchCustomer.bind(null, record),
138 141 },
139 142 {
... ...
... ... @@ -33,9 +33,15 @@
33 33 </Button>
34 34 </Authority>
35 35 <Button type="primary" @click="handleOpenTsl"> 物模型TSL </Button>
36   - <Upload v-if="isShowBtn" :show-upload-list="false" :customRequest="handleImportModel">
37   - <Button type="primary" :loading="importLoading"> 导入物模型 </Button>
38   - </Upload>
  36 + <Authority value="api:yt:things_model:import">
  37 + <Upload
  38 + v-if="isShowBtn"
  39 + :show-upload-list="false"
  40 + :customRequest="handleImportModel"
  41 + >
  42 + <Button type="primary" :loading="importLoading"> 导入物模型 </Button>
  43 + </Upload>
  44 + </Authority>
39 45 </div>
40 46 <div class="flex gap-2">
41 47 <Authority :value="ModelOfMatterPermission.RELEASE">
... ...
... ... @@ -19,6 +19,6 @@
19 19 </template>
20 20 <style scoped>
21 21 :deep(.ant-table-body) {
22   - overflow: hidden !important;
  22 + overflow: auto !important;
23 23 }
24 24 </style>
... ...
... ... @@ -17,16 +17,26 @@
17 17 </Authority>
18 18 </template>
19 19 <template #config="{ record }">
20   - <a-button type="link" class="ml-2" @click="showData(record)"> 查看配置 </a-button>
  20 + <Authority value="api:yt:message:get">
  21 + <a-button type="link" class="ml-2" @click="showData(record)"> 查看配置 </a-button>
  22 + </Authority>
21 23 </template>
22 24 <template #status="{ record }">
23   - <Switch
24   - :checked="record.status === 1"
25   - :loading="record.pendingStatus"
26   - checkedChildren="启用"
27   - unCheckedChildren="禁用"
28   - @change="(checked:boolean)=>statusChange(checked,record)"
29   - />
  25 + <Authority value="api:yt:message:status">
  26 + <Switch
  27 + :checked="record.status === 1"
  28 + :loading="record.pendingStatus"
  29 + checkedChildren="启用"
  30 + unCheckedChildren="禁用"
  31 + @change="(checked:boolean)=>statusChange(checked,record)"
  32 + />
  33 + </Authority>
  34 + <Tag
  35 + v-if="!hasPermission('api:yt:message:status')"
  36 + :color="record.status ? 'green' : 'red'"
  37 + >
  38 + {{ record.status ? '启用' : '禁用' }}
  39 + </Tag>
30 40 </template>
31 41 <template #action="{ record }">
32 42 <TableAction
... ... @@ -62,18 +72,20 @@
62 72 import { useDrawer } from '/@/components/Drawer';
63 73 import ConfigDrawer from './ConfigDrawer.vue';
64 74 import { columns, searchFormSchema } from './config.data';
65   - import { Modal, Popconfirm } from 'ant-design-vue';
  75 + import { Modal, Popconfirm, Tag } from 'ant-design-vue';
66 76 import { JsonPreview } from '/@/components/CodeEditor';
67 77 import { useMessage } from '/@/hooks/web/useMessage';
68 78 import { useBatchDelete } from '/@/hooks/web/useBatchDelete';
69 79 import { Switch } from 'ant-design-vue';
70 80 import { setMessageConfigStatus } from '/@/api/message/config';
71 81 import { Authority } from '/@/components/Authority';
  82 + import { usePermission } from '/@/hooks/web/usePermission';
72 83
73 84 export default defineComponent({
74 85 name: 'MessageConfigManagement',
75   - components: { BasicTable, ConfigDrawer, TableAction, Switch, Authority, Popconfirm },
  86 + components: { BasicTable, ConfigDrawer, TableAction, Switch, Authority, Popconfirm, Tag },
76 87 setup() {
  88 + const { hasPermission } = usePermission();
77 89 const [registerDrawer, { openDrawer }] = useDrawer();
78 90 function handleSuccess() {
79 91 reload();
... ... @@ -168,6 +180,7 @@
168 180 handleDeleteOrBatchDelete,
169 181 hasBatchDelete,
170 182 statusChange,
  183 + hasPermission,
171 184 };
172 185 },
173 186 });
... ...
... ... @@ -22,13 +22,21 @@
22 22 </a-button>
23 23 </template>
24 24 <template #status="{ record }">
25   - <Switch
26   - :checked="record.status === 1"
27   - :loading="record.pendingStatus"
28   - checkedChildren="启用"
29   - unCheckedChildren="禁用"
30   - @change="(checked:boolean)=>statusChange(checked,record)"
31   - />
  25 + <Authority value="api:yt:template:status">
  26 + <Switch
  27 + :checked="record.status === 1"
  28 + :loading="record.pendingStatus"
  29 + checkedChildren="启用"
  30 + unCheckedChildren="禁用"
  31 + @change="(checked:boolean)=>statusChange(checked,record)"
  32 + />
  33 + </Authority>
  34 + <Tag
  35 + v-if="!hasPermission('api:yt:template:status')"
  36 + :color="record.status ? 'green' : 'red'"
  37 + >
  38 + {{ record.status ? '启用' : '禁用' }}
  39 + </Tag>
32 40 </template>
33 41 <template #action="{ record }">
34 42 <TableAction
... ... @@ -80,9 +88,10 @@
80 88 import SendEmail from '/@/views/message/template/SendEmail.vue';
81 89 import { useMessage } from '/@/hooks/web/useMessage';
82 90 import { useBatchDelete } from '/@/hooks/web/useBatchDelete';
83   - import { Switch, Popconfirm } from 'ant-design-vue';
  91 + import { Switch, Popconfirm, Tag } from 'ant-design-vue';
84 92 import { setMessageTemplateStatus } from '/@/api/message/template';
85 93 import { Authority } from '/@/components/Authority';
  94 + import { usePermission } from '/@/hooks/web/usePermission';
86 95
87 96 export default defineComponent({
88 97 name: 'MessageTemplateManagement',
... ... @@ -95,8 +104,10 @@
95 104 Switch,
96 105 Authority,
97 106 Popconfirm,
  107 + Tag,
98 108 },
99 109 setup() {
  110 + const { hasPermission } = usePermission();
100 111 const [registerModal, { openModal: openModal }] = useModal();
101 112 const [registerMailModal, { openModal: openMailModal }] = useModal();
102 113 const go = useGo();
... ... @@ -218,6 +229,7 @@
218 229 handleDeleteOrBatchDelete,
219 230 statusChange,
220 231 hasBatchDelete,
  232 + hasPermission,
221 233 };
222 234 },
223 235 });
... ...
... ... @@ -135,10 +135,14 @@
135 135 <Tabs.TabPane tab="详情" key="detail">
136 136 <Space>
137 137 <!-- <Button type="primary" @click="openDetailPage">打开详情页</Button> -->
138   - <Button type="primary" @click="downloadPackage" :disabled="!!otaRecord.url">
139   - 下载包
140   - </Button>
141   - <Button type="primary" @click="deletePackage" danger>删除包</Button>
  138 + <Authority :value="OtaPermissionKey.DOWNLOAD">
  139 + <Button type="primary" @click="downloadPackage" :disabled="!!otaRecord.url">
  140 + 下载包
  141 + </Button>
  142 + </Authority>
  143 + <Authority :value="OtaPermissionKey.DELETE">
  144 + <Button type="primary" @click="deletePackage" danger>删除包</Button>
  145 + </Authority>
142 146 </Space>
143 147 <div class="mt-3">
144 148 <Space>
... ...
... ... @@ -9,6 +9,7 @@ export enum PermissionReportConfigEnum {
9 9 PERMISSION_GET = 'api:yt:report:get',
10 10 PERMISSION_DELETE = 'api:yt:report_form:config:delete',
11 11 PERMISSION_UPDATE = 'api:yt:report_form:config:update',
  12 + PERMISSION_STATUS = 'api:yt:report_form:config:status',
12 13 }
13 14
14 15 //业务文字描述配置枚举
... ...
... ... @@ -74,14 +74,22 @@
74 74 />
75 75 </template>
76 76 <template #configStatus="{ record }">
77   - <Switch
78   - :disabled="disabledSwitch"
79   - :checked="record.status === 1"
80   - :loading="record.pendingStatus"
81   - :checkedChildren="BusinessReportConfigTextEnum.BUSINESS_ENABLE_TEXT"
82   - :unCheckedChildren="BusinessReportConfigTextEnum.BUSINESS_DISABLE_TEXT"
83   - @change="(checked: boolean) => statusChange(checked, record)"
84   - />
  77 + <Authority :value="PermissionReportConfigEnum.PERMISSION_STATUS">
  78 + <Switch
  79 + :disabled="disabledSwitch"
  80 + :checked="record.status === 1"
  81 + :loading="record.pendingStatus"
  82 + :checkedChildren="BusinessReportConfigTextEnum.BUSINESS_ENABLE_TEXT"
  83 + :unCheckedChildren="BusinessReportConfigTextEnum.BUSINESS_DISABLE_TEXT"
  84 + @change="(checked: boolean) => statusChange(checked, record)"
  85 + />
  86 + </Authority>
  87 + <Tag
  88 + v-if="!hasPermission(PermissionReportConfigEnum.PERMISSION_STATUS)"
  89 + :color="record.status ? 'green' : 'red'"
  90 + >
  91 + {{ record.status ? '启用' : '禁用' }}
  92 + </Tag>
85 93 </template>
86 94 </BasicTable>
87 95 <ReportConfigDrawer @register="registerDrawer" @success="handleSuccess" />
... ... @@ -101,7 +109,7 @@
101 109 import { defaultTableAttribtes } from './config';
102 110 import { Authority } from '/@/components/Authority';
103 111 import { useBatchDelete } from '/@/hooks/web/useBatchDelete';
104   - import { Popconfirm, Switch } from 'ant-design-vue';
  112 + import { Popconfirm, Switch, Tag } from 'ant-design-vue';
105 113 import { useModal } from '/@/components/Modal';
106 114 import { useGo } from '/@/hooks/web/usePage';
107 115 import { useMessage } from '/@/hooks/web/useMessage';
... ... @@ -111,6 +119,9 @@
111 119 BusinessReportConfigTextEnum,
112 120 BusinessReportConfigStatusEnum,
113 121 } from './enum';
  122 + import { usePermission } from '/@/hooks/web/usePermission';
  123 +
  124 + const { hasPermission } = usePermission();
114 125
115 126 const disabledSwitch = ref(false);
116 127
... ...
... ... @@ -8,6 +8,7 @@ export enum PermissionDataFlowEnum {
8 8 PERMISSION_GET = 'api:yt:convert:config:get',
9 9 PERMISSION_DELETE = 'api:yt:convert:config:delete',
10 10 PERMISSION_UPDATE = 'api:yt:convert:config:update',
  11 + PERMISSION_STATUS = 'api:yt:convert:config:status',
11 12 }
12 13
13 14 //业务文字描述配置枚举
... ...
... ... @@ -79,13 +79,21 @@
79 79 />
80 80 </template>
81 81 <template #status="{ record }">
82   - <Switch
83   - :checked="record.status === 1"
84   - :loading="record.pendingStatus"
85   - checkedChildren="启用"
86   - unCheckedChildren="禁用"
87   - @change="(checked:boolean)=>hanldeSwitch(checked,record)"
88   - />
  82 + <Authority :value="PermissionDataFlowEnum.PERMISSION_STATUS">
  83 + <Switch
  84 + :checked="record.status === 1"
  85 + :loading="record.pendingStatus"
  86 + checkedChildren="启用"
  87 + unCheckedChildren="禁用"
  88 + @change="(checked:boolean)=>hanldeSwitch(checked,record)"
  89 + />
  90 + </Authority>
  91 + <Tag
  92 + v-if="!hasPermission(PermissionDataFlowEnum.PERMISSION_STATUS)"
  93 + :color="record.status ? 'green' : 'red'"
  94 + >
  95 + {{ record.status ? '启用' : '禁用' }}
  96 + </Tag>
89 97 </template>
90 98 </BasicTable>
91 99 <DataFlowModal @register="registerModal" @success="handleSuccess" />
... ... @@ -103,15 +111,18 @@
103 111 import { useMessage } from '/@/hooks/web/useMessage';
104 112 import { Authority } from '/@/components/Authority';
105 113 import { useBatchDelete } from '/@/hooks/web/useBatchDelete';
106   - import { Switch, Popconfirm } from 'ant-design-vue';
  114 + import { Switch, Popconfirm, Tag } from 'ant-design-vue';
107 115 import { PermissionDataFlowEnum, BusinessDataFlowTextEnum } from './enum';
108 116 import { DataFlowModal } from './components/dataflowmodal';
109 117 import { defaultTableAttribute } from './config';
  118 + import { usePermission } from '/@/hooks/web/usePermission';
110 119
111 120 const { createMessage } = useMessage();
112 121
113 122 const loading = ref(true);
114 123
  124 + const { hasPermission } = usePermission();
  125 +
115 126 const handleSuccess = () => {
116 127 reload();
117 128 };
... ...
... ... @@ -164,26 +164,26 @@ export const genActionData = (actionData) => {
164 164 };
165 165
166 166 export const operationNumber_OR_TIME = [
167   - { label: '等于', value: Number_Operation.EQUAL },
168   - { label: '不等于', value: Number_Operation.NOT_EQUAL },
169   - { label: '小于', value: Number_Operation.LESS },
170   - { label: '小于等于', value: Number_Operation.LESS_OR_EQUAL },
171   - { label: '大于', value: Number_Operation.GREATER },
172   - { label: '大于等于', value: Number_Operation.GREATER_OR_EQUAL },
  167 + { label: '等于', value: Number_Operation.EQUAL, symbol: '=' },
  168 + { label: '不等于', value: Number_Operation.NOT_EQUAL, symbol: '!=' },
  169 + { label: '小于', value: Number_Operation.LESS, symbol: '<' },
  170 + { label: '小于等于', value: Number_Operation.LESS_OR_EQUAL, symbol: '<=' },
  171 + { label: '大于', value: Number_Operation.GREATER, symbol: '>' },
  172 + { label: '大于等于', value: Number_Operation.GREATER_OR_EQUAL, symbol: '>=' },
173 173 ];
174 174
175 175 export const operationString = [
176   - { label: '等于', value: String_Operation.EQUAL },
177   - { label: '不等于', value: String_Operation.NOT_EQUAL },
178   - { label: '开始于', value: String_Operation.BEGAN_IN },
179   - { label: '结束于', value: String_Operation.END_IN },
180   - { label: '包含', value: String_Operation.INCLUDE },
181   - { label: '不包含', value: String_Operation.NOT_INCLUDE },
  176 + { label: '等于', value: String_Operation.EQUAL, symbol: '=' },
  177 + { label: '不等于', value: String_Operation.NOT_EQUAL, symbol: '!=' },
  178 + { label: '开始于', value: String_Operation.BEGAN_IN, symbol: '开始于' },
  179 + { label: '结束于', value: String_Operation.END_IN, symbol: '结束于' },
  180 + { label: '包含', value: String_Operation.INCLUDE, symbol: '包含' },
  181 + { label: '不包含', value: String_Operation.NOT_INCLUDE, symbol: '不包含' },
182 182 ];
183 183
184 184 export const operationBoolean = [
185   - { label: '等于', value: Boolean_Operation.EQUAL },
186   - { label: '不等于', value: Boolean_Operation.NOT_EQUAL },
  185 + { label: '等于', value: Boolean_Operation.EQUAL, symbol: '=' },
  186 + { label: '不等于', value: Boolean_Operation.NOT_EQUAL, symbol: '!=' },
187 187 ];
188 188
189 189 export const scheduleOptions = [
... ...
... ... @@ -50,13 +50,21 @@
50 50 </template>
51 51
52 52 <template #status="{ record }">
53   - <Switch
54   - :checked="record.status === 1"
55   - :loading="record.pendingStatus"
56   - checkedChildren="启用"
57   - unCheckedChildren="禁用"
58   - @change="(checked:boolean)=>statusChange(checked,record)"
59   - />
  53 + <Authority value="api:yt:sceneLinkage:status">
  54 + <Switch
  55 + :checked="record.status === 1"
  56 + :loading="record.pendingStatus"
  57 + checkedChildren="启用"
  58 + unCheckedChildren="禁用"
  59 + @change="(checked:boolean)=>statusChange(checked,record)"
  60 + />
  61 + </Authority>
  62 + <Tag
  63 + v-if="!hasPermission('api:yt:sceneLinkage:status')"
  64 + :color="record.status ? 'green' : 'red'"
  65 + >
  66 + {{ record.status ? '启用' : '禁用' }}
  67 + </Tag>
60 68 </template>
61 69 </BasicTable>
62 70 <SceneLinkAgeDrawer @register="registerDrawer" @success="handleSuccess" />
... ... @@ -72,16 +80,18 @@
72 80 screenLinkPagePutApi,
73 81 } from '/@/api/ruleengine/ruleengineApi';
74 82 import { useBatchDelete } from '/@/hooks/web/useBatchDelete';
75   - import { Switch, Popconfirm } from 'ant-design-vue';
  83 + import { Switch, Popconfirm, Tag } from 'ant-design-vue';
76 84 import { columns, searchFormSchema } from './config/config.data';
77 85 import { USER_INFO_KEY } from '/@/enums/cacheEnum';
78 86 import { getAuthCache } from '/@/utils/auth';
79 87 import SceneLinkAgeDrawer from './SceneLinkAgeDrawer.vue';
80 88 import { useMessage } from '/@/hooks/web/useMessage';
81 89 import { Authority } from '/@/components/Authority';
  90 + import { usePermission } from '/@/hooks/web/usePermission';
82 91
83 92 const userInfo: any = getAuthCache(USER_INFO_KEY);
84 93 const userId = userInfo.userId;
  94 + const { hasPermission } = usePermission();
85 95
86 96 const [registerDrawer, { openDrawer }] = useDrawer();
87 97 const [registerTable, { reload, setProps, setSelectedRowKeys }] = useTable({
... ...
... ... @@ -17,13 +17,21 @@
17 17 </Authority>
18 18 </template>
19 19 <template #status="{ record }">
20   - <Switch
21   - :checked="record.status === 1"
22   - :loading="record.pendingStatus"
23   - checkedChildren="启用"
24   - unCheckedChildren="禁用"
25   - @change="(checked:boolean)=>statusChange(checked,record)"
26   - />
  20 + <Authority value="api:yt:convert:js:status">
  21 + <Switch
  22 + :checked="record.status === 1"
  23 + :loading="record.pendingStatus"
  24 + checkedChildren="启用"
  25 + unCheckedChildren="禁用"
  26 + @change="(checked:boolean)=>statusChange(checked,record)"
  27 + />
  28 + </Authority>
  29 + <Tag
  30 + v-if="!hasPermission('api:yt:convert:js:status')"
  31 + :color="record.status ? 'green' : 'red'"
  32 + >
  33 + {{ record.status ? '启用' : '禁用' }}
  34 + </Tag>
27 35 </template>
28 36 <template #action="{ record }">
29 37 <TableAction
... ... @@ -69,7 +77,7 @@
69 77
70 78 <script lang="ts" setup>
71 79 import { ref, nextTick } from 'vue';
72   - import { Switch, Popconfirm } from 'ant-design-vue';
  80 + import { Switch, Popconfirm, Tag } from 'ant-design-vue';
73 81 import { BasicTable, useTable, TableAction } from '/@/components/Table';
74 82 import { columns } from '../config/config.data';
75 83 import { getConvertApi, deleteTransformApi } from '/@/api/device/TransformScriptApi';
... ... @@ -81,11 +89,14 @@
81 89 import { useBatchDelete } from '/@/hooks/web/useBatchDelete';
82 90 import { Authority } from '/@/components/Authority';
83 91 import { computed, unref } from 'vue';
  92 + import { usePermission } from '/@/hooks/web/usePermission';
84 93
85 94 const props = defineProps<{ searchInfo: Recordable }>();
86 95
87 96 const getSearchInfo = computed(() => props.searchInfo);
88 97
  98 + const { hasPermission } = usePermission();
  99 +
89 100 const handleSuccess = () => {
90 101 reload();
91 102 };
... ...
... ... @@ -24,8 +24,9 @@
24 24 :customRequest="customUploadIconPic"
25 25 :before-upload="beforeUploadIconPic"
26 26 >
27   - <div v-if="iconPic">
  27 + <div v-if="iconPic" class="relative">
28 28 <img :src="iconPic" class="m-auto fill-img" />
  29 + <CloseCircleOutlined class="absolute icon-delete" @click.stop="handleDelete" />
29 30 <div style="background-color: #ccc; margin-top: 20px">重新上传</div>
30 31 </div>
31 32 <div v-else>
... ... @@ -81,7 +82,7 @@
81 82 import { useMessage } from '/@/hooks/web/useMessage';
82 83 import type { FileItem } from '/@/components/Upload/src/typing';
83 84 import { iconUpload, getPlatForm, updatePlatForm, resetPlateInfo } from '/@/api/oem/index';
84   - import { PlusOutlined } from '@ant-design/icons-vue';
  85 + import { PlusOutlined, CloseCircleOutlined } from '@ant-design/icons-vue';
85 86 import { useUserStore } from '/@/store/modules/user';
86 87 import { createLocalStorage } from '/@/utils/cache/index';
87 88 import { Authority } from '/@/components/Authority';
... ... @@ -100,6 +101,7 @@
100 101 ContentUploadText,
101 102 Spin,
102 103 CustomUploadComp,
  104 + CloseCircleOutlined,
103 105 },
104 106 setup() {
105 107 const loading = ref(false);
... ... @@ -188,6 +190,11 @@
188 190 storage.set('platformInfo', newFieldValue);
189 191 }
190 192
  193 + // 删除浏览器ico图标
  194 + const handleDelete = () => {
  195 + iconPic.value = '';
  196 + };
  197 +
191 198 onMounted(async () => {
192 199 const res = await getPlatForm();
193 200 setFieldsValue(res);
... ... @@ -227,6 +234,7 @@
227 234 handleResetInfo,
228 235 handleSetBgImgUrl,
229 236 handleSetLogoImgUrl,
  237 + handleDelete,
230 238 };
231 239 },
232 240 });
... ... @@ -251,4 +259,12 @@
251 259 width: 100%;
252 260 height: 100%;
253 261 }
  262 +
  263 + .icon-delete {
  264 + top: -13px;
  265 + right: -13px;
  266 + color: rgb(146, 145, 145);
  267 + font-size: 1rem;
  268 + z-index: 9999;
  269 + }
254 270 </style>
... ...
1 1 <script setup name="CustomUploadComp" lang="ts">
2   - import { PlusOutlined } from '@ant-design/icons-vue';
  2 + import { PlusOutlined, CloseCircleOutlined } from '@ant-design/icons-vue';
3 3 import { Upload, Spin } from 'ant-design-vue';
4 4 import { ref, watchEffect } from 'vue';
5 5 import { useUpload } from './hooks/useUploadFile.hook';
... ... @@ -40,6 +40,12 @@
40 40 }
41 41 };
42 42
  43 + // 删除图片
  44 + const handleDelete = () => {
  45 + uploadImgUrl.value = '';
  46 + emit('setImg', uploadImgUrl.value);
  47 + };
  48 +
43 49 watchEffect(() => {
44 50 init();
45 51 });
... ... @@ -59,7 +65,10 @@
59 65 :customRequest="customUpload"
60 66 :before-upload="beforeUploadVerify"
61 67 >
62   - <img v-if="uploadImgUrl" class="fill-img" :src="uploadImgUrl" alt="avatar" />
  68 + <div v-if="uploadImgUrl" class="w-full h-full modal-div relative">
  69 + <img class="fill-img" :src="uploadImgUrl" alt="avatar" />
  70 + <CloseCircleOutlined class="absolute icon-delete" @click.stop="handleDelete" />
  71 + </div>
63 72 <div v-else>
64 73 <Spin v-if="uploadLoading" tip="正在上传中..." />
65 74 <PlusOutlined v-else />
... ... @@ -74,4 +83,12 @@
74 83 width: 100%;
75 84 height: 100%;
76 85 }
  86 +
  87 + .icon-delete {
  88 + top: -13px;
  89 + right: -13px;
  90 + color: rgb(146, 145, 145);
  91 + font-size: 1rem;
  92 + z-index: 9999;
  93 + }
77 94 </style>
... ...
... ... @@ -21,13 +21,18 @@
21 21 </Authority>
22 22 </template>
23 23 <template #status="{ record }">
24   - <Switch
25   - :checked="record.status === 1"
26   - :loading="record.pendingStatus"
27   - checkedChildren="启用"
28   - unCheckedChildren="禁用"
29   - @change="(checked:boolean)=>statusChange(checked,record)"
30   - />
  24 + <Authority value="api:yt:role:status">
  25 + <Switch
  26 + :checked="record.status === 1"
  27 + :loading="record.pendingStatus"
  28 + checkedChildren="启用"
  29 + unCheckedChildren="禁用"
  30 + @change="(checked:boolean)=>statusChange(checked,record)"
  31 + />
  32 + </Authority>
  33 + <Tag v-if="!hasPermission('api:yt:role:status')" :color="record.status ? 'green' : 'red'">
  34 + {{ record.status ? '启用' : '禁用' }}
  35 + </Tag>
31 36 </template>
32 37 <template #action="{ record }">
33 38 <TableAction
... ... @@ -67,12 +72,14 @@
67 72 import { Authority } from '/@/components/Authority';
68 73 import { useBatchDelete } from '/@/hooks/web/useBatchDelete';
69 74 import { useMessage } from '/@/hooks/web/useMessage';
70   - import { Switch, Popconfirm } from 'ant-design-vue';
  75 + import { Switch, Popconfirm, Tag } from 'ant-design-vue';
  76 + import { usePermission } from '/@/hooks/web/usePermission';
71 77
72 78 export default defineComponent({
73 79 name: 'RoleManagement',
74   - components: { BasicTable, RoleDrawer, TableAction, Authority, Switch, Popconfirm },
  80 + components: { BasicTable, RoleDrawer, TableAction, Authority, Switch, Popconfirm, Tag },
75 81 setup() {
  82 + const { hasPermission } = usePermission();
76 83 const [registerDrawer, { openDrawer }] = useDrawer();
77 84 function handleSuccess() {
78 85 reload();
... ... @@ -157,6 +164,7 @@
157 164 hasBatchDelete,
158 165 handleDeleteOrBatchDelete,
159 166 statusChange,
  167 + hasPermission,
160 168 };
161 169 },
162 170 });
... ...
... ... @@ -7,7 +7,7 @@ export const validateDevicePicker = () => {
7 7 validateTrigger: 'blur',
8 8 validator(_rule: Recordable, value: Recordable, _callback: Fn) {
9 9 const device = Reflect.get(value || {}, FormFieldsEnum.DEVICE);
10   - if (!device) return Promise.reject('请选择设备');
  10 + if (!device || !device.length) return Promise.reject('请选择设备');
11 11 return Promise.resolve();
12 12 },
13 13 } as Rule;
... ...
... ... @@ -133,6 +133,7 @@
133 133 text: '详情',
134 134 event: DropMenuEvent.DETAIL,
135 135 icon: 'ant-design:eye-outlined',
  136 + auth: PermissionEnum.DETAIL,
136 137 onClick: emit.bind(null, 'detail', getRecord),
137 138 },
138 139 {
... ...
... ... @@ -7,6 +7,7 @@ export enum PermissionEnum {
7 7 DELETE = 'api:yt:task_center:delete',
8 8 ALLOW = 'api:yt:task_center:cancel:allow',
9 9 EXECUTE = 'api:yt:task_center:immediate:execute',
  10 + DETAIL = 'api:yt:task_center:get',
10 11 }
11 12
12 13 export enum FormFieldsEnum {
... ...
... ... @@ -54,7 +54,7 @@
54 54 },
55 55 labelWidth: 120,
56 56 fieldMapToTime: [
57   - [SchemaFiled.DATE_RANGE, [SchemaFiled.START_TS, SchemaFiled.END_TS], 'YYYY-MM-DD HH:ss'],
  57 + [SchemaFiled.DATE_RANGE, [SchemaFiled.START_TS, SchemaFiled.END_TS], 'YYYY-MM-DD HH:mm:ss'],
58 58 ],
59 59 submitButtonOptions: {
60 60 loading: loading as unknown as boolean,
... ... @@ -260,7 +260,7 @@
260 260 :show-ok-btn="false"
261 261 cancel-text="关闭"
262 262 width="70%"
263   - title="历史趋势"
  263 + title="选择时间"
264 264 >
265 265 <section
266 266 class="flex flex-col p-4 h-full w-full min-w-7/10"
... ...