vue 自定义模块内容

场景:从自己建的组件库中选择组件,选中后渲染到页面的模块中。

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>

组件分页面

  1. 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>
  1. rw组件

相关推荐
清水白石0081 小时前
Python 类定义阶段自动注册子类:从 `__init_subclass__` 到插件系统实战
linux·前端·python
西西学代码1 小时前
Flutter---StreamBuilder案例
javascript·windows·flutter
用户69371750013842 小时前
Kimi K3 综合能力处于全球第一梯队
android·前端·后端
anno2 小时前
Agent 新手 Skill 优先级指南:从第一套必装工作流,到专业能力补全
前端·后端
Liora_Yvonne2 小时前
前端请求层到底怎么封?为什么全局 axios 单例越用越难维护
前端
anno2 小时前
别再把文档“一刀切”:用 LangChain.js 做好 RAG 的第一公里
前端·后端
皮卡穆2 小时前
React 中使用 react-org-tree 实现组织结构树
前端·react.js·前端框架
MariaH2 小时前
费曼学习法与effort-learning理论
前端
qiaofugui3 小时前
如何用 Codex 做一次可靠的项目重构:方法、节奏和可复用提示词
前端