镜像自地址
https://github.com/lhly/cloud-drive-renamer.git
已同步 2026-10-21 06:01:51 +08:00
feat: surface conflict and error info
这个提交包含在:
父节点
7fa74c754e
当前提交
ec172a8762
@ -74,6 +74,15 @@ export class VirtualPreviewList extends LitElement {
|
||||
* Render a single preview item
|
||||
* @private
|
||||
*/
|
||||
private resolveErrorText(error?: string): string | undefined {
|
||||
if (!error) return undefined;
|
||||
|
||||
const translationKey = `error_${error}`;
|
||||
const translated = I18nService.t(translationKey);
|
||||
|
||||
return translated === translationKey ? error : translated;
|
||||
}
|
||||
|
||||
private renderPreviewItem(item: PreviewItem): any {
|
||||
const statusClass = item.conflict
|
||||
? 'conflict'
|
||||
@ -95,19 +104,28 @@ export class VirtualPreviewList extends LitElement {
|
||||
? html`<span class="status-badge pending">${I18nService.t('status_pending')}</span>`
|
||||
: '';
|
||||
|
||||
const errorText = this.resolveErrorText(item.error);
|
||||
const errorTitle = item.error ?? errorText ?? '';
|
||||
const conflictHint = item.conflict
|
||||
? html`<span class="conflict-hint">${I18nService.t('error_api_conflict')}</span>`
|
||||
: '';
|
||||
|
||||
return html`
|
||||
<div class="preview-item ${statusClass}">
|
||||
<div class="preview-content">
|
||||
<div class="old-name" title=${item.file.name}>
|
||||
${item.file.name}
|
||||
</div>
|
||||
<div class="new-name ${statusClass}" title=${item.newName}>
|
||||
${item.newName}
|
||||
<div class="old-name-row">
|
||||
<div class="old-name" title=${item.file.name}>
|
||||
${item.file.name}
|
||||
</div>
|
||||
${statusBadge}
|
||||
</div>
|
||||
<div class="new-name ${statusClass}" title=${item.newName}>
|
||||
<span class="new-name-text">${item.newName}</span>
|
||||
${conflictHint}
|
||||
</div>
|
||||
${
|
||||
item.error
|
||||
? html`<div class="error-message" title=${item.error}>${item.error}</div>`
|
||||
errorText
|
||||
? html`<div class="error-message" title=${errorTitle}>${errorText}</div>`
|
||||
: ''
|
||||
}
|
||||
</div>
|
||||
@ -232,10 +250,16 @@ export class VirtualPreviewList extends LitElement {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.old-name-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.old-name {
|
||||
font-size: 12px;
|
||||
color: var(--cdr-text-secondary, #595959);
|
||||
margin-bottom: 4px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
@ -253,6 +277,14 @@ export class VirtualPreviewList extends LitElement {
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.new-name-text {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.new-name.conflict {
|
||||
color: var(--cdr-warning-text, #fa8c16);
|
||||
}
|
||||
@ -269,6 +301,12 @@ export class VirtualPreviewList extends LitElement {
|
||||
color: var(--cdr-text-secondary, #595959);
|
||||
}
|
||||
|
||||
.conflict-hint {
|
||||
font-size: 12px;
|
||||
color: var(--cdr-warning-text, #fa8c16);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.error-message {
|
||||
margin-top: 4px;
|
||||
font-size: 12px;
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { VirtualPreviewList } from '../../src/content/components/virtual-preview-list';
|
||||
import { I18nService } from '../../src/utils/i18n';
|
||||
|
||||
describe('VirtualPreviewList pending preview mapping', () => {
|
||||
it('renders original name and new name for a pending preview item', async () => {
|
||||
@ -29,4 +30,92 @@ describe('VirtualPreviewList pending preview mapping', () => {
|
||||
|
||||
element.remove();
|
||||
});
|
||||
|
||||
it('renders conflict items with status badge and helper text', async () => {
|
||||
const element = new VirtualPreviewList();
|
||||
element.items = [
|
||||
{
|
||||
file: {
|
||||
id: 'file-conflict',
|
||||
name: 'old-conflict.mkv',
|
||||
ext: 'mkv',
|
||||
size: 1,
|
||||
mtime: Date.now(),
|
||||
isDir: false,
|
||||
},
|
||||
newName: 'new-conflict.mkv',
|
||||
conflict: true,
|
||||
done: false,
|
||||
},
|
||||
];
|
||||
|
||||
document.body.appendChild(element);
|
||||
await element.updateComplete;
|
||||
|
||||
const textContent = element.shadowRoot?.textContent ?? '';
|
||||
expect(textContent).toContain('old-conflict.mkv');
|
||||
expect(textContent).toContain('new-conflict.mkv');
|
||||
expect(textContent).toContain(I18nService.t('preview_badge_conflict'));
|
||||
expect(textContent).toContain(I18nService.t('error_api_conflict'));
|
||||
|
||||
const badge = element.shadowRoot?.querySelector('.old-name-row .status-badge.conflict');
|
||||
expect(badge).toBeTruthy();
|
||||
|
||||
element.remove();
|
||||
});
|
||||
|
||||
it('shows localized extract_episode_not_found message instead of raw error key', async () => {
|
||||
const element = new VirtualPreviewList();
|
||||
element.items = [
|
||||
{
|
||||
file: {
|
||||
id: 'file-error',
|
||||
name: 'old-error.mkv',
|
||||
ext: 'mkv',
|
||||
size: 1,
|
||||
mtime: Date.now(),
|
||||
isDir: false,
|
||||
},
|
||||
newName: 'new-error.mkv',
|
||||
conflict: false,
|
||||
error: 'extract_episode_not_found',
|
||||
},
|
||||
];
|
||||
|
||||
document.body.appendChild(element);
|
||||
await element.updateComplete;
|
||||
|
||||
const textContent = element.shadowRoot?.textContent ?? '';
|
||||
expect(textContent).toContain(I18nService.t('error_extract_episode_not_found'));
|
||||
|
||||
element.remove();
|
||||
});
|
||||
|
||||
it('keeps pending badge next to old name row when showStatus is true', async () => {
|
||||
const element = new VirtualPreviewList();
|
||||
element.showStatus = true;
|
||||
element.items = [
|
||||
{
|
||||
file: {
|
||||
id: 'file-pending',
|
||||
name: 'old-pending.mkv',
|
||||
ext: 'mkv',
|
||||
size: 1,
|
||||
mtime: Date.now(),
|
||||
isDir: false,
|
||||
},
|
||||
newName: 'new-pending.mkv',
|
||||
conflict: false,
|
||||
done: false,
|
||||
},
|
||||
];
|
||||
|
||||
document.body.appendChild(element);
|
||||
await element.updateComplete;
|
||||
|
||||
const badge = element.shadowRoot?.querySelector('.old-name-row .status-badge.pending');
|
||||
expect(badge).toBeTruthy();
|
||||
|
||||
element.remove();
|
||||
});
|
||||
});
|
||||
|
||||
正在加载...
在新工单中引用
屏蔽一个用户