886 lines
20 KiB
Plaintext
886 lines
20 KiB
Plaintext
<template>
|
||
<scroll-view direction="vertical" class="project-create" :scroll-y="true" :enable-back-to-top="true">
|
||
<!-- Header -->
|
||
<view class="page-header">
|
||
<text class="page-title">创建训练项目</text>
|
||
</view>
|
||
|
||
<!-- Form Container -->
|
||
<view class="form-container">
|
||
<form @submit="handleSubmit">
|
||
<!-- Basic Information -->
|
||
<view class="form-section">
|
||
<text class="section-title">基本信息</text>
|
||
<view class="form-group">
|
||
<text class="form-label">项目名称 *</text>
|
||
<input class="form-input" v-model="title" placeholder="请输入项目名称" maxlength="50" />
|
||
</view>
|
||
|
||
<view class="form-group">
|
||
<text class="form-label">项目描述</text>
|
||
<textarea class="form-textarea" v-model="description" placeholder="请输入项目描述"
|
||
maxlength="500"></textarea>
|
||
</view>
|
||
<view class="form-group">
|
||
<text class="form-label">训练类别 *</text>
|
||
<button class="form-selector" @click="showCategoryPicker">
|
||
<text class="selector-text">{{ category ?? '选择训练类别' }}</text>
|
||
<text class="selector-arrow">></text>
|
||
</button>
|
||
</view>
|
||
|
||
<view class="form-group">
|
||
<text class="form-label">难度等级 *</text>
|
||
<view class="difficulty-options">
|
||
<button class="difficulty-btn" v-for="(level, index) in difficultyLevels" :key="index"
|
||
:class="{ active: difficulty === level.value }"
|
||
@click="setDifficulty(level.getString('value')??'')">
|
||
<text class="difficulty-icon">{{ level.icon }}</text>
|
||
<text class="difficulty-text">{{ level.label }}</text>
|
||
</button>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- Training Requirements -->
|
||
<view class="form-section">
|
||
<text class="section-title">训练要求</text>
|
||
<view class="requirements-list">
|
||
<view class="requirement-item" v-for="(requirement, index) in requirements" :key="index">
|
||
<input class="requirement-input" v-model="requirement.text" placeholder="输入训练要求" />
|
||
<button class="remove-btn" @click="removeRequirement(index)">
|
||
<text class="remove-icon">×</text>
|
||
</button>
|
||
</view>
|
||
</view>
|
||
|
||
<button class="add-requirement-btn" @click="addRequirement">
|
||
<text class="add-icon">+</text>
|
||
<text class="add-text">添加要求</text>
|
||
</button>
|
||
</view>
|
||
|
||
<!-- Scoring Criteria -->
|
||
<view class="form-section">
|
||
<text class="section-title">评分标准</text>
|
||
<view class="scoring-list">
|
||
<view class="scoring-item" v-for="(criteria, index) in scoringCriteria" :key="index">
|
||
<view class="score-range-group">
|
||
<input class="score-input" v-model="criteria.min_score" placeholder="最低分"
|
||
type="number" />
|
||
<text class="score-separator">-</text>
|
||
<input class="score-input" v-model="criteria.max_score" placeholder="最高分"
|
||
type="number" />
|
||
</view>
|
||
<input class="criteria-input" v-model="criteria.description" placeholder="评分标准描述" />
|
||
<button class="remove-btn" @click="removeCriteria(index)">
|
||
<text class="remove-icon">×</text>
|
||
</button>
|
||
</view>
|
||
</view>
|
||
|
||
<button class="add-criteria-btn" @click="addCriteria">
|
||
<text class="add-icon">+</text>
|
||
<text class="add-text">添加标准</text>
|
||
</button>
|
||
</view>
|
||
|
||
<!-- Performance Metrics -->
|
||
<view class="form-section">
|
||
<text class="section-title">绩效指标</text>
|
||
<view class="metrics-list">
|
||
<view class="metric-item" v-for="(metric, index) in performanceMetrics" :key="index">
|
||
<input class="metric-name" v-model="metric.name" placeholder="指标名称" />
|
||
<input class="metric-unit" v-model="metric.unit" placeholder="单位" />
|
||
<button class="remove-btn" @click="removeMetric(index)">
|
||
<text class="remove-icon">×</text>
|
||
</button>
|
||
</view>
|
||
</view>
|
||
|
||
<button class="add-metric-btn" @click="addMetric">
|
||
<text class="add-icon">+</text>
|
||
<text class="add-text">添加指标</text>
|
||
</button>
|
||
</view>
|
||
|
||
<!-- Action Buttons -->
|
||
<view class="action-buttons">
|
||
<button class="action-btn secondary-btn" @click="saveDraft">
|
||
保存草稿
|
||
</button>
|
||
<button class="action-btn primary-btn" @click="submitProject">
|
||
创建项目
|
||
</button>
|
||
</view>
|
||
</form>
|
||
</view>
|
||
|
||
<!-- Category Picker Modal -->
|
||
<view class="modal-overlay" v-if="showCategoryModal" @click="hideCategoryPicker">
|
||
<view class="category-modal" @click.stop>
|
||
<view class="modal-header">
|
||
<text class="modal-title">选择训练类别</text>
|
||
<button class="modal-close-btn" @click="hideCategoryPicker">×</button>
|
||
</view>
|
||
<view class="category-list"> <button class="category-option" v-for="(categoryItem, index) in categories"
|
||
:key="index" @click="selectCategory(categoryItem)">
|
||
<text class="category-icon">{{ categoryItem.icon }}</text>
|
||
<text class="category-name">{{ categoryItem.name }}</text>
|
||
</button>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</scroll-view>
|
||
</template>
|
||
|
||
<script setup lang="uts">
|
||
import supaClient from '../../../components/supadb/aksupainstance.uts'
|
||
|
||
// Type definitions
|
||
type CategoryItem = {
|
||
name : string
|
||
value : string
|
||
icon : string
|
||
}
|
||
|
||
type RequirementItem = {
|
||
text : string
|
||
}
|
||
|
||
type ScoringCriteriaItem = {
|
||
min_score : string
|
||
max_score : string
|
||
description : string
|
||
}
|
||
|
||
type PerformanceMetricItem = {
|
||
name : string
|
||
unit : string
|
||
}
|
||
// 1-dimensional reactive refs
|
||
const title = ref<string>('')
|
||
const description = ref<string>('')
|
||
const category = ref<string>('')
|
||
const difficulty = ref<string>('')
|
||
// Array refs for dynamic lists - using regular arrays
|
||
const requirements = ref<Array<RequirementItem>>([{ text: '' } as RequirementItem])
|
||
const scoringCriteria = ref<Array<ScoringCriteriaItem>>([
|
||
{ min_score: '', max_score: '', description: '' } as ScoringCriteriaItem
|
||
])
|
||
const performanceMetrics = ref<Array<PerformanceMetricItem>>([
|
||
{ name: '', unit: '' } as PerformanceMetricItem
|
||
])
|
||
|
||
// UI state
|
||
const showCategoryModal = ref(false)
|
||
const loading = ref(false)
|
||
|
||
// Computed formData for database operations
|
||
const formData = computed(() => {
|
||
return {
|
||
title: title.value,
|
||
description: description.value,
|
||
category: category.value,
|
||
difficulty: difficulty.value,
|
||
requirements: requirements.value,
|
||
scoring_criteria: scoringCriteria.value,
|
||
performance_metrics: performanceMetrics.value,
|
||
status: 'draft'
|
||
}
|
||
})
|
||
const categories : Array<CategoryItem> = [
|
||
{ name: '田径运动', icon: '', value: 'athletics' },
|
||
{ name: '球类运动', icon: '⚽', value: 'ball_sports' },
|
||
{ name: '游泳运动', icon: '', value: 'swimming' },
|
||
{ name: '体操运动', icon: '', value: 'gymnastics' },
|
||
{ name: '武术运动', icon: '', value: 'martial_arts' },
|
||
{ name: '健身运动', icon: '', value: 'fitness' }
|
||
]
|
||
const difficultyLevels = [
|
||
{ label: '初级', value: 'beginner', icon: '' },
|
||
{ label: '中级', value: 'intermediate', icon: '' },
|
||
{ label: '高级', value: 'advanced', icon: '' },
|
||
{ label: '专家', value: 'expert', icon: '' }
|
||
]
|
||
function initializeForm() {
|
||
title.value = ''
|
||
description.value = ''
|
||
category.value = ''
|
||
difficulty.value = ''
|
||
requirements.value = [{ text: '' } as RequirementItem]
|
||
scoringCriteria.value = [{ min_score: '', max_score: '', description: '' } as ScoringCriteriaItem]
|
||
performanceMetrics.value = [{ name: '', unit: '' } as PerformanceMetricItem]
|
||
}
|
||
|
||
function showCategoryPicker() {
|
||
showCategoryModal.value = true
|
||
}
|
||
|
||
function hideCategoryPicker() {
|
||
showCategoryModal.value = false
|
||
}
|
||
function selectCategory(categoryItem : any) {
|
||
const categoryObj = categoryItem as CategoryItem
|
||
category.value = categoryObj.name
|
||
hideCategoryPicker()
|
||
}
|
||
|
||
function setDifficulty(difficultyValue : string) {
|
||
difficulty.value = difficultyValue
|
||
}
|
||
function addRequirement() {
|
||
requirements.value.push({ text: '' } as RequirementItem)
|
||
}
|
||
|
||
function removeRequirement(index : number) {
|
||
if (requirements.value.length > 1) {
|
||
requirements.value.splice(index, 1)
|
||
}
|
||
}
|
||
|
||
function addCriteria() {
|
||
scoringCriteria.value.push({ min_score: '', max_score: '', description: '' } as ScoringCriteriaItem)
|
||
}
|
||
|
||
function removeCriteria(index : number) {
|
||
if (scoringCriteria.value.length > 1) {
|
||
scoringCriteria.value.splice(index, 1)
|
||
}
|
||
}
|
||
|
||
function addMetric() {
|
||
performanceMetrics.value.push({ name: '', unit: '' } as PerformanceMetricItem)
|
||
}
|
||
|
||
function removeMetric(index : number) {
|
||
if (performanceMetrics.value.length > 1) {
|
||
performanceMetrics.value.splice(index, 1)
|
||
}
|
||
}
|
||
function validateForm() : boolean {
|
||
if (title.value.trim() === '') {
|
||
uni.showToast({
|
||
title: '请输入项目名称',
|
||
icon: 'none'
|
||
})
|
||
return false
|
||
}
|
||
|
||
if (category.value.trim() === '') {
|
||
uni.showToast({
|
||
title: '请选择训练类别',
|
||
icon: 'none'
|
||
})
|
||
return false
|
||
}
|
||
|
||
if (difficulty.value.trim() === '') {
|
||
uni.showToast({
|
||
title: '请选择难度等级',
|
||
icon: 'none'
|
||
})
|
||
return false
|
||
}
|
||
|
||
return true
|
||
}
|
||
async function saveDraft() {
|
||
if (title.value.trim() === '') {
|
||
uni.showToast({
|
||
title: '请至少输入项目名称',
|
||
icon: 'none'
|
||
})
|
||
return
|
||
}
|
||
|
||
loading.value = true
|
||
try {
|
||
// Convert form data to database format
|
||
const objectives = requirements.value
|
||
.map(req => req.text)
|
||
.filter(text => text.trim().length > 0)
|
||
|
||
// Create scoring criteria JSON structure
|
||
type MappedCriteria = {
|
||
min_score : number
|
||
max_score : number
|
||
description : string
|
||
}
|
||
const mappedCriteria : Array<MappedCriteria> = []
|
||
for (let i = 0; i < scoringCriteria.value.length; i++) {
|
||
const criteria = scoringCriteria.value[i]
|
||
mappedCriteria.push({
|
||
min_score: parseInt(criteria.min_score) ?? 0,
|
||
max_score: parseInt(criteria.max_score) ?? 100,
|
||
description: criteria.description
|
||
} as MappedCriteria)
|
||
}
|
||
const filteredCriteria = mappedCriteria.filter((item : MappedCriteria) : boolean => {
|
||
return item.description.trim().length > 0
|
||
})
|
||
|
||
const scoringCriteriaJson = {
|
||
criteria: filteredCriteria,
|
||
scoring_method: "comprehensive",
|
||
weight_distribution: {
|
||
technique: 0.4, // 技术动作权重 40%
|
||
effort: 0.3, // 努力程度权重 30%
|
||
improvement: 0.3 // 进步幅度权重 30%
|
||
}
|
||
}
|
||
const equipmentRequired = performanceMetrics.value
|
||
.map(metric => metric.name)
|
||
.filter(name => name.trim().length > 0)
|
||
|
||
|
||
const insertResult = await supaClient
|
||
.from('ak_training_projects')
|
||
.insert({
|
||
title: title.value,
|
||
description: description.value,
|
||
sport_type: category.value,
|
||
difficulty_level: difficulty.value,
|
||
is_active: false, // draft is inactive image_url: '',
|
||
video_url: '',
|
||
objectives: objectives,
|
||
scoring_criteria: scoringCriteriaJson,
|
||
equipment_required: equipmentRequired,
|
||
created_at: new Date().toISOString(), updated_at: new Date().toISOString()
|
||
})
|
||
.execute()
|
||
|
||
if (insertResult.error != null) {
|
||
console.error('Error saving draft:', insertResult.error)
|
||
uni.showToast({
|
||
title: '保存草稿失败',
|
||
icon: 'none'
|
||
})
|
||
} else {
|
||
uni.showToast({
|
||
title: '草稿保存成功',
|
||
icon: 'success'
|
||
})
|
||
setTimeout(() => {
|
||
uni.navigateBack()
|
||
}, 1500)
|
||
}
|
||
} catch (error) {
|
||
console.error('Error saving draft:', error)
|
||
uni.showToast({
|
||
title: '保存草稿失败',
|
||
icon: 'none'
|
||
})
|
||
} finally {
|
||
loading.value = false
|
||
}
|
||
} async function submitProject() {
|
||
if (!validateForm()) return
|
||
|
||
loading.value = true
|
||
|
||
try {
|
||
// Convert form data to database format
|
||
const objectives = requirements.value
|
||
.map(req => req.text)
|
||
.filter(text => text.trim().length > 0)
|
||
|
||
// Create scoring criteria JSON structure
|
||
type MappedCriteria = {
|
||
min_score : number
|
||
max_score : number
|
||
description : string
|
||
}
|
||
const mappedCriteria : Array<MappedCriteria> = []
|
||
for (let i = 0; i < scoringCriteria.value.length; i++) {
|
||
const criteria = scoringCriteria.value[i]
|
||
mappedCriteria.push({
|
||
min_score: parseInt(criteria.min_score) ?? 0,
|
||
max_score: parseInt(criteria.max_score) ?? 100,
|
||
description: criteria.description
|
||
} as MappedCriteria)
|
||
}
|
||
const filteredCriteria = mappedCriteria.filter((item : MappedCriteria) : boolean => {
|
||
return item.description.trim().length > 0
|
||
})
|
||
|
||
const scoringCriteriaJson = {
|
||
criteria: filteredCriteria,
|
||
scoring_method: "comprehensive",
|
||
weight_distribution: {
|
||
technique: 0.4, // 技术动作权重 40%
|
||
effort: 0.3, // 努力程度权重 30%
|
||
improvement: 0.3 // 进步幅度权重 30%
|
||
}
|
||
}
|
||
|
||
const equipmentRequired = performanceMetrics.value
|
||
.map((metric : PerformanceMetricItem) => metric.name)
|
||
.filter((name : string) => name.trim().length > 0)
|
||
|
||
const insertResult = await supaClient
|
||
.from('ak_training_projects')
|
||
.insert({
|
||
title: title.value,
|
||
description: description.value,
|
||
sport_type: category.value,
|
||
difficulty_level: difficulty.value,
|
||
is_active: true, // active project
|
||
image_url: '',
|
||
video_url: '',
|
||
objectives: objectives,
|
||
scoring_criteria: scoringCriteriaJson,
|
||
equipment_required: equipmentRequired,
|
||
created_at: new Date().toISOString(),
|
||
updated_at: new Date().toISOString()
|
||
})
|
||
.execute()
|
||
|
||
if (insertResult.error != null) {
|
||
console.error('Error creating project:', insertResult.error)
|
||
uni.showToast({
|
||
title: '项目创建失败',
|
||
icon: 'none'
|
||
})
|
||
} else {
|
||
uni.showToast({
|
||
title: '项目创建成功',
|
||
icon: 'success'
|
||
})
|
||
setTimeout(() => {
|
||
uni.navigateBack()
|
||
}, 1500)
|
||
}
|
||
} catch (error) {
|
||
console.error('Error creating project:', error)
|
||
uni.showToast({
|
||
title: '项目创建失败',
|
||
icon: 'none'
|
||
})
|
||
} finally {
|
||
loading.value = false
|
||
}
|
||
}
|
||
|
||
function handleSubmit(event : Event) {
|
||
event.preventDefault()
|
||
submitProject()
|
||
}
|
||
// Lifecycle
|
||
onLoad(() => {
|
||
initializeForm()
|
||
})
|
||
</script>
|
||
|
||
<style>
|
||
.project-create {
|
||
flex:1;
|
||
background-color: #f5f5f5;
|
||
|
||
padding: 20rpx;
|
||
padding-bottom: 40rpx;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
/* Header */
|
||
.page-header {
|
||
margin-bottom: 25rpx;
|
||
}
|
||
|
||
.page-title {
|
||
font-size: 40rpx;
|
||
font-weight: bold;
|
||
color: #333;
|
||
}
|
||
|
||
/* Form Container */
|
||
.form-container {
|
||
background-color: white;
|
||
border-radius: 20rpx;
|
||
padding: 30rpx;
|
||
box-shadow: 0 2rpx 20rpx rgba(0, 0, 0, 0.05);
|
||
}
|
||
|
||
.form-section {
|
||
margin-bottom: 40rpx;
|
||
}
|
||
|
||
.form-section:last-child {
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
.section-title {
|
||
font-size: 32rpx;
|
||
font-weight: bold;
|
||
color: #333;
|
||
margin-bottom: 25rpx;
|
||
display: block;
|
||
}
|
||
|
||
/* Form Controls */
|
||
.form-group {
|
||
margin-bottom: 25rpx;
|
||
}
|
||
|
||
.form-label {
|
||
font-size: 28rpx;
|
||
color: #333;
|
||
margin-bottom: 10rpx;
|
||
display: block;
|
||
font-weight: 400;
|
||
}
|
||
|
||
.form-input {
|
||
width: 100%;
|
||
height: 88rpx;
|
||
border: 2rpx solid #e0e0e0;
|
||
border-radius: 12rpx;
|
||
padding: 0 20rpx;
|
||
font-size: 28rpx;
|
||
color: #333;
|
||
background-color: #fff;
|
||
}
|
||
|
||
.form-input:focus {
|
||
border-color: #667eea;
|
||
outline: none;
|
||
}
|
||
|
||
.form-textarea {
|
||
width: 100%;
|
||
min-height: 160rpx;
|
||
border: 2rpx solid #e0e0e0;
|
||
border-radius: 12rpx;
|
||
padding: 20rpx;
|
||
font-size: 28rpx;
|
||
color: #333;
|
||
background-color: #fff;
|
||
resize: vertical;
|
||
}
|
||
|
||
.form-textarea:focus {
|
||
border-color: #667eea;
|
||
outline: none;
|
||
}
|
||
|
||
.form-selector {
|
||
width: 100%;
|
||
height: 88rpx;
|
||
border: 2rpx solid #e0e0e0;
|
||
border-radius: 12rpx;
|
||
padding: 0 20rpx;
|
||
background-color: #fff;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
}
|
||
|
||
.selector-text {
|
||
font-size: 28rpx;
|
||
color: #333;
|
||
}
|
||
|
||
.selector-arrow {
|
||
font-size: 24rpx;
|
||
color: #666;
|
||
}
|
||
|
||
/* Difficulty Options */
|
||
.difficulty-options {
|
||
display: flex;
|
||
flex-direction: row;
|
||
flex-wrap: wrap;
|
||
margin: 0 -7.5rpx;
|
||
}
|
||
|
||
.difficulty-options .difficulty-btn {
|
||
width: 45%;
|
||
flex: 0 0 45%;
|
||
margin: 0 7.5rpx 15rpx;
|
||
}
|
||
|
||
.difficulty-btn {
|
||
height: 100rpx;
|
||
border: 2rpx solid #e0e0e0;
|
||
border-radius: 12rpx;
|
||
background-color: #fff;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.difficulty-btn .difficulty-text {
|
||
margin-top: 8rpx;
|
||
}
|
||
|
||
|
||
.difficulty-btn.active {
|
||
border-color: #667eea;
|
||
background-color: #f8f9ff;
|
||
}
|
||
|
||
.difficulty-icon {
|
||
font-size: 32rpx;
|
||
}
|
||
|
||
.difficulty-text {
|
||
font-size: 24rpx;
|
||
color: #333;
|
||
font-weight: 400;
|
||
}
|
||
|
||
/* Dynamic Lists */
|
||
.requirements-list,
|
||
.scoring-list,
|
||
.metrics-list {
|
||
margin-bottom: 20rpx;
|
||
}
|
||
|
||
.requirement-item,
|
||
.scoring-item,
|
||
.metric-item {
|
||
display: flex;
|
||
align-items: center;
|
||
margin-bottom: 15rpx;
|
||
}
|
||
|
||
.requirement-item .remove-btn,
|
||
.scoring-item .remove-btn,
|
||
.metric-item .remove-btn {
|
||
margin-left: 15rpx;
|
||
}
|
||
|
||
.requirement-input,
|
||
.criteria-input,
|
||
.metric-name {
|
||
flex: 1;
|
||
height: 70rpx;
|
||
border: 2rpx solid #e0e0e0;
|
||
border-radius: 8rpx;
|
||
padding: 0 15rpx;
|
||
font-size: 26rpx;
|
||
color: #333;
|
||
}
|
||
|
||
.score-range-group {
|
||
display: flex;
|
||
align-items: center;
|
||
min-width: 200rpx;
|
||
}
|
||
|
||
.score-range-group .score-input {
|
||
margin-right: 10rpx;
|
||
}
|
||
|
||
.score-range-group .score-input:last-child {
|
||
margin-right: 0;
|
||
margin-left: 10rpx;
|
||
}
|
||
|
||
|
||
.score-input {
|
||
width: 80rpx;
|
||
height: 70rpx;
|
||
border: 2rpx solid #e0e0e0;
|
||
border-radius: 8rpx;
|
||
padding: 0 10rpx;
|
||
font-size: 26rpx;
|
||
text-align: center;
|
||
}
|
||
|
||
.score-separator {
|
||
font-size: 24rpx;
|
||
color: #666;
|
||
}
|
||
|
||
.metric-unit {
|
||
width: 120rpx;
|
||
height: 70rpx;
|
||
border: 2rpx solid #e0e0e0;
|
||
border-radius: 8rpx;
|
||
padding: 0 15rpx;
|
||
font-size: 26rpx;
|
||
text-align: center;
|
||
}
|
||
|
||
.remove-btn {
|
||
width: 60rpx;
|
||
height: 60rpx;
|
||
border-radius: 30rpx;
|
||
background-color: #ff4757;
|
||
border: none;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.remove-icon {
|
||
font-size: 32rpx;
|
||
color: white;
|
||
font-weight: bold;
|
||
}
|
||
|
||
/* Add Buttons */
|
||
.add-requirement-btn,
|
||
.add-criteria-btn,
|
||
.add-metric-btn {
|
||
width: 100%;
|
||
height: 70rpx;
|
||
border: 2rpx dashed #667eea;
|
||
border-radius: 8rpx;
|
||
background-color: #f8f9ff;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.add-requirement-btn .add-text,
|
||
.add-criteria-btn .add-text,
|
||
.add-metric-btn .add-text {
|
||
margin-left: 10rpx;
|
||
}
|
||
|
||
.add-icon {
|
||
font-size: 28rpx;
|
||
color: #667eea;
|
||
font-weight: bold;
|
||
}
|
||
|
||
.add-text {
|
||
font-size: 26rpx;
|
||
color: #667eea;
|
||
font-weight: 400;
|
||
}
|
||
|
||
/* Action Buttons */
|
||
.action-buttons {
|
||
display: flex;
|
||
margin-top: 40rpx;
|
||
}
|
||
|
||
.action-buttons .action-btn {
|
||
margin-right: 20rpx;
|
||
}
|
||
|
||
.action-buttons .action-btn:last-child {
|
||
margin-right: 0;
|
||
}
|
||
|
||
.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;
|
||
}
|
||
|
||
.primary-btn {
|
||
background-image: linear-gradient(to bottom right, #667eea, #764ba2);
|
||
color: white;
|
||
}
|
||
|
||
.secondary-btn {
|
||
background-color: #f5f5f5;
|
||
color: #666;
|
||
border: 2rpx solid #e0e0e0;
|
||
}
|
||
|
||
/* Category Modal */
|
||
.modal-overlay {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
background-color: rgba(0, 0, 0, 0.5);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
z-index: 1000;
|
||
}
|
||
|
||
.category-modal {
|
||
width: 90%;
|
||
max-width: 600rpx;
|
||
background-color: white;
|
||
border-radius: 20rpx;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.modal-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 30rpx;
|
||
border-bottom: 1rpx solid #f0f0f0;
|
||
}
|
||
|
||
.modal-title {
|
||
font-size: 32rpx;
|
||
font-weight: bold;
|
||
color: #333;
|
||
}
|
||
|
||
.modal-close-btn {
|
||
width: 60rpx;
|
||
height: 60rpx;
|
||
border-radius: 30rpx;
|
||
background-color: #f5f5f5;
|
||
border: none;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 28rpx;
|
||
color: #666;
|
||
}
|
||
|
||
.category-list {
|
||
padding: 20rpx;
|
||
display: flex;
|
||
flex-direction: row;
|
||
flex-wrap: wrap;
|
||
margin: 0 -7.5rpx;
|
||
}
|
||
|
||
.category-list .category-option {
|
||
width: 45%;
|
||
flex: 0 0 45%;
|
||
margin: 0 7.5rpx 15rpx;
|
||
}
|
||
|
||
.category-option {
|
||
height: 120rpx;
|
||
border: 2rpx solid #e0e0e0;
|
||
border-radius: 12rpx;
|
||
background-color: white;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.category-option .category-name {
|
||
margin-top: 8rpx;
|
||
}
|
||
|
||
.category-option:active {
|
||
background-color: #f8f9ff;
|
||
border-color: #667eea;
|
||
}
|
||
|
||
.category-icon {
|
||
font-size: 32rpx;
|
||
}
|
||
|
||
.category-name {
|
||
font-size: 24rpx;
|
||
color: #333;
|
||
font-weight: 400;
|
||
}
|
||
</style> |