parent
2f52a66c74
commit
c8063fa38f
@ -0,0 +1,149 @@
|
|||||||
|
// api/post.ts
|
||||||
|
import request from './request'
|
||||||
|
import type { Post, PostListParams, PostListResponse } from '@/views/post'
|
||||||
|
|
||||||
|
export const postApi = {
|
||||||
|
// 获取我的帖子列表
|
||||||
|
getMyPosts(params: PostListParams): Promise<PostListResponse> {
|
||||||
|
return request({
|
||||||
|
url: '/posts/my-posts',
|
||||||
|
method: 'GET',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// 获取帖子详情
|
||||||
|
getPostDetail(id: number): Promise<Post> {
|
||||||
|
return request({
|
||||||
|
url: `/posts/${id}`,
|
||||||
|
method: 'GET'
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// 创建帖子
|
||||||
|
createPost(data: Partial<Post>): Promise<Post> {
|
||||||
|
return request({
|
||||||
|
url: '/posts',
|
||||||
|
method: 'POST',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// 更新帖子
|
||||||
|
updatePost(id: number, data: Partial<Post>): Promise<Post> {
|
||||||
|
return request({
|
||||||
|
url: `/posts/${id}`,
|
||||||
|
method: 'PUT',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// 删除单个帖子
|
||||||
|
deletePost(id: number): Promise<void> {
|
||||||
|
return request({
|
||||||
|
url: `/posts/${id}`,
|
||||||
|
method: 'DELETE'
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// 批量删除帖子
|
||||||
|
batchDeletePosts(ids: number[]): Promise<void> {
|
||||||
|
return request({
|
||||||
|
url: '/posts/batch-delete',
|
||||||
|
method: 'DELETE',
|
||||||
|
data: { ids }
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// 发布帖子(从草稿状态发布)
|
||||||
|
publishPost(id: number): Promise<Post> {
|
||||||
|
return request({
|
||||||
|
url: `/posts/${id}/publish`,
|
||||||
|
method: 'POST'
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// 将帖子设为草稿
|
||||||
|
draftPost(id: number): Promise<Post> {
|
||||||
|
return request({
|
||||||
|
url: `/posts/${id}/draft`,
|
||||||
|
method: 'POST'
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// 获取帖子统计信息
|
||||||
|
getPostStats(): Promise<{
|
||||||
|
totalPosts: number
|
||||||
|
totalViews: number
|
||||||
|
totalLikes: number
|
||||||
|
totalComments: number
|
||||||
|
todayPosts: number
|
||||||
|
weekPosts: number
|
||||||
|
monthPosts: number
|
||||||
|
}> {
|
||||||
|
return request({
|
||||||
|
url: '/posts/stats',
|
||||||
|
method: 'GET'
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// 点赞帖子
|
||||||
|
likePost(id: number): Promise<{ liked: boolean; likesCount: number }> {
|
||||||
|
return request({
|
||||||
|
url: `/posts/${id}/like`,
|
||||||
|
method: 'POST'
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// 取消点赞
|
||||||
|
unlikePost(id: number): Promise<{ liked: boolean; likesCount: number }> {
|
||||||
|
return request({
|
||||||
|
url: `/posts/${id}/unlike`,
|
||||||
|
method: 'POST'
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// 增加浏览量
|
||||||
|
increaseViews(id: number): Promise<{ views: number }> {
|
||||||
|
return request({
|
||||||
|
url: `/posts/${id}/view`,
|
||||||
|
method: 'POST'
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// 搜索帖子
|
||||||
|
searchPosts(params: {
|
||||||
|
keyword: string
|
||||||
|
category?: string
|
||||||
|
page?: number
|
||||||
|
pageSize?: number
|
||||||
|
}): Promise<PostListResponse> {
|
||||||
|
return request({
|
||||||
|
url: '/posts/search',
|
||||||
|
method: 'GET',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// 获取热门帖子
|
||||||
|
getHotPosts(params: {
|
||||||
|
period?: 'day' | 'week' | 'month'
|
||||||
|
limit?: number
|
||||||
|
} = {}): Promise<Post[]> {
|
||||||
|
return request({
|
||||||
|
url: '/posts/hot',
|
||||||
|
method: 'GET',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// 获取推荐帖子
|
||||||
|
getRecommendedPosts(limit = 10): Promise<Post[]> {
|
||||||
|
return request({
|
||||||
|
url: '/posts/recommended',
|
||||||
|
method: 'GET',
|
||||||
|
params: { limit }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//
|
@ -0,0 +1,111 @@
|
|||||||
|
// types/post.ts
|
||||||
|
export interface Post {
|
||||||
|
id: number
|
||||||
|
title: string
|
||||||
|
content?: string
|
||||||
|
category: string
|
||||||
|
views: number
|
||||||
|
likes: number
|
||||||
|
comments?: number
|
||||||
|
createdAt: string
|
||||||
|
updatedAt?: string
|
||||||
|
status: 'published' | 'draft'
|
||||||
|
author?: {
|
||||||
|
id: number
|
||||||
|
username: string
|
||||||
|
avatar?: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PostListParams {
|
||||||
|
page: number
|
||||||
|
pageSize: number
|
||||||
|
title?: string
|
||||||
|
category?: string
|
||||||
|
status?: string
|
||||||
|
sortBy?: 'createdAt' | 'views' | 'likes'
|
||||||
|
sortOrder?: 'asc' | 'desc'
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PostListResponse {
|
||||||
|
data: Post[]
|
||||||
|
total: number
|
||||||
|
page: number
|
||||||
|
pageSize: number
|
||||||
|
totalPages: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FilterForm {
|
||||||
|
title: string
|
||||||
|
category: string
|
||||||
|
status?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Pagination {
|
||||||
|
currentPage: number
|
||||||
|
pageSize: number
|
||||||
|
total: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Stats {
|
||||||
|
totalPosts: number
|
||||||
|
totalViews: number
|
||||||
|
totalLikes: number
|
||||||
|
totalComments: number
|
||||||
|
avgViews: number
|
||||||
|
avgLikes: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DeleteDialog {
|
||||||
|
visible: boolean
|
||||||
|
loading: boolean
|
||||||
|
type: 'single' | 'batch'
|
||||||
|
post?: Post
|
||||||
|
}
|
||||||
|
|
||||||
|
// types/api.ts
|
||||||
|
export interface ApiResponse<T = any> {
|
||||||
|
code: number
|
||||||
|
message: string
|
||||||
|
data: T
|
||||||
|
timestamp: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ApiError {
|
||||||
|
code: number
|
||||||
|
message: string
|
||||||
|
details?: any
|
||||||
|
}
|
||||||
|
|
||||||
|
// types/category.ts
|
||||||
|
export interface Category {
|
||||||
|
id: string
|
||||||
|
name: string
|
||||||
|
description?: string
|
||||||
|
color?: string
|
||||||
|
icon?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export const CATEGORIES: Category[] = [
|
||||||
|
{ id: 'tech', name: '技术讨论', color: 'primary', icon: 'Monitor' },
|
||||||
|
{ id: 'life', name: '生活随笔', color: 'success', icon: 'Coffee' },
|
||||||
|
{ id: 'study', name: '学习分享', color: 'warning', icon: 'Reading' },
|
||||||
|
{ id: 'qa', name: '问答求助', color: 'info', icon: 'QuestionFilled' }
|
||||||
|
]
|
||||||
|
|
||||||
|
// types/user.ts
|
||||||
|
export interface User {
|
||||||
|
id: number
|
||||||
|
username: string
|
||||||
|
email: string
|
||||||
|
avatar?: string
|
||||||
|
role: 'admin' | 'user'
|
||||||
|
createdAt: string
|
||||||
|
profile?: {
|
||||||
|
nickname?: string
|
||||||
|
bio?: string
|
||||||
|
location?: string
|
||||||
|
website?: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,138 @@
|
|||||||
|
// hooks/usePost.ts
|
||||||
|
import { ref, reactive, computed } from 'vue'
|
||||||
|
import type { Post, PostListParams, FilterForm, Pagination, Stats } from '@/views/post'
|
||||||
|
import { postApi } from '@/utils/post'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
|
||||||
|
export function usePostManagement() {
|
||||||
|
const loading = ref(false)
|
||||||
|
const posts = ref<Post[]>([])
|
||||||
|
const selectedPosts = ref<Post[]>([])
|
||||||
|
|
||||||
|
const filterForm = reactive<FilterForm>({
|
||||||
|
title: '',
|
||||||
|
category: '',
|
||||||
|
status: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
const pagination = reactive<Pagination>({
|
||||||
|
currentPage: 1,
|
||||||
|
pageSize: 20,
|
||||||
|
total: 0
|
||||||
|
})
|
||||||
|
|
||||||
|
// 计算统计数据
|
||||||
|
const stats = computed<Stats>(() => {
|
||||||
|
const totalPosts = posts.value.length
|
||||||
|
const totalViews = posts.value.reduce((sum, post) => sum + post.views, 0)
|
||||||
|
const totalLikes = posts.value.reduce((sum, post) => sum + post.likes, 0)
|
||||||
|
const totalComments = posts.value.reduce((sum, post) => sum + (post.comments || 0), 0)
|
||||||
|
const avgViews = totalPosts > 0 ? totalViews / totalPosts : 0
|
||||||
|
const avgLikes = totalPosts > 0 ? totalLikes / totalPosts : 0
|
||||||
|
|
||||||
|
return {
|
||||||
|
totalPosts,
|
||||||
|
totalViews,
|
||||||
|
totalLikes,
|
||||||
|
totalComments,
|
||||||
|
avgViews,
|
||||||
|
avgLikes
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 获取帖子列表
|
||||||
|
const fetchPosts = async () => {
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
const params: PostListParams = {
|
||||||
|
page: pagination.currentPage,
|
||||||
|
pageSize: pagination.pageSize,
|
||||||
|
title: filterForm.title || undefined,
|
||||||
|
category: filterForm.category || undefined,
|
||||||
|
status: filterForm.status || undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await postApi.getMyPosts(params)
|
||||||
|
posts.value = response.data
|
||||||
|
pagination.total = response.total
|
||||||
|
} catch (error) {
|
||||||
|
ElMessage.error('获取帖子列表失败')
|
||||||
|
console.error('Fetch posts error:', error)
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除帖子
|
||||||
|
const deletePost = async (postId: number): Promise<boolean> => {
|
||||||
|
try {
|
||||||
|
await postApi.deletePost(postId)
|
||||||
|
ElMessage.success('删除成功')
|
||||||
|
return true
|
||||||
|
} catch (error) {
|
||||||
|
ElMessage.error('删除失败')
|
||||||
|
console.error('Delete post error:', error)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 批量删除
|
||||||
|
const batchDeletePosts = async (postIds: number[]): Promise<boolean> => {
|
||||||
|
try {
|
||||||
|
await postApi.batchDeletePosts(postIds)
|
||||||
|
ElMessage.success(`成功删除 ${postIds.length} 个帖子`)
|
||||||
|
return true
|
||||||
|
} catch (error) {
|
||||||
|
ElMessage.error('批量删除失败')
|
||||||
|
console.error('Batch delete posts error:', error)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 搜索
|
||||||
|
const handleSearch = () => {
|
||||||
|
pagination.currentPage = 1
|
||||||
|
fetchPosts()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置筛选
|
||||||
|
const handleReset = () => {
|
||||||
|
Object.keys(filterForm).forEach(key => {
|
||||||
|
filterForm[key as keyof FilterForm] = ''
|
||||||
|
})
|
||||||
|
handleSearch()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 选择变化
|
||||||
|
const handleSelectionChange = (selection: Post[]) => {
|
||||||
|
selectedPosts.value = selection
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分页变化
|
||||||
|
const handleSizeChange = (newSize: number) => {
|
||||||
|
pagination.pageSize = newSize
|
||||||
|
fetchPosts()
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleCurrentChange = (newPage: number) => {
|
||||||
|
pagination.currentPage = newPage
|
||||||
|
fetchPosts()
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
loading,
|
||||||
|
posts,
|
||||||
|
selectedPosts,
|
||||||
|
filterForm,
|
||||||
|
pagination,
|
||||||
|
stats,
|
||||||
|
fetchPosts,
|
||||||
|
deletePost,
|
||||||
|
batchDeletePosts,
|
||||||
|
handleSearch,
|
||||||
|
handleReset,
|
||||||
|
handleSelectionChange,
|
||||||
|
handleSizeChange,
|
||||||
|
handleCurrentChange
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in new issue