管灌系统农户端微信小程序(嘉峪关应用)
zuoxiao
2025-03-06 91751b1b9b16c0044e16ad80c237b7275a409046
修改首页和登录相关

修改首页和登录相关
13个文件已修改
5个文件已添加
1196 ■■■■ 已修改文件
api/config.js 57 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
api/request.js 11 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app.js 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
images/eye-close.svg 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
images/eye-open.svg 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
images/lock.svg 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
images/login-bg.svg 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
images/user.svg 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/home/home.js 258 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/home/home.wxml 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/home/home.wxss 248 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/login/login.js 316 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/login/login.wxml 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/login/login.wxss 173 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/openCard/openCard.js 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/rechargeCard/rechargeCard.js 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/waterIntake/waterIntake.js 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
project.private.config.json 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
api/config.js
@@ -1,17 +1,62 @@
const { ENV } = require('./env')
let BASEURL
const PROJECT_URLS = {
  JYG: 'https://irrigate.dayuyanjiuyuan.top/', // 嘉峪关项目
  MQ: 'https://minqin.dayuyanjiuyuan.top/'    // 民勤项目
}
// 尝试从本地存储获取已选择的项目
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);
    }
  }
  // 如果全局应用已初始化,也尝试从全局变量获取
  const app = getApp();
  if (app && app.globalData && app.globalData.selectedProject) {
    selectedProject = app.globalData.selectedProject;
    console.log('从全局变量加载项目设置:', selectedProject);
  }
} catch (e) {
  console.error('获取已选择项目失败:', e);
}
switch (ENV) {
  case 'production':
    BASEURL = ''
    break
    BASEURL = PROJECT_URLS[selectedProject] || PROJECT_URLS.JYG;
    break;
  case 'test':
    BASEURL = 'https://irrigate.dayuyanjiuyuan.top/'
    break
    // BASEURL 将根据用户选择的项目动态设置
    BASEURL = PROJECT_URLS[selectedProject] || PROJECT_URLS.JYG;
    break;
  default:
    BASEURL = ''
    break
    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;
}
module.exports = {
  BASEURL,// 项目接口地址,支持多域名
  PROJECT_URLS,
  setBaseUrl
}
api/request.js
@@ -1,9 +1,7 @@
// 引入状态码statusCode
const statusCode = require('./statusCode')
// 定义请求路径, BASEURL: 普通请求API; CBASEURL: 中台API,不使用中台可不引入CBASEURL
const {
  BASEURL
} = require('./config')
const config = require('./config')
// 定义默认参数
const defaultOptions = {
  data: {},
@@ -57,7 +55,10 @@
  }
  header.tag = app.globalData.tag;
  return new Promise((resolve, reject) => {
    console.log("url:" + BASEURL + url);
    // 获取最新的 BASEURL
    let currentBaseUrl = app.globalData.baseUrl || config.BASEURL;
    console.log("url:" + currentBaseUrl + url);
    if (isShowLoding) {
      wx.showLoading({
        title: '通信中...', // 加载动画标题
@@ -68,7 +69,7 @@
    if (url.startsWith('http')) {
      myUrl = url;
    } else {
      myUrl = BASEURL + url;
      myUrl = currentBaseUrl + url;
    }
    wx.request({
      url: myUrl,
app.js
@@ -14,11 +14,14 @@
    })
  },
  globalData: {
    sessionId:'2024052821300200006',
    tag:'ym',
    // sessionId:'2024052821300200006',
    sessionId:'2025030416200600006',
    tag:'mq',
    userInfo: null,
    userId:"",
    userCode:"",
    token:""
    token:"",
    isLoggedIn:false,
    operator:"2025030416200600006"
  }
})
images/eye-close.svg
New file
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
    <g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
        <path d="M3.70711,2.29289 C3.31658,1.90237 2.68342,1.90237 2.29289,2.29289 C1.90237,2.68342 1.90237,3.31658 2.29289,3.70711 L20.2929,21.7071 C20.6834,22.0976 21.3166,22.0976 21.7071,21.7071 C22.0976,21.3166 22.0976,20.6834 21.7071,20.2929 L20.3032,18.889 C21.9524,17.4299 23.1734,15.5462 23.9281,13.4668 C23.9759,13.3258 24,13.1741 24,13.0193 C24,12.8644 23.9759,12.7128 23.9281,12.5717 C21.7396,6.83 17.1937,3 12,3 C9.74324,3 7.63584,3.57668 5.76477,4.62852 L3.70711,2.29289 Z M7.97987,6.84362 C9.22051,6.29395 10.5793,6 12,6 C15.3137,6 18.3137,7.84315 19.6569,10.5 C19.2269,11.3745 18.6616,12.1658 17.9953,12.8483 L15.9906,10.8436 C15.9968,10.7292 16,10.6151 16,10.5 C16,8.567 14.433,7 12.5,7 C12.3849,7 12.2708,7.00317 12.1564,7.0094 L7.97987,6.84362 Z M12.5,14 C12.6151,14 12.7292,13.9968 12.8436,13.9906 L8.00472,9.15165 C7.33841,9.83416 6.77312,10.6255 6.34315,11.5 C7.68629,14.1569 10.6863,16 14,16 C14.3389,16 14.6731,15.9859 15.0017,15.9585 L13.0094,13.9661 C12.8444,13.9884 12.6737,14 12.5,14 Z M4.07192,12.5717 C4.02411,12.7128 4,12.8644 4,13.0193 C4,13.1741 4.02411,13.3258 4.07192,13.4668 C6.26038,19.2085 10.8063,23.0385 16,23.0385 C17.8627,23.0385 19.6159,22.6648 21.2075,21.9953 L16.9953,17.7831 C16.0194,17.9258 15.0177,18 14,18 C8.80628,18 4.26038,14.17 2.07192,8.42833 C2.02411,8.28715 2,8.13555 2,7.98074 C2,7.82593 2.02411,7.67433 2.07192,7.53315 C2.82662,5.45379 4.04758,3.57011 5.69679,2.11098 L4.07192,12.5717 Z" fill="#999999" fill-rule="nonzero"></path>
    </g>
</svg>
images/eye-open.svg
New file
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
    <g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
        <path d="M12,4 C16.4183,4 20.4183,6.4183 22,10 C20.4183,13.5817 16.4183,16 12,16 C7.58172,16 3.58172,13.5817 2,10 C3.58172,6.4183 7.58172,4 12,4 Z M12,6 C8.68629,6 5.68629,7.84315 4.34315,10.5 C5.68629,13.1569 8.68629,15 12,15 C15.3137,15 18.3137,13.1569 19.6569,10.5 C18.3137,7.84315 15.3137,6 12,6 Z M12,7 C13.6569,7 15,8.34315 15,10 C15,11.6569 13.6569,13 12,13 C10.3431,13 9,11.6569 9,10 C9,8.34315 10.3431,7 12,7 Z M12,9 C11.4477,9 11,9.44772 11,10 C11,10.5523 11.4477,11 12,11 C12.5523,11 13,10.5523 13,10 C13,9.44772 12.5523,9 12,9 Z" fill="#999999" fill-rule="nonzero"></path>
    </g>
</svg>
images/lock.svg
New file
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
    <g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
        <path d="M12,1 C15.3137,1 18,3.68629 18,7 L18,10 L19,10 C20.1046,10 21,10.8954 21,12 L21,21 C21,22.1046 20.1046,23 19,23 L5,23 C3.89543,23 3,22.1046 3,21 L3,12 C3,10.8954 3.89543,10 5,10 L6,10 L6,7 C6,3.68629 8.68629,1 12,1 Z M19,12 L5,12 L5,21 L19,21 L19,12 Z M12,15 C12.5523,15 13,15.4477 13,16 C13,16.5523 12.5523,17 12,17 C11.4477,17 11,16.5523 11,16 C11,15.4477 11.4477,15 12,15 Z M12,3 C9.79086,3 8,4.79086 8,7 L8,10 L16,10 L16,7 C16,4.79086 14.2091,3 12,3 Z" fill="#1890FF" fill-rule="nonzero"></path>
    </g>
</svg>
images/login-bg.svg
New file
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="200px" height="200px" viewBox="0 0 200 200" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
    <defs>
        <linearGradient x1="50%" y1="0%" x2="50%" y2="100%" id="linearGradient-1">
            <stop stop-color="#1890FF" offset="0%"></stop>
            <stop stop-color="#0050B3" offset="100%"></stop>
        </linearGradient>
        <circle id="path-2" cx="100" cy="100" r="100"></circle>
    </defs>
    <g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
        <g>
            <mask id="mask-3" fill="white">
                <use xlink:href="#path-2"></use>
            </mask>
            <use id="Circle" fill="url(#linearGradient-1)" xlink:href="#path-2"></use>
            <path d="M120,80 C131.046,80 140,88.954 140,100 C140,111.046 131.046,120 120,120 C108.954,120 100,111.046 100,100 C100,88.954 108.954,80 120,80 Z M120,90 C114.477,90 110,94.477 110,100 C110,105.523 114.477,110 120,110 C125.523,110 130,105.523 130,100 C130,94.477 125.523,90 120,90 Z M120,125 C142.091,125 160,142.909 160,165 L80,165 C80,142.909 97.909,125 120,125 Z M120,135 C103.431,135 90,148.431 90,165 L150,165 C150,148.431 136.569,135 120,135 Z" fill="#FFFFFF" fill-rule="nonzero" mask="url(#mask-3)"></path>
        </g>
    </g>
</svg>
images/user.svg
New file
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
    <g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
        <path d="M12,2 C14.7614,2 17,4.23858 17,7 C17,9.76142 14.7614,12 12,12 C9.23858,12 7,9.76142 7,7 C7,4.23858 9.23858,2 12,2 Z M12,4 C10.3431,4 9,5.34315 9,7 C9,8.65685 10.3431,10 12,10 C13.6569,10 15,8.65685 15,7 C15,5.34315 13.6569,4 12,4 Z M12,13 C16.9706,13 21,17.0294 21,22 L3,22 C3,17.0294 7.02944,13 12,13 Z M12,15 C8.13401,15 5,18.134 5,22 L19,22 C19,18.134 15.866,15 12,15 Z" fill="#1890FF" fill-rule="nonzero"></path>
    </g>
</svg>
pages/home/home.js
@@ -4,6 +4,7 @@
  get,
  post
} = require('../../api/request.js');
const { PROJECT_URLS } = require('../../api/config.js');
Page({
  /**
@@ -15,8 +16,8 @@
    myItem: {},
    waterIntakeName: "",
    image: "/images/ic_head_bg.jpg",
    userPhone: "158****0723",
    userName: "张三",
    userPhone: "",
    userName: "请登录",
    scrollViewHeight: 0,
    listData: [],
    isRefreshing: false,
@@ -28,7 +29,10 @@
    },
    errorDialogTitle: "关阀错误",
    showForceConfirm: false, //是否强制开阀
    lastIntakeName: ""
    lastIntakeName: "",
    showProjectDialog: false,
    selectedProject: '',
    avatarTapCount: 0
  },
  openValve: function (e) {
@@ -75,9 +79,45 @@
   * 生命周期函数--监听页面加载
   */
  onLoad(options) {
    // 检查是否已选择项目
    const { PROJECT_URLS } = require('../../api/config.js');
    storage.getItem('selectedProject').then((project) => {
      if (project) {
        this.setData({
          selectedProject: project
        });
        // 确保全局变量存在
        getApp().globalData = getApp().globalData || {};
        // 设置 baseUrl
        const baseUrl = PROJECT_URLS[project];
        getApp().globalData.baseUrl = baseUrl;
        getApp().globalData.selectedProject = project;
        // 根据项目设置对应的tag
        if (project === 'JYG') {
          getApp().globalData.tag = 'ym'; // 嘉峪关项目对应tag为ym
        } else if (project === 'MQ') {
          getApp().globalData.tag = 'mq'; // 民勤项目对应tag为mq
        }
        console.log('加载已保存的项目:', project, '域名:', baseUrl, 'tag:', getApp().globalData.tag);
        // 检查登录状态
        if (!getApp().globalData.isLoggedIn) {
          this.checkLoginStatus();
          return; // 如果未登录,等待跳转到登录页面
        }
      } else {
        // 首次进入,显示项目选择弹窗
        this.setData({
          showProjectDialog: true
        });
        return; // 等待用户选择项目
      }
    //判断本地是否保存sessionId
    // 使用 wx.nextTick 等待页面渲染完成
    wx.nextTick(() => {
      this.calculateScrollViewHeight();
@@ -93,6 +133,13 @@
      this.getOpenList();
    }
    this.initData();
    }).catch(err => {
      console.error('Failed to get selectedProject:', err);
      // 出错时也显示项目选择弹窗
      this.setData({
        showProjectDialog: true
      });
    });
  },
  /**
@@ -214,7 +261,7 @@
      rtuAddr: rtuAddr,
      vcNum: vcNum, //虚拟卡ID
      orderNo: orderNo,
      operator: app.globalData.sessionId //操作员
      operator: app.globalData.operator //操作员
    };
    console.log("postCloseValaue" + data);
    post({
@@ -254,7 +301,7 @@
    const params = {
      url: 'wx/valve/get',
      data: {
        operator: app.globalData.sessionId
        operator: app.globalData.operator
      }
    };
    get(params).then(data => {
@@ -374,7 +421,7 @@
    const data = {
      intakeName: intakeName, //取水口ID
      // vcId: vcId, //虚拟卡ID
      operator: app.globalData.sessionId, //操作员
      operator: app.globalData.operator, //操作员
      forceOpen: !!isforce // 使用逻辑非操作符 !! 来确保 isForce 是布尔值  
    };
    post({
@@ -440,5 +487,200 @@
      console.log('Failed to load parameter:false');
    }
  
  },
  // 处理头像点击
  handleAvatarTap() {
    this.setData({
      avatarTapCount: this.data.avatarTapCount + 1
    });
    if (this.data.avatarTapCount >= 5) {
      this.setData({
        showProjectDialog: true,
        avatarTapCount: 0
      });
  }
  },
  // 处理弹窗可见性变化
  onVisibleChange(e) {
    // 如果尝试关闭弹窗且没有选择项目,则阻止关闭
    if (!e.detail.visible && !this.data.selectedProject) {
      return;
    }
    this.setData({ showProjectDialog: e.detail.visible });
  },
  // 处理项目选择变化
  onProjectChange(event) {
    console.log('选择的项目:', event.detail.value);
    this.setData({
      selectedProject: event.detail.value
    });
  },
  // 处理项目选择确认
  handleProjectConfirm() {
    if (!this.data.selectedProject) {
      wx.showToast({
        title: '请选择项目',
        icon: 'none'
      });
      return;
    }
    const projectName = this.data.selectedProject === 'JYG' ? '嘉峪关项目' : '民勤项目';
    // 保存项目选择到本地存储
    storage.setItem('selectedProject', this.data.selectedProject).then(() => {
      // 更新 BASEURL
      const { PROJECT_URLS } = require('../../api/config.js');
      const baseUrl = PROJECT_URLS[this.data.selectedProject];
      // 直接修改全局变量
      getApp().globalData = getApp().globalData || {};
      getApp().globalData.baseUrl = baseUrl;
      getApp().globalData.selectedProject = this.data.selectedProject;
      // 根据项目设置对应的tag
      if (this.data.selectedProject === 'JYG') {
        getApp().globalData.tag = 'ym'; // 嘉峪关项目对应tag为ym
      } else if (this.data.selectedProject === 'MQ') {
        getApp().globalData.tag = 'mq'; // 民勤项目对应tag为mq
      }
      console.log('已切换到项目:', projectName, '域名:', baseUrl, 'tag:', getApp().globalData.tag);
      this.setData({
        showProjectDialog: false
      });
      wx.showToast({
        title: `已选择${projectName}`,
        icon: 'success',
        duration: 2000
      });
      // 检查登录状态
      // setTimeout(() => {
      //   this.checkLoginStatus();
      // }, 500);
    }).catch(err => {
      console.error('保存项目选择失败:', err);
      wx.showToast({
        title: '保存失败,请重试',
        icon: 'error',
        duration: 2000
      });
    });
  },
  // 检查登录状态
  checkLoginStatus() {
    const app = getApp();
    // 检查是否已登录
    if (app.globalData.isLoggedIn && app.globalData.sessionId) {
      // 已登录,重新加载页面
      wx.reLaunch({
        url: '/pages/home/home'
      });
    } else {
      // 尝试从本地存储获取用户信息
      storage.getItem('userInfo').then(userInfoStr => {
        if (userInfoStr) {
          try {
            const userInfo = JSON.parse(userInfoStr);
            // 验证用户信息是否有效
            if (userInfo && userInfo.sessionId) {
              // 恢复登录状态
              app.globalData.sessionId = userInfo.sessionId;
              app.globalData.token = userInfo.token;
              app.globalData.userInfo = userInfo;
              app.globalData.isLoggedIn = true;
              // 已登录,重新加载页面
              wx.reLaunch({
                url: '/pages/home/home'
              });
              return;
            }
          } catch (e) {
            console.error('解析用户信息失败:', e);
          }
        }
        // 未登录,跳转到登录页面
        wx.redirectTo({
          url: `/pages/login/login?project=${this.data.selectedProject}`
        });
      }).catch(err => {
        console.error('获取用户信息失败:', err);
        // 未登录,跳转到登录页面
        wx.redirectTo({
          url: `/pages/login/login?project=${this.data.selectedProject}`
        });
      });
    }
  },
  wxLogin(){
    if(!getApp().globalData.isLoggedIn){
      const that = this;
      wx.login({
        success: function (res) {
          if (res.code) {
            var code = res.code;
            console.log(code);
            // 将code发送到服务器获取openid
            that.codeLogin(code);
          } else {
            console.log('登录失败!' + res.errMsg);
          }
        }
      });
    }
  },
  //微信code登录
  codeLogin(codeData) {
    wx.showLoading({
      title: '正在登录请稍候...', // 加载提示文字
      mask: true // 是否显示透明蒙层,防止触摸穿透,默认为 false
    });
    const data = {
      code: codeData, //临时登录凭证
    };
    post({
      url: "wx/client/code_login",
      data: data,
    }).then(response => {
      // 处理成功响应
      console.log('请求成功:', response);
      // 加载完成后隐藏加载动画
      wx.hideLoading();
      if (response.code === "0001") {
        //假如为空则跳转到绑定界面
        if (response.content.client.clientId === "") {
          wx.navigateTo({
            url: '/pages/login/login'
          })
        } else {
          //缓存在本地
          this.setData({
            isLogin: true
          })
          getApp().globalData.sessionId = response.content.client.sessionId
          storage.setItem("sessionId", response.content.client.sessionId)
          getApp().globalData.clientId = response.content.client.clientId
          storage.setItem("clientId", response.content.client.clientId)
          this.initData();
        }
      } else {
      }
    }).catch(error => {
      // 加载完成后隐藏加载动画
      wx.hideLoading();
      // 处理错误响应
      console.error('请求失败:', error);
    });
  },
})
pages/home/home.wxml
@@ -3,9 +3,9 @@
  <view class="head-wrapper">
    <view class="head-top">
      <t-avatar class="avatar-example" image="{{image}}" size="120rpx" />
      <t-avatar class="avatar-example" image="{{image}}" size="120rpx" bind:tap="handleAvatarTap" />
      <view class="head-text-wrapper">
        <text>{{userName}}</text>
        <text bind:tap="wxLogin">{{userName}}</text>
        <text class="head-bottom">{{userPhone}}</text>
      </view>
      <view class="head-button-wrapper">
@@ -75,4 +75,30 @@
  <t-dialog class="error-dialog" title="{{errorDialogTitle}}" visible="{{showErrorDialog}}" content="{{errorData}}" confirm-btn="{{ confirmBtn }}" bind:confirm="closeDialog" />
  <t-dialog visible="{{showForceConfirm}}" content="当前虚拟卡被占用,是否强制开阀?" confirm-btn="{{ { content: '强制开阀', variant: 'base', theme: 'danger' } }}" cancel-btn="取消" bind:confirm="confirmForceDialog" bind:cancel="cancelDialog" />
  <!-- 使用微信原生弹窗组件 -->
  <view class="project-modal" wx:if="{{showProjectDialog}}">
    <view class="project-modal-mask"></view>
    <view class="project-modal-content">
      <view class="project-modal-header">
        <text class="project-modal-title">请选择项目</text>
      </view>
      <view class="project-modal-body">
        <radio-group class="project-radio-group" bindchange="onProjectChange">
          <label class="project-radio {{selectedProject === 'JYG' ? 'project-radio-selected' : ''}}">
            <radio value="JYG" checked="{{selectedProject === 'JYG'}}" color="#1890FF" />
            <text>嘉峪关项目</text>
          </label>
          <label class="project-radio {{selectedProject === 'MQ' ? 'project-radio-selected' : ''}}">
            <radio value="MQ" checked="{{selectedProject === 'MQ'}}" color="#1890FF" />
            <text>民勤项目</text>
          </label>
        </radio-group>
      </view>
      <view class="project-modal-footer">
        <button class="project-modal-btn" disabled="{{!selectedProject}}" bindtap="handleProjectConfirm">确认</button>
      </view>
    </view>
  </view>
</view>
pages/home/home.wxss
@@ -354,3 +354,251 @@
  font-size: 30rpx;
  color: #ffffff;
}
.project-select-container {
  padding: 20rpx 0;
}
.project-select-container .t-radio {
  margin-bottom: 20rpx;
  padding: 10rpx 0;
}
.project-select-container .t-radio:last-child {
  margin-bottom: 0;
}
.project-select-container .t-radio__label {
  font-size: 28rpx;
  color: #333;
}
.project-select-container .t-radio--checked {
  background: #e6f4ff;
}
/* 弹窗按钮样式 */
.dialog__button-group {
  display: flex;
  justify-content: space-between;
  margin-top: 32rpx;
}
.dialog__button-group .t-button {
  flex: 1;
  margin: 0 16rpx;
}
.dialog__button-group .t-button:first-child {
  margin-left: 0;
}
.dialog__button-group .t-button:last-child {
  margin-right: 0;
}
.project-select-popup {
  background-color: #fff;
  border-radius: 16rpx;
  width: 600rpx;
}
.popup-title {
  font-size: 32rpx;
  font-weight: bold;
  text-align: center;
  padding: 32rpx;
  border-bottom: 1px solid #f0f0f0;
}
.popup-content {
  padding: 32rpx;
}
.radio-group {
  display: flex;
  flex-direction: column;
  gap: 20rpx;
}
.radio-group .t-radio {
  margin-bottom: 20rpx;
  padding: 20rpx;
  background: #f5f5f5;
  border-radius: 8rpx;
}
.radio-group .t-radio:last-child {
  margin-bottom: 0;
}
.popup-footer {
  padding: 32rpx;
  border-top: 1px solid #f0f0f0;
}
.dialog-content {
  padding: 32rpx;
  max-height: 60vh;
}
.project-options {
  display: flex;
  flex-direction: column;
  gap: 20rpx;
}
.project-option {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 30rpx;
  background: #f5f5f5;
  border-radius: 8rpx;
  font-size: 32rpx;
}
.project-option.selected {
  background: #e6f4ff;
  border: 2rpx solid #1890FF;
}
.selected-icon {
  width: 40rpx;
  height: 40rpx;
}
.project-dialog-content {
  padding: 20rpx;
}
.project-item {
  margin: 20rpx 0;
  padding: 30rpx;
  background-color: #f5f5f5;
  border-radius: 8rpx;
  text-align: center;
  font-size: 32rpx;
}
.project-item-selected {
  background-color: #e6f4ff;
  color: #1890FF;
  border: 2rpx solid #1890FF;
}
.radio-content {
  padding: 30rpx 20rpx 10rpx;
}
.radio-content .t-radio {
  margin-bottom: 20rpx;
  padding: 20rpx;
  background-color: #f5f5f5;
  border-radius: 8rpx;
}
.radio-content .t-radio--checked {
  background-color: #e6f4ff;
}
.project-modal {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 9999;
  display: flex;
  align-items: center;
  justify-content: center;
}
.project-modal-mask {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background-color: rgba(0, 0, 0, 0.6);
}
.project-modal-content {
  position: relative;
  width: 80%;
  max-width: 600rpx;
  background-color: #fff;
  border-radius: 16rpx;
  overflow: hidden;
  display: flex;
  flex-direction: column;
}
.project-modal-header {
  padding: 30rpx;
  text-align: center;
  border-bottom: 1rpx solid #f0f0f0;
}
.project-modal-title {
  font-size: 36rpx;
  font-weight: bold;
  color: #333;
}
.project-modal-body {
  padding: 30rpx;
}
.project-modal-tip {
  display: block;
  font-size: 28rpx;
  color: #999;
  margin-bottom: 30rpx;
  text-align: center;
}
.project-radio-group {
  display: flex;
  flex-direction: column;
  gap: 20rpx;
}
.project-radio {
  display: flex;
  align-items: center;
  padding: 20rpx;
  background-color: #f5f5f5;
  border-radius: 8rpx;
}
.project-radio-selected {
  background-color: #e6f4ff;
  border: 2rpx solid #1890FF;
}
.project-radio text {
  margin-left: 10rpx;
  font-size: 32rpx;
}
.project-modal-footer {
  padding: 30rpx;
  border-top: 1rpx solid #f0f0f0;
}
.project-modal-btn {
  width: 100%;
  height: 80rpx;
  line-height: 80rpx;
  text-align: center;
  background-color: #1890FF;
  color: #fff;
  font-size: 32rpx;
  border-radius: 8rpx;
}
.project-modal-btn[disabled] {
  background-color: #cccccc;
  color: #ffffff;
}
pages/login/login.js
@@ -1,4 +1,8 @@
const app = getApp();
const storage = require('../../utils/storage.js');
const {
  post
} = require('../../api/request.js');
Page({
@@ -6,79 +10,217 @@
   * 页面的初始数据
   */
  data: {
    mobile: '',
    code: '',
    codeSent: false,
    countdown: 60,
    phone: '',
    verificationCode: '',
    codeText: '获取验证码',
    counting: false,
    countDown: 60,
    projectName: '嘉峪关项目', // 默认项目名称
    selectedProject: 'JYG', // 默认项目代码
    showErrorDialog: false
  },
  bindMobileInput: function (e) {
  /**
   * 绑定手机号输入
   */
  bindPhoneInput: function (e) {
    this.setData({
      mobile: e.detail.value,
      phone: e.detail.value
    });
  },
  sendCode: function () {
    if (!this.data.mobile) {
  /**
   * 绑定验证码输入
   */
  bindCodeInput: function (e) {
    this.setData({
      verificationCode: e.detail.value
    });
  },
  /**
   * 发送验证码
   */
  sendVerificationCode: function () {
    const {
      phone,
      counting
    } = this.data;
    // 如果正在倒计时,则不允许再次发送
    if (counting) {
      return;
    }
    // 验证手机号格式
    if (!/^1\d{10}$/.test(phone)) {
      wx.showToast({
        title: '请输入手机号',
        icon: 'none',
        duration: 2000,
        title: '请输入正确的手机号',
        icon: 'none'
      });
      return;
    }
    // 在这里处理发送验证码的逻辑,可以调用后台接口实现
    // 以下是一个简单的示例,仅作参考
    wx.showToast({
      title: '验证码已发送',
      icon: 'success',
      duration: 2000,
    });
    // 开始倒计时
    this.startCountDown();
    this.setData({
      codeSent: true,
    });
    // 启动倒计时
    this.startCountdown();
    // 发送验证码请求
    this.postCode();
  },
  //倒计时
  startCountdown: function () {
    let that = this;
    let timer = setInterval(function () {
      let countdown = that.data.countdown - 1;
      that.setData({
        countdown: countdown,
  /**
   * 开始倒计时
   */
  startCountDown: function () {
    this.setData({
      counting: true,
      countDown: 60
      });
      if (countdown <= 0) {
    const timer = setInterval(() => {
      if (this.data.countDown <= 1) {
        clearInterval(timer);
        that.setData({
          codeSent: false,
          countdown: 60,
        this.setData({
          counting: false,
          codeText: '获取验证码'
        });
      } else {
        this.setData({
          countDown: this.data.countDown - 1,
          codeText: `${this.data.countDown - 1}秒后重发`
        });
      }
    }, 1000);
    // 保存timer引用,以便在页面卸载时清除
    this.countDownTimer = timer;
  },
  /**
   * 停止倒计时
   */
  stopCountDown: function () {
    if (this.countDownTimer) {
      clearInterval(this.countDownTimer);
    }
    this.setData({
      counting: false,
      codeText: '获取验证码'
    });
  },
  /**
   * 登录
   */
  login: function (e) {
    console.log("login")
    let userName = "张三"
    wx.navigateTo({
      url: '/pages/wxbind/wxbind'
    })
  login: function () {
    const {
      phone,
      verificationCode
    } = this.data;
    // 验证手机号和验证码
    if (!/^1\d{10}$/.test(phone)) {
      wx.showToast({
        title: '请输入正确的手机号',
        icon: 'none'
      });
      return;
    }
    if (!/^\d{6}$/.test(verificationCode)) {
      wx.showToast({
        title: '请输入6位验证码',
        icon: 'none'
      });
      return;
    }
    // 显示加载中
    wx.showLoading({
      title: '登录中...',
      mask: true
    });
    this.wsLogin();
    // 发送登录请求
    post('/api/loginByCode', {
      phone: phone,
      code: verificationCode,
      projectCode: this.data.selectedProject
    }).then(res => {
      wx.hideLoading();
      if (res.code === 0 && res.data) {
        // 保存用户信息和token
        storage.setUserInfo(res.data);
        storage.setToken(res.data.token);
        // 跳转到首页
        wx.switchTab({
          url: '/pages/index/index'
        });
      } else {
        wx.showToast({
          title: res.msg || '登录失败,请重试',
          icon: 'none'
        });
      }
    }).catch(err => {
      wx.hideLoading();
      console.error('登录失败', err);
      wx.showToast({
        title: '网络异常,请重试',
        icon: 'none'
      });
    });
  },
  /**
   * 跳转到注册页面
   */
  goToRegister: function () {
    wx.showToast({
      title: '注册功能暂未开放',
      icon: 'none',
      duration: 2000
    });
  },
  /**
   * 跳转到忘记密码页面
   */
  goToForgetPassword: function () {
    wx.showToast({
      title: '找回密码功能暂未开放',
      icon: 'none',
      duration: 2000
    });
  },
  /**
   * 生命周期函数--监听页面加载
   */
  onLoad(options) {
  onLoad: function (options) {
    // 获取选择的项目
    if (options.project) {
      this.setData({
        selectedProject: options.project,
        projectName: options.project === 'JYG' ? '嘉峪关项目' : '民勤项目'
      });
    } else {
      // 尝试从本地存储获取已选择的项目
      storage.getItem('selectedProject').then(project => {
        if (project) {
          this.setData({
            selectedProject: project,
            projectName: project === 'JYG' ? '嘉峪关项目' : '民勤项目'
          });
        }
      }).catch(err => {
        console.error('获取已选择项目失败:', err);
      });
    }
  },
  /**
@@ -106,7 +248,11 @@
   * 生命周期函数--监听页面卸载
   */
  onUnload() {
    // 清除验证码倒计时定时器
    if (this.countDownTimer) {
      clearInterval(this.countDownTimer);
      this.countDownTimer = null;
    }
  },
  /**
@@ -129,7 +275,83 @@
  onShareAppMessage() {
  },
  //获取验证码
  postCode: function () {
    const params = {
      url: 'wx/client/send_sms?phoneNumber=' + this.data.phone
    };
    post(params)
      .then((data) => {
        wx.showToast({
          title: '验证码已发送',
          icon: 'success',
          duration: 2000,
        });
        this.setData({
          codeSent: true,
        });
        // 启动倒计时
        this.startCountdown();
      })
      .catch((error) => {
        wx.showToast({
          title: error.msg,
          icon: 'none'
        });
        console.error('Failed to add item:', error);
      });
  },
  wsLogin() {
    wx.login({
      success: res => {
        if (res.code) {
          console.log('登录成功,获取到的code:', res.code);
          // 发送 res.code 到后台服务器换取 openId, sessionKey, unionId
          this.verify(res.code)
        } else {
          console.log('登录失败!' + res.errMsg);
        }
      }
    });
  },
  //用户绑定
  verify(wxCode) {
    const params = {
      url: 'wx/client/verify',
      data: {
        phoneNumber: this.data.phone,
        securityCode: this.data.verificationCode,
        code: wxCode
      }
    };
    post(params)
      .then((data) => {
        wx.hideLoading();
        getApp().globalData.sessionId = String(data.content.sessionId)
        storage.setItem("sessionId", String(data.content.sessionId))
        getApp().globalData.clientId = String(data.content.clientId)
        storage.setItem("clientId", String(data.content.clientId))
        this.bindSuccess();
      })
      .catch((error) => {
        wx.hideLoading();
        wx.showToast({
          title: error.msg,
          icon: 'error',
          duration: 3000,
        });
        console.error('Failed to add item:', error);
      });
  },
  bindSuccess: function () {
    if (!this.data.isButtonEnabled) return;
    wx.showToast({
      title: '绑定成功',
      icon: 'success'
    });
    // 跳转到 TabBar 页面
    wx.redirectTo({
      url: '/pages/home/home' // 这里填写你想要跳转的 TabBar 页面路径
    });
  },
})
pages/login/login.wxml
@@ -1,19 +1,32 @@
<!--pages/login/login.wxml-->
<view class="login-container">
  <view>
    <image src="/images/login_bg.jpeg"></image>
  </view>
  <view class="input-wrapper">
    <input class="input" placeholder="请输入11位手机号" maxlength="11" bindinput="bindMobileInput" />
  </view>
  <view class="input-wrapper">
    <input class="input" type="number" placeholder="请输入验证码"  />
    <button class="code-button" wx:if="{{!codeSent}}" bindtap="sendCode">获取验证码</button>
    <button class="code-button" wx:if="{{codeSent}}" disabled>重新获取({{countdown}}s)</button>
  </view>
  <view>
    <button class="login-button" bindtap="login">登录</button>
  <view class="logo-container">
  </view>
  <view class="form-container">
    <view class="title">用户登录</view>
    <view class="input-wrapper">
      <view class="input-icon user-icon"></view>
      <input class="input" type="number" placeholder="请输入手机号" maxlength="11" bindinput="bindPhoneInput" />
    </view>
    <view class="input-wrapper">
      <view class="input-icon lock-icon"></view>
      <input class="input" type="number" placeholder="请输入验证码" maxlength="6" bindinput="bindCodeInput" />
      <view class="code-button" bindtap="sendVerificationCode">
        <text>{{codeText}}</text>
      </view>
    </view>
    <view class="login-btn-container">
      <button class="login-button" bindtap="login">登录</button>
    </view>
  </view>
  <view class="project-info">
    <text>当前项目: {{projectName}}</text>
  </view>
</view>
pages/login/login.wxss
@@ -1,15 +1,131 @@
/* pages/login/login.wxss */
.login-container {
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  height: 80vh;
  height: 100vh;
  width: 100%;
  background-color: #f5f5f5;
}
.logo-container {
  display: flex;
  justify-content: center;
  align-items: center;
  padding: 60rpx 0;
}
.logo-circle {
  width: 200rpx;
  height: 200rpx;
  border-radius: 50%;
  background: #1890FF;
  display: flex;
  justify-content: center;
  align-items: center;
  color: white;
  font-size: 80rpx;
  font-weight: bold;
}
.form-container {
  background-color: #fff;
  border-radius: 20rpx;
  margin: 0 40rpx;
  padding: 40rpx;
  box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.1);
}
.title {
  font-size: 40rpx;
  font-weight: bold;
  color: #333;
  margin-bottom: 40rpx;
  text-align: center;
}
.input-wrapper {
  display: flex;
  align-items: center;
  border-bottom: 1rpx solid #e0e0e0;
  margin-bottom: 40rpx;
  padding-bottom: 20rpx;
}
.input-icon {
  width: 40rpx;
  height: 40rpx;
  margin-right: 20rpx;
  display: flex;
  justify-content: center;
  align-items: center;
  color: #1890FF;
  font-weight: bold;
  font-size: 32rpx;
}
.user-icon::before {
  content: "👤";
}
.lock-icon::before {
  content: "🔒";
}
.eye-open-icon::before {
  content: "👁️";
}
.eye-close-icon::before {
  content: "👁️‍🗨️";
}
.input {
  flex: 1;
  height: 60rpx;
  font-size: 32rpx;
  color: #333;
}
.password-toggle {
  padding: 10rpx;
  font-size: 32rpx;
}
.login-btn-container {
  margin-top: 60rpx;
}
.login-button {
  height: 90rpx;
  background-color: #1890FF;
  color: #fff;
  font-size: 34rpx;
  border-radius: 45rpx;
  display: flex;
  justify-content: center;
  align-items: center;
}
.footer-links {
  display: flex;
  justify-content: space-between;
  margin-top: 40rpx;
  font-size: 28rpx;
}
.link {
  color: #1aad19;
}
.project-info {
  position: fixed;
  bottom: 40rpx;
  width: 100%;
  text-align: center;
  font-size: 28rpx;
  color: #999;
}
image {
  position: fixed;
@@ -21,30 +137,6 @@
  z-index: -999;
}
.input {
  flex: 1;
  height: 30rpx;
  padding: 10rpx;
  border: 1px solid #ccc;
  border-radius: 5px;
  background-color: #fff;
  font-size: 35rpx;
  padding-left: 15rpx;
}
.input-wrapper {
  display: flex;
  /* 使用 Flex 布局使 text 和 input 在同一行 */
  align-items: center;
  justify-content: center;
  margin-bottom: 40rpx;
  margin-left: 40rpx;
  margin-right: 40rpx;
}
.input-label {
  flex: 1;
  /* 让值占据剩余空间 */
@@ -52,7 +144,7 @@
  width: 150rpx;
  margin-right: 10rpx;
  font-size: 31rpx;
  color: #fff;
  color: #333;
  font-weight: bold;
  text-align: justify;
  text-align-last: justify;
@@ -64,18 +156,17 @@
  align-items: center;
}
.login-button {
  margin-top: 50rpx;
  margin-left: 40rpx;
  margin-right: 40rpx;
  height: 90rpx;
  justify-content: center; /* 水平居中 */
  align-items: center; /* 垂直居中 */
  display: flex;
.code-button {
  width: 220rpx;
  font-size: 28rpx;
  color: #1890FF;
  text-align: center;
  padding: 10rpx 0;
  border-left: 1rpx solid #e0e0e0;
}
.code-button {
  width: 50%;
  font-size: 30rpx;
  margin-left: 15rpx;
.code-button text {
  display: block;
  width: 100%;
  height: 100%;
}
pages/openCard/openCard.js
@@ -32,7 +32,7 @@
    const data = {
      intakeId: this.data.intakeId, //取水口ID
      vcId: vcId, //虚拟卡ID
      operator: app.globalData.sessionId //操作员
      operator: app.globalData.operator //操作员
    };
    post({
      url: "8087/wx/valve/open_wx",
pages/rechargeCard/rechargeCard.js
@@ -80,7 +80,7 @@
  },//获取虚拟卡列表
  getCardList(){
    get({url:'https://d4x9787456.vicp.fun/sell/virtual_card/get', data: {
      operator: getApp().globalData.sessionId
      operator: getApp().globalData.operator
    }})
    .then((data) => {
      if(data.success&&data.code==="0001"){
@@ -100,7 +100,7 @@
  },
  getWaterCardList(){
    get({url:'https://d4x9787456.vicp.fun/sell/virtual_card/get', data: {
      operator: getApp().globalData.sessionId
      operator: getApp().globalData.operator
    }})
    .then((data) => {
      if(data.success&&data.code==="0001"){
pages/waterIntake/waterIntake.js
@@ -89,7 +89,7 @@
    const data = {
      intakeId: intakeId, //取水口ID
      // vcId: vcId, //虚拟卡ID
      operator: app.globalData.sessionId, //操作员
      operator: app.globalData.operator, //操作员
      forceOpen: !!isforce // 使用逻辑非操作符 !! 来确保 isForce 是布尔值  
    };
    post({
@@ -230,7 +230,7 @@
    get({
        url: 'wx/intake/used_intakes',
        data: {
          operator: getApp().globalData.sessionId
          operatorId: getApp().globalData.operator
        }
      })
      .then((data) => {
project.private.config.json
@@ -3,6 +3,6 @@
  "projectname": "irrigate_user",
  "setting": {
    "compileHotReLoad": true,
    "urlCheck": true
    "urlCheck": false
  }
}