|
|
@@ -183,12 +183,13 @@
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
|
|
|
- <el-table-column label="操作" width="200" fixed="right">
|
|
|
+ <el-table-column label="操作" width="250" fixed="right">
|
|
|
<template #default="{ row }">
|
|
|
<div class="flex gap-2">
|
|
|
<el-button type="primary" size="small" link @click="copyUrl(row)">复制链接</el-button>
|
|
|
<el-button type="primary" size="small" link @click="downloadFile(row)">下载</el-button>
|
|
|
<el-button type="danger" size="small" link @click="handleDelete(row)">删除</el-button>
|
|
|
+ <el-button type="danger" size="small" link @click="handleStatistics(row)">统计</el-button>
|
|
|
</div>
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
@@ -214,6 +215,78 @@
|
|
|
<img :src="previewUrl" alt="预览" style="max-width: 100%; max-height: 600px;">
|
|
|
</div>
|
|
|
</el-dialog>
|
|
|
+
|
|
|
+ <!-- 统计信息弹窗 -->
|
|
|
+ <el-dialog v-model="statisticsDialogVisible" :title="`文件统计 - ${currentFileName}`" width="900px">
|
|
|
+ <div class="space-y-4">
|
|
|
+ <!-- 日期筛选 -->
|
|
|
+ <div class="bg-gray-50 p-4 rounded-lg">
|
|
|
+ <el-form :inline="true" :model="statisticsQuery" class="flex flex-wrap gap-4">
|
|
|
+ <el-form-item label="日期范围">
|
|
|
+ <el-date-picker
|
|
|
+ v-model="statisticsQuery.dateRange"
|
|
|
+ type="daterange"
|
|
|
+ range-separator="至"
|
|
|
+ start-placeholder="开始日期"
|
|
|
+ end-placeholder="结束日期"
|
|
|
+ value-format="YYYY-MM-DD"
|
|
|
+ style="width: 280px">
|
|
|
+ </el-date-picker>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item>
|
|
|
+ <el-button type="primary" @click="handleStatisticsQuery">查询</el-button>
|
|
|
+ <el-button @click="handleStatisticsReset">重置</el-button>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 统计表格 -->
|
|
|
+ <el-table
|
|
|
+ :data="statisticsList"
|
|
|
+ v-loading="statisticsLoading"
|
|
|
+ style="width: 100%"
|
|
|
+ :header-cell-style="{ background: '#f9fafb', color: '#374151', fontWeight: '600' }">
|
|
|
+ <el-table-column prop="date" label="日期" width="120">
|
|
|
+ <template #default="{ row }">
|
|
|
+ {{ row.date.substring(0, 10) }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="viewCount" label="曝光次数" width="120">
|
|
|
+ <template #default="{ row }">
|
|
|
+ {{ formatNumber(row.viewCount) }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="validClickCount" label="点击次数" width="120">
|
|
|
+ <template #default="{ row }">
|
|
|
+ {{ formatNumber(row.validClickCount) }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="ctr" label="点击率" width="120">
|
|
|
+ <template #default="{ row }">
|
|
|
+ <el-tag :type="getCtrTagType(row.ctr)" size="small">{{ formatCtr(row.ctr) }}</el-tag>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="cost" label="花费(元)" min-width="120">
|
|
|
+ <template #default="{ row }">
|
|
|
+ {{ formatCost(row.cost) }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+
|
|
|
+ <!-- 分页 -->
|
|
|
+ <div class="flex justify-end">
|
|
|
+ <el-pagination
|
|
|
+ v-model:current-page="statisticsPagination.page"
|
|
|
+ v-model:page-size="statisticsPagination.size"
|
|
|
+ :page-sizes="[10, 20, 50, 100]"
|
|
|
+ :total="statisticsPagination.total"
|
|
|
+ layout="total, sizes, prev, pager, next, jumper"
|
|
|
+ @size-change="handleStatisticsSizeChange"
|
|
|
+ @current-change="handleStatisticsPageChange">
|
|
|
+ </el-pagination>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-dialog>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
@@ -227,6 +300,7 @@ import { useRouter } from 'vue-router'
|
|
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
|
|
import { UploadFilled } from '@element-plus/icons-vue'
|
|
|
import { uploadFile, getFileList, deleteFile, batchDeleteFiles } from '@/api/ossFile'
|
|
|
+import { getFileStatisticsList } from '@/api/ossFileStatistics'
|
|
|
|
|
|
const router = useRouter()
|
|
|
|
|
|
@@ -347,6 +421,21 @@ const sortParams = reactive({
|
|
|
const previewVisible = ref(false)
|
|
|
const previewUrl = ref('')
|
|
|
|
|
|
+// 统计弹窗相关
|
|
|
+const statisticsDialogVisible = ref(false)
|
|
|
+const currentFileMd5 = ref('')
|
|
|
+const currentFileName = ref('')
|
|
|
+const statisticsList = ref([])
|
|
|
+const statisticsLoading = ref(false)
|
|
|
+const statisticsQuery = reactive({
|
|
|
+ dateRange: []
|
|
|
+})
|
|
|
+const statisticsPagination = reactive({
|
|
|
+ page: 1,
|
|
|
+ size: 10,
|
|
|
+ total: 0
|
|
|
+})
|
|
|
+
|
|
|
// 验证普通图片
|
|
|
const validateNormalImage = (file) => {
|
|
|
return new Promise((resolve, reject) => {
|
|
|
@@ -529,6 +618,17 @@ const handleSelectionChange = (selection) => {
|
|
|
selectedFiles.value = selection
|
|
|
}
|
|
|
|
|
|
+const handleStatistics = (row) => {
|
|
|
+ currentFileMd5.value = row.fileMd5
|
|
|
+ currentFileName.value = row.originalFileName
|
|
|
+ statisticsDialogVisible.value = true
|
|
|
+ // 重置查询条件并加载数据
|
|
|
+ statisticsQuery.dateRange = []
|
|
|
+ statisticsPagination.page = 1
|
|
|
+ loadStatisticsList()
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
// 删除文件
|
|
|
const handleDelete = (row) => {
|
|
|
ElMessageBox.confirm('确定要删除该文件吗?', '提示', {
|
|
|
@@ -649,6 +749,83 @@ const goToCreativeList = (md5) => {
|
|
|
onMounted(() => {
|
|
|
loadFileList()
|
|
|
})
|
|
|
+
|
|
|
+// 加载统计列表
|
|
|
+const loadStatisticsList = async () => {
|
|
|
+ if (!currentFileMd5.value) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ statisticsLoading.value = true
|
|
|
+ try {
|
|
|
+ const res = await getFileStatisticsList({
|
|
|
+ page: statisticsPagination.page,
|
|
|
+ size: statisticsPagination.size,
|
|
|
+ fileMd5: currentFileMd5.value,
|
|
|
+ startDate: statisticsQuery.dateRange && statisticsQuery.dateRange.length > 0 ? statisticsQuery.dateRange[0] : '',
|
|
|
+ endDate: statisticsQuery.dateRange && statisticsQuery.dateRange.length > 0 ? statisticsQuery.dateRange[1] : ''
|
|
|
+ })
|
|
|
+ if (res.success) {
|
|
|
+ statisticsList.value = res.data.records
|
|
|
+ statisticsPagination.total = res.data.total
|
|
|
+ } else {
|
|
|
+ ElMessage.error(res.message || '查询统计失败')
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('查询统计失败:', error)
|
|
|
+ ElMessage.error('查询统计失败')
|
|
|
+ } finally {
|
|
|
+ statisticsLoading.value = false
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 统计查询
|
|
|
+const handleStatisticsQuery = () => {
|
|
|
+ statisticsPagination.page = 1
|
|
|
+ loadStatisticsList()
|
|
|
+}
|
|
|
+
|
|
|
+// 统计重置
|
|
|
+const handleStatisticsReset = () => {
|
|
|
+ statisticsQuery.dateRange = []
|
|
|
+ handleStatisticsQuery()
|
|
|
+}
|
|
|
+
|
|
|
+// 统计分页变化
|
|
|
+const handleStatisticsSizeChange = (size) => {
|
|
|
+ statisticsPagination.size = size
|
|
|
+ loadStatisticsList()
|
|
|
+}
|
|
|
+
|
|
|
+const handleStatisticsPageChange = (page) => {
|
|
|
+ statisticsPagination.page = page
|
|
|
+ loadStatisticsList()
|
|
|
+}
|
|
|
+
|
|
|
+// 格式化数字
|
|
|
+const formatNumber = (num) => {
|
|
|
+ if (num === null || num === undefined) return '-'
|
|
|
+ return num.toLocaleString('zh-CN')
|
|
|
+}
|
|
|
+
|
|
|
+// 格式化点击率
|
|
|
+const formatCtr = (ctr) => {
|
|
|
+ if (ctr === null || ctr === undefined) return '-'
|
|
|
+ return (ctr * 100).toFixed(2) + '%'
|
|
|
+}
|
|
|
+
|
|
|
+// 根据点击率获取标签类型
|
|
|
+const getCtrTagType = (ctr) => {
|
|
|
+ if (ctr === null || ctr === undefined) return 'info'
|
|
|
+ if (ctr >= 0.05) return 'success'
|
|
|
+ if (ctr >= 0.02) return 'warning'
|
|
|
+ return 'danger'
|
|
|
+}
|
|
|
+
|
|
|
+// 格式化花费(分转元)
|
|
|
+const formatCost = (cost) => {
|
|
|
+ if (cost === null || cost === undefined) return '-'
|
|
|
+ return '¥' + (cost / 100).toFixed(2)
|
|
|
+}
|
|
|
</script>
|
|
|
|
|
|
<style scoped>
|