u
This commit is contained in:
1 parent
9bf7c40b70
commit
9eb5639d84
24 files changed
+915
-98
No files matched your search
+54
@@ -0,0 +1,54 @@
|
|||||||
|
package cn.link56.common.mybatis;
|
||||||
|
|
||||||
|
import org.apache.ibatis.type.BaseTypeHandler;
|
||||||
|
import org.apache.ibatis.type.JdbcType;
|
||||||
|
import org.apache.ibatis.type.MappedJdbcTypes;
|
||||||
|
import org.apache.ibatis.type.MappedTypes;
|
||||||
|
|
||||||
|
import java.sql.CallableStatement;
|
||||||
|
import java.sql.PreparedStatement;
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@MappedTypes(List.class)
|
||||||
|
@MappedJdbcTypes(JdbcType.VARCHAR)
|
||||||
|
public class StringListTypeHandler extends BaseTypeHandler<List<String>> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setNonNullParameter(
|
||||||
|
PreparedStatement ps,
|
||||||
|
int i,
|
||||||
|
List<String> parameter,
|
||||||
|
JdbcType jdbcType
|
||||||
|
) throws SQLException {
|
||||||
|
ps.setString(i, String.join(",", parameter));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getNullableResult(ResultSet rs, String columnName)
|
||||||
|
throws SQLException {
|
||||||
|
return parse(rs.getString(columnName));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getNullableResult(ResultSet rs, int columnIndex)
|
||||||
|
throws SQLException {
|
||||||
|
return parse(rs.getString(columnIndex));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getNullableResult(CallableStatement cs, int columnIndex)
|
||||||
|
throws SQLException {
|
||||||
|
return parse(cs.getString(columnIndex));
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> parse(String value) {
|
||||||
|
if (value == null || value.isEmpty()) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
return Arrays.asList(value.split(","));
|
||||||
|
}
|
||||||
|
}
|
||||||
+21
-8
@@ -1,16 +1,17 @@
|
|||||||
package cn.link56.post.controller;
|
package cn.link56.post.controller;
|
||||||
|
|
||||||
|
import cn.link56.common.BasePageVO;
|
||||||
import cn.link56.common.Result;
|
import cn.link56.common.Result;
|
||||||
import cn.link56.post.model.dto.PostAddDTO;
|
import cn.link56.post.model.dto.AddPostDTO;
|
||||||
import cn.link56.post.model.vo.PostEditVO;
|
import cn.link56.post.model.dto.QueryPostDTO;
|
||||||
import cn.link56.post.model.vo.PostTagVO;
|
import cn.link56.post.model.dto.UpdatePostDTO;
|
||||||
|
import cn.link56.post.model.vo.EditPostVO;
|
||||||
|
import cn.link56.post.model.vo.QueryPostVO;
|
||||||
import cn.link56.post.service.PostService;
|
import cn.link56.post.service.PostService;
|
||||||
import cn.link56.post.service.PostTagService;
|
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/post")
|
@RequestMapping("/post")
|
||||||
@@ -20,8 +21,20 @@ public class PostController {
|
|||||||
private PostService postService;
|
private PostService postService;
|
||||||
|
|
||||||
@PostMapping("/add")
|
@PostMapping("/add")
|
||||||
public Result<PostEditVO> addPost(@RequestBody @Validated PostAddDTO postAddDTO) {
|
public Result<EditPostVO> addPost(@RequestBody @Validated AddPostDTO addPostDTO) {
|
||||||
PostEditVO postEditVO = postService.addPost(postAddDTO);
|
EditPostVO editPostVO = postService.addPost(addPostDTO);
|
||||||
return Result.success(postEditVO);
|
return Result.success(editPostVO);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/update")
|
||||||
|
public Result<EditPostVO> updatePost(@RequestBody @Validated UpdatePostDTO updatePostDTO){
|
||||||
|
EditPostVO editPostVO = postService.updatePost(updatePostDTO);
|
||||||
|
return Result.success(editPostVO);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/query")
|
||||||
|
public Result<BasePageVO<QueryPostVO>> queryPost(@RequestBody QueryPostDTO queryPostDTO){
|
||||||
|
BasePageVO<QueryPostVO> data = postService.queryPost(queryPostDTO);
|
||||||
|
return Result.success(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,31 @@
|
|||||||
package cn.link56.post.mapper;
|
package cn.link56.post.mapper;
|
||||||
|
|
||||||
import cn.link56.post.model.po.PostPO;
|
import cn.link56.post.model.po.PostPO;
|
||||||
|
import cn.link56.post.model.vo.QueryPostVO;
|
||||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
import org.apache.ibatis.annotations.Mapper;
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@Mapper
|
@Mapper
|
||||||
public interface PostMapper extends BaseMapper<PostPO> {
|
public interface PostMapper extends BaseMapper<PostPO> {
|
||||||
|
|
||||||
|
List<QueryPostVO> queryPostPage(
|
||||||
|
@Param("offset") int offset,
|
||||||
|
@Param("limit") int limit,
|
||||||
|
@Param("userId") Long userId,
|
||||||
|
@Param("title") String title,
|
||||||
|
@Param("status") Integer status,
|
||||||
|
@Param("year") String year,
|
||||||
|
@Param("month") String month
|
||||||
|
);
|
||||||
|
|
||||||
|
Long queryPostTotal(
|
||||||
|
@Param("userId") Long userId,
|
||||||
|
@Param("title") String title,
|
||||||
|
@Param("status") Integer status,
|
||||||
|
@Param("year") String year,
|
||||||
|
@Param("month") String month
|
||||||
|
);
|
||||||
}
|
}
|
||||||
+1
-1
@@ -10,7 +10,7 @@ import java.util.List;
|
|||||||
@Data
|
@Data
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
public class PostAddDTO {
|
public class AddPostDTO {
|
||||||
@NotBlank(message = "帖子标题不能为空")
|
@NotBlank(message = "帖子标题不能为空")
|
||||||
private String title;
|
private String title;
|
||||||
@NotBlank(message = "帖子内容不能为空")
|
@NotBlank(message = "帖子内容不能为空")
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package cn.link56.post.model.dto;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class QueryPostDTO {
|
||||||
|
private Integer currentPage;
|
||||||
|
private Integer pageSize;
|
||||||
|
|
||||||
|
private Integer status;
|
||||||
|
private String title;
|
||||||
|
|
||||||
|
private String year;
|
||||||
|
private String month;
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package cn.link56.post.model.dto;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotBlank;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class UpdatePostDTO {
|
||||||
|
@NotNull(message = "消息id不能为空")
|
||||||
|
private Long id;
|
||||||
|
@NotBlank(message = "帖子标题不能为空")
|
||||||
|
private String title;
|
||||||
|
@NotBlank(message = "帖子内容不能为空")
|
||||||
|
private String content;
|
||||||
|
@NotBlank(message = "帖子摘要不能为空")
|
||||||
|
private String summary;
|
||||||
|
private String cover;
|
||||||
|
private List<String> tags;
|
||||||
|
|
||||||
|
private Integer status;
|
||||||
|
}
|
||||||
+16
@@ -0,0 +1,16 @@
|
|||||||
|
package cn.link56.post.model.enums;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor
|
||||||
|
public enum PostStatusEnum {
|
||||||
|
DRAFT(0, "草稿"),
|
||||||
|
PUBLISHED(1, "已发布"),
|
||||||
|
OFFLINE(2, "已下架");
|
||||||
|
|
||||||
|
private final int code;
|
||||||
|
private final String desc;
|
||||||
|
}
|
||||||
@@ -39,7 +39,7 @@ public class PostPO {
|
|||||||
private String cover;
|
private String cover;
|
||||||
|
|
||||||
@TableField("b_publish_time")
|
@TableField("b_publish_time")
|
||||||
private String publishTime;
|
private Date publishTime;
|
||||||
|
|
||||||
@TableField("b_status")
|
@TableField("b_status")
|
||||||
private Integer status;
|
private Integer status;
|
||||||
|
|||||||
+1
-2
@@ -1,6 +1,5 @@
|
|||||||
package cn.link56.post.model.vo;
|
package cn.link56.post.model.vo;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
@@ -11,7 +10,7 @@ import java.util.List;
|
|||||||
@Data
|
@Data
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
public class PostEditVO {
|
public class EditPostVO {
|
||||||
private Long id;
|
private Long id;
|
||||||
private String title;
|
private String title;
|
||||||
private String content;
|
private String content;
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package cn.link56.post.model.vo;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class QueryPostVO {
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
private String title;
|
||||||
|
private String cover;
|
||||||
|
private List<String> tags;
|
||||||
|
|
||||||
|
private Date updateTime;
|
||||||
|
private Date publishTime;
|
||||||
|
private Integer status;
|
||||||
|
|
||||||
|
private Integer viewCount;
|
||||||
|
private Integer likeCount;
|
||||||
|
private Integer favoriteCount;
|
||||||
|
private Integer commentCount;
|
||||||
|
}
|
||||||
@@ -1,17 +1,31 @@
|
|||||||
package cn.link56.post.service;
|
package cn.link56.post.service;
|
||||||
|
|
||||||
|
import cn.link56.common.BasePageVO;
|
||||||
import cn.link56.common.context.UserContext;
|
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.mapper.PostMapper;
|
||||||
import cn.link56.post.model.dto.PostAddDTO;
|
import cn.link56.post.model.dto.AddPostDTO;
|
||||||
|
import cn.link56.post.model.dto.QueryPostDTO;
|
||||||
|
import cn.link56.post.model.dto.UpdatePostDTO;
|
||||||
|
import cn.link56.post.model.enums.PostStatusEnum;
|
||||||
import cn.link56.post.model.po.PostPO;
|
import cn.link56.post.model.po.PostPO;
|
||||||
import cn.link56.post.model.vo.PostEditVO;
|
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 com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
|
import org.apache.commons.lang3.ObjectUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.springframework.beans.BeanUtils;
|
import org.springframework.beans.BeanUtils;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class PostService extends ServiceImpl<PostMapper, PostPO> {
|
public class PostService extends ServiceImpl<PostMapper, PostPO> {
|
||||||
@@ -19,21 +33,68 @@ public class PostService extends ServiceImpl<PostMapper, PostPO> {
|
|||||||
private PostMapper postMapper;
|
private PostMapper postMapper;
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public PostEditVO addPost(PostAddDTO postAddDTO) {
|
public EditPostVO addPost(AddPostDTO addPostDTO) {
|
||||||
PostPO postPO = new PostPO();
|
PostPO postPO = new PostPO();
|
||||||
|
|
||||||
BeanUtils.copyProperties(postAddDTO, postPO);
|
BeanUtils.copyProperties(addPostDTO, postPO);
|
||||||
|
|
||||||
postPO.setTag(StringUtils.join(postAddDTO.getTags(), ","));
|
postPO.setTag(StringUtils.join(addPostDTO.getTags(), ","));
|
||||||
postPO.setCompanyId(UserContext.getCompanyId());
|
postPO.setCompanyId(UserContext.getCompanyId());
|
||||||
|
|
||||||
|
if(addPostDTO.getStatus() == PostStatusEnum.PUBLISHED.getCode()){
|
||||||
|
postPO.setPublishTime(new Date());
|
||||||
|
}
|
||||||
|
|
||||||
|
postPO.setId(IdGenerator.nextId());
|
||||||
postMapper.insert(postPO);
|
postMapper.insert(postPO);
|
||||||
|
|
||||||
PostEditVO postEditVO = new PostEditVO();
|
EditPostVO editPostVO = new EditPostVO();
|
||||||
|
|
||||||
BeanUtils.copyProperties(postPO, postEditVO);
|
BeanUtils.copyProperties(postPO, editPostVO);
|
||||||
postEditVO.setTags(postAddDTO.getTags());
|
editPostVO.setTags(addPostDTO.getTags());
|
||||||
|
|
||||||
return postEditVO;
|
return editPostVO;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BasePageVO<QueryPostVO> queryPost(QueryPostDTO queryPostDTO) {
|
||||||
|
Integer currentPage = queryPostDTO.getCurrentPage();
|
||||||
|
Integer pageSize = queryPostDTO.getPageSize();
|
||||||
|
|
||||||
|
String title = queryPostDTO.getTitle();
|
||||||
|
Integer status = queryPostDTO.getStatus();
|
||||||
|
String year = queryPostDTO.getYear();
|
||||||
|
String month = queryPostDTO.getMonth();
|
||||||
|
|
||||||
|
int offset = (currentPage - 1) * pageSize + 1;
|
||||||
|
int limit = currentPage * pageSize;
|
||||||
|
|
||||||
|
List<QueryPostVO> queryPostVOS = postMapper.queryPostPage(offset, limit, UserContext.getUserId(), title, status, year, month);
|
||||||
|
Long total = postMapper.queryPostTotal(UserContext.getUserId(), title, status, year, month);
|
||||||
|
|
||||||
|
BasePageVO<QueryPostVO> result = new BasePageVO<>();
|
||||||
|
result.setRecords(queryPostVOS);
|
||||||
|
result.setTotal(total);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public EditPostVO updatePost(UpdatePostDTO updatePostDTO) {
|
||||||
|
Long id = updatePostDTO.getId();
|
||||||
|
|
||||||
|
PostPO postPO = postMapper.selectById(id);
|
||||||
|
|
||||||
|
System.out.println(postPO);
|
||||||
|
|
||||||
|
BeanUtils.copyProperties(updatePostDTO,postPO);
|
||||||
|
postPO.setTag(StringUtils.join(updatePostDTO.getTags(), ","));
|
||||||
|
|
||||||
|
postMapper.updateById(postPO);
|
||||||
|
|
||||||
|
EditPostVO editPostVO = new EditPostVO();
|
||||||
|
|
||||||
|
BeanUtils.copyProperties(postPO, editPostVO);
|
||||||
|
editPostVO.setTags(updatePostDTO.getTags());
|
||||||
|
|
||||||
|
return editPostVO;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<!DOCTYPE mapper
|
||||||
|
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
|
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
|
<mapper namespace="cn.link56.post.mapper.PostMapper">
|
||||||
|
|
||||||
|
<resultMap id="QueryPostVOMap"
|
||||||
|
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
|
||||||
|
property="tags"
|
||||||
|
column="tagsString"
|
||||||
|
typeHandler="cn.link56.common.mybatis.StringListTypeHandler"/>
|
||||||
|
|
||||||
|
<result property="publishTime" column="publishTime"/>
|
||||||
|
<result property="updateTime" column="updateTime"/>
|
||||||
|
<result property="status" column="status"/>
|
||||||
|
|
||||||
|
<result property="viewCount" column="viewCount"/>
|
||||||
|
<result property="likeCount" column="likeCount"/>
|
||||||
|
<result property="favoriteCount" column="favoriteCount"/>
|
||||||
|
<result property="commentCount" column="commentCount"/>
|
||||||
|
</resultMap>
|
||||||
|
|
||||||
|
<select id="queryPostPage" resultMap="QueryPostVOMap">
|
||||||
|
SELECT *
|
||||||
|
FROM (
|
||||||
|
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,
|
||||||
|
p.b_status AS status,
|
||||||
|
|
||||||
|
(SELECT COUNT(*) FROM b_post_view v WHERE v.b_post_id = p.b_id) AS viewCount,
|
||||||
|
(SELECT COUNT(*) FROM b_post_like l WHERE l.b_post_id = p.b_id) AS likeCount,
|
||||||
|
(SELECT COUNT(*) FROM b_post_favorite f WHERE f.b_post_id = p.b_id) AS favoriteCount,
|
||||||
|
(SELECT COUNT(*) FROM b_post_comment c WHERE c.b_post_id = p.b_id) AS commentCount,
|
||||||
|
|
||||||
|
ROW_NUMBER() OVER (ORDER BY p.b_update_time DESC) AS rn
|
||||||
|
FROM b_post p
|
||||||
|
<where>
|
||||||
|
<if test="userId != null">
|
||||||
|
AND p.b_create_by = #{userId}
|
||||||
|
</if>
|
||||||
|
|
||||||
|
<if test="status != null and status != -1">
|
||||||
|
AND p.b_status = #{status}
|
||||||
|
</if>
|
||||||
|
|
||||||
|
<if test="title != null and title != ''">
|
||||||
|
AND p.b_title LIKE '%' + #{title} + '%'
|
||||||
|
</if>
|
||||||
|
|
||||||
|
<if test="year != null and year != ''">
|
||||||
|
AND YEAR(p.b_update_time) = #{year}
|
||||||
|
</if>
|
||||||
|
|
||||||
|
<if test="month != null and month != ''">
|
||||||
|
AND MONTH(p.b_update_time) = #{month}
|
||||||
|
</if>
|
||||||
|
</where>
|
||||||
|
) t
|
||||||
|
WHERE t.rn BETWEEN #{offset} AND #{limit}
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<select id="queryPostTotal" resultType="java.lang.Long">
|
||||||
|
SELECT COUNT(*)
|
||||||
|
FROM b_post p
|
||||||
|
<where>
|
||||||
|
<if test="userId != null">
|
||||||
|
AND p.b_create_by = #{userId}
|
||||||
|
</if>
|
||||||
|
|
||||||
|
<if test="status != null and status != -1">
|
||||||
|
AND p.b_status = #{status}
|
||||||
|
</if>
|
||||||
|
|
||||||
|
<if test="title != null and title != ''">
|
||||||
|
AND p.b_title LIKE '%' + #{title} + '%'
|
||||||
|
</if>
|
||||||
|
|
||||||
|
<if test="year != null and year != ''">
|
||||||
|
AND YEAR(p.b_update_time) = #{year}
|
||||||
|
</if>
|
||||||
|
|
||||||
|
<if test="month != null and month != ''">
|
||||||
|
AND MONTH(p.b_update_time) = #{month}
|
||||||
|
</if>
|
||||||
|
</where>
|
||||||
|
</select>
|
||||||
|
</mapper>
|
||||||
@@ -4,9 +4,7 @@ import { computed } from 'vue'
|
|||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
title: {
|
title: {
|
||||||
type: String,
|
type: String,
|
||||||
},
|
default: '这里什么也没有',
|
||||||
subTitle: {
|
|
||||||
type: String,
|
|
||||||
},
|
},
|
||||||
inContainer: {
|
inContainer: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
@@ -28,10 +26,6 @@ const containerClass = computed(() => {
|
|||||||
{{ title }}
|
{{ title }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="subTitle" class="empty-subtitle">
|
|
||||||
{{ subTitle }}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-if="$slots.extra" class="empty-extra">
|
<div v-if="$slots.extra" class="empty-extra">
|
||||||
<slot name="extra" />
|
<slot name="extra" />
|
||||||
</div>
|
</div>
|
||||||
@@ -70,18 +64,10 @@ const containerClass = computed(() => {
|
|||||||
|
|
||||||
.empty-title {
|
.empty-title {
|
||||||
margin-top: 24px;
|
margin-top: 24px;
|
||||||
font-size: 24px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #333;
|
|
||||||
line-height: 1.4;
|
|
||||||
}
|
|
||||||
|
|
||||||
.empty-subtitle {
|
|
||||||
margin-top: 12px;
|
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
|
font-weight: 500;
|
||||||
color: #666;
|
color: #666;
|
||||||
line-height: 1.6;
|
line-height: 1.4;
|
||||||
max-width: 360px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.empty-extra {
|
.empty-extra {
|
||||||
|
|||||||
@@ -1,6 +1,15 @@
|
|||||||
import type { AddPostParams, PostEditorForm } from '@/types/post'
|
import type { AddPostParams, Post, PostEditorForm, QueryPostParams, UpdatePostParams } from '@/types/post/editor'
|
||||||
|
|
||||||
import { httpGet, httpPostJson } from '@/request/utils'
|
import { httpGet, httpPostJson } from '@/request/utils'
|
||||||
|
|
||||||
export function addPostApi(params: AddPostParams) {
|
export function addPostApi(params: AddPostParams) {
|
||||||
return httpPostJson<PostEditorForm>('/post/add', params)
|
return httpPostJson<PostEditorForm>('/post/add', params)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function updatePostApi(params: UpdatePostParams) {
|
||||||
|
return httpPostJson<PostEditorForm>('/post/update', params)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function queryPostApi(params: QueryPostParams) {
|
||||||
|
return httpPostJson<BasePageResult<Post>>('/post/query', params)
|
||||||
|
}
|
||||||
@@ -52,7 +52,7 @@ const router = createRouter({
|
|||||||
component: () => import('@/views/settings/creator/home.vue'),
|
component: () => import('@/views/settings/creator/home.vue'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/settings/creator/editor',
|
path: '/settings/creator/editor/:id?',
|
||||||
component: () => import('@/views/settings/creator/editor.vue'),
|
component: () => import('@/views/settings/creator/editor.vue'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
html, body {
|
html,
|
||||||
|
body {
|
||||||
background-color: #f7f7f7;
|
background-color: #f7f7f7;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ declare module 'vue' {
|
|||||||
'IIc:outlineSettings': typeof import('~icons/ic/outline-settings')['default']
|
'IIc:outlineSettings': typeof import('~icons/ic/outline-settings')['default']
|
||||||
'IIc:twotoneHeadphones': typeof import('~icons/ic/twotone-headphones')['default']
|
'IIc:twotoneHeadphones': typeof import('~icons/ic/twotone-headphones')['default']
|
||||||
'IIconoir:infoCircleSolid': typeof import('~icons/iconoir/info-circle-solid')['default']
|
'IIconoir:infoCircleSolid': typeof import('~icons/iconoir/info-circle-solid')['default']
|
||||||
|
'IIon:iosMore': typeof import('~icons/ion/ios-more')['default']
|
||||||
ImageUploadCard: typeof import('./../components/ImageUploadCard.vue')['default']
|
ImageUploadCard: typeof import('./../components/ImageUploadCard.vue')['default']
|
||||||
'IMajesticons:settingsCogLine': typeof import('~icons/majesticons/settings-cog-line')['default']
|
'IMajesticons:settingsCogLine': typeof import('~icons/majesticons/settings-cog-line')['default']
|
||||||
'IMaterialSymbols:call': typeof import('~icons/material-symbols/call')['default']
|
'IMaterialSymbols:call': typeof import('~icons/material-symbols/call')['default']
|
||||||
|
|||||||
@@ -0,0 +1,63 @@
|
|||||||
|
export enum PostStatusEnum {
|
||||||
|
DRAFT = 0, // 草稿
|
||||||
|
PUBLISHED = 1, // 已发布
|
||||||
|
OFFLINE = 2, // 已下架
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PostEditorForm {
|
||||||
|
id: number
|
||||||
|
title: string
|
||||||
|
content: string
|
||||||
|
summary: string
|
||||||
|
cover: string
|
||||||
|
tags: string[]
|
||||||
|
status: number
|
||||||
|
publishTime: string
|
||||||
|
updateTime: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AddPostParams {
|
||||||
|
title: string
|
||||||
|
content: string
|
||||||
|
summary: string
|
||||||
|
cover: string
|
||||||
|
status: number
|
||||||
|
tags: string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpdatePostParams {
|
||||||
|
id: number
|
||||||
|
title: string
|
||||||
|
content: string
|
||||||
|
summary: string
|
||||||
|
cover: string
|
||||||
|
status: number
|
||||||
|
tags: string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface QueryPostParams {
|
||||||
|
currentPage: number
|
||||||
|
pageSize: number
|
||||||
|
|
||||||
|
status: number
|
||||||
|
title: string
|
||||||
|
|
||||||
|
year: string
|
||||||
|
month: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Post {
|
||||||
|
id: number
|
||||||
|
title: string
|
||||||
|
cover: string
|
||||||
|
tags: string[]
|
||||||
|
|
||||||
|
status: number
|
||||||
|
updateTime: string
|
||||||
|
publishTime: string
|
||||||
|
|
||||||
|
viewCount: number
|
||||||
|
likeCount: number
|
||||||
|
favoriteCount: number
|
||||||
|
commentCount: number
|
||||||
|
}
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
export interface PostEditorForm {
|
|
||||||
id: number
|
|
||||||
title: string
|
|
||||||
content: string
|
|
||||||
summary: string
|
|
||||||
cover: string
|
|
||||||
tags: string[]
|
|
||||||
status: number
|
|
||||||
publishTime: string
|
|
||||||
updateTime: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface AddPostParams {
|
|
||||||
title: string
|
|
||||||
content: string
|
|
||||||
summary: string
|
|
||||||
cover: string
|
|
||||||
status: number
|
|
||||||
tags: string[]
|
|
||||||
}
|
|
||||||
@@ -1,17 +1,23 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { FormInstance } from 'element-plus'
|
import type { FormInstance } from 'element-plus'
|
||||||
import type { PostEditorForm } from '@/types/post'
|
import type { PostEditorForm } from '@/types/post/editor'
|
||||||
import type { PostTag } from '@/types/post/tag'
|
import type { PostTag } from '@/types/post/tag'
|
||||||
import type { UploadImageResponse } from '@/utils/file'
|
import type { UploadImageResponse } from '@/utils/file'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
import { computed, onMounted, reactive, ref, watch } from 'vue'
|
import { computed, nextTick, onMounted, reactive, ref } from 'vue'
|
||||||
|
import { useRoute } from 'vue-router'
|
||||||
import CustomLoading from '@/components/CustomLoading.vue'
|
import CustomLoading from '@/components/CustomLoading.vue'
|
||||||
import ImageUploadCard from '@/components/ImageUploadCard.vue'
|
import ImageUploadCard from '@/components/ImageUploadCard.vue'
|
||||||
import WangEditor from '@/components/wang-editor/index.vue'
|
import WangEditor from '@/components/wang-editor/index.vue'
|
||||||
import { addPostApi } from '@/request/api/post'
|
import { addPostApi, updatePostApi } from '@/request/api/post'
|
||||||
import { getPostTagApi } from '@/request/api/post/tag'
|
import { getPostTagApi } from '@/request/api/post/tag'
|
||||||
import { uploadImage, uploadToAliyun } from '@/utils/file'
|
import { PostStatusEnum } from '@/types/post/editor'
|
||||||
|
import { uploadToAliyun } from '@/utils/file'
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
const id = route.params.id as string | undefined
|
||||||
|
|
||||||
|
const titleInputRef = ref<HTMLInputElement | null>(null)
|
||||||
const wangEditorRef = ref<any>(null)
|
const wangEditorRef = ref<any>(null)
|
||||||
const formRef = ref<FormInstance>()
|
const formRef = ref<FormInstance>()
|
||||||
|
|
||||||
@@ -27,11 +33,11 @@ const tagOptions = ref<PostTag[]>([])
|
|||||||
const statusOptions = [
|
const statusOptions = [
|
||||||
{
|
{
|
||||||
label: '草稿',
|
label: '草稿',
|
||||||
value: 0,
|
value: PostStatusEnum.DRAFT,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '发布',
|
label: '发布',
|
||||||
value: 1,
|
value: PostStatusEnum.PUBLISHED,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -77,6 +83,10 @@ onMounted(() => {
|
|||||||
loading.init = false
|
loading.init = false
|
||||||
|
|
||||||
oldForm.value = JSON.parse(JSON.stringify(form.value))
|
oldForm.value = JSON.parse(JSON.stringify(form.value))
|
||||||
|
|
||||||
|
nextTick(() => {
|
||||||
|
titleInputRef.value?.focus()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
async function handleShowPushDrawer() {
|
async function handleShowPushDrawer() {
|
||||||
@@ -124,28 +134,55 @@ async function handleSave(formEl: FormInstance | undefined) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
addPostApi({
|
if (isUpdate.value) {
|
||||||
title: form.value.title,
|
updatePostApi({
|
||||||
content: form.value.content,
|
id: form.value.id,
|
||||||
summary: form.value.summary,
|
title: form.value.title,
|
||||||
status: form.value.status,
|
content: form.value.content,
|
||||||
cover: form.value.cover,
|
summary: form.value.summary,
|
||||||
tags: form.value.tags,
|
status: form.value.status,
|
||||||
}).then((res) => {
|
cover: form.value.cover,
|
||||||
if (res.isSuccess) {
|
tags: form.value.tags,
|
||||||
console.log(res)
|
}).then((res) => {
|
||||||
}
|
if (res.isSuccess) {
|
||||||
else {
|
form.value = res.data
|
||||||
ElMessage.error(res.message || '保存失败,请稍后再试')
|
}
|
||||||
}
|
else {
|
||||||
}).catch((err) => {
|
ElMessage.error(res.message || '更新失败,请稍后再试')
|
||||||
console.log(err)
|
}
|
||||||
ElMessage.error('保存失败,请稍后再试')
|
}).catch((err) => {
|
||||||
}).finally(() => {
|
console.log(err)
|
||||||
loading.confirm = false
|
ElMessage.error('更新失败,请稍后再试')
|
||||||
|
}).finally(() => {
|
||||||
|
loading.confirm = false
|
||||||
|
|
||||||
showPushDrawer.value = false
|
showPushDrawer.value = false
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
addPostApi({
|
||||||
|
title: form.value.title,
|
||||||
|
content: form.value.content,
|
||||||
|
summary: form.value.summary,
|
||||||
|
status: form.value.status,
|
||||||
|
cover: form.value.cover,
|
||||||
|
tags: form.value.tags,
|
||||||
|
}).then((res) => {
|
||||||
|
if (res.isSuccess) {
|
||||||
|
form.value = res.data
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ElMessage.error(res.message || '保存失败,请稍后再试')
|
||||||
|
}
|
||||||
|
}).catch((err) => {
|
||||||
|
console.log(err)
|
||||||
|
ElMessage.error('保存失败,请稍后再试')
|
||||||
|
}).finally(() => {
|
||||||
|
loading.confirm = false
|
||||||
|
|
||||||
|
showPushDrawer.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@@ -153,10 +190,10 @@ async function handleSave(formEl: FormInstance | undefined) {
|
|||||||
<template>
|
<template>
|
||||||
<div class="editor-container">
|
<div class="editor-container">
|
||||||
<div class="toolbar">
|
<div class="toolbar">
|
||||||
<input v-model="form.title" placeholder="输入标题" class="editor-title-input" maxlength="100">
|
<input ref="titleInputRef" v-model="form.title" placeholder="输入标题" class="editor-title-input" maxlength="100">
|
||||||
<div class="toolbar-extra">
|
<div class="toolbar-extra">
|
||||||
<div v-if="form.publishTime" class="time">
|
<div v-if="form.updateTime" class="time">
|
||||||
更新于:{{ form.publishTime }}
|
更新于:{{ form.updateTime }}
|
||||||
</div>
|
</div>
|
||||||
<el-button type="primary" @click="handleShowPushDrawer()">
|
<el-button type="primary" @click="handleShowPushDrawer()">
|
||||||
发布
|
发布
|
||||||
@@ -252,7 +289,7 @@ async function handleSave(formEl: FormInstance | undefined) {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
.editor-title-input {
|
.editor-title-input {
|
||||||
width: 1000px;
|
flex: 1;
|
||||||
border: none;
|
border: none;
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
padding: 8px 0px;
|
padding: 8px 0px;
|
||||||
@@ -262,9 +299,12 @@ async function handleSave(formEl: FormInstance | undefined) {
|
|||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
}
|
}
|
||||||
.toolbar-extra {
|
.toolbar-extra {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
.time {
|
.time {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: #666;
|
color: #666;
|
||||||
|
margin: 0px 16px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,411 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import type { TabsPaneContext } from 'element-plus'
|
||||||
|
import type { Post, QueryPostParams } from '@/types/post/editor'
|
||||||
|
import { Delete, Plus, Refresh, Search } from '@element-plus/icons-vue'
|
||||||
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
|
import { onMounted, reactive, ref } from 'vue'
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
|
import CustomLoading from '@/components/CustomLoading.vue'
|
||||||
|
import SvgIcon from '@/components/SvgIcon.vue'
|
||||||
|
import { queryPostApi } from '@/request/api/post'
|
||||||
|
import { PostStatusEnum } from '@/types/post/editor'
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
const loading = ref<boolean>(false)
|
||||||
|
|
||||||
|
interface OptionItem {
|
||||||
|
label: string
|
||||||
|
value: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const yearOptions = ref<OptionItem[]>([])
|
||||||
|
const monthOptions = ref<OptionItem[]>([])
|
||||||
|
|
||||||
|
// 生成选项
|
||||||
|
function initYearMonthOptions() {
|
||||||
|
const currentYear = new Date().getFullYear()
|
||||||
|
|
||||||
|
// 年份:不限 + 近 5 年(含今年)
|
||||||
|
yearOptions.value = [
|
||||||
|
{ label: '不限', value: '' },
|
||||||
|
...Array.from({ length: 5 }).map((_, index) => {
|
||||||
|
const year = currentYear - index
|
||||||
|
return {
|
||||||
|
label: String(year),
|
||||||
|
value: String(year),
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
|
||||||
|
// 月份:不限 + 1~12 月
|
||||||
|
monthOptions.value = [
|
||||||
|
{ label: '不限', value: '' },
|
||||||
|
...Array.from({ length: 12 }).map((_, index) => {
|
||||||
|
const month = index + 1
|
||||||
|
return {
|
||||||
|
label: `${month}月`,
|
||||||
|
value: String(month),
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
const postStatusOptions = [
|
||||||
|
{
|
||||||
|
label: '全部',
|
||||||
|
value: -1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '草稿',
|
||||||
|
value: PostStatusEnum.DRAFT,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '已发布',
|
||||||
|
value: PostStatusEnum.PUBLISHED,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '已下架',
|
||||||
|
value: PostStatusEnum.OFFLINE,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
const page = reactive<Pagination>({
|
||||||
|
current: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
total: 0,
|
||||||
|
})
|
||||||
|
|
||||||
|
const queryForm = reactive<QueryPostParams>({
|
||||||
|
currentPage: page.current,
|
||||||
|
pageSize: page.pageSize,
|
||||||
|
|
||||||
|
title: '',
|
||||||
|
status: -1,
|
||||||
|
year: '',
|
||||||
|
month: '',
|
||||||
|
})
|
||||||
|
|
||||||
|
const tableData = ref<Post[]>([])
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
initYearMonthOptions()
|
||||||
|
queryPost()
|
||||||
|
})
|
||||||
|
|
||||||
|
function queryPost() {
|
||||||
|
loading.value = true
|
||||||
|
queryPostApi({
|
||||||
|
...queryForm,
|
||||||
|
currentPage: page.current,
|
||||||
|
pageSize: page.pageSize,
|
||||||
|
}).then((res) => {
|
||||||
|
if (res.isSuccess) {
|
||||||
|
tableData.value = res.data.records
|
||||||
|
page.total = res.data.total
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ElMessage.error(res.message || '查询失败,请稍后再试')
|
||||||
|
}
|
||||||
|
}).catch((err) => {
|
||||||
|
console.log(err)
|
||||||
|
ElMessage.error('查询失败,请稍后再试')
|
||||||
|
}).finally(() => {
|
||||||
|
loading.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSearch() {
|
||||||
|
page.current = 1
|
||||||
|
|
||||||
|
queryPost()
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleTabChange() {
|
||||||
|
page.current = 1
|
||||||
|
|
||||||
|
queryForm.title = ''
|
||||||
|
queryForm.year = ''
|
||||||
|
queryForm.month = ''
|
||||||
|
|
||||||
|
queryPost()
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleDeleteRow(row: Post) {
|
||||||
|
ElMessageBox.confirm('删除内容后不可恢复,确定删除吗?', '删除提示', {
|
||||||
|
confirmButtonText: '确认',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning',
|
||||||
|
}).then(() => {
|
||||||
|
// 确认删除
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleToEdit(row: Post) {
|
||||||
|
router.push({
|
||||||
|
path: `/settings/creator/editor/${row.id}`,
|
||||||
|
})
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="" />
|
<div class="post-wrapper">
|
||||||
|
<div class="post-header">
|
||||||
|
<el-tabs v-model="queryForm.status" @tab-change="handleTabChange">
|
||||||
|
<el-tab-pane v-for="item in postStatusOptions" :key="item.value" :label="item.label" :name="item.value" />
|
||||||
|
</el-tabs>
|
||||||
|
<div class="post-query">
|
||||||
|
<el-form :model="queryForm">
|
||||||
|
<el-row :gutter="16">
|
||||||
|
<el-col :span="6">
|
||||||
|
<el-form-item label="年份">
|
||||||
|
<el-select v-model="queryForm.year" @change="handleSearch()">
|
||||||
|
<el-option
|
||||||
|
v-for="item in yearOptions"
|
||||||
|
:key="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="6">
|
||||||
|
<el-form-item label="月份">
|
||||||
|
<el-select v-model="queryForm.month" @change="handleSearch()">
|
||||||
|
<el-option
|
||||||
|
v-for="item in monthOptions"
|
||||||
|
:key="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="4" :offset="8">
|
||||||
|
<el-input
|
||||||
|
v-model="queryForm.title"
|
||||||
|
placeholder="请输入标题搜索"
|
||||||
|
:prefix-icon="Search"
|
||||||
|
@keyup.enter="handleSearch"
|
||||||
|
/>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="post-content">
|
||||||
|
<CustomLoading v-if="loading" :in-container="true" />
|
||||||
|
|
||||||
|
<div v-else-if="tableData.length > 0" class="post-list">
|
||||||
|
<div v-for="item in tableData" :key="item.id" class="post-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">
|
||||||
|
{{ item.title }}
|
||||||
|
</div>
|
||||||
|
<div class="post-actions">
|
||||||
|
<el-dropdown>
|
||||||
|
<div class="post-action">
|
||||||
|
<SvgIcon icon="ion:ios-more" />
|
||||||
|
</div>
|
||||||
|
<template #dropdown>
|
||||||
|
<el-dropdown-item @click="handleToEdit(item)">
|
||||||
|
编辑
|
||||||
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item v-if="item.status !== PostStatusEnum.DRAFT">
|
||||||
|
移入草稿箱
|
||||||
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item @click="handleDeleteRow(item)">
|
||||||
|
删除
|
||||||
|
</el-dropdown-item>
|
||||||
|
</template>
|
||||||
|
</el-dropdown>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="post-meta">
|
||||||
|
<div class="post-meta-message">
|
||||||
|
<div>{{ item.updateTime }}</div>
|
||||||
|
<div class="post-meta-divider" />
|
||||||
|
<div>{{ item.viewCount }} 浏览</div>
|
||||||
|
<div>{{ item.likeCount }} 点赞</div>
|
||||||
|
<div>{{ item.commentCount }} 评论</div>
|
||||||
|
<div>{{ item.favoriteCount }} 收藏</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="item.tags && item.tags.length > 0" class="post-tags">
|
||||||
|
<div v-for="tag in item.tags" :key="tag" class="post-tag">
|
||||||
|
{{ tag }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<CustomEmpty v-else :in-container="true">
|
||||||
|
<template #extra>
|
||||||
|
<el-button type="primary">
|
||||||
|
开始创作
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</CustomEmpty>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="post-footer">
|
||||||
|
<el-pagination
|
||||||
|
v-model:current-page="page.current"
|
||||||
|
:page-size="page.pageSize"
|
||||||
|
:total="page.total"
|
||||||
|
layout="prev, pager, next"
|
||||||
|
background
|
||||||
|
@change="queryPost()"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
//
|
.post-wrapper {
|
||||||
|
padding: 0px 16px;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.post-content {
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-footer {
|
||||||
|
height: 56px;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
border-top: 1px solid #e5e7eb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-item {
|
||||||
|
padding: 12px 16px;
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
min-height: 60px;
|
||||||
|
background-color: #ffffff;
|
||||||
|
border-bottom: 1px solid #e5e7eb;
|
||||||
|
box-sizing: content-box;
|
||||||
|
|
||||||
|
.post-cover {
|
||||||
|
height: 80px;
|
||||||
|
width: 140px;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
img {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-content {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
.post-main {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
font-size: 16px;
|
||||||
|
|
||||||
|
.post-title {
|
||||||
|
width: 800px;
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: bold;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-actions {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
.post-action {
|
||||||
|
font-size: 24px;
|
||||||
|
outline: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-action:hover {
|
||||||
|
color: #165dff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-summary {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #555666;
|
||||||
|
word-break: break-word;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
-webkit-line-clamp: 2; // 限制为两行
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-meta {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #6b7280;
|
||||||
|
|
||||||
|
.post-meta-message {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-meta-divider {
|
||||||
|
width: 1px;
|
||||||
|
height: 1.1em;
|
||||||
|
margin-top: 3px;
|
||||||
|
background-color: #d1d5db;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-tags {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
|
||||||
|
.post-tag {
|
||||||
|
background-color: #165dff;
|
||||||
|
border-radius: 2px;
|
||||||
|
max-width: 120px;
|
||||||
|
font-size: 12px;
|
||||||
|
padding: 4px 8px;
|
||||||
|
text-align: center;
|
||||||
|
color: #ffffff;
|
||||||
|
display: inline-block;
|
||||||
|
line-height: 12px;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-item:hover {
|
||||||
|
background-color: #fafafa;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -164,7 +164,7 @@ watch(searchKey, async (val) => {
|
|||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
border-radius: 4px;
|
border-radius: 0px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -105,7 +105,6 @@ function handleQuery() {
|
|||||||
getCompanyUser()
|
getCompanyUser()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function handleShowEditDrawer(user: CompanyUser | null) {
|
function handleShowEditDrawer(user: CompanyUser | null) {
|
||||||
editUser.value = user
|
editUser.value = user
|
||||||
showEdit.value = true
|
showEdit.value = true
|
||||||
@@ -321,7 +320,7 @@ function handleDelteRows(val?: CompanyUser) {
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
padding: 16px 32px 0px 32px;
|
padding: 8px 24px 0px 24px;
|
||||||
|
|
||||||
:deep(.el-collapse) {
|
:deep(.el-collapse) {
|
||||||
border: none;
|
border: none;
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ export default defineConfig((configEnv) => {
|
|||||||
],
|
],
|
||||||
server: {
|
server: {
|
||||||
host: '0.0.0.0',
|
host: '0.0.0.0',
|
||||||
port: 3000,
|
port: 8080,
|
||||||
open: true,
|
open: true,
|
||||||
// 用于配置开发服务器启动时预加载的一些客户端文件,以提高初次加载的速度。
|
// 用于配置开发服务器启动时预加载的一些客户端文件,以提高初次加载的速度。
|
||||||
warmup: {
|
warmup: {
|
||||||
|
|||||||
Reference in new issue
Block a user