Files
order_site/apps/frontend/src/components/admin/AdminPlatformCloudtentaclesSection.vue
T
2026-05-14 23:11:19 +08:00

334 lines
14 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup lang="ts">
import AdminResultCard from '@/components/admin/AdminResultCard.vue'
import type {
AdminCloudtentaclesLoginTestResult,
AdminCloudtentaclesPersistedSession,
AdminCloudtentaclesSendSmsResult,
AdminCloudtentaclesValidateSessionResult,
} from '@/types/admin'
import { formatAdminDateTime } from '@/utils/admin-time'
type CloudtentaclesForm = {
baseUrl: string
username: string
password: string
phone: string
code: string
token: string
vnKey: string
vnId: number
skuId: number
skuCount: number
deviceId: string
deviceType: number
}
type CloudtentaclesStats = {
hasCredential: boolean
smsSent: boolean
validated: boolean
}
type Props = {
cloudtentaclesFilePath: string
cloudtentaclesSessionFilePath: string
cloudtentaclesForm: CloudtentaclesForm
cloudtentaclesSaving: boolean
cloudtentaclesSendingSms: boolean
cloudtentaclesTesting: boolean
cloudtentaclesValidating: boolean
cloudtentaclesResultError: string
cloudtentaclesSmsResult: AdminCloudtentaclesSendSmsResult | null
cloudtentaclesLoginResult: AdminCloudtentaclesLoginTestResult | null
cloudtentaclesValidateResult: AdminCloudtentaclesValidateSessionResult | null
cloudtentaclesDebugLoading: boolean
cloudtentaclesDebugResult: string
cloudtentaclesPersistedSession: AdminCloudtentaclesPersistedSession | null
cloudtentaclesStats: CloudtentaclesStats
handleCloudtentaclesSaveSource: () => void | Promise<void>
handleCloudtentaclesSendSmsCode: () => void | Promise<void>
handleCloudtentaclesTestLogin: () => void | Promise<void>
handleCloudtentaclesValidateSession: () => void | Promise<void>
handleCloudtentaclesFetchAsset: () => void | Promise<void>
handleCloudtentaclesFetchCategories: () => void | Promise<void>
handleCloudtentaclesFetchSkuList: () => void | Promise<void>
handleCloudtentaclesBuySku: () => void | Promise<void>
handleCloudtentaclesUseSku: () => void | Promise<void>
handleCloudtentaclesFetchKnapsack: () => void | Promise<void>
handleCloudtentaclesFetchVnList: () => void | Promise<void>
handleCloudtentaclesAppointVn: () => void | Promise<void>
handleCloudtentaclesGenerateVnLoginCode: () => void | Promise<void>
handleCloudtentaclesFetchVnCode: () => void | Promise<void>
handleCloudtentaclesVerifyVnCode: () => void | Promise<void>
handleCloudtentaclesFetchBindUrl: () => void | Promise<void>
handleCloudtentaclesBackVn: () => void | Promise<void>
handleCloudtentaclesRunFullFlow: () => void | Promise<void>
}
defineProps<Props>()
</script>
<template>
<div class="platform-section">
<!-- 配置信息 -->
<el-card class="section-card">
<template #header>
<span class="card-title">配置信息</span>
</template>
<el-descriptions :column="1" border size="small">
<el-descriptions-item label="配置文件">
<code>{{ cloudtentaclesFilePath || '-' }}</code>
</el-descriptions-item>
<el-descriptions-item label="会话文件">
<code>{{ cloudtentaclesSessionFilePath || '-' }}</code>
</el-descriptions-item>
<el-descriptions-item label="职责">
管理 cloudtentacles 外部履约平台的账号、短信验证码登录与 token 探活。该区域不处理订单来源同步。
</el-descriptions-item>
<el-descriptions-item label="已保存 token">
{{ cloudtentaclesPersistedSession?.hasToken ? `${cloudtentaclesPersistedSession.tokenMasked} / ${formatAdminDateTime(cloudtentaclesPersistedSession.loggedInAt)}` : '未保存' }}
</el-descriptions-item>
</el-descriptions>
</el-card>
<!-- 履约平台配置 -->
<el-card class="section-card">
<template #header>
<div class="card-header">
<div>
<span class="card-title">cloudtentacles 履约平台配置</span>
<p class="card-desc">先保存账号、密码、手机号和设备头,再发送短信验证码,最后执行登录测试或手动校验 token。</p>
</div>
<div class="card-actions">
<el-button :loading="cloudtentaclesSaving" @click="handleCloudtentaclesSaveSource">保存履约配置</el-button>
<el-button :loading="cloudtentaclesSendingSms" @click="handleCloudtentaclesSendSmsCode">发送短信验证码</el-button>
<el-button :loading="cloudtentaclesTesting" type="primary" @click="handleCloudtentaclesTestLogin">测试登录</el-button>
<el-button :loading="cloudtentaclesValidating" @click="handleCloudtentaclesValidateSession">校验 token</el-button>
</div>
</div>
</template>
<el-form label-width="auto" label-position="top">
<el-form-item label="Base URL">
<el-input v-model="cloudtentaclesForm.baseUrl" placeholder="https://123.207.217.176" />
</el-form-item>
<el-row :gutter="16">
<el-col :span="8">
<el-form-item label="账号">
<el-input v-model="cloudtentaclesForm.username" placeholder="cloudtentacles 登录账号" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="密码">
<el-input v-model="cloudtentaclesForm.password" type="password" show-password placeholder="cloudtentacles 登录密码" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="手机号">
<el-input v-model="cloudtentaclesForm.phone" placeholder="接收短信验证码的手机号" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="16">
<el-col :span="8">
<el-form-item label="短信验证码">
<el-input v-model="cloudtentaclesForm.code" placeholder="收到验证码后填写" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="VN Key">
<el-input v-model="cloudtentaclesForm.vnKey" placeholder="例如 1" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="VN ID">
<el-input-number v-model="cloudtentaclesForm.vnId" :min="0" controls-position="right" style="width: 100%" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="16">
<el-col :span="8">
<el-form-item label="SKU ID">
<el-input-number v-model="cloudtentaclesForm.skuId" :min="0" controls-position="right" style="width: 100%" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="购买数量">
<el-input-number v-model="cloudtentaclesForm.skuCount" :min="1" controls-position="right" style="width: 100%" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="deviceId">
<el-input v-model="cloudtentaclesForm.deviceId" placeholder="-" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="16">
<el-col :span="12">
<el-form-item label="deviceType">
<el-input-number v-model="cloudtentaclesForm.deviceType" :min="0" controls-position="right" style="width: 100%" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="token(可手动粘贴)">
<el-input v-model="cloudtentaclesForm.token" placeholder="登录成功后可自动回填,也可手动粘贴" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<el-alert
v-if="cloudtentaclesResultError"
:title="cloudtentaclesResultError"
type="error"
show-icon
:closable="false"
style="margin-top: 16px"
/>
<div class="result-grid" style="margin-top: 16px">
<AdminResultCard label="短信发送" :value="cloudtentaclesSmsResult ? '成功' : '未执行'">
<p v-if="cloudtentaclesSmsResult">
{{ cloudtentaclesSmsResult.phoneMasked }},{{ formatAdminDateTime(cloudtentaclesSmsResult.sentAt) }}
</p>
<p v-else>先发送短信验证码,确认手机号可用。</p>
</AdminResultCard>
<AdminResultCard label="登录结果" :value="cloudtentaclesLoginResult ? '成功' : '未执行'">
<p v-if="cloudtentaclesLoginResult">
{{ cloudtentaclesLoginResult.phoneMasked }},{{ formatAdminDateTime(cloudtentaclesLoginResult.loggedInAt) }}
</p>
<p v-else>填写短信验证码后可执行测试登录。</p>
</AdminResultCard>
<AdminResultCard label="会话校验" :value="cloudtentaclesValidateResult ? '成功' : '未执行'">
<p v-if="cloudtentaclesValidateResult">
权限 {{ cloudtentaclesValidateResult.session.permissionCount }} 项,余额 {{ cloudtentaclesValidateResult.asset }}
</p>
<p v-else>登录成功后可继续校验 token 会话。</p>
</AdminResultCard>
</div>
</el-card>
<!-- 开发调试工具 -->
<el-card class="section-card">
<template #header>
<div>
<span class="card-title">cloudtentacles 开发调试工具</span>
<p class="card-desc">仅开发模式展示。用于调试分类、SKU、购买、发货、虚拟号、兑换链接和结束退还链路。</p>
</div>
</template>
<div class="debug-buttons">
<el-button :loading="cloudtentaclesDebugLoading" @click="handleCloudtentaclesFetchAsset">余额</el-button>
<el-button :loading="cloudtentaclesDebugLoading" @click="handleCloudtentaclesFetchCategories">分类</el-button>
<el-button :loading="cloudtentaclesDebugLoading" @click="handleCloudtentaclesFetchSkuList">SKU</el-button>
<el-button :loading="cloudtentaclesDebugLoading" @click="handleCloudtentaclesBuySku">购买</el-button>
<el-button :loading="cloudtentaclesDebugLoading" type="primary" @click="handleCloudtentaclesUseSku">发货</el-button>
<el-button :loading="cloudtentaclesDebugLoading" @click="handleCloudtentaclesFetchKnapsack">背包</el-button>
<el-button :loading="cloudtentaclesDebugLoading" @click="handleCloudtentaclesFetchVnList">虚拟号列表</el-button>
<el-button :loading="cloudtentaclesDebugLoading" @click="handleCloudtentaclesAppointVn">申请虚拟号</el-button>
<el-button :loading="cloudtentaclesDebugLoading" @click="handleCloudtentaclesGenerateVnLoginCode">生成登录码</el-button>
<el-button :loading="cloudtentaclesDebugLoading" @click="handleCloudtentaclesFetchVnCode">获取验证码</el-button>
<el-button :loading="cloudtentaclesDebugLoading" @click="handleCloudtentaclesVerifyVnCode">校验验证码</el-button>
<el-button :loading="cloudtentaclesDebugLoading" @click="handleCloudtentaclesFetchBindUrl">兑换链接</el-button>
<el-button :loading="cloudtentaclesDebugLoading" type="warning" @click="handleCloudtentaclesBackVn">退还号码</el-button>
<el-button :loading="cloudtentaclesDebugLoading" type="success" @click="handleCloudtentaclesRunFullFlow">完整流程</el-button>
</div>
<pre v-if="cloudtentaclesDebugResult" class="debug-pre" style="margin-top: 16px">{{ cloudtentaclesDebugResult }}</pre>
<el-empty v-else description="执行一次调试动作后,这里会显示原始返回数据。" :image-size="60" />
</el-card>
<!-- 调试结果 -->
<el-card v-if="cloudtentaclesLoginResult || cloudtentaclesValidateResult" class="section-card">
<template #header>
<div>
<span class="card-title">cloudtentacles 调试结果</span>
<p class="card-desc">用于确认当前账号会话、权限和用户信息,后续发货执行器会复用这里的登录能力。</p>
</div>
</template>
<div class="result-grid">
<AdminResultCard
label="当前 token"
:value="cloudtentaclesValidateResult?.session.tokenMasked || cloudtentaclesLoginResult?.session.tokenMasked || '-'"
>
<p>原始 token 已写入上方输入框,可继续用于校验或后续调试。</p>
</AdminResultCard>
<AdminResultCard
label="权限"
:value="`${cloudtentaclesValidateResult?.session.permissionCount || cloudtentaclesLoginResult?.session.permissionCount || 0} 项`"
>
<p>{{ (cloudtentaclesValidateResult?.session.permissions || cloudtentaclesLoginResult?.session.permissions || []).join('、') || '暂无' }}</p>
</AdminResultCard>
<AdminResultCard label="余额" :value="cloudtentaclesValidateResult?.asset || cloudtentaclesLoginResult?.asset || 0">
<p>可用于确认当前履约账号状态。</p>
</AdminResultCard>
</div>
</el-card>
</div>
</template>
<style scoped>
.platform-section {
display: grid;
gap: var(--space-4);
}
.section-card {
border-radius: var(--radius-lg);
}
.card-title {
font-size: var(--text-lg);
font-weight: 600;
color: var(--text-primary);
}
.card-desc {
margin: var(--space-1) 0 0;
font-size: var(--text-sm);
color: var(--text-secondary);
}
.card-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
flex-wrap: wrap;
gap: var(--space-3);
}
.card-actions {
display: flex;
gap: var(--space-2);
flex-shrink: 0;
flex-wrap: wrap;
}
.debug-buttons {
display: flex;
gap: var(--space-2);
flex-wrap: wrap;
}
.result-grid {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: var(--space-3);
}
.debug-pre {
margin: 0;
padding: var(--space-4);
border-radius: var(--radius-lg);
background: #0f172a;
color: #e2e8f0;
overflow-y: auto;
white-space: pre-wrap;
word-break: break-word;
font-size: var(--text-xs);
line-height: 1.6;
font-family: var(--font-mono);
}
</style>