待看名单增加覆盖功能
This commit is contained in:
@@ -56,11 +56,13 @@
|
||||
:batch-urls="batchUrls"
|
||||
:auto-generate-title="autoGenerateTitle"
|
||||
:parsed-urls="parsedUrls"
|
||||
:import-mode="importMode"
|
||||
@update:mode="addMode = $event"
|
||||
@update:title="addTitle = $event"
|
||||
@update:url="addUrl = $event"
|
||||
@update:batchUrls="batchUrls = $event"
|
||||
@update:autoGenerateTitle="autoGenerateTitle = $event"
|
||||
@update:importMode="importMode = $event"
|
||||
@quickAdd="fillQuickAdd"
|
||||
@save="saveAdd"
|
||||
@cancel="cancelAdd"
|
||||
@@ -92,6 +94,7 @@ const addUrl = ref('');
|
||||
const addMode = ref<'single' | 'batch'>('single');
|
||||
const batchUrls = ref('');
|
||||
const autoGenerateTitle = ref(true);
|
||||
const importMode = ref<'merge' | 'overwrite'>('merge');
|
||||
|
||||
// 搜索和排序
|
||||
const searchQuery = ref('');
|
||||
@@ -401,7 +404,29 @@ const saveAdd = async () => {
|
||||
}
|
||||
} else {
|
||||
// 批量添加模式
|
||||
const urlsToAdd = parsedUrls.value.filter(item => !item.isDuplicate);
|
||||
let urlsToAdd = parsedUrls.value;
|
||||
|
||||
if (importMode.value === 'merge') {
|
||||
// 重合模式:只添加不重复的URL
|
||||
urlsToAdd = urlsToAdd.filter(item => !item.isDuplicate);
|
||||
} else if (importMode.value === 'overwrite') {
|
||||
// 覆盖模式:删除所有现有项目,然后添加所有新项目
|
||||
if (urlsToAdd.length === 0) return;
|
||||
|
||||
// 确认覆盖操作
|
||||
if (!confirm('覆盖模式将删除所有现有的待看项目并导入新的项目。确定要继续吗?')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 删除所有现有项目
|
||||
const allItems = items.value;
|
||||
for (const item of allItems) {
|
||||
await watchlistStore.deleteItem(item.id);
|
||||
}
|
||||
|
||||
console.log(`已删除 ${allItems.length} 个现有项目`);
|
||||
}
|
||||
|
||||
if (urlsToAdd.length === 0) return;
|
||||
|
||||
// 依次添加每个URL
|
||||
@@ -422,7 +447,11 @@ const saveAdd = async () => {
|
||||
|
||||
if (successCount > 0) {
|
||||
cancelAdd();
|
||||
console.log(`成功添加 ${successCount} 个项目`);
|
||||
if (importMode.value === 'overwrite') {
|
||||
console.log(`覆盖导入完成:成功添加 ${successCount} 个项目`);
|
||||
} else {
|
||||
console.log(`重合导入完成:成功添加 ${successCount} 个项目`);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -52,6 +52,35 @@
|
||||
|
||||
<!-- 批量添加模式 -->
|
||||
<template v-else>
|
||||
<!-- 导入模式选择 -->
|
||||
<div class="form-group">
|
||||
<label>导入模式</label>
|
||||
<div class="import-mode-selector">
|
||||
<button @click="$emit('update:importMode', 'merge')"
|
||||
:class="['import-mode-btn', { active: importMode === 'merge' }]"
|
||||
type="button">
|
||||
<div class="mode-icon">🔄</div>
|
||||
<div class="mode-info">
|
||||
<div class="mode-title">重合模式</div>
|
||||
<div class="mode-desc">跳过已存在的项目,只添加新项目</div>
|
||||
</div>
|
||||
</button>
|
||||
<button @click="$emit('update:importMode', 'overwrite')"
|
||||
:class="['import-mode-btn', { active: importMode === 'overwrite' }]"
|
||||
type="button">
|
||||
<div class="mode-icon">⚠️</div>
|
||||
<div class="mode-info">
|
||||
<div class="mode-title">覆盖模式</div>
|
||||
<div class="mode-desc">清空现有数据,重新导入所有项目</div>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
<small class="form-help">
|
||||
<strong>重合模式:</strong>保留现有数据,只添加新的项目(推荐)<br>
|
||||
<strong>覆盖模式:</strong>删除所有现有数据,重新导入(谨慎使用)
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>批量URL列表</label>
|
||||
<textarea :value="batchUrls"
|
||||
@@ -83,7 +112,8 @@ http://localhost:3001/artist/103047332
|
||||
<div class="preview-list">
|
||||
<div v-for="(item, index) in parsedUrls" :key="index" class="preview-item">
|
||||
<div class="preview-url">{{ item.path }}</div>
|
||||
<div v-if="item.isDuplicate" class="preview-status duplicate">已存在</div>
|
||||
<div v-if="importMode === 'overwrite'" class="preview-status overwrite">将导入</div>
|
||||
<div v-else-if="item.isDuplicate" class="preview-status duplicate">已存在</div>
|
||||
<div v-else class="preview-status new">新增</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -116,6 +146,7 @@ interface Props {
|
||||
batchUrls: string;
|
||||
autoGenerateTitle: boolean;
|
||||
parsedUrls: ParsedUrl[];
|
||||
importMode: 'merge' | 'overwrite';
|
||||
}
|
||||
|
||||
defineProps<Props>();
|
||||
@@ -128,6 +159,7 @@ const emit = defineEmits<{
|
||||
'update:url': [value: string];
|
||||
'update:batchUrls': [value: string];
|
||||
'update:autoGenerateTitle': [value: boolean];
|
||||
'update:importMode': [value: 'merge' | 'overwrite'];
|
||||
quickAdd: [path: string, title: string];
|
||||
}>();
|
||||
|
||||
@@ -378,6 +410,64 @@ const handleSave = () => {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.preview-status.overwrite {
|
||||
background: var(--color-primary);
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* 导入模式选择器样式 */
|
||||
.import-mode-selector {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.import-mode-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 12px 16px;
|
||||
border: 2px solid var(--color-border);
|
||||
border-radius: 8px;
|
||||
background: var(--color-bg-secondary);
|
||||
color: var(--color-text-secondary);
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.import-mode-btn:hover {
|
||||
background: var(--color-bg-tertiary);
|
||||
border-color: var(--color-primary-light);
|
||||
}
|
||||
|
||||
.import-mode-btn.active {
|
||||
background: var(--color-primary-light);
|
||||
border-color: var(--color-primary);
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.mode-icon {
|
||||
font-size: 20px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.mode-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.mode-title {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.mode-desc {
|
||||
font-size: 12px;
|
||||
opacity: 0.8;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.modal-actions {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
|
||||
@@ -7,9 +7,27 @@
|
||||
<button @click="exportWatchlist" class="export-btn" title="导出待看名单">
|
||||
<SvgIcon name="download" class="export-icon" />
|
||||
</button>
|
||||
<button @click="triggerImport" class="import-btn" title="导入待看名单">
|
||||
<SvgIcon name="upload" class="import-icon" />
|
||||
</button>
|
||||
<div class="import-section">
|
||||
<button @click="triggerImport" class="import-btn" title="导入待看名单">
|
||||
<SvgIcon name="upload" class="import-icon" />
|
||||
</button>
|
||||
<div class="import-mode-selector">
|
||||
<button
|
||||
@click="importMode = 'merge'"
|
||||
:class="['mode-btn', { active: importMode === 'merge' }]"
|
||||
title="重合模式:跳过已存在的项目"
|
||||
>
|
||||
重合
|
||||
</button>
|
||||
<button
|
||||
@click="importMode = 'overwrite'"
|
||||
:class="['mode-btn', { active: importMode === 'overwrite' }]"
|
||||
title="覆盖模式:删除所有现有项目后导入"
|
||||
>
|
||||
覆盖
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<input
|
||||
ref="fileInput"
|
||||
type="file"
|
||||
@@ -77,6 +95,9 @@ defineEmits<{
|
||||
// 获取watchlist store实例
|
||||
const watchlistStore = useWatchlistStore();
|
||||
|
||||
// 导入模式状态
|
||||
const importMode = ref<'merge' | 'overwrite'>('merge');
|
||||
|
||||
// 文件输入引用
|
||||
const fileInput = ref<HTMLInputElement>();
|
||||
|
||||
@@ -97,8 +118,17 @@ const handleFileImport = async (event: Event) => {
|
||||
|
||||
if (!file) return;
|
||||
|
||||
// 如果是覆盖模式,先确认
|
||||
if (importMode.value === 'overwrite') {
|
||||
const confirmed = confirm('覆盖模式将删除所有现有的待看项目,确定要继续吗?');
|
||||
if (!confirmed) {
|
||||
target.value = '';
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await watchlistStore.importWatchlist(file);
|
||||
const result = await watchlistStore.importWatchlist(file, importMode.value);
|
||||
|
||||
if (result.success) {
|
||||
alert(result.message);
|
||||
@@ -219,6 +249,49 @@ const handleFileImport = async (event: Event) => {
|
||||
height: 14px;
|
||||
}
|
||||
|
||||
/* 导入区域样式 */
|
||||
.import-section {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.import-mode-selector {
|
||||
display: flex;
|
||||
background: var(--color-bg-tertiary);
|
||||
border-radius: 4px;
|
||||
padding: 2px;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.mode-btn {
|
||||
padding: 4px 8px;
|
||||
font-size: 11px;
|
||||
border: none;
|
||||
border-radius: 3px;
|
||||
background: transparent;
|
||||
color: var(--color-text-secondary);
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.mode-btn:hover {
|
||||
background: var(--color-bg-hover);
|
||||
color: var(--color-text-primary);
|
||||
}
|
||||
|
||||
.mode-btn.active {
|
||||
background: var(--color-primary);
|
||||
color: white;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.mode-btn.active:hover {
|
||||
background: var(--color-primary-dark);
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 768px) {
|
||||
.watchlist-panel {
|
||||
|
||||
Reference in New Issue
Block a user