Commit e947951a2319b28e322d7495b458f3c278b12152

Authored by fengwotao
2 parents 0d634f6b 43058d19

Merge branch 'main_dev' into ft

Showing 84 changed files with 2457 additions and 984 deletions

Too many changes to show.

To preserve performance only 84 of 129 files are displayed.

... ... @@ -52,7 +52,7 @@ VITE_GLOB_ALARM_NOTIFY_POLLING_INTERVAL_TIME = 500000
52 52 VITE_GLOB_ALARM_NOTIFY_DURATION = 5
53 53
54 54 # Should Disabled Task Center Execute Interval Unit (Second)
55   -VITE_GLOB_DISABLED_TASK_CENTER_EXECUTE_INTERVAL_UNIT_SECOND = true
  55 +VITE_GLOB_DISABLED_TASK_CENTER_EXECUTE_INTERVAL_UNIT_SECOND = false
56 56
57 57 # Software version number
58 58 VITE_GLOB_SOFTWARE_VERSION_NUMBER = ThingsKit v1.2.0_release
... ...
... ... @@ -25,7 +25,7 @@ module.exports = defineConfig({
25 25 'plugin:jest/recommended',
26 26 ],
27 27 rules: {
28   - 'no-console': 'off',
  28 + 'no-console': 'error',
29 29 'vue/script-setup-uses-vars': 'error',
30 30 '@typescript-eslint/ban-ts-ignore': 'off',
31 31 '@typescript-eslint/explicit-function-return-type': 'off',
... ...
1   -版本:v1.1.2_release
  1 +版本:v1.2.0_release
2 2
3 3 ## 准备
4 4
... ...
1 1 import { DataType } from '../../device/model/modelOfMatterModel';
2   -import { DataTypeEnum } from '/@/components/Form/src/externalCompns/components/StructForm/config';
  2 +import { DataTypeEnum } from '/@/enums/objectModelEnum';
3 3 import { DataSource } from '/@/views/visual/palette/types';
4 4
5 5 export interface AddDataBoardParams {
... ...
  1 +import { defHttp } from '/@/utils/http/axios';
  2 +// import { PaginationResult } from '/#/axios';
  3 +
  4 +enum Api {
  5 + DEVICE_PROFILE_CATEGORY = '/device_profile/category',
  6 +}
  7 +
  8 +/**
  9 + * 新增产品分类
  10 + */
  11 +export const deviceProfileCategory = (params) => {
  12 + return defHttp.post<any>({
  13 + url: `${Api.DEVICE_PROFILE_CATEGORY}`,
  14 + params,
  15 + });
  16 +};
  17 +
  18 +/**
  19 + * 获取列表
  20 + */
  21 +export const getDeviceClassList = (params) => {
  22 + return defHttp.get<any>({
  23 + url: Api.DEVICE_PROFILE_CATEGORY,
  24 + params,
  25 + });
  26 +};
  27 +
  28 +/**
  29 + * 删除产品分类
  30 + */
  31 +export const deleteDeviceClass = (params) => {
  32 + return defHttp.delete<any>({
  33 + url: Api.DEVICE_PROFILE_CATEGORY,
  34 + params,
  35 + });
  36 +};
... ...
... ... @@ -194,6 +194,7 @@ export interface DeviceRecord {
194 194 customerAdditionalInfo?: {
195 195 isPublic?: boolean;
196 196 };
  197 + ifShowClass?: Boolean;
197 198 }
198 199
199 200 export interface DeviceModelOfMatterAttrs {
... ...
1   -import {
2   - DataTypeEnum,
3   - FunctionType,
4   -} from '/@/views/device/profiles/step/cpns/physical/cpns/config';
  1 +import { DataTypeEnum } from '/@/enums/objectModelEnum';
  2 +import { FunctionType } from '/@/views/device/profiles/step/cpns/physical/cpns/config';
5 3
6 4 export interface Specs {
7 5 min: string;
... ... @@ -26,7 +24,7 @@ export interface DataType {
26 24
27 25 export interface StructJSON {
28 26 functionName?: string;
29   - identifier?: string;
  27 + identifier: string;
30 28 remark?: string;
31 29 dataType?: DataType;
32 30 serviceCommand?: string;
... ... @@ -41,13 +39,14 @@ export interface FunctionJson {
41 39 }
42 40
43 41 export interface ModelOfMatterParams {
44   - deviceProfileId: string;
  42 + deviceProfileId?: string;
45 43 functionJson: FunctionJson;
46 44 functionName: string;
47 45 functionType: FunctionType;
48 46 identifier: string;
49 47 remark: string;
50 48 id?: string;
  49 + categoryId?: string;
51 50 callType?: string;
52 51 eventType?: string;
53 52 accessMode?: string;
... ... @@ -57,10 +56,12 @@ export interface ModelOfMatterParams {
57 56 export interface GetModelTslParams {
58 57 functionType: FunctionType;
59 58 deviceProfileId: string;
  59 + ifShowClass?: string | Boolean;
60 60 }
61 61
62 62 export interface ImportModelOfMatterType {
63 63 data: Recordable;
64 64 functionType: string;
65   - tkDeviceProfileId: string;
  65 + tkDeviceProfileId?: string;
  66 + categoryId?: string;
66 67 }
... ...
... ... @@ -18,13 +18,21 @@ enum ModelOfMatter {
18 18 IMPORT = '/things_model/import',
19 19
20 20 GET_MODEL_SERVICE = '/things_model/get_services',
  21 + CATEGORY = '/things_model/category',
  22 + CATEGORY_IMPORT = '/things_model/categoryImport',
  23 +
  24 + CATEGORY_EXPORT = '/things_model/categoryGetExport',
  25 +
  26 + IMPORT_CSV = '/things_model/csvImport',
21 27 }
22 28
23 29 export const getModelList = (
24 30 params: BasicPageParams & {
25   - deviceProfileId: string;
  31 + deviceProfileId?: string;
26 32 functionTyp?: FunctionType;
27 33 nameOrIdentifier?: string;
  34 + selectType?: string | undefined;
  35 + id?: string;
28 36 }
29 37 ) => {
30 38 return defHttp.get({
... ... @@ -82,3 +90,82 @@ export const importModelOfMatter = (data: ImportModelOfMatterType) => {
82 90 data,
83 91 });
84 92 };
  93 +
  94 +// 产品品类接口
  95 +
  96 +// 新增
  97 +export const createModelCategory = (params: Partial<ModelOfMatterParams>) => {
  98 + return defHttp.post({
  99 + url: ModelOfMatter.CATEGORY,
  100 + params,
  101 + });
  102 +};
  103 +
  104 +// 修改
  105 +export const updateModelCategory = (params: Partial<ModelOfMatterParams>) => {
  106 + return defHttp.put({
  107 + url: ModelOfMatter.CATEGORY,
  108 + params,
  109 + });
  110 +};
  111 +
  112 +// 删除
  113 +export const deleteModelCategory = (params: string[]) => {
  114 + return defHttp.delete({
  115 + url: ModelOfMatter.CATEGORY,
  116 + params: {
  117 + ids: params,
  118 + },
  119 + });
  120 +};
  121 +
  122 +// 导入
  123 +export const importModelCategory = (data: ImportModelOfMatterType) => {
  124 + return defHttp.post({
  125 + url: ModelOfMatter.CATEGORY_IMPORT,
  126 + data,
  127 + });
  128 +};
  129 +
  130 +// 导出
  131 +export const ExportModelCategory = (params: any) => {
  132 + const { functionType, deviceProfileId } = params;
  133 + return defHttp.get({
  134 + url: `${ModelOfMatter.CATEGORY_EXPORT}/${functionType}/${deviceProfileId}`,
  135 + params,
  136 + });
  137 +};
  138 +
  139 +/**
  140 + * 物模型产品品类界面excel导入
  141 + */
  142 +
  143 +export const importCsvCategory = (params: {
  144 + categoryId?: string;
  145 + deviceProfileId?: string;
  146 + file: FormData;
  147 +}) => {
  148 + const { categoryId } = params || {};
  149 +
  150 + return defHttp.post<any>({
  151 + url: `${ModelOfMatter.IMPORT_CSV}?categoryId=${categoryId}`,
  152 + params: params.file,
  153 + });
  154 +};
  155 +
  156 +/**
  157 + * 物模型产品物模型界面excel导入
  158 + */
  159 +
  160 +export const importCsvDeviceProfileId = (params: {
  161 + categoryId?: string;
  162 + deviceProfileId?: string;
  163 + file: FormData;
  164 +}) => {
  165 + const { deviceProfileId } = params || {};
  166 +
  167 + return defHttp.post<any>({
  168 + url: `${ModelOfMatter.IMPORT_CSV}?deviceProfileId=${deviceProfileId}`,
  169 + params: params.file,
  170 + });
  171 +};
... ...
1 1 import { BasicPageParams } from '/@/api/model/baseModel';
  2 +import { SceneLinkageDataType } from '/@/views/rule/linkedge/components/SceneLinkageDrawer/type';
2 3
3 4 export type ScreenLinkPageQueryParam = BasicPageParams & ScreenParams;
4 5
... ... @@ -14,97 +15,7 @@ export type ScreenByDeptIdParams = {
14 15 // organizationId: '2f5c8f2a-196c-4941-8771-290f9da76219';
15 16 };
16 17
17   -export interface ScreenAddModel {
18   - createTime?: string;
19   - creator?: string;
20   - defaultConfig?: string;
21   - description?: string;
22   - doAction?: [
23   - {
24   - command: string;
25   - createTime: string;
26   - creator: string;
27   - defaultConfig: string;
28   - description: string;
29   - deviceId: string;
30   - enabled: true;
31   - icon: string;
32   - id: string;
33   - name: string;
34   - outPut: string;
35   - outTarget: string;
36   - roleIds: [];
37   - tenantCode: string;
38   - tenantExpireTime: string;
39   - tenantId: string;
40   - tenantStatus: 'DISABLED';
41   - updateTime: string;
42   - updater: string;
43   - }
44   - ];
45   - doCondition?: [
46   - {
47   - compare: 0;
48   - createTime: string;
49   - creator: string;
50   - defaultConfig: string;
51   - description: string;
52   - deviceId: string;
53   - enabled: true;
54   - icon: string;
55   - id: string;
56   - name: string;
57   - property: string;
58   - roleIds: [];
59   - status: string;
60   - tenantCode: string;
61   - tenantExpireTime: string;
62   - tenantId: string;
63   - tenantStatus: 'DISABLED';
64   - updateTime: string;
65   - updater: string;
66   - value: string;
67   - }
68   - ];
69   - enabled?: true;
70   - icon?: string;
71   - id?: string;
72   - name?: string;
73   - organizationId?: string;
74   - roleIds?: [string];
75   - status?: string;
76   - tenantCode?: string;
77   - tenantExpireTime?: string;
78   - tenantId?: string;
79   - tenantStatus?: 'DISABLED';
80   - triggers?: [
81   - {
82   - attributeChoose?: string;
83   - compare?: 0;
84   - createTime?: string;
85   - creator?: string;
86   - defaultConfig?: string;
87   - description?: string;
88   - deviceId?: string;
89   - enabled?: true;
90   - icon?: string;
91   - id?: string;
92   - name?: string;
93   - roleIds?: [];
94   - tenantCode?: string;
95   - tenantExpireTime?: string;
96   - tenantId?: string;
97   - tenantStatus?: 'DISABLED';
98   - tiggerEvent?: string;
99   - touchWay?: string;
100   - updateTime?: string;
101   - updater?: string;
102   - value?: string;
103   - }
104   - ];
105   - updateTime?: string;
106   - updater?: string;
107   -}
  18 +export type ScreenAddModel = SceneLinkageDataType;
108 19
109 20 export interface IChangeStatus {
110 21 status?: number;
... ...
... ... @@ -6,6 +6,7 @@ import {
6 6 ScreenByDeptIdParams,
7 7 IChangeStatus,
8 8 } from '/@/api/ruleengine/model/ruleengineModel';
  9 +import { DeviceModel } from '../device/model/deviceModel';
9 10
10 11 enum ScreenManagerApi {
11 12 /**
... ... @@ -126,7 +127,7 @@ export const byOrganizationIdGetMasterDevice = (params: {
126 127 deviceProfileId?: string;
127 128 }) => {
128 129 const { organizationId, deviceProfileId } = params;
129   - return defHttp.get({
  130 + return defHttp.get<DeviceModel[]>({
130 131 url: `${ScreenManagerApi.MASTER_GET_DEVICE}`,
131 132 params: { deviceProfileId, organizationId },
132 133 });
... ...
... ... @@ -37,10 +37,11 @@ export interface AccountListItem {
37 37 }
38 38
39 39 export interface OrganizationListItem {
40   - id: string;
41   - name: string;
  40 + id?: string;
  41 + name?: string;
42 42 parentId?: string;
43   - remark: string;
  43 + remark?: string;
  44 + organizationId?: string;
44 45 }
45 46
46 47 export interface MenuListItem {
... ...
  1 +<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1699588251601" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1411" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M145.621959 0c-44.79888 0-79.998 36.81188-79.998 81.61076v860.77848c0 44.79888 35.19912 81.61076 79.998 81.61076h732.781681a81.969151 81.969151 0 0 0 81.61076-81.61076V324.80468L657.60916 0h-511.987201z" fill="#F17F53" p-id="1412"></path><path d="M657.60916 0v233.59416c0 25.59936 17.61236 92.79768 97.61036 92.79768h204.79488L657.60916 0z" fill="#FFFFFF" p-id="1413"></path><path d="M109.117272 659.874703c1.61276 0 2.867128 0.614385 3.814305 1.868753s1.663958 2.764731 2.175945 4.582286 0.81918 3.737507 0.972776 5.862253 0.230394 4.044699 0.230394 5.862254v5.708657c2.099148 3.302317 4.78708 6.886228 8.0126 10.726132s6.963026 7.449414 11.18692 10.80293 8.78058 6.143846 13.721257 8.39659 10.214145 3.379116 15.820405 3.379115c5.99025 0 11.622109-1.407965 16.869978-4.198295s10.137347-6.527837 14.617235-11.18692 8.652584-9.98375 12.441288-15.974001 7.21902-12.236494 10.265344-18.661933 5.759856-12.876478 8.089398-19.276318 4.377491-12.287693 6.067048-17.689158 3.046324-10.086148 4.044699-14.028449 1.689558-6.681433 2.099147-8.166196c2.611135-10.598135 4.81268-20.761081 6.681433-30.437639s2.764731-19.250719 2.764731-28.645684c0-2.303942-0.691183-3.60951-2.099147-3.891103l-37.19587 10.495738a19.916302 19.916302 0 0 1-5.094273 0.895978c-3.788705 0-6.963026-1.20317-9.522962-3.60951s-3.814305-5.862253-3.814305-10.342142 0.435189-8.114997 1.279968-10.879728 2.201545-4.81268 4.044699-6.220644 4.275093-2.329542 7.270218-2.764731 6.655834-0.665583 10.956527-0.665583h2.099147c2.40634-0.511987 5.478263-1.20317 9.21577-2.099148s7.935802-1.919952 12.518087-3.071923l14.694032-3.737507 15.743607-4.044699c5.299068-1.356766 10.572536-2.662333 15.820404-3.9679a1637.283868 1637.283868 0 0 1 23.474613-5.63186l5.990251-1.279968a22.01545 22.01545 0 0 1 3.737506-0.588785c1.20317 0 2.303942 0.358391 3.302318 1.049574s1.817555 1.58716 2.483138 2.636734a14.514837 14.514837 0 0 1 2.175945 7.423814c0 2.995125-1.151971 5.60626-3.455913 7.807805s-5.19667 4.121497-8.703783 5.785456-7.321417 3.097523-11.468513 4.351891l-11.62211 3.532711c-3.60951 1.100772-6.681433 2.175946-9.215769 3.22552s-3.967901 2.227144-4.275093 3.532711c-0.511987 4.40309-0.998375 9.292568-1.510363 14.694033s-1.049574 10.546936-1.638359 15.462014c-0.204795 1.20317-0.614385 4.095898-1.279968 8.703782s-1.561561 10.188545-2.687932 16.79318-2.559936 13.823654-4.198295 21.682658-3.532712 15.564411-5.63186 23.167421c-2.303942 8.294193-5.324667 17.151571-9.062173 26.546536s-8.191795 18.559536-13.337267 27.442514-10.982125 17.228369-17.484363 24.984976-13.542061 14.130847-21.145071 19.122721a58.929727 58.929727 0 0 1-23.19302 8.089398 61.336067 61.336067 0 0 1-9.369366 0.742382c-5.19667 0-10.828529-0.691183-16.869978-2.099148s-12.134097-3.404715-18.226744-5.99025-11.9805-5.734257-17.61236-9.369366-10.674933-7.705407-15.078023-12.159696-7.910202-9.21577-10.495738-14.335641-3.891103-10.444539-3.891102-16.050799c0-4.300692 0.435189-8.0126 1.279968-11.110122s2.227144-5.60626 4.121496-7.500613 4.428689-3.302317 7.577411-4.198295 7.014225-1.484763 11.519712-1.484763zM305.61796 693.333067c3.404715 1.20317 6.886228 2.611135 10.418939 4.198295s7.21902 3.123122 11.033324 4.582285 7.782205 2.662333 11.929302 3.686308 8.575786 1.510362 13.286068 1.510362c5.503862 0 11.058924-0.639984 16.639584-1.945551s11.084523-3.225519 16.434789-5.785455 10.470138-5.657459 15.385215-9.292568 9.394965-7.884603 13.490863-12.671683c4.505487-5.094273 8.242994-9.727757 11.238119-13.874854s5.427064-7.859004 7.270218-11.110122 3.174321-6.041449 3.967901-8.39659 1.20317-4.377491 1.20317-6.067048c0-2.508737-0.844779-4.684683-2.559936-6.527837s-3.967901-3.379116-6.835029-4.582285-6.067048-2.073548-9.676558-2.636734-7.347016-0.81918-11.238119-0.81918c-2.303942 0-4.991875 0.076798-8.089398 0.230394l-9.446164 0.460789-9.292567 0.38399a150.39624 150.39624 0 0 1-7.807805 0.153596c-3.302317 0-6.732632-0.38399-10.265344-1.126372s-7.014225-1.971151-10.418939-3.686307-6.553436-3.9935-9.446164-6.911828a35.403915 35.403915 0 0 1-8.831779-15.743606 24.626584 24.626584 0 0 1-0.742382-6.143846c0-2.40634 0.179196-4.684683 0.537587-6.83503s0.767981-4.223894 1.279968-6.220644c1.407965-4.991875 3.737507-10.086148 6.963026-15.231619s7.142221-10.162946 11.698907-15.078023 9.497363-9.650959 14.847629-14.258844 10.854129-8.934177 16.511587-12.978875 11.21252-7.705407 16.639584-10.956526 10.521337-6.01585 15.23162-8.319792c6.604635-3.19992 13.337267-5.529462 20.172295-6.963026s13.61886-2.175946 20.325892-2.175946c3.9935 0 7.807805 0.537587 11.391715 1.58716s6.78383 2.585535 9.522962 4.582286 4.915077 4.479888 6.527837 7.423814 2.40634 6.323042 2.40634 10.137347c0 2.201545-0.255994 4.044699-0.742381 5.555061s-1.151971 2.713532-1.945552 3.686308-1.663958 1.61276-2.636734 2.022349-1.919952 0.588785-2.918327 0.588785c-2.687933 0-5.171071-0.307192-7.423814-0.895977s-4.428689-1.279968-6.527837-2.02235-4.198295-1.459164-6.297443-2.099147-4.40309-0.972776-6.911827-0.972776c-7.398215 0-14.924427 1.484763-22.578635 4.428689s-15.103622 6.707032-22.348242 11.23812-14.105247 9.625359-20.556286 15.231619-12.236494 11.058924-17.330767 16.357991c-2.687933 2.79033-4.684683 5.60626-5.913452 8.39659s-1.868753 5.452664-1.868753 7.961401c0 3.711907 1.61276 6.604635 4.81268 8.703782s7.091023 3.148721 11.698907 3.148722c2.687933 0 5.759856-0.051199 9.138972-0.153597a796.140096 796.140096 0 0 0 20.761081-0.844779c3.353516-0.153596 6.323042-0.230394 8.934176-0.230394 6.195045 0 12.159696 0.870378 17.842754 2.636734s10.751731 4.326292 15.154821 7.731007 7.935802 7.628609 10.572536 12.671683 3.967901 10.828529 3.967901 17.330767c0 3.9935-0.588785 8.038199-1.791955 12.159696s-2.841529 8.217395-4.940677 12.364491-4.505487 8.242994-7.19342 12.287693-5.555061 8.0126-8.550186 11.929301c-5.401465 6.988625-11.417315 13.59326-18.073148 19.788306s-13.721257 11.59651-21.22187 16.204395-15.436414 8.242994-23.781805 10.956526-16.921177 4.044699-25.727357 4.044699a74.724532 74.724532 0 0 1-23.551411-4.121497c-3.891103-1.356766-7.577411-3.046324-11.033325-5.094273s-6.476638-4.377491-9.062173-6.963026-4.684683-5.478263-6.220644-8.626984-2.329542-6.579036-2.329542-10.265344c0-1.791955 0.281593-3.532712 0.819179-5.17107s1.868753-2.559936 4.070299-2.559936zM497.152371 651.631709c1.791955-7.500612 4.40309-15.18042 7.807805-23.013824s7.398215-15.718007 12.0061-23.62821 9.727757-15.666808 15.385215-23.321017a316.868878 316.868878 0 0 1 57.982551-59.928102c3.19992-2.201545 6.143846-3.660708 8.857378-4.351891s5.555061-1.049574 8.550186-1.049573c3.19992 0 6.041449 0.486388 8.550187 1.433564s4.991875 2.662333 7.500612 5.17107c0.79358 0.895978 1.638359 1.689558 2.559936 2.40634s1.740756 1.049574 2.559936 1.049574c0.204795 0 0.460788-0.230394 0.742382-0.665583l0.972775-1.587161c0.358391-0.614385 0.767981-1.228769 1.279968-1.868753s1.100772-1.126372 1.791956-1.433564c1.689558-0.79358 3.737507-1.331167 6.067048-1.58716s4.735882-0.38399 7.116622-0.383991c4.505487 0 8.857379 0.81918 13.055674 2.483138s7.910202 3.967901 11.110122 6.963026c7.60301 7.091023 13.362866 15.59001 17.330767 25.496963s5.913452 20.556286 5.913452 31.948001c0 10.188545-1.510362 21.19627-4.505488 32.997575-2.611135 10.39334-6.323042 20.505087-11.18692 30.309642s-10.546936 18.994725-17.100373 27.59611-13.849254 16.460388-21.887452 23.551412-16.613985 13.20927-25.727357 18.303542-18.610735 9.062173-28.492088 11.929302-19.941901 4.275093-30.156046 4.275093c-9.497363 0-18.021949-1.459164-25.573761-4.351891s-13.951651-6.988625-19.19952-12.287693-9.241369-11.59651-12.006099-18.892328-4.121497-15.410815-4.121497-24.293793c0-3.788705 0.230394-7.628609 0.665583-11.468513s1.151971-7.807805 2.150346-11.801305z m25.343367 10.060549a63.230419 63.230419 0 0 0-1.638359 13.490862c0 5.60626 0.972776 10.521337 2.918327 14.770831s4.710282 7.807805 8.242994 10.649334 7.807805 4.991875 12.748481 6.451039 10.367741 2.175946 16.281193 2.175945c6.988625 0 14.130847-1.100772 21.375465-3.302317s14.335642-5.401465 21.22187-9.59976 13.542061-9.241369 19.941901-15.154821 12.313292-12.594885 17.765956-20.095498 10.290943-15.641209 14.540437-24.447389 7.679808-18.149946 10.265343-28.056898c0.998375-3.60951 1.715157-7.347016 2.175946-11.238119s0.665583-7.654209 0.665583-11.238119c0-5.811055-0.639984-11.340516-1.945551-16.639584s-3.455914-10.00935-6.451039-14.105248-6.937427-7.372616-11.852504-9.830154-10.905327-3.686308-17.99635-3.686308c-0.998375 0-1.766356 0.435189-2.329542 1.279968s-1.126372 1.919952-1.715157 3.225519c-0.511987 1.100772-1.075173 2.252744-1.715157 3.455914a11.878103 11.878103 0 0 1-6.451039 5.555061 15.794805 15.794805 0 0 1-5.785455 0.895978c-1.689558 0-3.19992-0.38399-4.505487-1.126372s-2.611135-1.766356-3.891103-3.071923l-1.510362-1.433564a1.971151 1.971151 0 0 0-1.356766-0.537587c-0.895978 0-2.073548 0.40959-3.532712 1.20317s-2.943926 1.740756-4.505487 2.841529-3.046324 2.252744-4.505488 3.455914-2.585535 2.252744-3.379115 3.148721c-6.80943 6.988625-13.567661 14.617235-20.325892 22.885828s-12.978876 16.767581-18.661934 25.57376-10.674933 17.663558-14.924427 26.623335-7.270218 17.561161-9.16457 25.880953zM760.083398 546.495138c0.588785-2.611135 1.356766-5.375866 2.252744-8.319792s2.099148-5.657459 3.609509-8.089398 3.455914-4.505487 5.862254-6.143847 5.452664-2.483138 9.138971-2.483137c3.507112 0 6.630234 0.972776 9.369366 2.918327s5.222269 3.916702 7.423814 5.913452c2.611135 1.689558 5.094273 5.145471 7.500613 10.342141s4.863878 11.59651 7.423814 19.19952 5.299068 16.204395 8.242994 25.804155 6.195045 19.660308 9.753356 30.156046 7.526212 21.221869 11.929302 32.178396 9.394965 21.631459 15.001225 32.024799l3.148721 5.631859a95.076023 95.076023 0 0 0 7.142222 10.879728c1.049574 1.305567 1.817555 1.945551 2.329542 1.945552 0.307192 0 0.639984-0.742381 1.049573-2.252744s0.844779-3.302317 1.356767-5.401465l1.58716-6.451039a59.902502 59.902502 0 0 1 1.58716-5.401465 476.736882 476.736882 0 0 0 13.721257-37.426264c2.739132-8.447789 5.785455-18.482738 9.062174-30.079248a1560.127397 1560.127397 0 0 0 14.182045-54.526637c1.151971-4.966276 2.252744-10.00935 3.302317-15.154821s1.971151-10.162946 2.764731-15.078023 1.356766-9.138972 1.638359-12.748481a77.540461 77.540461 0 0 1 0.691183-8.242994c0.255994-1.894353 0.563186-3.865503 0.972776-5.913452s0.921577-3.916702 1.58716-5.63186 1.484763-3.097523 2.483138-4.198295 2.201545-1.638359 3.60951-1.638359 3.123122 0.204795 5.171071 0.588786 3.916702 0.998375 5.631859 1.791955c1.894353 2.611135 3.635109 5.734257 5.17107 9.369366s2.329542 8.575786 2.329542 14.77083c0 3.891103-0.40959 8.575786-1.20317 14.02845s-1.817555 11.289318-3.071923 17.561161a634.864128 634.864128 0 0 1-8.934177 38.245443c-1.61276 6.041449-3.123122 11.59651-4.582285 16.639584s-2.662333 9.21577-3.686308 12.518087c-1.791955 5.811055-3.686308 11.775706-5.631859 17.919552s-4.095898 12.594885-6.451039 19.353117-4.889478 13.900452-7.654208 21.452263-5.836654 15.61561-9.21577 24.216995c-1.510362 3.891103-3.225519 7.807805-5.171071 11.698907s-4.172696 7.423814-6.681433 10.572536-5.324667 5.708657-8.473388 7.654209-6.681433 2.918327-10.572536 2.918327c-2.303942 0-4.684683-0.332792-7.116622-0.972776s-4.531087-1.971151-6.220644-3.967901c-8.191795-9.497363-15.487613-20.274693-21.887453-32.331991s-12.134097-24.652184-17.17717-37.810255-9.522962-26.444139-13.414065-39.909402-7.449414-26.316142-10.649334-38.629435l-0.895978-3.455913c-0.40959-1.407965-0.767981-2.81593-1.126371-4.275093s-0.691183-2.764731-1.049574-3.967901-0.742381-2.175946-1.20317-2.918327-0.972776-1.126372-1.58716-1.126372-1.356766 1.100772-2.252744 3.302317-1.791955 4.684683-2.687933 7.423815-1.715157 5.427064-2.483138 8.012599l-1.433564 4.940677c-0.691183 2.611135-1.740756 6.323042-3.148721 11.18692l-4.735882 16.434789c-1.766356 6.118247-3.660708 12.543686-5.708657 19.353117l-5.913452 19.737106a1165.026874 1165.026874 0 0 1-12.902078 40.728582l-3.891102 12.364491c-1.279968 4.147096-2.508737 7.935802-3.60951 11.314917s-1.791955 5.657459-2.099148 6.758231c-1.20317 3.788705-2.969526 6.681433-5.324667 8.626984s-4.735882 2.918327-7.116622 2.918327c-1.510362 0-2.995125-0.537587-4.505487-1.58716s-2.81593-2.329542-3.967901-3.814305-2.099148-3.123122-2.841529-4.863878-1.126372-3.327917-1.126372-4.735882c0-0.79358 0.255994-2.073548 0.742382-3.814304s1.305567-4.735882 2.40634-8.934177l3.891102-15.462013 44.338092-143.638009z" fill="#FFFFFF" p-id="1414"></path></svg>
\ No newline at end of file
... ...
  1 +<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1699933658783" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1443" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M145.621959 0c-44.79888 0-79.998 36.81188-79.998 81.61076v860.77848c0 44.79888 35.19912 81.61076 79.998 81.61076h732.781681a81.969151 81.969151 0 0 0 81.61076-81.61076V324.80468L657.60916 0h-511.987201z" fill="#25B39E" p-id="1444"></path><path d="M657.60916 0v233.59416c0 25.59936 17.61236 92.79768 97.61036 92.79768h204.79488L657.60916 0z" fill="#FFFFFF" p-id="1445"></path><path d="M158.498438 694.485038c0-3.507112 0.511987-7.19342 1.510362-11.110122 3.404715-13.59326 7.372616-26.930527 11.929302-39.986201s8.678183-26.034549 12.36449-38.936626 6.78383-23.781805 9.21577-32.613585 4.633484-16.460388 6.527837-22.80903 3.660708-11.826904 5.247869-16.434789 3.353516-9.138972 5.247868-13.644459c1.20317-2.687933 2.534337-5.503862 3.967901-8.39659s3.020724-5.58066 4.735882-8.0126 3.481513-4.454289 5.324667-5.99025 3.763106-2.329542 5.785455-2.329542c4.095898 0 7.21902 0.665583 9.369366 2.02235s3.481513 2.585535 3.9935 3.686308l1.356766 6.143846c1.510362-0.102397 3.455914-0.38399 5.862254-0.819179s4.81268-0.921577 7.270218-1.433565 4.735882-0.998375 6.835029-1.510362 3.711907-0.947176 4.81268-1.356766c6.604635-1.791955 12.697283-3.353516 18.303542-4.659083s10.930927-2.38074 15.974001-3.22552 9.881353-1.484763 14.463638-1.868753 9.241369-0.588785 13.951651-0.588785c4.607885 0 8.345391 0.255994 11.238119 0.742381s5.19667 1.177571 6.911828 2.02235 2.867128 1.843154 3.532711 2.995125 0.972776 2.38074 0.972776 3.686308c0 1.20317-0.332792 2.483138-0.972776 3.814304s-1.715157 2.636734-3.225519 3.814305-3.455914 2.252744-5.862254 3.148721-5.401465 1.510362-9.010974 1.791955c-0.895978 0.204795-2.969526 0.563186-6.220645 1.126372l-10.572535 1.791955-10.80293 1.868754a129.60956 129.60956 0 0 0-7.039824 1.356766h0.153596c-0.588785 0.102397-2.022349 0.486388-4.275093 1.126372s-4.966276 1.407965-8.166196 2.252743-6.681433 1.817555-10.41894 2.918327l-11.110122 3.22552a687.163621 687.163621 0 0 0-17.637959 5.324667c-6.092648 2.40634-10.879728 5.58066-14.335642 9.522961s-6.169446 8.370991-8.166195 13.286068c-0.40959 0.895978-0.998375 2.431939-1.791956 4.582286l-2.636734 7.347016-3.071923 8.780581-3.148721 8.857378c-0.998375 2.81593-1.868753 5.350266-2.636734 7.654209s-1.279968 3.9935-1.587161 5.094272c-0.307192 0.998375-0.511987 1.894353-0.588785 2.687933s-0.153596 1.407965-0.153596 1.791955c0 0.588785 0.153596 0.895978 0.460788 0.895978 1.510362 0 4.633484-0.665583 9.369366-2.022349s10.290943-3.020724 16.639584-5.017475l20.40269-6.451039c7.244619-2.303942 14.207645-4.454289 20.837879-6.451038s12.569286-3.686308 17.765956-5.017475 8.959776-2.022349 11.238119-2.022349c3.507112 0 5.862253 0.563186 7.039824 1.715157s1.791955 2.431939 1.791955 3.814304c0 3.60951-1.279968 6.758231-3.814304 9.446164s-5.734257 5.043074-9.522962 7.039824c-1.20317 0.588785-3.430314 1.484763-6.681433 2.636734s-6.758231 2.329542-10.495738 3.532712l-10.726132 3.455914-7.347016 2.406339c-9.804555 3.788705-17.765956 6.937427-23.935402 9.369366s-11.135722 4.454289-14.924427 5.99025l-8.78058 3.532712c-2.047949 0.81918-3.942301 1.484763-5.708657 2.02235s-3.686308 1.075173-5.785456 1.58716-4.940676 1.254369-8.652583 2.150346c-0.895978 2.099148-1.945551 5.273468-3.148722 9.522962s-2.355141 8.652584-3.455913 13.20927-2.022349 8.78058-2.764731 12.671683-1.126372 6.604635-1.126372 8.089398c0 1.791955 0.255994 3.123122 0.742381 3.967901s1.100772 1.433564 1.791956 1.715157 1.407965 0.460788 2.099147 0.460788h1.638359c4.81268 0 10.572536-0.691183 17.330767-2.099147s13.414065-3.251119 20.018699-5.555061c6.297443-2.201545 11.801305-4.607885 16.511588-7.193421s9.113372-5.017475 13.286068-7.270218 8.191795-4.147096 12.159696-5.708657 8.268593-2.38074 12.978875-2.483138c0.998375 0 2.175946 0.102397 3.532712 0.307192s2.636734 0.563186 3.814304 1.126372 2.175946 1.279968 2.918327 2.175946 1.023974 2.047949 0.81918 3.455913c-0.40959 2.892728-1.843154 5.759856-4.351891 8.550186s-5.657459 5.503862-9.446164 8.089398-8.038199 5.094273-12.748481 7.500613-9.497363 4.684683-14.41244 6.835029-9.702157 4.095898-14.41244 5.862253-8.908577 3.276718-12.594885 4.582286c-9.19017 3.19992-18.149946 5.555061-26.853729 7.039824s-16.460388 2.252744-23.372215 2.252743c-7.60301 0-13.414065-1.433564-17.484363-4.275093s-6.067048-7.577411-6.067048-14.182045zM336.541986 699.579311a14.489238 14.489238 0 0 1-13.721257 1.638359c-1.356766-0.691183-2.508737-1.61276-3.455913-2.764731a12.902077 12.902077 0 0 1-2.918327-8.319792c0-2.099148 0.435189-3.865503 1.279968-5.324667s1.894353-2.687933 3.148721-3.737507 2.611135-1.919952 4.044699-2.636734 2.764731-1.305567 3.967901-1.791955c3.9935-2.611135 8.498988-5.811055 13.490863-9.59976s10.137347-7.884603 15.462013-12.236494 10.572536-8.78058 15.820404-13.286068l14.924427-12.902077 6.758231-5.990251a121.59696 121.59696 0 0 0-13.59326-31.128821l-5.017474-8.166196-3.686308-5.990251a7.782205 7.782205 0 0 1-1.279968-4.198295 14.258844 14.258844 0 0 1 7.116622-11.340516 11.417315 11.417315 0 0 1 5.785455-1.433564c3.19992 0 6.01585 0.665583 8.473388 2.022349s4.633484 3.071923 6.527837 5.171071 3.558311 4.377491 4.940677 6.835029 2.662333 4.78708 3.737506 6.963026c0.588785 1.20317 1.254369 2.79033 1.945552 4.81268s1.382365 4.121497 2.022349 6.37424l1.868753 6.604635 1.510363 5.631859c4.40309-4.198295 8.678183-8.601385 12.825279-13.209269a587.556511 587.556511 0 0 0 18.892328-22.194646c1.740756-2.201545 3.58391-4.223894 5.478263-6.067048s3.839904-3.379116 5.862253-4.582285 4.147096-1.791955 6.451039-1.791955c3.891103 0 6.681433 0.998375 8.319792 2.995125s2.483138 4.607885 2.483138 7.807804c0 3.404715-0.614385 6.527837-1.868753 9.369366s-2.867128 5.068673-4.863879 6.681433c-2.892728 2.303942-6.067048 5.068673-9.522962 8.319792s-6.937427 6.681433-10.495737 10.265344l-10.572536 10.649333c-3.507112 3.507112-6.707032 6.655834-9.59976 9.446164-1.20317 1.100772-2.227144 2.201545-3.071923 3.302318s-1.279968 2.508737-1.279968 4.198295c0 0.998375 0.281593 2.252744 0.819179 3.737506s1.254369 3.097523 2.099148 4.81268l2.559936 5.171071c0.870378 1.740756 1.58716 3.327917 2.175945 4.735881 1.20317 2.687933 2.867128 5.887853 5.017475 9.59976s4.300692 7.577411 6.451039 11.62211 4.0191 8.166196 5.631859 12.364491 2.40634 8.191795 2.40634 12.006099a20.761081 20.761081 0 0 1-3.737507 12.082898c-1.075173 1.561561-2.329542 2.764731-3.686308 3.686308s-2.636734 1.356766-3.814304 1.356766c-2.611135 0-4.684683-0.307192-6.220645-0.895977s-2.867128-1.510362-3.967901-2.687933-2.022349-2.662333-2.764731-4.428689-1.58716-3.763106-2.483137-6.067049c-1.305567-3.711907-2.995125-7.961401-5.094273-12.748481s-4.275093-9.548561-6.527837-14.258844-4.377491-9.062173-6.374241-13.132471-3.558311-7.116622-4.659083-9.21577l-8.626984 6.835029-11.468514 9.062174-17.919552 14.105247c-7.244619 5.708657-16.588385 12.953276-28.0057 21.861854zM490.752531 646.921427a158.588035 158.588035 0 0 1 22.041049-48.510787c5.299068-7.833404 11.314917-15.103622 18.073148-21.759456s14.130847-12.082898 22.117847-16.281193c2.611135-1.407965 5.452664-2.457539 8.550187-3.148722s6.246244-1.049574 9.446163-1.049573c3.302317 0 6.374241 0.307192 9.21577 0.895977s5.299068 1.61276 7.347016 2.995125 3.686308 3.19992 4.863879 5.401465 1.791955 4.889478 1.791955 8.089398c0 2.40634-0.358391 4.81268-1.049574 7.270218s-1.740756 4.659084-3.148721 6.604635-3.148721 3.532712-5.247869 4.735882-4.556686 1.791955-7.347016 1.791955c-2.201545 0-4.223894-0.40959-6.067048-1.20317a47.102822 47.102822 0 0 1-7.884603-4.275093 19.916302 19.916302 0 0 0-2.841529-1.279968c-3.9935 1.791955-7.782205 4.172696-11.314917 7.116622s-6.860628 6.220644-9.906953 9.830154-5.862253 7.449414-8.39659 11.545312-4.838279 8.191795-6.911827 12.287693-3.788705 8.114997-5.247869 12.082897-2.636734 7.577411-3.532712 10.879728c-0.691183 2.40634-1.331167 5.119872-1.868753 8.166196s-0.81918 6.01585-0.819179 8.934177c0 1.99675 0.179196 3.891103 0.537586 5.708657s0.972776 3.404715 1.868753 4.81268 2.099148 2.508737 3.60951 3.302317 3.353516 1.20317 5.555061 1.20317c3.507112 0 6.963026-0.460788 10.41894-1.356766s6.835029-2.047949 10.137346-3.455913 6.527837-3.020724 9.676559-4.863879a314.129747 314.129747 0 0 0 14.924426-9.369366c1.919952-1.279968 3.711907-2.457539 5.401465-3.455913s3.276718-1.817555 4.735882-2.483138 2.713532-0.972776 3.814305-0.972776c1.510362 0 2.892728 0.38399 4.198295 1.126372s1.945551 2.022349 1.945551 3.814305a12.79968 12.79968 0 0 1-0.895978 4.351891c-1.407965 3.891103-3.302317 7.398215-5.708657 10.495738s-5.068673 5.862253-8.0126 8.319792-6.092648 4.633484-9.446163 6.527836-6.681433 3.660708-9.983751 5.247869c-6.195045 3.302317-12.466888 5.785455-18.815529 7.423815s-11.673308 2.483138-15.974001 2.483137c-1.407965 0-3.046324-0.153596-4.940677-0.460788s-3.558311-0.742381-4.940676-1.356766c-8.089398-3.507112-14.00285-8.191795-17.689158-14.105247s-5.555061-12.902077-5.555061-20.991476c0-3.507112 0.281593-7.167821 0.81918-11.033324s1.356766-7.807805 2.457538-12.031699zM618.698133 647.689408c0-4.300692 1.151971-7.961401 3.455913-10.956526s5.350266-4.991875 9.138972-5.990251a138.287743 138.287743 0 0 1 13.644459-21.17067c3.404715-4.40309 7.270218-8.831779 11.622109-13.286068s9.138972-8.498988 14.41244-12.159696 10.905327-6.630234 16.946776-8.934177 12.466888-3.455914 19.276318-3.455913c5.811055 0 10.854129 0.691183 15.154821 2.099147s7.859004 3.353516 10.649334 5.862254 4.889478 5.478263 6.297443 8.934176 2.099148 7.167821 2.099147 11.186921c0 3.404715-0.460788 6.655834-1.356766 9.753356-1.407965 5.60626-3.9935 10.495738-7.807805 14.694032s-8.268593 7.884603-13.414064 11.033325-10.751731 5.785455-16.793181 7.884602-12.108497 3.839904-18.149946 5.247869-11.878103 2.483138-17.484363 3.22552-10.444539 1.331167-14.540436 1.715157c-0.895978 1.791955-1.663958 4.070298-2.329542 6.835029s-0.972776 5.427064-0.972776 8.012599c0 5.811055 2.303942 10.290943 6.911828 13.490863s10.956526 4.81268 19.045923 4.81268c4.710282 0 9.19017-0.537587 13.490863-1.58716s8.370991-2.40634 12.236494-4.044699 7.475013-3.455914 10.879728-5.401465 6.502237-3.865503 9.292568-5.785456c3.60951-2.40634 7.014225-4.428689 10.265343-6.067048s6.118247-2.483138 8.626985-2.483138 4.095898 0.614385 4.812679 1.868753 1.049574 2.585535 1.049574 3.967901a34.251944 34.251944 0 0 1-0.307192 4.198295 25.804155 25.804155 0 0 1-8.703783 14.566036c-10.39334 8.191795-21.068273 14.054049-32.024799 17.561161s-22.629834 5.247869-35.019925 5.247869c-2.40634 0-5.222269-0.153596-8.473388-0.460789s-6.630234-0.895978-10.137346-1.791955-6.937427-2.201545-10.342142-3.891103a31.538412 31.538412 0 0 1-15.513212-17.100372 40.037399 40.037399 0 0 1-2.483138-14.847629c0-4.710282 0.639984-9.958151 1.945552-15.743606-1.305567-0.204795-2.534337-0.870378-3.686308-2.02235s-1.715157-2.81593-1.715157-5.017474z m93.898452-41.547762c0.204795-0.511987 0.307192-0.947176 0.307192-1.356766v-1.356766c0-2.687933-0.998375-4.735882-2.995125-6.067048s-4.40309-2.022349-7.19342-2.022349c-4.40309 0-8.729382 1.151971-12.978875 3.455913s-8.242994 5.171071-12.0061 8.626984-7.065423 7.142221-9.983751 11.110123-5.145471 7.526212-6.758231 10.726132c7.19342-0.588785 13.798055-1.58716 19.788306-2.918327s11.21252-3.046324 15.666808-5.094273 8.038199-4.351891 10.80293-6.911827 4.556686-5.299068 5.350266-8.191796zM818.347541 499.929902c0.511987-3.9935 1.305567-7.705407 2.40634-11.110122s2.585535-6.220644 4.428689-8.473389 4.223894-3.379116 7.116623-3.379115c1.61276 0 3.379116 0.307192 5.324666 0.895977s3.788705 1.433564 5.555062 2.483138 3.225519 2.329542 4.428689 3.814305 1.791955 3.148721 1.791955 4.940676c0 0.79358-0.153596 1.715157-0.460788 2.764731s-0.537587 2.073548-0.742382 3.071924l-37.503062 150.293842c-1.510362 5.99025-2.739132 11.724507-3.737507 17.177171s-1.740756 9.932552-2.252744 13.414064v3.60951c0 2.995125-0.204795 6.271843-0.588785 9.830154s-1.151971 6.860628-2.252744 9.906953-2.636734 5.60626-4.582285 7.654208-4.479888 3.071923-7.577411 3.071924c-2.687933 0-5.043074-0.563186-7.039824-1.715158s-3.660708-2.636734-4.940676-4.428689-2.252744-3.737507-2.841529-5.785455-0.895978-3.967901-0.895978-5.785456c0-1.61276 0.153596-3.58391 0.460789-5.913452s0.665583-4.81268 1.126372-7.347016 0.921577-5.094273 1.433564-7.654209 0.998375-4.78708 1.510362-6.681433l38.706232-153.59616a193.275168 193.275168 0 0 1 1.126372-11.058923z" fill="#FFFFFF" p-id="1446"></path></svg>
\ No newline at end of file
... ...
... ... @@ -191,6 +191,10 @@
191 191 <slot name="afterFullScreen"></slot>
192 192 </div>
193 193 </div>
194   - <div ref="jsonEditorElRef" class="flex-auto"></div>
  194 + <div
  195 + ref="jsonEditorElRef"
  196 + class="flex-auto"
  197 + :style="{ backgroundColor: disabled ? '#f0f0f0' : '' }"
  198 + ></div>
195 199 </div>
196 200 </template>
... ...
... ... @@ -47,6 +47,7 @@
47 47 isClose: { type: Boolean, default: true },
48 48 title: { type: String, default: '' },
49 49 loading: { type: Boolean },
  50 + defaultExpand: { type: Boolean, default: true },
50 51 /**
51 52 * Can it be expanded
52 53 */
... ... @@ -75,7 +76,7 @@
75 76
76 77 const emit = defineEmits(['expand', 'change', 'hchange']);
77 78
78   - const show = ref(true);
  79 + const show = ref(props.defaultExpand);
79 80
80 81 const { prefixCls } = useDesign('collapse-container');
81 82
... ...
... ... @@ -15,6 +15,8 @@ export { default as ApiUpload } from './src/components/ApiUpload.vue';
15 15 export { default as StructForm } from './src/externalCompns/components/StructForm/StructForm.vue';
16 16 export { default as JavaScriptFunctionEditor } from './src/components/JavaScriptFunctionEditor.vue';
17 17
  18 +export { ThingsModelForm } from './src/externalCompns/components/ThingsModelForm';
  19 +
18 20 //注册自定义组件
19 21 export {
20 22 JEasyCron,
... ...
... ... @@ -69,7 +69,7 @@
69 69 name: 'BasicForm',
70 70 components: { FormItem, Form, Row, FormAction },
71 71 props: basicProps,
72   - emits: ['advanced-change', 'reset', 'submit', 'register'],
  72 + emits: ['advanced-change', 'reset', 'submit', 'register', 'fieldValueChange'],
73 73 setup(props, { emit, attrs }) {
74 74 const formModel = reactive<Recordable>({});
75 75 const modalFn = useModalContext();
... ... @@ -230,6 +230,7 @@
230 230
231 231 function setFormModel(key: string, value: any) {
232 232 formModel[key] = value;
  233 + emit('fieldValueChange', key, value);
233 234 const { validateTrigger } = unref(getBindValue);
234 235 if (!validateTrigger || validateTrigger === 'change') {
235 236 validateFields([key]).catch((_) => {});
... ...
... ... @@ -4,77 +4,102 @@
4 4 <!-- 待完善封装InputGroup -->
5 5 <InputGroup compact>
6 6 <Select
7   - v-if="type !== '2'"
  7 + v-if="type !== RequestMethodTypeEnum.WEBSOCKET"
8 8 placeholder="请求类型"
9   - :style="{ width: type !== '2' ? 15 + '%' : 0 + '%' }"
10   - v-model:value="valueObj.requestHttpType"
  9 + :style="{ width: type !== RequestMethodTypeEnum.WEBSOCKET ? 15 + '%' : 0 + '%' }"
  10 + v-model:value="requestTypeUrlValue.requestHttpType"
11 11 :options="selectOptions"
12 12 allowClear
13 13 @change="emitChange"
14 14 />
15 15 <Input
16   - @change="emitChange"
17 16 placeholder="请输入接口地址"
18   - v-model:value="valueObj.requestUrl"
19   - :style="{ width: type !== '2' ? 85 + '%' : 100 + '%' }"
  17 + v-model:value="requestTypeUrlValue.requestUrl"
  18 + :style="{ width: type !== RequestMethodTypeEnum.WEBSOCKET ? 85 + '%' : 100 + '%' }"
  19 + @change="emitChange"
20 20 />
21 21 </InputGroup>
22 22 </div>
23 23 </template>
24 24 <script lang="ts" setup>
25   - import { reactive, ref, watchEffect } from 'vue';
  25 + import { reactive, ref, PropType, watch } from 'vue';
26 26 import { InputGroup, Select, Input } from 'ant-design-vue';
27   - import type { SelectValue } from 'ant-design-vue/lib/select';
28 27 import { findDictItemByCode } from '/@/api/system/dict';
29   - import { propTypes } from '/@/utils/propTypes';
  28 + import { RequestMethodTypeEnum } from '/@/views/dataview/publicApi/config/enum';
30 29
31   - type TypeInputGroup = {
32   - requestHttpType: SelectValue | undefined;
  30 + interface requestTypeUrlConfig {
  31 + requestHttpType: undefined;
33 32 requestUrl?: string;
34 33 disabled?: boolean;
35   - };
  34 + }
36 35
37   - type selectType = { label: string; value: string; disabled?: boolean };
  36 + type selectType = {
  37 + label: string;
  38 + value: string;
  39 + };
38 40
39 41 const props = defineProps({
40 42 type: {
41 43 type: String,
  44 + default: RequestMethodTypeEnum.COMMOM,
42 45 },
43   - value: propTypes.object.def({}),
  46 + value: Object as PropType<requestTypeUrlConfig>,
44 47 });
45 48
46 49 const emits = defineEmits(['change', 'update:value']);
47 50
48 51 const selectOptions = ref<selectType[]>([]);
49 52
50   - const getOptions = async (e) => {
51   - const res = await findDictItemByCode({
52   - dictCode: e === '1' ? 'dataview_select_sql_request' : 'dataview_select_request',
  53 + const getOptions = async (requestType) => {
  54 + // 暂且排除SQL和WEBSOCKET
  55 + const resItem = await findDictItemByCode({
  56 + dictCode: 'dataview_select_request',
53 57 });
54   - if (e === '1' || e === '0') {
55   - selectOptions.value = res.map((m) => ({ label: m.itemText, value: m.itemValue }));
  58 + if (requestType === RequestMethodTypeEnum.COMMOM) {
  59 + selectOptions.value = resItem.map((m) => ({ label: m.itemText, value: m.itemValue }));
56 60 } else {
57 61 selectOptions.value = [];
58 62 }
59 63 };
60 64
61   - const valueObj = reactive<TypeInputGroup>({
  65 + const requestTypeUrlValue = reactive<requestTypeUrlConfig>({
62 66 requestHttpType: undefined,
63 67 requestUrl: '',
64 68 });
65 69
66   - watchEffect(() => {
67   - initVal();
68   - });
  70 + watch(
  71 + () => props.type,
  72 + () => {
  73 + initOption();
  74 + },
  75 + {
  76 + immediate: true,
  77 + }
  78 + );
  79 +
  80 + watch(
  81 + () => props.value,
  82 + () => {
  83 + initConfig();
  84 + },
  85 + {
  86 + immediate: true,
  87 + }
  88 + );
  89 +
  90 + function initOption() {
  91 + if (props.type) {
  92 + getOptions(props.type);
  93 + }
  94 + }
69 95
70   - async function initVal() {
71   - if (props?.type) await getOptions(props?.type);
72   - if (props?.value) for (let i in props.value) Reflect.set(valueObj, i, props.value[i]);
  96 + function initConfig() {
  97 + if (props?.value)
  98 + for (let i in props.value) Reflect.set(requestTypeUrlValue, i, props.value[i]);
73 99 }
74 100
75 101 function emitChange() {
76   - emits('change', valueObj);
77   - emits('update:value', valueObj);
  102 + emits('change', requestTypeUrlValue);
  103 + emits('update:value', requestTypeUrlValue);
78 104 }
79 105 </script>
80   -<style scoped></style>
... ...
1 1 <script lang="ts" setup>
2   - import { Button, Transfer, Tag } from 'ant-design-vue';
  2 + import { Transfer, Select } from 'ant-design-vue';
3 3 import { cloneDeep, get } from 'lodash-es';
4 4 import { computed, CSSProperties, ExtractPropTypes, onMounted, ref, unref, watch } from 'vue';
5 5 import { BasicModal, useModal } from '/@/components/Modal';
... ... @@ -29,7 +29,7 @@
29 29 buttonName?: string;
30 30 modalProps?: ExtractPropTypes<InstanceType<typeof BasicModal>['$props']>;
31 31 transferProps?: ExtractPropTypes<TransferType['$props']>;
32   - buttonProps?: ExtractPropTypes<InstanceType<typeof Button>['$props']>;
  32 + selectProps?: ExtractPropTypes<InstanceType<typeof Select>['$props']>;
33 33 disabled?: any;
34 34 }>(),
35 35 {
... ... @@ -52,24 +52,15 @@
52 52 return unref(getOptions).filter((item) => unref(targetKeys).includes(item[valueField]));
53 53 });
54 54
55   - const getShowTagOptions = computed(() => {
56   - const { maxTagLength } = props;
57   - return unref(targetOptions).slice(0, maxTagLength);
  55 + const getSelectOptions = computed(() => {
  56 + return unref(targetOptions).map((item) => ({ ...item, label: item.title, value: item.key }));
58 57 });
59 58
60   - const getSurplusOptionsLength = computed(() => {
61   - const { maxTagLength } = props;
62   - const surplusValue = unref(targetKeys).length - maxTagLength;
63   - return surplusValue < 0 ? 0 : surplusValue;
64   - });
65   -
66   - const count = computed(() => {
67   - return unref(targetKeys).length;
68   - });
  59 + const getSelectValue = computed(() => unref(getSelectOptions).map((item) => item.value));
69 60
70 61 const targetKeys = ref<string[]>(props.value || []);
71 62
72   - const getOptions = computed<Recordable[]>(() => {
  63 + const getOptions = computed<Record<'key' | 'title', string>[]>(() => {
73 64 const { labelField, valueField } = props;
74 65 return unref(options).map((item) => ({
75 66 ...item,
... ... @@ -99,13 +90,14 @@
99 90 };
100 91 });
101 92
102   - const getBindButtonProps = computed(() => {
103   - const { buttonProps = {} } = props;
  93 + const getBindSelectProps = computed(() => {
  94 + const { selectProps = {} } = props;
104 95 return {
105   - ...buttonProps,
106   - type: 'link',
107   - onClick: handleOpenModal,
108   - } as ExtractPropTypes<InstanceType<typeof Button>['$props']>;
  96 + maxTagCount: 3,
  97 + ...selectProps,
  98 + open: false,
  99 + onDropdownVisibleChange: handleOpenModal,
  100 + } as ExtractPropTypes<InstanceType<typeof Select>['$props']>;
109 101 });
110 102
111 103 const handleChange = (_targetKeys: string[], direction: 'left' | 'right', moveKeys: string[]) => {
... ... @@ -165,22 +157,11 @@
165 157 <Transfer v-bind="getBindProps" />
166 158 </section>
167 159 </BasicModal>
168   - <Button v-bind="getBindButtonProps" class="!flex !justify-center !items-center min-h-8">
169   - <span v-if="!count">{{ $props.buttonName }}</span>
170   - <div v-if="!!count">
171   - <Tag
172   - class="!px-2 !py-1 !bg-gray-50 !border-gray-100"
173   - v-for="item in getShowTagOptions"
174   - :key="item.key"
175   - >
176   - <span>
177   - {{ item.title }}
178   - </span>
179   - </Tag>
180   - <Tag class="!px-2 !py-1 !bg-gray-50 !border-gray-100" v-if="getSurplusOptionsLength">
181   - <span> +{{ getSurplusOptionsLength }}... </span>
182   - </Tag>
183   - </div>
184   - </Button>
  160 + <Select
  161 + v-bind="getBindSelectProps"
  162 + :options="getSelectOptions"
  163 + :value="getSelectValue"
  164 + mode="multiple"
  165 + />
185 166 </div>
186 167 </template>
... ...
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>
... ... @@ -5,7 +5,8 @@
5 5 import { BasicModal } from '/@/components/Modal';
6 6 import { PlusCircleOutlined } from '@ant-design/icons-vue';
7 7 import { FormFieldsEnum, formSchemas } from './config';
8   - import { DataTypeEnum } from '../StructForm/config';
  8 + import { DataTypeEnum } from '/@/enums/objectModelEnum';
  9 +
9 10 const show = ref(false);
10 11
11 12 const props = withDefaults(
... ... @@ -50,7 +51,7 @@
50 51 updateSchema([
51 52 {
52 53 field: FormFieldsEnum.ZOOM_FACTOR,
53   - ifShow: props.dataType == DataTypeEnum.IS_BOOL ? false : true,
  54 + ifShow: props.dataType == DataTypeEnum.BOOL ? false : true,
54 55 },
55 56 ]);
56 57 }
... ...
1 1 <script lang="ts" setup>
2 2 import { Card } from 'ant-design-vue';
3 3 import { computed, nextTick, onMounted, onUpdated, unref, watch } from 'vue';
4   - import { DataTypeEnum } from '../StructForm/config';
5 4 import { BasicCreateFormParams } from './type';
6 5 import { DynamicProps } from '/#/utils';
7 6 import { Specs, StructJSON } from '/@/api/device/model/modelOfMatterModel';
8 7 import { BasicForm, FormProps, FormSchema, useForm } from '/@/components/Form';
  8 + import { DataTypeEnum } from '/@/enums/objectModelEnum';
9 9 import { ReadAndWriteEnum } from '/@/enums/toolEnum';
10 10
11 11 const props = withDefaults(
... ... @@ -86,7 +86,7 @@
86 86 // step: step,
87 87 // formatter: (value: string) => value,
88 88 // parser: (string: string) => {
89   - // if (dataType === DataTypeEnum.IS_NUMBER_INT) {
  89 + // if (dataType === DataTypeEnum.NUMBER_INT) {
90 90 // return Number(Number(string).toFixed());
91 91 // }
92 92 // return Number(string);
... ... @@ -188,19 +188,19 @@
188 188 dataType: dataType! as unknown as DataTypeEnum,
189 189 specs: specs as Partial<Specs>,
190 190 };
191   - if (type === DataTypeEnum.IS_NUMBER_INT || type === DataTypeEnum.IS_NUMBER_DOUBLE) {
  191 + if (type === DataTypeEnum.NUMBER_INT || type === DataTypeEnum.NUMBER_DOUBLE) {
192 192 schemas.push(createInputNumber(params));
193 193 }
194 194
195   - if (type === DataTypeEnum.IS_BOOL) {
  195 + if (type === DataTypeEnum.BOOL) {
196 196 schemas.push(createSelect(params));
197 197 }
198 198
199   - if (type === DataTypeEnum.IS_STRING) {
  199 + if (type === DataTypeEnum.STRING) {
200 200 schemas.push(createInput(params));
201 201 }
202 202
203   - if (type === DataTypeEnum.IS_STRUCT) {
  203 + if (type === DataTypeEnum.STRUCT) {
204 204 schemas.push(createInputJson(params));
205 205 }
206 206 }
... ...
1   -import { DataTypeEnum } from '../StructForm/config';
2 1 import { Specs } from '/@/api/device/model/modelOfMatterModel';
  2 +import { DataTypeEnum } from '/@/enums/objectModelEnum';
3 3
4 4 export interface BasicCreateFormParams {
5 5 identifier: string;
... ...
1 1 import { ModelOfMatterParams } from '/@/api/device/model/modelOfMatterModel';
2 2 import { findDictItemByCode } from '/@/api/system/dict';
3 3 import { FormSchema } from '/@/components/Table';
  4 +import { DataTypeEnum } from '/@/enums/objectModelEnum';
4 5 import { isNullOrUnDef } from '/@/utils/is';
5 6 import { FormField } from '/@/views/device/profiles/step/cpns/physical/cpns/config';
6 7
7   -export enum DataTypeEnum {
8   - IS_NUMBER_INT = 'INT',
9   - IS_NUMBER_DOUBLE = 'DOUBLE',
10   - IS_STRING = 'TEXT',
11   - IS_STRUCT = 'STRUCT',
12   - IS_BOOL = 'BOOL',
13   -}
14   -
15 8 export const validateValueRange = (_rule, value: Record<'min' | 'max', number>, _callback) => {
16 9 value = value || {};
17 10 const { min, max } = value;
... ... @@ -77,10 +70,9 @@ export const formSchemas = (
77 70 try {
78 71 const record = await findDictItemByCode(params);
79 72 return hasStructForm
80   - ? record.filter((item) => item.itemValue !== DataTypeEnum.IS_STRUCT)
  73 + ? record.filter((item) => item.itemValue !== DataTypeEnum.STRUCT)
81 74 : record;
82 75 } catch (error) {
83   - console.log(error);
84 76 return [];
85 77 }
86 78 },
... ... @@ -91,7 +83,7 @@ export const formSchemas = (
91 83 valueField: 'itemValue',
92 84 getPopupContainer: () => document.body,
93 85 onChange: (value: string) => {
94   - if (value == DataTypeEnum.IS_STRUCT) {
  86 + if (value == DataTypeEnum.STRUCT) {
95 87 updateSchema({
96 88 field: FormField.SPECS_LIST,
97 89 componentProps: {
... ... @@ -114,8 +106,8 @@ export const formSchemas = (
114 106 span: 18,
115 107 },
116 108 ifShow: ({ values }) =>
117   - values[FormField.TYPE] === DataTypeEnum.IS_NUMBER_INT ||
118   - values[FormField.TYPE] === DataTypeEnum.IS_NUMBER_DOUBLE,
  109 + values[FormField.TYPE] === DataTypeEnum.NUMBER_INT ||
  110 + values[FormField.TYPE] === DataTypeEnum.NUMBER_DOUBLE,
119 111 rules: [{ validator: validateValueRange }],
120 112 },
121 113 {
... ... @@ -134,8 +126,8 @@ export const formSchemas = (
134 126 },
135 127 },
136 128 ifShow: ({ values }) =>
137   - values[FormField.TYPE] === DataTypeEnum.IS_NUMBER_INT ||
138   - values[FormField.TYPE] === DataTypeEnum.IS_NUMBER_DOUBLE,
  129 + values[FormField.TYPE] === DataTypeEnum.NUMBER_INT ||
  130 + values[FormField.TYPE] === DataTypeEnum.NUMBER_DOUBLE,
139 131 dynamicRules: ({ model }) => {
140 132 const valueRange = model[FormField.VALUE_RANGE] || {};
141 133 const { min, max } = valueRange;
... ... @@ -199,8 +191,8 @@ export const formSchemas = (
199 191 };
200 192 },
201 193 ifShow: ({ values }) =>
202   - values[FormField.TYPE] === DataTypeEnum.IS_NUMBER_INT ||
203   - values[FormField.TYPE] === DataTypeEnum.IS_NUMBER_DOUBLE,
  194 + values[FormField.TYPE] === DataTypeEnum.NUMBER_INT ||
  195 + values[FormField.TYPE] === DataTypeEnum.NUMBER_DOUBLE,
204 196 },
205 197 {
206 198 field: FormField.BOOL_CLOSE,
... ... @@ -214,7 +206,7 @@ export const formSchemas = (
214 206 placeholder: '如:关',
215 207 },
216 208 defaultValue: '关',
217   - ifShow: ({ values }) => values[FormField.TYPE] === DataTypeEnum.IS_BOOL,
  209 + ifShow: ({ values }) => values[FormField.TYPE] === DataTypeEnum.BOOL,
218 210 dynamicRules: ({ model }) => {
219 211 const close = model[FormField.BOOL_CLOSE];
220 212 const open = model[FormField.BOOL_OPEN];
... ... @@ -243,7 +235,7 @@ export const formSchemas = (
243 235 placeholder: '如:开',
244 236 },
245 237 defaultValue: '开',
246   - ifShow: ({ values }) => values[FormField.TYPE] === DataTypeEnum.IS_BOOL,
  238 + ifShow: ({ values }) => values[FormField.TYPE] === DataTypeEnum.BOOL,
247 239 dynamicRules: ({ model }) => {
248 240 const close = model[FormField.BOOL_CLOSE];
249 241 const open = model[FormField.BOOL_OPEN];
... ... @@ -277,7 +269,7 @@ export const formSchemas = (
277 269 suffix: () => '字节',
278 270 };
279 271 },
280   - ifShow: ({ values }) => values[FormField.TYPE] === DataTypeEnum.IS_STRING,
  272 + ifShow: ({ values }) => values[FormField.TYPE] === DataTypeEnum.STRING,
281 273 },
282 274 {
283 275 field: FormField.EXTENSION_DESC,
... ... @@ -322,7 +314,7 @@ export const formSchemas = (
322 314 valueField: 'value',
323 315 changeEvent: 'update:value',
324 316 colProps: { span: 24 },
325   - ifShow: ({ values }) => values[FormField.TYPE] === DataTypeEnum.IS_STRUCT,
  317 + ifShow: ({ values }) => values[FormField.TYPE] === DataTypeEnum.STRUCT,
326 318 rules: [{ required: true, validator: validateJSON }],
327 319 },
328 320 {
... ...
1   -import { DataTypeEnum } from './config';
2 1 import { StructJSON } from '/@/api/device/model/modelOfMatterModel';
  2 +import { DataTypeEnum } from '/@/enums/objectModelEnum';
3 3 import { FormField } from '/@/views/device/profiles/step/cpns/physical/cpns/config';
4 4
5 5 export enum OpenModalMode {
... ...
1 1 import { cloneDeep } from 'lodash-es';
2   -import { DataTypeEnum } from './config';
3 2 import { StructFormValue } from './type';
4 3 import { DataType, ModelOfMatterParams, StructJSON } from '/@/api/device/model/modelOfMatterModel';
5 4 import { isArray } from '/@/utils/is';
  5 +import { DataTypeEnum } from '/@/enums/objectModelEnum';
6 6
7 7 export function transfromToStructJSON(value: StructFormValue): StructJSON {
8 8 const {
... ... @@ -24,21 +24,21 @@ export function transfromToStructJSON(value: StructFormValue): StructJSON {
24 24 let dataType = {} as unknown as DataType;
25 25
26 26 switch (type) {
27   - case DataTypeEnum.IS_NUMBER_INT:
  27 + case DataTypeEnum.NUMBER_INT:
28 28 dataType = {
29 29 type,
30 30 specs: { valueRange, step, unit, unitName },
31 31 };
32 32 break;
33 33
34   - case DataTypeEnum.IS_NUMBER_DOUBLE:
  34 + case DataTypeEnum.NUMBER_DOUBLE:
35 35 dataType = {
36 36 type,
37 37 specs: { valueRange, step, unit, unitName },
38 38 };
39 39 break;
40 40
41   - case DataTypeEnum.IS_BOOL:
  41 + case DataTypeEnum.BOOL:
42 42 dataType = {
43 43 type,
44 44 specs: {
... ... @@ -48,14 +48,14 @@ export function transfromToStructJSON(value: StructFormValue): StructJSON {
48 48 };
49 49 break;
50 50
51   - case DataTypeEnum.IS_STRING:
  51 + case DataTypeEnum.STRING:
52 52 dataType = {
53 53 type,
54 54 specs: { length },
55 55 };
56 56 break;
57 57
58   - case DataTypeEnum.IS_STRUCT:
  58 + case DataTypeEnum.STRUCT:
59 59 dataType = {
60 60 type,
61 61 specs: specs! as unknown as ModelOfMatterParams[],
... ...
  1 +<script setup lang="ts">
  2 + import { StructJSON } from '/@/api/device/model/modelOfMatterModel';
  3 + import { JsonPreview } from '/@/components/CodeEditor';
  4 + import { BasicModal, useModalInner } from '/@/components/Modal';
  5 +
  6 + defineProps<{
  7 + inputData?: StructJSON[];
  8 + }>();
  9 +
  10 + defineEmits(['register']);
  11 +
  12 + const data = {};
  13 +
  14 + const [register] = useModalInner();
  15 +</script>
  16 +
  17 +<template>
  18 + <BasicModal @register="register" title="">
  19 + <!-- -->
  20 + <JsonPreview :data="data" />
  21 + </BasicModal>
  22 +</template>
... ...
  1 +import { FormSchema } from '../../../types/form';
  2 +import { Specs, StructJSON } from '/@/api/device/model/modelOfMatterModel';
  3 +import { DataTypeEnum } from '/@/enums/objectModelEnum';
  4 +import { TransportTypeEnum } from '/@/views/device/profiles/components/TransportDescript/const';
  5 +
  6 +export const getFormSchemas = ({
  7 + structJSON: structJson,
  8 + required,
  9 + transportType,
  10 +}: {
  11 + structJSON: StructJSON[];
  12 + required?: boolean;
  13 + transportType?: string;
  14 +}): FormSchema[] => {
  15 + const createInputNumber = ({ identifier, functionName, dataType }: StructJSON): FormSchema => {
  16 + const { specs } = dataType || {};
  17 + const { valueRange } = specs! as Specs;
  18 + const { max = 2147483647, min = -2147483648 } = valueRange || {};
  19 + return {
  20 + field: identifier,
  21 + label: functionName,
  22 + component: 'InputNumber',
  23 + rules: [
  24 + {
  25 + required,
  26 + message: `${functionName}是必填项`,
  27 + },
  28 + {
  29 + type: 'number',
  30 + trigger: 'change',
  31 + validator: (_rule, value) => {
  32 + const reg = /^[0-9]*$/;
  33 + if (!reg.test(value))
  34 + return Promise.reject(new Error(`${functionName}不是一个有效的数字`));
  35 + if (value < min || value > max)
  36 + return Promise.reject(new Error(`${functionName}取值范围在${min}~${max}之间`));
  37 +
  38 + return Promise.resolve(value);
  39 + },
  40 + },
  41 + ],
  42 + componentProps: {
  43 + max,
  44 + min,
  45 + placeholder: `请输入${functionName}`,
  46 + },
  47 + } as FormSchema;
  48 + };
  49 +
  50 + const createInput = ({ identifier, functionName, dataType }: StructJSON): FormSchema => {
  51 + const { specs } = dataType || {};
  52 + const { length = 10240 } = specs! as Partial<Specs>;
  53 + return {
  54 + field: identifier,
  55 + label: functionName,
  56 + component: 'Input',
  57 + rules: [
  58 + {
  59 + required,
  60 + message: `${functionName}是必填项`,
  61 + },
  62 + {
  63 + type: 'string',
  64 + trigger: 'change',
  65 + validator: (_rule, value) => {
  66 + if ((value?.length || 0) > length)
  67 + return Promise.reject(new Error(`${functionName}数据长度应该小于${length}`));
  68 +
  69 + return Promise.resolve(value);
  70 + },
  71 + },
  72 + ],
  73 + componentProps: {
  74 + maxLength: length,
  75 + placeholder: `请输入${functionName}`,
  76 + },
  77 + } as FormSchema;
  78 + };
  79 +
  80 + const createSelect = ({ identifier, functionName, dataType }: StructJSON): FormSchema => {
  81 + const { specs } = dataType || {};
  82 + const { boolClose, boolOpen } = specs! as Partial<Specs>;
  83 + return {
  84 + field: identifier,
  85 + label: functionName!,
  86 + component: 'Select',
  87 + rules: [
  88 + {
  89 + required,
  90 + message: `${functionName}是必填项`,
  91 + type: 'number',
  92 + },
  93 + ],
  94 + componentProps: {
  95 + options: [
  96 + { label: `${boolClose}-0`, value: 0 },
  97 + { label: `${boolOpen}-1`, value: 1 },
  98 + ],
  99 + placeholder: `请选择${functionName}`,
  100 + },
  101 + };
  102 + };
  103 +
  104 + const createStructJson = ({ identifier, functionName, dataType }: StructJSON): FormSchema => {
  105 + return {
  106 + field: identifier,
  107 + label: functionName!,
  108 + component: 'Input',
  109 + changeEvent: 'update:value',
  110 + componentProps: () => {
  111 + return {
  112 + inputData: dataType?.specs || [],
  113 + };
  114 + },
  115 + colSlot: identifier,
  116 + };
  117 + };
  118 +
  119 + const createTCPServiceCommandInput = ({ serviceCommand }: StructJSON): FormSchema => {
  120 + return {
  121 + field: 'serviceCommand',
  122 + label: '服务命令',
  123 + component: 'Input',
  124 + required,
  125 + defaultValue: serviceCommand,
  126 + componentProps: {
  127 + placeholder: `请输入服务命令`,
  128 + },
  129 + };
  130 + };
  131 +
  132 + const schemas: FormSchema[] = [];
  133 +
  134 + for (const item of structJson) {
  135 + const { dataType } = item;
  136 + const { type } = dataType || {};
  137 + if (transportType === TransportTypeEnum.TCP) {
  138 + item.serviceCommand && schemas.push(createTCPServiceCommandInput(item));
  139 + break;
  140 + }
  141 +
  142 + if (type === DataTypeEnum.BOOL) schemas.push(createSelect(item));
  143 + else if (type === DataTypeEnum.NUMBER_INT) schemas.push(createInputNumber(item));
  144 + else if (type === DataTypeEnum.NUMBER_DOUBLE) schemas.push(createInputNumber(item));
  145 + else if (type === DataTypeEnum.STRING) schemas.push(createInput(item));
  146 + else if (type === DataTypeEnum.STRUCT) schemas.push(createStructJson(item));
  147 + }
  148 +
  149 + return schemas;
  150 +};
... ...
  1 +import { ValidatorRule } from 'ant-design-vue/lib/form/interface';
  2 +export { default as ThingsModelForm } from './index.vue';
  3 +
  4 +export const validateTCPCustomCommand: ValidatorRule['validator'] = (_rule, value) => {
  5 + const reg = /^[\s0-9a-fA-F]+$/;
  6 + if (reg.test(value)) return Promise.resolve();
  7 + return Promise.reject('请输入ASCII或HEX服务命令(0~9/A~F)');
  8 +};
  9 +
  10 +export const trimBlankSpace = (string: string) => string.replace(/(?!^)(?=(\w{2})+$)/g, '');
  11 +
  12 +export const formatCommandString = (string = '') => string.replace(/(?!^)(?=(\w{2})+$)/g, ' ');
... ...
  1 +<script setup lang="ts">
  2 + import { Card } from 'ant-design-vue';
  3 + import { ComponentPublicInstance, computed, nextTick, reactive, unref, watch } from 'vue';
  4 + import { getFormSchemas } from './config';
  5 + import { ThingsModelForm } from '.';
  6 + import { DefineComponentsBasicExpose } from '/#/utils';
  7 + import { StructJSON } from '/@/api/device/model/modelOfMatterModel';
  8 + import { useForm } from '../../../hooks/useForm';
  9 + import { DataTypeEnum } from '/@/enums/objectModelEnum';
  10 + import { BasicForm } from '/@/components/Form';
  11 +
  12 + const props = withDefaults(
  13 + defineProps<{
  14 + value?: Recordable;
  15 + inputData?: StructJSON[];
  16 + required?: boolean;
  17 + title?: string;
  18 + transportType?: string;
  19 + disabled?: boolean;
  20 + identifier?: string;
  21 + }>(),
  22 + {
  23 + inputData: () => [],
  24 + required: true,
  25 + }
  26 + );
  27 +
  28 + const thingsModelFormListElMap = reactive<
  29 + Record<string, { el: InstanceType<typeof ThingsModelForm>; structJSON: StructJSON }>
  30 + >({});
  31 +
  32 + const [register, formActionType] = useForm({
  33 + schemas: getFormSchemas({
  34 + structJSON: props.inputData || [],
  35 + required: props.required,
  36 + transportType: props.transportType,
  37 + }),
  38 + showActionButtonGroup: false,
  39 + layout: 'inline',
  40 + labelWidth: 100,
  41 + });
  42 +
  43 + const getStructFormItem = computed(() => {
  44 + const { inputData } = props;
  45 + return inputData.filter((item) => item.dataType?.type === DataTypeEnum.STRUCT);
  46 + });
  47 +
  48 + const setFormElRef = (
  49 + el: Nullable<Element | ComponentPublicInstance>,
  50 + structJSON: StructJSON
  51 + ) => {
  52 + const _structJSON = unref(getStructFormItem).find(
  53 + (item) => item.identifier === structJSON.identifier
  54 + );
  55 +
  56 + if (_structJSON)
  57 + thingsModelFormListElMap[structJSON.identifier!] = {
  58 + el: el as InstanceType<typeof ThingsModelForm>,
  59 + structJSON,
  60 + };
  61 + };
  62 +
  63 + const getFieldsValue = () => {
  64 + const basicValue = formActionType.getFieldsValue();
  65 +
  66 + const structValue: Recordable = {};
  67 + for (const key of Object.keys(thingsModelFormListElMap)) {
  68 + const item = thingsModelFormListElMap[key];
  69 + const { el } = item;
  70 + structValue[key] = el.getFieldsValue() || {};
  71 + }
  72 +
  73 + return {
  74 + ...basicValue,
  75 + ...structValue,
  76 + };
  77 + };
  78 +
  79 + const setFieldsValue = (value: Recordable) => {
  80 + formActionType.setFieldsValue(value);
  81 + };
  82 +
  83 + const validate = async () => {
  84 + await formActionType.validate();
  85 + for (const key of Object.keys(thingsModelFormListElMap))
  86 + await thingsModelFormListElMap[key]?.el?.validate?.();
  87 + };
  88 +
  89 + watch(
  90 + () => props.value,
  91 + async (value) => {
  92 + await nextTick();
  93 + formActionType.resetFields();
  94 + formActionType.setFieldsValue(value || {});
  95 + },
  96 + { immediate: true }
  97 + );
  98 +
  99 + watch(
  100 + () => [props.inputData, props.identifier],
  101 + (value) => {
  102 + if (value && value.length) {
  103 + const schemas = getFormSchemas({
  104 + structJSON: props.inputData || [],
  105 + required: props.required,
  106 + transportType: props.transportType,
  107 + });
  108 + formActionType.setProps({ schemas });
  109 + }
  110 + }
  111 + );
  112 +
  113 + watch(
  114 + () => props.disabled,
  115 + (value) => {
  116 + formActionType.setProps({ disabled: value });
  117 + }
  118 + );
  119 +
  120 + defineExpose<DefineComponentsBasicExpose>({
  121 + getFieldsValue,
  122 + setFieldsValue,
  123 + validate,
  124 + });
  125 +</script>
  126 +
  127 +<template>
  128 + <Card
  129 + class="!border-2 !border-dashed"
  130 + :title="title"
  131 + :style="{ backgroundColor: disabled ? '#f0f0f0' : '', borderColor: disabled ? '#e3e3e3' : '' }"
  132 + >
  133 + <BasicForm class="things-model-form" @register="register" :disabled="disabled">
  134 + <template
  135 + v-for="item in getStructFormItem"
  136 + #[item.identifier!]="{ model, field }"
  137 + :key="item.identifier"
  138 + >
  139 + <ThingsModelForm
  140 + class="!ml-10"
  141 + :ref="(el) => setFormElRef(el, item)"
  142 + v-model:value="model[field]"
  143 + :input-data="(item.dataType?.specs as StructJSON[]) || []"
  144 + :title="item.functionName"
  145 + :disabled="disabled"
  146 + />
  147 + </template>
  148 + </BasicForm>
  149 + </Card>
  150 +</template>
  151 +
  152 +<style lang="less" scoped>
  153 + .things-model-form {
  154 + > :deep(.ant-row) {
  155 + width: 100%;
  156 + }
  157 +
  158 + :deep(.ant-input-number) {
  159 + width: 100%;
  160 + }
  161 +
  162 + :deep(.ant-form-item-label) {
  163 + label {
  164 + width: 100%;
  165 + display: block;
  166 + overflow: hidden;
  167 + text-overflow: ellipsis;
  168 + }
  169 + }
  170 + }
  171 +
  172 + :deep(.ant-form-item-control) {
  173 + margin-left: 6px;
  174 + }
  175 +</style>
... ...
... ... @@ -123,6 +123,7 @@ export type ComponentType =
123 123 | 'TransferModal'
124 124 | 'TransferTableModal'
125 125 | 'ObjectModelValidateForm'
  126 + | 'ThingsModelForm'
126 127 | 'DevicePicker'
127 128 | 'ProductPicker'
128 129 | 'PollCommandInput'
... ... @@ -138,4 +139,8 @@ export type ComponentType =
138 139 | 'RelationsQuery'
139 140 | 'CredentialsCard'
140 141 | 'ApiComplete'
141   - | 'DeviceProfileForm';
  142 + | 'DeviceProfileForm'
  143 + | 'ConditionFilter'
  144 + | 'TimeRangePicker'
  145 + | 'TriggerDurationInput'
  146 + | 'AlarmProfileSelect';
... ...
... ... @@ -398,8 +398,6 @@
398 398 display: flex;
399 399 align-items: center;
400 400 justify-content: center;
401   - height: 670px;
402   - max-height: 670px;
403 401 }
404 402 }
405 403
... ...
... ... @@ -25,19 +25,18 @@ export function usePagination(refProps: ComputedRef<BasicTableProps>) {
25 25 const { t } = useI18n();
26 26
27 27 const configRef = ref<PaginationProps>({
28   - hideOnSinglePage:true
  28 + hideOnSinglePage: true,
29 29 });
30 30 const show = ref(true);
31 31
32 32 watchEffect(() => {
33 33 const { pagination } = unref(refProps);
34   -
  34 +
35 35 if (!isBoolean(pagination) && pagination) {
36 36 configRef.value = {
37 37 ...unref(configRef),
38 38 ...(pagination ?? {}),
39   - };
40   -
  39 + };
41 40 }
42 41 });
43 42
... ...
... ... @@ -88,7 +88,7 @@ export function useTableScroll(
88 88
89 89 bodyEl!.style.height = 'unset';
90 90
91   - if (!unref(getCanResize) || tableData.length === 0) return;
  91 + if (!unref(getCanResize)) return;
92 92
93 93 await nextTick();
94 94 //Add a delay to get the correct bottomIncludeBody paginationHeight footerHeight headerHeight
... ... @@ -143,7 +143,12 @@ export function useTableScroll(
143 143 height = (height > maxHeight! ? (maxHeight as number) : height) ?? height;
144 144 setHeight(height);
145 145
146   - bodyEl!.style.height = `${height}px`;
  146 + if (tableData.length) {
  147 + bodyEl!.style.height = `${height}px`;
  148 + } else {
  149 + const emptyPlaceholder: HTMLDivElement = tableEl.querySelector('.ant-table-placeholder')!;
  150 + emptyPlaceholder && (emptyPlaceholder.style.height = `${height}px`);
  151 + }
147 152 }
148 153 useWindowSizeFn(calcTableHeight, 280);
149 154 onMountedOrActivated(() => {
... ...
... ... @@ -11,3 +11,19 @@ export enum AlarmStatusMean {
11 11 CLEARED_ACK = '清除已确认',
12 12 ACTIVE_ACK = '激活已确认',
13 13 }
  14 +
  15 +export enum AlarmLevelEnum {
  16 + CRITICAL = 'CRITICAL',
  17 + MAJOR = 'MAJOR',
  18 + MINOR = 'MINOR',
  19 + WARNING = 'WARNING',
  20 + INDETERMINATE = 'INDETERMINATE',
  21 +}
  22 +
  23 +export enum AlarmLevelNameEnum {
  24 + CRITICAL = '紧急',
  25 + MAJOR = '重要',
  26 + MINOR = '次要',
  27 + WARNING = '警告',
  28 + INDETERMINATE = '不确定',
  29 +}
... ...
... ... @@ -23,4 +23,7 @@ export enum DictEnum {
23 23 MESSAGE_TYPES_FILTER = 'message_types_filter',
24 24 // 实体类型 规则节点 Filter originator types switch
25 25 ORIGINATOR_TYPES = 'originator_types',
  26 +
  27 + // 产品品类领域
  28 + CATEGORY_FIELD = 'category_field',
26 29 }
... ...
  1 +/**
  2 + * 运算符枚举值
  3 + */
  4 +
  5 +// 数字运算符或者时间运算符
  6 +export enum NumberOperationEnum {
  7 + EQUAL = 'EQUAL',
  8 + NOT_EQUAL = 'NOT_EQUAL',
  9 + LESS = 'LESS',
  10 + LESS_OR_EQUAL = 'LESS_OR_EQUAL',
  11 + GREATER = 'GREATER',
  12 + GREATER_OR_EQUAL = 'GREATER_OR_EQUAL',
  13 +}
  14 +
  15 +export enum NumberOperationNameEnum {
  16 + EQUAL = '等于',
  17 + NOT_EQUAL = '不等于',
  18 + LESS = '小于',
  19 + LESS_OR_EQUAL = '小于等于',
  20 + GREATER = '大于',
  21 + GREATER_OR_EQUAL = '大于等于',
  22 +}
  23 +
  24 +export enum StringOperationEnum {
  25 + EQUAL = 'EQUAL',
  26 + NOT_EQUAL = 'NOT_EQUAL',
  27 + STARTS_WITH = 'STARTS_WITH',
  28 + ENDS_WITH = 'ENDS_WITH',
  29 + CONTAINS = 'CONTAINS',
  30 + NOT_CONTAINS = 'NOT_CONTAINS',
  31 +}
  32 +
  33 +export enum StringOperationNameEnum {
  34 + EQUAL = '等于',
  35 + NOT_EQUAL = '不等于',
  36 + STARTS_WITH = '开始于',
  37 + ENDS_WITH = '结束于',
  38 + CONTAINS = '包含',
  39 + NOT_CONTAINS = '不包含',
  40 +}
  41 +
  42 +export enum BooleanOperationEnum {
  43 + EQUAL = 'EQUAL',
  44 + NOT_EQUAL = 'NOT_EQUAL',
  45 +}
  46 +
  47 +export enum BooleanOperationNameEnum {
  48 + EQUAL = '等于',
  49 + NOT_EQUAL = '不等于',
  50 +}
  51 +
  52 +export enum BooleanOperationValueEnum {
  53 + TRUE = 'true',
  54 + FALSE = 'false',
  55 +}
  56 +
  57 +export enum BooleanOperationValueNameEnum {
  58 + TRUE = '真',
  59 + FALSE = '假',
  60 +}
  61 +
  62 +export enum FlipFlopTypeEnum {
  63 + SIMPLE = 'SIMPLE',
  64 + DURATION = 'DURATION',
  65 + REPEATING = 'REPEATING',
  66 +}
  67 +
  68 +export enum ScheduleTypeEnum {
  69 + ANY_TIME = 'ANY_TIME',
  70 + SPECIFIC_TIME = 'SPECIFIC_TIME',
  71 + CUSTOM = 'CUSTOM',
  72 +}
  73 +
  74 +export enum ScheduleTypeNameEnum {
  75 + ANY_TIME = '始终启用',
  76 + SPECIFIC_TIME = '定时启用',
  77 + CUSTOM = '自定义启用',
  78 +}
  79 +
  80 +export enum FlipFlopTypeNameEnum {
  81 + SIMPLE = '简单',
  82 + DURATION = '持续时长',
  83 + REPEATING = '重复次数',
  84 +}
  85 +
  86 +export enum TriggerTypeEnum {
  87 + DEVICE_TRIGGER = 'DEVICE_TRIGGER',
  88 + SCHEDULE_TRIGGER = 'SCHEDULE_TRIGGER',
  89 + SCENE_TRIGGER = 'SCENE_TRIGGER',
  90 + HAND_ACT = 'HAND_ACT',
  91 +}
  92 +
  93 +export enum TriggerTypeNameEnum {
  94 + DEVICE_TRIGGER = '设备触发',
  95 + SCHEDULE_TRIGGER = '定时触发',
  96 + SCENE_TRIGGER = '场景触发',
  97 + HAND_ACT = '手动触发',
  98 +}
  99 +
  100 +export enum DeviceTriggerTypeEum {
  101 + TIME_SERIES = 'TIME_SERIES',
  102 +}
  103 +
  104 +export enum DeviceTriggerTypeNameEum {
  105 + TIME_SERIES = '属性触发',
  106 +}
  107 +
  108 +export enum TriggerEntityTypeEnum {
  109 + ALL = 'ALL',
  110 + PART = 'PART',
  111 +}
  112 +
  113 +export enum TriggerEntityTypeNameEnum {
  114 + ALL = '全部',
  115 + PART = '部分',
  116 +}
  117 +
  118 +export enum TriggerValueTypeEnum {
  119 + NUMERIC = 'NUMERIC',
  120 + BOOLEAN = 'BOOLEAN',
  121 + STRING = 'STRING',
  122 + DATE_TIME = 'DATE_TIME',
  123 +}
  124 +
  125 +export enum TriggerValueTypeNameEnum {
  126 + NUMERIC = '数字',
  127 + BOOLEAN = '布尔值',
  128 + STRING = '字符串',
  129 + DATE_TIME = '时间',
  130 +}
  131 +
  132 +export enum TriggerUnitEnum {
  133 + SECONDS = 'SECONDS',
  134 + MINUTES = 'MINUTES',
  135 + HOURS = 'HOURS',
  136 + DAYS = 'DAYS',
  137 +}
  138 +
  139 +export enum TriggerUnitNameEnum {
  140 + SECONDS = '秒',
  141 + MINUTES = '分',
  142 + HOURS = '时',
  143 + DAYS = '天',
  144 +}
  145 +
  146 +export enum ExecutionActionEnum {
  147 + DEVICE_OUT = 'DEVICE_OUT',
  148 + MSG_NOTIFY = 'MSG_NOTIFY',
  149 +}
  150 +
  151 +export enum ExecutionActionNameEnum {
  152 + DEVICE_OUT = '设备输出',
  153 + MSG_NOTIFY = '告警输出',
  154 +}
  155 +
  156 +export enum CommandTypeEnum {
  157 + CUSTOM = 0,
  158 + SERVICE = 1,
  159 + ATTRIBUTE = 2,
  160 + API = 'api',
  161 +}
  162 +
  163 +export enum CommandTypeNameEnum {
  164 + CUSTOM = '自定义',
  165 + SERVICE = '服务',
  166 + ATTRIBUTE = '属性',
  167 +}
... ...
  1 +export enum DataTypeEnum {
  2 + NUMBER_INT = 'INT',
  3 + NUMBER_DOUBLE = 'DOUBLE',
  4 + STRING = 'TEXT',
  5 + STRUCT = 'STRUCT',
  6 + BOOL = 'BOOL',
  7 +}
... ...
1   -/**
2   - * 运算符枚举值
3   - */
4   -
5   -// 数字运算符或者时间运算符
6   -export enum Number_Operation {
7   - EQUAL = 'EQUAL',
8   - NOT_EQUAL = 'NOT_EQUAL',
9   - LESS = 'LESS',
10   - LESS_OR_EQUAL = 'LESS_OR_EQUAL',
11   - GREATER = 'GREATER',
12   - GREATER_OR_EQUAL = 'GREATER_OR_EQUAL',
13   -}
14   -
15   -export enum String_Operation {
16   - EQUAL = 'EQUAL',
17   - NOT_EQUAL = 'NOT_EQUAL',
18   - BEGAN_IN = 'STARTS_WITH',
19   - END_IN = 'ENDS_WITH',
20   - INCLUDE = 'CONTAINS',
21   - NOT_INCLUDE = 'NOT_CONTAINS',
22   -}
23   -
24   -export enum Boolean_Operation {
25   - EQUAL = 'EQUAL',
26   - NOT_EQUAL = 'NOT_EQUAL',
27   -}
... ... @@ -7,7 +7,7 @@ export enum DataActionModeEnum {
7 7 }
8 8
9 9 export enum DataActionModeNameEnum {
10   - CREATE = '创建',
  10 + CREATE = '新增',
11 11 READ = '查看',
12 12 UPDATE = '编辑',
13 13 DELETE = '删除',
... ... @@ -40,6 +40,11 @@ export enum ServiceCallTypeEnum {
40 40 SYNC = 'SYNC',
41 41 }
42 42
  43 +export enum ServiceCallTypeNameEnum {
  44 + ASYNC = '异步',
  45 + SYNC = '同步',
  46 +}
  47 +
43 48 export enum CommandDeliveryWayEnum {
44 49 ONE_WAY = 'oneway',
45 50 TWO_WAY = 'twoway',
... ...
... ... @@ -5,4 +5,5 @@
5 5 export const enum UserDropDownItemEnum {
6 6 FORGOT_PASSWORD = 'system:password:view', //忘记密码权限标识key
7 7 ABOUT_SOFTWARE = 'system:about_software:view', //关于软件权限标识key
  8 + PERSONAL_CENTER = 'system:personal_center:view', //个人中心权限标识key
8 9 }
... ...
... ... @@ -12,6 +12,7 @@
12 12 <template #overlay>
13 13 <Menu @click="handleMenuClick">
14 14 <MenuItem
  15 + v-if="hasPermission(UserDropDownItemEnum.PERSONAL_CENTER)"
15 16 key="personal"
16 17 :text="t('layout.header.dropdownItemPersonal')"
17 18 icon="ion:document-text-outline"
... ...
1 1 export const createPickerSearch = (searchValue = false) => {
2 2 return {
3 3 showSearch: true,
  4 + getPopupContainer: (triggerNode) => triggerNode.parentNode,
4 5 filterOption: (inputValue: string, option: Record<'label' | 'value', string>) => {
5 6 let { label, value } = option;
6 7 label = label.toLowerCase();
... ...
... ... @@ -120,6 +120,22 @@ export const emailRule: Rule[] = [
120 120 },
121 121 ];
122 122
  123 +// 钉钉验证
  124 +export const dingRule: Rule[] = [
  125 + {
  126 + validator: (_, value: string) => {
  127 + const reg = /^[1][3,4,5,6,7,8,9][0-9]{9}$/;
  128 + if (!value) {
  129 + return Promise.resolve();
  130 + } else if (!reg.test(value)) {
  131 + return Promise.reject('手机号格式不正确');
  132 + }
  133 + return Promise.resolve();
  134 + },
  135 + validateTrigger: 'blur',
  136 + },
  137 +];
  138 +
123 139 // 中文正则
124 140 export const ChineseRegexp = /[\u4E00-\u9FA5]/;
125 141
... ...
1 1 import { BasicColumn, FormSchema } from '/@/components/Table';
2   -import { emailRule, phoneRule } from '/@/utils/rules';
  2 +import { dingRule, emailRule, phoneRule } from '/@/utils/rules';
3 3 import { useComponentRegister } from '/@/components/Form';
4 4 import { OrgTreeSelect } from '../../common/OrgTreeSelect';
5 5 useComponentRegister('OrgTreeSelect', OrgTreeSelect);
... ... @@ -31,6 +31,11 @@ export const columns: BasicColumn[] = [
31 31 width: 180,
32 32 },
33 33 {
  34 + title: '钉钉',
  35 + dataIndex: 'dingtalk',
  36 + width: 160,
  37 + },
  38 + {
34 39 title: '备注',
35 40 dataIndex: 'remark',
36 41 width: 120,
... ... @@ -109,6 +114,17 @@ export const formSchema: FormSchema[] = [
109 114 },
110 115 },
111 116 {
  117 + field: 'dingtalk',
  118 + label: '钉钉',
  119 + component: 'Input',
  120 + helpMessage: '请输入与钉钉相关联的手机号',
  121 + rules: dingRule,
  122 + componentProps: {
  123 + placeholder: '不填默认为手机号码',
  124 + maxLength: 11,
  125 + },
  126 + },
  127 + {
112 128 field: 'remark',
113 129 label: '备注',
114 130 component: 'InputTextArea',
... ...
... ... @@ -127,10 +127,19 @@ export function useAlarmNotify(params: UseAlarmNotifyParams = {}) {
127 127 clearInterval(timeout as NodeJS.Timer);
128 128 timeout = null;
129 129 };
  130 +
130 131 onMounted(() => {
131 132 if (getRoleHasNotifyFlag()) polling();
132 133 });
133 134
  135 + document.addEventListener('visibilitychange', () => {
  136 + if (document.hidden) {
  137 + clearTimeout();
  138 + } else {
  139 + polling();
  140 + }
  141 + });
  142 +
134 143 onUnmounted(() => {
135 144 clearTimeout();
136 145 });
... ...
... ... @@ -16,15 +16,17 @@
16 16 value?: string | string[];
17 17 apiTreeSelectProps?: Recordable;
18 18 showCreate?: boolean;
  19 + disabled?: boolean;
19 20 }>(),
20 21 {
21 22 showCreate: true,
  23 + disabled: false,
22 24 }
23 25 );
24 26
25 27 const needReload = ref(true);
26 28
27   - const emit = defineEmits(['change']);
  29 + const emit = defineEmits(['change', 'optionsChange']);
28 30
29 31 const handleOpenCreate = () => {
30 32 openDrawer(true, { isUpdate: false });
... ... @@ -35,13 +37,14 @@
35 37 const orgList = ref<Recordable[]>([]);
36 38
37 39 const getBindProps = computed<Recordable>(() => {
38   - const { value, apiTreeSelectProps = {} } = props;
  40 + const { value, apiTreeSelectProps = {}, disabled } = props;
39 41 const { params = {} } = apiTreeSelectProps;
40 42 return {
41 43 replaceFields: { children: 'children', key: 'id', title: 'name', value: 'id' },
42 44 getPopupContainer: () => document.body,
43 45 placeholder: '请选择所属组织',
44 46 maxLength: 250,
  47 + disabled,
45 48 ...apiTreeSelectProps,
46 49 value,
47 50 dropdownStyle: { maxHeight: '300px' },
... ... @@ -63,6 +66,7 @@
63 66 onChange: (...args: any[]) => {
64 67 emit('change', ...args);
65 68 },
  69 + onOptionsChange: (...args: any[]) => emit('optionsChange', ...args),
66 70 };
67 71 });
68 72
... ... @@ -80,7 +84,9 @@
80 84 <template>
81 85 <section class="flex">
82 86 <ApiTreeSelect v-bind="getBindProps" />
83   - <Button v-if="getShowCreate" type="link" @click="handleOpenCreate">新增组织</Button>
  87 + <Button v-if="getShowCreate" type="link" @click="handleOpenCreate" :disabled="disabled"
  88 + >新增组织</Button
  89 + >
84 90 <OrganizationDrawer v-if="getShowCreate" @register="registerDrawer" @success="handleReload" />
85 91 </section>
86 92 </template>
... ...
... ... @@ -222,6 +222,14 @@
222 222 </div>
223 223 </template>
224 224 <template class="ant-card-actions" #actions>
  225 + <Tooltip title="预览">
  226 + <AuthIcon
  227 + :auth="ConfigurationTemplatePermission.PREVIEW"
  228 + class="!text-lg"
  229 + icon="ant-design:eye-outlined"
  230 + @click="handlePreview(item)"
  231 + />
  232 + </Tooltip>
225 233 <Tooltip v-if="!isCustomerUser" title="设计">
226 234 <AuthIcon
227 235 :auth="ConfigurationTemplatePermission.DESIGN"
... ...
... ... @@ -96,6 +96,10 @@
96 96 top: '48%',
97 97 z: 10,
98 98 style: {
  99 + width: 35,
  100 + textAlign: 'left',
  101 + overflow: 'truncate',
  102 + textVerticalAlign: 'middle',
99 103 fill: '#1a1a1a',
100 104 text: value + '个',
101 105 font: '0.85rem Microsoft YaHei',
... ...
... ... @@ -232,12 +232,6 @@ export const schemas = (): FormSchema[] => {
232 232 component: 'InputGroup',
233 233 required: true,
234 234 colProps: { span: 24 },
235   - componentProps: ({ formActionType }) => {
236   - const { getFieldsValue } = formActionType;
237   - return {
238   - type: getFieldsValue().requestContentType,
239   - };
240   - },
241 235 },
242 236 {
243 237 field: 'fillAddress',
... ...
... ... @@ -127,6 +127,15 @@
127 127 showActionButtonGroup: false,
128 128 });
129 129
  130 + const updatePartFormScheme = (requestType: string) => {
  131 + updateSchema({
  132 + field: 'requestHttpTypeAndUrl',
  133 + componentProps: {
  134 + type: requestType,
  135 + },
  136 + });
  137 + };
  138 +
130 139 const [registerDrawer, { setDrawerProps, closeDrawer }] = useDrawerInner(async (data) => {
131 140 await resetFields();
132 141 await nextTick();
... ... @@ -152,13 +161,12 @@
152 161 requestUrl: data.record?.requestUrl,
153 162 },
154 163 });
155   - await nextTick(() => simpleRequestRef.value?.setValue(data.record, false, []));
156   - updateSchema({
157   - field: 'requestHttpTypeAndUrl',
158   - componentProps: {
159   - type: String(data.record?.requestContentType),
160   - },
  164 + await nextTick(() => {
  165 + simpleRequestRef.value?.setValue(data.record, false, []);
  166 + updatePartFormScheme(String(data.record?.requestContentType));
161 167 });
  168 + } else {
  169 + await updatePartFormScheme(RequestMethodTypeEnum.COMMOM);
162 170 }
163 171 });
164 172
... ...
  1 +<script setup lang="ts">
  2 + import { computed, unref } from 'vue';
  3 + import { ref } from 'vue';
  4 + import { BasicModal, useModalInner } from '/@/components/Modal';
  5 + import { schemas } from '../index';
  6 + import { useForm, BasicForm } from '/@/components/Form';
  7 +
  8 + import { deviceProfileCategory } from '/@/api/device/classModal';
  9 + import { useMessage } from '/@/hooks/web/useMessage';
  10 +
  11 + const emit = defineEmits(['handleReload', 'register']);
  12 +
  13 + const isUpdate = ref<Boolean>(false);
  14 + const getTitle = computed(() => (!unref(isUpdate) ? '新增品类' : '编辑品类'));
  15 + const { createMessage } = useMessage();
  16 +
  17 + const [registerForm, { getFieldsValue, setFieldsValue, validate }] = useForm({
  18 + labelWidth: 140,
  19 + schemas,
  20 + actionColOptions: {
  21 + span: 14,
  22 + },
  23 + showActionButtonGroup: false,
  24 + });
  25 + const recordInfo = ref<any>({});
  26 +
  27 + const [register, { closeModal, setModalProps }] = useModalInner(async (data) => {
  28 + console.log(123131, data);
  29 + setModalProps({ confirmLoading: false, loading: true });
  30 + isUpdate.value = data?.isUpdate;
  31 + recordInfo.value = data?.record;
  32 + if (data?.record) {
  33 + setFieldsValue(data?.record);
  34 + }
  35 + setModalProps({ loading: false });
  36 + });
  37 +
  38 + const handleCancel = () => {
  39 + closeModal();
  40 + };
  41 +
  42 + const handleOk = async () => {
  43 + await validate();
  44 + let values = getFieldsValue();
  45 + if (unref(isUpdate)) {
  46 + values = { ...values, id: unref(recordInfo).id };
  47 + }
  48 + await deviceProfileCategory(values);
  49 + createMessage.success('操作成功');
  50 + emit('handleReload');
  51 + handleCancel();
  52 + };
  53 +</script>
  54 +
  55 +<template>
  56 + <div>
  57 + <BasicModal
  58 + v-bind="$attrs"
  59 + width="30rem"
  60 + :title="getTitle"
  61 + @register="register"
  62 + @cancel="handleCancel"
  63 + @ok="handleOk"
  64 + destroyOnClose
  65 + >
  66 + <div>
  67 + <BasicForm @register="registerForm" />
  68 + </div>
  69 + </BasicModal>
  70 + </div>
  71 +</template>
... ...
  1 +import classModal from './classModal.vue';
  2 +
  3 +export { classModal };
... ...
  1 +import { BasicColumn, FormSchema } from '/@/components/Table';
  2 +import { findDictItemByCode } from '/@/api/system/dict';
  3 +
  4 +import { DictEnum } from '/@/enums/dictEnum';
  5 +
  6 +export const columns: BasicColumn[] = [
  7 + {
  8 + title: '名称',
  9 + dataIndex: 'name',
  10 + },
  11 + {
  12 + title: '领域',
  13 + dataIndex: 'dictItemName',
  14 + },
  15 + {
  16 + title: '状态',
  17 + dataIndex: 'status',
  18 + slots: { customRender: 'status' },
  19 + },
  20 +];
  21 +
  22 +export const searchFormSchema: FormSchema[] = [
  23 + {
  24 + field: 'name',
  25 + label: '名称',
  26 + component: 'Input',
  27 + colProps: { span: 6 },
  28 + componentProps: {
  29 + maxLength: 255,
  30 + placeholder: '请输入品类名称',
  31 + },
  32 + },
  33 + {
  34 + field: 'status',
  35 + label: '状态',
  36 + component: 'Select',
  37 + colProps: { span: 6 },
  38 + componentProps: {
  39 + options: [
  40 + { label: '启用', value: '1' },
  41 + { label: '禁用', value: '0' },
  42 + ],
  43 + placeholder: '请选择品类状态',
  44 + },
  45 + },
  46 + {
  47 + field: 'dictItemId',
  48 + label: '领域',
  49 + component: 'ApiSelect',
  50 + colProps: { span: 6 },
  51 + componentProps() {
  52 + return {
  53 + api: findDictItemByCode,
  54 + params: {
  55 + dictCode: DictEnum.CATEGORY_FIELD,
  56 + },
  57 + labelField: 'itemText',
  58 + valueField: 'itemValue',
  59 + placeholder: '请选择领域',
  60 + getPopupContainer: () => document.body,
  61 + };
  62 + },
  63 + },
  64 +];
  65 +
  66 +export const schemas: FormSchema[] = [
  67 + {
  68 + field: 'name',
  69 + label: '品类名称',
  70 + component: 'Input',
  71 + colProps: { span: 21 },
  72 + required: true,
  73 + componentProps: {
  74 + maxLength: 20,
  75 + },
  76 + },
  77 + {
  78 + field: 'dictItemId',
  79 + label: '领域',
  80 + component: 'ApiSelect',
  81 + colProps: { span: 21 },
  82 + required: true,
  83 + componentProps({ formActionType }) {
  84 + return {
  85 + api: findDictItemByCode,
  86 + params: {
  87 + dictCode: DictEnum.CATEGORY_FIELD,
  88 + },
  89 + onChange(value, options) {
  90 + formActionType.setFieldsValue({ dictItemName: value ? options.label : '' });
  91 + },
  92 + labelField: 'itemText',
  93 + valueField: 'itemValue',
  94 + placeholder: '请选择领域',
  95 + getPopupContainer: () => document.body,
  96 + };
  97 + },
  98 + },
  99 + {
  100 + field: 'dictItemName',
  101 + label: '领域名称',
  102 + ifShow: false,
  103 + component: 'Input',
  104 + },
  105 + {
  106 + field: 'status',
  107 + label: '状态',
  108 + component: 'RadioButtonGroup',
  109 + defaultValue: 1,
  110 + componentProps: {
  111 + options: [
  112 + { label: '启用', value: 1 },
  113 + { label: '禁用', value: 0 },
  114 + ],
  115 + },
  116 + },
  117 +];
... ...
  1 +<script setup lang="ts">
  2 + import { ref } from 'vue';
  3 + import { columns, searchFormSchema } from '.';
  4 + import { BasicTable, useTable, TableAction } from '/@/components/Table';
  5 + import { Button, Popconfirm, Switch } from 'ant-design-vue';
  6 + import { useBatchOperation } from '/@/utils/useBatchOperation';
  7 + import { useMessage } from '/@/hooks/web/useMessage';
  8 + import { Authority } from '/@/components/Authority';
  9 + import { useModal } from '/@/components/Modal';
  10 + import { classModal } from './components/index';
  11 + import { useDrawer } from '/@/components/Drawer';
  12 + import { USER_INFO_KEY } from '/@/enums/cacheEnum';
  13 + import { getAuthCache } from '/@/utils/auth';
  14 + import { authBtn } from '/@/enums/roleEnum';
  15 + import {
  16 + getDeviceClassList,
  17 + deleteDeviceClass,
  18 + deviceProfileCategory,
  19 + } from '/@/api/device/classModal';
  20 +
  21 + import { BasicDrawer } from '/@/components/Drawer';
  22 + import PhysicalModelManagementStep from '/@/views/device/profiles/step/PhysicalModelManagementStep.vue';
  23 + const [
  24 + registerTable,
  25 + { reload, setLoading, getSelectRowKeys, setSelectedRowKeys, getRowSelection },
  26 + ] = useTable({
  27 + title: '产品品类列表',
  28 + api: getDeviceClassList,
  29 + columns,
  30 + formConfig: {
  31 + labelWidth: 100,
  32 + schemas: searchFormSchema,
  33 + },
  34 + immediate: true,
  35 + useSearchForm: true,
  36 + showTableSetting: true,
  37 + bordered: true,
  38 + showIndexColumn: false,
  39 + clickToRowSelect: false,
  40 + rowKey: 'id',
  41 + // searchInfo: searchInfo,
  42 + actionColumn: {
  43 + width: 230,
  44 + title: '操作',
  45 + slots: { customRender: 'action' },
  46 + fixed: 'right',
  47 + },
  48 + rowSelection: {
  49 + type: 'checkbox',
  50 + getCheckboxProps: (record: any) => {
  51 + return { disabled: !!record.status };
  52 + },
  53 + },
  54 + });
  55 +
  56 + const [registerModal, { openModal }] = useModal();
  57 + const [registerDetailDrawer, { openDrawer }] = useDrawer();
  58 +
  59 + const { createMessage } = useMessage();
  60 + const { isExistOption } = useBatchOperation(getRowSelection, setSelectedRowKeys);
  61 +
  62 + const switchLoading = ref<boolean>(false);
  63 +
  64 + const userInfo: any = getAuthCache(USER_INFO_KEY);
  65 + const role: string = userInfo.roles[0];
  66 +
  67 + const handleReload = () => {
  68 + setSelectedRowKeys([]);
  69 + reload();
  70 + };
  71 +
  72 + // 新增
  73 + const handleCreate = () => {
  74 + openModal(true, {
  75 + isUpdate: false,
  76 + });
  77 + };
  78 +
  79 + // 编辑
  80 + const handleEdit = (record?: any) => {
  81 + openModal(true, {
  82 + isUpdate: true,
  83 + record,
  84 + });
  85 + };
  86 +
  87 + // 详情
  88 + const registerDetailRecord = ref<any>({});
  89 + const handleDetail = (record?: any) => {
  90 + openDrawer(true);
  91 + registerDetailRecord.value = {
  92 + ...record,
  93 + ifShowClass: true,
  94 + };
  95 + };
  96 +
  97 + // 状态->编辑
  98 + const handleSwitch = async (e: any, record: any) => {
  99 + switchLoading.value = true;
  100 + console.log(e, record);
  101 + await deviceProfileCategory({ ...record, status: e });
  102 + switchLoading.value = false;
  103 + createMessage.success('操作成功');
  104 + handleReload();
  105 + };
  106 +
  107 + // 删除
  108 + const handleDelete = async (record?: any) => {
  109 + let ids: string[] = [];
  110 + if (record) {
  111 + ids = [record.id];
  112 + } else {
  113 + ids = getSelectRowKeys();
  114 + }
  115 + try {
  116 + setLoading(true);
  117 + await deleteDeviceClass({ ids });
  118 + createMessage.success('删除成功');
  119 + handleReload();
  120 + } catch (error) {
  121 + throw error;
  122 + } finally {
  123 + setLoading(false);
  124 + }
  125 + };
  126 +</script>
  127 +<template>
  128 + <div>
  129 + <BasicTable style="flex: auto" @register="registerTable">
  130 + <template #toolbar>
  131 + <Authority value="api:yt:product:category:post">
  132 + <Button type="primary" @click="handleCreate"> 新增品类 </Button>
  133 + </Authority>
  134 + <Authority value="api:yt:product:category:delete">
  135 + <Popconfirm
  136 + title="您确定要批量删除数据"
  137 + ok-text="确定"
  138 + cancel-text="取消"
  139 + @confirm="handleDelete()"
  140 + >
  141 + <a-button color="error" :disabled="!isExistOption"> 批量删除 </a-button>
  142 + </Popconfirm>
  143 + </Authority>
  144 + </template>
  145 + <template #status="{ record }">
  146 + <Switch
  147 + @click="(e) => handleSwitch(e, record)"
  148 + :loading="switchLoading"
  149 + v-model:checked="record.status"
  150 + checked-children="启用"
  151 + un-checked-children="禁用"
  152 + :checkedValue="1"
  153 + :unCheckedValue="0"
  154 + />
  155 + </template>
  156 + <template #action="{ record }">
  157 + <TableAction
  158 + :actions="[
  159 + {
  160 + label: '详情',
  161 + icon: 'ant-design:eye-outlined',
  162 + auth: 'api:yt:product:category:get',
  163 + onClick: handleDetail.bind(null, record),
  164 + },
  165 + {
  166 + label: '编辑',
  167 + auth: 'api:yt:product:category:update',
  168 + icon: 'clarity:note-edit-line',
  169 + ifShow: authBtn(role),
  170 + onClick: handleEdit.bind(null, record),
  171 + },
  172 + {
  173 + label: '删除',
  174 + auth: 'api:yt:product:category:delete',
  175 + icon: 'ant-design:delete-outlined',
  176 + ifShow: authBtn(role) && !record.status,
  177 + color: 'error',
  178 + popConfirm: {
  179 + title: '是否确认删除',
  180 + confirm: handleDelete.bind(null, record),
  181 + },
  182 + },
  183 + ]"
  184 + />
  185 + </template>
  186 + </BasicTable>
  187 + <classModal @register="registerModal" @handleReload="handleReload" />
  188 + <!-- <physicalModel @register="registerDetailDrawer" :record /> -->
  189 + <BasicDrawer title="物模型" @register="registerDetailDrawer" width="60%" destroy-on-close>
  190 + <PhysicalModelManagementStep :record="registerDetailRecord" />
  191 + </BasicDrawer>
  192 + </div>
  193 +</template>
... ...
... ... @@ -16,6 +16,9 @@ import { FileItem } from '/@/components/Form/src/components/ApiUpload.vue';
16 16 import { createImgPreview } from '/@/components/Preview';
17 17 import { uploadThumbnail } from '/@/api/configuration/center/configurationCenter';
18 18
  19 +import { getOrganizationList } from '/@/api/system/system';
  20 +import { copyTransFun } from '/@/utils/fnUtils';
  21 +
19 22 useComponentRegister('JSONEditor', JSONEditor);
20 23 useComponentRegister('ObjectModelValidateForm', ObjectModelValidateForm);
21 24
... ... @@ -263,22 +266,15 @@ export const step1Schemas: FormSchema[] = [
263 266 ifShow: ({ values }) => isGateWay(values.deviceType),
264 267 },
265 268 {
266   - field: 'organizationId',
267   - label: '所属组织',
268   - component: 'Input',
269   - required: true,
270   - slot: 'addOrg',
271   - },
272   - {
273 269 field: 'gatewayId',
274 270 label: '网关设备',
275 271 required: true,
276 272 component: 'ApiSelect',
277   - ifShow: ({ values }) => values.deviceType === 'SENSOR' && values.organizationId,
  273 + ifShow: ({ values }) => values.deviceType === 'SENSOR',
278 274 componentProps: ({ formModel, formActionType }) => {
279   - const { organizationId, transportType } = formModel;
  275 + const { transportType } = formModel;
280 276 const { validateFields } = formActionType;
281   - if (![organizationId, transportType].every(Boolean)) return {};
  277 + if (!transportType) return {};
282 278 return {
283 279 api: async (params: Recordable) => {
284 280 try {
... ... @@ -291,19 +287,36 @@ export const step1Schemas: FormSchema[] = [
291 287 },
292 288 showSearch: true,
293 289 params: {
294   - organizationId,
295 290 transportType,
296 291 },
297 292 valueField: 'tbDeviceId',
298 293 labelField: 'alias',
299   - onChange: async () => {
  294 + onChange: async (value, options) => {
300 295 await nextTick();
  296 + if (value) {
  297 + const data = await getOrganizationList({ organizationId: options?.organizationId });
  298 + copyTransFun(data as any as any[]);
  299 + formModel.organizationList = data;
  300 + }
301 301 validateFields(['gatewayId']);
302 302 },
303 303 };
304 304 },
305 305 },
306 306 {
  307 + field: 'organizationList',
  308 + label: '依据网关设备请求的组织数组',
  309 + component: 'Input',
  310 + ifShow: false,
  311 + },
  312 + {
  313 + field: 'organizationId',
  314 + label: '所属组织',
  315 + component: 'Select',
  316 + required: true,
  317 + slot: 'addOrg',
  318 + },
  319 + {
307 320 field: 'label',
308 321 label: '设备标签',
309 322 component: 'Input',
... ...
... ... @@ -14,7 +14,7 @@
14 14 placeholder="请选择组织"
15 15 allow-clear
16 16 tree-default-expand-all
17   - :tree-data="treeData"
  17 + :tree-data="model?.['organizationList'] || treeData"
18 18 />
19 19 </div>
20 20 <div>
... ... @@ -49,44 +49,46 @@
49 49 centered
50 50 >
51 51 <div>
52   - <Form :label-col="labelCol" :colon="false" :rules="rules" :model="positionState">
53   - <Row :gutter="20" class="mt-4">
54   - <Col :span="20">
55   - <FormItem label="搜索位置">
56   - <AutoComplete
57   - v-model:value="positionState.address"
58   - :options="dataSource"
59   - style="width: 100%"
60   - placeholder="搜索位置"
61   - @search="debounceSearch"
62   - @select="handleSelect"
63   - backfill
64   - />
65   - </FormItem>
66   - </Col>
67   - </Row>
68   - <Row :gutter="20" class="">
69   - <Col :span="10">
70   - <FormItem label="经度" name="longitude">
71   - <Input
72   - @blur="redirectPosition"
73   - @change="redirectPosition"
74   - v-model:value="positionState.longitude"
75   - />
76   - </FormItem>
77   - </Col>
78   - <Col :span="10">
79   - <FormItem label="纬度" name="latitude">
80   - <Input
81   - @blur="redirectPosition"
82   - @change="redirectPosition"
83   - v-model:value="positionState.latitude"
84   - />
85   - </FormItem>
86   - </Col>
87   - </Row>
88   - </Form>
89   - <div ref="wrapRef" style="height: 300px; width: 90%" class="ml-6"></div>
  52 + <Spin :spinning="spinning">
  53 + <Form :label-col="labelCol" :colon="false" :rules="rules" :model="positionState">
  54 + <Row :gutter="20" class="mt-4">
  55 + <Col :span="20">
  56 + <FormItem label="搜索位置">
  57 + <AutoComplete
  58 + v-model:value="positionState.address"
  59 + :options="dataSource"
  60 + style="width: 100%"
  61 + placeholder="搜索位置"
  62 + @search="debounceSearch"
  63 + @select="handleSelect"
  64 + backfill
  65 + />
  66 + </FormItem>
  67 + </Col>
  68 + </Row>
  69 + <Row :gutter="20" class="">
  70 + <Col :span="10">
  71 + <FormItem label="经度" name="longitude">
  72 + <Input
  73 + @blur="redirectPosition"
  74 + @change="redirectPosition"
  75 + v-model:value="positionState.longitude"
  76 + />
  77 + </FormItem>
  78 + </Col>
  79 + <Col :span="10">
  80 + <FormItem label="纬度" name="latitude">
  81 + <Input
  82 + @blur="redirectPosition"
  83 + @change="redirectPosition"
  84 + v-model:value="positionState.latitude"
  85 + />
  86 + </FormItem>
  87 + </Col>
  88 + </Row>
  89 + </Form>
  90 + <div ref="wrapRef" style="height: 300px; width: 90%" class="ml-6"></div>
  91 + </Spin>
90 92 </div>
91 93 </Modal>
92 94 <DeptDrawer @register="registerModal" @success="handleSuccess" />
... ... @@ -97,7 +99,7 @@
97 99 import { BasicForm, useForm } from '/@/components/Form';
98 100 import { step1Schemas } from '../../config/data';
99 101 import { useScript } from '/@/hooks/web/useScript';
100   - import { Input, message, Modal, Form, Row, Col, AutoComplete } from 'ant-design-vue';
  102 + import { Input, message, Modal, Form, Row, Col, AutoComplete, Spin } from 'ant-design-vue';
101 103 import { EnvironmentTwoTone } from '@ant-design/icons-vue';
102 104 import { upload } from '/@/api/oss/ossFileUploader';
103 105 import { BAI_DU_MAP_URL } from '/@/utils/fnUtils';
... ... @@ -113,6 +115,8 @@
113 115 import { toRaw } from 'vue';
114 116 import { FileItem } from '/@/components/Form/src/components/ApiUpload.vue';
115 117 import { buildUUID } from '/@/utils/uuid';
  118 + import { useMessage } from '/@/hooks/web/useMessage';
  119 + import { computed } from 'vue';
116 120
117 121 export default defineComponent({
118 122 components: {
... ... @@ -126,6 +130,7 @@
126 130 Row,
127 131 Col,
128 132 DeptDrawer,
  133 + Spin,
129 134 },
130 135 props: {
131 136 isUpdate: {
... ... @@ -134,9 +139,14 @@
134 139 },
135 140 emits: ['next'],
136 141 setup(props, { emit }) {
  142 + const { createMessage } = useMessage();
137 143 const orgData: any = reactive({
138 144 treeData: [],
139 145 });
  146 + const isUpdate1 = computed(() => {
  147 + const { isUpdate } = props;
  148 + return isUpdate;
  149 + });
140 150 const getOrganizationListFunc = async () => {
141 151 const data = await getOrganizationList();
142 152 copyTransFun(data as any as any[]);
... ... @@ -168,17 +178,27 @@
168 178 const devicePic = ref('');
169 179 const loading = ref(false);
170 180
171   - const [register, { validate, resetFields, setFieldsValue, getFieldsValue, updateSchema }] =
172   - useForm({
173   - labelWidth: 140,
174   - schemas: step1Schemas,
175   - actionColOptions: {
176   - span: 14,
177   - },
178   - labelAlign: 'right',
179   - showResetButton: false,
180   - showSubmitButton: false,
181   - });
  181 + const [
  182 + register,
  183 + {
  184 + validate,
  185 + resetFields,
  186 + setFieldsValue,
  187 + getFieldsValue,
  188 + updateSchema,
  189 + clearValidate,
  190 + validateFields,
  191 + },
  192 + ] = useForm({
  193 + labelWidth: 140,
  194 + schemas: step1Schemas,
  195 + actionColOptions: {
  196 + span: 14,
  197 + },
  198 + labelAlign: 'right',
  199 + showResetButton: false,
  200 + showSubmitButton: false,
  201 + });
182 202 async function nextStep() {
183 203 try {
184 204 let values = await validate();
... ... @@ -220,21 +240,53 @@
220 240 };
221 241
222 242 // 地图的弹框
  243 + const spinning = ref<boolean>(false);
223 244 const visible = ref(false);
224   - const selectPosition = () => {
  245 + const selectPosition = async () => {
225 246 visible.value = true;
226   - if (!positionState.longitude) {
227   - positionState.longitude = '104.05326410962411';
228   - positionState.latitude = '30.54855093076791';
229   -
230   - // 根据经纬度获取详细位置
231   - if (positionState.longitude && positionState.latitude) {
232   - var pt = new BMap.Point(positionState.longitude, positionState.latitude);
233   - getAddrByPoint(pt);
  247 + if (
  248 + !unref(isUpdate1) &&
  249 + unref(devicePositionState).longitude &&
  250 + unref(devicePositionState).latitude
  251 + ) {
  252 + positionState.longitude = unref(devicePositionState).longitude;
  253 + positionState.latitude = unref(devicePositionState).latitude;
  254 + }
  255 + if (!positionState.longitude || !positionState.latitude) {
  256 + let getLocalCity = new BMap.LocalCity();
  257 + let getLocation = new BMap.Geolocation();
  258 + const userAgent = navigator.userAgent;
  259 + try {
  260 + spinning.value = true;
  261 + if (
  262 + (userAgent.indexOf('Chrome') > -1 || userAgent.indexOf('WebKit') > -1) &&
  263 + userAgent.indexOf('Edg') == -1
  264 + ) {
  265 + //判断是否是谷歌 或则是谷歌的内核 '104.05326410962411'; '30.54855093076791';
  266 + await getLocalCity.get(function (res) {
  267 + positionState.longitude = res?.center.lng || '104.05326410962411';
  268 + positionState.latitude = res?.center.lat || '30.54855093076791';
  269 + var pt = new BMap.Point(positionState.longitude, positionState.latitude);
  270 + getAddrByPoint(pt);
  271 + spinning.value = false;
  272 + });
  273 + } else {
  274 + await getLocation.getCurrentPosition(function (res) {
  275 + positionState.longitude = res?.longitude || '104.05326410962411';
  276 + positionState.latitude = res?.latitude || '30.54855093076791';
  277 + var pt = new BMap.Point(positionState.longitude, positionState.latitude);
  278 + getAddrByPoint(pt);
  279 + spinning.value = false;
  280 + });
  281 + }
  282 + } catch (err) {
  283 + createMessage.error('获取定位失败');
234 284 }
235 285 initMap(positionState.longitude, positionState.latitude);
236 286 } else {
237 287 initMap(positionState.longitude, positionState.latitude);
  288 + var pt = new BMap.Point(positionState.longitude, positionState.latitude);
  289 + getAddrByPoint(pt);
238 290 }
239 291 };
240 292
... ... @@ -447,11 +499,11 @@
447 499 });
448 500 }
449 501
450   - const handleTreeOrg = () => {
451   - const clearGatewayId = {
452   - gatewayId: '',
453   - };
454   - setFieldsValue(clearGatewayId);
  502 + const handleTreeOrg = (option: string) => {
  503 + if (option) clearValidate('organizationId');
  504 + else {
  505 + validateFields(['organizationId']);
  506 + }
455 507 };
456 508
457 509 return {
... ... @@ -488,6 +540,7 @@
488 540 handleSuccess,
489 541 handleTreeOrg,
490 542 devicePositionState,
  543 + spinning,
491 544 };
492 545 },
493 546 });
... ...
... ... @@ -15,13 +15,13 @@
15 15 import { useHistoryData } from '../../hook/useHistoryData';
16 16 import { formatToDateTime } from '/@/utils/dateUtil';
17 17 import { useTable, BasicTable, BasicColumn } from '/@/components/Table';
18   - import { DataTypeEnum } from '/@/components/Form/src/externalCompns/components/StructForm/config';
19 18 import {
20 19 ModeSwitchButton,
21 20 TABLE_CHART_MODE_LIST,
22 21 EnumTableChartMode,
23 22 } from '/@/components/Widget';
24 23 import { SchemaFiled } from '/@/views/visual/palette/components/HistoryTrendModal/config';
  24 + import { DataTypeEnum } from '/@/enums/objectModelEnum';
25 25
26 26 interface DeviceDetail {
27 27 tbDeviceId: string;
... ... @@ -188,7 +188,7 @@
188 188 method.setFieldsValue({ keys: props.attr });
189 189 const attrInfo = unref(deviceAttrs).find((item) => item.identifier === props.attr);
190 190 if (
191   - [DataTypeEnum.IS_STRING, DataTypeEnum.IS_STRUCT].includes(
  191 + [DataTypeEnum.STRING, DataTypeEnum.STRUCT].includes(
192 192 attrInfo?.detail.dataType.type as unknown as DataTypeEnum
193 193 )
194 194 ) {
... ...
... ... @@ -16,7 +16,6 @@
16 16 import { DeviceModelOfMatterAttrs, DeviceRecord } from '/@/api/device/model/deviceModel';
17 17 import { Specs, StructJSON } from '/@/api/device/model/modelOfMatterModel';
18 18 import { isArray, isNull, isObject } from '/@/utils/is';
19   - import { DataTypeEnum } from '/@/components/Form/src/externalCompns/components/StructForm/config';
20 19 import { useGlobSetting } from '/@/hooks/setting';
21 20 import { ModeSwitchButton, EnumTableCardMode } from '/@/components/Widget';
22 21 import { toRaw } from 'vue';
... ... @@ -25,6 +24,7 @@
25 24 import { ModalParamsType } from '/#/utils';
26 25 import { AreaChartOutlined } from '@ant-design/icons-vue';
27 26 import { SvgIcon } from '/@/components/Icon';
  27 + import { DataTypeEnum } from '/@/enums/objectModelEnum';
28 28
29 29 interface ReceiveMessage {
30 30 data: {
... ... @@ -220,7 +220,7 @@
220 220 };
221 221
222 222 const isStructAndTextType = (type: DataTypeEnum) => {
223   - return [DataTypeEnum.IS_STRUCT, DataTypeEnum.IS_STRING].includes(type);
  223 + return [DataTypeEnum.STRUCT, DataTypeEnum.STRING].includes(type);
224 224 };
225 225
226 226 const setDataSource = () => {
... ... @@ -311,7 +311,7 @@
311 311 });
312 312
313 313 const formatValue = (item: DataSource) => {
314   - return item.type === DataTypeEnum.IS_BOOL
  314 + return item.type === DataTypeEnum.BOOL
315 315 ? !isNull(item.value)
316 316 ? !!Number(item.value)
317 317 ? item.boolOpen
... ...
... ... @@ -6,7 +6,6 @@
6 6 import { StructJSON } from '/@/api/device/model/modelOfMatterModel';
7 7 import { BasicForm, useForm } from '/@/components/Form';
8 8 import { BasicModal, useModalInner } from '/@/components/Modal';
9   - import { DataTypeEnum } from '/@/views/device/profiles/step/cpns/physical/cpns/config';
10 9 import { sendCommandOneway } from '/@/api/dataBoard';
11 10 import { useMessage } from '/@/hooks/web/useMessage';
12 11 import { unref } from 'vue';
... ... @@ -14,6 +13,7 @@
14 13 import { TaskTypeEnum } from '/@/views/task/center/config';
15 14 import { SingleToHex, formSchemasConfig } from './config';
16 15 import { TransportTypeEnum } from '/@/views/device/profiles/components/TransportDescript/const';
  16 + import { DataTypeEnum } from '/@/enums/objectModelEnum';
17 17
18 18 defineEmits(['register']);
19 19 const props = defineProps<{ deviceId: string; deviceName: string }>();
... ... @@ -51,7 +51,7 @@
51 51
52 52 let schemas = [{ dataType: dataType, identifier, functionName: name } as StructJSON];
53 53
54   - if (type === DataTypeEnum.IS_STRUCT) {
  54 + if (type === DataTypeEnum.STRUCT) {
55 55 schemas = dataType?.specs as StructJSON[];
56 56 }
57 57
... ...
1 1 import { DataType, Specs, StructJSON } from '/@/api/device/model/modelOfMatterModel';
2 2 import { JSONEditor } from '/@/components/CodeEditor';
3 3 import { FormSchema, useComponentRegister } from '/@/components/Form';
  4 +import { DataTypeEnum } from '/@/enums/objectModelEnum';
4 5 import { useJsonParse } from '/@/hooks/business/useJsonParse';
5   -import { DataTypeEnum } from '/@/views/device/profiles/step/cpns/physical/cpns/config';
6 6
7 7 export interface BasicCreateFormParams {
8 8 identifier: string;
... ... @@ -19,9 +19,9 @@ const validateDouble = (
19 19 max?: number | string
20 20 ) => {
21 21 min =
22   - Number(min) || type === DataTypeEnum.IS_NUMBER_INT ? Number.MIN_SAFE_INTEGER : Number.MIN_VALUE;
  22 + Number(min) || type === DataTypeEnum.NUMBER_INT ? Number.MIN_SAFE_INTEGER : Number.MIN_VALUE;
23 23 max =
24   - Number(max) || type === DataTypeEnum.IS_NUMBER_INT ? Number.MAX_SAFE_INTEGER : Number.MAX_VALUE;
  24 + Number(max) || type === DataTypeEnum.NUMBER_INT ? Number.MAX_SAFE_INTEGER : Number.MAX_VALUE;
25 25
26 26 return {
27 27 flag: value < min || value > max,
... ... @@ -56,13 +56,11 @@ export const useGenDynamicForm = () => {
56 56 },
57 57 ],
58 58 componentProps: {
59   - max:
60   - max ?? type === DataTypeEnum.IS_NUMBER_INT ? Number.MAX_SAFE_INTEGER : Number.MAX_VALUE,
61   - min:
62   - min ?? type === DataTypeEnum.IS_NUMBER_INT ? Number.MIN_SAFE_INTEGER : Number.MIN_VALUE,
  59 + max: max ?? type === DataTypeEnum.NUMBER_INT ? Number.MAX_SAFE_INTEGER : Number.MAX_VALUE,
  60 + min: min ?? type === DataTypeEnum.NUMBER_INT ? Number.MIN_SAFE_INTEGER : Number.MIN_VALUE,
63 61 step,
64 62 placeholder: `请输入${functionName}`,
65   - precision: type === DataTypeEnum.IS_NUMBER_INT ? 0 : 2,
  63 + precision: type === DataTypeEnum.NUMBER_INT ? 0 : 2,
66 64 },
67 65 } as FormSchema;
68 66 };
... ... @@ -141,11 +139,11 @@ export const useGenDynamicForm = () => {
141 139 };
142 140
143 141 const schemaMethod = {
144   - [DataTypeEnum.IS_BOOL]: createSelect,
145   - [DataTypeEnum.IS_NUMBER_DOUBLE]: createInputNumber,
146   - [DataTypeEnum.IS_NUMBER_INT]: createInputNumber,
147   - [DataTypeEnum.IS_STRING]: createInput,
148   - [DataTypeEnum.IS_STRUCT]: createInputJson,
  142 + [DataTypeEnum.BOOL]: createSelect,
  143 + [DataTypeEnum.NUMBER_DOUBLE]: createInputNumber,
  144 + [DataTypeEnum.NUMBER_INT]: createInputNumber,
  145 + [DataTypeEnum.STRING]: createInput,
  146 + [DataTypeEnum.STRUCT]: createInputJson,
149 147 };
150 148
151 149 const fieldTypeMap = new Map<string, DataTypeEnum>();
... ... @@ -176,7 +174,7 @@ export const useGenDynamicForm = () => {
176 174 const dataType = fieldTypeMap.get(next)!;
177 175
178 176 let itemValue = value[next];
179   - if (dataType === DataTypeEnum.IS_STRUCT) {
  177 + if (dataType === DataTypeEnum.STRUCT) {
180 178 const { value } = useJsonParse(itemValue);
181 179 itemValue = value;
182 180 }
... ...
... ... @@ -322,4 +322,8 @@
322 322 .profile-list:deep(.ant-card-body) {
323 323 @apply !p-4;
324 324 }
  325 +
  326 + :deep(.ant-spin-nested-loading) {
  327 + height: 40rem;
  328 + }
325 329 </style>
... ...
... ... @@ -4,6 +4,7 @@
4 4 :maskClosable="false"
5 5 v-bind="$attrs"
6 6 :width="dynamicWidth"
  7 + :destroy-on-close="true"
7 8 @register="register"
8 9 @ok="handleSubmit"
9 10 @cancel="handleCancel"
... ... @@ -125,6 +126,8 @@
125 126 const title = unref(isUpdate) ? '编辑产品' : '新增产品';
126 127 setModalProps({ title, showOkBtn: true, showCancelBtn: true });
127 128 if (unref(isUpdate)) {
  129 + const { deviceType } = res || {};
  130 + deviceTypeStr.value = deviceType;
128 131 await setDeviceConfEditFormData(res);
129 132 await setTransConfEditFormData(res);
130 133 handleStepNext(false, res);
... ... @@ -157,7 +160,6 @@
157 160 };
158 161 const deviceTypeStr = ref('');
159 162 const handleGetDeviceType = async (e) => {
160   - console.log(e);
161 163 deviceTypeStr.value = e;
162 164 await nextTick();
163 165 TransConStRef.value?.updateDisabled(e);
... ...
  1 +<script lang="ts" setup>
  2 + import { columns, searchFormSchema } from './config';
  3 + import { BasicDrawer, useDrawerInner } from '/@/components/Drawer';
  4 + import { BasicTable } from '/@/components/Table';
  5 + import { useTable } from '/@/components/Table';
  6 + import { Button } from 'ant-design-vue';
  7 + import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
  8 + import { getDeviceClassList } from '/@/api/device/classModal';
  9 + import { ref } from 'vue';
  10 + const emits = defineEmits([
  11 + 'handleOpenListDrawer',
  12 + 'handleSelectCategory',
  13 + 'handleClose',
  14 + 'register',
  15 + ]);
  16 +
  17 + const categoryId = ref<string | undefined | null>();
  18 + const [registerDrawer] = useDrawerInner((data) => {
  19 + categoryId.value = data.categoryId || {};
  20 + });
  21 + const [registerTable] = useTable({
  22 + title: '',
  23 + api: getDeviceClassList,
  24 + columns,
  25 + formConfig: {
  26 + schemas: searchFormSchema,
  27 + },
  28 + searchInfo: {
  29 + status: 1,
  30 + },
  31 + resizeHeightOffset: 50,
  32 + useSearchForm: true,
  33 + showTableSetting: false,
  34 + bordered: true,
  35 + showIndexColumn: false,
  36 + clickToRowSelect: false,
  37 + rowKey: 'id',
  38 + actionColumn: {
  39 + width: 100,
  40 + title: '操作',
  41 + slots: { customRender: 'action' },
  42 + fixed: 'right',
  43 + },
  44 + });
  45 +
  46 + const closeDrawer = () => {
  47 + emits('handleClose');
  48 + };
  49 +
  50 + const handleChecked = (item?: any) => {
  51 + emits('handleSelectCategory', item);
  52 + };
  53 +
  54 + const handleOpenListDrawer = (record?: any) => {
  55 + emits('handleOpenListDrawer', record);
  56 + };
  57 +</script>
  58 +
  59 +<template>
  60 + <BasicDrawer
  61 + :zIndex="1100"
  62 + v-bind="$attrs"
  63 + @register="registerDrawer"
  64 + :showFooter="true"
  65 + destroyOnClose
  66 + @close="closeDrawer"
  67 + title="选择品类"
  68 + width="32%"
  69 + >
  70 + <BasicTable @register="registerTable">
  71 + <template #name="{ record }">
  72 + <span>{{ record.name }}</span>
  73 + <ExclamationCircleOutlined
  74 + style="color: #377dff"
  75 + @click.stop="handleOpenListDrawer(record)"
  76 + class="ml-1"
  77 + />
  78 + </template>
  79 + <template #action="{ record }">
  80 + <Button type="link" :disabled="categoryId === record.id" @click="handleChecked(record)">{{
  81 + categoryId === record.id ? '已选择' : '选择'
  82 + }}</Button>
  83 + </template>
  84 + </BasicTable>
  85 + </BasicDrawer>
  86 +</template>
... ...
  1 +<script lang="ts" setup>
  2 + import { columnsDrawer } from './config';
  3 + import { unref, ref, watch, nextTick } from 'vue';
  4 + import { BasicDrawer, useDrawerInner } from '/@/components/Drawer';
  5 + import { BasicTable } from '/@/components/Table';
  6 + import { useTable } from '/@/components/Table';
  7 + import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
  8 + import { getModelList } from '/@/api/device/modelOfMatter';
  9 +
  10 + defineEmits(['register']);
  11 +
  12 + const props = defineProps<{ cateforyListInfo: any }>();
  13 +
  14 + const recordInfo = ref<any>({});
  15 + const secondVisible = ref<Boolean>(false);
  16 +
  17 + watch(
  18 + () => props.cateforyListInfo,
  19 + async () => {
  20 + recordInfo.value = props.cateforyListInfo.record || {};
  21 + secondVisible.value = props.cateforyListInfo.isRight || false;
  22 + await nextTick();
  23 + reload();
  24 + }
  25 + );
  26 + const [registerListDrawer, { closeDrawer }] = useDrawerInner();
  27 + const [registerTable, { reload }] = useTable({
  28 + title: '',
  29 + api: async (params: Record<'page' | 'pageSize', number>) => {
  30 + return await getModelList({
  31 + ...params,
  32 + id: unref(recordInfo).id,
  33 + selectType: 'category',
  34 + });
  35 + },
  36 + columns: columnsDrawer,
  37 + useSearchForm: false,
  38 + showTableSetting: false,
  39 + bordered: true,
  40 + showIndexColumn: false,
  41 + clickToRowSelect: false,
  42 + rowKey: 'id',
  43 + });
  44 +
  45 + const handleClose = () => {
  46 + closeDrawer();
  47 + };
  48 +</script>
  49 +
  50 +<template>
  51 + <BasicDrawer
  52 + :zIndex="1100"
  53 + @register="registerListDrawer"
  54 + wrap-class-name="secondDrawer"
  55 + :wrap-style="{
  56 + right: secondVisible ? '32%' : 0,
  57 + transition: 'none',
  58 + }"
  59 + v-bind="$attrs"
  60 + destroyOnClose
  61 + @close="handleClose"
  62 + title="物模型"
  63 + width="36%"
  64 + >
  65 + <BasicTable @register="registerTable">
  66 + <template #name="{ record }">
  67 + <span>{{ record.name }}</span>
  68 + <ExclamationCircleOutlined class="ml-1" />
  69 + </template>
  70 + </BasicTable>
  71 + </BasicDrawer>
  72 +</template>
... ...
  1 +<template>
  2 + <BasicModal
  3 + title="导入物模型"
  4 + :maskClosable="false"
  5 + destroyOnClose
  6 + v-bind="$attrs"
  7 + width="25rem"
  8 + @register="register"
  9 + @cancel="handleCancel"
  10 + :showOkBtn="false"
  11 + >
  12 + <div class="w-full h-full" ref="loadingRef">
  13 + <!-- <div class="flex justify-end">
  14 + <Button @click="handleTemplateDownload" type="link">EXCEL模板下载</Button>
  15 + </div> -->
  16 + <div class="flex justify-evenly items-center h-50 !w-full">
  17 + <Upload
  18 + accept=".json,"
  19 + :show-upload-list="false"
  20 + :customRequest="handleImportModel"
  21 + class="flex justify-center items-center"
  22 + >
  23 + <div class="flex flex-col justify-center items-center">
  24 + <img :src="JSONImage" alt="avatar" class="w-20 h-20" />
  25 + </div>
  26 + </Upload>
  27 + <Upload
  28 + accept=".csv,.xls,.xlsx"
  29 + :show-upload-list="false"
  30 + :customRequest="handleCSVImport"
  31 + class="flex justify-center items-center"
  32 + >
  33 + <div class="flex flex-col justify-center items-center">
  34 + <img :src="CSVImage" alt="avatar" class="w-20 h-20" />
  35 + </div>
  36 + </Upload>
  37 + </div>
  38 + </div>
  39 + </BasicModal>
  40 +</template>
  41 +<script lang="ts" setup>
  42 + import { ref, unref } from 'vue';
  43 + import { Upload } from 'ant-design-vue';
  44 + import { BasicModal, useModalInner } from '/@/components/Modal';
  45 + import { DeviceRecord } from '/@/api/device/model/deviceModel';
  46 + import { useMessage } from '/@/hooks/web/useMessage';
  47 + import { isObject, isString } from '/@/utils/is';
  48 + import {
  49 + importCsvCategory,
  50 + importModelCategory,
  51 + importModelOfMatter,
  52 + importCsvDeviceProfileId,
  53 + } from '/@/api/device/modelOfMatter';
  54 + // import XLSX, { CellObject } from 'xlsx';
  55 + import { useLoading } from '/@/components/Loading';
  56 + import JSONImage from '/@/assets/svg/JSON.svg';
  57 + import CSVImage from '/@/assets/svg/excel.svg';
  58 +
  59 + const emits = defineEmits(['register', 'handleImportCSV', 'handleReload']);
  60 +
  61 + defineProps<{
  62 + record: DeviceRecord;
  63 + }>();
  64 +
  65 + const { createMessage } = useMessage();
  66 + const loadingRef = ref();
  67 + const [openLoading, closeLoading] = useLoading({
  68 + target: loadingRef,
  69 + props: {
  70 + tip: '加载中',
  71 + absolute: true,
  72 + },
  73 + });
  74 +
  75 + const isEmptyObject = (value: any) => isObject(value) && !Object.keys(value).length;
  76 +
  77 + const ImportInfo = ref<{ id?: string; isCateGory?: string | Boolean }>({});
  78 +
  79 + const [register, { closeModal }] = useModalInner(async (data) => {
  80 + ImportInfo.value = data;
  81 + });
  82 + // 导入loading
  83 + const importLoading = ref(false);
  84 +
  85 + const paseJSON = (string: string) => {
  86 + let data = null;
  87 + let flag = false;
  88 + try {
  89 + if (!isString(string)) return { flag: false, data };
  90 + data = JSON.parse(string);
  91 + flag = true;
  92 + if (!isObject(data)) flag = false;
  93 + } catch (error) {}
  94 + return { flag, data };
  95 + };
  96 +
  97 + // JSON导入
  98 + const handleImportModel = async (data: { file: File }) => {
  99 + const fileReader = new FileReader();
  100 + const { isCateGory, id } = unref(ImportInfo);
  101 +
  102 + fileReader.onload = async () => {
  103 + const { flag, data } = paseJSON(fileReader.result as string);
  104 + if (!flag) {
  105 + createMessage.warning('JSON解析失败,请导入正确的JSON');
  106 + return;
  107 + }
  108 + openLoading();
  109 + try {
  110 + importLoading.value = true;
  111 +
  112 + Object.keys(data || {}).forEach((key) => {
  113 + const value = (data || {})[key];
  114 + if (value && isEmptyObject(value)) {
  115 + (data || {})[key] = [];
  116 + }
  117 + });
  118 +
  119 + const result = isCateGory
  120 + ? await importModelCategory({
  121 + categoryId: id,
  122 + data: data!,
  123 + functionType: 'all',
  124 + })
  125 + : await importModelOfMatter({
  126 + tkDeviceProfileId: id,
  127 + data: data!,
  128 + functionType: 'all',
  129 + });
  130 +
  131 + result
  132 + ? createMessage.success('导入成功~')
  133 + : createMessage.error('JSON解析失败,请导入正确的JSON');
  134 +
  135 + result && emits('handleReload');
  136 + } catch (error) {
  137 + throw error;
  138 + } finally {
  139 + importLoading.value = false;
  140 + closeLoading();
  141 + closeModal();
  142 + }
  143 + };
  144 +
  145 + fileReader.readAsText(data.file, 'utf-8');
  146 + };
  147 +
  148 + // CSV导入
  149 + const handleCSVImport = async ({ file }) => {
  150 + const { isCateGory, id } = unref(ImportInfo);
  151 +
  152 + try {
  153 + openLoading();
  154 + const formData = new FormData();
  155 + formData.set('file', file);
  156 + const flag = isCateGory
  157 + ? await importCsvCategory({ categoryId: id, deviceProfileId: undefined, file: formData })
  158 + : await importCsvDeviceProfileId({
  159 + categoryId: undefined,
  160 + deviceProfileId: id,
  161 + file: formData,
  162 + });
  163 + flag && createMessage.info(flag?.message);
  164 + } catch (msg) {
  165 + console.log(msg, 'msg');
  166 + } finally {
  167 + closeLoading();
  168 + closeModal();
  169 + emits('handleReload');
  170 + }
  171 + };
  172 +
  173 + // const downloadFile = (data: string, fileName: string, type: string, ext: string) => {
  174 + // const blob = new Blob([data], { type: type });
  175 + // const objectURL = URL.createObjectURL(blob);
  176 + // const element = document.createElement('a');
  177 + // element.href = objectURL;
  178 + // element.download = `${fileName}.${ext}`;
  179 + // element.style.display = 'none';
  180 + // document.body.appendChild(element);
  181 + // element.click();
  182 + // element.remove();
  183 + // URL.revokeObjectURL(objectURL);
  184 + // };
  185 +
  186 + // 模板下载
  187 + // const handleTemplateDownload = () => {
  188 +
  189 + // };
  190 +
  191 + const handleCancel = () => {
  192 + closeModal();
  193 + };
  194 +</script>
  195 +
  196 +<style lang="less" scope></style>
... ...
  1 +import { BasicColumn, FormSchema } from '/@/components/Table';
  2 +import { findDictItemByCode } from '/@/api/system/dict';
  3 +import { h, unref } from 'vue';
  4 +import { Tooltip } from 'ant-design-vue';
  5 +
  6 +import { FunctionType } from '/@/views/device/profiles/step/cpns/physical/cpns/config';
  7 +import { useMessage } from '/@/hooks/web/useMessage';
  8 +import { useClipboard } from '@vueuse/core';
  9 +import { DictEnum } from '/@/enums/dictEnum';
  10 +
  11 +// import {
  12 +// EventType,
  13 +// EventTypeColor,
  14 +// EventTypeName,
  15 +// } from '/@/views/device/list/cpns/tabs/EventManage/config';
  16 +
  17 +export const columns: BasicColumn[] = [
  18 + {
  19 + title: '品类名称',
  20 + dataIndex: 'name',
  21 + slots: { customRender: 'name' },
  22 + },
  23 + {
  24 + title: '领域',
  25 + dataIndex: 'dictItemName',
  26 + },
  27 +];
  28 +
  29 +export const formatFunctionType: Record<FunctionType, string> = {
  30 + [FunctionType.PROPERTIES]: '属性',
  31 + [FunctionType.EVENTS]: '事件',
  32 + [FunctionType.SERVICE]: '服务',
  33 +};
  34 +
  35 +const handleCopy = async (value: string) => {
  36 + const { createMessage } = useMessage();
  37 + const { copied, copy } = useClipboard({ legacy: true });
  38 + await copy(value);
  39 + if (unref(copied)) createMessage.success('复制成功~');
  40 + else createMessage.error('复制失败~');
  41 +};
  42 +
  43 +export const columnsDrawer: BasicColumn[] = [
  44 + {
  45 + title: '功能类型',
  46 + dataIndex: 'functionType',
  47 + width: 90,
  48 + format: (text: FunctionType) => {
  49 + return formatFunctionType[text];
  50 + },
  51 + },
  52 + {
  53 + title: '功能名称',
  54 + dataIndex: 'functionName',
  55 + width: 90,
  56 + ellipsis: true,
  57 + },
  58 + {
  59 + title: '标识符',
  60 + dataIndex: 'identifier',
  61 + width: 90,
  62 + ellipsis: true,
  63 + customRender: ({ text }: Record<'text', string>) => {
  64 + return h(Tooltip, { title: text }, () =>
  65 + h('span', { class: 'cursor-pointer', onClick: () => handleCopy(text) }, text)
  66 + );
  67 + },
  68 + },
  69 + {
  70 + title: '数据类型',
  71 + dataIndex: 'functionJson.dataType.type',
  72 + width: 100,
  73 + format: (text: string) => {
  74 + return text || '--';
  75 + },
  76 + ellipsis: true,
  77 + },
  78 + // {
  79 + // title: '事件类型',
  80 + // dataIndex: 'eventType',
  81 + // customRender({ text }) {
  82 + // return h(
  83 + // Tag,
  84 + // {
  85 + // color: EventTypeColor[text as EventType],
  86 + // },
  87 + // () => EventTypeName[text as EventType]
  88 + // );
  89 + // },
  90 + // ellipsis: true,
  91 + // },
  92 + // {
  93 + // title: '状态',
  94 + // dataIndex: 'status',
  95 + // width: 100,
  96 + // customRender: (value: Record<'text', number>) => {
  97 + // const { text } = value;
  98 + // return h(
  99 + // Tag,
  100 + // {
  101 + // color: text ? 'green' : 'red',
  102 + // },
  103 + // () => (text ? '已发布' : '待发布')
  104 + // );
  105 + // },
  106 + // },
  107 +];
  108 +
  109 +export const searchFormSchema: FormSchema[] = [
  110 + {
  111 + field: 'dictItemId',
  112 + label: '',
  113 + component: 'ApiSelect',
  114 + colProps: { span: 7 },
  115 + componentProps() {
  116 + return {
  117 + api: findDictItemByCode,
  118 + params: {
  119 + dictCode: DictEnum.CATEGORY_FIELD,
  120 + },
  121 + labelField: 'itemText',
  122 + valueField: 'itemValue',
  123 + placeholder: '请选择领域',
  124 + // getPopupContainer: () => document.body,
  125 + };
  126 + },
  127 + },
  128 + {
  129 + field: 'name',
  130 + label: '',
  131 + component: 'Input',
  132 + colProps: { span: 9 },
  133 + componentProps: {
  134 + placeholder: '请输入品类名称',
  135 + },
  136 + },
  137 +];
... ...
... ... @@ -42,6 +42,12 @@ export enum ModelOfMatterPermission {
42 42 DELETE = 'api:yt:things_model:delete',
43 43 RELEASE = 'api:yt:things_model:release',
44 44 }
  45 +export enum ModelCategoryPermission {
  46 + CREATE = 'api:yt:things_model:category:post',
  47 + UPDATE = 'api:yt:things_model:category:put',
  48 + DELETE = 'api:yt:things_model:category:delete',
  49 + RELEASE = 'api:yt:things_model:category:release',
  50 +}
45 51
46 52 const handleCopy = async (value: string) => {
47 53 const { createMessage } = useMessage();
... ... @@ -212,6 +218,41 @@ export const step1Schemas: FormSchema[] = [
212 218 },
213 219 },
214 220 {
  221 + field: 'category',
  222 + label: '产品品类',
  223 + component: 'RadioGroup',
  224 + defaultValue: 2,
  225 + required: true,
  226 + componentProps({ formModel }) {
  227 + return {
  228 + options: [
  229 + { label: '自定义品类', value: 2 },
  230 + { label: '标准品类', value: 1 },
  231 + ],
  232 + onChange() {
  233 + formModel.categoryId = null;
  234 + formModel.categoryName = undefined;
  235 + },
  236 + };
  237 + },
  238 + },
  239 + {
  240 + field: 'categoryId',
  241 + label: '产品品类',
  242 + component: 'Input',
  243 + defaultValue: undefined,
  244 + ifShow: false,
  245 + },
  246 + {
  247 + field: 'categoryName',
  248 + label: ' ',
  249 + component: 'ApiSelect',
  250 + colProps: { span: 14 },
  251 + slot: 'categoryName',
  252 + required: true,
  253 + ifShow: ({ model }) => model.category === 1,
  254 + },
  255 + {
215 256 field: 'name',
216 257 label: '产品名称',
217 258 required: true,
... ... @@ -325,6 +366,14 @@ export const columns: BasicColumn[] = [
325 366 width: 120,
326 367 },
327 368 {
  369 + title: '产品品类',
  370 + dataIndex: 'categoryName',
  371 + width: 120,
  372 + format: (text) => {
  373 + return text ? text : '自定义品类';
  374 + },
  375 + },
  376 + {
328 377 title: '设备类型',
329 378 dataIndex: 'deviceType',
330 379 width: 90,
... ...
1 1 <template>
2 2 <div class="step1">
3   - <BasicForm @register="register" />
  3 + <BasicForm @register="register">
  4 + <template #categoryName="{ model }">
  5 + <div class="flex">
  6 + <Input
  7 + v-model:value="model.categoryName"
  8 + placeholder="请选择所属品类"
  9 + style="width: 300px"
  10 + :disabled="cateGoryDisabled"
  11 + @focus="handleFocus"
  12 + />
  13 + <Button type="link" :disabled="!model.categoryName" @click="handleCategoryId"
  14 + >查看功能</Button
  15 + >
  16 + </div>
  17 + </template>
  18 + </BasicForm>
  19 + <CategoryList
  20 + @register="registerDrawer"
  21 + @handleOpenListDrawer="handleOpenListDrawer"
  22 + @handleSelectCategory="handleSelectCategory"
  23 + @handleClose="handleClose"
  24 + />
  25 + <CategoryListDrawer @register="registerListDrawer" :cateforyListInfo="cateforyListInfo" />
4 26 </div>
5 27 </template>
6 28 <script lang="ts" setup>
7   - import { nextTick } from 'vue';
  29 + import { nextTick, ref } from 'vue';
8 30 import { BasicForm, useForm } from '/@/components/Form';
9 31 import { step1Schemas } from '../device.profile.data';
10 32 import { FileItem } from '/@/components/Form/src/components/ApiUpload.vue';
11 33 import { buildUUID } from '/@/utils/uuid';
  34 + import { Input, Button } from 'ant-design-vue';
  35 + import { useDrawer } from '/@/components/Drawer';
  36 + import CategoryList from '../components/CategoryList.vue';
  37 + import CategoryListDrawer from '../components/CategoryListDrawer.vue';
12 38
13 39 const emits = defineEmits(['next', 'emitDeviceType']);
14 40 const props = defineProps({
15 41 ifShowBtn: { type: Boolean, default: true },
16 42 });
  43 + const [registerDrawer, { openDrawer, closeDrawer }] = useDrawer();
  44 + const [registerListDrawer, { openDrawer: openListDrawer, closeDrawer: closeListDrawer }] =
  45 + useDrawer();
17 46
18 47 const [register, { validate, setFieldsValue, resetFields, updateSchema, getFieldsValue }] =
19 48 useForm({
... ... @@ -29,6 +58,9 @@
29 58 },
30 59 submitFunc: customSubmitFunc,
31 60 });
  61 +
  62 + const cateGoryDisabled = ref<boolean>(false);
  63 +
32 64 const editOrAddNameStatus = (nameStatus) =>
33 65 updateSchema({
34 66 field: 'name',
... ... @@ -48,19 +80,20 @@
48 80 emits('emitDeviceType', values?.deviceType);
49 81 }
50 82 //回显数据
51   - const setFormData = (v) => {
  83 + const setFormData = async (v) => {
52 84 if (v.image) {
53 85 setFieldsValue({
54 86 image: [{ uid: buildUUID(), name: 'name', url: v.image } as FileItem],
55 87 });
56 88 }
57 89 const { image, ...params } = v;
58   - console.log(image);
59   - setFieldsValue({ ...params });
  90 + console.log(image, props.isUpdate);
  91 + setFieldsValue({ ...params, category: params?.categoryId ? 1 : 2 });
60 92 };
61 93 //获取数据
62 94 async function getFormData() {
63   - const values = await validate();
  95 + await validate();
  96 + const values = getFieldsValue();
64 97 if (!values) return;
65 98 if (Reflect.has(values, 'image')) {
66 99 const file = (values.image || []).at(0) || {};
... ... @@ -74,12 +107,65 @@
74 107 };
75 108
76 109 const editOrAddDeviceTypeStatus = (status: boolean) => {
77   - updateSchema({
78   - field: 'deviceType',
79   - componentProps: {
80   - disabled: status,
  110 + updateSchema([
  111 + {
  112 + field: 'deviceType',
  113 + componentProps: {
  114 + disabled: status,
  115 + },
81 116 },
82   - });
  117 + {
  118 + field: 'category',
  119 + componentProps({ formModel }) {
  120 + return {
  121 + options: [
  122 + { label: '自定义品类', value: 2 },
  123 + { label: '标准品类', value: 1 },
  124 + ],
  125 + onChange() {
  126 + formModel.categoryId = null;
  127 + formModel.categoryName = undefined;
  128 + },
  129 + disabled: status,
  130 + };
  131 + },
  132 + },
  133 + ]);
  134 +
  135 + cateGoryDisabled.value = status;
  136 + };
  137 +
  138 + // 查看功能
  139 + const handleCategoryId = () => {
  140 + const { categoryId } = getFieldsValue() || {};
  141 + if (!categoryId) return;
  142 + cateforyListInfo.value = { record: { id: categoryId }, isRight: false };
  143 + openListDrawer(true);
  144 + };
  145 +
  146 + // 获取焦点
  147 + const handleFocus = () => {
  148 + const { categoryId } = getFieldsValue() || {};
  149 + openDrawer(true, { categoryId });
  150 + };
  151 +
  152 + //打开品类名称
  153 + const cateforyListInfo = ref<any>({});
  154 + const handleOpenListDrawer = (record?: any) => {
  155 + cateforyListInfo.value = { record, isRight: true };
  156 + openListDrawer(true);
  157 + };
  158 +
  159 + // 选择产品品类
  160 + const handleSelectCategory = (item) => {
  161 + closeDrawer();
  162 + closeListDrawer();
  163 + item.id && setFieldsValue({ categoryId: item.id, categoryName: item?.name });
  164 + };
  165 +
  166 + // 关闭抽屉
  167 + const handleClose = () => {
  168 + closeListDrawer();
83 169 };
84 170
85 171 defineExpose({
... ...
... ... @@ -27,25 +27,33 @@
27 27 </div>
28 28 <div class="flex justify-between items-end">
29 29 <div class="flex gap-2">
30   - <Authority :value="ModelOfMatterPermission.CREATE">
  30 + <Authority :value="[ModelOfMatterPermission.CREATE, ModelCategoryPermission.CREATE]">
31 31 <Button v-if="isShowBtn" type="primary" @click="handleCreateOrEdit()">
32 32 新增物模型
33 33 </Button>
34 34 </Authority>
35   - <Button type="primary" @click="handleOpenTsl"> 物模型TSL </Button>
36   - <Authority value="api:yt:things_model:import">
37   - <Upload
  35 + <Button v-if="!record.ifShowClass" type="primary" @click="handleOpenTsl">
  36 + 物模型TSL</Button
  37 + >
  38 + <Button v-else type="primary" @click="handleExport" :loading="loading"
  39 + >导出物模型</Button
  40 + >
  41 + <Authority
  42 + :value="['api:yt:things_model:import', 'api:yt:things_model:category:import']"
  43 + >
  44 + <!-- <Upload
38 45 v-if="isShowBtn"
39 46 accept=".json,"
40 47 :show-upload-list="false"
41 48 :customRequest="handleImportModel"
42 49 >
43 50 <Button type="primary" :loading="importLoading"> 导入物模型 </Button>
44   - </Upload>
  51 + </Upload> -->
  52 + <Button type="primary" @click="handleSelectImport">导入物模型</Button>
45 53 </Authority>
46 54 </div>
47 55 <div class="flex gap-2">
48   - <Authority :value="ModelOfMatterPermission.RELEASE">
  56 + <Authority :value="[ModelOfMatterPermission.RELEASE]">
49 57 <Popconfirm
50 58 title="是否需要发布上线?"
51 59 ok-text="确定"
... ... @@ -61,7 +69,7 @@
61 69 <Button v-if="isShowBtn" class="!bg-gray-200" type="text" @click="handleReturn">
62 70 返回
63 71 </Button>
64   - <Authority :value="ModelOfMatterPermission.DELETE">
  72 + <Authority :value="[ModelOfMatterPermission.DELETE, ModelCategoryPermission.DELETE]">
65 73 <Popconfirm
66 74 title="您确定要批量删除数据"
67 75 ok-text="确定"
... ... @@ -94,14 +102,14 @@
94 102 {
95 103 label: '编辑',
96 104 icon: 'clarity:note-edit-line',
97   - auth: ModelOfMatterPermission.UPDATE,
  105 + auth: [ModelOfMatterPermission.UPDATE, ModelCategoryPermission.UPDATE],
98 106 onClick: handleCreateOrEdit.bind(null, record),
99 107 ifShow: !isShowBtn ? false : true,
100 108 },
101 109 {
102 110 label: '删除',
103 111 icon: 'ant-design:delete-outlined',
104   - auth: ModelOfMatterPermission.DELETE,
  112 + auth: [ModelOfMatterPermission.DELETE, ModelCategoryPermission.DELETE],
105 113 color: 'error',
106 114 ifShow: !isShowBtn ? false : true,
107 115 popConfirm: {
... ... @@ -119,6 +127,11 @@
119 127 @success="handleSuccess"
120 128 />
121 129 <PhysicalModelTsl :record="$props.record" @register="registerModalTsl" />
  130 + <SelectImport
  131 + :record="$props.record"
  132 + @register="registerModalSelect"
  133 + @handleReload="handleReload"
  134 + />
122 135 </div>
123 136 </template>
124 137 <script lang="ts" setup>
... ... @@ -127,25 +140,32 @@
127 140 import {
128 141 modelOfMatterForm,
129 142 ModelOfMatterPermission,
  143 + ModelCategoryPermission,
130 144 physicalColumn,
131 145 } from '../device.profile.data';
132 146 import { useBatchDelete } from '/@/hooks/web/useBatchDelete';
133 147 import { Authority } from '/@/components/Authority';
134 148 import PhysicalModelModal from './cpns/physical/PhysicalModelModal.vue';
135 149 import PhysicalModelTsl from './cpns/physical/PhysicalModelTsl.vue';
136   - import { Popconfirm, Button, Alert, Upload } from 'ant-design-vue';
  150 + import { Popconfirm, Button, Alert } from 'ant-design-vue';
137 151 import { useMessage } from '/@/hooks/web/useMessage';
138 152 import { DeviceRecord } from '/@/api/device/model/deviceModel';
139 153 import {
140 154 deleteModel,
  155 + deleteModelCategory,
141 156 getModelList,
142   - importModelOfMatter,
143 157 releaseModel,
144 158 } from '/@/api/device/modelOfMatter';
145 159 import { OpenModelOfMatterModelParams, OpenModelMode } from './cpns/physical/types';
146 160 import { ModelOfMatterParams } from '/@/api/device/model/modelOfMatterModel';
147   - import { ref } from 'vue';
148   - import { isObject, isString } from '/@/utils/is';
  161 + import { ref, unref } from 'vue';
  162 + import { isObject } from '/@/utils/is';
  163 + import { useRole } from '/@/hooks/business/useRole';
  164 + import { ExportModelCategory } from '/@/api/device/modelOfMatter';
  165 + import { FunctionType } from './cpns/physical/cpns/config';
  166 + import SelectImport from '../components/SelectImport.vue';
  167 +
  168 + const { isPlatformAdmin, isSysadmin } = useRole();
149 169 defineEmits(['register']);
150 170
151 171 const props = defineProps<{
... ... @@ -156,10 +176,15 @@
156 176 const isShowBtn = ref(false);
157 177 const [registerModal, { openModal }] = useModal();
158 178 const [registerModalTsl, { openModal: openModalTsl }] = useModal();
  179 + const [registerModalSelect, { openModal: openModalSelect }] = useModal();
159 180
160 181 const [registerTable, { reload, setProps }] = useTable({
161 182 api: async (params: Record<'page' | 'pageSize', number>) => {
162   - return await getModelList({ ...params, deviceProfileId: props.record.id });
  183 + return await getModelList({
  184 + ...params,
  185 + id: props.record.id,
  186 + selectType: props.record.ifShowClass ? 'category' : undefined,
  187 + });
163 188 },
164 189 columns: physicalColumn,
165 190 showIndexColumn: false,
... ... @@ -184,9 +209,10 @@
184 209 const handleSuccess = () => {
185 210 reload();
186 211 };
187   -
188 212 const { hasBatchDelete, handleDeleteOrBatchDelete, selectionOptions } = useBatchDelete(
189   - deleteModel,
  213 + props.record.ifShowClass && (unref(isPlatformAdmin) || unref(isSysadmin))
  214 + ? deleteModelCategory
  215 + : deleteModel,
190 216 handleSuccess,
191 217 setProps
192 218 );
... ... @@ -242,59 +268,76 @@
242 268 }
243 269 };
244 270
245   - const paseJSON = (string: string) => {
246   - let data = null;
247   - let flag = false;
248   - try {
249   - if (!isString(string)) return { flag: false, data };
250   - data = JSON.parse(string);
251   - flag = true;
252   - if (!isObject(data)) flag = false;
253   - } catch (error) {}
254   - return { flag, data };
255   - };
256   -
257 271 const isEmptyObject = (value: any) => isObject(value) && !Object.keys(value).length;
258 272
259   - const importLoading = ref(false);
260   - const handleImportModel = async (data: { file: File }) => {
261   - const fileReader = new FileReader();
262   -
263   - fileReader.onload = async () => {
264   - const { flag, data } = paseJSON(fileReader.result as string);
265   - if (!flag) {
266   - createMessage.warning('JSON解析失败,请导入正确的JSON~');
267   - return;
268   - }
269   - try {
270   - importLoading.value = true;
  273 + // 选择导入物模型的方式
  274 + const handleSelectImport = () => {
  275 + openModalSelect(true, {
  276 + id: props.record.id,
  277 + isCateGory: (unref(isPlatformAdmin) || unref(isSysadmin)) && props.record.ifShowClass,
  278 + });
  279 + };
271 280
272   - Object.keys(data || {}).forEach((key) => {
273   - const value = (data || {})[key];
274   - if (value && isEmptyObject(value)) {
275   - (data || {})[key] = [];
276   - }
277   - });
  281 + const handleReload = () => {
  282 + reload();
  283 + };
278 284
279   - const result = await importModelOfMatter({
280   - tkDeviceProfileId: props.record.id,
281   - data: data!,
282   - functionType: 'all',
283   - });
  285 + // 导出物模型
  286 + const loading = ref<boolean>(false);
284 287
285   - result
286   - ? createMessage.success('导入成功~')
287   - : createMessage.error('JSON解析失败,请导入正确的JSON~');
  288 + const getAllCategory = () => {
  289 + const { id: deviceProfileId } = props.record;
  290 + return Promise.all([
  291 + ExportModelCategory({
  292 + deviceProfileId,
  293 + functionType: FunctionType.EVENTS,
  294 + }),
  295 + ExportModelCategory({
  296 + deviceProfileId,
  297 + functionType: FunctionType.PROPERTIES,
  298 + }),
  299 + ExportModelCategory({
  300 + deviceProfileId,
  301 + functionType: FunctionType.SERVICE,
  302 + }),
  303 + ]);
  304 + };
  305 + const exportJSONFile = (value: Recordable) => {
  306 + const blob = new Blob([JSON.stringify(value, null, 2)], { type: 'text/json' });
  307 + const objectURL = URL.createObjectURL(blob);
  308 + const element = document.createElement('a');
  309 + element.href = objectURL;
  310 + element.download = `${props.record.name}-model.json`;
  311 + element.style.display = 'none';
  312 + document.body.appendChild(element);
  313 + element.click();
  314 + element.remove();
  315 + URL.revokeObjectURL(objectURL);
  316 + };
288 317
289   - result && reload();
290   - } catch (error) {
291   - throw error;
292   - } finally {
293   - importLoading.value = false;
294   - }
295   - };
  318 + const isEmptyStr = (s: any) => {
  319 + if (s == undefined || s == null || s == '') {
  320 + return true;
  321 + }
  322 + return false;
  323 + };
296 324
297   - fileReader.readAsText(data.file, 'utf-8');
  325 + //产品品类物模型导出
  326 + const handleExport = async () => {
  327 + loading.value = true;
  328 + try {
  329 + const [events, properties, services] = await getAllCategory();
  330 + const value = {
  331 + properties: isEmptyObject(properties) || isEmptyStr(properties) ? [] : properties,
  332 + services: isEmptyObject(services) || isEmptyStr(services) ? [] : services,
  333 + events: isEmptyObject(events) || isEmptyStr(events) ? [] : events,
  334 + };
  335 + exportJSONFile(value);
  336 + } catch (error) {
  337 + throw error;
  338 + } finally {
  339 + loading.value = false;
  340 + }
298 341 };
299 342
300 343 defineExpose({});
... ...
... ... @@ -60,11 +60,19 @@
60 60 import Service from './cpns/Service.vue';
61 61 import Events from './cpns/Events.vue';
62 62 import { ModelOfMatterParams } from '/@/api/device/model/modelOfMatterModel';
63   - import { createModel, updateModel } from '/@/api/device/modelOfMatter';
  63 + import {
  64 + createModel,
  65 + updateModel,
  66 + createModelCategory,
  67 + updateModelCategory,
  68 + } from '/@/api/device/modelOfMatter';
64 69 import { DeviceRecord, DeviceTypeEnum } from '/@/api/device/model/deviceModel';
65 70 import { useMessage } from '/@/hooks/web/useMessage';
66 71 import { OpenModelMode, OpenModelOfMatterModelParams } from './types/index';
67 72 import { FunctionType } from './cpns/config';
  73 + import { useRole } from '/@/hooks/business/useRole';
  74 +
  75 + const { isPlatformAdmin, isSysadmin } = useRole();
68 76
69 77 const emit = defineEmits(['register', 'success']);
70 78
... ... @@ -158,14 +166,28 @@
158 166 params = (await EventsRef.value?.getFormData()) || {};
159 167 }
160 168 params.deviceProfileId = props.record.id;
  169 +
161 170 if (unref(openModalMode) === OpenModelMode.CREATE) {
162   - await createModel(params);
  171 + (unref(isSysadmin) || unref(isPlatformAdmin)) && props.record.ifShowClass
  172 + ? await createModelCategory({
  173 + ...params,
  174 + deviceProfileId: undefined,
  175 + categoryId: props.record.id,
  176 + })
  177 + : await createModel(params);
163 178 createMessage.success('创建成功');
164 179 } else {
165 180 params.id = unref(openModalParams)?.record.id;
166   - await updateModel(params);
  181 + (unref(isSysadmin) || unref(isPlatformAdmin)) && props.record.ifShowClass
  182 + ? await updateModelCategory({
  183 + ...params,
  184 + deviceProfileId: undefined,
  185 + categoryId: props.record.id,
  186 + })
  187 + : await updateModel(params);
167 188 createMessage.success('修改成功');
168 189 }
  190 +
169 191 closeModal();
170 192 emit('success');
171 193 } catch (error) {
... ...
... ... @@ -73,9 +73,18 @@
73 73 const getAllModel = () => {
74 74 const { id: deviceProfileId } = props.record;
75 75 return Promise.all([
76   - getModelTsl({ deviceProfileId, functionType: FunctionType.EVENTS }),
77   - getModelTsl({ deviceProfileId, functionType: FunctionType.PROPERTIES }),
78   - getModelTsl({ deviceProfileId, functionType: FunctionType.SERVICE }),
  76 + getModelTsl({
  77 + deviceProfileId,
  78 + functionType: FunctionType.EVENTS,
  79 + }),
  80 + getModelTsl({
  81 + deviceProfileId,
  82 + functionType: FunctionType.PROPERTIES,
  83 + }),
  84 + getModelTsl({
  85 + deviceProfileId,
  86 + functionType: FunctionType.SERVICE,
  87 + }),
79 88 ]);
80 89 };
81 90
... ...
... ... @@ -84,7 +84,10 @@
84 84 const handleSwitchTsl = async (functionType: FunctionType) => {
85 85 try {
86 86 loading.value = true;
87   - const record = await getModelTsl({ deviceProfileId: props.record.id, functionType });
  87 + const record = await getModelTsl({
  88 + deviceProfileId: props.record.id,
  89 + functionType,
  90 + });
88 91 jsonValue.value = JSON.stringify(
89 92 {
90 93 [functionType]: record,
... ...
1 1 import { FormSchema } from '/@/components/Table';
2 2 import { findDictItemByCode } from '/@/api/system/dict';
  3 +import { DataTypeEnum } from '/@/enums/objectModelEnum';
3 4
4 5 export enum FormField {
5 6 FUNCTION_NAME = 'functionName',
... ... @@ -40,30 +41,19 @@ export enum AssessMode {
40 41 WRITE = 'w',
41 42 }
42 43
43   -/**
44   - * 新增参数 动态显示表单
45   - */
46   -export enum DataTypeEnum {
47   - IS_NUMBER_INT = 'INT',
48   - IS_NUMBER_DOUBLE = 'DOUBLE',
49   - IS_STRING = 'TEXT',
50   - IS_STRUCT = 'STRUCT',
51   - IS_BOOL = 'BOOL',
52   -}
53   -
54 44 const isNumber = (type: string) => {
55   - return type === DataTypeEnum.IS_NUMBER_INT || type === DataTypeEnum.IS_NUMBER_DOUBLE;
  45 + return type === DataTypeEnum.NUMBER_INT || type === DataTypeEnum.NUMBER_DOUBLE;
56 46 };
57 47
58 48 const isString = (type: string) => {
59   - return type === DataTypeEnum.IS_STRING;
  49 + return type === DataTypeEnum.STRING;
60 50 };
61 51 const isStruct = (type: string) => {
62   - return type === DataTypeEnum.IS_STRUCT;
  52 + return type === DataTypeEnum.STRUCT;
63 53 };
64 54
65 55 const isBool = (type: string) => {
66   - return type === DataTypeEnum.IS_BOOL;
  56 + return type === DataTypeEnum.BOOL;
67 57 };
68 58
69 59 export const serviceSchemas = (tcpDeviceFlag: boolean): FormSchema[] => {
... ...
... ... @@ -138,13 +138,90 @@
138 138 });
139 139 }
140 140
  141 + const cipherLength = 7;
  142 +
  143 + //字符串星号替代
  144 + function parseStringToStar(originalStr: string, startLength: number, endLength: number) {
  145 + //字符串大于7位,前3后4明文,其他以星号替代
  146 + //字符串小于7位,前1后1明文,其他以星号替代
  147 + return originalStr?.length > startLength
  148 + ? originalStr?.substr(0, startLength) +
  149 + new Array(originalStr?.length - endLength)?.join('*') +
  150 + originalStr?.substr(-endLength)
  151 + : originalStr;
  152 + }
  153 +
  154 + //长度 都大于或者小于7或者其中一个大于7,一个小于7个字符的处理
  155 + //二维数组优化if else if else
  156 + const mapConditionConfig = [
  157 + [
  158 + (startParam, endParam) =>
  159 + String(startParam)?.length > cipherLength && String(endParam)?.length > cipherLength,
  160 + (startParam) => parseStringToStar(startParam, 3, 4),
  161 + (endParam) => parseStringToStar(endParam, 3, 4),
  162 + ],
  163 + [
  164 + (startParam, endParam) =>
  165 + String(startParam)?.length < cipherLength && String(endParam)?.length < cipherLength,
  166 + (startParam) => parseStringToStar(startParam, 1, 1),
  167 + (endParam) => parseStringToStar(endParam, 1, 1),
  168 + ],
  169 + [
  170 + (startParam, endParam) =>
  171 + String(startParam)?.length > cipherLength || String(endParam)?.length < cipherLength,
  172 + (startParam) => parseStringToStar(startParam, 3, 4),
  173 + (endParam) => parseStringToStar(endParam, 1, 1),
  174 + ],
  175 + [
  176 + (startParam, endParam) =>
  177 + String(startParam)?.length < cipherLength || String(endParam)?.length > cipherLength,
  178 + (startParam) => parseStringToStar(startParam, 1, 1),
  179 + (endParam) => parseStringToStar(endParam, 3, 4),
  180 + ],
  181 + ];
  182 +
  183 + const handleStandardLength = (
  184 + startKey: string,
  185 + endKey: string,
  186 + startParam: string,
  187 + endParam: string
  188 + ) => {
  189 + const findDateFlow: any = mapConditionConfig.find((item: Function[]) =>
  190 + item[0](startParam, endParam)
  191 + );
  192 + if (!findDateFlow) return;
  193 + const cipherData: Recordable = {};
  194 + cipherData[startKey] = findDateFlow[1](startParam);
  195 + cipherData[endKey] = findDateFlow[2](endParam);
  196 + return cipherData;
  197 + };
  198 +
  199 + const handleCipherTextConfig = (e) => {
  200 + /**
  201 + * username和password 存在则是邮件配置
  202 + * secretId和secretKey 存在则是腾讯云配置信息
  203 + * accessKeyId和accessKeySecret 存在则是阿里云配置信息
  204 + */
  205 + const { username, password, secretId, secretKey, accessKeyId, accessKeySecret } = e;
  206 + return JSON.parse(
  207 + JSON.stringify({
  208 + ...e,
  209 + ...handleStandardLength('username', 'password', username, password),
  210 + ...handleStandardLength('secretId', 'secretKey', secretId, secretKey),
  211 + ...handleStandardLength('accessKeyId', 'accessKeySecret', accessKeyId, accessKeySecret),
  212 + })
  213 + );
  214 + };
  215 +
141 216 function showData(record: Recordable) {
142 217 Modal.info({
143 218 title: '当前配置',
144 219 width: 600,
145 220 centered: true,
146 221 maskClosable: true,
147   - content: h(JsonPreview, { data: JSON.parse(JSON.stringify(record.config)) }),
  222 + content: h(JsonPreview, {
  223 + data: handleCipherTextConfig(record.config),
  224 + }),
148 225 });
149 226 }
150 227 const statusChange = async (checked, record) => {
... ...
... ... @@ -184,10 +184,12 @@
184 184 businessText.value === BusinessReportConfigTextEnum.BUSINESS_ADD_TEXT
185 185 ? await createOrEditReportManage(data)
186 186 : putReportConfigManage({ ...restData.data, ...data });
187   - emits('success');
188 187 createMessage.success(`${businessText.value}成功`);
189 188 closeDrawer();
190 189 handleClose();
  190 + setTimeout(() => {
  191 + emits('success');
  192 + }, 500);
191 193 } finally {
192 194 setDrawerProps({ confirmLoading: false });
193 195 }
... ...
... ... @@ -471,7 +471,6 @@ export const formSchema: BFormSchema[] = [
471 471 dynamicRules: () => {
472 472 return [
473 473 {
474   - // required: model[SchemaFiled.AGG] !== AggregateDataEnum.NONE,
475 474 required: true,
476 475 message: '间隔时间为必填项',
477 476 type: 'number',
... ... @@ -479,7 +478,11 @@ export const formSchema: BFormSchema[] = [
479 478 ];
480 479 },
481 480 ifShow({ values }) {
482   - return values[SchemaFiled.WAY] === QueryWay.TIME_PERIOD && exectueIsImmed(values.executeWay);
  481 + return (
  482 + values[SchemaFiled.WAY] === QueryWay.TIME_PERIOD &&
  483 + exectueIsImmed(values.executeWay) &&
  484 + values[SchemaFiled.DATA_TYPE] !== DataTypeEnum.ORIGINAL
  485 + );
483 486 },
484 487 componentProps({ formModel, formActionType }) {
485 488 const options =
... ...
... ... @@ -166,6 +166,8 @@ export const useHooks = () => {
166 166 dataRange: [value?.startTs, value?.endTs],
167 167 dateGroupGap: value?.interval,
168 168 };
  169 + Reflect.deleteProperty(value, 'startTs');
  170 + Reflect.deleteProperty(value, 'interval');
169 171 }
170 172 const spanDisance = executeContent?.split(' ');
171 173 const cronTime =
... ...
... ... @@ -34,7 +34,12 @@
34 34 @change="(value) => handleChangeChars(value, item.device, item)"
35 35 v-bind="createPickerSearch()"
36 36 placeholder="请选择设备属性"
37   - :options="item?.attributes?.map((item1) => ({ label: item1, value: item1 }))"
  37 + :options="
  38 + item?.attributes?.map((attrItem: any) => ({
  39 + label: attrItem.label,
  40 + value: attrItem.value,
  41 + }))
  42 + "
38 43 />
39 44 </div>
40 45 <div class="w-full h-full flex justify-center items-center">
... ... @@ -64,6 +69,8 @@
64 69 import { ExecuteReportRecord } from '/@/api/export/model/exportModel';
65 70 import { Select, Spin, Empty } from 'ant-design-vue';
66 71 import { createPickerSearch } from '/@/utils/pickerSearch';
  72 + import { getDeviceDetail } from '/@/api/device/deviceManager';
  73 + import { getAttribute } from '/@/api/ruleengine/ruleengineApi';
67 74
68 75 interface ResponsData {
69 76 attr: string;
... ... @@ -141,20 +148,66 @@
141 148 return chartOption;
142 149 };
143 150
  151 + const handleDeviceProfileAttributes = async (entityId: string) => {
  152 + const deviceDetailRes = await getDeviceDetail(entityId);
  153 + const { deviceProfileId } = deviceDetailRes;
  154 + if (!deviceProfileId) return;
  155 + const attributeRes = await getAttribute(deviceProfileId);
  156 + return handleDataFormat(deviceDetailRes, attributeRes);
  157 + };
  158 +
  159 + const handleDataFormat = (deviceDetail: any, attributes: any) => {
  160 + const { name, tbDeviceId } = deviceDetail;
  161 + const attribute = attributes.map((item: any) => ({
  162 + identifier: item.identifier,
  163 + name: item.name,
  164 + detail: item.detail,
  165 + }));
  166 + return {
  167 + name,
  168 + tbDeviceId,
  169 + attribute,
  170 + };
  171 + };
  172 +
144 173 const [register, { setModalProps }] = useModalInner(
145 174 async (data: { record: ExecuteReportRecord }) => {
146 175 setModalProps({ loading: true });
147 176 try {
148 177 currentRecord = data.record;
149 178 const deviceInfo = data.record.executeCondition.executeAttributes || [];
150   - chartInstance.value = deviceInfo.map((item) => ({
151   - ...item,
152   - active: item.attributes.at(0),
153   - }));
  179 + let attributesList: Recordable[] = [];
  180 + // 处理为物模型里的标识符名
  181 + const reflectDeviceList = deviceInfo.map(async (item) => {
  182 + const { device, attributes } = item as Recordable;
  183 + if (!device) return;
  184 + const thingsModel = (await handleDeviceProfileAttributes(item.device)) as Recordable;
  185 + const { tbDeviceId, attribute } = thingsModel as Recordable;
  186 + if (!tbDeviceId) return;
  187 + if (device === tbDeviceId) {
  188 + attributesList = attributes.reduce((acc, curr) => {
  189 + attribute.forEach((item: Recordable) => {
  190 + if (item.identifier === curr) {
  191 + acc.push({
  192 + label: item.name,
  193 + value: item.identifier,
  194 + });
  195 + }
  196 + });
  197 + return [...acc];
  198 + }, []);
  199 + }
  200 + return {
  201 + ...item,
  202 + active: attributes.at(0),
  203 + attributes: attributesList,
  204 + };
  205 + });
  206 + chartInstance.value = (await Promise.all(reflectDeviceList)) as any as ChartInstance[];
154 207 for (const item of unref(chartInstance)) {
155 208 const { attributes, device } = item;
156 209
157   - const keys = attributes.length ? attributes.at(0) : '';
  210 + const keys = attributes.length ? (attributes as any[]).at(0)?.value : '';
158 211
159 212 const sendParams = {
160 213 ...data.record.executeCondition.executeCondition,
... ...
1 1 <script lang="ts" setup>
2   - import { Button, Tabs, Tag } from 'ant-design-vue';
  2 + import { Button, Tabs, Select } from 'ant-design-vue';
3 3 import { remove, uniqBy, cloneDeep } from 'lodash';
4   - import { computed, nextTick, onMounted, ref, unref, toRaw } from 'vue';
  4 + import { nextTick, onMounted, ref, unref, toRaw, watch } from 'vue';
5 5 import {
6 6 deviceTableColumn,
7 7 deviceTableFormSchema,
... ... @@ -21,8 +21,7 @@
21 21
22 22 const props = withDefaults(
23 23 defineProps<{
24   - getPendingTableParams: (params: Recordable) => any;
25   - getSelectedTableParams: (params: Recordable) => any;
  24 + params?: Recordable;
26 25 value?: (Recordable & DeviceModel)[];
27 26 maxTagLength?: number;
28 27 openModalValidate?: () => boolean;
... ... @@ -34,6 +33,7 @@
34 33 value: () => [],
35 34 maxTagLength: 2,
36 35 primaryKey: 'tbDeviceId',
  36 + params: () => ({}),
37 37 }
38 38 );
39 39
... ... @@ -56,23 +56,6 @@
56 56
57 57 const selectedConfirmQueue = ref<DeviceModel[]>([]);
58 58
59   - const getShowTagOptions = computed(() => {
60   - const { maxTagLength } = props;
61   - return unref(selectedTotalList).slice(0, maxTagLength);
62   - });
63   -
64   - const getSurplusOptionsLength = computed(() => {
65   - const { maxTagLength } = props;
66   - const surplusValue = unref(selectedTotalList).length - maxTagLength;
67   - return surplusValue < 0 ? 0 : surplusValue;
68   - });
69   -
70   - // const pendingListCount = computed(() => {
71   - // const { value } = props;
72   - // const selectedList = unref(pendingTotalList).filter((item) => value.includes(item.id));
73   - // return unref(pendingTotalList).length - selectedList.length;
74   - // });
75   -
76 59 const [registerModal, { openModal }] = useModal();
77 60
78 61 const [regsterPendingTable, pendingTableActionType] = useTable({
... ... @@ -82,9 +65,8 @@
82 65 immediate: false,
83 66 clickToRowSelect: false,
84 67 beforeFetch: (params) => {
85   - const { getPendingTableParams } = props;
86   - const data = getPendingTableParams?.(params) || {};
87   - Object.assign(params, { ...data, selected: false });
  68 + const { params: otherParams } = props;
  69 + Object.assign(params, { ...otherParams, selected: false });
88 70 return params;
89 71 },
90 72 afterFetch: (list: DeviceModel[]) => {
... ... @@ -150,9 +132,8 @@
150 132 dataSource: selectedTotalList,
151 133 clickToRowSelect: false,
152 134 beforeFetch: (params) => {
153   - const { getSelectedTableParams } = props;
154   - const data = getSelectedTableParams?.(params) || {};
155   - Object.assign(params, { ...data, selected: false });
  135 + const { params: otherParams } = props;
  136 + Object.assign(params, { ...otherParams, selected: false });
156 137 return params;
157 138 },
158 139 rowSelection: {
... ... @@ -219,18 +200,31 @@
219 200
220 201 if (openModalValidate && isFunction(openModalValidate) && !openModalValidate()) return;
221 202
  203 + activeKey.value = Active.PENDING;
222 204 openModal(true);
223 205 await nextTick();
224 206 pendingTableActionType.reload();
225 207 };
226 208
  209 + watch(
  210 + () => props.params,
  211 + () => {
  212 + selectedTotalList.value = [];
  213 + selectedConfirmQueue.value = [];
  214 + pendingConfirmQueue.value = [];
  215 + try {
  216 + pendingTableActionType.clearSelectedRowKeys();
  217 + selectedTableActionType.clearSelectedRowKeys();
  218 + } catch (error) {}
  219 + }
  220 + );
  221 +
227 222 onMounted(async () => {
228   - const { getSelectedTableParams } = props;
229   - const data = getSelectedTableParams?.({}) || {};
230   - if (!data?.convertConfigId || !data?.deviceProfileIds) {
  223 + const { params } = props;
  224 + if (!params?.convertConfigId || !params?.deviceProfileIds) {
231 225 return;
232 226 }
233   - const { items } = await devicePage({ page: 1, pageSize: 10, ...data, selected: true });
  227 + const { items } = await devicePage({ page: 1, pageSize: 10, ...params, selected: true });
234 228 selectedTotalList.value = items;
235 229 });
236 230 </script>
... ... @@ -239,7 +233,7 @@
239 233 <section>
240 234 <BasicModal
241 235 @register="registerModal"
242   - title="穿梭表格"
  236 + title="设备选择"
243 237 width="60%"
244 238 :wrapClassName="prefixCls"
245 239 :showOkBtn="false"
... ... @@ -251,13 +245,11 @@
251 245 <template #tab>
252 246 <div class="flex items-center justify-center">
253 247 <span>待选设备</span>
254   - <!-- <Badge show-zero :count="pendingListCount" /> -->
255 248 </div>
256 249 </template>
257 250 <BasicTable @register="regsterPendingTable">
258 251 <template #toolbar>
259 252 <section class="flex w-full justify-end items-center">
260   - <!-- <Button type="primary">全选</Button> -->
261 253 <div class="text-blue-400">
262 254 <span class="mr-2">选择设备:</span>
263 255 <span>{{ pendingConfirmQueue.length }}</span>
... ... @@ -279,7 +271,6 @@
279 271 <template #tab>
280 272 <div class="flex items-center justify-center">
281 273 <span>已选设备</span>
282   - <!-- <Badge show-zero :count="selectedTotalList.length" /> -->
283 274 </div>
284 275 </template>
285 276 <BasicTable @register="registerSelectedTable">
... ... @@ -305,23 +296,22 @@
305 296 </Tabs>
306 297 </section>
307 298 </BasicModal>
308   - <Button @click="handleOpenModal" type="link" :disabled="disabled">
309   - <span v-if="!selectedTotalList.length">选择设备</span>
310   - <div v-if="selectedTotalList.length">
311   - <Tag
312   - class="!px-2 !py-1 !bg-gray-50 !border-gray-100"
313   - v-for="item in getShowTagOptions"
314   - :key="item[primaryKey]"
315   - >
316   - <span>
317   - {{ item.alias || item.name }}
318   - </span>
319   - </Tag>
320   - <Tag class="!px-2 !py-1 !bg-gray-50 !border-gray-100" v-if="getSurplusOptionsLength">
321   - <span> +{{ getSurplusOptionsLength }}... </span>
322   - </Tag>
323   - </div>
324   - </Button>
  299 + <Select
  300 + placeholder="请选择设备"
  301 + :disabled="disabled"
  302 + :value="selectedTotalList.map((item) => item[primaryKey])"
  303 + mode="multiple"
  304 + :maxTagCount="3"
  305 + :open="false"
  306 + @dropdownVisibleChange="handleOpenModal"
  307 + :options="
  308 + selectedTotalList.map((item) => ({
  309 + ...item,
  310 + label: item.alias || item.name,
  311 + value: item[primaryKey],
  312 + }))
  313 + "
  314 + />
325 315 </section>
326 316 </template>
327 317
... ...
... ... @@ -73,6 +73,10 @@ export const modeForm = (disabled: boolean): FormSchema[] => {
73 73 labelField: 'name',
74 74 valueField: 'tbProfileId',
75 75 disabled,
  76 + selectProps: {
  77 + disabled,
  78 + placeholder: '请选择产品',
  79 + },
76 80 transferProps: {
77 81 listStyle: { height: '400px' },
78 82 showSearch: true,
... ... @@ -98,26 +102,14 @@ export const modeForm = (disabled: boolean): FormSchema[] => {
98 102 valueField: 'value',
99 103 changeEvent: 'update:value',
100 104 rules: [{ required: true, message: '数据源设备为必选项', type: 'array' }],
101   - componentProps: ({ formActionType }) => {
102   - const { getFieldsValue } = formActionType;
  105 + componentProps: ({ formModel }) => {
  106 + const convertConfigId = formModel[BasicInfoFormField.CONVERT_CONFIG_ID];
  107 + const deviceProfileIds = formModel[BasicInfoFormField.DATA_SOURCE_PRODUCT];
103 108 return {
104 109 disabled,
105   - getPendingTableParams: () => {
106   - const values = getFieldsValue();
107   - const convertConfigId = Reflect.get(values, BasicInfoFormField.CONVERT_CONFIG_ID);
108   - const deviceProfileIds = Reflect.get(values, BasicInfoFormField.DATA_SOURCE_PRODUCT);
109   - return { convertConfigId, deviceProfileIds };
110   - },
111   - getSelectedTableParams: () => {
112   - const values = getFieldsValue();
113   - const convertConfigId = Reflect.get(values, BasicInfoFormField.CONVERT_CONFIG_ID);
114   - const deviceProfileIds = Reflect.get(values, BasicInfoFormField.DATA_SOURCE_PRODUCT);
115   - return { convertConfigId, deviceProfileIds };
116   - },
  110 + params: { convertConfigId, deviceProfileIds },
117 111 transformValue: handleGroupDevice,
118 112 openModalValidate: () => {
119   - const values = getFieldsValue();
120   - const deviceProfileIds = Reflect.get(values, BasicInfoFormField.DATA_SOURCE_PRODUCT);
121 113 if (!deviceProfileIds || !deviceProfileIds?.length) {
122 114 createMessage.warning('请选择数据源设备');
123 115 }
... ...
... ... @@ -9,6 +9,7 @@ export const formSchemas: FormSchema[] = [
9 9 field: CheckExistenceFieldsEnum.MESSAGE_NAMES,
10 10 component: 'Select',
11 11 label: CheckExistenceFieldsNameEnum.MESSAGE_NAMES,
  12 + rules: [{ required: true, type: 'array' }],
12 13 componentProps: {
13 14 mode: 'tags',
14 15 open: false,
... ... @@ -20,6 +21,7 @@ export const formSchemas: FormSchema[] = [
20 21 field: CheckExistenceFieldsEnum.METADATA_NAMES,
21 22 component: 'Select',
22 23 label: CheckExistenceFieldsNameEnum.METADATA_NAMES,
  24 + rules: [{ required: true, type: 'array' }],
23 25 componentProps: {
24 26 mode: 'tags',
25 27 open: false,
... ...
... ... @@ -25,7 +25,7 @@ export const getFormSchemas = (route: RouteLocationNormalizedLoaded): FormSchema
25 25 component: 'ApiSearchSelect',
26 26 componentProps: () => {
27 27 return {
28   - placeholder: '请选择所属产品',
  28 + placeholder: '请选择规则链',
29 29 showSearch: true,
30 30 params: {
31 31 pageSize: 50,
... ...
... ... @@ -62,7 +62,7 @@ export const formSchemas: FormSchema[] = [
62 62 {
63 63 field: FormFieldsEnum.STATUS,
64 64 label: '状态',
65   - component: 'Input',
  65 + component: 'Select',
66 66 componentProps: {
67 67 options: Object.keys(StatusEnum).map((value) => ({ label: StatusNameEnum[value], value })),
68 68 allowClear: true,
... ...