Commit 07ef1fd239b7a52bfa0cf2b970f57885729fa032

Authored by fengwotao
2 parents 038d1b5f c5077dc9

Merge branch 'main_dev' into ft

... ... @@ -5,6 +5,13 @@ export enum DataActionModeEnum {
5 5 DELETE = 'DELETE',
6 6 }
7 7
  8 +export enum DataActionModeNameEnum {
  9 + CREATE = '创建',
  10 + READ = '查看',
  11 + UPDATE = '编辑',
  12 + DELETE = '删除',
  13 +}
  14 +
8 15 export enum TimeUnitEnum {
9 16 SECOND = 'SECOND',
10 17 MINUTE = 'MINUTE',
... ...
... ... @@ -3,9 +3,13 @@
3 3 import { BasicModal, useModalInner } from '/@/components/Modal';
4 4 import { FieldsEnum, schemas, ViewTypeEnum } from './config';
5 5 import { DataBoardRecord } from '/@/api/dataBoard/model';
6   - import { Alert } from 'ant-design-vue';
  6 + import { Alert, Button, Tooltip } from 'ant-design-vue';
7 7 import { ref, unref } from 'vue';
8 8 import { nextTick } from 'vue';
  9 + import { ModalParamsType } from '/#/utils';
  10 + import { useCopyToClipboard } from '/@/hooks/web/useCopyToClipboard';
  11 + import { useMessage } from '/@/hooks/web/useMessage';
  12 + import { computed } from 'vue';
9 13
10 14 const props = defineProps<{
11 15 shareApi?: (...args: any[]) => Promise<any>;
... ... @@ -13,15 +17,20 @@
13 17
14 18 const loading = ref(false);
15 19 const record = ref<DataBoardRecord>({} as unknown as DataBoardRecord);
  20 + const link = ref('');
16 21
17   - const [register, { closeModal }] = useModalInner(async (data: DataBoardRecord) => {
18   - record.value = data;
19   - await nextTick();
20   - setFieldsValue({
21   - [FieldsEnum.IS_SHARE]: data.viewType === ViewTypeEnum.PUBLIC_VIEW,
22   - [FieldsEnum.ACCESS_CREDENTIALS]: data.accessCredentials,
23   - });
24   - });
  22 + const [register, { closeModal }] = useModalInner(
  23 + async (data: ModalParamsType<DataBoardRecord>) => {
  24 + const { record: value, href } = data;
  25 + record.value = value;
  26 + link.value = href;
  27 + await nextTick();
  28 + setFieldsValue({
  29 + [FieldsEnum.IS_SHARE]: value.viewType === ViewTypeEnum.PUBLIC_VIEW,
  30 + [FieldsEnum.ACCESS_CREDENTIALS]: value.accessCredentials,
  31 + });
  32 + }
  33 + );
25 34
26 35 const [registerForm, { getFieldsValue, setFieldsValue }] = useForm({
27 36 schemas,
... ... @@ -32,6 +41,10 @@
32 41
33 42 const emit = defineEmits(['register', 'success']);
34 43
  44 + const handleOpenLink = () => {
  45 + window.open(unref(link));
  46 + };
  47 +
35 48 const handleSubmit = async () => {
36 49 try {
37 50 loading.value = true;
... ... @@ -44,6 +57,19 @@
44 57 loading.value = false;
45 58 }
46 59 };
  60 +
  61 + const isPublicState = computed(() => {
  62 + return unref(record).viewType === ViewTypeEnum.PUBLIC_VIEW;
  63 + });
  64 +
  65 + const { clipboardRef, isSuccessRef } = useCopyToClipboard();
  66 + const { createMessage } = useMessage();
  67 + const handleCopy = () => {
  68 + clipboardRef.value = unref(link);
  69 + if (unref(isSuccessRef)) {
  70 + createMessage.success('复制成功~');
  71 + }
  72 + };
47 73 </script>
48 74
49 75 <template>
... ... @@ -54,5 +80,27 @@
54 80 message="私有视图只有项目成员可以浏览,公开视图拥有一个公开的 URL,任何人无需登录即可浏览."
55 81 />
56 82 <BasicForm @register="registerForm" />
  83 + <section>
  84 + <div v-if="isPublicState" class="mt-4">
  85 + <span> 设置分享后, 可通过如下 </span>
  86 + <Button type="link" class="!px-1" @click="handleOpenLink"> 链接 </Button>
  87 + <span>访问:</span>
  88 + </div>
  89 + <Tooltip v-if="isPublicState" title="点击复制链接">
  90 + <div
  91 + class="px-4 py-2 my-2 bg-gray-50 font-semibold tracking-wider text-base cursor-pointer truncate"
  92 + @click="handleCopy"
  93 + >
  94 + <span>{{ link }}</span>
  95 + </div>
  96 + </Tooltip>
  97 +
  98 + <Alert type="warning">
  99 + <template #message>
  100 + <span class="font-bold mr-1">提示:</span>
  101 + <span>不要忘记将相关设备 <span class="mx-1 font-bold">公开</span> 以访问其数据</span>
  102 + </template>
  103 + </Alert>
  104 + </section>
57 105 </BasicModal>
58 106 </template>
... ...
... ... @@ -28,6 +28,7 @@
28 28 import { ViewTypeNameEnum } from '../../common/ShareModal/config';
29 29 import { useModal } from '/@/components/Modal';
30 30 import { useCopyToClipboard } from '/@/hooks/web/useCopyToClipboard';
  31 + import { ViewType } from '../../visual/board/config/panelDetail';
31 32
32 33 const listColumn = ref(5);
33 34
... ... @@ -153,17 +154,20 @@
153 154 getListData();
154 155 };
155 156
156   - const { clipboardRef, isSuccessRef } = useCopyToClipboard();
157   - const handleCreateShareUrl = (record: ConfigurationCenterItemsModal) => {
158   - if (!unref(getShareFlag)) return;
159   - const { origin } = location;
  157 + const createShareUrl = (record: ConfigurationCenterItemsModal) => {
160 158 const searchParams = new URLSearchParams();
161 159 isDev && searchParams.set('dev', '1');
162 160 searchParams.set('share', 'SCADA');
163 161 searchParams.set('configurationId', record.id);
164 162 searchParams.set('publicId', record.publicId || '');
165 163 searchParams.set('lightbox', '1');
166   - const url = `${origin}${configurationPrefix}/?${searchParams.toString()}`;
  164 + return `${origin}${configurationPrefix}/?${searchParams.toString()}`;
  165 + };
  166 +
  167 + const { clipboardRef, isSuccessRef } = useCopyToClipboard();
  168 + const handleCreateShareUrl = (record: ConfigurationCenterItemsModal) => {
  169 + if (!unref(getShareFlag)) return;
  170 + const url = createShareUrl(record);
167 171 clipboardRef.value = url;
168 172 if (unref(isSuccessRef)) {
169 173 createMessage.success('复制成功~');
... ... @@ -173,7 +177,7 @@
173 177 const [registerShareModal, { openModal }] = useModal();
174 178
175 179 const handleOpenShareModal = (record: ConfigurationCenterItemsModal) => {
176   - openModal(true, record);
  180 + openModal(true, { record, href: createShareUrl(record) });
177 181 };
178 182
179 183 const listEl = ref<Nullable<ComponentElRef>>(null);
... ... @@ -228,7 +232,13 @@
228 232 </template>
229 233 <template #renderItem="{ item }">
230 234 <List.Item>
231   - <Card hoverable class="card-container">
  235 + <Card
  236 + :style="{
  237 + '--viewType': item.viewType === ViewType.PUBLIC_VIEW ? '#1890ff' : '#faad14',
  238 + }"
  239 + hoverable
  240 + class="card-container"
  241 + >
232 242 <template #cover>
233 243 <div
234 244 class="img-container h-full w-full !flex justify-center items-center text-center p-1 relative"
... ... @@ -357,6 +367,6 @@
357 367 }
358 368
359 369 .card-container:deep(.ant-card-cover) {
360   - background-color: #2db7f5;
  370 + background-color: var(--viewType);
361 371 }
362 372 </style>
... ...
... ... @@ -29,6 +29,7 @@
29 29 import { ShareModal } from '/@/views/common/ShareModal';
30 30 import { ViewTypeNameEnum } from '../common/ShareModal/config';
31 31 import { useCopyToClipboard } from '/@/hooks/web/useCopyToClipboard';
  32 + import { ViewType } from '../visual/board/config/panelDetail';
32 33
33 34 const listColumn = ref(5);
34 35
... ... @@ -163,15 +164,18 @@
163 164 const getPublicApiListData = () => {};
164 165
165 166 const [registerShareModal, { openModal }] = useModal();
166   - const handleOpenShareModal = (item: BigScreenCenterItemsModel) => {
167   - openModal(true, item);
  167 + const handleOpenShareModal = (record: BigScreenCenterItemsModel) => {
  168 + openModal(true, { record, href: createShareUrl(record) });
  169 + };
  170 +
  171 + const createShareUrl = (record: BigScreenCenterItemsModel) => {
  172 + const { origin } = location;
  173 + return `${origin}${largeDesignerPrefix}/#/share/preview/${record.id}/${record.publicId}`;
168 174 };
169 175
170 176 const { clipboardRef, isSuccessRef } = useCopyToClipboard();
171 177 const handleCreateShareUrl = (record: BigScreenCenterItemsModel) => {
172   - const { origin } = location;
173   - const { largeDesignerPrefix } = useGlobSetting();
174   - clipboardRef.value = `${origin}${largeDesignerPrefix}/#/share/preview/${record.id}/${record.publicId}`;
  178 + clipboardRef.value = createShareUrl(record);
175 179 if (unref(isSuccessRef)) {
176 180 createMessage.success('复制成功~');
177 181 }
... ... @@ -223,7 +227,12 @@
223 227 </template>
224 228 <template #renderItem="{ item }">
225 229 <List.Item>
226   - <Card class="card-container">
  230 + <Card
  231 + :style="{
  232 + '--viewType': item.viewType === ViewType.PUBLIC_VIEW ? '#1890ff' : '#faad14',
  233 + }"
  234 + class="card-container"
  235 + >
227 236 <template #cover>
228 237 <div class="h-full w-full relative hover-show-modal-content img-container">
229 238 <img
... ... @@ -421,6 +430,6 @@
421 430 }
422 431
423 432 .card-container:deep(.ant-card-cover) {
424   - background-color: #2db7f5;
  433 + background-color: var(--viewType);
425 434 }
426 435 </style>
... ...
  1 +import { DescItem } from '/@/components/Description';
  2 +import {
  3 + ExecuteTimeTypeEnum,
  4 + ExecuteTimeTypeNameEnum,
  5 + FormFieldsEnum,
  6 + PeriodTypeNameEnum,
  7 +} from '../DetailModal/config';
  8 +import { TaskStatusEnum, TaskTargetEnum, TaskTargetNameEnum } from '../../config';
  9 +import { h } from 'vue';
  10 +import { Tag } from 'ant-design-vue';
  11 +import { TaskStatusNameEnum } from '../../config';
  12 +import { TaskRecordType } from '/@/api/task/model';
  13 +import { TaskTypeNameEnum } from '../../config';
  14 +import { TimeUnitNameEnum } from '/@/enums/toolEnum';
  15 +import { PeriodTypeEnum } from '../DetailModal/config';
  16 +
  17 +enum WeekEnum {
  18 + MON = 'MON',
  19 + TUES = 'TUES',
  20 + WED = 'WED',
  21 + THUR = 'THUR',
  22 + FRI = 'FRI',
  23 + SAT = 'SAT',
  24 + SUN = 'SUN',
  25 +}
  26 +
  27 +enum WeekNameEnum {
  28 + MON = '星期一',
  29 + TUES = '星期二',
  30 + WED = '星期三',
  31 + THUR = '星期四',
  32 + FRI = '星期五',
  33 + SAT = '星期六',
  34 + SUN = '星期天',
  35 +}
  36 +
  37 +const isWeek = (string: string) => {
  38 + return Object.keys(WeekEnum).some((item) => string.includes(item));
  39 +};
  40 +
  41 +const extractCronExpression = (expression: string) => {
  42 + const flag = isWeek(expression);
  43 + const list = expression.split(' ');
  44 + const unit = flag ? list[list.length - 1] : list[3];
  45 + return flag ? WeekNameEnum[unit] : unit;
  46 +};
  47 +
  48 +export const schemas: DescItem[] = [
  49 + {
  50 + field: FormFieldsEnum.NAME,
  51 + label: '任务名',
  52 + },
  53 + {
  54 + field: FormFieldsEnum.TARGET_TYPE,
  55 + label: '目标类型',
  56 + render(val: TaskTargetEnum) {
  57 + return h(
  58 + Tag,
  59 + { color: val === TaskTargetEnum.DEVICES ? 'blue' : 'cyan' },
  60 + () => TaskTargetNameEnum[val]
  61 + );
  62 + },
  63 + },
  64 + {
  65 + field: 'state',
  66 + label: '任务状态',
  67 + render(val: TaskStatusEnum) {
  68 + return h(
  69 + Tag,
  70 + { color: val === TaskStatusEnum.NORMAL ? 'success' : 'error' },
  71 + () => TaskStatusNameEnum[TaskStatusEnum[val]]
  72 + );
  73 + },
  74 + },
  75 + {
  76 + field: FormFieldsEnum.EXECUTE_CONTENT_TYPE,
  77 + label: '任务类型',
  78 + render(_val: any, data: TaskRecordType) {
  79 + return TaskTypeNameEnum[data.executeContent.type];
  80 + },
  81 + },
  82 + {
  83 + field: FormFieldsEnum.EXECUTE_TIME_TYPE,
  84 + label: '定时方式',
  85 + render(_val: any, data: TaskRecordType) {
  86 + return ExecuteTimeTypeNameEnum[data.executeTime.type];
  87 + },
  88 + },
  89 + {
  90 + field: FormFieldsEnum.EXECUTE_TIME_INTERVAL,
  91 + label: '间隔时间',
  92 + render(_val: any, data: TaskRecordType) {
  93 + const { executeTime } = data;
  94 + const { time, type, periodType, pollUnit, period } = executeTime;
  95 + console.log({ time, type, periodType, pollUnit });
  96 + return type === ExecuteTimeTypeEnum.POLL
  97 + ? `${time}${TimeUnitNameEnum[pollUnit]}`
  98 + : `${PeriodTypeNameEnum[periodType]} ${
  99 + periodType === PeriodTypeEnum.DAY
  100 + ? ''
  101 + : `/ ${extractCronExpression(period)}${isWeek(period) ? '' : '号'}`
  102 + } / ${time}`;
  103 + },
  104 + },
  105 + {
  106 + field: 'createTime',
  107 + label: '创建时间',
  108 + },
  109 + {
  110 + field: 'updateTime',
  111 + label: '修改时间',
  112 + },
  113 +];
... ...
  1 +export { default as DetailDrawer } from './index.vue';
... ...
  1 +<script lang="ts" setup>
  2 + import { useDescription, Description } from '/@/components/Description';
  3 + import { BasicDrawer, useDrawerInner } from '/@/components/Drawer';
  4 + import { schemas } from './config';
  5 + import { ModalParamsType } from '/#/utils';
  6 + import { TaskRecordType } from '/@/api/task/model';
  7 + defineEmits(['register']);
  8 +
  9 + const [registerDrawer] = useDrawerInner((data: ModalParamsType<TaskRecordType>) => {
  10 + const { record } = data;
  11 + setDescProps({ data: record });
  12 + });
  13 +
  14 + const [registerDescription, { setDescProps }] = useDescription({
  15 + schema: schemas,
  16 + labelStyle: { width: '120px' },
  17 + column: 1,
  18 + });
  19 +</script>
  20 +
  21 +<template>
  22 + <BasicDrawer title="任务详情" @register="registerDrawer" width="30%">
  23 + <Description @register="registerDescription" />
  24 + </BasicDrawer>
  25 +</template>
... ...
... ... @@ -263,14 +263,20 @@ export const formSchemas: FormSchema[] = [
263 263 label: '周期',
264 264 required: true,
265 265 defaultValue: PeriodTypeEnum.MONTH,
266   - componentProps: {
267   - placeholder: '请选择周期',
268   - options: [
269   - { label: PeriodTypeNameEnum.DAY, value: PeriodTypeEnum.DAY },
270   - { label: PeriodTypeNameEnum.WEEK, value: PeriodTypeEnum.WEEK },
271   - { label: PeriodTypeNameEnum.MONTH, value: PeriodTypeEnum.MONTH },
272   - ],
273   - getPopupContainer: () => document.body,
  266 + componentProps: ({ formActionType }) => {
  267 + const { setFieldsValue } = formActionType;
  268 + return {
  269 + placeholder: '请选择周期',
  270 + options: [
  271 + { label: PeriodTypeNameEnum.DAY, value: PeriodTypeEnum.DAY },
  272 + { label: PeriodTypeNameEnum.WEEK, value: PeriodTypeEnum.WEEK },
  273 + { label: PeriodTypeNameEnum.MONTH, value: PeriodTypeEnum.MONTH },
  274 + ],
  275 + getPopupContainer: () => document.body,
  276 + onChange() {
  277 + setFieldsValue({ [FormFieldsEnum.EXECUTE_TIME_PERIOD]: null });
  278 + },
  279 + };
274 280 },
275 281 ifShow: ({ model }) => model[FormFieldsEnum.EXECUTE_TIME_TYPE] === ExecuteTimeTypeEnum.CUSTOM,
276 282 },
... ...
... ... @@ -6,10 +6,11 @@
6 6 import { composeData, parseData } from './util';
7 7 import { createTask, updateTask } from '/@/api/task';
8 8 import { ModalParamsType } from '/#/utils';
9   - import { DataActionModeEnum } from '/@/enums/toolEnum';
  9 + import { DataActionModeEnum, DataActionModeNameEnum } from '/@/enums/toolEnum';
10 10 import { TaskRecordType } from '/@/api/task/model';
11 11 import { FormValueType } from './util';
12 12 import { unref } from 'vue';
  13 + import { computed } from 'vue';
13 14
14 15 const props = defineProps<{
15 16 reload: Fn;
... ... @@ -18,6 +19,8 @@
18 19
19 20 const formMode = ref(DataActionModeEnum.CREATE);
20 21
  22 + const modalMode = ref<DataActionModeEnum>(DataActionModeEnum.CREATE);
  23 +
21 24 const dataSource = ref<TaskRecordType>();
22 25 const [registerModal, { closeModal }] = useModalInner(
23 26 (
... ... @@ -26,6 +29,7 @@
26 29 mode: DataActionModeEnum.CREATE,
27 30 }
28 31 ) => {
  32 + modalMode.value = mode;
29 33 dataSource.value = record;
30 34 formMode.value = mode;
31 35 resetFields();
... ... @@ -62,12 +66,16 @@
62 66 loading.value = false;
63 67 }
64 68 };
  69 +
  70 + const getTitle = computed(() => {
  71 + return `${DataActionModeNameEnum[unref(modalMode)]}任务`;
  72 + });
65 73 </script>
66 74
67 75 <template>
68 76 <BasicModal
69 77 @register="registerModal"
70   - title="创建任务"
  78 + :title="getTitle"
71 79 width="700px"
72 80 :okButtonProps="{ loading }"
73 81 @ok="handleOk"
... ...
1   -import CronParser, { CronFields, HourRange, SixtyRange } from 'cron-parser';
  1 +import { HourRange, SixtyRange } from 'cron-parser';
2 2 import { dateUtil } from '/@/utils/dateUtil';
3   -import { ExecuteTimeTypeEnum, FormFieldsEnum, PushWayEnum } from './config';
  3 +import { ExecuteTimeTypeEnum, FormFieldsEnum, PeriodTypeEnum, PushWayEnum } from './config';
4 4 import { CreateTaskRecordType, TaskRecordType } from '/@/api/task/model';
5 5 import { DeviceCascadePickerValueType } from '../DevicePicker';
6 6 import { TaskTargetEnum } from '../../config';
... ... @@ -12,52 +12,31 @@ export interface FormValueType extends Partial<Record<FormFieldsEnum, any>> {
12 12 [FormFieldsEnum.DEVICE_PROFILE]: ProductCascadePickerValueType;
13 13 }
14 14
15   -type CanWrite<T> = {
16   - -readonly [K in keyof T]: T[K];
17   -};
18   -
19 15 interface GenCronExpressionResultType {
20 16 effective: boolean;
21 17 expression?: string;
22 18 }
23 19
24   -export const usePluginGenCronExpression = (
25   - time: string,
26   - expression = '* * * * * * *',
27   - includesYear = true
28   -): GenCronExpressionResultType => {
29   - try {
30   - const separator = ' ';
31   - const removeYear = expression.split(separator).slice(0, 6).join(separator);
32   -
33   - const date = dateUtil(time, 'HH:mm:ss');
34   -
35   - const second = date.get('second') as SixtyRange;
36   - const minute = date.get('minute') as SixtyRange;
37   - const hour = date.get('hour') as HourRange;
38   -
39   - const result = CronParser.parseExpression(removeYear, { utc: true, nthDayOfWeek: 4 });
40   - const fields = JSON.parse(JSON.stringify(result.fields)) as CanWrite<CronFields>;
41   - fields.second = [second];
42   - fields.minute = [minute];
43   - fields.hour = [hour];
44   -
45   - // console.log(CronParser.fieldsToExpression(CronParser.parseExpression('').fields).stringify());
46   -
47   - let newExpression = CronParser.fieldsToExpression(fields).stringify(true);
48   - newExpression = includesYear ? `${newExpression} *` : newExpression;
49   - return { effective: true, expression: newExpression };
50   - } catch (error) {
51   - // throw error;
52   - return { effective: false };
53   - }
  20 +export const genPollCronExpression = (value: number, type: TimeUnitEnum) => {
  21 + const placeholder = 'placeholder';
  22 + const expression = {
  23 + [TimeUnitEnum.SECOND]: `${placeholder} * * * * ? *`,
  24 + [TimeUnitEnum.MINUTE]: `* ${placeholder} * * * ? *`,
  25 + [TimeUnitEnum.HOUR]: `* * ${placeholder} * * ? *`,
  26 + };
  27 + const newExpression = expression[type];
  28 + return newExpression.replace(placeholder, `0/${value}`);
54 29 };
55 30
56 31 export const genCronExpression = (
57 32 time: string,
58   - expression = '* * * * * * *'
  33 + expression = '* * * * * * *',
  34 + periodType: PeriodTypeEnum
59 35 ): GenCronExpressionResultType => {
60 36 try {
  37 + if (periodType === PeriodTypeEnum.DAY) {
  38 + expression = '* * * * * ? *';
  39 + }
61 40 const separator = ' ';
62 41 const list: (string | number)[] = expression.split(separator);
63 42
... ... @@ -103,10 +82,12 @@ export const composeData = (result: Required<FormValueType>): CreateTaskRecordTy
103 82 deviceType: productDeviceType,
104 83 } = deviceProfile || {};
105 84
106   - const { expression } = genCronExpression(time, period);
  85 + const { expression } = genCronExpression(time, period, periodType);
107 86
108 87 const cron =
109   - executeTimeType === ExecuteTimeTypeEnum.POLL ? `0/${interval} * * * * ? *` : expression!;
  88 + executeTimeType === ExecuteTimeTypeEnum.POLL
  89 + ? genPollCronExpression(interval, pollUnit)
  90 + : expression!;
110 91
111 92 return {
112 93 name,
... ...
... ... @@ -63,8 +63,9 @@ export const formSchemas: FormSchema[] = [
63 63 {
64 64 field: FormFieldsEnum.TARGET_IDS,
65 65 component: 'ApiSelect',
66   - label: '定目标设备',
  66 + label: '定目标设备',
67 67 ifShow: ({ model }) => model[FormFieldsEnum.EXECUTE_TARGET_TYPE] === TargetType.ASSIGN,
  68 + rules: [{ required: true, message: '请选择指定目标设备' }],
68 69 componentProps: ({ formModel }) => {
69 70 const record = JSON.parse(formModel[FormFieldsEnum.TASK_RECORD]) as TaskRecordType;
70 71 const isDevices = record.targetType === TaskTargetEnum.DEVICES;
... ...
... ... @@ -24,7 +24,7 @@
24 24 }
25 25 });
26 26
27   - const [registerForm, { setFieldsValue, getFieldsValue, resetFields }] = useForm({
  27 + const [registerForm, { setFieldsValue, getFieldsValue, resetFields, validate }] = useForm({
28 28 schemas: formSchemas,
29 29 showActionButtonGroup: false,
30 30 });
... ... @@ -37,7 +37,8 @@
37 37 ? TaskTargetEnum.DEVICES
38 38 : unref(dataSource)!.targetType,
39 39 id: unref(dataSource)!.id,
40   - targetIds,
  40 + targetIds:
  41 + executeTarget === TargetType.ASSIGN ? targetIds : unref(dataSource)?.executeTarget.data,
41 42 cronExpression: unref(dataSource)!.executeTime.cron,
42 43 name: unref(dataSource)!.name,
43 44 };
... ... @@ -46,6 +47,7 @@
46 47 const loading = ref(false);
47 48 const { createMessage } = useMessage();
48 49 const handleOk = async () => {
  50 + await validate();
49 51 const record = getFieldsValue() as FormValue;
50 52 const value = composeData(record);
51 53 try {
... ...
... ... @@ -21,6 +21,7 @@
21 21 enum DropMenuEvent {
22 22 DELETE = 'DELETE',
23 23 EDIT = 'EDIT',
  24 + DETAIL = 'DETAIL',
24 25 }
25 26
26 27 const props = withDefaults(
... ... @@ -35,7 +36,7 @@
35 36 }
36 37 );
37 38
38   - const emit = defineEmits(['edit', 'runTask']);
  39 + const emit = defineEmits(['edit', 'runTask', 'detail']);
39 40
40 41 const loading = ref(false);
41 42
... ... @@ -131,6 +132,12 @@
131 132 :trigger="['hover']"
132 133 :drop-menu-list="[
133 134 {
  135 + text: '详情',
  136 + event: DropMenuEvent.DETAIL,
  137 + icon: 'ant-design:eye-outlined',
  138 + onClick: emit.bind(null, 'detail', getRecord),
  139 + },
  140 + {
134 141 text: '编辑',
135 142 event: DropMenuEvent.DELETE,
136 143 disabled: !!getRecord.state,
... ... @@ -190,12 +197,14 @@
190 197 </div>
191 198 </div>
192 199 <div class="mt-4 flex justify-between items-center gap-3">
193   - <Button size="small" @click="handleRunTask">
194   - <div class="text-xs px-1">
195   - <PlayCircleOutlined class="mr-1" />
196   - <span>运行任务</span>
197   - </div>
198   - </Button>
  200 + <Authority :value="PermissionEnum.EXECUTE">
  201 + <Button size="small" @click="handleRunTask">
  202 + <div class="text-xs px-1">
  203 + <PlayCircleOutlined class="mr-1" />
  204 + <span>运行任务</span>
  205 + </div>
  206 + </Button>
  207 + </Authority>
199 208 <Tooltip
200 209 v-if="getLastExecuteTime"
201 210 overlay-class-name="task-last-execute-time-tooltip"
... ...
... ... @@ -6,6 +6,7 @@ export enum PermissionEnum {
6 6 START_TASK = 'api:yt:task_center:update:state',
7 7 DELETE = 'api:yt:task_center:delete',
8 8 ALLOW = 'api:yt:task_center:cancel:allow',
  9 + EXECUTE = 'api:yt:task_center:immediate:execute',
9 10 }
10 11
11 12 export enum FormFieldsEnum {
... ... @@ -17,13 +18,11 @@ export enum FormFieldsEnum {
17 18 export enum TaskTargetEnum {
18 19 DEVICES = 'DEVICES',
19 20 PRODUCTS = 'PRODUCTS',
20   - ALL = 'all',
21 21 }
22 22
23 23 export enum TaskTargetNameEnum {
24 24 DEVICES = '设备',
25 25 PRODUCTS = '产品',
26   - ALL = '不限任务目标类型',
27 26 }
28 27
29 28 export enum TaskTypeEnum {
... ... @@ -43,7 +42,7 @@ export enum TaskStatusEnum {
43 42
44 43 export enum TaskStatusNameEnum {
45 44 DEACTIVATE = '已停用',
46   - NORMAL = '正常',
  45 + NORMAL = '已启用',
47 46 }
48 47
49 48 export const formSchemas: FormSchema[] = [
... ...
... ... @@ -17,8 +17,11 @@
17 17 import { Authority } from '/@/components/Authority';
18 18 import { getBoundingClientRect } from '/@/utils/domUtils';
19 19 import { RunTaskModal } from './components/RunTaskModal';
  20 + import { DetailDrawer } from './components/DetailDrawer';
  21 + import { useDrawer } from '/@/components/Drawer';
20 22
21 23 const [registerModal, { openModal }] = useModal();
  24 + const [registerDrawer, { openDrawer }] = useDrawer();
22 25 const [registerRunTaskModal, { openModal: openRunTaskModal }] = useModal();
23 26
24 27 const [registerForm, { getFieldsValue }] = useForm({
... ... @@ -28,7 +31,6 @@
28 31 showAdvancedButton: true,
29 32 labelWidth: 100,
30 33 submitFunc: async () => {
31   - pagination.params = getFieldsValue();
32 34 getDataSource();
33 35 },
34 36 });
... ... @@ -39,7 +41,6 @@
39 41 showQuickJumper: true,
40 42 size: 'small',
41 43 showTotal: (total: number) => `共 ${total} 条数据`,
42   - params: {} as Recordable,
43 44 });
44 45
45 46 const dataSource = ref<TaskRecordType[]>([]);
... ... @@ -47,7 +48,8 @@
47 48 const getDataSource = async () => {
48 49 try {
49 50 loading.value = true;
50   - const { items } = await getTaskCenterList({ page: 1, pageSize: 10, ...pagination.params });
  51 + const params = getFieldsValue();
  52 + const { items } = await getTaskCenterList({ page: 1, pageSize: 10, ...params });
51 53 dataSource.value = items;
52 54 } catch (error) {
53 55 throw error;
... ... @@ -67,6 +69,10 @@
67 69 openRunTaskModal(true, record);
68 70 };
69 71
  72 + const handleDetail = (record: TaskRecordType) => {
  73 + openDrawer(true, { mode: DataActionModeEnum.READ, record } as ModalParamsType);
  74 + };
  75 +
70 76 const reload = () => getDataSource();
71 77
72 78 const listElRef = ref<Nullable<ComponentElRef>>(null);
... ... @@ -137,11 +143,18 @@
137 143 </template>
138 144 <template #renderItem="{ item }">
139 145 <List.Item :key="item.id">
140   - <TaskCard :record="item" :reload="reload" @runTask="handleRunTask" @edit="handleEdit" />
  146 + <TaskCard
  147 + :record="item"
  148 + :reload="reload"
  149 + @runTask="handleRunTask"
  150 + @detail="handleDetail"
  151 + @edit="handleEdit"
  152 + />
141 153 </List.Item>
142 154 </template>
143 155 </List>
144 156 </section>
  157 + <DetailDrawer @register="registerDrawer" />
145 158 <DetailModal @register="registerModal" :reload="reload" />
146 159 <RunTaskModal :reload="reload" @register="registerRunTaskModal" />
147 160 </PageWrapper>
... ...
... ... @@ -55,10 +55,10 @@ export function useSendCommand() {
55 55 },
56 56 });
57 57 createMessage.success('命令下发成功');
  58 + return true;
58 59 } catch (msg) {
  60 + console.log('enter');
59 61 return error();
60   - } finally {
61   - return true;
62 62 }
63 63 };
64 64 return {
... ...
... ... @@ -60,7 +60,8 @@
60 60 const resetFormFields = async () => {
61 61 const hasExistEl = Object.keys(dataSourceEl).filter((key) => dataSourceEl[key]);
62 62 for (const id of hasExistEl) {
63   - await dataSourceEl[id]?.resetFields();
  63 + const oldValues = dataSourceEl[id]?.getFieldsValue();
  64 + dataSourceEl[id]?.setFieldsValue({ ...oldValues, [DataSourceField.ATTRIBUTE]: null });
64 65 }
65 66 };
66 67
... ... @@ -268,10 +269,8 @@
268 269 watch(
269 270 () => props.frontId,
270 271 async (target, oldTarget) => {
271   - if (isControlComponent(oldTarget!)) return;
272   - if (isControlComponent(target!)) {
273   - await resetFormFields();
274   - }
  272 + if (isControlComponent(oldTarget!) && isControlComponent(target!)) return;
  273 + await resetFormFields();
275 274 }
276 275 );
277 276
... ...
... ... @@ -16,7 +16,7 @@
16 16 list: Recordable[];
17 17 }
18 18 const props = defineProps<{
19   - value: string;
  19 + value?: string;
20 20 }>();
21 21 const emit = defineEmits(['update:value', 'change']);
22 22
... ...
... ... @@ -10,7 +10,6 @@ import { findDictItemByCode } from '/@/api/system/dict';
10 10 import { DeviceTypeEnum } from '/@/api/device/model/deviceModel';
11 11 import { DataTypeEnum } from '/@/components/Form/src/externalCompns/components/StructForm/config';
12 12 import { TransportTypeEnum } from '/@/views/device/profiles/components/TransportDescript/const';
13   -import { nextTick } from 'vue';
14 13
15 14 export enum BasicConfigField {
16 15 NAME = 'name',
... ... @@ -183,7 +182,7 @@ export const dataSourceSchema = (isEdit: boolean, frontId?: FrontComponent): For
183 182 colProps: { span: 8 },
184 183 rules: [{ required: true, message: '组织为必填项' }],
185 184 componentProps({ formActionType }) {
186   - const { setFieldsValue, getFieldsValue } = formActionType;
  185 + const { setFieldsValue } = formActionType;
187 186 return {
188 187 placeholder: '请选择组织',
189 188 api: async () => {
... ... @@ -195,9 +194,6 @@ export const dataSourceSchema = (isEdit: boolean, frontId?: FrontComponent): For
195 194 setFieldsValue({
196 195 [DataSourceField.DEVICE_ID]: null,
197 196 });
198   - nextTick(() => {
199   - console.log('org change', getFieldsValue());
200   - });
201 197 },
202 198 getPopupContainer: () => document.body,
203 199 };
... ...
... ... @@ -76,7 +76,7 @@
76 76 });
77 77
78 78 const getSharePageData = computed(() => {
79   - return props.value;
  79 + return props.value!;
80 80 });
81 81
82 82 const getIsSharePage = computed(() => {
... ... @@ -230,8 +230,9 @@
230 230 const getDataBoradDetail = async () => {
231 231 try {
232 232 return unref(getIsSharePage) ? unref(getSharePageData) : await getBasePageComponentData();
233   - } catch (error) {}
234   - return {} as ComponentInfoDetail;
  233 + } catch (error) {
  234 + return {} as ComponentInfoDetail;
  235 + }
235 236 };
236 237
237 238 const loading = ref(false);
... ...
... ... @@ -22,6 +22,8 @@
22 22 import { useForm, BasicForm } from '/@/components/Form';
23 23 import { formSchema } from './config/searchForm';
24 24 import { ShareModal } from '/@/views/common/ShareModal';
  25 + import { ModalParamsType } from '/#/utils';
  26 + import { DataActionModeEnum } from '/@/enums/toolEnum';
25 27
26 28 const ListItem = List.Item;
27 29 const router = useRouter();
... ... @@ -133,7 +135,11 @@
133 135 };
134 136
135 137 const handleOpenShareModal = (record: DataBoardRecord) => {
136   - openShareModal(true, record);
  138 + openShareModal(true, {
  139 + record,
  140 + mode: DataActionModeEnum.READ,
  141 + href: createShareUrl(record),
  142 + } as ModalParamsType<DataBoardRecord>);
137 143 };
138 144
139 145 const handleMenuEvent = (event: DropMenu, record: DataBoardRecord) => {
... ...
... ... @@ -8,4 +8,5 @@ export type DynamicProps<T> = {
8 8 export interface ModalParamsType<T = Recordable> {
9 9 mode: DataActionModeEnum;
10 10 record: T;
  11 + [key: string]: any;
11 12 }
... ...