mirror of
https://github.com/kuaifan/dootask.git
synced 2026-08-18 10:48:59 +00:00
单个创建:邮箱/昵称/初始密码 + 可选首登改密、职位、部门(多选,选子部门自动补选上级,并加入对应部门群)。 批量导入:上传 Excel/CSV → 预览逐行校验 → 确认后导入。职位为模板第4列(选填,逐行解析校验),部门在预览表按行勾选后由底部设置部门到选中写入;导入按行返回结果(全成功关弹窗+成功提示;含失败留弹窗显示失败明细;仅 success>0 才刷新列表)。 后端:User::createByAdmin 选项数组化 + 校验助手 assertValidProfession/assertValidDepartments;importUsers 逐行 department/profession;UsersController createuser/import;UserImport/UserImportTemplate(含职位列)。 测试:tests/Feature/AdminCreateUserTest、tests/Unit/UserImportParseTest。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
144 lines
5.6 KiB
Vue
144 lines
5.6 KiB
Vue
<template>
|
|
<Modal
|
|
v-model="show"
|
|
:title="$L('创建用户')"
|
|
:mask-closable="false"
|
|
@on-cancel="onCancel">
|
|
<Form ref="form" :model="formData" :label-width="80" @submit.native.prevent>
|
|
<FormItem :label="$L('邮箱')" required>
|
|
<Input v-model="formData.email" :placeholder="$L('请输入邮箱')" clearable/>
|
|
</FormItem>
|
|
<FormItem :label="$L('昵称')" required>
|
|
<Input v-model="formData.nickname" :placeholder="$L('请输入昵称')" clearable/>
|
|
</FormItem>
|
|
<FormItem :label="$L('初始密码')" required>
|
|
<Input v-model="formData.password" type="password" password :placeholder="$L('请输入初始密码')" clearable/>
|
|
</FormItem>
|
|
<FormItem :label="$L('职位')">
|
|
<Input v-model="formData.profession" :maxlength="20" :placeholder="$L('请输入职位/职称')" clearable/>
|
|
</FormItem>
|
|
<FormItem :label="$L('所属部门')">
|
|
<Select
|
|
v-model="formData.department"
|
|
multiple
|
|
:multiple-max="10"
|
|
:multiple-max-before="onMultipleMaxBefore"
|
|
:placeholder="$L('留空为默认部门')">
|
|
<Option
|
|
v-for="(item, index) in departmentList"
|
|
:value="item.id"
|
|
:key="index"
|
|
:label="item.chains.join(' - ')">
|
|
<div :class="`department-level-name level-${item.level - 1}`">{{ item.name }}</div>
|
|
</Option>
|
|
</Select>
|
|
</FormItem>
|
|
<FormItem>
|
|
<Checkbox v-model="formData.changepass">{{$L('员工首次登录需修改密码')}}</Checkbox>
|
|
</FormItem>
|
|
</Form>
|
|
<div slot="footer">
|
|
<Button type="default" @click="show=false">{{$L('取消')}}</Button>
|
|
<Button type="primary" :loading="loading" @click="onSubmit">{{$L('创建')}}</Button>
|
|
</div>
|
|
</Modal>
|
|
</template>
|
|
|
|
<script>
|
|
export default {
|
|
name: 'CreateUserModal',
|
|
props: {
|
|
value: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
departmentList: {
|
|
type: Array,
|
|
default: () => []
|
|
}
|
|
},
|
|
data() {
|
|
return {
|
|
show: false,
|
|
loading: false,
|
|
formData: {email: '', nickname: '', password: '', changepass: true, profession: '', department: []},
|
|
}
|
|
},
|
|
watch: {
|
|
value(val) {
|
|
this.show = val;
|
|
if (val) {
|
|
this.formData = {email: '', nickname: '', password: '', changepass: true, profession: '', department: []};
|
|
}
|
|
},
|
|
show(val) {
|
|
this.$emit('input', val);
|
|
},
|
|
// 选择子部门时自动补选其所有上级部门(与 UserEditModal 行为一致)
|
|
'formData.department': {
|
|
handler(value, oldValue = []) {
|
|
if (!Array.isArray(value) || value.length === 0 || this.departmentList.length === 0) {
|
|
return;
|
|
}
|
|
const previous = Array.isArray(oldValue) ? new Set(oldValue) : new Set();
|
|
const selected = new Set(value);
|
|
const hasNewSelection = Array.from(selected).some(id => !previous.has(id));
|
|
if (!hasNewSelection) {
|
|
return;
|
|
}
|
|
const departmentMap = this.departmentList.reduce((acc, item) => {
|
|
acc[item.id] = item;
|
|
return acc;
|
|
}, {});
|
|
const needAdd = new Set();
|
|
value.forEach((id) => {
|
|
let cursor = departmentMap[id];
|
|
while (cursor && cursor.parent_id && cursor.parent_id > 0) {
|
|
if (!selected.has(cursor.parent_id)) {
|
|
needAdd.add(cursor.parent_id);
|
|
}
|
|
cursor = departmentMap[cursor.parent_id];
|
|
}
|
|
});
|
|
if (needAdd.size > 0) {
|
|
const merged = Array.from(new Set([...value, ...needAdd])).sort((a, b) => a - b);
|
|
if (merged.length !== value.length || merged.some((id, index) => id !== value[index])) {
|
|
this.$set(this.formData, 'department', merged);
|
|
}
|
|
}
|
|
},
|
|
deep: true
|
|
}
|
|
},
|
|
methods: {
|
|
onCancel() {
|
|
this.show = false;
|
|
},
|
|
onSubmit() {
|
|
const {email, nickname, password, changepass, profession, department} = this.formData;
|
|
if (!email || !nickname || !password) {
|
|
$A.messageWarning('邮箱、昵称、初始密码均为必填');
|
|
return;
|
|
}
|
|
this.loading = true;
|
|
this.$store.dispatch("call", {
|
|
url: 'users/createuser',
|
|
data: {email, nickname, password, changepass: changepass ? 1 : 0, profession, department},
|
|
}).then(() => {
|
|
this.loading = false;
|
|
$A.messageSuccess('创建成功');
|
|
this.show = false;
|
|
this.$emit('created');
|
|
}).catch(({msg}) => {
|
|
this.loading = false;
|
|
$A.modalError(msg);
|
|
});
|
|
},
|
|
onMultipleMaxBefore(num) {
|
|
$A.messageError(this.$L('最多选择(*)个部门', num));
|
|
return false;
|
|
},
|
|
}
|
|
}
|
|
</script>
|