1+ import type { Recordable } from '@vben/types' ;
2+
13import { h } from 'vue' ;
24
5+ import { IconifyIcon } from '@vben/icons' ;
6+ import { $te } from '@vben/locales' ;
37import { setupVbenVxeTable , useVbenVxeGrid } from '@vben/plugins/vxe-table' ;
8+ import { get , isFunction , isString } from '@vben/utils' ;
9+
10+ import { objectOmit } from '@vueuse/core' ;
11+ import { Button , Image , Popconfirm , Switch , Tag } from 'ant-design-vue' ;
412
5- import { Button , Image } from 'ant-design-vue ' ;
13+ import { $t } from '#/locales ' ;
614
715import { useVbenForm } from './form' ;
816
@@ -15,7 +23,7 @@ setupVbenVxeTable({
1523 columnConfig : {
1624 resizable : true ,
1725 } ,
18- minHeight : 180 ,
26+ minHeight : 320 ,
1927 formConfig : {
2028 // 全局禁用vxe-table的表单配置,使用formOptions
2129 enabled : false ,
@@ -36,6 +44,15 @@ setupVbenVxeTable({
3644 } ,
3745 } ) ;
3846
47+ /**
48+ * 解决vxeTable在热更新时可能会出错的问题
49+ */
50+ vxeUI . renderer . forEach ( ( _item , key ) => {
51+ if ( key . startsWith ( 'Cell' ) ) {
52+ vxeUI . renderer . delete ( key ) ;
53+ }
54+ } ) ;
55+
3956 // 表格配置项可以用 cellRender: { name: 'CellImage' },
4057 vxeUI . renderer . add ( 'CellImage' , {
4158 renderTableDefault ( _renderOpts , params ) {
@@ -56,6 +73,186 @@ setupVbenVxeTable({
5673 } ,
5774 } ) ;
5875
76+ // 单元格渲染:Tag
77+ vxeUI . renderer . add ( 'CellTag' , {
78+ renderTableDefault ( { options, props } , { column, row } ) {
79+ const value = get ( row , column . field ) ;
80+ const tagOptions = options ?? [
81+ { color : 'success' , label : $t ( 'common.enabled' ) , value : 1 } ,
82+ { color : 'error' , label : $t ( 'common.disabled' ) , value : 0 } ,
83+ ] ;
84+ const tagItem = tagOptions . find ( ( item ) => item . value === value ) ;
85+ return h (
86+ Tag ,
87+ {
88+ ...props ,
89+ ...objectOmit ( tagItem ?? { } , [ 'label' ] ) ,
90+ } ,
91+ { default : ( ) => tagItem ?. label ?? value } ,
92+ ) ;
93+ } ,
94+ } ) ;
95+
96+ vxeUI . renderer . add ( 'CellSwitch' , {
97+ renderTableDefault ( { attrs, props } , { column, row } ) {
98+ const loadingKey = `__loading_${ column . field } ` ;
99+ const finallyProps = {
100+ checkedChildren : $t ( 'common.enabled' ) ,
101+ checkedValue : 1 ,
102+ unCheckedChildren : $t ( 'common.disabled' ) ,
103+ unCheckedValue : 0 ,
104+ ...props ,
105+ checked : row [ column . field ] ,
106+ loading : row [ loadingKey ] ?? false ,
107+ 'onUpdate:checked' : onChange ,
108+ } ;
109+ async function onChange ( newVal : any ) {
110+ row [ loadingKey ] = true ;
111+ try {
112+ const result = await attrs ?. beforeChange ?.( newVal , row ) ;
113+ if ( result !== false ) {
114+ row [ column . field ] = newVal ;
115+ }
116+ } finally {
117+ row [ loadingKey ] = false ;
118+ }
119+ }
120+ return h ( Switch , finallyProps ) ;
121+ } ,
122+ } ) ;
123+
124+ /**
125+ * 注册表格的操作按钮渲染器
126+ */
127+ vxeUI . renderer . add ( 'CellOperation' , {
128+ renderTableDefault ( { attrs, options, props } , { column, row } ) {
129+ const defaultProps = { size : 'small' , type : 'link' , ...props } ;
130+ let align = 'end' ;
131+ switch ( column . align ) {
132+ case 'center' : {
133+ align = 'center' ;
134+ break ;
135+ }
136+ case 'left' : {
137+ align = 'start' ;
138+ break ;
139+ }
140+ default : {
141+ align = 'end' ;
142+ break ;
143+ }
144+ }
145+ const presets : Recordable < Recordable < any > > = {
146+ delete : {
147+ danger : true ,
148+ text : $t ( 'common.delete' ) ,
149+ } ,
150+ edit : {
151+ text : $t ( 'common.edit' ) ,
152+ } ,
153+ } ;
154+ const operations : Array < Recordable < any > > = (
155+ options || [ 'edit' , 'delete' ]
156+ )
157+ . map ( ( opt ) => {
158+ if ( isString ( opt ) ) {
159+ return presets [ opt ]
160+ ? { code : opt , ...presets [ opt ] , ...defaultProps }
161+ : {
162+ code : opt ,
163+ text : $te ( `common.${ opt } ` ) ? $t ( `common.${ opt } ` ) : opt ,
164+ ...defaultProps ,
165+ } ;
166+ } else {
167+ return { ...defaultProps , ...presets [ opt . code ] , ...opt } ;
168+ }
169+ } )
170+ . map ( ( opt ) => {
171+ const optBtn : Recordable < any > = { } ;
172+ Object . keys ( opt ) . forEach ( ( key ) => {
173+ optBtn [ key ] = isFunction ( opt [ key ] ) ? opt [ key ] ( row ) : opt [ key ] ;
174+ } ) ;
175+ return optBtn ;
176+ } )
177+ . filter ( ( opt ) => opt . show !== false ) ;
178+
179+ function renderBtn ( opt : Recordable < any > , listen = true ) {
180+ return h (
181+ Button ,
182+ {
183+ ...props ,
184+ ...opt ,
185+ icon : undefined ,
186+ onClick : listen
187+ ? ( ) =>
188+ attrs ?. onClick ?.( {
189+ code : opt . code ,
190+ row,
191+ } )
192+ : undefined ,
193+ } ,
194+ {
195+ default : ( ) => {
196+ const content = [ ] ;
197+ if ( opt . icon ) {
198+ content . push (
199+ h ( IconifyIcon , { class : 'size-5' , icon : opt . icon } ) ,
200+ ) ;
201+ }
202+ content . push ( opt . text ) ;
203+ return content ;
204+ } ,
205+ } ,
206+ ) ;
207+ }
208+
209+ function renderConfirm ( opt : Recordable < any > ) {
210+ return h (
211+ Popconfirm ,
212+ {
213+ getPopupContainer ( el ) {
214+ return el . closest ( 'tbody' ) || document . body ;
215+ } ,
216+ placement : 'topLeft' ,
217+ title : $t ( 'ui.actionTitle.delete' , [ attrs ?. nameTitle || '' ] ) ,
218+ ...props ,
219+ ...opt ,
220+ icon : undefined ,
221+ onConfirm : ( ) => {
222+ attrs ?. onClick ?.( {
223+ code : opt . code ,
224+ row,
225+ } ) ;
226+ } ,
227+ } ,
228+ {
229+ default : ( ) => renderBtn ( { ...opt } , false ) ,
230+ description : ( ) =>
231+ h (
232+ 'div' ,
233+ { class : 'truncate' } ,
234+ $t ( 'ui.actionMessage.deleteConfirm' , [
235+ row [ attrs ?. nameField || 'name' ] ,
236+ ] ) ,
237+ ) ,
238+ } ,
239+ ) ;
240+ }
241+
242+ const btns = operations . map ( ( opt ) =>
243+ opt . code === 'delete' ? renderConfirm ( opt ) : renderBtn ( opt ) ,
244+ ) ;
245+ return h (
246+ 'div' ,
247+ {
248+ class : 'flex table-operations' ,
249+ style : { justifyContent : align } ,
250+ } ,
251+ btns ,
252+ ) ;
253+ } ,
254+ } ) ;
255+
59256 // 这里可以自行扩展 vxe-table 的全局配置,比如自定义格式化
60257 // vxeUI.formats.add
61258 } ,
@@ -64,4 +261,12 @@ setupVbenVxeTable({
64261
65262export { useVbenVxeGrid } ;
66263
264+ export type OnActionClickParams < T = Recordable < any > > = {
265+ code : string ;
266+ row : T ;
267+ } ;
268+
269+ export type OnActionClickFn < T = Recordable < any > > = (
270+ params : OnActionClickParams < T > ,
271+ ) => void ;
67272export type * from '@vben/plugins/vxe-table' ;
0 commit comments