Archived
forked from admin/userbase
init: UserBaseFront (frontend)
This commit is contained in:
@@ -0,0 +1,295 @@
|
||||
<template>
|
||||
<Modal v-model="show" :mask-closable="false" width="800" scrollable :title="title"
|
||||
@on-visible-change="onVisibleChange">
|
||||
<Form ref="formRef" :model="form" :rules="rules" :label-width="130" class="user-form width-95">
|
||||
<!-- 第 1 行:用户名 / 真实姓名 -->
|
||||
<Row>
|
||||
<Col span="12">
|
||||
<FormItem label="用户名:" prop="account">
|
||||
<Input v-model="form.account" :disabled="isEdit" placeholder="请输入用户名" />
|
||||
</FormItem>
|
||||
</Col>
|
||||
<Col span="12">
|
||||
<FormItem label="真实姓名:" prop="realName">
|
||||
<Input v-model="form.realName" placeholder="请输入真实姓名" />
|
||||
</FormItem>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<!-- 第 2 行:密码 / 确认密码(编辑时变为「重置密码」语义,但视觉一致) -->
|
||||
<Row>
|
||||
<Col span="12">
|
||||
<FormItem label="密码:" prop="password">
|
||||
<Input v-model="form.password" type="password" :placeholder="isEdit ? '不修改请留空' : '请输入密码'" />
|
||||
</FormItem>
|
||||
</Col>
|
||||
<Col span="12">
|
||||
<FormItem label="确认密码:" prop="confirmPassword">
|
||||
<Input v-model="form.confirmPassword" type="password"
|
||||
:placeholder="isEdit ? '不修改请留空' : '请再次确认密码'" />
|
||||
</FormItem>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<!-- 第 3 行:身份证号 / 手机号码 -->
|
||||
<Row>
|
||||
<Col span="12">
|
||||
<FormItem label="身份证号:" prop="idcard">
|
||||
<Input v-model="form.idcard" placeholder="请输入身份证号" />
|
||||
</FormItem>
|
||||
</Col>
|
||||
<Col span="12">
|
||||
<FormItem label="手机号码:" prop="phone">
|
||||
<Input v-model="form.phone" placeholder="请输入手机号码" />
|
||||
</FormItem>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<!-- 第 4 行:手机号码2 / 性别 -->
|
||||
<Row>
|
||||
<Col span="12">
|
||||
<FormItem label="手机号码2:">
|
||||
<Input v-model="form.phone2" placeholder="请输入手机号码" />
|
||||
</FormItem>
|
||||
</Col>
|
||||
<Col span="12">
|
||||
<FormItem label="性别:">
|
||||
<RadioGroup v-model="form.gender">
|
||||
<Radio :label="0">女</Radio>
|
||||
<Radio :label="1">男</Radio>
|
||||
</RadioGroup>
|
||||
</FormItem>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<!-- 第 5 行:所在组(整行) -->
|
||||
<Row>
|
||||
<Col span="24">
|
||||
<FormItem label="所在组:" prop="groupId">
|
||||
<Select v-model="form.groupId" placeholder="请选择所在组" filterable transfer>
|
||||
<Option v-for="g in groupOptions" :key="g.id" :value="g.id" :label="g.name" />
|
||||
</Select>
|
||||
</FormItem>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<!-- 第 6 行:用户身份(checkbox 多选,整行) -->
|
||||
<Row>
|
||||
<Col span="24">
|
||||
<FormItem label="用户身份:" prop="userType">
|
||||
<CheckboxGroup v-model="form.userType" class="user-type-group">
|
||||
<Checkbox v-for="opt in userTypeOptions" :key="opt.value" :label="opt.value">
|
||||
{{ opt.label }}
|
||||
</Checkbox>
|
||||
</CheckboxGroup>
|
||||
</FormItem>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<!-- 第 7 行:角色属性(整行) -->
|
||||
<Row>
|
||||
<Col span="24">
|
||||
<FormItem label="角色属性:" prop="role">
|
||||
<Select v-model="form.role" placeholder="请选择" clearable transfer style="width: 220px">
|
||||
<Option v-for="r in roleOptions" :key="r.value" :value="r.value" :label="r.label" />
|
||||
</Select>
|
||||
</FormItem>
|
||||
</Col>
|
||||
</Row>
|
||||
</Form>
|
||||
|
||||
<template #footer>
|
||||
<Button type="primary" :loading="loading" @click="handleSubmit">提交</Button>
|
||||
<Button @click="handleCancel">取消</Button>
|
||||
</template>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, reactive, watch } from "vue";
|
||||
|
||||
/* ---------- Props / Emits ---------- */
|
||||
const props = withDefaults(defineProps<{
|
||||
modelValue: boolean;
|
||||
initForm?: Record<string, any>;
|
||||
groupOptions?: any[];
|
||||
isEdit?: boolean;
|
||||
loading?: boolean;
|
||||
}>(), {
|
||||
initForm: () => ({}),
|
||||
groupOptions: () => [],
|
||||
isEdit: false,
|
||||
loading: false,
|
||||
});
|
||||
|
||||
const emit = defineEmits(['update:modelValue', 'submit', 'cancel']);
|
||||
|
||||
const show = computed({
|
||||
get: () => props.modelValue,
|
||||
set: val => emit('update:modelValue', val),
|
||||
});
|
||||
|
||||
const title = computed(() => props.isEdit ? '编辑用户' : '新增用户');
|
||||
|
||||
/* ---------- 表单数据 ---------- */
|
||||
const defaultForm = (): Record<string, any> => ({
|
||||
id: null,
|
||||
account: '',
|
||||
realName: '',
|
||||
password: '',
|
||||
confirmPassword: '',
|
||||
idcard: '',
|
||||
phone: '',
|
||||
phone2: '',
|
||||
gender: 0,
|
||||
groupId: undefined as number | undefined,
|
||||
userType: [] as number[],
|
||||
role: undefined as number | undefined,
|
||||
});
|
||||
|
||||
const form = reactive(defaultForm());
|
||||
|
||||
// 外部 initForm 变化时回填
|
||||
watch(() => props.initForm, (val) => {
|
||||
if (val && Object.keys(val).length > 0) {
|
||||
Object.assign(form, defaultForm(), val);
|
||||
} else {
|
||||
Object.assign(form, defaultForm());
|
||||
}
|
||||
}, { 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: '工作号' },
|
||||
];
|
||||
|
||||
/* ---------- 角色属性(具体角色单选,固定枚举)---------- */
|
||||
const roleOptions = [
|
||||
{ value: 1, label: '超级管理员' },
|
||||
{ value: 2, label: '学校管理员' },
|
||||
{ value: 3, label: '普通用户' },
|
||||
];
|
||||
|
||||
/* ---------- 校验规则 ---------- */
|
||||
// 自定义确认密码校验
|
||||
const validateConfirmPassword = (rule: any, value: string, callback: any) => {
|
||||
if (!props.isEdit) {
|
||||
// 新增:必填 + 必须与密码一致
|
||||
if (!value) {
|
||||
callback(new Error('请再次输入密码'));
|
||||
} else if (value !== form.password) {
|
||||
callback(new Error('两次输入的密码不一致'));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
} else {
|
||||
// 编辑:留空表示不修改密码;非空时必须与密码一致
|
||||
if (value && value !== form.password) {
|
||||
callback(new Error('两次输入的密码不一致'));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 自定义密码校验(编辑时可留空)
|
||||
const validatePassword = (rule: any, value: string, callback: any) => {
|
||||
if (!props.isEdit && !value) {
|
||||
callback(new Error('请输入密码'));
|
||||
} else if (value && value.length < 6) {
|
||||
callback(new Error('密码长度不能少于 6 位'));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
const rules = {
|
||||
account: [
|
||||
{ required: true, message: '请输入用户名', trigger: 'blur' },
|
||||
{ min: 3, max: 30, message: '用户名长度 3-30 位', trigger: 'blur' },
|
||||
],
|
||||
realName: [
|
||||
{ required: true, message: '请输入真实姓名', trigger: 'blur' },
|
||||
],
|
||||
password: [{ validator: validatePassword, trigger: 'blur' }],
|
||||
confirmPassword: [{ validator: validateConfirmPassword, trigger: 'blur' }],
|
||||
phone: [
|
||||
{ required: true, message: '请输入手机号码', trigger: 'blur' },
|
||||
{ pattern: /^1\d{10}$/, message: '请输入正确的手机号', trigger: 'blur' },
|
||||
],
|
||||
groupId: [
|
||||
{ required: true, type: 'number' as const, message: '请选择所在组', trigger: 'change' },
|
||||
],
|
||||
userType: [
|
||||
{ required: true, type: 'array' as const, min: 1, message: '请选择至少一个用户身份', trigger: 'change' },
|
||||
],
|
||||
role: [
|
||||
{ required: true, type: 'number' as const, message: '请选择角色属性', trigger: 'change' },
|
||||
],
|
||||
};
|
||||
|
||||
/* ---------- 提交 ---------- */
|
||||
const formRef = ref();
|
||||
|
||||
async function handleSubmit() {
|
||||
try {
|
||||
await formRef.value?.validate();
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
emit('submit', { ...form });
|
||||
}
|
||||
|
||||
function handleCancel() {
|
||||
emit('cancel');
|
||||
}
|
||||
|
||||
function onVisibleChange(val: boolean) {
|
||||
if (!val) {
|
||||
emit('cancel');
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.user-form {
|
||||
margin: 0 auto;
|
||||
max-height: 60vh;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
padding-right: 12px;
|
||||
|
||||
:deep(.ivu-form-item) {
|
||||
display: block;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
:deep(.ivu-form-item-label) {
|
||||
font-size: 13px;
|
||||
color: #515a6e;
|
||||
}
|
||||
|
||||
:deep(.ivu-row) {
|
||||
margin-left: 0 !important;
|
||||
margin-right: 0 !important;
|
||||
margin-bottom: 22px;
|
||||
}
|
||||
|
||||
:deep(.ivu-row:last-child) {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* 用户身份多选:横向换行,避免撑爆 Col */
|
||||
.user-type-group {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0 18px;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user