diff --git a/src/api/Article.ts b/src/api/Article.ts index ea38870..a0609ee 100644 --- a/src/api/Article.ts +++ b/src/api/Article.ts @@ -1,5 +1,5 @@ import Request from "@/utils/request"; -import { Article } from "@/types/app/article"; +import { Article, FilterArticle } from "@/types/app/article"; // 新增文章 export const addArticleDataAPI = (data: Article) => @@ -17,7 +17,7 @@ export const editArticleDataAPI = (data: Article) => export const getArticleDataAPI = (id?: number) => Request
("GET", `/article/${id}`) // 获取文章列表 -export const getArticleListAPI = (data?: QueryData) => Request("POST", `/article/list`, { +export const getArticleListAPI = (data?: QueryData) => Request("POST", `/article/list`, { data: { ...data?.query }, params: { sort: data?.sort, diff --git a/src/components/RouteList/index.tsx b/src/components/RouteList/index.tsx index 72a8952..6a6f310 100644 --- a/src/components/RouteList/index.tsx +++ b/src/components/RouteList/index.tsx @@ -21,6 +21,7 @@ import Page from '@/pages/Route'; import Role from '@/pages/Role'; import Login from "@/pages/Login"; import Work from "@/pages/Work"; +import Draft from "@/pages/Draft"; import PageTitle from "../PageTitle"; @@ -55,6 +56,7 @@ export default () => { { path: "/file", title: "文件管理", component: }, { path: "/iter", title: "项目更新记录", component: }, { path: "/work", title: "工作台", component: }, + { path: "/draft", title: "草稿箱", component: }, ]; const [routes, setRoutes] = useState(null); diff --git a/src/components/Sidebar/index.tsx b/src/components/Sidebar/index.tsx index 05e2831..53d02f4 100644 --- a/src/components/Sidebar/index.tsx +++ b/src/components/Sidebar/index.tsx @@ -2,9 +2,10 @@ import React, { useEffect, useRef, useState } from 'react'; import { NavLink, useLocation } from 'react-router-dom'; import SidebarLinkGroup from './SidebarLinkGroup'; -import { BiEditAlt, BiFolderOpen, BiHomeSmile, BiSliderAlt, BiShieldQuarter, BiLineChart, BiCategoryAlt, BiBug } from "react-icons/bi"; +import { BiEditAlt, BiFolderOpen, BiHomeSmile, BiSliderAlt, BiShieldQuarter, BiCategoryAlt, BiBug } from "react-icons/bi"; import { LiaRssSolid } from "react-icons/lia"; import { TbBrandAirtable } from "react-icons/tb"; +import { RiDraftLine } from "react-icons/ri"; import { useUserStore } from '@/stores'; import { getRouteListAPI } from '@/api/Role' @@ -104,6 +105,12 @@ const Sidebar = ({ sidebarOpen, setSidebarOpen }: SidebarProps) => { icon: , name: "创作" }, + { + to: "/draft", + path: "draft", + icon: , + name: "草稿箱" + }, { to: "#", path: "manage", diff --git a/src/pages/Article/index.tsx b/src/pages/Article/index.tsx index 8cb27df..ac9d960 100644 --- a/src/pages/Article/index.tsx +++ b/src/pages/Article/index.tsx @@ -132,9 +132,9 @@ const ArticlePage = () => { fixed: 'right', align: 'center', render: (text: string, record: Article) => ( -
+
- + delArticleData(record.id!)}> @@ -150,6 +150,7 @@ const ArticlePage = () => { key: values.title, cateIds: values.cateIds, tagId: values.tagId, + isDraft: 0, startDate: values.createTime && values.createTime[0].valueOf() + '', endDate: values.createTime && values.createTime[1].valueOf() + '' } diff --git a/src/pages/Create/components/PublishForm/index.tsx b/src/pages/Create/components/PublishForm/index.tsx index 0cbc254..f874217 100644 --- a/src/pages/Create/components/PublishForm/index.tsx +++ b/src/pages/Create/components/PublishForm/index.tsx @@ -30,6 +30,7 @@ interface FieldType { const PublishForm = ({ data, closeModel }: { data: Article, closeModel: () => void }) => { const [params] = useSearchParams() const id = +params.get('id')! + const isDraftParams = Boolean(params.get('draft')) const [btnLoading, setBtnLoading] = useState(false) @@ -51,7 +52,7 @@ const PublishForm = ({ data, closeModel }: { data: Article, closeModel: () => vo } }); - const tagIds = data.tagList.map(item => item.id) + const tagIds = data.tagList!.map(item => item.id) form.setFieldsValue({ ...data, @@ -83,7 +84,7 @@ const PublishForm = ({ data, closeModel }: { data: Article, closeModel: () => vo return !value || /^(https?:\/\/)/.test(value) ? Promise.resolve() : Promise.reject(new Error('请输入有效的封面链接')); }; - const onSubmit: FormProps['onFinish'] = async (values) => { + const onSubmit = async (values: FieldType, isDraft?: boolean) => { setBtnLoading(true) // 如果是文章标签,则先判断是否存在,如果不存在则添加 @@ -112,7 +113,7 @@ const PublishForm = ({ data, closeModel }: { data: Article, closeModel: () => vo values.createTime = values.createTime.valueOf() values.cateIds = [...new Set(values.cateIds?.flat())] - if (id) { + if (id && !isDraftParams) { await editArticleDataAPI({ id, ...values, @@ -120,32 +121,48 @@ const PublishForm = ({ data, closeModel }: { data: Article, closeModel: () => vo tagIds: tagIds.join(','), config: { status: values.status, - top: values.top ? 1 : 0, password: values.password } } as any) message.success("🎉 编辑成功") } else { - await addArticleDataAPI({ - id, - ...values, - content: data.content, - tagIds: tagIds.join(','), - config: { - status: values.status, - top: values.top ? 1 : 0, - password: values.password - } - } as any) - message.success("🎉 发布成功") + if (!isDraftParams) { + await addArticleDataAPI({ + id, + ...values, + content: data.content, + tagIds: tagIds.join(','), + isDraft: isDraft ? 1 : 0, + config: { + status: values.status, + password: values.password + }, + createTime: values.createTime.toString() + }) + + isDraft ? message.success("🎉 保存为草稿成功") : message.success("🎉 发布成功") + } else { + // 修改草稿状态为发布文章 + await editArticleDataAPI({ + id, + ...values, + content: data.content, + tagIds: tagIds.join(','), + isDraft: 0, + config: { + status: values.status, + password: values.password + } + } as any) + } } // 关闭弹框 closeModel() // 清除本地持久化的数据 localStorage.removeItem('article_content') - // 跳转到文章页 - navigate("/article") + // 如果是草稿就跳转到草稿页,否则文章页 + isDraft ? navigate("/draft") : navigate("/article") // 初始化表单 form.resetFields() @@ -226,9 +243,16 @@ const PublishForm = ({ data, closeModel }: { data: Article, closeModel: () => vo - - + + + + {/* 草稿和编辑状态下不再显示保存草稿按钮 */} + {(!isDraftParams && !id) && ( + + + + )} ); diff --git a/src/pages/Create/index.tsx b/src/pages/Create/index.tsx index 6526e14..360d257 100644 --- a/src/pages/Create/index.tsx +++ b/src/pages/Create/index.tsx @@ -15,6 +15,7 @@ import { AiOutlineEdit, AiOutlineSend } from 'react-icons/ai'; const CreatePage = () => { const [params] = useSearchParams() const id = +params.get('id')! + const isDraftParams = Boolean(params.get('draft')) const [data, setData] = useState
({} as Article) const [content, setContent] = useState(''); @@ -178,7 +179,7 @@ const CreatePage = () => { setContent(value)} /> setPublishOpen(false)} diff --git a/src/pages/Draft/index.tsx b/src/pages/Draft/index.tsx new file mode 100644 index 0000000..adcb3ca --- /dev/null +++ b/src/pages/Draft/index.tsx @@ -0,0 +1,129 @@ +import { useState, useEffect } from 'react'; +import { Table, Button, Tag, notification, Card, Popconfirm, Form, Input, Cascader, Select, DatePicker } from 'antd'; +import { titleSty } from '@/styles/sty' +import Title from '@/components/Title'; +import { Link } from 'react-router-dom'; + +import { delArticleDataAPI, getArticleListAPI } from '@/api/Article'; +import type { Tag as ArticleTag } from '@/types/app/tag'; +import type { Cate } from '@/types/app/cate'; +import type { Article } from '@/types/app/article'; + +import { useWebStore } from '@/stores'; + +export default () => { + const web = useWebStore(state => state.web) + + const [current, setCurrent] = useState(1); + const [loading, setLoading] = useState(false); + const [articleList, setArticleList] = useState([]); + + const [form] = Form.useForm(); + + const getArticleList = async () => { + setLoading(true); + const { data } = await getArticleListAPI({ query: { isDraft: 1 } }); + setArticleList(data as Article[]); + setLoading(false); + }; + + useEffect(() => { + getArticleList() + }, []); + + const delArticleData = async (id: number) => { + setLoading(true); + + await delArticleDataAPI(id); + await getArticleList(); + form.resetFields() + setCurrent(1) + notification.success({ message: '🎉 删除文章成功' }) + + setLoading(false); + }; + + // 标签颜色 + const colors = ['', '#2db7f5', '#87d068', '#f50', '#108ee9']; + + const columns = [ + { + title: 'ID', + dataIndex: 'id', + key: 'id', + align: 'center', + width: 100, + }, + { + title: '标题', + dataIndex: 'title', + key: 'title', + align: 'center', + width: 300, + render: (text: string, record: Article) => {text}, + }, + { + title: '摘要', + dataIndex: 'description', + key: 'description', + align: 'center', + width: 350, + render: (text: string) =>
{text ? text : '该文章暂未设置文章摘要'}
, + }, + { + title: '分类', + dataIndex: 'cateList', + key: 'cateList', + align: 'center', + render: (cates: Cate[]) => cates.map((item, index) => {item.name}) + }, + { + title: '标签', + dataIndex: 'tagList', + key: 'tagList', + align: 'center', + render: (tags: ArticleTag[]) => tags.map((item, index) => {item.name}) + }, + { + title: '操作', + key: 'action', + fixed: 'right', + align: 'center', + render: (text: string, record: Article) => ( +
+ + + + + delArticleData(record.id!)}> + + +
+ ), + }, + ]; + + return ( + <> + + + <Card className={`${titleSty} mt-2 min-h-[calc(100vh-250px)]`}> + <Table + rowKey="id" + dataSource={articleList} + columns={columns as any} + loading={loading} + scroll={{ x: 'max-content' }} + pagination={{ + position: ['bottomCenter'], + current, + defaultPageSize: 8, + onChange(current) { + setCurrent(current) + } + }} + /> + </Card> + </> + ); +}; \ No newline at end of file diff --git a/src/types/app/article.d.ts b/src/types/app/article.d.ts index a2af72c..abc76c3 100644 --- a/src/types/app/article.d.ts +++ b/src/types/app/article.d.ts @@ -7,7 +7,7 @@ export interface Config { id?: number, articleId?: number, status: Status, - password:string + password: string } export interface Article { @@ -17,12 +17,13 @@ export interface Article { content: string, cover: string, cateIds: number[], - cateList: Cate[] + cateList?: Cate[] tagIds: string, - tagList: Tag[] + tagList?: Tag[] view?: number comment?: number, config: Config, + isDraft: number, createTime?: string, } @@ -36,4 +37,5 @@ export interface FilterForm { export interface FilterArticle extends FilterData { cateIds?: number[], tagId?: number, + isDraft: number } \ No newline at end of file