- {(detail.deliveryType === 'CROSS_CITY' || !detail.fulfillmentWarehouseId) && (
- <>
- 总部快递填单
-
- 适用于跨城订单或同城无仓订单
-
-
-
-
-
-
-
-
-
-
-
-
- >
- )}
-
- {detail.fulfillmentWarehouseId && (
- <>
-
- 仓配小飞侠重试
-
-
- 仓配订单通常支付后自动推单;失败时可手动重试
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- >
- ),
- }]}
- />
-
-
- >
- )}
-
- )}
-
{detail.statusLogs && detail.statusLogs.length > 0 && (
<>
小飞侠接口联调
- 通过 HQ 后台直接调用后端封装的小飞侠 API(cmd 100101~100301)。请确保服务器已配置
- {' '}XIAOFEIXIA_MCH_ID、XIAOFEIXIA_API_KEY。
+ 通过 HQ 后台直接调用后端封装的小飞侠 API(cmd 100101~100301)。凭证请在
+ 仓配管理
+ 中配置;联调会优先使用仓配管理里启用的小飞侠承运商。
{config && (
@@ -132,13 +136,17 @@ export default function XiaofeixiaTestPage() {
{config.ready ? 可调用 : 配置不完整}
+
+ {config.source === 'fulfillment_provider' ? '仓配管理' : '环境变量(兼容)'}
+ {config.hint ? ` · ${config.hint}` : ''}
+
{!config.ready && (
)}
diff --git a/apps/h5-partner/src/PartnerAppRoutes.tsx b/apps/h5-partner/src/PartnerAppRoutes.tsx
index 3c0f461..f7edfc6 100644
--- a/apps/h5-partner/src/PartnerAppRoutes.tsx
+++ b/apps/h5-partner/src/PartnerAppRoutes.tsx
@@ -13,7 +13,6 @@ import CenterPage from './pages/CenterPage';
import PartnerMePage from './pages/PartnerMePage';
import BillsPage from './pages/BillsPage';
import SettlementPage from './pages/SettlementPage';
-import ReshipPage from './pages/ReshipPage';
import WeeklyReportPage from './pages/WeeklyReportPage';
import ProxyOrderPage from './pages/ProxyOrderPage';
import StaffListPage from './pages/StaffListPage';
@@ -34,7 +33,6 @@ function PrimaryRoutes() {
} />
} />
} />
-
} />
} />
} />
} />
diff --git a/apps/h5-partner/src/contexts/PartnerSessionContext.tsx b/apps/h5-partner/src/contexts/PartnerSessionContext.tsx
index dd20eb2..197c14f 100644
--- a/apps/h5-partner/src/contexts/PartnerSessionContext.tsx
+++ b/apps/h5-partner/src/contexts/PartnerSessionContext.tsx
@@ -53,6 +53,10 @@ function accountFromProfile(profile: PartnerSessionProfile): PartnerAccount {
primaryPhone: profile.primaryPhone,
primaryName: profile.primaryName,
hasWechat: profile.hasWechat,
+ wxNickname: profile.wxNickname,
+ wxAvatarUrl: profile.wxAvatarUrl,
+ managedWarehouseId: profile.managedWarehouseId,
+ hasWarehouseAccess: profile.hasWarehouseAccess,
};
}
diff --git a/apps/h5-partner/src/lib/api.ts b/apps/h5-partner/src/lib/api.ts
index 232f0fc..2c7c8cb 100644
--- a/apps/h5-partner/src/lib/api.ts
+++ b/apps/h5-partner/src/lib/api.ts
@@ -24,7 +24,20 @@ const AUTH_RECOVERY_EXEMPT_PATHS = [
export type PartnerSessionProfile = Pick<
PartnerMe,
- 'id' | 'name' | 'phone' | 'companyName' | 'isPrimary' | 'permissions' | 'primaryAccountId' | 'primaryPhone' | 'primaryName' | 'hasWechat'
+ | 'id'
+ | 'name'
+ | 'phone'
+ | 'companyName'
+ | 'isPrimary'
+ | 'permissions'
+ | 'primaryAccountId'
+ | 'primaryPhone'
+ | 'primaryName'
+ | 'hasWechat'
+ | 'wxNickname'
+ | 'wxAvatarUrl'
+ | 'managedWarehouseId'
+ | 'hasWarehouseAccess'
> & {
staffRole?: PartnerStaffRole;
};
@@ -112,6 +125,10 @@ function profileFromMe(me: PartnerMe): PartnerSessionProfile {
primaryPhone: me.primaryPhone,
primaryName: me.primaryName,
hasWechat: me.hasWechat,
+ wxNickname: me.wxNickname,
+ wxAvatarUrl: me.wxAvatarUrl,
+ managedWarehouseId: me.managedWarehouseId,
+ hasWarehouseAccess: me.hasWarehouseAccess,
};
}
diff --git a/apps/h5-partner/src/lib/partnerAccess.ts b/apps/h5-partner/src/lib/partnerAccess.ts
index fd6c5cc..a8b32ca 100644
--- a/apps/h5-partner/src/lib/partnerAccess.ts
+++ b/apps/h5-partner/src/lib/partnerAccess.ts
@@ -35,6 +35,15 @@ export function getPartnerNavKind(account: PartnerMe | null | undefined): Partne
return 'store_staff';
}
+/** 是否已配置管仓:主账号看 hasWarehouseAccess;子账号还需有仓库相关权限 */
+export function hasWarehouseAccess(account: PartnerMe | null | undefined): boolean {
+ if (!account) return false;
+ if (account.hasWarehouseAccess === false) return false;
+ if (account.hasWarehouseAccess === true) return true;
+ // 旧缓存无字段时保守:主账号未知视为无,避免误展示全城订单
+ return false;
+}
+
export function partnerHomePath(account: PartnerMe | null | undefined): string {
return '/';
}
diff --git a/apps/h5-partner/src/lib/wechat-auth.ts b/apps/h5-partner/src/lib/wechat-auth.ts
index 47e721a..5a14d7b 100644
--- a/apps/h5-partner/src/lib/wechat-auth.ts
+++ b/apps/h5-partner/src/lib/wechat-auth.ts
@@ -14,6 +14,8 @@ export type PartnerProfile = {
phone: string;
companyName?: string;
hasWechat?: boolean;
+ wxNickname?: string | null;
+ wxAvatarUrl?: string | null;
isPrimary?: boolean;
};
@@ -82,6 +84,8 @@ export function sessionFromWechatLogin(result: WechatLoginResult): PartnerSessio
companyName: partner.companyName ? String(partner.companyName) : undefined,
isPrimary: partner.isPrimary !== false,
hasWechat: true,
+ wxNickname: partner.wxNickname ? String(partner.wxNickname) : undefined,
+ wxAvatarUrl: partner.wxAvatarUrl ? String(partner.wxAvatarUrl) : undefined,
}
: undefined,
};
diff --git a/apps/h5-partner/src/pages/BillsPage.tsx b/apps/h5-partner/src/pages/BillsPage.tsx
index 2132f08..9f6ad20 100644
--- a/apps/h5-partner/src/pages/BillsPage.tsx
+++ b/apps/h5-partner/src/pages/BillsPage.tsx
@@ -1,42 +1,96 @@
-import { useEffect, useState } from 'react';
+import { useEffect, useMemo, useState } from 'react';
import { useNavigate, useSearchParams } from 'react-router-dom';
import PageHeader from '@dukang/shared-ui/PageHeader';
+import type { PartnerBillDto } from '@dukang/shared-types';
import { isLoggedIn, request } from '../lib/api';
+import { toastError, toastSuccess } from '../lib/toast';
+
+function fmtMoney(n: number) {
+ return n.toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
+}
+
+function billStatusLabel(status: string) {
+ switch (status) {
+ case 'DRAFT': return '待确认';
+ case 'CONFIRMED': return '审核中';
+ case 'PAID': return '已结算';
+ case 'REJECTED': return '已驳回';
+ default: return status;
+ }
+}
+
+function canApplyPayment(status: string) {
+ return status === 'DRAFT' || status === 'REJECTED';
+}
export default function BillsPage() {
const navigate = useNavigate();
const [searchParams] = useSearchParams();
- const [bills, setBills] = useState
>>([]);
+ const [bills, setBills] = useState([]);
const [confirmed, setConfirmed] = useState(false);
const [submitting, setSubmitting] = useState(false);
+ const [loading, setLoading] = useState(true);
+
+ async function loadBills() {
+ setLoading(true);
+ try {
+ const list = await request('PARTNER_H5', '/partner/settlement/bills');
+ setBills(Array.isArray(list) ? list : []);
+ } catch {
+ setBills([]);
+ } finally {
+ setLoading(false);
+ }
+ }
useEffect(() => {
if (!isLoggedIn()) { navigate('/login'); return; }
- request>>('PARTNER_H5', '/partner/settlement/bills').then(setBills);
+ void loadBills();
}, [navigate]);
const billId = searchParams.get('id');
+ const actionable = useMemo(
+ () => bills.filter((b) => canApplyPayment(b.status)),
+ [bills],
+ );
const bill = billId
- ? bills.find((b) => String(b.id) === billId) ?? bills[0]
- : bills[0];
+ ? bills.find((b) => String(b.id) === billId) ?? actionable[0] ?? bills[0]
+ : actionable[0] ?? bills[0];
- function confirmBill() {
- if (!confirmed) return;
+ const status = String(bill?.status || '');
+ const showApplyForm = !!bill && canApplyPayment(status);
+ const isReviewing = status === 'CONFIRMED';
+ const isRejected = status === 'REJECTED';
+ const isPaid = status === 'PAID';
+
+ async function confirmBill() {
+ if (!bill || !confirmed || !canApplyPayment(String(bill.status))) return;
setSubmitting(true);
- setTimeout(() => {
+ try {
+ await request('PARTNER_H5', `/partner/settlement/bills/${bill.id}/confirm`, {
+ method: 'POST',
+ });
+ toastSuccess('申请已提交,请耐心等待总部打款审核');
+ setConfirmed(false);
+ await loadBills();
+ } catch (e) {
+ toastError(e instanceof Error ? e.message : '提交失败');
+ } finally {
setSubmitting(false);
- alert('申请已提交,请耐心等待打款审核。(preV1 演示)');
- }, 1500);
+ }
}
return (
-
navigate('/center/settlement')} />
+ navigate('/center/settlement')} />
@@ -45,17 +99,28 @@ export default function BillsPage() {
数据核算
-
2
+
+ {showApplyForm && !confirmed ? '2' : (
+ check
+ )}
+
账单确认
-
3
-
申请打款
+
+ {isPaid ? (
+ check
+ ) : '3'}
+
+
+ {isRejected ? '已驳回' : isPaid ? '已打款' : '申请打款'}
+
- {!bill &&
暂无账单
}
+ {loading &&
加载中…
}
+ {!loading && !bill &&
暂无账单
}
{bill && (
@@ -66,25 +131,46 @@ export default function BillsPage() {
SETTLEMENT PERIOD
{String(bill.billNo || '月度结算账单')}
- {String(bill.status || '待确认')}
+
+ {billStatusLabel(status)}
+
+ {isRejected && bill.rejectReason && (
+
+
error
+
+
打款申请已驳回
+
{bill.rejectReason}
+
+
+ )}
+
+ {isReviewing && (
+
+
hourglass_top
+
+ 您已提交打款申请,总部审核中,请耐心等待。
+
+
+ )}
+
应结总金额
¥
- {Number(bill.totalAmount || 0).toLocaleString('zh-CN', { minimumFractionDigits: 2 })}
+ {fmtMoney(Number(bill.totalAmount || 0))}
订单分佣收入
- ¥ {(Number(bill.totalAmount || 0) * 0.82).toLocaleString('zh-CN', { minimumFractionDigits: 2 })}
+ ¥ {fmtMoney(Number(bill.orderCommission || 0))}
核销权益分佣
- ¥ {(Number(bill.totalAmount || 0) * 0.18).toLocaleString('zh-CN', { minimumFractionDigits: 2 })}
+ ¥ {fmtMoney(Number(bill.redeemCommission || 0))}
@@ -93,45 +179,58 @@ export default function BillsPage() {
{bills.length > 1 && (