Lufaneng 11 months ago
parent 3d4a802ba5
commit d7c95a7d71

@ -1,5 +1,5 @@
{ {
"name": "lift-wechat", "name": "lift-drawing-wechat",
"version": "1.0.0", "version": "1.0.0",
"description": "电梯图纸生成", "description": "电梯图纸生成",
"main": "main.js", "main": "main.js",
@ -10,7 +10,7 @@
"commit": "git-cz" "commit": "git-cz"
}, },
"keywords": [ "keywords": [
"卡叔电梯" "智能电梯图纸生成"
], ],
"author": "Lufaneng", "author": "Lufaneng",
"license": "MIT", "license": "MIT",

@ -49,7 +49,8 @@
"needLogin": true, "needLogin": true,
"style" : "style" :
{ {
"navigationBarTitleText" : "我的" "navigationBarTitleText" : "我的",
"enablePullDownRefresh":true
} }
}, },
{ {

@ -1,379 +1,316 @@
<template> <template>
<view class="container"> <view class="container">
<!-- 顶部筛选标签 --> <!-- 顶部筛选标签 -->
<view class="filter-tabs"> <uv-sticky bgColor="#fff" offsetTop="0">
<view <div class="tabBox">
class="tab-item" <div class="tabItem" :class="{selected:active==='全部'}" @click="switchTab('全部')"></div>
:class="{ active: activeTab === 'all' }" <div class="tabItem" :class="{selected:active==='生成'}" @click="switchTab('生成')"></div>
@click="switchTab('all')" <div class="tabItem" :class="{selected:active==='会员'}" @click="switchTab('会员')"></div>
> </div>
全部 </uv-sticky>
</view> <!-- 消费记录列表 -->
<view <view class="consumption-list">
class="tab-item" <view class="consumption-item" v-for="item in list" :key="item.id">
:class="{ active: activeTab === 'generate' }" <!-- 消费项目和金额 -->
@click="switchTab('generate')" <view class="item-header">
> <view class="item-title">{{ item.type }}</view>
生成 <view class="item-amount" :class="{
</view>
<view
class="tab-item"
:class="{ active: activeTab === 'member' }"
@click="switchTab('member')"
>
会员
</view>
<view
class="tab-item"
:class="{ active: activeTab === 'other' }"
@click="switchTab('other')"
>
其他
</view>
</view>
<!-- 消费记录列表 -->
<view class="consumption-list">
<view
class="consumption-item"
v-for="item in filteredConsumptions"
:key="item.id"
>
<!-- 消费项目和金额 -->
<view class="item-header">
<view class="item-title">{{ item.title }}</view>
<view
class="item-amount"
:class="{
positive: item.amount > 0, positive: item.amount > 0,
negative: item.amount < 0, negative: item.amount < 0,
}" }">
> {{ item.amount > 0 ? "+" : "-" }}¥{{
{{ item.amount > 0 ? "+" : "-" }}¥{{
Math.abs(item.amount).toFixed(2) Math.abs(item.amount).toFixed(2)
}} }}
</view> </view>
</view> </view>
<!-- 消费详情 --> <!-- 消费详情 -->
<view class="item-details"> <view class="item-details">
<view class="detail-row"> <view class="detail-row">
<text class="detail-label">消费编号:</text> <text class="detail-label">消费编号:</text>
<text class="detail-value">{{ item.consumptionNumber }}</text> <text class="detail-value">{{ item.order_no }}</text>
</view> </view>
<view class="detail-row"> <view class="detail-row">
<text class="detail-label">消费时间:</text> <text class="detail-label">消费时间:</text>
<text class="detail-value">{{ item.consumptionTime }}</text> <text class="detail-value">{{ item.finished_at ? dayjs(item.finished_at * 1000).format('YYYY-MM-DD HH:mm:ss') :'' }}</text>
</view> </view>
<view class="balance-row"> <view class="balance-row">
<view class="balance-item"> <view class="balance-item">
<text class="balance-label">账前金额:</text> <text class="balance-label">账前金额:</text>
<text class="balance-value"> <text class="balance-value">
¥{{ item.beforeBalance.toFixed(2) }} ¥{{ item.balance_before_cents / 100 }}
</text> </text>
</view> </view>
<view class="balance-item"> <view class="balance-item">
<text class="balance-label">账后金额:</text> <text class="balance-label">账后金额:</text>
<text class="balance-value"> <text class="balance-value">
¥{{ item.afterBalance.toFixed(2) }} ¥{{ item.balance_after_cents / 100 }}
</text> </text>
</view> </view>
</view> </view>
</view> </view>
</view> </view>
</view> </view>
<!-- 空状态 --> <!-- 空状态 -->
<view v-if="filteredConsumptions.length === 0" class="empty-state"> <view v-if="list.length === 0 && loadingStatus !== 'loading'" class="empty-state">
<u-icon name="file-text" size="60" color="#ccc"></u-icon> <u-icon name="file-text" size="60" color="#ccc"></u-icon>
<text class="empty-text">暂无消费记录</text> <text class="empty-text">暂无消费记录</text>
</view> </view>
<uv-load-more v-else :status="loadingStatus" />
<!-- 加载更多 -->
<view v-if="hasMore && filteredConsumptions.length > 0" class="load-more"> </view>
<u-button
type="default"
size="small"
:loading="loading"
@click="loadMore"
>
{{ loading ? "加载中..." : "加载更多" }}
</u-button>
</view>
</view>
</template> </template>
<script setup> <script setup>
import { computed, onMounted, ref } from "vue"; import {
import { onShow } from "@dcloudio/uni-app"; computed,
import { useAuthStore, useUserStore } from "@/store"; onMounted,
import HeaderCustom from "/components/HeaderCustom/HeaderCustom.vue"; ref
import RPC from "@/utils/request"; } from "vue";
import {
const authStore = useAuthStore(); onShow,
const userStore = useUserStore(); onReachBottom
} from "@dcloudio/uni-app";
// import {
const activeTab = ref("all"); useAuthStore,
const loading = ref(false); useUserStore
const hasMore = ref(true); } from "@/store";
const consumptions = ref([ import HeaderCustom from "/components/HeaderCustom/HeaderCustom.vue";
{ import RPC from "@/utils/request";
id: 1, import dayjs from "dayjs";
title: "乘客电梯图纸生成",
amount: -30.0, const authStore = useAuthStore();
consumptionNumber: "CONS20230516024", const userStore = useUserStore();
consumptionTime: "2025-05-16 14:30:22",
beforeBalance: 1250.0, const active = ref('全部')
afterBalance: 1220.0,
type: "generate", const list = ref([{
}, id: 1,
{ title: "乘客电梯图纸生成",
id: 2, amount: -30.0,
title: "货运电梯图纸生成", consumptionNumber: "CONS20230516024",
amount: -45.0, consumptionTime: "2025-05-16 14:30:22",
consumptionNumber: "CONS20230516024", beforeBalance: 1250.0,
consumptionTime: "2025-05-16 14:30:22", afterBalance: 1220.0,
beforeBalance: 1295.0, type: "generate",
afterBalance: 1250.0, },
type: "generate", {
}, id: 2,
{ title: "货运电梯图纸生成",
id: 3, amount: -45.0,
title: "包年会员订阅", consumptionNumber: "CONS20230516024",
amount: -45.0, consumptionTime: "2025-05-16 14:30:22",
consumptionNumber: "CONS20230516024", beforeBalance: 1295.0,
consumptionTime: "2025-05-16 14:30:22", afterBalance: 1250.0,
beforeBalance: 1295.0, type: "generate",
afterBalance: 1250.0, },
type: "member", {
}, id: 3,
{ title: "包年会员订阅",
id: 4, amount: -45.0,
title: "账户充值", consumptionNumber: "CONS20230516024",
amount: 1000.0, consumptionTime: "2025-05-16 14:30:22",
consumptionNumber: "CONS20230516024", beforeBalance: 1295.0,
consumptionTime: "2025-05-16 14:30:22", afterBalance: 1250.0,
beforeBalance: 1795.0, type: "member",
afterBalance: 2795.0, },
type: "other", {
}, id: 4,
]); title: "账户充值",
amount: 1000.0,
// consumptionNumber: "CONS20230516024",
const filteredConsumptions = computed(() => { consumptionTime: "2025-05-16 14:30:22",
if (activeTab.value === "all") { beforeBalance: 1795.0,
return consumptions.value; afterBalance: 2795.0,
} type: "other",
return consumptions.value.filter((item) => item.type === activeTab.value); },
}); ]);
// const switchTab = (tab) => {
const switchTab = (tab) => { list.value = []
activeTab.value = tab; active.value = tab
}; page.value = 1
if(tab === '全部') {
// typeParams.value = ''
const loadMore = async () => { }
if (loading.value) return; if(tab === '生成') {
typeParams.value = '生成'
loading.value = true; }
try { if(tab === '会员') {
// API typeParams.value = '会员'
// const result = await RPC.get('/api/consumptions', { }
// page: currentPage.value + 1, getList()
// type: activeTab.value }
// }) const page = ref(1)
const typeParams = ref('')
// const loadingStatus = ref('loadmore')
await new Promise((resolve) => setTimeout(resolve, 1000)); const count = ref(0);
//
// const getList = () => {
hasMore.value = false; uni.showLoading();
RPC.post('/wallet/txns/list', {
uni.showToast({ user_id: userStore.userInfo.id,
title: "没有更多数据了", page:page.value,
icon: "none", size:20,
}); type:typeParams.value
} catch (error) { }).then(res => {
console.error("加载更多失败:", error); list.value = [...list.value, ...res.list];
uni.showToast({ count.value = res.total;
title: "加载失败,请重试", if (list.value.length >= count.value) {
icon: "none", loadingStatus.value = "nomore";
}); } else {
} finally { loadingStatus.value = "loadmore";
loading.value = false; }
} }).catch(() => {
}; loadingStatus.value = "loadmore";
}).finally(() => {
// uni.hideLoading();
const loadConsumptions = async () => { })
try { }
// API
// const result = await RPC.get('/api/consumptions', {
// type: activeTab.value onMounted(() => {
// })
// consumptions.value = result.data });
} catch (error) {
console.error("加载消费记录失败:", error); onReachBottom(() => {
uni.showToast({ if (list.value.length >= count.value || loadingStatus.value === "loading")
title: "加载失败,请重试", return;
icon: "none", console.log("上拉加载:");
}); loadingStatus.value = "loading";
} page.value = ++page.value;
}; getList();
});
//
const onRefresh = async () => { onShow(() => {
hasMore.value = true; //
await loadConsumptions(); list.value = []
}; page.value = 1
active.value = '全部'
onMounted(() => { typeParams.value = ''
loadConsumptions(); getList();
}); });
onShow(() => {
//
loadConsumptions();
});
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.container { .container {
font-size: 14px; font-size: 14px;
min-height: 100vh; min-height: 100vh;
background-color: #f8f8f8; background-color: #f8f8f8;
} }
.filter-tabs { .tabBox {
display: flex; height: 50px;
background: white; background-color: #fff;
padding: 0 16px; display: flex;
border-bottom: 1px solid #e5e5e5;
.tabItem {
.tab-item { height: 100%;
flex: 1; font-weight: 600;
text-align: center; display: flex;
padding: 16px 0; align-items: center;
color: #666; justify-content: center;
font-size: 14px; flex: 1;
position: relative;
&.selected {
&.active { color: #5479ED;
color: #4285f4; border-bottom: 1px solid #5479ED;
font-weight: 500; }
}
&::after { }
content: "";
position: absolute; .consumption-list {
bottom: 0; padding: 16px;
left: 50%;
transform: translateX(-50%); .consumption-item {
width: 20px; background: white;
height: 2px; border-radius: 12px;
background: #4285f4; padding: 16px;
border-radius: 1px; margin-bottom: 12px;
}
} .item-header {
} display: flex;
} justify-content: space-between;
align-items: center;
.consumption-list { margin-bottom: 12px;
padding: 16px;
.item-title {
.consumption-item { font-size: 16px;
background: white; color: #333;
border-radius: 12px; font-weight: 500;
padding: 16px; }
margin-bottom: 12px;
.item-amount {
.item-header { font-size: 16px;
display: flex; font-weight: bold;
justify-content: space-between;
align-items: center; &.positive {
margin-bottom: 12px; color: #52c41a;
}
.item-title {
font-size: 16px; &.negative {
color: #333; color: #ff4d4f;
font-weight: 500; }
} }
}
.item-amount {
font-size: 16px; .item-details {
font-weight: bold; .detail-row {
display: flex;
&.positive { justify-content: space-between;
color: #52c41a; align-items: center;
} margin-bottom: 6px;
&.negative { .detail-label {
color: #ff4d4f; color: #999;
} font-size: 13px;
} }
}
.detail-value {
.item-details { color: #666;
.detail-row { font-size: 13px;
display: flex; }
justify-content: space-between; }
align-items: center;
margin-bottom: 6px; .balance-row {
display: flex;
.detail-label { justify-content: space-between;
color: #999; margin-top: 8px;
font-size: 13px;
} .balance-item {
display: flex;
.detail-value { align-items: center;
color: #666; gap: 4px;
font-size: 13px;
} .balance-label {
} color: #999;
font-size: 13px;
.balance-row { }
display: flex;
justify-content: space-between; .balance-value {
margin-top: 8px; color: #333;
font-size: 13px;
.balance-item { font-weight: 500;
display: flex; }
align-items: center; }
gap: 4px; }
}
.balance-label { }
color: #999; }
font-size: 13px;
} .empty-state {
display: flex;
.balance-value { flex-direction: column;
color: #333; align-items: center;
font-size: 13px; justify-content: center;
font-weight: 500; padding: 80px 20px;
}
} .empty-text {
} color: #999;
} font-size: 14px;
} margin-top: 16px;
} }
}
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 80px 20px;
.empty-text {
color: #999;
font-size: 14px;
margin-top: 16px;
}
}
.load-more {
padding: 20px;
text-align: center;
}
</style> </style>

@ -604,19 +604,19 @@
onMounted(() => { onMounted(() => {
checkEnvironment(); checkEnvironment();
// #ifdef MP-WEIXIN // // #ifdef MP-WEIXIN
// // //
const urlParams = new URLSearchParams(window.location.search); // const urlParams = new URLSearchParams(window.location.search);
const code = urlParams.get("code"); // const code = urlParams.get("code");
const from = urlParams.get("from"); // const from = urlParams.get("from");
console.log('from',from) // console.log('from',from)
if (from && from === 'h5') { // if (from && from === 'h5') {
handleWechatH5Callback(code); // handleWechatH5Callback(code);
} // }
// JS-SDK // // JS-SDK
// initWechatJSSDK(); // // initWechatJSSDK();
// #endif // // #endif
}); });
</script> </script>

@ -16,7 +16,9 @@
</view> </view>
<view class="user-id">用户ID: {{userStore.userInfo.id}}</view> <view class="user-id">用户ID: {{userStore.userInfo.id}}</view>
<view class="membership"> <view class="membership">
会员等级: 年会员 (有效期至: 2026-05-15) 会员等级: <span v-if="memberInfo.is_vip">{{memberInfo.plan?.name}} (:
{{memberInfo.end_at ? dayjs(memberInfo.end_at * 1000).format('YYYY-MM-DD') : ''}})</span><span
v-else></span>
</view> </view>
</view> </view>
</view> </view>
@ -31,7 +33,7 @@
<view class="balance-header"> <view class="balance-header">
<view class="balance-label">账户余额</view> <view class="balance-label">账户余额</view>
</view> </view>
<view class="balance-amount">¥50.00</view> <view class="balance-amount">¥{{memberInfo.wallet_balance || 0}}</view>
</div> </div>
<uv-button type="primary" color="#fff" customStyle="color:#000;width:90px" shape="circle" @click="recharge"> <uv-button type="primary" color="#fff" customStyle="color:#000;width:90px" shape="circle" @click="recharge">
充值 充值
@ -45,7 +47,7 @@
:key="item.id" @click="selectPlan(item)"> :key="item.id" @click="selectPlan(item)">
<view class="tag" v-if="item.tags">{{item.tags}}</view> <view class="tag" v-if="item.tags">{{item.tags}}</view>
<view class="plan-name">{{item.name}}</view> <view class="plan-name">{{item.name}}</view>
<view class="plan-price">¥{{item.price_cents}}</view> <view class="plan-price">¥{{item.price}}</view>
</view> </view>
<!-- <view <!-- <view
class="membership-item" class="membership-item"
@ -122,7 +124,8 @@
onMounted onMounted
} from "vue"; } from "vue";
import { import {
onShow onShow,
onPullDownRefresh
} from "@dcloudio/uni-app"; } from "@dcloudio/uni-app";
import HeaderCustom from "/components/HeaderCustom/HeaderCustom.vue"; import HeaderCustom from "/components/HeaderCustom/HeaderCustom.vue";
import { import {
@ -130,11 +133,32 @@
useUserStore useUserStore
} from "@/store"; } from "@/store";
import RPC from "@/utils/request"; import RPC from "@/utils/request";
import dayjs from 'dayjs'
const modal = ref(null); const modal = ref(null);
const authStore = useAuthStore(); const authStore = useAuthStore();
const userStore = useUserStore(); const userStore = useUserStore();
const balance = ref(null)
// const getBalance = () => {
// RPC.post('/wallet/balance', {
// user_id: userStore.userInfo.id
// }).then(res => {
// balance.value = res.balance_cents || 0
// })
// }
const memberInfo = ref({})
const getMemberInfo = () => {
uni.showLoading();
RPC.post('/membership/user/info', {
user_id: userStore.userInfo.id
}).then(res => {
memberInfo.value = res || {}
}).finally(() => {
uni.hideLoading();
})
}
// //
const selectedPlan = ref(""); const selectedPlan = ref("");
const selectedPayment = ref("wxpay"); // const selectedPayment = ref("wxpay"); //
@ -153,7 +177,6 @@
selectedPlan.value = planList.value[0] ? planList.value[0].id : '' selectedPlan.value = planList.value[0] ? planList.value[0].id : ''
}) })
} }
getPlanList()
// //
const selectPlan = (plan) => { const selectPlan = (plan) => {
selectedPlan.value = plan.id; selectedPlan.value = plan.id;
@ -213,7 +236,7 @@
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
WeixinJSBridge.invoke( WeixinJSBridge.invoke(
"getBrandWCPayRequest", { "getBrandWCPayRequest", {
appId: orderInfo.appId, appId: 'wxc504638e03981812',
timeStamp: orderInfo.timeStamp, timeStamp: orderInfo.timeStamp,
nonceStr: orderInfo.nonceStr, nonceStr: orderInfo.nonceStr,
package: orderInfo.package, package: orderInfo.package,
@ -246,6 +269,20 @@
// #endif // #endif
}; };
function showModalAsync(options) {
return new Promise((resolve, reject) => {
uni.showModal({
...options,
success: (res) => {
resolve(res); // await
},
fail: (err) => {
reject(err); //
}
});
});
}
// H5 // H5
const alipayPay = async (orderInfo) => { const alipayPay = async (orderInfo) => {
// #ifdef H5 // #ifdef H5
@ -320,28 +357,45 @@
// 3. // 3.
let paymentResult; let paymentResult;
if (selectedPayment.value === "wxpay") { if (selectedPayment.value === "wxpay") {
const orderResult = await RPC.post('/membership/order/pay_by_wechat', { const res = await uni.login({
provider: 'weixin'
})
const orderResult = await RPC.post('/membership/order/pay_by_wechat_mini', {
code: res.code,
"order_no": orderNum.order_no "order_no": orderNum.order_no
}) })
uni.showLoading({ uni.showLoading({
title: "正在跳转微信支付...", title: "正在跳转微信支付...",
}); });
if (1 == 1) { console.log('orderResult', orderResult)
console.log('orderResult', orderResult) paymentResult = await wechatPay(orderResult);
return
}
paymentResult = await wechatPay(orderResult.wechatPayInfo);
} else if (selectedPayment.value === "alipay") { } else if (selectedPayment.value === "alipay") {
uni.showLoading({ uni.showLoading({
title: "正在跳转支付宝...", title: "正在跳转支付宝...",
}); });
paymentResult = await alipayPay(orderResult.alipayInfo); paymentResult = await alipayPay(orderResult.alipayInfo);
} else if (selectedPayment.value === 'wallet') {
const res = await showModalAsync({
title: '确认支付',
content: '确认使用钱包余额支付?'
});
//
if (res.confirm) {
paymentResult = await RPC.post('/membership/order/pay_by_wallet', {
order_no: orderNum.order_no
})
console.log('paymentResult', paymentResult)
} else if (res.cancel) {
console.log('用户点击了取消');
paymentResult={success:false}
}
} }
uni.hideLoading(); uni.hideLoading();
console.log('paymentResult111',paymentResult)
// 4. // 4.
if (paymentResult.success) { if (paymentResult?.success) {
if (paymentResult.redirect) { if (paymentResult.redirect) {
// H5 // H5
uni.showToast({ uni.showToast({
@ -351,21 +405,27 @@
}); });
} else { } else {
// //
uni.showToast({
title: "支付成功",
icon: "success",
});
//
setTimeout(() => { setTimeout(() => {
// uni.showToast({
uni.navigateTo({ title: "支付成功",
url: `/pages/order-detail/order-detail?orderId=${orderResult.orderId}`, icon: "success",
}); });
setTimeout(()=>{
getMemberInfo()
},500)
}, 1000)
// getMemberInfo()
// //
// loadUserInfo(); // setTimeout(() => {
}, 1500); //
// uni.navigateTo({
// url: `/pages/order-detail/order-detail?orderId=${orderResult.orderId}`,
// });
//
// loadUserInfo();
// }, 1500);
} }
} }
} catch (error) { } catch (error) {
@ -484,12 +544,20 @@
// //
onMounted(() => { onMounted(() => {
checkEnvironment(); checkEnvironment();
handlePaymentCallback(); getPlanList()
// handlePaymentCallback();
}); });
// //
onShow(() => { onShow(() => {
handlePaymentCallback(); handlePaymentCallback();
// getBalance()
getMemberInfo()
});
onPullDownRefresh(() => {
getPlanList()
// getBalance()
getMemberInfo()
}); });
</script> </script>

@ -2,22 +2,28 @@
<view class="container"> <view class="container">
<uv-sticky bgColor="#fff" offsetTop="0"> <uv-sticky bgColor="#fff" offsetTop="0">
<div class="tabBox"> <div class="tabBox">
<div class="tabItem" :class="{selected:active==='全部'}" @click="() => active = '全部'"></div> <div class="tabItem" :class="{selected:active==='全部'}" @click="switchTab('全部')"></div>
<div class="tabItem" :class="{selected:active==='已支付'}" @click="() => active = '已支付'">已支付</div> <div class="tabItem" :class="{selected:active==='会员购买'}" @click="switchTab('会员购买')"></div>
<div class="tabItem" :class="{selected:active==='待支付'}" @click="() => active = '待支付'">待支付</div> <div class="tabItem" :class="{selected:active==='钱包充值'}" @click="switchTab('钱包充值')"></div>
</div> </div>
</uv-sticky> </uv-sticky>
<!-- 订单列表 --> <!-- 订单列表 -->
<view class="order-list"> <view class="order-list">
<view class="order-item" v-for="order in filteredOrders" :key="order.id"> <view class="order-item" v-for="order in list" :key="order.id">
<!-- 订单头部 --> <!-- 订单头部 -->
<view class="order-header"> <view class="order-header">
<view class="order-info"> <view class="order-info">
<text class="order-number">订单编号: {{ order.orderNumber }}</text> <text class="order-number">订单编号: {{ order.order_no }}</text>
<view class="order-type-tag"></view> <view class="order-type-tag" @click="copyOrderNo(order.order_no)"></view>
</view> </view>
<view class="order-status" :class="order.status"> <view class="order-status" :class="{paid:order.status === 'paid',pending:order.status === 'created' || order.status === 'pending',
{{ order.statusText }} cancelled:order.status === 'failed' || order.status === 'canceled' || order.status === 'refunded'}">
<span v-if="order.status === 'created'"></span>
<span v-if="order.status === 'pending'"></span>
<span v-if="order.status === 'paid'"></span>
<span v-if="order.status === 'failed'"></span>
<span v-if="order.status === 'canceled'"></span>
<span v-if="order.status === 'refunded'">退</span>
</view> </view>
</view> </view>
@ -25,50 +31,58 @@
<view class="order-details"> <view class="order-details">
<view class="detail-row"> <view class="detail-row">
<text class="label">用户名称:</text> <text class="label">用户名称:</text>
<text class="value">{{ order.userName }}</text> <text class="value">{{ order.user_name }}</text>
</view> </view>
<view class="detail-row"> <view class="detail-row">
<text class="label">用户ID:</text> <text class="label">用户ID:</text>
<text class="value">{{ order.userId }}</text> <text class="value">{{ order.user_id }}</text>
</view> </view>
<view class="detail-row"> <view class="detail-row">
<text class="label">订单类型:</text> <text class="label">订单类型:</text>
<text class="value">{{ order.orderType }}</text> <text class="value">{{ order.order_type }}</text>
</view> </view>
<view class="detail-row"> <view class="detail-row">
<text class="label">订单金额:</text> <text class="label">订单金额:</text>
<text class="value price">¥{{ order.amount }}</text> <text class="value price">¥{{ order.amount }}</text>
</view> </view>
<view class="detail-row" v-if="order.discount > 0"> <view class="detail-row">
<text class="label">支付渠道:</text>
<text class="value">
<template v-if="order.pay_channel === 'wallet'"></template>
<template v-if="order.pay_channel === 'wxpay'"></template>
<template v-if="order.pay_channel === 'alipay'"></template>
</text>
</view>
<!-- <view class="detail-row" v-if="order.discount > 0">
<text class="label">折扣金额:</text> <text class="label">折扣金额:</text>
<text class="value discount">-¥{{ order.discount }}</text> <text class="value discount">-¥{{ order.discount }}</text>
</view> </view> -->
<view class="detail-row"> <!-- <view class="detail-row">
<text class="label">实付金额:</text> <text class="label">实付金额:</text>
<text class="value final-price">¥{{ order.finalAmount }}</text> <text class="value final-price">¥{{ order.finalAmount }}</text>
</view> </view> -->
<view class="detail-row"> <view class="detail-row">
<text class="label">{{ order.status === 'pending' ? '创建时间:' : '支付时间:' }}</text> <text class="label">创建时间</text>
<text class="value">{{ order.payTime }}</text> <text class="value">{{ order.created_at ? dayjs(order.created_at * 1000).format('YYYY-MM-DD HH:mm:ss') :'' }}</text>
</view> </view>
</view> </view>
<!-- 订单操作 --> <!-- 订单操作 -->
<view class="order-actions"> <view class="order-actions" v-if="order.status === 'pending' || order.status === 'created'">
<div> <div>
<div class="countdown-text" v-if="order.status === 'pending'">: {{ order.countdown }} <!-- <div class="countdown-text" v-if="order.status === 'pending'">: {{ order.countdown }}
</div> </div> -->
</div> </div>
<div class="buttonBox"> <div class="buttonBox">
<template v-if="order.status === 'pending'"> <template v-if="order.status === 'pending' || order.status === 'created'">
<uv-button type="default" size="small" @click="cancelOrder(order)"> <uv-button type="error" size="small" @click="cancelOrder(order)">
取消订单 取消订单
</uv-button> </uv-button>
<uv-button type="error" size="small" @click="payOrder(order)"> <uv-button type="primary" size="small" @click="payOrder(order)">
去支付 去支付
</uv-button> </uv-button>
</template> </template>
<template v-else> <!-- <template v-else>
<uv-button type="default" size="small" @click="deleteOrder(order)"> <uv-button type="default" size="small" @click="deleteOrder(order)">
删除订单 删除订单
@ -76,40 +90,18 @@
<uv-button type="primary" color="#3F70FF" size="small" @click="viewOrderDetail(order)"> <uv-button type="primary" color="#3F70FF" size="small" @click="viewOrderDetail(order)">
查看详情 查看详情
</uv-button> </uv-button>
</template> </template> -->
</div> </div>
</view> </view>
<!-- 待支付订单底部 -->
<!-- <view v-if="order.status === 'pending'" class="pending-footer">
<view class="countdown">
<text class="countdown-text">支付剩余时间: {{ order.countdown }}</text>
</view>
<view class="pending-actions">
<uv-button
type="default"
size="large"
@click="cancelOrder(order)"
>
取消订单
</uv-button>
<uv-button
type="error"
size="large"
@click="payOrder(order)"
>
去支付
</uv-button>
</view>
</view> -->
</view> </view>
</view> </view>
<!-- 空状态 --> <!-- 空状态 -->
<view v-if="filteredOrders.length === 0" class="empty-state"> <view v-if="list.length === 0 && loadingStatus !== 'loading'" class="empty-state">
<u-icon name="file-text" size="60" color="#ccc"></u-icon> <u-icon name="file-text" size="60" color="#ccc"></u-icon>
<text class="empty-text">暂无订单记录</text> <text class="empty-text">暂无订单记录</text>
</view> </view>
<uv-load-more v-else :status="loadingStatus" />
</view> </view>
</template> </template>
@ -120,7 +112,8 @@
ref ref
} from "vue"; } from "vue";
import { import {
onShow onShow,
onReachBottom
} from "@dcloudio/uni-app"; } from "@dcloudio/uni-app";
import { import {
useAuthStore, useAuthStore,
@ -128,14 +121,26 @@
} from "@/store"; } from "@/store";
import HeaderCustom from '/components/HeaderCustom/HeaderCustom.vue' import HeaderCustom from '/components/HeaderCustom/HeaderCustom.vue'
import RPC from "@/utils/request"; import RPC from "@/utils/request";
import dayjs from "dayjs";
const active = ref('全部') const active = ref('全部')
const authStore = useAuthStore() const authStore = useAuthStore()
const userStore = useUserStore() const userStore = useUserStore()
// const copyOrderNo = (orderNo) => {
const activeTab = ref('all') uni.setClipboardData({
const orders = ref([{ data: orderNo,
success: () => {
uni.showToast({
title: '订单号已复制',
icon: 'success'
})
}
})
}
const list = ref([{
id: 1, id: 1,
orderNumber: 'ORD20230515001', orderNumber: 'ORD20230515001',
typeLabel: '续费', typeLabel: '续费',
@ -183,17 +188,17 @@
} }
]) ])
//
const filteredOrders = computed(() => {
if (activeTab.value === 'all') {
return orders.value
}
return orders.value.filter(order => order.type === activeTab.value)
})
// //
const switchTab = (tab) => { const switchTab = (tab) => {
activeTab.value = tab list.value = []
active.value = tab
page.value = 1
if(tab === '全部') {
typeParams.value = ''
} else {
typeParams.value = tab
}
getList()
} }
// //
@ -212,13 +217,12 @@
if (res.confirm) { if (res.confirm) {
try { try {
// API // API
// await RPC.post('/api/order/cancel', { orderId: order.id }) await RPC.post('/membership/order/cancel', { order_no: order.order_no })
// //
const index = orders.value.findIndex(item => item.id === order.id) const index = list.value.findIndex(item => item.id === order.id)
if (index !== -1) { if (index !== -1) {
orders.value[index].status = 'cancelled' list.value[index].status = 'canceled'
orders.value[index].statusText = '已取消'
} }
uni.showToast({ uni.showToast({
@ -272,33 +276,63 @@
// //
const payOrder = (order) => { const payOrder = (order) => {
uni.navigateTo({ uni.showModal({
url: `/pages/payment/payment?orderId=${order.id}` title: '确认支付',
content: '确定继续支付这个订单吗?',
success: async (res) => {
if (res.confirm) {
}
}
}) })
} }
// const page = ref(1)
const loadOrders = async () => { const typeParams = ref('')
try { const loadingStatus = ref('loadmore')
// API const count = ref(0);
// const result = await RPC.get('/api/orders') const getList = () => {
// orders.value = result.data uni.showLoading();
} catch (error) { RPC.post('/membership/order/list', {
console.error('加载订单失败:', error) user_id: userStore.userInfo.id,
uni.showToast({ page:page.value,
title: '加载失败,请重试', order_type:typeParams.value,
icon: 'none' size:20
}) }).then(res => {
} list.value = [...list.value, ...res.list];
count.value = res.total;
if (list.value.length >= count.value) {
loadingStatus.value = "nomore";
} else {
loadingStatus.value = "loadmore";
}
}).catch(() => {
loadingStatus.value = "loadmore";
}).finally(() => {
uni.hideLoading();
})
} }
onMounted(() => { onMounted(() => {
loadOrders()
}) })
onReachBottom(() => {
if (list.value.length >= count.value || loadingStatus.value === "loading")
return;
console.log("上拉加载:");
loadingStatus.value = "loading";
page.value = ++page.value;
getList();
});
onShow(() => { onShow(() => {
// //
loadOrders() list.value = []
page.value = 1
active.value = '全部'
typeParams.value = ''
getList()
}) })
</script> </script>

@ -74,7 +74,7 @@
</view> </view>
<!-- 加载更多 --> <!-- 加载更多 -->
<uv-load-more :status="loadingStatus" @loadmore="loadmore" /> <uv-load-more v-else :status="loadingStatus" @loadmore="loadmore" />
</view> </view>
</template> </template>
@ -128,7 +128,7 @@ const downloadRecord = (record) => {
// #ifdef MP-WEIXIN // #ifdef MP-WEIXIN
// App使uni.downloadFile // App使uni.downloadFile
uni.downloadFile({ uni.downloadFile({
url: 'https://ksdt-1255846884.cos.ap-shanghai.myqcloud.com/lift-result/2025/09/24/d6180add4cd94875a1854a4febce8966.pdf', url: record.result_url,
success: (res) => { success: (res) => {
console.log(res); console.log(res);
if (res.statusCode === 200) { if (res.statusCode === 200) {
@ -200,6 +200,7 @@ const deleteRecord = (record) => {
// //
const loadRecords = async () => { const loadRecords = async () => {
uni.showLoading();
loadingStatus.value = "loading"; loadingStatus.value = "loading";
RPC.post("/planned_list", { page: page.value, page_size: 20 }) RPC.post("/planned_list", { page: page.value, page_size: 20 })
.then((res) => { .then((res) => {
@ -213,7 +214,9 @@ const loadRecords = async () => {
}) })
.catch(() => { .catch(() => {
loadingStatus.value = "loadmore"; loadingStatus.value = "loadmore";
}); }).finally(() => {
uni.hideLoading();
});
}; };
// //

@ -0,0 +1,40 @@
订单列表的
"id": it.ID, // 订单ID
"order_no": it.OrderNo, // 订单号M=会员订单R=充值订单)
"user_id": it.UserID, // 用户ID
"user_name": userNameMap[it.UserID], // 用户名
"plan_id": it.PlanID, // 套餐ID充值订单为0
"plan_name": planName, // 套餐名称(充值订单固定为“钱包充值”)
"order_type": it.OrderType, // 订单类型:会员购买/钱包充值(中文)
"amount_cents": it.AmountCents.ToInt64(), // 订单金额(分)
"amount": it.AmountCents.String(), // 订单金额(元,字符串,保留两位小数)
"pay_channel": it.PayChannel, // 支付渠道wallet|wxpay|alipay|other
"status": it.Status, // 订单状态created|paid|canceled|refunded
"pay_time": it.PayTime, // 支付时间Unix 时间戳,秒)
"transaction_id": it.TransactionID, // 第三方交易号/平台流水号
"remark": it.Remark, // 备注
"created_at": it.CreatedAt, // 创建时间Unix 时间戳,秒)
"updated_at": it.UpdatedAt, // 更新时间Unix 时间戳,秒)
消费列表
"id": it.ID, // 流水ID
"user_id": it.UserID, // 用户ID
"user_name": userName, // 用户名
"type": it.Type, // 交易类型recharge(充值)|purchase(消费)
"amount_cents": it.AmountCents.ToInt64(), // 金额(分)
"amount": it.AmountCents.String(), // 金额(元,字符串,保留两位小数)
"related_order_id": it.RelatedOrderID, // 关联的订单ID可为空
"order_no": it.OrderNo, // 关联订单号M* 会员订单 / R* 充值订单)
"pay_channel": it.PayChannel, // 支付渠道wallet|wxpay|alipay|other
"status": it.Status, // 交易状态success|failed|pending
"remark": it.Remark, // 备注
"balance_before_cents": it.BalanceBeforeCents.ToInt64(), // 变动前余额(分)
"balance_after_cents": it.BalanceAfterCents.ToInt64(), // 变动后余额(分)
"finished_at": it.FinishedAt, // 完成时间Unix 时间戳,秒)
"external_txn_id": it.ExternalTxnID, // 外部交易号(第三方平台返回)
"currency": it.Currency, // 币种(默认 CNY
"created_at": it.CreatedAt, // 创建时间Unix 时间戳,秒)
})

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save