Commit 9b473ae32686bd35dd4aa6e450a3cecf2309cd8f

Authored by xp.Huang
2 parents b4cbab05 c660991e

Merge branch 'ww' into 'main'

feat: 设备列表新增批量分配按钮

See merge request yunteng/thingskit-front!504
@@ -15,6 +15,7 @@ @@ -15,6 +15,7 @@
15 "SNMP", 15 "SNMP",
16 "unref", 16 "unref",
17 "vben", 17 "vben",
18 - "VITE" 18 + "VITE",
  19 + "windicss"
19 ] 20 ]
20 } 21 }
1 <!DOCTYPE html> 1 <!DOCTYPE html>
2 <html lang="en" id="htmlRoot"> 2 <html lang="en" id="htmlRoot">
3 <head> 3 <head>
4 - <%- contentSecurityPolicy %>  
5 <meta charset="UTF-8" /> 4 <meta charset="UTF-8" />
6 <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" /> 5 <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
7 <meta name="renderer" content="webkit" /> 6 <meta name="renderer" content="webkit" />
@@ -48,6 +48,8 @@ export interface DeviceModel { @@ -48,6 +48,8 @@ export interface DeviceModel {
48 label: string; 48 label: string;
49 lastConnectTime: string; 49 lastConnectTime: string;
50 deviceType: DeviceTypeEnum; 50 deviceType: DeviceTypeEnum;
  51 + organizationId: string;
  52 + customerId?: string;
51 } 53 }
52 54
53 export interface DeviceProfileModel { 55 export interface DeviceProfileModel {
  1 +import { computed } from 'vue';
  2 +import { TableActionType } from '/@//components/Table';
  3 +
  4 +const useBatchOperation = (
  5 + getRowSelection: TableActionType['getRowSelection'],
  6 + setSelectedRowKeys: TableActionType['setSelectedRowKeys']
  7 +) => {
  8 + const isExistOption = computed(() => {
  9 + const rowSelection = getRowSelection();
  10 + return !!rowSelection.selectedRowKeys?.length;
  11 + });
  12 +
  13 + const resetSelectedOptions = () => {
  14 + setSelectedRowKeys([]);
  15 + };
  16 +
  17 + return {
  18 + isExistOption,
  19 + resetSelectedOptions,
  20 + };
  21 +};
  22 +
  23 +export { useBatchOperation };
@@ -20,6 +20,8 @@ @@ -20,6 +20,8 @@
20 import { BasicForm, useForm } from '/@/components/Form'; 20 import { BasicForm, useForm } from '/@/components/Form';
21 import { customerForm } from '../../config/detail.config'; 21 import { customerForm } from '../../config/detail.config';
22 import { dispatchCustomer as dispatchCustomerApi } from '/@/api/device/deviceManager'; 22 import { dispatchCustomer as dispatchCustomerApi } from '/@/api/device/deviceManager';
  23 + import { DeviceModel } from '/@/api/device/model/deviceModel';
  24 + import { isArray } from '/@/utils/is';
23 export default defineComponent({ 25 export default defineComponent({
24 name: 'AlarmDetailModal', 26 name: 'AlarmDetailModal',
25 components: { 27 components: {
@@ -28,9 +30,10 @@ @@ -28,9 +30,10 @@
28 }, 30 },
29 emits: ['reload', 'register'], 31 emits: ['reload', 'register'],
30 setup(_, { emit }) { 32 setup(_, { emit }) {
31 - let record = {};  
32 - const [registerModal, { closeModal }] = useModalInner((data: any) => {  
33 - const { organizationId } = data; 33 + let record: DeviceModel[] = [];
  34 + const [registerModal, { closeModal }] = useModalInner((data: DeviceModel | DeviceModel[]) => {
  35 + data = isArray(data) ? data : [data as DeviceModel];
  36 + const { organizationId } = data.at(0) || {};
34 record = data; 37 record = data;
35 updateSchema([ 38 updateSchema([
36 { 39 {
@@ -50,7 +53,11 @@ @@ -50,7 +53,11 @@
50 const dispatchCustomer = async () => { 53 const dispatchCustomer = async () => {
51 await validate(); 54 await validate();
52 const { customerId } = getFieldsValue(); 55 const { customerId } = getFieldsValue();
53 - await dispatchCustomerApi({ ...record, customerId }); 56 + const task: Promise<any>[] = [];
  57 + for await (const item of record) {
  58 + task.push(dispatchCustomerApi({ ...item, customerId }));
  59 + }
  60 + await Promise.all(task);
54 closeModal(); 61 closeModal();
55 resetFields(); 62 resetFields();
56 emit('reload'); 63 emit('reload');
@@ -14,13 +14,21 @@ @@ -14,13 +14,21 @@
14 title="您确定要批量删除数据" 14 title="您确定要批量删除数据"
15 ok-text="确定" 15 ok-text="确定"
16 cancel-text="取消" 16 cancel-text="取消"
17 - @confirm="handleDeleteOrBatchDelete(null)" 17 + @confirm="handleDelete()"
18 > 18 >
19 - <a-button color="error" v-if="authBtn(role)" :disabled="hasBatchDelete"> 19 + <a-button color="error" v-if="authBtn(role)" :disabled="!isExistOption">
20 批量删除 20 批量删除
21 </a-button> 21 </a-button>
22 </Popconfirm> 22 </Popconfirm>
23 </Authority> 23 </Authority>
  24 + <a-button
  25 + v-if="authBtn(role)"
  26 + type="primary"
  27 + @click="handleBatchAssign"
  28 + :disabled="!isExistOption"
  29 + >
  30 + 批量分配
  31 + </a-button>
24 </template> 32 </template>
25 <template #img="{ record }"> 33 <template #img="{ record }">
26 <TableImg 34 <TableImg
@@ -147,7 +155,7 @@ @@ -147,7 +155,7 @@
147 color: 'error', 155 color: 'error',
148 popConfirm: { 156 popConfirm: {
149 title: '是否确认删除', 157 title: '是否确认删除',
150 - confirm: handleDeleteOrBatchDelete.bind(null, record), 158 + confirm: handleDelete.bind(null, record),
151 }, 159 },
152 }, 160 },
153 ]" 161 ]"
@@ -170,7 +178,12 @@ @@ -170,7 +178,12 @@
170 </template> 178 </template>
171 <script lang="ts"> 179 <script lang="ts">
172 import { defineComponent, reactive, unref, nextTick, h, onUnmounted, ref } from 'vue'; 180 import { defineComponent, reactive, unref, nextTick, h, onUnmounted, ref } from 'vue';
173 - import { DeviceState, DeviceTypeEnum } from '/@/api/device/model/deviceModel'; 181 + import {
  182 + DeviceModel,
  183 + DeviceRecord,
  184 + DeviceState,
  185 + DeviceTypeEnum,
  186 + } from '/@/api/device/model/deviceModel';
174 import { BasicTable, useTable, TableAction, TableImg } from '/@/components/Table'; 187 import { BasicTable, useTable, TableAction, TableImg } from '/@/components/Table';
175 import { columns, searchFormSchema } from './config/device.data'; 188 import { columns, searchFormSchema } from './config/device.data';
176 import { Tag, Tooltip, Popover, Popconfirm } from 'ant-design-vue'; 189 import { Tag, Tooltip, Popover, Popconfirm } from 'ant-design-vue';
@@ -194,11 +207,11 @@ @@ -194,11 +207,11 @@
194 import { USER_INFO_KEY } from '/@/enums/cacheEnum'; 207 import { USER_INFO_KEY } from '/@/enums/cacheEnum';
195 import { getAuthCache } from '/@/utils/auth'; 208 import { getAuthCache } from '/@/utils/auth';
196 import { authBtn } from '/@/enums/roleEnum'; 209 import { authBtn } from '/@/enums/roleEnum';
197 - import { useBatchDelete } from '/@/hooks/web/useBatchDelete';  
198 import { useCopyToClipboard } from '/@/hooks/web/useCopyToClipboard'; 210 import { useCopyToClipboard } from '/@/hooks/web/useCopyToClipboard';
199 import { QuestionCircleOutlined } from '@ant-design/icons-vue'; 211 import { QuestionCircleOutlined } from '@ant-design/icons-vue';
200 import { Authority } from '/@/components/Authority'; 212 import { Authority } from '/@/components/Authority';
201 import { useRouter } from 'vue-router'; 213 import { useRouter } from 'vue-router';
  214 + import { useBatchOperation } from '/@/utils/useBatchOperation';
202 215
203 export default defineComponent({ 216 export default defineComponent({
204 name: 'DeviceManagement', 217 name: 'DeviceManagement',
@@ -233,7 +246,17 @@ @@ -233,7 +246,17 @@
233 246
234 const [ 247 const [
235 registerTable, 248 registerTable,
236 - { reload, setSelectedRowKeys, setProps, setTableData, getForm, setPagination }, 249 + {
  250 + reload,
  251 + setLoading,
  252 + setSelectedRowKeys,
  253 + setTableData,
  254 + getForm,
  255 + setPagination,
  256 + getSelectRowKeys,
  257 + getSelectRows,
  258 + getRowSelection,
  259 + },
237 ] = useTable({ 260 ] = useTable({
238 title: '设备列表', 261 title: '设备列表',
239 api: devicePage, 262 api: devicePage,
@@ -241,7 +264,6 @@ @@ -241,7 +264,6 @@
241 columns, 264 columns,
242 beforeFetch: (params) => { 265 beforeFetch: (params) => {
243 const { deviceProfileId } = params; 266 const { deviceProfileId } = params;
244 - console.log(deviceProfileId);  
245 const obj = { 267 const obj = {
246 ...params, 268 ...params,
247 ...{ 269 ...{
@@ -272,21 +294,16 @@ @@ -272,21 +294,16 @@
272 slots: { customRender: 'action' }, 294 slots: { customRender: 'action' },
273 fixed: 'right', 295 fixed: 'right',
274 }, 296 },
275 - });  
276 - const { hasBatchDelete, handleDeleteOrBatchDelete, selectionOptions, resetSelectedRowKeys } =  
277 - useBatchDelete(deleteDevice, handleSuccess, setProps);  
278 - selectionOptions.rowSelection.getCheckboxProps = (record: Recordable) => {  
279 - // Demo:status为1的选择框禁用  
280 - if (record.customerId) {  
281 - return { disabled: true };  
282 - } else {  
283 - return { disabled: false };  
284 - }  
285 - };  
286 - nextTick(() => {  
287 - setProps(selectionOptions); 297 + rowSelection: {
  298 + type: 'checkbox',
  299 + getCheckboxProps: (record: DeviceModel) => {
  300 + return { disabled: !!record.customerId };
  301 + },
  302 + },
288 }); 303 });
289 304
  305 + const { isExistOption } = useBatchOperation(getRowSelection, setSelectedRowKeys);
  306 +
290 function getParams(keyword) { 307 function getParams(keyword) {
291 const reg = new RegExp('(^|&)' + keyword + '=([^&]*)(&|$)', 'i'); 308 const reg = new RegExp('(^|&)' + keyword + '=([^&]*)(&|$)', 'i');
292 const r = window.location.search.substr(1).match(reg); 309 const r = window.location.search.substr(1).match(reg);
@@ -349,7 +366,6 @@ @@ -349,7 +366,6 @@
349 } 366 }
350 function handleReload() { 367 function handleReload() {
351 setSelectedRowKeys([]); 368 setSelectedRowKeys([]);
352 - resetSelectedRowKeys();  
353 handleSuccess(); 369 handleSuccess();
354 } 370 }
355 // 取消分配客户 371 // 取消分配客户
@@ -419,6 +435,48 @@ @@ -419,6 +435,48 @@
419 }); 435 });
420 }; 436 };
421 437
  438 + const handleCheckHasDiffenterOrg = (options: DeviceModel[]) => {
  439 + let orgId: string | undefined;
  440 + let flag = false;
  441 + for (const item of options) {
  442 + const _orgId = item.organizationId;
  443 + if (!orgId) orgId = _orgId;
  444 + if (orgId !== _orgId) {
  445 + flag = true;
  446 + break;
  447 + }
  448 + }
  449 + return flag;
  450 + };
  451 +
  452 + const handleBatchAssign = () => {
  453 + const options = getSelectRows();
  454 + if (handleCheckHasDiffenterOrg(options)) {
  455 + createMessage.error('当前选中项中存在不同所属组织的设备!');
  456 + return;
  457 + }
  458 + openCustomerModal(true, options);
  459 + };
  460 +
  461 + const handleDelete = async (record?: DeviceRecord) => {
  462 + let ids: string[] = [];
  463 + if (record) {
  464 + ids.push(record.id);
  465 + } else {
  466 + ids = getSelectRowKeys();
  467 + }
  468 + try {
  469 + setLoading(true);
  470 + await deleteDevice(ids);
  471 + createMessage.success('删除成功');
  472 + handleReload();
  473 + } catch (error) {
  474 + createMessage.error('删除失败');
  475 + } finally {
  476 + setLoading(false);
  477 + }
  478 + };
  479 +
422 return { 480 return {
423 registerTable, 481 registerTable,
424 handleCreate, 482 handleCreate,
@@ -439,14 +497,17 @@ @@ -439,14 +497,17 @@
439 authBtn, 497 authBtn,
440 role, 498 role,
441 copySN, 499 copySN,
442 - hasBatchDelete,  
443 - handleDeleteOrBatchDelete, 500 + isExistOption,
  501 + handleDelete,
  502 + // hasBatchDelete,
  503 + // handleDeleteOrBatchDelete,
444 handleReload, 504 handleReload,
445 registerTbDetailDrawer, 505 registerTbDetailDrawer,
446 handleOpenTbDeviceDetail, 506 handleOpenTbDeviceDetail,
447 handleOpenGatewayDetail, 507 handleOpenGatewayDetail,
448 registerGatewayDetailDrawer, 508 registerGatewayDetailDrawer,
449 handleUpAndDownRecord, 509 handleUpAndDownRecord,
  510 + handleBatchAssign,
450 }; 511 };
451 }, 512 },
452 }); 513 });
@@ -260,6 +260,8 @@ @@ -260,6 +260,8 @@
260 } 260 }
261 }; 261 };
262 const setClientProperties = (record: Recordable) => { 262 const setClientProperties = (record: Recordable) => {
  263 + const type = Reflect.get(record, 'type');
  264 + if (type === 'org.thingsboard.rule.engine.rabbitmq.TbRabbitMqNode') return;
263 const configuration = Reflect.get(record, 'configuration'); 265 const configuration = Reflect.get(record, 'configuration');
264 const clientProperties = Reflect.get(configuration, 'clientProperties'); 266 const clientProperties = Reflect.get(configuration, 'clientProperties');
265 !clientProperties && record.configuration && (record.configuration.clientProperties = {}); 267 !clientProperties && record.configuration && (record.configuration.clientProperties = {});