从零开始Vue3+Element Plus后台管理系统(24)——优化版字典组件

字典组件作为常用组件,好好封装一下很有必要。

使用字典的常见方式

  1. 字典数据作为 JSON 文件在前端项目中引入;
  2. 应用加载时,把所有字典的数据请求过来缓存在前端;
  3. 根据需要按需调用接口获取字典数据,然后渲染组件;

方式 1 非常不灵活,适用于不会变动的字典。要不后端数据一变化,前端还要跟着维护,这样容易出问题。
方式 2 缺点也比较明显:

  1. 把数据库所有字典数据请求过来,安全性不高(不想暴露的字典也被拿出来了),而且有的系统字典数据非常大。
  2. 不会实时更新,一般是在用户重新登录或者刷新页面后才能更新

在我的 mocha Vue3 Admin 中很早就封装了字典组件,但是感觉并不理想。

之前的做法

  1. 使用 useDict 钩子在渲染数据前通过接口调用字典API获取需要字典的数据(以此避免多个相同的字典组件多次请求接口)
  2. Modict 组件根据返回的数据渲染字典的文字和样式

以上方法最大的缺点是:虽然使用 useDict 减少了请求,但是每次切换页面都会重新调用API,增加网络请求。另外,因为每次都要先调用useDict,用起来还是稍嫌麻烦。

优点是可以实时读取字典,不会出现滞后。

新方案

所以趁着假期,继续优化我们的字典组件。

因为在实际场景中,大部分字典的值是几乎不会变动的。所以把字典数据缓存在前端没问题,对于需要实时更新也让它能够更新起来。

接下来,就是实现下面 4 个需求:

  1. 按需加载数据,只请求需要的字典数据
  2. 无需频繁更新的数据,保存在缓存中
  3. 需要实时更新的数据,可以保持及时更新
  4. 减少请求次数

需求 1 根据当前字典的 name 返回字典数据即可。

需求 2,我们先判断缓存中有没有当前字典的数据,如果有,直接用缓存的,反之,调用接口后再放入缓存中,这样可以极大减少网络请求。

在表格、列表这种,同一个字典标签被重复使用几十次,在第一次进入还没有缓存时,依然会向后端发送多次请求。需求 3:实时更新数据,这种情况不再依赖缓存,每个字典标签都会发出一个请求。 所以如何减少重复请求呢?当然少不了好用的 promise。

首先,我们使用pinia来管理字典,在 store 下新建 dict.ts,
主要的两个方法代码如下:

注:在此使用 piniaPluginPersistedstate 将 pinia 的 字典数据保存在缓存中,本项目使用SessionStorage。

可以在应用重新加载/重新登录时清除缓存,重新获取字典数据。

// ...

export const useDictStore = defineStore(
  'dict',
  () => {
   const dicts = ref<DictMap>({})
  
   const getDictData = async (dictType: string, refresh: boolean = false) => {
      return new Promise((resolve, reject) => {
        let data: any = dicts.value[dictType]
        // 根据缓存中是否有数据 && refresh(==false),读取缓存中的数据
        if (data && !refresh) {
          try {
            resolve(data)
          } catch (e) {
            reject(e)
          }
        } 
        // 否则从接口中获取数据
        else {
          const p = handleRepeatedRequest(dictType)
          resolve(p)
        }
      })
    }

    // 处理重复的 Promise 请求
    let promiseRecords = <PromiseRequestMap>{}
    // 多次相同的请求只调用一次
    const handleRepeatedRequest = (key: string) => {
      if (!promiseRecords[key]) {
        console.log('no repeated request')
        promiseRecords[key] = systemApi
          .getDicts(key)
          .then((res: any) => {
            // 存入缓存
            dicts.value[key] = res
            return res
          })
          .catch((e: Error) => {})
          .finally(() => {
            // 请求完毕,清理掉,否则会一直保留该promise
            promiseRecords[key] = null
          })
      } else {
        console.log('already has repeated request')
      }
      return promiseRecords[key]
    }
    
// ...

字典数据获取的方法写好了,接下来就开始封装字典组件。
主要实现以下功能:

  1. 渲染为 ElTag 或者 纯文本
  2. 根据 DictName 获取字典数据渲染
  3. 可以使用自定义的字典数据渲染
  4. 设置 refresh 属性为 true,实时获取最新的字典数据

代码如下:

<template>
  <span v-if="item">
    <span v-if="text">{{ item.label }}</span>
    <el-tag
      :type="$attrs.type || item.type"
      :class="item.class"
      :effect="$attrs.effect || item.effect"
      v-else
      >{{ item.label }}</el-tag
    >
  </span>
</template>

<script lang="ts" setup>
import { ref, computed, watch, watchEffect } from 'vue'
import { useDictStore } from '~/store/dict'
import { DictItem } from '#/dict'

const useDict = useDictStore()

const props = defineProps({
  value: {
    type: String,
    default: ''
  },
  // 根据 DictName 从接口/缓存获取字典数据集
  dictName: {
    type: String,
    default: ''
  },
  // 使用自定义的字典数据集,不使用接口/缓存中
  dictData: { type: Array },
  text: { type: Boolean, default: false },
  // 不使用缓存,实时获取获取
  refresh: { type: Boolean, default: false }
})

const dictOptions = ref<DictItem[]>([])

watchEffect(async () => {
  // 优先使用自定义的 DictData
  if (props.dictData) {
    dictOptions.value = props.dictData
  } else {
    dictOptions.value = (await useDict.getDictData(props.dictName, props.refresh)) as DictItem[]
  }
})

const item = computed<DictItem | undefined>(() => {
  const tempItem = dictOptions.value?.find((item) => item.value === props.value)
  if (tempItem) return tempItem
  else return undefined
})
</script>

OK,最后放在页面中实践一下,基本的字典标签功能都有了,可以根据自己的需要,继续完善。

<template>
  <div>
    <div>字典标签</div>
    <div class="m-4">
      <div class="mb-2">获取 clientType 的字典</div>
      <div>ElTag:<MoDict :value="formData.type" dictName="clientType" refresh /></div>
      <div>普通文本:<MoDict :value="formData.type" dictName="clientType" text /></div>
    </div>
    <el-divider></el-divider>
    <div class="m-4">
      <div class="mb-2">自定义字典数据</div>
      <div class="mb-2"><el-button @click="changeColor">Change Color</el-button></div>
      <MoDict :value="formData.color" :dictData="customDictData" />
    </div>
  </div>
</template>

<script lang="ts" setup>
import { reactive } from 'vue'

const formData = reactive({
  type: 'highschool',
  color: 'red'
})

const customDictData = [
  {
    label: '红色',
    value: 'red',
    class: 'text-red-500 bg-red-100 border-red-200'
  },
  {
    label: '蓝色',
    value: 'blue',
    class: 'text-blue-500 bg-blue-100 border-blue-200'
  },
  {
    label: '绿色',
    value: 'green',
    class: 'text-green-500 bg-green-100 border-green-200'
  },
  {
    label: '粉色',
    value: 'pink',
    class: 'text-pink-500 bg-pink-100 border-pink-200'
  }
]

const changeColor = () => {
  formData.color = customDictData[Math.floor(Math.random() * 4)].value
}
</script>

字典标签组件有了,那么常用的selector、radio、checkbox也可以封装起来。这里就不再赘述,详见mocha-vue3-system 仓库代码。

项目地址

本项目GIT地址:github.com/lucidity99/…

如果有帮助,给个star ✨ 点个赞

  • 6
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Vue 3.0是一个JavaScript框架,而Element-Plus是一个基于Vue 3.0开发的UI组件库,可以用于构建后台管理系统。开发Vue 3.0 Element-Plus的后台管理系统需要使用Vite 2.0作为构建工具,Vue-Router 4.0作为路由管理,Echarts 5.0作为数据可视化工具,以及Axios作为HTTP请求库。 要创建一个使用Vue 3.0和Element-Plus的后台管理系统,可以使用以下步骤: 1. 首先,使用命令行工具创建一个新的Vue项目,可以使用以下命令: ``` yarn create vite my-vue-app --template vue ``` 这将使用Vite模板创建一个名为"my-vue-app"的项目。 2. 安装Element-Plus包,可以使用以下命令: ``` yarn add element-plus ``` 这将安装Element-Plus UI组件库。 3. 在项目的主入口文件中引入Element-Plus并注册它,可以使用以下代码: ```javascript import { createApp } from 'vue'; import ElementPlus from 'element-plus'; import 'element-plus/dist/index.css'; const app = createApp(App); app.use(ElementPlus); ``` 4. 在需要使用Element-Plus组件Vue文件中,可以通过引入包并配置el-config-provider来使用Element-Plus,例如: ```html <template> <el-config-provider :locale="zhCn"> <Vab-App /> </el-config-provider> </template> <script setup> import zhCn from 'element-plus/lib/locale/lang/zh-cn'; </script> ``` 这将使用中文语言配置Element-Plus,并在Vab-App组件中使用Element-Plus组件。 通过以上步骤,你就可以开始开发使用Vue 3.0和Element-Plus的后台管理系统了。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [Vue 3.0+Vite 2.0+Vue-Router4.0+Element-Plus+Echarts 5.0后台管理系统](https://download.csdn.net/download/weixin_47367099/85260580)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [vue3+ElementPlus后台管理搭建](https://blog.csdn.net/qq_25286361/article/details/122132722)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

immocha

人生得意须尽欢

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值