feat(仪表板): 移动端支持独立样式设计

This commit is contained in:
wangjiahao 2024-10-23 15:05:04 +08:00
parent d42afc0970
commit dc11e81d20
14 changed files with 2040 additions and 1635 deletions

View File

@ -14,18 +14,18 @@ public class MybatisPlusGenerator {
* 第一 我嫌麻烦
* 第二 后面配置会放到nacos读起来更麻烦了
*/
private static final String url = "jdbc:mysql://localhost:3306/dataease4?autoReconnect=false&useUnicode=true&characterEncoding=UTF-8&characterSetResults=UTF-8&zeroDateTimeBehavior=convertToNull&useSSL=false";
private static final String url = "jdbc:mysql://localhost:3306/dataease?autoReconnect=false&useUnicode=true&characterEncoding=UTF-8&characterSetResults=UTF-8&zeroDateTimeBehavior=convertToNull&useSSL=false";
private static final String username = "root";
private static final String password = "123456";
/**
* 业务模块例如datasource,dataset,panel等
*/
private static final String busi = "visualization";
private static final String busi = "chart";
/**
* 这是要生成代码的表名称
*/
private static final String TABLE_NAME = "visualization_link_jump_info";
private static final String TABLE_NAME = "core_chart_view";
/**
* 下面两个配置基本上不用动

View File

@ -9,7 +9,7 @@ import java.io.Serializable;
* </p>
*
* @author fit2cloud
* @since 2024-05-07
* @since 2024-10-23
*/
@TableName("core_chart_view")
public class CoreChartView implements Serializable {
@ -101,11 +101,21 @@ public class CoreChartView implements Serializable {
*/
private String customAttr;
/**
* 图形属性_移动端
*/
private String customAttrMobile;
/**
* 组件样式
*/
private String customStyle;
/**
* 组件样式_移动端
*/
private String customStyleMobile;
/**
* 结果过滤
*/
@ -137,7 +147,7 @@ public class CoreChartView implements Serializable {
private Long updateTime;
/**
* 缩略图
* 缩略图
*/
private String snapshot;
@ -206,8 +216,14 @@ public class CoreChartView implements Serializable {
*/
private Boolean aggregate;
/**
* 流向地图起点名称field
*/
private String flowMapStartName;
/**
* 流向地图终点名称field
*/
private String flowMapEndName;
/**
@ -215,7 +231,6 @@ public class CoreChartView implements Serializable {
*/
private String extColor;
public Long getId() {
return id;
}
@ -352,6 +367,14 @@ public class CoreChartView implements Serializable {
this.customAttr = customAttr;
}
public String getCustomAttrMobile() {
return customAttrMobile;
}
public void setCustomAttrMobile(String customAttrMobile) {
this.customAttrMobile = customAttrMobile;
}
public String getCustomStyle() {
return customStyle;
}
@ -360,6 +383,14 @@ public class CoreChartView implements Serializable {
this.customStyle = customStyle;
}
public String getCustomStyleMobile() {
return customStyleMobile;
}
public void setCustomStyleMobile(String customStyleMobile) {
this.customStyleMobile = customStyleMobile;
}
public String getCustomFilter() {
return customFilter;
}
@ -564,7 +595,9 @@ public class CoreChartView implements Serializable {
", extLabel = " + extLabel +
", extTooltip = " + extTooltip +
", customAttr = " + customAttr +
", customAttrMobile = " + customAttrMobile +
", customStyle = " + customStyle +
", customStyleMobile = " + customStyleMobile +
", customFilter = " + customFilter +
", drillFields = " + drillFields +
", senior = " + senior +
@ -585,9 +618,9 @@ public class CoreChartView implements Serializable {
", copyFrom = " + copyFrom +
", copyId = " + copyId +
", aggregate = " + aggregate +
", flowMapStartName=" + flowMapStartName +
", flowMapEndName=" + flowMapEndName +
", extColor=" + extColor +
", flowMapStartName = " + flowMapStartName +
", flowMapEndName = " + flowMapEndName +
", extColor = " + extColor +
"}";
}
}

View File

@ -10,7 +10,7 @@ import org.apache.ibatis.annotations.Mapper;
* </p>
*
* @author fit2cloud
* @since 2024-05-07
* @since 2024-10-23
*/
@Mapper
public interface CoreChartViewMapper extends BaseMapper<CoreChartView> {

View File

@ -6,4 +6,9 @@ FROM
INNER JOIN data_visualization_info dvi ON ccv.scene_id = dvi.id
WHERE
dvi.delete_flag =1;
delete from data_visualization_info dvi where dvi.delete_flag =1;
delete from data_visualization_info dvi where dvi.delete_flag =1;
ALTER TABLE `core_chart_view`
ADD COLUMN `custom_attr_mobile` longtext NULL COMMENT '图形属性_移动端',
ADD COLUMN `custom_style_mobile` longtext NULL COMMENT '组件样式_移动端';

View File

@ -7,4 +7,8 @@ FROM
WHERE
dvi.delete_flag =1;
delete from data_visualization_info dvi where dvi.delete_flag =1;
DELETE FROM area where pid = '156710100' OR id = '156710100';
DELETE FROM area where pid = '156710100' OR id = '156710100';
ALTER TABLE `core_chart_view`
ADD COLUMN `custom_attr_mobile` longtext NULL COMMENT '图形属性_移动端' AFTER `custom_attr`,
ADD COLUMN `custom_style_mobile` longtext NULL COMMENT '组件样式_移动端' AFTER `custom_style`;

View File

@ -210,7 +210,6 @@ watch(
canEdit.value = false
reShow()
myValue.value = assignment(element.value.propValue.textValue)
console.log('===myValue.value=' + myValue.value)
ed.setContent(myValue.value)
}
}

View File

@ -45,7 +45,9 @@ declare interface Chart {
customFilter: {}
senior: CustomSenior
customAttr: CustomAttr
customAttrMobile: CustomAttr
customStyle: CustomStyle
customStyleMobile: CustomStyle
drillFields: ChartViewField[]
drillFilters: Filter[]
datasetMode: 0 | 1

View File

@ -307,6 +307,13 @@ export const dvMainStore = defineStore('dataVisualization', {
this.canvasState['curPointArea'] = this.curComponent['category']
}
}
// 移动端通知
if (this.mobileInPc) {
useEmitt().emitter.emit('curComponentChange', {
type: 'curComponentChange',
value: JSON.parse(JSON.stringify(this.curComponent))
})
}
},
setBashMatrixInfo(bashMatrixInfo) {
this.bashMatrixInfo = bashMatrixInfo

View File

@ -743,3 +743,39 @@ export function maxYComponentCount() {
.reduce((max, current) => Math.max(max, current), 0)
}
}
export function componentSwitch(componentData, changeComponent) {
componentData.map(obj => (obj.id === changeComponent.id ? changeComponent : obj))
}
export function findComponentById(componentId) {
let result
componentData.forEach(item => {
if (item.id === componentId) {
result = item
} else if (item.component === 'Group') {
item.propValue.forEach(groupItem => {
if (groupItem.id === componentId) {
result = groupItem
}
})
} else if (item.component === 'DeTabs') {
item.propValue.forEach(tabItem => {
tabItem.componentData.forEach(tabComponent => {
if (tabComponent.id === componentId) {
result = tabComponent
}
})
})
}
})
return result
}
export function mobileViewStyleSwitch(component) {
if (component) {
const viewInfo = canvasViewInfo.value[component.id]
viewInfo.customStyle = component.customStyle
viewInfo.customStyleMobile = component.customStyleMobile
}
}

View File

@ -0,0 +1,141 @@
<script setup lang="ts">
import { PluginComponent } from '@/components/plugin'
import ChartStyle from '@/views/chart/components/editor/editor-style/ChartStyle.vue'
import { PropType } from 'vue'
const emit = defineEmits([
'onColorChange',
'onMiscChange',
'onLabelChange',
'onTooltipChange',
'onChangeXAxisForm',
'onChangeYAxisForm',
'onChangeYAxisExtForm',
'onTextChange',
'onLegendChange',
'onBasicStyleChange',
'onBackgroundChange',
'onStyleAttrChange',
'onTableHeaderChange',
'onTableCellChange',
'onTableTotalChange',
'onChangeMiscStyleForm',
'onExtTooltipChange',
'onIndicatorChange',
'onIndicatorNameChange',
'onChangeQuadrantForm',
'onChangeFlowMapLineForm',
'onChangeFlowMapPointForm'
])
const props = defineProps({
commonBackgroundPop: {
type: Object,
required: false
},
commonBorderPop: {
type: Object,
required: false
},
eventInfo: {
type: Object,
required: false
},
view: {
type: Object as PropType<ChartObj>,
required: true
},
themes: {
type: String as PropType<EditorTheme>,
default: 'dark'
},
dimension: {
type: Array,
required: true
},
quota: {
type: Array,
required: true
},
properties: {
type: Array as PropType<EditorProperty[]>,
required: false,
default: () => {
return []
}
},
propertyInnerAll: {
type: Object as PropType<EditorPropertyInner>,
required: false,
default: () => {
return {}
}
},
selectorSpec: {
type: Object as PropType<EditorSelectorSpec>,
required: false,
default: () => {
return {}
}
},
allFields: {
type: Array,
required: true
}
})
</script>
<template>
<el-container direction="vertical" id="main-style-p">
<el-scrollbar class="drag_main_area">
<template v-if="view.plugin?.isPlugin">
<plugin-component
:jsname="view.plugin.staticMap['editor-style']"
:view="view"
:dimension="dimension"
:quota="quota"
:themes="themes"
:emitter="emitter"
/>
</template>
<template v-else>
<chart-style
v-if="chartStyleShow"
:properties="chartViewInstance.properties"
:property-inner-all="chartViewInstance.propertyInner"
:selector-spec="chartViewInstance.selectorSpec"
:common-background-pop="curComponent?.commonBackground"
:common-border-pop="curComponent?.style"
:event-info="curComponent?.events"
:chart="view"
:themes="themes"
:dimension-data="dimension"
:quota-data="quota"
:all-fields="allFields"
@onColorChange="(val, prop) => emit('onColorChange', val, prop)"
@onMiscChange="(val, prop) => emit('onMiscChange', val, prop)"
@onLabelChange="(val, prop) => emit('onLabelChange', val, prop)"
@onTooltipChange="(val, prop) => emit('onTooltipChange', val, prop)"
@onChangeXAxisForm="(val, prop) => emit('onChangeXAxisForm', val, prop)"
@onChangeYAxisForm="(val, prop) => emit('onChangeYAxisForm', val, prop)"
@onChangeYAxisExtForm="(val, prop) => emit('onChangeYAxisExtForm', val, prop)"
@onTextChange="(val, prop) => emit('onTextChange', val, prop)"
@onIndicatorChange="(val, prop) => emit('onIndicatorChange', val, prop)"
@onIndicatorNameChange="(val, prop) => emit('onIndicatorNameChange', val, prop)"
@onLegendChange="(val, prop) => emit('onLegendChange', val, prop)"
@onBackgroundChange="(val, prop) => emit('onBackgroundChange', val, prop)"
@onStyleAttrChange="(val, prop) => emit('onStyleAttrChange', val, prop)"
@onBasicStyleChange="(val, prop) => emit('onBasicStyleChange', val, prop)"
@onTableHeaderChange="(val, prop) => emit('onTableHeaderChange', val, prop)"
@onTableCellChange="(val, prop) => emit('onTableCellChange', val, prop)"
@onTableTotalChange="(val, prop) => emit('onTableTotalChange', val, prop)"
@onChangeMiscStyleForm="(val, prop) => emit('onChangeMiscStyleForm', val, prop)"
@onExtTooltipChange="(val, prop) => emit('onExtTooltipChange', val, prop)"
@onChangeQuadrantForm="(val, prop) => emit('onChangeQuadrantForm', val, prop)"
@onChangeFlowMapLineForm="(val, prop) => emit('onChangeFlowMapLineForm', val, prop)"
@onChangeFlowMapPointForm="(val, prop) => emit('onChangeFlowMapPointForm', val, prop)"
/>
</template>
</el-scrollbar>
</el-container>
</template>
<style scoped lang="less"></style>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,49 @@
<script setup lang="ts">
import { findComponentAttr } from '@/utils/components'
import { storeToRefs } from 'pinia'
import { dvMainStoreWithOut } from '@/store/modules/data-visualization/dvMain'
import ViewEditor from '@/views/chart/components/editor/index.vue'
import { computed } from 'vue'
const dvMainStore = dvMainStoreWithOut()
const { curComponent, canvasViewInfo, batchOptStatus } = storeToRefs(dvMainStore)
const otherEditorShow = computed(() => {
return Boolean(
curComponent.value &&
(!['UserView', 'VQuery'].includes(curComponent.value?.component) ||
(curComponent.value?.component === 'UserView' &&
curComponent.value?.innerType === 'picture-group')) &&
!batchOptStatus.value
)
})
const viewEditorShow = computed(() => {
return Boolean(
curComponent.value &&
['UserView', 'VQuery'].includes(curComponent.value.component) &&
curComponent.value.innerType !== 'picture-group' &&
!batchOptStatus.value
)
})
</script>
<template>
<div class="mobile_content">
<template v-if="otherEditorShow">
<component :is="findComponentAttr(curComponent)" :themes="'light'" />
</template>
<template v-if="viewEditorShow">
<view-editor
:themes="'light'"
:view="canvasViewInfo[curComponent ? curComponent.id : 'default']"
></view-editor>
</template>
</div>
</template>
<style scoped lang="less">
.mobile_content {
width: 100%;
height: 100%;
}
</style>

View File

@ -14,6 +14,7 @@ import { backCanvasData } from '@/utils/canvasUtils'
import { storeToRefs } from 'pinia'
import { debounce } from 'lodash-es'
import mobileHeader from '@/assets/img/mobile-header.png'
import ComponentStyleEditor from '@/views/common/ComponentStyleEditor.vue'
const dvMainStore = dvMainStoreWithOut()
const { componentData, canvasStyleData, canvasViewInfo, dvInfo } = storeToRefs(dvMainStore)
@ -90,6 +91,11 @@ const hanedleMessage = event => {
loadCanvasData()
}
if (event.data.type === 'curComponentChange') {
// CurComponentdvMain
dvMainStore.setCurComponent({ component: event.data.value, index: 0 })
}
if (event.data.type === 'delFromMobile') {
changeTimes.value++
componentData.value.some(ele => {
@ -158,6 +164,9 @@ const setMobileStyle = debounce(() => {
transformOrigin: '0 0'
}
}, 100)
const curComponentChangeHandle = info => {
// do change
}
onMounted(() => {
window.addEventListener('message', hanedleMessage)
window.addEventListener('resize', setMobileStyle)
@ -168,6 +177,12 @@ onMounted(() => {
mobileStatusChange(type, value)
}
})
useEmitt({
name: 'curComponentChange',
callback: info => {
curComponentChangeHandle(info)
}
})
setMobileStyle()
})
@ -252,43 +267,44 @@ const save = () => {
<div class="config-mobile-sidebar">移动端配置</div>
<el-tabs size="small" v-model="activeCollapse">
<el-tab-pane label="可视化组件" name="com"> </el-tab-pane>
<el-tab-pane label="样式" name="style"> </el-tab-pane>
<el-tab-pane label="组件样式" name="componentStyle"> </el-tab-pane>
<el-tab-pane label="整体样式" name="style"> </el-tab-pane>
</el-tabs>
<div class="config-mobile-tab">
<MobileBackgroundSelector
v-if="activeCollapse === 'style'"
@styleChange="changeTimes++"
></MobileBackgroundSelector>
<template v-else>
<div
:style="{ height: '198px', width: '198px' }"
class="mobile-wrapper-inner-adaptor"
v-for="item in componentDataNotInMobile"
:key="item.id"
>
<div class="component-outer">
<ComponentWrapper
v-show="item.isShow"
canvas-id="canvas-main"
:canvas-style-data="canvasStyleData"
:dv-info="dvInfo"
:canvas-view-info="canvasViewInfo"
:view-info="canvasViewInfo[item.id]"
:config="item"
:style="getComponentStyleDefault()"
show-position="preview"
:search-count="0"
:scale="80"
/>
</div>
<div class="mobile-com-mask" @click="addToMobile(item)">
<span v-show="item.component === 'DeStreamMedia'" style="color: #909399"
>IOS可能无法显示</span
>
</div>
<div class="pc-select-to-mobile" @click="addToMobile(item)" v-if="!mobileLoading"></div>
<div class="config-mobile-tab" v-show="activeCollapse === 'style'">
<MobileBackgroundSelector @styleChange="changeTimes++"></MobileBackgroundSelector>
</div>
<div class="config-mobile-tab-style" v-show="activeCollapse === 'componentStyle'">
<component-style-editor></component-style-editor>
</div>
<div class="config-mobile-tab" v-show="activeCollapse === 'com'">
<div
:style="{ height: '198px', width: '198px' }"
class="mobile-wrapper-inner-adaptor"
v-for="item in componentDataNotInMobile"
:key="item.id"
>
<div class="component-outer">
<ComponentWrapper
v-show="item.isShow"
canvas-id="canvas-main"
:canvas-style-data="canvasStyleData"
:dv-info="dvInfo"
:canvas-view-info="canvasViewInfo"
:view-info="canvasViewInfo[item.id]"
:config="item"
:style="getComponentStyleDefault()"
show-position="preview"
:search-count="0"
:scale="80"
/>
</div>
</template>
<div class="mobile-com-mask" @click="addToMobile(item)">
<span v-show="item.component === 'DeStreamMedia'" style="color: #909399"
>IOS可能无法显示</span
>
</div>
<div class="pc-select-to-mobile" @click="addToMobile(item)" v-if="!mobileLoading"></div>
</div>
</div>
</div>
</div>
@ -485,6 +501,14 @@ const save = () => {
.config-mobile-tab {
padding: 16px 8px;
}
.config-mobile-tab-style {
padding: 0;
overflow-y: auto;
overflow-x: hidden;
::v-deep(.editor-light) {
border-left: none !important;
}
}
.mobile-wrapper-inner-adaptor {
position: relative;
margin-right: 8px;

View File

@ -5,6 +5,7 @@ import eventBus from '@/utils/eventBus'
import { dvMainStoreWithOut } from '@/store/modules/data-visualization/dvMain'
import { XpackComponent } from '@/components/plugin'
import DePreviewMobile from './MobileInPc.vue'
import { mobileViewStyleSwitch } from '@/utils/canvasUtils'
const panelInit = ref(false)
const dvMainStore = dvMainStoreWithOut()
@ -50,6 +51,18 @@ const hanedleMessage = event => {
if (isEmbedded) return
panelInit.value = true
}
// type render calcData
if (event.data.type === 'componentStyleChange') {
const { type, innerOptType, component } = event.data.value
if (type === 'renderChart') {
mobileViewStyleSwitch(component)
useEmitt().emitter.emit('renderChart-' + component.id, component)
} else if (type === 'calcData') {
mobileViewStyleSwitch(component)
useEmitt().emitter.emit('calcData-' + component.id, component)
} else if (type === 'component') {
}
}
if (event.data.type === 'addToMobile') {
const component = event.data.value
@ -97,7 +110,9 @@ const initIframe = () => {
panelInit.value = true
})
}
const curComponentChangeHandle = (type, value) => {
window.parent.postMessage({ type: type, value: value }, '*')
}
onBeforeMount(() => {
window.parent.postMessage({ type: 'panelInit', value: true }, '*')
window.addEventListener('message', hanedleMessage)
@ -107,6 +122,12 @@ onBeforeMount(() => {
mobileStatusChange(type, value)
}
})
useEmitt({
name: 'curComponentChange',
callback: ({ type, value }) => {
curComponentChangeHandle(type, value)
}
})
})
const mobileStatusChange = (type, value) => {