Commit 222a16b9b1a019231eb5bb7173783d69dec0f20a

Authored by fengtao
2 parents 97e7c6e1 195ab9cb

Merge branch 'main' into local_dev_ft

... ... @@ -69,6 +69,7 @@ export interface ComponentInfo {
69 69 icon: string;
70 70 value?: string | number;
71 71 updateTime?: number;
  72 + showDeviceName: boolean;
72 73 }
73 74
74 75 export interface DataSource {
... ...
... ... @@ -14,7 +14,7 @@ enum Api {
14 14 * @description: Get user menu based on id
15 15 */
16 16
17   -export const getMenuList = (args) => {
  17 +export const getMenuList = (args?: number) => {
18 18 const userStore = useUserStore();
19 19 let url = Api.GetMenuList;
20 20 try {
... ...
... ... @@ -94,3 +94,9 @@ export function forgetPasswordApi(data) {
94 94 data,
95 95 });
96 96 }
  97 +
  98 +export const getUserToken = (id: string) => {
  99 + return defHttp.get<Record<'token' | 'refreshToken', string>>({
  100 + url: `/third/login/id/${id}`,
  101 + });
  102 +};
... ...
  1 +<svg t="1671693733059" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="25398" width="200" height="200"><path d="M585.290213 343.524914c0-37.136805-33.106002-67.357084-73.789587-67.357084-40.684608 0-73.78754 30.220279-73.78754 67.357084 0 37.120432 33.102932 67.329455 73.78754 67.329455C552.184211 410.854369 585.290213 380.645346 585.290213 343.524914zM467.227284 343.524914c0-22.289656 19.860327-40.417525 44.273343-40.417525 24.415063 0 44.273343 18.127869 44.273343 40.417525 0 22.271236-19.85828 40.389896-44.273343 40.389896C487.087611 383.91481 467.227284 365.79615 467.227284 343.524914zM511.141446 741.92667l11.455913-12.768815c8.013511-8.92937 204.344015-220.213447 203.697285-358.458021-0.506537-111.529119-101.375875-208.420867-214.795041-208.420867-113.923656 0-215.816301 96.907097-215.816301 208.483288 0 138.182153 196.101283 349.432461 204.055442 358.356714L511.141446 741.92667zM511.500627 189.219549c97.206926 0 184.830589 84.850503 185.278797 181.601034 0.517793 109.87034-144.55121 279.638885-185.553043 328.016709-40.799218-48.428989-186.026834-218.179115-186.026834-328.072991C325.199547 274.042423 413.845493 189.219549 511.500627 189.219549zM946.101839 789.395798 832.104505 600.221274c-10.316973-17.114796-30.019711-27.752064-51.421136-27.752064L673.835875 572.46921c-8.156774 0-14.759145 6.03239-14.759145 13.472849 0 7.436366 6.601348 13.46671 14.759145 13.46671l106.848517 0c10.636245 0 20.437471 5.287423 25.566282 13.795191L920.250054 802.37746c5.013177 8.337899 4.856611 18.201547-0.448208 26.380834-5.303796 8.190543-14.699794 13.077853-25.120121 13.077853L128.318504 841.836147c-10.41828 0-19.814278-4.88731-25.119097-13.077853-5.302773-8.179286-5.462408-18.042935-0.446162-26.380834l113.997334-189.174524c5.130857-8.507768 14.930037-13.795191 25.566282-13.795191L349.166401 599.407746c8.15575 0 14.757099-6.030343 14.757099-13.46671 0-7.440459-6.601348-13.472849-14.757099-13.472849L242.317884 572.468187c-21.401426 0-41.10314 10.638291-51.42216 27.752064L76.898391 789.395798c-10.102079 16.758686-9.769504 36.600593 0.89437 53.073776 10.665921 16.476253 29.5582 26.307156 50.526767 26.307156L894.680702 868.776729c20.968567 0 39.862893-9.830903 50.528813-26.307156C955.87339 825.99639 956.204941 806.154483 946.101839 789.395798z" fill="#bfbfbf" p-id="25399"></path></svg>
... ...
1   -<script lang="ts" setup></script>
  1 +<script lang="ts" setup>
  2 + import { Dropdown, Menu, Popconfirm } from 'ant-design-vue';
  3 + import { computed, useSlots } from 'vue';
  4 + import Icon from '../Icon';
  5 + import { usePermission } from '/@/hooks/web/usePermission';
2 6
3   -<template> <div></div></template>
  7 + export interface AuthDropDownProps {
  8 + dropMenuList: AuthDropMenuList[];
  9 + trigger?: ('contextmenu' | 'click' | 'hover')[];
  10 + }
  11 +
  12 + export interface AuthDropMenuList {
  13 + icon?: string;
  14 + event: string | number;
  15 + text: string;
  16 + disabled?: boolean;
  17 + divider?: boolean;
  18 + auth?: string;
  19 + onClick?: Fn;
  20 + popconfirm?: {
  21 + cancelText?: string;
  22 + okText?: string;
  23 + okType?: string;
  24 + title?: string;
  25 + icon?: string;
  26 + disabled?: boolean;
  27 + onCancel?: Fn;
  28 + onConfirm?: Fn;
  29 + onVisibleChange?: Fn;
  30 + };
  31 + }
  32 +
  33 + const props = defineProps<AuthDropDownProps>();
  34 +
  35 + const slot = useSlots();
  36 +
  37 + const { hasPermission } = usePermission();
  38 +
  39 + const getMenuList = computed(() => {
  40 + const { dropMenuList } = props;
  41 + return dropMenuList.filter((menu) => (menu.auth ? hasPermission(menu.auth) : true));
  42 + });
  43 +
  44 + const hasDefaultSlot = computed(() => {
  45 + return !!slot.default;
  46 + });
  47 +</script>
  48 +
  49 +<template>
  50 + <Dropdown :trigger="$props.trigger">
  51 + <template #overlay>
  52 + <Menu v-if="getMenuList.length">
  53 + <template v-for="item in getMenuList" :key="item.event">
  54 + <Menu.Divider v-if="item.divider" />
  55 + <Menu.Item v-if="!item.popconfirm" @click="item.onClick">
  56 + <span class="flex justify-center items-center">
  57 + <Icon :icon="item.icon" />
  58 + <span class="ml-2">{{ item.text }}</span>
  59 + </span>
  60 + </Menu.Item>
  61 + <Menu.Item v-if="item.popconfirm">
  62 + <Popconfirm v-bind="item.popconfirm">
  63 + <template v-if="item.popconfirm.icon" #icon>
  64 + <Icon :icon="item.popconfirm.icon" />
  65 + </template>
  66 + <span class="flex justify-center items-center">
  67 + <Icon :icon="item.icon" />
  68 + <span class="ml-2">{{ item.text }}</span>
  69 + </span>
  70 + </Popconfirm>
  71 + </Menu.Item>
  72 + </template>
  73 + </Menu>
  74 + </template>
  75 + <Icon
  76 + v-if="!hasDefaultSlot"
  77 + class="items-center justify-center"
  78 + icon="ant-design:ellipsis-outlined"
  79 + :class="!getMenuList.length ? '!text-gray-200 !cursor-not-allowed' : ''"
  80 + />
  81 + <slot name="default"></slot>
  82 + </Dropdown>
  83 +</template>
... ...
  1 +<script lang="ts" setup>
  2 + import { Icon } from '/@/components/Icon';
  3 + import { computed, ExtractPropTypes, unref } from 'vue';
  4 + import { usePermission } from '/@/hooks/web/usePermission';
  5 +
  6 + interface AuthIconProps extends ExtractPropTypes<typeof Icon> {
  7 + auth?: string;
  8 + }
  9 +
  10 + const props = defineProps<AuthIconProps>();
  11 +
  12 + const emit = defineEmits(['click']);
  13 +
  14 + const { hasPermission } = usePermission();
  15 +
  16 + const getHasPermission = computed(() => {
  17 + const { auth } = props;
  18 + return auth ? hasPermission(auth) : true;
  19 + });
  20 +
  21 + const getBindProps = computed(() => {
  22 + return {
  23 + ...props,
  24 + ...(unref(getHasPermission) ? { onClick: (event: Event) => emit('click', event) } : {}),
  25 + };
  26 + });
  27 +</script>
  28 +
  29 +<template>
  30 + <Icon
  31 + v-bind="getBindProps"
  32 + class="justify-center items-center"
  33 + :class="getHasPermission ? '' : '!cursor-not-allowed !text-gray-200'"
  34 + />
  35 +</template>
... ...
1 1 export { default as ModeSwitchButton } from './ModeSwitchButton.vue';
2 2 export { default as CardLayoutButton } from './CardLayoutButton.vue';
  3 +export { default as AuthIcon } from './AuthIcon.vue';
3 4 export {
4 5 EnumTableCardMode,
5 6 EnumTableChartMode,
... ...
... ... @@ -26,3 +26,26 @@ export function buildShortUUID(prefix = ''): string {
26 26 unique++;
27 27 return prefix + '_' + random + unique + String(time);
28 28 }
  29 +
  30 +export function getlowerCaseChars() {
  31 + return Array.from({ length: 26 }).map((_, index) => String.fromCharCode(index + 97));
  32 +}
  33 +
  34 +export function getUpperCaseChars() {
  35 + return Array.from({ length: 26 }).map((_, index) => String.fromCharCode(index + 65));
  36 +}
  37 +
  38 +export function randomString(length = 20) {
  39 + const upperCaseChars = getUpperCaseChars();
  40 + const lowerCaseChars = getlowerCaseChars();
  41 + const numberChars = Array.from({ length: 10 }).map((_, index) => index);
  42 + const allChars = [...numberChars, ...upperCaseChars, ...lowerCaseChars].sort(
  43 + () => 0.5 - Math.random()
  44 + );
  45 +
  46 + const rangeFn = () => (Math.random() * 63) | 0;
  47 +
  48 + return Array.from({ length })
  49 + .map(() => allChars[rangeFn()])
  50 + .join('');
  51 +}
... ...
1 1 <script setup lang="ts">
2 2 import { List, Card, Button, PaginationProps, Popover, Slider, Tooltip } from 'ant-design-vue';
3   - import {
4   - ReloadOutlined,
5   - AppstoreOutlined,
6   - EyeOutlined,
7   - EditOutlined,
8   - EllipsisOutlined,
9   - } from '@ant-design/icons-vue';
  3 + import { ReloadOutlined, AppstoreOutlined } from '@ant-design/icons-vue';
10 4 import { computed, onMounted, reactive, ref, unref } from 'vue';
11 5 import { OrganizationIdTree, useResetOrganizationTree } from '../../common/organizationIdTree';
12 6 import {
... ... @@ -15,7 +9,6 @@
15 9 } from '/@/api/configuration/center/configurationCenter';
16 10 import { ConfigurationCenterItemsModal } from '/@/api/configuration/center/model/configurationCenterModal';
17 11 import { PageWrapper } from '/@/components/Page';
18   - import { Dropdown } from '/@/components/Dropdown';
19 12 import { BasicForm, useForm } from '/@/components/Form';
20 13 import { ConfigurationPermission, searchFormSchema } from './center.data';
21 14 import { useMessage } from '/@/hooks/web/useMessage';
... ... @@ -23,12 +16,13 @@
23 16 import { isDevMode } from '/@/utils/env';
24 17 import ConfigurationCenterDrawer from './ConfigurationCenterDrawer.vue';
25 18 import { useDrawer } from '/@/components/Drawer';
26   - import { useSyncConfirm } from '/@/hooks/component/useSyncConfirm';
27 19 import { getBoundingClientRect } from '/@/utils/domUtils';
28 20 import configurationSrc from '/@/assets/icons/configuration.svg';
29 21 import { cloneDeep } from 'lodash';
30 22 import { usePermission } from '/@/hooks/web/usePermission';
31 23 import { useGlobSetting } from '/@/hooks/setting';
  24 + import { AuthIcon } from '/@/components/Widget';
  25 + import AuthDropDown from '/@/components/Widget/AuthDropDown.vue';
32 26
33 27 const listColumn = ref(5);
34 28
... ... @@ -136,10 +130,8 @@
136 130 window.open(`${configurationPrefix}/${isDev ? '?dev=1&' : '?'}configurationId=${record!.id}`);
137 131 };
138 132
139   - const { createSyncConfirm } = useSyncConfirm();
140 133 const handleDelete = async (record: ConfigurationCenterItemsModal) => {
141 134 try {
142   - await createSyncConfirm({ iconType: 'warning', content: '是否确认删除操作?' });
143 135 await deleteConfigurationCenter([record.id]);
144 136 createMessage.success('删除成功');
145 137 await getListData();
... ... @@ -227,20 +219,44 @@
227 219 </template>
228 220 <template class="ant-card-actions" #actions>
229 221 <Tooltip title="预览">
230   - <EyeOutlined
231   - :class="getPreviewFlag ? '' : '!cursor-not-allowed !text-gray-200'"
232   - key="setting"
  222 + <AuthIcon
  223 + :auth="ConfigurationPermission.PREVIEW"
  224 + class="!text-lg"
  225 + icon="ant-design:eye-outlined"
233 226 @click="handlePreview(item)"
234 227 />
235 228 </Tooltip>
236 229 <Tooltip title="设计">
237   - <EditOutlined
238   - :class="getDesignFlag ? '' : '!cursor-not-allowed !text-gray-200'"
239   - key="edit"
  230 + <AuthIcon
  231 + :auth="ConfigurationPermission.DESIGN"
  232 + class="!text-lg"
  233 + icon="ant-design:edit-outlined"
240 234 @click="handleDesign(item)"
241 235 />
242 236 </Tooltip>
243   - <Dropdown
  237 + <AuthDropDown
  238 + :dropMenuList="[
  239 + {
  240 + text: '编辑',
  241 + auth: ConfigurationPermission.UPDATE,
  242 + icon: 'clarity:note-edit-line',
  243 + event: '',
  244 + onClick: handleCreateOrUpdate.bind(null, item),
  245 + },
  246 + {
  247 + text: '删除',
  248 + auth: ConfigurationPermission.DELETE,
  249 + icon: 'ant-design:delete-outlined',
  250 + event: '',
  251 + popconfirm: {
  252 + title: '是否确认删除操作?',
  253 + onConfirm: handleDelete.bind(null, item),
  254 + },
  255 + },
  256 + ]"
  257 + :trigger="['hover']"
  258 + />
  259 + <!-- <Dropdown
244 260 :dropMenuList="[
245 261 {
246 262 text: '编辑',
... ... @@ -261,7 +277,7 @@
261 277 :trigger="['hover']"
262 278 >
263 279 <EllipsisOutlined key="ellipsis" />
264   - </Dropdown>
  280 + </Dropdown> -->
265 281 </template>
266 282 <Card.Meta>
267 283 <template #title>
... ...
... ... @@ -80,6 +80,9 @@ export const step1Schemas: FormSchema[] = [
80 80 gatewayId: null,
81 81 });
82 82 },
  83 + showSearch: true,
  84 + filterOption: (inputValue: string, option: Record<'label' | 'value', string>) =>
  85 + option.label.includes(inputValue),
83 86 };
84 87 },
85 88 },
... ... @@ -399,6 +402,7 @@ export const step2Schemas: FormSchema[] = [
399 402 field: 'credentialsId',
400 403 required: true,
401 404 ifShow: false,
  405 + slot: 'credentialsId',
402 406 componentProps: {
403 407 maxLength: 36,
404 408 placeholder: '请输入访问令牌',
... ... @@ -421,6 +425,7 @@ export const step2Schemas: FormSchema[] = [
421 425 field: 'clientId',
422 426 required: true,
423 427 ifShow: false,
  428 + slot: 'clientId',
424 429 componentProps: {
425 430 maxLength: 36,
426 431 placeholder: '请输入客户端ID',
... ... @@ -597,6 +602,7 @@ export const TokenSchemas: FormSchema[] = [
597 602 field: 'credentialsId',
598 603 required: true,
599 604 ifShow: false,
  605 + slot: 'credentialsId',
600 606 componentProps: {
601 607 maxLength: 36,
602 608 placeholder: '请输入访问令牌',
... ...
... ... @@ -90,6 +90,7 @@ export const searchFormSchema: FormSchema[] = [
90 90 { label: '直连设备', value: DeviceTypeEnum.DIRECT_CONNECTION },
91 91 { label: '网关子设备', value: DeviceTypeEnum.SENSOR },
92 92 ],
  93 + placeholder: '请选择设备类型',
93 94 },
94 95 colProps: { span: 6 },
95 96 },
... ... @@ -103,13 +104,14 @@ export const searchFormSchema: FormSchema[] = [
103 104 { label: '在线', value: DeviceState.ONLINE },
104 105 { label: '离线', value: DeviceState.OFFLINE },
105 106 ],
  107 + placeholder: '请选择设备状态',
106 108 },
107 109 colProps: { span: 6 },
108 110 },
109 111 {
110 112 field: 'deviceProfileId',
111 113 label: '产品',
112   - component: 'ApiSearchSelect',
  114 + component: 'ApiSelect',
113 115 colProps: { span: 6 },
114 116 componentProps: () => {
115 117 return {
... ... @@ -118,26 +120,9 @@ export const searchFormSchema: FormSchema[] = [
118 120 valueField: 'id',
119 121 resultField: 'data',
120 122 placeholder: '请选择产品',
121   - api: async () => {
122   - const data = await deviceProfile({ params: '' });
123   - const returnData = data.map((m) => {
124   - return {
125   - name: m.name,
126   - id: m.id,
127   - };
128   - });
129   - return returnData;
130   - },
131   - searchApi: async (params: Recordable) => {
132   - const data = await deviceProfile({ textSearch: params.text });
133   - const returnData = data.map((m) => {
134   - return {
135   - name: m.name,
136   - id: m.id,
137   - };
138   - });
139   - return returnData;
140   - },
  123 + api: deviceProfile,
  124 + filterOption: (inputValue: string, option: Record<'label' | 'value', string>) =>
  125 + option.label.includes(inputValue),
141 126 };
142 127 },
143 128 },
... ...
... ... @@ -16,7 +16,7 @@
16 16 @open-gateway-device="handleOpenGatewayDevice"
17 17 />
18 18 </TabPane>
19   - <TabPane key="modelOfMatter" tab="物模型">
  19 + <TabPane key="modelOfMatter" tab="物模型数据">
20 20 <ModelOfMatter :deviceDetail="deviceDetail" />
21 21 </TabPane>
22 22 <!-- <TabPane key="2" tab="实时数据" v-if="deviceDetail?.deviceType !== 'GATEWAY'">
... ...
... ... @@ -12,7 +12,17 @@
12 12 <template #clientId="{ field, model }">
13 13 <div class="flex items-center">
14 14 <Input v-model:value="model[field]" placeholder="请输入客户端ID" />
15   - <ReloadOutlined class="ml-3 !text-blue-600" @click="handleCreateUUID" />
  15 + <Tooltip title="刷新客户端ID">
  16 + <ReloadOutlined class="ml-3 !text-blue-600" @click="handleCreateUUID" />
  17 + </Tooltip>
  18 + </div>
  19 + </template>
  20 + <template #credentialsId="{ field, model }">
  21 + <div class="flex items-center">
  22 + <Input v-model:value="model[field]" placeholder="请输入访问令牌" />
  23 + <Tooltip title="刷新访问令牌">
  24 + <ReloadOutlined class="ml-3 !text-blue-600" @click="handleCreateCredentialsId" />
  25 + </Tooltip>
16 26 </div>
17 27 </template>
18 28 </BasicForm>
... ... @@ -26,8 +36,8 @@
26 36 import { TokenSchemas, credentialTypeEnum } from '../../config/data';
27 37 import { saveDeviceToken } from '/@/api/device/deviceManager';
28 38 import { useMessage } from '/@/hooks/web/useMessage';
29   - import { Input } from 'ant-design-vue';
30   - import { buildUUID } from '/@/utils/uuid';
  39 + import { Input, Tooltip } from 'ant-design-vue';
  40 + import { buildUUID, randomString } from '/@/utils/uuid';
31 41 import { ReloadOutlined } from '@ant-design/icons-vue';
32 42 export default defineComponent({
33 43 components: {
... ... @@ -35,6 +45,7 @@
35 45 BasicForm,
36 46 Input,
37 47 ReloadOutlined,
  48 + Tooltip,
38 49 },
39 50 emits: ['register'],
40 51 setup() {
... ... @@ -224,6 +235,10 @@
224 235 const handleCreateUUID = () => {
225 236 setFieldsValue({ clientId: buildUUID() });
226 237 };
  238 +
  239 + const handleCreateCredentialsId = () => {
  240 + setFieldsValue({ credentialsId: randomString() });
  241 + };
227 242 return {
228 243 registerModal,
229 244 registerForm,
... ... @@ -231,6 +246,7 @@
231 246 handleCancel,
232 247 handleOk,
233 248 handleCreateUUID,
  249 + handleCreateCredentialsId,
234 250 };
235 251 },
236 252 });
... ...
... ... @@ -4,6 +4,30 @@
4 4 <template #addAgree="{ model, field }">
5 5 <Checkbox v-model:checked="model[field]" @change="checkedChange">添加协议</Checkbox>
6 6 </template>
  7 + <template #clientId="{ model, field }">
  8 + <div class="flex justify-center items-center">
  9 + <Input v-model:value="model[field]" placeholder="请输入客户端ID" />
  10 + <Tooltip title="刷新客户端ID">
  11 + <Icon
  12 + class="ml-3 cursor-pointer !text-blue-600"
  13 + icon="ant-design:reload-outlined"
  14 + @click="handleGenerateClientId"
  15 + />
  16 + </Tooltip>
  17 + </div>
  18 + </template>
  19 + <template #credentialsId="{ model, field }">
  20 + <div class="flex justify-center items-center">
  21 + <Input v-model:value="model[field]" placeholder="请输入访问令牌" />
  22 + <Tooltip title="刷新访问令牌">
  23 + <Icon
  24 + class="ml-3 cursor-pointer !text-blue-600"
  25 + icon="ant-design:reload-outlined"
  26 + @click="handleGenerateCredentialsId"
  27 + />
  28 + </Tooltip>
  29 + </div>
  30 + </template>
7 31 </BasicForm>
8 32 <div>
9 33 <a-button @click="prevStep">上一步</a-button>
... ... @@ -13,13 +37,18 @@
13 37 <script lang="ts">
14 38 import { defineComponent } from 'vue';
15 39
16   - import { Checkbox } from 'ant-design-vue';
  40 + import { Checkbox, Input, Tooltip } from 'ant-design-vue';
17 41 import { BasicForm, useForm } from '/@/components/Form';
18 42 import { step2Schemas } from '../../config/data';
  43 + import { Icon } from '/@/components/Icon';
  44 + import { buildUUID, randomString } from '/@/utils/uuid';
19 45 export default defineComponent({
20 46 components: {
21 47 BasicForm,
22 48 Checkbox,
  49 + Input,
  50 + Icon,
  51 + Tooltip,
23 52 },
24 53
25 54 emits: ['prev', 'next'],
... ... @@ -105,6 +134,14 @@
105 134 validate();
106 135 }
107 136
  137 + const handleGenerateClientId = () => {
  138 + setFieldsValue({ clientId: buildUUID() });
  139 + };
  140 +
  141 + const handleGenerateCredentialsId = () => {
  142 + setFieldsValue({ credentialsId: randomString() });
  143 + };
  144 +
108 145 return {
109 146 prevStep,
110 147 registerForm,
... ... @@ -113,6 +150,8 @@
113 150 validate,
114 151 resetFieldsValueAndStatus,
115 152 validateStep2Method,
  153 + handleGenerateClientId,
  154 + handleGenerateCredentialsId,
116 155 };
117 156 },
118 157 });
... ...
... ... @@ -55,15 +55,23 @@
55 55 <a-button type="primary" class="mr-4" @click="manageDeviceToken">管理设备凭证</a-button>
56 56 <ManageDeviceTokenModal @register="registerModal" />
57 57 </div>
58   - <div v-if="deviceDetail?.deviceInfo?.address" class="mt-4">
  58 + <div class="mt-4">
59 59 <p>设备位置</p>
60   - <div ref="mapWrapRef" style="height: 550px; width: 100%"></div>
  60 + <div v-if="deviceDetail?.deviceInfo?.address">
  61 + <div ref="mapWrapRef" style="height: 550px; width: 100%"></div>
  62 + </div>
  63 + <Empty
  64 + v-if="!deviceDetail?.deviceInfo?.address"
  65 + :image="locationImage"
  66 + :imageStyle="{ display: 'flex', 'justify-content': 'center', height: '150px' }"
  67 + description="请添加设备地理位置"
  68 + />
61 69 </div>
62 70 </div>
63 71 </template>
64 72 <script lang="ts">
65 73 import { defineComponent, ref, unref, nextTick } from 'vue';
66   - import { Image, Tooltip } from 'ant-design-vue';
  74 + import { Empty, Image, Tooltip } from 'ant-design-vue';
67 75 import { descSchema } from '../../config/detail.config';
68 76 import { useAsyncScript } from '/@/hooks/web/useAsyncScript';
69 77 import { useCopyToClipboard } from '/@/hooks/web/useCopyToClipboard';
... ... @@ -78,6 +86,8 @@
78 86
79 87 import wz from '/@/assets/images/wz.png';
80 88 import { useAsyncQueue } from '../../../localtion/useAsyncQueue';
  89 + import locationImage from '/@/assets/icons/location.svg';
  90 +
81 91 export default defineComponent({
82 92 components: {
83 93 Image,
... ... @@ -86,6 +96,7 @@
86 96 QuestionCircleOutlined,
87 97 BasicModal,
88 98 Tooltip,
  99 + Empty,
89 100 },
90 101 props: {
91 102 deviceDetail: {
... ... @@ -197,6 +208,7 @@
197 208 DeviceTypeEnum,
198 209 copyTopic,
199 210 remoteConnectiondGateway,
  211 + locationImage,
200 212 };
201 213 },
202 214 });
... ...
... ... @@ -286,7 +286,7 @@
286 286 setTableData(items);
287 287 const { setFieldsValue, resetFields } = getForm();
288 288 setFieldsValue({
289   - deviceProfileId: deviceProfileId.value,
  289 + deviceProfileId: deviceProfileId.value || null,
290 290 });
291 291 if (onCloseVal.value == 1) {
292 292 resetFields();
... ...
... ... @@ -2,9 +2,14 @@
2 2 import { PageWrapper } from '/@/components/Page';
3 3 import { BasicForm, useForm } from '/@/components/Form';
4 4 import { List, Button, Tooltip, Card, PaginationProps, Image } from 'ant-design-vue';
5   - import { ReloadOutlined, EyeOutlined, FormOutlined, MoreOutlined } from '@ant-design/icons-vue';
  5 + import { ReloadOutlined } from '@ant-design/icons-vue';
6 6 import { computed, onMounted, reactive, ref, unref } from 'vue';
7   - import { CardLayoutButton, EnumTableCardMode, ModeSwitchButton } from '/@/components/Widget';
  7 + import {
  8 + AuthIcon,
  9 + CardLayoutButton,
  10 + EnumTableCardMode,
  11 + ModeSwitchButton,
  12 + } from '/@/components/Widget';
8 13 import { Authority } from '/@/components/Authority';
9 14 import {
10 15 deviceConfigDelete,
... ... @@ -12,7 +17,6 @@
12 17 setDeviceProfileIsDefaultApi,
13 18 } from '/@/api/device/deviceConfigApi';
14 19 import { ProfileRecord } from '/@/api/device/model/deviceConfigModel';
15   - import { Dropdown } from '/@/components/Dropdown';
16 20 import {
17 21 defaultObj,
18 22 searchFormSchema,
... ... @@ -20,14 +24,13 @@
20 24 ProductPermission,
21 25 } from './device.profile.data';
22 26 import { useMessage } from '/@/hooks/web/useMessage';
23   - import { useSyncConfirm } from '/@/hooks/component/useSyncConfirm';
24 27 import DeviceProfileModal from './DeviceProfileModal.vue';
25 28 import DeviceProfileDrawer from './DeviceProfileDrawer.vue';
26 29 import { useModal } from '/@/components/Modal';
27 30 import { useDrawer } from '/@/components/Drawer';
28 31 import productDefault from '/@/assets/icons/product-default.svg';
29   - import { usePermission } from '/@/hooks/web/usePermission';
30 32 import { useRoute } from 'vue-router';
  33 + import AuthDropDown from '/@/components/Widget/AuthDropDown.vue';
31 34
32 35 defineProps<{
33 36 mode: EnumTableCardMode;
... ... @@ -42,7 +45,6 @@
42 45 const IMAGE_FALLBACK = productDefault;
43 46
44 47 const { createMessage } = useMessage();
45   - const { createSyncConfirm } = useSyncConfirm();
46 48
47 49 const [register, { getFieldsValue, setFieldsValue }] = useForm({
48 50 showAdvancedButton: true,
... ... @@ -72,7 +74,7 @@
72 74
73 75 const dataSource = ref<ProfileRecord[]>([]);
74 76
75   - const colNumber = ref(4);
  77 + const colNumber = ref(5);
76 78
77 79 const getSelectAllFlag = computed(() => {
78 80 return unref(dataSource).every((item) => item.checked);
... ... @@ -105,40 +107,6 @@
105 107 }
106 108 };
107 109
108   - const { hasPermission } = usePermission();
109   -
110   - const getHasDeleteFlag = computed(() => {
111   - return hasPermission(ProductPermission.DELETE);
112   - });
113   -
114   - const getHasDetailFlag = computed(() => {
115   - return hasPermission(ProductPermission.DETAIL);
116   - });
117   -
118   - const getHasUpdateFlag = computed(() => {
119   - return hasPermission(ProductPermission.UPDATE);
120   - });
121   -
122   - const getDropDownList = (record: ProfileRecord) => {
123   - const list = [
124   - {
125   - text: '默认',
126   - event: DropMenuEvent.SET_DEFAULT,
127   - icon: 'ant-design:unordered-list-outlined',
128   - onClick: handleSetDefault.bind(null, record),
129   - },
130   - ];
131   - if (unref(getHasDeleteFlag)) {
132   - list.push({
133   - text: '删除',
134   - event: DropMenuEvent.DELETE,
135   - icon: 'ant-design:delete-outlined',
136   - onClick: handleDelete.bind(null, [record.id]),
137   - });
138   - }
139   - return list;
140   - };
141   -
142 110 const handleModeChange = (mode: EnumTableCardMode) => {
143 111 emit('changeMode', mode);
144 112 };
... ... @@ -163,12 +131,10 @@
163 131 };
164 132
165 133 const handleShowDetail = (record: ProfileRecord) => {
166   - if (!unref(getHasDetailFlag)) return;
167 134 openDrawer(true, { record });
168 135 };
169 136
170 137 const handleUpdate = (record: ProfileRecord) => {
171   - if (!unref(getHasUpdateFlag)) return;
172 138 openModal(true, {
173 139 record,
174 140 isUpdate: true,
... ... @@ -177,7 +143,6 @@
177 143
178 144 const handleDelete = async (id: string[]) => {
179 145 try {
180   - await createSyncConfirm({ iconType: 'warning', content: '是否确认删除操作?' });
181 146 await deviceConfigDelete(id);
182 147 createMessage.success('删除成功');
183 148 await getDataSource();
... ... @@ -268,22 +233,43 @@
268 233 </template>
269 234 <template class="ant-card-actions" #actions>
270 235 <Tooltip title="详情">
271   - <EyeOutlined
272   - :class="getHasDetailFlag ? '' : '!cursor-not-allowed !text-gray-200'"
273   - key="setting"
  236 + <AuthIcon
  237 + :auth="ProductPermission.DETAIL"
  238 + class="!text-lg"
  239 + icon="ant-design:eye-outlined"
274 240 @click.stop="handleShowDetail(item)"
275 241 />
276 242 </Tooltip>
277 243 <Tooltip title="编辑">
278   - <FormOutlined
279   - :class="getHasUpdateFlag ? '' : '!cursor-not-allowed !text-gray-200'"
280   - key="edit"
  244 + <AuthIcon
  245 + :auth="ProductPermission.UPDATE"
  246 + class="!text-lg"
  247 + icon="ant-design:form-outlined"
281 248 @click.stop="handleUpdate(item)"
282 249 />
283 250 </Tooltip>
284   - <Dropdown :trigger="['hover']" :drop-menu-list="getDropDownList(item)">
285   - <MoreOutlined @click.stop class="transform rotate-90" />
286   - </Dropdown>
  251 + <AuthDropDown
  252 + @click.stop
  253 + :trigger="['hover']"
  254 + :drop-menu-list="[
  255 + {
  256 + text: '默认',
  257 + event: DropMenuEvent.SET_DEFAULT,
  258 + icon: 'ant-design:unordered-list-outlined',
  259 + onClick: handleSetDefault.bind(null, item),
  260 + },
  261 + {
  262 + text: '删除',
  263 + event: DropMenuEvent.DELETE,
  264 + auth: ProductPermission.DELETE,
  265 + icon: 'ant-design:delete-outlined',
  266 + popconfirm: {
  267 + title: '是否确认删除操作?',
  268 + onConfirm: handleDelete.bind(null, [item.id]),
  269 + },
  270 + },
  271 + ]"
  272 + />
287 273 </template>
288 274 <Card.Meta>
289 275 <template #title>
... ... @@ -308,7 +294,10 @@
308 294
309 295 <style lang="less" scoped>
310 296 .profile-list:deep(.ant-image-img) {
311   - width: 100% !important;
312   - height: 100% !important;
  297 + @apply !w-full !h-full;
  298 + }
  299 +
  300 + .profile-list:deep(.ant-card-body) {
  301 + @apply !p-4;
313 302 }
314 303 </style>
... ...
... ... @@ -28,10 +28,13 @@
28 28 field: 'scriptName',
29 29 label: '转换脚本',
30 30 render: (value: string) => {
31   - return h('div', [
32   - h(Tag, { color: 'blue' }, () => value),
33   - h(Button, { type: 'link', onClick: handleTestScript }, () => '测试脚本'),
34   - ]);
  31 + return (
  32 + value &&
  33 + h('div', [
  34 + h(Tag, { color: 'blue' }, () => value),
  35 + h(Button, { type: 'link', onClick: handleTestScript }, () => '测试脚本'),
  36 + ])
  37 + );
35 38 },
36 39 },
37 40 ],
... ...
... ... @@ -261,12 +261,14 @@ export const columns: BasicColumn[] = [
261 261 title: '默认配置',
262 262 dataIndex: 'default',
263 263 width: 80,
264   - format: (text) => (text ? '是' : '否'),
  264 + customRender: ({ text }) =>
  265 + text ? h(Tag, { color: 'blue' }, () => '是') : h(Tag, { color: 'red' }, () => '否'),
265 266 },
266 267 {
267 268 title: '描述',
268 269 dataIndex: 'description',
269 270 width: 90,
  271 + ellipsis: true,
270 272 },
271 273 {
272 274 title: '创建时间',
... ...
... ... @@ -8,7 +8,9 @@
8 8 placeholder="请选择转换脚本"
9 9 v-model:value="selectScript.script"
10 10 style="width: 305px"
  11 + show-search
11 12 :options="selectOptions"
  13 + :filter-option="handleSearch"
12 14 allowClear
13 15 />
14 16 </div>
... ... @@ -113,6 +115,11 @@
113 115 const setFormData = (v) => {
114 116 selectScript.script = v?.scriptId;
115 117 };
  118 +
  119 + const handleSearch = (inputValue: string, option: Record<'label' | 'value', string>) => {
  120 + return option.label.includes(inputValue);
  121 + };
  122 +
116 123 defineExpose({
117 124 getFormData,
118 125 resetFormData,
... ...
... ... @@ -16,6 +16,14 @@
16 16 <TableAction
17 17 :actions="[
18 18 {
  19 + label: '进入',
  20 + icon: 'ant-design:login-outlined',
  21 + tooltip: '以租户管理员身份登录',
  22 + onClick: handleLoginTenantAdmin.bind(null, record),
  23 + },
  24 + ]"
  25 + :drop-down-actions="[
  26 + {
19 27 label: '短信通知',
20 28 icon: 'ant-design:send-outlined',
21 29 tooltip: '发送通知短信',
... ... @@ -94,6 +102,12 @@
94 102 import { MessageTypeEnum, SendResetPasswordEmailMsg } from '/@/api/tenant/tenantInfo';
95 103 import { useMessage } from '/@/hooks/web/useMessage';
96 104 import { Authority } from '/@/components/Authority';
  105 + import { getMyInfo, getPermCode, getUserToken } from '/@/api/sys/user';
  106 + import { useUserStore } from '/@/store/modules/user';
  107 + import { usePermissionStore } from '/@/store/modules/permission';
  108 + import { RoleEnum } from '/@/enums/roleEnum';
  109 + import { useGo } from '/@/hooks/web/usePage';
  110 + import { PageEnum } from '/@/enums/pageEnum';
97 111
98 112 export default defineComponent({
99 113 name: 'TenantAdminDrawer',
... ... @@ -198,10 +212,15 @@
198 212 fixed: 'right',
199 213 },
200 214 });
  215 +
  216 + let firstEnterFlag = true;
201 217 //默认传递页面数据
202 218 const [tenantAdminDrawer, { closeDrawer }] = useDrawerInner(async (data) => {
203 219 tenantId.value = data.record.tenantId;
204   - reload();
  220 + !firstEnterFlag && reload();
  221 + if (firstEnterFlag) {
  222 + firstEnterFlag = false;
  223 + }
205 224 });
206 225
207 226 //提交按钮
... ... @@ -213,6 +232,24 @@
213 232 reload();
214 233 }
215 234
  235 + const userStore = useUserStore();
  236 + const permissionStore = usePermissionStore();
  237 + const go = useGo();
  238 + async function handleLoginTenantAdmin(record: { tbUser: string; id: string }) {
  239 + try {
  240 + const { token, refreshToken } = await getUserToken(record.id);
  241 + userStore.storeToken(token, refreshToken);
  242 + const userInfo = await getMyInfo();
  243 + const permissionList = await getPermCode();
  244 + permissionStore.setPermCodeList(permissionList);
  245 + userStore.setUserInfo(userInfo);
  246 + userStore.setRoleList(userInfo.roles as RoleEnum[]);
  247 + go(PageEnum.BASE_HOME);
  248 + } catch (error) {
  249 + } finally {
  250 + }
  251 + }
  252 +
216 253 return {
217 254 tenantAdminDrawer,
218 255 handleCreateTenantAdmin,
... ... @@ -225,6 +262,7 @@
225 262 handleDelete,
226 263 handleResetPassword,
227 264 handleSendMsg,
  265 + handleLoginTenantAdmin,
228 266 };
229 267 },
230 268 });
... ...
... ... @@ -72,7 +72,9 @@
72 72 >
73 73 <Tooltip :title="item.deviceName" placement="topLeft">
74 74 <div class="flex p-1">
75   - <div class="truncate font-bold">{{ item.deviceRename || item.deviceName }}</div>
  75 + <div v-if="item.componentInfo.showDeviceName" class="truncate font-bold">
  76 + {{ item.deviceRename || item.deviceName }}
  77 + </div>
76 78 </div>
77 79 </Tooltip>
78 80 </div>
... ...
... ... @@ -33,7 +33,7 @@
33 33 return decode((ROUTE.params as DataComponentRouteParams).boardId as string);
34 34 });
35 35
36   - const frontId = ref(FrontComponent.TEXT_COMPONENT_1);
  36 + const frontId = ref();
37 37
38 38 const isEdit = ref(false);
39 39
... ... @@ -48,6 +48,7 @@
48 48 const [register, { closeModal, changeOkLoading }] = useModalInner(
49 49 (data: { isEdit: boolean; record?: DataBoardLayoutInfo }) => {
50 50 componentRecord.value = data.record || ({} as unknown as DataBoardLayoutInfo);
  51 + if (!unref(isEdit)) frontId.value = FrontComponent.TEXT_COMPONENT_1;
51 52 frontId.value =
52 53 (data.record?.record?.frontId as FrontComponent) || FrontComponent.TEXT_COMPONENT_1;
53 54 isEdit.value = data.isEdit || false;
... ...
1 1 <script lang="ts" setup>
2   - import { ref, onMounted, unref } from 'vue';
  2 + import { ref, onMounted, unref, computed } from 'vue';
3 3 import { FrontComponent } from '../../../const/const';
4 4 import { DataSourceField, dataSourceSchema } from '../../config/basicConfiguration';
5 5 import { DeviceTypeEnum } from '/@/api/device/model/deviceModel';
... ... @@ -7,12 +7,18 @@
7 7 import BasicForm from '/@/components/Form/src/BasicForm.vue';
8 8 const formEl = ref<Nullable<FormActionType>>(null);
9 9
10   - defineProps<{
  10 + const props = defineProps<{
11 11 frontId?: FrontComponent;
12 12 }>();
13 13
14 14 defineExpose({ formActionType: formEl });
15 15
  16 + const getDataSchema = computed(() => {
  17 + const { frontId } = props;
  18 + if (!frontId) return [];
  19 + return dataSourceSchema(frontId);
  20 + });
  21 +
16 22 onMounted(() => {
17 23 unref(formEl)?.setFieldsValue({ [DataSourceField.DEVICE_TYPE]: DeviceTypeEnum.SENSOR });
18 24 });
... ... @@ -21,7 +27,7 @@
21 27 <template>
22 28 <BasicForm
23 29 ref="formEl"
24   - :schemas="dataSourceSchema($props.frontId)"
  30 + :schemas="getDataSchema"
25 31 class="w-full flex-1 data-source-form"
26 32 :show-action-button-group="false"
27 33 :row-props="{
... ...
... ... @@ -61,6 +61,7 @@
61 61 icon: value.icon || null,
62 62 iconColor: value.iconColor || null,
63 63 unit: value.unit || null,
  64 + showDeviceName: value.showDeviceName,
64 65 gradientInfo: [
65 66 { key: Gradient.FIRST, value: value.firstPhaseValue, color: value.firstPhaseColor },
66 67 { key: Gradient.SECOND, value: value.secondPhaseValue, color: value.secondPhaseColor },
... ...
... ... @@ -149,6 +149,7 @@ export const dataSourceSchema = (frontId?: FrontComponent): FormSchema[] => {
149 149 const { setFieldsValue } = formActionType;
150 150 const deviceProfileId = formModel[DataSourceField.DEVICE_PROFILE_ID];
151 151 const deviceType = formModel[DataSourceField.DEVICE_TYPE];
  152 + if (![deviceType, deviceProfileId].every(Boolean)) return {};
152 153 return {
153 154 api: async () => {
154 155 if (!deviceType) return [];
... ... @@ -253,6 +254,7 @@ export const dataSourceSchema = (frontId?: FrontComponent): FormSchema[] => {
253 254 componentProps({ formModel }) {
254 255 const deviceProfileId = formModel[DataSourceField.DEVICE_PROFILE_ID];
255 256 const transportType = formModel[DataSourceField.TRANSPORT_TYPE];
  257 + if (![deviceProfileId, transportType].every(Boolean)) return {};
256 258 return {
257 259 api: async () => {
258 260 try {
... ...
... ... @@ -12,6 +12,7 @@ export interface VisualOptionParams {
12 12 [visualOptionField.FIRST_PHASE_VALUE]: string;
13 13 [visualOptionField.SECOND_PHASE_VALUE]: string;
14 14 [visualOptionField.THIRD_PHASE_VALUE]: string;
  15 + [visualOptionField.SHOW_DEVICE_NAME]: string;
15 16 }
16 17
17 18 export enum visualOptionField {
... ... @@ -25,6 +26,7 @@ export enum visualOptionField {
25 26 FIRST_PHASE_VALUE = 'firstPhaseValue',
26 27 SECOND_PHASE_VALUE = 'secondPhaseValue',
27 28 THIRD_PHASE_VALUE = 'thirdPhaseValue',
  29 + SHOW_DEVICE_NAME = 'showDeviceName',
28 30 }
29 31
30 32 export const modeOne: FormSchema[] = [
... ... @@ -37,6 +39,11 @@ export const modeOne: FormSchema[] = [
37 39 defaultValue: '#000',
38 40 },
39 41 },
  42 + {
  43 + field: visualOptionField.SHOW_DEVICE_NAME,
  44 + label: '显示设备名称',
  45 + component: 'Checkbox',
  46 + },
40 47 ];
41 48
42 49 export const modeTwo: FormSchema[] = [
... ... @@ -78,6 +85,11 @@ export const modeTwo: FormSchema[] = [
78 85 };
79 86 },
80 87 },
  88 + {
  89 + field: visualOptionField.SHOW_DEVICE_NAME,
  90 + label: '显示设备名称',
  91 + component: 'Checkbox',
  92 + },
81 93 ];
82 94
83 95 export const modeThree: FormSchema[] = [
... ... @@ -156,6 +168,11 @@ export const modeThree: FormSchema[] = [
156 168 };
157 169 },
158 170 },
  171 + {
  172 + field: visualOptionField.SHOW_DEVICE_NAME,
  173 + label: '显示设备名称',
  174 + component: 'Checkbox',
  175 + },
159 176 ];
160 177
161 178 export const modeFour: FormSchema[] = [
... ... @@ -176,6 +193,11 @@ export const modeFour: FormSchema[] = [
176 193 placeholder: '请输入数值单位',
177 194 },
178 195 },
  196 + {
  197 + field: visualOptionField.SHOW_DEVICE_NAME,
  198 + label: '显示设备名称',
  199 + component: 'Checkbox',
  200 + },
179 201 ];
180 202
181 203 export const modeFive: FormSchema[] = [
... ... @@ -209,6 +231,11 @@ export const modeFive: FormSchema[] = [
209 231 };
210 232 },
211 233 },
  234 + {
  235 + field: visualOptionField.SHOW_DEVICE_NAME,
  236 + label: '显示设备名称',
  237 + component: 'Checkbox',
  238 + },
212 239 ];
213 240
214 241 export const schemasMap = new Map<FrontComponent, FormSchema[]>();
... ...