Commit 45d95317ee1ee1f32ecb0f297ce2b006d9e03d5c

Authored by ww
1 parent f58124e6

feat: 完成任务中心modbus指令生成&&设备详情任务是否允许任务执行

@@ -14,9 +14,9 @@ VITE_GLOB_PUBLIC_PATH = / @@ -14,9 +14,9 @@ VITE_GLOB_PUBLIC_PATH = /
14 # VITE_PROXY = [["/api","http://101.133.234.90:8080/api"]] 14 # VITE_PROXY = [["/api","http://101.133.234.90:8080/api"]]
15 # 线上测试环境 15 # 线上测试环境
16 # VITE_PROXY = [["/api","http://localhost:8080/api"],["/thingskit-drawio","http://localhost:3000/"]] 16 # VITE_PROXY = [["/api","http://localhost:8080/api"],["/thingskit-drawio","http://localhost:3000/"]]
17 -VITE_PROXY = [["/api","http://222.180.200.114:48080/api"],["/thingskit-drawio","http://localhost:3000/"],["/large-designer", "http://localhost:5555/large-designer/"]] 17 +# VITE_PROXY = [["/api","http://222.180.200.114:48080/api"],["/thingskit-drawio","http://localhost:3000/"],["/large-designer", "http://localhost:5555/large-designer/"]]
18 # VITE_PROXY = [["/api","http://121.37.251.8:8080/api"],["/thingskit-drawio","http://localhost:3000/"]] 18 # VITE_PROXY = [["/api","http://121.37.251.8:8080/api"],["/thingskit-drawio","http://localhost:3000/"]]
19 -# VITE_PROXY = [["/api","http://192.168.10.125:8080/api"],["/thingskit-drawio","http://192.168.10.125:8080/api"]] 19 +VITE_PROXY = [["/api","http://192.168.10.114:8080/api"],["/thingskit-drawio","http://192.168.10.125:8080/api"]]
20 20
21 # 实时数据的ws地址 21 # 实时数据的ws地址
22 # VITE_WEB_SOCKET = ws://localhost:8080/api/ws/plugins/telemetry?token= 22 # VITE_WEB_SOCKET = ws://localhost:8080/api/ws/plugins/telemetry?token=
1 -import { CreateTaskRecordType, GetTaskListParamsType, TaskRecordType } from './model'; 1 +import {
  2 + CreateTaskRecordType,
  3 + GenModbusCommandType,
  4 + GetTaskListParamsType,
  5 + TaskRecordType,
  6 +} from './model';
2 import { PaginationResult } from '/#/axios'; 7 import { PaginationResult } from '/#/axios';
3 import { defHttp } from '/@/utils/http/axios'; 8 import { defHttp } from '/@/utils/http/axios';
4 9
@@ -9,6 +14,8 @@ enum Api { @@ -9,6 +14,8 @@ enum Api {
9 DELETE_TASK = '/task_center', 14 DELETE_TASK = '/task_center',
10 UPDATE_TASK = '/task_center/update', 15 UPDATE_TASK = '/task_center/update',
11 CANCEL_TASK = '/task_center', 16 CANCEL_TASK = '/task_center',
  17 +
  18 + GEN_MODBUS_COMMAND = '/js/modbus',
12 } 19 }
13 20
14 export const getTaskCenterList = (params: GetTaskListParamsType) => { 21 export const getTaskCenterList = (params: GetTaskListParamsType) => {
@@ -45,8 +52,27 @@ export const updateTask = (data: CreateTaskRecordType & Record<'id', string>) => @@ -45,8 +52,27 @@ export const updateTask = (data: CreateTaskRecordType & Record<'id', string>) =>
45 }); 52 });
46 }; 53 };
47 54
48 -export const cancelTask = (data: Record<'id' | 'tbDeviceId', string>) => { 55 +/**
  56 + * @description 取消任务
  57 + * @param data
  58 + * @returns
  59 + */
  60 +export const cancelTask = (
  61 + data: Record<'id' | 'tbDeviceId', string> & Record<'allow', boolean>
  62 +) => {
49 return defHttp.put({ 63 return defHttp.put({
50 - url: `${Api.CANCEL_TASK}/${data.id}/cancel/${data.tbDeviceId}`, 64 + url: `${Api.CANCEL_TASK}/${data.id}/update/${data.tbDeviceId}/${data.allow}`,
  65 + });
  66 +};
  67 +
  68 +/**
  69 + * @description 生成modbus指令
  70 + * @param data
  71 + * @returns {string}
  72 + */
  73 +export const genModbusCommand = (data: GenModbusCommandType) => {
  74 + return defHttp.post<string>({
  75 + url: Api.GEN_MODBUS_COMMAND,
  76 + data,
51 }); 77 });
52 }; 78 };
@@ -3,13 +3,14 @@ import { @@ -3,13 +3,14 @@ import {
3 ExecuteTimeTypeEnum, 3 ExecuteTimeTypeEnum,
4 PeriodTypeEnum, 4 PeriodTypeEnum,
5 PushWayEnum, 5 PushWayEnum,
6 -} from '/@/views/task/center/components/CreateModal/config'; 6 +} from '/@/views/task/center/components/DetailModal/config';
7 import { TaskTypeEnum } from '/@/views/task/center/config'; 7 import { TaskTypeEnum } from '/@/views/task/center/config';
8 8
9 export interface GetTaskListParamsType { 9 export interface GetTaskListParamsType {
10 page: number; 10 page: number;
11 pageSize: number; 11 pageSize: number;
12 state?: string; 12 state?: string;
  13 + tbDeviceId?: string;
13 } 14 }
14 15
15 export interface CreateTaskRecordType { 16 export interface CreateTaskRecordType {
@@ -45,4 +46,19 @@ export interface TaskRecordType extends CreateTaskRecordType { @@ -45,4 +46,19 @@ export interface TaskRecordType extends CreateTaskRecordType {
45 creator: string; 46 creator: string;
46 enabled: boolean; 47 enabled: boolean;
47 state: number; 48 state: number;
  49 + lastExecuteTime?: number;
  50 + tkDeviceTaskCenter?: {
  51 + allowState: number;
  52 + taskCenterId: string;
  53 + tbDeviceId: string;
  54 + };
  55 +}
  56 +
  57 +export interface GenModbusCommandType {
  58 + crc: string;
  59 + deviceCode: string;
  60 + method: string;
  61 + registerAddr: string;
  62 + registerNum?: number;
  63 + registerValues?: number[];
48 } 64 }
@@ -6,7 +6,6 @@ @@ -6,7 +6,6 @@
6 import { onMounted } from 'vue'; 6 import { onMounted } from 'vue';
7 import { computed } from '@vue/reactivity'; 7 import { computed } from '@vue/reactivity';
8 import { onUnmounted } from 'vue'; 8 import { onUnmounted } from 'vue';
9 - import { watch } from 'vue';  
10 9
11 enum EventEnum { 10 enum EventEnum {
12 UPDATE_VALUE = 'update:value', 11 UPDATE_VALUE = 'update:value',
@@ -64,15 +63,15 @@ @@ -64,15 +63,15 @@
64 editoreRef.value = new JSONEditor(unref(jsonEditorElRef), unref(getOptions)); 63 editoreRef.value = new JSONEditor(unref(jsonEditorElRef), unref(getOptions));
65 }; 64 };
66 65
67 - watch(  
68 - () => props.value,  
69 - (target) => {  
70 - unref(editoreRef)?.setText(target || '');  
71 - },  
72 - {  
73 - immediate: true,  
74 - }  
75 - ); 66 + // watch(
  67 + // () => props.value,
  68 + // (target) => {
  69 + // unref(editoreRef)?.setText(target || '');
  70 + // },
  71 + // {
  72 + // immediate: true,
  73 + // }
  74 + // );
76 75
77 const get = (): string => { 76 const get = (): string => {
78 return unref(editoreRef)?.getText() || ''; 77 return unref(editoreRef)?.getText() || '';
@@ -84,6 +83,7 @@ @@ -84,6 +83,7 @@
84 83
85 onMounted(() => { 84 onMounted(() => {
86 initialize(); 85 initialize();
  86 + unref(editoreRef)?.setText(props.value || '');
87 }); 87 });
88 88
89 onUnmounted(() => { 89 onUnmounted(() => {
@@ -11,7 +11,7 @@ @@ -11,7 +11,7 @@
11 import { getBoundingClientRect } from '/@/utils/domUtils'; 11 import { getBoundingClientRect } from '/@/utils/domUtils';
12 import { TaskRecordType } from '/@/api/task/model'; 12 import { TaskRecordType } from '/@/api/task/model';
13 13
14 - defineProps<{ 14 + const props = defineProps<{
15 tbDeviceId: string; 15 tbDeviceId: string;
16 }>(); 16 }>();
17 17
@@ -48,6 +48,7 @@ @@ -48,6 +48,7 @@
48 const { items } = await getTaskCenterList({ 48 const { items } = await getTaskCenterList({
49 page: pagination.current, 49 page: pagination.current,
50 pageSize: pagination.pageSize, 50 pageSize: pagination.pageSize,
  51 + tbDeviceId: props.tbDeviceId,
51 ...pagination.params, 52 ...pagination.params,
52 }); 53 });
53 dataSource.value = items; 54 dataSource.value = items;
@@ -13,6 +13,7 @@ import { getDeviceProfile } from '/@/api/alarm/position'; @@ -13,6 +13,7 @@ import { getDeviceProfile } from '/@/api/alarm/position';
13 import { JSONEditorValidator } from '/@/components/CodeEditor/src/JSONEditor'; 13 import { JSONEditorValidator } from '/@/components/CodeEditor/src/JSONEditor';
14 import { TimeUnitEnum, TimeUnitNameEnum } from '/@/enums/toolEnum'; 14 import { TimeUnitEnum, TimeUnitNameEnum } from '/@/enums/toolEnum';
15 import { createPickerSearch } from '/@/utils/pickerSearch'; 15 import { createPickerSearch } from '/@/utils/pickerSearch';
  16 +import { dateUtil } from '/@/utils/dateUtil';
16 17
17 useComponentRegister('DevicePicker', DevicePicker); 18 useComponentRegister('DevicePicker', DevicePicker);
18 useComponentRegister('PollCommandInput', PollCommandInput); 19 useComponentRegister('PollCommandInput', PollCommandInput);
@@ -64,9 +65,9 @@ export enum ExecuteTimeTypeNameEnum { @@ -64,9 +65,9 @@ export enum ExecuteTimeTypeNameEnum {
64 } 65 }
65 66
66 export enum PeriodTypeEnum { 67 export enum PeriodTypeEnum {
67 - MONTH,  
68 - WEEK,  
69 - DAY, 68 + MONTH = 'MONTH',
  69 + WEEK = 'WEEK',
  70 + DAY = 'DAY',
70 } 71 }
71 72
72 export enum PeriodTypeNameEnum { 73 export enum PeriodTypeNameEnum {
@@ -197,7 +198,7 @@ export const formSchemas: FormSchema[] = [ @@ -197,7 +198,7 @@ export const formSchemas: FormSchema[] = [
197 field: FormFieldsEnum.PUSH_WAY, 198 field: FormFieldsEnum.PUSH_WAY,
198 component: 'RadioGroup', 199 component: 'RadioGroup',
199 label: '推送方式', 200 label: '推送方式',
200 - defaultValue: PushWayEnum.MQTT, 201 + defaultValue: PushWayEnum.TCP,
201 show: false, 202 show: false,
202 componentProps: { 203 componentProps: {
203 options: [ 204 options: [
@@ -317,6 +318,7 @@ export const formSchemas: FormSchema[] = [ @@ -317,6 +318,7 @@ export const formSchemas: FormSchema[] = [
317 componentProps: { 318 componentProps: {
318 getPopupContainer: () => document.body, 319 getPopupContainer: () => document.body,
319 valueFormat: 'HH:mm:ss', 320 valueFormat: 'HH:mm:ss',
  321 + defaultOpenValue: dateUtil('00:00:00', 'HH:mm:ss'),
320 }, 322 },
321 }, 323 },
322 { 324 {
@@ -31,7 +31,6 @@ @@ -31,7 +31,6 @@
31 resetFields(); 31 resetFields();
32 if (record && mode === DataActionModeEnum.UPDATE) { 32 if (record && mode === DataActionModeEnum.UPDATE) {
33 const res = parseData(record); 33 const res = parseData(record);
34 - console.log({ record, res });  
35 setFieldsValue({ ...res }); 34 setFieldsValue({ ...res });
36 } 35 }
37 } 36 }
@@ -6,8 +6,7 @@ import { DeviceCascadePickerValueType } from '../DevicePicker'; @@ -6,8 +6,7 @@ import { DeviceCascadePickerValueType } from '../DevicePicker';
6 import { TaskTargetEnum } from '../../config'; 6 import { TaskTargetEnum } from '../../config';
7 import { TimeUnitEnum } from '/@/enums/toolEnum'; 7 import { TimeUnitEnum } from '/@/enums/toolEnum';
8 8
9 -export interface FormValueType  
10 - extends Partial<Record<Exclude<FormFieldsEnum, FormFieldsEnum.TRANSPORT_TYPE>, any>> { 9 +export interface FormValueType extends Partial<Record<FormFieldsEnum, any>> {
11 [FormFieldsEnum.EXECUTE_TARGET_DATA]: DeviceCascadePickerValueType; 10 [FormFieldsEnum.EXECUTE_TARGET_DATA]: DeviceCascadePickerValueType;
12 } 11 }
13 12
@@ -106,7 +105,7 @@ export const composeData = (result: Required<FormValueType>): CreateTaskRecordTy @@ -106,7 +105,7 @@ export const composeData = (result: Required<FormValueType>): CreateTaskRecordTy
106 targetType, 105 targetType,
107 executeContent: { 106 executeContent: {
108 pushContent: { 107 pushContent: {
109 - rpcCommand: pushWay === PushWayEnum.MQTT ? JSON.parse(rpcCommand) : pushWay, 108 + rpcCommand: pushWay === PushWayEnum.MQTT ? JSON.parse(rpcCommand) : rpcCommand,
110 }, 109 },
111 pushWay, 110 pushWay,
112 type: executeContentType, 111 type: executeContentType,
@@ -138,7 +137,8 @@ export const parseData = (result: TaskRecordType): Required<FormValueType> => { @@ -138,7 +137,8 @@ export const parseData = (result: TaskRecordType): Required<FormValueType> => {
138 return { 137 return {
139 name, 138 name,
140 targetType, 139 targetType,
141 - rpcCommand: pushWay === PushWayEnum.MQTT ? JSON.stringify(rpcCommand) : rpcCommand, 140 + rpcCommand: pushWay === PushWayEnum.MQTT ? JSON.stringify(rpcCommand, null, 2) : rpcCommand,
  141 + transportType: pushWay,
142 pushWay, 142 pushWay,
143 executeContentType, 143 executeContentType,
144 executeTargetData: { 144 executeTargetData: {
src/views/task/center/components/PollCommandInput/GenModbusCommandModal.vue renamed from src/views/task/center/components/PollCommandInput/CreateTCPCommandModal.vue
1 <script lang="ts" setup> 1 <script lang="ts" setup>
2 - import { Button } from 'ant-design-vue'; 2 + import { Button, Spin } from 'ant-design-vue';
3 import { BasicForm, useForm } from '/@/components/Form'; 3 import { BasicForm, useForm } from '/@/components/Form';
4 import { BasicModal, useModal } from '/@/components/Modal'; 4 import { BasicModal, useModal } from '/@/components/Modal';
5 import { formSchemas } from './config'; 5 import { formSchemas } from './config';
6 import { ref } from 'vue'; 6 import { ref } from 'vue';
7 import { unref } from 'vue'; 7 import { unref } from 'vue';
  8 + import { composeModbusModalData } from './util';
  9 + import { ModbusCommandValueType } from './type';
  10 + import { genModbusCommand } from '/@/api/task';
8 11
9 const emit = defineEmits(['update:value']); 12 const emit = defineEmits(['update:value']);
10 13
@@ -19,9 +22,29 @@ @@ -19,9 +22,29 @@
19 22
20 const commandValue = ref(''); 23 const commandValue = ref('');
21 24
  25 + const loading = ref(false);
22 const handleGetValue = async () => { 26 const handleGetValue = async () => {
23 - const value = getFieldsValue();  
24 - console.log(value); 27 + try {
  28 + const value = getFieldsValue();
  29 + const record = composeModbusModalData(value as ModbusCommandValueType);
  30 + loading.value = true;
  31 + const result = await genModbusCommand(record);
  32 + console.log(result);
  33 + commandValue.value = result;
  34 + } catch (error) {
  35 + } finally {
  36 + loading.value = false;
  37 + }
  38 + };
  39 +
  40 + const formatCommand = (command: string, subNumber: number) => {
  41 + const list = command.split('');
  42 + let index = 0;
  43 + const arr: string[][] = [];
  44 + while (index <= list.length) {
  45 + arr.push(list.slice(index, (index += subNumber)));
  46 + }
  47 + return arr.reduce((prev, next) => `${prev} ${next.join('')}`, '');
25 }; 48 };
26 49
27 const handleOk = () => { 50 const handleOk = () => {
@@ -30,13 +53,22 @@ @@ -30,13 +53,22 @@
30 </script> 53 </script>
31 54
32 <template> 55 <template>
33 - <BasicModal @register="registerModal" title="配置操作" @ok="handleOk"> 56 + <BasicModal
  57 + @register="registerModal"
  58 + :okButtonProps="{ loading }"
  59 + title="配置操作"
  60 + @ok="handleOk"
  61 + >
34 <BasicForm @register="registerForm" class="create-tcp-command-form" /> 62 <BasicForm @register="registerForm" class="create-tcp-command-form" />
35 <section> 63 <section>
36 <Button @click="handleGetValue" type="link" class="!px-0">生成预览</Button> 64 <Button @click="handleGetValue" type="link" class="!px-0">生成预览</Button>
37 - <div v-if="commandValue"> 65 + <div>
38 <div class="text-gray-400">Modbus 指令预览</div> 66 <div class="text-gray-400">Modbus 指令预览</div>
39 - <code class="bg-dark-50 text-light-50 p-1 w-full block mt-1">{{ commandValue }}</code> 67 + <Spin :spinning="loading" size="small">
  68 + <div class="bg-dark-50 text-light-50 p-1 w-full block mt-1 min-h-8">{{
  69 + formatCommand(commandValue, 2)
  70 + }}</div>
  71 + </Spin>
40 </div> 72 </div>
41 </section> 73 </section>
42 </BasicModal> 74 </BasicModal>
1 <script lang="ts" setup> 1 <script lang="ts" setup>
2 import { InputGroup, InputNumber, Select, Input } from 'ant-design-vue'; 2 import { InputGroup, InputNumber, Select, Input } from 'ant-design-vue';
3 - import { watch } from 'vue';  
4 import { unref } from 'vue'; 3 import { unref } from 'vue';
5 import { computed } from 'vue'; 4 import { computed } from 'vue';
6 import { ref } from 'vue'; 5 import { ref } from 'vue';
@@ -14,13 +13,13 @@ @@ -14,13 +13,13 @@
14 13
15 const DEC_MAX_VALUE = parseInt('0xffff', 16); 14 const DEC_MAX_VALUE = parseInt('0xffff', 16);
16 15
17 - const props = withDefaults( 16 + withDefaults(
18 defineProps<{ 17 defineProps<{
19 - value?: string; 18 + value?: number | string;
20 inputProps?: Recordable; 19 inputProps?: Recordable;
21 }>(), 20 }>(),
22 { 21 {
23 - value: '0', 22 + value: 0,
24 inputProps: () => ({}), 23 inputProps: () => ({}),
25 } 24 }
26 ); 25 );
@@ -44,23 +43,24 @@ @@ -44,23 +43,24 @@
44 return (inputValue.value as number) > DEC_MAX_VALUE ? '0x0000' : formatValue; 43 return (inputValue.value as number) > DEC_MAX_VALUE ? '0x0000' : formatValue;
45 }); 44 });
46 45
  46 + const toDEC = (value: number | string) => {
  47 + return unref(type) === AddressTypeEnum.DEC
  48 + ? isNaN(value as number)
  49 + ? 0
  50 + : Number(value)
  51 + : parseInt(value, 16);
  52 + };
  53 +
47 const handleEmit = () => { 54 const handleEmit = () => {
48 - const syncValue = unref(type) === AddressTypeEnum.DEC ? unref(getDecValue) : unref(getHexValue); 55 + const syncValue = toDEC(unref(inputValue));
49 emit('update:value', syncValue); 56 emit('update:value', syncValue);
50 }; 57 };
51 58
52 const handleChange = (value: AddressTypeEnum) => { 59 const handleChange = (value: AddressTypeEnum) => {
53 const syncValue = value === AddressTypeEnum.DEC ? unref(getHexValue) : unref(getDecValue); 60 const syncValue = value === AddressTypeEnum.DEC ? unref(getHexValue) : unref(getDecValue);
54 inputValue.value = syncValue; 61 inputValue.value = syncValue;
55 - emit('update:value', syncValue); 62 + emit('update:value', toDEC(syncValue));
56 }; 63 };
57 -  
58 - watch(  
59 - () => props.value,  
60 - (targetValue) => {  
61 - inputValue.value = targetValue || 0;  
62 - }  
63 - );  
64 </script> 64 </script>
65 65
66 <template> 66 <template>
@@ -76,10 +76,10 @@ @@ -76,10 +76,10 @@
76 v-model:value="inputValue" 76 v-model:value="inputValue"
77 :step="1" 77 :step="1"
78 class="flex-1" 78 class="flex-1"
79 - @change="handleEmit"  
80 v-bind="inputProps" 79 v-bind="inputProps"
  80 + @change="handleEmit"
81 /> 81 />
82 - <Input v-if="type === AddressTypeEnum.HEX" v-model:value="inputValue" /> 82 + <Input v-if="type === AddressTypeEnum.HEX" v-model:value="inputValue" @change="handleEmit" />
83 <div class="text-center h-8 leading-8 px-2 bg-gray-200 cursor-pointer rounded-1 w-20"> 83 <div class="text-center h-8 leading-8 px-2 bg-gray-200 cursor-pointer rounded-1 w-20">
84 <div v-if="type === AddressTypeEnum.DEC">{{ getDecValue }}</div> 84 <div v-if="type === AddressTypeEnum.DEC">{{ getDecValue }}</div>
85 <div v-if="type === AddressTypeEnum.HEX">{{ getHexValue }}</div> 85 <div v-if="type === AddressTypeEnum.HEX">{{ getHexValue }}</div>
@@ -6,10 +6,14 @@ import { createPickerSearch } from '/@/utils/pickerSearch'; @@ -6,10 +6,14 @@ import { createPickerSearch } from '/@/utils/pickerSearch';
6 import { ControlGroup } from '../ControlGroup'; 6 import { ControlGroup } from '../ControlGroup';
7 7
8 export enum FormFieldsEnum { 8 export enum FormFieldsEnum {
9 - ADDRESS = 'address',  
10 - FUNCTION_CODE = 'functionCode',  
11 - START_REGISTER_ADDRESS = 'startRegisterAddress',  
12 - DATA_VALID = 'dataValid', 9 + // 设备地址码
  10 + DEVICE_CODE = 'deviceCode',
  11 + // 功能码
  12 + METHOD = 'method',
  13 + // 寄存器地址
  14 + REGISTER_ADDR = 'registerAddr',
  15 + // 数据校验算法
  16 + CRC = 'crc',
13 // 线圈个数 17 // 线圈个数
14 COIL_NUMBER = 'coilNumber', 18 COIL_NUMBER = 'coilNumber',
15 // 寄存器个数 19 // 寄存器个数
@@ -27,7 +31,7 @@ export enum FormFieldsEnum { @@ -27,7 +31,7 @@ export enum FormFieldsEnum {
27 useComponentRegister('RegisterAddressInput', RegisterAddressInput); 31 useComponentRegister('RegisterAddressInput', RegisterAddressInput);
28 useComponentRegister('ControlGroup', ControlGroup); 32 useComponentRegister('ControlGroup', ControlGroup);
29 33
30 -enum FunctionCodeEnum { 34 +export enum FunctionCodeEnum {
31 // 读取线圈状态01 35 // 读取线圈状态01
32 READ_COIL_STATE_01 = '01', 36 READ_COIL_STATE_01 = '01',
33 // 读取输入状态02 37 // 读取输入状态02
@@ -46,35 +50,45 @@ enum FunctionCodeEnum { @@ -46,35 +50,45 @@ enum FunctionCodeEnum {
46 WRITE_MULTIPLE_KEEP_REGISTER_16 = '16', 50 WRITE_MULTIPLE_KEEP_REGISTER_16 = '16',
47 } 51 }
48 52
49 -const showCoilNumber = (value: FunctionCodeEnum) => 53 +export enum CRCValidTypeEnum {
  54 + CRC_32_HIGH = 'CRC_32_HIGH',
  55 + CRC_32_LOWER = 'CRC_32_LOWER',
  56 + AND_TOTAL_XOR = 'AND_TOTAL_XOR',
  57 + TOTAL_AND_SUM = 'TOTAL_AND_SUM',
  58 + CRC_8 = 'CRC_8',
  59 + CRC_16_HIGH = 'CRC_16_HIGH',
  60 + CRC_16_LOWER = 'CRC_16_LOWER',
  61 +}
  62 +
  63 +export const showCoilNumber = (value: FunctionCodeEnum) =>
50 [ 64 [
51 FunctionCodeEnum.READ_COIL_STATE_01, 65 FunctionCodeEnum.READ_COIL_STATE_01,
52 FunctionCodeEnum.READ_INPUT_STATE_02, 66 FunctionCodeEnum.READ_INPUT_STATE_02,
53 FunctionCodeEnum.WRITE_MULTIPLE_COIL_STATE_15, 67 FunctionCodeEnum.WRITE_MULTIPLE_COIL_STATE_15,
54 ].includes(value); 68 ].includes(value);
55 69
56 -const showRegisterNumber = (value: FunctionCodeEnum) => 70 +export const showRegisterNumber = (value: FunctionCodeEnum) =>
57 [ 71 [
58 FunctionCodeEnum.READ_KEEP_REGISTER_03, 72 FunctionCodeEnum.READ_KEEP_REGISTER_03,
59 FunctionCodeEnum.READ_INPUT_REGISTER_04, 73 FunctionCodeEnum.READ_INPUT_REGISTER_04,
60 FunctionCodeEnum.WRITE_MULTIPLE_KEEP_REGISTER_16, 74 FunctionCodeEnum.WRITE_MULTIPLE_KEEP_REGISTER_16,
61 ].includes(value); 75 ].includes(value);
62 76
63 -const showCoilValue = (value: FunctionCodeEnum) => 77 +export const showCoilValue = (value: FunctionCodeEnum) =>
64 [FunctionCodeEnum.WRITE_SINGLE_COIL_REGISTER_05].includes(value); 78 [FunctionCodeEnum.WRITE_SINGLE_COIL_REGISTER_05].includes(value);
65 79
66 -const showRegisterValue = (value: FunctionCodeEnum) => 80 +export const showRegisterValue = (value: FunctionCodeEnum) =>
67 [FunctionCodeEnum.WRITE_SINGLE_KEEP_COIL_REGISTER_06].includes(value); 81 [FunctionCodeEnum.WRITE_SINGLE_KEEP_COIL_REGISTER_06].includes(value);
68 82
69 -const isWriteCoilGroup = (value: FunctionCodeEnum) => 83 +export const isWriteCoilGroup = (value: FunctionCodeEnum) =>
70 [FunctionCodeEnum.WRITE_MULTIPLE_COIL_STATE_15].includes(value); 84 [FunctionCodeEnum.WRITE_MULTIPLE_COIL_STATE_15].includes(value);
71 85
72 -const isWriteRegisterGroup = (value: FunctionCodeEnum) => 86 +export const isWriteRegisterGroup = (value: FunctionCodeEnum) =>
73 [FunctionCodeEnum.WRITE_MULTIPLE_KEEP_REGISTER_16].includes(value); 87 [FunctionCodeEnum.WRITE_MULTIPLE_KEEP_REGISTER_16].includes(value);
74 88
75 export const formSchemas: FormSchema[] = [ 89 export const formSchemas: FormSchema[] = [
76 { 90 {
77 - field: FormFieldsEnum.ADDRESS, 91 + field: FormFieldsEnum.DEVICE_CODE,
78 component: 'ApiSelect', 92 component: 'ApiSelect',
79 label: '从机地址', 93 label: '从机地址',
80 rules: [{ required: true, message: '请选择从机地址' }], 94 rules: [{ required: true, message: '请选择从机地址' }],
@@ -103,7 +117,7 @@ export const formSchemas: FormSchema[] = [ @@ -103,7 +117,7 @@ export const formSchemas: FormSchema[] = [
103 }, 117 },
104 }, 118 },
105 { 119 {
106 - field: FormFieldsEnum.FUNCTION_CODE, 120 + field: FormFieldsEnum.METHOD,
107 component: 'ApiSelect', 121 component: 'ApiSelect',
108 label: '功能码', 122 label: '功能码',
109 defaultValue: '01', 123 defaultValue: '01',
@@ -121,18 +135,18 @@ export const formSchemas: FormSchema[] = [ @@ -121,18 +135,18 @@ export const formSchemas: FormSchema[] = [
121 }, 135 },
122 }, 136 },
123 { 137 {
124 - field: FormFieldsEnum.START_REGISTER_ADDRESS, 138 + field: FormFieldsEnum.REGISTER_ADDR,
125 label: '起始寄存器地址', 139 label: '起始寄存器地址',
126 component: 'RegisterAddressInput', 140 component: 'RegisterAddressInput',
127 valueField: 'value', 141 valueField: 'value',
128 changeEvent: 'update:value', 142 changeEvent: 'update:value',
129 - defaultValue: '0', 143 + defaultValue: 0,
130 }, 144 },
131 { 145 {
132 field: FormFieldsEnum.REGISTER_NUMBER, 146 field: FormFieldsEnum.REGISTER_NUMBER,
133 label: '寄存器个数', 147 label: '寄存器个数',
134 component: 'InputNumber', 148 component: 'InputNumber',
135 - ifShow: ({ model }) => showRegisterNumber(model[FormFieldsEnum.FUNCTION_CODE]), 149 + ifShow: ({ model }) => showRegisterNumber(model[FormFieldsEnum.METHOD]),
136 defaultValue: 1, 150 defaultValue: 1,
137 rules: [{ required: true, message: '请输入寄存器个数' }], 151 rules: [{ required: true, message: '请输入寄存器个数' }],
138 componentProps: { 152 componentProps: {
@@ -146,7 +160,7 @@ export const formSchemas: FormSchema[] = [ @@ -146,7 +160,7 @@ export const formSchemas: FormSchema[] = [
146 field: FormFieldsEnum.COIL_NUMBER, 160 field: FormFieldsEnum.COIL_NUMBER,
147 label: '线圈个数', 161 label: '线圈个数',
148 component: 'InputNumber', 162 component: 'InputNumber',
149 - ifShow: ({ model }) => showCoilNumber(model[FormFieldsEnum.FUNCTION_CODE]), 163 + ifShow: ({ model }) => showCoilNumber(model[FormFieldsEnum.METHOD]),
150 defaultValue: 1, 164 defaultValue: 1,
151 rules: [{ required: true, message: '请输入线圈个数' }], 165 rules: [{ required: true, message: '请输入线圈个数' }],
152 componentProps: { 166 componentProps: {
@@ -162,7 +176,7 @@ export const formSchemas: FormSchema[] = [ @@ -162,7 +176,7 @@ export const formSchemas: FormSchema[] = [
162 component: 'RegisterAddressInput', 176 component: 'RegisterAddressInput',
163 valueField: 'value', 177 valueField: 'value',
164 changeEvent: 'update:value', 178 changeEvent: 'update:value',
165 - ifShow: ({ model }) => showCoilValue(model[FormFieldsEnum.FUNCTION_CODE]), 179 + ifShow: ({ model }) => showCoilValue(model[FormFieldsEnum.METHOD]),
166 defaultValue: '0', 180 defaultValue: '0',
167 rules: [{ required: true, message: '请输入线圈值' }], 181 rules: [{ required: true, message: '请输入线圈值' }],
168 componentProps: { 182 componentProps: {
@@ -175,7 +189,7 @@ export const formSchemas: FormSchema[] = [ @@ -175,7 +189,7 @@ export const formSchemas: FormSchema[] = [
175 component: 'RegisterAddressInput', 189 component: 'RegisterAddressInput',
176 valueField: 'value', 190 valueField: 'value',
177 changeEvent: 'update:value', 191 changeEvent: 'update:value',
178 - ifShow: ({ model }) => showRegisterValue(model[FormFieldsEnum.FUNCTION_CODE]), 192 + ifShow: ({ model }) => showRegisterValue(model[FormFieldsEnum.METHOD]),
179 defaultValue: '0', 193 defaultValue: '0',
180 rules: [{ required: true, message: '请输入寄存器值' }], 194 rules: [{ required: true, message: '请输入寄存器值' }],
181 componentProps: { 195 componentProps: {
@@ -189,7 +203,7 @@ export const formSchemas: FormSchema[] = [ @@ -189,7 +203,7 @@ export const formSchemas: FormSchema[] = [
189 colProps: { span: 24 }, 203 colProps: { span: 24 },
190 valueField: 'value', 204 valueField: 'value',
191 changeEvent: 'update:value', 205 changeEvent: 'update:value',
192 - ifShow: ({ model }) => isWriteRegisterGroup(model[FormFieldsEnum.FUNCTION_CODE]), 206 + ifShow: ({ model }) => isWriteRegisterGroup(model[FormFieldsEnum.METHOD]),
193 componentProps: ({ formModel }) => { 207 componentProps: ({ formModel }) => {
194 const length = formModel[FormFieldsEnum.REGISTER_NUMBER]; 208 const length = formModel[FormFieldsEnum.REGISTER_NUMBER];
195 return { 209 return {
@@ -213,7 +227,7 @@ export const formSchemas: FormSchema[] = [ @@ -213,7 +227,7 @@ export const formSchemas: FormSchema[] = [
213 colProps: { span: 24 }, 227 colProps: { span: 24 },
214 valueField: 'value', 228 valueField: 'value',
215 changeEvent: 'update:value', 229 changeEvent: 'update:value',
216 - ifShow: ({ model }) => isWriteCoilGroup(model[FormFieldsEnum.FUNCTION_CODE]), 230 + ifShow: ({ model }) => isWriteCoilGroup(model[FormFieldsEnum.METHOD]),
217 componentProps: ({ formModel }) => { 231 componentProps: ({ formModel }) => {
218 const length = formModel[FormFieldsEnum.COIL_NUMBER]; 232 const length = formModel[FormFieldsEnum.COIL_NUMBER];
219 return { 233 return {
@@ -235,11 +249,12 @@ export const formSchemas: FormSchema[] = [ @@ -235,11 +249,12 @@ export const formSchemas: FormSchema[] = [
235 }, 249 },
236 }, 250 },
237 { 251 {
238 - field: FormFieldsEnum.DATA_VALID, 252 + field: FormFieldsEnum.CRC,
239 label: '数据校验', 253 label: '数据校验',
240 component: 'ApiSelect', 254 component: 'ApiSelect',
241 colProps: { span: 24 }, 255 colProps: { span: 24 },
242 rules: [{ required: true, message: '请选择数据校验方式' }], 256 rules: [{ required: true, message: '请选择数据校验方式' }],
  257 + defaultValue: CRCValidTypeEnum.CRC_16_LOWER,
243 componentProps: () => { 258 componentProps: () => {
244 return { 259 return {
245 api: findDictItemByCode, 260 api: findDictItemByCode,
@@ -4,11 +4,10 @@ @@ -4,11 +4,10 @@
4 import { SettingOutlined } from '@ant-design/icons-vue'; 4 import { SettingOutlined } from '@ant-design/icons-vue';
5 import { ModeEnum } from './index'; 5 import { ModeEnum } from './index';
6 import { computed } from '@vue/reactivity'; 6 import { computed } from '@vue/reactivity';
7 - import CreateTCPCommandModal from './CreateTCPCommandModal.vue'; 7 + import GenModbusCommandModal from './GenModbusCommandModal.vue';
8 import { useModal } from '/@/components/Modal'; 8 import { useModal } from '/@/components/Modal';
9 import { ref } from 'vue'; 9 import { ref } from 'vue';
10 import { unref } from 'vue'; 10 import { unref } from 'vue';
11 - import JSONEditorType from 'jsoneditor';  
12 11
13 const props = withDefaults( 12 const props = withDefaults(
14 defineProps<{ 13 defineProps<{
@@ -54,8 +53,7 @@ @@ -54,8 +53,7 @@
54 } 53 }
55 }; 54 };
56 55
57 - const handleEditorBlur = (_event: Event, instance: JSONEditorType) => {  
58 - const value = instance.getText(); 56 + const handleUpdateEditorValue = (value: string) => {
59 handleEmit(value); 57 handleEmit(value);
60 }; 58 };
61 </script> 59 </script>
@@ -74,7 +72,11 @@ @@ -74,7 +72,11 @@
74 <SettingOutlined class="cursor-pointer" @click="handleClick" /> 72 <SettingOutlined class="cursor-pointer" @click="handleClick" />
75 </template> 73 </template>
76 </Input> 74 </Input>
77 - <JSONEditor v-if="mode === ModeEnum.JSON" :value="getJSONValue" @blur="handleEditorBlur" />  
78 - <CreateTCPCommandModal @register="registerCreateTCPCommandModal" @update:value="handleEmit" /> 75 + <JSONEditor
  76 + v-if="mode === ModeEnum.JSON"
  77 + :value="getJSONValue"
  78 + @update:value="handleUpdateEditorValue"
  79 + />
  80 + <GenModbusCommandModal @register="registerCreateTCPCommandModal" @update:value="handleEmit" />
79 </section> 81 </section>
80 </template> 82 </template>
  1 +import { FormFieldsEnum } from './config';
  2 +
  3 +type ValuesItem = { value: any };
  4 +
  5 +export interface ModbusCommandValueType extends Record<FormFieldsEnum, any> {
  6 + [FormFieldsEnum.REGISTER_VALUES]: ValuesItem[];
  7 + [FormFieldsEnum.COIL_VALUES]: ValuesItem[];
  8 +}
  1 +import { FunctionCodeEnum } from './config';
  2 +import { ModbusCommandValueType } from './type';
  3 +import { GenModbusCommandType } from '/@/api/task/model';
  4 +
  5 +const registerInfo = (record: ModbusCommandValueType): Partial<GenModbusCommandType> => {
  6 + const {
  7 + method,
  8 + registerNumber,
  9 + registerValue,
  10 + registerValues,
  11 + coilNumber,
  12 + coilValue,
  13 + coilValues,
  14 + } = record;
  15 + switch (method) {
  16 + case FunctionCodeEnum.READ_COIL_STATE_01 || FunctionCodeEnum.READ_INPUT_STATE_02:
  17 + return { registerNum: coilNumber };
  18 + case FunctionCodeEnum.READ_KEEP_REGISTER_03 || FunctionCodeEnum.READ_INPUT_REGISTER_04:
  19 + return { registerNum: registerNumber };
  20 + case FunctionCodeEnum.WRITE_SINGLE_COIL_REGISTER_05:
  21 + return { registerValues: [coilValue] };
  22 + case FunctionCodeEnum.WRITE_SINGLE_KEEP_COIL_REGISTER_06:
  23 + return { registerValues: [registerValue] };
  24 + case FunctionCodeEnum.WRITE_MULTIPLE_COIL_STATE_15:
  25 + return {
  26 + registerNum: coilNumber,
  27 + registerValues: coilValues.map((item) => (item.value ? 1 : 0)),
  28 + };
  29 + case FunctionCodeEnum.WRITE_MULTIPLE_KEEP_REGISTER_16:
  30 + return {
  31 + registerNum: registerNumber,
  32 + registerValues: registerValues.map((item) => item.value),
  33 + };
  34 + default:
  35 + return {};
  36 + }
  37 +};
  38 +
  39 +export const composeModbusModalData = (record: ModbusCommandValueType): GenModbusCommandType => {
  40 + const { crc, deviceCode, method, registerAddr } = record;
  41 + return {
  42 + crc,
  43 + deviceCode,
  44 + method,
  45 + registerAddr,
  46 + ...registerInfo(record),
  47 + };
  48 +};
@@ -14,6 +14,8 @@ @@ -14,6 +14,8 @@
14 import { computed } from '@vue/reactivity'; 14 import { computed } from '@vue/reactivity';
15 import { unref } from 'vue'; 15 import { unref } from 'vue';
16 import { ref } from 'vue'; 16 import { ref } from 'vue';
  17 + import { dateUtil } from '/@/utils/dateUtil';
  18 + import { DEFAULT_DATE_FORMAT } from '/@/views/visual/board/detail/config/util';
17 19
18 enum DropMenuEvent { 20 enum DropMenuEvent {
19 DELETE = 'DELETE', 21 DELETE = 'DELETE',
@@ -43,9 +45,9 @@ @@ -43,9 +45,9 @@
43 }); 45 });
44 46
45 const getCancelState = computed(() => { 47 const getCancelState = computed(() => {
46 - const { executeTarget } = unref(getRecord);  
47 - const { cancelExecuteDevices } = executeTarget;  
48 - return !!(cancelExecuteDevices && cancelExecuteDevices.length); 48 + return !!(
  49 + unref(getRecord).tkDeviceTaskCenter && unref(getRecord).tkDeviceTaskCenter?.allowState
  50 + );
49 }); 51 });
50 52
51 const handleDelete = async () => { 53 const handleDelete = async () => {
@@ -76,7 +78,11 @@ @@ -76,7 +78,11 @@
76 try { 78 try {
77 if (!props.tbDeviceId) return; 79 if (!props.tbDeviceId) return;
78 loading.value = true; 80 loading.value = true;
79 - await cancelTask({ id: unref(getRecord).id, tbDeviceId: props.tbDeviceId }); 81 + await cancelTask({
  82 + id: unref(getRecord).id,
  83 + tbDeviceId: props.tbDeviceId,
  84 + allow: !unref(getCancelState),
  85 + });
80 createMessage.success('设置成功'); 86 createMessage.success('设置成功');
81 props.reload?.(); 87 props.reload?.();
82 } catch (error) { 88 } catch (error) {
@@ -85,6 +91,31 @@ @@ -85,6 +91,31 @@
85 loading.value = false; 91 loading.value = false;
86 } 92 }
87 }; 93 };
  94 +
  95 + const getTwoDateDiff = (date: number, now = dateUtil()) => {
  96 + const unitList = [
  97 + { radix: null, unitName: '年', unit: 'year' },
  98 + { radix: 11, unitName: '月', unit: 'month' },
  99 + { radix: 30, unitName: '日', unit: 'day' },
  100 + { radix: 23, unitName: '时', unit: 'hour' },
  101 + { radix: 59, unitName: '分', unit: 'minute' },
  102 + { radix: 59, unitName: '秒', unit: 'second' },
  103 + ];
  104 +
  105 + for (let i = unitList.length - 1; i >= 0; i--) {
  106 + const { unit, radix, unitName } = unitList[i];
  107 + const lastDate = dateUtil(date);
  108 + const diffValue = now.diff(lastDate, unit as any);
  109 + if (!radix || diffValue <= radix) {
  110 + return { value: diffValue, unit, unitName };
  111 + }
  112 + }
  113 + };
  114 +
  115 + const getLastExecuteTime = computed(() => {
  116 + if (!unref(getRecord).lastExecuteTime) return;
  117 + return getTwoDateDiff(unref(getRecord).lastExecuteTime!);
  118 + });
88 </script> 119 </script>
89 120
90 <template> 121 <template>
@@ -155,10 +186,15 @@ @@ -155,10 +186,15 @@
155 <span>运行任务</span> 186 <span>运行任务</span>
156 </div> 187 </div>
157 </Button> 188 </Button>
158 - <Tooltip title="最后运行时间:"> 189 + <Tooltip
  190 + v-if="getLastExecuteTime"
  191 + overlay-class-name="task-last-execute-time-tooltip"
  192 + placement="topLeft"
  193 + :title="`最后运行时间: ${dateUtil(getRecord.lastExecuteTime).format(DEFAULT_DATE_FORMAT)}`"
  194 + >
159 <div class="text-gray-400 text-xs truncate"> 195 <div class="text-gray-400 text-xs truncate">
160 <span class="mr-2">间隔时间重复</span> 196 <span class="mr-2">间隔时间重复</span>
161 - <span>三分钟前</span> 197 + <span>{{ `${getLastExecuteTime.value}${getLastExecuteTime.unitName}前` }}</span>
162 </div> 198 </div>
163 </Tooltip> 199 </Tooltip>
164 </div> 200 </div>
@@ -184,3 +220,9 @@ @@ -184,3 +220,9 @@
184 </section> 220 </section>
185 </Card> 221 </Card>
186 </template> 222 </template>
  223 +
  224 +<style>
  225 + .task-last-execute-time-tooltip {
  226 + max-width: 300px;
  227 + }
  228 +</style>
@@ -52,19 +52,6 @@ export const formSchemas: FormSchema[] = [ @@ -52,19 +52,6 @@ export const formSchemas: FormSchema[] = [
52 getPopupContainer: () => document.body, 52 getPopupContainer: () => document.body,
53 }, 53 },
54 }, 54 },
55 - // {  
56 - // label: '',  
57 - // component: 'Select',  
58 - // field: FormFieldsEnum.TASK_TYPE,  
59 - // defaultValue: TaskTypeEnum.ALL,  
60 - // componentProps: {  
61 - // options: [  
62 - // { label: TaskTypeNameEnum.CUSTOM, value: TaskTypeEnum.CUSTOM },  
63 - // { label: TaskTypeNameEnum.MODBUS_RTU, value: TaskTypeEnum.MODBUS_RTU },  
64 - // ],  
65 - // getPopupContainer: () => document.body,  
66 - // },  
67 - // },  
68 { 55 {
69 label: '状态', 56 label: '状态',
70 component: 'Select', 57 component: 'Select',
@@ -5,7 +5,7 @@ @@ -5,7 +5,7 @@
5 import { formSchemas } from './config'; 5 import { formSchemas } from './config';
6 import { TaskCard } from './components/TaskCard'; 6 import { TaskCard } from './components/TaskCard';
7 import { getTaskCenterList } from '/@/api/task'; 7 import { getTaskCenterList } from '/@/api/task';
8 - import { ref } from 'vue'; 8 + import { ref, unref } from 'vue';
9 import { onMounted } from 'vue'; 9 import { onMounted } from 'vue';
10 import { DetailModal } from './components/DetailModal'; 10 import { DetailModal } from './components/DetailModal';
11 import { useModal } from '/@/components/Modal'; 11 import { useModal } from '/@/components/Modal';
@@ -14,6 +14,8 @@ @@ -14,6 +14,8 @@
14 import { ReloadOutlined } from '@ant-design/icons-vue'; 14 import { ReloadOutlined } from '@ant-design/icons-vue';
15 import { DataActionModeEnum } from '/@/enums/toolEnum'; 15 import { DataActionModeEnum } from '/@/enums/toolEnum';
16 import { ModalParamsType } from '/#/utils'; 16 import { ModalParamsType } from '/#/utils';
  17 + import { Authority } from '/@/components/Authority';
  18 + import { getBoundingClientRect } from '/@/utils/domUtils';
17 19
18 const [registerModal, { openModal }] = useModal(); 20 const [registerModal, { openModal }] = useModal();
19 21
@@ -61,7 +63,28 @@ @@ -61,7 +63,28 @@
61 63
62 const reload = () => getDataSource(); 64 const reload = () => getDataSource();
63 65
  66 + const listElRef = ref<Nullable<ComponentElRef>>(null);
  67 +
  68 + const setListHeight = () => {
  69 + const clientHeight = document.documentElement.clientHeight;
  70 + const rect = getBoundingClientRect(unref(listElRef)!.$el!) as DOMRect;
  71 + // margin-top 24 height 24
  72 + const paginationHeight = 24 + 24 + 8;
  73 + // list pading top 8 maring-top 8 extra slot 56
  74 + const listContainerMarginBottom = 8 + 8 + 56;
  75 + const listContainerHeight =
  76 + clientHeight - rect.top - paginationHeight - listContainerMarginBottom;
  77 + const listContainerEl = (unref(listElRef)!.$el as HTMLElement).querySelector(
  78 + '.ant-spin-container'
  79 + ) as HTMLElement;
  80 + listContainerEl &&
  81 + (listContainerEl.style.height = listContainerHeight + 'px') &&
  82 + (listContainerEl.style.overflowY = 'auto') &&
  83 + (listContainerEl.style.overflowX = 'hidden');
  84 + };
  85 +
64 onMounted(() => { 86 onMounted(() => {
  87 + setListHeight();
65 getDataSource(); 88 getDataSource();
66 }); 89 });
67 </script> 90 </script>
@@ -72,11 +95,13 @@ @@ -72,11 +95,13 @@
72 class="bg-light-50 flex p-4 justify-between items-center x dark:text-gray-300 dark:bg-dark-900" 95 class="bg-light-50 flex p-4 justify-between items-center x dark:text-gray-300 dark:bg-dark-900"
73 > 96 >
74 <div class="text-2xl">任务</div> 97 <div class="text-2xl">任务</div>
75 - <Button  
76 - type="primary"  
77 - @click="openModal(true, { mode: DataActionModeEnum.CREATE } as ModalParamsType)"  
78 - >创建任务</Button  
79 - > 98 + <Authority>
  99 + <Button
  100 + type="primary"
  101 + @click="openModal(true, { mode: DataActionModeEnum.CREATE } as ModalParamsType)"
  102 + >创建任务
  103 + </Button>
  104 + </Authority>
80 </section> 105 </section>
81 <section 106 <section
82 class="form-container bg-light-50 px-4 pt-4 mt-4 x dark:text-gray-300 dark:bg-dark-900" 107 class="form-container bg-light-50 px-4 pt-4 mt-4 x dark:text-gray-300 dark:bg-dark-900"
@@ -85,6 +110,7 @@ @@ -85,6 +110,7 @@
85 </section> 110 </section>
86 <section class="bg-light-50 mt-4 p-4 x dark:text-gray-300 dark:bg-dark-900"> 111 <section class="bg-light-50 mt-4 p-4 x dark:text-gray-300 dark:bg-dark-900">
87 <List 112 <List
  113 + ref="listElRef"
88 :dataSource="dataSource" 114 :dataSource="dataSource"
89 :pagination="pagination" 115 :pagination="pagination"
90 :grid="{ gutter: 16, xs: 1, sm: 1, md: 2, lg: 3, xl: 3, xxl: 4, column: 4 }" 116 :grid="{ gutter: 16, xs: 1, sm: 1, md: 2, lg: 3, xl: 3, xxl: 4, column: 4 }"
@@ -92,7 +118,6 @@ @@ -92,7 +118,6 @@
92 > 118 >
93 <template #header> 119 <template #header>
94 <section class="flex justify-end gap-4"> 120 <section class="flex justify-end gap-4">
95 - <!-- <CardLayoutButton /> -->  
96 <Tooltip title="刷新"> 121 <Tooltip title="刷新">
97 <Button type="primary" @click="getDataSource"> 122 <Button type="primary" @click="getDataSource">
98 <ReloadOutlined :spin="loading" /> 123 <ReloadOutlined :spin="loading" />