拆分平台配置页 Agiso 与 khhao 区块
This commit is contained in:
Vendored
+2
@@ -12,6 +12,8 @@ export {}
|
||||
declare module 'vue' {
|
||||
export interface GlobalComponents {
|
||||
AdminPaginationBar: typeof import('./components/admin/AdminPaginationBar.vue')['default']
|
||||
AdminPlatformAgisoSection: typeof import('./components/admin/AdminPlatformAgisoSection.vue')['default']
|
||||
AdminPlatformKhhaoSection: typeof import('./components/admin/AdminPlatformKhhaoSection.vue')['default']
|
||||
AdminStatusTag: typeof import('./components/admin/AdminStatusTag.vue')['default']
|
||||
ElButton: typeof import('element-plus/es')['ElButton']
|
||||
ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
|
||||
|
||||
@@ -0,0 +1,184 @@
|
||||
<script setup lang="ts">
|
||||
import type { AdminAgisoObservedShopItem } from '@/types/admin'
|
||||
import { formatAdminDateTime } from '@/utils/admin-time'
|
||||
|
||||
import type { EditableDefaults, EditableShop } from '@/composables/admin/platform-shops/types'
|
||||
|
||||
type Props = {
|
||||
agisoFilePath: string
|
||||
defaults: EditableDefaults
|
||||
shops: EditableShop[]
|
||||
observedShops: AdminAgisoObservedShopItem[]
|
||||
saving: boolean
|
||||
isShopExpanded: (id: string) => boolean
|
||||
toggleShop: (id: string) => void
|
||||
expandAllShops: () => void
|
||||
collapseAllShops: () => void
|
||||
describeShop: (shop: EditableShop) => string
|
||||
removeShop: (id: string) => void
|
||||
importObservedShop: (item: AdminAgisoObservedShopItem) => void
|
||||
saveAgisoConfigs: () => void | Promise<void>
|
||||
}
|
||||
|
||||
defineProps<Props>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="meta-card">
|
||||
<div class="meta-line">
|
||||
<span class="meta-label">配置文件</span>
|
||||
<code>{{ agisoFilePath || '-' }}</code>
|
||||
</div>
|
||||
<div class="meta-line">
|
||||
<span class="meta-label">职责</span>
|
||||
<span>管理 Agiso 默认消息模板、店铺 AccessToken 以及店铺级模板覆盖。该区域不处理 khhao 登录与拉单。</span>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="table-card">
|
||||
<div class="section-title-row">
|
||||
<div>
|
||||
<h3>默认消息模板</h3>
|
||||
<p>Agiso 店铺未单独配置时,统一使用这里的领取消息与自动发货消息模板。</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-grid">
|
||||
<label class="field-block field-wide">
|
||||
<span>默认领取链接消息模板</span>
|
||||
<textarea
|
||||
v-model="defaults.messageTemplate"
|
||||
class="text-area"
|
||||
placeholder="例如:您的订单 {platformOrderId} 已创建领取链接,请尽快打开并完成领取:{claimUrl}"
|
||||
/>
|
||||
</label>
|
||||
|
||||
<label class="field-block field-wide">
|
||||
<span>默认自动发货消息模板</span>
|
||||
<textarea
|
||||
v-model="defaults.autoDeliveryMessageTemplate"
|
||||
class="text-area"
|
||||
placeholder="例如:您的订单 {platformOrderId} 已完成自动发货,请注意查收。"
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="table-card">
|
||||
<div class="section-title-row">
|
||||
<div>
|
||||
<h3>Agiso 店铺列表</h3>
|
||||
<p>每家店铺可单独配置 AccessToken 和模板覆盖。建议先用“近期识别到的店铺”导入,再补 AccessToken。</p>
|
||||
</div>
|
||||
<div class="section-actions">
|
||||
<el-button :disabled="shops.length === 0" text @click="expandAllShops">全部展开</el-button>
|
||||
<el-button :disabled="shops.length === 0" text @click="collapseAllShops">全部折叠</el-button>
|
||||
<el-button :loading="saving" round type="primary" @click="saveAgisoConfigs">保存 Agiso 配置</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="shops.length === 0" class="empty-inline">当前还没有 Agiso 店铺配置,先新增一条。</div>
|
||||
|
||||
<div v-for="shop in shops" :key="shop.id" class="shop-card">
|
||||
<button type="button" class="shop-summary" @click="toggleShop(shop.id)">
|
||||
<div class="shop-summary-main">
|
||||
<strong>{{ shop.shopName || shop.shopId || '未命名店铺' }}</strong>
|
||||
<span class="cell-subtle">ID: {{ shop.shopId || '未填写' }}</span>
|
||||
</div>
|
||||
<div class="shop-summary-side">
|
||||
<span class="shop-status" :class="{ 'is-disabled': !shop.enabled }">
|
||||
{{ shop.enabled ? '已启用' : '已停用' }}
|
||||
</span>
|
||||
<span class="shop-summary-copy">{{ describeShop(shop) }}</span>
|
||||
<span class="shop-toggle">{{ isShopExpanded(shop.id) ? '收起' : '展开' }}</span>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<div v-if="isShopExpanded(shop.id)" class="shop-body">
|
||||
<div class="form-grid">
|
||||
<label class="field-block">
|
||||
<span>店铺 ID</span>
|
||||
<input v-model="shop.shopId" class="text-input" placeholder="例如 693760716" />
|
||||
</label>
|
||||
|
||||
<label class="field-block">
|
||||
<span>店铺名称</span>
|
||||
<input v-model="shop.shopName" class="text-input" placeholder="例如 大锤商行" />
|
||||
</label>
|
||||
|
||||
<label class="field-block field-wide">
|
||||
<span>AccessToken</span>
|
||||
<input v-model="shop.accessToken" class="text-input" placeholder="AldsIdle..." />
|
||||
</label>
|
||||
|
||||
<label class="field-block field-wide">
|
||||
<span>领取链接消息模板</span>
|
||||
<textarea
|
||||
v-model="shop.messageTemplate"
|
||||
class="text-area"
|
||||
placeholder="留空则沿用默认领取链接模板"
|
||||
/>
|
||||
</label>
|
||||
|
||||
<label class="field-block field-wide">
|
||||
<span>自动发货消息模板</span>
|
||||
<textarea
|
||||
v-model="shop.autoDeliveryMessageTemplate"
|
||||
class="text-area"
|
||||
placeholder="留空则沿用默认自动发货模板"
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="shop-actions">
|
||||
<label class="checkbox-line">
|
||||
<input v-model="shop.enabled" type="checkbox" />
|
||||
<span>启用店铺级覆盖</span>
|
||||
</label>
|
||||
<el-button link type="danger" @click="removeShop(shop.id)">删除</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="table-card">
|
||||
<div class="section-title-row">
|
||||
<div>
|
||||
<h3>近期识别到的 Agiso 店铺</h3>
|
||||
<p>从 webhook 自动识别出的店铺,可一键导入到上方店铺列表。</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>店铺</th>
|
||||
<th>Webhook 次数</th>
|
||||
<th>最近出现时间</th>
|
||||
<th>状态</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="item in observedShops" :key="item.shopId">
|
||||
<td>
|
||||
<div class="cell-stack">
|
||||
<strong>{{ item.displayShopName || item.shopId }}</strong>
|
||||
<span class="cell-subtle">ID: {{ item.shopId }}</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>{{ item.webhookEventCount }}</td>
|
||||
<td>{{ formatAdminDateTime(item.latestSeenAt) }}</td>
|
||||
<td>{{ item.configured ? '已配置' : '未配置' }}</td>
|
||||
<td>
|
||||
<el-button v-if="!item.configured" link type="primary" @click="importObservedShop(item)">导入到店铺</el-button>
|
||||
<span v-else class="cell-subtle">已存在</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="observedShops.length === 0">
|
||||
<td colspan="5" class="empty-inline">最近还没有识别到可用店铺。</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
</template>
|
||||
@@ -0,0 +1,269 @@
|
||||
<script setup lang="ts">
|
||||
import type {
|
||||
AdminKhhaoLoginTestResult,
|
||||
AdminKhhaoOrderQueryResult,
|
||||
AdminKhhaoOrderSyncResult,
|
||||
AdminKhhaoSyncState,
|
||||
} from '@/types/admin'
|
||||
import { formatAdminDateTime } from '@/utils/admin-time'
|
||||
|
||||
type KhhaoForm = {
|
||||
baseUrl: string
|
||||
username: string
|
||||
password: string
|
||||
page: number
|
||||
limit: number
|
||||
maxCaptchaAttempts: number
|
||||
autoSyncEnabled: boolean
|
||||
autoSyncIntervalMinutes: number
|
||||
autoSyncPages: number
|
||||
autoSyncPageSize: number
|
||||
}
|
||||
|
||||
type Props = {
|
||||
khhaoFilePath: string
|
||||
khhaoStateFilePath: string
|
||||
khhaoForm: KhhaoForm
|
||||
khhaoSaving: boolean
|
||||
khhaoTesting: boolean
|
||||
khhaoQuerying: boolean
|
||||
khhaoSyncing: boolean
|
||||
khhaoResultError: string
|
||||
khhaoLoginResult: AdminKhhaoLoginTestResult | null
|
||||
khhaoQueryResult: AdminKhhaoOrderQueryResult | null
|
||||
khhaoSyncResult: AdminKhhaoOrderSyncResult | null
|
||||
khhaoAutoSyncState: AdminKhhaoSyncState | null
|
||||
handleKhhaoSaveSource: () => void | Promise<void>
|
||||
handleKhhaoTestLogin: () => void | Promise<void>
|
||||
handleKhhaoQueryOrders: () => void | Promise<void>
|
||||
handleKhhaoSyncOrders: () => void | Promise<void>
|
||||
}
|
||||
|
||||
defineProps<Props>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="meta-card">
|
||||
<div class="meta-line">
|
||||
<span class="meta-label">配置文件</span>
|
||||
<code>{{ khhaoFilePath || '-' }}</code>
|
||||
</div>
|
||||
<div class="meta-line">
|
||||
<span class="meta-label">状态文件</span>
|
||||
<code>{{ khhaoStateFilePath || '-' }}</code>
|
||||
</div>
|
||||
<div class="meta-line">
|
||||
<span class="meta-label">职责</span>
|
||||
<span>管理 khhao 的来源地址、登录账号、密码与验证码重试次数,并执行测试登录、查询订单与手动同步。</span>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="table-card">
|
||||
<div class="section-title-row">
|
||||
<div>
|
||||
<h3>khhao 来源配置</h3>
|
||||
<p>先保存 khhao 凭据,再按需执行登录测试、订单查询或同步。履约规则请前往“履约配置”页处理。</p>
|
||||
</div>
|
||||
<div class="section-actions">
|
||||
<el-button :loading="khhaoSaving" round @click="handleKhhaoSaveSource">保存来源配置</el-button>
|
||||
<el-button :loading="khhaoTesting" round @click="handleKhhaoTestLogin">测试登录</el-button>
|
||||
<el-button :loading="khhaoQuerying" round @click="handleKhhaoQueryOrders">查询订单</el-button>
|
||||
<el-button :loading="khhaoSyncing" round type="primary" @click="handleKhhaoSyncOrders">同步订单</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-grid">
|
||||
<label class="field-block field-wide">
|
||||
<span>Base URL</span>
|
||||
<input v-model="khhaoForm.baseUrl" class="text-input" placeholder="https://admin.khhao.com" />
|
||||
</label>
|
||||
|
||||
<label class="field-block">
|
||||
<span>账号</span>
|
||||
<input v-model="khhaoForm.username" class="text-input" placeholder="khhao 登录账号" />
|
||||
</label>
|
||||
|
||||
<label class="field-block">
|
||||
<span>密码</span>
|
||||
<input v-model="khhaoForm.password" class="text-input" type="password" placeholder="khhao 登录密码" />
|
||||
</label>
|
||||
|
||||
<label class="field-block">
|
||||
<span>页码</span>
|
||||
<input v-model.number="khhaoForm.page" class="text-input" type="number" min="1" />
|
||||
</label>
|
||||
|
||||
<label class="field-block">
|
||||
<span>每页条数</span>
|
||||
<input v-model.number="khhaoForm.limit" class="text-input" type="number" min="1" max="50" />
|
||||
</label>
|
||||
|
||||
<label class="field-block">
|
||||
<span>验证码重试次数</span>
|
||||
<input v-model.number="khhaoForm.maxCaptchaAttempts" class="text-input" type="number" min="1" max="5" />
|
||||
</label>
|
||||
|
||||
<label class="field-block">
|
||||
<span>自动同步开关</span>
|
||||
<label class="checkbox-line checkbox-box">
|
||||
<input v-model="khhaoForm.autoSyncEnabled" type="checkbox" />
|
||||
<span>启用自动同步</span>
|
||||
</label>
|
||||
</label>
|
||||
|
||||
<label class="field-block">
|
||||
<span>同步间隔(分钟)</span>
|
||||
<input v-model.number="khhaoForm.autoSyncIntervalMinutes" class="text-input" type="number" min="1" max="60" />
|
||||
</label>
|
||||
|
||||
<label class="field-block">
|
||||
<span>同步页数</span>
|
||||
<input v-model.number="khhaoForm.autoSyncPages" class="text-input" type="number" min="1" max="5" />
|
||||
</label>
|
||||
|
||||
<label class="field-block">
|
||||
<span>每页条数</span>
|
||||
<input v-model.number="khhaoForm.autoSyncPageSize" class="text-input" type="number" min="1" max="50" />
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<p v-if="khhaoResultError" class="error-copy">{{ khhaoResultError }}</p>
|
||||
|
||||
<div class="result-grid">
|
||||
<article class="result-card">
|
||||
<span class="result-label">登录结果</span>
|
||||
<strong>{{ khhaoLoginResult ? '成功' : '未执行' }}</strong>
|
||||
<p v-if="khhaoLoginResult">
|
||||
第 {{ khhaoLoginResult.attempt }} 次成功,验证码识别 {{ khhaoLoginResult.captcha.recognizedText || '-' }},
|
||||
{{ formatAdminDateTime(khhaoLoginResult.loggedInAt) }}
|
||||
</p>
|
||||
<p v-else>可先点击“测试登录”验证账号密码与 OCR。</p>
|
||||
</article>
|
||||
|
||||
<article class="result-card">
|
||||
<span class="result-label">查询结果</span>
|
||||
<strong>{{ khhaoQueryResult?.itemCount || 0 }} 条</strong>
|
||||
<p v-if="khhaoQueryResult">当前页 {{ khhaoQueryResult.page }} / 总计 {{ khhaoQueryResult.total }} 条</p>
|
||||
<p v-else>可先点击“查询订单”验证拉单链路。</p>
|
||||
</article>
|
||||
|
||||
<article class="result-card">
|
||||
<span class="result-label">同步结果</span>
|
||||
<strong>{{ khhaoSyncResult?.syncedCount || 0 }} 条</strong>
|
||||
<p v-if="khhaoSyncResult">忽略 {{ khhaoSyncResult.ignoredCount }} 条,拉取 {{ khhaoSyncResult.fetchedCount }} 条</p>
|
||||
<p v-else>完成履约配置后可在这里手动同步。</p>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<div v-if="khhaoAutoSyncState" class="meta-card compact-meta">
|
||||
<div class="meta-line">
|
||||
<span class="meta-label">自动同步状态</span>
|
||||
<span>{{ khhaoAutoSyncState.lastRunStatus || 'idle' }}</span>
|
||||
</div>
|
||||
<div class="meta-line">
|
||||
<span class="meta-label">当前节奏</span>
|
||||
<span>{{ khhaoAutoSyncState.watchMode === 'boosted' ? '提频观察中(3-5 秒)' : '常规同步' }}</span>
|
||||
</div>
|
||||
<div class="meta-line">
|
||||
<span class="meta-label">同步基线</span>
|
||||
<span>{{ formatAdminDateTime(khhaoAutoSyncState.syncFromCreatedAt) }}</span>
|
||||
</div>
|
||||
<div class="meta-line">
|
||||
<span class="meta-label">最近开始</span>
|
||||
<span>{{ formatAdminDateTime(khhaoAutoSyncState.lastRunStartedAt) }}</span>
|
||||
</div>
|
||||
<div class="meta-line">
|
||||
<span class="meta-label">最近完成</span>
|
||||
<span>{{ formatAdminDateTime(khhaoAutoSyncState.lastRunFinishedAt) }}</span>
|
||||
</div>
|
||||
<div class="meta-line">
|
||||
<span class="meta-label">最近结果</span>
|
||||
<span>拉取 {{ khhaoAutoSyncState.fetchedCount }} 条,成功 {{ khhaoAutoSyncState.syncedCount }} 条,忽略 {{ khhaoAutoSyncState.ignoredCount }} 条</span>
|
||||
</div>
|
||||
<div v-if="khhaoAutoSyncState.watchOrders.length > 0" class="meta-line">
|
||||
<span class="meta-label">观察订单</span>
|
||||
<span>{{ khhaoAutoSyncState.watchOrders.map((item) => `${item.shopName || item.shopId || '-'} / ${item.platformOrderId}`).join(';') }}</span>
|
||||
</div>
|
||||
<div v-if="khhaoAutoSyncState.lastErrorMessage" class="meta-line">
|
||||
<span class="meta-label">最近错误</span>
|
||||
<span class="error-copy">{{ khhaoAutoSyncState.lastErrorMessage }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section v-if="khhaoQueryResult" class="table-card">
|
||||
<div class="section-title-row">
|
||||
<div>
|
||||
<h3>khhao 查询结果</h3>
|
||||
<p>当前页 {{ khhaoQueryResult.page }},本页 {{ khhaoQueryResult.itemCount }} 条,总数 {{ khhaoQueryResult.total }}。</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>订单</th>
|
||||
<th>店铺</th>
|
||||
<th>商品</th>
|
||||
<th>金额</th>
|
||||
<th>状态</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="item in khhaoQueryResult.items" :key="`${item.platformOrderId}:${item.skuCode}:${item.itemId}`">
|
||||
<td>
|
||||
<div class="cell-stack">
|
||||
<strong>{{ item.platformOrderId }}</strong>
|
||||
<span class="cell-subtle">{{ item.platformLabel || item.platform || '-' }}</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="cell-stack">
|
||||
<strong>{{ item.shopName || item.shopId || '-' }}</strong>
|
||||
<span class="cell-subtle">ID: {{ item.shopId || '-' }}</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="cell-stack">
|
||||
<strong>{{ item.itemTitle || '-' }}</strong>
|
||||
<span class="cell-subtle">SKU: {{ item.skuCode || '-' }}</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>{{ (item.totalAmountFen / 100).toFixed(2) }}</td>
|
||||
<td>{{ item.statusLabel || item.status || '-' }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
|
||||
<section v-if="khhaoSyncResult" class="table-card">
|
||||
<div class="section-title-row">
|
||||
<div>
|
||||
<h3>khhao 同步结果</h3>
|
||||
<p>拉取 {{ khhaoSyncResult.fetchedCount }} 条,成功 {{ khhaoSyncResult.syncedCount }} 条,忽略 {{ khhaoSyncResult.ignoredCount }} 条。</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>订单</th>
|
||||
<th>店铺</th>
|
||||
<th>商品</th>
|
||||
<th>结果</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="item in khhaoSyncResult.results" :key="`${item.platformOrderId}:${item.skuCode}`">
|
||||
<td>{{ item.platformOrderId }}</td>
|
||||
<td>{{ item.shopId || '-' }}</td>
|
||||
<td>{{ item.itemTitle || item.skuCode || '-' }}</td>
|
||||
<td>
|
||||
<span v-if="item.ignored">已忽略:{{ item.ignoreReason || '-' }}</span>
|
||||
<span v-else>已入库,订单 {{ item.orderId || '-' }},任务 {{ item.taskCount }}</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
</template>
|
||||
@@ -1,6 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref } from 'vue'
|
||||
|
||||
import AdminPlatformAgisoSection from '@/components/admin/AdminPlatformAgisoSection.vue'
|
||||
import AdminPlatformKhhaoSection from '@/components/admin/AdminPlatformKhhaoSection.vue'
|
||||
import { useAdminAgisoPlatform } from '@/composables/admin/platform-shops/useAdminAgisoPlatform'
|
||||
import { useAdminCloudtentaclesPlatform } from '@/composables/admin/platform-shops/useAdminCloudtentaclesPlatform'
|
||||
import { useAdminKhhaoPlatform } from '@/composables/admin/platform-shops/useAdminKhhaoPlatform'
|
||||
@@ -290,391 +292,42 @@ onMounted(loadConfigs)
|
||||
</button>
|
||||
</section>
|
||||
|
||||
<template v-if="activePlatform === 'agiso'">
|
||||
<section class="meta-card">
|
||||
<div class="meta-line">
|
||||
<span class="meta-label">配置文件</span>
|
||||
<code>{{ agisoFilePath || '-' }}</code>
|
||||
</div>
|
||||
<div class="meta-line">
|
||||
<span class="meta-label">职责</span>
|
||||
<span>管理 Agiso 默认消息模板、店铺 AccessToken 以及店铺级模板覆盖。该区域不处理 khhao 登录与拉单。</span>
|
||||
</div>
|
||||
</section>
|
||||
<AdminPlatformAgisoSection
|
||||
v-if="activePlatform === 'agiso'"
|
||||
:agiso-file-path="agisoFilePath"
|
||||
:defaults="defaults"
|
||||
:shops="shops"
|
||||
:observed-shops="observedShops"
|
||||
:saving="saving"
|
||||
:is-shop-expanded="isShopExpanded"
|
||||
:toggle-shop="toggleShop"
|
||||
:expand-all-shops="expandAllShops"
|
||||
:collapse-all-shops="collapseAllShops"
|
||||
:describe-shop="describeShop"
|
||||
:remove-shop="removeShop"
|
||||
:import-observed-shop="importObservedShop"
|
||||
:save-agiso-configs="saveAgisoConfigs"
|
||||
/>
|
||||
|
||||
<section class="table-card">
|
||||
<div class="section-title-row">
|
||||
<div>
|
||||
<h3>默认消息模板</h3>
|
||||
<p>Agiso 店铺未单独配置时,统一使用这里的领取消息与自动发货消息模板。</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-grid">
|
||||
<label class="field-block field-wide">
|
||||
<span>默认领取链接消息模板</span>
|
||||
<textarea
|
||||
v-model="defaults.messageTemplate"
|
||||
class="text-area"
|
||||
placeholder="例如:您的订单 {platformOrderId} 已创建领取链接,请尽快打开并完成领取:{claimUrl}"
|
||||
/>
|
||||
</label>
|
||||
|
||||
<label class="field-block field-wide">
|
||||
<span>默认自动发货消息模板</span>
|
||||
<textarea
|
||||
v-model="defaults.autoDeliveryMessageTemplate"
|
||||
class="text-area"
|
||||
placeholder="例如:您的订单 {platformOrderId} 已完成自动发货,请注意查收。"
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="table-card">
|
||||
<div class="section-title-row">
|
||||
<div>
|
||||
<h3>Agiso 店铺列表</h3>
|
||||
<p>每家店铺可单独配置 AccessToken 和模板覆盖。建议先用“近期识别到的店铺”导入,再补 AccessToken。</p>
|
||||
</div>
|
||||
<div class="section-actions">
|
||||
<el-button :disabled="shops.length === 0" text @click="expandAllShops">全部展开</el-button>
|
||||
<el-button :disabled="shops.length === 0" text @click="collapseAllShops">全部折叠</el-button>
|
||||
<el-button :loading="saving" round type="primary" @click="saveAgisoConfigs">保存 Agiso 配置</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="shops.length === 0" class="empty-inline">当前还没有 Agiso 店铺配置,先新增一条。</div>
|
||||
|
||||
<div v-for="shop in shops" :key="shop.id" class="shop-card">
|
||||
<button type="button" class="shop-summary" @click="toggleShop(shop.id)">
|
||||
<div class="shop-summary-main">
|
||||
<strong>{{ shop.shopName || shop.shopId || '未命名店铺' }}</strong>
|
||||
<span class="cell-subtle">ID: {{ shop.shopId || '未填写' }}</span>
|
||||
</div>
|
||||
<div class="shop-summary-side">
|
||||
<span class="shop-status" :class="{ 'is-disabled': !shop.enabled }">
|
||||
{{ shop.enabled ? '已启用' : '已停用' }}
|
||||
</span>
|
||||
<span class="shop-summary-copy">{{ describeShop(shop) }}</span>
|
||||
<span class="shop-toggle">{{ isShopExpanded(shop.id) ? '收起' : '展开' }}</span>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<div v-if="isShopExpanded(shop.id)" class="shop-body">
|
||||
<div class="form-grid">
|
||||
<label class="field-block">
|
||||
<span>店铺 ID</span>
|
||||
<input v-model="shop.shopId" class="text-input" placeholder="例如 693760716" />
|
||||
</label>
|
||||
|
||||
<label class="field-block">
|
||||
<span>店铺名称</span>
|
||||
<input v-model="shop.shopName" class="text-input" placeholder="例如 大锤商行" />
|
||||
</label>
|
||||
|
||||
<label class="field-block field-wide">
|
||||
<span>AccessToken</span>
|
||||
<input v-model="shop.accessToken" class="text-input" placeholder="AldsIdle..." />
|
||||
</label>
|
||||
|
||||
<label class="field-block field-wide">
|
||||
<span>领取链接消息模板</span>
|
||||
<textarea
|
||||
v-model="shop.messageTemplate"
|
||||
class="text-area"
|
||||
placeholder="留空则沿用默认领取链接模板"
|
||||
/>
|
||||
</label>
|
||||
|
||||
<label class="field-block field-wide">
|
||||
<span>自动发货消息模板</span>
|
||||
<textarea
|
||||
v-model="shop.autoDeliveryMessageTemplate"
|
||||
class="text-area"
|
||||
placeholder="留空则沿用默认自动发货模板"
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="shop-actions">
|
||||
<label class="checkbox-line">
|
||||
<input v-model="shop.enabled" type="checkbox" />
|
||||
<span>启用店铺级覆盖</span>
|
||||
</label>
|
||||
<el-button link type="danger" @click="removeShop(shop.id)">删除</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="table-card">
|
||||
<div class="section-title-row">
|
||||
<div>
|
||||
<h3>近期识别到的 Agiso 店铺</h3>
|
||||
<p>从 webhook 自动识别出的店铺,可一键导入到上方店铺列表。</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>店铺</th>
|
||||
<th>Webhook 次数</th>
|
||||
<th>最近出现时间</th>
|
||||
<th>状态</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="item in observedShops" :key="item.shopId">
|
||||
<td>
|
||||
<div class="cell-stack">
|
||||
<strong>{{ item.displayShopName || item.shopId }}</strong>
|
||||
<span class="cell-subtle">ID: {{ item.shopId }}</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>{{ item.webhookEventCount }}</td>
|
||||
<td>{{ formatAdminDateTime(item.latestSeenAt) }}</td>
|
||||
<td>{{ item.configured ? '已配置' : '未配置' }}</td>
|
||||
<td>
|
||||
<el-button v-if="!item.configured" link type="primary" @click="importObservedShop(item)">导入到店铺</el-button>
|
||||
<span v-else class="cell-subtle">已存在</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="observedShops.length === 0">
|
||||
<td colspan="5" class="empty-inline">最近还没有识别到可用店铺。</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<template v-else-if="activePlatform === 'khhao'">
|
||||
<section class="meta-card">
|
||||
<div class="meta-line">
|
||||
<span class="meta-label">配置文件</span>
|
||||
<code>{{ khhaoFilePath || '-' }}</code>
|
||||
</div>
|
||||
<div class="meta-line">
|
||||
<span class="meta-label">状态文件</span>
|
||||
<code>{{ khhaoStateFilePath || '-' }}</code>
|
||||
</div>
|
||||
<div class="meta-line">
|
||||
<span class="meta-label">职责</span>
|
||||
<span>管理 khhao 的来源地址、登录账号、密码与验证码重试次数,并执行测试登录、查询订单与手动同步。</span>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="table-card">
|
||||
<div class="section-title-row">
|
||||
<div>
|
||||
<h3>khhao 来源配置</h3>
|
||||
<p>先保存 khhao 凭据,再按需执行登录测试、订单查询或同步。履约规则请前往“履约配置”页处理。</p>
|
||||
</div>
|
||||
<div class="section-actions">
|
||||
<el-button :loading="khhaoSaving" round @click="handleKhhaoSaveSource">保存来源配置</el-button>
|
||||
<el-button :loading="khhaoTesting" round @click="handleKhhaoTestLogin">测试登录</el-button>
|
||||
<el-button :loading="khhaoQuerying" round @click="handleKhhaoQueryOrders">查询订单</el-button>
|
||||
<el-button :loading="khhaoSyncing" round type="primary" @click="handleKhhaoSyncOrders">同步订单</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-grid">
|
||||
<label class="field-block field-wide">
|
||||
<span>Base URL</span>
|
||||
<input v-model="khhaoForm.baseUrl" class="text-input" placeholder="https://admin.khhao.com" />
|
||||
</label>
|
||||
|
||||
<label class="field-block">
|
||||
<span>账号</span>
|
||||
<input v-model="khhaoForm.username" class="text-input" placeholder="khhao 登录账号" />
|
||||
</label>
|
||||
|
||||
<label class="field-block">
|
||||
<span>密码</span>
|
||||
<input v-model="khhaoForm.password" class="text-input" type="password" placeholder="khhao 登录密码" />
|
||||
</label>
|
||||
|
||||
<label class="field-block">
|
||||
<span>页码</span>
|
||||
<input v-model.number="khhaoForm.page" class="text-input" type="number" min="1" />
|
||||
</label>
|
||||
|
||||
<label class="field-block">
|
||||
<span>每页条数</span>
|
||||
<input v-model.number="khhaoForm.limit" class="text-input" type="number" min="1" max="50" />
|
||||
</label>
|
||||
|
||||
<label class="field-block">
|
||||
<span>验证码重试次数</span>
|
||||
<input v-model.number="khhaoForm.maxCaptchaAttempts" class="text-input" type="number" min="1" max="5" />
|
||||
</label>
|
||||
|
||||
<label class="field-block">
|
||||
<span>自动同步开关</span>
|
||||
<label class="checkbox-line checkbox-box">
|
||||
<input v-model="khhaoForm.autoSyncEnabled" type="checkbox" />
|
||||
<span>启用自动同步</span>
|
||||
</label>
|
||||
</label>
|
||||
|
||||
<label class="field-block">
|
||||
<span>同步间隔(分钟)</span>
|
||||
<input v-model.number="khhaoForm.autoSyncIntervalMinutes" class="text-input" type="number" min="1" max="60" />
|
||||
</label>
|
||||
|
||||
<label class="field-block">
|
||||
<span>同步页数</span>
|
||||
<input v-model.number="khhaoForm.autoSyncPages" class="text-input" type="number" min="1" max="5" />
|
||||
</label>
|
||||
|
||||
<label class="field-block">
|
||||
<span>每页条数</span>
|
||||
<input v-model.number="khhaoForm.autoSyncPageSize" class="text-input" type="number" min="1" max="50" />
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<p v-if="khhaoResultError" class="error-copy">{{ khhaoResultError }}</p>
|
||||
|
||||
<div class="result-grid">
|
||||
<article class="result-card">
|
||||
<span class="result-label">登录结果</span>
|
||||
<strong>{{ khhaoLoginResult ? '成功' : '未执行' }}</strong>
|
||||
<p v-if="khhaoLoginResult">
|
||||
第 {{ khhaoLoginResult.attempt }} 次成功,验证码识别 {{ khhaoLoginResult.captcha.recognizedText || '-' }},
|
||||
{{ formatAdminDateTime(khhaoLoginResult.loggedInAt) }}
|
||||
</p>
|
||||
<p v-else>可先点击“测试登录”验证账号密码与 OCR。</p>
|
||||
</article>
|
||||
|
||||
<article class="result-card">
|
||||
<span class="result-label">查询结果</span>
|
||||
<strong>{{ khhaoQueryResult?.itemCount || 0 }} 条</strong>
|
||||
<p v-if="khhaoQueryResult">当前页 {{ khhaoQueryResult.page }} / 总计 {{ khhaoQueryResult.total }} 条</p>
|
||||
<p v-else>可先点击“查询订单”验证拉单链路。</p>
|
||||
</article>
|
||||
|
||||
<article class="result-card">
|
||||
<span class="result-label">同步结果</span>
|
||||
<strong>{{ khhaoSyncResult?.syncedCount || 0 }} 条</strong>
|
||||
<p v-if="khhaoSyncResult">忽略 {{ khhaoSyncResult.ignoredCount }} 条,拉取 {{ khhaoSyncResult.fetchedCount }} 条</p>
|
||||
<p v-else>完成履约配置后可在这里手动同步。</p>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<div v-if="khhaoAutoSyncState" class="meta-card compact-meta">
|
||||
<div class="meta-line">
|
||||
<span class="meta-label">自动同步状态</span>
|
||||
<span>{{ khhaoAutoSyncState.lastRunStatus || 'idle' }}</span>
|
||||
</div>
|
||||
<div class="meta-line">
|
||||
<span class="meta-label">当前节奏</span>
|
||||
<span>{{ khhaoAutoSyncState.watchMode === 'boosted' ? '提频观察中(3-5 秒)' : '常规同步' }}</span>
|
||||
</div>
|
||||
<div class="meta-line">
|
||||
<span class="meta-label">同步基线</span>
|
||||
<span>{{ formatAdminDateTime(khhaoAutoSyncState.syncFromCreatedAt) }}</span>
|
||||
</div>
|
||||
<div class="meta-line">
|
||||
<span class="meta-label">最近开始</span>
|
||||
<span>{{ formatAdminDateTime(khhaoAutoSyncState.lastRunStartedAt) }}</span>
|
||||
</div>
|
||||
<div class="meta-line">
|
||||
<span class="meta-label">最近完成</span>
|
||||
<span>{{ formatAdminDateTime(khhaoAutoSyncState.lastRunFinishedAt) }}</span>
|
||||
</div>
|
||||
<div class="meta-line">
|
||||
<span class="meta-label">最近结果</span>
|
||||
<span>拉取 {{ khhaoAutoSyncState.fetchedCount }} 条,成功 {{ khhaoAutoSyncState.syncedCount }} 条,忽略 {{ khhaoAutoSyncState.ignoredCount }} 条</span>
|
||||
</div>
|
||||
<div v-if="khhaoAutoSyncState.watchOrders.length > 0" class="meta-line">
|
||||
<span class="meta-label">观察订单</span>
|
||||
<span>{{ khhaoAutoSyncState.watchOrders.map((item) => `${item.shopName || item.shopId || '-'} / ${item.platformOrderId}`).join(';') }}</span>
|
||||
</div>
|
||||
<div v-if="khhaoAutoSyncState.lastErrorMessage" class="meta-line">
|
||||
<span class="meta-label">最近错误</span>
|
||||
<span class="error-copy">{{ khhaoAutoSyncState.lastErrorMessage }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section v-if="khhaoQueryResult" class="table-card">
|
||||
<div class="section-title-row">
|
||||
<div>
|
||||
<h3>khhao 查询结果</h3>
|
||||
<p>当前页 {{ khhaoQueryResult.page }},本页 {{ khhaoQueryResult.itemCount }} 条,总数 {{ khhaoQueryResult.total }}。</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>订单</th>
|
||||
<th>店铺</th>
|
||||
<th>商品</th>
|
||||
<th>金额</th>
|
||||
<th>状态</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="item in khhaoQueryResult.items" :key="`${item.platformOrderId}:${item.skuCode}:${item.itemId}`">
|
||||
<td>
|
||||
<div class="cell-stack">
|
||||
<strong>{{ item.platformOrderId }}</strong>
|
||||
<span class="cell-subtle">{{ item.platformLabel || item.platform || '-' }}</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="cell-stack">
|
||||
<strong>{{ item.shopName || item.shopId || '-' }}</strong>
|
||||
<span class="cell-subtle">ID: {{ item.shopId || '-' }}</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="cell-stack">
|
||||
<strong>{{ item.itemTitle || '-' }}</strong>
|
||||
<span class="cell-subtle">SKU: {{ item.skuCode || '-' }}</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>{{ (item.totalAmountFen / 100).toFixed(2) }}</td>
|
||||
<td>{{ item.statusLabel || item.status || '-' }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
|
||||
<section v-if="khhaoSyncResult" class="table-card">
|
||||
<div class="section-title-row">
|
||||
<div>
|
||||
<h3>khhao 同步结果</h3>
|
||||
<p>拉取 {{ khhaoSyncResult.fetchedCount }} 条,成功 {{ khhaoSyncResult.syncedCount }} 条,忽略 {{ khhaoSyncResult.ignoredCount }} 条。</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>订单</th>
|
||||
<th>店铺</th>
|
||||
<th>商品</th>
|
||||
<th>结果</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="item in khhaoSyncResult.results" :key="`${item.platformOrderId}:${item.skuCode}`">
|
||||
<td>{{ item.platformOrderId }}</td>
|
||||
<td>{{ item.shopId || '-' }}</td>
|
||||
<td>{{ item.itemTitle || item.skuCode || '-' }}</td>
|
||||
<td>
|
||||
<span v-if="item.ignored">已忽略:{{ item.ignoreReason || '-' }}</span>
|
||||
<span v-else>已入库,订单 {{ item.orderId || '-' }},任务 {{ item.taskCount }}</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
</template>
|
||||
<AdminPlatformKhhaoSection
|
||||
v-else-if="activePlatform === 'khhao'"
|
||||
:khhao-file-path="khhaoFilePath"
|
||||
:khhao-state-file-path="khhaoStateFilePath"
|
||||
:khhao-form="khhaoForm"
|
||||
:khhao-saving="khhaoSaving"
|
||||
:khhao-testing="khhaoTesting"
|
||||
:khhao-querying="khhaoQuerying"
|
||||
:khhao-syncing="khhaoSyncing"
|
||||
:khhao-result-error="khhaoResultError"
|
||||
:khhao-login-result="khhaoLoginResult"
|
||||
:khhao-query-result="khhaoQueryResult"
|
||||
:khhao-sync-result="khhaoSyncResult"
|
||||
:khhao-auto-sync-state="khhaoAutoSyncState"
|
||||
:handle-khhao-save-source="handleKhhaoSaveSource"
|
||||
:handle-khhao-test-login="handleKhhaoTestLogin"
|
||||
:handle-khhao-query-orders="handleKhhaoQueryOrders"
|
||||
:handle-khhao-sync-orders="handleKhhaoSyncOrders"
|
||||
/>
|
||||
|
||||
<template v-else-if="activePlatform === 'kuaishouEticket'">
|
||||
<section class="meta-card">
|
||||
@@ -1146,4 +799,3 @@ onMounted(loadConfigs)
|
||||
</template>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user