feat: 【ele】增加 TableAction 组件
							parent
							
								
									a026f88fde
								
							
						
					
					
						commit
						268a69c530
					
				|  | @ -86,17 +86,17 @@ const [FormModal, formModalApi] = useVbenModal({ | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| /** 创建示例联系人 */ | /** 创建示例联系人 */ | ||||||
| function onCreate() { | function handleCreate() { | ||||||
|   formModalApi.setData({}).open(); |   formModalApi.setData({}).open(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** 编辑示例联系人 */ | /** 编辑示例联系人 */ | ||||||
| function onEdit(row: Demo01ContactApi.Demo01Contact) { | function handleEdit(row: Demo01ContactApi.Demo01Contact) { | ||||||
|   formModalApi.setData(row).open(); |   formModalApi.setData(row).open(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** 删除示例联系人 */ | /** 删除示例联系人 */ | ||||||
| async function onDelete(row: Demo01ContactApi.Demo01Contact) { | async function handleDelete(row: Demo01ContactApi.Demo01Contact) { | ||||||
|   const hideLoading = message.loading({ |   const hideLoading = message.loading({ | ||||||
|     content: $t('ui.actionMessage.deleting', [row.id]), |     content: $t('ui.actionMessage.deleting', [row.id]), | ||||||
|     duration: 0, |     duration: 0, | ||||||
|  | @ -115,7 +115,7 @@ async function onDelete(row: Demo01ContactApi.Demo01Contact) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** 批量删除示例联系人 */ | /** 批量删除示例联系人 */ | ||||||
| async function onDeleteBatch() { | async function handleDeleteBatch() { | ||||||
|   const hideLoading = message.loading({ |   const hideLoading = message.loading({ | ||||||
|     content: $t('ui.actionMessage.deleting'), |     content: $t('ui.actionMessage.deleting'), | ||||||
|     duration: 0, |     duration: 0, | ||||||
|  | @ -173,7 +173,6 @@ onMounted(() => { | ||||||
|             class="w-full" |             class="w-full" | ||||||
|           /> |           /> | ||||||
|         </Form.Item> |         </Form.Item> | ||||||
|         <!-- TODO @puhui999:貌似性别的宽度不对;并且选择后,会变哈; --> |  | ||||||
|         <Form.Item label="性别" name="sex"> |         <Form.Item label="性别" name="sex"> | ||||||
|           <Select |           <Select | ||||||
|             v-model:value="queryParams.sex" |             v-model:value="queryParams.sex" | ||||||
|  | @ -181,7 +180,6 @@ onMounted(() => { | ||||||
|             allow-clear |             allow-clear | ||||||
|             class="w-full" |             class="w-full" | ||||||
|           > |           > | ||||||
|             <!-- TODO @puhui999:要不咱还是把 getIntDictOptions 还是搞出来?总归方便点~ --> |  | ||||||
|             <Select.Option |             <Select.Option | ||||||
|               v-for="dict in getDictOptions( |               v-for="dict in getDictOptions( | ||||||
|                 DICT_TYPE.SYSTEM_USER_SEX, |                 DICT_TYPE.SYSTEM_USER_SEX, | ||||||
|  | @ -221,7 +219,7 @@ onMounted(() => { | ||||||
|             class="ml-2" |             class="ml-2" | ||||||
|             :icon="h(Plus)" |             :icon="h(Plus)" | ||||||
|             type="primary" |             type="primary" | ||||||
|             @click="onCreate" |             @click="handleCreate" | ||||||
|             v-access:code="['infra:demo01-contact:create']" |             v-access:code="['infra:demo01-contact:create']" | ||||||
|           > |           > | ||||||
|             {{ $t('ui.actionTitle.create', ['示例联系人']) }} |             {{ $t('ui.actionTitle.create', ['示例联系人']) }} | ||||||
|  | @ -242,7 +240,7 @@ onMounted(() => { | ||||||
|             danger |             danger | ||||||
|             class="ml-2" |             class="ml-2" | ||||||
|             :disabled="isEmpty(checkedIds)" |             :disabled="isEmpty(checkedIds)" | ||||||
|             @click="onDeleteBatch" |             @click="handleDeleteBatch" | ||||||
|             v-access:code="['infra:demo01-contact:delete']" |             v-access:code="['infra:demo01-contact:delete']" | ||||||
|           > |           > | ||||||
|             批量删除 |             批量删除 | ||||||
|  | @ -282,7 +280,7 @@ onMounted(() => { | ||||||
|             <Button |             <Button | ||||||
|               size="small" |               size="small" | ||||||
|               type="link" |               type="link" | ||||||
|               @click="onEdit(row as any)" |               @click="handleEdit(row as any)" | ||||||
|               v-access:code="['infra:demo01-contact:update']" |               v-access:code="['infra:demo01-contact:update']" | ||||||
|             > |             > | ||||||
|               {{ $t('ui.actionTitle.edit') }} |               {{ $t('ui.actionTitle.edit') }} | ||||||
|  | @ -292,7 +290,7 @@ onMounted(() => { | ||||||
|               type="link" |               type="link" | ||||||
|               danger |               danger | ||||||
|               class="ml-2" |               class="ml-2" | ||||||
|               @click="onDelete(row as any)" |               @click="handleDelete(row as any)" | ||||||
|               v-access:code="['infra:demo01-contact:delete']" |               v-access:code="['infra:demo01-contact:delete']" | ||||||
|             > |             > | ||||||
|               {{ $t('ui.actionTitle.delete') }} |               {{ $t('ui.actionTitle.delete') }} | ||||||
|  |  | ||||||
|  | @ -0,0 +1,13 @@ | ||||||
|  | export const ACTION_ICON = { | ||||||
|  |   DOWNLOAD: 'lucide:download', | ||||||
|  |   UPLOAD: 'lucide:upload', | ||||||
|  |   ADD: 'lucide:plus', | ||||||
|  |   EDIT: 'lucide:edit', | ||||||
|  |   DELETE: 'lucide:trash', | ||||||
|  |   REFRESH: 'lucide:refresh-cw', | ||||||
|  |   SEARCH: 'lucide:search', | ||||||
|  |   FILTER: 'lucide:filter', | ||||||
|  |   MORE: 'lucide:ellipsis-vertical', | ||||||
|  |   VIEW: 'lucide:eye', | ||||||
|  |   COPY: 'lucide:copy', | ||||||
|  | }; | ||||||
|  | @ -0,0 +1,4 @@ | ||||||
|  | export * from './icons'; | ||||||
|  | 
 | ||||||
|  | export { default as TableAction } from './table-action.vue'; | ||||||
|  | export * from './typing'; | ||||||
|  | @ -0,0 +1,289 @@ | ||||||
|  | <!-- add by 星语:参考 vben2 的方式,增加 TableAction 组件 --> | ||||||
|  | <script setup lang="ts"> | ||||||
|  | import type { PropType } from 'vue'; | ||||||
|  | 
 | ||||||
|  | import type { ActionItem, PopConfirm } from './typing'; | ||||||
|  | 
 | ||||||
|  | import { computed, toRaw } from 'vue'; | ||||||
|  | 
 | ||||||
|  | import { useAccess } from '@vben/access'; | ||||||
|  | import { IconifyIcon } from '@vben/icons'; | ||||||
|  | import { $t } from '@vben/locales'; | ||||||
|  | import { isBoolean, isFunction } from '@vben/utils'; | ||||||
|  | 
 | ||||||
|  | import { | ||||||
|  |   ElButton, | ||||||
|  |   ElDropdown, | ||||||
|  |   ElDropdownItem, | ||||||
|  |   ElDropdownMenu, | ||||||
|  |   ElPopconfirm, | ||||||
|  |   ElSpace, | ||||||
|  |   ElTooltip, | ||||||
|  | } from 'element-plus'; | ||||||
|  | 
 | ||||||
|  | const props = defineProps({ | ||||||
|  |   actions: { | ||||||
|  |     type: Array as PropType<ActionItem[]>, | ||||||
|  |     default() { | ||||||
|  |       return []; | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   dropDownActions: { | ||||||
|  |     type: Array as PropType<ActionItem[]>, | ||||||
|  |     default() { | ||||||
|  |       return []; | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   divider: { | ||||||
|  |     type: Boolean, | ||||||
|  |     default: true, | ||||||
|  |   }, | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | const { hasAccessByCodes } = useAccess(); | ||||||
|  | 
 | ||||||
|  | function isIfShow(action: ActionItem): boolean { | ||||||
|  |   const ifShow = action.ifShow; | ||||||
|  | 
 | ||||||
|  |   let isIfShow = true; | ||||||
|  | 
 | ||||||
|  |   if (isBoolean(ifShow)) { | ||||||
|  |     isIfShow = ifShow; | ||||||
|  |   } | ||||||
|  |   if (isFunction(ifShow)) { | ||||||
|  |     isIfShow = ifShow(action); | ||||||
|  |   } | ||||||
|  |   return isIfShow; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const getActions = computed(() => { | ||||||
|  |   return (toRaw(props.actions) || []) | ||||||
|  |     .filter((action) => { | ||||||
|  |       return ( | ||||||
|  |         (hasAccessByCodes(action.auth || []) || | ||||||
|  |           (action.auth || []).length === 0) && | ||||||
|  |         isIfShow(action) | ||||||
|  |       ); | ||||||
|  |     }) | ||||||
|  |     .map((action) => { | ||||||
|  |       const { popConfirm } = action; | ||||||
|  |       return { | ||||||
|  |         type: action.type || 'primary', | ||||||
|  |         ...action, | ||||||
|  |         ...popConfirm, | ||||||
|  |         onConfirm: popConfirm?.confirm, | ||||||
|  |         onCancel: popConfirm?.cancel, | ||||||
|  |         enable: !!popConfirm, | ||||||
|  |       }; | ||||||
|  |     }); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | const getDropdownList = computed((): any[] => { | ||||||
|  |   return (toRaw(props.dropDownActions) || []) | ||||||
|  |     .filter((action) => { | ||||||
|  |       return ( | ||||||
|  |         (hasAccessByCodes(action.auth || []) || | ||||||
|  |           (action.auth || []).length === 0) && | ||||||
|  |         isIfShow(action) | ||||||
|  |       ); | ||||||
|  |     }) | ||||||
|  |     .map((action, index) => { | ||||||
|  |       const { label, popConfirm } = action; | ||||||
|  |       return { | ||||||
|  |         ...action, | ||||||
|  |         ...popConfirm, | ||||||
|  |         onConfirm: popConfirm?.confirm, | ||||||
|  |         onCancel: popConfirm?.cancel, | ||||||
|  |         text: label, | ||||||
|  |         divider: | ||||||
|  |           index < props.dropDownActions.length - 1 ? props.divider : false, | ||||||
|  |       }; | ||||||
|  |     }); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | function getPopConfirmProps(attrs: PopConfirm) { | ||||||
|  |   const originAttrs: any = attrs; | ||||||
|  |   delete originAttrs.icon; | ||||||
|  |   if (attrs.confirm && isFunction(attrs.confirm)) { | ||||||
|  |     originAttrs.onConfirm = attrs.confirm; | ||||||
|  |     delete originAttrs.confirm; | ||||||
|  |   } | ||||||
|  |   if (attrs.cancel && isFunction(attrs.cancel)) { | ||||||
|  |     originAttrs.onCancel = attrs.cancel; | ||||||
|  |     delete originAttrs.cancel; | ||||||
|  |   } | ||||||
|  |   return originAttrs; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function getButtonProps(action: ActionItem) { | ||||||
|  |   const res = { | ||||||
|  |     type: action.type || 'primary', | ||||||
|  |     ...action, | ||||||
|  |   }; | ||||||
|  |   delete res.icon; | ||||||
|  |   return res; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function handleMenuClick(command: any) { | ||||||
|  |   const action = getDropdownList.value[command]; | ||||||
|  |   if (action.onClick && isFunction(action.onClick)) { | ||||||
|  |     action.onClick(); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <template> | ||||||
|  |   <div class="table-actions"> | ||||||
|  |     <ElSpace | ||||||
|  |       :size=" | ||||||
|  |         getActions?.some((item: ActionItem) => item.type === 'text') ? 0 : 8 | ||||||
|  |       " | ||||||
|  |     > | ||||||
|  |       <template v-for="(action, index) in getActions" :key="index"> | ||||||
|  |         <ElPopconfirm | ||||||
|  |           v-if="action.popConfirm" | ||||||
|  |           v-bind="getPopConfirmProps(action.popConfirm)" | ||||||
|  |         > | ||||||
|  |           <template v-if="action.popConfirm.icon" #icon> | ||||||
|  |             <IconifyIcon :icon="action.popConfirm.icon" /> | ||||||
|  |           </template> | ||||||
|  |           <template #reference> | ||||||
|  |             <ElTooltip | ||||||
|  |               v-if=" | ||||||
|  |                 action.tooltip && | ||||||
|  |                 ((typeof action.tooltip === 'string' && action.tooltip) || | ||||||
|  |                   (typeof action.tooltip === 'object' && | ||||||
|  |                     action.tooltip.content)) | ||||||
|  |               " | ||||||
|  |               v-bind=" | ||||||
|  |                 typeof action.tooltip === 'string' | ||||||
|  |                   ? { content: action.tooltip } | ||||||
|  |                   : { ...action.tooltip } | ||||||
|  |               " | ||||||
|  |             > | ||||||
|  |               <ElButton v-bind="getButtonProps(action)"> | ||||||
|  |                 <template v-if="action.icon"> | ||||||
|  |                   <IconifyIcon :icon="action.icon" class="mr-1" /> | ||||||
|  |                 </template> | ||||||
|  |                 {{ action.label }} | ||||||
|  |               </ElButton> | ||||||
|  |             </ElTooltip> | ||||||
|  |             <ElButton v-else v-bind="getButtonProps(action)"> | ||||||
|  |               <template v-if="action.icon"> | ||||||
|  |                 <IconifyIcon :icon="action.icon" class="mr-1" /> | ||||||
|  |               </template> | ||||||
|  |               {{ action.label }} | ||||||
|  |             </ElButton> | ||||||
|  |           </template> | ||||||
|  |         </ElPopconfirm> | ||||||
|  |         <ElTooltip | ||||||
|  |           v-else-if=" | ||||||
|  |             action.tooltip && | ||||||
|  |             ((typeof action.tooltip === 'string' && action.tooltip) || | ||||||
|  |               (typeof action.tooltip === 'object' && action.tooltip.content)) | ||||||
|  |           " | ||||||
|  |           v-bind=" | ||||||
|  |             typeof action.tooltip === 'string' | ||||||
|  |               ? { content: action.tooltip } | ||||||
|  |               : { ...action.tooltip } | ||||||
|  |           " | ||||||
|  |         > | ||||||
|  |           <ElButton v-bind="getButtonProps(action)" @click="action.onClick"> | ||||||
|  |             <template v-if="action.icon"> | ||||||
|  |               <IconifyIcon :icon="action.icon" class="mr-1" /> | ||||||
|  |             </template> | ||||||
|  |             {{ action.label }} | ||||||
|  |           </ElButton> | ||||||
|  |         </ElTooltip> | ||||||
|  |         <ElButton | ||||||
|  |           v-else | ||||||
|  |           v-bind="getButtonProps(action)" | ||||||
|  |           @click="action.onClick" | ||||||
|  |         > | ||||||
|  |           <template v-if="action.icon"> | ||||||
|  |             <IconifyIcon :icon="action.icon" class="mr-1" /> | ||||||
|  |           </template> | ||||||
|  |           {{ action.label }} | ||||||
|  |         </ElButton> | ||||||
|  |       </template> | ||||||
|  |     </ElSpace> | ||||||
|  | 
 | ||||||
|  |     <ElDropdown v-if="getDropdownList.length > 0" @command="handleMenuClick"> | ||||||
|  |       <slot name="more"> | ||||||
|  |         <ElButton :type="getDropdownList[0].type"> | ||||||
|  |           {{ $t('page.action.more') }} | ||||||
|  |           <IconifyIcon icon="lucide:ellipsis-vertical" class="ml-1" /> | ||||||
|  |         </ElButton> | ||||||
|  |       </slot> | ||||||
|  |       <template #dropdown> | ||||||
|  |         <ElDropdownMenu> | ||||||
|  |           <ElDropdownItem | ||||||
|  |             v-for="(action, index) in getDropdownList" | ||||||
|  |             :key="index" | ||||||
|  |             :command="index" | ||||||
|  |             :disabled="action.disabled" | ||||||
|  |           > | ||||||
|  |             <template v-if="action.popConfirm"> | ||||||
|  |               <ElPopconfirm v-bind="getPopConfirmProps(action.popConfirm)"> | ||||||
|  |                 <template v-if="action.popConfirm.icon" #icon> | ||||||
|  |                   <IconifyIcon :icon="action.popConfirm.icon" /> | ||||||
|  |                 </template> | ||||||
|  |                 <template #reference> | ||||||
|  |                   <div> | ||||||
|  |                     <IconifyIcon v-if="action.icon" :icon="action.icon" /> | ||||||
|  |                     <span :class="action.icon ? 'ml-1' : ''"> | ||||||
|  |                       {{ action.text }} | ||||||
|  |                     </span> | ||||||
|  |                   </div> | ||||||
|  |                 </template> | ||||||
|  |               </ElPopconfirm> | ||||||
|  |             </template> | ||||||
|  |             <template v-else> | ||||||
|  |               <div> | ||||||
|  |                 <IconifyIcon v-if="action.icon" :icon="action.icon" /> | ||||||
|  |                 {{ action.label }} | ||||||
|  |               </div> | ||||||
|  |             </template> | ||||||
|  |           </ElDropdownItem> | ||||||
|  |         </ElDropdownMenu> | ||||||
|  |       </template> | ||||||
|  |     </ElDropdown> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | <style lang="scss"> | ||||||
|  | .table-actions { | ||||||
|  |   .el-button--text { | ||||||
|  |     padding: 4px; | ||||||
|  |     margin-left: 0; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .el-button .iconify + span, | ||||||
|  |   .el-button span + .iconify { | ||||||
|  |     margin-inline-start: 4px; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .iconify { | ||||||
|  |     display: inline-flex; | ||||||
|  |     align-items: center; | ||||||
|  |     width: 1em; | ||||||
|  |     height: 1em; | ||||||
|  |     font-style: normal; | ||||||
|  |     line-height: 0; | ||||||
|  |     vertical-align: -0.125em; | ||||||
|  |     color: inherit; | ||||||
|  |     text-align: center; | ||||||
|  |     text-transform: none; | ||||||
|  |     text-rendering: optimizelegibility; | ||||||
|  |     -webkit-font-smoothing: antialiased; | ||||||
|  |     -moz-osx-font-smoothing: grayscale; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .el-popconfirm { | ||||||
|  |   .el-popconfirm__action { | ||||||
|  |     .el-button { | ||||||
|  |       margin-left: 8px !important; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </style> | ||||||
|  | @ -0,0 +1,36 @@ | ||||||
|  | import type { ButtonProps } from 'element-plus'; | ||||||
|  | 
 | ||||||
|  | export type ButtonType = | ||||||
|  |   | 'danger' | ||||||
|  |   | 'default' | ||||||
|  |   | 'info' | ||||||
|  |   | 'primary' | ||||||
|  |   | 'success' | ||||||
|  |   | 'text' | ||||||
|  |   | 'warning'; | ||||||
|  | 
 | ||||||
|  | export interface PopConfirm { | ||||||
|  |   title: string; | ||||||
|  |   okText?: string; | ||||||
|  |   cancelText?: string; | ||||||
|  |   confirm: () => void; | ||||||
|  |   cancel?: () => void; | ||||||
|  |   icon?: string; | ||||||
|  |   disabled?: boolean; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export interface ActionItem extends Partial<ButtonProps> { | ||||||
|  |   onClick?: () => void; | ||||||
|  |   type?: ButtonType; | ||||||
|  |   label?: string; | ||||||
|  |   color?: 'error' | 'success' | 'warning'; | ||||||
|  |   icon?: string; | ||||||
|  |   popConfirm?: PopConfirm; | ||||||
|  |   disabled?: boolean; | ||||||
|  |   divider?: boolean; | ||||||
|  |   // 权限编码控制是否显示
 | ||||||
|  |   auth?: string[]; | ||||||
|  |   // 业务控制是否显示
 | ||||||
|  |   ifShow?: ((action: ActionItem) => boolean) | boolean; | ||||||
|  |   tooltip?: string | { [key: string]: any; content?: string }; | ||||||
|  | } | ||||||
|  | @ -23,7 +23,8 @@ | ||||||
|     "cancel": "Cancel", |     "cancel": "Cancel", | ||||||
|     "confirm": "Confirm", |     "confirm": "Confirm", | ||||||
|     "reset": "Reset", |     "reset": "Reset", | ||||||
|     "search": "Search" |     "search": "Search", | ||||||
|  |     "more": "More" | ||||||
|   }, |   }, | ||||||
|   "tenant": { |   "tenant": { | ||||||
|     "placeholder": "Please select tenant", |     "placeholder": "Please select tenant", | ||||||
|  |  | ||||||
|  | @ -23,7 +23,8 @@ | ||||||
|     "cancel": "取消", |     "cancel": "取消", | ||||||
|     "confirm": "确认", |     "confirm": "确认", | ||||||
|     "reset": "重置", |     "reset": "重置", | ||||||
|     "search": "搜索" |     "search": "搜索", | ||||||
|  |     "more": "更多" | ||||||
|   }, |   }, | ||||||
|   "tenant": { |   "tenant": { | ||||||
|     "placeholder": "请选择租户", |     "placeholder": "请选择租户", | ||||||
|  |  | ||||||
|  | @ -1,8 +1,7 @@ | ||||||
| import type { VbenFormSchema } from '#/adapter/form'; | import type { VbenFormSchema } from '#/adapter/form'; | ||||||
| import type { OnActionClickFn, VxeTableGridOptions } from '#/adapter/vxe-table'; | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; | ||||||
| import type { SystemUserApi } from '#/api/system/user'; | import type { SystemUserApi } from '#/api/system/user'; | ||||||
| 
 | 
 | ||||||
| import { useAccess } from '@vben/access'; |  | ||||||
| import { $t } from '@vben/locales'; | import { $t } from '@vben/locales'; | ||||||
| import { handleTree } from '@vben/utils'; | import { handleTree } from '@vben/utils'; | ||||||
| 
 | 
 | ||||||
|  | @ -17,8 +16,6 @@ import { | ||||||
|   getRangePickerDefaultProps, |   getRangePickerDefaultProps, | ||||||
| } from '#/utils'; | } from '#/utils'; | ||||||
| 
 | 
 | ||||||
| const { hasAccessByCodes } = useAccess(); |  | ||||||
| 
 |  | ||||||
| /** 新增/修改的表单 */ | /** 新增/修改的表单 */ | ||||||
| export function useFormSchema(): VbenFormSchema[] { | export function useFormSchema(): VbenFormSchema[] { | ||||||
|   return [ |   return [ | ||||||
|  | @ -282,7 +279,6 @@ export function useGridFormSchema(): VbenFormSchema[] { | ||||||
| 
 | 
 | ||||||
| /** 列表的字段 */ | /** 列表的字段 */ | ||||||
| export function useGridColumns<T = SystemUserApi.User>( | export function useGridColumns<T = SystemUserApi.User>( | ||||||
|   onActionClick: OnActionClickFn<T>, |  | ||||||
|   onStatusChange?: ( |   onStatusChange?: ( | ||||||
|     newStatus: number, |     newStatus: number, | ||||||
|     row: T, |     row: T, | ||||||
|  | @ -335,41 +331,10 @@ export function useGridColumns<T = SystemUserApi.User>( | ||||||
|       formatter: 'formatDateTime', |       formatter: 'formatDateTime', | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       field: 'operation', |  | ||||||
|       title: '操作', |       title: '操作', | ||||||
|       minWidth: 130, |       width: 180, | ||||||
|       fixed: 'right', |       fixed: 'right', | ||||||
|       align: 'center', |       slots: { default: 'actions' }, | ||||||
|       cellRender: { |  | ||||||
|         attrs: { |  | ||||||
|           nameField: 'username', |  | ||||||
|           nameTitle: '用户', |  | ||||||
|           onClick: onActionClick, |  | ||||||
|         }, |  | ||||||
|         name: 'CellOperation', |  | ||||||
|         // TODO @芋艿:后续把 delete、assign-role、reset-password 搞成"更多"
 |  | ||||||
|         options: [ |  | ||||||
|           { |  | ||||||
|             code: 'edit', |  | ||||||
|             show: hasAccessByCodes(['system:user:update']), |  | ||||||
|           }, |  | ||||||
|           { |  | ||||||
|             code: 'delete', |  | ||||||
|             show: hasAccessByCodes(['system:user:delete']), |  | ||||||
|           }, |  | ||||||
|           { |  | ||||||
|             code: 'assign-role', |  | ||||||
|             text: '分配角色', |  | ||||||
|             show: hasAccessByCodes(['system:permission:assign-user-role']), |  | ||||||
|             'v-access:code': 'system:user:assign-role1', |  | ||||||
|           }, |  | ||||||
|           { |  | ||||||
|             code: 'reset-password', |  | ||||||
|             text: '重置密码', |  | ||||||
|             show: hasAccessByCodes(['system:user:update-password']), |  | ||||||
|           }, |  | ||||||
|         ], |  | ||||||
|       }, |  | ||||||
|     }, |     }, | ||||||
|   ]; |   ]; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -9,10 +9,9 @@ import type { SystemUserApi } from '#/api/system/user'; | ||||||
| import { ref } from 'vue'; | import { ref } from 'vue'; | ||||||
| 
 | 
 | ||||||
| import { confirm, Page, useVbenModal } from '@vben/common-ui'; | import { confirm, Page, useVbenModal } from '@vben/common-ui'; | ||||||
| import { Download, Plus, Upload } from '@vben/icons'; |  | ||||||
| import { downloadFileFromBlobPart } from '@vben/utils'; | import { downloadFileFromBlobPart } from '@vben/utils'; | ||||||
| 
 | 
 | ||||||
| import { ElButton, ElLoading, ElMessage } from 'element-plus'; | import { ElLoading, ElMessage } from 'element-plus'; | ||||||
| 
 | 
 | ||||||
| import { useVbenVxeGrid } from '#/adapter/vxe-table'; | import { useVbenVxeGrid } from '#/adapter/vxe-table'; | ||||||
| import { | import { | ||||||
|  | @ -22,6 +21,7 @@ import { | ||||||
|   updateUserStatus, |   updateUserStatus, | ||||||
| } from '#/api/system/user'; | } from '#/api/system/user'; | ||||||
| import { DocAlert } from '#/components/doc-alert'; | import { DocAlert } from '#/components/doc-alert'; | ||||||
|  | import { ACTION_ICON, TableAction } from '#/components/table-action'; | ||||||
| import { $t } from '#/locales'; | import { $t } from '#/locales'; | ||||||
| import { DICT_TYPE, getDictLabel } from '#/utils'; | import { DICT_TYPE, getDictLabel } from '#/utils'; | ||||||
| 
 | 
 | ||||||
|  | @ -216,32 +216,69 @@ const [Grid, gridApi] = useVbenVxeGrid({ | ||||||
|       <div class="w-5/6"> |       <div class="w-5/6"> | ||||||
|         <Grid table-title="用户列表"> |         <Grid table-title="用户列表"> | ||||||
|           <template #toolbar-tools> |           <template #toolbar-tools> | ||||||
|             <ElButton |             <TableAction | ||||||
|               type="primary" |               :actions="[ | ||||||
|               @click="onCreate" |                 { | ||||||
|               v-access:code="['system:user:create']" |                   label: $t('ui.actionTitle.create', ['用户']), | ||||||
|             > |                   type: 'primary', | ||||||
|               <Plus class="mr-2 size-5" /> |                   icon: ACTION_ICON.ADD, | ||||||
|               {{ $t('ui.actionTitle.create', ['用户']) }} |                   auth: ['system:user:create'], | ||||||
|             </ElButton> |                   onClick: onCreate, | ||||||
|             <ElButton |                 }, | ||||||
|               type="primary" |                 { | ||||||
|               class="ml-2" |                   label: $t('ui.actionTitle.export'), | ||||||
|               @click="onExport" |                   type: 'primary', | ||||||
|               v-access:code="['system:user:export']" |                   icon: ACTION_ICON.DOWNLOAD, | ||||||
|             > |                   auth: ['system:user:export'], | ||||||
|               <Download class="mr-2 size-5" /> |                   onClick: onExport, | ||||||
|               {{ $t('ui.actionTitle.export') }} |                 }, | ||||||
|             </ElButton> |                 { | ||||||
|             <ElButton |                   label: $t('ui.actionTitle.import', ['用户']), | ||||||
|               type="primary" |                   type: 'primary', | ||||||
|               class="ml-2" |                   icon: ACTION_ICON.UPLOAD, | ||||||
|               @click="onImport" |                   auth: ['system:user:import'], | ||||||
|               v-access:code="['system:user:import']" |                   onClick: onImport, | ||||||
|             > |                 }, | ||||||
|               <Upload class="mr-2 size-5" /> |               ]" | ||||||
|               {{ $t('ui.actionTitle.import', ['用户']) }} |             /> | ||||||
|             </ElButton> |           </template> | ||||||
|  |           <template #actions="{ row }"> | ||||||
|  |             <TableAction | ||||||
|  |               :actions="[ | ||||||
|  |                 { | ||||||
|  |                   label: $t('common.edit'), | ||||||
|  |                   type: 'text', | ||||||
|  |                   icon: ACTION_ICON.EDIT, | ||||||
|  |                   auth: ['system:user:update'], | ||||||
|  |                   onClick: onEdit.bind(null, row), | ||||||
|  |                 }, | ||||||
|  |                 { | ||||||
|  |                   label: $t('common.delete'), | ||||||
|  |                   type: 'danger', | ||||||
|  |                   text: true, | ||||||
|  |                   icon: ACTION_ICON.DELETE, | ||||||
|  |                   auth: ['system:user:delete'], | ||||||
|  |                   popConfirm: { | ||||||
|  |                     title: $t('ui.actionMessage.deleteConfirm', [row.name]), | ||||||
|  |                     confirm: onDelete.bind(null, row), | ||||||
|  |                   }, | ||||||
|  |                 }, | ||||||
|  |               ]" | ||||||
|  |               :drop-down-actions="[ | ||||||
|  |                 { | ||||||
|  |                   label: '分配角色', | ||||||
|  |                   type: 'text', | ||||||
|  |                   auth: ['system:permission:assign-user-role'], | ||||||
|  |                   onClick: onAssignRole.bind(null, row), | ||||||
|  |                 }, | ||||||
|  |                 { | ||||||
|  |                   label: '重置密码', | ||||||
|  |                   type: 'text', | ||||||
|  |                   auth: ['system:user:update-password'], | ||||||
|  |                   onClick: onResetPassword.bind(null, row), | ||||||
|  |                 }, | ||||||
|  |               ]" | ||||||
|  |             /> | ||||||
|           </template> |           </template> | ||||||
|         </Grid> |         </Grid> | ||||||
|       </div> |       </div> | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	 puhui999
						puhui999