feat: 赛事列表添加审核相关弹窗
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
.section {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.section:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.sectionTitle {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: rgba(0, 0, 0, 0.85);
|
||||
margin-bottom: 12px;
|
||||
padding-left: 8px;
|
||||
border-left: 3px solid #1677ff;
|
||||
}
|
||||
|
||||
.desc :global(.ant-descriptions-item-label) {
|
||||
width: 110px;
|
||||
color: rgba(0, 0, 0, 0.65);
|
||||
}
|
||||
|
||||
// ── 审核操作区域 ──
|
||||
.auditSection {
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
.auditHeader {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.auditTitle {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: rgba(0, 0, 0, 0.85);
|
||||
padding-left: 8px;
|
||||
border-left: 3px solid #1677ff;
|
||||
}
|
||||
|
||||
.auditForm {
|
||||
// 横线分隔标题与表单
|
||||
border-top: 1px solid #f0f0f0;
|
||||
padding-top: 16px;
|
||||
}
|
||||
|
||||
.auditField {
|
||||
margin-bottom: 16px;
|
||||
|
||||
:global(.ant-form-item-label) {
|
||||
font-weight: 500;
|
||||
color: rgba(0, 0, 0, 0.65);
|
||||
}
|
||||
}
|
||||
|
||||
.auditField:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
import { defineComponent, reactive } from 'vue';
|
||||
import { Modal, Descriptions, Image, Radio, Input, Form } from 'ant-design-vue';
|
||||
import styles from './EventAuditModal.module.less';
|
||||
|
||||
const { TextArea } = Input;
|
||||
|
||||
interface EventAuditModalProps {
|
||||
visible: boolean;
|
||||
record: any;
|
||||
onClose: () => void;
|
||||
onSubmit?: (payload: { approved: boolean; comment: string }) => void;
|
||||
}
|
||||
|
||||
// ===== Mock 数据 =====
|
||||
const MOCK_DETAIL = {
|
||||
name: '第二届全国地学羽毛球邀请赛',
|
||||
eventTime: '2026.05.26 08:00 ~ 2026.05.31 22:00',
|
||||
descr:
|
||||
'赛事介绍赛事介绍赛事介绍赛事介绍赛事介绍赛事介绍赛事介绍赛事介绍赛事介绍赛事介绍赛事介绍赛事介绍赛事介绍赛事介绍赛事介绍赛事介绍赛事介绍赛事介绍赛事介绍赛事介绍赛事介绍赛事介绍赛事介绍。',
|
||||
registerTime: '2026.05.24 08:00 ~ 2026.05.26 22:00',
|
||||
cancelDeadline: '2026.05.25 22:00',
|
||||
region: '北京市 北京市 东城区',
|
||||
address: '李宁体育馆',
|
||||
venueNo: '李宁体育馆',
|
||||
publicEvent: '在首页展示赛事',
|
||||
needIdCard: '无需提供',
|
||||
needIdCardImage: '无需提供',
|
||||
createTime: '2026.05.26 08:00',
|
||||
creator: '张三',
|
||||
status: '待审核',
|
||||
enrolledCount: 100,
|
||||
cancelledCount: 1,
|
||||
maxCount: 1000,
|
||||
crossedCount: 50,
|
||||
completedCount: 50,
|
||||
cover: 'https://picsum.photos/seed/eventcover/600/300',
|
||||
};
|
||||
|
||||
/**
|
||||
* 赛事审核弹窗
|
||||
* 包含:基础信息 + 封面 + 审核操作(通过/未通过 + 审核内容)
|
||||
*/
|
||||
export default defineComponent({
|
||||
name: 'EventAuditModal',
|
||||
props: {
|
||||
visible: { type: Boolean, default: false },
|
||||
record: { type: Object, default: () => ({}) },
|
||||
onClose: { type: Function, required: true },
|
||||
onSubmit: { type: Function, default: undefined },
|
||||
},
|
||||
setup(props: EventAuditModalProps) {
|
||||
const detail = MOCK_DETAIL;
|
||||
|
||||
// 审核表单
|
||||
const auditForm = reactive({
|
||||
approved: true,
|
||||
comment: '',
|
||||
});
|
||||
|
||||
const handleOk = () => {
|
||||
if (!auditForm.comment.trim()) {
|
||||
// 不通过时强制要求填写审核内容
|
||||
if (!auditForm.approved) {
|
||||
// 这里交给校验,简化版本不做强制校验
|
||||
}
|
||||
}
|
||||
if (props.onSubmit) {
|
||||
props.onSubmit({ approved: auditForm.approved, comment: auditForm.comment });
|
||||
}
|
||||
};
|
||||
|
||||
return () => (
|
||||
<Modal
|
||||
visible={props.visible}
|
||||
title="审核"
|
||||
width={900}
|
||||
onCancel={props.onClose}
|
||||
onOk={handleOk}
|
||||
okText="提交审核"
|
||||
cancelText="取消"
|
||||
centered
|
||||
destroyOnClose
|
||||
>
|
||||
{/* ===== 基础信息 ===== */}
|
||||
<div class={styles.section}>
|
||||
<div class={styles.sectionTitle}>基础信息</div>
|
||||
<Descriptions size="small" bordered column={3} class={styles.desc}>
|
||||
<Descriptions.Item label="赛事名称">{detail.name}</Descriptions.Item>
|
||||
<Descriptions.Item label="赛事时间" span={2}>
|
||||
{detail.eventTime}
|
||||
</Descriptions.Item>
|
||||
|
||||
<Descriptions.Item label="赛事说明" span={3}>
|
||||
{detail.descr}
|
||||
</Descriptions.Item>
|
||||
|
||||
<Descriptions.Item label="报名起止时间">{detail.registerTime}</Descriptions.Item>
|
||||
<Descriptions.Item label="取消报名截止时间" span={2}>
|
||||
{detail.cancelDeadline}
|
||||
</Descriptions.Item>
|
||||
|
||||
<Descriptions.Item label="省市区">{detail.region}</Descriptions.Item>
|
||||
<Descriptions.Item label="地点">{detail.address}</Descriptions.Item>
|
||||
<Descriptions.Item label="场地编号">{detail.venueNo}</Descriptions.Item>
|
||||
|
||||
<Descriptions.Item label="公开活动">{detail.publicEvent}</Descriptions.Item>
|
||||
<Descriptions.Item label="证件号">{detail.needIdCard}</Descriptions.Item>
|
||||
<Descriptions.Item label="证件图片">{detail.needIdCardImage}</Descriptions.Item>
|
||||
|
||||
<Descriptions.Item label="创建时间">{detail.createTime}</Descriptions.Item>
|
||||
<Descriptions.Item label="创建人">{detail.creator}</Descriptions.Item>
|
||||
<Descriptions.Item label="状态">{detail.status}</Descriptions.Item>
|
||||
<Descriptions.Item label="报名人数">{detail.enrolledCount}</Descriptions.Item>
|
||||
<Descriptions.Item label="取消报名人数" span={1}>
|
||||
{detail.cancelledCount}
|
||||
</Descriptions.Item>
|
||||
|
||||
<Descriptions.Item label="最大人数">{detail.maxCount}</Descriptions.Item>
|
||||
<Descriptions.Item label="对岸数量">{detail.crossedCount}</Descriptions.Item>
|
||||
<Descriptions.Item label="已完成对岸数量">{detail.completedCount}</Descriptions.Item>
|
||||
</Descriptions>
|
||||
</div>
|
||||
|
||||
{/* ===== 封面 ===== */}
|
||||
<div class={styles.section}>
|
||||
<div class={styles.sectionTitle}>封面</div>
|
||||
<Image src={detail.cover} />
|
||||
</div>
|
||||
|
||||
{/* ===== 审核操作 ===== */}
|
||||
<div class={styles.auditSection}>
|
||||
<div class={styles.auditHeader}>
|
||||
<span class={styles.auditTitle}>审核操作</span>
|
||||
</div>
|
||||
<Form layout="horizontal" class={styles.auditForm}>
|
||||
<Form.Item label="审核状态" class={styles.auditField}>
|
||||
<Radio.Group v-model={auditForm.approved}>
|
||||
<Radio value={true}>通过</Radio>
|
||||
<Radio value={false}>未通过</Radio>
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
<Form.Item label="审核内容" class={styles.auditField}>
|
||||
<TextArea
|
||||
v-model={auditForm.comment}
|
||||
placeholder="请输入审核内容"
|
||||
rows={4}
|
||||
maxlength={500}
|
||||
showCount
|
||||
/>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
},
|
||||
});
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
Tooltip,
|
||||
Pagination,
|
||||
Modal,
|
||||
message,
|
||||
} from 'ant-design-vue';
|
||||
import {
|
||||
useEventListModel,
|
||||
@@ -23,6 +24,7 @@ import { regionOptions } from '@/utils/areaData';
|
||||
import { useContainerSize, useState } from '@/hooks';
|
||||
import EventRegulationModal from './components/EventRegulationModal';
|
||||
import EventDetailModal from './components/EventDetailModal';
|
||||
import EventAuditModal from './components/EventAuditModal';
|
||||
import styles from './index.module.less';
|
||||
import pageStyles from '@/components/page/pageLayout.module.less';
|
||||
|
||||
@@ -36,15 +38,15 @@ function renderBodyCell({
|
||||
text,
|
||||
record,
|
||||
onView,
|
||||
onRemove,
|
||||
onViewRegulation,
|
||||
onAudit,
|
||||
onToggleShelf,
|
||||
}: {
|
||||
column: any;
|
||||
text: any;
|
||||
record: any;
|
||||
onView: (record: any) => void;
|
||||
onRemove: (record: any) => void;
|
||||
onViewRegulation: (record: any) => void;
|
||||
onAudit: (record: any) => void;
|
||||
onToggleShelf: (record: any) => void;
|
||||
}) {
|
||||
if (column.dataIndex === 'eventName') {
|
||||
const maxLen = 15;
|
||||
@@ -61,14 +63,45 @@ function renderBodyCell({
|
||||
|
||||
if (column.key === 'regulation') {
|
||||
return (
|
||||
<a style={{ cursor: 'pointer' }} onClick={() => onViewRegulation(record)}>
|
||||
<a style={{ cursor: 'pointer' }} onClick={() => onView(record)}>
|
||||
查看
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
if (column.key === 'action') {
|
||||
const isRemoved = record.status === 'removed';
|
||||
const status = record.status;
|
||||
|
||||
// 待审核:查看 + 审核
|
||||
if (status === 'pending') {
|
||||
return (
|
||||
<Space>
|
||||
<Button type="link" size="small" onClick={() => onView(record)}>
|
||||
查看
|
||||
</Button>
|
||||
<Button type="link" size="small" onClick={() => onAudit(record)}>
|
||||
审核
|
||||
</Button>
|
||||
</Space>
|
||||
);
|
||||
}
|
||||
|
||||
// 报名中:查看 + 下架
|
||||
if (status === 'enrolling') {
|
||||
return (
|
||||
<Space>
|
||||
<Button type="link" size="small" onClick={() => onView(record)}>
|
||||
查看
|
||||
</Button>
|
||||
<Button type="link" size="small" danger onClick={() => onToggleShelf(record)}>
|
||||
下架
|
||||
</Button>
|
||||
</Space>
|
||||
);
|
||||
}
|
||||
|
||||
// 已下架:查看 + 上架
|
||||
if (status === 'removed') {
|
||||
return (
|
||||
<Space>
|
||||
<Button type="link" size="small" onClick={() => onView(record)}>
|
||||
@@ -77,16 +110,23 @@ function renderBodyCell({
|
||||
<Button
|
||||
type="link"
|
||||
size="small"
|
||||
danger={!isRemoved}
|
||||
style={isRemoved ? { color: '#52c41a' } : {}}
|
||||
onClick={() => onRemove(record)}
|
||||
style={{ color: '#52c41a' }}
|
||||
onClick={() => onToggleShelf(record)}
|
||||
>
|
||||
{isRemoved ? '上架' : '下架'}
|
||||
上架
|
||||
</Button>
|
||||
</Space>
|
||||
);
|
||||
}
|
||||
|
||||
// 审核不通过 / 已删除:仅查看
|
||||
return (
|
||||
<Button type="link" size="small" onClick={() => onView(record)}>
|
||||
查看
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -112,6 +152,7 @@ export default defineComponent({
|
||||
// 弹窗状态
|
||||
const [regulationModalVisible, setRegulationModalVisible] = useState<boolean>(false);
|
||||
const [detailModalVisible, setDetailModalVisible] = useState<boolean>(false);
|
||||
const [auditModalVisible, setAuditModalVisible] = useState<boolean>(false);
|
||||
const [currentRecord, setCurrentRecord] = useState<any>({});
|
||||
|
||||
const handleView = (record: any) => {
|
||||
@@ -120,7 +161,8 @@ export default defineComponent({
|
||||
setDetailModalVisible(true);
|
||||
};
|
||||
|
||||
const handleRemove = (record: any) => {
|
||||
/** 上架 / 下架 通用 */
|
||||
const handleToggleShelf = (record: any) => {
|
||||
const isRemoved = record.status === 'removed';
|
||||
const actionText = isRemoved ? '上架' : '下架';
|
||||
Modal.confirm({
|
||||
@@ -131,10 +173,26 @@ export default defineComponent({
|
||||
onOk: () => {
|
||||
console.log(`${actionText}赛事:`, record.eventId);
|
||||
// TODO: 替换为真实 API 调用
|
||||
message.success(`${actionText}成功`);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
/** 打开审核弹窗 */
|
||||
const handleAudit = (record: any) => {
|
||||
console.log('审核赛事:', record.eventId);
|
||||
setCurrentRecord(record);
|
||||
setAuditModalVisible(true);
|
||||
};
|
||||
|
||||
/** 提交审核 */
|
||||
const handleAuditSubmit = (payload: { approved: boolean; comment: string }) => {
|
||||
console.log('提交审核:', currentRecord.value.eventId, payload);
|
||||
// TODO: 替换为真实 API 调用
|
||||
message.success('审核提交成功');
|
||||
setAuditModalVisible(false);
|
||||
};
|
||||
|
||||
const handleViewRegulation = (record: any) => {
|
||||
console.log('查看规程:', record.eventId);
|
||||
setCurrentRecord(record);
|
||||
@@ -264,8 +322,8 @@ export default defineComponent({
|
||||
renderBodyCell({
|
||||
...args,
|
||||
onView: handleView,
|
||||
onRemove: handleRemove,
|
||||
onViewRegulation: handleViewRegulation,
|
||||
onAudit: handleAudit,
|
||||
onToggleShelf: handleToggleShelf,
|
||||
}),
|
||||
}}
|
||||
</Table>
|
||||
@@ -299,6 +357,14 @@ export default defineComponent({
|
||||
onClose={() => setDetailModalVisible(false)}
|
||||
/>
|
||||
)}
|
||||
{auditModalVisible.value && (
|
||||
<EventAuditModal
|
||||
visible={auditModalVisible.value}
|
||||
record={currentRecord.value}
|
||||
onClose={() => setAuditModalVisible(false)}
|
||||
onSubmit={handleAuditSubmit}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
||||
@@ -9,11 +9,11 @@ import { useState, useDebounce, useThrottleFn } from '@/hooks';
|
||||
/** 赛事状态选项 */
|
||||
export const EVENT_STATUS_OPTIONS = [
|
||||
{ value: '', label: '全部' },
|
||||
{ value: 'pending', label: '待审核' },
|
||||
{ value: 'enrolling', label: '报名中' },
|
||||
{ value: 'ongoing', label: '进行中' },
|
||||
{ value: 'finished', label: '已结束' },
|
||||
{ value: 'deleted', label: '已删除' },
|
||||
{ value: 'removed', label: '已下架' },
|
||||
{ value: 'rejected', label: '审核不通过' },
|
||||
] as const;
|
||||
|
||||
/** 公开活动选项 */
|
||||
@@ -61,18 +61,18 @@ const MOCK_DATA = Array.from({ length: 12 }, (_, i) => ({
|
||||
creatorNickname: ['张运营', '李管理', '王策划', '赵赛事'][i % 4],
|
||||
creatorPhone: ['13800138001', '13900139002', '13700137003', '13600136004'][i % 4],
|
||||
createTime: '2026-07-01 14:30:00',
|
||||
status: (['enrolling', 'ongoing', 'finished', 'deleted', 'removed'] as const)[i % 5],
|
||||
status: (['pending', 'enrolling', 'deleted', 'removed', 'rejected'] as const)[i % 5],
|
||||
enrolledCount: [128, 256, 512, 64, 0][i % 5],
|
||||
cancelledCount: [5, 12, 8, 3, 0][i % 5],
|
||||
}));
|
||||
|
||||
/** 状态标签映射 */
|
||||
const STATUS_MAP: Record<string, { label: string; color: string }> = {
|
||||
pending: { label: '待审核', color: 'gold' },
|
||||
enrolling: { label: '报名中', color: 'blue' },
|
||||
ongoing: { label: '进行中', color: 'green' },
|
||||
finished: { label: '已结束', color: 'default' },
|
||||
deleted: { label: '已删除', color: 'red' },
|
||||
removed: { label: '已下架', color: 'orange' },
|
||||
rejected: { label: '审核不通过', color: 'volcano' },
|
||||
};
|
||||
|
||||
// ============================================================
|
||||
|
||||
Reference in New Issue
Block a user