Archived
forked from admin/userbase
This commit is contained in:
@@ -34,7 +34,6 @@ export default {
|
||||
'SysGroup.sysGroupAdd': ['POST', '/api/sysgroup/Add'],
|
||||
'PushSubscription.test': ['POST', '/api/push/Test'],
|
||||
'PushSubscription.retry': ['POST', '/api/push/Retry/{id}'],
|
||||
'Identity.switchIdentity': ['POST', '/api/identity/switch'],
|
||||
'GroupAuthz.setSchoolApp': ['POST', '/api/groupauthz/SetSchoolApp'],
|
||||
'GroupAuthz.setGroupModule': ['POST', '/api/groupauthz/SetGroupModule'],
|
||||
'GroupAuthz.saveModulePerms': ['POST', '/api/groupauthz/SaveModulePerms'],
|
||||
@@ -72,6 +71,7 @@ export default {
|
||||
'Group.add_2': ['POST', '/oapi/group/add'],
|
||||
'SysUser.list': ['GET', '/api/sysuser/List'],
|
||||
'SysUser.getUserGroups': ['GET', '/api/sysuser/Groups/{userId}'],
|
||||
'SysUser.userIdentity': ['GET', '/api/sysuser/UserIdentity'],
|
||||
'SysUser.export_': ['GET', '/api/sysuser/Export'],
|
||||
'SysUser.downloadTemplate': ['GET', '/api/sysuser/DownloadTemplate'],
|
||||
'SysGroup.sysGroupList': ['GET', '/api/sysgroup/List'],
|
||||
@@ -83,8 +83,6 @@ export default {
|
||||
'SysOperationLog.operationLogDownload': ['GET', '/api/oplog/Download/{id}'],
|
||||
'SysOapiLog.oapiLogList': ['GET', '/api/oapilog/List'],
|
||||
'SysOapiLog.oapiLogDownload': ['GET', '/api/oapilog/Download/{id}'],
|
||||
'Identity.current': ['GET', '/api/identity/current'],
|
||||
'Identity.available': ['GET', '/api/identity/available'],
|
||||
'GroupAuthz.userRoles': ['GET', '/api/groupauthz/UserRoles'],
|
||||
'GroupAuthz.userPermissionTree': ['GET', '/api/groupauthz/UserPermissionTree'],
|
||||
'GroupAuthz.userGroupTree': ['GET', '/api/groupauthz/UserGroupTree'],
|
||||
|
||||
Vendored
+44
-104
@@ -114,10 +114,17 @@ export interface SysUserRequest {
|
||||
email?: string;
|
||||
gender?: number;
|
||||
profilePicture?: string;
|
||||
roles?: number[];
|
||||
/** 身份归属学校(schoolId 解析后的真实组 id;编辑必传,新增可不传按 groupId 解析) */
|
||||
schoolId?: number;
|
||||
/** 用户身份角色(SysRoleEnum.key 1-7),按 schoolId 全量覆盖保存;空数组=清空该校身份 */
|
||||
roleIds?: number[];
|
||||
disabled?: number;
|
||||
expired?: boolean;
|
||||
}
|
||||
export interface UserIdentityVo {
|
||||
schoolId?: number;
|
||||
roleIds?: number[];
|
||||
}
|
||||
export interface SysGroupSortRequest {
|
||||
pid: number;
|
||||
ids: number[];
|
||||
@@ -146,10 +153,6 @@ export interface PushTestRequest {
|
||||
interfacePath?: string;
|
||||
targetIds?: number[];
|
||||
}
|
||||
export interface IdentitySwitchRequest {
|
||||
roleId: number;
|
||||
schoolId: number;
|
||||
}
|
||||
export interface SchoolAppAuthzRequest {
|
||||
schid: number;
|
||||
appid: number;
|
||||
@@ -233,20 +236,6 @@ export interface SysGroupVo {
|
||||
sort?: number;
|
||||
children?: SysGroupVo[];
|
||||
}
|
||||
export interface IdentityCurrentVo {
|
||||
roleId?: number;
|
||||
roleName?: string;
|
||||
schoolId?: number;
|
||||
schoolName?: string;
|
||||
fallback?: boolean;
|
||||
}
|
||||
export interface IdentityOptionVo {
|
||||
roleId?: number;
|
||||
roleName?: string;
|
||||
schoolId?: number;
|
||||
schoolName?: string;
|
||||
isMain?: boolean;
|
||||
}
|
||||
export interface AppItem {
|
||||
id?: number;
|
||||
name?: string;
|
||||
@@ -320,6 +309,8 @@ export interface SysUserVo {
|
||||
type?: number;
|
||||
sortNo?: number;
|
||||
groupStatus?: number;
|
||||
/** 主校身份名(如「老师/校领导」):列表「用户身份」列展示字段(roles 列已停写) */
|
||||
identityNames?: string;
|
||||
expired?: boolean;
|
||||
createTime?: string;
|
||||
updateTime?: string;
|
||||
@@ -1282,6 +1273,40 @@ declare global {
|
||||
>(
|
||||
config: Config
|
||||
): Alova2Method<number[], 'SysUser.getUserGroups', Config>;
|
||||
/**
|
||||
* ---
|
||||
*
|
||||
* [GET]
|
||||
*
|
||||
* **path:** /api/sysuser/UserIdentity
|
||||
*
|
||||
* ---
|
||||
*
|
||||
* **Query Parameters**
|
||||
* ```ts
|
||||
* type QueryParameters = {
|
||||
* userId: number
|
||||
* groupId: number
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* ---
|
||||
*
|
||||
* **Response**
|
||||
* ```ts
|
||||
* type Response = UserIdentityVo
|
||||
* ```
|
||||
*/
|
||||
userIdentity<
|
||||
Config extends Alova2MethodConfig<UserIdentityVo> & {
|
||||
params: {
|
||||
userId: number;
|
||||
groupId: number;
|
||||
};
|
||||
}
|
||||
>(
|
||||
config: Config
|
||||
): Alova2Method<UserIdentityVo, 'SysUser.userIdentity', Config>;
|
||||
/**
|
||||
* ---
|
||||
*
|
||||
@@ -1836,91 +1861,6 @@ declare global {
|
||||
config: Config
|
||||
): Alova2Method<Blob, 'PushSubscription.download', Config>;
|
||||
};
|
||||
Identity: {
|
||||
/**
|
||||
* ---
|
||||
*
|
||||
* [POST]
|
||||
*
|
||||
* **path:** /api/identity/switch
|
||||
*
|
||||
* ---
|
||||
*
|
||||
* **RequestBody**
|
||||
* ```ts
|
||||
* type RequestBody = {
|
||||
* roleId: number
|
||||
* schoolId: number
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* ---
|
||||
*
|
||||
* **Response**
|
||||
* ```ts
|
||||
* type Response = {
|
||||
* roleId?: number
|
||||
* roleName?: string
|
||||
* schoolId?: number
|
||||
* schoolName?: string
|
||||
* fallback?: boolean
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
switchIdentity<
|
||||
Config extends Alova2MethodConfig<IdentityCurrentVo> & {
|
||||
data: IdentitySwitchRequest;
|
||||
}
|
||||
>(
|
||||
config: Config
|
||||
): Alova2Method<IdentityCurrentVo, 'Identity.switchIdentity', Config>;
|
||||
/**
|
||||
* ---
|
||||
*
|
||||
* [GET]
|
||||
*
|
||||
* **path:** /api/identity/current
|
||||
*
|
||||
* ---
|
||||
*
|
||||
* **Response**
|
||||
* ```ts
|
||||
* type Response = {
|
||||
* roleId?: number
|
||||
* roleName?: string
|
||||
* schoolId?: number
|
||||
* schoolName?: string
|
||||
* fallback?: boolean
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
current<Config extends Alova2MethodConfig<IdentityCurrentVo>>(
|
||||
config?: Config
|
||||
): Alova2Method<IdentityCurrentVo, 'Identity.current', Config>;
|
||||
/**
|
||||
* ---
|
||||
*
|
||||
* [GET]
|
||||
*
|
||||
* **path:** /api/identity/available
|
||||
*
|
||||
* ---
|
||||
*
|
||||
* **Response**
|
||||
* ```ts
|
||||
* type Response = Array<{
|
||||
* roleId?: number
|
||||
* roleName?: string
|
||||
* schoolId?: number
|
||||
* schoolName?: string
|
||||
* isMain?: boolean
|
||||
* }>
|
||||
* ```
|
||||
*/
|
||||
available<Config extends Alova2MethodConfig<IdentityOptionVo[]>>(
|
||||
config?: Config
|
||||
): Alova2Method<IdentityOptionVo[], 'Identity.available', Config>;
|
||||
};
|
||||
GroupAuthz: {
|
||||
/**
|
||||
* ---
|
||||
|
||||
@@ -69,7 +69,8 @@ export default {
|
||||
// console.log(event, treeId, treeNode, clickFlag);
|
||||
if (_this.isCheck) {
|
||||
var treeObj = $.fn.zTree.getZTreeObj(treeId);
|
||||
treeObj.checkNode(treeNode, !treeNode.checked, true);
|
||||
// 第4参 callbackFlag=true:点击文字切换勾选时同步触发 onCheck,与点击勾选框行为一致
|
||||
treeObj.checkNode(treeNode, !treeNode.checked, true, true);
|
||||
}
|
||||
_this.$emit("on-tree-click", treeNode);
|
||||
};
|
||||
|
||||
@@ -84,7 +84,7 @@ function onTreeData(tree: any[]) {
|
||||
|
||||
async function loadUserGroups(userId: number) {
|
||||
try {
|
||||
const groupIds: number[] = await Apis.SysUser.getGroups({ pathParams: { userId }, cacheFor: null });
|
||||
const groupIds: number[] = await Apis.SysUser.getUserGroups({ pathParams: { userId }, cacheFor: null });
|
||||
if (treeData.value?.length) {
|
||||
initSelected(groupIds || []);
|
||||
} else {
|
||||
|
||||
@@ -60,7 +60,6 @@
|
||||
<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,31 +148,6 @@ const tableApi = (params: { pageIndex: number; pageSize: number }) => {
|
||||
|
||||
const tableHeight = ref($utils.useTableHeight({ isOperat: false, isPage: true, isSearch: true }))
|
||||
|
||||
/* ================= 用户身份(roles[])展示 ================= */
|
||||
// 唯一真源:后端 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 names = roles
|
||||
.map((r: any) => userTypeMap.value[Number(r)])
|
||||
.filter((n: string) => !!n);
|
||||
return names.length ? names.join('/') : '-';
|
||||
}
|
||||
|
||||
/* ================= 表格列 ================= */
|
||||
const columns = [
|
||||
{ type: 'selection', width: 55, align: 'center' },
|
||||
@@ -182,8 +156,9 @@ const columns = [
|
||||
{ title: '真实姓名', key: 'realName', minWidth: 120 },
|
||||
{ title: '手机号', key: 'phone', minWidth: 140 },
|
||||
{
|
||||
title: '用户身份', key: 'roles', minWidth: 180,
|
||||
render: (h: any, { row }: any) => h('span', describeRoles(row.roles)),
|
||||
title: '用户身份', key: 'identityNames', minWidth: 180,
|
||||
// 数据源:后端按页批量带出的主校身份名(is_main 行,roles 列已停写,不再直读旧列)
|
||||
render: (h: any, { row }: any) => h('span', row.identityNames || '-'),
|
||||
},
|
||||
{
|
||||
title: '状态', key: 'disabled', width: 130, align: 'center',
|
||||
|
||||
@@ -82,10 +82,11 @@ async function handleSubmit(formData: Record<string, any>) {
|
||||
/**
|
||||
* 把表单字段映射成 SysUserRequest:
|
||||
* - 只提交后端 SysUserRequest 里声明的字段
|
||||
* - 角色仅来自"用户身份"勾选(userType),groupId 由当前所在组回写
|
||||
* - 身份角色来自"用户身份"勾选(userType → roleIds);schoolId 不传,由后端按 groupId 解析落库
|
||||
* - groupId 由当前所在组回写
|
||||
*/
|
||||
function buildPayload(formData: Record<string, any>) {
|
||||
const roles: number[] = (formData.userType || []).map((v: any) => Number(v));
|
||||
const roleIds: number[] = (formData.userType || []).map((v: any) => Number(v));
|
||||
return {
|
||||
account: formData.account,
|
||||
password: formData.password,
|
||||
@@ -95,7 +96,7 @@ function buildPayload(formData: Record<string, any>) {
|
||||
phone: formData.phone || undefined,
|
||||
gender: formData.gender,
|
||||
groupId: currentGroupId.value,
|
||||
roles,
|
||||
roleIds,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ let onSuccessCallback: (() => void) | null = null;
|
||||
// useForm 托管提交——currentId 是 ref,闭包内 .value 在 sendForm 时取当前值
|
||||
const { form, loading: submitting, send: sendForm, onSuccess, onError } = useForm(
|
||||
(dto) => Apis.SysUser.edit({ pathParams: { id: currentId.value }, data: dto }),
|
||||
{ initialForm: { id: null, account: '', realName: '', password: '', confirmPassword: '', idcard: '', phone: '', gender: 0, userType: [] }, resetAfterSubmiting: true }
|
||||
{ initialForm: { id: null, account: '', realName: '', password: '', confirmPassword: '', idcard: '', phone: '', gender: 0, userType: [], schoolId: null }, resetAfterSubmiting: true }
|
||||
);
|
||||
|
||||
onSuccess(() => {
|
||||
@@ -69,9 +69,9 @@ async function open(row: any, groups: any[], curGroupId?: number | null, onSucce
|
||||
detailData.value = {};
|
||||
visible.value = true;
|
||||
|
||||
// 1) 用户所属组(权威来源 getGroups);失败或为空则用当前组兜底,绝不留空
|
||||
// 1) 用户所属组(权威来源 getUserGroups);失败或为空则用当前组兜底,绝不留空
|
||||
try {
|
||||
const groupIdsResp = await Apis.SysUser.getGroups({ pathParams: { userId: row.id } });
|
||||
const groupIdsResp = await Apis.SysUser.getUserGroups({ pathParams: { userId: row.id }, cacheFor: null });
|
||||
const ids: number[] = Array.isArray(groupIdsResp) ? groupIdsResp : [];
|
||||
const groupMap = new Map<number, any>((groupList as any[]).map(g => [g.id, g]));
|
||||
let groups_: { id: number; name: string }[] = ids.map(id => ({
|
||||
@@ -84,13 +84,13 @@ async function open(row: any, groups: any[], curGroupId?: number | null, onSucce
|
||||
userGroups.value = groups_;
|
||||
userGroupIdsArr.value = groups_.map(g => g.id);
|
||||
} catch (e: any) {
|
||||
// getGroups 不可用(如后端未部署该接口)时,至少展示当前点击的组,避免空白
|
||||
// getUserGroups 不可用(如后端未部署该接口)时,至少展示当前点击的组,避免空白
|
||||
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);
|
||||
console.warn('[EditUserContainer] getUserGroups 失败,已用当前组兜底:', e?.message || e);
|
||||
}
|
||||
|
||||
// 2) 角色属性下拉(相互独立,失败不影响所在组)
|
||||
@@ -126,10 +126,6 @@ async function open(row: any, groups: any[], curGroupId?: number | null, onSucce
|
||||
|
||||
/* ---------- 把行数据映射成表单回填数据 ---------- */
|
||||
function buildInitForm(row: any, userRoleIds: number[], defaultGroupId: number | null = null) {
|
||||
const roles: number[] = Array.isArray(row.roles) ? row.roles : [];
|
||||
// 用户身份(业务身份)= 1..7;角色属性已移除(超管手动入库、管理员走勾选)
|
||||
const userType = roles.filter(r => r >= 1 && r <= 7);
|
||||
|
||||
return {
|
||||
id: row.id,
|
||||
account: row.account || '',
|
||||
@@ -139,7 +135,9 @@ function buildInitForm(row: any, userRoleIds: number[], defaultGroupId: number |
|
||||
password: '',
|
||||
confirmPassword: '',
|
||||
gender: row.gender ?? 0,
|
||||
userType,
|
||||
// 用户身份回显唯一来源是 GET UserIdentity(UserForm 内部按「所在组」加载):
|
||||
// roles 列已停写,row.roles 为冻结历史值,误用回显会把废弃值固化进权威表
|
||||
userType: [],
|
||||
appRoles: userRoleIds || [],
|
||||
userGroupId: defaultGroupId ?? null,
|
||||
};
|
||||
@@ -172,9 +170,10 @@ async function handleSubmit(formData: Record<string, any>) {
|
||||
* - 只提交后端 SysUserRequest 里声明的字段
|
||||
* - 编辑时不提交 groupId(后端 edit 会保留用户原分组,且 groupId 仅在 Create 分组校验)
|
||||
* - 编辑时密码留空表示不修改,所以密码字段不传
|
||||
* - 身份随 Select 当前选中组提交 {schoolId, roleIds}(未修改也提交原值),由后端按校全量覆盖并对齐
|
||||
*/
|
||||
function buildPayload(formData: Record<string, any>, id: number) {
|
||||
const roles: number[] = (formData.userType || []).map((v: any) => Number(v));
|
||||
const roleIds: number[] = (formData.userType || []).map((v: any) => Number(v));
|
||||
|
||||
const payload: any = {
|
||||
id,
|
||||
@@ -183,7 +182,8 @@ function buildPayload(formData: Record<string, any>, id: number) {
|
||||
idcard: formData.idcard || undefined,
|
||||
phone: formData.phone || undefined,
|
||||
gender: formData.gender,
|
||||
roles,
|
||||
schoolId: formData.schoolId,
|
||||
roleIds,
|
||||
};
|
||||
// 编辑时若填写了密码才提交(密码为空表示不修改)
|
||||
if (formData.password) {
|
||||
|
||||
@@ -57,19 +57,19 @@
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<!-- 第 5 行:所在组(只读)(仅编辑展示,普通单选、默认选中当前组;提交时不带 groupId,不改变所在组) -->
|
||||
<!-- 第 5 行:所在组(仅编辑展示;切换只触发下方「用户身份」回显,不改变实际所在组) -->
|
||||
<Row v-if="isEdit">
|
||||
<Col span="24">
|
||||
<FormItem label="所在组(只读):">
|
||||
<FormItem label="所在组:">
|
||||
<Select v-model="form.userGroupId" transfer clearable
|
||||
placeholder="默认选中当前组(提交不改变所在组)">
|
||||
placeholder="切换后回显该组身份(不改变实际所在组)">
|
||||
<Option v-for="g in userGroups" :key="g.id" :value="g.id" :label="g.name" />
|
||||
</Select>
|
||||
</FormItem>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<!-- 第 6 行:用户身份(checkbox 多选,整行) -->
|
||||
<!-- 第 6 行:用户身份(checkbox 多选,整行;编辑时跟随所在组 Select 回显) -->
|
||||
<Row>
|
||||
<Col span="24">
|
||||
<FormItem label="用户身份:" prop="userType">
|
||||
@@ -105,7 +105,9 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, reactive, ref, watch } from "vue";
|
||||
import { Message } from "view-ui-plus";
|
||||
import { useDefaultStore } from "@/store";
|
||||
import MyConfirm from "@/components/MyConfirm";
|
||||
|
||||
/* ---------- Props / Emits ---------- */
|
||||
const props = withDefaults(defineProps<{
|
||||
@@ -145,10 +147,14 @@ const defaultForm = (): Record<string, any> => ({
|
||||
userType: [] as number[],
|
||||
appRoles: [] as number[],
|
||||
userGroupId: null as number | null,
|
||||
schoolId: null as number | null,
|
||||
});
|
||||
|
||||
const form = reactive(defaultForm());
|
||||
|
||||
// 编辑模式回显到的最新的身份基线(用于「非空清成空」二次确认;随所在组切换同步更新)
|
||||
let baselineUserType: number[] = [];
|
||||
|
||||
// 外部 initForm 变化时回填
|
||||
watch(() => props.initForm, (val) => {
|
||||
if (val && Object.keys(val).length > 0) {
|
||||
@@ -179,6 +185,24 @@ const userTypeOptions = computed(() => {
|
||||
return SYS_ROLE_FALLBACK;
|
||||
});
|
||||
|
||||
/* ---------- 用户身份回显(唯一来源 GET UserIdentity) ---------- */
|
||||
// 所在组 Select 切换(含弹窗打开时的初始选中)→ 后端解析 schoolId 后回显 {schoolId, roleIds}。
|
||||
// 同一学校多组由后端返回同一 schoolId 与角色并集;前端不感知解析逻辑。
|
||||
watch(() => form.userGroupId, (gid: number | null) => {
|
||||
if (!props.isEdit || gid == null || !form.id) return;
|
||||
Apis.SysUser.userIdentity({ params: { userId: form.id, groupId: gid }, cacheFor: null })
|
||||
.then((resp: any) => {
|
||||
const roleIds = Array.isArray(resp?.roleIds) ? resp.roleIds.map((v: any) => Number(v)) : [];
|
||||
form.schoolId = resp?.schoolId ?? null;
|
||||
form.userType = roleIds;
|
||||
baselineUserType = roleIds;
|
||||
})
|
||||
.catch((e: any) => {
|
||||
// 全局拦截器已弹窗,这里仅记录日志避免二次弹窗;schoolId 保持 null 时提交会被后端拒绝,不会误清空
|
||||
console.warn('[UserForm] 加载用户身份失败:', e?.message || '未知错误');
|
||||
});
|
||||
});
|
||||
|
||||
/* ---------- 校验规则 ---------- */
|
||||
// 自定义确认密码校验:新增必填且必须与密码一致;编辑时两者皆空表示不修改,任一非空则必须一致
|
||||
const validateConfirmPassword = (rule: any, value: string, callback: any) => {
|
||||
@@ -223,6 +247,14 @@ const validateAccount = (rule: any, value: string, callback: any) => {
|
||||
}
|
||||
};
|
||||
|
||||
// 用户身份校验拆分:新增强制 ≥1 必填;编辑允许为空(空数组提交 = 清空该校身份,
|
||||
// 否则切到一个尚无身份数据的学校后连改手机号都会被 min:1 卡死)
|
||||
const validateUserType = (rule: any, value: number[], callback: any) => {
|
||||
if (props.isEdit) return callback();
|
||||
if (!value || value.length === 0) return callback(new Error('请选择至少一个用户身份'));
|
||||
callback();
|
||||
};
|
||||
|
||||
// 密码红五星号仅新增显示;校验规则随 isEdit 变化
|
||||
const rules = computed(() => ({
|
||||
account: [
|
||||
@@ -239,7 +271,7 @@ const rules = computed(() => ({
|
||||
{ pattern: /^1\d{10}$/, message: '请输入正确的手机号', trigger: 'blur' },
|
||||
],
|
||||
userType: [
|
||||
{ required: true, type: 'array' as const, min: 1, message: '请选择至少一个用户身份', trigger: 'change' },
|
||||
{ required: !props.isEdit, validator: validateUserType, trigger: 'change' },
|
||||
],
|
||||
}));
|
||||
|
||||
@@ -254,6 +286,19 @@ watch(() => form.password, () => {
|
||||
});
|
||||
|
||||
async function handleSubmit() {
|
||||
// 编辑模式身份回显未就绪(schoolId 为空)时拦截:避免在身份数据未加载时提交造成误清空
|
||||
if (props.isEdit && form.schoolId == null) {
|
||||
Message.warning('用户身份数据加载中,请稍候再提交');
|
||||
return false;
|
||||
}
|
||||
// 勾选由非空清成空:二次确认(空数组提交 = 清空该校身份)
|
||||
if (props.isEdit && baselineUserType.length > 0 && form.userType.length === 0) {
|
||||
const ok = await MyConfirm({
|
||||
msg: '确定清空该用户在此学校的身份吗?',
|
||||
info: '清空后该校用户将无任何身份',
|
||||
});
|
||||
if (!ok) return false;
|
||||
}
|
||||
try {
|
||||
await formRef.value?.validate();
|
||||
} catch {
|
||||
@@ -308,4 +353,4 @@ function onVisibleChange(val: boolean) {
|
||||
flex-wrap: wrap;
|
||||
gap: 0 18px;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user