feat: 新增现场勘察派单全流程业务能力
1. 新增共享电工管理模块:包含实体、DTO、控制器、服务以及前后端API 2. 重构勘察工单实体与查询逻辑,完善工单全状态流程与字段 3. 新增勘察SOP模板与工单回执业务体系 4. 更新数据库字典与初始化脚本,补充业务数据 5. 新增勘察派单增删改查、指派、复核、催办等全流程接口 6. 修复用户服务角色关联SQL生成问题 7. 新增勘察派单前后端完整交互链路
This commit is contained in:
1 parent
1c83e09fa1
commit
6b00f61550
21 files changed
+4739
-388
No files matched your search
+238
-19
@@ -1,15 +1,143 @@
|
||||
import request from '@/utils/request';
|
||||
|
||||
// 工单状态:0待指派 1现场勘察中 2待技术复核 3已归档 4预留 5已取消
|
||||
export const SURVEY_STATUS = {
|
||||
PENDING_ASSIGN: 0,
|
||||
SURVEYING: 1,
|
||||
PENDING_REVIEW: 2,
|
||||
ARCHIVED: 3,
|
||||
CANCELED: 5
|
||||
};
|
||||
|
||||
export const SURVEY_STATUS_OPTIONS = [
|
||||
{ value: 0, label: '待指派' },
|
||||
{ value: 1, label: '现场勘察中' },
|
||||
{ value: 2, label: '待技术复核' },
|
||||
{ value: 3, label: '已归档' },
|
||||
{ value: 5, label: '已取消' }
|
||||
];
|
||||
|
||||
// 优先级:1特急P1 2加急P2 3常规P3
|
||||
export const SURVEY_PRIORITY = {
|
||||
URGENT: 1,
|
||||
EXPEDITED: 2,
|
||||
REGULAR: 3
|
||||
};
|
||||
|
||||
export const SURVEY_PRIORITY_OPTIONS = [
|
||||
{ value: 1, label: '特急 · P1' },
|
||||
{ value: 2, label: '加急 · P2' },
|
||||
{ value: 3, label: '常规 · P3' }
|
||||
];
|
||||
|
||||
// SOP 规范模板编码与名称
|
||||
export const SOP_TEMPLATE_CODE = 'standard_dist';
|
||||
export const SOP_TEMPLATE_LABEL = '标准配电改造模板';
|
||||
|
||||
// 报酬规则(后端 dispatch_price 未配置时的前端兜底展示值)
|
||||
export const SURVEY_BASE_FEE = 350;
|
||||
export const SURVEY_LOOP_FEE = 5;
|
||||
|
||||
export interface SurveyOrderCustomer {
|
||||
id: number;
|
||||
customerNo?: string;
|
||||
customerName?: string;
|
||||
customerLevel?: string;
|
||||
contactPerson?: string;
|
||||
contactPhone?: string;
|
||||
address?: string;
|
||||
city?: string;
|
||||
salesmanId?: number;
|
||||
}
|
||||
|
||||
export interface SurveyOrderWorker {
|
||||
id: number;
|
||||
workerName?: string;
|
||||
phone?: string;
|
||||
workerTitle?: string;
|
||||
rating?: number;
|
||||
certInfo?: string;
|
||||
certVerified?: boolean;
|
||||
completedCount?: number;
|
||||
}
|
||||
|
||||
export interface SurveySopItem {
|
||||
id: number;
|
||||
templateCode: string;
|
||||
sortOrder: number;
|
||||
itemName: string;
|
||||
itemDesc?: string;
|
||||
// 回执形式:photo 现场照片 / form 清单表单 / meter 测仪截图
|
||||
receiptType: 'photo' | 'form' | 'meter' | string;
|
||||
receiptRequire?: string;
|
||||
required: boolean;
|
||||
}
|
||||
|
||||
export interface SurveyOrderSopResult {
|
||||
id: number;
|
||||
surveyOrderId: number;
|
||||
sopItemId: number;
|
||||
checked: boolean;
|
||||
receiptValue?: string;
|
||||
remark?: string;
|
||||
checkedAt?: string;
|
||||
// 由后端 join 的检查项信息
|
||||
sopItem?: SurveySopItem;
|
||||
itemName?: string;
|
||||
receiptType?: string;
|
||||
receiptRequire?: string;
|
||||
sortOrder?: number;
|
||||
}
|
||||
|
||||
export interface SurveyOrderInfo {
|
||||
id: number;
|
||||
surveyNo: string;
|
||||
customerId: number;
|
||||
customerName?: string;
|
||||
customer?: SurveyOrderCustomer;
|
||||
surveyor?: string;
|
||||
workerId?: number;
|
||||
workerName?: string;
|
||||
worker?: SurveyOrderWorker;
|
||||
workerTitle?: string;
|
||||
workerRating?: number;
|
||||
workerCertInfo?: string;
|
||||
surveyTime?: string;
|
||||
priority?: number;
|
||||
taskTitle?: string;
|
||||
siteLocation?: string;
|
||||
source?: string;
|
||||
loopSpec?: string;
|
||||
loopCount?: number;
|
||||
progressDone?: number;
|
||||
progressTotal?: number;
|
||||
drawingCount?: number;
|
||||
salesmanId?: number;
|
||||
salesmanName?: string;
|
||||
salesmanPhone?: string;
|
||||
techContact?: string;
|
||||
planStart?: string;
|
||||
planEnd?: string;
|
||||
expectMinutes?: number;
|
||||
expectArrivalAt?: string;
|
||||
slaLevel?: string;
|
||||
urgeCount?: number;
|
||||
lastUrgedAt?: string;
|
||||
isDraft?: number;
|
||||
estimatedFee?: number | string;
|
||||
actualFee?: number | string;
|
||||
photos?: string[];
|
||||
reviewRemark?: string;
|
||||
reviewedAt?: string;
|
||||
cancelReason?: string;
|
||||
remark?: string;
|
||||
status?: number;
|
||||
createdAt: string;
|
||||
status: number;
|
||||
createdAt?: string;
|
||||
updatedAt?: string;
|
||||
created_at?: string;
|
||||
updated_at?: string;
|
||||
// 详情附带:SOP 回执
|
||||
sopResults?: SurveyOrderSopResult[];
|
||||
}
|
||||
|
||||
export interface SurveyOrderQuery {
|
||||
@@ -18,6 +146,12 @@ export interface SurveyOrderQuery {
|
||||
keyword?: string;
|
||||
customerId?: number;
|
||||
status?: number;
|
||||
priority?: number;
|
||||
workerId?: number;
|
||||
salesmanId?: number;
|
||||
planStartDate?: string;
|
||||
planEndDate?: string;
|
||||
includeDraft?: number;
|
||||
}
|
||||
|
||||
export interface SurveyOrderListResult {
|
||||
@@ -27,37 +161,122 @@ export interface SurveyOrderListResult {
|
||||
pageSize: number;
|
||||
}
|
||||
|
||||
export interface SurveyOrderCreateParams {
|
||||
customerId: number;
|
||||
surveyor?: string;
|
||||
workerId?: number;
|
||||
surveyTime?: string;
|
||||
remark?: string;
|
||||
status?: number;
|
||||
export interface SurveyOrderStats {
|
||||
total: number;
|
||||
monthGrowth: number;
|
||||
pendingAssign: number;
|
||||
pendingWarn: number;
|
||||
surveying: number;
|
||||
onTimeRate: number;
|
||||
pendingReview: number;
|
||||
avgReviewHours: number;
|
||||
archived: number;
|
||||
planRate: number;
|
||||
priorityCounts: {
|
||||
urgent: number;
|
||||
expedited: number;
|
||||
regular: number;
|
||||
};
|
||||
}
|
||||
|
||||
export interface SurveyOrderUpdateParams {
|
||||
customerId?: number;
|
||||
surveyor?: string;
|
||||
export interface SurveyQuote {
|
||||
baseFee: number;
|
||||
loopFee: number;
|
||||
loopCount: number;
|
||||
total: number;
|
||||
}
|
||||
|
||||
export interface SurveyOrderCreateParams {
|
||||
customerId: number;
|
||||
taskTitle?: string;
|
||||
siteLocation?: string;
|
||||
priority?: number;
|
||||
loopSpec?: string;
|
||||
loopCount?: number;
|
||||
techContact?: string;
|
||||
planStart?: string;
|
||||
planEnd?: string;
|
||||
expectMinutes?: number;
|
||||
slaLevel?: string;
|
||||
workerId?: number;
|
||||
surveyTime?: string;
|
||||
salesmanId?: number;
|
||||
estimatedFee?: number;
|
||||
remark?: string;
|
||||
status?: number;
|
||||
source?: string;
|
||||
isDraft?: number;
|
||||
templateCode?: string;
|
||||
}
|
||||
|
||||
export type SurveyOrderUpdateParams = Partial<SurveyOrderCreateParams>;
|
||||
|
||||
export interface SurveyOrderAssignParams {
|
||||
workerId: number;
|
||||
estimatedFee?: number;
|
||||
}
|
||||
|
||||
export interface SurveyOrderBatchAssignParams {
|
||||
ids: number[];
|
||||
workerId: number;
|
||||
}
|
||||
|
||||
export interface SurveyOrderBatchAssignResult {
|
||||
successCount: number;
|
||||
failCount: number;
|
||||
failIds: number[];
|
||||
}
|
||||
|
||||
export interface SurveyOrderReviewParams {
|
||||
// pass 复核通过归档 / reject 驳回退回现场勘察中
|
||||
result: 'pass' | 'reject';
|
||||
remark?: string;
|
||||
}
|
||||
|
||||
export interface SurveyOrderCancelParams {
|
||||
reason?: string;
|
||||
}
|
||||
|
||||
export const surveyOrderApi = {
|
||||
getList: (params: SurveyOrderQuery) =>
|
||||
request.get<SurveyOrderListResult>('/survey-orders', { params }),
|
||||
|
||||
// KPI 统计卡
|
||||
getStats: () => request.get<SurveyOrderStats>('/survey-orders/stats'),
|
||||
|
||||
// SOP 规范模板检查项
|
||||
getSopItems: (templateCode: string = SOP_TEMPLATE_CODE) =>
|
||||
request.get<SurveySopItem[]>('/survey-orders/sop-items', { params: { templateCode } }),
|
||||
|
||||
// 报酬测算:基础出场工时费 + 回路数 × 单价
|
||||
getQuote: (loopCount: number) =>
|
||||
request.get<SurveyQuote>('/survey-orders/quote', { params: { loopCount } }),
|
||||
|
||||
getDetail: (id: number) =>
|
||||
request.get<SurveyOrderInfo>(`/survey-orders/${id}`),
|
||||
|
||||
create: (data: SurveyOrderCreateParams) =>
|
||||
request.post('/survey-orders', data),
|
||||
request.post<SurveyOrderInfo>('/survey-orders', data),
|
||||
|
||||
update: (id: number, data: SurveyOrderUpdateParams) =>
|
||||
request.put(`/survey-orders/${id}`, data),
|
||||
request.put<SurveyOrderInfo>(`/survey-orders/${id}`, data),
|
||||
|
||||
delete: (id: number) =>
|
||||
request.delete(`/survey-orders/${id}`),
|
||||
};
|
||||
// 指派 / 改派共享电工
|
||||
assign: (id: number, data: SurveyOrderAssignParams) =>
|
||||
request.put<SurveyOrderInfo>(`/survey-orders/${id}/assign`, data),
|
||||
|
||||
// 批量指定电工
|
||||
batchAssign: (data: SurveyOrderBatchAssignParams) =>
|
||||
request.post<SurveyOrderBatchAssignResult>('/survey-orders/batch-assign', data),
|
||||
|
||||
// 技术复核:通过归档 / 驳回退回现场
|
||||
review: (id: number, data: SurveyOrderReviewParams) =>
|
||||
request.put<SurveyOrderInfo>(`/survey-orders/${id}/review`, data),
|
||||
|
||||
// 催办
|
||||
urge: (id: number) => request.post<SurveyOrderInfo>(`/survey-orders/${id}/urge`),
|
||||
|
||||
// 取消工单
|
||||
cancel: (id: number, data: SurveyOrderCancelParams) =>
|
||||
request.put<SurveyOrderInfo>(`/survey-orders/${id}/cancel`, data),
|
||||
|
||||
delete: (id: number) => request.delete(`/survey-orders/${id}`)
|
||||
};
|
||||
@@ -0,0 +1,66 @@
|
||||
import request from '@/utils/request';
|
||||
|
||||
export interface WorkerInfo {
|
||||
id: number;
|
||||
workerName: string;
|
||||
phone?: string;
|
||||
idCard?: string;
|
||||
workerTitle?: string;
|
||||
rating?: number | string;
|
||||
certInfo?: string;
|
||||
certVerified?: boolean;
|
||||
completedCount?: number;
|
||||
status?: number;
|
||||
createdAt?: string;
|
||||
updatedAt?: string;
|
||||
}
|
||||
|
||||
export interface WorkerOption {
|
||||
id: number;
|
||||
workerName: string;
|
||||
workerTitle?: string;
|
||||
rating?: number | string;
|
||||
certInfo?: string;
|
||||
certVerified?: boolean;
|
||||
completedCount?: number;
|
||||
}
|
||||
|
||||
export interface WorkerRecommend extends WorkerOption {
|
||||
// 匹配度(0-100,保留 1 位小数)
|
||||
matchRate: number;
|
||||
rank: number;
|
||||
// 是否为「首选推荐」
|
||||
isTop: boolean;
|
||||
}
|
||||
|
||||
export interface WorkerQuery {
|
||||
page?: number;
|
||||
pageSize?: number;
|
||||
limit?: number;
|
||||
keyword?: string;
|
||||
status?: number;
|
||||
}
|
||||
|
||||
export interface WorkerListResult {
|
||||
list: WorkerInfo[];
|
||||
total: number;
|
||||
page: number;
|
||||
pageSize: number;
|
||||
}
|
||||
|
||||
export interface WorkerRecommendQuery {
|
||||
loopCount?: number;
|
||||
priority?: number;
|
||||
certKeyword?: string;
|
||||
}
|
||||
|
||||
export const workerApi = {
|
||||
getList: (params?: WorkerQuery) => request.get<WorkerListResult>('/workers', { params }),
|
||||
|
||||
// 下拉选项(仅在岗电工,按评分倒序)
|
||||
getOptions: () => request.get<WorkerOption[]>('/workers/options'),
|
||||
|
||||
// 智能推荐电工:评分 50% + 资质 30% + 经验 20% 加权排序
|
||||
getRecommend: (params?: WorkerRecommendQuery) =>
|
||||
request.get<WorkerRecommend[]>('/workers/recommend', { params })
|
||||
};
|
||||
@@ -97,6 +97,30 @@ const routes: RouteRecordRaw[] = [
|
||||
component: () => import("@/views/project/survey/index.vue"),
|
||||
meta: { requiresAuth: true, title: "项目勘察", permission: "project:survey" }
|
||||
},
|
||||
{
|
||||
path: "project/survey/create",
|
||||
name: "ProjectSurveyCreate",
|
||||
component: () => import("@/views/project/survey/form.vue"),
|
||||
meta: {
|
||||
requiresAuth: true,
|
||||
title: "新建勘察派单",
|
||||
permission: "project:survey",
|
||||
parentTitle: "现场勘察派单",
|
||||
parentPath: "/project/survey"
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "project/survey/edit/:id",
|
||||
name: "ProjectSurveyEdit",
|
||||
component: () => import("@/views/project/survey/form.vue"),
|
||||
meta: {
|
||||
requiresAuth: true,
|
||||
title: "编辑勘察派单",
|
||||
permission: "project:survey",
|
||||
parentTitle: "现场勘察派单",
|
||||
parentPath: "/project/survey"
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "project/plan",
|
||||
name: "ProjectPlan",
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user