Browse Source

feat: 接口对接;

pull/5/head
Fox-33 1 month ago
parent
commit
a400580710
37 changed files with 670 additions and 871 deletions
  1. +4
    -1
      api/api.js
  2. +5
    -0
      api/model/comment.js
  3. +6
    -0
      api/model/login.js
  4. +18
    -0
      api/model/notice.js
  5. +8
    -0
      api/model/order.js
  6. +3
    -3
      components/growing/recordsView.vue
  7. +35
    -26
      components/growing/userCard.vue
  8. +1
    -1
      config.js
  9. +4
    -1
      pages.json
  10. +2
    -11
      pages/index/center.vue
  11. +4
    -3
      pages/index/growing.vue
  12. +8
    -12
      pages_order/center/orderCard.vue
  13. +23
    -7
      pages_order/comment/commentCard.vue
  14. +51
    -13
      pages_order/comment/commentPopup.vue
  15. +2
    -16
      pages_order/comment/commentRecords.vue
  16. +43
    -12
      pages_order/comment/commentRecordsOfProduct.vue
  17. +33
    -63
      pages_order/comment/recordFormPopup.vue
  18. +2
    -0
      pages_order/components/reloateProjectPopup.vue
  19. +3
    -3
      pages_order/coupon/couponList/couponCard.vue
  20. +10
    -7
      pages_order/message/card.vue
  21. +15
    -5
      pages_order/message/index.vue
  22. +1
    -2
      pages_order/message/list.vue
  23. +2
    -15
      pages_order/order/components/orderInfoView.vue
  24. +24
    -8
      pages_order/order/components/productCard.vue
  25. +75
    -114
      pages_order/order/orderConfirm/index.vue
  26. +32
    -45
      pages_order/order/orderConfirm/infoPopup.vue
  27. +14
    -8
      pages_order/order/orderConfirm/memberCard.vue
  28. +42
    -155
      pages_order/order/orderDetail/index.vue
  29. +7
    -7
      pages_order/order/orderList/index.vue
  30. +27
    -30
      pages_order/order/orderList/orderCard.vue
  31. +87
    -133
      pages_order/order/orderPay/index.vue
  32. +5
    -5
      pages_order/product/commentList.vue
  33. +16
    -83
      pages_order/product/productDetail.vue
  34. +14
    -9
      pages_order/traveler/travelerCard.vue
  35. +1
    -3
      pages_order/traveler/travelerList.vue
  36. +24
    -43
      pages_order/traveler/travelerPopup.vue
  37. +19
    -27
      store/store.js

+ 4
- 1
api/api.js View File

@ -5,7 +5,7 @@ import utils from '../utils/utils.js'
let limit = {}
let debounce = {}
const models = ['login', 'index', 'image', 'activity', 'coupon', 'order', 'comment', 'bind', 'experience', 'partner', 'info']
const models = ['login', 'index', 'image', 'activity', 'coupon', 'order', 'comment', 'bind', 'experience', 'partner', 'info', 'notice']
const config = {
// 示例
@ -16,6 +16,9 @@ const config = {
getConfig : {url : '/config/queryConfigList', method : 'GET', limit : 500},
queryConfigByParamCode : {url : '/config/queryConfigByParamCode', method : 'GET'},
queryPeriodList : {url : '/config/queryPeriodList', method : 'GET'},
queryCommentOptionList : {url : '/config/queryCommentOptionList', method : 'GET'},
queryExperienceQuestionList : {url : '/config/queryExperienceQuestionList', method : 'GET'},
}


+ 5
- 0
api/model/comment.js View File

@ -1,6 +1,11 @@
// 评论相关接口
const api = {
// 我的评论-查询评价列表
queryCommentList: {
url: '/comment/queryCommentList',
method: 'GET',
},
// 我的评论-查询我的评价列表
queryMyCommentList: {
url: '/comment/queryMyCommentList',


+ 6
- 0
api/model/login.js View File

@ -30,6 +30,12 @@ const api = {
method: 'GET',
auth: true,
},
// 获取个人基础数据
queryUserCenterData: {
url: '/userInfo/queryUserCenterData',
method: 'GET',
auth: true,
},
}
export default api

+ 18
- 0
api/model/notice.js View File

@ -0,0 +1,18 @@
// 开营通知相关接口
const api = {
// 开营通知-查询开营通知列表
queryNoticeList: {
url: '/notice/queryNoticeList',
method: 'GET',
auth: true,
},
// 开营通知-查询开营通知详情
queryNoticeById: {
url: '/notice/queryNoticeById',
method: 'GET',
auth: true,
},
}
export default api

+ 8
- 0
api/model/order.js View File

@ -19,6 +19,14 @@ const api = {
limit : 500,
showLoading : true,
},
// 我的订单-修改订单
updateOrder: {
url: '/order/updateOrder',
method: 'POST',
auth: true,
limit : 500,
showLoading : true,
},
// 我的订单-支付订单
payOrder: {
url: '/order/payOrder',


+ 3
- 3
components/growing/recordsView.vue View File

@ -11,14 +11,14 @@
</view>
</template>
<template #title>
<view class="title" @click="onClickActivity(record.id)">{{ record.createTime }}</view>
<view class="title" @click="onClickActivity(record.id)">{{ $dayjs(record.createTime).format('YYYY-MM-DD') }}</view>
</template>
<template #desc>
<view class="content" @click="onClickActivity(record.id)">
<view class="desc">{{ record.name }}</view>
<view class="desc">{{ record.activityTitle || record.activityId_dictText || '' }}</view>
<view class="image">
<view class="image-item" v-for="(image, imgIdx) in record.image" :key="imgIdx">
<image class="img" :src="image" mode="scaleToFill"></image>
<image class="img" :src="image" mode="aspectFill"></image>
</view>
</view>
</view>


+ 35
- 26
components/growing/userCard.vue View File

@ -10,27 +10,27 @@
</view>
<view class="flex">
<button class="btn btn-switch" @click="onSwitch">切换</button>
<button class="btn btn-add" @click="onAdd">新增记录</button>
<!-- <button class="btn btn-add" @click="onAdd">新增记录</button> -->
</view>
</view>
<view class="card-content">
<view class="flex info">
<view class="avatar">
<image class="img" src="@/pages_order/static/temp-30.png" mode="scaleToFill"></image>
<image class="img" :src="memberInfo.headImage" mode="scaleToFill"></image>
<view :class="['tag', `tag-0`]">学生</view>
</view>
<view class="flex summary">
<view class="flex flex-column summary-item name">
<view class="summary-item-content">战斗世界</view>
<view class="summary-item-label">ID5625354</view>
<view class="summary-item-content">{{ memberInfo.nickName }}</view>
<view class="summary-item-label">{{ `ID:${memberInfo.id}` }}</view>
</view>
<view class="flex flex-column summary-item" @click="jumpToAchievement">
<view class="summary-item-content">8</view>
<view class="summary-item-label">成就</view>
<view class="summary-item-label nowrap">成就</view>
</view>
<view class="flex flex-column summary-item">
<view class="summary-item-content">68</view>
<view class="summary-item-label">足迹</view>
<view class="summary-item-label nowrap">足迹</view>
</view>
</view>
</view>
@ -49,31 +49,36 @@
</template>
<script>
export default {
data() {
return {
}
},
onLoad() {
import { mapState } from 'vuex'
},
methods: {
getData() {
// todo
export default {
data() {
return {
}
},
onAdd() {
this.$emit('addRecord')
onLoad() {
},
onSwitch() {
this.$emit('switchMember')
computed: {
...mapState(['memberInfo']),
},
jumpToAchievement() {
uni.navigateTo({
url: '/pages_order/growing/achievement/index'
})
methods: {
getData() {
// todo
},
onAdd() {
this.$emit('addRecord')
},
onSwitch() {
this.$emit('switchMember')
},
jumpToAchievement() {
uni.navigateTo({
url: '/pages_order/growing/achievement/index'
})
},
},
},
}
}
</script>
<style scoped lang="scss">
@ -199,4 +204,8 @@ export default {
}
}
.nowrap {
white-space: nowrap;
}
</style>

+ 1
- 1
config.js View File

@ -14,7 +14,7 @@ const type = 'prod'
// 环境配置
const config = {
dev : {
baseUrl : 'http://www.gcosc.fun:82',
baseUrl : 'http://augcl.natapp1.cc/studytour-admin/studytour',
},
prod : {
baseUrl : 'https://www.hongyujiaoyu.com/studytour-admin/studytour',


+ 4
- 1
pages.json View File

@ -74,7 +74,10 @@
"path": "order/orderConfirm/index"
},
{
"path": "order/orderPay/index"
"path": "order/orderPay/index",
"style": {
"enablePullDownRefresh": true
}
},
{
"path": "order/orderDetail/index",


+ 2
- 11
pages/index/center.vue View File

@ -44,7 +44,7 @@
</view>
<!-- 订单信息 -->
<order-card :statistics="statistics"></order-card>
<order-card></order-card>
<!-- 用户菜单 -->
<view class="card">
@ -112,7 +112,6 @@
},
data() {
return {
statistics: {},
list1: [
{ id: '001', label: '我的收藏', icon: '/pages_order/static/center/icon-collect.png', path: '/pages_order/product/collectList' },
{ id: '004', label: '学员管理', icon: '/pages_order/static/center/icon-student.png', path: `/pages_order/member/memberList` },
@ -135,7 +134,7 @@
onShow() {
if(uni.getStorageSync('token')){
this.$store.commit('getUserInfo')
this.fetchOrderStatistics()
this.$store.commit('getUserCenterData')
}
},
methods: {
@ -154,14 +153,6 @@
break
}
},
async fetchOrderStatistics() {
return
try {
this.statistics = await this.$fetch('getOrderStatistics', { id: '' })
} catch (err) {
}
},
jumpToBindMember() {
this.$utils.navigateTo('/pages_order/member/memberBind')
},


+ 4
- 3
pages/index/growing.vue View File

@ -70,6 +70,8 @@
computed: {
...mapState(['memberInfo']),
mixinsListApi() {
return 'queryExperienceList'
// todo
return this.queryParams.userId ? 'queryExperienceList' : ''
},
},
@ -80,12 +82,11 @@
},
onShow() {
// todo: set queryParams.userId by memberInfo
if (!this.memberInfo && this.userInfo.id) {
this.$store.commit('setMemberInfo', this.userInfo)
}
this.queryParams.userId = this.memberInfo.id
this.getData()
// this.queryParams.userId = this.memberInfo.id
// this.getData()
},
methods: {
getDataThen(records) {


+ 8
- 12
pages_order/center/orderCard.vue View File

@ -11,27 +11,23 @@
</template>
<script>
import { mapState } from 'vuex'
export default {
props: {
statistics: {
type: Object,
default() {
return {}
}
}
},
data() {
return {
}
},
computed: {
...mapState(['userCenterData']),
list() {
return [
{ id: '001', label: '待支付', value: this.statistics[0] || 0, index: 1, icon: '/pages_order/static/center/order-1.png' },
{ id: '002', label: '待发货', value: this.statistics[1] || 0, index: 2, icon: '/pages_order/static/center/order-2.png' },
{ id: '003', label: '待收货', value: this.statistics[2] || 0, index: 3, icon: '/pages_order/static/center/order-3.png' },
// 0 1 2
{ id: '001', label: '待支付', value: this.userCenterData.orderStatus0 || 0, index: 1, icon: '/pages_order/static/center/order-1.png' },
{ id: '002', label: '已支付', value: this.userCenterData.orderStatus1 || 0, index: 2, icon: '/pages_order/static/center/order-2.png' },
{ id: '003', label: '已完成', value: this.userCenterData.orderStatus2 || 0, index: 3, icon: '/pages_order/static/center/order-3.png' },
// todo: check
{ id: '004', label: '售后', value: this.statistics['afterSales'] || 0, index: 0, icon: '/pages_order/static/center/order-4.png' },
{ id: '004', label: '售后', value: 0, index: 0, icon: '/pages_order/static/center/order-4.png' },
{ id: '005', label: '全部', index: 0, icon: '/pages_order/static/center/order-5.png' },
]
}


+ 23
- 7
pages_order/comment/commentCard.vue View File

@ -5,10 +5,10 @@
<view class="flex left">
<view class="avatar">
<!-- todo: check key -->
<image class="avatar-img" :src="data.user.avatar" mode="scaleToFill"></image>
<image class="avatar-img" :src="data.user.headImage" mode="scaleToFill"></image>
</view>
<view class="info">
<view class="name">{{ data.user.name }}</view>
<view class="name">{{ data.user.nickName }}</view>
<view>{{ $dayjs(data.createTime).format('YYYY-MM-DD') }}</view>
</view>
</view>
@ -35,17 +35,17 @@
<formRate :value="data.teacherScore" :readonly="true"></formRate>
</view>
</view>
<view class="section operate">
<view class="flex section operate">
<button class="flex btn" @click="onComment">
<image class="icon" src="@/pages_order/static/comment/icon-comment.png" mode="aspectFill"></image>
<image class="icon" src="@/pages_order/static/comment/icon-comment.png" mode="widthFix"></image>
<view>评论</view>
</button>
<button v-if="data.liked" class="flex btn" @click="onLike">
<image class="icon" src="@/pages_order/static/comment/icon-like-active.png" mode="aspectFill"></image>
<image class="icon" src="@/pages_order/static/comment/icon-like-active.png" mode="widthFix"></image>
<view>点赞</view>
</button>
<button v-else class="flex btn" @click="onCancelLike">
<image class="icon" src="@/pages_order/static/comment/icon-like.png" mode="aspectFill"></image>
<image class="icon" src="@/pages_order/static/comment/icon-like.png" mode="widthFix"></image>
<view>点赞</view>
</button>
</view>
@ -89,7 +89,7 @@
images() {
const { image } = this.data || {}
return image?.split?.(',')
return image?.split?.(',')?.filter(val => val)
},
disabled() {
return this.mode !== 'edit'
@ -223,6 +223,22 @@
}
}
.operate {
justify-content: flex-start;
column-gap: 24rpx;
.btn {
column-gap: 8rpx;
font-size: 24rpx;
color: #999999;
.icon {
width: 32rpx;
height: auto;
}
}
}
.comment {
&-user {
column-gap: 16rpx;


+ 51
- 13
pages_order/comment/commentPopup.vue View File

@ -14,7 +14,7 @@
errorType="toast"
>
<view class="section">
<productCard :data="detail"></productCard>
<productCard :data="productCardData" style="background: linear-gradient(120deg, #DAF3FF, #FBFEFF 30%, #FBFEFF); border: 2rpx solid #FFFFFF;"></productCard>
</view>
<view class="section">
<view class="form-item">
@ -49,15 +49,15 @@
</view>
</view>
<view class="form-item">
<uv-form-item prop="content" :customStyle="formItemStyle">
<uv-form-item prop="contentIds" :customStyle="formItemStyle">
<view class="form-item-content">
<view class="tags">
<view
v-for="(item, oIdx) in options" :key="oIdx"
:class="['tag', item === form.content ? 'is-active' : '']"
@click="onSelectContent(item)"
v-for="(item, oIdx) in configList.commentOptionList" :key="oIdx"
:class="['tag', form.contentIds.includes(item.id) ? 'is-active' : '']"
@click="onSelectContent(item.id)"
>
{{ item }}
{{ item.title }}
</view>
</view>
</view>
@ -92,7 +92,7 @@
processScore: null,
spotScore: null,
teacherScore: null,
content: null,
contentIds: [],
},
rules: {
'processScore': {
@ -110,8 +110,8 @@
required: true,
message: '请为导师打分',
},
'content': {
type: 'string',
'contentIds': {
type: 'array',
required: true,
message: '请选择评语',
},
@ -120,11 +120,41 @@
options: [],
}
},
computed: {
productCardData() {
const {
activityId,
activityTitle,
activityBrief,
activityTag,
startDate,
endDate,
} = this.detail
return {
time: 'time',
product: {
id: activityId,
title: activityTitle,
brief: activityBrief,
tagDetails: activityTag,
dateList: [
{
id: 'time',
startDate,
endDate,
}
]
}
}
},
},
methods: {
async getData() {
// todo: fetch order product
},
async open(id, detail) {
console.log('open', id, detail)
this.id = id
this.detail = detail
@ -135,7 +165,7 @@
processScore: null,
spotScore: null,
teacherScore: null,
content: null,
contentIds: [],
}
this.$refs.popup.open()
@ -146,8 +176,9 @@
onPopupChange(e) {
this.isShow = e.show
},
onSelectContent(content) {
this.form.content = content
onSelectContent(id) {
let arr = this.form.contentIds
this.form.contentIds = arr.includes(id) ? arr.filter(item => item !== id) : arr.concat(id)
},
async onPublish() {
try {
@ -157,9 +188,15 @@
processScore,
spotScore,
teacherScore,
content,
contentIds,
} = this.form
const commentOptionList = this.configList.commentOptionList
const content = contentIds.map(id => {
return commentOptionList.find(item => item.id === id)?.title
}).join('、')
const params = {
orderId: this.id,
processScore,
@ -190,6 +227,7 @@
<style lang="scss" scoped>
.popup__view {
width: 100vw;
display: flex;
flex-direction: column;


+ 2
- 16
pages_order/comment/commentRecords.vue View File

@ -12,7 +12,7 @@
<image class="icon" src="@/static/image/icon-mark-highlight.png" mode="widthFix"></image>
</view>
<view class="title">
{{ `来自 ${item.productName}` }}
{{ `来自 ${item.activityTitle || ''}` }}
</view>
</view>
<commentCard :data="item"></commentCard>
@ -36,24 +36,10 @@
},
data() {
return {
// todo
// mixinsListApi: '',
list: [],
total: 0,
mixinsListApi: 'queryMyCommentList',
}
},
onLoad() {
this.getData()
},
methods: {
async getData() {
try {
this.list = await this.$fetch('queryMyCommentList')
this.total = this.list.length
} catch (err) {
}
},
},
}
</script>


+ 43
- 12
pages_order/comment/commentRecordsOfProduct.vue View File

@ -1,7 +1,7 @@
<template>
<view class="page__view">
<navbar title="全部评" leftClick @leftClick="$utils.navigateBack" bgColor="#FFFFFF" />
<navbar title="全部评" leftClick @leftClick="$utils.navigateBack" bgColor="#FFFFFF" />
<view class="main">
@ -28,19 +28,17 @@
},
data() {
return {
// todo
mixinsListApi: '',
queryParams: {
pageNo: 1,
pageSize: 10,
activityId: '',
},
mixinsListApi: 'queryCommentList',
}
},
onShow() {
console.log('onShow')
},
onLoad(arg) {
console.log('onLoad')
const { productId } = arg
this.queryParams.productId = productId
const { id } = arg
this.queryParams.activityId = id
this.getData()
},
methods: {
@ -63,8 +61,41 @@
border-radius: 24rpx;
&-item {
padding-bottom: 40rpx;
border-bottom: 2rpx solid #F5F5F5;
& + & {
margin-top: 16rpx;
margin-top: 40rpx;
}
&:last-child {
border: none;
}
&-header {
justify-content: flex-start;
column-gap: 24rpx;
margin-bottom: 14rpx;
.mark {
width: 36rpx;
height: 36rpx;
background: linear-gradient(to right, #21FEEC, #019AF9);
border-radius: 50%;
.icon {
width: 24rpx;
height: auto;
}
}
.title {
flex: 1;
font-size: 30rpx;
font-weight: 600;
line-height: 1.4;
color: #00A9FF;
}
}
}
}


+ 33
- 63
pages_order/comment/recordFormPopup.vue View File

@ -66,11 +66,11 @@
</view>
</uv-form-item>
</view>
<view class="form-item" v-for="(item, index) in questions" :key="item.id">
<view class="form-item" v-for="(item, index) in configList.experienceQuestionList" :key="item.id">
<uv-form-item :prop="`texts[${index}]`" :customStyle="formItemStyle">
<view class="form-item-label">
<image class="icon" src="@/pages_order/static/icon-require.png" mode="widthFix"></image>
{{ item.label }}
{{ item.question }}
</view>
<view class="form-item-content">
<formTextarea v-model="form.texts[index]"></formTextarea>
@ -114,11 +114,10 @@
texts: [],
},
projects: [],
questions: [],
}
},
computed: {
...mapState(['userInfo']),
...mapState(['userInfo', 'configList']),
projectDesc() {
const { activityId } = this.form
const target = this.projects?.find?.(item => item.id === activityId)
@ -127,53 +126,19 @@
},
},
methods: {
getData() {
// todo
this.projects = [
{
id: '001',
name: '亲子•坝上双草原6日 |乌兰布统+锡林郭勒+长城',
},
{
id: '002',
name: '青青草原•云中岭 |5-10公里AB线强度可选',
},
{
id: '003',
name: '新疆天山行7/9日丨醉美伊犁&吐鲁番双套餐',
},
{
id: '004',
name: '九色甘南|人间净土6日/7日深度游',
},
{
id: '005',
name: '北疆全景12日| 入疆首推!阿勒泰+伊犁+吐鲁番',
},
{
id: '006',
name: '塞上江南•神奇宁夏5日|穿越大漠与历史对话',
},
{
id: '007',
name: '尊享•天山环线9日| 伊犁全景+独库,头等舱大巴',
},
]
async fetchProjectOptions() {
try {
const records = (await this.$fetch('queryExperienceList', { pageNo: 1, pageSize: 1000, }))?.records
this.projects = records.map(item => {
return {
id: item.activityId,
name: item.activityTitle || item.activityId_dictText || ''
}
})
} catch (err) {
this.questions = [
{
id: '001',
label: '这次研学之旅,整体给你留下了怎样的印象?用几个词或几句话简单概括一下',
},
{
id: '002',
label: '在整个行程中,你最喜欢的部分是哪里?为什么?',
},
{
id: '003',
label: '你觉得这次研学的行程安排是否合理?有没有哪些地方让你觉得特别满意或需要改进的?',
},
]
}
},
setRules() {
const rules = {
@ -185,21 +150,24 @@
}
// todo: check
this.questions.forEach((item, index) => {
rules[`texts[${index}]`] = {
type: 'string',
required: true,
message: `请回答“${item.label}`,
}
})
this.$refs.form.setRules(this.rules)
// this.questions.forEach((item, index) => {
// rules[`texts[${index}]`] = {
// type: 'string',
// required: true,
// message: `${item.label}`,
// }
// })
this.$refs.form.setRules(rules)
},
async open() {
await this.getData()
await this.fetchProjectOptions()
console.log('projects', this.projects)
console.log('experienceQuestionList', this.configList.experienceQuestionList)
const texts = this.questions.map(() => '')
const texts = this.configList.experienceQuestionList.map(() => '')
this.form = {
activityId: null,
@ -219,10 +187,12 @@
this.isShow = e.show
// todo: need settimeout?
this.setRules()
setTimeout(() => {
this.setRules()
}, 800)
},
openRelatePojectPicker() {
this.$refs.reloateProjectPopup.open(this.form.activityId?.id || null)
this.$refs.reloateProjectPopup.open(this.form.activityId || null)
},
onRelateProjectChange(id) {
this.form.activityId = id


+ 2
- 0
pages_order/components/reloateProjectPopup.vue View File

@ -49,6 +49,8 @@
methods: {
async open(projectId) {
this.selectedId = projectId
console.log('selectedId', this.selectedId)
console.log('options', this.options)
this.$refs.popup.open()
},
close() {


+ 3
- 3
pages_order/coupon/couponList/couponCard.vue View File

@ -1,11 +1,11 @@
<template>
<view class="flex card">
<view class="flex price">
¥<view class="highlight">{{ data.price }}</view>
¥<view class="highlight">{{ data.discountAmount }}</view>
</view>
<view class="flex flex-column info">
<view class="title">{{ data.label }}</view>
<view class="desc">{{ `有效期至 ${data.validTime}` }}</view>
<view class="title">{{ data.title }}</view>
<view class="desc">{{ `有效期至 ${$dayjs(data.validDate).format('YYYY-MM-DD')}` }}</view>
<button class="btn" @click="jumpToCategory">去使用</button>
</view>
</view>


+ 10
- 7
pages_order/message/card.vue View File

@ -5,12 +5,12 @@
<view class="desc">{{ data.createTime }}</view>
</view>
<view class="card-content">
{{ data.content }}
<uv-parse :content="data.content"></uv-parse>
</view>
<view class="card-footer">
<button class="btn">了解详情</button>
<button class="btn" @click="jumpToDetail">了解详情</button>
</view>
<view v-if="data.new" class="dot"></view>
<view v-if="data.isRead == '0'" class="dot"></view>
</view>
</template>
@ -41,7 +41,9 @@ export default {
font-weight: 400;
line-height: 1.4;
background-image: linear-gradient(164deg,#DAF3FF 88rpx, #FBFEFF 176rpx, #FBFEFF);
background: linear-gradient(110deg, #DAF3FF, #FBFEFF 25%, #FBFEFF);
border: 2rpx solid #FFFFFF;
border-radius: 32rpx;
&-header {
.title {
@ -58,14 +60,15 @@ export default {
&-content {
margin-top: 16rpx;
max-height: 176rpx;
font-size: 26rpx;
line-height: 1.7;
color: #666666;
overflow: hidden;
text-overflow: ellipsis;
display:-webkit-box; //
-webkit-box-orient:vertical; //--
-webkit-line-clamp:4; //
// text-overflow: ellipsis;
// display:-webkit-box; //
// -webkit-box-orient:vertical; //--
// -webkit-line-clamp:4; //
}
&-footer {


+ 15
- 5
pages_order/message/index.vue View File

@ -1,6 +1,6 @@
<template>
<view class="page__view">
<navbar title="详情" leftClick @leftClick="$utils.navigateBack" />
<navbar title="详情" leftClick @leftClick="$utils.navigateBack" bgColor="#FFFFFF" />
<view class="main">
<view class="card">
@ -9,18 +9,21 @@
<view class="desc">{{ detail.createTime }}</view>
</view>
<view class="card-content">
{{ detail.content }}
<uv-parse :content="detail.content"></uv-parse>
</view>
</view>
</view>
<view class="bottom">
<view class="flex bottom">
<button plain class="flex flex-column btn btn-simple" open-type="contact">
<image class="icon" src="@/pages_order/static/product/icon-service.png" mode="widthFix"></image>
<view>联系客服</view>
</button>
<button class="col btn" @click="onContactMentor">联系导师</button>
</view>
<contactMentorPopup ref="contactMentorPopup" ></contactMentorPopup>
</view>
</template>
@ -45,9 +48,15 @@
},
methods: {
async getData() {
// todo
// todo: mark read
try {
this.detail = await this.$fetch('queryNoticeById', { noticeId: this.id })
} catch (err) {
}
},
onContactMentor() {
this.$refs.contactMentorPopup.open(this.detail.teacherPhone)
},
},
}
@ -58,6 +67,7 @@
.page__view {
width: 100vw;
min-height: 100vh;
background: $uni-bg-color;
position: relative;
/deep/ .nav-bar__view {


+ 1
- 2
pages_order/message/list.vue View File

@ -25,8 +25,7 @@
},
data() {
return {
// todo: check key
mixinsListApi: '',
mixinsListApi: 'queryNoticeList',
}
},
methods: {


+ 2
- 15
pages_order/order/components/orderInfoView.vue View File

@ -23,7 +23,7 @@
<view class="row-content">
<view class="flex total-price">
<view>¥</view>
<view class="highlight">{{ totalPrice }}</view>
<view class="highlight">{{ data.payAmount }}</view>
<view class="light" v-if="data.discount">
{{ `优惠(${data.discount}` }}
</view>
@ -46,7 +46,7 @@
<view class="card-header">订单信息</view>
<view class="row">
<view class="row-label">订单编号</view>
<view class="row-content">{{ data.orderNo }}</view>
<view class="row-content">{{ data.id }}</view>
</view>
<view class="row">
<view class="row-label">下单时间</view>
@ -94,19 +94,6 @@
return timeOptions?.find?.(item => item.id === time) || {}
},
totalPrice() {
const { adults, teenager, child } = this.data
const { adultsPrice, teenagerPrice, childPrice } = this.productPackage
let total = 0
adults && (total += adults * (adultsPrice || 0))
teenager && (total += teenager * (teenagerPrice || 0))
child && (total += child * (childPrice || 0))
return total
},
members() {
const { members } = this.data
const { adultsPrice, teenagerPrice, childPrice } = this.productPackage


+ 24
- 8
pages_order/order/components/productCard.vue View File

@ -1,10 +1,10 @@
<template>
<view class="card info">
<view class="card-header">{{ product.name }}</view>
<view class="card info" :style="style">
<view class="card-header">{{ product.title }}</view>
<view class="card-content">
<view class="row desc">{{ product.desc }}</view>
<view class="flex row tags" v-if="product.tags && product.tags.length">
<view class="tag" v-for="(tag, tIdx) in product.tags" :key="tIdx">
<view class="row desc">{{ product.brief }}</view>
<view class="flex row tags" v-if="tagList.length">
<view class="tag" v-for="(tag, tIdx) in tagList" :key="tIdx">
{{ tag }}
</view>
</view>
@ -45,11 +45,17 @@
product() {
return this.data?.product || {}
},
tagList() {
// todo: check key
const { tagDetails } = this.product || {}
return tagDetails?.length ? tagDetails.split('、') : []
},
productPackage() {
const { time, product } = this.data
const { timeOptions } = product || {}
const { time, product } = this.data || {}
const { dateList } = product || {}
return timeOptions?.find?.(item => item.id === time) || {}
return dateList?.find?.(item => item.id === time) || {}
},
days() {
console.log('productPackage', this.productPackage)
@ -58,6 +64,15 @@
return this.$dayjs(endDate).diff(this.$dayjs(startDate), 'day')
},
},
watch: {
data: {
handler(val) {
console.log('watch data', val)
},
deep: true,
immediate: true
},
},
}
</script>
@ -92,6 +107,7 @@
.desc {
font-size: 26rpx;
white-space: pre-wrap;
color: #8B8B8B;
}


+ 75
- 114
pages_order/order/orderConfirm/index.vue View File

@ -40,15 +40,10 @@
<view class="card-header">订单详情</view>
<view style="margin-top: 16rpx;">
<uv-form-item prop="members" :customStyle="formItemStyle">
<!-- style="width: calc(100vw - 40rpx*2);" -->
<view>
<peopleNumberInput
:adults.sync="form.adults"
:teenager.sync="form.teenager"
:child.sync="form.child"
:adultsPrice="productPackage.adultsPrice"
:teenagerPrice="productPackage.teenagerPrice"
:childPrice="productPackage.childPrice"
<peopleNumberInput style="width: calc(100vw - 40rpx*2);"
v-model="form.prices"
:options="orderInfo.priceList"
></peopleNumberInput>
<memberChooseView
@ -65,7 +60,7 @@
<view class="form-item-label">选择优惠券</view>
<view class="form-item-content">
<view class="flex row" @click="jumpToSelectCoupon">
<view v-if="form.couponId" class="text">{{ couponInfo.label }}</view>
<view v-if="form.couponId" class="text">{{ couponInfo.title }}</view>
<view v-else class="text placeholder">请选择</view>
<uv-icon name="arrow-right" color="#C6C6C6" size="32rpx"></uv-icon>
</view>
@ -161,14 +156,41 @@
data() {
return {
form: {
name: null,
phone: null,
adults: 0,
teenager: 0,
child: 0,
name: '',
phone: '',
prices: [],
members: [],
couponId: null,
receiptId: null,
couponId: '',
receiptId: '',
},
rules: {
'name': {
type: 'string',
required: true,
message: '请输入真实姓名',
},
'phone': {
type: 'string',
required: true,
message: '请输入手机号码',
},
'prices': {
type: 'array',
message: '请选择人数',
validator: (rule, value, callback) => {
if (value.some(num => num > 0)) {
return true
}
return false
},
},
'members': {
type: 'array',
required: true,
message: '请选择出行人',
},
},
checkboxValue: [],
formItemStyle: { padding: 0 },
@ -178,39 +200,31 @@
...mapState(['configList', 'userInfo', 'orderInfo', 'couponInfo']),
productPackage() {
const { time, product } = this.orderInfo
const { timeOptions } = product || {}
const { dateList } = product || {}
return timeOptions?.find?.(item => item.id === time) || {}
return dateList?.find?.(item => item.id === time) || {}
},
totolPeople() {
const { adults, teenager, child } = this.form
const { prices } = this.orderInfo
return (adults || 0) + (teenager || 0) + (child || 0)
return prices.reduce((total, num) => {
return total + num
}, 0)
},
totalPrice() {
const { adults, teenager, child, couponId } = this.form
const { adultsPrice, teenagerPrice, childPrice } = this.productPackage
priceOrigin() {
const { prices, priceList } = this.orderInfo
let total = 0
adults && (total += adults * (adultsPrice || 0))
teenager && (total += teenager * (teenagerPrice || 0))
child && (total += child * (childPrice || 0))
couponId && (total -= (this.couponInfo?.price || 0))
return total
return prices.reduce((total, num, index) => {
return total + priceList[index].price * (num || 0)
}, 0)
},
discount() {
return this.couponInfo?.discountAmount || 0
},
totalPrice() {
return this.priceOrigin - this.discount
},
},
watch: {
form: {
handler(val) {
this.$refs.form.setRules(this.getRules())
},
deep: true
}
},
onShow() {
if (this.couponInfo) {
this.form.couponId = this.couponInfo.id
@ -221,65 +235,27 @@
console.log('orderInfo', this.orderInfo)
this.initData()
},
onReady() {
this.$refs.form.setRules(this.getRules())
},
onReady() {
this.$refs.form.setRules(this.rules);
},
onUnload() {
this.$store.commit('setCouponInfo', null)
},
methods: {
getRules() {
const { adults, teenager, child } = this.form
return {
'name': {
type: 'string',
required: true,
message: '请输入真实姓名',
},
'phone': {
type: 'string',
required: true,
message: '请输入手机号码',
},
'adults': {
type: 'number',
required: true,
message: '请选择人数',
validator: (rule, value, callback) => {
if (adults || teenager || child) {
return true
}
return false
},
},
'members': {
type: 'array',
required: true,
message: '请选择出行人',
},
}
},
initData() {
const {
time,
adults,
teenager,
child,
prices,
members,
} = this.orderInfo
this.form = {
name: null,
phone: null,
adults,
teenager,
child,
name: '',
phone: '',
prices,
members: members.map(item => ({ ...item, isSelected: true })),
couponId: null,
receiptId: null,
couponId: '',
receiptId: '',
}
},
jumpToSelectCoupon() {
@ -323,52 +299,37 @@
const {
name,
phone,
adults,
teenager,
child,
prices,
members,
couponId,
receiptId,
} = this.form
const { startDate, endDate } = time
const { startDate, endDate } = this.productPackage
let params = {
activityId: product.id,
startDate,
endDate,
dayNum: this.$dayjs(endDate).diff(this.$dayjs(startDate), 'day'),
couponId,
receiptId,
name,
phone,
priceOrigin: this.priceOrigin,
discount: this.discount,
priceDiscount: this.totalPrice,
payAmount: this.totalPrice,
tourisIds: members.map(touris => touris.id).join(',')
}
const result = await this.$fetch('createOrder', params)
// todo: check result includes order id?
const orderId = await this.$fetch('createOrder', params)
const orderInfo = {
product,
couponInfo: this.couponInfo,
time,
adults,
teenager,
child,
members,
couponId,
receiptId,
name,
phone,
}
this.$store.commit('setOrderInfo', orderInfo)
// todo: get id?
uni.navigateTo({
url: `/pages_order/order/orderPay/index?id=${result.id}`
url: `/pages_order/order/orderPay/index?id=${orderId}`
})
} catch (err) {
console.log('createOrder', err)
}
},


+ 32
- 45
pages_order/order/orderConfirm/infoPopup.vue View File

@ -8,6 +8,7 @@
<uv-form
ref="form"
:model="form"
:rules="rules"
errorType="toast"
>
<view class="section">
@ -101,6 +102,30 @@
prices: [],
members: [],
},
rules: {
'time': {
type: 'string',
required: true,
message: '请选择团期',
},
'prices': {
type: 'array',
message: '请选择人数',
validator: (rule, value, callback) => {
if (value.some(num => num > 0)) {
return true
}
return false
},
},
'members': {
type: 'array',
required: true,
message: '请选择出行人',
},
},
formItemStyle: { padding: 0 },
}
},
@ -130,52 +155,17 @@
this.$store.commit('setTravelerList', [])
}
},
form: {
handler(val) {
this.$refs.form.setRules(this.getRules())
},
deep: true
}
},
onReady() {
this.$refs.form.setRules(this.getRules())
},
onReady() {
this.$refs.form.setRules(this.rules);
},
methods: {
getRules() {
const { adults, teenager, child } = this.form
return {
'time': {
type: 'string',
required: true,
message: '请选择团期',
},
'prices': {
type: 'array',
message: '请选择人数',
validator: (rule, value, callback) => {
if (value.some(num => num > 0)) {
return true
}
return false
},
},
'members': {
type: 'array',
required: true,
message: '请选择出行人',
},
}
},
openTimePicker() {
this.$refs.timeCalendarSelect.open()
},
async getDefaultMembers() {
try {
// todo: check params
return (await this.$fetch('queryTouristList', { pageNo: 1, pageSize: 1000, userId: this.userInfo.id, isDefault: '1' })).records
return (await this.$fetch('queryTouristList', { pageNo: 1, pageSize: 1000, isDefault: '1' })).records
} catch (err) {
return []
}
@ -208,18 +198,15 @@
const {
time,
adults,
teenager,
child,
prices,
members,
} = this.form
const orderInfo = {
product: this.data,
priceList: this.priceList,
time,
adults,
teenager,
child,
prices,
members,
}
this.$store.commit('setOrderInfo', orderInfo)


+ 14
- 8
pages_order/order/orderConfirm/memberCard.vue View File

@ -16,7 +16,7 @@
<view class="info">
<view class="flex name">
<view>{{ data.name }}</view>
<view :class="['tag', `tag-${data.type}`]">{{ typeDesc }}</view>
<view :class="['tag', `tag-${type}`]">{{ typeDesc }}</view>
</view>
<view class="id">{{ data.cerNo }}</view>
</view>
@ -24,12 +24,6 @@
</template>
<script>
const MEMBER_TYPE_AND_DESC_MAPPING = {
0: '成人',
1: '青少年',
2: '儿童',
}
export default {
props: {
@ -61,7 +55,19 @@
}
},
typeDesc() {
return MEMBER_TYPE_AND_DESC_MAPPING[this.data.type]
const { periodId } = this.data
return this.configList.periodList?.find?.(item => item.id === periodId)?.title
},
type() {
if (this.typeDesc == '青少年') {
return 1
}
if (this.typeDesc == '儿童') {
return 2
}
return 0
},
},
watch: {


+ 42
- 155
pages_order/order/orderDetail/index.vue View File

@ -4,7 +4,7 @@
<view class="main">
<productCard :data="detail"></productCard>
<productCard :data="productCardData"></productCard>
<orderInfoView :data="detail"></orderInfoView>
@ -19,30 +19,6 @@
</view>
<view class="bottom">
<view class="agreement">
<uv-checkbox-group
v-model="checkboxValue"
shape="circle"
>
<uv-checkbox
size="40rpx"
icon-size="40rpx"
activeColor="#00A9FF"
:name="1"
></uv-checkbox>
</uv-checkbox-group>
<view class="desc">
我已阅读并同意
<!-- todo: 替换配置项key -->
<text class="highlight" @click="$refs.modal.open('config_agreement', '退订政策')">退订政策</text>
<!-- todo: 替换配置项key -->
<text class="highlight" @click="$refs.modal.open('config_privacy', '合同范本')">合同范本</text>
<!-- todo: 替换配置项key -->
<text class="highlight" @click="$refs.modal.open('config_privacy', '预订须知')">预订须知</text>
<!-- todo: 替换配置项key -->
<text class="highlight" @click="$refs.modal.open('config_privacy', '安全提示')">安全提示</text>
</view>
</view>
<view class="flex bar">
<button plain class="flex flex-column btn btn-simple" open-type="contact">
<image class="icon" src="@/pages_order/static/product/icon-service.png" mode="widthFix"></image>
@ -51,15 +27,14 @@
<!-- 已支付 -->
<template v-if="detail.status == 1">
<button class="col btn" @click="onContactMentor">联系导师</button>
<button class="col btn" @click.stop="onFinish">已完成</button>
</template>
<!-- 待评价 -->
<template v-else-if="data.status == 2">
<button class="btn" @click="onComment">立即评价</button>
</template>
<!-- 已完成 -->
<template v-else-if="detail.status == 3">
<button class="col btn" @click="onComment">立即评价</button>
<button class="col btn" @click="onApplyService">申请售后</button>
</template>
<view class="col" v-else></view>
</view>
</view>
@ -67,8 +42,6 @@
<commentPopup ref="commentPopup" @submitted="getData"></commentPopup>
<agreementModal ref="modal" @confirm="onConfirmAgreement"></agreementModal>
</view>
</template>
@ -79,7 +52,6 @@
import orderInfoView from '@/pages_order/order/components/orderInfoView.vue'
import contactMentorPopup from '@/pages_order/order/components/contactMentorPopup.vue'
import commentPopup from '@/pages_order/comment/commentPopup.vue'
import agreementModal from '@/pages_order/components/agreementModal.vue'
export default {
components: {
@ -87,7 +59,6 @@
orderInfoView,
contactMentorPopup,
commentPopup,
agreementModal,
},
data() {
return {
@ -97,27 +68,33 @@
},
computed: {
...mapState(['configList', 'userInfo', 'orderInfo']),
productPackage() {
const { time, product } = this.detail
const { timeOptions } = product || {}
return timeOptions?.find?.(item => item.id === time) || {}
productCardData() {
const {
activityId,
activityTitle,
activityBrief,
activityTag,
startDate,
endDate,
} = this.detail
return {
time: 'time',
product: {
id: activityId,
title: activityTitle,
brief: activityBrief,
tagDetails: activityTag,
dateList: [
{
id: 'time',
startDate,
endDate,
}
]
}
}
},
totalPrice() {
const { adults, teenager, child, coupon, couponInfo } = this.detail
const { adultsPrice, teenagerPrice, childPrice } = this.productPackage
let total = 0
adults && (total += adults * (adultsPrice || 0))
teenager && (total += teenager * (teenagerPrice || 0))
child && (total += child * (childPrice || 0))
coupon && (total -= (couponInfo?.price || 0))
return total
},
},
onLoad(arg) {
const { id } = arg
@ -125,8 +102,9 @@
this.getData()
},
onPullDownRefresh() {
this.getData()
async onPullDownRefresh() {
await this.getData()
uni.stopPullDownRefresh()
},
methods: {
async getData() {
@ -134,105 +112,6 @@
this.detail = await this.$fetch('queryOrderById', { orderId: this.id })
return
this.detail = {
product: {
id: '001',
image: new Array(6).fill('/static/image/temp-20.png').join(','),
name: '新疆天山行7/9日丨醉美伊犁&吐鲁番双套餐',
desc: '每天车程4小时内,含一程高铁丨喀拉峻草原、夏塔古道、昭苏天马、赛里木湖、昭苏油菜花、伊犁薰衣草丨吐鲁番坎儿井&火焰山',
tags: ['坝上草原', '自然探索', '户外探索', '亲子游玩'],
priceDiscount: 688.99,
priceOrigin: 1200,
registered: 4168,
timeOptions: [
{
id: '0011',
startDate: '08/25',
endDate: '09/01',
priceDiscount: 1200.99,
priceOrigin: 2300,
adultsPrice: 2400,
teenagerPrice: 1800,
childPrice: 1200.99,
},
{
id: '0012',
startDate: '09/02',
endDate: '09/11',
priceDiscount: 1200.99,
priceOrigin: 2300,
adultsPrice: 2400,
teenagerPrice: 1800,
childPrice: 1200.99,
},
{
id: '0013',
startDate: '09/12',
endDate: '09/19',
priceDiscount: 1200.99,
priceOrigin: 2300,
adultsPrice: 2400,
teenagerPrice: 1800,
childPrice: 1200.99,
},
],
},
time: "0012",
adults: 2,
teenager: 1,
child: 1,
members: [
{
id: "001",
cerNo: "430223********9999",
isDefault: false,
isSelected: true,
name: "李梓发",
type: 0,
},
{
id: "002",
cerNo: "430223********9999",
isDefault: false,
isSelected: true,
name: "吴彦谦",
type: 0,
},
{
id: "003",
cerNo: "430223********9999",
isDefault: false,
isSelected: true,
name: "冯云",
type: 1,
},
{
id: "004",
cerNo: "430223********9999",
isDefault: false,
isSelected: true,
name: "冯思钗",
type: 2,
},
],
coupon: "001",
discount: 88,
invoice: null,
name: "测试",
phone: "13345678910",
orderNo: 'BH872381728321983929',
createTime: '2025-04-28 08:14',
mentorPhone: '0731-599327-8899',
status: 1,
}
console.log('orderInfo', this.detail)
// todo: check fetch by id?
// this.detail = await this.$fetch('queryOrderById', { id: this.id })
} catch (err) {
}
@ -246,7 +125,15 @@
// todo
},
onContactMentor() {
this.$refs.contactMentorPopup.open(this.detail.mentorPhone)
this.$refs.contactMentorPopup.open(this.detail.teacherPhone)
},
async onFinish() {
try {
await this.$fetch('updateOrder', { id: this.id, status: '2' }) // 0 1 2
this.getData()
} catch (err) {
}
},
},
}


+ 7
- 7
pages_order/order/orderList/index.vue View File

@ -44,7 +44,7 @@
</view>
<contactMentorPopup ref="contactMentorPopup" :phone="data.mentorPhone"></contactMentorPopup>
<contactMentorPopup ref="contactMentorPopup" ></contactMentorPopup>
<commentPopup ref="commentPopup" @submitted="getData"></commentPopup>
@ -68,13 +68,13 @@
},
data() {
return {
// 0 1 2 3 4
// 0 1 2 // isService0- 1-
tabs: [
{ name: '全部' },
{ name: '待支付' },
{ name: '待发货' },
{ name: '待收货' },
{ name: '待评价' },
{ name: '已支付' },
{ name: '已完成' },
{ name: '售后' },
],
mixinsListApi: 'queryOrderList',
current: 0,
@ -106,8 +106,8 @@
// todo
// this.$refs.serviceSelectPopup.open(obj)
},
onContactMentor(mentorPhone) {
this.$refs.contactMentorPopup.open(mentorPhone)
onContactMentor(teacherPhone) {
this.$refs.contactMentorPopup.open(teacherPhone)
},
},
}


+ 27
- 30
pages_order/order/orderList/orderCard.vue View File

@ -1,29 +1,30 @@
<template>
<view class="card" @click="jumpToOrderDetail">
<view class="flex top">
{{ data.title }}
<view class="top">
{{ data.activityTitle }}
</view>
<view :class="['tag', `tag-${data.status}`]">
{{ statusDesc }}
{{ data.status_dictText }}
</view>
<view class="flex row">
<view class="row-label">联系人</view>
<view class="row-content">{{ data.customerName }}</view>
<view class="row-content">{{ data.name }}</view>
</view>
<view class="flex row">
<view class="row-label">下单时间</view>
<view class="row-content">{{ data.orderDate }}</view>
<view class="row-content">{{ data.createTime }}</view>
</view>
<view class="flex row">
<view class="row-label">联系电话</view>
<view class="row-content">{{ data.customerPhone }}</view>
<view class="row-content">{{ data.phone }}</view>
</view>
<view class="bottom">
<view class="flex bottom">
<view class="flex row">
<text class="row-label">总价格</text>
<text class="flex row-content price">¥</text><text class="highlight">{{ data.orderAmount }}</text>
<text class="flex row-content price">¥<text class="highlight">{{ data.payAmount }}</text></text>
</view>
<view class="flex btns">
<!-- 订单状态 0待支付 1已支付 2已完成 -->
<!-- 待支付 -->
<template v-if="data.status == 0">
<button class="btn" @click.stop="onPay">立即支付</button>
@ -31,32 +32,24 @@
<!-- 已支付 -->
<template v-else-if="data.status == 1">
<button class="btn" @click.stop="onContactMentor">联系导师</button>
</template>
<!-- 待评价 -->
<template v-else-if="data.status == 2">
<button class="btn" @click.stop="onComment">立即评价</button>
<button class="btn" @click.stop="onFinish">已完成</button>
</template>
<!-- 已完成 -->
<template v-else-if="data.status == 3">
<template v-else-if="data.status == 2">
<button class="btn" @click.stop="onApplyService">申请售后</button>
<button class="btn" @click.stop="onComment">立即评价</button>
</template>
<!-- 售后 -->
<template v-else-if="data.status == 4">
<!-- todo -->
<!-- <template v-else-if="data.status == 4">
</template>
</template> -->
</view>
</view>
</view>
</template>
<script>
const STATUS_AND_DESC_MAPPING = {
0: '待支付',
1: '已支付',
2: '待评价',
3: '已完成',
4: '售后',
}
export default {
props: {
@ -67,18 +60,12 @@
}
}
},
computed: {
statusDesc() {
const { status } = this.data
return STATUS_AND_DESC_MAPPING[status]
},
},
methods: {
onPay() {
this.$utils.navigateTo(`/pages_order/order/orderPay/index?id=${this.data.id}`)
},
onContactMentor() {
this.$emit('contatcMentor', this.data.mentorPhone)
this.$emit('contatcMentor', this.data.teacherPhone)
},
onComment() {
this.$emit('comment')
@ -97,6 +84,15 @@
this.$utils.navigateTo(`/pages_order/order/orderDetail/index?id=${this.data.id}`)
}
},
async onFinish() {
try {
await this.$fetch('updateOrder', { id: this.data.id, status: '2' }) // 0 1 2
this.$emit('statusChange')
} catch (err) {
}
},
},
}
</script>
@ -129,6 +125,7 @@
}
.row {
margin-top: 16rpx;
justify-content: flex-start;
column-gap: 4rpx;
@ -147,10 +144,10 @@
}
.row + .row {
margin-top: 16rpx;
}
.bottom {
margin-top: 16rpx;
justify-content: space-between;
.price {


+ 87
- 133
pages_order/order/orderPay/index.vue View File

@ -3,18 +3,26 @@
<navbar leftClick @leftClick="$utils.navigateBack" >
<view class="flex">
<view>待支付</view>
<view>
<view>剩余</view>
<!-- todo -->
<view>10:00</view>
</view>
<view>后关闭</view>
<template v-if="time">
<uv-count-down
:time="time"
format="mm:ss"
autoStart
millisecond
@change="onChange">
<view class="flex countdown">
<text>剩余</text>
<text>{{ `${timeData.minutes}:${timeData.seconds}` }}</text>
</view>
</uv-count-down>
<view>后关闭</view>
</template>
</view>
</navbar>
<view class="main">
<productCard :data="detail"></productCard>
<productCard :data="productCardData"></productCard>
<orderInfoView :data="detail"></orderInfoView>
@ -64,7 +72,7 @@
</view>
<view class="price-unit">¥</view>
<view class="price-value">
{{ totalPrice }}
{{ detail.payAmount }}
</view>
</view>
<button class="col btn btn-primary" @click="onPay">立即支付</button>
@ -93,150 +101,80 @@
return {
id: null,
detail: {},
time: null,
timeData: {},
checkboxValue: [],
}
},
computed: {
...mapState(['configList', 'userInfo', 'orderInfo']),
productPackage() {
const { time, product } = this.detail
const { timeOptions } = product || {}
productCardData() {
const {
activityId,
activityTitle,
activityBrief,
activityTag,
startDate,
endDate,
} = this.detail
return timeOptions?.find?.(item => item.id === time) || {}
return {
time: 'time',
product: {
id: activityId,
title: activityTitle,
brief: activityBrief,
tagDetails: activityTag,
dateList: [
{
id: 'time',
startDate,
endDate,
}
]
}
}
},
totalPrice() {
const { adults, teenager, child, coupon, couponInfo } = this.detail
const { adultsPrice, teenagerPrice, childPrice } = this.productPackage
let total = 0
adults && (total += adults * (adultsPrice || 0))
teenager && (total += teenager * (teenagerPrice || 0))
child && (total += child * (childPrice || 0))
coupon && (total -= (couponInfo?.price || 0))
return total
},
},
onLoad({ id }) {
async onLoad({ id }) {
this.id = id
// console.log('orderInfo', this.orderInfo)
// this.detail = this.orderInfo
// todo: check fetch by id?
this.fetchOrderDetail()
await this.fetchOrderDetail()
// todo
// this.updateCountdown()
},
onPullDownRefresh() {
this.fetchOrderDetail()
async onPullDownRefresh() {
await this.fetchOrderDetail()
uni.stopPullDownRefresh()
},
methods: {
async fetchOrderDetail() {
try {
this.detail = {
product: {
id: '001',
image: new Array(6).fill('/static/image/temp-20.png').join(','),
name: '新疆天山行7/9日丨醉美伊犁&吐鲁番双套餐',
desc: '每天车程4小时内,含一程高铁丨喀拉峻草原、夏塔古道、昭苏天马、赛里木湖、昭苏油菜花、伊犁薰衣草丨吐鲁番坎儿井&火焰山',
tags: ['坝上草原', '自然探索', '户外探索', '亲子游玩'],
priceDiscount: 688.99,
priceOrigin: 1200,
registered: 4168,
timeOptions: [
{
id: '0011',
startDate: '08/25',
endDate: '09/01',
priceDiscount: 1200.99,
priceOrigin: 2300,
adultsPrice: 2400,
teenagerPrice: 1800,
childPrice: 1200.99,
},
{
id: '0012',
startDate: '09/02',
endDate: '09/11',
priceDiscount: 1200.99,
priceOrigin: 2300,
adultsPrice: 2400,
teenagerPrice: 1800,
childPrice: 1200.99,
},
{
id: '0013',
startDate: '09/12',
endDate: '09/19',
priceDiscount: 1200.99,
priceOrigin: 2300,
adultsPrice: 2400,
teenagerPrice: 1800,
childPrice: 1200.99,
},
],
},
time: "0012",
adults: 2,
teenager: 1,
child: 1,
members: [
{
id: "001",
cerNo: "430223********9999",
isDefault: false,
isSelected: true,
name: "李梓发",
type: 0,
},
{
id: "002",
cerNo: "430223********9999",
isDefault: false,
isSelected: true,
name: "吴彦谦",
type: 0,
},
{
id: "003",
cerNo: "430223********9999",
isDefault: false,
isSelected: true,
name: "冯云",
type: 1,
},
{
id: "004",
cerNo: "430223********9999",
isDefault: false,
isSelected: true,
name: "冯思钗",
type: 2,
},
],
coupon: "001",
discount: 88,
invoice: null,
name: "测试",
phone: "13345678910",
orderNo: 'BH872381728321983929',
createTime: '2025-04-28 08:14',
status: 0,
}
console.log('orderInfo', this.detail)
try {
// todo: check fetch by id?
// this.detail = await this.$fetch('queryOrderById', { id: this.id })
this.detail = await this.$fetch('queryOrderById', { orderId: this.id })
console.log('orderInfo', this.detail)
} catch (err) {
} catch (err) {
}
}
uni.stopPullDownRefresh()
uni.stopPullDownRefresh()
},
onConfirmAgreement(confirm) {
if (confirm) {
this.checkboxValue = [1]
} else {
this.checkboxValue = []
}
},
async onPay() {
if(!this.checkboxValue.length){
return uni.showToast({
title: '请先同意《退订政策》《合同范本》《预订须知》《安全提示》',
icon:'none'
})
}
try {
const result = await this.$fetch('payOrder', { orderId: this.id })
@ -258,6 +196,22 @@
}
},
updateCountdown() {
let current = dayjs()
let startTime = dayjs(this.detail.createTime)
if (startTime.isSameOrBefore(current)) {
this.time = null
return
}
this.time = startTime.diff(current, 'second')
},
onChange(e) {
this.timeData = e
},
},
}
</script>


+ 5
- 5
pages_order/product/commentList.vue View File

@ -3,15 +3,15 @@
<view class="list-item" v-for="item in list" :key="item.id">
<view class="flex user">
<view class="avatar">
<image class="img" :src="item.avatar" mode="scaleToFill"></image>
<image class="img" :src="item.user.headImage" mode="scaleToFill"></image>
</view>
<view class="name">{{ item.name }}</view>
<view class="time">{{ item.createTime }}</view>
<view class="name">{{ item.user.nickName }}</view>
<view class="time">{{ $dayjs(item.createTime).format('YYYY-MM-DD') }}</view>
</view>
<view class="flex content">
<view class="content-text">{{ item.content }}</view>
<view class="content-img">
<image class="img" v-if="getCoverImg(item)" :src="getCoverImg(item)" mode="aspectFill"></image>
<view class="content-img" v-if="getCoverImg(item)">
<image class="img" :src="getCoverImg(item)" mode="aspectFill"></image>
</view>
</view>
</view>


+ 16
- 83
pages_order/product/productDetail.vue View File

@ -196,88 +196,21 @@
} catch (err) {
}
return
// todo: delete
this.detail = {
id: '001',
image: new Array(6).fill('/static/image/temp-20.png').join(','),
title: '新疆天山行7/9日丨醉美伊犁&吐鲁番双套餐',
brief: '每天车程4小时内,含一程高铁丨喀拉峻草原、夏塔古道、昭苏天马、赛里木湖、昭苏油菜花、伊犁薰衣草丨吐鲁番坎儿井&火焰山',
tagDetails: ['坝上草原', '自然探索', '户外探索', '亲子游玩'],
priceDiscount: 688.99,
priceOrigin: 1200,
applyNum: 4168,
timeOptions: [
{
id: '0011',
startDate: '08/25',
endDate: '09/01',
priceDiscount: 1200.99,
priceOrigin: 2300,
adultsPrice: 2400,
teenagerPrice: 1800,
childPrice: 1200.99,
},
{
id: '0012',
startDate: '09/02',
endDate: '09/11',
priceDiscount: 1200.99,
priceOrigin: 2300,
adultsPrice: 2400,
teenagerPrice: 1800,
childPrice: 1200.99,
},
{
id: '0013',
startDate: '09/12',
endDate: '09/19',
priceDiscount: 1200.99,
priceOrigin: 2300,
adultsPrice: 2400,
teenagerPrice: 1800,
childPrice: 1200.99,
},
],
special: `
<p style="font-size: 36rpx;">
行程亮点
</p>
`,
target: `
<p style="font-size: 36rpx;">
课程目标
<p>
`,
process: `
<p style="font-size: 36rpx;">
详细行程
<p>
`,
}
},
async fetchComment(id) {
// todo: fetch by activity id
this.commentList = [
{
avatar: '/pages_order/static/temp-30.png',
name: '战斗世界',
createTime: '2025-07-12',
content: '凌玉姐姐很温柔很耐心很负责我很喜欢她龙哥知识渊博很幽默给我们讲解很多内容行程很有趣我学到了很多东西最难忘的就是库木塔格沙漠我们爬到了很高的顶端看夕阳绝美还有我也很喜欢夏塔古道我们爬到了第四个卡拉房子的最远端看到了壮观的雪山下次还想参加活动去南疆',
image: '/pages_order/static/temp-38.png',
},
{
avatar: '/pages_order/static/temp-30.png',
name: '战斗世界',
createTime: '2025-07-12',
content: '凌玉姐姐很温柔很耐心很负责我很喜欢她龙哥知识渊博很幽默给我们讲解很多内容行程很有趣我学到了很多东西最难忘的就是库木塔格沙漠我们爬到了很高的顶端看夕阳绝美还有我也很喜欢夏塔古道我们爬到了第四个卡拉房子的最远端看到了壮观的雪山下次还想参加活动去南疆',
image: '/pages_order/static/temp-38.png',
},
]
async fetchComment(activityId) {
try {
const queryParams = {
pageNo: 1,
pageSize: 2,
activityId,
}
this.commentList = (await this.$fetch('queryCommentList', queryParams))?.records
console.log('commentList', this.commentList)
} catch (err) {
console.log('fetchComment', err)
}
},
onCollect() {
this.$store.dispatch('collect', this.id)
@ -288,7 +221,7 @@
this.$refs.orderInfoPopup.open({ selectTime: this.selectTime })
},
jumpToCommentRecords() {
this.$utils.navigateTo(`/pages_order/comment/commentRecordsOfProduct?productId=${this.id}`)
this.$utils.navigateTo(`/pages_order/comment/commentRecordsOfProduct?id=${this.id}`)
},
//tab
clickTabs({ index }) {
@ -439,7 +372,7 @@
}
.detail {
font-size: 0;
// font-size: 0;
}
.bottom {


+ 14
- 9
pages_order/traveler/travelerCard.vue View File

@ -12,8 +12,7 @@
<view class="info">
<view class="flex name">
<view>{{ data.name }}</view>
<!-- todo: transform periodId to desc -->
<view :class="['tag', `tag-${data.type}`]">{{ typeDesc }}</view>
<view :class="['tag', `tag-${type}`]">{{ typeDesc }}</view>
</view>
<view class="id">{{ data.cerNo }}</view>
</view>
@ -48,12 +47,6 @@
</template>
<script>
const MEMBER_TYPE_AND_DESC_MAPPING = {
0: '成人',
1: '青少年',
2: '儿童',
}
export default {
props: {
@ -85,7 +78,19 @@
}
},
typeDesc() {
return MEMBER_TYPE_AND_DESC_MAPPING[this.data.type]
const { periodId } = this.data
return this.configList.periodList?.find?.(item => item.id === periodId)?.title
},
type() {
if (this.typeDesc == '青少年') {
return 1
}
if (this.typeDesc == '儿童') {
return 2
}
return 0
},
},
watch: {


+ 1
- 3
pages_order/traveler/travelerList.vue View File

@ -73,10 +73,8 @@
},
async onDefaultChange(id, val) {
try {
const target = this.list.find(item => item.id === id)
const params = {
...target,
id,
isDefault: val ? '1' : '0',
}


+ 24
- 43
pages_order/traveler/travelerPopup.vue View File

@ -50,10 +50,10 @@
labelSize="26rpx"
>
<uv-radio
v-for="(item, index) in periodOptions"
:key="index"
:label="item.label"
:name="item.value"
v-for="item in configList.periodList"
:key="item.id"
:label="item.title"
:name="item.id"
:customStyle="{ flex: 1 }"
></uv-radio>
</uv-radio-group>
@ -261,25 +261,6 @@
},
},
formItemStyle: { padding: 0 },
// todo: fetch
periodOptions: [
{
id: '001',
label: '成人',
value: '0',
},
{
id: '002',
label: '青少年',
value: '1',
},
{
id: '003',
label: '儿童',
value: '2',
},
],
// todo: check
sexOptions: [
{
id: '001',
@ -317,18 +298,18 @@
} = result
this.form = {
name,
cerNo,
name: name || '',
cerNo: cerNo || '',
periodId,
sex,
phone,
wechat,
school,
grade,
age,
remark,
emergencyPhone,
chaperonPhone,
phone: phone || '',
wechat: wechat || '',
school: school || '',
grade: grade || '',
age: age || '',
remark: remark || '',
emergencyPhone: emergencyPhone || '',
chaperonPhone: chaperonPhone || '',
}
} catch (err) {
@ -345,19 +326,19 @@
this.id = null
this.title = '添加出行人'
this.form = {
name: null,
cerNo: null,
name: '',
cerNo: '',
// todo: fetch default
periodId: 0,
sex: 0,
phone: null,
wechat: null,
school: null,
grade: null,
age: null,
remark: null,
emergencyPhone: null,
chaperonPhone: null,
phone: '',
wechat: '',
school: '',
grade: '',
age: '',
remark: '',
emergencyPhone: '',
chaperonPhone: '',
}
}


+ 19
- 27
store/store.js View File

@ -12,6 +12,7 @@ const store = new Vuex.Store({
configList: {}, //配置列表
shop : false,//身份判断如果不需要,可以删除
userInfo : {}, //用户信息
userCenterData: {},
travelerList: null,
orderInfo: null,
couponInfo: null,
@ -35,37 +36,20 @@ const store = new Vuex.Store({
records.forEach(n => {
configList[n.paramCode] = n.paramImage || n.paramText || n.paramTextarea;
});
state.configList = configList
// todo: fetch
// fetch('queryConfigByParamCode', { paramCode: 'studytour_period' })
const periodList = (await fetch('queryPeriodList')).records
configList['periodList'] = periodList
uni.$emit('initConfig', state.configList)
const commentOptionList = (await fetch('queryCommentOptionList')).records
configList['commentOptionList'] = commentOptionList
return
const experienceQuestionList = (await fetch('queryExperienceQuestionList')).records
configList['experienceQuestionList'] = experienceQuestionList
state.configList = configList
uni.$emit('initConfig', state.configList)
api('getConfig', res => {
const configList = {
...state.configList,
}
if (res.code == 200) {
res.result.forEach(n => {
configList[n.keyName] = n.keyContent;
configList[n.keyName + '_keyValue'] = n.keyValue;
});
}
state.configList = configList
uni.$emit('initConfig', state.configList)
})
// let config = ['getPrivacyPolicy', 'getUserAgreement']
// config.forEach(k => {
// api(k, res => {
// if (res.code == 200) {
// state.configList[k] = res.result
// }
// })
// })
},
// 微信登录
login(state){
@ -110,6 +94,14 @@ const store = new Vuex.Store({
}
})
},
// 获取个人基础数据
getUserCenterData(state){
api('queryUserCenterData', res => {
if(res.code == 200){
state.userCenterData = res.result
}
})
},
// 退出登录
logout(state){
uni.showModal({


Loading…
Cancel
Save