在后台管理系统中存在大量的字典数据,比如各种状态、类型。后端有时会返回字典的code,前端需要通过字典表来渲染出对应的文字,有的后端比较体贴直接给我们字典的文字,前端无需处理即可展示。
但是,为了优化用户体验,常常需要对不同的字典code显示不同的UI效果,比如“禁用”使用红色标签,“启用”使用绿色,这样页面可读性就会好很多。
最简单的写法,直接在表格插槽中写:
<el-table-column label="状态">
<template #default="scope">
<el-tag :type="scope.row.status === '0' ? 'danger' : 'success'">
{{ scope.row.status === '0' ? '禁用' : '启用' }}
</el-tag>
</template>
</el-table-column>
如果使用字典的地方很少,字典也不会变,这样写没问题。但是实际系统中往往定义了几十上百个字典类型,每个类型下的字典项也会变化,上面的写法就显得吃力了,这时封装一个字典标签组件非常必要。
约定接口请求和返回
在apiFox中新建一个接口/getDicts
,用于mock返回
建立了2个期望,用于模拟不同字典类型的返回值
在项目中配置好api并使用后,可以看到如期返回
在页面中获取字典数据
给字典组件添加字典数据源,在我经历过的代码汇中,见过3种方式:
- 在字典组件中请求接口,获取数据源
- 把数据源一次性保存到vuex中
- 每次进入页面获取数据源
第一种每用一次字典组件就会发送一次请求,太频繁了。 第二种基本不需要几次请求,但是数据变化不及时,如果在使用期间修改字典选项,前端没反应。 我觉得第三种更合理,实现起来也不麻烦 😁。
进入页面,就获取所需字典的数据源,这里采用vue3的组合式函数(hook),让代码更加简洁高效
hooks/useDict.ts
一个页面可能有多个字典类型,此处接口只能查询一个类型,所以需要用Promise.all配合forEach返回数组中所有类型的字典选项。
当然也可以把mock接口改成支持多个类型的(前端更简单),但是想试试promise.all所以没改,大家可以根据实际情况修改。
import { reactive, onMounted } from 'vue'
import system from '~/api/system'
import systemApi from '~/api/system'
export function useDict(dictType: Array<string>) {
let dicts = reactive<any>({})
onMounted(() => {
let ps: Promise<any>[] = []
dictType.forEach((dt, index) => {
ps[index] = systemApi.getDicts(dt).then((res) => {
return { type: dt, values: res }
})
})
Promise.all(ps).then((res) => {
res.forEach((val) => {
dicts[val.type] = val.values
})
})
})
return { dicts }
}
在页面中使用
import { useDict } from '~/hooks/useDict'
...
let { dicts } = useDict(['status', 'clientType'])
基于el-tag,二次封装字典组件
<el-table-column label="状态">
<template #default="scope">
<MoDict :value="scope.row.status" :dicts="dicts.status" />
</template>
</el-table-column>
</template>
根据和后端约定好的字典结构,定义一个字典元素接口 DictItem
interface DictItem {
label: string
value: string
type?: string
class?: string
effect?: string
}
组件props,使用Dict组件时,只需传入字典选项的的数组和字段值,然后一切交给组件来处理
const props = defineProps<{
value: string
dicts: any
}>()
给组件增加对应的参数,选择了一些el-tag的属性,如有需要可以继续扩展。
属性名 | 说明 | 类型 | 默认 |
---|---|---|---|
type | Tag 的类型 | enum ** | '' |
effect | Tag 的主题 | enum ** | light |
const item = props.dicts.find((val: DictItem) => val.value === props.value) as unknown as DictItem
const label = item.label || props.value
const tagType = item.type
const tagClass = item.class || ''
const tagEffect = item.effect || 'light'
完工!
结束语
今天又被自己蠢哭了,在我写完以上useDict的hook后,网络请求始终只有最后一个字典类型,无论如何都不返回2个。改成直接用axios.get请求又是正常的。难道二次封装的axois有问题??? 看了半天也没看出问题。
在不断试错中,终于发现问题,之前封装axios的时候,不是增加了取消请求方法么,这个消失的请求就是被取消了。。。 然而F12并没有看出来“已取消”而是直接不见了。
找到取消请求拼接标识这块,补上get请求参数,一切正常。
function getRequestKey(config: AxiosRequestConfig) {
return (
(config.method || '') +
config.url +
'?' +
qs.stringify(config?.data) +
qs.stringify(config?.params) // 漏了这一截,get请求的参数没有拼接
)
}
这里不仅仅是浪费了我1个多小时的时间,还让人非常焦躁,怀疑自己对promise、axios的理解出了问题。实际还是在写代码时不够严谨,测试不完全,基础没打好,后面容易出问题。
项目地址
本项目GIT地址:github.com/lucidity99/…
如果有帮助,给个star ✨ 点个赞👍