|
|
@@ -0,0 +1,485 @@
|
|
|
+<template>
|
|
|
+ <div class="space-y-6">
|
|
|
+ <!-- 主卡片 -->
|
|
|
+ <div class="bg-white rounded-xl shadow-sm border border-gray-200 p-6">
|
|
|
+ <div class="border-b border-gray-200 pb-4 mb-6">
|
|
|
+ <div class="flex items-center justify-between">
|
|
|
+ <h2 class="text-xl font-semibold text-gray-800 flex items-center gap-2">
|
|
|
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-indigo-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
|
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253" />
|
|
|
+ </svg>
|
|
|
+ 小说资产列表
|
|
|
+ </h2>
|
|
|
+ <div class="flex items-center gap-3">
|
|
|
+ <el-button type="primary" @click="showSyncAccountDialog">我的同步账号管理</el-button>
|
|
|
+ <el-select v-model="accountId" placeholder="选择同步账号" clearable filterable style="width: 250px">
|
|
|
+ <el-option v-for="conf in confList" :key="conf.id" :label="`${conf.accountId}`" :value="conf.accountId" />
|
|
|
+ </el-select>
|
|
|
+ <el-button
|
|
|
+ type="primary"
|
|
|
+ @click="handleSync"
|
|
|
+ :loading="syncing"
|
|
|
+ >
|
|
|
+ <el-icon><Refresh /></el-icon>
|
|
|
+ 同步资产
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 查询条件 -->
|
|
|
+ <div class="flex flex-col gap-3 mb-6">
|
|
|
+ <div class="flex flex-wrap items-center gap-4">
|
|
|
+ <el-input
|
|
|
+ v-model="queryForm.fictionName"
|
|
|
+ placeholder="小说名称筛选"
|
|
|
+ clearable
|
|
|
+ style="width: 220px"
|
|
|
+ @clear="handleSearch"
|
|
|
+ @keyup.enter="handleSearch"
|
|
|
+ />
|
|
|
+ <el-button type="primary" @click="handleSearch">
|
|
|
+ <el-icon><Search /></el-icon>
|
|
|
+ 查询
|
|
|
+ </el-button>
|
|
|
+ <el-button @click="handleReset">重置</el-button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 数据统计 -->
|
|
|
+ <div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-6">
|
|
|
+ <div class="bg-gradient-to-br from-indigo-50 to-indigo-100 rounded-lg p-4 border border-indigo-200">
|
|
|
+ <div class="flex items-center justify-between">
|
|
|
+ <div>
|
|
|
+ <p class="text-sm text-indigo-600 mb-1">总资产数</p>
|
|
|
+ <p class="text-2xl font-bold text-indigo-700">{{ total }}</p>
|
|
|
+ </div>
|
|
|
+ <div class="p-3 bg-indigo-200 rounded-lg">
|
|
|
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-indigo-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
|
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253" />
|
|
|
+ </svg>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="bg-gradient-to-br from-blue-50 to-blue-100 rounded-lg p-4 border border-blue-200">
|
|
|
+ <div class="flex items-center justify-between">
|
|
|
+ <div>
|
|
|
+ <p class="text-sm text-blue-600 mb-1">当前页数量</p>
|
|
|
+ <p class="text-2xl font-bold text-blue-700">{{ fictionList.length }}</p>
|
|
|
+ </div>
|
|
|
+ <div class="p-3 bg-blue-200 rounded-lg">
|
|
|
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-blue-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
|
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
|
|
+ </svg>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="bg-gradient-to-br from-green-50 to-green-100 rounded-lg p-4 border border-green-200">
|
|
|
+ <div class="flex items-center justify-between">
|
|
|
+ <div>
|
|
|
+ <p class="text-sm text-green-600 mb-1">总页数</p>
|
|
|
+ <p class="text-2xl font-bold text-green-700">{{ totalPages }}</p>
|
|
|
+ </div>
|
|
|
+ <div class="p-3 bg-green-200 rounded-lg">
|
|
|
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-green-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
|
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10" />
|
|
|
+ </svg>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 小说资产列表表格 -->
|
|
|
+ <el-table
|
|
|
+ :data="fictionList"
|
|
|
+ style="width: 100%"
|
|
|
+ v-loading="loading"
|
|
|
+ stripe
|
|
|
+ border
|
|
|
+ >
|
|
|
+ <el-table-column prop="marketingAssetId" label="资产ID" width="150" />
|
|
|
+ <el-table-column prop="marketingAssetName" label="小说名称" width="300" show-overflow-tooltip />
|
|
|
+ <el-table-column prop="marketingAssetType" label="资产类型" width="200" />
|
|
|
+ <el-table-column label="创建时间" width="180">
|
|
|
+ <template #default="{ row }">
|
|
|
+ {{ formatTimestamp(row.createdTime) }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="更新时间" width="180">
|
|
|
+ <template #default="{ row }">
|
|
|
+ {{ formatDate(row.updateTime) }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="操作" width="150" fixed="right">
|
|
|
+ <template #default="{ row }">
|
|
|
+ <el-button
|
|
|
+ type="primary"
|
|
|
+ size="small"
|
|
|
+ link
|
|
|
+ @click="handleViewDetail(row)"
|
|
|
+ >
|
|
|
+ <el-icon><View /></el-icon>
|
|
|
+ 查看详情
|
|
|
+ </el-button>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+
|
|
|
+ <!-- 分页组件 -->
|
|
|
+ <div class="mt-6 flex justify-end">
|
|
|
+ <el-pagination
|
|
|
+ v-if="total > 0"
|
|
|
+ v-model:current-page="currentPage"
|
|
|
+ v-model:page-size="pageSize"
|
|
|
+ :page-sizes="[10, 20, 50, 100]"
|
|
|
+ :total="total"
|
|
|
+ layout="total, sizes, prev, pager, next, jumper"
|
|
|
+ @size-change="handleSizeChange"
|
|
|
+ @current-change="handleCurrentChange"
|
|
|
+ style="justify-content: flex-end"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 同步账号管理对话框 -->
|
|
|
+ <el-dialog
|
|
|
+ v-model="syncAccountDialogVisible"
|
|
|
+ title="我的同步账号管理"
|
|
|
+ width="600px"
|
|
|
+ @close="resetConfForm"
|
|
|
+ >
|
|
|
+ <!-- 新增表单 -->
|
|
|
+ <div class="bg-gray-50 rounded-lg p-4 mb-5 border border-gray-200">
|
|
|
+ <p class="text-sm font-medium text-gray-700 mb-3">新增同步账号</p>
|
|
|
+ <el-form
|
|
|
+ :model="confForm"
|
|
|
+ :rules="confFormRules"
|
|
|
+ ref="confFormRef"
|
|
|
+ label-width="80px"
|
|
|
+ size="default"
|
|
|
+ >
|
|
|
+ <el-form-item label="账号ID" prop="accountId">
|
|
|
+ <el-input
|
|
|
+ v-model="confForm.accountId"
|
|
|
+ placeholder="请输入推广账号ID"
|
|
|
+ clearable
|
|
|
+ style="width: 100%"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item style="margin-bottom: 0">
|
|
|
+ <el-button type="primary" :loading="addingConf" @click="handleAddConf">确认新增</el-button>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 账号列表 -->
|
|
|
+ <el-table
|
|
|
+ :data="confList"
|
|
|
+ v-loading="confListLoading"
|
|
|
+ stripe
|
|
|
+ border
|
|
|
+ style="width: 100%"
|
|
|
+ empty-text="暂无同步账号配置"
|
|
|
+ >
|
|
|
+ <el-table-column prop="accountId" label="账号ID" />
|
|
|
+ <el-table-column label="创建时间" width="170">
|
|
|
+ <template #default="{ row }">
|
|
|
+ {{ formatDate(row.createTime) }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="操作" width="80" fixed="right">
|
|
|
+ <template #default="{ row }">
|
|
|
+ <el-button
|
|
|
+ type="danger"
|
|
|
+ size="small"
|
|
|
+ link
|
|
|
+ @click="handleDelConf(row)"
|
|
|
+ >
|
|
|
+ <el-icon><Delete /></el-icon>
|
|
|
+ 删除
|
|
|
+ </el-button>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+
|
|
|
+ <template #footer>
|
|
|
+ <el-button @click="syncAccountDialogVisible = false">关闭</el-button>
|
|
|
+ </template>
|
|
|
+ </el-dialog>
|
|
|
+
|
|
|
+ <!-- 小说资产详情对话框 -->
|
|
|
+ <el-dialog
|
|
|
+ v-model="detailDialogVisible"
|
|
|
+ title="小说资产详情"
|
|
|
+ width="700px"
|
|
|
+ >
|
|
|
+ <el-descriptions :column="1" border v-if="currentFiction">
|
|
|
+ <el-descriptions-item label="资产ID">
|
|
|
+ {{ currentFiction.marketingAssetId }}
|
|
|
+ </el-descriptions-item>
|
|
|
+ <el-descriptions-item label="小说名称">
|
|
|
+ {{ currentFiction.marketingAssetName }}
|
|
|
+ </el-descriptions-item>
|
|
|
+ <el-descriptions-item label="资产类型">
|
|
|
+ {{ currentFiction.marketingAssetType }}
|
|
|
+ </el-descriptions-item>
|
|
|
+ <el-descriptions-item label="创建时间">
|
|
|
+ {{ formatTimestamp(currentFiction.createdTime) }}
|
|
|
+ </el-descriptions-item>
|
|
|
+ <el-descriptions-item label="记录创建时间">
|
|
|
+ {{ formatDate(currentFiction.createTime) }}
|
|
|
+ </el-descriptions-item>
|
|
|
+ <el-descriptions-item label="记录更新时间">
|
|
|
+ {{ formatDate(currentFiction.updateTime) }}
|
|
|
+ </el-descriptions-item>
|
|
|
+ <el-descriptions-item label="资产属性">
|
|
|
+ <pre v-if="currentFiction.properties" style="margin: 0; white-space: pre-wrap; word-break: break-all; max-height: 400px; overflow-y: auto;">{{ formatJSON(currentFiction.properties) }}</pre>
|
|
|
+ <span v-else>无</span>
|
|
|
+ </el-descriptions-item>
|
|
|
+ </el-descriptions>
|
|
|
+ <template #footer>
|
|
|
+ <el-button @click="detailDialogVisible = false">关闭</el-button>
|
|
|
+ </template>
|
|
|
+ </el-dialog>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup>
|
|
|
+import { ref, computed, onMounted } from 'vue'
|
|
|
+import { ElMessage, ElMessageBox } from 'element-plus'
|
|
|
+import { getFictionList, syncFiction } from '@/api/fiction'
|
|
|
+// 同步账号管理共用 drama 的 API
|
|
|
+import { myConfList, addConf, delConf } from '@/api/drama'
|
|
|
+
|
|
|
+import {
|
|
|
+ Refresh,
|
|
|
+ View,
|
|
|
+ Delete,
|
|
|
+ Search
|
|
|
+} from '@element-plus/icons-vue'
|
|
|
+
|
|
|
+// 数据
|
|
|
+const loading = ref(false)
|
|
|
+const syncing = ref(false)
|
|
|
+const fictionList = ref([])
|
|
|
+const detailDialogVisible = ref(false)
|
|
|
+const currentFiction = ref(null)
|
|
|
+
|
|
|
+// 同步账号管理 Dialog
|
|
|
+const syncAccountDialogVisible = ref(false)
|
|
|
+const confListLoading = ref(false)
|
|
|
+const addingConf = ref(false)
|
|
|
+const confFormRef = ref(null)
|
|
|
+const confForm = ref({ accountId: '' })
|
|
|
+const confFormRules = {
|
|
|
+ accountId: [
|
|
|
+ { required: true, message: '请输入账号ID', trigger: 'blur' },
|
|
|
+ { pattern: /^\d+$/, message: '账号ID必须为纯数字', trigger: 'blur' }
|
|
|
+ ]
|
|
|
+}
|
|
|
+
|
|
|
+// 同步参数
|
|
|
+const accountId = ref('')
|
|
|
+const confList = ref([])
|
|
|
+
|
|
|
+const queryForm = ref({ fictionName: '' })
|
|
|
+
|
|
|
+// 分页数据
|
|
|
+const currentPage = ref(1)
|
|
|
+const pageSize = ref(20)
|
|
|
+const total = ref(0)
|
|
|
+
|
|
|
+// 计算属性
|
|
|
+const totalPages = computed(() => {
|
|
|
+ return Math.ceil(total.value / pageSize.value)
|
|
|
+})
|
|
|
+
|
|
|
+const handleSearch = () => {
|
|
|
+ currentPage.value = 1
|
|
|
+ loadFictionList()
|
|
|
+}
|
|
|
+
|
|
|
+const handleReset = () => {
|
|
|
+ queryForm.value = { fictionName: '' }
|
|
|
+ currentPage.value = 1
|
|
|
+ loadFictionList()
|
|
|
+}
|
|
|
+
|
|
|
+// 方法
|
|
|
+const loadFictionList = async () => {
|
|
|
+ loading.value = true
|
|
|
+ try {
|
|
|
+ const { fictionName } = queryForm.value
|
|
|
+ const res = await getFictionList(currentPage.value, pageSize.value, fictionName)
|
|
|
+ if (res.success) {
|
|
|
+ fictionList.value = res.data || []
|
|
|
+ total.value = res.total || 0
|
|
|
+ } else {
|
|
|
+ ElMessage.error(res.message || '加载小说资产列表失败')
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('加载小说资产列表失败:', error)
|
|
|
+ ElMessage.error('加载小说资产列表失败')
|
|
|
+ } finally {
|
|
|
+ loading.value = false
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 分页方法
|
|
|
+const handleSizeChange = async (newSize) => {
|
|
|
+ pageSize.value = newSize
|
|
|
+ currentPage.value = 1
|
|
|
+ await loadFictionList()
|
|
|
+}
|
|
|
+
|
|
|
+const handleCurrentChange = async (newPage) => {
|
|
|
+ currentPage.value = newPage
|
|
|
+ await loadFictionList()
|
|
|
+}
|
|
|
+
|
|
|
+const handleSync = async () => {
|
|
|
+ if (!accountId.value) {
|
|
|
+ ElMessage.warning('请填写所有同步参数')
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ await ElMessageBox.confirm(
|
|
|
+ '确定要同步该账户下的小说资产吗?',
|
|
|
+ '提示',
|
|
|
+ {
|
|
|
+ confirmButtonText: '确定',
|
|
|
+ cancelButtonText: '取消',
|
|
|
+ type: 'warning'
|
|
|
+ }
|
|
|
+ )
|
|
|
+
|
|
|
+ syncing.value = true
|
|
|
+ const res = await syncFiction(accountId.value)
|
|
|
+
|
|
|
+ if (res.success) {
|
|
|
+ ElMessage.success(res.message || '同步成功')
|
|
|
+ // 重新加载资产列表
|
|
|
+ currentPage.value = 1 // 重置到第一页
|
|
|
+ await loadFictionList()
|
|
|
+ } else {
|
|
|
+ ElMessage.error(res.message || '同步失败')
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ if (error !== 'cancel') {
|
|
|
+ console.error('同步小说资产失败:', error)
|
|
|
+ ElMessage.error('同步小说资产失败')
|
|
|
+ }
|
|
|
+ } finally {
|
|
|
+ syncing.value = false
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const handleViewDetail = (fiction) => {
|
|
|
+ currentFiction.value = fiction
|
|
|
+ detailDialogVisible.value = true
|
|
|
+}
|
|
|
+
|
|
|
+const formatDate = (dateStr) => {
|
|
|
+ if (!dateStr) return '-'
|
|
|
+ return new Date(dateStr).toLocaleString('zh-CN')
|
|
|
+}
|
|
|
+
|
|
|
+const formatTimestamp = (timestamp) => {
|
|
|
+ if (!timestamp) return '-'
|
|
|
+ return new Date(timestamp * 1000).toLocaleString('zh-CN')
|
|
|
+}
|
|
|
+
|
|
|
+const formatJSON = (jsonStr) => {
|
|
|
+ if (!jsonStr) return ''
|
|
|
+ try {
|
|
|
+ return JSON.stringify(JSON.parse(jsonStr), null, 2)
|
|
|
+ } catch {
|
|
|
+ return jsonStr
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const loadConfList = async () => {
|
|
|
+ confListLoading.value = true
|
|
|
+ try {
|
|
|
+ const response = await myConfList()
|
|
|
+ if (response.success) {
|
|
|
+ confList.value = response.data || []
|
|
|
+ } else {
|
|
|
+ ElMessage.error(response.message || '加载授权列表失败')
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('加载授权列表失败:', error)
|
|
|
+ ElMessage.error('加载授权列表失败')
|
|
|
+ } finally {
|
|
|
+ confListLoading.value = false
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const showSyncAccountDialog = () => {
|
|
|
+ syncAccountDialogVisible.value = true
|
|
|
+}
|
|
|
+
|
|
|
+const resetConfForm = () => {
|
|
|
+ confForm.value = { accountId: '' }
|
|
|
+ confFormRef.value?.clearValidate()
|
|
|
+}
|
|
|
+
|
|
|
+const handleAddConf = async () => {
|
|
|
+ await confFormRef.value?.validate(async (valid) => {
|
|
|
+ if (!valid) return
|
|
|
+ addingConf.value = true
|
|
|
+ try {
|
|
|
+ const res = await addConf({ accountId: confForm.value.accountId })
|
|
|
+ if (res.success) {
|
|
|
+ ElMessage.success('新增成功')
|
|
|
+ resetConfForm()
|
|
|
+ await loadConfList()
|
|
|
+ } else {
|
|
|
+ ElMessage.error(res.message || '新增失败')
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('新增同步账号失败:', error)
|
|
|
+ ElMessage.error('新增失败')
|
|
|
+ } finally {
|
|
|
+ addingConf.value = false
|
|
|
+ }
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+const handleDelConf = async (row) => {
|
|
|
+ try {
|
|
|
+ await ElMessageBox.confirm(
|
|
|
+ `确定要删除账号 ${row.accountId} 的同步配置吗?`,
|
|
|
+ '确认删除',
|
|
|
+ {
|
|
|
+ confirmButtonText: '确定',
|
|
|
+ cancelButtonText: '取消',
|
|
|
+ type: 'warning'
|
|
|
+ }
|
|
|
+ )
|
|
|
+ const res = await delConf(row.id)
|
|
|
+ if (res.success) {
|
|
|
+ ElMessage.success('删除成功')
|
|
|
+ await loadConfList()
|
|
|
+ } else {
|
|
|
+ ElMessage.error(res.message || '删除失败')
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ if (error !== 'cancel') {
|
|
|
+ console.error('删除同步账号失败:', error)
|
|
|
+ ElMessage.error('删除失败')
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 生命周期
|
|
|
+onMounted(async () => {
|
|
|
+ await loadConfList()
|
|
|
+ loadFictionList()
|
|
|
+})
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+/* 使用Tailwind CSS类替代自定义样式 */
|
|
|
+</style>
|