“老哥,听说你写的页面挺多的,我这儿有一个页面,你觉得你需要多少代码?”
“我看看,也还行吧,不就是个查询表格嘛,现在大家都用 ProComponent
了,用那个写一下很快的。我想想,差不多 200 行左右就可以了吧。”
“那个呀,我知道,官方的二次封装组件库,200 行就可以了吗?嗯,可以。等等,你说的代码是否包含了操作按钮?”
“操作按钮?是指你图上的 [新增] [详情] 这些按钮吗?”
“是啊。”
“点了之后长啥样呀,你也没给我看呀!”
“哦,差不多长这个样。”
“哦~ 这样子啊,那这个简单,不就是个弹窗表单嘛,撑死再加个 100 行吧。”
“那 [删除] 呢?”
“就这。10 行就搞定了吧。”
“哈哈,你别着急嘛,还有批量删除呢!”
“哦哦,这也没多难啊,大部分都是表格自带的 Api
,删除完刷新下表格请求就完事儿了。无非就是记录下选项嘛,再给个 10 行我觉得就差不多了。不过我看你这勾选之后,页面下面还有个条,把 [批量删除] 按钮放那里了,有点意思,不过也没多难,再加个 20 行左右我觉得差不多了。”
“哈哈,是的,我算算,这差不多已经 340 行了吧。”
“是啊,写这样一个页面的话,这点代码量很正常。”
“那如果翻页后,仍然保留上个页面的选项呢?”
“这个呀,现在哪有人做这个呀,这都是现成的组件,你不嫌麻烦我还嫌麻烦呢,而且又不是必须的,爱谁谁做。”
“你就说你会不会吧!”
“你是故意找茬是吧!再说了,这也不难,antd
表格不是有个 preserveSelectedRowKeys
属性吗,我觉得可以用那个实现,或者监听下表格的选择事件,存个变量就好了。实在不行,就点页面单个删除呗,也没多麻烦吧。”
“说是这么说,确实也没必要,但我觉得有更好,如果要删除多个的话,每次删除总是要多点几次,而且删除完之后,数据不是没了嘛,那第二页的数据就会跑到第一个页面来,就会担心自己会不会删错。”
“还行吧,只要你不嫌累,你就写吧。我觉得也没啥,如果真要做,用那个 api 就好了,顶多再加个 20 行吧。”
“哦!对了,我忘记说了,这个数字悬浮上去是能看到选项的。”
“...你玩的还挺花,数据都有,搞个 Popover
套一下,就可以了吧。”
“是的,这么样一算的话,差不多有 400 行代码了吧。”
“嗯,你需求多嘛,差不多差不多。”
“哈哈,这个页面我总共花了 134 行代码,你觉得可行
“???你怎么做到的,给我看看。“
“我看了一下,有意思,这个组件用了一个大 json ,把配置传进去了是吧,这种封装还挺常见的。”
“是的,你看这是其中一个片段,表格的顶部查询,只需要指定 search: true
就可以了。”
{
title: '英文名',
key: 'en'
search: true
}
“这样子啊,那上面我看有 placeholder 呀,怎么没看你传进去。”
“哦,那个是自动生成的,像这里,会生成"请输入英文名"
。”
“那如果要自定义呢?”
“当然也可以,需要这样子写。”
{
title: '英文名',
key: 'en'
search: {
placeholder: '请输入英文名'
}
}
“哦~ 这还能是个对象是吧,那如果上面是个选择框呢?”
“也简单,添加 options
和 type: 'select'
就可以了。”
{
title: '职业',
key: 'class',
type: 'select',
options: [
{ label: '近卫干员', value: '1' },
{ label: '狙击干员', value: '2' },
{ label: '术师重装', value: '3' },
{ label: '医疗干员', value: '4' },
{ label: '重装干员', value: '5' },
{ label: '辅助干员', value: '6' },
{ label: '特种干员', value: '7' },
{ label: '先锋干员', value: '8' }
],
search: true
}
“你这 options
和 type
为什么没有放在 search
对象里面的呀?”
“哈哈,这你就不知道了吧,因为这样子写的话,表格也可以用这个配置,写在 search
对象里面的话,就只能查询区域自己用了。”
“表格也可以用这个配置?表格要这俩有啥用。”
“表格这一列,可以通过 options
翻译,假如数据是个 1
,那么这一列就会根据 options
去寻找这个 label
,这时候对应的 label
是 近卫干员
,所以页面上显示的就是 近卫干员
。另外如果表格有筛选,添加 filter: true
就会出现筛选了。”
“可以,还挺方便的,那 type
呢?这东西表格用不到吧。”
“是的,这个的话,其实是给 dialog
弹窗编辑用的。这样子的话可以让弹窗里面也展示一个选择框了。”
{
title: '职业',
key: 'class',
type: 'select',
options: [
{ label: '近卫干员', value: '1' },
{ label: '狙击干员', value: '2' },
{ label: '术师重装', value: '3' },
{ label: '医疗干员', value: '4' },
{ label: '重装干员', value: '5' },
{ label: '辅助干员', value: '6' },
{ label: '特种干员', value: '7' },
{ label: '先锋干员', value: '8' }
],
search: true,
+ dialog: true
}
“哦哦!明白了,指定了 dialog: true
后,就会在弹窗里面显示了是吧。”
“是的,那如果只在弹窗里展示,而表格不展示呢?”
“那可以这样子做。指定 table: false
就可以了。”
{
title: '职业',
key: 'class',
type: 'select',
options: [
{ label: '近卫干员', value: '1' },
{ label: '狙击干员', value: '2' },
{ label: '术师重装', value: '3' },
{ label: '医疗干员', value: '4' },
{ label: '重装干员', value: '5' },
{ label: '辅助干员', value: '6' },
{ label: '特种干员', value: '7' },
{ label: '先锋干员', value: '8' }
],
dialog: true,
+ table: false
}
“明白了。这是把弹窗、表格、查询揉在一起了是吧,对了,我怎么没有看到新增打开弹窗的代码呢?”
“那个呀,那个需要再加点代码。把 addApi
作为请求接口传进去,再指定 action="add"
就好了?”
<AySearchTable
title="Amiya 增删改查"
dialogFormExtend={{
fields: fields,
addApi
}}
>
<AyAction action="add">新增</AyAction>
</AySearchTable>
“这也可以?为什么?难到不需要监听按钮点击事件,然后控制弹窗显示,再请求接口,之后关闭弹窗刷新页面吗?”
“是的,这里默认认为弹窗和表格共用一个配置,且弹窗新增大多都是千篇一律的,所以把所有的操作封装了一下,就只剩下这么点了。当然如果太过复杂,或者跟表格都没有什么可以共用的列,就再定义一个 'fields: dialogFields'
,和表格完全分开用俩 fields
,各用各的。”
“哦哦,可以可以,那我理解了,修改也是同理吧。”
“是的,编辑的时候,不是会有默认值吗?此时我们把 record 传进去,当作表单的默认值就可以了。”
const ctrl: AyTableCtrlField = {
render: (value: string, record: Record) => {
return (
<AyCtrl>
<AyAction record={record} action="update">
编辑
</AyAction>
</AyCtrl>
)
}
}
<AySearchTable
title="Amiya 增删改查"
dialogFormExtend={{
fields: fields,
addApi
}}
/>
“可以,可以!这个组件把常用的操作都变成指令了啊。”
“是的呀,如果你的详情是需要请求接口的,那就不需要 record
了,删掉之后改成 detailApi
和 detailParams
, 分别是请求的接口和请求的参数,action="view"
指令会自动的把请求返回的数据,作为打开弹窗后表单的默认值的。”
<AyAction detailParams={record.sort_id} detailApi={detailApi} action="view">详情</AyAction>
“明白了,非常的棒!对了你刚刚还说到分页删除了,用这个组件是不是也很简单啊?”
“是的,我给你看一下,这个多个几步,第一步,先添加 selectionType="checkbox"
开启勾选;第二步,添加 selectShowKey="cn"
,让气泡悬浮时用它来决定展示选中的名称,并用 tag 标签裹住,因为 tag 标签可以删除的,这样子翻页之后也可以点击 tag 标签上面的 X ,来取消选项,不用再翻到上一页取消选择了;第三步,在删除按钮上添加 action="delete"
属性,批量删除上添加 action="batch-delete"
属性,标签上添加 deleteApi={deleteApi}
接口,就可以完成删除和批量删除的动作了。”
const ctrl: AyTableCtrlField = {
render: (value: string, record: Record) => {
return (
<AyCtrl>
<AyAction record={record} action="delete">
删除
</AyAction>
</AyCtrl>
)
}
}
<AySearchTable
title="Amiya 增删改查"
selectionType="checkbox"
rowKey="sort_id"
ctrl={ctrl}
selectShowKey="cn"
deleteApi={deleteApi}
>
<AyAction action="batch-delete">批量删除</AyAction>
</AySearchTable>
“哦哦,这样子就可以了吗?那还挺方便的,毕竟不需要自己写一堆代码来写。对了,我刚才看了下代码,上面有个 renderType: 'html'
,这是什么作用。”
{
title: '描述',
key: 'feature',
width: 200,
renderType: 'html'
}
“你猜猜?”
“把这一列渲染成 html 之类的?”
“是呀,除此之外,还有 unit
datetime
state
等等类型,你可以看看这个。”
“可以,那如果我要的这里面没有呢?”
“就等着你问这个呢,看来你用过其它的二次封装呀,这个组件提供两种方式,第一种可以指定 render
方法。”
{
title: '姓名',
key: 'cn',
search: true,
dialog: {
required: true
},
table: {
// 渲染自定义内容
render: (text: string, record: Record) => {
return (
<div>
<div>{record.cn}</div>
<div>{record.en}</div>
<div>{record.jp}</div>
</div>
)
}
}
},
“第二种,可以全局注册,注册完之后,可以像 renderType: 'star'
这样子使用。”
import { registerTableRender, RenderProps } from 'amiya'
/**
* @decs 注册 renderType
* @param renderTypeName string 注册类型名字
* @param text string 当前 col 的数据
* @param record object 当前 row 的数据
* @param field 当前配置配置项
*
* @returns ReactNode
*/
registerTableRender('renderTypeName', ({ text, record, field }: RenderProps) => {
return <span>{text}</span>
})
// 实际使用
const fields = [
{
renderType: 'renderTypeName' // 已经注册过后的名字
}
]
“好家伙,这跟我直接 render
也没什么区别嘛,不过用注册的方法,把 render 写在其它公共的地方的话,确实会让当前页面干净一点。”
“是的,两种方式自由选择。你看这样子一套下来,你看,是不是省了很多代码,如果你用 jsx 语法糖的话会更省,那样子只需要 90 行代码了。”
“对哦,我见到的其它二次封装就是少了这个,都用 json ,用起来编辑器右边空白一大片,代码又拉的老长,挺不爽的,你这样子写法我挺喜欢的,我跟他们提建议,他们都不听的,还云云这样容易控制什么的。”
<AyFields>
<AyField
title="头像"
key="icon"
width={80}
align="center"
renderType="image"
/>
<AyField title="姓名" key="cn" search />
<AyField title="英文名" key="en" search dialog table={false} />
{// ...}
</AyFields>
“真能省,还没见过这么能省的,不过,这个组件封装的这么多,别人会用吗?用的明白吗?”
“确实,如果不介绍介绍的话,没人用的明白。所以这个组件支持完整的 TypeScript 提示,也准备了文档,详细地介绍了表格的使用方式,你看左侧那一堆菜单,都是在介绍表格 api 的。”
“而且,也有完整的页面级别的示例,也可以做参考。”
“我看到了,这是个二次封装的组件库吧,我看还有其它组件,等我回头用用看体验一下。”
“好的,等你消息。”
这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。
V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。
V2EX is a community of developers, designers and creative people.