列表的批量操作组件封装 + 权限 ,如何更优雅的实现呢?Vue3

发布时间 2023-05-08 09:32:59作者: kitty0903
  1. 这个组件解决的问题?
    在以往的项目当中,我从未想过要对 批量/列表数据的操作按钮做什么变动,直到最近的一次开发,让我突然觉得可以将操作按钮也做成一个公共组件,在做前端开发时,更加专注于js代码逻辑。
  2. 如何使用?
    全局(main.js中)引用操作组件 BatchOperation.vue
    创建页面操作按钮 actions.js 文件
  3. BatchOperation.vue 全局操作按钮组件文件代码:
    <template>
       <div>
        <!-- 列表顶部的批量操作按钮组件  / 表格中的操作按钮-->
           <el-row :gutter="10" class="mb8 mt10" type="flex">
               <slot name="default"></slot>
               <el-col :span="1.5" v-for="action, index in visibleAction" :key="action.command" v-show="visibleAction.length > 0">
                   <el-button :link="inColumn" :type="getBtnType(index)" plain :disabled="action.disabled" v-hasPermi="action.permission" @click="commandAction(action.command)">{{action.name}}</el-button>
               </el-col>
               <el-col :span="1.5" v-if="moreActionsVisible">
                   <el-dropdown @command="commandAction" :size="inColumn ? 'default' : 'large'" style="line-height:inherit;vertical-align: bottom;">
                       <el-button type="primary" plain :link="inColumn">
                           更多操作<el-icon class="el-icon--right"><arrow-down /></el-icon>
                       </el-button>
                       <template #dropdown>
                           <el-dropdown-menu>
                               <div v-hasPermi="action.permission" v-for="action in moreActions" :key="action.command">
                                   <el-dropdown-item :disabled="action.disabled" :command="action.command" >
                                       {{action.name}}
                                   </el-dropdown-item>
                               </div>
                           </el-dropdown-menu>
                       </template>
                   </el-dropdown>
               </el-col>
               <slot name="rightToolbar"></slot>
           </el-row>
       </div>
    </template>
    <script setup>
     import take from 'lodash/take'
     import takeRight from 'lodash/takeRight'
     import mapValues from 'lodash/mapValues'
     import pickBy from 'lodash/pickBy'
     import values from 'lodash/values'
     import sortBy from 'lodash/sortBy'
     // button type
     const btnType = ['primary', 'success', 'warning', 'danger', 'info', 'default']
     // props
     const getBtnType =(index)=>{
         const indx = index % btnType.length
         return btnType[indx]
     }
     // actions command
     const emits = defineEmits()
     // props
     const props = defineProps(['actions','visibleCount','inColumn'])
     // 明显按钮 默认显示前五个按钮; 如果是在 数据行操作只显示 1 个
     const MAX = computed(()=>{
         const count = props.inColumn ? 1 : 5
         return props.visibleCount === undefined ? count : props.visibleCount
     })
     // 将 key 作为 command。 hiddenInColunm 在表中隐藏 onlyInColunm 只在表格列中显示 hidden 都不显示
     const operations = computed(()=>{
         let actions = pickBy(props.actions, a =>(props.inColumn ? (!a.hiddenInColunm) : !a.onlyInColunm) && !a.hidden)
         actions = mapValues(actions,(value,key)=>{
             value.command = key
             return value
         })
         // 将可用功能优先排序
        return sortBy(values(actions), (a)=>a.disabled)
     })
     // 明显按钮
     const visibleAction = computed(()=>{
         return take(operations.value, MAX.value)
     })
     // 更多操作下拉按钮
     const moreActions = computed(()=>{
         return takeRight(operations.value, operations.value.length - MAX.value)
     })
     // 是否显示更多按钮
     const moreActionsVisible = computed(()=>MAX.value == 0 || operations.value.length > visibleAction.value.length)
     // 点击更多操作里的按钮
     const commandAction =(command)=>{
         emits(command)
     }
    </script>
    
  4. actions.js 页面操作,举个例子:
    // single:批量勾选 false 单条数据,true 非单条数据
    // multiple:批量勾选 false 已勾选 true 未勾选
    // row:单行数据。当 single 为 false 时, row 也须有值 
    export default function (single, multiple, row) { 
         return {
             'create': {
                 name: "新增",// 按钮名称
                 permission: ['bless:product:permissionAdd'],// 权限字符
                 disabled: false, // 是否可点击
                 hiddenInColunm: true // 在表格列中隐藏
             },
             'edit': {
                 name: "修改",
                 permission: ['bless:product:permissionEdit'],
                 // 单行 或者 row 存在且符合( ... )中的条件时
                 disabled: single || ( row ? ( row.status !=0 && row.status !=3 ) : true),
                 onlyInColunm: true // 仅在列表中显示
             },
             'delete': {
                 name: "删除",
                 permission: ['bless:product:permissionRemove'],
                 disabled: multiple,
             },
             'search': {
                 name: "查询", 
                 permission: ['bless:product:permissionPage'],
                 disabled: false,
                 hidden: true // 隐藏
             }
         }
     }
    
  5. 页面使用操作按钮:UI 组件:element-plus
      <template>
         <!-- v-hasPermi 权限指令 -->
         <div v-hasPermi="actions['search'].permission">
             <!-- 批量操作 -->
             <BatchOperation 
                :actions="actions" 
                @create="handleCreate"
                @delete="handleDelete"
                >
             </BatchOperation>
             <!-- 列表 -->
             <el-table :data="list" row-key="productId" @selection-change="handleSelectionChange">
                <!-- 勾选框 -->
                <el-table-column type="selection" width="55" align="center" fixed="left" />
                ...(这里就是列表展示的数据)
                <!-- 单行操作按钮 -->
                <el-table-column label="操作" fixed="right" header-align="center" width="150px">
                      <template #default="{row}">
                         <BatchOperation 
                         :inColumn="true"
                         :actions="initAction(false, false, row)"
                         @edit="handleEdit(row)"
                         @delete="handleDelete(row.productId)"
                         ></BatchOperation>
                      </template>
                </el-table-column>
             </el-table>
         </div>
      </template>
      <script setup name="ProductIndex">
         import { getPages, remove } from '@/api/product'
         import initAction from './components/actions'
         // 勾选
         const multipleSelection = ref([]) // ids
         // 非单个禁用
         const single = computed(() => multipleSelection.value.length != 1)
         // 非多个禁用
         const multiple = computed(() => !multipleSelection.value.length)
         // 当前选中
         const selectedRow = computed(() => {
            if (multipleSelection.value.length == 1) {
                  return list.value.find(z => z.productId == multipleSelection.value[0])
            }
            return null
         })
         // 批量操作按钮
         const actions = computed(()=>initAction(single.value, multiple.value, selectedRow.value))
         // 批量勾选触发
         const handleSelectionChange = (val) => {
            multipleSelection.value = val.map(z => z.productId)
         }
         // 点击 ��增
           const handleCreate =()=>{
               router.push('/product/create')
           }
         // 点击 修改
         const handleEdit =(row)=>{
            const current = row || selectedRow.value
            router.push('/product/edit/' + current.productId)
         }
         // 批量删除
         const handleDelete = (id) => {
            proxy.$modal.confirm('确定删除?')
               .then(() => {
                  const ids = id || multipleSelection.value
                  remove(ids).then(res => {
                     if (res.code == 200) {
                           proxy.$modal.msgSuccess("操作成功")
                           getList()
                     } else {
                           proxy.$modal.msgError(res.msg || "请求发生错误,请稍后重试")
                     }
                  })
               })
               .catch(() => {
                  proxy.$modal.msg("已取消")
               })
         }
         // 获取列表数据
         const getList = () => {
            proxy.$modal.loading("加载中...")
            // **这里 searchRef 咱们下篇随笔见!**
            const { dateRange, ...search } = searchRef.value.form
            const query = { ...pager.value, ...search }
            const data = dateRange ? proxy.addDateRange(query, dateRange) : query
            getPages(data).then(response => {
                  list.value = response.data.rows;
                  total.value = parseInt(response.data.total);
                  expandRowKeys.value = []
            }).finally(() => {
                  proxy.$modal.closeLoading();
            });
         }
      </script>