feat(fulfillment): 同城运费与路由修复,配送单展示商品用户地址
同城 MANUAL/ZZXFX 按小飞侠价规计费,路由查询回退仓配凭证;HQ 配送单补商品、用户和收货地址。门店核销回跳与小程序核销码一并带上。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -11,10 +11,23 @@ import PackageImagesUpload from './PackageImagesUpload';
|
||||
type PackageRow = StorePackageItemDto;
|
||||
|
||||
export type AdminStorePackagesHandle = {
|
||||
/** 套餐已加载时写入;加载中则跳过,避免空数据覆盖线上套餐 */
|
||||
/** 仅在用户改过套餐时写入;加载中或未改动则跳过,避免空表单覆盖刚审核通过的线上套餐 */
|
||||
saveIfLoaded: (opts?: { quiet?: boolean }) => Promise<{ skipped: boolean }>;
|
||||
};
|
||||
|
||||
function mapLiveRows(live: StorePackagesResponse['live']): PackageRow[] {
|
||||
return (live ?? []).map((p, i) => {
|
||||
const imageUrls = normalizeStorePackageImageUrls(p);
|
||||
return {
|
||||
...p,
|
||||
price: String(p.price),
|
||||
imageUrl: imageUrls[0] ?? '',
|
||||
imageUrls,
|
||||
sortOrder: i,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function emptyRow(index = 0): PackageRow {
|
||||
return {
|
||||
name: '',
|
||||
@@ -37,6 +50,7 @@ const AdminStorePackagesSection = forwardRef<AdminStorePackagesHandle, { storeId
|
||||
const [pendingRequest, setPendingRequest] = useState<StorePackagesResponse['pendingRequest']>(null);
|
||||
const itemsRef = useRef(items);
|
||||
const loadingRef = useRef(loading);
|
||||
const dirtyRef = useRef(false);
|
||||
const navigate = useNavigate();
|
||||
|
||||
useEffect(() => {
|
||||
@@ -47,36 +61,33 @@ const AdminStorePackagesSection = forwardRef<AdminStorePackagesHandle, { storeId
|
||||
loadingRef.current = loading;
|
||||
}, [loading]);
|
||||
|
||||
function applyServerPackages(data: StorePackagesResponse) {
|
||||
dirtyRef.current = false;
|
||||
setPendingRequest(data.pendingRequest ?? null);
|
||||
setItems(mapLiveRows(data.live));
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
setLoading(true);
|
||||
setPendingRequest(null);
|
||||
dirtyRef.current = false;
|
||||
request<StorePackagesResponse>(`/admin/stores/${storeId}/packages`)
|
||||
.then((data) => {
|
||||
setPendingRequest(data.pendingRequest ?? null);
|
||||
setItems(
|
||||
data.live?.length
|
||||
? data.live.map((p, i) => {
|
||||
const imageUrls = normalizeStorePackageImageUrls(p);
|
||||
return {
|
||||
...p,
|
||||
price: String(p.price),
|
||||
imageUrl: imageUrls[0] ?? '',
|
||||
imageUrls,
|
||||
sortOrder: i,
|
||||
};
|
||||
})
|
||||
: [],
|
||||
);
|
||||
})
|
||||
.then((data) => applyServerPackages(data))
|
||||
.catch((e) => message.error(e instanceof Error ? e.message : '加载套餐失败'))
|
||||
.finally(() => setLoading(false));
|
||||
}, [storeId]);
|
||||
|
||||
// 在审核页完成审核后,自动刷新本页「有待审核套餐」提醒
|
||||
// 审核通过/驳回后刷新提醒;用户未改套餐时同步线上结果,避免抽屉里仍显示空套餐
|
||||
useEffect(() => {
|
||||
const onChanged = () => {
|
||||
request<StorePackagesResponse>(`/admin/stores/${storeId}/packages`)
|
||||
.then((data) => setPendingRequest(data.pendingRequest ?? null))
|
||||
.then((data) => {
|
||||
setPendingRequest(data.pendingRequest ?? null);
|
||||
if (!dirtyRef.current) {
|
||||
dirtyRef.current = false;
|
||||
setItems(mapLiveRows(data.live));
|
||||
}
|
||||
})
|
||||
.catch(() => undefined);
|
||||
};
|
||||
window.addEventListener(PACKAGE_AUDIT_CHANGED_EVENT, onChanged);
|
||||
@@ -108,16 +119,19 @@ const AdminStorePackagesSection = forwardRef<AdminStorePackagesHandle, { storeId
|
||||
) : null;
|
||||
|
||||
function updateAt(index: number, patch: Partial<PackageRow>) {
|
||||
dirtyRef.current = true;
|
||||
setItems((prev) => prev.map((item, i) => (i === index ? { ...item, ...patch } : item)));
|
||||
}
|
||||
|
||||
function addRow() {
|
||||
if (items.length >= STORE_PACKAGE_MAX_COUNT) return;
|
||||
dirtyRef.current = true;
|
||||
setItems((prev) => [...prev, emptyRow(prev.length)]);
|
||||
}
|
||||
|
||||
function removeAt(index: number) {
|
||||
const run = () => {
|
||||
dirtyRef.current = true;
|
||||
setItems((prev) => {
|
||||
const next = prev.filter((_, i) => i !== index).map((item, i) => ({ ...item, sortOrder: i }));
|
||||
return next.length ? next : [];
|
||||
@@ -203,20 +217,8 @@ const AdminStorePackagesSection = forwardRef<AdminStorePackagesHandle, { storeId
|
||||
}),
|
||||
});
|
||||
if (!opts?.quiet) message.success('套餐已保存并生效');
|
||||
setItems(
|
||||
data.live?.length
|
||||
? data.live.map((p, i) => {
|
||||
const imageUrls = normalizeStorePackageImageUrls(p);
|
||||
return {
|
||||
...p,
|
||||
price: String(p.price),
|
||||
imageUrl: imageUrls[0] ?? '',
|
||||
imageUrls,
|
||||
sortOrder: i,
|
||||
};
|
||||
})
|
||||
: [],
|
||||
);
|
||||
dirtyRef.current = false;
|
||||
setItems(mapLiveRows(data.live));
|
||||
} catch (e) {
|
||||
if (!opts?.quiet) message.error(e instanceof Error ? e.message : '保存失败');
|
||||
throw e;
|
||||
@@ -227,7 +229,7 @@ const AdminStorePackagesSection = forwardRef<AdminStorePackagesHandle, { storeId
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
saveIfLoaded: async (opts) => {
|
||||
if (loadingRef.current) return { skipped: true };
|
||||
if (loadingRef.current || !dirtyRef.current) return { skipped: true };
|
||||
await save(opts);
|
||||
return { skipped: false };
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user