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", "version": "0.0.1",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
"build": "vue-tsc -b && vite build", "build": "vite build",
"preview": "vite preview" "preview": "vite preview"
}, },
"dependencies": { "dependencies": {

View File

@@ -1,19 +1,19 @@
import { cloudService } from './index' 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 qs from 'qs'
import { IPageRecord } from '@/types' import { IPageRecord } from '@/types'
export const queryBlogApi = (currentPage: number, pageSize: number): Promise<IPageRecord<IBlog>> => export const queryBlogApi = (currentPage: number, pageSize: number): Promise<IPageRecord<IBlog>> =>
cloudService({ cloudService({
url: '/blog-service/blog/page', url: '/blog-api/blog/page',
method: 'get', method: 'get',
params: { currentPage, pageSize } params: { currentPage, pageSize }
}) })
export const queryBlogByConditionApi = (query: IQueryBlog): Promise<IBlog[]> => export const queryBlogByConditionApi = (query: IQueryBlog): Promise<IBlog[]> =>
cloudService({ cloudService({
url: '/blog-service/blog/condition', url: '/blog-api/blog/condition',
method: 'get', method: 'get',
params: query, params: query,
paramsSerializer: (params) => { paramsSerializer: (params) => {
@@ -23,85 +23,37 @@ export const queryBlogByConditionApi = (query: IQueryBlog): Promise<IBlog[]> =>
export const queryBlogByIdApi = (id: number): Promise<IBlog> => export const queryBlogByIdApi = (id: number): Promise<IBlog> =>
cloudService({ cloudService({
url: `/blog-service/blog/content/${id}`, url: `/blog-api/blog/content/${id}`,
method: 'get' method: 'get'
}) })
export const queryBlogCategoryApi = (): Promise<IBlogCategory[]> => export const queryBlogCategoryApi = (): Promise<IBlogCategory[]> =>
cloudService({ cloudService({
url: '/blog-service/blog/category', url: '/blog-api/blog/category',
method: 'get' method: 'get'
}) })
export const queryBlogStatsApi = (): Promise<IBlogStats> => export const queryBlogStatsApi = (): Promise<IBlogStats> =>
cloudService({ cloudService({
url: '/blog-service/blog/stats', url: '/blog-api/blog/stats',
method: 'get' method: 'get'
}) })
export const queryLatestBlogApi = (): Promise<IBlog[]> => export const queryLatestBlogApi = (): Promise<IBlog[]> =>
cloudService({ cloudService({
url: '/blog-service/blog/latest', url: '/blog-api/blog/latest',
method: 'get' method: 'get'
}) })
export const queryBlogCommentApi = (blogId: number): Promise<IBlogComment[]> => export const queryBlogCommentApi = (blogId: number): Promise<IBlogComment[]> =>
cloudService({ cloudService({
url: `/blog-service/blog/comment/${blogId}`, url: `/blog-api/blog/comment/${blogId}`,
method: 'get' method: 'get'
}) })
export const addBlogCommentApi = (blogComment: IBlogComment): Promise<boolean> => export const addBlogCommentApi = (blogComment: IBlogComment): Promise<boolean> =>
cloudService({ cloudService({
url: '/blog-service/blog/comment', url: '/blog-api/blog/comment',
method: 'put', method: 'put',
data: blogComment 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 viewBlogClick = async (index: number) => {
const blogId = blogStore.blogList[index].id const blogId = blogStore.blogList[index].id
await router.push({ name: 'BlogDetail', params: { id: blogId } }) await router.push({ name: 'DetailId', params: { id: blogId } })
} }
</script> </script>

View File

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

View File

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

View File

@@ -48,7 +48,7 @@ onMounted(async () => {
*/ */
const readBlogClick = async (index: number) => { const readBlogClick = async (index: number) => {
const blogId = blogStore.blogList[index].id 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> <template>
<el-menu router class="blog-menu-container"> <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" /> <i class="fa fa-fw fa-home" />
<span class="menu-title">首页</span> <span class="menu-title">首页</span>
</el-menu-item> </el-menu-item>
<el-menu-item index="/blog/category"> <el-menu-item index="/category">
<i class="fa fa-fw fa-th" /> <i class="fa fa-fw fa-th" />
<span class="menu-title">分类</span> <span class="menu-title">分类</span>
<span class="value">{{ blogStore.blogStats.categoryCount }}</span> <span class="value">{{ blogStore.blogStats.categoryCount }}</span>
</el-menu-item> </el-menu-item>
<el-menu-item index="/blog/archive"> <el-menu-item index="/archive">
<i class="fa fa-fw fa-archive" /> <i class="fa fa-fw fa-archive" />
<span class="menu-title">归档</span> <span class="menu-title">归档</span>
<span class="value">{{ blogStore.blogStats.blogCount }}</span> <span class="value">{{ blogStore.blogStats.blogCount }}</span>

View File

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

View File

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

View File

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

View File

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

View File

@@ -4,12 +4,11 @@ import {
IBlogLatest, IBlogNestedComment, IBlogStats, INewBlog, IQueryBlog IBlogLatest, IBlogNestedComment, IBlogStats, INewBlog, IQueryBlog
} from '@/types/blog' } from '@/types/blog'
import { import {
addBlogApi, addBlogCommentApi, deleteBlogApi, queryAdminBlogApi, queryAdminBlogByIdApi, queryAdminBlogCategoryApi, addBlogCommentApi,
queryBlogApi, queryBlogByConditionApi, queryBlogByIdApi, queryBlogApi, queryBlogByConditionApi, queryBlogByIdApi,
queryBlogCategoryApi, queryBlogCommentApi, queryBlogStatsApi, queryLatestBlogApi, updateBlogApi queryBlogCategoryApi, queryBlogCommentApi, queryBlogStatsApi, queryLatestBlogApi
} from '@/apis/blog.ts' } from '@/apis/blog.ts'
import { IHandleApi } from 'vue3-common/types' import { IHandleApi } from 'vue3-common/types'
import { ElMessage } from 'element-plus'
import { ImagePathEnum } from '@/types' import { ImagePathEnum } from '@/types'
import { flattenToTwoLevel } from '@/utils/blog/blogUtil.ts' import { flattenToTwoLevel } from '@/utils/blog/blogUtil.ts'
@@ -73,18 +72,6 @@ export const useBlogStore = defineStore('blog', {
this.blogList = result.records this.blogList = result.records
this.pageInfo.totalSize = result.total 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) { async queryBlogByCondition(query: IQueryBlog) {
this.blogList = await queryBlogByConditionApi(query) this.blogList = await queryBlogByConditionApi(query)
}, },
@@ -93,9 +80,6 @@ export const useBlogStore = defineStore('blog', {
await this.queryBlogComment(this.currentBlogId) await this.queryBlogComment(this.currentBlogId)
this.blogNestedCommentList = flattenToTwoLevel(this.blogCommentList) this.blogNestedCommentList = flattenToTwoLevel(this.blogCommentList)
}, },
async queryAdminBlogById(id: number) {
this.currentBlog = await queryAdminBlogByIdApi(id)
},
async queryBlogComment(blogId: number) { async queryBlogComment(blogId: number) {
this.blogCommentList = await queryBlogCommentApi(blogId) this.blogCommentList = await queryBlogCommentApi(blogId)
}, },
@@ -106,50 +90,12 @@ export const useBlogStore = defineStore('blog', {
async queryBlogCategory() { async queryBlogCategory() {
this.blogCategoryList = await queryBlogCategoryApi() this.blogCategoryList = await queryBlogCategoryApi()
}, },
async queryAdminBlogCategory() {
this.blogCategoryList = await queryAdminBlogCategoryApi()
},
async queryBlogStats() { async queryBlogStats() {
this.blogStats = await queryBlogStatsApi() this.blogStats = await queryBlogStatsApi()
}, },
async queryLatestBlog() { async queryLatestBlog() {
this.latestBlogList = await queryLatestBlogApi() 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 { getEmptyBlogComment(): IBlogComment {
return { return {
blogId: 0, 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' import { IRouteMetaConfig } from 'vue3-common/types'
export const blogMeta: IRouteMetaConfig = { export const blogMeta: IRouteMetaConfig = {
'/blog': {
path: '/blog',
redirect: '/blog/overview'
}
} }

View File

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