This commit is contained in:
oneao committed 2025-12-24 17:31:19 +08:00
1 parent e9ad523fcf
commit f310336188
39 files changed
+411 -303

No files matched your search

@@ -0,0 +1,27 @@
package cn.link56.post.controller;
import cn.link56.common.Result;
import cn.link56.post.model.vo.PostCategoryVO;
import cn.link56.post.model.vo.PostTagVO;
import cn.link56.post.service.PostCategoryService;
import cn.link56.post.service.PostTagService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.List;
@RestController
@RequestMapping("/post/category")
public class PostCategoryController {
@Resource
private PostCategoryService postCategoryService;
@GetMapping
public Result<List<PostCategoryVO>> listCategories(){
List<PostCategoryVO> list = postCategoryService.listCategories();
return Result.success(list);
}
}
@@ -0,0 +1,9 @@
package cn.link56.post.mapper;
import cn.link56.post.model.po.PostCategory;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface PostCategoryMapper extends BaseMapper<PostCategory> {
}
@@ -1,9 +1,9 @@
package cn.link56.post.mapper;
import cn.link56.post.model.po.PostCommentPO;
import cn.link56.post.model.po.PostComment;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface PostCommentMapper extends BaseMapper<PostCommentPO> {
public interface PostCommentMapper extends BaseMapper<PostComment> {
}
@@ -1,9 +1,9 @@
package cn.link56.post.mapper;
import cn.link56.post.model.po.PostFavoritePO;
import cn.link56.post.model.po.PostFavorite;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface PostFavoriteMapper extends BaseMapper<PostFavoritePO> {
public interface PostFavoriteMapper extends BaseMapper<PostFavorite> {
}
@@ -1,9 +1,9 @@
package cn.link56.post.mapper;
import cn.link56.post.model.po.PostLikePO;
import cn.link56.post.model.po.PostLike;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface PostLikeMapper extends BaseMapper<PostLikePO> {
public interface PostLikeMapper extends BaseMapper<PostLike> {
}
@@ -1,6 +1,6 @@
package cn.link56.post.mapper;
import cn.link56.post.model.po.PostPO;
import cn.link56.post.model.po.Post;
import cn.link56.post.model.vo.DashboardVO;
import cn.link56.post.model.vo.QueryPostVO;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
@@ -10,7 +10,7 @@ import org.apache.ibatis.annotations.Param;
import java.util.List;
@Mapper
public interface PostMapper extends BaseMapper<PostPO> {
public interface PostMapper extends BaseMapper<Post> {
List<QueryPostVO> selectPostPage(
@Param("offset") int offset,
@Param("limit") int limit,
@@ -1,12 +1,9 @@
package cn.link56.post.mapper;
import cn.link56.post.model.po.PostTagPO;
import cn.link56.post.model.vo.PostTagVO;
import cn.link56.post.model.po.PostTag;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface PostTagMapper extends BaseMapper<PostTagPO> {
public interface PostTagMapper extends BaseMapper<PostTag> {
}
@@ -1,9 +1,9 @@
package cn.link56.post.mapper;
import cn.link56.post.model.po.PostViewPO;
import cn.link56.post.model.po.PostView;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface PostViewMapper extends BaseMapper<PostViewPO> {
public interface PostViewMapper extends BaseMapper<PostView> {
}
@@ -18,9 +18,7 @@ public class UpdatePostDTO {
private String title;
@NotBlank(message = "帖子内容不能为空")
private String content;
@NotBlank(message = "帖子摘要不能为空")
private String summary;
private String cover;
private List<String> tags;
private Integer status;
@@ -4,11 +4,9 @@ import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import net.sf.jsqlparser.expression.DateTimeLiteralExpression;
import java.util.Date;
@@ -16,7 +14,7 @@ import java.util.Date;
@AllArgsConstructor
@NoArgsConstructor
@TableName("b_post")
public class PostPO {
public class Post {
@TableId(value = "b_id")
private Long id;
@@ -29,15 +27,9 @@ public class PostPO {
@TableField("b_content")
private String content;
@TableField("b_summary")
private String summary;
@TableField("b_tag")
private String tag;
@TableField("b_cover")
private String cover;
@TableField("b_publish_time")
private Date publishTime;
@@ -0,0 +1,23 @@
package cn.link56.post.model.po;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("b_post_category")
public class PostCategory {
@TableId(value = "b_id")
private Long id;
@TableField("b_name")
private String name;
@TableField("b_canuse")
private String canuse;
}
@@ -12,7 +12,7 @@ import lombok.NoArgsConstructor;
@AllArgsConstructor
@NoArgsConstructor
@TableName("b_post_comment")
public class PostCommentPO {
public class PostComment {
@TableId(value = "b_id")
private Long id;
@@ -12,7 +12,7 @@ import lombok.NoArgsConstructor;
@AllArgsConstructor
@NoArgsConstructor
@TableName("b_post_favorite")
public class PostFavoritePO {
public class PostFavorite {
@TableId(value = "b_id")
private Long id;
@@ -12,7 +12,7 @@ import lombok.NoArgsConstructor;
@AllArgsConstructor
@NoArgsConstructor
@TableName("b_post_like")
public class PostLikePO {
public class PostLike {
@TableId(value = "b_id")
private Long id;
@@ -11,7 +11,7 @@ import lombok.NoArgsConstructor;
@AllArgsConstructor
@NoArgsConstructor
@TableName("b_post_tag")
public class PostTagPO {
public class PostTag {
@TableId(value = "b_id")
private Long id;
@@ -12,7 +12,7 @@ import lombok.NoArgsConstructor;
@AllArgsConstructor
@NoArgsConstructor
@TableName("b_post_view")
public class PostViewPO {
public class PostView {
@TableId(value = "b_id")
private Long id;
@@ -0,0 +1,13 @@
package cn.link56.post.model.vo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class PostCategoryVO {
private Long id;
private String name;
}
@@ -0,0 +1,33 @@
package cn.link56.post.service;
import cn.link56.post.mapper.PostCategoryMapper;
import cn.link56.post.model.po.PostCategory;
import cn.link56.post.model.vo.PostCategoryVO;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
import java.util.stream.Collectors;
@Service
public class PostCategoryService extends ServiceImpl<PostCategoryMapper, PostCategory> {
@Resource
private PostCategoryMapper postCategoryMapper;
public List<PostCategoryVO> listCategories() {
LambdaQueryWrapper<PostCategory> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(PostCategory::getCanuse,"1");
List<PostCategory> postCategories = postCategoryMapper.selectList(wrapper);
return postCategories.stream().map(postCategory -> {
PostCategoryVO postCategoryVO = new PostCategoryVO();
BeanUtils.copyProperties(postCategory, postCategoryVO);
return postCategoryVO;
}).collect(Collectors.toList());
}
}
@@ -1,14 +1,14 @@
package cn.link56.post.service;
import cn.link56.post.mapper.PostCommentMapper;
import cn.link56.post.model.po.PostCommentPO;
import cn.link56.post.model.po.PostComment;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service
public class PostCommentService extends ServiceImpl<PostCommentMapper, PostCommentPO> {
public class PostCommentService extends ServiceImpl<PostCommentMapper, PostComment> {
@Resource
private PostCommentMapper postCommentMapper;
}
@@ -1,14 +1,14 @@
package cn.link56.post.service;
import cn.link56.post.mapper.PostFavoriteMapper;
import cn.link56.post.model.po.PostFavoritePO;
import cn.link56.post.model.po.PostFavorite;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service
public class PostFavoriteService extends ServiceImpl<PostFavoriteMapper, PostFavoritePO> {
public class PostFavoriteService extends ServiceImpl<PostFavoriteMapper, PostFavorite> {
@Resource
private PostFavoriteMapper postFavoriteMapper;
}
@@ -1,14 +1,14 @@
package cn.link56.post.service;
import cn.link56.post.mapper.PostLikeMapper;
import cn.link56.post.model.po.PostLikePO;
import cn.link56.post.model.po.PostLike;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service
public class PostLikeService extends ServiceImpl<PostLikeMapper, PostLikePO> {
public class PostLikeService extends ServiceImpl<PostLikeMapper, PostLike> {
@Resource
private PostLikeMapper postLikeMapper;
}
@@ -3,18 +3,15 @@ package cn.link56.post.service;
import cn.link56.common.BasePageVO;
import cn.link56.common.context.UserContext;
import cn.link56.common.utils.snowflake.idgen.IdGenerator;
import cn.link56.company.model.po.CompanyUserPO;
import cn.link56.post.mapper.PostMapper;
import cn.link56.post.model.dto.*;
import cn.link56.post.model.enums.PostStatusEnum;
import cn.link56.post.model.po.PostPO;
import cn.link56.post.model.po.Post;
import cn.link56.post.model.vo.DashboardVO;
import cn.link56.post.model.vo.EditPostVO;
import cn.link56.post.model.vo.QueryPostVO;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
@@ -27,48 +24,48 @@ import java.util.*;
import java.util.stream.Collectors;
@Service
public class PostService extends ServiceImpl<PostMapper, PostPO> {
public class PostService extends ServiceImpl<PostMapper, Post> {
@Resource
private PostMapper postMapper;
@Transactional
public EditPostVO addPost(AddPostDTO dto) {
PostPO postPO = new PostPO();
BeanUtils.copyProperties(dto, postPO);
Post post = new Post();
BeanUtils.copyProperties(dto, post);
postPO.setId(IdGenerator.nextId());
postPO.setCompanyId(UserContext.getCompanyId());
postPO.setTag(joinTags(dto.getTags()));
post.setId(IdGenerator.nextId());
post.setCompanyId(UserContext.getCompanyId());
post.setTag(joinTags(dto.getTags()));
handleUpdatePublishTime(postPO, dto.getStatus());
handleUpdatePublishTime(post, dto.getStatus());
postMapper.insert(postPO);
return buildEditPostVO(postPO);
postMapper.insert(post);
return buildEditPostVO(post);
}
public EditPostVO updatePost(UpdatePostDTO dto) {
PostPO postPO = postMapper.selectById(dto.getId());
if (postPO == null) {
Post post = postMapper.selectById(dto.getId());
if (post == null) {
return null;
}
BeanUtils.copyProperties(dto, postPO);
postPO.setTag(joinTags(dto.getTags()));
BeanUtils.copyProperties(dto, post);
post.setTag(joinTags(dto.getTags()));
handleUpdatePublishTime(postPO, dto.getStatus());
handleUpdatePublishTime(post, dto.getStatus());
postMapper.updateById(postPO);
return buildEditPostVO(postPO);
postMapper.updateById(post);
return buildEditPostVO(post);
}
public EditPostVO getDetail(Long id) {
PostPO postPO = postMapper.selectById(id);
if (postPO == null) {
Post post = postMapper.selectById(id);
if (post == null) {
return null;
}
return buildEditPostVO(postPO);
return buildEditPostVO(post);
}
public BasePageVO<QueryPostVO> queryPost(QueryPostDTO dto) {
@@ -101,14 +98,14 @@ public class PostService extends ServiceImpl<PostMapper, PostPO> {
/* ================= 私有方法 ================= */
private EditPostVO buildEditPostVO(PostPO postPO) {
private EditPostVO buildEditPostVO(Post post) {
EditPostVO vo = new EditPostVO();
BeanUtils.copyProperties(postPO, vo);
BeanUtils.copyProperties(post, vo);
vo.setTags(
StringUtils.isBlank(postPO.getTag())
StringUtils.isBlank(post.getTag())
? Collections.emptyList()
: Arrays.asList(StringUtils.split(postPO.getTag(), ","))
: Arrays.asList(StringUtils.split(post.getTag(), ","))
);
return vo;
}
@@ -119,10 +116,10 @@ public class PostService extends ServiceImpl<PostMapper, PostPO> {
: StringUtils.join(tags, ",");
}
private void handleUpdatePublishTime(PostPO postPO, Integer status) {
private void handleUpdatePublishTime(Post post, Integer status) {
if (PostStatusEnum.PUBLISHED.getCode() == status
&& postPO.getPublishTime() == null) {
postPO.setPublishTime(new Date());
&& post.getPublishTime() == null) {
post.setPublishTime(new Date());
}
}
@@ -136,13 +133,13 @@ public class PostService extends ServiceImpl<PostMapper, PostPO> {
Long id = updatePostStatusDTO.getId();
Integer status = updatePostStatusDTO.getStatus();
PostPO postPO = postMapper.selectById(id);
if (postPO == null) {
Post post = postMapper.selectById(id);
if (post == null) {
return;
}
postPO.setStatus(status);
postMapper.updateById(postPO);
post.setStatus(status);
postMapper.updateById(post);
}
public DashboardVO getDashboardData() {
@@ -184,21 +181,21 @@ public class PostService extends ServiceImpl<PostMapper, PostPO> {
}
private List<DashboardVO.Chart> getDashboardTypeStats(Long userId) {
LambdaQueryWrapper<PostPO> queryWrapper = new LambdaQueryWrapper<>();
LambdaQueryWrapper<Post> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper
.select(PostPO::getTag)
.eq(PostPO::getStatus, PostStatusEnum.PUBLISHED.getCode())
.eq(PostPO::getCreateBy, userId);
.select(Post::getTag)
.eq(Post::getStatus, PostStatusEnum.PUBLISHED.getCode())
.eq(Post::getCreateBy, userId);
List<PostPO> postPOS = postMapper.selectList(queryWrapper);
List<Post> posts = postMapper.selectList(queryWrapper);
// 用 map 统计每个 tag 出现次数
Map<String, Integer> tagCountMap = new HashMap<>();
for (PostPO postPO : postPOS) {
if (postPO == null) continue;
for (Post post : posts) {
if (post == null) continue;
String tagsStr = postPO.getTag();
String tagsStr = post.getTag();
if (tagsStr == null || tagsStr.isEmpty()) continue;
// 分割逗号,并去掉空格
@@ -1,7 +1,7 @@
package cn.link56.post.service;
import cn.link56.post.mapper.PostTagMapper;
import cn.link56.post.model.po.PostTagPO;
import cn.link56.post.model.po.PostTag;
import cn.link56.post.model.vo.PostTagVO;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
@@ -13,19 +13,19 @@ import java.util.List;
import java.util.stream.Collectors;
@Service
public class PostTagService extends ServiceImpl<PostTagMapper, PostTagPO> {
public class PostTagService extends ServiceImpl<PostTagMapper, PostTag> {
@Resource
private PostTagMapper postTagMapper;
public List<PostTagVO> listTags() {
LambdaQueryWrapper<PostTagPO> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(PostTagPO::getCanuse,"1");
LambdaQueryWrapper<PostTag> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(PostTag::getCanuse,"1");
List<PostTagPO> postTagPOS = postTagMapper.selectList(wrapper);
List<PostTag> postTags = postTagMapper.selectList(wrapper);
return postTagPOS.stream().map(postTagPO -> {
return postTags.stream().map(postTag -> {
PostTagVO postTagVO = new PostTagVO();
BeanUtils.copyProperties(postTagPO, postTagVO);
BeanUtils.copyProperties(postTag, postTagVO);
return postTagVO;
}).collect(Collectors.toList());
@@ -1,14 +1,14 @@
package cn.link56.post.service;
import cn.link56.post.mapper.PostViewMapper;
import cn.link56.post.model.po.PostViewPO;
import cn.link56.post.model.po.PostView;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service
public class PostViewService extends ServiceImpl<PostViewMapper, PostViewPO> {
public class PostViewService extends ServiceImpl<PostViewMapper, PostView> {
@Resource
private PostViewMapper postViewMapper;
}
@@ -8,7 +8,6 @@
type="cn.link56.post.model.vo.QueryPostVO">
<id property="id" column="id"/>
<result property="title" column="title"/>
<result property="cover" column="cover"/>
<!-- tags 字符串 → List<String> -->
<result
@@ -32,7 +31,6 @@
SELECT
p.b_id AS id,
p.b_title AS title,
p.b_cover AS cover,
p.b_tag AS tagsString,
p.b_update_time AS updateTime,
p.b_publish_time AS publishTime,
@@ -51,7 +51,7 @@ function onSearch() {
border-radius: 4px;
border: 0;
color: #000;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
// box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
background-color: #f4f4f4;
.query-item {
flex: 1;
@@ -99,11 +99,9 @@ function isActive(path: string) {
<style lang="scss" scoped>
.header {
position: fixed;
position: sticky;
top: 0;
right: 0;
left: 0;
z-index: 99;
z-index: 1000;
background-color: #fff;
.container {
@@ -112,16 +110,24 @@ function isActive(path: string) {
display: flex;
justify-content: space-between;
align-items: center;
height: 60px;
box-shadow: 0 5px 16px rgba(0, 0, 0, 0.1);
height: var(--header-height);
border-bottom: 1px solid rgb(232.9, 232.9, 232.9);
flex-shrink: 0;
min-width: 0;
.logo {
width: 160px;
height: 50px;
flex-shrink: 0;
min-width: 0;
}
.menu {
display: flex;
align-items: center;
flex-shrink: 0;
min-width: 0;
.menu-item {
display: flex;
align-items: center;
@@ -146,7 +152,8 @@ function isActive(path: string) {
&::after {
content: '';
position: absolute;
bottom: -4px;
bottom: 0px;
bottom: -8px;
left: 50%;
width: 0;
height: 2px;
@@ -173,6 +180,9 @@ function isActive(path: string) {
.action {
display: flex;
align-items: center;
flex-shrink: 0;
min-width: 0;
.action-item {
font-size: 20px;
margin-right: 24px;
@@ -3,7 +3,7 @@ import Header from './components/header.vue'
</script>
<template>
<div class="app">
<div class="layout">
<Header />
<div class="content">
<RouterView />
@@ -15,18 +15,16 @@ import Header from './components/header.vue'
</template>
<style lang="scss" scoped>
.app {
.layout {
position: relative;
}
.content {
padding-top: 60px;
}
.footer {
margin-top: 32px;
height: 100px;
background-color: #fff;
display: flex;
align-items: center;
justify-content: center;
@@ -1,32 +1,14 @@
:root {
--header-height: 60px;
}
html,
body {
background-color: #f7f7f7;
}
body::-webkit-scrollbar {
display: none;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
::-webkit-scrollbar {
width: 8px;
}
::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 10px;
}
::-webkit-scrollbar-thumb {
background: #888;
border-radius: 10px;
border: 2px solid #f1f1f1;
}
::-webkit-scrollbar-thumb:hover {
background: #555;
}
@@ -7,8 +7,6 @@ export interface PostEditorForm {
id: number
title: string
content: string
summary: string
cover: string
tags: string[]
status: number
publishTime: string
@@ -18,8 +16,6 @@ export interface PostEditorForm {
export interface AddPostParams {
title: string
content: string
summary: string
cover: string
status: number
tags: string[]
}
@@ -28,8 +24,6 @@ export interface UpdatePostParams {
id: number
title: string
content: string
summary: string
cover: string
status: number
tags: string[]
}
@@ -57,7 +51,6 @@ export interface UpdatePostStatusParams {
export interface Post {
id: number
title: string
cover: string
tags: string[]
status: number
@@ -44,8 +44,6 @@ const oldForm = ref<PostEditorForm>({
id: 0,
title: '',
content: '',
summary: '',
cover: '',
tags: [],
publishTime: '',
updateTime: '',
@@ -56,8 +54,6 @@ const form = ref<PostEditorForm>({
id: 0,
title: '',
content: '',
summary: '',
cover: '',
tags: [],
publishTime: '',
updateTime: '',
@@ -108,10 +104,6 @@ async function handleShowPushDrawer() {
loading.init = true
showPushDrawer.value = true
if (!isUpdate.value) {
form.value.summary = (wangEditorRef.value.getText() || '').slice(0, 100)
}
await getPostTagApi().then((res) => {
if (res.isSuccess) {
tagOptions.value = res.data
@@ -127,18 +119,6 @@ async function handleShowPushDrawer() {
loading.init = false
}
async function handleCoverUploadIfNeeded() {
if (form.value.cover === oldForm.value.cover)
return
if (!form.value.cover || !imageUpload.value)
return
const { blob, name, size } = imageUpload.value
const res = await uploadToAliyun(blob, name, size, false)
form.value.cover = res.url
}
async function handleSave(formEl?: FormInstance) {
if (!formEl)
return
@@ -158,14 +138,10 @@ async function handleSave(formEl?: FormInstance) {
}
try {
await handleCoverUploadIfNeeded()
const payload = {
title: form.value.title,
content: form.value.content,
summary: form.value.summary,
status: form.value.status,
cover: form.value.cover,
tags: form.value.tags,
}
@@ -244,7 +220,7 @@ async function handleSave(formEl?: FormInstance) {
</el-select>
</el-form-item>
<el-form-item label="文章封面 " prop="cover">
<!-- <el-form-item label="文章封面 " prop="cover">
<ImageUploadCard
v-model="form.cover"
@change="(data) => {
@@ -262,7 +238,7 @@ async function handleSave(formEl?: FormInstance) {
maxlength="100"
type="textarea"
/>
</el-form-item>
</el-form-item> -->
<el-form-item label="状态" prop="status">
<el-select v-model="form.status" placeholder="请选择状态">
@@ -291,7 +267,6 @@ async function handleSave(formEl?: FormInstance) {
<style lang="scss" scoped>
.editor-container {
padding: 16px 32px;
.toolbar {
display: flex;
justify-content: space-between;
@@ -1,7 +1,15 @@
<script setup lang="ts">
import { onMounted } from 'vue'
import type { PropType } from 'vue'
import type { DashboradChart } from '@/types/post'
import { watch } from 'vue'
import { useEcharts } from '@/hooks/echarts'
const props = defineProps({
data: {
type: Array as PropType<DashboradChart[]>,
},
})
const { domRef, updateOptions } = useEcharts(() => ({
tooltip: {
trigger: 'item', // 鼠标悬停时触发提示框,显示单个数据点
@@ -25,36 +33,30 @@ const { domRef, updateOptions } = useEcharts(() => ({
},
],
}))
onMounted(() => {
getData()
})
function getData() {
getForumPostChartNumberApi().then((res) => {
const labels: string[] = res.data.map(item => item.label)
const values: number[] = res.data.map(item => item.value)
updateOptions((opts) => {
opts.xAxis.data = labels
opts.series = [
{
data: values,
type: 'bar',
barWidth: '40%', // 调整柱子的宽度
},
]
return opts
})
watch(() => props.data, (newVal) => {
if (!newVal) {
return
}
const labels: string[] = newVal.map(item => item.label)
const values: number[] = newVal.map(item => item.value)
updateOptions((opts) => {
opts.xAxis.data = labels
opts.series = [
{
data: values,
type: 'bar',
barWidth: '40%', // 调整柱子的宽度
},
]
return opts
})
}
})
</script>
<template>
<el-card class="card-demo" title="文章发布数量" hoverable>
<template #extra>
<a-link>刷新</a-link>
</template>
<div ref="domRef" class="h-360px" />
</el-card>
<div ref="domRef" style="height: 360px;" />
</template>
<style lang="scss" scoped></style>
@@ -1,63 +1,66 @@
<template>
<a-card class="card-demo" title="文章分类" hoverable>
<template #extra>
<a-link @click="getData">刷新</a-link>
</template>
<div ref="domRef" class="h-360px overflow-hidden"></div>
</a-card>
</template>
<script setup lang="ts">
import { useEcharts } from "@/hooks/echarts";
import { getForumPostChartTypeApi } from "@/service/forum/post";
import { onMounted } from "vue";
import type { PropType } from 'vue'
import type { DashboradChart } from '@/types/post'
import { watch } from 'vue'
import { useEcharts } from '@/hooks/echarts'
const props = defineProps({
data: {
type: Array as PropType<DashboradChart[]>,
default: () => [],
},
})
const { domRef, updateOptions } = useEcharts(() => ({
title: {
text: "文章分类",
left: "center"
text: '文章分类',
left: 'center',
},
tooltip: {
trigger: "item"
trigger: 'item',
},
legend: {
orient: "vertical",
left: "left"
orient: 'vertical',
left: 'left',
},
series: [
{
name: "文章分类",
type: "pie",
radius: "50%",
name: '文章分类',
type: 'pie',
radius: '50%',
data: [] as any[], // Initial empty data
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: "rgba(0, 0, 0, 0.5)"
}
}
}
]
}));
shadowColor: 'rgba(0, 0, 0, 0.5)',
},
},
},
],
}))
// 数据请求并更新图表
function getData() {
getForumPostChartTypeApi().then((res) => {
// 更新饼图的数据
updateOptions((opts) => {
opts.series[0].data = res.data.map((item) => ({
value: item.value,
name: item.label
}));
return opts;
});
});
}
watch(() => props.data, (newVal) => {
if (!newVal) {
return
}
onMounted(() => {
getData(); // 在组件加载时获取数据
});
console.log('===type===')
console.log(newVal)
// 更新饼图的数据
updateOptions((opts) => {
opts.series[0].data = newVal.map(item => ({
value: item.value,
name: item.label,
}))
return opts
})
}, { deep: true })
</script>
<template>
<div ref="domRef" style="height: 360px;" />
</template>
<style lang="scss" scoped></style>
@@ -1,58 +1,59 @@
<template>
<a-card class="card-demo" title="文章浏览量" hoverable>
<template #extra>
<a-link @click="getData">刷新</a-link>
</template>
<div ref="domRef" class="h-360px overflow-hidden"></div>
</a-card>
</template>
<script setup lang="ts">
import { useEcharts } from "@/hooks/echarts";
import { getForumPostChartViewApi } from "@/service/forum/post";
import { onMounted } from "vue";
import type { PropType } from 'vue'
import type { DashboradChart } from '@/types/post'
import { onMounted, watch } from 'vue'
import { useEcharts } from '@/hooks/echarts'
const props = defineProps({
data: {
type: Array as PropType<DashboradChart[]>,
},
})
const { domRef, updateOptions } = useEcharts(() => ({
tooltip: {
trigger: "item", // 鼠标悬停时触发提示框,显示单个数据点
formatter: "{b}:{c}" // 自定义提示框显示格式:{b} 是 xAxis 的标签,{c} 是 series 的值
trigger: 'item', // 鼠标悬停时触发提示框,显示单个数据点
formatter: '{b}:{c}', // 自定义提示框显示格式:{b} 是 xAxis 的标签,{c} 是 series 的值
},
xAxis: {
type: "category",
data: [] as string[]
type: 'category',
data: [] as string[],
},
yAxis: {
type: "value"
type: 'value',
},
series: [
{
data: [] as number[],
type: "line"
}
]
}));
type: 'line',
},
],
}))
onMounted(() => {
getData();
});
watch(() => props.data, (newVal) => {
if (!newVal) {
return
}
const labels: string[] = newVal.map(item => item.label)
const values: number[] = newVal.map(item => item.value)
function getData() {
getForumPostChartViewApi().then((res) => {
const labels: string[] = res.data.map((item) => item.label);
const values: number[] = res.data.map((item) => item.value);
updateOptions((opts) => {
opts.xAxis.data = labels;
opts.series = [
{
data: values,
type: "line"
}
];
return opts;
});
});
}
updateOptions((opts) => {
opts.xAxis.data = labels
opts.series = [
{
data: values,
type: 'line',
},
]
return opts
})
})
</script>
<template>
<div ref="domRef" style="height: 360px;" />
</template>
<style lang="scss" scoped></style>
@@ -1,13 +1,44 @@
<script lang="ts" setup>
import type { DashboardData } from '@/types/post'
import { ElMessage } from 'element-plus'
import { onMounted, ref } from 'vue'
import CustomLoading from '@/components/CustomLoading.vue'
import { getDashboardDataApi } from '@/request/api/post'
import PostNumberChart from './components/post-number-chart.vue'
import PostTypeChart from './components/post-type-chart.vue'
import PostViewChart from './components/post-view-chart.vue'
const loading = ref<boolean>(false)
const dashboradData = ref<DashboardData>({
allCount: {
postCount: 0,
viewCount: 0,
likeCount: 0,
favoriteCount: 0,
},
numberChart: [],
viewChart: [],
typeChart: [],
})
onMounted(() => {
loading.value = true
getDashboardDataApi().then((res) => {
console.log(res)
if (res.isSuccess) {
// dashboradData.value = res.data
dashboradData.value.typeChart = [...res.data.typeChart]
console.log(dashboradData.value)
}
else {
ElMessage.error(res.message || '加载失败,请稍后再试')
}
}).catch((err) => {
console.log(err)
ElMessage.error('加载失败,请稍后再试')
}).finally(() => {
loading.value = false
})
})
</script>
@@ -30,7 +61,7 @@ onMounted(() => {
文章数
</div>
<div class="stat-value">
<el-statistic :value="98500" />
<el-statistic :value="dashboradData.allCount.postCount" />
</div>
</div>
</el-col>
@@ -40,7 +71,7 @@ onMounted(() => {
浏览量
</div>
<div class="stat-value">
<el-statistic :value="98500" />
<el-statistic :value="dashboradData.allCount.viewCount" />
</div>
</div>
</el-col>
@@ -50,7 +81,7 @@ onMounted(() => {
点赞数
</div>
<div class="stat-value">
<el-statistic :value="98500" />
<el-statistic :value="dashboradData.allCount.likeCount" />
</div>
</div>
</el-col>
@@ -60,7 +91,7 @@ onMounted(() => {
收藏数量
</div>
<div class="stat-value">
<el-statistic :value="98500" />
<el-statistic :value="dashboradData.allCount.favoriteCount" />
</div>
</div>
</el-col>
@@ -73,7 +104,11 @@ onMounted(() => {
数据报表
</div>
</div>
<div class="card-content" />
<div class="card-content">
<PostViewChart :data="dashboradData.viewChart" />
<PostNumberChart :data="dashboradData.numberChart" />
<PostTypeChart :data="dashboradData.typeChart" />
</div>
</div>
</div>
</template>
@@ -280,9 +280,6 @@ function handleToEdit(row?: Post) {
class="post-item"
@click="handleToEdit(item)"
>
<div v-if="item.cover" class="post-cover">
<img :src="item.cover">
</div>
<div class="post-content">
<div class="post-main">
<div class="post-title-area">
@@ -112,13 +112,15 @@ watch(searchKey, async (val) => {
<template>
<div class="settings-wrapper">
<div class="left-menu">
<el-input
v-model="searchKey"
placeholder="搜索菜单"
size="large"
:prefix-icon="Search"
clearable
/>
<div class="menu-search">
<el-input
v-model="searchKey"
placeholder="搜索菜单"
size="large"
:prefix-icon="Search"
clearable
/>
</div>
<el-menu
ref="menuRef"
@@ -128,7 +130,6 @@ watch(searchKey, async (val) => {
<NavMenu :menu="filteredMenu" />
</el-menu>
</div>
<div class="right-view">
<router-view />
</div>
@@ -138,33 +139,51 @@ watch(searchKey, async (val) => {
<style lang="scss" scoped>
.settings-wrapper {
display: flex;
gap: 40px;
margin-top: 30px;
background-color: #fff;
padding: 0px 240px;
height: 800px;
.left-menu {
height: calc(100vh - var(--header-height));
font-size: 16px;
overflow-y: auto;
width: 240px;
height: 100%;
background-color: #fff;
flex-shrink: 0;
:deep(.el-input__wrapper) {
margin: 12px 12px 4px 12px;
border-right: 1px solid rgb(232.9, 232.9, 232.9);
position: sticky;
top: var(--header-height);
align-self: flex-start;
padding: 12px 16px;
.menu-search {
margin-bottom: 8px;
}
:deep(.el-menu) {
border-right: none;
}
:deep(.el-menu-item) {
border-radius: 8px;
margin: 4px 0px;
}
:deep(.el-menu-item.is-active) {
background-color: #d1f0ff;
color: #303133;
font-weight: 600;
}
:deep(.el-sub-menu) {
margin: 4px 0px;
}
:deep(.el-sub-menu__title) {
border-radius: 8px;
}
}
.right-view {
width: 1200px;
flex: 1;
flex-shrink: 0;
overflow-y: auto;
background-color: #fff;
border-radius: 0px;
width: 100%;
}
}
</style>
@@ -239,8 +239,7 @@ const activeCollapseNames = ref(['1', '2', '3'])
<style lang="scss" scoped>
.company-wrapper {
padding: 24px 32px;
padding-bottom: 16px;
:deep(.el-collapse) {
border: none;
}
@@ -251,8 +250,12 @@ const activeCollapseNames = ref(['1', '2', '3'])
:deep(.el-collapse-item__header) {
font-size: 18px;
font-weight: 600;
padding: 16px;
border-bottom: 1px solid #ddd;
line-height: 0;
}
:deep(.el-collapse-item__content) {
padding: 20px 20px 0px 20px;
padding-bottom: 0px;
}
@@ -299,7 +299,6 @@ function handleDelteRows(val?: CompanyUser) {
:total="page.total"
layout="prev, pager, next"
background
size="large"
@change="getCompanyUser()"
/>
</div>
@@ -320,7 +319,6 @@ function handleDelteRows(val?: CompanyUser) {
display: flex;
flex-direction: column;
height: 100%;
padding: 8px 24px 0px 24px;
:deep(.el-collapse) {
border: none;
@@ -331,10 +329,14 @@ function handleDelteRows(val?: CompanyUser) {
:deep(.el-collapse-item__header) {
font-size: 18px;
font-weight: 500;
font-weight: 600;
padding: 16px;
border-bottom: 1px solid #ddd;
line-height: 0;
}
:deep(.el-collapse-item__content) {
padding-bottom: 0px;
padding: 20px 20px 0px 20px;
}
.table-container {
@@ -342,6 +344,7 @@ function handleDelteRows(val?: CompanyUser) {
display: flex;
flex-direction: column;
min-height: 0;
padding: 0px 20px;
.table-body-wrapper {
flex: 1;
@@ -357,7 +360,7 @@ function handleDelteRows(val?: CompanyUser) {
display: flex;
align-items: center;
justify-content: space-between;
height: 56px;
margin: 20px 0px;
}
:deep(.el-table__header) {
.el-table__cell {