fix(admin): improve promo layout and system settings save UX
CI / verify (pull_request) Has been cancelled

Fix mini home media form persistence; float save with unsaved leave prompt; promo detail QR on the right.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-23 13:54:10 +08:00
parent 479de04f7c
commit d11757c854
5 changed files with 206 additions and 288 deletions
@@ -1,3 +1,4 @@
import { useEffect, useState } from 'react';
import { Button, Space } from 'antd';
import { ArrowDownOutlined, ArrowUpOutlined, DeleteOutlined, PlusOutlined } from '@ant-design/icons';
import {
@@ -8,6 +9,21 @@ import OssUpload from './OssUpload';
const MAX_BANNERS = 8;
/** 编辑态保留空位;下发/入库仍用 parseMiniHomeBanners 过滤空串 */
function parseBannersForEdit(raw?: string | null): string[] {
if (!raw?.trim()) return [];
try {
const parsed = JSON.parse(raw.trim()) as unknown;
if (!Array.isArray(parsed)) return parseMiniHomeBanners(raw);
return parsed
.filter((u): u is string => typeof u === 'string')
.map((u) => u.trim())
.slice(0, MAX_BANNERS);
} catch {
return parseMiniHomeBanners(raw);
}
}
/** Ant Form 控件:单图 URL 字符串(默认 OSS 路径 footer */
export function ConfigImageField({
value,
@@ -22,8 +38,8 @@ export function ConfigImageField({
<OssUpload
bizType={bizType}
mediaType="IMAGE"
value={value}
onChange={onChange}
value={value ?? ''}
onChange={(url) => onChange?.(url ?? '')}
placeholder="上传或粘贴图片 URL"
/>
);
@@ -39,10 +55,17 @@ export function ConfigImageListField({
onChange?: (json: string) => void;
bizType?: string;
}) {
const urls = parseMiniHomeBanners(value);
const [urls, setUrls] = useState<string[]>(() => parseBannersForEdit(value));
useEffect(() => {
setUrls(parseBannersForEdit(value));
}, [value]);
/** 本地可含空位;写入 Form 时去掉空串 */
function commit(next: string[]) {
onChange?.(serializeMiniHomeBanners(next));
const clipped = next.slice(0, MAX_BANNERS);
setUrls(clipped);
onChange?.(serializeMiniHomeBanners(clipped));
}
function updateAt(index: number, url: string) {
@@ -67,13 +90,14 @@ export function ConfigImageListField({
function add() {
if (urls.length >= MAX_BANNERS) return;
commit([...urls, '']);
// 只加本地空位,避免 serialize 过滤空串导致「点击无反应」
setUrls((prev) => [...prev, '']);
}
return (
<Space direction="vertical" style={{ width: '100%' }} size="middle">
{urls.map((url, index) => (
<Space key={`${index}-${url || 'empty'}`} align="start" style={{ width: '100%' }} wrap>
<Space key={`banner-${index}`} align="start" style={{ width: '100%' }} wrap>
<div style={{ flex: 1, minWidth: 240 }}>
<OssUpload
bizType={bizType}