This commit is contained in:
yml
2026-04-08 16:30:42 +08:00
commit 313c036845
131 changed files with 22393 additions and 0 deletions
+368
View File
@@ -0,0 +1,368 @@
<script setup lang="ts">
import { computed, ref } from 'vue'
import { useRoute } from 'vue-router'
import TencentAuthCard from '@/components/tencent/TencentAuthCard.vue'
import TencentRedeemPanel from '@/components/tencent/TencentRedeemPanel.vue'
import TencentResultPanel from '@/components/tencent/TencentResultPanel.vue'
import { useClaimPage } from '@/composables/useClaimPage'
const route = useRoute()
const token = computed(() => String(route.params.token || '').trim())
const {
detailLoading,
roleConfirmLoading,
sessionLoading,
redeemLoading,
loginType,
loginTypeLabel,
loginTabs,
task,
order,
orderItem,
hasSession,
qrImage,
qrFigureStyle,
qrPreviewWidth,
statusLabel,
session,
sessionNotice,
roleFacts,
resultFacts,
roleConfirmed,
roleReady,
canConfirmRole,
canRedeem,
redeemBlockedReason,
redeemButtonLabel,
initButtonLabel,
scanInstruction,
screenshotEmptyTitle,
screenshotEmptyMessage,
screenshotUrl,
showScreenshot,
createSessionFlow,
refreshSessionSummary,
switchLoginType,
confirmRoleNow,
redeemNow,
handleQrImageLoad,
} = useClaimPage(token.value)
const qrPreviewVisible = ref(false)
const screenshotPreviewVisible = ref(false)
const currentLoginTabLabel = computed(
() => loginTabs.find((tab) => tab.value === loginType.value)?.label ?? loginTabs[0].label,
)
const helperText = computed(() => {
if (!task.value) {
return '正在加载领取任务'
}
return `订单 ${order.value?.platformOrderId || '-'} · 任务 ${task.value.taskNo}`
})
</script>
<template>
<main class="claim-page">
<section class="workspace">
<div class="page-toolbar">
<div>
<div class="brand-chip">订单领取</div>
<p class="helper-copy">{{ helperText }}</p>
</div>
<div class="toolbar-actions">
<div class="status-badge">{{ statusLabel }}</div>
<RouterLink class="back-link" to="/tx/browser">调试页</RouterLink>
</div>
</div>
<section class="order-card">
<div class="order-meta">
<article>
<span>订单号</span>
<strong>{{ order?.platformOrderId || '-' }}</strong>
</article>
<article>
<span>商品</span>
<strong>{{ orderItem?.skuName || '-' }}</strong>
</article>
<article>
<span>数量</span>
<strong>{{ orderItem?.quantity || 0 }}</strong>
</article>
<article>
<span>任务号</span>
<strong>{{ task?.taskNo || '-' }}</strong>
</article>
</div>
</section>
<div v-if="detailLoading" class="empty-block loading-block">
<strong>领取信息加载中</strong>
<p>正在校验链接并同步任务状态</p>
</div>
<template v-else>
<div class="layout-grid">
<TencentAuthCard
:current-login-tab-label="currentLoginTabLabel"
:has-session="hasSession"
:init-button-label="initButtonLabel"
:login-tabs="loginTabs"
:login-type="loginType"
:login-type-label="loginTypeLabel"
:qr-figure-style="qrFigureStyle"
:qr-image="qrImage"
:scan-instruction="scanInstruction"
:session-status="session?.status || ''"
:session-id="session?.sessionId || ''"
:session-loading="sessionLoading"
:session-notice="sessionNotice"
@create-session="createSessionFlow"
@open-qr-preview="qrPreviewVisible = true"
@qr-image-load="handleQrImageLoad"
@reload-session="refreshSessionSummary()"
@switch-login-type="switchLoginType"
/>
<div class="control-column">
<TencentRedeemPanel
:can-redeem="canRedeem"
:can-confirm-role="canConfirmRole"
confirm-action-label="确认当前角色并继续"
:confirm-action-loading="roleConfirmLoading"
:confirm-action-visible="!roleConfirmed"
:confirm-readonly="true"
:hide-redeem-form="true"
:login-type-label="loginTypeLabel"
:max-attempts="6"
:redeem-blocked-reason="redeemBlockedReason"
:redeem-button-label="redeemButtonLabel"
:redeem-code="''"
:redeem-loading="redeemLoading"
:role-confirmed="roleConfirmed"
:role-facts="roleFacts"
:role-ready="roleReady"
:status-label="statusLabel"
@confirm-role="confirmRoleNow"
@redeem="redeemNow"
@update:max-attempts="() => undefined"
@update:redeem-code="() => undefined"
@update:role-confirmed="() => undefined"
/>
</div>
</div>
<TencentResultPanel
:result-facts="resultFacts"
:screenshot-empty-message="screenshotEmptyMessage"
:screenshot-empty-title="screenshotEmptyTitle"
:screenshot-url="screenshotUrl"
:show-screenshot="showScreenshot"
@open-screenshot-preview="screenshotPreviewVisible = true"
/>
</template>
</section>
<el-dialog
v-model="qrPreviewVisible"
align-center
class="preview-dialog"
:width="qrPreviewWidth"
>
<div class="qr-preview-body">
<img
class="preview-image qr-preview-image"
:src="qrImage"
:alt="`Claim ${loginTypeLabel} QR Preview`"
/>
</div>
</el-dialog>
<el-dialog
v-model="screenshotPreviewVisible"
align-center
class="preview-dialog"
width="1080px"
>
<img class="preview-image" :src="screenshotUrl" alt="Claim Screenshot Preview" />
</el-dialog>
</main>
</template>
<style scoped>
.claim-page {
min-height: 100vh;
padding: 24px;
background:
radial-gradient(circle at top left, rgba(73, 136, 255, 0.12), transparent 32%),
radial-gradient(circle at top right, rgba(87, 194, 150, 0.12), transparent 28%),
linear-gradient(180deg, #f4f8fc 0%, #eef4fb 100%);
}
.workspace {
max-width: 1240px;
margin: 0 auto;
display: grid;
gap: 18px;
}
.page-toolbar,
.toolbar-actions {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
}
.brand-chip,
.status-badge,
.back-link {
display: inline-flex;
align-items: center;
min-height: 42px;
padding: 0 18px;
border-radius: 999px;
background: rgba(255, 255, 255, 0.92);
border: 1px solid rgba(86, 108, 138, 0.12);
color: #27405e;
box-shadow: 0 12px 32px rgba(30, 49, 78, 0.08);
}
.brand-chip {
font-weight: 800;
}
.status-badge {
font-weight: 700;
}
.back-link {
text-decoration: none;
}
.helper-copy {
margin: 8px 0 0;
color: #64748b;
}
.order-card {
padding: 18px 22px;
border-radius: 28px;
background: rgba(255, 255, 255, 0.94);
border: 1px solid rgba(86, 108, 138, 0.1);
box-shadow: 0 22px 70px rgba(30, 49, 78, 0.08);
}
.order-meta {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 12px;
}
.order-meta article {
padding: 14px;
border-radius: 16px;
background: #f7f9fc;
}
.order-meta span {
display: block;
font-size: 12px;
color: #708096;
}
.order-meta strong {
display: block;
margin-top: 6px;
color: #1f324a;
word-break: break-word;
}
.layout-grid {
display: grid;
grid-template-columns: minmax(0, 420px) minmax(0, 1fr);
gap: 18px;
align-items: stretch;
}
.control-column {
display: grid;
gap: 14px;
}
.empty-block {
min-height: 220px;
border-radius: 18px;
display: grid;
place-items: center;
text-align: center;
padding: 24px;
background: rgba(255, 255, 255, 0.92);
border: 1px dashed #d5dfec;
}
.loading-block strong {
color: #2c4058;
font-size: 16px;
}
.loading-block p {
margin: 8px 0 0;
color: #6b7a91;
line-height: 1.7;
}
.preview-image {
display: block;
width: 100%;
height: auto;
}
:deep(.preview-dialog .el-dialog) {
border-radius: 28px;
overflow: hidden;
}
:deep(.preview-dialog .el-dialog__body) {
padding: 0;
background: #f5f7fb;
}
.qr-preview-body {
display: grid;
place-items: center;
padding: 24px;
}
.qr-preview-image {
width: auto;
max-width: 100%;
image-rendering: -moz-crisp-edges;
image-rendering: crisp-edges;
image-rendering: pixelated;
}
@media (max-width: 1140px) {
.layout-grid,
.order-meta {
grid-template-columns: 1fr;
}
}
@media (max-width: 760px) {
.claim-page {
padding: 16px;
}
.page-toolbar,
.toolbar-actions {
flex-direction: column;
align-items: flex-start;
}
}
</style>