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