890 lines
22 KiB
Plaintext
890 lines
22 KiB
Plaintext
<!-- 养老管理系统 - 活动管理 -->
|
||
<template>
|
||
<view class="activity-management">
|
||
<view class="header">
|
||
<text class="title">活动管理</text>
|
||
<button class="add-btn" @click="showAddActivity">安排活动</button>
|
||
</view>
|
||
|
||
<!-- 筛选区域 -->
|
||
<view class="filter-section">
|
||
<view class="filter-item">
|
||
<text class="filter-label">类型:</text>
|
||
<picker-view class="picker" :value="selectedTypeIndex" @change="onTypeChange">
|
||
<picker-view-column>
|
||
<view v-for="(type, index) in typeOptions" :key="index" class="picker-item">
|
||
{{ type.label }}
|
||
</view>
|
||
</picker-view-column>
|
||
</picker-view>
|
||
</view>
|
||
<view class="filter-item">
|
||
<text class="filter-label">状态:</text>
|
||
<picker-view class="picker" :value="selectedStatusIndex" @change="onStatusChange">
|
||
<picker-view-column>
|
||
<view v-for="(status, index) in statusOptions" :key="index" class="picker-item">
|
||
{{ status.label }}
|
||
</view>
|
||
</picker-view-column>
|
||
</picker-view>
|
||
</view>
|
||
<view class="filter-item">
|
||
<text class="filter-label">日期:</text>
|
||
<input class="date-picker" :value="selectedDate" readonly @click="showDatePicker = true" placeholder="选择日期" />
|
||
|
||
</view>
|
||
<l-popup v-if="showDatePicker" v-model="showDatePicker" position="center" :closeable="true" @click-close="showDatePicker = false">
|
||
<l-date-time-picker
|
||
v-model="tempDate"
|
||
title="选择日期"
|
||
mode="年月日"
|
||
:start="'1920-01-01'"
|
||
:end="new Date().toISOString().split('T')[0]"
|
||
confirm-btn="确认"
|
||
cancel-btn="取消"
|
||
@confirm="onDateConfirm"
|
||
@cancel="showDatePicker = false"
|
||
/>
|
||
</l-popup>
|
||
<button class="search-btn" @click="searchActivities">搜索</button>
|
||
</view>
|
||
|
||
<!-- 活动列表 -->
|
||
<view class="activities-list">
|
||
<view v-for="activity in activities" :key="activity.id" class="activity-item" @click="viewActivityDetail(activity)">
|
||
<view class="activity-header">
|
||
<text class="activity-name">{{ activity.activity_name }}</text>
|
||
<view class="status-badge" :class="getStatusClass(activity.status)">
|
||
<text class="status-text">{{ getStatusText(activity.status) }}</text>
|
||
</view>
|
||
</view>
|
||
<view class="activity-info">
|
||
<text class="activity-type">{{ getTypeText(activity.activity_type) }}</text>
|
||
<text class="activity-location">地点: {{ activity.location ?? '未设置' }}</text>
|
||
<text class="activity-instructor">指导员: {{ activity.instructor ?? '未分配' }}</text>
|
||
</view>
|
||
<view class="activity-time">
|
||
<text class="time-text">开始: {{ formatDateTime(activity.start_time) }}</text>
|
||
<text class="time-text">结束: {{ formatDateTime(activity.end_time) }}</text>
|
||
</view>
|
||
<view class="activity-participants">
|
||
<text class="participants-text">最大参与人数: {{ activity.max_participants ?? '不限' }}</text>
|
||
<text class="participants-count">当前参与: {{ getParticipantsCount(activity.id) }} 人</text>
|
||
</view>
|
||
<view class="activity-actions">
|
||
<button class="action-btn edit-btn" @click.stop="editActivity(activity)">编辑</button>
|
||
<button class="action-btn participants-btn" @click.stop="manageParticipants(activity)">参与管理</button>
|
||
<button class="action-btn cancel-btn" v-if="activity.status === 'scheduled'" @click.stop="cancelActivity(activity)">取消</button>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 添加/编辑活动弹窗 -->
|
||
<view v-if="showActivityModal" class="modal-overlay" @click="closeActivityModal">
|
||
<view class="modal-content" @click.stop="">
|
||
<view class="modal-header">
|
||
<text class="modal-title">{{ isEditMode ? '编辑活动' : '安排活动' }}</text>
|
||
<button class="close-btn" @click="closeActivityModal">×</button>
|
||
</view>
|
||
<view class="modal-body">
|
||
<view class="form-group">
|
||
<text class="form-label">活动名称:</text>
|
||
<input class="form-input" v-model="formData.activity_name" placeholder="请输入活动名称" />
|
||
</view>
|
||
<view class="form-group">
|
||
<text class="form-label">活动类型:</text>
|
||
<picker-view class="form-picker" :value="formData.typeIndex" @change="onFormTypeChange">
|
||
<picker-view-column>
|
||
<view v-for="(type, index) in activityTypes" :key="index" class="picker-item">
|
||
{{ type.label }}
|
||
</view>
|
||
</picker-view-column>
|
||
</picker-view>
|
||
</view>
|
||
<view class="form-group">
|
||
<text class="form-label">活动描述:</text>
|
||
<textarea class="form-textarea" v-model="formData.description" placeholder="请输入活动描述"></textarea>
|
||
</view>
|
||
<view class="form-group">
|
||
<text class="form-label">活动地点:</text>
|
||
<input class="form-input" v-model="formData.location" placeholder="请输入活动地点" />
|
||
</view>
|
||
<view class="form-group">
|
||
<text class="form-label">开始时间:</text>
|
||
<input class="form-input" :value="formData.start_time" readonly @click="showStartTimePicker = true" placeholder="选择开始时间" />
|
||
<l-date-time-picker
|
||
v-if="showStartTimePicker"
|
||
v-model="tempStartTime"
|
||
title="选择开始时间"
|
||
mode="年月日 时分"
|
||
confirm-btn="确认"
|
||
cancel-btn="取消"
|
||
@confirm="onStartTimeConfirm"
|
||
@cancel="showStartTimePicker = false"
|
||
/>
|
||
</view>
|
||
<view class="form-group">
|
||
<text class="form-label">结束时间:</text>
|
||
<input class="form-input" :value="formData.end_time" readonly @click="showEndTimePicker = true" placeholder="选择结束时间" />
|
||
<l-date-time-picker
|
||
v-if="showEndTimePicker"
|
||
v-model="tempEndTime"
|
||
title="选择结束时间"
|
||
mode="年月日 时分"
|
||
confirm-btn="确认"
|
||
cancel-btn="取消"
|
||
@confirm="onEndTimeConfirm"
|
||
@cancel="showEndTimePicker = false"
|
||
/>
|
||
</view>
|
||
<view class="form-group">
|
||
<text class="form-label">最大参与人数:</text>
|
||
<input class="form-input" v-model="formData.max_participants" type="number" placeholder="不限制请留空" />
|
||
</view>
|
||
<view class="form-group">
|
||
<text class="form-label">指导员:</text>
|
||
<input class="form-input" v-model="formData.instructor" placeholder="请输入指导员姓名" />
|
||
</view>
|
||
<view class="form-group">
|
||
<text class="form-label">参与要求:</text>
|
||
<textarea class="form-textarea" v-model="formData.requirements" placeholder="请输入参与要求"></textarea>
|
||
</view>
|
||
<view class="form-group">
|
||
<text class="form-label">所需物品:</text>
|
||
<textarea class="form-textarea" v-model="formData.materials_needed" placeholder="请输入所需物品"></textarea>
|
||
</view>
|
||
</view>
|
||
<view class="modal-footer">
|
||
<button class="cancel-btn-modal" @click="closeActivityModal">取消</button>
|
||
<button class="save-btn" @click="saveActivity">保存</button>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup lang="uts">
|
||
import { ref, onMounted } from 'vue'
|
||
import type { Activity } from '../types.uts'
|
||
import { formatDateTime, getStatusClass, formatDate } from '../types.uts'
|
||
import supa from '@/components/supadb/aksupainstance.uts'
|
||
|
||
|
||
// 响应式数据
|
||
const activities = ref<Activity[]>([])
|
||
const participantsCountMap = ref<Map<string, number>>(new Map())
|
||
|
||
// 筛选相关
|
||
const selectedTypeIndex = ref([0])
|
||
const selectedStatusIndex = ref([0])
|
||
const selectedDate = ref('')
|
||
const showDatePicker = ref(false)
|
||
const tempDate = ref('')
|
||
|
||
const typeOptions = [
|
||
{ value: 'all', label: '全部类型' },
|
||
{ value: 'recreation', label: '娱乐活动' },
|
||
{ value: 'therapy', label: '治疗活动' },
|
||
{ value: 'education', label: '教育活动' },
|
||
{ value: 'social', label: '社交活动' },
|
||
{ value: 'exercise', label: '运动活动' }
|
||
]
|
||
|
||
const statusOptions = [
|
||
{ value: 'all', label: '全部状态' },
|
||
{ value: 'scheduled', label: '已安排' },
|
||
{ value: 'in_progress', label: '进行中' },
|
||
{ value: 'completed', label: '已完成' },
|
||
{ value: 'cancelled', label: '已取消' }
|
||
]
|
||
|
||
const activityTypes = [
|
||
{ value: 'recreation', label: '娱乐活动' },
|
||
{ value: 'therapy', label: '治疗活动' },
|
||
{ value: 'education', label: '教育活动' },
|
||
{ value: 'social', label: '社交活动' },
|
||
{ value: 'exercise', label: '运动活动' }
|
||
]
|
||
|
||
// 弹窗相关
|
||
const showActivityModal = ref(false)
|
||
const isEditMode = ref(false)
|
||
const currentActivityId = ref<string | null>(null)
|
||
|
||
// 表单数据
|
||
const formData = ref({
|
||
activity_name: '',
|
||
typeIndex: [0],
|
||
description: '',
|
||
location: '',
|
||
start_time: '',
|
||
end_time: '',
|
||
max_participants: '',
|
||
instructor: '',
|
||
requirements: '',
|
||
materials_needed: '' })
|
||
|
||
// 时间选择器相关
|
||
const showStartTimePicker = ref(false)
|
||
const showEndTimePicker = ref(false)
|
||
const tempStartTime = ref('')
|
||
const tempEndTime = ref('')
|
||
|
||
// 加载数据
|
||
async function loadData(): Promise<void> {
|
||
try {
|
||
await Promise.all([
|
||
loadActivities(),
|
||
loadParticipantsCounts()
|
||
])
|
||
} catch (error) {
|
||
console.error('加载数据失败:', error)
|
||
uni.showToast({
|
||
title: '加载数据失败',
|
||
icon: 'error'
|
||
})
|
||
}
|
||
} // 页面加载
|
||
onLoad(() => {
|
||
const today = new Date()
|
||
selectedDate.value = formatDate(today.toISOString())
|
||
loadData()
|
||
})
|
||
|
||
// 加载活动列表
|
||
async function loadActivities(): Promise<void> {
|
||
// 构建查询条件
|
||
const filters: any[] = []
|
||
if (selectedTypeIndex.value[0] > 0) {
|
||
const selectedType = typeOptions[selectedTypeIndex.value[0]]
|
||
filters.push({ key: 'activity_type', value: selectedType.value })
|
||
}
|
||
if (selectedStatusIndex.value[0] > 0) {
|
||
const selectedStatus = statusOptions[selectedStatusIndex.value[0]]
|
||
filters.push({ key: 'status', value: selectedStatus.value })
|
||
}
|
||
if (selectedDate.value !== '') {
|
||
filters.push({ key: 'start_time', date: selectedDate.value })
|
||
}
|
||
|
||
let query = supa.from('ec_activities').select('*')
|
||
for (let i = 0; i < filters.length; i++) {
|
||
const f = filters[i]
|
||
if (f.key === 'start_time' && f.date) {
|
||
// 日期筛选,start_time 字段只保留当天
|
||
query = query.gte('start_time', `${f.date} 00:00:00`).lte('start_time', `${f.date} 23:59:59`)
|
||
} else {
|
||
query = query.eq(f.key, f.value)
|
||
}
|
||
}
|
||
query = query.order('start_time', { ascending: false })
|
||
const result = await query.executeAs<Activity>()
|
||
activities.value = result.data
|
||
}
|
||
// 加载参与人数统计
|
||
async function loadParticipantsCounts(): Promise<void> {
|
||
const result = await supa
|
||
.from('ec_activity_participations')
|
||
.select('activity_id')
|
||
.eq('participation_status', 'registered')
|
||
.executeAs<ActivityParticipation>()
|
||
|
||
const countMap = new Map<string, number>()
|
||
if (result && Array.isArray(result)) {
|
||
for (let i = 0; i < result.length; i++) {
|
||
const id = result[i].activity_id
|
||
if (id) {
|
||
countMap.set(id, (countMap.get(id) ?? 0) + 1)
|
||
}
|
||
}
|
||
}
|
||
participantsCountMap.value = countMap
|
||
}
|
||
|
||
// 获取参与人数
|
||
function getParticipantsCount(activityId: string): number {
|
||
return participantsCountMap.value.get(activityId) ?? 0
|
||
}
|
||
|
||
// 获取类型文本
|
||
function getTypeText(type: string | null): string {
|
||
if (type === null) return '其他'
|
||
const typeMap: Record<string, string> = {
|
||
'recreation': '娱乐活动',
|
||
'therapy': '治疗活动',
|
||
'education': '教育活动',
|
||
'social': '社交活动',
|
||
'exercise': '运动活动'
|
||
}
|
||
return typeMap[type] ?? type
|
||
}
|
||
|
||
// 获取状态文本
|
||
function getStatusText(status: string): string {
|
||
const statusMap: Record<string, string> = {
|
||
'scheduled': '已安排',
|
||
'in_progress': '进行中',
|
||
'completed': '已完成',
|
||
'cancelled': '已取消'
|
||
}
|
||
return statusMap[status] ?? status
|
||
}
|
||
|
||
// 格式化日期
|
||
function formatDate(dateStr: string): string {
|
||
if (dateStr === '') return ''
|
||
const date = new Date(dateStr)
|
||
const year = date.getFullYear()
|
||
const month = (date.getMonth() + 1).toString().padStart(2, '0')
|
||
const day = date.getDate().toString().padStart(2, '0')
|
||
return `${year}-${month}-${day}`
|
||
}
|
||
|
||
// 筛选事件
|
||
function onTypeChange(e: any): void {
|
||
selectedTypeIndex.value = e.detail.value
|
||
}
|
||
|
||
function onStatusChange(e: any): void {
|
||
selectedStatusIndex.value = e.detail.value
|
||
}
|
||
|
||
function onDateChange(date: string): void {
|
||
selectedDate.value = date
|
||
}
|
||
|
||
function onDateConfirm(date: string): void {
|
||
selectedDate.value = date
|
||
showDatePicker.value = false
|
||
}
|
||
|
||
// 搜索活动
|
||
function searchActivities(): void {
|
||
loadActivities()
|
||
}
|
||
|
||
// 查看活动详情
|
||
function viewActivityDetail(activity: Activity): void {
|
||
uni.navigateTo({
|
||
url: `/pages/ec/activity/detail?id=${activity.id}`
|
||
})
|
||
}
|
||
|
||
// 编辑活动
|
||
function editActivity(activity: Activity): void {
|
||
isEditMode.value = true
|
||
currentActivityId.value = activity.id
|
||
|
||
// 填充表单数据
|
||
const typeIndex = activityTypes.findIndex(type => type.value === activity.activity_type)
|
||
const startDateTime = activity.start_time ? new Date(activity.start_time) : new Date()
|
||
const endDateTime = activity.end_time ? new Date(activity.end_time) : new Date()
|
||
|
||
formData.value = {
|
||
activity_name: activity.activity_name,
|
||
typeIndex: [typeIndex > 0 ? typeIndex : 0],
|
||
description: activity.description ?? '',
|
||
location: activity.location ?? '',
|
||
start_time: startDateTime.getHours().toString().padStart(2, '0') + ':' + startDateTime.getMinutes().toString().padStart(2, '0'),
|
||
end_time: endDateTime.getHours().toString().padStart(2, '0') + ':' + endDateTime.getMinutes().toString().padStart(2, '0'),
|
||
max_participants: activity.max_participants?.toString() ?? '',
|
||
instructor: activity.instructor ?? '',
|
||
requirements: activity.requirements ?? '',
|
||
materials_needed: activity.materials_needed ?? ''
|
||
}
|
||
|
||
showActivityModal.value = true
|
||
}
|
||
|
||
// 管理参与者
|
||
function manageParticipants(activity: Activity): void {
|
||
uni.navigateTo({
|
||
url: `/pages/ec/activity/participants?id=${activity.id}`
|
||
})
|
||
}
|
||
|
||
// 取消活动
|
||
async function cancelActivity(activity: Activity): Promise<void> {
|
||
uni.showModal({
|
||
title: '确认取消',
|
||
content: '确定要取消这个活动吗?',
|
||
success: async (res) => {
|
||
if (res.confirm) {
|
||
try {
|
||
await supa
|
||
.from('ec_activities')
|
||
.update({ status: 'cancelled', updated_at: new Date().toISOString() })
|
||
.eq('id', activity.id)
|
||
.executeAs<Activity>()
|
||
|
||
uni.showToast({
|
||
title: '取消成功',
|
||
icon: 'success'
|
||
})
|
||
loadActivities()
|
||
} catch (error) {
|
||
console.error('取消活动失败:', error)
|
||
uni.showToast({
|
||
title: '取消失败',
|
||
icon: 'error'
|
||
})
|
||
}
|
||
}
|
||
}
|
||
})
|
||
}
|
||
|
||
// 显示添加活动弹窗
|
||
function showAddActivity(): void {
|
||
isEditMode.value = false
|
||
currentActivityId.value = null
|
||
|
||
// 重置表单
|
||
const now = new Date()
|
||
const today = formatDate(now.toISOString())
|
||
const currentTime = now.getHours().toString().padStart(2, '0') + ':' + now.getMinutes().toString().padStart(2, '0')
|
||
|
||
formData.value = {
|
||
activity_name: '',
|
||
typeIndex: [0],
|
||
description: '',
|
||
location: '',
|
||
start_time: currentTime,
|
||
end_time: currentTime,
|
||
max_participants: '',
|
||
instructor: '',
|
||
requirements: '',
|
||
materials_needed: ''
|
||
}
|
||
|
||
showActivityModal.value = true
|
||
}
|
||
|
||
// 关闭弹窗
|
||
function closeActivityModal(): void {
|
||
showActivityModal.value = false
|
||
}
|
||
|
||
// 表单事件
|
||
function onFormTypeChange(e: any): void {
|
||
formData.value.typeIndex = e.detail.value
|
||
}
|
||
|
||
function onStartTimeConfirm(val: string): void {
|
||
formData.value.start_time = val
|
||
showStartTimePicker.value = false
|
||
}
|
||
function onEndTimeConfirm(val: string): void {
|
||
formData.value.end_time = val
|
||
showEndTimePicker.value = false
|
||
}
|
||
|
||
// 保存活动
|
||
async function saveActivity(): Promise<void> {
|
||
// 验证表单
|
||
if (formData.value.activity_name.trim() === '') {
|
||
uni.showToast({
|
||
title: '请输入活动名称',
|
||
icon: 'error'
|
||
})
|
||
return
|
||
}
|
||
|
||
try {
|
||
const selectedType = activityTypes[formData.value.typeIndex[0]]
|
||
const startDateTime = `${new Date().toISOString().split('T')[0]} ${formData.value.start_time}:00`
|
||
const endDateTime = `${new Date().toISOString().split('T')[0]} ${formData.value.end_time}:00`
|
||
|
||
if (isEditMode.value && currentActivityId.value !== null) {
|
||
// 更新活动(链式写法,移除SQL字符串,类型安全)
|
||
await supa
|
||
.from('ec_activities')
|
||
.update({
|
||
activity_name: formData.value.activity_name,
|
||
activity_type: selectedType.value,
|
||
description: formData.value.description,
|
||
location: formData.value.location,
|
||
start_time: startDateTime,
|
||
end_time: endDateTime,
|
||
max_participants: formData.value.max_participants ? Number(formData.value.max_participants) : null,
|
||
instructor: formData.value.instructor,
|
||
requirements: formData.value.requirements,
|
||
materials_needed: formData.value.materials_needed,
|
||
updated_at: new Date().toISOString()
|
||
})
|
||
.eq('id', currentActivityId.value)
|
||
.execute()
|
||
} else {
|
||
// 新增活动
|
||
// 先查 facility_id
|
||
const facilities = await supa.from('ec_facilities').select('id').limit(1).executeAs<UTSJSONObject>()
|
||
const facilityId = facilities && facilities.length > 0 ? facilities[0].id : ''
|
||
await supa
|
||
.from('ec_activities')
|
||
.insert([{
|
||
facility_id: facilityId,
|
||
activity_name: formData.value.activity_name,
|
||
activity_type: selectedType.value,
|
||
description: formData.value.description,
|
||
location: formData.value.location,
|
||
start_time: startDateTime,
|
||
end_time: endDateTime,
|
||
max_participants: formData.value.max_participants ? Number(formData.value.max_participants) : null,
|
||
instructor: formData.value.instructor,
|
||
requirements: formData.value.requirements,
|
||
materials_needed: formData.value.materials_needed,
|
||
status: 'scheduled'
|
||
}])
|
||
.execute()
|
||
}
|
||
|
||
uni.showToast({
|
||
title: '保存成功',
|
||
icon: 'success'
|
||
})
|
||
|
||
closeActivityModal()
|
||
loadActivities()
|
||
} catch (error) {
|
||
console.error('保存失败:', error)
|
||
uni.showToast({
|
||
title: '保存失败',
|
||
icon: 'error'
|
||
})
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style scoped>
|
||
.activity-management {
|
||
padding: 20px;
|
||
background-color: #f5f5f5;
|
||
min-height: 100vh;
|
||
}
|
||
|
||
.header {
|
||
display: flex;
|
||
flex-direction: row;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.title {
|
||
font-size: 24px;
|
||
font-weight: bold;
|
||
color: #333;
|
||
}
|
||
|
||
.add-btn {
|
||
background-color: #2196f3;
|
||
color: white;
|
||
border: none;
|
||
border-radius: 6px;
|
||
padding: 10px 20px;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.filter-section {
|
||
background-color: white;
|
||
border-radius: 12px;
|
||
padding: 20px;
|
||
margin-bottom: 20px;
|
||
display: flex;
|
||
flex-direction: row;
|
||
align-items: center;
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.filter-item {
|
||
display: flex;
|
||
flex-direction: row;
|
||
align-items: center;
|
||
margin-right: 20px;
|
||
margin-bottom: 10px;
|
||
}
|
||
|
||
.filter-label {
|
||
font-size: 14px;
|
||
color: #666;
|
||
margin-right: 10px;
|
||
}
|
||
|
||
.picker {
|
||
width: 120px;
|
||
height: 40px;
|
||
border: 1px solid #ddd;
|
||
border-radius: 6px;
|
||
}
|
||
|
||
.picker-item {
|
||
padding: 10px;
|
||
text-align: center;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.date-picker {
|
||
width: 140px;
|
||
}
|
||
|
||
.search-btn {
|
||
background-color: #4caf50;
|
||
color: white;
|
||
border: none;
|
||
border-radius: 6px;
|
||
padding: 10px 20px;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.activities-list {
|
||
background-color: white;
|
||
border-radius: 12px;
|
||
padding: 20px;
|
||
}
|
||
|
||
.activity-item {
|
||
padding: 15px 0;
|
||
border-bottom: 1px solid #f0f0f0;
|
||
}
|
||
|
||
.activity-header {
|
||
display: flex;
|
||
flex-direction: row;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 10px;
|
||
}
|
||
|
||
.activity-name {
|
||
font-size: 18px;
|
||
font-weight: bold;
|
||
color: #333;
|
||
}
|
||
|
||
.status-badge {
|
||
padding: 4px 8px;
|
||
border-radius: 4px;
|
||
font-size: 12px;
|
||
}
|
||
|
||
.status-text {
|
||
color: white;
|
||
}
|
||
|
||
.status-scheduled {
|
||
background-color: #ff9800;
|
||
}
|
||
|
||
.status-progress {
|
||
background-color: #2196f3;
|
||
}
|
||
|
||
.status-completed {
|
||
background-color: #4caf50;
|
||
}
|
||
|
||
.status-cancelled {
|
||
background-color: #f44336;
|
||
}
|
||
|
||
.activity-info, .activity-time, .activity-participants {
|
||
margin-bottom: 10px;
|
||
}
|
||
|
||
.activity-type, .activity-location, .activity-instructor,
|
||
.time-text, .participants-text, .participants-count {
|
||
font-size: 14px;
|
||
color: #666;
|
||
margin-bottom: 5px;
|
||
}
|
||
|
||
.activity-actions {
|
||
display: flex;
|
||
flex-direction: row;
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.action-btn {
|
||
border: none;
|
||
border-radius: 4px;
|
||
padding: 6px 12px;
|
||
font-size: 12px;
|
||
margin-right: 10px;
|
||
margin-bottom: 5px;
|
||
}
|
||
|
||
.edit-btn {
|
||
background-color: #ff9800;
|
||
color: white;
|
||
}
|
||
|
||
.participants-btn {
|
||
background-color: #2196f3;
|
||
color: white;
|
||
}
|
||
|
||
.cancel-btn {
|
||
background-color: #f44336;
|
||
color: white;
|
||
}
|
||
|
||
.modal-overlay {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
background-color: rgba(0,0,0,0.5);
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
z-index: 1000;
|
||
}
|
||
|
||
.modal-content {
|
||
background-color: white;
|
||
border-radius: 12px;
|
||
width: 90%;
|
||
max-width: 600px;
|
||
max-height: 80%;
|
||
}
|
||
|
||
.modal-header {
|
||
display: flex;
|
||
flex-direction: row;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 20px;
|
||
border-bottom: 1px solid #f0f0f0;
|
||
}
|
||
|
||
.modal-title {
|
||
font-size: 18px;
|
||
font-weight: bold;
|
||
color: #333;
|
||
}
|
||
|
||
.close-btn {
|
||
background: none;
|
||
border: none;
|
||
font-size: 24px;
|
||
color: #999;
|
||
}
|
||
|
||
.modal-body {
|
||
padding: 20px;
|
||
max-height: 500px;
|
||
overflow-y: auto;
|
||
}
|
||
|
||
.form-group {
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.form-label {
|
||
font-size: 14px;
|
||
color: #333;
|
||
margin-bottom: 8px;
|
||
display: block;
|
||
}
|
||
|
||
.form-input {
|
||
width: 100%;
|
||
height: 40px;
|
||
border: 1px solid #ddd;
|
||
border-radius: 6px;
|
||
padding: 0 12px;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.form-picker {
|
||
width: 100%;
|
||
height: 40px;
|
||
border: 1px solid #ddd;
|
||
border-radius: 6px;
|
||
}
|
||
|
||
.form-textarea {
|
||
width: 100%;
|
||
height: 80px;
|
||
border: 1px solid #ddd;
|
||
border-radius: 6px;
|
||
padding: 12px;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.datetime-row {
|
||
display: flex;
|
||
flex-direction: row;
|
||
}
|
||
|
||
.date-input, .time-input {
|
||
flex: 1;
|
||
margin-right: 10px;
|
||
}
|
||
|
||
.time-input {
|
||
margin-right: 0;
|
||
}
|
||
|
||
.modal-footer {
|
||
display: flex;
|
||
flex-direction: row;
|
||
justify-content: flex-end;
|
||
padding: 20px;
|
||
border-top: 1px solid #f0f0f0;
|
||
}
|
||
|
||
.cancel-btn-modal, .save-btn {
|
||
border: none;
|
||
border-radius: 6px;
|
||
padding: 10px 20px;
|
||
font-size: 14px;
|
||
margin-left: 10px;
|
||
}
|
||
|
||
.cancel-btn-modal {
|
||
background-color: #f5f5f5;
|
||
color: #666;
|
||
}
|
||
|
||
.save-btn {
|
||
background-color: #2196f3;
|
||
color: white;
|
||
}
|
||
|
||
/* 小屏幕适配 */
|
||
@media (max-width: 768px) {
|
||
.activity-management {
|
||
padding: 15px;
|
||
}
|
||
|
||
.filter-section {
|
||
flex-direction: column;
|
||
align-items: stretch;
|
||
}
|
||
|
||
.filter-item {
|
||
margin-right: 0;
|
||
justify-content: space-between;
|
||
}
|
||
|
||
.picker {
|
||
width: 150px;
|
||
}
|
||
|
||
.modal-content {
|
||
width: 95%;
|
||
}
|
||
|
||
.datetime-row {
|
||
flex-direction: column;
|
||
}
|
||
|
||
.date-input, .time-input {
|
||
margin-right: 0;
|
||
margin-bottom: 10px;
|
||
}
|
||
}
|
||
</style>
|