B1-B8 建立项目级流程与QA门禁

这个提交包含在:
2026-05-09 01:57:05 +08:00
父节点 555e7b7e29
当前提交 485328426a
共修改 29 个文件,包含 1633 行新增和 102 行删除
+17 -2
查看文件
@@ -25,6 +25,21 @@ import { toast } from "sonner";
const PAYMENT_CHANNEL_OPTIONS: PaymentChannel[] = ["bank", "alipay", "wechat", "cash", "other"];
function batchStatusLabel(value: string) {
if (value === "draft") return "草稿";
if (value === "reviewed") return "已复核";
if (value === "executed") return "已执行";
if (value === "failed") return "执行失败";
if (value === "cancelled") return "已取消";
return value || "-";
}
function entityTypeLabel(value: string) {
if (value === "reimbursement") return "报销";
if (value === "travel_order") return "差旅订单";
return value || "-";
}
function resolveError(error: unknown) {
return isApiError(error)
? `${error.code}: ${mapApiErrorMessage(error.code, error.details, error.message)}`
@@ -342,8 +357,8 @@ export function PaymentBatchesPage() {
</div>
</div>
<div className="grid gap-3 text-sm md:grid-cols-3 xl:grid-cols-6">
<div>状态:{selectedBatch.status}</div>
<div>实体:{selectedBatch.entityType}</div>
<div>状态:{batchStatusLabel(selectedBatch.status)}</div>
<div>实体:{entityTypeLabel(selectedBatch.entityType)}</div>
<div>总金额:{selectedBatch.totalAmount}</div>
<div>默认日期:{selectedBatch.defaultPaymentDate ?? "-"}</div>
<div>默认渠道:{selectedBatch.defaultPaymentChannel ?? "-"}</div>
@@ -7,6 +7,40 @@ import { resolveReimbursementDisplayCode } from "../../lib/reimbursementDisplay"
export type InvoiceBindMode = "reimbursement" | "travelOrder";
const INVOICE_STATUS_LABEL: Record<string, string> = {
pending_ocr: "识别中",
ocr_failed: "识别失败",
available: "可用",
bound: "已绑定",
archived: "已归档",
};
const REIMBURSEMENT_STATUS_LABEL: Record<string, string> = {
draft: "草稿",
reviewing: "审批中",
approved: "已通过",
rejected: "已驳回",
paid: "已支付",
archived: "已归档",
};
const TRAVEL_STATUS_LABEL: Record<string, string> = {
draft: "草稿",
submitted: "已提交",
reviewing: "审批中",
approved: "已通过",
rejected: "已驳回",
confirmed: "已确认",
paid: "已支付",
completed: "已完成",
cancelled: "已取消",
};
function statusLabel(status: string | null | undefined, labels: Record<string, string>) {
if (!status) return "未知状态";
return labels[status] ?? status;
}
export type InvoiceBindCardProps = {
listQuery: UseQueryResult<InvoicePoolListResponse, Error>;
reimbursementQuery: UseQueryResult<ReimbursementListResponse, Error>;
@@ -73,7 +107,7 @@ export function InvoiceBindCard({
<option value="">选择票据</option>
{(listQuery.data?.items ?? []).map((item) => (
<option key={item.id} value={item.id}>
{item.invoiceCode ? `${item.invoiceCode}-${item.invoiceNumber}` : item.id.substring(0, 8)} ({item.status})
{`${item.invoiceCode ? `${item.invoiceCode}-${item.invoiceNumber}` : item.id.substring(0, 8)} (${statusLabel(item.status, INVOICE_STATUS_LABEL)})`}
</option>
))}
</select>
@@ -88,7 +122,7 @@ export function InvoiceBindCard({
<option value="">选择报销单</option>
{(reimbursementQuery.data?.items ?? []).map((item) => (
<option key={item.id} value={item.id}>
{resolveReimbursementDisplayCode(item)} ({item.status})
{resolveReimbursementDisplayCode(item)} ({statusLabel(item.status, REIMBURSEMENT_STATUS_LABEL)})
</option>
))}
</select>
@@ -106,7 +140,7 @@ export function InvoiceBindCard({
<option value="">选择差旅申请</option>
{(travelRequestQuery.data?.items ?? []).map((item) => (
<option key={item.id} value={item.id}>
{item.requestNo ?? item.purpose} ({item.status})
{item.requestNo ?? item.purpose} ({statusLabel(item.status, TRAVEL_STATUS_LABEL)})
</option>
))}
</select>
@@ -121,7 +155,7 @@ export function InvoiceBindCard({
<option value="">选择差旅订单</option>
{bindableTravelOrders.map((item) => (
<option key={item.id} value={item.id}>
{item.externalOrderNo} ({item.status})
{item.externalOrderNo} ({statusLabel(item.status, TRAVEL_STATUS_LABEL)})
</option>
))}
</select>
@@ -306,7 +306,7 @@ export function InvoiceDetailPanel({
onChange={(event) => setEditSetAvailable(event.target.checked)}
type="checkbox"
/>
<span className="text-zinc-700">将状态标记为 available(人工确认可用)</span>
<span className="text-zinc-700">将状态标记为可用(人工确认可用)</span>
</label>
) : null}