场景:从自己建的组件库中选择组件,选中后渲染到页面的模块中。
html
<template>
<div class="dashbordBox">
<div class="topBox">
<div class="tLeftBox"></div>
<div class="tMidBox"></div>
<div class="tRightBox"></div>
</div>
<div class="btmBox">
<div class="bLeft">
<p class="iconBox" @click="editHandle(1)"><el-icon color="#999" size="18"><Edit /></el-icon></p>
<component v-if="moduleData[1].key && compMap[moduleData[1].key]"
:is="compMap[moduleData[1].key]" />
</div>
<div class="bRight">
<div class="rTopBox">
<div class="rwBox">
<p class="iconBox" @click="editHandle(2)"><el-icon color="#999" size="18"><Edit /></el-icon></p>
<component v-if="moduleData[2].key && compMap[moduleData[2].key]"
:is="compMap[moduleData[2].key]" />
</div>
<div class="rcBox">
<p class="iconBox" @click="editHandle(3)"><el-icon color="#999" size="18"><Edit /></el-icon></p>
<!-- <BarChart ref="barChartsChild"></BarChart> -->
<component v-if="moduleData[3].key && compMap[moduleData[3].key]"
:is="compMap[moduleData[3].key]" />
</div>
</div>
<div class="rBtmBox"></div>
</div>
</div>
<el-dialog v-model="state.comDialog" title="组件库" v-if="state.comDialog" width="90%" style="margin-top: 4%">
<div class="comBox">
<div class="xxBox" @click="selectComp('msg')">
<XxCom></XxCom>
</div>
<div class="rwBox" @click="selectComp('task')">
<RwCom></RwCom>
</div>
<div class="chartBox" @click="selectComp('chart')">
<BarChart ref="barChartsChild"></BarChart>
</div>
<!-- <div class="zbBox">
<p>值班表值班表值班表值班表值班表值班表值班表值班表</p>
</div> -->
</div>
</el-dialog>
</div>
</template>
<script setup>
import { onMounted,reactive,ref,nextTick,watch,computed } from 'vue'
import * as echarts from 'echarts'
import BarChart from '@/components/chart/barChart.vue'
import XxCom from '@/components/text/xx.vue'
import RwCom from '@/components/text/rw.vue'
const barChartsChild = ref(null)
// 组件映射:key对应组件实例
const compMap = {
msg: XxCom,
task: RwCom,
chart: BarChart
}
// 记录当前正在操作的模块id
let activeModuleId = null
const state = reactive({
comDialog: false,
})
// localStorage存储key,全局唯一
const STORAGE_KEY = 'moduleCache'
// 默认初始化数据
const defaultModuleData = {
1: { key: '' },
2: { key: '' },
3: { key: '' },
4: { key: '' }
}
// 读取本地缓存,合并默认值
let cacheStr = localStorage.getItem(STORAGE_KEY)
let cacheData = null
try {
if (cacheStr) {
cacheData = JSON.parse(cacheStr)
}
} catch (err) {
// 缓存损坏时清空,防止页面报错
localStorage.removeItem(STORAGE_KEY)
cacheData = null
}
// 合并缓存与默认配置,保证4个模块一定存在
const moduleData = reactive({
...defaultModuleData, // 兜底:保证 1/2/3/4 一定存在,key 默认空字符串
...(cacheData || {}) // 用本地缓存覆盖默认值,有则替换,无则保留默认
})
// 监听数据自动持久化
watch(moduleData, (newVal) => {
localStorage.setItem(STORAGE_KEY, JSON.stringify(newVal))
}, { deep: true })
// 打开弹窗,记录当前点击的模块编号
const editHandle = (moduleId)=>{
activeModuleId = moduleId
state.comDialog = true
}
// 弹窗选中组件,赋值给对应模块
const selectComp = (compKey) => {
if (!activeModuleId) return
// 给当前操作模块赋值组件
moduleData[activeModuleId].key = compKey
// 关闭弹窗
state.comDialog = false
}
onMounted(() => {
barChartsChild?.value?.initChart() //只用于弹框中图表初始化展示
})
</script>
<style lang="scss" scoped>
.dashbordBox {
width: 100%;
height: 100%;
.topBox{
width: 100%;
height: 20%;
// padding: .5% 1%;
margin-bottom: 1%;
display: flex;
justify-content: space-between;
align-items: center;
// flex-wrap: wrap;
.tLeftBox{
width: 20%;
height: 100%;
background-color: #fff;
}
.tMidBox {
width: 60%;
height: 100%;
background-color: #fff;
}
.tRightBox {
width: 18%;
height: 100%;
background-color: #fff;
}
}
.btmBox{
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
height: 78%;
.bLeft{
width: 28%;
height: 100%;
background-color: #fff;
}
.bRight {
width: 71%;
height: 100%;
// background-color: pink;
.rTopBox{
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1.2%;
width: 100%;
height: 75%;
.rwBox{
width: 63.5%;
height: 100%;
background-color: #fff;
}
.rcBox{
position: relative;
width: 35%;
height: 100%;
background-color: #fff;
.iconBox{
position: absolute;
right: 0;
top: 0;
z-index: 1;
}
}
}
.rBtmBox{
width: 100%;
height: 22.5%;
background-color: #fff;
}
}
}
:deep(.el-dialog__body){
height: 82vh;
}
.comBox{
display: flex;
flex-wrap: wrap;
gap: 12px; /* 模块间距,按需调整 */
width: 100%;
height: 100%;
overflow: auto;
.xxBox, .rwBox, .chartBox, .zbBox{
width: 24.4%;
height: 36%;
background: #f5f7fa;
border-radius: 8px;
padding: 12px;
overflow-y: auto; /* 内容超出时滚动 */
}
}
}
</style>
组件分页面
- chart组件
html
<template>
<div ref="barChart" class="barChart"></div>
</template>
<script setup>
import { ref, onMounted, reactive, onBeforeUnmount,nextTick } from 'vue'
import * as echarts from 'echarts';
const barChart = ref(null)
const state = reactive({
myChart: null
})
const initChart = (res, type) => {
// 如果实例已存在,先销毁
if (state.myChart) {
state.myChart.dispose()
state.myChart = null
}
state.myChart = echarts.init(barChart.value)
// if (state.myChart == null) {
// state.myChart = echarts.init(barChart.value)
// }
let option = {
// title: {
// text: '标题',
// x: "2%",
// y: "3%",
// left:'2%',
// },
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow',
},
},
grid: {
top: '16%',
left: '3%',
right: '4%',
bottom: '4%',
containLabel: true,
},
xAxis: [
{
type: 'category',
data: ['名称1', '名称2', '名称3', '名称4', '名称5'],
axisTick: {
alignWithLabel: true,
},
axisLabel: {
// color: '#65c0ff',
rotate: 26,
formatter:(val)=>{
return val.length > 5 ? val.substring(0,6)+'...' : val
}
},
},
],
yAxis: [
{
type: 'value',
axisLabel: {
// color: '#65c0ff',
},
axisLine: {
show:true,//纵轴刻度线
},
splitLine: {
show: true,
lineStyle: {
type: 'dashed',
color: '#ccc',
},
},
},
],
series: [
{
name: '',
type: 'bar',
barWidth: '36%',
data: [10, 52, 200, 334, 490],
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
offset: 0,
color: '#5f86ff'
}, {
offset: 1,
color: '#a1b8ff'
}]),
},
},
],
}
state.myChart.setOption(option)
window.addEventListener('resize', () => {
state.myChart.resize()
})
}
defineExpose({ initChart })
onMounted(()=>{
nextTick(()=>{
initChart()
})
})
onBeforeUnmount(()=>{
window.removeEventListener('resize', () => {
state.myChart.resize()
})
if(state.myChart) {
state.myChart.dispose()
state.myChart = null
}
})
</script>
<style lang="scss" scoped>
.barChart {
width: 100%;
height: 100%;
// min-height: 300px;
}
</style>
2.xx组件
html
<template>
<div class="xxContainer">
消息中心 消息中心 消息中心 消息中心 消息中心 消息中心
</div>
</template>
<script setup>
import { ref, onMounted, reactive, } from 'vue'
const state = reactive({
})
onMounted(()=>{})
</script>
<style lang="scss" scoped>
.xxContainer {
width: 100%;
height: 100%;
}
</style>
-
rw组件
任务中心 任务中心