¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <!-- 顶鍿使 --> |
| | | <div class="header-actions"> |
| | | <div class="left-actions"> |
| | | <el-select v-model="currentLevel" placeholder="éæ©è®¡å级å«" style="width: 150px" @change="handleLevelChange"> |
| | | <el-option label="个人计å" value="personal" /> |
| | | <el-option label="å°ç»è®¡å" value="group" /> |
| | | <el-option label="é¨é¨è®¡å" value="department" /> |
| | | <el-option label="å
¬å¸è®¡å" value="company" /> |
| | | </el-select> |
| | | <el-select v-model="currentPeriod" placeholder="éæ©æ¶é´å¨æ" style="width: 120px; margin-left: 10px" @change="handlePeriodChange"> |
| | | <el-option label="å¨è®¡å" value="week" /> |
| | | <el-option label="æè®¡å" value="month" /> |
| | | <el-option label="年计å" value="year" /> |
| | | </el-select> |
| | | <el-date-picker |
| | | v-model="currentDate" |
| | | :type="datePickerType" |
| | | placeholder="éæ©æ¥æ" |
| | | style="width: 180px; margin-left: 10px" |
| | | @change="handleDateChange" |
| | | /> |
| | | </div> |
| | | <div class="right-actions"> |
| | | <el-button type="primary" @click="handleAddPlan">æ°å¢è®¡å</el-button> |
| | | <el-button @click="handleExport">导åºè®¡å</el-button> |
| | | <el-button @click="handleShare">å
±äº«è®¡å</el-button> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- è®¡åæ¦è§å¡ç --> |
| | | <div class="overview-cards"> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="6"> |
| | | <el-card class="overview-card"> |
| | | <div class="card-content"> |
| | | <div class="card-icon personal"> |
| | | <el-icon><User /></el-icon> |
| | | </div> |
| | | <div class="card-info"> |
| | | <div class="card-title">个人计å</div> |
| | | <div class="card-number">{{ overviewData.personal.total }}</div> |
| | | <div class="card-progress"> |
| | | <el-progress :percentage="overviewData.personal.completion" :stroke-width="6" /> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </el-card> |
| | | </el-col> |
| | | <el-col :span="6"> |
| | | <el-card class="overview-card"> |
| | | <div class="card-content"> |
| | | <div class="card-icon group"> |
| | | <el-icon><UserFilled /></el-icon> |
| | | </div> |
| | | <div class="card-info"> |
| | | <div class="card-title">å°ç»è®¡å</div> |
| | | <div class="card-number">{{ overviewData.group.total }}</div> |
| | | <div class="card-progress"> |
| | | <el-progress :percentage="overviewData.group.completion" :stroke-width="6" /> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </el-card> |
| | | </el-col> |
| | | <el-col :span="6"> |
| | | <el-card class="overview-card"> |
| | | <div class="card-content"> |
| | | <div class="card-icon department"> |
| | | <el-icon><OfficeBuilding /></el-icon> |
| | | </div> |
| | | <div class="card-info"> |
| | | <div class="card-title">é¨é¨è®¡å</div> |
| | | <div class="card-number">{{ overviewData.department.total }}</div> |
| | | <div class="card-progress"> |
| | | <el-progress :percentage="overviewData.department.completion" :stroke-width="6" /> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </el-card> |
| | | </el-col> |
| | | <el-col :span="6"> |
| | | <el-card class="overview-card"> |
| | | <div class="card-content"> |
| | | <div class="card-icon company"> |
| | | <el-icon><House /></el-icon> |
| | | </div> |
| | | <div class="card-info"> |
| | | <div class="card-title">å
¬å¸è®¡å</div> |
| | | <div class="card-number">{{ overviewData.company.total }}</div> |
| | | <div class="card-progress"> |
| | | <el-progress :percentage="overviewData.company.completion" :stroke-width="6" /> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </el-card> |
| | | </el-col> |
| | | </el-row> |
| | | </div> |
| | | |
| | | <!-- 计åå表 --> |
| | | <div class="plan-content"> |
| | | <el-card> |
| | | <template #header> |
| | | <div class="card-header"> |
| | | <span>{{ getCurrentLevelText() }} - {{ getCurrentPeriodText() }}</span> |
| | | <div> |
| | | <el-button size="small" @click="handleRefresh">å·æ°</el-button> |
| | | <el-button size="small" @click="handleFilter">çé</el-button> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <div class="plan-list"> |
| | | <div v-for="plan in planList" :key="plan.id" class="plan-item"> |
| | | <div class="plan-header"> |
| | | <div class="plan-title"> |
| | | <el-tag :type="getPriorityType(plan.priority)" size="small">{{ getPriorityText(plan.priority) }}</el-tag> |
| | | <span class="title-text">{{ plan.title }}</span> |
| | | </div> |
| | | <div class="plan-actions"> |
| | | <el-button size="small" @click="handleEditPlan(plan)">ç¼è¾</el-button> |
| | | <el-button size="small" @click="handleViewDetail(plan)">详æ
</el-button> |
| | | <el-dropdown @command="handleMoreAction"> |
| | | <el-button size="small"> |
| | | æ´å¤<el-icon class="el-icon--right"><ArrowDown /></el-icon> |
| | | </el-button> |
| | | <template #dropdown> |
| | | <el-dropdown-menu> |
| | | <el-dropdown-item command="share">å
񄧮</el-dropdown-item> |
| | | <el-dropdown-item command="copy">å¤å¶</el-dropdown-item> |
| | | <el-dropdown-item command="delete" divided>å é¤</el-dropdown-item> |
| | | </el-dropdown-menu> |
| | | </template> |
| | | </el-dropdown> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="plan-content"> |
| | | <div class="plan-description">{{ plan.description }}</div> |
| | | <div class="plan-meta"> |
| | | <div class="meta-item"> |
| | | <el-icon><Calendar /></el-icon> |
| | | <span>{{ plan.startDate }} - {{ plan.endDate }}</span> |
| | | </div> |
| | | <div class="meta-item"> |
| | | <el-icon><User /></el-icon> |
| | | <span>{{ plan.assignee }}</span> |
| | | </div> |
| | | <div class="meta-item"> |
| | | <el-icon><Clock /></el-icon> |
| | | <span>è¿åº¦: {{ plan.progress }}%</span> |
| | | </div> |
| | | <div class="meta-item"> |
| | | <el-icon><Flag /></el-icon> |
| | | <span>{{ getStatusText(plan.status) }}</span> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="plan-progress"> |
| | | <el-progress |
| | | :percentage="plan.progress" |
| | | :color="getProgressColor(plan.progress)" |
| | | :stroke-width="8" |
| | | /> |
| | | </div> |
| | | |
| | | <div class="plan-tags"> |
| | | <el-tag v-for="tag in plan.tags" :key="tag" size="small" style="margin-right: 5px"> |
| | | {{ tag }} |
| | | </el-tag> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </el-card> |
| | | </div> |
| | | |
| | | <!-- æ°å¢/ç¼è¾è®¡åå¯¹è¯æ¡ --> |
| | | <el-dialog |
| | | v-model="planDialogVisible" |
| | | :title="dialogTitle" |
| | | width="600px" |
| | | @close="handleDialogClose" |
| | | > |
| | | <el-form :model="planForm" :rules="planRules" ref="planFormRef" label-width="100px"> |
| | | <el-form-item label="è®¡åæ é¢" prop="title"> |
| | | <el-input v-model="planForm.title" placeholder="请è¾å
¥è®¡åæ é¢" /> |
| | | </el-form-item> |
| | | <el-form-item label="计åæè¿°" prop="description"> |
| | | <el-input |
| | | v-model="planForm.description" |
| | | type="textarea" |
| | | :rows="3" |
| | | placeholder="请è¾å
¥è®¡åæè¿°" |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item label="计å级å«" prop="level"> |
| | | <el-select v-model="planForm.level" placeholder="éæ©è®¡å级å«" style="width: 100%"> |
| | | <el-option label="个人计å" value="personal" /> |
| | | <el-option label="å°ç»è®¡å" value="group" /> |
| | | <el-option label="é¨é¨è®¡å" value="department" /> |
| | | <el-option label="å
¬å¸è®¡å" value="company" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="æ¶é´å¨æ" prop="period"> |
| | | <el-select v-model="planForm.period" placeholder="éæ©æ¶é´å¨æ" style="width: 100%"> |
| | | <el-option label="å¨è®¡å" value="week" /> |
| | | <el-option label="æè®¡å" value="month" /> |
| | | <el-option label="年计å" value="year" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="å¼å§æ¶é´" prop="startDate"> |
| | | <el-date-picker |
| | | v-model="planForm.startDate" |
| | | type="date" |
| | | placeholder="éæ©å¼å§æ¶é´" |
| | | style="width: 100%" |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item label="ç»ææ¶é´" prop="endDate"> |
| | | <el-date-picker |
| | | v-model="planForm.endDate" |
| | | type="date" |
| | | placeholder="éæ©ç»ææ¶é´" |
| | | style="width: 100%" |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item label="è´è´£äºº" prop="assignee"> |
| | | <el-input v-model="planForm.assignee" placeholder="请è¾å
¥è´è´£äºº" /> |
| | | </el-form-item> |
| | | <el-form-item label="ä¼å
级" prop="priority"> |
| | | <el-select v-model="planForm.priority" placeholder="éæ©ä¼å
级" style="width: 100%"> |
| | | <el-option label="é«" value="high" /> |
| | | <el-option label="ä¸" value="medium" /> |
| | | <el-option label="ä½" value="low" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="æ ç¾"> |
| | | <el-input v-model="planForm.tags" placeholder="请è¾å
¥æ ç¾ï¼ç¨éå·åé" /> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | | <el-button @click="planDialogVisible = false">åæ¶</el-button> |
| | | <el-button type="primary" @click="handleSavePlan">ä¿å</el-button> |
| | | </span> |
| | | </template> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, computed, onMounted } from 'vue' |
| | | import { ElMessage, ElMessageBox } from 'element-plus' |
| | | import { |
| | | User, |
| | | UserFilled, |
| | | OfficeBuilding, |
| | | House, |
| | | Calendar, |
| | | Clock, |
| | | Flag, |
| | | ArrowDown |
| | | } from '@element-plus/icons-vue' |
| | | |
| | | // ååºå¼æ°æ® |
| | | const currentLevel = ref('personal') |
| | | const currentPeriod = ref('week') |
| | | const currentDate = ref(new Date()) |
| | | const planDialogVisible = ref(false) |
| | | const dialogTitle = ref('æ°å¢è®¡å') |
| | | const planFormRef = ref() |
| | | |
| | | // è¡¨åæ°æ® |
| | | const planForm = reactive({ |
| | | title: '', |
| | | description: '', |
| | | level: 'personal', |
| | | period: 'week', |
| | | startDate: '', |
| | | endDate: '', |
| | | assignee: '', |
| | | priority: 'medium', |
| | | tags: '' |
| | | }) |
| | | |
| | | // 表åéªè¯è§å |
| | | const planRules = { |
| | | title: [{ required: true, message: '请è¾å
¥è®¡åæ é¢', trigger: 'blur' }], |
| | | description: [{ required: true, message: '请è¾å
¥è®¡åæè¿°', trigger: 'blur' }], |
| | | level: [{ required: true, message: 'è¯·éæ©è®¡å级å«', trigger: 'change' }], |
| | | period: [{ required: true, message: 'è¯·éæ©æ¶é´å¨æ', trigger: 'change' }], |
| | | startDate: [{ required: true, message: 'è¯·éæ©å¼å§æ¶é´', trigger: 'change' }], |
| | | endDate: [{ required: true, message: 'è¯·éæ©ç»ææ¶é´', trigger: 'change' }], |
| | | assignee: [{ required: true, message: '请è¾å
¥è´è´£äºº', trigger: 'blur' }], |
| | | priority: [{ required: true, message: 'è¯·éæ©ä¼å
级', trigger: 'change' }] |
| | | } |
| | | |
| | | // æ¦è§æ°æ® |
| | | const overviewData = reactive({ |
| | | personal: { total: 12, completion: 75 }, |
| | | group: { total: 8, completion: 60 }, |
| | | department: { total: 15, completion: 45 }, |
| | | company: { total: 6, completion: 30 } |
| | | }) |
| | | |
| | | // 计ååè¡¨æ°æ® |
| | | const planList = ref([ |
| | | { |
| | | id: 1, |
| | | title: '产åéæ±åæ', |
| | | description: '对æ°äº§åè¿è¡è¯¦ç»çéæ±åæåå¸åºè°ç ï¼å¶å®äº§åè§åæ¹æ¡', |
| | | level: 'personal', |
| | | period: 'week', |
| | | startDate: '2025-01-15', |
| | | endDate: '2025-01-21', |
| | | assignee: 'éå¿å¼º', |
| | | priority: 'high', |
| | | status: 'in_progress', |
| | | progress: 80, |
| | | tags: ['产å', 'åæ', 'è°ç '] |
| | | }, |
| | | { |
| | | id: 2, |
| | | title: 'ææ¯æ¶æè®¾è®¡', |
| | | description: 'è®¾è®¡ç³»ç»ææ¯æ¶æï¼å
æ¬æ°æ®åºè®¾è®¡ãæ¥å£è®¾è®¡ç', |
| | | level: 'group', |
| | | period: 'month', |
| | | startDate: '2025-01-01', |
| | | endDate: '2025-01-31', |
| | | assignee: 'åé
å©·', |
| | | priority: 'high', |
| | | status: 'completed', |
| | | progress: 100, |
| | | tags: ['ææ¯', 'æ¶æ', '设计'] |
| | | }, |
| | | { |
| | | id: 3, |
| | | title: 'å¸åºæ¨å¹¿è®¡å', |
| | | description: 'å¶å®å¹´åº¦å¸åºæ¨å¹¿çç¥åè¥é计å', |
| | | level: 'department', |
| | | period: 'year', |
| | | startDate: '2025-01-01', |
| | | endDate: '2025-12-31', |
| | | assignee: 'ç建å½', |
| | | priority: 'medium', |
| | | status: 'not_started', |
| | | progress: 0, |
| | | tags: ['å¸åº', 'æ¨å¹¿', 'è¥é'] |
| | | }, |
| | | { |
| | | id: 4, |
| | | title: 'å¢é建设活å¨', |
| | | description: 'ç»ç»å¢é建设活å¨ï¼æåå¢éåèååå使ç', |
| | | level: 'company', |
| | | period: 'month', |
| | | startDate: '2025-01-15', |
| | | endDate: '2025-02-15', |
| | | assignee: '赵丽å', |
| | | priority: 'low', |
| | | status: 'in_progress', |
| | | progress: 30, |
| | | tags: ['å¢é', '建设', 'æ´»å¨'] |
| | | } |
| | | ]) |
| | | |
| | | // 计ç®å±æ§ |
| | | const datePickerType = computed(() => { |
| | | switch (currentPeriod.value) { |
| | | case 'week': |
| | | return 'week' |
| | | case 'month': |
| | | return 'month' |
| | | case 'year': |
| | | return 'year' |
| | | default: |
| | | return 'date' |
| | | } |
| | | }) |
| | | |
| | | // æ¹æ³ |
| | | const handleLevelChange = (value) => { |
| | | console.log('计å级å«åæ´:', value) |
| | | // è¿éå¯ä»¥æ ¹æ®çº§å«çéæ°æ® |
| | | } |
| | | |
| | | const handlePeriodChange = (value) => { |
| | | console.log('æ¶é´å¨æåæ´:', value) |
| | | // è¿éå¯ä»¥æ ¹æ®å¨æçéæ°æ® |
| | | } |
| | | |
| | | const handleDateChange = (value) => { |
| | | console.log('æ¥æåæ´:', value) |
| | | // è¿éå¯ä»¥æ ¹æ®æ¥æçéæ°æ® |
| | | } |
| | | |
| | | const handleAddPlan = () => { |
| | | dialogTitle.value = 'æ°å¢è®¡å' |
| | | planDialogVisible.value = true |
| | | // é置表å |
| | | Object.keys(planForm).forEach(key => { |
| | | planForm[key] = '' |
| | | }) |
| | | planForm.level = 'personal' |
| | | planForm.period = 'week' |
| | | planForm.priority = 'medium' |
| | | } |
| | | |
| | | const handleEditPlan = (plan) => { |
| | | dialogTitle.value = 'ç¼è¾è®¡å' |
| | | planDialogVisible.value = true |
| | | // å¡«å
è¡¨åæ°æ® |
| | | Object.keys(planForm).forEach(key => { |
| | | if (key === 'tags') { |
| | | planForm[key] = plan[key].join(', ') |
| | | } else { |
| | | planForm[key] = plan[key] |
| | | } |
| | | }) |
| | | } |
| | | |
| | | const handleViewDetail = (plan) => { |
| | | ElMessage.info(`æ¥ç计å详æ
: ${plan.title}`) |
| | | } |
| | | |
| | | const handleMoreAction = (command) => { |
| | | switch (command) { |
| | | case 'share': |
| | | ElMessage.success('计åå·²å
񄧮') |
| | | break |
| | | case 'copy': |
| | | ElMessage.success('计åå·²å¤å¶') |
| | | break |
| | | case 'delete': |
| | | ElMessageBox.confirm('ç¡®å®è¦å é¤è¿ä¸ªè®¡ååï¼', 'æç¤º', { |
| | | confirmButtonText: 'ç¡®å®', |
| | | cancelButtonText: 'åæ¶', |
| | | type: 'warning' |
| | | }).then(() => { |
| | | ElMessage.success('计åå·²å é¤') |
| | | }) |
| | | break |
| | | } |
| | | } |
| | | |
| | | const handleSavePlan = async () => { |
| | | try { |
| | | await planFormRef.value.validate() |
| | | ElMessage.success('计åä¿åæå') |
| | | planDialogVisible.value = false |
| | | } catch (error) { |
| | | console.log('表åéªè¯å¤±è´¥:', error) |
| | | } |
| | | } |
| | | |
| | | const handleDialogClose = () => { |
| | | planFormRef.value?.resetFields() |
| | | } |
| | | |
| | | const handleRefresh = () => { |
| | | ElMessage.success('æ°æ®å·²å·æ°') |
| | | } |
| | | |
| | | const handleFilter = () => { |
| | | ElMessage.info('æå¼çé颿¿') |
| | | } |
| | | |
| | | const handleExport = () => { |
| | | ElMessage.success('计å已导åº') |
| | | } |
| | | |
| | | const handleShare = () => { |
| | | ElMessage.success('计åå·²å
񄧮') |
| | | } |
| | | |
| | | const getCurrentLevelText = () => { |
| | | const levelMap = { |
| | | personal: '个人计å', |
| | | group: 'å°ç»è®¡å', |
| | | department: 'é¨é¨è®¡å', |
| | | company: 'å
¬å¸è®¡å' |
| | | } |
| | | return levelMap[currentLevel.value] || '个人计å' |
| | | } |
| | | |
| | | const getCurrentPeriodText = () => { |
| | | const periodMap = { |
| | | week: 'å¨è®¡å', |
| | | month: 'æè®¡å', |
| | | year: '年计å' |
| | | } |
| | | return periodMap[currentPeriod.value] || 'å¨è®¡å' |
| | | } |
| | | |
| | | const getPriorityType = (priority) => { |
| | | const typeMap = { |
| | | high: 'danger', |
| | | medium: 'warning', |
| | | low: 'info' |
| | | } |
| | | return typeMap[priority] || 'info' |
| | | } |
| | | |
| | | const getPriorityText = (priority) => { |
| | | const textMap = { |
| | | high: 'é«', |
| | | medium: 'ä¸', |
| | | low: 'ä½' |
| | | } |
| | | return textMap[priority] || 'ä¸' |
| | | } |
| | | |
| | | const getStatusText = (status) => { |
| | | const statusMap = { |
| | | not_started: 'æªå¼å§', |
| | | in_progress: 'è¿è¡ä¸', |
| | | completed: '已宿', |
| | | paused: 'å·²æå' |
| | | } |
| | | return statusMap[status] || 'æªç¥' |
| | | } |
| | | |
| | | const getProgressColor = (progress) => { |
| | | if (progress >= 80) return '#67C23A' |
| | | if (progress >= 50) return '#E6A23C' |
| | | return '#F56C6C' |
| | | } |
| | | |
| | | onMounted(() => { |
| | | console.log('å¤çº§è®¡å模æ¿é¡µé¢å·²å è½½') |
| | | }) |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .app-container { |
| | | padding: 20px; |
| | | background-color: #f5f5f5; |
| | | min-height: 100vh; |
| | | } |
| | | |
| | | .header-actions { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 20px; |
| | | background: white; |
| | | padding: 20px; |
| | | box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); |
| | | } |
| | | |
| | | .left-actions { |
| | | display: flex; |
| | | align-items: center; |
| | | } |
| | | |
| | | .right-actions { |
| | | display: flex; |
| | | gap: 10px; |
| | | } |
| | | |
| | | .overview-cards { |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | .overview-card { |
| | | height: 120px; |
| | | } |
| | | |
| | | .card-content { |
| | | display: flex; |
| | | align-items: center; |
| | | height: 100%; |
| | | } |
| | | |
| | | .card-icon { |
| | | width: 60px; |
| | | height: 60px; |
| | | border-radius: 50%; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | margin-right: 15px; |
| | | font-size: 24px; |
| | | color: white; |
| | | } |
| | | |
| | | .card-icon.personal { |
| | | background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
| | | } |
| | | |
| | | .card-icon.group { |
| | | background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); |
| | | } |
| | | |
| | | .card-icon.department { |
| | | background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%); |
| | | } |
| | | |
| | | .card-icon.company { |
| | | background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%); |
| | | } |
| | | |
| | | .card-info { |
| | | flex: 1; |
| | | } |
| | | |
| | | .card-title { |
| | | font-size: 14px; |
| | | color: #666; |
| | | margin-bottom: 5px; |
| | | } |
| | | |
| | | .card-number { |
| | | font-size: 24px; |
| | | font-weight: bold; |
| | | color: #333; |
| | | margin-bottom: 10px; |
| | | } |
| | | |
| | | .card-progress { |
| | | width: 100%; |
| | | } |
| | | |
| | | .plan-content { |
| | | background: white; |
| | | border-radius: 8px; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .card-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | font-weight: bold; |
| | | color: #333; |
| | | } |
| | | |
| | | .header-actions { |
| | | display: flex; |
| | | gap: 10px; |
| | | } |
| | | |
| | | .plan-list { |
| | | padding: 20px 0; |
| | | } |
| | | |
| | | .plan-item { |
| | | border: 1px solid #e4e7ed; |
| | | border-radius: 8px; |
| | | margin-bottom: 15px; |
| | | padding: 20px; |
| | | transition: all 0.3s ease; |
| | | } |
| | | |
| | | .plan-item:hover { |
| | | box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); |
| | | transform: translateY(-2px); |
| | | } |
| | | |
| | | .plan-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 15px; |
| | | } |
| | | |
| | | .plan-title { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 10px; |
| | | } |
| | | |
| | | .title-text { |
| | | font-size: 16px; |
| | | font-weight: bold; |
| | | color: #333; |
| | | } |
| | | |
| | | .plan-actions { |
| | | display: flex; |
| | | gap: 10px; |
| | | } |
| | | |
| | | .plan-content { |
| | | margin-bottom: 15px; |
| | | } |
| | | |
| | | .plan-description { |
| | | color: #666; |
| | | margin-bottom: 15px; |
| | | line-height: 1.6; |
| | | } |
| | | |
| | | .plan-meta { |
| | | display: flex; |
| | | flex-wrap: wrap; |
| | | gap: 20px; |
| | | margin-bottom: 15px; |
| | | } |
| | | |
| | | .meta-item { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 5px; |
| | | color: #666; |
| | | font-size: 14px; |
| | | } |
| | | |
| | | .plan-progress { |
| | | margin-bottom: 15px; |
| | | } |
| | | |
| | | .plan-tags { |
| | | display: flex; |
| | | flex-wrap: wrap; |
| | | gap: 5px; |
| | | } |
| | | |
| | | .dialog-footer { |
| | | display: flex; |
| | | justify-content: flex-end; |
| | | gap: 10px; |
| | | } |
| | | |
| | | /* ååºå¼è®¾è®¡ */ |
| | | @media (max-width: 768px) { |
| | | .header-actions { |
| | | flex-direction: column; |
| | | gap: 15px; |
| | | } |
| | | |
| | | .left-actions { |
| | | flex-wrap: wrap; |
| | | gap: 10px; |
| | | } |
| | | |
| | | .plan-meta { |
| | | flex-direction: column; |
| | | gap: 10px; |
| | | } |
| | | |
| | | .plan-header { |
| | | flex-direction: column; |
| | | align-items: flex-start; |
| | | gap: 10px; |
| | | } |
| | | } |
| | | </style> |