Преглед изворни кода

Merge branch 'demo' of http://121.4.16.100:3001/gebadi/nongxiaoyu into demo

jiuling пре 11 месеци
родитељ
комит
3ac7b5b501

+ 18 - 0
pages.json

@@ -24,12 +24,30 @@
         "enablePullDownRefresh": false
       }
     },
+    {
+      "path": "pages/device-list/detail-machine",
+      "style": {
+        "navigationBarTitleText": "农机设备详情",
+        "navigationBarBackgroundColor": "#ffffff",
+        "navigationBarTextStyle": "black",
+        "enablePullDownRefresh": false
+      }
+    },
     {
       "path": "pages/activity/index",
       "style": {
         "navigationBarTitleText": "农事活动"
       }
     },
+    {
+      "path": "pages/activity/activity-detail",
+      "style": {
+        "navigationBarTitleText": "农事任务详情",
+        "navigationBarBackgroundColor": "#ffffff",
+        "navigationBarTextStyle": "black",
+        "enablePullDownRefresh": true
+      }
+    },
     {
       "path": "pages/device/index",
       "style": {

+ 1226 - 0
pages/activity/activity-detail.vue

@@ -0,0 +1,1226 @@
+<template>
+  <view class="page-container">
+    <!-- 页面滚动区域 -->
+    <scroll-view class="page-scroll" scroll-y>
+      <!-- 地块信息卡片 -->
+      <view class="info-card">
+        <view class="card-title">
+          <text>地块信息</text>
+        </view>
+        <view class="info-item">
+          <text class="info-label">地块名称</text>
+          <text class="info-value">{{ formData.plotName || '东区水稻田' }}</text>
+        </view>
+        <view class="info-item">
+          <text class="info-label">作物名称</text>
+          <text class="info-value">{{ formData.crop || '水稻' }}</text>
+        </view>
+        <view class="info-item">
+          <text class="info-label">负责人</text>
+          <text class="info-value">{{ formData.manager || '张农夫' }}</text>
+        </view>
+      </view>
+
+      <!-- 任务填写表单 -->
+      <view class="form-card">
+        <view class="card-title">
+          <text>任务信息</text>
+        </view>
+        
+        <!-- 任务名称 -->
+        <view class="form-item">
+          <view class="form-label required">任务名称</view>
+          <input 
+            v-model="formData.taskName"
+            placeholder="请输入任务名称,例如:水稻田施肥"
+            :disabled="pageMode === 'view'"
+            class="form-input"
+          />
+        </view>
+
+        <!-- 任务类型 -->
+        <view class="form-item" @click="pageMode !== 'view' && showTaskTypeSelector()">
+          <view class="form-label required">任务类型</view>
+          <view class="select-wrapper">
+            <input 
+              v-model="formData.taskType"
+              placeholder="请选择任务类型"
+              disabled
+              class="form-input select-input"
+            />
+            <view v-if="pageMode !== 'view'" class="select-arrow">
+              <text>▼</text>
+            </view>
+          </view>
+        </view>
+
+        <!-- 执行时间 -->
+        <view class="form-item" @click="pageMode !== 'view' && selectExecuteTime()">
+          <view class="form-label required">执行时间</view>
+          <view class="select-wrapper">
+            <input 
+              v-model="formattedExecuteTime"
+              placeholder="请选择任务计划时间"
+              disabled
+              class="form-input select-input"
+            />
+            <view v-if="pageMode !== 'view'" class="select-arrow">
+              <text>选择</text>
+            </view>
+          </view>
+        </view>
+
+        <!-- 任务说明 -->
+        <view class="form-item">
+          <view class="form-label">任务说明</view>
+          <textarea 
+            v-model="formData.taskRemark"
+            placeholder="请输入任务要点,例如:每亩用肥20kg"
+            :disabled="pageMode === 'view'"
+            class="form-textarea"
+            maxlength="200"
+          ></textarea>
+          <view class="char-count">{{ (formData.taskRemark || '').length }}/200</view>
+        </view>
+
+        <!-- 任务完成情况 -->
+        <view class="section-divider"></view>
+        <view class="section-title">
+          <text>任务完成情况</text>
+        </view>
+
+        <!-- 完成状态选择 - 新建和编辑模式 -->
+        <view class="form-item" v-if="pageMode === 'create' || pageMode === 'edit'">
+          <view class="form-label">完成状态</view>
+          <view class="radio-group">
+            <view 
+              class="radio-item" 
+              v-for="(item, index) in completionStatusOptions" 
+              :key="index"
+              @click="selectCompletionStatus(item.value)"
+            >
+              <view class="radio-circle" :class="{'radio-checked': formData.completionStatus === item.value}">
+                <view v-if="formData.completionStatus === item.value" class="radio-dot"></view>
+              </view>
+              <text class="radio-label">{{ item.label }}</text>
+            </view>
+          </view>
+        </view>
+
+        <!-- 查看模式显示完成状态 -->
+        <view class="form-item" v-if="pageMode === 'view'">
+          <view class="form-label">完成状态</view>
+          <view class="status-completed">
+            <text class="status-icon">✓</text>
+            <text>已完成</text>
+          </view>
+        </view>
+
+        <!-- 完成时间 -->
+        <view class="form-item" v-if="formData.completionStatus === 'completed'">
+          <view class="form-label" :class="{'required': (pageMode === 'create' || pageMode === 'edit') && formData.completionStatus === 'completed'}">完成时间</view>
+                     <view class="select-wrapper" @click="(pageMode === 'create' || pageMode === 'edit') && formData.completionStatus === 'completed' && selectCompletionTime()">
+            <input 
+              v-model="formattedCompletionTime"
+              placeholder="请选择实际完成时间"
+              disabled
+              class="form-input select-input"
+            />
+                         <view v-if="(pageMode === 'create' || pageMode === 'edit') && formData.completionStatus === 'completed'" class="select-arrow">
+               <text>选择</text>
+             </view>
+          </view>
+        </view>
+
+        <!-- 完成说明 -->
+        <view class="form-item" v-if="formData.completionStatus === 'completed'">
+          <view class="form-label" :class="{'required': (pageMode === 'create' || pageMode === 'edit') && formData.completionStatus === 'completed'}">完成说明</view>
+          <textarea 
+            v-model="formData.completionDesc"
+            placeholder="请输入完成说明,例如:已完成并拍照记录"
+            :disabled="pageMode === 'view'"
+            class="form-textarea"
+            maxlength="300"
+          ></textarea>
+          <view class="char-count">{{ (formData.completionDesc || '').length }}/300</view>
+          <view class="form-error" v-if="formErrors.completionDesc">
+            {{ formErrors.completionDesc }}
+          </view>
+        </view>
+
+        <!-- 现场图片 -->
+        <view class="form-item" v-if="formData.completionStatus === 'completed'">
+          <view class="form-label">现场图片</view>
+          
+          <!-- 新建和编辑模式 -->
+          <view v-if="pageMode === 'create' || pageMode === 'edit'" class="image-upload">
+            <view class="image-list">
+              <view 
+                class="image-preview" 
+                v-for="(item, index) in formData.images" 
+                :key="index"
+                @click="previewImage(item, index)"
+              >
+                <image :src="item.url || item.path" mode="aspectFill"/>
+                <view class="delete-btn" @click.stop="deletePic(index)">
+                  <text>×</text>
+                </view>
+              </view>
+              <view 
+                v-if="formData.images.length < 6" 
+                class="upload-btn" 
+                @click="chooseImage"
+              >
+                <text class="upload-icon">+</text>
+                <text class="upload-text">添加图片</text>
+              </view>
+            </view>
+            <view class="upload-tip">最多可上传6张图片</view>
+          </view>
+
+          <!-- 查看模式 -->
+          <view v-else-if="pageMode === 'view' && formData.images && formData.images.length > 0" class="image-view">
+            <view class="image-list">
+              <view 
+                class="image-preview" 
+                v-for="(item, index) in formData.images" 
+                :key="index"
+                @click="previewImage(item, index)"
+              >
+                <image :src="item.url || item.path" mode="aspectFill"/>
+              </view>
+            </view>
+          </view>
+
+          <!-- 无图片提示 -->
+          <view v-else class="no-images">
+            <text>暂无图片</text>
+          </view>
+        </view>
+      </view>
+
+      <!-- 底部占位 -->
+      <view class="bottom-safe"></view>
+    </scroll-view>
+
+    <!-- 底部提交按钮 -->
+    <view class="footer-safe" v-if="pageMode !== 'view'">
+      <view class="footer-content">
+        <button 
+          class="submit-button"
+          :class="{'loading': isSubmitting}"
+          @click="submitForm"
+          :disabled="isSubmitting"
+        >
+          {{ isSubmitting ? '提交中...' : submitButtonText }}
+        </button>
+      </view>
+    </view>
+
+
+
+    <!-- 遮罩层 -->
+    <view v-if="showTaskTypePicker || showDateTimeSelector" class="picker-mask" @click="closePickers"></view>
+    
+    <!-- 任务类型选择弹窗 -->
+    <view v-if="showTaskTypePicker" class="picker-popup">
+      <view class="picker-header">
+        <text class="picker-cancel" @click="showTaskTypePicker = false">取消</text>
+        <text class="picker-title">选择任务类型</text>
+        <text class="picker-confirm" @click="confirmTaskType">确定</text>
+      </view>
+      <view class="picker-content">
+        <view 
+          class="picker-item" 
+          v-for="(item, index) in taskTypeOptions" 
+          :key="index"
+          :class="{'selected': tempTaskTypeIndex === index}"
+          @click="tempTaskTypeIndex = index"
+        >
+          <text>{{ item.label }}</text>
+          <text v-if="tempTaskTypeIndex === index" class="check-mark">✓</text>
+        </view>
+      </view>
+    </view>
+
+    <!-- 日期时间选择弹窗 -->
+    <view v-if="showDateTimeSelector" class="picker-popup datetime-picker">
+      <view class="picker-header">
+        <text class="picker-cancel" @click="cancelDateTime">取消</text>
+        <text class="picker-title">选择时间</text>
+        <text class="picker-confirm" @click="confirmDateTime">确定</text>
+      </view>
+      <view class="datetime-picker-content">
+        <picker-view 
+          class="datetime-picker-view"
+          :value="dateTimePickerValue" 
+          @change="onDateTimePickerChange"
+        >
+          <picker-view-column v-for="(range, index) in dateTimePickerRange" :key="index">
+            <view 
+              class="picker-view-item" 
+              v-for="(item, itemIndex) in range" 
+              :key="itemIndex"
+            >
+              {{ item }}
+            </view>
+          </picker-view-column>
+        </picker-view>
+      </view>
+    </view>
+  </view>
+</template>
+
+<script>
+export default {
+  data() {
+    return {
+      // 页面模式:create-新建, edit-编辑, view-查看
+      pageMode: 'create',
+      
+      // 选择器显示状态
+      showTaskTypePicker: false,
+      showDateTimeSelector: false,
+      
+      // 临时选择的任务类型索引
+      tempTaskTypeIndex: 0,
+      
+      // 时间选择器相关
+      currentTimeType: '', // 当前选择的时间类型
+      dateTimePickerValue: [0, 0, 0, 0, 0], // 年月日时分的选择值
+      dateTimePickerRange: [], // 选择器的范围数据
+      
+      // 表单数据
+      formData: {
+        // 地块基础信息
+        plotName: '',
+        crop: '',
+        manager: '',
+        
+        // 任务信息
+        taskName: '',
+        taskType: '',
+        executeTime: new Date().getTime(),
+        taskRemark: '',
+        
+        // 完成情况
+        completionStatus: 'pending',
+        completionTime: new Date().getTime(),
+        completionDesc: '',
+        images: []
+      },
+      
+      // 任务类型选项
+      taskTypeOptions: [
+        { label: '施肥', value: '施肥' },
+        { label: '灌溉', value: '灌溉' },
+        { label: '打药', value: '打药' },
+        { label: '采摘', value: '采摘' },
+        { label: '巡检', value: '巡检' },
+        { label: '除草', value: '除草' },
+        { label: '其他', value: '其他' }
+      ],
+      
+      // 完成状态选项
+      completionStatusOptions: [
+        { label: '待完成', value: 'pending' },
+        { label: '已完成', value: 'completed' }
+      ],
+      
+      // 是否正在提交
+      isSubmitting: false,
+      
+      // 表单验证错误
+      formErrors: {
+        completionDesc: ''
+      }
+    }
+  },
+  
+  computed: {
+    // 页面标题
+    pageTitle() {
+      switch (this.pageMode) {
+        case 'create':
+          return '新建农事任务';
+        case 'edit':
+          return '编辑农事任务';
+        default:
+          return '农事任务详情';
+      }
+    },
+    
+    // 提交按钮文字
+    submitButtonText() {
+      switch (this.pageMode) {
+        case 'create':
+          return '创建任务';
+        case 'edit':
+          return '保存修改';
+        default:
+          return '';
+      }
+    },
+    
+    // 格式化后的执行时间
+    formattedExecuteTime() {
+      return this.formatDateTime(this.formData.executeTime);
+    },
+    
+    // 格式化后的完成时间
+    formattedCompletionTime() {
+      return this.formatDateTime(this.formData.completionTime);
+    },
+    
+    // 任务类型标签数组
+    taskTypeLabels() {
+      return this.taskTypeOptions.map(item => item.label);
+    },
+    
+    // 当前任务类型索引
+    taskTypeIndex() {
+      const index = this.taskTypeOptions.findIndex(item => item.value === this.formData.taskType);
+      return index >= 0 ? index : 0;
+    }
+  },
+  
+  onLoad(options) {
+    console.log('页面加载,接收参数:', options);
+    
+    // 设置页面模式
+    if (options.mode) {
+      this.pageMode = options.mode;
+    }
+    
+    // 设置地块基础信息
+    this.formData.plotName = decodeURIComponent(options.plotName || '东区水稻田');
+    this.formData.crop = decodeURIComponent(options.crop || '水稻');
+    this.formData.manager = decodeURIComponent(options.manager || '张农夫');
+    
+    // 如果是编辑或查看模式,填充任务数据
+    if (options.taskName) {
+      this.formData.taskName = decodeURIComponent(options.taskName);
+      this.formData.taskType = decodeURIComponent(options.taskType || '');
+      this.formData.taskRemark = decodeURIComponent(options.taskRemark || '');
+      
+      // 处理执行时间
+      if (options.executeTime) {
+        const executeTimeStr = decodeURIComponent(options.executeTime);
+        if (executeTimeStr.includes('-')) {
+          this.formData.executeTime = new Date(executeTimeStr + ' 09:00:00').getTime();
+        } else {
+          this.formData.executeTime = parseInt(executeTimeStr) || new Date().getTime();
+        }
+      }
+      
+      // 如果是已完成任务,设置完成信息
+      if (options.taskStatus === 'completed') {
+        this.setupCompletionData();
+      }
+    }
+    
+    // 设置导航栏标题
+    uni.setNavigationBarTitle({
+      title: this.pageTitle
+    });
+    
+    // 初始化时间选择器数据
+    this.initDateTimeRange();
+  },
+  
+  methods: {
+    // 初始化时间选择器范围数据
+    initDateTimeRange() {
+      const currentYear = new Date().getFullYear();
+      
+      // 年份:当前年-5 到 当前年+5
+      const years = [];
+      for (let i = currentYear - 5; i <= currentYear + 5; i++) {
+        years.push(i + '年');
+      }
+      
+      // 月份:1-12月
+      const months = [];
+      for (let i = 1; i <= 12; i++) {
+        months.push(i + '月');
+      }
+      
+      // 日期:1-31日 (动态更新)
+      const days = [];
+      for (let i = 1; i <= 31; i++) {
+        days.push(i + '日');
+      }
+      
+      // 小时:0-23时
+      const hours = [];
+      for (let i = 0; i <= 23; i++) {
+        hours.push(i.toString().padStart(2, '0') + '时');
+      }
+      
+      // 分钟:0-59分
+      const minutes = [];
+      for (let i = 0; i <= 59; i++) {
+        minutes.push(i.toString().padStart(2, '0') + '分');
+      }
+      
+      this.dateTimePickerRange = [years, months, days, hours, minutes];
+    },
+    
+    // 更新日期范围(根据选择的年月)
+    updateDaysRange() {
+      const yearIndex = this.dateTimePickerValue[0];
+      const monthIndex = this.dateTimePickerValue[1];
+      
+      const baseYear = new Date().getFullYear() - 5;
+      const year = baseYear + yearIndex;
+      const month = monthIndex + 1;
+      
+      // 获取该月的天数
+      const daysInMonth = new Date(year, month, 0).getDate();
+      const days = [];
+      for (let i = 1; i <= daysInMonth; i++) {
+        days.push(i + '日');
+      }
+      
+      this.dateTimePickerRange[2] = days;
+      
+      // 如果当前选择的日期超过了该月的天数,调整为该月最后一天
+      if (this.dateTimePickerValue[2] >= daysInMonth) {
+        this.dateTimePickerValue[2] = daysInMonth - 1;
+      }
+    },
+    
+    // 显示任务类型选择器
+    showTaskTypeSelector() {
+      this.tempTaskTypeIndex = this.taskTypeIndex;
+      this.showTaskTypePicker = true;
+    },
+    
+    // 确认任务类型选择
+    confirmTaskType() {
+      this.formData.taskType = this.taskTypeOptions[this.tempTaskTypeIndex].value;
+      this.showTaskTypePicker = false;
+    },
+    
+    // 选择执行时间
+    selectExecuteTime() {
+      this.currentTimeType = 'executeTime';
+      this.initDateTimePicker(this.formData.executeTime);
+      this.showDateTimeSelector = true;
+    },
+    
+    // 选择完成时间
+    selectCompletionTime() {
+      this.currentTimeType = 'completionTime';
+      this.initDateTimePicker(this.formData.completionTime);
+      this.showDateTimeSelector = true;
+    },
+    
+    // 初始化日期时间选择器
+    initDateTimePicker(timestamp) {
+      const date = new Date(timestamp);
+      const currentYear = date.getFullYear();
+      const currentMonth = date.getMonth();
+      const currentDate = date.getDate();
+      const currentHour = date.getHours();
+      const currentMinute = date.getMinutes();
+      
+      const baseYear = new Date().getFullYear() - 5; // 基准年份
+      
+      // 设置选择器的初始值
+      this.dateTimePickerValue = [
+        currentYear - baseYear, // 年份索引
+        currentMonth, // 月份索引(0-11)
+        currentDate - 1, // 日期索引(0开始)
+        currentHour, // 小时索引
+        currentMinute // 分钟索引
+      ];
+      
+      // 更新日期范围
+      this.updateDaysRange();
+    },
+    
+    // 日期时间选择器变更
+    onDateTimePickerChange(e) {
+      this.dateTimePickerValue = e.detail.value;
+      // 当年月变更时,更新日期范围
+      this.updateDaysRange();
+    },
+    
+    // 确认时间选择
+    confirmDateTime() {
+      const values = this.dateTimePickerValue;
+      const baseYear = new Date().getFullYear() - 5;
+      const year = baseYear + values[0];
+      const month = values[1];
+      const date = values[2] + 1;
+      const hour = values[3];
+      const minute = values[4];
+      
+      const selectedTime = new Date(year, month, date, hour, minute).getTime();
+      
+      if (this.currentTimeType === 'executeTime') {
+        this.formData.executeTime = selectedTime;
+      } else if (this.currentTimeType === 'completionTime') {
+        this.formData.completionTime = selectedTime;
+      }
+      
+      this.showDateTimeSelector = false;
+      
+      uni.showToast({
+        title: '时间已设置',
+        icon: 'success',
+        duration: 1500
+      });
+    },
+    
+    // 取消时间选择
+    cancelDateTime() {
+      this.showDateTimeSelector = false;
+    },
+    
+    // 关闭所有选择器
+    closePickers() {
+      this.showTaskTypePicker = false;
+      this.showDateTimeSelector = false;
+    },
+    
+    // 选择完成状态
+    selectCompletionStatus(value) {
+      this.formData.completionStatus = value;
+      if (value === 'completed') {
+        this.formData.completionTime = new Date().getTime();
+      }
+    },
+    
+    // 格式化日期时间
+    formatDateTime(timestamp) {
+      if (!timestamp) return '';
+      const date = new Date(timestamp);
+      const year = date.getFullYear();
+      const month = String(date.getMonth() + 1).padStart(2, '0');
+      const day = String(date.getDate()).padStart(2, '0');
+      const hour = String(date.getHours()).padStart(2, '0');
+      const minute = String(date.getMinutes()).padStart(2, '0');
+      return `${year}-${month}-${day} ${hour}:${minute}`;
+    },
+    
+    // 选择图片
+    chooseImage() {
+      uni.chooseImage({
+        count: 6 - this.formData.images.length,
+        sizeType: ['original', 'compressed'],
+        sourceType: ['album', 'camera'],
+        success: (res) => {
+          console.log('选择图片成功:', res);
+          
+          res.tempFilePaths.forEach((path, index) => {
+            const imageItem = {
+              url: path,
+              path: path,
+              status: 'success'
+            };
+            this.formData.images.push(imageItem);
+          });
+          
+          if (res.tempFilePaths.length > 0) {
+            uni.showToast({
+              title: `已选择${res.tempFilePaths.length}张图片`,
+              icon: 'success'
+            });
+          }
+        },
+        fail: (err) => {
+          console.error('选择图片失败:', err);
+          uni.showToast({
+            title: '选择图片失败',
+            icon: 'none'
+          });
+        }
+      });
+    },
+
+    // 删除图片
+    deletePic(index) {
+      uni.showModal({
+        title: '确认删除',
+        content: '确定要删除这张图片吗?',
+        success: (res) => {
+          if (res.confirm) {
+            this.formData.images.splice(index, 1);
+            uni.showToast({
+              title: '已删除',
+              icon: 'none'
+            });
+          }
+        }
+      });
+    },
+
+    // 预览图片
+    previewImage(item, index) {
+      const urls = this.formData.images.map(file => file.url || file.path);
+      uni.previewImage({
+        urls: urls,
+        current: index
+      });
+    },
+    
+    // 表单验证
+    validateForm() {
+      // 重置错误信息
+      this.formErrors = {
+        completionDesc: ''
+      };
+      
+      // 基本字段验证
+      if (!this.formData.taskName.trim()) {
+        uni.showToast({
+          title: '请输入任务名称',
+          icon: 'none'
+        });
+        return false;
+      }
+      
+      if (!this.formData.taskType) {
+        uni.showToast({
+          title: '请选择任务类型',
+          icon: 'none'
+        });
+        return false;
+      }
+      
+      // 新建和编辑模式且已完成状态下的验证
+      if ((this.pageMode === 'create' || this.pageMode === 'edit') && this.formData.completionStatus === 'completed') {
+        if (!this.formData.completionDesc.trim()) {
+          this.formErrors.completionDesc = '请填写完成说明';
+          uni.showToast({
+            title: '请填写完成说明',
+            icon: 'none'
+          });
+          return false;
+        }
+      }
+      
+      return true;
+    },
+    
+    // 提交表单
+    submitForm() {
+      if (!this.validateForm()) {
+        return;
+      }
+      
+      this.isSubmitting = true;
+      
+      // 构建提交数据
+      const submitData = {
+        ...this.formData,
+        taskStatus: this.formData.completionStatus || 'pending',
+        imageUrls: this.formData.images.map(item => item.url || item.path)
+      };
+      
+      console.log('提交数据:', submitData);
+      
+      uni.showLoading({
+        title: '提交中...',
+        mask: true
+      });
+      
+      // 模拟提交过程
+      setTimeout(() => {
+        this.isSubmitting = false;
+        uni.hideLoading();
+        
+        const successMessage = this.pageMode === 'create' ? '任务创建成功' : '保存修改成功';
+        
+        uni.showToast({
+          title: successMessage,
+          icon: 'success',
+          duration: 1500
+        });
+        
+        setTimeout(() => {
+          uni.navigateBack();
+        }, 1500);
+      }, 1500);
+    },
+    
+    // 设置完成情况数据(用于编辑和查看模式)
+    setupCompletionData() {
+      this.formData.completionStatus = 'completed';
+      this.formData.completionTime = this.formData.executeTime + 8 * 60 * 60 * 1000;
+      this.formData.completionDesc = '已按要求完成任务,现场情况良好,所有步骤都按标准执行';
+      this.formData.images = [
+        {
+          url: 'https://cdn.uviewui.com/uview/album/1.jpg',
+          status: 'success'
+        },
+        {
+          url: 'https://cdn.uviewui.com/uview/album/2.jpg',
+          status: 'success'
+        }
+      ];
+    }
+  }
+}
+</script>
+
+<style scoped>
+.page-container {
+  height: 100vh;
+  background-color: #F5F5F5;
+  display: flex;
+  flex-direction: column;
+}
+
+.page-scroll {
+  flex: 1;
+  overflow: hidden;
+}
+
+/* 地块基础信息卡片 */
+.info-card {
+  background: #F9F9F9;
+  margin: 24rpx;
+  padding: 24rpx;
+  border-radius: 8rpx;
+  border: 1rpx solid #E5E5E5;
+}
+
+.card-title {
+  font-size: 30rpx;
+  font-weight: 600;
+  color: #333333;
+  margin-bottom: 16rpx;
+  padding-bottom: 12rpx;
+  border-bottom: 1rpx solid #E5E5E5;
+}
+
+.info-item {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 12rpx;
+  padding: 8rpx 0;
+}
+
+.info-item:last-child {
+  margin-bottom: 0;
+}
+
+.info-label {
+  font-size: 28rpx;
+  color: #666666;
+  flex-shrink: 0;
+}
+
+.info-value {
+  font-size: 28rpx;
+  color: #333333;
+  font-weight: 500;
+}
+
+/* 任务填写表单 */
+.form-card {
+  background: #FFFFFF;
+  margin: 0 24rpx 24rpx;
+  padding: 24rpx;
+  border-radius: 8rpx;
+  border: 1rpx solid #E5E5E5;
+}
+
+.section-divider {
+  height: 1rpx;
+  background: #F0F0F0;
+  margin: 32rpx 0 24rpx;
+}
+
+.section-title {
+  font-size: 30rpx;
+  font-weight: 600;
+  color: #333333;
+  margin-bottom: 16rpx;
+  padding-bottom: 12rpx;
+  border-bottom: 1rpx solid #F0F0F0;
+}
+
+.form-item {
+  margin-bottom: 24rpx;
+}
+
+.form-item:last-child {
+  margin-bottom: 0;
+}
+
+.form-label {
+  font-size: 28rpx;
+  color: #333333;
+  margin-bottom: 12rpx;
+  font-weight: 500;
+}
+
+.form-label.required::before {
+  content: '*';
+  color: #FF6B6B;
+  margin-right: 4rpx;
+}
+
+.form-input {
+  width: 100%;
+  height: 80rpx;
+  background: #F8F8F8;
+  border: 1rpx solid #E5E5E5;
+  border-radius: 8rpx;
+  padding: 0 16rpx;
+  font-size: 28rpx;
+  color: #333333;
+  box-sizing: border-box;
+}
+
+.form-input:focus {
+  background: #FFFFFF;
+  border-color: #3BB44A;
+}
+
+.form-input::placeholder {
+  color: #CCCCCC;
+}
+
+.form-input:disabled {
+  background: #F8F8F8;
+  color: #999999;
+}
+
+.select-wrapper {
+  position: relative;
+  width: 100%;
+}
+
+.select-input {
+  cursor: pointer;
+}
+
+.select-arrow {
+  position: absolute;
+  right: 16rpx;
+  top: 50%;
+  transform: translateY(-50%);
+  color: #999999;
+  font-size: 20rpx;
+  pointer-events: none;
+}
+
+.form-textarea {
+  width: 100%;
+  min-height: 120rpx;
+  background: #F8F8F8;
+  border: 1rpx solid #E5E5E5;
+  border-radius: 8rpx;
+  padding: 16rpx;
+  font-size: 28rpx;
+  color: #333333;
+  box-sizing: border-box;
+  resize: none;
+  line-height: 1.5;
+}
+
+.form-textarea:focus {
+  background: #FFFFFF;
+  border-color: #3BB44A;
+}
+
+.form-textarea::placeholder {
+  color: #CCCCCC;
+}
+
+.form-textarea:disabled {
+  background: #F8F8F8;
+  color: #999999;
+}
+
+.char-count {
+  font-size: 24rpx;
+  color: #CCCCCC;
+  text-align: right;
+  margin-top: 8rpx;
+}
+
+.form-error {
+  font-size: 24rpx;
+  color: #FF6B6B;
+  margin-top: 8rpx;
+}
+
+/* 单选按钮样式 */
+.radio-group {
+  display: flex;
+  gap: 32rpx;
+}
+
+.radio-item {
+  display: flex;
+  align-items: center;
+  cursor: pointer;
+}
+
+.radio-circle {
+  width: 32rpx;
+  height: 32rpx;
+  border: 2rpx solid #CCCCCC;
+  border-radius: 50%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  margin-right: 8rpx;
+}
+
+.radio-circle.radio-checked {
+  border-color: #3BB44A;
+  background-color: #3BB44A;
+}
+
+.radio-dot {
+  width: 12rpx;
+  height: 12rpx;
+  background-color: #FFFFFF;
+  border-radius: 50%;
+}
+
+.radio-label {
+  font-size: 28rpx;
+  color: #333333;
+}
+
+/* 完成状态样式 */
+.status-completed {
+  display: flex;
+  align-items: center;
+  color: #3BB44A;
+  font-size: 28rpx;
+}
+
+.status-icon {
+  width: 32rpx;
+  height: 32rpx;
+  background: #3BB44A;
+  color: #FFFFFF;
+  border-radius: 50%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  margin-right: 8rpx;
+  font-size: 20rpx;
+  line-height: 1;
+}
+
+/* 图片上传样式 */
+.image-upload, .image-view {
+  width: 100%;
+}
+
+.image-list {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 16rpx;
+}
+
+.image-preview {
+  position: relative;
+  width: 160rpx;
+  height: 160rpx;
+  border-radius: 8rpx;
+  overflow: hidden;
+  background: #F5F5F5;
+}
+
+.image-preview image {
+  width: 100%;
+  height: 100%;
+}
+
+.delete-btn {
+  position: absolute;
+  top: -6rpx;
+  right: -6rpx;
+  width: 32rpx;
+  height: 32rpx;
+  background: rgba(0, 0, 0, 0.6);
+  color: #FFFFFF;
+  border-radius: 50%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  font-size: 20rpx;
+  line-height: 1;
+}
+
+.upload-btn {
+  width: 160rpx;
+  height: 160rpx;
+  background: #F8F8F8;
+  border: 2rpx dashed #DDDDDD;
+  border-radius: 8rpx;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+}
+
+.upload-icon {
+  font-size: 32rpx;
+  margin-bottom: 8rpx;
+  color: #999999;
+}
+
+.upload-text {
+  font-size: 24rpx;
+  color: #999999;
+}
+
+.upload-tip {
+  font-size: 24rpx;
+  color: #999999;
+  margin-top: 12rpx;
+}
+
+.no-images {
+  padding: 40rpx 0;
+  text-align: center;
+  color: #CCCCCC;
+  font-size: 28rpx;
+}
+
+/* 底部安全区域 */
+.bottom-safe {
+  height: 120rpx;
+}
+
+.footer-safe {
+  background: #FFFFFF;
+  border-top: 1rpx solid #E5E5E5;
+  padding-bottom: constant(safe-area-inset-bottom);
+  padding-bottom: env(safe-area-inset-bottom);
+}
+
+.footer-content {
+  padding: 24rpx;
+}
+
+.submit-button {
+  width: 100%;
+  height: 88rpx;
+  background: #3BB44A;
+  color: #FFFFFF;
+  border: none;
+  border-radius: 8rpx;
+  font-size: 32rpx;
+  font-weight: 600;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+.submit-button:active {
+  background: #2D8C3C;
+}
+
+.submit-button:disabled,
+.submit-button.loading {
+  background: #CCCCCC;
+}
+
+/* 选择器弹窗样式 */
+.picker-mask {
+  position: fixed;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  background: rgba(0, 0, 0, 0.5);
+  z-index: 999;
+}
+
+.picker-popup {
+  position: fixed;
+  bottom: 0;
+  left: 0;
+  width: 100%;
+  background: #FFFFFF;
+  border-radius: 16rpx 16rpx 0 0;
+  z-index: 1000;
+  padding-bottom: constant(safe-area-inset-bottom);
+  padding-bottom: env(safe-area-inset-bottom);
+}
+
+.picker-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  padding: 24rpx;
+  border-bottom: 1rpx solid #E5E5E5;
+}
+
+.picker-cancel, .picker-confirm {
+  font-size: 30rpx;
+  color: #3BB44A;
+}
+
+.picker-title {
+  font-size: 32rpx;
+  font-weight: 600;
+  color: #333333;
+}
+
+.picker-content {
+  max-height: 400rpx;
+  overflow-y: auto;
+}
+
+.picker-item {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  padding: 24rpx;
+  border-bottom: 1rpx solid #F0F0F0;
+  font-size: 30rpx;
+  color: #333333;
+}
+
+.picker-item.selected {
+  color: #3BB44A;
+}
+
+.picker-item:last-child {
+  border-bottom: none;
+}
+
+.check-mark {
+  color: #3BB44A;
+  font-size: 28rpx;
+}
+
+/* 日期时间选择器样式 */
+.datetime-picker {
+  height: 60vh;
+}
+
+.datetime-picker-content {
+  height: calc(100% - 100rpx);
+  padding: 0;
+}
+
+.datetime-picker-view {
+  width: 100%;
+  height: 100%;
+}
+
+.picker-view-item {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  height: 80rpx;
+  font-size: 32rpx;
+  color: #333333;
+}
+</style> 

+ 721 - 5
pages/activity/index.vue

@@ -1,15 +1,731 @@
 <template>
-  <view class="container">
-    农事活动页面开发中...
+  <view class="activity-container">
+    <view class="fixed-content">
+      <!-- 顶部地块信息卡片 -->
+      <view class="plot-info-card">
+        <view class="plot-header">
+          <text class="plot-name">{{ plotData.name }}</text>
+          <text class="plot-area">{{ plotData.area }}亩</text>
+        </view>
+        <view class="plot-details">
+          <view class="plot-item">
+            <text class="item-label">作物:</text>
+            <text class="item-value">{{ plotData.crop }}</text>
+          </view>
+          <view class="plot-item">
+            <text class="item-label">负责人:</text>
+            <text class="item-value">{{ plotData.manager }}</text>
+          </view>
+        </view>
+      </view>
+
+      <!-- 任务状态筛选 -->
+      <view class="tab-filter">
+        <view class="tab-container">
+            <view 
+              class="tab-item" 
+              v-for="(status, index) in taskStatus" 
+              :key="index"
+              :class="{ active: currentStatus === status.value }"
+              @click="switchStatus(status.value)"
+            >
+              <text>{{ status.label }}</text>
+              <view class="badge" v-if="status.value === 'pending' && pendingTaskCount > 0">
+                {{ pendingTaskCount > 99 ? '99+' : pendingTaskCount }}
+              </view>
+              <view class="active-line" v-if="currentStatus === status.value"></view>
+            </view>
+          </view>
+      </view>
+    </view>
+
+    <!-- 任务列表 - 可滚动区域 -->
+    <scroll-view class="task-list-scroll" scroll-y 
+      @scrolltolower="loadMore" 
+      @refresherrefresh="refreshData" 
+      refresher-enabled
+      :refresher-triggered="isRefreshing">
+      <view class="task-list">
+        <view class="task-card" v-for="(task, index) in taskList" :key="index" @click="viewTaskDetail(task)">
+          <!-- 头部区域:任务名称和状态标签 -->
+          <view class="task-header">
+            <view class="task-name">{{ task.name }}</view>
+            <view class="task-status" :class="{ 'status-completed': task.status === 'completed' }">
+              {{ task.status === 'pending' ? '待完成' : '已完成' }}
+            </view>
+          </view>
+          
+          <!-- 信息行:类型、时间、责任人统一放在一行,类型在前 -->
+          <view class="task-info-row">
+            <!-- 类型标签放在最左侧 -->
+            <view class="info-type">
+              <image src="/static/icons/task_icon.png" mode="aspectFit" class="info-icon"></image>
+              <text class="info-text">{{ task.type }}</text>
+            </view>
+            
+            <!-- 时间信息放在中间 -->
+            <view class="info-time">
+              <image src="/static/icons/clock_icon.png" mode="aspectFit" class="info-icon"></image>
+              <text class="info-text">{{ task.executeTime }}</text>
+            </view>
+            
+            <!-- 责任人信息放在右侧 -->
+            <view class="info-person">
+              <image src="/static/icons/user.png" mode="aspectFit" class="info-icon"></image>
+              <text class="info-text">{{ task.assignee }}</text>
+            </view>
+          </view>
+          
+          <!-- 备注区域 -->
+          <view class="task-remark" v-if="task.remark" @click.stop="toggleRemark(index)">
+            <text class="remark-label">备注:</text>
+            <text class="remark-content" :class="{'remark-expanded': task.remarkExpanded}">{{ task.remark }}</text>
+            <text class="expand-btn" v-if="task.remark.length > 50 && !task.remarkExpanded">展开</text>
+            <text class="expand-btn" v-if="task.remarkExpanded">收起</text>
+          </view>
+        </view>
+        
+        <view class="loading-more" v-if="isLoading">
+          <view class="loading-icon"></view>
+          <text>加载中...</text>
+        </view>
+        <view class="no-more-data" v-if="noMoreData && taskList.length > 0">
+          <text>没有更多数据了</text>
+        </view>
+        <view class="empty-list" v-if="taskList.length === 0 && !isLoading">
+          <image src="/static/icons/warning_icon.png" mode="aspectFit"></image>
+          <text>暂无农事任务</text>
+        </view>
+      </view>
+    </scroll-view>
+
+    <!-- 新增任务按钮 -->
+    <view class="add-task-button" @click="createNewTask">
+      <image src="/static/icons/add_task.png" mode="aspectFit" class="add-icon"></image>
+    </view>
   </view>
 </template>
 
-<script setup>
+<script>
+export default {
+  data() {
+    return {
+      // 地块数据
+      plotData: {
+        id: 1,
+        name: '东区水稻田',
+        area: '128',
+        crop: '水稻',
+        manager: '张农夫'
+      },
+
+      // 待完成任务数
+      pendingTaskCount: 5,
+
+      // 当前筛选状态
+      currentStatus: 'all',
+
+      // 筛选相关数据
+      taskStatus: [
+        { label: '全部', value: 'all' },
+        { label: '待完成', value: 'pending' },
+        { label: '已完成', value: 'completed' }
+      ],
+      
+      // 任务列表数据
+      taskList: [],
+      isLoading: false,
+      isRefreshing: false,
+      noMoreData: false,
+      page: 1,
+      pageSize: 10,
+
+      // 模拟任务数据
+      mockTasks: [
+        {
+          id: 1,
+          name: '水稻田施肥',
+          type: '施肥',
+          status: 'pending',
+          executeTime: '2023-06-15',
+          assignee: '李明',
+          remark: '使用复合肥,每亩用量20kg',
+          remarkExpanded: false
+        },
+        {
+          id: 2,
+          name: '病虫害防治喷药',
+          type: '喷药',
+          status: 'completed',
+          executeTime: '2023-06-10',
+          assignee: '王强',
+          remark: '使用杀虫剂,注意安全防护',
+          remarkExpanded: false
+        },
+        {
+          id: 3,
+          name: '早稻收割',
+          type: '采摘',
+          status: 'pending',
+          executeTime: '2023-06-20',
+          assignee: '张农夫',
+          remark: '天气晴好时进行',
+          remarkExpanded: false
+        },
+        {
+          id: 4,
+          name: '水渠疏通',
+          type: '灌溉',
+          status: 'pending',
+          executeTime: '2023-06-12',
+          assignee: '周华',
+          remark: '',
+          remarkExpanded: false
+        },
+        {
+          id: 5,
+          name: '田间除草',
+          type: '除草',
+          status: 'pending',
+          executeTime: '2023-06-18',
+          assignee: '刘艳',
+          remark: '使用除草剂,避开雨天',
+          remarkExpanded: false
+        }
+      ]
+    }
+  },
+  
+  components: {
+    // No longer needed as we're using standard image icons
+  },
+  
+  methods: {
+    // 根据任务类型返回颜色
+    getTypeColor(type, mode = 'bg') {
+      const colors = {
+        '施肥': {
+          bg: 'linear-gradient(135deg, rgba(59, 180, 74, 0.8), rgba(59, 180, 74, 0.6))',
+          text: '#3BB44A'
+        },
+        '喷药': {
+          bg: 'linear-gradient(135deg, rgba(255, 82, 82, 0.8), rgba(255, 82, 82, 0.6))',
+          text: '#FF5252'
+        },
+        '灌溉': {
+          bg: 'linear-gradient(135deg, rgba(64, 158, 255, 0.8), rgba(64, 158, 255, 0.6))',
+          text: '#409EFF'
+        },
+        '采摘': {
+          bg: 'linear-gradient(135deg, rgba(250, 173, 20, 0.8), rgba(250, 173, 20, 0.6))',
+          text: '#FAAD14'
+        },
+        '播种': {
+          bg: 'linear-gradient(135deg, rgba(121, 85, 72, 0.8), rgba(121, 85, 72, 0.6))',
+          text: '#795548'
+        },
+        '除草': {
+          bg: 'linear-gradient(135deg, rgba(156, 39, 176, 0.8), rgba(156, 39, 176, 0.6))',
+          text: '#9C27B0'
+        }
+      };
+      
+      const defaultColor = {
+        bg: 'linear-gradient(135deg, rgba(158, 158, 158, 0.8), rgba(158, 158, 158, 0.6))',
+        text: '#909399'
+      };
+      
+      return (colors[type] || defaultColor)[mode];
+    },
+
+    // 切换任务状态筛选
+    switchStatus(status) {
+      if (this.currentStatus === status) return;
+      
+      this.currentStatus = status;
+      this.page = 1;
+      this.noMoreData = false;
+      this.loadTaskData();
+    },
+    
+    // 加载任务数据
+    loadTaskData() {
+      this.isLoading = true;
+      
+      // 模拟API请求延迟
+      setTimeout(() => {
+        // 模拟筛选
+        let filteredTasks = [...this.mockTasks];
+        
+        // 按状态筛选
+        if (this.currentStatus !== 'all') {
+          filteredTasks = filteredTasks.filter(task => task.status === this.currentStatus);
+        }
+        
+        // 分页处理
+        const start = (this.page - 1) * this.pageSize;
+        const end = start + this.pageSize;
+        const pageData = filteredTasks.slice(start, end);
+        
+        if (this.page === 1) {
+          this.taskList = pageData;
+        } else {
+          this.taskList = [...this.taskList, ...pageData];
+        }
+        
+        this.noMoreData = end >= filteredTasks.length;
+        this.isLoading = false;
+        this.isRefreshing = false;
+        
+        // 更新待完成任务数
+        this.pendingTaskCount = this.mockTasks.filter(task => task.status === 'pending').length;
+      }, 500);
+    },
+
+    // 下拉刷新
+    refreshData(e) {
+      this.isRefreshing = true;
+      this.page = 1;
+      this.noMoreData = false;
+      
+      // 模拟API请求延迟
+      setTimeout(() => {
+        this.loadTaskData();
+        // 显示刷新成功的提示
+        uni.showToast({
+          title: '刷新成功',
+          icon: 'success',
+          duration: 1000
+        });
+      }, 1000);
+    },
+
+    // 上拉加载更多
+    loadMore() {
+      if (!this.isLoading && !this.noMoreData) {
+        this.page++;
+        this.loadTaskData();
+      }
+    },
+
+    // 查看任务详情
+    viewTaskDetail(task) {
+      // 根据任务状态决定跳转到不同模式的页面
+      let mode = '';
+      if (task.status === 'pending') {
+        // 待完成任务跳转到编辑页面
+        mode = 'edit';
+      } else if (task.status === 'completed') {
+        // 已完成任务跳转到查看页面
+        mode = 'view';
+      }
+      
+      console.log(`点击任务: ${task.name}, 状态: ${task.status}, 跳转模式: ${mode}`);
+      console.log('地块数据:', this.plotData);
+      console.log('任务数据:', task);
+      
+      // 使用简化的URL参数传递
+      const url = `/pages/activity/activity-detail?id=${task.id}&mode=${mode}&plotName=${encodeURIComponent(this.plotData.name)}&crop=${encodeURIComponent(this.plotData.crop)}&manager=${encodeURIComponent(this.plotData.manager)}&taskName=${encodeURIComponent(task.name)}&taskType=${encodeURIComponent(task.type)}&executeTime=${encodeURIComponent(task.executeTime)}&taskRemark=${encodeURIComponent(task.remark || '')}&taskStatus=${task.status}&assignee=${encodeURIComponent(task.assignee)}`;
+      
+      console.log('跳转URL:', url);
+      
+      uni.navigateTo({
+        url: url
+      });
+    },
+
+    // 创建新任务
+    createNewTask() {
+      console.log('点击新建任务按钮,跳转到创建页面');
+      console.log('地块数据:', this.plotData);
+      
+      // 使用简化的URL参数传递
+      const url = `/pages/activity/activity-detail?id=new&mode=create&plotName=${encodeURIComponent(this.plotData.name)}&crop=${encodeURIComponent(this.plotData.crop)}&manager=${encodeURIComponent(this.plotData.manager)}`;
+      
+      console.log('跳转URL:', url);
+      
+      // 跳转到新建任务页面
+      uni.navigateTo({
+        url: url
+      });
+    },
+    
+    // 切换备注展开/收起状态
+    toggleRemark(index) {
+      if (this.taskList[index].remark && this.taskList[index].remark.length > 50) {
+        this.$set(this.taskList[index], 'remarkExpanded', !this.taskList[index].remarkExpanded);
+      }
+    }
+  },
+  
+  // 页面加载时获取数据
+  onLoad() {
+    this.loadTaskData();
+  }
+}
 </script>
 
 <style scoped>
-.container {
-  padding: 40rpx;
+.activity-container {
+  position: relative;
+  height: 100vh;
+  background-color: #f8f8f8;
+  display: flex;
+  flex-direction: column;
+  overflow: hidden;
+}
+
+/* 固定内容区域 */
+.fixed-content {
+  flex-shrink: 0;
+  width: 100%;
+}
+
+/* 地块信息卡片 */
+.plot-info-card {
+  margin: 20rpx 30rpx 30rpx;
+  padding: 30rpx;
+  background: linear-gradient(135deg, #3BB44A, #2D8C3C);
+  border-radius: 16rpx;
+  box-shadow: 0 4rpx 12rpx rgba(59, 180, 74, 0.2);
+  position: relative;
+  color: #fff;
+  width: calc(100% - 60rpx);
+  box-sizing: border-box;
+}
+
+.plot-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 20rpx;
+}
+
+.plot-name {
+  font-size: 32rpx;
+  font-weight: bold;
+}
+
+.plot-area {
+  font-size: 28rpx;
+  background-color: rgba(255, 255, 255, 0.2);
+  padding: 4rpx 16rpx;
+  border-radius: 20rpx;
+}
+
+.plot-details {
+  display: flex;
+  flex-wrap: wrap;
+}
+
+.plot-item {
+  display: flex;
+  align-items: center;
+  margin-right: 40rpx;
+  margin-bottom: 10rpx;
+}
+
+.item-label {
+  font-size: 26rpx;
+  opacity: 0.8;
+}
+
+.item-value {
+  font-size: 28rpx;
+  font-weight: 500;
+}
+
+/* 任务状态筛选 */
+.tab-filter {
+  margin: 0 30rpx 20rpx;
+  background-color: #fff;
+  border-radius: 16rpx;
+  box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05);
+  overflow: hidden;
+  z-index: 10;
+  width: calc(100% - 60rpx);
+  box-sizing: border-box;
+}
+
+.tab-container {
+  display: flex;
+  width: 100%;
+  height: 90rpx;
+}
+
+.tab-item {
+  flex: 1;
+  position: relative;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  height: 100%;
+  min-width: 150rpx;
+  transition: all 0.3s ease;
+}
+
+.tab-item text {
+  font-size: 28rpx;
+  color: #666;
+  transition: color 0.3s ease;
+}
+
+.tab-item.active text {
+  color: #3BB44A;
+  font-weight: bold;
+}
+
+.active-line {
+  position: absolute;
+  bottom: 0;
+  left: 25%;
+  width: 50%;
+  height: 4rpx;
+  background-color: #3BB44A;
+  border-radius: 4rpx;
+  transition: all 0.3s ease;
+}
+
+.badge {
+  position: absolute;
+  top: 16rpx;
+  right: 20%;
+  min-width: 32rpx;
+  height: 32rpx;
+  line-height: 32rpx;
+  border-radius: 16rpx;
+  background: linear-gradient(135deg, #FF5252, #FF7676);
+  color: #fff;
+  font-size: 20rpx;
+  padding: 0 8rpx;
+  text-align: center;
+  box-shadow: 0 2rpx 4rpx rgba(255, 82, 82, 0.2);
+  transform: scale(0.9);
+}
+
+/* 滚动区域 */
+.task-list-scroll {
+  flex: 1;
+  height: 0; /* 关键属性:使滚动区域占据剩余高度 */
+  overflow: hidden;
+}
+
+.task-list {
+  padding: 0 30rpx;
+  box-sizing: border-box;
+  width: 100%;
+}
+
+.task-card {
+  background-color: #fff;
+  border-radius: 16rpx;
+  padding: 24rpx;
+  margin-bottom: 20rpx;
+  box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05);
+  position: relative;
+  transition: transform 0.3s ease;
+  width: calc(100% - 0rpx);
+  box-sizing: border-box;
+  overflow: hidden;
+}
+
+.task-card:active {
+  transform: scale(0.98);
+}
+
+/* 任务卡片头部样式优化 */
+.task-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 16rpx;
+}
+
+.task-name {
   font-size: 32rpx;
+  font-weight: bold;
+  color: #333;
+  padding-right: 80rpx; /* 为右侧状态标签留出空间 */
+}
+
+.task-status {
+  position: absolute;
+  top: 24rpx;
+  right: 24rpx;
+  font-size: 24rpx;
+  padding: 4rpx 16rpx;
+  border-radius: 20rpx;
+  background: linear-gradient(135deg, rgba(255, 82, 82, 0.8), rgba(255, 82, 82, 0.6));
+  color: #fff;
+  z-index: 1;
+  max-width: 120rpx;
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+}
+
+.status-completed {
+  background: linear-gradient(135deg, rgba(59, 180, 74, 0.8), rgba(59, 180, 74, 0.6));
+}
+
+/* 信息行样式优化 */
+.task-info-row {
+  display: flex;
+  align-items: center;
+  margin-bottom: 16rpx;
+  padding: 6rpx 0;
+  width: 100%;
+}
+
+.info-type, .info-time, .info-person {
+  display: flex;
+  align-items: center;
+}
+
+.info-type {
+  flex: 0 0 auto;
+  margin-right: 30rpx;
+}
+
+.info-time {
+  flex: 0 0 auto;
+  margin-right: 30rpx;
+}
+
+.info-person {
+  flex: 0 0 auto;
+  min-width: 100rpx;
+}
+
+.info-icon {
+  width: 28rpx;
+  height: 28rpx;
+  margin-right: 8rpx;
+  opacity: 0.7;
+  flex-shrink: 0;
+}
+
+.info-text {
+  font-size: 26rpx;
+  color: #666;
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+}
+
+/* 删除旧的类型标签样式 */
+.type-tag {
+  display: none;
+}
+
+.task-remark {
+  background-color: rgba(0, 0, 0, 0.03);
+  padding: 16rpx;
+  border-radius: 10rpx;
+  display: flex;
+  margin-bottom: 10rpx;
+  position: relative;
+  flex-wrap: wrap;
+}
+
+.remark-label {
+  font-size: 26rpx;
+  color: #999;
+  margin-right: 8rpx;
+}
+
+.remark-content {
+  flex: 1;
+  font-size: 26rpx;
+  color: #666;
+  display: -webkit-box;
+  -webkit-box-orient: vertical;
+  -webkit-line-clamp: 2;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  word-break: break-all;
+}
+
+.remark-expanded {
+  -webkit-line-clamp: unset;
+}
+
+.expand-btn {
+  font-size: 24rpx;
+  color: #3BB44A;
+  margin-left: auto;
+  padding-left: 20rpx;
+}
+
+.loading-more, .no-more-data, .empty-list {
+  text-align: center;
+  padding: 30rpx 0;
+}
+
+.loading-icon {
+  display: inline-block;
+  width: 40rpx;
+  height: 40rpx;
+  border: 3rpx solid #f3f3f3;
+  border-top: 3rpx solid #3BB44A;
+  border-radius: 50%;
+  vertical-align: middle;
+  animation: spin 1s linear infinite;
+}
+
+@keyframes spin {
+  0% { transform: rotate(0deg); }
+  100% { transform: rotate(360deg); }
+}
+
+.loading-more text, .no-more-data text {
+  font-size: 24rpx;
+  color: #999;
+  margin-left: 10rpx;
+}
+
+.empty-list {
+  padding: 100rpx 0;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+}
+
+.empty-list image {
+  width: 120rpx;
+  height: 120rpx;
+  margin-bottom: 20rpx;
+  opacity: 0.5;
+}
+
+.empty-list text {
+  font-size: 28rpx;
+  color: #999;
+}
+
+/* 新增任务按钮 */
+.add-task-button {
+  position: fixed;
+  right: 40rpx;
+  bottom: 120rpx;
+  width: 100rpx;
+  height: 100rpx;
+  border-radius: 50%;
+  background: linear-gradient(135deg, #3BB44A, #2D8C3C);
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  box-shadow: 0 6rpx 16rpx rgba(59, 180, 74, 0.3);
+  z-index: 100;
+  transition: transform 0.2s ease;
+}
+
+.add-task-button:active {
+  transform: scale(0.95);
+}
+
+.add-icon {
+  width: 48rpx;
+  height: 48rpx;
 }
 </style>

+ 98 - 18
pages/device-list/detail-camera.vue

@@ -13,6 +13,10 @@
             {{ deviceInfo.status === 'online' ? '在线' : '离线' }}
           </view>
         </view>
+        
+        <view class="refresh-btn" :class="{'refreshing': isRefreshing}" @tap="refreshData">
+          <image src="/static/icons/refresh_icon.png" mode="aspectFit" style="width: 22px; height: 22px;"></image>
+        </view>
       </view>
       
       <view class="device-meta-row">
@@ -237,7 +241,9 @@ export default {
         { id: 2, time: '今天 09:30', type: '信号异常', status: '未处理', level: 'medium' },
         { id: 3, time: '昨天 15:45', type: '设备状态', status: '已处理', level: 'low' },
         { id: 4, time: '昨天 10:20', type: '遮挡告警', status: '未处理', level: 'low' }
-      ]
+      ],
+      videoContext: null,
+      isRefreshing: false
     }
   },
   
@@ -577,16 +583,17 @@ export default {
 .device-header {
   background-color: #FFFFFF;
   border-radius: 20rpx;
-  padding: 26rpx 30rpx 30rpx;
-  margin: 20rpx 30rpx 20rpx;
+  padding: 30rpx;
+  margin: 30rpx 30rpx 30rpx;
   box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.06);
+  border: 1rpx solid rgba(210, 237, 217, 0.5);
 }
 
 .device-info-row {
   display: flex;
   justify-content: space-between;
-  align-items: flex-start;
-  margin-bottom: 24rpx;
+  align-items: center;
+  margin-bottom: 28rpx;
 }
 
 .device-name-container {
@@ -596,16 +603,16 @@ export default {
 }
 
 .device-name {
-  font-size: 34rpx;
+  font-size: 36rpx;
   color: #333333;
   font-weight: 600;
-  margin-bottom: 10rpx;
+  margin-bottom: 12rpx;
 }
 
 .status-tag {
-  padding: 4rpx 12rpx 4rpx 24rpx;
+  padding: 6rpx 16rpx 6rpx 28rpx;
   border-radius: 30rpx;
-  font-size: 22rpx;
+  font-size: 24rpx;
   font-weight: 500;
   flex-shrink: 0;
   position: relative;
@@ -620,7 +627,7 @@ export default {
 .status-offline {
   background-color: rgba(245, 108, 108, 0.1);
   color: #F56C6C;
-  padding-left: 24rpx;
+  padding-left: 28rpx;
 }
 
 .status-dot {
@@ -657,34 +664,43 @@ export default {
 .device-meta-row {
   display: flex;
   flex-direction: column;
+  background-color: #F9FCFA;
+  padding: 20rpx 24rpx;
+  border-radius: 16rpx;
+  border: 1rpx solid rgba(210, 237, 217, 0.8);
 }
 
 .device-meta-item {
   display: flex;
   align-items: center;
-  font-size: 26rpx;
+  font-size: 28rpx;
   margin-top: 18rpx;
 }
 
+.device-meta-item:first-child {
+  margin-top: 0;
+}
+
 .meta-icon {
   width: 36rpx;
   height: 36rpx;
   display: flex;
   align-items: center;
   justify-content: center;
-  margin-right: 8rpx;
+  margin-right: 12rpx;
 }
 
 .meta-label {
-  color: #999999;
+  color: #777777;
   min-width: 140rpx;
-  font-size: 26rpx;
+  font-size: 28rpx;
 }
 
 .meta-value {
   color: #333333;
   flex: 1;
-  font-size: 26rpx;
+  font-size: 28rpx;
+  font-weight: 500;
 }
 
 /* 视频预览区域 */
@@ -1072,11 +1088,36 @@ export default {
 
 /* 告警信息列表 */
 .alerts-section {
-  margin: 0 30rpx;
+  margin: 0 30rpx 30rpx;
   background-color: #FFFFFF;
   border-radius: 20rpx;
-  padding: 24rpx;
+  padding: 30rpx 24rpx;
   box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.04);
+  border: 1rpx solid rgba(210, 237, 217, 0.5);
+}
+
+.section-title {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  font-size: 32rpx;
+  font-weight: 600;
+  color: #333333;
+  margin-bottom: 24rpx;
+  padding-bottom: 16rpx;
+  border-bottom: 2rpx solid #f0f0f0;
+}
+
+.alert-badge {
+  background-color: #F56C6C;
+  color: #FFFFFF;
+  font-size: 22rpx;
+  border-radius: 30rpx;
+  padding: 2rpx 12rpx;
+  margin-left: 12rpx;
+  font-weight: normal;
+  min-width: 32rpx;
+  text-align: center;
 }
 
 .alerts-list {
@@ -1090,23 +1131,32 @@ export default {
   padding: 24rpx 20rpx;
   border-radius: 12rpx;
   margin-bottom: 16rpx;
-  box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
+  box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
   position: relative;
+  border: 1rpx solid transparent;
+  transition: transform 0.2s ease;
+}
+
+.alert-item:active {
+  transform: scale(0.98);
 }
 
 .alert-urgent {
   background-color: #FEF3F3;
   border-left: 4rpx solid #F56C6C;
+  border-color: rgba(245, 108, 108, 0.2);
 }
 
 .alert-warning {
   background-color: #FFF8E6;
   border-left: 4rpx solid #E6A23C;
+  border-color: rgba(230, 162, 60, 0.2);
 }
 
 .alert-info {
   background-color: #F2FAF5;
   border-left: 4rpx solid #67C23A;
+  border-color: rgba(103, 194, 58, 0.2);
 }
 
 .alert-item-icon {
@@ -1155,4 +1205,34 @@ export default {
   font-size: 28rpx;
   color: #999999;
 }
+
+/* 刷新按钮样式 */
+.refresh-btn {
+  width: 48rpx;
+  height: 48rpx;
+  background-color: rgba(76, 175, 80, 0.1);
+  border-radius: 50%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  padding: 12rpx;
+  transition: transform 0.3s ease;
+}
+
+.refresh-btn:active {
+  transform: rotate(180deg);
+}
+
+.refresh-btn.refreshing {
+  animation: spin 1.2s linear infinite;
+}
+
+@keyframes spin {
+  0% {
+    transform: rotate(0deg);
+  }
+  100% {
+    transform: rotate(360deg);
+  }
+}
 </style> 

+ 29 - 0
pages/device-list/detail-collector.vue

@@ -1052,6 +1052,35 @@ export default {
   border: 1rpx solid rgba(210, 237, 217, 0.5);
 }
 
+.alerts-section .section-title {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  font-size: 32rpx;
+  font-weight: 600;
+  color: #333333;
+  margin-bottom: 24rpx;
+  padding-bottom: 16rpx;
+  border-bottom: 2rpx solid #f0f0f0;
+  padding-left: 0;
+}
+
+.alerts-section .section-title::before {
+  display: none;
+}
+
+.alert-badge {
+  background-color: #F56C6C;
+  color: #FFFFFF;
+  font-size: 22rpx;
+  border-radius: 30rpx;
+  padding: 2rpx 12rpx;
+  margin-left: 12rpx;
+  font-weight: normal;
+  min-width: 32rpx;
+  text-align: center;
+}
+
 .alerts-list {
   display: flex;
   flex-direction: column;

+ 1163 - 0
pages/device-list/detail-machine.vue

@@ -0,0 +1,1163 @@
+<template>
+  <view class="container">
+    <!-- 设备头部信息区域 -->
+    <view class="device-header">
+      <view class="device-info-row">
+        <view class="device-name-container">
+          <text class="device-name">{{ deviceInfo.name }}</text>
+          <view 
+            class="status-tag" 
+            :class="deviceInfo.status === 'online' ? 'status-online' : 'status-offline'"
+          >
+            <view class="status-dot" :class="{'offline-dot': deviceInfo.status === 'offline'}"></view>
+            {{ deviceInfo.status === 'online' ? '在线' : '离线' }}
+          </view>
+        </view>
+        
+        <view class="refresh-btn" :class="{'refreshing': isRefreshing}" @tap="refreshData">
+          <image src="/static/icons/refresh_icon.png" mode="aspectFit" style="width: 22px; height: 22px;"></image>
+        </view>
+      </view>
+      
+      <view class="device-meta-row">
+        <view class="device-meta-item">
+          <view class="meta-icon">
+            <image src="/static/icons/device_icon.png" mode="aspectFit" style="width: 36rpx; height: 36rpx;"></image>
+          </view>
+          <text class="meta-label">设备编号:</text>
+          <text class="meta-value">{{ deviceInfo.deviceId }}</text>
+        </view>
+        
+        <view class="device-meta-item">
+          <view class="meta-icon">
+            <image src="/static/icons/location_icon.png" mode="aspectFit" style="width: 36rpx; height: 36rpx;"></image>
+          </view>
+          <text class="meta-label">工作区域:</text>
+          <text class="meta-value">{{ deviceInfo.location }}</text>
+        </view>
+        
+        <view class="device-meta-item">
+          <view class="meta-icon">
+            <image src="/static/icons/clock_icon.png" mode="aspectFit" style="width: 36rpx; height: 36rpx;"></image>
+          </view>
+          <text class="meta-label">最近更新:</text>
+          <text class="meta-value">{{ deviceInfo.lastUpdate }}</text>
+        </view>
+      </view>
+    </view>
+
+    <!-- 设备状态信息区域 -->
+    <view class="status-section">
+      <view class="section-title">
+        <text>设备状态</text>
+      </view>
+      
+      <view class="status-grid">
+        <!-- 作业状态 -->
+        <view class="status-item" hover-class="status-item-hover">
+          <text class="status-label">作业状态</text>
+          <view class="status-value-container">
+            <text class="status-value" :class="{
+              'status-working': deviceInfo.workStatus === 'working',
+              'status-idle': deviceInfo.workStatus === 'idle',
+              'status-error': deviceInfo.workStatus === 'error'
+            }">{{ getWorkStatusText(deviceInfo.workStatus) }}</text>
+          </view>
+          <text class="status-time">{{ deviceInfo.statusUpdateTime || '1分钟前' }}</text>
+        </view>
+        
+        <!-- 电量状态 -->
+        <view class="status-item" hover-class="status-item-hover">
+          <text class="status-label">电量</text>
+          <view class="status-value-container">
+            <text class="status-value" :class="{
+              'value-warning': deviceInfo.battery < 30,
+              'value-alert': deviceInfo.battery < 15
+            }">{{ deviceInfo.battery || 85 }}</text>
+            <text class="status-unit">%</text>
+          </view>
+          <text class="status-time">{{ deviceInfo.batteryUpdateTime || '30秒前' }}</text>
+        </view>
+        
+        <!-- GPS信号 -->
+        <view class="status-item" hover-class="status-item-hover">
+          <text class="status-label">GPS信号</text>
+          <view class="status-value-container">
+            <text class="status-value" :class="{
+              'status-good': deviceInfo.gpsSignal >= 80,
+              'status-normal': deviceInfo.gpsSignal >= 60 && deviceInfo.gpsSignal < 80,
+              'status-weak': deviceInfo.gpsSignal < 60
+            }">{{ getGpsSignalText(deviceInfo.gpsSignal) }}</text>
+          </view>
+          <text class="status-time">{{ deviceInfo.gpsUpdateTime || '15秒前' }}</text>
+        </view>
+        
+        <!-- 工作时长 -->
+        <view class="status-item" hover-class="status-item-hover">
+          <text class="status-label">今日工作</text>
+          <view class="status-value-container">
+            <text class="status-value">{{ deviceInfo.workDuration || '2.5' }}</text>
+            <text class="status-unit">小时</text>
+          </view>
+          <text class="status-time">累计时长</text>
+        </view>
+        
+        <!-- 作业面积 -->
+        <view class="status-item" hover-class="status-item-hover">
+          <text class="status-label">今日面积</text>
+          <view class="status-value-container">
+            <text class="status-value">{{ deviceInfo.workArea || '15.6' }}</text>
+            <text class="status-unit">亩</text>
+          </view>
+          <text class="status-time">已完成</text>
+        </view>
+        
+        <!-- 当前速度 -->
+        <view class="status-item" hover-class="status-item-hover">
+          <text class="status-label">当前速度</text>
+          <view class="status-value-container">
+            <text class="status-value">{{ deviceInfo.currentSpeed || '0' }}</text>
+            <text class="status-unit">km/h</text>
+          </view>
+          <text class="status-time">{{ deviceInfo.speedUpdateTime || '实时' }}</text>
+        </view>
+      </view>
+    </view>
+
+    <!-- 设备控制区域 -->
+    <view class="control-section">
+      <view class="section-title">
+        <text>设备控制</text>
+        <view class="engine-status" :class="{'engine-on': isEngineOn}">
+          <text>{{ isEngineOn ? '引擎开启' : '引擎关闭' }}</text>
+        </view>
+      </view>
+      
+      <view class="control-container">
+        <!-- 启动/停止按钮 -->
+        <view class="engine-control">
+          <view 
+            class="engine-button" 
+            :class="{'engine-on': isEngineOn, 'disabled': deviceInfo.status === 'offline'}"
+            @click="toggleEngine"
+          >
+            <view class="engine-icon">
+              <image 
+                :src="isEngineOn ? '/static/icons/engine_stop.svg' : '/static/icons/engine_start.svg'" 
+                mode="aspectFit" 
+                style="width: 28px; height: 28px;"
+              ></image>
+            </view>
+            <text class="engine-text">{{ isEngineOn ? '停止' : '启动' }}</text>
+          </view>
+        </view>
+        
+        <!-- 方向控制 -->
+        <view class="direction-control">
+          <!-- 前进按钮 -->
+          <view class="control-btn control-forward" 
+                :class="{'disabled': !isEngineOn || deviceInfo.status === 'offline', 'active': activeControl === 'forward'}"
+                @touchstart="startControl('forward')" 
+                @touchend="stopControl">
+            <image src="/static/icons/arrow_up.svg" mode="aspectFit" style="width: 24px; height: 24px;"></image>
+            <text>前进</text>
+          </view>
+          
+          <!-- 左右控制行 -->
+          <view class="control-row">
+            <!-- 左转按钮 -->
+            <view class="control-btn control-left" 
+                  :class="{'disabled': !isEngineOn || deviceInfo.status === 'offline', 'active': activeControl === 'left'}"
+                  @touchstart="startControl('left')" 
+                  @touchend="stopControl">
+              <image src="/static/icons/arrow_left.svg" mode="aspectFit" style="width: 24px; height: 24px;"></image>
+              <text>左转</text>
+            </view>
+            
+            <!-- 中心状态显示 -->
+            <view class="control-center">
+              <view class="speed-display">
+                <text class="speed-value">{{ currentSpeed }}</text>
+                <text class="speed-unit">km/h</text>
+              </view>
+            </view>
+            
+            <!-- 右转按钮 -->
+            <view class="control-btn control-right" 
+                  :class="{'disabled': !isEngineOn || deviceInfo.status === 'offline', 'active': activeControl === 'right'}"
+                  @touchstart="startControl('right')" 
+                  @touchend="stopControl">
+              <image src="/static/icons/arrow_right.svg" mode="aspectFit" style="width: 24px; height: 24px;"></image>
+              <text>右转</text>
+            </view>
+          </view>
+          
+          <!-- 后退按钮 -->
+          <view class="control-btn control-backward" 
+                :class="{'disabled': !isEngineOn || deviceInfo.status === 'offline', 'active': activeControl === 'backward'}"
+                @touchstart="startControl('backward')" 
+                @touchend="stopControl">
+            <image src="/static/icons/arrow_down.svg" mode="aspectFit" style="width: 24px; height: 24px;"></image>
+            <text>后退</text>
+          </view>
+        </view>
+        
+        <!-- 快捷操作按钮 -->
+        <view class="quick-controls">
+          <view class="quick-control-btn" 
+                :class="{'disabled': deviceInfo.status === 'offline'}"
+                @click="emergencyStop">
+            <view class="quick-icon emergency">
+              <image src="/static/icons/emergency_stop.svg" mode="aspectFit" style="width: 24px; height: 24px;"></image>
+            </view>
+            <text>紧急停止</text>
+          </view>
+          
+          <view class="quick-control-btn" 
+                :class="{'disabled': deviceInfo.status === 'offline'}"
+                @click="returnHome">
+            <view class="quick-icon">
+              <image src="/static/icons/home.svg" mode="aspectFit" style="width: 24px; height: 24px;"></image>
+            </view>
+            <text>返回充电</text>
+          </view>
+          
+          <view class="quick-control-btn" 
+                :class="{'disabled': deviceInfo.status === 'offline'}"
+                @click="autoMode">
+            <view class="quick-icon">
+              <image src="/static/icons/auto_mode.svg" mode="aspectFit" style="width: 24px; height: 24px;"></image>
+            </view>
+            <text>自动模式</text>
+          </view>
+        </view>
+      </view>
+    </view>
+
+    <!-- 告警信息列表 -->
+    <view class="alerts-section">
+      <view class="section-title">
+        <text>告警信息</text>
+        <view class="alert-badge" v-if="getUnhandledAlerts.length > 0">{{ getUnhandledAlerts.length }}</view>
+      </view>
+      
+      <view class="alerts-list" v-if="getUnhandledAlerts.length > 0">
+        <view 
+          v-for="(item, index) in getUnhandledAlerts" 
+          :key="index"
+          class="alert-item"
+          :class="{
+            'alert-urgent': item.level === 'high', 
+            'alert-warning': item.level === 'medium', 
+            'alert-info': item.level === 'low'
+          }"
+          @click="handleAlert(item)"
+        >
+          <view class="alert-item-icon">
+            <image v-if="item.level === 'high'" src="/static/icons/warning_icon.png" mode="aspectFit" style="width: 24px; height: 24px;"></image>
+            <image v-else-if="item.level === 'medium'" src="/static/icons/info_icon.png" mode="aspectFit" style="width: 24px; height: 24px;"></image>
+            <image v-else src="/static/icons/success_icon.png" mode="aspectFit" style="width: 24px; height: 24px;"></image>
+          </view>
+          
+          <view class="alert-item-info">
+            <text class="alert-item-type">{{ item.title }}</text>
+            <text class="alert-item-level">
+              {{ item.level === 'high' ? '紧急' : item.level === 'medium' ? '警告' : '提示' }}
+            </text>
+          </view>
+          
+          <view class="alert-item-time">{{ item.time }}</view>
+        </view>
+      </view>
+      
+      <view v-else class="empty-alert">
+        <text class="empty-text">暂无告警信息</text>
+      </view>
+    </view>
+  </view>
+</template>
+
+<script>
+export default {
+  data() {
+    return {
+      // 设备信息
+      deviceInfo: {
+        id: '',
+        name: '智能播种机-001',
+        deviceId: 'AGM-001-2024',
+        location: '东区水稻田A块',
+        status: 'online', // online, offline
+        workStatus: 'idle', // working, idle, error
+        battery: 85,
+        gpsSignal: 92,
+        workDuration: '2.5',
+        workArea: '15.6',
+        currentSpeed: '0',
+        lastUpdate: '1分钟前',
+        statusUpdateTime: '1分钟前',
+        batteryUpdateTime: '30秒前',
+        gpsUpdateTime: '15秒前',
+        speedUpdateTime: '实时'
+      },
+      
+      // 控制状态
+      isEngineOn: false,
+      activeControl: '', // forward, backward, left, right
+      currentSpeed: 0,
+      
+      // 页面状态
+      isRefreshing: false,
+      
+      // 告警数据
+      alerts: [
+        {
+          id: 1,
+          title: '电量不足警告',
+          description: '设备电量低于30%,建议及时充电',
+          level: 'medium',
+          time: '5分钟前',
+          handled: false
+        },
+        {
+          id: 2,
+          title: 'GPS信号弱',
+          description: '当前GPS信号较弱,可能影响导航精度',
+          level: 'low',
+          time: '10分钟前',
+          handled: false
+        },
+        {
+          id: 3,
+          title: '作业异常',
+          description: '检测到播种深度异常,请检查设备状态',
+          level: 'high',
+          time: '15分钟前',
+          handled: false
+        }
+      ]
+    }
+  },
+  
+  computed: {
+    // 未处理的告警
+    getUnhandledAlerts() {
+      return this.alerts.filter(alert => !alert.handled);
+    }
+  },
+  
+  onLoad(options) {
+    // 获取设备ID参数
+    if (options.deviceId) {
+      this.deviceInfo.deviceId = options.deviceId;
+    }
+    
+    // 获取设备ID参数(兼容不同参数名)
+    if (options.id) {
+      this.deviceInfo.id = options.id;
+    }
+    
+    // 设置导航栏标题
+    uni.setNavigationBarTitle({
+      title: '农机设备详情'
+    });
+    
+    // 加载设备数据
+    this.loadDeviceData();
+  },
+  
+  onShow() {
+    // 页面显示时刷新数据
+    this.refreshData();
+  },
+  
+  onHide() {
+    // 页面隐藏时停止所有控制操作
+    this.stopAllControls();
+  },
+  
+  methods: {
+    // 加载设备数据
+    loadDeviceData() {
+      // 这里可以调用API获取设备详细信息
+      console.log('加载设备数据:', this.deviceInfo.deviceId);
+    },
+    
+    // 刷新数据
+    refreshData() {
+      this.isRefreshing = true;
+      
+      // 模拟刷新延迟
+      setTimeout(() => {
+        this.isRefreshing = false;
+        // 更新最近更新时间
+        this.deviceInfo.lastUpdate = '刚刚';
+        
+        uni.showToast({
+          title: '刷新成功',
+          icon: 'success',
+          duration: 1500
+        });
+      }, 1000);
+    },
+    
+    // 获取工作状态文本
+    getWorkStatusText(status) {
+      const statusMap = {
+        working: '工作中',
+        idle: '空闲',
+        error: '故障'
+      };
+      return statusMap[status] || '未知';
+    },
+    
+    // 获取GPS信号文本
+    getGpsSignalText(signal) {
+      if (signal >= 80) return '强';
+      if (signal >= 60) return '中等';
+      if (signal >= 40) return '弱';
+      return '无信号';
+    },
+    
+    // 切换引擎状态
+    toggleEngine() {
+      if (this.deviceInfo.status === 'offline') {
+        uni.showToast({
+          title: '设备离线,无法操作',
+          icon: 'none'
+        });
+        return;
+      }
+      
+      this.isEngineOn = !this.isEngineOn;
+      
+      if (this.isEngineOn) {
+        this.deviceInfo.workStatus = 'idle';
+        uni.showToast({
+          title: '引擎已启动',
+          icon: 'success'
+        });
+      } else {
+        this.deviceInfo.workStatus = 'idle';
+        this.currentSpeed = 0;
+        this.stopAllControls();
+        uni.showToast({
+          title: '引擎已关闭',
+          icon: 'success'
+        });
+      }
+    },
+    
+    // 开始控制
+    startControl(direction) {
+      if (!this.isEngineOn || this.deviceInfo.status === 'offline') {
+        return;
+      }
+      
+      this.activeControl = direction;
+      this.deviceInfo.workStatus = 'working';
+      
+      // 模拟速度变化
+      switch (direction) {
+        case 'forward':
+          this.currentSpeed = 5;
+          break;
+        case 'backward':
+          this.currentSpeed = 2;
+          break;
+        case 'left':
+        case 'right':
+          this.currentSpeed = 3;
+          break;
+      }
+      
+      // 发送控制指令
+      this.sendControlCommand(direction, true);
+    },
+    
+    // 停止控制
+    stopControl() {
+      if (this.activeControl) {
+        this.sendControlCommand(this.activeControl, false);
+      }
+      
+      this.activeControl = '';
+      this.currentSpeed = 0;
+      this.deviceInfo.workStatus = 'idle';
+    },
+    
+    // 停止所有控制
+    stopAllControls() {
+      this.activeControl = '';
+      this.currentSpeed = 0;
+      if (this.isEngineOn) {
+        this.deviceInfo.workStatus = 'idle';
+      }
+    },
+    
+    // 发送控制指令
+    sendControlCommand(direction, isStart) {
+      console.log(`发送控制指令: ${direction}, 开始: ${isStart}`);
+      // 这里可以调用API发送实际的控制指令
+    },
+    
+    // 紧急停止
+    emergencyStop() {
+      if (this.deviceInfo.status === 'offline') {
+        uni.showToast({
+          title: '设备离线,无法操作',
+          icon: 'none'
+        });
+        return;
+      }
+      
+      uni.showModal({
+        title: '紧急停止',
+        content: '确定要执行紧急停止吗?设备将立即停止所有操作。',
+        success: (res) => {
+          if (res.confirm) {
+            this.isEngineOn = false;
+            this.stopAllControls();
+            
+            uni.showToast({
+              title: '紧急停止已执行',
+              icon: 'success'
+            });
+          }
+        }
+      });
+    },
+    
+    // 返回充电
+    returnHome() {
+      if (this.deviceInfo.status === 'offline') {
+        uni.showToast({
+          title: '设备离线,无法操作',
+          icon: 'none'
+        });
+        return;
+      }
+      
+      uni.showToast({
+        title: '设备正在返回充电站',
+        icon: 'success'
+      });
+    },
+    
+    // 自动模式
+    autoMode() {
+      if (this.deviceInfo.status === 'offline') {
+        uni.showToast({
+          title: '设备离线,无法操作',
+          icon: 'none'
+        });
+        return;
+      }
+      
+      uni.showToast({
+        title: '已切换到自动模式',
+        icon: 'success'
+      });
+    },
+    
+    // 处理告警
+    handleAlert(alert) {
+      uni.showModal({
+        title: alert.title,
+        content: alert.description + '\n\n是否标记为已处理?',
+        success: (res) => {
+          if (res.confirm) {
+            const index = this.alerts.findIndex(item => item.id === alert.id);
+            if (index !== -1) {
+              this.alerts[index].handled = true;
+            }
+            
+            uni.showToast({
+              title: '已标记为处理',
+              icon: 'success'
+            });
+          }
+        }
+      });
+    }
+  }
+}
+</script>
+
+<style scoped>
+.container {
+  display: flex;
+  flex-direction: column;
+  min-height: 100vh;
+  background-color: #F8FCF9;
+  padding-bottom: 30rpx;
+}
+
+/* 设备头部信息 */
+.device-header {
+  background-color: #FFFFFF;
+  border-radius: 20rpx;
+  padding: 30rpx;
+  margin: 30rpx 30rpx 30rpx;
+  box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.06);
+  border: 1rpx solid rgba(210, 237, 217, 0.5);
+}
+
+.device-info-row {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 28rpx;
+}
+
+.device-name-container {
+  display: flex;
+  flex-direction: column;
+  align-items: flex-start;
+}
+
+.device-name {
+  font-size: 36rpx;
+  color: #333333;
+  font-weight: 600;
+  margin-bottom: 12rpx;
+}
+
+.status-tag {
+  padding: 6rpx 16rpx 6rpx 28rpx;
+  border-radius: 30rpx;
+  font-size: 24rpx;
+  font-weight: 500;
+  flex-shrink: 0;
+  position: relative;
+  overflow: hidden;
+}
+
+.status-online {
+  background-color: rgba(76, 175, 80, 0.1);
+  color: #3BB44A;
+}
+
+.status-offline {
+  background-color: rgba(245, 108, 108, 0.1);
+  color: #F56C6C;
+  padding-left: 28rpx;
+}
+
+.status-dot {
+  position: absolute;
+  width: 6rpx;
+  height: 6rpx;
+  background-color: #3BB44A;
+  border-radius: 50%;
+  top: 50%;
+  left: 12rpx;
+  transform: translateY(-50%);
+  box-shadow: 0 0 4rpx rgba(76, 175, 80, 0.8);
+  animation: blink 1.5s infinite;
+  display: inline-block;
+}
+
+.status-dot.offline-dot {
+  background-color: #F56C6C;
+  box-shadow: 0 0 4rpx rgba(245, 108, 108, 0.8);
+}
+
+.refresh-btn {
+  width: 48rpx;
+  height: 48rpx;
+  background-color: rgba(76, 175, 80, 0.1);
+  border-radius: 50%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  padding: 12rpx;
+  transition: transform 0.3s ease;
+}
+
+.refresh-btn:active {
+  transform: rotate(180deg);
+}
+
+.refresh-btn.refreshing {
+  animation: spin 1.2s linear infinite;
+}
+
+@keyframes spin {
+  0% {
+    transform: rotate(0deg);
+  }
+  100% {
+    transform: rotate(360deg);
+  }
+}
+
+@keyframes blink {
+  0% {
+    opacity: 0.4;
+  }
+  50% {
+    opacity: 1;
+  }
+  100% {
+    opacity: 0.4;
+  }
+}
+
+.device-meta-row {
+  display: flex;
+  flex-direction: column;
+  background-color: #F9FCFA;
+  padding: 20rpx 24rpx;
+  border-radius: 16rpx;
+  border: 1rpx solid rgba(210, 237, 217, 0.8);
+}
+
+.device-meta-item {
+  display: flex;
+  align-items: center;
+  font-size: 28rpx;
+  margin-top: 18rpx;
+}
+
+.device-meta-item:first-child {
+  margin-top: 0;
+}
+
+.meta-icon {
+  width: 36rpx;
+  height: 36rpx;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  margin-right: 12rpx;
+}
+
+.meta-label {
+  color: #777777;
+  min-width: 140rpx;
+  font-size: 28rpx;
+}
+
+.meta-value {
+  color: #333333;
+  flex: 1;
+  font-size: 28rpx;
+  font-weight: 500;
+}
+
+/* 设备状态区域 */
+.status-section {
+  background: #ffffff;
+  margin: 0 24rpx 20rpx;
+  padding: 32rpx 24rpx;
+  border-radius: 20rpx;
+  box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.06);
+}
+
+.section-title {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  font-size: 32rpx;
+  font-weight: 600;
+  color: #333333;
+  margin-bottom: 24rpx;
+  padding-bottom: 16rpx;
+  border-bottom: 2rpx solid #f0f0f0;
+}
+
+.status-grid {
+  display: grid;
+  grid-template-columns: repeat(2, 1fr);
+  gap: 20rpx;
+}
+
+.status-item {
+  background: #f8f9fa;
+  padding: 24rpx;
+  border-radius: 16rpx;
+  border: 2rpx solid transparent;
+  transition: all 0.3s ease;
+}
+
+.status-item-hover {
+  border-color: #40a9ff;
+  box-shadow: 0 4rpx 12rpx rgba(64, 169, 255, 0.15);
+}
+
+.status-label {
+  font-size: 24rpx;
+  color: #999999;
+  margin-bottom: 8rpx;
+  display: block;
+}
+
+.status-value-container {
+  display: flex;
+  align-items: baseline;
+  margin-bottom: 8rpx;
+}
+
+.status-value {
+  font-size: 36rpx;
+  font-weight: 600;
+  color: #333333;
+}
+
+.status-unit {
+  font-size: 24rpx;
+  color: #666666;
+  margin-left: 4rpx;
+}
+
+.status-time {
+  font-size: 20rpx;
+  color: #cccccc;
+}
+
+/* 状态颜色 */
+.status-working {
+  color: #52c41a;
+}
+
+.status-idle {
+  color: #1890ff;
+}
+
+.status-error {
+  color: #ff4d4f;
+}
+
+.status-good {
+  color: #52c41a;
+}
+
+.status-normal {
+  color: #faad14;
+}
+
+.status-weak {
+  color: #ff4d4f;
+}
+
+.value-warning {
+  color: #faad14;
+}
+
+.value-alert {
+  color: #ff4d4f;
+}
+
+/* 控制区域 */
+.control-section {
+  background: #ffffff;
+  margin: 0 24rpx 20rpx;
+  padding: 32rpx 24rpx;
+  border-radius: 20rpx;
+  box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.06);
+}
+
+.engine-status {
+  padding: 8rpx 16rpx;
+  border-radius: 20rpx;
+  font-size: 24rpx;
+  background-color: #fff1f0;
+  color: #ff4d4f;
+}
+
+.engine-status.engine-on {
+  background-color: #e8f5e8;
+  color: #52c41a;
+}
+
+.control-container {
+  display: flex;
+  flex-direction: column;
+  gap: 32rpx;
+}
+
+/* 引擎控制 */
+.engine-control {
+  display: flex;
+  justify-content: center;
+}
+
+.engine-button {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  padding: 24rpx;
+  background: #ffffff;
+  border-radius: 20rpx;
+  border: 3rpx solid #4CAF50;
+  transition: all 0.3s ease;
+  width: 160rpx;
+  box-shadow: 0 4rpx 12rpx rgba(76, 175, 80, 0.2);
+}
+
+.engine-button.engine-on {
+  background: #F44336;
+  border-color: #F44336;
+  color: white;
+  box-shadow: 0 4rpx 12rpx rgba(244, 67, 54, 0.3);
+}
+
+.engine-button:not(.engine-on) {
+  background: #4CAF50;
+  border-color: #4CAF50;
+  color: white;
+}
+
+.engine-button.disabled {
+  opacity: 0.3;
+  pointer-events: none;
+  background: #DDDDDD;
+  border-color: #DDDDDD;
+}
+
+.engine-button:active {
+  transform: scale(0.95);
+}
+
+.engine-icon {
+  margin-bottom: 12rpx;
+}
+
+.engine-text {
+  font-size: 28rpx;
+  font-weight: 600;
+}
+
+/* 方向控制 */
+.direction-control {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  gap: 20rpx;
+}
+
+.control-row {
+  display: flex;
+  align-items: center;
+  gap: 20rpx;
+}
+
+.control-btn {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  width: 120rpx;
+  height: 120rpx;
+  background: #f8f9fa;
+  border-radius: 12rpx;
+  border: 2rpx solid #e0e0e0;
+  transition: all 0.3s ease;
+  font-size: 24rpx;
+  color: #999999;
+  gap: 8rpx;
+}
+
+.control-btn.active {
+  background: rgba(76, 175, 80, 0.1);
+  border-color: #4CAF50;
+  color: #4CAF50;
+  transform: scale(0.9);
+  box-shadow: 0 2rpx 8rpx rgba(76, 175, 80, 0.3);
+}
+
+.control-btn.disabled {
+  opacity: 0.3;
+  pointer-events: none;
+  background: #DDDDDD;
+  border-color: #DDDDDD;
+}
+
+.control-center {
+  width: 120rpx;
+  height: 120rpx;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  background: #f0f0f0;
+  border-radius: 20rpx;
+  border: 2rpx solid #d0d0d0;
+}
+
+.speed-display {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+}
+
+.speed-value {
+  font-size: 32rpx;
+  font-weight: 600;
+  color: #333333;
+}
+
+.speed-unit {
+  font-size: 20rpx;
+  color: #999999;
+}
+
+/* 快捷控制 */
+.quick-controls {
+  display: flex;
+  justify-content: space-around;
+  padding-top: 20rpx;
+  border-top: 2rpx solid #f0f0f0;
+}
+
+.quick-control-btn {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  gap: 8rpx;
+  padding: 16rpx;
+  border-radius: 16rpx;
+  transition: all 0.3s ease;
+}
+
+.quick-control-btn.disabled {
+  opacity: 0.5;
+  pointer-events: none;
+}
+
+.quick-control-btn:active {
+  background: #f0f0f0;
+}
+
+.quick-icon {
+  width: 48rpx;
+  height: 48rpx;
+  background: #f8f9fa;
+  border-radius: 12rpx;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  border: 2rpx solid #e0e0e0;
+}
+
+.quick-icon.emergency {
+  background: rgba(245, 108, 108, 0.1);
+  border-color: #F56C6C;
+}
+
+.quick-control-btn text {
+  font-size: 24rpx;
+  color: #666666;
+}
+
+/* 告警信息 */
+.alerts-section {
+  margin: 0 30rpx 30rpx;
+  background-color: #FFFFFF;
+  border-radius: 20rpx;
+  padding: 30rpx 24rpx;
+  box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.04);
+  border: 1rpx solid rgba(210, 237, 217, 0.5);
+}
+
+.alert-badge {
+  background-color: #F56C6C;
+  color: #FFFFFF;
+  font-size: 22rpx;
+  border-radius: 30rpx;
+  padding: 2rpx 12rpx;
+  margin-left: 12rpx;
+  font-weight: normal;
+  min-width: 32rpx;
+  text-align: center;
+}
+
+.alerts-list {
+  display: flex;
+  flex-direction: column;
+  gap: 16rpx;
+}
+
+.alert-item {
+  display: flex;
+  align-items: center;
+  padding: 24rpx 20rpx;
+  border-radius: 12rpx;
+  margin-bottom: 16rpx;
+  box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
+  position: relative;
+  border: 1rpx solid transparent;
+  transition: transform 0.2s ease;
+}
+
+.alert-item:active {
+  transform: scale(0.98);
+}
+
+.alert-urgent {
+  background-color: #FEF3F3;
+  border-left: 4rpx solid #F56C6C;
+  border-color: rgba(245, 108, 108, 0.2);
+}
+
+.alert-warning {
+  background-color: #FFF8E6;
+  border-left: 4rpx solid #E6A23C;
+  border-color: rgba(230, 162, 60, 0.2);
+}
+
+.alert-info {
+  background-color: #F2FAF5;
+  border-left: 4rpx solid #67C23A;
+  border-color: rgba(103, 194, 58, 0.2);
+}
+
+.alert-item-icon {
+  width: 50rpx;
+  height: 50rpx;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  margin-right: 16rpx;
+}
+
+.alert-item-info {
+  flex: 1;
+  display: flex;
+  flex-direction: column;
+  gap: 8rpx;
+}
+
+.alert-item-type {
+  font-size: 28rpx;
+  color: #333333;
+  font-weight: 500;
+  margin-bottom: 8rpx;
+}
+
+.alert-item-level {
+  font-size: 24rpx;
+  color: #999999;
+}
+
+.alert-item-time {
+  font-size: 24rpx;
+  color: #999999;
+  margin-left: 16rpx;
+  min-width: 100rpx;
+  text-align: right;
+}
+
+.empty-alert {
+  padding: 60rpx 0;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+}
+
+.empty-text {
+  font-size: 28rpx;
+  color: #999999;
+}
+</style> 

+ 3 - 0
pages/device-list/index.vue

@@ -356,6 +356,9 @@ export default {
       } else if (device.type === 'sensor') {
         // 采集设备跳转到采集设备详情页,同时传递设备编码,便于判断设备子类型
         url = `/pages/device-list/detail-collector?id=${device.id}&code=${device.code}`;
+      } else if (device.type === 'tractor') {
+        // 农机设备跳转到农机设备详情页
+        url = `/pages/device-list/detail-machine?id=${device.id}&deviceId=${device.code}`;
       } else {
         // 其他类型设备暂时使用通用详情页
         url = `/pages/device-detail/index?id=${device.id}&type=${device.type}`;


BIN
static/icons/.DS_Store


+ 179 - 0
static/icons/README.md

@@ -0,0 +1,179 @@
+# 农机设备控制图标库
+
+本图标库专为农机设备控制小程序设计,采用简洁线条风格,符合微信小程序视觉规范。
+
+## 设计规范
+
+- **尺寸**: 
+  - 方向控制:24×24px
+  - 底部按钮:24×24px(统一尺寸)
+  - 启动按钮:28×28px
+- **风格**: 简洁线条风,状态化设计
+- **线宽**: 
+  - 普通状态:2px
+  - 激活状态:3px
+  - 底部按钮:3px
+- **色彩状态**: 
+  - 默认状态:灰色 `#999999`
+  - 激活状态:绿色 `#4CAF50`
+  - 禁用状态:淡灰 `#DDDDDD`
+  - 警告状态:红色 `#F56C6C`
+  - 自动模式:蓝色 `#2196F3`
+- **特点**: 状态区分明确,视觉反馈强烈
+
+## 图标列表
+
+### 1. 启动按钮 (engine_start.svg) - 28×28px
+```svg
+<!-- 放大的白色播放三角形,适配绿色按钮背景 -->
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+  <path d="M7 5V19L19 12L7 5Z" fill="white" stroke="white" stroke-width="0.5" stroke-linejoin="round"/>
+</svg>
+```
+
+### 2. 停止按钮 (engine_stop.svg) - 28×28px
+```svg
+<!-- 白色方形,适配红色按钮背景 -->
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+  <rect x="6" y="6" width="12" height="12" rx="2" fill="white" stroke="white" stroke-width="0.5"/>
+</svg>
+```
+
+### 3. 前进按钮 (arrow_up.svg)
+```svg
+<!-- 简洁向上箭头 -->
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+  <path d="M12 4L12 20" stroke="#4CAF50" stroke-width="2" stroke-linecap="round"/>
+  <path d="M6 10L12 4L18 10" stroke="#4CAF50" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+</svg>
+```
+
+### 4. 后退按钮 (arrow_down.svg)
+```svg
+<!-- 简洁向下箭头 -->
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+  <path d="M12 4L12 20" stroke="#4CAF50" stroke-width="2" stroke-linecap="round"/>
+  <path d="M6 14L12 20L18 14" stroke="#4CAF50" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+</svg>
+```
+
+### 5. 左转按钮 (arrow_left.svg)
+```svg
+<!-- 简洁向左箭头 -->
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+  <path d="M4 12L20 12" stroke="#4CAF50" stroke-width="2" stroke-linecap="round"/>
+  <path d="M10 6L4 12L10 18" stroke="#4CAF50" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+</svg>
+```
+
+### 6. 右转按钮 (arrow_right.svg)
+```svg
+<!-- 简洁向右箭头 -->
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+  <path d="M4 12L20 12" stroke="#4CAF50" stroke-width="2" stroke-linecap="round"/>
+  <path d="M14 6L20 12L14 18" stroke="#4CAF50" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+</svg>
+```
+
+### 7. 紧急停止 (emergency_stop.svg) - 24×24px
+```svg
+<!-- 警告三角形,保持警告感 -->
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+  <path d="M12 2L22 20H2L12 2Z" stroke="#F56C6C" stroke-width="2" stroke-linejoin="round" fill="none"/>
+  <path d="M12 8L12 14" stroke="#F56C6C" stroke-width="2" stroke-linecap="round"/>
+  <circle cx="12" cy="17" r="1" fill="#F56C6C"/>
+</svg>
+```
+
+### 8. 返回充电 (home.svg) - 24×24px
+```svg
+<!-- 简单房子图标 -->
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+  <path d="M3 20V12L12 3L21 12V20H3Z" stroke="#4CAF50" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
+  <path d="M9 20V14H15V20" stroke="#4CAF50" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
+</svg>
+```
+
+### 9. 自动模式 (auto_mode.svg) - 24×24px
+```svg
+<!-- 简洁的AUTO文字图标 -->
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+  <text x="12" y="14" text-anchor="middle" fill="#2196F3" font-size="10" font-weight="700" font-family="Arial, sans-serif">AUTO</text>
+</svg>
+```
+
+## 设计特点
+
+- **状态化设计**: 默认灰色、激活绿色、禁用淡灰,状态区分明确
+- **视觉突出**: 启动按钮放大1.2倍,底部图标36px,视觉层次清晰
+- **强化反馈**: 紧急停止加粗线条,使用#F56C6C增强警告感
+- **统一圆角**: 按钮圆角统一为12px,视觉更协调
+- **智能识别**: 自动模式改为AUTO文字图标,更加直观
+
+## 按钮样式设计
+
+### 启动按钮
+- **未启动**: 绿色背景 + 白色图标 + 绿色边框
+- **已启动**: 红色背景 + 白色图标 + 红色边框
+- **禁用**: 淡灰背景 + 淡灰边框
+
+### 方向控制按钮
+- **默认**: 灰色图标 + 浅灰背景
+- **激活**: 绿色图标 + 淡绿背景 + 绿色边框 + 阴影
+- **禁用**: 淡灰图标 + 淡灰背景
+
+### 底部功能按钮
+- **图标尺寸**: 24×24px,统一尺寸
+- **圆角**: 12px统一圆角
+- **颜色**: 功能性语义色彩
+
+## 使用方法
+
+```vue
+<!-- 启动按钮 28px -->
+<image src="/static/icons/engine_start.svg" mode="aspectFit" style="width: 28px; height: 28px;"></image>
+
+<!-- 方向控制 24px -->
+<image src="/static/icons/arrow_up.svg" mode="aspectFit" style="width: 24px; height: 24px;"></image>
+
+<!-- 底部按钮 24px -->
+<image src="/static/icons/emergency_stop.svg" mode="aspectFit" style="width: 24px; height: 24px;"></image>
+```
+
+## CSS样式配合
+
+```css
+/* 启动按钮样式 */
+.engine-button {
+  background: #4CAF50;
+  border: 3px solid #4CAF50;
+  color: white;
+  border-radius: 20rpx;
+  box-shadow: 0 4rpx 12rpx rgba(76, 175, 80, 0.2);
+}
+
+.engine-button.engine-on {
+  background: #F44336;
+  border-color: #F44336;
+  box-shadow: 0 4rpx 12rpx rgba(244, 67, 54, 0.3);
+}
+
+/* 方向控制按钮 */
+.control-btn.active {
+  background: rgba(76, 175, 80, 0.1);
+  border-color: #4CAF50;
+  color: #4CAF50;
+  transform: scale(0.9);
+  box-shadow: 0 2rpx 8rpx rgba(76, 175, 80, 0.3);
+}
+
+/* 禁用状态 */
+.disabled {
+  opacity: 0.3;
+  background: #DDDDDD;
+  border-color: #DDDDDD;
+  pointer-events: none;
+}
+```
+
+这套图标库经过专业优化,完全满足农机设备控制界面的专业需求,具有出色的视觉效果和用户体验。 

BIN
static/icons/add_task.png


+ 5 - 0
static/icons/arrow_down.svg

@@ -0,0 +1,5 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+  <!-- 默认状态向下箭头 -->
+  <path d="M12 4L12 20" stroke="#999999" stroke-width="2" stroke-linecap="round"/>
+  <path d="M6 14L12 20L18 14" stroke="#999999" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+</svg> 

+ 5 - 0
static/icons/arrow_left.svg

@@ -0,0 +1,5 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+  <!-- 默认状态向左箭头 -->
+  <path d="M4 12L20 12" stroke="#999999" stroke-width="2" stroke-linecap="round"/>
+  <path d="M10 6L4 12L10 18" stroke="#999999" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+</svg> 

+ 5 - 0
static/icons/arrow_right.svg

@@ -0,0 +1,5 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+  <!-- 默认状态向右箭头 -->
+  <path d="M4 12L20 12" stroke="#999999" stroke-width="2" stroke-linecap="round"/>
+  <path d="M14 6L20 12L14 18" stroke="#999999" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+</svg> 

+ 5 - 0
static/icons/arrow_up.svg

@@ -0,0 +1,5 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+  <!-- 默认状态向上箭头 -->
+  <path d="M12 4L12 20" stroke="#999999" stroke-width="2" stroke-linecap="round"/>
+  <path d="M6 10L12 4L18 10" stroke="#999999" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+</svg> 

+ 5 - 0
static/icons/arrow_up_active.svg

@@ -0,0 +1,5 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+  <!-- 激活状态向上箭头 -->
+  <path d="M12 4L12 20" stroke="#4CAF50" stroke-width="3" stroke-linecap="round"/>
+  <path d="M6 10L12 4L18 10" stroke="#4CAF50" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/>
+</svg> 

+ 4 - 0
static/icons/auto_mode.svg

@@ -0,0 +1,4 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+  <!-- AUTO文字,居中显示 -->
+  <text x="12" y="14" text-anchor="middle" fill="#2196F3" font-size="8" font-weight="700" font-family="Arial, sans-serif">AUTO</text>
+</svg> 

+ 7 - 0
static/icons/emergency_stop.svg

@@ -0,0 +1,7 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+  <!-- 警告三角形 -->
+  <path d="M12 3L20 19H4L12 3Z" stroke="#F56C6C" stroke-width="2" stroke-linejoin="round" fill="none"/>
+  <!-- 感叹号 -->
+  <path d="M12 9L12 14" stroke="#F56C6C" stroke-width="2" stroke-linecap="round"/>
+  <circle cx="12" cy="16" r="1" fill="#F56C6C"/>
+</svg> 

+ 4 - 0
static/icons/engine_start.svg

@@ -0,0 +1,4 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+  <!-- 放大的播放三角形,1.2倍大小 -->
+  <path d="M7 5V19L19 12L7 5Z" fill="white" stroke="white" stroke-width="0.5" stroke-linejoin="round"/>
+</svg> 

+ 4 - 0
static/icons/engine_stop.svg

@@ -0,0 +1,4 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+  <!-- 停止方块,白色填充 -->
+  <rect x="6" y="6" width="12" height="12" rx="2" fill="white" stroke="white" stroke-width="0.5"/>
+</svg> 

+ 6 - 0
static/icons/home.svg

@@ -0,0 +1,6 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+  <!-- 简单房子轮廓 -->
+  <path d="M4 19V12L12 4L20 12V19H4Z" stroke="#4CAF50" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
+  <!-- 门 -->
+  <path d="M10 19V14H14V19" stroke="#4CAF50" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
+</svg> 

BIN
static/icons/task_icon.png


BIN
static/icons/upload_add.png