From 2a04e29ae744310ae805b7dfbd1bb3eb617c9dc5 Mon Sep 17 00:00:00 2001 From: panda <7934952@qq.com> Date: Tue, 2 Jul 2024 17:13:47 +0800 Subject: [PATCH] add new --- src/api/admin/index.ts | 28 ++- src/components/admin/MainWrapper.vue | 13 +- src/components/blogs/HomePage.vue | 37 +++- src/components/blogs/ceshi.vue | 139 +++++++----- src/router/admin.ts | 66 +++--- src/stores/index.ts | 131 ++++------- src/views/admin/LabelManageView.vue | 221 +++++++++++++++++++ src/views/admin/TypeManageView.vue | 221 +++++++++++++++++++ src/views/admin/blogmange/BlogFormView.vue | 26 ++- src/views/admin/blogmange/BlogManageView.vue | 151 ++++++++++--- 10 files changed, 817 insertions(+), 216 deletions(-) create mode 100644 src/views/admin/LabelManageView.vue create mode 100644 src/views/admin/TypeManageView.vue diff --git a/src/api/admin/index.ts b/src/api/admin/index.ts index a67f7fc..038e017 100644 --- a/src/api/admin/index.ts +++ b/src/api/admin/index.ts @@ -1,4 +1,16 @@ -// 语录管理 +export interface blogInterface { + key: string, + blogtitle: string, + create_at: Date, + readnum: number, + readminite: number, + wordcount: number, + img: string, + blogcontent: string, + typename: string, + labelnames: string +} + export interface classticInterface { key: string, id?: number, @@ -13,4 +25,18 @@ export interface comLinkInterface { linktext: string, linkurl: string, descr: string +} + +export interface typeInterface { + key: string, + id?: number, + typename: string, + descr: string +} + +export interface labelInterface { + key: string, + id?: number, + labelname: string, + descr: string } \ No newline at end of file diff --git a/src/components/admin/MainWrapper.vue b/src/components/admin/MainWrapper.vue index ed6ea69..b9ff55e 100644 --- a/src/components/admin/MainWrapper.vue +++ b/src/components/admin/MainWrapper.vue @@ -100,27 +100,34 @@ const items = reactive([ }, { key: '7', + icon: () => h(iconComponents.TypeOutLined), + label: '标签管理', + title: '标签管理', + url: '/admin/labelmanage' + }, + { + key: '8', icon: () => h(iconComponents.CommentOutLined), label: '评论管理', title: '评论管理', url: '/admin/commentmanage' }, { - key: '8', + key: '9', icon: () => h(iconComponents.PhotoOutLined), label: '相册管理', title: '相册管理', url: '/admin/imagemanage' }, { - key: '9', + key: '10', icon: () => h(iconComponents.FileOutLined), label: '文件管理', title: '文件管理', url: '/admin/filemanage' }, { - key: '10', + key: '11', icon: () => h(iconComponents.SystemOutLined), label: '系统设置', title: '系统设置', diff --git a/src/components/blogs/HomePage.vue b/src/components/blogs/HomePage.vue index 17b1ae7..e97f281 100644 --- a/src/components/blogs/HomePage.vue +++ b/src/components/blogs/HomePage.vue @@ -2,7 +2,8 @@ <Simplebar @scroll="handleScroll"> <!-- 头部导航菜单 --> <div class="headerMenu" v-if="show_menu"> - <a-menu v-model:selectedKeys="current" mode="horizontal" :items="items" style="border-bottom: none;" /> + <a-menu v-model:selectedKeys="current" mode="horizontal" :items="items" style="border-bottom: none;" + @click="jumpMenu" /> <a-input-search v-model:value="value" placeholder="search" style="width: 200px" @search="onSearch" /> </div> <div class="author" v-if="show_author">{{ author }}</div> @@ -114,7 +115,7 @@ import { onMounted, watch } from 'vue'; import { CaretRightOutlined } from '@ant-design/icons-vue'; import 'APlayer/dist/APlayer.min.css'; import APlayer from 'APlayer'; -import {get} from "@/tools/request" +import { get } from "@/tools/request" import { createEcharts } from "@/hooks/intex" import { useRouter, useRoute } from 'vue-router'; import iconComponents from "@/assets/index"; @@ -123,7 +124,7 @@ import type { comLinkInterface } from '@/api/admin/index'; const router = useRouter() const route = useRoute() -const author=ref("SunFree.") +const author = ref("SunFree.") /** * 隐藏参数 */ @@ -138,14 +139,14 @@ const { show_menu, show_carousel, show_author, show_anchornDown } = toRefs(idSho const mainCss = reactive({ marginTop: "0px" }) -type MenuProps = any; const current = ref<string[]>(['home']); const items = ref<MenuProps['items']>([ { key: 'home', icon: () => h(HomeOutlined), - label: h('a', { href: '/' }, '首页'), + label: '首页', title: '首页', + url: "/home" }, { @@ -153,12 +154,14 @@ const items = ref<MenuProps['items']>([ icon: () => h(HighlightOutlined), label: h('a', { href: "/blog" }, '博客'), title: '博客', + url: "/blog" }, { key: 'diary', icon: () => h(ProfileOutlined), label: h('a', { href: "/diary" }, '日记'), title: '日记', + url: "/diary" }, { key: 'album', @@ -189,14 +192,24 @@ const items = ref<MenuProps['items']>([ icon: () => h(UsergroupDeleteOutlined), label: h('a', { href: "/chart" }, '收支图'), title: '收支图', + url: "/chart" }, { key: 'aboutme', icon: () => h(UsergroupDeleteOutlined), label: h('a', { href: "/aboutme" }, '关于sunfree'), title: '关于sunfree', + url: "/aboutme" }, ]); + +const jumpMenu = ({ key }:{key:string}) => { + // 从 `item` 中获取 `key` 和 `url` 信息 + const selectedItem = items.value.find((i:any) => i.key === key); + if (selectedItem && selectedItem.url) { + router.push(selectedItem.url); + } + }; const updateCarouselVisibility = (routeName: any) => { handleScrollEnabled.value = false; if (scrollbar.value) { @@ -255,8 +268,8 @@ const downScroll = () => { * 左侧栏 */ // 折叠面板 -const classticlist=ref<classticInterface[]>([]) -const classticList=async ()=>{ +const classticlist = ref<classticInterface[]>([]) +const classticList = async () => { try { await get("/classtics/list").then(response => { if (response) { @@ -276,11 +289,11 @@ const classticList=async ()=>{ } const comLinkClick = (url: string) => { if (url.startsWith('http://') || url.startsWith('https://')) { - window.open(url, "_blank"); // 使用 window.location.href 打开外部链接 + window.open(url, "_blank"); // 使用 window.location.href 打开外部链接 } else { - router.push(url); // 否则使用 Vue Router 的 push 方法导航 + router.push(url); // 否则使用 Vue Router 的 push 方法导航 } - } +} const comlinklist = ref<comLinkInterface[]>([]) const comLinkList = async () => { try { @@ -586,10 +599,12 @@ watch( border-style: hidden; transform: translateX(-50%); } -.anchorDown span{ + +.anchorDown span { font-size: 35px; color: aliceblue; } + .mainContainer { display: flex; justify-content: center; diff --git a/src/components/blogs/ceshi.vue b/src/components/blogs/ceshi.vue index d5c37b0..e997e5f 100644 --- a/src/components/blogs/ceshi.vue +++ b/src/components/blogs/ceshi.vue @@ -1,58 +1,95 @@ <template> - <a-form - :model="formState" - name="basic" - :label-col="{ span: 8 }" - :wrapper-col="{ span: 16 }" - autocomplete="off" - @finish="onFinish" - @finishFailed="onFinishFailed" - > - <a-form-item - label="Username" - name="username" - :rules="[{ required: true, message: 'Please input your username!' }]" - > - <a-input v-model:value="formState.username" /> - </a-form-item> + <a-table :columns="columns" :data-source="data"> + <template #headerCell="{ column }"> + <template v-if="column.key === 'name'"> + <span> + <!-- <smile-outlined /> --> + Name + </span> + </template> + </template> - <a-form-item - label="Password" - name="password" - :rules="[{ required: true, message: 'Please input your password!' }]" - > - <a-input-password v-model:value="formState.password" /> - </a-form-item> - - <a-form-item name="remember" :wrapper-col="{ offset: 8, span: 16 }"> - <a-checkbox v-model:checked="formState.remember">Remember me</a-checkbox> - </a-form-item> - - <a-form-item :wrapper-col="{ offset: 8, span: 16 }"> - <a-button type="primary" html-type="submit">Submit</a-button> - </a-form-item> - </a-form> + <template #bodyCell="{ column, record }"> + <template v-if="column.key === 'name'"> + {{ record.name }} + </template> + <template v-else-if="column.key === 'tags'"> + <span> + <a-tag + v-for="tag in record.tags" + :key="tag" + :color="tag === 'loser' ? 'volcano' : tag.length > 5 ? 'geekblue' : 'green'" + > + {{ tag.toUpperCase() }} + </a-tag> + </span> + </template> + <template v-else-if="column.key === 'action'"> + <span> + <a>Invite 一 {{ record.name }}</a> + <a-divider type="vertical" /> + <a>Delete</a> + <a-divider type="vertical" /> + <a class="ant-dropdown-link"> + More actions + <down-outlined /> + </a> + </span> + </template> + </template> + </a-table> </template> <script lang="ts" setup> -import { reactive } from 'vue'; - -interface FormState { - username: string; - password: string; - remember: boolean; -} - -const formState = reactive<FormState>({ - username: '', - password: '', - remember: true, -}); -const onFinish = (values: any) => { - console.log('Success:', values); -}; +import { SmileOutlined, DownOutlined } from '@ant-design/icons-vue'; +const columns = [ + { + name: 'Name', + dataIndex: 'name', + key: 'name', + }, + { + title: 'Age', + dataIndex: 'age', + key: 'age', + }, + { + title: 'Address', + dataIndex: 'address', + key: 'address', + }, + { + title: 'Tags', + key: 'tags', + dataIndex: 'tags', + }, + { + title: 'Action', + key: 'action', + }, +]; -const onFinishFailed = (errorInfo: any) => { - console.log('Failed:', errorInfo); -}; +const data = [ + { + key: '1', + name: 'John Brown', + age: 32, + address: 'New York No. 1 Lake Park', + tags: ['nice', 'developer'], + }, + { + key: '2', + name: 'Jim Green', + age: 42, + address: 'London No. 1 Lake Park', + tags: ['loser'], + }, + { + key: '3', + name: 'Joe Black', + age: 32, + address: 'Sidney No. 1 Lake Park', + tags: ['cool', 'teacher'], + }, +]; </script> diff --git a/src/router/admin.ts b/src/router/admin.ts index 60a3fe1..c6c3237 100644 --- a/src/router/admin.ts +++ b/src/router/admin.ts @@ -1,20 +1,20 @@ import type { RouteRecordRaw } from 'vue-router' -const adminRoute:Array<RouteRecordRaw>=[ +const adminRoute: Array<RouteRecordRaw> = [ { - path:"/admin", - redirect:"/admin/dashboard", - name:'admin', - component:()=> import("@/components/admin/MainWrapper.vue"), - children:[ - { - path:"dashboard", - name:"dashboard", - component:()=>import("@/views/admin/DashBoardView.vue") + path: "/admin", + redirect: "/admin/dashboard", + name: 'admin', + component: () => import("@/components/admin/MainWrapper.vue"), + children: [ + { + path: "dashboard", + name: "dashboard", + component: () => import("@/views/admin/DashBoardView.vue") }, { - path:"blogmanage", - name:"blogmanage", - component:()=>import("@/views/admin/blogmange/BlogManageView.vue") + path: "blogmanage", + name: "blogmanage", + component: () => import("@/views/admin/blogmange/BlogManageView.vue") }, { path: "blogmanage/add", @@ -22,28 +22,38 @@ const adminRoute:Array<RouteRecordRaw>=[ meta: { requiresAuth: true } }, { - path: "blogmanage/update/:id", - component: () => import("@/views/admin/blogmange/BlogFormView.vue"), - meta: { requiresAuth: true }, - props:true + path: "blogmanage/update/:id", + component: () => import("@/views/admin/blogmange/BlogFormView.vue"), + meta: { requiresAuth: true }, + props: true + }, + { + path: "classticmanage", + name: "classticmanage", + component: () => import("@/views/admin/ClassticManageView.vue") }, { - path:"classticmanage", - name:"classticmanage", - component:()=>import("@/views/admin/ClassticManageView.vue") + path: "commonlinkmanage", + name: "commonlinkmanage", + component: () => import("@/views/admin/CommonLinkManageView.vue") }, { - path:"commonlinkmanage", - name:"commonlinkmanage", - component:()=>import("@/views/admin/CommonLinkManageView.vue") + path: "typemanage", + name: "typemanage", + component: () => import("@/views/admin/TypeManageView.vue") + }, + { + path: "labelmanage", + name: "labelmanage", + component: () => import("@/views/admin/LabelManageView.vue") } - + ] }, - { - path: "/login", - name: "login", - component: () => import("@/components/admin/SignIn.vue"), + { + path: "/login", + name: "login", + component: () => import("@/components/admin/SignIn.vue"), }, ] export default adminRoute \ No newline at end of file diff --git a/src/stores/index.ts b/src/stores/index.ts index 05304eb..84b1ec4 100644 --- a/src/stores/index.ts +++ b/src/stores/index.ts @@ -1,12 +1,19 @@ import { reactive, ref } from 'vue' import { defineStore } from 'pinia' -import { get } from "@/tools/request" -import dayjs from 'dayjs'; -import type { blogInterface } from '@/api/admin'; export const mainWrapperStore=defineStore("mainWrapper",()=>{ }) + +export const blogStore = defineStore("blog", () => { + + + const delControl = reactive({ + open: false, + ids: "" + }) + return { delControl } +}) export const classticStore = defineStore("classtic", () => { const addControl = reactive({ open: false, @@ -47,19 +54,46 @@ export const comLinkStore = defineStore("comLink", () => { }) return { addControl, delControl,editControl } }) +export const typeStore = defineStore("type", () => { + const addControl = reactive({ + open: false, + title: "", + ids: "" + }) + const delControl = reactive({ + open: false, + ids: "" + }) + const editControl = reactive({ + open: false, + ids: "", + title:"" + }) + return { addControl, delControl,editControl } +}) +export const labelStore = defineStore("label", () => { + const addControl = reactive({ + open: false, + title: "", + ids: "" + }) + const delControl = reactive({ + open: false, + ids: "" + }) + const editControl = reactive({ + open: false, + ids: "", + title:"" + }) + return { addControl, delControl,editControl } +}) - - - - -// 博客列表 -const bloglist = ref<blogInterface[]>([]) - export const useAuthStore = defineStore("auth", () => { const tokenValue = ref("") function setToken(token: string) { @@ -71,82 +105,5 @@ export const useAuthStore = defineStore("auth", () => { return { setToken, removeToken } }) -// 博客列表接口 -export const blogContentStore = defineStore("blog", () => { - const blogList = async () => { - try { - const response = await get("/blogs/list"); - if (response) { - bloglist.value = response.data.data.map((items: any, index: any) => ({ - key: (index + 1).toString(), - blogtitle: items.blogtitle, - create_at: dayjs(items.create_at).format('YYYY-MM-DD HH:mm:ss'), - update_at: dayjs(items.update_at).format('YYYY-MM-DD HH:mm:ss'), - readnum: items.readnum, - readminite: items.readminite, - wordcount: items.wordcount, - img: items.img, - blogcontent: items.blogcontent, - typename: items.typename, - labelnames: items.labelnames - })) - } else { - console.log("bloglist is not exits") - } - } catch (error) { - console.log("bloglist is error") - } - - } - return { blogList, bloglist } -}) -// 博客查询接口 -export const blogSearchStore = defineStore('blogsearch', () => { - const searchValue = reactive({ - blogtitle: '', - typename: '', - start_date: '', - end_date: '' - }); - - const onChange = (date: string, dateString: string[]) => { - if (date && dateString.length === 2) { - searchValue.start_date = dateString[0]; - searchValue.end_date = dateString[1]; - } else { - searchValue.start_date = ''; - searchValue.end_date = ''; - } - console.log(dateString[0]); - }; - - const blogSearch = async () => { - try { - const response = await get("/blogs/list/search", { - blogtitle: searchValue.blogtitle, - typename: searchValue.typename, - start_date: searchValue.start_date, - end_date: searchValue.end_date - }); - if (response && response.data) { - bloglist.value = response.data.data.map((items: any, index: any) => ({ - key: (index + 1).toString(), - blogtitle: items.blogtitle, - typename: items.typename, - blogcontent: items.blogcontent, - create_at: dayjs(items.create_at).format('YYYY-MM-DD HH:mm:ss'), - update_at: dayjs(items.update_at).format('YYYY-MM-DD HH:mm:ss'), - descr: items.descr - })); - console.log(response) - } else { - console.log("request has no data"); - } - } catch (error) { - console.error("request is exception", error); - } - }; - return { searchValue, onChange, blogSearch }; -}); diff --git a/src/views/admin/LabelManageView.vue b/src/views/admin/LabelManageView.vue new file mode 100644 index 0000000..8c7fe92 --- /dev/null +++ b/src/views/admin/LabelManageView.vue @@ -0,0 +1,221 @@ +<template> + <div class="content"> + <div class="search"> + <a-space> + <a-input v-model:value="searchlist.labelname" placeholder="标签名称" /> + </a-space> + <a-space style="margin-left: 16px;"> + <a-button @click="search">查询</a-button> + <a-button type="primary" ghost @click="addModal">新增</a-button> + <a-modal v-model:open="addControl.open" :title="addControl.title" cancelText="取消" okText="确定" @ok="add"> + <a-form ref="formRef" :model="addList" name="basic" :label-col="{ span: 4, offset: 2 }" + :wrapper-col="{ span: 16 }"> + <a-form-item label="标签名称" name="labelname" :rules="[{ required: true, message: '请输入标签名称!' }]"> + <a-input v-model:value="addList.labelname" /> + </a-form-item> + <a-form-item label="备注" name="descr" :rules="[{ required: true, message: '请输入备注!' }]"> + <a-input v-model:value="addList.descr" /> + </a-form-item> + </a-form> + </a-modal> + </a-space> + </div> + <div class="table"> + <a-table bordered :data-source="labellist" :columns="columns"> + <template #bodyCell="{ column, record }"> + <template v-if="column.dataIndex === 'operation'"> + <a-space> + <div> + <a-button size="small" danger @click="delModal(record.id)">删除</a-button> + <a-modal v-model:open="delControl.open" title="提示" ok-text="确认" cancel-text="取消" + @ok="del"> + <p>确认删除吗?</p> + </a-modal> + </div> + <a-button size="small" type="primary" ghost @click="editModal(record.id)">编辑</a-button> + <a-modal v-model:open="editControl.open" :title="editControl.title" cancelText="取消" + okText="确定" @ok="edit"> + <a-form ref="formRef" :model="editList" name="basic" :label-col="{ span: 4, offset: 2 }" + :wrapper-col="{ span: 16 }"> + <a-form-item label="标签名称" name="labelname" + :rules="[{ required: true, message: '请修改标签名称!' }]"> + <a-input v-model:value="editList.labelname" /> + </a-form-item> + <a-form-item label="备注" name="descr" + :rules="[{ required: true, message: '请修改备注!' }]"> + <a-input v-model:value="editList.descr" /> + </a-form-item> + </a-form> + </a-modal> + </a-space> + </template> + </template> + </a-table> + </div> + </div> +</template> + +<script setup lang='ts'> +import { onMounted, reactive, ref } from 'vue'; +import { labelStore } from "@/stores/index" +import type { labelInterface } from "@/api/admin/index" +import { get, post, remove, put } from '@/tools/request'; +const { delControl, addControl, editControl } = labelStore() +const labellist = ref<labelInterface[]>([]) +const searchlist = reactive({ + labelname: "" +}) +const addList = ref({ + labelname: "", + descr: "" +}) + +const editList = ref({ + labelname: "", + descr: "" +}) +const labelList = async () => { + try { + await get("/labels/list").then(response => { + if (response) { + labellist.value = response.data.data.map((item: any, index: any) => ({ + key: (index + 1).toString(), + id: item.id, + labelname: item.labelname, + descr: item.descr + })); + } else { + console.log("the interface request data does not exist!") + } + }) + } catch (error) { + console.error("Failed to fetch data", error); + } +} + +const search = async () => { + try { + await get( + "/labels/list/search", + { labelname: searchlist.labelname } + ).then(response => { + if (response) { + labellist.value = response.data.data.map((items: any, index: any) => ({ + key: (index + 1).toString(), + id: items.id, + labelname: items.labelname, + descr: items.descr + })) + } else { + console.log("the interface request data does not exist!") + } + }) + } catch (error) { + console.log("interface request exception") + } +} + +const addModal = () => { + addControl.title = "新增" + addControl.open = true; +}; +const formRef = ref(); +const add = async () => { + try { + await post( + "/labels/add", + addList.value + ) + labelList() + addControl.open = false + formRef.value.resetFields(); + } catch (error) { + console.log("interface request exception") + } +} +const delModal = (id: any) => { + delControl.ids = id + delControl.open = true +}; +// 删除 +const del = async (id: any) => { + id = delControl.ids + try { + if (id) { + await remove( + `/labels/delete/${id}` + ) + labelList() + } else { + console.log("id do not exist!") + } + } catch (error) { + console.log("interface request exception") + } + + delControl.open = false +} + +const editModal = (id: any) => { + editControl.ids = id + editControl.title = "编辑" + editControl.open = true + get( + `/labels/list/search/${id}` + ).then(response => { + editList.value = response.data.data + }) +} +const edit = async (id: any) => { + id = editControl.ids + try { + if (id) { + await put( + `/labels/update/${id}`, + editList.value + ) + editControl.open = false + formRef.value.resetFields() + labelList() + } else { + console.log("id do not exist!") + } + } catch (error) { + console.log("interface request exception") + } +} +// 获取列表 +onMounted(async () => { + labelList() +}); +const columns = [ + { + title: '序号', + dataIndex: 'key', + width: '5%', + }, + { + title: '标签名称', + dataIndex: 'labelname', + width: '20%', + }, + { + title: '备注', + dataIndex: 'descr', + }, + { + title: '操作', + dataIndex: 'operation', + }, +]; +</script> + +<style scoped> +.content { + padding: 24px 24px 0 24px; +} + +.search { + margin: 0 0 24px 0; +} +</style> \ No newline at end of file diff --git a/src/views/admin/TypeManageView.vue b/src/views/admin/TypeManageView.vue new file mode 100644 index 0000000..e20b342 --- /dev/null +++ b/src/views/admin/TypeManageView.vue @@ -0,0 +1,221 @@ +<template> + <div class="content"> + <div class="search"> + <a-space> + <a-input v-model:value="searchlist.typename" placeholder="类型名称" /> + </a-space> + <a-space style="margin-left: 16px;"> + <a-button @click="search">查询</a-button> + <a-button type="primary" ghost @click="addModal">新增</a-button> + <a-modal v-model:open="addControl.open" :title="addControl.title" cancelText="取消" okText="确定" @ok="add"> + <a-form ref="formRef" :model="addList" name="basic" :label-col="{ span: 4, offset: 2 }" + :wrapper-col="{ span: 16 }"> + <a-form-item label="类型名称" name="typename" :rules="[{ required: true, message: '请输入类型名称!' }]"> + <a-input v-model:value="addList.typename" /> + </a-form-item> + <a-form-item label="备注" name="descr" :rules="[{ required: true, message: '请输入备注!' }]"> + <a-input v-model:value="addList.descr" /> + </a-form-item> + </a-form> + </a-modal> + </a-space> + </div> + <div class="table"> + <a-table bordered :data-source="typelist" :columns="columns"> + <template #bodyCell="{ column, record }"> + <template v-if="column.dataIndex === 'operation'"> + <a-space> + <div> + <a-button size="small" danger @click="delModal(record.id)">删除</a-button> + <a-modal v-model:open="delControl.open" title="提示" ok-text="确认" cancel-text="取消" + @ok="del"> + <p>确认删除吗?</p> + </a-modal> + </div> + <a-button size="small" type="primary" ghost @click="editModal(record.id)">编辑</a-button> + <a-modal v-model:open="editControl.open" :title="editControl.title" cancelText="取消" + okText="确定" @ok="edit"> + <a-form ref="formRef" :model="editList" name="basic" :label-col="{ span: 4, offset: 2 }" + :wrapper-col="{ span: 16 }"> + <a-form-item label="类型名称" name="typename" + :rules="[{ required: true, message: '请修改类型名称!' }]"> + <a-input v-model:value="editList.typename" /> + </a-form-item> + <a-form-item label="备注" name="descr" + :rules="[{ required: true, message: '请修改备注!' }]"> + <a-input v-model:value="editList.descr" /> + </a-form-item> + </a-form> + </a-modal> + </a-space> + </template> + </template> + </a-table> + </div> + </div> +</template> + +<script setup lang='ts'> +import { onMounted, reactive, ref } from 'vue'; +import { typeStore } from "@/stores/index" +import type { typeInterface } from "@/api/admin/index" +import { get, post, remove, put } from '@/tools/request'; +const { delControl, addControl, editControl } = typeStore() +const typelist = ref<typeInterface[]>([]) +const searchlist = reactive({ + typename: "" +}) +const addList = ref({ + typename: "", + descr: "" +}) + +const editList = ref({ + typename: "", + descr: "" +}) +const typeList = async () => { + try { + await get("/types/list").then(response => { + if (response) { + typelist.value = response.data.data.map((item: any, index: any) => ({ + key: (index + 1).toString(), + id: item.id, + typename: item.typename, + descr: item.descr + })); + } else { + console.log("the interface request data does not exist!") + } + }) + } catch (error) { + console.error("Failed to fetch data", error); + } +} + +const search = async () => { + try { + await get( + "/types/list/search", + { typename: searchlist.typename } + ).then(response => { + if (response) { + typelist.value = response.data.data.map((items: any, index: any) => ({ + key: (index + 1).toString(), + id: items.id, + typename: items.typename, + descr: items.descr + })) + } else { + console.log("the interface request data does not exist!") + } + }) + } catch (error) { + console.log("interface request exception") + } +} + +const addModal = () => { + addControl.title = "新增" + addControl.open = true; +}; +const formRef = ref(); +const add = async () => { + try { + await post( + "/types/add", + addList.value + ) + typeList() + addControl.open = false + formRef.value.resetFields(); + } catch (error) { + console.log("interface request exception") + } +} +const delModal = (id: any) => { + delControl.ids = id + delControl.open = true +}; +// 删除 +const del = async (id: any) => { + id = delControl.ids + try { + if (id) { + await remove( + `/types/delete/${id}` + ) + typeList() + } else { + console.log("id do not exist!") + } + } catch (error) { + console.log("interface request exception") + } + + delControl.open = false +} + +const editModal = (id: any) => { + editControl.ids = id + editControl.title = "编辑" + editControl.open = true + get( + `/types/list/search/${id}` + ).then(response => { + editList.value = response.data.data + }) +} +const edit = async (id: any) => { + id = editControl.ids + try { + if (id) { + await put( + `/types/update/${id}`, + editList.value + ) + editControl.open = false + formRef.value.resetFields() + typeList() + } else { + console.log("id do not exist!") + } + } catch (error) { + console.log("interface request exception") + } +} +// 获取列表 +onMounted(async () => { + typeList() +}); +const columns = [ + { + title: '序号', + dataIndex: 'key', + width: '5%', + }, + { + title: '类型名称', + dataIndex: 'typename', + width: '20%', + }, + { + title: '备注', + dataIndex: 'descr', + }, + { + title: '操作', + dataIndex: 'operation', + }, +]; +</script> + +<style scoped> +.content { + padding: 24px 24px 0 24px; +} + +.search { + margin: 0 0 24px 0; +} +</style> \ No newline at end of file diff --git a/src/views/admin/blogmange/BlogFormView.vue b/src/views/admin/blogmange/BlogFormView.vue index 807fab0..08a2e17 100644 --- a/src/views/admin/blogmange/BlogFormView.vue +++ b/src/views/admin/blogmange/BlogFormView.vue @@ -11,6 +11,15 @@ typeItem.typename }}</a-select-option> </a-select> </a-form-item> + <a-form-item name="labelname" label="标签名称" class="items"> + <a-select v-model:value="formState['labelname']" mode="multiple" placeholder="请选择标签"> + <a-select-option value="red">Red</a-select-option> + <a-select-option value="green">Green</a-select-option> + </a-select> + </a-form-item> + <a-form-item label="文图地址" name="textimg" class="items"> + <a-input v-model:value="formState.imglink" placeholder="请输入文图地址"/> + </a-form-item> </a-flex> <v-md-editor v-model="content" height="900px"></v-md-editor> <a-form-item label="博客备注" name="descr" class="desrpad"> @@ -34,13 +43,16 @@ import type { UnwrapRef } from 'vue'; import type { Rule } from 'ant-design-vue/es/form'; import { post, get, put } from '@/tools/request'; import { useRouter } from 'vue-router' + const router = useRouter() interface FormState { id?: number; blogtitle: string; typeid: number; blogcontent: string; + labelname: string[]; descr: string; + imglink: string } const formRef = ref(); @@ -48,7 +60,9 @@ const formState: UnwrapRef<FormState> = reactive({ blogtitle: '', typeid: 1, blogcontent: "", + labelname: [], descr: '', + imglink: "" }); const rules: Record<string, Rule[]> = { blogtitle: [ @@ -61,11 +75,11 @@ const rules: Record<string, Rule[]> = { ], }; const content = ref(); -interface TypeStateData{ - id:number, - key:string, - typename:string, - descr:string +interface TypeStateData { + id: number, + key: string, + typename: string, + descr: string } const typedata = ref<TypeStateData[]>([]) onMounted(async () => { @@ -112,7 +126,7 @@ const resetForm = () => { } .items { - width: calc(75vw/2); + width: calc(75vw/4); } #content { diff --git a/src/views/admin/blogmange/BlogManageView.vue b/src/views/admin/blogmange/BlogManageView.vue index 44a1809..f2f9755 100644 --- a/src/views/admin/blogmange/BlogManageView.vue +++ b/src/views/admin/blogmange/BlogManageView.vue @@ -2,25 +2,31 @@ <div class="content"> <div class="search"> <a-space> - <a-input v-model:value="blogSearch.searchValue.blogtitle" placeholder="博客标题" /> - <a-input v-model:value="blogSearch.searchValue.typename" placeholder="分类名称" /> - <a-range-picker :locale="locale" show-time @change="blogSearch.onChange" /> + <a-input v-model:value="searchlist.blogtitle" placeholder="博客标题" /> + <a-input v-model:value="searchlist.typename" placeholder="分类名称" /> + <a-range-picker :locale="locale" show-time @change="onChange" /> </a-space> <a-space style="margin-left: 16px;"> - <a-button @click="blogSearch.blogSearch">查询</a-button> + <a-button @click="search">查询</a-button> <a-button type="primary" ghost @click="blogAdd">新增</a-button> </a-space> </div> <div class="table"> - <a-table bordered :data-source="blogContent.bloglist" :columns="columns"> + <a-table bordered :data-source="bloglist" :columns="columns"> <template #bodyCell="{ column, record }"> - - + <template v-if="column.key === 'labels'"> + <span> + <a-tag v-for="label in labelfilter(record.labelnames)" :key="label" color="cyan"> + {{ label }} + </a-tag> + </span> + </template> <template v-if="column.dataIndex === 'operation'"> <a-space> <div> - <a-button size="small" danger @click="showModal(record.key, record.id)">删除</a-button> - <a-modal v-model:open="open" title="提示" @ok="ackDelete(record)" ok-text="确认" cancel-text="取消" @cancel="unDelete"> + <a-button size="small" danger @click="showModal(record.id)">删除</a-button> + <a-modal v-model:open="open" title="提示" @ok="ackDelete(record)" ok-text="确认" + cancel-text="取消" @cancel="unDelete"> <p>确认删除吗?</p> </a-modal> </div> @@ -41,33 +47,106 @@ import locale from 'ant-design-vue/es/date-picker/locale/zh_CN'; import dayjs from 'dayjs'; import { useRouter } from "vue-router" import { message } from 'ant-design-vue'; -import { blogContentStore,blogSearchStore } from '@/stores'; +import type { blogInterface } from "@/api/admin/index" +import { get } from "@/tools/request" +import { blogStore } from '@/stores'; dayjs.locale('zh-cn'); const router = useRouter() -const blogContent=blogContentStore() -const blogSearch=blogSearchStore() +const { delControl } = blogStore() +// 查询 +const searchlist = reactive({ + blogtitle: "", + typename: "", + start_date: '', + end_date: '' +}) +const onChange = (date: string, dateString: string[]) => { + if (date && dateString.length === 2) { + searchlist.start_date = dateString[0]; + searchlist.end_date = dateString[1]; + } else { + searchlist.start_date = ''; + searchlist.end_date = ''; + } + console.log(dateString[0]); +}; + +const search = async () => { + try { + const response = await get("/blogs/list/search", { + blogtitle: searchlist.blogtitle, + typename: searchlist.typename, + start_date: searchlist.start_date, + end_date: searchlist.end_date + }); + if (response && response.data) { + bloglist.value = response.data.data.map((items: any, index: any) => ({ + key: (index + 1).toString(), + blogtitle: items.blogtitle, + typename: items.typename, + blogcontent: items.blogcontent, + wordcount:items.wordcount, + labelnames: items.labelnames, + create_at: dayjs(items.create_at).format('YYYY-MM-DD HH:mm:ss'), + update_at: dayjs(items.update_at).format('YYYY-MM-DD HH:mm:ss'), + descr: items.descr + })); + console.log(response) + } else { + console.log("request has no data"); + } + } catch (error) { + console.error("request is exception", error); + } +}; const open = ref<boolean>(false); -const toDelete = reactive<{ key: string, id: number }>({ - key: '', - id: 0 -}); -const showModal = (key: string, id: number) => { - open.value = true; - toDelete.key = key; - toDelete.id = id; + +const showModal = (id: any) => { + delControl.open = true; + delControl.ids = id; }; // 新增单独跳转到一个页面 const blogAdd = function () { router.push('/admin/blogmanage/add') } +const bloglist = ref<blogInterface[]>([]) +const blogList = async () => { + try { + const response = await get("/blogs/list"); + if (response) { + bloglist.value = response.data.data.map((items: any, index: any) => ({ + key: (index + 1).toString(), + blogtitle: items.blogtitle, + create_at: dayjs(items.create_at).format('YYYY-MM-DD HH:mm:ss'), + update_at: dayjs(items.update_at).format('YYYY-MM-DD HH:mm:ss'), + readnum: items.readnum, + readminite: items.readminite, + wordcount: items.wordcount, + img: items.img, + blogcontent: items.blogcontent, + typename: items.typename, + labelnames: items.labelnames + })) + } else { + console.log("bloglist is not exits") + } + } catch (error) { + console.log("bloglist is error") + } -// 获取列表 -onMounted(async () => { - blogContent.blogList() -}); - - +} +const labelfilter = (labelnames: any) => { + let labels = [] + if (labelnames) { + try { + labels = JSON.parse(labelnames) + } catch (error) { + console.error('Invalid JSON string:', labelnames); + } + } + return Array.isArray(labels) ? labels.filter(label => label && label.trim() !== '') : []; +} // 博客删除 // const ackDelete = async (record:{key:string,id:number}) => { // remove(`/blogs/delete/${toDelete.id}`).then((response) => { @@ -82,13 +161,13 @@ onMounted(async () => { // }; // 博客修改 -const ackUpdate =async (record: { key: string, id: number }) => { +const ackUpdate = async (record: { key: string, id: number }) => { router.push(`/admin/blogmanage/update/${record.id}`); }; -const ackWatch=async (record: { key: string, id: number }) => { +const ackWatch = async (record: { key: string, id: number }) => { router.push(`/admin/blogmanage/watch`); } -const unDelete=async ()=>{ +const unDelete = async () => { message.warn("取消删除") } const columns = [ @@ -106,6 +185,16 @@ const columns = [ title: '博客分类', dataIndex: 'typename', }, + { + title: '博客标签', + dataIndex: 'labelnames', + key: "labels" + }, + { + title: '总字数', + dataIndex: 'wordcount', + key: "wordcount" + }, { title: '发布时间', dataIndex: 'create_at', @@ -119,6 +208,10 @@ const columns = [ dataIndex: 'operation', }, ]; + +onMounted(async () => { + blogList() +}); </script> <style scoped>