Commit 906f2771c65d24d2054b929c765e27e5f2c81c47

Authored by xp.Huang
2 parents da9fcd94 629206a2

Merge branch 'sqy_dev' into 'main'

fix:修改租户管理:验证,400错误,fix:我的通知显示’无‘问题     fefactor:重构公共组件组织树

See merge request huang/yun-teng-iot-front!43
... ... @@ -72,18 +72,16 @@ export const deleteMessageTemplate = (ids: string[]) =>
72 72 * @param id 配置id
73 73 * @param status 状态
74 74 */
75   -export const setMessageTemplateStatus = (
76   - id: string,
77   - templatePurpose: string,
78   - messageType: string,
79   - status: number
80   -) =>
  75 +
  76 +interface dataType {
  77 + id: string;
  78 + templatePurpose: string;
  79 + messageType: string;
  80 + status: number;
  81 + tenantId: string;
  82 +}
  83 +export const setMessageTemplateStatus = (data: dataType) =>
81 84 defHttp.put<MessageConfigResultModel>({
82 85 url: MessageTemplateApi.TEMPLATE_URL,
83   - data: {
84   - id,
85   - status,
86   - templatePurpose,
87   - messageType,
88   - },
  86 + data,
89 87 });
... ...
1   -<template> 111</template>
... ... @@ -41,7 +41,7 @@
41 41
42 42 import { defineComponent } from 'vue';
43 43 import { Dropdown, Menu, Popconfirm } from 'ant-design-vue';
44   - // import { Icon } from '/@/components/Icon';
  44 + import { Icon } from '/@/components/Icon';
45 45 import { omit } from 'lodash-es';
46 46 import { isFunction } from '/@/utils/is';
47 47
... ... @@ -52,7 +52,7 @@
52 52 Menu,
53 53 MenuItem: Menu.Item,
54 54 MenuDivider: Menu.Divider,
55   - // Icon,
  55 + Icon,
56 56 Popconfirm,
57 57 },
58 58 props: {
... ...
... ... @@ -88,7 +88,7 @@
88 88 const wrapEl = unref(wrapRef);
89 89 const map = new BMap.Map(wrapEl);
90 90 if (record.deviceInfo.address) {
91   - const { name, organizationDTO, updateTime, deviceState } = record;
  91 + const { name, organizationDTO, updateTime, deviceState, deviceProfile } = record;
92 92 const { longitude, latitude, address } = record.deviceInfo;
93 93 const point = new BMap.Point(longitude, latitude);
94 94 let options = {
... ... @@ -110,7 +110,7 @@
110 110 }
111 111 </div>
112 112 <div>所属组织:${organizationDTO.name}</div>
113   - <div style="margin-top:6px;">接入协议:${address}</div>
  113 + <div style="margin-top:6px;">接入协议:${deviceProfile.transportType}</div>
114 114 <div style="margin-top:6px;">设备位置:${address}</div>
115 115 <div style="margin-top:6px;">下线时间:${updateTime}</div>
116 116 <div style="display:flex;justify-content:space-between; margin-top:10px">
... ...
1 1 <template>
2 2 <div>
3 3 <PageWrapper dense contentFullHeight fixedHeight contentClass="flex">
4   - <OrganizationIdTree class="w-1/4 xl:w-1/5" @select="handleSelect" />
  4 + <OrganizationIdTree
  5 + class="w-1/4 xl:w-1/5"
  6 + @select="handleSelect"
  7 + ref="organizationIdTreeRef"
  8 + />
5 9 <BasicTable @register="registerTable" :searchInfo="searchInfo" class="w-3/4 xl:w-4/5">
6 10 <template #toolbar>
7 11 <a-button type="primary" @click="handleCreateOrEdit(null)"> 新增联系人 </a-button>
... ... @@ -47,7 +51,8 @@
47 51 import { useMessage } from '/@/hooks/web/useMessage';
48 52 import { useDrawer } from '/@/components/Drawer';
49 53 import ContactDrawer from './ContactDrawer.vue';
50   - import OrganizationIdTree from '../../common/OrganizationIdTree.vue';
  54 + import { useResetOrganizationTree, OrganizationIdTree } from '/@/views/common/organizationIdTree';
  55 +
51 56 import { getAlarmContact, deleteAlarmContact } from '/@/api/alarm/contact/alarmContact';
52 57 import { searchFormSchema, columns } from './config.data';
53 58 export default defineComponent({
... ... @@ -65,6 +70,8 @@
65 70 const onSelectRowChange = (selectedRowKeys: string[]) => {
66 71 selectedRowIds.value = selectedRowKeys;
67 72 };
  73 + const searchInfo = reactive<Recordable>({});
  74 + const { organizationIdTreeRef, resetFn } = useResetOrganizationTree(searchInfo);
68 75 // 表格hooks
69 76 const [registerTable, { reload }] = useTable({
70 77 api: getAlarmContact,
... ... @@ -73,6 +80,7 @@
73 80 formConfig: {
74 81 labelWidth: 120,
75 82 schemas: searchFormSchema,
  83 + resetFunc: resetFn,
76 84 },
77 85 useSearchForm: true,
78 86 showTableSetting: true,
... ... @@ -93,7 +101,7 @@
93 101 // 弹框
94 102 const [registerDrawer, { openDrawer }] = useDrawer();
95 103 const { createMessage } = useMessage();
96   - const searchInfo = reactive<Recordable>({});
  104 +
97 105 // 刷新
98 106 const handleSuccess = () => {
99 107 reload();
... ... @@ -147,6 +155,7 @@
147 155 handleSuccess,
148 156 registerTable,
149 157 registerDrawer,
  158 + organizationIdTreeRef,
150 159 };
151 160 },
152 161 });
... ...
  1 +/**
  2 + * 这是一个重置OrganizationIdTree组件的hooks,目的是用于重置分页查询的字段。
  3 + *
  4 + * 使用方式 const {organizationIdTreeRef,resetFn} = useResetOrganizationTree()
  5 + *
  6 + * organizationIdTreeRef 将ref和organizationIdTreeRef绑定即可,第二个是重置的方法
  7 + */
  8 +
  9 +import { ref } from 'vue';
  10 +import organizationIdTree from '../OrganizationIdTree.vue';
  11 +export const useResetOrganizationTree = (searchInfo: Recordable) => {
  12 + const organizationIdTreeRef = ref<InstanceType<typeof organizationIdTree>>();
  13 + async function resetFn() {
  14 + organizationIdTreeRef.value.resetOrganization();
  15 + searchInfo.organizationId = null;
  16 + }
  17 + return { organizationIdTreeRef, resetFn };
  18 +};
... ...
  1 +import { useResetOrganizationTree } from './hooks/useOrganization';
  2 +import OrganizationIdTree from './src/OrganizationIdTree.vue';
  3 +
  4 +export { OrganizationIdTree, useResetOrganizationTree };
... ...
... ... @@ -7,6 +7,7 @@
7 7 :clickRowToExpand="false"
8 8 :treeData="treeData"
9 9 :replaceFields="{ key: 'id', title: 'name' }"
  10 + v-model:selectedKeys="selectedKeys"
10 11 @select="handleSelect"
11 12 />
12 13 </div>
... ... @@ -24,7 +25,7 @@
24 25 emits: ['select'],
25 26 setup(_, { emit }) {
26 27 const treeData = ref<TreeItem[]>([]);
27   -
  28 + const selectedKeys = ref<string[]>();
28 29 async function fetch() {
29 30 treeData.value = (await getOrganizationList()) as unknown as TreeItem[];
30 31 }
... ... @@ -32,11 +33,14 @@
32 33 function handleSelect(keys) {
33 34 emit('select', keys[0]);
34 35 }
  36 + function resetOrganization() {
  37 + selectedKeys.value = [];
  38 + }
35 39
36 40 onMounted(() => {
37 41 fetch();
38 42 });
39   - return { treeData, handleSelect };
  43 + return { treeData, handleSelect, resetOrganization, selectedKeys };
40 44 },
41 45 });
42 46 </script>
... ...
1 1 <template>
2 2 <div>
3 3 <PageWrapper dense contentFullHeight contentClass="flex">
4   - <OrganizationIdTree class="w-1/6 xl:w-1/5" @select="handleSelect" />
  4 + <OrganizationIdTree
  5 + class="w-1/6 xl:w-1/5"
  6 + @select="handleSelect"
  7 + ref="organizationIdTreeRef"
  8 + />
5 9 <BasicTable @register="registerTable" class="w-5/6 xl:w-4/5">
6 10 <template #toolbar>
7 11 <a-button type="primary" @click="handleCreate"> 新增设备 </a-button>
... ... @@ -86,7 +90,8 @@
86 90 import { useGo } from '/@/hooks/web/usePage';
87 91 import { PageWrapper } from '/@/components/Page';
88 92 import { useModal } from '/@/components/Modal';
89   - import OrganizationIdTree from '/@/views/common/OrganizationIdTree.vue';
  93 + import { OrganizationIdTree, useResetOrganizationTree } from '/@/views/common/organizationIdTree';
  94 +
90 95 import DeviceModal from './cpns/modal/DeviceModal.vue';
91 96 import { useDrawer } from '/@/components/Drawer';
92 97 import DeviceDetailDrawer from './cpns/modal/DeviceDetailDrawer.vue';
... ... @@ -107,6 +112,7 @@
107 112 const go = useGo();
108 113
109 114 const searchInfo = reactive<Recordable>({});
  115 + const { organizationIdTreeRef, resetFn } = useResetOrganizationTree(searchInfo);
110 116 const [registerModal, { openModal }] = useModal();
111 117 const [registerDetailDrawer, { openDrawer }] = useDrawer();
112 118 const [registerTable, { reload }] = useTable({
... ... @@ -116,6 +122,7 @@
116 122 formConfig: {
117 123 labelWidth: 120,
118 124 schemas: searchFormSchema,
  125 + resetFunc: resetFn,
119 126 },
120 127 useSearchForm: true,
121 128 showTableSetting: true,
... ... @@ -188,6 +195,7 @@
188 195 DeviceTypeEnum,
189 196 DeviceState,
190 197 searchInfo,
  198 + organizationIdTreeRef,
191 199 };
192 200 },
193 201 });
... ...
  1 +import { MessageTypeEnum } from '/@/api/tenant/tenantInfo';
1 2 import { BasicColumn } from '/@/components/Table';
2 3 import { FormSchema } from '/@/components/Table';
3 4 import { h } from 'vue';
... ... @@ -28,7 +29,13 @@ export const columns: BasicColumn[] = [
28 29 title: '平台类型',
29 30 dataIndex: 'platformType',
30 31 format: (_, record: Recordable) => {
31   - return record.platformType === 'ALI_CLOUD' ? '阿里云平台' : '腾讯云平台';
  32 + return record.messageType === MessageTypeEnum.EMAIL_MESSAGE
  33 + ? '无'
  34 + : record.messageType === MessageTypeEnum.PHONE_MESSAGE
  35 + ? record.platformType === 'ALI_CLOUD'
  36 + ? '阿里云平台'
  37 + : '腾讯云平台'
  38 + : '无';
32 39 },
33 40 width: 180,
34 41 },
... ...
... ... @@ -39,9 +39,7 @@
39 39 for (const key in config) {
40 40 Reflect.set(data.record, key + '', config[key]);
41 41 }
42   - await setFieldsValue({
43   - ...data.record,
44   - });
  42 + await setFieldsValue(data.record);
45 43 }
46 44 });
47 45
... ...
... ... @@ -49,10 +49,17 @@ export const columns: BasicColumn[] = [
49 49 unCheckedChildren: '已禁用',
50 50 loading: record.pendingStatus,
51 51 onChange(checked: boolean) {
  52 + const { id, templatePurpose, messageType, tenantId } = record;
52 53 record.pendingStatus = true;
53 54 const newStatus = checked ? 1 : 0;
54 55 const { createMessage } = useMessage();
55   - setMessageTemplateStatus(record.id, record.templatePurpose, record.messageType, newStatus)
  56 + setMessageTemplateStatus({
  57 + id,
  58 + templatePurpose,
  59 + messageType,
  60 + tenantId,
  61 + status: newStatus,
  62 + })
56 63 .then(() => {
57 64 record.status = newStatus;
58 65 createMessage.success(`修改成功`);
... ... @@ -163,4 +170,10 @@ export const formSchema: FormSchema[] = [
163 170 valueField: 'itemValue',
164 171 },
165 172 },
  173 + {
  174 + field: 'tenantId',
  175 + label: '租户ID',
  176 + component: 'Input',
  177 + show: false,
  178 + },
166 179 ];
... ...
1 1 <template>
2 2 <div>
3   - <PageWrapper dense contentFullHeight fixedHeight contentClass="flex">
4   - <OrganizationIdTree class="w-1/4 xl:w-1/5" @select="handleSelect" />
5   - <BasicTable @register="registerTable" class="w-3/4 xl:w-4/5" :searchInfo="searchInfo">
  3 + <PageWrapper dense contentFullHeight contentClass="flex">
  4 + <OrganizationIdTree
  5 + class="w-1/4 xl:w-1/5"
  6 + @select="handleSelect"
  7 + ref="organizationIdTreeRef"
  8 + />
  9 + <BasicTable @register="registerTable" class="w-3/4 xl:w-4/5">
6 10 <template #toolbar>
7 11 <a-button type="primary" @click="handleCreate">新增账号</a-button>
8 12 </template>
... ... @@ -63,13 +67,10 @@
63 67 </template>
64 68 <script lang="ts">
65 69 import { defineComponent, reactive } from 'vue';
66   -
67   - import { TenantCodeEnum } from '/@/enums/codeEnum';
68 70 import { BasicTable, useTable, TableAction } from '/@/components/Table';
69 71 import { deleteUser, getAccountList } from '/@/api/system/system';
70 72 import { PageWrapper } from '/@/components/Page';
71   -
72   - import OrganizationIdTree from '../../common/OrganizationIdTree.vue';
  73 + import { useResetOrganizationTree, OrganizationIdTree } from '/@/views/common/organizationIdTree';
73 74 import { Tag } from 'ant-design-vue';
74 75 import { useModal } from '/@/components/Modal';
75 76 import AccountModal from './AccountModal.vue';
... ... @@ -85,23 +86,23 @@
85 86 const go = useGo();
86 87 const [registerModal, { openModal }] = useModal();
87 88 const { createMessage } = useMessage();
88   - const searchInfo = reactive<Recordable>({});
  89 + let searchInfo = reactive<Recordable>({});
  90 + const { organizationIdTreeRef, resetFn } = useResetOrganizationTree(searchInfo);
89 91 const [registerTable, { reload, updateTableDataRecord }] = useTable({
90 92 title: '账号列表',
91 93 api: getAccountList,
92 94 rowKey: 'id',
93 95 columns,
  96 + searchInfo,
94 97 formConfig: {
95 98 labelWidth: 120,
96 99 schemas: searchFormSchema,
97 100 autoSubmitOnEnter: true,
  101 + resetFunc: resetFn,
98 102 },
99 103 useSearchForm: true,
100 104 showTableSetting: true,
101 105 bordered: true,
102   - handleSearchInfoFn(info) {
103   - return info;
104   - },
105 106 actionColumn: {
106 107 width: 220,
107 108 title: '操作',
... ... @@ -115,7 +116,6 @@
115 116 isUpdate: false,
116 117 });
117 118 }
118   -
119 119 function handleEdit(record: Recordable) {
120 120 openModal(true, {
121 121 record,
... ... @@ -159,8 +159,7 @@
159 159 handleSuccess,
160 160 handleSelect,
161 161 handleView,
162   - searchInfo,
163   - TenantCodeEnum,
  162 + organizationIdTreeRef,
164 163 };
165 164 },
166 165 });
... ...
... ... @@ -16,7 +16,8 @@
16 16 import { BasicForm, FormSchema, useForm } from '/@/components/Form/index';
17 17 import { saveTenantAdmin } from '/@/api/tenant/tenantApi';
18 18 import { UserDTO } from '/@/api/tenant/tenantInfo';
19   - import { emailRule, phoneRule } from '/@/utils/rules';
  19 + import { ChineseRegexp, EmailRegexp, emailRule, phoneRule } from '/@/utils/rules';
  20 + import { isAccountExist } from '/@/api/system/system';
20 21
21 22 export default defineComponent({
22 23 name: 'TenantAdminFormDrawer',
... ... @@ -38,8 +39,36 @@
38 39 {
39 40 field: 'username',
40 41 label: '账号',
41   - required: true,
42 42 component: 'Input',
  43 + dynamicRules: ({ values }) => {
  44 + return [
  45 + {
  46 + validator(_, value) {
  47 + return new Promise((resolve, reject) => {
  48 + if (value == '') {
  49 + reject('请输入账号');
  50 + } else if (ChineseRegexp.test(value)) {
  51 + reject('账号不能含有中文');
  52 + } else if (EmailRegexp.test(value)) {
  53 + reject('账号不能为电子邮箱格式');
  54 + } else {
  55 + if (values.username != undefined && values.id == undefined) {
  56 + isAccountExist(value).then((data) => {
  57 + if (data.data != null) {
  58 + reject('账号已存在');
  59 + } else {
  60 + resolve();
  61 + }
  62 + });
  63 + } else {
  64 + resolve();
  65 + }
  66 + }
  67 + });
  68 + },
  69 + },
  70 + ];
  71 + },
43 72 },
44 73 {
45 74 field: 'realName',
... ... @@ -86,7 +115,6 @@
86 115 await resetFields();
87 116 isUpdate.value = !!data?.isUpdate;
88 117 tenantId.value = data?.tenantId;
89   -
90 118 if (unref(isUpdate)) {
91 119 await updateSchema({ field: 'username', componentProps: { disabled: true } });
92 120 await setFieldsValue({
... ...