审批流程:用户名改为常见人名 + 按销售归属的审批线指派(软约束)
- 用户名改为常见人名(张伟/王芳/李娜/刘洋/陈静/赵磊/孙莉/周强) - 审批线模型:每个销售→指定风控+管理层审批人(含默认线兜底),resolveAssignees 纯函数+4单测 - 提交时按销售归属计算并落库指派(assessment_assignments 表) - 软约束:待办默认只看分给我的(含未指派),同角色他人仍可代审;详情页显示指派审批人 - 审批流程页新增「审批人指派·审批线」配置区(启用/默认线/按销售配线) - 配置 GET/PUT 扩展 assignment;getApprovalConfig 向后兼容回填
This commit is contained in:
@@ -17,8 +17,8 @@ import {
|
||||
space,
|
||||
typographyStyle,
|
||||
} from '../design-system/components/styles.js';
|
||||
import { fetchAssessmentsPage, fetchSummary, archiveAssessment, applyCalibration, listDrafts, deleteDraftApi, API_BASE } from '../api/client.js';
|
||||
import type { AssessmentListItem, WorkflowStatus, DraftItem } from '../api/client.js';
|
||||
import { fetchAssessmentsPage, fetchSummary, archiveAssessment, applyCalibration, listDrafts, deleteDraftApi, fetchAssignments, API_BASE } from '../api/client.js';
|
||||
import type { AssessmentListItem, WorkflowStatus, DraftItem, AssignmentRecord } from '../api/client.js';
|
||||
import { useAuthStore } from '../stores/authStore.js';
|
||||
import { GuideBanner } from '../app/Guidance.js';
|
||||
import { RiskBadge } from '../charts/index.js';
|
||||
@@ -104,6 +104,9 @@ export function Dashboard(): JSX.Element {
|
||||
|
||||
// 待办列表
|
||||
const [todoItems, setTodoItems] = useState<AssessmentListItem[]>([]);
|
||||
// 审批人指派(assessmentId → 记录);待办软过滤用。
|
||||
const [assignments, setAssignments] = useState<Record<string, AssignmentRecord>>({});
|
||||
const [onlyMine, setOnlyMine] = useState(true);
|
||||
// 草稿箱(销售:未运行/未提交的向导进度,服务端持久化)
|
||||
const [drafts, setDrafts] = useState<DraftItem[]>([]);
|
||||
// 统计
|
||||
@@ -160,6 +163,12 @@ export function Dashboard(): JSX.Element {
|
||||
} else {
|
||||
setDrafts([]);
|
||||
}
|
||||
// 审批人指派(风控/管理层待办软过滤用)。
|
||||
if (role === '风控' || role === '管理层') {
|
||||
fetchAssignments().then(setAssignments).catch(() => setAssignments({}));
|
||||
} else {
|
||||
setAssignments({});
|
||||
}
|
||||
}, [role, user?.username]);
|
||||
|
||||
/** 删除一条草稿并刷新草稿箱。 */
|
||||
@@ -507,13 +516,47 @@ export function Dashboard(): JSX.Element {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{todoItems.length > 0 && (
|
||||
<div style={{ marginBottom: `${space(4)}px` }}>
|
||||
<Card title={`待处理 (${todoCount})`}>
|
||||
<Table columns={columns} data={todoItems} getRowKey={(row) => row.id} caption={`${role} 待处理列表`} emptyMessage="当前没有需要你处理的评估" />
|
||||
</Card>
|
||||
</div>
|
||||
)}
|
||||
{todoItems.length > 0 && (() => {
|
||||
const myName = user?.username;
|
||||
const assignedToMe = (id: string): boolean => {
|
||||
const a = assignments[id];
|
||||
if (a === undefined) return false;
|
||||
return role === '风控' ? a.riskReviewerName === myName : role === '管理层' ? a.managerName === myName : false;
|
||||
};
|
||||
const isAssigned = (id: string): boolean => {
|
||||
const a = assignments[id];
|
||||
if (a === undefined) return false;
|
||||
return role === '风控' ? a.riskReviewerName !== null : role === '管理层' ? a.managerName !== null : false;
|
||||
};
|
||||
// 软约束:默认只看分给我的;未指派的也展示(避免遗漏)。
|
||||
const shown = onlyMine ? todoItems.filter((t) => assignedToMe(t.id) || !isAssigned(t.id)) : todoItems;
|
||||
const assignCol: TableColumn<AssessmentListItem> = {
|
||||
key: 'assignee',
|
||||
header: '指派审批人',
|
||||
render: (r) => {
|
||||
const a = assignments[r.id];
|
||||
const name = a !== undefined ? (role === '管理层' ? a.managerName : a.riskReviewerName) : null;
|
||||
if (name === null || name === undefined) return <span style={{ color: colorVar('color.text.secondary') }}>未指派</span>;
|
||||
const mine = name === myName;
|
||||
return <span style={{ ...typographyStyle('caption'), fontWeight: 600, color: mine ? '#15803D' : colorVar('color.text.primary') }}>{name}{mine ? '(我)' : ''}</span>;
|
||||
},
|
||||
};
|
||||
const todoColumns = [...columns.slice(0, -1), assignCol, columns[columns.length - 1]!];
|
||||
return (
|
||||
<div style={{ marginBottom: `${space(4)}px` }}>
|
||||
<Card title={
|
||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', flexWrap: 'wrap', gap: `${space(2)}px` }}>
|
||||
<span>待处理 ({shown.length})</span>
|
||||
<label style={{ display: 'inline-flex', alignItems: 'center', gap: 6, ...typographyStyle('caption'), color: colorVar('color.text.secondary'), fontWeight: 400 }}>
|
||||
<input type="checkbox" checked={onlyMine} onChange={(e) => setOnlyMine(e.target.checked)} /> 只看分给我的(含未指派)
|
||||
</label>
|
||||
</div>
|
||||
}>
|
||||
<Table columns={todoColumns} data={shown} getRowKey={(row) => row.id} caption={`${role} 待处理列表`} emptyMessage={onlyMine ? '没有分给你的待处理项(可取消勾选查看全部)' : '当前没有需要你处理的评估'} />
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
})()}
|
||||
|
||||
{role === '商务/销售' && drafts.length > 0 && (
|
||||
<div style={{ marginBottom: `${space(4)}px` }}>
|
||||
|
||||
Reference in New Issue
Block a user