feat: 新增电工 APP 账号开通与密码重置功能

- 新增后端重置密码接口及前端对应操作入口
- 新增补开电工 APP 账号的更新参数
- 新增 hasAccount 字段标识电工是否开通 APP 账号
- 新增数据库菜单及角色绑定支持密码重置权限
- 移除后台角色中错误的 WORKER 和 CUSTOMER 角色
This commit is contained in:
andyesfly committed 2026-10-11 15:07:45 +08:00
1 parent 6cf6a3d5b9
commit f8117e33b6
9 files changed
+164 -18

No files matched your search

+5
View File
@@ -43,6 +43,8 @@ export interface WorkerRecord {
auditBy?: number;
auditAt?: string;
lastLoginAt?: string;
// 是否已开通电工 APP 登录账号
hasAccount?: boolean;
certStatus?: WorkerCertStatus;
certs?: WorkerCertSummary[];
createdAt?: string;
@@ -119,6 +121,9 @@ export const workerRegistryApi = {
audit: (id: number, data: WorkerAuditPayload) => request.put(`/workers/${id}/audit`, data),
// 重置 APP 登录密码(后端固定重置为手机号后 6 位)
resetPassword: (id: number) => request.post(`/workers/${id}/reset-password`),
addCert: (id: number, data: WorkerCertPayload) => request.post(`/workers/${id}/certs`, data),
updateCert: (id: number, certId: number, data: WorkerCertPayload) =>
+44 -3
View File
@@ -165,6 +165,19 @@
<template #icon><AuditOutlined /></template>
审核
</a-button>
<a-popconfirm
v-if="canResetPassword"
title="确定重置该电工的 APP 登录密码吗?"
description="重置后密码为手机号后 6 位"
ok-text="确定"
cancel-text="取消"
@confirm="handleResetPassword(record.id)"
>
<a-button type="link" size="small">
<template #icon><KeyOutlined /></template>
重置密码
</a-button>
</a-popconfirm>
<a-popconfirm
v-if="canDelete"
title="确定要删除该电工吗?"
@@ -240,8 +253,11 @@
<a-form-item label="服务评分" name="rating">
<a-input-number v-model:value="formData.rating" :min="0" :max="5" :step="0.1" style="width: 160px" />
</a-form-item>
<a-form-item v-if="!isEdit" label="APP 登录账号">
<div class="switch-line">
<a-form-item label="APP 登录账号">
<div v-if="isEdit && formData.hasAccount" class="switch-line">
<span class="form-tip">该电工已开通 APP 登录账号,如需重置密码请使用列表中的「重置密码」。</span>
</div>
<div v-else class="switch-line">
<a-switch v-model:checked="formData.withAccount" />
<span class="form-tip">开启后同时创建电工 APP 登录密码,默认密码为手机号后 6 位</span>
</div>
@@ -381,6 +397,7 @@ import {
DeleteOutlined,
EditOutlined,
AuditOutlined,
KeyOutlined,
TeamOutlined,
ClockCircleOutlined,
WarningOutlined,
@@ -425,6 +442,7 @@ const canCreate = computed(() => permissionStore.hasPermission('worker:create'))
const canUpdate = computed(() => permissionStore.hasPermission('worker:update'));
const canDelete = computed(() => permissionStore.hasPermission('worker:delete'));
const canAudit = computed(() => permissionStore.hasPermission('worker:audit'));
const canResetPassword = computed(() => permissionStore.hasPermission('worker:reset-password'));
// ==================== 列表数据 ====================
const loading = ref(false);
@@ -501,7 +519,7 @@ const columns = [
{ title: '在岗状态', key: 'status', align: 'center' as const, width: 110 },
{ title: '评分', key: 'rating', align: 'center' as const, width: 90 },
{ title: '完成数', dataIndex: 'completedCount', key: 'completedCount', align: 'center' as const, width: 90 },
{ title: '操作', key: 'action', width: 210, fixed: 'right' as const },
{ title: '操作', key: 'action', width: 300, fixed: 'right' as const },
];
const pagination = computed(() => ({
@@ -529,6 +547,8 @@ const formData = reactive({
status: 1,
rating: 5,
withAccount: false,
// 编辑时用于判断该电工是否已开通 APP 账号
hasAccount: false,
});
const formRules = {
@@ -664,6 +684,8 @@ async function handleEdit(record: WorkerRecord) {
formData.workerTitle = record.workerTitle || '';
formData.status = record.status;
formData.rating = Number(record.rating ?? 5);
formData.hasAccount = record.hasAccount === true;
formData.withAccount = false;
modalVisible.value = true;
await loadCertRows(record.id);
@@ -674,6 +696,7 @@ async function loadCertRows(workerId: number) {
try {
const res: any = await workerRegistryApi.getDetail(workerId);
if (res.code === 200 && res.data) {
formData.hasAccount = res.data.hasAccount === true;
const certs: WorkerCert[] = res.data.certs || [];
certRows.value = certs.map((c) => ({
_key: certKeySeed++,
@@ -700,6 +723,7 @@ function resetForm() {
formData.status = 1;
formData.rating = 5;
formData.withAccount = false;
formData.hasAccount = false;
certRows.value = [];
if (formRef.value) {
@@ -822,6 +846,8 @@ async function handleSubmit() {
let res: any;
if (isEdit.value && editId.value) {
payload.status = formData.status;
// 补开 APP 账号:仅对尚未开通的电工生效
payload.withAccount = formData.withAccount;
res = await workerRegistryApi.update(editId.value, payload);
} else {
payload.withAccount = formData.withAccount;
@@ -880,6 +906,21 @@ async function handleDelete(id: number) {
}
}
// ==================== 重置密码 ====================
async function handleResetPassword(id: number) {
try {
const res: any = await workerRegistryApi.resetPassword(id);
if (res.code === 200) {
message.success('密码已重置为手机号后 6 位');
fetchList();
} else {
message.error(res.message || '重置密码失败');
}
} catch (error) {
message.error('重置密码失败');
}
}
// ==================== 审核 ====================
function handleAudit(record: WorkerRecord) {
auditRecord.value = record;