优化平台端租户端
This commit is contained in:
@@ -1,10 +1,12 @@
|
|||||||
import request from "@/utils/request";
|
import request from "@/utils/request";
|
||||||
|
|
||||||
// 获取所有菜单
|
// 获取所有菜单
|
||||||
export function getAllMenus() {
|
// params.cid=2 时仅返回租户端(backend)菜单,用于角色权限编辑
|
||||||
|
export function getAllMenus(params = {}) {
|
||||||
return request({
|
return request({
|
||||||
url: `/backend/allmenu`,
|
url: `/backend/allmenu`,
|
||||||
method: "get",
|
method: "get",
|
||||||
|
params,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
import request from '@/utils/request'
|
||||||
|
|
||||||
|
// 平台端角色管理(yz_system_admin_role,可同时管理平台角色 cid=1 与租户角色 cid=2)
|
||||||
|
export function getAllPlatformRoles() {
|
||||||
|
return request({
|
||||||
|
url: '/platform/allRoles',
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getPlatformRoleById(id) {
|
||||||
|
return request({
|
||||||
|
url: `/platform/roles/${id}`,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createPlatformRole(data) {
|
||||||
|
return request({
|
||||||
|
url: '/platform/roles',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function updatePlatformRole(id, data) {
|
||||||
|
return request({
|
||||||
|
url: `/platform/roles/${id}`,
|
||||||
|
method: 'put',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function deletePlatformRole(id) {
|
||||||
|
return request({
|
||||||
|
url: `/platform/roles/${id}`,
|
||||||
|
method: 'delete'
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
import request from '@/utils/request'
|
||||||
|
|
||||||
|
// 平台端租户用户(yz_system_tenant_user)相关接口
|
||||||
|
export function getTenantUserList(params) {
|
||||||
|
return request({
|
||||||
|
url: '/platform/tenantUser/list',
|
||||||
|
method: 'get',
|
||||||
|
params,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getTenantUsersByTid(tid) {
|
||||||
|
return request({
|
||||||
|
url: `/platform/getTenantUsers/${tid}`,
|
||||||
|
method: 'get',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createTenantUser(data) {
|
||||||
|
return request({
|
||||||
|
url: '/platform/tenantUser/create',
|
||||||
|
method: 'post',
|
||||||
|
data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function editTenantUser(id, data) {
|
||||||
|
return request({
|
||||||
|
url: `/platform/tenantUser/edit/${id}`,
|
||||||
|
method: 'post',
|
||||||
|
data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function deleteTenantUser(id) {
|
||||||
|
return request({
|
||||||
|
url: `/platform/tenantUser/delete/${id}`,
|
||||||
|
method: 'delete',
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -12,6 +12,11 @@
|
|||||||
{{ roleDetail.status === 1 ? "启用" : "禁用" }}
|
{{ roleDetail.status === 1 ? "启用" : "禁用" }}
|
||||||
</el-tag>
|
</el-tag>
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="分类">
|
||||||
|
<el-tag :type="roleDetail.cid === 1 ? 'warning' : 'primary'" size="small">
|
||||||
|
{{ roleDetail.cid === 1 ? "平台角色" : "租户角色" }}
|
||||||
|
</el-tag>
|
||||||
|
</el-descriptions-item>
|
||||||
<el-descriptions-item label="权限列表">
|
<el-descriptions-item label="权限列表">
|
||||||
<div v-if="menuNames.length > 0" style="max-height: 300px; overflow-y: auto;">
|
<div v-if="menuNames.length > 0" style="max-height: 300px; overflow-y: auto;">
|
||||||
<el-tag
|
<el-tag
|
||||||
@@ -44,7 +49,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, watch, computed } from "vue";
|
import { ref, watch, computed } from "vue";
|
||||||
import { ElMessage } from "element-plus";
|
import { ElMessage } from "element-plus";
|
||||||
import { getRoleById } from "@/api/role";
|
import { getPlatformRoleById } from "@/api/platformRole";
|
||||||
import { getAllMenus } from "@/api/menu";
|
import { getAllMenus } from "@/api/menu";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@@ -67,11 +72,11 @@ const allMenus = ref<any[]>([]);
|
|||||||
// 监听 modelValue
|
// 监听 modelValue
|
||||||
watch(
|
watch(
|
||||||
() => props.modelValue,
|
() => props.modelValue,
|
||||||
(val) => {
|
async (val) => {
|
||||||
visible.value = val;
|
visible.value = val;
|
||||||
if (val && props.roleId) {
|
if (val && props.roleId) {
|
||||||
loadRoleDetail();
|
await loadRoleDetail();
|
||||||
loadMenus();
|
await loadMenus(roleDetail.value.cid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -127,7 +132,7 @@ const loadRoleDetail = async () => {
|
|||||||
|
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
try {
|
try {
|
||||||
const res = await getRoleById(props.roleId);
|
const res = await getPlatformRoleById(props.roleId);
|
||||||
if (res.code === 200) {
|
if (res.code === 200) {
|
||||||
roleDetail.value = res.data || {};
|
roleDetail.value = res.data || {};
|
||||||
} else {
|
} else {
|
||||||
@@ -141,10 +146,12 @@ const loadRoleDetail = async () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 加载菜单树
|
// 加载菜单树:租户角色(cid=2)只展示租户端菜单;平台角色展示全部权限
|
||||||
const loadMenus = async () => {
|
const loadMenus = async (cid?: number) => {
|
||||||
try {
|
try {
|
||||||
const res = await getAllMenus();
|
const isTenant = cid === undefined || cid === 2;
|
||||||
|
const params = isTenant ? { cid: 2 } : {};
|
||||||
|
const res = await getAllMenus(params);
|
||||||
if (res.code === 200) {
|
if (res.code === 200) {
|
||||||
allMenus.value = res.data || [];
|
allMenus.value = res.data || [];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,14 @@
|
|||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="分类" prop="cid">
|
||||||
|
<el-radio-group v-model="form.cid" @change="onCidChange">
|
||||||
|
<el-radio :label="2">租户角色</el-radio>
|
||||||
|
<el-radio :label="1">平台角色</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
<div class="form-tip">租户角色仅显示租户端菜单;平台角色显示全部权限</div>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
<el-form-item label="权限设置" prop="rights">
|
<el-form-item label="权限设置" prop="rights">
|
||||||
<div style="margin-bottom: 10px;">
|
<div style="margin-bottom: 10px;">
|
||||||
<el-button size="small" @click="toggleSelectAll">
|
<el-button size="small" @click="toggleSelectAll">
|
||||||
@@ -38,7 +46,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, watch, nextTick } from "vue";
|
import { ref, watch, nextTick } from "vue";
|
||||||
import { ElMessage } from "element-plus";
|
import { ElMessage } from "element-plus";
|
||||||
import { createRole, updateRole } from "@/api/role";
|
import { createPlatformRole, updatePlatformRole } from "@/api/platformRole";
|
||||||
import { getAllMenus } from "@/api/menu";
|
import { getAllMenus } from "@/api/menu";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@@ -66,6 +74,7 @@ const isExpandAll = ref(false);
|
|||||||
const form = ref({
|
const form = ref({
|
||||||
name: "",
|
name: "",
|
||||||
status: 1,
|
status: 1,
|
||||||
|
cid: 2,
|
||||||
rights: [] as number[],
|
rights: [] as number[],
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -140,9 +149,11 @@ const toggleSelectAll = () => {
|
|||||||
// --- 数据加载与监听 ---
|
// --- 数据加载与监听 ---
|
||||||
|
|
||||||
// 加载菜单树数据
|
// 加载菜单树数据
|
||||||
|
// 租户角色(cid=2)只展示租户端菜单;平台角色(cid=1)展示全部权限
|
||||||
const loadMenus = async () => {
|
const loadMenus = async () => {
|
||||||
try {
|
try {
|
||||||
const res = await getAllMenus();
|
const params = form.value.cid === 2 ? { cid: 2 } : {};
|
||||||
|
const res = await getAllMenus(params);
|
||||||
if (res.code === 200) {
|
if (res.code === 200) {
|
||||||
menuTree.value = res.data || [];
|
menuTree.value = res.data || [];
|
||||||
}
|
}
|
||||||
@@ -152,6 +163,18 @@ const loadMenus = async () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 切换分类时重新加载菜单树并回显已选权限
|
||||||
|
const onCidChange = async () => {
|
||||||
|
if (!visible.value) return;
|
||||||
|
await loadMenus();
|
||||||
|
nextTick(() => {
|
||||||
|
if (treeRef.value) {
|
||||||
|
treeRef.value.setCheckedKeys(form.value.rights);
|
||||||
|
updateSelectStatus();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
// 解析权限数据
|
// 解析权限数据
|
||||||
const parseRights = (rights: any): number[] => {
|
const parseRights = (rights: any): number[] => {
|
||||||
if (!rights) return [];
|
if (!rights) return [];
|
||||||
@@ -182,6 +205,7 @@ watch(
|
|||||||
form.value = {
|
form.value = {
|
||||||
name: props.role.name,
|
name: props.role.name,
|
||||||
status: props.role.status,
|
status: props.role.status,
|
||||||
|
cid: props.role.cid === 1 ? 1 : 2,
|
||||||
rights: parseRights(props.role.rights),
|
rights: parseRights(props.role.rights),
|
||||||
};
|
};
|
||||||
// 设置树的回显
|
// 设置树的回显
|
||||||
@@ -210,6 +234,7 @@ const resetForm = () => {
|
|||||||
form.value = {
|
form.value = {
|
||||||
name: "",
|
name: "",
|
||||||
status: 1,
|
status: 1,
|
||||||
|
cid: 2,
|
||||||
rights: [],
|
rights: [],
|
||||||
};
|
};
|
||||||
if (formRef.value) formRef.value.clearValidate();
|
if (formRef.value) formRef.value.clearValidate();
|
||||||
@@ -233,6 +258,7 @@ const handleSubmit = async () => {
|
|||||||
const submitData: any = {
|
const submitData: any = {
|
||||||
name: form.value.name,
|
name: form.value.name,
|
||||||
status: form.value.status,
|
status: form.value.status,
|
||||||
|
cid: form.value.cid,
|
||||||
rights: checkedKeys,
|
rights: checkedKeys,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -245,9 +271,9 @@ const handleSubmit = async () => {
|
|||||||
|
|
||||||
let res;
|
let res;
|
||||||
if (isEdit.value && props.role) {
|
if (isEdit.value && props.role) {
|
||||||
res = await updateRole(props.role.id, submitData);
|
res = await updatePlatformRole(props.role.id, submitData);
|
||||||
} else {
|
} else {
|
||||||
res = await createRole(submitData);
|
res = await createPlatformRole(submitData);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (res.code === 200) {
|
if (res.code === 200) {
|
||||||
@@ -271,4 +297,11 @@ const handleSubmit = async () => {
|
|||||||
:deep(.el-tree) {
|
:deep(.el-tree) {
|
||||||
background-color: var(--el-bg-color);
|
background-color: var(--el-bg-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.form-tip {
|
||||||
|
margin-top: 6px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--el-text-color-secondary);
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -15,6 +15,15 @@
|
|||||||
</div>
|
</div>
|
||||||
<el-divider></el-divider>
|
<el-divider></el-divider>
|
||||||
|
|
||||||
|
<!-- 分类筛选 -->
|
||||||
|
<div class="filter-bar">
|
||||||
|
<el-radio-group v-model="typeFilter">
|
||||||
|
<el-radio-button :value="'all'">全部</el-radio-button>
|
||||||
|
<el-radio-button :value="1">平台角色</el-radio-button>
|
||||||
|
<el-radio-button :value="2">租户角色</el-radio-button>
|
||||||
|
</el-radio-group>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- 错误状态 -->
|
<!-- 错误状态 -->
|
||||||
<div v-if="error" class="error-state">
|
<div v-if="error" class="error-state">
|
||||||
<el-alert title="加载失败" :message="error" type="error" show-icon />
|
<el-alert title="加载失败" :message="error" type="error" show-icon />
|
||||||
@@ -23,14 +32,20 @@
|
|||||||
|
|
||||||
<!-- 角色列表 -->
|
<!-- 角色列表 -->
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<el-table :data="roles" stripe style="width: 100%" v-loading="loading">
|
<el-table :data="displayRoles" stripe style="width: 100%" v-loading="loading">
|
||||||
<el-table-column prop="id" label="ID" width="80" align="center" />
|
|
||||||
<el-table-column
|
<el-table-column
|
||||||
prop="name"
|
prop="name"
|
||||||
label="角色名称"
|
label="角色名称"
|
||||||
min-width="150"
|
min-width="150"
|
||||||
align="center"
|
align="center"
|
||||||
/>
|
/>
|
||||||
|
<el-table-column prop="cid" label="类型" width="120" align="center">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-tag :type="row.cid === 1 ? 'warning' : 'primary'" size="small">
|
||||||
|
{{ row.cid === 1 ? "平台角色" : "租户角色" }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column prop="status" label="状态" width="100" align="center">
|
<el-table-column prop="status" label="状态" width="100" align="center">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-tag :type="row.status === 1 ? 'success' : 'info'" size="small">
|
<el-tag :type="row.status === 1 ? 'success' : 'info'" size="small">
|
||||||
@@ -38,12 +53,12 @@
|
|||||||
</el-tag>
|
</el-tag>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column
|
<el-table-column label="权限" min-width="150" align="center">
|
||||||
prop="rights"
|
<template #default="{ row }">
|
||||||
label="权限"
|
<el-tag v-if="!row.rights" type="success" size="small">全权限</el-tag>
|
||||||
min-width="150"
|
<el-tag v-else type="info" size="small">自定义权限</el-tag>
|
||||||
align="center"
|
</template>
|
||||||
/>
|
</el-table-column>
|
||||||
<el-table-column label="操作" width="220" fixed="right" align="center">
|
<el-table-column label="操作" width="220" fixed="right" align="center">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-button
|
<el-button
|
||||||
@@ -96,10 +111,13 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, onMounted } from "vue";
|
import { ref, computed, onMounted } from "vue";
|
||||||
import { ElMessage, ElMessageBox } from "element-plus";
|
import { ElMessage, ElMessageBox } from "element-plus";
|
||||||
import { Plus, View, Edit, Delete, Refresh } from "@element-plus/icons-vue";
|
import { Plus, View, Edit, Delete, Refresh } from "@element-plus/icons-vue";
|
||||||
import { getAllRoles, getRoleById, deleteRole } from "@/api/role";
|
import {
|
||||||
|
getAllPlatformRoles,
|
||||||
|
deletePlatformRole,
|
||||||
|
} from "@/api/platformRole";
|
||||||
import RoleEditDialog from "./components/edit.vue";
|
import RoleEditDialog from "./components/edit.vue";
|
||||||
import RoleDetailDialog from "./components/detail.vue";
|
import RoleDetailDialog from "./components/detail.vue";
|
||||||
import { useAuthStore } from "@/stores/auth";
|
import { useAuthStore } from "@/stores/auth";
|
||||||
@@ -110,6 +128,11 @@ const authStore = useAuthStore();
|
|||||||
const roles = ref<any[]>([]);
|
const roles = ref<any[]>([]);
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const error = ref("");
|
const error = ref("");
|
||||||
|
const typeFilter = ref<string | number>("all");
|
||||||
|
const displayRoles = computed(() => {
|
||||||
|
if (typeFilter.value === "all") return roles.value;
|
||||||
|
return roles.value.filter((r) => Number(r.cid) === Number(typeFilter.value));
|
||||||
|
});
|
||||||
|
|
||||||
const editDialogVisible = ref(false);
|
const editDialogVisible = ref(false);
|
||||||
const detailDialogVisible = ref(false);
|
const detailDialogVisible = ref(false);
|
||||||
@@ -131,7 +154,7 @@ const fetchRoles = async () => {
|
|||||||
loading.value = true;
|
loading.value = true;
|
||||||
error.value = "";
|
error.value = "";
|
||||||
try {
|
try {
|
||||||
const res = await getAllRoles();
|
const res = await getAllPlatformRoles();
|
||||||
if (res.code === 200) {
|
if (res.code === 200) {
|
||||||
roles.value = res.data || [];
|
roles.value = res.data || [];
|
||||||
// console.log('角色列表:', roles.value);
|
// console.log('角色列表:', roles.value);
|
||||||
@@ -184,7 +207,7 @@ async function handleDelete(row: any) {
|
|||||||
|
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
try {
|
try {
|
||||||
const res = await deleteRole(row.id);
|
const res = await deletePlatformRole(row.id);
|
||||||
if (res.code === 200) {
|
if (res.code === 200) {
|
||||||
ElMessage.success("删除成功");
|
ElMessage.success("删除成功");
|
||||||
fetchRoles();
|
fetchRoles();
|
||||||
|
|||||||
@@ -35,11 +35,17 @@
|
|||||||
</el-tag>
|
</el-tag>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="操作" width="140" align="center" fixed="right">
|
<el-table-column label="操作" width="260" align="center" fixed="right">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-button text type="primary" @click="openPasswordDialog(row)">
|
<el-button text type="primary" @click="openPasswordDialog(row)">修改密码</el-button>
|
||||||
修改密码
|
<el-button
|
||||||
|
text
|
||||||
|
:type="Number(row.status) === 1 ? 'warning' : 'success'"
|
||||||
|
@click="handleToggleStatus(row)"
|
||||||
|
>
|
||||||
|
{{ Number(row.status) === 1 ? "禁用" : "启用" }}
|
||||||
</el-button>
|
</el-button>
|
||||||
|
<el-button text type="danger" @click="handleDelete(row)">删除</el-button>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
@@ -65,9 +71,13 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, reactive, watch } from "vue";
|
import { ref, reactive, watch } from "vue";
|
||||||
import { ElMessage } from "element-plus";
|
import { ElMessage, ElMessageBox } from "element-plus";
|
||||||
import { Plus } from "@element-plus/icons-vue";
|
import { Plus } from "@element-plus/icons-vue";
|
||||||
import { getAllUsers, changePassword } from "@/api/user";
|
import {
|
||||||
|
getTenantUserList,
|
||||||
|
editTenantUser,
|
||||||
|
deleteTenantUser,
|
||||||
|
} from "@/api/tenantUser";
|
||||||
import AddUser from "./adduser.vue";
|
import AddUser from "./adduser.vue";
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
@@ -119,7 +129,7 @@ const refreshTenantUsers = async () => {
|
|||||||
if (id == null) return;
|
if (id == null) return;
|
||||||
usersLoading.value = true;
|
usersLoading.value = true;
|
||||||
try {
|
try {
|
||||||
const usersRes = await getAllUsers(buildTenantUserQuery(id));
|
const usersRes = await getTenantUserList(buildTenantUserQuery(id));
|
||||||
if (usersRes?.code === 200) {
|
if (usersRes?.code === 200) {
|
||||||
tenantUsers.value = usersRes?.data?.list || usersRes?.data || [];
|
tenantUsers.value = usersRes?.data?.list || usersRes?.data || [];
|
||||||
} else {
|
} else {
|
||||||
@@ -135,7 +145,7 @@ const refreshTenantUsers = async () => {
|
|||||||
const loadUsersForTid = async (tid: number) => {
|
const loadUsersForTid = async (tid: number) => {
|
||||||
usersLoading.value = true;
|
usersLoading.value = true;
|
||||||
try {
|
try {
|
||||||
const usersRes = await getAllUsers({ tid });
|
const usersRes = await getTenantUserList({ tid });
|
||||||
if (usersRes?.code === 200) {
|
if (usersRes?.code === 200) {
|
||||||
tenantUsers.value = usersRes?.data?.list || usersRes?.data || [];
|
tenantUsers.value = usersRes?.data?.list || usersRes?.data || [];
|
||||||
} else {
|
} else {
|
||||||
@@ -194,7 +204,9 @@ const submitPassword = async () => {
|
|||||||
|
|
||||||
passwordSubmitting.value = true;
|
passwordSubmitting.value = true;
|
||||||
try {
|
try {
|
||||||
const res = await changePassword(currentTenantUserId.value, { newPassword: passwordForm.password });
|
const res = await editTenantUser(currentTenantUserId.value, {
|
||||||
|
password: passwordForm.password,
|
||||||
|
});
|
||||||
if (res.code === 200) {
|
if (res.code === 200) {
|
||||||
ElMessage.success("密码修改成功");
|
ElMessage.success("密码修改成功");
|
||||||
passwordDialogVisible.value = false;
|
passwordDialogVisible.value = false;
|
||||||
@@ -206,6 +218,52 @@ const submitPassword = async () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleToggleStatus = async (row: any) => {
|
||||||
|
const next = Number(row.status) === 1 ? 0 : 1;
|
||||||
|
const actionText = next === 0 ? "禁用" : "启用";
|
||||||
|
const name = row.name || row.account || `ID ${row.id}`;
|
||||||
|
try {
|
||||||
|
await ElMessageBox.confirm(`确定要${actionText}用户「${name}」吗?`, "提示", {
|
||||||
|
type: "warning",
|
||||||
|
});
|
||||||
|
} catch {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const res = await editTenantUser(row.id, { status: next });
|
||||||
|
if (res.code === 200) {
|
||||||
|
ElMessage.success(`${actionText}成功`);
|
||||||
|
refreshTenantUsers();
|
||||||
|
} else {
|
||||||
|
ElMessage.error(res.msg || `${actionText}失败`);
|
||||||
|
}
|
||||||
|
} catch (e: any) {
|
||||||
|
ElMessage.error(e?.message || `${actionText}失败`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDelete = async (row: any) => {
|
||||||
|
const name = row.name || row.account || `ID ${row.id}`;
|
||||||
|
try {
|
||||||
|
await ElMessageBox.confirm(`确定要删除用户「${name}」吗?删除后不可恢复。`, "警告", {
|
||||||
|
type: "warning",
|
||||||
|
});
|
||||||
|
} catch {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const res = await deleteTenantUser(row.id);
|
||||||
|
if (res.code === 200) {
|
||||||
|
ElMessage.success("删除成功");
|
||||||
|
refreshTenantUsers();
|
||||||
|
} else {
|
||||||
|
ElMessage.error(res.msg || "删除失败");
|
||||||
|
}
|
||||||
|
} catch (e: any) {
|
||||||
|
ElMessage.error(e?.message || "删除失败");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
refreshTenantUsers,
|
refreshTenantUsers,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -43,7 +43,7 @@
|
|||||||
*/
|
*/
|
||||||
import { ref, reactive } from 'vue';
|
import { ref, reactive } from 'vue';
|
||||||
import { ElMessage } from 'element-plus';
|
import { ElMessage } from 'element-plus';
|
||||||
import { addUser } from '@/api/user';
|
import { createTenantUser } from '@/api/tenantUser';
|
||||||
|
|
||||||
const emit = defineEmits(['success']);
|
const emit = defineEmits(['success']);
|
||||||
const visible = ref(false);
|
const visible = ref(false);
|
||||||
@@ -123,7 +123,7 @@ const submitForm = async () => {
|
|||||||
};
|
};
|
||||||
// 提交时不需要把确认密码发给后端
|
// 提交时不需要把确认密码发给后端
|
||||||
delete (submitData as any).password2;
|
delete (submitData as any).password2;
|
||||||
const res = await addUser(submitData);
|
const res = await createTenantUser(submitData);
|
||||||
if (res.code === 200) {
|
if (res.code === 200) {
|
||||||
ElMessage.success('添加成功');
|
ElMessage.success('添加成功');
|
||||||
visible.value = false;
|
visible.value = false;
|
||||||
|
|||||||
@@ -70,6 +70,23 @@
|
|||||||
<el-option label="禁用" :value="0" />
|
<el-option label="禁用" :value="0" />
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
|
<!-- 角色(关联 yz_system_admin_role, cid=2) -->
|
||||||
|
<el-form-item label="角色">
|
||||||
|
<el-select
|
||||||
|
v-model="form.group_id"
|
||||||
|
placeholder="请选择角色(未分配则拥有全部功能权限)"
|
||||||
|
clearable
|
||||||
|
style="width: 100%"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="role in roles"
|
||||||
|
:key="role.id"
|
||||||
|
:label="role.name"
|
||||||
|
:value="role.id"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
|
|
||||||
<!-- 对话框脚部 -->
|
<!-- 对话框脚部 -->
|
||||||
@@ -84,6 +101,7 @@
|
|||||||
import { ref, computed, watch } from "vue";
|
import { ref, computed, watch } from "vue";
|
||||||
import { ElMessage } from "element-plus";
|
import { ElMessage } from "element-plus";
|
||||||
import { addUser, editUser, getUserInfo } from "@/api/user";
|
import { addUser, editUser, getUserInfo } from "@/api/user";
|
||||||
|
import { getAllRoles } from "@/api/role";
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
modelValue: {
|
modelValue: {
|
||||||
@@ -117,8 +135,26 @@ const form = ref<any>({
|
|||||||
confirmPassword: "",
|
confirmPassword: "",
|
||||||
email: "",
|
email: "",
|
||||||
status: 1,
|
status: 1,
|
||||||
|
group_id: undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
interface RoleOption {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
status?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const roles = ref<RoleOption[]>([]);
|
||||||
|
|
||||||
|
const fetchRoles = async () => {
|
||||||
|
try {
|
||||||
|
const res = await getAllRoles();
|
||||||
|
roles.value = (res.data || []).filter((r: RoleOption) => r.status !== 0);
|
||||||
|
} catch (e) {
|
||||||
|
roles.value = [];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const dialogTitle = computed(() => {
|
const dialogTitle = computed(() => {
|
||||||
return isAdd.value ? "添加用户" : "编辑用户";
|
return isAdd.value ? "添加用户" : "编辑用户";
|
||||||
});
|
});
|
||||||
@@ -189,6 +225,9 @@ watch(
|
|||||||
() => props.modelValue,
|
() => props.modelValue,
|
||||||
(newVal) => {
|
(newVal) => {
|
||||||
visible.value = newVal;
|
visible.value = newVal;
|
||||||
|
if (newVal) {
|
||||||
|
fetchRoles();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -240,6 +279,7 @@ const loadUserData = async (user: any) => {
|
|||||||
confirmPassword: "",
|
confirmPassword: "",
|
||||||
email: data.email,
|
email: data.email,
|
||||||
status: statusValue,
|
status: statusValue,
|
||||||
|
group_id: data.group_id ? Number(data.group_id) : undefined,
|
||||||
};
|
};
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error("Failed to load user data:", e);
|
console.error("Failed to load user data:", e);
|
||||||
@@ -299,6 +339,7 @@ const handleSubmit = async () => {
|
|||||||
email: form.value.email,
|
email: form.value.email,
|
||||||
status: form.value.status,
|
status: form.value.status,
|
||||||
password: form.value.password,
|
password: form.value.password,
|
||||||
|
group_id: form.value.group_id,
|
||||||
};
|
};
|
||||||
|
|
||||||
await addUser(submitData);
|
await addUser(submitData);
|
||||||
@@ -319,6 +360,7 @@ const handleSubmit = async () => {
|
|||||||
sex: form.value.sex,
|
sex: form.value.sex,
|
||||||
email: form.value.email,
|
email: form.value.email,
|
||||||
status: form.value.status,
|
status: form.value.status,
|
||||||
|
group_id: form.value.group_id,
|
||||||
};
|
};
|
||||||
|
|
||||||
// 只有在填写了密码时才添加到提交数据中
|
// 只有在填写了密码时才添加到提交数据中
|
||||||
@@ -354,6 +396,7 @@ defineExpose({
|
|||||||
confirmPassword: "",
|
confirmPassword: "",
|
||||||
email: "",
|
email: "",
|
||||||
status: 1,
|
status: 1,
|
||||||
|
group_id: undefined,
|
||||||
};
|
};
|
||||||
visible.value = true;
|
visible.value = true;
|
||||||
// 清除表单验证
|
// 清除表单验证
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ type backendUserInfoDTO struct {
|
|||||||
ID uint64 `json:"id"`
|
ID uint64 `json:"id"`
|
||||||
Tid uint64 `json:"tid"`
|
Tid uint64 `json:"tid"`
|
||||||
Uid uint64 `json:"uid"`
|
Uid uint64 `json:"uid"`
|
||||||
|
GroupID uint64 `json:"group_id"`
|
||||||
Account *string `json:"account"`
|
Account *string `json:"account"`
|
||||||
Name *string `json:"name"`
|
Name *string `json:"name"`
|
||||||
Phone *string `json:"phone"`
|
Phone *string `json:"phone"`
|
||||||
@@ -40,6 +41,7 @@ type backendUserInfoDTO struct {
|
|||||||
type backendTenantUserPayload struct {
|
type backendTenantUserPayload struct {
|
||||||
Tid uint64 `json:"tid"`
|
Tid uint64 `json:"tid"`
|
||||||
Uid uint64 `json:"uid"`
|
Uid uint64 `json:"uid"`
|
||||||
|
GroupID *uint64 `json:"group_id"`
|
||||||
Account *string `json:"account"`
|
Account *string `json:"account"`
|
||||||
Name *string `json:"name"`
|
Name *string `json:"name"`
|
||||||
Phone *string `json:"phone"`
|
Phone *string `json:"phone"`
|
||||||
@@ -95,6 +97,7 @@ func toBackendUserInfoDTO(u models.SystemTenantUser) backendUserInfoDTO {
|
|||||||
ID: u.ID,
|
ID: u.ID,
|
||||||
Tid: u.Tid,
|
Tid: u.Tid,
|
||||||
Uid: u.Uid,
|
Uid: u.Uid,
|
||||||
|
GroupID: u.GroupID,
|
||||||
Account: u.Account,
|
Account: u.Account,
|
||||||
Name: u.Name,
|
Name: u.Name,
|
||||||
Phone: u.Phone,
|
Phone: u.Phone,
|
||||||
@@ -395,6 +398,23 @@ func (c *BackendAdminUserController) AddUser() {
|
|||||||
_ = services.SetDefaultTenant(p.Uid, p.Tid)
|
_ = services.SetDefaultTenant(p.Uid, p.Tid)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 关联角色(group_id):0 表示未分配,前端展示为"未分配/全权限"
|
||||||
|
if p.GroupID != nil && *p.GroupID > 0 {
|
||||||
|
models.EnsureTenantUserGroupColumn()
|
||||||
|
_, _ = models.Orm.QueryTable(new(models.SystemTenantUser)).
|
||||||
|
Filter("id", id).
|
||||||
|
Update(map[string]interface{}{"group_id": *p.GroupID})
|
||||||
|
} else if isDefault == 1 {
|
||||||
|
// 租户第一个账号(普通租户)即租户管理员:自动绑定「租户管理员」角色,
|
||||||
|
// 使"普通租户"与"租户管理员"合二为一(均可登录 backend 并拥有全权限)。
|
||||||
|
if adminRoleID, err := models.GetTenantAdminRole(p.Tid); err == nil && adminRoleID > 0 {
|
||||||
|
models.EnsureTenantUserGroupColumn()
|
||||||
|
_, _ = models.Orm.QueryTable(new(models.SystemTenantUser)).
|
||||||
|
Filter("id", id).
|
||||||
|
Update(map[string]interface{}{"group_id": adminRoleID})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
c.Data["json"] = map[string]interface{}{
|
c.Data["json"] = map[string]interface{}{
|
||||||
"code": 200,
|
"code": 200,
|
||||||
"msg": "success",
|
"msg": "success",
|
||||||
@@ -493,6 +513,10 @@ func (c *BackendAdminUserController) EditUser() {
|
|||||||
update["remark"] = *p.Remark
|
update["remark"] = *p.Remark
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if p.GroupID != nil {
|
||||||
|
update["group_id"] = *p.GroupID
|
||||||
|
}
|
||||||
|
|
||||||
if len(update) == 0 {
|
if len(update) == 0 {
|
||||||
c.Data["json"] = map[string]interface{}{"code": 400, "msg": "无更新字段"}
|
c.Data["json"] = map[string]interface{}{"code": 400, "msg": "无更新字段"}
|
||||||
_ = c.ServeJSON()
|
_ = c.ServeJSON()
|
||||||
|
|||||||
@@ -77,6 +77,9 @@ func (c *BackendMenuController) GetMenu() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *BackendMenuController) GetBackendMenu() {
|
func (c *BackendMenuController) GetBackendMenu() {
|
||||||
|
// 确保租户用户 group_id 列存在(老库兼容)
|
||||||
|
models.EnsureTenantUserGroupColumn()
|
||||||
|
|
||||||
var menus []models.SystemMenu
|
var menus []models.SystemMenu
|
||||||
_, err := models.Orm.QueryTable(new(models.SystemMenu)).Filter("status", 1).All(&menus)
|
_, err := models.Orm.QueryTable(new(models.SystemMenu)).Filter("status", 1).All(&menus)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -84,10 +87,105 @@ func (c *BackendMenuController) GetBackendMenu() {
|
|||||||
_ = c.ServeJSON()
|
_ = c.ServeJSON()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "success", "data": buildMenuTree(filterMenusByView(menus, 2), 0)}
|
backendMenus := filterMenusByView(menus, 2)
|
||||||
|
|
||||||
|
// 按当前登录用户角色过滤可见功能菜单:
|
||||||
|
// 角色 rights 为空(或未分配角色)→ 全权限,返回全部后端菜单;
|
||||||
|
// 否则仅返回角色允许访问的菜单(含其祖先节点,保证树结构完整)。
|
||||||
|
idStr := c.Ctx.Input.Param(":id")
|
||||||
|
uid, _ := strconv.ParseUint(idStr, 10, 64)
|
||||||
|
if uid > 0 {
|
||||||
|
var tu models.SystemTenantUser
|
||||||
|
if e := models.Orm.QueryTable(new(models.SystemTenantUser)).
|
||||||
|
Filter("id", uid).
|
||||||
|
Filter("delete_time__isnull", true).
|
||||||
|
One(&tu); e == nil && tu.GroupID > 0 {
|
||||||
|
var role models.AdminRole
|
||||||
|
if re := models.Orm.QueryTable(new(models.AdminRole)).
|
||||||
|
Filter("id", tu.GroupID).
|
||||||
|
Filter("cid", 2).
|
||||||
|
One(&role); re == nil {
|
||||||
|
backendMenus = filterMenusByRights(backendMenus, role.Rights)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "success", "data": buildMenuTree(backendMenus, 0)}
|
||||||
_ = c.ServeJSON()
|
_ = c.ServeJSON()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// parseRightsToSet 将角色 rights 解析为菜单 ID 集合(兼容 JSON 数组 / 逗号分隔)
|
||||||
|
func parseRightsToSet(raw string) map[uint64]bool {
|
||||||
|
set := map[uint64]bool{}
|
||||||
|
var arr []string
|
||||||
|
if err := json.Unmarshal([]byte(raw), &arr); err == nil {
|
||||||
|
for _, s := range arr {
|
||||||
|
s = strings.TrimSpace(s)
|
||||||
|
if s == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if n, e := strconv.ParseUint(s, 10, 64); e == nil {
|
||||||
|
set[n] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return set
|
||||||
|
}
|
||||||
|
for _, s := range strings.Split(raw, ",") {
|
||||||
|
s = strings.TrimSpace(s)
|
||||||
|
if s == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if n, e := strconv.ParseUint(s, 10, 64); e == nil {
|
||||||
|
set[n] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return set
|
||||||
|
}
|
||||||
|
|
||||||
|
// filterMenusByRights 按角色 rights(菜单 ID 集合)过滤菜单,并补全祖先节点,保证树结构完整。
|
||||||
|
// rights 为空(nil / 空串)→ 原样返回(全权限)。
|
||||||
|
func filterMenusByRights(menus []models.SystemMenu, rights *string) []models.SystemMenu {
|
||||||
|
if rights == nil || strings.TrimSpace(*rights) == "" {
|
||||||
|
return menus
|
||||||
|
}
|
||||||
|
allowed := parseRightsToSet(*rights)
|
||||||
|
if len(allowed) == 0 {
|
||||||
|
return menus
|
||||||
|
}
|
||||||
|
|
||||||
|
pidOf := make(map[uint64]int64, len(menus))
|
||||||
|
byID := make(map[uint64]models.SystemMenu, len(menus))
|
||||||
|
for _, m := range menus {
|
||||||
|
pidOf[m.ID] = m.Pid
|
||||||
|
byID[m.ID] = m
|
||||||
|
}
|
||||||
|
|
||||||
|
final := make(map[uint64]bool, len(allowed))
|
||||||
|
for id := range allowed {
|
||||||
|
cur := id
|
||||||
|
for {
|
||||||
|
if _, ok := byID[cur]; !ok {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if final[cur] {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
final[cur] = true
|
||||||
|
p := pidOf[cur]
|
||||||
|
if p == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
cur = uint64(p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out := make([]models.SystemMenu, 0, len(final))
|
||||||
|
for id := range final {
|
||||||
|
out = append(out, byID[id])
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
func (c *BackendMenuController) GetTenantList() {
|
func (c *BackendMenuController) GetTenantList() {
|
||||||
var tid uint64
|
var tid uint64
|
||||||
if jwtTid := c.Ctx.Input.GetData("tid"); jwtTid != nil {
|
if jwtTid := c.Ctx.Input.GetData("tid"); jwtTid != nil {
|
||||||
|
|||||||
@@ -0,0 +1,249 @@
|
|||||||
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"io"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"server/models"
|
||||||
|
"server/pkg/jwtutil"
|
||||||
|
|
||||||
|
beego "github.com/beego/beego/v2/server/web"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BackendRoleController 租户端(backend)角色管理(yz_system_admin_role, cid=2)
|
||||||
|
// 角色按租户隔离:所有读写均以当前登录租户(JWT 中的 tenant_id)为边界,
|
||||||
|
// 租户仅能查看/管理自己名下的角色,无法越权访问其他租户数据。
|
||||||
|
type BackendRoleController struct {
|
||||||
|
beego.Controller
|
||||||
|
}
|
||||||
|
|
||||||
|
type backendRolePayload struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Status *uint8 `json:"status"`
|
||||||
|
Rights interface{} `json:"rights"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// currentTenantID 从 Bearer Token 解析当前登录租户 ID。
|
||||||
|
// 返回 (tid, true) 表示成功;失败时已写入 401/403 响应并返回 (0, false)。
|
||||||
|
func (c *BackendRoleController) currentTenantID() (uint64, bool) {
|
||||||
|
authHeader := c.Ctx.Request.Header.Get("Authorization")
|
||||||
|
if authHeader == "" {
|
||||||
|
c.Data["json"] = map[string]interface{}{"code": 401, "msg": "未登录"}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
authParts := strings.SplitN(authHeader, " ", 2)
|
||||||
|
if len(authParts) != 2 || authParts[0] != "Bearer" {
|
||||||
|
c.Data["json"] = map[string]interface{}{"code": 401, "msg": "认证信息格式错误"}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
claims, err := jwtutil.ParseToken(authParts[1])
|
||||||
|
if err != nil {
|
||||||
|
c.Data["json"] = map[string]interface{}{"code": 401, "msg": "无效的token"}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
if claims.UserType != "backend" && claims.UserType != "app" {
|
||||||
|
c.Data["json"] = map[string]interface{}{"code": 403, "msg": "无权访问"}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
if claims.TenantId <= 0 {
|
||||||
|
c.Data["json"] = map[string]interface{}{"code": 401, "msg": "租户信息缺失"}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
return uint64(claims.TenantId), true
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAllRoles 获取当前租户的角色列表
|
||||||
|
// GET /backend/allRoles
|
||||||
|
func (c *BackendRoleController) GetAllRoles() {
|
||||||
|
tid, ok := c.currentTenantID()
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 懒加载补齐当前租户的默认角色(租户管理员/部门负责人/员工)
|
||||||
|
models.EnsureDefaultTenantRoles(tid)
|
||||||
|
|
||||||
|
var rows []models.AdminRole
|
||||||
|
_, err := models.Orm.QueryTable(new(models.AdminRole)).
|
||||||
|
Filter("cid", 2).
|
||||||
|
Filter("tenant_id", tid).
|
||||||
|
OrderBy("-id").
|
||||||
|
All(&rows)
|
||||||
|
if err != nil {
|
||||||
|
c.Data["json"] = map[string]interface{}{"code": 500, "msg": "查询失败"}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "success", "data": rows}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRoleByID 获取当前租户的角色详情
|
||||||
|
// GET /backend/roles/:id
|
||||||
|
func (c *BackendRoleController) GetRoleByID() {
|
||||||
|
tid, ok := c.currentTenantID()
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
idStr := c.Ctx.Input.Param(":id")
|
||||||
|
id, _ := strconv.ParseUint(idStr, 10, 64)
|
||||||
|
if id == 0 {
|
||||||
|
c.Data["json"] = map[string]interface{}{"code": 400, "msg": "id 不能为空"}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var role models.AdminRole
|
||||||
|
if err := models.Orm.QueryTable(new(models.AdminRole)).
|
||||||
|
Filter("id", id).
|
||||||
|
Filter("cid", 2).
|
||||||
|
Filter("tenant_id", tid).
|
||||||
|
One(&role); err != nil {
|
||||||
|
c.Data["json"] = map[string]interface{}{"code": 404, "msg": "角色不存在"}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "success", "data": role}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateRole 创建当前租户的角色
|
||||||
|
// POST /backend/roles
|
||||||
|
func (c *BackendRoleController) CreateRole() {
|
||||||
|
tid, ok := c.currentTenantID()
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var p backendRolePayload
|
||||||
|
raw, _ := io.ReadAll(c.Ctx.Request.Body)
|
||||||
|
if err := json.Unmarshal(raw, &p); err != nil {
|
||||||
|
c.Data["json"] = map[string]interface{}{"code": 400, "msg": "参数错误"}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
p.Name = strings.TrimSpace(p.Name)
|
||||||
|
if p.Name == "" {
|
||||||
|
c.Data["json"] = map[string]interface{}{"code": 400, "msg": "name 不能为空"}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
status := uint8(1)
|
||||||
|
if p.Status != nil {
|
||||||
|
status = *p.Status
|
||||||
|
}
|
||||||
|
rights := normalizeRights(p.Rights)
|
||||||
|
role := &models.AdminRole{
|
||||||
|
TenantID: tid,
|
||||||
|
Cid: 2,
|
||||||
|
Name: p.Name,
|
||||||
|
Status: status,
|
||||||
|
Rights: rights,
|
||||||
|
}
|
||||||
|
id, err := models.Orm.Insert(role)
|
||||||
|
if err != nil {
|
||||||
|
c.Data["json"] = map[string]interface{}{"code": 500, "msg": "创建失败"}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "success", "data": map[string]interface{}{"id": id}}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateRole 更新当前租户的角色
|
||||||
|
// PUT /backend/roles/:id
|
||||||
|
func (c *BackendRoleController) UpdateRole() {
|
||||||
|
tid, ok := c.currentTenantID()
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
idStr := c.Ctx.Input.Param(":id")
|
||||||
|
id, _ := strconv.ParseUint(idStr, 10, 64)
|
||||||
|
if id == 0 {
|
||||||
|
c.Data["json"] = map[string]interface{}{"code": 400, "msg": "id 不能为空"}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var p backendRolePayload
|
||||||
|
raw, _ := io.ReadAll(c.Ctx.Request.Body)
|
||||||
|
if err := json.Unmarshal(raw, &p); err != nil {
|
||||||
|
c.Data["json"] = map[string]interface{}{"code": 400, "msg": "参数错误"}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
update := map[string]interface{}{}
|
||||||
|
if strings.TrimSpace(p.Name) != "" {
|
||||||
|
update["name"] = strings.TrimSpace(p.Name)
|
||||||
|
}
|
||||||
|
if p.Status != nil {
|
||||||
|
update["status"] = *p.Status
|
||||||
|
}
|
||||||
|
if p.Rights != nil {
|
||||||
|
update["rights"] = normalizeRights(p.Rights)
|
||||||
|
}
|
||||||
|
if len(update) == 0 {
|
||||||
|
c.Data["json"] = map[string]interface{}{"code": 400, "msg": "无更新字段"}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 仅允许更新本租户名下的角色,防止越权改到其他租户
|
||||||
|
cnt, err := models.Orm.QueryTable(new(models.AdminRole)).
|
||||||
|
Filter("id", id).
|
||||||
|
Filter("cid", 2).
|
||||||
|
Filter("tenant_id", tid).
|
||||||
|
Update(update)
|
||||||
|
if err != nil {
|
||||||
|
c.Data["json"] = map[string]interface{}{"code": 500, "msg": "更新失败"}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if cnt == 0 {
|
||||||
|
c.Data["json"] = map[string]interface{}{"code": 404, "msg": "角色不存在"}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "success"}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteRole 删除当前租户的角色
|
||||||
|
// DELETE /backend/roles/:id
|
||||||
|
func (c *BackendRoleController) DeleteRole() {
|
||||||
|
tid, ok := c.currentTenantID()
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
idStr := c.Ctx.Input.Param(":id")
|
||||||
|
id, _ := strconv.ParseUint(idStr, 10, 64)
|
||||||
|
if id == 0 {
|
||||||
|
c.Data["json"] = map[string]interface{}{"code": 400, "msg": "id 不能为空"}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 仅允许删除本租户名下的角色
|
||||||
|
cnt, err := models.Orm.QueryTable(new(models.AdminRole)).
|
||||||
|
Filter("id", id).
|
||||||
|
Filter("cid", 2).
|
||||||
|
Filter("tenant_id", tid).
|
||||||
|
Delete()
|
||||||
|
if err != nil {
|
||||||
|
c.Data["json"] = map[string]interface{}{"code": 500, "msg": "删除失败"}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if cnt == 0 {
|
||||||
|
c.Data["json"] = map[string]interface{}{"code": 404, "msg": "角色不存在"}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "success"}
|
||||||
|
_ = c.ServeJSON()
|
||||||
|
}
|
||||||
@@ -202,6 +202,14 @@ func (c *PlatformTenantUserController) CreateTenantUser() {
|
|||||||
}
|
}
|
||||||
if isDefault == 1 {
|
if isDefault == 1 {
|
||||||
_ = services.SetDefaultTenant(p.Uid, p.Tid)
|
_ = services.SetDefaultTenant(p.Uid, p.Tid)
|
||||||
|
// 租户第一个账号(普通租户)即租户管理员:自动绑定「租户管理员」角色,
|
||||||
|
// 使"普通租户"与"租户管理员"合二为一(均可登录 backend 并拥有全权限)。
|
||||||
|
if adminRoleID, err := models.GetTenantAdminRole(p.Tid); err == nil && adminRoleID > 0 {
|
||||||
|
models.EnsureTenantUserGroupColumn()
|
||||||
|
_, _ = models.Orm.QueryTable(new(models.SystemTenantUser)).
|
||||||
|
Filter("id", id).
|
||||||
|
Update(map[string]interface{}{"group_id": adminRoleID})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "success", "data": map[string]interface{}{"id": id}}
|
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "success", "data": map[string]interface{}{"id": id}}
|
||||||
|
|||||||
@@ -5,7 +5,8 @@ import "time"
|
|||||||
// AdminRole 平台角色表 yz_system_admin_role
|
// AdminRole 平台角色表 yz_system_admin_role
|
||||||
type AdminRole struct {
|
type AdminRole struct {
|
||||||
ID uint64 `orm:"column(id);pk;auto" json:"id"`
|
ID uint64 `orm:"column(id);pk;auto" json:"id"`
|
||||||
Cid uint8 `orm:"column(cid);default(1)" json:"cid"` // 1平台角色 2租户角色
|
TenantID uint64 `orm:"column(tenant_id);default(0)" json:"tenant_id"` // 租户ID:0=平台/全局,>0=具体租户(仅 cid=2 租户角色使用)
|
||||||
|
Cid uint8 `orm:"column(cid);default(1)" json:"cid"` // 1平台角色 2租户角色
|
||||||
Name string `orm:"column(name);size(32)" json:"name"`
|
Name string `orm:"column(name);size(32)" json:"name"`
|
||||||
Status uint8 `orm:"column(status);default(1)" json:"status"`
|
Status uint8 `orm:"column(status);default(1)" json:"status"`
|
||||||
Rights *string `orm:"column(rights);type(text);null" json:"rights"`
|
Rights *string `orm:"column(rights);type(text);null" json:"rights"`
|
||||||
|
|||||||
@@ -0,0 +1,80 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/beego/beego/v2/client/orm"
|
||||||
|
)
|
||||||
|
|
||||||
|
var adminRoleTenantColOnce sync.Once
|
||||||
|
|
||||||
|
// EnsureAdminRoleTenantColumn 为角色表补齐 tenant_id 列(历史库可能早于租户隔离特性建表)。
|
||||||
|
// 字段已存在时 MySQL 报 duplicate column,统一忽略,不阻断主流程。
|
||||||
|
func EnsureAdminRoleTenantColumn() {
|
||||||
|
if Orm == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
adminRoleTenantColOnce.Do(func() {
|
||||||
|
_, _ = Orm.Raw(`
|
||||||
|
ALTER TABLE yz_system_admin_role
|
||||||
|
ADD COLUMN tenant_id bigint unsigned NOT NULL DEFAULT 0 COMMENT '租户ID 0-平台/全局 >0-具体租户'
|
||||||
|
`).Exec()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnsureDefaultTenantRoles 保证指定租户(cid=2)的默认角色齐全:
|
||||||
|
// - 租户管理员:全权限(rights 为空,见 permission.go 空权限即放行)
|
||||||
|
// - 部门负责人
|
||||||
|
// - 员工
|
||||||
|
//
|
||||||
|
// 默认角色按租户隔离:每个租户首次访问角色列表时补建自己的一套,互不影响。
|
||||||
|
// 缺失的自动插入;已存在(同名同 cid 同租户)的不动。任何失败均静默忽略不阻断主流程。
|
||||||
|
func EnsureDefaultTenantRoles(tid uint64) {
|
||||||
|
if Orm == nil || tid == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 确保 tenant_id 列存在(兼容老表)
|
||||||
|
EnsureAdminRoleTenantColumn()
|
||||||
|
|
||||||
|
defaults := []string{"租户管理员", "部门负责人", "员工"}
|
||||||
|
for _, name := range defaults {
|
||||||
|
cnt, err := Orm.QueryTable(new(AdminRole)).
|
||||||
|
Filter("cid", 2).
|
||||||
|
Filter("tenant_id", tid).
|
||||||
|
Filter("name", name).
|
||||||
|
Count()
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if cnt > 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
role := &AdminRole{
|
||||||
|
TenantID: tid,
|
||||||
|
Cid: 2,
|
||||||
|
Name: name,
|
||||||
|
Status: 1,
|
||||||
|
Rights: nil, // 空权限 = 全权限(permission.go)
|
||||||
|
}
|
||||||
|
_, _ = Orm.Insert(role)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTenantAdminRole 返回指定租户(cid=2)的「租户管理员」角色 ID。
|
||||||
|
// 角色不存在时先调用 EnsureDefaultTenantRoles 补建再查;任何失败返回 (0, err)。
|
||||||
|
// 普通租户(租户第一个账号)与租户管理员实为同一概念,均绑定此角色以获得 backend 全权限。
|
||||||
|
func GetTenantAdminRole(tid uint64) (uint64, error) {
|
||||||
|
if Orm == nil || tid == 0 {
|
||||||
|
return 0, orm.ErrNoRows
|
||||||
|
}
|
||||||
|
EnsureDefaultTenantRoles(tid)
|
||||||
|
var role AdminRole
|
||||||
|
if err := Orm.QueryTable(new(AdminRole)).
|
||||||
|
Filter("cid", 2).
|
||||||
|
Filter("tenant_id", tid).
|
||||||
|
Filter("name", "租户管理员").
|
||||||
|
One(&role); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return role.ID, nil
|
||||||
|
}
|
||||||
@@ -1,11 +1,15 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
import "time"
|
import (
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
type SystemTenantUser struct {
|
type SystemTenantUser struct {
|
||||||
ID uint64 `orm:"column(id);pk;auto" json:"id"`
|
ID uint64 `orm:"column(id);pk;auto" json:"id"`
|
||||||
Tid uint64 `orm:"column(tid)" json:"tid"`
|
Tid uint64 `orm:"column(tid)" json:"tid"`
|
||||||
Uid uint64 `orm:"column(uid)" json:"uid"`
|
Uid uint64 `orm:"column(uid)" json:"uid"`
|
||||||
|
GroupID uint64 `orm:"column(group_id);default(0)" json:"group_id"` // 角色ID:关联 yz_system_admin_role(id, cid=2);0=未分配(视为全权限)
|
||||||
Account *string `orm:"column(account);size(64);null" json:"account"`
|
Account *string `orm:"column(account);size(64);null" json:"account"`
|
||||||
Name *string `orm:"column(name);size(64);null" json:"name"`
|
Name *string `orm:"column(name);size(64);null" json:"name"`
|
||||||
Phone *string `orm:"column(phone);size(20);null" json:"phone"`
|
Phone *string `orm:"column(phone);size(20);null" json:"phone"`
|
||||||
@@ -24,3 +28,19 @@ type SystemTenantUser struct {
|
|||||||
func (m *SystemTenantUser) TableName() string {
|
func (m *SystemTenantUser) TableName() string {
|
||||||
return "yz_system_tenant_user"
|
return "yz_system_tenant_user"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var tenantUserGroupColOnce sync.Once
|
||||||
|
|
||||||
|
// EnsureTenantUserGroupColumn 为租户用户表补齐 group_id(角色)列。
|
||||||
|
// 历史库可能早于"用户-角色"特性建表,字段已存在时 MySQL 报 duplicate column,统一忽略。
|
||||||
|
func EnsureTenantUserGroupColumn() {
|
||||||
|
if Orm == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tenantUserGroupColOnce.Do(func() {
|
||||||
|
_, _ = Orm.Raw(`
|
||||||
|
ALTER TABLE yz_system_tenant_user
|
||||||
|
ADD COLUMN group_id bigint unsigned NOT NULL DEFAULT 0 COMMENT '角色ID 关联 yz_system_admin_role(cid=2)'
|
||||||
|
`).Exec()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -10,6 +10,17 @@ import (
|
|||||||
// 该端不包含平台菜单配置接口。
|
// 该端不包含平台菜单配置接口。
|
||||||
func Register() {
|
func Register() {
|
||||||
RegisterAuthRoutes()
|
RegisterAuthRoutes()
|
||||||
|
RegisterRoleRoutes()
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterRoleRoutes 注册 backend 租户角色管理路由。
|
||||||
|
func RegisterRoleRoutes() {
|
||||||
|
// 租户角色管理(yz_system_admin_role, cid=2)
|
||||||
|
beego.Router("/backend/allRoles", &controllers.BackendRoleController{}, "get:GetAllRoles")
|
||||||
|
beego.Router("/backend/roles/:id", &controllers.BackendRoleController{}, "get:GetRoleByID")
|
||||||
|
beego.Router("/backend/roles", &controllers.BackendRoleController{}, "post:CreateRole")
|
||||||
|
beego.Router("/backend/roles/:id", &controllers.BackendRoleController{}, "put:UpdateRole")
|
||||||
|
beego.Router("/backend/roles/:id", &controllers.BackendRoleController{}, "delete:DeleteRole")
|
||||||
}
|
}
|
||||||
|
|
||||||
// RegisterAuthRoutes 注册 backend 认证相关路由。
|
// RegisterAuthRoutes 注册 backend 认证相关路由。
|
||||||
@@ -33,6 +44,7 @@ func RegisterAuthRoutes() {
|
|||||||
beego.Router("/backend/getCurrentUser", &controllers.BackendAuthController{}, "get:GetCurrentUser")
|
beego.Router("/backend/getCurrentUser", &controllers.BackendAuthController{}, "get:GetCurrentUser")
|
||||||
|
|
||||||
// 菜单接口
|
// 菜单接口
|
||||||
|
beego.Router("/backend/allmenu", &controllers.BackendMenuController{}, "get:GetAllMenus")
|
||||||
beego.Router("/backend/menu/:id", &controllers.BackendMenuController{}, "get:GetBackendMenu")
|
beego.Router("/backend/menu/:id", &controllers.BackendMenuController{}, "get:GetBackendMenu")
|
||||||
// 前端菜单接口
|
// 前端菜单接口
|
||||||
beego.Router("/backend/frontmenus", &controllers.BackendMenuFrontController{}, "get:List;post:Create")
|
beego.Router("/backend/frontmenus", &controllers.BackendMenuFrontController{}, "get:List;post:Create")
|
||||||
|
|||||||
@@ -8,6 +8,10 @@ import (
|
|||||||
|
|
||||||
// BindTenantUser 绑定用户到租户(若已存在则更新状态/默认值)
|
// BindTenantUser 绑定用户到租户(若已存在则更新状态/默认值)
|
||||||
func BindTenantUser(tid, uid uint64, account, name, phone, email *string, sex *uint8, birth *string, password *string, isDefault, status int8, remark *string) (uint64, error) {
|
func BindTenantUser(tid, uid uint64, account, name, phone, email *string, sex *uint8, birth *string, password *string, isDefault, status int8, remark *string) (uint64, error) {
|
||||||
|
// 确保 group_id(角色)列存在,避免老表插入时报 Unknown column。
|
||||||
|
// INSERT 会带上该列,列不存在会直接失败,故必须在写入前补齐。
|
||||||
|
models.EnsureTenantUserGroupColumn()
|
||||||
|
|
||||||
var existed models.SystemTenantUser
|
var existed models.SystemTenantUser
|
||||||
err := models.Orm.QueryTable(new(models.SystemTenantUser)).
|
err := models.Orm.QueryTable(new(models.SystemTenantUser)).
|
||||||
Filter("tid", tid).
|
Filter("tid", tid).
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ export function createTenantUser(data) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 修改租户用户绑定信息(用于修改密码等)
|
// 修改租户用户绑定信息(用于修改密码、启用/禁用等)
|
||||||
export function editTenantUser(id, data) {
|
export function editTenantUser(id, data) {
|
||||||
return request({
|
return request({
|
||||||
url: `/platform/tenantUser/edit/${id}`,
|
url: `/platform/tenantUser/edit/${id}`,
|
||||||
@@ -27,3 +27,11 @@ export function editTenantUser(id, data) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 删除租户用户绑定
|
||||||
|
export function deleteTenantUser(id) {
|
||||||
|
return request({
|
||||||
|
url: `/platform/tenantUser/delete/${id}`,
|
||||||
|
method: "delete",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -35,11 +35,19 @@
|
|||||||
</el-tag>
|
</el-tag>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="操作" width="140" align="center" fixed="right">
|
<el-table-column label="操作" width="260" align="center" fixed="right">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-button text type="primary" @click="openPasswordDialog(row)">
|
<el-button text type="primary" @click="openPasswordDialog(row)">
|
||||||
修改密码
|
修改密码
|
||||||
</el-button>
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
text
|
||||||
|
:type="Number(row.status) === 1 ? 'warning' : 'success'"
|
||||||
|
@click="handleToggleStatus(row)"
|
||||||
|
>
|
||||||
|
{{ Number(row.status) === 1 ? "禁用" : "启用" }}
|
||||||
|
</el-button>
|
||||||
|
<el-button text type="danger" @click="handleDelete(row)">删除</el-button>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
@@ -65,9 +73,9 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, reactive, watch } from "vue";
|
import { ref, reactive, watch } from "vue";
|
||||||
import { ElMessage } from "element-plus";
|
import { ElMessage, ElMessageBox } from "element-plus";
|
||||||
import { Plus } from "@element-plus/icons-vue";
|
import { Plus } from "@element-plus/icons-vue";
|
||||||
import { getTenantUserList, editTenantUser } from "@/api/tenantUser";
|
import { getTenantUserList, editTenantUser, deleteTenantUser } from "@/api/tenantUser";
|
||||||
import AddUser from "./adduser.vue";
|
import AddUser from "./adduser.vue";
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
@@ -206,6 +214,54 @@ const submitPassword = async () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleToggleStatus = async (row: any) => {
|
||||||
|
const next = Number(row.status) === 1 ? 0 : 1;
|
||||||
|
const actionText = next === 0 ? "禁用" : "启用";
|
||||||
|
const name = row.name || row.account || `ID ${row.id}`;
|
||||||
|
try {
|
||||||
|
await ElMessageBox.confirm(`确定要${actionText}用户「${name}」吗?`, "提示", {
|
||||||
|
type: "warning",
|
||||||
|
});
|
||||||
|
} catch {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const res = await editTenantUser(row.id, { status: next });
|
||||||
|
if (res.code === 200) {
|
||||||
|
ElMessage.success(`${actionText}成功`);
|
||||||
|
refreshTenantUsers();
|
||||||
|
} else {
|
||||||
|
ElMessage.error(res.msg || `${actionText}失败`);
|
||||||
|
}
|
||||||
|
} catch (e: any) {
|
||||||
|
ElMessage.error(e?.message || `${actionText}失败`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDelete = async (row: any) => {
|
||||||
|
const name = row.name || row.account || `ID ${row.id}`;
|
||||||
|
try {
|
||||||
|
await ElMessageBox.confirm(
|
||||||
|
`确定要删除用户「${name}」吗?删除后不可恢复。`,
|
||||||
|
"警告",
|
||||||
|
{ type: "warning" }
|
||||||
|
);
|
||||||
|
} catch {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const res = await deleteTenantUser(row.id);
|
||||||
|
if (res.code === 200) {
|
||||||
|
ElMessage.success("删除成功");
|
||||||
|
refreshTenantUsers();
|
||||||
|
} else {
|
||||||
|
ElMessage.error(res.msg || "删除失败");
|
||||||
|
}
|
||||||
|
} catch (e: any) {
|
||||||
|
ElMessage.error(e?.message || "删除失败");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
refreshTenantUsers,
|
refreshTenantUsers,
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user