主题
访客管理(一)
本页对应 访客管理,共 5 段视频,约 39 分钟。
学习目标
- 能够使用
authorization组件检测访客邀请页面的登录状态,并完成房屋列表接口调用与页面渲染。 - 能够分别通过
model:value、action-sheet的select事件、datetime-picker的confirm/cancel事件获取姓名、性别、手机号、房屋信息和访问日期,并正确回显到页面。 - 能够为
datetime-picker设置当前时间至未来 3 天的可选范围,并通过验证插件配置姓名、手机号、房屋、访问日期的校验规则,调用验证方法得到通过或不通过的结果。 - 能够根据接口文档以 POST 方式提交访客邀请表单数据,解析返回的
code和访客 ID,并使用reLaunch携带 ID 跳转到访客详情页。 - 能够在访客详情页通过
onload获取地址参数 ID,调用详情接口并将房屋名称、二维码、倒计时等数据渲染到页面。 - 能够根据返回的失效时间判断二维码状态,分别展示有效倒计时样式与失效提示弹层样式。
知识点
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 的返回按钮表现不同,联调时注意页面栈。
课件图示







代码示例
示例 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="{{houseList}}"直接把接口返回的数组(含id、name)交给 action-sheet,所以select事件里能直接拿到ev.detail.id和ev.detail.name,无需再手动转换。- 房屋、日期两处都用
{{xxx || '提示文字'}}+ 三元类名:有值时用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="{{passport.encryptedData}}",分享场景下encryptedData为空,按钮自然隐藏,避免被分享者再往下传播。
常见错误
- 现象:未登录也能进入表单或提交成功。→ 原因:只在页面放了
authorization,未包住需登录区域,提交入口也没有登录态兜底。→ 修法:用authorization包住表单/弹层区域,提交前确保登录态,未登录不发起请求。 - 现象:房屋列表不显示或仍显示旧 mock 数据。→ 原因:接口成功后未
setData,或保留了初始假数据。→ 修法:houseList初始为[],code === 10000后setData({ houseList }),失败toast并return。 - 现象:选了房屋,提交时
houseId仍为空。→ 原因:select事件只更新了显示文本,或取值字段写错。→ 修法:同时setData({ houseId: ev.detail.id, houseInfo: ev.detail.name }),并在data中初始化houseId: ''。 - 现象:日期能选过去日期或超过 3 天。→ 原因:
minDate/maxDate未绑定或时间计算单位错误。→ 修法:currentDate = Date.now(),maxDate = Date.now() + 1000 * 3600 * 24 * 3,分别绑定到日期组件的最小/最大值。 - 现象:日期显示成时间戳,或 10 点被补零成
010。→ 原因:confirm后直接渲染ev.detail,或格式化补零条件写错。→ 修法:confirm后先wx.utils.dataFormat(ev.detail)再渲染,补零判断用< 10。 - 现象:空
houseId、空visitDate也能通过校验并提交。→ 原因:字段未在data初始化,required规则拿不到值。→ 修法:houseId、visitDate初始为空字符串,提交前if (!this.validate()) return。 - 现象:校验失败仍请求接口,或接口失败仍跳详情。→ 原因:漏判
validate()返回值或漏判code。→ 修法:先校验,再post,code !== 10000时toast并return,成功后再reLaunch。 - 现象:详情页分享入口和 id 入口互相覆盖,或分享打开后仍能再次分享。→ 原因:
onLoad不判空都调用接口,工具栏条件未按encryptedData区分。→ 修法:getPassport(id)、getPassportShare(encryptedData)内部先判空return;分享入口与encryptedData状态绑定,并自测两种入口。
练习
- 在
goPassport中补齐提交链路:校验、取参、POST /visitor、code判断、按返回id跳转详情页。要求所有失败路径都不跳转。 - 为日期选择器配置“今天起只能选未来 3 天”,写出
currentDate、maxDate及模板绑定,并列出今天、第 3 天、第 4 天三个自测用例。 - 实现房屋选择回写:
select后同时更新houseId和houseInfo,并保证未选择房屋时提交被拦截。写出setData与rules的关键配置。 - 详情页双入口自测:分别用
id和encryptedData打开页面,要求无参不发请求、分享打开不显示再次分享入口、validTime === -1显示失效蒙层。列出你的测试步骤和预期结果。
原视频对照
| # | 视频 | 时长 |
|---|---|---|
| 1 | 享+生活-访客邀请-1 | 05:29 |
| 2 | 享+生活-访客邀请-2 | 10:21 |
| 3 | 享+生活-访客邀请-3 | 08:44 |
| 4 | 享+生活-访客邀请-4 | 06:20 |
| 5 | 享+生活-访客详情-1 | 08:29 |
小结
- 访客邀请的主链路是:登录校验 → 采集房屋/日期 → 表单校验 → 提交 → 按返回
id跳详情。 - 表单数据要统一落到
data后再提交,普通字段与组件事件回写字段都不能漏初始化。 - 时间范围和时间格式化应在采集阶段处理,不要留到提交或渲染阶段补救。
- 提交动作必须遵守“先校验、再请求、判
code、最后跳转”,任一步失败立即终止。 - 详情页要同时兼容登录态
id入口与分享encryptedData入口,方法内部先判空。 - 详情渲染要按
validTime区分有效/失效,倒计时、蒙层、工具栏条件要成对自测。 - 页面跳转 API 会影响返回路径,详情页使用
reLaunch的业务意图要明确。 - 分享路径必须携带
encryptedData,分享打开后要避免再次分享。