This commit is contained in:
2025-12-12 01:44:41 +08:00
parent 21abcaff53
commit 2f1d732a36
46 changed files with 7756 additions and 484 deletions

View File

@@ -1,28 +1,134 @@
<template>
<view class="event-rules-page">
<!-- 章节列表 -->
<view class="rules-list">
<view class="rules-item" v-for="(item, index) in rulesList" :key="index" @click="toggleSection(index)">
<view class="rules-header">
<view class="chapter-number">{{ item.chapter }}</view>
<view class="chapter-title">{{ item.title }}</view>
<view class="arrow" :class="{ expanded: item.expanded }"></view>
</view>
<view class="rules-content" v-if="item.expanded">
<view class="content-item" v-for="(content, idx) in item.contents" :key="idx">
<text class="content-text">{{ content }}</text>
<!-- 附件下载区 -->
<view class="attachments-section" v-if="attachments.length > 0">
<view class="section-title">
<text class="title-icon">📎</text>
<text class="title-text">规程附件</text>
</view>
<view class="attachments-list">
<view class="attachment-item" v-for="(file, index) in attachments" :key="index" @click="downloadFile(file)">
<view class="file-info">
<text class="file-icon">{{ getFileIcon(file.fileType) }}</text>
<view class="file-details">
<text class="file-name">{{ file.fileName }}</text>
<text class="file-size">{{ file.fileSize }}</text>
</view>
</view>
<view class="download-btn">
<text class="download-icon"></text>
</view>
</view>
</view>
</view>
<!-- 规程内容区 -->
<view class="content-section" v-if="rulesList.length > 0">
<view class="section-title">
<text class="title-icon">📄</text>
<text class="title-text">规程内容</text>
</view>
<view class="rules-list">
<view class="rules-item" v-for="(item, index) in rulesList" :key="index" @click="toggleSection(index)">
<view class="rules-header">
<view class="chapter-number">{{ item.chapter }}</view>
<view class="chapter-title">{{ item.title }}</view>
<view class="arrow" :class="{ expanded: item.expanded }"></view>
</view>
<view class="rules-content" v-if="item.expanded">
<view class="content-item" v-for="(content, idx) in item.contents" :key="idx">
<text class="content-text">{{ content }}</text>
</view>
</view>
</view>
</view>
</view>
<!-- 空状态 -->
<view class="empty-state" v-if="attachments.length === 0 && rulesList.length === 0">
<text class="empty-icon">📋</text>
<text class="empty-text">暂无规程信息</text>
</view>
</view>
</template>
<script>
import competitionAPI from '@/api/competition.js'
export default {
data() {
return {
rulesList: [
eventId: '',
// 附件列表
attachments: [],
// 规程章节列表
rulesList: []
};
},
onLoad(options) {
if (options.eventId) {
this.eventId = options.eventId
this.loadRulesData()
}
},
methods: {
/**
* 加载规程数据
*/
async loadRulesData() {
try {
// 调用API获取规程数据
const res = await competitionAPI.getCompetitionRules(this.eventId)
// 处理附件数据
if (res.attachments && res.attachments.length > 0) {
this.attachments = res.attachments.map(file => ({
id: file.id,
fileName: file.name || file.fileName,
fileUrl: file.url || file.fileUrl,
fileSize: this.formatFileSize(file.size || file.fileSize),
fileType: this.getFileType(file.name || file.fileName)
}))
}
// 处理规程内容数据
if (res.chapters && res.chapters.length > 0) {
this.rulesList = res.chapters.map(chapter => ({
chapter: chapter.chapterNumber || chapter.number,
title: chapter.title || chapter.name,
expanded: false,
contents: chapter.contents || chapter.items || []
}))
}
} catch (err) {
console.error('加载规程数据失败:', err)
// 如果API失败使用模拟数据
this.loadMockData()
}
},
/**
* 加载模拟数据(用于开发测试)
*/
loadMockData() {
this.attachments = [
{
id: '1',
fileName: '2025年郑州武术大赛规程.pdf',
fileUrl: 'https://example.com/rules.pdf',
fileSize: '2.5 MB',
fileType: 'pdf'
},
{
id: '2',
fileName: '参赛报名表.docx',
fileUrl: 'https://example.com/form.docx',
fileSize: '156 KB',
fileType: 'docx'
}
]
this.rulesList = [
{
chapter: '第一章',
title: '总则',
@@ -64,11 +170,102 @@ export default {
]
}
]
};
},
methods: {
},
/**
* 切换章节展开/收起
*/
toggleSection(index) {
this.rulesList[index].expanded = !this.rulesList[index].expanded;
this.rulesList[index].expanded = !this.rulesList[index].expanded
},
/**
* 下载文件
*/
downloadFile(file) {
uni.showLoading({
title: '准备下载'
})
// 下载文件
uni.downloadFile({
url: file.fileUrl,
success: (res) => {
if (res.statusCode === 200) {
// 保存文件到本地
const filePath = res.tempFilePath
// 打开文档
uni.openDocument({
filePath: filePath,
fileType: file.fileType,
success: () => {
uni.hideLoading()
uni.showToast({
title: '打开成功',
icon: 'success'
})
},
fail: (err) => {
uni.hideLoading()
console.error('打开文件失败:', err)
uni.showToast({
title: '打开失败',
icon: 'none'
})
}
})
}
},
fail: (err) => {
uni.hideLoading()
console.error('下载失败:', err)
uni.showToast({
title: '下载失败',
icon: 'none'
})
}
})
},
/**
* 获取文件类型
*/
getFileType(fileName) {
const ext = fileName.split('.').pop().toLowerCase()
return ext
},
/**
* 获取文件图标
*/
getFileIcon(fileType) {
const iconMap = {
'pdf': '📕',
'doc': '📘',
'docx': '📘',
'xls': '📗',
'xlsx': '📗',
'ppt': '📙',
'pptx': '📙',
'txt': '📄',
'zip': '📦',
'rar': '📦'
}
return iconMap[fileType] || '📄'
},
/**
* 格式化文件大小
*/
formatFileSize(bytes) {
if (!bytes || bytes === 0) return '0 B'
if (typeof bytes === 'string') return bytes
const k = 1024
const sizes = ['B', 'KB', 'MB', 'GB']
const i = Math.floor(Math.log(bytes) / Math.log(k))
return (bytes / Math.pow(k, i)).toFixed(2) + ' ' + sizes[i]
}
}
};
@@ -81,6 +278,108 @@ export default {
padding: 20rpx 30rpx;
}
// 区块标题
.section-title {
display: flex;
align-items: center;
gap: 10rpx;
margin-bottom: 20rpx;
padding: 0 10rpx;
}
.title-icon {
font-size: 32rpx;
}
.title-text {
font-size: 30rpx;
font-weight: bold;
color: #333333;
}
// 附件下载区
.attachments-section {
margin-bottom: 30rpx;
}
.attachments-list {
display: flex;
flex-direction: column;
gap: 15rpx;
}
.attachment-item {
background-color: #fff;
border-radius: 16rpx;
padding: 25rpx 30rpx;
display: flex;
align-items: center;
justify-content: space-between;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05);
transition: all 0.3s;
&:active {
background-color: #f8f8f8;
transform: scale(0.98);
}
}
.file-info {
display: flex;
align-items: center;
gap: 20rpx;
flex: 1;
min-width: 0;
}
.file-icon {
font-size: 48rpx;
flex-shrink: 0;
}
.file-details {
display: flex;
flex-direction: column;
gap: 8rpx;
flex: 1;
min-width: 0;
}
.file-name {
font-size: 28rpx;
color: #333333;
font-weight: 500;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.file-size {
font-size: 24rpx;
color: #999999;
}
.download-btn {
width: 60rpx;
height: 60rpx;
background-color: #C93639;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.download-icon {
font-size: 32rpx;
color: #fff;
}
// 规程内容区
.content-section {
margin-bottom: 30rpx;
}
.rules-list {
display: flex;
flex-direction: column;
@@ -91,6 +390,7 @@ export default {
background-color: #fff;
border-radius: 16rpx;
overflow: hidden;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05);
}
.rules-header {
@@ -98,6 +398,11 @@ export default {
align-items: center;
padding: 30rpx;
gap: 15rpx;
transition: background-color 0.3s;
&:active {
background-color: #f8f8f8;
}
}
.chapter-number {
@@ -127,6 +432,18 @@ export default {
.rules-content {
padding: 0 30rpx 30rpx;
border-top: 1rpx solid #f5f5f5;
animation: slideDown 0.3s ease;
}
@keyframes slideDown {
from {
opacity: 0;
transform: translateY(-10rpx);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.content-item {
@@ -151,4 +468,24 @@ export default {
color: #666666;
line-height: 1.8;
}
// 空状态
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 120rpx 0;
gap: 20rpx;
}
.empty-icon {
font-size: 120rpx;
opacity: 0.3;
}
.empty-text {
font-size: 28rpx;
color: #999999;
}
</style>