300 lines
7.0 KiB
Vue
300 lines
7.0 KiB
Vue
<script setup lang="ts">
|
|
import { computed, ref, watch } from 'vue'
|
|
|
|
import TencentAuthCard from '@/components/tencent/TencentAuthCard.vue'
|
|
import TencentRedeemPanel from '@/components/tencent/TencentRedeemPanel.vue'
|
|
import TencentResultPanel from '@/components/tencent/TencentResultPanel.vue'
|
|
import { useTencentBrowserSessionPage } from '@/composables/useTencentBrowserSessionPage'
|
|
|
|
const {
|
|
activityInfo,
|
|
canRedeem,
|
|
createSessionFlow,
|
|
hasSession,
|
|
initButtonLabel,
|
|
loginTabs,
|
|
loginType,
|
|
loginTypeLabel,
|
|
maxAttempts,
|
|
qrImage,
|
|
redeemButtonLabel,
|
|
redeemBlockedReason,
|
|
redeemCode,
|
|
redeemLoading,
|
|
redeemNow,
|
|
reloadSessionPage,
|
|
resultFacts,
|
|
roleConfirmed,
|
|
roleFacts,
|
|
scanInstruction,
|
|
screenshotEmptyMessage,
|
|
screenshotEmptyTitle,
|
|
screenshotUrl,
|
|
session,
|
|
sessionLoading,
|
|
sessionNotice,
|
|
statusLabel,
|
|
switchLoginType,
|
|
} = useTencentBrowserSessionPage()
|
|
|
|
const qrPreviewVisible = ref(false)
|
|
const screenshotPreviewVisible = ref(false)
|
|
const qrImageNaturalWidth = ref(0)
|
|
|
|
const currentLoginTabLabel = computed(
|
|
() => loginTabs.find((tab) => tab.value === loginType.value)?.label ?? loginTabs[0].label,
|
|
)
|
|
|
|
watch(qrImage, () => {
|
|
qrImageNaturalWidth.value = 0
|
|
})
|
|
|
|
function handleQrImageLoad(event: Event) {
|
|
const target = event.target
|
|
|
|
if (!(target instanceof HTMLImageElement)) {
|
|
return
|
|
}
|
|
|
|
qrImageNaturalWidth.value = target.naturalWidth || 0
|
|
}
|
|
|
|
const qrDisplayWidth = computed(() => {
|
|
const naturalWidth = qrImageNaturalWidth.value
|
|
|
|
if (!naturalWidth) {
|
|
return 220
|
|
}
|
|
|
|
if (naturalWidth < 160) {
|
|
return Math.min(naturalWidth * 2, 220)
|
|
}
|
|
|
|
return Math.min(naturalWidth, 240)
|
|
})
|
|
|
|
const qrFigureStyle = computed(() => ({
|
|
width: `${qrDisplayWidth.value}px`,
|
|
maxWidth: '100%',
|
|
}))
|
|
|
|
const qrPreviewWidth = computed(() => `${Math.min(Math.max(qrDisplayWidth.value + 120, 360), 480)}px`)
|
|
const showScreenshot = computed(() => Boolean(screenshotUrl.value))
|
|
</script>
|
|
|
|
<template>
|
|
<main class="browser-page">
|
|
<section class="workspace">
|
|
<div class="page-toolbar">
|
|
<div class="brand-chip">腾讯浏览器兑换</div>
|
|
<div class="toolbar-actions">
|
|
<div class="status-badge">{{ statusLabel }}</div>
|
|
<RouterLink class="back-link" to="/">返回首页</RouterLink>
|
|
</div>
|
|
</div>
|
|
|
|
<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="reloadSessionPage"
|
|
@switch-login-type="switchLoginType"
|
|
/>
|
|
|
|
<aside class="control-column">
|
|
<TencentRedeemPanel
|
|
:can-redeem="canRedeem"
|
|
:login-type-label="loginTypeLabel"
|
|
:max-attempts="maxAttempts"
|
|
:redeem-blocked-reason="redeemBlockedReason"
|
|
:redeem-button-label="redeemButtonLabel"
|
|
:redeem-code="redeemCode"
|
|
:redeem-loading="redeemLoading"
|
|
:role-confirmed="roleConfirmed"
|
|
:role-facts="roleFacts"
|
|
:role-ready="Boolean(activityInfo?.role?.ready)"
|
|
:status-label="statusLabel"
|
|
@redeem="redeemNow"
|
|
@update:max-attempts="maxAttempts = $event"
|
|
@update:redeem-code="redeemCode = $event"
|
|
@update:role-confirmed="roleConfirmed = $event"
|
|
/>
|
|
</aside>
|
|
</div>
|
|
|
|
<TencentResultPanel
|
|
:result-facts="resultFacts"
|
|
:screenshot-empty-message="screenshotEmptyMessage"
|
|
:screenshot-empty-title="screenshotEmptyTitle"
|
|
:screenshot-url="screenshotUrl"
|
|
:show-screenshot="showScreenshot"
|
|
@open-screenshot-preview="screenshotPreviewVisible = true"
|
|
/>
|
|
</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="`Tencent ${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="Tencent Screenshot Preview" />
|
|
</el-dialog>
|
|
</main>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.browser-page {
|
|
min-height: 100vh;
|
|
padding: 22px 16px 34px;
|
|
font-family: 'Avenir Next', 'PingFang SC', 'Helvetica Neue', sans-serif;
|
|
background:
|
|
radial-gradient(circle at top left, rgba(116, 168, 255, 0.12), transparent 28%),
|
|
radial-gradient(circle at bottom right, rgba(248, 186, 103, 0.12), transparent 24%),
|
|
linear-gradient(180deg, #f4f7fb 0%, #edf2f8 100%);
|
|
}
|
|
|
|
.workspace {
|
|
width: min(1120px, 100%);
|
|
margin: 0 auto;
|
|
display: grid;
|
|
gap: 16px;
|
|
}
|
|
|
|
.page-toolbar {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: 12px;
|
|
}
|
|
|
|
.toolbar-actions {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
}
|
|
|
|
.brand-chip {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
min-height: 38px;
|
|
padding: 0 14px;
|
|
border-radius: 999px;
|
|
background: rgba(255, 255, 255, 0.9);
|
|
border: 1px solid rgba(86, 108, 138, 0.12);
|
|
color: #2f425d;
|
|
font-size: 13px;
|
|
font-weight: 700;
|
|
letter-spacing: 0.04em;
|
|
}
|
|
|
|
.status-badge,
|
|
.back-link {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
min-height: 38px;
|
|
padding: 0 14px;
|
|
border-radius: 999px;
|
|
border: 1px solid rgba(86, 108, 138, 0.14);
|
|
background: rgba(255, 255, 255, 0.92);
|
|
color: #31445e;
|
|
text-decoration: none;
|
|
font-size: 13px;
|
|
}
|
|
|
|
.status-badge {
|
|
font-weight: 700;
|
|
color: #5f6f87;
|
|
}
|
|
|
|
.layout-grid {
|
|
display: grid;
|
|
grid-template-columns: minmax(340px, 392px) minmax(0, 1fr);
|
|
gap: 16px;
|
|
align-items: stretch;
|
|
}
|
|
|
|
.control-column {
|
|
display: grid;
|
|
gap: 0;
|
|
}
|
|
|
|
.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 {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
}
|
|
|
|
@media (max-width: 760px) {
|
|
.browser-page {
|
|
padding: 16px;
|
|
}
|
|
|
|
.page-toolbar,
|
|
.toolbar-actions {
|
|
flex-direction: column;
|
|
align-items: flex-start;
|
|
}
|
|
}
|
|
</style>
|