lion 10 months ago
commit c2af027bb5

@ -24,22 +24,36 @@ let apiApp = {
courseContentDetail:'/api/mobile/course/course-content-detail',
courseContentForm:'/api/mobile/course/course-content-form',
// 资讯
courseNews: "/api/mobile/course/news",
// 预约
scheduleIndex: '/api/mobile/schedule/index',
scheduleSave: '/api/mobile/schedule/save',
scheduleDetail: '/api/mobile/schedule/detail',
scheduleCheck: '/api/mobile/schedule/check',
scheduleCancel: '/api/mobile/schedule/cancel',
scheduleCheck: '/api/mobile/schedule/check',
scheduleCancel: '/api/mobile/schedule/cancel',
// 其他
otherConfig: '/api/mobile/other/config',
otherBanner: '/api/mobile/other/banner',
otherUploadFile: '/api/mobile/upload-file',
getparameter: '/api/admin/parameter/show',
updateDonates: '/api/mobile/user/update-donates',
uploadFile:'/api/mobile/upload-file'
updateDonates: '/api/mobile/user/update-donates',
uploadFile:'/api/mobile/upload-file',
// 供应需求
supplyDemandSave: '/api/mobile/supply-demand/save',
supplyDemandList: '/api/mobile/supply-demand/index',
supplyDemandDetail: '/api/mobile/supply-demand/detail',
supplyDemandMessageList: '/api/mobile/supply-demand/message-list',
supplyDemandSendMessage: '/api/mobile/supply-demand/send-message',
supplyDemandDialogues: '/api/mobile/supply-demand/dialogues',
// 图书
bookIndex: '/api/mobile/book/index',
bookDetail: '/api/mobile/book/detail',
bookOther: '/api/mobile/book/other',
}
// 此处第二个参数vm就是我们在页面使用的this你可以通过vm获取vuex等操作
@ -67,6 +81,7 @@ const install = (Vue, vm) => {
let myCourseContent = (params = {}) => vm.$u.get(apiApp.myCourseContent, params);
let courseContentDetail = (params = {}) => vm.$u.get(apiApp.courseContentDetail, params);
let courseContentForm = (params = {}) => vm.$u.post(apiApp.courseContentForm, params);
// 资讯
@ -76,9 +91,9 @@ const install = (Vue, vm) => {
let scheduleIndex = (params = {}) => vm.$u.get(apiApp.scheduleIndex, params);
let scheduleSave = (params = {}) => vm.$u.post(apiApp.scheduleSave, params);
let scheduleDetail = (params = {}) => vm.$u.get(apiApp.scheduleDetail, params);
let scheduleCheck = (params = {}) => vm.$u.get(apiApp.scheduleCheck, params);
let scheduleCancel = (params = {}) => vm.$u.get(apiApp.scheduleCancel, params);
let scheduleCheck = (params = {}) => vm.$u.get(apiApp.scheduleCheck, params);
let scheduleCancel = (params = {}) => vm.$u.get(apiApp.scheduleCancel, params);
// 其他
@ -86,9 +101,23 @@ const install = (Vue, vm) => {
let otherBanner = (params = {}) => vm.$u.get(apiApp.otherBanner, params);
let otherUploadFile = (params = {}) => vm.$u.post(apiApp.otherUploadFile, params);
let getparameter = (params = {}) => vm.$u.get(apiApp.getparameter, params);
let updateDonates = (params = {}) => vm.$u.post(apiApp.updateDonates, params);
let updateDonates = (params = {}) => vm.$u.post(apiApp.updateDonates, params);
let uploadFile = (params = {}) => vm.$u.post(apiApp.uploadFile, params);
// 供应需求
let supplyDemandSave = (params = {}) => vm.$u.post(apiApp.supplyDemandSave, params);
let supplyDemandList = (params = {}) => vm.$u.get(apiApp.supplyDemandList, params);
let supplyDemandDetail = (params = {}) => vm.$u.get(apiApp.supplyDemandDetail, params);
let supplyDemandMessageList = (params = {}) => vm.$u.get(apiApp.supplyDemandMessageList, params);
let supplyDemandSendMessage = (params = {}) => vm.$u.post(apiApp.supplyDemandSendMessage, params);
let supplyDemandDialogues = (params = {}) => vm.$u.get(apiApp.supplyDemandDialogues, 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 = {
// 用户
@ -118,7 +147,7 @@ const install = (Vue, vm) => {
scheduleIndex,
scheduleSave,
scheduleDetail,
scheduleCheck,
scheduleCheck,
scheduleCancel,
// 资讯
courseNews,
@ -126,9 +155,22 @@ const install = (Vue, vm) => {
otherConfig,
otherBanner,
otherUploadFile,
getparameter: getparameter,
updateDonates:updateDonates,
uploadFile:uploadFile
getparameter: getparameter,
updateDonates:updateDonates,
uploadFile:uploadFile,
// 供应需求
supplyDemandSave,
supplyDemandList,
supplyDemandDetail,
supplyDemandMessageList,
supplyDemandSendMessage,
supplyDemandDialogues,
// 图书
bookIndex,
bookDetail,
bookOther,
};
}

@ -0,0 +1,362 @@
<template>
<view class="chat-window">
<scroll-view
:scroll-y="true"
class="message-list"
:scroll-into-view="lastMessageId"
@scrolltoupper="onScrollToUpper"
:scroll-with-animation="false"
:upper-threshold="50">
<!-- 加载更多提示 -->
<view v-if="loading" class="loading-more">
<u-loading mode="circle" size="20"></u-loading>
<text class="loading-text">加载中...</text>
</view>
<view v-for="(message, index) in messages" :key="message.id">
<!-- 时间分割线 -->
<view v-if="shouldShowTimeDivider(message, index)" class="time-divider">
<text class="time-text">{{ formatTime(message.timestamp) }}</text>
</view>
<!-- 消息内容 -->
<view :id="'msg-' + message.id"
:class="['message-item', message.from === 'me' ? 'my-message' : 'their-message']">
<u-avatar v-if="message.from !== 'me'" :src="theirAvatar" size="80"></u-avatar>
<view class="message-content">
<text class="message-text">{{ message.content }}</text>
</view>
<u-avatar v-if="message.from === 'me'" :src="myAvatar" size="80"></u-avatar>
</view>
</view>
</scroll-view>
<view class="input-area">
<u-input v-model="newMessage" placeholder="输入消息..." class="input-field" :border="false" />
<u-button type="primary" size="medium" @click="sendMessage" class="send-button">发送</u-button>
</view>
</view>
</template>
<script>
export default {
data() {
return {
myAvatar: '',
theirAvatar: '',
newMessage: '',
messages: [],
lastMessageId: '',
supplyDemandId: '',
theirUserId: '',
loading: false,
currentPage: 1,
hasMore: true,
pageSize: 20
}
},
onLoad(options) {
const userId = options.userId;
const userName = options.userName;
const supplyDemandId = options.supplyDemandId;
const supplyDemandTitle = options.supplyDemandTitle ? decodeURIComponent(options.supplyDemandTitle) : '';
// ID
this.supplyDemandId = supplyDemandId;
this.theirUserId = userId;
//
console.log('当前用户信息:', this.$store.state.vuex_user);
//
const title = supplyDemandTitle ? `${supplyDemandTitle} - ${userName || userId}` : `${userName || userId} 的对话`;
uni.setNavigationBarTitle({
title: title
});
this.fetchMessages(supplyDemandId);
},
methods: {
fetchMessages(supplyDemandId, page = 1, append = false) {
if (this.loading || (!append && !this.hasMore)) return;
this.loading = true;
// 使API
this.$u.api.supplyDemandMessageList({
supply_demand_id: supplyDemandId,
page: page,
per_page: this.pageSize
}).then(res => {
console.log('获取消息列表成功:', res);
// res.message.data
if (res.message && res.message.data) {
const newMessages = res.message.data.map((item, index) => ({
id: item.id,
from: item.user_id === this.$store.state.vuex_user.id ? 'me' : 'them',
content: item.content,
timestamp: item.created_at,
user: item.user,
to_user: item.to_user
}));
if (append) {
//
this.messages.unshift(...newMessages);
} else {
//
this.messages = newMessages.sort((a, b) => new Date(a.timestamp) - new Date(b.timestamp));
}
//
this.hasMore = res.message.current_page < res.message.last_page;
this.currentPage = res.message.current_page;
//
if (this.messages.length > 0) {
const firstMessage = this.messages[0];
if (firstMessage.from === 'me') {
this.myAvatar = firstMessage.user?.headimgurl || '';
this.theirAvatar = firstMessage.to_user?.headimgurl || '';
} else {
this.myAvatar = firstMessage.to_user?.headimgurl || '';
this.theirAvatar = firstMessage.user?.headimgurl || '';
}
// 使
const theirUser = firstMessage.from === 'me' ? firstMessage.to_user : firstMessage.user;
if (theirUser) {
const displayName = theirUser.nickname || theirUser.name || `用户${theirUser.id}`;
uni.setNavigationBarTitle({
title: `${displayName} 的对话`
});
}
}
if (!append) {
this.scrollToBottom();
}
}
}).catch(err => {
console.error('获取消息列表失败:', err);
}).finally(() => {
this.loading = false;
});
},
sendMessage() {
if (this.newMessage.trim() === '') return;
const messageContent = this.newMessage.trim();
this.newMessage = '';
// ID使
const tempId = Date.now();
//
const newMsg = {
id: tempId,
from: 'me',
content: messageContent,
timestamp: new Date().toISOString(),
user: this.$store.state.vuex_user,
to_user: null
};
//
this.messages.push(newMsg);
this.scrollToBottom();
// API
this.$u.api.supplyDemandSendMessage({
supply_demand_id: this.supplyDemandId,
content: messageContent,
to_user_id: this.theirUserId
}).then(res => {
console.log('发送消息成功:', res);
// IDID
if (res && res.id) {
const lastMessage = this.messages[this.messages.length - 1];
if (lastMessage.id === tempId) {
lastMessage.id = res.id;
}
}
}).catch(err => {
console.error('发送消息失败:', err);
//
this.messages = this.messages.filter(msg => msg.id !== tempId);
//
uni.showToast({
title: '发送失败,请重试',
icon: 'none'
});
});
},
scrollToBottom() {
this.$nextTick(() => {
if (this.messages.length > 0) {
//
this.lastMessageId = `msg-${this.messages[this.messages.length - 1].id}`;
}
})
},
formatTime(timestamp) {
if (!timestamp) return '';
const date = new Date(timestamp);
const now = new Date();
const diff = now - date;
//
if (diff < 24 * 60 * 60 * 1000 && date.getDate() === now.getDate()) {
return date.toLocaleTimeString('zh-CN', {
hour: '2-digit',
minute: '2-digit'
});
}
// " + "
if (diff < 48 * 60 * 60 * 1000 && date.getDate() === now.getDate() - 1) {
return `昨天 ${date.toLocaleTimeString('zh-CN', {
hour: '2-digit',
minute: '2-digit'
})}`;
}
//
return date.toLocaleString('zh-CN', {
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit'
});
},
shouldShowTimeDivider(message, index) {
//
if (index === 0) return true;
const currentTime = new Date(message.timestamp);
const prevTime = new Date(this.messages[index - 1].timestamp);
// 5线
const timeDiff = Math.abs(currentTime - prevTime);
return timeDiff > 5 * 60 * 1000; // 5
},
onScrollToUpper() {
//
if (this.hasMore && !this.loading) {
this.fetchMessages(this.supplyDemandId, this.currentPage + 1, true);
}
}
}
}
</script>
<style lang="scss" scoped>
.chat-window {
display: flex;
flex-direction: column;
height: 100vh;
background-color: #f5f5f5;
}
.message-list {
flex: 1;
padding: 20rpx;
overflow-y: auto;
box-sizing: border-box;
}
.message-item {
display: flex;
margin-bottom: 30rpx;
align-items: flex-start;
}
.my-message {
justify-content: flex-end;
.message-content {
background-color: #409eff;
color: #fff;
border-radius: 20rpx 20rpx 4rpx 20rpx;
margin-right: 20rpx;
}
}
.their-message {
justify-content: flex-start;
.message-content {
background-color: #fff;
color: #333;
border-radius: 20rpx 20rpx 20rpx 4rpx;
margin-left: 20rpx;
}
}
.message-content {
padding: 20rpx 25rpx;
max-width: 70%;
font-size: 30rpx;
line-height: 1.6;
display: flex;
flex-direction: column;
}
.message-text {
margin-bottom: 8rpx;
}
.message-time {
font-size: 22rpx;
color: #999;
opacity: 0.8;
}
.time-divider {
display: flex;
justify-content: center;
align-items: center;
margin: 30rpx 0 20rpx 0;
}
.time-text {
background-color: rgba(0, 0, 0, 0.1);
color: #999;
font-size: 24rpx;
padding: 8rpx 16rpx;
border-radius: 20rpx;
text-align: center;
}
.loading-more {
display: flex;
justify-content: center;
align-items: center;
padding: 20rpx 0;
}
.loading-text {
font-size: 24rpx;
color: #999;
margin-left: 10rpx;
}
.input-area {
display: flex;
align-items: center;
padding: 20rpx;
background-color: #fff;
border-top: 1rpx solid #e0e0e0;
}
.input-field {
flex: 1;
margin-right: 20rpx;
background-color: #f5f5f5;
border-radius: 12rpx;
padding: 0 20rpx;
}
.send-button {
height: 70rpx;
line-height: 70rpx;
}
</style>

@ -0,0 +1,166 @@
<template>
<view class="chat-list-container">
<view v-if="chatList.length === 0" class="empty-state">
<u-empty text="暂无聊天记录" mode="chat"></u-empty>
</view>
<view v-else v-for="chat in chatList" :key="chat.id" class="chat-item" @click="goToChat(chat.id)">
<u-avatar :src="chat.user.avatar" size="90" loading-icon="account"></u-avatar>
<view class="chat-content">
<view class="content-header">
<text class="user-name">{{ chat.user.name }}</text>
<text class="last-time">{{ chat.lastMessage.time }}</text>
</view>
<view class="content-body">
<text class="last-message">{{ chat.lastMessage.content }}</text>
<u-badge :is-dot="true" type="error" v-if="chat.unreadCount > 0"></u-badge>
</view>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
chatList: []
}
},
onLoad() {
this.fetchDialogues();
},
methods: {
fetchDialogues() {
this.$u.api.supplyDemandDialogues().then(res => {
console.log('获取会话列表成功:', res);
if (res.dialogue && res.dialogue.data) {
this.chatList = res.dialogue.data.map(item => {
//
const currentUserId = this.$store.state.vuex_user.id;
const otherUser = item.user_id === currentUserId ? item.to_user : item.user;
return {
id: item.id,
user: {
id: otherUser.id,
name: otherUser.nickname || otherUser.name || otherUser.username || `用户${otherUser.id}`,
avatar: otherUser.headimgurl || ''
},
lastMessage: {
content: item.last_content || '暂无消息',
time: this.formatTime(item.last_datetime || item.updated_at)
},
unreadCount: 0, // 0
supplyDemandId: item.supply_demand_id,
supplyDemandTitle: item.supply_demand?.title || ''
};
});
}
}).catch(err => {
console.error('获取会话列表失败:', err);
});
},
goToChat(id) {
//
const chat = this.chatList.find(item => item.id === id);
if (chat) {
uni.navigateTo({
url: `/packages/chat/chatWindow?userId=${chat.user.id}&userName=${chat.user.name}&supplyDemandId=${chat.supplyDemandId}`
});
}
},
formatTime(timestamp) {
if (!timestamp) return '';
const date = new Date(timestamp);
const now = new Date();
const diff = now - date;
//
if (diff < 24 * 60 * 60 * 1000 && date.getDate() === now.getDate()) {
return date.toLocaleTimeString('zh-CN', {
hour: '2-digit',
minute: '2-digit'
});
}
// " + "
if (diff < 48 * 60 * 60 * 1000 && date.getDate() === now.getDate() - 1) {
return `昨天 ${date.toLocaleTimeString('zh-CN', {
hour: '2-digit',
minute: '2-digit'
})}`;
}
//
if (diff < 7 * 24 * 60 * 60 * 1000) {
const weekdays = ['周日', '周一', '周二', '周三', '周四', '周五', '周六'];
return weekdays[date.getDay()];
}
//
return date.toLocaleDateString('zh-CN', {
month: '2-digit',
day: '2-digit'
});
}
}
}
</script>
<style lang="scss" scoped>
.chat-list-container {
background-color: #fff;
}
.chat-item {
display: flex;
align-items: center;
padding: 25rpx 30rpx;
border-bottom: 1rpx solid #f5f5f5;
}
.chat-content {
flex: 1;
margin-left: 20rpx;
overflow: hidden;
}
.content-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 8rpx;
}
.user-name {
font-size: 32rpx;
font-weight: 500;
}
.last-time {
font-size: 24rpx;
color: #999;
}
.content-body {
display: flex;
justify-content: space-between;
align-items: center;
}
.last-message {
font-size: 28rpx;
color: #999;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.empty-state {
padding: 100rpx 0;
display: flex;
justify-content: center;
align-items: center;
}
</style>

@ -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>

@ -0,0 +1,322 @@
<template>
<view class="library-container">
<view class="search-bar">
<u-search placeholder="书名/作者" v-model="keyword" :show-action="false" bg-color="#f5f5f5"
border-color="#e0e0e0" @search="searchBooks"></u-search>
</view>
<view class="tabs-container">
<u-tabs :list="tabsList" :is-scroll="true" :current="currentTab" @change="tabChange" active-color="#73685c"></u-tabs>
</view>
<view class="book-list">
<!-- 加载状态 -->
<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 class="card-footer">
<view class="detail-button" @click="viewDetails(book.id)"></view>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
import uSearch from '@/uview-ui/components/u-search/u-search.vue';
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,
uTabs,
uImage,
uTag
},
data() {
return {
keyword: '',
currentTab: 0,
loading: false,
currentPage: 1,
totalPages: 1,
hasMore: true,
tabsList: [{
name: '全部'
}],
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}`
});
},
//
loadMore() {
if (this.hasMore && !this.loading) {
this.currentPage += 1;
this.loadBookList();
}
}
}
}
</script>
<style lang="scss" scoped>
.library-container {
background-color: #f5f5f5;
min-height: 100vh;
}
.search-bar {
padding: 20rpx 30rpx;
background-color: #fff;
}
.tabs-container {
border-top: 1rpx solid #eee;
}
.book-list {
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;
padding: 30rpx;
margin-bottom: 20rpx;
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05);
}
.book-item {
display: flex;
margin-bottom: 20rpx;
}
.book-cover {
margin-right: 30rpx;
flex-shrink: 0;
}
.book-info {
display: flex;
flex-direction: column;
justify-content: space-between;
flex: 1;
}
.book-title {
font-size: 34rpx;
font-weight: bold;
color: #333;
margin-bottom: 10rpx;
}
.book-author,
.book-publisher {
font-size: 26rpx;
color: #666;
margin-bottom: 10rpx;
}
.tags-container {
display: flex;
flex-wrap: wrap;
gap: 10rpx;
margin-top: 10rpx;
}
.card-footer {
display: flex;
justify-content: center;
margin-top: 20rpx;
padding-top: 20rpx;
border-top: 1rpx solid #eee;
}
.detail-button {
height: 60rpx;
line-height: 60rpx;
display: inline-block;
padding: 0 160rpx;
border-radius: 40rpx;
color: #fff;
font-size: 30rpx;
background-image: linear-gradient(to right, #6a7dfe, #12099a);
text-align: center;
}
</style>

@ -0,0 +1,213 @@
<template>
<view class="container" v-if="detail">
<view class="user-info-header">
<u-avatar :src="detail.user && detail.user.headimgurl || 'https://via.placeholder.com/80'" size="80"></u-avatar>
<view class="user-details">
<text class="user-name">{{ detail.user && detail.user.name || detail.user && detail.user.nickname || '匿名用户' }}</text>
<text class="post-time">{{ detail.created_at }}</text>
</view>
<view class="stats">
<text class="type-badge" :class="detail.type === 1 ? 'supply' : 'demand'">{{ detail.type === 1 ? '供应' : '需求' }}</text>
<text class="views">{{ detail.contact_count }}人私信 {{ detail.view_count }}浏览</text>
</view>
</view>
<view class="content-card">
<text class="title">{{ detail.title }}</text>
<text class="description">{{ detail.content }}</text>
<view class="tags" v-if="detail.tag">
<text v-for="tag in detail.tag.split(',')" :key="tag" class="tag">{{ tag }}</text>
</view>
</view>
<view class="footer">
<view class="footer-action" @click="toggleCollect">
<u-icon :name="isCollected ? 'star-fill' : 'star'" size="40" :color="isCollected ? '#f29100' : '#606266'"></u-icon>
<text :style="{ color: isCollected ? '#f29100' : '#606266' }">收藏</text>
</view>
<u-button type="primary" shape="circle" class="message-btn" @click="goToChat"></u-button>
</view>
</view>
</template>
<script>
import uAvatar from '@/uview-ui/components/u-avatar/u-avatar.vue';
import uIcon from '@/uview-ui/components/u-icon/u-icon.vue';
import uButton from '@/uview-ui/components/u-button/u-button.vue';
export default {
components: {
uAvatar,
uIcon,
uButton
},
data() {
return {
detail: null,
isCollected: false
}
},
onLoad(options) {
console.log("页面ID:", options.id);
this.fetchDetailData(options.id);
},
methods: {
fetchDetailData(id) {
this.$u.api.supplyDemandDetail({ id: id }).then(res => {
console.log('详情数据:', res);
if (res && res.id) {
this.detail = res;
} else {
this.$u.toast('获取详情失败');
}
}).catch(err => {
console.error('获取详情失败:', err);
this.$u.toast('网络错误,请重试');
});
},
toggleCollect() {
this.isCollected = !this.isCollected;
this.$u.toast(this.isCollected ? '收藏成功' : '取消收藏');
},
goToChat() {
if (this.detail && this.detail.user && this.detail.user.id) {
const userName = this.detail.user.name || this.detail.user.nickname || '匿名用户';
uni.navigateTo({
url: `/packages/chat/chatWindow?userId=${this.detail.user.id}&userName=${userName}`
});
} else {
this.$u.toast('用户信息不完整');
}
}
}
}
</script>
<style lang="scss" scoped>
.container {
background: linear-gradient(to bottom, #e9f2fa, #f5f7fa);
min-height: 100vh;
}
.user-info-header {
display: flex;
align-items: center;
padding: 30rpx;
background: transparent;
}
.user-details {
display: flex;
flex-direction: column;
margin-left: 20rpx;
flex-grow: 1;
}
.user-name {
font-size: 32rpx;
font-weight: bold;
}
.post-time {
font-size: 24rpx;
color: #909399;
margin-top: 5rpx;
}
.stats {
display: flex;
flex-direction: column;
align-items: flex-end;
}
.type-badge {
font-size: 24rpx;
padding: 8rpx 15rpx;
border-radius: 10rpx;
}
.type-badge.supply {
background-color: #fff0e6;
color: #f29100;
}
.type-badge.demand {
background-color: #e6f0ff;
color: #007aff;
}
.views {
font-size: 24rpx;
color: #909399;
margin-top: 10rpx;
}
.content-card {
background-color: #fff;
margin: 30rpx 30rpx 0;
padding: 30rpx;
border-radius: 20rpx;
}
.title {
font-size: 36rpx;
font-weight: bold;
display: block;
margin-bottom: 20rpx;
}
.description {
font-size: 28rpx;
color: #606266;
line-height: 1.8;
display: block;
margin-bottom: 30rpx;
}
.content-image {
width: 100%;
border-radius: 12rpx;
margin-bottom: 30rpx;
}
.tags {
display: flex;
flex-wrap: wrap;
}
.tag {
background-color: #f5f5f5;
color: #606266;
font-size: 24rpx;
padding: 10rpx 20rpx;
border-radius: 30rpx;
margin-right: 15rpx;
margin-bottom: 10rpx;
}
.footer {
position: fixed;
bottom: 0;
left: 0;
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
padding: 20rpx 70rpx;
background-color: #fff;
box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.05);
box-sizing: border-box;
}
.footer-action {
display: flex;
flex-direction: column;
align-items: center;
color: #606266;
font-size: 24rpx;
}
.message-btn {
width: 250rpx;
}
</style>

@ -0,0 +1,316 @@
<template>
<view class="container">
<view class="search-bar">
<u-search placeholder="请输入关键词" v-model="keyword" :show-action="false" @search="search" @input="onSearchInput"></u-search>
</view>
<u-tabs :list="tabs" :is-scroll="false" :current="currentTab" @change="changeTab"></u-tabs>
<view class="list-container">
<view v-for="item in list" :key="item.id" class="list-item">
<view class="item-header">
<view :class="['type-badge', item.type === 1 ? 'supply' : 'demand']">{{ item.type === 1 ? '供应' : '需求' }}</view>
<text class="time">{{ item.created_at }}</text>
</view>
<text class="title">{{ item.title }}</text>
<text class="description">{{ item.content }}</text>
<view class="tags" v-if="item.tag">
<text v-for="tag in item.tag.split(',')" :key="tag" class="tag">{{ tag }}</text>
</view>
<u-line color="#e8e8e8" margin="20rpx 0" />
<view class="item-footer">
<view class="user-info">
<u-avatar :src="item.user.headimgurl || ''" size="60"></u-avatar>
<text class="user-name">{{ item.user.name || '匿名用户' }}</text>
</view>
<view class="actions">
<view class="view-button view-button-check" @click="goToDetail(item.id)">
<text class="button-text">查看</text>
</view>
<view class="view-button view-button-msg" @click="goToChat(item)">
<text class="button-text">私信</text>
</view>
</view>
</view>
</view>
<u-loadmore :status="status" nomore-text="~" />
</view>
<image class="publish-image" :src="base.imgHost('publish.png')" @click="goToPublish"></image>
</view>
</template>
<script>
import uSearch from '@/uview-ui/components/u-search/u-search.vue';
import uLoadmore from '@/uview-ui/components/u-loadmore/u-loadmore.vue';
import uTabs from '@/uview-ui/components/u-tabs/u-tabs.vue';
import uLine from '@/uview-ui/components/u-line/u-line.vue';
import uAvatar from '@/uview-ui/components/u-avatar/u-avatar.vue';
import { base } from '@/common/util.js';
export default {
components: {
uSearch,
uLoadmore,
uTabs,
uLine,
uAvatar
},
data() {
return {
base,
keyword: '',
currentTab: 0,
tabs: [{
name: '全部'
}, {
name: '供应'
}, {
name: '需求'
}],
list: [],
page: 1,
pageSize: 10,
status: 'loadmore',
loading: false
}
},
onLoad() {
this.fetchList();
},
onReachBottom() {
if (this.status === 'loadmore' && !this.loading) {
this.page++;
this.fetchList();
}
},
watch: {
keyword(newVal, oldVal) {
//
if (oldVal && !newVal) {
this.page = 1;
this.list = [];
this.status = 'loadmore';
this.fetchList();
}
}
},
methods: {
changeTab(index) {
this.currentTab = index;
this.page = 1;
this.list = [];
this.status = 'loadmore';
this.fetchList();
},
search() {
this.page = 1;
this.list = [];
this.status = 'loadmore';
this.fetchList();
},
onSearchInput(value) {
//
// watch
},
fetchList() {
if (this.loading) return;
this.loading = true;
const params = {
page: this.page,
pageSize: this.pageSize
};
// tabtype
if (this.currentTab === 1) {
params.type = 1; //
} else if (this.currentTab === 2) {
params.type = 2; //
}
//
if (this.keyword) {
params.keyword = this.keyword;
}
this.$u.api.supplyDemandList(params).then(res => {
// res supplyDemands
const supplyDemands = res.supplyDemands;
const newList = supplyDemands.data || [];
if (this.page === 1) {
this.list = newList;
} else {
this.list = [...this.list, ...newList];
}
//
if (supplyDemands.current_page >= supplyDemands.last_page) {
this.status = 'nomore';
} else {
this.status = 'loadmore';
}
}).catch(err => {
console.error('获取供需列表失败:', err);
this.$u.toast('网络错误,请重试');
}).finally(() => {
this.loading = false;
});
},
goToPublish() {
uni.navigateTo({
url: '/packages/supply/publish'
})
},
goToDetail(id) {
uni.navigateTo({
url: `/packages/supply/detail?id=${id}`
})
},
goToChat(item) {
//
uni.navigateTo({
url: `/packages/chat/chatWindow?userId=${item.user_id}&userName=${item.user.name}&supplyDemandId=${item.id}`
})
}
}
}
</script>
<style lang="scss" scoped>
.container {
background-color: #f5f5f5;
min-height: 100vh;
}
.search-bar {
padding: 20rpx;
background-color: #fff;
}
.list-container {
background: linear-gradient(to bottom, #e9f2fa, #e9f2fa);
padding: 20rpx;
min-height: calc(100vh - 180rpx);
/* Adjust as needed */
}
.list-item {
background-color: #fff;
border-radius: 20rpx;
padding: 25rpx;
margin-bottom: 20rpx;
}
.item-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15rpx;
}
.type-badge {
font-size: 24rpx;
padding: 8rpx 15rpx;
border-radius: 10rpx;
}
.supply {
background-color: #fff0e6;
color: #f29100;
}
.demand {
background-color: #e6f0ff;
color: #007aff;
}
.time {
font-size: 24rpx;
color: #999;
}
.title {
font-size: 32rpx;
font-weight: bold;
display: block;
margin-bottom: 10rpx;
}
.description {
font-size: 28rpx;
color: #666;
line-height: 1.6;
margin-bottom: 30rpx;
display: block;
max-height: 120rpx; /* 约3行文字的高度 */
overflow: hidden;
}
.tags {
display: flex;
flex-wrap: wrap;
}
.tag {
background-color: #f5f5f5;
color: #666;
font-size: 24rpx;
padding: 8rpx 15rpx;
border-radius: 30rpx;
margin-right: 15rpx;
margin-bottom: 10rpx;
}
.item-footer {
display: flex;
justify-content: space-between;
align-items: center;
}
.user-info {
display: flex;
align-items: center;
}
.user-name {
font-size: 28rpx;
margin-left: 15rpx;
}
.actions {
display: flex;
align-items: center;
}
.view-button {
width: 150rpx;
height: 60rpx;
border-radius: 30rpx;
display: flex;
align-items: center;
justify-content: center;
}
.view-button-check {
background: linear-gradient(to right, #e3ccb2, #cba579);
margin-right: 20rpx;
}
.view-button-msg {
background: linear-gradient(to right, #5d5ebc, #12099a);
}
.button-text {
color: white;
font-size: 26rpx;
}
.publish-image {
position: fixed;
bottom: 80rpx;
right: 0rpx;
width: 180rpx;
height: 140rpx;
z-index: 99;
}
</style>

@ -0,0 +1,281 @@
<template>
<view class="container">
<view class="card">
<text class="section-title">供需类型</text>
<view class="type-selector">
<view :class="['type-button', form.type === 'supply' ? 'active' : '']" @click="form.type = 'supply'">
<u-icon name="server-fill" :color="form.type === 'supply' ? '#C9A36D' : '#909399'"></u-icon>
<text class="type-text">供应</text>
</view>
<view :class="['type-button', form.type === 'demand' ? 'active' : '']" @click="form.type = 'demand'">
<u-icon name="order" :color="form.type === 'demand' ? '#C9A36D' : '#909399'"></u-icon>
<text class="type-text">需求</text>
</view>
</view>
</view>
<view class="card">
<text class="section-title">基本信息</text>
<u-form :model="form" ref="uForm">
<u-form-item label="标题" label-width="150" prop="title" :border-bottom="false">
<u-input v-model="form.title" placeholder="请输入标题, 简明扼要" :maxlength="50" type="text"
:custom-style="inputStyle('title')" @focus="activeInput = 'title'" @blur="activeInput = null" />
</u-form-item>
<u-form-item label="详细描述" label-width="150" prop="description" :border-bottom="false">
<u-input v-model="form.description" type="textarea" placeholder="请详细描述您的供需内容..." :maxlength="500" height="200"
:custom-style="inputStyle('description')" @focus="activeInput = 'description'" @blur="activeInput = null" />
</u-form-item>
<u-form-item label="行业标签" label-width="150" prop="tags" :border-bottom="false">
<u-input v-model="tagInput" type="text" placeholder="输入后按回车键确认"
:custom-style="inputStyle('tags')" @focus="activeInput = 'tags'" @blur="activeInput = null" @confirm="addTag" />
</u-form-item>
<view class="tag-container" v-if="form.tags.length > 0">
<view v-for="(tag, index) in form.tags" :key="index" class="tag-item">
<text>{{ tag }}</text>
<u-icon name="close" size="20" @click="removeTag(index)"></u-icon>
</view>
</view>
<view class="form-tip">建议添加相关行业标签, 方便其他校友找到你</view>
</u-form>
</view>
<view class="card">
<text class="section-title">联系方式 *</text>
<view class="contact-selector">
<view :class="['contact-button', form.contactType === 'wechat' ? 'active' : '']" @click="form.contactType = 'wechat'">
<u-icon name="chat-fill" :color="form.contactType === 'wechat' ? '#C9A36D' : '#909399'"></u-icon>
<text class="contact-text">微信</text>
</view>
<view :class="['contact-button', form.contactType === 'phone' ? 'active' : '']" @click="form.contactType = 'phone'">
<u-icon name="phone-fill" :color="form.contactType === 'phone' ? '#C9A36D' : '#909399'"></u-icon>
<text class="contact-text">电话</text>
</view>
<view :class="['contact-button', form.contactType === 'email' ? 'active' : '']" @click="form.contactType = 'email'">
<u-icon name="email-fill" :color="form.contactType === 'email' ? '#C9A36D' : '#909399'"></u-icon>
<text class="contact-text">邮箱</text>
</view>
</view>
<u-input v-model="form.contactValue" :placeholder="contactPlaceholder"
:custom-style="inputStyle('contact')" @focus="activeInput = 'contact'" @blur="activeInput = null" />
<text class="privacy-notice">隐私保护: 您的联系方式仅对感兴趣的校友可见, 平台内置防骚扰机制, 保护您的隐私安全</text>
</view>
<view class="footer-button">
<u-button type="primary" shape="circle" @click="submit">{{ form.type === 'supply' ? '' : '' }}</u-button>
</view>
</view>
</template>
<script>
export default {
data() {
return {
activeInput: null,
tagInput: '',
form: {
type: 'supply', // supply or demand
title: '',
description: '',
tags: [],
contactType: 'wechat', // wechat, phone, email
contactValue: '',
},
};
},
computed:{
contactPlaceholder(){
const placeholders = {
wechat: '输入微信号',
phone: '输入电话号码',
email: '输入邮箱地址'
}
return placeholders[this.form.contactType]
}
},
methods: {
inputStyle(name) {
const style = {
backgroundColor: '#f7f8fa',
borderRadius: '16rpx',
padding: '18rpx 25rpx',
border: '1px solid #f7f8fa',
transition: 'border-color 0.2s ease',
};
if (this.activeInput === name) {
style.borderColor = '#D4C3AB';
}
return style;
},
addTag() {
if (this.tagInput && !this.form.tags.includes(this.tagInput)) {
this.form.tags.push(this.tagInput);
this.tagInput = '';
}
},
removeTag(index) {
this.form.tags.splice(index, 1);
},
submit() {
const params = {
title: this.form.title,
type: this.form.type === 'supply' ? 1 : 2,
content: this.form.description,
tag: this.form.tags.join(','),
wechat: this.form.contactType === 'wechat' ? this.form.contactValue : '',
mobile: this.form.contactType === 'phone' ? this.form.contactValue : '',
email: this.form.contactType === 'email' ? this.form.contactValue : ''
};
this.$u.api.supplyDemandSave(params).then(res => {
uni.showToast({
title: '发布成功',
icon: 'success',
success: () => {
setTimeout(() => {
uni.navigateBack();
}, 1200);
}
});
});
}
}
}
</script>
<style lang="scss" scoped>
.container {
min-height: 100vh;
background: linear-gradient(to bottom, #e9f2fa, #f5f7fa);
padding: 30rpx 20rpx 140rpx 20rpx;
box-sizing: border-box;
}
.card {
background-color: #fff;
border-radius: 20rpx;
padding: 30rpx;
margin-bottom: 30rpx;
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05);
}
.section-title {
font-size: 32rpx;
font-weight: bold;
display: block;
margin-bottom: 30rpx;
}
.type-selector, .contact-selector {
display: flex;
justify-content: space-between;
margin-bottom: 30rpx;
}
.type-button {
width: 48%;
display: flex;
justify-content: center;
align-items: center;
background-color: #f5f5f5;
border-radius: 12rpx;
padding: 20rpx 0;
transition: all 0.3s;
&.active {
background-color: #fdf3e8;
border: 1rpx solid #e8d1b5;
.type-text {
color: #C9A36D;
}
}
}
.contact-button {
width: 31%;
display: flex;
justify-content: center;
align-items: center;
background-color: #f5f5f5;
border-radius: 12rpx;
padding: 20rpx 0;
transition: all 0.3s;
&.active {
background-color: #fdf3e8;
border: 1rpx solid #e8d1b5;
.contact-text {
color: #C9A36D;
}
}
}
.type-text, .contact-text {
font-size: 28rpx;
margin-left: 10rpx;
color: #606266;
}
.form-tip {
font-size: 24rpx;
color: #909399;
margin-top: 10rpx;
padding-left: 150rpx;
}
.tag-container {
display: flex;
flex-wrap: wrap;
margin-top: 20rpx;
padding-left: 150rpx;
}
.tag-item {
background-color: #f5f5f5;
color: #606266;
padding: 10rpx 20rpx;
border-radius: 12rpx;
margin-right: 15rpx;
margin-bottom: 15rpx;
display: flex;
align-items: center;
text {
margin-right: 10rpx;
}
}
.privacy-notice {
display: block;
font-size: 24rpx;
color: #909399;
margin-top: 30rpx;
line-height: 1.6;
}
.footer-button {
position: fixed;
bottom: 0;
left: 0;
width: 100%;
padding: 20rpx 40rpx;
background-color: #fff;
box-sizing: border-box;
box-shadow: 0 -2rpx 10rpx rgba(0,0,0,0.05);
}
// --- Custom Input Styles ---
::v-deep .u-form-item .u-line {
display: none;
}
::v-deep .u-input__body {
background-color: #f7f8fa !important;
border-radius: 16rpx !important;
padding: 18rpx 25rpx !important;
border: 1px solid #f7f8fa !important;
transition: border-color 0.2s ease;
}
::v-deep .u-input--focus .u-input__body {
border-color: #D4C3AB !important;
}
</style>

@ -38,7 +38,7 @@
</view>
<view>
<span>授课时间</span>
<span>{{survey.date}} {{survey.start_time}}-{{survey.end_time}}</span>
<span>{{survey.date}} {{survey.start_time?survey.start_time:''}}-{{survey.end_time?survey.end_time:''}}</span>
</view>
</view>
</view>

@ -20,7 +20,7 @@
}
}, {
"path": "pages/me/index",
"style": {
"style": {
"navigationBarTitleText": "我的"
}
}],
@ -28,103 +28,103 @@
"root": "packages",
"pages": [{
"path": "my/index",
"style": {
"style": {
"navigationBarTitleText": "个人信息"
}
},{
"path": "course/detail",
"style": {
"style": {
"navigationBarTitleText": ""
}
},{
"path": "course/freeDetail",
"style": {
"style": {
"navigationBarTitleText": ""
}
},{
"path": "register/index",
"style": {
"style": {
"navigationBarTitleText": "注册"
}
},{
"path": "register/login",
"style": {
"style": {
"navigationBarTitleText": "登录",
"navigationStyle": "custom"
}
},{
"path": "apply/index",
"style": {
"style": {
"navigationBarTitleText": "在线报名"
}
},{
"path": "booksubmit/index",
"style": {
"style": {
"navigationBarTitleText": "我要预约"
}
},{
"path": "booksubmit/appointment",
"style": {
"style": {
"navigationBarTitleText": "场地预约"
}
},{
"path": "schoolmate/index",
"style": {
"style": {
"navigationBarTitleText": "校友库"
}
},{
"path": "mycourse/index",
"style": {
"style": {
"navigationBarTitleText": "我的课程"
}
},{
"path": "mycourse/detail",
"style": {
"style": {
"navigationBarTitleText": "课程详情"
}
},{
"path": "mycourse/courseStatus",
"style": {
"style": {
"navigationBarTitleText": "报名状态"
}
},{
"path": "mycourse/coursePay",
"style": {
"style": {
"navigationBarTitleText": "报名缴费"
}
},{
"path": "mycourse/courseContents",
"style": {
"style": {
"navigationBarTitleText": ""
}
},{
"path": "mycourse/courseTxl",
"style": {
"style": {
"navigationBarTitleText": "本班通讯录"
}
},{
"path": "mybook/index",
"style": {
"style": {
"navigationBarTitleText": "我的预约"
}
},{
"path": "mybook/detail",
"style": {
"style": {
"navigationBarTitleText": "预约详情"
}
},{
"path": "donate/index",
"style": {
"style": {
"navigationBarTitleText": "校友捐赠"
}
},{
"path": "webview/index",
"style": {
"style": {
"navigationBarTitleText": "苏州科技商学院"
}
},{
"path": "avatarUpload/index",
"style": {
"style": {
"navigationBarTitleText": "更换头像"
}
},{
@ -140,7 +140,42 @@
},{
"path": "sign/index",
"style": {
"navigationBarTitleText": "课程签到"
"navigationBarTitleText": "课程签到"
}
},{
"path": "library/index",
"style": {
"navigationBarTitleText": "图书馆"
}
},{
"path": "library/detail",
"style": {
"navigationBarTitleText": "图书详情"
}
},{
"path": "supply/index",
"style": {
"navigationBarTitleText": "供需发布"
}
},{
"path": "supply/publish",
"style": {
"navigationBarTitleText": "发布供需"
}
},{
"path": "supply/detail",
"style": {
"navigationBarTitleText": "供需详情"
}
},{
"path": "chat/index",
"style": {
"navigationBarTitleText": "校友私信"
}
},{
"path": "chat/chatWindow",
"style": {
"navigationBarTitleText": "校友私信"
}
}]
}],

@ -1,14 +1,37 @@
<template>
<view class="container">
<image class="cbg" :src="base.imgHost('book-bg.png')"></image>
<view class="schoolmate btn">
<image mode="widthFix" @click="toUrl(2)" :src="base.imgHost('book-schoolmate1.png')"></image>
<!-- <view>加入我们</view> -->
<image class="cbg" :src="base.imgHost('alumni-benefits-bg.png')"></image>
<image class="profile-icon" src="/static/index_icon1-4.png" @click="goToProfile"></image>
<view class="button-grid">
<view class="grid-item" @click="handleButtonClick('alumni')">
<image class="item-bg" :src="base.imgHost('alumni-benefits-item2.png')"></image>
<view class="item-content">
<image class="icon" :src="base.imgHost('alumni-benefits-icon1.png')" mode="aspectFit"></image>
<text class="label-1">校友库</text>
</view>
</view>
<view class="grid-item" @click="handleButtonClick('booking')">
<image class="item-bg" :src="base.imgHost('alumni-benefits-item1.png')"></image>
<view class="item-content">
<image class="icon" :src="base.imgHost('alumni-benefits-icon2.png')" mode="aspectFit"></image>
<text class="label-2">场地预约</text>
</view>
</view>
<view class="grid-item" @click="handleButtonClick('supply-demand')">
<image class="item-bg" :src="base.imgHost('alumni-benefits-item1.png')"></image>
<view class="item-content">
<image class="icon" :src="base.imgHost('alumni-benefits-icon3.png')" mode="aspectFit"></image>
<text class="label-2">供需发布</text>
</view>
</view>
<view class="grid-item" @click="handleButtonClick('library')">
<image class="item-bg" :src="base.imgHost('alumni-benefits-item2.png')"></image>
<view class="item-content">
<image class="icon" :src="base.imgHost('alumni-benefits-icon4.png')" mode="aspectFit"></image>
<text class="label-1">图书馆查询</text>
</view>
</view>
</view>
<view class="book btn">
<image mode="widthFix" @click="toUrl(1)" :src="base.imgHost('book-book1.png')"></image>
<!-- <view>立即预约</view> -->
</view>
<tabbar :currentPage="2"></tabbar>
</view>
</template>
@ -22,22 +45,20 @@
data() {
return {
user: {},
can_appointment: false,
enter_schoolmate: 0,
door_appointments: false, //
enter_schoolmate: 0
}
},
onShareAppMessage() {
return{
title:"苏州科技商学院",
imageUrl:"/static/share.jpg"
}
},
onShareTimeline() {
return{
title:"苏州科技商学院",
imageUrl:"/static/share.jpg"
}
},
onShareAppMessage() {
return{
title:"苏州科技商学院",
imageUrl:"/static/share.jpg"
}
},
onShareTimeline() {
return{
title:"苏州科技商学院",
imageUrl:"/static/share.jpg"
}
},
onShow() {
this.getUser()
@ -49,48 +70,47 @@
this.$u.api.user().then(res => {
console.log("res", res)
this.enter_schoolmate = res.enter_schoolmate
if (res.user.appointment_total - res.user.pass_appointments > 0) {
this.can_appointment = true
} else {
this.can_appointment = false
}
// this.door_appointments = res.door_appointments ? true : false
this.$u.vuex('vuex_user', res.user)
})
},
async toUrl(type) {
if (type === 1) {
uni.navigateTo({
url: '/packages/booksubmit/appointment'
})
// await this.$u.api.user().then(res => {
// if (res.user.appointment_total - res.user.pass_appointments > 0) {
// this.can_appointment = true
// uni.navigateTo({
// url: '/packages/booksubmit/appointment'
// })
// } else {
// this.can_appointment = false
// this.base.toast("")
// }
// })
} else if (type === 2) {
if (this.enter_schoolmate) {
goToProfile() {
uni.navigateTo({
url: '/packages/chat/index'
});
},
handleButtonClick(type) {
switch (type) {
case 'alumni':
if (this.enter_schoolmate) {
uni.navigateTo({
url: '/packages/schoolmate/index'
})
} else {
this.base.toast("您还不是校友,无权查看", 1000, function() {
setTimeout(function() {
uni.switchTab({
url: '/pages/course/index'
})
}, 1000)
})
}
break;
case 'booking':
uni.navigateTo({
url: '/packages/schoolmate/index'
url: '/packages/booksubmit/appointment'
})
} else {
this.base.toast("您还不是校友,无权查看",1000,function(){
setTimeout(function(){
uni.switchTab({
url: '/pages/course/index'
})
},1000)
})
}
break;
case 'supply-demand':
uni.navigateTo({
url: '/packages/supply/index'
})
break;
case 'library':
uni.navigateTo({
url: '/packages/library/index'
})
break;
}
}
},
@ -103,6 +123,10 @@
width: 100%;
height: 100vh;
padding-bottom: 200rpx;
display: flex;
justify-content: center;
align-items: center;
position: relative;
.cbg {
position: absolute;
@ -110,34 +134,72 @@
left: 0;
width: 100%;
height: 100vh;
}
.btn{
width:320rpx;
// height:136rpx;
position: absolute;
image{
width:100%;
height:100%;
}
view{
text-align: center;
margin-top:10rpx;
color:#806e5c;
font-size:30rpx;
}
}
.schoolmate{
top: 300rpx;
left: 40rpx;
}
.book{
bottom: 300rpx;
right: 40rpx;
}
z-index: -1;
}
.profile-icon {
position: absolute;
top: 80rpx;
right: 60rpx;
width: 60rpx;
height: 60rpx;
z-index: 10;
}
.button-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 20rpx;
width: 100%;
padding: 0 40rpx;
box-sizing: border-box;
}
.grid-item {
position: relative;
width: 100%;
padding-top: 127.8125%;
/* Creates a 320:409 aspect ratio */
}
.item-bg {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.item-content {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: flex-start;
padding: 60rpx 40rpx;
box-sizing: border-box;
}
.icon {
width: 90rpx;
height: 90rpx;
margin-bottom: 60rpx;
}
.label-1 {
font-size: 34rpx;
font-weight: bold;
color: #4f4a7b;
}
.label-2 {
font-size: 34rpx;
font-weight: bold;
color: #8f6e4d;
}
}
</style>
Loading…
Cancel
Save