hqweb端
This commit is contained in:
@@ -0,0 +1,125 @@
|
||||
import { useState } from 'react';
|
||||
import {
|
||||
Button, Form, Image, Input, InputNumber, Modal, Popconfirm, Select, Space, Table, Tag, Typography, message,
|
||||
} from 'antd';
|
||||
import type { ColumnsType } from 'antd/es/table';
|
||||
import { request, type Paginated } from '../lib/api';
|
||||
import { MEDIA_TYPE_LABELS, fmtTime } from '../lib/constants';
|
||||
import { useAdminList } from '../lib/useAdminList';
|
||||
|
||||
type Row = {
|
||||
id: string; mediaType: string; url: string; sortOrder: number; createdAt: string;
|
||||
store?: { id: string; name: string };
|
||||
};
|
||||
|
||||
type StoreOption = { id: string; name: string };
|
||||
|
||||
export default function StoreMediaPage() {
|
||||
const [form] = Form.useForm();
|
||||
const [editForm] = Form.useForm();
|
||||
const [createForm] = Form.useForm();
|
||||
const [filters, setFilters] = useState<Record<string, string>>({});
|
||||
const { data, loading, page, pageSize, setPage, setPageSize, reload } = useAdminList<Row>(
|
||||
'/admin/store-media',
|
||||
() => {
|
||||
const qs = new URLSearchParams();
|
||||
if (filters.storeId) qs.set('storeId', filters.storeId);
|
||||
if (filters.mediaType) qs.set('mediaType', filters.mediaType);
|
||||
return qs;
|
||||
},
|
||||
[filters],
|
||||
);
|
||||
const [createOpen, setCreateOpen] = useState(false);
|
||||
const [editOpen, setEditOpen] = useState(false);
|
||||
const [editing, setEditing] = useState<Row | null>(null);
|
||||
const [stores, setStores] = useState<StoreOption[]>([]);
|
||||
|
||||
async function loadStores() {
|
||||
const res = await request<Paginated<StoreOption>>('/admin/stores?pageSize=200');
|
||||
setStores(res.items);
|
||||
}
|
||||
|
||||
const columns: ColumnsType<Row> = [
|
||||
{ title: '门店', dataIndex: ['store', 'name'], width: 140 },
|
||||
{ title: '类型', dataIndex: 'mediaType', width: 80, render: (t) => <Tag>{MEDIA_TYPE_LABELS[t] || t}</Tag> },
|
||||
{
|
||||
title: '预览', dataIndex: 'url', width: 100,
|
||||
render: (url, row) => row.mediaType === 'IMAGE'
|
||||
? <Image src={url} width={60} height={40} style={{ objectFit: 'cover' }} />
|
||||
: <a href={url} target="_blank" rel="noreferrer">视频</a>,
|
||||
},
|
||||
{ title: 'URL', dataIndex: 'url', ellipsis: true },
|
||||
{ title: '排序', dataIndex: 'sortOrder', width: 70 },
|
||||
{ title: '创建', dataIndex: 'createdAt', width: 160, render: fmtTime },
|
||||
{
|
||||
title: '操作', width: 120,
|
||||
render: (_, row) => (
|
||||
<Space>
|
||||
<Button type="link" size="small" onClick={() => {
|
||||
setEditing(row);
|
||||
editForm.setFieldsValue(row);
|
||||
setEditOpen(true);
|
||||
}}>编辑</Button>
|
||||
<Popconfirm title="确认删除?" onConfirm={async () => {
|
||||
await request(`/admin/store-media/${row.id}`, { method: 'DELETE' });
|
||||
message.success('已删除');
|
||||
void reload();
|
||||
}}>
|
||||
<Button type="link" size="small" danger>删除</Button>
|
||||
</Popconfirm>
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Space style={{ marginBottom: 16, width: '100%', justifyContent: 'space-between' }}>
|
||||
<Typography.Title level={4} style={{ margin: 0 }}>门店资源</Typography.Title>
|
||||
<Button type="primary" onClick={() => { void loadStores(); setCreateOpen(true); }}>新增资源</Button>
|
||||
</Space>
|
||||
<Form form={form} layout="inline" style={{ marginBottom: 16 }} onFinish={(v) => { setFilters(v); setPage(1); }}>
|
||||
<Form.Item name="storeId" label="门店ID"><Input allowClear /></Form.Item>
|
||||
<Form.Item name="mediaType" label="类型">
|
||||
<Select allowClear style={{ width: 100 }} options={Object.entries(MEDIA_TYPE_LABELS).map(([value, label]) => ({ value, label }))} />
|
||||
</Form.Item>
|
||||
<Form.Item><Button type="primary" htmlType="submit">查询</Button></Form.Item>
|
||||
</Form>
|
||||
<Table rowKey="id" className="admin-table-nowrap" loading={loading} columns={columns} dataSource={data?.items ?? []} scroll={{ x: 900 }}
|
||||
pagination={{ current: page, pageSize, total: data?.total ?? 0, showSizeChanger: true, onChange: (p, ps) => { setPage(p); setPageSize(ps); } }} />
|
||||
<Modal title="新增门店资源" open={createOpen} onCancel={() => setCreateOpen(false)} onOk={async () => {
|
||||
const v = await createForm.validateFields();
|
||||
await request('/admin/store-media', { method: 'POST', body: JSON.stringify(v) });
|
||||
message.success('已创建');
|
||||
setCreateOpen(false);
|
||||
createForm.resetFields();
|
||||
void reload();
|
||||
}}>
|
||||
<Form form={createForm} layout="vertical">
|
||||
<Form.Item name="storeId" label="门店" rules={[{ required: true }]}>
|
||||
<Select showSearch optionFilterProp="label" options={stores.map((s) => ({ value: s.id, label: s.name }))} />
|
||||
</Form.Item>
|
||||
<Form.Item name="mediaType" label="类型" rules={[{ required: true }]} initialValue="IMAGE">
|
||||
<Select options={Object.entries(MEDIA_TYPE_LABELS).map(([value, label]) => ({ value, label }))} />
|
||||
</Form.Item>
|
||||
<Form.Item name="url" label="资源 URL" rules={[{ required: true }]}><Input /></Form.Item>
|
||||
<Form.Item name="sortOrder" label="排序" initialValue={0}><InputNumber min={0} style={{ width: '100%' }} /></Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
<Modal title="编辑门店资源" open={editOpen} onCancel={() => setEditOpen(false)} onOk={async () => {
|
||||
if (!editing) return;
|
||||
const v = await editForm.validateFields();
|
||||
await request(`/admin/store-media/${editing.id}`, { method: 'PUT', body: JSON.stringify(v) });
|
||||
message.success('已保存');
|
||||
setEditOpen(false);
|
||||
void reload();
|
||||
}}>
|
||||
<Form form={editForm} layout="vertical">
|
||||
<Form.Item name="mediaType" label="类型"><Select options={Object.entries(MEDIA_TYPE_LABELS).map(([value, label]) => ({ value, label }))} /></Form.Item>
|
||||
<Form.Item name="url" label="资源 URL" rules={[{ required: true }]}><Input /></Form.Item>
|
||||
<Form.Item name="sortOrder" label="排序"><InputNumber min={0} style={{ width: '100%' }} /></Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user