Commit d89a9f019ec82f8edcef07193ab8d7c3303f6f5d
Merge branch 'perf/visual-app' into 'main_dev'
feat:看板添加手机端逻辑 See merge request yunteng/thingskit-front!1122
Showing
27 changed files
with
462 additions
and
66 deletions
| @@ -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 { |
| @@ -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/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> |
| @@ -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 | ]; |
| @@ -105,7 +105,7 @@ export const getFormSchemas = (hasAlarmNotify: Ref<boolean>): FormSchema[] => { | @@ -105,7 +105,7 @@ export const getFormSchemas = (hasAlarmNotify: Ref<boolean>): FormSchema[] => { | ||
| 105 | field: FormFieldsEnum.ALARM_PROFILED, | 105 | field: FormFieldsEnum.ALARM_PROFILED, |
| 106 | label: '', | 106 | label: '', |
| 107 | component: 'AlarmProfileSelect', | 107 | component: 'AlarmProfileSelect', |
| 108 | - rules: [{ required: true, message: `请选择${FormFieldsEnum.ALARM_PROFILED}` }], | 108 | + // rules: [{ required: true, message: `请选择${FormFieldsEnum.ALARM_PROFILED}` }], |
| 109 | ifShow: ({ model }) => model[FormFieldsEnum.OUT_TARGET] === ExecutionActionEnum.MSG_NOTIFY, | 109 | ifShow: ({ model }) => model[FormFieldsEnum.OUT_TARGET] === ExecutionActionEnum.MSG_NOTIFY, |
| 110 | componentProps: () => { | 110 | componentProps: () => { |
| 111 | return { | 111 | return { |
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> |
| 1 | <script lang="ts" setup> | 1 | <script lang="ts" setup> |
| 2 | import { BasicModal, useModalInner } from '/@/components/Modal'; | 2 | import { BasicModal, useModalInner } from '/@/components/Modal'; |
| 3 | import { BasicForm, useForm } from '/@/components/Form'; | 3 | import { BasicForm, useForm } from '/@/components/Form'; |
| 4 | - import { formSchema } from '../config/panelDetail'; | 4 | + import { formSchema, PlatformType } from '../config/panelDetail'; |
| 5 | import { addDataBoard, updateDataBoard } from '/@/api/dataBoard'; | 5 | import { addDataBoard, updateDataBoard } from '/@/api/dataBoard'; |
| 6 | import { AddDataBoardParams } from '/@/api/dataBoard/model'; | 6 | import { AddDataBoardParams } from '/@/api/dataBoard/model'; |
| 7 | import { useMessage } from '/@/hooks/web/useMessage'; | 7 | import { useMessage } from '/@/hooks/web/useMessage'; |
| @@ -34,6 +34,9 @@ | @@ -34,6 +34,9 @@ | ||
| 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 | + if (value.platform === PlatformType.PC) { | ||
| 38 | + Reflect.deleteProperty(value, 'phoneModel'); | ||
| 39 | + } | ||
| 37 | return value as any; | 40 | return value as any; |
| 38 | }; | 41 | }; |
| 39 | 42 |
| @@ -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', |
| @@ -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"> |
| @@ -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 | +]; |
| 1 | <script lang="ts" setup> | 1 | <script lang="ts" setup> |
| 2 | - import { PageHeader } from 'ant-design-vue'; | ||
| 3 | - import { computed, unref } from 'vue'; | 2 | + import { PageHeader, RadioButton, RadioGroup, InputNumber, Button } from 'ant-design-vue'; |
| 3 | + import { computed, ref, unref, onMounted } from 'vue'; | ||
| 4 | import { useRoute, useRouter } from 'vue-router'; | 4 | import { useRoute, useRouter } from 'vue-router'; |
| 5 | import { decode } from '../..'; | 5 | import { decode } from '../..'; |
| 6 | import { RollbackOutlined } from '@ant-design/icons-vue'; | 6 | import { RollbackOutlined } from '@ant-design/icons-vue'; |
| 7 | + import { Platform } from './config'; | ||
| 8 | + import { useApp } from '../../hooks/useApp'; | ||
| 9 | + import { getDataComponent } from '/@/api/dataBoard'; | ||
| 10 | + import { updateDataBoard } from '/@/api/dataBoard'; | ||
| 11 | + import { useMessage } from '/@/hooks/web/useMessage'; | ||
| 7 | 12 | ||
| 8 | defineProps<{ widgetNumber: number }>(); | 13 | defineProps<{ widgetNumber: number }>(); |
| 14 | + const emits = defineEmits(['getPhoneSize']); | ||
| 9 | 15 | ||
| 10 | const ROUTE = useRoute(); | 16 | const ROUTE = useRoute(); |
| 11 | const ROUTER = useRouter(); | 17 | const ROUTER = useRouter(); |
| 18 | + | ||
| 19 | + const { isPhone } = useApp(); | ||
| 12 | const getIsSharePage = computed(() => { | 20 | const getIsSharePage = computed(() => { |
| 13 | return ROUTE.matched.find((item) => item.path === '/share/:viewType/:id/:publicId'); | 21 | return ROUTE.matched.find((item) => item.path === '/share/:viewType/:id/:publicId'); |
| 14 | }); | 22 | }); |
| @@ -22,7 +30,85 @@ | @@ -22,7 +30,85 @@ | ||
| 22 | ROUTER.go(-1); | 30 | ROUTER.go(-1); |
| 23 | }; | 31 | }; |
| 24 | 32 | ||
| 25 | - // const handleOpenCreatePanel = () => {}; | 33 | + const getSize = () => { |
| 34 | + return { | ||
| 35 | + width: '', | ||
| 36 | + height: '', | ||
| 37 | + }; | ||
| 38 | + }; | ||
| 39 | + | ||
| 40 | + const phoneRadio = ref<string>('iPhone 8'); | ||
| 41 | + const phoneSize = ref<{ width?: number | string; height?: number | string }>(getSize()); | ||
| 42 | + const ifShowWidth = ref<boolean>(false); | ||
| 43 | + | ||
| 44 | + const phoneModel = ref<{ | ||
| 45 | + key: string; | ||
| 46 | + width: string | number; | ||
| 47 | + height: number | string; | ||
| 48 | + title: string; | ||
| 49 | + }>(); | ||
| 50 | + const getDataList = async () => { | ||
| 51 | + const values = await getDataComponent(decode((ROUTE.params as { boardId: string }).boardId)); | ||
| 52 | + phoneModel.value = values?.data.phoneModel && JSON.parse(values?.data.phoneModel as string); | ||
| 53 | + phoneRadio.value = unref(phoneModel)?.key as any; | ||
| 54 | + ifShowWidth.value = unref(phoneRadio) === 'custom' ? true : false; | ||
| 55 | + phoneSize.value = { | ||
| 56 | + width: unref(phoneModel)?.width, | ||
| 57 | + height: unref(phoneModel)?.height, | ||
| 58 | + }; | ||
| 59 | + | ||
| 60 | + emits('getPhoneSize', unref(phoneSize)); | ||
| 61 | + }; | ||
| 62 | + | ||
| 63 | + const updateComponent = () => { | ||
| 64 | + const { boardId, boardName, organizationId, platform } = ROUTE.params; | ||
| 65 | + | ||
| 66 | + const values = { | ||
| 67 | + id: boardId, | ||
| 68 | + boardName, | ||
| 69 | + organizationId, | ||
| 70 | + platform, | ||
| 71 | + phoneModel: JSON.stringify({ | ||
| 72 | + key: unref(phoneRadio), | ||
| 73 | + ...phoneSize.value, | ||
| 74 | + }), | ||
| 75 | + }; | ||
| 76 | + updateDataBoard(values as any); | ||
| 77 | + }; | ||
| 78 | + | ||
| 79 | + const handleChange = (e) => { | ||
| 80 | + const { target } = e || {}; | ||
| 81 | + phoneSize.value = getSize(); | ||
| 82 | + if (target.value === 'custom') { | ||
| 83 | + ifShowWidth.value = true; | ||
| 84 | + return; | ||
| 85 | + } else { | ||
| 86 | + ifShowWidth.value = false; | ||
| 87 | + const values = Platform.find((item) => item.key == target.value); | ||
| 88 | + phoneSize.value = { | ||
| 89 | + width: values?.width || '', | ||
| 90 | + height: values?.height || '', | ||
| 91 | + }; | ||
| 92 | + } | ||
| 93 | + updateComponent(); | ||
| 94 | + emits('getPhoneSize', unref(phoneSize)); | ||
| 95 | + }; | ||
| 96 | + | ||
| 97 | + const { createMessage } = useMessage(); | ||
| 98 | + | ||
| 99 | + const handleCustom = () => { | ||
| 100 | + const { width, height } = unref(phoneSize); | ||
| 101 | + if (!width || !height) { | ||
| 102 | + createMessage.warning('请填写尺寸大小'); | ||
| 103 | + return; | ||
| 104 | + } | ||
| 105 | + emits('getPhoneSize', unref(phoneSize)); | ||
| 106 | + updateComponent(); | ||
| 107 | + }; | ||
| 108 | + | ||
| 109 | + onMounted(() => { | ||
| 110 | + getDataList(); | ||
| 111 | + }); | ||
| 26 | </script> | 112 | </script> |
| 27 | 113 | ||
| 28 | <template> | 114 | <template> |
| @@ -46,6 +132,18 @@ | @@ -46,6 +132,18 @@ | ||
| 46 | <span class="mr-3 text-sm text-gray-500">已创建组件:</span> | 132 | <span class="mr-3 text-sm text-gray-500">已创建组件:</span> |
| 47 | <span class="text-blue-500"> {{ widgetNumber }}个</span> | 133 | <span class="text-blue-500"> {{ widgetNumber }}个</span> |
| 48 | </div> | 134 | </div> |
| 135 | + <div v-if="isPhone()" class="flex items-center my-1"> | ||
| 136 | + <RadioGroup v-model:value="phoneRadio" button-style="solid" @change="handleChange"> | ||
| 137 | + <RadioButton v-for="item in Platform" :value="item.key" :key="item.key">{{ | ||
| 138 | + item.title | ||
| 139 | + }}</RadioButton> | ||
| 140 | + </RadioGroup> | ||
| 141 | + <div class="ml-1" v-if="ifShowWidth"> | ||
| 142 | + <InputNumber v-model:value="phoneSize.width" :min="300" placeholder="请输入宽度" /> | ||
| 143 | + <InputNumber v-model:value="phoneSize.height" :min="300" placeholder="请输入高度" /> | ||
| 144 | + <Button type="primary" @click="handleCustom" class="ml-1">确定</Button> | ||
| 145 | + </div> | ||
| 146 | + </div> | ||
| 49 | </PageHeader> | 147 | </PageHeader> |
| 50 | </template> | 148 | </template> |
| 51 | 149 |
| @@ -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: 12, | ||
| 16 | + DEFAULT_WIDGET_HEIGHT: 4, | ||
| 17 | + DEFAULT_MIN_HEIGHT: 4, | ||
| 18 | + DEFAULT_MIN_WIDTH: 11, | ||
| 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,28 @@ | @@ -157,6 +169,28 @@ | ||
| 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 | + }); | ||
| 191 | + const getPhoneSize = (values) => { | ||
| 192 | + phoneSize.value = { ...values }; | ||
| 193 | + }; | ||
| 160 | </script> | 194 | </script> |
| 161 | 195 | ||
| 162 | <template> | 196 | <template> |
| @@ -164,7 +198,11 @@ | @@ -164,7 +198,11 @@ | ||
| 164 | ref="containerRefEl" | 198 | ref="containerRefEl" |
| 165 | class="palette w-full h-full flex-col bg-neutral-100 flex dark:bg-dark-700 dark:text-light-50" | 199 | class="palette w-full h-full flex-col bg-neutral-100 flex dark:bg-dark-700 dark:text-light-50" |
| 166 | > | 200 | > |
| 167 | - <PageHeader :widget-number="dataSource.length"> | 201 | + <PageHeader |
| 202 | + v-if="!getIsAppPage" | ||
| 203 | + :widget-number="dataSource.length" | ||
| 204 | + @getPhoneSize="getPhoneSize" | ||
| 205 | + > | ||
| 168 | <Authority :value="VisualComponentPermission.CREATE"> | 206 | <Authority :value="VisualComponentPermission.CREATE"> |
| 169 | <Button | 207 | <Button |
| 170 | v-if="!getIsSharePage && !isCustomerUser" | 208 | v-if="!getIsSharePage && !isCustomerUser" |
| @@ -177,55 +215,104 @@ | @@ -177,55 +215,104 @@ | ||
| 177 | </PageHeader> | 215 | </PageHeader> |
| 178 | 216 | ||
| 179 | <Spin :spinning="loading"> | 217 | <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" | 218 | + <div class="w-full h-full flex justify-center items-center mb-3"> |
| 219 | + <div | ||
| 220 | + :style=" | ||
| 221 | + !getIsAppPage && isPhone() | ||
| 222 | + ? { | ||
| 223 | + width: phoneSize.width + 'px', | ||
| 224 | + height: phoneSize.height + 'px', | ||
| 225 | + border: '2px solid #e5e7eb', | ||
| 226 | + } | ||
| 227 | + : { width: '100%', height: '100%' } | ||
| 228 | + " | ||
| 229 | + style="border-radius: 1%" | ||
| 209 | > | 230 | > |
| 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 | - /> | 231 | + <!-- 手机端写的模拟样式 --> |
| 232 | + <div | ||
| 233 | + v-if="!getIsAppPage && isPhone()" | ||
| 234 | + style="height: 60px; background: white" | ||
| 235 | + class="px-1 py-1 relative" | ||
| 236 | + > | ||
| 237 | + <div class="flex justify-between"> | ||
| 238 | + <div>thingskit</div> | ||
| 239 | + <div class="flex items-center"> | ||
| 240 | + <img :src="WIFISVG" alt="" class="w-3 h-3" /> | ||
| 241 | + <img :src="SIGNALSVG" alt="" class="w-3 h-3 mx-1" /> | ||
| 242 | + <img :src="BATTERYSVG" alt="" class="w-4 h-4 rotate-45" /> | ||
| 243 | + <h1 class="mb-0">18:13</h1> | ||
| 244 | + </div> | ||
| 245 | + </div> | ||
| 246 | + <div class="flex items-center justify-between"> | ||
| 247 | + <LeftOutlined class="transform cursor-pointer text-lg" /> | ||
| 248 | + <h1 class="font-bold">{{ getDataBoardName }}</h1> | ||
| 249 | + <MoreOutlined class="transform rotate-90 cursor-pointer text-lg" /> | ||
| 250 | + </div> | ||
| 251 | + </div> | ||
| 252 | + | ||
| 253 | + <div | ||
| 254 | + id="appLayoutId" | ||
| 255 | + :style=" | ||
| 256 | + !getIsAppPage && isPhone() ? { height: `calc(${phoneSize.height}px - 67px)` } : {} | ||
| 257 | + " | ||
| 258 | + class="overflow-y-scroll" | ||
| 259 | + > | ||
| 260 | + <GridLayout | ||
| 261 | + v-model:layout="dataSource" | ||
| 262 | + :col-num="DEFAULT_MAX_COL" | ||
| 263 | + :row-height="30" | ||
| 264 | + :margin="[DEFAULT_ITEM_MARIGN, DEFAULT_ITEM_MARIGN]" | ||
| 265 | + :is-draggable="draggable" | ||
| 266 | + :is-resizable="resizable" | ||
| 267 | + :vertical-compact="true" | ||
| 268 | + :use-css-transforms="true" | ||
| 269 | + style="width: 100%" | ||
| 270 | + :style="{ | ||
| 271 | + '--is-app': isPhone() ? 'auto' : 'none', | ||
| 272 | + }" | ||
| 273 | + > | ||
| 274 | + <GridItem | ||
| 275 | + v-for="item in dataSource" | ||
| 276 | + :key="item.i" | ||
| 277 | + :static="item.static" | ||
| 278 | + :x="item.x" | ||
| 279 | + :y="item.y" | ||
| 280 | + :w="item.w" | ||
| 281 | + :h="item.h" | ||
| 282 | + :i="item.i" | ||
| 283 | + :min-h="isPhone() ? PHONE_SIZE.DEFAULT_MIN_HEIGHT : DEFAULT_MIN_HEIGHT" | ||
| 284 | + :min-w="isPhone() ? PHONE_SIZE.DEFAULT_MIN_WIDTH : DEFAULT_MIN_WIDTH" | ||
| 285 | + :style="{ display: 'flex', flexWrap: 'wrap' }" | ||
| 286 | + class="grid-item-layout" | ||
| 287 | + @resized="resized" | ||
| 288 | + @resize="resize" | ||
| 289 | + @moved="moved" | ||
| 290 | + @container-resized="containerResized" | ||
| 291 | + drag-ignore-from=".no-drag" | ||
| 292 | + > | ||
| 293 | + <WidgetWrapper> | ||
| 294 | + <template #header> | ||
| 295 | + <WidgetHeader | ||
| 296 | + :raw-data-source="rawDataSource" | ||
| 297 | + :source-info="item" | ||
| 298 | + @update="handleUpdateWidget" | ||
| 299 | + @open-trend="handleOpenTrend" | ||
| 300 | + @open-alarm="handleOpenAlarm" | ||
| 301 | + @ok="getDataSource" | ||
| 302 | + /> | ||
| 303 | + </template> | ||
| 304 | + <WidgetDistribute :source-info="item" /> | ||
| 305 | + </WidgetWrapper> | ||
| 306 | + </GridItem> | ||
| 307 | + </GridLayout> | ||
| 308 | + </div> | ||
| 309 | + <Empty | ||
| 310 | + v-if="!dataSource.length" | ||
| 311 | + :class="isPhone() ? 'absolute' : 'fixed'" | ||
| 312 | + class="top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2" | ||
| 313 | + /> | ||
| 314 | + </div> | ||
| 315 | + </div> | ||
| 229 | </Spin> | 316 | </Spin> |
| 230 | 317 | ||
| 231 | <DataSourceBindPanel @register="register" :layout="dataSource" @ok="getDataSource" /> | 318 | <DataSourceBindPanel @register="register" :layout="dataSource" @ok="getDataSource" /> |
| @@ -238,4 +325,9 @@ | @@ -238,4 +325,9 @@ | ||
| 238 | </section> | 325 | </section> |
| 239 | </template> | 326 | </template> |
| 240 | 327 | ||
| 241 | -<style lang="less" scoped></style> | 328 | +<style lang="less"> |
| 329 | + .vue-grid-item { | ||
| 330 | + pointer-events: painted; | ||
| 331 | + touch-action: var(--is-app) !important; | ||
| 332 | + } | ||
| 333 | +</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 { |