fix(subscribe): keep card execution states compact and clear stale hints

This commit is contained in:
jxxghp
2026-09-08 08:59:18 +08:00
parent b2b0810908
commit eac0329360
6 changed files with 76 additions and 43 deletions
+7 -2
View File
@@ -146,7 +146,10 @@ const executionStateDisplay = computed(() => {
return {
...display,
label: t(`subscribe.execution.state.${displayState}`),
error: execution.error,
// 兼容旧后端残留的等待原因:恢复搜索后不再把旧提示带入 tooltip。
error: ['matching', 'searching', 'preparing', 'submitting', 'running'].includes(displayState)
? undefined
: execution.error,
}
})
@@ -1106,8 +1109,10 @@ function handleCardClick() {
text-overflow: ellipsis;
}
/* 状态与集数共用单行槽位,小屏空间不足时允许省略而不挤压操作按钮。 */
.subscribe-card-mobile-progress-text {
flex-shrink: 0;
min-inline-size: 0;
flex-shrink: 1;
}
.subscribe-card-mobile-menu {
@@ -1,6 +1,8 @@
import { formatDateDifference } from '@/@core/utils/formatters'
import type { Subscribe } from '@/api/types'
import SubscribeCard from '@/components/cards/SubscribeCard.vue'
import zhCN from '@/locales/zh-CN'
import zhTW from '@/locales/zh-TW'
import { fireEvent, screen, waitFor } from '@testing-library/vue'
import { createSubscribe } from '@tests/support/factories/subscribe'
import {
@@ -12,6 +14,7 @@ import {
import { server } from '@tests/support/msw/server'
import { renderWithProviders } from '@tests/support/render'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { createI18n } from 'vue-i18n'
const mocks = vi.hoisted(() => ({
confirm: vi.fn(),
@@ -350,7 +353,7 @@ describe('SubscribeCard display and progress', () => {
expect(container.querySelector('.subscribe-card')).not.toHaveClass('subscribe-card-paused')
})
it.each([480, 1024])('shows governed execution state and safe failure detail at %ipx', async width => {
it.each([375, 480, 1024])('shows governed execution state and safe failure detail at %ipx', async width => {
setViewport(width)
await renderCard({
execution_status: {
@@ -364,13 +367,29 @@ describe('SubscribeCard display and progress', () => {
},
})
expect(screen.getByText('站点暂时忙,稍后继续')).toBeInTheDocument()
expect(screen.getByText('等待站点')).toBeInTheDocument()
const status = screen.getByTitle('站点暂时忙,系统会自动继续搜索')
expect(status).toBeInTheDocument()
await fireEvent.mouseEnter(status)
await waitFor(() => expect(screen.getByRole('tooltip')).toHaveTextContent('站点暂时忙,系统会自动继续搜索'))
})
it.each([375, 1024])('clears old waiting detail when search resumes at %ipx', async width => {
setViewport(width)
await renderCard({
execution_status: {
can_cancel: true,
error: '站点暂时忙,系统会自动继续搜索',
phase: 'searching',
state: 'running',
updated_at: new Date().toISOString(),
},
})
expect(screen.getByText('搜索中')).toBeInTheDocument()
expect(screen.queryByTitle('站点暂时忙,系统会自动继续搜索')).not.toBeInTheDocument()
})
it.each([480, 1024])('shows a skipped execution as a non-error terminal state at %ipx', async width => {
setViewport(width)
await renderCard({
@@ -382,7 +401,7 @@ describe('SubscribeCard display and progress', () => {
},
})
expect(screen.getByText('这次未搜索')).toBeInTheDocument()
expect(screen.getByText('已跳过')).toBeInTheDocument()
if (width < 600) {
expect(document.querySelector('[data-subscribe-state-icon="mdi-skip-next-circle-outline"]')).toBeInTheDocument()
}
@@ -461,7 +480,7 @@ describe('SubscribeCard display and progress', () => {
type: '电视剧',
})
expect(screen.queryByText('已安排,稍后开始')).not.toBeInTheDocument()
expect(screen.queryByText('待搜索')).not.toBeInTheDocument()
expect(screen.getByText('6 / 10')).toBeInTheDocument()
if (width >= 600) expect(screen.getByText(formatDateDifference(media.last_update))).toBeInTheDocument()
})
@@ -710,3 +729,12 @@ describe('SubscribeCard item operations', () => {
},
)
})
describe('SubscribeCard compact execution vocabulary', () => {
it.each([zhCN, zhTW])('keeps every Chinese state within four characters', locale => {
const i18n = createI18n({ legacy: false, locale: 'test', messages: { test: locale } })
for (const state of Object.keys(locale.subscribe.execution.state)) {
expect([...i18n.global.t(`subscribe.execution.state.${state}`)].length).toBeLessThanOrEqual(4)
}
})
})
+10 -10
View File
@@ -1421,17 +1421,17 @@ export default {
cancelRequested: 'Stopping this search',
cancelFailed: 'Unable to stop the search right now. Try again shortly.',
state: {
queued: 'Starting soon',
scheduled: 'Scheduled to start shortly',
running: 'In progress',
matching: 'Preparing the search',
queued: 'Queued',
scheduled: 'Scheduled',
running: 'Processing',
matching: 'Preparing',
searching: 'Searching',
waiting_subscription: 'Continuing after the current task',
waiting_site_budget: 'Site is busy; continuing shortly',
preparing: 'Resource found; preparing download',
submitting: 'Submitting download',
skipped: 'Not searched this time',
failed: 'Search did not finish',
waiting_subscription: 'Task pending',
waiting_site_budget: 'Site pending',
preparing: 'Ready',
submitting: 'Submitting',
skipped: 'Skipped',
failed: 'Search failed',
cancelling: 'Stopping',
cancelled: 'Stopped',
completed: 'Search complete',
+12 -12
View File
@@ -1401,18 +1401,18 @@ export default {
cancelRequested: '正在停止这次搜索',
cancelFailed: '暂时无法停止,请稍后重试',
state: {
queued: '即将开始',
scheduled: '已安排,稍后开始',
running: '正在处理',
matching: '正在准备搜索',
searching: '正在搜索',
waiting_subscription: '当前任务结束后继续',
waiting_site_budget: '站点暂时忙,稍后继续',
preparing: '找到资源,准备下载',
submitting: '正在提交下载',
skipped: '这次未搜索',
failed: '搜索没有完成',
cancelling: '正在停止',
queued: '排队中',
scheduled: '待搜索',
running: '处理',
matching: '准备中',
searching: '搜索',
waiting_subscription: '等待任务',
waiting_site_budget: '等待站点',
preparing: '下载',
submitting: '提交中',
skipped: '已跳过',
failed: '搜索失败',
cancelling: '停止',
cancelled: '已停止',
completed: '搜索完成',
},
+12 -12
View File
@@ -1399,18 +1399,18 @@ export default {
cancelRequested: '正在停止這次搜索',
cancelFailed: '暫時無法停止,請稍後重試',
state: {
queued: '即將開始',
scheduled: '已安排,稍後開始',
running: '正在處理',
matching: '正在準備搜索',
searching: '正在搜索',
waiting_subscription: '目前任務結束後繼續',
waiting_site_budget: '站點暫時忙,稍後繼續',
preparing: '找到資源,準備下載',
submitting: '正在提交下載',
skipped: '這次未搜索',
failed: '搜索沒有完成',
cancelling: '正在停止',
queued: '排隊中',
scheduled: '待搜尋',
running: '處理',
matching: '準備中',
searching: '搜尋中',
waiting_subscription: '等待任務',
waiting_site_budget: '等待站點',
preparing: '下載',
submitting: '提交中',
skipped: '已略過',
failed: '搜尋失敗',
cancelling: '停止',
cancelled: '已停止',
completed: '搜索完成',
},
@@ -385,7 +385,7 @@ describe('SubscribeListView loading and filtering', () => {
server.use(cancelSubscriptionExecutionBatchHandler(batch.batch_id, { success: true }, 200, cancelRequested))
await renderList({ batchResponse: [batch], listResponse: [movie(1, 'Own movie')] })
expect(await screen.findByText('正在搜索')).toBeInTheDocument()
expect(await screen.findByText('搜索')).toBeInTheDocument()
expect(screen.getByText('1/3')).toBeInTheDocument()
await fireEvent.click(screen.getByTitle('停止这次搜索'))
@@ -845,7 +845,7 @@ describe('SubscribeListView loading and filtering', () => {
})
expect(await screen.findByText('跳过批次订阅')).toBeInTheDocument()
expect(await screen.findByText('这次未搜索')).toBeInTheDocument()
expect(await screen.findByText('已跳过')).toBeInTheDocument()
expect(await screen.findByText('2/3')).toBeInTheDocument()
})
@@ -856,7 +856,7 @@ describe('SubscribeListView loading and filtering', () => {
})
expect(await screen.findByText('后台自动搜索订阅')).toBeInTheDocument()
expect(screen.queryByText('已安排,稍后开始')).not.toBeInTheDocument()
expect(screen.queryByText('待搜索')).not.toBeInTheDocument()
expect(document.querySelector('.subscribe-execution-banner')).not.toBeInTheDocument()
})