Commit 46c4a487fc9b4ac8f3d33e023d2b72bb02b0ecdb

Authored by fengwotao
2 parents 3653fc06 97e7ffd0

Merge branch 'main_dev' into ft

Showing 40 changed files with 1214 additions and 724 deletions
@@ -12,11 +12,11 @@ export const getDeviceProfile = (deviceType?: string) => { @@ -12,11 +12,11 @@ export const getDeviceProfile = (deviceType?: string) => {
12 }; 12 };
13 13
14 // 获取历史数据 14 // 获取历史数据
15 -export const getDeviceHistoryInfo = (params: Recordable, orderBy) => { 15 +export const getDeviceHistoryInfo = (params: Recordable, orderBy?: string) => {
16 return defHttp.get<HistoryData>( 16 return defHttp.get<HistoryData>(
17 { 17 {
18 url: `/plugins/telemetry/DEVICE/${params.entityId}/values/timeseries`, 18 url: `/plugins/telemetry/DEVICE/${params.entityId}/values/timeseries`,
19 - params: { ...params, entityId: null, orderBy }, 19 + params: { ...params, entityId: null, orderBy: orderBy || 'DESC' },
20 }, 20 },
21 { 21 {
22 joinPrefix: false, 22 joinPrefix: false,
1 -import { defHttp } from '/@/utils/http/axios';  
2 -import {  
3 - TDeviceConfigParams,  
4 - IDeviceConfigAddOrEditModel,  
5 - ProfileRecord,  
6 - RuleChainRecord,  
7 -} from '/@/api/device/model/deviceConfigModel';  
8 -import { PaginationResult } from '/#/axios';  
9 -  
10 -enum EDeviceConfigApi {  
11 - /**  
12 - * 设备配置URL  
13 - */  
14 - DEVICE_CONFIG_GET_PAGE = '/device_profile',  
15 - DEVICE_CONFIG_POST_ADD_OR_EDIT = '/device_profile',  
16 - DEVICE_CONFIG_GET_DETAIL = '/device_profile/',  
17 - DEVICE_CONFIG_DELETE = '/device_profile',  
18 - DEVICE_CONFIG_GET_RULECHAIN = '/rule_chain/me/list',  
19 - ALARM_CONTACT_GET_PAGE = '/alarm_contact',  
20 - DEVICE_CONFIG_EXPORT = '/device_profile/export',  
21 - DEVICE_CONFIG_IMPORT = '/device_profile/import',  
22 - SET_DEVICE_ISDEFAULT = '/deviceProfile',  
23 - FRP_API = '/frp/',  
24 - GET_TB_DEVICE_ID = '/device/get_subset',  
25 - COMMODRECORD = '/rpc',  
26 -}  
27 -  
28 -/**  
29 - * 设备配置详情  
30 - */  
31 -export const deviceConfigGetDetail = (id: string) => {  
32 - return defHttp.get({  
33 - url: `${EDeviceConfigApi.DEVICE_CONFIG_GET_DETAIL}${id}`,  
34 - });  
35 -};  
36 -  
37 -/**  
38 - * 获取规则链  
39 - */  
40 -export const deviceConfigGetRuleChain = () => {  
41 - return defHttp.get<RuleChainRecord[]>({  
42 - url: EDeviceConfigApi.DEVICE_CONFIG_GET_RULECHAIN,  
43 - });  
44 -};  
45 -  
46 -/**  
47 - * 获取告警联系人  
48 - */  
49 -export const alarmContactGetPage = () => {  
50 - return defHttp.get({  
51 - url: `${EDeviceConfigApi.ALARM_CONTACT_GET_PAGE}?page=1&pageSize=10`,  
52 - });  
53 -};  
54 -  
55 -/**  
56 - * 分页查询设备配置页面  
57 - */  
58 -export const deviceConfigGetQuery = (params?: TDeviceConfigParams) => {  
59 - return defHttp.get<PaginationResult<ProfileRecord>>({  
60 - url: EDeviceConfigApi.DEVICE_CONFIG_GET_PAGE,  
61 - params,  
62 - });  
63 -};  
64 -  
65 -/**  
66 - * 新增或者编辑设备配置  
67 -  
68 - */  
69 -export const deviceConfigAddOrEdit = (params: IDeviceConfigAddOrEditModel) => {  
70 - return defHttp.post<IDeviceConfigAddOrEditModel>({  
71 - url: EDeviceConfigApi.DEVICE_CONFIG_POST_ADD_OR_EDIT,  
72 - params,  
73 - });  
74 -};  
75 -  
76 -/**  
77 - * 删除设备配置  
78 - */  
79 -export const deviceConfigDelete = (ids: string[]) => {  
80 - return defHttp.delete({  
81 - url: EDeviceConfigApi.DEVICE_CONFIG_DELETE,  
82 - data: {  
83 - ids,  
84 - },  
85 - });  
86 -};  
87 -  
88 -/**  
89 - * 导出设备配置  
90 - */  
91 -export const deviceConfigExport = (params: IDeviceConfigAddOrEditModel) => {  
92 - return defHttp.post<IDeviceConfigAddOrEditModel>({  
93 - url: EDeviceConfigApi.DEVICE_CONFIG_EXPORT,  
94 - params,  
95 - });  
96 -};  
97 -  
98 -/**  
99 - * 导入设备配置  
100 - */  
101 -export const deviceConfigImport = (params: IDeviceConfigAddOrEditModel) => {  
102 - return defHttp.post<IDeviceConfigAddOrEditModel>({  
103 - url: EDeviceConfigApi.DEVICE_CONFIG_IMPORT,  
104 - params,  
105 - });  
106 -};  
107 -  
108 -/**  
109 - *  
110 - * 设置该设备配置为默认  
111 - */  
112 -export const setDeviceProfileIsDefaultApi = (id: string, v, params?: {}) => {  
113 - return defHttp.post(  
114 - {  
115 - url: EDeviceConfigApi.SET_DEVICE_ISDEFAULT + '/' + id + '/' + v,  
116 - params,  
117 - },  
118 - {  
119 - joinPrefix: false,  
120 - }  
121 - );  
122 -};  
123 -  
124 -/**  
125 - * Frp内网穿透信息API  
126 - */  
127 -export const frpGetInfoApi = (proxyName: string) => {  
128 - return defHttp.get({  
129 - url: `${EDeviceConfigApi.FRP_API}${proxyName}`,  
130 - });  
131 -};  
132 -  
133 -export const frpPutInfoApi = (proxyName: string, enableRemote: number) => {  
134 - return defHttp.put({  
135 - url: `${EDeviceConfigApi.FRP_API}${proxyName}/${enableRemote}`,  
136 - });  
137 -};  
138 -  
139 -export const getTbDeviceId = (params: string) => {  
140 - return defHttp.get({  
141 - url: `${EDeviceConfigApi.GET_TB_DEVICE_ID}/${params}`,  
142 - });  
143 -};  
144 -  
145 -/**  
146 - * 命令下发记录  
147 - */  
148 -export const deviceCommandRecordGetQuery = (params?: TDeviceConfigParams) => {  
149 - return defHttp.get({  
150 - url: EDeviceConfigApi.COMMODRECORD,  
151 - params,  
152 - });  
153 -}; 1 +import { defHttp } from '/@/utils/http/axios';
  2 +import {
  3 + TDeviceConfigParams,
  4 + IDeviceConfigAddOrEditModel,
  5 + ProfileRecord,
  6 + RuleChainRecord,
  7 + DeviceProfileDetail,
  8 +} from '/@/api/device/model/deviceConfigModel';
  9 +import { PaginationResult } from '/#/axios';
  10 +
  11 +enum EDeviceConfigApi {
  12 + /**
  13 + * 设备配置URL
  14 + */
  15 + DEVICE_CONFIG_GET_PAGE = '/device_profile',
  16 + DEVICE_CONFIG_POST_ADD_OR_EDIT = '/device_profile',
  17 + DEVICE_CONFIG_GET_DETAIL = '/device_profile/',
  18 + DEVICE_CONFIG_DELETE = '/device_profile',
  19 + DEVICE_CONFIG_GET_RULECHAIN = '/rule_chain/me/list',
  20 + ALARM_CONTACT_GET_PAGE = '/alarm_contact',
  21 + DEVICE_CONFIG_EXPORT = '/device_profile/export',
  22 + DEVICE_CONFIG_IMPORT = '/device_profile/import',
  23 + SET_DEVICE_ISDEFAULT = '/deviceProfile',
  24 + FRP_API = '/frp/',
  25 + GET_TB_DEVICE_ID = '/device/get_subset',
  26 + COMMODRECORD = '/rpc',
  27 +}
  28 +
  29 +/**
  30 + * 设备配置详情
  31 + */
  32 +export const deviceConfigGetDetail = (id: string) => {
  33 + return defHttp.get<DeviceProfileDetail>({
  34 + url: `${EDeviceConfigApi.DEVICE_CONFIG_GET_DETAIL}${id}`,
  35 + });
  36 +};
  37 +
  38 +/**
  39 + * 获取规则链
  40 + */
  41 +export const deviceConfigGetRuleChain = () => {
  42 + return defHttp.get<RuleChainRecord[]>({
  43 + url: EDeviceConfigApi.DEVICE_CONFIG_GET_RULECHAIN,
  44 + });
  45 +};
  46 +
  47 +/**
  48 + * 获取告警联系人
  49 + */
  50 +export const alarmContactGetPage = () => {
  51 + return defHttp.get({
  52 + url: `${EDeviceConfigApi.ALARM_CONTACT_GET_PAGE}?page=1&pageSize=10`,
  53 + });
  54 +};
  55 +
  56 +/**
  57 + * 分页查询设备配置页面
  58 + */
  59 +export const deviceConfigGetQuery = (params?: TDeviceConfigParams) => {
  60 + return defHttp.get<PaginationResult<ProfileRecord>>({
  61 + url: EDeviceConfigApi.DEVICE_CONFIG_GET_PAGE,
  62 + params,
  63 + });
  64 +};
  65 +
  66 +/**
  67 + * 新增或者编辑设备配置
  68 +
  69 + */
  70 +export const deviceConfigAddOrEdit = (params: IDeviceConfigAddOrEditModel) => {
  71 + return defHttp.post<IDeviceConfigAddOrEditModel>({
  72 + url: EDeviceConfigApi.DEVICE_CONFIG_POST_ADD_OR_EDIT,
  73 + params,
  74 + });
  75 +};
  76 +
  77 +/**
  78 + * 删除设备配置
  79 + */
  80 +export const deviceConfigDelete = (ids: string[]) => {
  81 + return defHttp.delete({
  82 + url: EDeviceConfigApi.DEVICE_CONFIG_DELETE,
  83 + data: {
  84 + ids,
  85 + },
  86 + });
  87 +};
  88 +
  89 +/**
  90 + * 导出设备配置
  91 + */
  92 +export const deviceConfigExport = (params: IDeviceConfigAddOrEditModel) => {
  93 + return defHttp.post<IDeviceConfigAddOrEditModel>({
  94 + url: EDeviceConfigApi.DEVICE_CONFIG_EXPORT,
  95 + params,
  96 + });
  97 +};
  98 +
  99 +/**
  100 + * 导入设备配置
  101 + */
  102 +export const deviceConfigImport = (params: IDeviceConfigAddOrEditModel) => {
  103 + return defHttp.post<IDeviceConfigAddOrEditModel>({
  104 + url: EDeviceConfigApi.DEVICE_CONFIG_IMPORT,
  105 + params,
  106 + });
  107 +};
  108 +
  109 +/**
  110 + *
  111 + * 设置该设备配置为默认
  112 + */
  113 +export const setDeviceProfileIsDefaultApi = (id: string, v, params?: {}) => {
  114 + return defHttp.post(
  115 + {
  116 + url: EDeviceConfigApi.SET_DEVICE_ISDEFAULT + '/' + id + '/' + v,
  117 + params,
  118 + },
  119 + {
  120 + joinPrefix: false,
  121 + }
  122 + );
  123 +};
  124 +
  125 +/**
  126 + * Frp内网穿透信息API
  127 + */
  128 +export const frpGetInfoApi = (proxyName: string) => {
  129 + return defHttp.get({
  130 + url: `${EDeviceConfigApi.FRP_API}${proxyName}`,
  131 + });
  132 +};
  133 +
  134 +export const frpPutInfoApi = (proxyName: string, enableRemote: number) => {
  135 + return defHttp.put({
  136 + url: `${EDeviceConfigApi.FRP_API}${proxyName}/${enableRemote}`,
  137 + });
  138 +};
  139 +
  140 +export const getTbDeviceId = (params: string) => {
  141 + return defHttp.get({
  142 + url: `${EDeviceConfigApi.GET_TB_DEVICE_ID}/${params}`,
  143 + });
  144 +};
  145 +
  146 +/**
  147 + * 命令下发记录
  148 + */
  149 +export const deviceCommandRecordGetQuery = (params?: TDeviceConfigParams) => {
  150 + return defHttp.get({
  151 + url: EDeviceConfigApi.COMMODRECORD,
  152 + params,
  153 + });
  154 +};
1 -import { BasicPageParams } from '/@/api/model/baseModel';  
2 -  
3 -export type TDeviceConfigPageQueryParam = BasicPageParams & TDeviceConfigParams;  
4 -  
5 -export type TDeviceConfigParams = {  
6 - page?: any;  
7 - pageSize?: any;  
8 - name?: string;  
9 - transportType?: string;  
10 - orderFiled?: string;  
11 - orderType?: string;  
12 - tbDeviceId?: string;  
13 -};  
14 -  
15 -export interface IDeviceConfigAddOrEditModel {  
16 - defaultQueueName?: string; //处理队列  
17 - alarmProfile?: {  
18 - alarmContactId: string;  
19 - createTime: '2021-12-15T02:17:26.644Z';  
20 - creator: string;  
21 - defaultConfig: string;  
22 - description: string;  
23 - deviceProfileId: string;  
24 - enabled: true;  
25 - icon: string;  
26 - id: string;  
27 - messageMode: string;  
28 - name: string;  
29 - roleIds: [string];  
30 - tenantExpireTime: '2021-12-15T02:17:26.644Z';  
31 - tenantId: string;  
32 - tenantStatus: 'DISABLED';  
33 - updateTime: '2021-12-15T02:17:26.644Z';  
34 - updater: string;  
35 - };  
36 - convertJs?: string;  
37 - createTime?: '2021-12-15T02:17:26.644Z';  
38 - creator?: string;  
39 - defaultConfig?: string;  
40 - defaultRuleChainId?: string;  
41 - description?: string;  
42 - enabled?: true;  
43 - icon?: string;  
44 - id?: string;  
45 - name?: string;  
46 - profileData?: {  
47 - configuration: {};  
48 - transportConfiguration: {};  
49 - provisionConfiguration: {  
50 - provisionDeviceSecret: string;  
51 - };  
52 - //报警类型字段  
53 - alarms: [  
54 - {  
55 - id: 'highTemperatureAlarmID';  
56 - alarmType: 'High Temperature Alarm';  
57 - createRules: {  
58 - additionalProp1: {  
59 - condition: {  
60 - condition: [  
61 - {  
62 - key: {  
63 - type: 'TIME_SERIES';  
64 - key: 'temp';  
65 - };  
66 - valueType: 'NUMERIC';  
67 - value: {};  
68 - predicate: {};  
69 - }  
70 - ];  
71 - spec: {};  
72 - };  
73 - schedule: {  
74 - type: 'ANY_TIME';  
75 - };  
76 - alarmDetails: string;  
77 - dashboardId: {  
78 - id: '784f394c-42b6-435a-983c-b7beff2784f9';  
79 - entityType: 'DASHBOARD';  
80 - };  
81 - };  
82 - additionalProp2: {  
83 - condition: {  
84 - condition: [  
85 - {  
86 - key: {  
87 - type: 'TIME_SERIES';  
88 - key: 'temp';  
89 - };  
90 - valueType: 'NUMERIC';  
91 - value: {};  
92 - predicate: {};  
93 - }  
94 - ];  
95 - spec: {};  
96 - };  
97 - schedule: {  
98 - type: 'ANY_TIME';  
99 - };  
100 - alarmDetails: string;  
101 - dashboardId: {  
102 - id: '784f394c-42b6-435a-983c-b7beff2784f9';  
103 - entityType: 'DASHBOARD';  
104 - };  
105 - };  
106 - additionalProp3: {  
107 - condition: {  
108 - condition: [  
109 - {  
110 - key: {  
111 - type: 'TIME_SERIES';  
112 - key: 'temp';  
113 - };  
114 - valueType: 'NUMERIC';  
115 - value: {};  
116 - predicate: {};  
117 - }  
118 - ];  
119 - spec: {};  
120 - };  
121 - schedule: {  
122 - type: 'ANY_TIME';  
123 - };  
124 - alarmDetails: string;  
125 - dashboardId: {  
126 - id: '784f394c-42b6-435a-983c-b7beff2784f9';  
127 - entityType: 'DASHBOARD';  
128 - };  
129 - };  
130 - };  
131 - clearRule: {  
132 - condition: {  
133 - condition: [  
134 - {  
135 - key: {  
136 - type: 'TIME_SERIES';  
137 - key: 'temp';  
138 - };  
139 - valueType: 'NUMERIC';  
140 - value: {};  
141 - predicate: {};  
142 - }  
143 - ];  
144 - spec: {};  
145 - };  
146 - schedule: {  
147 - type: 'ANY_TIME';  
148 - };  
149 - alarmDetails: string;  
150 - dashboardId: {  
151 - id: '784f394c-42b6-435a-983c-b7beff2784f9';  
152 - entityType: 'DASHBOARD';  
153 - };  
154 - };  
155 - propagate: true;  
156 - propagateRelationTypes: [string];  
157 - }  
158 - ];  
159 - };  
160 - roleIds?: [string];  
161 - tbProfileId?: string;  
162 - tenantExpireTime?: '2021-12-15T02:17:26.645Z';  
163 - tenantId?: string;  
164 - tenantStatus?: 'DISABLED';  
165 - transportType?: string;  
166 - updateTime?: '2021-12-15T02:17:26.645Z';  
167 - updater?: string;  
168 -}  
169 -  
170 -export interface Data {  
171 - CO: number;  
172 -}  
173 -  
174 -export interface Details {  
175 - data: Data;  
176 -}  
177 -  
178 -export interface AlarmLogItem {  
179 - id: string;  
180 - tenantId: string;  
181 - creator?: any;  
182 - updater?: any;  
183 - createdTime: string;  
184 - updatedTime: string;  
185 - customerId: string;  
186 - tbDeviceId: string;  
187 - originatorType: number;  
188 - deviceId: string;  
189 - deviceName: string;  
190 - type: string;  
191 - severity: string;  
192 - status: string;  
193 - startTs: string;  
194 - endTs: string;  
195 - ackTs: string;  
196 - clearTs: string;  
197 - details: Details;  
198 - propagate: boolean;  
199 - propagateRelationTypes?: any;  
200 - organizationId: string;  
201 - organizationName: string;  
202 -}  
203 -  
204 -export interface Configuration {  
205 - type: string;  
206 -}  
207 -  
208 -export interface TransportConfiguration {  
209 - type: string;  
210 -}  
211 -  
212 -export interface ProvisionConfiguration {  
213 - type: string;  
214 - provisionDeviceSecret?: any;  
215 -}  
216 -  
217 -export interface ProfileData {  
218 - configuration: Configuration;  
219 - transportConfiguration: TransportConfiguration;  
220 - provisionConfiguration: ProvisionConfiguration;  
221 - alarms?: any;  
222 -}  
223 -  
224 -export interface ProfileRecord {  
225 - id: string;  
226 - creator: string;  
227 - createTime: string;  
228 - updater: string;  
229 - updateTime: string;  
230 - name: string;  
231 - tenantId: string;  
232 - transportType: string;  
233 - provisionType: string;  
234 - deviceType: string;  
235 - tbProfileId: string;  
236 - profileData: ProfileData;  
237 - defaultRuleChainId: string;  
238 - defaultQueueName: string;  
239 - image: string;  
240 - type: string;  
241 - default: boolean;  
242 -  
243 - checked?: boolean;  
244 -}  
245 -  
246 -export interface IDRecord {  
247 - entityType: string;  
248 - id: string;  
249 -}  
250 -  
251 -export interface RuleChainRecord {  
252 - id: IDRecord;  
253 - createdTime: number;  
254 - additionalInfo?: any;  
255 - tenantId: IDRecord;  
256 - name: string;  
257 - type: string;  
258 - firstRuleNodeId: IDRecord;  
259 - root: boolean;  
260 - debugMode: boolean;  
261 - configuration?: any;  
262 -} 1 +import { BasicPageParams } from '/@/api/model/baseModel';
  2 +
  3 +export type TDeviceConfigPageQueryParam = BasicPageParams & TDeviceConfigParams;
  4 +
  5 +export type TDeviceConfigParams = {
  6 + page?: any;
  7 + pageSize?: any;
  8 + name?: string;
  9 + transportType?: string;
  10 + orderFiled?: string;
  11 + orderType?: string;
  12 + tbDeviceId?: string;
  13 +};
  14 +
  15 +export interface IDeviceConfigAddOrEditModel {
  16 + defaultQueueName?: string; //处理队列
  17 + alarmProfile?: {
  18 + alarmContactId: string;
  19 + createTime: '2021-12-15T02:17:26.644Z';
  20 + creator: string;
  21 + defaultConfig: string;
  22 + description: string;
  23 + deviceProfileId: string;
  24 + enabled: true;
  25 + icon: string;
  26 + id: string;
  27 + messageMode: string;
  28 + name: string;
  29 + roleIds: [string];
  30 + tenantExpireTime: '2021-12-15T02:17:26.644Z';
  31 + tenantId: string;
  32 + tenantStatus: 'DISABLED';
  33 + updateTime: '2021-12-15T02:17:26.644Z';
  34 + updater: string;
  35 + };
  36 + convertJs?: string;
  37 + createTime?: '2021-12-15T02:17:26.644Z';
  38 + creator?: string;
  39 + defaultConfig?: string;
  40 + defaultRuleChainId?: string;
  41 + description?: string;
  42 + enabled?: true;
  43 + icon?: string;
  44 + id?: string;
  45 + name?: string;
  46 + profileData?: {
  47 + configuration: {};
  48 + transportConfiguration: {};
  49 + provisionConfiguration: {
  50 + provisionDeviceSecret: string;
  51 + };
  52 + //报警类型字段
  53 + alarms: [
  54 + {
  55 + id: 'highTemperatureAlarmID';
  56 + alarmType: 'High Temperature Alarm';
  57 + createRules: {
  58 + additionalProp1: {
  59 + condition: {
  60 + condition: [
  61 + {
  62 + key: {
  63 + type: 'TIME_SERIES';
  64 + key: 'temp';
  65 + };
  66 + valueType: 'NUMERIC';
  67 + value: {};
  68 + predicate: {};
  69 + }
  70 + ];
  71 + spec: {};
  72 + };
  73 + schedule: {
  74 + type: 'ANY_TIME';
  75 + };
  76 + alarmDetails: string;
  77 + dashboardId: {
  78 + id: '784f394c-42b6-435a-983c-b7beff2784f9';
  79 + entityType: 'DASHBOARD';
  80 + };
  81 + };
  82 + additionalProp2: {
  83 + condition: {
  84 + condition: [
  85 + {
  86 + key: {
  87 + type: 'TIME_SERIES';
  88 + key: 'temp';
  89 + };
  90 + valueType: 'NUMERIC';
  91 + value: {};
  92 + predicate: {};
  93 + }
  94 + ];
  95 + spec: {};
  96 + };
  97 + schedule: {
  98 + type: 'ANY_TIME';
  99 + };
  100 + alarmDetails: string;
  101 + dashboardId: {
  102 + id: '784f394c-42b6-435a-983c-b7beff2784f9';
  103 + entityType: 'DASHBOARD';
  104 + };
  105 + };
  106 + additionalProp3: {
  107 + condition: {
  108 + condition: [
  109 + {
  110 + key: {
  111 + type: 'TIME_SERIES';
  112 + key: 'temp';
  113 + };
  114 + valueType: 'NUMERIC';
  115 + value: {};
  116 + predicate: {};
  117 + }
  118 + ];
  119 + spec: {};
  120 + };
  121 + schedule: {
  122 + type: 'ANY_TIME';
  123 + };
  124 + alarmDetails: string;
  125 + dashboardId: {
  126 + id: '784f394c-42b6-435a-983c-b7beff2784f9';
  127 + entityType: 'DASHBOARD';
  128 + };
  129 + };
  130 + };
  131 + clearRule: {
  132 + condition: {
  133 + condition: [
  134 + {
  135 + key: {
  136 + type: 'TIME_SERIES';
  137 + key: 'temp';
  138 + };
  139 + valueType: 'NUMERIC';
  140 + value: {};
  141 + predicate: {};
  142 + }
  143 + ];
  144 + spec: {};
  145 + };
  146 + schedule: {
  147 + type: 'ANY_TIME';
  148 + };
  149 + alarmDetails: string;
  150 + dashboardId: {
  151 + id: '784f394c-42b6-435a-983c-b7beff2784f9';
  152 + entityType: 'DASHBOARD';
  153 + };
  154 + };
  155 + propagate: true;
  156 + propagateRelationTypes: [string];
  157 + }
  158 + ];
  159 + };
  160 + roleIds?: [string];
  161 + tbProfileId?: string;
  162 + tenantExpireTime?: '2021-12-15T02:17:26.645Z';
  163 + tenantId?: string;
  164 + tenantStatus?: 'DISABLED';
  165 + transportType?: string;
  166 + updateTime?: '2021-12-15T02:17:26.645Z';
  167 + updater?: string;
  168 +}
  169 +
  170 +export interface Data {
  171 + CO: number;
  172 +}
  173 +
  174 +export interface Details {
  175 + data: Data;
  176 +}
  177 +
  178 +export interface AlarmLogItem {
  179 + id: string;
  180 + tenantId: string;
  181 + creator?: any;
  182 + updater?: any;
  183 + createdTime: string;
  184 + updatedTime: string;
  185 + customerId: string;
  186 + tbDeviceId: string;
  187 + originatorType: number;
  188 + deviceId: string;
  189 + deviceName: string;
  190 + type: string;
  191 + severity: string;
  192 + status: string;
  193 + startTs: string;
  194 + endTs: string;
  195 + ackTs: string;
  196 + clearTs: string;
  197 + details: Details;
  198 + propagate: boolean;
  199 + propagateRelationTypes?: any;
  200 + organizationId: string;
  201 + organizationName: string;
  202 +}
  203 +
  204 +export interface Configuration {
  205 + type: string;
  206 +}
  207 +
  208 +export interface TransportConfiguration {
  209 + type: string;
  210 +}
  211 +
  212 +export interface ProvisionConfiguration {
  213 + type: string;
  214 + provisionDeviceSecret?: any;
  215 +}
  216 +
  217 +export interface ProfileData {
  218 + configuration: Configuration;
  219 + transportConfiguration: TransportConfiguration;
  220 + provisionConfiguration: ProvisionConfiguration;
  221 + alarms?: any;
  222 +}
  223 +
  224 +export interface ProfileRecord {
  225 + id: string;
  226 + creator: string;
  227 + createTime: string;
  228 + updater: string;
  229 + updateTime: string;
  230 + name: string;
  231 + tenantId: string;
  232 + transportType: string;
  233 + provisionType: string;
  234 + deviceType: string;
  235 + tbProfileId: string;
  236 + profileData: ProfileData;
  237 + defaultRuleChainId: string;
  238 + defaultQueueName: string;
  239 + image: string;
  240 + type: string;
  241 + default: boolean;
  242 +
  243 + checked?: boolean;
  244 +}
  245 +
  246 +export interface IDRecord {
  247 + entityType: string;
  248 + id: string;
  249 +}
  250 +
  251 +export interface RuleChainRecord {
  252 + id: IDRecord;
  253 + createdTime: number;
  254 + additionalInfo?: any;
  255 + tenantId: IDRecord;
  256 + name: string;
  257 + type: string;
  258 + firstRuleNodeId: IDRecord;
  259 + root: boolean;
  260 + debugMode: boolean;
  261 + configuration?: any;
  262 +}
  263 +
  264 +export interface DeviceProfileDetail {
  265 + id: string;
  266 + creator: string;
  267 + createTime: string;
  268 + updater: string;
  269 + updateTime: string;
  270 + name: string;
  271 + description: string;
  272 + tenantId: string;
  273 + transportType: string;
  274 + provisionType: string;
  275 + deviceType: string;
  276 + tbProfileId: string;
  277 + profileData: ProfileData;
  278 + defaultQueueName: string;
  279 + type: string;
  280 + deviceCount: number;
  281 + default: boolean;
  282 +}
  283 +
  284 +export interface ProfileData {
  285 + configuration: Configuration;
  286 + transportConfiguration: TransportConfiguration;
  287 + provisionConfiguration: ProvisionConfiguration;
  288 + alarms: any;
  289 + thingsModel: any;
  290 +}
  291 +
  292 +export interface Configuration {
  293 + type: string;
  294 +}
  295 +
  296 +export interface TransportConfiguration {
  297 + type: string;
  298 +}
  299 +
  300 +export interface ProvisionConfiguration {
  301 + type: string;
  302 + provisionDeviceSecret: any;
  303 +}
@@ -230,10 +230,10 @@ @@ -230,10 +230,10 @@
230 230
231 function setFormModel(key: string, value: any) { 231 function setFormModel(key: string, value: any) {
232 formModel[key] = value; 232 formModel[key] = value;
233 - // const { validateTrigger } = unref(getBindValue);  
234 - // if (!validateTrigger || validateTrigger === 'change') {  
235 - // validateFields([key]).catch((_) => {});  
236 - // } 233 + const { validateTrigger } = unref(getBindValue);
  234 + if (!validateTrigger || validateTrigger === 'change') {
  235 + validateFields([key]).catch((_) => {});
  236 + }
237 } 237 }
238 238
239 function handleEnterPress(e: KeyboardEvent) { 239 function handleEnterPress(e: KeyboardEvent) {
@@ -242,7 +242,7 @@ @@ -242,7 +242,7 @@
242 const value = target ? (isCheck ? target.checked : target.value) : e; 242 const value = target ? (isCheck ? target.checked : target.value) : e;
243 props.setFormModel(field, value); 243 props.setFormModel(field, value);
244 }, 244 },
245 - onBlur: (...args) => { 245 + onBlur: (...args: any[]) => {
246 unref(getComponentsProps)?.onBlur?.(...args); 246 unref(getComponentsProps)?.onBlur?.(...args);
247 props.validateFields([field], { triggerName: 'blur' }).catch((_) => {}); 247 props.validateFields([field], { triggerName: 'blur' }).catch((_) => {});
248 }, 248 },
@@ -38,7 +38,7 @@ export const basicProps = { @@ -38,7 +38,7 @@ export const basicProps = {
38 }, 38 },
39 autoSetPlaceHolder: propTypes.bool.def(true), 39 autoSetPlaceHolder: propTypes.bool.def(true),
40 // 在INPUT组件上单击回车时,是否自动提交 40 // 在INPUT组件上单击回车时,是否自动提交
41 - autoSubmitOnEnter: propTypes.bool.def(false), 41 + autoSubmitOnEnter: propTypes.bool.def(true),
42 submitOnReset: propTypes.bool, 42 submitOnReset: propTypes.bool,
43 size: propTypes.oneOf(['default', 'small', 'large']).def('default'), 43 size: propTypes.oneOf(['default', 'small', 'large']).def('default'),
44 // 禁用表单 44 // 禁用表单
@@ -52,6 +52,7 @@ export function useBatchDelete( @@ -52,6 +52,7 @@ export function useBatchDelete(
52 if (record) { 52 if (record) {
53 await deleteFn([record.id]); 53 await deleteFn([record.id]);
54 createMessage.success('删除成功'); 54 createMessage.success('删除成功');
  55 + selectedRowIds.value = [];
55 } else { 56 } else {
56 await deleteFn(selectedRowIds.value); 57 await deleteFn(selectedRowIds.value);
57 createMessage.success('批量删除成功'); 58 createMessage.success('批量删除成功');
@@ -116,7 +116,7 @@ @@ -116,7 +116,7 @@
116 const { organizationIdTreeRef, resetFn } = useResetOrganizationTree(searchInfo); 116 const { organizationIdTreeRef, resetFn } = useResetOrganizationTree(searchInfo);
117 const [registerModal, { openModal }] = useModal(); 117 const [registerModal, { openModal }] = useModal();
118 // 表格hooks 118 // 表格hooks
119 - const [registerTable, { reload, setProps }] = useTable({ 119 + const [registerTable, { reload, setProps, clearSelectedRowKeys }] = useTable({
120 title: '视频列表', 120 title: '视频列表',
121 api: cameraPage, 121 api: cameraPage,
122 columns, 122 columns,
@@ -145,6 +145,7 @@ @@ -145,6 +145,7 @@
145 // 刷新 145 // 刷新
146 const handleSuccess = () => { 146 const handleSuccess = () => {
147 reload(); 147 reload();
  148 + clearSelectedRowKeys();
148 }; 149 };
149 const { hasBatchDelete, handleDeleteOrBatchDelete, selectionOptions } = useBatchDelete( 150 const { hasBatchDelete, handleDeleteOrBatchDelete, selectionOptions } = useBatchDelete(
150 deleteCameraManage, 151 deleteCameraManage,
@@ -2,9 +2,10 @@ @@ -2,9 +2,10 @@
2 <div ref="chartRef" :style="{ height, width }"></div> 2 <div ref="chartRef" :style="{ height, width }"></div>
3 </template> 3 </template>
4 <script lang="ts" setup> 4 <script lang="ts" setup>
5 - import { ref, Ref, withDefaults, onMounted, watch } from 'vue'; 5 + import { ref, Ref, onMounted, watch } from 'vue';
6 import { useECharts } from '/@/hooks/web/useECharts'; 6 import { useECharts } from '/@/hooks/web/useECharts';
7 import { getTrendData } from '/@/api/dashboard'; 7 import { getTrendData } from '/@/api/dashboard';
  8 + import { getDateByShortcutQueryKey, ShortcutQueryKeyEnum } from '../hooks/useDate';
8 9
9 interface Props { 10 interface Props {
10 width?: string; 11 width?: string;
@@ -77,11 +78,8 @@ @@ -77,11 +78,8 @@
77 const chartRef = ref<HTMLDivElement | null>(null); 78 const chartRef = ref<HTMLDivElement | null>(null);
78 const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>); 79 const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>);
79 onMounted(async () => { 80 onMounted(async () => {
80 - const endTs = Date.now();  
81 const res = await getTrendData({ 81 const res = await getTrendData({
82 - startTs: endTs - 2592000000,  
83 - endTs,  
84 - interval: 86400000, 82 + ...getDateByShortcutQueryKey(ShortcutQueryKeyEnum.LATEST_30_DAY),
85 trend: 'CUSTOMER_TREND', 83 trend: 'CUSTOMER_TREND',
86 }); 84 });
87 85
@@ -95,6 +95,20 @@ @@ -95,6 +95,20 @@
95 > 95 >
96 <template #extra> 96 <template #extra>
97 <div class="extra-date"> 97 <div class="extra-date">
  98 + <Tooltip :overlayStyle="{ maxWidth: '340px' }">
  99 + <template #title>
  100 + <section>
  101 + <div>30天: 查询最近30天的数据,间隔时间为1天.</div>
  102 + <div>最近三个月: 查询最近三个月的数据,间隔时间为1天.</div>
  103 + <div>最近一年: 查询最近一年的数据,间隔时间为30天.</div>
  104 + <div>
  105 + 间隔时间:
  106 + 以当前时间作为结束时间,往前推移对应天数或小时的时间作为开始时间,然后在此时间区间内进行分组聚合查询.
  107 + </div>
  108 + </section>
  109 + </template>
  110 + <QuestionCircleOutlined class="!mr-2 cursor-pointer" />
  111 + </Tooltip>
98 <template v-for="(item, index) in TenantOrCustomerDateList" :key="item.value"> 112 <template v-for="(item, index) in TenantOrCustomerDateList" :key="item.value">
99 <span 113 <span
100 @click="quickQueryTenantOrCustomerTime(index, item.value)" 114 @click="quickQueryTenantOrCustomerTime(index, item.value)"
@@ -80,6 +80,20 @@ @@ -80,6 +80,20 @@
80 > 80 >
81 <template #extra> 81 <template #extra>
82 <div class="extra-date"> 82 <div class="extra-date">
  83 + <Tooltip :overlayStyle="{ maxWidth: '340px' }">
  84 + <template #title>
  85 + <section>
  86 + <div>30天: 查询最近30天的数据,间隔时间为1天.</div>
  87 + <div>最近三个月: 查询最近三个月的数据,间隔时间为1天.</div>
  88 + <div>最近一年: 查询最近一年的数据,间隔时间为30天.</div>
  89 + <div>
  90 + 间隔时间:
  91 + 以当前时间作为结束时间,往前推移对应天数或小时的时间作为开始时间,然后在此时间区间内进行分组聚合查询.
  92 + </div>
  93 + </section>
  94 + </template>
  95 + <QuestionCircleOutlined class="!mr-2 cursor-pointer" />
  96 + </Tooltip>
83 <template v-for="(item, index) in TenantOrCustomerDateList" :key="item.value"> 97 <template v-for="(item, index) in TenantOrCustomerDateList" :key="item.value">
84 <span 98 <span
85 @click="quickQueryTenantOrCustomerTime(index, item.value, 'tenant')" 99 @click="quickQueryTenantOrCustomerTime(index, item.value, 'tenant')"
@@ -445,7 +459,7 @@ @@ -445,7 +459,7 @@
445 if (activeIndex.value === index) return; 459 if (activeIndex.value === index) return;
446 activeIndex.value = index; 460 activeIndex.value = index;
447 dateValue.value = ''; 461 dateValue.value = '';
448 - console.log(interval); 462 +
449 if (isCustomer) { 463 if (isCustomer) {
450 if (activeKey.value === '1') { 464 if (activeKey.value === '1') {
451 const data = await getTrendData({ 465 const data = await getTrendData({
@@ -2,9 +2,10 @@ @@ -2,9 +2,10 @@
2 <div ref="chartRef" :style="{ height, width }"></div> 2 <div ref="chartRef" :style="{ height, width }"></div>
3 </template> 3 </template>
4 <script lang="ts" setup> 4 <script lang="ts" setup>
5 - import { ref, Ref, withDefaults, onMounted, watch } from 'vue'; 5 + import { ref, Ref, onMounted, watch } from 'vue';
6 import { useECharts } from '/@/hooks/web/useECharts'; 6 import { useECharts } from '/@/hooks/web/useECharts';
7 import { getTrendData } from '/@/api/dashboard/index'; 7 import { getTrendData } from '/@/api/dashboard/index';
  8 + import { getDateByShortcutQueryKey, ShortcutQueryKeyEnum } from '../hooks/useDate';
8 interface Props { 9 interface Props {
9 width?: string; 10 width?: string;
10 height?: string; 11 height?: string;
@@ -77,11 +78,8 @@ @@ -77,11 +78,8 @@
77 const chartRef = ref<HTMLDivElement | null>(null); 78 const chartRef = ref<HTMLDivElement | null>(null);
78 const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>); 79 const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>);
79 onMounted(async () => { 80 onMounted(async () => {
80 - const endTs = Date.now();  
81 const res = await getTrendData({ 81 const res = await getTrendData({
82 - startTs: endTs - 2592000000,  
83 - endTs,  
84 - interval: 86400000, 82 + ...getDateByShortcutQueryKey(ShortcutQueryKeyEnum.LATEST_30_DAY),
85 trend: 'TENANT_TREND', 83 trend: 'TENANT_TREND',
86 }); 84 });
87 const transferResult = res.map((item) => [item.ts, item.value]); 85 const transferResult = res.map((item) => [item.ts, item.value]);
1 -import { formatToDateTime } from '/@/utils/dateUtil'; 1 +import { dateUtil, formatToDateTime } from '/@/utils/dateUtil';
2 import { ref } from 'vue'; 2 import { ref } from 'vue';
3 import { getTrendData } from '/@/api/dashboard'; 3 import { getTrendData } from '/@/api/dashboard';
4 4
  5 +export enum ShortcutQueryKeyEnum {
  6 + LATEST_30_DAY = 'LATEST_30_DAY',
  7 + LATEST_3_MONTH = 'LATEST_3_MONTH',
  8 + LATEST_1_YEAR = 'LATEST_1_YEAR',
  9 +}
  10 +
  11 +export function getDateByShortcutQueryKey(value: ShortcutQueryKeyEnum) {
  12 + const mapping = {
  13 + [ShortcutQueryKeyEnum.LATEST_30_DAY]: () => {
  14 + return {
  15 + startTs: dateUtil().subtract(30, 'day').startOf('day').valueOf(),
  16 + interval: 24 * 60 * 60 * 1000,
  17 + };
  18 + },
  19 + [ShortcutQueryKeyEnum.LATEST_3_MONTH]: () => {
  20 + return {
  21 + startTs: dateUtil().subtract(3, 'month').startOf('day').valueOf(),
  22 + interval: 24 * 60 * 60 * 1000,
  23 + };
  24 + },
  25 + [ShortcutQueryKeyEnum.LATEST_1_YEAR]: () => {
  26 + return {
  27 + startTs: dateUtil().subtract(1, 'year').startOf('day').valueOf(),
  28 + interval: 30 * 24 * 60 * 60 * 1000,
  29 + };
  30 + },
  31 + };
  32 +
  33 + const result = mapping?.[value]?.();
  34 +
  35 + return {
  36 + ...result,
  37 + endTs: dateUtil().add(1, 'day').startOf('day').valueOf(),
  38 + };
  39 +}
  40 +
5 export function useDate() { 41 export function useDate() {
6 const tenantDateValue = ref([]); 42 const tenantDateValue = ref([]);
7 const customerDateValue = ref([]); 43 const customerDateValue = ref([]);
@@ -10,26 +46,23 @@ export function useDate() { @@ -10,26 +46,23 @@ export function useDate() {
10 const activeTenantIndex = ref(0); 46 const activeTenantIndex = ref(0);
11 const activeCustomerIndex = ref(0); 47 const activeCustomerIndex = ref(0);
12 const TenantOrCustomerDateList = ref([ 48 const TenantOrCustomerDateList = ref([
13 - { label: '30天', value: 2592000000 },  
14 - { label: '最近三个月', value: 7776000000 },  
15 - { label: '最近一年', value: 31536000000 }, 49 + { label: '30天', value: ShortcutQueryKeyEnum.LATEST_30_DAY },
  50 + { label: '最近三个月', value: ShortcutQueryKeyEnum.LATEST_3_MONTH },
  51 + { label: '最近一年', value: ShortcutQueryKeyEnum.LATEST_1_YEAR },
16 ]); 52 ]);
17 53
18 // 租户趋势和客户趋势快速选择时间 54 // 租户趋势和客户趋势快速选择时间
19 async function quickQueryTenantOrCustomerTime( 55 async function quickQueryTenantOrCustomerTime(
20 index: number, 56 index: number,
21 - value: number, 57 + value: ShortcutQueryKeyEnum,
22 flag: 'tenant' | 'customer' 58 flag: 'tenant' | 'customer'
23 ) { 59 ) {
24 - const endTs = Date.now();  
25 if (flag === 'tenant') { 60 if (flag === 'tenant') {
26 if (activeTenantIndex.value === index) return; 61 if (activeTenantIndex.value === index) return;
27 activeTenantIndex.value = index; 62 activeTenantIndex.value = index;
28 tenantDateValue.value = []; 63 tenantDateValue.value = [];
29 const res = await getTrendData({ 64 const res = await getTrendData({
30 - startTs: endTs - value,  
31 - endTs,  
32 - interval: value === 2592000000 ? 86400000 : value === 7776000000 ? 172800000 : 2592000000, 65 + ...getDateByShortcutQueryKey(value),
33 trend: 'TENANT_TREND', 66 trend: 'TENANT_TREND',
34 }); 67 });
35 tenantTrendList.value = res.map((item) => [item.ts, item.value]); 68 tenantTrendList.value = res.map((item) => [item.ts, item.value]);
@@ -38,9 +71,7 @@ export function useDate() { @@ -38,9 +71,7 @@ export function useDate() {
38 activeCustomerIndex.value = index; 71 activeCustomerIndex.value = index;
39 customerDateValue.value = []; 72 customerDateValue.value = [];
40 const res = await getTrendData({ 73 const res = await getTrendData({
41 - startTs: endTs - value,  
42 - endTs,  
43 - interval: value === 2592000000 ? 86400000 : value === 7776000000 ? 172800000 : 2592000000, 74 + ...getDateByShortcutQueryKey(value),
44 trend: 'CUSTOMER_TREND', 75 trend: 'CUSTOMER_TREND',
45 }); 76 });
46 customerTrendList.value = res.map((item) => [item.ts, item.value]); 77 customerTrendList.value = res.map((item) => [item.ts, item.value]);
@@ -7,7 +7,7 @@ import { JSONEditor } from '/@/components/CodeEditor'; @@ -7,7 +7,7 @@ import { JSONEditor } from '/@/components/CodeEditor';
7 import { DeviceTypeEnum } from '/@/api/device/model/deviceModel'; 7 import { DeviceTypeEnum } from '/@/api/device/model/deviceModel';
8 import { getModelServices } from '/@/api/device/modelOfMatter'; 8 import { getModelServices } from '/@/api/device/modelOfMatter';
9 import { ModelOfMatterParams } from '/@/api/device/model/modelOfMatterModel'; 9 import { ModelOfMatterParams } from '/@/api/device/model/modelOfMatterModel';
10 -import { toRaw, unref } from 'vue'; 10 +import { nextTick, toRaw, unref } from 'vue';
11 import ObjectModelValidateForm from '/@/components/Form/src/externalCompns/components/ObjectModelValidateForm/ObjectModelValidateForm.vue'; 11 import ObjectModelValidateForm from '/@/components/Form/src/externalCompns/components/ObjectModelValidateForm/ObjectModelValidateForm.vue';
12 import { CommandDeliveryWayEnum, ServiceCallTypeEnum } from '/@/enums/toolEnum'; 12 import { CommandDeliveryWayEnum, ServiceCallTypeEnum } from '/@/enums/toolEnum';
13 import { TaskTypeEnum } from '/@/views/task/center/config'; 13 import { TaskTypeEnum } from '/@/views/task/center/config';
@@ -246,8 +246,9 @@ export const step1Schemas: FormSchema[] = [ @@ -246,8 +246,9 @@ export const step1Schemas: FormSchema[] = [
246 required: true, 246 required: true,
247 component: 'ApiSelect', 247 component: 'ApiSelect',
248 ifShow: ({ values }) => values.deviceType === 'SENSOR' && values.organizationId, 248 ifShow: ({ values }) => values.deviceType === 'SENSOR' && values.organizationId,
249 - componentProps: ({ formModel }) => { 249 + componentProps: ({ formModel, formActionType }) => {
250 const { organizationId, transportType } = formModel; 250 const { organizationId, transportType } = formModel;
  251 + const { validateFields } = formActionType;
251 if (![organizationId, transportType].every(Boolean)) return {}; 252 if (![organizationId, transportType].every(Boolean)) return {};
252 return { 253 return {
253 api: async (params: Recordable) => { 254 api: async (params: Recordable) => {
@@ -266,6 +267,10 @@ export const step1Schemas: FormSchema[] = [ @@ -266,6 +267,10 @@ export const step1Schemas: FormSchema[] = [
266 }, 267 },
267 valueField: 'tbDeviceId', 268 valueField: 'tbDeviceId',
268 labelField: 'alias', 269 labelField: 'alias',
  270 + onChange: async () => {
  271 + await nextTick();
  272 + validateFields(['gatewayId']);
  273 + },
269 }; 274 };
270 }, 275 },
271 }, 276 },
1 <script lang="ts" setup> 1 <script lang="ts" setup>
2 import { Upload, Button } from 'ant-design-vue'; 2 import { Upload, Button } from 'ant-design-vue';
3 import { InboxOutlined } from '@ant-design/icons-vue'; 3 import { InboxOutlined } from '@ant-design/icons-vue';
4 - import { computed, ref } from 'vue'; 4 + import { computed } from 'vue';
5 import StepContainer from './StepContainer.vue'; 5 import StepContainer from './StepContainer.vue';
6 import XLSX, { CellObject } from 'xlsx'; 6 import XLSX, { CellObject } from 'xlsx';
7 import { basicProps } from './props'; 7 import { basicProps } from './props';
8 import { UploadFileParseValue } from './type'; 8 import { UploadFileParseValue } from './type';
  9 + import { useMessage } from '/@/hooks/web/useMessage';
9 10
10 const props = defineProps({ 11 const props = defineProps({
11 ...basicProps, 12 ...basicProps,
  13 + fileList: {
  14 + required: true,
  15 + type: Array as PropType<(Record<'uid' | 'name', string> & File)[]>,
  16 + },
12 value: { 17 value: {
13 require: true, 18 require: true,
14 type: Object as PropType<UploadFileParseValue>, 19 type: Object as PropType<UploadFileParseValue>,
15 }, 20 },
16 }); 21 });
17 22
18 - const emit = defineEmits(['update:value']);  
19 -  
20 - const fileList = ref<Record<'uid' | 'name', string>[]>([]); 23 + const emit = defineEmits(['update:value', 'update:fileList']);
21 24
22 interface FileRequestParams { 25 interface FileRequestParams {
23 file: File & { uid: string }; 26 file: File & { uid: string };
@@ -48,23 +51,30 @@ @@ -48,23 +51,30 @@
48 return new Promise((resolve, reject) => { 51 return new Promise((resolve, reject) => {
49 const fileReader = new FileReader(); 52 const fileReader = new FileReader();
50 fileReader.onload = (event: ProgressEvent) => { 53 fileReader.onload = (event: ProgressEvent) => {
51 - const data = (event.target as FileReader).result as string;  
52 -  
53 - const result = XLSX.read(data, { type: 'string' });  
54 -  
55 - const sheetName = result.SheetNames.at(0);  
56 - const workbook = result.Sheets;  
57 - const sheet = workbook[sheetName as string];  
58 - const sheetRange = sheet['!ref'];  
59 -  
60 - const {  
61 - s: { c: startColumn },  
62 - e: { c: endColumn },  
63 - } = XLSX.utils.decode_range(sheetRange!);  
64 -  
65 - const header = getTableHeader(sheet, [startColumn, endColumn]);  
66 - const content = XLSX.utils.sheet_to_json(sheet, { range: sheetRange }) as Recordable[];  
67 - resolve({ header, content }); 54 + try {
  55 + const data = (event.target as FileReader).result as string;
  56 +
  57 + const result = XLSX.read(data, { type: 'string' });
  58 +
  59 + const sheetName = result.SheetNames.at(0);
  60 + const workbook = result.Sheets;
  61 + const sheet = workbook[sheetName as string];
  62 + const sheetRange = sheet['!ref'];
  63 +
  64 + const {
  65 + s: { c: startColumn },
  66 + e: { c: endColumn },
  67 + } = XLSX.utils.decode_range(sheetRange!);
  68 +
  69 + const header = getTableHeader(sheet, [startColumn, endColumn]);
  70 + const content = XLSX.utils.sheet_to_json(sheet, { range: sheetRange }) as Recordable[];
  71 +
  72 + resolve({ header, content });
  73 + } catch (error) {
  74 + const { createMessage } = useMessage();
  75 + createMessage.error('请检查csv文件是否正确');
  76 + throw error;
  77 + }
68 }; 78 };
69 79
70 fileReader.onerror = () => { 80 fileReader.onerror = () => {
@@ -75,19 +85,19 @@ @@ -75,19 +85,19 @@
75 }; 85 };
76 86
77 const handleParseFile = async ({ file, onSuccess, onError }: FileRequestParams) => { 87 const handleParseFile = async ({ file, onSuccess, onError }: FileRequestParams) => {
78 - fileList.value = [];  
79 const value = await readFile(file); 88 const value = await readFile(file);
80 if (!value) { 89 if (!value) {
81 onError(); 90 onError();
82 return; 91 return;
83 } 92 }
84 - fileList.value = [file]; 93 +
  94 + emit('update:fileList', [file]);
85 emit('update:value', value); 95 emit('update:value', value);
86 onSuccess({}, file); 96 onSuccess({}, file);
87 }; 97 };
88 98
89 const canGoNextStep = computed(() => { 99 const canGoNextStep = computed(() => {
90 - return !!fileList.value.length; 100 + return !!props.fileList.length;
91 }); 101 });
92 102
93 const handlePreviousStep = () => { 103 const handlePreviousStep = () => {
@@ -97,16 +107,22 @@ @@ -97,16 +107,22 @@
97 const handleNextStep = () => { 107 const handleNextStep = () => {
98 props.goNextStep?.(); 108 props.goNextStep?.();
99 }; 109 };
  110 +
  111 + const handleRemove = () => {
  112 + emit('update:fileList', []);
  113 + return true;
  114 + };
100 </script> 115 </script>
101 116
102 <template> 117 <template>
103 <StepContainer> 118 <StepContainer>
104 <div class="">设备文件</div> 119 <div class="">设备文件</div>
105 <Upload.Dragger 120 <Upload.Dragger
106 - v-model:fileList="fileList" 121 + :fileList="fileList"
107 :customRequest="handleParseFile" 122 :customRequest="handleParseFile"
108 accept=".csv" 123 accept=".csv"
109 name="file" 124 name="file"
  125 + :remove="handleRemove"
110 > 126 >
111 <section class="cursor-pointer flex flex-col justify-center items-center"> 127 <section class="cursor-pointer flex flex-col justify-center items-center">
112 <InboxOutlined class="text-[4rem] !text-blue-400" /> 128 <InboxOutlined class="text-[4rem] !text-blue-400" />
@@ -17,6 +17,8 @@ @@ -17,6 +17,8 @@
17 17
18 const basicInfo = ref({}); 18 const basicInfo = ref({});
19 19
  20 + const fileList = ref<(File & Record<'uid' | 'name', string>)[]>([]);
  21 +
20 const fileParseValue = ref<UploadFileParseValue>({ header: [], content: [] }); 22 const fileParseValue = ref<UploadFileParseValue>({ header: [], content: [] });
21 23
22 const columnConfiguration = ref<Record<'type', string>[]>([]); 24 const columnConfiguration = ref<Record<'type', string>[]>([]);
@@ -61,6 +63,7 @@ @@ -61,6 +63,7 @@
61 63
62 const reset = () => { 64 const reset = () => {
63 basicInfo.value = {}; 65 basicInfo.value = {};
  66 + fileList.value = [];
64 fileParseValue.value = { header: [], content: [] }; 67 fileParseValue.value = { header: [], content: [] };
65 columnConfiguration.value = []; 68 columnConfiguration.value = [];
66 importResult.value = {} as unknown as ImportDeviceResponse; 69 importResult.value = {} as unknown as ImportDeviceResponse;
@@ -109,6 +112,7 @@ @@ -109,6 +112,7 @@
109 /> 112 />
110 <ImportCsv 113 <ImportCsv
111 v-if="StepsEnum[item] === StepsEnum.IMPORT_FILE && StepsEnum[item] === currentStep" 114 v-if="StepsEnum[item] === StepsEnum.IMPORT_FILE && StepsEnum[item] === currentStep"
  115 + v-model:fileList="fileList"
112 v-model:value="fileParseValue" 116 v-model:value="fileParseValue"
113 :go-next-step="goNextStep" 117 :go-next-step="goNextStep"
114 :go-previous-step="goPreviousStep" 118 :go-previous-step="goPreviousStep"
@@ -11,7 +11,7 @@ @@ -11,7 +11,7 @@
11 import { ColEx } from '/@/components/Form/src/types'; 11 import { ColEx } from '/@/components/Form/src/types';
12 import { useHistoryData } from '../../hook/useHistoryData'; 12 import { useHistoryData } from '../../hook/useHistoryData';
13 import { formatToDateTime } from '/@/utils/dateUtil'; 13 import { formatToDateTime } from '/@/utils/dateUtil';
14 - import { useTable, BasicTable } from '/@/components/Table'; 14 + import { useTable, BasicTable, BasicColumn } from '/@/components/Table';
15 import { DataTypeEnum } from '/@/components/Form/src/externalCompns/components/StructForm/config'; 15 import { DataTypeEnum } from '/@/components/Form/src/externalCompns/components/StructForm/config';
16 import { 16 import {
17 ModeSwitchButton, 17 ModeSwitchButton,
@@ -60,13 +60,9 @@ @@ -60,13 +60,9 @@
60 } 60 }
61 } 61 }
62 62
63 - const [registerTable] = useTable({  
64 - showIndexColumn: false,  
65 - showTableSetting: false,  
66 - dataSource: historyData,  
67 - maxHeight: 300,  
68 - size: 'small',  
69 - columns: [ 63 + const sortOrder = ref<any>('descend');
  64 + const columns: BasicColumn[] | any = computed(() => {
  65 + return [
70 { 66 {
71 title: '属性', 67 title: '属性',
72 dataIndex: 'name', 68 dataIndex: 'name',
@@ -84,18 +80,50 @@ @@ -84,18 +80,50 @@
84 format: (val) => { 80 format: (val) => {
85 return formatToDateTime(val, 'YYYY-MM-DD HH:mm:ss'); 81 return formatToDateTime(val, 'YYYY-MM-DD HH:mm:ss');
86 }, 82 },
87 - sorter: 'descend', 83 + sorter: true,
  84 + sortOrder: unref(sortOrder),
  85 + sortDirections: ['descend', 'ascend', 'descend'],
88 }, 86 },
89 - ], 87 + ];
  88 + });
  89 +
  90 + const [registerTable, { setColumns }] = useTable({
  91 + showIndexColumn: false,
  92 + showTableSetting: false,
  93 + dataSource: historyData,
  94 + maxHeight: 300,
  95 + columns: unref(columns),
  96 + size: 'small',
90 }); 97 });
91 98
92 - const handleTableChange = async (pag, filters, sorter: any) => {  
93 - console.log(pag, filters, sorter, 'pag, filters, sorter'); 99 + const getTableList = async (orderBy?: string) => {
  100 + // 表单验证
  101 + await method.validate();
  102 + const value = method.getFieldsValue();
  103 + const searchParams = getSearchParams(value);
  104 +
  105 + if (!hasDeviceAttr()) return;
  106 + // 发送请求
  107 + loading.value = true;
  108 + const res = await getDeviceHistoryInfo(
  109 + {
  110 + ...searchParams,
  111 + entityId: props.deviceDetail.tbDeviceId,
  112 + },
  113 + orderBy
  114 + );
  115 + historyData.value = getTableHistoryData(res);
  116 + loading.value = false;
  117 + };
  118 +
  119 + const handleTableChange = async (_pag, _filters, sorter: any) => {
  120 + sortOrder.value = sorter.order;
  121 + await setColumns(unref(columns));
94 if (sorter.field == 'ts') { 122 if (sorter.field == 'ts') {
95 if (sorter.order == 'descend') { 123 if (sorter.order == 'descend') {
96 - openHistoryPanel('ASC'); 124 + getTableList('DESC');
97 } else { 125 } else {
98 - openHistoryPanel('DESC'); 126 + getTableList('ASC');
99 } 127 }
100 } 128 }
101 }; 129 };
@@ -214,7 +242,7 @@ @@ -214,7 +242,7 @@
214 setOptions(setChartOptions(res, selectedKeys)); 242 setOptions(setChartOptions(res, selectedKeys));
215 }; 243 };
216 244
217 - const switchMode = (flag: EnumTableChartMode) => { 245 + const switchMode = async (flag: EnumTableChartMode) => {
218 mode.value = flag; 246 mode.value = flag;
219 }; 247 };
220 248
@@ -12,6 +12,23 @@ export interface BasicCreateFormParams { @@ -12,6 +12,23 @@ export interface BasicCreateFormParams {
12 12
13 useComponentRegister('JSONEditor', JSONEditor); 13 useComponentRegister('JSONEditor', JSONEditor);
14 14
  15 +const validateDouble = (
  16 + value: number,
  17 + type: DataTypeEnum,
  18 + min?: number | string,
  19 + max?: number | string
  20 +) => {
  21 + min =
  22 + Number(min) || type === DataTypeEnum.IS_NUMBER_INT ? Number.MIN_SAFE_INTEGER : Number.MIN_VALUE;
  23 + max =
  24 + Number(max) || type === DataTypeEnum.IS_NUMBER_INT ? Number.MAX_SAFE_INTEGER : Number.MAX_VALUE;
  25 +
  26 + return {
  27 + flag: value < min || value > max,
  28 + message: `取值范围在${min}~${max}之间`,
  29 + };
  30 +};
  31 +
15 export const useGenDynamicForm = () => { 32 export const useGenDynamicForm = () => {
16 const createInputNumber = ({ 33 const createInputNumber = ({
17 identifier, 34 identifier,
@@ -30,19 +47,19 @@ export const useGenDynamicForm = () => { @@ -30,19 +47,19 @@ export const useGenDynamicForm = () => {
30 type: 'number', 47 type: 'number',
31 trigger: 'change', 48 trigger: 'change',
32 validator: (_rule, value) => { 49 validator: (_rule, value) => {
33 - if (  
34 - value < (min ?? Number.MIN_SAFE_INTEGER) ||  
35 - value > (max || Number.MAX_SAFE_INTEGER)  
36 - ) {  
37 - return Promise.reject(`${functionName}取值范围在${min}~${max}之间`); 50 + const { flag, message } = validateDouble(value, type, min, max);
  51 + if (flag) {
  52 + return Promise.reject(`${functionName}${message}`);
38 } 53 }
39 return Promise.resolve(value); 54 return Promise.resolve(value);
40 }, 55 },
41 }, 56 },
42 ], 57 ],
43 componentProps: { 58 componentProps: {
44 - max: max ?? Number.MAX_SAFE_INTEGER,  
45 - min: min ?? Number.MIN_SAFE_INTEGER, 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,
46 step, 63 step,
47 placeholder: `请输入${functionName}`, 64 placeholder: `请输入${functionName}`,
48 precision: type === DataTypeEnum.IS_NUMBER_INT ? 0 : 2, 65 precision: type === DataTypeEnum.IS_NUMBER_INT ? 0 : 2,
@@ -136,7 +153,7 @@ export const useGenDynamicForm = () => { @@ -136,7 +153,7 @@ export const useGenDynamicForm = () => {
136 fieldTypeMap.clear(); 153 fieldTypeMap.clear();
137 const formSchema = schemas.map((item) => { 154 const formSchema = schemas.map((item) => {
138 const { functionName, identifier, dataType } = item; 155 const { functionName, identifier, dataType } = item;
139 - console.log(item, 'item'); 156 +
140 const { type } = dataType || {}; 157 const { type } = dataType || {};
141 158
142 fieldTypeMap.set(identifier!, dataType!.type); 159 fieldTypeMap.set(identifier!, dataType!.type);
@@ -84,6 +84,7 @@ @@ -84,6 +84,7 @@
84 import DeviceConfigurationStep from './step/DeviceConfigurationStep.vue'; 84 import DeviceConfigurationStep from './step/DeviceConfigurationStep.vue';
85 import TransportConfigurationStep from './step/TransportConfigurationStep.vue'; 85 import TransportConfigurationStep from './step/TransportConfigurationStep.vue';
86 import PhysicalModelManagementStep from './step/PhysicalModelManagementStep.vue'; 86 import PhysicalModelManagementStep from './step/PhysicalModelManagementStep.vue';
  87 + import { DeviceProfileDetail } from '/@/api/device/model/deviceConfigModel';
87 88
88 const emits = defineEmits(['success', 'register']); 89 const emits = defineEmits(['success', 'register']);
89 const activeKey = ref('1'); 90 const activeKey = ref('1');
@@ -105,15 +106,22 @@ @@ -105,15 +106,22 @@
105 }); 106 });
106 const transportTypeStr = ref(''); 107 const transportTypeStr = ref('');
107 const dynamicWidth = ref('55rem'); 108 const dynamicWidth = ref('55rem');
  109 +
  110 + const isDefault = ref(false);
108 const [register, { closeModal, setModalProps }] = useModalInner(async (data) => { 111 const [register, { closeModal, setModalProps }] = useModalInner(async (data) => {
109 setModalProps({ confirmLoading: false }); 112 setModalProps({ confirmLoading: false });
110 current.value = 0; 113 current.value = 0;
111 isUpdate.value = data.isUpdate; 114 isUpdate.value = data.isUpdate;
112 isViewDetail.value = data.isView; 115 isViewDetail.value = data.isView;
113 - const res = data.record !== undefined ? await deviceConfigGetDetail(data.record.id) : {}; 116 + const res =
  117 + data.record !== undefined
  118 + ? await deviceConfigGetDetail(data.record.id)
  119 + : ({} as DeviceProfileDetail);
114 isEditId.value = data.record !== undefined ? data.record.id : null; 120 isEditId.value = data.record !== undefined ? data.record.id : null;
115 isEditCreatTime.value = data.record !== undefined ? data.record.createTime : null; 121 isEditCreatTime.value = data.record !== undefined ? data.record.createTime : null;
116 122
  123 + isDefault.value = res.default;
  124 +
117 const title = unref(isUpdate) ? '编辑产品' : '新增产品'; 125 const title = unref(isUpdate) ? '编辑产品' : '新增产品';
118 setModalProps({ title, showOkBtn: true, showCancelBtn: true }); 126 setModalProps({ title, showOkBtn: true, showCancelBtn: true });
119 if (unref(isUpdate)) { 127 if (unref(isUpdate)) {
@@ -183,6 +191,7 @@ @@ -183,6 +191,7 @@
183 ...{ profileData: !isEmptyObj ? transportConfData.profileData : null }, 191 ...{ profileData: !isEmptyObj ? transportConfData.profileData : null },
184 ...{ id: isUpdate.value ? isEditId.value : null }, 192 ...{ id: isUpdate.value ? isEditId.value : null },
185 ...{ createTime: isUpdate.value ? isEditCreatTime.value : null }, 193 ...{ createTime: isUpdate.value ? isEditCreatTime.value : null },
  194 + default: !!unref(isDefault),
186 }); 195 });
187 createMessage.success(isUpdate.value ? `编辑成功` : `新增成功`); 196 createMessage.success(isUpdate.value ? `编辑成功` : `新增成功`);
188 handleCancel(); 197 handleCancel();
@@ -70,7 +70,7 @@ @@ -70,7 +70,7 @@
70 formConfig: { 70 formConfig: {
71 labelWidth: 120, 71 labelWidth: 120,
72 schemas: searchFormSchema, 72 schemas: searchFormSchema,
73 - fieldMapToTime: [['sendTime', ['startTime', 'endTime'], 'YYYY-MM-DD HH:mm:ss']], 73 + fieldMapToTime: [['sendTime', ['startTime', 'endTime'], 'x']],
74 }, 74 },
75 useSearchForm: true, 75 useSearchForm: true,
76 showTableSetting: true, 76 showTableSetting: true,
@@ -67,7 +67,7 @@ @@ -67,7 +67,7 @@
67 formConfig: { 67 formConfig: {
68 labelWidth: 120, 68 labelWidth: 120,
69 schemas: searchFormSchema, 69 schemas: searchFormSchema,
70 - fieldMapToTime: [['sendTime', ['startTime', 'endTime'], 'YYYY-MM-DD HH:mm:ss']], 70 + fieldMapToTime: [['sendTime', ['startTime', 'endTime'], 'x']],
71 }, 71 },
72 useSearchForm: true, 72 useSearchForm: true,
73 showTableSetting: true, 73 showTableSetting: true,
@@ -79,7 +79,6 @@ @@ -79,7 +79,6 @@
79 debugMode, 79 debugMode,
80 name, 80 name,
81 }; 81 };
82 - console.log(value, 'value');  
83 await createRuleChine(value); 82 await createRuleChine(value);
84 createMessage.success('编辑成功'); 83 createMessage.success('编辑成功');
85 } 84 }
1 import { BasicColumn, FormSchema } from '/@/components/Table'; 1 import { BasicColumn, FormSchema } from '/@/components/Table';
2 import { transformTime } from '/@/hooks/web/useDateToLocaleString'; 2 import { transformTime } from '/@/hooks/web/useDateToLocaleString';
  3 +import { isObject, isString } from '/@/utils/is';
3 4
4 export const columns: BasicColumn[] = [ 5 export const columns: BasicColumn[] = [
5 { 6 {
@@ -55,6 +56,18 @@ export const exportJSONFile = (value: Recordable, name: string) => { @@ -55,6 +56,18 @@ export const exportJSONFile = (value: Recordable, name: string) => {
55 URL.revokeObjectURL(objectURL); 56 URL.revokeObjectURL(objectURL);
56 }; 57 };
57 58
  59 +export const paseJSON = (string: string) => {
  60 + let data = null;
  61 + let flag = false;
  62 + try {
  63 + if (!isString(string)) return { flag: false, data };
  64 + data = JSON.parse(string);
  65 + flag = true;
  66 + if (!isObject(data)) flag = false;
  67 + } catch (error) {}
  68 + return { flag, data };
  69 +};
  70 +
58 export enum RuleChainPermisssion { 71 export enum RuleChainPermisssion {
59 DETAIL = 'rule:chain:detail', 72 DETAIL = 'rule:chain:detail',
60 } 73 }
@@ -8,7 +8,7 @@ @@ -8,7 +8,7 @@
8 <Upload :show-upload-list="false" :customRequest="handleImport"> 8 <Upload :show-upload-list="false" :customRequest="handleImport">
9 <Button type="primary" :loading="importLoading"> 导入规则链 </Button> 9 <Button type="primary" :loading="importLoading"> 导入规则链 </Button>
10 </Upload> 10 </Upload>
11 - <!-- <Authority> 11 + <Authority>
12 <Popconfirm 12 <Popconfirm
13 title="您确定要批量删除数据" 13 title="您确定要批量删除数据"
14 ok-text="确定" 14 ok-text="确定"
@@ -17,7 +17,7 @@ @@ -17,7 +17,7 @@
17 > 17 >
18 <a-button color="error" :disabled="hasBatchDelete"> 批量删除 </a-button> 18 <a-button color="error" :disabled="hasBatchDelete"> 批量删除 </a-button>
19 </Popconfirm> 19 </Popconfirm>
20 - </Authority> --> 20 + </Authority>
21 </template> 21 </template>
22 <template #root="{ record }"> 22 <template #root="{ record }">
23 <Tag :color="record.root ? 'green' : 'red'"> {{ record.root ? '是' : '否' }}</Tag> 23 <Tag :color="record.root ? 'green' : 'red'"> {{ record.root ? '是' : '否' }}</Tag>
@@ -84,6 +84,7 @@ @@ -84,6 +84,7 @@
84 encode, 84 encode,
85 exportJSONFile, 85 exportJSONFile,
86 searchFormSchema, 86 searchFormSchema,
  87 + paseJSON,
87 } from './config/config.data'; 88 } from './config/config.data';
88 import { 89 import {
89 deleteRuleChine, 90 deleteRuleChine,
@@ -95,17 +96,45 @@ @@ -95,17 +96,45 @@
95 } from '/@/api/ruleengine/ruleengineApi'; 96 } from '/@/api/ruleengine/ruleengineApi';
96 import { useModal } from '/@/components/Modal'; 97 import { useModal } from '/@/components/Modal';
97 import { Authority } from '/@/components/Authority'; 98 import { Authority } from '/@/components/Authority';
98 - import { Tag, Button, Upload } from 'ant-design-vue'; 99 + import { Tag, Button, Upload, Popconfirm } from 'ant-design-vue';
99 import { RuleChainModal } from './component/index'; 100 import { RuleChainModal } from './component/index';
100 import { useMessage } from '/@/hooks/web/useMessage'; 101 import { useMessage } from '/@/hooks/web/useMessage';
101 import { usePermission } from '/@/hooks/web/usePermission'; 102 import { usePermission } from '/@/hooks/web/usePermission';
102 import { useRouter } from 'vue-router'; 103 import { useRouter } from 'vue-router';
103 import { ref } from 'vue'; 104 import { ref } from 'vue';
104 - import { isObject, isString } from '/@/utils/is'; 105 + import { isObject } from '/@/utils/is';
105 // import { ChainDetailDrawer } from './chainDetail/index'; 106 // import { ChainDetailDrawer } from './chainDetail/index';
106 // import { useDrawer } from '/@/components/Drawer'; 107 // import { useDrawer } from '/@/components/Drawer';
107 108
108 - const [registerTable, { reload, setProps }] = useTable({ 109 + const [registerModal, { openModal }] = useModal();
  110 + const { createMessage } = useMessage();
  111 + const { hasPermission } = usePermission();
  112 + const router = useRouter();
  113 +
  114 + const isEmptyObject = (value: any) => isObject(value) && !Object.keys(value).length;
  115 + const importLoading = ref<boolean>(false);
  116 + const hasBatchDelete = ref<boolean>(false);
  117 +
  118 + const beforeFetch = (params) => {
  119 + Reflect.set(params, 'page', params.page - 1);
  120 + Reflect.set(params, 'sortProperty', 'createdTime');
  121 + Reflect.set(params, 'sortOrder', 'DESC');
  122 + return params;
  123 + };
  124 +
  125 + const rowSelection = () => {
  126 + return {
  127 + type: 'checkbox',
  128 + getCheckboxProps: (record: Recordable) => {
  129 + return { disabled: record.root };
  130 + },
  131 + onChange(rowKeys: string[]) {
  132 + hasBatchDelete.value = rowKeys.length <= 0;
  133 + },
  134 + };
  135 + };
  136 +
  137 + const [registerTable, { reload, setProps, getSelectRowKeys, clearSelectedRowKeys }] = useTable({
109 title: '规则链库', 138 title: '规则链库',
110 api: getRuleChinsList, 139 api: getRuleChinsList,
111 rowKey: (record) => record.id.id, 140 rowKey: (record) => record.id.id,
@@ -123,19 +152,8 @@ @@ -123,19 +152,8 @@
123 pageField: 'page', 152 pageField: 'page',
124 listField: 'data', 153 listField: 'data',
125 }, 154 },
126 - beforeFetch(params) {  
127 - Reflect.set(params, 'page', params.page - 1);  
128 - Reflect.set(params, 'sortProperty', 'createdTime');  
129 - Reflect.set(params, 'sortOrder', 'DESC');  
130 - return params;  
131 - },  
132 - rowSelection: {  
133 - type: 'checkbox',  
134 - getCheckboxProps: (record: Recordable) => {  
135 - console.log(record, 'record');  
136 - return { disabled: record.root };  
137 - },  
138 - }, 155 + beforeFetch: (params) => beforeFetch(params),
  156 + rowSelection: rowSelection() as any,
139 actionColumn: { 157 actionColumn: {
140 width: 220, 158 width: 220,
141 title: '操作', 159 title: '操作',
@@ -145,10 +163,6 @@ @@ -145,10 +163,6 @@
145 }, 163 },
146 }); 164 });
147 165
148 - const [registerModal, { openModal }] = useModal();  
149 -  
150 - // const [registerDrawer, { openDrawer }] = useDrawer();  
151 -  
152 const handleSuccess = () => { 166 const handleSuccess = () => {
153 reload(); 167 reload();
154 }; 168 };
@@ -166,10 +180,6 @@ @@ -166,10 +180,6 @@
166 }); 180 });
167 }; 181 };
168 182
169 - const { createMessage } = useMessage();  
170 - const { hasPermission } = usePermission();  
171 - const router = useRouter();  
172 -  
173 const handleView = (record: Recordable) => { 183 const handleView = (record: Recordable) => {
174 const hasDetailPermission = hasPermission(RuleChainPermisssion.DETAIL); 184 const hasDetailPermission = hasPermission(RuleChainPermisssion.DETAIL);
175 if (hasDetailPermission) { 185 if (hasDetailPermission) {
@@ -178,30 +188,6 @@ @@ -178,30 +188,6 @@
178 } else createMessage.warning('没有权限'); 188 } else createMessage.warning('没有权限');
179 }; 189 };
180 190
181 - // const handleRowClick = (record) => {  
182 - // openDrawer(true, { record });  
183 - // console.log('点击行', record);  
184 - // };  
185 -  
186 - // const handleDetail = (record) => {  
187 - // console.log(record, '详情');  
188 - // };  
189 -  
190 - const paseJSON = (string: string) => {  
191 - let data = null;  
192 - let flag = false;  
193 - try {  
194 - if (!isString(string)) return { flag: false, data };  
195 - data = JSON.parse(string);  
196 - flag = true;  
197 - if (!isObject(data)) flag = false;  
198 - } catch (error) {}  
199 - return { flag, data };  
200 - };  
201 -  
202 - const isEmptyObject = (value: any) => isObject(value) && !Object.keys(value).length;  
203 -  
204 - const importLoading = ref<boolean>(false);  
205 const handleImport = (data: { file: File }) => { 191 const handleImport = (data: { file: File }) => {
206 const fileReader = new FileReader(); 192 const fileReader = new FileReader();
207 193
@@ -275,18 +261,25 @@ @@ -275,18 +261,25 @@
275 reload(); 261 reload();
276 }; 262 };
277 263
278 - const handleDeleteOrBatchDelete = async (record: Recordable) => { 264 + const handleDeleteOrBatchDelete = async (record: Recordable | null) => {
279 setProps({ 265 setProps({
280 loading: true, 266 loading: true,
281 }); 267 });
282 try { 268 try {
  269 + if (!record) {
  270 + const ids = getSelectRowKeys();
  271 + await Promise.all(ids.map((item) => deleteRuleChine(item)));
  272 + return;
  273 + }
283 await deleteRuleChine(record.id.id); 274 await deleteRuleChine(record.id.id);
284 - createMessage.success('删除成功');  
285 } finally { 275 } finally {
286 setProps({ 276 setProps({
287 loading: false, 277 loading: false,
288 }); 278 });
  279 +
  280 + createMessage.success('删除成功');
  281 + clearSelectedRowKeys();
  282 + reload();
289 } 283 }
290 - reload();  
291 }; 284 };
292 </script> 285 </script>
@@ -126,7 +126,10 @@ @@ -126,7 +126,10 @@
126 password: getDataFlowParams.password ? getDataFlowParams.password : undefined, 126 password: getDataFlowParams.password ? getDataFlowParams.password : undefined,
127 } 127 }
128 : undefined, 128 : undefined,
  129 + appendClientIdSuffix: getDataFlowParams.appendClientIdSuffix || undefined,
  130 + type: undefined,
129 }; 131 };
  132 +
130 const rest = isRabbitmq(getDataFlowMethod?.type) 133 const rest = isRabbitmq(getDataFlowMethod?.type)
131 ? await postAddConvertApi({ ...restData.data, ...data }) 134 ? await postAddConvertApi({ ...restData.data, ...data })
132 : await postAddConvertApi({ ...restData.data, ...data, configuration }); 135 : await postAddConvertApi({ ...restData.data, ...data, configuration });
1 import { Ref, toRaw, unref } from 'vue'; 1 import { Ref, toRaw, unref } from 'vue';
2 -import { BasicNodeBindData, NodeData } from '../types/node';  
3 -import { Elements, GraphNode } from '@vue-flow/core';  
4 -import { RuleChainType } from '../types/ruleNode'; 2 +import { BasicNodeBindData, EdgeData, NodeData } from '../types/node';
  3 +import { Elements, GraphEdge, GraphNode } from '@vue-flow/core';
  4 +import { ConnectionItemType, RuleChainType } from '../types/ruleNode';
5 import { allComponents } from '../packages'; 5 import { allComponents } from '../packages';
6 import { RuleNodeTypeEnum } from '../packages/index.type'; 6 import { RuleNodeTypeEnum } from '../packages/index.type';
7 import { buildUUID } from '/@/utils/uuid'; 7 import { buildUUID } from '/@/utils/uuid';
@@ -9,6 +9,9 @@ import { isNullOrUnDef } from '/@/utils/is'; @@ -9,6 +9,9 @@ import { isNullOrUnDef } from '/@/utils/is';
9 import { useAddNodes } from './useAddNodes'; 9 import { useAddNodes } from './useAddNodes';
10 import { useAddEdges } from './useAddEdges'; 10 import { useAddEdges } from './useAddEdges';
11 import { RuleChainEntityType } from '../enum/entity'; 11 import { RuleChainEntityType } from '../enum/entity';
  12 +import { EntryCategoryComponentEnum } from '../enum/category';
  13 +
  14 +const ignoreNodeKeys: string[] = [EntryCategoryComponentEnum.INPUT];
12 15
13 export function useBasicDataTransform() { 16 export function useBasicDataTransform() {
14 const nodeConfigMap = new Map<string, NodeData>(); 17 const nodeConfigMap = new Map<string, NodeData>();
@@ -24,7 +27,7 @@ export function useBasicDataTransform() { @@ -24,7 +27,7 @@ export function useBasicDataTransform() {
24 } 27 }
25 28
26 function mergeData(data: NodeData['data'], nodeData: NodeData, node: GraphNode) { 29 function mergeData(data: NodeData['data'], nodeData: NodeData, node: GraphNode) {
27 - const { x: layoutX, y: layoutY } = node.computedPosition; 30 + const { x: layoutX, y: layoutY } = node.position;
28 31
29 return { 32 return {
30 debugMode: !!data?.debugMode, 33 debugMode: !!data?.debugMode,
@@ -172,8 +175,126 @@ export function useBasicDataTransform() { @@ -172,8 +175,126 @@ export function useBasicDataTransform() {
172 175
173 initNodeConfigMap(); 176 initNodeConfigMap();
174 177
  178 + /**
  179 + * @description 保存连接信息
  180 + */
  181 + function getConnections(
  182 + nodesRef: Ref<GraphNode[]> | GraphNode[],
  183 + edges: Ref<GraphEdge[]> | GraphEdge[]
  184 + ) {
  185 + const nodeIndexMap = new Map();
  186 +
  187 + const connections: ConnectionItemType[] = [];
  188 +
  189 + unref(nodesRef).forEach((item, index) => {
  190 + nodeIndexMap.set(item.id, index);
  191 + });
  192 +
  193 + for (const item of unref(edges)) {
  194 + const { data, target, source } = item;
  195 + const { data: bindData } = data as EdgeData;
  196 + const { type } = bindData || {};
  197 + const fromIndex = nodeIndexMap.get(source);
  198 + const toIndex = nodeIndexMap.get(target);
  199 + type?.forEach((key) => {
  200 + connections.push({ fromIndex, toIndex, type: key });
  201 + });
  202 + }
  203 +
  204 + return connections;
  205 + }
  206 +
  207 + function getNodes(nodesRef: Ref<GraphNode[]> | GraphNode[], removeId: boolean) {
  208 + const nodes: BasicNodeBindData[] = [];
  209 +
  210 + for (const node of unref(nodesRef)) {
  211 + const nodeData = node.data as NodeData;
  212 +
  213 + if (ignoreNodeKeys.includes(nodeData.config?.key as string)) continue;
  214 +
  215 + const data = nodeData.data;
  216 +
  217 + nodes.push(
  218 + Object.assign(
  219 + mergeData(data, nodeData, node),
  220 + nodeData.created && !removeId
  221 + ? ({
  222 + id: { id: node.id, entityType: RuleChainEntityType.RULE_NODE },
  223 + } as BasicNodeBindData)
  224 + : {}
  225 + )
  226 + );
  227 + }
  228 +
  229 + return nodes;
  230 + }
  231 +
  232 + function getFirsetNodeIndex(
  233 + nodesRef: Ref<GraphNode[]> | GraphNode[],
  234 + edges: Ref<GraphEdge[]> | GraphEdge[]
  235 + ) {
  236 + const inputNode = unref(edges).find(
  237 + (item) => (item.sourceNode.data as NodeData).config?.key === EntryCategoryComponentEnum.INPUT
  238 + );
  239 +
  240 + if (inputNode) {
  241 + const targetId = inputNode.target;
  242 + const index = unref(nodesRef).findIndex((item) => item.id === targetId);
  243 + return index;
  244 + }
  245 + }
  246 +
  247 + function combineData(
  248 + nodesRef: Ref<GraphNode[]> | GraphNode[] = [],
  249 + edgesRef: Ref<GraphEdge[]> | GraphEdge[] = [],
  250 + removeId = false
  251 + ) {
  252 + const extraIgnoreNodeRef = unref(nodesRef).filter(
  253 + (item) => !ignoreNodeKeys.includes((item.data as NodeData).config?.key as string)
  254 + );
  255 +
  256 + const connections = getConnections(extraIgnoreNodeRef, edgesRef);
  257 +
  258 + const nodes = getNodes(extraIgnoreNodeRef, removeId);
  259 +
  260 + const firstNodeIndex = getFirsetNodeIndex(extraIgnoreNodeRef, edgesRef);
  261 +
  262 + return { connections, nodes, firstNodeIndex };
  263 + }
  264 +
  265 + function validateCanCreateRuleChain(
  266 + nodes: Ref<GraphNode[]> | GraphNode[] = [],
  267 + edges: Ref<GraphEdge[]> | GraphEdge[] = []
  268 + ) {
  269 + const rootNode: GraphNode[] = [];
  270 +
  271 + let flag = true;
  272 + for (const node of unref(nodes)) {
  273 + const list = unref(edges).filter(
  274 + (edge) => edge.source === node.id || edge.target === node.id
  275 + );
  276 +
  277 + if (!list.length) {
  278 + flag = false;
  279 + break;
  280 + }
  281 +
  282 + if (!list.filter((edge) => edge.target === node.id).length) {
  283 + if (!rootNode.length) rootNode.push(node);
  284 + else {
  285 + flag = false;
  286 + break;
  287 + }
  288 + }
  289 + }
  290 +
  291 + return { flag, firstNode: rootNode[0] || null };
  292 + }
  293 +
175 return { 294 return {
176 mergeData, 295 mergeData,
  296 + combineData,
177 deconstructionData, 297 deconstructionData,
  298 + validateCanCreateRuleChain,
178 }; 299 };
179 } 300 }
@@ -3,11 +3,19 @@ import { getRuleNodeCache, setRuleNodeCache } from './useRuleChainCache'; @@ -3,11 +3,19 @@ import { getRuleNodeCache, setRuleNodeCache } from './useRuleChainCache';
3 import { RuleContextMenuEnum } from './useRuleChainContextMenu'; 3 import { RuleContextMenuEnum } from './useRuleChainContextMenu';
4 import { useAddNodes } from './useAddNodes'; 4 import { useAddNodes } from './useAddNodes';
5 import { buildUUID } from '/@/utils/uuid'; 5 import { buildUUID } from '/@/utils/uuid';
6 -import { toRaw, unref } from 'vue'; 6 +import { Ref, toRaw, unref } from 'vue';
7 import { useSaveAndRedo } from './useSaveAndRedo'; 7 import { useSaveAndRedo } from './useSaveAndRedo';
8 import { isUnDef } from '/@/utils/is'; 8 import { isUnDef } from '/@/utils/is';
9 import { useAddEdges } from './useAddEdges'; 9 import { useAddEdges } from './useAddEdges';
10 import { EdgeData } from '../types/node'; 10 import { EdgeData } from '../types/node';
  11 +import { CreateNodeModal } from '../src/components/CreateNodeModal';
  12 +import { CreateEdgeModal } from '../src/components/CreateEdgeModal';
  13 +import { UpdateNodeDrawer } from '../src/components/UpdateNodeDrawer';
  14 +import { UpdateEdgeDrawer } from '../src/components/UpdateEdgeDrawer';
  15 +import { CreateRuleChainModal } from '../src/components/CreateRuleChainModal';
  16 +import { useBasicDataTransform } from './useBasicDataTransform';
  17 +import { cloneDeep } from 'lodash-es';
  18 +import { useNewNode } from './useNewNode';
11 19
12 interface HandleContextMenuActionParamsType { 20 interface HandleContextMenuActionParamsType {
13 menuType: RuleContextMenuEnum; 21 menuType: RuleContextMenuEnum;
@@ -16,18 +24,58 @@ interface HandleContextMenuActionParamsType { @@ -16,18 +24,58 @@ interface HandleContextMenuActionParamsType {
16 node?: GraphNode; 24 node?: GraphNode;
17 edge?: GraphEdge; 25 edge?: GraphEdge;
18 useSaveAndRedoActionType?: ReturnType<typeof useSaveAndRedo>; 26 useSaveAndRedoActionType?: ReturnType<typeof useSaveAndRedo>;
  27 + modalActionType: {
  28 + createNodeModalActionType: Ref<Nullable<InstanceType<typeof CreateNodeModal>>>;
  29 + createEdgeModalActionType: Ref<Nullable<InstanceType<typeof CreateEdgeModal>>>;
  30 + updateNodeDrawerActionType: Ref<Nullable<InstanceType<typeof UpdateNodeDrawer>>>;
  31 + updateEdgeDrawerActionType: Ref<Nullable<InstanceType<typeof UpdateEdgeDrawer>>>;
  32 + createRuleChainModalActionType: Ref<Nullable<InstanceType<typeof CreateRuleChainModal>>>;
  33 + };
  34 +}
  35 +
  36 +export function transformToRuleChain(
  37 + nodesRef: Ref<GraphNode[]> | GraphNode[] = [],
  38 + edgesRef: Ref<GraphEdge[]> | GraphEdge[] = []
  39 +) {
  40 + const nodeMap = new Map<string, GraphNode>();
  41 + const { combineData, validateCanCreateRuleChain } = useBasicDataTransform();
  42 +
  43 + nodesRef = cloneDeep(unref(nodesRef));
  44 + edgesRef = cloneDeep(unref(edgesRef));
  45 +
  46 + unref(nodesRef).forEach((node) => nodeMap.set(node.id, node));
  47 + const outputEdges = unref(edgesRef).filter((edge) => !nodeMap.has(edge.target));
  48 + const outputEdgesId = outputEdges.map((edge) => edge.id);
  49 +
  50 + const { getOutputNodeConfig } = useNewNode();
  51 + const outputNode = outputEdges.map((edge) => {
  52 + const id = buildUUID();
  53 + const name = (edge.data as EdgeData).data?.type?.join(' / ') || '';
  54 + edge.target = id;
  55 + return getOutputNodeConfig(name, edge.targetNode.position, id);
  56 + });
  57 +
  58 + nodesRef = [...nodesRef, ...(outputNode as GraphNode[])];
  59 +
  60 + const { connections, nodes } = combineData(nodesRef, edgesRef, true);
  61 +
  62 + const { firstNode } = validateCanCreateRuleChain(nodesRef, edgesRef);
  63 +
  64 + const firstNodeIndex = nodesRef.findIndex((node) => node.id === firstNode.id);
  65 +
  66 + return { connections, nodes, firstNodeIndex, outputEdgesId };
19 } 67 }
20 68
21 export const NODE_WIDTH = 176; 69 export const NODE_WIDTH = 176;
22 export const NODE_HEIGHT = 48; 70 export const NODE_HEIGHT = 48;
23 71
24 -function getElementsCenter(nodes: GraphNode[]) { 72 +function getElementsCenter(nodes: Ref<GraphNode[]> | GraphNode[] = []) {
25 let leftTopX: number | undefined; 73 let leftTopX: number | undefined;
26 let leftTopY: number | undefined; 74 let leftTopY: number | undefined;
27 let rightBottomX: number | undefined; 75 let rightBottomX: number | undefined;
28 let rightBottomY: number | undefined; 76 let rightBottomY: number | undefined;
29 77
30 - for (const node of nodes) { 78 + for (const node of unref(nodes)) {
31 const { position } = node; 79 const { position } = node;
32 const { x, y } = position; 80 const { x, y } = position;
33 if (isUnDef(leftTopX)) { 81 if (isUnDef(leftTopX)) {
@@ -200,6 +248,37 @@ export function useContextMenuAction() { @@ -200,6 +248,37 @@ export function useContextMenuAction() {
200 useSaveAndRedoActionType?.handleRedoChange(flowActionType!); 248 useSaveAndRedoActionType?.handleRedoChange(flowActionType!);
201 }; 249 };
202 250
  251 + const createRuleChain = async (_params: HandleContextMenuActionParamsType) => {
  252 + // const { useSaveAndRedoActionType, modalActionType, flowActionType } = params;
  253 + // const { createRuleChainModalActionType } = modalActionType;
  254 + // const result = (await unref(createRuleChainModalActionType)?.openCreateRuleChainModal()) as {
  255 + // name: string;
  256 + // additionalInfo: { description: string };
  257 + // };
  258 + // const ruleChainDetail = await saveRuleChainDetail(
  259 + // Object.assign(result, { debugger: false, type: 'CORE' }) as Partial<RuleChainDetail>
  260 + // );
  261 + // const selectedNodes = unref(flowActionType?.getSelectedNodes);
  262 + // const selectedEdges = unref(flowActionType?.getSelectedEdges);
  263 + // const { firstNodeIndex, connections, nodes, outputEdgesId } = transformToRuleChain(
  264 + // selectedNodes,
  265 + // selectedEdges
  266 + // );
  267 + // await saveRuleChainData({
  268 + // firstNodeIndex,
  269 + // connections,
  270 + // nodes,
  271 + // ruleChainId: ruleChainDetail.id,
  272 + // });
  273 + // const outputEdges = outputEdgesId.map((id) => flowActionType?.findEdge(id));
  274 + // console.log(getElementsCenter(unref(selectedNodes)));
  275 + // const { originX, originY } = getElementsCenter(unref(selectedNodes));
  276 + // const {} = useNewNode();
  277 + // flowActionType?.removeNodes(selectedNodes || []);
  278 + // flowActionType?.removeEdges(selectedEdges || []);
  279 + // useSaveAndRedoActionType?.triggerChange();
  280 + };
  281 +
203 const handleContextMenuAction = (params: HandleContextMenuActionParamsType) => { 282 const handleContextMenuAction = (params: HandleContextMenuActionParamsType) => {
204 const { menuType } = params; 283 const { menuType } = params;
205 284
@@ -213,6 +292,7 @@ export function useContextMenuAction() { @@ -213,6 +292,7 @@ export function useContextMenuAction() {
213 [RuleContextMenuEnum.SELECT_COPY]: selectCopy, 292 [RuleContextMenuEnum.SELECT_COPY]: selectCopy,
214 [RuleContextMenuEnum.APPLY_CHANGE]: applyChange, 293 [RuleContextMenuEnum.APPLY_CHANGE]: applyChange,
215 [RuleContextMenuEnum.UNDO_CHANGE]: undoChange, 294 [RuleContextMenuEnum.UNDO_CHANGE]: undoChange,
  295 + [RuleContextMenuEnum.CREATE_RULE_CHAIN]: createRuleChain,
216 }; 296 };
217 297
218 if (handlerMapping[menuType]) { 298 if (handlerMapping[menuType]) {
@@ -5,6 +5,7 @@ import type { CreateNodeModal } from '../src/components/CreateNodeModal'; @@ -5,6 +5,7 @@ import type { CreateNodeModal } from '../src/components/CreateNodeModal';
5 import type { CreateEdgeModal } from '../src/components/CreateEdgeModal'; 5 import type { CreateEdgeModal } from '../src/components/CreateEdgeModal';
6 import { UpdateNodeDrawer } from '../src/components/UpdateNodeDrawer'; 6 import { UpdateNodeDrawer } from '../src/components/UpdateNodeDrawer';
7 import { UpdateEdgeDrawer } from '../src/components/UpdateEdgeDrawer'; 7 import { UpdateEdgeDrawer } from '../src/components/UpdateEdgeDrawer';
  8 +import { CreateRuleChainModal } from '../src/components/CreateRuleChainModal';
8 9
9 const SYMBOL = Symbol('flow-context'); 10 const SYMBOL = Symbol('flow-context');
10 11
@@ -30,6 +31,11 @@ interface FlowContextOptionsType { @@ -30,6 +31,11 @@ interface FlowContextOptionsType {
30 updateEdgeDrawerActionType: Ref<Nullable<InstanceType<typeof UpdateEdgeDrawer>>>; 31 updateEdgeDrawerActionType: Ref<Nullable<InstanceType<typeof UpdateEdgeDrawer>>>;
31 32
32 /** 33 /**
  34 + * @description 创建规则链 actions
  35 + */
  36 + createRuleChainModalActionType: Ref<Nullable<InstanceType<typeof CreateRuleChainModal>>>;
  37 +
  38 + /**
33 * @description vue flow store 39 * @description vue flow store
34 */ 40 */
35 flowActionType: VueFlowStore; 41 flowActionType: VueFlowStore;
src/views/rule/designer/hook/useNewNode.ts renamed from src/views/rule/designer/hook/useInputNode.ts
  1 +import { XYPosition } from '@vue-flow/core';
1 import { Config as InputConfig } from '../packages/Entry/Input/config'; 2 import { Config as InputConfig } from '../packages/Entry/Input/config';
  3 +import { Config as OutputConfig } from '../packages/Flow/Output/config';
  4 +import { Config as RuleChainConfig } from '../packages/Flow/RuleChain/config';
2 import { useAddNodes } from './useAddNodes'; 5 import { useAddNodes } from './useAddNodes';
3 6
4 -export function useInputNode() { 7 +export function useNewNode() {
5 const getInputNodeConfig = (id?: string) => { 8 const getInputNodeConfig = (id?: string) => {
6 const { getAddNodesParams } = useAddNodes(); 9 const { getAddNodesParams } = useAddNodes();
7 10
@@ -20,5 +23,38 @@ export function useInputNode() { @@ -20,5 +23,38 @@ export function useInputNode() {
20 return newNode; 23 return newNode;
21 }; 24 };
22 25
23 - return { getInputNodeConfig }; 26 + const getOutputNodeConfig = (name: string, position: XYPosition, id?: string) => {
  27 + const { getAddNodesParams } = useAddNodes();
  28 +
  29 + const newNode = getAddNodesParams(
  30 + position,
  31 + {
  32 + ...new OutputConfig(),
  33 + data: {
  34 + name,
  35 + },
  36 + },
  37 + { id, draggable: false, selectable: false }
  38 + );
  39 +
  40 + return newNode;
  41 + };
  42 +
  43 + const getRuleChainNodeConfig = (name: string, position: XYPosition, id?: string) => {
  44 + const { getAddNodesParams } = useAddNodes();
  45 + const newNode = getAddNodesParams(
  46 + position,
  47 + {
  48 + ...new RuleChainConfig(),
  49 + data: { name },
  50 + },
  51 + {
  52 + id,
  53 + }
  54 + );
  55 +
  56 + return newNode;
  57 + };
  58 +
  59 + return { getInputNodeConfig, getOutputNodeConfig, getRuleChainNodeConfig };
24 } 60 }
@@ -27,14 +27,19 @@ import { RuleChainDetail } from '../types/ruleNode'; @@ -27,14 +27,19 @@ import { RuleChainDetail } from '../types/ruleNode';
27 import { useContextMenuAction } from './useContextMenuAction'; 27 import { useContextMenuAction } from './useContextMenuAction';
28 import { useSaveAndRedo } from './useSaveAndRedo'; 28 import { useSaveAndRedo } from './useSaveAndRedo';
29 import { EntryCategoryComponentEnum } from '../enum/category'; 29 import { EntryCategoryComponentEnum } from '../enum/category';
  30 +import { CreateRuleChainModal } from '../src/components/CreateRuleChainModal';
  31 +import { useBasicDataTransform } from './useBasicDataTransform';
30 32
31 interface UseRuleFlowOptionsType { 33 interface UseRuleFlowOptionsType {
32 id: string; 34 id: string;
33 ruleChainDetail: Ref<RuleChainDetail | undefined>; 35 ruleChainDetail: Ref<RuleChainDetail | undefined>;
34 - createNodeModalActionType: Ref<Nullable<InstanceType<typeof CreateNodeModal>>>;  
35 - createEdgeModalActionType: Ref<Nullable<InstanceType<typeof CreateEdgeModal>>>;  
36 - updateNodeDrawerActionType: Ref<Nullable<InstanceType<typeof UpdateNodeDrawer>>>;  
37 - updateEdgeDrawerActionType: Ref<Nullable<InstanceType<typeof UpdateEdgeDrawer>>>; 36 + modalActionType: {
  37 + createNodeModalActionType: Ref<Nullable<InstanceType<typeof CreateNodeModal>>>;
  38 + createEdgeModalActionType: Ref<Nullable<InstanceType<typeof CreateEdgeModal>>>;
  39 + updateNodeDrawerActionType: Ref<Nullable<InstanceType<typeof UpdateNodeDrawer>>>;
  40 + updateEdgeDrawerActionType: Ref<Nullable<InstanceType<typeof UpdateEdgeDrawer>>>;
  41 + createRuleChainModalActionType: Ref<Nullable<InstanceType<typeof CreateRuleChainModal>>>;
  42 + };
38 useSaveAndRedoActionType: ReturnType<typeof useSaveAndRedo>; 43 useSaveAndRedoActionType: ReturnType<typeof useSaveAndRedo>;
39 } 44 }
40 45
@@ -47,14 +52,10 @@ const validateInputAndOutput: ValidConnectionFunc = (connection: Connection) => @@ -47,14 +52,10 @@ const validateInputAndOutput: ValidConnectionFunc = (connection: Connection) =>
47 }; 52 };
48 53
49 export function useRuleFlow(options: UseRuleFlowOptionsType) { 54 export function useRuleFlow(options: UseRuleFlowOptionsType) {
50 - const {  
51 - id,  
52 - ruleChainDetail,  
53 - createEdgeModalActionType,  
54 - updateEdgeDrawerActionType,  
55 - updateNodeDrawerActionType,  
56 - useSaveAndRedoActionType,  
57 - } = options; 55 + const { id, ruleChainDetail, modalActionType, useSaveAndRedoActionType } = options;
  56 +
  57 + const { createEdgeModalActionType, updateEdgeDrawerActionType, updateNodeDrawerActionType } =
  58 + modalActionType;
58 59
59 const { triggerChange } = useSaveAndRedoActionType; 60 const { triggerChange } = useSaveAndRedoActionType;
60 61
@@ -162,29 +163,35 @@ export function useRuleFlow(options: UseRuleFlowOptionsType) { @@ -162,29 +163,35 @@ export function useRuleFlow(options: UseRuleFlowOptionsType) {
162 163
163 const { handleContextMenuAction } = useContextMenuAction(); 164 const { handleContextMenuAction } = useContextMenuAction();
164 165
  166 + const { validateCanCreateRuleChain } = useBasicDataTransform();
  167 +
165 onNodeContextMenu(async (params) => { 168 onNodeContextMenu(async (params) => {
166 const menuType = params.node.selected 169 const menuType = params.node.selected
167 ? await createElementsSelectedContextMenu( 170 ? await createElementsSelectedContextMenu(
168 params, 171 params,
169 unref(useSaveAndRedoActionType.changeMarker), 172 unref(useSaveAndRedoActionType.changeMarker),
170 - validateSelectionElementsCanCreateRuleChain() 173 + validateCanCreateRuleChain(
  174 + flowActionType.getSelectedNodes,
  175 + flowActionType.getSelectedEdges
  176 + ).flag
171 ) 177 )
172 : await createNodeContextMenu(params); 178 : await createNodeContextMenu(params);
173 179
174 - if (menuType) {  
175 - if (menuType === RuleContextMenuEnum.DETAIL) {  
176 - handleUpdateNode(params.node);  
177 - return;  
178 - } 180 + if (!menuType) return;
179 181
180 - handleContextMenuAction({  
181 - menuType,  
182 - flowActionType,  
183 - event: params.event,  
184 - node: params.node,  
185 - useSaveAndRedoActionType,  
186 - }); 182 + if (menuType === RuleContextMenuEnum.DETAIL) {
  183 + handleUpdateNode(params.node);
  184 + return;
187 } 185 }
  186 +
  187 + handleContextMenuAction({
  188 + menuType,
  189 + flowActionType,
  190 + event: params.event,
  191 + node: params.node,
  192 + useSaveAndRedoActionType,
  193 + modalActionType,
  194 + });
188 }); 195 });
189 196
190 onEdgeContextMenu(async (params) => { 197 onEdgeContextMenu(async (params) => {
@@ -207,6 +214,7 @@ export function useRuleFlow(options: UseRuleFlowOptionsType) { @@ -207,6 +214,7 @@ export function useRuleFlow(options: UseRuleFlowOptionsType) {
207 event: params.event, 214 event: params.event,
208 useSaveAndRedoActionType, 215 useSaveAndRedoActionType,
209 edge: params.edge, 216 edge: params.edge,
  217 + modalActionType,
210 }); 218 });
211 } 219 }
212 }); 220 });
@@ -216,21 +224,25 @@ export function useRuleFlow(options: UseRuleFlowOptionsType) { @@ -216,21 +224,25 @@ export function useRuleFlow(options: UseRuleFlowOptionsType) {
216 ? await createElementsSelectedContextMenu( 224 ? await createElementsSelectedContextMenu(
217 getCreatePanelContextMenuParams(params), 225 getCreatePanelContextMenuParams(params),
218 unref(useSaveAndRedoActionType.changeMarker), 226 unref(useSaveAndRedoActionType.changeMarker),
219 - validateSelectionElementsCanCreateRuleChain() 227 + validateCanCreateRuleChain(
  228 + flowActionType.getSelectedNodes,
  229 + flowActionType.getSelectedEdges
  230 + ).flag
220 ) 231 )
221 : await createPanelContextMenu( 232 : await createPanelContextMenu(
222 getCreatePanelContextMenuParams(params), 233 getCreatePanelContextMenuParams(params),
223 unref(useSaveAndRedoActionType.changeMarker) 234 unref(useSaveAndRedoActionType.changeMarker)
224 ); 235 );
225 236
226 - if (menuType) {  
227 - handleContextMenuAction({  
228 - menuType,  
229 - flowActionType,  
230 - event: params,  
231 - useSaveAndRedoActionType,  
232 - });  
233 - } 237 + if (!menuType) return;
  238 +
  239 + handleContextMenuAction({
  240 + menuType,
  241 + flowActionType,
  242 + event: params,
  243 + useSaveAndRedoActionType,
  244 + modalActionType,
  245 + });
234 }); 246 });
235 247
236 /** 248 /**
@@ -382,17 +394,5 @@ export function useRuleFlow(options: UseRuleFlowOptionsType) { @@ -382,17 +394,5 @@ export function useRuleFlow(options: UseRuleFlowOptionsType) {
382 } as NodeMouseEvent; 394 } as NodeMouseEvent;
383 } 395 }
384 396
385 - function validateSelectionElementsCanCreateRuleChain() {  
386 - const nodes = unref(flowActionType.getSelectedNodes);  
387 - const edges = unref(flowActionType.getSelectedEdges);  
388 -  
389 - for (const node of nodes) {  
390 - const index = edges.findIndex((edge) => edge.target === node.id || edge.source === node.id);  
391 - if (!~index) return false;  
392 - }  
393 -  
394 - return true;  
395 - }  
396 -  
397 return { flowActionType }; 397 return { flowActionType };
398 } 398 }
1 -import type { VueFlowStore, Getters, Elements } from '@vue-flow/core';  
2 -import { ComputedRef, computed, ref, unref } from 'vue';  
3 -import { BasicNodeBindData, EdgeData, NodeData } from '../types/node';  
4 -import { EntryCategoryComponentEnum } from '../enum/category'; 1 +import type { VueFlowStore, Elements } from '@vue-flow/core';
  2 +import { computed, ref, unref } from 'vue';
  3 +import { BasicNodeBindData, NodeData } from '../types/node';
5 import { useBasicDataTransform } from './useBasicDataTransform'; 4 import { useBasicDataTransform } from './useBasicDataTransform';
6 import { 5 import {
7 getRuleChainData, 6 getRuleChainData,
@@ -10,15 +9,13 @@ import { @@ -10,15 +9,13 @@ import {
10 saveRuleChainDetail, 9 saveRuleChainDetail,
11 } from '/@/api/ruleDesigner'; 10 } from '/@/api/ruleDesigner';
12 import { ConnectionItemType, RuleChainDetail, RuleChainType } from '../types/ruleNode'; 11 import { ConnectionItemType, RuleChainDetail, RuleChainType } from '../types/ruleNode';
13 -import { useInputNode } from './useInputNode'; 12 +import { useNewNode } from './useNewNode';
14 import { buildUUID } from '/@/utils/uuid'; 13 import { buildUUID } from '/@/utils/uuid';
15 import { useRoute, useRouter } from 'vue-router'; 14 import { useRoute, useRouter } from 'vue-router';
16 import { RuleChainEntityType } from '../enum/entity'; 15 import { RuleChainEntityType } from '../enum/entity';
17 import { PageEnum } from '/@/enums/pageEnum'; 16 import { PageEnum } from '/@/enums/pageEnum';
18 import { clearRuleChainImportCache, getRuleChainImportCache } from './useRuleChainCache'; 17 import { clearRuleChainImportCache, getRuleChainImportCache } from './useRuleChainCache';
19 18
20 -const ignoreNodeKeys: string[] = [EntryCategoryComponentEnum.INPUT];  
21 -  
22 export function useSaveAndRedo() { 19 export function useSaveAndRedo() {
23 const changeMarker = ref(false); 20 const changeMarker = ref(false);
24 21
@@ -38,7 +35,7 @@ export function useSaveAndRedo() { @@ -38,7 +35,7 @@ export function useSaveAndRedo() {
38 35
39 const ruleChainDetail = ref<RuleChainDetail>(); 36 const ruleChainDetail = ref<RuleChainDetail>();
40 37
41 - const { mergeData, deconstructionData } = useBasicDataTransform(); 38 + const { combineData, deconstructionData } = useBasicDataTransform();
42 39
43 const triggerChange = () => { 40 const triggerChange = () => {
44 changeMarker.value = true; 41 changeMarker.value = true;
@@ -48,90 +45,14 @@ export function useSaveAndRedo() { @@ -48,90 +45,14 @@ export function useSaveAndRedo() {
48 changeMarker.value = false; 45 changeMarker.value = false;
49 }; 46 };
50 47
51 - /**  
52 - * @description 保存连接信息  
53 - */  
54 - function getConnections(  
55 - nodesRef: ComputedRef<Getters['getNodes']> | Getters['getNodes'],  
56 - edges: ComputedRef<Getters['getEdges']>  
57 - ) {  
58 - const nodeIndexMap = new Map();  
59 -  
60 - const connections: ConnectionItemType[] = [];  
61 -  
62 - unref(nodesRef).forEach((item, index) => {  
63 - nodeIndexMap.set(item.id, index);  
64 - });  
65 -  
66 - for (const item of unref(edges)) {  
67 - const { data, target, source } = item;  
68 - const { data: bindData } = data as EdgeData;  
69 - const { type } = bindData || {};  
70 - const fromIndex = nodeIndexMap.get(source);  
71 - const toIndex = nodeIndexMap.get(target);  
72 - type?.forEach((key) => {  
73 - connections.push({ fromIndex, toIndex, type: key });  
74 - });  
75 - }  
76 -  
77 - return connections;  
78 - }  
79 -  
80 - function getNodes(nodesRef: ComputedRef<Getters['getNodes']> | Getters['getNodes']) {  
81 - const nodes: BasicNodeBindData[] = [];  
82 -  
83 - for (const node of unref(nodesRef)) {  
84 - const nodeData = node.data as NodeData;  
85 -  
86 - if (ignoreNodeKeys.includes(nodeData.config?.key as string)) continue;  
87 -  
88 - const data = nodeData.data;  
89 -  
90 - nodes.push(  
91 - Object.assign(  
92 - mergeData(data, nodeData, node),  
93 - nodeData.created  
94 - ? ({  
95 - id: { id: node.id, entityType: RuleChainEntityType.RULE_NODE },  
96 - } as BasicNodeBindData)  
97 - : {}  
98 - )  
99 - );  
100 - }  
101 -  
102 - return nodes;  
103 - }  
104 -  
105 - function getFirsetNodeIndex(  
106 - nodesRef: ComputedRef<Getters['getNodes']> | Getters['getNodes'],  
107 - edges: ComputedRef<Getters['getEdges']>  
108 - ) {  
109 - const inputNode = unref(edges).find(  
110 - (item) => (item.sourceNode.data as NodeData).config?.key === EntryCategoryComponentEnum.INPUT  
111 - );  
112 -  
113 - if (inputNode) {  
114 - const targetId = inputNode.target;  
115 - const index = unref(nodesRef).findIndex((item) => item.id === targetId);  
116 - return index;  
117 - }  
118 - }  
119 -  
120 const handleApplyChange = (flowActionType: VueFlowStore) => { 48 const handleApplyChange = (flowActionType: VueFlowStore) => {
121 if (!unref(changeMarker)) return; 49 if (!unref(changeMarker)) return;
122 50
123 - const edgesRef = flowActionType.getEdges;  
124 -  
125 - const extraIgnoreNodeRef = unref(flowActionType.getNodes).filter(  
126 - (item) => !ignoreNodeKeys.includes((item.data as NodeData).config?.key as string) 51 + const { connections, nodes, firstNodeIndex } = combineData(
  52 + flowActionType.getNodes,
  53 + flowActionType.getEdges
127 ); 54 );
128 55
129 - const connections = getConnections(extraIgnoreNodeRef, edgesRef);  
130 -  
131 - const nodes = getNodes(extraIgnoreNodeRef);  
132 -  
133 - const firstNodeIndex = getFirsetNodeIndex(extraIgnoreNodeRef, edgesRef);  
134 -  
135 handleSaveRuleChain(connections, nodes, firstNodeIndex); 56 handleSaveRuleChain(connections, nodes, firstNodeIndex);
136 }; 57 };
137 58
@@ -211,7 +132,7 @@ export function useSaveAndRedo() { @@ -211,7 +132,7 @@ export function useSaveAndRedo() {
211 function parseRuleChain(ruleChain: RuleChainType) { 132 function parseRuleChain(ruleChain: RuleChainType) {
212 const inputId = buildUUID(); 133 const inputId = buildUUID();
213 134
214 - const { getInputNodeConfig } = useInputNode(); 135 + const { getInputNodeConfig } = useNewNode();
215 136
216 const value = deconstructionData(ruleChain, inputId); 137 const value = deconstructionData(ruleChain, inputId);
217 138
@@ -19,10 +19,11 @@ @@ -19,10 +19,11 @@
19 import { CreateEdgeModal } from './src/components/CreateEdgeModal'; 19 import { CreateEdgeModal } from './src/components/CreateEdgeModal';
20 import { useFullScreen } from './hook/useFullScreen'; 20 import { useFullScreen } from './hook/useFullScreen';
21 import { useSaveAndRedo } from './hook/useSaveAndRedo'; 21 import { useSaveAndRedo } from './hook/useSaveAndRedo';
  22 + import { NodeData } from './types/node';
22 import { Icon } from '/@/components/Icon'; 23 import { Icon } from '/@/components/Icon';
23 import { UpdateNodeDrawer } from './src/components/UpdateNodeDrawer'; 24 import { UpdateNodeDrawer } from './src/components/UpdateNodeDrawer';
24 import { UpdateEdgeDrawer } from './src/components/UpdateEdgeDrawer'; 25 import { UpdateEdgeDrawer } from './src/components/UpdateEdgeDrawer';
25 - import { NodeData } from './types/node'; 26 + import { CreateRuleChainModal } from './src/components/CreateRuleChainModal';
26 27
27 const getId = Number(Math.random().toString().substring(2)).toString(16); 28 const getId = Number(Math.random().toString().substring(2)).toString(16);
28 29
@@ -36,6 +37,9 @@ @@ -36,6 +37,9 @@
36 37
37 const updateEdgeDrawerActionType = ref<Nullable<InstanceType<typeof UpdateEdgeDrawer>>>(null); 38 const updateEdgeDrawerActionType = ref<Nullable<InstanceType<typeof UpdateEdgeDrawer>>>(null);
38 39
  40 + const createRuleChainModalActionType =
  41 + ref<Nullable<InstanceType<typeof CreateRuleChainModal>>>(null);
  42 +
39 const flowElRef = ref<Nullable<FlowElRef>>(null); 43 const flowElRef = ref<Nullable<FlowElRef>>(null);
40 44
41 const elements = ref([]); 45 const elements = ref([]);
@@ -57,11 +61,14 @@ @@ -57,11 +61,14 @@
57 const { flowActionType } = useRuleFlow({ 61 const { flowActionType } = useRuleFlow({
58 id: getId, 62 id: getId,
59 ruleChainDetail, 63 ruleChainDetail,
60 - createNodeModalActionType,  
61 - createEdgeModalActionType,  
62 - updateEdgeDrawerActionType,  
63 - updateNodeDrawerActionType,  
64 useSaveAndRedoActionType, 64 useSaveAndRedoActionType,
  65 + modalActionType: {
  66 + createNodeModalActionType,
  67 + createEdgeModalActionType,
  68 + updateEdgeDrawerActionType,
  69 + updateNodeDrawerActionType,
  70 + createRuleChainModalActionType,
  71 + },
65 }); 72 });
66 73
67 const { handleOnDragOver, handleOnDrop } = useDragCreate({ 74 const { handleOnDragOver, handleOnDrop } = useDragCreate({
@@ -85,6 +92,8 @@ @@ -85,6 +92,8 @@
85 92
86 const handleDeleteSelectionElements = () => { 93 const handleDeleteSelectionElements = () => {
87 flowActionType.removeNodes(unref(flowActionType.getSelectedNodes)); 94 flowActionType.removeNodes(unref(flowActionType.getSelectedNodes));
  95 + flowActionType.removeEdges(unref(flowActionType.getSelectedEdges));
  96 + useSaveAndRedoActionType.triggerChange?.();
88 }; 97 };
89 98
90 onMounted(() => { 99 onMounted(() => {
@@ -97,6 +106,7 @@ @@ -97,6 +106,7 @@
97 createNodeModalActionType, 106 createNodeModalActionType,
98 updateEdgeDrawerActionType, 107 updateEdgeDrawerActionType,
99 updateNodeDrawerActionType, 108 updateNodeDrawerActionType,
  109 + createRuleChainModalActionType,
100 flowActionType, 110 flowActionType,
101 triggerChange, 111 triggerChange,
102 }); 112 });
@@ -177,6 +187,8 @@ @@ -177,6 +187,8 @@
177 187
178 <UpdateEdgeDrawer ref="updateEdgeDrawerActionType" /> 188 <UpdateEdgeDrawer ref="updateEdgeDrawerActionType" />
179 <UpdateNodeDrawer ref="updateNodeDrawerActionType" /> 189 <UpdateNodeDrawer ref="updateNodeDrawerActionType" />
  190 +
  191 + <CreateRuleChainModal ref="createRuleChainModalActionType" />
180 </main> 192 </main>
181 </template> 193 </template>
182 194
  1 +import { FormSchema } from '/@/components/Form';
  2 +
  3 +export enum FormFieldsEnum {
  4 + NAME = 'name',
  5 + DESCRIPTION = 'description',
  6 + ADDITIONAL_INFO = 'additionalInfo',
  7 +}
  8 +
  9 +export enum FormFieldsNameEnum {
  10 + NAME = '名称',
  11 + DESCRIPTION = '说明',
  12 +}
  13 +
  14 +export const formSchemas: FormSchema[] = [
  15 + {
  16 + field: FormFieldsEnum.NAME,
  17 + label: FormFieldsNameEnum.NAME,
  18 + component: 'Input',
  19 + required: true,
  20 + componentProps: {
  21 + placeholder: `请输入${FormFieldsNameEnum.NAME}`,
  22 + },
  23 + },
  24 + {
  25 + field: FormFieldsEnum.DESCRIPTION,
  26 + label: FormFieldsNameEnum.DESCRIPTION,
  27 + component: 'InputTextArea',
  28 + componentProps: {
  29 + placeholder: `请输入${FormFieldsNameEnum.DESCRIPTION}`,
  30 + },
  31 + },
  32 +];
  1 +export { default as CreateRuleChainModal } from './index.vue';
  1 +<script setup lang="ts">
  2 + import { BasicModal, useModalInner } from '/@/components/Modal';
  3 + import { BasicForm, useForm } from '/@/components/Form';
  4 + import { formSchemas, FormFieldsEnum } from './config';
  5 + import { ref } from 'vue';
  6 +
  7 + interface SaveParamsType {
  8 + name: string;
  9 + additional: { description: string };
  10 + }
  11 +
  12 + const visible = ref(false);
  13 +
  14 + const [register, { closeModal }] = useModalInner();
  15 +
  16 + const [registerForm, { getFieldsValue, validate }] = useForm({
  17 + schemas: formSchemas,
  18 + showActionButtonGroup: false,
  19 + layout: 'vertical',
  20 + });
  21 +
  22 + let resolveFn: undefined | ((value: SaveParamsType) => any);
  23 +
  24 + const openCreateRuleChainModal = async (): Promise<{
  25 + name: string;
  26 + additionalInfo: { description: string };
  27 + }> => {
  28 + visible.value = true;
  29 + return new Promise((resolve) => {
  30 + resolveFn = resolve;
  31 + });
  32 + };
  33 +
  34 + const handleSave = async () => {
  35 + await validate();
  36 + const value = getFieldsValue();
  37 + resolveFn?.({
  38 + name: value[FormFieldsEnum.NAME],
  39 + additional: { description: value[FormFieldsEnum.DESCRIPTION] || '' },
  40 + });
  41 + closeModal();
  42 + };
  43 +
  44 + defineExpose({ openCreateRuleChainModal });
  45 +</script>
  46 +
  47 +<template>
  48 + <BasicModal
  49 + v-model:visible="visible"
  50 + @register="register"
  51 + @ok="handleSave"
  52 + title="Create nested rule chain"
  53 + >
  54 + <BasicForm @register="registerForm" />
  55 + </BasicModal>
  56 +</template>
@@ -24,7 +24,7 @@ export interface RuleChainDetail { @@ -24,7 +24,7 @@ export interface RuleChainDetail {
24 createdTime: number; 24 createdTime: number;
25 additionalInfo: AdditionalInfo; 25 additionalInfo: AdditionalInfo;
26 tenantId: Id; 26 tenantId: Id;
27 - name: Id; 27 + name: string;
28 type: string; 28 type: string;
29 firstRuleNodeId: Id; 29 firstRuleNodeId: Id;
30 root: boolean; 30 root: boolean;
@@ -35,6 +35,7 @@ @@ -35,6 +35,7 @@
35 const { proxy } = getCurrentInstance() as any; 35 const { proxy } = getCurrentInstance() as any;
36 const getChildData = ref(null); 36 const getChildData = ref(null);
37 const editGetId: any = ref(''); 37 const editGetId: any = ref('');
  38 + const createTime = ref<string>('');
38 const [registerForm, { validate, resetFields, setFieldsValue, updateSchema }] = useForm({ 39 const [registerForm, { validate, resetFields, setFieldsValue, updateSchema }] = useForm({
39 schemas: formSchema, 40 schemas: formSchema,
40 showActionButtonGroup: false, 41 showActionButtonGroup: false,
@@ -57,6 +58,7 @@ @@ -57,6 +58,7 @@
57 await setFieldsValue({ 58 await setFieldsValue({
58 ...data.record, 59 ...data.record,
59 }); 60 });
  61 + createTime.value = data.record.createdTime;
60 updateStatusSchema(true); 62 updateStatusSchema(true);
61 } 63 }
62 }); 64 });
@@ -79,7 +81,7 @@ @@ -79,7 +81,7 @@
79 ]); 81 ]);
80 }; 82 };
81 83
82 - const getAllFieldsFunc = async () => { 84 + const getAllFieldsFunc = async (isUpdate?: boolean) => {
83 getValuesFormData = await validate(); 85 getValuesFormData = await validate();
84 if (!getValuesFormData) return; 86 if (!getValuesFormData) return;
85 let getChildValues = proxy.$refs.getChildData.getAllFields(); 87 let getChildValues = proxy.$refs.getChildData.getAllFields();
@@ -90,10 +92,9 @@ @@ -90,10 +92,9 @@
90 id: unref(isUpdate) ? editGetId.value : '', 92 id: unref(isUpdate) ? editGetId.value : '',
91 }; 93 };
92 94
93 - const createTime = {  
94 - createdTime: Date.now(), 95 + const createTime1 = {
  96 + createdTime: isUpdate ? unref(createTime) : Date.now(),
95 }; 97 };
96 -  
97 Object.assign( 98 Object.assign(
98 postAllData, 99 postAllData,
99 { 100 {
@@ -101,7 +102,7 @@ @@ -101,7 +102,7 @@
101 }, 102 },
102 getValuesFormData, 103 getValuesFormData,
103 id, 104 id,
104 - createTime 105 + createTime1
105 ); 106 );
106 if (!unref(isUpdate)) { 107 if (!unref(isUpdate)) {
107 delete postAllData.id; 108 delete postAllData.id;
@@ -119,7 +120,7 @@ @@ -119,7 +120,7 @@
119 emit('success'); 120 emit('success');
120 resetFields(); 121 resetFields();
121 } else { 122 } else {
122 - await getAllFieldsFunc(); 123 + await getAllFieldsFunc(true);
123 await saveTenantProfileApi(postAllData); 124 await saveTenantProfileApi(postAllData);
124 createMessage.success('租户配置编辑成功'); 125 createMessage.success('租户配置编辑成功');
125 closeDrawer(); 126 closeDrawer();
@@ -173,10 +173,12 @@ @@ -173,10 +173,12 @@
173 const { codeType, customCommand } = unref(getDesign) || {}; 173 const { codeType, customCommand } = unref(getDesign) || {};
174 const { transportType } = customCommand || {}; 174 const { transportType } = customCommand || {};
175 const sendValue = ref<any>(unref(sliderValue)); 175 const sendValue = ref<any>(unref(sliderValue));
  176 + const isModbusCommand = ref<boolean>(false);
176 if (transportType == 'TCP' && codeType == TaskTypeEnum.MODBUS_RTU) { 177 if (transportType == 'TCP' && codeType == TaskTypeEnum.MODBUS_RTU) {
177 sendValue.value = await getSendValue(unref(sliderValue)); 178 sendValue.value = await getSendValue(unref(sliderValue));
  179 + isModbusCommand.value = true;
178 } 180 }
179 - const flag = await sendCommand(props.config.option, unref(sendValue), true); 181 + const flag = await sendCommand(props.config.option, unref(sendValue), unref(isModbusCommand));
180 flag 182 flag
181 ? ((sliderValue.value = unref(sliderValue)), 183 ? ((sliderValue.value = unref(sliderValue)),
182 (oldSliderValue.value = sliderValue.value), 184 (oldSliderValue.value = sliderValue.value),
@@ -159,7 +159,6 @@ @@ -159,7 +159,6 @@
159 }); 159 });
160 160
161 const resize = async () => { 161 const resize = async () => {
162 - console.log(123321);  
163 await nextTick(); 162 await nextTick();
164 163
165 // 修改echarts大小 164 // 修改echarts大小
@@ -12,7 +12,7 @@ @@ -12,7 +12,7 @@
12 import { useModalInner } from '/@/components/Modal'; 12 import { useModalInner } from '/@/components/Modal';
13 import { getAllDeviceByOrg } from '/@/api/dataBoard'; 13 import { getAllDeviceByOrg } from '/@/api/dataBoard';
14 import { useHistoryData } from '/@/views/device/list/hook/useHistoryData'; 14 import { useHistoryData } from '/@/views/device/list/hook/useHistoryData';
15 - import { BasicTable, useTable } from '/@/components/Table'; 15 + import { BasicColumn, BasicTable, useTable } from '/@/components/Table';
16 import { formatToDateTime } from '/@/utils/dateUtil'; 16 import { formatToDateTime } from '/@/utils/dateUtil';
17 import { 17 import {
18 ModeSwitchButton, 18 ModeSwitchButton,
@@ -112,18 +112,14 @@ @@ -112,18 +112,14 @@
112 return mapping; 112 return mapping;
113 }); 113 });
114 114
115 - const [registerTable] = useTable({  
116 - showIndexColumn: false,  
117 - showTableSetting: false,  
118 - dataSource: historyData,  
119 - maxHeight: 300,  
120 - size: 'small',  
121 - columns: [ 115 + const sortOrder = ref<any>('descend');
  116 + const columns: BasicColumn[] | any = computed(() => {
  117 + return [
122 { 118 {
123 title: '属性', 119 title: '属性',
124 dataIndex: 'name', 120 dataIndex: 'name',
125 - format: (value) => {  
126 - return unref(getIdentifierNameMapping)[value]; 121 + format: (text) => {
  122 + return unref(getIdentifierNameMapping)[text];
127 }, 123 },
128 }, 124 },
129 { 125 {
@@ -136,10 +132,47 @@ @@ -136,10 +132,47 @@
136 format: (val) => { 132 format: (val) => {
137 return formatToDateTime(val, 'YYYY-MM-DD HH:mm:ss'); 133 return formatToDateTime(val, 'YYYY-MM-DD HH:mm:ss');
138 }, 134 },
  135 + sorter: true,
  136 + sortOrder: unref(sortOrder),
  137 + sortDirections: ['descend', 'ascend', 'descend'],
139 }, 138 },
140 - ], 139 + ];
141 }); 140 });
142 141
  142 + const [registerTable, { setColumns }] = useTable({
  143 + showIndexColumn: false,
  144 + showTableSetting: false,
  145 + dataSource: historyData,
  146 + maxHeight: 300,
  147 + size: 'small',
  148 + columns: unref(columns),
  149 + });
  150 +
  151 + const getTableList = async (orderBy?: string) => {
  152 + // 表单验证
  153 + await method.validate();
  154 + const value = method.getFieldsValue();
  155 + const searchParams = getSearchParams(value);
  156 + if (!hasDeviceAttr()) return;
  157 + // 发送请求
  158 + loading.value = true;
  159 + const res = await getDeviceHistoryInfo(searchParams, orderBy);
  160 + historyData.value = getTableHistoryData(res);
  161 + loading.value = false;
  162 + };
  163 +
  164 + const handleTableChange = async (_pag, _filters, sorter: any) => {
  165 + sortOrder.value = sorter.order;
  166 + await setColumns(unref(columns));
  167 + if (sorter.field == 'ts') {
  168 + if (sorter.order == 'descend') {
  169 + getTableList('DESC');
  170 + } else {
  171 + getTableList('ASC');
  172 + }
  173 + }
  174 + };
  175 +
143 const getDeviceDataKey = async (record: DeviceOption) => { 176 const getDeviceDataKey = async (record: DeviceOption) => {
144 const { organizationId, value } = record; 177 const { organizationId, value } = record;
145 try { 178 try {
@@ -260,7 +293,7 @@ @@ -260,7 +293,7 @@
260 :show-ok-btn="false" 293 :show-ok-btn="false"
261 cancel-text="关闭" 294 cancel-text="关闭"
262 width="70%" 295 width="70%"
263 - title="选择时间" 296 + title="历史趋势"
264 > 297 >
265 <section 298 <section
266 class="flex flex-col p-4 h-full w-full min-w-7/10" 299 class="flex flex-col p-4 h-full w-full min-w-7/10"
@@ -295,7 +328,11 @@ @@ -295,7 +328,11 @@
295 v-show="!isNull" 328 v-show="!isNull"
296 /> 329 />
297 330
298 - <BasicTable v-show="mode === EnumTableChartMode.TABLE" @register="registerTable"> 331 + <BasicTable
  332 + v-show="mode === EnumTableChartMode.TABLE"
  333 + @change="handleTableChange"
  334 + @register="registerTable"
  335 + >
299 <template #toolbar> 336 <template #toolbar>
300 <div class="flex h-70px items-center justify-end p-2"> 337 <div class="flex h-70px items-center justify-end p-2">
301 <ModeSwitchButton 338 <ModeSwitchButton