diff --git a/apps/h5-partner/src/pages/WeeklyReportPage.tsx b/apps/h5-partner/src/pages/WeeklyReportPage.tsx
new file mode 100644
index 0000000..ea22d50
--- /dev/null
+++ b/apps/h5-partner/src/pages/WeeklyReportPage.tsx
@@ -0,0 +1,43 @@
+import { useNavigate } from 'react-router-dom';
+import PageHeader from '@dukang/shared-ui/PageHeader';
+
+function fmtMoney(n: number) {
+ return n.toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
+}
+
+/** BUWR-25:经营周报(preV1 占位,待 `/partner/reports/weekly` 接入) */
+export default function WeeklyReportPage() {
+ const navigate = useNavigate();
+
+ return (
+
+
navigate('/')} />
+
+
+
+ 近 7 日 GMV (CNY)
+ {fmtMoney(128450)}
+
+
+
+
+
+ 本周新签门店
+ 3 家
+ 详细趋势图待 API 接入后展示
+
+
+
+ );
+}
diff --git a/apps/h5-partner/src/styles.css b/apps/h5-partner/src/styles.css
index 6d40409..95de32b 100644
--- a/apps/h5-partner/src/styles.css
+++ b/apps/h5-partner/src/styles.css
@@ -581,6 +581,106 @@ html, body, #root {
border-left: 1px solid rgba(226, 190, 188, 0.2);
}
+.partner-form-error {
+ margin: 0 20px 12px;
+ padding: 10px 12px;
+ border-radius: 8px;
+ background: var(--color-error-container);
+ color: var(--color-error);
+ font-size: 14px;
+}
+
+.partner-store-name-list {
+ list-style: none;
+ margin: 0 0 16px;
+ padding: 0;
+ border-bottom: 1px solid rgba(226, 190, 188, 0.15);
+ padding-bottom: 12px;
+}
+
+.partner-store-name-list li {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 8px 0;
+ gap: 12px;
+}
+
+.partner-store-name-list-header {
+ padding-bottom: 4px !important;
+ border-bottom: 1px solid rgba(226, 190, 188, 0.15);
+}
+
+.partner-store-name-list--compact li {
+ padding: 4px 0;
+}
+
+.partner-pending-stores {
+ margin-bottom: 12px;
+ padding-bottom: 12px;
+ border-bottom: 1px solid rgba(226, 190, 188, 0.15);
+}
+
+.partner-field-input--block {
+ width: 100%;
+ padding: 0 16px;
+ min-height: 48px;
+ border: 1px solid rgba(226, 190, 188, 0.3);
+ border-radius: 8px;
+ background: var(--color-background);
+}
+
+.partner-leaderboard-section {
+ padding-top: 12px;
+}
+
+.partner-leaderboard-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 8px;
+}
+
+.partner-leaderboard-row {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ padding: 10px 0;
+ border-bottom: 1px solid rgba(226, 190, 188, 0.1);
+}
+
+.partner-leaderboard-row--compact {
+ padding: 8px 0;
+}
+
+.partner-leaderboard-rank {
+ width: 28px;
+ height: 28px;
+ border-radius: 50%;
+ background: var(--color-surface-container-high);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 12px;
+ font-weight: 700;
+ flex-shrink: 0;
+}
+
+.partner-leaderboard-row:first-of-type .partner-leaderboard-rank {
+ background: var(--color-aged-amber);
+ color: #fff;
+}
+
+.partner-leaderboard-info {
+ flex: 1;
+ min-width: 0;
+}
+
+.partner-leaderboard-stat {
+ text-align: right;
+ flex-shrink: 0;
+}
+
/* ── Partner bottom tabbar ── */
.app-tabbar {
display: grid;
diff --git a/server/dukang-api/src/modules/store/store.service.ts b/server/dukang-api/src/modules/store/store.service.ts
index 46ace49..9811930 100644
--- a/server/dukang-api/src/modules/store/store.service.ts
+++ b/server/dukang-api/src/modules/store/store.service.ts
@@ -124,13 +124,24 @@ export class StoreService {
async partnerDashboard(partnerAccountId: bigint) {
const account = await this.getPartnerAccount(partnerAccountId);
- const [storeCount, orderCount] = await Promise.all([
+ const [storeCount, orderCount, recentStores] = await Promise.all([
this.prisma.store.count({ where: { partnerId: account.partnerId } }),
this.prisma.order.count({
where: { city: { partnerId: account.partnerId } },
}),
+ this.prisma.store.findMany({
+ where: { partnerId: account.partnerId },
+ select: { id: true, name: true, status: true, createdAt: true },
+ orderBy: { createdAt: 'desc' },
+ take: 10,
+ }),
]);
- return { storeCount, orderCount, companyName: account.partner.companyName };
+ return {
+ storeCount,
+ orderCount,
+ companyName: account.partner.companyName,
+ recentStores: serializeBigInt(recentStores),
+ };
}
private async getPartnerAccount(partnerAccountId: bigint) {