123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682 |
- import {
- addMatch,
- batchDeleteMatches,
- createTeam,
- deleteMatch,
- getMatches,
- getTeamLogoByName,
- updateMatch,
- updatePredictionsForMatch,
- uploadImage,
- } from '@/services/api';
- import { PlusOutlined } from '@ant-design/icons';
- import type { ActionType, ProColumns } from '@ant-design/pro-components';
- import { debounce } from 'lodash';
- import {
- ModalForm,
- ProFormDatePicker,
- ProFormDigit,
- ProFormSelect,
- ProFormText,
- ProFormUploadButton,
- } from '@ant-design/pro-form';
- import { FooterToolbar, PageContainer, ProTable } from '@ant-design/pro-components';
- import { Button, FormInstance, message, Popconfirm } from 'antd';
- import Modal from 'antd/lib/modal';
- import React, { useCallback, useMemo, useRef, useState } from 'react';
- import UpdateForm from './UpdateForm';
- const BasketballMatch: React.FC = () => {
- const [createModalOpen, handleModalOpen] = useState<boolean>(false);
- const [updateModalOpen, handleUpdateModalOpen] = useState<boolean>(false);
- const actionRef = useRef<ActionType>();
- const [currentRow, setCurrentRow] = useState<API.MatchItem>();
- const [selectedRows, setSelectedRows] = useState<API.MatchItem[]>([]);
- const [pageSize, setPageSize] = useState<number>(10);
- const formRef = useRef<FormInstance>(null);
- const columns: ProColumns<API.MatchItem>[] = [
- {
- title: '主队',
- dataIndex: ['homeTeam', 'name'],
- valueType: 'text',
- width: 100,
- ellipsis: true,
- },
- {
- title: '客队',
- dataIndex: ['awayTeam', 'name'],
- valueType: 'text',
- width: 100,
- ellipsis: true,
- },
- {
- title: '日期',
- dataIndex: 'date',
- valueType: 'date',
- width: 100,
- ellipsis: true,
- },
- {
- title: '时间',
- dataIndex: 'time',
- valueType: 'text',
- },
- {
- title: '联赛',
- dataIndex: 'league',
- valueType: 'text',
- width: 100,
- ellipsis: true,
- },
- {
- title: '状态',
- dataIndex: 'status',
- valueEnum: {
- 未开始: { text: '未开始', status: 'Default' },
- 进行中: { text: '进行中', status: 'Processing' },
- 已结束: { text: '已结束', status: 'Success' },
- },
- },
- {
- title: '参与人数',
- dataIndex: 'canyuNumber',
- valueType: 'digit',
- },
- {
- title: '主队得分',
- dataIndex: 'homeTeamScore',
- valueType: 'digit',
- },
- {
- title: '客队得分',
- dataIndex: 'awayTeamScore',
- valueType: 'digit',
- },
- {
- title: '让分',
- dataIndex: ['basketball', 'spread', 'points'],
- valueType: 'digit',
- render: (_, record) => {
- const points = record.basketball?.spread?.points;
- const favorite = record.basketball?.spread?.favorite;
- if (!points || !favorite) return '-';
- return `${favorite === 'home' ? '主队' : '客队'}让${Math.abs(points)}分`;
- },
- },
- {
- title: '总分线',
- dataIndex: ['basketball', 'totalPoints', 'value'],
- valueType: 'digit',
- },
- {
- title: '胜负结果',
- dataIndex: ['basketball', 'result', 'whoWillWin'],
- valueEnum: {
- home: { text: '主队胜' },
- away: { text: '客队胜' },
- },
- },
- {
- title: '让分结果',
- dataIndex: ['basketball', 'result', 'spreadWinner'],
- valueEnum: {
- home: { text: '主队赢盘' },
- away: { text: '客队赢盘' },
- },
- },
- {
- title: '总分结果',
- dataIndex: ['basketball', 'result', 'totalPointsResult'],
- valueEnum: {
- over: { text: '大分' },
- under: { text: '小分' },
- },
- },
- {
- title: '胜负预测积分',
- dataIndex: ['basketball', 'pointRewards', 'whoWillWin'],
- valueType: 'digit',
- },
- {
- title: '总分预测积分',
- dataIndex: ['basketball', 'pointRewards', 'totalPoints'],
- valueType: 'digit',
- },
- {
- title: '让分预测积分',
- dataIndex: ['basketball', 'pointRewards', 'spread'],
- valueType: 'digit',
- },
- {
- title: '操作',
- dataIndex: 'option',
- valueType: 'option',
- render: (_, record) => [
- <a
- key="edit"
- onClick={() => {
- console.log('record', record);
- handleUpdateModalOpen(true);
- setCurrentRow(record);
- }}
- >
- 编辑
- </a>,
- <Popconfirm
- key="delete"
- title="确认删除"
- description={`您确定要删除 ${record.homeTeam.name} vs ${record.awayTeam.name} 的比赛记录吗?`}
- onConfirm={() => handleDelete(record)}
- okText="确定"
- cancelText="取消"
- >
- <a>删除</a>
- </Popconfirm>,
- ],
- },
- ];
- const handleDelete = async (record: API.MatchItem) => {
- const res = await deleteMatch(record._id);
- if (res.success) {
- message.success('比赛记录已成功删除');
- actionRef.current?.reload();
- } else {
- message.error('删除失败,请重试');
- }
- };
- // 批量删除
- const handleBatchDelete = async (selectedRowKeys: string[]) => {
- if (selectedRowKeys.length === 0) return;
- console.log('selectedRowKeys', selectedRowKeys);
- const res = await batchDeleteMatches(selectedRowKeys);
- if (res.success) {
- message.success(`${res.message}`);
- actionRef.current?.reload();
- }
- };
- const [homeTeamLogoUrl, setHomeTeamLogoUrl] = useState('');
- const [awayTeamLogoUrl, setAwayTeamLogoUrl] = useState('');
- const [homeTeamName, setHomeTeamName] = useState('');
- const [awayTeamName, setAwayTeamName] = useState('');
- console.log(
- 'homeTeamName,awayTeamName, homeTeamLogoUrl,awayTeamLogoUrl',
- homeTeamName,
- awayTeamName,
- homeTeamLogoUrl,
- awayTeamLogoUrl,
- );
- const handleUpload = useCallback((teamType) => async (file) => {
- const formData = new FormData();
- formData.append('file', file);
- formData.append('teamName', teamType === 'home' ? homeTeamName : awayTeamName);
- formData.append('teamType', teamType);
- try {
- const uploadData = await uploadImage(formData);
- console.log('uploadData', uploadData);
- if (uploadData.success) {
- // 文件上传成功后,更新球队信息
- const teamData = await createTeam({
- teamName: teamType === 'home' ? homeTeamName : awayTeamName,
- logoUrl: uploadData.url,
- teamType,
- });
- if (teamData.success) {
- message.success(`图片上传成功`);
- if (teamType === 'home') {
- setHomeTeamLogoUrl(uploadData.url);
- } else {
- setAwayTeamLogoUrl(uploadData.url);
- }
- return {
- status: 'done',
- url: uploadData.url,
- };
- }
- }
- } catch (error) {
- console.error('Upload error:', error);
- message.error(`文件上传失败: ${error.message}`);
- return {
- status: 'error',
- error: error.message,
- };
- }
- });
- const debouncedFetchTeamLogo = useRef(
- debounce(async (teamName, setLogoFunc) => {
- console.log('teamName', teamName);
- if (!teamName) {
- setLogoFunc('');
- return;
- }
- try {
- const res = await getTeamLogoByName({ name: teamName });
- if (res.success && res.team && res.team.logo) {
- setLogoFunc(res.team.logo);
- } else {
- setLogoFunc('');
- }
- } catch (error) {
- console.error('Error fetching team logo:', error);
- setLogoFunc('');
- }
- }, 300),
- ).current;
- const handleUpdateSubmit = async (value: API.MatchItem) => {
- console.log('value', value);
- if (value.status === '已结束') {
- // 检查必填字段
- const missingFields = checkRequiredFields(value);
- if (missingFields.length > 0) {
- message.error(`以下字段不能为空: ${missingFields.join(', ')}`);
- return;
- }
- Modal.confirm({
- title: '确认结束比赛?',
- content: '比赛结束后将会给预测对本次比赛的用户添加积分,是否确认结束比赛?',
- okText: '确定',
- cancelText: '取消',
- onOk: async () => {
- await updateMatchAndHandleResponse(value);
- },
- onCancel: () => {
- message.info('取消了比赛结束操作');
- },
- });
- } else {
- await updateMatchAndHandleResponse(value);
- }
- };
- const checkRequiredFields = (value: API.MatchItem): string[] => {
- const missingFields: string[] = [];
- // 检查 basketball.result 字段
- // if (!value.basketball?.result?.spreadWinner) missingFields.push('让分结果');
- // if (!value.basketball?.result?.totalPointsResult) missingFields.push('总分结果');
- // 检查 basketball.pointRewards 字段
- if (
- value.basketball?.pointRewards?.spread === undefined ||
- value.basketball?.pointRewards?.spread === null
- ) {
- missingFields.push('让分预测积分');
- }
- if (
- value.basketball?.pointRewards?.totalPoints === undefined ||
- value.basketball?.pointRewards?.totalPoints === null
- ) {
- missingFields.push('总分预测积分');
- }
- // 检查比分
- if (value.homeTeamScore === undefined || value.homeTeamScore === null) {
- missingFields.push('主队得分');
- }
- if (value.awayTeamScore === undefined || value.awayTeamScore === null) {
- missingFields.push('客队得分');
- }
- return missingFields;
- };
- const updateMatchAndHandleResponse = async (value: API.MatchItem) => {
- try {
- // 确保更新的数据包含足球类型
- const updateData = {
- ...value,
- type: 'basketball',
- };
- const res = await updateMatch(value._id, 'basketball', updateData);
- console.log('updateMatch res', res);
- if (res.success) {
- message.success('修改成功');
- if (value.status === '已结束') {
- await handleMatchEnd(value._id);
- }
- handleUpdateModalOpen(false);
- setCurrentRow(undefined);
- actionRef.current?.reload();
- } else {
- message.error(res.message || '修改失败,请重试');
- }
- } catch (error) {
- console.error('更新比赛时出错:', error);
- message.error('修改失败,请重试');
- }
- };
- // 新增函数来处理比赛结束后的预测更新
- const handleMatchEnd = async (matchId: string) => {
- try {
- const res = await updatePredictionsForMatch(matchId);
- if (res.success) {
- message.success('用户积分已更新');
- } else {
- message.error('更新用户积分时出错');
- }
- } catch (error) {
- console.error('处理比赛结束时出错:', error);
- message.error('更新用户积分失败');
- }
- };
- return (
- <PageContainer>
- <ProTable<API.MatchItem, API.PageParams>
- actionRef={actionRef}
- rowKey="_id"
- search={{
- labelWidth: 120,
- }}
- toolBarRender={() => [
- <Button
- type="primary"
- key="primary"
- onClick={() => {
- setHomeTeamLogoUrl('');
- setAwayTeamLogoUrl('');
- setHomeTeamName('');
- setAwayTeamName('');
- formRef.current?.resetFields();
- handleModalOpen(true);
- }}
- >
- <PlusOutlined /> 新建
- </Button>,
- ]}
- request={(params) =>
- getMatches({
- ...params,
- type: 'basketball',
- })
- }
- columns={columns}
- scroll={{ x: 1480 }}
- pagination={{
- showSizeChanger: true,
- pageSize: pageSize,
- onChange: (page, pageSize) => {
- setPageSize(pageSize);
- // 这里可以处理分页参数的变化
- console.log('当前页:', page, '每页显示条数:', pageSize);
- if (actionRef.current) {
- actionRef.current.reload();
- }
- },
- }}
- rowSelection={{
- onChange: (_, selectedRows) => {
- setSelectedRows(selectedRows);
- },
- }}
- />
- {selectedRows?.length > 0 && (
- <FooterToolbar
- extra={
- <div>
- 已选择 <a style={{ fontWeight: 600 }}>{selectedRows.length}</a> 项
- </div>
- }
- >
- <Button
- onClick={async () => {
- console.log('selectedRows', selectedRows);
- const selectedRowKeys = selectedRows.map((row) => row._id);
- await handleBatchDelete(selectedRowKeys);
- setSelectedRows([]);
- actionRef.current?.reloadAndRest?.();
- }}
- >
- 批量删除
- </Button>
- </FooterToolbar>
- )}
- {/* 新建弹框 */}
- <ModalForm
- title="新建篮球赛事"
- width="400px"
- open={createModalOpen}
- onOpenChange={handleModalOpen}
- formRef={formRef}
- onFinish={async (value) => {
- const formData = {
- type: 'basketball',
- ...value,
- homeTeam: {
- ...value.homeTeam,
- logo: homeTeamLogoUrl,
- },
- awayTeam: {
- ...value.awayTeam,
- logo: awayTeamLogoUrl,
- },
- };
- const res = await addMatch('basketball', formData as API.MatchItem);
- if (res.success) {
- message.success('创建成功');
- handleModalOpen(false);
- if (actionRef.current) {
- actionRef.current.reload();
- }
- formRef.current?.resetFields();
- }
- }}
- >
- <ProFormText
- rules={[{ required: true }]}
- width="md"
- name={['homeTeam', 'name']}
- label="主队名称"
- fieldProps={{
- onChange: (e) => {
- const name = e.target.value;
- setHomeTeamName(name);
- debouncedFetchTeamLogo(name, setHomeTeamLogoUrl);
- },
- }}
- />
- <ProFormUploadButton
- name={['homeTeam', 'logo']}
- label="主队Logo"
- max={1}
- action={handleUpload('home')}
- fileList={useMemo(() => {
- return homeTeamLogoUrl ? [{ url: `${API_URL}${homeTeamLogoUrl}` }] : [];
- }, [homeTeamLogoUrl])}
- onChange={({ fileList }) => {
- if (fileList.length === 0) {
- setHomeTeamLogoUrl('');
- }
- }}
- disabled={!homeTeamName}
- />
- <ProFormText
- rules={[
- {
- required: true,
- message: '客队名称是必填项',
- },
- ]}
- width="md"
- name={['awayTeam', 'name']}
- label="客队名称"
- fieldProps={{
- onChange: (e) => {
- const name = e.target.value;
- setAwayTeamName(name);
- debouncedFetchTeamLogo(name, setAwayTeamLogoUrl);
- },
- }}
- />
- <ProFormUploadButton
- name={['awayTeam', 'logo']}
- label="客队Logo"
- max={1}
- action={handleUpload('away')}
- fileList={useMemo(() => {
- return awayTeamLogoUrl ? [{ url: `${API_URL}/${awayTeamLogoUrl}` }] : [];
- }, [awayTeamLogoUrl])}
- onChange={({ fileList }) => {
- if (fileList.length === 0) {
- setAwayTeamLogoUrl('');
- }
- }}
- disabled={!awayTeamName}
- />
- <ProFormDatePicker
- rules={[
- {
- required: true,
- message: '比赛日期是必填项',
- },
- ]}
- width="md"
- name="date"
- label="比赛日期"
- />
- <ProFormText
- rules={[
- {
- required: true,
- message: '比赛时间是必填项',
- },
- ]}
- width="md"
- name="time"
- label="比赛时间"
- />
- <ProFormText width="md" name="league" label="联赛" />
- <ProFormDigit width="md" name={['basketball', 'spread', 'points']} label="预测让分值"
- rules={[
- {
- required: true,
- message: '预测让分值是必填项',
- },
- ]} />
- <ProFormSelect
- width="md"
- name={['basketball', 'spread', 'favorite']}
- label="预测让分方"
- valueEnum={{
- home: '主队',
- away: '客队',
- }}
- rules={[
- {
- required: true,
- message: '预测让分方是必填项',
- },
- ]}
- />
- <ProFormDigit width="md" name={['basketball', 'totalPoints', 'value']} label="预测总得分" rules={[
- {
- required: true,
- message: '预测总得分是必填项',
- },
- ]} />
- <ProFormDigit
- width="md"
- name={['basketball', 'pointRewards', 'whoWillWin']}
- label="总分预测积分"
- rules={[
- {
- required: true,
- message: '总分预测积分是必填项',
- },
- ]}
- />
- <ProFormDigit
- width="md"
- name={['basketball', 'pointRewards', 'spread']}
- label="让分预测积分"
- rules={[
- {
- required: true,
- message: '让分预测积分是必填项',
- },
- ]}
- />
- <ProFormDigit
- width="md"
- name={['basketball', 'pointRewards', 'totalPoints']}
- label="总分预测积分"
- rules={[
- {
- required: true,
- message: '总分预测积分是必填项',
- },
- ]}
- />
- <ProFormSelect
- rules={[
- {
- required: true,
- message: '比赛状态是必填项',
- },
- ]}
- width="md"
- name="status"
- label="比赛状态"
- valueEnum={{
- 未开始: '未开始',
- 进行中: '进行中',
- 已结束: '已结束',
- }}
- />
- </ModalForm>
- {/* 新建弹框--END */}
- <UpdateForm
- onSubmit={handleUpdateSubmit}
- onCancel={() => {
- handleUpdateModalOpen(false);
- }}
- updateModalOpen={updateModalOpen}
- values={currentRow || {}}
- />
- </PageContainer>
- );
- };
- export default BasketballMatch;
|