mirror of
https://github.com/kuaifan/dootask.git
synced 2026-01-18 13:28:12 +00:00
feat: 统一用户编辑入口为独立弹窗组件
- 新增 UserEditModal 组件,整合昵称、电话、职位、邮箱、密码、部门、个人简介、个性标签编辑 - 签到模式下支持编辑人脸图片和 MAC 地址,并高亮显示相关字段 - TeamManagement 移除分散的编辑入口(快捷修改、修改邮箱/密码/部门/人脸/MAC 等菜单) - 简化 operationUser 方法,移除冗余的 data/watch/methods
This commit is contained in:
parent
f496bc5fca
commit
c9a0b7481a
@ -241,89 +241,13 @@
|
|||||||
<Button type="primary" :loading="departmentLoading > 0" @click="onSaveDepartment">{{$L(departmentData.id > 0 ? '保存' : '新建')}}</Button>
|
<Button type="primary" :loading="departmentLoading > 0" @click="onSaveDepartment">{{$L(departmentData.id > 0 ? '保存' : '新建')}}</Button>
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
<!--编辑用户信息-->
|
||||||
<!--修改MAC-->
|
<UserEditModal
|
||||||
<Modal
|
v-model="userEditShow"
|
||||||
v-model="checkinMacEditShow"
|
:user-data="userEditData"
|
||||||
:title="$L('修改签到MAC地址')">
|
:checkin-mode="checkinMode"
|
||||||
<Form :model="checkinMacEditData" v-bind="formOptions" @submit.native.prevent>
|
:department-list="departmentList"
|
||||||
<Alert type="error" style="margin-bottom:18px">{{$L(`正在进行帐号【ID:${checkinMacEditData.userid}, ${checkinMacEditData.nickname}】MAC地址修改。`)}}</Alert>
|
@updated="getLists"/>
|
||||||
<Row class="team-department-checkin-item">
|
|
||||||
<Col span="12">{{$L('设备MAC地址')}}</Col>
|
|
||||||
<Col span="12">{{$L('备注')}}</Col>
|
|
||||||
</Row>
|
|
||||||
<Row v-for="(item, key) in checkinMacEditData.checkin_macs" :key="key" class="team-department-checkin-item">
|
|
||||||
<Col span="12">
|
|
||||||
<Input
|
|
||||||
v-model="item.mac"
|
|
||||||
:maxlength="20"
|
|
||||||
:placeholder="$L('请输入设备MAC地址')"
|
|
||||||
clearable
|
|
||||||
@on-clear="delCheckinDatum(key)"/>
|
|
||||||
</Col>
|
|
||||||
<Col span="12">
|
|
||||||
<Input v-model="item.remark" :maxlength="100" :placeholder="$L('备注')"/>
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
<Button type="default" icon="md-add" @click="addCheckinDatum">{{$L('添加设备')}}</Button>
|
|
||||||
</Form>
|
|
||||||
<div slot="footer" class="adaption">
|
|
||||||
<Button type="default" @click="checkinMacEditShow=false">{{$L('取消')}}</Button>
|
|
||||||
<Button type="primary" :loading="checkinMacEditLoading > 0" @click="operationUser(checkinMacEditData, true)">{{$L('确定修改')}}</Button>
|
|
||||||
</div>
|
|
||||||
</Modal>
|
|
||||||
|
|
||||||
|
|
||||||
<!--修改Face-->
|
|
||||||
<Modal
|
|
||||||
v-model="checkinFaceEditShow"
|
|
||||||
:title="$L('修改签到人脸图片')">
|
|
||||||
<Form :model="checkinMacEditData" v-bind="formOptions" @submit.native.prevent>
|
|
||||||
<Alert type="error" style="margin-bottom:18px">{{$L(`正在进行帐号【ID:${checkinFaceEditData.userid}, ${checkinFaceEditData.nickname}】人脸图片修改。`)}}</Alert>
|
|
||||||
<Row class="team-department-checkin-item">
|
|
||||||
<Col span="24">{{$L('人脸图片')}}</Col>
|
|
||||||
</Row>
|
|
||||||
<Row class="team-department-checkin-item">
|
|
||||||
<Col span="24">
|
|
||||||
<ImgUpload v-model="checkinFaceEditData.faceimg" :num="1" :width="512" :height="512" whcut="cover"/>
|
|
||||||
<div class="form-tip">{{$L('建议尺寸:500x500')}}</div>
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
</Form>
|
|
||||||
<div slot="footer" class="adaption">
|
|
||||||
<Button type="default" @click="checkinFaceEditShow=false">{{$L('取消')}}</Button>
|
|
||||||
<Button type="primary" :loading="checkinFaceEditLoading > 0" @click="operationUser(checkinFaceEditData, true)">{{$L('确定修改')}}</Button>
|
|
||||||
</div>
|
|
||||||
</Modal>
|
|
||||||
|
|
||||||
<!--修改部门-->
|
|
||||||
<Modal
|
|
||||||
v-model="departmentEditShow"
|
|
||||||
:title="$L('修改部门')">
|
|
||||||
<Form :model="departmentEditData" v-bind="formOptions" @submit.native.prevent>
|
|
||||||
<Alert type="error" style="margin-bottom:18px">{{$L(`正在进行帐号【ID:${departmentEditData.userid}, ${departmentEditData.nickname}】部门修改。`)}}</Alert>
|
|
||||||
<FormItem :label="$L('修改部门')">
|
|
||||||
<Select
|
|
||||||
v-model="departmentEditData.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>
|
|
||||||
</Form>
|
|
||||||
<div slot="footer" class="adaption">
|
|
||||||
<Button type="default" @click="departmentEditShow=false">{{$L('取消')}}</Button>
|
|
||||||
<Button type="primary" :loading="departmentEditLoading > 0" @click="operationUser(departmentEditData, true)">{{$L('确定修改')}}</Button>
|
|
||||||
</div>
|
|
||||||
</Modal>
|
|
||||||
|
|
||||||
<!--操作离职-->
|
<!--操作离职-->
|
||||||
<Modal
|
<Modal
|
||||||
@ -371,14 +295,14 @@
|
|||||||
<script>
|
<script>
|
||||||
import UserSelect from "../../../components/UserSelect.vue";
|
import UserSelect from "../../../components/UserSelect.vue";
|
||||||
import UserAvatarTip from "../../../components/UserAvatar/tip.vue";
|
import UserAvatarTip from "../../../components/UserAvatar/tip.vue";
|
||||||
import ImgUpload from "../../../components/ImgUpload";
|
|
||||||
import ResizeLine from "../../../components/ResizeLine.vue";
|
import ResizeLine from "../../../components/ResizeLine.vue";
|
||||||
import SearchButton from "../../../components/SearchButton.vue";
|
import SearchButton from "../../../components/SearchButton.vue";
|
||||||
|
import UserEditModal from "./UserEditModal.vue";
|
||||||
import {mapState} from "vuex";
|
import {mapState} from "vuex";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "TeamManagement",
|
name: "TeamManagement",
|
||||||
components: {SearchButton, ResizeLine, UserAvatarTip, UserSelect, ImgUpload},
|
components: {SearchButton, ResizeLine, UserAvatarTip, UserSelect, UserEditModal},
|
||||||
props: {
|
props: {
|
||||||
checkinMode: {
|
checkinMode: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
@ -480,21 +404,7 @@ export default {
|
|||||||
key: 'tel',
|
key: 'tel',
|
||||||
minWidth: 80,
|
minWidth: 80,
|
||||||
render: (h, {row}) => {
|
render: (h, {row}) => {
|
||||||
return h('QuickEdit', {
|
return h('AutoTip', row.tel || '-');
|
||||||
props: {
|
|
||||||
value: row.tel,
|
|
||||||
},
|
|
||||||
on: {
|
|
||||||
'on-update': (val, cb) => {
|
|
||||||
this.operationUser({
|
|
||||||
userid: row.userid,
|
|
||||||
tel: val
|
|
||||||
}, true).finally(cb);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [
|
|
||||||
h('AutoTip', row.tel || '-')
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -502,21 +412,7 @@ export default {
|
|||||||
key: 'nickname',
|
key: 'nickname',
|
||||||
minWidth: 80,
|
minWidth: 80,
|
||||||
render: (h, {row}) => {
|
render: (h, {row}) => {
|
||||||
return h('QuickEdit', {
|
return h('AutoTip', row.nickname_original || '-');
|
||||||
props: {
|
|
||||||
value: row.nickname_original,
|
|
||||||
},
|
|
||||||
on: {
|
|
||||||
'on-update': (val, cb) => {
|
|
||||||
this.operationUser({
|
|
||||||
userid: row.userid,
|
|
||||||
nickname: val
|
|
||||||
}, true).finally(cb);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [
|
|
||||||
h('AutoTip', row.nickname_original || '-')
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -524,21 +420,7 @@ export default {
|
|||||||
key: 'profession',
|
key: 'profession',
|
||||||
minWidth: 80,
|
minWidth: 80,
|
||||||
render: (h, {row}) => {
|
render: (h, {row}) => {
|
||||||
return h('QuickEdit', {
|
return h('AutoTip', row.profession || '-');
|
||||||
props: {
|
|
||||||
value: row.profession,
|
|
||||||
},
|
|
||||||
on: {
|
|
||||||
'on-update': (val, cb) => {
|
|
||||||
this.operationUser({
|
|
||||||
userid: row.userid,
|
|
||||||
profession: val
|
|
||||||
}, true).finally(cb);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [
|
|
||||||
h('AutoTip', row.profession || '-')
|
|
||||||
]);
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -629,32 +511,9 @@ export default {
|
|||||||
render: (h, params) => {
|
render: (h, params) => {
|
||||||
const identity = params.row.identity;
|
const identity = params.row.identity;
|
||||||
const dropdownItems = [];
|
const dropdownItems = [];
|
||||||
if (this.checkinMode) {
|
|
||||||
dropdownItems.push(...[
|
|
||||||
h('EDropdownItem', {
|
|
||||||
props: {
|
|
||||||
command: 'checkin_face',
|
|
||||||
},
|
|
||||||
style: {
|
|
||||||
color: '#f90',
|
|
||||||
fontWeight: 'bold'
|
|
||||||
}
|
|
||||||
}, [h('div', this.$L('修改人脸图片'))]),
|
|
||||||
h('EDropdownItem', {
|
|
||||||
props: {
|
|
||||||
command: 'checkin_mac',
|
|
||||||
},
|
|
||||||
style: {
|
|
||||||
color: '#f90',
|
|
||||||
fontWeight: 'bold'
|
|
||||||
}
|
|
||||||
}, [h('div', this.$L('修改MAC地址'))])
|
|
||||||
])
|
|
||||||
}
|
|
||||||
dropdownItems.push(h('EDropdownItem', {
|
dropdownItems.push(h('EDropdownItem', {
|
||||||
props: {
|
props: {
|
||||||
command: 'openDialog',
|
command: 'openDialog',
|
||||||
divided: this.checkinMode
|
|
||||||
},
|
},
|
||||||
}, [h('div', this.$L('打开会话窗口'))]));
|
}, [h('div', this.$L('打开会话窗口'))]));
|
||||||
if (identity.includes('admin')) {
|
if (identity.includes('admin')) {
|
||||||
@ -685,24 +544,13 @@ export default {
|
|||||||
},
|
},
|
||||||
}, [h('div', this.$L('设为临时帐号'))]));
|
}, [h('div', this.$L('设为临时帐号'))]));
|
||||||
}
|
}
|
||||||
dropdownItems.push(...[
|
// 编辑用户信息
|
||||||
h('EDropdownItem', {
|
dropdownItems.push(h('EDropdownItem', {
|
||||||
props: {
|
props: {
|
||||||
command: 'email',
|
command: 'edit_user_info',
|
||||||
divided: true
|
divided: true
|
||||||
},
|
},
|
||||||
}, [h('div', this.$L('修改邮箱'))]),
|
}, [h('div', this.$L('编辑用户信息'))]));
|
||||||
h('EDropdownItem', {
|
|
||||||
props: {
|
|
||||||
command: 'password',
|
|
||||||
},
|
|
||||||
}, [h('div', this.$L('修改密码'))]),
|
|
||||||
h('EDropdownItem', {
|
|
||||||
props: {
|
|
||||||
command: 'department',
|
|
||||||
},
|
|
||||||
}, [h('div', this.$L('修改部门'))])
|
|
||||||
])
|
|
||||||
if (identity.includes('disable')) {
|
if (identity.includes('disable')) {
|
||||||
dropdownItems.push(h('EDropdownItem', {
|
dropdownItems.push(h('EDropdownItem', {
|
||||||
props: {
|
props: {
|
||||||
@ -773,17 +621,9 @@ export default {
|
|||||||
total: 0,
|
total: 0,
|
||||||
noText: '',
|
noText: '',
|
||||||
|
|
||||||
checkinMacEditShow: false,
|
userEditShow: false,
|
||||||
checkinMacEditLoading: 0,
|
userEditData: {},
|
||||||
checkinMacEditData: {},
|
|
||||||
|
|
||||||
checkinFaceEditShow: false,
|
|
||||||
checkinFaceEditLoading: 0,
|
|
||||||
checkinFaceEditData: {},
|
|
||||||
|
|
||||||
departmentEditShow: false,
|
|
||||||
departmentEditLoading: 0,
|
|
||||||
departmentEditData: {},
|
|
||||||
departmentWidth: $A.getStorageInt('management.departmentWidth', 239),
|
departmentWidth: $A.getStorageInt('management.departmentWidth', 239),
|
||||||
|
|
||||||
disableShow: false,
|
disableShow: false,
|
||||||
@ -858,11 +698,6 @@ export default {
|
|||||||
|
|
||||||
dialogLoad: false,
|
dialogLoad: false,
|
||||||
dialogList: [],
|
dialogList: [],
|
||||||
|
|
||||||
nullCheckinDatum: {
|
|
||||||
'mac': '',
|
|
||||||
'remark': '',
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
@ -962,46 +797,6 @@ export default {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
immediate: true
|
immediate: true
|
||||||
},
|
|
||||||
'departmentEditData.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.departmentEditData, 'department', merged);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
deep: true
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@ -1064,26 +859,9 @@ export default {
|
|||||||
|
|
||||||
dropUser(name, row) {
|
dropUser(name, row) {
|
||||||
switch (name) {
|
switch (name) {
|
||||||
case 'checkin_mac':
|
case 'edit_user_info':
|
||||||
this.checkinMacEditData = {
|
this.userEditData = $A.cloneJSON(row);
|
||||||
type: 'checkin_macs',
|
this.userEditShow = true;
|
||||||
userid: row.userid,
|
|
||||||
nickname: row.nickname,
|
|
||||||
checkin_macs: row.checkin_macs,
|
|
||||||
};
|
|
||||||
if (this.checkinMacEditData.checkin_macs.length === 0) {
|
|
||||||
this.addCheckinDatum();
|
|
||||||
}
|
|
||||||
this.checkinMacEditShow = true;
|
|
||||||
break;
|
|
||||||
case 'checkin_face':
|
|
||||||
this.checkinFaceEditData = {
|
|
||||||
type: 'checkin_face',
|
|
||||||
userid: row.userid,
|
|
||||||
nickname: row.nickname,
|
|
||||||
faceimg: row.checkin_face
|
|
||||||
};
|
|
||||||
this.checkinFaceEditShow = true;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'openDialog':
|
case 'openDialog':
|
||||||
@ -1144,55 +922,6 @@ export default {
|
|||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'email':
|
|
||||||
$A.modalInput({
|
|
||||||
title: "修改邮箱",
|
|
||||||
placeholder: `请输入新的邮箱(${row.email})`,
|
|
||||||
onOk: (value) => {
|
|
||||||
if (!value) {
|
|
||||||
return '请输入新的邮箱地址'
|
|
||||||
}
|
|
||||||
return this.operationUser({
|
|
||||||
userid: row.userid,
|
|
||||||
email: value
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'password':
|
|
||||||
$A.modalInput({
|
|
||||||
title: "修改密码",
|
|
||||||
placeholder: "请输入新的密码",
|
|
||||||
onOk: (value) => {
|
|
||||||
if (!value) {
|
|
||||||
return '请输入新的密码'
|
|
||||||
}
|
|
||||||
return this.operationUser({
|
|
||||||
userid: row.userid,
|
|
||||||
password: value
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'department':
|
|
||||||
let departments = []
|
|
||||||
row.department.some(did => {
|
|
||||||
const data = this.departmentList.find(d => d.id == did)
|
|
||||||
if (data) {
|
|
||||||
departments.push(data.owner_userid === row.userid ? `${data.name} (${this.$L('负责人')})` : data.name)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
this.departmentEditData = {
|
|
||||||
type: 'department',
|
|
||||||
userid: row.userid,
|
|
||||||
nickname: row.nickname,
|
|
||||||
department: row.department.map(id => parseInt(id)),
|
|
||||||
};
|
|
||||||
this.departmentEditShow = true;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'setdisable':
|
case 'setdisable':
|
||||||
this.disableData = {
|
this.disableData = {
|
||||||
type: 'setdisable',
|
type: 'setdisable',
|
||||||
@ -1247,19 +976,7 @@ export default {
|
|||||||
operationUser(data, tipErr) {
|
operationUser(data, tipErr) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
let submitData = data;
|
let submitData = data;
|
||||||
if (data.type == 'checkin_macs') {
|
if (data.type == 'setdisable') {
|
||||||
this.checkinMacEditLoading++;
|
|
||||||
} else if (data.type == 'checkin_face') {
|
|
||||||
this.checkinFaceEditLoading++;
|
|
||||||
submitData = {
|
|
||||||
type: data.type,
|
|
||||||
userid: data.userid,
|
|
||||||
nickname: data.nickname,
|
|
||||||
checkin_face: $A.arrayLength(data.faceimg) > 0 ? data.faceimg[0].url : ''
|
|
||||||
}
|
|
||||||
} else if (data.type == 'department') {
|
|
||||||
this.departmentEditLoading++;
|
|
||||||
} else if (data.type == 'setdisable') {
|
|
||||||
this.disableLoading++;
|
this.disableLoading++;
|
||||||
submitData = Object.assign({}, data);
|
submitData = Object.assign({}, data);
|
||||||
if (Array.isArray(submitData.transfer_userid)) {
|
if (Array.isArray(submitData.transfer_userid)) {
|
||||||
@ -1280,14 +997,8 @@ export default {
|
|||||||
}).then(({msg}) => {
|
}).then(({msg}) => {
|
||||||
$A.messageSuccess(msg);
|
$A.messageSuccess(msg);
|
||||||
this.getLists();
|
this.getLists();
|
||||||
resolve()
|
resolve();
|
||||||
if (data.type == 'checkin_macs') {
|
if (data.type == 'setdisable') {
|
||||||
this.checkinMacEditShow = false;
|
|
||||||
} else if (data.type == 'checkin_face') {
|
|
||||||
this.checkinFaceEditShow = false;
|
|
||||||
} else if (data.type == 'department') {
|
|
||||||
this.departmentEditShow = false;
|
|
||||||
} else if (data.type == 'setdisable') {
|
|
||||||
this.disableShow = false;
|
this.disableShow = false;
|
||||||
}
|
}
|
||||||
}).catch(({msg}) => {
|
}).catch(({msg}) => {
|
||||||
@ -1295,21 +1006,15 @@ export default {
|
|||||||
$A.modalError(msg);
|
$A.modalError(msg);
|
||||||
}
|
}
|
||||||
this.getLists();
|
this.getLists();
|
||||||
reject(msg)
|
reject(msg);
|
||||||
}).finally(_ => {
|
}).finally(_ => {
|
||||||
if (data.type == 'checkin_macs') {
|
if (data.type == 'setdisable') {
|
||||||
this.checkinMacEditLoading--;
|
|
||||||
} else if (data.type == 'checkin_face') {
|
|
||||||
this.checkinFaceEditLoading--;
|
|
||||||
} else if (data.type == 'department') {
|
|
||||||
this.departmentEditLoading--;
|
|
||||||
} else if (data.type == 'setdisable') {
|
|
||||||
this.disableLoading--;
|
this.disableLoading--;
|
||||||
} else {
|
} else {
|
||||||
this.loadIng--;
|
this.loadIng--;
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
getDepartmentLists() {
|
getDepartmentLists() {
|
||||||
@ -1321,11 +1026,6 @@ export default {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
onMultipleMaxBefore(num) {
|
|
||||||
$A.messageError(`最多选择${num}个部门`)
|
|
||||||
return false
|
|
||||||
},
|
|
||||||
|
|
||||||
onShowDepartment(data) {
|
onShowDepartment(data) {
|
||||||
this.departmentData = Object.assign({
|
this.departmentData = Object.assign({
|
||||||
id: 0,
|
id: 0,
|
||||||
@ -1481,17 +1181,6 @@ export default {
|
|||||||
this.dialogList = [];
|
this.dialogList = [];
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
addCheckinDatum() {
|
|
||||||
this.checkinMacEditData.checkin_macs.push($A.cloneJSON(this.nullCheckinDatum));
|
|
||||||
},
|
|
||||||
|
|
||||||
delCheckinDatum(key) {
|
|
||||||
this.checkinMacEditData.checkin_macs.splice(key, 1);
|
|
||||||
if (this.checkinMacEditData.checkin_macs.length === 0) {
|
|
||||||
this.addCheckinDatum();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
525
resources/assets/js/pages/manage/components/UserEditModal.vue
Normal file
525
resources/assets/js/pages/manage/components/UserEditModal.vue
Normal file
@ -0,0 +1,525 @@
|
|||||||
|
<template>
|
||||||
|
<Modal
|
||||||
|
v-model="visible"
|
||||||
|
:title="$L('编辑用户信息')"
|
||||||
|
:mask-closable="false"
|
||||||
|
width="560">
|
||||||
|
<Form :model="formData" v-bind="formOptions" @submit.native.prevent>
|
||||||
|
<Alert type="warning" style="margin-bottom:18px">
|
||||||
|
{{ $L(`正在编辑帐号【ID:${userData.userid}, ${userData.nickname}】的信息。`) }}
|
||||||
|
</Alert>
|
||||||
|
|
||||||
|
<FormItem :label="$L('昵称')">
|
||||||
|
<Input
|
||||||
|
v-model="formData.nickname"
|
||||||
|
:maxlength="20"
|
||||||
|
:placeholder="$L('请输入昵称')"/>
|
||||||
|
</FormItem>
|
||||||
|
<FormItem :label="$L('电话')">
|
||||||
|
<Input
|
||||||
|
v-model="formData.tel"
|
||||||
|
:placeholder="$L('请输入电话号码')"/>
|
||||||
|
</FormItem>
|
||||||
|
<FormItem :label="$L('职位')">
|
||||||
|
<Input
|
||||||
|
v-model="formData.profession"
|
||||||
|
:maxlength="20"
|
||||||
|
:placeholder="$L('请输入职位/职称')"/>
|
||||||
|
</FormItem>
|
||||||
|
<FormItem :label="$L('邮箱')">
|
||||||
|
<Input
|
||||||
|
v-model="formData.email"
|
||||||
|
:placeholder="$L('请输入邮箱地址')"
|
||||||
|
:disabled="isLdapUser"/>
|
||||||
|
<div v-if="isLdapUser" class="form-tip">
|
||||||
|
{{ $L('LDAP 用户禁止修改邮箱') }}
|
||||||
|
</div>
|
||||||
|
</FormItem>
|
||||||
|
|
||||||
|
<FormItem :label="$L('新密码')">
|
||||||
|
<Input
|
||||||
|
v-model="formData.password"
|
||||||
|
type="password"
|
||||||
|
password
|
||||||
|
:placeholder="$L('留空则不修改密码')"/>
|
||||||
|
</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 :label="$L('个人简介')">
|
||||||
|
<Input
|
||||||
|
v-model="formData.introduction"
|
||||||
|
type="textarea"
|
||||||
|
:rows="2"
|
||||||
|
:autosize="{ minRows: 2, maxRows: 6 }"
|
||||||
|
:maxlength="500"
|
||||||
|
:placeholder="$L('请输入个人简介')"/>
|
||||||
|
</FormItem>
|
||||||
|
<FormItem :label="$L('个性标签')">
|
||||||
|
<div class="user-tags-preview">
|
||||||
|
<template v-if="personalTags.length">
|
||||||
|
<div
|
||||||
|
v-for="tag in personalTags"
|
||||||
|
:key="tag.id"
|
||||||
|
class="tag-pill"
|
||||||
|
:class="{'is-recognized': tag.recognized}"
|
||||||
|
@click="openTagModal">
|
||||||
|
{{ tag.name }}
|
||||||
|
<span v-if="tag.recognition_total > 0">{{ tag.recognition_total }}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<span v-else class="tags-empty">{{ $L('暂无个性标签') }}</span>
|
||||||
|
<span v-if="personalTagTotal > personalTags.length" class="tags-total">{{ $L('共(*)个', personalTagTotal) }}</span>
|
||||||
|
<Button type="text" size="small" class="manage-button" @click.stop="openTagModal">
|
||||||
|
<Icon type="md-create"/>
|
||||||
|
{{ $L('管理') }}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</FormItem>
|
||||||
|
|
||||||
|
<template v-if="checkinMode">
|
||||||
|
<FormItem :label="$L('人脸图片')" class="checkin-field">
|
||||||
|
<ImgUpload v-model="formData.faceimg" :num="1" :width="512" :height="512" whcut="cover"/>
|
||||||
|
<div class="form-tip">{{ $L('建议尺寸:500x500') }}</div>
|
||||||
|
</FormItem>
|
||||||
|
|
||||||
|
<FormItem :label="$L('MAC地址')" class="checkin-field">
|
||||||
|
<Row class="checkin-mac-header">
|
||||||
|
<Col span="11">{{ $L('设备MAC地址') }}</Col>
|
||||||
|
<Col span="11">{{ $L('备注') }}</Col>
|
||||||
|
<Col span="2"></Col>
|
||||||
|
</Row>
|
||||||
|
<Row
|
||||||
|
v-for="(item, key) in formData.checkin_macs"
|
||||||
|
:key="key"
|
||||||
|
class="checkin-mac-item">
|
||||||
|
<Col span="11">
|
||||||
|
<Input
|
||||||
|
v-model="item.mac"
|
||||||
|
:maxlength="20"
|
||||||
|
:placeholder="$L('请输入设备MAC地址')"/>
|
||||||
|
</Col>
|
||||||
|
<Col span="11">
|
||||||
|
<Input v-model="item.remark" :maxlength="100" :placeholder="$L('备注')"/>
|
||||||
|
</Col>
|
||||||
|
<Col span="2" class="checkin-mac-del">
|
||||||
|
<Icon type="md-close" @click="delCheckinMac(key)"/>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Button type="default" icon="md-add" @click="addCheckinMac">
|
||||||
|
{{ $L('添加设备') }}
|
||||||
|
</Button>
|
||||||
|
</FormItem>
|
||||||
|
</template>
|
||||||
|
</Form>
|
||||||
|
|
||||||
|
<UserTagsModal
|
||||||
|
v-if="userData.userid"
|
||||||
|
v-model="tagModalVisible"
|
||||||
|
:userid="userData.userid"
|
||||||
|
@updated="onTagsUpdated"/>
|
||||||
|
|
||||||
|
<div slot="footer" class="adaption">
|
||||||
|
<Button type="default" @click="visible = false">{{ $L('取消') }}</Button>
|
||||||
|
<Button type="primary" :loading="loading" @click="handleSave">{{ $L('保存') }}</Button>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import ImgUpload from "../../../components/ImgUpload.vue";
|
||||||
|
import UserTagsModal from "./UserTagsModal.vue";
|
||||||
|
import {mapState} from "vuex";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "UserEditModal",
|
||||||
|
components: {ImgUpload, UserTagsModal},
|
||||||
|
props: {
|
||||||
|
value: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
userData: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({})
|
||||||
|
},
|
||||||
|
checkinMode: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
departmentList: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
visible: this.value,
|
||||||
|
loading: false,
|
||||||
|
formData: {
|
||||||
|
nickname: '',
|
||||||
|
tel: '',
|
||||||
|
profession: '',
|
||||||
|
email: '',
|
||||||
|
password: '',
|
||||||
|
department: [],
|
||||||
|
introduction: '',
|
||||||
|
faceimg: [],
|
||||||
|
checkin_macs: []
|
||||||
|
},
|
||||||
|
extraInfo: {},
|
||||||
|
tagModalVisible: false,
|
||||||
|
personalTags: [],
|
||||||
|
personalTagTotal: 0
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState(['formOptions']),
|
||||||
|
|
||||||
|
isLdapUser() {
|
||||||
|
return this.userData.identity && this.userData.identity.includes('ldap');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
value(v) {
|
||||||
|
this.visible = v;
|
||||||
|
if (v) {
|
||||||
|
this.initFormData();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
visible(v) {
|
||||||
|
this.$emit('input', v);
|
||||||
|
},
|
||||||
|
'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: {
|
||||||
|
initFormData() {
|
||||||
|
const {nickname_original, tel, profession, email, department, checkin_face, checkin_macs} = this.userData;
|
||||||
|
this.formData = {
|
||||||
|
nickname: nickname_original || '',
|
||||||
|
tel: tel || '',
|
||||||
|
profession: profession || '',
|
||||||
|
email: email || '',
|
||||||
|
password: '',
|
||||||
|
department: Array.isArray(department)
|
||||||
|
? department.map(id => parseInt(id))
|
||||||
|
: [],
|
||||||
|
introduction: '',
|
||||||
|
faceimg: checkin_face ? [{url: checkin_face}] : [],
|
||||||
|
checkin_macs: Array.isArray(checkin_macs) && checkin_macs.length > 0
|
||||||
|
? $A.cloneJSON(checkin_macs)
|
||||||
|
: [{mac: '', remark: ''}]
|
||||||
|
};
|
||||||
|
this.extraInfo = {};
|
||||||
|
this.personalTags = [];
|
||||||
|
this.personalTagTotal = 0;
|
||||||
|
this.loadUserExtra();
|
||||||
|
},
|
||||||
|
|
||||||
|
loadUserExtra() {
|
||||||
|
const userid = this.userData?.userid;
|
||||||
|
if (!userid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.$store.dispatch("getUserExtra", userid)
|
||||||
|
.then((data) => {
|
||||||
|
if ($A.isJson(data)) {
|
||||||
|
this.extraInfo = data;
|
||||||
|
this.formData.introduction = data.introduction || '';
|
||||||
|
this.syncPersonalTags();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {});
|
||||||
|
},
|
||||||
|
|
||||||
|
syncPersonalTags() {
|
||||||
|
const extra = this.extraInfo || {};
|
||||||
|
const tags = Array.isArray(extra.personal_tags) ? extra.personal_tags : [];
|
||||||
|
this.personalTags = tags.slice(0, 10);
|
||||||
|
this.personalTagTotal = typeof extra.personal_tags_total === 'number'
|
||||||
|
? extra.personal_tags_total
|
||||||
|
: this.personalTags.length;
|
||||||
|
},
|
||||||
|
|
||||||
|
openTagModal() {
|
||||||
|
if (!this.userData.userid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.tagModalVisible = true;
|
||||||
|
},
|
||||||
|
|
||||||
|
onTagsUpdated({top, total}) {
|
||||||
|
this.personalTags = Array.isArray(top) ? top : [];
|
||||||
|
this.personalTagTotal = typeof total === 'number' ? total : this.personalTags.length;
|
||||||
|
this.extraInfo = Object.assign({}, this.extraInfo, {
|
||||||
|
personal_tags: this.personalTags,
|
||||||
|
personal_tags_total: this.personalTagTotal
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
onMultipleMaxBefore(num) {
|
||||||
|
$A.messageError(`最多选择${num}个部门`);
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
addCheckinMac() {
|
||||||
|
this.formData.checkin_macs.push({mac: '', remark: ''});
|
||||||
|
},
|
||||||
|
|
||||||
|
delCheckinMac(index) {
|
||||||
|
this.formData.checkin_macs.splice(index, 1);
|
||||||
|
if (this.formData.checkin_macs.length === 0) {
|
||||||
|
this.addCheckinMac();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async handleSave() {
|
||||||
|
this.loading = true;
|
||||||
|
try {
|
||||||
|
await this.saveBasicInfo();
|
||||||
|
await this.saveExtraInfo();
|
||||||
|
if (this.checkinMode) {
|
||||||
|
await this.saveCheckinInfo();
|
||||||
|
}
|
||||||
|
$A.messageSuccess(this.$L('保存成功'));
|
||||||
|
this.visible = false;
|
||||||
|
this.$emit('updated');
|
||||||
|
} catch (error) {
|
||||||
|
$A.modalError(error.msg || this.$L('保存失败'));
|
||||||
|
} finally {
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
saveExtraInfo() {
|
||||||
|
const userid = this.userData?.userid;
|
||||||
|
if (!userid) {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
const oldIntroduction = this.extraInfo?.introduction || '';
|
||||||
|
if (this.formData.introduction === oldIntroduction) {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
return this.$store.dispatch('saveUserExtra', {
|
||||||
|
userid,
|
||||||
|
data: {
|
||||||
|
introduction: this.formData.introduction || ''
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
saveBasicInfo() {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const data = {
|
||||||
|
userid: this.userData.userid,
|
||||||
|
department: this.formData.department,
|
||||||
|
type: 'department'
|
||||||
|
};
|
||||||
|
if (this.formData.nickname !== (this.userData.nickname_original || '')) {
|
||||||
|
data.nickname = this.formData.nickname;
|
||||||
|
}
|
||||||
|
if (this.formData.tel !== (this.userData.tel || '')) {
|
||||||
|
data.tel = this.formData.tel;
|
||||||
|
}
|
||||||
|
if (this.formData.profession !== (this.userData.profession || '')) {
|
||||||
|
data.profession = this.formData.profession;
|
||||||
|
}
|
||||||
|
if (this.formData.email !== this.userData.email) {
|
||||||
|
data.email = this.formData.email;
|
||||||
|
}
|
||||||
|
if (this.formData.password) {
|
||||||
|
data.password = this.formData.password;
|
||||||
|
}
|
||||||
|
this.$store.dispatch("call", {
|
||||||
|
url: 'users/operation',
|
||||||
|
data
|
||||||
|
}).then(resolve).catch(reject);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
saveCheckinInfo() {
|
||||||
|
const promises = [];
|
||||||
|
const newFaceUrl = $A.arrayLength(this.formData.faceimg) > 0
|
||||||
|
? this.formData.faceimg[0].url
|
||||||
|
: '';
|
||||||
|
const oldFaceUrl = this.userData.checkin_face || '';
|
||||||
|
if (newFaceUrl !== oldFaceUrl) {
|
||||||
|
promises.push(
|
||||||
|
this.$store.dispatch("call", {
|
||||||
|
url: 'users/operation',
|
||||||
|
data: {
|
||||||
|
userid: this.userData.userid,
|
||||||
|
type: 'checkin_face',
|
||||||
|
checkin_face: newFaceUrl
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const validMacs = this.formData.checkin_macs.filter(item => item.mac && item.mac.trim());
|
||||||
|
promises.push(
|
||||||
|
this.$store.dispatch("call", {
|
||||||
|
url: 'users/operation',
|
||||||
|
data: {
|
||||||
|
userid: this.userData.userid,
|
||||||
|
type: 'checkin_macs',
|
||||||
|
checkin_macs: validMacs
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
return Promise.all(promises);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.checkin-field {
|
||||||
|
.ivu-form-item-label {
|
||||||
|
color: #f90;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.checkin-mac-header {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #606266;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkin-mac-item {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
|
||||||
|
.ivu-col {
|
||||||
|
padding-right: 8px;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkin-mac-del {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
cursor: pointer;
|
||||||
|
color: #f00;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-tip {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #999;
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-tags-preview {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 8px;
|
||||||
|
min-height: 32px;
|
||||||
|
|
||||||
|
.tag-pill {
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 6px 12px;
|
||||||
|
border-radius: 12px;
|
||||||
|
font-size: 13px;
|
||||||
|
user-select: none;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
color: #606266;
|
||||||
|
line-height: 14px;
|
||||||
|
height: 26px;
|
||||||
|
max-width: 160px;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
&.is-recognized {
|
||||||
|
color: #67c23a;
|
||||||
|
}
|
||||||
|
span {
|
||||||
|
padding-left: 8px;
|
||||||
|
position: relative;
|
||||||
|
&:before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
left: 2px;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
width: 2px;
|
||||||
|
height: 2px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: currentColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tags-empty {
|
||||||
|
color: #909399;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tags-total {
|
||||||
|
color: #909399;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.manage-button {
|
||||||
|
margin-left: auto;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Loading…
x
Reference in New Issue
Block a user