Gaokun Wang преди 3 седмици
родител
ревизия
b37de25dcc

+ 32 - 0
src/api/interface/system/position.ts

@@ -0,0 +1,32 @@
+/**
+ * 查询参数
+ */
+export interface PositionQuery extends PageQuery {
+  name?: string
+  status?: number
+}
+
+/**
+ * 接收后端返回信息
+ */
+export interface PositionVO extends BaseEntity {
+  positionId: number | string
+  orgId: number | string
+  name: string
+  category: string
+  orderNum: number
+  remark?: string
+}
+
+/**
+ * 传入后端的表单信息
+ */
+export interface PositionBO {
+  positionId: number | string
+  orgId: number | string
+  name: string
+  category: string
+  orderNum: number
+  remark?: string
+  status?: string
+}

+ 2 - 2
src/api/module/system/config.ts

@@ -34,10 +34,10 @@ class ConfigApi {
   }
 
   /**
-   * @name 更新
+   * @name 删除
    * @returns returns
    */
-  static delete = (data: ConfigBo): Promise<ResultData<any>> => {
+  static delete = (data: string[]): Promise<ResultData<any>> => {
     return http.delete({ url: '/system/config/delete', data })
   }
 }

+ 45 - 0
src/api/module/system/position.ts

@@ -0,0 +1,45 @@
+import http from '@/axios'
+import { PositionBO, PositionQuery, PositionVO } from '@/api/interface/system/position'
+
+class ConfigApi {
+  /**
+   * @name 查询分页
+   * @returns returns
+   */
+  static page = (params: PositionQuery): Promise<ResultData<any>> => {
+    return http.get<PositionVO>({ url: '/system/position/page', params })
+  }
+
+  /**
+   * @name 查询列表
+   * @returns returns
+   */
+  static list = (params: PositionBO): Promise<ResultData<any>> => {
+    return http.get<PositionVO>({ url: '/system/position/list', params })
+  }
+
+  /**
+   * @name 添加
+   * @returns returns
+   */
+  static add = (data: PositionBO): Promise<ResultData<any>> => {
+    return http.post({ url: '/system/position/add', data })
+  }
+
+  /**
+   * @name 更新
+   * @returns returns
+   */
+  static edit = (data: PositionBO): Promise<ResultData<any>> => {
+    return http.post({ url: '/system/position/edit', data })
+  }
+
+  /**
+   * @name 删除
+   * @returns returns
+   */
+  static delete = (data: string[]): Promise<ResultData<any>> => {
+    return http.delete({ url: '/system/position/delete', data })
+  }
+}
+export default ConfigApi

+ 8 - 5
src/axios/config.ts

@@ -1,10 +1,10 @@
 import type { InternalAxiosRequestConfig, AxiosResponse } from 'axios'
 import { ResultEnum } from '@/enums/HttpEnum'
-import { useUserStoreWithOut } from '@/stores'
+import { useAuthStore, useUserStore } from '@/stores'
 import router from '@/router'
 import { LOGIN_URL } from '@/constants'
 const defaultRequestInterceptors = (config: InternalAxiosRequestConfig) => {
-  const userStore = useUserStoreWithOut()
+  const userStore = useUserStore()
   if (config.headers && typeof config.headers.set === 'function') {
     config.headers.set('Authorization', 'Bearer ' + userStore.token)
     config.headers.set('clientId', import.meta.env.VITE_APP_CLIENT_ID)
@@ -14,7 +14,9 @@ const defaultRequestInterceptors = (config: InternalAxiosRequestConfig) => {
 
 const defaultResponseInterceptors = (response: AxiosResponse) => {
   const { code, msg } = response.data
-  const userStore = useUserStoreWithOut()
+  const userStore = useUserStore()
+  const authStore = useAuthStore()
+
   if (response?.config?.responseType === 'blob') {
     // 如果是文件流,直接过
     return response
@@ -24,10 +26,11 @@ const defaultResponseInterceptors = (response: AxiosResponse) => {
     ElMessage.error('无权限')
     return response.data
   } else if (code === ResultEnum.OVERDUE) {
-    userStore.setToken('')
+    userStore.clear()
+    authStore.clear()
     router.replace(LOGIN_URL)
     ElMessage.error(msg)
-    return response.data
+    return
   } else {
     console.error(msg)
     ElMessage.error(msg)

+ 1 - 0
src/axios/service.ts

@@ -42,6 +42,7 @@ axiosInstance.interceptors.request.use((res: InternalAxiosRequestConfig) => {
 axiosInstance.interceptors.response.use(
   (res: AxiosResponse) => {
     const url = res.config.url || ''
+
     abortControllerMap.delete(url)
     return res
   },

+ 1 - 1
src/hooks/useTable.ts

@@ -49,7 +49,7 @@ export const useTable = (
       }
     },
     set: newVal => {
-      console.log('我是分页更新之后的值', newVal)
+      console.log('分页更新', newVal)
     }
   })
 

+ 6 - 0
src/stores/modules/user.ts

@@ -79,6 +79,12 @@ export const useUserStore = defineStore('eco-user', {
     },
     setToken(tokenStr: string) {
       this.token = tokenStr
+    },
+    clear() {
+      this.token = ''
+      this.user = { userId: undefined, account: '', userName: '', orgId: '', nickname: '', userType: '' }
+      this.permissionCodes = []
+      this.roleCodes = []
     }
   },
   persist: true

+ 1 - 0
src/types/auto-components.d.ts

@@ -73,6 +73,7 @@ declare module 'vue' {
     OrgDrawer: typeof import('./../views/system/org/components/OrgDrawer.vue')['default']
     OrgForm: typeof import('./../views/system/org/components/orgForm.vue')['default']
     Pagination: typeof import('./../components/ProTable/Pagination.vue')['default']
+    PositionDrawer: typeof import('./../views/system/position/components/PositionDrawer.vue')['default']
     ProTable: typeof import('./../components/ProTable/index.vue')['default']
     RouterLink: typeof import('vue-router')['RouterLink']
     RouterView: typeof import('vue-router')['RouterView']

+ 1 - 1
src/views/home/index.vue

@@ -33,7 +33,7 @@ import RoleApi from '@/api/module/system/role'
 import { ColumnProps, SearchProps } from '@/components/ProTable/interface'
 // 树形筛选切换
 const changeTreeFilter = (val: string) => {
-  ElMessage.success(`请注意查看请求参数变化 🤔${val}`)
+  ElMessage.success({ message: `请注意查看请求参数变化 🤔${val}`, duration: 500 })
 }
 // 表格配置项
 const columns: ColumnProps<RoleInfo>[] = [

+ 136 - 0
src/views/system/position/components/PositionDrawer.vue

@@ -0,0 +1,136 @@
+<template>
+  <ElDrawer ref="elDrawerRef" v-model="drawerVisible" :title="drawerProps.title" v-bind="$attrs" destroy-on-close>
+    <template #header="scope">
+      <slot name="header" v-bind="scope">
+        <div style="display: flex">
+          <slot name="title">
+            <span style="flex: 1">{{ drawerProps.title }}</span>
+          </slot>
+        </div>
+      </slot>
+    </template>
+
+    <el-form ref="ruleFormRef" label-width="120px" label-suffix=" :" :rules="rules" :model="drawerProps.row" @submit.enter.prevent="handleConfirm">
+      <el-form-item label="所属组织" prop="orgId">
+        <el-tree-select
+          v-model="drawerProps.row.orgId"
+          :data="treeData"
+          check-strictly
+          placeholder="请选择组织"
+          :render-after-expand="false"
+          clearable
+          :default-expand-all="true"
+          :props="treeProps" />
+      </el-form-item>
+      <el-form-item label="职位名称" prop="name">
+        <el-input v-model="drawerProps.row.name" placeholder="请填职位名称" clearable />
+      </el-form-item>
+      <el-form-item label="排序" prop="orderNum">
+        <el-input-number v-model="drawerProps.row.orderNum" :precision="0" :min="1" :max="999999" />
+      </el-form-item>
+      <el-form-item label="状态" prop="status">
+        <el-radio-group v-model="drawerProps.row.status" default-value="1">
+          <el-radio-button :value="item.dictValue" v-for="(item, index) in commonStatus" :key="index" :label="item.dictLabel" />
+        </el-radio-group>
+      </el-form-item>
+      <el-form-item label="备注" prop="remark">
+        <el-input v-model="drawerProps.row.remark" placeholder="请填写备注" clearable />
+      </el-form-item>
+    </el-form>
+
+    <template #footer>
+      <slot name="footer">
+        <ElButton @click="drawerVisible = false">取 消</ElButton>
+        <ElButton type="primary" @click="handleConfirm()">确 定</ElButton>
+      </slot>
+    </template>
+  </ElDrawer>
+</template>
+
+<script setup lang="ts" name="OrgForm">
+import { PositionBO } from '@/api/interface/system/position'
+import OrgApi from '@/api/module/system/org'
+import { ResultEnum } from '@/enums/HttpEnum'
+import { FormInstance } from 'element-plus'
+import { useDictOptions } from '@/hooks'
+import { OrgTreeVO } from '@/api/interface/system/org'
+
+const commonStatus = useDictOptions('COMMON_STATUS')
+const rules = reactive({
+  name: [{ required: true, message: '请填写职位名称' }],
+  orgId: [{ required: true, message: '请选择所属组织' }],
+  orderNum: [{ required: true, message: '排序不能为空' }],
+  status: [{ required: true, message: '状态不能为空' }]
+})
+
+interface EcoDrawerProps {
+  api?: (params: any) => Promise<any> // 调用接口
+  title: string // 顶部标题
+  isView: boolean
+  row: Partial<PositionBO>
+  getTableList?: () => void
+}
+
+const treeData = ref<OrgTreeVO[]>([])
+const treeProps = {
+  label: 'name',
+  value: 'orgId'
+}
+
+const drawerVisible = ref(false)
+const drawerProps = ref<EcoDrawerProps>({
+  isView: false,
+  title: '',
+  row: {}
+})
+
+// emit
+const emit = defineEmits(['submit'])
+
+// 接收父组件传过来的参数
+const acceptParams = (params: EcoDrawerProps) => {
+  drawerProps.value = params
+  drawerVisible.value = true
+  loadTree()
+}
+
+const loadTree = () => {
+  OrgApi.tree().then(res => {
+    treeData.value = [
+      {
+        parentId: '-1',
+        orgId: '0',
+        name: '顶层',
+        children: res.data
+      }
+    ]
+  })
+}
+
+// 提交数据(新增/编辑)
+const ruleFormRef = ref<FormInstance>()
+const handleConfirm = () => {
+  ruleFormRef.value!.validate(async valid => {
+    if (!valid) return
+    try {
+      console.log(drawerProps.value.row)
+
+      const { code } = await drawerProps.value.api!(drawerProps.value.row)
+      if (code == ResultEnum.SUCCESS) {
+        ElMessage.success({ message: `${drawerProps.value.title}成功!` })
+        drawerProps.value.getTableList!()
+        emit('submit')
+        drawerVisible.value = false
+      }
+    } catch (error) {
+      console.log(error)
+    }
+  })
+}
+
+defineExpose({
+  acceptParams
+})
+</script>
+
+<style scoped lang="scss"></style>

+ 103 - 1
src/views/system/position/index.vue

@@ -1,3 +1,105 @@
 <template>
-  <div>职位</div>
+  <div class="main-box">
+    <TreeFilter title="组织架构" ref="treeRef" :request-api="OrgApi.tree" label="name" :default-value="initParam.positionId" @change="changeTree" />
+    <div class="table-box">
+      <ProTable
+        ref="proTableRef"
+        title="组织列表"
+        row-key="positionId"
+        :indent="20"
+        :columns="columns"
+        :init-param="initParam"
+        :search-columns="searchColumns"
+        :request-api="getTableList">
+        <!-- 表格 header 按钮 -->
+        <template #tableHeader="scope">
+          <el-button type="primary" icon="CirclePlus" @click="openDrawer('新增')"> 新增职位 </el-button>
+          <el-button type="danger" icon="Delete" plain :disabled="!scope.isSelected" @click="batchDelete(scope.selectedListIds)">
+            批量删除
+          </el-button>
+        </template>
+
+        <template #operation="{ row }">
+          <el-button type="primary" link icon="EditPen" @click="openDrawer('编辑', row)"> 编辑 </el-button>
+          <el-button v-if="row.id !== 1" type="primary" link icon="Delete" @click="deleteRow(row)"> 删除 </el-button>
+        </template>
+      </ProTable>
+    </div>
+    <PositionDrawer ref="drawerRef" />
+  </div>
 </template>
+<script lang="tsx" setup name="OrgManage">
+import OrgApi from '@/api/module/system/org'
+import PositionApi from '@/api/module/system/position'
+import PositionDrawer from './components/PositionDrawer.vue'
+import TreeFilter from '@/components/TreeFilter/index.vue'
+import { ColumnProps, ProTableInstance, SearchProps } from '@/components/ProTable/interface'
+import { PositionBO, PositionQuery, PositionVO } from '@/api/interface/system/position'
+import { useDictOptions, useHandleData } from '@/hooks'
+
+const initParam = reactive({ positionId: '', orgId: '' })
+
+const selectTreeId = ref<string>('')
+const proTableRef = ref<ProTableInstance>()
+const treeRef = ref<InstanceType<typeof TreeFilter>>()
+
+// 树形筛选切换
+const changeTree = (val: string) => {
+  if (val) {
+    selectTreeId.value = val
+    initParam.orgId = val
+    proTableRef.value?.clearSelection()
+  }
+}
+
+// 表格配置项
+const columns: ColumnProps<PositionVO>[] = [
+  { type: 'selection', width: 60 },
+  { prop: 'name', label: '职位名称' },
+  { prop: 'orderNum', label: '排序' },
+  {
+    prop: 'status',
+    label: '状态',
+    tag: true,
+    enum: useDictOptions('COMMON_STATUS'),
+    width: 80,
+    fieldNames: { label: 'dictLabel', value: 'dictValue', tagType: 'callbackShowStyle' }
+  },
+  { prop: 'remark', label: '备注' },
+  { prop: 'createByName', label: '创建人' },
+  { prop: 'createTime', label: '创建时间' },
+  { prop: 'operation', label: '操作', width: 250, fixed: 'right' }
+]
+
+// 表格配置项
+const searchColumns: SearchProps[] = [{ prop: 'name', label: '职位名称', el: 'input' }]
+
+// 获取table列表
+const getTableList = (params: PositionQuery) => PositionApi.page(params)
+
+// 单行删除
+const deleteRow = async (row: PositionVO) => {
+  await useHandleData(PositionApi.delete, [row.positionId], `删除【${row.name}】职位`)
+  proTableRef.value?.getTableList()
+}
+
+// 批量删除信息
+const batchDelete = async (ids: (string | number)[]) => {
+  await useHandleData(PositionApi.delete, ids, '删除所选职位信息')
+  proTableRef.value?.clearSelection()
+  proTableRef.value?.getTableList()
+}
+
+// 打开 drawer(新增、查看、编辑)
+const drawerRef = ref<InstanceType<typeof PositionDrawer> | null>(null)
+const openDrawer = (title: string, row: Partial<PositionBO> = {}) => {
+  const params = {
+    title,
+    row: title === '新增' ? { orgId: selectTreeId.value } : { ...row },
+    isView: title === '查看',
+    api: title === '新增' ? PositionApi.add : title === '编辑' ? PositionApi.edit : undefined,
+    getTableList: proTableRef.value?.getTableList
+  }
+  drawerRef.value?.acceptParams(params)
+}
+</script>