refactor(backend): 重构动物相关 API 接口
- 更新了动物数据结构和相关类型定义 - 优化了动物列表、详情、创建、更新和删除接口 - 新增了更新动物状态接口 - 移除了与认领记录相关的接口 -调整了 API 响应结构
This commit is contained in:
@@ -34,7 +34,14 @@ const endpoints = {
|
||||
LIST: '/travel/list',
|
||||
DETAIL: '/travel/detail',
|
||||
CREATE: '/travel/create',
|
||||
UPDATE: '/travel/update',
|
||||
DELETE: '/travel/delete',
|
||||
JOIN: '/travel/join',
|
||||
QUIT: '/travel/quit',
|
||||
LIKE: '/travel/like',
|
||||
UNLIKE: '/travel/unlike',
|
||||
COMMENT: '/travel/comment',
|
||||
COMMENTS: '/travel/comments',
|
||||
MY_PLANS: '/travel/my-plans',
|
||||
SEARCH: '/travel/search'
|
||||
},
|
||||
|
||||
@@ -39,8 +39,29 @@ export const travelService = {
|
||||
// 创建旅行计划
|
||||
create: (data) => request.post(endpoints.TRAVEL.CREATE, data),
|
||||
|
||||
// 更新旅行计划
|
||||
update: (id, data) => request.put(`${endpoints.TRAVEL.UPDATE}/${id}`, data),
|
||||
|
||||
// 删除旅行计划
|
||||
deletePlan: (id) => request.delete(`${endpoints.TRAVEL.DELETE}/${id}`),
|
||||
|
||||
// 加入旅行计划
|
||||
join: (travelId) => request.post(`${endpoints.TRAVEL.JOIN}/${travelId}`),
|
||||
joinPlan: (travelId) => request.post(`${endpoints.TRAVEL.JOIN}/${travelId}`),
|
||||
|
||||
// 退出旅行计划
|
||||
quitPlan: (travelId) => request.post(`${endpoints.TRAVEL.QUIT}/${travelId}`),
|
||||
|
||||
// 点赞旅行计划
|
||||
likePlan: (travelId) => request.post(`${endpoints.TRAVEL.LIKE}/${travelId}`),
|
||||
|
||||
// 取消点赞旅行计划
|
||||
unlikePlan: (travelId) => request.post(`${endpoints.TRAVEL.UNLIKE}/${travelId}`),
|
||||
|
||||
// 添加评论
|
||||
addComment: (travelId, data) => request.post(`${endpoints.TRAVEL.COMMENT}/${travelId}`, data),
|
||||
|
||||
// 获取评论列表
|
||||
getComments: (travelId, params = {}) => request.get(`${endpoints.TRAVEL.COMMENTS}/${travelId}`, params),
|
||||
|
||||
// 获取我的旅行计划
|
||||
getMyPlans: (params = {}) => request.get(endpoints.TRAVEL.MY_PLANS, params),
|
||||
|
||||
@@ -12,19 +12,19 @@
|
||||
"build:mp-weixin": "cross-env NODE_ENV=production UNI_PLATFORM=mp-weixin vue-cli-service uni-build"
|
||||
},
|
||||
"dependencies": {
|
||||
"@dcloudio/uni-app": "^3.0.0",
|
||||
"@dcloudio/uni-components": "^3.0.0",
|
||||
"@dcloudio/uni-h5": "^3.0.0",
|
||||
"@dcloudio/uni-mp-weixin": "^3.0.0",
|
||||
"@dcloudio/uni-app": "^3.4.19",
|
||||
"@dcloudio/uni-components": "^3.4.19",
|
||||
"@dcloudio/uni-h5": "^3.4.19",
|
||||
"@dcloudio/uni-mp-weixin": "^3.4.19",
|
||||
"vue": "^3.2.0",
|
||||
"vuex": "^4.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@dcloudio/types": "^3.0.0",
|
||||
"@dcloudio/uni-cli-shared": "^3.0.0",
|
||||
"@dcloudio/uni-migration": "^3.0.0",
|
||||
"@dcloudio/uni-template-compiler": "^3.0.0",
|
||||
"@dcloudio/vue-cli-plugin-uni": "^3.0.0",
|
||||
"@dcloudio/types": "^3.4.19",
|
||||
"@dcloudio/uni-cli-shared": "^3.4.19",
|
||||
"@dcloudio/uni-migration": "^3.4.19",
|
||||
"@dcloudio/uni-template-compiler": "^3.4.19",
|
||||
"@dcloudio/vue-cli-plugin-uni": "^3.4.19",
|
||||
"@vue/cli-service": "^5.0.0",
|
||||
"cross-env": "^7.0.0",
|
||||
"sass": "^1.32.0",
|
||||
|
||||
@@ -55,6 +55,22 @@
|
||||
"navigationBarBackgroundColor": "#007aff",
|
||||
"navigationBarTextStyle": "white"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/travel/publish",
|
||||
"style": {
|
||||
"navigationBarTitleText": "发布旅行",
|
||||
"navigationBarBackgroundColor": "#007aff",
|
||||
"navigationBarTextStyle": "white"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/travel/comments",
|
||||
"style": {
|
||||
"navigationBarTitleText": "评论详情",
|
||||
"navigationBarBackgroundColor": "#007aff",
|
||||
"navigationBarTextStyle": "white"
|
||||
}
|
||||
}
|
||||
],
|
||||
"globalStyle": {
|
||||
|
||||
552
mini-program/pages/travel/comments.vue
Normal file
552
mini-program/pages/travel/comments.vue
Normal file
@@ -0,0 +1,552 @@
|
||||
<template>
|
||||
<view class="comments-page">
|
||||
<!-- 头部导航 -->
|
||||
<view class="header">
|
||||
<view class="back-btn" @click="goBack">
|
||||
<uni-icons type="arrowleft" size="24" color="#333"></uni-icons>
|
||||
</view>
|
||||
<text class="title">全部评论 ({{ totalCount }})</text>
|
||||
<view class="placeholder"></view>
|
||||
</view>
|
||||
|
||||
<!-- 评论列表 -->
|
||||
<scroll-view
|
||||
class="comments-list"
|
||||
scroll-y
|
||||
@scrolltolower="loadMore"
|
||||
:scroll-with-animation="true"
|
||||
>
|
||||
<view v-if="loading && comments.length === 0" class="loading-container">
|
||||
<uni-loading size="32" color="#007aff"></uni-loading>
|
||||
<text class="loading-text">加载中...</text>
|
||||
</view>
|
||||
|
||||
<view v-else-if="comments.length === 0" class="empty-container">
|
||||
<uni-icons type="chat" size="64" color="#ccc"></uni-icons>
|
||||
<text class="empty-text">暂无评论</text>
|
||||
</view>
|
||||
|
||||
<view v-else>
|
||||
<view
|
||||
v-for="comment in comments"
|
||||
:key="comment.id"
|
||||
class="comment-item"
|
||||
>
|
||||
<image
|
||||
:src="comment.user.avatar || '/static/avatar/default.png'"
|
||||
class="comment-avatar"
|
||||
mode="aspectFill"
|
||||
></image>
|
||||
|
||||
<view class="comment-content">
|
||||
<view class="comment-header">
|
||||
<text class="comment-user">{{ comment.user.nickname }}</text>
|
||||
<text class="comment-time">{{ formatTime(comment.createTime) }}</text>
|
||||
</view>
|
||||
|
||||
<text class="comment-text">{{ comment.content }}</text>
|
||||
|
||||
<view class="comment-actions">
|
||||
<view class="action-btn" @click="likeComment(comment)">
|
||||
<uni-icons
|
||||
:type="comment.isLiked ? 'heart-filled' : 'heart'"
|
||||
size="16"
|
||||
:color="comment.isLiked ? '#ff4757' : '#999'"
|
||||
></uni-icons>
|
||||
<text class="action-count">{{ comment.likeCount || 0 }}</text>
|
||||
</view>
|
||||
|
||||
<view class="action-btn" @click="replyComment(comment)">
|
||||
<uni-icons type="chat" size="16" color="#999"></uni-icons>
|
||||
<text class="action-count">回复</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 回复列表 -->
|
||||
<view v-if="comment.replies && comment.replies.length > 0" class="replies-container">
|
||||
<view
|
||||
v-for="reply in comment.replies"
|
||||
:key="reply.id"
|
||||
class="reply-item"
|
||||
>
|
||||
<text class="reply-user">{{ reply.user.nickname }}</text>
|
||||
<text class="reply-text">: {{ reply.content }}</text>
|
||||
<text class="reply-time">{{ formatTime(reply.createTime) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 加载更多 -->
|
||||
<view v-if="hasMore" class="load-more">
|
||||
<uni-loading size="24" color="#999" v-if="loadingMore"></uni-loading>
|
||||
<text class="load-more-text" v-else>上拉加载更多</text>
|
||||
</view>
|
||||
|
||||
<view v-else class="no-more">
|
||||
<text>没有更多评论了</text>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<!-- 底部输入框 -->
|
||||
<view class="comment-input-container">
|
||||
<input
|
||||
v-model="newComment"
|
||||
class="comment-input"
|
||||
placeholder="写下你的评论..."
|
||||
:focus="isFocus"
|
||||
@focus="onInputFocus"
|
||||
@blur="onInputBlur"
|
||||
@confirm="submitComment"
|
||||
/>
|
||||
<view
|
||||
class="send-btn"
|
||||
:class="{ active: newComment.trim() }"
|
||||
@click="submitComment"
|
||||
>
|
||||
<text>发送</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 回复面板 -->
|
||||
<view
|
||||
v-if="showReplyPanel"
|
||||
class="reply-panel"
|
||||
:style="{ bottom: inputHeight + 'px' }"
|
||||
>
|
||||
<text class="reply-hint">回复 @{{ replyTarget.user.nickname }}</text>
|
||||
<view class="close-reply" @click="cancelReply">
|
||||
<uni-icons type="close" size="20" color="#999"></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import travelService from '@/api/services'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
travelId: null,
|
||||
comments: [],
|
||||
totalCount: 0,
|
||||
loading: false,
|
||||
loadingMore: false,
|
||||
hasMore: true,
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
|
||||
newComment: '',
|
||||
isFocus: false,
|
||||
inputHeight: 0,
|
||||
|
||||
showReplyPanel: false,
|
||||
replyTarget: null
|
||||
}
|
||||
},
|
||||
|
||||
onLoad(options) {
|
||||
this.travelId = options.id
|
||||
this.loadComments()
|
||||
},
|
||||
|
||||
onReady() {
|
||||
this.getInputHeight()
|
||||
},
|
||||
|
||||
methods: {
|
||||
async loadComments() {
|
||||
if (this.loading) return
|
||||
|
||||
this.loading = true
|
||||
try {
|
||||
const params = {
|
||||
page: this.page,
|
||||
pageSize: this.pageSize
|
||||
}
|
||||
|
||||
const res = await travelService.getComments(this.travelId, params)
|
||||
const { list, total } = res.data
|
||||
|
||||
if (this.page === 1) {
|
||||
this.comments = list
|
||||
} else {
|
||||
this.comments = [...this.comments, ...list]
|
||||
}
|
||||
|
||||
this.totalCount = total
|
||||
this.hasMore = this.comments.length < total
|
||||
|
||||
} catch (error) {
|
||||
uni.showToast({
|
||||
title: '加载评论失败',
|
||||
icon: 'none'
|
||||
})
|
||||
} finally {
|
||||
this.loading = false
|
||||
this.loadingMore = false
|
||||
}
|
||||
},
|
||||
|
||||
async loadMore() {
|
||||
if (this.loadingMore || !this.hasMore) return
|
||||
|
||||
this.loadingMore = true
|
||||
this.page += 1
|
||||
await this.loadComments()
|
||||
},
|
||||
|
||||
async submitComment() {
|
||||
if (!this.newComment.trim()) {
|
||||
uni.showToast({
|
||||
title: '评论内容不能为空',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
let commentData = { content: this.newComment.trim() }
|
||||
|
||||
// 如果是回复
|
||||
if (this.showReplyPanel && this.replyTarget) {
|
||||
commentData.replyTo = this.replyTarget.id
|
||||
commentData.parentId = this.replyTarget.parentId || this.replyTarget.id
|
||||
}
|
||||
|
||||
await travelService.addComment(this.travelId, commentData)
|
||||
|
||||
uni.showToast({
|
||||
title: '评论成功',
|
||||
icon: 'success'
|
||||
})
|
||||
|
||||
this.newComment = ''
|
||||
this.cancelReply()
|
||||
this.isFocus = false
|
||||
|
||||
// 重新加载评论
|
||||
this.page = 1
|
||||
this.loadComments()
|
||||
|
||||
} catch (error) {
|
||||
uni.showToast({
|
||||
title: error.message || '评论失败',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
async likeComment(comment) {
|
||||
try {
|
||||
// 这里需要实现点赞评论的API
|
||||
// await commentService.likeComment(comment.id)
|
||||
|
||||
comment.isLiked = !comment.isLiked
|
||||
if (comment.isLiked) {
|
||||
comment.likeCount = (comment.likeCount || 0) + 1
|
||||
} else {
|
||||
comment.likeCount = Math.max(0, (comment.likeCount || 1) - 1)
|
||||
}
|
||||
|
||||
this.$forceUpdate()
|
||||
|
||||
} catch (error) {
|
||||
uni.showToast({
|
||||
title: '操作失败',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
replyComment(comment) {
|
||||
this.replyTarget = comment
|
||||
this.showReplyPanel = true
|
||||
this.isFocus = true
|
||||
},
|
||||
|
||||
cancelReply() {
|
||||
this.replyTarget = null
|
||||
this.showReplyPanel = false
|
||||
},
|
||||
|
||||
onInputFocus() {
|
||||
this.isFocus = true
|
||||
},
|
||||
|
||||
onInputBlur() {
|
||||
this.isFocus = false
|
||||
},
|
||||
|
||||
getInputHeight() {
|
||||
const query = uni.createSelectorQuery().in(this)
|
||||
query.select('.comment-input-container').boundingClientRect(data => {
|
||||
if (data) {
|
||||
this.inputHeight = data.height
|
||||
}
|
||||
}).exec()
|
||||
},
|
||||
|
||||
formatTime(time) {
|
||||
if (!time) return ''
|
||||
|
||||
const now = new Date()
|
||||
const commentTime = new Date(time)
|
||||
const diff = now - commentTime
|
||||
|
||||
const minute = 60 * 1000
|
||||
const hour = 60 * minute
|
||||
const day = 24 * hour
|
||||
|
||||
if (diff < minute) {
|
||||
return '刚刚'
|
||||
} else if (diff < hour) {
|
||||
return Math.floor(diff / minute) + '分钟前'
|
||||
} else if (diff < day) {
|
||||
return Math.floor(diff / hour) + '小时前'
|
||||
} else if (diff < 7 * day) {
|
||||
return Math.floor(diff / day) + '天前'
|
||||
} else {
|
||||
return commentTime.toLocaleDateString()
|
||||
}
|
||||
},
|
||||
|
||||
goBack() {
|
||||
uni.navigateBack()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.comments-page {
|
||||
height: 100vh;
|
||||
background: #f8f8f8;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* 头部导航 */
|
||||
.header {
|
||||
height: 88rpx;
|
||||
background: #fff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 32rpx;
|
||||
border-bottom: 1rpx solid #eee;
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.back-btn {
|
||||
padding: 16rpx;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 36rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.placeholder {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
}
|
||||
|
||||
/* 评论列表 */
|
||||
.comments-list {
|
||||
flex: 1;
|
||||
background: #fff;
|
||||
padding: 0 32rpx;
|
||||
}
|
||||
|
||||
.loading-container,
|
||||
.empty-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 100rpx 0;
|
||||
}
|
||||
|
||||
.loading-text,
|
||||
.empty-text {
|
||||
font-size: 28rpx;
|
||||
color: #999;
|
||||
margin-top: 20rpx;
|
||||
}
|
||||
|
||||
.comment-item {
|
||||
display: flex;
|
||||
padding: 32rpx 0;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
}
|
||||
|
||||
.comment-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.comment-avatar {
|
||||
width: 64rpx;
|
||||
height: 64rpx;
|
||||
border-radius: 50%;
|
||||
margin-right: 20rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.comment-content {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.comment-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.comment-user {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin-right: 16rpx;
|
||||
}
|
||||
|
||||
.comment-time {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.comment-text {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
line-height: 1.5;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.comment-actions {
|
||||
display: flex;
|
||||
gap: 32rpx;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 8rpx 16rpx;
|
||||
border-radius: 24rpx;
|
||||
background: #f8f8f8;
|
||||
}
|
||||
|
||||
.action-count {
|
||||
font-size: 24rpx;
|
||||
color: #666;
|
||||
margin-left: 8rpx;
|
||||
}
|
||||
|
||||
/* 回复列表 */
|
||||
.replies-container {
|
||||
margin-top: 20rpx;
|
||||
padding: 20rpx;
|
||||
background: #f8f8f8;
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
|
||||
.reply-item {
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.reply-item:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.reply-user {
|
||||
font-size: 26rpx;
|
||||
font-weight: 600;
|
||||
color: #007aff;
|
||||
margin-right: 8rpx;
|
||||
}
|
||||
|
||||
.reply-text {
|
||||
font-size: 26rpx;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.reply-time {
|
||||
font-size: 22rpx;
|
||||
color: #999;
|
||||
margin-left: 16rpx;
|
||||
}
|
||||
|
||||
/* 加载更多 */
|
||||
.load-more,
|
||||
.no-more {
|
||||
text-align: center;
|
||||
padding: 32rpx 0;
|
||||
color: #999;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.load-more-text {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
/* 底部输入框 */
|
||||
.comment-input-container {
|
||||
background: #fff;
|
||||
padding: 20rpx 32rpx;
|
||||
border-top: 1rpx solid #eee;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.comment-input {
|
||||
flex: 1;
|
||||
height: 80rpx;
|
||||
background: #f8f8f8;
|
||||
border-radius: 40rpx;
|
||||
padding: 0 32rpx;
|
||||
font-size: 28rpx;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
|
||||
.send-btn {
|
||||
background: #ccc;
|
||||
color: #fff;
|
||||
padding: 16rpx 32rpx;
|
||||
border-radius: 32rpx;
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
transition: background 0.3s;
|
||||
}
|
||||
|
||||
.send-btn.active {
|
||||
background: #007aff;
|
||||
}
|
||||
|
||||
/* 回复面板 */
|
||||
.reply-panel {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: #fff;
|
||||
padding: 16rpx 32rpx;
|
||||
border-top: 1rpx solid #eee;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
z-index: 90;
|
||||
}
|
||||
|
||||
.reply-hint {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.close-reply {
|
||||
padding: 8rpx;
|
||||
}
|
||||
</style>
|
||||
@@ -1,96 +1,410 @@
|
||||
<template>
|
||||
<view class="container">
|
||||
<!-- 封面图 -->
|
||||
<image :src="plan.coverImage" mode="widthFix" class="cover-image"></image>
|
||||
|
||||
<!-- 计划标题和基本信息 -->
|
||||
<view class="plan-header">
|
||||
<text class="title">{{ plan.destination }}</text>
|
||||
<view class="meta">
|
||||
<text class="date">{{ plan.startDate }} - {{ plan.endDate }}</text>
|
||||
<text class="budget">预算: ¥{{ plan.budget }}</text>
|
||||
<view class="travel-detail-page">
|
||||
<!-- 头部导航 -->
|
||||
<view class="header">
|
||||
<view class="back-btn" @click="goBack">
|
||||
<uni-icons type="arrowleft" size="24" color="#333"></uni-icons>
|
||||
</view>
|
||||
<text class="title">旅行详情</text>
|
||||
<view class="more-btn" @click="showActionSheet">
|
||||
<uni-icons type="more" size="24" color="#333"></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 行程详情 -->
|
||||
<view class="section">
|
||||
<text class="section-title">行程安排</text>
|
||||
<view class="schedule" v-for="(day, index) in plan.schedule" :key="index">
|
||||
<text class="day">第{{ index + 1 }}天</text>
|
||||
<text class="content">{{ day }}</text>
|
||||
<!-- 内容区域 -->
|
||||
<scroll-view class="content" scroll-y>
|
||||
<!-- 封面图片 -->
|
||||
<view class="cover-section">
|
||||
<image :src="plan.coverImage" class="cover-image" mode="aspectFill"></image>
|
||||
<view class="cover-overlay">
|
||||
<text class="travel-title">{{ plan.title || plan.destination }}</text>
|
||||
<text class="travel-destination">{{ plan.destination }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 基本信息 -->
|
||||
<view class="info-section">
|
||||
<view class="info-item">
|
||||
<uni-icons type="calendar" size="20" color="#007aff"></uni-icons>
|
||||
<text class="info-text">{{ plan.startDate }} - {{ plan.endDate }}</text>
|
||||
</view>
|
||||
|
||||
<view class="info-item">
|
||||
<uni-icons type="wallet" size="20" color="#007aff"></uni-icons>
|
||||
<text class="info-text">预算: ¥{{ plan.budget }}</text>
|
||||
</view>
|
||||
|
||||
<view class="info-item">
|
||||
<uni-icons type="person" size="20" color="#007aff"></uni-icons>
|
||||
<text class="info-text">{{ plan.currentMembers || 0 }}/{{ plan.maxMembers || 4 }}人</text>
|
||||
</view>
|
||||
|
||||
<view class="info-item">
|
||||
<uni-icons type="flag" size="20" color="#007aff"></uni-icons>
|
||||
<text class="info-text">{{ getStatusText(plan.status) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 发布者信息 -->
|
||||
<view class="publisher-section">
|
||||
<view class="publisher-header">
|
||||
<image :src="plan.publisher?.avatar || '/static/avatar/default.png'" class="avatar" mode="aspectFill"></image>
|
||||
<view class="publisher-info">
|
||||
<text class="nickname">{{ plan.publisher?.nickname || '匿名用户' }}</text>
|
||||
<text class="level">Lv.{{ plan.publisher?.level || 1 }}</text>
|
||||
</view>
|
||||
<view class="rating">
|
||||
<uni-rate :value="plan.publisher?.rating || 5" size="14" readonly></uni-rate>
|
||||
<text class="rating-text">{{ plan.publisher?.rating || 5.0 }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 行程详情 -->
|
||||
<view class="description-section">
|
||||
<text class="section-title">行程安排</text>
|
||||
<view class="schedule" v-for="(day, index) in plan.schedule" :key="index">
|
||||
<text class="day">第{{ index + 1 }}天</text>
|
||||
<text class="content">{{ day }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 同行要求 -->
|
||||
<view class="description-section">
|
||||
<text class="section-title">同行要求</text>
|
||||
<text class="description">{{ plan.requirements || '无特殊要求' }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 兴趣标签 -->
|
||||
<view class="tags-section" v-if="plan.tags && plan.tags.length > 0">
|
||||
<text class="section-title">兴趣标签</text>
|
||||
<view class="tags-container">
|
||||
<view v-for="tag in plan.tags" :key="tag" class="tag">
|
||||
<text>{{ tag }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 参与者列表 -->
|
||||
<view class="members-section" v-if="plan.members && plan.members.length > 0">
|
||||
<text class="section-title">参与者 ({{ plan.members.length }})</text>
|
||||
<scroll-view class="members-list" scroll-x>
|
||||
<view v-for="member in plan.members" :key="member.id" class="member-item">
|
||||
<image :src="member.avatar || '/static/avatar/default.png'" class="member-avatar" mode="aspectFill"></image>
|
||||
<text class="member-name">{{ member.nickname }}</text>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
|
||||
<!-- 评论区域 -->
|
||||
<view class="comments-section">
|
||||
<text class="section-title">评论 ({{ plan.commentCount || 0 }})</text>
|
||||
<view class="comment-list">
|
||||
<view v-for="comment in plan.comments" :key="comment.id" class="comment-item">
|
||||
<image :src="comment.user.avatar || '/static/avatar/default.png'" class="comment-avatar" mode="aspectFill"></image>
|
||||
<view class="comment-content">
|
||||
<text class="comment-user">{{ comment.user.nickname }}</text>
|
||||
<text class="comment-text">{{ comment.content }}</text>
|
||||
<text class="comment-time">{{ comment.createTime }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="view-all-comments" @click="viewAllComments" v-if="plan.commentCount > 2">
|
||||
<text>查看全部{{ plan.commentCount }}条评论</text>
|
||||
<uni-icons type="arrowright" size="16" color="#999"></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<!-- 底部操作栏 -->
|
||||
<view class="footer">
|
||||
<view class="action-btn" @click="navigateToChat">
|
||||
<uni-icons type="chat" size="20" color="#666"></uni-icons>
|
||||
<text>私信</text>
|
||||
</view>
|
||||
|
||||
<view class="action-btn" @click="toggleLike" :class="{ liked: plan.isLiked }">
|
||||
<uni-icons :type="plan.isLiked ? 'heart-filled' : 'heart'" size="20" :color="plan.isLiked ? '#ff4757' : '#666'"></uni-icons>
|
||||
<text>{{ plan.likeCount || 0 }}</text>
|
||||
</view>
|
||||
|
||||
<view class="action-btn" @click="focusCommentInput">
|
||||
<uni-icons type="chatboxes" size="20" color="#666"></uni-icons>
|
||||
<text>评论</text>
|
||||
</view>
|
||||
|
||||
<view class="join-btn" @click="handleJoin" :class="{ joined: plan.isJoined }">
|
||||
<text>{{ plan.isJoined ? '已加入' : '立即加入' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 同行要求 -->
|
||||
<view class="section">
|
||||
<text class="section-title">同行要求</text>
|
||||
<text class="requirements">{{ plan.requirements }}</text>
|
||||
<!-- 评论输入框 -->
|
||||
<view class="comment-input-container" v-if="showCommentInput">
|
||||
<input
|
||||
v-model="commentText"
|
||||
class="comment-input"
|
||||
placeholder="说点什么..."
|
||||
@confirm="submitComment"
|
||||
/>
|
||||
<view class="send-btn" @click="submitComment">
|
||||
<text>发送</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<view class="action-bar">
|
||||
<button class="btn join" @click="handleJoin">加入计划</button>
|
||||
<button class="btn contact" @click="handleContact">联系发起人</button>
|
||||
<!-- 操作菜单 -->
|
||||
<uni-popup ref="actionSheet" type="bottom">
|
||||
<view class="action-sheet">
|
||||
<view class="action-item" @click="shareTravel">
|
||||
<uni-icons type="share" size="20" color="#333"></uni-icons>
|
||||
<text>分享</text>
|
||||
</view>
|
||||
<view class="action-item" @click="reportTravel" v-if="!isPublisher">
|
||||
<uni-icons type="flag" size="20" color="#333"></uni-icons>
|
||||
<text>举报</text>
|
||||
</view>
|
||||
<view class="action-item" @click="editTravel" v-if="isPublisher">
|
||||
<uni-icons type="compose" size="20" color="#333"></uni-icons>
|
||||
<text>编辑</text>
|
||||
</view>
|
||||
<view class="action-item" @click="deleteTravel" v-if="isPublisher">
|
||||
<uni-icons type="trash" size="20" color="#ff4757"></uni-icons>
|
||||
<text style="color: #ff4757;">删除</text>
|
||||
</view>
|
||||
<view class="cancel-btn" @click="closeActionSheet">
|
||||
<text>取消</text>
|
||||
</view>
|
||||
</view>
|
||||
</uni-popup>
|
||||
|
||||
<!-- 加载中 -->
|
||||
<view v-if="loading" class="loading-mask">
|
||||
<uni-loading size="32" color="#007aff"></uni-loading>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { travelService } from '../../api/services.js'
|
||||
import travelService from '@/api/services'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
plan: {
|
||||
id: null,
|
||||
title: '',
|
||||
destination: '',
|
||||
coverImage: '',
|
||||
startDate: '',
|
||||
endDate: '',
|
||||
budget: 0,
|
||||
coverImage: '',
|
||||
schedule: [],
|
||||
requirements: ''
|
||||
}
|
||||
requirements: '',
|
||||
tags: [],
|
||||
status: 1,
|
||||
currentMembers: 0,
|
||||
maxMembers: 4,
|
||||
publisher: {},
|
||||
members: [],
|
||||
comments: [],
|
||||
commentCount: 0,
|
||||
likeCount: 0,
|
||||
isLiked: false,
|
||||
isJoined: false
|
||||
},
|
||||
planId: null,
|
||||
loading: false,
|
||||
showCommentInput: false,
|
||||
commentText: '',
|
||||
isPublisher: false
|
||||
}
|
||||
},
|
||||
|
||||
onLoad(options) {
|
||||
const id = options.id
|
||||
if (id) {
|
||||
this.loadTravelDetail(id)
|
||||
}
|
||||
this.planId = options.id
|
||||
this.loadPlanDetail()
|
||||
},
|
||||
|
||||
methods: {
|
||||
async loadTravelDetail(id) {
|
||||
async loadPlanDetail() {
|
||||
this.loading = true
|
||||
try {
|
||||
const data = await travelService.getDetail(id)
|
||||
this.plan = data
|
||||
const res = await travelService.getDetail(this.planId)
|
||||
this.plan = { ...this.plan, ...res.data }
|
||||
this.checkUserStatus()
|
||||
} catch (error) {
|
||||
console.error('获取旅行计划详情失败:', error)
|
||||
uni.showToast({
|
||||
title: '获取数据失败',
|
||||
title: '加载失败',
|
||||
icon: 'none'
|
||||
})
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
|
||||
checkUserStatus() {
|
||||
// 检查当前用户是否是发布者或已加入
|
||||
const userInfo = uni.getStorageSync('userInfo')
|
||||
if (userInfo && userInfo.id) {
|
||||
this.isPublisher = this.plan.publisher?.id === userInfo.id
|
||||
this.plan.isJoined = this.plan.members?.some(member => member.id === userInfo.id)
|
||||
}
|
||||
},
|
||||
|
||||
goBack() {
|
||||
uni.navigateBack()
|
||||
},
|
||||
|
||||
showActionSheet() {
|
||||
this.$refs.actionSheet.open()
|
||||
},
|
||||
|
||||
closeActionSheet() {
|
||||
this.$refs.actionSheet.close()
|
||||
},
|
||||
|
||||
async handleJoin() {
|
||||
if (this.plan.isJoined) {
|
||||
uni.showToast({
|
||||
title: '您已加入该计划',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if (this.plan.currentMembers >= this.plan.maxMembers) {
|
||||
uni.showToast({
|
||||
title: '该计划人数已满',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
await travelService.joinPlan(this.planId)
|
||||
uni.showToast({
|
||||
title: '加入成功',
|
||||
icon: 'success'
|
||||
})
|
||||
this.plan.isJoined = true
|
||||
this.plan.currentMembers += 1
|
||||
} catch (error) {
|
||||
uni.showToast({
|
||||
title: error.message || '加入失败',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
async handleJoin() {
|
||||
navigateToChat() {
|
||||
uni.navigateTo({
|
||||
url: `/pages/chat/chat?id=${this.plan.publisher?.id}&name=${this.plan.publisher?.nickname}`
|
||||
})
|
||||
},
|
||||
|
||||
async toggleLike() {
|
||||
try {
|
||||
if (this.plan.isLiked) {
|
||||
await travelService.unlikePlan(this.planId)
|
||||
this.plan.likeCount -= 1
|
||||
} else {
|
||||
await travelService.likePlan(this.planId)
|
||||
this.plan.likeCount += 1
|
||||
}
|
||||
this.plan.isLiked = !this.plan.isLiked
|
||||
} catch (error) {
|
||||
uni.showToast({
|
||||
title: error.message || '操作失败',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
focusCommentInput() {
|
||||
this.showCommentInput = true
|
||||
},
|
||||
|
||||
async submitComment() {
|
||||
if (!this.commentText.trim()) {
|
||||
uni.showToast({
|
||||
title: '评论内容不能为空',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
await travelService.addComment(this.planId, {
|
||||
content: this.commentText.trim()
|
||||
})
|
||||
|
||||
uni.showToast({
|
||||
title: '评论成功',
|
||||
icon: 'success'
|
||||
})
|
||||
|
||||
this.commentText = ''
|
||||
this.showCommentInput = false
|
||||
this.loadPlanDetail()
|
||||
} catch (error) {
|
||||
uni.showToast({
|
||||
title: error.message || '评论失败',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
viewAllComments() {
|
||||
uni.navigateTo({
|
||||
url: `/pages/travel/comments?id=${this.planId}`
|
||||
})
|
||||
},
|
||||
|
||||
shareTravel() {
|
||||
uni.share({
|
||||
provider: 'weixin',
|
||||
scene: 'WXSceneSession',
|
||||
type: 0,
|
||||
href: `https://jiebanke.com/travel/${this.planId}`,
|
||||
title: this.plan.title || this.plan.destination,
|
||||
summary: this.plan.requirements || '一起来结伴旅行吧!',
|
||||
imageUrl: this.plan.coverImage,
|
||||
success: () => {
|
||||
uni.showToast({
|
||||
title: '分享成功',
|
||||
icon: 'success'
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
reportTravel() {
|
||||
uni.navigateTo({
|
||||
url: `/pages/report/report?type=travel&id=${this.planId}`
|
||||
})
|
||||
},
|
||||
|
||||
editTravel() {
|
||||
uni.navigateTo({
|
||||
url: `/pages/travel/publish?id=${this.planId}`
|
||||
})
|
||||
},
|
||||
|
||||
async deleteTravel() {
|
||||
uni.showModal({
|
||||
title: '确认加入',
|
||||
content: '确定要加入这个旅行计划吗?',
|
||||
title: '确认删除',
|
||||
content: '确定要删除这个旅行计划吗?此操作不可恢复。',
|
||||
success: async (res) => {
|
||||
if (res.confirm) {
|
||||
try {
|
||||
await travelService.join(this.plan.id)
|
||||
await travelService.deletePlan(this.planId)
|
||||
uni.showToast({
|
||||
title: '申请已提交',
|
||||
title: '删除成功',
|
||||
icon: 'success'
|
||||
})
|
||||
setTimeout(() => {
|
||||
uni.navigateBack()
|
||||
}, 1500)
|
||||
} catch (error) {
|
||||
console.error('加入计划失败:', error)
|
||||
uni.showToast({
|
||||
title: '申请失败',
|
||||
title: error.message || '删除失败',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
@@ -99,100 +413,443 @@ export default {
|
||||
})
|
||||
},
|
||||
|
||||
handleContact() {
|
||||
uni.makePhoneCall({
|
||||
phoneNumber: '13800138000'
|
||||
})
|
||||
getStatusText(status) {
|
||||
const statusMap = {
|
||||
1: '招募中',
|
||||
2: '进行中',
|
||||
3: '已结束',
|
||||
4: '已取消'
|
||||
}
|
||||
return statusMap[status] || '未知状态'
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.container {
|
||||
padding-bottom: 100rpx;
|
||||
.travel-detail-page {
|
||||
height: 100vh;
|
||||
background: #f8f8f8;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.cover-image {
|
||||
width: 100%;
|
||||
/* 头部导航 */
|
||||
.header {
|
||||
height: 88rpx;
|
||||
background: #fff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 32rpx;
|
||||
border-bottom: 1rpx solid #eee;
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.plan-header {
|
||||
padding: 30rpx;
|
||||
.back-btn,
|
||||
.more-btn {
|
||||
padding: 16rpx;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 36rpx;
|
||||
font-weight: bold;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.meta {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-top: 20rpx;
|
||||
color: #666;
|
||||
/* 内容区域 */
|
||||
.content {
|
||||
flex: 1;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.section {
|
||||
padding: 30rpx;
|
||||
/* 封面区域 */
|
||||
.cover-section {
|
||||
position: relative;
|
||||
height: 400rpx;
|
||||
}
|
||||
|
||||
.cover-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.cover-overlay {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: linear-gradient(transparent, rgba(0, 0, 0, 0.7));
|
||||
padding: 32rpx;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.travel-title {
|
||||
font-size: 40rpx;
|
||||
font-weight: bold;
|
||||
display: block;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.travel-destination {
|
||||
font-size: 28rpx;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
/* 信息区域 */
|
||||
.info-section {
|
||||
padding: 32rpx;
|
||||
background: #fff;
|
||||
margin-bottom: 20rpx;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
}
|
||||
|
||||
.info-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.info-item:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.info-text {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
margin-left: 16rpx;
|
||||
}
|
||||
|
||||
/* 发布者信息 */
|
||||
.publisher-section {
|
||||
padding: 32rpx;
|
||||
background: #fff;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
}
|
||||
|
||||
.publisher-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
border-radius: 50%;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
|
||||
.publisher-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.nickname {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
display: block;
|
||||
margin-bottom: 4rpx;
|
||||
}
|
||||
|
||||
.level {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.rating {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.rating-text {
|
||||
font-size: 24rpx;
|
||||
color: #ff9500;
|
||||
margin-left: 8rpx;
|
||||
}
|
||||
|
||||
/* 描述区域 */
|
||||
.description-section {
|
||||
padding: 32rpx;
|
||||
background: #fff;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin-bottom: 20rpx;
|
||||
margin-bottom: 24rpx;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.schedule {
|
||||
margin-bottom: 20rpx;
|
||||
padding-left: 20rpx;
|
||||
border-left: 4rpx solid #007aff;
|
||||
}
|
||||
|
||||
.day {
|
||||
font-weight: bold;
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #007aff;
|
||||
margin-bottom: 10rpx;
|
||||
display: block;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.content {
|
||||
color: #666;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.requirements {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.action-bar {
|
||||
.description {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
/* 标签区域 */
|
||||
.tags-section {
|
||||
padding: 32rpx;
|
||||
background: #fff;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
}
|
||||
|
||||
.tags-container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.tag {
|
||||
background: #f0f6ff;
|
||||
padding: 8rpx 20rpx;
|
||||
border-radius: 24rpx;
|
||||
font-size: 24rpx;
|
||||
color: #007aff;
|
||||
}
|
||||
|
||||
/* 参与者区域 */
|
||||
.members-section {
|
||||
padding: 32rpx;
|
||||
background: #fff;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
}
|
||||
|
||||
.members-list {
|
||||
display: flex;
|
||||
gap: 24rpx;
|
||||
padding: 16rpx 0;
|
||||
}
|
||||
|
||||
.member-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
min-width: 100rpx;
|
||||
}
|
||||
|
||||
.member-avatar {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
border-radius: 50%;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.member-name {
|
||||
font-size: 24rpx;
|
||||
color: #666;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* 评论区域 */
|
||||
.comments-section {
|
||||
padding: 32rpx;
|
||||
background: #fff;
|
||||
margin-bottom: 120rpx;
|
||||
}
|
||||
|
||||
.comment-list {
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.comment-item {
|
||||
display: flex;
|
||||
margin-bottom: 32rpx;
|
||||
}
|
||||
|
||||
.comment-avatar {
|
||||
width: 64rpx;
|
||||
height: 64rpx;
|
||||
border-radius: 50%;
|
||||
margin-right: 20rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.comment-content {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.comment-user {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
display: block;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.comment-text {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
line-height: 1.5;
|
||||
display: block;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.comment-time {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.view-all-comments {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 24rpx;
|
||||
color: #999;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
/* 底部操作栏 */
|
||||
.footer {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 100rpx;
|
||||
background: #fff;
|
||||
display: flex;
|
||||
border-top: 1rpx solid #eee;
|
||||
padding: 20rpx 32rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.btn {
|
||||
.action-btn {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin-right: 40rpx;
|
||||
padding: 16rpx;
|
||||
}
|
||||
|
||||
.action-btn.liked {
|
||||
color: #ff4757;
|
||||
}
|
||||
|
||||
.action-btn text {
|
||||
font-size: 20rpx;
|
||||
color: #666;
|
||||
margin-top: 4rpx;
|
||||
}
|
||||
|
||||
.action-btn.liked text {
|
||||
color: #ff4757;
|
||||
}
|
||||
|
||||
.join-btn {
|
||||
flex: 1;
|
||||
border: none;
|
||||
font-size: 32rpx;
|
||||
}
|
||||
|
||||
.join {
|
||||
background: #007aff;
|
||||
color: #fff;
|
||||
height: 80rpx;
|
||||
border-radius: 40rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.contact {
|
||||
.join-btn.joined {
|
||||
background: #34c759;
|
||||
}
|
||||
|
||||
/* 评论输入框 */
|
||||
.comment-input-container {
|
||||
position: fixed;
|
||||
bottom: 120rpx;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: #fff;
|
||||
padding: 20rpx 32rpx;
|
||||
border-top: 1rpx solid #eee;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.comment-input {
|
||||
flex: 1;
|
||||
height: 80rpx;
|
||||
background: #f8f8f8;
|
||||
border-radius: 40rpx;
|
||||
padding: 0 32rpx;
|
||||
font-size: 28rpx;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
|
||||
.send-btn {
|
||||
background: #007aff;
|
||||
color: #fff;
|
||||
padding: 16rpx 32rpx;
|
||||
border-radius: 32rpx;
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* 操作菜单 */
|
||||
.action-sheet {
|
||||
background: #fff;
|
||||
border-radius: 24rpx 24rpx 0 0;
|
||||
padding: 32rpx 0;
|
||||
}
|
||||
|
||||
.action-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 32rpx;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
}
|
||||
|
||||
.action-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.action-item text {
|
||||
font-size: 32rpx;
|
||||
margin-left: 20rpx;
|
||||
}
|
||||
|
||||
.cancel-btn {
|
||||
margin-top: 16rpx;
|
||||
padding: 32rpx;
|
||||
text-align: center;
|
||||
border-top: 1rpx solid #f0f0f0;
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* 加载中 */
|
||||
.loading-mask {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 1000;
|
||||
}
|
||||
</style>
|
||||
566
mini-program/pages/travel/publish.vue
Normal file
566
mini-program/pages/travel/publish.vue
Normal file
@@ -0,0 +1,566 @@
|
||||
<template>
|
||||
<view class="publish-page">
|
||||
<!-- 头部导航 -->
|
||||
<view class="header">
|
||||
<view class="back-btn" @click="goBack">
|
||||
<uni-icons type="arrowleft" size="24" color="#333"></uni-icons>
|
||||
</view>
|
||||
<text class="title">发布旅行计划</text>
|
||||
<view class="submit-btn" @click="submitForm">发布</view>
|
||||
</view>
|
||||
|
||||
<!-- 表单内容 -->
|
||||
<scroll-view class="form-content" scroll-y>
|
||||
<!-- 封面图片 -->
|
||||
<view class="form-section">
|
||||
<text class="section-title">封面图片</text>
|
||||
<view class="upload-area" @click="chooseImage">
|
||||
<image v-if="formData.coverImage" :src="formData.coverImage" class="cover-image" mode="aspectFill"></image>
|
||||
<view v-else class="upload-placeholder">
|
||||
<uni-icons type="plus" size="32" color="#ccc"></uni-icons>
|
||||
<text class="upload-text">添加封面图片</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 基本信息 -->
|
||||
<view class="form-section">
|
||||
<text class="section-title">基本信息</text>
|
||||
|
||||
<view class="form-item">
|
||||
<text class="label">旅行标题</text>
|
||||
<input
|
||||
v-model="formData.title"
|
||||
class="input"
|
||||
placeholder="请输入旅行标题"
|
||||
maxlength="30"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<view class="form-item">
|
||||
<text class="label">目的地</text>
|
||||
<input
|
||||
v-model="formData.destination"
|
||||
class="input"
|
||||
placeholder="请输入目的地"
|
||||
@focus="showLocationPicker = true"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<view class="form-item">
|
||||
<text class="label">旅行时间</text>
|
||||
<view class="date-range">
|
||||
<view class="date-input" @click="showStartDatePicker = true">
|
||||
<text>{{ formData.startDate || '开始日期' }}</text>
|
||||
<uni-icons type="calendar" size="16" color="#999"></uni-icons>
|
||||
</view>
|
||||
<text class="date-separator">至</text>
|
||||
<view class="date-input" @click="showEndDatePicker = true">
|
||||
<text>{{ formData.endDate || '结束日期' }}</text>
|
||||
<uni-icons type="calendar" size="16" color="#999"></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="form-item">
|
||||
<text class="label">预算</text>
|
||||
<view class="budget-input">
|
||||
<text class="currency">¥</text>
|
||||
<input
|
||||
v-model="formData.budget"
|
||||
class="input"
|
||||
type="number"
|
||||
placeholder="0"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 行程详情 -->
|
||||
<view class="form-section">
|
||||
<text class="section-title">行程详情</text>
|
||||
|
||||
<view class="form-item">
|
||||
<text class="label">行程描述</text>
|
||||
<textarea
|
||||
v-model="formData.description"
|
||||
class="textarea"
|
||||
placeholder="请详细描述您的旅行计划,包括行程安排、活动内容等"
|
||||
maxlength="500"
|
||||
/>
|
||||
<text class="char-count">{{ formData.description?.length || 0 }}/500</text>
|
||||
</view>
|
||||
|
||||
<view class="form-item">
|
||||
<text class="label">人数限制</text>
|
||||
<view class="member-limit">
|
||||
<text>最多</text>
|
||||
<input
|
||||
v-model="formData.maxMembers"
|
||||
class="number-input"
|
||||
type="number"
|
||||
placeholder="2"
|
||||
/>
|
||||
<text>人</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="form-item">
|
||||
<text class="label">兴趣标签</text>
|
||||
<view class="tags-container">
|
||||
<view
|
||||
v-for="tag in interestTags"
|
||||
:key="tag"
|
||||
class="tag"
|
||||
:class="{ active: formData.tags?.includes(tag) }"
|
||||
@click="toggleTag(tag)"
|
||||
>
|
||||
<text>{{ tag }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 隐私设置 -->
|
||||
<view class="form-section">
|
||||
<text class="section-title">隐私设置</text>
|
||||
|
||||
<view class="form-item">
|
||||
<view class="privacy-setting">
|
||||
<text class="label">公开可见</text>
|
||||
<switch :checked="formData.isPublic" @change="togglePrivacy" color="#007aff" />
|
||||
</view>
|
||||
<text class="privacy-tip">{{ formData.isPublic ? '所有人都可以看到您的旅行计划' : '仅自己可见' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<!-- 日期选择器 -->
|
||||
<uni-datetime-picker
|
||||
v-if="showStartDatePicker"
|
||||
type="date"
|
||||
:value="formData.startDate"
|
||||
@maskClick="showStartDatePicker = false"
|
||||
@change="onStartDateChange"
|
||||
/>
|
||||
|
||||
<uni-datetime-picker
|
||||
v-if="showEndDatePicker"
|
||||
type="date"
|
||||
:value="formData.endDate"
|
||||
@maskClick="showEndDatePicker = false"
|
||||
@change="onEndDateChange"
|
||||
/>
|
||||
|
||||
<!-- 加载中遮罩 -->
|
||||
<view v-if="loading" class="loading-mask">
|
||||
<uni-loading size="32" color="#007aff"></uni-loading>
|
||||
<text>发布中...</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { travelService } from '../../api/services.js'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
formData: {
|
||||
title: '',
|
||||
destination: '',
|
||||
startDate: '',
|
||||
endDate: '',
|
||||
budget: '',
|
||||
description: '',
|
||||
maxMembers: 4,
|
||||
coverImage: '',
|
||||
tags: [],
|
||||
isPublic: true
|
||||
},
|
||||
interestTags: [
|
||||
'摄影', '美食', '徒步', '自驾', '露营',
|
||||
'文化', '历史', '自然', '冒险', '休闲',
|
||||
'购物', '海滩', '登山', '滑雪', '潜水'
|
||||
],
|
||||
showStartDatePicker: false,
|
||||
showEndDatePicker: false,
|
||||
loading: false
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
// 选择封面图片
|
||||
async chooseImage() {
|
||||
try {
|
||||
const res = await uni.chooseImage({
|
||||
count: 1,
|
||||
sizeType: ['compressed'],
|
||||
sourceType: ['album', 'camera']
|
||||
})
|
||||
|
||||
if (res.tempFilePaths.length > 0) {
|
||||
this.formData.coverImage = res.tempFilePaths[0]
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('选择图片失败:', error)
|
||||
uni.showToast({
|
||||
title: '选择图片失败',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
// 切换兴趣标签
|
||||
toggleTag(tag) {
|
||||
const index = this.formData.tags.indexOf(tag)
|
||||
if (index === -1) {
|
||||
this.formData.tags.push(tag)
|
||||
} else {
|
||||
this.formData.tags.splice(index, 1)
|
||||
}
|
||||
},
|
||||
|
||||
// 切换隐私设置
|
||||
togglePrivacy(e) {
|
||||
this.formData.isPublic = e.detail.value
|
||||
},
|
||||
|
||||
// 日期选择
|
||||
onStartDateChange(e) {
|
||||
this.formData.startDate = e
|
||||
this.showStartDatePicker = false
|
||||
},
|
||||
|
||||
onEndDateChange(e) {
|
||||
this.formData.endDate = e
|
||||
this.showEndDatePicker = false
|
||||
},
|
||||
|
||||
// 表单验证
|
||||
validateForm() {
|
||||
if (!this.formData.title.trim()) {
|
||||
uni.showToast({ title: '请输入旅行标题', icon: 'none' })
|
||||
return false
|
||||
}
|
||||
|
||||
if (!this.formData.destination.trim()) {
|
||||
uni.showToast({ title: '请输入目的地', icon: 'none' })
|
||||
return false
|
||||
}
|
||||
|
||||
if (!this.formData.startDate) {
|
||||
uni.showToast({ title: '请选择开始日期', icon: 'none' })
|
||||
return false
|
||||
}
|
||||
|
||||
if (!this.formData.endDate) {
|
||||
uni.showToast({ title: '请选择结束日期', icon: 'none' })
|
||||
return false
|
||||
}
|
||||
|
||||
if (new Date(this.formData.startDate) > new Date(this.formData.endDate)) {
|
||||
uni.showToast({ title: '结束日期不能早于开始日期', icon: 'none' })
|
||||
return false
|
||||
}
|
||||
|
||||
if (!this.formData.budget || Number(this.formData.budget) <= 0) {
|
||||
uni.showToast({ title: '请输入合理的预算', icon: 'none' })
|
||||
return false
|
||||
}
|
||||
|
||||
if (!this.formData.description.trim()) {
|
||||
uni.showToast({ title: '请输入行程描述', icon: 'none' })
|
||||
return false
|
||||
}
|
||||
|
||||
if (!this.formData.maxMembers || Number(this.formData.maxMembers) < 2) {
|
||||
uni.showToast({ title: '人数限制至少2人', icon: 'none' })
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
},
|
||||
|
||||
// 提交表单
|
||||
async submitForm() {
|
||||
if (!this.validateForm()) return
|
||||
|
||||
this.loading = true
|
||||
try {
|
||||
const submitData = {
|
||||
...this.formData,
|
||||
budget: Number(this.formData.budget),
|
||||
maxMembers: Number(this.formData.maxMembers)
|
||||
}
|
||||
|
||||
await travelService.create(submitData)
|
||||
|
||||
uni.showToast({
|
||||
title: '发布成功',
|
||||
icon: 'success',
|
||||
duration: 2000
|
||||
})
|
||||
|
||||
// 发布成功后返回上一页
|
||||
setTimeout(() => {
|
||||
uni.navigateBack()
|
||||
}, 1500)
|
||||
|
||||
} catch (error) {
|
||||
console.error('发布失败:', error)
|
||||
uni.showToast({
|
||||
title: '发布失败,请重试',
|
||||
icon: 'none'
|
||||
})
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
|
||||
// 返回上一页
|
||||
goBack() {
|
||||
uni.navigateBack()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.publish-page {
|
||||
background-color: #f8f9fa;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 20rpx 30rpx;
|
||||
background-color: #fff;
|
||||
border-bottom: 1rpx solid #eee;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.back-btn {
|
||||
padding: 10rpx;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
font-size: 28rpx;
|
||||
color: #007aff;
|
||||
font-weight: 500;
|
||||
padding: 10rpx 20rpx;
|
||||
}
|
||||
|
||||
.form-content {
|
||||
height: calc(100vh - 120rpx);
|
||||
padding: 20rpx 30rpx;
|
||||
}
|
||||
|
||||
.form-section {
|
||||
background-color: #fff;
|
||||
border-radius: 16rpx;
|
||||
padding: 30rpx;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin-bottom: 30rpx;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.upload-area {
|
||||
height: 200rpx;
|
||||
border: 2rpx dashed #ddd;
|
||||
border-radius: 12rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: #fafafa;
|
||||
}
|
||||
|
||||
.cover-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
|
||||
.upload-placeholder {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.upload-text {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
margin-top: 10rpx;
|
||||
}
|
||||
|
||||
.form-item {
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
|
||||
.form-item:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.label {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
margin-bottom: 15rpx;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.input, .textarea {
|
||||
background-color: #f8f9fa;
|
||||
border-radius: 8rpx;
|
||||
padding: 20rpx;
|
||||
font-size: 28rpx;
|
||||
border: 1rpx solid #eee;
|
||||
}
|
||||
|
||||
.textarea {
|
||||
height: 200rpx;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.char-count {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
text-align: right;
|
||||
display: block;
|
||||
margin-top: 10rpx;
|
||||
}
|
||||
|
||||
.date-range {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20rpx;
|
||||
}
|
||||
|
||||
.date-input {
|
||||
flex: 1;
|
||||
background-color: #f8f9fa;
|
||||
border-radius: 8rpx;
|
||||
padding: 20rpx;
|
||||
border: 1rpx solid #eee;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.date-separator {
|
||||
color: #999;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.budget-input {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background-color: #f8f9fa;
|
||||
border-radius: 8rpx;
|
||||
border: 1rpx solid #eee;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.currency {
|
||||
padding: 20rpx;
|
||||
background-color: #f0f0f0;
|
||||
color: #333;
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.budget-input .input {
|
||||
flex: 1;
|
||||
border: none;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.member-limit {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10rpx;
|
||||
}
|
||||
|
||||
.number-input {
|
||||
width: 120rpx;
|
||||
background-color: #f8f9fa;
|
||||
border-radius: 8rpx;
|
||||
padding: 20rpx;
|
||||
border: 1rpx solid #eee;
|
||||
text-align: center;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.tags-container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 15rpx;
|
||||
margin-top: 15rpx;
|
||||
}
|
||||
|
||||
.tag {
|
||||
padding: 12rpx 24rpx;
|
||||
background-color: #f8f9fa;
|
||||
border-radius: 30rpx;
|
||||
border: 1rpx solid #eee;
|
||||
font-size: 24rpx;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.tag.active {
|
||||
background-color: #007aff;
|
||||
color: #fff;
|
||||
border-color: #007aff;
|
||||
}
|
||||
|
||||
.privacy-setting {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
|
||||
.privacy-tip {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.loading-mask {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.loading-mask text {
|
||||
color: #fff;
|
||||
margin-top: 20rpx;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
</style>
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"config.js","sources":["api/config.js"],"sourcesContent":["// API基础配置\r\nconst config = {\r\n // 开发环境\r\n development: {\r\n baseURL: 'http://localhost:3100/api',\r\n timeout: 10000\r\n },\r\n // 生产环境\r\n production: {\r\n baseURL: 'https://api.jiebanke.com/api',\r\n timeout: 15000\r\n }\r\n}\r\n\r\n// 获取当前环境配置\r\nconst getConfig = () => {\r\n const env = process.env.NODE_ENV || 'development'\r\n return config[env]\r\n}\r\n\r\n// API端点\r\nconst endpoints = {\r\n // 用户相关\r\n USER: {\r\n LOGIN: '/auth/login',\r\n REGISTER: '/auth/register',\r\n PROFILE: '/user/profile',\r\n UPDATE_PROFILE: '/user/profile',\r\n UPLOAD_AVATAR: '/user/avatar'\r\n },\r\n \r\n // 旅行计划\r\n TRAVEL: {\r\n LIST: '/travel/list',\r\n DETAIL: '/travel/detail',\r\n CREATE: '/travel/create',\r\n JOIN: '/travel/join',\r\n MY_PLANS: '/travel/my-plans',\r\n SEARCH: '/travel/search'\r\n },\r\n \r\n // 动物认养\r\n ANIMAL: {\r\n LIST: '/animal/list',\r\n DETAIL: '/animal/detail',\r\n ADOPT: '/animal/adopt',\r\n MY_ANIMALS: '/animal/my-animals',\r\n CATEGORIES: '/animal/categories'\r\n },\r\n \r\n // 送花服务\r\n FLOWER: {\r\n LIST: '/flower/list',\r\n DETAIL: '/flower/detail',\r\n ORDER: '/flower/order',\r\n MY_ORDERS: '/flower/my-orders',\r\n CATEGORIES: '/flower/categories'\r\n },\r\n \r\n // 订单管理\r\n ORDER: {\r\n LIST: '/order/list',\r\n DETAIL: '/order/detail',\r\n CANCEL: '/order/cancel',\r\n PAY: '/order/pay',\r\n CONFIRM: '/order/confirm'\r\n },\r\n \r\n // 支付相关\r\n PAYMENT: {\r\n CREATE: '/payment/create',\r\n QUERY: '/payment/query',\r\n REFUND: '/payment/refund'\r\n },\r\n \r\n // 系统相关\r\n SYSTEM: {\r\n CONFIG: '/system/config',\r\n NOTICE: '/system/notice',\r\n FEEDBACK: '/system/feedback'\r\n }\r\n}\r\n\r\nexport default {\r\n ...getConfig(),\r\n endpoints\r\n}"],"names":[],"mappings":";AACA,MAAM,SAAS;AAAA;AAAA,EAEb,aAAa;AAAA,IACX,SAAS;AAAA,IACT,SAAS;AAAA,EACX;AAAA;AAAA,EAEA,YAAY;AAAA,IACV,SAAS;AAAA,IACT,SAAS;AAAA,EACX;AACF;AAGA,MAAM,YAAY,MAAM;AACtB,QAAM,MAAM;AACZ,SAAO,OAAO,GAAG;AACnB;AAGA,MAAM,YAAY;AAAA;AAAA,EAEhB,MAAM;AAAA,IACJ,OAAO;AAAA,IACP,UAAU;AAAA,IACV,SAAS;AAAA,IACT,gBAAgB;AAAA,IAChB,eAAe;AAAA,EACjB;AAAA;AAAA,EAGA,QAAQ;AAAA,IACN,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,UAAU;AAAA,IACV,QAAQ;AAAA,EACV;AAAA;AAAA,EAGA,QAAQ;AAAA,IACN,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,YAAY;AAAA,IACZ,YAAY;AAAA,EACd;AAAA;AAAA,EAGA,QAAQ;AAAA,IACN,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,WAAW;AAAA,IACX,YAAY;AAAA,EACd;AAAA;AAAA,EAGA,OAAO;AAAA,IACL,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,KAAK;AAAA,IACL,SAAS;AAAA,EACX;AAAA;AAAA,EAGA,SAAS;AAAA,IACP,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,QAAQ;AAAA,EACV;AAAA;AAAA,EAGA,QAAQ;AAAA,IACN,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,UAAU;AAAA,EACZ;AACF;AAEA,MAAe,WAAA;AAAA,EACb,GAAG,UAAU;AAAA,EACb;AACF;;"}
|
||||
{"version":3,"file":"config.js","sources":["api/config.js"],"sourcesContent":["// API基础配置\nconst config = {\n // 开发环境\n development: {\n baseURL: 'http://localhost:3100/api',\n timeout: 10000\n },\n // 生产环境\n production: {\n baseURL: 'https://api.jiebanke.com/api',\n timeout: 15000\n }\n}\n\n// 获取当前环境配置\nconst getConfig = () => {\n const env = process.env.NODE_ENV || 'development'\n return config[env]\n}\n\n// API端点\nconst endpoints = {\n // 用户相关\n USER: {\n LOGIN: '/auth/login',\n REGISTER: '/auth/register',\n PROFILE: '/user/profile',\n UPDATE_PROFILE: '/user/profile',\n UPLOAD_AVATAR: '/user/avatar'\n },\n \n // 旅行计划\n TRAVEL: {\n LIST: '/travel/list',\n DETAIL: '/travel/detail',\n CREATE: '/travel/create',\n UPDATE: '/travel/update',\n DELETE: '/travel/delete',\n JOIN: '/travel/join',\n QUIT: '/travel/quit',\n LIKE: '/travel/like',\n UNLIKE: '/travel/unlike',\n COMMENT: '/travel/comment',\n COMMENTS: '/travel/comments',\n MY_PLANS: '/travel/my-plans',\n SEARCH: '/travel/search'\n },\n \n // 动物认养\n ANIMAL: {\n LIST: '/animal/list',\n DETAIL: '/animal/detail',\n ADOPT: '/animal/adopt',\n MY_ANIMALS: '/animal/my-animals',\n CATEGORIES: '/animal/categories'\n },\n \n // 送花服务\n FLOWER: {\n LIST: '/flower/list',\n DETAIL: '/flower/detail',\n ORDER: '/flower/order',\n MY_ORDERS: '/flower/my-orders',\n CATEGORIES: '/flower/categories'\n },\n \n // 订单管理\n ORDER: {\n LIST: '/order/list',\n DETAIL: '/order/detail',\n CANCEL: '/order/cancel',\n PAY: '/order/pay',\n CONFIRM: '/order/confirm'\n },\n \n // 支付相关\n PAYMENT: {\n CREATE: '/payment/create',\n QUERY: '/payment/query',\n REFUND: '/payment/refund'\n },\n \n // 系统相关\n SYSTEM: {\n CONFIG: '/system/config',\n NOTICE: '/system/notice',\n FEEDBACK: '/system/feedback'\n },\n\n // 搜索相关\n SEARCH: {\n GLOBAL: '/search/global',\n SUGGESTIONS: '/search/suggestions',\n TRAVEL: '/search/travel',\n ANIMAL: '/search/animal',\n FLOWER: '/search/flower',\n USER: '/search/user'\n },\n\n // 推广相关\n PROMOTION: {\n DATA: '/promotion/data',\n RECORDS: '/promotion/records',\n ALL_RECORDS: '/promotion/all-records',\n QRCODE: '/promotion/qrcode',\n REWARD_DETAILS: '/promotion/reward-details',\n WITHDRAW: '/promotion/withdraw',\n WITHDRAW_RECORDS: '/promotion/withdraw-records'\n },\n\n // 认证相关\n AUTH: {\n PHONE_LOGIN: '/auth/phone-login',\n WECHAT_LOGIN: '/auth/wechat-login',\n PASSWORD_LOGIN: '/auth/password-login',\n SEND_SMS_CODE: '/auth/send-sms-code',\n CHECK_TOKEN: '/auth/check-token',\n REFRESH_TOKEN: '/auth/refresh-token',\n BIND_PHONE: '/auth/bind-phone',\n CHANGE_PASSWORD: '/auth/change-password',\n RESET_PASSWORD: '/auth/reset-password'\n }\n}\n\nexport default {\n ...getConfig(),\n endpoints\n}"],"names":[],"mappings":";AACA,MAAM,SAAS;AAAA;AAAA,EAEb,aAAa;AAAA,IACX,SAAS;AAAA,IACT,SAAS;AAAA,EACX;AAAA;AAAA,EAEA,YAAY;AAAA,IACV,SAAS;AAAA,IACT,SAAS;AAAA,EACX;AACF;AAGA,MAAM,YAAY,MAAM;AACtB,QAAM,MAAM;AACZ,SAAO,OAAO,GAAG;AACnB;AAGA,MAAM,YAAY;AAAA;AAAA,EAEhB,MAAM;AAAA,IACJ,OAAO;AAAA,IACP,UAAU;AAAA,IACV,SAAS;AAAA,IACT,gBAAgB;AAAA,IAChB,eAAe;AAAA,EACjB;AAAA;AAAA,EAGA,QAAQ;AAAA,IACN,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,MAAM;AAAA,IACN,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,UAAU;AAAA,IACV,UAAU;AAAA,IACV,QAAQ;AAAA,EACV;AAAA;AAAA,EAGA,QAAQ;AAAA,IACN,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,YAAY;AAAA,IACZ,YAAY;AAAA,EACd;AAAA;AAAA,EAGA,QAAQ;AAAA,IACN,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,WAAW;AAAA,IACX,YAAY;AAAA,EACd;AAAA;AAAA,EAGA,OAAO;AAAA,IACL,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,KAAK;AAAA,IACL,SAAS;AAAA,EACX;AAAA;AAAA,EAGA,SAAS;AAAA,IACP,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,QAAQ;AAAA,EACV;AAAA;AAAA,EAGA,QAAQ;AAAA,IACN,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,UAAU;AAAA,EACZ;AAAA;AAAA,EAGA,QAAQ;AAAA,IACN,QAAQ;AAAA,IACR,aAAa;AAAA,IACb,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,MAAM;AAAA,EACR;AAAA;AAAA,EAGA,WAAW;AAAA,IACT,MAAM;AAAA,IACN,SAAS;AAAA,IACT,aAAa;AAAA,IACb,QAAQ;AAAA,IACR,gBAAgB;AAAA,IAChB,UAAU;AAAA,IACV,kBAAkB;AAAA,EACpB;AAAA;AAAA,EAGA,MAAM;AAAA,IACJ,aAAa;AAAA,IACb,cAAc;AAAA,IACd,gBAAgB;AAAA,IAChB,eAAe;AAAA,IACf,aAAa;AAAA,IACb,eAAe;AAAA,IACf,YAAY;AAAA,IACZ,iBAAiB;AAAA,IACjB,gBAAgB;AAAA,EAClB;AACF;AAEA,MAAe,WAAA;AAAA,EACb,GAAG,UAAU;AAAA,EACb;AACF;;"}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -29,7 +29,14 @@ const endpoints = {
|
||||
LIST: "/travel/list",
|
||||
DETAIL: "/travel/detail",
|
||||
CREATE: "/travel/create",
|
||||
UPDATE: "/travel/update",
|
||||
DELETE: "/travel/delete",
|
||||
JOIN: "/travel/join",
|
||||
QUIT: "/travel/quit",
|
||||
LIKE: "/travel/like",
|
||||
UNLIKE: "/travel/unlike",
|
||||
COMMENT: "/travel/comment",
|
||||
COMMENTS: "/travel/comments",
|
||||
MY_PLANS: "/travel/my-plans",
|
||||
SEARCH: "/travel/search"
|
||||
},
|
||||
@@ -68,6 +75,37 @@ const endpoints = {
|
||||
CONFIG: "/system/config",
|
||||
NOTICE: "/system/notice",
|
||||
FEEDBACK: "/system/feedback"
|
||||
},
|
||||
// 搜索相关
|
||||
SEARCH: {
|
||||
GLOBAL: "/search/global",
|
||||
SUGGESTIONS: "/search/suggestions",
|
||||
TRAVEL: "/search/travel",
|
||||
ANIMAL: "/search/animal",
|
||||
FLOWER: "/search/flower",
|
||||
USER: "/search/user"
|
||||
},
|
||||
// 推广相关
|
||||
PROMOTION: {
|
||||
DATA: "/promotion/data",
|
||||
RECORDS: "/promotion/records",
|
||||
ALL_RECORDS: "/promotion/all-records",
|
||||
QRCODE: "/promotion/qrcode",
|
||||
REWARD_DETAILS: "/promotion/reward-details",
|
||||
WITHDRAW: "/promotion/withdraw",
|
||||
WITHDRAW_RECORDS: "/promotion/withdraw-records"
|
||||
},
|
||||
// 认证相关
|
||||
AUTH: {
|
||||
PHONE_LOGIN: "/auth/phone-login",
|
||||
WECHAT_LOGIN: "/auth/wechat-login",
|
||||
PASSWORD_LOGIN: "/auth/password-login",
|
||||
SEND_SMS_CODE: "/auth/send-sms-code",
|
||||
CHECK_TOKEN: "/auth/check-token",
|
||||
REFRESH_TOKEN: "/auth/refresh-token",
|
||||
BIND_PHONE: "/auth/bind-phone",
|
||||
CHANGE_PASSWORD: "/auth/change-password",
|
||||
RESET_PASSWORD: "/auth/reset-password"
|
||||
}
|
||||
};
|
||||
const config$1 = {
|
||||
|
||||
@@ -1,7 +1,106 @@
|
||||
"use strict";
|
||||
require("../common/vendor.js");
|
||||
const common_vendor = require("../common/vendor.js");
|
||||
const api_request = require("./request.js");
|
||||
require("./config.js");
|
||||
const api_config = require("./config.js");
|
||||
const { endpoints } = api_config.config;
|
||||
const userService = {
|
||||
// 登录
|
||||
login: (data) => api_request.request.post(endpoints.USER.LOGIN, data),
|
||||
// 注册
|
||||
register: (data) => api_request.request.post(endpoints.USER.REGISTER, data),
|
||||
// 获取用户信息
|
||||
getProfile: () => api_request.request.get(endpoints.USER.PROFILE),
|
||||
// 更新用户信息
|
||||
updateProfile: (data) => api_request.request.put(endpoints.USER.UPDATE_PROFILE, data),
|
||||
// 上传头像
|
||||
uploadAvatar: (filePath) => api_request.request.upload(endpoints.USER.UPLOAD_AVATAR, filePath),
|
||||
// 退出登录
|
||||
logout: () => {
|
||||
common_vendor.index.removeStorageSync("token");
|
||||
common_vendor.index.removeStorageSync("refreshToken");
|
||||
return Promise.resolve();
|
||||
}
|
||||
};
|
||||
const travelService = {
|
||||
// 获取旅行计划列表
|
||||
getList: (params = {}) => api_request.request.get(endpoints.TRAVEL.LIST, params),
|
||||
// 获取旅行计划详情
|
||||
getDetail: (id) => api_request.request.get(`${endpoints.TRAVEL.DETAIL}/${id}`),
|
||||
// 创建旅行计划
|
||||
create: (data) => api_request.request.post(endpoints.TRAVEL.CREATE, data),
|
||||
// 更新旅行计划
|
||||
update: (id, data) => api_request.request.put(`${endpoints.TRAVEL.UPDATE}/${id}`, data),
|
||||
// 删除旅行计划
|
||||
deletePlan: (id) => api_request.request.delete(`${endpoints.TRAVEL.DELETE}/${id}`),
|
||||
// 加入旅行计划
|
||||
joinPlan: (travelId) => api_request.request.post(`${endpoints.TRAVEL.JOIN}/${travelId}`),
|
||||
// 退出旅行计划
|
||||
quitPlan: (travelId) => api_request.request.post(`${endpoints.TRAVEL.QUIT}/${travelId}`),
|
||||
// 点赞旅行计划
|
||||
likePlan: (travelId) => api_request.request.post(`${endpoints.TRAVEL.LIKE}/${travelId}`),
|
||||
// 取消点赞旅行计划
|
||||
unlikePlan: (travelId) => api_request.request.post(`${endpoints.TRAVEL.UNLIKE}/${travelId}`),
|
||||
// 添加评论
|
||||
addComment: (travelId, data) => api_request.request.post(`${endpoints.TRAVEL.COMMENT}/${travelId}`, data),
|
||||
// 获取评论列表
|
||||
getComments: (travelId, params = {}) => api_request.request.get(`${endpoints.TRAVEL.COMMENTS}/${travelId}`, params),
|
||||
// 获取我的旅行计划
|
||||
getMyPlans: (params = {}) => api_request.request.get(endpoints.TRAVEL.MY_PLANS, params),
|
||||
// 搜索旅行计划
|
||||
search: (keyword, params = {}) => api_request.request.get(endpoints.TRAVEL.SEARCH, { keyword, ...params })
|
||||
};
|
||||
const animalService = {
|
||||
// 获取动物列表
|
||||
getList: (params = {}) => api_request.request.get(endpoints.ANIMAL.LIST, params),
|
||||
// 获取动物详情
|
||||
getDetail: (id) => api_request.request.get(`${endpoints.ANIMAL.DETAIL}/${id}`),
|
||||
// 认养动物
|
||||
adopt: (animalId, data) => api_request.request.post(`${endpoints.ANIMAL.ADOPT}/${animalId}`, data),
|
||||
// 获取我的动物
|
||||
getMyAnimals: (params = {}) => api_request.request.get(endpoints.ANIMAL.MY_ANIMALS, params),
|
||||
// 获取动物分类
|
||||
getCategories: () => api_request.request.get(endpoints.ANIMAL.CATEGORIES)
|
||||
};
|
||||
const flowerService = {
|
||||
// 获取花束列表
|
||||
getList: (params = {}) => api_request.request.get(endpoints.FLOWER.LIST, params),
|
||||
// 获取花束详情
|
||||
getDetail: (id) => api_request.request.get(`${endpoints.FLOWER.DETAIL}/${id}`),
|
||||
// 下单
|
||||
order: (data) => api_request.request.post(endpoints.FLOWER.ORDER, data),
|
||||
// 获取我的订单
|
||||
getMyOrders: (params = {}) => api_request.request.get(endpoints.FLOWER.MY_ORDERS, params),
|
||||
// 获取花束分类
|
||||
getCategories: () => api_request.request.get(endpoints.FLOWER.CATEGORIES)
|
||||
};
|
||||
const orderService = {
|
||||
// 获取订单列表
|
||||
getList: (params = {}) => api_request.request.get(endpoints.ORDER.LIST, params),
|
||||
// 获取订单详情
|
||||
getDetail: (id) => api_request.request.get(`${endpoints.ORDER.DETAIL}/${id}`),
|
||||
// 取消订单
|
||||
cancel: (id) => api_request.request.post(`${endpoints.ORDER.CANCEL}/${id}`),
|
||||
// 支付订单
|
||||
pay: (id) => api_request.request.post(`${endpoints.ORDER.PAY}/${id}`),
|
||||
// 确认收货
|
||||
confirm: (id) => api_request.request.post(`${endpoints.ORDER.CONFIRM}/${id}`)
|
||||
};
|
||||
const paymentService = {
|
||||
// 创建支付
|
||||
create: (data) => api_request.request.post(endpoints.PAYMENT.CREATE, data),
|
||||
// 查询支付状态
|
||||
query: (paymentId) => api_request.request.get(`${endpoints.PAYMENT.QUERY}/${paymentId}`),
|
||||
// 退款
|
||||
refund: (paymentId, data) => api_request.request.post(`${endpoints.PAYMENT.REFUND}/${paymentId}`, data)
|
||||
};
|
||||
const systemService = {
|
||||
// 获取系统配置
|
||||
getConfig: () => api_request.request.get(endpoints.SYSTEM.CONFIG),
|
||||
// 获取公告列表
|
||||
getNotices: (params = {}) => api_request.request.get(endpoints.SYSTEM.NOTICE, params),
|
||||
// 提交反馈
|
||||
submitFeedback: (data) => api_request.request.post(endpoints.SYSTEM.FEEDBACK, data)
|
||||
};
|
||||
const homeService = {
|
||||
// 获取首页数据
|
||||
getHomeData: () => api_request.request.get("/home/data"),
|
||||
@@ -14,5 +113,101 @@ const homeService = {
|
||||
// 获取精选花束
|
||||
getFeaturedFlowers: () => api_request.request.get("/home/featured-flowers")
|
||||
};
|
||||
const searchService = {
|
||||
// 全局搜索
|
||||
search: (params = {}) => api_request.request.get(endpoints.SEARCH.GLOBAL, params),
|
||||
// 获取搜索建议
|
||||
getSuggestions: (keyword) => api_request.request.get(endpoints.SEARCH.SUGGESTIONS, { keyword }),
|
||||
// 旅行计划搜索
|
||||
searchTravel: (params = {}) => api_request.request.get(endpoints.SEARCH.TRAVEL, params),
|
||||
// 动物搜索
|
||||
searchAnimal: (params = {}) => api_request.request.get(endpoints.SEARCH.ANIMAL, params),
|
||||
// 花束搜索
|
||||
searchFlower: (params = {}) => api_request.request.get(endpoints.SEARCH.FLOWER, params),
|
||||
// 用户搜索
|
||||
searchUser: (params = {}) => api_request.request.get(endpoints.SEARCH.USER, params)
|
||||
};
|
||||
const promotionService = {
|
||||
// 获取推广数据
|
||||
getPromotionData: () => api_request.request.get(endpoints.PROMOTION.DATA),
|
||||
// 获取邀请记录
|
||||
getRecentRecords: (params = {}) => api_request.request.get(endpoints.PROMOTION.RECORDS, params),
|
||||
// 获取所有邀请记录
|
||||
getAllRecords: (params = {}) => api_request.request.get(endpoints.PROMOTION.ALL_RECORDS, params),
|
||||
// 生成邀请二维码
|
||||
generateQRCode: () => api_request.request.get(endpoints.PROMOTION.QRCODE),
|
||||
// 获取奖励明细
|
||||
getRewardDetails: (params = {}) => api_request.request.get(endpoints.PROMOTION.REWARD_DETAILS, params),
|
||||
// 提现申请
|
||||
applyWithdraw: (data) => api_request.request.post(endpoints.PROMOTION.WITHDRAW, data),
|
||||
// 获取提现记录
|
||||
getWithdrawRecords: (params = {}) => api_request.request.get(endpoints.PROMOTION.WITHDRAW_RECORDS, params)
|
||||
};
|
||||
const authService = {
|
||||
// 手机号登录
|
||||
phoneLogin: (data) => api_request.request.post(endpoints.AUTH.PHONE_LOGIN, data),
|
||||
// 微信登录
|
||||
wechatLogin: (data) => api_request.request.post(endpoints.AUTH.WECHAT_LOGIN, data),
|
||||
// 密码登录
|
||||
passwordLogin: (data) => api_request.request.post(endpoints.AUTH.PASSWORD_LOGIN, data),
|
||||
// 发送短信验证码
|
||||
sendSmsCode: (phone) => api_request.request.post(endpoints.AUTH.SEND_SMS_CODE, { phone }),
|
||||
// 验证token
|
||||
checkToken: (token) => api_request.request.post(endpoints.AUTH.CHECK_TOKEN, { token }),
|
||||
// 刷新token
|
||||
refreshToken: (refreshToken) => api_request.request.post(endpoints.AUTH.REFRESH_TOKEN, { refreshToken }),
|
||||
// 绑定手机号
|
||||
bindPhone: (data) => api_request.request.post(endpoints.AUTH.BIND_PHONE, data),
|
||||
// 修改密码
|
||||
changePassword: (data) => api_request.request.post(endpoints.AUTH.CHANGE_PASSWORD, data),
|
||||
// 重置密码
|
||||
resetPassword: (data) => api_request.request.post(endpoints.AUTH.RESET_PASSWORD, data)
|
||||
};
|
||||
const apiUtils = {
|
||||
// 生成分页参数
|
||||
generatePagination: (page = 1, pageSize = 10) => ({
|
||||
page,
|
||||
pageSize,
|
||||
skip: (page - 1) * pageSize
|
||||
}),
|
||||
// 处理上传进度
|
||||
handleUploadProgress: (progressEvent) => {
|
||||
const percent = Math.round(progressEvent.loaded * 100 / progressEvent.total);
|
||||
return percent;
|
||||
},
|
||||
// 处理下载进度
|
||||
handleDownloadProgress: (progressEvent) => {
|
||||
const percent = Math.round(progressEvent.loaded * 100 / progressEvent.total);
|
||||
return percent;
|
||||
},
|
||||
// 格式化错误信息
|
||||
formatError: (error) => {
|
||||
if (error.code) {
|
||||
return error.message;
|
||||
}
|
||||
return "网络连接失败,请检查网络设置";
|
||||
}
|
||||
};
|
||||
const travelService$1 = {
|
||||
userService,
|
||||
travelService,
|
||||
animalService,
|
||||
flowerService,
|
||||
orderService,
|
||||
paymentService,
|
||||
systemService,
|
||||
homeService,
|
||||
authService,
|
||||
searchService,
|
||||
promotionService,
|
||||
apiUtils
|
||||
};
|
||||
exports.animalService = animalService;
|
||||
exports.authService = authService;
|
||||
exports.flowerService = flowerService;
|
||||
exports.homeService = homeService;
|
||||
exports.promotionService = promotionService;
|
||||
exports.searchService = searchService;
|
||||
exports.travelService = travelService;
|
||||
exports.travelService$1 = travelService$1;
|
||||
//# sourceMappingURL=../../.sourcemap/mp-weixin/api/services.js.map
|
||||
|
||||
Reference in New Issue
Block a user