wuqingchuimiexiaoshanhe 1 месяц назад
Родитель
Сommit
5a08e9489e
4 измененных файлов с 1110 добавлено и 0 удалено
  1. 79 0
      src/api/post.js
  2. 414 0
      src/views/OrderManagement/packingOrder.vue
  3. 486 0
      src/views/system/PostManagement.vue
  4. 131 0
      src/views/system/post.js

+ 79 - 0
src/api/post.js

@@ -0,0 +1,79 @@
+import request from '@/utils/request'
+
+// 获取岗位列表
+export function getPostList(parameter) {
+  return request({
+    url: '/api/post',
+    method: 'get',
+    params: parameter
+  })
+}
+
+// 获取岗位详情
+export function getPostDetail(id) {
+  return request({
+    url: '/api/post/' + id,
+    method: 'get'
+  })
+}
+
+// 新增岗位
+export function addPost(parameter) {
+  return request({
+    url: '/api/post',
+    method: 'post',
+    data: parameter
+  })
+}
+
+// 修改岗位
+export function putPost(parameter) {
+  return request({
+    url: '/api/post',
+    method: 'put',
+    data: parameter
+  })
+}
+
+// 删除岗位
+export function delPost(parameter) {
+  return request({
+    url: '/api/post',
+    method: 'delete',
+    data: parameter
+  })
+}
+
+// 获取所有岗位
+export function getAllPosts(parameter) {
+  return request({
+    url: '/api/post/all',
+    method: 'get',
+    params: parameter
+  })
+}
+
+// 获取菜单权限树
+export function getMenuTree() {
+  return request({
+    url: '/api/post/menu-tree',
+    method: 'get'
+  })
+}
+
+// 根据ID列表批量获取岗位名称映射
+export function getPostNames(ids) {
+  return request({
+    url: '/api/post/names',
+    method: 'get',
+    params: { ids: ids.join(',') }
+  })
+}
+
+// 获取岗位已分配的菜单ID
+export function getPostMenuIds(id) {
+  return request({
+    url: '/api/post/' + id + '/menu-ids',
+    method: 'get'
+  })
+}

+ 414 - 0
src/views/OrderManagement/packingOrder.vue

@@ -0,0 +1,414 @@
+<template>
+  <!-- 待取药装箱 -->
+  <div class="home">
+    <div ref="grabble">
+      <actionBar :operateList="operateList" :formList="formList" :ruleForm="searchRuleForm" @openModel="openModel"
+        @searchProtocol="searchProtocol"></actionBar>
+    </div>
+    <div class="card_content">
+      <tables ref="refWaybill" controlswidth="360px" tableHeight="68vh" :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>
+    <orderDetails ref="order" :waybillData="waybillData"></orderDetails>
+    <el-dialog title="装箱操作" :visible.sync="packingDialogVisible" width="600px" :close-on-click-modal="false"
+      @close="closeDialog">
+      <forms ref="packingRules" :formNewList="packingRuleList" :ruleForm="packingRuleForm" :formIceTracing="true"
+        :iceTracing="true" :iceBank="true" labelWidth="70px" @handleScroll="handleScroll" @remoteMethod="remoteMethod"
+        @changeOption="changeOption">
+      </forms>
+      <span slot="footer" class="dialog-footer">
+        <el-button plain @click="packingDialogVisible = false">取 消</el-button>
+        <el-button type="primary" :loading="packingConfirmLoading" @click.stop="packingHandleAdd">确 定</el-button>
+      </span>
+    </el-dialog>
+    <prescriptionPicture ref="picture" :srcList="srcList" :fileList="fileList" :orderID="orderId"
+      @prescription="prescription" @closeDialog="closeDialogil"></prescriptionPicture>
+    <addEditOrder ref="orderEdit" :orderId="orderId" :staffTitle="staffTitle" :orderInformation="orderInformation"
+      :operationType="operationType" @refreshList="refreshList">
+    </addEditOrder>
+  </div>
+</template>
+
+<script>
+  import {
+    getWaybill,
+    waybillPacking,
+    putUpdateStatus
+  } from '@/api/waybill'
+  import {
+    getCoolerBox,
+    getRaftCoolerBox
+  } from '@/api/incubator.js'
+  import actionBar from '@/components/actionBar'
+  import tables from '@/components/tables'
+  import pagination from '@/components/pagination'
+  import forms from '@/components/forms'
+  import orderDetails from '@/components/orderDetails'
+  import prescriptionPicture from '@/components/prescriptionPicture'
+  import addEditOrder from '@/components/addEditOrder'
+  import {
+    employee,
+    packingList
+  } from "./delivered.js";
+  export default {
+    name: 'OrderManagement',
+    components: {
+      actionBar,
+      tables,
+      pagination,
+      forms,
+      orderDetails,
+      prescriptionPicture,
+      addEditOrder
+    },
+    data() {
+      return {
+        operateList: [{
+          type: 'packings',
+          title: '批量装箱',
+          colour: 'success'
+        }],
+        formList: [{
+          type: 'input',
+          label: '订单编号',
+          field: 'waybillNo',
+          placeholder: '请输入订单编号',
+        }, {
+          type: 'input',
+          label: '患者名称',
+          field: 'Consignee_address_name',
+          placeholder: '请输入患者名称',
+        }, {
+          type: 'input',
+          label: '患者手机号',
+          field: 'Consignee_address_phone',
+          placeholder: '请输入患者手机号',
+        }],
+        searchRuleForm: {
+          waybillNo: '',
+          Consignee_address_name: '',
+          Consignee_address_phone: '',
+        },
+        searchValue: {},
+        Pagination: {
+          PageIndex: 1,
+          PageSize: 10,
+        },
+        Total: 0,
+        operationType: '',
+        tableData: [],
+        tableList: [],
+        btnData: [{
+          type: 'logs',
+          label: '详情',
+          icon: 'el-icon-tickets',
+          style: 'success',
+        }, {
+          type: 'edit',
+          label: '修改',
+          icon: 'el-icon-edit',
+          style: 'success',
+        }, {
+          type: 'send',
+          label: '处方',
+          icon: 'el-icon-tickets',
+          style: 'primary',
+        }, {
+          type: 'packing',
+          label: '装箱',
+          icon: 'el-icon-box',
+          style: 'warning',
+        }, {
+          type: 'delOrder',
+          label: '取消订单',
+          icon: 'el-icon-delete',
+          style: 'danger',
+        }],
+        packingDialogVisible: false,
+        packingRuleList: packingList(),
+        packingRuleForm: {
+          fileList: [],
+          coolerBoxId: null,
+          code: [],
+          tamperProofLabelImg: '',
+          tamperProofLabel: '',
+        },
+        packingConfirmLoading: false,
+        page: 1,
+        staffName: '',
+        limitNo: true,
+        packingOrdersId: 2,
+        waybillIdList: [],
+        orderId: null,
+        waybillData: {},
+        srcList: [],
+        fileList: [],
+        staffTitle: '',
+        orderInformation: {},
+      }
+    },
+    mounted() {
+      let arr = employee()
+      arr.forEach(item => {
+        if (item.field == 'unpackBtn') {
+          item.labelButton = this.btnData
+        }
+      })
+      this.tableList = arr
+      this.getList()
+    },
+    methods: {
+      searchProtocol(value) {
+        this.Pagination.PageIndex = 1
+        this.searchValue = value
+        if (value.time) {
+          this.searchValue.orderStartTime = value.time[0]
+          this.searchValue.orderEndTime = value.time[1]
+        }
+        this.getList()
+      },
+      getList() {
+        var params = {
+          status: 9,
+          page: this.Pagination.PageIndex,
+          pageSize: this.Pagination.PageSize,
+          ...this.searchValue
+        }
+        delete params.time
+        getWaybill(params).then(res => {
+          if (res.code == 200) {
+            this.tableData = res.data.list
+            this.tableData.forEach(item => {
+              if (item.iceRaftCode) {
+                item.iceRaftCode = item.iceRaftCode.toString()
+              }
+              item.visible = false
+            })
+            this.Total = res.data.count
+          }
+        })
+      },
+      async openModel(type) {
+        this.operationType = type
+        if (type == 'packings') {
+          const arrID = this.$refs.refWaybill.waybillIds
+          let arrList = []
+          arrID.forEach(item => {
+            arrList.push(item.id)
+          })
+          this.waybillIdList = arrList
+          if (arrList.length != 0) {
+            this.packingDialogVisible = true
+            this.getIncubator(2)
+          } else {
+            this.$message({
+              message: '请先选择需要批量操作的订单',
+              type: 'warning'
+            });
+          }
+        }
+      },
+      async buttonData(row, type) {
+        this.operationType = type
+        this.orderId = row.id
+        if (type == 'logs') {
+          this.$refs.order.staffDialogVisible = true
+          this.$refs.order.tableData = row.drugs
+          this.waybillData = row
+          this.waybillData.coolerBoxName = row.coolerBox ? row.coolerBox.name : ''
+          this.waybillData.courierName = row.delivery ? row.delivery.nickName : ''
+          this.waybillData.courierPhone = row.delivery ? row.delivery.phone : ''
+          this.waybillData.reCheckName = row.reCheck ? row.reCheck.nickName : ''
+          this.waybillData.reCheckPhone = row.reCheck ? row.reCheck.phone : ''
+        } else if (type == 'send') {
+          if (row.prescription) {
+            this.srcList = row.prescription
+          }
+          this.$refs.picture.prescriptionVisible = true
+        } else if (type == 'packing') {
+          this.packingDialogVisible = true
+          if (row.tamperProofLabelImg) {
+            var arr = row.tamperProofLabelImg.split(',')
+            var arr3 = []
+            arr.forEach((item) => {
+              var arr1 = { url: '' }
+              arr1.url = item
+              arr3.push(arr1)
+            })
+            this.packingRuleForm.fileList = arr3
+            this.packingRuleForm.tamperProofLabelImg = row.tamperProofLabelImg
+          }
+          this.packingRuleForm.tamperProofLabel = row.tamperProofLabel
+          this.getIncubator(2)
+        } else if (type == 'delOrder') {
+          this.$confirm('此操作将永久删除该订单, 是否继续?', '提示', {
+            confirmButtonText: '确定',
+            cancelButtonText: '取消',
+            type: 'warning'
+          }).then(() => {
+            let arrOrderId = []
+            arrOrderId.push(row.id)
+            putUpdateStatus({
+              id: arrOrderId,
+              status: 8,
+            }).then(res => {
+              if (res.code == 200) {
+                this.$message({ message: '操作成功', type: 'success' });
+                this.getList()
+              }
+            })
+          }).catch(() => {})
+        } else if (type == "edit") {
+          this.staffTitle = '修改'
+          this.orderInformation = {}
+          this.$refs.orderEdit.staffDialogVisible = true
+          this.$nextTick(() => {
+            this.orderInformation = row
+          })
+        }
+      },
+      refreshList() {
+        this.getList()
+      },
+      handleScroll() {
+        if (this.limitNo) {
+          this.getIncubator()
+        }
+      },
+      remoteMethod(value, type) {
+        this.resetSelect(type)
+        this.staffName = value
+        if (type == 'coolerBoxId') {
+          this.getIncubator(this.packingOrdersId)
+        }
+      },
+      resetSelect(type) {
+        this.packingRuleList.forEach(item => {
+          if (item.field == type) {
+            item.options = []
+          }
+        })
+        this.page = 1
+        this.limitNo = true
+        this.staffName = ''
+      },
+      packingHandleAdd() {
+        let flag = this.$refs['packingRules'].validateForm();
+        if (flag) {
+          this.packingConfirmLoading = true
+          let arrID = []
+          if (this.operationType == 'packing') {
+            arrID.push(this.orderId)
+          } else if (this.operationType == 'packings') {
+            arrID = this.waybillIdList
+          }
+          var fileImage = ''
+          if (this.packingRuleForm.fileList && this.packingRuleForm.fileList.length > 0) {
+            var imgList = JSON.parse(JSON.stringify(this.packingRuleForm.fileList))
+            var imgArr = []
+            imgList.forEach((item) => {
+              imgArr.push(item.url)
+            })
+            fileImage = imgArr.join()
+          }
+          const param = {
+            waybillIds: arrID,
+            coolerBoxId: Number(this.packingRuleForm.coolerBoxId),
+            iceRaftCode: this.packingRuleForm.code,
+            tamperProofLabel: this.packingRuleForm.tamperProofLabel,
+            tamperProofLabelImg: fileImage,
+          }
+          waybillPacking(param).then(res => {
+            if (res.code == 200) {
+              this.$message({ message: res.msg, type: 'success' });
+              this.getList()
+              this.packingDialogVisible = false
+            }
+            this.packingConfirmLoading = false
+          }).catch(() => {
+            this.packingConfirmLoading = false
+          })
+        } else {
+          this.$message.error('表单信息不完整,请继续填写完整');
+        }
+      },
+      getIncubator(type) {
+        let params = {
+          page: this.page,
+          pageSize: 10,
+          status: '2',
+          name: this.staffName,
+        }
+        getCoolerBox(params).then(res => {
+          if (res.code == 200) {
+            let arr = res.data.list
+            let arrList = []
+            arr.forEach(item1 => {
+              var arrData = { label: null, value: null }
+              arrData.label = item1.name
+              arrData.value = item1.id
+              arrList.push(arrData)
+            })
+            if (this.limitNo == true) {
+              this.packingRuleList.forEach(item => {
+                if (item.field == 'coolerBoxId') {
+                  item.options = item.options.concat(arrList);
+                  let some = [];
+                  item.options.forEach(el => {
+                    if (!some.some(e => e.value == el.value)) {
+                      some.push(el)
+                    }
+                  })
+                  item.options = some
+                }
+              })
+            }
+            if (arrList.length >= 10) {
+              this.page = ++this.page;
+            } else {
+              this.limitNo = false;
+            }
+          }
+        })
+      },
+      changeOption(id, field) {
+        if (field == 'coolerBoxId') {
+          getRaftCoolerBox({ coolerBoxId: id }).then(res => {
+            if (res.code == 200) {
+              if (res.data) {
+                this.packingRuleForm.code = res.data
+                this.$refs.packingRules.inputsList = res.data
+              }
+            }
+          })
+        }
+      },
+      prescription() {
+        this.Pagination.PageIndex = 1
+        this.getList()
+      },
+      changeSize(val) {
+        this.Pagination.PageSize = val
+        this.getList()
+      },
+      changeCurrent(val) {
+        this.Pagination.PageIndex = val
+        this.getList()
+      },
+      closeDialog() {},
+      closeDialogil() {
+        this.srcList = []
+        this.fileList = []
+      }
+    },
+  }
+</script>
+
+<style lang="scss" scoped>
+</style>

+ 486 - 0
src/views/system/PostManagement.vue

@@ -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>

+ 131 - 0
src/views/system/post.js

@@ -0,0 +1,131 @@
+// 岗位列表——表格列配置
+export const employee = () => {
+  return [{
+    field: 'postName',
+    label: '岗位名称',
+    align: 'center',
+  }, {
+    field: 'postCode',
+    label: '岗位编码',
+    align: 'center',
+  }, {
+    field: 'deptId',
+    label: '所属部门',
+    align: 'center',
+    options: [], // 动态填充部门名称映射
+  }, {
+    field: 'sort',
+    label: '排序',
+    align: 'center',
+  }, {
+    field: 'status',
+    label: '状态',
+    align: 'center',
+    options: [{
+      label: '停用',
+      value: '1',
+      bgcolor: '#F56C6C',
+    }, {
+      label: '正常',
+      value: '2',
+      bgcolor: '#67C23A',
+    }]
+  }, {
+    field: 'remark',
+    label: '备注',
+    align: 'center',
+  }, {
+    field: 'action',
+    label: '操作',
+    colWidth: '200px',
+    align: 'center',
+    labelButton: [{
+      type: 'edit',
+      label: '编辑',
+      icon: 'el-icon-edit',
+      style: 'primary',
+    }, {
+      type: 'del',
+      label: '删除',
+      icon: 'el-icon-delete',
+      style: 'danger',
+    }]
+  }]
+}
+
+// 岗位表单——新增/编辑表单配置
+export const formRules = () => {
+  return [{
+    field: 'postName',
+    label: '岗位名称',
+    placeholder: '请输入岗位名称',
+    type: 'input',
+    colWidth: 24,
+    rules: [{
+      required: true,
+      message: '请输入岗位名称',
+      trigger: 'blur'
+    }]
+  }, {
+    field: 'postCode',
+    label: '岗位编码',
+    placeholder: '请输入岗位编码',
+    type: 'input',
+    colWidth: 24,
+    rules: [{
+      required: true,
+      message: '请输入岗位编码',
+      trigger: 'blur'
+    }]
+  }, {
+    field: 'deptId',
+    label: '所属部门',
+    placeholder: '请选择所属部门',
+    type: 'searchSelect',
+    colWidth: 24,
+    options: [],
+    rules: [{
+      required: true,
+      message: '请选择所属部门',
+      trigger: 'change'
+    }]
+  }, {
+    field: 'sort',
+    label: '排序',
+    placeholder: '请输入排序值',
+    type: 'input',
+    colWidth: 12,
+    rules: [{
+      required: false,
+      message: '请输入排序值',
+      trigger: 'blur'
+    }]
+  }, {
+    field: 'status',
+    label: '状态',
+    placeholder: '请选择状态',
+    type: 'select',
+    colWidth: 12,
+    options: [{
+      label: '正常',
+      value: '2',
+    }, {
+      label: '停用',
+      value: '1',
+    }],
+    rules: [{
+      required: true,
+      message: '请选择状态',
+      trigger: 'change'
+    }]
+  }, {
+    field: 'remark',
+    label: '备注',
+    placeholder: '请输入备注',
+    type: 'input',
+    colWidth: 24,
+    rules: [{
+      required: false,
+    }]
+  }]
+}