11 changed files with 493 additions and 127 deletions
-
4src/components/admin/MainWrapper.vue
-
106src/components/blogs/HomePage.vue
-
68src/components/blogs/ceshi.vue
-
5src/router/admin.ts
-
4src/router/blog.ts
-
48src/stores/blog.ts
-
1src/stores/counter.ts
-
138src/views/admin/blogmange/BlogFormView.vue
-
188src/views/admin/blogmange/BlogManageView.vue
-
11src/views/blog/blogcontent/BlogDetailView.vue
-
39src/views/blog/blogcontent/BlogListView.vue
@ -1,65 +1,11 @@ |
|||
<template> |
|||
<a-menu v-model:selectedKeys="current" mode="horizontal" :items="items" /> |
|||
<div>{{ }}</div> |
|||
</template> |
|||
<script lang="ts" setup> |
|||
import { h, ref } from 'vue'; |
|||
import { MailOutlined, AppstoreOutlined, SettingOutlined } from '@ant-design/icons-vue'; |
|||
import { MenuProps } from 'ant-design-vue'; |
|||
const current = ref<string[]>(['app']); |
|||
const items = ref<MenuProps['items']>([ |
|||
{ |
|||
key: 'mail', |
|||
icon: () => h(MailOutlined), |
|||
label: 'Navigation One', |
|||
title: 'Navigation One', |
|||
}, |
|||
{ |
|||
key: 'app', |
|||
icon: () => h(AppstoreOutlined), |
|||
label: 'Navigation Two', |
|||
title: 'Navigation Two', |
|||
}, |
|||
{ |
|||
key: 'sub1', |
|||
icon: () => h(SettingOutlined), |
|||
label: 'Navigation Three - Submenu', |
|||
title: 'Navigation Three - Submenu', |
|||
children: [ |
|||
{ |
|||
type: 'group', |
|||
label: 'Item 1', |
|||
children: [ |
|||
{ |
|||
label: 'Option 1', |
|||
key: 'setting:1', |
|||
}, |
|||
{ |
|||
label: 'Option 2', |
|||
key: 'setting:2', |
|||
}, |
|||
], |
|||
}, |
|||
{ |
|||
type: 'group', |
|||
label: 'Item 2', |
|||
children: [ |
|||
{ |
|||
label: 'Option 3', |
|||
key: 'setting:3', |
|||
}, |
|||
{ |
|||
label: 'Option 4', |
|||
key: 'setting:4', |
|||
}, |
|||
], |
|||
}, |
|||
], |
|||
}, |
|||
{ |
|||
key: 'alipay', |
|||
label: h('a', { href: 'https://antdv.com', target: '_blank' }, 'Navigation Four - Link'), |
|||
title: 'Navigation Four - Link', |
|||
}, |
|||
]); |
|||
|
|||
<script setup lang='ts'> |
|||
|
|||
</script> |
|||
|
|||
<style> |
|||
|
|||
</style> |
@ -0,0 +1,48 @@ |
|||
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 } |
|||
}) |
@ -0,0 +1,138 @@ |
|||
<template> |
|||
<div id="content"> |
|||
<a-form ref="formRef" :model="formState" :rules="rules"> |
|||
<a-flex gap="large" justify="space-between"> |
|||
<a-form-item ref="blogtitle" label="博客标题" name="blogtitle"> |
|||
<a-input v-model:value="formState.blogtitle" placeholder="请输入博客标题" class="items" /> |
|||
</a-form-item> |
|||
<a-form-item label="博客类型" name="blogtype"> |
|||
<a-select v-model:value="formState.typeid" placeholder="请选择博客类型" class="items"> |
|||
<a-select-option v-for="typeItem in typedata" :key="typeItem.key" :value="typeItem.id">{{ |
|||
typeItem.typename }}</a-select-option> |
|||
</a-select> |
|||
</a-form-item> |
|||
</a-flex> |
|||
<v-md-editor v-model="content" height="900px"></v-md-editor> |
|||
<a-form-item label="博客备注" name="descr" class="desrpad"> |
|||
<a-textarea v-model:value="formState.descr" /> |
|||
</a-form-item> |
|||
<a-form-item :wrapper-col="{ span: 2, offset: 22 }"> |
|||
<a-button @click="resetForm">取消</a-button> |
|||
<a-button style="margin-left: 24px" type="primary" @click="onSubmit">提交</a-button> |
|||
</a-form-item> |
|||
<div> |
|||
<div class="blog-content" v-html="formState.blogcontent"></div> |
|||
<!-- 其他表单元素 --> |
|||
</div> |
|||
</a-form> |
|||
</div> |
|||
</template> |
|||
|
|||
<script lang="ts" setup> |
|||
import { ref, onMounted, reactive } from 'vue'; |
|||
import type { UnwrapRef } from 'vue'; |
|||
import type { Rule } from 'ant-design-vue/es/form'; |
|||
import { post, get, put } from '@/tools/request'; |
|||
import { useRouter, useRoute } from 'vue-router' |
|||
import type { TypeStateData } from '@/api/types' |
|||
const router = useRouter() |
|||
const route = useRoute() |
|||
interface FormState { |
|||
id?: number; |
|||
blogtitle: string; |
|||
typeid: number; |
|||
blogcontent: string; |
|||
descr: string; |
|||
} |
|||
const formRef = ref(); |
|||
|
|||
const formState: UnwrapRef<FormState> = reactive({ |
|||
blogtitle: '', |
|||
typeid: 1, |
|||
blogcontent: "", |
|||
descr: '', |
|||
}); |
|||
const rules: Record<string, Rule[]> = { |
|||
blogtitle: [ |
|||
{ required: true, message: '请输入博客标题', trigger: 'change' }, |
|||
{ min: 4, max: 20, message: '博客标题允许4-20个字符', trigger: 'blur' }, |
|||
], |
|||
descr: [ |
|||
{ required: false }, |
|||
{ max: 255, message: '博客备注不得超过255个字符', trigger: 'change' }, |
|||
], |
|||
}; |
|||
const content = ref(); |
|||
const typedata = ref<TypeStateData[]>([]) |
|||
onMounted(async () => { |
|||
get("/types/list").then(response => { |
|||
typedata.value = response.data.data.map((items: any) => ({ |
|||
...items |
|||
})) |
|||
}) |
|||
const { id } = route.params |
|||
if (id) { |
|||
get(`/blogs/list/search/${id}`).then(response => { |
|||
const blog = response.data.data; |
|||
// 设置表单的初始值为编辑博客的信息 |
|||
formState.id = blog.id; |
|||
formState.blogtitle = blog.blogtitle; |
|||
formState.typeid = blog.typeid; |
|||
formState.blogcontent = blog.blogcontent; |
|||
formState.descr = blog.descr; |
|||
// 初始化 Vditor 并设置内容 |
|||
console.log(blog.id) |
|||
if (blog.id) { |
|||
content.value=blog.blogcontent; |
|||
} |
|||
}) |
|||
} |
|||
}); |
|||
const onSubmit = () => { |
|||
formRef.value |
|||
.validate() |
|||
.then(async () => { |
|||
const formdata = { |
|||
blogtitle: formState.blogtitle, |
|||
typeid: formState.typeid, |
|||
blogcontent: content.value, |
|||
descr: formState.descr |
|||
} |
|||
if (formState.id) { |
|||
await put(`/blogs/update/${formState.id}`, formdata); |
|||
} else { |
|||
await post('/blogs/add', formdata); |
|||
} |
|||
}) |
|||
router.push("/admin/blogmanage") |
|||
.catch((error: any) => { |
|||
console.log('error', error); |
|||
}); |
|||
}; |
|||
const resetForm = () => { |
|||
formRef.value.resetFields(); |
|||
}; |
|||
</script> |
|||
|
|||
<style scoped> |
|||
.editor-container { |
|||
width: 100%; |
|||
} |
|||
|
|||
.vditor { |
|||
width: 100%; |
|||
border: 1px solid #ccc; |
|||
} |
|||
|
|||
.items { |
|||
width: calc(75vw/2); |
|||
} |
|||
|
|||
#content { |
|||
padding: 24px 24px 0 24px; |
|||
} |
|||
|
|||
.desrpad { |
|||
padding: 24px 0 0 0; |
|||
} |
|||
</style> |
@ -0,0 +1,188 @@ |
|||
<template> |
|||
<div class="content"> |
|||
<div class="search"> |
|||
<a-space> |
|||
<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">查询</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"> |
|||
<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 {blogContentStore} from "@/stores/blog" |
|||
import 'dayjs/locale/zh-cn'; |
|||
import locale from 'ant-design-vue/es/date-picker/locale/zh_CN'; |
|||
import dayjs from 'dayjs'; |
|||
import { useRouter } from "vue-router" |
|||
import { get, remove } from '@/tools/request'; |
|||
import { message } from 'ant-design-vue'; |
|||
dayjs.locale('zh-cn'); |
|||
const blogContent=blogContentStore() |
|||
const router = useRouter() |
|||
const searchList = reactive<{ blogtitle: string, typename: string, start_date: string, end_date: string }>({ |
|||
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 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 () => { |
|||
// 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() |
|||
}); |
|||
|
|||
// 查询事件 |
|||
const blogSearch = () => { |
|||
get("/blogs/list/search", { |
|||
blogtitle: searchList.blogtitle, |
|||
typename: searchList.typename, |
|||
start_date: searchList.start_date, |
|||
end_date: searchList.end_date, |
|||
}) |
|||
.then(response => { |
|||
dataSource.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 |
|||
})); |
|||
}) |
|||
.catch(error => { |
|||
console.error('There was an error seraching the blog!', error); |
|||
}); |
|||
}; |
|||
|
|||
// 博客删除 |
|||
const ackDelete = async (record:{key:string,id:number}) => { |
|||
remove(`/blogs/delete/${toDelete.id}`).then((response) => { |
|||
if (response.status === 200) { |
|||
dataSource.value = dataSource.value.filter(item => item.key !== record.key); |
|||
open.value = false; |
|||
} |
|||
message.info("删除成功") |
|||
}).catch(error => { |
|||
console.error('There was an error deleting the blog!', error); |
|||
}) |
|||
}; |
|||
|
|||
// 博客修改 |
|||
const ackUpdate =async (record: { key: string, id: number }) => { |
|||
router.push(`/admin/blogmanage/update/${record.id}`); |
|||
}; |
|||
const ackWatch=async (record: { key: string, id: number }) => { |
|||
router.push(`/admin/blogmanage/watch`); |
|||
} |
|||
const unDelete=async ()=>{ |
|||
message.warn("取消删除") |
|||
} |
|||
const columns = [ |
|||
{ |
|||
title: '序号', |
|||
dataIndex: 'key', |
|||
width: '5%', |
|||
}, |
|||
{ |
|||
title: '博客标题', |
|||
dataIndex: 'blogtitle', |
|||
width: '20%', |
|||
}, |
|||
{ |
|||
title: '博客分类', |
|||
dataIndex: 'typename', |
|||
}, |
|||
{ |
|||
title: '博客标签', |
|||
dataIndex: 'labelname', |
|||
}, |
|||
{ |
|||
title: '发布时间', |
|||
dataIndex: 'create_at', |
|||
}, |
|||
{ |
|||
title: '修改时间', |
|||
dataIndex: 'update_at', |
|||
}, |
|||
{ |
|||
title: '操作', |
|||
dataIndex: 'operation', |
|||
}, |
|||
]; |
|||
</script> |
|||
|
|||
<style scoped> |
|||
.content { |
|||
padding: 24px 24px 0 24px; |
|||
} |
|||
|
|||
.search { |
|||
margin: 0 0 24px 0; |
|||
} |
|||
</style> |
@ -0,0 +1,11 @@ |
|||
<template> |
|||
|
|||
</template> |
|||
|
|||
<script setup lang='ts'> |
|||
|
|||
</script> |
|||
|
|||
<style> |
|||
|
|||
</style> |
Write
Preview
Loading…
Cancel
Save
Reference in new issue