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

前言

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

表单场景

表单列表场景

描述列表场景

查询表格场景

编辑表格场景

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

  1. 表单场景示例中 和 表单列表场景示例中 使用 readonly,在实际业务中可能会应用到 编辑、详情 中
  2. 描述列表场景示例中 使用 readonly,在实际业务中可能会应用到 单独的详情页 页面中
  3. 查询表格场景示例中 使用 readonly,在实际业务中应用很广泛,比如常见的日期,后端可能会返回字符串、空、时间戳,就不需要用户单独处理了 (挺麻烦的,不是吗)
  4. 编辑表格场景示例中 使用 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 的响应式属性,方便切换,具体的实现就不细说了,因为偏题了

结语

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

相关推荐
对空六课35 分钟前
两个统计平台数据不同:用同一时间窗和事件样本做差异定位
大数据·前端·网络·数据库
lpfasd1239 小时前
node-sass 停止维护!老项目迁移 Dart Sass 完整教程
前端·rust·sass
Data analyse45611 小时前
rnweb 回放数据量太大怎么办?分片存储与断点续传的实现思路
前端·网络·算法·数据分析
xiaogai_gai11 小时前
金蝶收料通知单查询策略实战教程:从源系统拉取到中间层落地的完整路径
java·前端·数据库
环境栈笔记12 小时前
指纹浏览器 WebRTC 怎么设置?从 ICE 候选、代理出口到功能验证
前端·人工智能·后端·自动化
赵大仁12 小时前
官网性能优化实战:LCP 3.2s → 2.1s、CLS 0.107 → 0,以及被我推翻的 6 个判断
前端·性能优化·cls·lcp·core web vitals
xiancai_xianyu12 小时前
Text2SQL落地生产库前,SQL要过哪三道闸
前端·数据库·sql·sql注入·text2sql·ai agent开发·企业级ai框架
小羊没烦恼!13 小时前
T-SQL查询进阶--理解SQL SERVER中的分区表
服务器·开发语言·前端·javascript·html
史瓦辛格13 小时前
给 DeepSeek Harness 写费用插件:把「这次会话花了多少钱」算准
前端·deepseek
星光不问赶路人13 小时前
Vue 3 + TypeScript 中 enum 的使用实践
前端·vue.js