Commit 14468e22febab59d123c9154332e06ea59d546b3

Authored by xp.Huang
2 parents 78961514 c4ed03fc

Merge branch 'ft_local_dev' into 'main'

feat:新增脚本管理-转换脚本静态页面

See merge request huang/yun-teng-iot-front!347
@@ -4,6 +4,27 @@ import { Rule } from 'ant-design-vue/lib/form/interface'; @@ -4,6 +4,27 @@ import { Rule } from 'ant-design-vue/lib/form/interface';
4 * 4 *
5 */ 5 */
6 6
  7 +export const validatorLongitude = (_rule: Rule, value: string) => {
  8 + const reg =
  9 + /^(\-|\+)?(((\d|[1-9]\d|1[0-7]\d|0{1,3})\.\d{0,20})|(\d|[1-9]\d|1[0-7]\d|0{1,3})|180\.0{0,20}|180)$/;
  10 + if (!value) {
  11 + return Promise.reject('请输入经度');
  12 + } else if (!reg.test(value)) {
  13 + return Promise.reject('经度整数部分为0-180,小数部分为0到6位!');
  14 + }
  15 + return Promise.resolve();
  16 +};
  17 +export const validatorLatitude = (_rule: Rule, value: string) => {
  18 + const reg = /^(\-|\+)?([0-8]?\d{1}\.\d{0,20}|90\.0{0,20}|[0-8]?\d{1}|90)$/;
  19 + if (value === '') {
  20 + return Promise.reject('请输入维度');
  21 + } else if (!reg.test(value)) {
  22 + return Promise.reject('纬度整数部分为0-90,小数部分为0到6位!');
  23 + }
  24 +
  25 + return Promise.resolve();
  26 +};
  27 +
7 //正整数并且可以是英文 28 //正整数并且可以是英文
8 export const numberAndEngLishRule: Rule[] = [ 29 export const numberAndEngLishRule: Rule[] = [
9 { 30 {
@@ -46,7 +46,7 @@ @@ -46,7 +46,7 @@
46 centered 46 centered
47 > 47 >
48 <div> 48 <div>
49 - <Form :label-col="labelCol" :colon="false"> 49 + <Form :label-col="labelCol" :colon="false" :rules="rules" :model="positionState">
50 <Row :gutter="20" class="mt-4"> 50 <Row :gutter="20" class="mt-4">
51 <Col :span="20"> 51 <Col :span="20">
52 <FormItem label="搜索位置"> 52 <FormItem label="搜索位置">
@@ -64,13 +64,21 @@ @@ -64,13 +64,21 @@
64 </Row> 64 </Row>
65 <Row :gutter="20" class=""> 65 <Row :gutter="20" class="">
66 <Col :span="10"> 66 <Col :span="10">
67 - <FormItem label="经度">  
68 - <Input v-model:value="positionState.longitude" disabled /> 67 + <FormItem label="经度" name="longitude">
  68 + <Input
  69 + @blur="redirectPosition"
  70 + @change="redirectPosition"
  71 + v-model:value="positionState.longitude"
  72 + />
69 </FormItem> 73 </FormItem>
70 </Col> 74 </Col>
71 <Col :span="10"> 75 <Col :span="10">
72 - <FormItem label="纬度">  
73 - <Input v-model:value="positionState.latitude" disabled /> 76 + <FormItem label="纬度" name="latitude">
  77 + <Input
  78 + @blur="redirectPosition"
  79 + @change="redirectPosition"
  80 + v-model:value="positionState.latitude"
  81 + />
74 </FormItem> 82 </FormItem>
75 </Col> 83 </Col>
76 </Row> 84 </Row>
@@ -90,9 +98,11 @@ @@ -90,9 +98,11 @@
90 import { upload } from '/@/api/oss/ossFileUploader'; 98 import { upload } from '/@/api/oss/ossFileUploader';
91 import { FileItem } from '/@/components/Upload/src/typing'; 99 import { FileItem } from '/@/components/Upload/src/typing';
92 import { BAI_DU_MAP_URL } from '/@/utils/fnUtils'; 100 import { BAI_DU_MAP_URL } from '/@/utils/fnUtils';
93 - import { generateSNCode } from '/@/api/device/deviceManager.ts'; 101 + import { generateSNCode } from '/@/api/device/deviceManager';
94 import icon from '/@/assets/images/wz.png'; 102 import icon from '/@/assets/images/wz.png';
95 import { useDebounceFn } from '@vueuse/core'; 103 import { useDebounceFn } from '@vueuse/core';
  104 + import { validatorLongitude, validatorLatitude } from '/@/utils/rules';
  105 +
96 export default defineComponent({ 106 export default defineComponent({
97 components: { 107 components: {
98 BasicForm, 108 BasicForm,
@@ -115,6 +125,16 @@ @@ -115,6 +125,16 @@
115 }, 125 },
116 emits: ['next'], 126 emits: ['next'],
117 setup(props, { emit }) { 127 setup(props, { emit }) {
  128 + const redirectPosition = () => {
  129 + if (positionState.longitude && positionState.latitude) {
  130 + var pt = new BMap.Point(positionState.longitude, positionState.latitude);
  131 + getAddrByPoint(pt);
  132 + }
  133 + };
  134 + const rules: any = {
  135 + longitude: [{ required: true, validator: validatorLongitude, trigger: 'blur' }],
  136 + latitude: [{ required: true, validator: validatorLatitude, trigger: 'blur' }],
  137 + };
118 const devicePic = ref(''); 138 const devicePic = ref('');
119 const loading = ref(false); 139 const loading = ref(false);
120 140
@@ -202,9 +222,9 @@ @@ -202,9 +222,9 @@
202 geco.getLocation(point, function (res) { 222 geco.getLocation(point, function (res) {
203 positionState.marker.setPosition(point); //重新设置标注的地理坐标 223 positionState.marker.setPosition(point); //重新设置标注的地理坐标
204 positionState.map.panTo(point); //将地图的中心点更改为给定的点 224 positionState.map.panTo(point); //将地图的中心点更改为给定的点
205 - positionState.address = res.address; //记录该点的详细地址信息  
206 - positionState.longitude = point.lng; //记录当前坐标点  
207 - positionState.latitude = point.lat; 225 + positionState.address = res?.address; //记录该点的详细地址信息
  226 + positionState.longitude = point?.lng; //记录当前坐标点
  227 + positionState.latitude = point?.lat;
208 }); 228 });
209 } 229 }
210 230
@@ -378,6 +398,8 @@ @@ -378,6 +398,8 @@
378 debounceSearch, 398 debounceSearch,
379 generateSN, 399 generateSN,
380 loading, 400 loading,
  401 + rules,
  402 + redirectPosition,
381 }; 403 };
382 }, 404 },
383 }); 405 });
@@ -387,6 +409,7 @@ @@ -387,6 +409,7 @@
387 width: 450px; 409 width: 450px;
388 margin: 0 auto; 410 margin: 0 auto;
389 } 411 }
  412 +
390 :deep(.ant-radio-group) { 413 :deep(.ant-radio-group) {
391 width: 15vw; 414 width: 15vw;
392 } 415 }
@@ -4,6 +4,95 @@ import { findDictItemByCode } from '/@/api/system/dict'; @@ -4,6 +4,95 @@ import { findDictItemByCode } from '/@/api/system/dict';
4 import { MessageEnum } from '/@/enums/messageEnum'; 4 import { MessageEnum } from '/@/enums/messageEnum';
5 import { numberRule } from '/@/utils/rules'; 5 import { numberRule } from '/@/utils/rules';
6 6
  7 +import { deviceConfigGetRuleChain } from '/@/api/device/deviceConfigApi';
  8 +
  9 +export const step1Schemas: FormSchema[] = [
  10 + {
  11 + field: 'image',
  12 + label: '上传图片',
  13 + component: 'Input',
  14 + slot: 'imageSelect',
  15 + },
  16 + {
  17 + field: 'name',
  18 + label: '配置名称',
  19 + required: true,
  20 + component: 'Input',
  21 + componentProps() {
  22 + return {
  23 + disabled: false,
  24 + ength: 255,
  25 + placeholder: '请输入配置名称',
  26 + };
  27 + },
  28 + },
  29 + {
  30 + field: 'defaultRuleChainId',
  31 + label: '规则链',
  32 + component: 'ApiSelect',
  33 + componentProps: {
  34 + api: async () => {
  35 + const data = await deviceConfigGetRuleChain();
  36 + const returnData = data.map((m) => {
  37 + return {
  38 + getValueField: m.name,
  39 + getKeyField: m.id.id,
  40 + };
  41 + });
  42 + return returnData;
  43 + },
  44 + labelField: 'getValueField',
  45 + valueField: 'getKeyField',
  46 + immediate: true,
  47 + },
  48 + },
  49 + {
  50 + field: 'defaultQueueName',
  51 + label: '处理队列',
  52 + component: 'ApiSelect',
  53 + componentProps: {
  54 + api: findDictItemByCode,
  55 + params: {
  56 + dictCode: 'queen_execute_sequence',
  57 + },
  58 + labelField: 'itemText',
  59 + valueField: 'itemValue',
  60 + resultField: 'items',
  61 + },
  62 + },
  63 +
  64 + {
  65 + label: '描述',
  66 + field: 'description',
  67 + component: 'InputTextArea',
  68 + componentProps: {
  69 + maxLength: 255,
  70 + placeholder: '请输入描述',
  71 + },
  72 + },
  73 +];
  74 +
  75 +export const step2Schemas: FormSchema[] = [
  76 + {
  77 + field: 'transportType',
  78 + component: 'Select',
  79 + label: '接入协议',
  80 + defaultValue: 'DEFAULT',
  81 + componentProps() {
  82 + return {
  83 + options: [
  84 + { label: '默认', value: 'DEFAULT' },
  85 + { label: 'MQTT', value: 'MQTT' },
  86 + { label: 'CoAP', value: 'COAP' },
  87 + { label: 'LWM2M', value: 'LWM2M' },
  88 + { label: 'SNMP', value: 'SNMP' },
  89 + ],
  90 + };
  91 + },
  92 + colProps: { span: 10 },
  93 + },
  94 +];
  95 +
7 export const columns: BasicColumn[] = [ 96 export const columns: BasicColumn[] = [
8 { 97 {
9 title: '配置图片', //图标 98 title: '配置图片', //图标
@@ -34,7 +34,7 @@ @@ -34,7 +34,7 @@
34 <script lang="ts" setup> 34 <script lang="ts" setup>
35 import { ref } from 'vue'; 35 import { ref } from 'vue';
36 import { BasicForm, useForm } from '/@/components/Form'; 36 import { BasicForm, useForm } from '/@/components/Form';
37 - import { step1Schemas } from './data'; 37 + import { step1Schemas } from '../device.profile.data';
38 import { uploadApi } from '/@/api/personal/index'; 38 import { uploadApi } from '/@/api/personal/index';
39 import { Upload } from 'ant-design-vue'; 39 import { Upload } from 'ant-design-vue';
40 import { PlusOutlined, LoadingOutlined } from '@ant-design/icons-vue'; 40 import { PlusOutlined, LoadingOutlined } from '@ant-design/icons-vue';
1 <template> 1 <template>
2 - <div class="step2-style"> 2 + <div
  3 + class="step2-style"
  4 + :style="[isMqttType == 'DEFAULT' ? { minHeight: 0 + 'px' } : { minHeight: 800 + 'px' }]"
  5 + >
3 <div 6 <div
4 - style="margin-top: 0.1vh; height: 15vh"  
5 :style="[ 7 :style="[
6 isMqttType == 'MQTT' 8 isMqttType == 'MQTT'
7 ? { minHeight: 45 + 'vh' } 9 ? { minHeight: 45 + 'vh' }
@@ -40,7 +42,7 @@ @@ -40,7 +42,7 @@
40 <script lang="ts" setup> 42 <script lang="ts" setup>
41 import { reactive, ref, onUnmounted, nextTick } from 'vue'; 43 import { reactive, ref, onUnmounted, nextTick } from 'vue';
42 import { BasicForm, useForm } from '/@/components/Form'; 44 import { BasicForm, useForm } from '/@/components/Form';
43 - import { step2Schemas } from './data'; 45 + import { step2Schemas } from '../device.profile.data';
44 import { Button } from '/@/components/Button'; 46 import { Button } from '/@/components/Button';
45 import MqttCpns from './cpns/mqtt/Mqtt.vue'; 47 import MqttCpns from './cpns/mqtt/Mqtt.vue';
46 import CoapCpns from './cpns/coap/Coap.vue'; 48 import CoapCpns from './cpns/coap/Coap.vue';
@@ -52,7 +54,7 @@ @@ -52,7 +54,7 @@
52 const coapRef = ref<InstanceType<typeof CoapCpns>>(); 54 const coapRef = ref<InstanceType<typeof CoapCpns>>();
53 const lwm2mRef = ref<InstanceType<typeof Lwm2mCpns>>(); 55 const lwm2mRef = ref<InstanceType<typeof Lwm2mCpns>>();
54 const snmpRef = ref<InstanceType<typeof SnmpCpns>>(); 56 const snmpRef = ref<InstanceType<typeof SnmpCpns>>();
55 - const isMqttType = ref(''); 57 + const isMqttType = ref('DEFAULT');
56 let step2Data = reactive({ 58 let step2Data = reactive({
57 transportConfiguration: {}, 59 transportConfiguration: {},
58 }); 60 });
@@ -75,7 +77,7 @@ @@ -75,7 +77,7 @@
75 }; 77 };
76 78
77 const resetFormData = () => { 79 const resetFormData = () => {
78 - isMqttType.value = ''; 80 + isMqttType.value = 'DEFAULT';
79 resetFields(); 81 resetFields();
80 nextTick(() => { 82 nextTick(() => {
81 mqttRef.value?.resetFormData(); 83 mqttRef.value?.resetFormData();
@@ -107,7 +109,7 @@ @@ -107,7 +109,7 @@
107 }); 109 });
108 }); 110 });
109 onUnmounted(() => { 111 onUnmounted(() => {
110 - isMqttType.value = ''; 112 + isMqttType.value = 'DEFAULT';
111 }); 113 });
112 const getFormData = async () => { 114 const getFormData = async () => {
113 const val = await validate(); 115 const val = await validate();
1 -import { FormSchema } from '/@/components/Form';  
2 -import { deviceConfigGetRuleChain } from '/@/api/device/deviceConfigApi';  
3 -import { findDictItemByCode } from '/@/api/system/dict';  
4 -  
5 -export const step1Schemas: FormSchema[] = [  
6 - {  
7 - field: 'image',  
8 - label: '上传图片',  
9 - component: 'Input',  
10 - slot: 'imageSelect',  
11 - },  
12 - {  
13 - field: 'name',  
14 - label: '配置名称',  
15 - required: true,  
16 - component: 'Input',  
17 - componentProps() {  
18 - return {  
19 - disabled: false,  
20 - ength: 255,  
21 - placeholder: '请输入配置名称',  
22 - };  
23 - },  
24 - },  
25 - {  
26 - field: 'defaultRuleChainId',  
27 - label: '规则链',  
28 - component: 'ApiSelect',  
29 - componentProps: {  
30 - api: async () => {  
31 - const data = await deviceConfigGetRuleChain();  
32 - const returnData = data.map((m) => {  
33 - return {  
34 - getValueField: m.name,  
35 - getKeyField: m.id.id,  
36 - };  
37 - });  
38 - return returnData;  
39 - },  
40 - labelField: 'getValueField',  
41 - valueField: 'getKeyField',  
42 - immediate: true,  
43 - },  
44 - },  
45 - {  
46 - field: 'defaultQueueName',  
47 - label: '处理队列',  
48 - component: 'ApiSelect',  
49 - componentProps: {  
50 - api: findDictItemByCode,  
51 - params: {  
52 - dictCode: 'queen_execute_sequence',  
53 - },  
54 - labelField: 'itemText',  
55 - valueField: 'itemValue',  
56 - resultField: 'items',  
57 - },  
58 - },  
59 -  
60 - {  
61 - label: '描述',  
62 - field: 'description',  
63 - component: 'InputTextArea',  
64 - componentProps: {  
65 - maxLength: 255,  
66 - placeholder: '请输入描述',  
67 - },  
68 - },  
69 -];  
70 -  
71 -export const step2Schemas: FormSchema[] = [  
72 - {  
73 - field: 'transportType',  
74 - component: 'Select',  
75 - label: '接入协议',  
76 - defaultValue: 'DEFAULT',  
77 - componentProps() {  
78 - return {  
79 - options: [  
80 - { label: '默认', value: 'DEFAULT' },  
81 - { label: 'MQTT', value: 'MQTT' },  
82 - { label: 'CoAP', value: 'COAP' },  
83 - { label: 'LWM2M', value: 'LWM2M' },  
84 - { label: 'SNMP', value: 'SNMP' },  
85 - ],  
86 - };  
87 - },  
88 - colProps: { span: 10 },  
89 - },  
90 -];  
  1 +<template>
  2 + <div>
  3 + <BasicForm @register="registerForm">
  4 + <template #scriptContent>
  5 + <Card title="脚本内容" :bodyStyle="{ padding: 0, height: '280px' }">
  6 + <div ref="aceRef" class="overflow-hidden"></div>
  7 + </Card>
  8 + <Button @click="handleCopy" class="mt-4">
  9 + <template #icon>
  10 + <CopyOutlined />
  11 + </template>
  12 + copy
  13 + </Button>
  14 + </template>
  15 + </BasicForm>
  16 + </div>
  17 +</template>
  18 +<script setup lang="ts">
  19 + import { ref, unref } from 'vue';
  20 + import { formSchema } from './config.data';
  21 + import { BasicForm, useForm } from '/@/components/Form';
  22 + import ace from 'ace-builds';
  23 + import { Card, Button } from 'ant-design-vue';
  24 + import 'ace-builds/src-noconflict/theme-chrome'; // 默认设置的主题
  25 + import 'ace-builds/src-noconflict/mode-javascript'; // 默认设置的语言模式
  26 + import { beautify } from 'ace-builds/src-noconflict/ext-beautify.js';
  27 + import { CopyOutlined } from '@ant-design/icons-vue';
  28 + import { useCopyToClipboard } from '/@/hooks/web/useCopyToClipboard';
  29 + import { useMessage } from '/@/hooks/web/useMessage';
  30 +
  31 + defineEmits(['register']);
  32 + const { createMessage } = useMessage();
  33 + const { clipboardRef, copiedRef } = useCopyToClipboard();
  34 + const aceEditor = ref();
  35 + const aceRef = ref();
  36 + const [registerForm, { validate, resetFields }] = useForm({
  37 + labelWidth: 120,
  38 + schemas: formSchema,
  39 + showActionButtonGroup: false,
  40 + });
  41 + // 初始化编辑器
  42 + const initEditor = (jsScript?: string) => {
  43 + aceEditor.value = ace.edit(aceRef.value, {
  44 + maxLines: 12, // 最大行数,超过会自动出现滚动条
  45 + minLines: 12, // 最小行数,还未到最大行数时,编辑器会自动伸缩大小
  46 + fontSize: 14, // 编辑器内字体大小
  47 + theme: 'ace/theme/chrome', // 默认设置的主题
  48 + mode: 'ace/mode/javascript', // 默认设置的语言模式
  49 + tabSize: 2, // 制表符设置为 4 个空格大小
  50 + });
  51 +
  52 + aceEditor.value.setOptions({
  53 + enableBasicAutocompletion: true,
  54 + enableLiveAutocompletion: true,
  55 + });
  56 + aceEditor.value.setValue(
  57 + jsScript ??
  58 + `
  59 + var trimSource =source.replaceAll(" ","");
  60 + if(trimSource.length==26 && trimSource.startsWith("020308")){
  61 + var str = "";
  62 + for(var i = 6;i<20;i+=2){
  63 + str += String.fromCharCode(parseInt(trimSource[i]+trimSource[i+1],16));
  64 + }`
  65 + );
  66 + beautify(aceEditor.value.session);
  67 + };
  68 + const handleCopy = () => {
  69 + const valueRef = aceEditor.value.getValue();
  70 + const value = unref(valueRef);
  71 + if (!value) {
  72 + createMessage.warning('请输入要拷贝的内容!');
  73 + return;
  74 + }
  75 + clipboardRef.value = value;
  76 + if (unref(copiedRef)) {
  77 + createMessage.success('复制成功!');
  78 + }
  79 + };
  80 + const getFormData = async () => {
  81 + const value = await validate();
  82 + if (!value) return;
  83 + return value;
  84 + };
  85 + const resetFormData = () => {
  86 + resetFields();
  87 + };
  88 +
  89 + defineExpose({
  90 + initEditor,
  91 + getFormData,
  92 + resetFormData,
  93 + });
  94 +</script>
  95 +<style lang="less" scoped>
  96 + @import url('./ConverScriptModal.less');
  97 +</style>
  1 +.wrapper {
  2 + margin: 10px 60px 60px 60px;
  3 +}
  4 +
  5 +.inner {
  6 + width: 400px;
  7 + height: 400px;
  8 +}
  9 +
  10 +.item {
  11 + text-align: center;
  12 + font-size: 200%;
  13 + color: #fff;
  14 +}
  1 +<template>
  2 + <div>
  3 + <BasicModal
  4 + destroyOnClose
  5 + v-bind="$attrs"
  6 + width="60rem"
  7 + @register="register"
  8 + :title="getTitle"
  9 + :minHeight="500"
  10 + @cancel="handleCancel"
  11 + @ok="handleSubmit"
  12 + >
  13 + <ConverScript ref="converScriptRef" />
  14 + </BasicModal>
  15 + </div>
  16 +</template>
  17 +<script setup lang="ts">
  18 + import { ref, computed, unref } from 'vue';
  19 + import { BasicModal, useModalInner } from '/@/components/Modal';
  20 + import ConverScript from './ConverScript.vue';
  21 +
  22 + const converScriptRef = ref<InstanceType<typeof ConverScript>>();
  23 + const getTitle = computed(() => (isUpdate.value ? '编辑转换脚本' : '新增转换脚本'));
  24 + const isUpdate = ref(false);
  25 + const isViewDetail = ref('');
  26 + const [register, { setModalProps, closeModal }] = useModalInner(async (data) => {
  27 + setModalProps({ loading: true });
  28 + isUpdate.value = data.isUpdate;
  29 + isViewDetail.value = data.isView;
  30 + converScriptRef.value?.initEditor(data.record?.configuration?.jsScript);
  31 + setModalProps({ loading: false });
  32 + if (!unref(isViewDetail)) {
  33 + const title = !unref(isUpdate) ? '编辑转换脚本' : '新增转换脚本';
  34 + setModalProps({ title, showOkBtn: true, showCancelBtn: true });
  35 + if (!unref(isUpdate)) {
  36 + }
  37 + } else {
  38 + setModalProps({ showOkBtn: false, showCancelBtn: false, title: '查看转换脚本' });
  39 + }
  40 + });
  41 + const handleSubmit = async () => {
  42 + const val = await converScriptRef.value?.getFormData();
  43 + console.log(val);
  44 + };
  45 + const handleCancel = () => {
  46 + closeModal();
  47 + converScriptRef.value?.resetFormData();
  48 + };
  49 +</script>
  50 +<style lang="less" scoped>
  51 + @import url('./ConverScriptModal.less');
  52 +</style>
  1 +import { BasicColumn, FormSchema } from '/@/components/Table';
  2 +import moment from 'moment';
  3 +import { h } from 'vue';
  4 +import { Tag } from 'ant-design-vue';
  5 +
  6 +// 表格配置
  7 +export const columns: BasicColumn[] = [
  8 + {
  9 + title: '脚本名称',
  10 + dataIndex: 'reportConfigName',
  11 + width: 80,
  12 + },
  13 + {
  14 + title: '脚本状态',
  15 + dataIndex: 'organizationName',
  16 + width: 120,
  17 + customRender: ({ record }) => {
  18 + const status = record.organizationName;
  19 + const color = status == 1 ? 'green' : 'red';
  20 + const text = status == 1 ? '启用' : '禁用';
  21 + return h(Tag, { color: color }, () => text);
  22 + },
  23 + },
  24 + {
  25 + title: '脚本内容',
  26 + dataIndex: 'dataType',
  27 + width: 120,
  28 + slots: { customRender: 'dataType' },
  29 + },
  30 + {
  31 + title: '描述',
  32 + dataIndex: 'executeWay',
  33 + width: 120,
  34 + },
  35 + {
  36 + title: '创建日期',
  37 + dataIndex: 'executeTime',
  38 + width: 180,
  39 + },
  40 +];
  41 +
  42 +// 查询配置
  43 +export const searchFormSchema: FormSchema[] = [
  44 + {
  45 + field: 'reportConfigName',
  46 + label: '脚本名称',
  47 + component: 'Input',
  48 + colProps: { span: 6 },
  49 + componentProps: {
  50 + maxLength: 36,
  51 + placeholder: '请输入配置名称',
  52 + },
  53 + },
  54 + {
  55 + field: 'sendTime',
  56 + label: '创建时间',
  57 + component: 'RangePicker',
  58 + componentProps: {
  59 + showTime: {
  60 + defaultValue: [moment('00:00:00', 'HH:mm:ss'), moment('23:59:59', 'HH:mm:ss')],
  61 + },
  62 + },
  63 + colProps: { span: 6 },
  64 + },
  65 +];
  66 +
  67 +// 新增编辑配置
  68 +export const formSchema: FormSchema[] = [
  69 + {
  70 + field: 'name',
  71 + label: '名称',
  72 + colProps: { span: 24 },
  73 + required: true,
  74 + component: 'Input',
  75 + componentProps: {
  76 + maxLength: 255,
  77 + placeholder: '请输入脚本名称',
  78 + },
  79 + },
  80 + {
  81 + field: 'scriptContent',
  82 + label: '脚本内容',
  83 + required: true,
  84 + component: 'Input',
  85 + slot: 'scriptContent',
  86 + colProps: { span: 24 },
  87 + },
  88 + {
  89 + field: 'remark',
  90 + label: '备注',
  91 + colProps: { span: 24 },
  92 + component: 'InputTextArea',
  93 + componentProps: {
  94 + rows: 6,
  95 + maxLength: 255,
  96 + placeholder: '请输入备注',
  97 + },
  98 + },
  99 +];
  1 +<template>
  2 + <div>
  3 + <BasicTable :clickToRowSelect="false" @register="registerTable" :searchInfo="searchInfo">
  4 + <template #toolbar>
  5 + <Authority value="">
  6 + <a-button type="primary" @click="handleCreateOrEdit(null)"> 新增转换脚本 </a-button>
  7 + </Authority>
  8 + <Authority value="">
  9 + <Popconfirm
  10 + title="您确定要批量删除数据"
  11 + ok-text="确定"
  12 + cancel-text="取消"
  13 + @confirm="handleDeleteOrBatchDelete(null)"
  14 + >
  15 + <a-button type="primary" color="error" :disabled="hasBatchDelete"> 批量删除 </a-button>
  16 + </Popconfirm>
  17 + </Authority>
  18 + </template>
  19 + <template #dataType="{ record }">
  20 + <a-button type="text" @click="handleScriptView(record)">
  21 + <span style="color: #377dff">查看脚本</span>
  22 + </a-button>
  23 + </template>
  24 + <template #action="{ record }">
  25 + <TableAction
  26 + :actions="[
  27 + {
  28 + label: '编辑',
  29 + icon: 'clarity:note-edit-line',
  30 + auth: '',
  31 + onClick: handleCreateOrEdit.bind(null, record),
  32 + },
  33 + {
  34 + label: '删除',
  35 + icon: 'ant-design:delete-outlined',
  36 + auth: '',
  37 + color: 'error',
  38 + popConfirm: {
  39 + title: '是否确认删除',
  40 + confirm: handleDeleteOrBatchDelete.bind(null, record),
  41 + },
  42 + },
  43 + ]"
  44 + />
  45 + </template>
  46 + </BasicTable>
  47 + <ConverScriptModal @register="registerModal" />
  48 + </div>
  49 +</template>
  50 +
  51 +<script lang="ts" setup>
  52 + import { reactive, nextTick } from 'vue';
  53 + import { BasicTable, useTable, TableAction } from '/@/components/Table';
  54 + import { searchFormSchema, columns } from './config.data';
  55 + import { Authority } from '/@/components/Authority';
  56 + import { useBatchDelete } from '/@/hooks/web/useBatchDelete';
  57 + import { Popconfirm } from 'ant-design-vue';
  58 + import { useModal } from '/@/components/Modal';
  59 + import ConverScriptModal from './ConverScriptModal.vue';
  60 + import { exportPage, deleteExportManage } from '/@/api/export/exportManager';
  61 +
  62 + const searchInfo = reactive<Recordable>({});
  63 + const [registerTable, { reload, setProps, setTableData }] = useTable({
  64 + title: '转换脚本列表',
  65 + api: exportPage,
  66 + columns,
  67 + showIndexColumn: false,
  68 + clickToRowSelect: false,
  69 + formConfig: {
  70 + labelWidth: 120,
  71 + schemas: searchFormSchema,
  72 + fieldMapToTime: [['sendTime', ['startTime', 'endTime'], 'x']],
  73 + },
  74 + useSearchForm: true,
  75 + showTableSetting: true,
  76 + bordered: true,
  77 + rowKey: 'id',
  78 + actionColumn: {
  79 + width: 200,
  80 + title: '操作',
  81 + dataIndex: 'action',
  82 + slots: { customRender: 'action' },
  83 + fixed: 'right',
  84 + },
  85 + });
  86 +
  87 + const handleSuccess = () => {
  88 + reload();
  89 + };
  90 +
  91 + const { hasBatchDelete, handleDeleteOrBatchDelete, selectionOptions } = useBatchDelete(
  92 + deleteExportManage,
  93 + handleSuccess,
  94 + setProps
  95 + );
  96 +
  97 + nextTick(() => {
  98 + setProps(selectionOptions);
  99 + });
  100 +
  101 + const [registerModal, { openModal }] = useModal();
  102 +
  103 + // 新增或编辑
  104 + const handleCreateOrEdit = (record: Recordable | null) => {
  105 + setTableData([
  106 + {
  107 + id: 1,
  108 + reportConfigName: '11',
  109 + organizationName: 0,
  110 + dataType: '11',
  111 + executeWay: '11',
  112 + executeTime: '2022-05-21',
  113 + },
  114 + {
  115 + id: 2,
  116 + reportConfigName: '11',
  117 + organizationName: 0,
  118 + dataType: '11',
  119 + executeWay: '11',
  120 + executeTime: '2022-05-21',
  121 + },
  122 + ]);
  123 + if (record) {
  124 + openModal(true, {
  125 + isUpdate: false,
  126 + record,
  127 + isView: false,
  128 + });
  129 + } else {
  130 + openModal(true, {
  131 + isUpdate: true,
  132 + isView: false,
  133 + });
  134 + }
  135 + };
  136 + const handleScriptView = (record: Recordable | null) => {
  137 + if (record) {
  138 + openModal(true, {
  139 + isUpdate: true,
  140 + record,
  141 + isView: true,
  142 + });
  143 + }
  144 + };
  145 +</script>