Skip to content

房屋管理(三) ​

本页对应 房屋管理,共 7 段视频,约 1 小时 8 分。

学习目标 ​

  • 能够调用图片上传接口完成房屋图片上传,并根据 type 将返回的图片地址分别写入身份证正面或反面数据,实现图片预览。
  • 能够完成房屋表单提交:先校验表单,剔除多余字段,以 POST 方式提交数据,并使用 wx.navigateBack 按指定层数返回房屋列表页。
  • 能够在 onShow 生命周期中获取房屋列表,渲染小区、楼栋、房间号、业主姓名、审核状态和空列表提示,并根据列表长度控制“添加房屋”按钮的显示。
  • 能够通过地址参数传递房屋 ID,在详情页 onLoad 中接收 ID,调用详情接口并渲染房屋详情及审核状态。
  • 能够使用 wd-swipe-cell 与 wd-dialog 实现侧滑删除交互,通过 mark 传递房屋 ID 和索引,并依据 close 事件中的 confirm/cancel 判断用户操作。
  • 能够调用删除房屋接口,将 ID 和索引挂载到页面实例以便共享,并在删除成功后使用 splice 同步本地列表数据、重新渲染列表与空状态。

知识点 ​

先明确这一节要做什么 ​

房屋列表页要完成三件事:一是只有登录用户才能看到自己的房屋;二是从接口取出房屋数组并渲染,同时处理“一条都没有”的空状态;三是对审核状态做区分展示,并在房屋达到 5 套后隐藏“添加新房屋”的入口。

为什么需要这一节?因为上一节只是把表单数据提交成功,用户点“提交审核”后通过 wx.navigateBack 回到了列表页。如果列表页不主动去拉数据,页面上仍然是旧内容,用户会以为“我明明添加成功了,怎么没显示”。所以列表页的职责不是“记住”数据,而是每次露面都去问一次服务器。

为什么必须在 onShow 里取列表 ​

怎么想:这里最容易踩的坑是写成 onLoad。onLoad 只在页面第一次创建时执行一次;而添加房屋是“列表页 → 选择小区 → 选择楼栋 → 选择房间号 → 填写表单 → 提交”,提交成功后用 wx.navigateBack 往回退,退回来的列表页早就存在了,只是重新显示出来,此时触发的是 onShow,onLoad 不会再跑。

怎么做:把取数据的方法放在 onShow 里调用。

易错点:写成 onLoad 后,第一次进页面有数据,添加完返回却看不到新房屋,很多人会误以为是接口没返回,其实是生命周期选错了。

用 authorization 组件守住登录状态 ​

为什么需要:接口文档明确要求请求时携带用户登录状态,未登录时后端不会返回数据,前端也不该把空列表当成“你没有房屋”展示给用户。

怎么做:项目里 authorization 已经全局注册,直接把列表主体包进去即可;未登录时组件会自动跳转登录页。注意只包裹列表这一块,底部的弹层不需要包。

易错点:包错范围,或者忘了包,导致未登录也能看到页面骨架。

封装 getHouseList:调接口、判 code、渲染 ​

怎么想:请求用 GET,路径 /room,不需要额外参数。返回结构是 { code, data },data 就是房屋数组,用解构重命名成 houseList 更好读。判断成功依旧看 code === 10000,不成功直接 return 并 toast 提示,避免拿脏数据去渲染。

怎么做:

javascript
Page({
  data: {
    houseList: [],
    isEmpty: false,
    dialogVisible: false,
  },
  onShow() {
    // 获取用房屋列表
    this.getHouseList()
  },
  // 用户房屋列表
  async getHouseList() {
    // 调用接口
    const { code, data: houseList } = await wx.http.get('/room')
    // 检测接口是否调用成功
    if (code !== 10000) return wx.utils.toast()
    // 渲染数据
    this.setData({
      houseList,
      isEmpty: houseList.length === 0,
    })
  },
	// ...
})

易错点:setData 里顺手把 isEmpty 一起算好,别只更新列表,否则空状态永远不出现。

列表为空时给用户一个出口 ​

为什么需要:新用户一条房屋都没有,页面会是一片空白,必须给一句提示和一个“去添加”的链接。

怎么做:用 wx:if="{{!isEmpty}}" 控制列表区,wx:else 走空状态,链接指向 /house_pkg/pages/locate/index。

渲染房屋信息与审核状态 ​

怎么做:wx:for 遍历 houseList,wx:key="id"(接口返回里带 id)。小区名取 point,房间号由 building 和 room 拼起来,业主取 name。审核状态用 status 判断:1 正在审核(info)、2 审核通过(success)、3 审核失败(fail)。

xml
<authorization>
  <block wx:if="{{!isEmpty}}">
    <scroll-view show-scrollbar="{{false}}" enhanced scroll-y>
      <view class="houses">
        <view class="houses-title">房屋信息</view>
        <view class="houses-list">
          <van-swipe-cell
            wx:for="{{houseList}}"
            wx:key="id"
            mark:index="{{index}}"
            mark:id="{{item.id}}"
            async-close
            bind:close="swipeClose"
            right-width="{{70}}"
          >
            <van-cell-group mark:id="{{item.id}}" bind:tap="goDetail" border="{{false}}">
              <van-cell size="large" title="{{item.point}}">
                <text wx:if="{{item.status === 1}}" class="tag info">正在审核</text>
                <text wx:if="{{item.status === 2}}" class="tag success">审核通过</text>
                <text wx:if="{{item.status === 3}}" class="tag fail">审核失败</text>
              </van-cell>
              <van-cell
                title="房间号"
                border="{{false}}"
                value="{{item.building}}{{item.room}}"
              />
              <van-cell title="业主" border="{{false}}" value="{{item.name}}" />
            </van-cell-group>
            <view slot="right">删除</view>
          </van-swipe-cell>
        </view>
      </view>
    </scroll-view>
    <view wx:if="{{houseList.length < 5}}" class="toolbar" bind:tap="addHouse">
      <text class="enjoy-icon icon-add"></text>
      <text class="button-text">添加新房屋</text>
    </view>
  </block>
  <view wx:else class="blank">
    您还没有认证房屋,请点击
    <navigator hover-class="none" class="link" url="/house_pkg/pages/locate/index">添加</navigator>
  </view>
</authorization>

易错点:状态类名写错(比如把失败写成别的名字),页面上文字对了但没有背景色,看起来像“没生效”,实际是类名拼错,对照 app.wxss 里预置的 success / info / fail 检查。

限制最多添加 5 套房屋 ​

为什么需要:业务上每户最多认证 5 套。怎么做:不隐藏整个按钮区,只给按钮加 wx:if="&#123;&#123;houseList.length < 5&#125;&#125;",到 5 条(或更多)时按钮消失,用户自然加不了。

调试审核状态 ​

新添加的房屋状态都是 1,页面只会显示“正在审核”,另外两种样式验证不到。转写稿里后端提供了一个专门用于调试的接口:参数 type=room、id(对应某条房屋的 id)、status(改成 2 或 3),发一次请求后刷新列表页,就能看到“审核通过 / 审核失败”的样式是否正常。注意这只是调试手段,真实项目里状态应由管理后台修改。

易错点小结 ​

生命周期用 onShow 不用 onLoad;isEmpty 跟着列表一起 setData;wx:key 用 id;状态判断用 === 并配对上类名;按钮的显隐条件写成 length < 5 而不是 <= 5。

1. 列表跳详情:先把房屋 ID 带过去 ​

为什么需要:详情页要展示“某一套房屋”的信息,而详情接口必须根据房屋 ID 查询;列表页本身已经拿到了每条房屋数据的 id,所以跳转时要把这个 id 传给详情页。

怎么想:详情页不是凭空知道用户点了哪套房,最直接的方式是把 id 拼在 URL 查询参数后面。转写稿里特别说明:列表页这里没有用 navigator 组件,而是在点击事件里通过 mark 把 id 传给事件回调,再从事件对象里取出 id。

怎么做:在列表页的点击回调中调用 wx.navigateTo,把 ev.mark.id 拼到详情页地址后面:

javascript
// house_pkg/pages/list/index.js
Page({
  // ...
  goDetail(ev) {
    // 通过 mark:id 来传递数据
    wx.navigateTo({
      url: '/house_pkg/pages/detail/index?id=' + ev.mark.id,
    })
  },
})

易错点:mark:id 要和事件对象里的 ev.mark.id 对应;不要漏掉 ?id=;详情页路径要写完整。转写稿中也是先确认地址里已经带上了参数,再进入详情页接收。

2. 在 onLoad 中接收 ID,并封装详情请求 ​

为什么需要:详情页加载时必须先拿到列表页传过来的 id,否则接口不知道查哪套房。接口要求以 GET 方式请求,并把 ID 拼在 /room/ 后面。

怎么想:onLoad 可以直接接收页面参数,所以用 onLoad({ id }) 解构出 id,再调用封装好的 getHouseDetail(id)。请求前先判断 id 是否存在;请求后根据 code 判断是否成功,成功后再 setData 渲染。

怎么做:

javascript
// house_pkg/pages/detail/index.js
Page({
  data: {
    houseDetail: {}
  },
  onLoad({id}) {
    // 获取房屋详情
    this.getHouseDetail(id)
  },
  // 房屋详情接口
  async getHouseDetail(id) {
    if (!id) return wx.utils.toast('参数有误!')
    // 调用接口
    const { code, data: houseDetail } = await wx.http.get('/room/' + id)
    // 检测接口是否调用成功
    if (code !== 10000) return wx.utils.toast()
    // 渲染数据
    this.setData({ houseDetail })
  },
})

易错点:houseDetail 最好初始化为空对象,避免页面首次渲染取字段报错;接口路径是 '/room/' + id,不要把 ID 放错位置;判断成功看 code === 10000,失败要 return,不要继续渲染。转写稿中也是先校验 ID,再调接口,再根据 code 决定是否 setData。

3. 把详情数据渲染到页面:字段要一一对应 ​

为什么需要:接口返回的是完整房屋详情,页面要展示小区、房间号、业主、手机号、身份证正反面照片等信息。

怎么想:按接口字段对号入座:point 是小区名,房间号由 building 和 room 拼起来,业主是 name,手机号是 mobile,身份证照片是 idcardFrontUrl 和 idcardBackUrl。状态单独用 status 判断。

怎么做:保留笔记中的页面结构:

xml
<scroll-view scroll-y enhanced show-scrollbar="{{false}}">
  <view class="house-detail">
    <van-cell-group border="{{false}}" title="房屋信息">
      <van-cell title="{{houseDetail.point}}">
        <text wx:if="{{houseDetail.status === 1}}" class="tag info">正在审核</text>
        <text wx:if="{{houseDetail.status === 2}}" class="tag success">审核通过</text>
        <text wx:if="{{houseDetail.status === 3}}" class="tag fail">审核失败</text>
      </van-cell>
    </van-cell-group>
    <van-cell-group title="业主信息" border="{{false}}">
      <van-cell
        title-width="200rpx"
        title="房间号"
        value="{{houseDetail.building}}{{houseDetail.room}}"
      />
      <van-cell title-width="200rpx" title="业主" value="{{houseDetail.name}}" />
      <van-cell
        title-width="200rpx"
        border="{{false}}"
        title="手机号"
        value="{{houseDetail.mobile}}"
      />
    </van-cell-group>
    <view class="id-card">
      <van-cell title="本人身份证照片" />
      <view class="id-card-front">
        <image src="{{houseDetail.idcardFrontUrl}}" />
      </view>
      <view class="id-card-back">
        <image src="{{houseDetail.idcardBackUrl}}" />
      </view>
    </view>
  </view>
</scroll-view>
<view class="toolbar" bind:tap="editHouse">
  <text class="enjoy-icon icon-edit"></text>
  <text class="button-text">修改房屋信息</text>
</view>

易错点:字段名大小写要准确,尤其是 idcardFrontUrl、idcardBackUrl;图片地址为空时页面可能没有内容,这是数据问题不是结构问题;房间号是两个字段拼接,不要只写一个。

4. 审核状态:用 status 控制文案和类名 ​

为什么需要:房屋有 3 种状态,页面要显示不同文案和不同颜色样式。

怎么想:接口返回 status:1 正在审核,类名 info;2 审核通过,类名 success;3 审核失败,类名 fail。用 wx:if 分别渲染即可。

怎么做:上面 XML 中已经按 status === 1/2/3 写了三个 text。转写稿中还提到可以用调试接口辅助修改状态:传 type=room、房屋 id、status=2 或 3 来验证页面显示。调试时曾发现失败状态类名写错,导致没有背景色,改成 fail 后正常。

易错点:状态值是数字,不要写成字符串比较;失败类名是 fail,不要写成转写稿里口误的 “ar”;调试接口只用于辅助验证,真实项目状态应由管理后台修改。

编辑房屋的目标与共用页面思路 ​

为什么需要: 编辑房屋不是重新添加一套数据,而是对原有房屋信息做修改。用户需要先看到原来的小区、楼栋、房间号等内容,再在表单中改完后提交,否则用户只能“盲改”,体验和正确性都无法保证。

怎么想: 添加房屋和编辑房屋的表单字段基本一致,因此不需要单独写两个页面。可以共用一个表单页,通过地址参数中是否带有 id 来区分:有 id 是编辑,没有 id 是添加。提交接口也可以共用,后端根据参数中是否包含 id 判断是新增还是修改。

怎么做: 在 onLoad 中接收 point、building、room、id。如果存在 id,调用详情接口获取原数据;否则把地址参数直接渲染到表单中。

易错点: 不要一进入页面就无条件请求详情;要先判断 id。添加和编辑共用页面时,不要把“添加”的默认渲染逻辑和“编辑”的详情回填逻辑混在一起。

通过 id 判断添加还是编辑 ​

为什么需要: 同一个页面、同一个提交接口要承担两种业务,必须有一个稳定标识让前端和后端都知道当前操作类型。

怎么想: 地址中带 id,说明操作对象已经存在,是编辑;不带 id,说明还没有保存到服务端,是添加。转写稿中也明确提到:添加编辑房屋接口共用,id 允许为空表示添加,编辑时 id 必须传递。

怎么做: 在 onLoad 中先解构参数,再判断:

javascript
// house_pkg/pages/form/index.js
Page({
  // ...
  onLoad({ point, building, room, id }) {
    // 根据 id 判断是否为修改房屋
    if (id) return this.getHouseDetail(id)
    // 添加房屋 (渲染数据)
    this.setData({ point, building, room })
  },
  // 房屋详情接口
  async getHouseDetail(id) {
    // 调用接口
    const { code, data: houseDetail } = await wx.http.get('/room/' + id)
    // 检测接口是否调用成功
    if (code !== 10000) return wx.utils.toast()
    // 渲染数据
    this.setData({ ...houseDetail })
  },
})

易错点: id 是地址参数,不要写成页面内固定值;详情接口路径要把 id 拼到 /room/ 后面,转写稿中特别提醒“ID 是拼凑到地址后面的,不要写错”。

获取房屋详情并回填表单 ​

为什么需要: 编辑房屋必须基于原数据修改。如果不回填,用户看到的是空表单,提交后可能覆盖掉原有信息。

怎么想: 先根据 id 调详情接口,拿到服务端返回的 houseDetail,再用 setData 展开回填。接口返回中通常包含 code 和 data,先判断 code === 10000 再渲染。

怎么做: 使用 wx.http.get('/room/' + id),解构出 code 和 data: houseDetail;失败时 toast,成功时 this.setData({ ...houseDetail })。这样表单中的小区、楼栋、房间号、姓名、手机号、身份证照片等字段都能回填。

易错点: 不要把整个返回结果直接 setData 到页面根数据里,应取 data;详情接口失败时不要继续渲染;字段名要按接口返回写,例如 point、building、room、name、mobile、status 等,避免同音字或拼写错误。

提交修改并剔除 status ​

为什么需要: 用户修改的是房屋基础信息,房屋审核状态不应由用户在表单里随意改。转写稿和笔记都强调:修改房屋信息时房屋状态不允许修改,因此提交前要把 status 从数据中剔除。

怎么想: 提交前先做表单校验;校验通过后取 this.data,去掉可能多余的 __webviewId__,再去掉 status,剩余字段通过 post /room 提交。后端根据是否带 id 判断新增或修改。

怎么做:

javascript
Page({
  onLoad() {
    // ...
  },
  getHouseDetail(id) {
    // ...
  },
  // 提交审核
  async submitForm() {
    // 验证数据
    if (!this.validate()) return
    // 获取全部的数据(剔除可能多余参数 __webviewId__)
    const { __webviewId__, status, ...data } = this.data
    // 调用接口
    const { code } = await wx.http.post('/room', data)
    // 检测接口是否调用成功
    if (code !== 10000) return wx.utils.toast('提交数据失败!')

    // 返回房屋列表页面
    wx.navigateBack({ delta: 4 })
  },
})

易错点: 不要只剔除 status 而忘记 __webviewId__;转写稿提到旧版开发者工具可能带这个多余参数,新版可能不带,但解构剔除更稳妥。提交后要判断 code,不能默认成功。示例中 navigateBack({ delta: 4 }) 是按添加房屋流程的页面层级返回列表;如果编辑入口层级不同,应按实际页面栈调整,不能机械照抄。接口文档地址保留如下:接口文档地址。

调试与易错点 ​

为什么需要: 编辑房屋涉及“详情回填”和“提交修改”两个接口,任何一个环节参数不对,页面看起来可能正常,但数据没有真正更新。

怎么想: 调试时先看详情请求是否带对 id,再看返回数据是否回填;提交时看 Network 中请求参数是否包含 id、是否误带 status,最后看 code 是否为 10000。

怎么做: 先进入编辑页,确认地址参数有 id;在 getHouseDetail 中检查 /room/ + id;提交时检查解构后的 data;成功后按页面栈返回列表并刷新列表数据。转写稿中通过 Network 观察 code 判断成功,这是很实用的调试方式。

易错点: 同音字和拼写要纠正:authorization、setData、onLoad、navigateBack、delta、status、__webviewId__ 不要写错;code !== 10000 时及时 return;编辑时不要修改审核状态;详情失败不要继续回填;提交失败不要跳转。

删除房屋要解决什么问题 ​

删除房屋是根据房屋 ID 完成的数据删除操作,也是房屋管理中的最后一个功能。它看起来只是“点一下删除”,实际包含两层工作:第一层是交互,用户通过侧滑露出删除入口,再通过对话框确认,避免误删;第二层是数据一致性,接口删除数据库中的房屋后,页面里的 houseList 也要同步移除对应项,否则会出现“后端已删、页面还在”的假数据。

所以这一节的思路不是直接发请求,而是先用 Vant 的 van-swipe-cell 和 van-dialog 把交互串起来:侧滑组件负责手势和删除入口,对话框负责二次确认,两个组件都监听 close 事件,在事件里推进下一步逻辑。

用两个 close 事件串起交互 ​

van-swipe-cell 的 close 会在侧滑区域关闭时触发,事件对象里能拿到 position 和 instance。position 表示用户操作的位置,只有 position === 'right' 时才认为用户点了右侧删除区域,这时才显示对话框,并把待删除房屋的 id 和索引保存到页面实例上。保存到 this 的原因是:后面确认删除的方法还要用这两个值,但那时已经不在当前事件回调的作用域里了。

javascript
Page({
  swipeClose(ev) {
    const { position, instance } = ev.detail
    if (position === 'right') {
      // 显示 Dialog 对话框
      this.setData({
        dialogVisible: true,
      })
      // 记录房屋的 id 和索引,在调用接口的时使用
      this.cellId = ev.mark.id
      this.cellIndex = ev.mark.index
      // swiper-cell 滑块关闭
      instance.close()
    }
  },
  dialogClose(ev) {
    // 当用户点了确认按钮时调用方法删除数据
    if (ev.detail === 'confirm') this.deleteHouse()
  },
})

这里的关键是 mark:id 和 mark:index。模板中每条房屋记录都要把当前房屋的 ID 和索引通过 mark 传进事件回调,否则 ev.mark.id、ev.mark.index 就是空的。转写稿里也强调过:用户点的是哪一条房屋的删除,必须靠 mark 区分,不能靠猜。instance.close() 的作用是把侧滑滑块恢复关闭状态,否则对话框弹出后侧滑区域可能还开着,交互会显得很乱。

确认后调用删除接口 ​

van-dialog 的 close 事件会在对话框关闭时触发。用户点“确认”时,ev.detail 的值是 'confirm';点“取消”时是 'cancel'。因此不能只看对话框关闭就删除,必须判断 ev.detail === 'confirm',只有确认才调用 deleteHouse()。这是防误删的核心。

javascript
Page({
  swipeClose() {
    // ...
  },
  dialogClose() {
    // ...
  },
  // 删除房屋
  async deleteHouse() {
    // 检测id是否存在
    if (!this.cellId) return wx.utils.toast('参数有误!')
    // 调用接口
    const { code } = await wx.http.delete('/room/' + this.cellId)
    // 检测接口是否调用成功
    if (code !== 10000) return wx.utils.toast()
    // 从 AppData 中将删除的房屋从数组移除
    this.data.houseList.splice(this.cellIndex, 1)
    // 渲染数据
    this.setData({
      houseList: this.data.houseList,
      isEmpty: this.data.houseList.length === 0,
    })
  },
})

deleteHouse 先检查 this.cellId,防止参数异常时发出无意义请求;然后以 DELETE 方式请求 /room/房屋ID。接口返回 code === 10000 才表示成功。成功后用 splice(this.cellIndex, 1) 从 houseList 中移除一条,再 setData 更新 houseList 和 isEmpty。isEmpty 用来控制列表为空时的提示,删除最后一条后页面就能显示空状态。

调试时可以这样验证:在开发者工具 Network 中观察确认后是否发出 DELETE /room/... 请求;请求成功后页面应少一条房屋;点“取消”时不应发请求。若页面没变化,先查 mark 是否传了、this.cellId 是否有值、code 是否等于 10000、splice 后是否执行了 setData。易错点是:把 dialogClose 的 detail 当成布尔值;position 不是 right 也弹框;忘记 instance.close();只改 this.data 不 setData;以及 mark 没绑定导致删错或删不了。

课件图示 ​

课件第8页:多⼀句没有,少⼀句不⾏,用更短时间,教会更实用的技术! ⾼级软件⼈才培训专家 l

课件第19页:多⼀句没有,少⼀句不⾏,用更短时间,教会更实用的技术! ⾼级软件⼈才培训专家 享

课件第20页:多⼀句没有,少⼀句不⾏,用更短时间,教会更实用的技术! ⾼级软件⼈才培训专家 享

课件第17页:多⼀句没有,少⼀句不⾏,用更短时间,教会更实用的技术! ⾼级软件⼈才培训专家 享

课件第18页:多⼀句没有,少⼀句不⾏,用更短时间,教会更实用的技术! ⾼级软件⼈才培训专家 享

课件第11页:多⼀句没有,少⼀句不⾏,用更短时间,教会更实用的技术! ⾼级软件⼈才培训专家 享

课件第12页:多⼀句没有,少⼀句不⾏,用更短时间,教会更实用的技术! ⾼级软件⼈才培训专家 享

课件第13页:多⼀句没有,少⼀句不⾏,用更短时间,教会更实用的技术! ⾼级软件⼈才培训专家 享

代码示例 ​

示例 1:房屋列表(house_pkg/pages/list/index.js + index.wxml) ​

javascript
// house_pkg/pages/list/index.js
Page({
  data: {
    houseList: [],
    isEmpty: false,
    dialogVisible: false,
  },
  onShow() {
    // 获取用户房屋列表
    this.getHouseList()
  },
  // 用户房屋列表
  async getHouseList() {
    // 调用接口
    const { code, data: houseList } = await wx.http.get('/room')
    // 检测接口是否调用成功
    if (code !== 10000) return wx.utils.toast()
    // 渲染数据
    this.setData({
      houseList,
      isEmpty: houseList.length === 0,
    })
  },
  // 跳转房屋详情
  goDetail(ev) {
    // 通过 mark:id 来传递数据
    wx.navigateTo({
      url: '/house_pkg/pages/detail/index?id=' + ev.mark.id,
    })
  },
})
xml
<!-- house_pkg/pages/list/index.wxml -->
<authorization>
  <block wx:if="{{!isEmpty}}">
    <scroll-view show-scrollbar="{{false}}" enhanced scroll-y>
      <view class="houses">
        <view class="houses-title">房屋信息</view>
        <view class="houses-list">
          <van-swipe-cell
            wx:for="{{houseList}}"
            wx:key="id"
            mark:index="{{index}}"
            mark:id="{{item.id}}"
            async-close
            bind:close="swipeClose"
            right-width="{{70}}"
          >
            <van-cell-group mark:id="{{item.id}}" bind:tap="goDetail" border="{{false}}">
              <van-cell size="large" title="{{item.point}}">
                <text wx:if="{{item.status === 1}}" class="tag info">正在审核</text>
                <text wx:if="{{item.status === 2}}" class="tag success">审核通过</text>
                <text wx:if="{{item.status === 3}}" class="tag fail">审核失败</text>
              </van-cell>
              <van-cell
                title="房间号"
                border="{{false}}"
                value="{{item.building}}{{item.room}}"
              />
              <van-cell title="业主" border="{{false}}" value="{{item.name}}" />
            </van-cell-group>
            <view slot="right">删除</view>
          </van-swipe-cell>
        </view>
      </view>
    </scroll-view>
    <view wx:if="{{houseList.length < 5}}" class="toolbar" bind:tap="addHouse">
      <text class="enjoy-icon icon-add"></text>
      <text class="button-text">添加新房屋</text>
    </view>
  </block>
  <view wx:else class="blank">
    您还没有认证房屋,请点击
    <navigator hover-class="none" class="link" url="/house_pkg/pages/locate/index">添加</navigator>
  </view>
</authorization>

要点解释与易错点:

  • 接口必须在 onShow 中调用:添加房屋成功后是通过 wx.navigateBack 返回列表页的,此时只触发 onShow,不会触发 onLoad,写在 onLoad 里会导致新增的房屋不显示。
  • data: houseList 是解构时重命名,把接口返回的 data 直接命名为 houseList,避免与 this.data 混淆。
  • code !== 10000 要 return 掉,防止接口失败时继续 setData 渲染脏数据。
  • isEmpty 用 houseList.length === 0 计算,配合 wx:if="&#123;&#123;!isEmpty&#125;&#125;" 决定显示列表还是空提示,两者是取反关系,容易写反。
  • 添加按钮的条件是 houseList.length < 5,等于或大于 5 条时按钮隐藏(笔记:列表数据限制最多为 5 条)。
  • 状态值:1 正在审核(info)、2 审核通过(success)、3 审核失败(fail),类名写错会导致没有背景色。
  • 整个列表区包在 <authorization> 中,未登录会自动跳转登录页;弹层部分不需要包。
  • 跳转详情用 mark:id 传值,回调里通过 ev.mark.id 取,不能用 dataset。

示例 2:房屋详情(house_pkg/pages/detail/index.js + index.wxml) ​

javascript
// house_pkg/pages/detail/index.js
Page({
  data: {
    houseDetail: {},
  },
  onLoad({ id }) {
    // 获取房屋详情
    this.getHouseDetail(id)
  },
  // 房屋详情接口
  async getHouseDetail(id) {
    if (!id) return wx.utils.toast('参数有误!')
    // 调用接口(ID 拼在路径后面)
    const { code, data: houseDetail } = await wx.http.get('/room/' + id)
    // 检测接口是否调用成功
    if (code !== 10000) return wx.utils.toast()
    // 渲染数据
    this.setData({ houseDetail })
  },
})
xml
<!-- house_pkg/pages/detail/index.wxml -->
<scroll-view scroll-y enhanced show-scrollbar="{{false}}">
  <view class="house-detail">
    <van-cell-group border="{{false}}" title="房屋信息">
      <van-cell title="{{houseDetail.point}}">
        <text wx:if="{{houseDetail.status === 1}}" class="tag info">正在审核</text>
        <text wx:if="{{houseDetail.status === 2}}" class="tag success">审核通过</text>
        <text wx:if="{{houseDetail.status === 3}}" class="tag fail">审核失败</text>
      </van-cell>
    </van-cell-group>
    <van-cell-group title="业主信息" border="{{false}}">
      <van-cell
        title-width="200rpx"
        title="房间号"
        value="{{houseDetail.building}}{{houseDetail.room}}"
      />
      <van-cell title-width="200rpx" title="业主" value="{{houseDetail.name}}" />
      <van-cell
        title-width="200rpx"
        border="{{false}}"
        title="手机号"
        value="{{houseDetail.mobile}}"
      />
    </van-cell-group>
    <view class="id-card">
      <van-cell title="本人身份证照片" />
      <view class="id-card-front">
        <image src="{{houseDetail.idcardFrontUrl}}" />
      </view>
      <view class="id-card-back">
        <image src="{{houseDetail.idcardBackUrl}}" />
      </view>
    </view>
  </view>
</scroll-view>
<view class="toolbar" bind:tap="editHouse">
  <text class="enjoy-icon icon-edit"></text>
  <text class="button-text">修改房屋信息</text>
</view>

要点解释与易错点:

  • onLoad({ id }) 直接解构地址参数,前提是列表页跳转时 URL 上拼了 ?id=;漏拼则 id 为 undefined。
  • ID 是拼在路径上的:'/room/' + id,不要写成 query 形式。
  • if (!id) return wx.utils.toast('参数有误!') 做前置校验,参数缺失时不发请求。
  • data 初始值给空对象 {},避免模板访问 houseDetail.point 时报错。
  • 详情与列表一样是三种状态 1/2/3 对应 info/success/fail。

示例 3:编辑房屋 + 删除房屋(house_pkg/pages/form/index.js、list/index.js 中的删除逻辑) ​

javascript
// house_pkg/pages/form/index.js
Page({
  onLoad({ point, building, room, id }) {
    // 根据 id 判断是否为修改房屋
    if (id) return this.getHouseDetail(id)
    // 添加房屋(渲染地址参数)
    this.setData({ point, building, room })
  },
  // 房屋详情接口
  async getHouseDetail(id) {
    const { code, data: houseDetail } = await wx.http.get('/room/' + id)
    if (code !== 10000) return wx.utils.toast()
    this.setData({ ...houseDetail })
  },
  // 提交审核
  async submitForm() {
    // 验证数据
    if (!this.validate()) return
    // 获取全部的数据(剔除可能多余参数 __webviewId__ 与不允许修改的 status)
    const { __webviewId__, status, ...data } = this.data
    // 调用接口(有 id 为修改,无 id 为添加)
    const { code } = await wx.http.post('/room', data)
    if (code !== 10000) return wx.utils.toast('提交数据失败!')

    // 返回房屋列表页面
    wx.navigateBack({ delta: 4 })
  },
})
javascript
// house_pkg/pages/list/index.js(删除房屋部分)
Page({
  // ... 前面的 data / onShow / getHouseList / goDetail 同示例 1
  swipeClose(ev) {
    const { position, instance } = ev.detail
    if (position === 'right') {
      // 显示 Dialog 对话框
      this.setData({
        dialogVisible: true,
      })
      // 记录房屋的 id 和索引,在调用接口时使用
      this.cellId = ev.mark.id
      this.cellIndex = ev.mark.index
      // swipe-cell 滑块关闭
      instance.close()
    }
  },
  dialogClose(ev) {
    // 当用户点了确认按钮时调用方法删除数据
    if (ev.detail === 'confirm') this.deleteHouse()
  },
  // 删除房屋
  async deleteHouse() {
    // 检测 id 是否存在
    if (!this.cellId) return wx.utils.toast('参数有误!')
    // 调用接口
    const { code } = await wx.http.delete('/room/' + this.cellId)
    if (code !== 10000) return wx.utils.toast()
    // 从 AppData 中将删除的房屋从数组移除
    this.data.houseList.splice(this.cellIndex, 1)
    // 渲染数据
    this.setData({
      houseList: this.data.houseList,
      isEmpty: this.data.houseList.length === 0,
    })
  },
})
xml
<!-- house_pkg/pages/list/index.wxml 中弹层部分 -->
<van-dialog
  use-slot
  title="提示"
  show-cancel-button
  visible="{{dialogVisible}}"
  bind:close="dialogClose"
>
  <view>确定要删除该房屋吗?</view>
</van-dialog>

要点解释与易错点:

  • 编辑与添加共用同一页面:地址参数里有 id 就调详情接口回填,没有就按添加处理;提交时同一个 POST /room 接口靠 id 区分添加/修改。
  • 提交前 if (!this.validate()) return,validate() 返回布尔值,任一字段不通过就不能发请求。
  • 解构剔除多余字段:__webviewId__ 是旧版开发者工具会自动带上的多余参数;status 是房屋状态,修改房屋时不允许改,必须剔除。
  • wx.navigateBack({ delta: 4 }):从列表 → 添加 → 选小区 → 选楼栋 → 选房间号共 4 层,delta 默认是 0(只返回上一页),层数写错会退不到列表页。
  • swipeClose 中 position === 'right' 表示点击了右侧删除区域;left、cell 不处理。
  • this.cellId / this.cellIndex 挂在页面实例上,因为 swipeClose 里拿到的数据要在 deleteHouse 里使用,不能用 data 存(会造成多余渲染)。
  • instance.close() 用来把滑块收回去,van-swipe-cell 上要加 async-close,否则不会等逻辑执行完再关闭。
  • dialogClose 中 ev.detail === 'confirm' 才删除,cancel 时什么都不做。
  • 接口删除成功后必须同步执行 this.data.houseList.splice(this.cellIndex, 1) 并重新 setData,否则数据库删了、页面数据还在,两者不一致;同时要重算 isEmpty。
  • splice 的第二个参数是删除个数,固定写 1。

常见错误 ​

1. 添加/编辑房屋返回列表后,列表还是旧数据

  • 现象:wx.navigateBack 回到列表页,新房屋不出现,手动下拉或重启才看到。
  • 原因:getHouseList 写在 onLoad 里;navigateBack 只触发 onShow,不触发 onLoad。
  • 修法:把拉取逻辑移到 onShow;若 onLoad 也需要,抽成同一方法避免重复请求。

2. 列表渲染出空白,控制台看到 data 是 Promise

  • 现象:houseList 为 {} 或 undefined,wx:for 无输出。
  • 原因:wx.http.get 返回 Promise,忘了 await,或 await 了但没解构 data 就直接 setData({ houseList: res })。
  • 修法:const { code, data: houseList } = await wx.http.get('/room'),并先判 code !== 10000 再渲染。

3. 图片上传成功但预览不出来,页面显示 [object Object] 或空

  • 现象:uploadFile 的 success 回调拿到了结果,但 setData 后图片不显示。
  • 原因:uploadFile 返回的 res.data 是 JSON 字符串,不是对象,取 data.url 取不到。
  • 修法:const result = JSON.parse(res.data),再判 result.code,最后取 result.data.url。

4. 上传接口 401/400,图片传不上去

  • 现象:接口报未登录或缺少文件字段。
  • 原因:header 里 Authorization 少了 'Bearer ' 后的空格;name 写成了 img/file 等;url 直接写 /upload 没拼 wx.http.baseURL。
  • 修法:'Bearer ' + getApp().token(注意空格)、name: 'file'、url: wx.http.baseURL + '/upload'。

5. 编辑房屋提交后,房屋状态被重置或接口报错

  • 现象:原本"审核通过"的房屋,改完信息变回"正在审核"。
  • 原因:this.data 整体提交,把 status(以及 __webviewId__)一起发给了服务端。
  • 修法:提交前解构剔除:const { __webviewId__, status, ...data } = this.data,只提交 data。

6. 侧滑删除,删掉的不是滑动的这一条

  • 现象:滑 A 删除,结果 B 消失了。
  • 原因:mark:id / mark:index 绑在了内层 van-cell-group 上而非 van-swipe-cell 上,ev.mark 取不到或取到错误值。
  • 修法:把 mark:id、mark:index 绑在 van-swipe-cell 上;并在 swipeClose 里立刻 this.cellId = ev.mark.id 存到页面实例,供后续方法使用。

7. 删除成功但列表没变化,或空列表提示不出现

  • 现象:接口 200,页面条目仍在;或删到最后一条仍显示列表。
  • 原因:只调了接口没同步 AppData;或 splice 后没 setData;或 isEmpty 没跟着重算。
  • 修法:splice 后一次 setData({ houseList, isEmpty: houseList.length === 0 });若改用 filter 重写更稳妥(不依赖索引)。

8. 审核状态标签样式错乱(文字对但没背景色)

  • 现象:显示"审核失败"但没有红色底。
  • 原因:类名写错,用了 error/arror,而样式表里定义的是 fail。
  • 修法:对照全局样式表核对 info / success / fail 三个类名,三处(列表、详情)同步修改。

练习 ​

练习 1|验证生命周期 在 getHouseList 中打印一行日志,分别用「添加房屋 → 提交 → 返回列表」和「切换 tabBar 回列表」两条路径观察触发次数。回答:如果改成 onLoad,哪条路径会失效?为什么 tabBar 切换也依赖 onShow?

练习 2|消除重复的状态判断 当前列表页与详情页各有三组 wx:if 判断状态文案与类名。请抽出一份状态映射(如 const STATUS_MAP = { 1: {...}, 2: {...}, 3: {...} }),用 WXS 或页面内的转换函数统一产出 { text, className },让模板只写一次。完成后对比改动前后的模板行数。

练习 3|把删除改为不依赖索引 重写 deleteHouse,不使用 this.cellIndex,改为按 id 过滤:houseList.filter(item => item.id !== this.cellId)。然后构造一个场景验证旧写法的问题:连续快速删除两条(第一条的动画未结束就滑第二条),观察两种实现的差异。

练习 4|封装图片上传 把「选择图片 → 上传 → 按 type 写入 idcardFrontUrl / idcardBackUrl」抽成一个可复用方法 uploadImage(type),要求:失败时有 toast、成功时用动态 key 写入、并保证返回 Promise 以便 await。抽完后让表单页的代码只剩两行调用。

练习 5|边界与回归测试 不改代码,只做验证并记录结论:①房屋数量达到 5 条时"添加新房屋"按钮是否隐藏;②删到 0 条时是否出现引导文案与跳转链接;③从详情页点"修改房屋信息"提交后,navigateBack 是否能回到列表且数据已刷新。任一项不符合,定位是哪一处 setData 或 delta 的问题。

原视频对照 ​

#视频时长
1享+生活-添加房屋(上传图片)-210:06
2享+生活-添加房屋(提交表单)10:17
3享+生活-房屋列表12:52
4享+生活-修改房屋的状态03:57
5享+生活-查询房屋详情09:38
6享+生活-删除房屋-111:09
7享+生活-删除房屋-210:13

小结 ​

  1. 页面数据何时刷新,由"数据可能在何处被改变"决定,而不是由"页面第一次打开"决定——凡是从别处返回的场景,都要检查是否只依赖了 onLoad。
  2. 所有接口调用都遵循同一骨架:解构 code 与 data → 判 code 失败即 toast 并 return → 成功才 setData。骨架统一,出错点就只剩参数。
  3. 组件库回调里拿到的数据往往不是最终形态:uploadFile 的 data 是字符串,swipe-cell 的 close 需要区分 position,dialog 的 close 需要区分 confirm。先打印再判断,比猜更快。
  4. 事件回调和后续方法之间需要"桥",这个桥就是页面实例 this;同时,跨组件传参优先用 mark: 与 URL query,不要依赖全局变量或隐式顺序。
  5. 服务端数据与页面数据是两份数据:接口成功只代表数据库变了,AppData 必须手动同步(增删改各一次),否则出现"刷新才对"的假象。
  6. 凡是"提交表单",都要先问一句"哪些字段不该由前端决定"——status、__webviewId__ 这类字段的剔除,比多传一次更难排查。
  7. 调试接口(改状态)只用于验证渲染分支,不能进入业务流程;能靠它验证的,说明前端只负责展示,不负责裁决。
  8. 边界条件(0 条、5 条上限、删最后一条)往往比主流程更容易出错,写完主流程立刻补一轮边界验证,成本最低。