init
This commit is contained in:
@@ -0,0 +1,299 @@
|
||||
<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>
|
||||
Reference in New Issue
Block a user