feat:内容管理基本功能,富文本编辑器基本封装等
This commit is contained in:
parent
1525f0abad
commit
5b893b29f4
1
.gitignore
vendored
1
.gitignore
vendored
@ -8,6 +8,7 @@ pnpm-debug.log*
|
|||||||
lerna-debug.log*
|
lerna-debug.log*
|
||||||
|
|
||||||
node_modules
|
node_modules
|
||||||
|
package-lock.json
|
||||||
.DS_Store
|
.DS_Store
|
||||||
dist
|
dist
|
||||||
dist-ssr
|
dist-ssr
|
||||||
|
|||||||
58
package-lock.json
generated
58
package-lock.json
generated
@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
"name": "light-blog-frontend",
|
"name": "light-blog-admin",
|
||||||
"version": "0.1.1",
|
"version": "0.1.1",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "light-blog-frontend",
|
"name": "light-blog-admin",
|
||||||
"version": "0.1.1",
|
"version": "0.1.1",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "~1.7.2",
|
"axios": "~1.7.2",
|
||||||
@ -15,7 +15,8 @@
|
|||||||
"pinia": "~2.1.7",
|
"pinia": "~2.1.7",
|
||||||
"pinia-plugin-persistedstate": "~3.2.1",
|
"pinia-plugin-persistedstate": "~3.2.1",
|
||||||
"vue": "~3.4.21",
|
"vue": "~3.4.21",
|
||||||
"vue-router": "~4.3.0"
|
"vue-router": "~4.3.0",
|
||||||
|
"wangeditor": "^4.7.15"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@rushstack/eslint-patch": "~1.8.0",
|
"@rushstack/eslint-patch": "~1.8.0",
|
||||||
@ -44,6 +45,29 @@
|
|||||||
"node": ">=6.0.0"
|
"node": ">=6.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@babel/runtime": {
|
||||||
|
"version": "7.24.6",
|
||||||
|
"resolved": "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.24.6.tgz",
|
||||||
|
"integrity": "sha512-Ja18XcETdEl5mzzACGd+DKgaGJzPTCow7EglgwTmHdwokzDFYh/MHua6lU6DV/hjF2IaOJ4oX2nqnjG7RElKOw==",
|
||||||
|
"dependencies": {
|
||||||
|
"regenerator-runtime": "^0.14.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@babel/runtime-corejs3": {
|
||||||
|
"version": "7.24.6",
|
||||||
|
"resolved": "https://registry.npmmirror.com/@babel/runtime-corejs3/-/runtime-corejs3-7.24.6.tgz",
|
||||||
|
"integrity": "sha512-tbC3o8uHK9xMgMsvUm9qGqxVpbv6yborMBLbDteHIc7JDNHsTV0vDMQ5j1O1NXvO+BDELtL9KgoWYaUVIVGt8w==",
|
||||||
|
"dependencies": {
|
||||||
|
"core-js-pure": "^3.30.2",
|
||||||
|
"regenerator-runtime": "^0.14.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@ctrl/tinycolor": {
|
"node_modules/@ctrl/tinycolor": {
|
||||||
"version": "3.6.1",
|
"version": "3.6.1",
|
||||||
"resolved": "https://registry.npmmirror.com/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz",
|
"resolved": "https://registry.npmmirror.com/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz",
|
||||||
@ -1306,6 +1330,16 @@
|
|||||||
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
|
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/core-js-pure": {
|
||||||
|
"version": "3.37.1",
|
||||||
|
"resolved": "https://registry.npmmirror.com/core-js-pure/-/core-js-pure-3.37.1.tgz",
|
||||||
|
"integrity": "sha512-J/r5JTHSmzTxbiYYrzXg9w1VpqrYt+gexenBE9pugeyhwPZTAEJddyiReJWsLO6uNQ8xJZFbod6XC7KKwatCiA==",
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/core-js"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/cross-spawn": {
|
"node_modules/cross-spawn": {
|
||||||
"version": "7.0.3",
|
"version": "7.0.3",
|
||||||
"resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.3.tgz",
|
"resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.3.tgz",
|
||||||
@ -2586,6 +2620,11 @@
|
|||||||
"node": ">=8.10.0"
|
"node": ">=8.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/regenerator-runtime": {
|
||||||
|
"version": "0.14.1",
|
||||||
|
"resolved": "https://registry.npmmirror.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
|
||||||
|
"integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="
|
||||||
|
},
|
||||||
"node_modules/require-directory": {
|
"node_modules/require-directory": {
|
||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"resolved": "https://registry.npmmirror.com/require-directory/-/require-directory-2.1.1.tgz",
|
"resolved": "https://registry.npmmirror.com/require-directory/-/require-directory-2.1.1.tgz",
|
||||||
@ -2867,8 +2906,7 @@
|
|||||||
"node_modules/tslib": {
|
"node_modules/tslib": {
|
||||||
"version": "2.6.2",
|
"version": "2.6.2",
|
||||||
"resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.6.2.tgz",
|
"resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.6.2.tgz",
|
||||||
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==",
|
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/type-check": {
|
"node_modules/type-check": {
|
||||||
"version": "0.4.0",
|
"version": "0.4.0",
|
||||||
@ -3022,6 +3060,16 @@
|
|||||||
"vue": "^3.2.0"
|
"vue": "^3.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/wangeditor": {
|
||||||
|
"version": "4.7.15",
|
||||||
|
"resolved": "https://registry.npmmirror.com/wangeditor/-/wangeditor-4.7.15.tgz",
|
||||||
|
"integrity": "sha512-aPTdREd8BxXVyJ5MI+LU83FQ7u1EPd341iXIorRNYSOvoimNoZ4nPg+yn3FGbB93/owEa6buLw8wdhYnMCJQLg==",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.11.2",
|
||||||
|
"@babel/runtime-corejs3": "^7.11.2",
|
||||||
|
"tslib": "^2.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/which": {
|
"node_modules/which": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmmirror.com/which/-/which-2.0.2.tgz",
|
"resolved": "https://registry.npmmirror.com/which/-/which-2.0.2.tgz",
|
||||||
|
|||||||
@ -18,7 +18,8 @@
|
|||||||
"pinia": "~2.1.7",
|
"pinia": "~2.1.7",
|
||||||
"pinia-plugin-persistedstate": "~3.2.1",
|
"pinia-plugin-persistedstate": "~3.2.1",
|
||||||
"vue": "~3.4.21",
|
"vue": "~3.4.21",
|
||||||
"vue-router": "~4.3.0"
|
"vue-router": "~4.3.0",
|
||||||
|
"wangeditor": "^4.7.15"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@rushstack/eslint-patch": "~1.8.0",
|
"@rushstack/eslint-patch": "~1.8.0",
|
||||||
|
|||||||
@ -16,6 +16,17 @@ export const getContentList = (data = { page: 1, size: 10, sort: 'asc' }) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取分类列表
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export const getCategoryList = () => {
|
||||||
|
return http({
|
||||||
|
url: '/admin/category',
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 删除此 id 的内容
|
* 删除此 id 的内容
|
||||||
* @param {String} id
|
* @param {String} id
|
||||||
@ -34,12 +45,35 @@ export const deleteOneById = (id = '') => {
|
|||||||
/**
|
/**
|
||||||
* 获取内容详情
|
* 获取内容详情
|
||||||
*/
|
*/
|
||||||
|
export const getContentDetailByIdOrTitle = (data = {}) => {
|
||||||
export const getContentDetailBy_uidOrTitle = (data = {}) => {
|
if (!data.id && !data.title) return false
|
||||||
if (!data._uid && !data.title) return false
|
|
||||||
return http({
|
return http({
|
||||||
url: '/admin/content',
|
url: '/admin/content',
|
||||||
method: 'get',
|
method: 'get',
|
||||||
params: data
|
params: data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新内容
|
||||||
|
*/
|
||||||
|
export const updateContent = (data = {}) => {
|
||||||
|
if (!data.id) return false
|
||||||
|
return http({
|
||||||
|
url: '/admin/content',
|
||||||
|
method: 'put',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新内容
|
||||||
|
*/
|
||||||
|
export const createContent = (data = {}) => {
|
||||||
|
if (!data.title || !data.content || !data.summary || !data.category) return false
|
||||||
|
return http({
|
||||||
|
url: '/admin/content',
|
||||||
|
method: 'post',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@ -1,3 +0,0 @@
|
|||||||
import Editor from './src/Editor.vue'
|
|
||||||
|
|
||||||
export { Editor }
|
|
||||||
3
src/components/my-editor/index.js
Normal file
3
src/components/my-editor/index.js
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import myEditor from './src/my-editor.vue'
|
||||||
|
|
||||||
|
export { myEditor }
|
||||||
173
src/components/my-editor/src/my-editor.vue
Normal file
173
src/components/my-editor/src/my-editor.vue
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
<script setup>
|
||||||
|
import { onBeforeUnmount, ref, onMounted, computed } from 'vue'
|
||||||
|
import E from 'wangeditor'
|
||||||
|
import { useLoginStore } from '@/stores/user/loginStore'
|
||||||
|
const { token } = useLoginStore()
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
category: {
|
||||||
|
type: Array,
|
||||||
|
required: true,
|
||||||
|
default: () => {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
content: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
default: () => {
|
||||||
|
return {
|
||||||
|
title: '',
|
||||||
|
_uid: '',
|
||||||
|
author: '',
|
||||||
|
category: '',
|
||||||
|
created_at: '',
|
||||||
|
summary: '',
|
||||||
|
content: '',
|
||||||
|
isTop: false,
|
||||||
|
isShow: false,
|
||||||
|
enableReward: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
loadingState: {
|
||||||
|
type: Boolean,
|
||||||
|
required: true,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const emit = defineEmits(['submit'])
|
||||||
|
|
||||||
|
const blogContent = computed(() => props.content)
|
||||||
|
|
||||||
|
let editor = ref(null)
|
||||||
|
|
||||||
|
// 编辑器配置和初始化
|
||||||
|
const initEditor = () => {
|
||||||
|
editor.value = new E('#editor-w')
|
||||||
|
// 设置编辑区域高度为 500px
|
||||||
|
editor.value.config.height = 540
|
||||||
|
editor.value.config.placeholder = '请输入内容...'
|
||||||
|
// 自动 focus
|
||||||
|
editor.value.config.focus = true
|
||||||
|
|
||||||
|
// 图片上传接口
|
||||||
|
editor.value.config.uploadImgServer = '/api/admin/file'
|
||||||
|
// file 别名字段
|
||||||
|
editor.value.config.uploadFileName = 'blog-files'
|
||||||
|
editor.value.config.uploadImgMaxSize = 4 * 1024 * 1024 // 4M
|
||||||
|
editor.value.config.uploadImgMaxLength = 9 // 一次最多上传 9 个图片
|
||||||
|
editor.value.config.uploadImgAccept = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'ico']
|
||||||
|
// Authorization TOKEN
|
||||||
|
editor.value.config.uploadImgHeaders = {
|
||||||
|
Authorization: `Bearer ${token}`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 视频上传接口
|
||||||
|
editor.value.config.uploadVideoServer = '/api/admin/file'
|
||||||
|
editor.value.config.uploadVideoMaxSize = 1024 * 1024 * 200 // 200m
|
||||||
|
editor.value.config.uploadVideoFileName = 'blog-files'
|
||||||
|
editor.value.config.uploadVideoAccept = ['mp4', 'ogg', 'webm']
|
||||||
|
editor.value.config.uploadVideoHeaders = {
|
||||||
|
Authorization: `Bearer ${token}`
|
||||||
|
}
|
||||||
|
// 创建编辑器
|
||||||
|
editor.value.create()
|
||||||
|
|
||||||
|
// 初始化内容
|
||||||
|
editor.value.txt.html(blogContent.value.content)
|
||||||
|
}
|
||||||
|
|
||||||
|
const confirmLeavePage = (event) => {
|
||||||
|
event.preventDefault() // 阻止默认的处理方式
|
||||||
|
event.returnValue = '' // 这一行在旧版浏览器中会显示自定义的提示
|
||||||
|
}
|
||||||
|
onMounted(() => {
|
||||||
|
initEditor()
|
||||||
|
window.addEventListener('beforeunload', confirmLeavePage)
|
||||||
|
})
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
editor.value.destroy()
|
||||||
|
window.removeEventListener('beforeunload', confirmLeavePage)
|
||||||
|
})
|
||||||
|
|
||||||
|
const handleClear = () => {
|
||||||
|
editor.value.txt.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSubmit = () => {
|
||||||
|
// console.log(editor.value.txt.html())
|
||||||
|
|
||||||
|
blogContent.value.content = editor.value.txt.html()
|
||||||
|
emit('submit', blogContent.value)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="content-others">
|
||||||
|
<el-form :data="blogContent" inline>
|
||||||
|
<el-form-item label="标题">
|
||||||
|
<el-input v-model="blogContent.title" placeholder="请输入标题" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="是否展示:">
|
||||||
|
<el-radio-group v-model="blogContent.isShow">
|
||||||
|
<el-radio :value="true">展示</el-radio>
|
||||||
|
<el-radio :value="false">不展示</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="所属分类:">
|
||||||
|
<el-select v-model="blogContent.category" style="width: 240px">
|
||||||
|
<el-option-group
|
||||||
|
v-for="group in props.category"
|
||||||
|
:key="group.name + group.id"
|
||||||
|
:label="group.name"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in group.children"
|
||||||
|
:key="item.name"
|
||||||
|
:label="item.name"
|
||||||
|
:value="item.name"
|
||||||
|
/>
|
||||||
|
</el-option-group>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="是否置顶推荐:">
|
||||||
|
<el-radio-group v-model="blogContent.isTop">
|
||||||
|
<el-radio :value="true">置顶</el-radio>
|
||||||
|
<el-radio :value="false">不置顶</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="是否开启打赏:">
|
||||||
|
<el-radio-group v-model="blogContent.enableReward">
|
||||||
|
<el-radio :value="true">开启</el-radio>
|
||||||
|
<el-radio :value="false">关闭</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="摘要">
|
||||||
|
<el-input
|
||||||
|
v-model="blogContent.summary"
|
||||||
|
style="width: 800px"
|
||||||
|
:rows="3"
|
||||||
|
type="textarea"
|
||||||
|
placeholder="请输入摘要..."
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
<div id="editor-w"></div>
|
||||||
|
<div class="handle-bar">
|
||||||
|
<el-button plain type="danger" @click="handleClear">清空</el-button>
|
||||||
|
<el-button type="warning" @click="handleSubmit" :loading="loadingState">提交</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.content-others {
|
||||||
|
}
|
||||||
|
.handle-bar {
|
||||||
|
padding: 20px 40px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
3
src/locale/index.js
Normal file
3
src/locale/index.js
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import zhCn from 'element-plus/es/locale/lang/zh-cn'
|
||||||
|
|
||||||
|
export { zhCn }
|
||||||
@ -8,6 +8,7 @@ import router from './router'
|
|||||||
import { useStore } from './stores'
|
import { useStore } from './stores'
|
||||||
|
|
||||||
import ElementPlus from 'element-plus'
|
import ElementPlus from 'element-plus'
|
||||||
|
|
||||||
import 'element-plus/dist/index.css'
|
import 'element-plus/dist/index.css'
|
||||||
|
|
||||||
const app = createApp(App)
|
const app = createApp(App)
|
||||||
|
|||||||
@ -5,6 +5,8 @@ const API_URL = import.meta.env.VITE_APP_API_BASE_URL || '/api'
|
|||||||
|
|
||||||
import { useLoginStore } from '@/stores/user/loginStore'
|
import { useLoginStore } from '@/stores/user/loginStore'
|
||||||
|
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
|
|
||||||
const http = axios.create({
|
const http = axios.create({
|
||||||
baseURL: API_URL,
|
baseURL: API_URL,
|
||||||
timeout: 1000 * 15,
|
timeout: 1000 * 15,
|
||||||
@ -57,6 +59,12 @@ http.interceptors.response.use(
|
|||||||
return response.data
|
return response.data
|
||||||
},
|
},
|
||||||
(error) => {
|
(error) => {
|
||||||
|
const { clearLoginInfo } = useLoginStore()
|
||||||
|
if (error.response.status === 401) {
|
||||||
|
const router = useRouter()
|
||||||
|
clearLoginInfo()
|
||||||
|
router.push({ name: 'login' })
|
||||||
|
}
|
||||||
ElNotification({
|
ElNotification({
|
||||||
title: '请求错误',
|
title: '请求错误',
|
||||||
message: `${error.response.status}-${!error.response.data.msg ? error.response.statusText : error.response.data.msg}`,
|
message: `${error.response.status}-${!error.response.data.msg ? error.response.statusText : error.response.data.msg}`,
|
||||||
|
|||||||
@ -1,24 +1,73 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { onMounted, ref } from 'vue'
|
import { onMounted, ref } from 'vue'
|
||||||
|
|
||||||
import { getContentList, deleteOneById, getContentDetailBy_uidOrTitle } from '@/api/content/content'
|
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
|
// wangEditor
|
||||||
|
import { myEditor } from '@c/my-editor'
|
||||||
|
|
||||||
|
import { useLoginStore } from '@/stores/user/loginStore'
|
||||||
|
|
||||||
|
import {
|
||||||
|
getContentList,
|
||||||
|
getCategoryList,
|
||||||
|
deleteOneById,
|
||||||
|
getContentDetailByIdOrTitle,
|
||||||
|
updateContent,
|
||||||
|
createContent
|
||||||
|
} from '@/api/content/content'
|
||||||
|
|
||||||
|
const { username } = useLoginStore()
|
||||||
|
|
||||||
|
const searchFromData = ref({
|
||||||
|
title: '',
|
||||||
|
timeRange: [],
|
||||||
|
page: 1,
|
||||||
|
size: 10,
|
||||||
|
sort: 'desc'
|
||||||
|
})
|
||||||
|
|
||||||
|
const handleSubmitSearch = () => {
|
||||||
|
getContentListData()
|
||||||
|
}
|
||||||
|
|
||||||
// 获取列表
|
// 获取列表
|
||||||
const blogList = ref([])
|
const blogList = ref([])
|
||||||
const getContentListData = () => {
|
const getContentListData = () => {
|
||||||
getContentList({
|
getContentList({
|
||||||
page: 1,
|
...searchFromData.value
|
||||||
size: 10,
|
|
||||||
sort: 'asc'
|
|
||||||
}).then((res) => {
|
}).then((res) => {
|
||||||
if (res.code === 20200) {
|
if (res.code === 20200) {
|
||||||
blogList.value = res.data.list
|
blogList.value = res.data.list
|
||||||
|
} else {
|
||||||
|
ElMessage({
|
||||||
|
message: '获取内容列表失败',
|
||||||
|
type: 'error',
|
||||||
|
duration: 2000
|
||||||
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取分类
|
||||||
|
const categoryList = ref([])
|
||||||
|
const getCategoryListData = () => {
|
||||||
|
getCategoryList().then((res) => {
|
||||||
|
if (res.code === 20200) {
|
||||||
|
categoryList.value = res.data.list
|
||||||
|
} else {
|
||||||
|
ElMessage({
|
||||||
|
message: '获取分类失败',
|
||||||
|
type: 'error',
|
||||||
|
duration: 2000
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取初始数据
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getContentListData()
|
getContentListData()
|
||||||
|
getCategoryListData()
|
||||||
})
|
})
|
||||||
|
|
||||||
// 删除
|
// 删除
|
||||||
@ -41,18 +90,159 @@ const handleDelete = (id) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 获取详情并编辑
|
// 获取详情并编辑
|
||||||
const handleEdit = (id) => {
|
// 编辑器弹窗
|
||||||
getContentDetailBy_uidOrTitle({ _uid: id }).then((res) => {
|
const editorData = ref({
|
||||||
console.log(res)
|
title: '',
|
||||||
|
content: {
|
||||||
|
title: '',
|
||||||
|
_uid: '',
|
||||||
|
author: '',
|
||||||
|
category: '',
|
||||||
|
created_at: '',
|
||||||
|
summary: '',
|
||||||
|
content: '',
|
||||||
|
isTop: false,
|
||||||
|
isShow: false,
|
||||||
|
enableReward: false
|
||||||
|
},
|
||||||
|
dialogShow: false,
|
||||||
|
submitBtnLoading: false
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const handleAdd = () => {
|
||||||
|
editorData.value.content = {
|
||||||
|
title: '',
|
||||||
|
_uid: '',
|
||||||
|
author: username,
|
||||||
|
category: '',
|
||||||
|
created_at: '',
|
||||||
|
summary: '',
|
||||||
|
content: '',
|
||||||
|
isTop: false,
|
||||||
|
isShow: false,
|
||||||
|
enableReward: false
|
||||||
|
}
|
||||||
|
editorData.value.dialogShow = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleEdit = (id) => {
|
||||||
|
getContentDetailByIdOrTitle({ id: id }).then((res) => {
|
||||||
|
if (res.code === 20200) {
|
||||||
|
editorData.value.content = res.data
|
||||||
|
editorData.value.dialogShow = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const onEditorSubmit = (data) => {
|
||||||
|
// 提交按钮 loding
|
||||||
|
editorData.value.submitBtnLoading = true
|
||||||
|
// 新增
|
||||||
|
if (data._uid === '') {
|
||||||
|
console.log(data)
|
||||||
|
createContent({
|
||||||
|
title: data.title,
|
||||||
|
author: data.author,
|
||||||
|
category: data.category,
|
||||||
|
summary: data.summary,
|
||||||
|
content: data.content,
|
||||||
|
enable_reward: data.enableReward,
|
||||||
|
is_show: data.isShow,
|
||||||
|
is_top: data.isTop
|
||||||
|
})
|
||||||
|
.then(async (res) => {
|
||||||
|
if (res.code === 20200) {
|
||||||
|
await getContentListData()
|
||||||
|
// 弹窗关闭
|
||||||
|
editorData.value.dialogShow = false
|
||||||
|
ElMessage({
|
||||||
|
message: '新增成功',
|
||||||
|
type: 'success',
|
||||||
|
duration: 2000
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
ElMessage({
|
||||||
|
message: res.msg,
|
||||||
|
type: 'error',
|
||||||
|
duration: 2000
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
editorData.value.submitBtnLoading = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 修改
|
||||||
|
else {
|
||||||
|
updateContent({
|
||||||
|
id: data._uid,
|
||||||
|
title: data.title,
|
||||||
|
category: data.category,
|
||||||
|
summary: data.summary,
|
||||||
|
content: data.content,
|
||||||
|
isShow: data.isShow,
|
||||||
|
isTop: data.isTop,
|
||||||
|
enableReward: data.enableReward
|
||||||
|
})
|
||||||
|
.then(async (res) => {
|
||||||
|
if (res.code === 20200) {
|
||||||
|
await getContentListData()
|
||||||
|
// 弹窗关闭
|
||||||
|
editorData.value.dialogShow = false
|
||||||
|
ElMessage({
|
||||||
|
message: '编辑成功',
|
||||||
|
type: 'success',
|
||||||
|
duration: 2000
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
ElMessage({
|
||||||
|
message: res.msg,
|
||||||
|
type: 'error',
|
||||||
|
duration: 2000
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
editorData.value.submitBtnLoading = false
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="blog-content">
|
<div class="blog-content">
|
||||||
<div class="search-box"></div>
|
<div class="search-box">
|
||||||
|
<el-form :data="searchFromData" inline>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="success" @click="handleAdd">新增</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="标题">
|
||||||
|
<el-input
|
||||||
|
v-model="searchFromData.title"
|
||||||
|
placeholder="请输入标题"
|
||||||
|
clearable
|
||||||
|
style="width: 220px"
|
||||||
|
></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="时间">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="searchFromData.timeRange"
|
||||||
|
placeholder="请选择时间段"
|
||||||
|
type="daterange"
|
||||||
|
range-separator="至"
|
||||||
|
start-placeholder="开始日期"
|
||||||
|
end-placeholder="结束日期"
|
||||||
|
value-format="YYYY-MM-DD"
|
||||||
|
clearable
|
||||||
|
></el-date-picker>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button @click="handleSubmitSearch">搜索</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
<div class="content-list">
|
<div class="content-list">
|
||||||
<el-table :data="blogList" border>
|
<el-table :data="blogList" border height="500">
|
||||||
<el-table-column label="序号" type="index" align="center" width="90"></el-table-column>
|
<el-table-column label="序号" type="index" align="center" width="90"></el-table-column>
|
||||||
<el-table-column label="标题" prop="title" align="center" width="140"></el-table-column>
|
<el-table-column label="标题" prop="title" align="center" width="140"></el-table-column>
|
||||||
<el-table-column label="作者" prop="author" align="center" width="100"></el-table-column>
|
<el-table-column label="作者" prop="author" align="center" width="100"></el-table-column>
|
||||||
@ -78,12 +268,42 @@ const handleEdit = (id) => {
|
|||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="操作" align="center" width="140">
|
<el-table-column label="操作" align="center" width="140">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-button type="warning" size="small" @click="handleEdit(row._uid)">编辑</el-button>
|
<el-button plain type="warning" size="small" @click="handleEdit(row._uid)"
|
||||||
<el-button type="danger" size="small" @click="handleDelete(row._uid)">删除</el-button>
|
>编辑</el-button
|
||||||
|
>
|
||||||
|
<el-popconfirm
|
||||||
|
width="220"
|
||||||
|
confirm-button-text="确定"
|
||||||
|
confirm-button-type="danger"
|
||||||
|
cancel-button-text="取消"
|
||||||
|
icon-color="#ff4c6c"
|
||||||
|
title="确定删除此项吗?删除后不可恢复!"
|
||||||
|
@confirm="handleDelete(row._uid)"
|
||||||
|
>
|
||||||
|
<template #reference>
|
||||||
|
<el-button plain type="danger" size="small">删除</el-button>
|
||||||
|
</template>
|
||||||
|
</el-popconfirm>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 编辑器dialog -->
|
||||||
|
<el-dialog
|
||||||
|
v-model="editorData.dialogShow"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
:close-on-press-escape="false"
|
||||||
|
destroy-on-close
|
||||||
|
fullscreen
|
||||||
|
>
|
||||||
|
<myEditor
|
||||||
|
:content="editorData.content"
|
||||||
|
:category="categoryList"
|
||||||
|
@submit="onEditorSubmit"
|
||||||
|
:loadingState="editorData.submitBtnLoading"
|
||||||
|
></myEditor>
|
||||||
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@ -23,5 +23,8 @@ const beian = '蜀ICP备2023007248号-2'
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
> div {
|
||||||
|
transform: scale(0.84);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -1,11 +1,13 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import baseLayout from '@/layout/baseLayout/baseLayout.vue'
|
import baseLayout from '@/layout/baseLayout/baseLayout.vue'
|
||||||
import { RouterView } from 'vue-router'
|
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
|
import { RouterView } from 'vue-router'
|
||||||
|
|
||||||
import { useLoginStore } from '@/stores/user/loginStore'
|
import { useLoginStore } from '@/stores/user/loginStore'
|
||||||
const loginStore = useLoginStore()
|
const loginStore = useLoginStore()
|
||||||
|
|
||||||
|
import { zhCn } from '@/locale/index'
|
||||||
|
|
||||||
let username = computed(() => loginStore.username)
|
let username = computed(() => loginStore.username)
|
||||||
|
|
||||||
import headerBar from './components/headerBar.vue'
|
import headerBar from './components/headerBar.vue'
|
||||||
@ -20,6 +22,7 @@ const handleLogout = () => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
<el-config-provider :locale="zhCn">
|
||||||
<div>
|
<div>
|
||||||
<baseLayout>
|
<baseLayout>
|
||||||
<template #header>
|
<template #header>
|
||||||
@ -36,6 +39,7 @@ const handleLogout = () => {
|
|||||||
</template>
|
</template>
|
||||||
</baseLayout>
|
</baseLayout>
|
||||||
</div>
|
</div>
|
||||||
|
</el-config-provider>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped></style>
|
<style lang="scss" scoped></style>
|
||||||
|
|||||||
@ -59,6 +59,11 @@ export default defineConfig(({ mode }) => {
|
|||||||
target: 'http://localhost:8081/',
|
target: 'http://localhost:8081/',
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
rewrite: (path) => path.replace(/^\/api/, '')
|
rewrite: (path) => path.replace(/^\/api/, '')
|
||||||
|
},
|
||||||
|
'/static': {
|
||||||
|
target: 'http://localhost:8081/static',
|
||||||
|
changeOrigin: true,
|
||||||
|
rewrite: (path) => path.replace(/^\/static/, '')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user