Browse Source

add new

master
panda 9 months ago
parent
commit
dbd1c332b0
  1. 4
      src/api/admin.ts
  2. 0
      src/api/blog.ts
  3. 91
      src/components/admin/MainWrapper.vue
  4. 5
      src/router/admin.ts
  5. 18
      src/store/index.ts
  6. 48
      src/stores/blog.ts
  7. 13
      src/stores/counter.ts
  8. 110
      src/stores/index.ts
  9. 1
      src/views/admin/blogmange/BlogFormView.vue
  10. 14
      src/views/admin/blogmange/BlogManageView.vue
  11. 100
      src/views/admin/classticmanage/ClassticManageView.vue
  12. 46
      src/views/blog/blogcontent/BlogListView.vue

4
src/api/admin.ts

@ -0,0 +1,4 @@
// 语录管理
export interface searchValue{
classtictitle: string,
}

0
src/api/blog.ts

91
src/components/admin/MainWrapper.vue

@ -1,42 +1,43 @@
<template> <template>
<Simplebar >
<div id="container">
<div class="leftSidebar" :style="{ width: state.menuWidth }">
<a-flex justify="center" align="center" style="height: 48px;">
<component :is=iconComponents.TitleOutLined :class="collapsed ? 'small' : 'large'" />
<span v-if="!collapsed">{{ state.name }}</span>
</a-flex>
<a-menu v-model:openKeys="state.openKeys" v-model:selectedKeys="state.selectedKeys" mode="inline" theme="light"
:inline-collapsed="state.collapsed" :items="items" style="border-inline-end: none;" @click="menuItemclick">
</a-menu>
</div>
<div class="contentArea">
<div>
<a-flex>
<a-space>
<a-button @click="toggleCollapsed">
<MenuUnfoldOutlined v-if="state.collapsed" />
<MenuFoldOutlined v-else />
</a-button>
<a-button type="text" @click="jumpdashboard">首页</a-button>
</a-space>
</a-flex>
<a-flex align="center" gap="middle">
<span>{{ username }}</span>
<div>
<a-button type="primary" @click="showModal" danger>
<LogoutOutlined />
</a-button>
<a-modal v-model:open="open" title="提示" @ok="handleOk" @cancel="handleCancel" ok-text="确认" cancel-text="取消">
<p>确定要注销吗</p>
</a-modal>
</div>
<Simplebar>
<div id="container">
<div class="leftSidebar" :style="{ width: state.menuWidth }">
<a-flex justify="center" align="center" style="height: 48px;">
<component :is=iconComponents.TitleOutLined :class="collapsed ? 'small' : 'large'" />
<span v-if="!collapsed">{{ state.name }}</span>
</a-flex> </a-flex>
<a-menu v-model:openKeys="state.openKeys" v-model:selectedKeys="state.selectedKeys" mode="inline" theme="light"
:inline-collapsed="state.collapsed" :items="items" style="border-inline-end: none;" @click="menuItemclick">
</a-menu>
</div>
<div class="contentArea">
<div>
<a-flex>
<a-space>
<a-button @click="toggleCollapsed">
<MenuUnfoldOutlined v-if="state.collapsed" />
<MenuFoldOutlined v-else />
</a-button>
<a-button type="text" @click="jumpdashboard">首页</a-button>
</a-space>
</a-flex>
<a-flex align="center" gap="middle">
<span>{{ username }}</span>
<div>
<a-button type="primary" @click="showModal" danger>
<LogoutOutlined />
</a-button>
<a-modal v-model:open="open" title="提示" @ok="handleOk" @cancel="handleCancel" ok-text="确认"
cancel-text="取消">
<p>确定要注销吗</p>
</a-modal>
</div>
</a-flex>
</div>
<RouterView />
</div> </div>
<RouterView />
</div> </div>
</div>
</Simplebar>
</Simplebar>
</template> </template>
<script setup lang='ts'> <script setup lang='ts'>
@ -46,7 +47,7 @@ import { RouterView, useRouter } from 'vue-router'
import { MenuFoldOutlined, MenuUnfoldOutlined, LogoutOutlined, DashboardOutlined } from '@ant-design/icons-vue'; import { MenuFoldOutlined, MenuUnfoldOutlined, LogoutOutlined, DashboardOutlined } from '@ant-design/icons-vue';
import { get } from "@/tools/request" import { get } from "@/tools/request"
import { message } from 'ant-design-vue'; import { message } from 'ant-design-vue';
import { useAuthStore } from '@/store/index'
import { useAuthStore } from '@/stores/index'
const state = reactive({ const state = reactive({
collapsed: false, collapsed: false,
@ -79,34 +80,41 @@ const items = reactive([
}, },
{ {
key: '4', key: '4',
icon: () => h(iconComponents.DiaryOutLined),
label: '语录管理',
title: '语录管理',
url: '/admin/classticmanage'
},
{
key: '5',
icon: () => h(iconComponents.TypeOutLined), icon: () => h(iconComponents.TypeOutLined),
label: '分类管理', label: '分类管理',
title: '分类管理', title: '分类管理',
url: '/admin/typemanage' url: '/admin/typemanage'
}, },
{ {
key: '5',
key: '6',
icon: () => h(iconComponents.CommentOutLined), icon: () => h(iconComponents.CommentOutLined),
label: '评论管理', label: '评论管理',
title: '评论管理', title: '评论管理',
url: '/admin/commentmanage' url: '/admin/commentmanage'
}, },
{ {
key: '6',
key: '7',
icon: () => h(iconComponents.PhotoOutLined), icon: () => h(iconComponents.PhotoOutLined),
label: '相册管理', label: '相册管理',
title: '相册管理', title: '相册管理',
url: '/admin/imagemanage' url: '/admin/imagemanage'
}, },
{ {
key: '7',
key: '8',
icon: () => h(iconComponents.FileOutLined), icon: () => h(iconComponents.FileOutLined),
label: '文件管理', label: '文件管理',
title: '文件管理', title: '文件管理',
url: '/admin/filemanage' url: '/admin/filemanage'
}, },
{ {
key: '8',
key: '9',
icon: () => h(iconComponents.SystemOutLined), icon: () => h(iconComponents.SystemOutLined),
label: '系统设置', label: '系统设置',
title: '系统设置', title: '系统设置',
@ -179,7 +187,8 @@ const handleCancel = (e: MouseEvent) => {
.contentArea { .contentArea {
flex: 1; flex: 1;
} }
.contentArea>:first-child{
.contentArea>:first-child {
margin: 24px; margin: 24px;
display: flex; display: flex;
flex-direction: row; flex-direction: row;

5
src/router/admin.ts

@ -15,6 +15,11 @@ const adminRoute:Array<RouteRecordRaw>=[
path:"blogmanage", path:"blogmanage",
name:"blogmanage", name:"blogmanage",
component:()=>import("@/views/admin/blogmange/BlogManageView.vue") component:()=>import("@/views/admin/blogmange/BlogManageView.vue")
},
{
path:"classticmanage",
name:"classticmanage",
component:()=>import("@/views/admin/classticmanage/ClassticManageView.vue")
} }
] ]

18
src/store/index.ts

@ -1,18 +0,0 @@
import { defineStore } from 'pinia';
export const useAuthStore = defineStore({
id: 'auth',
state: () => ({
token: '',
username:''
}),
actions: {
setToken(token:string) {
this.token = token;
},
removeToken(){
localStorage.removeItem('token');
}
},
});

48
src/stores/blog.ts

@ -1,48 +0,0 @@
import { ref } from 'vue'
import { defineStore } from 'pinia'
import { get } from "@/tools/request"
import dayjs from 'dayjs';
export const blogContentStore = defineStore("blog", () => {
interface blogInterface {
key:string,
id: number,
blogtitle: string,
create_at: Date,
readnum: number,
readminite: number,
wordcount: number,
img: string,
blogcontent: string,
typename: string,
labelname: string
}
const bloglist = ref<blogInterface[]>([])
async function blogList() {
try {
const response = await get("/blogs/list");
if (response) {
bloglist.value = response.data.data.map((items: any,index:any) => ({
key: (index + 1).toString(),
id: items.id,
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,
labelname: items.labelname
}))
} else {
console.log("bloglist is not exits")
}
} catch (error) {
console.log("bloglist is error")
}
}
return { blogList, bloglist }
})

13
src/stores/counter.ts

@ -1,13 +0,0 @@
import { ref, computed } from 'vue'
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', () => {
const count = ref(0)
const doubleCount = computed(() => count.value * 2)
function increment() {
count.value++
}
return { count, doubleCount, increment }
})

110
src/stores/index.ts

@ -0,0 +1,110 @@
import { reactive, ref } from 'vue'
import { defineStore } from 'pinia'
import { get } from "@/tools/request"
import dayjs from 'dayjs';
export const useAuthStore = defineStore("auth", () => {
const tokenValue = ref("")
function setToken(token: string) {
tokenValue.value = token
}
function removeToken() {
localStorage.removeItem('token');
}
return { setToken, removeToken }
})
// 博客列表接口
export const blogContentStore = defineStore("blog", () => {
interface blogInterface {
key: string,
blogtitle: string,
create_at: Date,
readnum: number,
readminite: number,
wordcount: number,
img: string,
blogcontent: string,
typename: string,
labelnames: string
}
const bloglist = ref<blogInterface[]>([])
async function blogList() {
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 classticContentStore = defineStore("classtic", () => {
interface classticInterface {
key: string,
header: string,
text: string,
descr: string
}
const classticlist = ref<classticInterface[]>([])
async function classticList() {
try {
const response = await get("/classtics/list")
if (response) {
classticlist.value = response.data.data.map((items: any, index: any) => ({
key: (index + 1).toString(),
header: items.header,
text: items.text,
descr: items.descr
}))
} else {
console.log("classtic is not exit")
}
} catch (error) {
console.log("classtic is error")
}
}
return { classticList, classticlist }
})
// 语录单查询
export const classticSearchStore = defineStore("classticSearch", () => {
const classticsearch = ref<{ header: string }>()
async function classticSearch() {
try {
const response = get(
"/classtics/search",
classticsearch.value?.header
)
if (response) {
classticsearch.value = (await response).data.data.map((items: any) => ({
header: items.header
}))
} else {
console.log("header is not exit")
}
} catch (error) {
console.log("header is error")
}
}
return { classticsearch, classticSearch }
})

1
src/views/admin/blogmange/BlogFormView.vue

@ -34,7 +34,6 @@ import type { UnwrapRef } from 'vue';
import type { Rule } from 'ant-design-vue/es/form'; import type { Rule } from 'ant-design-vue/es/form';
import { post, get, put } from '@/tools/request'; import { post, get, put } from '@/tools/request';
import { useRouter, useRoute } from 'vue-router' import { useRouter, useRoute } from 'vue-router'
import type { TypeStateData } from '@/api/types'
const router = useRouter() const router = useRouter()
const route = useRoute() const route = useRoute()
interface FormState { interface FormState {

14
src/views/admin/blogmange/BlogManageView.vue

@ -34,7 +34,7 @@
<script setup lang='ts'> <script setup lang='ts'>
import { ref, reactive, onMounted } from 'vue'; import { ref, reactive, onMounted } from 'vue';
import {blogContentStore} from "@/stores/blog"
import {blogContentStore} from "@/stores/index"
import 'dayjs/locale/zh-cn'; import 'dayjs/locale/zh-cn';
import locale from 'ant-design-vue/es/date-picker/locale/zh_CN'; import locale from 'ant-design-vue/es/date-picker/locale/zh_CN';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
@ -81,18 +81,6 @@ const blogAdd = function () {
// //
onMounted(async () => { onMounted(async () => {
// get("/blogs/list").then(response => {
// dataSource.value = (response as any).data.data.map((items: any, index: any) => ({
// key: (index + 1).toString(),
// id: items.id,
// 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
// }))
// })
blogContent.blogList() blogContent.blogList()
}); });

100
src/views/admin/classticmanage/ClassticManageView.vue

@ -0,0 +1,100 @@
<template>
<div class="content">
<div class="search">
<a-space>
<a-input v-model:value="searchList.classtictitle" placeholder="语录标题" />
</a-space>
<a-space style="margin-left: 16px;">
<a-button @click="classticSearch.classticSearch()">查询</a-button>
<a-button type="primary" ghost @click="classticAdd">新增</a-button>
</a-space>
</div>
<div class="table">
<a-table bordered :data-source="classticContent.classticlist" :columns="columns">
<template #bodyCell="{ column, record }">
<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">
<p>确认删除吗</p>
</a-modal>
</div>
<a-button size="small" type="primary" ghost @click="ackUpdate(record)">编辑</a-button>
<a-button size="small" type="primary" ghost @click="ackWatch(record)">预览</a-button>
</a-space>
</template>
</template>
</a-table>
</div>
</div>
</template>
<script setup lang='ts'>
import { ref, reactive, onMounted } from 'vue';
import type { searchValue } from "@/api/admin"
import { useRouter } from "vue-router"
const router = useRouter()
import {classticContentStore,classticSearchStore} from "@/stores"
const searchList = reactive<searchValue>({
classtictitle:""
})
const classticContent=classticContentStore()
const classticSearch=classticSearchStore()
const open = ref<boolean>(false);
const toDelete = reactive<{ key: string, id: number }>({
key: '',
id: 0
});
const showModal = (key: string, id: number) => {
open.value = true;
// key id ref 便使
toDelete.key = key;
toDelete.id = id;
};
//
const blogAdd = function () {
router.push('/admin/blogmanage/add')
}
//
onMounted(async () => {
classticContent.classticList()
});
const columns = [
{
title: '序号',
dataIndex: 'key',
width: '5%',
},
{
title: '语录标题',
dataIndex: 'header',
width: '20%',
},
{
title: '语录内容',
dataIndex: 'text',
},
{
title: '备注',
dataIndex: 'descr',
},
{
title: '操作',
dataIndex: 'operation',
},
];
</script>
<style scoped>
.content {
padding: 24px 24px 0 24px;
}
.search {
margin: 0 0 24px 0;
}
</style>

46
src/views/blog/blogcontent/BlogListView.vue

@ -1,47 +1,50 @@
<template> <template>
<div class="main"> <div class="main">
<div class="mainContent" v-for="article in blogContent.bloglist"> <div class="mainContent" v-for="article in blogContent.bloglist">
<a-badge-ribbon :text=article.labelid color="black">
<a-badge-ribbon :text=article.typename color="black">
<a-card hoverable> <a-card hoverable>
<h2>{{article.blogtitle}}</h2>
<h2>{{ article.blogtitle }}</h2>
<div class="tag-group"> <div class="tag-group">
<a-tag color="#E6E6FA"> <a-tag color="#E6E6FA">
<template #icon> <template #icon>
<RiLiLined />
<component :is=iconComponents.RiLiLined />
</template> </template>
{{ article.create_at }} {{ article.create_at }}
</a-tag> </a-tag>
<a-tag color="#6495ED"> <a-tag color="#6495ED">
<template #icon> <template #icon>
<YanJingLined />
<component :is=iconComponents.YanJingLined />
</template> </template>
{{ article.readminite }}
{{ article.readnum }}
</a-tag> </a-tag>
<a-tag color="#B0C4DE"> <a-tag color="#B0C4DE">
<template #icon> <template #icon>
<XieZiLined />
<component :is=iconComponents.XieZiLined />
</template> </template>
字数{{ article.wordcount }} 字数{{ article.wordcount }}
</a-tag> </a-tag>
<a-tag color="#20B2AA"> <a-tag color="#20B2AA">
<template #icon> <template #icon>
<YueDuLined />
<component :is=iconComponents.YueDuLined />
</template> </template>
阅读时长{{ article.readminite }} 阅读时长{{ article.readminite }}
</a-tag> </a-tag>
</div> </div>
<div class="blog-content"> <div class="blog-content">
<div> <div>
<a-image :preview="false" :width="200"
:src=article.img />
<a-image :preview="false" :width="200" :src=article.img />
</div> </div>
<div class="text-container"> <div class="text-container">
{{article.blogcontent}}
{{ article.blogcontent }}
</div> </div>
</div> </div>
<div class="read-button"> <div class="read-button">
<a-button type="primary" shape="round">阅读全文</a-button> <a-button type="primary" shape="round">阅读全文</a-button>
</div> </div>
<hr class="custom-line">
<div>
<a-tag :color=randomColor() v-for="label in JSON.parse(article.labelnames)">{{ label }}</a-tag>
</div>
</a-card> </a-card>
</a-badge-ribbon> </a-badge-ribbon>
</div> </div>
@ -49,9 +52,17 @@
</template> </template>
<script setup lang='ts'> <script setup lang='ts'>
import { onMounted } from 'vue';
import { blogContentStore } from "@/stores/blog"
import { onMounted, ref } from 'vue';
import { blogContentStore } from "@/stores"
import iconComponents from '@/assets';
const blogContent = blogContentStore(); const blogContent = blogContentStore();
const randomColor = () => {
const labelColor = ref(["processing", "success", "error", "warning", "magenta", "red", "volcano", "orange", "gold", "lime", "green", "cyan", "blue", "geekblue", "purple"])
return labelColor.value[Math.floor(Math.random() * labelColor.value.length)];
}
onMounted(() => { onMounted(() => {
blogContent.blogList(); blogContent.blogList();
}); });
@ -85,7 +96,8 @@ onMounted(() => {
display: flex; display: flex;
margin: 48px; margin: 48px;
} }
.main .blog-content>:first-child{
.main .blog-content>:first-child {
padding: 4px; padding: 4px;
border: 2px solid #ccc; border: 2px solid #ccc;
display: inline-block; display: inline-block;
@ -103,7 +115,6 @@ onMounted(() => {
.main .read-button { .main .read-button {
display: flex; display: flex;
justify-content: center; justify-content: center;
} }
.main .read-button button { .main .read-button button {
@ -116,6 +127,13 @@ onMounted(() => {
/* 添加颜色变化的动画 */ /* 添加颜色变化的动画 */
} }
.custom-line {
border: 0;
height: 1px;
background: #d9d6d6;
margin: 48px 0 24px 0;
}
@keyframes colorChange { @keyframes colorChange {
0% { 0% {
background-color: #007bff; background-color: #007bff;

Loading…
Cancel
Save