Commit 1b471b784586e802a1a8f662567e6544699f0cdf

Authored by loveumiko
2 parents 95d8c567 4239e0ae

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

…ix/board-style-issues
Showing 39 changed files with 263 additions and 106 deletions
... ... @@ -8,6 +8,7 @@ export interface BigScreenCenterItemsModel {
8 8 remark: string;
9 9 state: number;
10 10 publicId: string;
  11 + organizationId?: string;
11 12 }
12 13 export type queryPageParams = BasicPageParams & {
13 14 name?: Nullable<string>;
... ...
... ... @@ -7,6 +7,7 @@ export interface ConfigurationCenterItemsModal {
7 7 creator: string;
8 8 remark: string;
9 9 publicId?: string;
  10 + organizationId?: string;
10 11 }
11 12 export type queryPageParams = BasicPageParams & {
12 13 name?: Nullable<string>;
... ...
... ... @@ -85,7 +85,7 @@ interface TrendParamsType {
85 85 }
86 86 // 获取租户趋势或者客户趋势数据
87 87 export const getTrendData = (params: TrendParamsType) => {
88   - return defHttp.get({
  88 + return defHttp.get<Record<'date' | 'value', string>[]>({
89 89 url: HomeEnum.TrendAPI,
90 90 params,
91 91 });
... ...
... ... @@ -30,6 +30,8 @@ enum DeviceManagerApi {
30 30
31 31 ALARM_BATCH_ACK = '/alarm/batch/ack',
32 32
  33 + ALARM_BATCH_CLEAR = '/alarm/batch/clear',
  34 +
33 35 DEVICE_CREDENTIALS = '/device/credentials',
34 36
35 37 COMMAND_ISSUANCE = '/rpc',
... ... @@ -358,3 +360,13 @@ export const doBatchAckAlarm = (ids: string[]) => {
358 360 { joinPrefix: false }
359 361 );
360 362 };
  363 +
  364 +export const doBatchClearAlarm = (ids: string[]) => {
  365 + return defHttp.post(
  366 + {
  367 + url: DeviceManagerApi.ALARM_BATCH_CLEAR,
  368 + data: { alarmIds: ids },
  369 + },
  370 + { joinPrefix: false }
  371 + );
  372 +};
... ...
... ... @@ -9,7 +9,7 @@
9 9 import { useMessage } from '/@/hooks/web/useMessage';
10 10 import { computed, ref, unref } from 'vue';
11 11 import { cloneDeep } from 'lodash-es';
12   - import { isFunction, isNumber, isObject } from '/@/utils/is';
  12 + import { isFunction, isNumber, isObject, isString } from '/@/utils/is';
13 13 import { useDesign } from '/@/hooks/web/useDesign';
14 14
15 15 export interface FileItem {
... ... @@ -59,10 +59,19 @@
59 59 );
60 60
61 61 const handleBeforeUpload = (file: File, fileList: File[]) => {
62   - const { beforeUpload } = props;
  62 + const { beforeUpload, accept } = props;
63 63
64 64 if (beforeUpload && isFunction(beforeUpload)) return beforeUpload?.(file, fileList);
65 65
  66 + if (accept && isString(accept)) {
  67 + const limitFileSuffix = accept.split(',');
  68 +
  69 + if (limitFileSuffix.length && !limitFileSuffix.some((suffix) => file.name.includes(suffix))) {
  70 + createMessage.warning(`允许上传的文件类型包括${accept}`);
  71 + return false;
  72 + }
  73 + }
  74 +
66 75 if (file.size > props.maxSize) {
67 76 createMessage.warning(`文件大小超过${Math.floor(props.maxSize / 1024 / 1024)}mb`);
68 77 return false;
... ...
... ... @@ -2,7 +2,7 @@ import { RouteLocationNormalizedLoaded } from 'vue-router';
2 2
3 3 const menuMap = new Map();
4 4
5   -menuMap.set('/visual/board/detail/:boardId/:boardName?', '/visual/board');
  5 +menuMap.set('/visual/board/detail/:boardId/:boardName/:organizationId?', '/visual/board');
6 6 menuMap.set('/rule/chain/:id', '/rule/chain');
7 7
8 8 export const useMenuActiveFix = (route: RouteLocationNormalizedLoaded) => {
... ...
... ... @@ -37,7 +37,7 @@
37 37 </a-button>
38 38 </template>
39 39 <template #status="{ record }">
40   - <Authority value="api:yt:alarm:profile:status">
  40 + <Authority value="api:yt:alarm:profile:update">
41 41 <Switch
42 42 :checked="record.status === 1"
43 43 :loading="record.pendingStatus"
... ... @@ -47,7 +47,7 @@
47 47 />
48 48 </Authority>
49 49 <Tag
50   - v-if="!hasPermission('api:yt:alarm:profile:status')"
  50 + v-if="!hasPermission('api:yt:alarm:profile:update')"
51 51 :color="record.status ? 'green' : 'red'"
52 52 >
53 53 {{ record.status ? '启用' : '禁用' }}
... ...
... ... @@ -19,7 +19,30 @@
19 19 </a-button>
20 20 </template>
21 21 <template #toolbar>
22   - <Button @click="handleBatchAck" type="primary" :disabled="getCanBatchAck">批量处理</Button>
  22 + <Tooltip>
  23 + <template #title>
  24 + <div>激活未确认: 可以处理,清除</div>
  25 + <div>激活已确认: 只可清除,已经处理</div>
  26 + <div>清除未确认: 只可处理,已经清除</div>
  27 + <div>清除已确认: 不需要做处理和清除</div>
  28 + </template>
  29 + <Button @click="handleBatchClear" type="primary" :disabled="getCanBatchClear">
  30 + <QuestionCircleOutlined class="cursor-pointer" />
  31 + <span>批量清除</span>
  32 + </Button>
  33 + </Tooltip>
  34 + <Tooltip>
  35 + <template #title>
  36 + <div>激活未确认: 可以处理,清除</div>
  37 + <div>激活已确认: 只可清除,已经处理</div>
  38 + <div>清除未确认: 只可处理,已经清除</div>
  39 + <div>清除已确认: 不需要做处理和清除</div>
  40 + </template>
  41 + <Button @click="handleBatchAck" type="primary" :disabled="getCanBatchAck">
  42 + <QuestionCircleOutlined class="cursor-pointer" />
  43 + <span>批量处理</span>
  44 + </Button>
  45 + </Tooltip>
23 46 </template>
24 47 </BasicTable>
25 48 <AlarmDetailDrawer @register="registerDetailDrawer" @success="handleSuccess" />
... ... @@ -29,10 +52,10 @@
29 52 import { defineComponent, nextTick, h, computed } from 'vue';
30 53 import { BasicTable, useTable, TableAction } from '/@/components/Table';
31 54 import { alarmColumns, alarmSearchSchemas } from './config/detail.config';
32   - import { doBatchAckAlarm, getDeviceAlarm } from '/@/api/device/deviceManager';
  55 + import { doBatchAckAlarm, doBatchClearAlarm, getDeviceAlarm } from '/@/api/device/deviceManager';
33 56 import { useDrawer } from '/@/components/Drawer';
34 57 import AlarmDetailDrawer from './cpns/AlarmDetailDrawer.vue';
35   - import { Modal, Button } from 'ant-design-vue';
  58 + import { Modal, Button, Tooltip } from 'ant-design-vue';
36 59 import { JsonPreview } from '/@/components/CodeEditor';
37 60 import { getDeviceDetail } from '/@/api/device/deviceManager';
38 61 import { getAttribute } from '/@/api/ruleengine/ruleengineApi';
... ... @@ -44,6 +67,7 @@
44 67 import { AlarmLogItem } from '/@/api/device/model/deviceConfigModel';
45 68 import { AlarmStatus } from '/@/enums/alarmEnum';
46 69 import { useMessage } from '/@/hooks/web/useMessage';
  70 + import { QuestionCircleOutlined } from '@ant-design/icons-vue';
47 71
48 72 export default defineComponent({
49 73 name: 'AlarmCenter',
... ... @@ -52,6 +76,8 @@
52 76 BasicTable,
53 77 TableAction,
54 78 AlarmDetailDrawer,
  79 + QuestionCircleOutlined,
  80 + Tooltip,
55 81 },
56 82
57 83 setup() {
... ... @@ -60,6 +86,7 @@
60 86 title: '告警记录列表',
61 87 api: getDeviceAlarm,
62 88 columns: alarmColumns,
  89 + rowKey: 'id',
63 90 useSearchForm: true,
64 91 formConfig: {
65 92 labelWidth: 120,
... ... @@ -168,9 +195,30 @@
168 195 };
169 196 };
170 197
  198 + const getCanBatchClear = computed(() => {
  199 + const rowSelection = getRowSelection();
  200 + const getRows: AlarmLogItem[] = getSelectRows();
  201 +
  202 + return (
  203 + !rowSelection.selectedRowKeys?.length ||
  204 + getRows.some(
  205 + (item) =>
  206 + item.status === AlarmStatus.CLEARED_ACK || item.status === AlarmStatus.CLEARED_UN_ACK
  207 + )
  208 + );
  209 + });
  210 +
171 211 const getCanBatchAck = computed(() => {
172 212 const rowSelection = getRowSelection();
173   - return !rowSelection.selectedRowKeys?.length;
  213 + const getRows: AlarmLogItem[] = getSelectRows();
  214 +
  215 + return (
  216 + !rowSelection.selectedRowKeys?.length ||
  217 + getRows.some(
  218 + (item) =>
  219 + item.status === AlarmStatus.CLEARED_ACK || item.status === AlarmStatus.ACTIVE_ACK
  220 + )
  221 + );
174 222 });
175 223
176 224 const { createMessage } = useMessage();
... ... @@ -183,6 +231,15 @@
183 231 reload();
184 232 };
185 233
  234 + const handleBatchClear = async () => {
  235 + const ids = getSelectRows<AlarmLogItem>().map((item) => item.id);
  236 + if (!ids.length) return;
  237 + await doBatchClearAlarm(ids);
  238 + createMessage.success('操作成功');
  239 + clearSelectedRowKeys();
  240 + reload();
  241 + };
  242 +
186 243 return {
187 244 registerTable,
188 245 registerDetailDrawer,
... ... @@ -190,7 +247,9 @@
190 247 handleSuccess,
191 248 handleViewAlarmDetails,
192 249 handleBatchAck,
  250 + handleBatchClear,
193 251 getCanBatchAck,
  252 + getCanBatchClear,
194 253 };
195 254 },
196 255 });
... ...
... ... @@ -56,7 +56,10 @@
56 56 return unref(orgList);
57 57 }
58 58 },
59   - params: { ...params, _t: unref(timespan) },
  59 + params: {
  60 + ...params,
  61 + _t: unref(timespan),
  62 + },
60 63 onChange: (...args: any[]) => {
61 64 emit('change', ...args);
62 65 },
... ...
... ... @@ -136,13 +136,19 @@
136 136 const handlePreview = (record: ConfigurationCenterItemsModal) => {
137 137 if (!unref(getPreviewFlag)) return;
138 138 window.open(
139   - `${configurationPrefix}/${isDev ? '?dev=1&' : '?'}configurationId=${record!.id}&lightbox=1`
  139 + `${configurationPrefix}/${isDev ? '?dev=1&' : '?'}configurationId=${
  140 + record!.id
  141 + }&lightbox=1&organizationId=${record.organizationId}`
140 142 );
141 143 };
142 144
143 145 const handleDesign = (record: ConfigurationCenterItemsModal) => {
144 146 if (!unref(getDesignFlag)) return;
145   - window.open(`${configurationPrefix}/${isDev ? '?dev=1&' : '?'}configurationId=${record!.id}`);
  147 + window.open(
  148 + `${configurationPrefix}/${isDev ? '?dev=1&' : '?'}configurationId=${
  149 + record!.id
  150 + }&organizationId=${record.organizationId}`
  151 + );
146 152 };
147 153
148 154 const handleDelete = async (record: ConfigurationCenterItemsModal) => {
... ... @@ -165,6 +171,7 @@
165 171 searchParams.set('configurationId', record.id);
166 172 searchParams.set('publicId', record.publicId || '');
167 173 searchParams.set('lightbox', '1');
  174 + searchParams.set('organizationId', record!.organizationId || '');
168 175 return `${origin}${configurationPrefix}/?${searchParams.toString()}`;
169 176 };
170 177
... ...
... ... @@ -132,6 +132,9 @@
132 132 @calendarChange="handleCalendarChange"
133 133 size="small"
134 134 v-model:value="customerDateValue"
  135 + :showTime="{
  136 + defaultValue: [dateUtil('00:00:00', 'HH:mm:ss'), dateUtil('23:59:59', 'HH:mm:ss')],
  137 + }"
135 138 >
136 139 <Button
137 140 type="link"
... ... @@ -165,7 +168,7 @@
165 168 import { useWebSocket } from '@vueuse/core';
166 169 import { getAuthCache } from '/@/utils/auth';
167 170 import { JWT_TOKEN_KEY } from '/@/enums/cacheEnum';
168   - import { formatToDateTime } from '/@/utils/dateUtil';
  171 + import { dateUtil, formatToDateTime } from '/@/utils/dateUtil';
169 172 import CustomerTrend from './CustomerTrend.vue';
170 173 // import TenantTrend from './TenantTrend.vue';
171 174 import CustomerAlarmMessage from './CustomerAlarmMessage.vue';
... ... @@ -530,6 +533,7 @@
530 533 };
531 534
532 535 const handleDisableDate = (current: moment.Moment) => {
  536 + if (!current) return true;
533 537 if (!unref(customerDateValue) || unref(customerDateValue).length === 0) {
534 538 return false;
535 539 }
... ...
... ... @@ -117,6 +117,9 @@
117 117 @calendarChange="handleCalendarChange"
118 118 :disabledDate="handleDisableDate"
119 119 v-model:value="tenantDateValue"
  120 + :showTime="{
  121 + defaultValue: [dateUtil('00:00:00', 'HH:mm:ss'), dateUtil('23:59:59', 'HH:mm:ss')],
  122 + }"
120 123 >
121 124 <Button
122 125 type="link"
... ... @@ -175,7 +178,7 @@
175 178 import { useWebSocket } from '@vueuse/core';
176 179 import { getAuthCache } from '/@/utils/auth';
177 180 import { JWT_TOKEN_KEY } from '/@/enums/cacheEnum';
178   - import { formatToDateTime } from '/@/utils/dateUtil';
  181 + import { dateUtil, formatToDateTime } from '/@/utils/dateUtil';
179 182 // import CustomerTrend from './CustomerTrend.vue';
180 183 import TenantTrend from './TenantTrend.vue';
181 184 import CustomerAlarmMessage from './CustomerAlarmMessage.vue';
... ... @@ -548,6 +551,7 @@
548 551 };
549 552
550 553 const handleDisableDate = (current: moment.Moment) => {
  554 + if (!current) return true;
551 555 if (!unref(tenantDateValue) || unref(tenantDateValue).length === 0) {
552 556 return false;
553 557 }
... ...
1   -import { dateUtil, formatToDateTime } from '/@/utils/dateUtil';
2   -import { ref } from 'vue';
  1 +import { dateUtil } from '/@/utils/dateUtil';
  2 +import { Ref, ref } from 'vue';
3 3 import { getTrendData } from '/@/api/dashboard';
4 4 import { RangePickerValue } from 'ant-design-vue/lib/date-picker/interface';
  5 +import moment from 'moment';
5 6
6 7 export enum ShortcutQueryKeyEnum {
7 8 LATEST_1_MONTH = 'LATEST_1_MONTH',
... ... @@ -42,8 +43,8 @@ export function getDateByShortcutQueryKey(value: ShortcutQueryKeyEnum) {
42 43 export function useDate() {
43 44 const tenantDateValue = ref([]);
44 45 const customerDateValue = ref<RangePickerValue>([]);
45   - const tenantTrendList = ref([]);
46   - const customerTrendList = ref([]);
  46 + const tenantTrendList = ref<[string, string][]>([]);
  47 + const customerTrendList = ref<[string, string][]>([]);
47 48 const activeTenantIndex = ref(0);
48 49 const activeCustomerIndex = ref(0);
49 50 const TenantOrCustomerDateList = ref([
... ... @@ -80,52 +81,39 @@ export function useDate() {
80 81 }
81 82
82 83 // 获取选中的时间范围内的数据
83   - async function getDateData(startTs, endTs, trend: 'CUSTOMER_TREND' | 'TENANT_TREND', list) {
84   - // 计算时间间隔
85   - function computedInterval(startTs: number, endTs: number) {
86   - /**
87   - * 选择的时间间隔
88   - * <=1 2h
89   - * <=30 1day
90   - * >30<90 2day
91   - * >90 1month
92   - */
93   - let interval = 86400000;
94   - if (endTs - startTs <= 86400000) {
95   - interval = 7200000;
96   - } else if (endTs - startTs <= 2592000000) {
97   - interval = 86400000;
98   - } else if (endTs - startTs > 2592000000 && endTs - startTs < 7776000000) {
99   - interval = 172800000;
100   - } else if (endTs - startTs > 7776000000) {
101   - interval = 2592000000;
102   - }
103   - return interval;
104   - }
105   - startTs = parseInt(formatToDateTime(startTs, 'x')) - 86400000;
106   - endTs = parseInt(formatToDateTime(endTs, 'x'));
  84 + async function getDateData(
  85 + startTs: moment.Moment,
  86 + endTs: moment.Moment,
  87 + trend: 'CUSTOMER_TREND' | 'TENANT_TREND',
  88 + list: Ref<[string, string][]>
  89 + ) {
107 90 const res = await getTrendData({
108   - startTs,
109   - endTs,
110   - interval: computedInterval(startTs, endTs),
  91 + startTs: startTs.valueOf(),
  92 + endTs: endTs.valueOf(),
  93 + interval: 24 * 60 * 60 * 1000,
111 94 trend,
112 95 });
113   - list.value = res.map((item) => [item.ts, item.value]);
  96 + list.value = res.map((item) => [item.date, item.value]);
114 97 }
115 98
116 99 // 租户选择日期
117   - function onDateTenantChange(_, dateString) {
118   - if (!_.length) return;
119   - const [startTs, endTs] = dateString;
  100 + function onDateTenantChange(range: RangePickerValue) {
  101 + if (!range.length) return;
  102 + const [startTs, endTs] = range;
120 103 activeTenantIndex.value = -1;
121   - getDateData(startTs, endTs, 'TENANT_TREND', tenantTrendList);
  104 + getDateData(startTs as moment.Moment, endTs as moment.Moment, 'TENANT_TREND', tenantTrendList);
122 105 }
123 106 // 客户趋势选择日期
124   - function onDateCustomerChange(_, dateString) {
125   - if (!_.length) return;
126   - const [startTs, endTs] = dateString;
  107 + function onDateCustomerChange(range: RangePickerValue) {
  108 + if (!range.length) return;
  109 + const [startTs, endTs] = range;
127 110 activeCustomerIndex.value = -1;
128   - getDateData(startTs, endTs, 'CUSTOMER_TREND', customerTrendList);
  111 + getDateData(
  112 + startTs as moment.Moment,
  113 + endTs as moment.Moment,
  114 + 'CUSTOMER_TREND',
  115 + customerTrendList
  116 + );
129 117 }
130 118 return {
131 119 tenantDateValue,
... ...
... ... @@ -124,12 +124,16 @@
124 124 const { largeDesignerPrefix } = useGlobSetting();
125 125
126 126 const handlePreview = (record: BigScreenCenterItemsModel) => {
127   - window.open(`${largeDesignerPrefix}/#/chart/preview/${record.id}`);
  127 + window.open(
  128 + `${largeDesignerPrefix}/#/chart/preview/${record.id}?organizationId=${record.organizationId}`
  129 + );
128 130 };
129 131
130 132 const handleDesign = (record: BigScreenCenterItemsModel) => {
131 133 if (record.state === 1) return;
132   - window.open(`${largeDesignerPrefix}/#/chart/home/${record.id}`);
  134 + window.open(
  135 + `${largeDesignerPrefix}/#/chart/home/${record.id}?organizationId=${record.organizationId}`
  136 + );
133 137 };
134 138
135 139 const handleDelete = async (record: BigScreenCenterItemsModel) => {
... ...
... ... @@ -10,7 +10,7 @@
10 10 </div>
11 11 </template>
12 12 <script lang="ts">
13   - import { defineComponent, ref } from 'vue';
  13 + import { defineComponent, nextTick, ref } from 'vue';
14 14 import { BasicForm, useForm } from '/@/components/Form';
15 15 import { CommandFieldsEnum, CommandSchemas, CommandType, ValueType } from '../../config/data';
16 16 import { commandIssuanceApi } from '/@/api/device/deviceManager';
... ... @@ -35,7 +35,7 @@
35 35 const { createMessage } = useMessage();
36 36 const loading = ref(false);
37 37
38   - const [registerForm, { getFieldsValue, validate, resetFields }] = useForm({
  38 + const [registerForm, { getFieldsValue, validate, resetFields, clearValidate }] = useForm({
39 39 labelWidth: 120,
40 40 schemas: CommandSchemas(
41 41 props.deviceDetail.deviceProfile.transportType as TransportTypeEnum,
... ... @@ -47,8 +47,10 @@
47 47 showResetButton: false,
48 48 });
49 49
50   - const handleCancel = () => {
51   - resetFields();
  50 + const handleCancel = async () => {
  51 + await resetFields();
  52 + await nextTick();
  53 + await clearValidate();
52 54 };
53 55
54 56 const handleOk = async () => {
... ...
... ... @@ -36,6 +36,7 @@
36 36 <Authority value="api:yt:things_model:import">
37 37 <Upload
38 38 v-if="isShowBtn"
  39 + accept=".json,"
39 40 :show-upload-list="false"
40 41 :customRequest="handleImportModel"
41 42 >
... ...
... ... @@ -22,7 +22,7 @@
22 22 </Authority>
23 23 </template>
24 24 <template #status="{ record }">
25   - <Authority value="api:yt:message:status">
  25 + <Authority value="api:yt:message:update">
26 26 <Switch
27 27 :checked="record.status === 1"
28 28 :loading="record.pendingStatus"
... ... @@ -32,7 +32,7 @@
32 32 />
33 33 </Authority>
34 34 <Tag
35   - v-if="!hasPermission('api:yt:message:status')"
  35 + v-if="!hasPermission('api:yt:message:update')"
36 36 :color="record.status ? 'green' : 'red'"
37 37 >
38 38 {{ record.status ? '启用' : '禁用' }}
... ...
... ... @@ -22,7 +22,7 @@
22 22 </a-button>
23 23 </template>
24 24 <template #status="{ record }">
25   - <Authority value="api:yt:template:status">
  25 + <Authority value="api:yt:template:update">
26 26 <Switch
27 27 :checked="record.status === 1"
28 28 :loading="record.pendingStatus"
... ... @@ -32,7 +32,7 @@
32 32 />
33 33 </Authority>
34 34 <Tag
35   - v-if="!hasPermission('api:yt:template:status')"
  35 + v-if="!hasPermission('api:yt:template:update')"
36 36 :color="record.status ? 'green' : 'red'"
37 37 >
38 38 {{ record.status ? '启用' : '禁用' }}
... ...
... ... @@ -4,12 +4,12 @@
4 4 <div style="height: 30px"></div>
5 5 <BasicForm @register="registerForm" />
6 6 <div class="flex flex-end" style="float: right">
7   - <a-button type="primary" @click="handleDeviceState">处理</a-button>
  7 + <a-button v-if="!isHandler" type="primary" @click="handleDeviceState">处理</a-button>
8 8 </div>
9 9 </BasicDrawer>
10 10 </template>
11 11 <script lang="ts" setup>
12   - import { reactive } from 'vue';
  12 + import { reactive, ref } from 'vue';
13 13 import { BasicForm, useForm } from '/@/components/Form';
14 14 import { formHandleSchema, formDetailSchema } from './config.data';
15 15 import { BasicDrawer, useDrawerInner } from '/@/components/Drawer';
... ... @@ -26,7 +26,7 @@
26 26 obj: {},
27 27 });
28 28
29   - const [registerForm, { resetFields, getFieldsValue }] = useForm({
  29 + const [registerForm, { resetFields, getFieldsValue, setFieldsValue }] = useForm({
30 30 schemas: formHandleSchema,
31 31 showActionButtonGroup: false,
32 32 });
... ... @@ -37,11 +37,15 @@
37 37 layout: 'vertical',
38 38 });
39 39
  40 + const isHandler = ref(false);
  41 +
40 42 const [registerDrawer, { setDrawerProps, closeDrawer }] = useDrawerInner(async (data) => {
41 43 await resetFields();
42 44 setDrawerProps({ confirmLoading: false });
43 45 record.obj = data.record;
44 46 setDescProps({ data: data.record });
  47 + isHandler.value = data?.record?.remark;
  48 + setFieldsValue(data.record);
45 49 });
46 50
47 51 const handleDeviceState = async () => {
... ...
... ... @@ -194,6 +194,10 @@ export const formSchema: DescItem[] = [
194 194 field: 'createTime',
195 195 label: '时间',
196 196 },
  197 + {
  198 + field: 'remark',
  199 + label: '备注',
  200 + },
197 201 ];
198 202
199 203 export const formDetailSchema: DescItem[] = [
... ... @@ -221,7 +225,7 @@ export const formDetailSchema: DescItem[] = [
221 225 //处理表单
222 226 export const formHandleSchema: FormSchema[] = [
223 227 {
224   - field: 'description',
  228 + field: 'remark',
225 229 label: '备注',
226 230 colProps: { span: 24 },
227 231 component: 'InputTextArea',
... ...
... ... @@ -20,6 +20,7 @@
20 20 label: '处理',
21 21 auth: 'api:yt:device:state:log::post',
22 22 icon: 'clarity:note-edit-line',
  23 + ifShow: () => !record?.remark,
23 24 onClick: handleView.bind(null, record),
24 25 },
25 26 {
... ...
... ... @@ -74,7 +74,7 @@
74 74 />
75 75 </template>
76 76 <template #configStatus="{ record }">
77   - <Authority :value="PermissionReportConfigEnum.PERMISSION_STATUS">
  77 + <Authority :value="PermissionReportConfigEnum.PERMISSION_UPDATE">
78 78 <Switch
79 79 :disabled="disabledSwitch"
80 80 :checked="record.status === 1"
... ... @@ -85,7 +85,7 @@
85 85 />
86 86 </Authority>
87 87 <Tag
88   - v-if="!hasPermission(PermissionReportConfigEnum.PERMISSION_STATUS)"
  88 + v-if="!hasPermission(PermissionReportConfigEnum.PERMISSION_UPDATE)"
89 89 :color="record.status ? 'green' : 'red'"
90 90 >
91 91 {{ record.status ? '启用' : '禁用' }}
... ...
... ... @@ -31,9 +31,9 @@
31 31 import { CredentialsEnum } from '../mqtt/enum';
32 32
33 33 const credentialsFile = reactive({
34   - caCertFileName: '',
35   - certFileName: '',
36   - privateKeyFileName: '',
  34 + caCertFileName: undefined,
  35 + certFileName: undefined,
  36 + privateKeyFileName: undefined,
37 37 });
38 38
39 39 const [register, { validateFields, setFieldsValue, resetFields }] = useForm({
... ... @@ -50,6 +50,11 @@
50 50 const getValue = async () => {
51 51 const values = await validateFields();
52 52 if (!values) return;
  53 + // if (values.type == 'anonymous' || values.type == 'basic') {
  54 + // credentialsFile.caCertFileName = undefined;
  55 + // credentialsFile.certFileName = undefined;
  56 + // credentialsFile.privateKeyFileName = undefined;
  57 + // }
53 58 const credentials = {
54 59 type: values['type'],
55 60 ...credentialsFile,
... ...
... ... @@ -51,10 +51,16 @@
51 51 const getValue = async () => {
52 52 const values = await validateFields();
53 53 if (!values) return;
  54 + if (values.type == 'anonymous' || values.type == 'basic') {
  55 + credentialsFile.caCertFileName = undefined;
  56 + credentialsFile.certFileName = undefined;
  57 + credentialsFile.privateKeyFileName = undefined;
  58 + }
54 59 const credentials = {
55 60 type: values['type'],
56 61 ...credentialsFile,
57 62 };
  63 +
58 64 const mergeValues = {
59 65 ...values,
60 66 ...{ credentials },
... ... @@ -67,7 +73,12 @@
67 73 if (credentials) {
68 74 for (let i in credentials) Reflect.set(credentialsFile, i, credentials[i]);
69 75 }
70   - setFieldsValue({ ...value, type: credentials.type });
  76 + setFieldsValue({
  77 + ...value,
  78 + type: credentials.type,
  79 + username: credentials.username,
  80 + password: credentials.password,
  81 + });
71 82 };
72 83
73 84 const resetValue = () => resetFields();
... ...
... ... @@ -18,7 +18,7 @@ class MqttFormPartialConfig {
18 18 return [
19 19 { label: 'Anonymous', value: 'anonymous' },
20 20 { label: 'Basic', value: 'basic' },
21   - { label: 'PEM', value: 'pem' },
  21 + { label: 'PEM', value: 'cert.PEM' },
22 22 ];
23 23 }
24 24 }
... ... @@ -138,9 +138,21 @@ export const modeMqttForm: FormSchema[] = [
138 138 label: '凭据类型',
139 139 colProps: { span: 12 },
140 140 defaultValue: MqttFormPartialConfig.type,
141   - componentProps: {
142   - placeholder: '请选择Credentials',
143   - options: MqttFormPartialConfig.getType(),
  141 + componentProps: ({ formActionType }) => {
  142 + const { setFieldsValue } = formActionType;
  143 + return {
  144 + placeholder: '请选择Credentials',
  145 + options: MqttFormPartialConfig.getType(),
  146 + onChange(e) {
  147 + if (e) {
  148 + console.log('执行');
  149 + setFieldsValue({
  150 + password: undefined,
  151 + username: undefined,
  152 + });
  153 + }
  154 + },
  155 + };
144 156 },
145 157 },
146 158 {
... ...
... ... @@ -6,7 +6,7 @@
6 6 export enum CredentialsEnum {
7 7 IS_ANONYMOUS = 'anonymous',
8 8 IS_BASIC = 'basic',
9   - IS_PEM = 'pem',
  9 + IS_PEM = 'cert.PEM',
10 10 }
11 11
12 12 export const isBasic = (type: string) => {
... ...
... ... @@ -128,8 +128,9 @@
128 128 : undefined,
129 129 appendClientIdSuffix: getDataFlowParams.appendClientIdSuffix || undefined,
130 130 type: undefined,
  131 + username: undefined,
  132 + password: undefined,
131 133 };
132   -
133 134 const rest = isRabbitmq(getDataFlowMethod?.type)
134 135 ? await postAddConvertApi({ ...restData.data, ...data })
135 136 : await postAddConvertApi({ ...restData.data, ...data, configuration });
... ...
... ... @@ -79,7 +79,7 @@
79 79 />
80 80 </template>
81 81 <template #status="{ record }">
82   - <Authority :value="PermissionDataFlowEnum.PERMISSION_STATUS">
  82 + <Authority :value="PermissionDataFlowEnum.PERMISSION_UPDATE">
83 83 <Switch
84 84 :checked="record.status === 1"
85 85 :loading="record.pendingStatus"
... ... @@ -89,7 +89,7 @@
89 89 />
90 90 </Authority>
91 91 <Tag
92   - v-if="!hasPermission(PermissionDataFlowEnum.PERMISSION_STATUS)"
  92 + v-if="!hasPermission(PermissionDataFlowEnum.PERMISSION_UPDATE)"
93 93 :color="record.status ? 'green' : 'red'"
94 94 >
95 95 {{ record.status ? '启用' : '禁用' }}
... ...
... ... @@ -755,6 +755,18 @@ export const actionSchema: FormSchema[] = [
755 755 component: 'Select',
756 756 label: '',
757 757 show: ({ values }) => isAlarmOut(values.outTarget),
  758 + // dynamicRules: (params) => {
  759 + // const { outTarget } = params.values;
  760 + // return [
  761 + // {
  762 + // required: outTarget == 'MSG_NOTIFY' ? true : false,
  763 + // validator: (_, value) => {
  764 + // if (!value) return Promise.reject('请选择告警配置');
  765 + // Promise.resolve();
  766 + // },
  767 + // },
  768 + // ];
  769 + // },
758 770 componentProps: {
759 771 placeholder: '请选择告警等级',
760 772 options: [
... ...
... ... @@ -273,6 +273,10 @@
273 273 if (validate.device == 'PART' && validate.deviceId == undefined)
274 274 return createMessage.error('请选择设备');
275 275 }
  276 + if (type === 'MSG_NOTIFY') {
  277 + if (!validate.alarm_config) return createMessage.error('请选择告警配置');
  278 + if (!validate.alarm_level) return createMessage.error('请选择告警等级');
  279 + }
276 280 //ft-add-2022-11-22
277 281 //TODO-fengtao-设备验证
278 282 const value = getFieldsValue();
... ...
... ... @@ -50,7 +50,7 @@
50 50 </template>
51 51
52 52 <template #status="{ record }">
53   - <Authority value="api:yt:sceneLinkage:status">
  53 + <Authority value="api:yt:sceneLinkage:update">
54 54 <Switch
55 55 :checked="record.status === 1"
56 56 :loading="record.pendingStatus"
... ... @@ -60,7 +60,7 @@
60 60 />
61 61 </Authority>
62 62 <Tag
63   - v-if="!hasPermission('api:yt:sceneLinkage:status')"
  63 + v-if="!hasPermission('api:yt:sceneLinkage:update')"
64 64 :color="record.status ? 'green' : 'red'"
65 65 >
66 66 {{ record.status ? '启用' : '禁用' }}
... ...
... ... @@ -17,7 +17,7 @@
17 17 </Authority>
18 18 </template>
19 19 <template #status="{ record }">
20   - <Authority value="api:yt:convert:js:status">
  20 + <Authority value="api:yt:convert:js:update">
21 21 <Switch
22 22 :checked="record.status === 1"
23 23 :loading="record.pendingStatus"
... ... @@ -27,7 +27,7 @@
27 27 />
28 28 </Authority>
29 29 <Tag
30   - v-if="!hasPermission('api:yt:convert:js:status')"
  30 + v-if="!hasPermission('api:yt:convert:js:update')"
31 31 :color="record.status ? 'green' : 'red'"
32 32 >
33 33 {{ record.status ? '启用' : '禁用' }}
... ...
... ... @@ -71,7 +71,7 @@
71 71 />
72 72 </template>
73 73 <template #status="{ record }">
74   - <Authority :value="PermissionConvertScriptEnum.PERMISSION_UPDATE_STATUS">
  74 + <Authority :value="PermissionConvertScriptEnum.PERMISSION_UPDATE">
75 75 <Switch
76 76 :checked="record.status === 1"
77 77 :loading="record.pendingStatus"
... ... @@ -81,7 +81,7 @@
81 81 />
82 82 </Authority>
83 83 <Tag
84   - v-if="!hasPermission(PermissionConvertScriptEnum.PERMISSION_UPDATE_STATUS)"
  84 + v-if="!hasPermission(PermissionConvertScriptEnum.PERMISSION_UPDATE)"
85 85 :color="record.status ? 'green' : 'red'"
86 86 >
87 87 {{ record.status ? '启用' : '禁用' }}
... ...
... ... @@ -21,7 +21,7 @@
21 21 </Authority>
22 22 </template>
23 23 <template #status="{ record }">
24   - <Authority value="api:yt:role:status">
  24 + <Authority value="api:yt:role:saveOrUpdateRoleInfoWithMenu:update">
25 25 <Switch
26 26 :checked="record.status === 1"
27 27 :loading="record.pendingStatus"
... ... @@ -30,7 +30,10 @@
30 30 @change="(checked:boolean)=>statusChange(checked,record)"
31 31 />
32 32 </Authority>
33   - <Tag v-if="!hasPermission('api:yt:role:status')" :color="record.status ? 'green' : 'red'">
  33 + <Tag
  34 + v-if="!hasPermission('api:yt:role:saveOrUpdateRoleInfoWithMenu:update')"
  35 + :color="record.status ? 'green' : 'red'"
  36 + >
34 37 {{ record.status ? '启用' : '禁用' }}
35 38 </Tag>
36 39 </template>
... ...
... ... @@ -2,7 +2,7 @@
2 2 import { BasicForm, useForm } from '/@/components/Form';
3 3 import { BasicModal, useModalInner } from '/@/components/Modal';
4 4 import { formSchemas } from './config';
5   - import { ref } from 'vue';
  5 + import { nextTick, ref } from 'vue';
6 6 import { composeData, parseData } from './util';
7 7 import { createTask, updateTask } from '/@/api/task';
8 8 import { ModalParamsType } from '/#/utils';
... ... @@ -33,7 +33,10 @@
33 33 modalMode.value = mode;
34 34 dataSource.value = record;
35 35 formMode.value = mode;
36   - resetFields();
  36 + resetFields().then(async () => {
  37 + await nextTick();
  38 + clearValidate();
  39 + });
37 40 if (record && mode === DataActionModeEnum.UPDATE) {
38 41 const res = parseData(record);
39 42 setFieldsValue({ ...res });
... ... @@ -41,13 +44,14 @@
41 44 }
42 45 );
43 46
44   - const [registerForm, { getFieldsValue, validate, setFieldsValue, resetFields }] = useForm({
45   - schemas: formSchemas,
46   - showActionButtonGroup: false,
47   - layout: 'inline',
48   - baseColProps: { span: 24 },
49   - labelWidth: 140,
50   - });
  47 + const [registerForm, { getFieldsValue, validate, setFieldsValue, resetFields, clearValidate }] =
  48 + useForm({
  49 + schemas: formSchemas,
  50 + showActionButtonGroup: false,
  51 + layout: 'inline',
  52 + baseColProps: { span: 24 },
  53 + labelWidth: 140,
  54 + });
51 55
52 56 const loading = ref(false);
53 57 const { createMessage } = useMessage();
... ...
... ... @@ -124,7 +124,6 @@ export const parseData = (result: TaskRecordType): Required<FormValueType> => {
124 124 TaskRecordType['executeTarget']
125 125 >;
126 126 const { type: executeTimeType, period, periodType, time, pollUnit } = executeTime;
127   - console.log(pushWay === PushWayEnum.MQTT ? JSON.stringify(rpcCommand, null, 2) : rpcCommand);
128 127 return {
129 128 name,
130 129 targetType,
... ...
... ... @@ -188,7 +188,9 @@
188 188 if (hasDetailPermission) {
189 189 const boardId = encode(record.id);
190 190 const boardName = encode(record.name);
191   - router.push(`/visual/board/detail/${boardId}/${boardName}`);
  191 + const organizationId = encode(record?.organizationId);
  192 +
  193 + router.push(`/visual/board/detail/${boardId}/${boardName}/${organizationId}`);
192 194 } else createMessage.warning('没有权限');
193 195 };
194 196
... ...
... ... @@ -12,10 +12,6 @@
12 12 [PackagesCategoryEnum.MAP]: [
13 13 '地图组件,需绑定两个数据源,且数据源为同一设备。第一数据源为经度,第二数据源为纬度,否则地图组件不能正常显示。',
14 14 ],
15   - [PackagesCategoryEnum.CONTROL]: [
16   - '控制组件数据源为TCP产品,则其控制命令下发为TCP产品 物模型=>服务,且不具备状态显示功能.',
17   - '控制组件数据源为非TCP产品,则其控制命令下发为产品 物模型=>属性,且具备状态显示功能.',
18   - ],
19 15 };
20 16
21 17 const getMessage = computed(() => {
... ...
... ... @@ -90,6 +90,7 @@ export const commonDataSourceSchemas = (): FormSchema[] => {
90 90 const isUpdate = unref(mode) === DataActionModeEnum.UPDATE;
91 91 const selectWidgetKeys = useSelectWidgetKeys();
92 92 const category = unref(selectWidgetKeys).categoryKey;
  93 +
93 94 return [
94 95 {
95 96 field: DataSourceField.IS_GATEWAY_DEVICE,
... ... @@ -191,6 +192,9 @@ export const commonDataSourceSchemas = (): FormSchema[] => {
191 192 });
192 193 },
193 194 showCreate: false,
  195 + apiTreeSelectProps: {
  196 + params: { organizationId: location?.pathname?.split('/')?.pop() || '' },
  197 + },
194 198 getPopupContainer: () => document.body,
195 199 };
196 200 },
... ...