<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="选择日期"
|
format="YYYY-MM-DD"
|
value-format="YYYY-MM-DD"
|
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="(command) => handleMoreAction(plan, command)">
|
<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="operationType === 'add' ? '发布计划' : '编辑计划'"
|
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"
|
value-format="YYYY-MM-DD"
|
format="YYYY-MM-DD"
|
placeholder="选择开始时间"
|
style="width: 100%" />
|
</el-form-item>
|
<el-form-item label="结束时间"
|
prop="endDate">
|
<el-date-picker v-model="planForm.endDate"
|
type="date"
|
value-format="YYYY-MM-DD"
|
format="YYYY-MM-DD"
|
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-item label="标签"
|
prop="tags">
|
<!-- <el-checkbox-group v-model="planForm.tags">
|
<el-checkbox label="all"></el-checkbox>
|
<el-checkbox label="manager">管理层</el-checkbox>
|
<el-checkbox label="hr">人事部门</el-checkbox>
|
<el-checkbox label="finance">财务部门</el-checkbox>
|
<el-checkbox label="tech">技术部门</el-checkbox>
|
</el-checkbox-group> -->
|
<el-select v-model="planForm.tags"
|
multiple
|
placeholder="请选择标签"
|
style="width: 100%">
|
<el-option v-for="dept in departments"
|
:key="dept"
|
:label="dept"
|
:value="dept" />
|
</el-select>
|
</el-form-item>
|
<el-form-item label="状态"
|
prop="status">
|
<el-select v-model="planForm.status"
|
placeholder="选择状态"
|
style="width: 100%">
|
<el-option label="未开始"
|
value="not_started" />
|
<el-option label="进行中"
|
value="in_progress" />
|
<el-option label="已完成"
|
value="completed" />
|
<el-option label="已暂停"
|
value="paused" />
|
</el-select>
|
</el-form-item>
|
<el-form-item label="进度"
|
prop="progress">
|
<el-input-number v-model="planForm.progress"
|
min="0"
|
max="100"
|
step="1"
|
placeholder="请输入进度"
|
style="width: 100%" />
|
</el-form-item>
|
</el-form>
|
<template #footer>
|
<span class="dialog-footer">
|
<el-button type="primary"
|
@click="handleSavePlan">保存</el-button>
|
<el-button @click="planDialogVisible = false">取消</el-button>
|
</span>
|
</template>
|
</el-dialog>
|
<!-- 计划详情对话框 -->
|
<el-dialog v-model="showPlanDetailDialog"
|
title="计划详情"
|
width="700px">
|
<div v-if="currentPlanDetail"
|
class="mb10">
|
<el-descriptions :column="2"
|
border>
|
<el-descriptions-item label="计划标题">{{ currentPlanDetail.title }}</el-descriptions-item>
|
<el-descriptions-item label="计划描述">{{ currentPlanDetail.description }}</el-descriptions-item>
|
<el-descriptions-item label="计划级别">{{ getCurrentLevelText(currentPlanDetail.level) }}</el-descriptions-item>
|
<el-descriptions-item label="时间周期">{{ getCurrentPeriodText(currentPlanDetail.period) }}</el-descriptions-item>
|
<el-descriptions-item label="开始时间">{{ currentPlanDetail.startDate }}</el-descriptions-item>
|
<el-descriptions-item label="结束时间">{{ currentPlanDetail.endDate }}</el-descriptions-item>
|
<el-descriptions-item label="负责人">{{ currentPlanDetail.assignee }}</el-descriptions-item>
|
<el-descriptions-item label="优先级">{{ getPriorityText(currentPlanDetail.priority) }}</el-descriptions-item>
|
<el-descriptions-item label="标签">{{ currentPlanDetail.tags.join(', ') }}</el-descriptions-item>
|
<el-descriptions-item label="状态">{{ getStatusText(currentPlanDetail.status) }}</el-descriptions-item>
|
<el-descriptions-item label="进度">{{ currentPlanDetail.progress }}%</el-descriptions-item>
|
</el-descriptions>
|
</div>
|
</el-dialog>
|
</div>
|
</template>
|
|
<script setup>
|
import { ref, reactive, computed, onMounted } from "vue";
|
import { ElMessage, ElMessageBox } from "element-plus";
|
const { proxy } = getCurrentInstance();
|
import {
|
User,
|
UserFilled,
|
OfficeBuilding,
|
House,
|
Calendar,
|
Clock,
|
Flag,
|
ArrowDown,
|
} from "@element-plus/icons-vue";
|
import {
|
listDutyPlan,
|
addDutyPlan,
|
updateDutyPlan,
|
delDutyPlan,
|
NumDutyPlan,
|
exportDutyPlan,
|
} from "@/api/collaborativeApproval/planTemplate.js";
|
|
// 响应式数据
|
const operationType = ref("add");
|
const currentLevel = ref("personal");
|
const currentPeriod = ref("week");
|
const currentDate = ref(new Date());
|
const planDialogVisible = ref(false);
|
const dialogTitle = ref("新增计划");
|
const planFormRef = ref();
|
const showPlanDetailDialog = ref(false);
|
const currentPlanDetail = ref(null);
|
|
// 表单数据
|
const planForm = reactive({
|
id: "",
|
title: "",
|
description: "",
|
level: "personal",
|
period: "week",
|
startDate: "",
|
endDate: "",
|
assignee: "",
|
priority: "medium",
|
tags: [],
|
status: "",
|
progress: 0,
|
});
|
|
// 表单验证规则
|
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 departments = [
|
"产品",
|
"分析",
|
"调研",
|
"技术",
|
"架构",
|
"设计",
|
"市场",
|
"推广",
|
"营销",
|
];
|
// 概览数据
|
const overviewData = reactive({
|
personal: { total: 0, completion: 0 },
|
group: { total: 0, completion: 0 },
|
department: { total: 0, completion: 0 },
|
company: { total: 0, completion: 0 },
|
});
|
|
// 计划列表数据
|
const planList = ref([]);
|
|
// 计算属性
|
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);
|
getPlanList();
|
// 这里可以根据级别筛选数据
|
};
|
|
const handlePeriodChange = value => {
|
console.log("时间周期变更:", value);
|
getPlanList();
|
// 这里可以根据周期筛选数据
|
};
|
|
const handleDateChange = value => {
|
console.log("日期变更:", value);
|
getPlanList();
|
// 这里可以根据日期筛选数据
|
};
|
|
const handleAddPlan = () => {
|
operationType.value = "add";
|
dialogTitle.value = "新增计划";
|
planDialogVisible.value = true;
|
// 重置表单
|
Object.keys(planForm).forEach(key => {
|
planForm[key] = "";
|
});
|
planForm.level = "personal";
|
planForm.period = "week";
|
planForm.priority = "medium";
|
planForm.status = "not_started";
|
planForm.progress = 0;
|
};
|
|
const handleEditPlan = plan => {
|
operationType.value = "edit";
|
dialogTitle.value = "编辑计划";
|
planDialogVisible.value = true;
|
Object.assign(planForm, plan);
|
// // 填充表单数据
|
// Object.keys(planForm).forEach(key => {
|
// if (key === 'tags') {
|
// planForm[key] = plan[key].join(', ')
|
// } else {
|
// planForm[key] = plan[key]
|
// }
|
// })
|
};
|
|
const handleViewDetail = plan => {
|
currentPlanDetail.value = plan;
|
showPlanDetailDialog.value = true;
|
// ElMessage.info(`查看计划详情: ${plan.title}`)
|
};
|
|
const handleMoreAction = async (plan, command) => {
|
let ids = [];
|
ids.push(plan.id);
|
console.log("ids", ids);
|
switch (command) {
|
case "share":
|
ElMessage.success("计划已共享");
|
break;
|
case "copy":
|
const knowledgeText = `
|
计划标题:${plan.title}
|
计划描述:${plan.description}
|
计划级别:${getCurrentLevelText(plan.level)}
|
时间周期:${getCurrentPeriodText(plan.period)}
|
开始时间:${plan.startDate}
|
结束时间:${plan.endDate}
|
负责人:${plan.assignee}
|
优先级:${getPriorityText(plan.priority)}
|
标签:${plan.tags.join(", ")}
|
状态:${getStatusText(plan.status)}
|
进度:${plan.progress}%
|
`.trim();
|
|
// 复制到剪贴板
|
navigator.clipboard
|
.writeText(knowledgeText)
|
.then(() => {
|
ElMessage.success("知识内容已复制到剪贴板");
|
})
|
.catch(() => {
|
ElMessage.error("复制失败,请手动复制");
|
});
|
// ElMessage.success('计划已复制')
|
break;
|
case "delete":
|
ElMessageBox.confirm("确定要删除这个计划吗?", "提示", {
|
confirmButtonText: "确定",
|
cancelButtonText: "取消",
|
type: "warning",
|
}).then(() => {
|
delDutyPlan(ids).then(res => {
|
if (res.code === 200) {
|
ElMessage.success("计划已删除");
|
ids.value = [];
|
getPlanList();
|
}
|
});
|
});
|
break;
|
}
|
};
|
//
|
const handleSavePlan = async () => {
|
try {
|
await planFormRef.value.validate();
|
if (operationType.value === "add") {
|
addDutyPlan(planForm).then(res => {
|
if (res.code === 200) {
|
ElMessage.success("计划保存成功");
|
planDialogVisible.value = false;
|
}
|
getPlanList();
|
});
|
} else {
|
updateDutyPlan(planForm).then(res => {
|
if (res.code === 200) {
|
ElMessage.success("计划保存成功");
|
planDialogVisible.value = false;
|
}
|
getPlanList();
|
});
|
}
|
} catch (error) {
|
console.log("表单验证失败:", error);
|
}
|
};
|
|
const handleDialogClose = () => {
|
planFormRef.value?.resetFields();
|
};
|
|
const handleRefresh = () => {
|
getPlanList();
|
// ElMessage.success('数据已刷新')
|
};
|
|
const handleFilter = () => {
|
ElMessage.info("打开筛选面板");
|
};
|
|
const handleExport = () => {
|
ElMessageBox.confirm("是否确认导出?", "导出", {
|
confirmButtonText: "确认",
|
cancelButtonText: "取消",
|
type: "warning",
|
})
|
.then(() => {
|
// exportDutyPlan().then(res => {
|
|
// })
|
proxy.download("/dutyPlan/export", {}, "计划管理.xlsx");
|
})
|
.catch(() => {
|
proxy.$modal.msg("已取消");
|
});
|
};
|
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";
|
};
|
//获取数据列表
|
const getPlanList = async () => {
|
const params = {
|
level: currentLevel.value,
|
period: currentPeriod.value,
|
queryDate: currentDate.value,
|
};
|
listDutyPlan(params)
|
.then(res => {
|
if (res.code === 200) {
|
planList.value = res.data.records;
|
}
|
})
|
.catch(err => {
|
console.log(err);
|
});
|
};
|
//获取数据
|
const getPlanNum = async () => {
|
NumDutyPlan()
|
.then(res => {
|
if (res.code === 200) {
|
// console.log(res.data)
|
//讲结果里面的数据根据level 赋值给overviewData
|
res.data.forEach(item => {
|
overviewData[item.level].total = item.num;
|
overviewData[item.level].completion = item.completion;
|
});
|
}
|
})
|
.catch(err => {
|
console.log(err);
|
});
|
};
|
|
onMounted(() => {
|
getPlanList();
|
getPlanNum();
|
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>
|