Compare commits
No commits in common. "83dedd9e1b5fa2da6f13edcded3a223275755dac" and "5e42eb9930b4212c996bb97afe3fde0acfc52b4a" have entirely different histories.
83dedd9e1b
...
5e42eb9930
@ -53,7 +53,6 @@
|
||||
"@vueuse/core": "^13.3.0",
|
||||
"alova": "^3.3.2",
|
||||
"colord": "^2.9.3",
|
||||
"echarts": "6.0.0",
|
||||
"pinia": "^3.0.3",
|
||||
"pinia-plugin-persistedstate": "^4.3.0",
|
||||
"radash": "^12.1.0",
|
||||
|
||||
@ -4,7 +4,6 @@
|
||||
:locale="naiveLocale.locale" :date-locale="naiveLocale.dateLocale" :theme-overrides="appStore.theme"
|
||||
>
|
||||
<naive-provider>
|
||||
<IconPreloader />
|
||||
<router-view />
|
||||
</naive-provider>
|
||||
</n-config-provider>
|
||||
|
||||
@ -1,24 +0,0 @@
|
||||
<template>
|
||||
<!-- 图标预加载组件 - 确保所有图标都被注册 -->
|
||||
<div style="display: none;">
|
||||
<icon-park-outline:palace />
|
||||
<icon-park-outline:peoples />
|
||||
<icon-park-outline:user />
|
||||
<icon-park-outline:calendar />
|
||||
<icon-park-outline:file-text />
|
||||
<icon-park-outline:comment />
|
||||
<icon-park-outline:like />
|
||||
<icon-park-outline:star />
|
||||
<icon-park-outline:search />
|
||||
<icon-park-outline:refresh />
|
||||
<icon-park-outline:check />
|
||||
<icon-park-outline:close />
|
||||
<icon-park-outline:delete />
|
||||
<icon-park-outline:preview-open />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
// 这个组件的唯一目的是让 unplugin-vue-components 扫描并注册这些图标
|
||||
// 组件本身不会被显示,只是用于触发图标组件的注册
|
||||
</script>
|
||||
@ -1,68 +0,0 @@
|
||||
import { request } from '../../../http'
|
||||
import type {
|
||||
CommentAdminBo,
|
||||
CommentAdminDetailVo,
|
||||
CommentAdminQueryBo,
|
||||
PageCommentAdminVo,
|
||||
} from './types'
|
||||
|
||||
// 重新导出类型供外部使用
|
||||
export type {
|
||||
CommentAdminBo,
|
||||
CommentAdminDetailVo,
|
||||
CommentAdminQueryBo,
|
||||
CommentAdminSearchForm,
|
||||
CommentAdminVo,
|
||||
PageCommentAdminVo,
|
||||
} from './types'
|
||||
|
||||
/**
|
||||
* 分页查询评论列表
|
||||
*/
|
||||
export function getCommentAdminList(params: CommentAdminQueryBo) {
|
||||
return request.Get<Service.ResponseResult<PageCommentAdminVo>>('/coder/admin/comment/listPage', { params })
|
||||
}
|
||||
|
||||
/**
|
||||
* 查看评论详情
|
||||
*/
|
||||
export function getCommentAdminDetail(id: string) {
|
||||
return request.Get<Service.ResponseResult<CommentAdminDetailVo>>(`/coder/admin/comment/detail/${id}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 审核评论
|
||||
*/
|
||||
export function auditComment(data: CommentAdminBo) {
|
||||
return request.Put<Service.ResponseResult<boolean>>('/coder/admin/comment/audit', data)
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量审核评论
|
||||
*/
|
||||
export function batchAuditComments(ids: string[], status: number) {
|
||||
return request.Put<Service.ResponseResult<boolean>>('/coder/admin/comment/batchAudit', ids, {
|
||||
params: { status },
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除评论
|
||||
*/
|
||||
export function deleteComment(id: string) {
|
||||
return request.Delete<Service.ResponseResult<boolean>>(`/coder/admin/comment/delete/${id}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除评论
|
||||
*/
|
||||
export function batchDeleteComments(ids: string[]) {
|
||||
return request.Delete<Service.ResponseResult<boolean>>('/coder/admin/comment/batchDelete', { data: ids })
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取待审核评论数量
|
||||
*/
|
||||
export function getPendingCommentCount() {
|
||||
return request.Get<Service.ResponseResult<number>>('/coder/admin/comment/pendingCount')
|
||||
}
|
||||
@ -1,89 +0,0 @@
|
||||
/**
|
||||
* 评论管理查询参数类型
|
||||
*/
|
||||
export interface CommentAdminQueryBo {
|
||||
pageNum?: number
|
||||
pageSize?: number
|
||||
userId?: string
|
||||
targetType?: string
|
||||
targetId?: string
|
||||
content?: string
|
||||
status?: number
|
||||
startTime?: string
|
||||
endTime?: string
|
||||
userKeyword?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 评论管理列表VO
|
||||
*/
|
||||
export interface CommentAdminVo {
|
||||
id: string
|
||||
userId: string
|
||||
username: string
|
||||
nickname: string
|
||||
avatar?: string
|
||||
targetType: string
|
||||
targetId: string
|
||||
targetTitle: string
|
||||
content: string
|
||||
rating?: number
|
||||
parentId: string
|
||||
likeCount: number
|
||||
status: number
|
||||
createTime: string
|
||||
updateTime: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 评论管理详情VO
|
||||
*/
|
||||
export interface CommentAdminDetailVo {
|
||||
id: string
|
||||
userId: string
|
||||
username: string
|
||||
nickname: string
|
||||
avatar?: string
|
||||
targetType: string
|
||||
targetId: string
|
||||
targetTitle: string
|
||||
content: string
|
||||
rating?: number
|
||||
parentId: string
|
||||
likeCount: number
|
||||
status: number
|
||||
createTime: string
|
||||
updateTime: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 评论管理BO(审核)
|
||||
*/
|
||||
export interface CommentAdminBo {
|
||||
id: string
|
||||
status: number
|
||||
remark?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页结果类型
|
||||
*/
|
||||
export interface PageCommentAdminVo {
|
||||
records: CommentAdminVo[]
|
||||
total: number
|
||||
size: number
|
||||
current: number
|
||||
pages: number
|
||||
}
|
||||
|
||||
/**
|
||||
* 评论搜索表单类型
|
||||
*/
|
||||
export interface CommentAdminSearchForm {
|
||||
userId?: string
|
||||
targetType?: string | ''
|
||||
targetId?: string
|
||||
content?: string | ''
|
||||
status?: number | null
|
||||
userKeyword?: string | ''
|
||||
}
|
||||
@ -1,77 +0,0 @@
|
||||
import { request } from '../../../http'
|
||||
import type {
|
||||
EventAdminBo,
|
||||
EventAdminDetailVo,
|
||||
EventAdminQueryBo,
|
||||
PageEventAdminVo,
|
||||
} from './types'
|
||||
|
||||
// 重新导出类型供外部使用
|
||||
export type {
|
||||
EventAdminBo,
|
||||
EventAdminDetailVo,
|
||||
EventAdminQueryBo,
|
||||
EventAdminSearchForm,
|
||||
EventAdminVo,
|
||||
PageEventAdminVo,
|
||||
} from './types'
|
||||
|
||||
/**
|
||||
* 分页查询活动列表
|
||||
*/
|
||||
export function getEventAdminList(params: EventAdminQueryBo) {
|
||||
return request.Get<Service.ResponseResult<PageEventAdminVo>>('/coder/admin/event/listPage', { params })
|
||||
}
|
||||
|
||||
/**
|
||||
* 查看活动详情
|
||||
*/
|
||||
export function getEventAdminDetail(id: string) {
|
||||
return request.Get<Service.ResponseResult<EventAdminDetailVo>>(`/coder/admin/event/detail/${id}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增活动
|
||||
*/
|
||||
export function addEventAdmin(data: EventAdminBo) {
|
||||
return request.Post<Service.ResponseResult<string>>('/coder/admin/event/add', data)
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑活动
|
||||
*/
|
||||
export function updateEventAdmin(data: EventAdminBo) {
|
||||
return request.Put<Service.ResponseResult<boolean>>('/coder/admin/event/edit', data)
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除活动
|
||||
*/
|
||||
export function deleteEventAdmin(id: string) {
|
||||
return request.Delete<Service.ResponseResult<boolean>>(`/coder/admin/event/delete/${id}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除活动
|
||||
*/
|
||||
export function batchDeleteEventAdmin(ids: string[]) {
|
||||
return request.Delete<Service.ResponseResult<boolean>>('/coder/admin/event/batchDelete', { data: ids })
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改发布状态
|
||||
*/
|
||||
export function changeEventPublishStatus(id: string, publishStatus: number) {
|
||||
return request.Put<Service.ResponseResult<boolean>>('/coder/admin/event/changePublishStatus', null, {
|
||||
params: { id, publishStatus },
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改活动状态
|
||||
*/
|
||||
export function changeEventStatus(id: string, status: string) {
|
||||
return request.Put<Service.ResponseResult<boolean>>('/coder/admin/event/changeEventStatus', null, {
|
||||
params: { id, status },
|
||||
})
|
||||
}
|
||||
@ -1,106 +0,0 @@
|
||||
/**
|
||||
* 活动管理查询参数类型
|
||||
*/
|
||||
export interface EventAdminQueryBo {
|
||||
pageNum?: number
|
||||
pageSize?: number
|
||||
title?: string
|
||||
location?: string
|
||||
status?: string
|
||||
keyword?: string
|
||||
publishStatus?: number
|
||||
startTimeBegin?: string
|
||||
startTimeEnd?: string
|
||||
sortField?: string
|
||||
sortOrder?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 活动管理列表VO
|
||||
*/
|
||||
export interface EventAdminVo {
|
||||
id: string
|
||||
title: string
|
||||
summary?: string
|
||||
coverImage?: string
|
||||
location?: string
|
||||
startTime?: string
|
||||
endTime?: string
|
||||
maxParticipants?: number
|
||||
currentParticipants?: number
|
||||
registrationStart?: string
|
||||
registrationEnd?: string
|
||||
status: string
|
||||
viewCount?: number
|
||||
publishStatus: number
|
||||
createBy?: string
|
||||
createTime?: string
|
||||
updateBy?: string
|
||||
updateTime?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 活动管理详情VO
|
||||
*/
|
||||
export interface EventAdminDetailVo {
|
||||
id: string
|
||||
title: string
|
||||
summary?: string
|
||||
content?: string
|
||||
coverImage?: string
|
||||
location?: string
|
||||
startTime?: string
|
||||
endTime?: string
|
||||
maxParticipants?: number
|
||||
currentParticipants?: number
|
||||
registrationStart?: string
|
||||
registrationEnd?: string
|
||||
status: string
|
||||
viewCount?: number
|
||||
publishStatus: number
|
||||
createBy?: string
|
||||
createTime?: string
|
||||
updateBy?: string
|
||||
updateTime?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 活动管理BO(新增/编辑)
|
||||
*/
|
||||
export interface EventAdminBo {
|
||||
id?: string
|
||||
title: string
|
||||
summary?: string
|
||||
content?: string
|
||||
coverImage?: string
|
||||
location?: string
|
||||
startTime?: string
|
||||
endTime?: string
|
||||
maxParticipants?: number
|
||||
registrationStart?: string
|
||||
registrationEnd?: string
|
||||
status?: string
|
||||
publishStatus?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页结果类型
|
||||
*/
|
||||
export interface PageEventAdminVo {
|
||||
records: EventAdminVo[]
|
||||
total: number
|
||||
size: number
|
||||
current: number
|
||||
pages: number
|
||||
}
|
||||
|
||||
/**
|
||||
* 活动搜索表单类型
|
||||
*/
|
||||
export interface EventAdminSearchForm {
|
||||
title?: string
|
||||
location?: string
|
||||
status?: string
|
||||
keyword?: string
|
||||
publishStatus?: number
|
||||
}
|
||||
@ -1,77 +0,0 @@
|
||||
import { request } from '../../../http'
|
||||
import type {
|
||||
InheritorBo,
|
||||
InheritorDetailVo,
|
||||
InheritorQueryBo,
|
||||
PageInheritorVo,
|
||||
} from './types'
|
||||
|
||||
// 重新导出类型供外部使用
|
||||
export type {
|
||||
InheritorBo,
|
||||
InheritorDetailVo,
|
||||
InheritorQueryBo,
|
||||
InheritorSearchForm,
|
||||
InheritorVo,
|
||||
PageInheritorVo,
|
||||
} from './types'
|
||||
|
||||
/**
|
||||
* 分页查询传承人列表
|
||||
*/
|
||||
export function getInheritorList(params: InheritorQueryBo) {
|
||||
return request.Get<Service.ResponseResult<PageInheritorVo>>('/coder/admin/inheritor/listPage', { params })
|
||||
}
|
||||
|
||||
/**
|
||||
* 查看传承人详情
|
||||
*/
|
||||
export function getInheritorDetail(id: string) {
|
||||
return request.Get<Service.ResponseResult<InheritorDetailVo>>(`/coder/admin/inheritor/detail/${id}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增传承人
|
||||
*/
|
||||
export function addInheritor(data: InheritorBo) {
|
||||
return request.Post<Service.ResponseResult<string>>('/coder/admin/inheritor/add', data)
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑传承人
|
||||
*/
|
||||
export function updateInheritor(data: InheritorBo) {
|
||||
return request.Put<Service.ResponseResult<boolean>>('/coder/admin/inheritor/edit', data)
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除传承人
|
||||
*/
|
||||
export function deleteInheritor(id: string) {
|
||||
return request.Delete<Service.ResponseResult<boolean>>(`/coder/admin/inheritor/delete/${id}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除传承人
|
||||
*/
|
||||
export function batchDeleteInheritors(ids: string[]) {
|
||||
return request.Delete<Service.ResponseResult<boolean>>('/coder/admin/inheritor/batchDelete', { data: ids })
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改发布状态
|
||||
*/
|
||||
export function changePublishStatus(id: string, publishStatus: number) {
|
||||
return request.Put<Service.ResponseResult<boolean>>('/coder/admin/inheritor/changePublishStatus', null, {
|
||||
params: { id, publishStatus },
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置精选状态
|
||||
*/
|
||||
export function setFeatured(id: string, isFeatured: number) {
|
||||
return request.Put<Service.ResponseResult<boolean>>('/coder/admin/inheritor/setFeatured', null, {
|
||||
params: { id, isFeatured },
|
||||
})
|
||||
}
|
||||
@ -1,133 +0,0 @@
|
||||
/**
|
||||
* 传承人管理查询参数类型
|
||||
*/
|
||||
export interface InheritorQueryBo {
|
||||
pageNum?: number
|
||||
pageSize?: number
|
||||
name?: string
|
||||
nameEn?: string
|
||||
gender?: number
|
||||
heritageId?: string
|
||||
heritageName?: string
|
||||
level?: string
|
||||
province?: string
|
||||
city?: string
|
||||
keyword?: string
|
||||
publishStatus?: number
|
||||
isFeatured?: number
|
||||
sortField?: string
|
||||
sortOrder?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 传承人管理列表VO
|
||||
*/
|
||||
export interface InheritorVo {
|
||||
id: string
|
||||
name: string
|
||||
nameEn?: string
|
||||
gender?: number
|
||||
birthYear?: number
|
||||
avatar?: string
|
||||
heritageId?: string
|
||||
heritageName?: string
|
||||
level?: string
|
||||
province?: string
|
||||
city?: string
|
||||
introduction?: string
|
||||
viewCount?: number
|
||||
likeCount?: number
|
||||
isFeatured: number
|
||||
sortOrder?: number
|
||||
publishStatus: number
|
||||
createBy?: string
|
||||
createTime?: string
|
||||
updateBy?: string
|
||||
updateTime?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 传承人管理详情VO
|
||||
*/
|
||||
export interface InheritorDetailVo {
|
||||
id: string
|
||||
name: string
|
||||
nameEn?: string
|
||||
gender?: number
|
||||
birthYear?: number
|
||||
avatar?: string
|
||||
heritageId?: string
|
||||
heritageName?: string
|
||||
level?: string
|
||||
province?: string
|
||||
city?: string
|
||||
introduction?: string
|
||||
story?: string
|
||||
achievements?: string
|
||||
works?: string
|
||||
images?: string
|
||||
videoUrl?: string
|
||||
viewCount?: number
|
||||
likeCount?: number
|
||||
isFeatured: number
|
||||
sortOrder?: number
|
||||
publishStatus: number
|
||||
createBy?: string
|
||||
createTime?: string
|
||||
updateBy?: string
|
||||
updateTime?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 传承人管理BO(新增/编辑)
|
||||
*/
|
||||
export interface InheritorBo {
|
||||
id?: string
|
||||
name: string
|
||||
nameEn?: string
|
||||
gender?: number
|
||||
birthYear?: number
|
||||
avatar?: string
|
||||
heritageId?: string
|
||||
heritageName?: string
|
||||
level?: string
|
||||
province?: string
|
||||
city?: string
|
||||
introduction?: string
|
||||
story?: string
|
||||
achievements?: string
|
||||
works?: string
|
||||
images?: string
|
||||
videoUrl?: string
|
||||
isFeatured?: number
|
||||
sortOrder?: number
|
||||
publishStatus?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页结果类型
|
||||
*/
|
||||
export interface PageInheritorVo {
|
||||
records: InheritorVo[]
|
||||
total: number
|
||||
size: number
|
||||
current: number
|
||||
pages: number
|
||||
}
|
||||
|
||||
/**
|
||||
* 传承人搜索表单类型
|
||||
*/
|
||||
export interface InheritorSearchForm {
|
||||
name?: string
|
||||
nameEn?: string
|
||||
gender?: number
|
||||
heritageId?: string
|
||||
heritageName?: string
|
||||
level?: string
|
||||
province?: string
|
||||
city?: string
|
||||
keyword?: string
|
||||
publishStatus?: number
|
||||
isFeatured?: number
|
||||
}
|
||||
@ -1,77 +0,0 @@
|
||||
import { request } from '../../../http'
|
||||
import type {
|
||||
HeritageItemBo,
|
||||
HeritageItemDetailVo,
|
||||
HeritageItemQueryBo,
|
||||
PageHeritageItemVo,
|
||||
} from './types'
|
||||
|
||||
// 重新导出类型供外部使用
|
||||
export type {
|
||||
HeritageItemBo,
|
||||
HeritageItemDetailVo,
|
||||
HeritageItemQueryBo,
|
||||
HeritageItemSearchForm,
|
||||
HeritageItemVo,
|
||||
PageHeritageItemVo,
|
||||
} from './types'
|
||||
|
||||
/**
|
||||
* 分页查询非遗项目列表
|
||||
*/
|
||||
export function getHeritageItemList(params: HeritageItemQueryBo) {
|
||||
return request.Get<Service.ResponseResult<PageHeritageItemVo>>('/coder/admin/heritage/listPage', { params })
|
||||
}
|
||||
|
||||
/**
|
||||
* 查看非遗项目详情
|
||||
*/
|
||||
export function getHeritageItemDetail(id: string) {
|
||||
return request.Get<Service.ResponseResult<HeritageItemDetailVo>>(`/coder/admin/heritage/detail/${id}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增非遗项目
|
||||
*/
|
||||
export function addHeritageItem(data: HeritageItemBo) {
|
||||
return request.Post<Service.ResponseResult<string>>('/coder/admin/heritage/add', data)
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑非遗项目
|
||||
*/
|
||||
export function updateHeritageItem(data: HeritageItemBo) {
|
||||
return request.Put<Service.ResponseResult<boolean>>('/coder/admin/heritage/edit', data)
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除非遗项目
|
||||
*/
|
||||
export function deleteHeritageItem(id: string) {
|
||||
return request.Delete<Service.ResponseResult<boolean>>(`/coder/admin/heritage/delete/${id}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除非遗项目
|
||||
*/
|
||||
export function batchDeleteHeritageItems(ids: string[]) {
|
||||
return request.Delete<Service.ResponseResult<boolean>>('/coder/admin/heritage/batchDelete', { data: ids })
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改发布状态
|
||||
*/
|
||||
export function changePublishStatus(id: string, publishStatus: number) {
|
||||
return request.Put<Service.ResponseResult<boolean>>('/coder/admin/heritage/changePublishStatus', null, {
|
||||
params: { id, publishStatus },
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置精选状态
|
||||
*/
|
||||
export function setFeatured(id: string, isFeatured: number) {
|
||||
return request.Put<Service.ResponseResult<boolean>>('/coder/admin/heritage/setFeatured', null, {
|
||||
params: { id, isFeatured },
|
||||
})
|
||||
}
|
||||
@ -1,134 +0,0 @@
|
||||
/**
|
||||
* 非遗项目管理查询参数类型
|
||||
*/
|
||||
export interface HeritageItemQueryBo {
|
||||
pageNum?: number
|
||||
pageSize?: number
|
||||
name?: string
|
||||
nameEn?: string
|
||||
category?: string
|
||||
level?: string
|
||||
province?: string
|
||||
city?: string
|
||||
status?: string
|
||||
tag?: string
|
||||
keyword?: string
|
||||
publishStatus?: number
|
||||
isFeatured?: number
|
||||
sortField?: string
|
||||
sortOrder?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 非遗项目管理列表VO
|
||||
*/
|
||||
export interface HeritageItemVo {
|
||||
id: string
|
||||
name: string
|
||||
nameEn?: string
|
||||
category: string
|
||||
level: string
|
||||
province?: string
|
||||
city?: string
|
||||
description?: string
|
||||
coverImage?: string
|
||||
tags?: string
|
||||
status: string
|
||||
viewCount?: number
|
||||
likeCount?: number
|
||||
favoriteCount?: number
|
||||
commentCount?: number
|
||||
isFeatured: number
|
||||
sortOrder?: number
|
||||
publishStatus: number
|
||||
createBy?: string
|
||||
createTime?: string
|
||||
updateBy?: string
|
||||
updateTime?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 非遗项目管理详情VO
|
||||
*/
|
||||
export interface HeritageItemDetailVo {
|
||||
id: string
|
||||
name: string
|
||||
nameEn?: string
|
||||
category: string
|
||||
level: string
|
||||
province?: string
|
||||
city?: string
|
||||
description?: string
|
||||
history?: string
|
||||
skills?: string
|
||||
significance?: string
|
||||
coverImage?: string
|
||||
images?: string
|
||||
videoUrl?: string
|
||||
tags?: string
|
||||
status: string
|
||||
viewCount?: number
|
||||
likeCount?: number
|
||||
favoriteCount?: number
|
||||
commentCount?: number
|
||||
isFeatured: number
|
||||
sortOrder?: number
|
||||
publishStatus: number
|
||||
createBy?: string
|
||||
createTime?: string
|
||||
updateBy?: string
|
||||
updateTime?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 非遗项目管理BO(新增/编辑)
|
||||
*/
|
||||
export interface HeritageItemBo {
|
||||
id?: string
|
||||
name: string
|
||||
nameEn?: string
|
||||
category: string
|
||||
level: string
|
||||
province?: string
|
||||
city?: string
|
||||
description?: string
|
||||
history?: string
|
||||
skills?: string
|
||||
significance?: string
|
||||
coverImage?: string
|
||||
images?: string
|
||||
videoUrl?: string
|
||||
tags?: string
|
||||
status?: string
|
||||
isFeatured?: number
|
||||
sortOrder?: number
|
||||
publishStatus?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页结果类型
|
||||
*/
|
||||
export interface PageHeritageItemVo {
|
||||
records: HeritageItemVo[]
|
||||
total: number
|
||||
size: number
|
||||
current: number
|
||||
pages: number
|
||||
}
|
||||
|
||||
/**
|
||||
* 非遗项目搜索表单类型
|
||||
*/
|
||||
export interface HeritageItemSearchForm {
|
||||
name?: string
|
||||
nameEn?: string
|
||||
category?: string
|
||||
level?: string
|
||||
province?: string
|
||||
city?: string
|
||||
status?: string
|
||||
tag?: string
|
||||
keyword?: string
|
||||
publishStatus?: number
|
||||
isFeatured?: number
|
||||
}
|
||||
@ -1,77 +0,0 @@
|
||||
import { request } from '../../../http'
|
||||
import type {
|
||||
NewsBo,
|
||||
NewsDetailVo,
|
||||
NewsQueryBo,
|
||||
PageNewsVo,
|
||||
} from './types'
|
||||
|
||||
// 重新导出类型供外部使用
|
||||
export type {
|
||||
NewsBo,
|
||||
NewsDetailVo,
|
||||
NewsQueryBo,
|
||||
NewsSearchForm,
|
||||
NewsVo,
|
||||
PageNewsVo,
|
||||
} from './types'
|
||||
|
||||
/**
|
||||
* 分页查询新闻资讯列表
|
||||
*/
|
||||
export function getNewsList(params: NewsQueryBo) {
|
||||
return request.Get<Service.ResponseResult<PageNewsVo>>('/coder/admin/news/listPage', { params })
|
||||
}
|
||||
|
||||
/**
|
||||
* 查看新闻资讯详情
|
||||
*/
|
||||
export function getNewsDetail(id: string) {
|
||||
return request.Get<Service.ResponseResult<NewsDetailVo>>(`/coder/admin/news/detail/${id}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增新闻资讯
|
||||
*/
|
||||
export function addNews(data: NewsBo) {
|
||||
return request.Post<Service.ResponseResult<string>>('/coder/admin/news/add', data)
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑新闻资讯
|
||||
*/
|
||||
export function updateNews(data: NewsBo) {
|
||||
return request.Put<Service.ResponseResult<boolean>>('/coder/admin/news/edit', data)
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除新闻资讯
|
||||
*/
|
||||
export function deleteNews(id: string) {
|
||||
return request.Delete<Service.ResponseResult<boolean>>(`/coder/admin/news/delete/${id}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除新闻资讯
|
||||
*/
|
||||
export function batchDeleteNews(ids: string[]) {
|
||||
return request.Delete<Service.ResponseResult<boolean>>('/coder/admin/news/batchDelete', { data: ids })
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改发布状态
|
||||
*/
|
||||
export function changePublishStatus(id: string, publishStatus: number) {
|
||||
return request.Put<Service.ResponseResult<boolean>>('/coder/admin/news/changePublishStatus', null, {
|
||||
params: { id, publishStatus },
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置置顶状态
|
||||
*/
|
||||
export function setTop(id: string, isTop: number) {
|
||||
return request.Put<Service.ResponseResult<boolean>>('/coder/admin/news/setTop', null, {
|
||||
params: { id, isTop },
|
||||
})
|
||||
}
|
||||
@ -1,111 +0,0 @@
|
||||
/**
|
||||
* 新闻资讯管理查询参数类型
|
||||
*/
|
||||
export interface NewsQueryBo {
|
||||
pageNum?: number
|
||||
pageSize?: number
|
||||
title?: string
|
||||
author?: string
|
||||
source?: string
|
||||
category?: string
|
||||
tag?: string
|
||||
keyword?: string
|
||||
isTop?: number
|
||||
publishStatus?: number
|
||||
publishTimeStart?: string
|
||||
publishTimeEnd?: string
|
||||
sortField?: string
|
||||
sortOrder?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 新闻资讯管理列表VO
|
||||
*/
|
||||
export interface NewsVo {
|
||||
id: string
|
||||
title: string
|
||||
summary?: string
|
||||
coverImage?: string
|
||||
author?: string
|
||||
source?: string
|
||||
category?: string
|
||||
tags?: string
|
||||
viewCount?: number
|
||||
likeCount?: number
|
||||
isTop: number
|
||||
publishTime?: string
|
||||
publishStatus: number
|
||||
createBy?: string
|
||||
createTime?: string
|
||||
updateBy?: string
|
||||
updateTime?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 新闻资讯管理详情VO
|
||||
*/
|
||||
export interface NewsDetailVo {
|
||||
id: string
|
||||
title: string
|
||||
summary?: string
|
||||
content?: string
|
||||
coverImage?: string
|
||||
author?: string
|
||||
source?: string
|
||||
category?: string
|
||||
tags?: string
|
||||
viewCount?: number
|
||||
likeCount?: number
|
||||
isTop: number
|
||||
publishTime?: string
|
||||
publishStatus: number
|
||||
createBy?: string
|
||||
createTime?: string
|
||||
updateBy?: string
|
||||
updateTime?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 新闻资讯管理BO(新增/编辑)
|
||||
*/
|
||||
export interface NewsBo {
|
||||
id?: string
|
||||
title: string
|
||||
summary?: string
|
||||
content?: string
|
||||
coverImage?: string
|
||||
author?: string
|
||||
source?: string
|
||||
category?: string
|
||||
tags?: string
|
||||
isTop?: number
|
||||
publishTime?: string
|
||||
publishStatus?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页结果类型
|
||||
*/
|
||||
export interface PageNewsVo {
|
||||
records: NewsVo[]
|
||||
total: number
|
||||
size: number
|
||||
current: number
|
||||
pages: number
|
||||
}
|
||||
|
||||
/**
|
||||
* 新闻资讯搜索表单类型
|
||||
*/
|
||||
export interface NewsSearchForm {
|
||||
title?: string
|
||||
author?: string
|
||||
source?: string
|
||||
category?: string
|
||||
tag?: string
|
||||
keyword?: string
|
||||
isTop?: number
|
||||
publishStatus?: number
|
||||
publishTimeStart?: string
|
||||
publishTimeEnd?: string
|
||||
}
|
||||
@ -1,56 +0,0 @@
|
||||
import { request } from '../../../http'
|
||||
import type {
|
||||
RankingVo,
|
||||
StatisticsVo,
|
||||
TrendVo,
|
||||
} from './types'
|
||||
|
||||
// 重新导出类型供外部使用
|
||||
export type {
|
||||
RankingVo,
|
||||
StatisticsVo,
|
||||
TrendVo,
|
||||
} from './types'
|
||||
|
||||
/**
|
||||
* 获取核心统计数据
|
||||
*/
|
||||
export function getStatistics() {
|
||||
return request.Get<Service.ResponseResult<StatisticsVo>>('/coder/admin/statistics/core')
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户增长趋势
|
||||
*/
|
||||
export function getUserTrend(days: number = 7) {
|
||||
return request.Get<Service.ResponseResult<TrendVo[]>>('/coder/admin/statistics/userTrend', {
|
||||
params: { days },
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取内容发布趋势
|
||||
*/
|
||||
export function getContentTrend(days: number = 7) {
|
||||
return request.Get<Service.ResponseResult<TrendVo[]>>('/coder/admin/statistics/contentTrend', {
|
||||
params: { days },
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取热门非遗项目排行榜
|
||||
*/
|
||||
export function getHeritageRanking(type: string = 'view', limit: number = 10) {
|
||||
return request.Get<Service.ResponseResult<RankingVo[]>>('/coder/admin/statistics/heritageRanking', {
|
||||
params: { type, limit },
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取活跃用户排行榜
|
||||
*/
|
||||
export function getActiveUserRanking(limit: number = 10) {
|
||||
return request.Get<Service.ResponseResult<RankingVo[]>>('/coder/admin/statistics/activeUserRanking', {
|
||||
params: { limit },
|
||||
})
|
||||
}
|
||||
@ -1,66 +0,0 @@
|
||||
/**
|
||||
* 统计数据VO
|
||||
*/
|
||||
export interface StatisticsVo {
|
||||
// 非遗项目统计
|
||||
heritageTotal: number
|
||||
heritageTodayCount: number
|
||||
|
||||
// 传承人统计
|
||||
inheritorTotal: number
|
||||
inheritorTodayCount: number
|
||||
|
||||
// 用户统计
|
||||
userTotal: number
|
||||
userTodayCount: number
|
||||
|
||||
// 活动统计
|
||||
eventTotal: number
|
||||
eventTodayCount: number
|
||||
|
||||
// 新闻统计
|
||||
newsTotal: number
|
||||
newsTodayCount: number
|
||||
|
||||
// 评论统计
|
||||
commentTotal: number
|
||||
commentTodayCount: number
|
||||
commentPendingCount: number
|
||||
|
||||
// 点赞统计
|
||||
likeTotal: number
|
||||
likeTodayCount: number
|
||||
|
||||
// 收藏统计
|
||||
favoriteTotal: number
|
||||
favoriteTodayCount: number
|
||||
|
||||
// 浏览统计
|
||||
viewTotal: number
|
||||
viewTodayCount: number
|
||||
|
||||
// 活动报名统计
|
||||
registrationTotal: number
|
||||
registrationTodayCount: number
|
||||
}
|
||||
|
||||
/**
|
||||
* 趋势数据VO
|
||||
*/
|
||||
export interface TrendVo {
|
||||
date: string
|
||||
value: number
|
||||
}
|
||||
|
||||
/**
|
||||
* 排行榜数据VO
|
||||
*/
|
||||
export interface RankingVo {
|
||||
rank: number
|
||||
id: string
|
||||
title: string
|
||||
type: string
|
||||
coverImage?: string
|
||||
value: number
|
||||
valueType: string
|
||||
}
|
||||
@ -1,77 +0,0 @@
|
||||
import { request } from '../../../http'
|
||||
import type {
|
||||
PageUserAdminVo,
|
||||
UserAdminBo,
|
||||
UserAdminDetailVo,
|
||||
UserAdminQueryBo,
|
||||
} from './types'
|
||||
|
||||
// 重新导出类型供外部使用
|
||||
export type {
|
||||
PageUserAdminVo,
|
||||
UserAdminBo,
|
||||
UserAdminDetailVo,
|
||||
UserAdminQueryBo,
|
||||
UserAdminSearchForm,
|
||||
UserAdminVo,
|
||||
} from './types'
|
||||
|
||||
/**
|
||||
* 分页查询前台用户列表
|
||||
*/
|
||||
export function getUserAdminList(params: UserAdminQueryBo) {
|
||||
return request.Get<Service.ResponseResult<PageUserAdminVo>>('/coder/admin/user/listPage', { params })
|
||||
}
|
||||
|
||||
/**
|
||||
* 查看前台用户详情
|
||||
*/
|
||||
export function getUserAdminDetail(id: string) {
|
||||
return request.Get<Service.ResponseResult<UserAdminDetailVo>>(`/coder/admin/user/detail/${id}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增前台用户
|
||||
*/
|
||||
export function addUserAdmin(data: UserAdminBo) {
|
||||
return request.Post<Service.ResponseResult<string>>('/coder/admin/user/add', data)
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑前台用户
|
||||
*/
|
||||
export function updateUserAdmin(data: UserAdminBo) {
|
||||
return request.Put<Service.ResponseResult<boolean>>('/coder/admin/user/edit', data)
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除前台用户
|
||||
*/
|
||||
export function deleteUserAdmin(id: string) {
|
||||
return request.Delete<Service.ResponseResult<boolean>>(`/coder/admin/user/delete/${id}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除前台用户
|
||||
*/
|
||||
export function batchDeleteUserAdmin(ids: string[]) {
|
||||
return request.Delete<Service.ResponseResult<boolean>>('/coder/admin/user/batchDelete', { data: ids })
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改用户状态
|
||||
*/
|
||||
export function changeUserStatus(id: string, status: number) {
|
||||
return request.Put<Service.ResponseResult<boolean>>('/coder/admin/user/changeStatus', null, {
|
||||
params: { id, status },
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置用户密码
|
||||
*/
|
||||
export function resetUserPassword(id: string, newPassword: string) {
|
||||
return request.Put<Service.ResponseResult<boolean>>('/coder/admin/user/resetPassword', null, {
|
||||
params: { id, newPassword },
|
||||
})
|
||||
}
|
||||
@ -1,109 +0,0 @@
|
||||
/**
|
||||
* 前台用户管理查询参数类型
|
||||
*/
|
||||
export interface UserAdminQueryBo {
|
||||
pageNum?: number
|
||||
pageSize?: number
|
||||
username?: string
|
||||
nickname?: string
|
||||
phone?: string
|
||||
email?: string
|
||||
keyword?: string
|
||||
status?: number
|
||||
gender?: number
|
||||
province?: string
|
||||
city?: string
|
||||
createTimeBegin?: string
|
||||
createTimeEnd?: string
|
||||
sortField?: string
|
||||
sortOrder?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 前台用户管理列表VO
|
||||
*/
|
||||
export interface UserAdminVo {
|
||||
id: string
|
||||
username: string
|
||||
nickname?: string
|
||||
avatar?: string
|
||||
email?: string
|
||||
phone?: string
|
||||
gender?: number
|
||||
birthday?: string
|
||||
province?: string
|
||||
city?: string
|
||||
status: number
|
||||
loginIp?: string
|
||||
loginTime?: string
|
||||
createTime?: string
|
||||
updateTime?: string
|
||||
remark?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 前台用户管理详情VO
|
||||
*/
|
||||
export interface UserAdminDetailVo {
|
||||
id: string
|
||||
username: string
|
||||
nickname?: string
|
||||
avatar?: string
|
||||
email?: string
|
||||
phone?: string
|
||||
gender?: number
|
||||
birthday?: string
|
||||
province?: string
|
||||
city?: string
|
||||
status: number
|
||||
loginIp?: string
|
||||
loginTime?: string
|
||||
createTime?: string
|
||||
updateTime?: string
|
||||
remark?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 前台用户管理BO(新增/编辑)
|
||||
*/
|
||||
export interface UserAdminBo {
|
||||
id?: string
|
||||
username: string
|
||||
password?: string
|
||||
nickname?: string
|
||||
avatar?: string
|
||||
email?: string
|
||||
phone?: string
|
||||
gender?: number
|
||||
birthday?: string
|
||||
province?: string
|
||||
city?: string
|
||||
status?: number
|
||||
remark?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页结果类型
|
||||
*/
|
||||
export interface PageUserAdminVo {
|
||||
records: UserAdminVo[]
|
||||
total: number
|
||||
size: number
|
||||
current: number
|
||||
pages: number
|
||||
}
|
||||
|
||||
/**
|
||||
* 前台用户搜索表单类型
|
||||
*/
|
||||
export interface UserAdminSearchForm {
|
||||
username?: string
|
||||
nickname?: string
|
||||
phone?: string
|
||||
email?: string
|
||||
keyword?: string
|
||||
status?: number
|
||||
gender?: number
|
||||
province?: string
|
||||
city?: string
|
||||
}
|
||||
@ -62,18 +62,17 @@ export function batchDeleteSysPictures(ids: number[]) {
|
||||
|
||||
// 图片上传相关API
|
||||
|
||||
// 上传图片到指定存储服务
|
||||
export function uploadPicture(file: File, folderName = 'heritage', fileParam = '2', fileSize = 2, storageType = 'minio') {
|
||||
// 上传图片
|
||||
export function uploadPicture(file: File, pictureType = '9', fileSize = 2, storageType?: string) {
|
||||
const formData = new FormData()
|
||||
formData.append('file', file)
|
||||
|
||||
// 添加存储类型参数
|
||||
// 如果指定了存储类型,添加到表单数据中
|
||||
if (storageType) {
|
||||
formData.append('storageType', storageType)
|
||||
}
|
||||
|
||||
// 使用通用文件上传接口:/coder/file/uploadFile/{fileSize}/{folderName}/{fileParam}
|
||||
return request.Post<PictureUploadResult>(`/coder/file/uploadFile/${fileSize}/${folderName}/${fileParam}`, formData)
|
||||
return request.Post<PictureUploadResult>(`/coder/file/uploadFile/${fileSize}/pictures/${pictureType}`, formData)
|
||||
}
|
||||
|
||||
// 匿名上传图片(无需登录)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,715 +0,0 @@
|
||||
<template>
|
||||
<div class="statistics-dashboard p-4 bg-gray-50 min-h-screen">
|
||||
<!-- 页面标题 -->
|
||||
<div class="mb-4">
|
||||
<h2 class="text-2xl font-semibold text-gray-800">
|
||||
统计分析
|
||||
</h2>
|
||||
<p class="text-sm text-gray-500 mt-1">
|
||||
数据概览与趋势分析
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- 核心统计卡片 -->
|
||||
<div class="statistics-cards mb-4">
|
||||
<NGrid :cols="4" :x-gap="16" :y-gap="16">
|
||||
<!-- 非遗项目统计 -->
|
||||
<NGridItem>
|
||||
<div class="stat-card bg-white rounded-lg shadow-sm border border-gray-100 p-4 hover:shadow-md transition-shadow">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex-1">
|
||||
<p class="text-sm text-gray-500 mb-1">
|
||||
非遗项目
|
||||
</p>
|
||||
<p class="text-3xl font-bold text-gray-800 mb-1">
|
||||
{{ formatNumber(statistics.heritageTotal) }}
|
||||
</p>
|
||||
<p class="text-xs text-green-600">
|
||||
今日新增 {{ statistics.heritageTodayCount }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="stat-icon bg-blue-50 rounded-full p-3">
|
||||
<NIcon size="32" color="#3b82f6">
|
||||
<icon-park-outline:palace />
|
||||
</NIcon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</NGridItem>
|
||||
|
||||
<!-- 传承人统计 -->
|
||||
<NGridItem>
|
||||
<div class="stat-card bg-white rounded-lg shadow-sm border border-gray-100 p-4 hover:shadow-md transition-shadow">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex-1">
|
||||
<p class="text-sm text-gray-500 mb-1">
|
||||
传承人
|
||||
</p>
|
||||
<p class="text-3xl font-bold text-gray-800 mb-1">
|
||||
{{ formatNumber(statistics.inheritorTotal) }}
|
||||
</p>
|
||||
<p class="text-xs text-green-600">
|
||||
今日新增 {{ statistics.inheritorTodayCount }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="stat-icon bg-purple-50 rounded-full p-3">
|
||||
<NIcon size="32" color="#9333ea">
|
||||
<icon-park-outline:peoples />
|
||||
</NIcon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</NGridItem>
|
||||
|
||||
<!-- 用户统计 -->
|
||||
<NGridItem>
|
||||
<div class="stat-card bg-white rounded-lg shadow-sm border border-gray-100 p-4 hover:shadow-md transition-shadow">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex-1">
|
||||
<p class="text-sm text-gray-500 mb-1">
|
||||
注册用户
|
||||
</p>
|
||||
<p class="text-3xl font-bold text-gray-800 mb-1">
|
||||
{{ formatNumber(statistics.userTotal) }}
|
||||
</p>
|
||||
<p class="text-xs text-green-600">
|
||||
今日新增 {{ statistics.userTodayCount }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="stat-icon bg-green-50 rounded-full p-3">
|
||||
<NIcon size="32" color="#16a34a">
|
||||
<icon-park-outline:user />
|
||||
</NIcon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</NGridItem>
|
||||
|
||||
<!-- 活动统计 -->
|
||||
<NGridItem>
|
||||
<div class="stat-card bg-white rounded-lg shadow-sm border border-gray-100 p-4 hover:shadow-md transition-shadow">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex-1">
|
||||
<p class="text-sm text-gray-500 mb-1">
|
||||
活动总数
|
||||
</p>
|
||||
<p class="text-3xl font-bold text-gray-800 mb-1">
|
||||
{{ formatNumber(statistics.eventTotal) }}
|
||||
</p>
|
||||
<p class="text-xs text-green-600">
|
||||
今日新增 {{ statistics.eventTodayCount }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="stat-icon bg-yellow-50 rounded-full p-3">
|
||||
<NIcon size="32" color="#eab308">
|
||||
<icon-park-outline:calendar />
|
||||
</NIcon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</NGridItem>
|
||||
|
||||
<!-- 新闻统计 -->
|
||||
<NGridItem>
|
||||
<div class="stat-card bg-white rounded-lg shadow-sm border border-gray-100 p-4 hover:shadow-md transition-shadow">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex-1">
|
||||
<p class="text-sm text-gray-500 mb-1">
|
||||
新闻资讯
|
||||
</p>
|
||||
<p class="text-3xl font-bold text-gray-800 mb-1">
|
||||
{{ formatNumber(statistics.newsTotal) }}
|
||||
</p>
|
||||
<p class="text-xs text-green-600">
|
||||
今日新增 {{ statistics.newsTodayCount }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="stat-icon bg-red-50 rounded-full p-3">
|
||||
<NIcon size="32" color="#dc2626">
|
||||
<icon-park-outline:file-text />
|
||||
</NIcon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</NGridItem>
|
||||
|
||||
<!-- 评论统计 -->
|
||||
<NGridItem>
|
||||
<div class="stat-card bg-white rounded-lg shadow-sm border border-gray-100 p-4 hover:shadow-md transition-shadow">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex-1">
|
||||
<p class="text-sm text-gray-500 mb-1">
|
||||
评论总数
|
||||
</p>
|
||||
<p class="text-3xl font-bold text-gray-800 mb-1">
|
||||
{{ formatNumber(statistics.commentTotal) }}
|
||||
</p>
|
||||
<p class="text-xs text-orange-600">
|
||||
待审核 {{ statistics.commentPendingCount }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="stat-icon bg-indigo-50 rounded-full p-3">
|
||||
<NIcon size="32" color="#6366f1">
|
||||
<icon-park-outline:comment />
|
||||
</NIcon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</NGridItem>
|
||||
|
||||
<!-- 点赞统计 -->
|
||||
<NGridItem>
|
||||
<div class="stat-card bg-white rounded-lg shadow-sm border border-gray-100 p-4 hover:shadow-md transition-shadow">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex-1">
|
||||
<p class="text-sm text-gray-500 mb-1">
|
||||
点赞总数
|
||||
</p>
|
||||
<p class="text-3xl font-bold text-gray-800 mb-1">
|
||||
{{ formatNumber(statistics.likeTotal) }}
|
||||
</p>
|
||||
<p class="text-xs text-green-600">
|
||||
今日新增 {{ statistics.likeTodayCount }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="stat-icon bg-pink-50 rounded-full p-3">
|
||||
<NIcon size="32" color="#ec4899">
|
||||
<icon-park-outline:like />
|
||||
</NIcon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</NGridItem>
|
||||
|
||||
<!-- 收藏统计 -->
|
||||
<NGridItem>
|
||||
<div class="stat-card bg-white rounded-lg shadow-sm border border-gray-100 p-4 hover:shadow-md transition-shadow">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex-1">
|
||||
<p class="text-sm text-gray-500 mb-1">
|
||||
收藏总数
|
||||
</p>
|
||||
<p class="text-3xl font-bold text-gray-800 mb-1">
|
||||
{{ formatNumber(statistics.favoriteTotal) }}
|
||||
</p>
|
||||
<p class="text-xs text-green-600">
|
||||
今日新增 {{ statistics.favoriteTodayCount }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="stat-icon bg-amber-50 rounded-full p-3">
|
||||
<NIcon size="32" color="#f59e0b">
|
||||
<icon-park-outline:star />
|
||||
</NIcon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</NGridItem>
|
||||
</NGrid>
|
||||
</div>
|
||||
|
||||
<!-- 趋势图表区域 -->
|
||||
<div class="charts-section mb-4">
|
||||
<NGrid :cols="2" :x-gap="16">
|
||||
<!-- 用户增长趋势 -->
|
||||
<NGridItem>
|
||||
<div class="chart-card bg-white rounded-lg shadow-sm border border-gray-100 p-4">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<h3 class="text-lg font-semibold text-gray-800">
|
||||
用户增长趋势
|
||||
</h3>
|
||||
<NRadioGroup v-model:value="userTrendDays" size="small" @update:value="loadUserTrend">
|
||||
<NRadioButton :value="7">
|
||||
近7天
|
||||
</NRadioButton>
|
||||
<NRadioButton :value="30">
|
||||
近30天
|
||||
</NRadioButton>
|
||||
</NRadioGroup>
|
||||
</div>
|
||||
<div ref="userTrendChartRef" class="chart-container" style="height: 300px;" />
|
||||
</div>
|
||||
</NGridItem>
|
||||
|
||||
<!-- 内容发布趋势 -->
|
||||
<NGridItem>
|
||||
<div class="chart-card bg-white rounded-lg shadow-sm border border-gray-100 p-4">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<h3 class="text-lg font-semibold text-gray-800">
|
||||
内容发布趋势
|
||||
</h3>
|
||||
<NRadioGroup v-model:value="contentTrendDays" size="small" @update:value="loadContentTrend">
|
||||
<NRadioButton :value="7">
|
||||
近7天
|
||||
</NRadioButton>
|
||||
<NRadioButton :value="30">
|
||||
近30天
|
||||
</NRadioButton>
|
||||
</NRadioGroup>
|
||||
</div>
|
||||
<div ref="contentTrendChartRef" class="chart-container" style="height: 300px;" />
|
||||
</div>
|
||||
</NGridItem>
|
||||
</NGrid>
|
||||
</div>
|
||||
|
||||
<!-- 排行榜区域 -->
|
||||
<div class="rankings-section">
|
||||
<NGrid :cols="2" :x-gap="16">
|
||||
<!-- 热门非遗项目排行榜 -->
|
||||
<NGridItem>
|
||||
<div class="ranking-card bg-white rounded-lg shadow-sm border border-gray-100 p-4">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<h3 class="text-lg font-semibold text-gray-800">
|
||||
热门非遗项目排行榜
|
||||
</h3>
|
||||
<NSelect
|
||||
v-model:value="heritageRankingType"
|
||||
size="small"
|
||||
style="width: 120px"
|
||||
:options="rankingTypeOptions"
|
||||
@update:value="loadHeritageRanking"
|
||||
/>
|
||||
</div>
|
||||
<div v-if="heritageRankingLoading" class="flex items-center justify-center py-12">
|
||||
<NSpin size="medium" />
|
||||
</div>
|
||||
<div v-else-if="heritageRanking.length > 0" class="ranking-list">
|
||||
<div
|
||||
v-for="(item, index) in heritageRanking"
|
||||
:key="item.id"
|
||||
class="ranking-item flex items-center py-3 border-b border-gray-100 last:border-b-0 hover:bg-gray-50 transition-colors"
|
||||
>
|
||||
<div class="ranking-number flex-shrink-0 w-8 h-8 flex items-center justify-center mr-3">
|
||||
<span
|
||||
:class="{
|
||||
'text-lg font-bold text-red-500': index === 0,
|
||||
'text-lg font-bold text-orange-500': index === 1,
|
||||
'text-lg font-bold text-yellow-500': index === 2,
|
||||
'text-sm text-gray-500': index > 2,
|
||||
}"
|
||||
>
|
||||
{{ item.rank }}
|
||||
</span>
|
||||
</div>
|
||||
<div v-if="item.coverImage" class="ranking-cover flex-shrink-0 w-12 h-12 mr-3">
|
||||
<img :src="item.coverImage" :alt="item.title" class="w-full h-full object-cover rounded">
|
||||
</div>
|
||||
<div class="ranking-info flex-1 min-w-0">
|
||||
<p class="text-sm font-medium text-gray-800 truncate">
|
||||
{{ item.title }}
|
||||
</p>
|
||||
<p class="text-xs text-gray-500 mt-1">
|
||||
<NTag type="primary" size="small">
|
||||
{{ item.type }}
|
||||
</NTag>
|
||||
</p>
|
||||
</div>
|
||||
<div class="ranking-value flex-shrink-0 ml-3 text-right">
|
||||
<p class="text-lg font-semibold text-blue-600">
|
||||
{{ formatNumber(item.value) }}
|
||||
</p>
|
||||
<p class="text-xs text-gray-500">
|
||||
{{ getRankingValueLabel(item.valueType) }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="flex items-center justify-center py-12 text-gray-400">
|
||||
<span>暂无数据</span>
|
||||
</div>
|
||||
</div>
|
||||
</NGridItem>
|
||||
|
||||
<!-- 活跃用户排行榜 -->
|
||||
<NGridItem>
|
||||
<div class="ranking-card bg-white rounded-lg shadow-sm border border-gray-100 p-4">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<h3 class="text-lg font-semibold text-gray-800">
|
||||
活跃用户排行榜
|
||||
</h3>
|
||||
</div>
|
||||
<div v-if="activeUserRankingLoading" class="flex items-center justify-center py-12">
|
||||
<NSpin size="medium" />
|
||||
</div>
|
||||
<div v-else-if="activeUserRanking.length > 0" class="ranking-list">
|
||||
<div
|
||||
v-for="(item, index) in activeUserRanking"
|
||||
:key="item.id"
|
||||
class="ranking-item flex items-center py-3 border-b border-gray-100 last:border-b-0 hover:bg-gray-50 transition-colors"
|
||||
>
|
||||
<div class="ranking-number flex-shrink-0 w-8 h-8 flex items-center justify-center mr-3">
|
||||
<span
|
||||
:class="{
|
||||
'text-lg font-bold text-red-500': index === 0,
|
||||
'text-lg font-bold text-orange-500': index === 1,
|
||||
'text-lg font-bold text-yellow-500': index === 2,
|
||||
'text-sm text-gray-500': index > 2,
|
||||
}"
|
||||
>
|
||||
{{ item.rank }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="ranking-info flex-1 min-w-0">
|
||||
<p class="text-sm font-medium text-gray-800 truncate">
|
||||
{{ item.title }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="ranking-value flex-shrink-0 ml-3 text-right">
|
||||
<p class="text-lg font-semibold text-green-600">
|
||||
{{ formatNumber(item.value) }}
|
||||
</p>
|
||||
<p class="text-xs text-gray-500">
|
||||
{{ getRankingValueLabel(item.valueType) }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="flex items-center justify-center py-12 text-gray-400">
|
||||
<span>暂无数据</span>
|
||||
</div>
|
||||
</div>
|
||||
</NGridItem>
|
||||
</NGrid>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref } from 'vue'
|
||||
import { NGrid, NGridItem, NIcon, NRadioButton, NRadioGroup, NSelect, NSpin, NTag } from 'naive-ui'
|
||||
import * as echarts from 'echarts'
|
||||
import type { ECharts } from 'echarts'
|
||||
import {
|
||||
getActiveUserRanking,
|
||||
getContentTrend,
|
||||
getHeritageRanking,
|
||||
getStatistics,
|
||||
getUserTrend,
|
||||
} from '@/service/api/heritage/statistics'
|
||||
import type {
|
||||
RankingVo,
|
||||
StatisticsVo,
|
||||
TrendVo,
|
||||
} from '@/service/api/heritage/statistics'
|
||||
import { coiMsgError } from '@/utils/coi'
|
||||
|
||||
// 核心统计数据
|
||||
const statistics = ref<StatisticsVo>({
|
||||
heritageTotal: 0,
|
||||
heritageTodayCount: 0,
|
||||
inheritorTotal: 0,
|
||||
inheritorTodayCount: 0,
|
||||
userTotal: 0,
|
||||
userTodayCount: 0,
|
||||
eventTotal: 0,
|
||||
eventTodayCount: 0,
|
||||
newsTotal: 0,
|
||||
newsTodayCount: 0,
|
||||
commentTotal: 0,
|
||||
commentTodayCount: 0,
|
||||
commentPendingCount: 0,
|
||||
likeTotal: 0,
|
||||
likeTodayCount: 0,
|
||||
favoriteTotal: 0,
|
||||
favoriteTodayCount: 0,
|
||||
viewTotal: 0,
|
||||
viewTodayCount: 0,
|
||||
registrationTotal: 0,
|
||||
registrationTodayCount: 0,
|
||||
})
|
||||
|
||||
// 趋势图表
|
||||
const userTrendChartRef = ref<HTMLDivElement>()
|
||||
const contentTrendChartRef = ref<HTMLDivElement>()
|
||||
let userTrendChart: ECharts | null = null
|
||||
let contentTrendChart: ECharts | null = null
|
||||
|
||||
const userTrendDays = ref(7)
|
||||
const contentTrendDays = ref(7)
|
||||
|
||||
// 排行榜
|
||||
const heritageRankingType = ref('view')
|
||||
const heritageRanking = ref<RankingVo[]>([])
|
||||
const heritageRankingLoading = ref(false)
|
||||
|
||||
const activeUserRanking = ref<RankingVo[]>([])
|
||||
const activeUserRankingLoading = ref(false)
|
||||
|
||||
const rankingTypeOptions = [
|
||||
{ label: '浏览量', value: 'view' },
|
||||
{ label: '收藏量', value: 'favorite' },
|
||||
]
|
||||
|
||||
// 格式化数字
|
||||
function formatNumber(num: number): string {
|
||||
if (num >= 10000) {
|
||||
return `${(num / 10000).toFixed(1)}万`
|
||||
}
|
||||
return num.toString()
|
||||
}
|
||||
|
||||
// 获取排行榜数值标签
|
||||
function getRankingValueLabel(valueType: string): string {
|
||||
const labelMap: Record<string, string> = {
|
||||
view: '浏览量',
|
||||
favorite: '收藏量',
|
||||
activity: '活跃度',
|
||||
}
|
||||
return labelMap[valueType] || valueType
|
||||
}
|
||||
|
||||
// 加载核心统计数据
|
||||
async function loadStatistics() {
|
||||
try {
|
||||
const { data, isSuccess } = await getStatistics()
|
||||
if (isSuccess && data) {
|
||||
statistics.value = data
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
coiMsgError('加载统计数据失败')
|
||||
}
|
||||
}
|
||||
|
||||
// 加载用户增长趋势
|
||||
async function loadUserTrend() {
|
||||
try {
|
||||
const { data, isSuccess } = await getUserTrend(userTrendDays.value)
|
||||
if (isSuccess && data) {
|
||||
renderUserTrendChart(data)
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
coiMsgError('加载用户增长趋势失败')
|
||||
}
|
||||
}
|
||||
|
||||
// 加载内容发布趋势
|
||||
async function loadContentTrend() {
|
||||
try {
|
||||
const { data, isSuccess } = await getContentTrend(contentTrendDays.value)
|
||||
if (isSuccess && data) {
|
||||
renderContentTrendChart(data)
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
coiMsgError('加载内容发布趋势失败')
|
||||
}
|
||||
}
|
||||
|
||||
// 加载非遗项目排行榜
|
||||
async function loadHeritageRanking() {
|
||||
heritageRankingLoading.value = true
|
||||
try {
|
||||
const { data, isSuccess } = await getHeritageRanking(heritageRankingType.value, 10)
|
||||
if (isSuccess && data) {
|
||||
heritageRanking.value = data
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
coiMsgError('加载非遗项目排行榜失败')
|
||||
}
|
||||
finally {
|
||||
heritageRankingLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 加载活跃用户排行榜
|
||||
async function loadActiveUserRanking() {
|
||||
activeUserRankingLoading.value = true
|
||||
try {
|
||||
const { data, isSuccess } = await getActiveUserRanking(10)
|
||||
if (isSuccess && data) {
|
||||
activeUserRanking.value = data
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
coiMsgError('加载活跃用户排行榜失败')
|
||||
}
|
||||
finally {
|
||||
activeUserRankingLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 渲染用户增长趋势图表
|
||||
function renderUserTrendChart(data: TrendVo[]) {
|
||||
if (!userTrendChartRef.value)
|
||||
return
|
||||
|
||||
if (!userTrendChart) {
|
||||
userTrendChart = echarts.init(userTrendChartRef.value)
|
||||
}
|
||||
|
||||
const dates = data.map(item => item.date)
|
||||
const values = data.map(item => item.value)
|
||||
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'cross',
|
||||
label: {
|
||||
backgroundColor: '#6a7985',
|
||||
},
|
||||
},
|
||||
},
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '3%',
|
||||
containLabel: true,
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
boundaryGap: false,
|
||||
data: dates,
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '新增用户',
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
areaStyle: {
|
||||
color: {
|
||||
type: 'linear',
|
||||
x: 0,
|
||||
y: 0,
|
||||
x2: 0,
|
||||
y2: 1,
|
||||
colorStops: [
|
||||
{ offset: 0, color: 'rgba(34, 197, 94, 0.3)' },
|
||||
{ offset: 1, color: 'rgba(34, 197, 94, 0.05)' },
|
||||
],
|
||||
},
|
||||
},
|
||||
lineStyle: {
|
||||
color: '#22c55e',
|
||||
width: 2,
|
||||
},
|
||||
itemStyle: {
|
||||
color: '#22c55e',
|
||||
},
|
||||
data: values,
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
userTrendChart.setOption(option)
|
||||
}
|
||||
|
||||
// 渲染内容发布趋势图表
|
||||
function renderContentTrendChart(data: TrendVo[]) {
|
||||
if (!contentTrendChartRef.value)
|
||||
return
|
||||
|
||||
if (!contentTrendChart) {
|
||||
contentTrendChart = echarts.init(contentTrendChartRef.value)
|
||||
}
|
||||
|
||||
const dates = data.map(item => item.date)
|
||||
const values = data.map(item => item.value)
|
||||
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'cross',
|
||||
label: {
|
||||
backgroundColor: '#6a7985',
|
||||
},
|
||||
},
|
||||
},
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '3%',
|
||||
containLabel: true,
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
boundaryGap: false,
|
||||
data: dates,
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '发布内容',
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
areaStyle: {
|
||||
color: {
|
||||
type: 'linear',
|
||||
x: 0,
|
||||
y: 0,
|
||||
x2: 0,
|
||||
y2: 1,
|
||||
colorStops: [
|
||||
{ offset: 0, color: 'rgba(59, 130, 246, 0.3)' },
|
||||
{ offset: 1, color: 'rgba(59, 130, 246, 0.05)' },
|
||||
],
|
||||
},
|
||||
},
|
||||
lineStyle: {
|
||||
color: '#3b82f6',
|
||||
width: 2,
|
||||
},
|
||||
itemStyle: {
|
||||
color: '#3b82f6',
|
||||
},
|
||||
data: values,
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
contentTrendChart.setOption(option)
|
||||
}
|
||||
|
||||
// 初始化页面数据
|
||||
onMounted(() => {
|
||||
loadStatistics()
|
||||
loadUserTrend()
|
||||
loadContentTrend()
|
||||
loadHeritageRanking()
|
||||
loadActiveUserRanking()
|
||||
|
||||
// 监听窗口大小变化,调整图表
|
||||
window.addEventListener('resize', () => {
|
||||
userTrendChart?.resize()
|
||||
contentTrendChart?.resize()
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.statistics-dashboard {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.stat-card:hover .stat-icon {
|
||||
transform: scale(1.1);
|
||||
transition: transform 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
.chart-card,
|
||||
.ranking-card {
|
||||
min-height: 400px;
|
||||
}
|
||||
|
||||
.ranking-item:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.ranking-cover img {
|
||||
object-fit: cover;
|
||||
border-radius: 4px;
|
||||
}
|
||||
</style>
|
||||
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user