Merge branch 'main' of git.waypeak.work:martial/martial-web
This commit is contained in:
46
package-lock.json
generated
46
package-lock.json
generated
@@ -851,7 +851,6 @@
|
||||
"version": "4.17.12",
|
||||
"resolved": "https://registry.npmmirror.com/@types/lodash-es/-/lodash-es-4.17.12.tgz",
|
||||
"integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/lodash": "*"
|
||||
}
|
||||
@@ -874,7 +873,6 @@
|
||||
"version": "2.3.4",
|
||||
"resolved": "https://registry.npmmirror.com/@uppy/core/-/core-2.3.4.tgz",
|
||||
"integrity": "sha512-iWAqppC8FD8mMVqewavCz+TNaet6HPXitmGXpGGREGrakZ4FeuWytVdrelydzTdXx6vVKkOmI2FLztGg73sENQ==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@transloadit/prettier-bytes": "0.0.7",
|
||||
"@uppy/store-default": "^2.1.1",
|
||||
@@ -903,7 +901,6 @@
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmmirror.com/@uppy/xhr-upload/-/xhr-upload-2.1.3.tgz",
|
||||
"integrity": "sha512-YWOQ6myBVPs+mhNjfdWsQyMRWUlrDLMoaG7nvf/G6Y3GKZf8AyjFDjvvJ49XWQ+DaZOftGkHmF1uh/DBeGivJQ==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@uppy/companion-client": "^2.2.2",
|
||||
"@uppy/utils": "^4.1.2",
|
||||
@@ -1058,7 +1055,6 @@
|
||||
"version": "1.1.7",
|
||||
"resolved": "https://registry.npmmirror.com/@wangeditor/basic-modules/-/basic-modules-1.1.7.tgz",
|
||||
"integrity": "sha512-cY9CPkLJaqF05STqfpZKWG4LpxTMeGSIIF1fHvfm/mz+JXatCagjdkbxdikOuKYlxDdeqvOeBmsUBItufDLXZg==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"is-url": "^1.2.4"
|
||||
},
|
||||
@@ -1089,7 +1085,6 @@
|
||||
"version": "1.1.19",
|
||||
"resolved": "https://registry.npmmirror.com/@wangeditor/core/-/core-1.1.19.tgz",
|
||||
"integrity": "sha512-KevkB47+7GhVszyYF2pKGKtCSj/YzmClsD03C3zTt+9SR2XWT5T0e3yQqg8baZpcMvkjs1D8Dv4fk8ok/UaS2Q==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/event-emitter": "^0.3.3",
|
||||
"event-emitter": "^0.3.5",
|
||||
@@ -1119,7 +1114,6 @@
|
||||
"version": "5.1.23",
|
||||
"resolved": "https://registry.npmmirror.com/@wangeditor/editor/-/editor-5.1.23.tgz",
|
||||
"integrity": "sha512-0RxfeVTuK1tktUaPROnCoFfaHVJpRAIE2zdS0mpP+vq1axVQpLjM8+fCvKzqYIkH0Pg+C+44hJpe3VVroSkEuQ==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@uppy/core": "^2.1.1",
|
||||
"@uppy/xhr-upload": "^2.0.3",
|
||||
@@ -1476,7 +1470,6 @@
|
||||
"version": "11.13.1",
|
||||
"resolved": "https://registry.npmmirror.com/diagram-js/-/diagram-js-11.13.1.tgz",
|
||||
"integrity": "sha512-6kO0rBN6aBIQiMELfv1oX2Ohes/brlIPuOVZUYAioeWM0EyuazhAXgHeq8iKFt29daU9NGRr4n78esGx8QjtjQ==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@bpmn-io/diagram-js-ui": "^0.2.2",
|
||||
"clsx": "^1.2.1",
|
||||
@@ -1514,7 +1507,6 @@
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/dom7/-/dom7-3.0.0.tgz",
|
||||
"integrity": "sha512-oNlcUdHsC4zb7Msx7JN3K0Nro1dzJ48knvBOnDPKJ2GV9wl1i5vydJZUSyOfrkKFDZEud/jBsTk92S/VGSAe/g==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"ssr-window": "^3.0.0-alpha.1"
|
||||
}
|
||||
@@ -1540,7 +1532,6 @@
|
||||
"version": "2.7.3",
|
||||
"resolved": "https://registry.npmjs.org/element-plus/-/element-plus-2.7.3.tgz",
|
||||
"integrity": "sha512-OaqY1kQ2xzNyRFyge3fzM7jqMwux+464RBEqd+ybRV9xPiGxtgnj/sVK4iEbnKnzQIa9XK03DOIFzoToUhu1DA==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@ctrl/tinycolor": "^3.4.1",
|
||||
"@element-plus/icons-vue": "^2.3.1",
|
||||
@@ -1866,8 +1857,7 @@
|
||||
"node_modules/is-hotkey": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmmirror.com/is-hotkey/-/is-hotkey-0.2.0.tgz",
|
||||
"integrity": "sha512-UknnZK4RakDmTgz4PI1wIph5yxSs/mvChWs9ifnlXsKuXgWmOkY/hAE0H/k2MIqH0RlRye0i1oC07MCRSD28Mw==",
|
||||
"peer": true
|
||||
"integrity": "sha512-UknnZK4RakDmTgz4PI1wIph5yxSs/mvChWs9ifnlXsKuXgWmOkY/hAE0H/k2MIqH0RlRye0i1oC07MCRSD28Mw=="
|
||||
},
|
||||
"node_modules/is-number": {
|
||||
"version": "7.0.0",
|
||||
@@ -1942,14 +1932,12 @@
|
||||
"node_modules/lodash": {
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz",
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
||||
"peer": true
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
||||
},
|
||||
"node_modules/lodash-es": {
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmmirror.com/lodash-es/-/lodash-es-4.17.21.tgz",
|
||||
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==",
|
||||
"peer": true
|
||||
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="
|
||||
},
|
||||
"node_modules/lodash-unified": {
|
||||
"version": "1.0.3",
|
||||
@@ -1964,44 +1952,37 @@
|
||||
"node_modules/lodash.camelcase": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmmirror.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
|
||||
"integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==",
|
||||
"peer": true
|
||||
"integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA=="
|
||||
},
|
||||
"node_modules/lodash.clonedeep": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmmirror.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
|
||||
"integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==",
|
||||
"peer": true
|
||||
"integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ=="
|
||||
},
|
||||
"node_modules/lodash.debounce": {
|
||||
"version": "4.0.8",
|
||||
"resolved": "https://registry.npmmirror.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
|
||||
"integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==",
|
||||
"peer": true
|
||||
"integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow=="
|
||||
},
|
||||
"node_modules/lodash.foreach": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmmirror.com/lodash.foreach/-/lodash.foreach-4.5.0.tgz",
|
||||
"integrity": "sha512-aEXTF4d+m05rVOAUG3z4vZZ4xVexLKZGF0lIxuHZ1Hplpk/3B6Z1+/ICICYRLm7c41Z2xiejbkCkJoTlypoXhQ==",
|
||||
"peer": true
|
||||
"integrity": "sha512-aEXTF4d+m05rVOAUG3z4vZZ4xVexLKZGF0lIxuHZ1Hplpk/3B6Z1+/ICICYRLm7c41Z2xiejbkCkJoTlypoXhQ=="
|
||||
},
|
||||
"node_modules/lodash.isequal": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmmirror.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
|
||||
"integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==",
|
||||
"peer": true
|
||||
"integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ=="
|
||||
},
|
||||
"node_modules/lodash.throttle": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmmirror.com/lodash.throttle/-/lodash.throttle-4.1.1.tgz",
|
||||
"integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==",
|
||||
"peer": true
|
||||
"integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ=="
|
||||
},
|
||||
"node_modules/lodash.toarray": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmmirror.com/lodash.toarray/-/lodash.toarray-4.4.0.tgz",
|
||||
"integrity": "sha512-QyffEA3i5dma5q2490+SgCvDN0pXLmRGSyAANuVi0HQ01Pkfr9fuoKQW8wm1wGBnJITs/mS7wQvS6VshUEBFCw==",
|
||||
"peer": true
|
||||
"integrity": "sha512-QyffEA3i5dma5q2490+SgCvDN0pXLmRGSyAANuVi0HQ01Pkfr9fuoKQW8wm1wGBnJITs/mS7wQvS6VshUEBFCw=="
|
||||
},
|
||||
"node_modules/magic-string": {
|
||||
"version": "0.30.10",
|
||||
@@ -2116,7 +2097,6 @@
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"nanoid": "bin/nanoid.cjs"
|
||||
},
|
||||
@@ -2308,7 +2288,6 @@
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.18.0.tgz",
|
||||
"integrity": "sha512-QmJz14PX3rzbJCN1SG4Xe/bAAX2a6NpCP8ab2vfu2GiUr8AQcr2nCV/oEO3yneFarB67zk8ShlIyWb2LGTb3Sg==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/estree": "1.0.5"
|
||||
},
|
||||
@@ -2367,7 +2346,6 @@
|
||||
"resolved": "https://registry.npmjs.org/sass/-/sass-1.77.2.tgz",
|
||||
"integrity": "sha512-eb4GZt1C3avsX3heBNlrc7I09nyT00IUuo4eFhAbeXWU2fvA7oXI53SxODVAA+zgZCk9aunAZgO+losjR3fAwA==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"chokidar": ">=3.0.0 <4.0.0",
|
||||
"immutable": "^4.0.0",
|
||||
@@ -2403,7 +2381,6 @@
|
||||
"version": "0.72.8",
|
||||
"resolved": "https://registry.npmmirror.com/slate/-/slate-0.72.8.tgz",
|
||||
"integrity": "sha512-/nJwTswQgnRurpK+bGJFH1oM7naD5qDmHd89JyiKNT2oOKD8marW0QSBtuFnwEbL5aGCS8AmrhXQgNOsn4osAw==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"immer": "^9.0.6",
|
||||
"is-plain-object": "^5.0.0",
|
||||
@@ -2425,7 +2402,6 @@
|
||||
"version": "3.5.1",
|
||||
"resolved": "https://registry.npmmirror.com/snabbdom/-/snabbdom-3.5.1.tgz",
|
||||
"integrity": "sha512-wHMNIOjkm/YNE5EM3RCbr/+DVgPg6AqQAX1eOxO46zYNvCXjKP5Y865tqQj3EXnaMBjkxmQA5jFuDpDK/dbfiA==",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=8.3.0"
|
||||
}
|
||||
@@ -2623,7 +2599,6 @@
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-5.2.12.tgz",
|
||||
"integrity": "sha512-/gC8GxzxMK5ntBwb48pR32GGhENnjtY30G4A0jemunsBkiEZFw60s8InGpN8gkhHEkjnRK1aSAxeQgwvFhUHAA==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"esbuild": "^0.20.1",
|
||||
"postcss": "^8.4.38",
|
||||
@@ -2791,7 +2766,6 @@
|
||||
"version": "3.4.27",
|
||||
"resolved": "https://registry.npmjs.org/vue/-/vue-3.4.27.tgz",
|
||||
"integrity": "sha512-8s/56uK6r01r1icG/aEOHqyMVxd1bkYcSe9j8HcKtr/xTOFWvnzIVTehNW+5Yt89f+DLBe4A569pnZLS5HzAMA==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@vue/compiler-dom": "3.4.27",
|
||||
"@vue/compiler-sfc": "3.4.27",
|
||||
|
||||
@@ -183,3 +183,16 @@ export const saveDispatch = (data) => {
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出赛程表
|
||||
* @param {Number} competitionId - 赛事ID
|
||||
*/
|
||||
export const exportSchedule = (competitionId) => {
|
||||
return request({
|
||||
url: '/martial/export/schedule',
|
||||
method: 'get',
|
||||
params: { competitionId },
|
||||
responseType: 'blob'
|
||||
})
|
||||
}
|
||||
|
||||
@@ -98,6 +98,10 @@ axios.interceptors.request.use(
|
||||
axios.interceptors.response.use(
|
||||
res => {
|
||||
NProgress.done();
|
||||
// 如果是 blob 类型响应(文件下载),直接返回
|
||||
if (res.config.responseType === 'blob') {
|
||||
return res;
|
||||
}
|
||||
const status = res.data.code || res.status;
|
||||
const statusWhiteList = website.statusWhiteList || [];
|
||||
const message = res.data.msg || res.data.error_description || '系统错误';
|
||||
|
||||
@@ -181,13 +181,13 @@ export default {
|
||||
try {
|
||||
const res = await getScheduleResult(competition.id)
|
||||
if (res.data?.data) {
|
||||
this.$set(this.scheduleStatusMap, competition.id, res.data.data.isCompleted || false)
|
||||
this.scheduleStatusMap[competition.id] = res.data.data.isCompleted || false
|
||||
} else {
|
||||
this.$set(this.scheduleStatusMap, competition.id, false)
|
||||
this.scheduleStatusMap[competition.id] = false
|
||||
}
|
||||
} catch (err) {
|
||||
// 如果获取失败,默认为未完成
|
||||
this.$set(this.scheduleStatusMap, competition.id, false)
|
||||
this.scheduleStatusMap[competition.id] = false
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
355
src/views/martial/order/index.vue.bak
Normal file
355
src/views/martial/order/index.vue.bak
Normal file
@@ -0,0 +1,355 @@
|
||||
<template>
|
||||
<div class="martial-order-container">
|
||||
<el-card shadow="hover">
|
||||
<div class="page-header">
|
||||
<h2 class="page-title">订单管理</h2>
|
||||
</div>
|
||||
<el-form :inline="true" :model="searchForm" class="search-form">
|
||||
<el-form-item>
|
||||
<el-input
|
||||
v-model="searchForm.keyword"
|
||||
placeholder="搜索赛事名称"
|
||||
clearable
|
||||
size="small"
|
||||
style="width: 240px"
|
||||
>
|
||||
<i slot="prefix" class="el-input__icon el-icon-search"></i>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-select
|
||||
v-model="searchForm.status"
|
||||
placeholder="赛事状态"
|
||||
clearable
|
||||
size="small"
|
||||
style="width: 180px"
|
||||
>
|
||||
<el-option label="未开始" :value="1"></el-option>
|
||||
<el-option label="报名中" :value="2"></el-option>
|
||||
<el-option label="进行中" :value="3"></el-option>
|
||||
<el-option label="已结束" :value="4"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" size="small" @click="handleSearch">查询</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
:data="tableData"
|
||||
border
|
||||
stripe
|
||||
size="small"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-table-column type="index" label="序号" width="60" align="center"></el-table-column>
|
||||
<el-table-column prop="competitionName" label="赛事名称" min-width="200" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column prop="competitionCode" label="赛事编号" width="150"></el-table-column>
|
||||
<el-table-column prop="organizer" label="主办单位" min-width="150" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column prop="location" label="举办地点" width="120"></el-table-column>
|
||||
<el-table-column prop="registrationTime" label="报名时间" width="180" show-overflow-tooltip>
|
||||
<template #default="scope">
|
||||
<span>{{ formatDateRange(scope.row.registrationStartTime, scope.row.registrationEndTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="competitionTime" label="比赛时间" width="180" show-overflow-tooltip>
|
||||
<template #default="scope">
|
||||
<span>{{ formatDateRange(scope.row.competitionStartTime, scope.row.competitionEndTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="status" label="状态" width="90" align="center">
|
||||
<template #default="scope">
|
||||
<el-tag :type="getStatusType(scope.row.status)" size="small">
|
||||
{{ getStatusText(scope.row.status) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="320" align="center" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-button type="primary" size="small" @click="handleRegistrationDetail(scope.row)">报名详情</el-button>
|
||||
<el-button type="success" size="small" @click="handleSchedule(scope.row)">编排</el-button>
|
||||
<el-button
|
||||
type="warning"
|
||||
size="small"
|
||||
@click="handleDispatch(scope.row)"
|
||||
:title="isScheduleCompleted(scope.row.id) ? '进入调度' : '请先完成编排'"
|
||||
>
|
||||
调度
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<el-pagination
|
||||
class="pagination"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
:current-page="pagination.current"
|
||||
:page-sizes="[10, 20, 50, 100]"
|
||||
:page-size="pagination.size"
|
||||
layout="total, sizes, prev, pager, next"
|
||||
:total="pagination.total"
|
||||
small
|
||||
></el-pagination>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getCompetitionList } from '@/api/martial/competition'
|
||||
import { getScheduleResult } from '@/api/martial/activitySchedule'
|
||||
|
||||
export default {
|
||||
name: 'MartialOrderList',
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
searchForm: {
|
||||
keyword: '',
|
||||
status: null
|
||||
},
|
||||
tableData: [],
|
||||
pagination: {
|
||||
current: 1,
|
||||
size: 10,
|
||||
total: 0
|
||||
},
|
||||
scheduleStatusMap: {} // 存储每个赛事的编排状态
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.loadCompetitionList()
|
||||
},
|
||||
activated() {
|
||||
// 当页面被激活时(从其他页面返回),重新加载编排状态
|
||||
if (this.tableData.length > 0) {
|
||||
this.loadScheduleStatus()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 加载赛事列表
|
||||
loadCompetitionList() {
|
||||
this.loading = true
|
||||
const params = {}
|
||||
|
||||
if (this.searchForm.keyword) {
|
||||
params.competitionName = this.searchForm.keyword
|
||||
}
|
||||
if (this.searchForm.status !== null && this.searchForm.status !== '') {
|
||||
params.status = this.searchForm.status
|
||||
}
|
||||
|
||||
getCompetitionList(this.pagination.current, this.pagination.size, params)
|
||||
.then(res => {
|
||||
console.log('赛事列表返回数据:', res)
|
||||
const responseData = res.data?.data
|
||||
if (responseData && responseData.records) {
|
||||
// 处理赛事数据,兼容驼峰和下划线命名
|
||||
this.tableData = responseData.records.map(competition => ({
|
||||
id: competition.id,
|
||||
competitionName: competition.competitionName || competition.competition_name,
|
||||
competitionCode: competition.competitionCode || competition.competition_code,
|
||||
organizer: competition.organizer,
|
||||
location: competition.location,
|
||||
venue: competition.venue,
|
||||
registrationStartTime: competition.registrationStartTime || competition.registration_start_time,
|
||||
registrationEndTime: competition.registrationEndTime || competition.registration_end_time,
|
||||
competitionStartTime: competition.competitionStartTime || competition.competition_start_time,
|
||||
competitionEndTime: competition.competitionEndTime || competition.competition_end_time,
|
||||
status: competition.status,
|
||||
createTime: competition.createTime || competition.create_time
|
||||
}))
|
||||
this.pagination.total = responseData.total || 0
|
||||
|
||||
// 加载每个赛事的编排状态
|
||||
this.loadScheduleStatus()
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('加载赛事列表失败', err)
|
||||
this.$message.error('加载赛事列表失败')
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
|
||||
// 加载编排状态
|
||||
async loadScheduleStatus() {
|
||||
for (const competition of this.tableData) {
|
||||
try {
|
||||
const res = await getScheduleResult(competition.id)
|
||||
if (res.data?.data) {
|
||||
this.$set(this.scheduleStatusMap, competition.id, res.data.data.isCompleted || false)
|
||||
} else {
|
||||
this.$set(this.scheduleStatusMap, competition.id, false)
|
||||
}
|
||||
} catch (err) {
|
||||
// 如果获取失败,默认为未完成
|
||||
this.$set(this.scheduleStatusMap, competition.id, false)
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// 检查编排是否完成
|
||||
isScheduleCompleted(competitionId) {
|
||||
return this.scheduleStatusMap[competitionId] === true
|
||||
},
|
||||
|
||||
handleSearch() {
|
||||
this.pagination.current = 1
|
||||
this.loadCompetitionList()
|
||||
},
|
||||
|
||||
handleSizeChange(size) {
|
||||
this.pagination.size = size
|
||||
this.pagination.current = 1
|
||||
this.loadCompetitionList()
|
||||
},
|
||||
|
||||
handleCurrentChange(current) {
|
||||
this.pagination.current = current
|
||||
this.loadCompetitionList()
|
||||
},
|
||||
|
||||
// 查看报名详情 - 传递赛事ID
|
||||
handleRegistrationDetail(row) {
|
||||
this.$router.push({
|
||||
path: '/martial/registration/detail',
|
||||
query: { competitionId: row.id }
|
||||
})
|
||||
},
|
||||
|
||||
// 编排 - 传递赛事ID
|
||||
handleSchedule(row) {
|
||||
this.$router.push({
|
||||
path: '/martial/schedule/list',
|
||||
query: { competitionId: row.id }
|
||||
})
|
||||
},
|
||||
|
||||
// 调度 - 传递赛事ID
|
||||
handleDispatch(row) {
|
||||
// 检查编排是否完成
|
||||
if (!this.isScheduleCompleted(row.id)) {
|
||||
this.$message.warning('请先完成编排后再进行调度')
|
||||
return
|
||||
}
|
||||
|
||||
this.$router.push({
|
||||
path: '/martial/dispatch/list',
|
||||
query: { competitionId: row.id }
|
||||
})
|
||||
},
|
||||
|
||||
// 格式化日期范围
|
||||
formatDateRange(startTime, endTime) {
|
||||
if (!startTime || !endTime) return '-'
|
||||
// 简单格式化,只显示日期部分
|
||||
const start = startTime.split(' ')[0]
|
||||
const end = endTime.split(' ')[0]
|
||||
return `${start} ~ ${end}`
|
||||
},
|
||||
|
||||
getStatusType(status) {
|
||||
const statusMap = {
|
||||
1: 'info', // 未开始
|
||||
2: 'success', // 报名中
|
||||
3: 'warning', // 进行中
|
||||
4: 'info' // 已结束
|
||||
}
|
||||
return statusMap[status] || 'info'
|
||||
},
|
||||
|
||||
getStatusText(status) {
|
||||
const statusMap = {
|
||||
1: '未开始',
|
||||
2: '报名中',
|
||||
3: '进行中',
|
||||
4: '已结束'
|
||||
}
|
||||
return statusMap[status] || '未知'
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.martial-order-container {
|
||||
min-height: 100%;
|
||||
padding: 15px;
|
||||
background: #fff;
|
||||
|
||||
.page-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 15px;
|
||||
|
||||
.page-title {
|
||||
margin: 0;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: #2c3e50;
|
||||
}
|
||||
}
|
||||
|
||||
.tip-message {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
padding: 10px 14px;
|
||||
margin-bottom: 15px;
|
||||
background: linear-gradient(90deg, #ffd54f 0%, #ffecb3 100%);
|
||||
border: 1px solid #ffc107;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 2px 4px rgba(255, 193, 7, 0.2);
|
||||
|
||||
.tip-header {
|
||||
flex-shrink: 0;
|
||||
|
||||
.tip-number {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
background: #ff9800;
|
||||
color: #fff;
|
||||
font-size: 11px;
|
||||
font-weight: bold;
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
.tip-content {
|
||||
flex: 1;
|
||||
color: #5d4037;
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
|
||||
.tip-subtitle {
|
||||
margin-top: 3px;
|
||||
color: #6d4c41;
|
||||
font-size: 11px;
|
||||
font-style: italic;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.search-form {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.amount-text {
|
||||
color: #e6a23c;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.pagination {
|
||||
margin-top: 15px;
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -94,11 +94,14 @@
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column
|
||||
prop="age"
|
||||
label="年龄"
|
||||
width="80"
|
||||
align="center"
|
||||
/>
|
||||
>
|
||||
<template #default="scope">
|
||||
{{ scope.row.age === -1 || scope.row.age === null || scope.row.age === undefined ? '--' : scope.row.age }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column
|
||||
prop="organization"
|
||||
|
||||
990
src/views/martial/participant/index.vue.bak
Normal file
990
src/views/martial/participant/index.vue.bak
Normal file
@@ -0,0 +1,990 @@
|
||||
<template>
|
||||
<div class="participant-container">
|
||||
<!-- 列表视图 -->
|
||||
<div v-if="currentView === 'list'" class="list-view">
|
||||
<el-card shadow="hover">
|
||||
<div class="list-header">
|
||||
<h2 class="page-title">参赛选手管理</h2>
|
||||
<el-button type="primary" icon="el-icon-plus" @click="handleCreate">
|
||||
添加选手
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<el-form :inline="true" :model="searchForm" class="search-form">
|
||||
<el-form-item>
|
||||
<el-input
|
||||
v-model="searchForm.keyword"
|
||||
placeholder="搜索选手姓名"
|
||||
clearable
|
||||
size="small"
|
||||
style="width: 240px"
|
||||
>
|
||||
<template #prefix>
|
||||
<i class="el-input__icon el-icon-search"></i>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-select v-model="searchForm.competitionId" placeholder="选择赛事" clearable size="small" style="width: 200px">
|
||||
<el-option label="全部赛事" :value="null" />
|
||||
<el-option
|
||||
v-for="item in allCompetitionOptions"
|
||||
:key="item.id"
|
||||
:label="item.competitionName"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" size="small" @click="handleSearch">查询</el-button>
|
||||
<el-button size="small" @click="handleReset">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
:data="displayList"
|
||||
border
|
||||
stripe
|
||||
style="width: 100%"
|
||||
class="data-table"
|
||||
>
|
||||
<el-table-column
|
||||
type="index"
|
||||
label="序号"
|
||||
width="60"
|
||||
align="center"
|
||||
/>
|
||||
|
||||
<el-table-column
|
||||
prop="playerName"
|
||||
label="选手姓名"
|
||||
width="120"
|
||||
/>
|
||||
|
||||
<el-table-column
|
||||
prop="competitionName"
|
||||
label="所属赛事"
|
||||
min-width="180"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
|
||||
<el-table-column
|
||||
prop="projectName"
|
||||
label="参赛项目"
|
||||
width="120"
|
||||
/>
|
||||
|
||||
<el-table-column
|
||||
prop="category"
|
||||
label="组别"
|
||||
width="100"
|
||||
/>
|
||||
|
||||
<el-table-column
|
||||
label="性别"
|
||||
width="80"
|
||||
align="center"
|
||||
>
|
||||
<template #default="scope">
|
||||
<el-tag :type="scope.row.gender === 1 ? 'primary' : 'danger'" size="small">
|
||||
{{ scope.row.gender === 1 ? '男' : '女' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column
|
||||
prop="age"
|
||||
label="年龄"
|
||||
width="80"
|
||||
align="center"
|
||||
/>
|
||||
|
||||
<el-table-column
|
||||
prop="organization"
|
||||
label="所属单位"
|
||||
min-width="150"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
|
||||
<el-table-column
|
||||
prop="contactPhone"
|
||||
label="联系电话"
|
||||
width="120"
|
||||
/>
|
||||
|
||||
<el-table-column
|
||||
prop="orderNum"
|
||||
label="出场顺序"
|
||||
width="100"
|
||||
align="center"
|
||||
/>
|
||||
|
||||
<el-table-column
|
||||
label="操作"
|
||||
width="220"
|
||||
fixed="right"
|
||||
align="center"
|
||||
>
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
type="primary"
|
||||
link
|
||||
size="small"
|
||||
icon="el-icon-view"
|
||||
@click="handleView(scope.row)"
|
||||
>
|
||||
查看
|
||||
</el-button>
|
||||
<el-button
|
||||
type="warning"
|
||||
link
|
||||
size="small"
|
||||
icon="el-icon-edit"
|
||||
@click="handleEdit(scope.row)"
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
type="danger"
|
||||
link
|
||||
size="small"
|
||||
icon="el-icon-delete"
|
||||
@click="handleDelete(scope.row)"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- 分页 -->
|
||||
<el-pagination
|
||||
v-if="pagination.total > 0"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handlePageChange"
|
||||
:current-page="pagination.current"
|
||||
:page-sizes="[10, 20, 50, 100]"
|
||||
:page-size="pagination.size"
|
||||
:total="pagination.total"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
style="margin-top: 20px; text-align: right"
|
||||
/>
|
||||
</el-card>
|
||||
</div>
|
||||
|
||||
<!-- 表单视图 -->
|
||||
<div v-else class="form-view">
|
||||
<el-card shadow="hover" v-loading="loading">
|
||||
<div class="page-header">
|
||||
<el-button
|
||||
icon="el-icon-arrow-left"
|
||||
@click="backToList"
|
||||
>
|
||||
返回列表
|
||||
</el-button>
|
||||
<h2 class="page-title">{{ pageTitle }}</h2>
|
||||
<div class="header-actions" v-if="currentView !== 'view'">
|
||||
<el-button @click="backToList">取消</el-button>
|
||||
<el-button type="primary" @click="handleSave">
|
||||
{{ currentView === 'create' ? '创建' : '保存' }}
|
||||
</el-button>
|
||||
</div>
|
||||
<div class="header-actions" v-else>
|
||||
<el-button type="primary" @click="switchToEdit">编辑</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="formRules"
|
||||
label-width="120px"
|
||||
:disabled="currentView === 'view'"
|
||||
class="participant-form"
|
||||
>
|
||||
<!-- 基本信息 -->
|
||||
<div class="form-section">
|
||||
<div class="section-title">
|
||||
<i class="el-icon-user"></i>
|
||||
基本信息
|
||||
</div>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="选手姓名" prop="playerName">
|
||||
<el-input
|
||||
v-model="formData.playerName"
|
||||
placeholder="请输入选手姓名"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="性别" prop="gender">
|
||||
<el-radio-group v-model="formData.gender">
|
||||
<el-radio :label="1">男</el-radio>
|
||||
<el-radio :label="2">女</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="年龄" prop="age">
|
||||
<el-input-number
|
||||
v-model="formData.age"
|
||||
:min="6"
|
||||
:max="100"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="身份证号" prop="idCard">
|
||||
<el-input
|
||||
v-model="formData.idCard"
|
||||
placeholder="请输入身份证号"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="联系电话" prop="contactPhone">
|
||||
<el-input
|
||||
v-model="formData.contactPhone"
|
||||
placeholder="请输入联系电话"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-form-item label="所属单位" prop="organization">
|
||||
<el-input
|
||||
v-model="formData.organization"
|
||||
placeholder="请输入所属单位"
|
||||
/>
|
||||
</el-form-item>
|
||||
</div>
|
||||
|
||||
<!-- 赛事信息 -->
|
||||
<div class="form-section">
|
||||
<div class="section-title">
|
||||
<i class="el-icon-trophy"></i>
|
||||
赛事信息
|
||||
</div>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="所属赛事" prop="competitionId">
|
||||
<el-select
|
||||
v-model="formData.competitionId"
|
||||
placeholder="请选择赛事"
|
||||
style="width: 100%"
|
||||
@change="handleCompetitionChange"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in availableCompetitionOptions"
|
||||
:key="item.id"
|
||||
:label="item.competitionName"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="参赛项目" prop="projectId">
|
||||
<el-select
|
||||
v-model="formData.projectId"
|
||||
placeholder="请选择参赛项目"
|
||||
style="width: 100%"
|
||||
@change="handleProjectChange"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in projectOptions"
|
||||
:key="item.id"
|
||||
:label="item.projectName"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="组别" prop="category">
|
||||
<el-input
|
||||
v-model="formData.category"
|
||||
placeholder="例如:成年男子组"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="出场顺序" prop="orderNum">
|
||||
<el-input-number
|
||||
v-model="formData.orderNum"
|
||||
:min="1"
|
||||
:max="9999"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
|
||||
<!-- 其他信息 -->
|
||||
<div class="form-section">
|
||||
<div class="section-title">
|
||||
<i class="el-icon-document"></i>
|
||||
其他信息
|
||||
</div>
|
||||
|
||||
<el-form-item label="选手简介" prop="introduction">
|
||||
<el-input
|
||||
v-model="formData.introduction"
|
||||
type="textarea"
|
||||
:rows="4"
|
||||
placeholder="请输入选手简介"
|
||||
maxlength="500"
|
||||
show-word-limit
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input
|
||||
v-model="formData.remark"
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
placeholder="请输入备注"
|
||||
/>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getCompetitionList } from '@/api/martial/competition'
|
||||
import { getInfoPublishList } from '@/api/martial/infoPublish'
|
||||
import { getProjectsByCompetition } from '@/api/martial/project'
|
||||
import {
|
||||
getParticipantList,
|
||||
getParticipantDetail,
|
||||
addParticipant,
|
||||
updateParticipant,
|
||||
removeParticipant
|
||||
} from '@/api/martial/participant'
|
||||
|
||||
export default {
|
||||
name: 'ParticipantManagement',
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
currentView: 'list', // list, create, edit, view
|
||||
participantId: null,
|
||||
searchForm: {
|
||||
keyword: '',
|
||||
competitionId: null
|
||||
},
|
||||
pagination: {
|
||||
current: 1,
|
||||
size: 10,
|
||||
total: 0
|
||||
},
|
||||
competitionOptions: [], // 已发布的可报名赛事列表(用于新建)
|
||||
allCompetitionOptions: [], // 所有赛事列表(用于搜索和编辑)
|
||||
projectOptions: [], // 项目列表
|
||||
participantList: [],
|
||||
formData: {
|
||||
orderId: null,
|
||||
competitionId: null,
|
||||
competitionName: '',
|
||||
playerName: '',
|
||||
gender: 1,
|
||||
age: null,
|
||||
contactPhone: '',
|
||||
organization: '',
|
||||
idCard: '',
|
||||
projectId: null,
|
||||
category: '',
|
||||
orderNum: 1,
|
||||
introduction: '',
|
||||
remark: '',
|
||||
attachments: []
|
||||
},
|
||||
formRules: {
|
||||
playerName: [
|
||||
{ required: true, message: '请输入选手姓名', trigger: 'blur' }
|
||||
],
|
||||
gender: [
|
||||
{ required: true, message: '请选择性别', trigger: 'change' }
|
||||
],
|
||||
age: [
|
||||
{ required: true, message: '请输入年龄', trigger: 'blur' }
|
||||
],
|
||||
contactPhone: [
|
||||
{ required: true, message: '请输入联系电话', trigger: 'blur' },
|
||||
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号码', trigger: 'blur' }
|
||||
],
|
||||
competitionId: [
|
||||
{ required: true, message: '请选择赛事', trigger: 'change' }
|
||||
],
|
||||
projectId: [
|
||||
{ required: true, message: '请选择参赛项目', trigger: 'change' }
|
||||
]
|
||||
}
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
pageTitle() {
|
||||
const titleMap = {
|
||||
create: '添加参赛选手',
|
||||
edit: '编辑参赛选手',
|
||||
view: '查看参赛选手'
|
||||
};
|
||||
return titleMap[this.currentView] || '参赛选手信息';
|
||||
},
|
||||
displayList() {
|
||||
return this.participantList;
|
||||
},
|
||||
// 根据不同模式返回不同的赛事选项
|
||||
availableCompetitionOptions() {
|
||||
// 编辑和查看模式:显示所有赛事(因为可能编辑已过报名期的选手)
|
||||
if (this.currentView === 'edit' || this.currentView === 'view') {
|
||||
return this.allCompetitionOptions;
|
||||
}
|
||||
// 新建模式:只显示可报名的赛事
|
||||
return this.competitionOptions;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'$route.query': {
|
||||
handler(query) {
|
||||
this.initPage();
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.loadAvailableCompetitions();
|
||||
this.loadAllCompetitions();
|
||||
},
|
||||
methods: {
|
||||
initPage() {
|
||||
const { mode, id } = this.$route.query;
|
||||
this.currentView = mode || 'list';
|
||||
// 不使用 parseInt,保持 ID 为字符串避免精度丢失
|
||||
this.participantId = id || null;
|
||||
|
||||
if (this.currentView === 'list') {
|
||||
this.loadParticipantList();
|
||||
} else if (this.currentView !== 'list' && this.participantId) {
|
||||
this.loadParticipantData();
|
||||
} else if (this.currentView === 'create') {
|
||||
this.resetFormData();
|
||||
}
|
||||
},
|
||||
|
||||
// 加载可报名的赛事(从已发布的信息中获取)
|
||||
loadAvailableCompetitions() {
|
||||
getInfoPublishList(1, 100, { isPublished: 1 })
|
||||
.then(res => {
|
||||
console.log('已发布信息列表返回数据:', res);
|
||||
const responseData = res.data?.data;
|
||||
if (responseData && responseData.records) {
|
||||
const publishedCompetitionIds = new Set(
|
||||
responseData.records
|
||||
.filter(item => item.competitionId)
|
||||
.map(item => item.competitionId)
|
||||
);
|
||||
|
||||
console.log('已发布的赛事ID列表:', Array.from(publishedCompetitionIds));
|
||||
|
||||
if (publishedCompetitionIds.size > 0) {
|
||||
this.loadPublishedCompetitions(Array.from(publishedCompetitionIds));
|
||||
} else {
|
||||
// 如果没有发布信息,直接加载所有赛事作为可报名赛事
|
||||
console.log('没有已发布信息,加载所有赛事');
|
||||
this.loadPublishedCompetitions([]);
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('加载已发布信息列表失败', err);
|
||||
// 出错时也加载所有赛事
|
||||
this.loadPublishedCompetitions([]);
|
||||
});
|
||||
},
|
||||
|
||||
// 加载已发布的赛事详细信息,并过滤出可报名的赛事
|
||||
loadPublishedCompetitions(competitionIds) {
|
||||
getCompetitionList(1, 100, {})
|
||||
.then(res => {
|
||||
console.log('赛事列表返回数据:', res);
|
||||
const responseData = res.data?.data;
|
||||
if (responseData && responseData.records) {
|
||||
const now = new Date();
|
||||
|
||||
this.competitionOptions = responseData.records
|
||||
.filter(item => {
|
||||
// 如果没有发布信息(competitionIds为空数组),则显示所有在报名期内的赛事
|
||||
if (competitionIds.length > 0 && !competitionIds.includes(item.id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查报名时间
|
||||
if (!item.registrationStartTime || !item.registrationEndTime) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const regStart = new Date(item.registrationStartTime);
|
||||
const regEnd = new Date(item.registrationEndTime);
|
||||
return now >= regStart && now <= regEnd;
|
||||
})
|
||||
.map(item => ({
|
||||
id: item.id,
|
||||
competitionName: item.competitionName,
|
||||
registrationStartTime: item.registrationStartTime,
|
||||
registrationEndTime: item.registrationEndTime
|
||||
}));
|
||||
|
||||
console.log('可报名的赛事列表:', this.competitionOptions);
|
||||
|
||||
if (this.competitionOptions.length === 0) {
|
||||
console.log('当前没有可以报名的赛事(报名时间范围外)');
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('加载赛事列表失败', err);
|
||||
this.$message.error('加载赛事列表失败');
|
||||
});
|
||||
},
|
||||
|
||||
// 加载所有赛事(用于搜索过滤)
|
||||
loadAllCompetitions() {
|
||||
getCompetitionList(1, 100, {})
|
||||
.then(res => {
|
||||
const responseData = res.data?.data;
|
||||
if (responseData && responseData.records) {
|
||||
this.allCompetitionOptions = responseData.records.map(item => ({
|
||||
id: item.id,
|
||||
competitionName: item.competitionName
|
||||
}));
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('加载所有赛事失败', err);
|
||||
});
|
||||
},
|
||||
|
||||
loadParticipantList() {
|
||||
this.loading = true;
|
||||
const params = {};
|
||||
|
||||
if (this.searchForm.keyword) {
|
||||
params.playerName = this.searchForm.keyword;
|
||||
}
|
||||
|
||||
if (this.searchForm.competitionId) {
|
||||
params.competitionId = this.searchForm.competitionId;
|
||||
}
|
||||
|
||||
getParticipantList(null, this.pagination.current, this.pagination.size, params)
|
||||
.then(res => {
|
||||
console.log('参赛人员列表返回数据:', res);
|
||||
const responseData = res.data?.data;
|
||||
if (responseData && responseData.records) {
|
||||
this.participantList = responseData.records;
|
||||
this.pagination.total = responseData.total || 0;
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('加载参赛人员列表失败', err);
|
||||
this.$message.error('加载参赛人员列表失败');
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
|
||||
loadParticipantData() {
|
||||
if (!this.participantId) return;
|
||||
|
||||
this.loading = true;
|
||||
getParticipantDetail(this.participantId)
|
||||
.then(res => {
|
||||
const detailData = res.data?.data;
|
||||
if (detailData) {
|
||||
this.formData = { ...detailData };
|
||||
|
||||
// 将 attachments 字符串转换为数组(前端需要数组格式)
|
||||
if (typeof this.formData.attachments === 'string') {
|
||||
try {
|
||||
this.formData.attachments = JSON.parse(this.formData.attachments);
|
||||
} catch (e) {
|
||||
console.warn('解析 attachments 失败,使用空数组', e);
|
||||
this.formData.attachments = [];
|
||||
}
|
||||
} else if (!this.formData.attachments) {
|
||||
this.formData.attachments = [];
|
||||
}
|
||||
|
||||
// 加载该赛事的项目列表
|
||||
if (detailData.competitionId) {
|
||||
this.loadProjectsByCompetition(detailData.competitionId);
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('加载参赛人员详情失败', err);
|
||||
this.$message.error('加载参赛人员详情失败');
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
|
||||
handlePageChange(current) {
|
||||
this.pagination.current = current;
|
||||
this.loadParticipantList();
|
||||
},
|
||||
|
||||
handleSizeChange(size) {
|
||||
this.pagination.size = size;
|
||||
this.pagination.current = 1;
|
||||
this.loadParticipantList();
|
||||
},
|
||||
|
||||
handleSearch() {
|
||||
this.pagination.current = 1;
|
||||
this.loadParticipantList();
|
||||
},
|
||||
|
||||
handleReset() {
|
||||
this.searchForm = {
|
||||
keyword: '',
|
||||
competitionId: null
|
||||
};
|
||||
this.pagination.current = 1;
|
||||
this.loadParticipantList();
|
||||
},
|
||||
|
||||
handleCreate() {
|
||||
this.$router.push({
|
||||
path: '/martial/participant/list',
|
||||
query: { mode: 'create' }
|
||||
});
|
||||
},
|
||||
|
||||
handleView(row) {
|
||||
this.$router.push({
|
||||
path: '/martial/participant/list',
|
||||
query: { mode: 'view', id: row.id }
|
||||
});
|
||||
},
|
||||
|
||||
handleEdit(row) {
|
||||
this.$router.push({
|
||||
path: '/martial/participant/list',
|
||||
query: { mode: 'edit', id: row.id }
|
||||
});
|
||||
},
|
||||
|
||||
switchToEdit() {
|
||||
this.$router.push({
|
||||
path: '/martial/participant/list',
|
||||
query: { mode: 'edit', id: this.participantId }
|
||||
});
|
||||
},
|
||||
|
||||
handleDelete(row) {
|
||||
this.$confirm('确定要删除该选手吗?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
this.loading = true;
|
||||
removeParticipant(row.id.toString())
|
||||
.then(res => {
|
||||
this.$message.success('删除成功');
|
||||
this.loadParticipantList();
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('删除失败', err);
|
||||
this.$message.error('删除失败');
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
}).catch(() => {});
|
||||
},
|
||||
|
||||
handleCompetitionChange(competitionId) {
|
||||
// 从可用的选项列表中查找赛事
|
||||
const competition = this.availableCompetitionOptions.find(item => item.id === competitionId);
|
||||
if (competition) {
|
||||
this.formData.competitionName = competition.competitionName;
|
||||
}
|
||||
// 加载该赛事的项目列表
|
||||
this.loadProjectsByCompetition(competitionId);
|
||||
// 清空已选项目
|
||||
this.formData.projectId = null;
|
||||
},
|
||||
|
||||
handleProjectChange(projectId) {
|
||||
const project = this.projectOptions.find(item => item.id === projectId);
|
||||
if (project) {
|
||||
// 自动填充组别信息
|
||||
if (project.category && !this.formData.category) {
|
||||
this.formData.category = project.category;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
loadProjectsByCompetition(competitionId) {
|
||||
if (!competitionId) {
|
||||
this.projectOptions = [];
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('加载赛事项目,赛事ID:', competitionId);
|
||||
|
||||
getProjectsByCompetition(competitionId)
|
||||
.then(res => {
|
||||
console.log('项目列表返回数据:', res);
|
||||
const responseData = res.data?.data;
|
||||
|
||||
// 兼容两种数据格式:分页数据(有records)和直接数组
|
||||
let projectList = [];
|
||||
if (responseData) {
|
||||
if (Array.isArray(responseData)) {
|
||||
// 直接是数组
|
||||
projectList = responseData;
|
||||
console.log('返回的是直接数组,长度:', projectList.length);
|
||||
} else if (responseData.records && Array.isArray(responseData.records)) {
|
||||
// 分页数据
|
||||
projectList = responseData.records;
|
||||
console.log('返回的是分页数据,记录数:', projectList.length);
|
||||
} else {
|
||||
console.warn('未知的数据格式:', responseData);
|
||||
}
|
||||
}
|
||||
|
||||
if (projectList.length > 0) {
|
||||
this.projectOptions = projectList.map(item => ({
|
||||
id: item.id,
|
||||
projectName: item.projectName,
|
||||
projectCode: item.projectCode,
|
||||
category: item.category
|
||||
}));
|
||||
console.log('可选项目列表:', this.projectOptions);
|
||||
} else {
|
||||
this.projectOptions = [];
|
||||
console.log('该赛事没有项目数据');
|
||||
this.$message.warning('该赛事还没有配置项目,请先添加项目');
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('加载项目列表失败', err);
|
||||
this.$message.error('加载项目列表失败: ' + (err.message || '未知错误'));
|
||||
this.projectOptions = [];
|
||||
});
|
||||
},
|
||||
|
||||
handleSave() {
|
||||
this.$refs.formRef.validate((valid) => {
|
||||
if (valid) {
|
||||
this.loading = true;
|
||||
|
||||
// 确保有赛事名称
|
||||
if (!this.formData.competitionName) {
|
||||
const competition = this.availableCompetitionOptions.find(item => item.id === this.formData.competitionId);
|
||||
if (competition) {
|
||||
this.formData.competitionName = competition.competitionName;
|
||||
}
|
||||
}
|
||||
|
||||
const submitData = { ...this.formData };
|
||||
|
||||
console.log('=== 提交前的 formData ===', this.formData);
|
||||
console.log('formData.orderId:', this.formData.orderId);
|
||||
|
||||
// 将 attachments 数组转换为 JSON 字符串(后端需要 String 类型)
|
||||
if (Array.isArray(submitData.attachments)) {
|
||||
submitData.attachments = JSON.stringify(submitData.attachments);
|
||||
}
|
||||
|
||||
// 临时方案: 如果没有 orderId,使用 competitionId 作为 orderId
|
||||
// 警告: 这是临时解决方案,后续应修改数据库表结构或后端逻辑
|
||||
if (!submitData.orderId && submitData.competitionId) {
|
||||
submitData.orderId = submitData.competitionId;
|
||||
console.warn('⚠️ 临时方案: 使用 competitionId 作为 orderId', submitData.competitionId);
|
||||
}
|
||||
|
||||
console.log('=== 提交的数据 submitData ===', submitData);
|
||||
console.log('submitData.orderId:', submitData.orderId);
|
||||
|
||||
if (this.currentView === 'create') {
|
||||
// 新建
|
||||
addParticipant(submitData)
|
||||
.then(res => {
|
||||
this.$message.success('添加成功');
|
||||
this.backToList();
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('添加失败', err);
|
||||
this.$message.error('添加失败');
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
} else if (this.currentView === 'edit') {
|
||||
// 编辑
|
||||
submitData.id = this.participantId;
|
||||
updateParticipant(submitData)
|
||||
.then(res => {
|
||||
this.$message.success('保存成功');
|
||||
this.backToList();
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('保存失败', err);
|
||||
this.$message.error('保存失败');
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
}
|
||||
} else {
|
||||
this.$message.error('请完善必填信息');
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
backToList() {
|
||||
this.$router.push({
|
||||
path: '/martial/participant/list'
|
||||
});
|
||||
},
|
||||
|
||||
resetFormData() {
|
||||
this.formData = {
|
||||
orderId: null,
|
||||
competitionId: null,
|
||||
competitionName: '',
|
||||
playerName: '',
|
||||
gender: 1,
|
||||
age: null,
|
||||
contactPhone: '',
|
||||
organization: '',
|
||||
idCard: '',
|
||||
projectId: null,
|
||||
category: '',
|
||||
orderNum: 1,
|
||||
introduction: '',
|
||||
remark: '',
|
||||
attachments: []
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.participant-container {
|
||||
padding: 20px;
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
min-height: calc(100vh - 120px);
|
||||
}
|
||||
|
||||
.list-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
padding-bottom: 20px;
|
||||
border-bottom: 2px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
margin: 0;
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.search-form {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.data-table {
|
||||
:deep(.el-table__header) {
|
||||
th {
|
||||
background-color: #fafafa;
|
||||
color: #333;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.el-button--text) {
|
||||
padding: 0 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.page-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
margin-bottom: 30px;
|
||||
padding-bottom: 20px;
|
||||
border-bottom: 2px solid #f0f0f0;
|
||||
|
||||
.page-title {
|
||||
flex: 1;
|
||||
margin: 0;
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.header-actions {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.participant-form {
|
||||
max-width: 1200px;
|
||||
}
|
||||
|
||||
.form-section {
|
||||
margin-bottom: 30px;
|
||||
padding: 20px;
|
||||
background: #fafafa;
|
||||
border-radius: 8px;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.section-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 20px;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
|
||||
i {
|
||||
color: #dc2626;
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.el-form-item) {
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
</style>
|
||||
@@ -71,71 +71,176 @@
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<div v-for="(group, index) in filteredCompetitionGroups" :key="group.id" class="competition-group">
|
||||
<div class="group-header">
|
||||
<div class="group-info">
|
||||
<span class="group-title">{{ group.title }}</span>
|
||||
<span class="group-meta">{{ group.type }}</span>
|
||||
<span class="group-meta">{{ group.count }}</span>
|
||||
<span class="group-meta">{{ group.code }}</span>
|
||||
<!-- 项目卡片列表 -->
|
||||
<div v-for="(group, groupIndex) in filteredCompetitionGroups" :key="group.id" class="project-card">
|
||||
<!-- 项目头部 - 可点击展开 -->
|
||||
<div class="project-header" :class="{ 'project-header-collapsed': !isProjectExpanded(group.id) }" @click="toggleProjectExpand(group.id)">
|
||||
<div class="project-info">
|
||||
<!-- 展开图标 -->
|
||||
<span class="project-expand-icon">
|
||||
<el-icon v-if="isProjectExpanded(group.id)"><ArrowDown /></el-icon>
|
||||
<el-icon v-else><ArrowRight /></el-icon>
|
||||
</span>
|
||||
<span class="project-index">{{ groupIndex + 1 }}、</span>
|
||||
<span class="project-title">{{ group.title }}</span>
|
||||
<span class="project-meta">{{ group.type }}</span>
|
||||
<span class="project-meta">{{ getTeamCount(group) }}队</span>
|
||||
<span class="project-meta">{{ group.items?.length || 0 }}组</span>
|
||||
<span class="project-meta">{{ group.code }}</span>
|
||||
</div>
|
||||
<div class="group-actions">
|
||||
<el-button size="small" type="warning" @click="handleMoveGroup(group)">
|
||||
移动
|
||||
</el-button>
|
||||
<div class="project-actions" @click.stop>
|
||||
<el-popover
|
||||
placement="bottom"
|
||||
:width="200"
|
||||
trigger="click"
|
||||
:disabled="isScheduleCompleted"
|
||||
>
|
||||
<template #reference>
|
||||
<el-button size="small" type="warning" :disabled="isScheduleCompleted">
|
||||
移动
|
||||
</el-button>
|
||||
</template>
|
||||
<div class="move-popover">
|
||||
<div class="move-label">移动到</div>
|
||||
<el-select v-model="moveTargetVenueId" placeholder="选择场地" size="small" style="width: 100%; margin-bottom: 10px;">
|
||||
<el-option
|
||||
v-for="venue in venues"
|
||||
:key="venue.id"
|
||||
:label="venue.venueName"
|
||||
:value="venue.id"
|
||||
></el-option>
|
||||
</el-select>
|
||||
<el-button size="small" type="primary" @click="quickMoveGroup(group)">移动</el-button>
|
||||
</div>
|
||||
</el-popover>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-table :data="group.items" border stripe size="small">
|
||||
<el-table-column label="序号" type="index" width="60" align="center"></el-table-column>
|
||||
<el-table-column prop="schoolUnit" label="学校/单位" min-width="200"></el-table-column>
|
||||
<el-table-column prop="status" label="状态" width="100" align="center">
|
||||
<template #default="scope">
|
||||
<el-tag :type="scope.row.status === '已签到' ? 'success' : scope.row.status === '异常' ? 'danger' : 'info'" size="small">
|
||||
{{ scope.row.status || '未签到' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="150" align="center">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
link
|
||||
size="small"
|
||||
@click="handleMoveUp(group, scope.$index)"
|
||||
:disabled="scope.$index === 0 || isScheduleCompleted"
|
||||
title="上移"
|
||||
class="move-btn"
|
||||
<!-- 队伍列表 - 可折叠 -->
|
||||
<div v-if="isProjectExpanded(group.id)" class="team-list">
|
||||
<div
|
||||
v-for="(team, teamIndex) in groupItemsByTeam(group.items)"
|
||||
:key="team.id"
|
||||
class="team-row"
|
||||
:class="{ 'team-row-expanded': isTeamExpanded(group.id, team.id) }"
|
||||
>
|
||||
<!-- 队伍主行 - 可点击展开 -->
|
||||
<div class="team-main" @click="toggleTeamExpand(group.id, team.id)">
|
||||
<!-- 展开图标 -->
|
||||
<span class="expand-icon">
|
||||
<el-icon v-if="isTeamExpanded(group.id, team.id)"><ArrowDown /></el-icon>
|
||||
<el-icon v-else><ArrowRight /></el-icon>
|
||||
</span>
|
||||
|
||||
<!-- 队伍信息 -->
|
||||
<div class="team-info">
|
||||
<span class="team-index">{{ teamIndex + 1 }}、</span>
|
||||
<span class="team-name">{{ team.schoolUnit }}</span>
|
||||
</div>
|
||||
|
||||
<!-- 选手列表 -->
|
||||
<div class="player-list">
|
||||
<span
|
||||
v-for="(player, playerIndex) in team.players"
|
||||
:key="player.id"
|
||||
class="player-tag"
|
||||
:class="{ 'player-exception': player.status === '异常' }"
|
||||
>
|
||||
{{ player.playerName }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<div class="team-actions" @click.stop>
|
||||
<el-button
|
||||
link
|
||||
size="small"
|
||||
@click="handleTeamMoveUp(group, teamIndex)"
|
||||
:disabled="teamIndex === 0 || isScheduleCompleted"
|
||||
title="上移"
|
||||
class="move-btn"
|
||||
>
|
||||
<img src="/img/图标 3@3x.png" class="move-icon" alt="上移" />
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
size="small"
|
||||
@click="handleTeamMoveDown(group, teamIndex)"
|
||||
:disabled="teamIndex === groupItemsByTeam(group.items).length - 1 || isScheduleCompleted"
|
||||
title="下移"
|
||||
class="move-btn"
|
||||
>
|
||||
<img src="/img/图标 4@3x.png" class="move-icon" alt="下移" />
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 展开内容 - 选手详情 -->
|
||||
<div v-if="isTeamExpanded(group.id, team.id)" class="team-expand-content">
|
||||
<div
|
||||
v-for="(player, playerIndex) in team.players"
|
||||
:key="player.id"
|
||||
class="player-detail-row"
|
||||
>
|
||||
<img src="/img/图标 3@3x.png" class="move-icon" alt="上移" />
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
size="small"
|
||||
@click="handleMoveDown(group, scope.$index)"
|
||||
:disabled="scope.$index === group.items.length - 1 || isScheduleCompleted"
|
||||
title="下移"
|
||||
class="move-btn"
|
||||
>
|
||||
<img src="/img/图标 4@3x.png" class="move-icon" alt="下移" />
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="(scope.row.status || '未签到') === '未签到'"
|
||||
link
|
||||
size="small"
|
||||
@click="markAsException(group, scope.$index)"
|
||||
:disabled="isScheduleCompleted"
|
||||
style="color: #f56c6c;"
|
||||
>
|
||||
异常
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<span class="player-detail-index">{{ playerIndex + 1 }}</span>
|
||||
<span class="player-detail-name">{{ player.playerName }}</span>
|
||||
<span class="player-detail-status">
|
||||
<el-tag
|
||||
:type="player.status === '已签到' ? 'success' : player.status === '异常' ? 'danger' : 'info'"
|
||||
size="small"
|
||||
>
|
||||
{{ player.status || '未签到' }}
|
||||
</el-tag>
|
||||
</span>
|
||||
<span class="player-detail-actions">
|
||||
<el-button
|
||||
v-if="(player.status || '未签到') === '未签到'"
|
||||
link
|
||||
size="small"
|
||||
@click="markPlayerAsException(group, team, playerIndex)"
|
||||
:disabled="isScheduleCompleted"
|
||||
style="color: #f56c6c;"
|
||||
>
|
||||
标记异常
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="player.status === '异常'"
|
||||
link
|
||||
size="small"
|
||||
@click="removePlayerException(group, team, playerIndex)"
|
||||
:disabled="isScheduleCompleted"
|
||||
style="color: #67c23a;"
|
||||
>
|
||||
取消异常
|
||||
</el-button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 场地 Tab -->
|
||||
<div v-show="activeTab === 'venue'" class="tab-content">
|
||||
<!-- 场地列表 -->
|
||||
<div class="venue-list">
|
||||
<div class="venue-buttons">
|
||||
<el-button
|
||||
v-for="venue in venues"
|
||||
:key="'venue-tab-' + venue.id"
|
||||
size="small"
|
||||
:type="selectedVenueIdForVenueTab === venue.id ? 'primary' : ''"
|
||||
@click="selectedVenueIdForVenueTab = venue.id"
|
||||
>
|
||||
{{ venue.venueName }}
|
||||
</el-button>
|
||||
<div v-if="venues.length === 0" class="no-venue-hint">
|
||||
暂无场地信息,请先在赛事管理中配置场地
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="time-selector">
|
||||
<el-button
|
||||
v-for="(time, index) in timeSlots"
|
||||
@@ -272,19 +377,25 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ArrowDown, ArrowRight } from '@element-plus/icons-vue'
|
||||
import { getVenuesByCompetition } from '@/api/martial/venue'
|
||||
import { getCompetitionDetail } from '@/api/martial/competition'
|
||||
import { getScheduleResult, saveAndLockSchedule, saveDraftSchedule, triggerAutoArrange, moveScheduleGroup } from '@/api/martial/activitySchedule'
|
||||
import { getScheduleResult, saveAndLockSchedule, saveDraftSchedule, triggerAutoArrange, moveScheduleGroup, exportSchedule } from '@/api/martial/activitySchedule'
|
||||
|
||||
export default {
|
||||
name: 'MartialScheduleList',
|
||||
components: {
|
||||
ArrowDown,
|
||||
ArrowRight
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
competitionId: null,
|
||||
orderId: null,
|
||||
activeTab: 'competition',
|
||||
selectedTime: 0,
|
||||
selectedVenueId: null, // 选中的场地ID
|
||||
selectedVenueId: null, // 选中的场地ID(竞赛分组Tab)
|
||||
selectedVenueIdForVenueTab: null, // 选中的场地ID(场地Tab)
|
||||
confirmDialogVisible: false,
|
||||
isScheduleCompleted: false, // 是否已完成编排
|
||||
loading: false,
|
||||
@@ -306,7 +417,9 @@ export default {
|
||||
|
||||
// 异常组相关
|
||||
exceptionDialogVisible: false,
|
||||
exceptionList: [] // 异常参赛人员列表
|
||||
exceptionList: [], // 异常参赛人员列表
|
||||
expandedTeams: {}, // 展开的队伍 { 'groupId-teamId': true }
|
||||
expandedProjects: {} // 展开的项目 { 'groupId': true },默认收起
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -334,17 +447,25 @@ export default {
|
||||
return filtered
|
||||
},
|
||||
|
||||
// 场地标签页的数据 - 根据选中的时间段动态生成
|
||||
// 场地标签页的数据 - 根据选中的场地和时间段动态生成
|
||||
venueData() {
|
||||
if (this.selectedTime === null) {
|
||||
return []
|
||||
}
|
||||
|
||||
// 获取选中时间段的所有分组
|
||||
const groupsInTimeSlot = this.competitionGroups.filter(
|
||||
let groupsInTimeSlot = this.competitionGroups.filter(
|
||||
group => group.timeSlotIndex === this.selectedTime
|
||||
)
|
||||
|
||||
// 如果选中了场地,进一步过滤
|
||||
if (this.selectedVenueIdForVenueTab) {
|
||||
groupsInTimeSlot = groupsInTimeSlot.filter(group => {
|
||||
return group.venueId === this.selectedVenueIdForVenueTab ||
|
||||
Number(group.venueId) === Number(this.selectedVenueIdForVenueTab)
|
||||
})
|
||||
}
|
||||
|
||||
// 将分组转换为场地视图的数据格式
|
||||
return groupsInTimeSlot.map(group => ({
|
||||
project: group.title,
|
||||
@@ -372,6 +493,254 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 检查项目是否展开
|
||||
isProjectExpanded(groupId) {
|
||||
return this.expandedProjects[groupId] === true
|
||||
},
|
||||
|
||||
// 切换项目展开状态
|
||||
toggleProjectExpand(groupId) {
|
||||
if (this.expandedProjects[groupId]) {
|
||||
delete this.expandedProjects[groupId]
|
||||
} else {
|
||||
this.expandedProjects[groupId] = true
|
||||
}
|
||||
// 触发响应式更新
|
||||
this.expandedProjects = { ...this.expandedProjects }
|
||||
},
|
||||
|
||||
// 检查队伍是否展开
|
||||
isTeamExpanded(groupId, teamId) {
|
||||
const key = groupId + '-' + teamId
|
||||
return this.expandedTeams[key] === true
|
||||
},
|
||||
|
||||
// 切换队伍展开状态
|
||||
toggleTeamExpand(groupId, teamId) {
|
||||
const key = groupId + '-' + teamId
|
||||
if (this.expandedTeams[key]) {
|
||||
delete this.expandedTeams[key]
|
||||
} else {
|
||||
this.expandedTeams[key] = true
|
||||
}
|
||||
// 触发响应式更新
|
||||
this.expandedTeams = { ...this.expandedTeams }
|
||||
},
|
||||
|
||||
// 标记选手为异常
|
||||
markPlayerAsException(group, team, playerIndex) {
|
||||
const player = team.players[playerIndex]
|
||||
if (player) {
|
||||
player.status = '异常'
|
||||
// 添加到异常列表
|
||||
this.exceptionList.push({
|
||||
groupId: group.id,
|
||||
groupTitle: group.title,
|
||||
teamId: team.id,
|
||||
schoolUnit: team.schoolUnit,
|
||||
playerId: player.id,
|
||||
playerName: player.playerName,
|
||||
status: '异常'
|
||||
})
|
||||
this.$message.success('已标记为异常')
|
||||
}
|
||||
},
|
||||
|
||||
// 取消选手异常状态
|
||||
removePlayerException(group, team, playerIndex) {
|
||||
const player = team.players[playerIndex]
|
||||
if (player) {
|
||||
player.status = '未签到'
|
||||
// 从异常列表中移除
|
||||
const idx = this.exceptionList.findIndex(
|
||||
e => e.playerId === player.id && e.groupId === group.id
|
||||
)
|
||||
if (idx !== -1) {
|
||||
this.exceptionList.splice(idx, 1)
|
||||
}
|
||||
this.$message.success('已取消异常标记')
|
||||
}
|
||||
},
|
||||
|
||||
// 将选手按学校/单位分组为队伍
|
||||
groupItemsByTeam(items) {
|
||||
if (!items || items.length === 0) return []
|
||||
const teamMap = new Map()
|
||||
items.forEach(item => {
|
||||
const key = item.schoolUnit
|
||||
if (!teamMap.has(key)) {
|
||||
teamMap.set(key, {
|
||||
id: `team_${item.id}`,
|
||||
schoolUnit: key,
|
||||
players: [],
|
||||
playerIds: []
|
||||
})
|
||||
}
|
||||
teamMap.get(key).players.push(item)
|
||||
teamMap.get(key).playerIds.push(item.id)
|
||||
})
|
||||
return Array.from(teamMap.values())
|
||||
},
|
||||
|
||||
// 获取队伍数量
|
||||
getTeamCount(group) {
|
||||
if (!group.items || group.items.length === 0) return 0
|
||||
const teams = this.groupItemsByTeam(group.items)
|
||||
return teams.length
|
||||
},
|
||||
|
||||
// 快速移动分组(从popover中调用)
|
||||
async quickMoveGroup(group) {
|
||||
if (!this.moveTargetVenueId) {
|
||||
this.$message.warning('请选择目标场地')
|
||||
return
|
||||
}
|
||||
|
||||
const targetVenue = this.venues.find(v => v.id === this.moveTargetVenueId)
|
||||
|
||||
try {
|
||||
// 调用后端API移动分组
|
||||
const res = await moveScheduleGroup({
|
||||
groupId: group.id,
|
||||
targetVenueId: this.moveTargetVenueId,
|
||||
targetTimeSlotIndex: this.selectedTime // 保持当前时间段
|
||||
})
|
||||
|
||||
if (res.data.success) {
|
||||
// 更新前端数据
|
||||
group.venueId = this.moveTargetVenueId
|
||||
group.venueName = targetVenue ? targetVenue.venueName : ''
|
||||
|
||||
this.$message.success(`已移动到 ${group.venueName}`)
|
||||
} else {
|
||||
this.$message.error(res.data.msg || '移动分组失败')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('移动分组失败:', error)
|
||||
this.$message.error('移动分组失败,请稍后重试')
|
||||
}
|
||||
},
|
||||
|
||||
// 获取队伍状态
|
||||
getTeamStatus(team) {
|
||||
if (!team || !team.players) return '未签到'
|
||||
const statuses = team.players.map(p => p.status || '未签到')
|
||||
if (statuses.every(s => s === '已签到')) return '已签到'
|
||||
if (statuses.every(s => s === '未签到')) return '未签到'
|
||||
if (statuses.some(s => s === '异常')) return '部分异常'
|
||||
return '部分签到'
|
||||
},
|
||||
|
||||
// 获取队伍状态类型
|
||||
getTeamStatusType(team) {
|
||||
const status = this.getTeamStatus(team)
|
||||
switch(status) {
|
||||
case '已签到': return 'success'
|
||||
case '部分异常': return 'danger'
|
||||
case '部分签到': return 'warning'
|
||||
default: return 'info'
|
||||
}
|
||||
},
|
||||
|
||||
// 队伍上移
|
||||
handleTeamMoveUp(group, teamIndex) {
|
||||
const teams = this.groupItemsByTeam(group.items)
|
||||
if (teamIndex === 0 || this.isScheduleCompleted) return
|
||||
|
||||
const currentTeam = teams[teamIndex]
|
||||
const prevTeam = teams[teamIndex - 1]
|
||||
|
||||
// 找到两个队伍在原数组中的位置范围
|
||||
const currentPlayerIds = currentTeam.playerIds
|
||||
const prevPlayerIds = prevTeam.playerIds
|
||||
|
||||
// 重新排序:将当前队伍的所有选手移到前一个队伍之前
|
||||
const newItems = []
|
||||
let addedCurrent = false
|
||||
|
||||
for (const item of group.items) {
|
||||
if (prevPlayerIds.includes(item.id) && !addedCurrent) {
|
||||
// 先添加当前队伍的所有选手
|
||||
for (const cItem of group.items) {
|
||||
if (currentPlayerIds.includes(cItem.id)) {
|
||||
newItems.push(cItem)
|
||||
}
|
||||
}
|
||||
addedCurrent = true
|
||||
}
|
||||
if (!currentPlayerIds.includes(item.id)) {
|
||||
newItems.push(item)
|
||||
}
|
||||
}
|
||||
|
||||
group.items = newItems
|
||||
this.$message.success('上移成功')
|
||||
},
|
||||
|
||||
// 队伍下移
|
||||
handleTeamMoveDown(group, teamIndex) {
|
||||
const teams = this.groupItemsByTeam(group.items)
|
||||
if (teamIndex === teams.length - 1 || this.isScheduleCompleted) return
|
||||
|
||||
const currentTeam = teams[teamIndex]
|
||||
const nextTeam = teams[teamIndex + 1]
|
||||
|
||||
const currentPlayerIds = currentTeam.playerIds
|
||||
const nextPlayerIds = nextTeam.playerIds
|
||||
|
||||
// 重新排序:将当前队伍的所有选手移到下一个队伍之后
|
||||
const newItems = []
|
||||
let addedCurrent = false
|
||||
|
||||
for (const item of group.items) {
|
||||
if (!currentPlayerIds.includes(item.id)) {
|
||||
newItems.push(item)
|
||||
}
|
||||
if (nextPlayerIds.includes(item.id)) {
|
||||
// 检查是否是下一个队伍的最后一个选手
|
||||
const isLastOfNext = item.id === nextPlayerIds[nextPlayerIds.length - 1]
|
||||
if (isLastOfNext && !addedCurrent) {
|
||||
// 在下一个队伍最后一个选手之后添加当前队伍
|
||||
for (const cItem of group.items) {
|
||||
if (currentPlayerIds.includes(cItem.id)) {
|
||||
newItems.push(cItem)
|
||||
}
|
||||
}
|
||||
addedCurrent = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
group.items = newItems
|
||||
this.$message.success('下移成功')
|
||||
},
|
||||
|
||||
// 标记单个选手为异常
|
||||
markPlayerAsException(group, player) {
|
||||
if (this.isScheduleCompleted) {
|
||||
this.$message.warning('编排已完成,无法标记异常')
|
||||
return
|
||||
}
|
||||
|
||||
// 在 group.items 中找到该选手并修改状态
|
||||
const item = group.items.find(i => i.id === player.id)
|
||||
if (item) {
|
||||
item.status = '异常'
|
||||
|
||||
// 添加到异常列表
|
||||
this.exceptionList.push({
|
||||
groupId: group.id,
|
||||
groupTitle: group.title,
|
||||
participantId: player.id,
|
||||
schoolUnit: player.schoolUnit,
|
||||
playerName: player.playerName,
|
||||
status: '异常'
|
||||
})
|
||||
|
||||
this.$message.success(`已将 ${player.playerName} 标记为异常`)
|
||||
}
|
||||
},
|
||||
|
||||
goBack() {
|
||||
this.$router.go(-1)
|
||||
},
|
||||
@@ -463,6 +832,7 @@ export default {
|
||||
// 默认选中第一个场地
|
||||
if (this.venues.length > 0) {
|
||||
this.selectedVenueId = this.venues[0].id
|
||||
this.selectedVenueIdForVenueTab = this.venues[0].id
|
||||
}
|
||||
console.log('加载的场地数据:', this.venues)
|
||||
}
|
||||
@@ -505,6 +875,7 @@ export default {
|
||||
items: (group.participants || []).map(p => ({
|
||||
id: p.id,
|
||||
schoolUnit: p.schoolUnit,
|
||||
playerName: p.playerName,
|
||||
status: p.status || '未签到',
|
||||
sortOrder: p.sortOrder
|
||||
}))
|
||||
@@ -742,8 +1113,24 @@ export default {
|
||||
group.items.splice(itemIndex + 1, 0, temp)
|
||||
this.$message.success('下移成功')
|
||||
},
|
||||
handleExport() {
|
||||
this.$message.success('导出功能开发中')
|
||||
async handleExport() {
|
||||
try {
|
||||
this.loading = true
|
||||
const res = await exportSchedule(this.competitionId)
|
||||
// axios 返回的是 response 对象,需要取 res.data
|
||||
const blob = new Blob([res.data], { type: 'application/vnd.ms-excel' })
|
||||
const link = document.createElement('a')
|
||||
link.href = window.URL.createObjectURL(blob)
|
||||
link.download = `赛程表_${this.competitionInfo.competitionName || this.competitionId}.xlsx`
|
||||
link.click()
|
||||
window.URL.revokeObjectURL(link.href)
|
||||
this.$message.success('导出成功')
|
||||
} catch (error) {
|
||||
console.error('导出失败:', error)
|
||||
this.$message.error('导出失败,请稍后重试')
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
handleConfirm() {
|
||||
this.confirmDialogVisible = true
|
||||
@@ -945,6 +1332,196 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
// 新的项目卡片样式
|
||||
.project-card {
|
||||
margin-bottom: 20px;
|
||||
border: 1px solid #e4e7ed;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
|
||||
.project-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 12px 16px;
|
||||
background: #fafafa;
|
||||
border-bottom: 1px solid #e4e7ed;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s;
|
||||
|
||||
&:hover {
|
||||
background: #f0f0f0;
|
||||
}
|
||||
|
||||
&.project-header-collapsed {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.project-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
|
||||
.project-expand-icon {
|
||||
width: 20px;
|
||||
color: #909399;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.project-index {
|
||||
font-weight: 600;
|
||||
color: #e6a23c;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.project-title {
|
||||
font-weight: 600;
|
||||
color: #e6a23c;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.project-meta {
|
||||
color: #606266;
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
|
||||
.project-actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.team-list {
|
||||
.team-row {
|
||||
border-bottom: 1px solid #ebeef5;
|
||||
background: #fff;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
&.team-row-expanded {
|
||||
background: #fafafa;
|
||||
}
|
||||
|
||||
.team-main {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 12px 16px;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background: #f5f7fa;
|
||||
}
|
||||
|
||||
.expand-icon {
|
||||
width: 20px;
|
||||
color: #909399;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.team-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-width: 180px;
|
||||
|
||||
.team-index {
|
||||
color: #909399;
|
||||
font-size: 13px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.team-name {
|
||||
color: #303133;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
.player-list {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
padding: 0 20px;
|
||||
|
||||
.player-tag {
|
||||
color: #606266;
|
||||
font-size: 13px;
|
||||
padding: 2px 8px;
|
||||
background: #f4f4f5;
|
||||
border-radius: 4px;
|
||||
|
||||
&.player-exception {
|
||||
color: #f56c6c;
|
||||
background: #fef0f0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.team-actions {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
min-width: 80px;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
|
||||
.team-expand-content {
|
||||
background: #fafafa;
|
||||
border-top: 1px dashed #e4e7ed;
|
||||
padding: 8px 16px 8px 56px;
|
||||
|
||||
.player-detail-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 8px 12px;
|
||||
border-bottom: 1px solid #ebeef5;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.player-detail-index {
|
||||
width: 30px;
|
||||
color: #909399;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.player-detail-name {
|
||||
flex: 1;
|
||||
color: #303133;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.player-detail-status {
|
||||
width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.player-detail-actions {
|
||||
width: 100px;
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.move-popover {
|
||||
.move-label {
|
||||
font-size: 13px;
|
||||
color: #606266;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.group-footer-hints {
|
||||
margin-top: 15px;
|
||||
padding: 8px 12px;
|
||||
@@ -1014,4 +1591,28 @@ export default {
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
// 展开行样式
|
||||
.player-expand-list {
|
||||
padding: 10px 20px 10px 60px;
|
||||
background: #fafafa;
|
||||
|
||||
.player-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 15px;
|
||||
padding: 8px 0;
|
||||
border-bottom: 1px dashed #eee;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.player-name {
|
||||
min-width: 80px;
|
||||
color: #606266;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
1019
src/views/martial/schedule/index.vue.bak
Normal file
1019
src/views/martial/schedule/index.vue.bak
Normal file
File diff suppressed because it is too large
Load Diff
@@ -154,6 +154,11 @@
|
||||
<div class="total-score-display">
|
||||
<span class="label">总分:</span>
|
||||
<span class="value">{{ formatScore(currentDetail.totalScore) }}</span>
|
||||
<div class="calculation-note">
|
||||
<span v-if="currentDetail.judgeScores.length > 2">
|
||||
(去掉最高分和最低分后的平均分)
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -182,96 +187,6 @@ export default {
|
||||
projectOptions: [],
|
||||
venueOptions: [],
|
||||
scoreList: [],
|
||||
allTableData: [
|
||||
{
|
||||
id: 1,
|
||||
projectName: '男子组陈氏太极拳',
|
||||
venueName: '第一场地',
|
||||
playerName: '张三',
|
||||
teamName: '少林寺武术大学院',
|
||||
idCard: '123456789000000000',
|
||||
playerNo: '123-4567898275',
|
||||
judgeScores: [8.906, 8.905, 8.908, 8.907, 8.906],
|
||||
totalScore: 8.907
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
projectName: '女子组长拳',
|
||||
venueName: '第一场地',
|
||||
playerName: '李四',
|
||||
teamName: '武当武术学院',
|
||||
idCard: '123456789000000001',
|
||||
playerNo: '123-4567898276',
|
||||
judgeScores: [9.125, 9.130, 9.128, 9.126, 9.129],
|
||||
totalScore: 9.128
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
projectName: '男子组陈氏太极拳',
|
||||
venueName: '第二场地',
|
||||
playerName: '王五',
|
||||
teamName: '峨眉武术协会',
|
||||
idCard: '123456789000000002',
|
||||
playerNo: '123-4567898277',
|
||||
judgeScores: [8.550, 8.548, 8.552, 8.551, 8.549],
|
||||
totalScore: 8.550
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
projectName: '女子组双剑(含长穗双剑)',
|
||||
venueName: '第一场地',
|
||||
playerName: '赵六',
|
||||
teamName: '昆仑武术馆',
|
||||
idCard: '123456789000000003',
|
||||
playerNo: '123-4567898278',
|
||||
judgeScores: [9.245, 9.248, 9.246, 9.247, 9.249],
|
||||
totalScore: 9.247
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
projectName: '男子组杨氏太极拳',
|
||||
venueName: '第三场地',
|
||||
playerName: '孙七',
|
||||
teamName: '华山武术学校',
|
||||
idCard: '123456789000000004',
|
||||
playerNo: '123-4567898279',
|
||||
judgeScores: [8.785, 8.788, 8.786, 8.787, 8.785],
|
||||
totalScore: 8.786
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
projectName: '女子组刀术',
|
||||
venueName: '第二场地',
|
||||
playerName: '周八',
|
||||
teamName: '少林寺武术大学院',
|
||||
idCard: '123456789000000005',
|
||||
playerNo: '123-4567898280',
|
||||
judgeScores: [8.925, 8.928, 8.926, 8.927, 8.925],
|
||||
totalScore: 8.926
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
projectName: '男子组棍术',
|
||||
venueName: '第四场地',
|
||||
playerName: '吴九',
|
||||
teamName: '武当武术学院',
|
||||
idCard: '123456789000000006',
|
||||
playerNo: '123-4567898281',
|
||||
judgeScores: [9.015, 9.018, 9.016, 9.017, 9.015],
|
||||
totalScore: 9.016
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
projectName: '女子组枪术',
|
||||
venueName: '第三场地',
|
||||
playerName: '郑十',
|
||||
teamName: '峨眉武术协会',
|
||||
idCard: '123456789000000007',
|
||||
playerNo: '123-4567898282',
|
||||
judgeScores: [8.665, 8.668, 8.666, 8.667, 8.665],
|
||||
totalScore: 8.666
|
||||
}
|
||||
],
|
||||
tableData: [],
|
||||
pagination: {
|
||||
current: 1,
|
||||
@@ -345,25 +260,22 @@ export default {
|
||||
try {
|
||||
const res = await getScoreList(this.pagination.current, this.pagination.size, params)
|
||||
console.log('评分列表返回数据:', res)
|
||||
console.log('===== 调试:后端返回的数据结构 =====')
|
||||
const responseData = res.data?.data
|
||||
if (responseData && responseData.records && responseData.records.length > 0) {
|
||||
console.log('第一条评分记录:', responseData.records[0])
|
||||
console.log('记录字段:', Object.keys(responseData.records[0]))
|
||||
console.log('是否包含 projectName:', 'projectName' in responseData.records[0])
|
||||
console.log('是否包含 venueName:', 'venueName' in responseData.records[0])
|
||||
console.log('是否包含 playerName:', 'playerName' in responseData.records[0])
|
||||
console.log('projectId 值:', responseData.records[0].projectId)
|
||||
console.log('venueId 值:', responseData.records[0].venueId)
|
||||
console.log('athleteId 值:', responseData.records[0].athleteId)
|
||||
}
|
||||
console.log('======================================')
|
||||
|
||||
const responseData = res.data?.data
|
||||
if (responseData && responseData.records) {
|
||||
this.scoreList = responseData.records
|
||||
// 过滤掉 projectId 为 null 的无效记录
|
||||
const validScores = responseData.records.filter(score => {
|
||||
if (!score.projectId) {
|
||||
console.warn('发现无效评分记录(projectId为空):', score)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
this.scoreList = validScores
|
||||
|
||||
// 补充关联数据(项目名称、场地名称、选手名称)
|
||||
await this.enrichScoreData(responseData.records)
|
||||
await this.enrichScoreData(validScores)
|
||||
|
||||
// 按选手分组评分数据
|
||||
this.processScoreData(this.scoreList)
|
||||
@@ -466,6 +378,12 @@ export default {
|
||||
const athleteMap = new Map()
|
||||
|
||||
scores.forEach(score => {
|
||||
// 确保 projectId 存在
|
||||
if (!score.projectId) {
|
||||
console.warn('跳过无效评分记录:', score)
|
||||
return
|
||||
}
|
||||
|
||||
const key = `${score.athleteId}-${score.projectId}`
|
||||
if (!athleteMap.has(key)) {
|
||||
athleteMap.set(key, {
|
||||
@@ -495,11 +413,10 @@ export default {
|
||||
})
|
||||
})
|
||||
|
||||
// 计算总分(平均分)
|
||||
// 计算总分(去掉最高最低分后的平均分)
|
||||
this.tableData = Array.from(athleteMap.values()).map(athlete => {
|
||||
if (athlete.judgeScores.length > 0) {
|
||||
const sum = athlete.judgeScores.reduce((a, b) => a + b, 0)
|
||||
athlete.totalScore = sum / athlete.judgeScores.length
|
||||
athlete.totalScore = this.calculateFinalScore(athlete.judgeScores)
|
||||
}
|
||||
return athlete
|
||||
})
|
||||
@@ -516,6 +433,34 @@ export default {
|
||||
this.judgeColumns = Array(maxJudges).fill(null)
|
||||
},
|
||||
|
||||
/**
|
||||
* 计算最终得分
|
||||
* 规则:
|
||||
* - 如果裁判数 <= 2,直接取平均值
|
||||
* - 如果裁判数 > 2,去掉最高分和最低分后取平均值
|
||||
*/
|
||||
calculateFinalScore(scores) {
|
||||
if (!scores || scores.length === 0) {
|
||||
return 0
|
||||
}
|
||||
|
||||
// 如果只有1-2个裁判,直接取平均值
|
||||
if (scores.length <= 2) {
|
||||
const sum = scores.reduce((a, b) => a + b, 0)
|
||||
return sum / scores.length
|
||||
}
|
||||
|
||||
// 3个及以上裁判,去掉最高分和最低分
|
||||
const sortedScores = [...scores].sort((a, b) => a - b)
|
||||
|
||||
// 去掉第一个(最低分)和最后一个(最高分)
|
||||
const validScores = sortedScores.slice(1, -1)
|
||||
|
||||
// 计算平均值
|
||||
const sum = validScores.reduce((a, b) => a + b, 0)
|
||||
return sum / validScores.length
|
||||
},
|
||||
|
||||
// 查询
|
||||
handleSearch() {
|
||||
this.pagination.current = 1
|
||||
@@ -663,6 +608,12 @@ export default {
|
||||
font-weight: 700;
|
||||
color: #1b7c5e;
|
||||
}
|
||||
|
||||
.calculation-note {
|
||||
margin-top: 8px;
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user