feat: 完善淘宝小程序拍照、报告与登录流程
接入本地实时识别和自动三连拍,调整报告裁切、商品跳转、分享标题及居中页眉;完善游客会话、后台渠道隔离、部署脚本与回归测试。
This commit is contained in:
@@ -19,7 +19,7 @@ page { background: #0e0f18; }
|
||||
overflow: hidden;
|
||||
overscroll-behavior: none;
|
||||
background: #0e0f18;
|
||||
--profile-space: min(2rpx, max(0.6rpx, calc((100vh - 420px) / 306)));
|
||||
--profile-space: min(2rpx, max(0.6rpx, calc((100vh - var(--brand-header-height, 64px) - 420px) / 306)));
|
||||
}
|
||||
|
||||
/* H5 .profile-bg(ui-20261009 L31:bg-profile-h5,cover) */
|
||||
@@ -110,7 +110,6 @@ page { background: #0e0f18; }
|
||||
margin-top: -8rpx; /* calc(-4 * var(--u)) */
|
||||
border-width: 0 1.5px 1.5px 0;
|
||||
}
|
||||
.profile .pick-host { display: block; width: 100%; }
|
||||
|
||||
/* ─── H5 .skin-card(style.css L144-148 + ui-20261012 L189) ─── */
|
||||
.profile .skin-card {
|
||||
@@ -198,6 +197,7 @@ page { background: #0e0f18; }
|
||||
min-height: 0;
|
||||
transition: transform .32s cubic-bezier(.22, .61, .36, 1);
|
||||
}
|
||||
.profile .skin-track.dragging { transition: none; }
|
||||
/* H5 .skin-item / .skin-item img / .skin-name / .skin-desc */
|
||||
.profile .skin-item {
|
||||
flex: 0 0 100%;
|
||||
@@ -317,6 +317,15 @@ page { background: #0e0f18; }
|
||||
text-align: center;
|
||||
line-height: 44px;
|
||||
}
|
||||
/* H5 mobile-select: single wheel, 90% inner width and 44px yellow selection. */
|
||||
.gender-sheet { animation: gender-sheet-in .4s ease; }
|
||||
.gender-sheet .picker-btn-bar { padding: 0 5%; }
|
||||
.gender-picker { width: 90%; margin: 0 auto; }
|
||||
.gender-picker .picker-cell { height: 44px; }
|
||||
@keyframes gender-sheet-in {
|
||||
from { transform: translateY(100%); }
|
||||
to { transform: translateY(0); }
|
||||
}
|
||||
|
||||
/* ═══ 拍照引导弹窗 H5 #p-photoguide(L311-327) ═══ */
|
||||
.profile .overlay-guide {
|
||||
@@ -492,3 +501,5 @@ page { background: #0e0f18; }
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }
|
||||
.session-guard { position: relative; z-index: 10; padding: 120rpx 40rpx; text-align: center; font-size: 13px; line-height: 1.8; color: #707070; }
|
||||
.session-guard .btn-primary { margin: 32rpx auto 0; }
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
<import src="../../components/brand-header.axml" />
|
||||
<template is="brand-header" data="{{brandHeader: brandHeader}}" />
|
||||
<!-- ═════ 03 档案填写 —— 对照 H5 index.html #s-profile(L61-108) + #p-photoguide(L311-327) ═════ -->
|
||||
<view class="profile">
|
||||
<view class="profile" style="--brand-header-height:{{brandHeader.height}}px;margin-top:{{brandHeader.height}}px;height:calc(100vh - {{brandHeader.height}}px);">
|
||||
<view class="session-guard" a:if="{{identityChecking || identityError}}">
|
||||
<view>{{identityChecking ? '正在确认当前淘宝账号…' : identityError}}</view>
|
||||
<button a:if="{{identityError}}" class="btn-primary" onTap="onRetryIdentity">重新验证</button>
|
||||
</view>
|
||||
<!-- H5 .profile-bg -->
|
||||
<image class="profile-bg" src="{{bgUrl}}" mode="aspectFill" />
|
||||
|
||||
<!-- H5 .profile-body -->
|
||||
<view class="profile-body">
|
||||
<view class="profile-body" hidden="{{identityChecking || identityError}}">
|
||||
<view class="page-title">您的肌肤状态</view>
|
||||
|
||||
<!-- H5 .form-row.two:出生年月 + 性别 -->
|
||||
@@ -18,12 +24,10 @@
|
||||
</view>
|
||||
<view class="field">
|
||||
<view class="field-label">您的性别</view>
|
||||
<picker class="pick-host" mode="selector" range="{{genderRange}}" onChange="onGenderChange">
|
||||
<view class="pick">
|
||||
<text class="pick-value">{{genderText}}</text>
|
||||
<view class="chev down"></view>
|
||||
</view>
|
||||
</picker>
|
||||
<view class="pick" onTap="onOpenGender">
|
||||
<text class="pick-value">{{genderText}}</text>
|
||||
<view class="chev down"></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -49,8 +53,8 @@
|
||||
<view class="skin-arrow prev {{skinIndex === 0 ? 'disabled' : ''}}" onTap="onSkinPrev">
|
||||
<view class="chev left"></view>
|
||||
</view>
|
||||
<view class="skin-carousel" onTouchStart="onSkinTouchStart" onTouchEnd="onSkinTouchEnd">
|
||||
<view class="skin-track" style="transform: translateX(-{{skinPercent}}%);">
|
||||
<view class="skin-carousel" onTouchStart="onSkinTouchStart" onTouchMove="onSkinTouchMove" onTouchEnd="onSkinTouchEnd" onTouchCancel="onSkinTouchCancel">
|
||||
<view class="skin-track {{skinDragging ? 'dragging' : ''}}" style="transform: translateX(-{{skinPercent}}%) translateX({{skinDragOffset}}px);">
|
||||
<view class="skin-item" a:for="{{skins}}" a:key="name">
|
||||
<image class="skin-img" src="{{item.img}}" mode="aspectFit" />
|
||||
<text class="skin-name">{{item.name}}</text>
|
||||
@@ -75,15 +79,20 @@
|
||||
<button class="btn-primary btn-submit" hover-class="btn-press" onTap="onSubmit">提交</button>
|
||||
</view>
|
||||
|
||||
<!-- ═══ 弹层:出生年月/地区滚轮(保留现有 picker-view 自绘实现,仅统一外观) ═══ -->
|
||||
<view class="overlay overlay-sheet" a:if="{{picker}}" onTap="onPickerClose">
|
||||
<view class="picker-sheet" catchTap="noop">
|
||||
<!-- ═══ 弹层:出生年月/性别/地区滚轮 ═══ -->
|
||||
<view class="overlay overlay-sheet" a:if="{{picker && !identityChecking && !identityError}}" onTap="onPickerClose">
|
||||
<view class="picker-sheet {{picker === 'gender' ? 'gender-sheet' : ''}}" catchTap="noop">
|
||||
<view class="picker-btn-bar">
|
||||
<view class="picker-cancel" onTap="onPickerClose">取消</view>
|
||||
<view class="picker-title">{{pickerTitle}}</view>
|
||||
<view class="picker-ensure" onTap="onPickerConfirm">确定</view>
|
||||
</view>
|
||||
<picker-view class="native-picker" value="{{pickerValue}}" onChange="onPickerChange">
|
||||
<picker-view a:if="{{picker === 'gender'}}" class="native-picker gender-picker" value="{{pickerValue}}" onChange="onPickerChange" indicator-style="height:44px; border-top:1px solid #EFEFEF; border-bottom:1px solid #EFEFEF; background-color:rgba(245,240,120,.18);">
|
||||
<picker-view-column>
|
||||
<view class="picker-cell" a:for="{{genderRange}}" a:key="*this">{{item}}</view>
|
||||
</picker-view-column>
|
||||
</picker-view>
|
||||
<picker-view a:else class="native-picker" value="{{pickerValue}}" onChange="onPickerChange">
|
||||
<picker-view-column>
|
||||
<view class="picker-cell" a:for="{{picker === 'birth' ? birthRange[0] : regionRange[0]}}" a:key="*this">{{item}}</view>
|
||||
</picker-view-column>
|
||||
@@ -95,7 +104,7 @@
|
||||
</view>
|
||||
|
||||
<!-- ═══ 弹窗:拍照引导(H5 #p-photoguide:L311-327) ═══ -->
|
||||
<view class="overlay overlay-guide" a:if="{{showGuide}}" onTap="onCloseGuide">
|
||||
<view class="overlay overlay-guide" a:if="{{showGuide && !identityChecking && !identityError}}" onTap="onCloseGuide">
|
||||
<view class="guide-bg">
|
||||
<image class="guide-bg-img" src="{{guideBgUrl}}" mode="aspectFill" />
|
||||
<!-- 背景只保留启动页底图 + Slogan -->
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
/* ═════ 档案填写页:出生年月/性别/地区 + 日常肤质轮播 → 拍照
|
||||
对照 H5 index.html #s-profile(L61-108)、#p-photoguide(L311-327)
|
||||
档案字段与 H5 一致:{ real_age, gender, region, daily_skin_type }
|
||||
选择器弹层保持现有实现(自绘 picker-view 滚轮 + 原生 selector),只调整触发控件外观。 ═════ */
|
||||
三个选择器均用底部滚轮弹层,滚动只修改草稿,确定后写回字段。 ═════ */
|
||||
var api = require('../../utils/api.js');
|
||||
var REGION_DATA = require('../../utils/region-data.js');
|
||||
var swipe = require('../../utils/swipe-gesture');
|
||||
|
||||
// 肤质选项(与 H5 SKINS 一致,图片远程加载;第一条描述在 H5 里含 <br>,拆成两行)
|
||||
var SKINS = [
|
||||
@@ -21,7 +22,8 @@ for (var m = 1; m <= 12; m++) MONTHS.push(m + '月');
|
||||
|
||||
function ageFromBirth(y, m) { return NOW_Y - y - (NOW_M < m ? 1 : 0); } // 未过生日月份按少一岁(与 H5 一致)
|
||||
|
||||
Page({
|
||||
Page(require('../../utils/page-header').page({
|
||||
onShareAppMessage: function () { return require('../../utils/share').message(); },
|
||||
data: {
|
||||
bgUrl: api.assetUrl('img/ui-20261009/bg-profile-h5.jpg'),
|
||||
guideBgUrl: api.assetUrl('img/ui-20261009/bg-splash-h5.jpg'),
|
||||
@@ -52,16 +54,16 @@ Page({
|
||||
return { img: api.assetUrl(s.img), name: s.name, desc: s.desc, desc2: s.desc2 || '' };
|
||||
}),
|
||||
skinIndex: 0,
|
||||
skinPercent: 0,
|
||||
skinPercent: 0, skinDragOffset: 0, skinDragging: false,
|
||||
skinLastIndex: SKINS.length - 1,
|
||||
|
||||
showGuide: false, picker: '', pickerTitle: '', pickerValue: [0, 0],
|
||||
toastText: '', toastOn: false
|
||||
toastText: '', toastOn: false, identityChecking: true, identityError: ''
|
||||
},
|
||||
onLoad: function () {
|
||||
if (!api.user()) { my.redirectTo({ url: '/pages/splash/splash' }); return; }
|
||||
if (!api.user()) { my.redirectTo({ url: '/pages/login/login' }); return; }
|
||||
this.ownerId = api.user().id;
|
||||
try {
|
||||
if (api.user().kind !== 'taobao_guest') try {
|
||||
var saved = my.getStorageSync({ key: 'juzi.taobao.profile.' + api.user().id }).data;
|
||||
if (saved) {
|
||||
saved.age = ageFromBirth(Number(saved.birth.split('-')[0]), Number(saved.birth.split('-')[1]));
|
||||
@@ -72,11 +74,57 @@ Page({
|
||||
this.setData({ skinIndex: this.data.skinIndex || 0, skinPercent: (this.data.skinIndex || 0) * 100 });
|
||||
},
|
||||
onShow: function () {
|
||||
this.disableSwipeBack();
|
||||
this.visible = true;
|
||||
if (!api.user() || this.ownerId !== api.user().id) {
|
||||
this.setData({ birthText: '', genderText: '', regionText: '', showGuide: false });
|
||||
my.redirectTo({ url: '/pages/splash/splash' });
|
||||
this.clearPrivateProfile(); return Promise.resolve(false);
|
||||
}
|
||||
if (api.user().kind === 'taobao_guest') {
|
||||
this.identityPending = false; var guestPage = this;
|
||||
this.setData({ identityChecking: false, identityError: '' }, function () { guestPage.measureSkin(); }); return Promise.resolve(true);
|
||||
}
|
||||
var that = this, seq = this.identitySeq = (this.identitySeq || 0) + 1;
|
||||
this.identityPending = true; this.setData({ identityChecking: true, identityError: '' });
|
||||
return (typeof api.restore === 'function' ? api.restore() : Promise.resolve(api.user())).then(function (user) {
|
||||
if (!that.visible || seq !== that.identitySeq) return false;
|
||||
if (!user || user.id !== that.ownerId) { that.clearPrivateProfile(); return false; }
|
||||
that.identityPending = false; that.setData({ identityChecking: false }, function () { that.measureSkin(); }); return true;
|
||||
}).catch(function (err) {
|
||||
if (!that.visible || seq !== that.identitySeq) return false;
|
||||
if (err.status === 401 || !api.user() || api.user().id !== that.ownerId) { that.clearPrivateProfile(); return false; }
|
||||
that.setData({ identityChecking: false, identityError: '网络异常,暂时无法确认当前账号,请重试。' }); return false;
|
||||
});
|
||||
},
|
||||
onRetryIdentity: function () { return this.onShow(); },
|
||||
disableSwipeBack: function () {
|
||||
// Disable the native page-exit gesture, leaving skin carousel touches intact.
|
||||
try {
|
||||
if (my.tb && typeof my.tb.enableSwipeBack === 'function') {
|
||||
var pending = my.tb.enableSwipeBack({ enable: 0 });
|
||||
if (pending && typeof pending.catch === 'function') pending.catch(function () {});
|
||||
}
|
||||
} catch (_) {}
|
||||
},
|
||||
onReady: function () { this.disableSwipeBack(); this.measureSkin(); },
|
||||
measureSkin: function () {
|
||||
if (typeof my.createSelectorQuery !== 'function') return;
|
||||
var that = this;
|
||||
try { my.createSelectorQuery().select('.skin-carousel').boundingClientRect().exec(function (rows) {
|
||||
if (that.visible && rows && rows[0] && rows[0].width > 0) that.skinWidth = rows[0].width;
|
||||
}); } catch (_) {}
|
||||
},
|
||||
onResize: function () { this.onSkinTouchCancel(); this.skinWidth = 0; this.measureSkin(); },
|
||||
clearPrivateProfile: function () {
|
||||
this.identityPending = true;
|
||||
this.setData({ birthText: '', birth: '', age: 0, genderText: '', regionText: '', showGuide: false, picker: '', identityChecking: true, identityError: '' });
|
||||
my.redirectTo({ url: '/pages/login/login' });
|
||||
},
|
||||
onHide: function () {
|
||||
this.visible = false; this.identitySeq = (this.identitySeq || 0) + 1;
|
||||
this.onSkinTouchCancel();
|
||||
if (api.user() && api.user().kind !== 'taobao_guest') { this.identityPending = true; this.setData({ identityChecking: true, identityError: '' }); }
|
||||
},
|
||||
onUnload: function () { this.onHide(); clearTimeout(this.toastTimer); },
|
||||
|
||||
// H5 .toast
|
||||
toast: function (msg) {
|
||||
@@ -92,6 +140,10 @@ Page({
|
||||
// H5 的 mobile-select title 为「出生年月」/「所在地区」
|
||||
this.setData({ picker: 'birth', pickerTitle: '出生年月', pickerValue: this.data.birthIndex.slice() });
|
||||
},
|
||||
onOpenGender: function () {
|
||||
var index = this.data.genderRange.indexOf(this.data.genderText);
|
||||
this.setData({ picker: 'gender', pickerTitle: '性别', pickerValue: [index < 0 ? 0 : index] });
|
||||
},
|
||||
onOpenRegion: function () {
|
||||
var index = this.data.regionIndex.slice();
|
||||
// A canceled draft may have changed the cities while the saved province stayed put.
|
||||
@@ -107,7 +159,9 @@ Page({
|
||||
},
|
||||
onPickerConfirm: function () {
|
||||
var e = { detail: { value: this.data.pickerValue } };
|
||||
if (this.data.picker === 'birth') this.onBirthChange(e); else this.onRegionChange(e);
|
||||
if (this.data.picker === 'birth') this.onBirthChange(e);
|
||||
else if (this.data.picker === 'gender') this.onGenderChange(e);
|
||||
else if (this.data.picker === 'region') this.onRegionChange(e);
|
||||
this.onPickerClose();
|
||||
},
|
||||
onPickerClose: function () { this.setData({ picker: '' }); },
|
||||
@@ -127,7 +181,8 @@ Page({
|
||||
|
||||
/* ─── 性别 ─── */
|
||||
onGenderChange: function (e) {
|
||||
this.setData({ genderText: this.data.genderRange[e.detail.value] });
|
||||
var value = e.detail.value, index = Array.isArray(value) ? value[0] : value;
|
||||
if (index === 0 || index === 1) this.setData({ genderText: this.data.genderRange[index] });
|
||||
},
|
||||
|
||||
/* ─── 地区:省份列滚动时联动城市列 ─── */
|
||||
@@ -148,25 +203,30 @@ Page({
|
||||
/* ─── 肤质轮播(H5 goSkin 的等价实现:轨道 translateX) ─── */
|
||||
applySkin: function (index) {
|
||||
var i = Math.max(0, Math.min(SKINS.length - 1, index));
|
||||
this.setData({ skinIndex: i, skinPercent: i * 100 });
|
||||
this.skinGesture = null;
|
||||
this.setData({ skinIndex: i, skinPercent: i * 100, skinDragOffset: 0, skinDragging: false });
|
||||
},
|
||||
onSkinPrev: function () { this.applySkin(this.data.skinIndex - 1); },
|
||||
onSkinNext: function () { this.applySkin(this.data.skinIndex + 1); },
|
||||
onSkinTouchStart: function (e) {
|
||||
var t = (e.changedTouches || e.touches || [])[0];
|
||||
this.touchX = t ? (t.clientX != null ? t.clientX : t.pageX) : 0;
|
||||
if (this.identityPending) return;
|
||||
var info = typeof my.getSystemInfoSync === 'function' ? my.getSystemInfoSync() : {};
|
||||
this.skinGesture = swipe.start(e, this.data.skinIndex, this.skinWidth || (info.windowWidth || 375) * 191 / 375, SKINS.length);
|
||||
},
|
||||
onSkinTouchEnd: function (e) {
|
||||
var t = (e.changedTouches || e.touches || [])[0];
|
||||
if (!t) return;
|
||||
var x = t.clientX != null ? t.clientX : t.pageX;
|
||||
var dx = x - (this.touchX || 0);
|
||||
if (Math.abs(dx) < 40) return;
|
||||
this.applySkin(this.data.skinIndex + (dx < 0 ? 1 : -1));
|
||||
onSkinTouchMove: function (e) {
|
||||
var value = swipe.move(this.skinGesture, e);
|
||||
if (!value) { this.onSkinTouchCancel(); return; }
|
||||
if (value.dragging) this.setData({ skinDragOffset: value.offset, skinDragging: true });
|
||||
},
|
||||
onSkinTouchEnd: function (e) { var value = swipe.end(this.skinGesture, e, false); if (value) this.applySkin(value.index); },
|
||||
onSkinTouchCancel: function () {
|
||||
this.skinGesture = null;
|
||||
if (this.data.skinDragging || this.data.skinDragOffset) this.setData({ skinDragOffset: 0, skinDragging: false });
|
||||
},
|
||||
|
||||
/* ─── 提交 → 拍照引导弹窗(H5 data-action="submit-profile") ─── */
|
||||
onSubmit: function () {
|
||||
if (this.identityPending) return;
|
||||
var d = this.data;
|
||||
if (!d.birth || !(d.age >= 1 && d.age <= 99)) {
|
||||
this.toast('请选择出生年月');
|
||||
@@ -189,7 +249,8 @@ Page({
|
||||
|
||||
// H5 data-action="start-camera":档案入全局态,进入拍照页
|
||||
onStartCamera: function () {
|
||||
if (!api.user()) { my.redirectTo({ url: '/pages/splash/splash' }); return; }
|
||||
if (this.identityPending) return;
|
||||
if (!api.user()) { my.redirectTo({ url: '/pages/login/login' }); return; }
|
||||
var d = this.data;
|
||||
getApp().globalData.profile = {
|
||||
real_age: d.age,
|
||||
@@ -197,7 +258,7 @@ Page({
|
||||
region: d.regionText,
|
||||
daily_skin_type: SKINS[d.skinIndex].name
|
||||
};
|
||||
my.setStorageSync({ key: 'juzi.taobao.profile.' + api.user().id, data: {
|
||||
if (api.user().kind !== 'taobao_guest') my.setStorageSync({ key: 'juzi.taobao.profile.' + api.user().id, data: {
|
||||
birth: d.birth, birthIndex: d.birthIndex, birthText: d.birthText, genderText: d.genderText,
|
||||
regionIndex: d.regionIndex, regionText: d.regionText, regionRange: d.regionRange, skinIndex: d.skinIndex
|
||||
} });
|
||||
@@ -206,4 +267,4 @@ Page({
|
||||
},
|
||||
|
||||
noop: function () {}
|
||||
});
|
||||
}, typeof my === 'undefined' ? {} : my));
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
{
|
||||
"defaultTitle": "1'me在线肌肤测试",
|
||||
"titleBarColor": "#101823",
|
||||
"backgroundColor": "#101823"
|
||||
"defaultTitle": "",
|
||||
"titleBarColor": "#FFFFFF",
|
||||
"backgroundColor": "#101823",
|
||||
"navigationBarTextStyle": "black",
|
||||
"navigationBarForceEnable": true,
|
||||
"transparentTitle": "always",
|
||||
"gestureBack": "NO"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user