xingyu 2 лет назад
Родитель
Сommit
7cabf0ead5

+ 1 - 1
yudao-ui-admin-vue3/package.json

@@ -43,7 +43,7 @@
     "lodash-es": "^4.17.21",
     "mitt": "^3.0.0",
     "nprogress": "^0.2.0",
-    "pinia": "^2.0.23",
+    "pinia": "^2.0.24",
     "qrcode": "^1.5.1",
     "qs": "^6.11.0",
     "url": "^0.11.0",

+ 5 - 9
yudao-ui-admin-vue3/pnpm-lock.yaml

@@ -42,7 +42,7 @@ specifiers:
   lodash-es: ^4.17.21
   mitt: ^3.0.0
   nprogress: ^0.2.0
-  pinia: ^2.0.23
+  pinia: ^2.0.24
   plop: ^3.1.1
   postcss: ^8.4.19
   postcss-html: ^1.5.0
@@ -99,7 +99,7 @@ dependencies:
   lodash-es: 4.17.21
   mitt: 3.0.0
   nprogress: 0.2.0
-  pinia: 2.0.23_zwu2zepfy3m6u2gunxlolp35gi
+  pinia: 2.0.24_zwu2zepfy3m6u2gunxlolp35gi
   qrcode: 1.5.1
   qs: 6.11.0
   url: 0.11.0
@@ -1565,10 +1565,6 @@ packages:
   /@vue/devtools-api/6.4.3:
     resolution: {integrity: sha512-9WCRwdROJvWcHAdyrR7SZMM/qUvllDZnpndHXokThkUsjnJ2xe4/pvsH9FZrxFe22L+JmDKczL79HjLJ7DK9rg==}
 
-  /@vue/devtools-api/6.4.4:
-    resolution: {integrity: sha512-Ku31WzpOV/8cruFaXaEZKF81WkNnvCSlBY4eOGtz5WMSdJvX1v1WWlSMGZeqUwPtQ27ZZz7B62erEMq8JDjcXw==}
-    dev: false
-
   /@vue/devtools-api/6.4.5:
     resolution: {integrity: sha512-JD5fcdIuFxU4fQyXUu3w2KpAJHzTVdN+p4iOX2lMWSHMOoQdMAcpFLZzm9Z/2nmsoZ1a96QEhZ26e50xLBsgOQ==}
     dev: false
@@ -5564,8 +5560,8 @@ packages:
     dev: true
     optional: true
 
-  /pinia/2.0.23_zwu2zepfy3m6u2gunxlolp35gi:
-    resolution: {integrity: sha512-N15hFf4o5STrxpNrib1IEb1GOArvPYf1zPvQVRGOO1G1d74Ak0J0lVyalX/SmrzdT4Q0nlEFjbURsmBmIGUR5Q==}
+  /pinia/2.0.24_zwu2zepfy3m6u2gunxlolp35gi:
+    resolution: {integrity: sha512-DDLd4Iphyc+6PYYYbx7jkb6WP9gecgu9bz9huyB5rb7CdJI3DhzYiZI+/Ih8MLewRrP9DSpslF/BgSNrJtZU7A==}
     peerDependencies:
       '@vue/composition-api': ^1.4.0
       typescript: '>=4.4.4'
@@ -5576,7 +5572,7 @@ packages:
       typescript:
         optional: true
     dependencies:
-      '@vue/devtools-api': 6.4.4
+      '@vue/devtools-api': 6.4.5
       typescript: 4.8.4
       vue: 3.2.45
       vue-demi: 0.13.11_vue@3.2.45

+ 27 - 13
yudao-ui-admin-vue3/src/api/system/dept/index.ts

@@ -1,32 +1,46 @@
 import request from '@/config/axios'
-import type { DeptVO, DeptListReqVO } from './types'
+export type DeptVO = {
+  id: number
+  name: string
+  parentId: number
+  status: number
+  sort: number
+  leaderUserId: number
+  phone: string
+  email: string
+}
+
+export interface DeptPageReqVO {
+  name?: string
+  status?: number
+}
 
 // 查询部门(精简)列表
-export const listSimpleDeptApi = () => {
-  return request.get({ url: '/system/dept/list-all-simple' })
+export const listSimpleDeptApi = async () => {
+  return await request.get({ url: '/system/dept/list-all-simple' })
 }
 
 // 查询部门列表
-export const getDeptPageApi = (params: DeptListReqVO) => {
-  return request.get({ url: '/system/dept/list', params })
+export const getDeptPageApi = async (params: DeptPageReqVO) => {
+  return await request.get({ url: '/system/dept/list', params })
 }
 
 // 查询部门详情
-export const getDeptApi = (id: number) => {
-  return request.get({ url: '/system/dept/get?id=' + id })
+export const getDeptApi = async (id: number) => {
+  return await request.get({ url: '/system/dept/get?id=' + id })
 }
 
 // 新增部门
-export const createDeptApi = (data: DeptVO) => {
-  return request.post({ url: '/system/dept/create', data: data })
+export const createDeptApi = async (data: DeptVO) => {
+  return await request.post({ url: '/system/dept/create', data: data })
 }
 
 // 修改部门
-export const updateDeptApi = (params: DeptVO) => {
-  return request.put({ url: '/system/dept/update', data: params })
+export const updateDeptApi = async (params: DeptVO) => {
+  return await request.put({ url: '/system/dept/update', data: params })
 }
 
 // 删除部门
-export const deleteDeptApi = (id: number) => {
-  return request.delete({ url: '/system/dept/delete?id=' + id })
+export const deleteDeptApi = async (id: number) => {
+  return await request.delete({ url: '/system/dept/delete?id=' + id })
 }

+ 1 - 1
yudao-ui-admin-vue3/src/views/system/dept/dept.data.ts

@@ -49,7 +49,7 @@ export const modelSchema = reactive<FormSchema[]>([
   {
     label: '显示排序',
     field: 'sort',
-    component: 'InputNumber'
+    component: 'Input'
   },
   {
     label: '状态',

+ 235 - 176
yudao-ui-admin-vue3/src/views/system/dept/index.vue

@@ -1,229 +1,288 @@
 <template>
-  <div class="flex">
-    <el-card class="w-1/3 dept" :gutter="12" shadow="always">
-      <template #header>
-        <div class="card-header">
-          <span>部门列表</span>
-          <XButton
-            type="primary"
-            preIcon="ep:zoom-in"
-            title="新增根节点"
-            v-hasPermi="['system:dept:create']"
-            @click="handleCreate"
+  <ContentWrap>
+    <!-- 搜索工作栏 -->
+    <el-form :model="queryParams" ref="queryForm" :inline="true">
+      <el-form-item label="部门名称" prop="name">
+        <el-input v-model="queryParams.name" placeholder="请输入部门名称" />
+      </el-form-item>
+      <el-form-item label="状态" prop="status">
+        <el-select v-model="queryParams.status" placeholder="请选择部门状态">
+          <el-option
+            v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
+            :key="dict.value"
+            :label="dict.label"
+            :value="dict.value"
           />
-        </div>
+        </el-select>
+      </el-form-item>
+      <el-form-item>
+        <!-- 操作:搜索 -->
+        <XButton
+          type="primary"
+          preIcon="ep:search"
+          :title="t('common.query')"
+          @click="handleQuery()"
+        />
+        <!-- 操作:重置 -->
+        <XButton preIcon="ep:refresh-right" :title="t('common.reset')" @click="resetQuery()" />
+      </el-form-item>
+    </el-form>
+    <vxe-toolbar>
+      <template #buttons>
+        <!-- 操作:新增 -->
+        <XButton
+          type="primary"
+          preIcon="ep:zoom-in"
+          :title="t('action.add')"
+          v-hasPermi="['system:dept:create']"
+          @click="handleCreate()"
+        />
+        <XButton title="展开所有" @click="xTable?.setAllTreeExpand(true)" />
+        <XButton title="关闭所有" @click="xTable?.clearTreeExpand()" />
       </template>
-      <div class="custom-tree-container">
-        <!-- <p>部门列表</p> -->
-        <!-- 操作工具栏 -->
-        <el-input v-model="filterText" placeholder="搜索部门" />
-        <el-tree
-          ref="treeRef"
+    </vxe-toolbar>
+    <!-- 列表 -->
+    <vxe-table
+      show-overflow
+      keep-source
+      ref="xTable"
+      :loading="tableLoading"
+      :row-config="{ keyField: 'id' }"
+      :column-config="{ resizable: true }"
+      :tree-config="{ transform: true, rowField: 'id', parentField: 'parentId' }"
+      :print-config="{}"
+      :export-config="{}"
+      :data="tableData"
+      class="xtable"
+    >
+      <vxe-column title="部门名称" field="name" width="200" tree-node />
+      <vxe-column title="负责人" field="leaderUserId" :formatter="userNicknameFormat" />
+      <vxe-column title="排序" field="sort" />
+      <vxe-column title="状态" field="status">
+        <template #default="{ row }">
+          <DictTag :type="DICT_TYPE.COMMON_STATUS" :value="row.status" />
+        </template>
+      </vxe-column>
+      <vxe-column title="创建时间" field="createTime" formatter="formatDate" />
+      <vxe-column title="操作" width="200">
+        <template #default="{ row }">
+          <!-- 操作:修改 -->
+          <XTextButton
+            preIcon="ep:edit"
+            :title="t('action.edit')"
+            v-hasPermi="['system:dept:update']"
+            @click="handleUpdate(row.id)"
+          />
+          <!-- 操作:删除 -->
+          <XTextButton
+            preIcon="ep:delete"
+            :title="t('action.del')"
+            v-hasPermi="['system:dept:delete']"
+            @click="handleDelete(row.id)"
+          />
+        </template>
+      </vxe-column>
+    </vxe-table>
+  </ContentWrap>
+  <!-- 添加或修改菜单对话框 -->
+  <XModal id="deptModel" v-model="dialogVisible" :title="dialogTitle">
+    <!-- 对话框(添加 / 修改) -->
+    <!-- 操作工具栏 -->
+    <Form ref="formRef" :schema="modelSchema" :rules="rules">
+      <template #parentId>
+        <el-tree-select
           node-key="id"
-          :data="deptOptions"
+          v-model="deptParentId"
           :props="defaultProps"
-          :highlight-current="true"
-          default-expand-all
-          :filter-node-method="filterNode"
-          :expand-on-click-node="false"
-        >
-          <template #default="{ node, data }">
-            <span class="custom-tree-node">
-              <span>{{ node.label }}</span>
-              <span>
-                <XTextButton
-                  preIcon="ep:zoom-in"
-                  :title="t('action.add')"
-                  v-hasPermi="['system:dept:create']"
-                  @click="handleCreate(data)"
-                />
-                <XTextButton
-                  preIcon="ep:edit"
-                  :title="t('action.edit')"
-                  v-hasPermi="['system:dept:update']"
-                  @click="handleUpdate(data)"
-                />
-                <XTextButton
-                  preIcon="ep:delete"
-                  :title="t('action.del')"
-                  v-hasPermi="['system:dept:delete']"
-                  @click="handleDelete(data)"
-                />
-              </span>
-            </span>
-          </template>
-        </el-tree>
-      </div>
-    </el-card>
-    <el-card class="w-2/3 dept" style="margin-left: 10px" :gutter="12" shadow="hover">
-      <template #header>
-        <div class="card-header">
-          <span>{{ formTitle }}</span>
-        </div>
-      </template>
-      <div v-if="!showForm">
-        <span><p>请从左侧选择部门</p></span>
-      </div>
-      <div v-if="showForm">
-        <!-- 操作工具栏 -->
-        <Form ref="formRef" :schema="modelSchema" :rules="rules">
-          <template #parentId>
-            <el-tree-select
-              node-key="id"
-              v-model="deptParentId"
-              :props="defaultProps"
-              :data="deptOptions"
-              check-strictly
-            />
-          </template>
-          <template #leaderUserId>
-            <el-select v-model="leaderUserId">
-              <el-option
-                v-for="item in userOption"
-                :key="parseInt(item.id)"
-                :label="item.nickname"
-                :value="parseInt(item.id)"
-              />
-            </el-select>
-          </template>
-        </Form>
-        <!-- 按钮:保存 -->
-        <XButton
-          type="primary"
-          :title="t('action.save')"
-          v-hasPermi="['system:dept:update']"
-          :loading="loading"
-          @click="submitForm()"
+          :data="deptOptions"
+          :default-expanded-keys="[100]"
+          check-strictly
         />
-        <!-- 按钮:关闭 -->
-        <XButton :loading="loading" :title="t('dialog.close')" @click="showForm = false" />
-      </div>
-    </el-card>
-  </div>
+      </template>
+      <template #leaderUserId>
+        <el-select v-model="leaderUserId">
+          <el-option
+            v-for="item in userOption"
+            :key="parseInt(item.id)"
+            :label="item.nickname"
+            :value="parseInt(item.id)"
+          />
+        </el-select>
+      </template>
+    </Form>
+    <template #footer>
+      <!-- 按钮:保存 -->
+      <XButton
+        v-if="['create', 'update'].includes(actionType)"
+        type="primary"
+        :loading="actionLoading"
+        @click="submitForm()"
+        :title="t('action.save')"
+      />
+      <!-- 按钮:关闭 -->
+      <XButton :loading="actionLoading" @click="dialogVisible = false" :title="t('dialog.close')" />
+    </template>
+  </XModal>
 </template>
 <script setup lang="ts">
+import { nextTick, onMounted, reactive, ref, unref } from 'vue'
 import { useI18n } from '@/hooks/web/useI18n'
-import { ElInput, ElCard, ElTree, ElTreeSelect, ElSelect, ElOption } from 'element-plus'
-import { handleTree } from '@/utils/tree'
-import { onMounted, ref, unref, watch } from 'vue'
-import * as DeptApi from '@/api/system/dept'
-import { Form, FormExpose } from '@/components/Form'
-import { modelSchema, rules } from './dept.data'
-import { DeptVO } from '@/api/system/dept/types'
 import { useMessage } from '@/hooks/web/useMessage'
+import { ElForm, ElFormItem, ElInput, ElSelect, ElTreeSelect, ElOption } from 'element-plus'
+import { VxeColumn, VxeTable, VxeTableInstance, VxeToolbar } from 'vxe-table'
+import { modelSchema } from './dept.data'
+import * as DeptApi from '@/api/system/dept'
 import { getListSimpleUsersApi } from '@/api/system/user'
-const message = useMessage()
+import { required } from '@/utils/formRules.js'
+import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
+import { handleTree } from '@/utils/tree'
+import { FormExpose } from '@/components/Form'
 
+const { t } = useI18n() // 国际化
+const message = useMessage() // 消息弹窗
+// 列表相关的变量
+const xTable = ref<VxeTableInstance>()
+const tableLoading = ref(false)
+const tableData = ref()
+// 弹窗相关的变量
+const dialogVisible = ref(false) // 是否显示弹出层
+const dialogTitle = ref('edit') // 弹出层标题
+const actionType = ref('') // 操作按钮的类型
+const actionLoading = ref(false) // 遮罩层
+const deptParentId = ref(0) // 上级ID
+const leaderUserId = ref()
+const formRef = ref<FormExpose>() // 表单 Ref
+const deptOptions = ref() // 树形结构
+const userOption = ref()
+// 新增和修改的表单校验
+const rules = reactive({
+  name: [required],
+  sort: [required],
+  path: [required],
+  status: [required]
+})
+
+// 下拉框[上级]的配置项目
 const defaultProps = {
+  checkStrictly: true,
   children: 'children',
   label: 'name',
   value: 'id'
 }
-const { t } = useI18n() // 国际化
-const loading = ref(false) // 遮罩层
-const dialogVisible = ref(false) // 是否显示弹出层
-const showForm = ref(false) // 显示form表单
-const formTitle = ref('部门信息') // 显示form标题
-const deptParentId = ref(0) // 上级ID
-// 创建form表单
-const formRef = ref<FormExpose>()
-
-// ========== 创建部门树结构 ==========
-const filterText = ref('')
-const deptOptions = ref() // 树形结构
-const treeRef = ref<InstanceType<typeof ElTree>>()
+// 获取下拉框[上级]的数据
 const getTree = async () => {
   const res = await DeptApi.listSimpleDeptApi()
   deptOptions.value = handleTree(res)
+  console.info(deptOptions.value)
 }
-const filterNode = (value: string, data: Tree) => {
-  if (!value) return true
-  return data.name.includes(value)
-}
-watch(filterText, (val) => {
-  treeRef.value!.filter(val)
-})
-// 用户列表
-const userOption = ref()
-const leaderUserId = ref()
 const getUserList = async () => {
   const res = await getListSimpleUsersApi()
   userOption.value = res
 }
-// 新增
-const handleCreate = (data: { id: number }) => {
-  // 重置表单
-  deptParentId.value = data.id
-  formTitle.value = '新增部门'
-  showForm.value = true
+// ========== 查询 ==========
+const queryParams = reactive<DeptApi.DeptPageReqVO>({
+  name: undefined,
+  status: undefined
+})
+// 执行查询
+const getList = async () => {
+  tableLoading.value = true
+  const res = await DeptApi.getDeptPageApi(queryParams)
+  tableData.value = res
+  tableLoading.value = false
+}
+
+// 查询操作
+const handleQuery = async () => {
+  await getList()
+}
+
+// 重置操作
+const resetQuery = async () => {
+  queryParams.name = undefined
+  queryParams.status = undefined
+  await getList()
+}
+
+// ========== 新增/修改 ==========
+
+// 设置标题
+const setDialogTile = (type: string) => {
+  dialogTitle.value = t('action.' + type)
+  actionType.value = type
+  dialogVisible.value = true
+}
+
+// 新增操作
+const handleCreate = async () => {
+  deptParentId.value = 0
+  leaderUserId.value = null
+  setDialogTile('create')
 }
-// 编辑
-const handleUpdate = async (data: { id: number }) => {
-  const res = await DeptApi.getDeptApi(data.id)
-  formTitle.value = '修改- ' + res?.name
-  deptParentId.value = res.parentId
+
+// 修改操作
+const handleUpdate = async (rowId: number) => {
+  setDialogTile('update')
+  // 设置数据
+  const res = await DeptApi.getDeptApi(rowId)
+  console.info(res)
+  deptParentId.value = res.deptParentId
   leaderUserId.value = res.leaderUserId
+  await nextTick()
   unref(formRef)?.setValues(res)
-  showForm.value = true
-}
-// 删除
-const handleDelete = async (data: { id: number }) => {
-  message
-    .confirm(t('common.delDataMessage'), t('common.confirmTitle'))
-    .then(async () => {
-      await DeptApi.deleteDeptApi(data.id)
-      message.success(t('common.delSuccess'))
-    })
-    .catch(() => {})
-  await getTree()
 }
-// 提交按钮
+
+// 提交新增/修改的表单
 const submitForm = async () => {
   const elForm = unref(formRef)?.getElFormRef()
   if (!elForm) return
   elForm.validate(async (valid) => {
     if (valid) {
-      loading.value = true
+      actionLoading.value = true
       // 提交请求
       try {
-        const data = unref(formRef)?.formModel as DeptVO
+        const data = unref(formRef)?.formModel as DeptApi.DeptVO
         data.parentId = deptParentId.value
         data.leaderUserId = leaderUserId.value
-        if (formTitle.value.startsWith('新增')) {
+        if (dialogTitle.value.startsWith('新增')) {
           await DeptApi.createDeptApi(data)
-        } else if (formTitle.value.startsWith('修改')) {
+        } else if (dialogTitle.value.startsWith('修改')) {
           await DeptApi.updateDeptApi(data)
         }
         // 操作成功,重新加载列表
         dialogVisible.value = false
       } finally {
-        loading.value = false
+        actionLoading.value = false
       }
     }
   })
 }
+
+// 删除操作
+const handleDelete = async (rowId: number) => {
+  message.delConfirm().then(async () => {
+    await DeptApi.deleteDeptApi(rowId)
+    message.success(t('common.delSuccess'))
+    await getList()
+  })
+}
+
+const userNicknameFormat = (row) => {
+  if (!row && !row.row && !row.row.leaderUserId) {
+    return '未设置'
+  }
+  for (const user of userOption.value) {
+    if (row.row.leaderUserId === user.id) {
+      return user.nickname
+    }
+  }
+  return '未知【' + row.row.leaderUserId + '】'
+}
+
+// ========== 初始化 ==========
 onMounted(async () => {
   await getTree()
   await getUserList()
+  await getList()
 })
 </script>
-
-<style scoped>
-.dept {
-  height: 600px;
-  max-height: 1800px;
-}
-.card-header {
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
-}
-.custom-tree-node {
-  flex: 1;
-  display: flex;
-  align-items: center;
-  justify-content: space-between;
-  font-size: 14px;
-  padding-right: 8px;
-}
-</style>

+ 1 - 1
yudao-ui-admin-vue3/vite.config.ts

@@ -13,7 +13,7 @@ import { createHtmlPlugin } from 'vite-plugin-html'
 import viteCompression from 'vite-plugin-compression'
 import VueMarcos from 'unplugin-vue-macros/vite'
 
-// 当前执行node命令时文件夹的地址(工作目录)
+// 当前执行node命令时文件夹的地址(工作目录)
 const root = process.cwd()
 
 // 路径查找