前言
不是标题党,不是标题党,不是标题党,重要的话说三遍!大家常见的【只读模式】,下面简称 readonly ,可能最常用的是在 表单场景中
,除了正常的表单场景,你还会想象到它可以应用在我们中后台场景的 编辑表格
、描述列表
、查询表格
吗?先看看效果吧 ~
表单场景
表单列表场景
描述列表场景
查询表格场景
编辑表格场景
上面看到的所有效果,背后都有 readonly
的存在
- 表单场景示例中 和 表单列表场景示例中 使用
readonly
,在实际业务中可能会应用到编辑
、详情
中 - 描述列表场景示例中 使用
readonly
,在实际业务中可能会应用到单独的详情页
页面中 - 查询表格场景示例中 使用
readonly
,在实际业务中应用很广泛,比如常见的日期,后端可能会返回字符串、空、时间戳,就不需要用户单独处理了 (挺麻烦的,不是吗) - 编辑表格场景示例中 使用
readonly
,在做一些类似行编辑
、单元格编辑
功能中常用
下面就以 实现思路 + 伪代码
的方式和大家分享 readonly
的玩法
以 Date 组件为例
我们这里说的 Date
就是单纯的日期组件,不包含 picker
为 month(月份)
、quarter(季度)
等,我们先思考一下,如何让 日期组件
可以在多处公用(查询表格、表单、编辑表格、描述列表)
多处公用
我们可以将 Date
组件进行封装,变成 ProDate
,我们在 ProDate
中扩展一个属性为 readonly
,在扩展一个插槽 readonly
,方便用户自定义,以下为伪代码
ts
<script lang="tsx">
import { DatePicker, TypographyText } from 'ant-design-vue'
export default defineComponent({
name: 'ProDate',
inheritAttrs: false,
props: {
readonly:{
type: Boolean,
default:false
}
},
slots: {
readonly: { rawValue: any }
},
setup(props, { slots, expose }) {
const getReadonlyText = computed(() => {
const value = toValue(dateValue)
return getDateText(value, {
format: toValue(valueFormat),
defaultValue: toValue(emptyText),
})
})
return {
readonly,
getReadonlyText,
}
},
render() {
const {
readonly,
getReadonlyText,
} = this
if (readonly)
return $slots.readonly?.({ rawValue: xxx }) ?? getReadonlyText
return <DatePicker {...xxx} v-slots={...xxx} />
},
})
</script>
上面的伪代码中,我们扩展了 readonly
属性和 readonly
插槽,我们 readonly 模式下
会调用 getDateText
方法返回值,下面代码是 getDateText
的实现
ts
interface GetDateTextOptions {
format: string | ((date: number | string | Dayjs) => any)
defaultValue: any
}
// 工具函数
export function getDateText(date: Dayjs | number | string | undefined | null, options: GetDateTextOptions) {
const {
format,
defaultValue,
} = options
if (isNull(date) || isUndefined(date))
return defaultValue
if (isNumber(date) || isString(date)) {
// 可能为时间戳或者字符串
return isFunction(format)
? format(date)
: dayjs(date).format(format)
}
if (isDayjs(date)) {
return isFunction(format)
? format(date)
: date.format(format)
}
return defaultValue
}
好了,伪代码我们实现完了,现在我们就假设我们的 ProDate
就是加强版的 DatePicker
,这样我们就能很方便的集成到各个组件中了
集成到 表单中
因为我们是加强版的 DatePicker
,还应该支持原来的 DatePicker
用法,我们上面伪代码没有写出来的,但是如果使用的话,还是如下使用
ts
<template>
<AForm>
<AFormItem>
<ProDate v-model:value="xxxx" />
</AFormItem>
</AForm>
</template>
这样的话,我们如果是只读模式,可以在 ProDate
中增加 readonly
属性或插槽即可,当然,为了方便,我们实际上应该给 Form
组件也扩展一个 readonly
属性,然后 ProDate
的 readonly
属性的默认值应该是从 Form
中去取,这里实现我就不写出来了,思路的话可以通过在 Form
中 provide
注入默认值,然后 ProDate
中通过 inject
读
好了,我们集成到 表单中
就说这么多,实际上还是有很多的细节的,如果大家想看的话,后面再写吧
集成到 描述列表中
描述列表用的是 Descriptions
组件,因为大部分用来做详情页,比较简单,所以这里我将它封装成了 json
方式,用 schemas
属性来描述每一项的内容,大概是以下用法
ts
<ProDescriptions
title="详情页"
:data-source="{time:'2023-01-30'}"
:schemas="[
{
label:'日期',
name:'time',
component:'ProDate'
}
]"
/>
解释一下:
上面的 schemas
中的项可以简单看成如下代码
ts
<DescriptionsItem>
<ProDate
readonly
:value="get(dataSource,'time')"
/>
</DescriptionsItem>
我们在描述组件中应该始终传递 readonly: true
,这样渲染出来虽然也是一个文本,但是经过了 ProDate
组件的日期处理,这样就可以很方便的直接展示了,而不用去写一个 render
函数自己去处理
集成到 查询表格中
实际上是和 集成到描述列表中 一样的思路,无非是将 ProDescriptions
组件换成 ProTable
组件,schemas
我们用同一套就可以,伪代码如下
ts
<ProTable
title="详情页"
:data-source="{time:'2023-01-30'}"
:schemas="[
{
label:'日期',
name:'time',
component:'ProDate'
}
]"
/>
当然我们在 ProTable
内部对 schemas
的处理就要在 customRender
函数中去渲染了,内部实现的伪代码如下
ts
<Table
:columns="[
{
title:'日期',
dataIndex:'time',
customRender:({record}) =>{
return <ProDate
readonly
value={get(record,'time')}
/>
}
}
]"
/>
ProTable
和 ProDescriptions
的处理方式是类似的
集成到 编辑表格中
没啥好说的,实际上是和 集成到表单中 一样的思路,伪代码用法如下
ts
<ProForm>
<ProEditTable
:data-source="{time:'2023-01-30'}"
:schemas="[
{
label:'日期',
name:'time',
component:'ProDate'
}
]"
/>
</ProForm>
我们还是复用同一套的 schemas
,只不过组件换成了 ProEditTable
,不同的是,我们在内部就不能写死 readonly
了,因为可能会 全局切换成编辑或者只读
、某一行切换成编辑或者只读
、某个单元格切换成编辑或者只读
,所以我们这里应该对每一个单元格都需要定义一个 readonly
的响应式属性,方便切换,具体的实现就不细说了,因为偏题了
结语
好了,我们分享了 只读模式
下不同组件下的表现,而不是简单的在 表单中
为了好看而实现的,下期再见 ~