diff --git a/apps/admin-web/src/components/CityDistrictMultiSelect.tsx b/apps/admin-web/src/components/CityDistrictMultiSelect.tsx
new file mode 100644
index 0000000..779671c
--- /dev/null
+++ b/apps/admin-web/src/components/CityDistrictMultiSelect.tsx
@@ -0,0 +1,42 @@
+import { useMemo } from 'react';
+import { Select } from 'antd';
+import { getDistrictOptionsByCityCode } from '../lib/china-region';
+
+type Props = {
+ cityCode?: string | null;
+ value?: string[];
+ onChange?: (value: string[]) => void;
+ placeholder?: string;
+ disabled?: boolean;
+};
+
+/** 已选开城城市后,仅多选该市下区县(不再选省/市) */
+export default function CityDistrictMultiSelect({
+ cityCode,
+ value,
+ onChange,
+ placeholder,
+ disabled,
+}: Props) {
+ const options = useMemo(() => getDistrictOptionsByCityCode(cityCode), [cityCode]);
+ const ready = Boolean(cityCode) && options.length > 0;
+
+ return (
+
+ );
+}
diff --git a/apps/admin-web/src/components/CityPartnersPanel.tsx b/apps/admin-web/src/components/CityPartnersPanel.tsx
index 85c3f65..f7537fd 100644
--- a/apps/admin-web/src/components/CityPartnersPanel.tsx
+++ b/apps/admin-web/src/components/CityPartnersPanel.tsx
@@ -1,7 +1,6 @@
import { useCallback, useEffect, useState } from 'react';
import {
Button,
- Cascader,
Checkbox,
Drawer,
Form,
@@ -27,8 +26,9 @@ import {
type PartnerPermissionKey,
} from '@dukang/shared-types';
import { request, type Paginated } from '../lib/api';
-import { CHINA_REGION_OPTIONS } from '../lib/china-region';
+import { districtCodeLabel } from '../lib/china-region';
import { fmtTime } from '../lib/constants';
+import CityDistrictMultiSelect from './CityDistrictMultiSelect';
import PartnerSubAccountList from './PartnerSubAccountList';
type PartnerRow = {
@@ -65,6 +65,7 @@ type PartnerDetail = PartnerRow & {
type Props = {
cityId: string;
+ cityCode?: string;
maxPartnerCommissionRate?: number;
onChanged?: () => void;
};
@@ -102,7 +103,21 @@ function commissionSumError(
return null;
}
-export default function CityPartnersPanel({ cityId, maxPartnerCommissionRate = 0.05, onChanged }: Props) {
+function formatApiError(err: unknown): string | null {
+ if (err && typeof err === 'object' && 'errorFields' in err) return null;
+ if (!(err instanceof Error)) return '操作失败';
+ return err.message.replace(/\b(\d{6})\b/g, (code) => {
+ const label = districtCodeLabel(code);
+ return label !== code ? `${label}(${code})` : code;
+ });
+}
+
+export default function CityPartnersPanel({
+ cityId,
+ cityCode,
+ maxPartnerCommissionRate = 0.05,
+ onChanged,
+}: Props) {
const [editForm] = Form.useForm();
const [createForm] = Form.useForm();
const [subForm] = Form.useForm();
@@ -156,29 +171,34 @@ export default function CityPartnersPanel({ cityId, maxPartnerCommissionRate = 0
async function savePartner() {
if (!detail) return;
- const v = await editForm.validateFields();
- const err = commissionSumError(
- Number(v.orderCommissionRate ?? 0),
- Number(v.redeemCommissionRate ?? 0),
- maxPartnerCommissionRate,
- );
- if (err) {
- message.error(err);
- return;
+ try {
+ const v = await editForm.validateFields();
+ const err = commissionSumError(
+ Number(v.orderCommissionRate ?? 0),
+ Number(v.redeemCommissionRate ?? 0),
+ maxPartnerCommissionRate,
+ );
+ if (err) {
+ message.error(err);
+ return;
+ }
+ await request(`/admin/partners/${detail.id}`, {
+ method: 'PUT',
+ body: JSON.stringify({
+ ...v,
+ orderCommissionRate: Number(v.orderCommissionRate ?? 0) / 100,
+ redeemCommissionRate: Number(v.redeemCommissionRate ?? 3) / 100,
+ districtCodes: editScopeType === CityPartnerScopeType.DISTRICT ? flattenDistrictCodes(v.districtCodes) : undefined,
+ }),
+ });
+ message.success('已保存');
+ setDrawerOpen(false);
+ void loadPartners();
+ onChanged?.();
+ } catch (e) {
+ const msg = formatApiError(e);
+ if (msg) message.error(msg);
}
- await request(`/admin/partners/${detail.id}`, {
- method: 'PUT',
- body: JSON.stringify({
- ...v,
- orderCommissionRate: Number(v.orderCommissionRate ?? 0) / 100,
- redeemCommissionRate: Number(v.redeemCommissionRate ?? 3) / 100,
- districtCodes: editScopeType === CityPartnerScopeType.DISTRICT ? flattenDistrictCodes(v.districtCodes) : undefined,
- }),
- });
- message.success('已保存');
- setDrawerOpen(false);
- void loadPartners();
- onChanged?.();
}
async function deleteSubAccount(subId: string) {
@@ -264,8 +284,12 @@ export default function CityPartnersPanel({ cityId, maxPartnerCommissionRate = 0