Commit 33c8fc1e authored by youjie's avatar youjie

no message

parent cb31d999
<template>
<div class="absolute q-pb-sm text-center row justify-center" style="width: 100%; bottom: 0;"
:style="{'background-image':upIcon?'linear-gradient(-180deg,rgba(255,255,255,0) 70%,#fff 70%)':'linear-gradient(-180deg,rgba(255,255,255,0) 0%,#fff 70%)','height':upIcon?'25px':'25px'}">
<span class="text-red-8 cursor-pointer" style="width: 100px;" @click="viewMore">
<span class="row items-center" v-if="!upIcon">{{$t('expends.on')}}<span class="q-ml-sm q-mt-xs"></span></span>
<span class="row items-center" v-else>{{$t('expends.off')}}<span class="q-ml-sm q-pb-xs">︿</span></span>
</span>
</div>
</template>
<script lang="ts">
import { defineComponent, reactive,toRefs, PropType, computed, ref, toRef, Ref,watch } from 'vue'
import { copyToClipboard, useQuasar } from 'quasar'
import { useI18n } from 'vue-i18n'
export default defineComponent({
name: '',
props: ['upIcon'],
setup(props,context) {
const { t } = useI18n()
const data = reactive({
upIcon: false
})
watch(()=>props.upIcon,(n,o)=>{
data.upIcon = n
},{immediate:true})
const methods = {
viewMore() {
context.emit('change')
},
}
return {
...toRefs(data),
...methods, }
}
})
</script>
<template>
<div style="min-height: 80vh">
<div style="position: fixed; left: 0; right: 0; top: 0; border-top: 1px solid #eee; box-shadow: 0 2px 8px rgb(0 0 0 / 20%); z-index: 9999999" class="bg-white" v-if="currentHeight > navs[0].top + 100" :style="{ transform: stickyHeight }">
<div style="max-width: 1200px; margin-left: auto; margin-right: auto" :class="{ 'q-px-md': $q.screen.width < 1220 }" class="q-py-sm row items-center">
<span class="col product-price text-subtitle1 text-weight-bold" style="text-align: left">
<span>CNY {{ moneyFormat(dataList.priceList[0].originalB2CPrice, 0) }}</span>
<span class="q-ml-sm f12 text-grey-7">{{$t('v103.details.since')}}</span>
</span>
<q-btn color="primary" unelevated class="q-px-xl" :label="$t('v103.details.selectionscheme')" @click="goScrollHandler(priceListHeight)" />
</div>
</div>
<div v-if="dataList" style="max-width: 1200px; margin-left: auto; margin-right: auto" class="q-mt-md q-mb-lg" :class="{ 'q-px-md': $q.screen.width < 1220 }">
<div style="padding-bottom:calc((100% - 0px)/3);position:relative;">
<div style="position: absolute; left: 0; top: 0; right: 0; bottom: 0" class="overflow-hidden rounded-borders">
<q-carousel animated v-model="slide" navigation height="400px" infinite autoplay transition-prev="slide-right" transition-next="slide-left">
<q-carousel-slide v-for="(item, i) in dataList.imgCover" :name="i" :key="i" :img-src="item.Url" />
</q-carousel>
</div>
</div>
<div class="q-py-lg row" style="border-bottom: 1px solid #eee">
<div class="col q-mr-lg">
<div class="text-h6 ellipsis-2-lines">{{ dataList.title }}</div>
<div class="q-mt-md f12 text-grey-6">
<q-icon name="iconfont icondingweixiao" size="16px" class="q-mr-sm" />
<span
>日本 -
<span v-for="(x, i) in citys" :key="i">
<span class="cursor-pointer">{{ x }} </span>
<span v-if="i + 1 != citys.length" class="q-mr-sm">,</span>
</span>
</span>
</div>
<div class="q-mt-md row">
<q-chip class="transparent q-mr-xl no-padding" square icon="iconfont iconOwner-1" label=" 印象自組團" />
<q-chip class="transparent q-mr-xl no-padding" square icon="iconfont icontime" :label="` 行程時間 ${dataList.dayList.length} 天`" />
<q-chip class="transparent q-mr-xl no-padding" square icon="iconfont iconnetwork-fill" label="日本語/中文 導覽" />
<q-chip class="transparent q-mr-xl no-padding" square icon="iconfont iconcancel" label="15天前可免費取消" />
</div>
</div>
<div style="width: 293px" class="rounded-borders q-px-md q-py-lg">
<div class="product-price text-h6 text-right" v-if="dataList.priceList.length > 0">
<!-- CNY:{{ dataList. }} -->
CNY:{{ moneyFormat(dataList.priceList[0].originalB2CPrice, 0) }}
<span class="f12 text-grey-6"></span>
</div>
<div v-else class="text-subtitle1 text-grey-6">暫無報價</div>
<q-btn color="primary" label="選擇方案" unelevated @click="goScrollHandler(priceListHeight, 1)" class="q-mt-md full-width" />
</div>
</div>
<div class="q-mt-lg">
<div class="q-mt-sm" v-for="(x, i) in dataList.productRecommend.split('\n')" :key="i">
<q-icon name="iconfont iconhongqi" color="primary" size="16px" class="q-mr-md" />
{{ x }}
</div>
</div>
</div>
<div v-if="dataList" class="q-pa-lg q-mt-lg text-grey-9" ref="pricelistref">
<div style="max-width: 1200px; margin-left: auto; margin-right: auto">
<div class="text-h6">選擇方案</div>
<div class="bg-white rounded-borders q-mt-md" v-if="dataList.priceList.length > 0">
<div class="q-pa-md row">
<div class="col">
<div class="text-subtitle1 text-weight-bold row items-center">
<span class="q-mr-md">行程標準出行方案</span>
<q-chip square color="orange" size="sm" text-color="grey-2" class="text-light" label="15天前可免費取消" />
</div>
<div class="text-grey f12">
<ul class="q-pl-md no-margin">
<li v-for="(x, i) in warnBuy" class="q-mt-md" :key="i">
{{ x }}
</li>
</ul>
</div>
</div>
<div class="">
<div class="row items-center">
<span class="product-price text-h6 q-mr-md">CNY {{ moneyFormat(dataList.priceList[0].originalB2CPrice, 0) }}</span>
<q-btn color="primary" outline :label="showOrderPreview ? '取消選擇' : '選擇'" @click="showOrderPreview = !showOrderPreview" class="q-px-lg" />
</div>
<div class="text-info q-mt-md text-right">最早可預訂日期:{{ dataList.priceList[0].startDate }}</div>
</div>
</div>
<div class="q-pa-md" style="border-top: 1px solid #eee" v-if="showOrderPreview">
<div class="q-pa-md bg-grey-2 row items-center rounded-borders">
<div class="text-subtitle2 text-weight-bold q-mr-xl">關於此方案</div>
<q-chip class="transparent q-mr-xl no-padding" square icon="iconfont iconcancel" label="15天前可免費取消" />
<q-chip class="transparent q-mr-xl no-padding" square icon="iconfont iconroundcheck" label="6個工作日內(不含例休假)確認" />
</div>
<div class="q-mt-lg row">
<div class="col">
<calendar :priceList="dataList.priceList" @change="changeChosenDateHandler" ref="calendar"></calendar>
</div>
<div class="col q-ml-xl">
<order-preview :price="currentPrice" @reset="resetHandler"></order-preview>
</div>
</div>
</div>
</div>
</div>
</div>
<div v-if="dataList" style="max-width: 1200px; margin-left: auto; margin-right: auto" class="q-mt-xl row" :class="{ 'q-px-md': $q.screen.width < 1220 }">
<div class="col-8">
<div class="text-h5 text-weight-bold text-left" id="feature">行程特色</div>
<div class="q-mt-md trip-text q-pb-xl" v-html="dataList.feature.featureContent" v-if="dataList.feature.featureContent != ''"></div>
<div class="q-mt-md q-pb-xl" ref="diyContext" :style="{ zoom: zoomDiyContext }" v-html="dataList.feature.featureHtml" v-if="dataList.feature.featureHtml != ''"></div>
<div class="text-h5 text-weight-bold text-left q-mt-xl" id="product">行程介紹</div>
<div class="q-pb-xl">
<smaple :trip="dataList"></smaple>
<trip :trip="dataList" @change="changeTripShowHandler"></trip>
</div>
<div class="text-h5 text-weight-bold text-left q-mt-xl" id="price">費用說明</div>
<div class="text-subtitle1 text-weight-bold q-my-md">費用包含</div>
<div class="q-mt-md trip-text" v-html="dataList.feature.feeInclude"></div>
<div class="text-subtitle1 text-weight-bold q-my-md">費用不含</div>
<div class="q-mt-md trip-text q-pb-xl" v-html="dataList.feature.feeNonInclude"></div>
<div class="text-h5 text-weight-bold text-left q-mt-xl" id="warning">購買須知</div>
<div class="q-mt-lg trip-text bg-orange-1 q-pa-md rounded-borders q-mb-xl" style="border: 1px dashed var(--q-color-warning)" v-html="dataList.feature.importantTip"></div>
<div class="text-h5 text-weight-bold text-left q-mt-xl" id="tips">溫馨提示</div>
<div class="q-mt-lg trip-text bg-white q-pa-md rounded-borders q-mb-xl" style="border: 1px dashed var(--q-color-info)" v-html="dataList.feature.warmTip"></div>
<div class="text-h5 text-weight-bold text-left q-mt-xl" id="cancelTips">取消政策</div>
<div class="q-mt-lg trip-text q-mb-xl">
<ul class="no-padding text-grey-9 q-ml-md">
<li>所选日期 15 天(含)之前取消,收取手续费 0%</li>
<li class="q-mt-sm">所选日期 8 ~ 14 天之间取消,收取手续费 30%</li>
<li class="q-mt-sm">所选日期 4 ~ 7 天之间取消,收取手续费 50%</li>
<li class="q-mt-sm">所选日期 1 ~ 3 天之间取消,收取手续费 80%</li>
<li class="q-mt-sm">所选日期 0 ~ 0 天之间取消,收取手续费 100%</li>
</ul>
<div class="q-mt-lg f12 text-grey-6">
<q-icon name="iconfont icontishi" class="q-mr-sm" />
<span>注意:由于站内商品来自全球各地,订单取消时间将依该供应商所在时区判定。供应商需 2-5 个工作天进行取消流程,依照您购买的商品取消政策收取手续费,并于取消流程完成后14 个工作天内退款。</span>
</div>
</div>
</div>
<div class="col-1"></div>
<div class="col q-ml-xl position-relative">
<div style="position: sticky; top: 100px">
<div
class="text-subtitle2 text-grey-6 cursor-pointer q-mb-md"
v-for="(x, i) in navs"
:key="i"
@click="goScrollHandler(x.top + 20)"
:class="{
'active-trip-module': currentHeight >= x.top && (i == navs.length - 1 || currentHeight < navs[i + 1].top)
}"
>
{{ x.display }}
</div>
</div>
</div>
</div>
<div class="goBackTop bg-primary" :class="{ showGoback: isShowTop }" @click="hangleGoUp">
<i class="iconfont iconarrow-top"></i>
</div>
</div>
</template>
<script lang="ts">
import { date, useQuasar } from 'quasar'
import { inject, provide, reactive, toRefs, defineComponent, onMounted, ref, watch, getCurrentInstance, nextTick } from 'vue'
import { useRouter } from 'vue-router'
import { useI18n } from 'vue-i18n'
import { DirtionmaryHelper } from 'src/config/dictionary'
import scheduledTripService from 'src/api/scheduledTrip'
import { ApiResult } from 'src/@types/enumHelper'
import message from 'src/utils/message'
import { currentRouter } from 'src/router'
import calendar from 'src/components/trip/calendar.vue'
import OrderPreview from 'src/components/trip/orderPreview.vue'
import smaple from 'src/components/trip/smaple.vue'
import Trip from 'src/components/trip/trip.vue'
import { formatDate, moneyFormat } from 'src/utils/tools'
export default defineComponent({
components: {
calendar,
OrderPreview,
smaple,
Trip
},
props: [],
setup(props) {
const $router = useRouter()
const { t } = useI18n()
const $q = useQuasar()
const calendar = ref(null)
const navstext = ref([])
const getDivDom = el => {
navstext.value.push(el)
}
const diyContext = ref(null) as any
const pricelistref = ref(null) as any
let { ctx: that, proxy } = getCurrentInstance()
const data = reactive({
scrollareaobj: null,
isShowTop: false,
slide: 1,
msg: {
configId: '',
cityId: 0,
preview: 0,
tcid: 0,
teamType: 0,
isGetPriceFlight: true
},
currentPrice: {},
isShow: false,
dataList: null,
dayList: [],
isDirect: 1,
TripConfig: {},
isShowNav: false,
clickIndex: 1,
isLoading: false,
isShowDialog: false,
citys: [],
//Slider configuration [obj]
options: {
currentPage: 0,
speed: 300,
itemAnimation: true,
centeredSlides: true,
thresholdDistance: 100,
thresholdTime: 300,
loopedSlides: 2,
slidesToScroll: 1,
loop: true
},
warnBuy: ['未满 2 岁幼儿不占位可免费参加(不含座位,餐点,门票,床位),请先于下订时在「备注栏」告知。', '如需要單人房,請購買單房', '不占床2-11(包含)岁幼童可购买儿童价,如需占床請購買成人價', '12歲以上皆視同成人售價'],
showOrderPreview: false,
videoPosition: 0,
currentHeight: 0,
isPictureInPicture: false,
navs: [
{
val: 'feature',
top: 0,
isActive: false,
display: '行程特色'
},
{
val: 'product',
top: 0,
isActive: false,
display: '產品介紹'
},
{
val: 'price',
top: 0,
isActive: false,
display: '費用說明'
},
{
val: 'warning',
top: 0,
isActive: false,
display: '購買須知'
},
{
val: 'tips',
top: 0,
isActive: false,
display: '溫馨提示'
},
{
val: 'cancelTips',
top: 0,
isActive: false,
display: '取消政策'
}
],
priceListHeight: 0,
stickyHeight: 0,
zoomDiyContext: 1
})
data.scrollareaobj = inject(DirtionmaryHelper.SCROLL_AREA_OBJ) as any
data.msg.configId = decodeURIComponent(currentRouter.currentRoute.value.params.id)
data.msg.tcid = currentRouter.currentRoute.value.params.tcid
if (localStorage.baseifo) {
data.TripConfig = JSON.parse(window.localStorage.getItem('baseifo'))
}
const methods = {
//点击回到顶部
hangleGoUp() {
document.querySelector('#scrollId .q-scrollarea__container').scrollTop = 0
},
changeTripShowHandler() {
nextTick(() => {
data.navs.forEach((x, index) => {
const el = navstext.value[index]
x.top = el.getBoundingClientRect().top + data.currentHeight - 60
})
})
},
handleScroll(e) {
// console.log(document.querySelector('#scrollId .q-scrollarea__container').scrollTop, '...............')
let temp = document.querySelector('#scrollId .q-scrollarea__container').scrollTop
// if(temp-data.currentHeight>0){
// data.scrollDirection='down'
// }else{
// data.scrollDirection='up'
// }
data.stickyHeight = document.querySelector('.q-header--hidden') ? 'translateY(0px)' : 'translateY(0px)'
data.currentHeight = temp
if (data.videoPosition > 0 && data.videoPosition < data.currentHeight && data.options.currentPage == 0 && !data.isPictureInPicture) {
data.isPictureInPicture = true
let t = document.querySelectorAll('video')[0]
try {
t.requestPictureInPicture()
} catch (error) {}
} else if (data.videoPosition > 0 && data.videoPosition > data.currentHeight && data.options.currentPage == 0 && data.isPictureInPicture) {
data.isPictureInPicture = false
document.exitPictureInPicture()
}
let h = document.location.href
if (h.indexOf('scheduledTrip/details') == -1) {
document.querySelector('#scrollId .q-scrollarea__container').removeEventListener('scroll', methods.handleScroll)
}
let nowTop =document.querySelector('#scrollId .q-scrollarea__container').scrollTop
if (nowTop > 200) {
data.isShowTop = true
} else {
data.isShowTop = false
}
},
// 导航定位
goScrollHandler(top) {
data.scrollareaobj.pagesTop = top
},
playHandler(e) {},
pauseHandler(e) {},
timeChangeHandler(e) {
//console.log(e)
},
//
seekedChangeHandler(e) {
//console.log(e)
},
resetHandler() {
calendar.value.reset()
},
changeChosenDateHandler(val) {
val.price.version = new Date().getTime()
data.currentPrice = JSON.parse(JSON.stringify(val.price))
},
showDialog() {
data.isShowDialog = true
},
//关闭弹窗
closeDialog() {
data.isShowDialog = false
},
getData() {
$q.loading.show()
let param = Object.assign(data.msg)
scheduledTripService
.GetB2BTravelInfoV1(param)
.then(r => {
$q.loading.hide()
if (r.data.resultCode == ApiResult.SUCCESS) {
if (r.data.data) {
data.dataList = r.data.data
if (data.dataList && data.dataList.dayList && data.dataList.dayList.length > 0) {
data.dataList.dayList.forEach(item => {
item.slide = 0
})
if (r.data.data.scenicList) {
r.data.data.scenicList.forEach(x => {
if (x.cityName) {
data.citys.push(x.cityName)
}
})
}
}
data.dataList.imgCover = JSON.parse(data.dataList.imgCover)
data.dayList = data.dataList.dayList
data.isShow = true
data.isDirect = data.dataList.isDirect
if (data.dataList.videoStr && data.dataList.videoStr != '') {
data.options.loop = false
data.options.currentPage = 1
nextTick(() => {
setTimeout(() => {
let t = document.querySelectorAll('.slider-wrapper')
if (t.length > 0) {
let d = t[0].getBoundingClientRect()
data.videoPosition = d.top + d.height
}
}, 1000)
})
}
nextTick(() => {
try {
document.querySelector('#scrollId .q-scrollarea__container').addEventListener('scroll', methods.handleScroll)
} catch (error) {
// console.log(document.querySelector('#scrollId .q-scrollarea__container'))
// console.log('异常信息', error)
}
if (data.dataList.feature.featureHtml != '') {
let tw = parseFloat(diyContext.value.getBoundingClientRect().width) / 1123.0
data.zoomDiyContext = tw > 1 ? 1 : tw.toFixed(2)
}
setTimeout(() => {
data.navs.forEach((x, index) => {
const el = document.querySelector(`#${x.val}`)
x.top = el.offsetTop - 90
})
data.priceListHeight = pricelistref.value.getBoundingClientRect().top + data.currentHeight - 60
}, 1000)
})
} else {
message.errorMsg(r.data.message)
}
}
})
.catch(e => {
$q.loading.hide()
message.errorMsg(e.message)
})
},
menu() {
data.scroll = document.documentElement.scrollTop || document.body.scrollTop
if (data.scroll > 300) {
data.isShowNav = true
} else {
data.isShowNav = false
}
},
goNavList(val) {
let newval = '#' + val
document.querySelector(newval).scrollIntoView(true)
}
}
methods.getData()
return {
...toRefs(data),
...methods,
moneyFormat,
calendar,
getDivDom,
navstext,
diyContext,
pricelistref
}
}
})
</script>
<style>
.goBackTop {
width: 56px;
height: 56px;
border-radius: 50%;
/* background-color: #027be3; */
position: fixed;
right: 20px;
bottom: 20px;
color: #fff;
text-align: center;
line-height: 56px;
z-index: 9999999;
opacity: 0;
transition: opacity 2s linear;
}
.showGoback {
opacity: 100;
}
.slider-item {
transform: scale(1);
transition-timing-function: ease;
transition-duration: 300ms;
}
.slider-item.slider-active {
transform: scale(1);
z-index: 999;
}
.trip-text img {
max-width: 100%;
height: auto;
border-radius: 6px;
}
.trip-text {
line-height: 1.6;
}
.slider-item.slider-active-copy {
transform: scale(1);
z-index: 999;
}
.active-trip-module {
color: var(--q-primary) !important;
font-weight: 800;
position: relative;
}
.active-trip-module::before {
position: absolute;
content: " ";
top: 0;
bottom: 0;
left: -25px;
width: 5px;
border-radius: 5px;
background: var(--q-primary);
}
</style>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment