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:
@@ -23,11 +23,10 @@ import {
|
||||
} from 'antd';
|
||||
import type { ColumnsType } from 'antd/es/table';
|
||||
import type { FormInstance } from 'antd/es/form';
|
||||
import { FilePdfOutlined, LinkOutlined, EnvironmentOutlined } from '@ant-design/icons';
|
||||
import { EnvironmentOutlined } from '@ant-design/icons';
|
||||
import { request, type Paginated } from '../lib/api';
|
||||
import {
|
||||
ADMIN_OPTIONS_PAGE_SIZE,
|
||||
RESOURCE_BIZ_TYPE_LABELS,
|
||||
STORE_AUDIT_STATUS_LABELS,
|
||||
STORE_STATUS_LABELS,
|
||||
fmtTime,
|
||||
@@ -82,18 +81,6 @@ type StoreMediaItem = {
|
||||
url?: string | null;
|
||||
};
|
||||
|
||||
function isImageMedia(url: string, mediaType?: string) {
|
||||
if (mediaType === 'IMAGE') return true;
|
||||
if (mediaType === 'VIDEO' || mediaType === 'FILE') {
|
||||
return /\.(png|jpe?g|gif|webp|bmp|heic)(\?|#|$)/i.test(url);
|
||||
}
|
||||
return /\.(png|jpe?g|gif|webp|bmp|heic)(\?|#|$)/i.test(url);
|
||||
}
|
||||
|
||||
function isPdfUrl(url: string) {
|
||||
return /\.pdf(\?|#|$)/i.test(url);
|
||||
}
|
||||
|
||||
function collectMediaUrls(detail: Record<string, unknown>) {
|
||||
const media = Array.isArray(detail.media) ? (detail.media as StoreMediaItem[]) : [];
|
||||
const byType = (bizType: string) =>
|
||||
@@ -119,161 +106,57 @@ function collectMediaUrls(detail: Record<string, unknown>) {
|
||||
};
|
||||
}
|
||||
|
||||
function StoreAuditMediaSection({ detail }: { detail: Record<string, unknown> }) {
|
||||
const { covers, envs, contracts } = collectMediaUrls(detail);
|
||||
const [pdfUrl, setPdfUrl] = useState<string | null>(null);
|
||||
const gallery = [...covers, ...envs].filter((item) => isImageMedia(item.url, item.mediaType));
|
||||
|
||||
if (covers.length === 0 && envs.length === 0 && contracts.length === 0) {
|
||||
return (
|
||||
<Alert
|
||||
type="warning"
|
||||
showIcon
|
||||
style={{ marginBottom: 16 }}
|
||||
message="暂无门头照 / 环境照 / 签约合同,请谨慎审核"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function StoreAuditMediaEditor() {
|
||||
return (
|
||||
<div style={{ marginBottom: 16 }}>
|
||||
<Typography.Title level={5} style={{ marginTop: 0, marginBottom: 12 }}>
|
||||
审核材料
|
||||
</Typography.Title>
|
||||
|
||||
{(covers.length > 0 || envs.length > 0) && (
|
||||
<div style={{ marginBottom: 16 }}>
|
||||
<Typography.Text type="secondary" style={{ display: 'block', marginBottom: 8 }}>
|
||||
门头照 / 环境照(点击可放大浏览)
|
||||
</Typography.Text>
|
||||
<Image.PreviewGroup>
|
||||
<Space wrap size={12}>
|
||||
{gallery.map((item) => (
|
||||
<div key={item.id} style={{ textAlign: 'center' }}>
|
||||
<Image
|
||||
src={item.url}
|
||||
width={112}
|
||||
height={84}
|
||||
style={{ objectFit: 'cover', borderRadius: 6, border: '1px solid #f0f0f0' }}
|
||||
/>
|
||||
<div style={{ fontSize: 12, color: '#8c8c8c', marginTop: 4 }}>
|
||||
{covers.some((c) => c.id === item.id) ? '门头照' : '环境照'}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</Space>
|
||||
</Image.PreviewGroup>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{contracts.length > 0 && (
|
||||
<div>
|
||||
<Typography.Text type="secondary" style={{ display: 'block', marginBottom: 8 }}>
|
||||
签约合同
|
||||
</Typography.Text>
|
||||
<Space direction="vertical" size={12} style={{ width: '100%' }}>
|
||||
{contracts.map((item, index) => {
|
||||
const imageLike = isImageMedia(item.url, item.mediaType);
|
||||
const pdf = isPdfUrl(item.url);
|
||||
return (
|
||||
<div
|
||||
key={item.id || `${item.url}-${index}`}
|
||||
style={{
|
||||
display: 'flex',
|
||||
gap: 12,
|
||||
alignItems: 'center',
|
||||
padding: 12,
|
||||
border: '1px solid #f0f0f0',
|
||||
borderRadius: 8,
|
||||
background: '#fafafa',
|
||||
}}
|
||||
<Alert
|
||||
type="info"
|
||||
showIcon
|
||||
style={{ marginBottom: 16 }}
|
||||
message="可替换或删除门头照 / 环境照 / 签约合同,点击右上角「保存修改」后生效。环境照最多 20 张。"
|
||||
/>
|
||||
<Form.Item name="coverUrl" label="门头照">
|
||||
<OssUpload bizType="STORE_TITLE" mediaType="IMAGE" />
|
||||
</Form.Item>
|
||||
<Typography.Text strong>环境照片</Typography.Text>
|
||||
<Typography.Paragraph type="secondary" style={{ marginTop: 4 }}>
|
||||
建议至少 3 张;可替换、删除,最多 20 张
|
||||
</Typography.Paragraph>
|
||||
<Form.List name="envPhotoUrls">
|
||||
{(fields, { add, remove }) => (
|
||||
<div style={{ marginTop: 8 }}>
|
||||
{fields.map((field, index) => (
|
||||
<div key={field.key} style={{ display: 'flex', gap: 8, alignItems: 'flex-start' }}>
|
||||
<Form.Item
|
||||
name={field.name}
|
||||
label={`环境图 ${index + 1}`}
|
||||
style={{ flex: 1, marginBottom: 12 }}
|
||||
>
|
||||
{imageLike ? (
|
||||
<Image.PreviewGroup>
|
||||
<Image
|
||||
src={item.url}
|
||||
width={96}
|
||||
height={72}
|
||||
style={{ objectFit: 'cover', borderRadius: 6 }}
|
||||
/>
|
||||
</Image.PreviewGroup>
|
||||
) : (
|
||||
<div
|
||||
style={{
|
||||
width: 96,
|
||||
height: 72,
|
||||
borderRadius: 6,
|
||||
background: '#fff',
|
||||
border: '1px dashed #d9d9d9',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
color: '#cf1322',
|
||||
}}
|
||||
>
|
||||
<FilePdfOutlined style={{ fontSize: 28 }} />
|
||||
</div>
|
||||
)}
|
||||
<Space direction="vertical" size={4} style={{ flex: 1, minWidth: 0 }}>
|
||||
<Typography.Text strong>
|
||||
{RESOURCE_BIZ_TYPE_LABELS.CONTRACT || '合同'}
|
||||
{contracts.length > 1 ? ` ${index + 1}` : ''}
|
||||
</Typography.Text>
|
||||
<Typography.Text type="secondary" ellipsis style={{ maxWidth: '100%' }}>
|
||||
{item.url}
|
||||
</Typography.Text>
|
||||
<Space wrap>
|
||||
{imageLike ? (
|
||||
<Typography.Text type="secondary">点击缩略图放大查看</Typography.Text>
|
||||
) : null}
|
||||
{pdf ? (
|
||||
<Button type="link" size="small" style={{ padding: 0 }} onClick={() => setPdfUrl(item.url)}>
|
||||
页内预览 PDF
|
||||
</Button>
|
||||
) : null}
|
||||
<Button
|
||||
type="link"
|
||||
size="small"
|
||||
icon={<LinkOutlined />}
|
||||
style={{ padding: 0 }}
|
||||
href={item.url}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
新窗口打开
|
||||
</Button>
|
||||
</Space>
|
||||
</Space>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</Space>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<Modal
|
||||
title="合同预览"
|
||||
open={!!pdfUrl}
|
||||
onCancel={() => setPdfUrl(null)}
|
||||
width={900}
|
||||
footer={[
|
||||
<Button key="open" href={pdfUrl || undefined} target="_blank" rel="noreferrer">
|
||||
新窗口打开
|
||||
</Button>,
|
||||
<Button key="close" type="primary" onClick={() => setPdfUrl(null)}>
|
||||
关闭
|
||||
</Button>,
|
||||
]}
|
||||
destroyOnClose
|
||||
>
|
||||
{pdfUrl ? (
|
||||
<iframe
|
||||
title="合同 PDF 预览"
|
||||
src={pdfUrl}
|
||||
style={{ width: '100%', height: '70vh', border: 'none', borderRadius: 8 }}
|
||||
/>
|
||||
) : null}
|
||||
</Modal>
|
||||
<OssUpload bizType="STORE_ENV" mediaType="IMAGE" />
|
||||
</Form.Item>
|
||||
<Button
|
||||
type="link"
|
||||
danger
|
||||
style={{ marginTop: 30 }}
|
||||
onClick={() => remove(field.name)}
|
||||
>
|
||||
删除
|
||||
</Button>
|
||||
</div>
|
||||
))}
|
||||
{fields.length < 20 ? (
|
||||
<Button type="dashed" onClick={() => add('')} block>
|
||||
添加环境照片
|
||||
</Button>
|
||||
) : (
|
||||
<Typography.Text type="secondary">已达上限 20 张</Typography.Text>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</Form.List>
|
||||
<Form.Item name="contractUrl" label="签约合同" style={{ marginTop: 16 }}>
|
||||
<OssUpload bizType="STORE_CONTRACT" mediaType="FILE" accept="image/*,.pdf" />
|
||||
</Form.Item>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -518,6 +401,15 @@ export default function StoresPage() {
|
||||
? String(d.benefitUsageRule)
|
||||
: '',
|
||||
coverUrl: d.coverUrl,
|
||||
envPhotoUrls: (() => {
|
||||
const { envs } = collectMediaUrls(d);
|
||||
const urls = envs.map((item) => item.url).filter(Boolean);
|
||||
return urls.length ? urls : [''];
|
||||
})(),
|
||||
contractUrl: (() => {
|
||||
const { contracts } = collectMediaUrls(d);
|
||||
return contracts[0]?.url || '';
|
||||
})(),
|
||||
province: d.province,
|
||||
city: d.cityName,
|
||||
district: d.district,
|
||||
@@ -557,7 +449,11 @@ export default function StoresPage() {
|
||||
const payload = {
|
||||
name: v.name,
|
||||
phone: v.phone,
|
||||
coverUrl: v.coverUrl,
|
||||
coverUrl: v.coverUrl ?? '',
|
||||
envPhotoUrls: Array.isArray(v.envPhotoUrls)
|
||||
? v.envPhotoUrls.map((u: string) => String(u || '').trim()).filter(Boolean)
|
||||
: [],
|
||||
contractUrl: v.contractUrl ?? '',
|
||||
intro: v.intro,
|
||||
benefitUsageRule:
|
||||
typeof v.benefitUsageRule === 'string' &&
|
||||
@@ -1174,7 +1070,8 @@ export default function StoresPage() {
|
||||
{
|
||||
key: 'media',
|
||||
label: '审核材料',
|
||||
children: <StoreAuditMediaSection detail={detail} />,
|
||||
forceRender: true,
|
||||
children: <StoreAuditMediaEditor />,
|
||||
},
|
||||
{
|
||||
key: 'packages',
|
||||
@@ -1366,19 +1263,28 @@ export default function StoresPage() {
|
||||
</Form.Item>
|
||||
<Typography.Text strong>环境照片</Typography.Text>
|
||||
<Typography.Paragraph type="secondary" style={{ marginTop: 4 }}>
|
||||
至少 3 张,可继续添加
|
||||
至少 3 张,可继续添加,最多 20 张
|
||||
</Typography.Paragraph>
|
||||
<Form.List name="envPhotoUrls">
|
||||
{(fields, { add }) => (
|
||||
{(fields, { add, remove }) => (
|
||||
<div style={{ marginTop: 8 }}>
|
||||
{fields.map((field, index) => (
|
||||
<Form.Item key={field.key} name={field.name} label={`环境图 ${index + 1}`}>
|
||||
<OssUpload bizType="STORE_ENV" mediaType="IMAGE" />
|
||||
</Form.Item>
|
||||
<div key={field.key} style={{ display: 'flex', gap: 8, alignItems: 'flex-start' }}>
|
||||
<Form.Item key={field.key} name={field.name} label={`环境图 ${index + 1}`} style={{ flex: 1 }}>
|
||||
<OssUpload bizType="STORE_ENV" mediaType="IMAGE" />
|
||||
</Form.Item>
|
||||
<Button type="link" danger style={{ marginTop: 30 }} onClick={() => remove(field.name)}>
|
||||
删除
|
||||
</Button>
|
||||
</div>
|
||||
))}
|
||||
<Button type="dashed" onClick={() => add('')} block>
|
||||
添加环境照片
|
||||
</Button>
|
||||
{fields.length < 20 ? (
|
||||
<Button type="dashed" onClick={() => add('')} block>
|
||||
添加环境照片
|
||||
</Button>
|
||||
) : (
|
||||
<Typography.Text type="secondary">已达上限 20 张</Typography.Text>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</Form.List>
|
||||
|
||||
Reference in New Issue
Block a user