Commit afde031f90ddbab1e1b958c78a4668a84f509831
Merge branch 'main_dev'
# Conflicts: # src/views/message/template/TemplateDrawer.vue
Showing
79 changed files
with
1186 additions
and
310 deletions
@@ -13,7 +13,7 @@ | @@ -13,7 +13,7 @@ | ||
13 | name="viewport" | 13 | name="viewport" |
14 | content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=0" | 14 | content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=0" |
15 | /> | 15 | /> |
16 | - <title></title> | 16 | + <title>‎</title> |
17 | <link rel="icon" href="/favicon.ico" /> | 17 | <link rel="icon" href="/favicon.ico" /> |
18 | </head> | 18 | </head> |
19 | 19 |
@@ -53,6 +53,7 @@ export interface DataBoardRecord { | @@ -53,6 +53,7 @@ export interface DataBoardRecord { | ||
53 | publicId: string; | 53 | publicId: string; |
54 | organizationId?: string; | 54 | organizationId?: string; |
55 | accessCredentials?: string; | 55 | accessCredentials?: string; |
56 | + platform?: string; | ||
56 | } | 57 | } |
57 | 58 | ||
58 | export interface DataBoardList { | 59 | export interface DataBoardList { |
@@ -137,7 +138,12 @@ export interface MasterDeviceList { | @@ -137,7 +138,12 @@ export interface MasterDeviceList { | ||
137 | } | 138 | } |
138 | 139 | ||
139 | export interface ComponentInfoDetail { | 140 | export interface ComponentInfoDetail { |
140 | - data: { componentData: DataComponentRecord[]; componentLayout: Layout[] }; | 141 | + data: { |
142 | + componentData: DataComponentRecord[]; | ||
143 | + componentLayout: Layout[]; | ||
144 | + phoneModel?: string | object; | ||
145 | + platform: string; | ||
146 | + }; | ||
141 | } | 147 | } |
142 | 148 | ||
143 | export interface DeviceAttributeParams { | 149 | export interface DeviceAttributeParams { |
@@ -187,6 +187,7 @@ export interface DeviceRecord { | @@ -187,6 +187,7 @@ export interface DeviceRecord { | ||
187 | brand?: string; | 187 | brand?: string; |
188 | deviceProfileId: string; | 188 | deviceProfileId: string; |
189 | organizationId: string; | 189 | organizationId: string; |
190 | + alarmStatus: number; | ||
190 | deviceProfile: { | 191 | deviceProfile: { |
191 | default: boolean; | 192 | default: boolean; |
192 | name: string; | 193 | name: string; |
@@ -7,6 +7,9 @@ export interface Specs { | @@ -7,6 +7,9 @@ export interface Specs { | ||
7 | unit: string; | 7 | unit: string; |
8 | unitName: string; | 8 | unitName: string; |
9 | 9 | ||
10 | + dataType?: string; | ||
11 | + name?: string; | ||
12 | + value?: string; | ||
10 | step: string; | 13 | step: string; |
11 | length: string; | 14 | length: string; |
12 | boolOpen: string; | 15 | boolOpen: string; |
@@ -20,6 +23,7 @@ export interface Specs { | @@ -20,6 +23,7 @@ export interface Specs { | ||
20 | export interface DataType { | 23 | export interface DataType { |
21 | type: DataTypeEnum; | 24 | type: DataTypeEnum; |
22 | specs?: Partial<Specs> | StructJSON[]; | 25 | specs?: Partial<Specs> | StructJSON[]; |
26 | + specsList?: Specs[]; | ||
23 | } | 27 | } |
24 | 28 | ||
25 | export interface StructJSON { | 29 | export interface StructJSON { |
@@ -21,6 +21,7 @@ enum Api { | @@ -21,6 +21,7 @@ enum Api { | ||
21 | SendLoginSmsCode = '/noauth/send_login_code/', | 21 | SendLoginSmsCode = '/noauth/send_login_code/', |
22 | ResetCode = '/noauth/reset_code/', | 22 | ResetCode = '/noauth/reset_code/', |
23 | ResetPassword = '/noauth/reset/', | 23 | ResetPassword = '/noauth/reset/', |
24 | + APP_GET_TOKEN = '/third/login/id/', | ||
24 | } | 25 | } |
25 | 26 | ||
26 | /** | 27 | /** |
@@ -100,3 +101,9 @@ export const getUserToken = (id: string) => { | @@ -100,3 +101,9 @@ export const getUserToken = (id: string) => { | ||
100 | url: `/third/login/id/${id}`, | 101 | url: `/third/login/id/${id}`, |
101 | }); | 102 | }); |
102 | }; | 103 | }; |
104 | + | ||
105 | +export const doAppLogin = (userId: string) => { | ||
106 | + return defHttp.get<Record<'refreshToken' | 'token', string>>({ | ||
107 | + url: `${Api.APP_GET_TOKEN}${userId}`, | ||
108 | + }); | ||
109 | +}; |
src/assets/images/sn-step4.png
0 → 100644
197 KB
src/assets/svg/battery.svg
0 → 100644
1 | +<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1702374950759" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1997" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M144.700101 684.994006l535.580045 0L680.280146 359.237781 144.700101 359.237781 144.700101 684.994006 144.700101 684.994006zM918.373823 440.680675l0-81.442894c0-44.791136-36.649711-81.437777-81.437777-81.437777l-692.235944 0c-44.791136 0-81.437777 36.646642-81.437777 81.437777L63.262324 684.994006c0 44.791136 36.646642 81.442894 81.437777 81.442894l692.235944 0c44.788066 0 81.437777-36.650735 81.437777-81.442894l0-81.437777c22.396079 0 40.7194-18.322297 40.7194-40.7194l0-81.436754C959.093223 459.003995 940.769902 440.680675 918.373823 440.680675L918.373823 440.680675zM877.655446 481.400075l0 81.436754L877.655446 684.994006c0 22.395056-18.323321 40.718377-40.7194 40.718377l-692.235944 0c-22.396079 0-40.7194-18.323321-40.7194-40.718377L103.980701 359.237781c0-22.396079 18.323321-40.7194 40.7194-40.7194l692.235944 0c22.396079 0 40.7194 18.323321 40.7194 40.7194L877.655446 481.400075 877.655446 481.400075zM877.655446 481.400075" fill="#272636" p-id="1998"></path></svg> |
src/assets/svg/signal.svg
0 → 100644
1 | +<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1702374946029" class="icon" viewBox="0 0 1294 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1850" xmlns:xlink="http://www.w3.org/1999/xlink" width="252.734375" height="200"><path d="M0 727.578947l188.631579 0 0 296.421053-188.631579 0 0-296.421053Z" p-id="1851"></path><path d="M269.473684 565.894737l188.631579 0 0 458.105263-188.631579 0 0-458.105263Z" p-id="1852"></path><path d="M565.894737 377.263158l161.684211 0 0 646.736842-161.684211 0 0-646.736842Z" p-id="1853"></path><path d="M835.368421 188.631579l188.631579 0 0 835.368421-188.631579 0 0-835.368421Z" p-id="1854"></path><path d="M1104.842105 0l188.631579 0 0 1024-188.631579 0 0-1024Z" p-id="1855"></path></svg> |
src/assets/svg/wifi.svg
0 → 100644
1 | +<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1702374954963" class="icon" viewBox="0 0 1280 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2277" xmlns:xlink="http://www.w3.org/1999/xlink" width="250" height="200"><path d="M1269.82 309.76C915.48-17.98 364.38-17.86 10.18 309.76c-13.32 12.32-13.58 33.18-0.7 45.96l68.48 67.94c12.28 12.2 32.04 12.46 44.8 0.76 291.84-267.36 742.6-267.42 1034.5 0 12.76 11.7 32.52 11.42 44.8-0.76l68.48-67.94c12.86-12.78 12.6-33.64-0.72-45.96zM640 704c-70.7 0-128 57.3-128 128s57.3 128 128 128 128-57.3 128-128-57.3-128-128-128z m405.34-167.18c-230.52-203.86-580.42-203.64-810.68 0-13.8 12.2-14.24 33.38-1.14 46.3l68.88 67.98c12 11.84 31.32 12.64 44.1 1.6 167.9-145.14 419.48-144.82 586.98 0 12.78 11.04 32.1 10.26 44.1-1.6l68.88-67.98c13.12-12.92 12.66-34.12-1.12-46.3z" p-id="2278"></path></svg> |
@@ -42,6 +42,7 @@ import InputGroup from './components/InputGroup.vue'; | @@ -42,6 +42,7 @@ import InputGroup from './components/InputGroup.vue'; | ||
42 | import RegisterAddressInput from '/@/views/task/center/components/PollCommandInput/RegisterAddressInput.vue'; | 42 | import RegisterAddressInput from '/@/views/task/center/components/PollCommandInput/RegisterAddressInput.vue'; |
43 | import ExtendDesc from '/@/components/Form/src/externalCompns/components/ExtendDesc/index.vue'; | 43 | import ExtendDesc from '/@/components/Form/src/externalCompns/components/ExtendDesc/index.vue'; |
44 | import DeviceProfileForm from '/@/components/Form/src/externalCompns/components/DeviceProfileForm/index.vue'; | 44 | import DeviceProfileForm from '/@/components/Form/src/externalCompns/components/DeviceProfileForm/index.vue'; |
45 | +import EnumList from './externalCompns/components/StructForm/EnumList.vue'; | ||
45 | 46 | ||
46 | const componentMap = new Map<ComponentType, Component>(); | 47 | const componentMap = new Map<ComponentType, Component>(); |
47 | 48 | ||
@@ -90,6 +91,7 @@ componentMap.set('ApiSelectScrollLoad', ApiSelectScrollLoad); | @@ -90,6 +91,7 @@ componentMap.set('ApiSelectScrollLoad', ApiSelectScrollLoad); | ||
90 | componentMap.set('InputGroup', InputGroup); | 91 | componentMap.set('InputGroup', InputGroup); |
91 | componentMap.set('RegisterAddressInput', RegisterAddressInput); | 92 | componentMap.set('RegisterAddressInput', RegisterAddressInput); |
92 | componentMap.set('ExtendDesc', ExtendDesc); | 93 | componentMap.set('ExtendDesc', ExtendDesc); |
94 | +componentMap.set('EnumList', EnumList); | ||
93 | componentMap.set('DeviceProfileForm', DeviceProfileForm); | 95 | componentMap.set('DeviceProfileForm', DeviceProfileForm); |
94 | 96 | ||
95 | export function add(compName: ComponentType, component: Component) { | 97 | export function add(compName: ComponentType, component: Component) { |
1 | +import { FormSchema } from '/@/components/Table'; | ||
2 | + | ||
3 | +export enum FormFieldsEnum { | ||
4 | + VALUE = 'value', | ||
5 | + NAME = 'name', | ||
6 | + DATA_TYPE = 'dataType', | ||
7 | +} | ||
8 | + | ||
9 | +export const getFormSchemas = (): FormSchema[] => { | ||
10 | + return [ | ||
11 | + { | ||
12 | + field: FormFieldsEnum.VALUE, | ||
13 | + label: '', | ||
14 | + component: 'InputNumber', | ||
15 | + rules: [ | ||
16 | + { required: true, message: `支持整型,取值范围:-2147483648 ~ 2147483647`, type: 'number' }, | ||
17 | + ], | ||
18 | + componentProps: () => { | ||
19 | + return { | ||
20 | + placeholder: '编号如"0"', | ||
21 | + min: -2147483648, | ||
22 | + max: 2147483647, | ||
23 | + step: 1, | ||
24 | + precision: 0, | ||
25 | + }; | ||
26 | + }, | ||
27 | + colProps: { | ||
28 | + span: 11, | ||
29 | + }, | ||
30 | + }, | ||
31 | + { | ||
32 | + field: 'division', | ||
33 | + label: '', | ||
34 | + component: 'Input', | ||
35 | + slot: 'division', | ||
36 | + colProps: { | ||
37 | + span: 1, | ||
38 | + }, | ||
39 | + }, | ||
40 | + { | ||
41 | + field: FormFieldsEnum.NAME, | ||
42 | + label: '', | ||
43 | + component: 'Input', | ||
44 | + rules: [ | ||
45 | + { | ||
46 | + required: true, | ||
47 | + message: `支持中文、英文大小写、数字、下划线和短划线,必须以中文、英文或数字开头,不超过20个字符`, | ||
48 | + type: 'string', | ||
49 | + pattern: /^[a-zA-Z0-9\u4e00-\u9fa5a][\u4e00-\u9fa5a-zA-Z0-9_-]*$/, | ||
50 | + }, | ||
51 | + ], | ||
52 | + componentProps: () => { | ||
53 | + return { | ||
54 | + placeholder: '对该枚举项的描述', | ||
55 | + maxLength: 20, | ||
56 | + }; | ||
57 | + }, | ||
58 | + colProps: { | ||
59 | + span: 11, | ||
60 | + }, | ||
61 | + }, | ||
62 | + ]; | ||
63 | +}; |
1 | +<script setup lang="ts"> | ||
2 | + import { Button, Tooltip } from 'ant-design-vue'; | ||
3 | + import { computed, nextTick, ref, unref, watch } from 'vue'; | ||
4 | + import { useForm, BasicForm } from '/@/components/Form'; | ||
5 | + import { Specs } from '/@/api/device/model/modelOfMatterModel'; | ||
6 | + import { Icon } from '/@/components/Icon'; | ||
7 | + import { getFormSchemas } from './EnumList.config'; | ||
8 | + import { FormActionType } from '../../../types/form'; | ||
9 | + import { buildUUID } from '/@/utils/uuid'; | ||
10 | + import { DataTypeEnum } from '/@/enums/objectModelEnum'; | ||
11 | + import { isNullOrUnDef } from '/@/utils/is'; | ||
12 | + | ||
13 | + const props = defineProps<{ disabled?: boolean; value?: Specs[] }>(); | ||
14 | + | ||
15 | + interface EnumElItemType { | ||
16 | + uuid: string; | ||
17 | + formActionType?: FormActionType; | ||
18 | + dataSource?: Recordable; | ||
19 | + } | ||
20 | + | ||
21 | + const [registerForm] = useForm({ | ||
22 | + schemas: getFormSchemas(), | ||
23 | + showActionButtonGroup: false, | ||
24 | + layout: 'inline', | ||
25 | + }); | ||
26 | + | ||
27 | + const enumsListElRef = ref<EnumElItemType[]>([{ uuid: buildUUID() }]); | ||
28 | + | ||
29 | + const setFormActionType = (item: EnumElItemType, el: any) => { | ||
30 | + item.formActionType = el as unknown as FormActionType; | ||
31 | + }; | ||
32 | + | ||
33 | + const getEnumsLimit = computed(() => unref(enumsListElRef).length >= 100); | ||
34 | + | ||
35 | + const hasSameEnum = ref(false); | ||
36 | + | ||
37 | + const validateSameEnum = () => { | ||
38 | + const value = getFieldsValue(); | ||
39 | + hasSameEnum.value = false; | ||
40 | + const values = value.map((item) => item.value).filter((value) => !isNullOrUnDef(value)); | ||
41 | + const names = value.map((item) => item.name).filter((value) => !isNullOrUnDef(value)); | ||
42 | + | ||
43 | + if (values.length !== new Set(values).size || names.length !== new Set(names).size) { | ||
44 | + hasSameEnum.value = true; | ||
45 | + } | ||
46 | + }; | ||
47 | + | ||
48 | + const validate = async () => { | ||
49 | + if (unref(hasSameEnum)) throw Error('存在相同的枚举'); | ||
50 | + for (const enumElItem of unref(enumsListElRef)) { | ||
51 | + await enumElItem.formActionType?.validate?.(); | ||
52 | + } | ||
53 | + validateSameEnum(); | ||
54 | + }; | ||
55 | + | ||
56 | + const getFieldsValue = () => { | ||
57 | + return unref(enumsListElRef).map( | ||
58 | + (item) => | ||
59 | + ({ | ||
60 | + ...(item.formActionType?.getFieldsValue?.() || {}), | ||
61 | + dataType: DataTypeEnum.ENUM, | ||
62 | + } as Specs) | ||
63 | + ); | ||
64 | + }; | ||
65 | + | ||
66 | + const setFieldsValue = (spaceList: Specs[]) => { | ||
67 | + enumsListElRef.value = spaceList.map((item) => ({ uuid: buildUUID(), dataSource: item })); | ||
68 | + | ||
69 | + nextTick(() => { | ||
70 | + unref(enumsListElRef).forEach((item) => | ||
71 | + item.formActionType?.setFieldsValue?.(item.dataSource) | ||
72 | + ); | ||
73 | + }); | ||
74 | + }; | ||
75 | + | ||
76 | + const handleDeleteEnums = (item: EnumElItemType) => { | ||
77 | + const index = unref(enumsListElRef).findIndex((temp) => item.uuid === temp.uuid); | ||
78 | + | ||
79 | + if (~index) { | ||
80 | + enumsListElRef.value.splice(index, 1); | ||
81 | + validateSameEnum(); | ||
82 | + } | ||
83 | + }; | ||
84 | + | ||
85 | + const handleAddEnums = () => { | ||
86 | + unref(enumsListElRef).push({ uuid: buildUUID() }); | ||
87 | + }; | ||
88 | + | ||
89 | + watch( | ||
90 | + () => props.value, | ||
91 | + (target) => { | ||
92 | + setFieldsValue(target || [{} as Specs]); | ||
93 | + }, | ||
94 | + { | ||
95 | + immediate: true, | ||
96 | + } | ||
97 | + ); | ||
98 | + | ||
99 | + defineExpose({ | ||
100 | + validate, | ||
101 | + getFieldsValue, | ||
102 | + setFieldsValue, | ||
103 | + }); | ||
104 | +</script> | ||
105 | + | ||
106 | +<template> | ||
107 | + <section class="w-full"> | ||
108 | + <header class="flex h-8 items-center"> | ||
109 | + <div class="w-1/2"> | ||
110 | + <span>参考值</span> | ||
111 | + <Tooltip title="支持整型,取值范围:-2147483648 ~ 2147483647"> | ||
112 | + <Icon icon="ant-design:question-circle-outlined" class="cursor-pointer ml-1" /> | ||
113 | + </Tooltip> | ||
114 | + </div> | ||
115 | + <div class="w-1/2"> | ||
116 | + <span>参考描述</span> | ||
117 | + <Tooltip | ||
118 | + title="支持中文、英文大小写、数字、下划线和短划线,必须以中文、英文或数字开头,不超过20个字符" | ||
119 | + > | ||
120 | + <Icon icon="ant-design:question-circle-outlined" class="cursor-pointer ml-1" /> | ||
121 | + </Tooltip> | ||
122 | + </div> | ||
123 | + </header> | ||
124 | + <main class="w-full"> | ||
125 | + <section class="w-full flex" v-for="item in enumsListElRef" :key="item.uuid"> | ||
126 | + <BasicForm | ||
127 | + :ref="(el) => setFormActionType(item, el)" | ||
128 | + @register="registerForm" | ||
129 | + class="enums-form" | ||
130 | + :disabled="disabled" | ||
131 | + @field-value-change="validateSameEnum" | ||
132 | + > | ||
133 | + <template #division> | ||
134 | + <div>~</div> | ||
135 | + </template> | ||
136 | + </BasicForm> | ||
137 | + <Button | ||
138 | + type="link" | ||
139 | + class="relative -left-6" | ||
140 | + :disabled="disabled" | ||
141 | + @click="handleDeleteEnums(item)" | ||
142 | + > | ||
143 | + 删除 | ||
144 | + </Button> | ||
145 | + </section> | ||
146 | + </main> | ||
147 | + <div v-if="hasSameEnum" class="text-red-400">枚举项中存在相同的参数值或参数描述</div> | ||
148 | + <Tooltip title="枚举项最多创建 100 个"> | ||
149 | + <Button type="link" @click="handleAddEnums" :disabled="disabled || getEnumsLimit"> | ||
150 | + +添加枚举项 | ||
151 | + </Button> | ||
152 | + </Tooltip> | ||
153 | + </section> | ||
154 | +</template> | ||
155 | + | ||
156 | +<style scoped lang="less"> | ||
157 | + .enums-form { | ||
158 | + @apply w-full; | ||
159 | + | ||
160 | + > :deep(.ant-row) { | ||
161 | + @apply w-full; | ||
162 | + | ||
163 | + .ant-input-number { | ||
164 | + width: 100%; | ||
165 | + } | ||
166 | + } | ||
167 | + } | ||
168 | +</style> |
@@ -14,6 +14,8 @@ | @@ -14,6 +14,8 @@ | ||
14 | import { DataType, StructJSON } from '/@/api/device/model/modelOfMatterModel'; | 14 | import { DataType, StructJSON } from '/@/api/device/model/modelOfMatterModel'; |
15 | import { isArray } from '/@/utils/is'; | 15 | import { isArray } from '/@/utils/is'; |
16 | import { useMessage } from '/@/hooks/web/useMessage'; | 16 | import { useMessage } from '/@/hooks/web/useMessage'; |
17 | + import EnumList from './EnumList.vue'; | ||
18 | + import { DataTypeEnum } from '/@/enums/objectModelEnum'; | ||
17 | 19 | ||
18 | const modalReceiveRecord = ref<OpenModalParams>({ | 20 | const modalReceiveRecord = ref<OpenModalParams>({ |
19 | mode: OpenModalMode.CREATE, | 21 | mode: OpenModalMode.CREATE, |
@@ -26,6 +28,8 @@ | @@ -26,6 +28,8 @@ | ||
26 | hiddenAccessMode: boolean; | 28 | hiddenAccessMode: boolean; |
27 | }>(); | 29 | }>(); |
28 | 30 | ||
31 | + const enumListRef = ref<InstanceType<typeof EnumList>>(); | ||
32 | + | ||
29 | const emit = defineEmits(['register', 'submit']); | 33 | const emit = defineEmits(['register', 'submit']); |
30 | 34 | ||
31 | const { createMessage } = useMessage(); | 35 | const { createMessage } = useMessage(); |
@@ -53,13 +57,14 @@ | @@ -53,13 +57,14 @@ | ||
53 | modalReceiveRecord.value = record; | 57 | modalReceiveRecord.value = record; |
54 | const data = record.record || {}; | 58 | const data = record.record || {}; |
55 | const { dataType = {} } = data! as StructJSON; | 59 | const { dataType = {} } = data! as StructJSON; |
56 | - const { specs = {}, type } = dataType as DataType; | 60 | + const { specs = {}, type, specsList } = dataType as DataType; |
57 | 61 | ||
58 | if (record.record) { | 62 | if (record.record) { |
59 | const value = { | 63 | const value = { |
60 | type, | 64 | type, |
61 | ...data, | 65 | ...data, |
62 | ...(isArray(specs) ? { specs } : { ...specs }), | 66 | ...(isArray(specs) ? { specs } : { ...specs }), |
67 | + enumList: type === DataTypeEnum.ENUM ? specsList : [], | ||
63 | }; | 68 | }; |
64 | 69 | ||
65 | setFieldsValue(value); | 70 | setFieldsValue(value); |
@@ -74,7 +79,8 @@ | @@ -74,7 +79,8 @@ | ||
74 | const handleSubmit = async () => { | 79 | const handleSubmit = async () => { |
75 | try { | 80 | try { |
76 | const _value = await validate(); | 81 | const _value = await validate(); |
77 | - let structJSON = transfromToStructJSON(_value); | 82 | + await unref(enumListRef)?.validate?.(); |
83 | + let structJSON = transfromToStructJSON(_value, unref(enumListRef)?.getFieldsValue?.() || []); | ||
78 | const value = { | 84 | const value = { |
79 | ...structJSON, | 85 | ...structJSON, |
80 | ...(unref(modalReceiveRecord)?.record?.id | 86 | ...(unref(modalReceiveRecord)?.record?.id |
@@ -104,7 +110,11 @@ | @@ -104,7 +110,11 @@ | ||
104 | destroy-on-close | 110 | destroy-on-close |
105 | :show-ok-btn="!$props.disabled" | 111 | :show-ok-btn="!$props.disabled" |
106 | > | 112 | > |
107 | - <BasicForm @register="register" :schemas="getFormSchemas" /> | 113 | + <BasicForm @register="register" :schemas="getFormSchemas"> |
114 | + <template #EnumList="{ field, model }"> | ||
115 | + <EnumList ref="enumListRef" :value="model[field]" :disabled="disabled" /> | ||
116 | + </template> | ||
117 | + </BasicForm> | ||
108 | </BasicModal> | 118 | </BasicModal> |
109 | </template> | 119 | </template> |
110 | 120 |
@@ -37,7 +37,7 @@ const validateExcludeComma = (field: string, errorName: string): Rule[] => { | @@ -37,7 +37,7 @@ const validateExcludeComma = (field: string, errorName: string): Rule[] => { | ||
37 | validator: () => { | 37 | validator: () => { |
38 | const reg = /[,,]+/; | 38 | const reg = /[,,]+/; |
39 | if (reg.test(field)) { | 39 | if (reg.test(field)) { |
40 | - return Promise.reject(errorName); | 40 | + return Promise.reject(`${errorName}不能包含逗号`); |
41 | } | 41 | } |
42 | return Promise.resolve(); | 42 | return Promise.resolve(); |
43 | }, | 43 | }, |
@@ -64,7 +64,10 @@ export const formSchemas = ({ | @@ -64,7 +64,10 @@ export const formSchemas = ({ | ||
64 | placeholder: '请输入功能名称', | 64 | placeholder: '请输入功能名称', |
65 | }, | 65 | }, |
66 | dynamicRules: ({ values }) => { | 66 | dynamicRules: ({ values }) => { |
67 | - return validateExcludeComma(values[FormField.FUNCTION_NAME], '功能名称不能包含逗号'); | 67 | + return [ |
68 | + { required: true, message: '请输入功能名称' }, | ||
69 | + ...validateExcludeComma(values[FormField.FUNCTION_NAME], '功能名称'), | ||
70 | + ]; | ||
68 | }, | 71 | }, |
69 | }, | 72 | }, |
70 | { | 73 | { |
@@ -80,7 +83,10 @@ export const formSchemas = ({ | @@ -80,7 +83,10 @@ export const formSchemas = ({ | ||
80 | placeholder: '请输入标识符', | 83 | placeholder: '请输入标识符', |
81 | }, | 84 | }, |
82 | dynamicRules: ({ values }) => { | 85 | dynamicRules: ({ values }) => { |
83 | - return validateExcludeComma(values[FormField.IDENTIFIER], '标识符不能包含逗号'); | 86 | + return [ |
87 | + { required: true, message: '请输入标识符' }, | ||
88 | + ...validateExcludeComma(values[FormField.IDENTIFIER], '标识符'), | ||
89 | + ]; | ||
84 | }, | 90 | }, |
85 | }, | 91 | }, |
86 | { | 92 | { |
@@ -105,6 +111,19 @@ export const formSchemas = ({ | @@ -105,6 +111,19 @@ export const formSchemas = ({ | ||
105 | api: async (params: Recordable) => { | 111 | api: async (params: Recordable) => { |
106 | try { | 112 | try { |
107 | const record = await findDictItemByCode(params); | 113 | const record = await findDictItemByCode(params); |
114 | + | ||
115 | + if (isTcp) { | ||
116 | + // TCP 产品 属性可创建范围 | ||
117 | + return record.filter((item) => | ||
118 | + [ | ||
119 | + DataTypeEnum.BOOL, | ||
120 | + DataTypeEnum.NUMBER_DOUBLE, | ||
121 | + DataTypeEnum.NUMBER_INT, | ||
122 | + DataTypeEnum.STRING, | ||
123 | + ].includes(item.itemValue as DataTypeEnum) | ||
124 | + ); | ||
125 | + } | ||
126 | + | ||
108 | return hasStructForm | 127 | return hasStructForm |
109 | ? record.filter((item) => item.itemValue !== DataTypeEnum.STRUCT) | 128 | ? record.filter((item) => item.itemValue !== DataTypeEnum.STRUCT) |
110 | : record; | 129 | : record; |
@@ -127,6 +146,16 @@ export const formSchemas = ({ | @@ -127,6 +146,16 @@ export const formSchemas = ({ | ||
127 | }, | 146 | }, |
128 | }, | 147 | }, |
129 | { | 148 | { |
149 | + field: FormField.ENUM_LIST, | ||
150 | + component: 'Input', | ||
151 | + label: '枚举', | ||
152 | + ifShow: ({ values }) => values[FormField.TYPE] === DataTypeEnum.ENUM, | ||
153 | + slot: 'EnumList', | ||
154 | + colProps: { | ||
155 | + span: 24, | ||
156 | + }, | ||
157 | + }, | ||
158 | + { | ||
130 | field: FormField.VALUE_RANGE, | 159 | field: FormField.VALUE_RANGE, |
131 | label: '取值范围', | 160 | label: '取值范围', |
132 | component: 'CustomMinMaxInput', | 161 | component: 'CustomMinMaxInput', |
1 | import { cloneDeep } from 'lodash-es'; | 1 | import { cloneDeep } from 'lodash-es'; |
2 | import { StructFormValue } from './type'; | 2 | import { StructFormValue } from './type'; |
3 | -import { DataType, ModelOfMatterParams, StructJSON } from '/@/api/device/model/modelOfMatterModel'; | 3 | +import { |
4 | + DataType, | ||
5 | + ModelOfMatterParams, | ||
6 | + Specs, | ||
7 | + StructJSON, | ||
8 | +} from '/@/api/device/model/modelOfMatterModel'; | ||
4 | import { isArray } from '/@/utils/is'; | 9 | import { isArray } from '/@/utils/is'; |
5 | import { DataTypeEnum } from '/@/enums/objectModelEnum'; | 10 | import { DataTypeEnum } from '/@/enums/objectModelEnum'; |
6 | 11 | ||
7 | -export function transfromToStructJSON(value: StructFormValue): StructJSON { | 12 | +export function transfromToStructJSON(value: StructFormValue, enumList: Specs[] = []): StructJSON { |
8 | const { | 13 | const { |
9 | type, | 14 | type, |
10 | valueRange, | 15 | valueRange, |
@@ -55,6 +60,13 @@ export function transfromToStructJSON(value: StructFormValue): StructJSON { | @@ -55,6 +60,13 @@ export function transfromToStructJSON(value: StructFormValue): StructJSON { | ||
55 | }; | 60 | }; |
56 | break; | 61 | break; |
57 | 62 | ||
63 | + case DataTypeEnum.ENUM: | ||
64 | + dataType = { | ||
65 | + type, | ||
66 | + specsList: enumList, | ||
67 | + }; | ||
68 | + break; | ||
69 | + | ||
58 | case DataTypeEnum.STRUCT: | 70 | case DataTypeEnum.STRUCT: |
59 | dataType = { | 71 | dataType = { |
60 | type, | 72 | type, |
@@ -62,12 +74,13 @@ export function transfromToStructJSON(value: StructFormValue): StructJSON { | @@ -62,12 +74,13 @@ export function transfromToStructJSON(value: StructFormValue): StructJSON { | ||
62 | }; | 74 | }; |
63 | break; | 75 | break; |
64 | } | 76 | } |
65 | - return { ...basic, dataType }; | 77 | + return { ...basic, dataType } as StructJSON; |
66 | } | 78 | } |
67 | 79 | ||
68 | export const excludeIdInStructJSON = (struct: DataType) => { | 80 | export const excludeIdInStructJSON = (struct: DataType) => { |
69 | const _value = cloneDeep(struct); | 81 | const _value = cloneDeep(struct); |
70 | const { specs } = _value; | 82 | const { specs } = _value; |
83 | + if (!specs) return _value; | ||
71 | const list = [specs]; | 84 | const list = [specs]; |
72 | 85 | ||
73 | while (list.length) { | 86 | while (list.length) { |
@@ -77,10 +90,10 @@ export const excludeIdInStructJSON = (struct: DataType) => { | @@ -77,10 +90,10 @@ export const excludeIdInStructJSON = (struct: DataType) => { | ||
77 | if (temp.dataType?.specs) { | 90 | if (temp.dataType?.specs) { |
78 | list.push(temp.dataType.specs); | 91 | list.push(temp.dataType.specs); |
79 | } | 92 | } |
80 | - Reflect.deleteProperty(temp, 'id'); | 93 | + Reflect.has(temp, 'id') && Reflect.deleteProperty(temp, 'id'); |
81 | }); | 94 | }); |
82 | } else { | 95 | } else { |
83 | - Reflect.deleteProperty(item as Recordable, 'id'); | 96 | + Reflect.has(item as Recordable, 'id') && Reflect.deleteProperty(item as Recordable, 'id'); |
84 | } | 97 | } |
85 | list.shift(); | 98 | list.shift(); |
86 | } | 99 | } |
@@ -101,6 +101,26 @@ export const getFormSchemas = ({ | @@ -101,6 +101,26 @@ export const getFormSchemas = ({ | ||
101 | }; | 101 | }; |
102 | }; | 102 | }; |
103 | 103 | ||
104 | + const createEnumsSelect = ({ identifier, functionName, dataType }: StructJSON): FormSchema => { | ||
105 | + const { specsList } = dataType || {}; | ||
106 | + return { | ||
107 | + field: identifier, | ||
108 | + label: functionName!, | ||
109 | + component: 'Select', | ||
110 | + rules: [ | ||
111 | + { | ||
112 | + required, | ||
113 | + message: `${functionName}是必填项`, | ||
114 | + type: 'number', | ||
115 | + }, | ||
116 | + ], | ||
117 | + componentProps: { | ||
118 | + options: specsList?.map((item) => ({ label: item.name, value: item.value })), | ||
119 | + placeholder: `请选择${functionName}`, | ||
120 | + }, | ||
121 | + }; | ||
122 | + }; | ||
123 | + | ||
104 | const createStructJson = ({ identifier, functionName, dataType }: StructJSON): FormSchema => { | 124 | const createStructJson = ({ identifier, functionName, dataType }: StructJSON): FormSchema => { |
105 | return { | 125 | return { |
106 | field: identifier, | 126 | field: identifier, |
@@ -140,6 +160,7 @@ export const getFormSchemas = ({ | @@ -140,6 +160,7 @@ export const getFormSchemas = ({ | ||
140 | } | 160 | } |
141 | 161 | ||
142 | if (type === DataTypeEnum.BOOL) schemas.push(createSelect(item)); | 162 | if (type === DataTypeEnum.BOOL) schemas.push(createSelect(item)); |
163 | + else if (type === DataTypeEnum.ENUM) schemas.push(createEnumsSelect(item)); | ||
143 | else if (type === DataTypeEnum.NUMBER_INT) schemas.push(createInputNumber(item)); | 164 | else if (type === DataTypeEnum.NUMBER_INT) schemas.push(createInputNumber(item)); |
144 | else if (type === DataTypeEnum.NUMBER_DOUBLE) schemas.push(createInputNumber(item)); | 165 | else if (type === DataTypeEnum.NUMBER_DOUBLE) schemas.push(createInputNumber(item)); |
145 | else if (type === DataTypeEnum.STRING) schemas.push(createInput(item)); | 166 | else if (type === DataTypeEnum.STRING) schemas.push(createInput(item)); |
@@ -144,4 +144,5 @@ export type ComponentType = | @@ -144,4 +144,5 @@ export type ComponentType = | ||
144 | | 'TimeRangePicker' | 144 | | 'TimeRangePicker' |
145 | | 'TriggerDurationInput' | 145 | | 'TriggerDurationInput' |
146 | | 'AlarmProfileSelect' | 146 | | 'AlarmProfileSelect' |
147 | - | 'LockControlGroup'; | 147 | + | 'LockControlGroup' |
148 | + | 'EnumList'; |
@@ -4,12 +4,12 @@ | @@ -4,12 +4,12 @@ | ||
4 | <Tooltip v-if="action.tooltip" v-bind="getTooltip(action.tooltip)"> | 4 | <Tooltip v-if="action.tooltip" v-bind="getTooltip(action.tooltip)"> |
5 | <PopConfirmButton v-bind="action"> | 5 | <PopConfirmButton v-bind="action"> |
6 | <Icon :icon="action.icon" :class="{ 'mr-1': !!action.label }" v-if="action.icon" /> | 6 | <Icon :icon="action.icon" :class="{ 'mr-1': !!action.label }" v-if="action.icon" /> |
7 | - <template v-if="action.label">{{ action.label }}</template> | 7 | + <template v-if="action.label"><Label :label="action.label" /></template> |
8 | </PopConfirmButton> | 8 | </PopConfirmButton> |
9 | </Tooltip> | 9 | </Tooltip> |
10 | <PopConfirmButton v-else v-bind="action"> | 10 | <PopConfirmButton v-else v-bind="action"> |
11 | <Icon :icon="action.icon" :class="{ 'mr-1': !!action.label }" v-if="action.icon" /> | 11 | <Icon :icon="action.icon" :class="{ 'mr-1': !!action.label }" v-if="action.icon" /> |
12 | - <template v-if="action.label">{{ action.label }}</template> | 12 | + <template v-if="action.label"><Label :label="action.label" /> </template> |
13 | </PopConfirmButton> | 13 | </PopConfirmButton> |
14 | <Divider | 14 | <Divider |
15 | type="vertical" | 15 | type="vertical" |
@@ -36,7 +36,7 @@ | @@ -36,7 +36,7 @@ | ||
36 | </div> | 36 | </div> |
37 | </template> | 37 | </template> |
38 | <script lang="ts"> | 38 | <script lang="ts"> |
39 | - import { defineComponent, PropType, computed, toRaw, unref } from 'vue'; | 39 | + import { defineComponent, PropType, computed, toRaw, unref, VNode } from 'vue'; |
40 | // import { MoreOutlined } from '@ant-design/icons-vue'; | 40 | // import { MoreOutlined } from '@ant-design/icons-vue'; |
41 | import { Divider, Tooltip, TooltipProps } from 'ant-design-vue'; | 41 | import { Divider, Tooltip, TooltipProps } from 'ant-design-vue'; |
42 | import Icon from '/@/components/Icon/index'; | 42 | import Icon from '/@/components/Icon/index'; |
@@ -52,7 +52,14 @@ | @@ -52,7 +52,14 @@ | ||
52 | 52 | ||
53 | export default defineComponent({ | 53 | export default defineComponent({ |
54 | name: 'TableAction', | 54 | name: 'TableAction', |
55 | - components: { Icon, PopConfirmButton, Divider, Dropdown, Tooltip }, | 55 | + components: { |
56 | + Icon, | ||
57 | + PopConfirmButton, | ||
58 | + Divider, | ||
59 | + Dropdown, | ||
60 | + Tooltip, | ||
61 | + Label: (props: { label: VNode | string }) => props.label, | ||
62 | + }, | ||
56 | props: { | 63 | props: { |
57 | actions: { | 64 | actions: { |
58 | type: Array as PropType<ActionItem[]>, | 65 | type: Array as PropType<ActionItem[]>, |
1 | import { ButtonProps } from 'ant-design-vue/es/button/buttonTypes'; | 1 | import { ButtonProps } from 'ant-design-vue/es/button/buttonTypes'; |
2 | import { TooltipProps } from 'ant-design-vue/es/tooltip/Tooltip'; | 2 | import { TooltipProps } from 'ant-design-vue/es/tooltip/Tooltip'; |
3 | import { RoleEnum } from '/@/enums/roleEnum'; | 3 | import { RoleEnum } from '/@/enums/roleEnum'; |
4 | +import { VNode } from 'vue'; | ||
4 | export interface ActionItem extends ButtonProps { | 5 | export interface ActionItem extends ButtonProps { |
5 | onClick?: Fn; | 6 | onClick?: Fn; |
6 | - label?: string; | 7 | + label?: string | VNode; |
7 | color?: 'success' | 'error' | 'warning'; | 8 | color?: 'success' | 'error' | 'warning'; |
8 | icon?: string; | 9 | icon?: string; |
9 | popConfirm?: PopConfirm; | 10 | popConfirm?: PopConfirm; |
@@ -24,6 +24,9 @@ export enum DictEnum { | @@ -24,6 +24,9 @@ export enum DictEnum { | ||
24 | // 实体类型 规则节点 Filter originator types switch | 24 | // 实体类型 规则节点 Filter originator types switch |
25 | ORIGINATOR_TYPES = 'originator_types', | 25 | ORIGINATOR_TYPES = 'originator_types', |
26 | 26 | ||
27 | + // 流媒体平台 | ||
28 | + STREAMING_MEDIA_TYPE = 'streaming_media_type', | ||
29 | + | ||
27 | // 产品品类领域 | 30 | // 产品品类领域 |
28 | CATEGORY_FIELD = 'category_field', | 31 | CATEGORY_FIELD = 'category_field', |
29 | } | 32 | } |
@@ -16,6 +16,7 @@ export const PageEnum = { | @@ -16,6 +16,7 @@ export const PageEnum = { | ||
16 | DEVICE_LIST: '/device/list', | 16 | DEVICE_LIST: '/device/list', |
17 | 17 | ||
18 | SHARE_PAGE: '/share/:viewType/:id/:publicId', | 18 | SHARE_PAGE: '/share/:viewType/:id/:publicId', |
19 | + APP_PAGE: '/appPage/:boardId/:userId', | ||
19 | 20 | ||
20 | RULE_CHAIN_DETAIL: '/rule/chain/:id', | 21 | RULE_CHAIN_DETAIL: '/rule/chain/:id', |
21 | 22 |
@@ -2,7 +2,7 @@ import { RouteLocationNormalizedLoaded } from 'vue-router'; | @@ -2,7 +2,7 @@ import { RouteLocationNormalizedLoaded } from 'vue-router'; | ||
2 | 2 | ||
3 | const menuMap = new Map(); | 3 | const menuMap = new Map(); |
4 | 4 | ||
5 | -menuMap.set('/visual/board/detail/:boardId/:boardName/:organizationId?', '/visual/board'); | 5 | +menuMap.set('/visual/board/detail/:boardId/:boardName/:platform/:organizationId?', '/visual/board'); |
6 | menuMap.set('/rule/chain/:id', '/rule/chain'); | 6 | menuMap.set('/rule/chain/:id', '/rule/chain'); |
7 | 7 | ||
8 | export const useMenuActiveFix = (route: RouteLocationNormalizedLoaded) => { | 8 | export const useMenuActiveFix = (route: RouteLocationNormalizedLoaded) => { |
@@ -11,7 +11,8 @@ import { getAuthCache } from '/@/utils/auth'; | @@ -11,7 +11,8 @@ import { getAuthCache } from '/@/utils/auth'; | ||
11 | const LOGIN_PATH = PageEnum.BASE_LOGIN; | 11 | const LOGIN_PATH = PageEnum.BASE_LOGIN; |
12 | const ROOT_PATH = RootRoute.path; | 12 | const ROOT_PATH = RootRoute.path; |
13 | const SHARE_PATH = PageEnum.SHARE_PAGE; | 13 | const SHARE_PATH = PageEnum.SHARE_PAGE; |
14 | -const whitePathList: string[] = [LOGIN_PATH, SHARE_PATH]; | 14 | +const APP_PATH = PageEnum.APP_PAGE; |
15 | +const whitePathList: string[] = [LOGIN_PATH, SHARE_PATH, APP_PATH]; | ||
15 | // const userInfo1 = getAuthCache(USER_INFO_KEY); | 16 | // const userInfo1 = getAuthCache(USER_INFO_KEY); |
16 | // const userInfo = ref(userInfo1); | 17 | // const userInfo = ref(userInfo1); |
17 | 18 |
src/router/routes/appPage.ts
0 → 100644
1 | +import { AppRouteRecordRaw } from '../types'; | ||
2 | +import { PageEnum } from '/@/enums/pageEnum'; | ||
3 | + | ||
4 | +export const APP_PAGE_ROUTER: AppRouteRecordRaw = { | ||
5 | + path: PageEnum.APP_PAGE, | ||
6 | + name: 'appPage', | ||
7 | + component: () => import('/@/views/sys/appPage/index.vue'), | ||
8 | + meta: { | ||
9 | + title: '公开', | ||
10 | + hideBreadcrumb: true, | ||
11 | + hideChildrenInMenu: true, | ||
12 | + }, | ||
13 | +}; |
@@ -5,6 +5,7 @@ import { PageEnum } from '/@/enums/pageEnum'; | @@ -5,6 +5,7 @@ import { PageEnum } from '/@/enums/pageEnum'; | ||
5 | import { t } from '/@/hooks/web/useI18n'; | 5 | import { t } from '/@/hooks/web/useI18n'; |
6 | import { LAYOUT } from '../constant'; | 6 | import { LAYOUT } from '../constant'; |
7 | import { PUBLIC_PAGE_ROUTER } from './public'; | 7 | import { PUBLIC_PAGE_ROUTER } from './public'; |
8 | +import { APP_PAGE_ROUTER } from './appPage'; | ||
8 | 9 | ||
9 | const modules = import.meta.globEager('./modules/**/*.ts'); | 10 | const modules = import.meta.globEager('./modules/**/*.ts'); |
10 | const routeModuleList: AppRouteModule[] = []; | 11 | const routeModuleList: AppRouteModule[] = []; |
@@ -87,4 +88,5 @@ export const basicRoutes = [ | @@ -87,4 +88,5 @@ export const basicRoutes = [ | ||
87 | REDIRECT_ROUTE, | 88 | REDIRECT_ROUTE, |
88 | PAGE_NOT_FOUND_ROUTE, | 89 | PAGE_NOT_FOUND_ROUTE, |
89 | PUBLIC_PAGE_ROUTER, | 90 | PUBLIC_PAGE_ROUTER, |
91 | + APP_PAGE_ROUTER, | ||
90 | ]; | 92 | ]; |
@@ -12,7 +12,16 @@ | @@ -12,7 +12,16 @@ | ||
12 | <a-select | 12 | <a-select |
13 | placeholder="请选择流媒体配置" | 13 | placeholder="请选择流媒体配置" |
14 | v-model:value="model[field]" | 14 | v-model:value="model[field]" |
15 | - :options="streamConfigOptions.map((item) => ({ value: item.value, label: item.label }))" | 15 | + :options=" |
16 | + streamConfigOptions | ||
17 | + .filter((item) => item.type === model.videoType) | ||
18 | + .map((item) => ({ | ||
19 | + value: item.value, | ||
20 | + label: item.label, | ||
21 | + type: item.type, | ||
22 | + })) | ||
23 | + " | ||
24 | + @change="handleChange" | ||
16 | > | 25 | > |
17 | <template #dropdownRender="{ menuNode: menu }"> | 26 | <template #dropdownRender="{ menuNode: menu }"> |
18 | <v-nodes :vnodes="menu" /> | 27 | <v-nodes :vnodes="menu" /> |
@@ -31,7 +40,7 @@ | @@ -31,7 +40,7 @@ | ||
31 | <script lang="ts"> | 40 | <script lang="ts"> |
32 | import { defineComponent, ref, computed, unref, nextTick, onMounted } from 'vue'; | 41 | import { defineComponent, ref, computed, unref, nextTick, onMounted } from 'vue'; |
33 | import { BasicForm, useForm } from '/@/components/Form'; | 42 | import { BasicForm, useForm } from '/@/components/Form'; |
34 | - import { formSchema } from './config.data'; | 43 | + import { AccessMode, formSchema, VideoPlatformEnum } from './config.data'; |
35 | import { BasicDrawer, useDrawerInner } from '/@/components/Drawer'; | 44 | import { BasicDrawer, useDrawerInner } from '/@/components/Drawer'; |
36 | import { createOrEditCameraManage } from '/@/api/camera/cameraManager'; | 45 | import { createOrEditCameraManage } from '/@/api/camera/cameraManager'; |
37 | import { useMessage } from '/@/hooks/web/useMessage'; | 46 | import { useMessage } from '/@/hooks/web/useMessage'; |
@@ -70,6 +79,7 @@ | @@ -70,6 +79,7 @@ | ||
70 | return { | 79 | return { |
71 | label: m.host, | 80 | label: m.host, |
72 | value: m.id, | 81 | value: m.id, |
82 | + type: m.type, | ||
73 | }; | 83 | }; |
74 | }); | 84 | }); |
75 | }); | 85 | }); |
@@ -96,14 +106,26 @@ | @@ -96,14 +106,26 @@ | ||
96 | isUpdate.value = !!data?.isUpdate; | 106 | isUpdate.value = !!data?.isUpdate; |
97 | if (unref(isUpdate)) { | 107 | if (unref(isUpdate)) { |
98 | await nextTick(); | 108 | await nextTick(); |
109 | + const { record } = data || {}; | ||
99 | editId.value = data.record.id; | 110 | editId.value = data.record.id; |
111 | + | ||
100 | if (data.record.avatar) { | 112 | if (data.record.avatar) { |
101 | - setFieldsValue({ | ||
102 | - avatar: [{ uid: buildUUID(), name: 'name', url: data.record.avatar } as FileItem], | 113 | + Object.assign(record, { |
114 | + avatar: [{ uid: buildUUID(), name: 'name', url: record.avatar } as FileItem], | ||
103 | }); | 115 | }); |
104 | } | 116 | } |
105 | - const { ...params } = data.record; | ||
106 | - await setFieldsValue({ ...params }); | 117 | + |
118 | + if ( | ||
119 | + record?.accessMode === AccessMode.Streaming && | ||
120 | + record.videoPlatformDTO?.type === VideoPlatformEnum.FLUORITE | ||
121 | + ) { | ||
122 | + Object.assign(record, { | ||
123 | + articulation: record.streamType, | ||
124 | + videoFormat: record.playProtocol, | ||
125 | + }); | ||
126 | + } | ||
127 | + | ||
128 | + setFieldsValue({ ...record, videoType: record.videoPlatformDTO?.type }); | ||
107 | } else { | 129 | } else { |
108 | editId.value = ''; | 130 | editId.value = ''; |
109 | } | 131 | } |
@@ -128,6 +150,15 @@ | @@ -128,6 +150,15 @@ | ||
128 | } | 150 | } |
129 | let saveMessage = '添加成功'; | 151 | let saveMessage = '添加成功'; |
130 | let updateMessage = '修改成功'; | 152 | let updateMessage = '修改成功'; |
153 | + | ||
154 | + if ( | ||
155 | + values?.accessMode === AccessMode.Streaming && | ||
156 | + values.videoType === VideoPlatformEnum.FLUORITE | ||
157 | + ) { | ||
158 | + values.streamType = values.articulation; | ||
159 | + values.playProtocol = values.videoFormat; | ||
160 | + } | ||
161 | + | ||
131 | await createOrEditCameraManage(values); | 162 | await createOrEditCameraManage(values); |
132 | closeDrawer(); | 163 | closeDrawer(); |
133 | emit('success'); | 164 | emit('success'); |
@@ -139,6 +170,11 @@ | @@ -139,6 +170,11 @@ | ||
139 | } | 170 | } |
140 | } | 171 | } |
141 | 172 | ||
173 | + const handleChange = (e, options) => { | ||
174 | + //流媒体配置 | ||
175 | + setFieldsValue({ videoType: e ? options.type : null }); | ||
176 | + }; | ||
177 | + | ||
142 | return { | 178 | return { |
143 | getTitle, | 179 | getTitle, |
144 | registerDrawer, | 180 | registerDrawer, |
@@ -149,6 +185,7 @@ | @@ -149,6 +185,7 @@ | ||
149 | registerSteramingDrawer, | 185 | registerSteramingDrawer, |
150 | handleOpenStreamConfig, | 186 | handleOpenStreamConfig, |
151 | handleSuccess, | 187 | handleSuccess, |
188 | + handleChange, | ||
152 | }; | 189 | }; |
153 | }, | 190 | }, |
154 | }); | 191 | }); |
@@ -42,6 +42,8 @@ | @@ -42,6 +42,8 @@ | ||
42 | 42 | ||
43 | const withToken = ref(false); | 43 | const withToken = ref(false); |
44 | 44 | ||
45 | + const videoId = ref<string>(); | ||
46 | + | ||
45 | const fingerprintResult = ref<Nullable<GetResult>>(null); | 47 | const fingerprintResult = ref<Nullable<GetResult>>(null); |
46 | 48 | ||
47 | const options = reactive<VideoJsPlayerOptions>({ | 49 | const options = reactive<VideoJsPlayerOptions>({ |
@@ -64,6 +66,7 @@ | @@ -64,6 +66,7 @@ | ||
64 | const [register] = useModalInner( | 66 | const [register] = useModalInner( |
65 | async (data: { record: CameraModel | StreamingManageRecord }) => { | 67 | async (data: { record: CameraModel | StreamingManageRecord }) => { |
66 | const { record } = data; | 68 | const { record } = data; |
69 | + videoId.value = record.id || ''; | ||
67 | const result = await getResult(); | 70 | const result = await getResult(); |
68 | fingerprintResult.value = result; | 71 | fingerprintResult.value = result; |
69 | if (record.accessMode === AccessMode.ManuallyEnter) { | 72 | if (record.accessMode === AccessMode.ManuallyEnter) { |
src/views/camera/manage/SnHelpMessage1.vue
0 → 100644
1 | +<script lang="ts" setup> | ||
2 | + import { BasicHelp } from '/@/components/Basic'; | ||
3 | + import { createImgPreview } from '/@/components/Preview/index'; | ||
4 | + import snStep4 from '/@/assets/images/sn-step4.png'; | ||
5 | + const imgList: string[] = [snStep4]; | ||
6 | + function handlePreview() { | ||
7 | + createImgPreview({ imageList: imgList, defaultWidth: 1000 }); | ||
8 | + } | ||
9 | +</script> | ||
10 | + | ||
11 | +<template> | ||
12 | + <div>监控点编号</div> | ||
13 | + <BasicHelp | ||
14 | + placement="top" | ||
15 | + @click="handlePreview" | ||
16 | + class="mx-1" | ||
17 | + text='点击查看如何获取"监控点编号"' | ||
18 | + /> | ||
19 | +</template> |
@@ -306,7 +306,7 @@ | @@ -306,7 +306,7 @@ | ||
306 | @on-unmounted="handleCloseFlvPlayUrl(item)" | 306 | @on-unmounted="handleCloseFlvPlayUrl(item)" |
307 | /> | 307 | /> |
308 | <div | 308 | <div |
309 | - class="video-container-mask absolute top-0 left-0 z-50 text-lg w-full text-light-50 flex justify-center items-center" | 309 | + class="video-container-mask absolute top-0 left-0 z-50 text-lg w-full text-light-50 flex justify-center" |
310 | style="height: 100%; background-color: rgba(0, 0, 0, 0.5)" | 310 | style="height: 100%; background-color: rgba(0, 0, 0, 0.5)" |
311 | > | 311 | > |
312 | <span>{{ item.name }}</span> | 312 | <span>{{ item.name }}</span> |
@@ -4,10 +4,13 @@ import { FormSchema as QFormSchema, useComponentRegister } from '/@/components/F | @@ -4,10 +4,13 @@ import { FormSchema as QFormSchema, useComponentRegister } from '/@/components/F | ||
4 | import { CameraVideoUrl, CameraMaxLength } from '/@/utils/rules'; | 4 | import { CameraVideoUrl, CameraMaxLength } from '/@/utils/rules'; |
5 | import { h } from 'vue'; | 5 | import { h } from 'vue'; |
6 | import SnHelpMessage from './SnHelpMessage.vue'; | 6 | import SnHelpMessage from './SnHelpMessage.vue'; |
7 | +import SnHelpMessage1 from './SnHelpMessage1.vue'; | ||
7 | import { OrgTreeSelect } from '../../common/OrgTreeSelect'; | 8 | import { OrgTreeSelect } from '../../common/OrgTreeSelect'; |
8 | import { FileItem } from '/@/components/Form/src/components/ApiUpload.vue'; | 9 | import { FileItem } from '/@/components/Form/src/components/ApiUpload.vue'; |
9 | import { createImgPreview } from '/@/components/Preview'; | 10 | import { createImgPreview } from '/@/components/Preview'; |
10 | import { uploadThumbnail } from '/@/api/configuration/center/configurationCenter'; | 11 | import { uploadThumbnail } from '/@/api/configuration/center/configurationCenter'; |
12 | +import { findDictItemByCode } from '/@/api/system/dict'; | ||
13 | +import { DictEnum } from '/@/enums/dictEnum'; | ||
11 | 14 | ||
12 | useComponentRegister('OrgTreeSelect', OrgTreeSelect); | 15 | useComponentRegister('OrgTreeSelect', OrgTreeSelect); |
13 | 16 | ||
@@ -45,6 +48,19 @@ export enum MediaType { | @@ -45,6 +48,19 @@ export enum MediaType { | ||
45 | M3U8 = 'm3u8', | 48 | M3U8 = 'm3u8', |
46 | } | 49 | } |
47 | 50 | ||
51 | +export enum FluoriteMideaProtocolEnum { | ||
52 | + HLS = 2, | ||
53 | + FLV = 4, | ||
54 | +} | ||
55 | + | ||
56 | +export enum ArticulationEnumType { | ||
57 | + HIGH_DEFINITION = 1, | ||
58 | + SMOOTH = 2, | ||
59 | +} | ||
60 | +export enum ArticulationEnumNameType { | ||
61 | + HIGH_DEFINITION = '高清', | ||
62 | + SMOOTH = '流畅', | ||
63 | +} | ||
48 | // 表格列数据 | 64 | // 表格列数据 |
49 | export const columns: BasicColumn[] = [ | 65 | export const columns: BasicColumn[] = [ |
50 | { | 66 | { |
@@ -100,6 +116,13 @@ export const searchFormSchema: FormSchema[] = [ | @@ -100,6 +116,13 @@ export const searchFormSchema: FormSchema[] = [ | ||
100 | }, | 116 | }, |
101 | ]; | 117 | ]; |
102 | 118 | ||
119 | +export enum VideoPlatformEnum { | ||
120 | + // 海康 | ||
121 | + SCI = 0, | ||
122 | + // 萤石云 | ||
123 | + FLUORITE = 1, | ||
124 | +} | ||
125 | + | ||
103 | // 弹框配置项 | 126 | // 弹框配置项 |
104 | export const formSchema: QFormSchema[] = [ | 127 | export const formSchema: QFormSchema[] = [ |
105 | { | 128 | { |
@@ -215,7 +238,25 @@ export const formSchema: QFormSchema[] = [ | @@ -215,7 +238,25 @@ export const formSchema: QFormSchema[] = [ | ||
215 | }, | 238 | }, |
216 | rules: [{ required: true, message: '视频流是必填项' }, ...CameraVideoUrl], | 239 | rules: [{ required: true, message: '视频流是必填项' }, ...CameraVideoUrl], |
217 | }, | 240 | }, |
218 | - | 241 | + { |
242 | + field: 'videoType', | ||
243 | + label: '流媒体平台', | ||
244 | + component: 'ApiRadioGroup', | ||
245 | + required: true, | ||
246 | + defaultValue: VideoPlatformEnum.SCI, | ||
247 | + ifShow: ({ values }) => values.accessMode === AccessMode.Streaming, | ||
248 | + componentProps: { | ||
249 | + api: async (params) => { | ||
250 | + const values = await findDictItemByCode(params); | ||
251 | + return values.map((item) => ({ label: item.itemText, value: Number(item.itemValue) })); | ||
252 | + }, | ||
253 | + params: { | ||
254 | + dictCode: DictEnum.STREAMING_MEDIA_TYPE, | ||
255 | + }, | ||
256 | + getPopupContainer: () => document.body, | ||
257 | + placeholder: `请选择平台类型`, | ||
258 | + }, | ||
259 | + }, | ||
219 | { | 260 | { |
220 | field: 'videoPlatformId', | 261 | field: 'videoPlatformId', |
221 | label: '流媒体配置', | 262 | label: '流媒体配置', |
@@ -234,7 +275,10 @@ export const formSchema: QFormSchema[] = [ | @@ -234,7 +275,10 @@ export const formSchema: QFormSchema[] = [ | ||
234 | component: 'RadioGroup', | 275 | component: 'RadioGroup', |
235 | defaultValue: StreamType.MASTER, | 276 | defaultValue: StreamType.MASTER, |
236 | ifShow({ values }) { | 277 | ifShow({ values }) { |
237 | - return values.accessMode === AccessMode.Streaming; | 278 | + return ( |
279 | + values.accessMode === AccessMode.Streaming && | ||
280 | + values.videoType !== VideoPlatformEnum.FLUORITE | ||
281 | + ); | ||
238 | }, | 282 | }, |
239 | componentProps: { | 283 | componentProps: { |
240 | placeholder: '请选择码流', | 284 | placeholder: '请选择码流', |
@@ -247,12 +291,53 @@ export const formSchema: QFormSchema[] = [ | @@ -247,12 +291,53 @@ export const formSchema: QFormSchema[] = [ | ||
247 | }, | 291 | }, |
248 | }, | 292 | }, |
249 | { | 293 | { |
294 | + field: 'articulation', | ||
295 | + label: '清晰度', | ||
296 | + component: 'RadioGroup', | ||
297 | + defaultValue: ArticulationEnumType.HIGH_DEFINITION, | ||
298 | + ifShow: ({ model }) => | ||
299 | + model.accessMode === AccessMode.Streaming && model.videoType === VideoPlatformEnum.FLUORITE, | ||
300 | + componentProps: () => { | ||
301 | + return { | ||
302 | + options: [ | ||
303 | + { | ||
304 | + label: ArticulationEnumNameType.HIGH_DEFINITION, | ||
305 | + value: ArticulationEnumType.HIGH_DEFINITION, | ||
306 | + }, | ||
307 | + { | ||
308 | + label: ArticulationEnumNameType.SMOOTH, | ||
309 | + value: ArticulationEnumType.SMOOTH, | ||
310 | + }, | ||
311 | + ], | ||
312 | + }; | ||
313 | + }, | ||
314 | + }, | ||
315 | + { | ||
316 | + field: 'videoFormat', | ||
317 | + label: '视频格式', | ||
318 | + component: 'Select', | ||
319 | + ifShow: ({ model }) => | ||
320 | + model.accessMode === AccessMode.Streaming && model.videoType === VideoPlatformEnum.FLUORITE, | ||
321 | + defaultValue: FluoriteMideaProtocolEnum.FLV, | ||
322 | + required: true, | ||
323 | + componentProps: { | ||
324 | + options: [ | ||
325 | + { label: 'FLV', value: FluoriteMideaProtocolEnum.FLV }, | ||
326 | + { label: 'HLS', value: FluoriteMideaProtocolEnum.HLS }, | ||
327 | + ], | ||
328 | + allowClear: false, | ||
329 | + }, | ||
330 | + }, | ||
331 | + { | ||
250 | field: 'playProtocol', | 332 | field: 'playProtocol', |
251 | label: '播放协议', | 333 | label: '播放协议', |
252 | component: 'RadioGroup', | 334 | component: 'RadioGroup', |
253 | defaultValue: PlayProtocol.HTTP, | 335 | defaultValue: PlayProtocol.HTTP, |
254 | ifShow({ values }) { | 336 | ifShow({ values }) { |
255 | - return values.accessMode === AccessMode.Streaming; | 337 | + return ( |
338 | + values.accessMode === AccessMode.Streaming && | ||
339 | + values.videoType !== VideoPlatformEnum.FLUORITE | ||
340 | + ); | ||
256 | }, | 341 | }, |
257 | helpMessage: ['平台使用https的hls协议,需联系海康开放平台专家支持。'], | 342 | helpMessage: ['平台使用https的hls协议,需联系海康开放平台专家支持。'], |
258 | componentProps: { | 343 | componentProps: { |
@@ -270,7 +355,25 @@ export const formSchema: QFormSchema[] = [ | @@ -270,7 +355,25 @@ export const formSchema: QFormSchema[] = [ | ||
270 | component: 'Input', | 355 | component: 'Input', |
271 | rules: [...CameraVideoUrl, { required: true, message: '摄像头编号是必填项' }], | 356 | rules: [...CameraVideoUrl, { required: true, message: '摄像头编号是必填项' }], |
272 | ifShow({ values }) { | 357 | ifShow({ values }) { |
273 | - return values.accessMode === AccessMode.Streaming; | 358 | + return ( |
359 | + values.accessMode === AccessMode.Streaming && | ||
360 | + values.videoType !== VideoPlatformEnum.FLUORITE | ||
361 | + ); | ||
362 | + }, | ||
363 | + componentProps: { | ||
364 | + placeholder: '请输入监控点编号', | ||
365 | + }, | ||
366 | + }, | ||
367 | + { | ||
368 | + field: 'sn', | ||
369 | + label: h(SnHelpMessage1) as any, | ||
370 | + component: 'Input', | ||
371 | + rules: [...CameraVideoUrl, { required: true, message: '摄像头编号是必填项' }], | ||
372 | + ifShow({ values }) { | ||
373 | + return ( | ||
374 | + values.accessMode === AccessMode.Streaming && | ||
375 | + values.videoType === VideoPlatformEnum.FLUORITE | ||
376 | + ); | ||
274 | }, | 377 | }, |
275 | componentProps: { | 378 | componentProps: { |
276 | placeholder: '请输入监控点编号', | 379 | placeholder: '请输入监控点编号', |
1 | import { PlayProtocol } from '../manage/config.data'; | 1 | import { PlayProtocol } from '../manage/config.data'; |
2 | import type { StreamingMediaModel } from '/@/api/camera/model/cameraModel'; | 2 | import type { StreamingMediaModel } from '/@/api/camera/model/cameraModel'; |
3 | +import { findDictItemByCode } from '/@/api/system/dict'; | ||
4 | +import { DictEnum } from '/@/enums/dictEnum'; | ||
3 | import { BasicColumn, FormSchema } from '/@/components/Table'; | 5 | import { BasicColumn, FormSchema } from '/@/components/Table'; |
4 | 6 | ||
5 | export interface DrawerParams { | 7 | export interface DrawerParams { |
@@ -9,6 +11,7 @@ export interface DrawerParams { | @@ -9,6 +11,7 @@ export interface DrawerParams { | ||
9 | 11 | ||
10 | export const streamingMediaTypeMapping = { | 12 | export const streamingMediaTypeMapping = { |
11 | 0: '海康ISC平台', | 13 | 0: '海康ISC平台', |
14 | + 1: '萤石平台', | ||
12 | }; | 15 | }; |
13 | 16 | ||
14 | export const streamingMediaSSLMapping = { | 17 | export const streamingMediaSSLMapping = { |
@@ -37,6 +40,9 @@ export const columnSchema: BasicColumn[] = [ | @@ -37,6 +40,9 @@ export const columnSchema: BasicColumn[] = [ | ||
37 | title: '用户Key', | 40 | title: '用户Key', |
38 | dataIndex: 'appKey', | 41 | dataIndex: 'appKey', |
39 | width: 80, | 42 | width: 80, |
43 | + format(text) { | ||
44 | + return formatSecret(text); | ||
45 | + }, | ||
40 | }, | 46 | }, |
41 | { | 47 | { |
42 | title: '用户密钥', | 48 | title: '用户密钥', |
@@ -73,26 +79,27 @@ export const formDetailSchema: FormSchema[] = [ | @@ -73,26 +79,27 @@ export const formDetailSchema: FormSchema[] = [ | ||
73 | { | 79 | { |
74 | label: '平台类型', | 80 | label: '平台类型', |
75 | field: 'type', | 81 | field: 'type', |
76 | - component: 'Select', | ||
77 | - rules: [{ required: true, message: '平台类型为必填项', type: 'number' }], | 82 | + component: 'ApiSelect', |
83 | + required: true, | ||
78 | componentProps: { | 84 | componentProps: { |
79 | - options: [{ label: '海康ISC平台', value: 0 }], | ||
80 | - placeholder: '请输入选择平台类型', | 85 | + api: async (params) => { |
86 | + const values = await findDictItemByCode(params); | ||
87 | + return values.map((item) => ({ label: item.itemText, value: Number(item.itemValue) })); | ||
88 | + }, | ||
89 | + params: { | ||
90 | + dictCode: DictEnum.STREAMING_MEDIA_TYPE, | ||
91 | + }, | ||
92 | + getPopupContainer: () => document.body, | ||
93 | + placeholder: `请选择平台类型`, | ||
81 | }, | 94 | }, |
82 | }, | 95 | }, |
83 | { | 96 | { |
84 | label: '部署环境', | 97 | label: '部署环境', |
85 | field: 'ssl', | 98 | field: 'ssl', |
86 | component: 'RadioGroup', | 99 | component: 'RadioGroup', |
100 | + ifShow: false, | ||
87 | rules: [{ required: true, message: '流媒体部署环境为必填项', type: 'number' }], | 101 | rules: [{ required: true, message: '流媒体部署环境为必填项', type: 'number' }], |
88 | - defaultValue: PlayProtocol.HTTP, | ||
89 | - componentProps: { | ||
90 | - defaultValue: PlayProtocol.HTTP, | ||
91 | - options: [ | ||
92 | - { label: 'http', value: PlayProtocol.HTTP }, | ||
93 | - { label: 'https', value: PlayProtocol.HTTPS }, | ||
94 | - ], | ||
95 | - }, | 102 | + defaultValue: PlayProtocol.HTTPS, |
96 | }, | 103 | }, |
97 | { | 104 | { |
98 | label: '平台地址', | 105 | label: '平台地址', |
@@ -108,7 +115,7 @@ export const formDetailSchema: FormSchema[] = [ | @@ -108,7 +115,7 @@ export const formDetailSchema: FormSchema[] = [ | ||
108 | { | 115 | { |
109 | label: '用户Key', | 116 | label: '用户Key', |
110 | field: 'appKey', | 117 | field: 'appKey', |
111 | - component: 'Input', | 118 | + component: 'InputPassword', |
112 | rules: [{ required: true, message: '用户Key为必填项' }], | 119 | rules: [{ required: true, message: '用户Key为必填项' }], |
113 | componentProps: { | 120 | componentProps: { |
114 | maxLength: 36, | 121 | maxLength: 36, |
@@ -118,7 +125,7 @@ export const formDetailSchema: FormSchema[] = [ | @@ -118,7 +125,7 @@ export const formDetailSchema: FormSchema[] = [ | ||
118 | { | 125 | { |
119 | label: '用户密钥', | 126 | label: '用户密钥', |
120 | field: 'appSecret', | 127 | field: 'appSecret', |
121 | - component: 'Input', | 128 | + component: 'InputPassword', |
122 | rules: [ | 129 | rules: [ |
123 | { required: true, message: '用户密钥为必填项' }, | 130 | { required: true, message: '用户密钥为必填项' }, |
124 | { required: true, min: 20, message: '用户密钥不能少于20位字符' }, | 131 | { required: true, min: 20, message: '用户密钥不能少于20位字符' }, |
@@ -14,7 +14,9 @@ | @@ -14,7 +14,9 @@ | ||
14 | v-model:value="model['templateId']" | 14 | v-model:value="model['templateId']" |
15 | placeholder="请选择模板" | 15 | placeholder="请选择模板" |
16 | style="width: 100%" | 16 | style="width: 100%" |
17 | - :options="selectTemplateOptions" | 17 | + :options=" |
18 | + selectTemplateOptions?.filter((item) => item.platform === model['platform']) || [] | ||
19 | + " | ||
18 | @change="handleTemplateChange" | 20 | @change="handleTemplateChange" |
19 | v-bind="createPickerSearch()" | 21 | v-bind="createPickerSearch()" |
20 | :disabled="templateDisabled" | 22 | :disabled="templateDisabled" |
@@ -140,6 +142,7 @@ | @@ -140,6 +142,7 @@ | ||
140 | }); | 142 | }); |
141 | 143 | ||
142 | const selectTemplateOptions: Ref<any[]> = ref([]); | 144 | const selectTemplateOptions: Ref<any[]> = ref([]); |
145 | + | ||
143 | const getTemplate = async (params: queryPageParams) => { | 146 | const getTemplate = async (params: queryPageParams) => { |
144 | const { items } = await getPage({ ...params, isTemplate: 1 }); | 147 | const { items } = await getPage({ ...params, isTemplate: 1 }); |
145 | selectTemplateOptions.value = items.map((item) => ({ | 148 | selectTemplateOptions.value = items.map((item) => ({ |
@@ -152,11 +155,7 @@ | @@ -152,11 +155,7 @@ | ||
152 | 155 | ||
153 | const handleTemplateChange = async (_, option) => { | 156 | const handleTemplateChange = async (_, option) => { |
154 | const { productAndDevice } = option; | 157 | const { productAndDevice } = option; |
155 | - // if (!productAndDevice) return; | ||
156 | - // selectOptions.value = productAndDevice?.map((item) => ({ | ||
157 | - // label: item.profileName || item.name, | ||
158 | - // value: item.profileId, | ||
159 | - // })); | 158 | + setFieldsValue({ platform: option?.platform }); |
160 | await nextTick(); | 159 | await nextTick(); |
161 | // 赋值 | 160 | // 赋值 |
162 | selectDeviceProfileRef.value?.setFieldsValue( | 161 | selectDeviceProfileRef.value?.setFieldsValue( |
@@ -144,6 +144,7 @@ export const formSchema: FormSchema[] = [ | @@ -144,6 +144,7 @@ export const formSchema: FormSchema[] = [ | ||
144 | { label: '移动端', value: Platform.PHONE }, | 144 | { label: '移动端', value: Platform.PHONE }, |
145 | ], | 145 | ], |
146 | }, | 146 | }, |
147 | + dynamicDisabled: ({ model }) => !!model?.enableTemplate, | ||
147 | }, | 148 | }, |
148 | { | 149 | { |
149 | field: 'enableTemplate', //前端控制 | 150 | field: 'enableTemplate', //前端控制 |
@@ -101,10 +101,6 @@ export const formSchema: FormSchema[] = [ | @@ -101,10 +101,6 @@ export const formSchema: FormSchema[] = [ | ||
101 | onPreview: (fileList: FileItem) => { | 101 | onPreview: (fileList: FileItem) => { |
102 | createImgPreview({ imageList: [fileList.url!] }); | 102 | createImgPreview({ imageList: [fileList.url!] }); |
103 | }, | 103 | }, |
104 | - // showUploadList: { | ||
105 | - // showDownloadIcon: true, | ||
106 | - // showRemoveIcon: true, | ||
107 | - // }, | ||
108 | }; | 104 | }; |
109 | }, | 105 | }, |
110 | }, | 106 | }, |
@@ -348,7 +348,7 @@ export const step1Schemas: FormSchema[] = [ | @@ -348,7 +348,7 @@ export const step1Schemas: FormSchema[] = [ | ||
348 | popconfirmTitle: () => | 348 | popconfirmTitle: () => |
349 | updateOrgHelpMessage.map((text) => h('div', { style: { maxWidth: '240px' } }, text)), | 349 | updateOrgHelpMessage.map((text) => h('div', { style: { maxWidth: '240px' } }, text)), |
350 | }), | 350 | }), |
351 | - componentProps: ({ formModel }) => { | 351 | + componentProps: ({ formModel, formActionType }) => { |
352 | return { | 352 | return { |
353 | component: 'OrgTreeSelect', | 353 | component: 'OrgTreeSelect', |
354 | defaultLockStatus: !!formModel?.isUpdate, | 354 | defaultLockStatus: !!formModel?.isUpdate, |
@@ -358,6 +358,17 @@ export const step1Schemas: FormSchema[] = [ | @@ -358,6 +358,17 @@ export const step1Schemas: FormSchema[] = [ | ||
358 | organizationId: formModel?.sensorOrganizationId, | 358 | organizationId: formModel?.sensorOrganizationId, |
359 | }, | 359 | }, |
360 | }, | 360 | }, |
361 | + onOptionsChange: (options: Recordable[]) => { | ||
362 | + if (!formModel?.organizationId && formModel?.deviceType === DeviceTypeEnum.SENSOR) { | ||
363 | + const firstItem = options?.[0]; | ||
364 | + | ||
365 | + if (firstItem && firstItem?.id) { | ||
366 | + const { setFieldsValue, clearValidate } = formActionType; | ||
367 | + setFieldsValue({ organizationId: firstItem.id }); | ||
368 | + clearValidate('organizationId'); | ||
369 | + } | ||
370 | + } | ||
371 | + }, | ||
361 | }, | 372 | }, |
362 | }; | 373 | }; |
363 | }, | 374 | }, |
@@ -70,6 +70,7 @@ export const columns: BasicColumn[] = [ | @@ -70,6 +70,7 @@ export const columns: BasicColumn[] = [ | ||
70 | title: '状态', | 70 | title: '状态', |
71 | dataIndex: 'deviceState', | 71 | dataIndex: 'deviceState', |
72 | width: 110, | 72 | width: 110, |
73 | + className: 'device-status', | ||
73 | slots: { customRender: 'deviceState' }, | 74 | slots: { customRender: 'deviceState' }, |
74 | }, | 75 | }, |
75 | { | 76 | { |
@@ -237,4 +238,17 @@ export const searchFormSchema: FormSchema[] = [ | @@ -237,4 +238,17 @@ export const searchFormSchema: FormSchema[] = [ | ||
237 | placeholder: '请选择', | 238 | placeholder: '请选择', |
238 | }, | 239 | }, |
239 | }, | 240 | }, |
241 | + { | ||
242 | + field: 'alarmStatus', | ||
243 | + label: '是否告警', | ||
244 | + component: 'Select', | ||
245 | + colProps: { span: 6 }, | ||
246 | + componentProps: { | ||
247 | + options: [ | ||
248 | + { label: '是', value: 1 }, | ||
249 | + { label: '否', value: 0 }, | ||
250 | + ], | ||
251 | + placeholder: '请选择设备是否存在告警', | ||
252 | + }, | ||
253 | + }, | ||
240 | ]; | 254 | ]; |
@@ -19,7 +19,20 @@ | @@ -19,7 +19,20 @@ | ||
19 | <Tabs.TabPane key="modelOfMatter" tab="物模型数据"> | 19 | <Tabs.TabPane key="modelOfMatter" tab="物模型数据"> |
20 | <ModelOfMatter :deviceDetail="deviceDetail" /> | 20 | <ModelOfMatter :deviceDetail="deviceDetail" /> |
21 | </Tabs.TabPane> | 21 | </Tabs.TabPane> |
22 | - <Tabs.TabPane key="3" tab="告警"> | 22 | + <Tabs.TabPane key="3"> |
23 | + <template #tab> | ||
24 | + <Badge :offset="[2, -5]" style="color: inherit"> | ||
25 | + <span>告警</span> | ||
26 | + <template #count> | ||
27 | + <div | ||
28 | + :style="{ visibility: deviceDetail.alarmStatus ? 'visible' : 'hidden' }" | ||
29 | + class="w-3.5 h-3.5 !flex justify-center items-center rounded-1 border-red-400 border" | ||
30 | + > | ||
31 | + <Icon icon="mdi:bell-warning" color="#f46161" :size="12" class="!mr-0" /> | ||
32 | + </div> | ||
33 | + </template> | ||
34 | + </Badge> | ||
35 | + </template> | ||
23 | <AlarmLog :device-id="deviceDetail.id" class="bg-gray-100" /> | 36 | <AlarmLog :device-id="deviceDetail.id" class="bg-gray-100" /> |
24 | </Tabs.TabPane> | 37 | </Tabs.TabPane> |
25 | <Tabs.TabPane key="4" tab="子设备" v-if="deviceDetail?.deviceType === 'GATEWAY'"> | 38 | <Tabs.TabPane key="4" tab="子设备" v-if="deviceDetail?.deviceType === 'GATEWAY'"> |
@@ -51,7 +64,7 @@ | @@ -51,7 +64,7 @@ | ||
51 | <script lang="ts" setup> | 64 | <script lang="ts" setup> |
52 | import { ref, computed } from 'vue'; | 65 | import { ref, computed } from 'vue'; |
53 | import { BasicDrawer, useDrawerInner } from '/@/components/Drawer'; | 66 | import { BasicDrawer, useDrawerInner } from '/@/components/Drawer'; |
54 | - import { Tabs } from 'ant-design-vue'; | 67 | + import { Tabs, Badge } from 'ant-design-vue'; |
55 | import Detail from '../tabs/Detail.vue'; | 68 | import Detail from '../tabs/Detail.vue'; |
56 | import ChildDevice from '../tabs/ChildDevice.vue'; | 69 | import ChildDevice from '../tabs/ChildDevice.vue'; |
57 | import TBoxDetail from '../tabs/TBoxDetail.vue'; | 70 | import TBoxDetail from '../tabs/TBoxDetail.vue'; |
@@ -62,6 +75,7 @@ | @@ -62,6 +75,7 @@ | ||
62 | import { DeviceRecord } from '/@/api/device/model/deviceModel'; | 75 | import { DeviceRecord } from '/@/api/device/model/deviceModel'; |
63 | import Task from '../tabs/Task.vue'; | 76 | import Task from '../tabs/Task.vue'; |
64 | import AlarmLog from '/@/views/alarm/log/index.vue'; | 77 | import AlarmLog from '/@/views/alarm/log/index.vue'; |
78 | + import { Icon } from '/@/components/Icon'; | ||
65 | 79 | ||
66 | const emit = defineEmits(['reload', 'register', 'openTbDeviceDetail', 'openGatewayDeviceDetail']); | 80 | const emit = defineEmits(['reload', 'register', 'openTbDeviceDetail', 'openGatewayDeviceDetail']); |
67 | 81 |
@@ -27,16 +27,26 @@ export interface SocketInfoDataSourceItemType extends BaseAdditionalInfo { | @@ -27,16 +27,26 @@ export interface SocketInfoDataSourceItemType extends BaseAdditionalInfo { | ||
27 | expand?: boolean; | 27 | expand?: boolean; |
28 | showHistoryDataButton?: boolean; | 28 | showHistoryDataButton?: boolean; |
29 | rawValue?: any; | 29 | rawValue?: any; |
30 | + enum?: Record<string, string>; | ||
30 | } | 31 | } |
31 | 32 | ||
32 | export function buildTableDataSourceByObjectModel( | 33 | export function buildTableDataSourceByObjectModel( |
33 | models: DeviceModelOfMatterAttrs[] | 34 | models: DeviceModelOfMatterAttrs[] |
34 | ): SocketInfoDataSourceItemType[] { | 35 | ): SocketInfoDataSourceItemType[] { |
35 | function getAdditionalInfoByDataType(dataType?: DataType) { | 36 | function getAdditionalInfoByDataType(dataType?: DataType) { |
36 | - const { specs } = dataType || {}; | 37 | + const { specs, specsList, type } = dataType || {}; |
37 | if (isArray(specs)) return {}; | 38 | if (isArray(specs)) return {}; |
38 | const { unit, boolClose, boolOpen, unitName } = (specs as Partial<Specs>) || {}; | 39 | const { unit, boolClose, boolOpen, unitName } = (specs as Partial<Specs>) || {}; |
39 | - return { unit, boolClose, boolOpen, unitName }; | 40 | + const result = { unit, boolClose, boolOpen, unitName }; |
41 | + if (type == DataTypeEnum.ENUM && specsList && specsList.length) { | ||
42 | + Reflect.set( | ||
43 | + result, | ||
44 | + 'enum', | ||
45 | + specsList.reduce((prev, next) => ({ ...prev, [next.value!]: next.name }), {}) | ||
46 | + ); | ||
47 | + } | ||
48 | + | ||
49 | + return result; | ||
40 | } | 50 | } |
41 | 51 | ||
42 | return models.map((item) => { | 52 | return models.map((item) => { |
@@ -72,7 +82,6 @@ export function buildTableDataSourceByObjectModel( | @@ -72,7 +82,6 @@ export function buildTableDataSourceByObjectModel( | ||
72 | } else { | 82 | } else { |
73 | Object.assign(res, getAdditionalInfoByDataType(dataType)); | 83 | Object.assign(res, getAdditionalInfoByDataType(dataType)); |
74 | } | 84 | } |
75 | - | ||
76 | return res; | 85 | return res; |
77 | }); | 86 | }); |
78 | } | 87 | } |
@@ -284,13 +284,15 @@ | @@ -284,13 +284,15 @@ | ||
284 | }); | 284 | }); |
285 | 285 | ||
286 | const formatValue = (item: SocketInfoDataSourceItemType) => { | 286 | const formatValue = (item: SocketInfoDataSourceItemType) => { |
287 | - return item.type === DataTypeEnum.BOOL | ||
288 | - ? !isNullOrUnDef(item.value) | ||
289 | - ? !!Number(item.value) | ||
290 | - ? item.boolOpen | ||
291 | - : item.boolClose | ||
292 | - : '--' | ||
293 | - : (item.value as string) || '--'; | 287 | + if (isNullOrUnDef(item)) return '--'; |
288 | + switch (item.type) { | ||
289 | + case DataTypeEnum.BOOL: | ||
290 | + return !!Number(item.value) ? item.boolOpen : item.boolClose; | ||
291 | + case DataTypeEnum.ENUM: | ||
292 | + return item.enum?.[item.value as string]; | ||
293 | + default: | ||
294 | + return item.value || '--'; | ||
295 | + } | ||
294 | }; | 296 | }; |
295 | 297 | ||
296 | const [register, { openModal: openSendCommandModal }] = useModal(); | 298 | const [register, { openModal: openSendCommandModal }] = useModal(); |
1 | +import { StructJSON } from '/@/api/device/model/modelOfMatterModel'; | ||
1 | import { FormSchema } from '/@/components/Form'; | 2 | import { FormSchema } from '/@/components/Form'; |
3 | +import { validateTCPCustomCommand } from '/@/components/Form/src/externalCompns/components/ThingsModelForm'; | ||
4 | +import { DataTypeEnum } from '/@/enums/objectModelEnum'; | ||
2 | 5 | ||
3 | const InsertString = (t, c, n) => { | 6 | const InsertString = (t, c, n) => { |
4 | const r: string | number[] = []; | 7 | const r: string | number[] = []; |
@@ -127,21 +130,33 @@ const SingleToHexBatch = (t) => { | @@ -127,21 +130,33 @@ const SingleToHexBatch = (t) => { | ||
127 | return r.join('\r\n'); | 130 | return r.join('\r\n'); |
128 | }; | 131 | }; |
129 | 132 | ||
130 | -const formSchemasConfig = (schemas, actionType): FormSchema[] => { | ||
131 | - const { identifier, functionName } = schemas; | 133 | +const formSchemasConfig = (schemas: StructJSON, actionType: string): FormSchema[] => { |
134 | + const { identifier, functionName, dataType } = schemas; | ||
135 | + | ||
136 | + if (dataType?.type === DataTypeEnum.STRING) { | ||
137 | + return [ | ||
138 | + { | ||
139 | + field: identifier, | ||
140 | + label: functionName!, | ||
141 | + component: 'Input', | ||
142 | + rules: [{ required: true, validator: validateTCPCustomCommand }], | ||
143 | + componentProps: { | ||
144 | + placeholder: `请输入${functionName}`, | ||
145 | + }, | ||
146 | + }, | ||
147 | + ]; | ||
148 | + } | ||
149 | + | ||
132 | if (actionType == '06') { | 150 | if (actionType == '06') { |
133 | return [ | 151 | return [ |
134 | { | 152 | { |
135 | field: identifier, | 153 | field: identifier, |
136 | - label: functionName, | 154 | + label: functionName!, |
137 | component: 'InputNumber', | 155 | component: 'InputNumber', |
138 | rules: [{ required: true, message: '请输入正数' }], | 156 | rules: [{ required: true, message: '请输入正数' }], |
139 | componentProps: { | 157 | componentProps: { |
140 | min: 0, | 158 | min: 0, |
141 | - formatter: (e) => { | ||
142 | - const value = `${e}`.replace('-', '').replace(/^(-)*(\d+)\.(\d\d).*$/, '$1$2.$3'); | ||
143 | - return value; | ||
144 | - }, | 159 | + precision: 2, |
145 | placeholder: `请输入正数`, | 160 | placeholder: `请输入正数`, |
146 | }, | 161 | }, |
147 | }, | 162 | }, |
@@ -150,7 +165,7 @@ const formSchemasConfig = (schemas, actionType): FormSchema[] => { | @@ -150,7 +165,7 @@ const formSchemasConfig = (schemas, actionType): FormSchema[] => { | ||
150 | return [ | 165 | return [ |
151 | { | 166 | { |
152 | field: identifier, | 167 | field: identifier, |
153 | - label: functionName, | 168 | + label: functionName!, |
154 | component: 'InputNumber', | 169 | component: 'InputNumber', |
155 | rules: [{ required: true, message: '请输入值' }], | 170 | rules: [{ required: true, message: '请输入值' }], |
156 | componentProps: { | 171 | componentProps: { |
@@ -165,13 +180,12 @@ const formSchemasConfig = (schemas, actionType): FormSchema[] => { | @@ -165,13 +180,12 @@ const formSchemasConfig = (schemas, actionType): FormSchema[] => { | ||
165 | return [ | 180 | return [ |
166 | { | 181 | { |
167 | field: identifier, | 182 | field: identifier, |
168 | - label: functionName, | 183 | + label: functionName!, |
169 | component: 'InputNumber', | 184 | component: 'InputNumber', |
170 | rules: [{ required: true, message: '请输入值' }], | 185 | rules: [{ required: true, message: '请输入值' }], |
171 | componentProps: { | 186 | componentProps: { |
172 | placeholder: `请输入数字`, | 187 | placeholder: `请输入数字`, |
173 | - formatter: (e) => | ||
174 | - `${e}`.replace(/\B(?=(\d{3})+(?!\d))/g, '').replace(/^(-)*(\d+)\.(\d\d).*$/, '$1$2.$3'), | 188 | + precision: 2, |
175 | }, | 189 | }, |
176 | }, | 190 | }, |
177 | ]; | 191 | ]; |
@@ -35,6 +35,7 @@ | @@ -35,6 +35,7 @@ | ||
35 | const zoomFactorValue = ref<number>(1); //缩放因子 | 35 | const zoomFactorValue = ref<number>(1); //缩放因子 |
36 | const isShowMultiply = ref<Boolean>(false); // 只有tcp --> int和double类型才相乘缩放因子 | 36 | const isShowMultiply = ref<Boolean>(false); // 只有tcp --> int和double类型才相乘缩放因子 |
37 | const deviceTransportType = ref<string>(); | 37 | const deviceTransportType = ref<string>(); |
38 | + const objectDataType = ref<DataTypeEnum>(); | ||
38 | 39 | ||
39 | const [register] = useModalInner(async (params: ModalParamsType<DeviceModelOfMatterAttrs>) => { | 40 | const [register] = useModalInner(async (params: ModalParamsType<DeviceModelOfMatterAttrs>) => { |
40 | const { record } = params; | 41 | const { record } = params; |
@@ -48,6 +49,7 @@ | @@ -48,6 +49,7 @@ | ||
48 | zoomFactorValue.value = zoomFactor ? Number(zoomFactor) : 1; | 49 | zoomFactorValue.value = zoomFactor ? Number(zoomFactor) : 1; |
49 | isShowMultiply.value = type == 'INT' || type == 'DOUBLE' ? true : false; | 50 | isShowMultiply.value = type == 'INT' || type == 'DOUBLE' ? true : false; |
50 | deviceTransportType.value = transportType; | 51 | deviceTransportType.value = transportType; |
52 | + objectDataType.value = type; | ||
51 | 53 | ||
52 | let schemas = [{ dataType: dataType, identifier, functionName: name } as StructJSON]; | 54 | let schemas = [{ dataType: dataType, identifier, functionName: name } as StructJSON]; |
53 | 55 | ||
@@ -136,7 +138,12 @@ | @@ -136,7 +138,12 @@ | ||
136 | 138 | ||
137 | const sendValue = ref({}); | 139 | const sendValue = ref({}); |
138 | //判断tcp类型 标识符是自定义还是ModBus | 140 | //判断tcp类型 标识符是自定义还是ModBus |
139 | - if (unref(isShowModBUS)) { | 141 | + if (unref(objectDataType) === DataTypeEnum.STRING) { |
142 | + const flag = await validate(); | ||
143 | + if (!flag) return; | ||
144 | + const value = getFieldsValue()[unref(formField)]; | ||
145 | + sendValue.value = value; | ||
146 | + } else if (unref(isShowModBUS)) { | ||
140 | if (!unref(isShowActionType)) { | 147 | if (!unref(isShowActionType)) { |
141 | createMessage.warning('当前物模型扩展描述没有填写'); | 148 | createMessage.warning('当前物模型扩展描述没有填写'); |
142 | return; | 149 | return; |
@@ -12,16 +12,9 @@ export interface BasicCreateFormParams { | @@ -12,16 +12,9 @@ export interface BasicCreateFormParams { | ||
12 | 12 | ||
13 | useComponentRegister('JSONEditor', JSONEditor); | 13 | useComponentRegister('JSONEditor', JSONEditor); |
14 | 14 | ||
15 | -const validateDouble = ( | ||
16 | - value: number, | ||
17 | - type: DataTypeEnum, | ||
18 | - min?: number | string, | ||
19 | - max?: number | string | ||
20 | -) => { | ||
21 | - min = | ||
22 | - Number(min) || type === DataTypeEnum.NUMBER_INT ? Number.MIN_SAFE_INTEGER : Number.MIN_VALUE; | ||
23 | - max = | ||
24 | - Number(max) || type === DataTypeEnum.NUMBER_INT ? Number.MAX_SAFE_INTEGER : Number.MAX_VALUE; | 15 | +const validateDouble = (value: number, min?: number | string, max?: number | string) => { |
16 | + min = Number(min) || Number.MIN_SAFE_INTEGER; | ||
17 | + max = Number(max) || Number.MAX_SAFE_INTEGER; | ||
25 | 18 | ||
26 | return { | 19 | return { |
27 | flag: value < min || value > max, | 20 | flag: value < min || value > max, |
@@ -47,7 +40,7 @@ export const useGenDynamicForm = () => { | @@ -47,7 +40,7 @@ export const useGenDynamicForm = () => { | ||
47 | type: 'number', | 40 | type: 'number', |
48 | trigger: 'change', | 41 | trigger: 'change', |
49 | validator: (_rule, value) => { | 42 | validator: (_rule, value) => { |
50 | - const { flag, message } = validateDouble(value, type, min, max); | 43 | + const { flag, message } = validateDouble(value, min, max); |
51 | if (flag) { | 44 | if (flag) { |
52 | return Promise.reject(`${functionName}${message}`); | 45 | return Promise.reject(`${functionName}${message}`); |
53 | } | 46 | } |
@@ -56,8 +49,8 @@ export const useGenDynamicForm = () => { | @@ -56,8 +49,8 @@ export const useGenDynamicForm = () => { | ||
56 | }, | 49 | }, |
57 | ], | 50 | ], |
58 | componentProps: { | 51 | componentProps: { |
59 | - max: max ?? type === DataTypeEnum.NUMBER_INT ? Number.MAX_SAFE_INTEGER : Number.MAX_VALUE, | ||
60 | - min: min ?? type === DataTypeEnum.NUMBER_INT ? Number.MIN_SAFE_INTEGER : Number.MIN_VALUE, | 52 | + max: max ?? Number.MAX_SAFE_INTEGER, |
53 | + min: min ?? Number.MIN_SAFE_INTEGER, | ||
61 | step, | 54 | step, |
62 | placeholder: `请输入${functionName}`, | 55 | placeholder: `请输入${functionName}`, |
63 | precision: type === DataTypeEnum.NUMBER_INT ? 0 : 2, | 56 | precision: type === DataTypeEnum.NUMBER_INT ? 0 : 2, |
@@ -81,7 +74,7 @@ export const useGenDynamicForm = () => { | @@ -81,7 +74,7 @@ export const useGenDynamicForm = () => { | ||
81 | type: 'string', | 74 | type: 'string', |
82 | trigger: 'change', | 75 | trigger: 'change', |
83 | validator: (_rule, value) => { | 76 | validator: (_rule, value) => { |
84 | - if (value.length > length) { | 77 | + if (value?.length > length) { |
85 | return Promise.reject(`${functionName}数据长度应该小于${length}`); | 78 | return Promise.reject(`${functionName}数据长度应该小于${length}`); |
86 | } | 79 | } |
87 | return Promise.resolve(value); | 80 | return Promise.resolve(value); |
@@ -117,6 +110,24 @@ export const useGenDynamicForm = () => { | @@ -117,6 +110,24 @@ export const useGenDynamicForm = () => { | ||
117 | }; | 110 | }; |
118 | }; | 111 | }; |
119 | 112 | ||
113 | + const createEnumSelect = ({ | ||
114 | + identifier, | ||
115 | + functionName, | ||
116 | + dataType, | ||
117 | + }: BasicCreateFormParams): FormSchema => { | ||
118 | + const { specsList } = dataType; | ||
119 | + return { | ||
120 | + field: identifier, | ||
121 | + label: functionName, | ||
122 | + component: 'Select', | ||
123 | + componentProps: { | ||
124 | + options: specsList?.map((item) => ({ label: item.name, value: item.value })), | ||
125 | + placeholder: `请选择${functionName}`, | ||
126 | + getPopupContainer: () => document.body, | ||
127 | + }, | ||
128 | + }; | ||
129 | + }; | ||
130 | + | ||
120 | const createInputJson = ({ identifier, functionName }: BasicCreateFormParams): FormSchema => { | 131 | const createInputJson = ({ identifier, functionName }: BasicCreateFormParams): FormSchema => { |
121 | return { | 132 | return { |
122 | field: identifier, | 133 | field: identifier, |
@@ -144,6 +155,7 @@ export const useGenDynamicForm = () => { | @@ -144,6 +155,7 @@ export const useGenDynamicForm = () => { | ||
144 | [DataTypeEnum.NUMBER_INT]: createInputNumber, | 155 | [DataTypeEnum.NUMBER_INT]: createInputNumber, |
145 | [DataTypeEnum.STRING]: createInput, | 156 | [DataTypeEnum.STRING]: createInput, |
146 | [DataTypeEnum.STRUCT]: createInputJson, | 157 | [DataTypeEnum.STRUCT]: createInputJson, |
158 | + [DataTypeEnum.ENUM]: createEnumSelect, | ||
147 | }; | 159 | }; |
148 | 160 | ||
149 | const fieldTypeMap = new Map<string, DataTypeEnum>(); | 161 | const fieldTypeMap = new Map<string, DataTypeEnum>(); |
@@ -39,6 +39,8 @@ | @@ -39,6 +39,8 @@ | ||
39 | 39 | ||
40 | const playUrl = ref(''); | 40 | const playUrl = ref(''); |
41 | 41 | ||
42 | + const videoId = ref<string>(); | ||
43 | + | ||
42 | const withToken = ref(false); | 44 | const withToken = ref(false); |
43 | 45 | ||
44 | const fingerprintResult = ref<Nullable<GetResult>>(null); | 46 | const fingerprintResult = ref<Nullable<GetResult>>(null); |
@@ -64,6 +66,7 @@ | @@ -64,6 +66,7 @@ | ||
64 | const [register] = useModalInner( | 66 | const [register] = useModalInner( |
65 | async (data: { record: CameraModel | StreamingManageRecord }) => { | 67 | async (data: { record: CameraModel | StreamingManageRecord }) => { |
66 | const { record } = data; | 68 | const { record } = data; |
69 | + videoId.value = record.id || ''; | ||
67 | const result = await getResult(); | 70 | const result = await getResult(); |
68 | fingerprintResult.value = result; | 71 | fingerprintResult.value = result; |
69 | if (record.accessMode === AccessMode.ManuallyEnter) { | 72 | if (record.accessMode === AccessMode.ManuallyEnter) { |
@@ -2,7 +2,7 @@ | @@ -2,7 +2,7 @@ | ||
2 | <div> | 2 | <div> |
3 | <PageWrapper dense contentFullHeight contentClass="flex"> | 3 | <PageWrapper dense contentFullHeight contentClass="flex"> |
4 | <OrganizationIdTree @select="handleSelect" ref="organizationIdTreeRef" /> | 4 | <OrganizationIdTree @select="handleSelect" ref="organizationIdTreeRef" /> |
5 | - <BasicTable style="flex: auto" @register="registerTable" class="w-5/6 xl:w-4/5"> | 5 | + <BasicTable style="flex: auto" @register="registerTable" class="w-5/6 xl:w-4/5 device-table"> |
6 | <template #toolbar> | 6 | <template #toolbar> |
7 | <Authority :value="DeviceListAuthEnum.CREATE"> | 7 | <Authority :value="DeviceListAuthEnum.CREATE"> |
8 | <a-button type="primary" @click="handleCreate" v-if="authBtn(role)"> | 8 | <a-button type="primary" @click="handleCreate" v-if="authBtn(role)"> |
@@ -106,13 +106,17 @@ | @@ -106,13 +106,17 @@ | ||
106 | </Tag> | 106 | </Tag> |
107 | </template> | 107 | </template> |
108 | <template #deviceState="{ record }"> | 108 | <template #deviceState="{ record }"> |
109 | - <!-- <HeartOutlined v-if="!record.isCollect" class="mr-1" style="color: red" /> --> | ||
110 | - <Tooltip> | ||
111 | - <template #title> 我的收藏</template> | ||
112 | - <HeartTwoTone v-if="record.isCollect" class="mr-1" twoToneColor="#3B82F6" /> | ||
113 | - </Tooltip> | 109 | + <div v-if="record.isCollect"> |
110 | + <div class="absolute top-0 left-0 device-collect"> </div> | ||
111 | + <Icon | ||
112 | + icon="ph:star-fill" | ||
113 | + class="fill-light-50 absolute top-0.5 left-0.5" | ||
114 | + color="#fff" | ||
115 | + :size="12" | ||
116 | + /> | ||
117 | + </div> | ||
118 | + | ||
114 | <Tag | 119 | <Tag |
115 | - :style="{ marginLeft: !record.isCollect ? '17px' : '' }" | ||
116 | :color=" | 120 | :color=" |
117 | record.deviceState == DeviceState.INACTIVE | 121 | record.deviceState == DeviceState.INACTIVE |
118 | ? 'warning' | 122 | ? 'warning' |
@@ -135,7 +139,7 @@ | @@ -135,7 +139,7 @@ | ||
135 | <TableAction | 139 | <TableAction |
136 | :actions="[ | 140 | :actions="[ |
137 | { | 141 | { |
138 | - label: '详情', | 142 | + label: AlarmDetailActionButton({ hasAlarm: !!record.alarmStatus }), |
139 | icon: 'ant-design:eye-outlined', | 143 | icon: 'ant-design:eye-outlined', |
140 | auth: DeviceListAuthEnum.DETAIL, | 144 | auth: DeviceListAuthEnum.DETAIL, |
141 | onClick: handleDetail.bind(null, record), | 145 | onClick: handleDetail.bind(null, record), |
@@ -232,7 +236,7 @@ | @@ -232,7 +236,7 @@ | ||
232 | </div> | 236 | </div> |
233 | </template> | 237 | </template> |
234 | <script lang="ts" setup> | 238 | <script lang="ts" setup> |
235 | - import { reactive, onMounted, ref } from 'vue'; | 239 | + import { reactive, onMounted, ref, h, CSSProperties } from 'vue'; |
236 | import { | 240 | import { |
237 | DeviceModel, | 241 | DeviceModel, |
238 | DeviceRecord, | 242 | DeviceRecord, |
@@ -241,8 +245,7 @@ | @@ -241,8 +245,7 @@ | ||
241 | } from '/@/api/device/model/deviceModel'; | 245 | } from '/@/api/device/model/deviceModel'; |
242 | import { BasicTable, useTable, TableAction, TableImg } from '/@/components/Table'; | 246 | import { BasicTable, useTable, TableAction, TableImg } from '/@/components/Table'; |
243 | import { columns, DeviceListAuthEnum, searchFormSchema } from './config/device.data'; | 247 | import { columns, DeviceListAuthEnum, searchFormSchema } from './config/device.data'; |
244 | - import { Tag, Popover, Button, Tooltip } from 'ant-design-vue'; | ||
245 | - import { HeartTwoTone } from '@ant-design/icons-vue'; | 248 | + import { Tag, Popover, Button, Badge } from 'ant-design-vue'; |
246 | import { | 249 | import { |
247 | deleteDevice, | 250 | deleteDevice, |
248 | devicePage, | 251 | devicePage, |
@@ -278,6 +281,7 @@ | @@ -278,6 +281,7 @@ | ||
278 | } from './cpns/modal/BatchUpdateProductModal'; | 281 | } from './cpns/modal/BatchUpdateProductModal'; |
279 | import { DataActionModeEnum } from '/@/enums/toolEnum'; | 282 | import { DataActionModeEnum } from '/@/enums/toolEnum'; |
280 | import { AuthDropDown } from '/@/components/Widget'; | 283 | import { AuthDropDown } from '/@/components/Widget'; |
284 | + import Icon from '/@/components/Icon'; | ||
281 | 285 | ||
282 | const { isCustomer } = useAuthDeviceDetail(); | 286 | const { isCustomer } = useAuthDeviceDetail(); |
283 | const { createMessage } = useMessage(); | 287 | const { createMessage } = useMessage(); |
@@ -294,6 +298,32 @@ | @@ -294,6 +298,32 @@ | ||
294 | const [registerImportModal, { openModal: openImportModal }] = useModal(); | 298 | const [registerImportModal, { openModal: openImportModal }] = useModal(); |
295 | const [registerBatchUpdateProductModal, { openModal: openBatchUpdateProductModal }] = useModal(); | 299 | const [registerBatchUpdateProductModal, { openModal: openBatchUpdateProductModal }] = useModal(); |
296 | 300 | ||
301 | + const AlarmDetailActionButton = ({ hasAlarm }: { hasAlarm?: boolean }) => | ||
302 | + h( | ||
303 | + Badge, | ||
304 | + { offset: [0, -5] }, | ||
305 | + { | ||
306 | + default: () => h('span', { style: { color: '#377dff' } }, '详情'), | ||
307 | + count: () => | ||
308 | + h( | ||
309 | + 'div', | ||
310 | + { | ||
311 | + style: { | ||
312 | + visibility: hasAlarm ? 'visible' : 'hidden', | ||
313 | + width: '14px', | ||
314 | + height: '14px', | ||
315 | + display: 'flex', | ||
316 | + justifyContent: 'center', | ||
317 | + alignItems: 'center', | ||
318 | + border: '1px solid #f46161', | ||
319 | + borderRadius: '50%', | ||
320 | + } as CSSProperties, | ||
321 | + }, | ||
322 | + h(Icon, { icon: 'mdi:bell-warning', color: '#f46161', size: 12 }) | ||
323 | + ), | ||
324 | + } | ||
325 | + ); | ||
326 | + | ||
297 | const batchUpdateProductFlag = ref(true); | 327 | const batchUpdateProductFlag = ref(true); |
298 | 328 | ||
299 | const [ | 329 | const [ |
@@ -336,6 +366,7 @@ | @@ -336,6 +366,7 @@ | ||
336 | rowKey: 'id', | 366 | rowKey: 'id', |
337 | searchInfo: searchInfo, | 367 | searchInfo: searchInfo, |
338 | clickToRowSelect: false, | 368 | clickToRowSelect: false, |
369 | + rowClassName: (record) => ((record as DeviceRecord).alarmStatus ? 'device-alarm-badge' : ''), | ||
339 | actionColumn: { | 370 | actionColumn: { |
340 | width: 200, | 371 | width: 200, |
341 | title: '操作', | 372 | title: '操作', |
@@ -557,4 +588,25 @@ | @@ -557,4 +588,25 @@ | ||
557 | }; | 588 | }; |
558 | </script> | 589 | </script> |
559 | 590 | ||
560 | -<style scoped lang="css"></style> | 591 | +<style scoped lang="less"> |
592 | + .device-table { | ||
593 | + :deep(.ant-form-item-control-input-content) { | ||
594 | + & > div > div { | ||
595 | + width: 100%; | ||
596 | + } | ||
597 | + } | ||
598 | + } | ||
599 | +</style> | ||
600 | + | ||
601 | +<style lang="less"> | ||
602 | + .device-status { | ||
603 | + position: relative; | ||
604 | + | ||
605 | + .device-collect { | ||
606 | + width: 0; | ||
607 | + height: 0; | ||
608 | + border-top: 30px solid #377dff; | ||
609 | + border-right: 30px solid transparent; | ||
610 | + } | ||
611 | + } | ||
612 | +</style> |
@@ -144,7 +144,7 @@ | @@ -144,7 +144,7 @@ | ||
144 | <div class="h-full w-full !flex justify-center items-center text-center p-1"> | 144 | <div class="h-full w-full !flex justify-center items-center text-center p-1"> |
145 | <Image | 145 | <Image |
146 | @click.stop | 146 | @click.stop |
147 | - wrapper-class-name="!w-32 !h-32 !flex !items-center" | 147 | + wrapper-class-name="!w-32 !h-32 !flex !items-center overflow-hidden" |
148 | :src="item.image || IMAGE_FALLBACK" | 148 | :src="item.image || IMAGE_FALLBACK" |
149 | placeholder | 149 | placeholder |
150 | :fallback="IMAGE_FALLBACK" | 150 | :fallback="IMAGE_FALLBACK" |
1 | -<template> | ||
2 | - <BasicForm @register="register" /> | ||
3 | -</template> | ||
4 | <script lang="ts" setup> | 1 | <script lang="ts" setup> |
5 | import { BasicForm, useForm } from '/@/components/Form'; | 2 | import { BasicForm, useForm } from '/@/components/Form'; |
6 | import { DataType, ModelOfMatterParams } from '/@/api/device/model/modelOfMatterModel'; | 3 | import { DataType, ModelOfMatterParams } from '/@/api/device/model/modelOfMatterModel'; |
@@ -15,34 +12,38 @@ | @@ -15,34 +12,38 @@ | ||
15 | import { formSchemas } from '/@/components/Form/src/externalCompns/components/StructForm/config'; | 12 | import { formSchemas } from '/@/components/Form/src/externalCompns/components/StructForm/config'; |
16 | import { TransportTypeEnum } from '../../../../components/TransportDescript/const'; | 13 | import { TransportTypeEnum } from '../../../../components/TransportDescript/const'; |
17 | import { DataTypeEnum } from '/@/enums/objectModelEnum'; | 14 | import { DataTypeEnum } from '/@/enums/objectModelEnum'; |
15 | + import { ref, unref } from 'vue'; | ||
16 | + import EnumList from '/@/components/Form/src/externalCompns/components/StructForm/EnumList.vue'; | ||
18 | 17 | ||
19 | const props = defineProps<{ openModalMode: OpenModelMode; transportType?: string | undefined }>(); | 18 | const props = defineProps<{ openModalMode: OpenModelMode; transportType?: string | undefined }>(); |
20 | 19 | ||
21 | - const [register, { validate, resetFields, setFieldsValue, setProps }] = useForm({ | 20 | + const enumListRef = ref<InstanceType<typeof EnumList>>(); |
21 | + | ||
22 | + const [register, { validate, getFieldsValue, resetFields, setFieldsValue }] = useForm({ | ||
22 | labelWidth: 100, | 23 | labelWidth: 100, |
23 | schemas: formSchemas({ | 24 | schemas: formSchemas({ |
24 | hasStructForm: false, | 25 | hasStructForm: false, |
25 | hiddenAccessMode: false, | 26 | hiddenAccessMode: false, |
26 | isTcp: props.transportType === TransportTypeEnum.TCP, | 27 | isTcp: props.transportType === TransportTypeEnum.TCP, |
27 | }), | 28 | }), |
28 | - actionColOptions: { | ||
29 | - span: 14, | ||
30 | - }, | ||
31 | - showResetButton: false, | ||
32 | - submitOnReset: false, | ||
33 | showActionButtonGroup: false, | 29 | showActionButtonGroup: false, |
34 | }); | 30 | }); |
35 | 31 | ||
32 | + const disabled = ref(false); | ||
36 | const setDisable = (flag: boolean) => { | 33 | const setDisable = (flag: boolean) => { |
37 | - setProps({ disabled: flag }); | 34 | + disabled.value = flag; |
38 | }; | 35 | }; |
39 | 36 | ||
40 | async function getFormData(): Promise<Partial<ModelOfMatterParams>> { | 37 | async function getFormData(): Promise<Partial<ModelOfMatterParams>> { |
41 | - const _values = (await validate()) as StructFormValue; | ||
42 | - if (!_values) return {}; | 38 | + await validate(); |
39 | + await unref(enumListRef)?.validate?.(); | ||
40 | + | ||
41 | + const _values = getFieldsValue() as StructFormValue; | ||
43 | const { functionName, remark, identifier, accessMode } = _values; | 42 | const { functionName, remark, identifier, accessMode } = _values; |
44 | - const structJSON = transfromToStructJSON(_values); | 43 | + const structJSON = transfromToStructJSON(_values, unref(enumListRef)?.getFieldsValue?.()); |
44 | + | ||
45 | const dataType = excludeIdInStructJSON(structJSON.dataType!); | 45 | const dataType = excludeIdInStructJSON(structJSON.dataType!); |
46 | + | ||
46 | const value = { | 47 | const value = { |
47 | functionName, | 48 | functionName, |
48 | functionType: FunctionType.PROPERTIES, | 49 | functionType: FunctionType.PROPERTIES, |
@@ -54,6 +55,7 @@ | @@ -54,6 +55,7 @@ | ||
54 | dataType: dataType, | 55 | dataType: dataType, |
55 | }, | 56 | }, |
56 | } as ModelOfMatterParams; | 57 | } as ModelOfMatterParams; |
58 | + | ||
57 | return value; | 59 | return value; |
58 | } | 60 | } |
59 | 61 | ||
@@ -63,18 +65,21 @@ | @@ -63,18 +65,21 @@ | ||
63 | 65 | ||
64 | const setFormData = (record: ModelOfMatterParams) => { | 66 | const setFormData = (record: ModelOfMatterParams) => { |
65 | const { functionJson } = record; | 67 | const { functionJson } = record; |
66 | - const { dataType = {} } = functionJson!; | 68 | + const { dataType } = functionJson!; |
67 | 69 | ||
68 | - const { specs } = dataType! as DataType; | 70 | + const { specs } = (dataType! || {}) as DataType; |
69 | 71 | ||
70 | const value = { | 72 | const value = { |
71 | ...record, | 73 | ...record, |
72 | ...functionJson, | 74 | ...functionJson, |
73 | ...dataType, | 75 | ...dataType, |
74 | ...(isArray(specs) ? specs : { ...specs }), | 76 | ...(isArray(specs) ? specs : { ...specs }), |
75 | - hasStructForm: (record?.functionJson?.dataType as DataType)?.type === DataTypeEnum.STRUCT, | 77 | + hasStructForm: (dataType as DataType)?.type === DataTypeEnum.STRUCT, |
78 | + enumList: | ||
79 | + (dataType as DataType)?.type === DataTypeEnum.ENUM | ||
80 | + ? unref((dataType as DataType).specsList) | ||
81 | + : [], | ||
76 | }; | 82 | }; |
77 | - | ||
78 | setFieldsValue(value); | 83 | setFieldsValue(value); |
79 | }; | 84 | }; |
80 | 85 | ||
@@ -85,4 +90,13 @@ | @@ -85,4 +90,13 @@ | ||
85 | setDisable, | 90 | setDisable, |
86 | }); | 91 | }); |
87 | </script> | 92 | </script> |
93 | + | ||
94 | +<template> | ||
95 | + <BasicForm @register="register" :disabled="disabled"> | ||
96 | + <template #EnumList="{ field, model }"> | ||
97 | + <EnumList ref="enumListRef" :value="model[field]" :disabled="disabled" /> | ||
98 | + </template> | ||
99 | + </BasicForm> | ||
100 | +</template> | ||
101 | + | ||
88 | <style lang="less" scoped></style> | 102 | <style lang="less" scoped></style> |
@@ -28,6 +28,7 @@ export enum FormField { | @@ -28,6 +28,7 @@ export enum FormField { | ||
28 | REGISTER_ADDRESS = 'registerAddress', | 28 | REGISTER_ADDRESS = 'registerAddress', |
29 | EXTENSION_DESC = 'extensionDesc', | 29 | EXTENSION_DESC = 'extensionDesc', |
30 | STRUCT = 'struct', | 30 | STRUCT = 'struct', |
31 | + ENUM_LIST = 'enumList', | ||
31 | 32 | ||
32 | HAS_STRUCT_FROM = 'hasStructForm', | 33 | HAS_STRUCT_FROM = 'hasStructForm', |
33 | } | 34 | } |
@@ -185,8 +185,8 @@ export const list = [ | @@ -185,8 +185,8 @@ export const list = [ | ||
185 | { | 185 | { |
186 | deviceType: '网关/直连/网关子设备', | 186 | deviceType: '网关/直连/网关子设备', |
187 | function: '事件上报', | 187 | function: '事件上报', |
188 | - release: 'v1/devices/event/${deviceId}||${deviceName}/${identifier}', | ||
189 | - subscribe: 'v1/devices/event/${deviceId}||${deviceName}/${identifier}', | 188 | + release: 'v1/devices/event/${deviceId}或${deviceName}/${identifier}', |
189 | + subscribe: 'v1/devices/event/${deviceId}或${deviceName}/${identifier}', | ||
190 | platform: '订阅', | 190 | platform: '订阅', |
191 | device: '发布', | 191 | device: '发布', |
192 | }, | 192 | }, |
@@ -132,10 +132,7 @@ | @@ -132,10 +132,7 @@ | ||
132 | }; | 132 | }; |
133 | } | 133 | } |
134 | Reflect.set(values, 'config', config); | 134 | Reflect.set(values, 'config', config); |
135 | - if ( | ||
136 | - Reflect.get(values, 'messageType') === MessageEnum.IS_VOICE && | ||
137 | - Reflect.get(values, 'voiceSignName') | ||
138 | - ) { | 135 | + if (Reflect.get(values, 'messageType') === MessageEnum.IS_VOICE) { |
139 | Reflect.set(values, 'signName', values['voiceSignName']); | 136 | Reflect.set(values, 'signName', values['voiceSignName']); |
140 | } | 137 | } |
141 | let saveMessage = '添加成功'; | 138 | let saveMessage = '添加成功'; |
@@ -102,36 +102,36 @@ export const getFormSchemas = (hasAlarmNotify: Ref<boolean>): FormSchema[] => { | @@ -102,36 +102,36 @@ export const getFormSchemas = (hasAlarmNotify: Ref<boolean>): FormSchema[] => { | ||
102 | }, | 102 | }, |
103 | }, | 103 | }, |
104 | { | 104 | { |
105 | - field: FormFieldsEnum.ALARM_PROFILED, | 105 | + field: FormFieldsEnum.ALARM_LEVEL, |
106 | label: '', | 106 | label: '', |
107 | - component: 'AlarmProfileSelect', | ||
108 | - rules: [{ required: true, message: `请选择${FormFieldsEnum.ALARM_PROFILED}` }], | 107 | + component: 'Select', |
109 | ifShow: ({ model }) => model[FormFieldsEnum.OUT_TARGET] === ExecutionActionEnum.MSG_NOTIFY, | 108 | ifShow: ({ model }) => model[FormFieldsEnum.OUT_TARGET] === ExecutionActionEnum.MSG_NOTIFY, |
109 | + rules: [{ required: true, message: `请选择${FormFieldsNameEnum.ALARM_LEVEL}` }], | ||
110 | componentProps: () => { | 110 | componentProps: () => { |
111 | return { | 111 | return { |
112 | - api: async () => { | ||
113 | - if (!unref(organizationId)) return []; | ||
114 | - return await getOrganizationAlarmConfig({ organizationId: unref(organizationId) }); | ||
115 | - }, | ||
116 | - labelField: 'name', | ||
117 | - valueField: 'id', | ||
118 | - placeholder: `请选择${FormFieldsNameEnum.ALARM_PROFILED}`, | 112 | + options: Object.keys(AlarmLevelEnum).map((value) => ({ |
113 | + label: AlarmLevelNameEnum[value], | ||
114 | + value, | ||
115 | + })), | ||
116 | + placeholder: `请选择${FormFieldsNameEnum.ALARM_LEVEL}`, | ||
119 | }; | 117 | }; |
120 | }, | 118 | }, |
121 | }, | 119 | }, |
122 | { | 120 | { |
123 | - field: FormFieldsEnum.ALARM_LEVEL, | 121 | + field: FormFieldsEnum.ALARM_PROFILED, |
124 | label: '', | 122 | label: '', |
125 | - component: 'Select', | 123 | + component: 'AlarmProfileSelect', |
124 | + // rules: [{ required: true, message: `请选择${FormFieldsEnum.ALARM_PROFILED}` }], | ||
126 | ifShow: ({ model }) => model[FormFieldsEnum.OUT_TARGET] === ExecutionActionEnum.MSG_NOTIFY, | 125 | ifShow: ({ model }) => model[FormFieldsEnum.OUT_TARGET] === ExecutionActionEnum.MSG_NOTIFY, |
127 | - rules: [{ required: true, message: `请选择${FormFieldsEnum.ALARM_LEVEL}` }], | ||
128 | componentProps: () => { | 126 | componentProps: () => { |
129 | return { | 127 | return { |
130 | - options: Object.keys(AlarmLevelEnum).map((value) => ({ | ||
131 | - label: AlarmLevelNameEnum[value], | ||
132 | - value, | ||
133 | - })), | ||
134 | - placeholder: `请选择${FormFieldsNameEnum.ALARM_LEVEL}`, | 128 | + api: async () => { |
129 | + if (!unref(organizationId)) return []; | ||
130 | + return await getOrganizationAlarmConfig({ organizationId: unref(organizationId) }); | ||
131 | + }, | ||
132 | + labelField: 'name', | ||
133 | + valueField: 'id', | ||
134 | + placeholder: `请选择${FormFieldsNameEnum.ALARM_PROFILED}(可选项)`, | ||
135 | }; | 135 | }; |
136 | }, | 136 | }, |
137 | }, | 137 | }, |
src/views/sys/appPage/config/config.ts
0 → 100644
1 | +import { useRouter } from 'vue-router'; | ||
2 | + | ||
3 | +export enum ViewTypeEnum { | ||
4 | + DATA_BOARD = 'DATA_BOARD', | ||
5 | + LARGE_SCREEN = 'LARGE_SCREEN', | ||
6 | + SCADA = 'SCADA', | ||
7 | +} | ||
8 | + | ||
9 | +export const goShareUrl = (options: { type: ViewTypeEnum; id: string }, openNew?: false) => { | ||
10 | + const { type, id } = options; | ||
11 | + const ROUTER = useRouter(); | ||
12 | + const { origin } = location; | ||
13 | + const path = `/share/${type}/${id}`; | ||
14 | + openNew ? ROUTER.push(path) : open(`${origin}${path}`); | ||
15 | +}; |
src/views/sys/appPage/hook/index.ts
0 → 100644
src/views/sys/appPage/index.vue
0 → 100644
1 | +<script lang="ts" setup> | ||
2 | + import { onMounted } from 'vue'; | ||
3 | + import { Spin } from 'ant-design-vue'; | ||
4 | + import { ref } from 'vue'; | ||
5 | + import { useRoute } from 'vue-router'; | ||
6 | + import { useUserStore } from '/@/store/modules/user'; | ||
7 | + import BoardDetail from '/@/views/visual/board/detail/index.vue'; | ||
8 | + import { doAppLogin } from '/@/api/sys/user'; | ||
9 | + | ||
10 | + const loading = ref(true); | ||
11 | + const ROUTE = useRoute(); | ||
12 | + const contentData = ref<any>(); | ||
13 | + const canLoadComponent = ref(false); | ||
14 | + | ||
15 | + const modelVisable = ref(false); | ||
16 | + | ||
17 | + const userStore = useUserStore(); | ||
18 | + | ||
19 | + const getShareToken = async () => { | ||
20 | + const { params } = ROUTE; | ||
21 | + const { userId }: any = params; | ||
22 | + const { token, refreshToken } = await doAppLogin(userId); | ||
23 | + userStore.storeToken(token, refreshToken); | ||
24 | + }; | ||
25 | + | ||
26 | + const getContentLoading = ref(false); | ||
27 | + const getContentData = async () => { | ||
28 | + try { | ||
29 | + getContentLoading.value = true; | ||
30 | + loading.value = false; | ||
31 | + canLoadComponent.value = true; | ||
32 | + modelVisable.value = false; | ||
33 | + } catch (error) { | ||
34 | + } finally { | ||
35 | + getContentLoading.value = false; | ||
36 | + } | ||
37 | + }; | ||
38 | + | ||
39 | + onMounted(async () => { | ||
40 | + await getShareToken(); | ||
41 | + await getContentData(); | ||
42 | + }); | ||
43 | +</script> | ||
44 | + | ||
45 | +<template> | ||
46 | + <Spin | ||
47 | + :spinning="loading" | ||
48 | + tip="正在加载中..." | ||
49 | + size="large" | ||
50 | + class="!flex justify-center items-center w-full h-full share-full-loading" | ||
51 | + > | ||
52 | + <BoardDetail v-if="canLoadComponent" :value="contentData" /> | ||
53 | + </Spin> | ||
54 | +</template> | ||
55 | + | ||
56 | +<style lang="less"> | ||
57 | + .share-page-token-modal { | ||
58 | + .ant-modal-close { | ||
59 | + display: none; | ||
60 | + } | ||
61 | + } | ||
62 | +</style> |
@@ -88,6 +88,7 @@ export const usePlatform = async () => { | @@ -88,6 +88,7 @@ export const usePlatform = async () => { | ||
88 | }; | 88 | }; |
89 | 89 | ||
90 | const setTitle = () => { | 90 | const setTitle = () => { |
91 | + if (!platformInfo.name) return; | ||
91 | document.title = platformInfo.name || ''; | 92 | document.title = platformInfo.name || ''; |
92 | }; | 93 | }; |
93 | 94 |
@@ -34,6 +34,7 @@ | @@ -34,6 +34,7 @@ | ||
34 | if (!Reflect.get(value, 'accessCredentials')) { | 34 | if (!Reflect.get(value, 'accessCredentials')) { |
35 | Reflect.deleteProperty(value, 'accessCredentials'); | 35 | Reflect.deleteProperty(value, 'accessCredentials'); |
36 | } | 36 | } |
37 | + | ||
37 | return value as any; | 38 | return value as any; |
38 | }; | 39 | }; |
39 | 40 |
src/views/visual/board/components/TextComponent/config.ts
deleted
100644 → 0
1 | -import { ComponentInfo, DataComponentRecord, DataSource } from '/@/api/dataBoard/model'; | ||
2 | -export interface TextComponentLayout { | ||
3 | - id: string; | ||
4 | - base?: boolean; | ||
5 | - showUpdate?: boolean; | ||
6 | - showIcon?: boolean; | ||
7 | - showUnit?: boolean; | ||
8 | -} | ||
9 | - | ||
10 | -export interface TextComponentValue { | ||
11 | - name: string; | ||
12 | - value: number; | ||
13 | - icon?: string; | ||
14 | - unit?: string; | ||
15 | - updateTime?: string; | ||
16 | - fontColor?: string; | ||
17 | - iconColor?: string; | ||
18 | - deviceName?: string; | ||
19 | -} | ||
20 | - | ||
21 | -type TextComponentDefault = TextComponentLayout; | ||
22 | - | ||
23 | -export const TextComponent1Config: TextComponentDefault = { | ||
24 | - id: 'text-component-1', | ||
25 | - base: true, | ||
26 | -}; | ||
27 | - | ||
28 | -export const TextComponent3Config: TextComponentDefault = { | ||
29 | - id: 'text-component-3', | ||
30 | - base: false, | ||
31 | - showUpdate: true, | ||
32 | -}; | ||
33 | -export const TextComponent4Config: TextComponentDefault = { | ||
34 | - id: 'text-component-4', | ||
35 | - base: false, | ||
36 | - showIcon: true, | ||
37 | - showUpdate: true, | ||
38 | - showUnit: true, | ||
39 | -}; | ||
40 | -export const TextComponent5Config: TextComponentDefault = { | ||
41 | - id: 'text-component-5', | ||
42 | - base: false, | ||
43 | - showIcon: true, | ||
44 | - showUnit: true, | ||
45 | -}; | ||
46 | - | ||
47 | -export const TextComponentDefaultConfig: Partial<ComponentInfo> = { | ||
48 | - fontColor: '#000', | ||
49 | - unit: '℃', | ||
50 | - iconColor: '#367BFF', | ||
51 | - icon: 'shuiwen', | ||
52 | -}; | ||
53 | - | ||
54 | -export const transformTextComponentConfig = ( | ||
55 | - config: TextComponentDefault, | ||
56 | - _record: DataComponentRecord, | ||
57 | - dataSourceRecord: DataSource | ||
58 | -) => { | ||
59 | - return { | ||
60 | - layout: { | ||
61 | - ...config, | ||
62 | - } as TextComponentLayout, | ||
63 | - value: { | ||
64 | - name: dataSourceRecord.attributeRename || dataSourceRecord.attribute, | ||
65 | - deviceName: dataSourceRecord.deviceRename || dataSourceRecord.deviceId, | ||
66 | - value: dataSourceRecord.componentInfo.value, | ||
67 | - icon: dataSourceRecord.componentInfo.icon, | ||
68 | - unit: dataSourceRecord.componentInfo.unit, | ||
69 | - updateTime: dataSourceRecord.componentInfo.updateTime, | ||
70 | - fontColor: dataSourceRecord.componentInfo.fontColor, | ||
71 | - iconColor: dataSourceRecord.componentInfo.iconColor, | ||
72 | - } as TextComponentValue, | ||
73 | - }; | ||
74 | -}; |
@@ -4,7 +4,13 @@ export enum ViewType { | @@ -4,7 +4,13 @@ export enum ViewType { | ||
4 | PRIVATE_VIEW = 'PRIVATE_VIEW', | 4 | PRIVATE_VIEW = 'PRIVATE_VIEW', |
5 | PUBLIC_VIEW = 'PUBLIC_VIEW', | 5 | PUBLIC_VIEW = 'PUBLIC_VIEW', |
6 | } | 6 | } |
7 | +import { Platform } from '../../palette/components/PagerHeader/config'; | ||
8 | + | ||
7 | useComponentRegister('OrgTreeSelect', OrgTreeSelect); | 9 | useComponentRegister('OrgTreeSelect', OrgTreeSelect); |
10 | +export enum PlatformType { | ||
11 | + PHONE = 'phone', | ||
12 | + PC = 'pc', | ||
13 | +} | ||
8 | 14 | ||
9 | export const formSchema: FormSchema[] = [ | 15 | export const formSchema: FormSchema[] = [ |
10 | { | 16 | { |
@@ -24,6 +30,32 @@ export const formSchema: FormSchema[] = [ | @@ -24,6 +30,32 @@ export const formSchema: FormSchema[] = [ | ||
24 | rules: [{ required: true, message: '组织为必填项' }], | 30 | rules: [{ required: true, message: '组织为必填项' }], |
25 | }, | 31 | }, |
26 | { | 32 | { |
33 | + field: 'platform', | ||
34 | + label: '平台', | ||
35 | + required: true, | ||
36 | + component: 'RadioGroup', | ||
37 | + defaultValue: PlatformType.PC, | ||
38 | + componentProps({ formModel }) { | ||
39 | + return { | ||
40 | + defaultValue: PlatformType.PC, | ||
41 | + options: [ | ||
42 | + { label: 'PC端', value: PlatformType.PC }, | ||
43 | + { label: '移动端', value: PlatformType.PHONE }, | ||
44 | + ], | ||
45 | + onChange(e) { | ||
46 | + formModel.phoneModel = | ||
47 | + e?.target?.value === PlatformType.PHONE ? JSON.stringify(Platform[1]) : []; | ||
48 | + }, | ||
49 | + }; | ||
50 | + }, | ||
51 | + }, | ||
52 | + { | ||
53 | + field: 'phoneModel', | ||
54 | + label: '手机端尺寸', | ||
55 | + component: 'Input', | ||
56 | + ifShow: false, | ||
57 | + }, | ||
58 | + { | ||
27 | field: 'remark', | 59 | field: 'remark', |
28 | label: '备注', | 60 | label: '备注', |
29 | component: 'InputTextArea', | 61 | component: 'InputTextArea', |
1 | +import { PlatformType } from './panelDetail'; | ||
1 | import { FormSchema } from '/@/components/Form'; | 2 | import { FormSchema } from '/@/components/Form'; |
2 | import { ColEx } from '/@/components/Form/src/types'; | 3 | import { ColEx } from '/@/components/Form/src/types'; |
3 | import { useGridLayout } from '/@/hooks/component/useGridLayout'; | 4 | import { useGridLayout } from '/@/hooks/component/useGridLayout'; |
@@ -13,4 +14,16 @@ export const formSchema: FormSchema[] = [ | @@ -13,4 +14,16 @@ export const formSchema: FormSchema[] = [ | ||
13 | placeholder: '请输入看板名称', | 14 | placeholder: '请输入看板名称', |
14 | }, | 15 | }, |
15 | }, | 16 | }, |
17 | + { | ||
18 | + field: 'platform', | ||
19 | + label: '平台', | ||
20 | + component: 'Select', | ||
21 | + colProps: useGridLayout(2, 3, 4) as unknown as ColEx, | ||
22 | + componentProps: { | ||
23 | + options: [ | ||
24 | + { label: 'PC端', value: PlatformType.PC }, | ||
25 | + { label: '移动端', value: PlatformType.PHONE }, | ||
26 | + ], | ||
27 | + }, | ||
28 | + }, | ||
16 | ]; | 29 | ]; |
@@ -135,12 +135,13 @@ | @@ -135,12 +135,13 @@ | ||
135 | 135 | ||
136 | const handleViewBoard = (record: DataBoardRecord) => { | 136 | const handleViewBoard = (record: DataBoardRecord) => { |
137 | const hasDetailPermission = hasPermission(VisualBoardPermission.DETAIL); | 137 | const hasDetailPermission = hasPermission(VisualBoardPermission.DETAIL); |
138 | + const { platform = 'pc' } = record || {}; | ||
138 | if (hasDetailPermission) { | 139 | if (hasDetailPermission) { |
139 | const boardId = encode(record.id); | 140 | const boardId = encode(record.id); |
140 | const boardName = encode(record.name); | 141 | const boardName = encode(record.name); |
141 | const organizationId = encode(record!.organizationId!); | 142 | const organizationId = encode(record!.organizationId!); |
142 | 143 | ||
143 | - router.push(`/visual/board/detail/${boardId}/${boardName}/${organizationId}`); | 144 | + router.push(`/visual/board/detail/${boardId}/${boardName}/${platform}/${organizationId}`); |
144 | } else createMessage.warning('没有权限'); | 145 | } else createMessage.warning('没有权限'); |
145 | }; | 146 | }; |
146 | </script> | 147 | </script> |
@@ -170,7 +171,7 @@ | @@ -170,7 +171,7 @@ | ||
170 | </Dropdown> | 171 | </Dropdown> |
171 | </template> | 172 | </template> |
172 | <section @click="handleViewBoard(item)"> | 173 | <section @click="handleViewBoard(item)"> |
173 | - <div class="flex data-card__info"> | 174 | + <div class="flex data-card__info relative"> |
174 | <div> | 175 | <div> |
175 | <div>组件数量</div> | 176 | <div>组件数量</div> |
176 | <Statistic class="text-2xl" :value="item.componentNum"> | 177 | <Statistic class="text-2xl" :value="item.componentNum"> |
@@ -179,6 +180,12 @@ | @@ -179,6 +180,12 @@ | ||
179 | </template> | 180 | </template> |
180 | </Statistic> | 181 | </Statistic> |
181 | </div> | 182 | </div> |
183 | + <div class="absolute" style="right: 1%"> | ||
184 | + <Icon | ||
185 | + :icon="item.platform === 'pc' ? 'ri:computer-line' : 'clarity:mobile-phone-solid'" | ||
186 | + style="font-size: 32px" | ||
187 | + /> | ||
188 | + </div> | ||
182 | </div> | 189 | </div> |
183 | <div class="flex justify-between mt-4 text-sm" style="color: #999"> | 190 | <div class="flex justify-between mt-4 text-sm" style="color: #999"> |
184 | <div class="flex min-w-20 mr-3"> | 191 | <div class="flex min-w-20 mr-3"> |
@@ -107,6 +107,9 @@ export const formSchemas = (): FormSchema[] => { | @@ -107,6 +107,9 @@ export const formSchemas = (): FormSchema[] => { | ||
107 | [DataSourceField.DEVICE_ID]: null, | 107 | [DataSourceField.DEVICE_ID]: null, |
108 | }); | 108 | }); |
109 | }, | 109 | }, |
110 | + apiTreeSelectProps: { | ||
111 | + params: { organizationId: location?.pathname?.split('/')?.pop() || '' }, | ||
112 | + }, | ||
110 | showCreate: false, | 113 | showCreate: false, |
111 | getPopupContainer: () => document.body, | 114 | getPopupContainer: () => document.body, |
112 | }; | 115 | }; |
@@ -157,7 +160,6 @@ export const formSchemas = (): FormSchema[] => { | @@ -157,7 +160,6 @@ export const formSchemas = (): FormSchema[] => { | ||
157 | }); | 160 | }); |
158 | }, | 161 | }, |
159 | placeholder: '请选择设备', | 162 | placeholder: '请选择设备', |
160 | - getPopupContainer: () => document.body, | ||
161 | ...createPickerSearch(), | 163 | ...createPickerSearch(), |
162 | }; | 164 | }; |
163 | }, | 165 | }, |
@@ -58,24 +58,24 @@ | @@ -58,24 +58,24 @@ | ||
58 | 58 | ||
59 | const alarmColumns: BasicColumn[] = [ | 59 | const alarmColumns: BasicColumn[] = [ |
60 | { | 60 | { |
61 | - title: '状态', | ||
62 | - dataIndex: 'severity', | 61 | + title: '告警等级', |
62 | + dataIndex: 'status', | ||
63 | ellipsis: true, | 63 | ellipsis: true, |
64 | width: 80, | 64 | width: 80, |
65 | - customRender: ({ record }) => { | ||
66 | - const { severity } = record; | ||
67 | - const { text, color } = alarmLevel(severity); | ||
68 | - return h(Tag, { color }, () => text); | ||
69 | - }, | 65 | + format: (text) => statusType(text), |
70 | }, | 66 | }, |
71 | { title: '设备', dataIndex: 'device', ellipsis: true, width: 120 }, | 67 | { title: '设备', dataIndex: 'device', ellipsis: true, width: 120 }, |
72 | { title: '告警场景', dataIndex: 'type', ellipsis: true, width: 80 }, | 68 | { title: '告警场景', dataIndex: 'type', ellipsis: true, width: 80 }, |
73 | { | 69 | { |
74 | - title: '状态', | ||
75 | - dataIndex: 'status', | 70 | + title: '告警级别', |
71 | + dataIndex: 'severity', | ||
76 | ellipsis: true, | 72 | ellipsis: true, |
77 | width: 80, | 73 | width: 80, |
78 | - format: (text) => statusType(text), | 74 | + customRender: ({ record }) => { |
75 | + const { severity } = record; | ||
76 | + const { text, color } = alarmLevel(severity); | ||
77 | + return h(Tag, { color }, () => text); | ||
78 | + }, | ||
79 | }, | 79 | }, |
80 | { | 80 | { |
81 | title: '时间', | 81 | title: '时间', |
@@ -4,8 +4,8 @@ | @@ -4,8 +4,8 @@ | ||
4 | destroyOnClose | 4 | destroyOnClose |
5 | v-bind="$attrs" | 5 | v-bind="$attrs" |
6 | width="30rem" | 6 | width="30rem" |
7 | - :height="50" | ||
8 | - :minHeight="50" | 7 | + :height="75" |
8 | + :minHeight="75" | ||
9 | @register="register" | 9 | @register="register" |
10 | title="操作密码" | 10 | title="操作密码" |
11 | @ok="handleSuccess" | 11 | @ok="handleSuccess" |
@@ -33,6 +33,8 @@ | @@ -33,6 +33,8 @@ | ||
33 | const [registerForm, { getFieldsValue, validate }] = useForm({ | 33 | const [registerForm, { getFieldsValue, validate }] = useForm({ |
34 | labelWidth: 70, | 34 | labelWidth: 70, |
35 | schemas: formSchema, | 35 | schemas: formSchema, |
36 | + | ||
37 | + wrapperCol: { span: 12 }, | ||
36 | showActionButtonGroup: false, | 38 | showActionButtonGroup: false, |
37 | }); | 39 | }); |
38 | 40 |
@@ -4,7 +4,7 @@ export const formSchema: FormSchema[] = [ | @@ -4,7 +4,7 @@ export const formSchema: FormSchema[] = [ | ||
4 | { | 4 | { |
5 | field: 'password', | 5 | field: 'password', |
6 | label: '操作密码', | 6 | label: '操作密码', |
7 | - colProps: { span: 16 }, | 7 | + colProps: { span: 18 }, |
8 | component: 'InputPassword', | 8 | component: 'InputPassword', |
9 | required: true, | 9 | required: true, |
10 | componentProps: { | 10 | componentProps: { |
@@ -14,7 +14,7 @@ | @@ -14,7 +14,7 @@ | ||
14 | import { useBaiduMapSDK } from '../../../hook/useBaiduMapSDK'; | 14 | import { useBaiduMapSDK } from '../../../hook/useBaiduMapSDK'; |
15 | import { HistoryModalOkEmitParams, TrackAnimationStatus } from './type'; | 15 | import { HistoryModalOkEmitParams, TrackAnimationStatus } from './type'; |
16 | import { useMapTrackPlayback } from './useMapTrackPlayback'; | 16 | import { useMapTrackPlayback } from './useMapTrackPlayback'; |
17 | - import { shallowRef } from 'vue'; | 17 | + // import { shallowRef } from 'vue'; |
18 | import { formatToDateTime } from '/@/utils/dateUtil'; | 18 | import { formatToDateTime } from '/@/utils/dateUtil'; |
19 | 19 | ||
20 | const props = defineProps<{ | 20 | const props = defineProps<{ |
@@ -25,7 +25,7 @@ | @@ -25,7 +25,7 @@ | ||
25 | 25 | ||
26 | const wrapId = `bai-map-${buildUUID()}`; | 26 | const wrapId = `bai-map-${buildUUID()}`; |
27 | 27 | ||
28 | - const mapInstance = shallowRef<Nullable<Recordable>>(null); | 28 | + const mapInstance = ref<Nullable<Recordable>>(null); |
29 | 29 | ||
30 | const rangString = ref<Nullable<string>>(null); | 30 | const rangString = ref<Nullable<string>>(null); |
31 | 31 | ||
@@ -35,6 +35,9 @@ | @@ -35,6 +35,9 @@ | ||
35 | openModal(true, toRaw(props.config.option.dataSource)); | 35 | openModal(true, toRaw(props.config.option.dataSource)); |
36 | }; | 36 | }; |
37 | 37 | ||
38 | + const { genTrackPlaybackAnimation, playStatus, playFn, continueFn, pauseFn } = | ||
39 | + useMapTrackPlayback(mapInstance); | ||
40 | + | ||
38 | const getIsWidgetLibSelectMode = computed(() => { | 41 | const getIsWidgetLibSelectMode = computed(() => { |
39 | return !props.config.option.dataSource; | 42 | return !props.config.option.dataSource; |
40 | }); | 43 | }); |
@@ -68,8 +71,8 @@ | @@ -68,8 +71,8 @@ | ||
68 | async function initMap() { | 71 | async function initMap() { |
69 | const wrapEl = unref(wrapRef); | 72 | const wrapEl = unref(wrapRef); |
70 | if (!wrapEl) return; | 73 | if (!wrapEl) return; |
71 | - const BMapGL = (window as any).BMapGL; | ||
72 | if (!Reflect.has(window, 'BMapGL')) return; | 74 | if (!Reflect.has(window, 'BMapGL')) return; |
75 | + const BMapGL = (window as any).BMapGL; | ||
73 | mapInstance.value = new BMapGL.Map(wrapId); | 76 | mapInstance.value = new BMapGL.Map(wrapId); |
74 | 77 | ||
75 | // 定位当前城市 | 78 | // 定位当前城市 |
@@ -89,13 +92,10 @@ | @@ -89,13 +92,10 @@ | ||
89 | } | 92 | } |
90 | 93 | ||
91 | const { loading } = useBaiduMapSDK(initMap); | 94 | const { loading } = useBaiduMapSDK(initMap); |
92 | - | ||
93 | - const { genTrackPlaybackAnimation, playStatus, playFn, continueFn, pauseFn } = | ||
94 | - useMapTrackPlayback(mapInstance); | ||
95 | </script> | 95 | </script> |
96 | 96 | ||
97 | <template> | 97 | <template> |
98 | - <main class="w-full h-full flex flex-col justify-center items-center"> | 98 | + <main class="w-full h-full flex flex-col p-2 justify-center items-center"> |
99 | <div class="w-full flex justify-end"> | 99 | <div class="w-full flex justify-end"> |
100 | <Button | 100 | <Button |
101 | type="text" | 101 | type="text" |
@@ -126,9 +126,8 @@ | @@ -126,9 +126,8 @@ | ||
126 | :spinning="loading" | 126 | :spinning="loading" |
127 | wrapper-class-name="map-spin-wrapper !w-full !h-full !flex justify-center items-center pointer-events-none" | 127 | wrapper-class-name="map-spin-wrapper !w-full !h-full !flex justify-center items-center pointer-events-none" |
128 | tip="地图加载中..." | 128 | tip="地图加载中..." |
129 | - > | ||
130 | - <div ref="wrapRef" :id="wrapId" class="w-full h-full no-drag"> </div> | ||
131 | - </Spin> | 129 | + /> |
130 | + <div ref="wrapRef" :id="wrapId" class="w-full h-full no-drag"> </div> | ||
132 | 131 | ||
133 | <HistoryDataModel @register="register" @ok="handleRenderHistroyData" /> | 132 | <HistoryDataModel @register="register" @ok="handleRenderHistroyData" /> |
134 | </main> | 133 | </main> |
@@ -88,6 +88,9 @@ export const formSchemas: FormSchema[] = [ | @@ -88,6 +88,9 @@ export const formSchemas: FormSchema[] = [ | ||
88 | return { | 88 | return { |
89 | showCreate: false, | 89 | showCreate: false, |
90 | allowClean: true, | 90 | allowClean: true, |
91 | + apiTreeSelectProps: { | ||
92 | + params: { organizationId: location?.pathname?.split('/')?.pop() || '' }, | ||
93 | + }, | ||
91 | onChange() { | 94 | onChange() { |
92 | setFieldsValue({ | 95 | setFieldsValue({ |
93 | [FormFieldEnum.ACCESS_MODE]: null, | 96 | [FormFieldEnum.ACCESS_MODE]: null, |
@@ -68,8 +68,6 @@ | @@ -68,8 +68,6 @@ | ||
68 | }; | 68 | }; |
69 | 69 | ||
70 | const instance = unref(basicVideoPlayEl)?.customInit((options) => { | 70 | const instance = unref(basicVideoPlayEl)?.customInit((options) => { |
71 | - withToken.value = true; | ||
72 | - | ||
73 | if (unref(withToken)) { | 71 | if (unref(withToken)) { |
74 | (options as any).flvjs.config.headers = { | 72 | (options as any).flvjs.config.headers = { |
75 | 'X-Authorization': `Bearer ${isShareMode() ? getShareJwtToken() : getJwtToken()}`, | 73 | 'X-Authorization': `Bearer ${isShareMode() ? getShareJwtToken() : getJwtToken()}`, |
@@ -5,6 +5,7 @@ | @@ -5,6 +5,7 @@ | ||
5 | import { ComponentPropsConfigType } from '../../../index.type'; | 5 | import { ComponentPropsConfigType } from '../../../index.type'; |
6 | import { useDataFetch } from '../../../hook/socket/useSocket'; | 6 | import { useDataFetch } from '../../../hook/socket/useSocket'; |
7 | import { DataFetchUpdateFn } from '../../../hook/socket/useSocket.type'; | 7 | import { DataFetchUpdateFn } from '../../../hook/socket/useSocket.type'; |
8 | + import { isNullOrUnDef } from '/@/utils/is'; | ||
8 | 9 | ||
9 | const props = defineProps<{ | 10 | const props = defineProps<{ |
10 | config: ComponentPropsConfigType; | 11 | config: ComponentPropsConfigType; |
@@ -42,6 +43,7 @@ | @@ -42,6 +43,7 @@ | ||
42 | const { data = {} } = message; | 43 | const { data = {} } = message; |
43 | const [latest] = data[attribute] || []; | 44 | const [latest] = data[attribute] || []; |
44 | const [timespan, value] = latest; | 45 | const [timespan, value] = latest; |
46 | + if (isNullOrUnDef(value)) return; | ||
45 | time.value = timespan; | 47 | time.value = timespan; |
46 | url.value = `${value}?timespan=${timespan}`; | 48 | url.value = `${value}?timespan=${timespan}`; |
47 | }; | 49 | }; |
@@ -23,6 +23,7 @@ | @@ -23,6 +23,7 @@ | ||
23 | import { WidgetDataType } from '../../hooks/useDataSource'; | 23 | import { WidgetDataType } from '../../hooks/useDataSource'; |
24 | import { ExtraDataSource } from '../../types'; | 24 | import { ExtraDataSource } from '../../types'; |
25 | import { OrderByEnum } from '/@/views/device/localtion/cpns/TimePeriodForm/config'; | 25 | import { OrderByEnum } from '/@/views/device/localtion/cpns/TimePeriodForm/config'; |
26 | + import { useApp } from '../../hooks/useApp'; | ||
26 | 27 | ||
27 | type DeviceOption = Record<'label' | 'value' | 'organizationId', string>; | 28 | type DeviceOption = Record<'label' | 'value' | 'organizationId', string>; |
28 | 29 | ||
@@ -38,6 +39,8 @@ | @@ -38,6 +39,8 @@ | ||
38 | 39 | ||
39 | const historyData = ref<{ ts: number; value: string; name: string }[]>([]); | 40 | const historyData = ref<{ ts: number; value: string; name: string }[]>([]); |
40 | 41 | ||
42 | + const { getIsAppPage } = useApp(); | ||
43 | + | ||
41 | const { deviceAttrs, getDeviceKeys, getSearchParams, setChartOptions, getDeviceAttribute } = | 44 | const { deviceAttrs, getDeviceKeys, getSearchParams, setChartOptions, getDeviceAttribute } = |
42 | useHistoryData(); | 45 | useHistoryData(); |
43 | 46 | ||
@@ -294,7 +297,7 @@ | @@ -294,7 +297,7 @@ | ||
294 | :destroy-on-close="true" | 297 | :destroy-on-close="true" |
295 | :show-ok-btn="false" | 298 | :show-ok-btn="false" |
296 | cancel-text="关闭" | 299 | cancel-text="关闭" |
297 | - width="70%" | 300 | + :width="getIsAppPage ? '100%' : '75%'" |
298 | title="历史趋势" | 301 | title="历史趋势" |
299 | > | 302 | > |
300 | <section | 303 | <section |
1 | +export const Platform = [ | ||
2 | + { key: 'iPhone 8', title: 'iPhone 8', width: 375, height: 667 }, | ||
3 | + { key: 'iPhone 8 Plus', title: 'iPhone 8 Plus', width: 415, height: 737 }, | ||
4 | + { key: 'iPhone X/XS', title: 'iPhone X/XS', width: 376, height: 813 }, | ||
5 | + { key: 'iPad 4', title: 'iPad 4', width: 709, height: 1025 }, | ||
6 | + { key: 'Galaxy S9', title: 'Galaxy S9', width: 361, height: 741 }, | ||
7 | + { key: 'Galaxy S10/S10+', title: 'Galaxy S10/S10+', width: 413, height: 870 }, | ||
8 | + { key: 'Pixel 2', title: 'Pixel 2', width: 413, height: 732 }, | ||
9 | + { key: 'custom', title: '自定义', width: '', height: '' }, | ||
10 | +]; |
@@ -9,6 +9,7 @@ | @@ -9,6 +9,7 @@ | ||
9 | 9 | ||
10 | const ROUTE = useRoute(); | 10 | const ROUTE = useRoute(); |
11 | const ROUTER = useRouter(); | 11 | const ROUTER = useRouter(); |
12 | + | ||
12 | const getIsSharePage = computed(() => { | 13 | const getIsSharePage = computed(() => { |
13 | return ROUTE.matched.find((item) => item.path === '/share/:viewType/:id/:publicId'); | 14 | return ROUTE.matched.find((item) => item.path === '/share/:viewType/:id/:publicId'); |
14 | }); | 15 | }); |
@@ -21,8 +22,6 @@ | @@ -21,8 +22,6 @@ | ||
21 | if (unref(getIsSharePage)) return; | 22 | if (unref(getIsSharePage)) return; |
22 | ROUTER.go(-1); | 23 | ROUTER.go(-1); |
23 | }; | 24 | }; |
24 | - | ||
25 | - // const handleOpenCreatePanel = () => {}; | ||
26 | </script> | 25 | </script> |
27 | 26 | ||
28 | <template> | 27 | <template> |
@@ -19,6 +19,7 @@ | @@ -19,6 +19,7 @@ | ||
19 | import { computed } from 'vue'; | 19 | import { computed } from 'vue'; |
20 | import { useGetComponentConfig } from '../../../packages/hook/useGetComponetConfig'; | 20 | import { useGetComponentConfig } from '../../../packages/hook/useGetComponetConfig'; |
21 | import { isBoolean } from '/@/utils/is'; | 21 | import { isBoolean } from '/@/utils/is'; |
22 | + import { useApp } from '../../hooks/useApp'; | ||
22 | 23 | ||
23 | const props = defineProps<{ | 24 | const props = defineProps<{ |
24 | sourceInfo: WidgetDataType; | 25 | sourceInfo: WidgetDataType; |
@@ -38,6 +39,8 @@ | @@ -38,6 +39,8 @@ | ||
38 | 39 | ||
39 | const { createMessage } = useMessage(); | 40 | const { createMessage } = useMessage(); |
40 | 41 | ||
42 | + const { getIsAppPage } = useApp(); | ||
43 | + | ||
41 | const { boardId } = useBoardId(); | 44 | const { boardId } = useBoardId(); |
42 | 45 | ||
43 | const dropMenuList = ref<AuthDropMenuList[]>([ | 46 | const dropMenuList = ref<AuthDropMenuList[]>([ |
@@ -171,7 +174,7 @@ | @@ -171,7 +174,7 @@ | ||
171 | <AreaChartOutlined v-else class="text-lg" @click="handleOpenTrendModal" /> | 174 | <AreaChartOutlined v-else class="text-lg" @click="handleOpenTrendModal" /> |
172 | </Tooltip> | 175 | </Tooltip> |
173 | <AuthDropDown | 176 | <AuthDropDown |
174 | - v-if="!isCustomerUser && dropMenuList.length" | 177 | + v-if="!isCustomerUser && dropMenuList.length && !getIsAppPage" |
175 | :drop-menu-list="dropMenuList" | 178 | :drop-menu-list="dropMenuList" |
176 | :trigger="['click']" | 179 | :trigger="['click']" |
177 | > | 180 | > |
@@ -7,6 +7,7 @@ | @@ -7,6 +7,7 @@ | ||
7 | import { BasicForm } from '/@/components/Form'; | 7 | import { BasicForm } from '/@/components/Form'; |
8 | import { BasicModal } from '/@/components/Modal'; | 8 | import { BasicModal } from '/@/components/Modal'; |
9 | import { nextTick } from 'vue'; | 9 | import { nextTick } from 'vue'; |
10 | + import { useApp } from '../../hooks/useApp'; | ||
10 | const emit = defineEmits(['register', 'getAlarmForm', 'getHistoryForm']); | 11 | const emit = defineEmits(['register', 'getAlarmForm', 'getHistoryForm']); |
11 | // const emit = defineEmits<{ | 12 | // const emit = defineEmits<{ |
12 | // (event: 'getAlarmForm', data: WidgetDataType): void; | 13 | // (event: 'getAlarmForm', data: WidgetDataType): void; |
@@ -15,6 +16,8 @@ | @@ -15,6 +16,8 @@ | ||
15 | // const fontId = ref(''); | 16 | // const fontId = ref(''); |
16 | const [registerModal, { closeModal }] = useModalInner(async () => {}); | 17 | const [registerModal, { closeModal }] = useModalInner(async () => {}); |
17 | 18 | ||
19 | + const { getIsAppPage } = useApp(); | ||
20 | + | ||
18 | const [register, method] = useForm({ | 21 | const [register, method] = useForm({ |
19 | schemas: formSchema(), | 22 | schemas: formSchema(), |
20 | baseColProps: useGridLayout(1) as unknown as ColEx, | 23 | baseColProps: useGridLayout(1) as unknown as ColEx, |
@@ -53,7 +56,7 @@ | @@ -53,7 +56,7 @@ | ||
53 | :destroy-on-close="true" | 56 | :destroy-on-close="true" |
54 | :show-ok-btn="true" | 57 | :show-ok-btn="true" |
55 | cancel-text="关闭" | 58 | cancel-text="关闭" |
56 | - width="40%" | 59 | + :width="getIsAppPage ? '80%' : '40%'" |
57 | title="历史趋势" | 60 | title="历史趋势" |
58 | > | 61 | > |
59 | <section | 62 | <section |
src/views/visual/palette/hooks/useApp.ts
0 → 100644
1 | +import { computed } from 'vue'; | ||
2 | +import { useRoute } from 'vue-router'; | ||
3 | + | ||
4 | +export const useApp = () => { | ||
5 | + const ROUTE = useRoute(); | ||
6 | + const getIsAppPage = computed(() => { | ||
7 | + return ROUTE.matched.find((item) => item.path === '/appPage/:boardId/:userId'); | ||
8 | + }); | ||
9 | + | ||
10 | + const isPhone = () => { | ||
11 | + const values = location?.pathname.split('/') || []; | ||
12 | + return values[values?.length - 2] === 'phone' ? true : false; | ||
13 | + }; | ||
14 | + | ||
15 | + return { getIsAppPage, isPhone }; | ||
16 | +}; |
1 | import { unref } from 'vue'; | 1 | import { unref } from 'vue'; |
2 | import { Layout } from 'vue3-grid-layout'; | 2 | import { Layout } from 'vue3-grid-layout'; |
3 | -import { DEFAULT_MAX_COL, DEFAULT_WIDGET_HEIGHT, DEFAULT_WIDGET_WIDTH } from '..'; | 3 | +import { DEFAULT_MAX_COL, DEFAULT_WIDGET_HEIGHT, DEFAULT_WIDGET_WIDTH, PHONE_SIZE } from '..'; |
4 | +import { useApp } from './useApp'; | ||
4 | 5 | ||
5 | interface GapRecord { | 6 | interface GapRecord { |
6 | maxGap: number; | 7 | maxGap: number; |
@@ -8,9 +9,14 @@ interface GapRecord { | @@ -8,9 +9,14 @@ interface GapRecord { | ||
8 | endIndex: Nullable<number>; | 9 | endIndex: Nullable<number>; |
9 | } | 10 | } |
10 | 11 | ||
12 | +const { isPhone } = useApp(); | ||
13 | + | ||
11 | export const useCalcNewWidgetPosition = ( | 14 | export const useCalcNewWidgetPosition = ( |
12 | layoutInfo: Layout[], | 15 | layoutInfo: Layout[], |
13 | - randomLayout = { width: DEFAULT_WIDGET_WIDTH, height: DEFAULT_WIDGET_HEIGHT } | 16 | + randomLayout = { |
17 | + width: isPhone() ? PHONE_SIZE.DEFAULT_WIDGET_WIDTH : DEFAULT_WIDGET_WIDTH, | ||
18 | + height: isPhone() ? PHONE_SIZE.DEFAULT_WIDGET_HEIGHT : DEFAULT_WIDGET_HEIGHT, | ||
19 | + } | ||
14 | ) => { | 20 | ) => { |
15 | let maxWidth = 0; | 21 | let maxWidth = 0; |
16 | let maxHeight = 0; | 22 | let maxHeight = 0; |
@@ -30,6 +30,10 @@ export const useDataSource = (propsRef: ComputedRef<Recordable>) => { | @@ -30,6 +30,10 @@ export const useDataSource = (propsRef: ComputedRef<Recordable>) => { | ||
30 | const getIsSharePage = computed(() => { | 30 | const getIsSharePage = computed(() => { |
31 | return ROUTE.matched.find((item) => item.path === '/share/:viewType/:id/:publicId'); | 31 | return ROUTE.matched.find((item) => item.path === '/share/:viewType/:id/:publicId'); |
32 | }); | 32 | }); |
33 | + //小程序打开的页面 | ||
34 | + const getIsAppPage = computed(() => { | ||
35 | + return ROUTE.matched.find((item) => item.path === '/appPage/:boardId/:userId'); | ||
36 | + }); | ||
33 | 37 | ||
34 | const getBoardId = computed(() => { | 38 | const getBoardId = computed(() => { |
35 | return decode((ROUTE.params as { boardId: string }).boardId); | 39 | return decode((ROUTE.params as { boardId: string }).boardId); |
@@ -131,8 +135,8 @@ export const useDataSource = (propsRef: ComputedRef<Recordable>) => { | @@ -131,8 +135,8 @@ export const useDataSource = (propsRef: ComputedRef<Recordable>) => { | ||
131 | 135 | ||
132 | return { | 136 | return { |
133 | loading, | 137 | loading, |
134 | - draggable: !unref(getIsSharePage), | ||
135 | - resizable: !unref(getIsSharePage), | 138 | + draggable: !unref(getIsSharePage) && !unref(getIsAppPage), |
139 | + resizable: !unref(getIsSharePage) && !unref(getIsAppPage), | ||
136 | dataSource, | 140 | dataSource, |
137 | rawDataSource, | 141 | rawDataSource, |
138 | getDataSource, | 142 | getDataSource, |
@@ -11,6 +11,12 @@ export const DEFAULT_WIDGET_HEIGHT = 6; | @@ -11,6 +11,12 @@ export const DEFAULT_WIDGET_HEIGHT = 6; | ||
11 | export const DEFAULT_MIN_HEIGHT = 5; | 11 | export const DEFAULT_MIN_HEIGHT = 5; |
12 | export const DEFAULT_MIN_WIDTH = 3; | 12 | export const DEFAULT_MIN_WIDTH = 3; |
13 | export const DEFAULT_ITEM_MARIGN = 20; | 13 | export const DEFAULT_ITEM_MARIGN = 20; |
14 | +export const PHONE_SIZE = { | ||
15 | + DEFAULT_WIDGET_WIDTH: 24, | ||
16 | + DEFAULT_WIDGET_HEIGHT: 5, | ||
17 | + DEFAULT_MIN_HEIGHT: 5, | ||
18 | + DEFAULT_MIN_WIDTH: 24, | ||
19 | +}; | ||
14 | 20 | ||
15 | import { ViewTypeEnum } from '/@/views/sys/share/config/config'; | 21 | import { ViewTypeEnum } from '/@/views/sys/share/config/config'; |
16 | 22 |
@@ -10,6 +10,7 @@ | @@ -10,6 +10,7 @@ | ||
10 | DEFAULT_MIN_WIDTH, | 10 | DEFAULT_MIN_WIDTH, |
11 | DEFAULT_ITEM_MARIGN, | 11 | DEFAULT_ITEM_MARIGN, |
12 | VisualComponentPermission, | 12 | VisualComponentPermission, |
13 | + PHONE_SIZE, | ||
13 | } from './index'; | 14 | } from './index'; |
14 | import { useDragGridLayout } from './hooks/useDragGridLayout'; | 15 | import { useDragGridLayout } from './hooks/useDragGridLayout'; |
15 | import { WidgetHeader, WidgetWrapper } from './components/WidgetWrapper'; | 16 | import { WidgetHeader, WidgetWrapper } from './components/WidgetWrapper'; |
@@ -19,6 +20,7 @@ | @@ -19,6 +20,7 @@ | ||
19 | import { DataSourceBindPanel } from '../dataSourceBindPanel'; | 20 | import { DataSourceBindPanel } from '../dataSourceBindPanel'; |
20 | import { PageHeader } from './components/PagerHeader'; | 21 | import { PageHeader } from './components/PagerHeader'; |
21 | import { useShare } from './hooks/useShare'; | 22 | import { useShare } from './hooks/useShare'; |
23 | + import { useApp } from './hooks/useApp'; | ||
22 | import { useRole } from '/@/hooks/business/useRole'; | 24 | import { useRole } from '/@/hooks/business/useRole'; |
23 | import { Authority } from '/@/components/Authority'; | 25 | import { Authority } from '/@/components/Authority'; |
24 | import { useModal } from '/@/components/Modal'; | 26 | import { useModal } from '/@/components/Modal'; |
@@ -33,6 +35,11 @@ | @@ -33,6 +35,11 @@ | ||
33 | import { useSocket } from '/@/views/visual/packages/hook/socket/useSocket'; | 35 | import { useSocket } from '/@/views/visual/packages/hook/socket/useSocket'; |
34 | import { createAlarmContext } from './hooks/useAlarmTime'; | 36 | import { createAlarmContext } from './hooks/useAlarmTime'; |
35 | import { createHistoryContext } from './hooks/useHistoryForm'; | 37 | import { createHistoryContext } from './hooks/useHistoryForm'; |
38 | + import { MoreOutlined, LeftOutlined } from '@ant-design/icons-vue'; | ||
39 | + import WIFISVG from '/@/assets/svg/wifi.svg'; | ||
40 | + import SIGNALSVG from '/@/assets/svg/signal.svg'; | ||
41 | + import BATTERYSVG from '/@/assets/svg/battery.svg'; | ||
42 | + import { useRoute } from 'vue-router'; | ||
36 | 43 | ||
37 | const props = defineProps<{ | 44 | const props = defineProps<{ |
38 | value?: Recordable; | 45 | value?: Recordable; |
@@ -44,6 +51,8 @@ | @@ -44,6 +51,8 @@ | ||
44 | 51 | ||
45 | const containerRectRef = ref<DOMRect>({} as unknown as DOMRect); | 52 | const containerRectRef = ref<DOMRect>({} as unknown as DOMRect); |
46 | 53 | ||
54 | + const ROUTE = useRoute(); | ||
55 | + | ||
47 | const { loading, draggable, resizable, dataSource, rawDataSource, setLayoutInfo, getDataSource } = | 56 | const { loading, draggable, resizable, dataSource, rawDataSource, setLayoutInfo, getDataSource } = |
48 | useDataSource(getProps); | 57 | useDataSource(getProps); |
49 | 58 | ||
@@ -63,6 +72,9 @@ | @@ -63,6 +72,9 @@ | ||
63 | } | 72 | } |
64 | }); | 73 | }); |
65 | const { getIsSharePage } = useShare(); | 74 | const { getIsSharePage } = useShare(); |
75 | + | ||
76 | + // getIsAppPage 是否是小程序进入的页面 isPhone 是否是创建的手机端 | ||
77 | + const { getIsAppPage, isPhone } = useApp(); | ||
66 | const { isCustomerUser } = useRole(); | 78 | const { isCustomerUser } = useRole(); |
67 | const handleOpenCreatePanel = () => { | 79 | const handleOpenCreatePanel = () => { |
68 | openModal(true, { mode: DataActionModeEnum.CREATE } as ModalParamsType); | 80 | openModal(true, { mode: DataActionModeEnum.CREATE } as ModalParamsType); |
@@ -157,6 +169,25 @@ | @@ -157,6 +169,25 @@ | ||
157 | }, | 169 | }, |
158 | { immediate: true } | 170 | { immediate: true } |
159 | ); | 171 | ); |
172 | + watch( | ||
173 | + getIsAppPage, | ||
174 | + (value) => { | ||
175 | + if (value) { | ||
176 | + const root = document.querySelector('#app'); | ||
177 | + (root as HTMLDivElement).style.backgroundColor = | ||
178 | + unref(getDarkMode) === ThemeEnum.LIGHT ? '#F5F5F5' : '#1b1b1b'; | ||
179 | + } | ||
180 | + }, | ||
181 | + { immediate: true } | ||
182 | + ); | ||
183 | + const getDataBoardName = computed(() => { | ||
184 | + return decodeURIComponent((ROUTE.params as { boardName: string }).boardName || ''); | ||
185 | + }); | ||
186 | + | ||
187 | + const phoneSize = ref({ | ||
188 | + width: 375, | ||
189 | + height: 667, | ||
190 | + }); | ||
160 | </script> | 191 | </script> |
161 | 192 | ||
162 | <template> | 193 | <template> |
@@ -164,7 +195,7 @@ | @@ -164,7 +195,7 @@ | ||
164 | ref="containerRefEl" | 195 | ref="containerRefEl" |
165 | class="palette w-full h-full flex-col bg-neutral-100 flex dark:bg-dark-700 dark:text-light-50" | 196 | class="palette w-full h-full flex-col bg-neutral-100 flex dark:bg-dark-700 dark:text-light-50" |
166 | > | 197 | > |
167 | - <PageHeader :widget-number="dataSource.length"> | 198 | + <PageHeader v-if="!getIsAppPage" :widget-number="dataSource.length"> |
168 | <Authority :value="VisualComponentPermission.CREATE"> | 199 | <Authority :value="VisualComponentPermission.CREATE"> |
169 | <Button | 200 | <Button |
170 | v-if="!getIsSharePage && !isCustomerUser" | 201 | v-if="!getIsSharePage && !isCustomerUser" |
@@ -177,55 +208,104 @@ | @@ -177,55 +208,104 @@ | ||
177 | </PageHeader> | 208 | </PageHeader> |
178 | 209 | ||
179 | <Spin :spinning="loading"> | 210 | <Spin :spinning="loading"> |
180 | - <GridLayout | ||
181 | - v-model:layout="dataSource" | ||
182 | - :col-num="DEFAULT_MAX_COL" | ||
183 | - :row-height="30" | ||
184 | - :margin="[DEFAULT_ITEM_MARIGN, DEFAULT_ITEM_MARIGN]" | ||
185 | - :is-draggable="draggable" | ||
186 | - :is-resizable="resizable" | ||
187 | - :vertical-compact="true" | ||
188 | - :use-css-transforms="true" | ||
189 | - style="width: 100%" | ||
190 | - > | ||
191 | - <GridItem | ||
192 | - v-for="item in dataSource" | ||
193 | - :key="item.i" | ||
194 | - :static="item.static" | ||
195 | - :x="item.x" | ||
196 | - :y="item.y" | ||
197 | - :w="item.w" | ||
198 | - :h="item.h" | ||
199 | - :i="item.i" | ||
200 | - :min-h="DEFAULT_MIN_HEIGHT" | ||
201 | - :min-w="DEFAULT_MIN_WIDTH" | ||
202 | - :style="{ display: 'flex', flexWrap: 'wrap' }" | ||
203 | - class="grid-item-layout" | ||
204 | - @resized="resized" | ||
205 | - @resize="resize" | ||
206 | - @moved="moved" | ||
207 | - @container-resized="containerResized" | ||
208 | - drag-ignore-from=".no-drag" | 211 | + <div class="w-full h-full flex justify-center items-center mb-3"> |
212 | + <div | ||
213 | + :style=" | ||
214 | + !getIsAppPage && isPhone() | ||
215 | + ? { | ||
216 | + width: phoneSize.width + 'px', | ||
217 | + height: phoneSize.height + 'px', | ||
218 | + border: '2px solid #e5e7eb', | ||
219 | + } | ||
220 | + : { width: '100%', height: '100%' } | ||
221 | + " | ||
222 | + style="border-radius: 1%" | ||
209 | > | 223 | > |
210 | - <WidgetWrapper> | ||
211 | - <template #header> | ||
212 | - <WidgetHeader | ||
213 | - :raw-data-source="rawDataSource" | ||
214 | - :source-info="item" | ||
215 | - @update="handleUpdateWidget" | ||
216 | - @open-trend="handleOpenTrend" | ||
217 | - @open-alarm="handleOpenAlarm" | ||
218 | - @ok="getDataSource" | ||
219 | - /> | ||
220 | - </template> | ||
221 | - <WidgetDistribute :source-info="item" /> | ||
222 | - </WidgetWrapper> | ||
223 | - </GridItem> | ||
224 | - </GridLayout> | ||
225 | - <Empty | ||
226 | - v-if="!dataSource.length" | ||
227 | - class="fixed top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2" | ||
228 | - /> | 224 | + <!-- 手机端的模拟样式 --> |
225 | + <div | ||
226 | + v-if="!getIsAppPage && isPhone()" | ||
227 | + style="height: 60px; background: white" | ||
228 | + class="px-1 py-1 relative" | ||
229 | + > | ||
230 | + <div class="flex justify-between"> | ||
231 | + <div>thingskit</div> | ||
232 | + <div class="flex items-center"> | ||
233 | + <img :src="WIFISVG" alt="" class="w-3 h-3" /> | ||
234 | + <img :src="SIGNALSVG" alt="" class="w-3 h-3 mx-1" /> | ||
235 | + <img :src="BATTERYSVG" alt="" class="w-4 h-4 rotate-45" /> | ||
236 | + <h1 class="mb-0">18:13</h1> | ||
237 | + </div> | ||
238 | + </div> | ||
239 | + <div class="flex items-center justify-between"> | ||
240 | + <LeftOutlined class="transform cursor-pointer text-lg" /> | ||
241 | + <h1 class="font-bold">{{ getDataBoardName }}</h1> | ||
242 | + <MoreOutlined class="transform rotate-90 cursor-pointer text-lg" /> | ||
243 | + </div> | ||
244 | + </div> | ||
245 | + | ||
246 | + <div | ||
247 | + id="appLayoutId" | ||
248 | + :style=" | ||
249 | + !getIsAppPage && isPhone() ? { height: `calc(${phoneSize.height}px - 67px)` } : {} | ||
250 | + " | ||
251 | + class="overflow-y-scroll" | ||
252 | + > | ||
253 | + <GridLayout | ||
254 | + v-model:layout="dataSource" | ||
255 | + :col-num="DEFAULT_MAX_COL" | ||
256 | + :row-height="30" | ||
257 | + :margin="[DEFAULT_ITEM_MARIGN, DEFAULT_ITEM_MARIGN]" | ||
258 | + :is-draggable="draggable" | ||
259 | + :is-resizable="resizable" | ||
260 | + :vertical-compact="true" | ||
261 | + :use-css-transforms="true" | ||
262 | + style="width: 100%" | ||
263 | + :style="{ | ||
264 | + '--is-app': getIsAppPage ? 'auto' : 'none', | ||
265 | + }" | ||
266 | + > | ||
267 | + <GridItem | ||
268 | + v-for="item in dataSource" | ||
269 | + :key="item.i" | ||
270 | + :static="item.static" | ||
271 | + :x="item.x" | ||
272 | + :y="item.y" | ||
273 | + :w="item.w" | ||
274 | + :h="item.h" | ||
275 | + :i="item.i" | ||
276 | + :min-h="isPhone() ? PHONE_SIZE.DEFAULT_MIN_HEIGHT : DEFAULT_MIN_HEIGHT" | ||
277 | + :min-w="isPhone() ? PHONE_SIZE.DEFAULT_MIN_WIDTH : DEFAULT_MIN_WIDTH" | ||
278 | + :style="{ display: 'flex', flexWrap: 'wrap' }" | ||
279 | + class="grid-item-layout" | ||
280 | + @resized="resized" | ||
281 | + @resize="resize" | ||
282 | + @moved="moved" | ||
283 | + @container-resized="containerResized" | ||
284 | + drag-ignore-from=".no-drag" | ||
285 | + > | ||
286 | + <WidgetWrapper> | ||
287 | + <template #header> | ||
288 | + <WidgetHeader | ||
289 | + :raw-data-source="rawDataSource" | ||
290 | + :source-info="item" | ||
291 | + @update="handleUpdateWidget" | ||
292 | + @open-trend="handleOpenTrend" | ||
293 | + @open-alarm="handleOpenAlarm" | ||
294 | + @ok="getDataSource" | ||
295 | + /> | ||
296 | + </template> | ||
297 | + <WidgetDistribute :source-info="item" /> | ||
298 | + </WidgetWrapper> | ||
299 | + </GridItem> | ||
300 | + </GridLayout> | ||
301 | + </div> | ||
302 | + <Empty | ||
303 | + v-if="!dataSource.length" | ||
304 | + :class="isPhone() ? 'absolute' : 'fixed'" | ||
305 | + class="top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2" | ||
306 | + /> | ||
307 | + </div> | ||
308 | + </div> | ||
229 | </Spin> | 309 | </Spin> |
230 | 310 | ||
231 | <DataSourceBindPanel @register="register" :layout="dataSource" @ok="getDataSource" /> | 311 | <DataSourceBindPanel @register="register" :layout="dataSource" @ok="getDataSource" /> |
@@ -238,4 +318,9 @@ | @@ -238,4 +318,9 @@ | ||
238 | </section> | 318 | </section> |
239 | </template> | 319 | </template> |
240 | 320 | ||
241 | -<style lang="less" scoped></style> | 321 | +<style lang="less"> |
322 | + .vue-grid-item { | ||
323 | + pointer-events: painted; | ||
324 | + touch-action: var(--is-app) !important; | ||
325 | + } | ||
326 | +</style> |
@@ -91,6 +91,7 @@ export interface ComponentLayoutType { | @@ -91,6 +91,7 @@ export interface ComponentLayoutType { | ||
91 | export interface ApiDataBoardDataType { | 91 | export interface ApiDataBoardDataType { |
92 | componentData: ComponentDataType[]; | 92 | componentData: ComponentDataType[]; |
93 | componentLayout: ComponentLayoutType[]; | 93 | componentLayout: ComponentLayoutType[]; |
94 | + phoneModel?: string; | ||
94 | } | 95 | } |
95 | 96 | ||
96 | export interface ApiDataBoardInfoType { | 97 | export interface ApiDataBoardInfoType { |