Phase 1 - 消灭巨石页面: - 拆分 AdminKuaishouCloudFulfillmentView (1,224行→kuaishou-cloud/子目录) - 拆分 AdminFulfillmentBindingsView (989行→bindings/子目录) - 拆分 AdminTaskDetailView (1,007行→9个子组件+2个composable) - 合并去重 useClaimPage + useAdminManualRedeemPage (1,554行→共享模块+差异化薄层) - 拆分 services/admin/platform-config.ts (477行→8个领域子模块) Phase 2 - 架构收口: - 拆分 types/admin.ts (925行→13个子文件+platform-config/子目录) - 统一 API code 检查(http.ts拦截器统一处理业务错误) - 修改 apiPost 签名消除 as unknown as 类型断言(6处) - 新增 BusinessError 类型便于错误分类处理 所有改动通过 vue-tsc --noEmit 零错误和 vite build 验证
230 lines
5.6 KiB
TypeScript
230 lines
5.6 KiB
TypeScript
import { computed, onBeforeUnmount, ref, watch } from 'vue'
|
|
|
|
import { showError, showSuccess } from '@/lib/feedback'
|
|
import {
|
|
createTencentBrowserSession,
|
|
refreshTencentBrowserSessionPage,
|
|
removeTencentBrowserSession,
|
|
} from '@/services/tencent/session'
|
|
import { redeemTencentBrowserSession } from '@/services/tencent/redeem'
|
|
import type { TencentBrowserSessionData, TencentLoginType } from '@/types/tencent/session'
|
|
|
|
import { notifyTencentActionError } from './tencent/session-errors'
|
|
import { useTencentBrowserSessionPolling } from './tencent/useTencentBrowserSessionPolling'
|
|
import { useTencentBrowserSessionPresentation } from './tencent/useTencentBrowserSessionPresentation'
|
|
|
|
const DEFAULT_LOGIN_TYPE: TencentLoginType = 'qq'
|
|
|
|
export function useTencentBrowserSessionPage() {
|
|
const sessionLoading = ref(false)
|
|
const redeemLoading = ref(false)
|
|
const loginType = ref<TencentLoginType>(DEFAULT_LOGIN_TYPE)
|
|
const session = ref<TencentBrowserSessionData | null>(null)
|
|
const redeemCode = ref('')
|
|
const maxAttempts = ref(6)
|
|
const roleConfirmed = ref(false)
|
|
|
|
const activityInfo = computed(() => session.value?.activityInfo || null)
|
|
const roleReady = computed(() => Boolean(activityInfo.value?.role?.ready))
|
|
const hasSession = computed(() => Boolean(session.value?.sessionId))
|
|
const loginTypeLabel = computed(() => (loginType.value === 'wx' ? '微信' : 'QQ'))
|
|
|
|
const loginTabs = [
|
|
{ value: 'qq' as const, label: 'QQ账号登录' },
|
|
{ value: 'wx' as const, label: '微信账号登录' },
|
|
]
|
|
|
|
const {
|
|
refreshSession,
|
|
resetPolling,
|
|
resetPollingWarning,
|
|
restartPollingIfActive,
|
|
sessionNotice,
|
|
startPolling,
|
|
} = useTencentBrowserSessionPolling({
|
|
session,
|
|
sessionLoading,
|
|
notifyActionError: notifyTencentActionError,
|
|
})
|
|
|
|
const {
|
|
canRedeem,
|
|
initButtonLabel,
|
|
qrImage,
|
|
redeemBlockedReason,
|
|
redeemButtonLabel,
|
|
resultFacts,
|
|
roleFacts,
|
|
scanInstruction,
|
|
screenshotEmptyMessage,
|
|
screenshotEmptyTitle,
|
|
screenshotUrl,
|
|
statusLabel,
|
|
} = useTencentBrowserSessionPresentation({
|
|
activityInfo,
|
|
hasSession,
|
|
loginTypeLabel,
|
|
redeemLoading,
|
|
roleConfirmed,
|
|
roleReady,
|
|
session,
|
|
sessionLoading,
|
|
sessionNotice,
|
|
})
|
|
|
|
watch(
|
|
() =>
|
|
[
|
|
session.value?.sessionId || '',
|
|
activityInfo.value?.nickname || '',
|
|
activityInfo.value?.role?.roleId || '',
|
|
activityInfo.value?.role?.roleName || '',
|
|
activityInfo.value?.role?.area || '',
|
|
activityInfo.value?.role?.partition || '',
|
|
].join('|'),
|
|
() => {
|
|
roleConfirmed.value = false
|
|
},
|
|
)
|
|
|
|
async function createSessionFlow(nextLoginType = loginType.value) {
|
|
resetPolling()
|
|
resetPollingWarning()
|
|
sessionLoading.value = true
|
|
|
|
try {
|
|
loginType.value = nextLoginType
|
|
await teardownSession()
|
|
|
|
const response = await createTencentBrowserSession({
|
|
loginType: nextLoginType,
|
|
})
|
|
|
|
session.value = response.data
|
|
startPolling()
|
|
} catch (error) {
|
|
notifyTencentActionError(error, '创建浏览器会话失败')
|
|
} finally {
|
|
sessionLoading.value = false
|
|
}
|
|
}
|
|
|
|
async function reloadSessionPage() {
|
|
if (!session.value?.sessionId) {
|
|
return
|
|
}
|
|
|
|
resetPolling()
|
|
resetPollingWarning()
|
|
sessionLoading.value = true
|
|
|
|
try {
|
|
const response = await refreshTencentBrowserSessionPage(session.value.sessionId)
|
|
|
|
session.value = response.data
|
|
restartPollingIfActive(response.data)
|
|
} catch (error) {
|
|
notifyTencentActionError(error, '刷新后端页面失败')
|
|
await refreshSession({ silent: true })
|
|
restartPollingIfActive()
|
|
} finally {
|
|
sessionLoading.value = false
|
|
}
|
|
}
|
|
|
|
async function switchLoginType(nextLoginType: TencentLoginType) {
|
|
if (nextLoginType === loginType.value) {
|
|
return
|
|
}
|
|
|
|
loginType.value = nextLoginType
|
|
|
|
if (session.value?.sessionId) {
|
|
await createSessionFlow(nextLoginType)
|
|
}
|
|
}
|
|
|
|
async function redeemNow() {
|
|
if (!session.value?.sessionId) {
|
|
showError('请先创建浏览器会话')
|
|
return
|
|
}
|
|
|
|
if (!redeemCode.value.trim()) {
|
|
showError('请输入兑换码')
|
|
return
|
|
}
|
|
|
|
redeemLoading.value = true
|
|
resetPolling()
|
|
resetPollingWarning()
|
|
|
|
try {
|
|
const response = await redeemTencentBrowserSession(session.value.sessionId, {
|
|
code: redeemCode.value.trim(),
|
|
maxAttempts: maxAttempts.value,
|
|
})
|
|
|
|
session.value = response.data
|
|
showSuccess(response.msg || '兑换完成')
|
|
} catch (error) {
|
|
notifyTencentActionError(error, '浏览器兑换失败')
|
|
await refreshSession({ silent: true })
|
|
} finally {
|
|
redeemLoading.value = false
|
|
}
|
|
}
|
|
|
|
async function teardownSession() {
|
|
if (!session.value?.sessionId) {
|
|
return
|
|
}
|
|
|
|
const sessionId = session.value.sessionId
|
|
session.value = null
|
|
|
|
try {
|
|
await removeTencentBrowserSession(sessionId)
|
|
} catch {
|
|
// ignore close failures on local teardown
|
|
}
|
|
}
|
|
|
|
onBeforeUnmount(() => {
|
|
resetPolling()
|
|
void teardownSession()
|
|
})
|
|
|
|
return {
|
|
activityInfo,
|
|
canRedeem,
|
|
teardownSession,
|
|
createSessionFlow,
|
|
hasSession,
|
|
initButtonLabel,
|
|
loginTabs,
|
|
loginType,
|
|
loginTypeLabel,
|
|
maxAttempts,
|
|
qrImage,
|
|
redeemBlockedReason,
|
|
redeemButtonLabel,
|
|
redeemCode,
|
|
redeemLoading,
|
|
redeemNow,
|
|
reloadSessionPage,
|
|
resultFacts,
|
|
roleConfirmed,
|
|
roleFacts,
|
|
scanInstruction,
|
|
screenshotEmptyMessage,
|
|
screenshotEmptyTitle,
|
|
screenshotUrl,
|
|
session,
|
|
sessionLoading,
|
|
sessionNotice,
|
|
statusLabel,
|
|
switchLoginType,
|
|
}
|
|
}
|