【admin-mini】函数化el-dialog弹出层 vue3手摸手

111 阅读2分钟

logo-mini.jpg

本专栏为了还在Vue2x的广大同胞提供一点升级3X的借鉴思路

适用人群

  • Vue前端开发者

阅读条件

  • 撸一遍Vue3文档

收获

  • 纯净的Vue3-admin框架

如果你是一名成熟的开发工程师,可以直接访问成品

gitee

github


【admin-mini】函数化el-dialog弹出层

接上次封装table列表

列表想要好,表单少不了 skr~

很糟糕的一种组织代码方式

(常见于祖传骂娘项目 🤬)

<template>
    <el-table>
        ...列表页代码
    </el-table>
    <el-dialog>
        ...新增业务表单
    </el-dialog>
    <el-dialog>
        ...编辑业务表单
    </el-dialog>
    <el-dialog>
        ...绑定业务表单
    </el-dialog>
    ....
</template>
<script>
    ...列表变量数据、各种函数、业务逻辑
    ...新增业务 变量数据、各种函数、业务逻辑
    ...编辑业务 变量数据、各种函数、业务逻辑
    ...绑定业务 变量数据、各种函数、业务逻辑
</script>
<style>
    ...列表样式
    ...新增样式
    ...编辑样式
    ...绑定样式
</style>

正常猿类写法

这种组织代码方式也是最为常见的方式,但是随着业务逻辑的复杂

一个列表页可能包含不下10个弹出层业务逻辑

我们仍需要上下反复横跳☹️

  • 维护 el-dialog 的显示与隐藏
  • 给业务组件传输 props
  • 维护业务组件的回调
<template>
    <el-table>
        ...列表页代码
    </el-table>
    <el-dialog>
        <新增业务组件 />
    </el-dialog>
    <el-dialog>
         <编辑业务表单 />
    </el-dialog>
    <el-dialog>
         <绑定业务表单 />
    </el-dialog>
    ....
</template>
<script>
    ...列表变量数据、各种函数、业务逻辑
    import 新增业务
    import 编辑业务
    import 绑定业务
</script>
<style>
    ...列表样式
</style>

遥遥领先写法 😮

<template>
    <el-table>
        ...列表页代码
    </el-table>   
    ....
</template>
<script>
    ...列表变量数据、各种函数、业务逻辑
    弹出('新增业务',{...props},{...回调})
    弹出('编辑业务',{...props},{...回调})
    弹出('绑定业务',{...props},{...回调})
</script>
<style>
    ...列表样式
</style>

要实现以上调用方式,首先要解决如果动态把一个vue组件插入到el-dialog

这里我们使用vue提供的渲染函数h

在index.vue中使用h把组件转成vnode 传入el-dialog的slot中

封装成一个vue插件:

import { render, h, getCurrentInstance, onBeforeUnmount } from 'vue'
import { ElDialog } from 'element-plus'
import 'element-plus/es/components/dialog/style/css'
export default function useAdminDialog() {
  const vm = getCurrentInstance()

  return function (vnode, opts) {
    onBeforeUnmount(() => {
      close()
    }, vm)
    vnode.appContext = vm.appContext

    let dom = document.createElement('div')
    let dialogVnode = h(
      ElDialog,
      {
        onClosed: () => {
          dom.remove()
        },
        modelValue: true,
        closeOnClickModal: false,
        alignCenter: true,
        ...opts
      },
      { default: () => vnode }
    )
    dialogVnode.appContext = vm.appContext
    let _success = vnode.props.onSuccess || function () {}
    let _end = vnode.props.onEnd || function () {}
    vnode.props.onSuccess = function (...arr) {
      _success(...arr)
      close()
    }
    vnode.props.onEnd = function (...arr) {
      _end(...arr)
      close()
    }
    function close() {
      dialogVnode.component.props.modelValue = false
    }
    render(dialogVnode, dom)
    document.body.appendChild(dom)
  }
}
useAdminDialog.install = function (app) {
  void app
  // app.config.globalProperties.useAdminDialog = useAdminDialog
}

调用

<template>
  <div class="admin-view">
    <div class="admin-view-body">
      <div class="admin-table-btns">
        <el-button @click="openAdd" type="primary" icon="plus">新增</el-button>
      </div>
      <div class="admin-table-list">
        <el-table border :data="$table.data" :empty-text="$table.emptyText" v-loading="$table.loading"
          table-layout="auto">
          <el-table-column prop="username" label="帐号" />
          <el-table-column prop="nickName" label="用户姓名" />
          <el-table-column label="操作" width="400px">
            <template #default="scope">
              <el-space spacer="|">
                <el-link :disabled="scope.row.loading" type="primary" @click="openEdit(scope.row)">编辑</el-link>
                <el-link :disabled="scope.row.loading" type="primary" @click="openDel(scope.row)">删除</el-link>
                <el-link :disabled="scope.row.loading" type="primary" @click="openPassword(scope.row)">修改密码</el-link>
              </el-space>
            </template>
          </el-table-column>
        </el-table>
      </div>
    </div>
  </div>
</template>
<script  setup>
import { userList, userDel } from '@/api'
import { h } from 'vue'
import useAdminDialog from '@/plugins/use-admin-dialog'
import useAdminTable from '@/plugins/use-admin-table'
import { ElMessage, ElMessageBox } from 'element-plus'
const adminDialog = useAdminDialog()
const $table = useAdminTable({
  api: userList
})

async function openAdd() {
  adminDialog(
    h((await import('./post.vue')).default, {
      onSuccess: () => {
        $table.getTable()
      }
    }),
    { title: '新增' }
  )
}

async function openEdit(row) {
  adminDialog(
    h((await import('./post.vue')).default, {
      row: row,
      onSuccess: () => {
        $table.getTable()
      }
    }),
    { title: '编辑' }
  )
}

async function openPassword(row) {
  adminDialog(
    h((await import('./change-password.vue')).default, {
      row: row,
      onSuccess: () => {
        $table.getTable()
      }
    }),
    { title: '修改密码' }
  )
}

function openDel(row) {
  row.loading = true;
  ElMessageBox.confirm("确定删除?", "提示", { type: "warning" }).then(() => {
    userDel({
      userId: row.userId
    }).then(res => {
      if (res.data.code == 0) {
        $table.getTable()
      } else {
        ElMessage.error(res.data.msg)
      }
    }).finally(() => {
      row.loading = false;
    })
  }).catch(() => { row.loading = false; })

}
</script>
<style></style>