feat:增加docker配置

This commit is contained in:
2025-09-18 16:09:49 +08:00
parent 44e6176150
commit 52c14f5f6f
23 changed files with 75 additions and 294 deletions

5
Dockerfile Normal file
View File

@@ -0,0 +1,5 @@
FROM nginx:1.27.0
COPY dist/ /usr/share/nginx/html/
COPY nginx.conf /etc/nginx/conf.d/default.conf
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
RUN echo 'Asia/Shanghai' > /etc/timezone

18
docker-compose.yml Normal file
View File

@@ -0,0 +1,18 @@
name:
sweet-hut
services:
blog-ui:
image: cxx/blog-ui:0.0.1
container_name: blog-ui
ports:
- "82:80"
extra_hosts:
- "host.docker.internal:host-gateway"
networks:
- sweet-hut
restart: always
networks:
sweet-hut:
driver: bridge

22
nginx.conf Normal file
View File

@@ -0,0 +1,22 @@
server {
listen 80 default_server;
listen [::]:80 default_server;
root /usr/share/nginx/html;
index index.html;
server_name _;
location /blog/ {
alias /usr/share/nginx/html/;
try_files $uri $uri/ /blog/index.html;
}
location /blog-api/ {
proxy_pass http://127.0.0.1:8082/;
}
location / {
try_files $uri $uri/ =404;
}
}

View File

@@ -4,7 +4,7 @@
"version": "0.0.1",
"scripts": {
"dev": "vite",
"build": "vue-tsc -b && vite build",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {

View File

@@ -1,19 +1,19 @@
import { cloudService } from './index'
import { IBlog, IBlogCategory, IBlogComment, IBlogStats, IBlogVisit, INewBlog, IQueryBlog } from '@/types/blog'
import { IBlog, IBlogCategory, IBlogComment, IBlogStats, IQueryBlog } from '@/types/blog'
import qs from 'qs'
import { IPageRecord } from '@/types'
export const queryBlogApi = (currentPage: number, pageSize: number): Promise<IPageRecord<IBlog>> =>
cloudService({
url: '/blog-service/blog/page',
url: '/blog-api/blog/page',
method: 'get',
params: { currentPage, pageSize }
})
export const queryBlogByConditionApi = (query: IQueryBlog): Promise<IBlog[]> =>
cloudService({
url: '/blog-service/blog/condition',
url: '/blog-api/blog/condition',
method: 'get',
params: query,
paramsSerializer: (params) => {
@@ -23,85 +23,37 @@ export const queryBlogByConditionApi = (query: IQueryBlog): Promise<IBlog[]> =>
export const queryBlogByIdApi = (id: number): Promise<IBlog> =>
cloudService({
url: `/blog-service/blog/content/${id}`,
url: `/blog-api/blog/content/${id}`,
method: 'get'
})
export const queryBlogCategoryApi = (): Promise<IBlogCategory[]> =>
cloudService({
url: '/blog-service/blog/category',
url: '/blog-api/blog/category',
method: 'get'
})
export const queryBlogStatsApi = (): Promise<IBlogStats> =>
cloudService({
url: '/blog-service/blog/stats',
url: '/blog-api/blog/stats',
method: 'get'
})
export const queryLatestBlogApi = (): Promise<IBlog[]> =>
cloudService({
url: '/blog-service/blog/latest',
url: '/blog-api/blog/latest',
method: 'get'
})
export const queryBlogCommentApi = (blogId: number): Promise<IBlogComment[]> =>
cloudService({
url: `/blog-service/blog/comment/${blogId}`,
url: `/blog-api/blog/comment/${blogId}`,
method: 'get'
})
export const addBlogCommentApi = (blogComment: IBlogComment): Promise<boolean> =>
cloudService({
url: '/blog-service/blog/comment',
url: '/blog-api/blog/comment',
method: 'put',
data: blogComment
})
export const queryAdminBlogApi = (currentPage: number,
pageSize: number): Promise<IPageRecord<IBlog>> =>
cloudService({
url: '/admin-service/blog-manage/page',
method: 'get',
params: { currentPage, pageSize }
})
export const queryAdminBlogByIdApi = (id: number): Promise<IBlog> =>
cloudService({
url: `/admin-service/blog-manage/content/${id}`,
method: 'get'
})
export const queryAdminBlogCategoryApi = (): Promise<IBlogCategory[]> =>
cloudService({
url: '/admin-service/blog-manage/category',
method: 'get'
})
export const addBlogApi = (blog: INewBlog): Promise<boolean> =>
cloudService({
url: '/admin-service/blog-manage',
method: 'post',
data: blog
})
export const updateBlogApi = (id: number, blog: INewBlog): Promise<boolean> =>
cloudService({
url: `/admin-service/blog-manage/${id}`,
method: 'put',
data: blog
})
export const deleteBlogApi = (id: number): Promise<boolean> =>
cloudService({
url: `/admin-service/blog-manage/${id}`,
method: 'DELETE'
})
export const queryBlogVisitApi = (currentPage: number,
pageSize: number): Promise<IPageRecord<IBlogVisit>> =>
cloudService({
url: '/admin-service/blog-manage/visit',
method: 'get',
params: { currentPage, pageSize }
})

View File

@@ -53,7 +53,7 @@ const changeDateEvent = async (value: Date) => {
*/
const viewBlogClick = async (index: number) => {
const blogId = blogStore.blogList[index].id
await router.push({ name: 'BlogDetail', params: { id: blogId } })
await router.push({ name: 'DetailId', params: { id: blogId } })
}
</script>

View File

@@ -31,7 +31,7 @@ onMounted(() => {
* @param name 类别名称
*/
const viewBlogCategoryClick = async (name: string) => {
await router.push({ name: 'BlogCategoryName', params: { name } })
await router.push({ name: 'CategoryName', params: { name } })
}
</script>

View File

@@ -32,7 +32,7 @@ onMounted(async () => {
const viewBlogClick = async (index: number) => {
const blogId = blogStore.blogList[index].id
await router.push({ name: 'BlogDetailId', params: { id: blogId } })
await router.push({ name: 'DetailId', params: { id: blogId } })
}
</script>

View File

@@ -48,7 +48,7 @@ onMounted(async () => {
*/
const readBlogClick = async (index: number) => {
const blogId = blogStore.blogList[index].id
await router.push({ name: 'BlogDetailId', params: { id: blogId } })
await router.push({ name: 'DetailId', params: { id: blogId } })
}
/**

View File

@@ -1,17 +1,17 @@
<template>
<el-menu router class="blog-menu-container">
<el-menu-item index="/blog/overview">
<el-menu-item index="/overview">
<i class="fa fa-fw fa-home" />
<span class="menu-title">首页</span>
</el-menu-item>
<el-menu-item index="/blog/category">
<el-menu-item index="/category">
<i class="fa fa-fw fa-th" />
<span class="menu-title">分类</span>
<span class="value">{{ blogStore.blogStats.categoryCount }}</span>
</el-menu-item>
<el-menu-item index="/blog/archive">
<el-menu-item index="/archive">
<i class="fa fa-fw fa-archive" />
<span class="menu-title">归档</span>
<span class="value">{{ blogStore.blogStats.blogCount }}</span>

View File

@@ -61,14 +61,14 @@ const router = useRouter()
* 点击查看博客归档按钮
*/
const viewBlogArchiveClick = () => {
router.push('/blog/archive')
router.push('/archive')
}
/**
* 点击查看博客分类按钮
*/
const viewBlogCategoryClick = () => {
router.push('/blog/category')
router.push('/category')
}
/**
@@ -77,6 +77,6 @@ const viewBlogCategoryClick = () => {
*/
const viewBlogClick = async (index: number) => {
const blogId = blogStore.latestBlogList[index].id
await router.push({ name: 'BlogDetail', params: { id: blogId } })
await router.push({ name: 'DetailId', params: { id: blogId } })
}
</script>

View File

@@ -1,8 +1,7 @@
import BlogLayout from '@/layout/index.vue'
import { getRoutersByModules } from 'vue3-common/utils/routerUtil'
import { blogMeta } from '@/views/blog/meta'
import { blogMeta } from '@/views/meta'
const blogRoutes = getRoutersByModules(import.meta.glob('@/views/blog/**/*.vue'), BlogLayout, blogMeta)
const blogRoutes = getRoutersByModules(import.meta.glob('@/views/**/*.vue'), BlogLayout, blogMeta)
export default blogRoutes

View File

@@ -3,7 +3,7 @@ import { RouteRecordRaw } from 'vue-router'
const constantRoutes: RouteRecordRaw[] = [
{
path: '/',
redirect: '/blog/overview'
redirect: { path: '/overview' }
}
]

View File

@@ -1,5 +1,4 @@
import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router'
import { setupRouteGuard } from 'vue3-common/utils/permissionUtil'
// 使用 import.meta.glob 自动导入所有 src/router 目录下的 .ts 文件
const routeModules = import.meta.glob('./*.ts', { eager: true })
@@ -10,11 +9,9 @@ const routes: RouteRecordRaw[] = Object.values(routeModules)
.flat() // 扁平化数组,确保所有路由项都在一个数组中
const router = createRouter({
history: createWebHashHistory(),
history: createWebHashHistory('/blog/'),
scrollBehavior: () => ({ top: 0 }),
routes
})
setupRouteGuard(router)
export default router

View File

@@ -4,12 +4,11 @@ import {
IBlogLatest, IBlogNestedComment, IBlogStats, INewBlog, IQueryBlog
} from '@/types/blog'
import {
addBlogApi, addBlogCommentApi, deleteBlogApi, queryAdminBlogApi, queryAdminBlogByIdApi, queryAdminBlogCategoryApi,
addBlogCommentApi,
queryBlogApi, queryBlogByConditionApi, queryBlogByIdApi,
queryBlogCategoryApi, queryBlogCommentApi, queryBlogStatsApi, queryLatestBlogApi, updateBlogApi
queryBlogCategoryApi, queryBlogCommentApi, queryBlogStatsApi, queryLatestBlogApi
} from '@/apis/blog.ts'
import { IHandleApi } from 'vue3-common/types'
import { ElMessage } from 'element-plus'
import { ImagePathEnum } from '@/types'
import { flattenToTwoLevel } from '@/utils/blog/blogUtil.ts'
@@ -73,18 +72,6 @@ export const useBlogStore = defineStore('blog', {
this.blogList = result.records
this.pageInfo.totalSize = result.total
},
async queryMobileBlog() {
const { currentPage, pageSize } = this.pageInfo
const result = await queryBlogApi(currentPage, pageSize)
this.blogList.push(...result.records)
this.pageInfo.totalSize = result.total
},
async queryAdminBlog() {
const { currentPage, pageSize } = this.adminPageInfo
const result = await queryAdminBlogApi(currentPage, pageSize)
this.blogList = result.records
this.pageInfo.totalSize = result.total
},
async queryBlogByCondition(query: IQueryBlog) {
this.blogList = await queryBlogByConditionApi(query)
},
@@ -93,9 +80,6 @@ export const useBlogStore = defineStore('blog', {
await this.queryBlogComment(this.currentBlogId)
this.blogNestedCommentList = flattenToTwoLevel(this.blogCommentList)
},
async queryAdminBlogById(id: number) {
this.currentBlog = await queryAdminBlogByIdApi(id)
},
async queryBlogComment(blogId: number) {
this.blogCommentList = await queryBlogCommentApi(blogId)
},
@@ -106,50 +90,12 @@ export const useBlogStore = defineStore('blog', {
async queryBlogCategory() {
this.blogCategoryList = await queryBlogCategoryApi()
},
async queryAdminBlogCategory() {
this.blogCategoryList = await queryAdminBlogCategoryApi()
},
async queryBlogStats() {
this.blogStats = await queryBlogStatsApi()
},
async queryLatestBlog() {
this.latestBlogList = await queryLatestBlogApi()
},
async handleBlogApi(id?: number) {
switch (this.blogApiType) {
case 'ADD':
await addBlogApi(this.newBlog)
ElMessage.success('新增博客成功')
break
case 'UPDATE':
await updateBlogApi(this.newBlog.id as number, this.newBlog)
ElMessage.success('更新博客成功')
break
case 'DELETE':
await deleteBlogApi(id as number)
ElMessage.success('删除博客成功')
break
default:
break
}
await this.refreshInfo()
},
async refreshInfo() {
this.pageInfo.currentPage = 1
await this.queryBlog()
await this.queryBlogStats()
await this.queryLatestBlog()
},
getEmptyBlog(): INewBlog {
return {
id: 0,
category: '',
content: '',
isGreat: false,
title: '',
topValue: 0
}
},
getEmptyBlogComment(): IBlogComment {
return {
blogId: 0,

View File

@@ -1,155 +0,0 @@
<template>
<div class="login-view">
<div class="login-container">
<div class="login-title">
<span class="title">
{{ loginInfo.title }}
</span>
</div>
<common-login v-if="authStore.loginType === 'common'"
class="animate__animated animate__bounceIn"/>
<phone-login v-if="authStore.loginType === 'phone'"
class="animate__animated animate__bounceIn"/>
<email-login v-if="authStore.loginType === 'email'"
class="animate__animated animate__bounceIn"/>
</div>
</div>
</template>
<script setup lang="ts">
import CommonLogin from '@/components/Home/Login/CommonLogin.vue'
import PhoneLogin from '@/components/Home/Login/PhoneLogin.vue'
import EmailLogin from '@/components/Home/Login/EmailLogin.vue'
import { useAuthStore } from '@/store/auth'
import { onMounted, onUnmounted, reactive } from 'vue'
const authStore = useAuthStore()
const loginInfo = reactive({
title: 'Sweet Hut'
})
onMounted(() => {
authStore.isVerified = false
// authStore.resetLoginForm()
})
onUnmounted(() => {
authStore.stopCountdown()
})
</script>
<style lang="scss">
$form-item-width: 300px;
$login-height: 500px;
.login-view {
height: 100%;
display: grid;
place-items: center;
.login-container {
// height: $login-height;
padding: 30px;
border-radius: 15px;
box-shadow: 2px 2px 5px #D3D3D3, 4px 4px 10px #A9A9A9;
display: flex;
flex-direction: column;
// justify-content: center;
align-items: center;
gap: 1vh;
.login-title {
margin-bottom: 20px;
.title {
font-size: 1.5rem;
}
}
.common-login {
flex-grow: 1;
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
gap: 1vh;
.login-form {
.el-form-item {
width: $form-item-width;
}
.login-option {
.el-form-item__content {
justify-content: space-between;
}
}
}
.login-type {
width: 100%;
display: grid;
grid-template-columns: repeat(2, 1fr);
}
.login-others-type {
margin-top: 5vh;
width: 100%;
.el-divider {
.el-divider__text {
color: #A9A9A9;
font-size: 0.8rem;
}
}
.type-list {
display: flex;
justify-content: space-evenly;
i {
font-size: 1.2rem;
}
i:hover {
cursor: pointer;
filter: brightness(1.5);
}
}
}
.login-register {
margin-top: 10px;
display: flex;
justify-content: center;
span {
font-size: 0.9rem;
}
}
}
.phone-login, .email-login {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 1vh;
.login-form {
.el-form-item {
width: $form-item-width;
}
.code-item {
.el-form-item__content {
flex-wrap: nowrap;
gap: 10px;
}
}
}
}
}
}
</style>

View File

@@ -1,8 +1,5 @@
import { IRouteMetaConfig } from 'vue3-common/types'
export const blogMeta: IRouteMetaConfig = {
'/blog': {
path: '/blog',
redirect: '/blog/overview'
}
}

View File

@@ -30,7 +30,7 @@ export default defineConfig({
}
}
},
base: './',
base: '/blog/',
resolve: {
// 配置路径别名
alias: {
@@ -41,10 +41,10 @@ export default defineConfig({
port: 5174,
// 服务器代理 服务器没有部署Nginx的情况下使用
proxy: {
'/blog-service': {
'/blog-api': {
target: `http://${API_HOST}:8082`,
changeOrigin: true,
rewrite: (item) => item.replace(/^\/blog-service/, '')
rewrite: (item) => item.replace(/^\/blog-api/, '')
}
}
}