feat(v3.4.15): HQ store media edit and package images up to 20

Allow HQ to replace/delete store photos; support multi-image packages with a 20-image cap.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-07 19:24:07 +08:00
parent 0692bebf07
commit 76375eff83
19 changed files with 657 additions and 302 deletions
@@ -1,6 +1,6 @@
import { useState } from 'react';
import type { PackageFormItem } from '../lib/storePackages';
import { STORE_PACKAGE_MAX_COUNT } from '@dukang/shared-types';
import { STORE_PACKAGE_IMAGE_MAX_COUNT, STORE_PACKAGE_MAX_COUNT } from '@dukang/shared-types';
import { emptyPackage } from '../lib/storePackages';
import OssUploadField from './OssUploadField';
@@ -131,13 +131,70 @@ export default function StorePackagesForm({ items, onChange, disabled, embedded
</div>
<div className="partner-field">
<label></label>
<OssUploadField
bizType="STORE_PACKAGE"
value={item.imageUrl || ''}
disabled={disabled}
onChange={(url) => updateAt(index, { imageUrl: url })}
/>
<label> {STORE_PACKAGE_IMAGE_MAX_COUNT} </label>
{(() => {
const slots =
Array.isArray(item.imageUrls) && item.imageUrls.length > 0
? item.imageUrls.map((u) => String(u ?? ''))
: item.imageUrl
? [String(item.imageUrl)]
: [''];
const filled = slots.map((u) => u.trim()).filter(Boolean);
return (
<div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
{slots.map((url, imgIndex) => (
<div key={`${imgIndex}-${url || 'empty'}`}>
<OssUploadField
bizType="STORE_PACKAGE"
value={url || ''}
onChange={(nextUrl) => {
const next = [...slots];
next[imgIndex] = nextUrl?.trim() || '';
const cleaned = next.map((u) => u.trim()).filter(Boolean);
updateAt(index, {
imageUrls: next,
imageUrl: cleaned[0] ?? '',
});
}}
/>
{!disabled && (filled.length > 0 || url) ? (
<button
type="button"
className="partner-packages-remove"
style={{ marginTop: 6 }}
onClick={() => {
const next = slots.filter((_, i) => i !== imgIndex);
const cleaned = next.map((u) => u.trim()).filter(Boolean);
updateAt(index, {
imageUrls: next.length ? next : [],
imageUrl: cleaned[0] ?? '',
});
}}
>
</button>
) : null}
</div>
))}
{!disabled && filled.length < STORE_PACKAGE_IMAGE_MAX_COUNT && slots.length < STORE_PACKAGE_IMAGE_MAX_COUNT ? (
<button
type="button"
className="partner-packages-add"
style={{ marginTop: 0 }}
onClick={() =>
updateAt(index, {
imageUrls: [...slots, ''],
imageUrl: filled[0] ?? '',
})
}
>
<span className="material-symbols-outlined">add_a_photo</span>
{filled.length}/{STORE_PACKAGE_IMAGE_MAX_COUNT}
</button>
) : null}
</div>
);
})()}
</div>
<div className="partner-field">