refactor: 重构博客管理模块

This commit is contained in:
2025-06-10 23:29:55 +08:00
parent 5bf38c2f1a
commit 2fcc2453cf
15 changed files with 149 additions and 87 deletions

View File

@@ -1,166 +0,0 @@
<template>
<div class="admin-module">
<div class="button-container">
<el-button type="primary" @click="addNewBookClick">新增账本</el-button>
</div>
<el-table
:data="billStore.billBookList"
border stripe
:header-cell-style="tableHeaderStyle()"
:cell-style="tableCellStyle()"
:row-style="tableRowStyle()"
style="width: 50%"
class="card-item">
<el-table-column type="index" align="center" width="50" />
<el-table-column prop="name" label="名称" align="center" min-width="10%"/>
<el-table-column label="图标" align="center" min-width="10%">
<template #default="scope">
<i :class="scope.row.icon" :style="{color: scope.row.color, fontSize: '24px'}"></i>
</template>
</el-table-column>
<el-table-column fixed="right" label="操作" align="center" width="200">
<template #default="scope">
<el-button type="primary" @click="editBookClick(scope.row)">编辑</el-button>
<el-button type="danger" @click="deleteBookClick(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<el-dialog v-model="billInfo.billBookDialog.visible"
:title="billInfo.billBookDialog.title"
center width="500" @open="openDialogEvent">
<el-form ref="formRef" :model="billInfo.billBookDialog.data"
:rules="rules" label-width="100px">
<el-form-item label="账本名称" prop="name">
<el-input v-model="billInfo.billBookDialog.data.name" autocomplete="off"
placeholder="请输入类型名称"
:maxlength="10" show-word-limit/>
</el-form-item>
<el-form-item label="类型图标" prop="icon">
<el-input v-model="billInfo.billBookDialog.data.icon" autocomplete="off"
placeholder="请输入类型图标"
:maxlength="40" show-word-limit/>
</el-form-item>
<el-form-item label="图标颜色" prop="color">
<el-color-picker v-model="billInfo.billBookDialog.data.color" />
</el-form-item>
<el-form-item label="图标预览">
<i :class="billInfo.billBookDialog.data.icon"
:style="{color: billInfo.billBookDialog.data.color, fontSize: '24px'}"></i>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="cancelClick">取消</el-button>
<el-button type="primary" @click="confirmClick">确认</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script lang="ts" setup>
import { nextTick, onMounted, reactive, ref } from 'vue'
import { useBillStore } from '@/store/bill'
import { ElMessage, FormRules } from 'element-plus'
import { IBillBook } from '@/types/bill'
import { billBookRules } from '@/utils/element/elRules'
import { deepCopyObject } from 'vue3-common/utils/dataUtil'
import type { IDialog, IHandleApi } from 'vue3-common/types'
import { addBillBookApi, updateBillBookApi } from '@/apis/bill'
import { tableCellStyle, tableHeaderStyle, tableRowStyle } from 'vue3-common/utils/elUtil'
const formRef = ref()
const rules = reactive<FormRules>(billBookRules)
const billStore = useBillStore()
const billInfo = reactive({
setBillBookType: 'ADD' as IHandleApi,
billBookDialog: {
title: '',
visible: false,
data: {}
} as IDialog<IBillBook>
})
onMounted(async () => {
await billStore.queryBillBook()
})
/**
* 点击新增账本按钮
*/
const addNewBookClick = () => {
billInfo.setBillBookType = 'ADD'
billInfo.billBookDialog = {
title: '新增账本',
visible: true,
data: {
name: '',
icon: '',
color: ''
}
}
}
/**
* 点击编辑账本按钮
* @param book 账本
*/
const editBookClick = async (book: IBillBook) => {
billInfo.setBillBookType = 'UPDATE'
billInfo.billBookDialog = {
title: '编辑账本',
visible: true,
data: deepCopyObject(book)
}
}
/**
* 点击删除账本按钮
* @param book 账本
*/
const deleteBookClick = (book: IBillBook) => {
billInfo.setBillBookType = 'DELETE'
ElMessage.error('暂不支持该功能')
}
const openDialogEvent = () => {
nextTick(() => {
formRef.value.clearValidate()
})
}
const setBillBookApi = async (id?: number) => {
switch (billInfo.setBillBookType) {
case 'ADD':
await addBillBookApi(billInfo.billBookDialog.data)
ElMessage.success('新增账本成功')
break
case 'UPDATE':
await updateBillBookApi(id as number, billInfo.billBookDialog.data)
ElMessage.success('更新账本成功')
break
case 'DELETE':
ElMessage.success('删除账本成功')
break
default:
break
}
await billStore.queryBillBook()
billInfo.billBookDialog.visible = false
}
const cancelClick = () => {
billInfo.billBookDialog.visible = false
}
const confirmClick = async () => {
await formRef.value.validate(async (valid) => {
if (valid) {
await setBillBookApi(billInfo.billBookDialog.data.id)
}
})
}
</script>

View File

@@ -1,223 +0,0 @@
<template>
<div class="admin-module">
<div class="button-container">
<el-select
v-model="billInfo.bookName"
placeholder="请选择账本名称"
@focus="focusBookNameEvent"
@change="selectBookNameEvent"
style="width: 150px;">
<el-option
v-for="(item, index) in billInfo.billBookList"
:key="index" :label="item.name" :value="item.name"
/>
</el-select>
<el-button type="primary" @click="addNewCategoryClick">新增类别</el-button>
</div>
<el-table
:data="billInfo.billCategoryList"
border stripe
:header-cell-style="tableHeaderStyle()"
:cell-style="tableCellStyle()"
:row-style="tableRowStyle()"
class="card-item">
<el-table-column type="index" align="center" width="50"/>
<el-table-column prop="name" label="名称" align="center" min-width="10%"/>
<el-table-column prop="bookName" label="账本" align="center" min-width="10%"/>
<el-table-column label="类型" align="center" min-width="10%">
<template #default="scope">
<el-tag v-if="scope.row.type === 'expensive'" effect="dark" color="#CA6C65">支出</el-tag>
<el-tag v-else effect="dark" color="#6FBB69">收入</el-tag>
</template>
</el-table-column>
<el-table-column label="图标" align="center" min-width="10%">
<template #default="scope">
<i :class="scope.row.icon" :style="{color: scope.row.color, fontSize: '24px'}"></i>
</template>
</el-table-column>
<el-table-column fixed="right" label="操作" align="center" width="200">
<template #default="scope">
<el-button type="primary" @click="editCategoryClick(scope.row)">编辑</el-button>
<el-button type="danger" @click="deleteCategoryClick(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<el-dialog v-model="billInfo.billCategoryDialog.visible"
:title="billInfo.billCategoryDialog.title"
center width="500" @open="openDialogEvent">
<el-form ref="formRef" :model="billInfo.billCategoryDialog.data"
:rules="rules" label-width="100px">
<el-form-item label="账本名称" prop="bookName">
<el-select
v-model="billInfo.billCategoryDialog.data.bookName"
placeholder="请选择账本名称"
@focus="focusBookNameEvent">
<el-option
v-for="(item, index) in billInfo.billBookList"
:key="index" :label="item.name" :value="item.name"
/>
</el-select>
</el-form-item>
<el-form-item label="类别名称" prop="name">
<el-input v-model="billInfo.billCategoryDialog.data.name" autocomplete="off"
placeholder="请输入类型名称"
:maxlength="10" show-word-limit/>
</el-form-item>
<el-form-item label="类型名称" prop="type">
<el-radio-group v-model="billInfo.billCategoryDialog.data.type">
<el-radio value="expensive">支出</el-radio>
<el-radio value="income">收入</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="类别图标" prop="icon">
<el-input v-model="billInfo.billCategoryDialog.data.icon" autocomplete="off"
placeholder="请输入类型图标"
:maxlength="40" show-word-limit/>
</el-form-item>
<el-form-item label="图标颜色" prop="color">
<el-color-picker v-model="billInfo.billCategoryDialog.data.color" />
</el-form-item>
<el-form-item label="图标预览">
<i :class="billInfo.billCategoryDialog.data.icon"
:style="{color: billInfo.billCategoryDialog.data.color, fontSize: '24px'}"></i>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="cancelClick">取消</el-button>
<el-button type="primary" @click="confirmClick">确认</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script lang="ts" setup>
import { nextTick, onMounted, reactive, ref } from 'vue'
import { useBillStore } from '@/store/bill'
import { IBillBook, IBillCategory } from '@/types/bill'
import { ElMessage, FormRules } from 'element-plus'
import { billCategoryRules } from '@/utils/element/elRules'
import { addBillCategoryApi, updateBillCategoryApi } from '@/apis/bill'
import { deepCopyObject } from 'vue3-common/utils/dataUtil'
import { tableHeaderStyle, tableRowStyle, tableCellStyle } from 'vue3-common/utils/elUtil'
import type { IDialog, IHandleApi } from 'vue3-common/types'
const formRef = ref()
const rules = reactive<FormRules>(billCategoryRules)
const billStore = useBillStore()
const billInfo = reactive({
bookName: '日常账本',
setBillCategoryType: 'ADD' as IHandleApi,
billCategoryDialog: {
title: '',
visible: false,
data: {}
} as IDialog<IBillCategory>,
billBookList: [] as IBillBook[],
billCategoryList: [] as IBillCategory[]
})
onMounted(async () => {
await billStore.queryBillCategory()
queryBookCategoryByBookName(billInfo.bookName)
})
const selectBookNameEvent = (value: string) => {
queryBookCategoryByBookName(value)
}
const queryBookCategoryByBookName = (bookName: string) => {
billInfo.billCategoryList = billStore.billCategoryList.filter((item) => {
return item.bookName === bookName
})
}
/**
* 点击新增类别按钮
*/
const addNewCategoryClick = () => {
billInfo.setBillCategoryType = 'ADD'
billInfo.billCategoryDialog = {
title: '新增账单类别',
visible: true,
data: {
bookName: '',
name: '',
type: '',
icon: '',
color: ''
}
}
}
/**
* 点击编辑类别按钮
* @param category 类别
*/
const editCategoryClick = async (category: IBillCategory) => {
billInfo.setBillCategoryType = 'UPDATE'
billInfo.billCategoryDialog = {
title: '编辑账单类别',
visible: true,
data: deepCopyObject(category)
}
}
/**
* 点击删除类别按钮
* @param category 类别
*/
const deleteCategoryClick = (category: IBillCategory) => {
billInfo.setBillCategoryType = 'DELETE'
ElMessage.error('暂不支持该功能')
}
const openDialogEvent = () => {
nextTick(() => {
formRef.value.clearValidate()
})
}
const focusBookNameEvent = async () => {
await billStore.queryBillBook()
billInfo.billBookList = billStore.billBookList
}
const setBillCategoryApi = async (id?: number) => {
switch (billInfo.setBillCategoryType) {
case 'ADD':
await addBillCategoryApi(billInfo.billCategoryDialog.data)
ElMessage.success('新增账单类别成功')
break
case 'UPDATE':
await updateBillCategoryApi(id as number, billInfo.billCategoryDialog.data)
ElMessage.success('更新账单类别成功')
break
case 'DELETE':
ElMessage.success('删除账单类别成功')
break
default:
break
}
await billStore.queryBillCategory()
billInfo.billCategoryDialog.visible = false
}
const cancelClick = () => {
billInfo.billCategoryDialog.visible = false
}
const confirmClick = async () => {
await formRef.value.validate(async (valid) => {
if (valid) {
await setBillCategoryApi(billInfo.billCategoryDialog.data.id)
queryBookCategoryByBookName(billInfo.bookName)
}
})
}
</script>

View File

@@ -1,166 +0,0 @@
<template>
<div class="admin-module">
<div class="button-container">
<el-button type="primary" @click="addNewPayClick">新增账户</el-button>
</div>
<el-table
:data="billStore.billPayList"
border stripe
:header-cell-style="tableHeaderStyle()"
:cell-style="tableCellStyle()"
:row-style="tableRowStyle()"
style="width: 50%"
class="card-item">
<el-table-column type="index" align="center" width="50" />
<el-table-column prop="name" label="名称" align="center" min-width="10%"/>
<el-table-column label="图标" align="center" min-width="10%">
<template #default="scope">
<i :class="scope.row.icon" :style="{color: scope.row.color, fontSize: '24px'}"></i>
</template>
</el-table-column>
<el-table-column fixed="right" label="操作" align="center" width="200">
<template #default="scope">
<el-button type="primary" @click="editPayClick(scope.row)">编辑</el-button>
<el-button type="danger" @click="deletePayClick(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<el-dialog v-model="billInfo.billPayDialog.visible"
:title="billInfo.billPayDialog.title"
center width="500" @open="openDialogEvent">
<el-form ref="formRef" :model="billInfo.billPayDialog.data"
:rules="rules" label-width="100px">
<el-form-item label="账户名称" prop="name">
<el-input v-model="billInfo.billPayDialog.data.name" autocomplete="off"
placeholder="请输入类型名称"
:maxlength="10" show-word-limit/>
</el-form-item>
<el-form-item label="账户图标" prop="icon">
<el-input v-model="billInfo.billPayDialog.data.icon" autocomplete="off"
placeholder="请输入类型图标"
:maxlength="40" show-word-limit/>
</el-form-item>
<el-form-item label="图标颜色" prop="color">
<el-color-picker v-model="billInfo.billPayDialog.data.color" />
</el-form-item>
<el-form-item label="图标预览">
<i :class="billInfo.billPayDialog.data.icon"
:style="{color: billInfo.billPayDialog.data.color, fontSize: '24px'}"></i>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="cancelClick">取消</el-button>
<el-button type="primary" @click="confirmClick">确认</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script lang="ts" setup>
import { nextTick, onMounted, reactive, ref } from 'vue'
import { useBillStore } from '@/store/bill'
import { ElMessage, FormRules } from 'element-plus'
import { IBillPay } from '@/types/bill'
import { billPayRules } from '@/utils/element/elRules'
import { deepCopyObject } from 'vue3-common/utils/dataUtil'
import { addBillPayApi, updateBillPayApi } from '@/apis/bill'
import { tableCellStyle, tableHeaderStyle, tableRowStyle } from 'vue3-common/utils/elUtil'
import type { IDialog, IHandleApi } from 'vue3-common/types'
const formRef = ref()
const rules = reactive<FormRules>(billPayRules)
const billStore = useBillStore()
const billInfo = reactive({
setBillPayType: 'ADD' as IHandleApi,
billPayDialog: {
title: '',
visible: false,
data: {}
} as IDialog<IBillPay>
})
onMounted(async () => {
await billStore.queryBillPay()
})
/**
* 点击新增账户按钮
*/
const addNewPayClick = () => {
billInfo.setBillPayType = 'ADD'
billInfo.billPayDialog = {
title: '新增账户',
visible: true,
data: {
name: '',
icon: '',
color: ''
}
}
}
/**
* 点击编辑账户按钮
* @param pay 账户
*/
const editPayClick = async (pay: IBillPay) => {
billInfo.setBillPayType = 'UPDATE'
billInfo.billPayDialog = {
title: '编辑账户',
visible: true,
data: deepCopyObject(pay)
}
}
/**
* 点击删除账户按钮
* @param pay 账户
*/
const deletePayClick = (pay: IBillPay) => {
billInfo.setBillPayType = 'DELETE'
ElMessage.error('暂不支持该功能')
}
const openDialogEvent = () => {
nextTick(() => {
formRef.value.clearValidate()
})
}
const setBillBookApi = async (id?: number) => {
switch (billInfo.setBillPayType) {
case 'ADD':
await addBillPayApi(billInfo.billPayDialog.data)
ElMessage.success('新增账户成功')
break
case 'UPDATE':
await updateBillPayApi(id as number, billInfo.billPayDialog.data)
ElMessage.success('更新账户成功')
break
case 'DELETE':
ElMessage.success('删除账户成功')
break
default:
break
}
await billStore.queryBillPay()
billInfo.billPayDialog.visible = false
}
const cancelClick = () => {
billInfo.billPayDialog.visible = false
}
const confirmClick = async () => {
await formRef.value.validate((valid) => {
if (valid) {
setBillBookApi(billInfo.billPayDialog.data.id)
}
})
}
</script>

View File

@@ -28,8 +28,9 @@
</el-table-column>
<el-table-column prop="wordCount" label="字数" align="center" min-width="10%" />
<el-table-column prop="visitCount" label="阅读次数" align="center" min-width="10%" />
<el-table-column fixed="right" label="操作" align="center" width="200">
<el-table-column fixed="right" label="操作" align="center" width="250">
<template #default="scope">
<el-button type="primary" @click="previewBlogClick(scope.row)">预览</el-button>
<el-button type="primary" @click="editBlogClick(scope.row)">编辑</el-button>
<el-button type="danger" @click="deleteBlogClick(scope.row)">删除</el-button>
</template>
@@ -78,6 +79,14 @@ const addNewBlogClick = () => {
router.push({ name: 'AdminBlogDetailId', params: { id: 0 } })
}
/**
* 点击预览博客按钮
* @param blog 博客
*/
const previewBlogClick = async (blog: IBlog) => {
await router.push({ name: 'AdminBlogPreviewId', params: { id: blog.id } })
}
/**
* 点击编辑博客按钮
* @param blog 博客

View File

@@ -33,11 +33,8 @@
<MdEditor v-model="blogStore.newBlog.content" @onUploadImg="onUploadImg"/>
<div class="button-container">
<el-button type="primary" @click="submitFormEvent">保存</el-button>
</div>
<div class="back-button">
<el-button type="primary" @click="backClick">返回</el-button>
<el-button type="primary" @click="submitFormEvent">保存</el-button>
</div>
</div>
</template>
@@ -148,11 +145,5 @@ const backClick = () => {
.md-editor {
height: 80vh;
}
.back-button {
position: absolute;
top: 10px;
left: 10px;
}
}
</style>

View File

@@ -0,0 +1,34 @@
<template>
<div class="admin-module">
<h3>{{ blogStore.currentBlog.title }}</h3>
<MdPreview editorId="blog-id" v-model="blogStore.currentBlog.content"/>
<div class="button-container">
<el-button type="primary" @click="backClick">返回</el-button>
</div>
</div>
</template>
<script lang="ts" setup>
import { onMounted } from 'vue'
import { useBlogStore } from '@/store/blog.ts'
import { MdPreview } from 'md-editor-v3'
import 'md-editor-v3/lib/style.css'
import { useRouter, useRoute } from 'vue-router'
const route = useRoute()
const router = useRouter()
const blogStore = useBlogStore()
onMounted(async () => {
const blogId = route.params.id as number
await blogStore.queryAdminBlogById(blogId)
blogStore.newBlog = blogStore.currentBlog
})
const backClick = () => {
router.push('/admin/blog/article')
}
</script>

View File

@@ -20,49 +20,29 @@ export const adminMeta: IRouteMetaConfig = {
'/admin/blog/article': {
title: '文章管理',
icon: '',
order: 1
},
'/admin/blog/category': {
title: '类别管理',
icon: '',
order: 2
},
'/admin/blog/visit': {
title: '访问记录',
icon: '',
order: 3
},
'/admin/blog/detail/:id': {
title: '详细',
icon: '',
order: 4,
hidden: true
},
'/admin/bill': {
title: '账单管理',
icon: 'admin-bill',
order: 3,
redirect: '/admin/bill/book'
},
'/admin/bill/book': {
title: '账本管理',
'/admin/blog/preview/:id': {
title: '详细',
icon: '',
order: 1
},
'/admin/bill/pay': {
title: '账户管理',
icon: '',
order: 2
},
'/admin/bill/category': {
title: '类别管理',
icon: '',
order: 3
hidden: true
},
'/admin/quartz': {
title: '定时任务',
icon: 'admin-quartz',
order: 4,
order: 3,
redirect: '/admin/quartz/index'
},
'/admin/quartz/index': {
@@ -72,7 +52,7 @@ export const adminMeta: IRouteMetaConfig = {
'/admin/account': {
title: '账号管理',
icon: 'admin-account',
order: 5,
order: 4,
redirect: '/admin/account/index'
},
'/admin/account/index': {