docs: add pagewise ui test checklist
This commit is contained in:
@@ -248,7 +248,7 @@ onMounted(fetchData);
|
||||
v-for="creator in matchedCreators"
|
||||
:key="creator.id"
|
||||
class="flex items-center gap-3 p-3 rounded-xl hover:bg-slate-50 transition-colors cursor-pointer border border-transparent hover:border-slate-200"
|
||||
@click="$router.push(`/t/${creator.id}`)"
|
||||
@click="$router.push(`/t/${creator.id}`)"
|
||||
>
|
||||
<img
|
||||
:src="
|
||||
@@ -402,7 +402,7 @@ onMounted(fetchData);
|
||||
<div class="flex-1 min-w-0">
|
||||
<div
|
||||
class="font-bold text-slate-900 text-sm truncate hover:text-primary-600 cursor-pointer"
|
||||
@click="$router.push(`/t/${creator.id}`)"
|
||||
@click="$router.push(`/t/${creator.id}`)"
|
||||
>
|
||||
{{ creator.name }}
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,94 @@
|
||||
<script setup>
|
||||
import { ref, onMounted } from "vue";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
import { contentApi } from "../../api/content";
|
||||
import { orderApi } from "../../api/order";
|
||||
import { tenantPath } from "../../utils/tenant";
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const tenantRoute = (path) => tenantPath(path, route);
|
||||
|
||||
const contentId = route.query.contentId || route.query.content_id;
|
||||
const loading = ref(false);
|
||||
const errorMsg = ref("");
|
||||
const content = ref(null);
|
||||
const price = ref("0.00");
|
||||
|
||||
const loadContent = async () => {
|
||||
if (!contentId) {
|
||||
errorMsg.value = "缺少 contentId";
|
||||
return;
|
||||
}
|
||||
try {
|
||||
loading.value = true;
|
||||
const res = await contentApi.get(contentId);
|
||||
content.value = res;
|
||||
if (res?.prices?.length) {
|
||||
price.value = (res.prices[0].price_amount / 100).toFixed(2);
|
||||
} else if (res?.price_amount !== undefined) {
|
||||
price.value = (res.price_amount / 100).toFixed(2);
|
||||
} else if (res?.price !== undefined) {
|
||||
price.value = Number(res.price).toFixed(2);
|
||||
}
|
||||
} catch (e) {
|
||||
errorMsg.value = "加载内容失败";
|
||||
console.error(e);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const createOrder = async () => {
|
||||
if (!contentId) return;
|
||||
try {
|
||||
loading.value = true;
|
||||
const res = await orderApi.create({ content_id: Number(contentId) });
|
||||
const orderID = res?.id || res?.order_id || res?.orderID;
|
||||
if (!orderID) {
|
||||
throw new Error("未返回订单ID");
|
||||
}
|
||||
router.push(tenantRoute(`/payment/${orderID}`));
|
||||
} catch (e) {
|
||||
errorMsg.value = "创建订单失败";
|
||||
console.error(e);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(loadContent);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="mx-auto max-w-screen-xl my-8 p-8 bg-white rounded-xl shadow-sm">
|
||||
<h1 class="text-2xl font-bold mb-4">Checkout</h1>
|
||||
<p class="text-slate-400">(Checkout flow)</p>
|
||||
<h1 class="text-2xl font-bold mb-4">结算</h1>
|
||||
<p v-if="loading" class="text-slate-400">加载中...</p>
|
||||
<p v-else-if="errorMsg" class="text-red-500 text-sm">{{ errorMsg }}</p>
|
||||
<div v-else class="space-y-6">
|
||||
<div class="border rounded-lg p-4 bg-slate-50">
|
||||
<div class="text-lg font-semibold text-slate-900">
|
||||
{{ content?.title }}
|
||||
</div>
|
||||
<div class="text-slate-500 text-sm">{{ content?.description }}</div>
|
||||
<div class="text-2xl font-bold text-primary-600 mt-2">
|
||||
¥ {{ price }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex gap-3">
|
||||
<button
|
||||
class="px-5 py-2 rounded-lg bg-primary-600 text-white hover:bg-primary-700"
|
||||
@click="createOrder"
|
||||
>
|
||||
提交订单
|
||||
</button>
|
||||
<button
|
||||
class="px-4 py-2 rounded-lg border border-slate-200 text-slate-600"
|
||||
@click="router.back()"
|
||||
>
|
||||
取消
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -8,9 +8,10 @@ const route = useRoute();
|
||||
const router = useRouter();
|
||||
const tenantRoute = (path) => tenantPath(path, route);
|
||||
|
||||
const orderId = route.params.id || "82934712";
|
||||
const amount = "9.90"; // Should fetch order details first
|
||||
const productName = "《霸王别姬》全本实录珍藏版";
|
||||
const orderId = route.params.id;
|
||||
const amount = ref("0.00");
|
||||
const productName = ref("");
|
||||
const orderStatus = ref("");
|
||||
|
||||
const paymentMethod = ref("alipay");
|
||||
const timeLeft = ref(900); // 15 minutes
|
||||
@@ -29,6 +30,38 @@ const formatTime = (seconds) => {
|
||||
return `${m.toString().padStart(2, "0")}:${s.toString().padStart(2, "0")}`;
|
||||
};
|
||||
|
||||
const loadOrder = async () => {
|
||||
try {
|
||||
const res = await orderApi.status(orderId);
|
||||
orderStatus.value = res?.status || "";
|
||||
if (res?.amount_paid !== undefined) {
|
||||
amount.value = (res.amount_paid / 100).toFixed(2);
|
||||
} else if (res?.amount_original !== undefined) {
|
||||
amount.value = (res.amount_original / 100).toFixed(2);
|
||||
}
|
||||
if (res?.content_title) {
|
||||
productName.value = res.content_title;
|
||||
}
|
||||
if (orderStatus.value === "paid" || orderStatus.value === "completed") {
|
||||
isSuccess.value = true;
|
||||
isScanning.value = false;
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("Load order failed", e);
|
||||
}
|
||||
};
|
||||
|
||||
const payOrder = async () => {
|
||||
try {
|
||||
isScanning.value = true;
|
||||
await orderApi.pay(orderId, { method: paymentMethod.value });
|
||||
// 支付接口若未立即更新状态,将继续依赖轮询
|
||||
} catch (e) {
|
||||
console.error("Pay order failed", e);
|
||||
isScanning.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const simulateSuccess = () => {
|
||||
isScanning.value = true;
|
||||
setTimeout(() => {
|
||||
@@ -45,11 +78,20 @@ onMounted(() => {
|
||||
if (timeLeft.value > 0) timeLeft.value--;
|
||||
}, 1000);
|
||||
|
||||
loadOrder();
|
||||
|
||||
// Poll Status
|
||||
pollTimer = setInterval(async () => {
|
||||
try {
|
||||
const res = await orderApi.status(orderId);
|
||||
if (res.status === "paid" || res.status === "completed") {
|
||||
orderStatus.value = res?.status || "";
|
||||
if (res?.amount_paid !== undefined) {
|
||||
amount.value = (res.amount_paid / 100).toFixed(2);
|
||||
}
|
||||
if (res?.content_title) {
|
||||
productName.value = res.content_title;
|
||||
}
|
||||
if (orderStatus.value === "paid" || orderStatus.value === "completed") {
|
||||
isScanning.value = false;
|
||||
isSuccess.value = true;
|
||||
clearInterval(pollTimer);
|
||||
@@ -105,7 +147,10 @@ onUnmounted(() => {
|
||||
<p class="text-slate-500 mb-2">订单提交成功,请尽快支付</p>
|
||||
<div class="text-4xl font-bold text-slate-900">¥ {{ amount }}</div>
|
||||
<div class="text-sm text-slate-500 mt-2">
|
||||
商品:{{ productName }}
|
||||
商品:{{ productName || "加载中..." }}
|
||||
</div>
|
||||
<div class="text-xs text-slate-400 mt-1">
|
||||
状态:{{ orderStatus || "pending" }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -193,12 +238,20 @@ onUnmounted(() => {
|
||||
</div>
|
||||
|
||||
<!-- Dev Tool -->
|
||||
<button
|
||||
@click="simulateSuccess"
|
||||
class="mt-8 text-xs text-slate-300 hover:text-slate-500 underline"
|
||||
>
|
||||
[开发调试] 模拟支付成功
|
||||
</button>
|
||||
<div class="flex flex-col items-center gap-3 mt-6">
|
||||
<button
|
||||
@click="payOrder"
|
||||
class="px-4 py-2 rounded-lg bg-primary-600 text-white hover:bg-primary-700"
|
||||
>
|
||||
立即支付
|
||||
</button>
|
||||
<button
|
||||
@click="simulateSuccess"
|
||||
class="text-xs text-slate-300 hover:text-slate-500 underline"
|
||||
>
|
||||
[开发调试] 模拟支付成功
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user