管灌系统农户端微信小程序(嘉峪关应用)
更新创建灌溉计划页面,添加API通信模块,优化项目选择器和时间选择功能;增强用户体验,修复轮灌组显示逻辑,调整样式和配置文件。
11个文件已修改
10个文件已添加
2059 ■■■■■ 已修改文件
README.md 109 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
api/config.js 87 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
images/add-icon.svg 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
images/duration-icon.svg 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
images/execute-icon.svg 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
images/group-icon.svg 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
images/hourglass.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
images/info.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
images/progress-icon.svg 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
images/publish-icon.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
images/stop-icon.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
images/time-icon.svg 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
miniprogram_npm/dayjs/index.js 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/createIrrigation/createIrrigation.js 740 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/createIrrigation/createIrrigation.json 13 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/createIrrigation/createIrrigation.wxml 91 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/createIrrigation/createIrrigation.wxss 100 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/home/home.js 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/irrigation/irrigation.js 220 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/irrigation/irrigation.wxml 137 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/irrigation/irrigation.wxss 507 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
README.md
@@ -71,6 +71,115 @@
4. **云开发**:项目可能使用了微信小程序云开发功能,用于数据存储和云函数。
## API通信模块
API目录包含了项目中所有与后端通信相关的文件,提供了统一的网络请求处理机制。
### 文件结构
- **request.js**: 网络请求核心模块,封装了微信小程序的`wx.request`接口
  - `request()`: 基础请求函数,处理请求配置、请求头设置和响应处理
  - `get()`: GET请求简化方法
  - `post()`: POST请求简化方法
  - 自动处理token验证、错误处理和loading状态
- **config.js**: 配置文件,定义API基础URL和环境配置
  - 支持多项目环境:嘉峪关(JYG)、民勤(MQ)、测试环境(TEST)
  - `setBaseUrl()`: 动态切换项目环境的函数
- **env.js**: 环境变量配置,定义当前运行环境(生产/测试)
- **statusCode.js**: HTTP状态码常量定义,用于统一处理响应状态
### 使用方法
1. **GET请求**:
   ```javascript
   const { get } = require('../../api/request')
   // 获取项目列表
   get({
     url: '/wx/irrigation/getSimpleProjects',
     isShowLoding: true
   }).then(res => {
     if (res.success) {
       // 处理成功响应
       console.log(res.content)
     } else {
       wx.showToast({
         title: res.msg || '请求失败',
         icon: 'none'
       })
     }
   }).catch(err => {
     console.error('请求失败:', err)
     wx.showToast({
       title: '请求失败',
       icon: 'none'
     })
   })
   ```
2. **POST请求**:
   ```javascript
   const { post } = require('../../api/request')
   // 获取轮灌组列表
   post({
     url: '/wx/irrigation/getSimpleGroups',
     data: {
       projectId: '2025032017240700001',
       planName: '2025032501',
       startupMode: 1,
       operatorId: 2024090516595200300,
       schedules: [
         {
           groupId: 2025032017371700000,
           duration: 5
         }
       ]
     },
     isShowLoding: true
   }).then(res => {
     if (res.success) {
       // 处理成功响应
       console.log(res.content)
     } else {
       wx.showToast({
         title: res.msg || '请求失败',
         icon: 'none'
       })
     }
   }).catch(err => {
     console.error('请求失败:', err)
     wx.showToast({
       title: '请求失败',
       icon: 'none'
     })
   })
   ```
3. **切换项目环境**:
   ```javascript
   const { setBaseUrl } = require('../../api/config')
   // 切换到民勤项目
   setBaseUrl('MQ')
   // 切换到嘉峪关项目
   setBaseUrl('JYG')
   // 切换到测试环境
   setBaseUrl('TEST')
   ```
所有网络请求会自动处理:
- token 认证(通过请求头 Authorization)
- 项目标识(通过请求头 tag)
- 应用标识(通过请求头 appId)
- 错误处理和响应解析
- 加载状态显示(通过 isShowLoding 参数控制)
## 代码风格和规范
1. **文件命名**:
api/config.js
@@ -2,64 +2,65 @@
let BASEURL
const PROJECT_URLS = {
  JYG: 'https://irrigate.dayuyanjiuyuan.top/', // 嘉峪关项目
  MQ: 'https://shifanqu1.dayuyanjiuyuan.top/' ,   // 民勤项目
  TEST:'https://no253541tf71.vicp.fun/'// test项目
// TEST:'http://192.168.40.182:8087/'// test项目
//   MQ: 'https://no253541tf71.vicp.fun/'    // 民勤项目
    JYG: 'https://irrigate.dayuyanjiuyuan.top/', // 嘉峪关项目
    MQ: 'https://shifanqu1.dayuyanjiuyuan.top/',   // 民勤项目
    //   TEST:'https://no253541tf71.vicp.fun/'// test项目
    // TEST:'http://192.168.40.182:8087/'// test项目
    //   MQ: 'https://no253541tf71.vicp.fun/'    // 民勤项目
    TEST: 'http://192.168.40.166:54321/'
}
// 尝试从本地存储获取已选择的项目
let selectedProject = 'JYG'; // 默认为嘉峪关项目
try {
  // 尝试从本地存储获取已选择的项目
  if (typeof wx !== 'undefined') {
    try {
      const selectedProjectFromStorage = wx.getStorageSync('selectedProject');
      if (selectedProjectFromStorage) {
        selectedProject = selectedProjectFromStorage;
        console.log('从本地存储加载项目设置:', selectedProject);
      }
    } catch (e) {
      console.error('从本地存储获取项目失败:', e);
    // 尝试从本地存储获取已选择的项目
    if (typeof wx !== 'undefined') {
        try {
            const selectedProjectFromStorage = wx.getStorageSync('selectedProject');
            if (selectedProjectFromStorage) {
                selectedProject = selectedProjectFromStorage;
                console.log('从本地存储加载项目设置:', selectedProject);
            }
        } catch (e) {
            console.error('从本地存储获取项目失败:', e);
        }
    }
  }
  // 如果全局应用已初始化,也尝试从全局变量获取
  const app = getApp();
  if (app && app.globalData && app.globalData.selectedProject) {
    selectedProject = app.globalData.selectedProject;
    console.log('从全局变量加载项目设置:', selectedProject);
  }
    // 如果全局应用已初始化,也尝试从全局变量获取
    const app = getApp();
    if (app && app.globalData && app.globalData.selectedProject) {
        selectedProject = app.globalData.selectedProject;
        console.log('从全局变量加载项目设置:', selectedProject);
    }
} catch (e) {
  console.error('获取已选择项目失败:', e);
    console.error('获取已选择项目失败:', e);
}
switch (ENV) {
  case 'production':
    BASEURL = PROJECT_URLS[selectedProject] || PROJECT_URLS.JYG;
    break;
  case 'test':
    // BASEURL 将根据用户选择的项目动态设置
    BASEURL = PROJECT_URLS[selectedProject] || PROJECT_URLS.JYG;
    break;
  default:
    BASEURL = PROJECT_URLS[selectedProject] || PROJECT_URLS.JYG;
    break;
    case 'production':
        BASEURL = PROJECT_URLS[selectedProject] || PROJECT_URLS.JYG;
        break;
    case 'test':
        // BASEURL 将根据用户选择的项目动态设置
        BASEURL = PROJECT_URLS[selectedProject] || PROJECT_URLS.JYG;
        break;
    default:
        BASEURL = PROJECT_URLS[selectedProject] || PROJECT_URLS.JYG;
        break;
}
// 导出动态设置 BASEURL 的函数
function setBaseUrl(project) {
  if (PROJECT_URLS[project]) {
    BASEURL = PROJECT_URLS[project];
    console.log('动态设置 BASEURL:', BASEURL);
    return true;
  }
  return false;
    if (PROJECT_URLS[project]) {
        BASEURL = PROJECT_URLS[project];
        console.log('动态设置 BASEURL:', BASEURL);
        return true;
    }
    return false;
}
module.exports = {
  BASEURL,// 项目接口地址,支持多域名
  PROJECT_URLS,
  setBaseUrl
    BASEURL,// 项目接口地址,支持多域名
    PROJECT_URLS,
    setBaseUrl
}
images/add-icon.svg
New file
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
  <path d="M19 13H13V19H11V13H5V11H11V5H13V11H19V13Z" fill="#FFFFFF"/>
</svg>
images/duration-icon.svg
New file
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
  <path d="M12 2C6.48 2 2 6.48 2 12C2 17.52 6.48 22 12 22C17.52 22 22 17.52 22 12C22 6.48 17.52 2 12 2ZM12 20C7.59 20 4 16.41 4 12C4 7.59 7.59 4 12 4C16.41 4 20 7.59 20 12C20 16.41 16.41 20 12 20Z" fill="#666666"/>
  <path d="M12 6V12L16.5 14.25L17.25 13.02L13.5 10.75V6H12Z" fill="#666666"/>
</svg>
images/execute-icon.svg
New file
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
  <path d="M8 5V19L19 12L8 5Z" fill="#FFFFFF"/>
</svg>
images/group-icon.svg
New file
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
  <path d="M12 2C6.48 2 2 6.48 2 12C2 17.52 6.48 22 12 22C17.52 22 22 17.52 22 12C22 6.48 17.52 2 12 2ZM12 20C7.59 20 4 16.41 4 12C4 7.59 7.59 4 12 4C16.41 4 20 7.59 20 12C20 16.41 16.41 20 12 20Z" fill="#666666"/>
  <path d="M12 6C8.69 6 6 8.69 6 12C6 15.31 8.69 18 12 18C15.31 18 18 15.31 18 12C18 8.69 15.31 6 12 6ZM12 16C9.79 16 8 14.21 8 12C8 9.79 9.79 8 12 8C14.21 8 16 9.79 16 12C16 14.21 14.21 16 12 16Z" fill="#666666"/>
</svg>
images/hourglass.svg
New file
@@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8"?><svg width="24" height="24" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M7 4H41" stroke="#333" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/><path d="M7 44H41" stroke="#333" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/><path d="M11 44C13.6667 30.6611 18 23.9944 24 24C30 24.0056 34.3333 30.6722 37 44H11Z" fill="none" stroke="#333" stroke-width="4" stroke-linejoin="round"/><path d="M37 4C34.3333 17.3389 30 24.0056 24 24C18 23.9944 13.6667 17.3278 11 4H37Z" fill="none" stroke="#333" stroke-width="4" stroke-linejoin="round"/><path d="M21 15H27" stroke="#333" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/><path d="M19 38H29" stroke="#333" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/></svg>
images/info.svg
New file
@@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8"?><svg width="24" height="24" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M24 44C29.5228 44 34.5228 41.7614 38.1421 38.1421C41.7614 34.5228 44 29.5228 44 24C44 18.4772 41.7614 13.4772 38.1421 9.85786C34.5228 6.23858 29.5228 4 24 4C18.4772 4 13.4772 6.23858 9.85786 9.85786C6.23858 13.4772 4 18.4772 4 24C4 29.5228 6.23858 34.5228 9.85786 38.1421C13.4772 41.7614 18.4772 44 24 44Z" fill="none" stroke="#333" stroke-width="4" stroke-linejoin="round"/><path fill-rule="evenodd" clip-rule="evenodd" d="M24 11C25.3807 11 26.5 12.1193 26.5 13.5C26.5 14.8807 25.3807 16 24 16C22.6193 16 21.5 14.8807 21.5 13.5C21.5 12.1193 22.6193 11 24 11Z" fill="#333"/><path d="M24.5 34V20H23.5H22.5" stroke="#333" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/><path d="M21 34H28" stroke="#333" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/></svg>
images/progress-icon.svg
New file
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
  <path d="M12 2C6.48 2 2 6.48 2 12C2 17.52 6.48 22 12 22C17.52 22 22 17.52 22 12C22 6.48 17.52 2 12 2ZM12 20C7.59 20 4 16.41 4 12C4 7.59 7.59 4 12 4C16.41 4 20 7.59 20 12C20 16.41 16.41 20 12 20Z" fill="#666666"/>
  <path d="M12 6V12L16.5 14.25L17.25 13.02L13.5 10.75V6H12Z" fill="#666666"/>
  <path d="M12 2C6.48 2 2 6.48 2 12C2 17.52 6.48 22 12 22C17.52 22 22 17.52 22 12C22 6.48 17.52 2 12 2ZM12 20C7.59 20 4 16.41 4 12C4 7.59 7.59 4 12 4C16.41 4 20 7.59 20 12C20 16.41 16.41 20 12 20Z" fill="#666666" fill-opacity="0.3"/>
</svg>
images/publish-icon.svg
New file
@@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8"?><svg width="24" height="24" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M42 6L4 20.1383L24 24.0083L29.0052 44L42 6Z" stroke="#fff" stroke-width="4" stroke-linejoin="round"/><path d="M24.0083 24.0084L29.6651 18.3516" stroke="#fff" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/></svg>
images/stop-icon.svg
New file
@@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8"?><svg width="24" height="24" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M14.5 8C13.8406 8.37652 13.2062 8.79103 12.6 9.24051C11.5625 10.0097 10.6074 10.8814 9.75 11.8402C6.79377 15.1463 5 19.4891 5 24.2455C5 34.6033 13.5066 43 24 43C34.4934 43 43 34.6033 43 24.2455C43 19.4891 41.2062 15.1463 38.25 11.8402C37.3926 10.8814 36.4375 10.0097 35.4 9.24051C34.7938 8.79103 34.1594 8.37652 33.5 8" stroke="#fff" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/><path d="M24 4V24" stroke="#fff" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/></svg>
images/time-icon.svg
New file
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
  <path d="M12 2C6.48 2 2 6.48 2 12C2 17.52 6.48 22 12 22C17.52 22 22 17.52 22 12C22 6.48 17.52 2 12 2ZM12 20C7.59 20 4 16.41 4 12C4 7.59 7.59 4 12 4C16.41 4 20 7.59 20 12C20 16.41 16.41 20 12 20Z" fill="#666666"/>
  <path d="M12.5 7H11V13L16.25 16.15L17 14.92L12.5 12.25V7Z" fill="#666666"/>
</svg>
miniprogram_npm/dayjs/index.js
@@ -4,10 +4,10 @@
var __REQUIRE__ = function(modId, source) { if(!__MODS__[modId]) return require(source); if(!__MODS__[modId].status) { var m = __MODS__[modId].m; m._exports = m._tempexports; var desp = Object.getOwnPropertyDescriptor(m, "exports"); if (desp && desp.configurable) Object.defineProperty(m, "exports", { set: function (val) { if(typeof val === "object" && val !== m._exports) { m._exports.__proto__ = val.__proto__; Object.keys(val).forEach(function (k) { m._exports[k] = val[k]; }); } m._tempexports = val }, get: function () { return m._tempexports; } }); __MODS__[modId].status = 1; __MODS__[modId].func(__MODS__[modId].req, m, m.exports); } return __MODS__[modId].m.exports; };
var __REQUIRE_WILDCARD__ = function(obj) { if(obj && obj.__esModule) { return obj; } else { var newObj = {}; if(obj != null) { for(var k in obj) { if (Object.prototype.hasOwnProperty.call(obj, k)) newObj[k] = obj[k]; } } newObj.default = obj; return newObj; } };
var __REQUIRE_DEFAULT__ = function(obj) { return obj && obj.__esModule ? obj.default : obj; };
__DEFINE__(1742351136645, function(require, module, exports) {
__DEFINE__(1742956904314, function(require, module, exports) {
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).dayjs=e()}(this,(function(){var t=1e3,e=6e4,n=36e5,r="millisecond",i="second",s="minute",u="hour",a="day",o="week",c="month",f="quarter",h="year",d="date",l="Invalid Date",$=/^(\d{4})[-/]?(\d{1,2})?[-/]?(\d{0,2})[Tt\s]*(\d{1,2})?:?(\d{1,2})?:?(\d{1,2})?[.:]?(\d+)?$/,y=/\[([^\]]+)]|Y{1,4}|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a|A|m{1,2}|s{1,2}|Z{1,2}|SSS/g,M={name:"en",weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),ordinal:function(t){var e=["th","st","nd","rd"],n=t%100;return"["+t+(e[(n-20)%10]||e[n]||e[0])+"]"}},m=function(t,e,n){var r=String(t);return!r||r.length>=e?t:""+Array(e+1-r.length).join(n)+t},v={s:m,z:function(t){var e=-t.utcOffset(),n=Math.abs(e),r=Math.floor(n/60),i=n%60;return(e<=0?"+":"-")+m(r,2,"0")+":"+m(i,2,"0")},m:function t(e,n){if(e.date()<n.date())return-t(n,e);var r=12*(n.year()-e.year())+(n.month()-e.month()),i=e.clone().add(r,c),s=n-i<0,u=e.clone().add(r+(s?-1:1),c);return+(-(r+(n-i)/(s?i-u:u-i))||0)},a:function(t){return t<0?Math.ceil(t)||0:Math.floor(t)},p:function(t){return{M:c,y:h,w:o,d:a,D:d,h:u,m:s,s:i,ms:r,Q:f}[t]||String(t||"").toLowerCase().replace(/s$/,"")},u:function(t){return void 0===t}},g="en",D={};D[g]=M;var p="$isDayjsObject",S=function(t){return t instanceof _||!(!t||!t[p])},w=function t(e,n,r){var i;if(!e)return g;if("string"==typeof e){var s=e.toLowerCase();D[s]&&(i=s),n&&(D[s]=n,i=s);var u=e.split("-");if(!i&&u.length>1)return t(u[0])}else{var a=e.name;D[a]=e,i=a}return!r&&i&&(g=i),i||!r&&g},O=function(t,e){if(S(t))return t.clone();var n="object"==typeof e?e:{};return n.date=t,n.args=arguments,new _(n)},b=v;b.l=w,b.i=S,b.w=function(t,e){return O(t,{locale:e.$L,utc:e.$u,x:e.$x,$offset:e.$offset})};var _=function(){function M(t){this.$L=w(t.locale,null,!0),this.parse(t),this.$x=this.$x||t.x||{},this[p]=!0}var m=M.prototype;return m.parse=function(t){this.$d=function(t){var e=t.date,n=t.utc;if(null===e)return new Date(NaN);if(b.u(e))return new Date;if(e instanceof Date)return new Date(e);if("string"==typeof e&&!/Z$/i.test(e)){var r=e.match($);if(r){var i=r[2]-1||0,s=(r[7]||"0").substring(0,3);return n?new Date(Date.UTC(r[1],i,r[3]||1,r[4]||0,r[5]||0,r[6]||0,s)):new Date(r[1],i,r[3]||1,r[4]||0,r[5]||0,r[6]||0,s)}}return new Date(e)}(t),this.init()},m.init=function(){var t=this.$d;this.$y=t.getFullYear(),this.$M=t.getMonth(),this.$D=t.getDate(),this.$W=t.getDay(),this.$H=t.getHours(),this.$m=t.getMinutes(),this.$s=t.getSeconds(),this.$ms=t.getMilliseconds()},m.$utils=function(){return b},m.isValid=function(){return!(this.$d.toString()===l)},m.isSame=function(t,e){var n=O(t);return this.startOf(e)<=n&&n<=this.endOf(e)},m.isAfter=function(t,e){return O(t)<this.startOf(e)},m.isBefore=function(t,e){return this.endOf(e)<O(t)},m.$g=function(t,e,n){return b.u(t)?this[e]:this.set(n,t)},m.unix=function(){return Math.floor(this.valueOf()/1e3)},m.valueOf=function(){return this.$d.getTime()},m.startOf=function(t,e){var n=this,r=!!b.u(e)||e,f=b.p(t),l=function(t,e){var i=b.w(n.$u?Date.UTC(n.$y,e,t):new Date(n.$y,e,t),n);return r?i:i.endOf(a)},$=function(t,e){return b.w(n.toDate()[t].apply(n.toDate("s"),(r?[0,0,0,0]:[23,59,59,999]).slice(e)),n)},y=this.$W,M=this.$M,m=this.$D,v="set"+(this.$u?"UTC":"");switch(f){case h:return r?l(1,0):l(31,11);case c:return r?l(1,M):l(0,M+1);case o:var g=this.$locale().weekStart||0,D=(y<g?y+7:y)-g;return l(r?m-D:m+(6-D),M);case a:case d:return $(v+"Hours",0);case u:return $(v+"Minutes",1);case s:return $(v+"Seconds",2);case i:return $(v+"Milliseconds",3);default:return this.clone()}},m.endOf=function(t){return this.startOf(t,!1)},m.$set=function(t,e){var n,o=b.p(t),f="set"+(this.$u?"UTC":""),l=(n={},n[a]=f+"Date",n[d]=f+"Date",n[c]=f+"Month",n[h]=f+"FullYear",n[u]=f+"Hours",n[s]=f+"Minutes",n[i]=f+"Seconds",n[r]=f+"Milliseconds",n)[o],$=o===a?this.$D+(e-this.$W):e;if(o===c||o===h){var y=this.clone().set(d,1);y.$d[l]($),y.init(),this.$d=y.set(d,Math.min(this.$D,y.daysInMonth())).$d}else l&&this.$d[l]($);return this.init(),this},m.set=function(t,e){return this.clone().$set(t,e)},m.get=function(t){return this[b.p(t)]()},m.add=function(r,f){var d,l=this;r=Number(r);var $=b.p(f),y=function(t){var e=O(l);return b.w(e.date(e.date()+Math.round(t*r)),l)};if($===c)return this.set(c,this.$M+r);if($===h)return this.set(h,this.$y+r);if($===a)return y(1);if($===o)return y(7);var M=(d={},d[s]=e,d[u]=n,d[i]=t,d)[$]||1,m=this.$d.getTime()+r*M;return b.w(m,this)},m.subtract=function(t,e){return this.add(-1*t,e)},m.format=function(t){var e=this,n=this.$locale();if(!this.isValid())return n.invalidDate||l;var r=t||"YYYY-MM-DDTHH:mm:ssZ",i=b.z(this),s=this.$H,u=this.$m,a=this.$M,o=n.weekdays,c=n.months,f=n.meridiem,h=function(t,n,i,s){return t&&(t[n]||t(e,r))||i[n].slice(0,s)},d=function(t){return b.s(s%12||12,t,"0")},$=f||function(t,e,n){var r=t<12?"AM":"PM";return n?r.toLowerCase():r};return r.replace(y,(function(t,r){return r||function(t){switch(t){case"YY":return String(e.$y).slice(-2);case"YYYY":return b.s(e.$y,4,"0");case"M":return a+1;case"MM":return b.s(a+1,2,"0");case"MMM":return h(n.monthsShort,a,c,3);case"MMMM":return h(c,a);case"D":return e.$D;case"DD":return b.s(e.$D,2,"0");case"d":return String(e.$W);case"dd":return h(n.weekdaysMin,e.$W,o,2);case"ddd":return h(n.weekdaysShort,e.$W,o,3);case"dddd":return o[e.$W];case"H":return String(s);case"HH":return b.s(s,2,"0");case"h":return d(1);case"hh":return d(2);case"a":return $(s,u,!0);case"A":return $(s,u,!1);case"m":return String(u);case"mm":return b.s(u,2,"0");case"s":return String(e.$s);case"ss":return b.s(e.$s,2,"0");case"SSS":return b.s(e.$ms,3,"0");case"Z":return i}return null}(t)||i.replace(":","")}))},m.utcOffset=function(){return 15*-Math.round(this.$d.getTimezoneOffset()/15)},m.diff=function(r,d,l){var $,y=this,M=b.p(d),m=O(r),v=(m.utcOffset()-this.utcOffset())*e,g=this-m,D=function(){return b.m(y,m)};switch(M){case h:$=D()/12;break;case c:$=D();break;case f:$=D()/3;break;case o:$=(g-v)/6048e5;break;case a:$=(g-v)/864e5;break;case u:$=g/n;break;case s:$=g/e;break;case i:$=g/t;break;default:$=g}return l?$:b.a($)},m.daysInMonth=function(){return this.endOf(c).$D},m.$locale=function(){return D[this.$L]},m.locale=function(t,e){if(!t)return this.$L;var n=this.clone(),r=w(t,e,!0);return r&&(n.$L=r),n},m.clone=function(){return b.w(this.$d,this)},m.toDate=function(){return new Date(this.valueOf())},m.toJSON=function(){return this.isValid()?this.toISOString():null},m.toISOString=function(){return this.$d.toISOString()},m.toString=function(){return this.$d.toUTCString()},M}(),k=_.prototype;return O.prototype=k,[["$ms",r],["$s",i],["$m",s],["$H",u],["$W",a],["$M",c],["$y",h],["$D",d]].forEach((function(t){k[t[1]]=function(e){return this.$g(e,t[0],t[1])}})),O.extend=function(t,e){return t.$i||(t(e,_,O),t.$i=!0),O},O.locale=w,O.isDayjs=S,O.unix=function(t){return O(1e3*t)},O.en=D[g],O.Ls=D,O.p={},O}));
}, function(modId) {var map = {}; return __REQUIRE__(map[modId], modId); })
return __REQUIRE__(1742351136645);
return __REQUIRE__(1742956904314);
})()
//miniprogram-npm-outsideDeps=[]
//# sourceMappingURL=index.js.map
pages/createIrrigation/createIrrigation.js
@@ -1,334 +1,450 @@
const app = getApp();
const { get, post } = require('../../api/request');
const dayjs = require('dayjs');
Page({
  /**
   * 页面的初始数据
   */
  data: {
    // 表单数据
    planCode: '', // 计划编号
    startTime: '', // 灌溉开始时间
    pickerValue: '', // 时间选择器的值
    timePickerVisible: false, // 时间选择器是否可见
    // 项目选择器相关
    projectPickerVisible: false,
    projectPickerValue: [],
    selectedProject: null,
    projectOptions: [],
    totalDuration: 0, // 添加总灌溉时间
    // 测试数据
    projectList: [
      {
        id: 1,
        name: '测试项目一',
        groups: [
          {
            id: 101,
            name: '轮灌组A',
            duration: 30,
            selected: false
          },
          {
            id: 102,
            name: '轮灌组B',
            duration: 45,
            selected: false
          },
          {
            id: 103,
            name: '轮灌组C',
            duration: 60,
            selected: false
          }
        ]
      },
      {
        id: 2,
        name: '测试项目二',
        groups: [
          {
            id: 201,
            name: '轮灌组1',
            duration: 40,
            selected: false
          },
          {
            id: 202,
            name: '轮灌组2',
            duration: 50,
            selected: false
          }
        ]
      },
      {
        id: 3,
        name: '测试项目三',
        groups: [
          {
            id: 301,
            name: '东区轮灌组',
            duration: 35,
            selected: false
          },
          {
            id: 302,
            name: '西区轮灌组',
            duration: 55,
            selected: false
          },
          {
            id: 303,
            name: '南区轮灌组',
            duration: 25,
            selected: false
          }
        ]
      }
    ]
  },
    /**
     * 页面的初始数据
     */
    data: {
        // 表单数据
        planCode: '', // 计划编号
        startTime: '', // 灌溉开始时间
        pickerValue: '', // 时间选择器的值
        timePickerVisible: false, // 时间选择器是否可见
        timeInfoVisible: false, // 时间提示弹窗是否可见
        // 项目选择器相关
        projectPickerVisible: false,
        projectPickerValue: [],
        selectedProject: null,
        projectOptions: [],
        totalDuration: 0, // 添加总灌溉时间
        // 项目列表
        projectList: [],
        // 时间选择器选项
        timeOptions: [],
        // 轮灌组列表刷新状态
        isRefreshing: false
    },
  /**
   * 生命周期函数--监听页面加载
   */
  onLoad: function (options) {
    // 初始化项目选择器选项
    const projectOptions = this.data.projectList.map(project => ({
      label: project.name,
      value: project.id
    }));
    this.setData({
      projectOptions: projectOptions // 直接使用一维数组,不需要包装成二维数组
    });
  },
    /**
     * 生命周期函数--监听页面加载
     */
    onLoad: function (options) {
        // 生成计划编号
        this.generatePlanCode();
        // 设置时间选择器的初始值
        const now = dayjs();
        this.setData({
            pickerValue: now.add(8.5, 'hour').format('YYYY-MM-DD HH:mm')
        });
        // 获取项目列表
        this.fetchProjects();
    },
  /**
   * 获取项目和轮灌组数据
   */
  fetchProjectsAndGroups: function () {
    // 这里可以添加API请求逻辑,获取真实数据
    // wx.request({
    //   url: 'your-api-url',
    //   success: (res) => {
    //     this.setData({
    //       projectList: res.data
    //     });
    //   }
    // });
  },
    /**
     * 生成计划编号
     */
    generatePlanCode: function () {
        const now = dayjs();
        const dateStr = now.format('YYYYMMDD');
        const randomNum = Math.floor(Math.random() * 10000).toString().padStart(4, '0');
        const planCode = `${dateStr}${randomNum}`;
        this.setData({ planCode });
    },
  /**
   * 处理计划编号输入
   */
  onPlanCodeInput: function (e) {
    this.setData({
      planCode: e.detail.value
    });
  },
    /**
     * 获取项目列表
     */
    fetchProjects: function () {
        return get({
            url: '/wx/irrigation/getSimpleProjects',
            isShowLoding: true
        }).then(res => {
            if (res.success) {
                const projectList = res.content.obj.map(project => ({
                    id: project.projectId,
                    name: project.projectName,
                    groupCount: project.groupCount,
                    groups: []
                }));
  /**
   * 显示时间选择器
   */
  showTimePicker: function () {
    this.setData({
      timePickerVisible: true
    });
  },
                const projectOptions = projectList.map(project => ({
                    label: project.name,
                    value: project.id
                }));
  /**
   * 时间选择器确认回调
   */
  onTimePickerConfirm: function (e) {
    const { value } = e.detail;
    this.setData({
      timePickerVisible: false,
      startTime: value
    });
  },
                this.setData({
                    projectList,
                    projectOptions
                });
            } else {
                wx.showToast({
                    title: res.msg || '获取项目列表失败',
                    icon: 'none'
                });
                return Promise.reject(new Error(res.msg || '获取项目列表失败'));
            }
        }).catch(err => {
            console.error('获取项目列表失败:', err);
            wx.showToast({
                title: '获取项目列表失败',
                icon: 'none'
            });
            return Promise.reject(err);
        });
    },
  /**
   * 时间选择器取消回调
   */
  onTimePickerCancel: function () {
    this.setData({
      timePickerVisible: false
    });
  },
    /**
     * 获取轮灌组列表
     */
    fetchGroups: function (projectId) {
        return get({
            url: '/wx/irrigation/getSimpleGroups',
            data: {
                projectId: projectId,
            },
            isShowLoding: true
        }).then(res => {
            if (res.success) {
                console.log('轮灌组数据:', res.content.obj);
  /**
   * 切换项目展开/折叠状态
   */
  toggleProject: function (e) {
    const index = e.currentTarget.dataset.index;
    const currentValue = this.data.projectList[index].expanded;
    // 创建新的项目列表,先将所有项目设为折叠状态
    const newProjectList = this.data.projectList.map((item, idx) => {
      return {
        ...item,
        expanded: false
      };
    });
    // 如果当前点击的项目已经是展开状态,则保持所有项目折叠
    // 否则,将当前点击的项目设为展开状态
    if (!currentValue) {
      newProjectList[index].expanded = true;
    }
    this.setData({
      projectList: newProjectList
    });
  },
                // 更新选中项目的轮灌组信息
                const projectList = this.data.projectList.map(project => {
                    if (project.id === projectId) {
                        return {
                            ...project,
                            groups: res.content.obj.map(group => ({
                                id: group.groupId,
                                name: group.groupCode,
                                duration: group.defaultDuration || 0,
                                selected: false,
                                intakeCount: group.intakeCount
                            }))
                        };
                    }
                    return project;
                });
  /**
   * 切换轮灌组选中状态
   */
  toggleGroupSelection: function (e) {
    const projectIndex = e.currentTarget.dataset.projectIndex;
    const groupIndex = e.currentTarget.dataset.groupIndex;
    const key = `projectList[${projectIndex}].groups[${groupIndex}].selected`;
    const currentValue = this.data.projectList[projectIndex].groups[groupIndex].selected;
    this.setData({
      [key]: !currentValue
    });
    // 更新项目总时长
    this.updateProjectTotalDuration(projectIndex);
  },
                // 更新选中的项目
                const selectedProject = projectList.find(project => project.id === projectId);
                console.log('更新后的选中项目:', selectedProject);
  /**
   * 处理时长输入
   */
  onDurationInput: function (e) {
    const { groupIndex } = e.currentTarget.dataset;
    const duration = parseInt(e.detail.value) || 0;
    const selectedProject = { ...this.data.selectedProject };
    selectedProject.groups[groupIndex].duration = duration;
    this.setData({
      selectedProject
    }, () => {
      // 输入时长后重新计算总时间
      this.calculateTotalDuration();
    });
  },
                this.setData({
                    projectList,
                    selectedProject
                }, () => {
                    // 计算总时间
                    this.calculateTotalDuration();
                });
            } else {
                wx.showToast({
                    title: res.msg || '获取轮灌组列表失败',
                    icon: 'none'
                });
                return Promise.reject(new Error(res.msg || '获取轮灌组列表失败'));
            }
        }).catch(err => {
            console.error('获取轮灌组列表失败:', err);
            wx.showToast({
                title: '获取轮灌组列表失败',
                icon: 'none'
            });
            return Promise.reject(err);
        });
    },
  /**
   * 计算并更新项目总时长
   */
  updateProjectTotalDuration: function (projectIndex) {
    const project = this.data.projectList[projectIndex];
    let totalDuration = 0;
    // 计算所有选中的轮灌组的时长总和
    project.groups.forEach(group => {
      if (group.selected) {
        totalDuration += parseInt(group.duration) || 0;
      }
    });
    // 更新项目总时长
    const totalDurationKey = `projectList[${projectIndex}].totalDuration`;
    this.setData({
      [totalDurationKey]: totalDuration
    });
  },
    /**
     * 显示时间选择器
     */
    showTimePicker: function () {
        // 如果没有选择时间,使用当前时间
        if (!this.data.pickerValue) {
            const now = dayjs();
            this.setData({
                pickerValue: now.format('YYYY-MM-DD HH:mm')
            });
        }
  /**
   * 阻止事件冒泡
   */
  stopPropagation: function () {
    // 阻止事件冒泡,防止点击输入框时触发父元素的点击事件
  },
        this.setData({
            timePickerVisible: true
        });
    },
  /**
   * 跳转到轮灌组详情页
   */
  navigateToGroupDetail: function (e) {
    const { groupIndex } = e.currentTarget.dataset;
    // TODO: 实现跳转逻辑
  },
    /**
     * 时间选择器确认回调
     */
    onTimePickerConfirm: function (e) {
        const { value } = e.detail;
        this.setData({
            timePickerVisible: false,
            startTime: value
        });
    },
  /**
   * 确认按钮点击事件
   */
  onConfirm: function () {
    const { planCode, startTime, selectedProject } = this.data;
    if (!planCode) {
      wx.showToast({
        title: '请输入计划编号',
        icon: 'none'
      });
      return;
    }
    if (!startTime) {
      wx.showToast({
        title: '请选择灌溉开始时间',
        icon: 'none'
      });
      return;
    }
    if (!selectedProject) {
      wx.showToast({
        title: '请选择项目',
        icon: 'none'
      });
      return;
    }
    /**
     * 时间选择器取消回调
     */
    onTimePickerCancel: function () {
        this.setData({
            timePickerVisible: false
        });
    },
    // TODO: 实现确认逻辑
    console.log('提交数据:', {
      planCode,
      startTime,
      project: selectedProject
    });
  },
    /**
     * 切换项目展开/折叠状态
     */
    toggleProject: function (e) {
        const index = e.currentTarget.dataset.index;
        const currentValue = this.data.projectList[index].expanded;
  // 显示项目选择器
  showProjectPicker() {
    this.setData({
      projectPickerVisible: true
    });
  },
        // 创建新的项目列表,先将所有项目设为折叠状态
        const newProjectList = this.data.projectList.map((item, idx) => {
            return {
                ...item,
                expanded: false
            };
        });
  // 项目选择器确认
  onProjectPickerConfirm(e) {
    const { value } = e.detail;
    const selectedProject = this.data.projectList.find(project => project.id === value[0]);
    this.setData({
      projectPickerVisible: false,
      selectedProject: selectedProject,
      projectPickerValue: value
    }, () => {
      // 选择项目后计算总时间
      this.calculateTotalDuration();
    });
  },
        // 如果当前点击的项目已经是展开状态,则保持所有项目折叠
        // 否则,将当前点击的项目设为展开状态
        if (!currentValue) {
            newProjectList[index].expanded = true;
        }
  // 项目选择器取消
  onProjectPickerCancel() {
    this.setData({
      projectPickerVisible: false
    });
  },
        this.setData({
            projectList: newProjectList
        });
    },
  // 计算总灌溉时间
  calculateTotalDuration() {
    if (!this.data.selectedProject) return;
    const totalDuration = this.data.selectedProject.groups.reduce((sum, group) => {
      return sum + (parseInt(group.duration) || 0);
    }, 0);
    this.setData({
      totalDuration
    });
  },
    /**
     * 切换轮灌组选中状态
     */
    toggleGroupSelection: function (e) {
        const projectIndex = e.currentTarget.dataset.projectIndex;
        const groupIndex = e.currentTarget.dataset.groupIndex;
        const key = `projectList[${projectIndex}].groups[${groupIndex}].selected`;
        const currentValue = this.data.projectList[projectIndex].groups[groupIndex].selected;
        this.setData({
            [key]: !currentValue
        });
        // 更新项目总时长
        this.updateProjectTotalDuration(projectIndex);
    },
    /**
     * 处理时长输入
     */
    onDurationInput: function (e) {
        const { groupIndex } = e.currentTarget.dataset;
        const duration = parseInt(e.detail.value) || 0;
        const selectedProject = { ...this.data.selectedProject };
        selectedProject.groups[groupIndex].duration = duration;
        this.setData({
            selectedProject
        }, () => {
            // 输入时长后重新计算总时间
            this.calculateTotalDuration();
        });
    },
    /**
     * 计算并更新项目总时长
     */
    updateProjectTotalDuration: function (projectIndex) {
        const project = this.data.projectList[projectIndex];
        let totalDuration = 0;
        // 计算所有选中的轮灌组的时长总和
        project.groups.forEach(group => {
            if (group.selected) {
                totalDuration += parseInt(group.duration) || 0;
            }
        });
        // 更新项目总时长
        const totalDurationKey = `projectList[${projectIndex}].totalDuration`;
        this.setData({
            [totalDurationKey]: totalDuration
        });
    },
    /**
     * 阻止事件冒泡
     */
    stopPropagation: function (e) {
        if (e && e.stopPropagation) {
            e.stopPropagation();
        }
        return false;
    },
    /**
     * 跳转到轮灌组详情页
     */
    navigateToGroupDetail: function (e) {
        const { groupIndex } = e.currentTarget.dataset;
        // TODO: 实现跳转逻辑
    },
    /**
     * 确认按钮点击事件
     */
    onConfirm: function () {
        const { planCode, startTime, selectedProject } = this.data;
        if (!planCode) {
            wx.showToast({
                title: '请输入计划编号',
                icon: 'none'
            });
            return;
        }
        if (!selectedProject) {
            wx.showToast({
                title: '请选择项目',
                icon: 'none'
            });
            return;
        }
        // 构建上报数据
        const schedules = selectedProject.groups.map(group => ({
            groupId: group.id,
            duration: parseInt(group.duration) || 0
        }));
        const requestData = {
            projectId: selectedProject.id,
            planName: planCode,
            startupMode: startTime ? 2 : 1,
            operatorId: app.globalData.clientId,
            schedules: schedules
        };
        // 如果有开始时间,添加到请求数据中
        if (startTime) {
            requestData.planStartTime = startTime;
        }
        // 发送请求
        post({
            url: '/wx/plan/createPlan',
            data: requestData,
            isShowLoding: true
        }).then(res => {
            if (res.success) {
                wx.showToast({
                    title: '创建成功',
                    icon: 'success'
                });
                // 返回上一页
                setTimeout(() => {
                    wx.navigateBack();
                }, 1500);
            } else {
                wx.showToast({
                    title: res.msg || '创建失败',
                    icon: 'none'
                });
            }
        }).catch(err => {
            console.error('创建计划失败:', err);
            wx.showToast({
                title: '创建失败',
                icon: 'none'
            });
        });
    },
    // 显示项目选择器
    showProjectPicker() {
        this.setData({
            projectPickerVisible: true
        });
    },
    // 项目选择器确认
    onProjectPickerConfirm(e) {
        const { value } = e.detail;
        console.log('选择的项目ID:', value[0]); // 添加日志查看数据
        const selectedProject = this.data.projectList.find(project => project.id === value[0]);
        console.log('找到的项目:', selectedProject); // 添加日志查看数据
        this.setData({
            projectPickerVisible: false,
            selectedProject: selectedProject,
            projectPickerValue: value
        }, () => {
            // 选择项目后获取轮灌组列表
            if (selectedProject) {
                this.fetchGroups(selectedProject.id);
            }
        });
    },
    // 项目选择器取消
    onProjectPickerCancel() {
        this.setData({
            projectPickerVisible: false
        });
    },
    /**
     * 计算总灌溉时间
     */
    calculateTotalDuration: function () {
        if (!this.data.selectedProject) return;
        const totalDuration = this.data.selectedProject.groups.reduce((sum, group) => {
            return sum + (parseInt(group.duration) || 0);
        }, 0);
        this.setData({
            totalDuration
        });
    },
    /**
     * 轮灌组列表下拉刷新
     */
    onGroupListRefresh: function () {
        if (!this.data.selectedProject) {
            this.setData({ isRefreshing: false });
            return;
        }
        this.setData({ isRefreshing: true });
        this.fetchGroups(this.data.selectedProject.id)
            .then(() => {
                this.setData({ isRefreshing: false });
            })
            .catch(() => {
                this.setData({ isRefreshing: false });
            });
    },
    /**
     * 显示时间提示弹窗
     */
    showTimeInfo: function () {
        this.setData({
            timeInfoVisible: true
        });
    },
    /**
     * 关闭时间提示弹窗
     */
    onTimeInfoConfirm: function () {
        this.setData({
            timeInfoVisible: false
        });
    },
}); 
pages/createIrrigation/createIrrigation.json
@@ -1,8 +1,15 @@
{
  "navigationBarTitleText": "新建灌溉计划",
  "navigationBarTitleText": "创建灌溉计划",
  "usingComponents": {
    "t-date-time-picker": "tdesign-miniprogram/date-time-picker/date-time-picker",
    "t-picker": "tdesign-miniprogram/picker/picker",
    "t-picker-item": "tdesign-miniprogram/picker-item/picker-item"
  }
    "t-picker-item": "tdesign-miniprogram/picker-item/picker-item",
    "t-input": "tdesign-miniprogram/input/input",
    "t-button": "tdesign-miniprogram/button/button",
    "t-dialog": "tdesign-miniprogram/dialog/dialog",
    "t-toast": "tdesign-miniprogram/toast/toast"
  },
  "enablePullDownRefresh": false,
  "backgroundColor": "#f5f5f5",
  "backgroundTextStyle": "dark"
pages/createIrrigation/createIrrigation.wxml
@@ -8,13 +8,31 @@
  </view>
  <!-- 灌溉开始时间 -->
  <view class="form-item" bindtap="showTimePicker">
    <view class="form-label">灌溉开始时间</view>
    <view class="form-input time-input">
  <view class="form-item">
    <view class="form-label">
      灌溉开始时间(选填)
      <image
        class="info-icon"
        src="/images/info.svg"
        mode="aspectFit"
        bindtap="showTimeInfo"
      ></image>
    </view>
    <view class="form-input time-input" bindtap="showTimePicker">
      <view class="time-text {{startTime ? '' : 'placeholder'}}">{{startTime || '请选择灌溉开始时间'}}</view>
      <image class="arrow-icon" src="/images/arrow-right.svg" mode="aspectFit"></image>
    </view>
  </view>
  <!-- 时间提示弹窗 -->
  <t-dialog
    visible="{{timeInfoVisible}}"
    title="时间设置说明"
    content="• 设置两小时后的灌溉时间:\n在此处选择具体的开始时间\n\n• 设置两小时内的灌溉时间:\n创建完成后点击发布即可立即开始灌溉"
    confirmBtn="我知道了"
    bind:confirm="onTimeInfoConfirm"
    bind:cancel="onTimeInfoConfirm"
  />
  <!-- 选择项目 -->
  <view class="form-item" bindtap="showProjectPicker">
@@ -36,26 +54,39 @@
        </view>
      </view>
    </view>
    <scroll-view scroll-y="true" class="group-list">
      <block wx:for="{{selectedProject.groups}}" wx:key="id" wx:for-item="group" wx:for-index="groupIndex">
        <view class="group-item {{group.selected ? 'selected' : ''}}" bindtap="navigateToGroupDetail" data-group-index="{{groupIndex}}">
          <view class="group-info">
            <view class="group-name">{{group.name}}</view>
    <scroll-view
      scroll-y="true"
      class="group-list"
      refresher-enabled="{{true}}"
      refresher-triggered="{{isRefreshing}}"
      bindrefresherrefresh="onGroupListRefresh"
    >
      <block wx:if="{{selectedProject.groups && selectedProject.groups.length > 0}}">
        <block wx:for="{{selectedProject.groups}}" wx:key="id" wx:for-item="group" wx:for-index="groupIndex">
          <view class="group-item {{group.selected ? 'selected' : ''}}" bindtap="navigateToGroupDetail" data-group-index="{{groupIndex}}">
            <view class="group-info">
              <view class="group-name">{{group.name || '未命名轮灌组'}}({{group.intakeCount}}个取水口)</view>
            </view>
            <view class="group-duration">
              <input
                class="duration-input"
                type="number"
                value="{{group.duration}}"
                bindinput="onDurationInput"
                data-group-index="{{groupIndex}}"
                placeholder="0"
                catchtap="stopPropagation"
              />
              <text class="duration-unit">分钟</text>
            </view>
          </view>
          <view class="group-duration">
            <input
              class="duration-input"
              type="number"
              value="{{group.duration}}"
              bindinput="onDurationInput"
              data-group-index="{{groupIndex}}"
              placeholder="0"
              catchtap="stopPropagation"
            />
            <text class="duration-unit">分钟</text>
          </view>
        </view>
        </block>
      </block>
      <view wx:else class="empty-container">
        <image class="empty-image" src="/images/empty.png" mode="aspectFit"></image>
        <view class="empty-text">暂无轮灌组数据</view>
        <view class="empty-text">请刷新或稍后再试</view>
      </view>
    </scroll-view>
  </view>
@@ -70,21 +101,29 @@
    visible="{{timePickerVisible}}"
    mode="{{['date', 'minute']}}"
    value="{{pickerValue}}"
    start="{{pickerValue}}"
    format="YYYY-MM-DD HH:mm"
    bindconfirm="onTimePickerConfirm"
    bindcancel="onTimePickerCancel"
    catchtouchmove="stopPropagation"
    z-index="{{1000}}"
  />
  <!-- 项目选择器弹窗 -->
  <t-picker
    title="选择项目"
    visible="{{projectPickerVisible}}"
    value="{{projectPickerValue}}"
    title="选择项目"
    cancelBtn="取消"
    confirmBtn="确认"
    bindconfirm="onProjectPickerConfirm"
    bindcancel="onProjectPickerCancel"
    bind:confirm="onProjectPickerConfirm"
    bind:cancel="onProjectPickerCancel"
    bind:touchmove="stopPropagation"
    bind:touchstart="stopPropagation"
    bind:touchend="stopPropagation"
    z-index="{{1000}}"
  >
    <t-picker-item options="{{projectOptions}}" />
    <t-picker-item options="{{projectOptions}}" value="{{projectPickerValue}}" />
  </t-picker>
</view> 
pages/createIrrigation/createIrrigation.wxss
@@ -8,6 +8,44 @@
  box-sizing: border-box;
}
/* 选择器容器样式 */
.picker-container {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  z-index: 9999;
  pointer-events: none;
}
/* 蒙层样式 */
.picker-mask {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, 0.5);
  pointer-events: auto;
}
.picker-container .t-date-time-picker,
.picker-container .t-picker {
  pointer-events: auto;
}
/* 当选择器显示时 */
.t-date-time-picker[visible],
.t-picker[visible] {
  position: absolute;
  bottom: 0;
  left: 0;
  width: 100%;
  background-color: #fff;
  border-radius: 24rpx 24rpx 0 0;
}
/* 表单项样式 */
.form-item {
  display: flex;
@@ -21,11 +59,17 @@
}
.form-label {
  display: flex;
  align-items: center;
  font-size: 28rpx;
  color: #333;
  font-weight: 500;
  width: 200rpx; /* 固定标签宽度 */
  flex-shrink: 0; /* 防止标签宽度被压缩 */
  margin-bottom: 16rpx;
}
.info-icon {
  width: 32rpx;
  height: 32rpx;
  margin-left: 8rpx;
}
.form-input {
@@ -361,4 +405,54 @@
  font-size: 32rpx;
  color: #1890FF;
  font-weight: 500;
}
.empty-container {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 40rpx 0;
}
.empty-image {
  width: 200rpx;
  height: 200rpx;
  margin-bottom: 20rpx;
}
.empty-text {
  color: #999;
  font-size: 30rpx;
}
.empty-tip {
  font-size: 28rpx;
  color: #999;
}
.dialog-content {
  padding: 20rpx 0;
}
.dialog-section {
  margin-bottom: 24rpx;
}
.dialog-section:last-child {
  margin-bottom: 0;
}
.dialog-title {
  font-size: 28rpx;
  color: #333;
  font-weight: 500;
  margin-bottom: 8rpx;
}
.dialog-desc {
  font-size: 26rpx;
  color: #666;
  line-height: 1.6;
  padding-left: 20rpx;
pages/home/home.js
@@ -260,22 +260,12 @@
   * 生命周期函数--监听页面初次渲染完成
   */
  onReady() {
    if (this.data.options.param === "1") {
        this.setData({
          showTipDialog: true,
          tipData: "开阀命令下发成功,因开阀需要时间,约20-60秒后可刷新快速关阀列表查看执行结果。"
        })
        setTimeout(() => {
          this.getOpenList();
        }, 20000)
      } else if (this.data.options.param === "2") {
        this.setData({
          showTipDialog: true,
          tipData: "预约开阀命令下发成功,当到达预约时间并且成功开阀后快速关阀列表会显示未关阀记录"
        })
    // 检查是否有options和param
    if (this.data.options && this.data.options.param) {
      if (this.data.options.param === "1" || this.data.options.param === "2") {
        this.getOpenList();
      }
    }
  },
  /**
pages/irrigation/irrigation.js
@@ -1,4 +1,7 @@
// pages/irrigation/irrigation.js
const { get, post } = require('../../api/request');
const app = getApp();
Page({
  /**
   * 页面的初始数据
@@ -9,14 +12,22 @@
    completedList: [], // 已完成的轮灌列表
    currentList: [], // 当前显示的列表
    isRefreshing: false, // 是否正在刷新
    isWXRefreshing: false // 微信原生下拉刷新状态
    isWXRefreshing: false, // 微信原生下拉刷新状态
    projectId: null // Added to store projectId
  },
  /**
   * 生命周期函数--监听页面加载
   */
  onLoad: function (options) {
    // 获取项目ID
    if (options.projectId) {
      this.setData({
        projectId: options.projectId
      });
    }
    this.loadIrrigationData();
    this.loadCompletedIrrigationData();
  },
  /**
@@ -24,100 +35,97 @@
   */
  onShow: function () {
    this.loadIrrigationData();
    this.loadCompletedIrrigationData();
  },
  /**
   * 加载轮灌数据
   */
  loadIrrigationData: function () {
    // 这里应该调用API获取数据
    // 模拟数据
    const mockData = {
      activeList: [
        {
          id: '1',
          title: 'LG-2023-001',
          status: '未发布',
          irrigationTime: '2023-05-20 08:00 - 17:00'
        },
        {
          id: '2',
          title: 'LG-2023-002',
          status: '已发布',
          irrigationTime: '2023-05-22 09:00 - 18:00'
        },
        {
          id: '2',
          title: 'LG-2023-002',
          status: '已发布',
          irrigationTime: '2023-05-22 09:00 - 18:00'
        },  {
          id: '2',
          title: 'LG-2023-002',
          status: '已发布',
          irrigationTime: '2023-05-22 09:00 - 18:00'
        },  {
          id: '2',
          title: 'LG-2023-002',
          status: '已发布',
          irrigationTime: '2023-05-22 09:00 - 18:00'
        },
        {
          id: '3',
          title: 'LG-2023-003',
          status: '执行中',
          irrigationTime: '2023-05-18 07:30 - 16:30',
          irrigatedTime: '3小时25分钟',
          irrigatedGroups: '1组、2组、3组'
        },   {
          id: '2',
          title: 'LG-2023-002',
          status: '已发布',
          irrigationTime: '2023-05-22 09:00 - 18:00'
        },
        {
          id: '3',
          title: 'LG-2023-003',
          status: '执行中',
          irrigationTime: '2023-05-18 07:30 - 16:30',
          irrigatedTime: '3小时25分钟',
          irrigatedGroups: '1组、2组、3组'
        },   {
          id: '2',
          title: 'LG-2023-002',
          status: '已发布',
          irrigationTime: '2023-05-22 09:00 - 18:00'
        },
        {
          id: '3',
          title: 'LG-2023-003',
          status: '执行中',
          irrigationTime: '2023-05-18 07:30 - 16:30',
          irrigatedTime: '3小时25分钟',
          irrigatedGroups: '1组、2组、3组'
        }
      ]
      // completedList: [
      //   {
      //     id: '4',
      //     title: 'LG-2023-004',
      //     status: '已完成',
      //     irrigationTime: '2023-05-10 10:00 - 19:00'
      //   }
      // ]
    };
    get({
      url: '/wx/plan/getNotCompletePlans'
    }).then(res => {
      if (res.success) {
        const activeList = res.content.map(item => ({
          id: item.planId,
          title: item.planName,
          projectName: item.projectName || '未分配项目',
          status: item.planState,
          planStartTime: item.planStartTime,
          planStopTime: item.planStopTime,
          duration: item.duration,
          startupMode: item.startupMode
        }));
    // 模拟网络请求延迟
    setTimeout(() => {
      this.setData({
        activeList: mockData.activeList || [],
        completedList: mockData.completedList || [],
        currentList: this.data.currentTab === 0 ? mockData.activeList || [] : mockData.completedList || [],
        isRefreshing: false, // 结束刷新状态
        isWXRefreshing: false // 结束微信原生下拉刷新状态
        console.log('轮灌计划数据:', activeList);
        this.setData({
          activeList: activeList,
          currentList: this.data.currentTab === 0 ? activeList : this.data.completedList,
          isRefreshing: false,
          isWXRefreshing: false
        });
      } else {
        wx.showToast({
          title: res.msg || '加载失败',
          icon: 'none'
        });
      }
    }).catch(err => {
      wx.showToast({
        title: '加载失败',
        icon: 'none'
      });
    }, 1000);
      this.setData({
        isRefreshing: false,
        isWXRefreshing: false
      });
    });
  },
  /**
   * 加载已完成的轮灌数据
   */
  loadCompletedIrrigationData: function () {
    get({
      url: '/wx/plan/getCompletedPlans'
    }).then(res => {
      if (res.success) {
        const completedList = res.content.map(item => ({
          id: item.planId,
          title: item.planName,
          projectName: item.projectName || '未分配项目',
          status: item.planState,
          planStartTime: item.planStartTime,
          planStopTime: item.planStopTime,
          duration: item.duration,
          startupMode: item.startupMode
        }));
        console.log('已完成轮灌计划数据:', completedList);
        this.setData({
          completedList: completedList,
          currentList: this.data.currentTab === 1 ? completedList : this.data.activeList,
          isRefreshing: false,
          isWXRefreshing: false
        });
      } else {
        wx.showToast({
          title: res.msg || '加载失败',
          icon: 'none'
        });
      }
    }).catch(err => {
      wx.showToast({
        title: '加载失败',
        icon: 'none'
      });
      this.setData({
        isRefreshing: false,
        isWXRefreshing: false
      });
    });
  },
  /**
@@ -161,13 +169,35 @@
      content: '确定要发布该轮灌计划吗?',
      success: (res) => {
        if (res.confirm) {
          // 这里应该调用API发布轮灌计划
          wx.showToast({
            title: '发布成功',
            icon: 'success'
          // 调用发布接口
          post({
            url: '/wx/plan/publishPlan',
            data: {
              planId: id,
              operatorId: app.globalData.clientId
            },
            isShowLoding: true
          }).then(res => {
            if (res.success) {
              wx.showToast({
                title: '发布成功',
                icon: 'success'
              });
              // 刷新数据
              this.loadIrrigationData();
            } else {
              wx.showToast({
                title: res.msg || '发布失败',
                icon: 'none'
              });
            }
          }).catch(err => {
            console.error('发布失败:', err);
            wx.showToast({
              title: '发布失败',
              icon: 'none'
            });
          });
          // 刷新数据
          this.loadIrrigationData();
        }
      }
    });
@@ -222,7 +252,7 @@
   */
  onAddIrrigation: function () {
    wx.navigateTo({
      url: '/pages/createIrrigation/createIrrigation'
      url: '/pages/createIrrigation/createIrrigation?projectId=' + this.data.projectId
    });
  },
@@ -243,6 +273,7 @@
        isRefreshing: true
      });
      this.loadIrrigationData();
      this.loadCompletedIrrigationData();
    }
  },
@@ -254,5 +285,6 @@
      isWXRefreshing: true
    });
    this.loadIrrigationData();
    this.loadCompletedIrrigationData();
  }
}) 
pages/irrigation/irrigation.wxml
@@ -18,54 +18,98 @@
    </view>
    <view class="scroll-bg">
      <block wx:if="{{currentList.length > 0}}">
        <!-- 统一显示所有列表项,不再按状态分组 -->
        <view class="list-item" wx:for="{{currentList}}" wx:key="id" bindtap="onItemTap" data-id="{{item.id}}" data-status="{{item.status}}">
          <view class="item-header">
            <view class="info-row title-row">
              <view class="info-label">编号:</view>
              <view class="info-value">{{item.title}}</view>
            </view>
            <!-- 根据状态显示不同的图标 -->
            <view class="item-status">
              <block wx:if="{{item.status === '已发布'}}">
                <image class="status-icon" src="/images/published-icon.svg" mode="aspectFit"></image>
              </block>
              <block wx:elif="{{item.status === '执行中'}}">
                <image class="status-icon" src="/images/progress.svg" mode="aspectFit"></image>
              </block>
              <block wx:elif="{{item.status === '未发布'}}">
                <text>{{item.status}}</text>
              </block>
            </view>
          <!-- 状态标签 -->
          <view class="status-tag {{item.status === '1' ? 'draft' : item.status === '2' ? 'published' : item.status === '3' ? 'executing' : 'completed'}}">
            {{item.status === '1' ? '草稿' : item.status === '2' ? '未执行' : item.status === '3' ? '执行中' : '已完成'}}
          </view>
          <view class="item-info">
            <view class="info-row">
              <view class="info-label">灌溉时间:</view>
              <view class="info-value">{{item.irrigationTime}}</view>
          <!-- 主要内容区 -->
          <view class="item-content">
            <!-- 标题区域 -->
            <view class="item-header">
              <view class="title-section">
                <view class="plan-title">{{item.title}}</view>
                <view class="project-name">{{item.projectName}}</view>
              </view>
            </view>
            <!-- 仅在执行中状态显示额外信息 -->
            <block wx:if="{{item.status === '执行中'}}">
              <view class="info-row irrigated-row">
                <view class="info-label">已灌溉时间:</view>
                <view class="info-value-time">{{item.irrigatedTime}}</view>
            <!-- 信息区域 -->
            <view class="info-section">
              <!-- 草稿状态(1)时横向排列,其他状态纵向排列 -->
              <view class="info-grid {{item.status === '1' ? '' : 'vertical-layout'}} {{currentTab === 1 ? 'history-grid' : ''}}">
                <view class="info-item">
                  <view class="info-icon">
                    <image src="/images/time-icon.svg" mode="aspectFit"></image>
                  </view>
                  <view class="info-content">
                    <view class="info-label">灌溉时间</view>
                    <view class="info-value">{{(item.planStartTime ? item.planStartTime : '手动发布') + (item.planStopTime ? ' - ' + item.planStopTime : '')}}</view>
                  </view>
                </view>
                <view class="info-item">
                  <view class="info-icon duration-icon">
                    <image src="/images/hourglass.svg" mode="aspectFit"></image>
                  </view>
                  <view class="info-content">
                    <view class="info-label">灌溉时长</view>
                    <view class="info-value">{{item.duration}}分钟</view>
                  </view>
                </view>
              </view>
              <view class="info-row irrigated-row">
                <view class="info-label">已灌溉轮组:</view>
                <view class="info-value-time">{{item.irrigatedGroups}}</view>
              </view>
            </block>
          </view>
          <view class="item-actions" catchtap="stopPropagation">
            <!-- 根据状态显示不同的按钮 -->
            <block wx:if="{{item.status === '未发布'}}">
              <button class="action-button publish-button" hover-class="publish-button-hover" bindtap="onPublish" data-id="{{item.id}}">发布</button>
            </block>
            <block wx:if="{{item.status === '执行中' || item.status === '已发布'}}">
              <button class="action-button stop-button" hover-class="stop-button-hover" bindtap="onStop" data-id="{{item.id}}">终止</button>
            </block>
            <block wx:if="{{item.status === '已发布'}}">
              <button class="action-button execute-button" hover-class="execute-button-hover" bindtap="onExecute" data-id="{{item.id}}">立即执行</button>
            </block>
              <!-- 执行中状态额外信息 -->
              <block wx:if="{{item.status === '3'}}">
                <view class="executing-info">
                  <view class="info-item">
                    <view class="info-icon">
                      <image src="/images/progress-icon.svg" mode="aspectFit"></image>
                    </view>
                    <view class="info-content">
                      <view class="info-label">已灌溉时间</view>
                      <view class="info-value">{{item.irrigatedTime}}</view>
                    </view>
                  </view>
                  <view class="info-item">
                    <view class="info-icon">
                      <image src="/images/group-icon.svg" mode="aspectFit"></image>
                    </view>
                    <view class="info-content">
                      <view class="info-label">已灌溉轮组</view>
                      <view class="info-value">{{item.irrigatedGroups}}</view>
                    </view>
                  </view>
                </view>
              </block>
            </view>
            <!-- 操作按钮区域 -->
            <view class="action-section" catchtap="stopPropagation" wx:if="{{item.status !== '4'}}">
              <block wx:if="{{item.status === '1'}}">
                <view class="action-buttons">
                  <button class="action-button publish-button" hover-class="publish-button-hover" bindtap="onPublish" data-id="{{item.id}}">
                    <image src="/images/publish-icon.svg" mode="aspectFit"></image>
                    <text>发布</text>
                  </button>
                </view>
              </block>
              <block wx:if="{{item.status === '2'}}">
                <view class="action-buttons">
                  <button class="action-button stop-button" hover-class="stop-button-hover" bindtap="onStop" data-id="{{item.id}}">
                    <image src="/images/stop-icon.svg" mode="aspectFit"></image>
                    <text>终止</text>
                  </button>
                </view>
              </block>
              <block wx:if="{{item.status === '3'}}">
                <view class="action-buttons">
                  <button class="action-button stop-button" hover-class="stop-button-hover" bindtap="onStop" data-id="{{item.id}}">
                    <image src="/images/stop-icon.svg" mode="aspectFit"></image>
                    <text>终止</text>
                  </button>
                </view>
              </block>
            </view>
          </view>
        </view>
      </block>
@@ -81,6 +125,9 @@
  <!-- 底部新建按钮 -->
  <view class="bottom-button">
    <button class="add-button" hover-class="add-button-hover" bindtap="onAddIrrigation">创建灌溉计划</button>
    <button class="add-button" hover-class="add-button-hover" bindtap="onAddIrrigation">
      <image src="/images/add-icon.svg" mode="aspectFit"></image>
      <text>创建灌溉计划</text>
    </button>
  </view>
</view>
pages/irrigation/irrigation.wxss
@@ -3,44 +3,34 @@
  flex-direction: column;
  height: 100vh;
  background-color: #f5f5f5;
  padding: 20rpx;
  box-sizing: border-box;
  overflow: hidden;
}
/* 顶部标签页样式 */
/* 标签页样式 */
.tabs {
  display: flex;
  background-color: #fff;
  border-radius: 12rpx;
  margin-bottom: 20rpx;
  overflow: hidden;
  box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
  padding: 20rpx 0;
  width: 100%;
  flex-shrink: 0; /* 防止被压缩 */
  box-shadow: 0 4rpx 8rpx rgba(0, 0, 0, 0.05);
  border-bottom: 1rpx solid #eaeaea;
  position: relative;
  z-index: 10;
}
.tab {
  flex: 1;
  text-align: center;
  padding: 30rpx 0;
  font-size: 28rpx;
  color: #666;
  position: relative;
  transition: all 0.2s ease;
}
.tab-hover {
  opacity: 0.8;
  background-color: rgba(0, 0, 0, 0.03);
}
.tab:active {
  opacity: 0.8;
  background-color: rgba(0, 0, 0, 0.03);
  padding: 20rpx 0;
}
.tab.active {
  color: rgba(45, 139, 247, 1);
  font-weight: bold;
  color: #0052d9;
  font-weight: 500;
}
.tab.active::after {
@@ -49,243 +39,234 @@
  bottom: 0;
  left: 50%;
  transform: translateX(-50%);
  width: 50vw;
  height: 6rpx;
  background-color: rgba(45, 139, 247, 1);
  border-radius: 3rpx;
  width: 40rpx;
  height: 4rpx;
  background-color: #0052d9;
  border-radius: 2rpx;
}
/* 刷新按钮和分隔线 */
.refresh-header {
  display: flex;
  justify-content: flex-end;
  padding: 10rpx 20rpx;
}
.refresh-button {
  font-size: 28rpx;
  color: #fff;
  background-color: rgba(45, 139, 247, 1);
  border: none;
  border-radius: 5px;
  padding: 10rpx 20rpx;
  display: flex;
  align-items: center;
  justify-content: center;
}
.refresh-button:active {
  background-color: #1a6fc7;
}
.divider {
  height: 1rpx;
  background-color: #e0e0e0;
  margin: 10rpx 0;
}
/* 下拉刷新动画 */
.refresh-view {
  text-align: center;
  padding: 30rpx;
  display: flex;
  justify-content: center;
  align-items: center;
}
.dot {
  width: 20rpx;
  height: 20rpx;
  background-color: rgba(45, 139, 247, 1);
  border-radius: 50%;
  margin: 0 5px;
  animation: blink 1.4s infinite both;
}
.dot:nth-child(2) {
  animation-delay: 0.2s;
}
.dot:nth-child(3) {
  animation-delay: 0.4s;
}
@keyframes blink {
  0%, 80%, 100% {
    opacity: 0;
  }
  40% {
    opacity: 1;
  }
}
/* scroll-view样式 */
/* 列表容器样式 */
.scroll-view {
  flex: 1;
  overflow-y: auto;
  margin-bottom: 20rpx;
  height: calc(100vh - 180rpx); /* 减去顶部标签页和底部按钮的高度 */
  overflow: hidden;
  padding-bottom: 140rpx; /* 增加底部内边距,为按钮留出更多空间 */
  background-color: #f5f5f5; /* 确保背景色与tabs不同 */
  margin-top: 10rpx; /* 与顶部tab保持一点距离 */
}
.scroll-bg {
  padding: 10rpx 0;
  height: 100%;
  display: flex;
  flex-direction: column;
}
/* 中间列表样式 */
.irrigation-list {
  flex: 1;
  background-color: transparent;
  margin-bottom: 20rpx;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  padding: 0;
}
.list-title {
  font-size: 28rpx;
  font-weight: bold;
  color: #333;
  padding: 20rpx;
  background-color: #f8f8f8;
  border-bottom: 1rpx solid #eee;
  padding-bottom: 40rpx; /* 添加底部内边距,防止最后一项被遮挡 */
}
/* 列表项样式 */
.list-item {
  padding: 30rpx;
  padding-bottom: 90rpx;
  position: relative;
  background-color: #fff;
  border-radius: 12rpx;
  margin-bottom: 16rpx;
  box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
  border-radius: 16rpx;
  margin-bottom: 20rpx;
  position: relative;
  overflow: hidden;
  box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05);
}
.item-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 10rpx;
  margin-top: 10rpx;
}
.item-title {
  font-size: 28rpx;
  font-weight: bold;
  color: #333;
}
.item-status {
  font-size: 24rpx;
  color: #999;
  padding: 4rpx 12rpx;
/* 状态标签样式 */
.status-tag {
  position: absolute;
  top: 20rpx;
  right: 20rpx;
  padding: 4rpx 16rpx;
  border-radius: 20rpx;
  display: flex;
  align-items: center;
}
.status-icon {
  width: 70rpx;
  height: 70rpx;
}
.status-active {
  font-size: 24rpx;
  color: #fff;
  background-color: rgba(45, 139, 247, 1);
}
.status-draft {
  color: #fff;
.status-tag.draft {
  background-color: #ff9d00;
}
.status-tag.published {
  background-color: #0052d9;
}
.status-tag.executing {
  background-color: #00a870;
}
.status-tag.completed {
  background-color: #999;
}
.status-published {
  display: flex;
  align-items: center;
  justify-content: center;
  color: #00AD45;
  background-color: rgba(0, 173, 69, 0.1);
/* 内容区域样式 */
.item-content {
  padding: 30rpx;
}
.status-published-icon {
  width: 34rpx;
  height: 34rpx;
  margin-right: 6rpx;
/* 标题区域样式 */
.title-section {
  margin-bottom: 30rpx;
}
.item-info {
  font-size: 24rpx;
  color: #666;
  margin-right: 160rpx;
}
.info-row {
  display: flex;
  margin-bottom: 8rpx;
  align-items: flex-end; /* 添加底部对齐 */
}
/* 编号行样式 - 作为标题突出显示 */
.title-row {
  margin-bottom: 10rpx;
}
.title-row .info-label,
.title-row .info-value {
  font-size: 26rpx;
  font-weight: bold;
.plan-title {
  font-size: 32rpx;
  font-weight: 500;
  color: #333;
  margin-bottom: 8rpx;
}
.project-name {
  font-size: 26rpx;
  color: #666;
}
/* 信息区域样式 */
.info-section {
  margin-bottom: 30rpx;
}
/* 信息网格 */
.info-grid {
  display: flex;
  flex-direction: row;
  justify-content: space-between;
  width: 100%;
  margin-top: 12rpx;
}
.info-item {
  display: flex;
  align-items: flex-start;
  /* 横向布局时占据一半宽度 */
  width: 48%;
}
/* 纵向布局的信息网格 */
.vertical-layout {
  flex-direction: column;
  gap: 16rpx;
}
.vertical-layout .info-item {
  width: 100%;
  margin-bottom: 16rpx;
}
/* 历史列表特定样式 */
.history-grid {
  flex-direction: column;
  gap: 20rpx;
}
.info-icon {
  width: 40rpx;
  height: 40rpx;
  margin-right: 16rpx;
}
.info-icon.duration-icon {
  width: 35rpx;
  height: 35rpx;
  margin-right: 16rpx;
}
.info-icon image {
  width: 100%;
  height: 100%;
}
.info-content {
  flex: 1;
}
.info-label {
  font-size: 24rpx;
  color: #999;
  width: 150rpx;
  margin-right: 10rpx; /* 添加右侧间距 */
  margin-bottom: 4rpx;
}
/* 为已灌溉时间和轮组添加特殊样式 */
.info-row.irrigated-row {
  margin-top: 12rpx;
  margin-bottom: 12rpx;
  align-items: center !important; /* 强制垂直居中对齐 */
  display: flex;
  justify-content: flex-start; /* 水平方向起点对齐 */
  height: 50rpx; /* 固定行高 */
.info-value {
  font-size: 28rpx;
  color: #333;
}
.info-row.irrigated-row .info-label {
/* 执行中状态额外信息样式 */
.executing-info {
  margin-top: 20rpx;
  padding-top: 20rpx;
  border-top: 1rpx solid #f5f5f5;
}
/* 操作按钮区域样式 */
.action-section {
  display: flex;
  justify-content: flex-end;
  gap: 20rpx;
  padding-top: 20rpx;
  border-top: 1rpx solid #f5f5f5;
  width: 100%;
  position: relative;
  z-index: 1;
}
.action-buttons {
  display: flex;
  justify-content: flex-end;
  gap: 20rpx;
  width: 100%;
}
.action-button {
  display: inline-flex;
  align-items: center;
  height: 100%; /* 使用百分比高度 */
  justify-content: center;
  gap: 8rpx;
  padding: 12rpx 24rpx;
  border-radius: 8rpx;
  font-size: 28rpx;
  line-height: 1;
  min-width: 160rpx;
  height: 60rpx;
  margin: 0;
  background-color: #1890FF;
  color: #fff;
  border: none;
  position: relative;
  z-index: 2;
}
.info-value-time {
  font-size: 30rpx;
  font-weight: bold;
  color: rgb(42, 130, 228);
  display: flex;
  align-items: center;
  height: 100%; /* 使用与info-label相同的高度 */
.action-button::after {
  display: none;
}
/* 空列表容器 */
.action-button image {
  width: 32rpx;
  height: 32rpx;
  display: block;
}
.action-button text {
  color: #fff;
  display: block;
}
.publish-button {
  background-color: #1890FF !important;
}
.stop-button {
  background-color: #ff4d4f !important;
}
.execute-button {
  background-color: #1890FF !important;
}
/* 空状态样式 */
.empty-list {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  background-color: #fff;
  border-radius: 12rpx;
  box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
  width: 100%;
  height: calc(100vh - 250rpx); /* 减去顶部标签页、底部按钮和内边距的高度 */
  min-height: 500rpx;
  justify-content: center;
  padding: 100rpx 0;
}
.empty-icon {
@@ -301,96 +282,46 @@
/* 底部按钮样式 */
.bottom-button {
  padding: 20rpx 0;
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  padding: 20rpx;
  background-color: #fff;
  box-shadow: 0 -2rpx 12rpx rgba(0, 0, 0, 0.05);
  z-index: 100;
}
.add-button {
  background-color: rgba(45, 139, 247, 1);
  color: #fff;
  font-size: 32rpx;
  border-radius: 12rpx;
  width: 100%;
  transition: all 0.2s ease;
}
.add-button-hover {
  background-color: rgba(35, 110, 200, 1);
  transform: scale(0.98);
}
.add-button:active {
  background-color: rgba(35, 110, 200, 1);
  transform: scale(0.98);
}
/* 列表项操作按钮 */
.item-actions {
  display: flex;
  justify-content: flex-end;
  align-items: center;
  position: absolute;
  bottom: 20rpx;
  right: 30rpx;
  z-index: 2;
  justify-content: center;
  background-color: #1890FF;
  color: #fff;
  border-radius: 8rpx;
  font-size: 30rpx;
  padding: 5rpx 0;
}
.action-button {
  font-size: 24rpx;
  padding: 6rpx 20rpx;
  margin-left: 16rpx;
  border-radius: 30rpx;
  background-color: #fff;
  line-height: 1.5;
  min-height: auto;
  box-shadow: 0 2rpx 6rpx rgba(0, 0, 0, 0.1);
  transition: all 0.2s ease;
.add-button image {
  width: 36rpx;
  height: 36rpx;
  margin-right: 8rpx;
}
.action-button-hover {
  transform: scale(0.95);
  box-shadow: 0 1rpx 3rpx rgba(0, 0, 0, 0.1);
}
.action-button:active {
  transform: scale(0.95);
  box-shadow: 0 1rpx 3rpx rgba(0, 0, 0, 0.1);
}
.publish-button {
  color: rgba(45, 139, 247, 1);
  border: 1rpx solid rgba(45, 139, 247, 1);
}
/* 按钮点击效果 */
.publish-button-hover {
  background-color: rgba(45, 139, 247, 0.1);
}
.publish-button:active {
  background-color: rgba(45, 139, 247, 0.1);
}
.stop-button {
  color: #f56c6c;
  border: 1rpx solid #f56c6c;
  opacity: 0.8 !important;
}
.stop-button-hover {
  background-color: rgba(245, 108, 108, 0.1);
}
.stop-button:active {
  background-color: rgba(245, 108, 108, 0.1);
}
.execute-button {
  color: rgba(45, 139, 247, 1);
  border: 1rpx solid rgba(45, 139, 247, 1);
  opacity: 0.8 !important;
}
.execute-button-hover {
  background-color: rgba(45, 139, 247, 0.1);
  opacity: 0.8 !important;
}
.execute-button:active {
  background-color: rgba(45, 139, 247, 0.1);
.add-button-hover {
  opacity: 0.8;