Compare commits

...

13 Commits

Author SHA1 Message Date
kk 580350cb33 常见问答修改
Uni-app H5 Deploy (Prod + Staging) / deploy (release) Successful in 39s
2026-07-11 21:47:29 +08:00
kk 020e131df0 解决user_id为空提示
Uni-app H5 Deploy (Prod + Staging) / deploy (release) Successful in 42s
2026-07-11 20:43:56 +08:00
kk 533170f0a9 接口返回数据结构问题处理
Uni-app H5 Deploy (Prod + Staging) / deploy (release) Successful in 39s
2026-07-11 20:33:58 +08:00
kk b1924e2623 userId 改为依赖 this.userInfo.user_id
Uni-app H5 Deploy (Prod + Staging) / deploy (release) Successful in 47s
2026-07-11 20:29:00 +08:00
kk e12663052f 查询用户信息接口增加open_id参数
Uni-app H5 Deploy (Prod + Staging) / deploy (release) Successful in 47s
2026-07-11 20:21:58 +08:00
kk 842defea21 注册成功后保存用户id与手机号
Uni-app H5 Deploy (Prod + Staging) / deploy (release) Successful in 44s
2026-07-11 18:30:16 +08:00
kk d392ff98b7 apiBaseUrl改成https
Uni-app H5 Deploy (Prod + Staging) / deploy (release) Successful in 41s
2026-07-11 18:22:44 +08:00
kk 18870f3e7d 注册成功后保存user_info
Uni-app H5 Deploy (Prod + Staging) / deploy (release) Successful in 46s
2026-07-11 18:05:45 +08:00
kk 878778f8f9 解决code被重复使用的问题
Uni-app H5 Deploy (Prod + Staging) / deploy (release) Successful in 41s
2026-07-11 17:52:33 +08:00
kk eac66c965b 修复bug
Uni-app H5 Deploy (Prod + Staging) / deploy (release) Successful in 41s
2026-07-11 17:22:41 +08:00
kk 149fcdd231 优化交易完成页
Uni-app H5 Deploy (Prod + Staging) / deploy (release) Successful in 46s
2026-07-11 17:17:34 +08:00
kk 2978ac25f7 调整开门状态样式
Uni-app H5 Deploy (Prod + Staging) / deploy (release) Successful in 48s
2026-07-11 16:56:15 +08:00
kk a08afd0cfd 退出登录跳到扫码页 2026-07-11 16:49:32 +08:00
9 changed files with 185 additions and 76 deletions
+101 -19
View File
@@ -39,7 +39,9 @@
<text class="step-text">完成订单</text>
</view>
</view>
<view class="door-btn-close" @click="handleClose">返回首页</view>
<view class="door-btn-area">
<view class="door-btn-close" @click="handleClose">返回首页</view>
</view>
</view>
</view>
@@ -74,7 +76,9 @@
<text class="step-text">完成订单</text>
</view>
</view>
<view class="door-btn-close" @click="handleClose">返回首页</view>
<view class="door-btn-area">
<view class="door-btn-close" @click="handleClose">返回首页</view>
</view>
</view>
</view>
@@ -92,25 +96,34 @@
<!-- 订单信息 -->
<view class="door-order-card" v-if="orderInfo">
<!-- 商品列表 -->
<view class="door-product-list" v-if="orderInfo.products && orderInfo.products.length">
<view class="door-product-item" v-for="(p, idx) in orderInfo.products" :key="idx">
<image class="door-product-img" :src="p.image" mode="aspectFill" />
<view class="door-product-info">
<text class="door-product-name">{{ p.name }}</text>
<text class="door-product-price">¥{{ getDecimal(p.unit_price) }} x {{ p.qty }}</text>
</view>
</view>
</view>
<view class="door-order-row">
<text class="door-order-label">订单编号</text>
<text class="door-order-value">{{ orderInfo.order_no }}</text>
</view>
<view class="door-order-row">
<text class="door-order-label">商品数量</text>
<text class="door-order-value">{{ orderInfo.product_count || '-' }} </text>
<text class="door-order-value">{{ orderInfo.total_qty || '-' }} </text>
</view>
<view class="door-order-row total">
<text class="door-order-label">订单金额</text>
<text class="door-order-value door-price">¥{{ orderInfo.total_amount || '0.00' }}</text>
<text class="door-order-value door-price">¥{{ getDecimal(orderInfo.total_amount) }}</text>
</view>
</view>
<view class="door-order-card" v-else-if="orderLoading">
<text class="door-order-loading">加载订单信息...</text>
</view>
<view class="door-btn-detail" @click="goOrderDetail" v-if="orderInfo">查看订单详情</view>
<view class="door-btn-close" @click="handleClose">返回首页</view>
<view class="door-btn-area">
<view class="door-btn-detail" @click="goOrderDetail">查看订单详情</view>
<view class="door-btn-close" @click="handleClose">返回首页</view>
</view>
</view>
</view>
</view>
@@ -138,17 +151,19 @@ export default {
},
methods: {
// 外部调用:启动开门流程
async open() {
async open(userId) {
if (this.step) return;
this.cancelled = false;
this.step = 'opening';
const uid = userId || this.userId;
try {
const res = await post('/device-scan/open', {
device_id: this.deviceId,
door_index: 0,
user_id: this.userId
user_id: uid
});
const ret = res.data || {};
@@ -218,13 +233,21 @@ export default {
},
// 查询订单信息
async fetchOrder(flowId) {
async fetchOrder(orderId) {
if (!orderId || !this.userId) {
this.orderLoading = false;
return;
}
this.orderLoading = true;
try {
const res = await get('/order/getByFlowId', { flow_id: flowId });
const res = await get('/user/orders', {
page: 1,
limit: 1,
filter: JSON.stringify({ user_id: this.userId, order_id: orderId })
});
const ret = res.data || {};
if (ret.code === 0 && ret.data) {
this.orderInfo = ret.data;
if (ret.code === 0 && Array.isArray(ret.data) && ret.data.length > 0) {
this.orderInfo = ret.data[0];
}
} catch (e) {
console.error('[door-panel] 获取订单信息失败:', e);
@@ -233,10 +256,24 @@ export default {
}
},
// MongoDB Decimal128 转换
getDecimal(val) {
if (val == null) return '0.00';
if (typeof val === 'object' && val.$numberDecimal) return val.$numberDecimal;
return String(val);
},
// 跳转订单详情
goOrderDetail() {
const orderId = this.orderInfo.order_id || this.orderInfo.order_no;
uni.navigateTo({ url: `/pages/order/detail?order_id=${orderId}` });
if (this.orderInfo) {
const orderId = this.orderInfo.order_id || '';
uni.setStorageSync('__order_detail_cache', this.orderInfo);
uni.navigateTo({ url: `/pages/order/detail?order_id=${orderId}` });
} else if (this.flowId) {
uni.navigateTo({ url: `/pages/order/detail?flow_id=${this.flowId}` });
} else {
uni.showToast({ title: '无法获取订单信息', icon: 'none' });
}
},
// 关闭遮罩
@@ -507,6 +544,44 @@ export default {
padding: 24rpx 30rpx;
margin-top: 16rpx;
}
/* 商品列表 */
.door-product-list {
margin-bottom: 12rpx;
}
.door-product-item {
display: flex;
align-items: center;
padding: 12rpx 0;
}
.door-product-item + .door-product-item {
border-top: 1rpx solid rgba(255, 255, 255, 0.06);
}
.door-product-img {
width: 80rpx;
height: 80rpx;
border-radius: 10rpx;
background: rgba(255, 255, 255, 0.1);
flex-shrink: 0;
}
.door-product-info {
flex: 1;
margin-left: 16rpx;
display: flex;
flex-direction: column;
gap: 6rpx;
}
.door-product-name {
font-size: 26rpx;
color: rgba(255, 255, 255, 0.85);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.door-product-price {
font-size: 24rpx;
color: rgba(255, 255, 255, 0.45);
}
.door-order-row {
display: flex;
justify-content: space-between;
@@ -541,9 +616,17 @@ export default {
padding: 16rpx 0;
}
/* ====== 按钮区域 ====== */
.door-btn-area {
margin-top: 64rpx;
display: flex;
flex-direction: column;
align-items: center;
gap: 24rpx;
}
/* ====== 查看订单详情按钮 ====== */
.door-btn-detail {
margin-top: 32rpx;
font-size: 30rpx;
color: #fff;
padding: 24rpx 80rpx;
@@ -555,7 +638,6 @@ export default {
/* ====== 关闭按钮 ====== */
.door-btn-close {
margin-top: 24rpx;
font-size: 30rpx;
color: rgba(255, 255, 255, 0.85);
padding: 24rpx 100rpx;
+1 -1
View File
@@ -3,7 +3,7 @@
*/
const prod = {
// 业务 API 地址(vms-api
apiBaseUrl: 'http://101.200.86.98:4000',
apiBaseUrl: 'https://vmsapi.arklinksmart.cn',
// 网关地址(vms-gateway)— 用户授权、短信等接口走网关
apiGatewayUrl: 'https://gateway.arklinksmart.cn',
+35 -13
View File
@@ -72,6 +72,9 @@ export default {
* 发起 OAuth 授权(静默授权,只获取 openid)
*/
async startAuth() {
// 清除上次使用的 code 标记
uni.removeStorageSync('__used_auth_code');
const platform = detectPlatform();
const appNo = getAppNo(platform);
@@ -140,6 +143,20 @@ export default {
return;
}
// 防止 code 重复提交:先检查再标记
const usedCode = uni.getStorageSync('__used_auth_code');
if (usedCode === code) {
console.warn('[loading] code 已使用过,跳过');
return;
}
uni.setStorageSync('__used_auth_code', code);
// 立即清除 URL 中的 code 参数,避免刷新重复处理
const url = new URL(window.location.href);
url.searchParams.delete('code');
url.searchParams.delete('state');
window.history.replaceState({}, '', url.toString());
try {
this.statusText = '正在验证身份...';
@@ -165,12 +182,6 @@ export default {
return;
}
// 清除 URL 中的 code 参数,避免刷新重复处理
const url = new URL(window.location.href);
url.searchParams.delete('code');
url.searchParams.delete('state');
window.history.replaceState({}, '', url.toString());
// 存储 openid
uni.setStorageSync('openid', openid);
@@ -219,6 +230,9 @@ export default {
* 发起非静默授权获取用户信息(snsapi_userinfo,弹窗确认)
*/
async startUserInfoAuth() {
// 清除上次使用的 code 标记
uni.removeStorageSync('__used_auth_code');
const platform = detectPlatform();
const appNo = getAppNo(platform);
@@ -288,16 +302,24 @@ export default {
return;
}
// 防止 code 重复提交
const usedCode = uni.getStorageSync('__used_auth_code');
if (usedCode === code) {
console.warn('[loading] code 已使用过,跳过');
return;
}
uni.setStorageSync('__used_auth_code', code);
// 立即清除 URL 中的 code/action 参数,避免刷新重复处理
const url = new URL(window.location.href);
url.searchParams.delete('code');
url.searchParams.delete('state');
url.searchParams.delete('action');
window.history.replaceState({}, '', url.toString());
try {
this.statusText = '正在获取用户信息...';
// 清除 URL 中的 code/action 参数,避免刷新重复处理
const url = new URL(window.location.href);
url.searchParams.delete('code');
url.searchParams.delete('state');
url.searchParams.delete('action');
window.history.replaceState({}, '', url.toString());
// 用 code 调用 POST /api/v1/user/info 获取用户信息(必须用 POST,GET 是另一个端点)
console.log('[loading] 请求 POST user/info, app_no:', appNo, 'code:', code);
const res = await gatewayPost('/api/v1/user/info', {
+2 -22
View File
@@ -44,17 +44,7 @@ export default {
faqList: [
{
question: '如何购买商品?',
answer: '在首页浏览商品,点击商品卡片选择商品,确认后即可完成购买。支持微信支付和支付宝支付。',
expanded: false
},
{
question: '如何开门取货?',
answer: '下单成功后,点击首页底部中间的"开门"按钮,柜门会自动打开,取出商品后关闭柜门即可。',
expanded: false
},
{
question: '商品质量问题怎么办?',
answer: '如发现商品有质量问题,请在24小时内通过"联系客服"反馈,我们会为您办理退款或换货。',
answer: '使用微信或支付宝扫描柜体上的二维码,进入开门页面,如果是首次使用需先进行授权登录与注册,浏览设备中售卖的商品及价格,点击开门,待门锁打开后,拉开门选购商品,关门即完成购买,约1-2分钟内系统将自动识别并完成扣款。',
expanded: false
},
{
@@ -62,16 +52,6 @@ export default {
answer: '进入"我的"页面,点击"我的订单"可查看所有订单记录,包括待付款、已完成、已退款等状态。',
expanded: false
},
{
question: '忘记密码怎么办?',
answer: '在登录页面点击"忘记密码",输入注册手机号,通过短信验证码即可重置密码。',
expanded: false
},
{
question: '如何修改收货地址?',
answer: '进入"我的"页面,点击"收货地址",可以新增、编辑或删除收货地址信息。',
expanded: false
},
{
question: '支持哪些支付方式?',
answer: '目前支持微信支付、支付宝支付两种方式,后续将开通更多支付渠道。',
@@ -79,7 +59,7 @@ export default {
},
{
question: '退款多久到账?',
answer: '退款申请审核通过后,微信支付1-3个工作日到账,支付宝1-5个工作日到账,具体以银行处理时间为准。',
answer: '审核通过后,一般1-3个工作日到账,具体到账时间以支付渠道为准。',
expanded: false
}
]
+14 -11
View File
@@ -181,7 +181,7 @@ export default {
loading: false,
categoryList: [],
activeCategory: 'all',
userInfo: { nickname: '', phone: '', avatar: '' },
userInfo: { user_id: '', nickname: '', phone: '', avatar: '' },
avatarClickCount: 0,
avatarClickTimer: null,
fromPage: '', // 记录从哪个页面跳转过来的
@@ -199,8 +199,7 @@ export default {
},
computed: {
userId() {
const info = getCachedUserInfo();
return (info && info.user_id) || '';
return this.userInfo.user_id || '';
},
canGoBack() {
// 有上一页,或者从 tab 切换过来的,都可以返回
@@ -304,7 +303,7 @@ export default {
if (cached) {
this.userInfo = { ...this.userInfo, ...cached };
} else {
this.userInfo = { nickname: '', phone: '', avatar: '' };
this.userInfo = { user_id: '', nickname: '', phone: '', avatar: '' };
}
console.log('[index] this.userInfo:', JSON.stringify(this.userInfo, null, 2));
},
@@ -317,6 +316,7 @@ export default {
this.userInfo = { ...this.userInfo, ...cached };
} else {
this.userInfo = {
user_id: config.mockUser.user_info.user_id || '',
nickname: config.mockUser.user_info.nickname || '',
phone: config.mockUser.user_info.phone || '',
avatar: config.mockUser.user_info.avatar || '',
@@ -415,16 +415,18 @@ export default {
// 检查用户信息,若缓存中没有则调接口获取
if (!this.userId) {
try {
const infoRes = await gatewayGet('/api/v1/user/info', {});
const openId = uni.getStorageSync('openid') || '';
const infoRes = await gatewayGet('/api/v1/user/info', { open_id: openId });
const infoData = (infoRes.data || {}).data || {};
if (infoData.user_id) {
const userInfo = infoData.user_info || {};
if (userInfo.user_id) {
const existing = getCachedUserInfo() || {};
uni.setStorageSync('user_info', {
...existing,
user_id: infoData.user_id,
nickname: infoData.nickname || existing.nickname || '',
avatar: infoData.avatar || existing.avatar || '',
phone: infoData.phone || existing.phone || '',
user_id: userInfo.user_id,
nickname: userInfo.nickname || existing.nickname || '',
avatar: userInfo.avatar || existing.avatar || '',
phone: userInfo.phone || existing.phone || '',
});
this.loadUserInfo();
}
@@ -439,7 +441,7 @@ export default {
}
// 调用组件方法启动开门流程
this.$refs.doorPanel.open();
this.$refs.doorPanel.open(this.userId);
},
handleMenuClick(item) {
switch (item.action) {
@@ -500,6 +502,7 @@ export default {
uni.clearStorageSync();
uni.showToast({ title: '已退出登录', icon: 'success' });
setTimeout(() => {
uni.setStorageSync('__logout_flag', true);
uni.reLaunch({ url: '/pages/index/scan' });
}, 1000);
},
+6
View File
@@ -67,6 +67,12 @@ export default {
};
},
onLoad(options) {
// 退出登录后跳转过来,清除标记,停留在扫码页
if (uni.getStorageSync('__logout_flag')) {
uni.removeStorageSync('__logout_flag');
return;
}
// 1. 检查 JWT(App.vue 已处理首次跳转,这里兜底)
const token = uni.getStorageSync('token');
if (!token) {
+7 -1
View File
@@ -146,7 +146,13 @@ export default {
},
onLoad(options) {
this.orderId = options.order_id || '';
if (this.orderId) {
// 优先从缓存读取(订单列表/交易完成页跳转时已存入)
const cached = uni.getStorageSync('__order_detail_cache');
if (cached) {
this.order = cached;
this.orderId = cached.order_id || this.orderId;
uni.removeStorageSync('__order_detail_cache');
} else if (this.orderId) {
this.fetchOrder();
}
},
+1
View File
@@ -335,6 +335,7 @@ export default {
},
goToDetail(order) {
uni.setStorageSync('__order_detail_cache', order);
uni.navigateTo({ url: `/pages/order/detail?order_id=${order.order_id}` });
},
handlePay(order) {
+18 -9
View File
@@ -183,23 +183,32 @@ export default {
});
const ret = res.data || {};
console.log('[register] 注册响应:', JSON.stringify(ret, null, 2));
if (ret.code === 0 && ret.data) {
// 保存 token
uni.setStorageSync('token', ret.data.token);
// Mock 模式下,合并真实注册信息和 mock 配置
// 存储 user_info(接口返回 user_info.user_id 和 phone
const existing = uni.getStorageSync('user_info') || {};
const apiUserInfo = ret.data.user_info || {};
const userInfo = {
user_id: apiUserInfo.user_id || existing.user_id || '',
phone: apiUserInfo.phone || this.phone || existing.phone || '',
nickname: existing.nickname || '',
avatar: existing.avatar || '',
};
// Mock 模式下补充 mock 数据
if (config.mockWechatLogin) {
const mockInfo = config.mockUser.user_info;
const userInfo = {
user_id: ret.data.user_id || mockInfo.user_id,
phone: this.phone, // 使用真实注册的手机号
nickname: mockInfo.nickname,
avatar: mockInfo.avatar,
};
uni.setStorageSync('user_info', userInfo);
console.log('[register] Mock 模式,合并用户信息:', userInfo);
userInfo.nickname = userInfo.nickname || mockInfo.nickname;
userInfo.avatar = userInfo.avatar || mockInfo.avatar;
}
uni.setStorageSync('user_info', userInfo);
console.log('[register] 存储 user_info:', userInfo);
// 清除临时数据
uni.removeStorageSync('temp_token');
uni.removeStorageSync('auth_info');