ct пре 1 дан
родитељ
комит
8dd0180b77
1 измењених фајлова са 144 додато и 33 уклоњено
  1. 144 33
      src/views/admin/sysCompany/index.vue

+ 144 - 33
src/views/admin/sysCompany/index.vue

@@ -87,9 +87,9 @@
     <pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList" />
 
     <!-- ===== 编辑租户弹窗 ===== -->
-    <el-dialog title="编辑租户" :visible.sync="viewOpen" width="680px" append-to-body destroy-on-close>
-      <el-form ref="editForm" :model="viewForm" :rules="editRules" label-width="100px" size="small">
-        <el-row :gutter="20">
+    <el-dialog title="编辑租户" :visible.sync="viewOpen" width="720px" append-to-body destroy-on-close class="tenant-dialog">
+      <el-form ref="editForm" :model="viewForm" :rules="editRules" label-width="112px" size="small" class="tenant-dialog-form">
+        <el-row :gutter="16">
           <el-col :span="12">
             <el-form-item label="租户ID">
               <span>{{ viewForm.id }}</span>
@@ -105,25 +105,19 @@
               <el-input v-model="viewForm.tenantName" placeholder="请输入租户名称" />
             </el-form-item>
           </el-col>
-          <el-col :span="12">
-            <el-form-item label="联系人" prop="contactName">
-              <el-input v-model="viewForm.contactName" placeholder="请输入联系人" />
-            </el-form-item>
-          </el-col>
           <el-col :span="12">
             <el-form-item label="联系电话" prop="contactPhone">
               <el-input v-model="viewForm.contactPhone" placeholder="请输入联系电话" />
             </el-form-item>
           </el-col>
           <el-col :span="12">
-            <el-form-item label="过期时间" prop="expireTime">
-              <el-date-picker v-model="viewForm.expireTime" type="date" value-format="yyyy-MM-dd" placeholder="选择过期时间" style="width:100%" />
+            <el-form-item label="联系人" prop="contactName">
+              <el-input v-model="viewForm.contactName" placeholder="请输入联系人" />
             </el-form-item>
           </el-col>
           <el-col :span="12">
-            <el-form-item label="租户余额">
-              <span style="color:#1890ff;font-weight:bold">¥{{ viewForm.balance || '0.00' }}</span>
-              <span style="color:#999;font-size:12px;margin-left:8px">(通过充值/扣款调整)</span>
+            <el-form-item label="过期时间" prop="expireTime">
+              <el-date-picker v-model="viewForm.expireTime" type="date" value-format="yyyy-MM-dd" placeholder="选择过期时间" style="width:100%" />
             </el-form-item>
           </el-col>
           <el-col :span="12">
@@ -139,6 +133,23 @@
                 :active-value="1" :inactive-value="0" />
             </el-form-item>
           </el-col>
+          <el-col :span="12">
+            <el-form-item label="公司数量" prop="companyNum">
+              <el-input-number v-model="viewForm.companyNum" :min="1" :precision="0" controls-position="right" style="width:100%" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item prop="accountNum">
+              <span slot="label" class="tenant-form-label" title="公司员工账户总数量">员工账户数</span>
+              <el-input-number v-model="viewForm.accountNum" :min="1" :precision="0" controls-position="right" style="width:100%" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="24">
+            <el-form-item label="租户余额" class="tenant-balance-item">
+              <span style="color:#1890ff;font-weight:bold">¥{{ viewForm.balance || '0.00' }}</span>
+              <span class="tenant-balance-hint">(通过充值/扣款调整)</span>
+            </el-form-item>
+          </el-col>
         </el-row>
       </el-form>
       <div slot="footer">
@@ -148,9 +159,9 @@
     </el-dialog>
 
     <!-- ===== 新增租户弹窗 ===== -->
-    <el-dialog title="新增租户" :visible.sync="addDialog.visible" width="680px" append-to-body destroy-on-close>
-      <el-form ref="addForm" :model="addDialog.form" :rules="addDialog.rules" label-width="100px" size="small">
-        <el-row :gutter="20">
+    <el-dialog title="新增租户" :visible.sync="addDialog.visible" width="720px" append-to-body destroy-on-close class="tenant-dialog">
+      <el-form ref="addForm" :model="addDialog.form" :rules="addDialog.rules" label-width="112px" size="small" class="tenant-dialog-form">
+        <el-row :gutter="16">
           <el-col :span="12">
             <el-form-item label="企业名称" prop="tenantName">
               <el-input v-model="addDialog.form.tenantName" placeholder="请输入企业名称" />
@@ -201,13 +212,25 @@
             </el-form-item>
           </el-col>
           <el-col :span="12">
-            <el-form-item label="状态" prop="status">
+            <el-form-item label="公司数量" prop="companyNum">
+              <el-input-number v-model="addDialog.form.companyNum" :min="1" :precision="0" controls-position="right" style="width:100%" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item prop="accountNum">
+              <span slot="label" class="tenant-form-label" title="公司员工账户总数量">员工账户数</span>
+              <el-input-number v-model="addDialog.form.accountNum" :min="1" :precision="0" controls-position="right" style="width:100%" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="状态" prop="status" class="tenant-status-item">
               <el-radio-group v-model="addDialog.form.status">
                 <el-radio :label="1">启用</el-radio>
                 <el-radio :label="0">禁用</el-radio>
               </el-radio-group>
             </el-form-item>
           </el-col>
+
         </el-row>
       </el-form>
       <div slot="footer">
@@ -248,6 +271,8 @@
     <el-dialog :title="menuDialog.title" :visible.sync="menuDialog.visible" width="560px" append-to-body destroy-on-close @opened="onMenuDialogOpened">
       <div v-loading="menuDialog.loading" class="menu-tree-scroll">
         <div v-if="menuDialog.treeReady && menuDialog.treeData.length" class="menu-tree-toolbar">
+          <el-button type="text" size="mini" @click="checkAllMenuNodes">全选</el-button>
+          <el-button type="text" size="mini" @click="uncheckAllMenuNodes">全不选</el-button>
           <el-button type="text" size="mini" @click="expandAllMenuNodes">展开全部</el-button>
           <el-button type="text" size="mini" @click="collapseAllMenuNodes">收起全部</el-button>
         </div>
@@ -350,7 +375,9 @@ export default {
       editSubmitting: false,
       editRules: {
         tenantName: [{ required: true, message: '请输入租户名称', trigger: 'blur' }],
-        contactPhone: [{ required: true, message: '请输入联系电话', trigger: 'blur' }]
+        contactPhone: [{ required: true, message: '请输入联系电话', trigger: 'blur' }],
+        companyNum: [{ required: true, message: '请输入公司数量', trigger: 'blur' }],
+        accountNum: [{ required: true, message: '请输入员工账户数', trigger: 'blur' }]
       },
       // 新增租户弹窗
       addDialog: {
@@ -365,13 +392,17 @@ export default {
           manager: '',
           startTime: null,
           expireTime: null,
-          status: 1
+          status: 1,
+          companyNum: 1,
+          accountNum: 1
         },
         rules: {
           tenantName: [{ required: true, message: '请输入企业名称', trigger: 'blur' }],
           contactPhone: [{ required: true, message: '请输入联系电话', trigger: 'blur' }],
           userName: [{ required: true, message: '请输入管理员账号', trigger: 'blur' }],
-          password: [{ required: true, message: '请输入密码', trigger: 'blur' }]
+          password: [{ required: true, message: '请输入密码', trigger: 'blur' }],
+          companyNum: [{ required: true, message: '请输入公司数量', trigger: 'blur' }],
+          accountNum: [{ required: true, message: '请输入员工账户数', trigger: 'blur' }]
         }
       },
       // 充值/扣款弹窗
@@ -394,6 +425,7 @@ export default {
         treeReady: false,
         checkedKeys: [],
         checkedKeySet: null,
+        initialAssignedIds: null,
         loading: false,
         submitting: false
       },
@@ -448,7 +480,9 @@ export default {
         proxyId: null,
         startTime: null,
         expireTime: null,
-        status: 1
+        status: 1,
+        companyNum: 1,
+        accountNum: 1
       }
       this.addDialog.visible = true
       this.$nextTick(() => {
@@ -497,6 +531,12 @@ export default {
     handleView(row) {
       getCompanyInfo(row.id).then(response => {
         this.viewForm = response.data
+        if (this.viewForm.companyNum == null) {
+          this.$set(this.viewForm, 'companyNum', 1)
+        }
+        if (this.viewForm.accountNum == null) {
+          this.$set(this.viewForm, 'accountNum', 1)
+        }
         // 为 el-switch 提供 number 类型的 status
         this.$set(this.viewForm, 'statusBool', this.viewForm.status)
         this.viewOpen = true
@@ -519,7 +559,9 @@ export default {
           contactPhone: this.viewForm.contactPhone,
           expireTime: this.viewForm.expireTime,
           status: this.viewForm.statusBool,
-          proxyId: this.viewForm.proxyId
+          proxyId: this.viewForm.proxyId,
+          companyNum: this.viewForm.companyNum,
+          accountNum: this.viewForm.accountNum
         }
         updateTenant(data).then(() => {
           this.$message.success('保存成功')
@@ -591,11 +633,19 @@ export default {
         this.exportLoading = false
       }).catch(() => { this.exportLoading = false })
     },
-    collectCheckedFromTree(nodes) {
+    normalizeMenuId(id) {
+      if (id == null || id === '') return null
+      const n = Number(id)
+      return Number.isNaN(n) ? id : n
+    },
+    collectAssignedMenuIds(nodes) {
       const ids = []
       const walk = (list) => {
         (list || []).forEach(n => {
-          if (n.visible === '0') ids.push(n.menuId)
+          if (n.visible === '0') {
+            const mid = this.normalizeMenuId(n.menuId)
+            if (mid != null) ids.push(mid)
+          }
           if (n.children && n.children.length) walk(n.children)
         })
       }
@@ -606,8 +656,13 @@ export default {
       const tree = this.$refs.menuTree
       const d = this.menuDialog
       if (!tree) return
-      tree.setCheckedKeys(this.collectCheckedFromTree(menus))
-      const keys = tree.getCheckedKeys().concat(tree.getHalfCheckedKeys())
+      // 勿用 setCheckedKeys(父节点):会级联勾选未分配子节点,导致提交 diff 为空
+      tree.setCheckedKeys([])
+      this.collectAssignedMenuIds(menus).forEach(menuId => {
+        const node = tree.getNode(menuId)
+        if (node) node.setChecked(true, false)
+      })
+      const keys = tree.getCheckedKeys().concat(tree.getHalfCheckedKeys()).map(id => this.normalizeMenuId(id))
       d.checkedKeys = keys
       d.checkedKeySet = new Set(keys)
     },
@@ -617,6 +672,24 @@ export default {
         if (n.children && n.children.length) this.walkMenuTreeNodes(n.children, fn)
       })
     },
+    collectAllMenuIds(nodes) {
+      const ids = []
+      this.walkMenuTreeNodes(nodes, n => {
+        const mid = this.normalizeMenuId(n.menuId)
+        if (mid != null) ids.push(mid)
+      })
+      return ids
+    },
+    checkAllMenuNodes() {
+      const tree = this.$refs.menuTree
+      if (!tree) return
+      tree.setCheckedKeys(this.collectAllMenuIds(this.menuDialog.treeData))
+    },
+    uncheckAllMenuNodes() {
+      const tree = this.$refs.menuTree
+      if (!tree) return
+      tree.setCheckedKeys([])
+    },
     expandAllMenuNodes() {
       const tree = this.$refs.menuTree
       if (!tree) return
@@ -649,6 +722,9 @@ export default {
           return Promise.reject(new Error(res.msg || '加载菜单失败'))
         }
         const flat = res.menus || []
+        d.initialAssignedIds = new Set(
+          flat.filter(m => m.visible === '0').map(m => this.normalizeMenuId(m.menuId)).filter(id => id != null)
+        )
         // 与模板维护页相同:扁平数据 + handleTree(menuId) 建树,保证层级一致
         const tree = this.handleTree(flat, 'menuId')
         d.treeData = tree
@@ -674,6 +750,7 @@ export default {
         treeReady: false,
         checkedKeys: [],
         checkedKeySet: null,
+        initialAssignedIds: null,
         loading: false,
         submitting: false
       }
@@ -816,12 +893,14 @@ export default {
     submitMenuEdit() {
       const tree = this.$refs.menuTree
       if (!tree) return
-      // 级联模式:纳入全选+半选节点作为当前勾选状态
-      const currentChecked = tree.getCheckedKeys().concat(tree.getHalfCheckedKeys())
-      const currentSet = new Set(currentChecked)
-      const originalSet = this.menuDialog.checkedKeySet || new Set(this.menuDialog.checkedKeys)
-      const selected = currentChecked.filter(id => !originalSet.has(id))
-      const unSelected = this.menuDialog.checkedKeys.filter(id => !currentSet.has(id))
+      const initialAssigned = this.menuDialog.initialAssignedIds || new Set()
+      const currentSelected = tree.getCheckedKeys()
+        .concat(tree.getHalfCheckedKeys())
+        .map(id => this.normalizeMenuId(id))
+        .filter(id => id != null)
+      const currentSet = new Set(currentSelected)
+      const selected = currentSelected.filter(id => !initialAssigned.has(id))
+      const unSelected = [...initialAssigned].filter(id => !currentSet.has(id))
       if (selected.length === 0 && unSelected.length === 0) {
         this.$message.info('菜单无变化')
         this.menuDialog.visible = false
@@ -860,11 +939,43 @@ export default {
 }
 .menu-tree-toolbar {
   margin-bottom: 8px;
-  text-align: right;
+  display: flex;
+  flex-wrap: wrap;
+  align-items: center;
+  justify-content: flex-end;
+  gap: 4px;
 }
 .custom-tree-node {
   display: flex;
   align-items: center;
   font-size: 13px;
 }
+.tenant-dialog-form ::v-deep .el-form-item__label {
+  white-space: nowrap;
+  line-height: 32px;
+}
+.tenant-dialog-form ::v-deep .el-form-item {
+  margin-bottom: 16px;
+}
+.tenant-dialog-form ::v-deep .el-input-number {
+  width: 100%;
+}
+.tenant-form-label {
+  display: inline-block;
+  max-width: 100%;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  vertical-align: middle;
+}
+.tenant-balance-hint {
+  color: #999;
+  font-size: 12px;
+  margin-left: 8px;
+}
+.tenant-balance-item ::v-deep .el-form-item__content {
+  line-height: 32px;
+}
+.tenant-status-item ::v-deep .el-form-item__content {
+  line-height: 32px;
+}
 </style>