feat(ops): v4.0.7 HQ 活动图快链与勾选导出
HQ 指定一张活动图为勾选主合伙人合成 PNG/zip;城市合伙人页增加快链与单下/导出。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { useState } from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Link, useSearchParams } from 'react-router-dom';
|
||||
import {
|
||||
Button, Form, Image, Input, InputNumber, Modal, Popconfirm, Select, Space, Table, Tag, message,
|
||||
Alert, Button, Form, Image, Input, InputNumber, Modal, Popconfirm, Select, Space, Table, Tag, message,
|
||||
} from 'antd';
|
||||
import type { ColumnsType } from 'antd/es/table';
|
||||
import {
|
||||
@@ -10,7 +11,7 @@ import {
|
||||
type ActivityPosterQrSlot,
|
||||
type ActivityPosterStatus,
|
||||
} from '@dukang/shared-types';
|
||||
import { request } from '../lib/api';
|
||||
import { request, requestDownload } from '../lib/api';
|
||||
import { fmtTime } from '../lib/constants';
|
||||
import { useAdminList } from '../lib/useAdminList';
|
||||
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
||||
@@ -30,7 +31,16 @@ type FormValues = {
|
||||
status?: ActivityPosterStatus;
|
||||
};
|
||||
|
||||
type PartnerHint = {
|
||||
id: string;
|
||||
companyName?: string | null;
|
||||
name?: string;
|
||||
phone?: string;
|
||||
};
|
||||
|
||||
export default function ActivityPostersPage() {
|
||||
const [searchParams] = useSearchParams();
|
||||
const partnerId = searchParams.get('partnerId')?.trim() || '';
|
||||
const [form] = Form.useForm();
|
||||
const [editForm] = Form.useForm<FormValues>();
|
||||
const [filters, setFilters] = useState<Record<string, string>>({});
|
||||
@@ -45,6 +55,8 @@ export default function ActivityPostersPage() {
|
||||
);
|
||||
const [modalOpen, setModalOpen] = useState(false);
|
||||
const [editing, setEditing] = useState<ActivityPosterItem | null>(null);
|
||||
const [partnerHint, setPartnerHint] = useState<PartnerHint | null>(null);
|
||||
const [downloadingId, setDownloadingId] = useState<string | null>(null);
|
||||
|
||||
const imageUrl = Form.useWatch('imageUrl', editForm);
|
||||
const qrXPct = Form.useWatch('qrXPct', editForm);
|
||||
@@ -56,6 +68,20 @@ export default function ActivityPostersPage() {
|
||||
qrSizePct: qrSizePct ?? DEFAULT_ACTIVITY_POSTER_QR_SLOT.qrSizePct,
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!partnerId) {
|
||||
setPartnerHint(null);
|
||||
return;
|
||||
}
|
||||
void request<PartnerHint>(`/admin/partners/${partnerId}`)
|
||||
.then(setPartnerHint)
|
||||
.catch(() => setPartnerHint({ id: partnerId }));
|
||||
}, [partnerId]);
|
||||
|
||||
const partnerLabel = partnerHint
|
||||
? (partnerHint.companyName || partnerHint.name || partnerHint.phone || partnerHint.id)
|
||||
: partnerId;
|
||||
|
||||
function openCreate() {
|
||||
setEditing(null);
|
||||
editForm.setFieldsValue({
|
||||
@@ -98,6 +124,23 @@ export default function ActivityPostersPage() {
|
||||
void reload();
|
||||
}
|
||||
|
||||
async function downloadForPartner(posterId: string) {
|
||||
if (!partnerId) return;
|
||||
setDownloadingId(posterId);
|
||||
try {
|
||||
await requestDownload(
|
||||
`/admin/activity-posters/${posterId}/image?partnerId=${encodeURIComponent(partnerId)}`,
|
||||
{},
|
||||
'activity-poster.png',
|
||||
);
|
||||
message.success('已开始下载');
|
||||
} catch (e) {
|
||||
message.error(e instanceof Error ? e.message : '下载失败');
|
||||
} finally {
|
||||
setDownloadingId(null);
|
||||
}
|
||||
}
|
||||
|
||||
const baseColumns: ColumnsType<ActivityPosterItem> = [
|
||||
{
|
||||
title: '标题',
|
||||
@@ -129,10 +172,20 @@ export default function ActivityPostersPage() {
|
||||
{ title: '更新', dataIndex: 'updatedAt', width: 160, render: fmtTime },
|
||||
{
|
||||
title: '操作',
|
||||
width: 200,
|
||||
width: partnerId ? 280 : 200,
|
||||
render: (_, row) => (
|
||||
<Space>
|
||||
<Button type="link" size="small" onClick={() => openEdit(row)}>编辑</Button>
|
||||
{partnerId && row.status === 'ACTIVE' ? (
|
||||
<Button
|
||||
type="link"
|
||||
size="small"
|
||||
loading={downloadingId === row.id}
|
||||
onClick={() => void downloadForPartner(row.id)}
|
||||
>
|
||||
为该合伙人下载
|
||||
</Button>
|
||||
) : null}
|
||||
<Button
|
||||
type="link"
|
||||
size="small"
|
||||
@@ -176,6 +229,19 @@ export default function ActivityPostersPage() {
|
||||
settings={settingsButton}
|
||||
actions={<Button type="primary" onClick={openCreate}>新建活动图</Button>}
|
||||
/>
|
||||
{partnerId ? (
|
||||
<Alert
|
||||
type="info"
|
||||
showIcon
|
||||
style={{ marginBottom: 16 }}
|
||||
message={
|
||||
<>
|
||||
正在为合伙人「{partnerLabel}」下载活动图。点行内「为该合伙人下载」,或返回{' '}
|
||||
<Link to="/city-partners">城市合伙人</Link>。
|
||||
</>
|
||||
}
|
||||
/>
|
||||
) : null}
|
||||
<Form form={form} layout="inline" style={{ marginBottom: 16 }} onFinish={(v) => { setFilters(v); setPage(1); }}>
|
||||
<Form.Item name="status" label="状态">
|
||||
<Select
|
||||
|
||||
Reference in New Issue
Block a user