Commit e563e9c80b8d8188155521c387a9a194096d4c1b

Authored by loveumiko
2 parents 22a3c563 3a45419b

拉取代码 解决看板修改之间的冲突

Showing 82 changed files with 1524 additions and 1210 deletions
@@ -25,6 +25,7 @@ module.exports = defineConfig({ @@ -25,6 +25,7 @@ module.exports = defineConfig({
25 'plugin:jest/recommended', 25 'plugin:jest/recommended',
26 ], 26 ],
27 rules: { 27 rules: {
  28 + 'no-console': 'off',
28 'vue/script-setup-uses-vars': 'error', 29 'vue/script-setup-uses-vars': 'error',
29 '@typescript-eslint/ban-ts-ignore': 'off', 30 '@typescript-eslint/ban-ts-ignore': 'off',
30 '@typescript-eslint/explicit-function-return-type': 'off', 31 '@typescript-eslint/explicit-function-return-type': 'off',
@@ -126,10 +126,16 @@ @@ -126,10 +126,16 @@
126 } 126 }
127 } 127 }
128 128
129 - async function handleFetch() {  
130 - if (!props.immediate && unref(isFirstLoad)) { 129 + // async function handleFetch() {
  130 + // if (!props.immediate && unref(isFirstLoad)) {
  131 + // await fetch();
  132 + // isFirstLoad.value = false;
  133 + // }
  134 + // }
  135 +
  136 + async function handleFetch(open) {
  137 + if (open) {
131 await fetch(); 138 await fetch();
132 - isFirstLoad.value = false;  
133 } 139 }
134 } 140 }
135 141
@@ -174,8 +174,7 @@ @@ -174,8 +174,7 @@
174 const handleFilterOption = async (inputValue: string, option: Recordable) => { 174 const handleFilterOption = async (inputValue: string, option: Recordable) => {
175 const { filterOption, fetchSearch } = props; 175 const { filterOption, fetchSearch } = props;
176 if (filterOption && isFunction(filterOption)) { 176 if (filterOption && isFunction(filterOption)) {
177 - filterOption?.(inputValue, option);  
178 - return; 177 + return filterOption?.(inputValue, option);
179 } 178 }
180 179
181 if (fetchSearch) { 180 if (fetchSearch) {
@@ -189,8 +188,9 @@ @@ -189,8 +188,9 @@
189 @dropdownVisibleChange="handleFetch" 188 @dropdownVisibleChange="handleFetch"
190 v-bind="attrs" 189 v-bind="attrs"
191 @change="handleChange" 190 @change="handleChange"
  191 + @filterOption="handleFilterOption"
192 :options="getOptions" 192 :options="getOptions"
193 - :filterOption="handleFilterOption" 193 + optionFilterProp="label"
194 :showSearch="true" 194 :showSearch="true"
195 v-model:value="state" 195 v-model:value="state"
196 @popup-scroll="debounceHandlePopupScroll" 196 @popup-scroll="debounceHandlePopupScroll"
@@ -30,12 +30,14 @@ @@ -30,12 +30,14 @@
30 modalProps?: ExtractPropTypes<InstanceType<typeof BasicModal>['$props']>; 30 modalProps?: ExtractPropTypes<InstanceType<typeof BasicModal>['$props']>;
31 transferProps?: ExtractPropTypes<TransferType['$props']>; 31 transferProps?: ExtractPropTypes<TransferType['$props']>;
32 buttonProps?: ExtractPropTypes<InstanceType<typeof Button>['$props']>; 32 buttonProps?: ExtractPropTypes<InstanceType<typeof Button>['$props']>;
  33 + disabled?: any;
33 }>(), 34 }>(),
34 { 35 {
35 labelField: 'label', 36 labelField: 'label',
36 valueField: 'value', 37 valueField: 'value',
37 buttonName: '选择产品', 38 buttonName: '选择产品',
38 maxTagLength: 2, 39 maxTagLength: 2,
  40 + disabled: false,
39 } 41 }
40 ); 42 );
41 43
@@ -146,6 +148,8 @@ @@ -146,6 +148,8 @@
146 ); 148 );
147 149
148 const handleOpenModal = () => { 150 const handleOpenModal = () => {
  151 + const { disabled } = props;
  152 + if (disabled) return;
149 openModal(true); 153 openModal(true);
150 }; 154 };
151 155
@@ -42,6 +42,7 @@ @@ -42,6 +42,7 @@
42 onValueChange?: (selectedRowkeys: string[]) => any[]; 42 onValueChange?: (selectedRowkeys: string[]) => any[];
43 onRemoveAfter?: (actionType: ActionType) => Promise<any>; 43 onRemoveAfter?: (actionType: ActionType) => Promise<any>;
44 onSelectedAfter?: (actionType: ActionType) => Promise<any>; 44 onSelectedAfter?: (actionType: ActionType) => Promise<any>;
  45 + disabled?: any;
45 }>(), 46 }>(),
46 { 47 {
47 buttonName: '选择设备', 48 buttonName: '选择设备',
@@ -49,6 +50,7 @@ @@ -49,6 +50,7 @@
49 maxTagLength: 2, 50 maxTagLength: 2,
50 labelField: 'label', 51 labelField: 'label',
51 valueField: 'value', 52 valueField: 'value',
  53 + disabled: false,
52 } 54 }
53 ); 55 );
54 56
@@ -217,6 +219,8 @@ @@ -217,6 +219,8 @@
217 } 219 }
218 220
219 const handleOpenModal = async () => { 221 const handleOpenModal = async () => {
  222 + const { disabled } = props;
  223 + if (disabled) return;
220 openModal(true); 224 openModal(true);
221 await nextTick(); 225 await nextTick();
222 if (props.value && !props.value.length) { 226 if (props.value && !props.value.length) {
@@ -3,24 +3,26 @@ @@ -3,24 +3,26 @@
3 <a-input 3 <a-input
4 placeholder="请输入参数key" 4 placeholder="请输入参数key"
5 v-model:value="param.label" 5 v-model:value="param.label"
  6 + :disabled="disabled"
6 style="width: 38%; margin-bottom: 5px" 7 style="width: 38%; margin-bottom: 5px"
7 @change="emitChange" 8 @change="emitChange"
8 /> 9 />
9 <a-input 10 <a-input
10 placeholder="请输入参数value" 11 placeholder="请输入参数value"
  12 + :disabled="disabled"
11 v-model:value="param.value" 13 v-model:value="param.value"
12 style="width: 38%; margin: 0 0 5px 60px" 14 style="width: 38%; margin: 0 0 5px 60px"
13 @change="emitChange" 15 @change="emitChange"
14 /> 16 />
15 <MinusCircleOutlined 17 <MinusCircleOutlined
16 - v-if="dynamicInput.params.length > min" 18 + v-if="dynamicInput.params.length > min && !disabled"
17 class="dynamic-delete-button" 19 class="dynamic-delete-button"
18 @click="remove(param)" 20 @click="remove(param)"
19 style="width: 50px" 21 style="width: 50px"
20 /> 22 />
21 </div> 23 </div>
22 <div> 24 <div>
23 - <a-button type="dashed" style="width: 38%" @click="add"> 25 + <a-button :disabled="disabled" type="dashed" style="width: 38%" @click="add">
24 <PlusOutlined /> 26 <PlusOutlined />
25 新增 27 新增
26 </a-button> 28 </a-button>
@@ -49,11 +51,16 @@ @@ -49,11 +51,16 @@
49 value: propTypes.object.def({}), 51 value: propTypes.object.def({}),
50 //自定义删除按钮多少才会显示 52 //自定义删除按钮多少才会显示
51 min: propTypes.integer.def(0), 53 min: propTypes.integer.def(0),
  54 + disabled: {
  55 + type: Boolean,
  56 + default: false,
  57 + },
52 }, 58 },
53 emits: ['change', 'update:value'], 59 emits: ['change', 'update:value'],
54 setup(props, { emit }) { 60 setup(props, { emit }) {
55 //input动态数据 61 //input动态数据
56 const dynamicInput: UnwrapRef<{ params: Params[] }> = reactive({ params: [] }); 62 const dynamicInput: UnwrapRef<{ params: Params[] }> = reactive({ params: [] });
  63 +
57 //删除Input 64 //删除Input
58 const remove = (item: Params) => { 65 const remove = (item: Params) => {
59 let index = dynamicInput.params.indexOf(item); 66 let index = dynamicInput.params.indexOf(item);
@@ -108,6 +115,7 @@ @@ -108,6 +115,7 @@
108 emitChange, 115 emitChange,
109 remove, 116 remove,
110 add, 117 add,
  118 + // disabled,
111 }; 119 };
112 }, 120 },
113 }); 121 });
@@ -39,7 +39,7 @@ export const formSchemas = (hasStructForm: boolean, isTcp = false): FormSchema[] @@ -39,7 +39,7 @@ export const formSchemas = (hasStructForm: boolean, isTcp = false): FormSchema[]
39 span: 18, 39 span: 18,
40 }, 40 },
41 componentProps: { 41 componentProps: {
42 - maxLength: 255, 42 + maxLength: 32,
43 placeholder: '请输入功能名称', 43 placeholder: '请输入功能名称',
44 }, 44 },
45 }, 45 },
@@ -52,7 +52,7 @@ export const formSchemas = (hasStructForm: boolean, isTcp = false): FormSchema[] @@ -52,7 +52,7 @@ export const formSchemas = (hasStructForm: boolean, isTcp = false): FormSchema[]
52 span: 18, 52 span: 18,
53 }, 53 },
54 componentProps: { 54 componentProps: {
55 - maxLength: 255, 55 + maxLength: 128,
56 placeholder: '请输入标识符', 56 placeholder: '请输入标识符',
57 }, 57 },
58 }, 58 },
@@ -394,6 +394,13 @@ @@ -394,6 +394,13 @@
394 //.ant-table-tbody > tr.ant-table-row-selected td { 394 //.ant-table-tbody > tr.ant-table-row-selected td {
395 //background-color: fade(@primary-color, 8%) !important; 395 //background-color: fade(@primary-color, 8%) !important;
396 //} 396 //}
  397 + .ant-table-placeholder {
  398 + display: flex;
  399 + align-items: center;
  400 + justify-content: center;
  401 + height: 670px;
  402 + max-height: 670px;
  403 + }
397 } 404 }
398 405
399 .ant-pagination { 406 .ant-pagination {
@@ -8,24 +8,6 @@ @@ -8,24 +8,6 @@
8 @ok="handleSubmit" 8 @ok="handleSubmit"
9 > 9 >
10 <BasicForm @register="registerForm"> 10 <BasicForm @register="registerForm">
11 - <template #iconSelect>  
12 - <Upload  
13 - name="avatar"  
14 - accept=".png,.jpg,.jpeg,.gif"  
15 - list-type="picture-card"  
16 - class="avatar-uploader"  
17 - :show-upload-list="false"  
18 - :customRequest="customUpload"  
19 - :before-upload="beforeUpload"  
20 - >  
21 - <img v-if="tenantLogo" :src="tenantLogo" alt="avatar" />  
22 - <div v-else>  
23 - <LoadingOutlined v-if="loading" />  
24 - <plus-outlined v-else />  
25 - <div class="ant-upload-text">上传</div>  
26 - </div>  
27 - </Upload>  
28 - </template>  
29 <template #videoPlatformIdSlot="{ model, field }"> 11 <template #videoPlatformIdSlot="{ model, field }">
30 <a-select 12 <a-select
31 placeholder="请选择流媒体配置" 13 placeholder="请选择流媒体配置"
@@ -52,23 +34,20 @@ @@ -52,23 +34,20 @@
52 import { formSchema } from './config.data'; 34 import { formSchema } from './config.data';
53 import { BasicDrawer, useDrawerInner } from '/@/components/Drawer'; 35 import { BasicDrawer, useDrawerInner } from '/@/components/Drawer';
54 import { createOrEditCameraManage } from '/@/api/camera/cameraManager'; 36 import { createOrEditCameraManage } from '/@/api/camera/cameraManager';
55 - import { message, Upload } from 'ant-design-vue';  
56 import { useMessage } from '/@/hooks/web/useMessage'; 37 import { useMessage } from '/@/hooks/web/useMessage';
57 - import { PlusOutlined, LoadingOutlined } from '@ant-design/icons-vue';  
58 - import { upload } from '/@/api/oss/ossFileUploader';  
59 - import { FileItem } from '/@/components/Upload/src/typing'; 38 + import { PlusOutlined } from '@ant-design/icons-vue';
60 import { getStreamingMediaList } from '/@/api/camera/cameraManager'; 39 import { getStreamingMediaList } from '/@/api/camera/cameraManager';
61 import SteramingDrawer from '../streaming/SteramingDrawer.vue'; 40 import SteramingDrawer from '../streaming/SteramingDrawer.vue';
62 import { useDrawer } from '/@/components/Drawer'; 41 import { useDrawer } from '/@/components/Drawer';
  42 + import { FileItem } from '/@/components/Form/src/components/ApiUpload.vue';
  43 + import { buildUUID } from '/@/utils/uuid';
63 44
64 export default defineComponent({ 45 export default defineComponent({
65 name: 'ContactDrawer', 46 name: 'ContactDrawer',
66 components: { 47 components: {
67 BasicDrawer, 48 BasicDrawer,
68 BasicForm, 49 BasicForm,
69 - Upload,  
70 PlusOutlined, 50 PlusOutlined,
71 - LoadingOutlined,  
72 SteramingDrawer, 51 SteramingDrawer,
73 VNodes: (_, { attrs }) => { 52 VNodes: (_, { attrs }) => {
74 return attrs.vnodes; 53 return attrs.vnodes;
@@ -118,42 +97,19 @@ @@ -118,42 +97,19 @@
118 if (unref(isUpdate)) { 97 if (unref(isUpdate)) {
119 await nextTick(); 98 await nextTick();
120 editId.value = data.record.id; 99 editId.value = data.record.id;
121 - tenantLogo.value = data.record?.avatar;  
122 - await setFieldsValue(data.record); 100 + if (data.record.avatar) {
  101 + setFieldsValue({
  102 + avatar: [{ uid: buildUUID(), name: 'name', url: data.record.avatar } as FileItem],
  103 + });
  104 + }
  105 + const { avatar, ...params } = data.record;
  106 + console.log(avatar);
  107 + await setFieldsValue({ ...params });
123 } else { 108 } else {
124 - tenantLogo.value = '';  
125 editId.value = ''; 109 editId.value = '';
126 } 110 }
127 }); 111 });
128 112
129 - const tenantLogo = ref('');  
130 -  
131 - async function customUpload({ file }) {  
132 - if (beforeUpload(file)) {  
133 - tenantLogo.value = '';  
134 - loading.value = true;  
135 - const formData = new FormData();  
136 - formData.append('file', file);  
137 - const response = await upload(formData);  
138 - if (response.fileStaticUri) {  
139 - tenantLogo.value = response.fileStaticUri;  
140 - loading.value = false;  
141 - }  
142 - }  
143 - }  
144 -  
145 - const beforeUpload = (file: FileItem) => {  
146 - const isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png';  
147 - if (!isJpgOrPng) {  
148 - message.error('只能上传图片文件!');  
149 - }  
150 - const isLt2M = (file.size as number) / 1024 / 1024 < 5;  
151 - if (!isLt2M) {  
152 - message.error('图片大小不能超过5MB!');  
153 - }  
154 - return isJpgOrPng && isLt2M;  
155 - };  
156 -  
157 const getTitle = computed(() => (!unref(isUpdate) ? '新增视频配置' : '编辑视频配置')); 113 const getTitle = computed(() => (!unref(isUpdate) ? '新增视频配置' : '编辑视频配置'));
158 114
159 async function handleSubmit() { 115 async function handleSubmit() {
@@ -162,8 +118,9 @@ @@ -162,8 +118,9 @@
162 const { createMessage } = useMessage(); 118 const { createMessage } = useMessage();
163 const values = await validate(); 119 const values = await validate();
164 if (!values) return; 120 if (!values) return;
165 - if (tenantLogo.value !== '') {  
166 - values.avatar = tenantLogo.value; 121 + if (Reflect.has(values, 'avatar')) {
  122 + const file = (values.avatar || []).at(0) || {};
  123 + values.avatar = file.url || null;
167 } 124 }
168 if (editId.value !== '') { 125 if (editId.value !== '') {
169 values.id = editId.value; 126 values.id = editId.value;
@@ -188,9 +145,6 @@ @@ -188,9 +145,6 @@
188 registerDrawer, 145 registerDrawer,
189 registerForm, 146 registerForm,
190 handleSubmit, 147 handleSubmit,
191 - customUpload,  
192 - beforeUpload,  
193 - tenantLogo,  
194 loading, 148 loading,
195 streamConfigOptions, 149 streamConfigOptions,
196 registerSteramingDrawer, 150 registerSteramingDrawer,
@@ -20,6 +20,12 @@ @@ -20,6 +20,12 @@
20 import { useFingerprint } from '/@/utils/useFingerprint'; 20 import { useFingerprint } from '/@/utils/useFingerprint';
21 import { GetResult } from '@fingerprintjs/fingerprintjs'; 21 import { GetResult } from '@fingerprintjs/fingerprintjs';
22 22
  23 + const props = defineProps({
  24 + mode: {
  25 + type: String,
  26 + default: PageMode.SPLIT_SCREEN_MODE,
  27 + },
  28 + });
23 type CameraRecordItem = CameraRecord & { 29 type CameraRecordItem = CameraRecord & {
24 canPlay?: boolean; 30 canPlay?: boolean;
25 isTransform?: boolean; 31 isTransform?: boolean;
@@ -252,7 +258,11 @@ @@ -252,7 +258,11 @@
252 </Authority> 258 </Authority>
253 </div> 259 </div>
254 <Space> 260 <Space>
255 - <Button type="primary" @click="handleChangeMode(PageMode.SPLIT_SCREEN_MODE)"> 261 + <Button
  262 + v-if="props.mode !== PageMode.SPLIT_SCREEN_MODE"
  263 + type="primary"
  264 + @click="handleChangeMode(PageMode.SPLIT_SCREEN_MODE)"
  265 + >
256 分屏模式 266 分屏模式
257 </Button> 267 </Button>
258 <Button type="primary" @click="handleChangeMode(PageMode.LIST_MODE)"> 268 <Button type="primary" @click="handleChangeMode(PageMode.LIST_MODE)">
@@ -5,6 +5,9 @@ import { CameraVideoUrl, CameraMaxLength } from '/@/utils/rules'; @@ -5,6 +5,9 @@ import { CameraVideoUrl, CameraMaxLength } from '/@/utils/rules';
5 import { h } from 'vue'; 5 import { h } from 'vue';
6 import SnHelpMessage from './SnHelpMessage.vue'; 6 import SnHelpMessage from './SnHelpMessage.vue';
7 import { OrgTreeSelect } from '../../common/OrgTreeSelect'; 7 import { OrgTreeSelect } from '../../common/OrgTreeSelect';
  8 +import { FileItem } from '/@/components/Form/src/components/ApiUpload.vue';
  9 +import { createImgPreview } from '/@/components/Preview';
  10 +import { uploadThumbnail } from '/@/api/configuration/center/configurationCenter';
8 11
9 useComponentRegister('OrgTreeSelect', OrgTreeSelect); 12 useComponentRegister('OrgTreeSelect', OrgTreeSelect);
10 13
@@ -102,8 +105,33 @@ export const formSchema: QFormSchema[] = [ @@ -102,8 +105,33 @@ export const formSchema: QFormSchema[] = [
102 { 105 {
103 field: 'avatar', 106 field: 'avatar',
104 label: '视频封面', 107 label: '视频封面',
105 - slot: 'iconSelect',  
106 - component: 'Input', 108 + component: 'ApiUpload',
  109 + changeEvent: 'update:fileList',
  110 + valueField: 'fileList',
  111 + componentProps: () => {
  112 + return {
  113 + listType: 'picture-card',
  114 + maxFileLimit: 1,
  115 + accept: '.png,.jpg,.jpeg,.gif',
  116 + api: async (file: File) => {
  117 + try {
  118 + const formData = new FormData();
  119 + formData.set('file', file);
  120 + const { fileStaticUri, fileName } = await uploadThumbnail(formData);
  121 + return {
  122 + uid: fileStaticUri,
  123 + name: fileName,
  124 + url: fileStaticUri,
  125 + } as FileItem;
  126 + } catch (error) {
  127 + return {};
  128 + }
  129 + },
  130 + onPreview: (fileList: FileItem) => {
  131 + createImgPreview({ imageList: [fileList.url!] });
  132 + },
  133 + };
  134 + },
107 }, 135 },
108 { 136 {
109 field: 'name', 137 field: 'name',
@@ -12,7 +12,11 @@ @@ -12,7 +12,11 @@
12 12
13 <template> 13 <template>
14 <div> 14 <div>
15 - <SplitScreenMode v-if="mode == PageMode.SPLIT_SCREEN_MODE" @switchMode="handleSwitchMode" /> 15 + <SplitScreenMode
  16 + :mode="mode"
  17 + v-if="mode == PageMode.SPLIT_SCREEN_MODE"
  18 + @switchMode="handleSwitchMode"
  19 + />
16 <ListMode v-if="mode === PageMode.LIST_MODE" @switchMode="handleSwitchMode" /> 20 <ListMode v-if="mode === PageMode.LIST_MODE" @switchMode="handleSwitchMode" />
17 </div> 21 </div>
18 </template> 22 </template>
@@ -12,6 +12,9 @@ import ObjectModelValidateForm from '/@/components/Form/src/externalCompns/compo @@ -12,6 +12,9 @@ import ObjectModelValidateForm from '/@/components/Form/src/externalCompns/compo
12 import { CommandDeliveryWayEnum, ServiceCallTypeEnum } from '/@/enums/toolEnum'; 12 import { CommandDeliveryWayEnum, ServiceCallTypeEnum } from '/@/enums/toolEnum';
13 import { TaskTypeEnum } from '/@/views/task/center/config'; 13 import { TaskTypeEnum } from '/@/views/task/center/config';
14 import { AddressTypeEnum } from '/@/views/task/center/components/PollCommandInput'; 14 import { AddressTypeEnum } from '/@/views/task/center/components/PollCommandInput';
  15 +import { FileItem } from '/@/components/Form/src/components/ApiUpload.vue';
  16 +import { createImgPreview } from '/@/components/Preview';
  17 +import { uploadThumbnail } from '/@/api/configuration/center/configurationCenter';
15 18
16 useComponentRegister('JSONEditor', JSONEditor); 19 useComponentRegister('JSONEditor', JSONEditor);
17 useComponentRegister('ObjectModelValidateForm', ObjectModelValidateForm); 20 useComponentRegister('ObjectModelValidateForm', ObjectModelValidateForm);
@@ -29,15 +32,40 @@ export const step1Schemas: FormSchema[] = [ @@ -29,15 +32,40 @@ export const step1Schemas: FormSchema[] = [
29 { 32 {
30 field: 'icon', 33 field: 'icon',
31 label: '设备图片', 34 label: '设备图片',
32 - slot: 'iconSelect',  
33 - component: 'Input', 35 + component: 'ApiUpload',
  36 + changeEvent: 'update:fileList',
  37 + valueField: 'fileList',
  38 + componentProps: () => {
  39 + return {
  40 + listType: 'picture-card',
  41 + maxFileLimit: 1,
  42 + accept: '.png,.jpg,.jpeg,.gif',
  43 + api: async (file: File) => {
  44 + try {
  45 + const formData = new FormData();
  46 + formData.set('file', file);
  47 + const { fileStaticUri, fileName } = await uploadThumbnail(formData);
  48 + return {
  49 + uid: fileStaticUri,
  50 + name: fileName,
  51 + url: fileStaticUri,
  52 + } as FileItem;
  53 + } catch (error) {
  54 + return {};
  55 + }
  56 + },
  57 + onPreview: (fileList: FileItem) => {
  58 + createImgPreview({ imageList: [fileList.url!] });
  59 + },
  60 + };
  61 + },
34 }, 62 },
35 { 63 {
36 field: 'alias', 64 field: 'alias',
37 label: '别名 ', 65 label: '别名 ',
38 component: 'Input', 66 component: 'Input',
39 componentProps: { 67 componentProps: {
40 - maxLength: 255, 68 + maxLength: 32,
41 placeholder: '请输入别名', 69 placeholder: '请输入别名',
42 }, 70 },
43 }, 71 },
@@ -45,6 +73,10 @@ export const step1Schemas: FormSchema[] = [ @@ -45,6 +73,10 @@ export const step1Schemas: FormSchema[] = [
45 field: 'name', 73 field: 'name',
46 label: '设备名称', 74 label: '设备名称',
47 component: 'Input', 75 component: 'Input',
  76 + componentProps: {
  77 + maxLength: 32,
  78 + placeholder: '请输入别名',
  79 + },
48 rules: [{ required: true, message: '设备名称为必填项' }], 80 rules: [{ required: true, message: '设备名称为必填项' }],
49 slot: 'snCode', 81 slot: 'snCode',
50 }, 82 },
@@ -5,8 +5,8 @@ import { DeviceTypeEnum } from '/@/api/device/model/deviceModel'; @@ -5,8 +5,8 @@ import { DeviceTypeEnum } from '/@/api/device/model/deviceModel';
5 import { getCustomerList } from '/@/api/device/deviceManager'; 5 import { getCustomerList } from '/@/api/device/deviceManager';
6 import { DescItem } from '/@/components/Description/index'; 6 import { DescItem } from '/@/components/Description/index';
7 import moment from 'moment'; 7 import moment from 'moment';
8 -import { h } from 'vue';  
9 -import { Button } from 'ant-design-vue'; 8 +import { CSSProperties, h } from 'vue';
  9 +import { Button, Tooltip } from 'ant-design-vue';
10 import { TypeEnum } from './data'; 10 import { TypeEnum } from './data';
11 import { PageEnum } from '/@/enums/pageEnum'; 11 import { PageEnum } from '/@/enums/pageEnum';
12 import { useGo } from '/@/hooks/web/usePage'; 12 import { useGo } from '/@/hooks/web/usePage';
@@ -24,12 +24,19 @@ export const descSchema = (emit: EmitType): DescItem[] => { @@ -24,12 +24,19 @@ export const descSchema = (emit: EmitType): DescItem[] => {
24 field: 'name', 24 field: 'name',
25 label: '设备名称', 25 label: '设备名称',
26 render(val, data: Record<'alias' | 'name', string>) { 26 render(val, data: Record<'alias' | 'name', string>) {
27 - return h('span', {}, data.alias || val); 27 + return h(Tooltip, { title: data.alias || val }, () =>
  28 + h('span', { style: { cursor: 'pointer' } as CSSProperties }, data.alias || val)
  29 + );
28 }, 30 },
29 }, 31 },
30 { 32 {
31 field: 'label', 33 field: 'label',
32 label: '设备标签', 34 label: '设备标签',
  35 + render(val) {
  36 + return h(Tooltip, { title: val }, () =>
  37 + h('span', { style: { cursor: 'pointer' } as CSSProperties }, val)
  38 + );
  39 + },
33 }, 40 },
34 { 41 {
35 field: 'deviceProfile.name', 42 field: 'deviceProfile.name',
@@ -79,6 +86,11 @@ export const descSchema = (emit: EmitType): DescItem[] => { @@ -79,6 +86,11 @@ export const descSchema = (emit: EmitType): DescItem[] => {
79 field: 'description', 86 field: 'description',
80 label: '描述', 87 label: '描述',
81 // span: 2, 88 // span: 2,
  89 + render(val) {
  90 + return h(Tooltip, { title: val }, () =>
  91 + h('span', { style: { cursor: 'pointer' } as CSSProperties }, val)
  92 + );
  93 + },
82 }, 94 },
83 ]; 95 ];
84 }; 96 };
@@ -4,7 +4,7 @@ import { FormSchema } from '/@/components/Table'; @@ -4,7 +4,7 @@ import { FormSchema } from '/@/components/Table';
4 import { DeviceTypeEnum, DeviceState, DeviceRecord } from '/@/api/device/model/deviceModel'; 4 import { DeviceTypeEnum, DeviceState, DeviceRecord } from '/@/api/device/model/deviceModel';
5 import { deviceProfile } from '/@/api/device/deviceManager'; 5 import { deviceProfile } from '/@/api/device/deviceManager';
6 import { h } from 'vue'; 6 import { h } from 'vue';
7 -import { Tag } from 'ant-design-vue'; 7 +import { Tag, Tooltip } from 'ant-design-vue';
8 import { handeleCopy } from '../../profiles/step/topic'; 8 import { handeleCopy } from '../../profiles/step/topic';
9 9
10 // 表格列数据 10 // 表格列数据
@@ -32,19 +32,33 @@ export const columns: BasicColumn[] = [ @@ -32,19 +32,33 @@ export const columns: BasicColumn[] = [
32 h( 32 h(
33 'div', 33 'div',
34 { 34 {
35 - class: 'cursor-pointer', 35 + class: 'cursor-pointer truncate',
36 }, 36 },
37 - `${record.alias}` 37 + h(
  38 + Tooltip,
  39 + {
  40 + placement: 'topLeft',
  41 + title: `${record.alias}`,
  42 + },
  43 + () => `${record.alias}`
  44 + )
38 ), 45 ),
39 h( 46 h(
40 'div', 47 'div',
41 { 48 {
42 - class: 'cursor-pointer text-blue-500', 49 + class: 'cursor-pointer text-blue-500 truncate',
43 onClick: () => { 50 onClick: () => {
44 handeleCopy(`${record.name}`); 51 handeleCopy(`${record.name}`);
45 }, 52 },
46 }, 53 },
47 - `${record.name}` 54 + h(
  55 + Tooltip,
  56 + {
  57 + placement: 'topLeft',
  58 + title: `${record.name}`,
  59 + },
  60 + () => `${record.name}`
  61 + )
48 ), 62 ),
49 ]); 63 ]);
50 }, 64 },
@@ -109,7 +109,8 @@ export const basicInfoForm: FormSchema[] = [ @@ -109,7 +109,8 @@ export const basicInfoForm: FormSchema[] = [
109 label: '所属组织', 109 label: '所属组织',
110 component: 'ApiTreeSelect', 110 component: 'ApiTreeSelect',
111 rules: [{ required: true, message: '所属组织为必填项' }], 111 rules: [{ required: true, message: '所属组织为必填项' }],
112 - componentProps: () => { 112 + componentProps: ({ formModel, formActionType }) => {
  113 + const { setFieldsValue } = formActionType;
113 return { 114 return {
114 maxLength: 250, 115 maxLength: 250,
115 placeholder: '请选择所属组织', 116 placeholder: '请选择所属组织',
@@ -119,6 +120,11 @@ export const basicInfoForm: FormSchema[] = [ @@ -119,6 +120,11 @@ export const basicInfoForm: FormSchema[] = [
119 return data; 120 return data;
120 }, 121 },
121 getPopupContainer: () => document.body, 122 getPopupContainer: () => document.body,
  123 + onChange(e) {
  124 + if (e != formModel?.[FieldsEnum.ORGANIZATION_ID]) {
  125 + setFieldsValue({ [FieldsEnum.GATEWAY_TB_DEVICE_ID]: null });
  126 + }
  127 + },
122 }; 128 };
123 }, 129 },
124 }, 130 },
@@ -193,7 +199,12 @@ export const basicInfoForm: FormSchema[] = [ @@ -193,7 +199,12 @@ export const basicInfoForm: FormSchema[] = [
193 deviceType: DeviceTypeEnum.GATEWAY, 199 deviceType: DeviceTypeEnum.GATEWAY,
194 organizationId, 200 organizationId,
195 }); 201 });
196 - return result; 202 + return result.map((item) => {
  203 + return {
  204 + ...item,
  205 + name: item.alias || item.name,
  206 + };
  207 + });
197 } catch (error) { 208 } catch (error) {
198 return []; 209 return [];
199 } 210 }
@@ -5,7 +5,7 @@ @@ -5,7 +5,7 @@
5 @register="register" 5 @register="register"
6 destroyOnClose 6 destroyOnClose
7 @close="closeDrawer" 7 @close="closeDrawer"
8 - :title="deviceDetail.alias || deviceDetail.name" 8 + :title="drawerTitle"
9 width="80%" 9 width="80%"
10 > 10 >
11 <Tabs v-model:activeKey="activeKey" :size="size"> 11 <Tabs v-model:activeKey="activeKey" :size="size">
@@ -58,7 +58,7 @@ @@ -58,7 +58,7 @@
58 </BasicDrawer> 58 </BasicDrawer>
59 </template> 59 </template>
60 <script lang="ts"> 60 <script lang="ts">
61 - import { defineComponent, ref } from 'vue'; 61 + import { defineComponent, ref, computed } from 'vue';
62 import { BasicDrawer, useDrawerInner } from '/@/components/Drawer'; 62 import { BasicDrawer, useDrawerInner } from '/@/components/Drawer';
63 63
64 import { Tabs } from 'ant-design-vue'; 64 import { Tabs } from 'ant-design-vue';
@@ -122,6 +122,10 @@ @@ -122,6 +122,10 @@
122 emit('openGatewayDeviceDetail', { id: data.gatewayId }); 122 emit('openGatewayDeviceDetail', { id: data.gatewayId });
123 }; 123 };
124 124
  125 + const drawerTitle = computed(() => {
  126 + return deviceDetail.value?.alias || deviceDetail.value?.name;
  127 + });
  128 +
125 return { 129 return {
126 size, 130 size,
127 activeKey, 131 activeKey,
@@ -132,6 +136,7 @@ @@ -132,6 +136,7 @@
132 tbDeviceId, 136 tbDeviceId,
133 handleOpenTbDeviceDetail, 137 handleOpenTbDeviceDetail,
134 handleOpenGatewayDevice, 138 handleOpenGatewayDevice,
  139 + drawerTitle,
135 }; 140 };
136 }, 141 },
137 }); 142 });
@@ -157,7 +157,7 @@ @@ -157,7 +157,7 @@
157 sn: stepRecord.name, 157 sn: stepRecord.name,
158 customerId: currentDeviceData.customerId, 158 customerId: currentDeviceData.customerId,
159 deviceInfo: { 159 deviceInfo: {
160 - avatar: DeviceStep1Ref.value?.devicePic, 160 + avatar: stepRecord?.icon,
161 ...DeviceStep1Ref.value?.devicePositionState, 161 ...DeviceStep1Ref.value?.devicePositionState,
162 }, 162 },
163 }; 163 };
@@ -169,7 +169,7 @@ @@ -169,7 +169,7 @@
169 ...stepRecord, 169 ...stepRecord,
170 sn: stepRecord.name, 170 sn: stepRecord.name,
171 deviceInfo: { 171 deviceInfo: {
172 - avatar: DeviceStep1Ref.value?.devicePic, 172 + avatar: stepRecord?.icon,
173 ...DeviceStep1Ref.value?.devicePositionState, 173 ...DeviceStep1Ref.value?.devicePositionState,
174 }, 174 },
175 deviceToken: 175 deviceToken:
@@ -22,24 +22,6 @@ @@ -22,24 +22,6 @@
22 </div> 22 </div>
23 </div> 23 </div>
24 </template> 24 </template>
25 - <template #iconSelect>  
26 - <Upload  
27 - name="avatar"  
28 - accept=".png,.jpg,.jpeg,.gif"  
29 - :show-upload-list="false"  
30 - list-type="picture-card"  
31 - class="avatar-uploader"  
32 - :customRequest="customUpload"  
33 - :before-upload="beforeUpload"  
34 - >  
35 - <img v-if="devicePic" :src="devicePic" alt="avatar" />  
36 - <div v-else>  
37 - <LoadingOutlined v-if="loading" />  
38 - <PlusOutlined v-else />  
39 - <div class="ant-upload-text">图片上传</div>  
40 - </div>  
41 - </Upload>  
42 - </template>  
43 <template #snCode="{ model, field }"> 25 <template #snCode="{ model, field }">
44 <div class="flex"> 26 <div class="flex">
45 <Input v-model:value="model[field]" placeholder="请输入设备名称" /> 27 <Input v-model:value="model[field]" placeholder="请输入设备名称" />
@@ -115,10 +97,9 @@ @@ -115,10 +97,9 @@
115 import { BasicForm, useForm } from '/@/components/Form'; 97 import { BasicForm, useForm } from '/@/components/Form';
116 import { step1Schemas } from '../../config/data'; 98 import { step1Schemas } from '../../config/data';
117 import { useScript } from '/@/hooks/web/useScript'; 99 import { useScript } from '/@/hooks/web/useScript';
118 - import { Input, Upload, message, Modal, Form, Row, Col, AutoComplete } from 'ant-design-vue';  
119 - import { EnvironmentTwoTone, PlusOutlined, LoadingOutlined } from '@ant-design/icons-vue'; 100 + import { Input, message, Modal, Form, Row, Col, AutoComplete } from 'ant-design-vue';
  101 + import { EnvironmentTwoTone } from '@ant-design/icons-vue';
120 import { upload } from '/@/api/oss/ossFileUploader'; 102 import { upload } from '/@/api/oss/ossFileUploader';
121 - import { FileItem } from '/@/components/Upload/src/typing';  
122 import { BAI_DU_MAP_URL } from '/@/utils/fnUtils'; 103 import { BAI_DU_MAP_URL } from '/@/utils/fnUtils';
123 import { generateSNCode } from '/@/api/device/deviceManager'; 104 import { generateSNCode } from '/@/api/device/deviceManager';
124 import icon from '/@/assets/images/wz.png'; 105 import icon from '/@/assets/images/wz.png';
@@ -130,21 +111,20 @@ @@ -130,21 +111,20 @@
130 import DeptDrawer from '/@/views/system/organization/OrganizationDrawer.vue'; 111 import DeptDrawer from '/@/views/system/organization/OrganizationDrawer.vue';
131 import { TaskTypeEnum } from '/@/views/task/center/config'; 112 import { TaskTypeEnum } from '/@/views/task/center/config';
132 import { toRaw } from 'vue'; 113 import { toRaw } from 'vue';
  114 + import { FileItem } from '/@/components/Form/src/components/ApiUpload.vue';
  115 + import { buildUUID } from '/@/utils/uuid';
133 116
134 export default defineComponent({ 117 export default defineComponent({
135 components: { 118 components: {
136 BasicForm, 119 BasicForm,
137 Input, 120 Input,
138 AutoComplete, 121 AutoComplete,
139 - Upload,  
140 EnvironmentTwoTone, 122 EnvironmentTwoTone,
141 - PlusOutlined,  
142 Modal, 123 Modal,
143 Form, 124 Form,
144 FormItem: Form.Item, 125 FormItem: Form.Item,
145 Row, 126 Row,
146 Col, 127 Col,
147 - LoadingOutlined,  
148 DeptDrawer, 128 DeptDrawer,
149 }, 129 },
150 props: { 130 props: {
@@ -202,8 +182,11 @@ @@ -202,8 +182,11 @@
202 async function nextStep() { 182 async function nextStep() {
203 try { 183 try {
204 let values = await validate(); 184 let values = await validate();
205 - values = { devicePic: devicePic.value, ...positionState, ...values };  
206 - delete values.icon; 185 + if (Reflect.has(values, 'icon')) {
  186 + const file = (unref(values.icon) || []).at(0) || {};
  187 + values.icon = file.url || null;
  188 + }
  189 + values = { ...positionState, ...values };
207 delete values.deviceAddress; 190 delete values.deviceAddress;
208 emit('next', values); 191 emit('next', values);
209 // 获取输入的数据 192 // 获取输入的数据
@@ -405,6 +388,11 @@ @@ -405,6 +388,11 @@
405 positionState.address = deviceInfo.address; 388 positionState.address = deviceInfo.address;
406 devicePositionState.value = { ...toRaw(positionState) }; 389 devicePositionState.value = { ...toRaw(positionState) };
407 devicePic.value = deviceInfo.avatar; 390 devicePic.value = deviceInfo.avatar;
  391 + if (deviceInfo.avatar) {
  392 + setFieldsValue({
  393 + icon: [{ uid: buildUUID(), name: 'name', url: deviceInfo.avatar } as FileItem],
  394 + });
  395 + }
408 setFieldsValue({ 396 setFieldsValue({
409 ...data, 397 ...data,
410 code: data?.code, 398 code: data?.code,
@@ -414,6 +402,10 @@ @@ -414,6 +402,10 @@
414 // 父组件调用获取字段值的方法 402 // 父组件调用获取字段值的方法
415 function parentGetFieldsValue() { 403 function parentGetFieldsValue() {
416 const value = getFieldsValue(); 404 const value = getFieldsValue();
  405 + if (Reflect.has(value, 'icon')) {
  406 + const file = (value.icon || []).at(0) || {};
  407 + value.icon = file.url || null;
  408 + }
417 return { 409 return {
418 ...value, 410 ...value,
419 ...(value?.code || value?.addressCode 411 ...(value?.code || value?.addressCode
@@ -47,7 +47,7 @@ @@ -47,7 +47,7 @@
47 </div> 47 </div>
48 </BasicModal> 48 </BasicModal>
49 </div> 49 </div>
50 - <Description @register="register" class="mt-4" :data="deviceDetail" /> 50 + <Description @register="register" class="mt-4" :data="deviceDetail" :contentStyle="CS" />
51 </div> 51 </div>
52 <div class="mt-4" v-if="!isCustomer"> 52 <div class="mt-4" v-if="!isCustomer">
53 <a-button type="primary" class="mr-4" @click="copyTbDeviceId">复制设备ID</a-button> 53 <a-button type="primary" class="mr-4" @click="copyTbDeviceId">复制设备ID</a-button>
@@ -116,6 +116,15 @@ @@ -116,6 +116,15 @@
116 column: 2, 116 column: 2,
117 }); 117 });
118 118
  119 + const CS = {
  120 + 'max-width': '600px',
  121 + 'word-break': 'break-all',
  122 + overflow: 'hidden',
  123 + display: '-webkit-box',
  124 + '-webkit-line-clamp': 2,
  125 + '-webkit-box-orient': 'vertical',
  126 + };
  127 +
119 // 地图 128 // 地图
120 const mapWrapRef = ref<HTMLDivElement>(); 129 const mapWrapRef = ref<HTMLDivElement>();
121 130
@@ -217,6 +226,7 @@ @@ -217,6 +226,7 @@
217 remoteConnectiondGateway, 226 remoteConnectiondGateway,
218 locationImage, 227 locationImage,
219 isCustomer, 228 isCustomer,
  229 + CS,
220 }; 230 };
221 }, 231 },
222 }); 232 });
@@ -25,7 +25,7 @@ @@ -25,7 +25,7 @@
25 listField: 'data', 25 listField: 'data',
26 }, 26 },
27 formConfig: { 27 formConfig: {
28 - labelWidth: 120, 28 + labelWidth: 80,
29 schemas: formSchemas, 29 schemas: formSchemas,
30 fieldMapToTime: [['dateRange', ['startTime', 'endTime'], 'YYYY-MM-DD HH:mm:ss']], 30 fieldMapToTime: [['dateRange', ['startTime', 'endTime'], 'YYYY-MM-DD HH:mm:ss']],
31 }, 31 },
@@ -61,6 +61,7 @@ @@ -61,6 +61,7 @@
61 </span> 61 </span>
62 </template> 62 </template>
63 </BasicTable> 63 </BasicTable>
  64 + <BasicTable />
64 <BasicModal title="输出参数" @register="registerModal" @ok="closeModal"> 65 <BasicModal title="输出参数" @register="registerModal" @ok="closeModal">
65 <Input.TextArea v-model:value="outputData" :autosize="true" /> 66 <Input.TextArea v-model:value="outputData" :autosize="true" />
66 </BasicModal> 67 </BasicModal>
@@ -15,7 +15,7 @@ @@ -15,7 +15,7 @@
15 import { getDeviceAttrs } from '/@/api/device/deviceManager'; 15 import { getDeviceAttrs } from '/@/api/device/deviceManager';
16 import { DeviceModelOfMatterAttrs, DeviceRecord } from '/@/api/device/model/deviceModel'; 16 import { DeviceModelOfMatterAttrs, DeviceRecord } from '/@/api/device/model/deviceModel';
17 import { Specs, StructJSON } from '/@/api/device/model/modelOfMatterModel'; 17 import { Specs, StructJSON } from '/@/api/device/model/modelOfMatterModel';
18 - import { isArray, isObject } from '/@/utils/is'; 18 + import { isArray, isNull, isObject } from '/@/utils/is';
19 import { DataTypeEnum } from '/@/components/Form/src/externalCompns/components/StructForm/config'; 19 import { DataTypeEnum } from '/@/components/Form/src/externalCompns/components/StructForm/config';
20 import { useGlobSetting } from '/@/hooks/setting'; 20 import { useGlobSetting } from '/@/hooks/setting';
21 import { ModeSwitchButton, EnumTableCardMode } from '/@/components/Widget'; 21 import { ModeSwitchButton, EnumTableCardMode } from '/@/components/Widget';
@@ -161,14 +161,14 @@ @@ -161,14 +161,14 @@
161 161
162 handleFilterChange(); 162 handleFilterChange();
163 163
164 - unref(mode) === EnumTableCardMode.TABLE && setTableData(socketInfo.dataSource); 164 + unref(mode) === EnumTableCardMode.TABLE && setTableModeData();
165 } catch (error) {} 165 } catch (error) {}
166 }, 166 },
167 resetFunc: async () => { 167 resetFunc: async () => {
168 try { 168 try {
169 socketInfo.filterAttrKeys = []; 169 socketInfo.filterAttrKeys = [];
170 handleFilterChange(); 170 handleFilterChange();
171 - unref(mode) === EnumTableCardMode.TABLE && setTableData(socketInfo.dataSource); 171 + unref(mode) === EnumTableCardMode.TABLE && setTableModeData();
172 } catch (error) {} 172 } catch (error) {}
173 }, 173 },
174 }); 174 });
@@ -200,7 +200,7 @@ @@ -200,7 +200,7 @@
200 const switchMode = async (value: EnumTableCardMode) => { 200 const switchMode = async (value: EnumTableCardMode) => {
201 mode.value = value; 201 mode.value = value;
202 await nextTick(); 202 await nextTick();
203 - unref(mode) === EnumTableCardMode.TABLE && setTableData(socketInfo.dataSource); 203 + unref(mode) === EnumTableCardMode.TABLE && setTableModeData();
204 socketInfo.filterAttrKeys = []; 204 socketInfo.filterAttrKeys = [];
205 }; 205 };
206 206
@@ -280,7 +280,7 @@ @@ -280,7 +280,7 @@
280 280
281 await nextTick(); 281 await nextTick();
282 282
283 - unref(mode) === EnumTableCardMode.TABLE && setTableData(socketInfo.dataSource); 283 + unref(mode) === EnumTableCardMode.TABLE && setTableModeData();
284 } 284 }
285 } catch (error) {} 285 } catch (error) {}
286 }, 286 },
@@ -292,6 +292,10 @@ @@ -292,6 +292,10 @@
292 }, 292 },
293 }); 293 });
294 294
  295 + function setTableModeData() {
  296 + setTableData(socketInfo.dataSource.filter((item) => !isNull(item.value)));
  297 + }
  298 +
295 const handleShowDetail = (record: DataSource) => { 299 const handleShowDetail = (record: DataSource) => {
296 const { key } = record; 300 const { key } = record;
297 socketInfo.attr = key; 301 socketInfo.attr = key;
@@ -308,9 +312,11 @@ @@ -308,9 +312,11 @@
308 312
309 const formatValue = (item: DataSource) => { 313 const formatValue = (item: DataSource) => {
310 return item.type === DataTypeEnum.IS_BOOL 314 return item.type === DataTypeEnum.IS_BOOL
311 - ? !!Number(item.value)  
312 - ? item.boolOpen  
313 - : item.boolClose 315 + ? !isNull(item.value)
  316 + ? !!Number(item.value)
  317 + ? item.boolOpen
  318 + : item.boolClose
  319 + : '--'
314 : item.value || '--'; 320 : item.value || '--';
315 }; 321 };
316 322
@@ -406,7 +412,11 @@ @@ -406,7 +412,11 @@
406 </List> 412 </List>
407 </section> 413 </section>
408 414
409 - <BasicTable v-if="mode === EnumTableCardMode.TABLE" @register="registerTable"> 415 + <BasicTable
  416 + v-if="mode === EnumTableCardMode.TABLE"
  417 + @register="registerTable"
  418 + class="device-things-model-table-mode"
  419 + >
410 <template #toolbar> 420 <template #toolbar>
411 <div 421 <div
412 v-show="mode === EnumTableCardMode.TABLE" 422 v-show="mode === EnumTableCardMode.TABLE"
@@ -447,6 +457,10 @@ @@ -447,6 +457,10 @@
447 display: flex; 457 display: flex;
448 align-items: center; 458 align-items: center;
449 } 459 }
  460 +
  461 + .device-things-model-table-mode:deep(.ant-table-placeholder) {
  462 + height: auto;
  463 + }
450 </style> 464 </style>
451 465
452 <style> 466 <style>
@@ -117,7 +117,7 @@ @@ -117,7 +117,7 @@
117 }; 117 };
118 118
119 const handleCheckCard = (item: ProfileRecord) => { 119 const handleCheckCard = (item: ProfileRecord) => {
120 - if (item.default) return; 120 + if (item.default || item.name == 'default') return;
121 item.checked = !item.checked; 121 item.checked = !item.checked;
122 }; 122 };
123 123
@@ -12,6 +12,9 @@ import { EventType, EventTypeColor, EventTypeName } from '../list/cpns/tabs/Even @@ -12,6 +12,9 @@ import { EventType, EventTypeColor, EventTypeName } from '../list/cpns/tabs/Even
12 12
13 import { useClipboard } from '@vueuse/core'; 13 import { useClipboard } from '@vueuse/core';
14 import { useMessage } from '/@/hooks/web/useMessage'; 14 import { useMessage } from '/@/hooks/web/useMessage';
  15 +import { FileItem } from '/@/components/Form/src/components/ApiUpload.vue';
  16 +import { createImgPreview } from '/@/components/Preview';
  17 +import { uploadThumbnail } from '/@/api/configuration/center/configurationCenter';
15 18
16 export enum Mode { 19 export enum Mode {
17 CARD = 'card', 20 CARD = 'card',
@@ -78,11 +81,13 @@ export const physicalColumn: BasicColumn[] = [ @@ -78,11 +81,13 @@ export const physicalColumn: BasicColumn[] = [
78 title: '功能名称', 81 title: '功能名称',
79 dataIndex: FormField.FUNCTION_NAME, 82 dataIndex: FormField.FUNCTION_NAME,
80 width: 90, 83 width: 90,
  84 + ellipsis: true,
81 }, 85 },
82 { 86 {
83 title: '标识符', 87 title: '标识符',
84 dataIndex: FormField.IDENTIFIER, 88 dataIndex: FormField.IDENTIFIER,
85 width: 90, 89 width: 90,
  90 + ellipsis: true,
86 customRender: ({ text }: Record<'text', string>) => { 91 customRender: ({ text }: Record<'text', string>) => {
87 return h(Tooltip, { title: text }, () => 92 return h(Tooltip, { title: text }, () =>
88 h('span', { class: 'cursor-pointer', onClick: () => handleCopy(text) }, text) 93 h('span', { class: 'cursor-pointer', onClick: () => handleCopy(text) }, text)
@@ -96,6 +101,7 @@ export const physicalColumn: BasicColumn[] = [ @@ -96,6 +101,7 @@ export const physicalColumn: BasicColumn[] = [
96 format: (text: string) => { 101 format: (text: string) => {
97 return text || '--'; 102 return text || '--';
98 }, 103 },
  104 + ellipsis: true,
99 }, 105 },
100 { 106 {
101 title: '事件类型', 107 title: '事件类型',
@@ -109,6 +115,7 @@ export const physicalColumn: BasicColumn[] = [ @@ -109,6 +115,7 @@ export const physicalColumn: BasicColumn[] = [
109 () => EventTypeName[text as EventType] 115 () => EventTypeName[text as EventType]
110 ); 116 );
111 }, 117 },
  118 + ellipsis: true,
112 }, 119 },
113 { 120 {
114 title: '状态', 121 title: '状态',
@@ -158,8 +165,33 @@ export const step1Schemas: FormSchema[] = [ @@ -158,8 +165,33 @@ export const step1Schemas: FormSchema[] = [
158 { 165 {
159 field: 'image', 166 field: 'image',
160 label: '上传图片', 167 label: '上传图片',
161 - component: 'Input',  
162 - slot: 'imageSelect', 168 + component: 'ApiUpload',
  169 + changeEvent: 'update:fileList',
  170 + valueField: 'fileList',
  171 + componentProps: () => {
  172 + return {
  173 + listType: 'picture-card',
  174 + maxFileLimit: 1,
  175 + accept: '.png,.jpg,.jpeg,.gif',
  176 + api: async (file: File) => {
  177 + try {
  178 + const formData = new FormData();
  179 + formData.set('file', file);
  180 + const { fileStaticUri, fileName } = await uploadThumbnail(formData);
  181 + return {
  182 + uid: fileStaticUri,
  183 + name: fileName,
  184 + url: fileStaticUri,
  185 + } as FileItem;
  186 + } catch (error) {
  187 + return {};
  188 + }
  189 + },
  190 + onPreview: (fileList: FileItem) => {
  191 + createImgPreview({ imageList: [fileList.url!] });
  192 + },
  193 + };
  194 + },
163 }, 195 },
164 { 196 {
165 field: 'deviceType', 197 field: 'deviceType',
1 <template> 1 <template>
2 <div class="step1"> 2 <div class="step1">
3 - <BasicForm @register="register">  
4 - <template #imageSelect>  
5 - <Upload  
6 - style="width: 20vw"  
7 - name="avatar"  
8 - accept=".png,.jpg,.jpeg,.gif"  
9 - list-type="picture-card"  
10 - class="avatar-uploader"  
11 - :show-upload-list="false"  
12 - :customRequest="customUploadqrcodePic"  
13 - :before-upload="beforeUploadqrcodePic"  
14 - >  
15 - <img  
16 - v-if="deviceConfigPic"  
17 - :src="deviceConfigPic"  
18 - alt=""  
19 - style="width: 6.25rem; height: 6.25rem"  
20 - />  
21 - <div v-else>  
22 - <LoadingOutlined v-if="loading" />  
23 - <PlusOutlined v-else />  
24 - <div class="ant-upload-text">图片上传</div>  
25 - </div>  
26 - </Upload>  
27 - </template>  
28 - </BasicForm> 3 + <BasicForm @register="register" />
29 </div> 4 </div>
30 </template> 5 </template>
31 <script lang="ts" setup> 6 <script lang="ts" setup>
32 - import { ref, nextTick } from 'vue'; 7 + import { nextTick } from 'vue';
33 import { BasicForm, useForm } from '/@/components/Form'; 8 import { BasicForm, useForm } from '/@/components/Form';
34 import { step1Schemas } from '../device.profile.data'; 9 import { step1Schemas } from '../device.profile.data';
35 - import { uploadApi } from '/@/api/personal/index';  
36 - import { Upload } from 'ant-design-vue';  
37 - import { PlusOutlined, LoadingOutlined } from '@ant-design/icons-vue';  
38 - import { useMessage } from '/@/hooks/web/useMessage';  
39 - import type { FileItem } from '/@/components/Upload/src/typing'; 10 + import { FileItem } from '/@/components/Form/src/components/ApiUpload.vue';
  11 + import { buildUUID } from '/@/utils/uuid';
40 12
41 const emits = defineEmits(['next', 'emitDeviceType']); 13 const emits = defineEmits(['next', 'emitDeviceType']);
42 - const loading = ref(false);  
43 - const { createMessage } = useMessage();  
44 - const deviceConfigPic = ref('');  
45 const props = defineProps({ 14 const props = defineProps({
46 ifShowBtn: { type: Boolean, default: true }, 15 ifShowBtn: { type: Boolean, default: true },
47 }); 16 });
@@ -66,32 +35,6 @@ @@ -66,32 +35,6 @@
66 disabled: nameStatus, 35 disabled: nameStatus,
67 }, 36 },
68 }); 37 });
69 - const customUploadqrcodePic = async ({ file }) => {  
70 - if (beforeUploadqrcodePic(file)) {  
71 - deviceConfigPic.value = '';  
72 - loading.value = true;  
73 - const formData = new FormData();  
74 - formData.append('file', file);  
75 - const response = await uploadApi(formData);  
76 - if (response.fileStaticUri) {  
77 - deviceConfigPic.value = response.fileStaticUri;  
78 - loading.value = false;  
79 - }  
80 - }  
81 - };  
82 - const beforeUploadqrcodePic = (file: FileItem) => {  
83 - const isJpgOrPng =  
84 - file.type === 'image/jpeg' || file.type === 'image/png' || file.type === 'image/jpg';  
85 - if (!isJpgOrPng) {  
86 - createMessage.error('只能上传图片文件!');  
87 - }  
88 - const isLt2M = (file.size as number) / 1024 / 1024 < 5;  
89 - if (!isLt2M) {  
90 - createMessage.error('图片大小不能超过5MB!');  
91 - }  
92 - return isJpgOrPng && isLt2M;  
93 - };  
94 -  
95 const setFieldsdefaultRuleChainId = async (id) => { 38 const setFieldsdefaultRuleChainId = async (id) => {
96 await nextTick(); 39 await nextTick();
97 setFieldsValue({ defaultRuleChainId: id }); 40 setFieldsValue({ defaultRuleChainId: id });
@@ -105,20 +48,28 @@ @@ -105,20 +48,28 @@
105 } 48 }
106 //回显数据 49 //回显数据
107 const setFormData = (v) => { 50 const setFormData = (v) => {
108 - setFieldsValue(v);  
109 - deviceConfigPic.value = v.image; 51 + if (v.image) {
  52 + setFieldsValue({
  53 + image: [{ uid: buildUUID(), name: 'name', url: v.image } as FileItem],
  54 + });
  55 + }
  56 + const { image, ...params } = v;
  57 + console.log(image);
  58 + setFieldsValue({ ...params });
110 }; 59 };
111 //获取数据 60 //获取数据
112 async function getFormData() { 61 async function getFormData() {
113 const values = await validate(); 62 const values = await validate();
114 if (!values) return; 63 if (!values) return;
115 - Reflect.set(values, 'image', deviceConfigPic.value); 64 + if (Reflect.has(values, 'image')) {
  65 + const file = (values.image || []).at(0) || {};
  66 + values.image = file.url || null;
  67 + }
116 return values; 68 return values;
117 } 69 }
118 //清空数据 70 //清空数据
119 const resetFormData = () => { 71 const resetFormData = () => {
120 resetFields(); 72 resetFields();
121 - deviceConfigPic.value = '';  
122 }; 73 };
123 74
124 const editOrAddDeviceTypeStatus = (status: boolean) => { 75 const editOrAddDeviceTypeStatus = (status: boolean) => {
@@ -77,7 +77,7 @@ export const serviceSchemas = (tcpDeviceFlag: boolean): FormSchema[] => { @@ -77,7 +77,7 @@ export const serviceSchemas = (tcpDeviceFlag: boolean): FormSchema[] => {
77 span: 18, 77 span: 18,
78 }, 78 },
79 componentProps: { 79 componentProps: {
80 - maxLength: 255, 80 + maxLength: 32,
81 placeholder: '请输入功能名称', 81 placeholder: '请输入功能名称',
82 }, 82 },
83 }, 83 },
@@ -90,7 +90,7 @@ export const serviceSchemas = (tcpDeviceFlag: boolean): FormSchema[] => { @@ -90,7 +90,7 @@ export const serviceSchemas = (tcpDeviceFlag: boolean): FormSchema[] => {
90 span: 18, 90 span: 18,
91 }, 91 },
92 componentProps: { 92 componentProps: {
93 - maxLength: 255, 93 + maxLength: 128,
94 placeholder: '请输入标识符', 94 placeholder: '请输入标识符',
95 }, 95 },
96 }, 96 },
@@ -175,7 +175,7 @@ export const eventSchemas: FormSchema[] = [ @@ -175,7 +175,7 @@ export const eventSchemas: FormSchema[] = [
175 span: 18, 175 span: 18,
176 }, 176 },
177 componentProps: { 177 componentProps: {
178 - maxLength: 255, 178 + maxLength: 32,
179 placeholder: '请输入功能名称', 179 placeholder: '请输入功能名称',
180 }, 180 },
181 }, 181 },
@@ -188,7 +188,7 @@ export const eventSchemas: FormSchema[] = [ @@ -188,7 +188,7 @@ export const eventSchemas: FormSchema[] = [
188 span: 18, 188 span: 18,
189 }, 189 },
190 componentProps: { 190 componentProps: {
191 - maxLength: 255, 191 + maxLength: 128,
192 placeholder: '请输入标识符', 192 placeholder: '请输入标识符',
193 }, 193 },
194 }, 194 },
@@ -17,7 +17,7 @@ @@ -17,7 +17,7 @@
17 </Authority> 17 </Authority>
18 </template> 18 </template>
19 <template #config="{ record }"> 19 <template #config="{ record }">
20 - <Authority value="api:yt:message:get:config"> 20 + <Authority value="api:yt:template:get">
21 <a-button type="link" class="ml-2" @click="showData(record)"> 查看配置 </a-button> 21 <a-button type="link" class="ml-2" @click="showData(record)"> 查看配置 </a-button>
22 </Authority> 22 </Authority>
23 </template> 23 </template>
@@ -78,6 +78,27 @@ @@ -78,6 +78,27 @@
78 componentProps: { 78 componentProps: {
79 placeholder: '示例:{"code":"3654"}', 79 placeholder: '示例:{"code":"3654"}',
80 }, 80 },
  81 + dynamicRules: () => {
  82 + return [
  83 + {
  84 + required: true,
  85 + validator: (_, value) => {
  86 + try {
  87 + if (typeof value == 'object') {
  88 + return Promise.resolve();
  89 + } else {
  90 + if (typeof JSON.parse(value) == 'object') {
  91 + return Promise.resolve();
  92 + }
  93 + return Promise.reject('请输入JSON格式例如{"code":"123"}');
  94 + }
  95 + } catch {
  96 + return Promise.reject('请输入JSON格式例如{"code":"123"}');
  97 + }
  98 + },
  99 + },
  100 + ];
  101 + },
81 }, 102 },
82 { 103 {
83 field: 'remark', 104 field: 'remark',
@@ -131,14 +131,19 @@ export const formSchema: FormSchema[] = [ @@ -131,14 +131,19 @@ export const formSchema: FormSchema[] = [
131 onChange: async (value) => { 131 onChange: async (value) => {
132 const res = await findMessageConfig({}); 132 const res = await findMessageConfig({});
133 let typeId: Nullable<string> = null; 133 let typeId: Nullable<string> = null;
134 - const options = res.map((item) => {  
135 - if (item.messageType === value) typeId = item.id;  
136 - return {  
137 - disabled: item.messageType !== value,  
138 - label: item.configName,  
139 - value: item.id,  
140 - };  
141 - }); 134 + const options = res
  135 + .map((item) => {
  136 + if (item.messageType === value && item.status === 1) {
  137 + typeId = item.id;
  138 + }
  139 + return {
  140 + disabled: item.messageType !== value,
  141 + label: item.configName,
  142 + value: item.id,
  143 + status: item.status,
  144 + };
  145 + })
  146 + .filter((item) => item.status === 1);
142 await formActionType.setFieldsValue({ messageConfigId: typeId }); 147 await formActionType.setFieldsValue({ messageConfigId: typeId });
143 await formActionType.updateSchema({ 148 await formActionType.updateSchema({
144 field: 'messageConfigId', 149 field: 'messageConfigId',
@@ -155,14 +160,25 @@ export const formSchema: FormSchema[] = [ @@ -155,14 +160,25 @@ export const formSchema: FormSchema[] = [
155 label: '配置名称', 160 label: '配置名称',
156 required: true, 161 required: true,
157 component: 'ApiSelect', 162 component: 'ApiSelect',
158 - componentProps: {  
159 - api: findMessageConfig,  
160 - params: {  
161 - messageType: ({ values }) => Reflect.get(values, 'messageType'),  
162 - },  
163 - immediate: true,  
164 - labelField: 'configName',  
165 - valueField: 'id', 163 + componentProps: () => {
  164 + return {
  165 + api: async (params: Recordable) => {
  166 + try {
  167 + const record = await findMessageConfig(params);
  168 + return record.filter((item) => item.status === 1);
  169 + } catch (error) {
  170 + console.log(error);
  171 + return [];
  172 + }
  173 + },
  174 + params: {
  175 + messageType: ({ values }) => Reflect.get(values, 'messageType'),
  176 + },
  177 + immediate: true,
  178 + labelField: 'configName',
  179 + valueField: 'id',
  180 + getPopupContainer: () => document.body,
  181 + };
166 }, 182 },
167 }, 183 },
168 { 184 {
@@ -9,12 +9,17 @@ @@ -9,12 +9,17 @@
9 @register="registerDrawer" 9 @register="registerDrawer"
10 @ok="handleSubmit" 10 @ok="handleSubmit"
11 @close="handleClose" 11 @close="handleClose"
  12 + wrapClassName="report-drawer"
12 > 13 >
13 <BasicForm @register="registerForm"> 14 <BasicForm @register="registerForm">
14 <!-- 设备选择 --> 15 <!-- 设备选择 -->
15 <template #devices="{ model }"> 16 <template #devices="{ model }">
16 <span class="hidden">{{ handleChangeOrg(model['organizationId']) }}</span> 17 <span class="hidden">{{ handleChangeOrg(model['organizationId']) }}</span>
17 - <SelectDevice ref="selectDeviceRef" :selectOptions="selectOptions" /> 18 + <SelectDevice
  19 + ref="selectDeviceRef"
  20 + :selectOptions="selectOptions"
  21 + v-model:disabled="deviceDisabled"
  22 + />
18 </template> 23 </template>
19 </BasicForm> 24 </BasicForm>
20 </BasicDrawer> 25 </BasicDrawer>
@@ -82,12 +87,14 @@ @@ -82,12 +87,14 @@
82 } 87 }
83 ); 88 );
84 89
85 - const [registerForm, { validate, resetFields, setFieldsValue, updateSchema }] = useForm({  
86 - labelWidth: 120,  
87 - schemas: formSchema,  
88 - showActionButtonGroup: false,  
89 - fieldMapToTime: [[SchemaFiled.DATE_RANGE, [SchemaFiled.START_TS, SchemaFiled.END_TS]]],  
90 - }); 90 + const [registerForm, { validate, resetFields, setFieldsValue, updateSchema, setProps }] = useForm(
  91 + {
  92 + labelWidth: 120,
  93 + schemas: formSchema,
  94 + showActionButtonGroup: false,
  95 + fieldMapToTime: [[SchemaFiled.DATE_RANGE, [SchemaFiled.START_TS, SchemaFiled.END_TS]]],
  96 + }
  97 + );
91 98
92 const businessText = ref(''); 99 const businessText = ref('');
93 100
@@ -95,11 +102,27 @@ @@ -95,11 +102,27 @@
95 data: {}, 102 data: {},
96 }); 103 });
97 104
  105 + const deviceDisabled = ref<boolean>(false);
  106 +
98 const [registerDrawer, { setDrawerProps, closeDrawer }] = useDrawerInner(async (data) => { 107 const [registerDrawer, { setDrawerProps, closeDrawer }] = useDrawerInner(async (data) => {
99 try { 108 try {
100 await nextTick(); 109 await nextTick();
101 handleClose(); 110 handleClose();
102 businessText.value = data.text; 111 businessText.value = data.text;
  112 + if (businessText.value == BusinessReportConfigTextEnum.BUSINESS_VIEW_TEXT) {
  113 + deviceDisabled.value = true;
  114 + setProps({ disabled: true });
  115 + updateSchema({
  116 + field: 'organizationId',
  117 + componentProps: { apiTreeSelectProps: { disabled: true } },
  118 + });
  119 + } else {
  120 + deviceDisabled.value = false;
  121 + updateSchema({
  122 + field: 'organizationId',
  123 + componentProps: { apiTreeSelectProps: { disabled: false } },
  124 + });
  125 + }
103 setFieldsValue(setDefaultTime()); 126 setFieldsValue(setDefaultTime());
104 updateSchema(disableCustomWeekly(BusinessExecutewayEnum.BUSINESS_EXECUTEWAY_IMMEDIATE)); 127 updateSchema(disableCustomWeekly(BusinessExecutewayEnum.BUSINESS_EXECUTEWAY_IMMEDIATE));
105 setDrawerProps(setPropsForModal(businessText.value)); 128 setDrawerProps(setPropsForModal(businessText.value));
@@ -177,3 +200,15 @@ @@ -177,3 +200,15 @@
177 selectDeviceRef.value?.resetValue(); 200 selectDeviceRef.value?.resetValue();
178 }; 201 };
179 </script> 202 </script>
  203 +
  204 +<style lang="less">
  205 + .report-drawer {
  206 + .ant-form-item-control-input-content {
  207 + > div {
  208 + > div {
  209 + @apply w-full;
  210 + }
  211 + }
  212 + }
  213 + }
  214 +</style>
1 <template> 1 <template>
2 - <div  
3 - class="flex"  
4 - v-for="param in dynamicInput.params"  
5 - :key="param.key"  
6 - style="margin-top: 1.25vh"  
7 - >  
8 - <a-input :disabled="true" v-model:value="param.name" style="width: 38%; margin-bottom: 5px" /> 2 + <div v-for="param in dynamicInput.params" :key="param.key" class="mt-4 flex gap-2">
  3 + <a-input :disabled="true" v-model:value="param.name" class="w-1/2 flex-1" />
9 <Select 4 <Select
10 placeholder="请选择设备属性" 5 placeholder="请选择设备属性"
11 v-model:value="param.attributes" 6 v-model:value="param.attributes"
12 - style="width: 38%; margin-left: 1.8vw" 7 + class="!w-1/2"
13 :options="selectOptions" 8 :options="selectOptions"
  9 + :disabled="disabled"
14 @change="emitChange" 10 @change="emitChange"
15 mode="multiple" 11 mode="multiple"
16 allowClear 12 allowClear
@@ -31,6 +27,10 @@ @@ -31,6 +27,10 @@
31 27
32 const props = defineProps({ 28 const props = defineProps({
33 value: propTypes.object.def({}), 29 value: propTypes.object.def({}),
  30 + disabled: {
  31 + type: Boolean,
  32 + required: false,
  33 + },
34 }); 34 });
35 35
36 const selectOptions: any = ref([]); 36 const selectOptions: any = ref([]);
@@ -4,12 +4,19 @@ @@ -4,12 +4,19 @@
4 v-model:value="selectValue" 4 v-model:value="selectValue"
5 style="width: 100%" 5 style="width: 100%"
6 :options="selectOptions" 6 :options="selectOptions"
  7 + v-bind="createPickerSearch()"
7 @change="handleDeviceChange" 8 @change="handleDeviceChange"
  9 + :disabled="disabled"
8 mode="multiple" 10 mode="multiple"
9 labelInValue 11 labelInValue
10 /> 12 />
11 <template v-for="(item, index) in deviceList" :key="item.value"> 13 <template v-for="(item, index) in deviceList" :key="item.value">
12 - <SelectAttributes :ref="bindDeviceRef.deviceAttrRef" :value="item" :index="index" /> 14 + <SelectAttributes
  15 + :ref="bindDeviceRef.deviceAttrRef"
  16 + :value="item"
  17 + :index="index"
  18 + :disabled="disabled"
  19 + />
13 </template> 20 </template>
14 </template> 21 </template>
15 <script lang="ts" setup name="SelectDevice"> 22 <script lang="ts" setup name="SelectDevice">
@@ -17,12 +24,17 @@ @@ -17,12 +24,17 @@
17 import { Select } from 'ant-design-vue'; 24 import { Select } from 'ant-design-vue';
18 import SelectAttributes from './SelectAttributes.vue'; 25 import SelectAttributes from './SelectAttributes.vue';
19 import { TDeviceList, TSelectOption } from '../type'; 26 import { TDeviceList, TSelectOption } from '../type';
  27 + import { createPickerSearch } from '/@/utils/pickerSearch';
20 28
21 const props = defineProps({ 29 const props = defineProps({
22 selectOptions: { 30 selectOptions: {
23 type: Array as PropType<TSelectOption[]>, 31 type: Array as PropType<TSelectOption[]>,
24 required: true, 32 required: true,
25 }, 33 },
  34 + disabled: {
  35 + type: Boolean,
  36 + required: false,
  37 + },
26 }); 38 });
27 39
28 const selectValue = ref([]); 40 const selectValue = ref([]);
@@ -465,14 +465,15 @@ export const formSchema: BFormSchema[] = [ @@ -465,14 +465,15 @@ export const formSchema: BFormSchema[] = [
465 }, 465 },
466 { 466 {
467 field: 'dateGroupGap', 467 field: 'dateGroupGap',
468 - label: '分组间隔', 468 + label: '间隔时间',
469 component: 'Select', 469 component: 'Select',
470 colProps: { span: 24 }, 470 colProps: { span: 24 },
471 - dynamicRules: ({ model }) => { 471 + dynamicRules: () => {
472 return [ 472 return [
473 { 473 {
474 - required: model[SchemaFiled.AGG] !== AggregateDataEnum.NONE,  
475 - message: '分组间隔为必填项', 474 + // required: model[SchemaFiled.AGG] !== AggregateDataEnum.NONE,
  475 + required: true,
  476 + message: '间隔时间为必填项',
476 type: 'number', 477 type: 'number',
477 }, 478 },
478 ]; 479 ];
@@ -41,7 +41,6 @@ @@ -41,7 +41,6 @@
41 const [register, { setModalProps, closeModal }] = useModalInner(async (data) => { 41 const [register, { setModalProps, closeModal }] = useModalInner(async (data) => {
42 setModalProps({ loading: true }); 42 setModalProps({ loading: true });
43 const { text, record } = data || {}; 43 const { text, record } = data || {};
44 - console.log(data, 'data');  
45 isUpdate.value = record ? true : false; 44 isUpdate.value = record ? true : false;
46 ruleTile.value = text; 45 ruleTile.value = text;
47 const { name, additionalInfo, debugMode } = record || {}; 46 const { name, additionalInfo, debugMode } = record || {};
@@ -31,7 +31,6 @@ export const searchFormSchema: FormSchema[] = [ @@ -31,7 +31,6 @@ export const searchFormSchema: FormSchema[] = [
31 field: 'textSearch', 31 field: 'textSearch',
32 label: '名称', 32 label: '名称',
33 component: 'Input', 33 component: 'Input',
34 -  
35 colProps: { span: 8 }, 34 colProps: { span: 8 },
36 componentProps: { 35 componentProps: {
37 placeholder: '请输入名称', 36 placeholder: '请输入名称',
@@ -30,11 +30,6 @@ @@ -30,11 +30,6 @@
30 icon: 'ant-design:eye-outlined', 30 icon: 'ant-design:eye-outlined',
31 onClick: handleView.bind(null, record), 31 onClick: handleView.bind(null, record),
32 }, 32 },
33 - // {  
34 - // label: '详情',  
35 - // icon: 'ant-design:field-time-outlined',  
36 - // onClick: handleDetail.bind(null, record),  
37 - // },  
38 { 33 {
39 label: '编辑', 34 label: '编辑',
40 icon: 'clarity:note-edit-line', 35 icon: 'clarity:note-edit-line',
@@ -103,9 +98,6 @@ @@ -103,9 +98,6 @@
103 import { useRouter } from 'vue-router'; 98 import { useRouter } from 'vue-router';
104 import { ref } from 'vue'; 99 import { ref } from 'vue';
105 import { isObject } from '/@/utils/is'; 100 import { isObject } from '/@/utils/is';
106 - // import { ChainDetailDrawer } from './chainDetail/index';  
107 - // import { useDrawer } from '/@/components/Drawer';  
108 -  
109 const [registerModal, { openModal }] = useModal(); 101 const [registerModal, { openModal }] = useModal();
110 const { createMessage } = useMessage(); 102 const { createMessage } = useMessage();
111 const { hasPermission } = usePermission(); 103 const { hasPermission } = usePermission();
@@ -4,18 +4,21 @@ @@ -4,18 +4,21 @@
4 <template #uploadFilesSlot="{ model }"> 4 <template #uploadFilesSlot="{ model }">
5 <UploadFile 5 <UploadFile
6 :url="credentialsFile.caCertFileName" 6 :url="credentialsFile.caCertFileName"
  7 + :disabled="disabled"
7 @fileUrlEmit="handleFileUrlEmitH" 8 @fileUrlEmit="handleFileUrlEmitH"
8 v-show="model['type'] === CredentialsEnum.IS_PEM" 9 v-show="model['type'] === CredentialsEnum.IS_PEM"
9 /> 10 />
10 <div class="h-4"></div> 11 <div class="h-4"></div>
11 <UploadFile 12 <UploadFile
12 :url="credentialsFile.certFileName" 13 :url="credentialsFile.certFileName"
  14 + :disabled="disabled"
13 @fileUrlEmit="handleFileUrlEmitC" 15 @fileUrlEmit="handleFileUrlEmitC"
14 v-show="model['type'] === CredentialsEnum.IS_PEM" 16 v-show="model['type'] === CredentialsEnum.IS_PEM"
15 /> 17 />
16 <div class="h-4"></div> 18 <div class="h-4"></div>
17 <UploadFile 19 <UploadFile
18 :url="credentialsFile.privateKeyFileName" 20 :url="credentialsFile.privateKeyFileName"
  21 + :disabled="disabled"
19 @fileUrlEmit="handleFileUrlEmitB" 22 @fileUrlEmit="handleFileUrlEmitB"
20 v-show="model['type'] === CredentialsEnum.IS_PEM" 23 v-show="model['type'] === CredentialsEnum.IS_PEM"
21 /> </template 24 /> </template
@@ -23,7 +26,7 @@ @@ -23,7 +26,7 @@
23 </div> 26 </div>
24 </template> 27 </template>
25 <script lang="ts" setup name="DataFlowMethodIsApi"> 28 <script lang="ts" setup name="DataFlowMethodIsApi">
26 - import { reactive } from 'vue'; 29 + import { reactive, ref } from 'vue';
27 import { BasicForm, useForm } from '/@/components/Form'; 30 import { BasicForm, useForm } from '/@/components/Form';
28 import { modeApiForm } from './config'; 31 import { modeApiForm } from './config';
29 import { modelFormPublicConfig } from '../../../dataflowmodal/config'; 32 import { modelFormPublicConfig } from '../../../dataflowmodal/config';
@@ -36,10 +39,11 @@ @@ -36,10 +39,11 @@
36 privateKeyFileName: undefined, 39 privateKeyFileName: undefined,
37 }); 40 });
38 41
39 - const [register, { validateFields, setFieldsValue, resetFields }] = useForm({  
40 - schemas: modeApiForm,  
41 - ...modelFormPublicConfig,  
42 - }); 42 + const [register, { validateFields, setFieldsValue, resetFields, setProps, updateSchema }] =
  43 + useForm({
  44 + schemas: modeApiForm,
  45 + ...modelFormPublicConfig,
  46 + });
43 47
44 const handleFileUrlEmitH = (fileUrl) => (credentialsFile.caCertFileName = fileUrl); 48 const handleFileUrlEmitH = (fileUrl) => (credentialsFile.caCertFileName = fileUrl);
45 49
@@ -78,10 +82,24 @@ @@ -78,10 +82,24 @@
78 }; 82 };
79 83
80 const resetValue = () => resetFields(); 84 const resetValue = () => resetFields();
  85 +
  86 + const disabled = ref<boolean>(false);
  87 + const setDisabledProps = (value) => {
  88 + setProps(value);
  89 + disabled.value = true;
  90 + updateSchema({ field: 'headers', componentProps: { disabled: false } });
  91 + };
  92 +
  93 + const setCancelDisabled = () => {
  94 + updateSchema({ field: 'headers', componentProps: { disabled: false } });
  95 + };
  96 +
81 defineExpose({ 97 defineExpose({
82 getValue, 98 getValue,
83 setValue, 99 setValue,
84 resetValue, 100 resetValue,
  101 + setDisabledProps,
  102 + setCancelDisabled,
85 }); 103 });
86 </script> 104 </script>
87 105
@@ -29,7 +29,7 @@ class ApiFormPartialConfig { @@ -29,7 +29,7 @@ class ApiFormPartialConfig {
29 return [ 29 return [
30 { label: 'Anonymous', value: 'anonymous' }, 30 { label: 'Anonymous', value: 'anonymous' },
31 { label: 'Basic', value: 'basic' }, 31 { label: 'Basic', value: 'basic' },
32 - { label: 'PEM', value: 'pem' }, 32 + { label: 'PEM', value: 'cert.PEM' },
33 ]; 33 ];
34 } 34 }
35 } 35 }
@@ -8,7 +8,7 @@ @@ -8,7 +8,7 @@
8 import { modelKafkaForm } from './config'; 8 import { modelKafkaForm } from './config';
9 import { modelFormPublicConfig } from '../../../dataflowmodal/config'; 9 import { modelFormPublicConfig } from '../../../dataflowmodal/config';
10 10
11 - const [register, { validate, setFieldsValue, resetFields }] = useForm({ 11 + const [register, { validate, setFieldsValue, resetFields, setProps, updateSchema }] = useForm({
12 schemas: modelKafkaForm, 12 schemas: modelKafkaForm,
13 ...modelFormPublicConfig, 13 ...modelFormPublicConfig,
14 }); 14 });
@@ -19,13 +19,28 @@ @@ -19,13 +19,28 @@
19 return values; 19 return values;
20 }; 20 };
21 21
22 - const setValue = (value) => setFieldsValue(value); 22 + const setValue = (value) => {
  23 + setFieldsValue(value);
  24 + };
  25 +
  26 + // 禁用表单
  27 + const setDisabledProps = (value) => {
  28 + setProps(value);
  29 + updateSchema({ field: 'otherProperties', componentProps: { disabled: true } });
  30 + };
  31 +
  32 + // 取消禁用
  33 + const setCancelDisabled = () => {
  34 + updateSchema({ field: 'otherProperties', componentProps: { disabled: false } });
  35 + };
23 36
24 const resetValue = () => resetFields(); 37 const resetValue = () => resetFields();
25 defineExpose({ 38 defineExpose({
26 getValue, 39 getValue,
27 setValue, 40 setValue,
28 resetValue, 41 resetValue,
  42 + setDisabledProps,
  43 + setCancelDisabled,
29 }); 44 });
30 </script> 45 </script>
31 46
@@ -4,18 +4,21 @@ @@ -4,18 +4,21 @@
4 <template #uploadFilesSlot="{ model }"> 4 <template #uploadFilesSlot="{ model }">
5 <UploadFile 5 <UploadFile
6 :url="credentialsFile.caCertFileName" 6 :url="credentialsFile.caCertFileName"
  7 + :disabled="disabled"
7 @fileUrlEmit="handleFileUrlEmitH" 8 @fileUrlEmit="handleFileUrlEmitH"
8 v-show="model['type'] === CredentialsEnum.IS_PEM" 9 v-show="model['type'] === CredentialsEnum.IS_PEM"
9 /> 10 />
10 <div class="h-4"></div> 11 <div class="h-4"></div>
11 <UploadFile 12 <UploadFile
12 :url="credentialsFile.certFileName" 13 :url="credentialsFile.certFileName"
  14 + :disabled="disabled"
13 @fileUrlEmit="handleFileUrlEmitC" 15 @fileUrlEmit="handleFileUrlEmitC"
14 v-show="model['type'] === CredentialsEnum.IS_PEM" 16 v-show="model['type'] === CredentialsEnum.IS_PEM"
15 /> 17 />
16 <div class="h-4"></div> 18 <div class="h-4"></div>
17 <UploadFile 19 <UploadFile
18 :url="credentialsFile.privateKeyFileName" 20 :url="credentialsFile.privateKeyFileName"
  21 + :disabled="disabled"
19 @fileUrlEmit="handleFileUrlEmitB" 22 @fileUrlEmit="handleFileUrlEmitB"
20 v-show="model['type'] === CredentialsEnum.IS_PEM" 23 v-show="model['type'] === CredentialsEnum.IS_PEM"
21 /> 24 />
@@ -24,7 +27,7 @@ @@ -24,7 +27,7 @@
24 </div> 27 </div>
25 </template> 28 </template>
26 <script lang="ts" setup name="DataFlowMethodIsMqtt"> 29 <script lang="ts" setup name="DataFlowMethodIsMqtt">
27 - import { reactive } from 'vue'; 30 + import { reactive, ref } from 'vue';
28 import { BasicForm, useForm } from '/@/components/Form'; 31 import { BasicForm, useForm } from '/@/components/Form';
29 import { modeMqttForm } from './config'; 32 import { modeMqttForm } from './config';
30 import { UploadFile } from '../../../uploadfile'; 33 import { UploadFile } from '../../../uploadfile';
@@ -37,7 +40,7 @@ @@ -37,7 +40,7 @@
37 privateKeyFileName: undefined, 40 privateKeyFileName: undefined,
38 }); 41 });
39 42
40 - const [register, { validateFields, setFieldsValue, resetFields }] = useForm({ 43 + const [register, { validateFields, setFieldsValue, resetFields, setProps }] = useForm({
41 schemas: modeMqttForm, 44 schemas: modeMqttForm,
42 ...modelFormPublicConfig, 45 ...modelFormPublicConfig,
43 }); 46 });
@@ -81,11 +84,18 @@ @@ -81,11 +84,18 @@
81 }); 84 });
82 }; 85 };
83 86
  87 + const disabled = ref<boolean>(false);
  88 + const setDisabledProps = (value) => {
  89 + setProps(value);
  90 + disabled.value = true;
  91 + };
  92 +
84 const resetValue = () => resetFields(); 93 const resetValue = () => resetFields();
85 defineExpose({ 94 defineExpose({
86 getValue, 95 getValue,
87 setValue, 96 setValue,
88 resetValue, 97 resetValue,
  98 + setDisabledProps,
89 }); 99 });
90 </script> 100 </script>
91 101
@@ -145,7 +145,6 @@ export const modeMqttForm: FormSchema[] = [ @@ -145,7 +145,6 @@ export const modeMqttForm: FormSchema[] = [
145 options: MqttFormPartialConfig.getType(), 145 options: MqttFormPartialConfig.getType(),
146 onChange(e) { 146 onChange(e) {
147 if (e) { 147 if (e) {
148 - console.log('执行');  
149 setFieldsValue({ 148 setFieldsValue({
150 password: undefined, 149 password: undefined,
151 username: undefined, 150 username: undefined,
@@ -8,10 +8,11 @@ @@ -8,10 +8,11 @@
8 import { modeRabbitMqForm } from './config'; 8 import { modeRabbitMqForm } from './config';
9 import { modelFormPublicConfig } from '../../../dataflowmodal/config'; 9 import { modelFormPublicConfig } from '../../../dataflowmodal/config';
10 10
11 - const [register, { validateFields, setFieldsValue, resetFields }] = useForm({  
12 - schemas: modeRabbitMqForm,  
13 - ...modelFormPublicConfig,  
14 - }); 11 + const [register, { validateFields, setFieldsValue, resetFields, setProps, updateSchema }] =
  12 + useForm({
  13 + schemas: modeRabbitMqForm,
  14 + ...modelFormPublicConfig,
  15 + });
15 16
16 const getValue = async () => { 17 const getValue = async () => {
17 const values = await validateFields(); 18 const values = await validateFields();
@@ -22,10 +23,22 @@ @@ -22,10 +23,22 @@
22 const setValue = (value) => setFieldsValue(value); 23 const setValue = (value) => setFieldsValue(value);
23 24
24 const resetValue = () => resetFields(); 25 const resetValue = () => resetFields();
  26 +
  27 + const setDisabledProps = (value) => {
  28 + setProps(value);
  29 + updateSchema({ field: 'clientProperties', componentProps: { disabled: true } });
  30 + };
  31 +
  32 + const setCancelDisabled = () => {
  33 + updateSchema({ field: 'clientProperties', componentProps: { disabled: false } });
  34 + };
  35 +
25 defineExpose({ 36 defineExpose({
26 getValue, 37 getValue,
27 setValue, 38 setValue,
28 resetValue, 39 resetValue,
  40 + setDisabledProps,
  41 + setCancelDisabled,
29 }); 42 });
30 </script> 43 </script>
31 <style lang="less" scoped></style> 44 <style lang="less" scoped></style>
@@ -16,8 +16,9 @@ @@ -16,8 +16,9 @@
16 import { BasicInfoFormField } from './enum'; 16 import { BasicInfoFormField } from './enum';
17 import { BasicInfoRecord } from './types'; 17 import { BasicInfoRecord } from './types';
18 import { Button } from 'ant-design-vue'; 18 import { Button } from 'ant-design-vue';
  19 + import { ref, unref } from 'vue';
19 20
20 - const props = defineProps({ 21 + defineProps({
21 saveContent: { 22 saveContent: {
22 type: Function, 23 type: Function,
23 }, 24 },
@@ -25,8 +26,9 @@ @@ -25,8 +26,9 @@
25 26
26 const emit = defineEmits(['currentDataFlowMethodEmitNext']); 27 const emit = defineEmits(['currentDataFlowMethodEmitNext']);
27 28
28 - const [register, { validateFields, setFieldsValue, resetFields }] = useForm({  
29 - schemas: modeForm(props.saveContent), 29 + const disabled = ref<boolean>(false);
  30 + const [register, { validateFields, setFieldsValue, resetFields, setProps }] = useForm({
  31 + schemas: modeForm(unref(disabled)),
30 ...modelFormPublicConfig, 32 ...modelFormPublicConfig,
31 }); 33 });
32 34
@@ -61,11 +63,21 @@ @@ -61,11 +63,21 @@
61 emit('currentDataFlowMethodEmitNext', getValue()); 63 emit('currentDataFlowMethodEmitNext', getValue());
62 }; 64 };
63 65
  66 + const setDisabledProps = (value) => {
  67 + setProps(value);
  68 + disabled.value = false;
  69 + };
  70 + const setCancelDisabled = () => {
  71 + disabled.value = false;
  72 + };
  73 +
64 const resetValue = () => resetFields(); 74 const resetValue = () => resetFields();
65 defineExpose({ 75 defineExpose({
66 getValue, 76 getValue,
67 setValue, 77 setValue,
68 resetValue, 78 resetValue,
  79 + setDisabledProps,
  80 + setCancelDisabled,
69 }); 81 });
70 </script> 82 </script>
71 <style lang="less" scoped> 83 <style lang="less" scoped>
@@ -37,7 +37,7 @@ @@ -37,7 +37,7 @@
37 import { BasicModal, useModalInner } from '/@/components/Modal'; 37 import { BasicModal, useModalInner } from '/@/components/Modal';
38 import { add } from '/@/components/Form/src/componentMap'; 38 import { add } from '/@/components/Form/src/componentMap';
39 import TransferModal from '/@/components/Form/src/components/TransferModal.vue'; 39 import TransferModal from '/@/components/Form/src/components/TransferModal.vue';
40 - import TransferTableModal from '/@/components/Form/src/components/TransferTableModal.vue'; 40 + import TransferTableModal from './TransferTableModal.vue';
41 import { DataFlowMethod, DataFlowParams } from './index'; 41 import { DataFlowMethod, DataFlowParams } from './index';
42 import { stepConfig, removeFieldByModeForm } from './config'; 42 import { stepConfig, removeFieldByModeForm } from './config';
43 import { postAddConvertApi } from '/@/api/datamanager/dataManagerApi'; 43 import { postAddConvertApi } from '/@/api/datamanager/dataManagerApi';
@@ -74,6 +74,11 @@ @@ -74,6 +74,11 @@
74 resetValue(); 74 resetValue();
75 const { text, record } = data; 75 const { text, record } = data;
76 businessText.value = text; 76 businessText.value = text;
  77 + if (businessText.value == BusinessDataFlowTextEnum.BUSINESS_MODAL_VIEW_TEXT) {
  78 + dataFlowMethodRef.value?.setDisabledProps({ disabled: true });
  79 + } else {
  80 + dataFlowMethodRef.value?.setCancelDisabled();
  81 + }
77 restData.data = record; 82 restData.data = record;
78 setModalProps(modalProps(businessText.value)); 83 setModalProps(modalProps(businessText.value));
79 if (!record) return; 84 if (!record) return;
@@ -154,13 +159,18 @@ @@ -154,13 +159,18 @@
154 dataFlowParamsRef.value?.setValue({ 159 dataFlowParamsRef.value?.setValue({
155 ...record, 160 ...record,
156 ...record?.configuration, 161 ...record?.configuration,
157 - name: record?.name,  
158 description: record?.configuration.description || record?.additionalInfo?.description, 162 description: record?.configuration.description || record?.additionalInfo?.description,
159 }); 163 });
160 }; 164 };
161 165
162 //下一步 166 //下一步
163 const handleNextDataFlowParams = async (value) => { 167 const handleNextDataFlowParams = async (value) => {
  168 + //判断是否是查看 查看禁用表单
  169 + if (businessText.value == BusinessDataFlowTextEnum.BUSINESS_MODAL_VIEW_TEXT) {
  170 + dataFlowParamsRef.value?.setProps();
  171 + } else {
  172 + dataFlowParamsRef.value?.setCancelDisabled();
  173 + }
164 value 174 value
165 .then(async (res) => { 175 .then(async (res) => {
166 currentStep.value = 1; 176 currentStep.value = 1;
@@ -104,10 +104,35 @@ @@ -104,10 +104,35 @@
104 if (!findDateFlow) return; 104 if (!findDateFlow) return;
105 findDateFlow[3](0); 105 findDateFlow[3](0);
106 }; 106 };
  107 +
  108 + const setProps = async () => {
  109 + await nextTick();
  110 + props.dataFlowType === BusinessDataFlowMethodEnum.DATAFLOW_METHOD_KAFKA &&
  111 + dataFlowMethodIsKafkaRef.value?.setDisabledProps({ disabled: true });
  112 + props.dataFlowType === BusinessDataFlowMethodEnum.DATAFLOW_METHOD_MQTT &&
  113 + dataFlowMethodIsMqttRef.value?.setDisabledProps({ disabled: true });
  114 + props.dataFlowType === BusinessDataFlowMethodEnum.DATAFLOW_METHOD_RABBITMQ &&
  115 + dataFlowMethodIsRabbitMqRef.value?.setDisabledProps({ disabled: true });
  116 + props.dataFlowType === BusinessDataFlowMethodEnum.DATAFLOW_METHOD_REST_API &&
  117 + dataFlowMethodIsApiRef.value?.setDisabledProps({ disabled: true });
  118 + };
  119 +
  120 + const setCancelDisabled = async () => {
  121 + await nextTick();
  122 + props.dataFlowType === BusinessDataFlowMethodEnum.DATAFLOW_METHOD_KAFKA &&
  123 + dataFlowMethodIsKafkaRef.value?.setCancelDisabled();
  124 + props.dataFlowType === BusinessDataFlowMethodEnum.DATAFLOW_METHOD_RABBITMQ &&
  125 + dataFlowMethodIsRabbitMqRef.value?.setCancelDisabled();
  126 + props.dataFlowType === BusinessDataFlowMethodEnum.DATAFLOW_METHOD_REST_API &&
  127 + dataFlowMethodIsApiRef.value?.setCancelDisabled();
  128 + };
  129 +
107 defineExpose({ 130 defineExpose({
108 getValue, 131 getValue,
109 setValue, 132 setValue,
110 resetValue, 133 resetValue,
  134 + setCancelDisabled,
  135 + setProps,
111 }); 136 });
112 </script> 137 </script>
113 <style lang="less" scoped> 138 <style lang="less" scoped>
  1 +import { h, unref } from 'vue';
  2 +import { findDictItemByCode } from '/@/api/system/dict';
  3 +import { FETCH_SETTING } from '/@/components/Table/src/const';
  4 +import { DeviceStatusEnum, DeviceStatusNameEnum, DeviceTypeNameEnum } from './enum';
  5 +import { Tag } from 'ant-design-vue';
  6 +import { BasicColumn, BasicTableProps, FormSchema } from '/@/components/Table';
  7 +import { useClipboard } from '@vueuse/core';
  8 +import { useMessage } from '/@/hooks/web/useMessage';
  9 +
  10 +export const deviceTableFormSchema: FormSchema[] = [
  11 + {
  12 + field: 'name',
  13 + label: '设备名称',
  14 + component: 'Input',
  15 + colProps: { span: 9 },
  16 + componentProps: {
  17 + placeholder: '请输入设备名称',
  18 + },
  19 + },
  20 + {
  21 + field: 'deviceType',
  22 + label: '设备类型',
  23 + component: 'ApiSelect',
  24 + colProps: { span: 9 },
  25 + componentProps: {
  26 + placeholder: '请选择设备类型',
  27 + api: findDictItemByCode,
  28 + params: {
  29 + dictCode: 'device_type',
  30 + },
  31 + labelField: 'itemText',
  32 + valueField: 'itemValue',
  33 + },
  34 + },
  35 +];
  36 +
  37 +const { copied, copy } = useClipboard({ legacy: true });
  38 +
  39 +const { createMessage } = useMessage();
  40 +
  41 +export const deviceTableColumn: BasicColumn[] = [
  42 + {
  43 + title: '状态',
  44 + dataIndex: 'deviceState',
  45 + customRender: ({ text }) => {
  46 + return h(
  47 + Tag,
  48 + {
  49 + color:
  50 + text === DeviceStatusEnum.INACTIVE
  51 + ? 'warning'
  52 + : text === DeviceStatusEnum.OFFLINE
  53 + ? 'error'
  54 + : 'success',
  55 + },
  56 + () => DeviceStatusNameEnum[text]
  57 + );
  58 + },
  59 + },
  60 + {
  61 + title: '别名/设备名称',
  62 + dataIndex: 'name',
  63 + customRender: ({ record }) => {
  64 + return h('div', [
  65 + h(
  66 + 'div',
  67 + {
  68 + class: 'cursor-pointer',
  69 + onClick: async () => {
  70 + await copy(record.name);
  71 + if (unref(copied)) createMessage.success('复制成功~');
  72 + },
  73 + },
  74 + [
  75 + record.alias && h('div', { class: 'truncate' }, record.alias),
  76 + h('div', { class: 'text-blue-400 truncate' }, record.name),
  77 + ]
  78 + ),
  79 + ]);
  80 + },
  81 + },
  82 + {
  83 + title: '设备类型',
  84 + dataIndex: 'deviceType',
  85 + customRender: ({ text }) => {
  86 + return h(Tag, { color: 'success' }, () => DeviceTypeNameEnum[text]);
  87 + },
  88 + },
  89 + {
  90 + title: '所属产品',
  91 + dataIndex: 'deviceProfile.name',
  92 + },
  93 + {
  94 + title: '所属组织',
  95 + dataIndex: 'organizationDTO.name',
  96 + },
  97 +];
  98 +
  99 +export const TransferTableProps: BasicTableProps = {
  100 + formConfig: {
  101 + layout: 'inline',
  102 + labelWidth: 80,
  103 + schemas: deviceTableFormSchema,
  104 + actionColOptions: { span: 6 },
  105 + },
  106 + size: 'small',
  107 + maxHeight: 240,
  108 + useSearchForm: true,
  109 + columns: deviceTableColumn,
  110 + showIndexColumn: false,
  111 + fetchSetting: FETCH_SETTING,
  112 +} as BasicTableProps;
  1 +<script lang="ts" setup>
  2 + import { Button, Tabs, Tag } from 'ant-design-vue';
  3 + import { remove, uniqBy, cloneDeep } from 'lodash';
  4 + import { computed, nextTick, onMounted, ref, unref, toRaw } from 'vue';
  5 + import {
  6 + deviceTableColumn,
  7 + deviceTableFormSchema,
  8 + TransferTableProps,
  9 + } from './TransferTableModal.config';
  10 + import { devicePage } from '/@/api/device/deviceManager';
  11 + import { DeviceModel as RawDeviceModal } from '/@/api/device/model/deviceModel';
  12 + import { BasicModal, useModal } from '/@/components/Modal';
  13 + import { BasicTable, useTable } from '/@/components/Table';
  14 + import { FETCH_SETTING } from '/@/components/Table/src/const';
  15 + import { useDesign } from '/@/hooks/web/useDesign';
  16 + import { isFunction } from '/@/utils/is';
  17 +
  18 + interface DeviceModel extends RawDeviceModal {
  19 + disabled?: boolean;
  20 + }
  21 +
  22 + const props = withDefaults(
  23 + defineProps<{
  24 + getPendingTableParams: (params: Recordable) => any;
  25 + getSelectedTableParams: (params: Recordable) => any;
  26 + value?: (Recordable & DeviceModel)[];
  27 + maxTagLength?: number;
  28 + openModalValidate?: () => boolean;
  29 + primaryKey?: string;
  30 + transformValue?: (list: Recordable[]) => any;
  31 + disabled?: boolean;
  32 + }>(),
  33 + {
  34 + value: () => [],
  35 + maxTagLength: 2,
  36 + primaryKey: 'tbDeviceId',
  37 + }
  38 + );
  39 +
  40 + const emit = defineEmits(['update:value']);
  41 +
  42 + enum Active {
  43 + PENDING = 'pending',
  44 + SELECTED = 'selected',
  45 + }
  46 +
  47 + const activeKey = ref(Active.PENDING);
  48 +
  49 + const { prefixCls } = useDesign('transfer-table-modal');
  50 +
  51 + const pendingTotalList = ref<DeviceModel[]>([]);
  52 +
  53 + const pendingConfirmQueue = ref<DeviceModel[]>([]);
  54 +
  55 + const selectedTotalList = ref<DeviceModel[]>([]);
  56 +
  57 + const selectedConfirmQueue = ref<DeviceModel[]>([]);
  58 +
  59 + const getShowTagOptions = computed(() => {
  60 + const { maxTagLength } = props;
  61 + return unref(selectedTotalList).slice(0, maxTagLength);
  62 + });
  63 +
  64 + const getSurplusOptionsLength = computed(() => {
  65 + const { maxTagLength } = props;
  66 + const surplusValue = unref(selectedTotalList).length - maxTagLength;
  67 + return surplusValue < 0 ? 0 : surplusValue;
  68 + });
  69 +
  70 + // const pendingListCount = computed(() => {
  71 + // const { value } = props;
  72 + // const selectedList = unref(pendingTotalList).filter((item) => value.includes(item.id));
  73 + // return unref(pendingTotalList).length - selectedList.length;
  74 + // });
  75 +
  76 + const [registerModal, { openModal }] = useModal();
  77 +
  78 + const [regsterPendingTable, pendingTableActionType] = useTable({
  79 + ...TransferTableProps,
  80 + rowKey: props.primaryKey,
  81 + api: devicePage,
  82 + immediate: false,
  83 + clickToRowSelect: false,
  84 + beforeFetch: (params) => {
  85 + const { getPendingTableParams } = props;
  86 + const data = getPendingTableParams?.(params) || {};
  87 + Object.assign(params, { ...data, selected: false });
  88 + return params;
  89 + },
  90 + afterFetch: (list: DeviceModel[]) => {
  91 + pendingTotalList.value = list;
  92 + return unref(pendingTotalList);
  93 + },
  94 + pagination: { hideOnSinglePage: false },
  95 + rowSelection: {
  96 + type: 'checkbox',
  97 + getCheckboxProps: (record: DeviceModel) => {
  98 + const { primaryKey } = props;
  99 + const checked = unref(selectedTotalList).map((item) => item[primaryKey]);
  100 + return {
  101 + disabled: checked.includes(record[props.primaryKey]!),
  102 + };
  103 + },
  104 + onSelect: (_record: Recordable, _selected: boolean, selectedRows: DeviceModel[]) => {
  105 + const { primaryKey } = props;
  106 + const checked = unref(selectedTotalList).map((item) => item[primaryKey]);
  107 + pendingConfirmQueue.value = selectedRows.filter(
  108 + (item) => !checked.includes(item[primaryKey]!)
  109 + );
  110 + },
  111 + onSelectAll: (_selected: boolean, selectedRows: DeviceModel[]) => {
  112 + const { primaryKey } = props;
  113 + const checked = unref(selectedTotalList).map((item) => item[primaryKey]);
  114 + pendingConfirmQueue.value = selectedRows.filter(
  115 + (item) => !checked.includes(item[primaryKey]!)
  116 + );
  117 + },
  118 + },
  119 + });
  120 +
  121 + const [registerSelectedTable, selectedTableActionType] = useTable({
  122 + formConfig: {
  123 + layout: 'inline',
  124 + labelWidth: 80,
  125 + schemas: deviceTableFormSchema,
  126 + actionColOptions: { span: 6 },
  127 + },
  128 + size: 'small',
  129 + maxHeight: 240,
  130 + useSearchForm: true,
  131 + columns: deviceTableColumn,
  132 + api: async (params) => {
  133 + const { name = '', deviceType = '' } = params || {};
  134 + const items = unref(selectedTotalList).filter((item) => {
  135 + return (
  136 + (item.name.toUpperCase().includes(name.toUpperCase()) ||
  137 + item.alias?.toUpperCase().includes(name.toUpperCase())) &&
  138 + item.deviceType.toUpperCase().includes(deviceType.toUpperCase())
  139 + );
  140 + });
  141 + return {
  142 + items,
  143 + total: items.length,
  144 + };
  145 + },
  146 + showIndexColumn: false,
  147 + pagination: { hideOnSinglePage: false },
  148 + fetchSetting: FETCH_SETTING,
  149 + rowKey: props.primaryKey,
  150 + dataSource: selectedTotalList,
  151 + clickToRowSelect: false,
  152 + beforeFetch: (params) => {
  153 + const { getSelectedTableParams } = props;
  154 + const data = getSelectedTableParams?.(params) || {};
  155 + Object.assign(params, { ...data, selected: false });
  156 + return params;
  157 + },
  158 + rowSelection: {
  159 + type: 'checkbox',
  160 + onSelect: (_record: Recordable, _selected: boolean, selectedRows: DeviceModel[]) => {
  161 + selectedConfirmQueue.value = selectedRows;
  162 + },
  163 + onSelectAll: (_selected: boolean, selectedRows: DeviceModel[]) => {
  164 + selectedConfirmQueue.value = selectedRows;
  165 + },
  166 + },
  167 + });
  168 +
  169 + const handleTriggerUpdateValue = () => {
  170 + let list: Recordable[] = cloneDeep(toRaw(unref(selectedTotalList)));
  171 + const { transformValue } = props;
  172 + if (transformValue && isFunction(transformValue)) list = transformValue(list);
  173 +
  174 + emit('update:value', list);
  175 + };
  176 +
  177 + const handleSelected = () => {
  178 + const { primaryKey } = props;
  179 + const _list = [...unref(selectedTotalList), ...unref(pendingConfirmQueue)];
  180 + selectedTotalList.value = uniqBy(_list, primaryKey);
  181 + pendingConfirmQueue.value = [];
  182 +
  183 + handleTriggerUpdateValue();
  184 + pendingTableActionType.setTableData([]);
  185 + nextTick(() => pendingTableActionType.setTableData(unref(pendingTotalList)));
  186 + };
  187 +
  188 + const handleRemoveSelected = () => {
  189 + const { primaryKey } = props;
  190 + const selectedIds = unref(selectedConfirmQueue).map((selected) => selected[primaryKey]);
  191 + remove(unref(selectedTotalList), (item) => {
  192 + const flag = selectedIds.includes(item[primaryKey]);
  193 + if (flag) {
  194 + pendingTableActionType.deleteSelectRowByKey(item[primaryKey]);
  195 + }
  196 + return flag;
  197 + });
  198 +
  199 + handleTriggerUpdateValue();
  200 +
  201 + selectedTableActionType.clearSelectedRowKeys();
  202 + selectedConfirmQueue.value = [];
  203 + selectedTableActionType.reload();
  204 + };
  205 +
  206 + const handleCheckoutPanel = async (key: Active) => {
  207 + if (key === Active.PENDING) {
  208 + pendingTableActionType.setTableData([]);
  209 + await nextTick();
  210 + pendingTableActionType.setTableData(unref(pendingTotalList));
  211 + } else {
  212 + await nextTick();
  213 + selectedTableActionType.reload();
  214 + }
  215 + };
  216 +
  217 + const handleOpenModal = async () => {
  218 + const { openModalValidate } = props;
  219 +
  220 + if (openModalValidate && isFunction(openModalValidate) && !openModalValidate()) return;
  221 +
  222 + openModal(true);
  223 + await nextTick();
  224 + pendingTableActionType.reload();
  225 + };
  226 +
  227 + onMounted(async () => {
  228 + const { getSelectedTableParams } = props;
  229 + const data = getSelectedTableParams?.({}) || {};
  230 + if (!data?.convertConfigId || !data?.deviceProfileIds) {
  231 + return;
  232 + }
  233 + const { items } = await devicePage({ page: 1, pageSize: 10, ...data, selected: true });
  234 + selectedTotalList.value = items;
  235 + });
  236 +</script>
  237 +
  238 +<template>
  239 + <section>
  240 + <BasicModal
  241 + @register="registerModal"
  242 + title="穿梭表格"
  243 + width="60%"
  244 + :wrapClassName="prefixCls"
  245 + :showOkBtn="false"
  246 + cancelText="关闭"
  247 + >
  248 + <section class="bg-gray-100">
  249 + <Tabs v-model:active-key="activeKey" type="card" @change="handleCheckoutPanel">
  250 + <Tabs.TabPane :key="Active.PENDING">
  251 + <template #tab>
  252 + <div class="flex items-center justify-center">
  253 + <span>待选设备</span>
  254 + <!-- <Badge show-zero :count="pendingListCount" /> -->
  255 + </div>
  256 + </template>
  257 + <BasicTable @register="regsterPendingTable">
  258 + <template #toolbar>
  259 + <section class="flex w-full justify-end items-center">
  260 + <!-- <Button type="primary">全选</Button> -->
  261 + <div class="text-blue-400">
  262 + <span class="mr-2">选择设备:</span>
  263 + <span>{{ pendingConfirmQueue.length }}</span>
  264 + </div>
  265 + </section>
  266 + </template>
  267 + </BasicTable>
  268 + <section class="flex justify-end px-4 pb-4">
  269 + <Button
  270 + type="primary"
  271 + @click="handleSelected"
  272 + :disabled="!pendingConfirmQueue.length"
  273 + >
  274 + <span>确定已选</span>
  275 + </Button>
  276 + </section>
  277 + </Tabs.TabPane>
  278 + <Tabs.TabPane :key="Active.SELECTED">
  279 + <template #tab>
  280 + <div class="flex items-center justify-center">
  281 + <span>已选设备</span>
  282 + <!-- <Badge show-zero :count="selectedTotalList.length" /> -->
  283 + </div>
  284 + </template>
  285 + <BasicTable @register="registerSelectedTable">
  286 + <template #toolbar>
  287 + <section class="flex w-full justify-end items-center">
  288 + <div class="text-blue-400">
  289 + <span class="mr-2">选择设备:</span>
  290 + <span>{{ selectedConfirmQueue.length }}</span>
  291 + </div>
  292 + </section>
  293 + </template>
  294 + </BasicTable>
  295 + <section class="flex justify-end px-4 pb-4">
  296 + <Button
  297 + type="primary"
  298 + :disabled="!selectedConfirmQueue.length"
  299 + @click="handleRemoveSelected"
  300 + >
  301 + <span>移除已选</span>
  302 + </Button>
  303 + </section>
  304 + </Tabs.TabPane>
  305 + </Tabs>
  306 + </section>
  307 + </BasicModal>
  308 + <Button @click="handleOpenModal" type="link" :disabled="disabled">
  309 + <span v-if="!selectedTotalList.length">选择设备</span>
  310 + <div v-if="selectedTotalList.length">
  311 + <Tag
  312 + class="!px-2 !py-1 !bg-gray-50 !border-gray-100"
  313 + v-for="item in getShowTagOptions"
  314 + :key="item[primaryKey]"
  315 + >
  316 + <span>
  317 + {{ item.alias || item.name }}
  318 + </span>
  319 + </Tag>
  320 + <Tag class="!px-2 !py-1 !bg-gray-50 !border-gray-100" v-if="getSurplusOptionsLength">
  321 + <span> +{{ getSurplusOptionsLength }}... </span>
  322 + </Tag>
  323 + </div>
  324 + </Button>
  325 + </section>
  326 +</template>
  327 +
  328 +<style lang="less">
  329 + @prefix-cls: ~'@{namespace}-transfer-table-modal';
  330 +
  331 + .@{prefix-cls} {
  332 + .vben-basic-table {
  333 + padding-top: 0;
  334 + }
  335 +
  336 + .vben-basic-form > .ant-row {
  337 + width: 100%;
  338 + }
  339 +
  340 + .ant-tabs-top-bar {
  341 + background-color: #fff;
  342 + }
  343 +
  344 + .ant-table-placeholder {
  345 + height: auto !important;
  346 + }
  347 + }
  348 +</style>
1 import { FormSchema } from '/@/components/Form'; 1 import { FormSchema } from '/@/components/Form';
2 import { findDictItemByCode } from '/@/api/system/dict'; 2 import { findDictItemByCode } from '/@/api/system/dict';
3 -import { h, unref } from 'vue';  
4 import { getDeviceProfile } from '/@/api/alarm/position'; 3 import { getDeviceProfile } from '/@/api/alarm/position';
5 -import { BasicColumn, BasicTableProps } from '/@/components/Table';  
6 -import { devicePage } from '/@/api/device/deviceManager';  
7 -import { Tag } from 'ant-design-vue'; 4 +import { BasicInfoFormField, DataSourceType } from '../enum';
8 import { DeviceRecord } from '/@/api/device/model/deviceModel'; 5 import { DeviceRecord } from '/@/api/device/model/deviceModel';
9 -import { FETCH_SETTING } from '/@/components/Table/src/const';  
10 import { useMessage } from '/@/hooks/web/useMessage'; 6 import { useMessage } from '/@/hooks/web/useMessage';
11 -import {  
12 - BasicInfoFormField,  
13 - DataSourceType,  
14 - DeviceStatusEnum,  
15 - DeviceStatusNameEnum,  
16 - DeviceTypeNameEnum,  
17 -} from '../enum';  
18 -import { useClipboard } from '@vueuse/core';  
19 7
20 export const stepConfig = ['选择流转方式', '完善配置参数']; 8 export const stepConfig = ['选择流转方式', '完善配置参数'];
21 9
22 export const removeFieldByModeForm = ['name', 'description']; 10 export const removeFieldByModeForm = ['name', 'description'];
23 11
24 -//表单通用配置  
25 -export const modelFormPublicConfig = {  
26 - labelWidth: 120,  
27 - actionColOptions: {  
28 - span: 14,  
29 - },  
30 - showResetButton: false,  
31 - showSubmitButton: false,  
32 -};  
33 -  
34 const handleGroupDevice = (options: DeviceRecord[]) => { 12 const handleGroupDevice = (options: DeviceRecord[]) => {
35 const map = new Map<string, string[]>(); 13 const map = new Map<string, string[]>();
36 options.forEach((item) => { 14 options.forEach((item) => {
@@ -46,111 +24,18 @@ const handleGroupDevice = (options: DeviceRecord[]) => { @@ -46,111 +24,18 @@ const handleGroupDevice = (options: DeviceRecord[]) => {
46 return value; 24 return value;
47 }; 25 };
48 26
49 -const deviceTableFormSchema: FormSchema[] = [  
50 - {  
51 - field: 'name',  
52 - label: '设备名称',  
53 - component: 'Input',  
54 - colProps: { span: 9 },  
55 - componentProps: {  
56 - placeholder: '请输入设备名称',  
57 - },  
58 - },  
59 - {  
60 - field: 'deviceType',  
61 - label: '设备类型',  
62 - component: 'ApiSelect',  
63 - colProps: { span: 9 },  
64 - componentProps: {  
65 - placeholder: '请选择设备类型',  
66 - api: findDictItemByCode,  
67 - params: {  
68 - dictCode: 'device_type',  
69 - },  
70 - labelField: 'itemText',  
71 - valueField: 'itemValue',  
72 - }, 27 +//表单通用配置
  28 +export const modelFormPublicConfig = {
  29 + labelWidth: 120,
  30 + actionColOptions: {
  31 + span: 14,
73 }, 32 },
74 -];  
75 -  
76 -const { copied, copy } = useClipboard({ legacy: true }); 33 + showResetButton: false,
  34 + showSubmitButton: false,
  35 +};
77 36
78 const { createMessage } = useMessage(); 37 const { createMessage } = useMessage();
79 -  
80 -const deviceTableColumn: BasicColumn[] = [  
81 - {  
82 - title: '状态',  
83 - dataIndex: 'deviceState',  
84 - customRender: ({ text }) => {  
85 - return h(  
86 - Tag,  
87 - {  
88 - color:  
89 - text === DeviceStatusEnum.INACTIVE  
90 - ? 'warning'  
91 - : text === DeviceStatusEnum.OFFLINE  
92 - ? 'error'  
93 - : 'success',  
94 - },  
95 - () => DeviceStatusNameEnum[text]  
96 - );  
97 - },  
98 - },  
99 - {  
100 - title: '别名/设备名称',  
101 - dataIndex: 'name',  
102 - customRender: ({ record }) => {  
103 - return h('div', [  
104 - h(  
105 - 'div',  
106 - {  
107 - class: 'cursor-pointer',  
108 - onClick: async () => {  
109 - await copy(record.name);  
110 - if (unref(copied)) createMessage.success('复制成功~');  
111 - },  
112 - },  
113 - [  
114 - record.alias && h('div', { class: 'truncate' }, record.alias),  
115 - h('div', { class: 'text-blue-400 truncate' }, record.name),  
116 - ]  
117 - ),  
118 - ]);  
119 - },  
120 - },  
121 - {  
122 - title: '设备类型',  
123 - dataIndex: 'deviceType',  
124 - customRender: ({ text }) => {  
125 - return h(Tag, { color: 'success' }, () => DeviceTypeNameEnum[text]);  
126 - },  
127 - },  
128 - {  
129 - title: '所属产品',  
130 - dataIndex: 'deviceProfile.name',  
131 - },  
132 - {  
133 - title: '所属组织',  
134 - dataIndex: 'organizationDTO.name',  
135 - },  
136 -];  
137 -  
138 -const TransferTableProps: BasicTableProps = {  
139 - formConfig: {  
140 - layout: 'inline',  
141 - labelWidth: 80,  
142 - schemas: deviceTableFormSchema,  
143 - actionColOptions: { span: 6 },  
144 - },  
145 - size: 'small',  
146 - maxHeight: 240,  
147 - useSearchForm: true,  
148 - columns: deviceTableColumn,  
149 - showIndexColumn: false,  
150 - fetchSetting: FETCH_SETTING,  
151 -} as BasicTableProps;  
152 -  
153 -export const modeForm = (submitFn?: Function): FormSchema[] => { 38 +export const modeForm = (disabled: boolean): FormSchema[] => {
154 return [ 39 return [
155 { 40 {
156 field: BasicInfoFormField.CONVERT_CONFIG_ID, 41 field: BasicInfoFormField.CONVERT_CONFIG_ID,
@@ -187,6 +72,7 @@ export const modeForm = (submitFn?: Function): FormSchema[] => { @@ -187,6 +72,7 @@ export const modeForm = (submitFn?: Function): FormSchema[] => {
187 api: getDeviceProfile, 72 api: getDeviceProfile,
188 labelField: 'name', 73 labelField: 'name',
189 valueField: 'tbProfileId', 74 valueField: 'tbProfileId',
  75 + disabled,
190 transferProps: { 76 transferProps: {
191 listStyle: { height: '400px' }, 77 listStyle: { height: '400px' },
192 showSearch: true, 78 showSearch: true,
@@ -214,68 +100,28 @@ export const modeForm = (submitFn?: Function): FormSchema[] => { @@ -214,68 +100,28 @@ export const modeForm = (submitFn?: Function): FormSchema[] => {
214 rules: [{ required: true, message: '数据源设备为必选项', type: 'array' }], 100 rules: [{ required: true, message: '数据源设备为必选项', type: 'array' }],
215 componentProps: ({ formActionType }) => { 101 componentProps: ({ formActionType }) => {
216 const { getFieldsValue } = formActionType; 102 const { getFieldsValue } = formActionType;
217 - const values = getFieldsValue();  
218 - const convertConfigId = Reflect.get(values, BasicInfoFormField.CONVERT_CONFIG_ID);  
219 - const devices = Reflect.get(values, BasicInfoFormField.DATA_SOURCE_DEVICE);  
220 -  
221 return { 103 return {
222 - labelField: 'name',  
223 - valueField: 'tbDeviceId',  
224 - primaryKey: 'tbDeviceId',  
225 - pendingTableProps: {  
226 - ...TransferTableProps,  
227 - api: devicePage,  
228 - beforeFetch: (params) => {  
229 - const values = getFieldsValue();  
230 - const deviceProfileIds = Reflect.get(values, BasicInfoFormField.DATA_SOURCE_PRODUCT);  
231 - const convertConfigId = Reflect.get(values, BasicInfoFormField.CONVERT_CONFIG_ID);  
232 - if (convertConfigId) {  
233 - Object.assign(params, { convertConfigId, selected: false });  
234 - }  
235 - return { ...params, deviceProfileIds };  
236 - },  
237 - } as BasicTableProps,  
238 - selectedTableProps: {  
239 - ...TransferTableProps,  
240 - // api  
241 - api: !!(convertConfigId && devices) ? devicePage : undefined,  
242 - beforeFetch: (params) => {  
243 - const values = getFieldsValue();  
244 - const deviceProfileIds = Reflect.get(values, BasicInfoFormField.DATA_SOURCE_PRODUCT);  
245 - const convertConfigId = Reflect.get(values, BasicInfoFormField.CONVERT_CONFIG_ID);  
246 - if (convertConfigId) {  
247 - Object.assign(params, { convertConfigId, selected: true });  
248 - }  
249 - return { ...params, deviceProfileIds };  
250 - },  
251 - } as BasicTableProps,  
252 - initSelectedOptions: async ({ setSelectedTotal }) => { 104 + disabled,
  105 + getPendingTableParams: () => {
253 const values = getFieldsValue(); 106 const values = getFieldsValue();
254 const convertConfigId = Reflect.get(values, BasicInfoFormField.CONVERT_CONFIG_ID); 107 const convertConfigId = Reflect.get(values, BasicInfoFormField.CONVERT_CONFIG_ID);
255 const deviceProfileIds = Reflect.get(values, BasicInfoFormField.DATA_SOURCE_PRODUCT); 108 const deviceProfileIds = Reflect.get(values, BasicInfoFormField.DATA_SOURCE_PRODUCT);
256 - const devices = Reflect.get(values, BasicInfoFormField.DATA_SOURCE_DEVICE);  
257 - if (convertConfigId && devices) {  
258 - const { items, total } = await devicePage({  
259 - page: 1,  
260 - pageSize: 10,  
261 - convertConfigId: values[BasicInfoFormField.CONVERT_CONFIG_ID],  
262 - deviceProfileIds,  
263 - selected: true,  
264 - });  
265 - setSelectedTotal(total);  
266 - return items;  
267 - }  
268 - return [];  
269 - },  
270 - onSelectedAfter: async () => {  
271 - submitFn && (await submitFn(false)); 109 + return { convertConfigId, deviceProfileIds };
272 }, 110 },
273 - onRemoveAfter: async ({ reloadSelected }) => {  
274 - submitFn && (await submitFn(false));  
275 - reloadSelected(); 111 + getSelectedTableParams: () => {
  112 + const values = getFieldsValue();
  113 + const convertConfigId = Reflect.get(values, BasicInfoFormField.CONVERT_CONFIG_ID);
  114 + const deviceProfileIds = Reflect.get(values, BasicInfoFormField.DATA_SOURCE_PRODUCT);
  115 + return { convertConfigId, deviceProfileIds };
276 }, 116 },
277 - transformValue: (_selectedRowKeys: string[], selectedRows: DeviceRecord[]) => {  
278 - return handleGroupDevice(selectedRows); 117 + transformValue: handleGroupDevice,
  118 + openModalValidate: () => {
  119 + const values = getFieldsValue();
  120 + const deviceProfileIds = Reflect.get(values, BasicInfoFormField.DATA_SOURCE_PRODUCT);
  121 + if (!deviceProfileIds || !deviceProfileIds?.length) {
  122 + createMessage.warning('请选择数据源设备');
  123 + }
  124 + return deviceProfileIds && deviceProfileIds?.length;
279 }, 125 },
280 }; 126 };
281 }, 127 },
@@ -3,6 +3,7 @@ @@ -3,6 +3,7 @@
3 <a-upload-dragger 3 <a-upload-dragger
4 v-model:fileList="fileList.list" 4 v-model:fileList="fileList.list"
5 name="file" 5 name="file"
  6 + :disabled="disabled"
6 :multiple="false" 7 :multiple="false"
7 @change="handleChange($event)" 8 @change="handleChange($event)"
8 :before-upload="() => false" 9 :before-upload="() => false"
@@ -37,6 +38,10 @@ @@ -37,6 +38,10 @@
37 type: String, 38 type: String,
38 default: '', 39 default: '',
39 }, 40 },
  41 + disabled: {
  42 + type: Boolean,
  43 + default: false,
  44 + },
40 }); 45 });
41 46
42 const emit = defineEmits(['fileUrlEmit']); 47 const emit = defineEmits(['fileUrlEmit']);
1 import { FormSchema } from '/@/components/Form'; 1 import { FormSchema } from '/@/components/Form';
2 import { findDictItemByCode } from '/@/api/system/dict'; 2 import { findDictItemByCode } from '/@/api/system/dict';
3 -import { h, ref, unref } from 'vue'; 3 +import { ref } from 'vue';
4 import { isExistDataManagerNameApi } from '/@/api/datamanager/dataManagerApi'; 4 import { isExistDataManagerNameApi } from '/@/api/datamanager/dataManagerApi';
5 import { getDeviceProfile } from '/@/api/alarm/position'; 5 import { getDeviceProfile } from '/@/api/alarm/position';
6 -import { BasicColumn, BasicTableProps } from '/@/components/Table';  
7 -import { devicePage } from '/@/api/device/deviceManager';  
8 -import { Tag } from 'ant-design-vue';  
9 import { DeviceRecord } from '/@/api/device/model/deviceModel'; 6 import { DeviceRecord } from '/@/api/device/model/deviceModel';
10 -import { FETCH_SETTING } from '/@/components/Table/src/const';  
11 -import { useClipboard } from '@vueuse/core';  
12 -import { useMessage } from '/@/hooks/web/useMessage';  
13 7
14 const typeValue = ref(''); 8 const typeValue = ref('');
15 export enum CredentialsEnum { 9 export enum CredentialsEnum {
@@ -76,108 +70,7 @@ const handleGroupDevice = (options: DeviceRecord[]) => { @@ -76,108 +70,7 @@ const handleGroupDevice = (options: DeviceRecord[]) => {
76 return value; 70 return value;
77 }; 71 };
78 72
79 -const deviceTableFormSchema: FormSchema[] = [  
80 - {  
81 - field: 'name',  
82 - label: '设备名称',  
83 - component: 'Input',  
84 - colProps: { span: 9 },  
85 - componentProps: {  
86 - placeholder: '请输入设备名称',  
87 - },  
88 - },  
89 - {  
90 - field: 'deviceType',  
91 - label: '设备类型',  
92 - component: 'ApiSelect',  
93 - colProps: { span: 9 },  
94 - componentProps: {  
95 - placeholder: '请选择设备类型',  
96 - api: findDictItemByCode,  
97 - params: {  
98 - dictCode: 'device_type',  
99 - },  
100 - labelField: 'itemText',  
101 - valueField: 'itemValue',  
102 - },  
103 - },  
104 -];  
105 -const { copied, copy } = useClipboard({ legacy: true });  
106 -const { createMessage } = useMessage();  
107 -const deviceTableColumn: BasicColumn[] = [  
108 - {  
109 - title: '状态',  
110 - dataIndex: 'deviceState',  
111 - customRender: ({ text }) => {  
112 - return h(  
113 - Tag,  
114 - {  
115 - color:  
116 - text === DeviceStatusEnum.INACTIVE  
117 - ? 'warning'  
118 - : text === DeviceStatusEnum.OFFLINE  
119 - ? 'error'  
120 - : 'success',  
121 - },  
122 - () => DeviceStatusNameEnum[text]  
123 - );  
124 - },  
125 - },  
126 - {  
127 - title: '别名/设备名称',  
128 - dataIndex: 'name',  
129 - customRender: ({ record }) => {  
130 - return h('div', [  
131 - h(  
132 - 'div',  
133 - {  
134 - class: 'cursor-pointer',  
135 - onClick: async () => {  
136 - await copy(record.name);  
137 - if (unref(copied)) createMessage.success('复制成功~');  
138 - },  
139 - },  
140 - [  
141 - record.alias && h('div', { class: 'truncate' }, record.alias),  
142 - h('div', { class: 'text-blue-400 truncate' }, record.name),  
143 - ]  
144 - ),  
145 - ]);  
146 - },  
147 - },  
148 - {  
149 - title: '设备类型',  
150 - dataIndex: 'deviceType',  
151 - customRender: ({ text }) => {  
152 - return h(Tag, { color: 'success' }, () => DeviceTypeNameEnum[text]);  
153 - },  
154 - },  
155 - {  
156 - title: '所属产品',  
157 - dataIndex: 'deviceProfile.name',  
158 - },  
159 - {  
160 - title: '所属组织',  
161 - dataIndex: 'organizationDTO.name',  
162 - },  
163 -];  
164 -  
165 -const TransferTableProps: BasicTableProps = {  
166 - formConfig: {  
167 - layout: 'inline',  
168 - labelWidth: 80,  
169 - schemas: deviceTableFormSchema,  
170 - actionColOptions: { span: 6 },  
171 - },  
172 - size: 'small',  
173 - maxHeight: 240,  
174 - useSearchForm: true,  
175 - columns: deviceTableColumn,  
176 - showIndexColumn: false,  
177 - fetchSetting: FETCH_SETTING,  
178 -} as BasicTableProps;  
179 -  
180 -export const modeForm = (submitFn?: Function): FormSchema[] => { 73 +export const modeForm = (): FormSchema[] => {
181 return [ 74 return [
182 { 75 {
183 field: BasicInfoFormField.CONVERT_CONFIG_ID, 76 field: BasicInfoFormField.CONVERT_CONFIG_ID,
@@ -239,69 +132,20 @@ export const modeForm = (submitFn?: Function): FormSchema[] => { @@ -239,69 +132,20 @@ export const modeForm = (submitFn?: Function): FormSchema[] => {
239 changeEvent: 'update:value', 132 changeEvent: 'update:value',
240 componentProps: ({ formActionType }) => { 133 componentProps: ({ formActionType }) => {
241 const { getFieldsValue } = formActionType; 134 const { getFieldsValue } = formActionType;
242 - const values = getFieldsValue();  
243 - const convertConfigId = Reflect.get(values, BasicInfoFormField.CONVERT_CONFIG_ID);  
244 - const devices = Reflect.get(values, BasicInfoFormField.DATA_SOURCE_DEVICE);  
245 -  
246 return { 135 return {
247 - labelField: 'name',  
248 - valueField: 'tbDeviceId',  
249 - primaryKey: 'tbDeviceId',  
250 - pendingTableProps: {  
251 - ...TransferTableProps,  
252 - api: devicePage,  
253 - beforeFetch: (params) => {  
254 - const values = getFieldsValue();  
255 - const deviceProfileIds = Reflect.get(values, BasicInfoFormField.DATA_SOURCE_PRODUCT);  
256 - const convertConfigId = Reflect.get(values, BasicInfoFormField.CONVERT_CONFIG_ID);  
257 - if (convertConfigId) {  
258 - Object.assign(params, { convertConfigId, selected: false });  
259 - }  
260 - return { ...params, deviceProfileIds };  
261 - },  
262 - } as BasicTableProps,  
263 - selectedTableProps: {  
264 - ...TransferTableProps,  
265 - // api  
266 - api: !!(convertConfigId && devices) ? devicePage : undefined,  
267 - beforeFetch: (params) => {  
268 - const values = getFieldsValue();  
269 - const deviceProfileIds = Reflect.get(values, BasicInfoFormField.DATA_SOURCE_PRODUCT);  
270 - const convertConfigId = Reflect.get(values, BasicInfoFormField.CONVERT_CONFIG_ID);  
271 - if (convertConfigId) {  
272 - Object.assign(params, { convertConfigId, selected: true });  
273 - }  
274 - return { ...params, deviceProfileIds };  
275 - },  
276 - } as BasicTableProps,  
277 - initSelectedOptions: async ({ setSelectedTotal }) => { 136 + getPendingTableParams: () => {
278 const values = getFieldsValue(); 137 const values = getFieldsValue();
279 const convertConfigId = Reflect.get(values, BasicInfoFormField.CONVERT_CONFIG_ID); 138 const convertConfigId = Reflect.get(values, BasicInfoFormField.CONVERT_CONFIG_ID);
280 const deviceProfileIds = Reflect.get(values, BasicInfoFormField.DATA_SOURCE_PRODUCT); 139 const deviceProfileIds = Reflect.get(values, BasicInfoFormField.DATA_SOURCE_PRODUCT);
281 - const devices = Reflect.get(values, BasicInfoFormField.DATA_SOURCE_DEVICE);  
282 - if (convertConfigId && devices) {  
283 - const { items, total } = await devicePage({  
284 - page: 1,  
285 - pageSize: 10,  
286 - convertConfigId: values[BasicInfoFormField.CONVERT_CONFIG_ID],  
287 - deviceProfileIds,  
288 - selected: true,  
289 - });  
290 - setSelectedTotal(total);  
291 - return items;  
292 - }  
293 - return [];  
294 - },  
295 - onSelectedAfter: async () => {  
296 - submitFn && (await submitFn(false)); 140 + return { convertConfigId, deviceProfileIds };
297 }, 141 },
298 - onRemoveAfter: async ({ reloadSelected }) => {  
299 - submitFn && (await submitFn(false));  
300 - reloadSelected();  
301 - },  
302 - transformValue: (_selectedRowKeys: string[], selectedRows: DeviceRecord[]) => {  
303 - return handleGroupDevice(selectedRows); 142 + getSelectedTableParams: () => {
  143 + const values = getFieldsValue();
  144 + const convertConfigId = Reflect.get(values, BasicInfoFormField.CONVERT_CONFIG_ID);
  145 + const deviceProfileIds = Reflect.get(values, BasicInfoFormField.DATA_SOURCE_PRODUCT);
  146 + return { convertConfigId, deviceProfileIds };
304 }, 147 },
  148 + transformValue: handleGroupDevice,
305 }; 149 };
306 }, 150 },
307 }, 151 },
1 -<template>  
2 - <div class="transfer-config-mode">  
3 - <BasicForm :showSubmitButton="false" @register="register">  
4 - <template #uploadAdd1="{ field }">  
5 - <span style="display: none">{{ field }}</span>  
6 - <a-upload-dragger  
7 - v-model:fileList="fileList1"  
8 - name="file"  
9 - :key="1"  
10 - :multiple="false"  
11 - @change="handleChange('T', $event)"  
12 - :before-upload="() => false"  
13 - >  
14 - <p class="ant-upload-drag-icon">  
15 - <InboxOutlined />  
16 - </p>  
17 - <p class="ant-upload-text">点击或将文件拖拽到这里上传</p>  
18 - <p class="ant-upload-hint">  
19 - 支持扩展名:.jpeg .png .jpg ...  
20 - <br />  
21 - 文件大小:最大支持5M  
22 - </p>  
23 - </a-upload-dragger>  
24 - </template>  
25 - <template #showImg1="{ field }">  
26 - <span style="display: none">{{ field }}</span>  
27 - <img  
28 - v-if="showImg1"  
29 - :src="showImg1Pic"  
30 - alt="avatar"  
31 - style="width: 6.25rem; height: 6.25rem"  
32 - />  
33 - </template>  
34 - <div style="margin-top: 50px"></div>  
35 - <template #uploadAdd2="{ field }">  
36 - <span style="display: none">{{ field }}</span>  
37 - <a-upload-dragger  
38 - v-model:fileList="fileList2"  
39 - name="file"  
40 - :key="2"  
41 - :multiple="false"  
42 - @change="handleChange('F', $event)"  
43 - :before-upload="() => false"  
44 - >  
45 - <p class="ant-upload-drag-icon">  
46 - <InboxOutlined />  
47 - </p>  
48 - <p class="ant-upload-text">点击或将文件拖拽到这里上传</p>  
49 - <p class="ant-upload-hint">  
50 - 支持扩展名:.jpeg .png .jpg ...  
51 - <br />  
52 - 文件大小:最大支持5M  
53 - </p>  
54 - </a-upload-dragger>  
55 - </template>  
56 - <template #showImg2="{ field }">  
57 - <span style="display: none">{{ field }}</span>  
58 - <img  
59 - v-if="showImg2"  
60 - :src="showImg2Pic"  
61 - alt="avatar"  
62 - style="width: 6.25rem; height: 6.25rem"  
63 - />  
64 - </template>  
65 - <div style="margin-top: 50px"></div>  
66 - <template #uploadAdd3="{ field }">  
67 - <span style="display: none">{{ field }}</span>  
68 - <a-upload-dragger  
69 - v-model:fileList="fileList3"  
70 - name="file"  
71 - :key="3"  
72 - :multiple="false"  
73 - @change="handleChange('C', $event)"  
74 - :before-upload="() => false"  
75 - >  
76 - <p class="ant-upload-drag-icon">  
77 - <InboxOutlined />  
78 - </p>  
79 - <p class="ant-upload-text">点击或将文件拖拽到这里上传</p>  
80 - <p class="ant-upload-hint">  
81 - 支持扩展名:.jpeg .png .jpg ...  
82 - <br />  
83 - 文件大小:最大支持5M  
84 - </p>  
85 - </a-upload-dragger>  
86 - </template>  
87 - <template #showImg3="{ field }">  
88 - <span style="display: none">{{ field }}</span>  
89 - <img  
90 - v-if="showImg3"  
91 - :src="showImg3Pic"  
92 - alt="avatar"  
93 - style="width: 6.25rem; height: 6.25rem"  
94 - />  
95 - </template>  
96 - </BasicForm>  
97 - </div>  
98 -</template>  
99 -<script lang="ts">  
100 - import { defineComponent, ref, reactive, nextTick } from 'vue';  
101 - import { BasicForm, useForm } from '/@/components/Form';  
102 - import { CredentialsEnum, modeMqttForm } from '../config';  
103 - import { InboxOutlined } from '@ant-design/icons-vue';  
104 - import { Alert, Divider, Descriptions, Upload } from 'ant-design-vue';  
105 - import { uploadApi } from '/@/api/personal/index';  
106 - import { useMessage } from '/@/hooks/web/useMessage';  
107 -  
108 - export default defineComponent({  
109 - components: {  
110 - BasicForm,  
111 - [Alert.name]: Alert,  
112 - [Divider.name]: Divider,  
113 - [Descriptions.name]: Descriptions,  
114 - [Descriptions.Item.name]: Descriptions.Item,  
115 - InboxOutlined,  
116 - [Upload.Dragger.name]: Upload.Dragger,  
117 - },  
118 - emits: ['next', 'prev', 'register'],  
119 - setup(_, { emit }) {  
120 - const showImg1 = ref(false);  
121 - const showImg1Pic = ref('');  
122 - const showImg2 = ref(false);  
123 - const showImg2Pic = ref('');  
124 - const showImg3 = ref(false);  
125 - const showImg3Pic = ref('');  
126 - const { createMessage } = useMessage();  
127 - let caCertFileName = ref('');  
128 - let privateKeyFileName = ref('');  
129 - let certFileName = ref('');  
130 - let fileList1: any = ref<[]>([]);  
131 - let fileList2: any = ref<[]>([]);  
132 - let fileList3: any = ref<[]>([]);  
133 - const credentialsV: any = reactive({  
134 - credentials: {  
135 - type: '',  
136 - },  
137 - });  
138 - const sonValues: any = reactive({  
139 - configuration: {},  
140 - });  
141 - const [register, { validate, setFieldsValue, resetFields: defineClearFunc }] = useForm({  
142 - labelWidth: 120,  
143 - schemas: modeMqttForm,  
144 - actionColOptions: {  
145 - span: 14,  
146 - },  
147 - resetButtonOptions: {  
148 - text: '上一步',  
149 - },  
150 - resetFunc: customResetFunc,  
151 - submitFunc: customSubmitFunc,  
152 - });  
153 -  
154 - /**  
155 - * 上传图片  
156 - */  
157 - const handleChange = async (e, { file }) => {  
158 - if (file.status === 'removed') {  
159 - if (e == 'T') {  
160 - fileList1.value = [];  
161 - showImg1.value = false;  
162 - showImg1Pic.value = '';  
163 - caCertFileName.value = '';  
164 - } else if (e == 'F') {  
165 - fileList2.value = [];  
166 - showImg2.value = false;  
167 - showImg2Pic.value = '';  
168 - certFileName.value = '';  
169 - } else {  
170 - fileList3.value = [];  
171 - showImg3.value = false;  
172 - showImg3Pic.value = '';  
173 - privateKeyFileName.value = '';  
174 - }  
175 - } else {  
176 - const isLt5M = file.size / 1024 / 1024 < 5;  
177 - if (!isLt5M) {  
178 - createMessage.error('图片大小不能超过5MB!');  
179 - } else {  
180 - e == 'T'  
181 - ? (fileList1.value = [file])  
182 - : e == 'F'  
183 - ? (fileList2.value = [file])  
184 - : (fileList3.value = [file]);  
185 - const formData = new FormData();  
186 - formData.append('file', file);  
187 - const response = await uploadApi(formData);  
188 - if (response.fileStaticUri) {  
189 - if (e == 'T') {  
190 - caCertFileName.value = response.fileStaticUri;  
191 - const iscaCertFileNamePic = caCertFileName.value.split('.').pop();  
192 - if (  
193 - iscaCertFileNamePic == 'jpg' ||  
194 - iscaCertFileNamePic == 'png' ||  
195 - iscaCertFileNamePic == 'jpeg' ||  
196 - iscaCertFileNamePic == 'gif'  
197 - ) {  
198 - showImg1.value = true;  
199 - showImg1Pic.value = response.fileStaticUri;  
200 - } else {  
201 - showImg1.value = false;  
202 - }  
203 - } else if (e == 'F') {  
204 - certFileName.value = response.fileStaticUri;  
205 - const iscertFileNamePic = certFileName.value.split('.').pop();  
206 - if (  
207 - iscertFileNamePic == 'jpg' ||  
208 - iscertFileNamePic == 'png' ||  
209 - iscertFileNamePic == 'jpeg' ||  
210 - iscertFileNamePic == 'gif'  
211 - ) {  
212 - showImg2.value = true;  
213 - showImg2Pic.value = response.fileStaticUri;  
214 - } else {  
215 - showImg2.value = false;  
216 - }  
217 - } else {  
218 - privateKeyFileName.value = response.fileStaticUri;  
219 - const isprivateKeyFileNamePic = privateKeyFileName.value.split('.').pop();  
220 - if (  
221 - isprivateKeyFileNamePic == 'jpg' ||  
222 - isprivateKeyFileNamePic == 'png' ||  
223 - isprivateKeyFileNamePic == 'jpeg' ||  
224 - isprivateKeyFileNamePic == 'gif'  
225 - ) {  
226 - showImg3.value = true;  
227 - showImg3Pic.value = response.fileStaticUri;  
228 - } else {  
229 - showImg3.value = false;  
230 - }  
231 - }  
232 - }  
233 - }  
234 - }  
235 - };  
236 - const setStepTwoFieldsValueFunc = (v, v1, v2) => {  
237 - setFieldsValue(v);  
238 - setFieldsValue({  
239 - name: v1,  
240 - description: v2,  
241 - });  
242 - setFieldsValue({  
243 - password: v.credentials?.password,  
244 - username: v.credentials?.username,  
245 - type: v.credentials?.type,  
246 - });  
247 - fileList1.value = [  
248 - {  
249 - name: v.credentials?.caCertFileName.slice(39),  
250 - uid: '1',  
251 - },  
252 - ];  
253 - fileList2.value = [  
254 - {  
255 - name: v.credentials?.certFileName.slice(39),  
256 - uid: '2',  
257 - },  
258 - ];  
259 - fileList3.value = [  
260 - {  
261 - name: v.credentials?.privateKeyFileName.slice(39),  
262 - uid: '3',  
263 - },  
264 - ];  
265 - caCertFileName.value = v.credentials?.caCertFileName;  
266 - certFileName.value = v.credentials?.certFileName;  
267 - privateKeyFileName.value = v.credentials?.privateKeyFileName;  
268 - const iscaCertFileNamePic = v.credentials?.caCertFileName.split('.').pop();  
269 - const iscertFileNamePic = v.credentials?.certFileName.split('.').pop();  
270 - const isprivateKeyFileNamePic = v.credentials?.privateKeyFileName.split('.').pop();  
271 - if (  
272 - iscaCertFileNamePic == 'jpg' ||  
273 - iscaCertFileNamePic == 'png' ||  
274 - iscaCertFileNamePic == 'jpeg' ||  
275 - iscaCertFileNamePic == 'gif'  
276 - ) {  
277 - showImg1.value = true;  
278 - showImg1Pic.value = v.credentials?.caCertFileName;  
279 - } else {  
280 - showImg1.value = false;  
281 - }  
282 - if (  
283 - iscertFileNamePic == 'jpg' ||  
284 - iscertFileNamePic == 'png' ||  
285 - iscertFileNamePic == 'jpeg' ||  
286 - iscertFileNamePic == 'gif'  
287 - ) {  
288 - showImg2.value = true;  
289 - showImg2Pic.value = v.credentials?.certFileName;  
290 - } else {  
291 - showImg2.value = false;  
292 - }  
293 - if (  
294 - isprivateKeyFileNamePic == 'jpg' ||  
295 - isprivateKeyFileNamePic == 'png' ||  
296 - isprivateKeyFileNamePic == 'jpeg' ||  
297 - isprivateKeyFileNamePic == 'gif'  
298 - ) {  
299 - showImg3.value = true;  
300 - showImg3Pic.value = v.credentials?.privateKeyFileName;  
301 - } else {  
302 - showImg3.value = false;  
303 - }  
304 - };  
305 - const customClearStepTwoValueFunc = async () => {  
306 - nextTick(() => {  
307 - defineClearFunc();  
308 - fileList1.value = [];  
309 - fileList2.value = [];  
310 - fileList3.value = [];  
311 - caCertFileName.value = '';  
312 - privateKeyFileName.value = '';  
313 - certFileName.value = '';  
314 - showImg1.value = false;  
315 - showImg1Pic.value = '';  
316 - showImg2.value = false;  
317 - showImg2Pic.value = '';  
318 - showImg3.value = false;  
319 - showImg3Pic.value = '';  
320 - });  
321 - };  
322 - async function customResetFunc() {  
323 - emit('prev');  
324 - }  
325 - async function customSubmitFunc() {  
326 - try {  
327 - const values = await validate();  
328 - emit('next', values);  
329 - } catch (error) {  
330 - } finally {  
331 - }  
332 - }  
333 - const getSonValueFunc = async () => {  
334 - sonValues.configuration = await validate();  
335 - credentialsV.credentials.type = sonValues.configuration.type;  
336 - if (credentialsV.credentials.type == CredentialsEnum.IS_BASIC) {  
337 - credentialsV.credentials.username = sonValues.configuration.username;  
338 - credentialsV.credentials.password = sonValues.configuration.password;  
339 - sonValues.configuration.username = undefined;  
340 - sonValues.configuration.password = undefined;  
341 - } else if (credentialsV.credentials.type == CredentialsEnum.IS_PEM) {  
342 - credentialsV.credentials.caCertFileName = caCertFileName.value;  
343 - credentialsV.credentials.certFileName = certFileName.value;  
344 - credentialsV.credentials.privateKeyFileName = privateKeyFileName.value;  
345 - }  
346 - if (!sonValues.configuration.clientId) {  
347 - sonValues.configuration.clientId = null;  
348 - }  
349 - Object.assign(sonValues.configuration, credentialsV);  
350 - return sonValues;  
351 - };  
352 - return {  
353 - getSonValueFunc,  
354 - register,  
355 - setStepTwoFieldsValueFunc,  
356 - customClearStepTwoValueFunc,  
357 - fileList1,  
358 - fileList2,  
359 - fileList3,  
360 - handleChange,  
361 - caCertFileName,  
362 - privateKeyFileName,  
363 - certFileName,  
364 - showImg1,  
365 - showImg1Pic,  
366 - showImg2,  
367 - showImg2Pic,  
368 - showImg3,  
369 - showImg3Pic,  
370 - };  
371 - },  
372 - });  
373 -</script>  
374 -  
375 -<style lang="less" scoped>  
376 - :deep(.ant-col-24) {  
377 - margin-bottom: 20px !important;  
378 - }  
379 -  
380 - :deep(.ant-btn-default) {  
381 - color: white;  
382 - background: #377dff;  
383 - }  
384 -</style>  
@@ -13,6 +13,9 @@ import { EntryCategoryComponentEnum } from '../enum/category'; @@ -13,6 +13,9 @@ import { EntryCategoryComponentEnum } from '../enum/category';
13 13
14 const ignoreNodeKeys: string[] = [EntryCategoryComponentEnum.INPUT]; 14 const ignoreNodeKeys: string[] = [EntryCategoryComponentEnum.INPUT];
15 15
  16 +export const SOURCE_HANDLE = '__handle-right';
  17 +export const TARGET_HANDLE = '__handle-left';
  18 +
16 export function useBasicDataTransform() { 19 export function useBasicDataTransform() {
17 const nodeConfigMap = new Map<string, NodeData>(); 20 const nodeConfigMap = new Map<string, NodeData>();
18 21
@@ -21,7 +24,7 @@ export function useBasicDataTransform() { @@ -21,7 +24,7 @@ export function useBasicDataTransform() {
21 const category = allComponents[key as RuleNodeTypeEnum]; 24 const category = allComponents[key as RuleNodeTypeEnum];
22 for (const nodeConfig of category.components) { 25 for (const nodeConfig of category.components) {
23 const { clazz } = nodeConfig; 26 const { clazz } = nodeConfig;
24 - nodeConfigMap.set(clazz, { config: nodeConfig, category: category.category }); 27 + nodeConfigMap.set(clazz, { config: nodeConfig, categoryConfig: category.category });
25 } 28 }
26 } 29 }
27 } 30 }
@@ -49,8 +52,7 @@ export function useBasicDataTransform() { @@ -49,8 +52,7 @@ export function useBasicDataTransform() {
49 const { connections, nodes, firstNodeIndex } = unref(ruleChain); 52 const { connections, nodes, firstNodeIndex } = unref(ruleChain);
50 const indexMap = new Map<number, BasicNodeBindData>(); 53 const indexMap = new Map<number, BasicNodeBindData>();
51 const groupByConnections = new Map<string, string[]>(); 54 const groupByConnections = new Map<string, string[]>();
52 - const SOURCE_HANDLE = '__handle-right';  
53 - const TARGET_HANDLE = '__handle-left'; 55 +
54 const SEPARATOR = ','; 56 const SEPARATOR = ',';
55 const edges: Elements = []; 57 const edges: Elements = [];
56 const { getAddedgesParams } = useAddEdges(); 58 const { getAddedgesParams } = useAddEdges();
@@ -207,6 +209,8 @@ export function useBasicDataTransform() { @@ -207,6 +209,8 @@ export function useBasicDataTransform() {
207 function getNodes(nodesRef: Ref<GraphNode[]> | GraphNode[], removeId: boolean) { 209 function getNodes(nodesRef: Ref<GraphNode[]> | GraphNode[], removeId: boolean) {
208 const nodes: BasicNodeBindData[] = []; 210 const nodes: BasicNodeBindData[] = [];
209 211
  212 + let offsetX = 0;
  213 + let offsetY = 0;
210 for (const node of unref(nodesRef)) { 214 for (const node of unref(nodesRef)) {
211 const nodeData = node.data as NodeData; 215 const nodeData = node.data as NodeData;
212 216
@@ -214,16 +218,31 @@ export function useBasicDataTransform() { @@ -214,16 +218,31 @@ export function useBasicDataTransform() {
214 218
215 const data = nodeData.data; 219 const data = nodeData.data;
216 220
217 - nodes.push(  
218 - Object.assign(  
219 - mergeData(data, nodeData, node),  
220 - nodeData.created && !removeId  
221 - ? ({  
222 - id: { id: node.id, entityType: RuleChainEntityType.RULE_NODE },  
223 - } as BasicNodeBindData)  
224 - : {}  
225 - ) 221 + const resultNode = Object.assign(
  222 + mergeData(data, nodeData, node),
  223 + nodeData.created && !removeId
  224 + ? ({
  225 + id: { id: node.id, entityType: RuleChainEntityType.RULE_NODE },
  226 + } as BasicNodeBindData)
  227 + : {}
226 ); 228 );
  229 +
  230 + if (resultNode.additionalInfo.layoutX < offsetX) offsetX = resultNode.additionalInfo.layoutX;
  231 + if (resultNode.additionalInfo.layoutY < offsetY) offsetY = resultNode.additionalInfo.layoutY;
  232 +
  233 + nodes.push(resultNode);
  234 + }
  235 +
  236 + /**
  237 + * compatible thingsboard rule chain designer.
  238 + * thingsboard rule chain designer does not have negative coordinated.
  239 + */
  240 + if (offsetX < 0 || offsetY < 0) {
  241 + nodes.forEach((node) => {
  242 + const { layoutX = 0, layoutY = 0 } = node.additionalInfo || {};
  243 + node.additionalInfo!.layoutX = layoutX + Math.abs(offsetX);
  244 + node.additionalInfo!.layoutY = layoutY + Math.abs(offsetY);
  245 + });
227 } 246 }
228 247
229 return nodes; 248 return nodes;
@@ -266,9 +285,22 @@ export function useBasicDataTransform() { @@ -266,9 +285,22 @@ export function useBasicDataTransform() {
266 nodes: Ref<GraphNode[]> | GraphNode[] = [], 285 nodes: Ref<GraphNode[]> | GraphNode[] = [],
267 edges: Ref<GraphEdge[]> | GraphEdge[] = [] 286 edges: Ref<GraphEdge[]> | GraphEdge[] = []
268 ) { 287 ) {
  288 + let flag = true;
  289 +
  290 + if (unref(nodes).length <= 1) flag = false;
  291 +
269 const rootNode: GraphNode[] = []; 292 const rootNode: GraphNode[] = [];
270 293
271 - let flag = true; 294 + for (const edge of unref(edges)) {
  295 + if (!unref(nodes).find((node) => node.id === edge.source)) rootNode.push(edge.targetNode);
  296 + }
  297 +
  298 + for (const node of unref(nodes)) {
  299 + if (!unref(edges).some((edge) => edge.target === node.id)) rootNode.push(node);
  300 + }
  301 +
  302 + if (rootNode.length > 1) return { flag: false, firstNode: null };
  303 +
272 for (const node of unref(nodes)) { 304 for (const node of unref(nodes)) {
273 const list = unref(edges).filter( 305 const list = unref(edges).filter(
274 (edge) => edge.source === node.id || edge.target === node.id 306 (edge) => edge.source === node.id || edge.target === node.id
@@ -278,14 +310,6 @@ export function useBasicDataTransform() { @@ -278,14 +310,6 @@ export function useBasicDataTransform() {
278 flag = false; 310 flag = false;
279 break; 311 break;
280 } 312 }
281 -  
282 - if (!list.filter((edge) => edge.target === node.id).length) {  
283 - if (!rootNode.length) rootNode.push(node);  
284 - else {  
285 - flag = false;  
286 - break;  
287 - }  
288 - }  
289 } 313 }
290 314
291 return { flag, firstNode: rootNode[0] || null }; 315 return { flag, firstNode: rootNode[0] || null };
@@ -5,7 +5,7 @@ import { useAddNodes } from './useAddNodes'; @@ -5,7 +5,7 @@ import { useAddNodes } from './useAddNodes';
5 import { buildUUID } from '/@/utils/uuid'; 5 import { buildUUID } from '/@/utils/uuid';
6 import { Ref, toRaw, unref } from 'vue'; 6 import { Ref, toRaw, unref } from 'vue';
7 import { useSaveAndRedo } from './useSaveAndRedo'; 7 import { useSaveAndRedo } from './useSaveAndRedo';
8 -import { isUnDef } from '/@/utils/is'; 8 +import { isNullOrUnDef, isUnDef } from '/@/utils/is';
9 import { useAddEdges } from './useAddEdges'; 9 import { useAddEdges } from './useAddEdges';
10 import { EdgeData } from '../types/node'; 10 import { EdgeData } from '../types/node';
11 import { CreateNodeModal } from '../src/components/CreateNodeModal'; 11 import { CreateNodeModal } from '../src/components/CreateNodeModal';
@@ -13,9 +13,11 @@ import { CreateEdgeModal } from '../src/components/CreateEdgeModal'; @@ -13,9 +13,11 @@ import { CreateEdgeModal } from '../src/components/CreateEdgeModal';
13 import { UpdateNodeDrawer } from '../src/components/UpdateNodeDrawer'; 13 import { UpdateNodeDrawer } from '../src/components/UpdateNodeDrawer';
14 import { UpdateEdgeDrawer } from '../src/components/UpdateEdgeDrawer'; 14 import { UpdateEdgeDrawer } from '../src/components/UpdateEdgeDrawer';
15 import { CreateRuleChainModal } from '../src/components/CreateRuleChainModal'; 15 import { CreateRuleChainModal } from '../src/components/CreateRuleChainModal';
16 -import { useBasicDataTransform } from './useBasicDataTransform'; 16 +import { SOURCE_HANDLE, TARGET_HANDLE, useBasicDataTransform } from './useBasicDataTransform';
17 import { cloneDeep } from 'lodash-es'; 17 import { cloneDeep } from 'lodash-es';
18 import { useNewNode } from './useNewNode'; 18 import { useNewNode } from './useNewNode';
  19 +import { RuleChainDetail } from '../types/ruleNode';
  20 +import { saveRuleChainData, saveRuleChainDetail } from '/@/api/ruleDesigner';
19 21
20 interface HandleContextMenuActionParamsType { 22 interface HandleContextMenuActionParamsType {
21 menuType: RuleContextMenuEnum; 23 menuType: RuleContextMenuEnum;
@@ -47,11 +49,15 @@ export function transformToRuleChain( @@ -47,11 +49,15 @@ export function transformToRuleChain(
47 const outputEdges = unref(edgesRef).filter((edge) => !nodeMap.has(edge.target)); 49 const outputEdges = unref(edgesRef).filter((edge) => !nodeMap.has(edge.target));
48 const outputEdgesId = outputEdges.map((edge) => edge.id); 50 const outputEdgesId = outputEdges.map((edge) => edge.id);
49 51
  52 + const inputEdges = unref(edgesRef).filter((edge) => !nodeMap.has(edge.source));
  53 + const inputEdgesId = inputEdges.map((edge) => edge.id);
  54 +
50 const { getOutputNodeConfig } = useNewNode(); 55 const { getOutputNodeConfig } = useNewNode();
51 const outputNode = outputEdges.map((edge) => { 56 const outputNode = outputEdges.map((edge) => {
52 const id = buildUUID(); 57 const id = buildUUID();
53 const name = (edge.data as EdgeData).data?.type?.join(' / ') || ''; 58 const name = (edge.data as EdgeData).data?.type?.join(' / ') || '';
54 edge.target = id; 59 edge.target = id;
  60 + edge.targetHandle = `${id}${TARGET_HANDLE}`;
55 return getOutputNodeConfig(name, edge.targetNode.position, id); 61 return getOutputNodeConfig(name, edge.targetNode.position, id);
56 }); 62 });
57 63
@@ -61,9 +67,9 @@ export function transformToRuleChain( @@ -61,9 +67,9 @@ export function transformToRuleChain(
61 67
62 const { firstNode } = validateCanCreateRuleChain(nodesRef, edgesRef); 68 const { firstNode } = validateCanCreateRuleChain(nodesRef, edgesRef);
63 69
64 - const firstNodeIndex = nodesRef.findIndex((node) => node.id === firstNode.id); 70 + const firstNodeIndex = nodesRef.findIndex((node) => node.id === firstNode?.id);
65 71
66 - return { connections, nodes, firstNodeIndex, outputEdgesId }; 72 + return { connections, nodes, firstNodeIndex, outputEdgesId, inputEdgesId };
67 } 73 }
68 74
69 export const NODE_WIDTH = 176; 75 export const NODE_WIDTH = 176;
@@ -87,20 +93,13 @@ function getElementsCenter(nodes: Ref<GraphNode[]> | GraphNode[] = []) { @@ -87,20 +93,13 @@ function getElementsCenter(nodes: Ref<GraphNode[]> | GraphNode[] = []) {
87 continue; 93 continue;
88 } 94 }
89 95
90 - if (x < leftTopX!) {  
91 - leftTopX = x;  
92 - if (y < leftTopY!) {  
93 - leftTopY = y;  
94 - }  
95 - continue;  
96 - } 96 + if (x < leftTopX!) leftTopX = x;
97 97
98 - if (x + NODE_WIDTH > rightBottomX!) {  
99 - rightBottomX = x + NODE_WIDTH;  
100 - if (y + NODE_HEIGHT > rightBottomY!) {  
101 - rightBottomY = y + NODE_HEIGHT;  
102 - }  
103 - } 98 + if (y < leftTopY!) leftTopY = y;
  99 +
  100 + if (x + NODE_WIDTH > rightBottomX!) rightBottomX = x + NODE_WIDTH;
  101 +
  102 + if (y + NODE_HEIGHT > rightBottomY!) rightBottomY = y + NODE_HEIGHT;
104 } 103 }
105 104
106 return { 105 return {
@@ -109,6 +108,60 @@ function getElementsCenter(nodes: Ref<GraphNode[]> | GraphNode[] = []) { @@ -109,6 +108,60 @@ function getElementsCenter(nodes: Ref<GraphNode[]> | GraphNode[] = []) {
109 }; 108 };
110 } 109 }
111 110
  111 +function createRuleChainNode({
  112 + originX,
  113 + originY,
  114 + outputEdges,
  115 + inputEdges,
  116 + ruleChainDetail,
  117 +}: {
  118 + originX: number;
  119 + originY: number;
  120 + ruleChainDetail: RuleChainDetail;
  121 + outputEdges: Ref<(GraphEdge | undefined)[]> | (GraphEdge | undefined)[];
  122 + inputEdges: Ref<(GraphEdge | undefined)[]> | (GraphEdge | undefined)[];
  123 +}) {
  124 + const { getRuleChainNodeConfig } = useNewNode();
  125 + const { getAddedgesParams } = useAddEdges();
  126 + const nodeId = buildUUID();
  127 +
  128 + const ruleChainNode = getRuleChainNodeConfig({
  129 + name: ruleChainDetail.name,
  130 + position: { x: originX - NODE_WIDTH / 2, y: originY - NODE_HEIGHT / 2 },
  131 + ruleChainId: ruleChainDetail.id.id,
  132 + id: nodeId,
  133 + });
  134 +
  135 + const newInputEdges = unref(inputEdges).map((edge) =>
  136 + getAddedgesParams(
  137 + {
  138 + source: edge!.source,
  139 + target: nodeId,
  140 + sourceHandle: `${edge?.source}${SOURCE_HANDLE}`,
  141 + targetHandle: `${nodeId}${TARGET_HANDLE}`,
  142 + },
  143 + toRaw(unref(edge!.data as EdgeData).data)
  144 + )
  145 + );
  146 +
  147 + const newOutputEdges = unref(outputEdges).map((edge) =>
  148 + getAddedgesParams(
  149 + {
  150 + source: nodeId,
  151 + target: edge!.target,
  152 + sourceHandle: `${nodeId}${SOURCE_HANDLE}`,
  153 + targetHandle: `${edge?.target}${TARGET_HANDLE}`,
  154 + },
  155 + toRaw(unref(edge!.data as EdgeData).data)
  156 + )
  157 + );
  158 +
  159 + return {
  160 + nodes: [ruleChainNode],
  161 + edges: [...newInputEdges, ...newOutputEdges],
  162 + };
  163 +}
  164 +
112 export function useContextMenuAction() { 165 export function useContextMenuAction() {
113 const copy = (params: HandleContextMenuActionParamsType) => { 166 const copy = (params: HandleContextMenuActionParamsType) => {
114 const { node } = params; 167 const { node } = params;
@@ -248,35 +301,51 @@ export function useContextMenuAction() { @@ -248,35 +301,51 @@ export function useContextMenuAction() {
248 useSaveAndRedoActionType?.handleRedoChange(flowActionType!); 301 useSaveAndRedoActionType?.handleRedoChange(flowActionType!);
249 }; 302 };
250 303
251 - const createRuleChain = async (_params: HandleContextMenuActionParamsType) => {  
252 - // const { useSaveAndRedoActionType, modalActionType, flowActionType } = params;  
253 - // const { createRuleChainModalActionType } = modalActionType;  
254 - // const result = (await unref(createRuleChainModalActionType)?.openCreateRuleChainModal()) as {  
255 - // name: string;  
256 - // additionalInfo: { description: string };  
257 - // };  
258 - // const ruleChainDetail = await saveRuleChainDetail(  
259 - // Object.assign(result, { debugger: false, type: 'CORE' }) as Partial<RuleChainDetail>  
260 - // );  
261 - // const selectedNodes = unref(flowActionType?.getSelectedNodes);  
262 - // const selectedEdges = unref(flowActionType?.getSelectedEdges);  
263 - // const { firstNodeIndex, connections, nodes, outputEdgesId } = transformToRuleChain(  
264 - // selectedNodes,  
265 - // selectedEdges  
266 - // );  
267 - // await saveRuleChainData({  
268 - // firstNodeIndex,  
269 - // connections,  
270 - // nodes,  
271 - // ruleChainId: ruleChainDetail.id,  
272 - // });  
273 - // const outputEdges = outputEdgesId.map((id) => flowActionType?.findEdge(id));  
274 - // console.log(getElementsCenter(unref(selectedNodes)));  
275 - // const { originX, originY } = getElementsCenter(unref(selectedNodes));  
276 - // const {} = useNewNode();  
277 - // flowActionType?.removeNodes(selectedNodes || []);  
278 - // flowActionType?.removeEdges(selectedEdges || []);  
279 - // useSaveAndRedoActionType?.triggerChange(); 304 + const createRuleChain = async (params: HandleContextMenuActionParamsType) => {
  305 + const { useSaveAndRedoActionType, modalActionType, flowActionType } = params;
  306 + const { createRuleChainModalActionType } = modalActionType;
  307 + const result = (await unref(createRuleChainModalActionType)?.openCreateRuleChainModal()) as {
  308 + name: string;
  309 + additionalInfo: { description: string };
  310 + };
  311 +
  312 + const ruleChainDetail = await saveRuleChainDetail(
  313 + Object.assign(result, { debugger: false, type: 'CORE' }) as Partial<RuleChainDetail>
  314 + );
  315 +
  316 + const selectedNodes = unref(flowActionType?.getSelectedNodes);
  317 + const selectedEdges = unref(flowActionType?.getSelectedEdges);
  318 + const { firstNodeIndex, connections, nodes, outputEdgesId, inputEdgesId } =
  319 + transformToRuleChain(selectedNodes, selectedEdges);
  320 +
  321 + await saveRuleChainData({
  322 + firstNodeIndex,
  323 + connections: connections.filter(
  324 + (connection) => !isNullOrUnDef(connection.fromIndex) && !isNullOrUnDef(connection.toIndex)
  325 + ),
  326 + nodes,
  327 + ruleChainId: ruleChainDetail.id,
  328 + });
  329 +
  330 + const outputEdges = outputEdgesId.map((id) => flowActionType?.findEdge(id));
  331 + const inputEdges = inputEdgesId.map((id) => flowActionType?.findEdge(id));
  332 +
  333 + const { originX, originY } = getElementsCenter(unref(selectedNodes));
  334 +
  335 + flowActionType?.removeNodes(selectedNodes || []);
  336 + flowActionType?.removeEdges(selectedEdges || []);
  337 +
  338 + const { nodes: newNode, edges: newEdges } = createRuleChainNode({
  339 + originX,
  340 + originY,
  341 + outputEdges,
  342 + inputEdges,
  343 + ruleChainDetail,
  344 + });
  345 +
  346 + flowActionType?.addNodes(newNode);
  347 + flowActionType?.addEdges(newEdges);
  348 + useSaveAndRedoActionType?.triggerChange();
280 }; 349 };
281 350
282 const handleContextMenuAction = (params: HandleContextMenuActionParamsType) => { 351 const handleContextMenuAction = (params: HandleContextMenuActionParamsType) => {
@@ -40,13 +40,23 @@ export function useNewNode() { @@ -40,13 +40,23 @@ export function useNewNode() {
40 return newNode; 40 return newNode;
41 }; 41 };
42 42
43 - const getRuleChainNodeConfig = (name: string, position: XYPosition, id?: string) => { 43 + const getRuleChainNodeConfig = ({
  44 + name,
  45 + position,
  46 + ruleChainId,
  47 + id,
  48 + }: {
  49 + name: string;
  50 + position: XYPosition;
  51 + ruleChainId: string;
  52 + id?: string;
  53 + }) => {
44 const { getAddNodesParams } = useAddNodes(); 54 const { getAddNodesParams } = useAddNodes();
45 const newNode = getAddNodesParams( 55 const newNode = getAddNodesParams(
46 position, 56 position,
47 { 57 {
48 ...new RuleChainConfig(), 58 ...new RuleChainConfig(),
49 - data: { name }, 59 + data: { name, configuration: { ruleChainId } },
50 }, 60 },
51 { 61 {
52 id, 62 id,
@@ -65,6 +65,7 @@ export function useRuleFlow(options: UseRuleFlowOptionsType) { @@ -65,6 +65,7 @@ export function useRuleFlow(options: UseRuleFlowOptionsType) {
65 minZoom: 1, 65 minZoom: 1,
66 panOnScroll: true, 66 panOnScroll: true,
67 selectionMode: SelectionMode.Partial, 67 selectionMode: SelectionMode.Partial,
  68 +
68 nodeTypes: { 69 nodeTypes: {
69 [NodeTypeEnum.CUSTOM]: markRaw(BasicNode) as NodeComponent, 70 [NodeTypeEnum.CUSTOM]: markRaw(BasicNode) as NodeComponent,
70 }, 71 },
@@ -79,14 +80,16 @@ export function useRuleFlow(options: UseRuleFlowOptionsType) { @@ -79,14 +80,16 @@ export function useRuleFlow(options: UseRuleFlowOptionsType) {
79 y: 0, 80 y: 0,
80 }, 81 },
81 isValidConnection(connection, elements) { 82 isValidConnection(connection, elements) {
  83 + if (!elements.targetNode || !elements.sourceNode) return false;
  84 +
82 const validateList = [validateInputAndOutput]; 85 const validateList = [validateInputAndOutput];
83 const targetData = elements.targetNode.data as NodeData; 86 const targetData = elements.targetNode.data as NodeData;
84 87
85 if ( 88 if (
86 - targetData.category?.validateConnection &&  
87 - isFunction(targetData.category.validateConnection) 89 + targetData.categoryConfig?.validateConnection &&
  90 + isFunction(targetData.categoryConfig.validateConnection)
88 ) 91 )
89 - validateList.push(targetData.category?.validateConnection); 92 + validateList.push(targetData.categoryConfig?.validateConnection);
90 93
91 if (targetData.config?.validateConnection && isFunction(targetData.config.validateConnection)) 94 if (targetData.config?.validateConnection && isFunction(targetData.config.validateConnection))
92 validateList.push(targetData.config.validateConnection); 95 validateList.push(targetData.config.validateConnection);
@@ -47,7 +47,6 @@ @@ -47,7 +47,6 @@
47 47
48 const setValue: CreateModalDefineExposeType['setFieldsValue'] = (value) => { 48 const setValue: CreateModalDefineExposeType['setFieldsValue'] = (value) => {
49 resetFields(); 49 resetFields();
50 - console.log(value);  
51 setFieldsValue({ 50 setFieldsValue({
52 ...value, 51 ...value,
53 ...(value?.[RelatedDeviceAttributeFieldsEnum.DEVICE_RELATIONS_QUERY] || {}), 52 ...(value?.[RelatedDeviceAttributeFieldsEnum.DEVICE_RELATIONS_QUERY] || {}),
@@ -14,6 +14,7 @@ export const RuleChainConfig: NodeItemConfigType = { @@ -14,6 +14,7 @@ export const RuleChainConfig: NodeItemConfigType = {
14 clazz: 'org.thingsboard.rule.engine.flow.TbRuleChainInputNode', 14 clazz: 'org.thingsboard.rule.engine.flow.TbRuleChainInputNode',
15 categoryType: RuleNodeTypeEnum.FLOW, 15 categoryType: RuleNodeTypeEnum.FLOW,
16 name: 'rule chain', 16 name: 'rule chain',
  17 + // backgroundColor: '#d6c4f1',
17 configurationDescriptor: { 18 configurationDescriptor: {
18 nodeDefinition: { 19 nodeDefinition: {
19 details: 20 details:
@@ -14,7 +14,7 @@ @@ -14,7 +14,7 @@
14 const handleClick = () => { 14 const handleClick = () => {
15 const { data } = props.nodeProps?.data || ({} as NodeData); 15 const { data } = props.nodeProps?.data || ({} as NodeData);
16 const { configuration } = (data || {}) as { configuration: Record<'ruleChainId', string> }; 16 const { configuration } = (data || {}) as { configuration: Record<'ruleChainId', string> };
17 - if (configuration.ruleChainId) { 17 + if (configuration?.ruleChainId) {
18 ROUTER.push(`/rule/chain/${configuration.ruleChainId}`); 18 ROUTER.push(`/rule/chain/${configuration.ruleChainId}`);
19 } 19 }
20 }; 20 };
@@ -36,8 +36,8 @@ @@ -36,8 +36,8 @@
36 36
37 const getIcon = computed(() => { 37 const getIcon = computed(() => {
38 const { icon } = unref(getNodeDefinition); 38 const { icon } = unref(getNodeDefinition);
39 - const { category } = unref(getData);  
40 - const { icon: categoryIcon } = category || {}; 39 + const { categoryConfig } = unref(getData);
  40 + const { icon: categoryIcon } = categoryConfig || {};
41 return icon || categoryIcon || 'tabler:circuit-ground'; 41 return icon || categoryIcon || 'tabler:circuit-ground';
42 }); 42 });
43 43
@@ -47,8 +47,8 @@ @@ -47,8 +47,8 @@
47 }); 47 });
48 48
49 const getBackgroundColor = computed(() => { 49 const getBackgroundColor = computed(() => {
50 - const { config, category } = unref(getData);  
51 - const { backgroundColor: categoryBackgroundColor } = category || {}; 50 + const { config, categoryConfig } = unref(getData);
  51 + const { backgroundColor: categoryBackgroundColor } = categoryConfig || {};
52 const { backgroundColor } = config || {}; 52 const { backgroundColor } = config || {};
53 return backgroundColor || categoryBackgroundColor; 53 return backgroundColor || categoryBackgroundColor;
54 }); 54 });
@@ -60,8 +60,8 @@ @@ -60,8 +60,8 @@
60 <section class="text-dark-900 text-xs"> 60 <section class="text-dark-900 text-xs">
61 <p class="mb-0 font-bold"> {{ getData?.data?.name }} </p> 61 <p class="mb-0 font-bold"> {{ getData?.data?.name }} </p>
62 <p class="mb-0 mt-2 text-gray-500 italic"> 62 <p class="mb-0 mt-2 text-gray-500 italic">
63 - {{ getData.category?.title }}  
64 - {{ getData?.category?.title && getData.config?.name ? '-' : '' }} 63 + {{ getData.categoryConfig?.title }}
  64 + {{ getData?.categoryConfig?.title && getData.config?.name ? '-' : '' }}
65 {{ getData.config?.name }} 65 {{ getData.config?.name }}
66 </p> 66 </p>
67 <p class="mt-1 mb-0 text-gray-500">{{ getData.data?.description }}</p> 67 <p class="mt-1 mb-0 text-gray-500">{{ getData.data?.description }}</p>
@@ -48,7 +48,7 @@ @@ -48,7 +48,7 @@
48 v-for="config in getCurrentCategoryNode.components" 48 v-for="config in getCurrentCategoryNode.components"
49 :key="config.clazz" 49 :key="config.clazz"
50 :config="config" 50 :config="config"
51 - :category="getCurrentCategoryNode.category" 51 + :categoryConfig="getCurrentCategoryNode.category"
52 /> 52 />
53 </body> 53 </body>
54 </section> 54 </section>
@@ -5,7 +5,7 @@ @@ -5,7 +5,7 @@
5 import { CategoryConfigType, NodeItemConfigType } from '../../../types/node'; 5 import { CategoryConfigType, NodeItemConfigType } from '../../../types/node';
6 6
7 const props = defineProps<{ 7 const props = defineProps<{
8 - category?: CategoryConfigType; 8 + categoryConfig?: CategoryConfigType;
9 config?: NodeItemConfigType; 9 config?: NodeItemConfigType;
10 }>(); 10 }>();
11 11
@@ -20,7 +20,7 @@ @@ -20,7 +20,7 @@
20 20
21 const getIcon = computed(() => { 21 const getIcon = computed(() => {
22 const { icon } = unref(getNodeDefinition); 22 const { icon } = unref(getNodeDefinition);
23 - const { icon: categoryIcon } = props.category || {}; 23 + const { icon: categoryIcon } = props.categoryConfig || {};
24 return icon || categoryIcon || 'tabler:circuit-ground'; 24 return icon || categoryIcon || 'tabler:circuit-ground';
25 }); 25 });
26 26
@@ -30,9 +30,9 @@ @@ -30,9 +30,9 @@
30 }); 30 });
31 31
32 const getBackgroundColor = computed(() => { 32 const getBackgroundColor = computed(() => {
33 - const { config, category } = props; 33 + const { config, categoryConfig } = props;
34 const { backgroundColor } = config || {}; 34 const { backgroundColor } = config || {};
35 - const { backgroundColor: categoryBackgroundColor } = category || {}; 35 + const { backgroundColor: categoryBackgroundColor } = categoryConfig || {};
36 return backgroundColor || categoryBackgroundColor; 36 return backgroundColor || categoryBackgroundColor;
37 }); 37 });
38 38
@@ -87,8 +87,8 @@ @@ -87,8 +87,8 @@
87 {{ config?.name }} 87 {{ config?.name }}
88 </span> 88 </span>
89 </div> 89 </div>
90 - <div class="w-4 h-4 bg-dark-50 rounded-1 border absolute -left-2 border-light-50"></div>  
91 - <div class="w-4 h-4 bg-dark-50 rounded-1 border absolute -right-2 border-light-50"></div> 90 + <div class="w-4 h-4 bg-gray-300 rounded-md border absolute -left-3 border-gray-500"></div>
  91 + <div class="w-4 h-4 bg-gray-300 rounded-md border absolute -right-3 border-gray-500"></div>
92 </main> 92 </main>
93 </Tooltip> 93 </Tooltip>
94 </template> 94 </template>
@@ -64,8 +64,8 @@ @@ -64,8 +64,8 @@
64 64
65 const getNodeIcon = computed(() => { 65 const getNodeIcon = computed(() => {
66 const { nodeData } = props; 66 const { nodeData } = props;
67 - const { category, config } = nodeData;  
68 - const { icon: categoryIcon } = category || {}; 67 + const { categoryConfig, config } = nodeData;
  68 + const { icon: categoryIcon } = categoryConfig || {};
69 const { configurationDescriptor } = config || {}; 69 const { configurationDescriptor } = config || {};
70 const { nodeDefinition } = configurationDescriptor || {}; 70 const { nodeDefinition } = configurationDescriptor || {};
71 const { icon } = nodeDefinition || {}; 71 const { icon } = nodeDefinition || {};
@@ -74,8 +74,8 @@ @@ -74,8 +74,8 @@
74 }); 74 });
75 75
76 const getTitleBackgroundColor = computed(() => { 76 const getTitleBackgroundColor = computed(() => {
77 - const { category, config } = props.nodeData;  
78 - const { backgroundColor: categoryBackgroundColor } = category || {}; 77 + const { categoryConfig, config } = props.nodeData;
  78 + const { backgroundColor: categoryBackgroundColor } = categoryConfig || {};
79 const { backgroundColor } = config || {}; 79 const { backgroundColor } = config || {};
80 return categoryBackgroundColor || backgroundColor; 80 return categoryBackgroundColor || backgroundColor;
81 }); 81 });
@@ -90,7 +90,7 @@ @@ -90,7 +90,7 @@
90 <template #title> 90 <template #title>
91 <h2 class="font-bold text-2xl truncate">{{ nodeData?.data?.name }}</h2> 91 <h2 class="font-bold text-2xl truncate">{{ nodeData?.data?.name }}</h2>
92 <p class="mb-0 text-gray-700"> 92 <p class="mb-0 text-gray-700">
93 - <span> {{ nodeData?.category?.title }}</span> 93 + <span> {{ nodeData?.categoryConfig?.title }}</span>
94 <span class="mx-1">-</span> 94 <span class="mx-1">-</span>
95 <span>{{ nodeData?.config?.name }}</span> 95 <span>{{ nodeData?.config?.name }}</span>
96 </p> 96 </p>
1 .vue-flow__handle { 1 .vue-flow__handle {
2 width: 16px; 2 width: 16px;
3 height: 16px; 3 height: 16px;
  4 + border-radius: 6px;
  5 + background-color: #d1d5db;
  6 + border-color: #6b7280;
  7 +}
  8 +
  9 +.vue-flow__handle:hover{
  10 + background-color: #000;
4 } 11 }
5 12
6 .vue-flow__handle-right { 13 .vue-flow__handle-right {
7 - right: -8px; 14 + right: -12px;
8 } 15 }
9 16
10 .vue-flow__handle-left { 17 .vue-flow__handle-left {
11 - left: -8px; 18 + left: -12px;
12 } 19 }
13 20
14 .vue-flow__background.vue-flow__container { 21 .vue-flow__background.vue-flow__container {
@@ -134,7 +134,7 @@ export interface DragTransferData { @@ -134,7 +134,7 @@ export interface DragTransferData {
134 } 134 }
135 135
136 export interface NodeData<T = BasicNodeFormData> { 136 export interface NodeData<T = BasicNodeFormData> {
137 - category?: CategoryConfigType; 137 + categoryConfig?: CategoryConfigType;
138 config?: NodeItemConfigType; 138 config?: NodeItemConfigType;
139 data?: T; 139 data?: T;
140 created?: boolean; 140 created?: boolean;
@@ -153,11 +153,14 @@ @@ -153,11 +153,14 @@
153 let getActionFormValue = ref([]); 153 let getActionFormValue = ref([]);
154 const editEntryIdData = ref([]); 154 const editEntryIdData = ref([]);
155 const editAlarmConfigData = ref([]); 155 const editAlarmConfigData = ref([]);
156 - const isUpdate = ref(false); 156 + const isUpdate = ref<boolean | number>(false);
157 const id = ref(undefined); 157 const id = ref(undefined);
158 const tenantId = ref(undefined); 158 const tenantId = ref(undefined);
159 const isView = ref(true); 159 const isView = ref(true);
160 - const [registerForm, { resetFields, validate, setFieldsValue, getFieldsValue }] = useForm({ 160 + const [
  161 + registerForm,
  162 + { resetFields, validate, setFieldsValue, getFieldsValue, setProps, updateSchema },
  163 + ] = useForm({
161 labelWidth: 120, 164 labelWidth: 120,
162 schemas: formSchema, 165 schemas: formSchema,
163 showActionButtonGroup: false, 166 showActionButtonGroup: false,
@@ -532,6 +535,37 @@ @@ -532,6 +535,37 @@
532 showFooter: unref(isView), 535 showFooter: unref(isView),
533 loading: false, 536 loading: false,
534 }); 537 });
  538 +
  539 + if (isUpdate.value == 3) {
  540 + setProps({ disabled: true });
  541 + updateSchema({
  542 + field: 'organizationId',
  543 + componentProps: { apiTreeSelectProps: { disabled: true } },
  544 + });
  545 + await nextTick();
  546 + unref(skipUnwrap.triggerItemRefs)?.forEach((item) => {
  547 + item.setDisabledProps({ disabled: true });
  548 + });
  549 +
  550 + unref(skipUnwrap.conditionItemRefs)?.forEach((item) => {
  551 + item.setDisabledProps({ disabled: true });
  552 + });
  553 +
  554 + unref(skipUnwrap.actionItemRefs)?.forEach((item) => {
  555 + item.setDisabledProps({ disabled: true });
  556 + });
  557 + } else {
  558 + updateSchema({
  559 + field: 'organizationId',
  560 + componentProps: { apiTreeSelectProps: { disabled: false } },
  561 + });
  562 + unref(skipUnwrap.triggerItemRefs)?.forEach((item) => {
  563 + item.setCancelDisabled();
  564 + });
  565 + unref(skipUnwrap.conditionItemRefs)?.forEach((item) => {
  566 + item.setCancelDisabled();
  567 + });
  568 + }
535 }); 569 });
536 570
537 // 设置设备的options 571 // 设置设备的options
@@ -29,6 +29,7 @@ @@ -29,6 +29,7 @@
29 <template #operationType="{ model, field }"> 29 <template #operationType="{ model, field }">
30 <Select 30 <Select
31 :options="options" 31 :options="options"
  32 + :disabled="disabled"
32 v-model:value="model[field]" 33 v-model:value="model[field]"
33 @change="operationType = model[field]" 34 @change="operationType = model[field]"
34 placeholder="请选择比较类型" 35 placeholder="请选择比较类型"
@@ -39,6 +40,7 @@ @@ -39,6 +40,7 @@
39 <Input v-model:value="model[field]" placeholder="请输入持续时间"> 40 <Input v-model:value="model[field]" placeholder="请输入持续时间">
40 <template #addonAfter> 41 <template #addonAfter>
41 <Select 42 <Select
  43 + :disabled="disabled"
42 v-model:value="model[`timeUnit`]" 44 v-model:value="model[`timeUnit`]"
43 :options="timeUnitOptions" 45 :options="timeUnitOptions"
44 style="width: 60px" 46 style="width: 60px"
@@ -58,7 +60,7 @@ @@ -58,7 +60,7 @@
58 </div> 60 </div>
59 </template> 61 </template>
60 <script lang="ts" setup> 62 <script lang="ts" setup>
61 - import { ref, provide, nextTick } from 'vue'; 63 + import { ref, provide, nextTick, unref } from 'vue';
62 import { CollapseContainer } from '/@/components/Container/index'; 64 import { CollapseContainer } from '/@/components/Container/index';
63 import { BasicForm, useForm } from '/@/components/Form/index'; 65 import { BasicForm, useForm } from '/@/components/Form/index';
64 import { Card, Select, Input, Tooltip } from 'ant-design-vue'; 66 import { Card, Select, Input, Tooltip } from 'ant-design-vue';
@@ -86,12 +88,13 @@ @@ -86,12 +88,13 @@
86 const emit = defineEmits(['delete']); 88 const emit = defineEmits(['delete']);
87 const isUpdate = ref(false); 89 const isUpdate = ref(false);
88 const conditionScreeningRef = ref(); 90 const conditionScreeningRef = ref();
89 - const [registerForm, { resetFields, getFieldsValue, updateSchema, setFieldsValue }] = useForm({  
90 - //TODO-wenwei-修复  
91 - schemas: cloneDeep(trigger_condition_schema),  
92 - //TODO-wenwei-修复  
93 - showActionButtonGroup: false,  
94 - }); 91 + const [registerForm, { resetFields, getFieldsValue, updateSchema, setFieldsValue, setProps }] =
  92 + useForm({
  93 + //TODO-wenwei-修复
  94 + schemas: cloneDeep(trigger_condition_schema),
  95 + //TODO-wenwei-修复
  96 + showActionButtonGroup: false,
  97 + });
95 98
96 const alarmScheduleRef = ref<InstanceType<typeof AlarmSchedule>>(); 99 const alarmScheduleRef = ref<InstanceType<typeof AlarmSchedule>>();
97 const getFieldsValueFunc = () => { 100 const getFieldsValueFunc = () => {
@@ -144,6 +147,7 @@ @@ -144,6 +147,7 @@
144 const currentIndex = ref(0); 147 const currentIndex = ref(0);
145 const [registerModal, { openModal }] = useModal(); 148 const [registerModal, { openModal }] = useModal();
146 const handleScheduleChange = (value) => { 149 const handleScheduleChange = (value) => {
  150 + if (unref(disabled)) return;
147 const index = scheduleOptions.findIndex((item) => item.value === value); 151 const index = scheduleOptions.findIndex((item) => item.value === value);
148 // 报警日程弹窗 152 // 报警日程弹窗
149 if (index !== 0) { 153 if (index !== 0) {
@@ -164,6 +168,16 @@ @@ -164,6 +168,16 @@
164 currentIndex.value = index; 168 currentIndex.value = index;
165 }; 169 };
166 const scheduleData = ref(null); 170 const scheduleData = ref(null);
  171 +
  172 + const disabled = ref<boolean>(false);
  173 + const setDisabledProps = (value) => {
  174 + setProps(value);
  175 + disabled.value = true;
  176 + };
  177 +
  178 + const setCancelDisabled = () => {
  179 + disabled.value = false;
  180 + };
167 defineExpose({ 181 defineExpose({
168 getFieldsValue, 182 getFieldsValue,
169 updateFieldDeviceId, 183 updateFieldDeviceId,
@@ -182,6 +196,8 @@ @@ -182,6 +196,8 @@
182 isUpdate, 196 isUpdate,
183 alarmScheduleRef, 197 alarmScheduleRef,
184 updateFieldAttributeFunc, 198 updateFieldAttributeFunc,
  199 + setDisabledProps,
  200 + setCancelDisabled,
185 }); 201 });
186 </script> 202 </script>
187 <style> 203 <style>
@@ -31,8 +31,11 @@ @@ -31,8 +31,11 @@
31 const emit = defineEmits(['deleteConditionForm']); 31 const emit = defineEmits(['deleteConditionForm']);
32 const operationType = inject('operationType'); 32 const operationType = inject('operationType');
33 let schemas = ref([]); 33 let schemas = ref([]);
  34 + const isViewDisabledBtn = window.localStorage.getItem('isViewDisabledBtn');
34 onMounted(() => { 35 onMounted(() => {
35 schemas.value = isType(operationType.value); 36 schemas.value = isType(operationType.value);
  37 +
  38 + if (isViewDisabledBtn == 'isView') setProps({ disabled: true });
36 }); 39 });
37 watch(operationType, (newValue) => { 40 watch(operationType, (newValue) => {
38 schemas.value = isType(newValue); 41 schemas.value = isType(newValue);
@@ -47,6 +50,7 @@ @@ -47,6 +50,7 @@
47 getFieldsValue, 50 getFieldsValue,
48 setFieldsValue, 51 setFieldsValue,
49 validate, 52 validate,
  53 + setProps,
50 }, 54 },
51 ] = useForm({ 55 ] = useForm({
52 showActionButtonGroup: false, 56 showActionButtonGroup: false,
@@ -55,6 +59,7 @@ @@ -55,6 +59,7 @@
55 schemas, 59 schemas,
56 }); 60 });
57 const deleteConditionForm = (index: number) => { 61 const deleteConditionForm = (index: number) => {
  62 + if (isViewDisabledBtn == 'isView') return;
58 emit('deleteConditionForm', index); 63 emit('deleteConditionForm', index);
59 }; 64 };
60 // ft add 65 // ft add
@@ -8,7 +8,8 @@ @@ -8,7 +8,8 @@
8 <template v-for="(item, scheduleIndex) in scheduleOptions" :key="item.label"> 8 <template v-for="(item, scheduleIndex) in scheduleOptions" :key="item.label">
9 <div 9 <div
10 :class="{ 'ml-4': scheduleIndex >= 1, active: scheduleIndex === currentIndex }" 10 :class="{ 'ml-4': scheduleIndex >= 1, active: scheduleIndex === currentIndex }"
11 - class="cursor-pointer" 11 + class="cursor-pointer !p-0"
  12 + :disabled="disabled"
12 @click="handleScheduleChange(item.value)" 13 @click="handleScheduleChange(item.value)"
13 >{{ item.label }}</div 14 >{{ item.label }}</div
14 > 15 >
@@ -28,6 +29,7 @@ @@ -28,6 +29,7 @@
28 <template #operationType="{ model, field }"> 29 <template #operationType="{ model, field }">
29 <Select 30 <Select
30 :options="options" 31 :options="options"
  32 + :disabled="disabled"
31 v-model:value="model[field]" 33 v-model:value="model[field]"
32 @change="operationType = model[field]" 34 @change="operationType = model[field]"
33 placeholder="请选择比较类型" 35 placeholder="请选择比较类型"
@@ -38,6 +40,7 @@ @@ -38,6 +40,7 @@
38 <Input v-model:value="model[field]" placeholder="请输入持续时间"> 40 <Input v-model:value="model[field]" placeholder="请输入持续时间">
39 <template #addonAfter> 41 <template #addonAfter>
40 <Select 42 <Select
  43 + :disabled="disabled"
41 v-model:value="model[`timeUnit`]" 44 v-model:value="model[`timeUnit`]"
42 :options="timeUnitOptions" 45 :options="timeUnitOptions"
43 style="width: 60px" 46 style="width: 60px"
@@ -71,6 +74,7 @@ @@ -71,6 +74,7 @@
71 import { Icon } from '/@/components/Icon'; 74 import { Icon } from '/@/components/Icon';
72 import { useModal } from '/@/components/Modal'; 75 import { useModal } from '/@/components/Modal';
73 import { useMessage } from '/@/hooks/web/useMessage'; 76 import { useMessage } from '/@/hooks/web/useMessage';
  77 + import { unref } from 'vue';
74 78
75 const { useByProductGetAttribute } = useCommonFun(); 79 const { useByProductGetAttribute } = useCommonFun();
76 defineProps({ 80 defineProps({
@@ -92,12 +96,13 @@ @@ -92,12 +96,13 @@
92 96
93 const isUpdate = ref(false); 97 const isUpdate = ref(false);
94 const conditionScreeningRef = ref(); 98 const conditionScreeningRef = ref();
95 - const [registerForm, { resetFields, getFieldsValue, updateSchema, setFieldsValue }] = useForm({  
96 - //TODO-wenwei-修复  
97 - schemas: cloneDeep(trigger_condition_schema),  
98 - //TODO-wenwei-修复  
99 - showActionButtonGroup: false,  
100 - }); 99 + const [registerForm, { resetFields, getFieldsValue, updateSchema, setFieldsValue, setProps }] =
  100 + useForm({
  101 + //TODO-wenwei-修复
  102 + schemas: cloneDeep(trigger_condition_schema),
  103 + //TODO-wenwei-修复
  104 + showActionButtonGroup: false,
  105 + });
101 106
102 const alarmScheduleRef = ref<InstanceType<typeof AlarmSchedule>>(); 107 const alarmScheduleRef = ref<InstanceType<typeof AlarmSchedule>>();
103 108
@@ -168,6 +173,7 @@ @@ -168,6 +173,7 @@
168 }; 173 };
169 //TODO-fengtao 174 //TODO-fengtao
170 const handleDelete = (params: { index: number; title: string }) => { 175 const handleDelete = (params: { index: number; title: string }) => {
  176 + if (unref(disabled)) return;
171 emit('delete', params); 177 emit('delete', params);
172 }; 178 };
173 const operationType = ref<string>(''); 179 const operationType = ref<string>('');
@@ -192,6 +198,7 @@ @@ -192,6 +198,7 @@
192 const [registerModal, { openModal }] = useModal(); 198 const [registerModal, { openModal }] = useModal();
193 const currentIndex = ref(0); 199 const currentIndex = ref(0);
194 const handleScheduleChange = (value) => { 200 const handleScheduleChange = (value) => {
  201 + if (unref(disabled)) return;
195 const index = scheduleOptions.findIndex((item) => item.value === value); 202 const index = scheduleOptions.findIndex((item) => item.value === value);
196 // 报警日程弹窗 203 // 报警日程弹窗
197 if (index !== 0) { 204 if (index !== 0) {
@@ -223,6 +230,16 @@ @@ -223,6 +230,16 @@
223 // console.log(alarmConfigList); 230 // console.log(alarmConfigList);
224 }; 231 };
225 232
  233 + const disabled = ref<boolean>(false);
  234 + const setDisabledProps = (value) => {
  235 + setProps(value);
  236 + disabled.value = true;
  237 + };
  238 +
  239 + const setCancelDisabled = () => {
  240 + disabled.value = false;
  241 + };
  242 +
226 defineExpose({ 243 defineExpose({
227 getFieldsValueFunc, 244 getFieldsValueFunc,
228 updateFieldDeviceId, 245 updateFieldDeviceId,
@@ -240,6 +257,8 @@ @@ -240,6 +257,8 @@
240 updateFieldAttributeFunc, 257 updateFieldAttributeFunc,
241 updateFieldAlarmConfig, 258 updateFieldAlarmConfig,
242 updateEditFieldAlarmConfig, 259 updateEditFieldAlarmConfig,
  260 + setDisabledProps,
  261 + setCancelDisabled,
243 }); 262 });
244 </script> 263 </script>
245 264
@@ -13,6 +13,7 @@ @@ -13,6 +13,7 @@
13 <BasicForm @register="registerAction"> 13 <BasicForm @register="registerAction">
14 <template #outTarget="{ model, field }"> 14 <template #outTarget="{ model, field }">
15 <Select 15 <Select
  16 + :disabled="disabled"
16 :options="options" 17 :options="options"
17 v-model:value="model[field]" 18 v-model:value="model[field]"
18 @change="changeOutTarget" 19 @change="changeOutTarget"
@@ -24,6 +25,7 @@ @@ -24,6 +25,7 @@
24 <template #alarmConfigSlot="{ model, field }"> 25 <template #alarmConfigSlot="{ model, field }">
25 <a-select 26 <a-select
26 allowClear 27 allowClear
  28 + :disabled="disabled"
27 placeholder="请选择告警配置" 29 placeholder="请选择告警配置"
28 v-model:value="model[field]" 30 v-model:value="model[field]"
29 style="width: 205px; margin-left: 10px" 31 style="width: 205px; margin-left: 10px"
@@ -41,14 +43,22 @@ @@ -41,14 +43,22 @@
41 </template> 43 </template>
42 <template #doContext="{ model, field }"> 44 <template #doContext="{ model, field }">
43 <div v-if="model['transportType'] === TransportTypeEnum.TCP"> 45 <div v-if="model['transportType'] === TransportTypeEnum.TCP">
44 - <Input v-model:value="model[field]" placeholder="请输入自定义命令" /> 46 + <Input v-model:value="model[field]" :disabled="disabled" placeholder="请输入自定义命令" />
45 </div> 47 </div>
46 <div 48 <div
47 v-show="model['transportType'] !== TransportTypeEnum.TCP" 49 v-show="model['transportType'] !== TransportTypeEnum.TCP"
48 class="flex" 50 class="flex"
49 style="align-items: center" 51 style="align-items: center"
50 > 52 >
51 - <div ref="jsoneditorRef" style="height: 100%; width: 100%"></div> 53 + <div v-show="!disabled" ref="jsoneditorRef" style="height: 100%; width: 100%"></div>
  54 +
  55 + <a-textarea
  56 + v-show="disabled"
  57 + :disabled="true"
  58 + :value="JSON.stringify(disabledValue)"
  59 + :rows="4"
  60 + />
  61 +
52 <a-button style="margin: -5px 0" type="text" @click="handlePremitter">格式化</a-button> 62 <a-button style="margin: -5px 0" type="text" @click="handlePremitter">格式化</a-button>
53 <Tooltip 63 <Tooltip
54 :title=" 64 :title="
@@ -64,7 +74,9 @@ @@ -64,7 +74,9 @@
64 </div> 74 </div>
65 </template> 75 </template>
66 <template #clearAlarm> 76 <template #clearAlarm>
67 - <Checkbox v-model:checked="checked" @change="handleCheckedChange"> 清除告警 </Checkbox> 77 + <Checkbox v-model:checked="checked" :disabled="disabled" @change="handleCheckedChange">
  78 + 清除告警
  79 + </Checkbox>
68 <Tooltip title="清除告警与触发器一一对应"> 80 <Tooltip title="清除告警与触发器一一对应">
69 <QuestionCircleOutlined /> 81 <QuestionCircleOutlined />
70 </Tooltip> 82 </Tooltip>
@@ -189,10 +201,23 @@ @@ -189,10 +201,23 @@
189 ]; 201 ];
190 }); 202 });
191 203
192 - const changeOutTarget = () => { 204 + const changeOutTarget = (e) => {
  205 + if (!e) validateFields(['outTarget']);
  206 + else clearValidate('outTarget');
193 emit('getActionFormArr'); 207 emit('getActionFormArr');
194 }; 208 };
195 - const [registerAction, { getFieldsValue, resetFields, setFieldsValue, validate }] = useForm({ 209 + const [
  210 + registerAction,
  211 + {
  212 + getFieldsValue,
  213 + resetFields,
  214 + setFieldsValue,
  215 + validate,
  216 + clearValidate,
  217 + validateFields,
  218 + setProps,
  219 + },
  220 + ] = useForm({
196 schemas: actionSchema, 221 schemas: actionSchema,
197 showActionButtonGroup: false, 222 showActionButtonGroup: false,
198 }); 223 });
@@ -369,6 +394,7 @@ @@ -369,6 +394,7 @@
369 }; 394 };
370 395
371 const handleDelete = (actionIndex) => { 396 const handleDelete = (actionIndex) => {
  397 + if (unref(disabled)) return;
372 emit('deleteAction', actionIndex); 398 emit('deleteAction', actionIndex);
373 }; 399 };
374 400
@@ -404,6 +430,8 @@ @@ -404,6 +430,8 @@
404 const jsoneditorRef = ref(); 430 const jsoneditorRef = ref();
405 const jsonValue = ref({}); 431 const jsonValue = ref({});
406 const jsonInstance = ref(); 432 const jsonInstance = ref();
  433 +
  434 + const disabledValue = ref(); //查看时使用表单全部禁用使用这个来获取表单数据
407 onMounted(() => { 435 onMounted(() => {
408 nextTick(() => { 436 nextTick(() => {
409 let options = { 437 let options = {
@@ -420,6 +448,7 @@ @@ -420,6 +448,7 @@
420 const getJsonValue = () => unref(jsonInstance).get(); 448 const getJsonValue = () => unref(jsonInstance).get();
421 const setJsonValue = (Json) => { 449 const setJsonValue = (Json) => {
422 nextTick(() => { 450 nextTick(() => {
  451 + disabledValue.value = Json;
423 unref(jsonInstance).set(Json); 452 unref(jsonInstance).set(Json);
424 }); 453 });
425 }; 454 };
@@ -447,6 +476,23 @@ @@ -447,6 +476,23 @@
447 emit('getActionFormArr'); 476 emit('getActionFormArr');
448 }; 477 };
449 478
  479 + const disabled = ref<boolean>(false);
  480 + const setDisabledProps = async (value) => {
  481 + setProps(value);
  482 + disabled.value = true;
  483 + await nextTick();
  484 + unref(refItem.clearRuleRefs)?.forEach((item) => {
  485 + item.setDisabledProps(value);
  486 + });
  487 + };
  488 +
  489 + const setCancelDisabled = () => {
  490 + disabled.value = false;
  491 + unref(refItem.clearRuleRefs)?.forEach((item) => {
  492 + item.setCancelDisabled();
  493 + });
  494 + };
  495 +
450 defineExpose({ 496 defineExpose({
451 getFieldsValue, 497 getFieldsValue,
452 getFieldsValueFunc, 498 getFieldsValueFunc,
@@ -467,6 +513,8 @@ @@ -467,6 +513,8 @@
467 clearRuleList, 513 clearRuleList,
468 resetConditionForm, 514 resetConditionForm,
469 handleDropdownVisibleChange, 515 handleDropdownVisibleChange,
  516 + setDisabledProps,
  517 + setCancelDisabled,
470 }); 518 });
471 </script> 519 </script>
472 520
@@ -114,7 +114,7 @@ @@ -114,7 +114,7 @@
114 </div> 114 </div>
115 </template> 115 </template>
116 <script setup lang="ts"> 116 <script setup lang="ts">
117 - import { ref, unref, reactive, onMounted, toRefs, computed } from 'vue'; 117 + import { ref, unref, reactive, onMounted, toRefs, computed, nextTick } from 'vue';
118 import ace from 'ace-builds'; 118 import ace from 'ace-builds';
119 import { Card, Button, Tooltip } from 'ant-design-vue'; 119 import { Card, Button, Tooltip } from 'ant-design-vue';
120 import 'ace-builds/src-noconflict/theme-chrome'; // 默认设置的主题 120 import 'ace-builds/src-noconflict/theme-chrome'; // 默认设置的主题
@@ -323,7 +323,19 @@ @@ -323,7 +323,19 @@
323 }; 323 };
324 324
325 const setDisableRadio = (value) => { 325 const setDisableRadio = (value) => {
326 - reportTypeOptions.scriptTypeOptions.forEach((item: any) => { 326 + //查看和表格里面的测试点击禁用脚本类型
  327 + unref(reportTypeOptions.scriptTypeOptions).forEach((item: any) => {
  328 + if (item.value === value) item.disabled = false;
  329 + else item.disabled = true;
  330 + });
  331 + };
  332 +
  333 + const setDisableTestRadio = async (value) => {
  334 + //内部弹窗,使用上面的setDisableRadio无效
  335 + //新增里面的测试点击禁用脚本类型
  336 + await getScriptType();
  337 + await nextTick();
  338 + unref(reportTypeOptions.scriptTypeOptions).forEach((item: any) => {
327 if (item.value === value) item.disabled = false; 339 if (item.value === value) item.disabled = false;
328 else item.disabled = true; 340 else item.disabled = true;
329 }); 341 });
@@ -336,6 +348,7 @@ @@ -336,6 +348,7 @@
336 setScriptOutputData, 348 setScriptOutputData,
337 setDefaultRadio, 349 setDefaultRadio,
338 setDisableRadio, 350 setDisableRadio,
  351 + setDisableTestRadio,
339 }); 352 });
340 </script> 353 </script>
341 <style lang="less" scoped> 354 <style lang="less" scoped>
@@ -56,7 +56,15 @@ @@ -56,7 +56,15 @@
56 if (!data.innerTest) { 56 if (!data.innerTest) {
57 const rest = await getScriptManageDetail(data.record?.id); 57 const rest = await getScriptManageDetail(data.record?.id);
58 converScriptFormRef.value?.setFormData(rest); 58 converScriptFormRef.value?.setFormData(rest);
59 - } else converScriptFormRef.value?.setFormData(data.record); 59 + if (data.text !== BusinessConvertScriptTextEnum.BUSINESS_EDIT_TEXT) {
  60 + //编辑是不能禁用脚本类型的
  61 + converScriptFormRef.value?.setDisableRadio(data.record.scriptType);
  62 + }
  63 + } else {
  64 + //从新增页面里点击的测试,禁用脚本类型
  65 + converScriptFormRef.value?.setFormData(data.record);
  66 + converScriptFormRef.value?.setDisableTestRadio(data.record.scriptType);
  67 + }
60 if (data.scriptType) { 68 if (data.scriptType) {
61 converScriptFormRef.value?.setDisableRadio(data.scriptType); 69 converScriptFormRef.value?.setDisableRadio(data.scriptType);
62 } 70 }
@@ -18,6 +18,7 @@ @@ -18,6 +18,7 @@
18 ref="basicTreeRef" 18 ref="basicTreeRef"
19 checkable 19 checkable
20 toolbar 20 toolbar
  21 + @change="handleTreeSelect"
21 /> 22 />
22 </template> 23 </template>
23 <template #roleSlot="{ model, field }"> 24 <template #roleSlot="{ model, field }">
@@ -26,6 +27,7 @@ @@ -26,6 +27,7 @@
26 allowClear 27 allowClear
27 placeholder="请选择角色" 28 placeholder="请选择角色"
28 v-model:value="model[field]" 29 v-model:value="model[field]"
  30 + @change="handleRoleSelect"
29 :options="roleOptions.map((item) => ({ value: item.value, label: item.label }))" 31 :options="roleOptions.map((item) => ({ value: item.value, label: item.label }))"
30 > 32 >
31 <template #dropdownRender="{ menuNode: menu }"> 33 <template #dropdownRender="{ menuNode: menu }">
@@ -108,12 +110,30 @@ @@ -108,12 +110,30 @@
108 isUpdate: false, 110 isUpdate: false,
109 }); 111 });
110 }; 112 };
  113 + const clearValidateByField = (field: string) => {
  114 + clearValidate(field);
  115 + };
  116 + const handleRoleSelect = (e) => {
  117 + if (e?.length > 0) clearValidateByField('roleIds');
  118 + else validateFields(['roleIds']);
  119 + };
  120 + const handleTreeSelect = (e) => {
  121 + if (e) clearValidateByField('organizationIds');
  122 + };
111 const handleSuccess = async () => { 123 const handleSuccess = async () => {
112 await getRoleList(); 124 await getRoleList();
113 }; 125 };
114 const [ 126 const [
115 registerForm, 127 registerForm,
116 - { setFieldsValue, updateSchema, resetFields, validate, getFieldsValue }, 128 + {
  129 + setFieldsValue,
  130 + updateSchema,
  131 + resetFields,
  132 + validate,
  133 + getFieldsValue,
  134 + clearValidate,
  135 + validateFields,
  136 + },
117 ] = useForm({ 137 ] = useForm({
118 labelWidth: 100, 138 labelWidth: 100,
119 schemas: accountFormSchema, 139 schemas: accountFormSchema,
@@ -225,6 +245,8 @@ @@ -225,6 +245,8 @@
225 registerRoleDrawer, 245 registerRoleDrawer,
226 handleOpenRole, 246 handleOpenRole,
227 handleSuccess, 247 handleSuccess,
  248 + handleRoleSelect,
  249 + handleTreeSelect,
228 }; 250 };
229 }, 251 },
230 }); 252 });
@@ -153,6 +153,7 @@ @@ -153,6 +153,7 @@
153 title: '是否确认删除操作?', 153 title: '是否确认删除操作?',
154 onConfirm: handleDelete.bind(null), 154 onConfirm: handleDelete.bind(null),
155 }, 155 },
  156 + disabled: !!getRecord.state,
156 }, 157 },
157 ]" 158 ]"
158 /> 159 />
@@ -35,6 +35,7 @@ @@ -35,6 +35,7 @@
35 const { proxy } = getCurrentInstance() as any; 35 const { proxy } = getCurrentInstance() as any;
36 const getChildData = ref(null); 36 const getChildData = ref(null);
37 const editGetId: any = ref(''); 37 const editGetId: any = ref('');
  38 + const isDefault = ref(false);
38 const createTime = ref<string>(''); 39 const createTime = ref<string>('');
39 const [registerForm, { validate, resetFields, setFieldsValue, updateSchema }] = useForm({ 40 const [registerForm, { validate, resetFields, setFieldsValue, updateSchema }] = useForm({
40 schemas: formSchema, 41 schemas: formSchema,
@@ -55,6 +56,7 @@ @@ -55,6 +56,7 @@
55 parentSetData.value = { ...data.record.profileData.configuration }; 56 parentSetData.value = { ...data.record.profileData.configuration };
56 proxy.$refs.getChildData.setFieldsValueFunc(parentSetData.value); 57 proxy.$refs.getChildData.setFieldsValueFunc(parentSetData.value);
57 editGetId.value = data.record.id; 58 editGetId.value = data.record.id;
  59 + isDefault.value = data.record.default;
58 await setFieldsValue({ 60 await setFieldsValue({
59 ...data.record, 61 ...data.record,
60 }); 62 });
@@ -95,6 +97,10 @@ @@ -95,6 +97,10 @@
95 const createTime1 = { 97 const createTime1 = {
96 createdTime: isUpdate ? unref(createTime) : Date.now(), 98 createdTime: isUpdate ? unref(createTime) : Date.now(),
97 }; 99 };
  100 +
  101 + const defaultInfo = {
  102 + default: unref(isUpdate) ? isDefault.value : false,
  103 + };
98 Object.assign( 104 Object.assign(
99 postAllData, 105 postAllData,
100 { 106 {
@@ -102,7 +108,8 @@ @@ -102,7 +108,8 @@
102 }, 108 },
103 getValuesFormData, 109 getValuesFormData,
104 id, 110 id,
105 - createTime1 111 + createTime1,
  112 + defaultInfo
106 ); 113 );
107 if (!unref(isUpdate)) { 114 if (!unref(isUpdate)) {
108 delete postAllData.id; 115 delete postAllData.id;
@@ -43,11 +43,11 @@ @@ -43,11 +43,11 @@
43 }, 43 },
44 { 44 {
45 field: 'username', 45 field: 'username',
46 - label: '账号', 46 + label: '用户名',
47 component: 'Input', 47 component: 'Input',
48 componentProps: { 48 componentProps: {
49 maxLength: 64, 49 maxLength: 64,
50 - placeholder: '请输入账号', 50 + placeholder: '请输入用户名',
51 }, 51 },
52 dynamicRules: ({ values }) => { 52 dynamicRules: ({ values }) => {
53 try { 53 try {
@@ -62,14 +62,13 @@ @@ -62,14 +62,13 @@
62 validator(_, value) { 62 validator(_, value) {
63 return new Promise((resolve, reject) => { 63 return new Promise((resolve, reject) => {
64 if (value == '' || value === undefined) { 64 if (value == '' || value === undefined) {
65 - reject('请输入账号'); 65 + reject('请输入用户名');
66 } else if (ChineseRegexp.test(value)) { 66 } else if (ChineseRegexp.test(value)) {
67 - reject('账号不能含有中文'); 67 + reject('用户名不能含有中文');
68 } else if (EmailRegexp.test(value)) { 68 } else if (EmailRegexp.test(value)) {
69 - reject('账号不能为电子邮箱格式'); 69 + reject('用户名不能为电子邮箱格式');
70 } else if (findUserName && value == findUserName?.username) { 70 } else if (findUserName && value == findUserName?.username) {
71 - console.log(1111111111);  
72 - reject('账号已存在'); 71 + reject('用户名已存在');
73 return; 72 return;
74 } else { 73 } else {
75 resolve(); 74 resolve();
@@ -7,50 +7,25 @@ @@ -7,50 +7,25 @@
7 width="500px" 7 width="500px"
8 @ok="handleSubmit" 8 @ok="handleSubmit"
9 > 9 >
10 - <BasicForm @register="tenantForm">  
11 - <template #iconSelect>  
12 - <Upload  
13 - name="avatar"  
14 - accept=".png,.jpg,.jpeg,.gif"  
15 - list-type="picture-card"  
16 - class="avatar-uploader"  
17 - :show-upload-list="false"  
18 - :customRequest="customUpload"  
19 - :before-upload="beforeUpload"  
20 - >  
21 - <img v-if="tenantLogo" :src="tenantLogo" alt="avatar" />  
22 - <div v-else>  
23 - <LoadingOutlined v-if="loading" />  
24 - <plus-outlined v-else />  
25 - <div class="ant-upload-text">上传</div>  
26 - </div>  
27 - </Upload>  
28 - </template>  
29 - </BasicForm> 10 + <BasicForm @register="tenantForm" />
30 </BasicDrawer> 11 </BasicDrawer>
31 </template> 12 </template>
32 <script lang="ts"> 13 <script lang="ts">
33 import { defineComponent, ref, computed, unref } from 'vue'; 14 import { defineComponent, ref, computed, unref } from 'vue';
34 import { BasicForm, useForm } from '/@/components/Form/index'; 15 import { BasicForm, useForm } from '/@/components/Form/index';
35 import { BasicDrawer, useDrawerInner } from '/@/components/Drawer'; 16 import { BasicDrawer, useDrawerInner } from '/@/components/Drawer';
36 - import { PlusOutlined, LoadingOutlined } from '@ant-design/icons-vue';  
37 - import { message, Upload } from 'ant-design-vue';  
38 -  
39 import { useI18n } from '/@/hooks/web/useI18n'; 17 import { useI18n } from '/@/hooks/web/useI18n';
40 import { tenantFormSchema } from '/@/views/tenant/list/tenantBaseColumns'; 18 import { tenantFormSchema } from '/@/views/tenant/list/tenantBaseColumns';
41 - import { FileItem } from '/@/components/Upload/src/typing';  
42 - import { upload } from '/@/api/oss/ossFileUploader';  
43 import { getTenantRoles, updateOrCreateTenant } from '/@/api/tenant/tenantApi'; 19 import { getTenantRoles, updateOrCreateTenant } from '/@/api/tenant/tenantApi';
44 import { useMessage } from '/@/hooks/web/useMessage'; 20 import { useMessage } from '/@/hooks/web/useMessage';
  21 + import { FileItem } from '/@/components/Form/src/components/ApiUpload.vue';
  22 + import { buildUUID } from '/@/utils/uuid';
45 23
46 export default defineComponent({ 24 export default defineComponent({
47 name: 'TenantDrawer', 25 name: 'TenantDrawer',
48 components: { 26 components: {
49 BasicDrawer, 27 BasicDrawer,
50 BasicForm, 28 BasicForm,
51 - Upload,  
52 - PlusOutlined,  
53 - LoadingOutlined,  
54 }, 29 },
55 emits: ['success', 'register'], 30 emits: ['success', 'register'],
56 setup(_, { emit }) { 31 setup(_, { emit }) {
@@ -58,33 +33,7 @@ @@ -58,33 +33,7 @@
58 const { createMessage } = useMessage(); 33 const { createMessage } = useMessage();
59 34
60 const isUpdate = ref(true); 35 const isUpdate = ref(true);
61 - const tenantLogo = ref('');  
62 -  
63 - async function customUpload({ file }) {  
64 - if (beforeUpload(file)) {  
65 - tenantLogo.value = '';  
66 - loading.value = true;  
67 - const formData = new FormData();  
68 - formData.append('file', file);  
69 - const response = await upload(formData);  
70 - if (response.fileStaticUri) {  
71 - tenantLogo.value = response.fileStaticUri;  
72 - loading.value = false;  
73 - }  
74 - }  
75 - }  
76 36
77 - const beforeUpload = (file: FileItem) => {  
78 - const isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png';  
79 - if (!isJpgOrPng) {  
80 - message.error('只能上传图片文件!');  
81 - }  
82 - const isLt2M = (file.size as number) / 1024 / 1024 < 5;  
83 - if (!isLt2M) {  
84 - message.error('图片大小不能超过5MB!');  
85 - }  
86 - return isJpgOrPng && isLt2M;  
87 - };  
88 const [tenantForm, { resetFields, setFieldsValue, updateSchema, validate }] = useForm({ 37 const [tenantForm, { resetFields, setFieldsValue, updateSchema, validate }] = useForm({
89 labelWidth: 100, 38 labelWidth: 100,
90 schemas: tenantFormSchema, 39 schemas: tenantFormSchema,
@@ -96,7 +45,6 @@ @@ -96,7 +45,6 @@
96 //默认传递页面数据 45 //默认传递页面数据
97 const [registerDrawer, { setDrawerProps, closeDrawer }] = useDrawerInner(async (data) => { 46 const [registerDrawer, { setDrawerProps, closeDrawer }] = useDrawerInner(async (data) => {
98 await resetFields(); 47 await resetFields();
99 - tenantLogo.value = '';  
100 setDrawerProps({ confirmLoading: false }); 48 setDrawerProps({ confirmLoading: false });
101 isUpdate.value = !!data?.isUpdate; 49 isUpdate.value = !!data?.isUpdate;
102 50
@@ -104,11 +52,19 @@ @@ -104,11 +52,19 @@
104 await updateSchema({ field: 'title', componentProps: { disabled: false } }); 52 await updateSchema({ field: 'title', componentProps: { disabled: false } });
105 //如果是编辑操作,设置页面数据 53 //如果是编辑操作,设置页面数据
106 if (unref(isUpdate)) { 54 if (unref(isUpdate)) {
  55 + if (data.record.icon) {
  56 + setFieldsValue({
  57 + icon: [{ uid: buildUUID(), name: 'name', url: data.record.icon } as FileItem],
  58 + });
  59 + }
107 getTenantRoles(data.record.tenantId).then((result) => { 60 getTenantRoles(data.record.tenantId).then((result) => {
108 - Reflect.set(data.record, 'roleIds', result); 61 + const { icon, ...params } = data.record;
  62 + console.log(icon);
109 //为表单赋值 63 //为表单赋值
110 - setFieldsValue(data.record);  
111 - tenantLogo.value = data.record.icon; 64 + setFieldsValue({
  65 + ...params,
  66 + roleIds: result,
  67 + });
112 //编辑模式,菜单名称为不可用 68 //编辑模式,菜单名称为不可用
113 updateSchema({ field: 'title', componentProps: { disabled: true } }); 69 updateSchema({ field: 'title', componentProps: { disabled: true } });
114 }); 70 });
@@ -126,9 +82,13 @@ @@ -126,9 +82,13 @@
126 setDrawerProps({ confirmLoading: true }); 82 setDrawerProps({ confirmLoading: true });
127 try { 83 try {
128 const values = await validate(); 84 const values = await validate();
  85 + if (Reflect.has(values, 'icon')) {
  86 + const file = (values.icon || []).at(0) || {};
  87 + values.icon = file.url || null;
  88 + }
129 const req = { 89 const req = {
130 id: values.id, 90 id: values.id,
131 - icon: tenantLogo.value, 91 + icon: values.icon,
132 name: values.name, 92 name: values.name,
133 enabled: values.enabled, 93 enabled: values.enabled,
134 description: values.description, 94 description: values.description,
@@ -161,9 +121,6 @@ @@ -161,9 +121,6 @@
161 tenantForm, 121 tenantForm,
162 getTitle, 122 getTitle,
163 handleSubmit, 123 handleSubmit,
164 - tenantLogo,  
165 - beforeUpload,  
166 - customUpload,  
167 loading, 124 loading,
168 }; 125 };
169 }, 126 },
@@ -3,6 +3,9 @@ import { FormSchema } from '/@/components/Form'; @@ -3,6 +3,9 @@ import { FormSchema } from '/@/components/Form';
3 import { getAllRoleList } from '/@/api/system/system'; 3 import { getAllRoleList } from '/@/api/system/system';
4 import { getTableTenantProfileApi, QueryTenantProfilesParam } from '/@/api/tenant/tenantApi'; 4 import { getTableTenantProfileApi, QueryTenantProfilesParam } from '/@/api/tenant/tenantApi';
5 import { RoleEnum } from '/@/enums/roleEnum'; 5 import { RoleEnum } from '/@/enums/roleEnum';
  6 +import { FileItem } from '/@/components/Form/src/components/ApiUpload.vue';
  7 +import { createImgPreview } from '/@/components/Preview';
  8 +import { uploadThumbnail } from '/@/api/configuration/center/configurationCenter';
6 9
7 export function getBasicColumns(): BasicColumn[] { 10 export function getBasicColumns(): BasicColumn[] {
8 return [ 11 return [
@@ -77,23 +80,32 @@ export const tenantFormSchema: FormSchema[] = [ @@ -77,23 +80,32 @@ export const tenantFormSchema: FormSchema[] = [
77 { 80 {
78 field: 'icon', 81 field: 'icon',
79 label: '租户图标', 82 label: '租户图标',
80 - slot: 'iconSelect',  
81 - component: 'Input',  
82 - componentProps: {  
83 - maxLength: 255,  
84 - },  
85 - dynamicRules: () => {  
86 - return [  
87 - {  
88 - required: false,  
89 - validator: (_, value) => {  
90 - if (String(value).length > 255) {  
91 - return Promise.reject('字数不超过255个字');  
92 - }  
93 - return Promise.resolve();  
94 - }, 83 + component: 'ApiUpload',
  84 + changeEvent: 'update:fileList',
  85 + valueField: 'fileList',
  86 + componentProps: () => {
  87 + return {
  88 + listType: 'picture-card',
  89 + maxFileLimit: 1,
  90 + accept: '.png,.jpg,.jpeg,.gif',
  91 + api: async (file: File) => {
  92 + try {
  93 + const formData = new FormData();
  94 + formData.set('file', file);
  95 + const { fileStaticUri, fileName } = await uploadThumbnail(formData);
  96 + return {
  97 + uid: fileStaticUri,
  98 + name: fileName,
  99 + url: fileStaticUri,
  100 + } as FileItem;
  101 + } catch (error) {
  102 + return {};
  103 + }
95 }, 104 },
96 - ]; 105 + onPreview: (fileList: FileItem) => {
  106 + createImgPreview({ imageList: [fileList.url!] });
  107 + },
  108 + };
97 }, 109 },
98 }, 110 },
99 { 111 {
@@ -169,8 +169,6 @@ @@ -169,8 +169,6 @@
169 } 169 }
170 ); 170 );
171 171
172 - const { containerEl } = useSort(emit, getFormValues);  
173 -  
174 const handleSettingOk = (data: DataSourceType) => { 172 const handleSettingOk = (data: DataSourceType) => {
175 const { uuid } = data; 173 const { uuid } = data;
176 const _dataSource = cloneDeep(getFormValues()); 174 const _dataSource = cloneDeep(getFormValues());
@@ -182,6 +180,8 @@ @@ -182,6 +180,8 @@
182 emit('update:dataSource', _dataSource); 180 emit('update:dataSource', _dataSource);
183 }; 181 };
184 182
  183 + const { containerEl } = useSort(emit, getFormValues);
  184 +
185 defineExpose({ 185 defineExpose({
186 getFormValues, 186 getFormValues,
187 validate, 187 validate,
@@ -198,6 +198,7 @@ @@ -198,6 +198,7 @@
198 <component 198 <component
199 :ref="(event) => setDataSourceFormsEl(item.uuid, event, index)" 199 :ref="(event) => setDataSourceFormsEl(item.uuid, event, index)"
200 class="flex-1 bg-light-50 dark:bg-dark-400" 200 class="flex-1 bg-light-50 dark:bg-dark-400"
  201 + style="max-width: calc(100% - 216px)"
201 :is="getComponent" 202 :is="getComponent"
202 :component-config="componentConfig" 203 :component-config="componentConfig"
203 :values="item" 204 :values="item"
@@ -235,3 +236,11 @@ @@ -235,3 +236,11 @@
235 /> 236 />
236 </section> 237 </section>
237 </template> 238 </template>
  239 +
  240 +<style scoped lang="less">
  241 + :deep(#deviceId) {
  242 + div {
  243 + width: 100%;
  244 + }
  245 + }
  246 +</style>
@@ -267,6 +267,7 @@ @@ -267,6 +267,7 @@
267 <BasicModal 267 <BasicModal
268 title="自定义组件" 268 title="自定义组件"
269 width="70%" 269 width="70%"
  270 + :destroy-on-close="true"
270 @register="registerModal" 271 @register="registerModal"
271 @ok="handleSubmit" 272 @ok="handleSubmit"
272 :ok-button-props="{ loading }" 273 :ok-button-props="{ loading }"
@@ -293,12 +293,13 @@ export const commonDataSourceSchemas = (): FormSchema[] => { @@ -293,12 +293,13 @@ export const commonDataSourceSchemas = (): FormSchema[] => {
293 (item) => item.itemValue == CommandTypeEnum.ATTRIBUTE.toString() 293 (item) => item.itemValue == CommandTypeEnum.ATTRIBUTE.toString()
294 ); 294 );
295 } 295 }
296 -  
297 // TCP网关子 --> 不能要服务命令类型 296 // TCP网关子 --> 不能要服务命令类型
298 if (deviceType == 'SENSOR' && transportType == 'TCP') { 297 if (deviceType == 'SENSOR' && transportType == 'TCP') {
299 - return record.filter(  
300 - (item) => item.itemValue == CommandTypeEnum.ATTRIBUTE.toString()  
301 - ); 298 + return codeType == 'MODBUS_RTU'
  299 + ? record.filter((item) => item.itemValue == CommandTypeEnum.ATTRIBUTE.toString())
  300 + : codeType == 'CUSTOM'
  301 + ? record.filter((item) => item.itemValue == CommandTypeEnum.CUSTOM.toString())
  302 + : [];
302 } 303 }
303 304
304 if (codeType == TaskTypeEnum.MODBUS_RTU) { 305 if (codeType == TaskTypeEnum.MODBUS_RTU) {
@@ -286,6 +286,7 @@ export const useSocket = (dataSourceRef: Ref<WidgetDataType[]>) => { @@ -286,6 +286,7 @@ export const useSocket = (dataSourceRef: Ref<WidgetDataType[]>) => {
286 throw Error(error as string); 286 throw Error(error as string);
287 } 287 }
288 }, 288 },
  289 + autoReconnect: true,
289 }); 290 });
290 291
291 const initSubscribe = () => { 292 const initSubscribe = () => {