Commit 9ac8875fa9001f8fda236551e31988c8d5dc9fe4

Authored by dev001
2 parents aef83c96 c72b1b08

Merge branch 'feat/device-batch' into feat/account-role-manage

... ... @@ -57,6 +57,21 @@ enum DeviceManagerApi {
57 57 * @description 批量变更产品
58 58 */
59 59 BATCH_UPDATE_PRODUCT = '/device/batch/update',
  60 +
  61 + /**
  62 + * @description 批量公开设备
  63 + */
  64 + BATCH_PUBLIC_DEVICE = '/batch/customer/public/device/',
  65 +
  66 + /**
  67 + * @description 批量私有设备
  68 + */
  69 + BATCH_PRIVATE_DEVICE = '/batch/customer/device/',
  70 +
  71 + /**
  72 + * @description 批量修改设备
  73 + */
  74 + BATCH_UPDATE_DEVICE = '/device/batch/update',
60 75 }
61 76
62 77 export const devicePage = (params: DeviceQueryParam) => {
... ... @@ -398,3 +413,43 @@ export const doBatchUpdateProduct = (params: { deviceIds: string[]; deviceProfil
398 413 { joinPrefix: false }
399 414 );
400 415 };
  416 +
  417 +/**
  418 + *
  419 + * @param deviceIds tbDeviceId分割的字符串
  420 + * @returns
  421 + */
  422 +export const doBatchPublicDevice = (deviceIds: string) => {
  423 + return defHttp.post(
  424 + {
  425 + url: `${DeviceManagerApi.BATCH_PUBLIC_DEVICE}?deviceIds=${deviceIds}`,
  426 + },
  427 + { joinPrefix: false }
  428 + );
  429 +};
  430 +
  431 +/**
  432 + *
  433 + * @param deviceIds tbDeviceId分割的字符串
  434 + * @returns
  435 + */
  436 +export const doBatchPrivateDevice = (deviceIds: string) => {
  437 + return defHttp.post(
  438 + {
  439 + url: `${DeviceManagerApi.BATCH_PRIVATE_DEVICE}?deviceIds=${deviceIds}`,
  440 + },
  441 + { joinPrefix: false }
  442 + );
  443 +};
  444 +
  445 +/**
  446 + *
  447 + * @param orgId
  448 + * @returns
  449 + */
  450 +export const doBatchUpdateDevice = (orgId: string, data: Recordable[]) => {
  451 + return defHttp.post({
  452 + url: `${DeviceManagerApi.BATCH_UPDATE_DEVICE}?orgId=${orgId}`,
  453 + data,
  454 + });
  455 +};
... ...
... ... @@ -53,6 +53,7 @@ export interface DeviceModel {
53 53 customerId?: string;
54 54 alias?: string;
55 55 tbDeviceId?: string;
  56 + customerName?: string;
56 57 }
57 58
58 59 export interface DeviceProfileModel {
... ... @@ -207,6 +208,8 @@ export interface DeviceRecord {
207 208 manufacturer: string;
208 209 streamMode: string;
209 210 };
  211 + customerName?: string;
  212 + customerId?: string;
210 213 }
211 214
212 215 export interface DeviceModelOfMatterAttrs {
... ...
... ... @@ -102,7 +102,11 @@
102 102 class="flex-auto"
103 103 :disabled="controlDisableStatus"
104 104 />
105   - <Popconfirm v-model:visible="popconfirmVisible" v-bind="getPopConfirmProps">
  105 + <Popconfirm
  106 + :disabled="disabled"
  107 + v-model:visible="popconfirmVisible"
  108 + v-bind="getPopConfirmProps"
  109 + >
106 110 <Button type="primary" :disabled="disabled">
107 111 <Icon
108 112 :icon="controlDisableStatus ? 'ant-design:lock-outlined' : 'ant-design:unlock-outlined'"
... ...
... ... @@ -36,7 +36,7 @@ const updateProductHelpMessage = [
36 36 '修改设备产品时,如果"大屏"、"组态"、"看板"、"场景联动"、"数据流转"等有关联设备,请自行调整并保存。',
37 37 ];
38 38
39   -const updateOrgHelpMessage = [
  39 +export const updateOrgHelpMessage = [
40 40 '注意:',
41 41 '1、修改设备组织时,如果"大屏"、"组态"、"看板"、"场景联动"、"数据流转"等有关联设备,请自行调整并保存。',
42 42 '2、网关设备在修改组织时,其关联网关子设备组织归属于网关组织及以下不用修改,否则将同步更新网关子设备组织为网关组织。',
... ... @@ -314,6 +314,12 @@ export const step1Schemas: FormSchema[] = [
314 314 ifShow: false,
315 315 },
316 316 {
  317 + field: 'customerId',
  318 + label: '用来判断编辑时禁用组织修改',
  319 + component: 'Input',
  320 + ifShow: false,
  321 + },
  322 + {
317 323 field: 'organizationId',
318 324 label: '所属组织',
319 325 component: 'LockControlGroup',
... ... @@ -327,6 +333,7 @@ export const step1Schemas: FormSchema[] = [
327 333 return {
328 334 component: 'OrgTreeSelect',
329 335 defaultLockStatus: !!formModel?.isUpdate,
  336 + disabled: !!formModel?.customerId,
330 337 componentProps: {
331 338 apiTreeSelectProps: {
332 339 params: {
... ...
1 1 import { formatToDateTime } from '/@/utils/dateUtil';
2   -import { FormSchema } from '/@/components/Form';
  2 +import { FormSchema, useComponentRegister } from '/@/components/Form';
3 3 import { BasicColumn } from '/@/components/Table';
4 4 import { DeviceTypeEnum } from '/@/api/device/model/deviceModel';
5 5 import { getCustomerList } from '/@/api/device/deviceManager';
... ... @@ -7,12 +7,15 @@ import { DescItem } from '/@/components/Description/index';
7 7 import moment from 'moment';
8 8 import { CSSProperties, h } from 'vue';
9 9 import { Button, Tooltip } from 'ant-design-vue';
10   -import { TypeEnum } from './data';
  10 +import { TypeEnum, updateOrgHelpMessage } from './data';
11 11 import { PageEnum } from '/@/enums/pageEnum';
12 12 import { useGo } from '/@/hooks/web/usePage';
13 13 import { useAuthDeviceDetail } from '../hook/useAuthDeviceDetail';
14 14 import { findDictItemByCode } from '/@/api/system/dict';
15 15 import { isNullOrUnDef } from '/@/utils/is';
  16 +import { OrgTreeSelect } from '/@/views/common/OrgTreeSelect';
  17 +
  18 +useComponentRegister('OrgTreeSelect', OrgTreeSelect);
16 19
17 20 // 设备详情的描述
18 21 export const descSchema = (emit: EmitType): DescItem[] => {
... ... @@ -407,3 +410,16 @@ export const customerForm: FormSchema[] = [
407 410 colProps: { span: 12 },
408 411 },
409 412 ];
  413 +
  414 +export const orgForm: FormSchema[] = [
  415 + {
  416 + field: 'organizationId',
  417 + component: 'OrgTreeSelect',
  418 + helpMessage: updateOrgHelpMessage,
  419 + label: '选择组织',
  420 + required: true,
  421 + componentProps: {
  422 + placeholder: '请选择',
  423 + },
  424 + },
  425 +];
... ...
  1 +<template>
  2 + <BasicModal
  3 + v-bind="$attrs"
  4 + @register="registerModal"
  5 + title="批量修改设备"
  6 + :canFullscreen="false"
  7 + centered
  8 + @ok="dispatchCustomer"
  9 + @cancel="resetFields"
  10 + :minHeight="300"
  11 + okText="确认"
  12 + >
  13 + <BasicForm @register="registerForm" />
  14 + </BasicModal>
  15 +</template>
  16 +
  17 +<script lang="ts">
  18 + import { defineComponent, ref } from 'vue';
  19 + import { BasicModal, useModalInner } from '/@/components/Modal';
  20 + import { BasicForm, useForm } from '/@/components/Form';
  21 + import { orgForm } from '../../config/detail.config';
  22 + import { doBatchUpdateDevice } from '/@/api/device/deviceManager';
  23 + import { useMessage } from '/@/hooks/web/useMessage';
  24 +
  25 + export default defineComponent({
  26 + name: 'AlarmDetailModal',
  27 + components: {
  28 + BasicModal,
  29 + BasicForm,
  30 + },
  31 + emits: ['reload', 'register'],
  32 + setup(_, { emit }) {
  33 + const { createMessage } = useMessage();
  34 +
  35 + const record = ref([]);
  36 +
  37 + const [registerModal, { closeModal, setModalProps }] = useModalInner((data) => {
  38 + record.value = data;
  39 + });
  40 +
  41 + const [registerForm, { validate, resetFields }] = useForm({
  42 + labelWidth: 100,
  43 + showActionButtonGroup: false,
  44 + schemas: orgForm,
  45 + });
  46 +
  47 + // 分配组织
  48 + const dispatchCustomer = async () => {
  49 + setModalProps({ loading: true });
  50 + try {
  51 + const value = await validate();
  52 + if (!value) return;
  53 + await doBatchUpdateDevice(value.organizationId, record.value);
  54 + closeModal();
  55 + resetFields();
  56 + emit('reload');
  57 + } finally {
  58 + setModalProps({ loading: false });
  59 + createMessage.success('操作成功');
  60 + }
  61 + };
  62 +
  63 + return {
  64 + registerModal,
  65 + registerForm,
  66 + dispatchCustomer,
  67 + resetFields,
  68 + };
  69 + },
  70 + });
  71 +</script>
... ...
... ... @@ -418,6 +418,7 @@
418 418 code: data?.code,
419 419 addressCode: data?.code,
420 420 isUpdate: unref(isUpdate1),
  421 + customerId: data?.customerId,
421 422 });
422 423 }
423 424 // 父组件调用获取字段值的方法
... ...
... ... @@ -23,13 +23,14 @@
23 23 >
24 24 <AuthDropDown
25 25 v-if="authBtn(role)"
26   - :disabled="!isExistOption"
  26 + :disabled="isPublicAndPrivateFlag || !isExistOption"
27 27 :dropMenuList="[
28 28 {
29 29 text: '删除设备',
30 30 auth: DeviceListAuthEnum.DELETE,
31 31 icon: 'ant-design:delete-outlined',
32 32 event: '',
  33 + disabled: !batchPrivateFlag,
33 34 popconfirm: {
34 35 title: '您确定要批量删除数据',
35 36 onConfirm: () => handleDelete(),
... ... @@ -40,6 +41,7 @@
40 41 auth: DeviceListAuthEnum.ASSIGN,
41 42 icon: 'mdi:account-arrow-left',
42 43 event: '',
  44 + disabled: !batchPrivateFlag,
43 45 onClick: handleBatchAssign.bind(null),
44 46 },
45 47 {
... ... @@ -47,12 +49,35 @@
47 49 auth: DeviceListAuthEnum.UPDATE_PRODUCT,
48 50 icon: 'clarity:note-edit-line',
49 51 event: '',
50   - disabled: batchUpdateProductFlag,
  52 + disabled: !batchPrivateFlag || batchUpdateProductFlag,
51 53 onClick: handelOpenBatchUpdateProductModal,
52 54 },
  55 + {
  56 + text: '批量公开',
  57 + icon: 'ant-design:wallet-outlined',
  58 + event: '',
  59 + disabled: !batchPrivateFlag,
  60 + onClick: handleBatchPublic.bind(null),
  61 + },
  62 + {
  63 + text: '批量私有',
  64 + icon: 'ant-design:wallet-outlined',
  65 + event: '',
  66 + disabled: batchPrivateFlag,
  67 + onClick: handleBatchPrivate.bind(null),
  68 + },
  69 + {
  70 + text: '批量修改组织',
  71 + icon: 'ant-design:wallet-outlined',
  72 + event: '',
  73 + disabled: !batchPrivateFlag,
  74 + onClick: handleBatchOrg.bind(null),
  75 + },
53 76 ]"
54 77 >
55   - <Button type="primary" :disabled="!isExistOption">批量操作</Button>
  78 + <Button type="primary" :disabled="isPublicAndPrivateFlag || !isExistOption"
  79 + >批量操作</Button
  80 + >
56 81 </AuthDropDown>
57 82 </Authority>
58 83 </template>
... ... @@ -225,6 +250,7 @@
225 250
226 251 <DeviceModal @register="registerModal" @success="handleSuccess" @reload="handleSuccess" />
227 252 <CustomerModal @register="registerCustomerModal" @reload="handleReload" />
  253 + <OrgModal @register="registerOrgModal" @reload="handleReload" />
228 254
229 255 <BatchImportModal @register="registerImportModal" @import-finally="handleImportFinally" />
230 256
... ... @@ -254,6 +280,8 @@
254 280 getGATEWAY,
255 281 privateDevice,
256 282 publicDevice,
  283 + doBatchPublicDevice,
  284 + doBatchPrivateDevice,
257 285 } from '/@/api/device/deviceManager';
258 286 import { PageEnum } from '/@/enums/pageEnum';
259 287 import { useGo } from '/@/hooks/web/usePage';
... ... @@ -264,6 +292,7 @@
264 292 import { useDrawer } from '/@/components/Drawer';
265 293 import DeviceDetailDrawer from './cpns/modal/DeviceDetailDrawer.vue';
266 294 import CustomerModal from './cpns/modal/CustomerModal.vue';
  295 + import OrgModal from './cpns/modal/OrgModal.vue';
267 296 import BatchImportModal from './cpns/modal/BatchImportModal/index.vue';
268 297 import { useMessage } from '/@/hooks/web/useMessage';
269 298 import { USER_INFO_KEY } from '/@/enums/cacheEnum';
... ... @@ -292,6 +321,7 @@
292 321 const { organizationIdTreeRef, resetFn } = useResetOrganizationTree(searchInfo);
293 322 const [registerModal, { openModal }] = useModal();
294 323 const [registerCustomerModal, { openModal: openCustomerModal }] = useModal();
  324 + const [registerOrgModal, { openModal: openOrgodal }] = useModal();
295 325 const [registerDetailDrawer, { openDrawer }] = useDrawer();
296 326 const [registerTbDetailDrawer, { openDrawer: openTbDeviceDrawer }] = useDrawer();
297 327 const [registerGatewayDetailDrawer, { openDrawer: openGatewayDetailDrawer }] = useDrawer();
... ... @@ -326,6 +356,10 @@
326 356
327 357 const batchUpdateProductFlag = ref(true);
328 358
  359 + const isPublicAndPrivateFlag = ref(false);
  360 +
  361 + const batchPrivateFlag = ref(true);
  362 +
329 363 const [
330 364 registerTable,
331 365 {
... ... @@ -376,7 +410,7 @@
376 410 rowSelection: {
377 411 type: 'checkbox',
378 412 getCheckboxProps: (record: DeviceModel) => {
379   - return { disabled: !!record.customerId };
  413 + return { disabled: !!record.customerId && record.customerName !== 'Public' };
380 414 },
381 415 onSelect(_record, _selected, selectedRows) {
382 416 const [firstItem] = selectedRows as DeviceRecord[];
... ... @@ -384,6 +418,11 @@
384 418 batchUpdateProductFlag.value =
385 419 !selectedRows.length ||
386 420 !selectedRows.every((item) => (item as DeviceRecord).deviceType === deviceType);
  421 +
  422 + batchPrivateFlag.value = selectedRows.some((item: DeviceRecord) => !item?.customerId);
  423 + isPublicAndPrivateFlag.value =
  424 + selectedRows.some((item: DeviceRecord) => !item?.customerId) &&
  425 + selectedRows.some((item: DeviceRecord) => item?.customerAdditionalInfo?.isPublic);
387 426 },
388 427 onSelectAll(_selected, selectedRows) {
389 428 const [firstItem] = selectedRows as DeviceRecord[];
... ... @@ -391,6 +430,11 @@
391 430 batchUpdateProductFlag.value =
392 431 !selectedRows.length ||
393 432 !selectedRows.every((item) => (item as DeviceRecord).deviceType === deviceType);
  433 +
  434 + batchPrivateFlag.value = selectedRows.some((item) => !item?.customerId);
  435 + isPublicAndPrivateFlag.value =
  436 + selectedRows.some((item) => !item?.customerId) &&
  437 + selectedRows.some((item) => item?.customerAdditionalInfo?.isPublic);
394 438 },
395 439 },
396 440 });
... ... @@ -492,6 +536,39 @@
492 536 openCustomerModal(true, options);
493 537 };
494 538
  539 + const handleBatchOrg = () => {
  540 + const options = getSelectRows();
  541 + openOrgodal(true, options);
  542 + };
  543 +
  544 + // 批量公开设备
  545 + const handleBatchPublic = async () => {
  546 + setLoading(true);
  547 + try {
  548 + const options = getSelectRows();
  549 + const tbDeviceIdJoinStr = options.map((item) => item.tbDeviceId).join(',');
  550 + const res = await doBatchPublicDevice(tbDeviceIdJoinStr);
  551 + createMessage.success(res);
  552 + } finally {
  553 + handleReload();
  554 + setLoading(false);
  555 + }
  556 + };
  557 +
  558 + // 批量私有设备
  559 + const handleBatchPrivate = async () => {
  560 + setLoading(true);
  561 + try {
  562 + const options = getSelectRows();
  563 + const tbDeviceIdJoinStr = options.map((item) => item.tbDeviceId).join(',');
  564 + const res = await doBatchPrivateDevice(tbDeviceIdJoinStr);
  565 + createMessage.success(res);
  566 + } finally {
  567 + handleReload();
  568 + setLoading(false);
  569 + }
  570 + };
  571 +
495 572 const handleDelete = async (record?: DeviceRecord) => {
496 573 let ids: string[] = [];
497 574 if (record) {
... ...