Commit 63b3580b7a943dee98aa46331fd772857a7469c4

Authored by xp.Huang
2 parents 9dbe312e 4b5ae137

Merge branch 'ww' into 'main'

feat: product profile add card mode && fix bug in teambition

See merge request huang/yun-teng-iot-front!445
@@ -2,7 +2,9 @@ import { defHttp } from '/@/utils/http/axios'; @@ -2,7 +2,9 @@ import { defHttp } from '/@/utils/http/axios';
2 import { 2 import {
3 TDeviceConfigParams, 3 TDeviceConfigParams,
4 IDeviceConfigAddOrEditModel, 4 IDeviceConfigAddOrEditModel,
  5 + ProfileRecord,
5 } from '/@/api/device/model/deviceConfigModel'; 6 } from '/@/api/device/model/deviceConfigModel';
  7 +import { PaginationResult } from '/#/axios';
6 8
7 enum EDeviceConfigApi { 9 enum EDeviceConfigApi {
8 /** 10 /**
@@ -52,7 +54,7 @@ export const alarmContactGetPage = () => { @@ -52,7 +54,7 @@ export const alarmContactGetPage = () => {
52 * 分页查询设备配置页面 54 * 分页查询设备配置页面
53 */ 55 */
54 export const deviceConfigGetQuery = (params?: TDeviceConfigParams) => { 56 export const deviceConfigGetQuery = (params?: TDeviceConfigParams) => {
55 - return defHttp.get({ 57 + return defHttp.get<PaginationResult<ProfileRecord>>({
56 url: EDeviceConfigApi.DEVICE_CONFIG_GET_PAGE, 58 url: EDeviceConfigApi.DEVICE_CONFIG_GET_PAGE,
57 params, 59 params,
58 }); 60 });
@@ -199,3 +199,45 @@ export interface AlarmLogItem { @@ -199,3 +199,45 @@ export interface AlarmLogItem {
199 organizationId: string; 199 organizationId: string;
200 organizationName: string; 200 organizationName: string;
201 } 201 }
  202 +
  203 +export interface Configuration {
  204 + type: string;
  205 +}
  206 +
  207 +export interface TransportConfiguration {
  208 + type: string;
  209 +}
  210 +
  211 +export interface ProvisionConfiguration {
  212 + type: string;
  213 + provisionDeviceSecret?: any;
  214 +}
  215 +
  216 +export interface ProfileData {
  217 + configuration: Configuration;
  218 + transportConfiguration: TransportConfiguration;
  219 + provisionConfiguration: ProvisionConfiguration;
  220 + alarms?: any;
  221 +}
  222 +
  223 +export interface ProfileRecord {
  224 + id: string;
  225 + creator: string;
  226 + createTime: string;
  227 + updater: string;
  228 + updateTime: string;
  229 + name: string;
  230 + tenantId: string;
  231 + transportType: string;
  232 + provisionType: string;
  233 + deviceType: string;
  234 + tbProfileId: string;
  235 + profileData: ProfileData;
  236 + defaultRuleChainId: string;
  237 + defaultQueueName: string;
  238 + image: string;
  239 + type: string;
  240 + default: boolean;
  241 +
  242 + checked?: boolean;
  243 +}
  1 +<svg t="1671442803003" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4002" width="200" height="200"><path d="M0 0m178.086957 0l667.826086 0q178.086957 0 178.086957 178.086957l0 667.826086q0 178.086957-178.086957 178.086957l-667.826086 0q-178.086957 0-178.086957-178.086957l0-667.826086q0-178.086957 178.086957-178.086957Z" fill="#2E7BFC" p-id="4003"></path><path d="M758.717217 394.373565v268.866783l-238.191304 134.455652v-268.911304l124.14887-70.054957v94.497391l51.956869-29.985391V429.412174l62.107826-35.038609z m-493.434434 0l238.191304 134.433392v268.866782l-238.191304-134.433391V394.373565zM512 226.304l246.717217 139.241739L512 504.787478l-246.717217-139.241739L512 226.326261z" fill="#FFFFFF" p-id="4004"></path></svg>
@@ -13,7 +13,7 @@ @@ -13,7 +13,7 @@
13 const iconList = IconData.icons; 13 const iconList = IconData.icons;
14 const [register, { openDrawer, closeDrawer }] = useDrawer(); 14 const [register, { openDrawer, closeDrawer }] = useDrawer();
15 const getIcon = computed(() => { 15 const getIcon = computed(() => {
16 - return props.value || 'temperature'; 16 + return props.value || 'shuiwen';
17 }); 17 });
18 18
19 const getColor = computed(() => { 19 const getColor = computed(() => {
  1 +<script lang="ts" setup>
  2 + import { Popover, Slider, Button } from 'ant-design-vue';
  3 + import { LayoutOutlined } from '@ant-design/icons-vue';
  4 + import { computed } from 'vue';
  5 +
  6 + const props = withDefaults(
  7 + defineProps<{
  8 + min?: number;
  9 + max?: number;
  10 + value?: number;
  11 + }>(),
  12 + {
  13 + min: 4,
  14 + max: 12,
  15 + value: 4,
  16 + }
  17 + );
  18 +
  19 + const emit = defineEmits(['change', 'update:value']);
  20 +
  21 + const generateLayoutMarks = (min: number, max: number) => {
  22 + const marks = {};
  23 + Array.from({ length: max - min + 1 }).forEach((_, index) => {
  24 + const key = index + min;
  25 + marks[key] = key;
  26 + });
  27 + return marks;
  28 + };
  29 +
  30 + const getMarks = computed(() => {
  31 + const { min, max } = props;
  32 + return generateLayoutMarks(min, max);
  33 + });
  34 +
  35 + const handleChange = (value: number) => {
  36 + emit('update:value', value);
  37 + emit('change', value);
  38 + };
  39 +</script>
  40 +
  41 +<template>
  42 + <Popover :trigger="['hover']">
  43 + <template #content>
  44 + <div class="w-60">
  45 + <div>每行显示数量</div>
  46 + <Slider
  47 + :value="props.value"
  48 + :max="props.max"
  49 + :min="props.min"
  50 + :marks="getMarks"
  51 + @change="handleChange"
  52 + />
  53 + </div>
  54 + </template>
  55 + <Button type="primary">
  56 + <LayoutOutlined />
  57 + </Button>
  58 + </Popover>
  59 +</template>
  1 +<script lang="ts" setup>
  2 + import { RadioButton, RadioGroup, Tooltip } from 'ant-design-vue';
  3 + import { TABLE_CARD_MODE_LIST } from './const';
  4 + import { ModeList } from './types';
  5 + import Icon from '../Icon';
  6 + const props = withDefaults(
  7 + defineProps<{
  8 + value: string;
  9 + mode?: ModeList[];
  10 + }>(),
  11 + {
  12 + mode: () => TABLE_CARD_MODE_LIST,
  13 + }
  14 + );
  15 +
  16 + const emit = defineEmits(['change']);
  17 +
  18 + const handleChange = (event: Event) => {
  19 + const value = (event.target as HTMLInputElement).value;
  20 + emit('change', value);
  21 + };
  22 +</script>
  23 +
  24 +<template>
  25 + <RadioGroup :value="props.value" button-style="solid" @change="handleChange">
  26 + <template v-for="item in $props.mode" :key="item.value">
  27 + <Tooltip :title="item.label">
  28 + <RadioButton class="cursor-pointer" :value="item.value">
  29 + <Icon :icon="item.icon" />
  30 + </RadioButton>
  31 + </Tooltip>
  32 + </template>
  33 + </RadioGroup>
  34 +</template>
  1 +import { ModeList } from './types';
  2 +
  3 +export enum EnumTableCardMode {
  4 + CARD = 'CARD',
  5 + TABLE = 'TABLE',
  6 +}
  7 +
  8 +export enum EnumTableChartMode {
  9 + TABLE = 'TABLE',
  10 + CHART = 'CHART',
  11 +}
  12 +
  13 +export const TABLE_CARD_MODE_LIST: ModeList[] = [
  14 + {
  15 + label: '卡片模式',
  16 + value: EnumTableCardMode.CARD,
  17 + icon: 'ant-design:appstore-outlined',
  18 + },
  19 + {
  20 + label: '列表模式',
  21 + value: EnumTableCardMode.TABLE,
  22 + icon: 'ant-design:unordered-list-outlined',
  23 + },
  24 +];
  25 +
  26 +export const TABLE_CHART_MODE_LIST: ModeList[] = [
  27 + {
  28 + label: '图表模式',
  29 + value: EnumTableChartMode.CHART,
  30 + icon: 'ant-design:line-chart-outlined',
  31 + },
  32 + {
  33 + label: '列表模式',
  34 + value: EnumTableChartMode.TABLE,
  35 + icon: 'ant-design:unordered-list-outlined',
  36 + },
  37 +];
  1 +export { default as ModeSwitchButton } from './ModeSwitchButton.vue';
  2 +export { default as CardLayoutButton } from './CardLayoutButton.vue';
  3 +export {
  4 + EnumTableCardMode,
  5 + EnumTableChartMode,
  6 + TABLE_CARD_MODE_LIST,
  7 + TABLE_CHART_MODE_LIST,
  8 +} from './const';
  9 +export type { ModeList } from './types';
  1 +import { ExtractPropTypes } from 'vue';
  2 +import Icon from '../Icon';
  3 +
  4 +export interface ModeList {
  5 + value: string;
  6 + label: string;
  7 + icon?: string;
  8 + iconProps?: ExtractPropTypes<typeof Icon>;
  9 +}
@@ -11,10 +11,10 @@ @@ -11,10 +11,10 @@
11 > 11 >
12 <template #toolbar> 12 <template #toolbar>
13 <a-button type="primary" @click="handleSwitchMode">分屏模式</a-button> 13 <a-button type="primary" @click="handleSwitchMode">分屏模式</a-button>
14 - <Authority value="api:yt:video:post"> 14 + <Authority :value="CameraPermission.CREATE">
15 <a-button type="primary" @click="handleCreateOrEdit(null)"> 新增视频 </a-button> 15 <a-button type="primary" @click="handleCreateOrEdit(null)"> 新增视频 </a-button>
16 </Authority> 16 </Authority>
17 - <Authority value="api:yt:video:delete"> 17 + <Authority :value="CameraPermission.DELETE">
18 <Popconfirm 18 <Popconfirm
19 title="您确定要批量删除数据" 19 title="您确定要批量删除数据"
20 ok-text="确定" 20 ok-text="确定"
@@ -50,12 +50,12 @@ @@ -50,12 +50,12 @@
50 { 50 {
51 label: '预览', 51 label: '预览',
52 icon: 'clarity:note-edit-line', 52 icon: 'clarity:note-edit-line',
53 - auth: 'api:yt:video:get', 53 + auth: CameraPermission.PREVIEW,
54 onClick: handleViewVideo.bind(null, record), 54 onClick: handleViewVideo.bind(null, record),
55 }, 55 },
56 { 56 {
57 label: '编辑', 57 label: '编辑',
58 - auth: 'api:yt:video:update', 58 + auth: CameraPermission.UPDATE,
59 icon: 'clarity:note-edit-line', 59 icon: 'clarity:note-edit-line',
60 onClick: handleCreateOrEdit.bind(null, record), 60 onClick: handleCreateOrEdit.bind(null, record),
61 }, 61 },
@@ -63,7 +63,7 @@ @@ -63,7 +63,7 @@
63 :drop-down-actions="[ 63 :drop-down-actions="[
64 { 64 {
65 label: '删除', 65 label: '删除',
66 - auth: 'api:yt:video:delete', 66 + auth: CameraPermission.DELETE,
67 icon: 'ant-design:delete-outlined', 67 icon: 'ant-design:delete-outlined',
68 color: 'error', 68 color: 'error',
69 popConfirm: { 69 popConfirm: {
@@ -89,7 +89,7 @@ @@ -89,7 +89,7 @@
89 import CameraDrawer from './CameraDrawer.vue'; 89 import CameraDrawer from './CameraDrawer.vue';
90 import { useResetOrganizationTree, OrganizationIdTree } from '/@/views/common/organizationIdTree'; 90 import { useResetOrganizationTree, OrganizationIdTree } from '/@/views/common/organizationIdTree';
91 import { cameraPage, deleteCameraManage } from '/@/api/camera/cameraManager'; 91 import { cameraPage, deleteCameraManage } from '/@/api/camera/cameraManager';
92 - import { searchFormSchema, columns, AccessMode, PageMode } from './config.data'; 92 + import { searchFormSchema, columns, AccessMode, PageMode, CameraPermission } from './config.data';
93 import VideoPreviewModal from './DialogPreviewVideo.vue'; 93 import VideoPreviewModal from './DialogPreviewVideo.vue';
94 import { useModal } from '/@/components/Modal'; 94 import { useModal } from '/@/components/Modal';
95 import { Authority } from '/@/components/Authority'; 95 import { Authority } from '/@/components/Authority';
@@ -199,6 +199,7 @@ @@ -199,6 +199,7 @@
199 registerModal, 199 registerModal,
200 AccessMode, 200 AccessMode,
201 handleSwitchMode, 201 handleSwitchMode,
  202 + CameraPermission,
202 }; 203 };
203 }, 204 },
204 }); 205 });
@@ -8,13 +8,14 @@ @@ -8,13 +8,14 @@
8 import { useFullscreen } from '@vueuse/core'; 8 import { useFullscreen } from '@vueuse/core';
9 import CameraDrawer from './CameraDrawer.vue'; 9 import CameraDrawer from './CameraDrawer.vue';
10 import { useDrawer } from '/@/components/Drawer'; 10 import { useDrawer } from '/@/components/Drawer';
11 - import { AccessMode, PageMode } from './config.data'; 11 + import { AccessMode, CameraPermission, PageMode } from './config.data';
12 import SvgIcon from '/@/components/Icon/src/SvgIcon.vue'; 12 import SvgIcon from '/@/components/Icon/src/SvgIcon.vue';
13 import { getStreamingPlayUrl } from '/@/api/camera/cameraManager'; 13 import { getStreamingPlayUrl } from '/@/api/camera/cameraManager';
14 import { buildUUID } from '/@/utils/uuid'; 14 import { buildUUID } from '/@/utils/uuid';
15 import { BasicVideoPlay, getVideoTypeByUrl } from '/@/components/Video'; 15 import { BasicVideoPlay, getVideoTypeByUrl } from '/@/components/Video';
16 import { VideoJsPlayerOptions } from 'video.js'; 16 import { VideoJsPlayerOptions } from 'video.js';
17 import { getBoundingClientRect } from '/@/utils/domUtils'; 17 import { getBoundingClientRect } from '/@/utils/domUtils';
  18 + import { Authority } from '/@/components/Authority';
18 19
19 type CameraRecordItem = CameraRecord & { 20 type CameraRecordItem = CameraRecord & {
20 canPlay?: boolean; 21 canPlay?: boolean;
@@ -220,7 +221,9 @@ @@ -220,7 +221,9 @@
220 </div> 221 </div>
221 <div class="flex items-center gap-4"> 222 <div class="flex items-center gap-4">
222 <div class="flex"> 223 <div class="flex">
223 - <Button type="primary" @click="handleAddCamera">新增视频</Button> 224 + <Authority :value="CameraPermission.CREATE">
  225 + <Button type="primary" @click="handleAddCamera">新增视频</Button>
  226 + </Authority>
224 </div> 227 </div>
225 <Space> 228 <Space>
226 <Button type="primary" @click="handleChangeMode(PageMode.SPLIT_SCREEN_MODE)"> 229 <Button type="primary" @click="handleChangeMode(PageMode.SPLIT_SCREEN_MODE)">
@@ -6,6 +6,14 @@ import type { FormSchema as QFormSchema } from '/@/components/Form/index'; @@ -6,6 +6,14 @@ import type { FormSchema as QFormSchema } from '/@/components/Form/index';
6 import { CameraVideoUrl, CameraMaxLength, MediaTypeValidate } from '/@/utils/rules'; 6 import { CameraVideoUrl, CameraMaxLength, MediaTypeValidate } from '/@/utils/rules';
7 import { h } from 'vue'; 7 import { h } from 'vue';
8 import SnHelpMessage from './SnHelpMessage.vue'; 8 import SnHelpMessage from './SnHelpMessage.vue';
  9 +
  10 +export enum CameraPermission {
  11 + PREVIEW = 'api:yt:video:get',
  12 + CREATE = 'api:yt:video:post',
  13 + UPDATE = 'api:yt:video:update',
  14 + DELETE = 'api:yt:video:delete',
  15 +}
  16 +
9 export enum AccessMode { 17 export enum AccessMode {
10 ManuallyEnter = 0, 18 ManuallyEnter = 0,
11 Streaming = 1, 19 Streaming = 1,
@@ -9,6 +9,14 @@ export enum Platform { @@ -9,6 +9,14 @@ export enum Platform {
9 PC = 'pc', 9 PC = 'pc',
10 } 10 }
11 11
  12 +export enum ConfigurationPermission {
  13 + CREATE = 'api:yt:configuration:center:post',
  14 + UPDATE = 'api:yt:configuration:center:update',
  15 + DELETE = 'api:yt:configuration:center:delete',
  16 + DESIGN = 'api:yt:configuration:center:get_configuration_info:design',
  17 + PREVIEW = 'api:yt:configuration:center:get_configuration_info:preview',
  18 +}
  19 +
12 export const PC_DEFAULT_CONTENT = 20 export const PC_DEFAULT_CONTENT =
13 '<mxfile><diagram>dZHBDsIgDIafhvuEzOh5Tr142sEzGXWQsHVhmKFP7xbAidMT5fv/UtoSVrTuZHgvLyhAE5oJR9iBUMrybT4dM3l4stnTzJPGKBHYAir1hACj7a4EDInRImqr+hTW2HVQ24RxY3BMbTfUadWeN7ACVc31ml6VsPK7jVk4g2pkLJ3tgtLy6A5gkFzg+IFYSVhhEK2PWleAnscXB+Pzjn/U988MdPZHwhQsb0+XZEesfAE=</diagram></mxfile>'; 21 '<mxfile><diagram>dZHBDsIgDIafhvuEzOh5Tr142sEzGXWQsHVhmKFP7xbAidMT5fv/UtoSVrTuZHgvLyhAE5oJR9iBUMrybT4dM3l4stnTzJPGKBHYAir1hACj7a4EDInRImqr+hTW2HVQ24RxY3BMbTfUadWeN7ACVc31ml6VsPK7jVk4g2pkLJ3tgtLy6A5gkFzg+IFYSVhhEK2PWleAnscXB+Pzjn/U988MdPZHwhQsb0+XZEesfAE=</diagram></mxfile>';
14 22
@@ -7,7 +7,7 @@ @@ -7,7 +7,7 @@
7 EditOutlined, 7 EditOutlined,
8 EllipsisOutlined, 8 EllipsisOutlined,
9 } from '@ant-design/icons-vue'; 9 } from '@ant-design/icons-vue';
10 - import { onMounted, reactive, ref, unref } from 'vue'; 10 + import { computed, onMounted, reactive, ref, unref } from 'vue';
11 import { OrganizationIdTree, useResetOrganizationTree } from '../../common/organizationIdTree'; 11 import { OrganizationIdTree, useResetOrganizationTree } from '../../common/organizationIdTree';
12 import { 12 import {
13 deleteConfigurationCenter, 13 deleteConfigurationCenter,
@@ -17,7 +17,7 @@ @@ -17,7 +17,7 @@
17 import { PageWrapper } from '/@/components/Page'; 17 import { PageWrapper } from '/@/components/Page';
18 import { Dropdown } from '/@/components/Dropdown'; 18 import { Dropdown } from '/@/components/Dropdown';
19 import { BasicForm, useForm } from '/@/components/Form'; 19 import { BasicForm, useForm } from '/@/components/Form';
20 - import { searchFormSchema } from './center.data'; 20 + import { ConfigurationPermission, searchFormSchema } from './center.data';
21 import { useMessage } from '/@/hooks/web/useMessage'; 21 import { useMessage } from '/@/hooks/web/useMessage';
22 import { Authority } from '/@/components/Authority'; 22 import { Authority } from '/@/components/Authority';
23 import { isDevMode } from '/@/utils/env'; 23 import { isDevMode } from '/@/utils/env';
@@ -98,6 +98,16 @@ @@ -98,6 +98,16 @@
98 98
99 const [registerDrawer, { openDrawer }] = useDrawer(); 99 const [registerDrawer, { openDrawer }] = useDrawer();
100 100
  101 + const { hasPermission } = usePermission();
  102 +
  103 + const getPreviewFlag = computed(() => {
  104 + return hasPermission(ConfigurationPermission.PREVIEW);
  105 + });
  106 +
  107 + const getDesignFlag = computed(() => {
  108 + return hasPermission(ConfigurationPermission.DESIGN);
  109 + });
  110 +
101 const handleCreateOrUpdate = (record?: ConfigurationCenterItemsModal) => { 111 const handleCreateOrUpdate = (record?: ConfigurationCenterItemsModal) => {
102 if (record) { 112 if (record) {
103 openDrawer(true, { 113 openDrawer(true, {
@@ -114,15 +124,15 @@ @@ -114,15 +124,15 @@
114 const { configurationPrefix } = useGlobSetting(); 124 const { configurationPrefix } = useGlobSetting();
115 const isDev = isDevMode(); 125 const isDev = isDevMode();
116 126
117 - const { hasPermission } = usePermission();  
118 const handlePreview = (record: ConfigurationCenterItemsModal) => { 127 const handlePreview = (record: ConfigurationCenterItemsModal) => {
119 - if (!hasPermission('api:yt:configuration:center:get_configuration_info:get')) return; 128 + if (!unref(getPreviewFlag)) return;
120 window.open( 129 window.open(
121 `${configurationPrefix}/${isDev ? '?dev=1&' : '?'}configurationId=${record!.id}&lightbox=1` 130 `${configurationPrefix}/${isDev ? '?dev=1&' : '?'}configurationId=${record!.id}&lightbox=1`
122 ); 131 );
123 }; 132 };
124 133
125 const handleDesign = (record: ConfigurationCenterItemsModal) => { 134 const handleDesign = (record: ConfigurationCenterItemsModal) => {
  135 + if (!unref(getDesignFlag)) return;
126 window.open(`${configurationPrefix}/${isDev ? '?dev=1&' : '?'}configurationId=${record!.id}`); 136 window.open(`${configurationPrefix}/${isDev ? '?dev=1&' : '?'}configurationId=${record!.id}`);
127 }; 137 };
128 138
@@ -175,7 +185,9 @@ @@ -175,7 +185,9 @@
175 > 185 >
176 <template #header> 186 <template #header>
177 <div class="flex gap-3 justify-end"> 187 <div class="flex gap-3 justify-end">
178 - <Button type="primary" @click="handleCreateOrUpdate()">新增组态</Button> 188 + <Authority :value="ConfigurationPermission.CREATE">
  189 + <Button type="primary" @click="handleCreateOrUpdate()">新增组态</Button>
  190 + </Authority>
179 <Popover :trigger="['hover']"> 191 <Popover :trigger="['hover']">
180 <template #content> 192 <template #content>
181 <div class="w-50"> 193 <div class="w-50">
@@ -204,7 +216,7 @@ @@ -204,7 +216,7 @@
204 <List.Item> 216 <List.Item>
205 <Card hoverable> 217 <Card hoverable>
206 <template #cover> 218 <template #cover>
207 - <div class="h-full w-full !flex justify-center items-center text-center"> 219 + <div class="h-full w-full !flex justify-center items-center text-center p-1">
208 <img 220 <img
209 class="w-full h-36" 221 class="w-full h-36"
210 alt="example" 222 alt="example"
@@ -214,29 +226,35 @@ @@ -214,29 +226,35 @@
214 </div> 226 </div>
215 </template> 227 </template>
216 <template class="ant-card-actions" #actions> 228 <template class="ant-card-actions" #actions>
217 - <Authority value="api:yt:configuration:center:get_configuration_info:get">  
218 - <Tooltip title="预览">  
219 - <EyeOutlined key="setting" @click="handlePreview(item)" />  
220 - </Tooltip>  
221 - </Authority>  
222 - <Authority value="api:yt:configuration:center:get_configuration_info:get">  
223 - <Tooltip title="设计">  
224 - <EditOutlined key="edit" @click="handleDesign(item)" />  
225 - </Tooltip>  
226 - </Authority> 229 + <Tooltip title="预览">
  230 + <EyeOutlined
  231 + :class="getPreviewFlag ? '' : '!cursor-not-allowed !text-gray-200'"
  232 + key="setting"
  233 + @click="handlePreview(item)"
  234 + />
  235 + </Tooltip>
  236 + <Tooltip title="设计">
  237 + <EditOutlined
  238 + :class="getDesignFlag ? '' : '!cursor-not-allowed !text-gray-200'"
  239 + key="edit"
  240 + @click="handleDesign(item)"
  241 + />
  242 + </Tooltip>
227 <Dropdown 243 <Dropdown
228 :dropMenuList="[ 244 :dropMenuList="[
229 { 245 {
230 text: '编辑', 246 text: '编辑',
231 - auth: 'api:yt:configuration:center:update', 247 + auth: ConfigurationPermission.UPDATE,
232 icon: 'clarity:note-edit-line', 248 icon: 'clarity:note-edit-line',
  249 + event: '',
233 onClick: handleCreateOrUpdate.bind(null, item), 250 onClick: handleCreateOrUpdate.bind(null, item),
234 }, 251 },
235 { 252 {
236 text: '删除', 253 text: '删除',
237 - auth: 'api:yt:configuration:center:delete', 254 + auth: ConfigurationPermission.DELETE,
238 icon: 'ant-design:delete-outlined', 255 icon: 'ant-design:delete-outlined',
239 color: 'error', 256 color: 'error',
  257 + event: '',
240 onClick: handleDelete.bind(null, item), 258 onClick: handleDelete.bind(null, item),
241 }, 259 },
242 ]" 260 ]"
1 <script lang="ts" setup> 1 <script lang="ts" setup>
2 import { computed, nextTick, onMounted, onUnmounted, Ref, ref, unref } from 'vue'; 2 import { computed, nextTick, onMounted, onUnmounted, Ref, ref, unref } from 'vue';
3 import { getDeviceHistoryInfo } from '/@/api/alarm/position'; 3 import { getDeviceHistoryInfo } from '/@/api/alarm/position';
4 - import { Empty, Spin, Tooltip, Button } from 'ant-design-vue'; 4 + import { Empty, Spin } from 'ant-design-vue';
5 import { useECharts } from '/@/hooks/web/useECharts'; 5 import { useECharts } from '/@/hooks/web/useECharts';
6 import { AggregateDataEnum, selectDeviceAttrSchema } from '/@/views/device/localtion/config.data'; 6 import { AggregateDataEnum, selectDeviceAttrSchema } from '/@/views/device/localtion/config.data';
7 import { useTimePeriodForm } from '/@/views/device/localtion/cpns/TimePeriodForm'; 7 import { useTimePeriodForm } from '/@/views/device/localtion/cpns/TimePeriodForm';
@@ -13,8 +13,12 @@ @@ -13,8 +13,12 @@
13 import { useHistoryData } from '../../hook/useHistoryData'; 13 import { useHistoryData } from '../../hook/useHistoryData';
14 import { formatToDateTime } from '/@/utils/dateUtil'; 14 import { formatToDateTime } from '/@/utils/dateUtil';
15 import { useTable, BasicTable } from '/@/components/Table'; 15 import { useTable, BasicTable } from '/@/components/Table';
16 - import { LineChartOutlined, BarsOutlined } from '@ant-design/icons-vue';  
17 import { DataTypeEnum } from '/@/components/Form/src/externalCompns/components/StructForm/config'; 16 import { DataTypeEnum } from '/@/components/Form/src/externalCompns/components/StructForm/config';
  17 + import {
  18 + ModeSwitchButton,
  19 + TABLE_CHART_MODE_LIST,
  20 + EnumTableChartMode,
  21 + } from '/@/components/Widget';
18 22
19 interface DeviceDetail { 23 interface DeviceDetail {
20 tbDeviceId: string; 24 tbDeviceId: string;
@@ -26,12 +30,7 @@ @@ -26,12 +30,7 @@
26 attr?: string; 30 attr?: string;
27 }>(); 31 }>();
28 32
29 - enum Mode {  
30 - TABLE = 'table',  
31 - CHART = 'chart',  
32 - }  
33 -  
34 - const mode = ref<Mode>(Mode.CHART); 33 + const mode = ref<EnumTableChartMode>(EnumTableChartMode.CHART);
35 34
36 const chartRef = ref(); 35 const chartRef = ref();
37 36
@@ -151,9 +150,9 @@ @@ -151,9 +150,9 @@
151 attrInfo?.detail.dataType.type as unknown as DataTypeEnum 150 attrInfo?.detail.dataType.type as unknown as DataTypeEnum
152 ) 151 )
153 ) { 152 ) {
154 - mode.value = Mode.TABLE; 153 + mode.value = EnumTableChartMode.TABLE;
155 } else { 154 } else {
156 - mode.value = Mode.CHART; 155 + mode.value = EnumTableChartMode.CHART;
157 } 156 }
158 } 157 }
159 } catch (error) {} 158 } catch (error) {}
@@ -200,7 +199,7 @@ @@ -200,7 +199,7 @@
200 setOptions(setChartOptions(res, selectedKeys)); 199 setOptions(setChartOptions(res, selectedKeys));
201 }; 200 };
202 201
203 - const switchMode = (flag: Mode) => { 202 + const switchMode = (flag: EnumTableChartMode) => {
204 mode.value = flag; 203 mode.value = flag;
205 }; 204 };
206 205
@@ -221,55 +220,32 @@ @@ -221,55 +220,32 @@
221 </section> 220 </section>
222 <section class="bg-white p-3"> 221 <section class="bg-white p-3">
223 <Spin :spinning="loading" :absolute="true"> 222 <Spin :spinning="loading" :absolute="true">
224 - <div v-show="mode === Mode.CHART" class="flex h-70px items-center justify-end p-2">  
225 - <Tooltip title="图表模式">  
226 - <Button  
227 - :class="[mode === Mode.CHART && '!bg-blue-500 svg:text-light-50']"  
228 - class="!p-2 !children:flex flex justify-center items-center border-r-0"  
229 - @click="switchMode(Mode.CHART)"  
230 - >  
231 - <LineChartOutlined />  
232 - </Button>  
233 - </Tooltip>  
234 -  
235 - <Tooltip title="列表模式">  
236 - <Button  
237 - class="!p-2 !children:flex flex justify-center items-center"  
238 - @click="switchMode(Mode.TABLE)"  
239 - >  
240 - <BarsOutlined />  
241 - </Button>  
242 - </Tooltip> 223 + <div
  224 + v-show="mode === EnumTableChartMode.CHART"
  225 + class="flex h-70px items-center justify-end p-2"
  226 + >
  227 + <ModeSwitchButton
  228 + v-model:value="mode"
  229 + :mode="TABLE_CHART_MODE_LIST"
  230 + @change="switchMode"
  231 + />
243 </div> 232 </div>
244 <div 233 <div
245 - v-show="isNull && mode === Mode.CHART" 234 + v-show="isNull && mode === EnumTableChartMode.CHART"
246 ref="chartRef" 235 ref="chartRef"
247 :style="{ height: '400px', width: '100%' }" 236 :style="{ height: '400px', width: '100%' }"
248 > 237 >
249 </div> 238 </div>
250 - <Empty v-show="!isNull && mode === Mode.CHART" /> 239 + <Empty v-show="!isNull && mode === EnumTableChartMode.CHART" />
251 240
252 - <BasicTable v-show="mode === Mode.TABLE" @register="registerTable"> 241 + <BasicTable v-show="mode === EnumTableChartMode.TABLE" @register="registerTable">
253 <template #toolbar> 242 <template #toolbar>
254 - <div v-show="mode === Mode.TABLE" class="flex h-70px items-center justify-end p-2">  
255 - <Tooltip title="图表模式">  
256 - <Button  
257 - class="!p-2 !children:flex flex justify-center items-center border-r-0"  
258 - @click="switchMode(Mode.CHART)"  
259 - >  
260 - <LineChartOutlined />  
261 - </Button>  
262 - </Tooltip>  
263 -  
264 - <Tooltip title="列表模式">  
265 - <Button  
266 - :class="[mode === Mode.TABLE && '!bg-blue-500 svg:text-light-50']"  
267 - class="!p-2 !children:flex flex justify-center items-center"  
268 - @click="switchMode(Mode.TABLE)"  
269 - >  
270 - <BarsOutlined />  
271 - </Button>  
272 - </Tooltip> 243 + <div class="flex h-70px items-center justify-end p-2">
  244 + <ModeSwitchButton
  245 + v-model:value="mode"
  246 + :mode="TABLE_CHART_MODE_LIST"
  247 + @change="switchMode"
  248 + />
273 </div> 249 </div>
274 </template> 250 </template>
275 </BasicTable> 251 </BasicTable>
1 <script lang="ts" setup> 1 <script lang="ts" setup>
2 import { nextTick, onUnmounted, reactive, ref, unref } from 'vue'; 2 import { nextTick, onUnmounted, reactive, ref, unref } from 'vue';
3 - import { List, Button, Tooltip, Card } from 'ant-design-vue'; 3 + import { List, Button, Card } from 'ant-design-vue';
4 import { PageWrapper } from '/@/components/Page'; 4 import { PageWrapper } from '/@/components/Page';
5 - import { AppstoreOutlined, BarsOutlined } from '@ant-design/icons-vue';  
6 import { BasicTable, useTable } from '/@/components/Table'; 5 import { BasicTable, useTable } from '/@/components/Table';
7 import { realTimeDataColumns } from '../../config/detail.config'; 6 import { realTimeDataColumns } from '../../config/detail.config';
8 import { useWebSocket } from '@vueuse/core'; 7 import { useWebSocket } from '@vueuse/core';
@@ -20,6 +19,7 @@ @@ -20,6 +19,7 @@
20 import { isArray, isObject } from '/@/utils/is'; 19 import { isArray, isObject } from '/@/utils/is';
21 import { DataTypeEnum } from '/@/components/Form/src/externalCompns/components/StructForm/config'; 20 import { DataTypeEnum } from '/@/components/Form/src/externalCompns/components/StructForm/config';
22 import { useGlobSetting } from '/@/hooks/setting'; 21 import { useGlobSetting } from '/@/hooks/setting';
  22 + import { ModeSwitchButton, EnumTableCardMode } from '/@/components/Widget';
23 23
24 interface ReceiveMessage { 24 interface ReceiveMessage {
25 data: { 25 data: {
@@ -112,14 +112,9 @@ @@ -112,14 +112,9 @@
112 112
113 const [registerModal, { openModal }] = useModal(); 113 const [registerModal, { openModal }] = useModal();
114 114
115 - enum Mode {  
116 - TABLE = 'table',  
117 - CARD = 'card',  
118 - }  
119 -  
120 - const mode = ref<Mode>(Mode.CARD); 115 + const mode = ref<EnumTableCardMode>(EnumTableCardMode.CARD);
121 116
122 - const switchMode = async (value: Mode) => { 117 + const switchMode = async (value: EnumTableCardMode) => {
123 mode.value = value; 118 mode.value = value;
124 await nextTick(); 119 await nextTick();
125 setTableData(socketInfo.dataSource); 120 setTableData(socketInfo.dataSource);
@@ -210,28 +205,14 @@ @@ -210,28 +205,14 @@
210 </div> 205 </div>
211 </section> 206 </section>
212 <section class="bg-light-50"> 207 <section class="bg-light-50">
213 - <div v-show="mode === Mode.CARD" class="flex h-70px items-center justify-end p-2">  
214 - <Tooltip title="卡片模式">  
215 - <Button  
216 - :class="[mode === Mode.CARD && '!bg-blue-500 svg:text-light-50']"  
217 - class="!p-2 !children:flex flex justify-center items-center border-r-0"  
218 - @click="switchMode(Mode.CARD)"  
219 - >  
220 - <AppstoreOutlined />  
221 - </Button>  
222 - </Tooltip>  
223 -  
224 - <Tooltip title="列表模式">  
225 - <Button  
226 - class="!p-2 !children:flex flex justify-center items-center"  
227 - @click="switchMode(Mode.TABLE)"  
228 - >  
229 - <BarsOutlined />  
230 - </Button>  
231 - </Tooltip> 208 + <div
  209 + v-show="mode === EnumTableCardMode.CARD"
  210 + class="flex h-70px items-center justify-end p-2"
  211 + >
  212 + <ModeSwitchButton v-model:value="mode" @change="switchMode" />
232 </div> 213 </div>
233 <List 214 <List
234 - v-if="mode === Mode.CARD" 215 + v-if="mode === EnumTableCardMode.CARD"
235 class="list-mode !px-2" 216 class="list-mode !px-2"
236 :data-source="socketInfo.dataSource" 217 :data-source="socketInfo.dataSource"
237 :grid="grid" 218 :grid="grid"
@@ -260,27 +241,13 @@ @@ -260,27 +241,13 @@
260 </List> 241 </List>
261 </section> 242 </section>
262 243
263 - <BasicTable v-if="mode === Mode.TABLE" @register="registerTable"> 244 + <BasicTable v-if="mode === EnumTableCardMode.TABLE" @register="registerTable">
264 <template #toolbar> 245 <template #toolbar>
265 - <div v-show="mode === Mode.TABLE" class="flex h-70px items-center justify-end p-2">  
266 - <Tooltip title="卡片模式">  
267 - <Button  
268 - class="!p-2 !children:flex flex justify-center items-center border-r-0"  
269 - @click="switchMode(Mode.CARD)"  
270 - >  
271 - <AppstoreOutlined />  
272 - </Button>  
273 - </Tooltip>  
274 -  
275 - <Tooltip title="列表模式">  
276 - <Button  
277 - :class="[mode === Mode.TABLE && '!bg-blue-500 svg:text-light-50']"  
278 - class="!p-2 !children:flex flex justify-center items-center"  
279 - @click="switchMode(Mode.TABLE)"  
280 - >  
281 - <BarsOutlined />  
282 - </Button>  
283 - </Tooltip> 246 + <div
  247 + v-show="mode === EnumTableCardMode.TABLE"
  248 + class="flex h-70px items-center justify-end p-2"
  249 + >
  250 + <ModeSwitchButton v-model:value="mode" @change="switchMode" />
284 </div> 251 </div>
285 </template> 252 </template>
286 </BasicTable> 253 </BasicTable>
@@ -50,28 +50,18 @@ @@ -50,28 +50,18 @@
50 <TimePeriodForm @register="timePeriodRegister" /> 50 <TimePeriodForm @register="timePeriodRegister" />
51 <section> 51 <section>
52 <Spin :spinning="loading"> 52 <Spin :spinning="loading">
53 - <div v-show="mode === Mode.CHART" class="flex h-70px items-center justify-end p-2">  
54 - <Tooltip title="图表模式">  
55 - <Button  
56 - :class="[mode === Mode.CHART && '!bg-blue-500 svg:text-light-50']"  
57 - class="!p-2 !children:flex flex justify-center items-center border-r-0"  
58 - @click="switchMode(Mode.CHART)"  
59 - >  
60 - <LineChartOutlined />  
61 - </Button>  
62 - </Tooltip>  
63 -  
64 - <Tooltip title="列表模式">  
65 - <Button  
66 - class="!p-2 !children:flex flex justify-center items-center"  
67 - @click="switchMode(Mode.TABLE)"  
68 - >  
69 - <BarsOutlined />  
70 - </Button>  
71 - </Tooltip> 53 + <div
  54 + v-show="mode === EnumTableChartMode.CHART"
  55 + class="flex h-70px items-center justify-end p-2"
  56 + >
  57 + <ModeSwitchButton
  58 + v-model:value="mode"
  59 + :mode="TABLE_CHART_MODE_LIST"
  60 + @change="switchMode"
  61 + />
72 </div> 62 </div>
73 <div 63 <div
74 - v-show="isNull && mode === Mode.CHART" 64 + v-show="isNull && mode === EnumTableChartMode.CHART"
75 ref="chartRef" 65 ref="chartRef"
76 :style="{ height: '400px', width }" 66 :style="{ height: '400px', width }"
77 > 67 >
@@ -79,30 +69,17 @@ @@ -79,30 +69,17 @@
79 <Empty 69 <Empty
80 :style="{ height: '550px', width }" 70 :style="{ height: '550px', width }"
81 class="flex flex-col justify-center items-center" 71 class="flex flex-col justify-center items-center"
82 - v-show="!isNull && mode === Mode.CHART" 72 + v-show="!isNull && mode === EnumTableChartMode.CHART"
83 /> 73 />
84 74
85 - <BasicTable v-show="mode === Mode.TABLE" @register="registerAttrTable"> 75 + <BasicTable v-show="mode === EnumTableChartMode.TABLE" @register="registerAttrTable">
86 <template #toolbar> 76 <template #toolbar>
87 - <div v-show="mode === Mode.TABLE" class="flex h-70px items-center justify-end p-2">  
88 - <Tooltip title="图表模式">  
89 - <Button  
90 - class="!p-2 !children:flex flex justify-center items-center border-r-0"  
91 - @click="switchMode(Mode.CHART)"  
92 - >  
93 - <LineChartOutlined />  
94 - </Button>  
95 - </Tooltip>  
96 -  
97 - <Tooltip title="列表模式">  
98 - <Button  
99 - :class="[mode === Mode.TABLE && '!bg-blue-500 svg:text-light-50']"  
100 - class="!p-2 !children:flex flex justify-center items-center"  
101 - @click="switchMode(Mode.TABLE)"  
102 - >  
103 - <BarsOutlined />  
104 - </Button>  
105 - </Tooltip> 77 + <div class="flex h-70px items-center justify-end p-2">
  78 + <ModeSwitchButton
  79 + v-model:value="mode"
  80 + :mode="TABLE_CHART_MODE_LIST"
  81 + @change="switchMode"
  82 + />
106 </div> 83 </div>
107 </template> 84 </template>
108 </BasicTable> 85 </BasicTable>
@@ -134,7 +111,7 @@ @@ -134,7 +111,7 @@
134 import { formSchema, columns } from './config.data'; 111 import { formSchema, columns } from './config.data';
135 import { BasicTable, useTable } from '/@/components/Table'; 112 import { BasicTable, useTable } from '/@/components/Table';
136 import { devicePage } from '/@/api/alarm/contact/alarmContact'; 113 import { devicePage } from '/@/api/alarm/contact/alarmContact';
137 - import { Tag, Empty, Spin, Tooltip, Button } from 'ant-design-vue'; 114 + import { Tag, Empty, Spin } from 'ant-design-vue';
138 import { DeviceState } from '/@/api/device/model/deviceModel'; 115 import { DeviceState } from '/@/api/device/model/deviceModel';
139 import { BAI_DU_MAP_URL } from '/@/utils/fnUtils'; 116 import { BAI_DU_MAP_URL } from '/@/utils/fnUtils';
140 import { useModal, BasicModal } from '/@/components/Modal'; 117 import { useModal, BasicModal } from '/@/components/Modal';
@@ -157,8 +134,12 @@ @@ -157,8 +134,12 @@
157 import { QueryWay, SchemaFiled, AggregateDataEnum } from './cpns/TimePeriodForm/config'; 134 import { QueryWay, SchemaFiled, AggregateDataEnum } from './cpns/TimePeriodForm/config';
158 import { useAsyncQueue } from './useAsyncQueue'; 135 import { useAsyncQueue } from './useAsyncQueue';
159 import { useHistoryData } from '../list/hook/useHistoryData'; 136 import { useHistoryData } from '../list/hook/useHistoryData';
160 - import { LineChartOutlined, BarsOutlined } from '@ant-design/icons-vue';  
161 import { formatToDateTime } from '/@/utils/dateUtil'; 137 import { formatToDateTime } from '/@/utils/dateUtil';
  138 + import {
  139 + TABLE_CHART_MODE_LIST,
  140 + EnumTableChartMode,
  141 + ModeSwitchButton,
  142 + } from '/@/components/Widget';
162 143
163 interface DeviceInfo { 144 interface DeviceInfo {
164 alarmStatus: 0 | 1; 145 alarmStatus: 0 | 1;
@@ -183,10 +164,7 @@ @@ -183,10 +164,7 @@
183 DeviceDetailDrawer, 164 DeviceDetailDrawer,
184 TimePeriodForm, 165 TimePeriodForm,
185 Spin, 166 Spin,
186 - Tooltip,  
187 - Button,  
188 - LineChartOutlined,  
189 - BarsOutlined, 167 + ModeSwitchButton,
190 }, 168 },
191 props: { 169 props: {
192 width: { 170 width: {
@@ -199,12 +177,7 @@ @@ -199,12 +177,7 @@
199 }, 177 },
200 }, 178 },
201 setup() { 179 setup() {
202 - enum Mode {  
203 - TABLE = 'table',  
204 - CHART = 'chart',  
205 - }  
206 -  
207 - const mode = ref<Mode>(Mode.CHART); 180 + const mode = ref<EnumTableChartMode>(EnumTableChartMode.CHART);
208 let entityId = ''; 181 let entityId = '';
209 let globalRecord: any = {}; 182 let globalRecord: any = {};
210 const wrapRef = ref<HTMLDivElement | null>(null); 183 const wrapRef = ref<HTMLDivElement | null>(null);
@@ -571,13 +544,14 @@ @@ -571,13 +544,14 @@
571 openGatewayDetailDrawer(true, data); 544 openGatewayDetailDrawer(true, data);
572 }; 545 };
573 546
574 - const switchMode = (flag: Mode) => { 547 + const switchMode = (flag: EnumTableChartMode) => {
575 mode.value = flag; 548 mode.value = flag;
576 }; 549 };
577 550
578 return { 551 return {
579 mode, 552 mode,
580 - Mode, 553 + EnumTableChartMode,
  554 + TABLE_CHART_MODE_LIST,
581 wrapRef, 555 wrapRef,
582 registerTable, 556 registerTable,
583 deviceRowClick, 557 deviceRowClick,
  1 +<script lang="ts" setup>
  2 + import { PageWrapper } from '/@/components/Page';
  3 + import { BasicForm, useForm } from '/@/components/Form';
  4 + import { List, Button, Tooltip, Card, PaginationProps, Image } from 'ant-design-vue';
  5 + import { ReloadOutlined, EyeOutlined, FormOutlined, MoreOutlined } from '@ant-design/icons-vue';
  6 + import { computed, onMounted, reactive, ref, unref } from 'vue';
  7 + import { CardLayoutButton, EnumTableCardMode, ModeSwitchButton } from '/@/components/Widget';
  8 + import { Authority } from '/@/components/Authority';
  9 + import {
  10 + deviceConfigDelete,
  11 + deviceConfigGetQuery,
  12 + setDeviceProfileIsDefaultApi,
  13 + } from '/@/api/device/deviceConfigApi';
  14 + import { ProfileRecord } from '/@/api/device/model/deviceConfigModel';
  15 + import { Dropdown } from '/@/components/Dropdown';
  16 + import {
  17 + defaultObj,
  18 + searchFormSchema,
  19 + DeviceTypeName,
  20 + ProductPermission,
  21 + } from './device.profile.data';
  22 + import { useMessage } from '/@/hooks/web/useMessage';
  23 + import { useSyncConfirm } from '/@/hooks/component/useSyncConfirm';
  24 + import DeviceProfileModal from './DeviceProfileModal.vue';
  25 + import DeviceProfileDrawer from './DeviceProfileDrawer.vue';
  26 + import { useModal } from '/@/components/Modal';
  27 + import { useDrawer } from '/@/components/Drawer';
  28 + import productDefault from '/@/assets/icons/product-default.svg';
  29 + import { usePermission } from '/@/hooks/web/usePermission';
  30 +
  31 + defineProps<{
  32 + mode: EnumTableCardMode;
  33 + }>();
  34 +
  35 + const emit = defineEmits(['changeMode']);
  36 +
  37 + enum DropMenuEvent {
  38 + SET_DEFAULT = 'setDefault',
  39 + DELETE = 'delete',
  40 + }
  41 + const IMAGE_FALLBACK = productDefault;
  42 +
  43 + const { createMessage } = useMessage();
  44 + const { createSyncConfirm } = useSyncConfirm();
  45 +
  46 + const [register, { getFieldsValue }] = useForm({
  47 + showAdvancedButton: true,
  48 + labelWidth: 100,
  49 + compact: true,
  50 + baseColProps: { span: 8 },
  51 + schemas: searchFormSchema,
  52 + submitFunc: async () => {
  53 + getDataSource();
  54 + },
  55 + });
  56 +
  57 + const [registerModal, { openModal }] = useModal();
  58 + const [registerDrawer, { openDrawer }] = useDrawer();
  59 +
  60 + const loading = ref(false);
  61 +
  62 + const pagination = reactive<PaginationProps>({
  63 + size: 'small',
  64 + showTotal: (total: number) => `共 ${total} 条数据`,
  65 + current: 1,
  66 + onChange: (page: number) => {
  67 + pagination.current = page;
  68 + getDataSource();
  69 + },
  70 + });
  71 +
  72 + const dataSource = ref<ProfileRecord[]>([]);
  73 +
  74 + const colNumber = ref(4);
  75 +
  76 + const getSelectAllFlag = computed(() => {
  77 + return unref(dataSource).every((item) => item.checked);
  78 + });
  79 +
  80 + const getCheckedRecord = computed(() => {
  81 + return unref(dataSource)
  82 + .filter((item) => item.checked)
  83 + .map((item) => item.id);
  84 + });
  85 +
  86 + const getDataSource = async () => {
  87 + try {
  88 + loading.value = true;
  89 + const params = getFieldsValue();
  90 + const { items, total } = await deviceConfigGetQuery({
  91 + page: pagination.current,
  92 + pageSize: unref(colNumber) * 2,
  93 + ...params,
  94 + });
  95 + pagination.total = total;
  96 + dataSource.value = items.map((item) => ({ ...item, checked: false }));
  97 + } catch (error) {
  98 + } finally {
  99 + loading.value = false;
  100 + }
  101 + };
  102 +
  103 + const { hasPermission } = usePermission();
  104 +
  105 + const getHasDeleteFlag = computed(() => {
  106 + return hasPermission(ProductPermission.DELETE);
  107 + });
  108 +
  109 + const getHasDetailFlag = computed(() => {
  110 + return hasPermission(ProductPermission.DETAIL);
  111 + });
  112 +
  113 + const getHasUpdateFlag = computed(() => {
  114 + return hasPermission(ProductPermission.UPDATE);
  115 + });
  116 +
  117 + const getDropDownList = (record: ProfileRecord) => {
  118 + const list = [
  119 + {
  120 + text: '默认',
  121 + event: DropMenuEvent.SET_DEFAULT,
  122 + icon: 'ant-design:unordered-list-outlined',
  123 + onClick: handleSetDefault.bind(null, record),
  124 + },
  125 + ];
  126 + if (unref(getHasDeleteFlag)) {
  127 + list.push({
  128 + text: '删除',
  129 + event: DropMenuEvent.DELETE,
  130 + icon: 'ant-design:delete-outlined',
  131 + onClick: handleDelete.bind(null, [record.id]),
  132 + });
  133 + }
  134 + return list;
  135 + };
  136 +
  137 + const handleModeChange = (mode: EnumTableCardMode) => {
  138 + emit('changeMode', mode);
  139 + };
  140 +
  141 + const handleCheckCard = (item: ProfileRecord) => {
  142 + item.checked = !item.checked;
  143 + };
  144 +
  145 + const handleSelectAll = () => {
  146 + dataSource.value = unref(dataSource).map((item) => {
  147 + return {
  148 + ...item,
  149 + checked: !unref(getSelectAllFlag),
  150 + };
  151 + });
  152 + };
  153 +
  154 + const handleCreate = () => {
  155 + openModal(true, {
  156 + isUpdate: false,
  157 + });
  158 + };
  159 +
  160 + const handleShowDetail = (record: ProfileRecord) => {
  161 + if (!unref(getHasDetailFlag)) return;
  162 + openDrawer(true, { record });
  163 + };
  164 +
  165 + const handleUpdate = (record: ProfileRecord) => {
  166 + if (!unref(getHasUpdateFlag)) return;
  167 + openModal(true, {
  168 + record,
  169 + isUpdate: true,
  170 + });
  171 + };
  172 +
  173 + const handleDelete = async (id: string[]) => {
  174 + try {
  175 + await createSyncConfirm({ iconType: 'warning', content: '是否确认删除操作?' });
  176 + await deviceConfigDelete(id);
  177 + createMessage.success('删除成功');
  178 + await getDataSource();
  179 + } catch (error) {
  180 + throw error;
  181 + }
  182 + };
  183 +
  184 + const handleSetDefault = async (record: ProfileRecord) => {
  185 + try {
  186 + const { tbProfileId } = record;
  187 + const data = await setDeviceProfileIsDefaultApi(tbProfileId, 'default', defaultObj);
  188 + if (!data) return createMessage.error('设置该产品为默认失败');
  189 + createMessage.success('设置该产品为默认成功');
  190 + await getDataSource();
  191 + } catch (error) {
  192 + throw error;
  193 + }
  194 + };
  195 +
  196 + onMounted(() => {
  197 + getDataSource();
  198 + });
  199 +</script>
  200 +
  201 +<template>
  202 + <PageWrapper dense contentFullHeight contentClass="flex">
  203 + <section class="flex-auto p-4 w-full profile-list">
  204 + <div class="flex-auto w-full bg-light-50 dark:bg-dark-900 p-4">
  205 + <BasicForm @register="register" />
  206 + </div>
  207 + <List
  208 + ref="listEl"
  209 + :loading="loading"
  210 + class="flex-auto bg-light-50 dark:bg-dark-900 !p-2 !mt-4"
  211 + position="bottom"
  212 + :pagination="pagination"
  213 + :data-source="dataSource"
  214 + :grid="{ gutter: 4, column: colNumber }"
  215 + >
  216 + <template #header>
  217 + <div class="flex gap-3 justify-end">
  218 + <Authority :value="ProductPermission.CREATE">
  219 + <Button type="primary" @click="handleCreate">新增产品</Button>
  220 + </Authority>
  221 +
  222 + <Button type="primary" @click="handleSelectAll">
  223 + {{ getSelectAllFlag ? '反选' : '全选' }}
  224 + </Button>
  225 + <Authority :value="ProductPermission.DELETE">
  226 + <Button
  227 + type="primary"
  228 + danger
  229 + :disabled="!getCheckedRecord.length"
  230 + @click="handleDelete(getCheckedRecord)"
  231 + >
  232 + 批量删除
  233 + </Button>
  234 + </Authority>
  235 +
  236 + <ModeSwitchButton :value="$props.mode" @change="handleModeChange" />
  237 + <CardLayoutButton v-model:value="colNumber" @change="getDataSource" />
  238 +
  239 + <Tooltip title="刷新">
  240 + <Button type="primary" @click="getDataSource">
  241 + <ReloadOutlined :spin="loading" />
  242 + </Button>
  243 + </Tooltip>
  244 + </div>
  245 + </template>
  246 + <template #renderItem="{ item }">
  247 + <List.Item>
  248 + <Card
  249 + hoverable
  250 + @click="handleCheckCard(item)"
  251 + :class="item.checked ? '!border-blue-500 !border-2' : ''"
  252 + >
  253 + <template #cover>
  254 + <div class="h-full w-full !flex justify-center items-center text-center p-1">
  255 + <Image
  256 + @click.stop
  257 + :height="144"
  258 + :src="item.image"
  259 + placeholder
  260 + :fallback="IMAGE_FALLBACK"
  261 + />
  262 + </div>
  263 + </template>
  264 + <template class="ant-card-actions" #actions>
  265 + <Tooltip title="详情">
  266 + <EyeOutlined
  267 + :class="getHasDetailFlag ? '' : '!cursor-not-allowed !text-gray-200'"
  268 + key="setting"
  269 + @click.stop="handleShowDetail(item)"
  270 + />
  271 + </Tooltip>
  272 + <Tooltip title="编辑">
  273 + <FormOutlined
  274 + :class="getHasUpdateFlag ? '' : '!cursor-not-allowed !text-gray-200'"
  275 + key="edit"
  276 + @click.stop="handleUpdate(item)"
  277 + />
  278 + </Tooltip>
  279 + <Dropdown :trigger="['hover']" :drop-menu-list="getDropDownList(item)">
  280 + <MoreOutlined @click.stop class="transform rotate-90" />
  281 + </Dropdown>
  282 + </template>
  283 + <Card.Meta>
  284 + <template #title>
  285 + <span class="truncate"> {{ item.name }} </span>
  286 + </template>
  287 + <template #description>
  288 + <div class="truncate h-11">
  289 + <div class="truncate">{{ DeviceTypeName[item.deviceType] }} </div>
  290 + <div class="truncate">{{ item.transportType }} </div>
  291 + </div>
  292 + </template>
  293 + </Card.Meta>
  294 + </Card>
  295 + </List.Item>
  296 + </template>
  297 + </List>
  298 + </section>
  299 + <DeviceProfileModal @register="registerModal" @success="getDataSource" />
  300 + <DeviceProfileDrawer @register="registerDrawer" />
  301 + </PageWrapper>
  302 +</template>
  303 +
  304 +<style lang="less" scoped>
  305 + .profile-list:deep(.ant-image-img) {
  306 + width: 100% !important;
  307 + height: 100% !important;
  308 + }
  309 +</style>
  1 +<template>
  2 + <div>
  3 + <BasicTable
  4 + class="devide-profiles"
  5 + @register="registerTable"
  6 + :rowSelection="{ type: 'checkbox' }"
  7 + :clickToRowSelect="false"
  8 + >
  9 + <template #toolbar>
  10 + <Authority :value="ProductPermission.CREATE">
  11 + <a-button type="primary" @click="handleCreate"> 新增产品 </a-button>
  12 + </Authority>
  13 + <Authority :value="ProductPermission.IMPORT">
  14 + <ImpExcel @success="loadDataSuccess" dateFormat="YYYY-MM-DD">
  15 + <a-button @click="handleImport"> 导入产品 </a-button>
  16 + </ImpExcel>
  17 + </Authority>
  18 + <Authority :value="ProductPermission.DELETE">
  19 + <Popconfirm
  20 + title="您确定要批量删除数据"
  21 + ok-text="确定"
  22 + cancel-text="取消"
  23 + @confirm="handleDeleteOrBatchDelete(null)"
  24 + >
  25 + <a-button type="primary" color="error" :disabled="hasBatchDelete"> 批量删除 </a-button>
  26 + </Popconfirm>
  27 + </Authority>
  28 + <ModeSwitchButton :value="$props.mode" @change="handleModeChange" />
  29 + </template>
  30 + <template #img="{ record }">
  31 + <TableImg
  32 + :size="30"
  33 + :showBadge="false"
  34 + :simpleShow="true"
  35 + :imgList="
  36 + typeof record.image !== 'undefined' && record.image !== '' && record.image != null
  37 + ? [record.image]
  38 + : null
  39 + "
  40 + />
  41 + </template>
  42 + <template #action="{ record }">
  43 + <TableAction
  44 + :actions="[
  45 + {
  46 + label: '详情',
  47 + auth: ProductPermission.DETAIL,
  48 + icon: 'ant-design:eye-outlined',
  49 + onClick: handleDetailView.bind(null, record),
  50 + },
  51 + {
  52 + label: '编辑',
  53 + auth: ProductPermission.UPDATE,
  54 + icon: 'clarity:note-edit-line',
  55 + onClick: handleEdit.bind(null, record),
  56 + ifShow: () => {
  57 + return record.name !== 'default' ? true : false;
  58 + },
  59 + },
  60 + ]"
  61 + :drop-down-actions="[
  62 + {
  63 + label: '默认',
  64 + icon: 'ant-design:profile-outlined',
  65 + onClick: handleSetDefault.bind(null, record),
  66 + ifShow: () => {
  67 + return record.default === false;
  68 + },
  69 + },
  70 + {
  71 + label: '导出',
  72 + auth: ProductPermission.EXPORT,
  73 + icon: 'ant-design:login-outlined',
  74 + onClick: handleExport.bind(null, record),
  75 + },
  76 + {
  77 + label: '删除',
  78 + auth: ProductPermission.DELETE,
  79 + icon: 'ant-design:delete-outlined',
  80 + color: 'error',
  81 + popConfirm: {
  82 + title: '是否确认删除',
  83 + confirm: handleDeleteOrBatchDelete.bind(null, record),
  84 + },
  85 + ifShow: () => {
  86 + return record.default === false && record.name !== 'default';
  87 + },
  88 + },
  89 + ]"
  90 + />
  91 + </template>
  92 + </BasicTable>
  93 + <DeviceProfileModal @register="registerModal" @success="handleSuccess" />
  94 + <DeviceProfileDrawer @register="registerDrawer" />
  95 + <ExpExcelModal
  96 + ref="expExcelModalRef"
  97 + @register="registerExportModal"
  98 + @success="defaultHeader"
  99 + />
  100 + </div>
  101 +</template>
  102 +<script lang="ts" setup>
  103 + import { ref, nextTick, onUnmounted } from 'vue';
  104 + import { BasicTable, TableImg, useTable, TableAction, BasicColumn } from '/@/components/Table';
  105 + import { columns, searchFormSchema, defaultObj, ProductPermission } from './device.profile.data';
  106 + import { useMessage } from '/@/hooks/web/useMessage';
  107 + import {
  108 + deviceConfigGetQuery,
  109 + deviceConfigDelete,
  110 + setDeviceProfileIsDefaultApi,
  111 + } from '/@/api/device/deviceConfigApi';
  112 + import { useModal } from '/@/components/Modal';
  113 + import { useDrawer } from '/@/components/Drawer';
  114 + import DeviceProfileModal from '/@/views/device/profiles/DeviceProfileModal.vue';
  115 + import { ImpExcel, ExcelData } from '/@/components/Excel';
  116 + import { jsonToSheetXlsx, ExpExcelModal, ExportModalResult } from '/@/components/Excel';
  117 + import { Authority } from '/@/components/Authority';
  118 + import { useBatchDelete } from '/@/hooks/web/useBatchDelete';
  119 + import { Popconfirm } from 'ant-design-vue';
  120 + import DeviceProfileDrawer from './DeviceProfileDrawer.vue';
  121 + import { EnumTableCardMode, ModeSwitchButton } from '/@/components/Widget';
  122 +
  123 + defineProps<{
  124 + mode: EnumTableCardMode;
  125 + }>();
  126 +
  127 + const emit = defineEmits(['changeMode']);
  128 +
  129 + const exportData: any = ref([]);
  130 + const expExcelModalRef: any = ref(null);
  131 + const getPathUrl = ref('');
  132 + const getPathUrlName = ref('');
  133 + const disabled = ref(true);
  134 + const onCloseVal = ref(0);
  135 + const immediateStatus = ref(false);
  136 + const { createMessage } = useMessage();
  137 + const [registerModal, { openModal }] = useModal();
  138 + const [registerExportModal, { openModal: openModalExcel }] = useModal();
  139 + const [registerTable, { setProps, reload, setTableData, getForm }] = useTable({
  140 + title: '产品列表',
  141 + clickToRowSelect: false,
  142 + api: deviceConfigGetQuery,
  143 + immediate: immediateStatus.value,
  144 + columns,
  145 + formConfig: {
  146 + labelWidth: 120,
  147 + schemas: searchFormSchema,
  148 + },
  149 + rowKey: 'id',
  150 + useSearchForm: true,
  151 + showTableSetting: true,
  152 + bordered: true,
  153 + showIndexColumn: false,
  154 + actionColumn: {
  155 + width: 200,
  156 + title: '操作',
  157 + dataIndex: 'action',
  158 + slots: { customRender: 'action' },
  159 + fixed: 'right',
  160 + },
  161 + });
  162 + const { hasBatchDelete, handleDeleteOrBatchDelete, selectionOptions } = useBatchDelete(
  163 + deviceConfigDelete,
  164 + handleSuccess,
  165 + setProps
  166 + );
  167 + selectionOptions.rowSelection.getCheckboxProps = (record: Recordable) => {
  168 + // Demo:status为1的选择框禁用
  169 + if (record.default === true) {
  170 + return { disabled: true };
  171 + } else if (record.name == 'default') {
  172 + return { disabled: true };
  173 + } else {
  174 + return { disabled: false };
  175 + }
  176 + };
  177 + nextTick(() => {
  178 + setProps(selectionOptions);
  179 + });
  180 + /**
  181 + *@param url,name
  182 + **/
  183 + function getParam(url, name) {
  184 + try {
  185 + let reg = new RegExp('(^|&)' + name + '=([^&]*)(&|$)');
  186 + let r = url.split('?')[1].match(reg);
  187 + if (r != null) {
  188 + return r[2];
  189 + }
  190 + return ''; //如果此处只写return;则返回的是undefined
  191 + } catch (e) {
  192 + return ''; //如果此处只写return;则返回的是undefined
  193 + }
  194 + }
  195 + getPathUrl.value = window.location.href;
  196 + const name = 'name';
  197 + const getName = getParam(getPathUrl.value, name);
  198 + getPathUrlName.value = decodeURIComponent(getName);
  199 +
  200 + const setRowClassName = async () => {
  201 + if (getPathUrlName.value !== '') {
  202 + const { items } = await deviceConfigGetQuery({
  203 + page: 1,
  204 + pageSize: 10,
  205 + name: getPathUrlName.value,
  206 + });
  207 + nextTick(() => {
  208 + setTableData(items);
  209 + const { setFieldsValue, resetFields } = getForm();
  210 + setFieldsValue({
  211 + name: getPathUrlName.value,
  212 + });
  213 + if (onCloseVal.value == 1) {
  214 + resetFields();
  215 + }
  216 + });
  217 + } else {
  218 + setTimeout(() => {
  219 + reload();
  220 + }, 80);
  221 + }
  222 + };
  223 + setRowClassName();
  224 + onUnmounted(() => {
  225 + getPathUrlName.value = '';
  226 + onCloseVal.value = 1;
  227 + });
  228 + const tableListRef = ref<
  229 + {
  230 + title: string;
  231 + columns?: any[];
  232 + dataSource?: any[];
  233 + }[]
  234 + >([]);
  235 +
  236 + function loadDataSuccess(excelDataList: ExcelData[]) {
  237 + tableListRef.value = [];
  238 + console.log(excelDataList);
  239 + for (const excelData of excelDataList) {
  240 + const {
  241 + header,
  242 + results,
  243 + meta: { sheetName },
  244 + } = excelData;
  245 + const columns: BasicColumn[] = [];
  246 + for (const title of header) {
  247 + columns.push({ title, dataIndex: title });
  248 + }
  249 + tableListRef.value.push({ title: sheetName, dataSource: results, columns });
  250 + }
  251 + }
  252 + //新增
  253 + function handleCreate() {
  254 + openModal(true, {
  255 + isUpdate: false,
  256 + });
  257 + }
  258 + //编辑
  259 + function handleEdit(record: Recordable) {
  260 + openModal(true, {
  261 + record,
  262 + isUpdate: true,
  263 + });
  264 + }
  265 +
  266 + const [registerDrawer, { openDrawer }] = useDrawer();
  267 + //详情
  268 + function handleDetailView(record: Recordable) {
  269 + openDrawer(true, { record });
  270 + }
  271 +
  272 + function defaultHeader({ filename, bookType }: ExportModalResult) {
  273 + // 默认Object.keys(data[0])作为header
  274 + const data = exportData.value;
  275 + jsonToSheetXlsx({
  276 + data,
  277 + filename,
  278 + write2excelOpts: {
  279 + bookType,
  280 + },
  281 + });
  282 + }
  283 + //导出
  284 + const handleExport = (record: Recordable) => {
  285 + exportData.value = [];
  286 + exportData.value.push({
  287 + createTime: record.createTime,
  288 + description: record.description,
  289 + name: record.name,
  290 + });
  291 + nextTick(() => {
  292 + openModalExcel();
  293 + expExcelModalRef.value?.clearFieldFunc();
  294 + });
  295 + };
  296 + //导入
  297 + function handleImport() {
  298 + console.log('record');
  299 + }
  300 + function handleSuccess() {
  301 + reload();
  302 + }
  303 +
  304 + const handleSetDefault = async (record: Recordable) => {
  305 + let id = record.tbProfileId;
  306 + const data = await setDeviceProfileIsDefaultApi(id, 'default', defaultObj);
  307 + if (!data) return createMessage.error('设置该产品为默认失败');
  308 + createMessage.success('设置该产品为默认成功');
  309 + reload();
  310 + disabled.value = true;
  311 + };
  312 +
  313 + const handleModeChange = (value: EnumTableCardMode) => {
  314 + emit('changeMode', value);
  315 + };
  316 +</script>
  317 +
  318 +<style lang="css">
  319 + .devide-profiles .rowcolor {
  320 + color: red;
  321 + }
  322 +
  323 + .devide-profiles .rowcolor2 {
  324 + background: #a2c3e6;
  325 + }
  326 +</style>
@@ -9,6 +9,26 @@ import { FormField, FunctionType } from './step/cpns/physical/cpns/config'; @@ -9,6 +9,26 @@ import { FormField, FunctionType } from './step/cpns/physical/cpns/config';
9 import { h } from 'vue'; 9 import { h } from 'vue';
10 import { Tag } from 'ant-design-vue'; 10 import { Tag } from 'ant-design-vue';
11 11
  12 +export enum Mode {
  13 + CARD = 'card',
  14 + TABLE = 'table',
  15 +}
  16 +
  17 +export enum DeviceTypeName {
  18 + DIRECT_CONNECTION = '直连设备',
  19 + GATEWAY = '网关设备',
  20 + SENSOR = '网关子设备',
  21 +}
  22 +export enum ProductPermission {
  23 + DETAIL = 'api:yt:deviceProfile:get',
  24 + CREATE = 'api:yt:deviceProfile:post',
  25 + UPDATE = 'api:yt:deviceProfile:update',
  26 + DELETE = 'api:yt:deviceProfile:delete',
  27 + SET_DEFAULT = '',
  28 + IMPORT = 'api:yt:deviceProfile:import',
  29 + EXPORT = 'api:yt:deviceProfile:export',
  30 +}
  31 +
12 export enum ModelOfMatterPermission { 32 export enum ModelOfMatterPermission {
13 CREATE = 'api:yt:things_model:post', 33 CREATE = 'api:yt:things_model:post',
14 UPDATE = 'api:yt:things_model:put', 34 UPDATE = 'api:yt:things_model:put',
1 -<template>  
2 - <div>  
3 - <BasicTable  
4 - class="devide-profiles"  
5 - @register="registerTable"  
6 - :rowSelection="{ type: 'checkbox' }"  
7 - :clickToRowSelect="false"  
8 - >  
9 - <template #toolbar>  
10 - <Authority value="api:yt:deviceProfile:post">  
11 - <a-button type="primary" @click="handleCreate"> 新增产品 </a-button>  
12 - </Authority>  
13 - <Authority value="api:yt:deviceProfile:import">  
14 - <ImpExcel @success="loadDataSuccess" dateFormat="YYYY-MM-DD">  
15 - <a-button @click="handleImport"> 导入产品 </a-button>  
16 - </ImpExcel>  
17 - </Authority>  
18 - <Authority value="api:yt:deviceProfile:delete">  
19 - <Popconfirm  
20 - title="您确定要批量删除数据"  
21 - ok-text="确定"  
22 - cancel-text="取消"  
23 - @confirm="handleDeleteOrBatchDelete(null)"  
24 - >  
25 - <a-button type="primary" color="error" :disabled="hasBatchDelete"> 批量删除 </a-button>  
26 - </Popconfirm>  
27 - </Authority>  
28 - </template>  
29 - <template #img="{ record }">  
30 - <TableImg  
31 - :size="30"  
32 - :showBadge="false"  
33 - :simpleShow="true"  
34 - :imgList="  
35 - typeof record.image !== 'undefined' && record.image !== '' && record.image != null  
36 - ? [record.image]  
37 - : null  
38 - "  
39 - />  
40 - </template>  
41 - <template #action="{ record }">  
42 - <TableAction  
43 - :actions="[  
44 - {  
45 - label: '详情',  
46 - auth: 'api:yt:deviceProfile:get',  
47 - icon: 'ant-design:eye-outlined',  
48 - onClick: handleDetailView.bind(null, record),  
49 - },  
50 - {  
51 - label: '编辑',  
52 - auth: 'api:yt:deviceProfile:update',  
53 - icon: 'clarity:note-edit-line',  
54 - onClick: handleEdit.bind(null, record),  
55 - ifShow: () => {  
56 - return record.name !== 'default' ? true : false;  
57 - },  
58 - },  
59 - ]"  
60 - :drop-down-actions="[  
61 - {  
62 - label: '默认',  
63 - icon: 'ant-design:profile-outlined',  
64 - onClick: handleSetDefault.bind(null, record),  
65 - ifShow: () => {  
66 - return record.default === false;  
67 - },  
68 - },  
69 - {  
70 - label: '导出',  
71 - auth: 'api:yt:deviceProfile:export',  
72 - icon: 'ant-design:login-outlined',  
73 - onClick: handleExport.bind(null, record),  
74 - },  
75 - {  
76 - label: '删除',  
77 - auth: 'api:yt:deviceProfile:delete',  
78 - icon: 'ant-design:delete-outlined',  
79 - color: 'error',  
80 - popConfirm: {  
81 - title: '是否确认删除',  
82 - confirm: handleDeleteOrBatchDelete.bind(null, record),  
83 - },  
84 - ifShow: () => {  
85 - return record.default === false && record.name !== 'default';  
86 - },  
87 - },  
88 - ]"  
89 - />  
90 - </template>  
91 - </BasicTable>  
92 - <DeviceProfileModal @register="registerModal" @success="handleSuccess" />  
93 - <DeviceProfileDrawer @register="registerDrawer" />  
94 - <ExpExcelModal  
95 - ref="expExcelModalRef"  
96 - @register="registerExportModal"  
97 - @success="defaultHeader"  
98 - />  
99 - </div>  
100 -</template>  
101 <script lang="ts" setup> 1 <script lang="ts" setup>
102 - import { ref, nextTick, onUnmounted } from 'vue';  
103 - import { BasicTable, TableImg, useTable, TableAction, BasicColumn } from '/@/components/Table';  
104 - import { columns, searchFormSchema, defaultObj } from './device.profile.data';  
105 - import { useMessage } from '/@/hooks/web/useMessage';  
106 - import {  
107 - deviceConfigGetQuery,  
108 - deviceConfigDelete,  
109 - setDeviceProfileIsDefaultApi,  
110 - } from '/@/api/device/deviceConfigApi';  
111 - import { useModal } from '/@/components/Modal';  
112 - import { useDrawer } from '/@/components/Drawer';  
113 - import DeviceProfileModal from '/@/views/device/profiles/DeviceProfileModal.vue';  
114 - import { ImpExcel, ExcelData } from '/@/components/Excel';  
115 - import { jsonToSheetXlsx, ExpExcelModal, ExportModalResult } from '/@/components/Excel';  
116 - import { Authority } from '/@/components/Authority';  
117 - import { useBatchDelete } from '/@/hooks/web/useBatchDelete';  
118 - import { Popconfirm } from 'ant-design-vue';  
119 - import DeviceProfileDrawer from './DeviceProfileDrawer.vue';  
120 -  
121 - const exportData: any = ref([]);  
122 - const expExcelModalRef: any = ref(null);  
123 - const getPathUrl = ref('');  
124 - const getPathUrlName = ref('');  
125 - const disabled = ref(true);  
126 - const onCloseVal = ref(0);  
127 - const immediateStatus = ref(false);  
128 - const { createMessage } = useMessage();  
129 - const [registerModal, { openModal }] = useModal();  
130 - const [registerExportModal, { openModal: openModalExcel }] = useModal();  
131 - const [registerTable, { setProps, reload, setTableData, getForm }] = useTable({  
132 - title: '产品列表',  
133 - clickToRowSelect: false,  
134 - api: deviceConfigGetQuery,  
135 - immediate: immediateStatus.value,  
136 - columns,  
137 - formConfig: {  
138 - labelWidth: 120,  
139 - schemas: searchFormSchema,  
140 - },  
141 - rowKey: 'id',  
142 - useSearchForm: true,  
143 - showTableSetting: true,  
144 - bordered: true,  
145 - showIndexColumn: false,  
146 - actionColumn: {  
147 - width: 200,  
148 - title: '操作',  
149 - dataIndex: 'action',  
150 - slots: { customRender: 'action' },  
151 - fixed: 'right',  
152 - },  
153 - });  
154 - const { hasBatchDelete, handleDeleteOrBatchDelete, selectionOptions } = useBatchDelete(  
155 - deviceConfigDelete,  
156 - handleSuccess,  
157 - setProps  
158 - );  
159 - selectionOptions.rowSelection.getCheckboxProps = (record: Recordable) => {  
160 - // Demo:status为1的选择框禁用  
161 - if (record.default === true) {  
162 - return { disabled: true };  
163 - } else if (record.name == 'default') {  
164 - return { disabled: true };  
165 - } else {  
166 - return { disabled: false };  
167 - }  
168 - };  
169 - nextTick(() => {  
170 - setProps(selectionOptions);  
171 - });  
172 - /**  
173 - *@param url,name  
174 - **/  
175 - function getParam(url, name) {  
176 - try {  
177 - let reg = new RegExp('(^|&)' + name + '=([^&]*)(&|$)');  
178 - let r = url.split('?')[1].match(reg);  
179 - if (r != null) {  
180 - return r[2];  
181 - }  
182 - return ''; //如果此处只写return;则返回的是undefined  
183 - } catch (e) {  
184 - return ''; //如果此处只写return;则返回的是undefined  
185 - }  
186 - }  
187 - getPathUrl.value = window.location.href;  
188 - const name = 'name';  
189 - const getName = getParam(getPathUrl.value, name);  
190 - getPathUrlName.value = decodeURIComponent(getName);  
191 -  
192 - const setRowClassName = async () => {  
193 - if (getPathUrlName.value !== '') {  
194 - const { items } = await deviceConfigGetQuery({  
195 - page: 1,  
196 - pageSize: 10,  
197 - name: getPathUrlName.value,  
198 - });  
199 - nextTick(() => {  
200 - setTableData(items);  
201 - const { setFieldsValue, resetFields } = getForm();  
202 - setFieldsValue({  
203 - name: getPathUrlName.value,  
204 - });  
205 - if (onCloseVal.value == 1) {  
206 - resetFields();  
207 - }  
208 - });  
209 - } else {  
210 - setTimeout(() => {  
211 - reload();  
212 - }, 80);  
213 - }  
214 - };  
215 - setRowClassName();  
216 - onUnmounted(() => {  
217 - getPathUrlName.value = '';  
218 - onCloseVal.value = 1;  
219 - });  
220 - const tableListRef = ref<  
221 - {  
222 - title: string;  
223 - columns?: any[];  
224 - dataSource?: any[];  
225 - }[]  
226 - >([]);  
227 -  
228 - function loadDataSuccess(excelDataList: ExcelData[]) {  
229 - tableListRef.value = [];  
230 - console.log(excelDataList);  
231 - for (const excelData of excelDataList) {  
232 - const {  
233 - header,  
234 - results,  
235 - meta: { sheetName },  
236 - } = excelData;  
237 - const columns: BasicColumn[] = [];  
238 - for (const title of header) {  
239 - columns.push({ title, dataIndex: title });  
240 - }  
241 - tableListRef.value.push({ title: sheetName, dataSource: results, columns });  
242 - }  
243 - }  
244 - //新增  
245 - function handleCreate() {  
246 - openModal(true, {  
247 - isUpdate: false,  
248 - });  
249 - }  
250 - //编辑  
251 - function handleEdit(record: Recordable) {  
252 - openModal(true, {  
253 - record,  
254 - isUpdate: true,  
255 - });  
256 - }  
257 -  
258 - const [registerDrawer, { openDrawer }] = useDrawer();  
259 - //详情  
260 - function handleDetailView(record: Recordable) {  
261 - openDrawer(true, { record });  
262 - }  
263 -  
264 - function defaultHeader({ filename, bookType }: ExportModalResult) {  
265 - // 默认Object.keys(data[0])作为header  
266 - const data = exportData.value;  
267 - jsonToSheetXlsx({  
268 - data,  
269 - filename,  
270 - write2excelOpts: {  
271 - bookType,  
272 - },  
273 - });  
274 - }  
275 - //导出  
276 - const handleExport = (record: Recordable) => {  
277 - exportData.value = [];  
278 - exportData.value.push({  
279 - createTime: record.createTime,  
280 - description: record.description,  
281 - name: record.name,  
282 - });  
283 - nextTick(() => {  
284 - openModalExcel();  
285 - expExcelModalRef.value?.clearFieldFunc();  
286 - });  
287 - };  
288 - //导入  
289 - function handleImport() {  
290 - console.log('record');  
291 - }  
292 - function handleSuccess() {  
293 - reload();  
294 - }  
295 -  
296 - const handleSetDefault = async (record: Recordable) => {  
297 - let id = record.tbProfileId;  
298 - const data = await setDeviceProfileIsDefaultApi(id, 'default', defaultObj);  
299 - if (!data) return createMessage.error('设置该产品为默认失败');  
300 - createMessage.success('设置该产品为默认成功');  
301 - reload();  
302 - disabled.value = true; 2 + import { ref } from 'vue';
  3 + import TableMode from './TableMode.vue';
  4 + import CardMode from './CardMode.vue';
  5 + import { EnumTableCardMode } from '/@/components/Widget';
  6 + const mode = ref(EnumTableCardMode.CARD);
  7 +
  8 + const handleChangeMode = (flag: EnumTableCardMode) => {
  9 + mode.value = flag;
303 }; 10 };
304 </script> 11 </script>
305 12
306 -<style lang="css">  
307 - .devide-profiles .rowcolor {  
308 - color: red;  
309 - }  
310 -  
311 - .devide-profiles .rowcolor2 {  
312 - background: #a2c3e6;  
313 - }  
314 -</style> 13 +<template>
  14 + <section>
  15 + <CardMode v-if="mode === EnumTableCardMode.CARD" :mode="mode" @change-mode="handleChangeMode" />
  16 + <TableMode
  17 + v-if="mode === EnumTableCardMode.TABLE"
  18 + :mode="mode"
  19 + @change-mode="handleChangeMode"
  20 + />
  21 + </section>
  22 +</template>
@@ -152,6 +152,17 @@ @@ -152,6 +152,17 @@
152 field: 'transportType', 152 field: 'transportType',
153 componentProps: { 153 componentProps: {
154 disabled: status, 154 disabled: status,
  155 + options: [
  156 + { label: '默认', value: 'DEFAULT' },
  157 + { label: 'MQTT', value: 'MQTT' },
  158 + { label: 'CoAP', value: 'COAP' },
  159 + { label: 'LWM2M', value: 'LWM2M' },
  160 + { label: 'SNMP', value: 'SNMP' },
  161 + { label: 'TCP', value: 'TCP' },
  162 + ],
  163 + onChange(e) {
  164 + isMqttType.value = e;
  165 + },
155 }, 166 },
156 }); 167 });
157 }; 168 };
1 <script lang="ts" setup> 1 <script lang="ts" setup>
2 import { computed, nextTick, Ref, ref, unref } from 'vue'; 2 import { computed, nextTick, Ref, ref, unref } from 'vue';
3 import { getDeviceHistoryInfo } from '/@/api/alarm/position'; 3 import { getDeviceHistoryInfo } from '/@/api/alarm/position';
4 - import { Empty, Tooltip, Button, Spin } from 'ant-design-vue'; 4 + import { Empty, Spin } from 'ant-design-vue';
5 import { useECharts } from '/@/hooks/web/useECharts'; 5 import { useECharts } from '/@/hooks/web/useECharts';
6 import { AggregateDataEnum } from '/@/views/device/localtion/config.data'; 6 import { AggregateDataEnum } from '/@/views/device/localtion/config.data';
7 import { useGridLayout } from '/@/hooks/component/useGridLayout'; 7 import { useGridLayout } from '/@/hooks/component/useGridLayout';
@@ -14,19 +14,18 @@ @@ -14,19 +14,18 @@
14 import { getAllDeviceByOrg } from '/@/api/dataBoard'; 14 import { getAllDeviceByOrg } from '/@/api/dataBoard';
15 import { useHistoryData } from '/@/views/device/list/hook/useHistoryData'; 15 import { useHistoryData } from '/@/views/device/list/hook/useHistoryData';
16 import { BasicTable, useTable } from '/@/components/Table'; 16 import { BasicTable, useTable } from '/@/components/Table';
17 - import { LineChartOutlined, BarsOutlined } from '@ant-design/icons-vue';  
18 import { formatToDateTime } from '/@/utils/dateUtil'; 17 import { formatToDateTime } from '/@/utils/dateUtil';
  18 + import {
  19 + ModeSwitchButton,
  20 + TABLE_CHART_MODE_LIST,
  21 + EnumTableChartMode,
  22 + } from '/@/components/Widget';
19 23
20 type DeviceOption = Record<'label' | 'value' | 'organizationId', string>; 24 type DeviceOption = Record<'label' | 'value' | 'organizationId', string>;
21 25
22 defineEmits(['register']); 26 defineEmits(['register']);
23 27
24 - enum Mode {  
25 - TABLE = 'table',  
26 - CHART = 'chart',  
27 - }  
28 -  
29 - const mode = ref<Mode>(Mode.CHART); 28 + const mode = ref<EnumTableChartMode>(EnumTableChartMode.CHART);
30 29
31 const chartRef = ref(); 30 const chartRef = ref();
32 31
@@ -245,7 +244,7 @@ @@ -245,7 +244,7 @@
245 destory(); 244 destory();
246 }; 245 };
247 246
248 - const switchMode = (flag: Mode) => { 247 + const switchMode = (flag: EnumTableChartMode) => {
249 mode.value = flag; 248 mode.value = flag;
250 }; 249 };
251 </script> 250 </script>
@@ -269,61 +268,38 @@ @@ -269,61 +268,38 @@
269 </section> 268 </section>
270 <section class="bg-white p-3" style="min-height: 350px"> 269 <section class="bg-white p-3" style="min-height: 350px">
271 <Spin :spinning="loading" :absolute="true"> 270 <Spin :spinning="loading" :absolute="true">
272 - <div v-show="mode === Mode.CHART" class="flex h-70px items-center justify-end p-2">  
273 - <Tooltip title="图表模式">  
274 - <Button  
275 - :class="[mode === Mode.CHART && '!bg-blue-500 svg:text-light-50']"  
276 - class="!p-2 !children:flex flex justify-center items-center border-r-0"  
277 - @click="switchMode(Mode.CHART)"  
278 - >  
279 - <LineChartOutlined />  
280 - </Button>  
281 - </Tooltip>  
282 -  
283 - <Tooltip title="列表模式">  
284 - <Button  
285 - class="!p-2 !children:flex flex justify-center items-center"  
286 - @click="switchMode(Mode.TABLE)"  
287 - >  
288 - <BarsOutlined />  
289 - </Button>  
290 - </Tooltip> 271 + <div
  272 + v-show="mode === EnumTableChartMode.CHART"
  273 + class="flex h-70px items-center justify-end p-2"
  274 + >
  275 + <ModeSwitchButton
  276 + v-model:value="mode"
  277 + :mode="TABLE_CHART_MODE_LIST"
  278 + @change="switchMode"
  279 + />
291 </div> 280 </div>
292 281
293 <div 282 <div
294 - v-show="isNull && mode === Mode.CHART" 283 + v-show="isNull && mode === EnumTableChartMode.CHART"
295 ref="chartRef" 284 ref="chartRef"
296 :style="{ height: '350px', width: '100%' }" 285 :style="{ height: '350px', width: '100%' }"
297 > 286 >
298 </div> 287 </div>
299 <Empty 288 <Empty
300 - v-if="mode === Mode.CHART" 289 + v-if="mode === EnumTableChartMode.CHART"
301 class="h-350px flex flex-col justify-center items-center" 290 class="h-350px flex flex-col justify-center items-center"
302 description="暂无数据,请选择设备查询" 291 description="暂无数据,请选择设备查询"
303 v-show="!isNull" 292 v-show="!isNull"
304 /> 293 />
305 294
306 - <BasicTable v-show="mode === Mode.TABLE" @register="registerTable"> 295 + <BasicTable v-show="mode === EnumTableChartMode.TABLE" @register="registerTable">
307 <template #toolbar> 296 <template #toolbar>
308 - <div v-show="mode === Mode.TABLE" class="flex h-70px items-center justify-end p-2">  
309 - <Tooltip title="图表模式">  
310 - <Button  
311 - class="!p-2 !children:flex flex justify-center items-center border-r-0"  
312 - @click="switchMode(Mode.CHART)"  
313 - >  
314 - <LineChartOutlined />  
315 - </Button>  
316 - </Tooltip>  
317 -  
318 - <Tooltip title="列表模式">  
319 - <Button  
320 - :class="[mode === Mode.TABLE && '!bg-blue-500 svg:text-light-50']"  
321 - class="!p-2 !children:flex flex justify-center items-center"  
322 - @click="switchMode(Mode.TABLE)"  
323 - >  
324 - <BarsOutlined />  
325 - </Button>  
326 - </Tooltip> 297 + <div class="flex h-70px items-center justify-end p-2">
  298 + <ModeSwitchButton
  299 + v-model:value="mode"
  300 + :mode="TABLE_CHART_MODE_LIST"
  301 + @change="switchMode"
  302 + />
327 </div> 303 </div>
328 </template> 304 </template>
329 </BasicTable> 305 </BasicTable>