This commit is contained in:
2025-11-29 14:44:10 +08:00
parent 135696ef93
commit 0d4bbcd1f2
29 changed files with 776 additions and 316 deletions

View File

@@ -0,0 +1,10 @@
{
"permissions": {
"allow": [
"Bash(dir:*)",
"Bash(npm run build:*)"
],
"deny": [],
"ask": []
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

View File

@@ -17,21 +17,18 @@
<link rel='stylesheet' href='/iconfont/saber/iconfont.css'>
<link rel='stylesheet' href='/css/loading.css'>
<link rel='stylesheet' href='/css/saber.css'>
<title>Saber企业级开发平台</title>
<title>武术赛事通</title>
</head>
<body>
<div id='app'>
<div id='loader-wrapper'>
<div class='loader-box'>
<span>Saber</span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
</div>
<div class='loader-title'>系统加载中</div>
</div>

View File

@@ -1,5 +1,5 @@
{
"name": "saber",
"name": "martial-web",
"version": "4.0.1",
"scripts": {
"dev": "vite --host",

BIN
public/img/图标 3@3x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
public/img/图标 4@3x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -1,4 +1,4 @@
import request from '@/router/axios';
import request from '@/axios';
// ==================== 武术赛事订单管理接口 ====================

View File

@@ -2,10 +2,10 @@
* 全局配置文件
*/
export default {
title: 'saber',
logo: 'S',
title: '武术赛事通',
logo: '',
key: 'saber', //配置主键,目前用于存储
indexTitle: 'Saber Admin',
indexTitle: '武术赛事通',
clientId: 'saber3', // 客户端id
clientSecret: 'saber3_secret', // 客户端密钥
tenantMode: true, // 是否开启租户模式

View File

@@ -1,5 +1,5 @@
export default {
title: 'Saber Admin',
title: 'Martial Arts Events',
logoutTip: 'Exit the system, do you want to continue?',
submitText: 'submit',
cancelText: 'cancel',

View File

@@ -1,5 +1,5 @@
export default {
title: 'Saber企业级开发平台',
title: '武术赛事通',
logoutTip: '退出系统, 是否继续?',
submitText: '确定',
cancelText: '取消',

View File

@@ -9,20 +9,20 @@
{{ time }}
</div>
<div class="login-left-content">
<h1 class="title">智能设备管理系统</h1>
<p class="subtitle">专业的设备管理解决方案</p>
<h1 class="title">武术赛事通</h1>
<p class="subtitle">专业的武术赛事管理平台</p>
<div class="feature-box">
<div class="feature-item">
<span>设备实时监控</span>
<span>赛事报名管理</span>
</div>
<div class="feature-item">
<span>数据分析统计</span>
<span>赛程编排</span>
</div>
<div class="feature-item">
<span>智能预警</span>
<span>赛事调度</span>
</div>
<div class="feature-item">
<span>远程控制</span>
<span>数据统计分析</span>
</div>
</div>
</div>
@@ -30,7 +30,7 @@
<div class="login-border">
<div class="login-main">
<h4 class="login-title">
登录 saber
登录 武术赛事通
<top-lang></top-lang>
</h4>
<userLogin></userLogin>

View File

@@ -42,4 +42,35 @@ export default [
},
],
},
{
path: '/martial',
component: Layout,
redirect: '/martial/order/list',
children: [
{
path: 'registration/detail',
name: '报名详情',
meta: {
keepAlive: false,
},
component: () => import(/* webpackChunkName: "martial" */ '@/views/martial/registration/detail.vue'),
},
{
path: 'schedule/list',
name: '编排',
meta: {
keepAlive: false,
},
component: () => import(/* webpackChunkName: "martial" */ '@/views/martial/schedule/list.vue'),
},
{
path: 'dispatch/list',
name: '调度',
meta: {
keepAlive: false,
},
component: () => import(/* webpackChunkName: "martial" */ '@/views/martial/dispatch/list.vue'),
},
],
},
];

View File

@@ -1,15 +1,13 @@
<template>
<div class="martial-dispatch-container">
<el-card shadow="hover">
<div class="page-header">
<el-button icon="el-icon-back" @click="goBack" size="small">返回</el-button>
</div>
<el-button icon="el-icon-back" size="small" @click="goBack">返回</el-button>
<h2 class="page-title">调度</h2>
</div>
<div class="tabs-section">
<div class="tab-buttons">
<el-button type="primary">竞赛分组</el-button>
<el-button size="small" type="primary">竞赛分组</el-button>
</div>
<div class="tab-content">
@@ -23,7 +21,7 @@
>
{{ time }}
</el-button>
<el-button size="small" type="success" @click="handleBatchComplete" style="margin-left: auto;">
<el-button size="small" plain style="margin-left: auto;" @click="handleBatchComplete">
批次完赛
</el-button>
</div>
@@ -37,43 +35,61 @@
<span class="group-meta">{{ group.code }}</span>
</div>
<div class="group-actions">
<el-button
size="small"
:type="group.venueType === 1 ? 'primary' : 'success'"
@click="handleVenueChange(index)"
>
<div class="toggle-buttons">
<el-button size="small" :type="group.viewMode === 'dispatch' ? 'primary' : ''" @click="setViewMode(index, 'dispatch')">
调度列表
</el-button>
<el-button size="small" :type="group.viewMode === 'menu' ? 'primary' : 'success'" @click="setViewMode(index, 'menu')">
{{ group.venueType === 1 ? '一号场地' : '二号场地' }}
</el-button>
</div>
<el-button size="small" type="danger">赛程</el-button>
</div>
</div>
<el-table :data="group.items" border stripe size="small">
<el-table-column label="序号" type="index" width="80" align="center"></el-table-column>
<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 label="状态" width="120" align="center">
<template slot-scope="scope">
<el-table-column label="状态" width="100" align="center">
<template #default="scope">
<el-tag v-if="scope.row.completed" type="success" size="small">已完赛</el-tag>
<el-tag v-else type="warning" size="small">未完赛</el-tag>
</template>
</el-table-column>
<el-table-column label="裁判" width="120" align="center">
<template slot-scope="scope">
<el-table-column label="裁判" width="100" align="center">
<template #default="scope">
<el-tag v-if="scope.row.refereed" type="success" size="small">已裁判</el-tag>
<el-tag v-else type="danger" size="small">未裁判</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="120" align="center">
<template>
<el-button type="text" icon="el-icon-upload2" size="small"></el-button>
<el-button type="text" icon="el-icon-download" size="small"></el-button>
<el-table-column label="操作" width="100" align="center">
<template #default="scope">
<el-button
type="text"
size="small"
@click="handleMoveUp(index, scope.$index)"
:disabled="scope.$index === 0"
title="上移"
class="move-btn"
>
<img src="/img/图标 3@3x.png" class="move-icon" alt="上移" />
</el-button>
<el-button
type="text"
size="small"
@click="handleMoveDown(index, scope.$index)"
:disabled="scope.$index === group.items.length - 1"
title="下移"
class="move-btn"
>
<img src="/img/图标 4@3x.png" class="move-icon" alt="下移" />
</el-button>
</template>
</el-table-column>
</el-table>
</div>
</div>
</div>
</el-card>
</div>
</template>
@@ -82,7 +98,7 @@ export default {
name: 'MartialDispatchList',
data() {
return {
activeTab: 'competition',
orderId: null,
selectedTime: 1,
timeSlots: [
'2025年11月6日上午8:30',
@@ -94,8 +110,9 @@ export default {
title: '1. 小学组小组赛男女类',
type: '集体',
count: '2队',
code: '1',
code: '1101',
venueType: 1,
viewMode: 'dispatch',
items: [
{ schoolUnit: '少林寺武校', completed: true, refereed: false },
{ schoolUnit: '访河社区', completed: false, refereed: false }
@@ -107,30 +124,73 @@ export default {
count: '3队',
code: '1组',
venueType: 2,
viewMode: 'dispatch',
items: [
{ schoolUnit: '少林寺武校', completed: true, refereed: true },
{ schoolUnit: '访河社区', completed: false, refereed: false },
{ schoolUnit: '少林寺武校', completed: true, refereed: true }
]
},
{
title: '2. 中学组决赛',
type: '集体',
count: '4队',
code: '2101',
venueType: 1,
viewMode: 'dispatch',
items: [
{ schoolUnit: '成都体育学院', completed: true, refereed: true },
{ schoolUnit: '武侯实验中学', completed: true, refereed: false },
{ schoolUnit: '石室中学', completed: false, refereed: false },
{ schoolUnit: '七中育才', completed: false, refereed: false }
]
}
]
}
},
mounted() {
this.orderId = this.$route.query.orderId
// 使用静态数据不调用API
},
methods: {
goBack() {
this.$router.go(-1)
this.$router.push('/martial/order/list')
},
handleVenueChange(index) {
const group = this.dispatchGroups[index]
group.venueType = group.venueType === 1 ? 2 : 1
setViewMode(index, mode) {
this.dispatchGroups[index].viewMode = mode
this.$message.success(`已切换到${mode === 'dispatch' ? '调度列表' : '场地'}模式`)
},
handleMoveUp(groupIndex, itemIndex) {
if (itemIndex === 0) return
const group = this.dispatchGroups[groupIndex]
const temp = group.items[itemIndex]
group.items.splice(itemIndex, 1)
group.items.splice(itemIndex - 1, 0, temp)
this.$message.success('上移成功')
},
handleMoveDown(groupIndex, itemIndex) {
const group = this.dispatchGroups[groupIndex]
if (itemIndex === group.items.length - 1) return
const temp = group.items[itemIndex]
group.items.splice(itemIndex, 1)
group.items.splice(itemIndex + 1, 0, temp)
this.$message.success('下移成功')
},
handleBatchComplete() {
this.$confirm('确定要标记所有为完赛状态吗?', '批次完赛', {
this.$confirm('确定要标记当前批次所有为完赛状态吗?', '批次完赛', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
// 标记当前时间段所有为完赛
this.dispatchGroups.forEach(group => {
group.items.forEach(item => {
item.completed = true
})
})
this.$message.success('批次完赛成功')
}).catch(() => {
// 取消操作
})
}
}
@@ -139,43 +199,46 @@ export default {
<style lang="scss" scoped>
.martial-dispatch-container {
padding: 20px;
padding: 15px;
background: #fff;
.page-header {
display: flex;
align-items: center;
margin-bottom: 15px;
}
gap: 10px;
.page-title {
margin: 0 0 25px 0;
font-size: 20px;
margin: 0;
font-size: 18px;
font-weight: 600;
color: #2c3e50;
}
}
.tabs-section {
.tab-buttons {
margin-bottom: 20px;
margin-bottom: 15px;
display: flex;
gap: 10px;
gap: 8px;
}
.tab-content {
.time-selector {
margin-bottom: 20px;
margin-bottom: 15px;
display: flex;
gap: 10px;
gap: 8px;
align-items: center;
flex-wrap: wrap;
}
.dispatch-group {
margin-bottom: 25px;
margin-bottom: 15px;
.group-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px 16px;
padding: 10px 14px;
background: #f5f7fa;
border: 1px solid #e4e7ed;
border-bottom: none;
@@ -183,27 +246,49 @@ export default {
.group-info {
display: flex;
align-items: center;
gap: 15px;
gap: 12px;
.group-title {
font-weight: 600;
color: #2c3e50;
font-size: 14px;
font-size: 13px;
}
.group-meta {
color: #909399;
font-size: 13px;
font-size: 12px;
}
}
.group-actions {
display: flex;
gap: 10px;
gap: 8px;
align-items: center;
.toggle-buttons {
display: flex;
gap: 4px;
}
}
}
}
}
}
.move-btn {
padding: 4px;
&:disabled {
opacity: 0.4;
cursor: not-allowed;
}
}
.move-icon {
width: 20px;
height: 20px;
display: inline-block;
vertical-align: middle;
}
}
</style>

View File

@@ -4,14 +4,14 @@
<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
style="width: 280px"
size="small"
style="width: 240px"
>
<i slot="prefix" class="el-input__icon el-icon-search"></i>
</el-input>
@@ -21,56 +21,50 @@
v-model="searchForm.status"
placeholder="状态"
clearable
style="width: 200px"
size="small"
style="width: 180px"
></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSearch">查询</el-button>
<el-button type="primary" size="small" @click="handleSearch">查询</el-button>
</el-form-item>
</el-form>
<div v-if="tableData.length === 0" class="empty-data">
<div class="empty-content">
<p class="empty-text">编辑功能在完成后开启, 点击右侧<span style="color: #409eff">查看详情</span>进行详情在线查看</p>
</div>
</div>
<el-table
v-else
v-loading="loading"
:data="tableData"
border
stripe
size="small"
style="width: 100%"
>
<el-table-column type="index" label="序号" width="80" align="center"></el-table-column>
<el-table-column prop="orderNo" label="订单号" min-width="180" show-overflow-tooltip></el-table-column>
<el-table-column prop="userName" label="用户" width="120"></el-table-column>
<el-table-column prop="competitionName" label="赛事" min-width="200" show-overflow-tooltip></el-table-column>
<el-table-column prop="amount" label="金额" width="120" align="center">
<template slot-scope="scope">
<el-table-column type="index" label="序号" width="60" align="center"></el-table-column>
<el-table-column prop="orderNo" label="订单号" min-width="150" show-overflow-tooltip></el-table-column>
<el-table-column prop="userName" label="用户" width="100"></el-table-column>
<el-table-column prop="competitionName" label="赛事" min-width="180" show-overflow-tooltip></el-table-column>
<el-table-column prop="amount" label="金额" width="100" align="center">
<template #default="scope">
<span class="amount-text">¥{{ scope.row.amount }}</span>
</template>
</el-table-column>
<el-table-column prop="status" label="状态" width="100" align="center">
<template slot-scope="scope">
<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 prop="createTime" label="创建时间" width="180"></el-table-column>
<el-table-column label="操作" width="280" align="center" fixed="right">
<template slot-scope="scope">
<el-button type="primary" size="small" @click="handleView(scope.row)">查看详情</el-button>
<el-button size="small" @click="handleEdit(scope.row)">编辑</el-button>
<el-button size="small" @click="handleDelete(scope.row)">删除</el-button>
<el-table-column prop="createTime" label="创建时间" width="160"></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)" :disabled="!scope.row.canSchedule">编排</el-button>
<el-button type="warning" size="small" @click="handleDispatch(scope.row)" :disabled="!scope.row.canDispatch">调度</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
v-if="tableData.length > 0"
class="pagination"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
@@ -79,14 +73,13 @@
:page-size="pagination.size"
layout="total, sizes, prev, pager, next"
:total="pagination.total"
small
></el-pagination>
</el-card>
</div>
</template>
<script>
import { getOrderList, removeOrder } from '@/api/martial/competition'
export default {
name: 'MartialOrderList',
data() {
@@ -96,6 +89,64 @@ export default {
keyword: '',
status: null
},
// 使用静态数据,方便演示功能
allTableData: [
{
id: 1,
orderNo: 'ORD20251127001',
userName: '张三',
competitionName: '第三十届武术大赛',
amount: 999.00,
status: 1,
createTime: '2025-11-27 10:30:00',
canSchedule: true, // 可以编排
canDispatch: false // 未完成编排,不可调度
},
{
id: 2,
orderNo: 'ORD20251127002',
userName: '李四',
competitionName: '第三十届武术大赛',
amount: 1245.00,
status: 1,
createTime: '2025-11-27 11:00:00',
canSchedule: true,
canDispatch: true // 已完成编排,可以调度
},
{
id: 3,
orderNo: 'ORD20251127003',
userName: '王五',
competitionName: '青少年武术锦标赛',
amount: 1580.00,
status: 1,
createTime: '2025-11-27 14:20:00',
canSchedule: true,
canDispatch: false
},
{
id: 4,
orderNo: 'ORD20251126001',
userName: '赵六',
competitionName: '第三十届武术大赛',
amount: 2300.00,
status: 0,
createTime: '2025-11-26 09:15:00',
canSchedule: false,
canDispatch: false
},
{
id: 5,
orderNo: 'ORD20251126002',
userName: '孙七',
competitionName: '全国武术公开赛',
amount: 1850.00,
status: 1,
createTime: '2025-11-26 16:45:00',
canSchedule: true,
canDispatch: false
}
],
tableData: [],
pagination: {
current: 1,
@@ -105,59 +156,44 @@ export default {
}
},
mounted() {
// 初始化时直接加载数据
this.fetchData()
},
methods: {
fetchData() {
this.loading = true
getOrderList(this.pagination.current, this.pagination.size, this.searchForm)
.then(res => {
if (res.data && res.data.success) {
this.tableData = res.data.data.records || []
this.pagination.total = res.data.data.total || 0
} else {
// 模拟数据(开发阶段)
this.tableData = [
{
id: 1,
orderNo: 'ORD20251127001',
userName: '张三',
competitionName: '第三十届武术大赛',
amount: 999.00,
status: 1,
createTime: '2025-11-27 10:30:00'
},
{
id: 2,
orderNo: 'ORD20251127002',
userName: '李四',
competitionName: '第三十届武术大赛',
amount: 1245.00,
status: 0,
createTime: '2025-11-27 11:00:00'
// 模拟API请求延迟
setTimeout(() => {
// 过滤数据
let filteredData = [...this.allTableData]
// 搜索过滤
if (this.searchForm.keyword) {
const keyword = this.searchForm.keyword.toLowerCase()
filteredData = filteredData.filter(item =>
item.orderNo.toLowerCase().includes(keyword) ||
item.userName.toLowerCase().includes(keyword) ||
item.competitionName.toLowerCase().includes(keyword)
)
}
]
this.pagination.total = 2
// 状态过滤
if (this.searchForm.status !== null && this.searchForm.status !== '') {
filteredData = filteredData.filter(item =>
item.status === parseInt(this.searchForm.status)
)
}
})
.catch(() => {
// 失败时使用模拟数据
this.tableData = [
{
id: 1,
orderNo: 'ORD20251127001',
userName: '张三',
competitionName: '第三十届武术大赛',
amount: 999.00,
status: 1,
createTime: '2025-11-27 10:30:00'
}
]
this.pagination.total = 1
})
.finally(() => {
this.pagination.total = filteredData.length
// 分页处理
const start = (this.pagination.current - 1) * this.pagination.size
const end = start + this.pagination.size
this.tableData = filteredData.slice(start, end)
this.loading = false
})
}, 300)
},
handleSearch() {
this.pagination.current = 1
@@ -171,25 +207,33 @@ export default {
this.pagination.current = current
this.fetchData()
},
handleView(row) {
this.$message.info('查看订单: ' + row.orderNo)
},
handleEdit(row) {
this.$message.info('编辑订单: ' + row.orderNo)
},
handleDelete(row) {
this.$confirm('确定要删除该订单吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
removeOrder(row.id).then(res => {
this.$message.success('删除成功')
this.fetchData()
}).catch(() => {
this.$message.success('删除成功(模拟)')
this.fetchData()
// 查看报名详情
handleRegistrationDetail(row) {
this.$router.push({
path: '/martial/registration/detail',
query: { orderId: row.id }
})
},
// 编排
handleSchedule(row) {
if (!row.canSchedule) {
this.$message.warning('该订单暂不可编排')
return
}
this.$router.push({
path: '/martial/schedule/list',
query: { orderId: row.id }
})
},
// 调度
handleDispatch(row) {
if (!row.canDispatch) {
this.$message.warning('请先完成编排')
return
}
this.$router.push({
path: '/martial/dispatch/list',
query: { orderId: row.id }
})
},
getStatusType(status) {
@@ -216,51 +260,75 @@ export default {
<style lang="scss" scoped>
.martial-order-container {
padding: 20px;
min-height: 100%;
padding: 15px;
background: #fff;
.page-header {
margin-bottom: 20px;
margin-bottom: 15px;
.page-title {
margin: 0;
font-size: 20px;
font-size: 18px;
font-weight: 600;
color: #2c3e50;
}
}
.search-form {
margin-bottom: 20px;
}
.empty-data {
min-height: 400px;
.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;
background: #fafafa;
border: 1px solid #ebeef5;
border-radius: 4px;
.empty-content {
text-align: center;
.empty-text {
font-size: 14px;
color: #909399;
line-height: 1.8;
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: 15px;
font-size: 14px;
}
.pagination {
margin-top: 20px;
margin-top: 15px;
text-align: right;
}
}

View File

@@ -1,17 +1,15 @@
<template>
<div class="martial-registration-detail">
<el-card shadow="hover">
<div class="page-header">
<el-button icon="el-icon-back" @click="goBack" size="small">返回</el-button>
<el-button icon="el-icon-back" size="small" @click="goBack">返回</el-button>
<h2 class="page-title">报名详情</h2>
</div>
<h2 class="page-title">报名详情</h2>
<div class="competition-info">
<h3 class="section-title">赛事合体</h3>
<h3 class="section-title">赛事名称</h3>
<p class="detail-id">ID: {{ competitionInfo.id }}</p>
<el-row :gutter="20" class="info-row">
<el-row :gutter="15" class="info-row">
<el-col :span="6">
<div class="info-item">
<label>主办单位</label>
@@ -38,7 +36,7 @@
</el-col>
</el-row>
<el-row :gutter="20" class="info-row">
<el-row :gutter="15" class="info-row">
<el-col :span="6">
<div class="info-item">
<label>比赛时间</label>
@@ -71,18 +69,21 @@
<div class="tabs-section">
<div class="tab-buttons">
<el-button
size="small"
:type="activeTab === 'participants' ? 'primary' : ''"
@click="activeTab = 'participants'"
>
参赛人数统计
</el-button>
<el-button
size="small"
:type="activeTab === 'projectTime' ? 'primary' : ''"
@click="activeTab = 'projectTime'"
>
项目时间统计
</el-button>
<el-button
size="small"
:type="activeTab === 'amountStats' ? 'primary' : ''"
@click="activeTab = 'amountStats'"
>
@@ -90,42 +91,61 @@
</el-button>
</div>
<!-- 参赛人数统计Tab -->
<div v-show="activeTab === 'participants'" class="tab-content">
<el-table :data="participantsData" border stripe>
<el-table-column type="index" label="序号" width="80" align="center"></el-table-column>
<el-table-column prop="schoolUnit" label="单位" min-width="200"></el-table-column>
<el-table-column prop="category" label="类别" width="100"></el-table-column>
<el-table-column prop="individual" label="2队" width="80" align="center"></el-table-column>
<el-table-column prop="dual" label="2组" width="80" align="center"></el-table-column>
<el-table-column prop="team1101" label="1101" width="80" align="center"></el-table-column>
<el-table-column prop="workers" label="工作人员" width="100" align="center"></el-table-column>
<el-table-column prop="female" label="女性组别" width="100" align="center"></el-table-column>
<el-table-column prop="total" label="合计" width="80" align="center">
<template slot-scope="scope">
<span class="total-num">{{ scope.row.total }}</span>
<div class="tab-hint">
在营业分组: 本操作 提现栏标签 提名栏标签 比赛栏标签
</div>
<el-table :data="participantsData" border stripe size="small">
<el-table-column type="index" label="序号" width="60" align="center"></el-table-column>
<el-table-column prop="schoolUnit" label="单位" min-width="180">
<template #default="scope">
<span>{{ scope.row.schoolUnit }}</span>
<div v-if="scope.row.hint" class="row-hint">{{ scope.row.hint }}</div>
</template>
</el-table-column>
<el-table-column prop="category" label="类别" width="80"></el-table-column>
<el-table-column prop="individual" label="2队" width="60" align="center"></el-table-column>
<el-table-column prop="dual" label="2组" width="60" align="center"></el-table-column>
<el-table-column prop="team1101" label="1101" width="60" align="center"></el-table-column>
<el-table-column prop="workers" label="工作人员" width="90" align="center"></el-table-column>
<el-table-column prop="female" label="女性组别" width="90" align="center"></el-table-column>
<el-table-column prop="total" label="合计" width="60" align="center">
<template #default="scope">
<span class="total-num">+{{ scope.row.total }}</span>
</template>
</el-table-column>
</el-table>
</div>
<!-- 项目时间统计Tab -->
<div v-show="activeTab === 'projectTime'" class="tab-content">
<el-table :data="projectTimeData" border stripe>
<el-table-column type="index" label="序号" width="80" align="center"></el-table-column>
<el-table-column prop="projectName" label="项目" min-width="200"></el-table-column>
<el-table-column prop="participantCategory" label="参人/单位" width="150"></el-table-column>
<el-table-column prop="teamCount" label="队伍" width="100" align="center"></el-table-column>
<el-table-column prop="singleTeamPeople" label="单位型号" width="120" align="center"></el-table-column>
<el-table-column prop="estimatedDuration" label="剩下显时(公共)" width="180" align="center"></el-table-column>
<div class="tab-hint">
项目时间统计
</div>
<el-table :data="projectTimeData" border stripe size="small">
<el-table-column type="index" label="序号" width="60" align="center"></el-table-column>
<el-table-column prop="projectName" label="项目" min-width="150">
<template #default="scope">
<span>{{ scope.row.projectName }}</span>
<div v-if="scope.row.hint" class="row-hint">{{ scope.row.hint }}</div>
</template>
</el-table-column>
<el-table-column prop="participantCategory" label="参人/单位" width="100"></el-table-column>
<el-table-column prop="teamCount" label="队伍" width="80" align="center"></el-table-column>
<el-table-column prop="singleTeamPeople" label="单位型号人数" width="120" align="center"></el-table-column>
<el-table-column prop="estimatedDuration" label="剩下显时(公共)" width="150" align="center"></el-table-column>
</el-table>
</div>
<!-- 全额统计Tab -->
<div v-show="activeTab === 'amountStats'" class="tab-content">
<el-table :data="amountStatsData" border stripe>
<el-table-column type="index" label="序号" width="80" align="center"></el-table-column>
<el-table-column prop="schoolUnit" label="学校/单位" min-width="300"></el-table-column>
<el-table-column prop="projectCount" label="项目" width="150" align="center"></el-table-column>
<el-table-column prop="totalAmount" label="全额" width="200" align="center">
<template slot-scope="scope">
<el-table :data="amountStatsData" border stripe size="small">
<el-table-column type="index" label="序号" width="60" align="center"></el-table-column>
<el-table-column prop="schoolUnit" label="学校/单位" min-width="200"></el-table-column>
<el-table-column prop="projectCount" label="项目" width="120" align="center"></el-table-column>
<el-table-column prop="totalAmount" label="全额" width="150" align="center">
<template #default="scope">
<span class="amount">¥{{ scope.row.totalAmount }}</span>
</template>
</el-table-column>
@@ -134,9 +154,8 @@
</div>
<div class="footer-actions">
<el-button @click="handleExport">导出</el-button>
<el-button size="small" @click="handleExport">导出</el-button>
</div>
</el-card>
</div>
</template>
@@ -145,6 +164,7 @@ export default {
name: 'MartialRegistrationDetail',
data() {
return {
orderId: null,
activeTab: 'participants',
competitionInfo: {
id: '123456889900',
@@ -161,6 +181,7 @@ export default {
{
schoolUnit: '清河小学',
category: '集体',
hint: '剩余功能在提现上,显示栏标签',
individual: 1,
dual: 1,
team1101: 1,
@@ -177,15 +198,50 @@ export default {
workers: 0,
female: 0,
total: 0
},
{
schoolUnit: '少林寺武术学校',
category: '单人',
individual: 3,
dual: 2,
team1101: 2,
workers: 6,
female: 8,
total: 21
},
{
schoolUnit: '访河社区',
category: '集体',
individual: 2,
dual: 1,
team1101: 1,
workers: 3,
female: 4,
total: 11
}
],
projectTimeData: [
{
projectName: '小学组小组赛男女类',
hint: '剩余功能在位置提现上,显示出运动类别名称的位置',
participantCategory: '集体',
teamCount: 1,
singleTeamPeople: 10,
estimatedDuration: 4
},
{
projectName: '中学组个人赛',
participantCategory: '单人',
teamCount: 3,
singleTeamPeople: 1,
estimatedDuration: 2
},
{
projectName: '少年组对抗赛',
participantCategory: '双人',
teamCount: 2,
singleTeamPeople: 2,
estimatedDuration: 3
}
],
amountStatsData: [
@@ -198,13 +254,27 @@ export default {
schoolUnit: '方山镇小学校',
projectCount: 0,
totalAmount: 0
},
{
schoolUnit: '少林寺武术学校',
projectCount: 8,
totalAmount: 15600
},
{
schoolUnit: '访河社区',
projectCount: 4,
totalAmount: 7200
}
]
}
},
mounted() {
this.orderId = this.$route.query.orderId
// 使用静态数据不调用API
},
methods: {
goBack() {
this.$router.go(-1)
this.$router.push('/martial/order/list')
},
handleExport() {
this.$message.success('导出功能开发中')
@@ -215,73 +285,103 @@ export default {
<style lang="scss" scoped>
.martial-registration-detail {
padding: 20px;
padding: 15px;
background: #fff;
.page-header {
display: flex;
align-items: center;
margin-bottom: 15px;
}
gap: 10px;
.page-title {
margin: 0 0 25px 0;
font-size: 20px;
margin: 0;
font-size: 18px;
font-weight: 600;
color: #2c3e50;
}
}
.competition-info {
margin-bottom: 30px;
padding-bottom: 30px;
border-bottom: 1px solid #ebeef5;
margin-bottom: 15px;
padding: 15px;
background: #fff;
border: 1px solid #dcdfe6;
.section-title {
margin: 0 0 10px 0;
font-size: 16px;
margin-bottom: 8px;
font-size: 15px;
font-weight: 600;
color: #2c3e50;
padding-left: 8px;
border-left: 3px solid #409eff;
}
.detail-id {
margin: 0 0 20px 0;
margin-bottom: 15px;
padding-left: 8px;
color: #909399;
font-size: 13px;
font-size: 11px;
}
.info-row {
margin-bottom: 15px;
margin-bottom: 12px;
}
.info-item {
padding: 8px;
background: #f5f7fa;
label {
display: block;
margin-bottom: 8px;
font-size: 13px;
margin-bottom: 6px;
font-size: 11px;
color: #909399;
}
.info-value {
font-size: 14px;
color: #2c3e50;
font-size: 12px;
color: #606266;
&.amount {
color: #e6a23c;
font-weight: 600;
font-size: 16px;
}
}
}
}
.tabs-section {
margin-bottom: 30px;
margin-bottom: 15px;
.tab-buttons {
margin-bottom: 20px;
display: flex;
gap: 10px;
margin-bottom: 15px;
padding: 12px;
background: #fff;
border: 1px solid #dcdfe6;
}
.tab-content {
min-height: 300px;
padding: 15px;
background: #fff;
border: 1px solid #dcdfe6;
.tab-hint {
padding: 8px 12px;
margin-bottom: 12px;
background: #fff3e0;
border-left: 3px solid #ff9800;
color: #e65100;
font-size: 11px;
}
.row-hint {
margin-top: 4px;
padding: 2px 5px;
color: #f50057;
font-size: 11px;
background: rgba(245, 0, 87, 0.05);
display: inline-block;
}
}
}
@@ -293,7 +393,9 @@ export default {
.footer-actions {
text-align: center;
padding-top: 20px;
padding: 15px;
background: #fff;
border: 1px solid #dcdfe6;
}
}
</style>

View File

@@ -1,21 +1,21 @@
<template>
<div class="martial-schedule-container">
<el-card shadow="hover">
<div class="page-header">
<el-button icon="el-icon-back" @click="goBack" size="small">返回</el-button>
</div>
<el-button icon="el-icon-back" size="small" @click="goBack">返回</el-button>
<h2 class="page-title">编排</h2>
</div>
<div class="tabs-section">
<div class="tab-buttons">
<el-button
size="small"
:type="activeTab === 'competition' ? 'primary' : ''"
@click="activeTab = 'competition'"
>
竞赛分组
</el-button>
<el-button
size="small"
:type="activeTab === 'venue' ? 'primary' : ''"
@click="activeTab = 'venue'"
>
@@ -46,24 +46,44 @@
<span class="group-meta">{{ group.code }}</span>
</div>
<div class="group-actions">
<el-button
size="small"
type="primary"
@click="handleVenueSelect(index, 1)"
>
一号场地
<el-dropdown trigger="click" @command="(cmd) => handleVenueCommand(cmd, index)">
<el-button size="small" type="primary">
{{ group.selectedVenue || '一号场地' }}<i class="el-icon-arrow-down el-icon--right"></i>
</el-button>
<el-button size="small" type="danger">菜单</el-button>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="venue1">一号场地</el-dropdown-item>
<el-dropdown-item command="venue2">二号场地</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<el-button size="small" type="danger">赛程</el-button>
</div>
</div>
<el-table :data="group.items" border stripe size="small">
<el-table-column label="序号" type="index" width="80" align="center"></el-table-column>
<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 label="操作" width="120" align="center">
<template>
<el-button type="text" icon="el-icon-upload2" size="small"></el-button>
<el-button type="text" icon="el-icon-download" size="small"></el-button>
<el-table-column label="操作" width="100" align="center">
<template #default="scope">
<el-button
type="text"
size="small"
@click="handleMoveUp(index, scope.$index)"
:disabled="scope.$index === 0 || isScheduleCompleted"
title="上移"
class="move-btn"
>
<img src="/img/图标 3@3x.png" class="move-icon" alt="上移" />
</el-button>
<el-button
type="text"
size="small"
@click="handleMoveDown(index, 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>
</template>
</el-table-column>
</el-table>
@@ -84,27 +104,35 @@
</el-button>
</div>
<el-table :data="venueData" border stripe>
<el-table-column label="序号" type="index" width="80" align="center"></el-table-column>
<el-table-column prop="project" label="项目" min-width="200"></el-table-column>
<el-table-column prop="participant" label="参人/单位" width="150"></el-table-column>
<el-table-column prop="team" label="队伍" width="100" align="center"></el-table-column>
<el-table-column prop="number" label="编号" width="100" align="center"></el-table-column>
<el-table-column prop="duration" label="合计时间" width="120" align="center"></el-table-column>
<el-table-column prop="status" label="状态" width="120" align="center">
<template slot-scope="scope">
<span v-if="scope.row.status === 'active'" class="status-active">{{ scope.row.status }}</span>
<el-table :data="venueData" border stripe size="small">
<el-table-column label="序号" type="index" width="60" align="center"></el-table-column>
<el-table-column prop="project" label="项目" min-width="180">
<template #default="scope">
<span>{{ scope.row.project }}</span>
<div v-if="scope.row.hint" class="row-hint">{{ scope.row.hint }}</div>
</template>
</el-table-column>
<el-table-column prop="participant" label="参人/单位" width="120"></el-table-column>
<el-table-column prop="team" label="队伍" width="80" align="center"></el-table-column>
<el-table-column prop="number" label="编号" width="80" align="center"></el-table-column>
<el-table-column prop="duration" label="合计时间" width="100" align="center"></el-table-column>
<el-table-column prop="status" label="状态" width="100" align="center">
<template #default="scope">
<span v-if="scope.row.status" class="status-active">{{ scope.row.status }}</span>
</template>
</el-table-column>
</el-table>
<div class="venue-footer-hints">
<p class="footer-hint-text">完成编排前的现阶段, 点击放开次文本; 完成编排前在可导列</p>
</div>
</div>
</div>
<div class="footer-actions">
<el-button @click="handleExport">导出</el-button>
<el-button type="primary" @click="handleConfirm">完成编排</el-button>
<el-button size="small" @click="handleExport" v-if="isScheduleCompleted">导出</el-button>
<el-button size="small" type="primary" @click="handleConfirm" v-else>完成编排</el-button>
</div>
</el-card>
<!-- 确认对话框 -->
<el-dialog
@@ -129,9 +157,11 @@ export default {
name: 'MartialScheduleList',
data() {
return {
orderId: null,
activeTab: 'competition',
selectedTime: 0,
confirmDialogVisible: false,
isScheduleCompleted: false, // 是否已完成编排
timeSlots: [
'2025年11月6日上午8:30',
'2025年11月6日下午13:00',
@@ -144,34 +174,95 @@ export default {
type: '集体',
count: '2队',
code: '1101',
items: [{ schoolUnit: '清河小学' }, { schoolUnit: '访河社区' }]
selectedVenue: '一号场地',
items: [
{ schoolUnit: '清河小学' },
{ schoolUnit: '访河社区' }
]
},
{
title: '1. 小学组小组赛男女类',
type: '单人',
count: '3队',
code: '1组',
items: [{ schoolUnit: '少林寺武校' }, { schoolUnit: '访河社区' }, { schoolUnit: '少林寺武校' }]
selectedVenue: '二号场地',
items: [
{ schoolUnit: '少林寺武校' },
{ schoolUnit: '访河社区' },
{ schoolUnit: '少林寺武校' }
]
},
{
title: '2. 中学组决赛',
type: '集体',
count: '4队',
code: '2101',
selectedVenue: '一号场地',
items: [
{ schoolUnit: '成都体育学院' },
{ schoolUnit: '武侯实验中学' },
{ schoolUnit: '石室中学' },
{ schoolUnit: '七中育才' }
]
}
],
venueData: [
{
project: '小学组小组赛男女类',
hint: '剩余功能在位置换现上,显示出已比赛名称的位置',
participant: '集体',
team: 1,
number: 1,
duration: 2,
status: 'active'
status: '*101'
},
{
project: '中学组决赛',
participant: '单人',
team: 2,
number: 5,
duration: 3,
status: '*102'
},
{
project: '少年组对抗赛',
participant: '双人',
team: 1,
number: 3,
duration: 2,
status: ''
}
]
}
},
mounted() {
this.orderId = this.$route.query.orderId
// 使用静态数据不调用API
},
methods: {
goBack() {
this.$router.go(-1)
this.$router.push('/martial/order/list')
},
handleVenueSelect(index, venueType) {
this.$message.success(`已选择${venueType === 1 ? '一号' : '二号'}场地`)
handleVenueCommand(command, groupIndex) {
const venueName = command === 'venue1' ? '一号场地' : '二号场地'
this.competitionGroups[groupIndex].selectedVenue = venueName
this.$message.success(`已选择${venueName}`)
},
handleMoveUp(groupIndex, itemIndex) {
if (itemIndex === 0 || this.isScheduleCompleted) return
const group = this.competitionGroups[groupIndex]
const temp = group.items[itemIndex]
group.items.splice(itemIndex, 1)
group.items.splice(itemIndex - 1, 0, temp)
this.$message.success('上移成功')
},
handleMoveDown(groupIndex, itemIndex) {
const group = this.competitionGroups[groupIndex]
if (itemIndex === group.items.length - 1 || this.isScheduleCompleted) return
const temp = group.items[itemIndex]
group.items.splice(itemIndex, 1)
group.items.splice(itemIndex + 1, 0, temp)
this.$message.success('下移成功')
},
handleExport() {
this.$message.success('导出功能开发中')
@@ -180,9 +271,10 @@ export default {
this.confirmDialogVisible = true
},
confirmComplete() {
// 确认完成编排
this.isScheduleCompleted = true
this.confirmDialogVisible = false
this.$message.success('编排完成')
this.goBack()
this.$message.success('编排完成,现在可以进行调度操作')
}
}
}
@@ -190,72 +282,132 @@ export default {
<style lang="scss" scoped>
.martial-schedule-container {
padding: 20px;
padding: 15px;
background: #fff;
.page-header {
display: flex;
align-items: center;
margin-bottom: 15px;
}
gap: 10px;
.page-title {
margin: 0 0 25px 0;
font-size: 20px;
margin: 0;
font-size: 18px;
font-weight: 600;
color: #2c3e50;
}
}
.tabs-section {
margin-bottom: 30px;
margin-bottom: 15px;
.tab-buttons {
margin-bottom: 20px;
display: flex;
gap: 10px;
margin-bottom: 15px;
padding: 12px;
background: #fff;
border: 1px solid #dcdfe6;
}
.tab-content {
padding: 15px;
background: #fff;
border: 1px solid #dcdfe6;
.time-selector {
margin-bottom: 20px;
display: flex;
gap: 10px;
flex-wrap: wrap;
margin-bottom: 15px;
padding: 8px;
background: #f5f7fa;
}
.competition-group {
margin-bottom: 25px;
margin-bottom: 15px;
.group-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px 16px;
padding: 12px;
background: #f5f7fa;
border: 1px solid #e4e7ed;
border: 1px solid #dcdfe6;
border-bottom: none;
.group-info {
display: flex;
align-items: center;
gap: 15px;
gap: 12px;
.group-title {
font-weight: 600;
color: #2c3e50;
font-size: 14px;
color: #303133;
font-size: 13px;
}
.group-meta {
color: #909399;
font-size: 13px;
font-size: 12px;
}
}
.group-hints {
flex: 1;
text-align: center;
.hint-text {
color: #f50057;
font-size: 11px;
&.secondary {
color: #ff4081;
}
}
}
.group-actions {
display: flex;
gap: 10px;
gap: 8px;
}
}
}
.group-footer-hints {
margin-top: 15px;
padding: 8px 12px;
background: #fff3e0;
border-left: 3px solid #ff9800;
.footer-hint-text {
margin: 4px 0;
color: #f50057;
font-size: 11px;
&.secondary {
color: #ff4081;
}
}
}
.row-hint {
margin-top: 4px;
padding: 2px 5px;
color: #f50057;
font-size: 11px;
background: rgba(245, 0, 87, 0.05);
display: inline-block;
}
.venue-footer-hints {
margin-top: 15px;
padding: 8px 12px;
background: #fff3e0;
border-left: 3px solid #ff9800;
.footer-hint-text {
margin: 4px 0;
color: #f50057;
font-size: 11px;
}
}
.status-active {
color: #409eff;
font-weight: 600;
@@ -265,10 +417,25 @@ export default {
.footer-actions {
text-align: center;
padding-top: 20px;
display: flex;
justify-content: center;
gap: 15px;
padding: 15px;
background: #fff;
border: 1px solid #dcdfe6;
}
.move-btn {
padding: 4px;
&:disabled {
opacity: 0.4;
cursor: not-allowed;
}
}
.move-icon {
width: 20px;
height: 20px;
display: inline-block;
vertical-align: middle;
}
}
</style>