Skip to content

访客管理(一) ​

本页对应 访客管理,共 5 段视频,约 39 分钟。

学习目标 ​

  1. 能够使用 authorization 组件检测访客邀请页面的登录状态,并完成房屋列表接口调用与页面渲染。
  2. 能够分别通过 model:value、action-sheet 的 select 事件、datetime-picker 的 confirm/cancel 事件获取姓名、性别、手机号、房屋信息和访问日期,并正确回显到页面。
  3. 能够为 datetime-picker 设置当前时间至未来 3 天的可选范围,并通过验证插件配置姓名、手机号、房屋、访问日期的校验规则,调用验证方法得到通过或不通过的结果。
  4. 能够根据接口文档以 POST 方式提交访客邀请表单数据,解析返回的 code 和访客 ID,并使用 reLaunch 携带 ID 跳转到访客详情页。
  5. 能够在访客详情页通过 onload 获取地址参数 ID,调用详情接口并将房屋名称、二维码、倒计时等数据渲染到页面。
  6. 能够根据返回的失效时间判断二维码状态,分别展示有效倒计时样式与失效提示弹层样式。

知识点 ​

1. 登录状态与本节目标 ​

为什么需要:访客邀请单要绑定当前住户和房屋,未登录时无法确定“谁在邀请、邀请到哪套房”,所以填写前必须保持登录状态。
怎么想:页面位于 visitor_pkg/pages/form/index.js,进入页面先做登录检测,再获取房屋列表;登录检测可复用 authorization 组件,把表单相关弹层包在组件内。
怎么做:在页面结构中使用 authorization 组件包裹需要登录后展示的内容;在 onLoad 中调用 getHouseList。转写稿明确本节先完成两件事:检测登录状态、获取并展示房屋列表。
易错点:不要先渲染表单再检测登录;未登录时接口会失败。转写稿中“坚挺 select”应理解为“监听 select 事件”,“authorization”不要写成普通按钮逻辑。

2. 获取房屋列表与选择房屋 ​

为什么需要:访客邀请必须选择到访房屋,房屋数据来自接口,不能写死。
怎么想:房屋列表用 van-action-sheet 展示,选择结果通过监听 select 事件获取;接口成功后再 setData 渲染。
怎么做:调用 /house,判断 code === 10000,失败用 wx.utils.toast() 提示;选择时从 ev.detail 中取 id 和 name 记录为 houseId、houseInfo。
易错点:houseList 要有初始值;接口失败要 return;houseId 要初始化为空字符串,否则后续验证可能异常。转写稿调试时在 Network 中确认请求成功并返回两条房屋数据,选择房屋后在 AppData 中能看到 houseId、houseInfo 更新。

javascript
// visitor_pkg/pages/form/index.js
Page({
	data: {
    houseList: []
  },
  onLoad() {
    // 获取房屋列表
    this.getHouseList()
  },
  // 获取房屋列表
  async getHouseList() {
    // 调用接口
    const { code, data: houseList } = await wx.http.get('/house')
    // 检测接口是否调用成功
    if (code !== 10000) return wx.utils.toast()
    // 渲染数据
    this.setData({houseList})
  },
  // 获取用户选择的房屋
  selectHouseInfo(ev) {
    // 记录获取的数据
    this.setData({
      houseId: ev.detail.id,
      houseInfo: ev.detail.name,
    })
  },
})

3. 获取到访日期与三天范围 ​

为什么需要:访客申请要限定到访日期,业务要求只能提前 3 天申请,避免选择过远日期。
怎么想:日期用 van-datepicker,通过 confirm 获取选择结果;最大值是当前时间加 3 天,最小值是当前时间。
怎么做:currentDate 用 Date.now(),maxDate 用 Date.now() + 1000 * 3600 * 24 * 3;确认后格式化时间并隐藏弹层。
易错点:confirm 返回的是时间戳,展示前要格式化;转写稿调试时发现时间格式化补零边界有问题,把补零判断从 10 调整为 9 后恢复正常;取消时也要关闭弹层。原笔记代码使用 wx.utils.dataFormat,保持原方法名。

javascript
// visitor_pkg/pages/form/index.js
Page({
	data: {
    currentDate: Date.now(),
    maxDate: Date.now() + 1000 * 3600 * 24 * 3,
  },
  // 获取用户选择的日期
  selectDateInfo(ev) {
    // 记录获取的时间并隐藏弹层
    this.setData({
      visitDate: wx.utils.dataFormat(ev.detail),
      dateLayerVisible: false,
    })
  },
})

4. 表单验证 ​

为什么需要:姓名、手机号、房屋、日期必须合法,否则提交无效数据。
怎么想:使用 wechat-validate,通过 behaviors 接入,在 rules 中分别声明必填和格式规则;性别有默认值可不验证。
怎么做:姓名必填且中文 2–5 位;手机号必填且符合 ^1[3-8]\d{9}$;houseId、visitDate 必填。
易错点:houseId、visitDate 要先初始化为空字符串;提交前必须 if (!this.validate()) return。转写稿中“rurs”是 rules,“behaviors”不要漏写。

javascript
// visitor_pkg/pages/form/index.js
// 导入验证插件
import wxValidate from 'wechat-validate'
Page({
  behaviors: [wxValidate],
  data: {
    name: '',
    gender: 1,
    mobile: '',
    houseId: '',
    visitDate: '',
  },
  rules: {
    name: [
      {required: true, message: '访客姓名不能为空!'},
      {pattern: /[\u4e00-\u9fa5]{2,5}/, message: '访客姓名只能为中文!'},
    ],
    mobile: [
      {required: true, message: '访客手机号不能为空!'},
      {pattern: /^1[3-8]\d{9}$/, message: '请填写正确的手机号码!'},
    ],
    houseId: [
      {required: true, message: '请选择到访的房屋!'}
    ],
    visitDate: [
      {required: true, message: '请选择到访的日期!'}
    ],
  },
})

5. 提交表单并跳转详情 ​

为什么需要:填写和验证完成后要把申请提交给后端,并用返回的 id 打开访客详情查看二维码。
怎么想:先验证,再解构 5 个字段,POST /visitor;成功判断 code,失败提示,成功用 wx.reLaunch 跳 /visitor_pkg/pages/passport/index?id=...。
怎么做:见下方代码。转写稿说明 reLaunch 会关闭其他页面,详情页左上角显示小房子而不是返回按钮。
易错点:不要提交多余字段;失败不要跳转;id 必须从返回 data.id 中取。

javascript
// visitor_pkg/pages/form/index.js
// 导入验证插件
import wxValidate from 'wechat-validate'
Page({
  behaviors: [wxValidate],
  data: {
    // ...
  },
  rules: {
    // ...
  },
  onLoad() {},
  getHouseList() {},
  // 提交表单数据
  async goPassport() {
    // 验证表单数据
    if (!this.validate()) return
    // 获取接口需要的数据
    const { name, gender, mobile, houseId, visitDate } = this.data
    // 调用接口
    const {
      code,
      data: { id },
    } = await wx.http.post('/visitor', { name, gender, mobile, houseId, visitDate })
    // 检测接口是否调用成功
    if (code !== 10000) return wx.utils.toast()
    // 跳转到访客详情页面
    wx.reLaunch({
      url: '/visitor_pkg/pages/passport/index?id=' + id,
    })
  },
})

先明确:访客详情有两种进入方式 ​

为什么需要:访客详情既可能由已登录用户从访客列表/提交成功页进入,也可能由朋友点击分享卡片进入。后者往往未登录,不能只靠 id 查详情。

怎么想:先看页面地址参数。若有 id,走登录态详情接口;若有 encryptedData,走分享态接口。两个入口可能同时判断,但各自都要判空。

怎么做:在 onLoad 中接收参数,分别调用 getPassport(id) 与 getPassportShare(encryptedData)。转写稿中先 console.log 地址参数确认能拿到 id,再发请求;若 id 不存在就不请求。

易错点:onLoad 参数解构不要漏;passport 要给初始空对象,避免渲染报错;接口 code !== 10000 要提示并返回。

登录态:按 id 获取访客详情 ​

为什么需要:登录用户已有身份,接口只需 id 就能返回通行证数据。

怎么想:先判 id,再拼 /visitor/ + id,最后解构 code 与 data: passport。

怎么做:参考代码如下:

javascript
// visitor_pkg/pages/passport/index.js
Page({
  data: {
    passport: {}
  },
  onLoad({id}) {
        // 获取访客详情
    this.getPassport(id)
  },
  // 获取访客详情(通行证)
  async getPassport(id) {
    // 检测是否存在 id
    if (!id) return
    // 调用接口
    const { code, data: passport } = await wx.http.get('/visitor/' + id)
    // 检测接口是否调用成功
    if (code !== 10000) return wx.utils.toast()
    // 渲染数据
    this.setData({ passport })
  },
})

易错点:转写稿中通过 Network 查看请求,确认返回数据后再渲染;不要把 data 直接当整个结果;code 判断不要写反。

渲染:倒计时与失效蒙层 ​

为什么需要:通行证核心是二维码和有效时间,过期后必须提示“二维码失效”,避免用户误用。

怎么想:接口返回 validTime。大于 0 表示有效,展示倒计时;等于 -1 表示失效,展示蒙层;倒计时组件通常按毫秒计时,所以 validTime * 1000。

怎么做:

xml
<view class="passport">
  <view class="countdown">
    <van-count-down wx:if="{{passport.validTime > 0}}" time="{{passport.validTime * 1000}}" />
    <view wx:else class="van-count-down">00:00:00</view>
    <view class="label">通行证有效时间</view>
  </view>
  <view class="qrcode">
    <image src="{{passport.url}}"></image>
    <view wx:if="{{passport.validTime === -1}}" class="mask">二维码失效</view>
  </view>
  <view class="description">
    <view class="house">{{passport.houseInfo}}</view>
    <view class="tips">将此二维码分享给访客,访客扫码即可开门</view>
  </view>
</view>
<view class="toolbar">
  <button class="button-share" open-type="share">
    <text class="enjoy-icon icon-share"></text>
    <text class="text">分享给朋友</text>
  </button>
  <button class="button-save">
    <text class="enjoy-icon icon-save"></text>
    <text class="text">保存到本地</text>
  </button>
</view>

易错点:validTime > 0 与 === -1 条件要分清;转写稿提到可用公共调试接口把访客状态改为失效,观察倒计时和蒙层是否切换。

分享态:用 encryptedData 获取详情 ​

为什么需要:新用户未登录,没有原 id 权限,分享 URL 中带 encryptedData,用它换取详情。

怎么想:自定义分享时从 this.data.passport 取 encryptedData 拼到路径;打开页面时若检测到该参数,再调分享详情接口。

怎么做:

javascript
// visitor_pkg/pages/passport/index.js
Page({
  data: {},
  onLoad() {},
  // 自定义分享
  onShareAppMessage() {
    // 获取加密数据
    const { encryptedData } = this.data.passport
    return {
      title: '查看通行证',
      path: '/visitor_pkg/pages/passport/index?encryptedData=' + encryptedData,
      imageUrl: 'https://enjoy-plus.oss-cn-beijing.aliyuncs.com/images/share_poster.png',
    }
  },
})
javascript
// visitor_pkg/pages/passport/index.js
Page({
  onLoad({ id, encryptedData }) {
    // 获取访客详情
    this.getPassport(id)
    this.getPassportShare(encryptedData)
  },
  async getPassport(id) {},
  async getPassportShare(encryptedData) {
    // 检测是否存在 id
    if (!encryptedData) return
    // 调用接口
    const { code, data: passport } = await wx.http.get('/visitor/share/' + encryptedData)
    // 检测接口是否调用成功
    if (code !== 10000) return wx.utils.toast()
    // 渲染数据
    this.setData({ passport })
  },
})

易错点:encryptedData 判空;路径拼接不要漏 /visitor/share/;分享页不要再让用户二次分享。

分享按钮:按入口隐藏,条件别写反 ​

为什么需要:分享页用户不应再次分享,否则会造成入口混乱。

怎么想:根据接口返回或入口参数控制 .toolbar 是否显示。笔记示例为:

xml
<view class="toolbar" wx:if="{{passport.encryptedData}}">
  <button class="button-share" open-type="share">
    <text class="enjoy-icon icon-share"></text>
    <text class="text">分享给朋友</text>
  </button>
  <button class="button-save">
    <text class="enjoy-icon icon-save"></text>
    <text class="text">保存到本地</text>
  </button>
</view>

易错点:按需求“分享页隐藏分享按钮”,实际条件要结合接口返回字段核对,避免把 wx:if 写反;转写稿还提到提交成功跳详情可用 reLaunch,与 navigateTo、redirectTo 的返回按钮表现不同,联调时注意页面栈。

课件图示 ​

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

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

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

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

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

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

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

代码示例 ​

示例 1:访客邀请表单页逻辑层(visitor_pkg/pages/form/index.js) ​

javascript
// visitor_pkg/pages/form/index.js
// 导入表单验证插件
import wxValidate from 'wechat-validate'

Page({
  // 混入验证插件提供的能力(validate 方法)
  behaviors: [wxValidate],

  data: {
    // 普通表单数据
    name: '',
    gender: 1, // 默认性别,因此不参与验证
    mobile: '',
    // 选择类数据,必须先给初始空值,否则验证不生效
    houseId: '',
    houseInfo: '',
    visitDate: '',
    // 房屋列表与两个弹层的显隐
    houseList: [],
    houseLayerVisible: false,
    dateLayerVisible: false,
    // 日期范围:今天 ~ 今天往后 3 天
    currentDate: Date.now(),
    maxDate: Date.now() + 1000 * 3600 * 24 * 3,
  },

  rules: {
    name: [
      { required: true, message: '访客姓名不能为空!' },
      { pattern: /[\u4e00-\u9fa5]{2,5}/, message: '访客姓名只能为中文!' },
    ],
    mobile: [
      { required: true, message: '访客手机号不能为空!' },
      { pattern: /^1[3-8]\d{9}$/, message: '请填写正确的手机号码!' },
    ],
    houseId: [{ required: true, message: '请选择到访的房屋!' }],
    visitDate: [{ required: true, message: '请选择到访的日期!' }],
  },

  onLoad() {
    // 获取房屋列表
    this.getHouseList()
  },

  // 获取房屋列表
  async getHouseList() {
    const { code, data: houseList } = await wx.http.get('/house')
    // 检测接口是否调用成功
    if (code !== 10000) return wx.utils.toast()
    // 渲染数据
    this.setData({ houseList })
  },

  // 弹层显隐控制
  showHouseLayer() {
    this.setData({ houseLayerVisible: true })
  },
  hideHouseLayer() {
    this.setData({ houseLayerVisible: false })
  },
  showDateLayer() {
    this.setData({ dateLayerVisible: true })
  },
  hideDateLayer() {
    this.setData({ dateLayerVisible: false })
  },

  // 获取用户选择的房屋(action-sheet 的 select 事件)
  selectHouseInfo(ev) {
    this.setData({
      houseId: ev.detail.id,
      houseInfo: ev.detail.name,
      houseLayerVisible: false,
    })
  },

  // 获取用户选择的日期(datetime-picker 的 confirm 事件)
  selectDateInfo(ev) {
    this.setData({
      visitDate: wx.utils.dataFormat(ev.detail),
      dateLayerVisible: false,
    })
  },

  // 提交表单数据
  async goPassport() {
    // 验证表单数据,不通过直接中断
    if (!this.validate()) return
    // 获取接口需要的数据
    const { name, gender, mobile, houseId, visitDate } = this.data
    // 调用接口
    const {
      code,
      data: { id },
    } = await wx.http.post('/visitor', { name, gender, mobile, houseId, visitDate })
    // 检测接口是否调用成功
    if (code !== 10000) return wx.utils.toast()
    // 跳转到访客详情页面(用 reLaunch 可清空页面栈)
    wx.reLaunch({
      url: '/visitor_pkg/pages/passport/index?id=' + id,
    })
  },
})

关键行与易错点

  • behaviors: [wxValidate] 是把插件能力混入 Page,混入之后才有 this.validate(),忘记写会导致提交时报 “validate is not a function”。
  • houseId、visitDate 必须初始化为空字符串。转写稿特别强调:如果不给初始值,验证插件的 required 规则会失效,或者根本不会触发。
  • gender 给了默认值 1,所以 rules 里不写它;ev.detail.id / ev.detail.name 是 action-sheet 的 select 事件回传的数据结构。
  • maxDate: Date.now() + 1000 * 3600 * 24 * 3 的顺序是「毫秒 → 秒 → 小时 → 天 → ×3」,写成 1000 * 3600 * 3 就变成 3 小时了,这是最容易算错的地方。
  • 日期 picker 回传的 ev.detail 是时间戳,必须用封装好的 wx.utils.dataFormat 格式化后再显示;转写稿还提示过:格式化的补零判断条件容易写错,10 前面不该补零。
  • 提交成功后用 wx.reLaunch 跳转,页面左上角显示小房子图标,而非返回箭头,因为详情页不该被返回堆栈。

示例 2:访客邀请表单页结构层(visitor_pkg/pages/form/index.wxml) ​

xml
<!-- visitor_pkg/pages/form/index.wxml -->
<!-- authorization 组件负责登录态检测,未登录时展示授权弹层 -->
<authorization>
  <view class="form">
    <!-- 普通数据:model:value 双向绑定 -->
    <view class="form-item">
      <text class="label">访客姓名</text>
      <input model:value="{{name}}" placeholder="请输入访客姓名" placeholder-class="placeholder" />
    </view>

    <view class="form-item">
      <text class="label">访客性别</text>
      <radio-group model:value="{{gender}}">
        <label><radio value="{{1}}" checked="{{gender === 1}}" color="#5591af" />男</label>
        <label><radio value="{{0}}" checked="{{gender === 0}}" color="#5591af" />女</label>
      </radio-group>
    </view>

    <view class="form-item">
      <text class="label">访客手机号</text>
      <input model:value="{{mobile}}" type="number" maxlength="11" placeholder="请输入手机号" placeholder-class="placeholder" />
    </view>

    <!-- 房屋:点击打开 action-sheet,选中后回填文字 -->
    <view class="form-item" bindtap="showHouseLayer">
      <text class="label">到访房屋</text>
      <text class="{{houseInfo ? 'active' : 'placeholder'}}">
        {{houseInfo || '请选择到访的房屋'}}
      </text>
    </view>

    <!-- 日期:点击打开 datetime-picker,选中后回填文字 -->
    <view class="form-item" bindtap="showDateLayer">
      <text class="label">到访日期</text>
      <text class="{{visitDate ? 'active' : 'placeholder'}}">
        {{visitDate || '请选择到访的日期'}}
      </text>
    </view>
  </view>

  <button class="button" bindtap="goPassport">确认提交</button>

  <!-- 房屋选择弹层:监听 select 事件 -->
  <van-action-sheet
    show="{{houseLayerVisible}}"
    actions="{{houseList}}"
    bind:select="selectHouseInfo"
    bind:close="hideHouseLayer"
  />

  <!-- 日期选择弹层:type=date,限定可选区间 -->
  <van-datetime-picker
    show="{{dateLayerVisible}}"
    type="date"
    value="{{currentDate}}"
    min-date="{{currentDate}}"
    max-date="{{maxDate}}"
    bind:confirm="selectDateInfo"
    bind:cancel="hideDateLayer"
  />
</authorization>

关键行与易错点

  • 两个弹层都要放进 <authorization> 里面:转写稿说明这两个节点本身就是默认隐藏的弹层,包裹进来才能受登录态控制;页面主体也要包进去。
  • actions="&#123;&#123;houseList&#125;&#125;" 直接把接口返回的数组(含 id、name)交给 action-sheet,所以 select 事件里能直接拿到 ev.detail.id 和 ev.detail.name,无需再手动转换。
  • 房屋、日期两处都用 &#123;&#123;xxx || '提示文字'&#125;&#125; + 三元类名:有值时用 active 让文字变深,无值时用占位色。转写稿中“颜色变深”靠的就是这个类名切换。
  • 日期组件必须同时给 min-date 和 max-date,只给 max-date 就还能选到过去日期;笔记里写的是 van-datepicker,实际 Vant Weapp 中组件名为 van-datetime-picker,属性写法一致。
  • cancel 事件必须单独监听,否则用户取消时弹层不会收起。
  • input type="number" 加 maxlength="11" 是手机号的常规处理,但校验仍要依赖 rules 里的正则,二者不能互相替代。

示例 3:访客详情页(visitor_pkg/pages/passport/index.js + index.wxml) ​

javascript
// visitor_pkg/pages/passport/index.js
Page({
  data: {
    // 给初始空对象,避免 wxml 取 passport.xxx 报错
    passport: {},
  },

  onLoad({ id, encryptedData }) {
    // 登录状态下按 id 查
    this.getPassport(id)
    // 分享打开时按 encryptedData 查
    this.getPassportShare(encryptedData)
  },

  // 获取访客详情(通行证)
  async getPassport(id) {
    // 检测是否存在 id
    if (!id) return
    const { code, data: passport } = await wx.http.get('/visitor/' + id)
    if (code !== 10000) return wx.utils.toast()
    this.setData({ passport })
  },

  // 分享场景下获取访客详情
  async getPassportShare(encryptedData) {
    // 检测是否存在 encryptedData
    if (!encryptedData) return
    const { code, data: passport } = await wx.http.get('/visitor/share/' + encryptedData)
    if (code !== 10000) return wx.utils.toast()
    this.setData({ passport })
  },

  // 自定义分享
  onShareAppMessage() {
    const { encryptedData } = this.data.passport
    return {
      title: '查看通行证',
      path: '/visitor_pkg/pages/passport/index?encryptedData=' + encryptedData,
      imageUrl: 'https://enjoy-plus.oss-cn-beijing.aliyuncs.com/images/share_poster.png',
    }
  },
})
xml
<!-- visitor_pkg/pages/passport/index.wxml -->
<view class="passport">
  <view class="countdown">
    <!-- validTime 大于 0 才渲染倒计时组件,单位换算成毫秒 -->
    <van-count-down wx:if="{{passport.validTime > 0}}" time="{{passport.validTime * 1000}}" />
    <view wx:else class="van-count-down">00:00:00</view>
    <view class="label">通行证有效时间</view>
  </view>

  <view class="qrcode">
    <image src="{{passport.url}}"></image>
    <!-- 失效时(validTime === -1)盖上半透明蒙层 -->
    <view wx:if="{{passport.validTime === -1}}" class="mask">二维码失效</view>
  </view>

  <view class="description">
    <view class="house">{{passport.houseInfo}}</view>
    <view class="tips">将此二维码分享给访客,访客扫码即可开门</view>
  </view>
</view>

<!-- 分享打开的场景下不再允许二次分享,靠 encryptedData 控制显隐 -->
<view class="toolbar" wx:if="{{passport.encryptedData}}">
  <button class="button-share" open-type="share">
    <text class="enjoy-icon icon-share"></text>
    <text class="text">分享给朋友</text>
  </button>
  <button class="button-save">
    <text class="enjoy-icon icon-save"></text>
    <text class="text">保存到本地</text>
  </button>
</view>

关键行与易错点

  • onLoad({ id, encryptedData }) 直接解构页面参数,两个接口都在这里调用,各自内部用 if (!id) return / if (!encryptedData) return 做保护;登录态进来只有 id,分享进来只有 encryptedData。
  • passport.validTime * 1000:接口给的是秒,van-count-down 的 time 要毫秒,不换算就少走 1000 倍。
  • 失效判断统一用 validTime === -1:倒计时用 > 0 控制渲染,蒙层用 === -1 控制,两者互斥,不能都写 !== -1 或都写 > 0,否则要么一直显示 00:00:00 而没有蒙层,要么二维码正常时也盖着蒙层。
  • data.passport 初值必须是 {},写成 null 或直接不写,passport.url、passport.houseInfo 会取属性报错。
  • onShareAppMessage 里的 path 必须带 ?encryptedData=,这是新用户(未登录)打开页面时唯一的数据来源;open-type="share" 的按钮才触发该方法。
  • 工具栏显隐用 wx:if="&#123;&#123;passport.encryptedData&#125;&#125;",分享场景下 encryptedData 为空,按钮自然隐藏,避免被分享者再往下传播。

常见错误 ​

  1. 现象:未登录也能进入表单或提交成功。→ 原因:只在页面放了 authorization,未包住需登录区域,提交入口也没有登录态兜底。→ 修法:用 authorization 包住表单/弹层区域,提交前确保登录态,未登录不发起请求。
  2. 现象:房屋列表不显示或仍显示旧 mock 数据。→ 原因:接口成功后未 setData,或保留了初始假数据。→ 修法:houseList 初始为 [],code === 10000 后 setData({ houseList }),失败 toast 并 return。
  3. 现象:选了房屋,提交时 houseId 仍为空。→ 原因:select 事件只更新了显示文本,或取值字段写错。→ 修法:同时 setData({ houseId: ev.detail.id, houseInfo: ev.detail.name }),并在 data 中初始化 houseId: ''。
  4. 现象:日期能选过去日期或超过 3 天。→ 原因:minDate/maxDate 未绑定或时间计算单位错误。→ 修法:currentDate = Date.now(),maxDate = Date.now() + 1000 * 3600 * 24 * 3,分别绑定到日期组件的最小/最大值。
  5. 现象:日期显示成时间戳,或 10 点被补零成 010。→ 原因:confirm 后直接渲染 ev.detail,或格式化补零条件写错。→ 修法:confirm 后先 wx.utils.dataFormat(ev.detail) 再渲染,补零判断用 < 10。
  6. 现象:空 houseId、空 visitDate 也能通过校验并提交。→ 原因:字段未在 data 初始化,required 规则拿不到值。→ 修法:houseId、visitDate 初始为空字符串,提交前 if (!this.validate()) return。
  7. 现象:校验失败仍请求接口,或接口失败仍跳详情。→ 原因:漏判 validate() 返回值或漏判 code。→ 修法:先校验,再 post,code !== 10000 时 toast 并 return,成功后再 reLaunch。
  8. 现象:详情页分享入口和 id 入口互相覆盖,或分享打开后仍能再次分享。→ 原因:onLoad 不判空都调用接口,工具栏条件未按 encryptedData 区分。→ 修法:getPassport(id)、getPassportShare(encryptedData) 内部先判空 return;分享入口与 encryptedData 状态绑定,并自测两种入口。

练习 ​

  1. 在 goPassport 中补齐提交链路:校验、取参、POST /visitor、code 判断、按返回 id 跳转详情页。要求所有失败路径都不跳转。
  2. 为日期选择器配置“今天起只能选未来 3 天”,写出 currentDate、maxDate 及模板绑定,并列出今天、第 3 天、第 4 天三个自测用例。
  3. 实现房屋选择回写:select 后同时更新 houseId 和 houseInfo,并保证未选择房屋时提交被拦截。写出 setData 与 rules 的关键配置。
  4. 详情页双入口自测:分别用 id 和 encryptedData 打开页面,要求无参不发请求、分享打开不显示再次分享入口、validTime === -1 显示失效蒙层。列出你的测试步骤和预期结果。

原视频对照 ​

#视频时长
1享+生活-访客邀请-105:29
2享+生活-访客邀请-210:21
3享+生活-访客邀请-308:44
4享+生活-访客邀请-406:20
5享+生活-访客详情-108:29

小结 ​

  1. 访客邀请的主链路是:登录校验 → 采集房屋/日期 → 表单校验 → 提交 → 按返回 id 跳详情。
  2. 表单数据要统一落到 data 后再提交,普通字段与组件事件回写字段都不能漏初始化。
  3. 时间范围和时间格式化应在采集阶段处理,不要留到提交或渲染阶段补救。
  4. 提交动作必须遵守“先校验、再请求、判 code、最后跳转”,任一步失败立即终止。
  5. 详情页要同时兼容登录态 id 入口与分享 encryptedData 入口,方法内部先判空。
  6. 详情渲染要按 validTime 区分有效/失效,倒计时、蒙层、工具栏条件要成对自测。
  7. 页面跳转 API 会影响返回路径,详情页使用 reLaunch 的业务意图要明确。
  8. 分享路径必须携带 encryptedData,分享打开后要避免再次分享。