Showing
22 changed files
with
342 additions
and
107 deletions
... | ... | @@ -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> | ... | ... |
... | ... | @@ -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 | ... | ... |
... | ... | @@ -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) => { | ... | ... |