拆分平台配置页快手核销区块

This commit is contained in:
yml
2026-05-04 11:06:37 +08:00
parent 60c2d91385
commit 254a03129a
3 changed files with 368 additions and 278 deletions
@@ -0,0 +1,337 @@
<script setup lang="ts">
import type { EditableKuaishouEticketShop } from '@/composables/admin/platform-shops/types'
import type {
AdminKuaishouEticketConsumeResult,
AdminKuaishouEticketDetailResult,
AdminKuaishouEticketShopInfoResult,
} from '@/types/admin'
type KuaishouEticketForm = {
baseUrl: string
selectedShopId: string
queryTicketCode: string
eTicketId: string
oid: string
formToken: string
num: number
storeId: string
}
type KuaishouEticketStats = {
configuredShopCount: number
cookieReadyCount: number
selectedShopReady: boolean
detailReady: boolean
consumed: boolean
}
type Props = {
kuaishouEticketFilePath: string
kuaishouEticketShops: EditableKuaishouEticketShop[]
kuaishouEticketForm: KuaishouEticketForm
kuaishouEticketSaving: boolean
kuaishouEticketResolvingShopId: string
kuaishouEticketDetailing: boolean
kuaishouEticketConsuming: boolean
kuaishouEticketResultError: string
kuaishouEticketShopInfoResult: AdminKuaishouEticketShopInfoResult | null
kuaishouEticketDetailResult: AdminKuaishouEticketDetailResult | null
kuaishouEticketConsumeResult: AdminKuaishouEticketConsumeResult | null
activeKuaishouEticketShop: EditableKuaishouEticketShop | null
kuaishouEticketStats: KuaishouEticketStats
addKuaishouEticketShop: () => void
removeKuaishouEticketShop: (id: string) => void
isKuaishouEticketShopExpanded: (id: string) => boolean
toggleKuaishouEticketShop: (id: string) => void
expandAllKuaishouEticketShops: () => void
collapseAllKuaishouEticketShops: () => void
setKuaishouEticketDebugShop: (id: string) => void
describeKuaishouEticketShop: (shop: EditableKuaishouEticketShop) => string
getKuaishouEticketShopInitial: (shop: EditableKuaishouEticketShop) => string
handleKuaishouEticketResolveShopInfo: (shop: EditableKuaishouEticketShop) => void | Promise<void>
handleKuaishouEticketSaveSource: () => void | Promise<void>
handleKuaishouEticketQueryDetail: () => void | Promise<void>
handleKuaishouEticketConsume: () => void | Promise<void>
}
defineProps<Props>()
</script>
<template>
<section class="meta-card">
<div class="meta-line">
<span class="meta-label">配置文件</span>
<code>{{ kuaishouEticketFilePath || '-' }}</code>
</div>
<div class="meta-line">
<span class="meta-label">职责</span>
<span>这是独立的新流程只负责快手小店券核销调试与配置不接入当前已完成的订单来源和履约主链路</span>
</div>
<div class="meta-line">
<span class="meta-label">建议流程</span>
<span>先为每个店铺录入 `kshopName + Cookie`可用 info 接口自动识别店铺名与店铺 ID保存后选择目标店铺再查询券码并执行核销</span>
</div>
</section>
<section class="table-card">
<div class="section-title-row">
<div>
<h3>快手小店店铺配置</h3>
<p>支持多店铺配置建议先粘贴 Cookie再用 info 接口自动识别 `kshopName / shopId`最后统一保存到文件</p>
</div>
<div class="section-actions">
<el-button :disabled="kuaishouEticketShops.length === 0" text @click="expandAllKuaishouEticketShops">全部展开</el-button>
<el-button :disabled="kuaishouEticketShops.length === 0" text @click="collapseAllKuaishouEticketShops">全部折叠</el-button>
<el-button round @click="addKuaishouEticketShop">新增店铺</el-button>
<el-button :loading="kuaishouEticketSaving" round type="primary" @click="handleKuaishouEticketSaveSource">保存配置</el-button>
</div>
</div>
<div class="kuaishou-workspace">
<div class="kuaishou-shop-stack">
<div class="form-grid kuaishou-base-grid">
<label class="field-block field-wide">
<span>Base URL</span>
<input v-model="kuaishouEticketForm.baseUrl" class="text-input" placeholder="https://s.kwaixiaodian.com" />
</label>
</div>
<div v-if="kuaishouEticketShops.length === 0" class="empty-inline">当前还没有快手小店店铺配置先新增一条</div>
<article
v-for="shop in kuaishouEticketShops"
:key="shop.id"
:class="['shop-card', 'kuaishou-shop-card', {
'is-active': kuaishouEticketForm.selectedShopId === shop.id,
'is-expanded': isKuaishouEticketShopExpanded(shop.id),
}]"
>
<button type="button" class="shop-summary kuaishou-shop-summary" @click="toggleKuaishouEticketShop(shop.id)">
<div class="kuaishou-shop-head">
<div class="shop-avatar">
<img v-if="shop.userAvatar" :src="shop.userAvatar" :alt="shop.kshopName || shop.shopId || '店铺头像'" />
<span v-else>{{ getKuaishouEticketShopInitial(shop) }}</span>
</div>
<div class="shop-summary-main">
<div class="shop-title-row">
<strong>{{ shop.kshopName || shop.shopId || '未命名店铺' }}</strong>
<span class="shop-status" :class="{ 'is-disabled': !shop.enabled }">
{{ shop.enabled ? '已启用' : '已停用' }}
</span>
</div>
<span class="cell-subtle">店铺 ID{{ shop.shopId || '等待 info 自动识别' }}</span>
<div class="shop-tag-row">
<span class="shop-mini-tag" :class="{ 'is-ready': Boolean(shop.cookie.trim()) }">
{{ shop.cookie.trim() ? 'Cookie 已粘贴' : '待补 Cookie' }}
</span>
<span class="shop-mini-tag" :class="{ 'is-ready': Boolean(shop.userAvatar || shop.shopId.trim()) }">
{{ shop.userAvatar || shop.shopId.trim() ? '店铺信息已识别' : '未读取店铺信息' }}
</span>
<span v-if="kuaishouEticketForm.selectedShopId === shop.id" class="shop-mini-tag is-highlight">当前调试店铺</span>
</div>
</div>
</div>
<div class="shop-summary-side">
<span class="shop-summary-copy">{{ describeKuaishouEticketShop(shop) }}</span>
<span class="shop-toggle">{{ isKuaishouEticketShopExpanded(shop.id) ? '收起详情' : '展开配置' }}</span>
</div>
</button>
<div v-if="isKuaishouEticketShopExpanded(shop.id)" class="shop-body kuaishou-shop-body">
<div class="form-grid">
<label class="field-block">
<span>kshopName</span>
<input v-model="shop.kshopName" class="text-input" placeholder="例如 稚嫩游戏交易店" />
</label>
<label class="field-block">
<span>店铺 ID</span>
<input v-model="shop.shopId" class="text-input" readonly placeholder="通过 info 接口自动识别" />
</label>
<label class="field-block field-wide">
<span>Cookie</span>
<textarea
v-model="shop.cookie"
class="text-area kuaishou-cookie-area"
placeholder="直接粘贴该店铺请求头中的完整 Cookie"
/>
</label>
</div>
<div class="shop-actions kuaishou-shop-actions">
<label class="checkbox-line">
<input v-model="shop.enabled" type="checkbox" />
<span>启用该店铺</span>
</label>
<span class="cell-subtle">{{ shop.userAvatar ? '已识别头像,可直接作为调试店铺' : '建议先读取店铺信息确认 Cookie 归属' }}</span>
<el-button
:loading="kuaishouEticketResolvingShopId === shop.id"
link
type="primary"
@click="handleKuaishouEticketResolveShopInfo(shop)"
>
读取店铺信息
</el-button>
<el-button
link
:type="kuaishouEticketForm.selectedShopId === shop.id ? 'success' : 'primary'"
@click="setKuaishouEticketDebugShop(shop.id)"
>
{{ kuaishouEticketForm.selectedShopId === shop.id ? '当前调试店铺' : '设为调试店铺' }}
</el-button>
<el-button link type="danger" @click="removeKuaishouEticketShop(shop.id)">删除</el-button>
</div>
</div>
</article>
</div>
<aside class="kuaishou-side-panel">
<article class="result-card kuaishou-side-card">
<span class="result-label">店铺总数</span>
<strong>{{ kuaishouEticketStats.configuredShopCount }}</strong>
<p>其中 Cookie 就绪 {{ kuaishouEticketStats.cookieReadyCount }} </p>
</article>
<article class="result-card kuaishou-side-card">
<span class="result-label">当前调试店铺</span>
<strong>{{ activeKuaishouEticketShop?.kshopName || activeKuaishouEticketShop?.shopId || '未选择' }}</strong>
<p>{{ kuaishouEticketStats.selectedShopReady ? '已具备调试条件,可直接查询核销信息。' : '请选择店铺并补全 Cookie。' }}</p>
</article>
<article class="result-card kuaishou-side-card">
<span class="result-label">最近识别结果</span>
<strong>{{ kuaishouEticketShopInfoResult?.shop.kshopName || kuaishouEticketShopInfoResult?.shop.shopId || '未执行' }}</strong>
<p v-if="kuaishouEticketShopInfoResult">结算状态 {{ kuaishouEticketShopInfoResult.shop.settleStatus }}</p>
<p v-else>可用读取店铺信息校验 Cookie 是否属于预期店铺</p>
</article>
<article class="result-card kuaishou-side-card kuaishou-guide-card">
<span class="result-label">建议顺序</span>
<strong>先配置再调试</strong>
<p>1. 新增店铺并粘贴 Cookie</p>
<p>2. 读取店铺信息确认 kshopName / shopId</p>
<p>3. 设为调试店铺后查询券码并执行核销</p>
</article>
</aside>
</div>
<p v-if="kuaishouEticketResultError" class="error-copy">{{ kuaishouEticketResultError }}</p>
</section>
<section class="table-card">
<div class="section-title-row">
<div>
<h3>快手小店核销调试</h3>
<p>这里使用当前调试店铺 Cookie 执行查询与核销`eTicketId / oid / formToken` 都来自查询结果</p>
</div>
<div class="section-actions">
<el-button :loading="kuaishouEticketDetailing" round @click="handleKuaishouEticketQueryDetail">查询核销信息</el-button>
<el-button :loading="kuaishouEticketConsuming" round type="primary" @click="handleKuaishouEticketConsume">执行核销</el-button>
</div>
</div>
<div class="kuaishou-debug-layout">
<div class="kuaishou-debug-panel">
<div class="form-grid">
<label class="field-block">
<span>调试店铺</span>
<select v-model="kuaishouEticketForm.selectedShopId" class="text-input">
<option value="">请选择店铺</option>
<option v-for="shop in kuaishouEticketShops" :key="shop.id" :value="shop.id">
{{ shop.kshopName || shop.shopId || '未命名店铺' }}
</option>
</select>
</label>
<label class="field-block">
<span>查询券码</span>
<input v-model="kuaishouEticketForm.queryTicketCode" class="text-input" placeholder="用于查询核销信息" />
</label>
<label class="field-block">
<span>核销数量</span>
<input v-model.number="kuaishouEticketForm.num" class="text-input" type="number" min="1" />
</label>
<label class="field-block">
<span>storeId</span>
<input v-model="kuaishouEticketForm.storeId" class="text-input" placeholder="默认 0" />
</label>
</div>
</div>
<div class="kuaishou-debug-panel kuaishou-debug-panel-soft">
<div class="kuaishou-debug-head">
<div>
<span class="result-label">自动回填参数</span>
<strong>查询后生成</strong>
</div>
<span class="cell-subtle">这些字段不需要手动填写</span>
</div>
<div class="form-grid">
<label class="field-block">
<span>eTicketId</span>
<input :value="kuaishouEticketForm.eTicketId" class="text-input" readonly placeholder="查询后自动回填" />
</label>
<label class="field-block">
<span>oid</span>
<input :value="kuaishouEticketForm.oid" class="text-input" readonly placeholder="查询后自动回填" />
</label>
<label class="field-block field-wide">
<span>formToken</span>
<input :value="kuaishouEticketForm.formToken" class="text-input" readonly placeholder="查询后自动回填" />
</label>
</div>
</div>
</div>
<div class="result-grid">
<article class="result-card">
<span class="result-label">详情查询</span>
<strong>{{ kuaishouEticketDetailResult ? (kuaishouEticketDetailResult.ok ? '成功' : '已返回') : '未执行' }}</strong>
<p v-if="kuaishouEticketDetailResult?.detail">
{{ kuaishouEticketDetailResult.goods?.itemTitle || kuaishouEticketDetailResult.goods?.skuDesc || '-' }}
剩余 {{ kuaishouEticketDetailResult.detail.leftCount }} / {{ kuaishouEticketDetailResult.detail.totalCount }}
</p>
<p v-else>先通过查询券码获取核销详情并自动带出核销参数</p>
</article>
<article class="result-card">
<span class="result-label">核销执行</span>
<strong>{{ kuaishouEticketConsumeResult ? (kuaishouEticketConsumeResult.consumed ? '已处理' : '已返回') : '未执行' }}</strong>
<p v-if="kuaishouEticketConsumeResult">
{{ kuaishouEticketConsumeResult.errorMessage || (kuaishouEticketConsumeResult.ok ? '核销成功' : '接口已返回结果') }}
</p>
<p v-else>查询详情后再执行核销会更稳妥</p>
</article>
</div>
</section>
<section v-if="kuaishouEticketDetailResult" class="table-card">
<div class="section-title-row">
<div>
<h3>核销详情返回</h3>
<p>成功查询后会自动回填 `eTicketId``oid``formToken` 和建议核销数量</p>
</div>
</div>
<pre class="debug-result">{{ JSON.stringify(kuaishouEticketDetailResult.raw, null, 2) }}</pre>
</section>
<section v-if="kuaishouEticketConsumeResult" class="table-card">
<div class="section-title-row">
<div>
<h3>核销执行返回</h3>
<p>这里保留接口原始响应方便核对 `result``error_msg` `requestId`</p>
</div>
</div>
<pre class="debug-result">{{ JSON.stringify(kuaishouEticketConsumeResult.raw, null, 2) }}</pre>
</section>
</template>