|
@@ -0,0 +1,453 @@
|
|
|
|
|
+<template>
|
|
|
|
|
+ <div class="plan-page">
|
|
|
|
|
+ <custom-header name="农事规划"></custom-header>
|
|
|
|
|
+ <div class="plan-content">
|
|
|
|
|
+ <div class="status-filter">
|
|
|
|
|
+ <div class="status-label">农事状态</div>
|
|
|
|
|
+ <div class="status-items">
|
|
|
|
|
+ <div v-for="status in statusList" :key="status.value" class="status-item" :class="status.color">
|
|
|
|
|
+ <div class="status-dot"></div>
|
|
|
|
|
+ <span class="status-text">{{ status.label }}</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 三行循环时间线 -->
|
|
|
|
|
+ <div class="cycle-timeline-container">
|
|
|
|
|
+ <div class="cycle-timeline">
|
|
|
|
|
+ <div
|
|
|
|
|
+ v-for="(row, rowIndex) in timelineRows"
|
|
|
|
|
+ :key="rowIndex"
|
|
|
|
|
+ class="cycle-row"
|
|
|
|
|
+ :class="{ 'odd-index': rowIndex % 2 === 1 }"
|
|
|
|
|
+ >
|
|
|
|
|
+ <div
|
|
|
|
|
+ v-for="(item, itemIndex) in row.items"
|
|
|
|
|
+ :key="itemIndex"
|
|
|
|
|
+ class="cycle-item"
|
|
|
|
|
+ :class="[item.type + '-item', item.status]"
|
|
|
|
|
+ >
|
|
|
|
|
+ <!-- 任务框 -->
|
|
|
|
|
+ <div v-if="item.type === 'task'" class="cycle-task-box">
|
|
|
|
|
+ <div class="cycle-task-text">梢期</div>
|
|
|
|
|
+ <div class="cycle-task-text">杀虫</div>
|
|
|
|
|
+ <div v-if="item.icon" class="status-icon" :class="item.icon.type">
|
|
|
|
|
+ <el-icon v-if="item.icon.type === 'complete'" size="16" color="#1CA900"><SuccessFilled /></el-icon>
|
|
|
|
|
+ <el-icon v-if="item.icon.type === 'warning'" size="18" color="#FF953D"><WarnTriangleFilled /></el-icon>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <!-- 任务连接器 -->
|
|
|
|
|
+ <div v-if="item.type === 'task'" class="cycle-task-connector"></div>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 节气节点 -->
|
|
|
|
|
+ <template v-if="item.type === 'term'">
|
|
|
|
|
+ <div class="cycle-term-dot"></div>
|
|
|
|
|
+ <div class="cycle-term-label">立春</div>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 行连接器 -->
|
|
|
|
|
+ <div
|
|
|
|
|
+ v-if="rowIndex < timelineRows.length - 1"
|
|
|
|
|
+ class="cycle-connector"
|
|
|
|
|
+ :class="rowIndex % 2 === 1 ? 'middle-connector' : 'top-connector'"
|
|
|
|
|
+ ></div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <div class="control-section">
|
|
|
|
|
+ <div class="toggle-group">
|
|
|
|
|
+ <el-switch v-model="isDefaultEnabled" />
|
|
|
|
|
+ <span class="toggle-label">默认发起农情需求</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="add-button" @click="addNewTask">新增农事</div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+</template>
|
|
|
|
|
+
|
|
|
|
|
+<script setup>
|
|
|
|
|
+import { reactive, ref } from "vue";
|
|
|
|
|
+import customHeader from "@/components/customHeader.vue";
|
|
|
|
|
+
|
|
|
|
|
+// 状态列表数据
|
|
|
|
|
+const statusList = reactive([
|
|
|
|
|
+ { value: "pending", label: "待触发", color: "gray" },
|
|
|
|
|
+ { value: "executing", label: "待执行", color: "blue" },
|
|
|
|
|
+ { value: "completed", label: "已完成", color: "green" },
|
|
|
|
|
+ { value: "expired", label: "已过期", color: "orange" },
|
|
|
|
|
+]);
|
|
|
|
|
+
|
|
|
|
|
+// 切换开关状态
|
|
|
|
|
+const isDefaultEnabled = ref(true);
|
|
|
|
|
+
|
|
|
|
|
+// 时间线行数据
|
|
|
|
|
+const timelineRows = reactive([
|
|
|
|
|
+ {
|
|
|
|
|
+ items: [
|
|
|
|
|
+ { type: "task", status: "default" },
|
|
|
|
|
+ { type: "term" },
|
|
|
|
|
+ { type: "task", status: "default" },
|
|
|
|
|
+ { type: "term" },
|
|
|
|
|
+ { type: "task", status: "default" },
|
|
|
|
|
+ { type: "term" },
|
|
|
|
|
+ ],
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ items: [
|
|
|
|
|
+ { type: "term" },
|
|
|
|
|
+ { type: "task", status: "default" },
|
|
|
|
|
+ { type: "term" },
|
|
|
|
|
+ { type: "task", status: "active" },
|
|
|
|
|
+ { type: "term" },
|
|
|
|
|
+ { type: "task", status: "default" },
|
|
|
|
|
+ ],
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ items: [
|
|
|
|
|
+ { type: "task", status: "default" },
|
|
|
|
|
+ { type: "term" },
|
|
|
|
|
+ { type: "task", status: "default" },
|
|
|
|
|
+ { type: "term" },
|
|
|
|
|
+ { type: "task", status: "default" },
|
|
|
|
|
+ { type: "term" },
|
|
|
|
|
+ ],
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ items: [
|
|
|
|
|
+ { type: "term" },
|
|
|
|
|
+ { type: "task", status: "default" },
|
|
|
|
|
+ { type: "term" },
|
|
|
|
|
+ { type: "task", status: "active" },
|
|
|
|
|
+ { type: "term" },
|
|
|
|
|
+ { type: "task", status: "default" },
|
|
|
|
|
+ ],
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ items: [
|
|
|
|
|
+ { type: "task", status: "default" },
|
|
|
|
|
+ { type: "term" },
|
|
|
|
|
+ { type: "task", status: "default" },
|
|
|
|
|
+ { type: "term" },
|
|
|
|
|
+ { type: "task", status: "default" },
|
|
|
|
|
+ { type: "term" },
|
|
|
|
|
+ ],
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ items: [
|
|
|
|
|
+ { type: "term" },
|
|
|
|
|
+ { type: "task", status: "default" },
|
|
|
|
|
+ { type: "term" },
|
|
|
|
|
+ { type: "task", status: "active" },
|
|
|
|
|
+ { type: "term" },
|
|
|
|
|
+ { type: "task", status: "default" },
|
|
|
|
|
+ ],
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ items: [
|
|
|
|
|
+ { type: "task", status: "default", icon: { type: "normal", text: "✓" } },
|
|
|
|
|
+ { type: "term" },
|
|
|
|
|
+ { type: "task", status: "normal", icon: { type: "normal", text: "!" } },
|
|
|
|
|
+ { type: "term", status: "active" },
|
|
|
|
|
+ { type: "task", status: "complete", icon: { type: "complete", text: "✓" } },
|
|
|
|
|
+ { type: "term", status: "active" },
|
|
|
|
|
+ ],
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ items: [
|
|
|
|
|
+ { type: "term", status: "active" },
|
|
|
|
|
+ { type: "task", status: "complete", icon: { type: "warning", text: "✓" } },
|
|
|
|
|
+ { type: "term", status: "active" },
|
|
|
|
|
+ { type: "task", status: "warning", icon: { type: "complete", text: "!" } },
|
|
|
|
|
+ { type: "term", status: "active" },
|
|
|
|
|
+ { type: "task", status: "complete", icon: { type: "complete", text: "✓" } },
|
|
|
|
|
+ ],
|
|
|
|
|
+ },
|
|
|
|
|
+]);
|
|
|
|
|
+
|
|
|
|
|
+// 切换默认发起农情需求
|
|
|
|
|
+const toggleDefault = () => {
|
|
|
|
|
+ isDefaultEnabled.value = !isDefaultEnabled.value;
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+// 新增农事
|
|
|
|
|
+const addNewTask = () => {
|
|
|
|
|
+ console.log("新增农事");
|
|
|
|
|
+ // 这里可以添加新增农事的逻辑
|
|
|
|
|
+};
|
|
|
|
|
+</script>
|
|
|
|
|
+
|
|
|
|
|
+<style scoped lang="scss">
|
|
|
|
|
+.plan-page {
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+ height: 100vh;
|
|
|
|
|
+ background: #f5f7fb;
|
|
|
|
|
+ .plan-content {
|
|
|
|
|
+ padding: 13px 12px;
|
|
|
|
|
+ .status-filter {
|
|
|
|
|
+ background: #fff;
|
|
|
|
|
+ border-radius: 25px;
|
|
|
|
|
+ padding: 6px 17px;
|
|
|
|
|
+ margin-bottom: 16px;
|
|
|
|
|
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ gap: 16px;
|
|
|
|
|
+ font-size: 12px;
|
|
|
|
|
+
|
|
|
|
|
+ .status-label {
|
|
|
|
|
+ font-size: 12px;
|
|
|
|
|
+ white-space: nowrap;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .status-items {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ gap: 20px;
|
|
|
|
|
+
|
|
|
|
|
+ .status-item {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ gap: 6px;
|
|
|
|
|
+ &.gray {
|
|
|
|
|
+ color: #c4c6c9;
|
|
|
|
|
+ .status-dot {
|
|
|
|
|
+ background-color: #c4c6c9;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ &.blue {
|
|
|
|
|
+ color: #2199f8;
|
|
|
|
|
+ .status-dot {
|
|
|
|
|
+ background-color: #2199f8;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ &.green {
|
|
|
|
|
+ color: #1ca900;
|
|
|
|
|
+ .status-dot {
|
|
|
|
|
+ background-color: #1ca900;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ &.orange {
|
|
|
|
|
+ color: #ff953d;
|
|
|
|
|
+ .status-dot {
|
|
|
|
|
+ background-color: #ff953d;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ .status-dot {
|
|
|
|
|
+ width: 6px;
|
|
|
|
|
+ height: 6px;
|
|
|
|
|
+ border-radius: 50%;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 循环时间线样式
|
|
|
|
|
+ .cycle-timeline-container {
|
|
|
|
|
+ padding: 15px;
|
|
|
|
|
+ .cycle-timeline {
|
|
|
|
|
+ position: relative;
|
|
|
|
|
+
|
|
|
|
|
+ .cycle-row {
|
|
|
|
|
+ position: relative;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ justify-content: space-between;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ margin-bottom: 36px;
|
|
|
|
|
+ padding-right: 10px;
|
|
|
|
|
+ &.odd-index {
|
|
|
|
|
+ padding: 0;
|
|
|
|
|
+ padding-left: 10px;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ &:last-child {
|
|
|
|
|
+ margin-bottom: 0;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 水平时间线
|
|
|
|
|
+ &::before {
|
|
|
|
|
+ content: "";
|
|
|
|
|
+ position: absolute;
|
|
|
|
|
+ top: 0;
|
|
|
|
|
+ left: 20px;
|
|
|
|
|
+ right: 10px;
|
|
|
|
|
+ height: 1px;
|
|
|
|
|
+ background: #e3e3e3;
|
|
|
|
|
+ transform: translateY(-50%);
|
|
|
|
|
+ z-index: 1;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .cycle-item {
|
|
|
|
|
+ position: relative;
|
|
|
|
|
+ z-index: 2;
|
|
|
|
|
+ top: 8px;
|
|
|
|
|
+
|
|
|
|
|
+ &.task-item {
|
|
|
|
|
+ .cycle-task-box {
|
|
|
|
|
+ background: #fff;
|
|
|
|
|
+ border: 1px solid #dde1e7;
|
|
|
|
|
+ border-radius: 4px;
|
|
|
|
|
+ padding: 4px 8px;
|
|
|
|
|
+ text-align: center;
|
|
|
|
|
+ position: relative;
|
|
|
|
|
+
|
|
|
|
|
+ .cycle-task-text {
|
|
|
|
|
+ font-size: 12px;
|
|
|
|
|
+ color: #dde1e7;
|
|
|
|
|
+ line-height: 1.2;
|
|
|
|
|
+
|
|
|
|
|
+ &:first-child {
|
|
|
|
|
+ margin-bottom: 2px;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ .status-icon {
|
|
|
|
|
+ position: absolute;
|
|
|
|
|
+ bottom: -10px;
|
|
|
|
|
+ right: -10px;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .cycle-task-connector {
|
|
|
|
|
+ position: absolute;
|
|
|
|
|
+ top: -4px;
|
|
|
|
|
+ left: 50%;
|
|
|
|
|
+ transform: translateX(-50%);
|
|
|
|
|
+ width: 0;
|
|
|
|
|
+ height: 0;
|
|
|
|
|
+ border-left: 4px solid transparent;
|
|
|
|
|
+ border-right: 4px solid transparent;
|
|
|
|
|
+ border-bottom: 4px solid #dde1e7;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ &.warning{
|
|
|
|
|
+ .cycle-task-box{
|
|
|
|
|
+ border-color: #FF953D;
|
|
|
|
|
+ }
|
|
|
|
|
+ .cycle-task-text {
|
|
|
|
|
+ color: #FF953D;
|
|
|
|
|
+ }
|
|
|
|
|
+ .cycle-task-connector{
|
|
|
|
|
+ border-bottom-color: #FF953D;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ &.complete{
|
|
|
|
|
+ .cycle-task-box{
|
|
|
|
|
+ border-color: #1CA900;
|
|
|
|
|
+ }
|
|
|
|
|
+ .cycle-task-text {
|
|
|
|
|
+ color: #1CA900;
|
|
|
|
|
+ }
|
|
|
|
|
+ .cycle-task-connector{
|
|
|
|
|
+ border-bottom-color: #1CA900;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ &.normal{
|
|
|
|
|
+ .cycle-task-box{
|
|
|
|
|
+ border-color: #2199F8;
|
|
|
|
|
+ }
|
|
|
|
|
+ .cycle-task-text {
|
|
|
|
|
+ color: #2199F8;
|
|
|
|
|
+ }
|
|
|
|
|
+ .cycle-task-connector{
|
|
|
|
|
+ border-bottom-color: #2199F8;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ &.term-item {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ top: -11px;
|
|
|
|
|
+
|
|
|
|
|
+ .cycle-term-dot {
|
|
|
|
|
+ width: 6px;
|
|
|
|
|
+ height: 6px;
|
|
|
|
|
+ background: #c7c7c7;
|
|
|
|
|
+ border-radius: 50%;
|
|
|
|
|
+ margin-bottom: 4px;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .cycle-term-label {
|
|
|
|
|
+ font-size: 11px;
|
|
|
|
|
+ color: #c7c7c7;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ &.active{
|
|
|
|
|
+ .cycle-term-dot {
|
|
|
|
|
+ background: #858383;
|
|
|
|
|
+ }
|
|
|
|
|
+ .cycle-term-label {
|
|
|
|
|
+ color: #858383;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .cycle-connector {
|
|
|
|
|
+ position: absolute;
|
|
|
|
|
+ right: 0;
|
|
|
|
|
+ top: 38.5px;
|
|
|
|
|
+ transform: translateY(-50%);
|
|
|
|
|
+ width: 30px;
|
|
|
|
|
+ height: 76px;
|
|
|
|
|
+ border: 1px solid #e3e3e3;
|
|
|
|
|
+ border-left: none;
|
|
|
|
|
+ background: transparent;
|
|
|
|
|
+
|
|
|
|
|
+ &.top-connector {
|
|
|
|
|
+ border-top-right-radius: 5px;
|
|
|
|
|
+ border-bottom-right-radius: 5px;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ &.middle-connector {
|
|
|
|
|
+ border-top-left-radius: 5px;
|
|
|
|
|
+ border-bottom-left-radius: 5px;
|
|
|
|
|
+ left: 0;
|
|
|
|
|
+ border-right: none;
|
|
|
|
|
+ border-left: 1px solid #e3e3e3;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 控制区域样式
|
|
|
|
|
+ .control-section {
|
|
|
|
|
+ position: fixed;
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+ left: 0;
|
|
|
|
|
+ box-sizing: border-box;
|
|
|
|
|
+ bottom: 0px;
|
|
|
|
|
+ background: #fff;
|
|
|
|
|
+ padding: 16px 12px;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ justify-content: space-between;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ border-top: 1px solid #f0f0f0;
|
|
|
|
|
+
|
|
|
|
|
+ .toggle-group {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ gap: 8px;
|
|
|
|
|
+
|
|
|
|
|
+ .toggle-label {
|
|
|
|
|
+ font-size: 14px;
|
|
|
|
|
+ color: #141414;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .add-button {
|
|
|
|
|
+ background: linear-gradient(120deg, #76c3ff 0%, #2199f8 100%);
|
|
|
|
|
+ color: white;
|
|
|
|
|
+ border-radius: 25px;
|
|
|
|
|
+ padding: 10px 20px;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+</style>
|