Commit ab9eacc33de6a49708a1bb40459d4ed6bc8c8bce

Authored by ww
2 parents 6a2b472b 70490d49

chore: 合并代码解决冲突

Showing 42 changed files with 899 additions and 702 deletions
@@ -25,7 +25,7 @@ module.exports = defineConfig({ @@ -25,7 +25,7 @@ module.exports = defineConfig({
25 'plugin:jest/recommended', 25 'plugin:jest/recommended',
26 ], 26 ],
27 rules: { 27 rules: {
28 - 'no-console': 'error', 28 + 'no-console': 'off',
29 'vue/script-setup-uses-vars': 'error', 29 'vue/script-setup-uses-vars': 'error',
30 '@typescript-eslint/ban-ts-ignore': 'off', 30 '@typescript-eslint/ban-ts-ignore': 'off',
31 '@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
@@ -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
  1 +<script lang="ts" setup>
  2 + import { Button, Tabs, Badge, ButtonProps, Tag } from 'ant-design-vue';
  3 + import { get, isFunction, set, uniqBy } from 'lodash-es';
  4 + import { ExtractPropTypes, computed, unref, ref, nextTick, onMounted, watch } from 'vue';
  5 + import { DynamicProps } from '/#/utils';
  6 + import { BasicModal, useModal } from '/@/components/Modal';
  7 + import { BasicTable, BasicTableProps, TableRowSelection, useTable } from '/@/components/Table';
  8 + import { FETCH_SETTING } from '/@/components/Table/src/const';
  9 + import { useDesign } from '/@/hooks/web/useDesign';
  10 +
  11 + interface Options extends Recordable {
  12 + primaryKey?: string;
  13 + disabled?: boolean;
  14 + }
  15 +
  16 + enum Active {
  17 + PENDING = 'pending',
  18 + SELECTED = 'selected',
  19 + }
  20 +
  21 + interface ActionType {
  22 + setSelectedOptions: (options: Recordable[]) => void;
  23 + }
  24 +
  25 + const emit = defineEmits(['change', 'update:value']);
  26 +
  27 + const props = withDefaults(
  28 + defineProps<{
  29 + value?: string[];
  30 + labelField?: string;
  31 + valueField?: string;
  32 + primaryKey?: string;
  33 + params?: Recordable;
  34 + buttonName?: string;
  35 + pendingTableProps?: BasicTableProps;
  36 + selectedTableProps?: BasicTableProps;
  37 + maxTagLength?: number;
  38 + modalProps?: ExtractPropTypes<InstanceType<typeof BasicModal>['$props']>;
  39 + buttonProps?: ExtractPropTypes<InstanceType<typeof Button>['$props']>;
  40 + initSelectedOptions?: (actionType: ActionType) => Promise<Recordable[]>;
  41 + transformValue?: (selectedRowKeys: string[], selectedRows: Options[]) => any[];
  42 + onValueChange?: (selectedRowkeys: string[]) => any[];
  43 + onRemoveAfter?: (actionType: ActionType) => Promise<any>;
  44 + onSelectedAfter?: (actionType: ActionType) => Promise<any>;
  45 + disabled?: any;
  46 + }>(),
  47 + {
  48 + buttonName: '选择设备',
  49 + primaryKey: 'id',
  50 + maxTagLength: 2,
  51 + labelField: 'label',
  52 + valueField: 'value',
  53 + disabled: false,
  54 + }
  55 + );
  56 +
  57 + const { prefixCls } = useDesign('transfer-table-modal');
  58 +
  59 + const activeKey = ref<Active>(Active.PENDING);
  60 +
  61 + const selectedRows = ref<Options[]>([]);
  62 +
  63 + const selectedRowKeys = ref<string[]>(props.value || []);
  64 +
  65 + const pendingOptions = ref<Options[]>([]);
  66 +
  67 + const selectedConfirmQueue = ref<Options[]>([]);
  68 +
  69 + const pendingConfirmQueue = ref<Options[]>([]);
  70 +
  71 + const selectedTotal = ref(0);
  72 +
  73 + const pendingTotal = ref(0);
  74 +
  75 + const getFetchSetting = computed(() => {
  76 + const { pendingTableProps } = props;
  77 + return pendingTableProps?.fetchSetting || FETCH_SETTING;
  78 + });
  79 +
  80 + const getPendingRowSelection = computed<TableRowSelection>(() => {
  81 + const rowKeys = unref(selectedRowKeys);
  82 + return {
  83 + type: 'checkbox',
  84 + getCheckboxProps: (record: Recordable) => {
  85 + const { primaryKey } = props;
  86 + return {
  87 + ...record,
  88 + disabled: rowKeys.includes(record[primaryKey]),
  89 + };
  90 + },
  91 + onSelect: (_record: Recordable, _selected: boolean, selectedRows: Object[]) => {
  92 + pendingConfirmQueue.value = selectedRows;
  93 + },
  94 + onSelectAll: (_selected: boolean, selectedRows: Recordable[]) => {
  95 + pendingConfirmQueue.value = selectedRows;
  96 + },
  97 + };
  98 + });
  99 +
  100 + const getPendingTableBindProps = computed<Partial<DynamicProps<BasicTableProps>>>(() => {
  101 + const { pendingTableProps, primaryKey } = props;
  102 + return {
  103 + ...pendingTableProps,
  104 + rowKey: primaryKey,
  105 + api: handlePendingApiIntercept,
  106 + clickToRowSelect: false,
  107 + rowSelection: getPendingRowSelection,
  108 + };
  109 + });
  110 +
  111 + const getSelectedTableBindProps = computed<Partial<DynamicProps<BasicTableProps>>>(() => {
  112 + const { selectedTableProps, primaryKey } = props;
  113 + return {
  114 + ...selectedTableProps,
  115 + dataSource: selectedRows,
  116 + clickToRowSelect: false,
  117 + rowKey: primaryKey,
  118 + api: selectedTableProps!.api ? handleSelectedApiIntercept : undefined,
  119 + rowSelection: {
  120 + type: 'checkbox',
  121 + onSelect: (_record: Recordable, _selected: boolean, selectedRows: Object[]) => {
  122 + selectedConfirmQueue.value = selectedRows;
  123 + },
  124 + onSelectAll: (_selected: boolean, selectedRows: Recordable[]) => {
  125 + selectedConfirmQueue.value = selectedRows;
  126 + },
  127 + },
  128 + };
  129 + });
  130 +
  131 + const getModalBindProps = computed(() => {
  132 + const { modalProps = {} } = props;
  133 + return {
  134 + width: '60%',
  135 + title: '穿梭表格',
  136 + wrapClassName: prefixCls,
  137 + ...modalProps,
  138 + showOkBtn: false,
  139 + };
  140 + });
  141 +
  142 + const getBindButtonProps = computed<ButtonProps>(() => {
  143 + const { buttonProps = {} } = props;
  144 + return {
  145 + type: 'link',
  146 + ...buttonProps,
  147 + };
  148 + });
  149 +
  150 + const getShowTagOptions = computed(() => {
  151 + const { maxTagLength } = props;
  152 + return unref(selectedRows).slice(0, maxTagLength);
  153 + });
  154 +
  155 + const getSurplusOptionsLength = computed(() => {
  156 + const { maxTagLength } = props;
  157 + const surplusValue = unref(selectedRows).length - maxTagLength;
  158 + return surplusValue < 0 ? 0 : surplusValue;
  159 + });
  160 +
  161 + const [registerModal, { openModal }] = useModal();
  162 +
  163 + const [
  164 + regsterPendingTable,
  165 + {
  166 + getSelectRows: getPendingSelectRows,
  167 + getSelectRowKeys: getPendingSelectRowKeys,
  168 + reload: reloadPending,
  169 + clearSelectedRowKeys: clearPendingSelectedRowKeys,
  170 + },
  171 + ] = useTable(unref(getPendingTableBindProps));
  172 +
  173 + const [
  174 + registerSelectedTable,
  175 + { getSelectRowKeys, setProps, clearSelectedRowKeys, reload: reloadSelected },
  176 + ] = useTable(unref(getSelectedTableBindProps));
  177 +
  178 + async function handlePendingApiIntercept(params?: Recordable) {
  179 + try {
  180 + const { api } = props.pendingTableProps || {};
  181 + if (api && isFunction(api)) {
  182 + let options = await api(params);
  183 + pendingOptions.value = options;
  184 + const { totalField, listField } = unref(getFetchSetting);
  185 + const total = get(options, totalField!);
  186 + if (unref(selectedTotal) + unref(pendingTotal) !== total) {
  187 + pendingTotal.value = total;
  188 + }
  189 + let list: Recordable[] = get(options, listField!);
  190 + list = getSelectedRows(list);
  191 + options = set(options, listField!, list);
  192 + return options;
  193 + }
  194 + } catch (error) {
  195 + console.error(error);
  196 + return [];
  197 + }
  198 + return [];
  199 + }
  200 +
  201 + async function handleSelectedApiIntercept(params?: Recordable) {
  202 + try {
  203 + const { api } = props.selectedTableProps || {};
  204 + if (api && isFunction(api)) {
  205 + let options = await api(params);
  206 + pendingOptions.value = options;
  207 + const { totalField, listField } = unref(getFetchSetting);
  208 + selectedTotal.value = get(options, totalField!);
  209 + let list: Recordable[] = get(options, listField!);
  210 + list = getSelectedRows(list);
  211 + options = set(options, listField!, list);
  212 + return options;
  213 + }
  214 + } catch (error) {
  215 + console.error(error);
  216 + return [];
  217 + }
  218 + return [];
  219 + }
  220 +
  221 + const handleOpenModal = async () => {
  222 + const { disabled } = props;
  223 + if (disabled) return;
  224 + openModal(true);
  225 + await nextTick();
  226 + if (props.value && !props.value.length) {
  227 + activeKey.value = Active.PENDING;
  228 + reloadPending();
  229 + }
  230 + };
  231 +
  232 + const handleTriggerEmit = (selectedRowKeys: string[], selectedRows: Options[]) => {
  233 + const { transformValue } = props;
  234 + let value = selectedRowKeys;
  235 + if (transformValue && isFunction(transformValue)) {
  236 + value = transformValue(selectedRowKeys, selectedRows);
  237 + }
  238 + emit('change', unref(selectedRowKeys), unref(selectedRows));
  239 + emit('update:value', unref(value));
  240 + };
  241 +
  242 + const handleSelected = async () => {
  243 + const { onSelectedAfter } = props;
  244 + const currentPageSelectRows = getPendingSelectRows();
  245 + const currentPageSelectRowKeys = getPendingSelectRowKeys();
  246 + const { primaryKey } = props;
  247 + selectedRows.value = uniqBy([...unref(selectedRows), ...currentPageSelectRows], primaryKey);
  248 + selectedRowKeys.value = [...new Set([...unref(selectedRowKeys), ...currentPageSelectRowKeys])];
  249 + pendingConfirmQueue.value = [];
  250 + // selectedTotal.value = unref(selectedRowKeys).length;
  251 + pendingTotal.value = unref(pendingTotal) - currentPageSelectRows.length;
  252 + selectedTotal.value = unref(selectedTotal) + currentPageSelectRows.length;
  253 +
  254 + clearPendingSelectedRowKeys();
  255 + handleTriggerEmit(unref(selectedRowKeys), unref(selectedRows));
  256 +
  257 + if (onSelectedAfter && isFunction(onSelectedAfter)) {
  258 + await onSelectedAfter(actionType);
  259 + }
  260 + reloadPending();
  261 + };
  262 +
  263 + const handleRemoveSelected = async () => {
  264 + const { onRemoveAfter } = props;
  265 + const removeRowKeys = getSelectRowKeys();
  266 + selectedRowKeys.value = unref(selectedRowKeys).filter((key) => !removeRowKeys.includes(key));
  267 + selectedRows.value = unref(selectedRows).filter((item) => {
  268 + const { primaryKey } = props;
  269 + return unref(selectedRowKeys).includes(item[primaryKey]);
  270 + });
  271 + pendingTotal.value = unref(pendingTotal) + removeRowKeys.length;
  272 + selectedTotal.value = unref(selectedTotal) - removeRowKeys.length;
  273 +
  274 + clearSelectedRowKeys();
  275 + selectedConfirmQueue.value = [];
  276 + setProps({ dataSource: unref(selectedRows) });
  277 + handleTriggerEmit(unref(selectedRowKeys), unref(selectedRows));
  278 +
  279 + if (onRemoveAfter && isFunction(onRemoveAfter)) {
  280 + await onRemoveAfter(actionType);
  281 + }
  282 + };
  283 +
  284 + const actionType = {
  285 + setSelectedOptions,
  286 + setSelectedTotal,
  287 + reloadPending,
  288 + reloadSelected,
  289 + };
  290 +
  291 + const getSelectedRows = (options: Recordable[]) => {
  292 + const { labelField, valueField } = props;
  293 + return options.map((item) => ({ ...item, label: item[labelField], value: item[valueField] }));
  294 + };
  295 +
  296 + const getSelectedKeys = (options: Recordable[]) => {
  297 + const { primaryKey } = props;
  298 + return options.map((item) => item[primaryKey]);
  299 + };
  300 +
  301 + function setSelectedOptions(options: Recordable[]) {
  302 + selectedRows.value = getSelectedRows(options);
  303 + selectedRowKeys.value = getSelectedKeys(options);
  304 + }
  305 +
  306 + function setSelectedTotal(number: number) {
  307 + selectedTotal.value = number;
  308 + }
  309 +
  310 + const handleCheckoutPanel = async (keys: Active) => {
  311 + await nextTick();
  312 + if (keys === Active.PENDING) {
  313 + reloadPending();
  314 + } else {
  315 + reloadSelected();
  316 + setProps({
  317 + dataSource: unref(selectedRows),
  318 + });
  319 + }
  320 + };
  321 +
  322 + watch(
  323 + () => props.value,
  324 + () => {
  325 + if (props.value && !props.value.length) {
  326 + selectedRowKeys.value = [];
  327 + selectedRows.value = [];
  328 + // pendingTotal.value = 0;
  329 + selectedTotal.value = 0;
  330 + }
  331 + }
  332 + );
  333 +
  334 + onMounted(async () => {
  335 + const { initSelectedOptions } = props;
  336 + if (initSelectedOptions && isFunction(initSelectedOptions)) {
  337 + const options = await initSelectedOptions(actionType);
  338 + setSelectedOptions(options);
  339 + }
  340 + });
  341 +</script>
  342 +
  343 +<template>
  344 + <section>
  345 + <BasicModal @register="registerModal" v-bind="getModalBindProps">
  346 + <section class="bg-gray-100">
  347 + <Tabs v-model:active-key="activeKey" type="card" @change="handleCheckoutPanel">
  348 + <Tabs.TabPane :key="Active.PENDING">
  349 + <template #tab>
  350 + <div class="flex items-center justify-center">
  351 + <span>待选设备</span>
  352 + <Badge show-zero :count="pendingTotal" />
  353 + </div>
  354 + </template>
  355 + <BasicTable @register="regsterPendingTable">
  356 + <template #toolbar>
  357 + <section class="flex w-full justify-end items-center">
  358 + <!-- <Button type="primary">全选</Button> -->
  359 + <div class="text-blue-400">
  360 + <span class="mr-2">选择设备:</span>
  361 + <span>{{ pendingConfirmQueue.length }}</span>
  362 + </div>
  363 + </section>
  364 + </template>
  365 + </BasicTable>
  366 + <section class="flex justify-end px-4 pb-4">
  367 + <Button
  368 + type="primary"
  369 + @click="handleSelected"
  370 + :disabled="!pendingConfirmQueue.length"
  371 + >
  372 + <span>确定已选</span>
  373 + </Button>
  374 + </section>
  375 + </Tabs.TabPane>
  376 + <Tabs.TabPane :key="Active.SELECTED">
  377 + <template #tab>
  378 + <div class="flex items-center justify-center">
  379 + <span>已选设备</span>
  380 + <Badge show-zero :count="selectedTotal" />
  381 + </div>
  382 + </template>
  383 + <BasicTable @register="registerSelectedTable">
  384 + <template #toolbar>
  385 + <section class="flex w-full justify-end items-center">
  386 + <!-- <Button type="primary">全选</Button> -->
  387 + <div class="text-blue-400">
  388 + <span class="mr-2">选择设备:</span>
  389 + <span>{{ selectedConfirmQueue.length }}</span>
  390 + </div>
  391 + </section>
  392 + </template>
  393 + </BasicTable>
  394 + <section class="flex justify-end px-4 pb-4">
  395 + <Button
  396 + type="primary"
  397 + :disabled="!selectedConfirmQueue.length"
  398 + @click="handleRemoveSelected"
  399 + >
  400 + <span>移除已选</span>
  401 + </Button>
  402 + </section>
  403 + </Tabs.TabPane>
  404 + </Tabs>
  405 + </section>
  406 + </BasicModal>
  407 + <Button @click="handleOpenModal" v-bind="getBindButtonProps">
  408 + <span v-if="!selectedRowKeys.length">选择设备</span>
  409 + <div v-if="selectedRowKeys.length">
  410 + <Tag
  411 + class="!px-2 !py-1 !bg-gray-50 !border-gray-100"
  412 + v-for="item in getShowTagOptions"
  413 + :key="item.value"
  414 + >
  415 + <span>
  416 + {{ item.alias || item.name }}
  417 + </span>
  418 + </Tag>
  419 + <Tag class="!px-2 !py-1 !bg-gray-50 !border-gray-100" v-if="getSurplusOptionsLength">
  420 + <span> +{{ getSurplusOptionsLength }}... </span>
  421 + </Tag>
  422 + </div>
  423 + </Button>
  424 + </section>
  425 +</template>
  426 +
  427 +<style lang="less">
  428 + @prefix-cls: ~'@{namespace}-transfer-table-modal';
  429 +
  430 + .@{prefix-cls} {
  431 + .vben-basic-table {
  432 + padding-top: 0;
  433 + }
  434 +
  435 + .vben-basic-form > .ant-row {
  436 + width: 100%;
  437 + }
  438 +
  439 + .ant-tabs-top-bar {
  440 + background-color: #fff;
  441 + }
  442 + }
  443 +</style>
@@ -70,11 +70,6 @@ @@ -70,11 +70,6 @@
70 emitChange(); 70 emitChange();
71 }; 71 };
72 72
73 - // const disabled = computed(() => {  
74 - // const { disabled } = props || {};  
75 - // return disabled;  
76 - // });  
77 -  
78 //新增Input 73 //新增Input
79 const add = () => { 74 const add = () => {
80 dynamicInput.params.push({ 75 dynamicInput.params.push({
@@ -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>
@@ -37,7 +37,6 @@ @@ -37,7 +37,6 @@
37 const getBindProps = computed<Recordable>(() => { 37 const getBindProps = computed<Recordable>(() => {
38 const { value, apiTreeSelectProps = {} } = props; 38 const { value, apiTreeSelectProps = {} } = props;
39 const { params = {} } = apiTreeSelectProps; 39 const { params = {} } = apiTreeSelectProps;
40 - console.log(props, 'props');  
41 return { 40 return {
42 replaceFields: { children: 'children', key: 'id', title: 'name', value: 'id' }, 41 replaceFields: { children: 'children', key: 'id', title: 'name', value: 'id' },
43 getPopupContainer: () => document.body, 42 getPopupContainer: () => document.body,
@@ -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,8 +32,33 @@ export const step1Schemas: FormSchema[] = [ @@ -29,8 +32,33 @@ 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',
@@ -32,14 +32,14 @@ export const columns: BasicColumn[] = [ @@ -32,14 +32,14 @@ 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 `${record.alias}`
38 ), 38 ),
39 h( 39 h(
40 'div', 40 'div',
41 { 41 {
42 - class: 'cursor-pointer text-blue-500', 42 + class: 'cursor-pointer text-blue-500 truncate',
43 onClick: () => { 43 onClick: () => {
44 handeleCopy(`${record.name}`); 44 handeleCopy(`${record.name}`);
45 }, 45 },
@@ -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,13 @@ @@ -122,6 +122,13 @@
122 emit('openGatewayDeviceDetail', { id: data.gatewayId }); 122 emit('openGatewayDeviceDetail', { id: data.gatewayId });
123 }; 123 };
124 124
  125 + const drawerTitle = computed(() => {
  126 + return (
  127 + deviceDetail.value?.alias?.slice(0, 40) + '...' ||
  128 + deviceDetail.value?.name?.slice(0, 40) + '...'
  129 + );
  130 + });
  131 +
125 return { 132 return {
126 size, 133 size,
127 activeKey, 134 activeKey,
@@ -132,6 +139,7 @@ @@ -132,6 +139,7 @@
132 tbDeviceId, 139 tbDeviceId,
133 handleOpenTbDeviceDetail, 140 handleOpenTbDeviceDetail,
134 handleOpenGatewayDevice, 141 handleOpenGatewayDevice,
  142 + drawerTitle,
135 }; 143 };
136 }, 144 },
137 }); 145 });
@@ -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 });
@@ -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>
@@ -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 {
@@ -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 ];
@@ -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
  29 + const disabled = ref<boolean>(false);
28 const [register, { validateFields, setFieldsValue, resetFields, setProps }] = useForm({ 30 const [register, { validateFields, setFieldsValue, resetFields, setProps }] = useForm({
29 - schemas: modeForm(props.saveContent), 31 + schemas: modeForm(unref(disabled)),
30 ...modelFormPublicConfig, 32 ...modelFormPublicConfig,
31 }); 33 });
32 34
@@ -61,12 +63,21 @@ @@ -61,12 +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,
69 - setProps, 79 + setDisabledProps,
  80 + setCancelDisabled,
70 }); 81 });
71 </script> 82 </script>
72 <style lang="less" scoped> 83 <style lang="less" scoped>
@@ -75,7 +75,9 @@ @@ -75,7 +75,9 @@
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) { 77 if (businessText.value == BusinessDataFlowTextEnum.BUSINESS_MODAL_VIEW_TEXT) {
78 - dataFlowMethodRef.value?.setProps({ disabled: true }); 78 + dataFlowMethodRef.value?.setDisabledProps({ disabled: true });
  79 + } else {
  80 + dataFlowMethodRef.value?.setCancelDisabled();
79 } 81 }
80 restData.data = record; 82 restData.data = record;
81 setModalProps(modalProps(businessText.value)); 83 setModalProps(modalProps(businessText.value));
@@ -28,6 +28,7 @@ @@ -28,6 +28,7 @@
28 openModalValidate?: () => boolean; 28 openModalValidate?: () => boolean;
29 primaryKey?: string; 29 primaryKey?: string;
30 transformValue?: (list: Recordable[]) => any; 30 transformValue?: (list: Recordable[]) => any;
  31 + disabled?: boolean;
31 }>(), 32 }>(),
32 { 33 {
33 value: () => [], 34 value: () => [],
@@ -290,7 +291,7 @@ @@ -290,7 +291,7 @@
290 </Tabs> 291 </Tabs>
291 </section> 292 </section>
292 </BasicModal> 293 </BasicModal>
293 - <Button @click="handleOpenModal" type="link"> 294 + <Button @click="handleOpenModal" type="link" :disabled="disabled">
294 <span v-if="!selectedTotalList.length">选择设备</span> 295 <span v-if="!selectedTotalList.length">选择设备</span>
295 <div v-if="selectedTotalList.length"> 296 <div v-if="selectedTotalList.length">
296 <Tag 297 <Tag
@@ -33,7 +33,7 @@ export const modelFormPublicConfig = { @@ -33,7 +33,7 @@ export const modelFormPublicConfig = {
33 showSubmitButton: false, 33 showSubmitButton: false,
34 }; 34 };
35 35
36 -export const modeForm = (): FormSchema[] => { 36 +export const modeForm = (disabled: boolean): FormSchema[] => {
37 return [ 37 return [
38 { 38 {
39 field: BasicInfoFormField.CONVERT_CONFIG_ID, 39 field: BasicInfoFormField.CONVERT_CONFIG_ID,
@@ -70,6 +70,7 @@ export const modeForm = (): FormSchema[] => { @@ -70,6 +70,7 @@ export const modeForm = (): FormSchema[] => {
70 api: getDeviceProfile, 70 api: getDeviceProfile,
71 labelField: 'name', 71 labelField: 'name',
72 valueField: 'tbProfileId', 72 valueField: 'tbProfileId',
  73 + disabled,
73 transferProps: { 74 transferProps: {
74 listStyle: { height: '400px' }, 75 listStyle: { height: '400px' },
75 showSearch: true, 76 showSearch: true,
@@ -98,6 +99,7 @@ export const modeForm = (): FormSchema[] => { @@ -98,6 +99,7 @@ export const modeForm = (): FormSchema[] => {
98 componentProps: ({ formActionType }) => { 99 componentProps: ({ formActionType }) => {
99 const { getFieldsValue } = formActionType; 100 const { getFieldsValue } = formActionType;
100 return { 101 return {
  102 + disabled,
101 getPendingTableParams: () => { 103 getPendingTableParams: () => {
102 const values = getFieldsValue(); 104 const values = getFieldsValue();
103 const convertConfigId = Reflect.get(values, BasicInfoFormField.CONVERT_CONFIG_ID); 105 const convertConfigId = Reflect.get(values, BasicInfoFormField.CONVERT_CONFIG_ID);
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>  
@@ -201,14 +201,26 @@ @@ -201,14 +201,26 @@
201 ]; 201 ];
202 }); 202 });
203 203
204 - const changeOutTarget = () => { 204 + const changeOutTarget = (e) => {
  205 + if (!e) validateFields(['outTarget']);
  206 + else clearValidate('outTarget');
205 emit('getActionFormArr'); 207 emit('getActionFormArr');
206 }; 208 };
207 - const [registerAction, { getFieldsValue, resetFields, setFieldsValue, validate, setProps }] =  
208 - useForm({  
209 - schemas: actionSchema,  
210 - showActionButtonGroup: false,  
211 - }); 209 + const [
  210 + registerAction,
  211 + {
  212 + getFieldsValue,
  213 + resetFields,
  214 + setFieldsValue,
  215 + validate,
  216 + clearValidate,
  217 + validateFields,
  218 + setProps,
  219 + },
  220 + ] = useForm({
  221 + schemas: actionSchema,
  222 + showActionButtonGroup: false,
  223 + });
212 224
213 // 获取整个执行动作表单值 225 // 获取整个执行动作表单值
214 const getFieldsValueFunc = () => { 226 const getFieldsValueFunc = () => {
@@ -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 });
@@ -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,
@@ -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 }"