原始记录:

2025-09-22:

  • 尝试实现数据超过指定长度自动清除并缓存的逻辑。
  • 尝试解决了x轴不删除的问题,这部分仍需要后续加强理解。

2025-09-23:

  • 尝试用npm构建,使用vant组件添加了一个DropdownMenu
  • 通过源码暴露的样式表改变了按钮样式的固定高度。仍需要解决border无法覆盖矩形按钮的问题。

2025-09-24:

  • 通过filter里关闭--dropdown-menu-background-color属性,发现backgroundborder的渲染冲突。
  • 通过wxss里设置--dropdown-menu-background-color: transparent;解决昨天的border在启用radius时圆角无法渲染的问题。
  • 替换启动增氧的buttonswitch并自定义颜色。

2025-09-25:

  • 确定了通过标志位实现历史数据-实时数据切换的实现逻辑。
  • 尝试在onLoad里加入缓存读取功能,并将读取到的key pushselectorOptions数组内,但失败了,读取到为undefined的值。并且加入标志位后,阻塞了原有数据显示和刷新逻辑的执行。
  • 原因:log的时间不对,在const声明变量之前就log当然是无效的。

下面列出逻辑方便回顾:

  • 如果用户选中「实时状态」→ flag = true,定时器里会不断触发渲染。
  • 如果用户选中历史 key → 设置 flag = false,定时器只写缓存、不更新图表,历史数据通过独立的渲染函数一次性画出来。

2025-09-26:

  • 实现了页面加载时读取缓存中的key、自动排序、并通过DropdownMenu预览的功能!

下面列出一些要点:

  1. ...是JS里的扩展运算符,简单理解就是把数组里的每一项单独“展开”。在JS里通常用它来合并两个数组。

    例如let arr1 = [1, 2, 3];使用let arr3 = [...arr1];,得到的新数组将是[1, 2, 3]

    需要注意,[...arr1, arr2][...arr1, ...arr2]的区别。前者将形成一个嵌套数组,后者还是一个一维数组。

  2. map 是数组的方法,全名是 Array.prototype.map。它的作用是:对数组里的每个元素执行一次函数,然后返回一个由结果组成的新数组

    1
    2
    3
    4
    5
    6
    // 这是一个例子
    const numbers = [1, 2, 3, 4];
    const doubled = numbers.map(x => x * 2);

    console.log(doubled); // [2, 4, 6, 8]
    console.log(numbers); // [1, 2, 3, 4] (原数组没变)
  3. 箭头函数=>。它是写函数的一种简化写法。可理解为“参数 => 返回值”。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // 只有一个参数,可以省略括号,以及return
    const square = x => x * x;
    console.log(square(4)); // 16

    const double = n => n * 2;
    console.log(double(7)); // 14

    // 把每个日期字符串 k,加工成一个带 text、value、icon 字段的对象数组
    const opts = dateKeys.map((k, idx) => ({text: k, value: baseLength + idx, icon: ""}))

2025-09-27

  • 通过:

    1
    2
    3
    4
    5
    /* 覆盖掉遮罩层颜色 */
    /* !important 是 CSS 里的一个“加权符”,意思是 这条规则的优先级最高 */
    .van-overlay {
    background-color: transparent !important;
    }

    在保留overlay功能的同时,去除了遮罩的颜色。下拉菜单的背景变为透明的。

2025-09-28:

  • 通过display: block;将元素撑开,解决了borderborder-radius无法设置的问题。

  • 通过filter里找到的暴露变量--popup-background-color,将其设置为transparent,干掉了下拉菜单的白色背景。

  • 通过将.van-dropdown-item__optionoverflow属性设置为auto,解决了border-radiusborder同时存在时,border缺角的问题。

    补充说明:overflow 是一个 CSS 盒模型的属性,它决定了 当元素的内容超出它的盒子(content box + padding box + border box定义的边界)时,浏览器该怎么办。默认是visible,不作裁剪,所以会露出来,看起来缺角。

    **参考文献:**https://stackoverflow.com/questions/8582176/should-border-radius-clip-the-content

  • bug:在真机测试时,出现了很多样式无法应用的问题。重新构建后在模拟器上出现了很多报错且部分交互失效。

2025-09-29:

  • 通过在页面js内写入

    1
    import '../../miniprogram_npm/@vant/weapp/mixins/transition';

    并重新构建npm,解决了页面交互失效的问题。

  • bug:真机测试时,样式无法生效的问题依然存在,且vant未提供对应的css变量,这个需要更深入的研究。

2025-10-06:

前几天旅游去了,故没有更新。

  • 通过使用Vant Weapp提供的样式覆盖办法,在wxml内加入custom-class修饰,在真机上实现组件部分样式自定义。
  • 通过在app.wxss内添加关于.van-dropdown-item__option的全局样式,能够为option添加圆角等属性,但真机调试时,单边border的圆角无法绘制,模拟器正常。
  • 通过拉取--popup-background-color--overlay-background-color变量,在真机上实现对下拉菜单和遮罩背景色的设置。

2025-10-07:

  • 汇总真机上的问题:

    • 下拉菜单的出现动画消失,收起动画能保持。
    • 单边border的圆角无法绘制,只绘制直线,直接写border可以绘制完整圆角的边框。
    • dropdown-menu的伪元素(小箭头)看起来在真机上失效了,不显示。
    • 由于伪元素在真机上不显示,导致图标丢失等系列问题。
  • 删除下面代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    "renderer": "skyline",
    "rendererOptions": {
    "skyline": {
    "defaultDisplayBlock": true,
    "defaultContentBox": true,
    "tagNameStyleIsolation": "legacy",
    "disableABTest": true,
    "sdkVersionBegin": "3.0.0",
    "sdkVersionEnd": "15.255.255"
    }
    },
    "componentFramework": "glass-easel",

    将渲染模式调整为WebView,解决了上面的所有问题。

  • bug:在真机上,canvas图表会穿透下拉菜单显示。且下拉菜单为fixed,导致无法被拖动。

2025-10-08:

  • 通过删掉DropdownItemoverflow属性,解决了下拉菜单无法滚动的问题。
  • 通过直接限制popup的高度,使界面更美观。
  • 压缩图片素材分辨率,减小包体大小
  • 未修复昨天的新问题。

2025-10-09:

  • 通过删除WXML中的force-use-old-canvas="true"语句,图表在真机上层级过高的问题得到解决。但导致在模拟器上无法随着页面滚动。

    该问题由微信小程序对canvas图表的不兼容导致。解决方法为,在需要真机测试时,删除该语句,模拟器调试时,还原该语句。

    **参考文献:**https://github.com/ecomfe/echarts-for-weixin/issues/940

  • 遗留bug:在下拉菜单展开时滚动页面,由于positionfixed,会导致页面滚动而下拉菜单不滚动。

2025-10-10:

  • 通过在页首加入下面代码,实现页面overflow属性控制:

    1
    <page-meta page-style="{{ enableScroll ? '' : 'overflow:hidden;' }}" />

    结合下面的两段代码,绑定下拉菜单开关事件与scroll-y开关变量:

    1
    2
    3
    <van-dropdown-item custom-class="my-dropdown-item" value="{{initialValue}}" options="{{selectorOption}}" bind:open="openDropdown" bind:close="closeDropdown" />

    <scroll-view scroll-y="{{enableScroll}}" class="body">

    配合绑定的js事件:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    openDropdown() { 
    this.setData({enableScroll: false });
    console.log("set enableScroll false");
    },

    closeDropdown() {
    this.setData({enableScroll: true });
    console.log("set enableScroll true");
    },

    三者组合拳,通过统一的变量enableScroll控制。实现禁止页面滚动的功能。解决了前面的遗留bug

  • bug:页面高度不好设置,设置过高(例如100vh),导致滚动到最下面时候navBar跟着跑。

2025-10-11:

  • 通过下面的函数,计算navigation-bar高度,并适当补偿,实现针对不同机型自动配置body高度,有较小偏差,但暂时能接受。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    /* 了解计算方法就好了,方法要用可以再查 */
    getBodyHeight() {
    const statusBarHeight = wx.getWindowInfo().statusBarHeight;
    const menuButtonInfo = wx.getMenuButtonBoundingClientRect();
    const navBarHeight = menuButtonInfo.height + (menuButtonInfo.top - statusBarHeight) * 2;
    const windowHeight = wx.getWindowInfo().windowHeight;
    const bodyHeight = windowHeight - statusBarHeight - navBarHeight - 3;
    this.setData({bodyHeight});
    },

    /*
    导航栏高度 = 胶囊高度 + 胶囊的上下边距
    胶囊的上下边距相等,所以乘2
    通过wx.getMenuButtonBoundingClientRect()可以获取胶囊的宽高等信息
    */
  • 通过修改navigation-bar.wxss.weui-navigation-bar__leftalign-itemcenter,从而使导航栏返回键对齐。

  • 通过j将this.isRealtimeRender替换为this.data.isRealtimeRender,解决了变量未定义的问题。

    下面是一些解释。

    this 指的是整个页面的实例对象。它不仅包含 data,还包含自定义函数和属性、微信系统注入的生命周期方法。换句话说,datathis的子对象。所以引用data里的内容,必然是this.data.XXX这样的形式。

    1
    2
    3
    4
    5
    6
    7
    8
    this = {
    data: { ... },
    onLoad() {},
    onReady() {},
    chart: xxx,
    timer: xxx,
    ...
    }

    如果引用了自定义函数,例如下面这样:

    1
    2
    3
    4
    5
    6
    7
    8
    Page({
    getBodyHeight() {
    },

    onLoad(options) {
    this.getBodyHeight();
    }
    })

    因为自定义函数也是this的子对象,所以如果不通过this引用,反而直接写,就会有未定义的报错。

2025-10-12:

  • 下拉菜单交互函数设计:

    1. 根据返回的value进行判断。假设选择了显示实时数据,isRealtimeRender设置为true
    2. 假如选择了其他选项,会把isRealtimeRender设置为false。同时,根据key读取缓存,然后清空图表并重新渲染数据。
    3. 选择其他选项后,再启用实时数据时,会先从缓存中数据,并渲染,然后isRealtimeRender设置为true,继续刷新数据。
    4. 今天已经实现了1和2两条,目前的逻辑是:选择其他选项后,会把当前所有暂存数据扔进当天的缓存。如果想修改得到第三条的效果,只需要额外添加一个数组用于存放临时数据。等页面关闭后统一存进缓存。
  • 知识点:

    1. 关于箭头函数=>。在2025-09-26已经有初步的了解。今天回顾一下。

      1
      2
      3
      4
      5
      /* 参数 => 返回值 。下面这句参数为item,引用item中的value,判断是否与e.detail相等 */
      item => item.value === e.detail

      /* 变量是数组item,返回值是它的第一个参数,再用map组合为新数组 */
      const times = this.data.dataList.map(item => item[0]);
    2. 关于find函数。

      Array.prototype.find() 是 ES6 引入的新方法,用来在数组中查找第一个符合条件的元素(找到后直接返回,不再继续找)。

      1
      array.find( callbackFn )

      callbackFn 会被自动调用多次——每次传入数组中的一个元素。
      如果你在 callbackFn 里返回 truefind() 就停下来并返回当前元素;
      如果全都返回 false,最后返回 undefined。聪明如你应该知道,回调函数应该是返回一个布尔值。

      1
      2
      /*箭头函数 + find函数,用来筛选指定value*/
      array.find( item => item.value === e.detail )
    3. 箭头函数?this?作用域!

      看下面两段代码,假设它们都要用Page中的chart绘制图表:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      wx.getStorage({
      key: selectedKey,
      success:(res) => {
      this.chart.setOption({
      xAxis: { data: times },
      series: [{ data: value }]
      })
      }

      wx.getStorage({
      key: selectedKey,
      success(res) {
      this.chart.setOption({
      xAxis: { data: times },
      series: [{ data: value }]
      })
      }

      聪明如你一定看出来了,两段代码实现了相同的功能。第一段代码用了箭头函数,第二段没用。但是第一段会报this.chart未定义的错误。为什么呢?

      因为箭头函数没有自己的 this,它会继承外层作用域this。这段代码想用Page实例中的this

      不过,如果一个函数不需要用全局的this,也不必用箭头函数,但是要注意,普通函数的 this 必须“靠调用者指定”,没人调用,可能指向未定义。

2025-10-13:

  • 修改逻辑:
    1. 在每次push数据时,不再定期判断数组长度和缓存数据,改为在OnUnload中统一缓存。
    2. 移除在读取缓存、切换显示历史数据时,即时缓存数据的功能。改为在OnUnload中统一缓存。
    3. 上述功能已实现。
  • 关闭页面后,数据是否继续存储,还有待考虑。目前关闭页面后会继续存储数据。
  • 暂时无法实现y轴数据自动吸附,或者formatter格式化数据并通过label显示,以方便用户查看y轴数据的功能。原因是图表初始化函数写在Page外,难以引用挂在页面内的数据。再评估之后,决定研究formatter的用法,以及直接让它调用Page上的数据的办法。
  • bug:在当天回看当天已缓存的内容,会出现内容覆盖或者丢失的情况。

2025-10-14:

  • 查文档发现,bug是由于微信缓存功能的自身特性导致。在同一个key下缓存新数据,会覆盖旧数据。

    结合之前写的逻辑,每超过20个数据,缓存前10个数据,导致回看历史数据时,每天只有10个数组在缓存中。这进一步验证了这点。

    解法为,在缓存前先读原来缓存的旧数据,再与新数据进行拼接,然后塞进同一个key,成功修复了昨天的bug

    实现方法如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    const removed = this.data.dataList.splice(0,this.data.dataList.length);

    wx.getStorage({
    key: currentDate,
    success(res) {
    const oldData = res.data || [];
    const newData = [...oldData, ...removed];
    wx.setStorageSync(currentDate , newData);
    console.log("已有数据,追加新的数据:", newData);
    },
    fail() {
    wx.setStorageSync(currentDate , removed);
    console.log("没有数据,存入新的数据:", removed);
    }
    })
  • 重新阅读官方文档。通过配置formatter实现了格式化数据,并通过label显示,以方便用户查看y轴数据。

    参考文档:Documentation - Apache ECharts。重点在于params.seriesData及其内容。

  • bug:点击某个点,显示对应的label后,概率性出现label显示范围异常宽大的问题。刷新页面后,坐标指示器不消失,概率性出现返回的y轴值为undefined的情况。

2025-10-15:

  • 实现了对昨天未定义bug的稳定复现。

    复现方法为:在数据较多的图表,选择第n个(顺序均为从左到右)的数据。此时可正常显示。对应的params.seriesData[0].data都有值。然后通过下拉菜单切换到数据量小于n的图表,此时会出现undefined现象。目前怀疑是加载问题导致。

    进一步观察,容易发现,当页面出现数据后,首次点击图表,坐标指示器会直接出现在该轴对应位置。但在图表刷新后,即使点击图表空白处,令坐标指示器消失,再点击某个位置重新,启用坐标指示器后,指示器是通过动画,从消失的位置移动到目标点。说明指示器并没有被完全关闭。

    label异常宽大的问题暂时无法复现。

  • 每次加载时重置图表,或许是一个好的选择。echartsInstance初始化时创建的实例,或许是一个突破口。但是今天事情比较多,太累了,先休息吧,放松一下。

2025-10-16:

  • 【修复】 修复了14号undefinedbug。测试结果表明,bug已经无法再被复现。

    修复方法:单独写一个函数用于重绘图表。在setOption之前执行。

    具体流程如下:

    1
    2
    3
    4
    5
    6
    7
    8
    redrawEchart() {
    var option = {
    /* 此处复制原配置项 */
    }
    this.chart.clear();
    this.chart.setOption(option, true);
    console.log("成功重启图表");
    }

    再次多次测试表明,label异常宽大的问题依然存在。目前没有稳定的复现方法。不论是否触发bug,它们的params没有明显异常。

    可能的测试方法:吧function移除,然后通过固定字串测试。然后通过固定字串+value测试。

  • 更换了较新版本的echarts.js

  • 【BUG】:在通过下拉菜单刷新图表后,图表会缩小。

  • 【BUG】:实时页面的数据,会连同时间一起显示在“值”的位置。

2025-10-17:

  • 目前找到了一种可能的复现label问题的方法。进入任意一个下拉菜单选项,图表绘制后,任意点击一个点,有较大的概率复现该问题。
  • 【修复】 修复了bug2。造成原因为:实时刷新时传入了dataList数组,而不是数组对应的value。应该将其从数组中提取出来,然后传入。
  • 尝试给label添加borderWidth,但并没有奏效。

2025-10-18:

  • 发现删除formatter函数之后,不再出现bug。此时axisPointer.label仅输出x轴对应值。
  • 发现配置axisPointer.label.width后,背景区域宽度为固定值,表明未配置时,该宽度与文字长度相关。因此bug可能是首次点击时宽度计算错误导致的。
  • 【修复】 通过删除WXML内的force-use-old-canvas="true"语句,使用新渲染模式,修复了label宽度 异常的问题。
  • 【更新】 修改了图表部分样式。包括图表位置。
  • 【更新】 修改随机数生成逻辑,限定在[7,7.2]范围内。
  • 【更新】 绑定图表数据与传感器卡片数据。下拉菜单切换选项浏览历史数据时,实时数据依然继续更新。
  • 【更新】 在开发者工具添加了注释模板。

2025-10-19:

  • 【需求】 ~~新增2个初始化函数,用于初始化溶解氧和水温的图表。~~不新增初始化函数,仅新增图表数据类,抬高图表高度,让所有数据同时显示在一个图内**(√)**
  • 【需求】 ~~新增2个额外的下拉菜单。~~三个图表公用一个下拉菜单。(√)
  • 【需求】 修改formatter函数以及配套初始化内容,以及数据刷新机制,以适应三组数据同时显示。(√)
  • 【需求】 开发通用性的onOptionChange函数,以适配不同下拉菜单的变化。或针对原有的函数进行修改,适配多个下拉菜单的处理。
  • 【需求】onLoad内的函数移出,独立封装。
  • 【需求】 修改存储数据的格式,适应三种传感器数据同时存储和操作。同时避免过于复杂的逻辑。考虑用一个二维数组来存储。记住顺序即可。(√)
  • 【需求】 设计连接状态标识、增氧设备启停状态标志或文字。(√)
  • 【需求】 新增使用说明按钮,打开即可转到说明书页面
  • 【需求】 或许计划新增配置菜单,打开即可配置当前链接的设备以及热点名。
  • 【更新】 修改了图表和下拉菜单的部分样式。
  • 【更新】 修改了传感器卡片的文字尺寸,添加了单位显示。
  • 【更新】 新增页面渐变背景色。

2025-10-20:

  • 【更新】 更改了随机数刷新机制,使之生成的模拟数据为1个包含三个数据的数组
  • 【更新】 更改了updateSensorData函数,集成了setOption功能,使之能直接处理数组,添加了部分参数,用于判断是否需要刷新图表,以及是否为实时数据更新。
  • 【BUG】 在浏览仅有单值的历史数据时,会出现异常。会出现一条不存在的曲线,横坐标会变为纯数值。

2025-10-21:

  • BUG的原因为,未修改逻辑时,存入的历史数据是字符串类型。

    1
    2
    3
    4
    5
    const valuesMap = {
    PH: value.map(v => v[0]),
    DO: value.map(v => v[1]),
    WT: value.map(v => v[2])
    };

    上面的代码仅针对数值类型,字符串只会提取第一个字符。

  • 【更新】 加入toNumber递归函数,实现字符串转为数字的功能,实现对旧数据的兼容,并保留原数组结构不变。

    1
    2
    3
    4
    5
    6
    toNumbers(value) {
    if (Array.isArray(value)) {
    return value.map(v => this.toNumbers(v));
    }
    return Number(value);
    },
  • 【更新】 通过判断是否为嵌套数组,并在非嵌套时在外围包裹[],实现对旧的一维数据的兼容。

    1
    2
    3
    if(!Array.isArray(value[0])) {
    value = [value];
    };
  • 【更新】 updateSensorData函数内新增isSetOption参数,用于确认是否将数据传入图表。重绘图表、传入数据、刷新实时数值单独控制,避免冲突。可实现浏览历史数据是,继续刷新最新数据而不更新图表。

  • 【更新】 将坐标轴指示器axisPointer替换为tooltip。对三维数据的展示效果更佳。

  • 【BUG】 tooltip在真机情况下,文字会出现异常的描边/阴影。视觉效果差。

  • 【BUG】 formatter函数删除后,会导致模拟器在新渲染模式下也出现边框异常宽大的情况。

2025-10-22:

  • 【修复】 通过查找微信开放社区。在tooltip.textStyle下添加以下样式:

    1
    2
    3
    4
    textStyle: {
    textShadowBlur: 10,
    textShadowColor: 'transparent'
    }

    修复了tooltip文字异常阴影的问题。在开放社区的开发者指出,必须配置textShadowBlur且不为0才可以 让textShadowColor生效。

    参考:微信开放社区

  • 【修复】 修正了在仅有一维数组传入的情况下,无法正常绘制图表的问题。

    但历史数据的问题依旧存在,表现为:当DOWT均输入了``undefined时,可以正常显示,由于后续时间轴上对应的内容仅PH有值,其余为空,也没有未定义值,所以不能刷新出来。考虑到现在即使传入数组仅有1个值,而不是预期的三个值,也可以默认返回undefined`值,故不打算对旧数据做适配。

  • 【需求】 调整tooltip。formatter,或通过其他方式,给tooltip的数据添加单位。

2025-10-23:

  • 【更新】 重写了formatter函数,为tooltip添加了自动显示单位的功能。对params的结构有了更好的理解。

  • 【更新】 针对传值时可能的数据缺失或者undefined的情况做了适配,当读取undefined值时,自动替换为 ‘-’ 。下面是一种很笨的写法,但是自己写的,能用。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    formatter: (params) => {
    var text = '时间:' + params[0].name;
    for (var i = 0, length = params.length; i < length; i++) {
    if (params[i].seriesName === 'DO') {
    var unit = ' mg/L';
    var seriesName = '溶解氧'
    } else if (params[i].seriesName === 'WT') {
    var unit = ' ℃';
    var seriesName = '水温'
    } else {
    var unit = '';
    var seriesName = 'PH'
    };
    text += '\n' + params[i].marker + seriesName + ':' + (params[i].data === undefined ? '--' : params[i].data + unit);
    }
    return text;
    },

2025-10-24:

  • 【更新】 新增了设备状态显示函数。针对不同的状态自动切换不同的颜色和显示文本。
  • 【修改】 以增大圆角为主,修改了页面部分组件的样式。

2025-10-25:

  • 【更新】 重做了传感器数据卡片布局。初步了解了伪元素的用法。

    此处代码实现的样式为:在每两个传感器数据卡片之间绘制1条线。实现分割。效果和代码如图。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    .sensorData-card:not(:last-child)::after {
    content: "";
    position: absolute;
    right: 0;
    top: 0%;
    bottom: 0%;
    width: 1px;
    background-color: rgb(233, 233, 233);
    }

    需要注意的是,在这里,父级元素应该将position设置为relative。否则这个伪元素无法定位。

    伪元素可以理解为被 CSS 虚拟创建出来的元素。它存在于渲染树中(浏览器的视觉层),但不出现在 HTML DOM 树里。它可以帮你“凭空生成”视觉或结构性元素,而不需要去动 DOM。它必须有 content 属性,即使是空的。

    同时还复习了justify-contentalign-items的区别。前者控制主轴方向的对齐方式,后者控制交叉轴方向的。

  • 【修改】 将开关全部替换为按钮,并调整自定义样式。但还未写相关功能。

  • 【更新】 在主页新增“广告位招租”图片。

  • 【更新】 新增了2个选项卡,但还未写跳转功能。

  • 【修改】 调整了页面的部分样式。

2025-10-26:

  • 【更新】 添加了一个新的页面,用来做配置项页面使用。同时实现了页面的跳转功能。

    同时复习了currentTarget.dataset的用法。也就是:dataset 是一个对象,包含了该元素上所有以 data- 开头的自定义属性。用点可以引出包含的属性。以下面代码为例:

    1
    <van-field value="{{ item.value }}" label="{{item.label}}" data-key="{{item.name}}" bind:change="onDataInput"/>

    js内,currentTarget.dataset.key就可以取到data-key的值了。

  • 【更新】 在G老师的指导下,学会用hover-class并实现了选项卡点下时变为灰色的效果。

    hover-class是小程序视图容器原生自带的属性,用来指定按下去的样式类。实现点击态效果。

  • 【更新】 添加了onDataInput函数,将用户输入的值自动存入对应变量。复习了用$拼接字串的办法。

  • 【更新】 添加了onButtonPressed函数,点击保存按钮时,将自动判断用户是否填写全部配置项,如未完全填写将不保存至缓存,并在退出页面后销毁。

    以下面的代码为例,学习了sometrim的使用,复习了箭头函数和map的使用。

    1
    2
    const data = this.data.deviceInfo.map(item => [item.name, item.value]);
    const isEmpty = data.some(item => !item[1].trim());

    trim()字符串方法,用于去掉首尾的空格(包括空格、换行、制表符)。

    some()数组方法,用于判断:数组中是否存在至少一个元素满足条件。

    那么上面的代码逻辑就是:把data里的每个数组的第二个元素取出来,去除空格,然后取非,如果是空的,就返回true

  • 【更新】 调整了该页面的部分样式。

  • 【计划】 计划明天完成用户配置数据保存和读取功能的部分。并新建一个页面,用来写项目说明。

2025-10-27:

  • 【更新】 初步写完了用户配置储存功能。并能自动识别用户是否完成全部配置。根据保存成功与否,会弹出不同的自定义popup

    在过程中学习了几个问题。

    1. 过程中我分别用了以下两个语句。巧妙地用findfindIndex方法来实现对索引和对应结构体的查找。

      1
      2
      const popup = this.data.popup.find(item => item.name === currentPopup);
      const index = this.data.deviceInfo.findIndex(item => item.name === key);
    2. const声明的变量是不可改变的。是只读的,如果在循环中用const声明变量,后面通过++实现自增,这就是在改变变量,会报错。建议用let

    3. 这是一种比较稳妥的写法:

      1
      2
      3
      4
      5
      const deviceInfo = this.data.deviceInfo;
      for (let i = 0; i < res.data.length; i++) {
      deviceInfo[i].value = res.data[i];
      }
      this.setData({deviceInfo: deviceInfo});

      我需要给data下的deviceInfo赋值。我先把这个复制一份到函数内,然后操作这个新的变量,可以避免在重新赋值的时候出现未定义的错误。

  • 【计划】 初步设计配网的逻辑流程和界面样式。

2025-10-28:

  • 认真思考了配网机制。计划采用最简单的AP配网。因为微信限制比较多,不能直接访问局域网IP,所以配网无法在小程序上实现,仅在手机浏览器网页操作。设备启动后,如果长时间未连上网,将自动进入配网模式,此时用手机连接指定SSID的WIFI,然后扫二维码或者直接输入,进入指定网页,就可以输入路由器的SSID和PWD,实现配网。

    那么小程序上需要保留的配置项只有UID(公用的,设备厂商的,实际上是我的)和设备编号(在云平台上指定,不同设备唯一的)。这两者都是MQTT协议用的。订阅需要提供用户ID和指定的topic。

  • 计划先完善小程序本体功能。主要包含传感器数据读取,MQTT通信,以及指令发送的部分。

  • 【更新】 配置页新增’清空‘按钮,用来清除已保存的数据,下次进入时配置项将为空。但此次显示内容并不清空,所以此处逻辑有待优化。

  • 【更新】 针对新增按钮,新增了弹出层样式。改写了按钮按下时绑定函数的处理逻辑。能够根据按下的不同按钮弹出不同的popup。

  • 【更新】 通过page-meta,禁止配置页滚动。

2025-10-29:

  • 初步了解了一下ESP8266的异步WebServer库。
  • 【更新】 通过在点击清空按钮后setData将空数组传入,实现了在点击清空按钮后,视图层也能同步更新清空效果。
  • 【更新】 新增aquacultureIntro页面,用来放项目介绍相关内容。值得注意的是,把页面的最外层元素设置为scroll-view似乎比较合适一些。它不会导致内层元素宽度异常的问题。但是内层元素(例如卡片)需要单独设置宽度。
  • 【更新】 主页新增wiper,能够滚动展示图片。
  • 【计划】 完成MQTT连接和订阅部分内容,实现根据用户配置的UID和设备编号,来进行数据的接受和指令发送的功能。然后就算是完工了。

2025-10-30:

  • 【更新】 借助巴法云示例代码,成功让小程序以MQTT协议连接至服务器并更新状态。但是仍然有一些BUG。例如未配置UID,仍然跳转‘已连接’状态。

    另外,考虑应当设计可关闭的状态,故计划将按钮替换为switch开关。

    此外,还需要认真读一下mqtt.min.js或学习其功能,实现对该模块的灵活应用,以及对状态显示的更好的适配。

  • 【更新】 在页面内加入popup,实现进入页面和点击连接按钮时,对未配置的提示。

  • 【计划】 进一步调试MQTT连接,修复存在的BUG。然后实现云平台推送消息的接收和更新。

2025-10-31:

  • 今天给买来的IR摄像头画了2块PCB板子,尝试了一些创新设计,即在板子正反面阻焊层上印图标,这样可以实现透光效果;通过实践,还学会了一些精准控制孔位的办法,以及针对元件封装的编辑(如不显示丝印和3D模型),所以今天并没有更新小程序。
  • 值得一提:嘉立创不更新最新版本,可能出现无法使用白嫖券的情况。

处理过程:

2025.09.22 - 09.28:组件库样式覆盖与渲染冲突

  • 遇到问题:引入 Vant 组件库后,遇到按钮固定高度无法修改、带圆角的按钮边框缺角、下拉菜单背景色无法覆盖,以及真机测试时部分样式失效的问题。

  • 如何解决

    1. 通过源码查找 CSS 变量(如 --dropdown-menu-background-color),在 wxss 或 filter 中覆盖为 transparent。
    2. 通过设置 display: block; 撑开元素,并将溢出属性设置为 overflow: auto。
    3. 引入组件的 transition 补丁解决交互失效。
  • 解决原理:CSS 盒模型中,当内容超出盒子边界时,默认的 visible 属性会导致圆角边框被内容遮盖(看起来像缺角),修改 overflow 可以改变裁剪规则;真机与模拟器存在渲染引擎差异,需要对齐环境依赖。

  • 总结复用:在使用第三方 UI 库时,不要硬写 CSS 强行覆盖,优先查阅源码暴露的 CSS 变量。overflow: auto 是修复边框与背景渲染冲突的通用“偏方”。

    PS:后续研究发现,这个问题实际上也是skyline渲染导致的。所以这些方式是否有效也有待考证(应该是没用)。一般还是用WebView就好。

2025.09.25 - 10.13:数据流控制与作用域踩坑

  • 遇到问题
    1. 历史数据与实时数据渲染冲突,导致页面卡死。
    2. this.chart报未定义错误。
    3. 页面高度写死导致滚动条异常,下拉菜单展开时背景页面跟着滚动。
  • 如何解决
    1. 引入布尔标志位(flag),将定时器轮询(实时)与一次性读取缓存(历史)逻辑分离。
    2. 将普通的回调函数替换为 ES6 的箭头函数 =>。
    3. 利用 wx.getWindowInfo() 动态计算状态栏和导航栏高度;利用 <page-meta> 结合下拉菜单的开关状态动态控制 overflow:hidden
  • 解决原理
    1. 作用域链:普通函数由调用者决定 this,回调中常丢失挂载页面的 this;而箭头函数没有自己的 this,会向外层寻找,完美继承 Page 实例。
    2. DOM 控制:position: fixed 只能固定元素自己,无法阻止事件穿透引发的页面滚动,必须在最外层(meta 或 body)切断滚动轴。
  • 总结复用:标志位切换状态、箭头函数解决 this 指向、page-meta 锁定滚动,这三套组合拳是前端交互开发的标准范式,几乎在所有页面开发中都能无缝复用。

2025.10.14 - 10.23:微信缓存机制与 ECharts 深度调优

  • 遇到问题
    1. 历史数据被覆盖,每天只能查看到最新的少量数据。
    2. ECharts 在真机出现图表缩小、坐标指示器(label/tooltip)宽度异常宽大、文字带奇怪阴影、老数据格式不兼容导致报错。
  • 如何解决
    1. 修改缓存逻辑:先读旧数据,用 [...oldData, ...newData] 拼接后再存入同一 key。
    2. ECharts Bug修复:降级渲染模式(移除 Skyline,改用 WebView);写一个彻底清空图表并重绘的 redrawEchart 函数;为 tooltip 增加单位的 formatter 函数;加入 textShadowColor: 'transparent' 修复真机阴影。
    3. 编写 toNumbers 递归函数,把老格式的一维字符串强转为兼容的新二维数字数组。
  • 解决原理
    1. 微信 Storage 是键值对覆盖模式,不支持“增量追加”,必须全量读取、合并、全量覆写。
    2. 图表实例(Canvas)有状态记忆,通过 .clear() 释放内存和旧配置能消灭 90% 的玄学 Bug。
    3. 真机的 UI 渲染底层对默认属性的支持不如模拟器完善,需要把阴影、边框等属性显式声明为0或透明。
  • 总结复用
    1. 针对本地存储,“先读-合并-再写” 是万能套路。
    2. 对于复杂数据清洗,递归+高阶函数(map/some/find) 的组合极为高效。
    3. 当模拟器正常、真机 UI 错乱时,第一时间考虑降级渲染引擎(WebView)或补全 CSS 默认值。

2025.10.24 - 10.29:界面重构与用户配置态管理

  • 遇到问题:卡片之间需要分割线但不想增加无用的 HTML 节点;用户填写配置时可能漏填,需要进行校验;需要跳转网页进行硬件 AP 配网。
  • 如何解决
    1. 使用 CSS 伪元素 ::after 配合绝对定位画出分割线。
    2. 使用 data.some(item => !item[1].trim()) 一行代码校验数组内是否存在空值,并配合自定义 popup 提示。
    3. 把网络配置下放到 ESP8266 的硬件 AP 模式,小程序只保留 UID 和设备号配置。
  • 解决原理
    1. 伪元素直接在浏览器的渲染树生成,不污染 DOM 树,且性能更好。
    2. trim() 去掉误触的空格,some() 只要遇到一个 true 就会短路返回,是做表单非空校验的最佳实践。
  • 总结复用:学会了利用伪元素做 UI 修饰(分割线/小图标),以及 trim+some 的优雅表单校验法。这也让我意识到“全栈思维”的重要性——软件难以解决的局域网限制,交给硬件自己开热点解决,架构设计大过死磕代码。

2025.10.30 - 10.31:MQTT联调与拓展探索

  • 遇到问题:未配置 UID 依然显示“已连接”;软硬件联调存在状态同步 BUG。
  • 如何解决:增加了强校验和 Popup 阻断未配置情况下的连接请求。同时抽空利用立创 EDA 设计了带透光效果的 PCB。
  • 总结复用:联调阶段最容易出现“假成功”的状态,前端的容错和边界情况(如未登录、空配置、断网)处理,比主逻辑的实现更考验产品的鲁棒性。

结论总结:

近期我尝试开发了一款物联网监测小程序,初步完成了 Vant 界面构建、ECharts 数据可视化、本地缓存管理及 MQTT 硬件通信。

我积累了一些基础知识:尝试应用了 CSS 盒模型与伪元素;尝试熟悉了 ES6 核心语法(如箭头函数与扩展运算符)来优化数据流;同时也摸索出了应对真机渲染差异与 Storage 缓存合并的实战套路。