修改角色bug
This commit is contained in:
@@ -60,6 +60,7 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed } from "vue";
|
||||
import { Message } from "view-ui-plus";
|
||||
import { useDefaultStore } from "@/store";
|
||||
import useCurrentInstance from "@/utils/hook/useCurrentInstance";
|
||||
import MyConfirm from "@/components/MyConfirm";
|
||||
import TablePageAlovaNew from '@/components/TablePageAlovaNew.vue';
|
||||
@@ -149,26 +150,28 @@ const tableApi = (params: { pageIndex: number; pageSize: number }) => {
|
||||
const tableHeight = ref($utils.useTableHeight({ isOperat: false, isPage: true, isSearch: true }))
|
||||
|
||||
/* ================= 用户身份(roles[])展示 ================= */
|
||||
const userTypeMap: Record<number, string> = {
|
||||
1: '管理员', 2: '老师', 3: '校领导', 4: '局领导',
|
||||
5: '学生', 6: '家长', 7: '工作号',
|
||||
};
|
||||
const roleMap: Record<number, string> = {
|
||||
1: '超级管理员', 2: '学校管理员', 3: '普通用户',
|
||||
// 唯一真源:后端 SysRoleEnum(经 GetSysDict.sysrole 下发),杜绝前端硬编码漂移。
|
||||
// roles 中仅存放 SysRoleEnum 的 key(1=老师 … 7=管理员),不再混用 UserTypeEnum。
|
||||
const SYS_ROLE_MAP_FALLBACK: Record<number, string> = {
|
||||
1: '老师', 2: '校领导', 3: '学生', 4: '家长',
|
||||
5: '局领导', 6: '工作号', 7: '管理员',
|
||||
};
|
||||
const store = useDefaultStore();
|
||||
const userTypeMap = computed<Record<number, string>>(() => {
|
||||
const sysrole = (store.getSysDict as any)?.sysrole;
|
||||
if (Array.isArray(sysrole) && sysrole.length) {
|
||||
const m: Record<number, string> = {};
|
||||
sysrole.forEach((it: any) => { m[Number(it.k)] = it.v; });
|
||||
return m;
|
||||
}
|
||||
return SYS_ROLE_MAP_FALLBACK;
|
||||
});
|
||||
function describeRoles(roles: any): string {
|
||||
if (!Array.isArray(roles) || roles.length === 0) return '-';
|
||||
const userTypes: string[] = [];
|
||||
const specificRoles: string[] = [];
|
||||
roles.forEach((r: any) => {
|
||||
const n = Number(r);
|
||||
if (userTypeMap[n]) userTypes.push(userTypeMap[n]);
|
||||
else if (roleMap[n]) specificRoles.push(roleMap[n]);
|
||||
});
|
||||
const parts: string[] = [];
|
||||
if (userTypes.length) parts.push(userTypes.join('/'));
|
||||
if (specificRoles.length) parts.push(specificRoles.join('/'));
|
||||
return parts.length ? parts.join(' · ') : '-';
|
||||
const names = roles
|
||||
.map((r: any) => userTypeMap.value[Number(r)])
|
||||
.filter((n: string) => !!n);
|
||||
return names.length ? names.join('/') : '-';
|
||||
}
|
||||
|
||||
/* ================= 表格列 ================= */
|
||||
|
||||
@@ -16,6 +16,8 @@ const currentId = ref<number | null>(null);
|
||||
const detailData = ref<Record<string, any>>({});
|
||||
// 所在组(只读):用户实际所属的多个组(ID + 完整路径名)
|
||||
const userGroups = ref<{ id: number; name: string }[]>([]);
|
||||
// 所在组(只读):默认选中的组 ID(来自 getGroups,失败则兜底当前组),仅用于展示,不提交
|
||||
const userGroupIdsArr = ref<number[]>([]);
|
||||
// 角色属性下拉:来自角色授权页的全量角色
|
||||
const roleOptions = ref<{ id: number; name: string }[]>([]);
|
||||
// 角色属性是否成功加载(控制编辑后是否回写 saveUserRoles,避免加载失败时误清空)
|
||||
@@ -62,6 +64,7 @@ async function open(row: any, groups: any[], curGroupId?: number | null, onSucce
|
||||
roleOptionsLoaded.value = false;
|
||||
userRolesLoaded.value = false;
|
||||
userGroups.value = [];
|
||||
userGroupIdsArr.value = [];
|
||||
roleOptions.value = [];
|
||||
detailData.value = {};
|
||||
visible.value = true;
|
||||
@@ -79,11 +82,13 @@ async function open(row: any, groups: any[], curGroupId?: number | null, onSucce
|
||||
groups_ = [{ id: curGroupId, name: groupMap.get(curGroupId)?.name ?? ('组#' + curGroupId) }];
|
||||
}
|
||||
userGroups.value = groups_;
|
||||
userGroupIdsArr.value = groups_.map(g => g.id);
|
||||
} catch (e: any) {
|
||||
// getGroups 不可用(如后端未部署该接口)时,至少展示当前点击的组,避免空白
|
||||
if (curGroupId != null) {
|
||||
const groupMap = new Map<number, any>((groupList as any[]).map(g => [g.id, g]));
|
||||
userGroups.value = [{ id: curGroupId, name: groupMap.get(curGroupId)?.name ?? ('组#' + curGroupId) }];
|
||||
userGroupIdsArr.value = [curGroupId];
|
||||
}
|
||||
console.warn('[EditUserContainer] getGroups 失败,已用当前组兜底:', e?.message || e);
|
||||
}
|
||||
@@ -105,16 +110,16 @@ async function open(row: any, groups: any[], curGroupId?: number | null, onSucce
|
||||
.map((r: any) => r.id)
|
||||
.filter((x: any): x is number => x != null);
|
||||
userRolesLoaded.value = true;
|
||||
detailData.value = buildInitForm(row, userRoleIds);
|
||||
detailData.value = buildInitForm(row, userRoleIds, userGroupIdsArr.value);
|
||||
} catch (e: any) {
|
||||
// 已绑定角色加载失败:仍回填其它字段,但标记未加载成功(提交时不回写,避免误清空)
|
||||
detailData.value = buildInitForm(row, []);
|
||||
detailData.value = buildInitForm(row, [], userGroupIdsArr.value);
|
||||
Message.error('加载用户已绑定角色失败:' + (e?.message || '未知错误'));
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------- 把行数据映射成表单回填数据 ---------- */
|
||||
function buildInitForm(row: any, userRoleIds: number[]) {
|
||||
function buildInitForm(row: any, userRoleIds: number[], userGroupIds: number[] = []) {
|
||||
const roles: number[] = Array.isArray(row.roles) ? row.roles : [];
|
||||
// 用户身份(业务身份)= 1..7;角色属性已移除(超管手动入库、管理员走勾选)
|
||||
const userType = roles.filter(r => r >= 1 && r <= 7);
|
||||
@@ -130,6 +135,7 @@ function buildInitForm(row: any, userRoleIds: number[]) {
|
||||
gender: row.gender ?? 0,
|
||||
userType,
|
||||
appRoles: userRoleIds || [],
|
||||
userGroupIds: userGroupIds || [],
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -57,14 +57,14 @@
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<!-- 第 5 行:所在组(只读)(仅编辑展示,显示用户全部所属组;不提交) -->
|
||||
<!-- 第 5 行:所在组(只读)(仅编辑展示,默认选中当前组;提交时不带 groupId,不改变所在组) -->
|
||||
<Row v-if="isEdit">
|
||||
<Col span="24">
|
||||
<FormItem label="所在组(只读):">
|
||||
<div class="group-readonly-box" :title="userGroups.map(g => g.name).join(' / ')">
|
||||
<span v-for="g in userGroups" :key="g.id" class="group-readonly-tag">{{ g.name }}</span>
|
||||
<span v-if="userGroups.length === 0" class="group-readonly-empty">—</span>
|
||||
</div>
|
||||
<Select v-model="form.userGroupIds" multiple transfer
|
||||
placeholder="默认选中当前组(提交不改变所在组)">
|
||||
<Option v-for="g in userGroups" :key="g.id" :value="g.id" :label="g.name" />
|
||||
</Select>
|
||||
</FormItem>
|
||||
</Col>
|
||||
</Row>
|
||||
@@ -105,6 +105,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, reactive, ref, watch } from "vue";
|
||||
import { useDefaultStore } from "@/store";
|
||||
|
||||
/* ---------- Props / Emits ---------- */
|
||||
const props = withDefaults(defineProps<{
|
||||
@@ -143,6 +144,7 @@ const defaultForm = (): Record<string, any> => ({
|
||||
gender: 0,
|
||||
userType: [] as number[],
|
||||
appRoles: [] as number[],
|
||||
userGroupIds: [] as number[],
|
||||
});
|
||||
|
||||
const form = reactive(defaultForm());
|
||||
@@ -157,15 +159,25 @@ watch(() => props.initForm, (val) => {
|
||||
}, { immediate: true, deep: true });
|
||||
|
||||
/* ---------- 用户身份(业务身份多选)---------- */
|
||||
const userTypeOptions = [
|
||||
{ value: 1, label: '管理员' },
|
||||
{ value: 2, label: '老师' },
|
||||
{ value: 3, label: '校领导' },
|
||||
{ value: 4, label: '局领导' },
|
||||
{ value: 5, label: '学生' },
|
||||
{ value: 6, label: '家长' },
|
||||
{ value: 7, label: '工作号' },
|
||||
// 唯一真源:后端 SysRoleEnum(经 GetSysDict.sysrole 下发),杜绝前端硬编码漂移。
|
||||
// 若字典未加载(接口异常)则回退到与 SysRoleEnum 完全一致的兜底值。
|
||||
const SYS_ROLE_FALLBACK = [
|
||||
{ value: 1, label: '老师' },
|
||||
{ value: 2, label: '校领导' },
|
||||
{ value: 3, label: '学生' },
|
||||
{ value: 4, label: '家长' },
|
||||
{ value: 5, label: '局领导' },
|
||||
{ value: 6, label: '工作号' },
|
||||
{ value: 7, label: '管理员' },
|
||||
];
|
||||
const store = useDefaultStore();
|
||||
const userTypeOptions = computed(() => {
|
||||
const sysrole = (store.getSysDict as any)?.sysrole;
|
||||
if (Array.isArray(sysrole) && sysrole.length) {
|
||||
return sysrole.map((it: any) => ({ value: it.k, label: it.v }));
|
||||
}
|
||||
return SYS_ROLE_FALLBACK;
|
||||
});
|
||||
|
||||
/* ---------- 校验规则 ---------- */
|
||||
// 自定义确认密码校验:新增必填且必须与密码一致;编辑时两者皆空表示不修改,任一非空则必须一致
|
||||
@@ -296,44 +308,4 @@ function onVisibleChange(val: boolean) {
|
||||
flex-wrap: wrap;
|
||||
gap: 0 18px;
|
||||
}
|
||||
|
||||
/* 所在组(只读):白底圆角盒子 + 标签展示,视觉等同只读 Select。
|
||||
改用纯 Vue 渲染,彻底绕开 view-ui-plus disabled+multiple Select
|
||||
在「空数据挂载后异步补选项」时不重渲染已选标签的渲染竞态。 */
|
||||
.group-readonly-box {
|
||||
min-height: 32px;
|
||||
border: 1px solid #dcdee2;
|
||||
border-radius: 4px;
|
||||
background: #fff;
|
||||
padding: 3px 7px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 4px 6px;
|
||||
cursor: default;
|
||||
transition: border-color .2s ease;
|
||||
}
|
||||
|
||||
.group-readonly-tag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
height: 24px;
|
||||
line-height: 22px;
|
||||
padding: 0 8px;
|
||||
border: 1px solid #e3e5e8;
|
||||
border-radius: 3px;
|
||||
background: #f7f7f7;
|
||||
color: #515a6e;
|
||||
font-size: 12px;
|
||||
white-space: nowrap;
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.group-readonly-empty {
|
||||
color: #c5c8ce;
|
||||
font-size: 13px;
|
||||
line-height: 24px;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user