feat: 添加Pinia全局状态管理并重构路由逻辑
添加Pinia作为全局状态管理工具,重构路由参数传递方式 移除URL中的动态参数,改为通过Pinia store传递数据 优化导航菜单样式和交互效果,修复路由激活状态问题 新增全局状态管理示例文档,说明基础使用方法 调整页面布局和样式,提升用户体验
This commit is contained in:
64
package-lock.json
generated
64
package-lock.json
generated
@@ -12,6 +12,7 @@
|
||||
"antd": "^5.27.3",
|
||||
"axios": "^1.12.2",
|
||||
"element-plus": "^2.11.2",
|
||||
"pinia": "^3.0.3",
|
||||
"vue": "^3.5.18",
|
||||
"vue-router": "^4.5.1"
|
||||
},
|
||||
@@ -2681,7 +2682,6 @@
|
||||
"version": "2.5.0",
|
||||
"resolved": "https://registry.npmmirror.com/birpc/-/birpc-2.5.0.tgz",
|
||||
"integrity": "sha512-VSWO/W6nNQdyP520F1mhf+Lc2f8pjGQOtoHHm7Ze8Go1kX7akpVIrtTa0fn+HB0QJEDVacl6aO08YE0PgXfdnQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
@@ -2938,7 +2938,6 @@
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmmirror.com/copy-anything/-/copy-anything-3.0.5.tgz",
|
||||
"integrity": "sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"is-what": "^4.1.8"
|
||||
@@ -3693,7 +3692,6 @@
|
||||
"version": "5.5.3",
|
||||
"resolved": "https://registry.npmmirror.com/hookable/-/hookable-5.5.3.tgz",
|
||||
"integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/html-encoding-sniffer": {
|
||||
@@ -3917,7 +3915,6 @@
|
||||
"version": "4.1.16",
|
||||
"resolved": "https://registry.npmmirror.com/is-what/-/is-what-4.1.16.tgz",
|
||||
"integrity": "sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12.13"
|
||||
@@ -4227,7 +4224,6 @@
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/mitt/-/mitt-3.0.1.tgz",
|
||||
"integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/mlly": {
|
||||
@@ -4486,7 +4482,6 @@
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/perfect-debounce/-/perfect-debounce-1.0.0.tgz",
|
||||
"integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/picocolors": {
|
||||
@@ -4508,6 +4503,60 @@
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/pinia": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmmirror.com/pinia/-/pinia-3.0.3.tgz",
|
||||
"integrity": "sha512-ttXO/InUULUXkMHpTdp9Fj4hLpD/2AoJdmAbAeW2yu1iy1k+pkFekQXw5VpC0/5p51IOR/jDaDRfRWRnMMsGOA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vue/devtools-api": "^7.7.2"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/posva"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": ">=4.4.4",
|
||||
"vue": "^2.7.0 || ^3.5.11"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"typescript": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/pinia/node_modules/@vue/devtools-api": {
|
||||
"version": "7.7.7",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-7.7.7.tgz",
|
||||
"integrity": "sha512-lwOnNBH2e7x1fIIbVT7yF5D+YWhqELm55/4ZKf45R9T8r9dE2AIOy8HKjfqzGsoTHFbWbr337O4E0A0QADnjBg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vue/devtools-kit": "^7.7.7"
|
||||
}
|
||||
},
|
||||
"node_modules/pinia/node_modules/@vue/devtools-kit": {
|
||||
"version": "7.7.7",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/devtools-kit/-/devtools-kit-7.7.7.tgz",
|
||||
"integrity": "sha512-wgoZtxcTta65cnZ1Q6MbAfePVFxfM+gq0saaeytoph7nEa7yMXoi6sCPy4ufO111B9msnw0VOWjPEFCXuAKRHA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vue/devtools-shared": "^7.7.7",
|
||||
"birpc": "^2.3.0",
|
||||
"hookable": "^5.5.3",
|
||||
"mitt": "^3.0.1",
|
||||
"perfect-debounce": "^1.0.0",
|
||||
"speakingurl": "^14.0.1",
|
||||
"superjson": "^2.2.2"
|
||||
}
|
||||
},
|
||||
"node_modules/pinia/node_modules/@vue/devtools-shared": {
|
||||
"version": "7.7.7",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/devtools-shared/-/devtools-shared-7.7.7.tgz",
|
||||
"integrity": "sha512-+udSj47aRl5aKb0memBvcUG9koarqnxNM5yjuREvqwK6T3ap4mn3Zqqc17QrBFTqSMjr3HK1cvStEZpMDpfdyw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"rfdc": "^1.4.1"
|
||||
}
|
||||
},
|
||||
"node_modules/pkg-types": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmmirror.com/pkg-types/-/pkg-types-2.3.0.tgz",
|
||||
@@ -5275,7 +5324,6 @@
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmmirror.com/rfdc/-/rfdc-1.4.1.tgz",
|
||||
"integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/rollup": {
|
||||
@@ -5472,7 +5520,6 @@
|
||||
"version": "14.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/speakingurl/-/speakingurl-14.0.1.tgz",
|
||||
"integrity": "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==",
|
||||
"dev": true,
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
@@ -5645,7 +5692,6 @@
|
||||
"version": "2.2.2",
|
||||
"resolved": "https://registry.npmmirror.com/superjson/-/superjson-2.2.2.tgz",
|
||||
"integrity": "sha512-5JRxVqC8I8NuOUjzBbvVJAKNM8qoVuH0O77h4WInc/qC2q5IreqKxYwgkga3PfA22OayK2ikceb/B26dztPl+Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"copy-anything": "^3.0.2"
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
"antd": "^5.27.3",
|
||||
"axios": "^1.12.2",
|
||||
"element-plus": "^2.11.2",
|
||||
"pinia": "^3.0.3",
|
||||
"vue": "^3.5.18",
|
||||
"vue-router": "^4.5.1"
|
||||
},
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
</div>
|
||||
<div class="cont2">
|
||||
<el-menu :default-active="activeIndex" class="el-menu-vertical-demo" @select="handleSelect">
|
||||
<el-menu-item index="/:type">
|
||||
<el-menu-item index="/home">
|
||||
<el-icon>
|
||||
</el-icon>
|
||||
<span>首页</span>
|
||||
@@ -144,9 +144,18 @@ onUnmounted(() => {
|
||||
display: block;
|
||||
background-color: rgba(0, 0, 0,0 ); /* 白色半透明背景 */
|
||||
}
|
||||
.cont2 .el-menu-vertical-demo ul li:hover{
|
||||
background-color: rgba(220, 53, 69, 0.9); /* 红色半透明背景 */
|
||||
color: white;
|
||||
.cont2 .el-menu-vertical-demo .el-menu-item:nth-child(3){
|
||||
border-radius: 0 0 10px 10px;
|
||||
}
|
||||
.cont2 .el-menu-vertical-demo .el-menu-item:hover{
|
||||
background-color: rgba(64, 158, 255, 0.9);
|
||||
}
|
||||
.cont2 .el-menu-vertical-demo .el-menu-item.is-active:hover{
|
||||
color: black; /* 蓝色半透明背景 */
|
||||
}
|
||||
|
||||
.cont2 .el-menu-vertical-demo .el-menu-item.is-active{
|
||||
color: var(--nav-is-active);
|
||||
}
|
||||
|
||||
/* 分类列表样式 */
|
||||
|
||||
@@ -9,19 +9,19 @@
|
||||
<el-col :span="14" justify="center">
|
||||
<div class="grid-content ep-bg-purple-dark">
|
||||
<el-menu :default-active="activeIndex" class="el-menu-demo" :collapse="false" @select="handleSelect">
|
||||
<el-menu-item index="/home">
|
||||
<el-menu-item index="home">
|
||||
首页
|
||||
</el-menu-item>
|
||||
<el-menu-item index="/article-list">
|
||||
<el-menu-item index="article-list">
|
||||
目录
|
||||
</el-menu-item>
|
||||
<el-menu-item index="/nonsense">
|
||||
<el-menu-item index="nonsense">
|
||||
疯言疯语
|
||||
</el-menu-item>
|
||||
<el-menu-item index="/about">
|
||||
<el-menu-item index="about">
|
||||
关于
|
||||
</el-menu-item>
|
||||
<el-menu-item index="/message">
|
||||
<el-menu-item index="message">
|
||||
留言板
|
||||
</el-menu-item>
|
||||
</el-menu>
|
||||
@@ -81,6 +81,9 @@ import LeftModule from '@/components/LeftModule.vue';
|
||||
// 路由相关
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
// 全局状态管理
|
||||
import { useGlobalStore } from '@/store/globalStore'
|
||||
const globalStore = useGlobalStore()
|
||||
|
||||
// 响应式状态
|
||||
const classhero = ref(false);
|
||||
@@ -89,9 +92,10 @@ const isScrollingleftmodlue = ref(false);
|
||||
const elrowtop = ref('transparent');
|
||||
const classnonsenset = ref(false);
|
||||
const windowwidth = ref(true);
|
||||
const activeIndex = ref('/home');
|
||||
const localhome= '/home';
|
||||
const activeIndex = ref('home');
|
||||
const localhome= 'home';
|
||||
|
||||
let rpsliturl = route.path.split('/')[1];
|
||||
|
||||
// 搜索相关状态
|
||||
const isSearchBoxOpen = ref(false);
|
||||
@@ -99,7 +103,7 @@ const searchKeyword = ref('');
|
||||
let searchCloseTimer: number | undefined;
|
||||
|
||||
// 打字机效果相关
|
||||
const fullHeroText = '测试打字机效果';
|
||||
let fullHeroText = '测试打字机效果';
|
||||
const heroText = ref('');
|
||||
let heroIndex = 0;
|
||||
let heroTimer: number | undefined;
|
||||
@@ -125,7 +129,7 @@ const startTypewriter = () => {
|
||||
* 菜单选择跳转
|
||||
*/
|
||||
const handleSelect = (key: string) => {
|
||||
router.push({ path: key });
|
||||
router.push({ path: '/' + key });
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -181,16 +185,19 @@ const performSearch = () => {
|
||||
*/
|
||||
const updatePageState = (url: string) => {
|
||||
classhero.value = url !== localhome;
|
||||
classnonsenset.value = url === '/nonsense';
|
||||
classnonsenset.value = url === 'nonsense';
|
||||
};
|
||||
|
||||
/**
|
||||
* 设置当前激活的菜单项
|
||||
*/
|
||||
const setActiveIndex = (path: string) => {
|
||||
activeIndex.value = path;
|
||||
console.log('设置激活索引:', path);
|
||||
activeIndex.value =path;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 处理窗口大小变化
|
||||
*/
|
||||
@@ -198,7 +205,7 @@ const handleResize = () => {
|
||||
windowwidth.value = window.innerWidth > 768;
|
||||
|
||||
// 根据屏幕大小调整内容区可见性
|
||||
if (route.path === localhome) {
|
||||
if (rpsliturl === localhome) {
|
||||
isconts.value = window.innerWidth <= 768 ? true : false;
|
||||
}
|
||||
};
|
||||
@@ -221,7 +228,7 @@ const handleScroll = () => {
|
||||
}
|
||||
|
||||
// 首页内容区滚动动画
|
||||
if (route.path === localhome) {
|
||||
if (rpsliturl === localhome) {
|
||||
isconts.value = window.scrollY > 200;
|
||||
isScrollingleftmodlue.value = window.scrollY > 600;
|
||||
}
|
||||
@@ -231,16 +238,30 @@ const handleScroll = () => {
|
||||
* 监听路由变化
|
||||
*/
|
||||
watch(() => route.path, (newPath) => {
|
||||
updatePageState(newPath);
|
||||
setActiveIndex(newPath);
|
||||
rpsliturl = route.path.split('/')[1];
|
||||
updatePageState(rpsliturl);
|
||||
setActiveIndex(rpsliturl);
|
||||
|
||||
const localname = route.path.split('/')[2];
|
||||
let articledata;
|
||||
// 优先使用attributeId参数(新接口)
|
||||
if (localname==='aericletype') {
|
||||
articledata = globalStore.getValue('attribute')
|
||||
}
|
||||
// 搜索标题
|
||||
if (localname==='aericletitle') {
|
||||
articledata = globalStore.getValue('title')
|
||||
}
|
||||
// hero 标题
|
||||
if (articledata) {
|
||||
fullHeroText = articledata.name
|
||||
}
|
||||
// 跳转后回到顶部
|
||||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||||
|
||||
// 首页内容区滚动动画仅大屏下生效
|
||||
if (newPath === localhome) {
|
||||
if (newPath.split('/')[1] === localhome) {
|
||||
isconts.value = window.innerWidth <= 768 ? true : false;
|
||||
// 首页时启动打字机
|
||||
// 首页时启动打字机效果
|
||||
startTypewriter();
|
||||
} else {
|
||||
isconts.value = true;
|
||||
@@ -257,7 +278,6 @@ onMounted(() => {
|
||||
|
||||
|
||||
handleResize();
|
||||
|
||||
// 添加事件监听器
|
||||
window.addEventListener('resize', handleResize);
|
||||
window.addEventListener('scroll', handleScroll);
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
import { createApp } from 'vue'
|
||||
import { createPinia } from 'pinia'
|
||||
import App from './App.vue'
|
||||
import Router from './router/Router'
|
||||
import ElementPlus from 'element-plus'
|
||||
import 'element-plus/dist/index.css'
|
||||
import './styles/MainLayout.css'
|
||||
|
||||
// 创建Pinia实例
|
||||
const pinia = createPinia()
|
||||
|
||||
const app = createApp(App)
|
||||
app.use(Router)
|
||||
app.use(ElementPlus)
|
||||
app.use(pinia) // 添加Pinia支持
|
||||
app.mount('#app')
|
||||
@@ -32,11 +32,11 @@ const routes = [
|
||||
meta: { title: '首页' },
|
||||
children: [
|
||||
{
|
||||
path: 'aericletype/:type',
|
||||
path: 'aericletype',
|
||||
name: 'homeByType'
|
||||
},
|
||||
{
|
||||
path: 'aericletitle/:title',
|
||||
path: 'aericletitle',
|
||||
name: 'homeByTitle'
|
||||
}
|
||||
]
|
||||
|
||||
124
src/store/globalStore.js
Normal file
124
src/store/globalStore.js
Normal file
@@ -0,0 +1,124 @@
|
||||
import { defineStore } from 'pinia'
|
||||
|
||||
/**
|
||||
* 全局状态管理store
|
||||
* 提供全局的传值和获取值的功能
|
||||
* 任何页面都可以调用和获取其中的值
|
||||
*/
|
||||
export const useGlobalStore = defineStore('global', {
|
||||
// 状态定义
|
||||
state: () => ({
|
||||
// 全局数据对象,存储所有需要共享的数据
|
||||
globalData: {},
|
||||
// 可以在这里定义特定的状态属性,便于类型提示和直接使用
|
||||
user: null,
|
||||
loading: false,
|
||||
notifications: []
|
||||
}),
|
||||
|
||||
// 计算属性 - 用于获取和转换状态
|
||||
getters: {
|
||||
/**
|
||||
* 检查全局数据中是否存在指定的键
|
||||
* @param {string} key - 要检查的键名
|
||||
* @returns {boolean} - 如果存在返回true,否则返回false
|
||||
*/
|
||||
hasValue: (state) => (key) => {
|
||||
return Object.prototype.hasOwnProperty.call(state.globalData, key)
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取指定键的值
|
||||
* @param {string} key - 要获取的键名
|
||||
* @returns {any} - 对应的值,如果不存在则返回undefined
|
||||
*/
|
||||
getValue: (state) => (key) => {
|
||||
return state.globalData[key]
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取所有全局数据
|
||||
* @returns {Object} - 所有全局数据
|
||||
*/
|
||||
getAllData: (state) => {
|
||||
return { ...state.globalData }
|
||||
}
|
||||
},
|
||||
|
||||
// 操作方法 - 用于修改状态
|
||||
actions: {
|
||||
/**
|
||||
* 设置全局数据
|
||||
* @param {string} key - 键名
|
||||
* @param {any} value - 要存储的值
|
||||
*/
|
||||
setValue(key, value) {
|
||||
this.globalData[key] = value
|
||||
},
|
||||
|
||||
/**
|
||||
* 批量设置多个键值对
|
||||
* @param {Object} data - 包含多个键值对的对象
|
||||
*/
|
||||
setMultipleValues(data) {
|
||||
if (typeof data === 'object' && data !== null) {
|
||||
Object.assign(this.globalData, data)
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 删除指定的全局数据
|
||||
* @param {string} key - 要删除的键名
|
||||
*/
|
||||
removeValue(key) {
|
||||
if (Object.prototype.hasOwnProperty.call(this.globalData, key)) {
|
||||
delete this.globalData[key]
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 清空所有全局数据
|
||||
*/
|
||||
clearAll() {
|
||||
this.globalData = {}
|
||||
},
|
||||
|
||||
/**
|
||||
* 设置用户信息
|
||||
* @param {Object} userInfo - 用户信息对象
|
||||
*/
|
||||
setUser(userInfo) {
|
||||
this.user = userInfo
|
||||
},
|
||||
|
||||
/**
|
||||
* 设置加载状态
|
||||
* @param {boolean} status - 加载状态
|
||||
*/
|
||||
setLoading(status) {
|
||||
this.loading = status
|
||||
},
|
||||
|
||||
/**
|
||||
* 添加通知
|
||||
* @param {Object} notification - 通知对象
|
||||
*/
|
||||
addNotification(notification) {
|
||||
this.notifications.push({
|
||||
id: Date.now(),
|
||||
...notification
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* 移除通知
|
||||
* @param {number} id - 通知ID
|
||||
*/
|
||||
removeNotification(id) {
|
||||
const index = this.notifications.findIndex(notification => notification.id === id)
|
||||
if (index !== -1) {
|
||||
this.notifications.splice(index, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
208
src/store/useExample.md
Normal file
208
src/store/useExample.md
Normal file
@@ -0,0 +1,208 @@
|
||||
# Pinia 全局状态管理使用示例
|
||||
|
||||
## 1. 基础使用方法
|
||||
|
||||
### 在任意组件中使用全局状态
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<div>
|
||||
<h2>全局状态管理示例</h2>
|
||||
|
||||
<!-- 显示全局数据 -->
|
||||
<div v-if="globalStore.hasValue('exampleData')">
|
||||
<p>从全局store获取的数据: {{ globalStore.getValue('exampleData') }}</p>
|
||||
</div>
|
||||
<div v-else>
|
||||
<p>全局数据尚未设置</p>
|
||||
</div>
|
||||
|
||||
<!-- 设置全局数据 -->
|
||||
<el-input v-model="inputValue" placeholder="输入要存储的全局数据" />
|
||||
<el-button @click="setGlobalData">设置全局数据</el-button>
|
||||
<el-button @click="checkGlobalData">检查是否存在</el-button>
|
||||
<el-button @click="clearGlobalData" type="danger">清空全局数据</el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { useGlobalStore } from '@/store/globalStore'
|
||||
|
||||
// 获取全局store实例
|
||||
const globalStore = useGlobalStore()
|
||||
const inputValue = ref('')
|
||||
|
||||
// 设置全局数据
|
||||
const setGlobalData = () => {
|
||||
globalStore.setValue('exampleData', inputValue.value)
|
||||
ElMessage.success('数据已保存到全局状态')
|
||||
}
|
||||
|
||||
// 检查数据是否存在
|
||||
const checkGlobalData = () => {
|
||||
if (globalStore.hasValue('exampleData')) {
|
||||
ElMessage.info(`数据存在: ${globalStore.getValue('exampleData')}`)
|
||||
} else {
|
||||
ElMessage.warning('数据不存在')
|
||||
}
|
||||
}
|
||||
|
||||
// 清空全局数据
|
||||
const clearGlobalData = () => {
|
||||
globalStore.removeValue('exampleData')
|
||||
ElMessage.success('数据已从全局状态中移除')
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
## 2. 页面间数据传递示例
|
||||
|
||||
### 页面A - 设置数据
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
import { useGlobalStore } from '@/store/globalStore'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
const globalStore = useGlobalStore()
|
||||
const router = useRouter()
|
||||
|
||||
const navigateToPageB = () => {
|
||||
// 存储页面间需要传递的数据
|
||||
globalStore.setValue('sharedData', {
|
||||
id: 123,
|
||||
name: '测试数据',
|
||||
timestamp: Date.now()
|
||||
})
|
||||
|
||||
// 跳转到页面B
|
||||
router.push('/page-b')
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
### 页面B - 获取数据
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
import { onMounted } from 'vue'
|
||||
import { useGlobalStore } from '@/store/globalStore'
|
||||
|
||||
const globalStore = useGlobalStore()
|
||||
|
||||
onMounted(() => {
|
||||
// 检查并获取从页面A传递过来的数据
|
||||
if (globalStore.hasValue('sharedData')) {
|
||||
const data = globalStore.getValue('sharedData')
|
||||
console.log('从页面A接收的数据:', data)
|
||||
// 使用接收到的数据进行操作
|
||||
}
|
||||
})
|
||||
</script>
|
||||
```
|
||||
|
||||
## 3. 批量操作示例
|
||||
|
||||
```javascript
|
||||
import { useGlobalStore } from '@/store/globalStore'
|
||||
|
||||
const globalStore = useGlobalStore()
|
||||
|
||||
// 批量设置多个键值对
|
||||
globalStore.setMultipleValues({
|
||||
appConfig: {
|
||||
theme: 'dark',
|
||||
language: 'zh-CN'
|
||||
},
|
||||
userPreferences: {
|
||||
notifications: true,
|
||||
fontSize: 16
|
||||
},
|
||||
lastUpdated: new Date().toISOString()
|
||||
})
|
||||
|
||||
// 获取所有全局数据
|
||||
const allData = globalStore.getAllData()
|
||||
console.log('所有全局数据:', allData)
|
||||
|
||||
// 清空所有数据
|
||||
globalStore.clearAll()
|
||||
```
|
||||
|
||||
## 4. 用户状态管理示例
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<div>
|
||||
<div v-if="globalStore.user">
|
||||
<h3>欢迎回来,{{ globalStore.user.username }}</h3>
|
||||
<el-button @click="logout">登出</el-button>
|
||||
</div>
|
||||
<div v-else>
|
||||
<h3>请登录</h3>
|
||||
<!-- 登录表单 -->
|
||||
</div>
|
||||
|
||||
<!-- 加载状态指示器 -->
|
||||
<el-loading v-loading="globalStore.loading" :text="'加载中...'">
|
||||
<!-- 内容区域 -->
|
||||
</el-loading>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useGlobalStore } from '@/store/globalStore'
|
||||
|
||||
const globalStore = useGlobalStore()
|
||||
|
||||
const login = async (credentials) => {
|
||||
try {
|
||||
// 设置加载状态
|
||||
globalStore.setLoading(true)
|
||||
|
||||
// 模拟登录请求
|
||||
const response = await authService.login(credentials)
|
||||
|
||||
// 保存用户信息到全局状态
|
||||
globalStore.setUser(response.user)
|
||||
|
||||
// 添加登录成功通知
|
||||
globalStore.addNotification({
|
||||
type: 'success',
|
||||
message: '登录成功!'
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('登录失败:', error)
|
||||
} finally {
|
||||
// 无论成功失败都清除加载状态
|
||||
globalStore.setLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
const logout = () => {
|
||||
// 清除用户信息
|
||||
globalStore.setUser(null)
|
||||
|
||||
// 添加登出通知
|
||||
globalStore.addNotification({
|
||||
type: 'info',
|
||||
message: '您已成功登出'
|
||||
})
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
## 5. 注意事项
|
||||
|
||||
1. **数据持久化**:默认情况下,Pinia状态只在内存中存在,页面刷新后会丢失。如果需要持久化,可以考虑:
|
||||
- 使用localStorage/sessionStorage结合Pinia插件
|
||||
- 使用Cookie存储关键信息
|
||||
|
||||
2. **性能考虑**:避免在全局状态中存储过大的数据,这可能会影响应用性能
|
||||
|
||||
3. **类型安全**:如果使用TypeScript,可以为store添加类型定义
|
||||
|
||||
4. **模块化**:随着应用复杂度增加,可以考虑将不同功能的数据分开存储到多个store中
|
||||
|
||||
5. **调试**:Pinia提供了良好的开发工具支持,可以通过Vue Devtools进行调试
|
||||
@@ -6,7 +6,7 @@
|
||||
/* 内容区圆角 */
|
||||
|
||||
/* 首页 hero 区域高度和间距 */
|
||||
--hero-height: 600px;
|
||||
--hero-height: 768px;
|
||||
/* hero 默认高度 */
|
||||
--hero-height-small: 150px;
|
||||
/* hero 收缩后高度 */
|
||||
@@ -48,9 +48,9 @@
|
||||
/* 分页区背景色 */
|
||||
|
||||
/* 导航栏样式 */
|
||||
--nav-padding: 20px 40px;
|
||||
--nav-padding: 15px 35px;
|
||||
/* 导航栏默认内边距 */
|
||||
--nav-padding-small: 15px 40px;
|
||||
--nav-padding-small: 10px 25px;
|
||||
/* 导航栏收缩后内边距 */
|
||||
--nav-bg: rgba(145, 196, 238, 0.85);
|
||||
/* 导航栏背景色 */
|
||||
@@ -60,7 +60,8 @@
|
||||
/* 导航栏隐藏时背景 */
|
||||
--nav-shadow: 0 5px 20px rgba(0, 0, 0, 0.1);
|
||||
/* 导航栏阴影 */
|
||||
|
||||
--nav-is-active: rgba(255, 0, 255, 0.5);
|
||||
/* 导航栏激活项字体颜色 */
|
||||
/* 字体颜色和菜单样式 */
|
||||
--font-color-title: #AF7AC5;
|
||||
/* 标题字体颜色 */
|
||||
@@ -115,7 +116,7 @@ p {
|
||||
width: 100%;
|
||||
padding: var(--nav-padding);
|
||||
z-index: 1000;
|
||||
transition: all 0.4s ease;
|
||||
transition: all 1s ease;
|
||||
font-size: inherit;
|
||||
/* display: flex; */
|
||||
justify-content: space-between;
|
||||
@@ -179,12 +180,21 @@ p {
|
||||
.el-menu-demo .el-menu-item {
|
||||
margin-left: 10px;
|
||||
font-size: inherit;
|
||||
color: rgba(255, 255, 255, .95);
|
||||
}
|
||||
.el-menu-demo .el-menu-item.is-active{
|
||||
color: var(--nav-is-active);
|
||||
}
|
||||
|
||||
/* 菜单项悬停和激活状态 */
|
||||
.el-menu-item:hover,
|
||||
.el-menu-item.is-active {
|
||||
.el-menu-demo .el-menu-item:hover{
|
||||
background-color: transparent;
|
||||
color: rgba(255, 200, 255, 0.5);
|
||||
opacity: 0.85;
|
||||
}
|
||||
.el-menu-demo .el-menu-item.is-active {
|
||||
background-color: transparent;
|
||||
color: var(--nav-is-active);
|
||||
opacity: 0.85;
|
||||
}
|
||||
|
||||
/* 去除菜单项悬停和激活时的背景色 */
|
||||
|
||||
@@ -17,30 +17,30 @@
|
||||
<div class="about-skills">
|
||||
<h3>前端技术栈</h3>
|
||||
<div class="skills-list">
|
||||
<el-tag type="primary">HTML5</el-tag>
|
||||
<el-tag type="primary">CSS3</el-tag>
|
||||
<el-tag type="primary">JavaScript</el-tag>
|
||||
<el-tag type="primary">TypeScript</el-tag>
|
||||
<el-tag type="primary">Vue.js</el-tag>
|
||||
<el-tag type="primary">React</el-tag>
|
||||
<el-tag type="primary">Node.js</el-tag>
|
||||
<el-tag type="primary">Webpack</el-tag>
|
||||
<el-tag type="primary">Git</el-tag>
|
||||
<a href="https://developer.mozilla.org/zh-CN/docs/Web/HTML" target="_blank" class="skill-link"><el-tag type="primary">HTML5</el-tag></a>
|
||||
<a href="https://developer.mozilla.org/zh-CN/docs/Web/CSS" target="_blank" class="skill-link"><el-tag type="primary">CSS3</el-tag></a>
|
||||
<a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript" target="_blank" class="skill-link"><el-tag type="primary">JavaScript</el-tag></a>
|
||||
<a href="https://www.typescriptlang.org/" target="_blank" class="skill-link"><el-tag type="primary">TypeScript</el-tag></a>
|
||||
<a href="https://vuejs.org/" target="_blank" class="skill-link"><el-tag type="primary">Vue.js</el-tag></a>
|
||||
<a href="https://react.dev/" target="_blank" class="skill-link"><el-tag type="primary">React</el-tag></a>
|
||||
<a href="https://nodejs.org/" target="_blank" class="skill-link"><el-tag type="primary">Node.js</el-tag></a>
|
||||
<a href="https://webpack.js.org/" target="_blank" class="skill-link"><el-tag type="primary">Webpack</el-tag></a>
|
||||
<a href="https://git-scm.com/" target="_blank" class="skill-link"><el-tag type="primary">Git</el-tag></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="about-skills">
|
||||
<h3>后端技术栈</h3>
|
||||
<div class="skills-list">
|
||||
<el-tag type="success">Spring Boot 2.6.13</el-tag>
|
||||
<el-tag type="success">Spring Security</el-tag>
|
||||
<el-tag type="success">Spring Data JPA</el-tag>
|
||||
<el-tag type="success">MyBatis</el-tag>
|
||||
<el-tag type="success">MySQL</el-tag>
|
||||
<el-tag type="success">Lombok</el-tag>
|
||||
<el-tag type="success">EHCache</el-tag>
|
||||
<el-tag type="success">Maven</el-tag>
|
||||
<el-tag type="success">Java 8</el-tag>
|
||||
<a href="https://spring.io/projects/spring-boot" target="_blank" class="skill-link"><el-tag type="success">Spring Boot 2.6.13</el-tag></a>
|
||||
<a href="https://spring.io/projects/spring-security" target="_blank" class="skill-link"><el-tag type="success">Spring Security</el-tag></a>
|
||||
<a href="https://spring.io/projects/spring-data-jpa" target="_blank" class="skill-link"><el-tag type="success">Spring Data JPA</el-tag></a>
|
||||
<a href="https://mybatis.org/mybatis-3/" target="_blank" class="skill-link"><el-tag type="success">MyBatis</el-tag></a>
|
||||
<a href="https://www.mysql.com/" target="_blank" class="skill-link"><el-tag type="success">MySQL</el-tag></a>
|
||||
<a href="https://projectlombok.org/" target="_blank" class="skill-link"><el-tag type="success">Lombok</el-tag></a>
|
||||
<a href="https://www.ehcache.org/" target="_blank" class="skill-link"><el-tag type="success">EHCache</el-tag></a>
|
||||
<a href="https://maven.apache.org/" target="_blank" class="skill-link"><el-tag type="success">Maven</el-tag></a>
|
||||
<a href="https://www.oracle.com/java/technologies/java8.html" target="_blank" class="skill-link"><el-tag type="success">Java 8</el-tag></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -139,6 +139,20 @@ const goToMessageBoard = () => {
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
/* 技能链接样式 */
|
||||
.skill-link {
|
||||
text-decoration: none;
|
||||
transition: transform 0.2s ease;
|
||||
}
|
||||
|
||||
.skill-link:hover {
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.skill-link:hover .el-tag {
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
/* 兴趣爱好 */
|
||||
.about-hobbies {
|
||||
margin-bottom: 32px;
|
||||
|
||||
@@ -26,9 +26,9 @@
|
||||
<span class="badge badge-primary">共 {{ categoryGroup.attributes.length }} 篇</span>
|
||||
<ul class="wp-block-list">
|
||||
<li v-for="category in categoryGroup.attributes" :key="category.typeid">
|
||||
<a class="btn" @click="handleCategoryClick(category.categoryid)"><kbd>{{ category.attributename
|
||||
<a class="btn" @click="handleCategoryClick(category)"><kbd>{{ category.attributename
|
||||
}}</kbd></a>
|
||||
— —({{ category.articles.length }})
|
||||
— —({{ category.articles.length }})
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -50,7 +50,9 @@ import { ref, onMounted } from 'vue'
|
||||
// 以下代码仅作示例,实际需根据 '@/services' 模块的真实导出调整
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { categoryService, categoryAttributeService, articleService } from '@/services'
|
||||
import { useGlobalStore } from '@/store/globalStore'
|
||||
|
||||
const globalStore = useGlobalStore()
|
||||
const router = useRouter()
|
||||
|
||||
// 响应式状态
|
||||
@@ -141,12 +143,14 @@ const getCategoryAttributes = async (processedCategories: any[]) => {
|
||||
* 注意:现在实际上使用的是属性ID而不是分类ID
|
||||
* @param {string | number} attributeId - 属性ID
|
||||
*/
|
||||
const handleCategoryClick = (attributeId: string | number) => {
|
||||
const handleCategoryClick = (attribute: any) => {
|
||||
globalStore.setValue('attribute', {
|
||||
id: attribute.typeid || attribute.categoryid,
|
||||
name: attribute.attributename || attribute.typename || '未命名属性',
|
||||
})
|
||||
console.log(attribute)
|
||||
router.push({
|
||||
path: '/home/aericletype',
|
||||
query: {
|
||||
attributeId: String(attributeId)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -192,7 +196,6 @@ onMounted(() => {
|
||||
.category-group ul li {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 5px 0;
|
||||
}
|
||||
|
||||
.category-group ul li .btn::before {
|
||||
@@ -201,7 +204,6 @@ onMounted(() => {
|
||||
|
||||
.category-group ul li .btn:hover {
|
||||
background-color: rgb(245, 247, 250, 0.7);
|
||||
border-bottom: #4b64f0 2px solid;
|
||||
}
|
||||
|
||||
.alert:not(.alert-secondary) {
|
||||
|
||||
@@ -12,16 +12,15 @@
|
||||
<div
|
||||
class="article-card"
|
||||
v-for="item in datas"
|
||||
:key="item.id || (item.title + item.publishedAt)"
|
||||
:key="item.articleid"
|
||||
@click="handleArticleClick(item.articleid)"
|
||||
>
|
||||
<h2 class="article-title">{{ item.title }}</h2>
|
||||
<div class="article-meta">
|
||||
<span class="article-author">{{ item.author || '清疯不颠' }}</span>
|
||||
<span class="article-date">{{ formatDateDisplay(item.publishedAt || item.createTime) }}</span>
|
||||
<!-- <span class="article-date">{{ formatDateDisplay(item.publishedAt || item.createTime) }}</span> -->
|
||||
<span v-if="item.categoryName" class="article-category">{{ item.categoryName }}</span>
|
||||
<span v-if="item.views" class="article-views">{{ item.views }} 阅读</span>
|
||||
<span v-if="item.commentCount" class="article-comments">{{ item.commentCount }} 评论</span>
|
||||
<span v-if="item.viewCount" class="article-views">{{ item.views }} 阅读</span>
|
||||
<!-- <span v-if="item.content" class="article-comments">{{ item.commentCount }} 评论</span> -->
|
||||
</div>
|
||||
<div v-if="item.mg" class="article-tag">mg</div>
|
||||
<p class="article-preview">{{ formatContentPreview(item.content, 150) }}</p>
|
||||
@@ -42,7 +41,9 @@ import { articleService } from '@/services'
|
||||
import { formatDate, formatRelativeTime } from '@/utils/dateUtils'
|
||||
import { formatContentPreview } from '@/utils/stringUtils'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { useGlobalStore } from '@/store/globalStore'
|
||||
|
||||
const globalStore = useGlobalStore()
|
||||
// 路由实例
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
@@ -58,38 +59,33 @@ const loading = ref(false)
|
||||
* 获取文章列表
|
||||
*/
|
||||
const fetchArticles = async () => {
|
||||
try {
|
||||
try {
|
||||
loading.value = true
|
||||
let res = {} // 使用let而不是const
|
||||
|
||||
// 检查URL参数
|
||||
const query = route.query
|
||||
const params = route.params
|
||||
const localhome = route.path.split('/')[2];
|
||||
|
||||
// 优先使用attributeId参数(新接口)
|
||||
if (query.attributeId) {
|
||||
console.log('根据属性ID获取文章列表')
|
||||
res = await articleService.getArticlesByAttribute(query.attributeId)
|
||||
}
|
||||
// 兼容旧的type参数
|
||||
else if (params.type) {
|
||||
console.log('根据类型ID获取文章列表(兼容模式)')
|
||||
res = await articleService.getArticlesByCategory(params.type)
|
||||
if (localhome==='aericletype') {
|
||||
const data = globalStore.getValue('attribute')
|
||||
res = await articleService.getArticlesByAttributeId(data.id)
|
||||
}
|
||||
// 搜索标题
|
||||
else if (params.title || query.title) {
|
||||
const title = params.title || query.title
|
||||
console.log('根据标题获取文章列表')
|
||||
res = await articleService.getArticlesByTitle(title)
|
||||
else if (localhome==='aericletitle') {
|
||||
const data = globalStore.getValue('title')
|
||||
res = await articleService.getArticlesByTitle(data.title)
|
||||
}
|
||||
// 获取所有文章
|
||||
else {
|
||||
res = await articleService.getAllArticles()
|
||||
console.log('获取所有文章列表')
|
||||
res = await articleService.getAllArticles()
|
||||
}
|
||||
|
||||
// 修复:使用正确的属性名data而不是date
|
||||
console.log('获取文章列表成功:', res.data)
|
||||
datas.value = res.data || []
|
||||
console.log('获取文章列表成功:', datas.value)
|
||||
console.log('文章数据已赋值:', datas.value)
|
||||
} catch (error) {
|
||||
console.error('获取文章列表失败:', error)
|
||||
ElMessage.error('获取文章列表失败,请稍后重试')
|
||||
|
||||
Reference in New Issue
Block a user