929 lines
22 KiB
Plaintext
929 lines
22 KiB
Plaintext
<template>
|
||
<scroll-view direction="vertical" class="project-detail" :class="{ 'small-screen': !isLargeScreen }" :scroll-y="true" :enable-back-to-top="true">
|
||
<!-- Header Card -->
|
||
<view class="header-card">
|
||
<view class="project-header">
|
||
<text class="project-title">{{ getProjectDisplayNameWrapper(project) }}</text>
|
||
<view class="status-badge" :class="`status-${project.getString('status') ?? 'active'}`">
|
||
<text class="status-text">{{ formatProjectStatusLocal(project.getString('status') ?? 'active') }}</text>
|
||
</view>
|
||
</view>
|
||
<text class="project-description">{{ getProjectDescriptionWrapper(project) }}</text>
|
||
<view class="project-meta">
|
||
<view class="meta-row">
|
||
<view class="meta-item">
|
||
<text class="meta-icon"></text>
|
||
<text class="meta-text">{{ getProjectCategoryWrapper(project) }}</text>
|
||
</view>
|
||
<view class="meta-item">
|
||
<text class="meta-icon">⭐</text>
|
||
<text class="meta-text">{{ formatDifficultyWrapper(getProjectDifficultyWrapper(project)) }}</text>
|
||
</view>
|
||
</view>
|
||
<view class="meta-row">
|
||
<view class="meta-item">
|
||
<text class="meta-icon"></text>
|
||
<text class="meta-text">{{ getAssignmentCount() }}个作业</text>
|
||
</view>
|
||
<view class="meta-item">
|
||
<text class="meta-icon"></text>
|
||
<text class="meta-text">{{ getRecordCount() }}条记录</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- Statistics Card -->
|
||
<view class="stats-card">
|
||
<view class="card-header">
|
||
<text class="card-title">统计概览</text>
|
||
</view>
|
||
<view class="stats-grid">
|
||
<view class="stat-item">
|
||
<text class="stat-value">{{ getTotalStudents() }}</text>
|
||
<text class="stat-label">参与学生</text>
|
||
<text class="stat-icon"></text>
|
||
</view>
|
||
<view class="stat-item">
|
||
<text class="stat-value">{{ getAverageScore() }}</text>
|
||
<text class="stat-label">平均分数</text>
|
||
<text class="stat-icon"></text>
|
||
</view>
|
||
<view class="stat-item">
|
||
<text class="stat-value">{{ getCompletionRate() }}%</text>
|
||
<text class="stat-label">完成率</text>
|
||
<text class="stat-icon">✅</text>
|
||
</view>
|
||
<view class="stat-item">
|
||
<text class="stat-value">{{ getImprovementRate() }}%</text>
|
||
<text class="stat-label">进步率</text>
|
||
<text class="stat-icon"></text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- Training Requirements Card -->
|
||
<view class="requirements-card">
|
||
<view class="card-header">
|
||
<text class="card-title">训练要求</text>
|
||
</view>
|
||
<view class="requirements-content">
|
||
<view class="requirement-section">
|
||
<text class="section-title">基础要求</text>
|
||
<view class="requirement-list"> <view
|
||
class="requirement-item"
|
||
v-for="(req, index) in getBasicRequirements()"
|
||
:key="'req-' + index"
|
||
>
|
||
<text class="requirement-icon">{{ getRequirementIcon(req) }}</text>
|
||
<text class="requirement-text">{{ getRequirementText(req) }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<view class="requirement-section">
|
||
<text class="section-title">评分标准</text>
|
||
<view class="scoring-table"> <view
|
||
class="scoring-row"
|
||
v-for="(criteria, index) in getScoringCriteria()"
|
||
:key="'criteria-' + index"
|
||
>
|
||
<view class="score-range">{{ getCriteriaRange(criteria) }}</view>
|
||
<view class="score-desc">{{ getCriteriaDescription(criteria) }}</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- Recent Assignments Card -->
|
||
<view class="assignments-card">
|
||
<view class="card-header">
|
||
<text class="card-title">近期作业</text>
|
||
<text class="view-all-btn" @click="viewAllAssignments">查看全部</text>
|
||
</view>
|
||
<view class="assignments-list"> <view
|
||
class="assignment-item"
|
||
v-for="(assignment, index) in getRecentAssignments()"
|
||
:key="'assignment-' + index"
|
||
@click="viewAssignmentDetail(assignment)"
|
||
>
|
||
<view class="assignment-content">
|
||
<text class="assignment-title">{{ getAssignmentDisplayNameWrapper(assignment) }}</text>
|
||
<text class="assignment-date">{{ formatDateWrapper(getAssignmentCreatedAtLocal(assignment)) }}</text>
|
||
</view>
|
||
<view class="assignment-stats">
|
||
<text class="participants">{{ getAssignmentParticipants(assignment) }}人参与</text>
|
||
<view class="status-dot" :class="`status-${getAssignmentStatusWrapper(assignment)}`"></view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- Performance Trends Card -->
|
||
<view class="trends-card">
|
||
<view class="card-header">
|
||
<text class="card-title">成绩趋势</text>
|
||
</view>
|
||
<view class="trends-content">
|
||
<view class="chart-placeholder">
|
||
<text class="chart-text">成绩趋势图</text>
|
||
<text class="chart-desc">显示最近30天的平均成绩变化</text>
|
||
</view>
|
||
<view class="trend-summary">
|
||
<view class="trend-item">
|
||
<text class="trend-label">本周平均:</text>
|
||
<text class="trend-value">{{ getWeeklyAverage() }}分</text>
|
||
<text class="trend-change positive">+{{ getWeeklyChange() }}%</text>
|
||
</view>
|
||
<view class="trend-item">
|
||
<text class="trend-label">本月平均:</text>
|
||
<text class="trend-value">{{ getMonthlyAverage() }}分</text>
|
||
<text class="trend-change negative">{{ getMonthlyChange() }}%</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- Action Buttons -->
|
||
<view class="action-buttons">
|
||
<button class="action-btn secondary-btn" @click="editProject">
|
||
编辑项目
|
||
</button>
|
||
<button class="action-btn primary-btn" @click="createAssignment">
|
||
创建作业
|
||
</button>
|
||
</view>
|
||
</scroll-view>
|
||
</template>
|
||
|
||
<script setup lang="uts">
|
||
import { getProjectId, getProjectDisplayName, getProjectDescription,
|
||
getProjectCategory, getProjectDifficulty, getAssignmentDisplayName,
|
||
getAssignmentStatus, getAssignmentId, formatDifficulty,
|
||
formatDate, formatProjectStatus, getAssignmentCreatedAt } from '../types.uts'
|
||
|
||
// Reactive data
|
||
const project = ref<UTSJSONObject>({})
|
||
const projectId = ref('')
|
||
const assignments = ref<Array<UTSJSONObject>>([])
|
||
const statistics = ref<UTSJSONObject>({})
|
||
const loading = ref(true) // Responsive state - using onResize for dynamic updates
|
||
const screenWidth = ref<number>(uni.getSystemInfoSync().windowWidth)
|
||
|
||
// Computed properties for responsive design
|
||
const isLargeScreen = computed(() : boolean => {
|
||
return screenWidth.value >= 768
|
||
})
|
||
|
||
// Methods
|
||
function loadProjectDetail() {
|
||
loading.value = true
|
||
|
||
// Mock data - replace with actual API calls
|
||
setTimeout(() => {
|
||
project.value = {
|
||
"id": projectId.value,
|
||
"title": "跳远技术训练",
|
||
"description": "全面训练跳远技术,包括助跑、起跳、空中姿态和落地等各个环节",
|
||
"category": "田径运动",
|
||
"difficulty": "intermediate",
|
||
"status": "active",
|
||
"basic_requirements": [
|
||
{ "icon": "", "text": "助跑距离12-16步,节奏均匀" },
|
||
{ "icon": "", "text": "单脚起跳,起跳点准确" },
|
||
{ "icon": "✈️", "text": "空中保持良好姿态" },
|
||
{ "icon": "", "text": "双脚并拢前伸落地" }
|
||
],
|
||
"scoring_criteria": [
|
||
{ "range": "90-100分", "description": "动作标准,技术熟练,成绩优异" },
|
||
{ "range": "80-89分", "description": "动作较标准,技术较熟练" },
|
||
{ "range": "70-79分", "description": "动作基本标准,需要继续练习" },
|
||
{ "range": "60-69分", "description": "动作不标准,需要重点改进" }
|
||
]
|
||
} as UTSJSONObject
|
||
|
||
assignments.value = [
|
||
{
|
||
"id": "1",
|
||
"title": "跳远基础训练",
|
||
"created_at": "2024-01-15T10:00:00",
|
||
"participants": 25,
|
||
"status": "active"
|
||
},
|
||
{
|
||
"id": "2",
|
||
"title": "跳远技术提升",
|
||
"created_at": "2024-01-10T14:30:00",
|
||
"participants": 22,
|
||
"status": "completed"
|
||
},
|
||
{
|
||
"id": "3",
|
||
"title": "跳远考核测试",
|
||
"created_at": "2024-01-08T09:15:00",
|
||
"participants": 28,
|
||
"status": "completed"
|
||
}
|
||
]
|
||
|
||
statistics.value = {
|
||
"total_students": 28,
|
||
"average_score": 82.5,
|
||
"completion_rate": 85,
|
||
"improvement_rate": 12,
|
||
"weekly_average": 84.2,
|
||
"weekly_change": 3.5,
|
||
"monthly_average": 81.8,
|
||
"monthly_change": -1.2,
|
||
"assignment_count": 8,
|
||
"record_count": 156
|
||
} as UTSJSONObject
|
||
|
||
loading.value = false
|
||
}, 1000)
|
||
}
|
||
|
||
// 监听屏幕尺寸变化
|
||
onMounted(() => {
|
||
screenWidth.value = uni.getSystemInfoSync().windowWidth
|
||
})
|
||
|
||
onResize((size) => {
|
||
screenWidth.value = size.size.windowWidth
|
||
})
|
||
|
||
// Lifecycle
|
||
onLoad((options: OnLoadOptions) => {
|
||
const id = options['id']
|
||
if (id !== null) {
|
||
projectId.value = id as string
|
||
} else {
|
||
projectId.value = ''
|
||
}
|
||
loadProjectDetail()
|
||
})
|
||
|
||
// Helper functions for data access
|
||
function getAssignmentCount(): number {
|
||
return (statistics.value.get('assignment_count') as number) ?? 0
|
||
}
|
||
function getRecordCount(): number {
|
||
return (statistics.value.get('record_count') as number) ?? 0
|
||
}
|
||
function getTotalStudents(): number {
|
||
return (statistics.value.get('total_students') as number) ?? 0
|
||
}
|
||
function getAverageScore(): string {
|
||
const score = (statistics.value.get('average_score') as number) ?? 0
|
||
return score.toFixed(1)
|
||
}
|
||
function getCompletionRate(): number {
|
||
return (statistics.value.get('completion_rate') as number) ?? 0
|
||
}
|
||
function getImprovementRate(): number {
|
||
return (statistics.value.get('improvement_rate') as number) ?? 0
|
||
} function getBasicRequirements(): Array<UTSJSONObject> {
|
||
const requirements = project.value.get('basic_requirements') as Array<any> ?? []
|
||
if (requirements instanceof Array) {
|
||
return requirements.map((item: any) => item as UTSJSONObject)
|
||
}
|
||
return []
|
||
} function getScoringCriteria(): Array<UTSJSONObject> {
|
||
const criteriaData = project.value.get('scoring_criteria') ?? null
|
||
|
||
if (criteriaData != null && typeof criteriaData === 'object') {
|
||
// New JSON format: {criteria: [{min_score, max_score, description}], ...}
|
||
const criteriaObj = criteriaData as UTSJSONObject
|
||
const criteria = criteriaObj.get('criteria') as Array<any> ?? []
|
||
if (criteria instanceof Array) {
|
||
return criteria.map((item: any) => {
|
||
const itemObj = item as UTSJSONObject
|
||
const minScore = itemObj.get('min_score') ?? 0
|
||
const maxScore = itemObj.get('max_score') ?? 100
|
||
const description = itemObj.get('description') ?? ''
|
||
return {
|
||
range: `${minScore}-${maxScore}分`,
|
||
description: description.toString()
|
||
} as UTSJSONObject
|
||
})
|
||
}
|
||
}
|
||
|
||
// Fallback: Legacy format or hardcoded data
|
||
const legacyCriteria = project.value.get('scoring_criteria') as Array<any> ?? []
|
||
if (legacyCriteria instanceof Array) {
|
||
return legacyCriteria.map((item: any) => item as UTSJSONObject)
|
||
}
|
||
|
||
return []
|
||
}
|
||
|
||
function getRecentAssignments(): Array<UTSJSONObject> {
|
||
return assignments.value.slice(0, 3)
|
||
}
|
||
function getAssignmentParticipants(assignment: UTSJSONObject): number {
|
||
return (assignment.get('participants') as number) ?? 0
|
||
}
|
||
function getWeeklyAverage(): string {
|
||
const score = (statistics.value.get('weekly_average') as number) ?? 0
|
||
return score.toFixed(1)
|
||
}
|
||
|
||
function getWeeklyChange(): string {
|
||
const change = (statistics.value.get('weekly_change') as number) ?? 0
|
||
return Math.abs(change).toFixed(1)
|
||
}
|
||
|
||
function getMonthlyAverage(): string {
|
||
const score = (statistics.value.get('monthly_average') as number) ?? 0
|
||
return score.toFixed(1)
|
||
}
|
||
|
||
function getMonthlyChange(): string {
|
||
const change = (statistics.value.get('monthly_change') as number) ?? 0
|
||
return change.toFixed(1)
|
||
}
|
||
|
||
function viewAllAssignments() {
|
||
uni.navigateTo({
|
||
url: `/pages/sport/teacher/assignments?projectId=${projectId.value}`
|
||
})
|
||
}
|
||
|
||
function viewAssignmentDetail(assignment: UTSJSONObject) {
|
||
const assignmentId = getAssignmentId(assignment)
|
||
uni.navigateTo({
|
||
url: `/pages/sport/teacher/assignment-detail?id=${assignmentId}`
|
||
})
|
||
}
|
||
|
||
function editProject() {
|
||
uni.navigateTo({
|
||
url: `/pages/sport/teacher/project-edit?id=${projectId.value}`
|
||
})
|
||
}
|
||
|
||
function createAssignment() {
|
||
uni.navigateTo({
|
||
url: `/pages/sport/teacher/create-assignment?projectId=${projectId.value}`
|
||
})
|
||
}
|
||
// Template helper functions
|
||
const getProjectDisplayNameWrapper = (project: UTSJSONObject): string => getProjectDisplayName(project)
|
||
const getProjectDescriptionWrapper = (project: UTSJSONObject): string => getProjectDescription(project)
|
||
const getProjectCategoryWrapper = (project: UTSJSONObject): string => getProjectCategory(project)
|
||
const getProjectDifficultyWrapper = (project: UTSJSONObject): number => getProjectDifficulty(project)
|
||
const getAssignmentDisplayNameWrapper = (assignment: UTSJSONObject): string => getAssignmentDisplayName(assignment)
|
||
const getAssignmentStatusWrapper = (assignment: UTSJSONObject): string => getAssignmentStatus(assignment)
|
||
const formatDifficultyWrapper = (difficulty: number): string => formatDifficulty(difficulty)
|
||
const formatDateWrapper = (date: string): string => formatDate(date)
|
||
const formatProjectStatusLocal = (status: string): string => formatProjectStatus(status)
|
||
const getAssignmentCreatedAtLocal = (assignment: UTSJSONObject): string => getAssignmentCreatedAt(assignment)
|
||
|
||
// Scoring criteria helpers for template
|
||
function getCriteriaRange(criteria: UTSJSONObject): string {
|
||
// Handles both legacy and new format
|
||
if (criteria.getString('range')!=null) {
|
||
return criteria.getString('range')?? ''
|
||
}
|
||
const min = criteria.get('min_score') ?? ''
|
||
const max = criteria.get('max_score') ?? ''
|
||
if (min !== '' && max !== '') {
|
||
return `${min}-${max}分`
|
||
}
|
||
return ''
|
||
}
|
||
function getCriteriaDescription(criteria: UTSJSONObject): string {
|
||
return criteria.getString('description') ?? ''
|
||
}
|
||
|
||
// Requirement helpers
|
||
function getRequirementIcon(req: UTSJSONObject): string {
|
||
return req.getString('icon') ?? ''
|
||
}
|
||
function getRequirementText(req: UTSJSONObject): string {
|
||
return req.getString('text') ?? ''
|
||
}
|
||
</script>
|
||
|
||
<style>
|
||
.project-detail {
|
||
background-color: #f5f5f5;
|
||
height: 100vh;
|
||
padding: 20rpx;
|
||
padding-bottom: 40rpx;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
/* Header Card */
|
||
.header-card {
|
||
background-image: linear-gradient(to bottom right, #667eea, #764ba2);
|
||
border-radius: 20rpx;
|
||
padding: 30rpx;
|
||
margin-bottom: 20rpx;
|
||
color: white;
|
||
}
|
||
|
||
.project-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 20rpx;
|
||
}
|
||
|
||
.project-title {
|
||
font-size: 36rpx;
|
||
font-weight: bold;
|
||
flex: 1;
|
||
}
|
||
|
||
.status-badge {
|
||
padding: 8rpx 16rpx;
|
||
border-radius: 16rpx;
|
||
margin-left: 20rpx;
|
||
}
|
||
|
||
.status-active {
|
||
background-color: rgba(40, 167, 69, 0.3);
|
||
border: 1px solid rgba(40, 167, 69, 0.6);
|
||
}
|
||
|
||
.status-inactive {
|
||
background-color: rgba(108, 117, 125, 0.3);
|
||
border: 1px solid rgba(108, 117, 125, 0.6);
|
||
}
|
||
|
||
.status-text {
|
||
font-size: 24rpx;
|
||
color: white;
|
||
}
|
||
|
||
.project-description {
|
||
font-size: 28rpx;
|
||
line-height: 1.6;
|
||
margin-bottom: 25rpx;
|
||
opacity: 0.9;
|
||
}
|
||
.project-meta {
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
|
||
.project-meta .meta-row {
|
||
margin-bottom: 15rpx;
|
||
}
|
||
|
||
.project-meta .meta-row:last-child {
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
.meta-row {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
}
|
||
|
||
.meta-item {
|
||
display: flex;
|
||
align-items: center;
|
||
flex: 1;
|
||
}
|
||
|
||
.meta-icon {
|
||
font-size: 24rpx;
|
||
margin-right: 8rpx;
|
||
}
|
||
|
||
.meta-text {
|
||
font-size: 26rpx;
|
||
opacity: 0.9;
|
||
}
|
||
|
||
/* Statistics Card */
|
||
.stats-card {
|
||
background-color: white;
|
||
border-radius: 20rpx;
|
||
padding: 30rpx;
|
||
margin-bottom: 20rpx;
|
||
box-shadow: 0 2rpx 20rpx rgba(0, 0, 0, 0.05);
|
||
}
|
||
|
||
.card-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 25rpx;
|
||
}
|
||
|
||
.card-title {
|
||
font-size: 32rpx;
|
||
font-weight: bold;
|
||
color: #333;
|
||
}
|
||
.stats-grid {
|
||
display: flex;
|
||
flex-direction: row;
|
||
flex-wrap: wrap;
|
||
}
|
||
.stat-item {
|
||
width: 300rpx;
|
||
margin-right: 20rpx;
|
||
margin-bottom: 20rpx;
|
||
text-align: center;
|
||
padding: 25rpx;
|
||
background-color: #f8f9ff;
|
||
border-radius: 16rpx;
|
||
position: relative;
|
||
}
|
||
|
||
.stat-item:nth-child(2n) {
|
||
margin-right: 0;
|
||
}
|
||
|
||
.stat-value {
|
||
font-size: 36rpx;
|
||
font-weight: bold;
|
||
color: #667eea;
|
||
display: block;
|
||
margin-bottom: 8rpx;
|
||
}
|
||
|
||
.stat-label {
|
||
font-size: 26rpx;
|
||
color: #666;
|
||
display: block;
|
||
}
|
||
|
||
.stat-icon {
|
||
position: absolute;
|
||
top: 15rpx;
|
||
right: 15rpx;
|
||
font-size: 32rpx;
|
||
opacity: 0.3;
|
||
}
|
||
|
||
/* Requirements Card */
|
||
.requirements-card {
|
||
background-color: white;
|
||
border-radius: 20rpx;
|
||
padding: 30rpx;
|
||
margin-bottom: 20rpx;
|
||
box-shadow: 0 2rpx 20rpx rgba(0, 0, 0, 0.05);
|
||
}
|
||
|
||
.requirement-section {
|
||
margin-bottom: 30rpx;
|
||
}
|
||
|
||
.requirement-section:last-child {
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
.section-title {
|
||
font-size: 28rpx;
|
||
font-weight: bold;
|
||
color: #333;
|
||
margin-bottom: 15rpx;
|
||
display: block;
|
||
}
|
||
.requirement-list {
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
|
||
.requirement-list .requirement-item {
|
||
margin-bottom: 12rpx;
|
||
}
|
||
|
||
.requirement-list .requirement-item:last-child {
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
.requirement-item {
|
||
display: flex;
|
||
align-items: center;
|
||
padding: 15rpx;
|
||
background-color: #f8f9ff;
|
||
border-radius: 12rpx;
|
||
}
|
||
|
||
.requirement-icon {
|
||
font-size: 24rpx;
|
||
margin-right: 12rpx;
|
||
width: 32rpx;
|
||
text-align: center;
|
||
}
|
||
|
||
.requirement-text {
|
||
font-size: 26rpx;
|
||
color: #333;
|
||
flex: 1;
|
||
}
|
||
|
||
.scoring-table {
|
||
border-radius: 12rpx;
|
||
overflow: hidden;
|
||
border: 1rpx solid #eee;
|
||
}
|
||
.scoring-row {
|
||
display: flex;
|
||
border-bottom: 1rpx solid #eee;
|
||
}
|
||
|
||
.scoring-row:nth-child(4) {
|
||
border-bottom: none;
|
||
}
|
||
|
||
.score-range {
|
||
width: 160rpx;
|
||
padding: 15rpx;
|
||
background-color: #f8f9ff;
|
||
font-size: 26rpx;
|
||
font-weight: bold;
|
||
color: #667eea;
|
||
text-align: center;
|
||
border-right: 1rpx solid #eee;
|
||
}
|
||
|
||
.score-desc {
|
||
flex: 1;
|
||
padding: 15rpx;
|
||
font-size: 26rpx;
|
||
color: #333;
|
||
background-color: white;
|
||
}
|
||
|
||
/* Assignments Card */
|
||
.assignments-card {
|
||
background-color: white;
|
||
border-radius: 20rpx;
|
||
padding: 30rpx;
|
||
margin-bottom: 20rpx;
|
||
box-shadow: 0 2rpx 20rpx rgba(0, 0, 0, 0.05);
|
||
}
|
||
|
||
.view-all-btn {
|
||
font-size: 26rpx;
|
||
color: #667eea;
|
||
padding: 8rpx 16rpx;
|
||
border-radius: 12rpx;
|
||
background-color: rgba(102, 126, 234, 0.1);
|
||
}
|
||
.assignments-list {
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
|
||
.assignment-item {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
margin-bottom: 15rpx;
|
||
align-items: center;
|
||
padding: 20rpx;
|
||
background-color: #fafafa;
|
||
border-radius: 12rpx;
|
||
}
|
||
|
||
.assignment-content {
|
||
flex: 1;
|
||
}
|
||
|
||
.assignment-title {
|
||
font-size: 28rpx;
|
||
font-weight: bold;
|
||
color: #333;
|
||
margin-bottom: 5rpx;
|
||
display: block;
|
||
}
|
||
|
||
.assignment-date {
|
||
font-size: 24rpx;
|
||
color: #666;
|
||
}
|
||
.assignment-stats {
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
|
||
.assignment-stats .participants {
|
||
margin-right: 15rpx;
|
||
}
|
||
|
||
.participants {
|
||
font-size: 24rpx;
|
||
color: #666;
|
||
}
|
||
|
||
.status-dot {
|
||
width: 12rpx;
|
||
height: 12rpx;
|
||
border-radius: 6rpx;
|
||
}
|
||
|
||
.status-dot.status-active {
|
||
background-color: #28a745;
|
||
}
|
||
|
||
.status-dot.status-completed {
|
||
background-color: #6c757d;
|
||
}
|
||
|
||
/* Trends Card */
|
||
.trends-card {
|
||
background-color: white;
|
||
border-radius: 20rpx;
|
||
padding: 30rpx;
|
||
margin-bottom: 20rpx;
|
||
box-shadow: 0 2rpx 20rpx rgba(0, 0, 0, 0.05);
|
||
}
|
||
|
||
.chart-placeholder {
|
||
height: 200rpx;
|
||
background-image: linear-gradient(to bottom right, #f8f9ff, #e3f2fd);
|
||
border-radius: 16rpx;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
margin-bottom: 25rpx;
|
||
border: 2rpx dashed #667eea;
|
||
}
|
||
|
||
.chart-text {
|
||
font-size: 32rpx;
|
||
font-weight: bold;
|
||
color: #667eea;
|
||
margin-bottom: 8rpx;
|
||
}
|
||
|
||
.chart-desc {
|
||
font-size: 24rpx;
|
||
color: #666;
|
||
}
|
||
|
||
.trend-summary {
|
||
display: flex;
|
||
justify-content: space-around;
|
||
}
|
||
|
||
.trend-item {
|
||
text-align: center;
|
||
}
|
||
|
||
.trend-label {
|
||
font-size: 24rpx;
|
||
color: #666;
|
||
display: block;
|
||
margin-bottom: 5rpx;
|
||
}
|
||
|
||
.trend-value {
|
||
font-size: 32rpx;
|
||
font-weight: bold;
|
||
color: #333;
|
||
display: block;
|
||
margin-bottom: 5rpx;
|
||
}
|
||
|
||
.trend-change {
|
||
font-size: 24rpx;
|
||
font-weight: bold;
|
||
padding: 4rpx 8rpx;
|
||
border-radius: 8rpx;
|
||
}
|
||
|
||
.trend-change.positive {
|
||
color: #28a745;
|
||
background-color: rgba(40, 167, 69, 0.1);
|
||
}
|
||
|
||
.trend-change.negative {
|
||
color: #dc3545;
|
||
background-color: rgba(220, 53, 69, 0.1);
|
||
}
|
||
|
||
/* Action Buttons */ .action-buttons {
|
||
padding: 20rpx 0;
|
||
display: flex;
|
||
}
|
||
|
||
.action-btn {
|
||
flex: 1;
|
||
height: 88rpx;
|
||
border-radius: 44rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 32rpx;
|
||
font-weight: bold;
|
||
border: none;
|
||
margin-right: 20rpx;
|
||
}
|
||
|
||
.action-btn:last-of-type {
|
||
margin-right: 0;
|
||
}
|
||
|
||
.primary-btn {
|
||
background-image: linear-gradient(to bottom right, #667eea, #764ba2);
|
||
color: white;
|
||
}
|
||
|
||
.secondary-btn {
|
||
background-color: white;
|
||
color: #667eea;
|
||
border: 2rpx solid #667eea;
|
||
}
|
||
|
||
.primary-btn:active {
|
||
transform: scale(0.98);
|
||
}
|
||
|
||
.secondary-btn:active {
|
||
transform: scale(0.98);
|
||
background-color: #f8f9ff;
|
||
}
|
||
|
||
/* 小屏幕专用样式 */
|
||
.small-screen .stats-grid {
|
||
flex-direction: column;
|
||
}
|
||
|
||
.small-screen .stat-item {
|
||
width: 100%;
|
||
margin-right: 0;
|
||
}
|
||
|
||
.small-screen .action-buttons {
|
||
flex-direction: column;
|
||
}
|
||
|
||
.small-screen .action-btn {
|
||
margin-right: 0;
|
||
margin-bottom: 15rpx;
|
||
}
|
||
|
||
.small-screen .action-btn:last-of-type {
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
/* 响应式布局 - 小屏幕优化 */
|
||
@media (max-width: 768px) {
|
||
.stats-grid {
|
||
flex-direction: column;
|
||
}
|
||
|
||
.stat-item {
|
||
width: 100%;
|
||
margin-right: 0;
|
||
}
|
||
|
||
.meta-row {
|
||
flex-direction: column;
|
||
}
|
||
|
||
.meta-item {
|
||
margin-bottom: 10rpx;
|
||
}
|
||
|
||
.action-buttons {
|
||
flex-direction: column;
|
||
}
|
||
|
||
.action-btn {
|
||
margin-right: 0;
|
||
margin-bottom: 15rpx;
|
||
}
|
||
|
||
.action-btn:last-of-type {
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
.project-detail {
|
||
padding: 15rpx;
|
||
}
|
||
|
||
.header-card, .stats-card, .requirements-card, .assignments-card, .trends-card {
|
||
padding: 20rpx;
|
||
margin-bottom: 15rpx;
|
||
}
|
||
}
|
||
|
||
/* Android 兼容性优化 */
|
||
.project-detail {
|
||
display: flex;
|
||
flex:1;
|
||
/* 确保滚动容器正确 */
|
||
overflow-y: auto;
|
||
/* 移除可能不支持的属性 */
|
||
-webkit-overflow-scrolling: touch;
|
||
}
|
||
|
||
/* 修复可能的渲染问题 */
|
||
.header-card, .stats-card, .requirements-card, .assignments-card, .trends-card {
|
||
/* 强制硬件加速 */
|
||
transform: translateZ(0);
|
||
/* 确保背景正确渲染 */
|
||
background-clip: padding-box;
|
||
}
|
||
</style>
|