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 12 };
13 13
14 14 // 获取历史数据
15   -export const getDeviceHistoryInfo = (params: Recordable, orderBy) => {
  15 +export const getDeviceHistoryInfo = (params: Recordable, orderBy?: string) => {
16 16 return defHttp.get<HistoryData>(
17 17 {
18 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 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 230
231 231 function setFormModel(key: string, value: any) {
232 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 239 function handleEnterPress(e: KeyboardEvent) {
... ...
... ... @@ -242,7 +242,7 @@
242 242 const value = target ? (isCheck ? target.checked : target.value) : e;
243 243 props.setFormModel(field, value);
244 244 },
245   - onBlur: (...args) => {
  245 + onBlur: (...args: any[]) => {
246 246 unref(getComponentsProps)?.onBlur?.(...args);
247 247 props.validateFields([field], { triggerName: 'blur' }).catch((_) => {});
248 248 },
... ...
... ... @@ -38,7 +38,7 @@ export const basicProps = {
38 38 },
39 39 autoSetPlaceHolder: propTypes.bool.def(true),
40 40 // 在INPUT组件上单击回车时,是否自动提交
41   - autoSubmitOnEnter: propTypes.bool.def(false),
  41 + autoSubmitOnEnter: propTypes.bool.def(true),
42 42 submitOnReset: propTypes.bool,
43 43 size: propTypes.oneOf(['default', 'small', 'large']).def('default'),
44 44 // 禁用表单
... ...
... ... @@ -52,6 +52,7 @@ export function useBatchDelete(
52 52 if (record) {
53 53 await deleteFn([record.id]);
54 54 createMessage.success('删除成功');
  55 + selectedRowIds.value = [];
55 56 } else {
56 57 await deleteFn(selectedRowIds.value);
57 58 createMessage.success('批量删除成功');
... ...
... ... @@ -116,7 +116,7 @@
116 116 const { organizationIdTreeRef, resetFn } = useResetOrganizationTree(searchInfo);
117 117 const [registerModal, { openModal }] = useModal();
118 118 // 表格hooks
119   - const [registerTable, { reload, setProps }] = useTable({
  119 + const [registerTable, { reload, setProps, clearSelectedRowKeys }] = useTable({
120 120 title: '视频列表',
121 121 api: cameraPage,
122 122 columns,
... ... @@ -145,6 +145,7 @@
145 145 // 刷新
146 146 const handleSuccess = () => {
147 147 reload();
  148 + clearSelectedRowKeys();
148 149 };
149 150 const { hasBatchDelete, handleDeleteOrBatchDelete, selectionOptions } = useBatchDelete(
150 151 deleteCameraManage,
... ...
... ... @@ -2,9 +2,10 @@
2 2 <div ref="chartRef" :style="{ height, width }"></div>
3 3 </template>
4 4 <script lang="ts" setup>
5   - import { ref, Ref, withDefaults, onMounted, watch } from 'vue';
  5 + import { ref, Ref, onMounted, watch } from 'vue';
6 6 import { useECharts } from '/@/hooks/web/useECharts';
7 7 import { getTrendData } from '/@/api/dashboard';
  8 + import { getDateByShortcutQueryKey, ShortcutQueryKeyEnum } from '../hooks/useDate';
8 9
9 10 interface Props {
10 11 width?: string;
... ... @@ -77,11 +78,8 @@
77 78 const chartRef = ref<HTMLDivElement | null>(null);
78 79 const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>);
79 80 onMounted(async () => {
80   - const endTs = Date.now();
81 81 const res = await getTrendData({
82   - startTs: endTs - 2592000000,
83   - endTs,
84   - interval: 86400000,
  82 + ...getDateByShortcutQueryKey(ShortcutQueryKeyEnum.LATEST_30_DAY),
85 83 trend: 'CUSTOMER_TREND',
86 84 });
87 85
... ...
... ... @@ -95,6 +95,20 @@
95 95 >
96 96 <template #extra>
97 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 112 <template v-for="(item, index) in TenantOrCustomerDateList" :key="item.value">
99 113 <span
100 114 @click="quickQueryTenantOrCustomerTime(index, item.value)"
... ...
... ... @@ -80,6 +80,20 @@
80 80 >
81 81 <template #extra>
82 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 97 <template v-for="(item, index) in TenantOrCustomerDateList" :key="item.value">
84 98 <span
85 99 @click="quickQueryTenantOrCustomerTime(index, item.value, 'tenant')"
... ... @@ -445,7 +459,7 @@
445 459 if (activeIndex.value === index) return;
446 460 activeIndex.value = index;
447 461 dateValue.value = '';
448   - console.log(interval);
  462 +
449 463 if (isCustomer) {
450 464 if (activeKey.value === '1') {
451 465 const data = await getTrendData({
... ...
... ... @@ -2,9 +2,10 @@
2 2 <div ref="chartRef" :style="{ height, width }"></div>
3 3 </template>
4 4 <script lang="ts" setup>
5   - import { ref, Ref, withDefaults, onMounted, watch } from 'vue';
  5 + import { ref, Ref, onMounted, watch } from 'vue';
6 6 import { useECharts } from '/@/hooks/web/useECharts';
7 7 import { getTrendData } from '/@/api/dashboard/index';
  8 + import { getDateByShortcutQueryKey, ShortcutQueryKeyEnum } from '../hooks/useDate';
8 9 interface Props {
9 10 width?: string;
10 11 height?: string;
... ... @@ -77,11 +78,8 @@
77 78 const chartRef = ref<HTMLDivElement | null>(null);
78 79 const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>);
79 80 onMounted(async () => {
80   - const endTs = Date.now();
81 81 const res = await getTrendData({
82   - startTs: endTs - 2592000000,
83   - endTs,
84   - interval: 86400000,
  82 + ...getDateByShortcutQueryKey(ShortcutQueryKeyEnum.LATEST_30_DAY),
85 83 trend: 'TENANT_TREND',
86 84 });
87 85 const transferResult = res.map((item) => [item.ts, item.value]);
... ...
1   -import { formatToDateTime } from '/@/utils/dateUtil';
  1 +import { dateUtil, formatToDateTime } from '/@/utils/dateUtil';
2 2 import { ref } from 'vue';
3 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 41 export function useDate() {
6 42 const tenantDateValue = ref([]);
7 43 const customerDateValue = ref([]);
... ... @@ -10,26 +46,23 @@ export function useDate() {
10 46 const activeTenantIndex = ref(0);
11 47 const activeCustomerIndex = ref(0);
12 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 55 async function quickQueryTenantOrCustomerTime(
20 56 index: number,
21   - value: number,
  57 + value: ShortcutQueryKeyEnum,
22 58 flag: 'tenant' | 'customer'
23 59 ) {
24   - const endTs = Date.now();
25 60 if (flag === 'tenant') {
26 61 if (activeTenantIndex.value === index) return;
27 62 activeTenantIndex.value = index;
28 63 tenantDateValue.value = [];
29 64 const res = await getTrendData({
30   - startTs: endTs - value,
31   - endTs,
32   - interval: value === 2592000000 ? 86400000 : value === 7776000000 ? 172800000 : 2592000000,
  65 + ...getDateByShortcutQueryKey(value),
33 66 trend: 'TENANT_TREND',
34 67 });
35 68 tenantTrendList.value = res.map((item) => [item.ts, item.value]);
... ... @@ -38,9 +71,7 @@ export function useDate() {
38 71 activeCustomerIndex.value = index;
39 72 customerDateValue.value = [];
40 73 const res = await getTrendData({
41   - startTs: endTs - value,
42   - endTs,
43   - interval: value === 2592000000 ? 86400000 : value === 7776000000 ? 172800000 : 2592000000,
  74 + ...getDateByShortcutQueryKey(value),
44 75 trend: 'CUSTOMER_TREND',
45 76 });
46 77 customerTrendList.value = res.map((item) => [item.ts, item.value]);
... ...
... ... @@ -7,7 +7,7 @@ import { JSONEditor } from '/@/components/CodeEditor';
7 7 import { DeviceTypeEnum } from '/@/api/device/model/deviceModel';
8 8 import { getModelServices } from '/@/api/device/modelOfMatter';
9 9 import { ModelOfMatterParams } from '/@/api/device/model/modelOfMatterModel';
10   -import { toRaw, unref } from 'vue';
  10 +import { nextTick, toRaw, unref } from 'vue';
11 11 import ObjectModelValidateForm from '/@/components/Form/src/externalCompns/components/ObjectModelValidateForm/ObjectModelValidateForm.vue';
12 12 import { CommandDeliveryWayEnum, ServiceCallTypeEnum } from '/@/enums/toolEnum';
13 13 import { TaskTypeEnum } from '/@/views/task/center/config';
... ... @@ -246,8 +246,9 @@ export const step1Schemas: FormSchema[] = [
246 246 required: true,
247 247 component: 'ApiSelect',
248 248 ifShow: ({ values }) => values.deviceType === 'SENSOR' && values.organizationId,
249   - componentProps: ({ formModel }) => {
  249 + componentProps: ({ formModel, formActionType }) => {
250 250 const { organizationId, transportType } = formModel;
  251 + const { validateFields } = formActionType;
251 252 if (![organizationId, transportType].every(Boolean)) return {};
252 253 return {
253 254 api: async (params: Recordable) => {
... ... @@ -266,6 +267,10 @@ export const step1Schemas: FormSchema[] = [
266 267 },
267 268 valueField: 'tbDeviceId',
268 269 labelField: 'alias',
  270 + onChange: async () => {
  271 + await nextTick();
  272 + validateFields(['gatewayId']);
  273 + },
269 274 };
270 275 },
271 276 },
... ...
1 1 <script lang="ts" setup>
2 2 import { Upload, Button } from 'ant-design-vue';
3 3 import { InboxOutlined } from '@ant-design/icons-vue';
4   - import { computed, ref } from 'vue';
  4 + import { computed } from 'vue';
5 5 import StepContainer from './StepContainer.vue';
6 6 import XLSX, { CellObject } from 'xlsx';
7 7 import { basicProps } from './props';
8 8 import { UploadFileParseValue } from './type';
  9 + import { useMessage } from '/@/hooks/web/useMessage';
9 10
10 11 const props = defineProps({
11 12 ...basicProps,
  13 + fileList: {
  14 + required: true,
  15 + type: Array as PropType<(Record<'uid' | 'name', string> & File)[]>,
  16 + },
12 17 value: {
13 18 require: true,
14 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 25 interface FileRequestParams {
23 26 file: File & { uid: string };
... ... @@ -48,23 +51,30 @@
48 51 return new Promise((resolve, reject) => {
49 52 const fileReader = new FileReader();
50 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 80 fileReader.onerror = () => {
... ... @@ -75,19 +85,19 @@
75 85 };
76 86
77 87 const handleParseFile = async ({ file, onSuccess, onError }: FileRequestParams) => {
78   - fileList.value = [];
79 88 const value = await readFile(file);
80 89 if (!value) {
81 90 onError();
82 91 return;
83 92 }
84   - fileList.value = [file];
  93 +
  94 + emit('update:fileList', [file]);
85 95 emit('update:value', value);
86 96 onSuccess({}, file);
87 97 };
88 98
89 99 const canGoNextStep = computed(() => {
90   - return !!fileList.value.length;
  100 + return !!props.fileList.length;
91 101 });
92 102
93 103 const handlePreviousStep = () => {
... ... @@ -97,16 +107,22 @@
97 107 const handleNextStep = () => {
98 108 props.goNextStep?.();
99 109 };
  110 +
  111 + const handleRemove = () => {
  112 + emit('update:fileList', []);
  113 + return true;
  114 + };
100 115 </script>
101 116
102 117 <template>
103 118 <StepContainer>
104 119 <div class="">设备文件</div>
105 120 <Upload.Dragger
106   - v-model:fileList="fileList"
  121 + :fileList="fileList"
107 122 :customRequest="handleParseFile"
108 123 accept=".csv"
109 124 name="file"
  125 + :remove="handleRemove"
110 126 >
111 127 <section class="cursor-pointer flex flex-col justify-center items-center">
112 128 <InboxOutlined class="text-[4rem] !text-blue-400" />
... ...
... ... @@ -17,6 +17,8 @@
17 17
18 18 const basicInfo = ref({});
19 19
  20 + const fileList = ref<(File & Record<'uid' | 'name', string>)[]>([]);
  21 +
20 22 const fileParseValue = ref<UploadFileParseValue>({ header: [], content: [] });
21 23
22 24 const columnConfiguration = ref<Record<'type', string>[]>([]);
... ... @@ -61,6 +63,7 @@
61 63
62 64 const reset = () => {
63 65 basicInfo.value = {};
  66 + fileList.value = [];
64 67 fileParseValue.value = { header: [], content: [] };
65 68 columnConfiguration.value = [];
66 69 importResult.value = {} as unknown as ImportDeviceResponse;
... ... @@ -109,6 +112,7 @@
109 112 />
110 113 <ImportCsv
111 114 v-if="StepsEnum[item] === StepsEnum.IMPORT_FILE && StepsEnum[item] === currentStep"
  115 + v-model:fileList="fileList"
112 116 v-model:value="fileParseValue"
113 117 :go-next-step="goNextStep"
114 118 :go-previous-step="goPreviousStep"
... ...
... ... @@ -11,7 +11,7 @@
11 11 import { ColEx } from '/@/components/Form/src/types';
12 12 import { useHistoryData } from '../../hook/useHistoryData';
13 13 import { formatToDateTime } from '/@/utils/dateUtil';
14   - import { useTable, BasicTable } from '/@/components/Table';
  14 + import { useTable, BasicTable, BasicColumn } from '/@/components/Table';
15 15 import { DataTypeEnum } from '/@/components/Form/src/externalCompns/components/StructForm/config';
16 16 import {
17 17 ModeSwitchButton,
... ... @@ -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 67 title: '属性',
72 68 dataIndex: 'name',
... ... @@ -84,18 +80,50 @@
84 80 format: (val) => {
85 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 122 if (sorter.field == 'ts') {
95 123 if (sorter.order == 'descend') {
96   - openHistoryPanel('ASC');
  124 + getTableList('DESC');
97 125 } else {
98   - openHistoryPanel('DESC');
  126 + getTableList('ASC');
99 127 }
100 128 }
101 129 };
... ... @@ -214,7 +242,7 @@
214 242 setOptions(setChartOptions(res, selectedKeys));
215 243 };
216 244
217   - const switchMode = (flag: EnumTableChartMode) => {
  245 + const switchMode = async (flag: EnumTableChartMode) => {
218 246 mode.value = flag;
219 247 };
220 248
... ...
... ... @@ -12,6 +12,23 @@ export interface BasicCreateFormParams {
12 12
13 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 32 export const useGenDynamicForm = () => {
16 33 const createInputNumber = ({
17 34 identifier,
... ... @@ -30,19 +47,19 @@ export const useGenDynamicForm = () => {
30 47 type: 'number',
31 48 trigger: 'change',
32 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 54 return Promise.resolve(value);
40 55 },
41 56 },
42 57 ],
43 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 63 step,
47 64 placeholder: `请输入${functionName}`,
48 65 precision: type === DataTypeEnum.IS_NUMBER_INT ? 0 : 2,
... ... @@ -136,7 +153,7 @@ export const useGenDynamicForm = () => {
136 153 fieldTypeMap.clear();
137 154 const formSchema = schemas.map((item) => {
138 155 const { functionName, identifier, dataType } = item;
139   - console.log(item, 'item');
  156 +
140 157 const { type } = dataType || {};
141 158
142 159 fieldTypeMap.set(identifier!, dataType!.type);
... ...
... ... @@ -84,6 +84,7 @@
84 84 import DeviceConfigurationStep from './step/DeviceConfigurationStep.vue';
85 85 import TransportConfigurationStep from './step/TransportConfigurationStep.vue';
86 86 import PhysicalModelManagementStep from './step/PhysicalModelManagementStep.vue';
  87 + import { DeviceProfileDetail } from '/@/api/device/model/deviceConfigModel';
87 88
88 89 const emits = defineEmits(['success', 'register']);
89 90 const activeKey = ref('1');
... ... @@ -105,15 +106,22 @@
105 106 });
106 107 const transportTypeStr = ref('');
107 108 const dynamicWidth = ref('55rem');
  109 +
  110 + const isDefault = ref(false);
108 111 const [register, { closeModal, setModalProps }] = useModalInner(async (data) => {
109 112 setModalProps({ confirmLoading: false });
110 113 current.value = 0;
111 114 isUpdate.value = data.isUpdate;
112 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 120 isEditId.value = data.record !== undefined ? data.record.id : null;
115 121 isEditCreatTime.value = data.record !== undefined ? data.record.createTime : null;
116 122
  123 + isDefault.value = res.default;
  124 +
117 125 const title = unref(isUpdate) ? '编辑产品' : '新增产品';
118 126 setModalProps({ title, showOkBtn: true, showCancelBtn: true });
119 127 if (unref(isUpdate)) {
... ... @@ -183,6 +191,7 @@
183 191 ...{ profileData: !isEmptyObj ? transportConfData.profileData : null },
184 192 ...{ id: isUpdate.value ? isEditId.value : null },
185 193 ...{ createTime: isUpdate.value ? isEditCreatTime.value : null },
  194 + default: !!unref(isDefault),
186 195 });
187 196 createMessage.success(isUpdate.value ? `编辑成功` : `新增成功`);
188 197 handleCancel();
... ...
... ... @@ -70,7 +70,7 @@
70 70 formConfig: {
71 71 labelWidth: 120,
72 72 schemas: searchFormSchema,
73   - fieldMapToTime: [['sendTime', ['startTime', 'endTime'], 'YYYY-MM-DD HH:mm:ss']],
  73 + fieldMapToTime: [['sendTime', ['startTime', 'endTime'], 'x']],
74 74 },
75 75 useSearchForm: true,
76 76 showTableSetting: true,
... ...
... ... @@ -67,7 +67,7 @@
67 67 formConfig: {
68 68 labelWidth: 120,
69 69 schemas: searchFormSchema,
70   - fieldMapToTime: [['sendTime', ['startTime', 'endTime'], 'YYYY-MM-DD HH:mm:ss']],
  70 + fieldMapToTime: [['sendTime', ['startTime', 'endTime'], 'x']],
71 71 },
72 72 useSearchForm: true,
73 73 showTableSetting: true,
... ...
... ... @@ -79,7 +79,6 @@
79 79 debugMode,
80 80 name,
81 81 };
82   - console.log(value, 'value');
83 82 await createRuleChine(value);
84 83 createMessage.success('编辑成功');
85 84 }
... ...
1 1 import { BasicColumn, FormSchema } from '/@/components/Table';
2 2 import { transformTime } from '/@/hooks/web/useDateToLocaleString';
  3 +import { isObject, isString } from '/@/utils/is';
3 4
4 5 export const columns: BasicColumn[] = [
5 6 {
... ... @@ -55,6 +56,18 @@ export const exportJSONFile = (value: Recordable, name: string) => {
55 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 71 export enum RuleChainPermisssion {
59 72 DETAIL = 'rule:chain:detail',
60 73 }
... ...
... ... @@ -8,7 +8,7 @@
8 8 <Upload :show-upload-list="false" :customRequest="handleImport">
9 9 <Button type="primary" :loading="importLoading"> 导入规则链 </Button>
10 10 </Upload>
11   - <!-- <Authority>
  11 + <Authority>
12 12 <Popconfirm
13 13 title="您确定要批量删除数据"
14 14 ok-text="确定"
... ... @@ -17,7 +17,7 @@
17 17 >
18 18 <a-button color="error" :disabled="hasBatchDelete"> 批量删除 </a-button>
19 19 </Popconfirm>
20   - </Authority> -->
  20 + </Authority>
21 21 </template>
22 22 <template #root="{ record }">
23 23 <Tag :color="record.root ? 'green' : 'red'"> {{ record.root ? '是' : '否' }}</Tag>
... ... @@ -84,6 +84,7 @@
84 84 encode,
85 85 exportJSONFile,
86 86 searchFormSchema,
  87 + paseJSON,
87 88 } from './config/config.data';
88 89 import {
89 90 deleteRuleChine,
... ... @@ -95,17 +96,45 @@
95 96 } from '/@/api/ruleengine/ruleengineApi';
96 97 import { useModal } from '/@/components/Modal';
97 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 100 import { RuleChainModal } from './component/index';
100 101 import { useMessage } from '/@/hooks/web/useMessage';
101 102 import { usePermission } from '/@/hooks/web/usePermission';
102 103 import { useRouter } from 'vue-router';
103 104 import { ref } from 'vue';
104   - import { isObject, isString } from '/@/utils/is';
  105 + import { isObject } from '/@/utils/is';
105 106 // import { ChainDetailDrawer } from './chainDetail/index';
106 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 138 title: '规则链库',
110 139 api: getRuleChinsList,
111 140 rowKey: (record) => record.id.id,
... ... @@ -123,19 +152,8 @@
123 152 pageField: 'page',
124 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 157 actionColumn: {
140 158 width: 220,
141 159 title: '操作',
... ... @@ -145,10 +163,6 @@
145 163 },
146 164 });
147 165
148   - const [registerModal, { openModal }] = useModal();
149   -
150   - // const [registerDrawer, { openDrawer }] = useDrawer();
151   -
152 166 const handleSuccess = () => {
153 167 reload();
154 168 };
... ... @@ -166,10 +180,6 @@
166 180 });
167 181 };
168 182
169   - const { createMessage } = useMessage();
170   - const { hasPermission } = usePermission();
171   - const router = useRouter();
172   -
173 183 const handleView = (record: Recordable) => {
174 184 const hasDetailPermission = hasPermission(RuleChainPermisssion.DETAIL);
175 185 if (hasDetailPermission) {
... ... @@ -178,30 +188,6 @@
178 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 191 const handleImport = (data: { file: File }) => {
206 192 const fileReader = new FileReader();
207 193
... ... @@ -275,18 +261,25 @@
275 261 reload();
276 262 };
277 263
278   - const handleDeleteOrBatchDelete = async (record: Recordable) => {
  264 + const handleDeleteOrBatchDelete = async (record: Recordable | null) => {
279 265 setProps({
280 266 loading: true,
281 267 });
282 268 try {
  269 + if (!record) {
  270 + const ids = getSelectRowKeys();
  271 + await Promise.all(ids.map((item) => deleteRuleChine(item)));
  272 + return;
  273 + }
283 274 await deleteRuleChine(record.id.id);
284   - createMessage.success('删除成功');
285 275 } finally {
286 276 setProps({
287 277 loading: false,
288 278 });
  279 +
  280 + createMessage.success('删除成功');
  281 + clearSelectedRowKeys();
  282 + reload();
289 283 }
290   - reload();
291 284 };
292 285 </script>
... ...
... ... @@ -126,7 +126,10 @@
126 126 password: getDataFlowParams.password ? getDataFlowParams.password : undefined,
127 127 }
128 128 : undefined,
  129 + appendClientIdSuffix: getDataFlowParams.appendClientIdSuffix || undefined,
  130 + type: undefined,
129 131 };
  132 +
130 133 const rest = isRabbitmq(getDataFlowMethod?.type)
131 134 ? await postAddConvertApi({ ...restData.data, ...data })
132 135 : await postAddConvertApi({ ...restData.data, ...data, configuration });
... ...
1 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 5 import { allComponents } from '../packages';
6 6 import { RuleNodeTypeEnum } from '../packages/index.type';
7 7 import { buildUUID } from '/@/utils/uuid';
... ... @@ -9,6 +9,9 @@ import { isNullOrUnDef } from '/@/utils/is';
9 9 import { useAddNodes } from './useAddNodes';
10 10 import { useAddEdges } from './useAddEdges';
11 11 import { RuleChainEntityType } from '../enum/entity';
  12 +import { EntryCategoryComponentEnum } from '../enum/category';
  13 +
  14 +const ignoreNodeKeys: string[] = [EntryCategoryComponentEnum.INPUT];
12 15
13 16 export function useBasicDataTransform() {
14 17 const nodeConfigMap = new Map<string, NodeData>();
... ... @@ -24,7 +27,7 @@ export function useBasicDataTransform() {
24 27 }
25 28
26 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 32 return {
30 33 debugMode: !!data?.debugMode,
... ... @@ -172,8 +175,126 @@ export function useBasicDataTransform() {
172 175
173 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 294 return {
176 295 mergeData,
  296 + combineData,
177 297 deconstructionData,
  298 + validateCanCreateRuleChain,
178 299 };
179 300 }
... ...
... ... @@ -3,11 +3,19 @@ import { getRuleNodeCache, setRuleNodeCache } from './useRuleChainCache';
3 3 import { RuleContextMenuEnum } from './useRuleChainContextMenu';
4 4 import { useAddNodes } from './useAddNodes';
5 5 import { buildUUID } from '/@/utils/uuid';
6   -import { toRaw, unref } from 'vue';
  6 +import { Ref, toRaw, unref } from 'vue';
7 7 import { useSaveAndRedo } from './useSaveAndRedo';
8 8 import { isUnDef } from '/@/utils/is';
9 9 import { useAddEdges } from './useAddEdges';
10 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 20 interface HandleContextMenuActionParamsType {
13 21 menuType: RuleContextMenuEnum;
... ... @@ -16,18 +24,58 @@ interface HandleContextMenuActionParamsType {
16 24 node?: GraphNode;
17 25 edge?: GraphEdge;
18 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 69 export const NODE_WIDTH = 176;
22 70 export const NODE_HEIGHT = 48;
23 71
24   -function getElementsCenter(nodes: GraphNode[]) {
  72 +function getElementsCenter(nodes: Ref<GraphNode[]> | GraphNode[] = []) {
25 73 let leftTopX: number | undefined;
26 74 let leftTopY: number | undefined;
27 75 let rightBottomX: number | undefined;
28 76 let rightBottomY: number | undefined;
29 77
30   - for (const node of nodes) {
  78 + for (const node of unref(nodes)) {
31 79 const { position } = node;
32 80 const { x, y } = position;
33 81 if (isUnDef(leftTopX)) {
... ... @@ -200,6 +248,37 @@ export function useContextMenuAction() {
200 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 282 const handleContextMenuAction = (params: HandleContextMenuActionParamsType) => {
204 283 const { menuType } = params;
205 284
... ... @@ -213,6 +292,7 @@ export function useContextMenuAction() {
213 292 [RuleContextMenuEnum.SELECT_COPY]: selectCopy,
214 293 [RuleContextMenuEnum.APPLY_CHANGE]: applyChange,
215 294 [RuleContextMenuEnum.UNDO_CHANGE]: undoChange,
  295 + [RuleContextMenuEnum.CREATE_RULE_CHAIN]: createRuleChain,
216 296 };
217 297
218 298 if (handlerMapping[menuType]) {
... ...
... ... @@ -5,6 +5,7 @@ import type { CreateNodeModal } from '../src/components/CreateNodeModal';
5 5 import type { CreateEdgeModal } from '../src/components/CreateEdgeModal';
6 6 import { UpdateNodeDrawer } from '../src/components/UpdateNodeDrawer';
7 7 import { UpdateEdgeDrawer } from '../src/components/UpdateEdgeDrawer';
  8 +import { CreateRuleChainModal } from '../src/components/CreateRuleChainModal';
8 9
9 10 const SYMBOL = Symbol('flow-context');
10 11
... ... @@ -30,6 +31,11 @@ interface FlowContextOptionsType {
30 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 39 * @description vue flow store
34 40 */
35 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 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 5 import { useAddNodes } from './useAddNodes';
3 6
4   -export function useInputNode() {
  7 +export function useNewNode() {
5 8 const getInputNodeConfig = (id?: string) => {
6 9 const { getAddNodesParams } = useAddNodes();
7 10
... ... @@ -20,5 +23,38 @@ export function useInputNode() {
20 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 27 import { useContextMenuAction } from './useContextMenuAction';
28 28 import { useSaveAndRedo } from './useSaveAndRedo';
29 29 import { EntryCategoryComponentEnum } from '../enum/category';
  30 +import { CreateRuleChainModal } from '../src/components/CreateRuleChainModal';
  31 +import { useBasicDataTransform } from './useBasicDataTransform';
30 32
31 33 interface UseRuleFlowOptionsType {
32 34 id: string;
33 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 43 useSaveAndRedoActionType: ReturnType<typeof useSaveAndRedo>;
39 44 }
40 45
... ... @@ -47,14 +52,10 @@ const validateInputAndOutput: ValidConnectionFunc = (connection: Connection) =>
47 52 };
48 53
49 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 60 const { triggerChange } = useSaveAndRedoActionType;
60 61
... ... @@ -162,29 +163,35 @@ export function useRuleFlow(options: UseRuleFlowOptionsType) {
162 163
163 164 const { handleContextMenuAction } = useContextMenuAction();
164 165
  166 + const { validateCanCreateRuleChain } = useBasicDataTransform();
  167 +
165 168 onNodeContextMenu(async (params) => {
166 169 const menuType = params.node.selected
167 170 ? await createElementsSelectedContextMenu(
168 171 params,
169 172 unref(useSaveAndRedoActionType.changeMarker),
170   - validateSelectionElementsCanCreateRuleChain()
  173 + validateCanCreateRuleChain(
  174 + flowActionType.getSelectedNodes,
  175 + flowActionType.getSelectedEdges
  176 + ).flag
171 177 )
172 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 197 onEdgeContextMenu(async (params) => {
... ... @@ -207,6 +214,7 @@ export function useRuleFlow(options: UseRuleFlowOptionsType) {
207 214 event: params.event,
208 215 useSaveAndRedoActionType,
209 216 edge: params.edge,
  217 + modalActionType,
210 218 });
211 219 }
212 220 });
... ... @@ -216,21 +224,25 @@ export function useRuleFlow(options: UseRuleFlowOptionsType) {
216 224 ? await createElementsSelectedContextMenu(
217 225 getCreatePanelContextMenuParams(params),
218 226 unref(useSaveAndRedoActionType.changeMarker),
219   - validateSelectionElementsCanCreateRuleChain()
  227 + validateCanCreateRuleChain(
  228 + flowActionType.getSelectedNodes,
  229 + flowActionType.getSelectedEdges
  230 + ).flag
220 231 )
221 232 : await createPanelContextMenu(
222 233 getCreatePanelContextMenuParams(params),
223 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 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 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 4 import { useBasicDataTransform } from './useBasicDataTransform';
6 5 import {
7 6 getRuleChainData,
... ... @@ -10,15 +9,13 @@ import {
10 9 saveRuleChainDetail,
11 10 } from '/@/api/ruleDesigner';
12 11 import { ConnectionItemType, RuleChainDetail, RuleChainType } from '../types/ruleNode';
13   -import { useInputNode } from './useInputNode';
  12 +import { useNewNode } from './useNewNode';
14 13 import { buildUUID } from '/@/utils/uuid';
15 14 import { useRoute, useRouter } from 'vue-router';
16 15 import { RuleChainEntityType } from '../enum/entity';
17 16 import { PageEnum } from '/@/enums/pageEnum';
18 17 import { clearRuleChainImportCache, getRuleChainImportCache } from './useRuleChainCache';
19 18
20   -const ignoreNodeKeys: string[] = [EntryCategoryComponentEnum.INPUT];
21   -
22 19 export function useSaveAndRedo() {
23 20 const changeMarker = ref(false);
24 21
... ... @@ -38,7 +35,7 @@ export function useSaveAndRedo() {
38 35
39 36 const ruleChainDetail = ref<RuleChainDetail>();
40 37
41   - const { mergeData, deconstructionData } = useBasicDataTransform();
  38 + const { combineData, deconstructionData } = useBasicDataTransform();
42 39
43 40 const triggerChange = () => {
44 41 changeMarker.value = true;
... ... @@ -48,90 +45,14 @@ export function useSaveAndRedo() {
48 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 48 const handleApplyChange = (flowActionType: VueFlowStore) => {
121 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 56 handleSaveRuleChain(connections, nodes, firstNodeIndex);
136 57 };
137 58
... ... @@ -211,7 +132,7 @@ export function useSaveAndRedo() {
211 132 function parseRuleChain(ruleChain: RuleChainType) {
212 133 const inputId = buildUUID();
213 134
214   - const { getInputNodeConfig } = useInputNode();
  135 + const { getInputNodeConfig } = useNewNode();
215 136
216 137 const value = deconstructionData(ruleChain, inputId);
217 138
... ...
... ... @@ -19,10 +19,11 @@
19 19 import { CreateEdgeModal } from './src/components/CreateEdgeModal';
20 20 import { useFullScreen } from './hook/useFullScreen';
21 21 import { useSaveAndRedo } from './hook/useSaveAndRedo';
  22 + import { NodeData } from './types/node';
22 23 import { Icon } from '/@/components/Icon';
23 24 import { UpdateNodeDrawer } from './src/components/UpdateNodeDrawer';
24 25 import { UpdateEdgeDrawer } from './src/components/UpdateEdgeDrawer';
25   - import { NodeData } from './types/node';
  26 + import { CreateRuleChainModal } from './src/components/CreateRuleChainModal';
26 27
27 28 const getId = Number(Math.random().toString().substring(2)).toString(16);
28 29
... ... @@ -36,6 +37,9 @@
36 37
37 38 const updateEdgeDrawerActionType = ref<Nullable<InstanceType<typeof UpdateEdgeDrawer>>>(null);
38 39
  40 + const createRuleChainModalActionType =
  41 + ref<Nullable<InstanceType<typeof CreateRuleChainModal>>>(null);
  42 +
39 43 const flowElRef = ref<Nullable<FlowElRef>>(null);
40 44
41 45 const elements = ref([]);
... ... @@ -57,11 +61,14 @@
57 61 const { flowActionType } = useRuleFlow({
58 62 id: getId,
59 63 ruleChainDetail,
60   - createNodeModalActionType,
61   - createEdgeModalActionType,
62   - updateEdgeDrawerActionType,
63   - updateNodeDrawerActionType,
64 64 useSaveAndRedoActionType,
  65 + modalActionType: {
  66 + createNodeModalActionType,
  67 + createEdgeModalActionType,
  68 + updateEdgeDrawerActionType,
  69 + updateNodeDrawerActionType,
  70 + createRuleChainModalActionType,
  71 + },
65 72 });
66 73
67 74 const { handleOnDragOver, handleOnDrop } = useDragCreate({
... ... @@ -85,6 +92,8 @@
85 92
86 93 const handleDeleteSelectionElements = () => {
87 94 flowActionType.removeNodes(unref(flowActionType.getSelectedNodes));
  95 + flowActionType.removeEdges(unref(flowActionType.getSelectedEdges));
  96 + useSaveAndRedoActionType.triggerChange?.();
88 97 };
89 98
90 99 onMounted(() => {
... ... @@ -97,6 +106,7 @@
97 106 createNodeModalActionType,
98 107 updateEdgeDrawerActionType,
99 108 updateNodeDrawerActionType,
  109 + createRuleChainModalActionType,
100 110 flowActionType,
101 111 triggerChange,
102 112 });
... ... @@ -177,6 +187,8 @@
177 187
178 188 <UpdateEdgeDrawer ref="updateEdgeDrawerActionType" />
179 189 <UpdateNodeDrawer ref="updateNodeDrawerActionType" />
  190 +
  191 + <CreateRuleChainModal ref="createRuleChainModalActionType" />
180 192 </main>
181 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 24 createdTime: number;
25 25 additionalInfo: AdditionalInfo;
26 26 tenantId: Id;
27   - name: Id;
  27 + name: string;
28 28 type: string;
29 29 firstRuleNodeId: Id;
30 30 root: boolean;
... ...
... ... @@ -35,6 +35,7 @@
35 35 const { proxy } = getCurrentInstance() as any;
36 36 const getChildData = ref(null);
37 37 const editGetId: any = ref('');
  38 + const createTime = ref<string>('');
38 39 const [registerForm, { validate, resetFields, setFieldsValue, updateSchema }] = useForm({
39 40 schemas: formSchema,
40 41 showActionButtonGroup: false,
... ... @@ -57,6 +58,7 @@
57 58 await setFieldsValue({
58 59 ...data.record,
59 60 });
  61 + createTime.value = data.record.createdTime;
60 62 updateStatusSchema(true);
61 63 }
62 64 });
... ... @@ -79,7 +81,7 @@
79 81 ]);
80 82 };
81 83
82   - const getAllFieldsFunc = async () => {
  84 + const getAllFieldsFunc = async (isUpdate?: boolean) => {
83 85 getValuesFormData = await validate();
84 86 if (!getValuesFormData) return;
85 87 let getChildValues = proxy.$refs.getChildData.getAllFields();
... ... @@ -90,10 +92,9 @@
90 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 98 Object.assign(
98 99 postAllData,
99 100 {
... ... @@ -101,7 +102,7 @@
101 102 },
102 103 getValuesFormData,
103 104 id,
104   - createTime
  105 + createTime1
105 106 );
106 107 if (!unref(isUpdate)) {
107 108 delete postAllData.id;
... ... @@ -119,7 +120,7 @@
119 120 emit('success');
120 121 resetFields();
121 122 } else {
122   - await getAllFieldsFunc();
  123 + await getAllFieldsFunc(true);
123 124 await saveTenantProfileApi(postAllData);
124 125 createMessage.success('租户配置编辑成功');
125 126 closeDrawer();
... ...
... ... @@ -173,10 +173,12 @@
173 173 const { codeType, customCommand } = unref(getDesign) || {};
174 174 const { transportType } = customCommand || {};
175 175 const sendValue = ref<any>(unref(sliderValue));
  176 + const isModbusCommand = ref<boolean>(false);
176 177 if (transportType == 'TCP' && codeType == TaskTypeEnum.MODBUS_RTU) {
177 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 182 flag
181 183 ? ((sliderValue.value = unref(sliderValue)),
182 184 (oldSliderValue.value = sliderValue.value),
... ...
... ... @@ -159,7 +159,6 @@
159 159 });
160 160
161 161 const resize = async () => {
162   - console.log(123321);
163 162 await nextTick();
164 163
165 164 // 修改echarts大小
... ...
... ... @@ -12,7 +12,7 @@
12 12 import { useModalInner } from '/@/components/Modal';
13 13 import { getAllDeviceByOrg } from '/@/api/dataBoard';
14 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 16 import { formatToDateTime } from '/@/utils/dateUtil';
17 17 import {
18 18 ModeSwitchButton,
... ... @@ -112,18 +112,14 @@
112 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 119 title: '属性',
124 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 132 format: (val) => {
137 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 176 const getDeviceDataKey = async (record: DeviceOption) => {
144 177 const { organizationId, value } = record;
145 178 try {
... ... @@ -260,7 +293,7 @@
260 293 :show-ok-btn="false"
261 294 cancel-text="关闭"
262 295 width="70%"
263   - title="选择时间"
  296 + title="历史趋势"
264 297 >
265 298 <section
266 299 class="flex flex-col p-4 h-full w-full min-w-7/10"
... ... @@ -295,7 +328,11 @@
295 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 336 <template #toolbar>
300 337 <div class="flex h-70px items-center justify-end p-2">
301 338 <ModeSwitchButton
... ...