注册
web

你没见过的【只读模式】,被我玩出花了

前言


不是标题党,不是标题党,不是标题党,重要的话说三遍!大家常见的【只读模式】,下面简称 readonly,可能最常用的是在 表单场景中,除了正常的表单场景,你还会想象到它可以应用在我们中后台场景的 编辑表格描述列表查询表格 吗?先看看效果吧 ~


表单场景


form-readonly.gif


表单列表场景


form-list-readonly.gif


描述列表场景


description-readonly.gif


查询表格场景


table-readonly.gif


编辑表格场景


edit-table-readonly.gif


上面看到的所有效果,背后都有 readonly 的存在



  1. 表单场景示例中表单列表场景示例中 使用 readonly,在实际业务中可能会应用到 编辑详情
  2. 描述列表场景示例中 使用 readonly,在实际业务中可能会应用到 单独的详情页 页面中
  3. 查询表格场景示例中 使用 readonly,在实际业务中应用很广泛,比如常见的日期,后端可能会返回字符串、空、时间戳,就不需要用户单独处理了 (挺麻烦的,不是吗)
  4. 编辑表格场景示例中 使用 readonly,在做一些类似 行编辑单元格编辑 功能中常用

下面就以 实现思路 + 伪代码 的方式和大家分享 readonly 的玩法


以 Date 组件为例


我们这里说的 Date 就是单纯的日期组件,不包含 pickermonth(月份)quarter(季度) 等,我们先思考一下,如何让 日期组件 可以在多处公用(查询表格、表单、编辑表格、描述列表)


多处公用


我们可以将 Date 组件进行封装,变成 ProDate,我们在 ProDate 中扩展一个属性为 readonly,在扩展一个插槽 readonly,方便用户自定义,以下为伪代码


<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 的实现


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 用法,我们上面伪代码没有写出来的,但是如果使用的话,还是如下使用


<template>
<AForm>
<AFormItem>
<ProDate v-model:value="xxxx" />
</AFormItem>
</AForm>

</template>

这样的话,我们如果是只读模式,可以在 ProDate 中增加 readonly 属性或插槽即可,当然,为了方便,我们实际上应该给 Form 组件也扩展一个 readonly 属性,然后 ProDatereadonly 属性的默认值应该是从 Form 中去取,这里实现我就不写出来了,思路的话可以通过在 Formprovide 注入默认值,然后 ProDate 中通过 inject


好了,我们集成到 表单中 就说这么多,实际上还是有很多的细节的,如果大家想看的话,后面再写吧


集成到 描述列表中


描述列表用的是 Descriptions 组件,因为大部分用来做详情页,比较简单,所以这里我将它封装成了 json 方式,用 schemas 属性来描述每一项的内容,大概是以下用法


<ProDescriptions
title="详情页"
:data-source="{time:'2023-01-30'}"
:schemas="[
{
label:'日期',
name:'time',
component:'ProDate'
}
]"

/>

解释一下:


上面的 schemas 中的项可以简单看成如下代码


<DescriptionsItem>
<ProDate
readonly
:value="get(dataSource,'time')"
/>

</DescriptionsItem>

我们在描述组件中应该始终传递 readonly: true,这样渲染出来虽然也是一个文本,但是经过了 ProDate 组件的日期处理,这样就可以很方便的直接展示了,而不用去写一个 render 函数自己去处理


集成到 查询表格中


实际上是和 集成到描述列表中 一样的思路,无非是将 ProDescriptions 组件换成 ProTable 组件,schemas 我们用同一套就可以,伪代码如下


<ProTable
title="详情页"
:data-source="{time:'2023-01-30'}"
:schemas="[
{
label:'日期',
name:'time',
component:'ProDate'
}
]"

/>

当然我们在 ProTable 内部对 schemas 的处理就要在 customRender 函数中去渲染了,内部实现的伪代码如下


<Table 
:columns="[
{
title:'日期',
dataIndex:'time',
customRender:({record}) =>{
return <ProDate
readonly
value={get(record,'time')}
/>
}
}
]"

/>

ProTableProDescriptions 的处理方式是类似的


集成到 编辑表格中


没啥好说的,实际上是和 集成到表单中 一样的思路,伪代码用法如下


<ProForm>
<ProEditTable
:data-source="{time:'2023-01-30'}"
:schemas="[
{
label:'日期',
name:'time',
component:'ProDate'
}
]"

/>

</ProForm>

我们还是复用同一套的 schemas,只不过组件换成了 ProEditTable,不同的是,我们在内部就不能写死 readonly 了,因为可能会 全局切换成编辑或者只读某一行切换成编辑或者只读某个单元格切换成编辑或者只读,所以我们这里应该对每一个单元格都需要定义一个 readonly 的响应式属性,方便切换,具体的实现就不细说了,因为偏题了


结语


好了,我们分享了 只读模式 下不同组件下的表现,而不是简单的在 表单中 为了好看而实现的,下期再见 ~


作者:一名爱小惠的前端
来源:juejin.cn/post/7329691357211361318

0 个评论

要回复文章请先登录注册