Files
tms-erp-web/src/views/system/mp-user.vue
T
2026-10-25 19:46:42 +08:00

492 lines
15 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<basic-container class="mp-user-page">
<avue-crud
:option="option"
:table-loading="loading"
:data="data"
v-model:page="page"
v-model="form"
ref="crud"
:permission="permissionList"
:before-open="beforeOpen"
@row-save="rowSave"
@row-update="rowUpdate"
@search-change="searchChange"
@search-reset="searchReset"
@selection-change="selectionChange"
@current-change="currentChange"
@size-change="sizeChange"
@refresh-change="refreshChange"
@on-load="onLoad"
>
<template #menu-left>
<el-button type="primary" v-if="permission.mp_user_add" @click="$refs.crud.rowAdd()">
新增
</el-button>
<el-button type="primary" plain v-if="permission.mp_user_reset" @click="handleReset">
密码重置
</el-button>
<el-button type="danger" plain v-if="permission.mp_user_delete" @click="handleDelete">
批量删除
</el-button>
</template>
<template #identities="{ row }">
{{ row.identitiesName || formatIdentities(row.identities) }}
</template>
<template #deptName="{ row }">
<template v-if="formatDeptNames(row.deptName).length">
<el-tag
v-for="name in formatDeptNames(row.deptName)"
:key="name"
size="small"
class="dept-tag"
>
{{ name }}
</el-tag>
</template>
<span v-else>-</span>
</template>
<template #status="{ row }">
<el-tag :type="row.status === 1 ? 'primary' : 'info'" class="status-text">
{{ row.status === 1 ? '启用' : '停用' }}
</el-tag>
</template>
<template #updateUserName="{ row }">
{{ formatUpdateUserName(row) }}
</template>
<template #deptId-form>
<el-cascader
v-model="deptCascaderValue"
:options="permissionDeptTree"
:props="deptCascaderProps"
:disabled="isViewMode"
clearable
filterable
collapse-tags
collapse-tags-tooltip
placeholder="请选择所属组织(可多选)"
style="width: 100%"
/>
</template>
<template #dataScopeDeptIds-form>
<el-tree
v-if="Number(form.dataScopeRange) === 3"
ref="dataScopeTree"
:data="permissionDeptTree"
show-checkbox
node-key="id"
:default-checked-keys="form.dataScopeDeptIds || []"
:props="{ label: 'title', children: 'children' }"
@check="handleDataScopeCheck"
/>
<span v-else class="form-tip">选择「自定义」后可勾选数据权限组织</span>
</template>
<template #menu="{ row, index }">
<el-link
type="primary"
v-if="permission.mp_user_view"
@click="$refs.crud.rowView(row, index)"
>
查看
</el-link>
<el-link
type="primary"
v-if="permission.mp_user_edit"
@click="$refs.crud.rowEdit(row, index)"
>
编辑
</el-link>
<el-link type="primary" v-if="permission.mp_user_status" @click="handleStatus(row)">
{{ row.status === 1 ? '停用' : '启用' }}
</el-link>
<el-link type="primary" v-if="permission.mp_user_reset" @click="handleResetForRow(row)">
重置密码
</el-link>
<el-link type="danger" v-if="permission.mp_user_delete" @click="rowDel(row)">
删除
</el-link>
</template>
<template #menu-form="{ size }">
<el-button
v-if="permission.mp_user_reset && form.id && dialogType === 'edit'"
:size="size"
@click="handleResetForRow(form)"
>
重置密码
</el-button>
</template>
</avue-crud>
</basic-container>
</template>
<script>
import { h } from 'vue';
import { getList, getDetail, submit, remove, changeStatus, resetPassword } from '@/api/system/mpUser';
import { getDeptTree } from '@/api/system/dept';
import option from '@/option/system/mpUser';
import { mapGetters } from 'vuex';
import { formatUpdateUserName } from '@/utils/audit';
import { sensitive } from '@/utils/sensitive';
import func from '@/utils/func';
const IDENTITY_LABEL = {
driver: '司机',
admin: '管理员',
};
export default {
data() {
return {
form: {},
dialogType: '',
query: {},
loading: true,
page: {
pageSize: 10,
currentPage: 1,
total: 0,
},
selectionList: [],
option,
data: [],
permissionDeptTree: [],
sensitiveManager: null,
};
},
computed: {
...mapGetters(['permission', 'userInfo']),
permissionList() {
return {
addBtn: this.validData(this.permission.mp_user_add, false),
viewBtn: this.validData(this.permission.mp_user_view, false),
delBtn: this.validData(this.permission.mp_user_delete, false),
editBtn: this.validData(this.permission.mp_user_edit, false),
};
},
ids() {
return this.selectionList.map(item => item.id).join(',');
},
isViewMode() {
return this.dialogType === 'view';
},
deptCascaderProps() {
return {
label: 'title',
value: 'id',
children: 'children',
emitPath: false,
multiple: true,
checkStrictly: true,
expandTrigger: 'click',
disabled: 'disabled',
};
},
deptCascaderValue: {
get() {
const ids = Array.isArray(this.form.deptId)
? this.form.deptId
: this.form.deptId
? String(this.form.deptId).split(',').filter(Boolean)
: [];
return ids
.filter(item => item !== undefined && item !== null && item !== '')
.map(item => (typeof item === 'number' ? item : String(item)));
},
set(value) {
this.form.deptId = Array.isArray(value)
? value.filter(item => item !== undefined && item !== null && item !== '')
: value
? [value]
: [];
},
},
},
created() {
this.sensitiveManager = sensitive.create({
fields: ['phone', 'email'],
});
this.loadDeptTree();
},
methods: {
formatUpdateUserName,
formatIdentities(value) {
if (!value) return '';
return String(value)
.split(',')
.map(item => IDENTITY_LABEL[item] || item)
.filter(Boolean)
.join(',');
},
formatDeptNames(deptName) {
if (!deptName) {
return [];
}
return String(deptName)
.split(',')
.map(item => item.trim())
.filter(Boolean);
},
loadDeptTree() {
const tenantId = this.userInfo?.tenantId;
if (!tenantId) {
return;
}
getDeptTree(tenantId).then(res => {
this.permissionDeptTree = res.data.data || [];
});
},
handleDataScopeCheck(_, checked) {
if (this.isViewMode) {
return;
}
this.form.dataScopeDeptIds = checked.checkedKeys || [];
},
normalizeSubmitRow(row = {}, isUpdate = false) {
let payload = { ...row };
// 编辑时剔除未改动的脱敏字段,避免把 138****8000 写回库
if (isUpdate && this.sensitiveManager) {
payload = this.sensitiveManager.getSubmitData(payload);
}
if (Array.isArray(payload.identities)) {
payload.identities = payload.identities.filter(Boolean).join(',');
}
payload.deptId = func.join(payload.deptId);
if (payload.phone) {
payload.phone = String(payload.phone).trim();
}
// 脱敏手机号不能作为账号默认值
if (!payload.account && payload.phone && !String(payload.phone).includes('*')) {
payload.account = payload.phone;
}
payload.dataScopeRange = Number(payload.dataScopeRange || 1);
payload.dataLevelRange = Number(payload.dataLevelRange || 1);
if (payload.dataScopeRange === 3) {
payload.dataScopeDeptIds = Array.isArray(payload.dataScopeDeptIds)
? payload.dataScopeDeptIds
: [];
} else {
payload.dataScopeDeptIds = [];
}
delete payload.password;
delete payload.identitiesName;
delete payload.deptName;
delete payload.sexName;
delete payload.createUserName;
delete payload.updateUserName;
return payload;
},
beforeOpen(done, type) {
this.dialogType = type;
this.loadDeptTree();
if (['edit', 'view'].includes(type) && this.form.id) {
getDetail(this.form.id).then(res => {
const detail = res.data.data || {};
if (detail.identities && typeof detail.identities === 'string') {
detail.identities = detail.identities.split(',').filter(Boolean);
}
const deptIds = func.split(detail.deptId || '');
this.form = {
...detail,
deptId: Array.isArray(deptIds) ? deptIds : [],
dataScopeRange: detail.dataScopeRange || 1,
dataLevelRange: detail.dataLevelRange || 1,
dataScopeDeptIds: detail.dataScopeDeptIds || [],
};
this.sensitiveManager?.saveInitialData(this.form);
this.$nextTick(() => {
this.$refs.dataScopeTree?.setCheckedKeys?.(this.form.dataScopeDeptIds || []);
});
done();
});
return;
}
if (type === 'add') {
this.form = {
status: 1,
identities: ['driver'],
deptId: [],
dataScopeRange: 1,
dataLevelRange: 1,
dataScopeDeptIds: [],
};
this.sensitiveManager?.saveInitialData({});
}
done();
},
rowSave(row, done, loading) {
const payload = this.normalizeSubmitRow(row, false);
if (payload.dataScopeRange === 3 && (!payload.dataScopeDeptIds || !payload.dataScopeDeptIds.length)) {
this.$message.warning('请选择自定义数据权限组织');
loading();
return;
}
submit(payload).then(
() => {
this.onLoad(this.page);
this.$message.success('操作成功');
done();
},
() => loading()
);
},
rowUpdate(row, index, done, loading) {
const payload = this.normalizeSubmitRow(row, true);
if (payload.dataScopeRange === 3 && (!payload.dataScopeDeptIds || !payload.dataScopeDeptIds.length)) {
this.$message.warning('请选择自定义数据权限组织');
loading();
return;
}
submit(payload).then(
() => {
this.onLoad(this.page);
this.$message.success('操作成功');
done();
},
() => loading()
);
},
rowDel(row) {
this.$confirm('确定删除该小程序用户?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => remove(row.id))
.then(() => {
this.onLoad(this.page);
this.$message.success('操作成功');
});
},
handleDelete() {
if (this.selectionList.length === 0) {
this.$message.warning('请选择至少一条数据');
return;
}
this.$confirm('确定删除选中的小程序用户?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => remove(this.ids))
.then(() => {
this.onLoad(this.page);
this.$message.success('操作成功');
this.$refs.crud.toggleSelection();
});
},
handleStatus(row) {
const status = row.status === 1 ? 0 : 1;
const text = status === 1 ? '启用' : '停用';
this.$confirm(`是否${text}该小程序用户?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => changeStatus(row.id, status))
.then(() => {
this.$message.success('操作成功');
this.onLoad(this.page);
});
},
handleReset() {
if (this.selectionList.length === 0) {
this.$message.warning('请选择至少一条数据');
return;
}
this.$confirm('确定将选中账号密码重置为随机密码?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => resetPassword(this.ids))
.then(res => {
this.showResetPassword(res.data.data);
this.$refs.crud.toggleSelection();
});
},
handleResetForRow(row) {
if (!row || !row.id) {
this.$message.warning('请先保存用户后再重置密码');
return;
}
const account = row.account || row.phone || row.id;
this.$confirm(`确定将账号【${account}】的密码重置为随机密码?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => resetPassword(row.id))
.then(res => {
this.showResetPassword(res.data.data);
});
},
showResetPassword(passwordMap) {
const passwordList = Object.keys(passwordMap || {}).map(account =>
h('div', null, `${account}:${passwordMap[account]}`)
);
this.$alert(h('div', null, passwordList), '密码重置成功,请立即保存', {
confirmButtonText: '我已保存',
type: 'success',
});
},
searchReset() {
this.query = {};
this.onLoad(this.page);
},
searchChange(params, done) {
this.query = params;
this.page.currentPage = 1;
this.onLoad(this.page, params);
done();
},
selectionChange(list) {
this.selectionList = list;
},
currentChange(currentPage) {
this.page.currentPage = currentPage;
},
sizeChange(pageSize) {
this.page.pageSize = pageSize;
},
refreshChange() {
this.onLoad(this.page, this.query);
},
onLoad(page, params = {}) {
this.loading = true;
const query = {
...this.query,
...params,
};
if (Array.isArray(query.identities)) {
query.identities = query.identities.filter(Boolean).join(',');
}
getList(page.currentPage, page.pageSize, query)
.then(res => {
const result = res.data.data || {};
this.page.total = result.total || 0;
this.data = result.records || [];
})
.finally(() => {
this.loading = false;
});
},
},
};
</script>
<style lang="scss" scoped>
.mp-user-page {
:deep(.avue-crud__menu) {
min-height: auto;
}
.dept-tag {
margin-right: 4px;
margin-bottom: 2px;
}
.form-tip {
color: var(--el-text-color-secondary);
font-size: 13px;
}
}
</style>