|
|
@@ -0,0 +1,486 @@
|
|
|
+<template>
|
|
|
+ <div class="home">
|
|
|
+ <actionBar :operateList="operateList" :formList="formList" :ruleForm="searchRuleForm" @openModel="openModel"
|
|
|
+ @searchProtocol="searchProtocol"></actionBar>
|
|
|
+ <div class="card_content">
|
|
|
+ <tables :suspension="true" :tableList="tableList" :tableData="tableData" @buttonData="buttonData"></tables>
|
|
|
+ <div v-if="Total">
|
|
|
+ <pagination :total="Total" :currentPage="Pagination.PageIndex" @changeSize="changeSize"
|
|
|
+ @changeCurrent="changeCurrent">
|
|
|
+ </pagination>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <el-dialog :title="staffTitle" :visible.sync="staffDialogVisible" width="950px" :close-on-click-modal="false"
|
|
|
+ @close="closeDialog">
|
|
|
+ <el-row :gutter="20">
|
|
|
+ <el-col :span="12">
|
|
|
+ <forms ref="childRules" :formNewList="formRuleList" :ruleForm="ruleForm" labelWidth="100px" @remoteMethod="remoteSearchDept"></forms>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <div class="perm-tree-box">
|
|
|
+ <div class="perm-tree-title">菜单权限分配</div>
|
|
|
+ <el-tree
|
|
|
+ ref="permTree"
|
|
|
+ :data="menuTreeData"
|
|
|
+ :props="treeProps"
|
|
|
+ show-checkbox
|
|
|
+ node-key="id"
|
|
|
+ :default-expand-all="false"
|
|
|
+ :default-checked-keys="checkedMenuIds"
|
|
|
+ :check-strictly="true"
|
|
|
+ @check="handleTreeCheck">
|
|
|
+ <!-- <span class="custom-tree-node" slot-scope="{ node, data }"> -->
|
|
|
+ <span class="custom-tree-node" slot-scope="{ node }">
|
|
|
+ <span>{{ node.label }}</span>
|
|
|
+ </span>
|
|
|
+ </el-tree>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ <span slot="footer" class="dialog-footer">
|
|
|
+ <el-button plain @click="resetDept">重置部门</el-button>
|
|
|
+ <el-button plain @click="staffDialogVisible = false">取 消</el-button>
|
|
|
+ <el-button type="primary" :loading="confirmLoading" @click="handleAdd">确 定</el-button>
|
|
|
+ </span>
|
|
|
+ </el-dialog>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+ import {
|
|
|
+ getPostList,
|
|
|
+ getPostDetail,
|
|
|
+ addPost,
|
|
|
+ putPost,
|
|
|
+ delPost,
|
|
|
+ getMenuTree
|
|
|
+ } from '@/api/post'
|
|
|
+ import {
|
|
|
+ getCompanyAll
|
|
|
+ } from '@/api/user'
|
|
|
+ import actionBar from '@/components/actionBar'
|
|
|
+ import tables from '@/components/tables'
|
|
|
+ import pagination from '@/components/pagination'
|
|
|
+ import forms from '@/components/forms'
|
|
|
+ import {
|
|
|
+ formRules,
|
|
|
+ employee
|
|
|
+ } from "./post.js";
|
|
|
+ export default {
|
|
|
+ name: 'PostManagement',
|
|
|
+ components: { actionBar, tables, pagination, forms },
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ operateList: [{
|
|
|
+ type: 'add', title: '新增岗位', icon: 'el-icon-plus',
|
|
|
+ }],
|
|
|
+ formList: [
|
|
|
+ { type: 'input', label: '岗位名称', field: 'postName', placeholder: '岗位名称' },
|
|
|
+ { type: 'select', label: '所属部门', field: 'deptId', placeholder: '所属部门', options: [] },
|
|
|
+ ],
|
|
|
+ searchRuleForm: { postName: '', deptId: '' },
|
|
|
+ searchValue: {},
|
|
|
+ Pagination: { PageIndex: 1, PageSize: 10 },
|
|
|
+ Total: 0,
|
|
|
+ tableData: [],
|
|
|
+ operationType: '',
|
|
|
+ tableList: employee(),
|
|
|
+ staffTitle: '新增岗位',
|
|
|
+ staffDialogVisible: false,
|
|
|
+ formRuleList: [],
|
|
|
+ ruleForm: {
|
|
|
+ postName: '',
|
|
|
+ postCode: '',
|
|
|
+ deptId: '',
|
|
|
+ sort: 0,
|
|
|
+ status: '2',
|
|
|
+ remark: '',
|
|
|
+ },
|
|
|
+ defaultRuleForm: {
|
|
|
+ postName: '',
|
|
|
+ postCode: '',
|
|
|
+ deptId: '',
|
|
|
+ sort: 0,
|
|
|
+ status: '2',
|
|
|
+ remark: '',
|
|
|
+ },
|
|
|
+ confirmLoading: false,
|
|
|
+ companyNameMap: {},
|
|
|
+ menuTreeData: [],
|
|
|
+ checkedMenuIds: [],
|
|
|
+ treeProps: { children: 'children', label: 'label' },
|
|
|
+ cachedCompanyTree: null,
|
|
|
+ }
|
|
|
+ },
|
|
|
+ mounted() {
|
|
|
+ const dataList = formRules();
|
|
|
+ this.formRuleList = dataList;
|
|
|
+ this.getCompany(() => {
|
|
|
+ this.getList(); // 等公司数据加载完再查列表
|
|
|
+ });
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+
|
|
|
+ // 构建带缩进的扁平选项列表
|
|
|
+ buildFlatOptionsFromTree(roots) {
|
|
|
+ const result = []
|
|
|
+ const build = (list, level, prefix) => {
|
|
|
+ list.forEach((item, index) => {
|
|
|
+ const isLast = index === list.length - 1
|
|
|
+ const connector = level > 0 ? (isLast ? '└─ ' : '├─ ') : ''
|
|
|
+ const label = prefix + connector + item.name
|
|
|
+ result.push({ label: label, value: item.id })
|
|
|
+ if (item.children && item.children.length > 0) {
|
|
|
+ const childPrefix = prefix + (level > 0 ? (isLast ? ' ' : '│ ') : '')
|
|
|
+ build(item.children, level + 1, childPrefix)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+ build(roots, 0, '')
|
|
|
+ return result
|
|
|
+ },
|
|
|
+
|
|
|
+ // 加载部门选项
|
|
|
+ loadDeptOptions(query = '') {
|
|
|
+ getCompanyAll({ name: query, pageSize: 9999 })
|
|
|
+ .then(res => {
|
|
|
+ if (res.code !== 200) return
|
|
|
+ const flatList = res.data.list || []
|
|
|
+ if (flatList.length === 0) {
|
|
|
+ // 清空选项
|
|
|
+ this.formRuleList.forEach(item => {
|
|
|
+ if (item.field === 'deptId') {
|
|
|
+ item.options = []
|
|
|
+ }
|
|
|
+ })
|
|
|
+ return
|
|
|
+ }
|
|
|
+ // 构建树形结构(基于 parentId)
|
|
|
+ const nodeMap = {}
|
|
|
+ flatList.forEach(item => {
|
|
|
+ nodeMap[item.id] = { ...item, children: [] }
|
|
|
+ })
|
|
|
+ const roots = []
|
|
|
+ flatList.forEach(item => {
|
|
|
+ const node = nodeMap[item.id]
|
|
|
+ if (item.parentId && nodeMap[item.parentId]) {
|
|
|
+ nodeMap[item.parentId].children.push(node)
|
|
|
+ } else {
|
|
|
+ roots.push(node)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ // 生成带缩进的选项列表
|
|
|
+ const flatOptions = this.buildFlatOptionsFromTree(roots)
|
|
|
+ this.formRuleList.forEach(item => {
|
|
|
+ if (item.field === 'deptId') {
|
|
|
+ item.options = flatOptions
|
|
|
+ }
|
|
|
+ })
|
|
|
+ })
|
|
|
+ .catch(err => {
|
|
|
+ console.error('加载部门选项失败:', err)
|
|
|
+ })
|
|
|
+ },
|
|
|
+
|
|
|
+ // 远程搜索方法
|
|
|
+ remoteSearchDept(query, field) {
|
|
|
+ if (field === 'deptId') {
|
|
|
+ this.loadDeptOptions(query)
|
|
|
+ }
|
|
|
+ },
|
|
|
+ // 获取公司列表
|
|
|
+ getCompany(callback) {
|
|
|
+ const processData = (res) => {
|
|
|
+ if (res.code == 200) {
|
|
|
+ const flatList = res.data.list || res.data || []
|
|
|
+ if (flatList.length === 0) {
|
|
|
+ if (callback) callback()
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ // 两遍扫描构建树:先用 Map 索引,再挂载子节点
|
|
|
+ const nodeMap = {}
|
|
|
+ flatList.forEach(item => {
|
|
|
+ nodeMap[item.id] = { ...item, children: [] }
|
|
|
+ })
|
|
|
+ const roots = []
|
|
|
+ flatList.forEach(item => {
|
|
|
+ const node = nodeMap[item.id]
|
|
|
+ if (item.parentId && nodeMap[item.parentId]) {
|
|
|
+ nodeMap[item.parentId].children.push(node)
|
|
|
+ } else {
|
|
|
+ roots.push(node)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ this.cachedCompanyTree = roots
|
|
|
+
|
|
|
+ // 用树形字符构建展示选项
|
|
|
+ const flatOptions = []
|
|
|
+ const buildFlat = (list, level, prefix) => {
|
|
|
+ list.forEach((item, index) => {
|
|
|
+ const isLast = index === list.length - 1
|
|
|
+ const connector = level > 0 ? (isLast ? '└─ ' : '├─ ') : ''
|
|
|
+ const label = prefix + connector + item.name
|
|
|
+ flatOptions.push({ label: label, value: item.id })
|
|
|
+ if (item.children && item.children.length > 0) {
|
|
|
+ const childPrefix = prefix + (level > 0 ? (isLast ? ' ' : '│ ') : '')
|
|
|
+ buildFlat(item.children, level + 1, childPrefix)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+ buildFlat(roots, 0, '')
|
|
|
+ this.optionMatchingSearch(flatOptions, 'deptId')
|
|
|
+ this.optionMatching(flatOptions, 'deptId')
|
|
|
+
|
|
|
+ // 构建名称映射
|
|
|
+ this.companyNameMap = {}
|
|
|
+ const buildMap = (list) => {
|
|
|
+ list.forEach(item => {
|
|
|
+ this.companyNameMap[item.id] = item.name
|
|
|
+ if (item.children && item.children.length > 0) {
|
|
|
+ buildMap(item.children)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+ buildMap(roots)
|
|
|
+
|
|
|
+ if (callback) callback()
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ getCompanyAll().then(processData)
|
|
|
+ },
|
|
|
+ optionMatching(value, field) {
|
|
|
+ // 创建全新数组引用,确保 forms.vue 能检测到变化
|
|
|
+ const newList = this.formRuleList.map(item => {
|
|
|
+ if (item.field == field) {
|
|
|
+ return { ...item, options: value }
|
|
|
+ }
|
|
|
+ return item
|
|
|
+ })
|
|
|
+ this.formRuleList = newList
|
|
|
+ },
|
|
|
+ optionMatchingSearch(value, field) {
|
|
|
+ this.formList.forEach((item, index) => {
|
|
|
+ if (item.field == field) {
|
|
|
+ this.$set(item, 'options', [{ label: '全部', value: '' }, ...value])
|
|
|
+ }
|
|
|
+ })
|
|
|
+ },
|
|
|
+
|
|
|
+ searchProtocol(value) {
|
|
|
+ this.searchValue = value
|
|
|
+ this.Pagination.PageIndex = 1
|
|
|
+ this.getList()
|
|
|
+ },
|
|
|
+
|
|
|
+ resetDept() {
|
|
|
+ // 清空部门选择
|
|
|
+ // this.ruleForm.deptId = ''
|
|
|
+ // 重新加载全部部门选项
|
|
|
+ this.loadDeptOptions('')
|
|
|
+ },
|
|
|
+
|
|
|
+ getList() {
|
|
|
+ var params = {
|
|
|
+ page: this.Pagination.PageIndex,
|
|
|
+ pageSize: this.Pagination.PageSize,
|
|
|
+ ...this.searchValue
|
|
|
+ }
|
|
|
+ if (params.deptId === '' || params.deptId === undefined) {
|
|
|
+ delete params.deptId
|
|
|
+ }
|
|
|
+ getPostList(params).then(res => {
|
|
|
+ if (res.code == 200) {
|
|
|
+ this.tableData = (res.data.list || []).map(item => {
|
|
|
+ return {
|
|
|
+ ...item,
|
|
|
+ deptId: this.companyNameMap[item.deptId] || item.deptId
|
|
|
+ }
|
|
|
+ })
|
|
|
+ this.Total = res.data.count || 0
|
|
|
+ }
|
|
|
+ })
|
|
|
+ },
|
|
|
+
|
|
|
+ loadMenuTree(menuIds) {
|
|
|
+ getMenuTree().then(res => {
|
|
|
+ if (res.code == 200) {
|
|
|
+ this.menuTreeData = res.data || []
|
|
|
+ if (menuIds && menuIds.length > 0) {
|
|
|
+ this.$nextTick(() => {
|
|
|
+ if (this.$refs.permTree) {
|
|
|
+ this.$refs.permTree.setCheckedKeys(menuIds)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+ })
|
|
|
+ },
|
|
|
+
|
|
|
+ handleTreeCheck(currNode, checkState) {
|
|
|
+ // 防止 setChecked 触发的事件导致递归
|
|
|
+ if (this._treeChecking) return
|
|
|
+ this._treeChecking = true
|
|
|
+
|
|
|
+ const checked = checkState.checkedKeys.includes(currNode.id)
|
|
|
+
|
|
|
+ // 递归处理所有子孙节点
|
|
|
+ const cascadeChildren = (node) => {
|
|
|
+ if (node.children && node.children.length > 0) {
|
|
|
+ node.children.forEach(child => {
|
|
|
+ this.$refs.permTree.setChecked(child.id, checked)
|
|
|
+ cascadeChildren(child)
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+ cascadeChildren(currNode)
|
|
|
+
|
|
|
+ this._treeChecking = false
|
|
|
+ },
|
|
|
+
|
|
|
+ handleAdd() {
|
|
|
+ let flag = this.$refs['childRules'].validateForm();
|
|
|
+ if (!flag) {
|
|
|
+ this.$message.error('表单信息不完整,请继续填写完整');
|
|
|
+ return
|
|
|
+ }
|
|
|
+ const checkedKeys = this.$refs.permTree ? this.$refs.permTree.getCheckedKeys() : []
|
|
|
+
|
|
|
+ if (this.operationType == 'add') {
|
|
|
+ this.confirmLoading = true
|
|
|
+ var params = {
|
|
|
+ postName: this.ruleForm.postName,
|
|
|
+ postCode: this.ruleForm.postCode,
|
|
|
+ deptId: Number(this.ruleForm.deptId),
|
|
|
+ sort: parseInt(this.ruleForm.sort) || 0,
|
|
|
+ status: this.ruleForm.status,
|
|
|
+ remark: this.ruleForm.remark,
|
|
|
+ menuIds: checkedKeys,
|
|
|
+ }
|
|
|
+ addPost(params).then(res => {
|
|
|
+ if (res.code == 200) {
|
|
|
+ this.$message({ message: '操作成功', type: 'success' });
|
|
|
+ this.getList()
|
|
|
+ }
|
|
|
+ this.staffDialogVisible = false
|
|
|
+ this.confirmLoading = false
|
|
|
+ }).catch(() => { this.confirmLoading = false })
|
|
|
+ } else if (this.operationType == 'edit') {
|
|
|
+ this.confirmLoading = true
|
|
|
+ var params2 = {
|
|
|
+ id: this.ruleForm.id,
|
|
|
+ postName: this.ruleForm.postName,
|
|
|
+ postCode: this.ruleForm.postCode,
|
|
|
+ deptId: Number(this.ruleForm.deptId),
|
|
|
+ sort: parseInt(this.ruleForm.sort) || 0,
|
|
|
+ status: this.ruleForm.status,
|
|
|
+ remark: this.ruleForm.remark,
|
|
|
+ menuIds: checkedKeys,
|
|
|
+ }
|
|
|
+ putPost(params2).then(res => {
|
|
|
+ if (res.code == 200) {
|
|
|
+ this.$message({ message: '操作成功', type: 'success' });
|
|
|
+ this.getList()
|
|
|
+ }
|
|
|
+ this.staffDialogVisible = false
|
|
|
+ this.confirmLoading = false
|
|
|
+ }).catch(() => { this.confirmLoading = false })
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ buttonData(row, type) {
|
|
|
+ this.operationType = type
|
|
|
+ if (type == 'edit') {
|
|
|
+ this.loadDeptOptions();
|
|
|
+ this.staffTitle = '编辑岗位'
|
|
|
+ // 先加载公司列表,再打开弹窗(确保 select 选项已就绪)
|
|
|
+ this.getCompany(() => {
|
|
|
+ getPostDetail(row.id).then(res => {
|
|
|
+ if (res.code == 200) {
|
|
|
+ const detail = res.data
|
|
|
+ this.staffDialogVisible = true
|
|
|
+ // 延迟设置表单值,确保 dialog 已渲染
|
|
|
+ setTimeout(() => {
|
|
|
+ this.$nextTick(() => {
|
|
|
+ this.ruleForm.id = detail.id
|
|
|
+ this.ruleForm.postName = detail.postName
|
|
|
+ this.ruleForm.postCode = detail.postCode
|
|
|
+ this.ruleForm.deptId = String(detail.deptId)
|
|
|
+ this.ruleForm.sort = detail.sort
|
|
|
+ this.ruleForm.status = detail.status
|
|
|
+ this.ruleForm.remark = detail.remark || ''
|
|
|
+ this.checkedMenuIds = detail.menuIds || []
|
|
|
+ this.loadMenuTree(detail.menuIds || [])
|
|
|
+ })
|
|
|
+ })
|
|
|
+ }
|
|
|
+ })
|
|
|
+ })
|
|
|
+ } else if (type == 'del') {
|
|
|
+ this.deletePost(row.id)
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ openModel(type) {
|
|
|
+ this.staffTitle = '新增岗位'
|
|
|
+ this.ruleForm = JSON.parse(JSON.stringify(this.defaultRuleForm))
|
|
|
+ this.checkedMenuIds = []
|
|
|
+ this.operationType = type
|
|
|
+ this.getCompany(() => {
|
|
|
+ this.staffDialogVisible = true
|
|
|
+ this.loadMenuTree([])
|
|
|
+ })
|
|
|
+ this.loadDeptOptions()
|
|
|
+ },
|
|
|
+
|
|
|
+ deletePost(id) {
|
|
|
+ this.$confirm('此操作将删除该岗位, 是否继续?', '提示', {
|
|
|
+ confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning'
|
|
|
+ }).then(() => {
|
|
|
+ delPost({ id: id }).then(res => {
|
|
|
+ if (res.code == 200) {
|
|
|
+ this.$message({ message: '操作成功', type: 'success' });
|
|
|
+ this.getList()
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }).catch(() => {});
|
|
|
+ },
|
|
|
+
|
|
|
+ changeSize(val) { this.Pagination.PageSize = val; this.getList() },
|
|
|
+ changeCurrent(val) { this.Pagination.PageIndex = val; this.getList() },
|
|
|
+
|
|
|
+ closeDialog() {
|
|
|
+ this.ruleForm = JSON.parse(JSON.stringify(this.defaultRuleForm))
|
|
|
+ this.checkedMenuIds = []
|
|
|
+ this.menuTreeData = []
|
|
|
+ if (this.$refs.childRules) {
|
|
|
+ this.$refs.childRules.resetCheck();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+ .perm-tree-box {
|
|
|
+ border: 1px solid #DCDFE6;
|
|
|
+ border-radius: 4px;
|
|
|
+ padding: 10px;
|
|
|
+ max-height: 420px;
|
|
|
+ overflow-y: auto;
|
|
|
+ }
|
|
|
+ .perm-tree-title {
|
|
|
+ font-size: 14px;
|
|
|
+ font-weight: bold;
|
|
|
+ color: #f3f3f3;
|
|
|
+ margin-bottom: 10px;
|
|
|
+ padding-bottom: 8px;
|
|
|
+ border-bottom: 1px solid #EBEEF5;
|
|
|
+ }
|
|
|
+ .custom-tree-node {
|
|
|
+ font-size: 13px;
|
|
|
+ flex: 1;
|
|
|
+ }
|
|
|
+</style>
|