feat: work-report module V1 — four report types with review flow
客户 2026-07-18 语音需求落地:「报」分四种——优秀案例/操作视频/
完整SOP/有用知识,员工提交、主管或运营审核。
后端(新包 org.dromara.aihr.report):
- aihr_work_report 表(自建,utf8mb4_general_ci 显式声明,含提交人/
审核人/状态/附件 OSS 编号,提交人与状态双索引)
- POST /api/aihr/work-report/reports 员工提交(类型/标题/说明校验)
- POST /attachment 附件上传(≤100MB,OSS 落盘,ossId 字符串安全)
- GET /reports/mine 我的上报;GET /reports 审核队列(canModerate 门禁,
复用社区 Policy);POST /{id}/review APPROVE/REJECT
(仅 PENDING 可审,重复审核 400)
- 审核通过后的入库动作留给运营在案例库/知识库人工完成(不自动入库,
遵守'入知识库必须人工确认'原则)
员工端:
- 新页 pages/user/report(替换'建设中'占位):四类型卡片选择、
标题+说明(说明支持录音转写)、附件选择上传(视频类必传校验)、
我的上报列表(待审核/已通过/已驳回+审核意见)
- 「我」页常用功能入口文案更新
生产验证(端到端):
- API:员工提交→我的上报可见→员工查审核队列403→主管队列可见
→审核通过→重复审核400 全通过
- UI:我页入口→上报页→选类型→填表→提交→列表显示'待审核',
历史记录显示'已通过'+审核意见
- 后端发布(备份 ruoyi-admin.jar.bak-20260718123032,25s恢复),
H5 已发布(备份 h5-before-20260718123032)
二期待办:主管端待审列表页(现主管可用接口审核)、通过后一键转
案例库/知识库、上报采纳积分奖励(等客户定规则)。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
import type { ApiPayload } from '@/types/api';
|
||||
import { apiRequest, apiUrl, authHeaders, readTextPayload } from './api';
|
||||
|
||||
export type WorkReportType = 'CASE' | 'VIDEO' | 'SOP' | 'KNOWLEDGE';
|
||||
|
||||
export interface WorkReportRecord {
|
||||
id: number;
|
||||
type: WorkReportType;
|
||||
typeLabel: string;
|
||||
title: string;
|
||||
content: string;
|
||||
attachmentName?: string;
|
||||
attachmentOssId?: number | string;
|
||||
status: 'PENDING' | 'APPROVED' | 'REJECTED';
|
||||
statusLabel: string;
|
||||
submitterName: string;
|
||||
reviewNote?: string;
|
||||
createdAt: string;
|
||||
reviewedAt?: string;
|
||||
}
|
||||
|
||||
export interface WorkReportAttachment {
|
||||
ossId: number | string;
|
||||
fileName: string;
|
||||
}
|
||||
|
||||
export const workReportTypes: Array<{ value: WorkReportType; label: string; desc: string; icon: string }> = [
|
||||
{ value: 'CASE', label: '优秀案例', desc: '现场处理得好的真实案例', icon: '⭐' },
|
||||
{ value: 'VIDEO', label: '操作视频', desc: '值得推广的操作演示视频', icon: '🎬' },
|
||||
{ value: 'SOP', label: '完整SOP', desc: '整理好的完整工作流程', icon: '📋' },
|
||||
{ value: 'KNOWLEDGE', label: '有用知识', desc: '对工作有帮助的经验知识', icon: '💡' }
|
||||
];
|
||||
|
||||
export const submitWorkReport = (params: {
|
||||
type: WorkReportType;
|
||||
title: string;
|
||||
content: string;
|
||||
attachmentOssId?: number | string;
|
||||
attachmentName?: string;
|
||||
}) =>
|
||||
apiRequest<WorkReportRecord>({
|
||||
url: '/api/aihr/work-report/reports',
|
||||
method: 'POST',
|
||||
data: params,
|
||||
timeout: 20000
|
||||
});
|
||||
|
||||
export const listMyWorkReports = () =>
|
||||
apiRequest<WorkReportRecord[]>({
|
||||
url: '/api/aihr/work-report/reports/mine',
|
||||
method: 'GET',
|
||||
timeout: 15000
|
||||
});
|
||||
|
||||
export const listReviewWorkReports = (status = '') =>
|
||||
apiRequest<WorkReportRecord[]>({
|
||||
url: `/api/aihr/work-report/reports${status ? `?status=${encodeURIComponent(status)}` : ''}`,
|
||||
method: 'GET',
|
||||
timeout: 15000
|
||||
});
|
||||
|
||||
export const reviewWorkReport = (id: number, decision: 'APPROVE' | 'REJECT', note = '') =>
|
||||
apiRequest<WorkReportRecord>({
|
||||
url: `/api/aihr/work-report/reports/${id}/review`,
|
||||
method: 'POST',
|
||||
data: { decision, note },
|
||||
timeout: 20000
|
||||
});
|
||||
|
||||
export const uploadWorkReportAttachment = (filePath: string) =>
|
||||
new Promise<WorkReportAttachment>((resolve, reject) => {
|
||||
uni.uploadFile({
|
||||
url: apiUrl('/api/aihr/work-report/attachment'),
|
||||
filePath,
|
||||
name: 'file',
|
||||
header: authHeaders(false),
|
||||
success: (response) => {
|
||||
try {
|
||||
resolve(readTextPayload<WorkReportAttachment>(response.statusCode, response.data));
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
},
|
||||
fail: () => reject(new Error('附件上传失败'))
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user