feat: 移动端

This commit is contained in:
dataeaseShu 2024-01-17 17:26:19 +08:00
parent 916a7ec456
commit 0b74a8ec36
21 changed files with 774 additions and 7 deletions

View File

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>DataEase</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/pages/mobile/main.ts"></script>
</body>
</html>

View File

@ -42,6 +42,7 @@
"qs": "^6.11.0", "qs": "^6.11.0",
"snowflake-id": "^1.1.0", "snowflake-id": "^1.1.0",
"tinymce": "^5.8.2", "tinymce": "^5.8.2",
"vant": "^4.8.2",
"vue": "^3.3.4", "vue": "^3.3.4",
"vue-clipboard3": "^2.0.0", "vue-clipboard3": "^2.0.0",
"vue-codemirror": "^6.1.1", "vue-codemirror": "^6.1.1",

View File

@ -182,6 +182,10 @@ const multiplexingCanvasOpen = () => {
multiplexingRef.value.dialogInit() multiplexingRef.value.dialogInit()
} }
const mobileConfig = () => {
useEmitt().emitter.emit('mobileConfig')
}
eventBus.on('preview', previewInner) eventBus.on('preview', previewInner)
eventBus.on('save', saveCanvasWithCheck) eventBus.on('save', saveCanvasWithCheck)
eventBus.on('clearCanvas', clearCanvas) eventBus.on('clearCanvas', clearCanvas)
@ -417,6 +421,12 @@ onMounted(() => {
is-label is-label
@customClick="multiplexingCanvasOpen" @customClick="multiplexingCanvasOpen"
></component-button-label> ></component-button-label>
<component-button-label
icon-name="icon_copy_filled"
title="移动端"
is-label
@customClick="mobileConfig"
></component-button-label>
</div> </div>
</template> </template>

View File

@ -1,6 +1,7 @@
<template> <template>
<div <div
class="bar-main" class="bar-main"
v-if="!mobileInPc"
:class="[ :class="[
showEditPosition, showEditPosition,
{ {
@ -249,7 +250,7 @@ const props = defineProps({
}) })
const { element, index, showPosition, canvasId } = toRefs(props) const { element, index, showPosition, canvasId } = toRefs(props)
const { batchOptStatus, pcMatrixCount, curComponent, componentData, canvasViewInfo } = const { batchOptStatus, pcMatrixCount, curComponent, componentData, canvasViewInfo, mobileInPc } =
storeToRefs(dvMainStore) storeToRefs(dvMainStore)
const state = reactive({ const state = reactive({

View File

@ -118,7 +118,7 @@ import DeCustomTab from '@/custom-component/de-tabs/DeCustomTab.vue'
import DePreview from '@/components/data-visualization/canvas/DePreview.vue' import DePreview from '@/components/data-visualization/canvas/DePreview.vue'
import { useEmitt } from '@/hooks/web/useEmitt' import { useEmitt } from '@/hooks/web/useEmitt'
const dvMainStore = dvMainStoreWithOut() const dvMainStore = dvMainStoreWithOut()
const { tabMoveInActiveId, bashMatrixInfo, editMode } = storeToRefs(dvMainStore) const { tabMoveInActiveId, bashMatrixInfo, editMode, mobileInPc } = storeToRefs(dvMainStore)
const tabComponentRef = ref(null) const tabComponentRef = ref(null)
const props = defineProps({ const props = defineProps({
@ -168,7 +168,7 @@ const editableTabsValue = ref(null)
const noBorderColor = ref('none') const noBorderColor = ref('none')
let currentInstance let currentInstance
const isEditMode = computed(() => editMode.value === 'edit' && isEdit.value) const isEditMode = computed(() => editMode.value === 'edit' && isEdit.value && !mobileInPc.value)
const calcTabLength = () => { const calcTabLength = () => {
setTimeout(() => { setTimeout(() => {

View File

@ -53,7 +53,7 @@ const props = defineProps({
const { element, view, scale } = toRefs(props) const { element, view, scale } = toRefs(props)
const { t } = useI18n() const { t } = useI18n()
const dvMainStore = dvMainStoreWithOut() const dvMainStore = dvMainStoreWithOut()
const { curComponent, canvasViewInfo } = storeToRefs(dvMainStore) const { curComponent, canvasViewInfo, mobileInPc } = storeToRefs(dvMainStore)
const canEdit = ref(false) const canEdit = ref(false)
const queryConfig = ref() const queryConfig = ref()
const defaultStyle = { const defaultStyle = {
@ -346,7 +346,11 @@ const autoStyle = computed(() => {
<div v-if="!listVisible.length" class="no-list-label flex-align-center"> <div v-if="!listVisible.length" class="no-list-label flex-align-center">
<div class="container flex-align-center"> <div class="container flex-align-center">
将右侧的字段拖拽到这里 点击 将右侧的字段拖拽到这里 点击
<el-button :disabled="showPosition === 'preview'" @click="addCriteriaConfigOut" text> <el-button
:disabled="showPosition === 'preview' || mobileInPc"
@click="addCriteriaConfigOut"
text
>
添加查询条件 添加查询条件
</el-button> </el-button>
</div> </div>

View File

@ -0,0 +1,8 @@
<script setup lang="ts">
import configGlobal from '@/components/config-global/src/ConfigGlobal.vue'
</script>
<template>
<config-global>
<router-view />
</config-global>
</template>

View File

@ -0,0 +1,28 @@
import { createApp } from 'vue'
import '../../assets/font/index.css'
import '@/style/index.less'
import '@/plugins/svg-icon'
import 'normalize.css/normalize.css'
import App from './App.vue'
import { setupI18n } from '@/plugins/vue-i18n'
import { setupStore } from '@/store'
import { setupRouter } from '@/router/mobile'
import { setupElementPlus, setupElementPlusIcons } from '@/plugins/element-plus'
// 注册数据大屏组件
import { setupCustomComponent } from '@/custom-component'
import { installDirective } from '@/directive'
import '@/utils/DateUtil'
import '@/permissionMobile'
const setupAll = async () => {
const app = createApp(App)
installDirective(app)
await setupI18n(app)
setupStore(app)
setupRouter(app)
setupElementPlus(app)
setupCustomComponent(app)
setupElementPlusIcons(app)
app.mount('#app')
}
setupAll()

View File

@ -0,0 +1,44 @@
import router from './router/mobile'
import { useUserStoreWithOut } from '@/store/modules/user'
import { useNProgress } from '@/hooks/web/useNProgress'
import { usePageLoading } from '@/hooks/web/usePageLoading'
import { useCache } from '@/hooks/web/useCache'
import { getRoleRouters } from '@/api/common'
import { usePermissionStoreWithOut } from '@/store/modules/permission'
import { interactiveStoreWithOut } from '@/store/modules/interactive'
const permissionStore = usePermissionStoreWithOut()
const { wsCache } = useCache()
const userStore = useUserStoreWithOut()
const { start, done } = useNProgress()
const interactiveStore = interactiveStoreWithOut()
const { loadStart, loadDone } = usePageLoading()
router.beforeEach(async (to, _, next) => {
start()
loadStart()
if (wsCache.get('user.token')) {
if (!userStore.getUid) {
await userStore.setUser()
}
if (to.path === '/login') {
next({ path: '/index' })
} else {
const roleRouters = (await getRoleRouters()) || []
const routers: any[] = roleRouters as AppCustomRouteRecordRaw[]
routers.forEach(item => (item['top'] = true))
await permissionStore.generateRoutes(routers as AppCustomRouteRecordRaw[])
permissionStore.setIsAddRouters(true)
await interactiveStore.initInteractive(true)
next()
}
} else {
next('/login') // 否则全部重定向到登录页
}
})
router.afterEach(() => {
done()
loadDone()
})

View File

@ -0,0 +1,45 @@
import { createRouter, createWebHashHistory } from 'vue-router'
import type { RouteRecordRaw } from 'vue-router'
import type { App } from 'vue'
export const routes: AppRouteRecordRaw[] = [
{
path: '/',
name: '/',
redirect: '/index',
hidden: true,
meta: {}
},
{
path: '/index',
name: 'index',
component: () => import('@/views/mobile/index.vue'),
hidden: true,
meta: {}
},
{
path: '/login',
name: 'login',
hidden: true,
meta: {},
component: () => import('@/views/mobile/login/index.vue')
},
{
path: '/panel',
name: 'panel',
hidden: true,
meta: {},
component: () => import('@/views/mobile/panel/index.vue')
}
]
const router = createRouter({
history: createWebHashHistory(),
routes: routes as RouteRecordRaw[]
})
export const setupRouter = (app: App<Element>) => {
app.use(router)
}
export default router

View File

@ -29,6 +29,7 @@ export const dvMainStore = defineStore('dataVisualization', {
datasetAreaCollapse: false datasetAreaCollapse: false
}, },
editMode: 'edit', // 编辑器模式 edit preview editMode: 'edit', // 编辑器模式 edit preview
mobileInPc: false,
canvasStyleData: { ...deepCopy(DEFAULT_CANVAS_STYLE_DATA_DARK), backgroundColor: null }, canvasStyleData: { ...deepCopy(DEFAULT_CANVAS_STYLE_DATA_DARK), backgroundColor: null },
// 当前展示画布缓存数据 // 当前展示画布缓存数据
componentDataCache: null, componentDataCache: null,
@ -196,6 +197,9 @@ export const dvMainStore = defineStore('dataVisualization', {
setEditMode(mode) { setEditMode(mode) {
this.editMode = mode this.editMode = mode
}, },
setMobileInPc(mobileInPc) {
this.mobileInPc = mobileInPc
},
setInEditorStatus(status) { setInEditorStatus(status) {
this.isInEditor = status this.isInEditor = status

View File

@ -104,6 +104,7 @@ const hasCurrentRouter = (locations, routers, index) => {
kids = router.children kids = router.children
return router.path === location || '/' + location === router.path return router.path === location || '/' + location === router.path
}) })
if (isvalid && index < locations.length - 1) { if (isvalid && index < locations.length - 1) {
return hasCurrentRouter(locations, kids, index + 1) return hasCurrentRouter(locations, kids, index + 1)
} }

View File

@ -0,0 +1,205 @@
<script lang="ts" setup>
import { ref, onMounted, unref, onBeforeUnmount, computed } from 'vue'
import { useEmitt } from '@/hooks/web/useEmitt'
import { dvMainStoreWithOut } from '@/store/modules/data-visualization/dvMain'
import { getStyle } from '@/utils/style'
import findComponent from '@/utils/components'
import { storeToRefs } from 'pinia'
const dvMainStore = dvMainStoreWithOut()
const { componentData, canvasStyleData, canvasViewInfo, dvInfo } = storeToRefs(dvMainStore)
const mobileLoading = ref(true)
const activeName = ref('hide')
const emits = defineEmits(['pcMode'])
const getComponentStyleDefault = style => {
return getStyle(style, ['top', 'left', 'width', 'height', 'rotate'])
}
const mobileStatusChange = (type, value) => {
const iframe = document.querySelector('iframe')
if (iframe) {
iframe.contentWindow.postMessage(
{
type,
value
},
'*'
)
}
}
const handleLoad = () => {
mobileLoading.value = false
console.log('dvInfo', JSON.parse(JSON.stringify(unref(dvInfo))))
setTimeout(() => {
mobileStatusChange(
'panelInit',
JSON.parse(
JSON.stringify({
componentData: JSON.parse(JSON.stringify(unref(componentData))),
canvasStyleData: JSON.parse(JSON.stringify(unref(canvasStyleData))),
canvasViewInfo: JSON.parse(JSON.stringify(unref(canvasViewInfo))),
dvInfo: JSON.parse(JSON.stringify(unref(dvInfo)))
})
)
)
}, 800)
}
const componentDataNotInMobile = computed(() => {
return componentData.value.filter(ele => !ele.inMobile)
})
onMounted(() => {
dvMainStore.setMobileInPc(true)
useEmitt({
name: 'onMobileStatusChange',
callback: (type, value) => {
mobileStatusChange(type, value)
}
})
})
onBeforeUnmount(() => {
dvMainStore.setMobileInPc(false)
})
const addToMobile = com => {
com.inMobile = true
mobileStatusChange('addToMobile', JSON.parse(JSON.stringify(unref(com))))
}
</script>
<template>
<div class="mobile-config-panel">
<div class="mobile-to-pc">
<el-icon class="custom-el-icon back-icon" @click="emits('pcMode')">
<Icon class="toolbar-icon" name="icon_left_outlined" />
</el-icon>
</div>
<div class="mobile-canvas">
<div class="config-panel-title">{{ dvInfo.name }}</div>
<div class="config-panel-content" v-loading="mobileLoading">
<iframe
src="./mobile.html#/panel"
frameborder="0"
width="360"
height="570"
@load="handleLoad"
/>
</div>
<div class="config-panel-foot"></div>
</div>
<div class="mobile-com-list">
<el-tabs v-model="activeName">
<el-tab-pane label="隐藏的组件" name="hide" />
<el-tab-pane label="样式设置" name="style" />
</el-tabs>
<template v-if="activeName === 'hide'">
<div
:style="{ height: '200px', width: '31%' }"
class="mobile-wrapper-inner-adaptor"
v-for="config in componentDataNotInMobile"
:key="config.id"
>
<component
:is="findComponent(config['component'])"
ref="component"
class="component"
:view="canvasViewInfo[config.id]"
:canvas-style-data="canvasStyleData"
:dv-info="dvInfo"
:dv-type="dvInfo.type"
:canvas-view-info="canvasViewInfo"
:prop-value="config?.propValue"
:element="config"
:request="config?.request"
:style="getComponentStyleDefault(config?.style)"
:linkage="config?.linkage"
show-position="preview"
:disabled="true"
:is-edit="false"
/>
<div class="mobile-com-mask"></div>
<div class="pc-select-to-mobile" @click="addToMobile(config)"></div>
</div>
</template>
</div>
</div>
</template>
<style lang="less" scoped>
.mobile-config-panel {
height: 100vh;
width: 100vw;
position: relative;
.mobile-canvas {
border-radius: 30px;
width: 370px;
min-height: 600px;
max-height: 700px;
overflow: hidden;
background-color: #000;
background-size: 100% 100% !important;
padding: 5px;
height: 777px;
position: absolute;
top: 100px;
left: 100px;
.config-panel-title {
margin-top: 30px;
background: #fff;
text-align: center;
}
}
.mobile-com-list {
position: absolute;
top: 100px;
left: 500px;
width: calc(100% - 600px);
max-height: 700px;
display: flex;
flex-wrap: wrap;
background: #f5f6f7;
padding: 16px;
padding-top: 0;
:deep(.ed-tabs) {
width: 100%;
margin-bottom: 16px;
}
.mobile-wrapper-inner-adaptor {
position: relative;
margin-right: 24px;
margin-bottom: 24px;
background: #fff;
}
.mobile-com-mask {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
z-index: 10;
}
.pc-select-to-mobile {
position: absolute;
width: 16px;
height: 16px;
top: 0;
right: 0;
border: 2px solid #ccc;
border-radius: 4px;
z-index: 11;
cursor: pointer;
}
}
}
</style>

View File

@ -5,6 +5,8 @@ import { storeToRefs } from 'pinia'
import findComponent from '../../utils/components' import findComponent from '../../utils/components'
import DvSidebar from '../../components/visualization/DvSidebar.vue' import DvSidebar from '../../components/visualization/DvSidebar.vue'
import router from '@/router' import router from '@/router'
import MobileConfigPanel from './MobileConfigPanel.vue'
import { useEmitt } from '@/hooks/web/useEmitt'
import DbToolbar from '@/components/dashboard/DbToolbar.vue' import DbToolbar from '@/components/dashboard/DbToolbar.vue'
import ViewEditor from '@/views/chart/components/editor/index.vue' import ViewEditor from '@/views/chart/components/editor/index.vue'
import { getDatasetTree } from '@/api/dataset' import { getDatasetTree } from '@/api/dataset'
@ -29,7 +31,6 @@ const eventCheck = e => {
} }
const dvMainStore = dvMainStoreWithOut() const dvMainStore = dvMainStoreWithOut()
const snapshotStore = snapshotStoreWithOut() const snapshotStore = snapshotStoreWithOut()
const { const {
componentData, componentData,
curComponent, curComponent,
@ -68,8 +69,20 @@ const checkPer = async resourceId => {
await interactiveStore.setInteractive(request) await interactiveStore.setInteractive(request)
return check(wsCache.get('panel-weight'), resourceId, 4) return check(wsCache.get('panel-weight'), resourceId, 4)
} }
const mobileConfig = ref(false)
const onMobileConfig = () => {
mobileConfig.value = true
}
// //
onMounted(async () => { onMounted(async () => {
useEmitt({
name: 'mobileConfig',
callback: () => {
onMobileConfig()
}
})
window.addEventListener('storage', eventCheck) window.addEventListener('storage', eventCheck)
const { resourceId, opt, pid, createType } = window.DataEaseBi || router.currentRoute.value.query const { resourceId, opt, pid, createType } = window.DataEaseBi || router.currentRoute.value.query
const checkResult = await checkPer(resourceId) const checkResult = await checkPer(resourceId)
@ -123,7 +136,7 @@ onUnmounted(() => {
</script> </script>
<template> <template>
<div class="dv-common-layout dv-teleport-query"> <div class="dv-common-layout dv-teleport-query" v-if="!mobileConfig">
<DbToolbar /> <DbToolbar />
<el-container <el-container
class="dv-layout-container" class="dv-layout-container"
@ -189,6 +202,7 @@ onUnmounted(() => {
</dv-sidebar> </dv-sidebar>
</el-container> </el-container>
</div> </div>
<MobileConfigPanel @pcMode="mobileConfig = false" v-else></MobileConfigPanel>
</template> </template>
<style lang="less"> <style lang="less">

View File

@ -0,0 +1,117 @@
<script lang="ts" setup>
import { ref, computed, onMounted } from 'vue'
import { storeToRefs } from 'pinia'
import { dvMainStoreWithOut } from '@/store/modules/data-visualization/dvMain'
import { BusiTreeRequest } from '@/models/tree/TreeNode'
import { interactiveStoreWithOut } from '@/store/modules/interactive'
import VanCell from 'vant/es/cell'
import VanSticky from 'vant/es/sticky'
import VanSearch from 'vant/es/search'
import VanCellGroup from 'vant/es/cell-group'
import VanNavBar from 'vant/es/nav-bar'
import 'vant/es/cell/style'
import 'vant/es/cell-group/style'
import 'vant/es/nav-bar/style'
import 'vant/es/sticky/style'
import 'vant/es/search/style'
const keywords = ref()
const anyManage = ref(false)
const rootManage = ref(false)
const tableData = ref([])
const directName = ref([])
const directId = ref([])
const activeDirectName = ref('')
const interactiveStore = interactiveStoreWithOut()
const dvMainStore = dvMainStoreWithOut()
const { dvInfo } = storeToRefs(dvMainStore)
const dfsTree = (ids, arr) => {
const id = ids.shift()
return arr.reduce((pre, ele) => {
if (id && ele.id === id) {
pre = ele.children
}
const children = dfsTree([...ids], ele.children || [])
if (children?.length) {
pre = children
}
return pre
}, [])
}
const activeTableData = computed(() => {
return directId.value.length ? dfsTree([...directId.value], tableData.value) : tableData.value
})
const emits = defineEmits(['hiddentabbar', 'setLoading'])
const onClickLeft = () => {
activeDirectName.value = directName.value.pop()
directId.value.pop()
if (!!directName.value.length) {
emits('hiddentabbar', false)
}
}
const dataClick = val => {
directName.value.push(val.name)
activeDirectName.value = val.name
directId.value.push(val.id)
if (directName.value.length === 1) {
emits('hiddentabbar', true)
}
}
const getTree = async () => {
const request = { busiFlag: 'dashboard' } as BusiTreeRequest
await interactiveStore.setInteractive(request)
const interactiveData = interactiveStore.getPanel
const nodeData = interactiveData.treeNodes
rootManage.value = interactiveData.rootManage
anyManage.value = interactiveData.anyManage
if (dvInfo.value && dvInfo.value.id && !JSON.stringify(nodeData).includes(dvInfo.value.id)) {
dvMainStore.resetDvInfo()
}
if (nodeData.length && nodeData[0]['id'] === '0' && nodeData[0]['name'] === 'root') {
tableData.value = nodeData[0]['children'] || []
return
}
tableData.value = nodeData
}
onMounted(() => {
getTree()
})
</script>
<template>
<div>
<van-sticky>
<van-search
v-if="!directName.length"
v-model="keywords"
shape="round"
placeholder="请输入仪表板名称"
/>
<van-nav-bar
v-else
:title="activeDirectName"
left-text="返回"
left-arrow
@click-left="onClickLeft"
/>
</van-sticky>
<van-cell-group>
<van-cell
v-for="ele in activeTableData"
:key="ele.id"
size="large"
@click="dataClick(ele)"
:title="ele.name"
:is-link="!ele.leaf"
:icon="ele.leaf ? 'bar-chart-o' : 'list-switch'"
/>
</van-cell-group>
</div>
</template>
<style lang="less" scoped></style>

View File

@ -0,0 +1,114 @@
<script lang="ts" setup>
import { ref, computed, onMounted, reactive } from 'vue'
import { interactiveStoreWithOut } from '@/store/modules/interactive'
import { shortcutOption } from '@/views/workbranch/ShortcutOption'
import { XpackComponent } from '@/components/plugin'
import VanTabs from 'vant/es/tabs'
import VanTab from 'vant/es/tab'
import VanCell from 'vant/es/cell'
import VanSticky from 'vant/es/sticky'
import VanCellGroup from 'vant/es/cell-group'
import 'vant/es/sticky/style'
import 'vant/es/tab/style'
import 'vant/es/tabs/style'
import 'vant/es/cell/style'
import 'vant/es/cell-group/style'
const activeTab = ref('recent')
const state = reactive({
tableData: [],
curTypeList: []
})
const interactiveStore = interactiveStoreWithOut()
const emits = defineEmits(['setLoading'])
const loadTableData = () => {
emits('setLoading', true)
shortcutOption
.loadData({ type: 'panel', keyword: '', asc: false })
.then(res => {
state.tableData = res.data
})
.finally(() => {
emits('setLoading', false)
})
}
const tablePaneList = ref([
{ title: '最近使用', name: 'recent', disabled: false },
{ title: '我的收藏', name: 'store', disabled: false }
])
const panelLoad = paneInfo => {
tablePaneList.value.push({
title: paneInfo.title,
name: paneInfo.name,
disabled: tablePaneList.value[1].disabled
})
}
const busiDataMap = computed(() => interactiveStore.getData)
const getBusiListWithPermission = () => {
const baseFlagList = ['panel', 'screen', 'dataset', 'datasource']
const busiFlagList: string[] = []
for (const key in busiDataMap.value) {
if (busiDataMap.value[key].menuAuth) {
busiFlagList.push(baseFlagList[parseInt(key)])
}
}
tablePaneList.value[0].disabled = !busiFlagList?.length
tablePaneList.value[1].disabled =
!busiFlagList.includes('panel') && !busiFlagList.includes('screen')
return busiFlagList
}
const busiAuthList = getBusiListWithPermission()
const handleClick = ({ name, disabled }) => {
if (disabled) return
if (name === 'recent' || name === 'store') {
emits('setLoading', true)
shortcutOption.setBusiFlag(name)
loadTableData()
}
}
onMounted(() => {
!!busiAuthList.length &&
handleClick({
name: 'recent',
disabled: false
})
})
const formatterTime = val => {
return new Date(val).toLocaleString()
}
</script>
<template>
<div class="mobile-panel-list">
<van-sticky>
<van-tabs @click-tab="handleClick" v-model:active="activeTab">
<van-tab
v-for="item in tablePaneList"
:key="item.name"
:disabled="item.disabled"
:name="item.name"
:title="item.title"
></van-tab>
</van-tabs>
</van-sticky>
<van-cell-group>
<van-cell
v-for="ele in state.tableData"
:key="ele.id"
size="large"
:title="ele.name"
:value="formatterTime(ele.lastEditTime)"
icon="bar-chart-o"
/>
</van-cell-group>
<div style="width: 100%; height: 50px"></div>
<XpackComponent jsname="c2hhcmUtcGFuZWw=" @loaded="panelLoad" />
</div>
</template>

View File

@ -0,0 +1,51 @@
<script lang="ts" setup>
import { ref } from 'vue'
import Home from './home/index.vue'
import Directory from './directory/index.vue'
import VanTabbar from 'vant/es/tabbar'
import VanTabbarItem from 'vant/es/tabbar-item'
import VanCell from 'vant/es/cell'
import VanSticky from 'vant/es/sticky'
import VanOverlay from 'vant/es/overlay'
import VanLoading from 'vant/es/loading'
import VanCellGroup from 'vant/es/cell-group'
import 'vant/es/tabbar-item/style'
import 'vant/es/tabbar/style'
import 'vant/es/sticky/style'
import 'vant/es/overlay/style'
import 'vant/es/cell/style'
import 'vant/es/loading/style'
import 'vant/es/cell-group/style'
const activeTabbar = ref('home')
const showLoading = ref(false)
const hiddenTabbar = ref(false)
</script>
<template>
<div class="mobile-index">
<Home v-if="activeTabbar === 'home'" @setLoading="val => (showLoading = val)"></Home>
<Directory
v-else-if="activeTabbar === 'direct'"
@setLoading="val => (showLoading = val)"
@hiddenTabbar="val => (hiddenTabbar = val)"
></Directory>
<van-tabbar v-if="!hiddenTabbar" v-model="activeTabbar">
<van-tabbar-item name="home" icon="wap-home-o">首页</van-tabbar-item>
<van-tabbar-item name="direct" icon="bars">目录</van-tabbar-item>
<van-tabbar-item name="user" icon="user-o">我的</van-tabbar-item>
</van-tabbar>
<van-overlay v-model:show="showLoading">
<div style="display: flex; align-items: center; justify-content: center; height: 100%">
<van-loading type="spinner" />
</div>
</van-overlay>
</div>
</template>
<style lang="less" scoped>
.mobile-index {
width: 100%;
height: 100%;
}
</style>

View File

@ -0,0 +1,9 @@
<script lang="ts" setup>
import { ref } from 'vue'
</script>
<template>
<div></div>
</template>
<style lang="less" scoped></style>

View File

@ -0,0 +1,33 @@
<script setup lang="ts">
import { reactive } from 'vue'
import { dvMainStoreWithOut } from '@/store/modules/data-visualization/dvMain'
import { storeToRefs } from 'pinia'
import DeCanvas from '@/views/canvas/DeCanvas.vue'
const dvMainStore = dvMainStoreWithOut()
const { componentData, canvasStyleData, canvasViewInfo } = storeToRefs(dvMainStore)
const state = reactive({
canvasId: 'canvas-main'
})
</script>
<template>
<div class="dv-common-layout-mobile">
<de-canvas
style="overflow-x: hidden"
:canvas-id="state.canvasId"
:component-data="componentData"
:canvas-style-data="canvasStyleData"
:canvas-view-info="canvasViewInfo"
></de-canvas>
</div>
</template>
<style lang="less">
.dv-common-layout-mobile {
height: 100vh;
width: 100vw;
overflow-y: auto;
}
</style>

View File

@ -0,0 +1,56 @@
<script lang="ts" setup>
import { onBeforeMount, ref, onBeforeUnmount } from 'vue'
import { dvMainStoreWithOut } from '@/store/modules/data-visualization/dvMain'
const dvMainStore = dvMainStoreWithOut()
import DePreviewMobile from './MobileInPc.vue'
const panelInit = ref(false)
const checkItemPosition = component => {
component.x = 1
component.sizeX = 36
component.y = dvMainStore.componentData.reduce((pre, next) => {
return Math.max(pre, next.y + next.sizeY)
}, 1)
}
const hanedleMessage = event => {
if (event.data.type === 'panelInit') {
const { componentData, canvasStyleData, dvInfo, canvasViewInfo } = event.data.value
dvMainStore.setComponentData(componentData.filter(ele => !!ele.inMobile))
dvMainStore.setMobileInPc(true)
dvMainStore.setCanvasStyle(canvasStyleData)
dvMainStore.updateCurDvInfo(dvInfo)
dvMainStore.setCanvasViewInfo(canvasViewInfo)
panelInit.value = true
}
if (event.data.type === 'addToMobile') {
console.log('event.data.value', event.data.value)
const component = event.data.value
checkItemPosition(component)
dvMainStore.componentData.push(component)
}
}
onBeforeMount(() => {
window.addEventListener('message', hanedleMessage)
})
onBeforeUnmount(() => {
window.removeEventListener('message', hanedleMessage)
})
</script>
<template>
<div class="panel-mobile">
<de-preview-mobile v-if="panelInit"></de-preview-mobile>
</div>
</template>
<style lang="less" scoped>
.panel-mobile {
width: 100vw;
height: 100vh;
overflow-y: auto;
}
</style>

View File

@ -0,0 +1,9 @@
<script lang="ts" setup>
import { ref } from 'vue'
</script>
<template>
<div></div>
</template>
<style lang="less" scoped></style>