B1-B8 建立项目级流程与QA门禁
这个提交包含在:
@@ -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}
|
||||
|
||||
|
||||
在新工单中引用
屏蔽一个用户