ソースを参照

修改个人信息页面,下单后支付逻辑一级订单状态问题

sunshibo 1 ヶ月 前
コミット
8c10bc11cc

+ 54 - 0
api/dining.js

@@ -5,6 +5,43 @@ import { useUserStore } from '@/store/user.js';
 // 微信登录
 export const DiningUserWechatLogin = (params) => api.post({ url: '/dining/user/wechatLogin', params });
 
+// 获取用户信息(GET /dining/user/getUserInfo),登录后调用以拿到头像等
+export const GetUserInfo = () => api.get({ url: '/dining/user/getUserInfo' });
+
+// 更新用户个人信息(POST /dining/user/updateProfile,body 为 dto)
+export const PostUpdateProfile = (dto) =>
+  api.post({ url: '/dining/user/updateProfile', params: dto });
+
+// 文件上传(POST /file/upload,multipart,返回图片 URL/path 供 avatarUrl 使用)
+function uploadFileToServerImpl(filePath) {
+  const userStore = useUserStore();
+  return new Promise((resolve, reject) => {
+    uni.uploadFile({
+      url: BASE_API_URL+'/dining/file/upload',
+      filePath,
+      name: 'file',
+      header: { Authorization: userStore.getToken || '' },
+      success: (res) => {
+        if (res.statusCode === 200) {
+          try {
+            const data = typeof res.data === 'string' ? JSON.parse(res.data) : res.data;
+            const payload = data?.data ?? data;
+            const url = payload?.url ?? payload?.path ?? payload?.relativePath ?? (typeof payload === 'string' ? payload : '');
+            resolve(url || '');
+          } catch (e) {
+            reject(e);
+          }
+        } else {
+          reject(new Error(res.data || '上传失败'));
+        }
+      },
+      fail: reject
+    });
+  });
+}
+export const uploadFileToServer = uploadFileToServerImpl;
+export const uploadFileToSever = uploadFileToServerImpl; // 兼容拼写错误
+
 // 点餐页数据(入参 dinerCount 就餐人数、tableId 桌号)
 export const DiningOrderFood = (params) => api.get({ url: '/store/dining/page-info', params });
 
@@ -74,6 +111,23 @@ export const PostOrderPay = (params) =>
     }
   });
 
+// 根据第三方订单号查询支付结果(GET /payment/searchOrderByOutTradeNoPath,入参 payType、transactionId)
+// 支付页调用时不显示全局 loading
+export const GetSearchOrderByOutTradeNoPath = (params) =>
+  api.get({
+    url: '/payment/searchOrderByOutTradeNoPath',
+    params: {
+      payType: params.payType ?? 'wechatPayMininProgram',
+      transactionId: params.transactionId
+    },
+    loading: false
+  });
+
+// 订单翻转/完成(POST /store/order/complete/{orderId},入参 orderId)
+// 支付页调用时不显示全局 loading
+export const PostOrderComplete = (orderId) =>
+  api.post({ url: `/store/order/complete/${encodeURIComponent(orderId)}`, loading: false });
+
 /**
  * 订单 SSE 接口配置(GET /store/order/sse/{tableId})
  * 仅提供 URL 与 header,实际连接请使用 utils/sse.js 的 createSSEConnection 封装

+ 46 - 2
components/TabBar.vue

@@ -9,8 +9,8 @@
 				<view class="text" :class="{ sele: getPath === menus[0].link }">{{ menus[0].title }}</view>
 			</view>
 
-			<!-- 中间:扫码点餐 - 特殊圆形按钮 -->
-			<view class="menu-center" @click="go(menus[1].link)">
+			<!-- 中间:扫码点餐 - 点击先调起微信扫一扫 -->
+			<view class="menu-center" @click="handleScanOrder">
 				<view class="scan-btn">
 					<image :src="getFileUrl(menus[1].img)" mode="aspectFill"></image>
 				</view>
@@ -30,6 +30,7 @@
 <script setup>
 import { computed, unref } from 'vue';
 import { getFileUrl } from '@/utils/file.js';
+import { SCAN_QR_CACHE } from '@/settings/enums.js';
 
 const menus = [
 	{ title: '首页', link: '/pages/index/index', img: 'img/tabbar/index1.png', imgon: 'img/tabbar/index2.png' },
@@ -45,6 +46,49 @@ const getTabBarWrapStyle = computed(() => ({
 	position: props.fixed ? 'fixed' : 'relative'
 }));
 
+// 解析扫码内容:s=店铺id,t=桌号id
+function parseScanResult(str) {
+	const trim = (v) => (v == null ? '' : String(v).trim());
+	let storeId = '';
+	let tableId = '';
+	if (!str) return { storeId, tableId };
+	const s = trim(str);
+	try {
+		const parts = s.split('&');
+		parts.forEach((pair) => {
+			const [k, v] = pair.split('=').map((x) => (x ? decodeURIComponent(String(x).trim()) : ''));
+			if (k === 's' || k === 'storeId' || k === 'store_id') storeId = trim(v);
+			if (k === 't' || k === 'tableId' || k === 'table_id' || k === 'tableid') tableId = trim(v);
+		});
+	} catch (err) {
+		console.warn('parseScanResult', err);
+	}
+	return { storeId, tableId };
+}
+
+// 点击扫码点餐:先调起微信扫描二维码,解析后缓存再跳转
+function handleScanOrder() {
+	uni.scanCode({
+		scanType: ['qrCode'],
+		success: (res) => {
+			const result = res?.result ?? '';
+			const { storeId, tableId } = parseScanResult(result);
+			const payload = { raw: result, storeId, tableId };
+			uni.setStorageSync(SCAN_QR_CACHE, JSON.stringify(payload));
+			if (storeId) uni.setStorageSync('currentStoreId', storeId);
+			if (tableId) uni.setStorageSync('currentTableId', tableId);
+			if (unref(getPath) !== menus[1].link) {
+				uni.switchTab({ url: menus[1].link });
+			}
+		},
+		fail: (err) => {
+			if (err?.errMsg && !String(err.errMsg).includes('cancel')) {
+				uni.showToast({ title: '扫码失败', icon: 'none' });
+			}
+		}
+	});
+}
+
 // 底部 tabBar 跳转(需在 pages.json 中配置 tabBar.list)
 function go(url) {
 	if (url === unref(getPath)) return;

+ 4 - 1
components/Upload/upload.js

@@ -13,7 +13,7 @@ const userStore = useUserStore();
 
 let APIURLSET = uni.getStorageSync('APIURL')
 let APIURL = UPLOAD_URL
-if(APIURLSET) APIURL = `${APIURLSET}/File/UploadImg`;
+if (APIURLSET) APIURL = `${String(APIURLSET).replace(/\/$/, '')}/file/upload`;
 
 export function chooseAndUploadFile(type, opts, dir = '') {
 	if (type === 'image') {
@@ -346,6 +346,7 @@ export const uploadFile = (params = {}) => {
 							title: jsonData.msg,
 							success: (res) => {}
 						});
+						reject(new Error(jsonData.msg || '上传失败'));
 					}
 				} else {
 					setTimeout(() => {
@@ -354,6 +355,7 @@ export const uploadFile = (params = {}) => {
 							success: (res) => {}
 						});
 					}, 500);
+					reject(new Error('接口错误'));
 				}
 			},
 			fail: (err) => {
@@ -363,6 +365,7 @@ export const uploadFile = (params = {}) => {
 						success: (res) => {}
 					});
 				}, 500);
+				reject(err || new Error('上传失败'));
 			},
 			complete: () => {
 				uni.hideLoading();

+ 6 - 0
pages.json

@@ -54,6 +54,12 @@
 			}
 		},
 		{
+			"path": "pages/paymentSuccess/index",
+			"style": {
+				"navigationBarTitleText": "支付"
+			}
+		},
+		{
 			"path": "pages/personal/userInfo",
 			"style": {
 				"navigationBarTitleText": "个人信息"

+ 17 - 3
pages/components/LoginModal.vue

@@ -40,7 +40,7 @@
 <script setup>
 import { ref, computed } from 'vue';
 import BasicModal from '@/components/Modal/BasicModal.vue';
-import { DiningUserWechatLogin } from '@/api/dining.js';
+import { DiningUserWechatLogin, GetUserInfo } from '@/api/dining.js';
 import { useUserStore } from '@/store/user.js';
 
 const props = defineProps({
@@ -103,6 +103,15 @@ const handleGetPhoneNumber = async (e) => {
 
           if (res && res.token) {
             userStore.login(res);
+            try {
+              const userRes = await GetUserInfo();
+              const raw = userRes?.data ?? userRes?.result ?? userRes?.userInfo ?? userRes?.user ?? userRes ?? {};
+              const userData = typeof raw === 'object' && raw !== null ? raw : {};
+              const merged = { ...userStore.getUserInfo, ...userData };
+              userStore.setUserInfo(merged);
+            } catch (err) {
+              console.warn('获取用户信息失败', err);
+            }
             getOpen.value = false;
             emit('success', res);
           } else {
@@ -155,12 +164,17 @@ const handleUserAgreement = () => {
 </script>
 
 <style scoped lang="scss">
+/* 优先级最高,避免被 tabbar 等挡住 */
 :deep(.uni-popup) {
-  z-index: 100 !important;
+  z-index: 99999 !important;
 }
 
 :deep(.uni-popup__wrapper) {
-  z-index: 100 !important;
+  z-index: 99999 !important;
+}
+
+:deep(.uni-popup__mask) {
+  z-index: 99998 !important;
 }
 
 .login-modal {

+ 128 - 0
pages/paymentSuccess/index.vue

@@ -0,0 +1,128 @@
+<template>
+  <view class="content">
+    <view class="status-icon-wrap">
+      <image v-if="!loading" :src="getFileUrl('img/icon/success.png')" mode="widthFix" class="status-img"></image>
+      <view v-else class="loading-dot"></view>
+    </view>
+    <view class="status-title">{{ loading ? '支付中' : '支付成功' }}</view>
+    <view class="status-desc">{{ loading ? '正在确认支付结果,请稍候...' : '感谢您的用餐' }}</view>
+    <view v-if="!loading" class="success-btn hover-active" @click="handleBackHome">返回首页</view>
+  </view>
+</template>
+
+<script setup>
+import { onLoad } from "@dcloudio/uni-app";
+import { ref } from "vue";
+import { go } from "@/utils/utils.js";
+import { getFileUrl } from "@/utils/file.js";
+import { GetSearchOrderByOutTradeNoPath, PostOrderComplete } from "@/api/dining.js";
+
+const orderId = ref('');
+const payType = ref('wechatPayMininProgram');
+const transactionId = ref('');
+const loading = ref(true);
+
+async function queryPayResult() {
+  const tid = transactionId.value;
+  const ptype = payType.value;
+  const oid = orderId.value;
+  if (!tid) {
+    loading.value = false;
+    return;
+  }
+  try {
+    await GetSearchOrderByOutTradeNoPath({
+      payType: ptype,
+      transactionId: tid
+    });
+    if (oid) {
+      try {
+        await PostOrderComplete(oid);
+      } catch (e) {
+        console.warn('订单翻转接口调用失败', e);
+      }
+    }
+    loading.value = false;
+  } catch (e) {
+    loading.value = false;
+  }
+}
+
+onLoad((options) => {
+  orderId.value = options?.id ?? '';
+  payType.value = options?.payType ?? 'wechatPayMininProgram';
+  transactionId.value = options?.transactionId ?? '';
+  queryPayResult();
+});
+
+// 返回首页:清空页面栈,避免返回到支付/确认订单页
+const handleBackHome = () => {
+  go('/pages/index/index', 'reLaunch');
+};
+</script>
+
+<style lang="scss" scoped>
+.content {
+  background-color: #fff;
+  width: 100%;
+  min-height: 100vh;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  padding-top: 120rpx;
+}
+
+.status-icon-wrap {
+  width: 120rpx;
+  height: 120rpx;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+.status-img {
+  width: 120rpx;
+  height: 120rpx;
+}
+
+.loading-dot {
+  width: 80rpx;
+  height: 80rpx;
+  border: 6rpx solid #e5e5e5;
+  border-top-color: #07c160;
+  border-radius: 50%;
+  animation: spin 0.8s linear infinite;
+}
+
+@keyframes spin {
+  to {
+    transform: rotate(360deg);
+  }
+}
+
+.status-title {
+  font-weight: bold;
+  font-size: 40rpx;
+  color: #151515;
+  margin-top: 40rpx;
+}
+
+.status-desc {
+  font-size: 28rpx;
+  color: #666666;
+  margin-top: 24rpx;
+}
+
+.success-btn {
+  margin-top: 80rpx;
+  width: 400rpx;
+  height: 88rpx;
+  border: 2rpx solid #e5e5e5;
+  border-radius: 44rpx;
+  font-size: 30rpx;
+  color: #333;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+</style>

+ 45 - 7
pages/personal/index.vue

@@ -12,10 +12,10 @@
 		<view class="content-box">
 			<view class="card">
 				<view class="card-header">
-					<image :src="getFileUrl('img/personal/userDemo.png')" mode="widthFix" class="card-header-icon">
+					<image :src="avatarDisplayUrl" mode="aspectFill" class="card-header-icon">
 					</image>
 					<view class="card-header-title" hover-class="none" @click="handleLoginClick">{{ userStore.getToken ?
-						(userStore.getUserInfo?.nickname || '用户') : '立即登录' }}</view>
+						(displayNickName) : '立即登录' }}</view>
 				</view>
 
 				<!-- 列表 -->
@@ -81,15 +81,54 @@
 </template>
 
 <script setup>
-import { ref } from 'vue';
+import { ref, computed } from 'vue';
+import { onShow } from '@dcloudio/uni-app';
 import TabBar from '@/components/TabBar.vue';
 import LoginModal from '@/pages/components/LoginModal.vue';
 import { getFileUrl } from '@/utils/file.js';
 import { useUserStore } from '@/store/user.js';
 import { go } from '@/utils/utils.js';
+import { USER_INFO } from '@/settings/enums.js';
 
 const userStore = useUserStore();
 const showLoginModal = ref(false);
+const userInfoSync = ref({});
+
+function syncUserFromStorage() {
+	try {
+		const raw = uni.getStorageSync(USER_INFO);
+		userInfoSync.value = raw && typeof raw === 'object' ? raw : {};
+	} catch (e) {
+		userInfoSync.value = {};
+	}
+}
+
+onShow(() => {
+	syncUserFromStorage();
+});
+syncUserFromStorage();
+
+// 展示用用户信息(优先 onShow 同步的 storage,保证登录后关弹窗能刷新)
+const displayUserInfo = computed(() => {
+	const sync = userInfoSync.value;
+	if (sync && typeof sync === 'object' && Object.keys(sync).length > 0) return sync;
+	return userStore.getUserInfo || {};
+});
+
+// 头像:绑定缓存的 avatarUrl;本地/临时路径(如 127.0.0.1/tmp、wxfile://)会报 500,改用默认图
+const avatarDisplayUrl = computed(() => {
+	const url = (displayUserInfo.value?.avatarUrl ?? '').trim();
+	if (!url) return getFileUrl('img/personal/userDemo.png');
+	const isLocalTemp = /127\.0\.0\.1|localhost|^\/tmp\/|wxfile:\/\/|^blob:/i.test(url);
+	if (isLocalTemp) return getFileUrl('img/personal/userDemo.png');
+	if (/^(https?|data):/i.test(url)) return url;
+	return getFileUrl(url);
+});
+
+const displayNickName = computed(() => {
+	const info = displayUserInfo.value;
+	return info.nickName ?? info.nickname ?? info.name ?? '用户';
+});
 
 // 处理登录点击
 const handleLoginClick = () => {
@@ -98,10 +137,9 @@ const handleLoginClick = () => {
 	}
 };
 
-// 登录成功回调
-const handleLoginSuccess = (res) => {
-	console.log('登录成功:', res);
-	// 登录成功后可以刷新用户信息等操作
+// 登录成功回调:立即从 storage 同步用户信息,保证头像/昵称马上更新
+const handleLoginSuccess = () => {
+	syncUserFromStorage();
 };
 
 // 取消登录回调

+ 124 - 33
pages/personal/userInfo.vue

@@ -7,7 +7,7 @@
 				<view class="user-item-label">头像</view>
 				<view class="user-item-value">
 					<!-- #ifdef MP-WEIXIN -->
-					<button class="avatar-btn" open-type="chooseAvatar" @chooseavatar="onChooseAvatar">
+					<button class="avatar-btn" open-type="chooseAvatar" @chooseavatar="onChooseAvatar" :disabled="uploadingAvatar">
 						<view class="avatar-container">
 							<image 
 								:src="formData.avatar || getFileUrl('img/personal/userDemo.png')" 
@@ -94,6 +94,9 @@
 				</view>
 			</view>
 		</view>
+		<view class="submit-wrap">
+			<view class="submit-btn hover-active" @click="handleConfirm">确定</view>
+		</view>
 	</view>
 </template>
 
@@ -102,8 +105,27 @@ import { ref, onMounted } from 'vue';
 import { useUserStore } from '@/store/user.js';
 import { getFileUrl } from '@/utils/file.js';
 import { go } from '@/utils/utils.js';
+import { PostUpdateProfile, uploadFileToServer } from '@/api/dining.js';
+import { UPLOAD } from '@/settings/siteSetting.js';
 
 const userStore = useUserStore();
+const uploadingAvatar = ref(false);
+
+// 选择完头像后直接上传到 /file/upload,返回完整图片 URL
+async function doUploadAvatar(tempPath) {
+	const res = await uploadFileToServer(tempPath);
+	if (!res || typeof res !== 'string') return '';
+	const p = res.trim();
+	if (/^https?:\/\//i.test(p)) return p;
+	const base = (UPLOAD || '').replace(/\/$/, '');
+	return base ? base + p.replace(/^\//, '') : p;
+}
+
+function isTempAvatarPath(url) {
+	if (!url || typeof url !== 'string') return false;
+	const u = url.trim();
+	return !/^https?:\/\//i.test(u) || /127\.0\.0\.1|localhost|\/tmp\/|wxfile:\/\//i.test(u);
+}
 
 // 性别选项
 const genderOptions = [
@@ -111,27 +133,29 @@ const genderOptions = [
 	{ value: 'female', label: '女' }
 ];
 
-// 表单数据
+// 表单数据(userId 用于更新资料接口必填)
 const formData = ref({
+	userId: null,
 	avatar: '',
 	nickname: '',
-	gender: 'male', // 默认选中男
+	gender: 'male',
 	birthday: '',
 	phone: '1510987760'
 });
 
-// 初始化用户信息
+// 初始化用户信息(头像用 avatarUrl/avatar,与缓存字段一致)
 onMounted(() => {
-	const userInfo = userStore.getUserInfo;
-	if (userInfo) {
-		formData.value = {
-			avatar: userInfo.avatar || '',
-			nickname: userInfo.nickname || '',
-			gender: userInfo.gender || 'male',
-			birthday: userInfo.birthday || '',
-			phone: userInfo.phone || '1510987760'
-		};
-	}
+	const userInfo = userStore.getUserInfo || {};
+	const g = userInfo.gender;
+	const genderVal = g === '女' || g === 'female' ? 'female' : 'male';
+	formData.value = {
+		userId: userInfo.id ?? userInfo.userId ?? null,
+		avatar: userInfo.avatarUrl ?? userInfo.avatar ?? '',
+		nickname: userInfo.nickName ?? userInfo.nickname ?? '',
+		gender: genderVal,
+		birthday: userInfo.birthday ?? '',
+		phone: userInfo.phone ?? userInfo.mobile ?? '1510987760'
+	};
 });
 
 // 处理头像点击
@@ -156,19 +180,32 @@ const handleAvatarClick = () => {
 	});
 };
 
-// 微信小程序:通过「头像昵称填写」能力选择头像(button open-type="chooseAvatar" 触发)
-// 注意:e.detail.avatarUrl 为临时路径,需上传到服务器后保存永久链接
-const onChooseAvatar = (e) => {
-	if (e.detail && e.detail.avatarUrl) {
-		formData.value.avatar = e.detail.avatarUrl;
-		uni.showToast({ title: '头像已选择', icon: 'success' });
+// 选择头像后直接上传到服务器接口 /file/upload,把返回的 url 写入表单
+async function uploadAvatarAndSet(tempPath) {
+	if (!tempPath) return;
+	try {
+		uploadingAvatar.value = true;
+		const url = await doUploadAvatar(tempPath);
+		formData.value.avatar = url || tempPath;
+		uni.showToast({ title: url ? '头像上传成功' : '头像上传未返回地址', icon: url ? 'success' : 'none' });
+	} catch (err) {
+		uni.showToast({ title: err?.message || '头像上传失败', icon: 'none' });
+		formData.value.avatar = tempPath;
+	} finally {
+		uploadingAvatar.value = false;
 	}
+}
+
+// 微信小程序:选择头像后立即上传,防止重复点击导致 another chooseAvatar is in progress
+const onChooseAvatar = async (e) => {
+	if (uploadingAvatar.value) return;
+	const tempPath = e.detail?.avatarUrl;
+	if (tempPath) await uploadAvatarAndSet(tempPath);
 };
 
 // 获取微信头像(仅非 MP-WEIXIN 时从 actionSheet 进入;MP-WEIXIN 已改用模板内 button chooseAvatar)
 const handleWechatAvatar = () => {
 	// #ifdef MP-WEIXIN
-	// 微信已废弃 getUserProfile,请直接点击页面头像区域,使用「选择头像」按钮
 	uni.showToast({
 		title: '请点击上方头像区域选择',
 		icon: 'none'
@@ -182,17 +219,14 @@ const handleWechatAvatar = () => {
 	// #endif
 };
 
-// 选择相册
+// 选择相册:选择后立即上传
 const handleChooseAlbum = () => {
 	uni.chooseImage({
 		count: 1,
 		sizeType: ['original', 'compressed'],
 		sourceType: ['album'],
-		success: (res) => {
-			const tempFilePath = res.tempFilePaths[0];
-			// 这里可以调用上传接口
-			// 暂时直接使用本地路径
-			formData.value.avatar = tempFilePath;
+		success: async (res) => {
+			await uploadAvatarAndSet(res.tempFilePaths[0]);
 		},
 		fail: (err) => {
 			console.log('选择相册失败', err);
@@ -200,17 +234,14 @@ const handleChooseAlbum = () => {
 	});
 };
 
-// 拍照
+// 拍照:选择后立即上传
 const handleTakePhoto = () => {
 	uni.chooseImage({
 		count: 1,
 		sizeType: ['original', 'compressed'],
 		sourceType: ['camera'],
-		success: (res) => {
-			const tempFilePath = res.tempFilePaths[0];
-			// 这里可以调用上传接口
-			// 暂时直接使用本地路径
-			formData.value.avatar = tempFilePath;
+		success: async (res) => {
+			await uploadAvatarAndSet(res.tempFilePaths[0]);
 		},
 		fail: (err) => {
 			console.log('拍照失败', err);
@@ -218,6 +249,47 @@ const handleTakePhoto = () => {
 	});
 };
 
+// 确定:先默认调用一次上传头像接口,等待返回后,再把返回的图片 url 传给 dining/user/updateProfile
+const handleConfirm = async () => {
+	const uid = formData.value.userId;
+	if (uid == null || uid === '') {
+		uni.showToast({ title: '请先登录', icon: 'none' });
+		return;
+	}
+	let avatarUrl = formData.value.avatar || '';
+	// 有头像且为本地临时路径时,先调用自己的上传接口,等待返回后再走更新资料
+	if (avatarUrl && isTempAvatarPath(avatarUrl)) {
+		try {
+			avatarUrl = await doUploadAvatar(avatarUrl);
+			if (!avatarUrl) {
+				uni.showToast({ title: '头像上传未返回地址', icon: 'none' });
+				return;
+			}
+		} catch (err) {
+			uni.showToast({ title: err?.message || '头像上传失败', icon: 'none' });
+			return;
+		}
+	}
+	const genderStr = formData.value.gender === 'female' ? '女' : '男';
+	const dto = { userId: Number(uid) };
+	if (avatarUrl) dto.avatarUrl = avatarUrl;
+	if (formData.value.nickname) dto.nickName = formData.value.nickname;
+	dto.gender = genderStr;
+	if (formData.value.birthday) dto.birthday = formData.value.birthday;
+	try {
+		await PostUpdateProfile(dto);
+		userStore.setUserInfo({
+			...userStore.getUserInfo,
+			avatarUrl: avatarUrl || formData.value.avatar,
+			nickName: formData.value.nickname
+		});
+		uni.showToast({ title: '保存成功', icon: 'success' });
+		setTimeout(() => uni.navigateBack(), 1500);
+	} catch (e) {
+		uni.showToast({ title: e?.message || '保存失败', icon: 'none' });
+	}
+};
+
 // 处理生日选择
 const handleBirthdayChange = (e) => {
 	formData.value.birthday = e.detail.value;
@@ -238,6 +310,22 @@ const handleChangePhone = () => {
 	padding: 20rpx;
 }
 
+.submit-wrap {
+	margin-top: 60rpx;
+	padding: 0 20rpx;
+}
+
+.submit-btn {
+	width: 100%;
+	height: 88rpx;
+	line-height: 88rpx;
+	text-align: center;
+	font-size: 32rpx;
+	color: #fff;
+	background: linear-gradient(90deg, #FCB73F 0%, #FC743D 100%);
+	border-radius: 44rpx;
+}
+
 .user-info {
 	width: 100%;
 	border-radius: 23rpx;
@@ -281,6 +369,9 @@ const handleChangePhone = () => {
 	&::after {
 		border: none;
 	}
+	&[disabled] {
+		opacity: 0.7;
+	}
 }
 
 .avatar-container {

+ 5 - 2
pages/placeOrder/index.vue

@@ -258,12 +258,15 @@ const handleConfirmPay = async () => {
       provider: 'wxpay',
       timeStamp:res.timestamp,
       nonceStr:res.nonce,
-      package: res.prepayId,
+      package:res.prepayId,
       signType:res.signType,
       paySign:res.sign,
       success: () => {
         uni.showToast({ title: '支付成功', icon: 'success' });
-        setTimeout(() => go(`/pages/result/index?id=${encodeURIComponent(orderId)}`), 1500);
+        const payType = 'wechatPayMininProgram';
+        const transactionId = orderNo;
+        const q = [`id=${encodeURIComponent(orderId)}`, `payType=${encodeURIComponent(payType)}`, `transactionId=${encodeURIComponent(transactionId)}`];
+        setTimeout(() => go(`/pages/paymentSuccess/index?${q.join('&')}`), 1500);
       },
       fail: (err) => {
         const msg = err?.errMsg ?? err?.message ?? '支付失败';