优化平台端租户端
This commit is contained in:
@@ -1,10 +1,12 @@
|
||||
import request from "@/utils/request";
|
||||
|
||||
// 获取所有菜单
|
||||
export function getAllMenus() {
|
||||
// params.cid=2 时仅返回租户端(backend)菜单,用于角色权限编辑
|
||||
export function getAllMenus(params = {}) {
|
||||
return request({
|
||||
url: `/backend/allmenu`,
|
||||
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 ? "启用" : "禁用" }}
|
||||
</el-tag>
|
||||
</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="权限列表">
|
||||
<div v-if="menuNames.length > 0" style="max-height: 300px; overflow-y: auto;">
|
||||
<el-tag
|
||||
@@ -44,7 +49,7 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, watch, computed } from "vue";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { getRoleById } from "@/api/role";
|
||||
import { getPlatformRoleById } from "@/api/platformRole";
|
||||
import { getAllMenus } from "@/api/menu";
|
||||
|
||||
interface Props {
|
||||
@@ -67,11 +72,11 @@ const allMenus = ref<any[]>([]);
|
||||
// 监听 modelValue
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(val) => {
|
||||
async (val) => {
|
||||
visible.value = val;
|
||||
if (val && props.roleId) {
|
||||
loadRoleDetail();
|
||||
loadMenus();
|
||||
await loadRoleDetail();
|
||||
await loadMenus(roleDetail.value.cid);
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -127,7 +132,7 @@ const loadRoleDetail = async () => {
|
||||
|
||||
loading.value = true;
|
||||
try {
|
||||
const res = await getRoleById(props.roleId);
|
||||
const res = await getPlatformRoleById(props.roleId);
|
||||
if (res.code === 200) {
|
||||
roleDetail.value = res.data || {};
|
||||
} else {
|
||||
@@ -141,10 +146,12 @@ const loadRoleDetail = async () => {
|
||||
}
|
||||
};
|
||||
|
||||
// 加载菜单树
|
||||
const loadMenus = async () => {
|
||||
// 加载菜单树:租户角色(cid=2)只展示租户端菜单;平台角色展示全部权限
|
||||
const loadMenus = async (cid?: number) => {
|
||||
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) {
|
||||
allMenus.value = res.data || [];
|
||||
}
|
||||
|
||||
@@ -12,6 +12,14 @@
|
||||
</el-radio-group>
|
||||
</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">
|
||||
<div style="margin-bottom: 10px;">
|
||||
<el-button size="small" @click="toggleSelectAll">
|
||||
@@ -38,7 +46,7 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, watch, nextTick } from "vue";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { createRole, updateRole } from "@/api/role";
|
||||
import { createPlatformRole, updatePlatformRole } from "@/api/platformRole";
|
||||
import { getAllMenus } from "@/api/menu";
|
||||
|
||||
interface Props {
|
||||
@@ -66,6 +74,7 @@ const isExpandAll = ref(false);
|
||||
const form = ref({
|
||||
name: "",
|
||||
status: 1,
|
||||
cid: 2,
|
||||
rights: [] as number[],
|
||||
});
|
||||
|
||||
@@ -140,9 +149,11 @@ const toggleSelectAll = () => {
|
||||
// --- 数据加载与监听 ---
|
||||
|
||||
// 加载菜单树数据
|
||||
// 租户角色(cid=2)只展示租户端菜单;平台角色(cid=1)展示全部权限
|
||||
const loadMenus = async () => {
|
||||
try {
|
||||
const res = await getAllMenus();
|
||||
const params = form.value.cid === 2 ? { cid: 2 } : {};
|
||||
const res = await getAllMenus(params);
|
||||
if (res.code === 200) {
|
||||
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[] => {
|
||||
if (!rights) return [];
|
||||
@@ -182,6 +205,7 @@ watch(
|
||||
form.value = {
|
||||
name: props.role.name,
|
||||
status: props.role.status,
|
||||
cid: props.role.cid === 1 ? 1 : 2,
|
||||
rights: parseRights(props.role.rights),
|
||||
};
|
||||
// 设置树的回显
|
||||
@@ -210,6 +234,7 @@ const resetForm = () => {
|
||||
form.value = {
|
||||
name: "",
|
||||
status: 1,
|
||||
cid: 2,
|
||||
rights: [],
|
||||
};
|
||||
if (formRef.value) formRef.value.clearValidate();
|
||||
@@ -233,6 +258,7 @@ const handleSubmit = async () => {
|
||||
const submitData: any = {
|
||||
name: form.value.name,
|
||||
status: form.value.status,
|
||||
cid: form.value.cid,
|
||||
rights: checkedKeys,
|
||||
};
|
||||
|
||||
@@ -245,9 +271,9 @@ const handleSubmit = async () => {
|
||||
|
||||
let res;
|
||||
if (isEdit.value && props.role) {
|
||||
res = await updateRole(props.role.id, submitData);
|
||||
res = await updatePlatformRole(props.role.id, submitData);
|
||||
} else {
|
||||
res = await createRole(submitData);
|
||||
res = await createPlatformRole(submitData);
|
||||
}
|
||||
|
||||
if (res.code === 200) {
|
||||
@@ -271,4 +297,11 @@ const handleSubmit = async () => {
|
||||
:deep(.el-tree) {
|
||||
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>
|
||||
|
||||
@@ -15,6 +15,15 @@
|
||||
</div>
|
||||
<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">
|
||||
<el-alert title="加载失败" :message="error" type="error" show-icon />
|
||||
@@ -23,14 +32,20 @@
|
||||
|
||||
<!-- 角色列表 -->
|
||||
<div v-else>
|
||||
<el-table :data="roles" stripe style="width: 100%" v-loading="loading">
|
||||
<el-table-column prop="id" label="ID" width="80" align="center" />
|
||||
<el-table :data="displayRoles" stripe style="width: 100%" v-loading="loading">
|
||||
<el-table-column
|
||||
prop="name"
|
||||
label="角色名称"
|
||||
min-width="150"
|
||||
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">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="row.status === 1 ? 'success' : 'info'" size="small">
|
||||
@@ -38,12 +53,12 @@
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="rights"
|
||||
label="权限"
|
||||
min-width="150"
|
||||
align="center"
|
||||
/>
|
||||
<el-table-column label="权限" min-width="150" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag v-if="!row.rights" type="success" size="small">全权限</el-tag>
|
||||
<el-tag v-else type="info" size="small">自定义权限</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="220" fixed="right" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-button
|
||||
@@ -96,10 +111,13 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from "vue";
|
||||
import { ref, computed, onMounted } from "vue";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
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 RoleDetailDialog from "./components/detail.vue";
|
||||
import { useAuthStore } from "@/stores/auth";
|
||||
@@ -110,6 +128,11 @@ const authStore = useAuthStore();
|
||||
const roles = ref<any[]>([]);
|
||||
const loading = ref(false);
|
||||
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 detailDialogVisible = ref(false);
|
||||
@@ -131,7 +154,7 @@ const fetchRoles = async () => {
|
||||
loading.value = true;
|
||||
error.value = "";
|
||||
try {
|
||||
const res = await getAllRoles();
|
||||
const res = await getAllPlatformRoles();
|
||||
if (res.code === 200) {
|
||||
roles.value = res.data || [];
|
||||
// console.log('角色列表:', roles.value);
|
||||
@@ -184,7 +207,7 @@ async function handleDelete(row: any) {
|
||||
|
||||
loading.value = true;
|
||||
try {
|
||||
const res = await deleteRole(row.id);
|
||||
const res = await deletePlatformRole(row.id);
|
||||
if (res.code === 200) {
|
||||
ElMessage.success("删除成功");
|
||||
fetchRoles();
|
||||
|
||||
@@ -35,11 +35,17 @@
|
||||
</el-tag>
|
||||
</template>
|
||||
</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 }">
|
||||
<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 text type="danger" @click="handleDelete(row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
@@ -65,9 +71,13 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
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 { getAllUsers, changePassword } from "@/api/user";
|
||||
import {
|
||||
getTenantUserList,
|
||||
editTenantUser,
|
||||
deleteTenantUser,
|
||||
} from "@/api/tenantUser";
|
||||
import AddUser from "./adduser.vue";
|
||||
|
||||
const props = defineProps<{
|
||||
@@ -119,7 +129,7 @@ const refreshTenantUsers = async () => {
|
||||
if (id == null) return;
|
||||
usersLoading.value = true;
|
||||
try {
|
||||
const usersRes = await getAllUsers(buildTenantUserQuery(id));
|
||||
const usersRes = await getTenantUserList(buildTenantUserQuery(id));
|
||||
if (usersRes?.code === 200) {
|
||||
tenantUsers.value = usersRes?.data?.list || usersRes?.data || [];
|
||||
} else {
|
||||
@@ -135,7 +145,7 @@ const refreshTenantUsers = async () => {
|
||||
const loadUsersForTid = async (tid: number) => {
|
||||
usersLoading.value = true;
|
||||
try {
|
||||
const usersRes = await getAllUsers({ tid });
|
||||
const usersRes = await getTenantUserList({ tid });
|
||||
if (usersRes?.code === 200) {
|
||||
tenantUsers.value = usersRes?.data?.list || usersRes?.data || [];
|
||||
} else {
|
||||
@@ -194,7 +204,9 @@ const submitPassword = async () => {
|
||||
|
||||
passwordSubmitting.value = true;
|
||||
try {
|
||||
const res = await changePassword(currentTenantUserId.value, { newPassword: passwordForm.password });
|
||||
const res = await editTenantUser(currentTenantUserId.value, {
|
||||
password: passwordForm.password,
|
||||
});
|
||||
if (res.code === 200) {
|
||||
ElMessage.success("密码修改成功");
|
||||
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({
|
||||
refreshTenantUsers,
|
||||
});
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
*/
|
||||
import { ref, reactive } from 'vue';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { addUser } from '@/api/user';
|
||||
import { createTenantUser } from '@/api/tenantUser';
|
||||
|
||||
const emit = defineEmits(['success']);
|
||||
const visible = ref(false);
|
||||
@@ -123,7 +123,7 @@ const submitForm = async () => {
|
||||
};
|
||||
// 提交时不需要把确认密码发给后端
|
||||
delete (submitData as any).password2;
|
||||
const res = await addUser(submitData);
|
||||
const res = await createTenantUser(submitData);
|
||||
if (res.code === 200) {
|
||||
ElMessage.success('添加成功');
|
||||
visible.value = false;
|
||||
|
||||
@@ -70,6 +70,23 @@
|
||||
<el-option label="禁用" :value="0" />
|
||||
</el-select>
|
||||
</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>
|
||||
|
||||
<!-- 对话框脚部 -->
|
||||
@@ -84,6 +101,7 @@
|
||||
import { ref, computed, watch } from "vue";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { addUser, editUser, getUserInfo } from "@/api/user";
|
||||
import { getAllRoles } from "@/api/role";
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
@@ -117,8 +135,26 @@ const form = ref<any>({
|
||||
confirmPassword: "",
|
||||
email: "",
|
||||
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(() => {
|
||||
return isAdd.value ? "添加用户" : "编辑用户";
|
||||
});
|
||||
@@ -189,6 +225,9 @@ watch(
|
||||
() => props.modelValue,
|
||||
(newVal) => {
|
||||
visible.value = newVal;
|
||||
if (newVal) {
|
||||
fetchRoles();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
@@ -240,6 +279,7 @@ const loadUserData = async (user: any) => {
|
||||
confirmPassword: "",
|
||||
email: data.email,
|
||||
status: statusValue,
|
||||
group_id: data.group_id ? Number(data.group_id) : undefined,
|
||||
};
|
||||
} catch (e: any) {
|
||||
console.error("Failed to load user data:", e);
|
||||
@@ -299,6 +339,7 @@ const handleSubmit = async () => {
|
||||
email: form.value.email,
|
||||
status: form.value.status,
|
||||
password: form.value.password,
|
||||
group_id: form.value.group_id,
|
||||
};
|
||||
|
||||
await addUser(submitData);
|
||||
@@ -319,6 +360,7 @@ const handleSubmit = async () => {
|
||||
sex: form.value.sex,
|
||||
email: form.value.email,
|
||||
status: form.value.status,
|
||||
group_id: form.value.group_id,
|
||||
};
|
||||
|
||||
// 只有在填写了密码时才添加到提交数据中
|
||||
@@ -354,6 +396,7 @@ defineExpose({
|
||||
confirmPassword: "",
|
||||
email: "",
|
||||
status: 1,
|
||||
group_id: undefined,
|
||||
};
|
||||
visible.value = true;
|
||||
// 清除表单验证
|
||||
|
||||
Reference in New Issue
Block a user