Merge pull request #8056 from dataease/pr@dev-v2_dzz_mobile

Pr@dev v2 dzz mobile
This commit is contained in:
dataeaseShu 2024-02-19 16:17:57 +08:00 committed by GitHub
commit 3c113c4673
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
33 changed files with 1496 additions and 19 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",
"snowflake-id": "^1.1.0",
"tinymce": "^5.8.2",
"vant": "^4.8.3",
"vue": "^3.3.4",
"vue-clipboard3": "^2.0.0",
"vue-codemirror": "^6.1.1",

View File

@ -32,7 +32,7 @@ export function uploadFileResult(file, callback) {
const fileUrl = staticResourcePath + newFileName
const param = new FormData()
param.append('file', file)
uploadFile(fileId, param).then(() => {
return uploadFile(fileId, param).then(() => {
callback(fileUrl)
})
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 561 KiB

View File

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

View File

@ -13,7 +13,7 @@ import { isMainCanvas } from '@/utils/canvasUtils'
import { activeWatermark } from '@/components/watermark/watermark'
import { personInfoApi } from '@/api/user'
const dvMainStore = dvMainStoreWithOut()
const { pcMatrixCount, curComponent } = storeToRefs(dvMainStore)
const { pcMatrixCount, curComponent, mobileInPc } = storeToRefs(dvMainStore)
const props = defineProps({
canvasStyleData: {
@ -274,7 +274,7 @@ defineExpose({
:style="getShapeItemShowStyle(item)"
:show-position="showPosition"
:search-count="searchCount"
:scale="scaleWidth"
:scale="mobileInPc ? scaleWidth * 3 : scaleWidth"
@userViewEnlargeOpen="userViewEnlargeOpen($event, item)"
/>
<user-view-enlarge ref="userViewEnlargeRef"></user-view-enlarge>

View File

@ -1,5 +1,13 @@
<template>
<div class="shape" ref="shapeInnerRef" :id="domId" @dblclick="handleDbClick">
<div v-if="showCheck" class="del-from-mobile" @click="delFromMobile">
<label class="el-checkbox el-checkbox--small is-checked"
><span class="el-checkbox__input is-checked"
><input class="el-checkbox__original" checked type="checkbox" /><span
class="el-checkbox__inner"
></span></span
></label>
</div>
<div
class="shape-outer"
v-show="contentDisplay"
@ -113,7 +121,8 @@ const {
curLinkageView,
tabCollisionActiveId,
tabMoveInActiveId,
tabMoveOutComponentId
tabMoveOutComponentId,
mobileInPc
} = storeToRefs(dvMainStore)
const { editorMap, areaData, isCtrlOrCmdDown } = storeToRefs(composeStore)
const emit = defineEmits([
@ -126,6 +135,7 @@ const emit = defineEmits([
'linkJumpSetOpen',
'linkageSetOpen'
])
const isEditMode = computed(() => editMode.value === 'edit')
const state = reactive({
seriesIdMap: {
@ -247,6 +257,17 @@ const initialAngle = {
}
const cursors = ref({})
const showCheck = computed(() => {
return mobileInPc.value && element.value.canvasId === 'canvas-main'
})
const delFromMobile = () => {
useEmitt().emitter.emit('onMobileStatusChange', {
type: 'delFromMobile',
value: element.value.id
})
}
const angleToCursor = [
//
{ start: 338, end: 23, cursor: 'nw' },
@ -902,6 +923,13 @@ onMounted(() => {
<style lang="less" scoped>
.shape {
position: absolute;
.del-from-mobile {
position: absolute;
right: 5px;
top: 5px;
z-index: 2;
}
}
.shape-shadow {

View File

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

View File

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

View File

@ -53,7 +53,7 @@ const props = defineProps({
const { element, view, scale } = toRefs(props)
const { t } = useI18n()
const dvMainStore = dvMainStoreWithOut()
const { curComponent, canvasViewInfo } = storeToRefs(dvMainStore)
const { curComponent, canvasViewInfo, mobileInPc } = storeToRefs(dvMainStore)
const canEdit = ref(false)
const queryConfig = ref()
const defaultStyle = {
@ -346,7 +346,11 @@ const autoStyle = computed(() => {
<div v-if="!listVisible.length" class="no-list-label 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>
</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

@ -7,6 +7,7 @@ import { usePermissionStoreWithOut, pathValid, getFirstAuthMenu } from '@/store/
import { usePageLoading } from '@/hooks/web/usePageLoading'
import { getRoleRouters } from '@/api/common'
import { useCache } from '@/hooks/web/useCache'
import { isMobile } from '@/utils/utils'
import { interactiveStoreWithOut } from '@/store/modules/interactive'
const { wsCache } = useCache()
const permissionStore = usePermissionStoreWithOut()
@ -23,6 +24,12 @@ const whiteList = ['/login', '/de-link', '/chart-view'] // 不重定向白名单
router.beforeEach(async (to, from, next) => {
start()
loadStart()
if (isMobile()) {
done()
loadDone()
window.location.href = window.origin + '/mobile.html#/index'
}
let isDesktop = wsCache.get('app.desktop')
if (isDesktop === null) {
await appStore.setAppModel()

View File

@ -0,0 +1,49 @@
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()
const whiteList = ['/login'] // 不重定向白名单
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 {
if (whiteList.includes(to.path)) {
next()
} else {
next('/login') // 否则全部重定向到登录页
}
}
})
router.afterEach(() => {
done()
loadDone()
})

View File

@ -0,0 +1,52 @@
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')
},
{
path: '/panel/mobile',
name: 'mobile',
hidden: true,
meta: {},
component: () => import('@/views/mobile/panel/Mobile.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
},
editMode: 'edit', // 编辑器模式 edit preview
mobileInPc: false,
canvasStyleData: { ...deepCopy(DEFAULT_CANVAS_STYLE_DATA_DARK), backgroundColor: null },
// 当前展示画布缓存数据
componentDataCache: null,
@ -196,6 +197,9 @@ export const dvMainStore = defineStore('dataVisualization', {
setEditMode(mode) {
this.editMode = mode
},
setMobileInPc(mobileInPc) {
this.mobileInPc = mobileInPc
},
setInEditorStatus(status) {
this.isInEditor = status

View File

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

View File

@ -147,6 +147,41 @@ export function initCanvasData(dvId, busiFlag, callBack) {
)
}
export function initCanvasDataMobile(dvId, busiFlag, callBack) {
initCanvasDataPrepare(
dvId,
busiFlag,
function ({ canvasDataResult, canvasStyleResult, dvInfo, canvasViewInfoPreview }) {
const componentData = canvasDataResult.filter(ele => !!ele.inMobile)
canvasDataResult.forEach(ele => {
const { mx, my, mSizeX, mSizeY } = ele
ele.x = mx
ele.y = my
ele.sizeX = mSizeX
ele.sizeY = mSizeY
})
dvMainStore.setComponentData(componentData)
dvMainStore.setCanvasStyle(canvasStyleResult)
dvMainStore.updateCurDvInfo(dvInfo)
dvMainStore.setCanvasViewInfo(canvasViewInfoPreview)
// 刷新联动信息
getPanelAllLinkageInfo(dvInfo.id).then(rsp => {
dvMainStore.setNowPanelTrackInfo(rsp.data)
})
// 刷新跳转信息
queryVisualizationJumpInfo(dvInfo.id).then(rsp => {
dvMainStore.setNowPanelJumpInfo(rsp.data)
})
callBack({
canvasDataResult: componentData,
canvasStyleResult,
dvInfo,
canvasViewInfoPreview
})
}
)
}
export function checkIsBatchOptView(viewId) {
return curBatchOptComponents.value.includes(viewId)
}

View File

@ -182,8 +182,14 @@ export function getComponentRotatedStyle(style) {
}
export function getCanvasStyle(canvasStyleData) {
const { backgroundColorSelect, background, backgroundColor, backgroundImageEnable, fontSize } =
canvasStyleData
const {
backgroundColorSelect,
background,
backgroundColor,
backgroundImageEnable,
fontSize,
mobileSetting
} = canvasStyleData
const style = { fontSize: fontSize + 'px', color: canvasStyleData.color }
// 仪表板默认色#f5f6f7 大屏默认配色 #1a1a1a
let colorRGBA = dvMainStore.dvInfo.type === 'dashboard' ? '#f5f6f7' : '#1a1a1a'
@ -195,6 +201,17 @@ export function getCanvasStyle(canvasStyleData) {
} else {
style['background-color'] = colorRGBA
}
if (dvMainStore.mobileInPc && mobileSetting?.customSetting) {
const { backgroundType, color, alpha, imageUrl } = mobileSetting
if (backgroundType === 'image' && typeof imageUrl === 'string') {
style['background'] = `url(${imgUrlTrans(imageUrl)}) no-repeat`
} else if (backgroundType === 'color') {
const colorRGBA = hexColorToRGBA(color, alpha === undefined ? 100 : alpha)
style['background'] = colorRGBA
}
}
console.log('style', style)
return style
}

View File

@ -83,6 +83,11 @@ export const isLarkPlatform = () => {
return !!getQueryString('state') && !!getQueryString('code')
}
export function isMobile() {
return navigator.userAgent.match(
/(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i
)
}
export function cutTargetTree(tree: BusiTreeNode[], targetId: string | number) {
tree.forEach((node, index) => {
if (node.id === targetId) {

View File

@ -0,0 +1,208 @@
<script lang="ts" setup>
import { dvMainStoreWithOut } from '@/store/modules/data-visualization/dvMain'
import { ref, reactive, unref, onMounted, watch } from 'vue'
import { COLOR_PANEL } from '@/views/chart/components/editor/util/chart'
import { imgUrlTrans } from '@/utils/imgUtils'
import ImgViewDialog from '@/custom-component/ImgViewDialog.vue'
import { storeToRefs } from 'pinia'
import { useEmitt } from '@/hooks/web/useEmitt'
import { beforeUploadCheck, uploadFileResult } from '@/api/staticResource'
import { snapshotStoreWithOut } from '@/store/modules/data-visualization/snapshot'
import { cloneDeep } from 'lodash-es'
const MOBILE_SETTING = {
customSetting: false,
color: '#ffffff',
alpha: 100,
imageUrl: null,
backgroundType: 'image'
}
const fileList = ref([])
const mobileSetting = reactive({ ...MOBILE_SETTING })
const dvMainStore = dvMainStoreWithOut()
const snapshotStore = snapshotStoreWithOut()
const { canvasStyleData } = storeToRefs(dvMainStore)
const init = () => {
const { imageUrl } = canvasStyleData.value.mobileSetting || {}
fileList.value = imageUrl ? [{ url: imgUrlTrans(imageUrl) }] : []
}
onMounted(() => {
init()
})
const commitStyle = () => {
const canvasStyleDataCopy = cloneDeep(canvasStyleData.value)
canvasStyleDataCopy.mobileSetting = unref(mobileSetting)
dvMainStore.setCanvasStyle(canvasStyleDataCopy)
useEmitt().emitter.emit('onMobileStatusChange', {
type: 'setCanvasStyle',
value: JSON.parse(JSON.stringify(unref(canvasStyleDataCopy)))
})
snapshotStore.recordSnapshotCache()
}
const upload = file => {
return uploadFileResult(file.file, fileUrl => {
mobileSetting.imageUrl = fileUrl
commitStyle()
})
}
const dialogVisible = ref(false)
const dialogImageUrl = ref('')
const handlePictureCardPreview = file => {
dialogImageUrl.value = file.url
dialogVisible.value = true
}
const handleRemove = () => {
mobileSetting.imageUrl = null
dialogImageUrl.value = ''
fileList.value = []
commitStyle()
}
</script>
<template>
<div class="mobile-bg-setting">
<div class="is-custom">
<el-checkbox v-model="mobileSetting.customSetting" label="自定义移动端背景" />
</div>
<div class="bg-type">
<el-radio-group
v-model="mobileSetting.backgroundType"
:disabled="!mobileSetting.customSetting"
@change="commitStyle"
>
<el-radio label="color">{{ $t('chart.color') }}</el-radio>
<el-radio label="image">图片</el-radio>
</el-radio-group>
</div>
<div v-show="mobileSetting.backgroundType === 'color'" class="bg-color-config">
<el-color-picker
v-model="mobileSetting.color"
:predefine="COLOR_PANEL"
:disabled="!mobileSetting.customSetting"
@change="commitStyle"
/>
<span> 不透明度 </span>
<el-slider
v-model="mobileSetting.alpha"
show-input
:show-input-controls="false"
@change="commitStyle"
/>
</div>
<div
:class="{
disabled: !mobileSetting.customSetting
}"
v-show="mobileSetting.backgroundType === 'image'"
class="upload-img"
>
<el-upload
action=""
accept=".jpeg,.jpg,.png,.gif,.svg"
class="avatar-uploader"
list-type="picture-card"
:http-request="upload"
:on-preview="handlePictureCardPreview"
:on-remove="handleRemove"
:before-upload="beforeUploadCheck"
:file-list="fileList"
:disabled="!mobileSetting.customSetting"
>
<el-icon><Plus /></el-icon>
</el-upload>
<img-view-dialog v-model="dialogVisible" :image-url="dialogImageUrl" />
</div>
<div v-show="mobileSetting.backgroundType === 'image'" class="image-hint">
当前支持jpeg,jpg,png,gif,svg文件,大小15M内
</div>
</div>
</template>
<style lang="less" scoped>
.mobile-bg-setting {
width: 50%;
.bg-color-config {
margin-top: 10px;
display: flex;
align-items: center;
& > :nth-child(2) {
margin: 0 20px;
white-space: nowrap;
}
}
.avatar-uploader {
width: 90px;
height: 80px;
overflow: hidden;
}
.avatar-uploader {
width: 90px;
:deep(.ed-upload) {
width: 80px;
height: 80px;
line-height: 90px;
}
:deep(.ed-upload-list li) {
width: 80px !important;
height: 80px !important;
}
:deep(.ed-upload--picture-card) {
background: #eff0f1;
border: 1px dashed #dee0e3;
border-radius: 4px;
.ed-icon {
color: #1f2329;
}
&:hover {
.ed-icon {
color: #3370ff;
}
}
}
}
.upload-img {
margin-top: 8px;
&.disabled {
cursor: not-allowed;
color: #8f959e;
:deep(.avatar-uploader) {
width: 90px;
pointer-events: none;
}
:deep(.ed-upload--picture-card) {
cursor: not-allowed;
.ed-icon {
color: #bbbfc4;
}
}
&:hover {
.ed-icon {
color: #8f959e;
}
}
}
}
.image-hint {
color: #8f959e;
font-size: 14px;
line-height: 22px;
font-weight: 400;
margin-top: 8px;
}
}
</style>

View File

@ -0,0 +1,250 @@
<script lang="ts" setup>
import { ref, onMounted, unref, onBeforeUnmount, computed } from 'vue'
import eventBus from '@/utils/eventBus'
import MobileBackgroundSelector from './MobileBackgroundSelector.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 = () => {
mobileStatusChange(
'panelInit',
JSON.parse(
JSON.stringify({
componentData: JSON.parse(
JSON.stringify(unref(componentData.value.filter(ele => !!ele.inMobile)))
),
canvasStyleData: JSON.parse(JSON.stringify(unref(canvasStyleData))),
canvasViewInfo: JSON.parse(JSON.stringify(unref(canvasViewInfo))),
dvInfo: JSON.parse(JSON.stringify(unref(dvInfo)))
})
)
)
}
const componentDataNotInMobile = computed(() => {
return componentData.value.filter(ele => !ele.inMobile)
})
const hanedleMessage = event => {
if (event.data.type === 'panelInit') {
mobileLoading.value = false
handleLoad()
}
if (event.data.type === 'delFromMobile') {
componentData.value.some(ele => {
if (ele.id === event.data.value) {
ele.inMobile = false
return true
}
return false
})
}
if (event.data.type === 'mobileSaveFromMobile') {
componentData.value.forEach(ele => {
const com = event.data.value[ele.id]
if (!!com) {
const { x, y, sizeX, sizeY } = com
ele.mx = x
ele.my = y
ele.mSizeX = sizeX
ele.mSizeY = sizeY
}
})
eventBus.emit('save')
}
}
onMounted(() => {
window.addEventListener('message', hanedleMessage)
dvMainStore.setMobileInPc(true)
useEmitt({
name: 'onMobileStatusChange',
callback: ({ type, value }) => {
mobileStatusChange(type, value)
}
})
})
onBeforeUnmount(() => {
dvMainStore.setMobileInPc(false)
window.removeEventListener('message', hanedleMessage)
})
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-save">
<el-button type="primary" @click="mobileStatusChange('mobileSave', undefined)"
>保存</el-button
>
</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" />
</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" v-if="!mobileLoading" @click="addToMobile(config)"></div>
</div>
</template>
<MobileBackgroundSelector v-else></MobileBackgroundSelector>
</div>
</div>
</template>
<style lang="less" scoped>
.mobile-config-panel {
height: 100vh;
width: 100vw;
position: relative;
.mobile-to-pc {
position: absolute;
left: 20px;
top: 20px;
cursor: pointer;
}
.mobile-save {
position: absolute;
right: 20px;
top: 20px;
}
.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 DvSidebar from '../../components/visualization/DvSidebar.vue'
import router from '@/router'
import MobileConfigPanel from './MobileConfigPanel.vue'
import { useEmitt } from '@/hooks/web/useEmitt'
import DbToolbar from '@/components/dashboard/DbToolbar.vue'
import ViewEditor from '@/views/chart/components/editor/index.vue'
import { getDatasetTree } from '@/api/dataset'
@ -33,7 +35,6 @@ const eventCheck = e => {
}
const dvMainStore = dvMainStoreWithOut()
const snapshotStore = snapshotStoreWithOut()
const {
componentData,
curComponent,
@ -72,8 +73,20 @@ const checkPer = async resourceId => {
await interactiveStore.setInteractive(request)
return check(wsCache.get('panel-weight'), resourceId, 4)
}
const mobileConfig = ref(false)
const onMobileConfig = () => {
mobileConfig.value = true
}
//
onMounted(async () => {
useEmitt({
name: 'mobileConfig',
callback: () => {
onMobileConfig()
}
})
window.addEventListener('storage', eventCheck)
const resourceId = embeddedStore.resourceId || router.currentRoute.value.query.resourceId
const pid = embeddedStore.pid || router.currentRoute.value.query.pid
@ -139,7 +152,7 @@ onUnmounted(() => {
</script>
<template>
<div class="dv-common-layout dv-teleport-query">
<div class="dv-common-layout dv-teleport-query" v-if="!mobileConfig">
<DbToolbar />
<el-container
class="dv-layout-container"
@ -205,6 +218,7 @@ onUnmounted(() => {
</dv-sidebar>
</el-container>
</div>
<MobileConfigPanel @pcMode="mobileConfig = false" v-else></MobileConfigPanel>
</template>
<style lang="less">

View File

@ -0,0 +1,130 @@
<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 { useRouter } from 'vue-router'
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) {
if (!ids.length) {
return ele.children || []
}
const children = dfsTree([...ids], ele.children || [])
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 = () => {
directName.value.pop()
activeDirectName.value = directName.value[directName.value.length - 1]
directId.value.pop()
if (!!directName.value.length) {
emits('hiddenTabbar', false)
}
}
const router = useRouter()
const handleCellClick = ele => {
router.push({
path: '/panel/mobile',
query: {
dvId: ele.id
}
})
}
const dataClick = val => {
directName.value.push(val.name)
activeDirectName.value = val.name
directId.value.push(val.id)
if (val.leaf) {
emits('hiddenTabbar', true)
handleCellClick(val)
}
}
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,127 @@
<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 { useRouter } from 'vue-router'
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 router = useRouter()
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 handleCellClick = ele => {
router.push({
path: '/panel/mobile',
query: {
dvId: ele.id
}
})
}
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
@click="handleCellClick(ele)"
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,56 @@
<script lang="ts" setup>
import { ref } from 'vue'
import Home from './home/index.vue'
import Directory from './directory/index.vue'
import Personal from './personal/index.vue'
import VanTabbar from 'vant/es/tabbar'
import VanTabbarItem from 'vant/es/tabbar-item'
import VanOverlay from 'vant/es/overlay'
import VanLoading from 'vant/es/loading'
import 'vant/es/tabbar-item/style'
import 'vant/es/tabbar/style'
import 'vant/es/overlay/style'
import 'vant/es/loading/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>
<Personal v-else-if="activeTabbar === 'user'"> </Personal>
<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">
body {
min-height: 100vh;
min-height: -webkit-fill-available;
}
html {
height: -webkit-fill-available;
}
.mobile-index {
width: 100vw;
height: 100vh;
overflow: hidden;
}
</style>

View File

@ -0,0 +1,132 @@
<script lang="ts" setup>
import { ref } from 'vue'
import VanCellGroup from 'vant/es/cell-group'
import { showToast } from 'vant'
import { loginApi, queryDekey } from '@/api/login'
import { useAppStoreWithOut } from '@/store/modules/app'
import { useUserStoreWithOut } from '@/store/modules/user'
import { useCache } from '@/hooks/web/useCache'
import { useRouter } from 'vue-router'
import { rsaEncryp } from '@/utils/encryption'
import VanForm from 'vant/es/form'
import VanField from 'vant/es/field'
import VanButton from 'vant/es/button'
import 'vant/es/button/style'
import 'vant/es/field/style'
import 'vant/es/form/style'
import 'vant/es/cell-group/style'
const { wsCache } = useCache()
const appStore = useAppStoreWithOut()
const userStore = useUserStoreWithOut()
const router = useRouter()
const username = ref('')
const password = ref('')
const duringLogin = ref(false)
const checkUsername = value => {
if (!value) {
return true
}
const pattern = /^[a-zA-Z0-9][a-zA-Z0-9\@._-]*$/
const reg = new RegExp(pattern)
return reg.test(value)
}
const validatePwd = value => {
if (!value) {
return true
}
const pattern =
/^.*(?=.{6,20})(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[~!@#$%^&*()_+\-\={}|":<>?`[\];',.\/])[a-zA-Z0-9~!@#$%^&*()_+\-\={}|":<>?`[\];',.\/]*$/
const regep = new RegExp(pattern)
return regep.test(value)
}
const onSubmit = async () => {
if (!checkUsername(username.value) || !validatePwd(password.value)) {
showToast('用户名或密码错误')
return
}
const name = username.value.trim()
const pwd = password.value
if (!wsCache.get(appStore.getDekey)) {
const res = await queryDekey()
wsCache.set(appStore.getDekey, res.data)
}
const param = { name: rsaEncryp(name), pwd: rsaEncryp(pwd) }
duringLogin.value = true
loginApi(param)
.then(res => {
const { token, exp } = res.data
userStore.setToken(token)
userStore.setExp(exp)
router.push({ path: '/index' })
})
.catch(() => {
duringLogin.value = false
})
}
</script>
<template>
<div class="de-mobile-login" v-loading="duringLogin">
<div class="mobile-login-content">
<div class="mobile-login-welcome">用户登录</div>
<van-form @submit="onSubmit">
<van-cell-group inset>
<van-field
v-model="username"
name="用户名"
label="用户名"
placeholder="用户名"
:rules="[{ required: true, message: '请填写用户名' }]"
/>
<van-field
v-model="password"
type="password"
name="密码"
label="密码"
placeholder="密码"
:rules="[{ required: true, message: '请填写密码' }]"
/>
</van-cell-group>
<div style="margin: 16px">
<van-button round block type="primary" native-type="submit"> 提交 </van-button>
</div>
</van-form>
</div>
</div>
</template>
<style lang="less" scoped>
.de-mobile-login {
height: 100vh;
width: 100vw;
display: flex;
align-items: center;
justify-content: center;
background-image: url(../../../assets/logo-bg.jpg);
.mobile-login-content {
background-color: #fff;
margin-top: 20px;
position: relative;
opacity: 0.95;
border-radius: 10px;
overflow: hidden;
:deep(.van-field) {
padding-left: 0 !important;
}
.mobile-login-welcome {
padding-left: 15px;
font-size: x-large;
font-weight: 500;
letter-spacing: 2px;
}
}
}
</style>

View File

@ -0,0 +1,96 @@
<script setup lang="ts">
import { reactive } from 'vue'
import { initCanvasDataMobile } from '@/utils/canvasUtils'
import { ref, nextTick, onBeforeMount } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import DePreview from '@/components/data-visualization/canvas/DePreview.vue'
import { dvMainStoreWithOut } from '@/store/modules/data-visualization/dvMain'
import VanSticky from 'vant/es/sticky'
import VanNavBar from 'vant/es/nav-bar'
import 'vant/es/nav-bar/style'
import 'vant/es/sticky/style'
const dvMainStore = dvMainStoreWithOut()
const state = reactive({
canvasDataPreview: null,
canvasStylePreview: null,
canvasViewInfoPreview: null,
dvInfo: {
name: ''
},
curPreviewGap: 0
})
const dataInitState = ref(true)
const dashboardPreview = ref(null)
const loadCanvasData = (dvId, weight?) => {
dataInitState.value = false
initCanvasDataMobile(
dvId,
'dashboard',
function ({
canvasDataResult,
canvasStyleResult,
dvInfo,
canvasViewInfoPreview,
curPreviewGap
}) {
dvInfo['weight'] = weight
state.canvasDataPreview = canvasDataResult
state.canvasStylePreview = canvasStyleResult
state.canvasViewInfoPreview = canvasViewInfoPreview
state.dvInfo = dvInfo
state.curPreviewGap = curPreviewGap
dataInitState.value = true
nextTick(() => {
dashboardPreview.value.restore()
})
}
)
}
const route = useRoute()
const router = useRouter()
onBeforeMount(() => {
dvMainStore.setMobileInPc(true)
const dvId = route.query.dvId as unknown as string
loadCanvasData(dvId)
})
const onClickLeft = () => {
router.replace({
path: '/index'
})
}
</script>
<template>
<div class="dv-common-layout-mobile">
<van-sticky>
<van-nav-bar
:title="state.dvInfo.name"
left-text="返回"
left-arrow
@click-left="onClickLeft"
/>
</van-sticky>
<de-preview
ref="dashboardPreview"
v-if="state.canvasStylePreview && dataInitState"
:dv-info="state.dvInfo"
:cur-gap="state.curPreviewGap"
:component-data="state.canvasDataPreview"
:canvas-style-data="state.canvasStylePreview"
:canvas-view-info="state.canvasViewInfoPreview"
:download-status="false"
></de-preview>
</div>
</template>
<style lang="less">
.dv-common-layout-mobile {
height: 100vh;
width: 100vw;
overflow-y: auto;
}
</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,95 @@
<script lang="ts" setup>
import { onBeforeMount, ref, onBeforeUnmount } from 'vue'
import { useEmitt } from '@/hooks/web/useEmitt'
import eventBus from '@/utils/eventBus'
import { dvMainStoreWithOut } from '@/store/modules/data-visualization/dvMain'
import DePreviewMobile from './MobileInPc.vue'
const panelInit = ref(false)
const dvMainStore = dvMainStoreWithOut()
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
componentData.forEach(ele => {
const { mx, my, mSizeX, mSizeY } = ele
ele.x = mx
ele.y = my
ele.sizeX = mSizeX
ele.sizeY = mSizeY
})
dvMainStore.setComponentData(componentData)
dvMainStore.setMobileInPc(true)
dvMainStore.setCanvasStyle(canvasStyleData)
dvMainStore.updateCurDvInfo(dvInfo)
dvMainStore.setCanvasViewInfo(canvasViewInfo)
panelInit.value = true
}
if (event.data.type === 'addToMobile') {
const component = event.data.value
checkItemPosition(component)
dvMainStore.componentData.push(component)
}
if (event.data.type === 'setCanvasStyle') {
dvMainStore.setCanvasStyle(event.data.value)
}
if (event.data.type === 'mobileSave') {
window.top.postMessage(
{
type: 'mobileSaveFromMobile',
value: dvMainStore.componentData.reduce((pre, next) => {
const { x, y, sizeX, sizeY, id } = next
pre[id] = { x, y, sizeX, sizeY }
return pre
}, {})
},
'*'
)
}
}
onBeforeMount(() => {
window.top.postMessage({ type: 'panelInit', value: true }, '*')
window.addEventListener('message', hanedleMessage)
useEmitt({
name: 'onMobileStatusChange',
callback: ({ type, value }) => {
mobileStatusChange(type, value)
}
})
})
const mobileStatusChange = (type, value) => {
window.top.postMessage({ type, value }, '*')
if (type === 'delFromMobile') {
eventBus.emit('removeMatrixItemById-canvas-main', value)
}
}
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,78 @@
<script lang="ts" setup>
import { useUserStoreWithOut } from '@/store/modules/user'
import userImg from '@/assets/img/user.png'
import { useRouter } from 'vue-router'
import { logoutApi } from '@/api/login'
import { logoutHandler } from '@/utils/logout'
import VanButton from 'vant/es/button'
import VanCell from 'vant/es/cell'
import VanIcon from 'vant/es/icon'
import VanImage from 'vant/es/image'
import 'vant/es/image/style'
import 'vant/es/icon/style'
import 'vant/es/cell/style'
import 'vant/es/button/style'
const userStore = useUserStoreWithOut()
const { push } = useRouter()
const logout = async () => {
await logoutApi()
logoutHandler()
push('/login')
}
</script>
<template>
<div class="de-mobile-user">
<div class="mobile-user-top">
<van-image width="75" height="75" :src="userImg" />
<div class="user-name">
{{ userStore.name }}
</div>
</div>
<van-cell value="系统信息" is-link>
<template #title>
<van-icon name="user-o" class="search-icon" />
<span class="custom-title">关于</span>
</template>
</van-cell>
<div style="margin: 16px">
<van-button round block type="primary" @click="logout"> 注销 </van-button>
</div>
</div>
</template>
<style lang="less" scoped>
.de-mobile-user {
height: 100vh;
width: 100vw;
.mobile-user-top {
padding: 24px 24px 16px 24px;
display: flex;
.user-name {
height: 45px;
text-align: left;
padding-left: 10px;
padding-top: 20px;
}
}
:deep(.van-cell__title) {
display: flex;
align-items: center;
}
.custom-title {
margin-right: 4px;
vertical-align: middle;
margin-left: 12px;
}
.search-icon {
font-size: 26px;
line-height: inherit;
}
}
</style>

View File

@ -263,12 +263,7 @@ const handleNodeClick = (data: BusiTreeNode) => {
}
const editorDataset = () => {
router.push({
path: '/dataset-form',
query: {
id: nodeInfo.id
}
})
handleEdit(nodeInfo.id)
}
const handleEdit = id => {