OAFLOW1 支持负责人本人报销兜底审批
这个提交包含在:
@@ -9,6 +9,7 @@ export type ApprovalRoleCode =
|
||||
| "administration"
|
||||
| "hr"
|
||||
| "admin";
|
||||
export type ApprovalOwnerSelfFallbackRoleCode = "finance_mgr";
|
||||
|
||||
export type ApprovalRule = {
|
||||
id: string;
|
||||
@@ -18,6 +19,7 @@ export type ApprovalRule = {
|
||||
level1Role: ApprovalRoleCode;
|
||||
level2Role: ApprovalRoleCode | null;
|
||||
level3Role: ApprovalRoleCode | null;
|
||||
ownerSelfFallbackRole: ApprovalOwnerSelfFallbackRoleCode | null;
|
||||
enabled: boolean;
|
||||
priority: number;
|
||||
createdAt: string | null;
|
||||
@@ -46,6 +48,7 @@ export type ApprovalRuleCreatePayload = {
|
||||
level1Role: ApprovalRoleCode;
|
||||
level2Role: ApprovalRoleCode | null;
|
||||
level3Role: ApprovalRoleCode | null;
|
||||
ownerSelfFallbackRole: ApprovalOwnerSelfFallbackRoleCode | null;
|
||||
enabled?: boolean;
|
||||
priority?: number;
|
||||
};
|
||||
@@ -65,4 +68,3 @@ export async function patchApprovalRule(approvalRuleId: string, payload: Approva
|
||||
body: JSON.stringify(payload),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
createApprovalRule,
|
||||
fetchApprovalRules,
|
||||
patchApprovalRule,
|
||||
type ApprovalOwnerSelfFallbackRoleCode,
|
||||
type ApprovalRoleCode,
|
||||
type ApprovalRule,
|
||||
} from "../api/approvalRules";
|
||||
@@ -27,6 +28,9 @@ const ROLE_OPTIONS: Array<{ code: ApprovalRoleCode; label: string }> = [
|
||||
{ code: "finance_mgr", label: "finance_mgr" },
|
||||
{ code: "admin", label: "admin" },
|
||||
];
|
||||
const OWNER_SELF_FALLBACK_ROLE_OPTIONS = ROLE_OPTIONS.filter(
|
||||
(role) => role.code === "finance_mgr",
|
||||
);
|
||||
|
||||
function formatAmount(value: string | null) {
|
||||
if (!value) return "-";
|
||||
@@ -44,6 +48,7 @@ export function ApprovalRulesPage() {
|
||||
const [level1Role, setLevel1Role] = useState<ApprovalRoleCode>("manager");
|
||||
const [level2Role, setLevel2Role] = useState<ApprovalRoleCode | "">("");
|
||||
const [level3Role, setLevel3Role] = useState<ApprovalRoleCode | "">("");
|
||||
const [ownerSelfFallbackRole, setOwnerSelfFallbackRole] = useState<ApprovalOwnerSelfFallbackRoleCode | "">("finance_mgr");
|
||||
const [enabled, setEnabled] = useState(true);
|
||||
|
||||
const [editOpen, setEditOpen] = useState(false);
|
||||
@@ -65,10 +70,11 @@ export function ApprovalRulesPage() {
|
||||
level1Role,
|
||||
level2Role: level2Role ? (level2Role as ApprovalRoleCode) : null,
|
||||
level3Role: level3Role ? (level3Role as ApprovalRoleCode) : null,
|
||||
ownerSelfFallbackRole: ownerSelfFallbackRole ? (ownerSelfFallbackRole as ApprovalOwnerSelfFallbackRoleCode) : null,
|
||||
enabled,
|
||||
priority: priority.trim() ? Number(priority) : undefined,
|
||||
};
|
||||
}, [enabled, level1Role, level2Role, level3Role, maxAmount, minAmount, name, priority]);
|
||||
}, [enabled, level1Role, level2Role, level3Role, maxAmount, minAmount, name, ownerSelfFallbackRole, priority]);
|
||||
|
||||
const createMutation = useMutation({
|
||||
mutationFn: () => createApprovalRule(createPayload),
|
||||
@@ -81,6 +87,7 @@ export function ApprovalRulesPage() {
|
||||
setLevel1Role("manager");
|
||||
setLevel2Role("");
|
||||
setLevel3Role("");
|
||||
setOwnerSelfFallbackRole("finance_mgr");
|
||||
setEnabled(true);
|
||||
setError("");
|
||||
toast.success("规则已创建");
|
||||
@@ -141,6 +148,7 @@ export function ApprovalRulesPage() {
|
||||
setLevel1Role(editRule.level1Role);
|
||||
setLevel2Role(editRule.level2Role ?? "");
|
||||
setLevel3Role(editRule.level3Role ?? "");
|
||||
setOwnerSelfFallbackRole(editRule.ownerSelfFallbackRole ?? "");
|
||||
}, [editOpen, editRule]);
|
||||
|
||||
return (
|
||||
@@ -262,6 +270,25 @@ export function ApprovalRulesPage() {
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div className="space-y-1">
|
||||
<Label htmlFor="ownerSelfFallbackRole">负责人本人兜底角色</Label>
|
||||
<select
|
||||
id="ownerSelfFallbackRole"
|
||||
data-testid="approval-rules-create-owner-self-fallback"
|
||||
className="select-std"
|
||||
value={ownerSelfFallbackRole}
|
||||
onChange={(event) => setOwnerSelfFallbackRole(event.target.value as ApprovalOwnerSelfFallbackRoleCode)}
|
||||
>
|
||||
<option value="">无</option>
|
||||
{OWNER_SELF_FALLBACK_ROLE_OPTIONS.map((role) => (
|
||||
<option key={role.code} value={role.code}>
|
||||
{role.label}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
<p className="text-xs text-zinc-500">推荐 finance_mgr;不使用 admin。</p>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2 pt-6">
|
||||
<input
|
||||
id="ruleEnabled"
|
||||
@@ -328,7 +355,10 @@ export function ApprovalRulesPage() {
|
||||
{formatAmount(rule.minAmount)} ~ {rule.maxAmount ? formatAmount(rule.maxAmount) : "∞"}
|
||||
</TableCell>
|
||||
<TableCell className="text-sm text-zinc-700">
|
||||
{[rule.level1Role, rule.level2Role, rule.level3Role].filter(Boolean).join(" -> ") || "-"}
|
||||
<div>{[rule.level1Role, rule.level2Role, rule.level3Role].filter(Boolean).join(" -> ") || "-"}</div>
|
||||
<div className="mt-1 text-xs text-zinc-500">
|
||||
本人兜底:{rule.ownerSelfFallbackRole ?? "无"}
|
||||
</div>
|
||||
</TableCell>
|
||||
<TableCell>{rule.priority}</TableCell>
|
||||
<TableCell className="text-sm">
|
||||
@@ -483,6 +513,24 @@ export function ApprovalRulesPage() {
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div className="space-y-1">
|
||||
<Label htmlFor="editRuleOwnerSelfFallback">负责人本人兜底角色</Label>
|
||||
<select
|
||||
id="editRuleOwnerSelfFallback"
|
||||
data-testid="approval-rule-edit-owner-self-fallback"
|
||||
className="select-std"
|
||||
value={ownerSelfFallbackRole}
|
||||
onChange={(event) => setOwnerSelfFallbackRole(event.target.value as ApprovalOwnerSelfFallbackRoleCode)}
|
||||
>
|
||||
<option value="">无</option>
|
||||
{OWNER_SELF_FALLBACK_ROLE_OPTIONS.map((role) => (
|
||||
<option key={role.code} value={role.code}>
|
||||
{role.label}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2 pt-6">
|
||||
<input
|
||||
id="editRuleEnabled"
|
||||
|
||||
@@ -2,30 +2,6 @@ import { defineConfig } from "vite";
|
||||
import react from "@vitejs/plugin-react";
|
||||
import { VitePWA } from "vite-plugin-pwa";
|
||||
|
||||
const PAGE_CHUNK_PATTERNS = {
|
||||
"page-travel": ["TravelRequestsPage"],
|
||||
"page-assets": ["AssetRegistryPage", "InventoryChecksPage"],
|
||||
"page-procurement": ["PurchaseRequestsPage"],
|
||||
"page-workflow": ["WorkflowTasksPage", "WorkflowTaskDetailPage"],
|
||||
"page-system": [
|
||||
"AuditLogsPage",
|
||||
"ApprovalRulesPage",
|
||||
"DelegationsPage",
|
||||
"DepartmentsPage",
|
||||
"ErpIntegrationPage",
|
||||
"FeatureFlagsPage",
|
||||
"InvoicePoolPage",
|
||||
"LeaveRequestPage",
|
||||
"MasterDataPage",
|
||||
"NotificationChannelsPage",
|
||||
"NotificationsPage",
|
||||
"ReimbursementDetailPage",
|
||||
"ReimbursementsPage",
|
||||
"UserManagementPage",
|
||||
],
|
||||
"page-mobile": ["MobileCapturePage", "MobileAssetScanPage", "MobileApprovalsPage"],
|
||||
} as const;
|
||||
|
||||
function resolveManualChunk(id: string) {
|
||||
if (id.includes("node_modules")) {
|
||||
if (["react", "react-dom", "react-router-dom"].some((pkg) => id.includes(`/node_modules/${pkg}/`))) {
|
||||
@@ -71,14 +47,6 @@ function resolveManualChunk(id: string) {
|
||||
return "app-domain-shared";
|
||||
}
|
||||
|
||||
for (const [chunkName, pageNames] of Object.entries(PAGE_CHUNK_PATTERNS)) {
|
||||
for (const pageName of pageNames) {
|
||||
if (id.includes(`/src/router/lazy/${pageName}.ts`)) {
|
||||
return chunkName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
||||
在新工单中引用
屏蔽一个用户