feat: manage venue booth templates in job fair admin
Some checks are pending
Node CI / build (14.x, macOS-latest) (push) Waiting to run
Node CI / build (14.x, ubuntu-latest) (push) Waiting to run
Node CI / build (14.x, windows-latest) (push) Waiting to run
Node CI / build (16.x, macOS-latest) (push) Waiting to run
Node CI / build (16.x, ubuntu-latest) (push) Waiting to run
Node CI / build (16.x, windows-latest) (push) Waiting to run
CodeQL / Analyze (javascript) (push) Waiting to run
coverage CI / build (push) Waiting to run
Node pnpm CI / build (16.x, macOS-latest) (push) Waiting to run
Node pnpm CI / build (16.x, ubuntu-latest) (push) Waiting to run
Node pnpm CI / build (16.x, windows-latest) (push) Waiting to run
Some checks are pending
Node CI / build (14.x, macOS-latest) (push) Waiting to run
Node CI / build (14.x, ubuntu-latest) (push) Waiting to run
Node CI / build (14.x, windows-latest) (push) Waiting to run
Node CI / build (16.x, macOS-latest) (push) Waiting to run
Node CI / build (16.x, ubuntu-latest) (push) Waiting to run
Node CI / build (16.x, windows-latest) (push) Waiting to run
CodeQL / Analyze (javascript) (push) Waiting to run
coverage CI / build (push) Waiting to run
Node pnpm CI / build (16.x, macOS-latest) (push) Waiting to run
Node pnpm CI / build (16.x, ubuntu-latest) (push) Waiting to run
Node pnpm CI / build (16.x, windows-latest) (push) Waiting to run
This commit is contained in:
@@ -147,7 +147,30 @@ const EditModal: React.FC<EditModalProps> = ({
|
||||
width={700}
|
||||
modalProps={{ destroyOnClose: true, onCancel }}
|
||||
onFinish={async (formValues) => {
|
||||
await onSubmit({ ...formValues, id: values?.id } as OutdoorFairForm);
|
||||
const payload = { ...formValues, id: values?.id } as OutdoorFairForm;
|
||||
const venueChanged =
|
||||
!!values?.id &&
|
||||
values.venueId !== undefined &&
|
||||
Number(values.venueId) !== Number(payload.venueId);
|
||||
if (venueChanged && (values?.boothCount || 0) > 0) {
|
||||
return new Promise<boolean>((resolve) => {
|
||||
Modal.confirm({
|
||||
title: '确认更换场地并重建展位图?',
|
||||
content:
|
||||
'当前招聘会已有展位图。确认后将删除原展位图和所有企业预定,再按新场地的展位模板重新生成;此操作不可恢复。',
|
||||
okText: '确认重建',
|
||||
okButtonProps: { danger: true },
|
||||
cancelText: '取消',
|
||||
onOk: async () => {
|
||||
await onSubmit({ ...payload, resetBoothMap: true });
|
||||
resolve(true);
|
||||
},
|
||||
onCancel: () => resolve(false),
|
||||
});
|
||||
});
|
||||
}
|
||||
await onSubmit(payload);
|
||||
return true;
|
||||
}}
|
||||
>
|
||||
<ProFormText name="id" hidden />
|
||||
|
||||
@@ -286,6 +286,12 @@ const OutdoorFairList: React.FC = () => {
|
||||
dataIndex: 'boothCount',
|
||||
hideInSearch: true,
|
||||
},
|
||||
{
|
||||
title: '已预定摊位数',
|
||||
dataIndex: 'reservedBoothCount',
|
||||
hideInSearch: true,
|
||||
render: (_, record) => record.reservedBoothCount ?? 0,
|
||||
},
|
||||
{
|
||||
title: '举办时间',
|
||||
dataIndex: 'holdTime',
|
||||
|
||||
421
src/pages/Jobfair/Venueinfo/components/VenueBoothMapModal.tsx
Normal file
421
src/pages/Jobfair/Venueinfo/components/VenueBoothMapModal.tsx
Normal file
@@ -0,0 +1,421 @@
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
Col,
|
||||
Empty,
|
||||
Input,
|
||||
InputNumber,
|
||||
List,
|
||||
Modal,
|
||||
Popover,
|
||||
Row,
|
||||
Space,
|
||||
Tag,
|
||||
message,
|
||||
} from 'antd';
|
||||
import { ReloadOutlined } from '@ant-design/icons';
|
||||
import { Rnd } from 'react-rnd';
|
||||
import { getVenueBoothMap, saveVenueBoothMap } from '@/services/jobportal/venueInfo';
|
||||
import type { VenueBoothItem, VenueInfoItem } from '@/services/jobportal/venueInfo';
|
||||
|
||||
interface Props {
|
||||
open: boolean;
|
||||
venue?: VenueInfoItem;
|
||||
onCancel: () => void;
|
||||
onSaved: () => void;
|
||||
}
|
||||
|
||||
const BOOTH_WIDTH = 70;
|
||||
const BOOTH_HEIGHT = 44;
|
||||
const BOOTH_GAP_X = 42;
|
||||
const BOOTH_GAP_Y = 54;
|
||||
const BOOTH_START_X = 40;
|
||||
const BOOTH_START_Y = 40;
|
||||
const BOOTHS_PER_ROW = 10;
|
||||
|
||||
/**
|
||||
* 场地模板模式的平面编辑器。
|
||||
* 它只处理布局数据,刻意不加载企业、不展示预定/调换/取消预定等招聘会副本交互。
|
||||
*/
|
||||
const VenueBoothMapModal: React.FC<Props> = ({ open, venue, onCancel, onSaved }) => {
|
||||
const [booths, setBooths] = useState<VenueBoothItem[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [saving, setSaving] = useState(false);
|
||||
const [activeBoothKey, setActiveBoothKey] = useState<string>();
|
||||
const [boothModalOpen, setBoothModalOpen] = useState(false);
|
||||
const [editingBoothKey, setEditingBoothKey] = useState<string>();
|
||||
const [boothNumber, setBoothNumber] = useState('');
|
||||
const [generateModalOpen, setGenerateModalOpen] = useState(false);
|
||||
const [generateRows, setGenerateRows] = useState(2);
|
||||
const [generateColumns, setGenerateColumns] = useState(10);
|
||||
|
||||
const getBoothKey = (booth: VenueBoothItem, index: number) =>
|
||||
String(booth.id || booth.boothId || `${booth.boothNumber}-${index}`);
|
||||
|
||||
const canvasSize = useMemo(() => {
|
||||
const maxX = booths.reduce((max, booth) => Math.max(max, booth.positionX || 0), 0);
|
||||
const maxY = booths.reduce((max, booth) => Math.max(max, booth.positionY || 0), 0);
|
||||
return {
|
||||
width: Math.max(960, maxX + BOOTH_WIDTH + BOOTH_START_X),
|
||||
height: Math.max(520, maxY + BOOTH_HEIGHT + BOOTH_START_Y),
|
||||
};
|
||||
}, [booths]);
|
||||
|
||||
const boothList = useMemo(
|
||||
() =>
|
||||
[...booths].sort((a, b) =>
|
||||
String(a.boothNumber || '').localeCompare(String(b.boothNumber || ''), 'zh-CN', {
|
||||
numeric: true,
|
||||
}),
|
||||
),
|
||||
[booths],
|
||||
);
|
||||
|
||||
const fetchData = async () => {
|
||||
if (!venue?.id) {
|
||||
return;
|
||||
}
|
||||
setLoading(true);
|
||||
try {
|
||||
const res = await getVenueBoothMap(venue.id);
|
||||
if (res.code === 200) {
|
||||
const fetchedBooths = res.data || [];
|
||||
setBooths(fetchedBooths);
|
||||
if (fetchedBooths.length > 0) {
|
||||
const columns = Math.min(BOOTHS_PER_ROW, fetchedBooths.length);
|
||||
setGenerateColumns(columns);
|
||||
setGenerateRows(Math.max(1, Math.ceil(fetchedBooths.length / columns)));
|
||||
}
|
||||
} else {
|
||||
message.error(res.msg || '场地展位图加载失败');
|
||||
}
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
setActiveBoothKey(undefined);
|
||||
fetchData();
|
||||
}
|
||||
}, [open, venue?.id]);
|
||||
|
||||
const getNextBoothNumber = () => {
|
||||
const existingNumbers = new Set(booths.map((item) => item.boothNumber));
|
||||
let next = booths.length + 1;
|
||||
while (existingNumbers.has(`KJ${String(next).padStart(2, '0')}`)) {
|
||||
next += 1;
|
||||
}
|
||||
return `KJ${String(next).padStart(2, '0')}`;
|
||||
};
|
||||
|
||||
const getNextBoothPosition = () => {
|
||||
const columns = Math.max(1, generateColumns || BOOTHS_PER_ROW);
|
||||
const index = booths.length;
|
||||
return {
|
||||
positionX: BOOTH_START_X + (index % columns) * (BOOTH_WIDTH + BOOTH_GAP_X),
|
||||
positionY: BOOTH_START_Y + Math.floor(index / columns) * (BOOTH_HEIGHT + BOOTH_GAP_Y),
|
||||
};
|
||||
};
|
||||
|
||||
const openBoothModal = (booth?: VenueBoothItem, index?: number) => {
|
||||
setEditingBoothKey(booth && index !== undefined ? getBoothKey(booth, index) : undefined);
|
||||
setBoothNumber(booth?.boothNumber || getNextBoothNumber());
|
||||
setBoothModalOpen(true);
|
||||
};
|
||||
|
||||
const handleSaveBoothNumber = () => {
|
||||
const trimmed = boothNumber.trim();
|
||||
if (!trimmed) {
|
||||
message.warning('请输入展位号');
|
||||
return;
|
||||
}
|
||||
if (
|
||||
booths.some(
|
||||
(booth, index) =>
|
||||
booth.boothNumber === trimmed && getBoothKey(booth, index) !== editingBoothKey,
|
||||
)
|
||||
) {
|
||||
message.warning('展位号不能重复');
|
||||
return;
|
||||
}
|
||||
if (editingBoothKey) {
|
||||
setBooths((prev) =>
|
||||
prev.map((booth, index) =>
|
||||
getBoothKey(booth, index) === editingBoothKey
|
||||
? { ...booth, boothNumber: trimmed }
|
||||
: booth,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
const nextPosition = getNextBoothPosition();
|
||||
setBooths((prev) => [...prev, { boothNumber: trimmed, ...nextPosition }]);
|
||||
}
|
||||
setBoothModalOpen(false);
|
||||
};
|
||||
|
||||
const handleMoveBooth = (key: string, x: number, y: number) => {
|
||||
setBooths((prev) =>
|
||||
prev.map((booth, index) =>
|
||||
getBoothKey(booth, index) === key
|
||||
? { ...booth, positionX: Math.round(x), positionY: Math.round(y) }
|
||||
: booth,
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
const handleDeleteBooth = (key: string) => {
|
||||
setBooths((prev) => prev.filter((booth, index) => getBoothKey(booth, index) !== key));
|
||||
setActiveBoothKey(undefined);
|
||||
};
|
||||
|
||||
const handleGenerateBooths = () => {
|
||||
const rows = Math.max(1, generateRows || 1);
|
||||
const columns = Math.max(1, generateColumns || 1);
|
||||
const count = rows * columns;
|
||||
const existingByNumber = new Map(booths.map((item) => [item.boothNumber, item]));
|
||||
const generated = Array.from({ length: count }).map((_, index) => {
|
||||
const generatedNumber = `KJ${String(index + 1).padStart(2, '0')}`;
|
||||
return {
|
||||
...existingByNumber.get(generatedNumber),
|
||||
boothNumber: generatedNumber,
|
||||
positionX: BOOTH_START_X + (index % columns) * (BOOTH_WIDTH + BOOTH_GAP_X),
|
||||
positionY: BOOTH_START_Y + Math.floor(index / columns) * (BOOTH_HEIGHT + BOOTH_GAP_Y),
|
||||
};
|
||||
});
|
||||
setBooths(generated);
|
||||
setGenerateModalOpen(false);
|
||||
message.success(`已生成 ${rows} 行 × ${columns} 列,共 ${count} 个展位;请点击保存场地模板`);
|
||||
};
|
||||
|
||||
const handleArrangeBooths = () => {
|
||||
const columns = Math.max(1, generateColumns || BOOTHS_PER_ROW);
|
||||
setBooths((prev) =>
|
||||
prev.map((booth, index) => ({
|
||||
...booth,
|
||||
positionX: BOOTH_START_X + (index % columns) * (BOOTH_WIDTH + BOOTH_GAP_X),
|
||||
positionY: BOOTH_START_Y + Math.floor(index / columns) * (BOOTH_HEIGHT + BOOTH_GAP_Y),
|
||||
})),
|
||||
);
|
||||
message.success('已整理布局;请点击保存场地模板');
|
||||
};
|
||||
|
||||
const handleSave = async () => {
|
||||
if (!venue?.id) {
|
||||
return;
|
||||
}
|
||||
const invalid = booths.find((booth) => !booth.boothNumber?.trim());
|
||||
if (invalid) {
|
||||
message.warning('展位号不能为空');
|
||||
return;
|
||||
}
|
||||
setSaving(true);
|
||||
try {
|
||||
const res = await saveVenueBoothMap(
|
||||
venue.id,
|
||||
booths.map((booth) => ({
|
||||
id: booth.id,
|
||||
boothNumber: booth.boothNumber.trim(),
|
||||
positionX: booth.positionX || 0,
|
||||
positionY: booth.positionY || 0,
|
||||
})),
|
||||
);
|
||||
if (res.code === 200) {
|
||||
message.success('场地展位模板保存成功');
|
||||
await fetchData();
|
||||
onSaved();
|
||||
} else {
|
||||
message.error(res.msg || '场地展位模板保存失败');
|
||||
}
|
||||
} finally {
|
||||
setSaving(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title={venue ? `${venue.venueName} - 展位管理` : '展位管理'}
|
||||
open={open}
|
||||
onCancel={onCancel}
|
||||
width={1240}
|
||||
destroyOnClose
|
||||
footer={null}
|
||||
>
|
||||
<Row gutter={16} wrap={false} align="top">
|
||||
<Col flex="1 1 0" style={{ minWidth: 0 }}>
|
||||
<Card loading={loading} bodyStyle={{ padding: 16 }}>
|
||||
<Space wrap size={12} style={{ marginBottom: 16 }}>
|
||||
<Button onClick={() => openBoothModal()}>新增展位</Button>
|
||||
<Button onClick={() => setGenerateModalOpen(true)}>按行列生成</Button>
|
||||
<Button onClick={handleArrangeBooths}>一键整理布局</Button>
|
||||
<Button type="primary" loading={saving} onClick={handleSave}>
|
||||
保存场地模板
|
||||
</Button>
|
||||
<Button icon={<ReloadOutlined />} onClick={fetchData}>
|
||||
刷新
|
||||
</Button>
|
||||
</Space>
|
||||
{booths.length === 0 ? (
|
||||
<Empty description="当前场地还没有展位模板,请点击“按行列生成”或“新增展位”创建" />
|
||||
) : (
|
||||
<div
|
||||
style={{
|
||||
minHeight: 520,
|
||||
position: 'relative',
|
||||
overflow: 'auto',
|
||||
background: '#fafafa',
|
||||
border: '1px solid #f0f0f0',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
width: canvasSize.width,
|
||||
height: canvasSize.height,
|
||||
position: 'relative',
|
||||
backgroundSize: '20px 20px',
|
||||
backgroundImage:
|
||||
'linear-gradient(to right, rgba(0,0,0,0.04) 1px, transparent 1px), linear-gradient(to bottom, rgba(0,0,0,0.04) 1px, transparent 1px)',
|
||||
}}
|
||||
>
|
||||
{booths.map((booth, index) => {
|
||||
const key = getBoothKey(booth, index);
|
||||
return (
|
||||
<Popover
|
||||
key={key}
|
||||
trigger="click"
|
||||
placement="rightTop"
|
||||
open={activeBoothKey === key}
|
||||
onOpenChange={(visible) => setActiveBoothKey(visible ? key : undefined)}
|
||||
title={`${booth.boothNumber}:场地模板展位`}
|
||||
content={
|
||||
<Space direction="vertical" size={8} style={{ width: 160 }}>
|
||||
<Button
|
||||
block
|
||||
size="small"
|
||||
onClick={() => {
|
||||
setActiveBoothKey(undefined);
|
||||
openBoothModal(booth, index);
|
||||
}}
|
||||
>
|
||||
改名称
|
||||
</Button>
|
||||
<Button
|
||||
block
|
||||
danger
|
||||
size="small"
|
||||
onClick={() => handleDeleteBooth(key)}
|
||||
>
|
||||
删除
|
||||
</Button>
|
||||
</Space>
|
||||
}
|
||||
>
|
||||
<Rnd
|
||||
bounds="parent"
|
||||
size={{ width: BOOTH_WIDTH, height: BOOTH_HEIGHT }}
|
||||
position={{ x: booth.positionX || 0, y: booth.positionY || 0 }}
|
||||
dragGrid={[8, 8]}
|
||||
enableResizing={false}
|
||||
onDragStop={(_, data) => handleMoveBooth(key, data.x, data.y)}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
padding: '4px 2px',
|
||||
textAlign: 'center',
|
||||
borderRadius: 4,
|
||||
cursor: 'move',
|
||||
color: '#fff',
|
||||
background: '#1890ff',
|
||||
fontSize: 12,
|
||||
userSelect: 'none',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
>
|
||||
{booth.boothNumber}
|
||||
</div>
|
||||
</Rnd>
|
||||
</Popover>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</Card>
|
||||
</Col>
|
||||
<Col flex="260px">
|
||||
<Card title="模板概况" size="small">
|
||||
<Space direction="vertical" style={{ width: '100%' }} size={12}>
|
||||
<Tag color="blue" style={{ width: '100%', textAlign: 'center', padding: 8 }}>
|
||||
模板展位({booths.length})
|
||||
</Tag>
|
||||
<div style={{ color: '#999', fontSize: 12, lineHeight: '20px' }}>
|
||||
此处只维护场地布局。创建户外招聘会并绑定该场地时,系统会复制当前模板为招聘会独立展位图,后续双方互不影响。
|
||||
</div>
|
||||
<List
|
||||
size="small"
|
||||
style={{ maxHeight: 420, overflow: 'auto' }}
|
||||
dataSource={boothList}
|
||||
locale={{ emptyText: '暂无模板展位' }}
|
||||
renderItem={(booth) => <List.Item>{booth.boothNumber}</List.Item>}
|
||||
/>
|
||||
</Space>
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
<Modal
|
||||
title="按行列生成展位模板"
|
||||
open={generateModalOpen}
|
||||
onCancel={() => setGenerateModalOpen(false)}
|
||||
onOk={handleGenerateBooths}
|
||||
okText="生成"
|
||||
>
|
||||
<Space direction="vertical" size={16} style={{ width: '100%' }}>
|
||||
<Space>
|
||||
<span>行数</span>
|
||||
<InputNumber
|
||||
min={1}
|
||||
max={50}
|
||||
value={generateRows}
|
||||
onChange={(value) => setGenerateRows(Number(value) || 1)}
|
||||
/>
|
||||
<span>列数</span>
|
||||
<InputNumber
|
||||
min={1}
|
||||
max={50}
|
||||
value={generateColumns}
|
||||
onChange={(value) => setGenerateColumns(Number(value) || 1)}
|
||||
/>
|
||||
</Space>
|
||||
<div style={{ color: '#999' }}>
|
||||
将生成 {Math.max(1, generateRows || 1)} 行 × {Math.max(1, generateColumns || 1)} 列,共{' '}
|
||||
{Math.max(1, generateRows || 1) * Math.max(1, generateColumns || 1)}{' '}
|
||||
个展位。生成后需要保存才会写入场地模板。
|
||||
</div>
|
||||
</Space>
|
||||
</Modal>
|
||||
<Modal
|
||||
title={editingBoothKey ? '修改展位号' : '新增展位'}
|
||||
open={boothModalOpen}
|
||||
onCancel={() => setBoothModalOpen(false)}
|
||||
onOk={handleSaveBoothNumber}
|
||||
okText="确定"
|
||||
>
|
||||
<Input
|
||||
value={boothNumber}
|
||||
maxLength={50}
|
||||
placeholder="请输入展位号,如 KJ01"
|
||||
onChange={(event) => setBoothNumber(event.target.value)}
|
||||
onPressEnter={handleSaveBoothNumber}
|
||||
/>
|
||||
</Modal>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default VenueBoothMapModal;
|
||||
@@ -2,7 +2,7 @@ import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { useAccess, useModel } from '@umijs/max';
|
||||
import { Button, message, Modal } from 'antd';
|
||||
import { ActionType, PageContainer, ProColumns, ProTable } from '@ant-design/pro-components';
|
||||
import { DeleteOutlined, FormOutlined, PlusOutlined } from '@ant-design/icons';
|
||||
import { ApartmentOutlined, DeleteOutlined, FormOutlined, PlusOutlined } from '@ant-design/icons';
|
||||
import {
|
||||
addVenueInfo,
|
||||
addVenueInfoDictOption,
|
||||
@@ -19,6 +19,7 @@ import type {
|
||||
import { getCmsLineList } from '@/services/area/subway';
|
||||
import { getDictSelectOption, getDictValueEnum } from '@/services/system/dict';
|
||||
import EditModal from './components/EditModal';
|
||||
import VenueBoothMapModal from './components/VenueBoothMapModal';
|
||||
|
||||
const VENUE_TYPE_DICT = 'venue_info_type';
|
||||
|
||||
@@ -28,6 +29,7 @@ const VenueInfoList: React.FC = () => {
|
||||
const actionRef = useRef<ActionType>();
|
||||
const [modalVisible, setModalVisible] = useState(false);
|
||||
const [currentRow, setCurrentRow] = useState<VenueInfoItem>();
|
||||
const [templateVenue, setTemplateVenue] = useState<VenueInfoItem>();
|
||||
const [venueTypeValueEnum, setVenueTypeValueEnum] = useState<Record<string, any>>({});
|
||||
const [venueTypeOptions, setVenueTypeOptions] = useState<any[]>([]);
|
||||
const [lineOptions, setLineOptions] = useState<any[]>([]);
|
||||
@@ -153,9 +155,19 @@ const VenueInfoList: React.FC = () => {
|
||||
{
|
||||
title: '操作',
|
||||
valueType: 'option',
|
||||
width: 140,
|
||||
width: 220,
|
||||
fixed: 'right',
|
||||
render: (_, record) => [
|
||||
<Button
|
||||
key="booths"
|
||||
type="link"
|
||||
size="small"
|
||||
icon={<ApartmentOutlined />}
|
||||
hidden={!access.hasPerms('cms:venueInfo:edit')}
|
||||
onClick={() => setTemplateVenue(record)}
|
||||
>
|
||||
展位管理
|
||||
</Button>,
|
||||
<Button
|
||||
key="edit"
|
||||
type="link"
|
||||
@@ -240,6 +252,12 @@ const VenueInfoList: React.FC = () => {
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<VenueBoothMapModal
|
||||
open={!!templateVenue}
|
||||
venue={templateVenue}
|
||||
onCancel={() => setTemplateVenue(undefined)}
|
||||
onSaved={() => actionRef.current?.reload()}
|
||||
/>
|
||||
</PageContainer>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -19,6 +19,8 @@ export interface OutdoorFairItem {
|
||||
address: string;
|
||||
/** 展位数量 */
|
||||
boothCount: number;
|
||||
/** 已预定摊位数 */
|
||||
reservedBoothCount?: number;
|
||||
/** 举办时间 */
|
||||
holdTime: string;
|
||||
/** 截止时间 */
|
||||
@@ -86,6 +88,8 @@ export interface OutdoorFairForm {
|
||||
applyEndTime: string;
|
||||
onlineApply: boolean;
|
||||
photoUrl: string;
|
||||
/** 更换场地时确认清空旧展位图与预定,并按新模板重建 */
|
||||
resetBoothMap?: boolean;
|
||||
}
|
||||
|
||||
/** 通用响应 */
|
||||
|
||||
@@ -170,6 +170,22 @@ export async function addVenueInfoDictOption(data: VenueInfoDictForm) {
|
||||
});
|
||||
}
|
||||
|
||||
/** 查询场地的展位图模板。模板不含企业及预定状态。 */
|
||||
export async function getVenueBoothMap(venueId: number) {
|
||||
return request<{ code: number; msg?: string; data: VenueBoothItem[] }>(
|
||||
`${CMS_VENUE_INFO_BASE}/${venueId}/booth-map`,
|
||||
{ method: 'GET' },
|
||||
);
|
||||
}
|
||||
|
||||
/** 完整覆盖保存场地的展位图模板。 */
|
||||
export async function saveVenueBoothMap(venueId: number, data: VenueBoothItem[]) {
|
||||
return request<VenueInfoCommonResult>(`${CMS_VENUE_INFO_BASE}/${venueId}/booths`, {
|
||||
method: 'POST',
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
export async function getOutdoorFairBoothMap(fairId: number) {
|
||||
return request<{ code: number; msg?: string; data: VenueBoothItem[] }>(
|
||||
`${CMS_VENUE_INFO_BASE}/fair/${fairId}/booth-map`,
|
||||
|
||||
Reference in New Issue
Block a user