完成图书列表

dev
lynn 6 months ago
parent 61faf51f9f
commit 15115d6a85

@ -42,6 +42,11 @@ let apiApp = {
supplyDemandSave: '/api/mobile/supply-demand/save',
supplyDemandList: '/api/mobile/supply-demand/index',
supplyDemandDetail: '/api/mobile/supply-demand/detail',
// 图书
bookIndex: '/api/mobile/book/index',
bookDetail: '/api/mobile/book/detail',
bookOther: '/api/mobile/book/other',
}
// 此处第二个参数vm就是我们在页面使用的this你可以通过vm获取vuex等操作
@ -95,6 +100,11 @@ const install = (Vue, vm) => {
let supplyDemandList = (params = {}) => vm.$u.get(apiApp.supplyDemandList, params);
let supplyDemandDetail = (params = {}) => vm.$u.get(apiApp.supplyDemandDetail, params);
// 图书
let bookIndex = (params = {}) => vm.$u.get(apiApp.bookIndex, params);
let bookDetail = (params = {}) => vm.$u.get(apiApp.bookDetail, params);
let bookOther = (params = {}) => vm.$u.get(apiApp.bookOther, params);
// 将各个定义的接口名称统一放进对象挂载到vm.$u.api(因为vm就是this也即this.$u.api)下
vm.$u.api = {
// 用户
@ -138,6 +148,11 @@ const install = (Vue, vm) => {
supplyDemandSave,
supplyDemandList,
supplyDemandDetail,
// 图书
bookIndex,
bookDetail,
bookOther,
};
}

@ -0,0 +1,274 @@
<template>
<view class="book-detail-container">
<!-- 加载状态 -->
<view v-if="loading" class="loading-container">
<view class="loading-text">加载中...</view>
</view>
<!-- 图书详情 -->
<view v-else-if="bookInfo" class="book-detail">
<!-- 图书基本信息 -->
<view class="book-header">
<view class="book-cover">
<u-image :src="bookInfo.image" width="240rpx" height="320rpx" border-radius="12"></u-image>
</view>
<view class="book-info">
<text class="book-title">{{ bookInfo.title }}</text>
<text class="book-author">作者{{ bookInfo.author }}</text>
<text class="book-publisher">出版社{{ bookInfo.publisher }}</text>
<text class="book-year">出版年份{{ bookInfo.year }}</text>
<text class="book-isbn">ISBN{{ bookInfo.isbn }}</text>
<view class="book-category">
<u-tag :text="bookInfo.category" type="primary" size="mini" shape="circle" mode="light" />
</view>
</view>
</view>
<!-- 图书描述 -->
<view class="book-description">
<view class="section-title">图书简介</view>
<text class="description-text">{{ bookInfo.description || '暂无简介' }}</text>
</view>
<!-- 图书状态 -->
<view class="book-status">
<view class="section-title">图书状态</view>
<view class="status-item">
<text class="status-label">状态</text>
<u-tag :text="getStatusText(bookInfo.status)" :type="getStatusType(bookInfo.status)" size="mini" />
</view>
<view class="status-item">
<text class="status-label">创建时间</text>
<text class="status-value">{{ formatDate(bookInfo.created_at) }}</text>
</view>
</view>
</view>
<!-- 空状态 -->
<view v-else class="empty-container">
<view class="empty-text">图书信息不存在</view>
</view>
</view>
</template>
<script>
import uImage from '@/uview-ui/components/u-image/u-image.vue';
import uTag from '@/uview-ui/components/u-tag/u-tag.vue';
export default {
components: {
uImage,
uTag
},
data() {
return {
loading: false,
bookId: null,
bookInfo: null
}
},
onLoad(options) {
if (options.id) {
this.bookId = options.id;
this.loadBookDetail();
} else {
uni.showToast({
title: '图书ID不存在',
icon: 'none'
});
}
},
methods: {
//
loadBookDetail() {
this.loading = true;
this.$u.api.bookDetail({
id: this.bookId
}).then(res => {
if (res) {
// -
const book = res;
this.bookInfo = {
id: book.id,
title: book.title,
author: book.author,
publisher: book.publisher,
year: book.publish_year,
isbn: book.isbn,
category: book.category,
description: book.description,
status: book.status,
created_at: book.created_at,
image: book.cover ? book.cover.url : ''
};
} else {
uni.showToast({
title: '图书信息不存在',
icon: 'none'
});
}
}).catch(err => {
console.error('获取图书详情失败:', err);
uni.showToast({
title: '网络错误,请重试',
icon: 'none'
});
}).finally(() => {
this.loading = false;
});
},
//
getStatusText(status) {
const statusMap = {
0: '可借阅',
1: '已借出',
2: '维护中',
3: '已下架'
};
return statusMap[status] || '未知状态';
},
//
getStatusType(status) {
const typeMap = {
0: 'success',
1: 'warning',
2: 'info',
3: 'error'
};
return typeMap[status] || 'info';
},
//
formatDate(dateStr) {
if (!dateStr) return '未知';
const date = new Date(dateStr);
return date.toLocaleDateString('zh-CN');
}
}
}
</script>
<style lang="scss" scoped>
.book-detail-container {
background-color: #f5f5f5;
min-height: 100vh;
padding: 20rpx;
}
.loading-container {
display: flex;
justify-content: center;
align-items: center;
min-height: 400rpx;
}
.loading-text {
font-size: 28rpx;
color: #999;
}
.empty-container {
display: flex;
justify-content: center;
align-items: center;
min-height: 400rpx;
}
.empty-text {
font-size: 28rpx;
color: #999;
}
.book-detail {
background-color: #fff;
border-radius: 20rpx;
padding: 30rpx;
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05);
}
.book-header {
display: flex;
margin-bottom: 40rpx;
}
.book-cover {
margin-right: 30rpx;
flex-shrink: 0;
}
.book-info {
flex: 1;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.book-title {
font-size: 36rpx;
font-weight: bold;
color: #333;
margin-bottom: 20rpx;
line-height: 1.4;
}
.book-author,
.book-publisher,
.book-year,
.book-isbn {
font-size: 28rpx;
color: #666;
margin-bottom: 15rpx;
line-height: 1.5;
}
.book-category {
margin-top: 20rpx;
}
.book-description {
margin-bottom: 40rpx;
padding-bottom: 30rpx;
border-bottom: 1rpx solid #eee;
}
.section-title {
font-size: 32rpx;
font-weight: bold;
color: #333;
margin-bottom: 20rpx;
}
.description-text {
font-size: 28rpx;
color: #666;
line-height: 1.6;
text-align: justify;
}
.book-status {
.section-title {
margin-bottom: 20rpx;
}
}
.status-item {
display: flex;
align-items: center;
margin-bottom: 15rpx;
}
.status-label {
font-size: 28rpx;
color: #666;
margin-right: 20rpx;
min-width: 120rpx;
}
.status-value {
font-size: 28rpx;
color: #333;
}
</style>

@ -1,8 +1,8 @@
<template>
<view class="library-container">
<view class="search-bar">
<u-search placeholder="书名/作者/关键词/完整ISBN..." v-model="keyword" :show-action="false" bg-color="#f5f5f5"
border-color="#e0e0e0"></u-search>
<u-search placeholder="书名/作者" v-model="keyword" :show-action="false" bg-color="#f5f5f5"
border-color="#e0e0e0" @search="searchBooks"></u-search>
</view>
<view class="tabs-container">
@ -10,23 +10,36 @@
</view>
<view class="book-list">
<view v-for="book in bookList" :key="book.id" class="book-card">
<view class="book-item">
<view class="book-cover">
<u-image :src="book.image" width="180rpx" height="240rpx" border-radius="8"></u-image>
</view>
<view class="book-info">
<text class="book-title">{{ book.title }}</text>
<text class="book-author">{{ book.author }}</text>
<text class="book-publisher">{{ book.publisher }} · {{ book.year }}</text>
<view class="tags-container">
<u-tag v-for="(tag, index) in book.tags" :key="index" :text="tag.text" :type="tag.type"
size="mini" shape="circle" mode="light" />
<!-- 加载状态 -->
<view v-if="loading" class="loading-container">
<view class="loading-text">加载中...</view>
</view>
<!-- 空状态 -->
<view v-else-if="bookList.length === 0" class="empty-container">
<view class="empty-text">暂无图书数据</view>
</view>
<!-- 图书列表 -->
<view v-else>
<view v-for="book in bookList" :key="book.id" class="book-card">
<view class="book-item">
<view class="book-cover">
<u-image :src="book.image" width="180rpx" height="240rpx" border-radius="8"></u-image>
</view>
<view class="book-info">
<text class="book-title">{{ book.title }}</text>
<text class="book-author">{{ book.author }}</text>
<text class="book-publisher">{{ book.publisher }} · {{ book.year }}</text>
<view class="tags-container">
<u-tag v-for="(tag, index) in book.tags" :key="index" :text="tag.text" :type="tag.type"
size="mini" shape="circle" mode="light" />
</view>
</view>
</view>
</view>
<view class="card-footer">
<view class="detail-button" @click="viewDetails(book.id)"></view>
<view class="card-footer">
<view class="detail-button" @click="viewDetails(book.id)"></view>
</view>
</view>
</view>
</view>
@ -38,6 +51,7 @@
import uTabs from '@/uview-ui/components/u-tabs/u-tabs.vue';
import uImage from '@/uview-ui/components/u-image/u-image.vue';
import uTag from '@/uview-ui/components/u-tag/u-tag.vue';
export default {
components: {
uSearch,
@ -49,75 +63,149 @@
return {
keyword: '',
currentTab: 0,
loading: false,
currentPage: 1,
totalPages: 1,
hasMore: true,
tabsList: [{
name: '全部'
}, {
name: '生物医药'
}, {
name: '半导体/集成电路'
}, {
name: '新能源'
}, {
name: '新材料'
}, {
name: '技术'
}],
bookList: [{
id: 1,
title: '股权激励',
author: '邱清荣著',
publisher: '中国友谊出版公司',
year: '2019',
image: 'https://via.placeholder.com/180x240.png/ffffff/000000?text=股权激励',
tags: [{
text: '技术类',
type: 'primary'
}, {
text: '图书馆 3F-A区-125',
type: 'info'
}]
},
{
id: 2,
title: '制造亚洲:一部地图上的历史',
author: '李四著',
publisher: '中国友谊出版公司',
year: '2019',
image: 'https://via.placeholder.com/180x240.png/ffffff/000000?text=制造亚洲',
tags: [{
text: '商业类',
type: 'primary'
}, {
text: '图书馆 3F-A区-125',
type: 'info'
}]
},
{
id: 3,
title: '股权金字塔',
author: '李四著',
publisher: '中国友谊出版公司',
year: '2019',
image: 'https://via.placeholder.com/180x240.png/ffffff/000000?text=股权金字塔',
tags: [{
text: '商业类',
type: 'primary'
}, {
text: '图书馆 3F-A区-125',
type: 'info'
}]
}
]
bookList: []
}
},
onLoad() {
this.loadCategories();
},
onReachBottom() {
if (this.hasMore && !this.loading) {
this.loadMore();
}
},
methods: {
//
loadCategories() {
this.$u.api.bookOther().then(res => {
if (res && res.category) {
// ""
const categories = [{
name: '全部'
}];
//
if (Array.isArray(res.category)) {
res.category.forEach(categoryName => {
categories.push({
name: categoryName
});
});
}
this.tabsList = categories;
}
}).catch(err => {
console.error('获取分类失败:', err);
// 使
this.tabsList = [{
name: '全部'
}];
}).finally(() => {
//
this.loadBookList();
});
},
//
loadBookList() {
this.loading = true;
const params = {
page: this.currentPage,
limit: 20
};
//
if (this.keyword.trim()) {
params.keyword = this.keyword.trim();
}
//
if (this.currentTab > 0) {
params.category = this.currentTab;
}
this.$u.api.bookIndex(params).then(res => {
//
const bookData = res || {};
const newBooks = (bookData.data || []).map(book => {
return {
id: book.id,
title: book.title,
author: book.author,
publisher: book.publisher,
year: book.publish_year,
image: book.cover ? book.cover.url : '',
tags: [
{
text: book.category || '未分类',
type: 'primary'
},
{
text: `ISBN: ${book.isbn || '未知'}`,
type: 'info'
}
]
};
});
//
if (this.currentPage === 1) {
this.bookList = newBooks;
} else {
this.bookList = [...this.bookList, ...newBooks];
}
//
this.currentPage = bookData.current_page || 1;
this.totalPages = bookData.last_page || 1;
this.hasMore = this.currentPage < this.totalPages;
}).catch(err => {
console.error('获取图书列表失败:', err);
uni.showToast({
title: '网络错误,请重试',
icon: 'none'
});
}).finally(() => {
this.loading = false;
});
},
//
searchBooks() {
this.currentPage = 1;
this.loadBookList();
},
//
tabChange(index) {
this.currentTab = index;
this.currentPage = 1;
this.loadBookList();
},
//
viewDetails(id) {
console.log('View details for book ID:', id);
// uni.navigateTo({ url: `/packages/library/detail?id=${id}` });
uni.navigateTo({
url: `/packages/library/detail?id=${id}`
});
},
tabChange(index) {
this.currentTab = index;
console.log('Tab changed to:', index);
//
loadMore() {
if (this.hasMore && !this.loading) {
this.currentPage += 1;
this.loadBookList();
}
}
}
}
@ -142,6 +230,30 @@
padding: 20rpx;
}
.loading-container {
display: flex;
justify-content: center;
align-items: center;
min-height: 400rpx;
}
.loading-text {
font-size: 28rpx;
color: #999;
}
.empty-container {
display: flex;
justify-content: center;
align-items: center;
min-height: 400rpx;
}
.empty-text {
font-size: 28rpx;
color: #999;
}
.book-card {
background-color: #fff;
border-radius: 20rpx;

@ -132,6 +132,11 @@
"style": {
"navigationBarTitleText": "图书馆"
}
},{
"path": "library/detail",
"style": {
"navigationBarTitleText": "图书详情"
}
},{
"path": "supply/index",
"style": {

Loading…
Cancel
Save