Initial commit of akmon project
This commit is contained in:
889
pages/ec/activity/management.uvue
Normal file
889
pages/ec/activity/management.uvue
Normal file
@@ -0,0 +1,889 @@
|
||||
<!-- 养老管理系统 - 活动管理 -->
|
||||
<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>
|
||||
Reference in New Issue
Block a user