8/15二次更新

hyx_brand
hyx 4 weeks ago
parent 9e8ce09450
commit 19862dc69d

@ -1,21 +1,32 @@
<template> <template>
<el-container class="app-container"> <div v-if="sessionInitialized">
<el-header> <el-container class="app-container">
<HeaderBar /> <el-header>
</el-header> <HeaderBar />
<el-main> </el-header>
<router-view /> <el-main>
</el-main> <router-view />
<el-footer> </el-main>
<div class="footer-content"> <el-footer>
<p>图书馆管理系统 &copy; {{ new Date().getFullYear() }}</p> <div class="footer-content">
</div> <p>图书馆管理系统 &copy; {{ new Date().getFullYear() }}</p>
</el-footer> </div>
</el-container> </el-footer>
</el-container>
</div>
<div v-else class="loading-container">
<el-loading-spinner />
<p>正在初始化...</p>
</div>
</template> </template>
<script setup> <script setup>
import { computed } from 'vue'
import { useStore } from 'vuex'
import HeaderBar from './components/HeaderBar.vue' import HeaderBar from './components/HeaderBar.vue'
const store = useStore()
const sessionInitialized = computed(() => store.state.sessionInitialized)
</script> </script>
<style> <style>
@ -42,4 +53,19 @@ import HeaderBar from './components/HeaderBar.vue'
max-width: 1200px; max-width: 1200px;
margin: 0 auto; margin: 0 auto;
} }
.loading-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100vh;
background-color: #f5f7fa;
}
.loading-container p {
margin-top: 20px;
color: #606266;
font-size: 16px;
}
</style> </style>

@ -22,10 +22,10 @@
<el-menu-item index="weekly">本周热租榜</el-menu-item> <el-menu-item index="weekly">本周热租榜</el-menu-item>
<el-menu-item index="monthly">本月热租榜</el-menu-item> <el-menu-item index="monthly">本月热租榜</el-menu-item>
</el-sub-menu> </el-sub-menu>
<el-menu-item index="bookManagement" v-if="isAdmin"> <el-menu-item index="bookManagement" v-if="user && user.admin">
图书管理 图书管理
</el-menu-item> </el-menu-item>
<el-menu-item index="allBorrowRecords" v-if="isAdmin"> <el-menu-item index="allBorrowRecords" v-if="user && user.admin">
用户借阅记录 用户借阅记录
</el-menu-item> </el-menu-item>
</el-menu> </el-menu>
@ -72,15 +72,22 @@ import { useStore } from 'vuex'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import { Reading, ArrowDown } from '@element-plus/icons-vue' import { Reading, ArrowDown } from '@element-plus/icons-vue'
import { Wallet } from '@element-plus/icons-vue' import { Wallet } from '@element-plus/icons-vue'
import { computed, onMounted } from 'vue' import { computed, onMounted, watch } from 'vue'
const store = useStore() const store = useStore()
const router = useRouter() const router = useRouter()
onMounted(async () => { onMounted(async () => {
// //
if (store.getters.isAuthenticated) { if (store.state.sessionInitialized && store.getters.isAuthenticated) {
await fetchUserInfo()
}
})
//
watch(() => store.state.sessionInitialized, async (newVal) => {
if (newVal && store.getters.isAuthenticated) {
await fetchUserInfo() await fetchUserInfo()
} }
}) })
@ -88,7 +95,7 @@ onMounted(async () => {
// //
async function fetchUserInfo() { async function fetchUserInfo() {
try { try {
// VIP // VIP
await store.dispatch('fetchBalanceAndVip') await store.dispatch('fetchBalanceAndVip')
} catch (error) { } catch (error) {
console.error('获取用户信息失败:', error) console.error('获取用户信息失败:', error)
@ -103,8 +110,6 @@ async function fetchUserInfo() {
const vip = computed(() => store.state.vipLevel) const vip = computed(() => store.state.vipLevel)
const balance = computed(() => store.state.balance) const balance = computed(() => store.state.balance)
const user = computed(() => store.state.user) const user = computed(() => store.state.user)
const isAdmin = computed(() => store.getters.isAdmin)

@ -17,6 +17,9 @@ app.use(ElementPlus)
app.use(store) app.use(store)
app.use(router) app.use(router)
// 初始化会话
store.dispatch('initSession')
app.mount('#app') app.mount('#app')
window.store = store window.store = store

@ -102,6 +102,11 @@ const router = createRouter({
// 路由守卫 // 路由守卫
router.beforeEach((to, from, next) => { router.beforeEach((to, from, next) => {
// 等待会话初始化完成
if (!store.state.sessionInitialized) {
return next()
}
const isAuthenticated = store.getters.isAuthenticated const isAuthenticated = store.getters.isAuthenticated
const isAdmin = store.getters.isAdmin const isAdmin = store.getters.isAdmin

@ -6,7 +6,8 @@ export default createStore({
user: JSON.parse(sessionStorage.getItem('user')) || null, user: JSON.parse(sessionStorage.getItem('user')) || null,
balance: JSON.parse(sessionStorage.getItem('balance')) || 0, balance: JSON.parse(sessionStorage.getItem('balance')) || 0,
vipLevel: JSON.parse(sessionStorage.getItem('vipLevel')) || 0, vipLevel: JSON.parse(sessionStorage.getItem('vipLevel')) || 0,
borrowedBooks: JSON.parse(sessionStorage.getItem('borrowedBooks')) || [] borrowedBooks: JSON.parse(sessionStorage.getItem('borrowedBooks')) || [],
sessionInitialized: false
}, },
getters: { getters: {
isAuthenticated: state => !!state.user, isAuthenticated: state => !!state.user,
@ -14,6 +15,12 @@ export default createStore({
}, },
mutations: { mutations: {
setUser(state, user) { setUser(state, user) {
const admin = user.admin === 1;
const userData = {
...user,
admin
};
state.user = user state.user = user
sessionStorage.setItem('user', JSON.stringify(user)) sessionStorage.setItem('user', JSON.stringify(user))
}, },
@ -53,18 +60,31 @@ export default createStore({
}) })
if (userData && userData.username) { if (userData && userData.username) {
const admin = userData.admin === 1;
commit('setUser', { commit('setUser', {
username: userData.username, username: userData.username,
pic: userData.pic || '', pic: userData.pic || '',
admin: userData.admin || false admin
}) })
// 获取关联信息 // 获取关联信息
await dispatch('fetchBalanceAndVip') try {
await dispatch('fetchBorrowedBooks') await dispatch('fetchBalanceAndVip')
} catch (balanceError) {
console.warn('获取余额信息失败:', balanceError)
}
try {
await dispatch('fetchBorrowedBooks')
} catch (booksError) {
console.warn('获取借阅书籍失败:', booksError)
}
} }
} catch (error) { } catch (error) {
console.log('未检测到有效会话') console.log('未检测到有效会话,用户需要重新登录')
// 清除可能存在的无效数据
commit('clearUser')
} finally { } finally {
commit('setSessionInitialized', true) commit('setSessionInitialized', true)
} }
@ -100,12 +120,12 @@ export default createStore({
async fetchUser({ commit, dispatch }) { async fetchUser({ commit, dispatch }) {
try { try {
const userData = await service.get('/user/getinfo') const userData = await service.get('/user/getinfo')
const admin = userData.admin === 1;
// 用户信息接口直接返回用户对象 // 用户信息接口直接返回用户对象
commit('setUser', { commit('setUser', {
username: userData.username || '', username: userData.username || '',
pic: userData.pic || '', pic: userData.pic || '',
admin: userData.admin || false admin
}) })
// 获取关联信息 // 获取关联信息
@ -128,40 +148,37 @@ export default createStore({
} }
}, },
// 获取余额和VIP等级 // 获取余额和VIP等级 - 符合接口文档1.5
async fetchBalanceAndVip({ commit }) {
async fetchBalanceAndVip({ commit }) { try {
try { const response = await service.post('/user/findmoney')
const response = await service.post('/user/findmoney') const resData = response.data || {}
const resData = response.data || {}
if (resData.code === 200) {
// 正确解析响应数据 const message = resData.message || ''
if (resData.code === 200) { // 使用正则表达式解析余额和VIP等级
const message = resData.message || '' const balanceMatch = message.match(/余额为:(\d+\.?\d*)元/)
const balanceMatch = message.match(/余额为:(\d+\.\d+)元/) const vipMatch = message.match(/当前VIP等级为(\d+)/)
const vipMatch = message.match(/当前VIP等级为(\d+)/)
if (balanceMatch && vipMatch) {
if (balanceMatch && vipMatch) { const balance = parseFloat(balanceMatch[1])
commit('setBalanceAndVip', { const vip = parseInt(vipMatch[1])
balance: parseFloat(balanceMatch[1]), commit('setBalanceAndVip', { balance, vip })
vip: parseInt(vipMatch[1]) } else {
}) console.warn('无法解析余额或VIP信息:', message)
} else { commit('setBalanceAndVip', { balance: 0, vip: 0 })
// 尝试从其他字段获取 }
const balance = parseFloat(resData.balance) || 0 } else {
const vip = parseInt(resData.vipLevel) || 0 throw new Error(resData.message || '获取余额信息失败')
commit('setBalanceAndVip', { balance, vip }) }
return resData
} catch (error) {
console.error('获取余额失败:', error)
// 不抛出错误,避免影响其他功能
return { code: 500, message: '获取余额失败' }
} }
} else { },
throw new Error(resData.message || '获取余额信息失败')
}
return resData
} catch (error) {
console.error('获取余额失败:', error)
throw error
}
},
// 账户充值 // 账户充值
@ -214,16 +231,22 @@ async borrowBook({ dispatch }, { title }) {
return response.data return response.data
}, },
// 归还书籍 // 归还书籍 - 符合接口文档3.2
async returnBook({ dispatch }, { title }) { async returnBook({ dispatch }, { title }) {
const response = await service.post('/borrow/returnbook', try {
`title=${encodeURIComponent(title)}`, const response = await service.post('/borrow/returnbook',
{ headers: { 'Content-Type': 'application/x-www-form-urlencoded' } } `title=${encodeURIComponent(title)}`,
) { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }
)
// 还书后刷新用户信息
await dispatch('fetchUser') // 还书成功后刷新借阅书籍列表
return response.data await dispatch('fetchBorrowedBooks')
return response.data
} catch (error) {
console.error('还书失败:', error)
throw error
}
}, },
// 查询全部书籍 - 符合接口文档 // 查询全部书籍 - 符合接口文档
@ -265,26 +288,26 @@ async returnBook({ dispatch }, { title }) {
} }
}, },
// 根据书名查单本书 - 符合接口文档 // 根据书名查单本书 - 符合接口文档2.3
async fetchBookByTitle(_, payload) { async fetchBookByTitle(_, payload) {
const { title } = payload; const { title } = payload;
try { try {
const response = await service.get('/api/selectone', { const response = await service.get('/api/selectone', {
params: { title } params: { title }
}) })
// 根据接口文档处理响应 // 根据接口文档处理响应
if (response.data && response.data.code === 200) { if (response.data && response.data.code === 200) {
return { data: response.data.data } return { data: response.data.data }
} else { } else {
throw new Error(response.data?.message || '获取书籍信息失败') throw new Error(response.data?.message || '获取书籍信息失败')
} }
} catch (error) { } catch (error) {
console.error('API请求失败:', error) console.error('API请求失败:', error)
throw error throw error
} }
}, },
// 新增书籍 // 新增书籍
async addBook(_, bookData) { async addBook(_, bookData) {

@ -29,7 +29,10 @@ service.interceptors.response.use(
// 处理业务错误 (code !== 200) // 处理业务错误 (code !== 200)
if (res && typeof res === 'object' && res.code !== undefined && res.code !== 200) { if (res && typeof res === 'object' && res.code !== undefined && res.code !== 200) {
ElMessage.error(res.message || '请求失败') // 检查是否为静默请求
if (!response.config.silent) {
ElMessage.error(res.message || '请求失败')
}
return Promise.reject(new Error(res.message || 'Error')) return Promise.reject(new Error(res.message || 'Error'))
} }
@ -41,18 +44,27 @@ service.interceptors.response.use(
if (error.response) { if (error.response) {
switch (error.response.status) { switch (error.response.status) {
case 401: case 401:
store.dispatch('logout') // 只有在非静默请求时才显示错误信息
router.push('/login') if (!error.config?.silent) {
ElMessage.error('请先登录') store.dispatch('logout')
router.push('/login')
ElMessage.error('请先登录')
}
break break
case 403: case 403:
ElMessage.error('没有操作权限') if (!error.config?.silent) {
ElMessage.error('没有操作权限')
}
break break
default: default:
ElMessage.error(error.response.data?.message || '请求失败') if (!error.config?.silent) {
ElMessage.error(error.response.data?.message || '请求失败')
}
} }
} else { } else {
ElMessage.error('网络错误,请检查连接') if (!error.config?.silent) {
ElMessage.error('网络错误,请检查连接')
}
} }
return Promise.reject(error) return Promise.reject(error)
} }

@ -69,20 +69,49 @@ const rules = ref({
const loginForm = ref(null) const loginForm = ref(null)
const loading = ref(false) const loading = ref(false)
// const login = async () => {
// try {
// await loginForm.value.validate()
// loading.value = true
// await store.dispatch('login', form.value)
// ElMessage.success('')
// //
// await store.dispatch('fetchUser')
// // localStorage
// const userInfo = store.state.user
// localStorage.setItem('userInfo', JSON.stringify(userInfo))
// //
// router.push('/')
// } catch (error) {
// console.error(':', error)
// ElMessage.error(error.message || '')
// } finally {
// loading.value = false
// }
// }
const login = async () => { const login = async () => {
try { try {
//
await loginForm.value.validate() await loginForm.value.validate()
loading.value = true loading.value = true
await store.dispatch('login', form.value) // action
ElMessage.success('登录成功') const response = await store.dispatch('login', form.value)
//
const userInfo = response.user
// // admin
await store.dispatch('fetchUser') store.commit('setUser', {
// localStorage ...userInfo,
const userInfo = store.state.user admin: userInfo.admin === 1 // 1/0 true/false
localStorage.setItem('userInfo', JSON.stringify(userInfo)) })
//
ElMessage.success('登录成功')
router.push('/') router.push('/')
} catch (error) { } catch (error) {
console.error('登录失败:', error) console.error('登录失败:', error)

@ -73,11 +73,8 @@ const fetchBook = async () => {
// //
const response = await store.dispatch('fetchBookByTitle', { title }) const response = await store.dispatch('fetchBookByTitle', { title })
// API // 2.3
if (response.data && response.data.code === 200) { if (response.data) {
book.value = response.data.data
} else if (response.data) {
// code
book.value = response.data book.value = response.data
} else { } else {
throw new Error('书籍不存在') throw new Error('书籍不存在')

@ -70,24 +70,25 @@
} }
const handleReturn = async (book) => { const handleReturn = async (book) => {
try { try {
const response = await store.dispatch('returnBook', { title: book.title }) const response = await store.dispatch('returnBook', { title: book.title })
// if (response.code === 200) {
const returnTime = response.data?.return_time || new Date().toISOString() ElMessage.success(`${book.title}》归还成功`)
// //
store.commit('removeBorrowedBook', book.title) store.commit('removeBorrowedBook', book.title)
ElMessage.success(`${book.title}》归还成功`) //
await fetchBorrowedBooks()
// } else {
await store.dispatch('fetchBorrowedBooks') ElMessage.error(response.message || '归还失败')
} catch (error) { }
console.error('归还失败:', error) } catch (error) {
ElMessage.error(error.message || '归还失败') console.error('归还失败:', error)
ElMessage.error(error.message || '归还失败')
}
} }
}
</script> </script>
<style scoped> <style scoped>

Loading…
Cancel
Save