feat:移植工程

This commit is contained in:
2025-06-10 16:20:21 +08:00
commit 829df0b1d1
356 changed files with 43395 additions and 0 deletions

View File

@@ -0,0 +1,95 @@
<template>
<section class="layout-navbar">
<div class="left-container">
<!-- 控制菜单栏的显示 -->
<hamburger
id="hamburger-container"
:is-active="sidebar.opened"
class="hamburger-container"
@toggleClick="toggleSideBar" />
</div>
<h3 class="title">Sweet Hut</h3>
<!-- 右上角菜单栏 -->
<div class="right-container">
<!-- <div class="note-wrapper">-->
<!-- <el-tooltip effect="dark" content="警告信息" placement="bottom">-->
<!-- <el-badge is-dot class="item">-->
<!-- <svg-icon icon-class="danger" />-->
<!-- </el-badge>-->
<!-- </el-tooltip>-->
<!-- <el-tooltip effect="dark" content="待办事项" placement="bottom">-->
<!-- <el-badge is-dot class="item">-->
<!-- <svg-icon icon-class="todo" @click="viewTodoList"/>-->
<!-- </el-badge>-->
<!-- </el-tooltip>-->
<!-- </div>-->
<!-- 下拉框组件 -->
<el-dropdown>
<div class="avatar-wrapper">
<svg-icon name="admin-user" class="user-avatar" style="color: #0d9488;"/>
<span class="username">管理员</span>
<el-icon><caret-bottom /></el-icon>
</div>
<!-- 下拉框菜单 -->
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click="updatePasswordClick">
<span style="display:block;">修改密码</span>
</el-dropdown-item>
<el-dropdown-item @click="logoutClick">
<span style="display:block;">退出登录</span>
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</section>
</template>
<script setup lang="ts">
import { Hamburger, SvgIcon } from 'vue3-common'
import 'vue3-common/styles/Hamburger.css'
import { CaretBottom } from '@element-plus/icons-vue'
import { computed } from 'vue'
import { useRouter } from 'vue-router'
import { useAppStore } from '@/store/app'
const router = useRouter()
const appStore = useAppStore()
const sidebar = computed(() => appStore.sidebar)
/**
* 侧边栏控制
*/
const toggleSideBar = () => {
appStore.toggleSideBar()
}
const viewTodoList = () => {
console.log('待办事项')
}
/**
* 修改密码
*/
const updatePasswordClick = () => {
router.push('/profile')
}
/**
* 退出登录
*/
const logoutClick = () => {
// userStore.removeAll()
router.push('/login')
// 刷新页面 否则会出现切换用户 丢失动态路由的现象
// location.reload()
}
</script>

View File

@@ -0,0 +1,39 @@
<template>
<div :class="{'collapse':collapse}">
<!-- 跳转到首页 -->
<transition name="sidebarLogoFade">
<!-- 根据collapse控制Logo组件即是否显示标题 -->
<router-link v-if="collapse" key="collapse" class="logo" to="/admin/dashboard">
<!-- <img :src="logoInfo.logo" alt="logo">-->
</router-link>
<router-link v-else key="expand" class="logo" to="/admin/dashboard">
<img :src="getAssetsImageFile('logo/vue.png')"
alt="logo">
</router-link>
</transition>
</div>
</template>
<script setup lang="ts">
import { defineProps } from 'vue'
import { getAssetsImageFile } from '@/utils'
defineProps({
collapse: {
type: Boolean,
require: true
}
})
</script>
<style lang="scss" scoped>
// 动画样式
.sidebarLogoFade-enter-active {
transition: opacity 1.5s;
}
.sidebarLogoFade-enter,
.sidebarLogoFade-leave-to {
opacity: 0;
}
</style>

View File

@@ -0,0 +1,23 @@
<template>
<aside class="layout-sidebar">
<logo :collapse="isCollapse" class="layout-logo"/>
<menu-list :route="useRoute()"
:routers="adminRoutes"
:collapse="isCollapse"
class="layout-menu"/>
</aside>
</template>
<script lang="ts" setup>
import Logo from './Logo.vue'
import { MenuList } from 'vue3-common'
import adminRoutes from '@/router/admin'
import { computed } from 'vue'
import { useRoute } from 'vue-router'
import { useAppStore } from '@/store/app'
const appStore = useAppStore()
const isCollapse = computed(() => !appStore.sidebar.opened)
</script>

View File

@@ -0,0 +1,37 @@
<template>
<section :class="classObj" class="layout-admin">
<side-bar/>
<section class="layout-main">
<nav-bar/>
<section class="app-main">
<router-view />
</section>
</section>
</section>
</template>
<script setup lang="ts">
import SideBar from './components/SideBar/index.vue'
import NavBar from './components/NavBar/index.vue'
import { computed } from 'vue'
import { useAppStore } from '@/store/app'
const app = useAppStore()
const classObj = computed(() => {
// 控制侧边栏的显示状态
return {
hideSidebar: !app.sidebar.opened,
openSidebar: app.sidebar.opened,
withoutAnimation: app.sidebar.withoutAnimation,
mobile: app.device === 'mobile'
}
})
</script>
<style lang="scss">
@use '@/styles/admin.layout.module.scss';
</style>

View File

@@ -0,0 +1,20 @@
<template>
<footer class="blog-site-footer">
<span>© {{ new Date(startDay).getFullYear() }}-{{ new Date().getFullYear() }} Cxx</span>
<span>本网站已安全运行 {{ getBlogRunTotalTime(startDay) }}</span>
<span>博客全站共 {{ formatBlogWordCount(blogStore.blogStats.wordCount) }} </span>
</footer>
</template>
<script setup lang="ts">
import { useBlogStore } from '@/store/blog.ts'
import { formatBlogWordCount, getBlogRunTotalTime } from '@/utils/blog/blogUtil.ts'
const startDay = '2025-01-07'
const blogStore = useBlogStore()
</script>
<style scoped lang="scss">
@use "@/styles/blog/blog.footbar.module.scss";
</style>

View File

@@ -0,0 +1,30 @@
<template>
<el-menu router class="blog-menu-container">
<el-menu-item index="/blog/overview">
<i class="fa fa-fw fa-home" />
<span class="menu-title">首页</span>
</el-menu-item>
<el-menu-item index="/blog/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">
<i class="fa fa-fw fa-archive" />
<span class="menu-title">归档</span>
<span class="value">{{ blogStore.blogStats.blogCount }}</span>
</el-menu-item>
<el-menu-item>
<i class="fa fa-search fa-fw" />
<span class="menu-title">搜索</span>
</el-menu-item>
</el-menu>
</template>
<script setup lang="ts">
import { useBlogStore } from '@/store/blog.ts'
const blogStore = useBlogStore()
</script>

View File

@@ -0,0 +1,82 @@
<template>
<div class="blog-brief">
<div class="avatar">
<img :src="getAssetsImageFile('avatar.jpg')" alt="暂无图片"/>
<span>Cxx</span>
</div>
<div class="info">
<div class="item" @click="viewBlogArchiveClick">
<span class="value">{{ blogStore.blogStats.blogCount }}</span>
<span class="name">日志</span>
</div>
<div class="item" @click="viewBlogCategoryClick">
<span class="value">{{ blogStore.blogStats.categoryCount }}</span>
<span class="name">分类</span>
</div>
</div>
<div class="social">
<div class="item">
<i class="fa fab fa-github"></i>
<span class="name">Github</span>
</div>
<div class="item">
<i class="fa fab fa-weixin"></i>
<span class="name">微信</span>
</div>
</div>
<div class="latest-blog">
<div class="title">
<i class="fa fa-history fa-" />
<span>近期文章</span>
</div>
<div class="blog-list">
<span v-for="(item, index) in blogStore.latestBlogList"
:key="index" @click="viewBlogClick(index)">
{{ item.title }}
</span>
</div>
</div>
<!-- <div class="blog-time">-->
<!-- <blog-time />-->
<!-- </div>-->
</div>
</template>
<script setup lang="ts">
import BlogTime from '@/layout/blog/components/Tools/BlogTime.vue'
import { getAssetsImageFile } from '@/utils'
import { useBlogStore } from '@/store/blog.ts'
import { useRouter } from 'vue-router'
const blogStore = useBlogStore()
const router = useRouter()
/**
* 点击查看博客归档按钮
*/
const viewBlogArchiveClick = () => {
router.push('/blog/archive')
}
/**
* 点击查看博客分类按钮
*/
const viewBlogCategoryClick = () => {
router.push('/blog/category')
}
/**
* 点击查看博客按钮
* @param index 索引
*/
const viewBlogClick = async (index: number) => {
const blogId = blogStore.latestBlogList[index].id
await router.push({ name: 'BlogDetail', params: { id: blogId } })
}
</script>

View File

@@ -0,0 +1,73 @@
<template>
<div class="blog-summary">
<div v-if="blogSummaryInfo.isBlogDetail" class="blog-summary-tab">
<span @click="changeTabClick('Catalog', $event)" class="tab-active">
文章目录
</span>
<span @click="changeTabClick('Summary', $event)">
站点概览
</span>
</div>
<blog-site-brief v-if="blogSummaryInfo.activePanelName === 'Summary'
|| !blogSummaryInfo.isBlogDetail"/>
<MdCatalog v-if="blogSummaryInfo.activePanelName === 'Catalog'
&& blogSummaryInfo.isBlogDetail"
editorId="blog-id" :scrollElement="scrollElement"/>
<div class="scroll-container" @click="back2topClick()">
<i class="fa fa-arrow-up"></i>
<span>{{ blogStore.progressValue }}</span>
</div>
</div>
</template>
<script setup lang="ts">
import BlogSiteBrief from '@/layout/blog/components/Sidebar/BlogSiteBrief.vue'
import { MdCatalog } from 'md-editor-v3'
import { setActiveClass } from '@/utils'
import { useBlogStore } from '@/store/blog.ts'
import { reactive, watch } from 'vue'
import { useRouter } from 'vue-router'
const blogStore = useBlogStore()
const router = useRouter()
const scrollElement = document.documentElement
const blogSummaryInfo = reactive({
activePanelName: 'Catalog',
isBlogDetail: false
})
/**
* 监听路由变化 如果当前是查看博客 则需要显示博客目录
*/
watch(() => router.currentRoute.value.path, (path) => {
blogSummaryInfo.isBlogDetail = path.includes('/blog/detail')
if (blogSummaryInfo.isBlogDetail) {
blogSummaryInfo.activePanelName = 'Catalog'
}
}, {
immediate: true
})
/**
* 切换tab标签
* @param panelName panel名称
* @param e 点击事件
*/
const changeTabClick = (panelName: string, e: Event) => {
blogSummaryInfo.activePanelName = panelName
const element = e.target as Element
setActiveClass('blog-summary-tab', 'span', element, 'tab-active')
}
/**
* 点击返回顶部按钮
*/
const back2topClick = () => {
document.body.scrollTop = document.documentElement.scrollTop = 0
}
</script>

View File

@@ -0,0 +1,15 @@
<template>
<div class="title-container">
<span class="title">{{ blogTitleInfo.title }}</span>
<span class="sub-title">{{ blogTitleInfo.subTitle }}</span>
</div>
</template>
<script setup lang="ts">
import { reactive } from 'vue'
const blogTitleInfo = reactive({
title: 'NJCxx0822',
subTitle: '不要因为别人5%的负面评价而否定自己100%的努力。'
})
</script>

View File

@@ -0,0 +1,28 @@
<template>
<aside class="blog-aside">
<div class="top-container card-container">
<blog-title />
<blog-menu />
</div>
<blog-summary class="card-container"/>
</aside>
</template>
<script setup lang="ts">
import BlogTitle from '@/layout/blog/components/Sidebar/BlogTitle.vue'
import BlogMenu from '@/layout/blog/components/Sidebar/BlogMenu.vue'
import BlogSummary from '@/layout/blog/components/Sidebar/BlogSummary.vue'
import { useBlogStore } from '@/store/blog.ts'
import { onMounted } from 'vue'
const blogStore = useBlogStore()
onMounted(async () => {
await blogStore.queryBlogStats()
await blogStore.queryLatestBlog()
})
</script>
<style lang="scss">
@use "@/styles/blog/blog.sidebar.module.scss";
</style>

View File

@@ -0,0 +1,123 @@
<template>
<div class="cube-container">
<div class="cube">
<div class="front">
<span>欢迎光临</span>
</div>
<div class="back">
<span></span>
</div>
<div class="right">
<span>Cxx0822</span>
</div>
<div class="left">
<span>请多关照</span>
</div>
<div class="top">
<span></span>
</div>
<div class="bottom">
<span></span>
</div>
</div>
</div>
</template>
<style scoped lang="scss">
.cube-container {
width: 50px;
height: 60px;
margin: 0 auto;
position: fixed;
z-index: 2;
-webkit-perspective: 1000px;
perspective: 1000px;
right: 0;
bottom: 0;
transform: translate(-50%, -50%);
}
.cube {
width: 0; /* 大角度旋转 */
position: absolute;
-webkit-transform-style: preserve-3d;
transform-style: preserve-3d;
transform: rotateX(-15deg) rotateY(-20deg) translateZ(-100px);
-webkit-transform-origin: center center -100px;
transform-origin: center center -100px;
-webkit-animation: around 5s cubic-bezier(0.94, -0.6, 0.45, 1.31) infinite;
animation: around 5s cubic-bezier(0.94, -0.6, 0.45, 1.31) infinite;
div {
width: 50px;
height: 50px;
display: block;
margin: 0;
position: absolute;
span {
color: white;
font-size: 12px;
text-decoration: none;
text-align: center;
position: fixed;
top: 50%;
left: 45%;
transform: translate(-50%, -50%);
}
}
.front {
transform: rotateY(0deg) translateZ(25px);
background-color: rgba(0, 191, 255, 0.7);
border: 1px solid rgba(0, 191, 255, 0.7);
}
.back {
transform: rotateX(180deg) translateZ(25px);
background-color: rgba(124, 252, 0, 0.7);
border: 1px solid rgba(124, 252, 0, 0.7);
}
.left {
transform: rotateY(-90deg) translateZ(25px);
background-color: rgba(255, 215, 0, 0.7);
border: 1px solid rgba(255, 215, 0, 0.7);
}
.right {
transform: rotateY(90deg) translateZ(25px);
background-color: rgba(255, 69, 0, 0.7);
border: 1px solid rgba(255, 69, 0, 0.7);
}
.top {
transform: rotateX(90deg) translateZ(25px);
background-color: rgba(255, 0, 157, 0.7);
border: 1px solid rgba(255, 0, 157, 0.7);
}
.bottom {
transform: rotateX(-90deg) translateZ(25px);
background-color: rgba(184, 111, 220, 0.7);
border: 1px solid rgba(184, 111, 220, 0.7);
}
}
@-webkit-keyframes around {
100% {
transform: rotateX(-15deg) rotateY(-380deg) translateZ(-100px);
}
}
@keyframes around {
100% {
transform: rotateX(-15deg) rotateY(-380deg) translateZ(-100px);
}
}
</style>

View File

@@ -0,0 +1,29 @@
<template>
<canvas id="time-canvas"></canvas>
</template>
<script setup lang="ts">
import { onMounted, onUnmounted } from 'vue'
import { TimeCanvas } from '@/utils/blog/timeCanvas'
let timeInterval: any = null
onMounted(() => {
const canvas = document.getElementById('time-canvas') as HTMLCanvasElement
canvas.width = canvas.parentElement?.offsetWidth as number
canvas.height = canvas.parentElement?.offsetHeight as number
const context = canvas.getContext('2d') as CanvasRenderingContext2D
const timeCanvas = new TimeCanvas(context, canvas.width, canvas.height)
timeInterval = setInterval(() => {
// 清空整个Canvas重新绘制内容
context.clearRect(0, 0, context.canvas.width, context.canvas.height)
timeCanvas.drawDatetime()
}, 1000)
})
onUnmounted(() => {
clearInterval(timeInterval)
})
</script>

View File

@@ -0,0 +1,196 @@
<template>
<div class="blog-tools">
<a class="book-mark-link fa fa-bookmark"/>
<a href="https://github.com/Cxx0822" class="github-corner" aria-label="View source on GitHub" target="_blank">
<svg width="80" height="80" viewBox="0 0 250 250"
style="fill:#FD6C6C; color:#fff; position: absolute; top: 0; border: 0; right: 0;"
aria-hidden="true">
<path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path>
<path
d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6
C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3
C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2"
fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path>
<path
d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6
C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0
C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1
C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4
C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9
C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5
C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5
C139.8,137.7 141.6,141.9 141.8,141.8 Z"
fill="currentColor" class="octo-body"></path>
</svg>
</a>
<!-- <vue-particles id="particles" :options="particlesOptions"/>-->
<progress class="blog-progress" id="blog_progress" value="0" />
<blog-cube/>
</div>
</template>
<script setup lang="ts">
import BlogCube from '@/layout/blog/components/Tools/BlogCube.vue'
import { createHeartDiv } from '@/utils/blog/blogUtil.ts'
import { Ribbon } from '@/utils/blog/ribbon'
import { onMounted, onUnmounted } from 'vue'
import { useBlogStore } from '@/store/blog.ts'
const ribbon = new Ribbon()
const blogStore = useBlogStore()
/**
* 处理点击事件
* @param event 事件
*/
const handleClick = (event) => {
createHeartDiv(event.clientX, event.clientY)
if (event.target.classList.contains('ribbon-canvas')) {
ribbon.reDraw()
}
}
onMounted(() => {
document.addEventListener('scroll', () => {
const progressBar = document.getElementById('blog_progress') as HTMLProgressElement
progressBar.max = document.documentElement.scrollHeight - window.innerHeight
progressBar.value = window.scrollY
blogStore.progressValue = `${(progressBar.value / progressBar.max * 100).toFixed(0)}%`
})
document.addEventListener('click', handleClick)
})
onUnmounted(() => {
ribbon.clear()
document.removeEventListener('click', handleClick)
})
</script>
<style lang="scss">
@use "@/styles/blog/blog.variable.module.scss" as blog;
.blog-tools {
position: absolute;
.book-mark-link {
border-bottom: none;
display: block;
position: fixed;
top: -2px;
left: 20px;
color: #222;
font-size: 26px;
transition: .3s;
z-index: 2;
}
.github-corner {
border-bottom: none;
display: block;
position: fixed;
top: 0;
right: 0;
color: #222;
font-size: 26px;
transition: .3s;
z-index: 2;
}
.github-corner:hover .octo-arm {
animation: octocat-wave 560ms ease-in-out
}
@keyframes octocat-wave {
0%, 100% {
transform: rotate(0)
}
20%, 60% {
transform: rotate(-25deg)
}
40%, 80% {
transform: rotate(10deg)
}
}
@media (max-width: 500px) {
.github-corner:hover .octo-arm {
animation: none
}
.github-corner .octo-arm {
animation: octocat-wave 560ms ease-in-out
}
}
#particles {
position: absolute;
z-index: 0;
}
}
.heart {
position: fixed;
opacity: 1;
scale: 1;
width: 10px;
height: 10px;
background: red;
transform: rotate(45deg);
z-index: 2;
}
.heart:after, .heart:before {
content: "";
width: inherit;
height: inherit;
background: inherit;
border-radius: 50%;
position: absolute;
}
.heart:after {
top: -5px;
}
.heart:before {
left: -5px;
}
@keyframes move {
100% {
transform: translateY(-20px) rotate(45deg);
opacity: 0;
}
}
.blog-progress {
/* Positioning */
position: fixed;
left: 0;
top: 0;
width: 100%;
height: 3px;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
border: none;
background-color: transparent;
color: blog.$theme-color;
z-index: 3;
}
.blog-progress::-webkit-progress-bar {
background-color: transparent;
}
.blog-progress::-webkit-progress-value {
background-color: blog.$theme-color;
}
.blog-progress::-moz-progress-bar {
background-color: blog.$theme-color;
}
</style>

23
src/layout/blog/index.vue Normal file
View File

@@ -0,0 +1,23 @@
<template>
<section class="blog-container" id="blog-container">
<tools />
<sidebar />
<section>
<router-view/>
<footer-bar />
</section>
</section>
</template>
<script setup lang="ts">
import Tools from '@/layout/blog/components/Tools/index.vue'
import Sidebar from '@/layout/blog/components/Sidebar/index.vue'
import FooterBar from '@/layout/blog/components/FooterBar/index.vue'
</script>
<style lang="scss">
@use "@/styles/blog/blog.module.scss";
</style>

View File

@@ -0,0 +1,10 @@
<template>
<div class="title-container">
<span class="title">{{ appStore.homeTitle }}</span>
</div>
</template>
<script setup lang="ts">
import { useAppStore } from '@/store/app.ts'
const appStore = useAppStore()
</script>

View File

@@ -0,0 +1,9 @@
<template>
<menu-list :route="useRoute()" :routers="homeRoutes" :mode="'horizontal'" class="layout-menu"/>
</template>
<script setup lang="ts">
import { useRoute } from 'vue-router'
import { MenuList } from 'vue3-common'
import homeRoutes from '@/router/home'
</script>

View File

@@ -0,0 +1,43 @@
<template>
<aside class="layout-navbar">
<div class="title-container">
</div>
<home-menu />
<div class="right-container">
<home-query />
<el-dropdown placement="bottom">
<el-avatar :src="getAssetsImageFile('avatar.jpg')" ></el-avatar>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click="profileClick">个人中心</el-dropdown-item>
<el-dropdown-item @click="logoutClick">退出登录</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</aside>
</template>
<script setup lang="ts">
import HomeMenu from '@/layout/home/components/Navbar/HomeMenu.vue'
import HomeQuery from '@/components/Home/HomeQuery.vue'
import { getAssetsImageFile } from '@/utils'
import { useRouter } from 'vue-router'
import { commonElMessageBox } from 'vue3-common/utils/elUtil'
const router = useRouter()
const profileClick = () => {
router.push('/home/profile')
}
const logoutClick = () => {
commonElMessageBox('请确认是否退出登录?').then(() => {
router.push('/login')
})
}
</script>

17
src/layout/home/index.vue Normal file
View File

@@ -0,0 +1,17 @@
<template>
<section class="layout-home">
<navbar/>
<section class="layout-main">
<router-view />
</section>
</section>
</template>
<script lang="ts" setup>
import Navbar from '@/layout/home/components/Navbar/index.vue'
</script>
<style lang="scss">
@use "@/styles/home.layout.module.scss";
</style>

View File

@@ -0,0 +1,156 @@
<template>
<div class="tab-bar">
<router-link to="/mobile-home" @click="routerClick()" class="item">
<i class="fa-solid fa-house"></i>
<span>主页</span>
</router-link>
<router-link v-if="path.includes('bill')" to="/mobile-home/bill/data"
@click="routerClick()" class="item">
<i class="fa-solid fa-calendar-days"></i>
<span>日常</span>
</router-link>
<router-link v-if="path.includes('bill')" to="/mobile-home/bill/chart"
@click="routerClick()" class="item">
<i class="fa-solid fa-chart-pie"></i>
<span>统计</span>
</router-link>
<router-link v-if="path.includes('food')" to="/mobile-home/food/recipe"
@click="routerClick()" class="item">
<i class="fa-solid fa-utensils"></i>
<span>菜谱</span>
</router-link>
<router-link v-if="path.includes('food')" to="/mobile-home/food/record"
@click="routerClick()" class="item">
<i class="fa-solid fa-calendar-days"></i>
<span>记录</span>
</router-link>
<router-link v-if="path.includes('travel')" to="/mobile-home/travel/spot"
@click="routerClick()" class="item">
<i class="iconfont icon-jingdian"></i>
<span>景点</span>
</router-link>
<router-link v-if="path.includes('travel')" to="/mobile-home/travel/record"
@click="routerClick()" class="item">
<i class="fa-solid fa-calendar-days"></i>
<span>记录</span>
</router-link>
<router-link v-if="path.includes('plan')" to="/mobile-home/plan/calendar"
@click="routerClick()" class="item">
<i class="fa-solid fa-calendar-days"></i>
<span>日程</span>
</router-link>
<router-link v-if="path.includes('plan')" to="/mobile-home/plan/list"
@click="routerClick()" class="item">
<i class="fa-solid fa-list"></i>
<span>清单</span>
</router-link>
<router-link v-if="path.includes('anniversary')" to="/mobile-home/anniversary/list"
@click="routerClick()" class="item">
<i class="fa-regular fa-calendar-check"></i>
<span>纪念日</span>
</router-link>
<router-link v-if="path.includes('anniversary')" to="/mobile-home/anniversary/category"
@click="routerClick()" class="item">
<i class="fa-solid fa-table-cells"></i>
<span>分类</span>
</router-link>
<router-link v-if="path.includes('period')" to="/mobile-home/period/calendar"
@click="routerClick()" class="item">
<i class="fa-solid fa-calendar-days"></i>
<span>记录</span>
</router-link>
<router-link v-if="path.includes('period')" to="/mobile-home/period/setting"
@click="routerClick()" class="item">
<i class="fa-solid fa-gear"></i>
<span>设置</span>
</router-link>
<router-link v-if="path.includes('product')" to="/mobile-home/product/list"
@click="routerClick()" class="item">
<i class="fa-solid fa-list-ul"></i>
<span>清单</span>
</router-link>
<router-link v-if="path.includes('journal')" to="/mobile-home/journal/record"
@click="routerClick()" class="item">
<i class="fa-solid fa-calendar-days"></i>
<span>记录</span>
</router-link>
<router-link v-if="path.includes('journal')" to="/mobile-home/journal/timeline"
@click="routerClick()" class="item">
<i class="fa-solid fa-timeline"></i>
<span>时间轴</span>
</router-link>
<router-link v-if="path.includes('kpi')" to="/mobile-home/kpi/record"
@click="routerClick()" class="item">
<i class="fa-solid fa-calendar-days"></i>
<span>记录</span>
</router-link>
<router-link v-if="path.includes('asset')" to="/mobile-home/asset/list"
@click="routerClick()" class="item">
<i class="fa-solid fa-list-ul"></i>
<span>清单</span>
</router-link>
<router-link v-if="path.includes('ledger')" to="/mobile-home/ledger/list"
@click="routerClick()" class="item">
<i class="fa-solid fa-list-ul"></i>
<span>清单</span>
</router-link>
<router-link v-if="path.includes('server')" to="/mobile-home/server/monitor"
@click="routerClick()" class="item">
<i class="fa-solid fa-desktop"></i>
<span>监控</span>
</router-link>
<router-link v-if="path.includes('server')" to="/mobile-home/server/file"
@click="routerClick()" class="item">
<i class="fa-solid fa-folder"></i>
<span>文件</span>
</router-link>
<router-link v-if="path.includes('blog')" to="/mobile-home/blog/overview"
@click="routerClick()" class="item">
<i class="fa-solid fa-book-open"></i>
<span>列表</span>
</router-link>
<router-link v-if="path.includes('blog')" to="/mobile-home/blog/category"
@click="routerClick()" class="item">
<i class="fa-solid fa-table-cells"></i>
<span>分类</span>
</router-link>
<router-link v-if="path.includes('blog')" to="/mobile-home/blog/archive"
@click="routerClick()" class="item">
<i class="fa-solid fa-box-archive"></i>
<span>归档</span>
</router-link>
<router-link to="/mobile-home/profile/index"
@click="routerClick()" class="item">
<i class="fa-solid fa-user"></i>
<span>我的</span>
</router-link>
</div>
</template>
<script setup lang="ts">
import { useRouter } from 'vue-router'
import { useAppStore } from '@/store/app.ts'
import { computed } from 'vue'
const router = useRouter()
const appStore = useAppStore()
const path = computed(() => router.currentRoute.value.path)
const routerClick = () => {
appStore.isShowMobileBack = false
}
</script>

View File

@@ -0,0 +1,72 @@
<template>
<section class="layout-mobile-home">
<div class="title-bar">
<div class="left-container">
<el-button v-if="appStore.isShowMobileBack"
type="info" :icon="Back" size="small"
circle color="#FFFFFF"
@click="goBackClick" />
<div v-else class="logo-container">
<img :src="getAssetsImageFile('logo/logo.png')" alt="暂无图片" class="logo"/>
<span>Sweet Hut</span>
</div>
</div>
<span class="title-container">{{ title }}</span>
<div class="right-container">
<svg-icon v-if="appStore.isWebSocket"
name="home-websocket" style="color: #FFFFFF;"></svg-icon>
<el-button type="info" :icon="Setting" size="small"
circle color="#FFFFFF" @click="openSettingClick"/>
<el-drawer
v-model="appStore.isShowSetting"
title="系统设置" :show-close="false"
size="50%" class="setting-drawer">
<mobile-setting />
</el-drawer>
</div>
</div>
<router-view />
<tab-bar />
</section>
</template>
<script lang="ts" setup>
import { SvgIcon } from 'vue3-common'
import MobileSetting from '@/components/Mobile/MobileSetting.vue'
import { Back } from '@element-plus/icons-vue'
import { Setting } from '@element-plus/icons-vue'
import TabBar from '@/layout/mobile-home/TabBar.vue'
import { useRouter } from 'vue-router'
import { useAppStore } from '@/store/app.ts'
import { computed } from 'vue'
import { getAssetsImageFile } from '@/utils'
const appStore = useAppStore()
const router = useRouter()
const title = computed(() => router.currentRoute.value.meta.title)
const goBackClick = () => {
appStore.isShowMobileBack = false
history.back()
}
const openSettingClick = () => {
appStore.isShowSetting = true
}
</script>
<style lang="scss">
@use "@/styles/mobile.home.layout.module.scss";
.setting-drawer {
.el-drawer__header {
margin-bottom: 0;
}
}
</style>