1
This commit is contained in:
@@ -0,0 +1,212 @@
|
||||
<template>
|
||||
<div class="collect-sources">
|
||||
<el-card shadow="never">
|
||||
<template #header>
|
||||
<div class="header">
|
||||
<span>采集源管理</span>
|
||||
<div>
|
||||
<el-button :loading="syncAllLoading" @click="syncAll">全部同步</el-button>
|
||||
<el-button @click="batchVisible = true">批量添加</el-button>
|
||||
<el-button type="primary" @click="showCreate">+ 添加采集源</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<el-alert type="info" :closable="false" style="margin-bottom: 12px">
|
||||
添加"分类名 + 列表URL"后点同步即可自动采集;URL 中用 <code v-pre>{{page}}</code> 表示页码占位。
|
||||
分类名不存在时会自动创建。同步为增量:已入库的视频自动跳过。
|
||||
</el-alert>
|
||||
|
||||
<el-table :data="list" v-loading="loading" stripe>
|
||||
<el-table-column prop="id" label="ID" width="60" />
|
||||
<el-table-column prop="name" label="分类名" width="130" />
|
||||
<el-table-column prop="list_url" label="列表 URL" min-width="320" show-overflow-tooltip />
|
||||
<el-table-column label="状态" width="80">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="row.status === 1 ? 'success' : 'info'" size="small">
|
||||
{{ row.status === 1 ? '启用' : '停用' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="last_sync_at" label="上次同步" width="160">
|
||||
<template #default="{ row }">{{ row.last_sync_at || '从未' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="240" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button link type="warning" :loading="syncingId === row.id" @click="syncOne(row)">同步</el-button>
|
||||
<el-button link type="primary" @click="showEdit(row)">编辑</el-button>
|
||||
<el-button link type="primary" @click="toggleStatus(row)">
|
||||
{{ row.status === 1 ? '停用' : '启用' }}
|
||||
</el-button>
|
||||
<el-button link type="danger" @click="del(row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
|
||||
<el-dialog v-model="batchVisible" title="批量添加采集源" width="640px">
|
||||
<el-alert type="info" :closable="false" style="margin-bottom: 10px">
|
||||
每行一条,格式:<code v-pre>分类名 URL</code>(URL 可带反引号)。提交后自动创建分类并立即开始采集。
|
||||
</el-alert>
|
||||
<el-input v-model="batchText" type="textarea" :rows="10"
|
||||
:placeholder="'每日更新 https://mdcmai4.xyz/api/v1/videos?categoryId=27&page={{page}}&size=60\n清纯少女 https://mdcmai4.xyz/api/v1/videos?categoryId=13&page={{page}}&size=60'" />
|
||||
<div v-if="batchResult" style="margin-top: 10px; font-size: 13px">
|
||||
<div v-if="batchResult.created.length" style="color: #67c23a">
|
||||
已创建 {{ batchResult.created.length }} 个并启动采集:{{ batchResult.created.map(c => c.name).join('、') }}
|
||||
</div>
|
||||
<div v-if="batchResult.skipped.length" style="color: #e6a23c">
|
||||
跳过 {{ batchResult.skipped.length }} 条(URL 已存在):{{ batchResult.skipped.map(s => s.name).join('、') }}
|
||||
</div>
|
||||
<div v-if="batchResult.failed.length" style="color: #f56c6c">
|
||||
失败 {{ batchResult.failed.length }} 条:{{ batchResult.failed.map(f => f.line).join(';') }}
|
||||
</div>
|
||||
</div>
|
||||
<template #footer>
|
||||
<el-button @click="batchVisible = false">关闭</el-button>
|
||||
<el-button type="primary" :loading="batchSubmitting" @click="submitBatch">提交</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog v-model="dialogVisible" :title="editing ? '编辑采集源' : '添加采集源'" width="560px">
|
||||
<el-form :model="form" label-width="90px">
|
||||
<el-form-item label="分类名">
|
||||
<el-input v-model="form.name" maxlength="50" placeholder="如:每日更新(不存在会自动创建分类)" />
|
||||
</el-form-item>
|
||||
<el-form-item label="列表 URL">
|
||||
<el-input v-model="form.list_url" type="textarea" :rows="3"
|
||||
placeholder="https://example.com/api/v1/videos?categoryId=27&page={{page}}&size=60" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" :loading="submitting" @click="save">保存</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, ref, onMounted } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import request from '@/api/request'
|
||||
|
||||
const list = ref([])
|
||||
const loading = ref(false)
|
||||
const dialogVisible = ref(false)
|
||||
const editing = ref(null)
|
||||
const submitting = ref(false)
|
||||
const syncingId = ref(0)
|
||||
const syncAllLoading = ref(false)
|
||||
|
||||
const form = reactive({ name: '', list_url: '' })
|
||||
|
||||
const batchVisible = ref(false)
|
||||
const batchText = ref('')
|
||||
const batchSubmitting = ref(false)
|
||||
const batchResult = ref(null)
|
||||
|
||||
async function submitBatch() {
|
||||
if (!batchText.value.trim()) { ElMessage.warning('请粘贴内容'); return }
|
||||
batchSubmitting.value = true
|
||||
batchResult.value = null
|
||||
try {
|
||||
const r = await request({ url: '/admin/collection/sources/batch', method: 'post', data: { text: batchText.value } })
|
||||
batchResult.value = r
|
||||
if (r.created.length) { batchText.value = ''; setTimeout(load, 3000) }
|
||||
else load()
|
||||
} catch (e) {} finally {
|
||||
batchSubmitting.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(load)
|
||||
|
||||
async function load() {
|
||||
loading.value = true
|
||||
try {
|
||||
const r = await request({ url: '/admin/collection/sources', method: 'get' })
|
||||
list.value = r.list
|
||||
} catch (e) {} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function showCreate() {
|
||||
editing.value = null
|
||||
Object.assign(form, { name: '', list_url: '' })
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
function showEdit(row) {
|
||||
editing.value = row
|
||||
Object.assign(form, { name: row.name, list_url: row.list_url })
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
async function save() {
|
||||
if (!form.name || !form.list_url) { ElMessage.warning('请填写完整'); return }
|
||||
if (!form.list_url.includes('{{page}}')) { ElMessage.warning('URL 需包含 {{page}} 页码占位符'); return }
|
||||
submitting.value = true
|
||||
try {
|
||||
if (editing.value) {
|
||||
await request({ url: `/admin/collection/sources/${editing.value.id}`, method: 'put', data: form })
|
||||
ElMessage.success('已更新')
|
||||
} else {
|
||||
await request({ url: '/admin/collection/sources', method: 'post', data: form })
|
||||
ElMessage.success('已添加,点击"同步"开始采集')
|
||||
}
|
||||
dialogVisible.value = false
|
||||
load()
|
||||
} catch (e) {} finally {
|
||||
submitting.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function toggleStatus(row) {
|
||||
try {
|
||||
await request({
|
||||
url: `/admin/collection/sources/${row.id}`, method: 'put',
|
||||
data: { status: row.status === 1 ? 0 : 1 },
|
||||
})
|
||||
ElMessage.success('已更新')
|
||||
load()
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
async function del(row) {
|
||||
await ElMessageBox.confirm(
|
||||
`确认删除采集源「${row.name}」?已采集的视频不会被删除。`, '警告', { type: 'warning' }
|
||||
)
|
||||
try {
|
||||
await request({ url: `/admin/collection/sources/${row.id}`, method: 'delete' })
|
||||
ElMessage.success('已删除')
|
||||
load()
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
async function syncOne(row) {
|
||||
syncingId.value = row.id
|
||||
try {
|
||||
await request({ url: `/admin/collection/sources/${row.id}/sync`, method: 'post', data: { max_pages: 0 } })
|
||||
ElMessage.success(`「${row.name}」已触发后台同步,稍后刷新查看`)
|
||||
setTimeout(load, 20000)
|
||||
} catch (e) {} finally {
|
||||
syncingId.value = 0
|
||||
}
|
||||
}
|
||||
|
||||
async function syncAll() {
|
||||
syncAllLoading.value = true
|
||||
try {
|
||||
const r = await request({ url: '/admin/collection/sources/sync-all', method: 'post' })
|
||||
ElMessage.success(`已触发 ${r.started.length} 个源的后台同步`)
|
||||
setTimeout(load, 20000)
|
||||
} catch (e) {} finally {
|
||||
syncAllLoading.value = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.header { display: flex; justify-content: space-between; align-items: center; }
|
||||
</style>
|
||||
Reference in New Issue
Block a user