@@ -35,6 +35,30 @@
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="aliprice-card">
|
||||
<div class="section-title">货源账号</div>
|
||||
<label class="aliprice-field">
|
||||
<span>账号</span>
|
||||
<input
|
||||
v-model="alipriceUsername"
|
||||
type="text"
|
||||
autocomplete="username"
|
||||
placeholder="请输入 Aliprice 账号"
|
||||
required
|
||||
/>
|
||||
</label>
|
||||
<label class="aliprice-field">
|
||||
<span>密码</span>
|
||||
<input
|
||||
v-model="alipricePassword"
|
||||
type="password"
|
||||
autocomplete="current-password"
|
||||
placeholder="请输入 Aliprice 密码"
|
||||
required
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="run-row">
|
||||
<button type="button" class="btn-run" :disabled="parsing || !uploadedFiles.length" @click="parseFiles">
|
||||
{{ parsing ? '解析中...' : '解析并创建任务' }}
|
||||
@@ -182,7 +206,7 @@ import {
|
||||
uploadTempFileToJava,
|
||||
} from '@/shared/api/java-modules'
|
||||
import { expandBrandFolderRecursive, type BrandExpandFolderItem } from '@/shared/api/brand'
|
||||
import { getPywebviewApi } from '@/shared/bridges/pywebview'
|
||||
import { getPywebviewApi, type ProxyMode } from '@/shared/bridges/pywebview'
|
||||
import { getTaskPollIntervalMs } from '@/shared/task-progress-config'
|
||||
import { getStoredApiSecret } from '@/shared/utils/api-secret-store'
|
||||
import { createCategorizedTimers } from '@/shared/utils/categorized-timers'
|
||||
@@ -197,6 +221,8 @@ const parsing = ref(false)
|
||||
const pushing = ref(false)
|
||||
const imgSwitch = ref(false)
|
||||
const categorySwitch = ref(false)
|
||||
const alipriceUsername = ref('')
|
||||
const alipricePassword = ref('')
|
||||
const queuePayloadText = ref('')
|
||||
const pollingTaskIds = ref<number[]>([])
|
||||
const pendingFileTaskIds = ref<number[]>([])
|
||||
@@ -206,6 +232,7 @@ const HISTORY_CACHE_TTL_MS = 3000
|
||||
let historyInFlight: Promise<void> | null = null
|
||||
let lastHistoryLoadedAt = 0
|
||||
let disposed = false
|
||||
let pywebviewReadyHandler: (() => void) | null = null
|
||||
const timers = createCategorizedTimers('similar-asin')
|
||||
|
||||
const dashboard = ref<SimilarAsinDashboardVo>({
|
||||
@@ -280,6 +307,16 @@ function effectiveCozeApiKey() {
|
||||
return getStoredApiSecret('similar-asin').trim()
|
||||
}
|
||||
|
||||
function getRequiredAlipriceCredentials() {
|
||||
const username = alipriceUsername.value.trim()
|
||||
const password = alipricePassword.value
|
||||
if (!username || !password.trim()) {
|
||||
ElMessage.warning('请填写 Aliprice 账号和密码')
|
||||
return null
|
||||
}
|
||||
return { username, password }
|
||||
}
|
||||
|
||||
function formatDateTime(value?: string) {
|
||||
if (!value) return '-'
|
||||
const date = new Date(value)
|
||||
@@ -306,6 +343,7 @@ function payloadForDisplay<T extends { data?: Record<string, unknown> }>(payload
|
||||
? {
|
||||
...payload.data,
|
||||
api_key: maskSecret(String(payload.data.api_key || '')),
|
||||
aliprice_pwd: maskSecret(String(payload.data.aliprice_pwd || '')),
|
||||
}
|
||||
: payload.data,
|
||||
}
|
||||
@@ -422,6 +460,7 @@ async function parseFiles() {
|
||||
ElMessage.warning('请先在左上角设置中填写货源查询密钥')
|
||||
return
|
||||
}
|
||||
if (!getRequiredAlipriceCredentials()) return
|
||||
parsing.value = true
|
||||
try {
|
||||
const files: UploadedFileRef[] = uploadedFiles.value.map((f) => ({
|
||||
@@ -458,8 +497,37 @@ async function pushToPythonQueue() {
|
||||
ElMessage.warning('请先在左上角设置中填写货源查询密钥')
|
||||
return
|
||||
}
|
||||
const alipriceCredentials = getRequiredAlipriceCredentials()
|
||||
if (!alipriceCredentials) return
|
||||
const alipriceUsename = alipriceCredentials.username
|
||||
const alipricePwd = alipriceCredentials.password
|
||||
pushing.value = true
|
||||
try {
|
||||
let proxyData: { proxy_url: string; proxy_mode: ProxyMode } | undefined
|
||||
if (api.read_config) {
|
||||
try {
|
||||
const config = await api.read_config()
|
||||
const proxyUrl = typeof config?.proxy_url === 'string' ? config.proxy_url.trim() : ''
|
||||
if (proxyUrl) {
|
||||
proxyData = {
|
||||
proxy_url: proxyUrl,
|
||||
proxy_mode: Number(config?.proxy_mode) === 2 ? 2 : 1,
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// Keep the existing queue behavior when proxy configuration is unavailable.
|
||||
}
|
||||
}
|
||||
if (api.save_config) {
|
||||
try {
|
||||
await api.save_config({
|
||||
aliprice_usename: alipriceUsename,
|
||||
aliprice_pwd: alipricePwd,
|
||||
})
|
||||
} catch {
|
||||
ElMessage.warning('Aliprice 账号配置保存失败,本次任务仍会继续')
|
||||
}
|
||||
}
|
||||
const payload = {
|
||||
type: 'similar-asin-run',
|
||||
ts: Date.now(),
|
||||
@@ -471,6 +539,9 @@ async function pushToPythonQueue() {
|
||||
totalRows: currentParseResult.totalRows || 0,
|
||||
acceptedRows: currentParseResult.acceptedRows || 0,
|
||||
groupCount: currentParseResult.groupCount || 0,
|
||||
aliprice_usename: alipriceUsename,
|
||||
aliprice_pwd: alipricePwd,
|
||||
...proxyData,
|
||||
},
|
||||
}
|
||||
queuePayloadText.value = JSON.stringify(payloadForDisplay(payload), null, 2)
|
||||
@@ -491,6 +562,19 @@ async function pushToPythonQueue() {
|
||||
}
|
||||
}
|
||||
|
||||
async function loadAlipriceConfig() {
|
||||
const api = getPywebviewApi()
|
||||
if (!api?.read_config) return
|
||||
try {
|
||||
const config = await api.read_config()
|
||||
if (disposed) return
|
||||
alipriceUsername.value = typeof config?.aliprice_usename === 'string' ? config.aliprice_usename : ''
|
||||
alipricePassword.value = typeof config?.aliprice_pwd === 'string' ? config.aliprice_pwd : ''
|
||||
} catch {
|
||||
ElMessage.warning('Aliprice 账号配置读取失败')
|
||||
}
|
||||
}
|
||||
|
||||
function clearParsedTask() {
|
||||
parseResult.value = null
|
||||
queuePayloadText.value = ''
|
||||
@@ -574,9 +658,14 @@ function scheduleNextPoll(immediate = false) {
|
||||
pollTimer.value = null
|
||||
if (disposed) return
|
||||
if (!pollingTaskIds.value.length && !pendingFileTaskIds.value.length) return
|
||||
await refreshTaskProgress()
|
||||
if (!disposed && pollTimer.value == null && (pollingTaskIds.value.length || pendingFileTaskIds.value.length)) {
|
||||
pollTimer.value = timers.setTimeout('task-poll', run, getTaskPollIntervalMs())
|
||||
try {
|
||||
await refreshTaskProgress()
|
||||
} catch {
|
||||
// A transient request failure must not stop progress polling permanently.
|
||||
} finally {
|
||||
if (!disposed && pollTimer.value == null && (pollingTaskIds.value.length || pendingFileTaskIds.value.length)) {
|
||||
pollTimer.value = timers.setTimeout('task-poll', run, getTaskPollIntervalMs())
|
||||
}
|
||||
}
|
||||
}
|
||||
if (immediate) void run()
|
||||
@@ -959,9 +1048,16 @@ async function deleteTaskRecord(item: SimilarAsinHistoryItem) {
|
||||
|
||||
onMounted(async () => {
|
||||
loadPollingIds()
|
||||
if (typeof window !== 'undefined') {
|
||||
pywebviewReadyHandler = () => {
|
||||
void loadAlipriceConfig()
|
||||
}
|
||||
window.addEventListener('pywebviewready', pywebviewReadyHandler)
|
||||
}
|
||||
await Promise.all([
|
||||
loadDashboard().catch(() => undefined),
|
||||
loadHistory().catch(() => undefined),
|
||||
loadAlipriceConfig(),
|
||||
])
|
||||
seedPendingFileTasksFromHistory()
|
||||
seedRunningTasksFromHistory()
|
||||
@@ -970,6 +1066,10 @@ onMounted(async () => {
|
||||
|
||||
onUnmounted(() => {
|
||||
disposed = true
|
||||
if (typeof window !== 'undefined' && pywebviewReadyHandler) {
|
||||
window.removeEventListener('pywebviewready', pywebviewReadyHandler)
|
||||
pywebviewReadyHandler = null
|
||||
}
|
||||
stopPolling()
|
||||
timers.clearScope()
|
||||
})
|
||||
@@ -1026,6 +1126,44 @@ onUnmounted(() => {
|
||||
background: linear-gradient(135deg, #222a25, #202020);
|
||||
}
|
||||
|
||||
.aliprice-card {
|
||||
margin-bottom: 18px;
|
||||
padding: 14px 16px;
|
||||
border: 1px solid #343434;
|
||||
border-radius: 8px;
|
||||
background: #232323;
|
||||
}
|
||||
|
||||
.aliprice-field {
|
||||
display: grid;
|
||||
grid-template-columns: 44px minmax(0, 1fr);
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
color: #bbb;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.aliprice-field + .aliprice-field {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.aliprice-field input {
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
height: 34px;
|
||||
padding: 0 10px;
|
||||
border: 1px solid #3b3b3b;
|
||||
border-radius: 6px;
|
||||
outline: none;
|
||||
background: #1b1b1b;
|
||||
color: #ddd;
|
||||
font: inherit;
|
||||
}
|
||||
|
||||
.aliprice-field input:focus {
|
||||
border-color: #4f91c7;
|
||||
}
|
||||
|
||||
.switch-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
Reference in New Issue
Block a user