This commit is contained in:
oneao committed 2025-12-23 17:30:51 +08:00
1 parent 9bf7c40b70
commit 9eb5639d84
24 files changed
+915 -98

No files matched your search

@@ -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(","));
}
}
@@ -1,16 +1,17 @@
package cn.link56.post.controller;
import cn.link56.common.BasePageVO;
import cn.link56.common.Result;
import cn.link56.post.model.dto.PostAddDTO;
import cn.link56.post.model.vo.PostEditVO;
import cn.link56.post.model.vo.PostTagVO;
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.vo.EditPostVO;
import cn.link56.post.model.vo.QueryPostVO;
import cn.link56.post.service.PostService;
import cn.link56.post.service.PostTagService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.List;
@RestController
@RequestMapping("/post")
@@ -20,8 +21,20 @@ public class PostController {
private PostService postService;
@PostMapping("/add")
public Result<PostEditVO> addPost(@RequestBody @Validated PostAddDTO postAddDTO) {
PostEditVO postEditVO = postService.addPost(postAddDTO);
return Result.success(postEditVO);
public Result<EditPostVO> addPost(@RequestBody @Validated AddPostDTO addPostDTO) {
EditPostVO editPostVO = postService.addPost(addPostDTO);
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;
import cn.link56.post.model.po.PostPO;
import cn.link56.post.model.vo.QueryPostVO;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@Mapper
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
);
}
@@ -10,7 +10,7 @@ import java.util.List;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class PostAddDTO {
public class AddPostDTO {
@NotBlank(message = "帖子标题不能为空")
private String title;
@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;
}
@@ -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;
@TableField("b_publish_time")
private String publishTime;
private Date publishTime;
@TableField("b_status")
private Integer status;
@@ -1,6 +1,5 @@
package cn.link56.post.model.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@@ -11,7 +10,7 @@ import java.util.List;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class PostEditVO {
public class EditPostVO {
private Long id;
private String title;
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;
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.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.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 org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
@Service
public class PostService extends ServiceImpl<PostMapper, PostPO> {
@@ -19,21 +33,68 @@ public class PostService extends ServiceImpl<PostMapper, PostPO> {
private PostMapper postMapper;
@Transactional
public PostEditVO addPost(PostAddDTO postAddDTO) {
public EditPostVO addPost(AddPostDTO addPostDTO) {
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());
if(addPostDTO.getStatus() == PostStatusEnum.PUBLISHED.getCode()){
postPO.setPublishTime(new Date());
}
postPO.setId(IdGenerator.nextId());
postMapper.insert(postPO);
PostEditVO postEditVO = new PostEditVO();
EditPostVO editPostVO = new EditPostVO();
BeanUtils.copyProperties(postPO, postEditVO);
postEditVO.setTags(postAddDTO.getTags());
BeanUtils.copyProperties(postPO, editPostVO);
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({
title: {
type: String,
},
subTitle: {
type: String,
default: '这里什么也没有',
},
inContainer: {
type: Boolean,
@@ -28,10 +26,6 @@ const containerClass = computed(() => {
{{ title }}
</div>
<div v-if="subTitle" class="empty-subtitle">
{{ subTitle }}
</div>
<div v-if="$slots.extra" class="empty-extra">
<slot name="extra" />
</div>
@@ -70,18 +64,10 @@ const containerClass = computed(() => {
.empty-title {
margin-top: 24px;
font-size: 24px;
font-weight: 600;
color: #333;
line-height: 1.4;
}
.empty-subtitle {
margin-top: 12px;
font-size: 18px;
font-weight: 500;
color: #666;
line-height: 1.6;
max-width: 360px;
line-height: 1.4;
}
.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'
export function addPostApi(params: AddPostParams) {
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'),
},
{
path: '/settings/creator/editor',
path: '/settings/creator/editor/:id?',
component: () => import('@/views/settings/creator/editor.vue'),
},
{
@@ -1,4 +1,5 @@
html, body {
html,
body {
background-color: #f7f7f7;
}
@@ -31,6 +31,7 @@ declare module 'vue' {
'IIc:outlineSettings': typeof import('~icons/ic/outline-settings')['default']
'IIc:twotoneHeadphones': typeof import('~icons/ic/twotone-headphones')['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']
'IMajesticons:settingsCogLine': typeof import('~icons/majesticons/settings-cog-line')['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>
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 { UploadImageResponse } from '@/utils/file'
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 ImageUploadCard from '@/components/ImageUploadCard.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 { 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 formRef = ref<FormInstance>()
@@ -27,11 +33,11 @@ const tagOptions = ref<PostTag[]>([])
const statusOptions = [
{
label: '草稿',
value: 0,
value: PostStatusEnum.DRAFT,
},
{
label: '发布',
value: 1,
value: PostStatusEnum.PUBLISHED,
},
]
@@ -77,6 +83,10 @@ onMounted(() => {
loading.init = false
oldForm.value = JSON.parse(JSON.stringify(form.value))
nextTick(() => {
titleInputRef.value?.focus()
})
})
async function handleShowPushDrawer() {
@@ -124,28 +134,55 @@ async function handleSave(formEl: FormInstance | undefined) {
}
}
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) {
console.log(res)
}
else {
ElMessage.error(res.message || '保存失败,请稍后再试')
}
}).catch((err) => {
console.log(err)
ElMessage.error('保存失败,请稍后再试')
}).finally(() => {
loading.confirm = false
if (isUpdate.value) {
updatePostApi({
id: form.value.id,
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
})
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>
@@ -153,10 +190,10 @@ async function handleSave(formEl: FormInstance | undefined) {
<template>
<div class="editor-container">
<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 v-if="form.publishTime" class="time">
更新于:{{ form.publishTime }}
<div v-if="form.updateTime" class="time">
更新于:{{ form.updateTime }}
</div>
<el-button type="primary" @click="handleShowPushDrawer()">
发布
@@ -252,7 +289,7 @@ async function handleSave(formEl: FormInstance | undefined) {
align-items: center;
margin-bottom: 16px;
.editor-title-input {
width: 1000px;
flex: 1;
border: none;
font-size: 24px;
padding: 8px 0px;
@@ -262,9 +299,12 @@ async function handleSave(formEl: FormInstance | undefined) {
line-height: 1.5;
}
.toolbar-extra {
display: flex;
align-items: center;
.time {
font-size: 14px;
color: #666;
margin: 0px 16px;
}
}
}
@@ -1,10 +1,411 @@
<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>
<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>
<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>
@@ -164,7 +164,7 @@ watch(searchKey, async (val) => {
flex-shrink: 0;
overflow-y: auto;
background-color: #fff;
border-radius: 4px;
border-radius: 0px;
}
}
</style>
@@ -105,7 +105,6 @@ function handleQuery() {
getCompanyUser()
}
function handleShowEditDrawer(user: CompanyUser | null) {
editUser.value = user
showEdit.value = true
@@ -321,7 +320,7 @@ function handleDelteRows(val?: CompanyUser) {
display: flex;
flex-direction: column;
height: 100%;
padding: 16px 32px 0px 32px;
padding: 8px 24px 0px 24px;
:deep(.el-collapse) {
border: none;
@@ -41,7 +41,7 @@ export default defineConfig((configEnv) => {
],
server: {
host: '0.0.0.0',
port: 3000,
port: 8080,
open: true,
// 用于配置开发服务器启动时预加载的一些客户端文件,以提高初次加载的速度。
warmup: {