Commit 6369ad050c252143e9f6753ad1e40932685cb25c

Authored by xp.Huang
2 parents a186d845 1a6c4e73

Merge branch 'feat/add-rule-chain' into 'main_dev'

fix: 修改规则链相关的问题

See merge request yunteng/thingskit-front!850
@@ -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) => { 15 +export const getDeviceHistoryInfo = (params: Recordable, orderBy) => {
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: 'ASC' }, 19 + params: { ...params, entityId: null, orderBy },
20 }, 20 },
21 { 21 {
22 joinPrefix: false, 22 joinPrefix: false,
@@ -161,15 +161,53 @@ export const createRuleChine = (params) => { @@ -161,15 +161,53 @@ export const createRuleChine = (params) => {
161 ); 161 );
162 }; 162 };
163 163
  164 +export const importRuleChine = (params) => {
  165 + return defHttp.post(
  166 + {
  167 + url: `${ScreenManagerApi.RULE_CHAIN}/metadata`,
  168 + params,
  169 + },
  170 + { joinPrefix: false }
  171 + );
  172 +};
  173 +
164 /** 174 /**
165 * 删除规则链 175 * 删除规则链
166 */ 176 */
167 177
168 -export const deleteRuleChine = (params) => { 178 +export const deleteRuleChine = (id) => {
169 return defHttp.delete( 179 return defHttp.delete(
170 { 180 {
171 url: `${ScreenManagerApi.RULE_CHAIN}/`, 181 url: `${ScreenManagerApi.RULE_CHAIN}/`,
172 - params, 182 + params: id,
  183 + },
  184 + { joinPrefix: false }
  185 + );
  186 +};
  187 +
  188 +/**
  189 + * 导出规则链
  190 + */
  191 +
  192 +export const exportRuleChine = (id) => {
  193 + return defHttp.get(
  194 + {
  195 + url: `${ScreenManagerApi.RULE_CHAIN}/`,
  196 + params: id + '/metadata',
  197 + },
  198 + { joinPrefix: false }
  199 + );
  200 +};
  201 +
  202 +/**
  203 + * 设置根规则链接
  204 + */
  205 +
  206 +export const settingRootChine = (id) => {
  207 + return defHttp.post(
  208 + {
  209 + url: `${ScreenManagerApi.RULE_CHAIN}/`,
  210 + params: id + '/root',
173 }, 211 },
174 { joinPrefix: false } 212 { joinPrefix: false }
175 ); 213 );
@@ -84,10 +84,22 @@ @@ -84,10 +84,22 @@
84 format: (val) => { 84 format: (val) => {
85 return formatToDateTime(val, 'YYYY-MM-DD HH:mm:ss'); 85 return formatToDateTime(val, 'YYYY-MM-DD HH:mm:ss');
86 }, 86 },
  87 + sorter: 'descend',
87 }, 88 },
88 ], 89 ],
89 }); 90 });
90 91
  92 + const handleTableChange = async (pag, filters, sorter: any) => {
  93 + console.log(pag, filters, sorter, 'pag, filters, sorter');
  94 + if (sorter.field == 'ts') {
  95 + if (sorter.order == 'descend') {
  96 + openHistoryPanel('ASC');
  97 + } else {
  98 + openHistoryPanel('DESC');
  99 + }
  100 + }
  101 + };
  102 +
91 const { setOptions, getInstance } = useECharts(chartRef as Ref<HTMLDivElement>); 103 const { setOptions, getInstance } = useECharts(chartRef as Ref<HTMLDivElement>);
92 104
93 const [register, method] = useTimePeriodForm({ 105 const [register, method] = useTimePeriodForm({
@@ -158,7 +170,7 @@ @@ -158,7 +170,7 @@
158 } catch (error) {} 170 } catch (error) {}
159 }; 171 };
160 172
161 - const openHistoryPanel = async () => { 173 + const openHistoryPanel = async (orderBy?: string) => {
162 await nextTick(); 174 await nextTick();
163 method.updateSchema({ 175 method.updateSchema({
164 field: 'keys', 176 field: 'keys',
@@ -177,14 +189,17 @@ @@ -177,14 +189,17 @@
177 189
178 const keys = props.attr ? props.attr : unref(getDeviceKeys).join(); 190 const keys = props.attr ? props.attr : unref(getDeviceKeys).join();
179 191
180 - const res = await getDeviceHistoryInfo({  
181 - entityId: props.deviceDetail.tbDeviceId,  
182 - keys,  
183 - startTs: Date.now() - 1 * 24 * 60 * 60 * 1000,  
184 - endTs: Date.now(),  
185 - agg: AggregateDataEnum.NONE,  
186 - limit: 7,  
187 - }); 192 + const res = await getDeviceHistoryInfo(
  193 + {
  194 + entityId: props.deviceDetail.tbDeviceId,
  195 + keys,
  196 + startTs: Date.now() - 1 * 24 * 60 * 60 * 1000,
  197 + endTs: Date.now(),
  198 + agg: AggregateDataEnum.NONE,
  199 + limit: 7,
  200 + },
  201 + orderBy
  202 + );
188 historyData.value = getTableHistoryData(res); 203 historyData.value = getTableHistoryData(res);
189 204
190 // 判断对象是否为空 205 // 判断对象是否为空
@@ -238,7 +253,11 @@ @@ -238,7 +253,11 @@
238 </div> 253 </div>
239 <Empty v-show="!isNull && mode === EnumTableChartMode.CHART" /> 254 <Empty v-show="!isNull && mode === EnumTableChartMode.CHART" />
240 255
241 - <BasicTable v-show="mode === EnumTableChartMode.TABLE" @register="registerTable"> 256 + <BasicTable
  257 + @change="handleTableChange"
  258 + v-show="mode === EnumTableChartMode.TABLE"
  259 + @register="registerTable"
  260 + >
242 <template #toolbar> 261 <template #toolbar>
243 <div class="flex h-70px items-center justify-end p-2"> 262 <div class="flex h-70px items-center justify-end p-2">
244 <ModeSwitchButton 263 <ModeSwitchButton
@@ -136,6 +136,7 @@ export const useGenDynamicForm = () => { @@ -136,6 +136,7 @@ export const useGenDynamicForm = () => {
136 fieldTypeMap.clear(); 136 fieldTypeMap.clear();
137 const formSchema = schemas.map((item) => { 137 const formSchema = schemas.map((item) => {
138 const { functionName, identifier, dataType } = item; 138 const { functionName, identifier, dataType } = item;
  139 + console.log(item, 'item');
139 const { type } = dataType || {}; 140 const { type } = dataType || {};
140 141
141 fieldTypeMap.set(identifier!, dataType!.type); 142 fieldTypeMap.set(identifier!, dataType!.type);
@@ -72,7 +72,11 @@ @@ -72,7 +72,11 @@
72 <script lang="ts" setup> 72 <script lang="ts" setup>
73 import { ref, unref, reactive, nextTick } from 'vue'; 73 import { ref, unref, reactive, nextTick } from 'vue';
74 import { BasicModal, useModalInner } from '/@/components/Modal'; 74 import { BasicModal, useModalInner } from '/@/components/Modal';
75 - import { deviceConfigAddOrEdit, deviceConfigGetDetail } from '/@/api/device/deviceConfigApi'; 75 + import {
  76 + deviceConfigAddOrEdit,
  77 + deviceConfigGetDetail,
  78 + deviceConfigGetRuleChain,
  79 + } from '/@/api/device/deviceConfigApi';
76 import { useMessage } from '/@/hooks/web/useMessage'; 80 import { useMessage } from '/@/hooks/web/useMessage';
77 import { steps } from './device.profile.data'; 81 import { steps } from './device.profile.data';
78 import { isEmpty } from '/@/utils/is'; 82 import { isEmpty } from '/@/utils/is';
@@ -119,6 +123,12 @@ @@ -119,6 +123,12 @@
119 unref(DevConStRef)?.editOrAddDeviceTypeStatus(true); 123 unref(DevConStRef)?.editOrAddDeviceTypeStatus(true);
120 } else { 124 } else {
121 unref(DevConStRef)?.editOrAddDeviceTypeStatus(false); 125 unref(DevConStRef)?.editOrAddDeviceTypeStatus(false);
  126 + const values = ref([]) as any;
  127 + if (!values.value.length) {
  128 + values.value = await deviceConfigGetRuleChain();
  129 + }
  130 + const defaultRuleChainId = unref(values).filter((item) => item.root)[0].id.id;
  131 + await unref(DevConStRef)?.setFieldsdefaultRuleChainId(defaultRuleChainId);
122 } 132 }
123 }); 133 });
124 const handleChange = (e) => { 134 const handleChange = (e) => {
@@ -196,7 +196,6 @@ export const step1Schemas: FormSchema[] = [ @@ -196,7 +196,6 @@ export const step1Schemas: FormSchema[] = [
196 label: '规则链', 196 label: '规则链',
197 component: 'ApiSelect', 197 component: 'ApiSelect',
198 colProps: { span: 14 }, 198 colProps: { span: 14 },
199 -  
200 componentProps: { 199 componentProps: {
201 api: async () => { 200 api: async () => {
202 const data = await deviceConfigGetRuleChain(); 201 const data = await deviceConfigGetRuleChain();
@@ -28,7 +28,7 @@ @@ -28,7 +28,7 @@
28 </div> 28 </div>
29 </template> 29 </template>
30 <script lang="ts" setup> 30 <script lang="ts" setup>
31 - import { ref } from 'vue'; 31 + import { ref, nextTick } from 'vue';
32 import { BasicForm, useForm } from '/@/components/Form'; 32 import { BasicForm, useForm } from '/@/components/Form';
33 import { step1Schemas } from '../device.profile.data'; 33 import { step1Schemas } from '../device.profile.data';
34 import { uploadApi } from '/@/api/personal/index'; 34 import { uploadApi } from '/@/api/personal/index';
@@ -90,6 +90,12 @@ @@ -90,6 +90,12 @@
90 } 90 }
91 return isJpgOrPng && isLt2M; 91 return isJpgOrPng && isLt2M;
92 }; 92 };
  93 +
  94 + const setFieldsdefaultRuleChainId = async (id) => {
  95 + await nextTick();
  96 + setFieldsValue({ defaultRuleChainId: id });
  97 + };
  98 +
93 async function customSubmitFunc() { 99 async function customSubmitFunc() {
94 const values = await validate(); 100 const values = await validate();
95 if (!values) return; 101 if (!values) return;
@@ -129,6 +135,7 @@ @@ -129,6 +135,7 @@
129 resetFormData, 135 resetFormData,
130 getFormData, 136 getFormData,
131 editOrAddDeviceTypeStatus, 137 editOrAddDeviceTypeStatus,
  138 + setFieldsdefaultRuleChainId,
132 }); 139 });
133 </script> 140 </script>
134 <style lang="less" scoped> 141 <style lang="less" scoped>
  1 +<template>
  2 + <div>属性</div>
  3 +</template>
  4 +
  5 +<script setup lang="ts"></script>
  6 +
  7 +<style lang="less" scoped></style>
  1 +<template>
  2 + <div>警告</div>
  3 +</template>
  4 +
  5 +<script setup lang="ts"></script>
  6 +
  7 +<style lang="less" scoped></style>
  1 +import ChainDetailDrawer from './index.vue';
  2 +export { ChainDetailDrawer };
  1 +<template>
  2 + <BasicDrawer
  3 + v-bind="$attrs"
  4 + isDetail
  5 + @register="register"
  6 + destroyOnClose
  7 + @close="closeDrawer"
  8 + :title="deviceDetail.alias || deviceDetail.name"
  9 + width="80%"
  10 + >
  11 + <Tabs v-model:activeKey="activeKey" :size="size">
  12 + <TabPane key="1" tab="详情">
  13 + <div>详情</div>
  14 + </TabPane>
  15 + <TabPane key="2" tab="属性">
  16 + <Attribute />
  17 + </TabPane>
  18 + <TabPane key="3" tab="最新遥测数据"><div>最新遥测数据</div></TabPane>
  19 + <TabPane key="4" tab="警告">
  20 + <div>警告</div>
  21 + </TabPane>
  22 + <TabPane key="5" tab="事件">
  23 + <div>事件</div>
  24 + </TabPane>
  25 + <!-- 网关设备并且场家是TBox -->
  26 + <TabPane key="6" tab="关联">
  27 + <div>关联</div>
  28 + </TabPane>
  29 + <!-- 网关设备并且是TBox -->
  30 +
  31 + <TabPane key="7" tab="审计日志">
  32 + <div>审计日志</div>
  33 + </TabPane>
  34 + </Tabs>
  35 + </BasicDrawer>
  36 +</template>
  37 +<script lang="ts" setup>
  38 + import { ref } from 'vue';
  39 + import { BasicDrawer, useDrawerInner } from '/@/components/Drawer';
  40 +
  41 + import { Tabs, TabPane } from 'ant-design-vue';
  42 + import { DeviceRecord } from '/@/api/device/model/deviceModel';
  43 +
  44 + import Attribute from './component/Attribute.vue';
  45 +
  46 + // defineProps({});
  47 +
  48 + // defineEmits([]);
  49 +
  50 + const activeKey = ref('1');
  51 + const size = ref('small');
  52 + const deviceDetail = ref<DeviceRecord>({} as unknown as DeviceRecord);
  53 + // 详情回显
  54 + const [register] = useDrawerInner(async (data) => {
  55 + console.log(data, 'data');
  56 + });
  57 + const closeDrawer = () => {
  58 + activeKey.value = '1';
  59 + };
  60 +</script>
@@ -5,7 +5,7 @@ @@ -5,7 +5,7 @@
5 v-bind="$attrs" 5 v-bind="$attrs"
6 width="30rem" 6 width="30rem"
7 @register="register" 7 @register="register"
8 - title="添加规则链" 8 + :title="ruleTile"
9 @ok="handleSuccess" 9 @ok="handleSuccess"
10 @close="handleClose" 10 @close="handleClose"
11 > 11 >
@@ -17,49 +17,73 @@ @@ -17,49 +17,73 @@
17 </template> 17 </template>
18 <script setup lang="ts"> 18 <script setup lang="ts">
19 import { BasicModal, useModalInner } from '/@/components/Modal'; 19 import { BasicModal, useModalInner } from '/@/components/Modal';
20 - import { ExecuteReportRecord } from '/@/api/export/model/exportModel';  
21 import { useForm, BasicForm } from '/@/components/Form'; 20 import { useForm, BasicForm } from '/@/components/Form';
22 import { formSchema } from './config'; 21 import { formSchema } from './config';
23 import { createRuleChine } from '/@/api/ruleengine/ruleengineApi'; 22 import { createRuleChine } from '/@/api/ruleengine/ruleengineApi';
24 import { useMessage } from '/@/hooks/web/useMessage'; 23 import { useMessage } from '/@/hooks/web/useMessage';
  24 + import { ref, unref } from 'vue';
25 25
26 const emit = defineEmits(['register', 'success']); 26 const emit = defineEmits(['register', 'success']);
27 27
28 const { createMessage } = useMessage(); 28 const { createMessage } = useMessage();
29 29
30 - const [registerForm, { getFieldsValue, validate }] = useForm({  
31 - labelWidth: 120, 30 + const ruleTile = ref();
  31 +
  32 + const [registerForm, { getFieldsValue, validate, setFieldsValue }] = useForm({
  33 + labelWidth: 70,
32 schemas: formSchema, 34 schemas: formSchema,
33 showActionButtonGroup: false, 35 showActionButtonGroup: false,
34 }); 36 });
35 37
36 - const [register, { setModalProps, closeModal }] = useModalInner(  
37 - async (data: { record: ExecuteReportRecord }) => {  
38 - setModalProps({ loading: true });  
39 - console.log(data, 'record');  
40 - }  
41 - ); 38 + const updateValue = ref({});
  39 + const isUpdate = ref<boolean>(true);
  40 +
  41 + const [register, { setModalProps, closeModal }] = useModalInner(async (data) => {
  42 + setModalProps({ loading: true });
  43 + const { text, record } = data || {};
  44 + console.log(data, 'data');
  45 + isUpdate.value = record ? true : false;
  46 + ruleTile.value = text;
  47 + const { name, additionalInfo, debugMode } = record || {};
  48 + const { description } = additionalInfo || {};
  49 + updateValue.value = record;
  50 + unref(isUpdate) && setFieldsValue({ name, debugMode, description });
  51 + setModalProps({ loading: false });
  52 + });
42 53
43 const handleClose = () => { 54 const handleClose = () => {
44 closeModal(); 55 closeModal();
45 }; 56 };
46 57
47 const handleSuccess = async () => { 58 const handleSuccess = async () => {
48 - console.log(1, '1', getFieldsValue());  
49 await validate(); 59 await validate();
50 const record = getFieldsValue(); 60 const record = getFieldsValue();
51 const { description, debugMode, name } = record; 61 const { description, debugMode, name } = record;
52 - const form = {  
53 - additionalInfo: {  
54 - description,  
55 - },  
56 - debugMode,  
57 - name,  
58 - type: 'CORE',  
59 - }; 62 + if (!isUpdate.value) {
  63 + const form = {
  64 + additionalInfo: {
  65 + description,
  66 + },
  67 + debugMode,
  68 + name,
  69 + type: 'CORE',
  70 + };
  71 + await createRuleChine(form);
  72 + createMessage.success('添加成功');
  73 + } else {
  74 + const value = {
  75 + ...updateValue.value,
  76 + additionalInfo: {
  77 + description,
  78 + },
  79 + debugMode,
  80 + name,
  81 + };
  82 + console.log(value, 'value');
  83 + await createRuleChine(value);
  84 + createMessage.success('编辑成功');
  85 + }
60 86
61 - await createRuleChine(form);  
62 - createMessage.success('添加成功');  
63 handleClose(); 87 handleClose();
64 emit('success'); 88 emit('success');
65 }; 89 };
@@ -9,6 +9,7 @@ export const formSchema: FormSchema[] = [ @@ -9,6 +9,7 @@ export const formSchema: FormSchema[] = [
9 required: true, 9 required: true,
10 componentProps: { 10 componentProps: {
11 placeholder: '请输入名称', 11 placeholder: '请输入名称',
  12 + max: 120,
12 }, 13 },
13 }, 14 },
14 { 15 {
@@ -16,7 +16,7 @@ export const columns: BasicColumn[] = [ @@ -16,7 +16,7 @@ export const columns: BasicColumn[] = [
16 width: 200, 16 width: 200,
17 }, 17 },
18 { 18 {
19 - title: '是否链', 19 + title: '是否链',
20 dataIndex: 'root', 20 dataIndex: 'root',
21 slots: { 21 slots: {
22 customRender: 'root', 22 customRender: 'root',
@@ -41,3 +41,20 @@ export const searchFormSchema: FormSchema[] = [ @@ -41,3 +41,20 @@ export const searchFormSchema: FormSchema[] = [
41 export const encode = (string: string) => { 41 export const encode = (string: string) => {
42 return encodeURIComponent(string); 42 return encodeURIComponent(string);
43 }; 43 };
  44 +
  45 +export const exportJSONFile = (value: Recordable, name: string) => {
  46 + const blob = new Blob([JSON.stringify(value, null, 2)], { type: 'text/json' });
  47 + const objectURL = URL.createObjectURL(blob);
  48 + const element = document.createElement('a');
  49 + element.href = objectURL;
  50 + element.download = `${name}.json`;
  51 + element.style.display = 'none';
  52 + document.body.appendChild(element);
  53 + element.click();
  54 + element.remove();
  55 + URL.revokeObjectURL(objectURL);
  56 +};
  57 +
  58 +export enum RuleChainPermisssion {
  59 + DETAIL = 'rule:chain:detail',
  60 +}
@@ -5,6 +5,9 @@ @@ -5,6 +5,9 @@
5 <Authority> 5 <Authority>
6 <a-button type="primary" @click="handleAdd"> 新增规则链 </a-button> 6 <a-button type="primary" @click="handleAdd"> 新增规则链 </a-button>
7 </Authority> 7 </Authority>
  8 + <Upload :show-upload-list="false" :customRequest="handleImport">
  9 + <Button type="primary" :loading="importLoading"> 导入规则链 </Button>
  10 + </Upload>
8 <!-- <Authority> 11 <!-- <Authority>
9 <Popconfirm 12 <Popconfirm
10 title="您确定要批量删除数据" 13 title="您确定要批量删除数据"
@@ -23,10 +26,37 @@ @@ -23,10 +26,37 @@
23 <TableAction 26 <TableAction
24 :actions="[ 27 :actions="[
25 { 28 {
26 - label: '查看', 29 + label: '打开规则链',
27 icon: 'ant-design:eye-outlined', 30 icon: 'ant-design:eye-outlined',
28 onClick: handleView.bind(null, record), 31 onClick: handleView.bind(null, record),
29 }, 32 },
  33 + // {
  34 + // label: '详情',
  35 + // icon: 'ant-design:field-time-outlined',
  36 + // onClick: handleDetail.bind(null, record),
  37 + // },
  38 + {
  39 + label: '编辑',
  40 + icon: 'clarity:note-edit-line',
  41 + onClick: handleBussinessModal.bind(null, record),
  42 + },
  43 + ]"
  44 + :dropDownActions="[
  45 + {
  46 + label: '设置为根规则链',
  47 + icon: 'ant-design:gateway-outlined',
  48 + color: 'error',
  49 + ifShow: !record.root,
  50 + popConfirm: {
  51 + title: '是否确认设置成根规则链',
  52 + confirm: handleSettingRoot.bind(null, record),
  53 + },
  54 + },
  55 + {
  56 + label: '导出规则链',
  57 + icon: 'ant-design:vertical-align-bottom-outlined',
  58 + onClick: handleExport.bind(null, record),
  59 + },
30 { 60 {
31 label: '删除', 61 label: '删除',
32 icon: 'ant-design:delete-outlined', 62 icon: 'ant-design:delete-outlined',
@@ -42,24 +72,49 @@ @@ -42,24 +72,49 @@
42 </template> 72 </template>
43 </BasicTable> 73 </BasicTable>
44 <RuleChainModal @register="registerModal" @success="handleSuccess" /> 74 <RuleChainModal @register="registerModal" @success="handleSuccess" />
  75 +
  76 + <!-- <ChainDetailDrawer @register="registerDrawer" /> -->
45 </div> 77 </div>
46 </template> 78 </template>
47 <script lang="ts" setup> 79 <script lang="ts" setup>
48 import { BasicTable, useTable, TableAction } from '/@/components/Table'; 80 import { BasicTable, useTable, TableAction } from '/@/components/Table';
49 - import { columns, encode, searchFormSchema } from './config/config.data';  
50 - import { deleteRuleChine, getRuleChinsList } from '/@/api/ruleengine/ruleengineApi'; 81 + import {
  82 + RuleChainPermisssion,
  83 + columns,
  84 + encode,
  85 + exportJSONFile,
  86 + searchFormSchema,
  87 + } from './config/config.data';
  88 + import {
  89 + deleteRuleChine,
  90 + getRuleChinsList,
  91 + exportRuleChine,
  92 + settingRootChine,
  93 + importRuleChine,
  94 + createRuleChine,
  95 + } from '/@/api/ruleengine/ruleengineApi';
51 import { useModal } from '/@/components/Modal'; 96 import { useModal } from '/@/components/Modal';
52 import { Authority } from '/@/components/Authority'; 97 import { Authority } from '/@/components/Authority';
53 - import { Tag } from 'ant-design-vue'; 98 + import { Tag, Button, Upload } from 'ant-design-vue';
54 import { RuleChainModal } from './component/index'; 99 import { RuleChainModal } from './component/index';
55 import { useMessage } from '/@/hooks/web/useMessage'; 100 import { useMessage } from '/@/hooks/web/useMessage';
56 import { usePermission } from '/@/hooks/web/usePermission'; 101 import { usePermission } from '/@/hooks/web/usePermission';
57 import { useRouter } from 'vue-router'; 102 import { useRouter } from 'vue-router';
  103 + import { ref } from 'vue';
  104 + import { isObject, isString } from '/@/utils/is';
  105 + // import { ChainDetailDrawer } from './chainDetail/index';
  106 + // import { useDrawer } from '/@/components/Drawer';
58 107
59 const [registerTable, { reload, setProps }] = useTable({ 108 const [registerTable, { reload, setProps }] = useTable({
60 title: '规则链库', 109 title: '规则链库',
61 api: getRuleChinsList, 110 api: getRuleChinsList,
  111 + rowKey: (record) => record.id.id,
62 columns, 112 columns,
  113 + pagination: true,
  114 + useSearchForm: true,
  115 + showTableSetting: true,
  116 + bordered: true,
  117 + showIndexColumn: false,
63 formConfig: { 118 formConfig: {
64 labelWidth: 120, 119 labelWidth: 120,
65 schemas: searchFormSchema, 120 schemas: searchFormSchema,
@@ -68,19 +123,21 @@ @@ -68,19 +123,21 @@
68 pageField: 'page', 123 pageField: 'page',
69 listField: 'data', 124 listField: 'data',
70 }, 125 },
71 - pagination: true,  
72 - useSearchForm: true,  
73 - showTableSetting: true,  
74 - bordered: true,  
75 - showIndexColumn: false,  
76 beforeFetch(params) { 126 beforeFetch(params) {
77 Reflect.set(params, 'page', params.page - 1); 127 Reflect.set(params, 'page', params.page - 1);
78 Reflect.set(params, 'sortProperty', 'createdTime'); 128 Reflect.set(params, 'sortProperty', 'createdTime');
79 Reflect.set(params, 'sortOrder', 'DESC'); 129 Reflect.set(params, 'sortOrder', 'DESC');
80 return params; 130 return params;
81 }, 131 },
  132 + rowSelection: {
  133 + type: 'checkbox',
  134 + getCheckboxProps: (record: Recordable) => {
  135 + console.log(record, 'record');
  136 + return { disabled: record.root };
  137 + },
  138 + },
82 actionColumn: { 139 actionColumn: {
83 - width: 200, 140 + width: 220,
84 title: '操作', 141 title: '操作',
85 dataIndex: 'action', 142 dataIndex: 'action',
86 slots: { customRender: 'action' }, 143 slots: { customRender: 'action' },
@@ -90,12 +147,23 @@ @@ -90,12 +147,23 @@
90 147
91 const [registerModal, { openModal }] = useModal(); 148 const [registerModal, { openModal }] = useModal();
92 149
  150 + // const [registerDrawer, { openDrawer }] = useDrawer();
  151 +
93 const handleSuccess = () => { 152 const handleSuccess = () => {
94 reload(); 153 reload();
95 }; 154 };
96 155
97 const handleAdd = () => { 156 const handleAdd = () => {
98 - openModal(true); 157 + openModal(true, {
  158 + text: '创建规则链',
  159 + });
  160 + };
  161 +
  162 + const handleBussinessModal = (record) => {
  163 + openModal(true, {
  164 + text: '编辑规则链',
  165 + record,
  166 + });
99 }; 167 };
100 168
101 const { createMessage } = useMessage(); 169 const { createMessage } = useMessage();
@@ -103,13 +171,110 @@ @@ -103,13 +171,110 @@
103 const router = useRouter(); 171 const router = useRouter();
104 172
105 const handleView = (record: Recordable) => { 173 const handleView = (record: Recordable) => {
106 - const hasDetailPermission = hasPermission('rule:chain:detail'); 174 + const hasDetailPermission = hasPermission(RuleChainPermisssion.DETAIL);
107 if (hasDetailPermission) { 175 if (hasDetailPermission) {
108 const boardId = encode(record.id.id); 176 const boardId = encode(record.id.id);
109 router.push(`/rule/chain/${boardId}`); 177 router.push(`/rule/chain/${boardId}`);
110 } else createMessage.warning('没有权限'); 178 } else createMessage.warning('没有权限');
111 }; 179 };
112 180
  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 }) => {
  206 + const fileReader = new FileReader();
  207 +
  208 + fileReader.onload = async () => {
  209 + const { flag, data } = paseJSON(fileReader.result as string);
  210 + if (!flag) {
  211 + createMessage.warning('JSON解析失败,请导入正确的JSON~');
  212 + return;
  213 + }
  214 + try {
  215 + importLoading.value = true;
  216 +
  217 + Object.keys(data || {}).forEach((key) => {
  218 + const value = (data || {})[key];
  219 + if (value && isEmptyObject(value)) {
  220 + (data || {})[key] = [];
  221 + }
  222 + });
  223 + const { ruleChain, metadata } = data as any;
  224 +
  225 + const value = await createRuleChine(ruleChain);
  226 + const { id } = value;
  227 +
  228 + const values = {
  229 + ruleChainId: id,
  230 + ...metadata,
  231 + };
  232 + const rules = await importRuleChine(values);
  233 +
  234 + rules
  235 + ? createMessage.success('导入成功~')
  236 + : createMessage.error('JSON解析失败,请导入正确的JSON~');
  237 +
  238 + rules && reload();
  239 + } catch (error) {
  240 + throw error;
  241 + } finally {
  242 + importLoading.value = false;
  243 + }
  244 + };
  245 +
  246 + fileReader.readAsText(data.file, 'utf-8');
  247 + };
  248 +
  249 + const handleExport = async (record: Recordable) => {
  250 + if (!record) return;
  251 + const { additionalInfo, name, type, firstRuleNodeId, root, debugMode, configuration } =
  252 + record || {};
  253 + const { firstNodeIndex, nodes, connections, ruleChainConnections } = await exportRuleChine(
  254 + record.id.id
  255 + );
  256 + const value = {
  257 + ruleChain: { additionalInfo, name, type, firstRuleNodeId, root, debugMode, configuration },
  258 + metadata: { firstNodeIndex, nodes, connections, ruleChainConnections },
  259 + };
  260 + exportJSONFile(value, name);
  261 + };
  262 +
  263 + const handleSettingRoot = async (record) => {
  264 + setProps({
  265 + loading: true,
  266 + });
  267 + try {
  268 + await settingRootChine(record.id.id);
  269 + createMessage.success('设置成功');
  270 + } finally {
  271 + setProps({
  272 + loading: false,
  273 + });
  274 + }
  275 + reload();
  276 + };
  277 +
113 const handleDeleteOrBatchDelete = async (record: Recordable) => { 278 const handleDeleteOrBatchDelete = async (record: Recordable) => {
114 setProps({ 279 setProps({
115 loading: true, 280 loading: true,