Merge branch 'dev' into test

This commit is contained in:
lifizer
2024-12-02 10:37:54 +08:00
84 changed files with 7001 additions and 435 deletions
+10
View File
@@ -151,3 +151,13 @@ export function getBargainUserList(data) {
export function getBargainUserCancel(data) {
return request.post("/bargain/user/cancel", data);
}
/**
* 新品专区
* @param {*} data
* @returns
*/
export function queryNewZone() {
return request.get("/newGoods");
}
+24
View File
@@ -0,0 +1,24 @@
import request from "@/utils/request";
export function getFarmerShop(id) {
return request.get("/countyFamous/farmerShop/" + id)
}
export function setFarmerShopZan(data) {
return request.post("/countyFamous/farmerShop/zan", data)
}
export function getFarmerShopNews(data) {
return request.get("/countyFamous/farmerShop/news", data)
}
export function getFarmerShopQualification(data) {
return request.get("/countyFamous/farmerShop/qualification", data)
}
export function setFarmerShopCollection(data) {
return request.post("/collection/farmerShop/add", data)
}
+81
View File
@@ -0,0 +1,81 @@
import request from "@/utils/request";
/**
* 康养生活-养生圣地分类列表
* @returns
*/
export function getCategory() {
return request.get("/wellness/place/category")
}
/**
* 康养生活-养生圣地列表
* @param {*} params
* @returns
*/
export function getWellnessPlace(params) {
return request.get("/wellness/place", params)
}
/**
* 康养生活-展会列表
* @param {*} params
* @returns
*/
export function getWellnessExhibition(params) {
return request.get("/wellness/exhibition", params)
}
/**
* 康养生活-展会详情
* @param {*} id
* @returns
*/
export function getWellnessExhibitionDetails(id) {
return request.get("/wellness/exhibition/" + id)
}
/**
* 康养生活-养生榜单
* @param {*} params
* @returns
*/
export function getRank(params) {
return request.get("/wellness/place/ranking", params)
}
/**
* 康养生活-养生食材列表
* @param {*} params
* @returns
*/
export function getFoods(params) {
return request.get("/wellness/place/food", params)
}
/**
* 康养生活-配方详情
* @param {*} id
* @returns
*/
export function getFoodDetails(id) {
return request.get("/wellness/recipe/" + id)
}
/**
* 康养生活-配方列表
* @param {*} params
* @returns
*/
export function getRecipe(params) {
return request.get("/wellness/recipe", params)
}
/**
* 康养生活-展会轮播图
* @returns
*/
export function getBanner() {
return request.get("/wellness/exhibition/banner")
}
+29
View File
@@ -0,0 +1,29 @@
import request from "@/utils/request";
/**
* 省份非遗数据
* @param {*} id
* @returns
*/
export function getIch(id) {
return request.get("/ich/v2/" + id);
}
/**
* 省份非遗专题数据
* @param {*} id
* @returns
*/
export function getIchContent(id) {
return request.get("/ich/v2/content/" + id);
}
/**
* 获取省份非遗推荐数据
* @param {*} params
* @returns
*/
export function getIchRecommend(params) {
return request.get("/ich/v2/recommend", params);
}
+5
View File
@@ -236,3 +236,8 @@ export function getHotelShareBackgroundImage(id) {
export function hotelNewsView(data) {
return request.post("/api/hotelNews/view", data, {login: true})
}
export function getFarmerShop(id) {
return request.get("/api/countyFamous/farmerShop/" + id)
}
+28
View File
@@ -131,4 +131,32 @@ export function getProvince(id) {
export function getProvinceGoods(parentId, keyword, page) {
const params = `?parentId=${parentId}&keyword=${keyword}&page=${page}&limit=10`;
return request.get('/landmarkGoods/productList' + params, {}, {login: false})
}
export function getPorjectRecommend(params) {
return request.get('/landmarkGoods/hotelList', params, {login: false})
}
export function getPorjectPolicyFiles(params) {
return request.get('/landmarkGoods/policyFiles', params, {login: false})
}
export function getCulture(params) {
return request.get('/landmarkGoods/culture', params, {login: false})
}
export function getNews(params) {
return request.get('/landmarkGoods/news', params, {login: false})
}
export function hadLike(data) {
return request.post('/landmarkGoods/news/zan', data, {login: false})
}
export function hadView(data) {
return request.post('/landmarkGoods/news/view', data, {login: false})
}
export function getShareImg(id) {
return request.get('/landmarkGoods/poster/' + id, {}, {login: false})
}
+5
View File
@@ -94,3 +94,8 @@ export function getCountyFamousVillageDetail(id) {
export function getCountyFamousVillageNews(param) {
return request.get('/countyFamous/village/news', param, { login: true })
}
// 获取千县分享图
export function getShareImg(id) {
return request.get('/countyFamous/poster/' + id, {login: true})
}
+6 -6
View File
@@ -11,8 +11,8 @@
.box-share {
.box-img {
position: relative;
width: 500rpx;
height: 632rpx;
width: 600rpx;
height: 832rpx;
border-radius: 40rpx;
overflow: hidden;
}
@@ -26,14 +26,14 @@
}
.main-img {
width: 500rpx;
height: 632rpx;
width: 600rpx;
height: 832rpx;
border-radius: 40rpx;
}
.btn {
width: 500rpx;
height: 100rpx;
width: 600rpx;
height: 120rpx;
margin-top: 20rpx;
}
}
+5 -2
View File
@@ -3,8 +3,8 @@
@dark-color: #333333;
@white-color: #fff;
@colors: #C52733, #333333, #fff, #666, #F0F0F0, #999999, #E92727, #FFC543;
@classnames: primary, dark, white, secondary-dark, grey, dark1, red, yellow;
@colors: #C52733, #333333, #fff, #666, #F0F0F0, #999999, #E92727, #FFC543, #69C573;
@classnames: primary, dark, white, secondary-dark, grey, dark1, red, yellow, success;
/*=====================================*/
each(@colors, {
@@ -192,6 +192,9 @@ each(@colors, {
.v12-full-width{
width: 100%;
}
.v12-nowrap{
white-space: nowrap
}
+3 -3
View File
@@ -1,10 +1,10 @@
<template>
<view class="slider-banner product-bg">
<swiper class="swiper-wrapper" @change="handleChange" v-if="imgUrls.length > 0" :indicator-dots='true' indicator-color="rgba(255, 255, 255, .3)" indicator-active-color="#ffffff">
<swiper autoplay circular interval="2000" class="swiper-wrapper" @change="handleChange" v-if="imgUrls.length > 0" :indicator-dots='true' indicator-color="rgba(255, 255, 255, .3)" indicator-active-color="#ffffff">
<block v-for="(item, imgUrlsIndex) in imgUrls" :key="imgUrlsIndex">
<swiper-item>
<image v-if="!checkIfVideo(item)" :src="item" class="slide-image" />
<video v-else class="slide-image" @play="storePlayObj" :show-fullscreen-btn="false" :src="item" :id="buildId(imgUrlsIndex)" autoplay controls></video>
<video v-else class="slide-image" @play="storePlayObj" :show-fullscreen-btn="false" :src="item" :id="buildId(imgUrlsIndex)" autoplay controls></video>
</swiper-item>
</block>
</swiper>
@@ -38,7 +38,7 @@ export default {
currentVideo:null,
ProductConSwiper: {
autoplay: {
disableOnInteraction: false,
disableOnInteraction: true,
delay: 2000
},
loop: true,
+1 -1
View File
@@ -80,7 +80,7 @@ export default {
overscroll-behavior: contain;
}
.poster-pop {
width: 4.5 * 100rpx;
width: 6 * 100rpx;
height: 8 * 100rpx;
position: fixed;
left: 50%;
+10 -7
View File
@@ -11,13 +11,13 @@
</view>
<view class="acea-row row-middle">
<view class="name line1">{{ item.nickname }}</view>
<view class="time">{{ item.createTime }} </view>
</view>
</view>
<view class="v12-px-4">
<view class="v12-secondary-dark-text v12-font-24 attr-bg"> {{ item.sku }}</view>
<view class="start" :class="'star' + item.star"></view>
</view>
<!-- <view class="time">{{ item.createTime }} {{ item.sku||'' }}</view> -->
<view class="evaluate-infor v12-font-24">{{ item.comment }}</view>
<view class="imgList acea-row">
<view v-if="item.video" class="pictrue">
@@ -61,12 +61,12 @@ export default {
mounted: function() {},
methods: {
dataFormat,
previewImage(imgs,index){
uni.previewImage({
current:imgs[index],
urls:imgs
})
}
previewImage(imgs,index){
uni.previewImage({
current:imgs[index],
urls:imgs
})
}
}
};
</script>
@@ -77,4 +77,7 @@ export default {
width: fit-content;
border-radius: 12rpx;
}
.time{
padding: 0 !important;
}
</style>
+11 -3
View File
@@ -7,11 +7,11 @@
class="sunui-uploader-file"
/>
<block v-for="(item, index) in upload_before_list" :key="index">
<view class="sunui-uploader-file" :class="[item.upload_percent < 100 ? 'sunui-uploader-file-status' : '']" :style="'width:' + upload_img_wh + 'rpx; height:' + upload_img_wh + 'rpx;'">
<view class="sunui-uploader-file" :class="[item.upload_percent < 100 ? '' : '']" :style="'width:' + upload_img_wh + 'rpx; height:' + upload_img_wh + 'rpx;'">
<image class="sunui-uploader-img" :style="'width:' + upload_img_wh + 'rpx; height:' + upload_img_wh + 'rpx;'" :src="item.path" mode="aspectFill" @tap="preview(index)" v-if="type === 'image'" />
<video :id="`myVideo_${index}`" :src="item.path" :data-index="index" @play="videoPlay" @error="videoError" controls :enable-play-gesture="true" objectFit="contain" :style="'width:' + upload_img_wh + 'rpx; height:' + upload_img_wh + 'rpx;'" v-if="type === 'video'"></video>
<view class="sunui-img-removeicon right" @tap.stop="remove(index)" v-show="upimg_move">x</view>
<view class="sunui-loader-filecontent" v-if="item.upload_percent < 100">{{item.upload_percent}}%</view>
<!-- <view class="sunui-loader-filecontent" v-if="item.upload_percent < 100">{{item.upload_percent}}%</view> -->
</view>
</block>
<view v-show="upload_before_list.length < upload_count" hover-class="sunui-uploader-hover" class="sunui-uploader-inputbox" @tap="choose" :style="'width:' + upload_img_wh + 'rpx; height:' + upload_img_wh + 'rpx;'">
@@ -185,6 +185,7 @@ export default {
if (Math.ceil(res.size / 1024) < this.upload_video_max * 1024) {
await this.upload_img_size.push(Math.ceil(res.size / 1024));
await this.upload_before_list.push(res);
this.$forceUpdate()
} else {
this.upload_exceeded_list.push(1);
@@ -219,6 +220,7 @@ export default {
if (Math.ceil(res.tempFiles[i].size / 1024) < this.upload_max * 1024) {
await this.upload_img_size.push(Math.ceil(res.tempFiles[i].size / 1024));
await this.upload_before_list.push(res.tempFiles[i]);
this.$forceUpdate()
// TODO v3.1增加图片格式限制
} else {
res.tempFilePaths.splice(i, 1);
@@ -239,6 +241,7 @@ export default {
}
this.upload_cache = await res.tempFilePaths;
this.upload(this.upload_auto);
this.$forceUpdate()
},
fail: (err) => {
console.log(err);
@@ -338,7 +341,12 @@ export default {
uploadTask.onProgressUpdate(async (res) => {
// this.upload_before_list[this.upload_before_list.length-1].upload_percent = await res.progress;
for(let i in this.upload_before_list) { if(this.upload_before_list[i].upload_percent != 100) { this.upload_before_list[i].upload_percent = await res.progress; break; } };
for (let i in this.upload_before_list) {
if (this.upload_before_list[i].upload_percent != 100) {
console.log(res.progress, '-----------res.progress-----------');
this.upload_before_list[i].upload_percent = await res.progress
}
};
});
}
}
+6
View File
@@ -6,11 +6,17 @@ if (NODE_ENV === 'development' || NODE_ENV === 'test') {
BASE_URL = 'https://shop.xdd618.com/api'
SERVICE_URL = 'https://service-test.xdd618.com/api'
SERVICE_WS_URL = 'wss://service-test.xdd618.com/ws'
wx.setEnableDebug({
enableDebug: false
})
}
if (NODE_ENV === 'prod') {
BASE_URL = 'https://wxapp.xdd618.com/api'
SERVICE_URL = 'https://service.xdd618.com/api'
SERVICE_WS_URL = 'wss://service.xdd618.com/ws'
wx.setEnableDebug({
enableDebug: false
})
}
export const VUE_APP_API_URL = BASE_URL
+79 -2
View File
@@ -42,6 +42,19 @@
"navigationBarTitleText": "地标好物"
}
},
{
"path": "pages/home/searchPage",
"style": {
"navigationBarTitleText": "搜索"
}
},
{
"path": "pages/home/newZone",
"style": {
"navigationBarTitleText": "新品专区",
"navigationBarBackgroundColor": "#FFFFFF"
}
},
{
"path": "pages/chose-city/chose-city",
"style": {
@@ -90,7 +103,7 @@
{
"path": "pages/cloud/haveFun",
"style": {
"navigationBarTitleText": "寻趣味"
"navigationBarTitleText": "探索生活好去处"
}
},
{
@@ -392,6 +405,13 @@
"navigationStyle": "custom"
}
},
{
"path": "inn/farmerStore",
"style": {
"navigationBarTitleText": "",
"navigationStyle": "custom"
}
},
{
"path": "inn/innGoodList",
"style": {
@@ -540,7 +560,50 @@
{
"path": "views/healthList",
"style": {
"navigationBarTitleText": "康养 · 食材"
"navigationBarTitleText": "选择城市",
"navigationBarBackgroundColor": "#FFFFFF"
}
},
{
"path": "views/expoList",
"style": {
"navigationBarTitleText": "康养旅居",
"navigationBarBackgroundColor": "#FFFFFF"
}
},
{
"path": "views/expoDetails",
"style": {
"navigationBarTitleText": "文化艺术",
"navigationBarBackgroundColor": "#FFFFFF"
}
},
{
"path": "views/healthSanctum",
"style": {
"navigationBarTitleText": "康养旅居",
"navigationBarBackgroundColor": "#FFFFFF"
}
},
{
"path": "views/healthFoods",
"style": {
"navigationBarTitleText": "康养旅居",
"navigationBarBackgroundColor": "#FAEED8"
}
},
{
"path": "views/healthFoodDetails",
"style": {
"navigationBarTitleText": "康养旅居",
"navigationBarBackgroundColor": "#FFFFFF"
}
},
{
"path": "views/healthSearch",
"style": {
"navigationBarTitleText": "搜索",
"navigationBarBackgroundColor": "#FFFFFF"
}
},
{
@@ -555,6 +618,20 @@
"navigationBarTitleText": ""
}
},
{
"path": "views/heritage/index",
"style": {
"navigationBarTitleText": "非遗文化",
"navigationBarBackgroundColor": "#FAEED8"
}
},
{
"path": "views/heritage/details",
"style": {
"navigationBarTitleText": "非遗文化",
"navigationBarBackgroundColor": "#FAEED8"
}
},
{
"path": "views/heritageVideo",
"style": {
+29 -32
View File
@@ -131,7 +131,7 @@
<view class="v12-mt-3 v12-font-32 v12-dark-text v12-font-weight-600">发现家乡好物</view>
</view>
<view class="btn-box">
<button class="btn-yes v12-primary" type="mini" v-if="canIUseGetUserProfile" @tap="getUserInfoProfile">快捷登录</button>
<button class="btn-yes v12-primary" type="mini" @tap="getUserInfoProfile">快捷登录</button>
<!-- <button class="btn-no" type="default" @tap="back">拒绝</button> -->
</view>
<view class="agree-box v12-align-center">
@@ -171,7 +171,6 @@ export default {
return {
webUrl: this.$VUE_APP_RESOURCES_URL,
authorize: false,
canIUseGetUserProfile: false,
agreeInfo: {},
isAgree: [],
show: false,
@@ -188,9 +187,6 @@ export default {
...mapState(['isAuthorization', '$deviceType', 'token']),
},
onLoad() {
if (wx.getUserProfile) {
this.canIUseGetUserProfile = true
}
// 先校验用户是否授权,如果没有授权,显示授权按钮
getAgree().then(({data}) => this.agreeInfo = data);
},
@@ -227,18 +223,19 @@ export default {
})
},
doneRedirect() {
let redirectUrl = cookie.get('redirect') || "";
redirectUrl = redirectUrl.replace(/\ /g, '');
//如果已经是授权页,跳转主页
let redirectUrl = cookie.get('redirect') || ''
redirectUrl = redirectUrl.replace(/\ /g, '')
// 如果已经是授权页,跳转主页
if (redirectUrl == '/pages/authorization/index' || redirectUrl.length === 0) {
redirectUrl = '/pages/home/index';
redirectUrl = '/pages/home/index'
}
this.$yrouter.reLaunch({
path: redirectUrl,
path: redirectUrl
})
},
getUserInfoProfile() {
if (!this.isAgree[0]) {
const _this = this
if (!_this.isAgree[0]) {
uni.showToast({
title: "请先阅读并同意《用户服务协议及隐私政策》",
icon: "none",
@@ -246,33 +243,33 @@ export default {
})
return
}
wx.getUserProfile({
lang: 'zh_CN',
desc: '需要获取您的信息用来展示',
success: res => {
uni.showLoading({
title: '登录中',
})
login(res).then(e => {
let db_nickname = e.data.nickname;
if (db_nickname === null || db_nickname === undefined || db_nickname === "" || db_nickname === "微信用户") {
this.showFillOut = true;
} else {
this.doneRedirect();
}
}).catch(error => {
uni.showToast({
title: error,
icon: 'none',
duration: 2000,
})
})
},
_this.loginReq(res)
}
})
},
loginReq(res) {
uni.showLoading({
title: '登录中'
})
login(res).then(e => {
const db_nickname = e.data.nickname
if (db_nickname === null || db_nickname === undefined || db_nickname === '' || db_nickname === '微信用户') {
this.showFillOut = true
} else {
this.doneRedirect()
}
}).catch(error => {
uni.showToast({
title: error,
icon: 'none',
duration: 2000
})
})
},
onChooseAvatar(e) {
const {avatarUrl} = e.detail;
this.avatarUrl = avatarUrl;
+13
View File
@@ -194,6 +194,7 @@
iconStyle="color: #fff"
class="num-step"
@change="e => changeNumHandle(index, gIndex, good, e)"
@overlimit="overlimit"
>
<template v-slot:minus>
<view class="v12-minus-btn v12-font-bold flex jc-center ai-center">
@@ -413,6 +414,7 @@ export default {
}
this.closeAttr()
this.getCart()
this.getAddress()
},
onHide() {
this.pageToHideHandle()
@@ -548,6 +550,7 @@ export default {
changeNumHandle(index, gIndex, good, e) {
const { type } = e
const value = good.cartNum + (type === 'plus' ? 1 : -1)
console.log('index', value);
const id = good.id
// uni.showLoading()
changeCartNum(id, value).then(res => {
@@ -562,6 +565,16 @@ export default {
}, 3000)
})
},
overlimit(e) {
const tip = {
'plus': '商品数量超出库存',
'minus': '商品数量不能减少了呦~'
}
uni.showToast({
title: tip[e],
icon: 'none'
})
},
handleIds() {
if (this.selectCount === 0) {
uni.showToast({
+11
View File
@@ -350,6 +350,14 @@ export default {
url: `/pages/shop/GoodsCon/index?id=${productId}&from=${this.getOptionsParams()}`
})
},
goToExhibition(linkExhibitionId) {
if (!linkExhibitionId) {
return
}
uni.navigateTo({
url: `/pkg_product/views/expoDetails?id=${linkExhibitionId}&from=${this.getOptionsParams()}`
})
},
getOptionsParams() {
// 0、特产;1、文玩;2、七彩云上;3、特色礼品;4、康养食材;5、千县名品;6、景点门票
let from = ''
@@ -392,6 +400,9 @@ export default {
case 2:
this.goToGoods(banner.linkProductId)
break
case 3:
this.goToExhibition(banner.linkExhibitionId)
break
default:
break
}
+7 -7
View File
@@ -159,19 +159,19 @@ export default {
}
},
onLoad() {
},
onShow() {
const memCityName = uni.getStorageSync('cityName')
this.page = 1
this.currType = {}
this.typeIndex = 0
this.name = ''
getHotelTypeList().then(({data}) => {
this.typeList = data
this.currType = data[this.typeIndex]
this.getMapData()
})
},
onShow() {
const memCityName = uni.getStorageSync('cityName')
this.page = 1
// this.typeIndex = 0
this.name = ''
if (memCityName.length) {
this.cityName = memCityName
uni.removeStorageSync('cityName')
+76
View File
@@ -0,0 +1,76 @@
<template>
<view>
<view class="culture v12-d-grid-columns-2">
<view class="img-item " v-for="(item, index) in list" :key="index" @click="viewImg(item)">
<image class="img-item" :src="item" mode="widthFix|heightFix" lazy-load="false"></image>
</view>
</view>
<image
v-if="list.length === 0"
:src="webUrl + '/orderIcon/wu.png'"
class="img-nodata"
mode="scaleToFill"
/>
</view>
</template>
<script>
import {
getCulture,
} from "@/api/product"
export default {
props: {
landmarkDataId: null
},
data() {
return {
webUrl: this.$VUE_APP_RESOURCES_URL,
list: []
}
},
watch: {
landmarkDataId(value) {
if(value) {
this.getData()
}
}
},
mounted() {
this.getData()
},
methods: {
viewImg(item) {
uni.previewImage({
current: item, // 当前显示图片的http链接
urls: this.list || [] // 需要预览的图片http链接列表
})
},
getData() {
getCulture({landmarkDataId: this.landmarkDataId}).then(res => {
this.list = res.data.cultureImages || []
})
}
}
}
</script>
<style lang="scss" scoped>
.culture{
grid-gap: 20rpx 20rpx;
padding: 20rpx;
}
.img-item{
width: 340rpx;
height: 320rpx;
border-radius: 16rpx;
}
.img-nodata {
position: relative;
top: 0;
left: 50%;
transform: translate(-50%, 0);
width: 352rpx;
height: 338rpx;
margin-bottom: 60rpx;
}
</style>
+134
View File
@@ -0,0 +1,134 @@
<template>
<view class="list-box flex flex-wrap">
<view
v-for="item in goods"
:key="item.id"
class="item"
@click="goGoods(item.productId)"
>
<image
:src="webUrl + '/home/mark-land.png'"
class="mark-img"
/>
<image
:src="item.storeImage"
mode="scaleToFill"
class="good-img"
/>
<view class="content flex flex-col jc-between">
<view class="more-t bold">{{ item.title }}</view>
<view class="one-t desc">{{ item.description }}</view>
<view class="flex jc-between ai-center">
<text class="price">{{ item.price }}</text>
<image
:src="webUrl + '/home/icon-cart.png'"
class="btn"
/>
</view>
</view>
</view>
<image
v-if="goods.length === 0"
:src="webUrl + '/20231023131208675588.png'"
class="img-nodata"
mode="scaleToFill"
/>
</view>
</template>
<script>
export default {
props: {
goods: {
default: () => []
}
},
data() {
return {
webUrl: this.$VUE_APP_RESOURCES_URL,
}
},
methods: {
goGoods(id) {
uni.navigateTo({
url: `/pages/shop/GoodsCon/index?id=${id}&from=landmark`
})
}
}
}
</script>
<style lang="scss" scoped>
.img-nodata {
position: relative;
top: 0;
left: 50%;
transform: translate(-50%, 0);
width: 352rpx;
height: 338rpx;
margin-bottom: 60rpx;
}
.list-box {
margin-top: 24rpx;
padding: 0 20rpx;
.item {
position: relative;
width: 344rpx;
margin-right: 22rpx;
margin-bottom: 22rpx;
border-radius: 20rpx;
background: #FFFFFF;
overflow: hidden;
.mark-img {
position: absolute;
top: 0;
right: 0;
z-index: 3;
display: block;
width: 84rpx;
height: 74rpx;
}
.good-img {
display: block;
width: 344rpx;
height: 344rpx;
}
.content {
height: 156rpx;
padding: 8rpx 8rpx 18rpx 8rpx;
.more-t {
font-size: 28rpx;
line-height: 36rpx;
color: #333;
}
.desc {
padding: 5rpx 0;
height: 34rpx;
color: #FFC543;
font-size: 24rpx;
}
.price {
font-size: 40rpx;
line-height: 40rpx;
font-weight: bold;
color: #C52733;
}
.btn {
display: block;
width: 48rpx;
height: 48rpx;
}
}
}
.item:nth-child(2n) {
margin-right: 0;
}
}
.list-wrap {
margin: 20rpx 0 0 0;
padding: 0 20rpx;
.loadend {
width: 100%;
}
}
</style>
+292
View File
@@ -0,0 +1,292 @@
<template>
<view>
<view v-if="infoArray.length > 0" class="info-wrapper">
<view class="info-list">
<view v-for="(item, index) in infoArray" :key="index" class="info-item">
<view
:class="item.isTop === 1 ? 'info-item-top' : ''"
:style="{
'background-image': item.isTop
? 'url(' + webUrl + '/page/hotel/info-top-bg.png)'
: 'none',
}"
class="info-item-header"
>
<view class="name">
{{ item.title }}
</view>
<view class="intro">
{{ item.content }}
</view>
</view>
<view class="info-item-body">
<img-box
:imgList="item.images"
:num="item.images.length"
:img-radius="10"
style="width: 100%"
@click="infoItemViewClick(item)"
/>
</view>
<view class="info-item-bottom">
<view class="time">
{{
item.createTime
? item.createTime.replace(/-/g, ".").substr(0, 10)
: ""
}}
</view>
<view class="btns">
<view class="flex">
<image
:src="webUrl + '/20230803152147141807.png'"
class="icon"
/>
<text class="seeNum-txt v12-font-28">{{ item.pv }}</text>
</view>
<view class="flex">
<image
v-if="!item.hasZan"
:src="webUrl + '/page/hotel/zan-off-2.png'"
class="zan-off-icon"
@click="likeInfoItemHandle(item, index, item.zan + 1, true)"
/>
<image
v-else
:src="webUrl + '/page/hotel/zan-on-2.png'"
class="zan-on-icon"
@click="likeInfoItemHandle(item, index, item.zan - 1, false)"
/>
<text
v-show="item.zan > 0"
:class="item.hasZan ? 'on' : ''"
class="seeNum-txt v12-font-28 v12-ml-1"
>{{ item.zan }}</text
>
</view>
</view>
</view>
</view>
</view>
<image
:src="webUrl + '/home/no-data-bg.png'"
mode="widthFix"
class="no-data-loadend"
style="width: 100%;"
/>
</view>
<image
v-if="infoArray.length === 0"
:src="webUrl + '/orderIcon/wu.png'"
class="img-nodata"
mode="scaleToFill"
/>
</view>
</template>
<script>
import {
getNews,
hadLike,
hadView
} from "@/api/product"
import imgBox from '@/components/imageTypeSet/imagebox.vue'
export default {
props: {
landmarkDataId: null
},
components: {
imgBox
},
data() {
return {
webUrl: this.$VUE_APP_RESOURCES_URL,
infoArray: []
}
},
watch: {
landmarkDataId(value) {
if(value) {
this.getData()
}
}
},
created() {
this.getData()
},
methods: {
getData() {
const params = {
landmarkDataId: this.landmarkDataId,
page: 1,
limit: 9999
}
getNews(params).then(res => {
this.infoArray = res.data.records || []
})
},
likeInfoItemHandle(item, index, value, voValue) {
hadLike({ landmarkNewsId: item.id }).then(res => {
const { success } = res
if (success) {
this.$dialog.toast({
mes: '操作成功!'
})
this.$set(this.infoArray[index], 'zan', value)
this.$set(this.infoArray[index], 'hasZan', voValue)
}
})
},
infoItemViewClick(item) {
uni.$u.debounce(() => {
hadView({ landmarkNewsId: item.id }).finally(() => {
this.infoArray.map(info => {
if (info.id === item.id) {
info.pv += 1
}
})
})
}, 500)
},
}
};
</script>
<style lang="scss" scoped>
.info-wrapper{
padding: 20px 10px;
}
.info-item + .info-item {
margin: 22rpx 0 0 0;
}
.info-item {
border-radius: 10rpx;
background-color: #fff;
.info-item-header {
position: relative;
padding: 20rpx 20rpx 0 20rpx;
&.info-item-top {
padding-top: 60rpx;
background-size: 100% 80rpx;
background-repeat: no-repeat;
background-image: url();
}
.name {
font-size: 16px;
color: #333;
font-weight: bold;
}
.intro {
margin: 10rpx 0 0 0;
color: #666;
font-size: 14px;
}
}
.info-item-body {
padding: 0 20rpx 20rpx 20rpx;
.video {
padding: 20rpx 0 0 0;
.video-item {
width: 100%;
height: 400rpx;
border-radius: 10rpx;
}
}
}
.info-item-bottom {
display: flex;
align-items: center;
padding: 20rpx;
border-top: 1px solid #f1f1f1;
font-size: 16px;
.time {
flex: 1;
color: #999999;
font-size: 28rpx;
}
.btns {
display: flex;
color: #999;
.flex + .flex {
margin: 0 0 0 32rpx;
}
.flex {
align-items: center;
}
.icon {
width: 32rpx;
height: 32rpx;
margin: 0 8rpx 0 0;
}
.zan-off-icon {
width: 32rpx;
height: 32rpx;
}
.zan-on-icon {
width: 32rpx;
height: 32rpx;
}
.seeNum-txt {
&.on {
color: #D81E06;
}
}
.more-btn {
position: relative;
.more-wrapper {
position: absolute;
right: -20rpx;
top: -150rpx;
z-index: 5;
width: 200rpx;
border-radius: 12rpx;
background-color: #fff;
box-shadow: 0px 1px 6px rgba(0,0,0,0.16);
.more-item + .more-item {
border-top: 1px solid #d5d5d5;
}
.more-item {
display: flex;
align-items: center;
justify-content: center;
height: 70rpx;
font-size: 14px;
color: #333;
.more-icon {
width: 36rpx;
height: 36rpx;
margin: 0 10rpx 0 0;
}
}
}
}
}
}
}
.info-item-top {
padding-top: 60rpx;
background-size: 100% 80rpx;
background-repeat: no-repeat;
background-image: url();
}
.name {
font-size: 16px;
color: #333;
font-weight: bold;
}
.intro {
margin: 10rpx 0 0 0;
color: #666;
font-size: 14px;
}
.img-nodata {
position: relative;
top: 0;
left: 50%;
transform: translate(-50%, 0);
width: 352rpx;
height: 338rpx;
margin-bottom: 60rpx;
}
</style>
+163
View File
@@ -0,0 +1,163 @@
<template>
<view class="policy">
<view class="list-item" v-for="(item, index) in currentList" :key="index" @click="fileItemClick(item)">
<view class="v12-justify-start v12-align-center">
<view class="v12-font-bold v12-font-28">·</view>
<view class="item-title">{{ item.title }}</view>
</view>
<view class="item-action v12-dark"><u-icon name="arrow-right" color="#fff" size="8"></u-icon></view>
</view>
<view class="more-wrap">
<view class="more-btn v12-primary v12-align-center" @click="showMore" v-if="list.length > 0">
{{ isMore ? '查看更多' : '查看更多' }}
<view class="item-action v12-white"><u-icon name="arrow-right" color="#C52733 " size="12"></u-icon></view>
</view>
</view>
<image
v-if="list.length === 0"
:src="webUrl + '/orderIcon/wu.png'"
class="img-nodata"
mode="scaleToFill"
/>
</view>
</template>
<script>
import {
getPorjectPolicyFiles,
} from "@/api/product"
export default {
props: {
landmarkDataId: null
},
data() {
return {
webUrl: this.$VUE_APP_RESOURCES_URL,
list: [],
currentList: [],
isMore: false
}
},
watch: {
landmarkDataId(value) {
if(value) {
this.getData()
}
}
},
mounted() {
this.getData()
},
methods: {
getData() {
const params = {
landmarkDataId: this.landmarkDataId,
page: 1,
limit: 99999
}
getPorjectPolicyFiles(params).then(res => {
this.list = res.data.records || []
if(this.isMore) {
this.currentList = this.list
} else {
this.currentList = this.list.filter((item, index) => index < 3)
}
})
},
showMore() {
// this.isMore = !this.isMore
// if(this.isMore) {
// this.currentList = this.list
// } else {
// this.currentList = this.list.filter((item, index) => index < 3)
// }
uni.navigateTo({ url: `/pages/home/searchPage?id=${this.landmarkDataId}&type=2` })
},
fileItemClick(file) {
const url = file.attachment || ''
if (!url) return
uni.downloadFile({
url,
success: res => {
const filePath = res.tempFilePath
wx.openDocument({
filePath,
fail: (err) => {
console.log(err)
}
})
}
})
},
}
}
</script>
<style lang="scss" scoped>
.more-btn{
.item-action{
width: 20rpx;
height: 20rpx;
margin-left: 10rpx;
}
}
.policy{
padding: 20rpx;
background: #f0f0f0;
border-radius: 24rpx;
}
.list-item{
display: flex;
justify-content: space-between;
background: #fff;
margin-bottom: 20rpx;
border-radius: 24rpx;
align-items: center;
padding: 10rpx 20rpx;
}
.item-action{
font-size: 22rpx;
width: 24rpx;
height: 24rpx;
color: #fff;
padding: 5rpx;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
line-height: 24rpx;
}
.item-title{
font-size: 24rpx;
color: #333;
margin-left: 10rpx;
max-width: 520rpx;
}
.log-img{
width: 68rpx;
height: 68rpx;
background: rgba(197,39,51,0.39);
border-radius: 50%;
}
.more-wrap{
display: flex;
align-content: center;
justify-content: center;
}
.more-btn{
color: #fff;
font-size: 28rpx;
padding: 5rpx 20rpx;
border-radius: 30rpx;
}
.img-nodata {
position: relative;
top: 0;
left: 50%;
transform: translate(-50%, 0);
width: 352rpx;
height: 338rpx;
margin-bottom: 60rpx;
}
</style>
+148
View File
@@ -0,0 +1,148 @@
<template>
<view class="projects">
<view class="tabs-wrap">
<view
@click="handleTab(item.value)"
class="tab-item"
:class="{
'tab-isActived': actived === item.value
}"
v-for="(item, index) in tabs"
:key="index">
<view
:class="{
'tab-isActived': actived === item.value,
'no-actived-1': actived === 2 && item.value === 1,
'no-actived-2': actived === 1 && item.value === 2,
'no-actived-3': actived === 3 && item.value === 2,
'no-actived-4': actived === 2 && item.value === 3,
}"
>
<image class="tab-icon" :src="webUrl + '/orderIcon/' + item.icon" mode="aspectFit|aspectFill|widthFix"></image>
{{ item.name }}
<!-- <view>
<view>{{ item.name }}</view>
</view> -->
</view>
</view>
</view>
<view
class="content"
:class="{
'first-one': actived === 1,
'last-one': actived === 3
}"
>
<recommend v-if="actived === 1" :landmarkDataId="landmarkDataId"></recommend>
<policy v-if="actived === 2" :landmarkDataId="landmarkDataId"></policy>
<serviec v-if="actived === 3" :info="info"></serviec>
</view>
</view>
</template>
<script>
import recommend from './recommend.vue'
import policy from './policy.vue'
import serviec from './serviec.vue'
export default {
components: {
recommend,
policy,
serviec
},
props: {
landmarkDataId: null,
info: {
default:() => {}
}
},
data() {
return {
webUrl: this.$VUE_APP_RESOURCES_URL,
actived: 1,
tabs: [
{name: '企业推荐', value: 1, icon: 'qytj.png'},
{name: '政策', value: 2, icon: 'zc.png'},
{name: '服务', value: 3, icon: 'fw.png'},
]
}
},
methods: {
handleTab(value) {
this.actived = value
}
}
}
</script>
<style lang="scss" scoped>
.tab-icon{
height: 30rpx;
width: 30rpx;
margin-right: 10rpx;
}
.fit-box-inner{
background: #f0f0f0;
width: initial;
height: inherit;
width: 40rpx;
}
.first-one{
border-radius: 0 24rpx 24rpx 24rpx !important;
}
.last-one{
border-radius: 24rpx 0 24rpx 24rpx !important;
}
.fit-box{
width: 40rpx;
height: initial;
background: #fff;
display: flex;
}
.projects{
padding: 20rpx;
}
.tabs-wrap{
display: flex;
justify-content: space-between;
}
.tab-item{
text-align: center;
flex: 1;
font-size: 32rpx;
font-weight: bold;
background: #fff;
view{
padding: 20rpx 0;
background: #f0f0f0;
width: inherit;
height: inherit;
display: flex;
justify-content: center;
align-items: center
}
}
.no-actived-1{
border-radius: 0 0 24rpx 0 !important;
}
.no-actived-2{
border-radius: 0 0 0 24rpx !important;
}
.no-actived-3{
border-radius: 0 0 24rpx 0 !important;
}
.no-actived-4{
border-radius: 0 0 0 24rpx !important;
}
.tab-isActived{
background: #fff !important;
border-radius: 24rpx 24rpx 0 0 !important;
position: relative;
}
.content{
background: #fff;
padding: 20rpx 10rpx;
border-radius: 24rpx;
}
</style>
+162
View File
@@ -0,0 +1,162 @@
<template>
<view class="recommend">
<view class="list-item" v-for="(item, index) in currentList" :key="index" @click="toStore(item.hotelId)">
<view class="v12-justify-start v12-align-center">
<view class="log-img">
<image class="log-img" :src="item.logo" mode="aspectFit|aspectFill|widthFix"></image>
</view>
<view class="item-title one-t">{{ item.name }}</view>
</view>
<view class="item-action-wrap v12-primary">
点击查看
<view class="item-action v12-white"><u-icon name="arrow-right" color="#C52733" size="8"></u-icon></view>
</view>
</view>
<view class="more-wrap" v-if="isMore ? isMore : list.length > 3">
<view class="more-btn v12-primary" @click="showMore">
{{ isMore ? '收起' : '查看更多' }}
<view class="item-action v12-white"><u-icon name="arrow-right" color="#C52733 " size="12"></u-icon></view>
</view>
</view>
<image
v-if="list.length === 0"
:src="webUrl + '/orderIcon/wu.png'"
class="img-nodata"
mode="scaleToFill"
/>
</view>
</template>
<script>
import {
getPorjectRecommend,
} from "@/api/product"
export default {
props: {
landmarkDataId: null
},
data() {
return {
webUrl: this.$VUE_APP_RESOURCES_URL,
list: [],
currentList: [],
isMore: false
}
},
watch: {
landmarkDataId(value) {
if(value) {
this.getData()
}
}
},
mounted() {
this.getData()
},
methods: {
getData() {
getPorjectRecommend({landmarkDataId: this.landmarkDataId}).then(res => {
this.list = res.data || []
if(this.isMore) {
this.currentList = this.list
} else {
this.currentList = this.list.filter((item, index) => index < 3)
}
})
},
toStore(id) {
if (!id) {
return
}
uni.navigateTo({
url: '/pagesInn/inn/innHome?id=' + id
})
},
showMore() {
this.isMore = !this.isMore
if(this.isMore) {
this.currentList = this.list
} else {
this.currentList = this.list.filter((item, index) => index < 3)
}
}
}
}
</script>
<style lang="scss" scoped>
.more-btn{
display: flex;
align-items: center;
justify-content: center;
.item-action{
width: 20rpx;
height: 20rpx;
margin-left: 10rpx;
padding: 5rpx;
}
}
.recommend{
padding: 20rpx;
background: #f0f0f0;
border-radius: 24rpx;
}
.list-item{
display: flex;
justify-content: space-between;
background: #fff;
margin-bottom: 20rpx;
border-radius: 24rpx;
align-items: center;
padding: 10rpx 20rpx;
}
.item-action-wrap{
font-size: 22rpx;
display: flex;
align-items: center;
border-radius: 30rpx;
color: #fff;
padding: 5rpx 10rpx;
}
.item-action{
width: 20rpx;
height: 20rpx;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
margin-left: 10rpx;
}
.item-title{
font-size: 26rpx;
color: #333;
margin-left: 20rpx;
max-width: 180rpx;
}
.log-img{
width: 68rpx;
height: 68rpx;
border-radius: 50%;
}
.more-wrap{
display: flex;
align-content: center;
justify-content: center;
}
.more-btn{
color: #fff;
font-size: 28rpx;
padding: 5rpx 20rpx;
border-radius: 30rpx;
}
.img-nodata {
position: relative;
top: 0;
left: 50%;
transform: translate(-50%, 0);
width: 352rpx;
height: 338rpx;
margin-bottom: 60rpx;
}
</style>
+124
View File
@@ -0,0 +1,124 @@
<template>
<view class="service">
<view class="map-cont">
<map
:longitude="info.longitude"
:latitude="info.latitude"
:markers="[{latitude: info.latitude,longitude: info.longitude,width: 20, height: 25, iconPath: webUrl + '/page/qianxian/icon-location.png'}]"
style="width: 100%;height: 100%; border-radius: 20rpx;"
/>
</view>
<view class="v12-mt-3 info-wrap">
<view>
<view class="v12-font-32 v12-font-bold v12-mb-2">{{ info.name || '' }}</view>
<view class="v12-font-24 v12-mb-1 v12-dark1-text">办公时间:{{ info.officeTime || '' }}</view>
<view class="v12-font-24 v12-mb-1 v12-dark1-text">{{ info.address || '' }}</view>
</view>
<view class="action-wrap">
<view @click="showLocation">
<view>
<image :src="webUrl + '/page/qianxian/icon-addr.png'" class="img" />
</view>
<view class="text-center">导航</view>
</view>
<view @click="call">
<view>
<image :src="webUrl + '/page/qianxian/icon-phone.png'" class="img" />
</view>
<view class="text-center">客服</view>
</view>
</view>
</view>
<view style="padding: 0 20rpx;"><u-divider></u-divider></view>
<view class="intro-wrap">
<view class="v12-dark-text v12-font-bold">
简介
</view>
<view class="intro-text">
{{ info.intro || '' }}
</view>
</view>
</view>
</template>
<script>
export default {
props: {
info: {
default: () => {}
}
},
data() {
return {
webUrl: this.$VUE_APP_RESOURCES_URL,
}
},
mounted() {
console.log(this.info);
},
methods: {
showLocation() {
console.log(this.info);
if (!this.info.latitude || !this.info.longitude) {
this.$dialog.error('暂无位置信息')
} else {
uni.openLocation({
latitude: this.info.latitude,
longitude: this.info.longitude
})
}
},
call() {
if (this.info.tel) {
uni.makePhoneCall({
phoneNumber: this.info.tel
})
} else {
this.$dialog.error('电话为空')
}
},
}
}
</script>
<style lang="scss" scoped>
.intro-text{
color: #999;
font-size: 24rpx;
margin-top: 20rpx;
}
.intro-wrap{
padding: 0 20rpx 20rpx;
}
.action-wrap{
display: flex;
align-items: center;
.text-center{
text-align: center;
font-size: 24rpx;
color: #666;
}
}
.info-wrap{
display: flex;
justify-content: space-between;
padding: 40rpx 20rpx 0;
}
.service{
position: relative;
}
.map-cont {
height: 400rpx;
border-radius: 20rpx;
background: #f2f1ed;
padding: 20rpx;
box-sizing: border-box;
}
.img{
display: block;
width: 64rpx;
height: 64rpx;
margin: 0 0 16rpx 16rpx;
}
</style>
+186
View File
@@ -0,0 +1,186 @@
<template>
<view v-if="posterImageStatus" class="poster-first">
<div class="posterCanvasWarp">
<canvas class="posterCanvas" canvas-id="myCanvas"></canvas>
</div>
<view class="poster-pop" v-show="canvasStatus">
<img
src="@/static/images/poster-close.png"
class="close"
@click="posterImageClose"
mode="widthFix"
/>
<image
:src="posterImage"
alt="tp"
class="poster-image"
:show-menu-by-longpress="false"
mode="widthFix"
/>
<view class="v12-white-text v12-radius-20 share-btn" @click="saveImg(posterImage)">保存图片</view>
</view>
<view class="mask"></view>
</view>
</template>
<script>
import { getShareImg } from "@/api/product";
export default {
name: "StorePoster",
data: function () {
return {
canvasStatus: false,
posterImage: "",
posterImageStatus: false,
};
},
mounted: function () {},
methods: {
saveImg(url) {
uni.showLoading({
mask:true,
})
uni.downloadFile({
url: url, // 文件的下载地址
success(res) {
if (res.statusCode === 200) {
let tempFilePath = res.tempFilePath; // 临时文件路径
uni.saveImageToPhotosAlbum({
filePath: tempFilePath,
success:(success)=> {
uni.showToast({
title: "保存成功",
icon: "none",
mask: true,
});
},
fail(err) {
uni.showToast({
title: "保存失败,请重新尝试",
icon: "none",
mask: true,
});
},
})
}
},
});
},
posterImageClose() {
this.posterImageStatus = false;
this.canvasStatus = false;
this.$emit("setPosterImageStatus");
},
savePosterPath(id) {
uni.showLoading({ title: "海报生成中", mask: true });
getShareImg(id)
.then((res) => {
this.canvasStatus = true;
this.posterImage = res.data;
this.posterImageStatus = true;
})
.finally(() => {
uni.hideLoading();
});
},
},
};
</script>
<style scoped lang="less" lang="less">
.share-btn{
text-align: center;
padding: 20rpx 0;
font-size: 28rpx;
background: #ff564a;
}
.poster-first {
overscroll-behavior: contain;
}
.poster-pop {
width: 6 * 100rpx;
height: 8 * 100rpx;
position: fixed;
left: 50%;
transform: translateX(-50%);
z-index: 99;
top: 50%;
margin-top: -4.6 * 100rpx;
}
.poster-pop .canvas {
background-color: #ffffff;
height: 8 * 100rpx;
}
.poster-pop .poster-image {
width: 100%;
height: auto;
border-radius: 20rpx;
}
.poster-pop .canvas .image {
width: 4.5 * 100rpx;
height: 4.5 * 100rpx;
display: block;
}
.poster-pop .canvas .text {
text-align: center;
color: #000000;
margin-top: 0.32 * 100rpx;
}
.poster-pop .canvas .text.black {
height: 0.68 * 100rpx;
}
.poster-pop .canvas .text.rad {
color: #ff0000;
}
.poster-pop .canvas .code {
height: 1.4 * 100rpx;
display: flex;
}
.poster-pop .canvas .code .code-img {
width: 33%;
padding: 0.06 * 100rpx;
}
.poster-pop .canvas .code .code-img image {
width: 100%;
}
.poster-pop .canvas .code .code-text {
width: 60%;
font-size: 0.12 * 100rpx;
line-height: 1.64 * 100rpx;
}
.poster-pop .close {
width: 0.46 * 100rpx;
height: 0.75 * 100rpx;
position: fixed;
right: 0;
top: -0.73 * 100rpx;
display: block;
}
.poster-pop .save-poster {
background-color: #df2d0a;
font-size: 0.22 * 100rpx;
color: #fff;
text-align: center;
height: 0.76 * 100rpx;
line-height: 0.76 * 100rpx;
width: 100%;
margin-top: -0.04 * 100rpx;
}
.poster-pop .keep {
color: #fff;
text-align: center;
font-size: 0.25 * 100rpx;
margin-top: 0.1 * 100rpx;
}
.mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.6);
z-index: 9;
}
</style>
+232 -44
View File
@@ -109,12 +109,97 @@
/>
</swiper-item>
</swiper>
<image
<!-- <image
v-if="contentFestival.indexImage"
:src="contentFestival.indexImage"
mode="scaleToFill"
@click="goFestival"
/>
/> -->
<!-- <swiper
class="swiper"
circular
autoplay
:interval="3000">
<swiper-item class="" item-id="" v-if="contentFestival.indexImage">
<image
:src="contentFestival.indexImage"
mode="scaleToFill"
@click="goFestival"
/>
</swiper-item>
<swiper-item class="" item-id="">
<view class="new-zone-wrap">
<view class="kd" @click="toKD">
<view class="img-wrap">
<image style="width: 112rpx; height:26rpx" :src="webUrl + '/orderIcon/kd.png'" mode="heightFix|widthFix"></image>
</view>
<view class="tip-img">
<image style="width: 142rpx; height:66.11rpx;border-radius: 0" :src="webUrl + '/orderIcon/dot.png'" mode="heightFix|widthFix"></image>
</view>
<view class="kd-cover">
<video v-if="indexVideo" class="kd-cover v12-mt-0" :src="indexVideo" controls :poster="indexVideo + '?vframe/jpg/offset/1'"></video>
<view class="enter-btn">点击进入</view>
</view>
</view>
<view class="xpsx">
<view class="img-wrap">
<image style="width: 140rpx; height:26.44rpx" :src="webUrl + '/orderIcon/xp.png'" mode="heightFix|widthFix"></image>
</view>
<view class="new-zone v12-mt-2" @click="toNewZone">
<swiper autoplay circular style="width: 100%;" interval="3000" next-margin="40rpx">
<swiper-item v-for="(item, index) in newZone" :key="index">
<view class="new-item">
<image :src="item.productImage" style="width: 120rpx; height: 120rpx; border-radius:10rpx"></image>
<view class="goods-info">
<view class="v12-font-22 more-t">{{ item.productTitle }}</view>
<view class="v12-primary-text v12-font-bold price-wrap">
<text class="v12-font-16"></text>
<text class="v12-font-24">{{ item.productPrice }}</text>
</view>
</view>
</view>
</swiper-item>
</swiper>
</view>
</view>
</view>
</swiper-item>
</swiper> -->
<view class="new-zone-wrap">
<view class="kd" @click="toKD">
<view class="img-wrap" style="padding-top: 10rpx; box-sizing:border-box">
<image style="width: 112rpx; height:26rpx" :src="webUrl + '/orderIcon/kd.png'" mode="heightFix|widthFix"></image>
</view>
<view class="tip-img">
<image style="width: 142rpx; height:66.11rpx;border-radius: 0" :src="webUrl + '/orderIcon/dot.png'" mode="heightFix|widthFix"></image>
</view>
<view class="kd-cover">
<video play-btn-position="center" v-if="indexVideo" :show-fullscreen-btn="false" class="kd-cover v12-mt-0" :src="indexVideo" controls autoplay :poster="indexVideo + '?vframe/jpg/offset/1'"></video>
<view class="enter-btn">点击进入</view>
</view>
</view>
<view class="xpsx">
<view class="img-wrap">
<image style="width: 140rpx; height:26.44rpx" :src="webUrl + '/orderIcon/xp.png'" mode="heightFix|widthFix"></image>
</view>
<view class="new-zone v12-mt-2" @click="toNewZone">
<swiper autoplay circular style="width: 100%;" interval="3000" next-margin="40rpx">
<swiper-item v-for="(item, index) in newZone" :key="index">
<view class="new-item">
<image :src="item.productImage" style="width: 120rpx; height: 120rpx; border-radius:10rpx"></image>
<view class="goods-info">
<view class="v12-font-22 more-t">{{ item.productTitle }}</view>
<view class="v12-primary-text v12-font-bold price-wrap">
<text class="v12-font-16"></text>
<text class="v12-font-24">{{ item.productPrice }}</text>
</view>
</view>
</view>
</swiper-item>
</swiper>
</view>
</view>
</view>
</view>
</view>
<!-- 2022/6/20 add 尖货推荐 -->
@@ -291,6 +376,9 @@ export default {
oldUserPopup: {},
// 民宿专题轮播
contentHomestay: [],
// 新品推荐
newZone: [],
indexVideo: '',
// 节日专题
contentFestival: {},
pageKeyId: Object.freeze('appIndex'),
@@ -329,12 +417,17 @@ export default {
this.scrollTop = e.scrollTop
},
methods: {
toKD() {
this.$yrouter.push('/pages/VideoPlayBackList/VideoPlayBackList')
},
toNewZone() {
this.$yrouter.push('/pages/home/newZone')
},
init() {
getShareImage().then(({ data }) => this.shareAppImg = data)
sharpBannar().then(res => {
const { data } = res
this.bannerList = data.banner
this.getPageData()
this.bannerList = data.banner || []
})
this.getLocationData()
},
@@ -345,54 +438,67 @@ export default {
this.getLocationData()
}))
},
async getLocationData() {
getLocationData() {
this.isLocating = true
const mapData = await getLocationPromise()
if (mapData.errMsg) {
const { latitude, longitude } = mapData
const address = await getCurAddress(latitude, longitude)
this.cityName = address[1].data.result.ad_info.city
this.isLocating = false
getLocationPromise().then(mapData => {
if (mapData.errMsg) {
const { latitude, longitude } = mapData
getCurAddress(latitude, longitude).then(address => {
this.cityName = address[1].data.result.ad_info.city
this.isLocating = false
this.getPageData()
}).catch(() => {
this.getPageData()
})
} else {
this.isLocating = false
this.getPageData()
}
}).catch(() => {
this.getPageData()
} else {
this.isLocating = false
}
})
},
getPageData() {
const _this = this
uni.showLoading({ title: '加载中' })
getHomeData({ cityName: this.cityName }).then(res => {
const { data } = res
_this.$set(_this, 'banner', data.banner)
_this.$set(_this, 'menus', data.menus)
_this.$set(_this, 'landmarkGoodsImage', data.landmarkGoodsImage)
_this.$set(_this, 'countyFamousImage', data.countyFamousImage)
_this.$set(_this, 'oldUserPopup', data.oldUserPopup)
_this.$set(_this, 'contentHomestay', data.contentHomestayV2)
_this.$set(_this, 'contentFestival', data.contentFestivalV2)
_this.bastLeftList = []
_this.bastRightList = []
_this.lotteryCanDraw = data.lotteryCanDraw
for (let i = 0; i < data.bastList.length; i++) {
if (i % 2 === 0) {
// 店铺排名
if (i === 4 && data.hotelList.length > 0) {
_this.bastLeftList.push({
type: 'hotel',
list: data.hotelList
})
const { success, data } = res
if (success && data) {
_this.$set(_this, 'banner', data.banner || [])
_this.$set(_this, 'menus', data.menus || [])
_this.$set(_this, 'landmarkGoodsImage', data.landmarkGoodsImage || {})
_this.$set(_this, 'countyFamousImage', data.countyFamousImage || {})
_this.$set(_this, 'oldUserPopup', data.oldUserPopup || {})
_this.$set(_this, 'contentHomestay', data.contentHomestayV2 || [])
_this.$set(_this, 'contentFestival', data.contentFestivalV2 || [])
_this.$set(_this, 'newZone', data.newGoods || [])
_this.indexVideo = data.indexVideo ? data.indexVideo.videoUrl : ''
_this.bastLeftList = []
_this.bastRightList = []
_this.lotteryCanDraw = data.lotteryCanDraw || false
if (data.bastList) {
for (let i = 0; i < data.bastList.length; i++) {
if (i % 2 === 0) {
// 店铺排名
if (i === 4 && data.hotelList.length > 0) {
_this.bastLeftList.push({
type: 'hotel',
list: data.hotelList
})
}
_this.bastLeftList.push(data.bastList[i])
}
if (i % 2 === 1) {
// 尖货专题
if (i === 3 && data.contentBest.length > 0) {
_this.bastRightList.push({
type: 'swiper',
list: data.contentBest
})
}
_this.bastRightList.push(data.bastList[i])
}
}
_this.bastLeftList.push(data.bastList[i])
}
if (i % 2 === 1) {
// 尖货专题
if (i === 3 && data.contentBest.length > 0) {
_this.bastRightList.push({
type: 'swiper',
list: data.contentBest
})
}
_this.bastRightList.push(data.bastList[i])
}
}
uni.hideLoading()
@@ -796,5 +902,87 @@ export default {
margin: 0 0 0 20rpx;
}
}
}
.new-zone-wrap{
width: 336rpx;
height: 480rpx;
background: rgba(240,240,240,0.8);
border-radius: 16rpx;
padding: 10rpx;
box-sizing: border-box;
display: flex;
flex-direction: column;
justify-content: space-between;
.kd{
width: 100%;
height: 266rpx;
background: #fff;
border-radius: 16rpx;
padding: 10rpx;
box-sizing: border-box;
position: relative;
}
.xpsx{
width: 100%;
height: 180rpx;
background: #fff;
border-radius: 16rpx;
padding: 10rpx;
box-sizing: border-box;
}
}
.img-wrap{
height: 27rpx;
width: 140rpx;
image{
border-radius: 0 !important;
vertical-align: text-top !important
}
}
.tip-img{
position: absolute;
right: 0;
top: 0;
}
.kd-cover{
width: 100%;
height: 180rpx;
border-radius: 20rpx;
margin-top: 15rpx;
}
.enter-btn{
color: #333;
background-color: rgba(255,255,255,0.6);
font-size: 28rpx;
width: fit-content;
padding: 6rpx 20rpx;
border-radius: 100rpx;
position: absolute;
left: 0;
right: 0;
bottom: 40rpx;
margin: auto;
}
.new-zone{
display: flex;
align-items: center;
justify-content: flex-start;
overflow: scroll;
}
.new-item{
display: flex;
margin-right: 20rpx;
align-items: center;
}
.goods-info{
width: 122rpx;
height: 110rpx;
background: rgba(240,240,240,0.8);
border-radius: 0rpx 20rpx 20rpx 0rpx;
box-sizing: border-box;
padding: 10rpx;
}
.price-wrap{
}
</style>
+141 -131
View File
@@ -1,92 +1,81 @@
<template>
<view class="pkg-product-land-mark">
<view v-if="isLoad">
<view class="top-box">
<view class="nav flex">
<view
v-for="(item,index) in navList"
:key="index"
:class="{
'active': navIndex === index
}"
class="flex flex-0 ai-center nav-item"
@click="changeNav(index)"
>
<image
:src="webUrl + '/home/landmark-icon.png'"
class="bg"
/>
{{ item.provinceName }}
<view class="top-wrap">
<view class="top-box">
<view class="nav flex">
<view
v-for="(item,index) in navList"
:key="index"
:class="{
'active': navIndex === index
}"
class="flex flex-0 ai-center nav-item"
@click="changeNav(index)"
>
<image
:src="webUrl + '/home/landmark-icon.png'"
class="bg"
/>
{{ item.provinceName }}
</view>
</view>
</view>
<view class="view-box">
<view class="map-box">
<image
:src="mapData.mapImage"
mode="widthFix"
class="img-map"
/>
<view class="view-box">
<view class="map-box">
<image
:src="mapData.mapImage"
mode="widthFix"
class="img-map"
/>
</view>
<view class="share-btn" @click="toShare"><u-icon name="share-square" color="#C52733"></u-icon>分享给好友</view>
</view>
</view>
</view>
<view
v-if="mapData.middleImage"
class="middle-img-wrap"
>
<image
v-if="mapData.middleImage"
:src="mapData.middleImage"
mode="widthFix"
class="img"
/>
<view class="tabs-wrap">
<view @click="handleTab(item.value)" :class="{ 'tab-actived' : item.value === actived }" class="tab-item" v-for="(item, index) in tabs" :key="index">{{ item.name }}</view>
</view>
</view>
<view v-if="goods.length > 0" class="list-wrap">
<view class="list-box flex flex-wrap">
<view
v-for="item in goods"
:key="item.id"
class="item"
@click="goGoods(item.productId)"
>
<image
:src="webUrl + '/home/mark-land.png'"
class="mark-img"
/>
<image
:src="item.storeImage"
mode="scaleToFill"
class="good-img"
/>
<view class="content flex flex-col jc-between">
<view class="more-t bold">{{ item.title }}</view>
<view class="one-t desc">{{ item.description }}</view>
<view class="flex jc-between ai-center">
<text class="price">{{ item.price }}</text>
<image
:src="webUrl + '/home/icon-cart.png'"
class="btn"
/>
</view>
</view>
<view class="list-wrap">
<view v-if="actived === 1">
<projects :landmarkDataId="landmarkDataId" :info="mapData.serviceInfo"></projects>
</view>
<view v-if="actived === 2">
<culture :landmarkDataId="landmarkDataId"></culture>
</view>
<view v-if="actived === 3">
<information :landmarkDataId="landmarkDataId"></information>
</view>
<view v-if="actived === 4">
<goods :goods="goods" v-if="goods.length > 0"></goods>
<!-- <image
v-if="isCompleteLoadGood"
:src="webUrl + '/home/no-data-bg.png'"
mode="widthFix"
class="loadend"
/> -->
<view class="v12-px-6" >
<u-divider :text="goods.length === 0 ? ' · 暂无数据 · ' : ' · 已经到底啦 · '" textPosition="center"></u-divider>
</view>
</view>
<image
v-if="isCompleteLoadGood"
:src="webUrl + '/home/no-data-bg.png'"
mode="widthFix"
class="loadend"
/>
</view>
<image
v-if="goods.length === 0"
:src="webUrl + '/20231023131208675588.png'"
class="img-nodata"
mode="scaleToFill"
/>
</view>
</view>
<goo-skeleton
v-else
:show-avatar="false"
/>
<shareImg ref="shareImg"></shareImg>
</view>
</template>
@@ -96,12 +85,31 @@ import {
getProvinceGoods,
getProvinceList
} from "@/api/product"
import goods from "./components/goods.vue"
import { pageListenMixins } from '@/mixins/pageListenMixins'
import projects from './components/projects.vue'
import culture from "./components/culture.vue"
import information from "./components/information.vue"
import shareImg from "./components/shareImg.vue"
export default {
name: 'LandMarkPage',
components: {
goods,
projects,
culture,
information,
shareImg
},
mixins: [pageListenMixins],
data() {
return {
actived: 1,
tabs: [
{name: '项目', value: 1},
{name: '文化', value: 2},
{name: '资讯', value: 3},
{name: '产品', value: 4},
],
webUrl: this.$VUE_APP_RESOURCES_URL,
keyword: '',
isLoad: false,
@@ -111,10 +119,23 @@ export default {
mapData: null,
goods: [],
isCompleteLoadGood: false,
pageKeyId: Object.freeze('landmarkGoodsIndex')
pageKeyId: Object.freeze('landmarkGoodsIndex'),
posterImageStatus: false,
sceneId: null
}
},
onLoad() {
computed: {
landmarkDataId() {
return this.navList[this.navIndex] ? this.navList[this.navIndex]['id'] : null
}
},
onLoad(opts) {
console.log('进入地标页');
console.log(opts, '地标opts');
if(opts && opts.scene) {
console.log(opts.scene, 'opts.scene');
this.sceneId = decodeURIComponent(opts.scene).split('&')[0].split('=')[1]
}
this.getNavList()
},
onHide() {
@@ -128,9 +149,19 @@ export default {
this.fetchGoods()
},
methods: {
toShare() {
this.$refs.shareImg.savePosterPath(this.landmarkDataId)
},
handleTab(value) {
this.actived = value
},
getNavList() {
getProvinceList().then(({data}) => {
this.navList = data
if(this.sceneId) {
this.navIndex = this.navList.map(e => +e.id).indexOf(+this.sceneId)
console.log(this.navIndex, 'navIndex');
}
this.changeNav(this.navIndex)
})
},
@@ -139,13 +170,14 @@ export default {
this.page = 1
this.goods = []
this.isCompleteLoadGood = false
this.actived = 1
this.getMap()
this.fetchGoods()
},
getMap() {
const id = this.navList[this.navIndex]['id']
getProvince(id).then(({data}) => {
this.mapData = data
this.mapData = data || {}
this.isLoad = true
})
},
@@ -171,9 +203,43 @@ export default {
</script>
<style scoped lang="less">
.share-btn{
color: #C52733;
background: rgba(211,92,101,0.5);
font-size: 24rpx;
width: fit-content;
padding: 8rpx 10rpx;
border-radius: 20rpx 0 0 20rpx;
position: absolute;
right: 0;
top: 80%;
display: flex;
align-items: center;
}
.tabs-wrap{
display: flex;
justify-content: space-around;
padding: 20rpx 20rpx 0;
color: #333;
font-weight: bold;
font-size: 36rpx;
}
.tab-item{
}
.tab-actived{
color: #fff;
font-weight: bold;
background: linear-gradient(to right, #C52733 0%, rgba(211,92,101,0.91) 54%, rgba(255,255,255,0.62) 100%);
border-radius: 38rpx 0rpx 0 38rpx;
padding: 0 20rpx;
}
.top-wrap{
padding: 20rpx 20rpx 0;
}
.pkg-product-land-mark {
padding: 20rpx;
background: #f0f0f0;
min-height: 100vh;
.top-box {
border-radius: 20rpx;
background-color: #fff;
@@ -219,73 +285,14 @@ export default {
}
.middle-img-wrap {
margin: 20rpx 0 0 0;
background: #fff;
padding: 20rpx;
.img {
display: block;
width: 100%;
border-radius: 20rpx;
}
}
.list-box {
margin-top: 24rpx;
.item {
position: relative;
width: 344rpx;
margin-right: 22rpx;
margin-bottom: 22rpx;
border-radius: 20rpx;
background: #FFFFFF;
overflow: hidden;
.mark-img {
position: absolute;
top: 0;
right: 0;
z-index: 3;
display: block;
width: 84rpx;
height: 74rpx;
}
.good-img {
display: block;
width: 344rpx;
height: 344rpx;
}
.content {
height: 156rpx;
padding: 8rpx 8rpx 18rpx 8rpx;
.more-t {
font-size: 28rpx;
line-height: 36rpx;
color: #333;
}
.desc {
padding: 5rpx 0;
height: 34rpx;
color: #FFC543;
font-size: 24rpx;
}
.price {
font-size: 40rpx;
line-height: 40rpx;
font-weight: bold;
color: #C52733;
}
.btn {
display: block;
width: 48rpx;
height: 48rpx;
}
}
}
.item:nth-child(2n) {
margin-right: 0;
}
}
.list-wrap {
margin: 20rpx 0 0 0;
.loadend {
width: 100%;
}
}
.img-nodata {
position: relative;
top: 0;
@@ -296,4 +303,7 @@ export default {
margin-bottom: 60rpx;
}
}
.loadend {
width: 100%;
}
</style>
+172
View File
@@ -0,0 +1,172 @@
<template>
<view class="new-zone-page">
<view class="zone-title">
<rich-text :nodes="details.title"></rich-text>
</view>
<view class="banner-zone v12-mt-2">
<image class="banner-zone" :src="bannerImage" ></image>
<!-- <swiper :current="current" interval="3000" circular style="height:760rpx">
<swiper-item v-for="(item, index) in details.newProducts" :key="index">
</swiper-item>
</swiper> -->
<view class="swiper-wrap">
<swiper @change="handleChange" style="height: 100%;" autoplay circular next-margin="460rpx" interval="4000">
<swiper-item v-for="(item, index) in details.newProducts" :key="index" @click="toGoods(item)">
<view class="new-card" :class="{'is-actived': current == index}">
<image :src="item.productCover" class="new-card-img" mode="heightFix|widthFix"></image>
<view class="box-emp"></view>
<view class="new-info">
<view class="v12-font-26 one-t" style="width: 100px;text-align:center">{{ item.productTitle }}</view>
<view class="v12-font-bold">
<text></text>
<text class=" v12-font-36">{{ item.productPrice }}</text>
</view>
<view class="buy-btn">即刻选购</view>
</view>
</view>
</swiper-item>
</swiper>
</view>
</view>
<view class="v12-mt-2 v12-align-center">
<view class="primary-title" v-if="details.contentTitle">
{{ details.contentTitle }}
<view class="title-line"></view>
</view>
<view class="secondary-title v12-ml-2" v-if="details.contentSubTitle">
{{ details.contentSubTitle }}
</view>
</view>
<view class="cover-img v12-mt-2" v-for="(item, index) in details.contents" :key="index" @click="toGoods(item)">
<image :src="item.contentImage" class="cover-img" mode="heightFix|widthFix"></image>
</view>
</view>
</template>
<script>
import { queryNewZone } from '@/api/activity'
export default{
data() {
return {
details: {},
current: 0,
}
},
onLoad() {
queryNewZone().then(res => {
this.details = res.data
})
},
computed: {
bannerImage() {
return this.details.newProducts && this.details.newProducts[this.current] && this.details.newProducts[this.current].bannerImage
}
},
methods: {
handleChange(e) {
this.current = e.detail.current
},
toGoods(item) {
uni.navigateTo({
url: `/pages/shop/GoodsCon/index?id=${item.productId}`
})
}
}
}
</script>
<style lang="scss" scoped>
.new-info{
display: flex;
align-items: center;
flex-direction: column;
}
.box-emp{
width: 100%;
height: 85rpx;
}
.buy-btn{
border-radius: 100px;
width: fit-content;
color: #fff;
background: #C52733;
font-size: 28rpx;
padding: 4rpx 20rpx;
}
.swiper-wrap{
position: absolute;
width: 100%;
height: 400rpx;
left: 20rpx;
bottom: 40rpx;
}
.new-card{
width: 220rpx;
height: 240rpx;
background: #fff;
border-radius: 20rpx;
position: absolute;
bottom: -17rpx;
transition: all ease-in-out 0.3s;
scale: 0.85;
left: 15rpx;
}
.is-actived{
// width: 225rpx;
// height: 260rpx;
bottom: 0;
scale: 1;
left: 0;
}
.cover-img{
width: 706rpx;
height: 400rpx;
border-radius: 20rpx;
}
.new-card-img{
position: absolute;
width: 150rpx;
height: 150rpx;
top: -75rpx;
left: 0;
right: 0;
margin: auto;
border-radius: 50%;
}
.primary-title{
width: fit-content;
position: relative;
font-size: 34rpx;
color: #333333;
font-weight: 800;
}
.secondary-title{
font-family: Source Han Sans SC;
font-weight: 500;
font-size: 28rpx;
color: #999999;
line-height: 40rpx;
}
.title-line{
width: 100%;
height: 6rpx;
background: linear-gradient(to right, #C52733 0%, rgba(202,56,67,0.92) 51%, rgba(255,255,255,0) 100%);
border-radius: 6rpx;
position: absolute;
bottom: 3rpx;
}
.new-zone-page{
min-height: 100vh;
background-color: #fff;
padding: 20rpx;
}
.banner-zone{
width: 706rpx !important;
height: 760rpx !important;
border-radius: 20rpx !important;
position: relative !important;
}
</style>
+135
View File
@@ -0,0 +1,135 @@
<template>
<view class="list-page">
<view class="v12-mb-3">
<u-search
placeholder="请输入关键词查询"
v-model="keyword"
shape="round"
:show-action="false"
height="72rpx"
@search="handleSearch"
bg-color="#fff">
</u-search>
</view>
<view v-if="isLoad" class="file-list">
<view
v-for="(file, fileIndex) in currentList"
:key="fileIndex"
class="file-item"
@click="fileItemClick(file)"
>
<view class="name more-t">
·&nbsp;&nbsp;{{ file.title }}
</view>
<image
:src="webUrl + '/page/qianxian/icon-05.png'"
class="icon"
/>
</view>
<view v-if="currentList.length === 0" class="no-data-txt">
暂无数据~
</view>
</view>
<goo-skeleton v-else />
</view>
</template>
<script>
import {
getPorjectPolicyFiles,
} from "@/api/product"
import { pageListenMixins } from '@/mixins/pageListenMixins'
export default {
name: 'searchPage',
mixins: [pageListenMixins],
data() {
return {
webUrl: this.$VUE_APP_RESOURCES_URL,
isLoad: false,
pageKeyId: '',
list: [],
keyword: '',
currentList: []
}
},
onLoad(options) {
const id = options.id
this.getListReq(id)
},
methods: {
handleSearch() {
this.currentList = this.list.filter(item => item.title.indexOf(this.keyword) > -1)
},
getListReq(id) {
const params = {
landmarkDataId : id,
page: 1,
limit: 9999
}
getPorjectPolicyFiles(params).then(res => {
const { success, data } = res
if (success) {
this.isLoad = true
this.list = data.records || []
this.currentList = [...this.list]
}
})
},
fileItemClick(file) {
const url = file.attachment || ''
if (!url) return
uni.downloadFile({
url,
success: res => {
const filePath = res.tempFilePath
wx.openDocument({
filePath,
fail: (err) => {
console.log(err)
}
})
}
})
}
}
}
</script>
<style scoped lang="less">
.list-page {
min-height: 100vh;
padding: 30rpx;
box-sizing: border-box;
background-color: #f0f0f0;
.file-item {
display: flex;
align-items: center;
justify-content: space-between;
height: 100rpx;
width: 100%;
padding: 0 20rpx;
margin: 0 0 20rpx 0;
border-radius: 20rpx;
box-sizing: border-box;
line-height: 36rpx;
background-color: #fff;
.name {
width: calc(100% - 40rpx);
font-size: 28rpx;
}
.icon {
display: block;
width: 26rpx;
height: 26rpx;
}
}
.loadend {
width: 100%;
}
.no-data-txt {
text-align: center;
line-height: 200rpx;
color: #999;
font-size: 36rpx;
}
}
</style>
+7 -1
View File
@@ -248,6 +248,7 @@ export default {
keyword: '',
_orderId: '',
addressId: '',
currentRoute: ''
};
},
components: {
@@ -263,7 +264,10 @@ export default {
this.getOrderListReq();
uni.$on('chooseAddress', (res) => {
this.addressId = res.id
})
const pages = getCurrentPages() // 获取栈实例
this.currentRoute = pages[pages.length - 1].route;
},
onHide() {
this.orderList = [];
@@ -274,7 +278,9 @@ export default {
},
watch: {
addressId(val) {
if(val) {
const pages = getCurrentPages() // 获取栈实例
let currentRoute = pages[pages.length - 2].route;
if(val && this.currentRoute === currentRoute) {
const params = {
addressId: this.addressId,
orderId: this._orderId
+37 -9
View File
@@ -8,7 +8,10 @@
<view :class="refundOrder ? 'on' : ''">
<view class="state v12-primary-text v12-font-32 v12-font-bold" v-if="orderInfo.isTickets===1 && orderInfo._status._type == 2">待核销</view>
<view class="state v12-primary-text v12-font-32 v12-font-bold" v-else>{{ getStatus(orderInfo) }}</view>
<view v-if="getStatus(orderInfo)!=='待付款'" class="v12-mt-2 v12-font-24 v12-secondary-dark-text" :class="{'fails-bg': orderInfo._status._type == -3}">{{ orderInfo._status._msg }}</view>
<view v-if="getStatus(orderInfo)!=='待付款'" class="v12-mt-2 v12-font-24 v12-secondary-dark-text" :class="{'fails-bg': orderInfo._status._type == -3}">
<view>{{ orderInfo._status._msg }}</view>
<view v-if="orderInfo._status._type == -3">驳回原因: {{ orderInfo.refundRejectReason }}</view>
</view>
<view class="pending-payments flex ai-center v12-mt-2 v12-font-24 v12-secondary-dark-text" v-else>
<text>需支付{{ orderInfo.payPrice }}</text>
<!-- {{ orderInfo.paymentTimeout }} -->
@@ -336,15 +339,15 @@
<template v-if="status.type == 1" >
<view class="v12-justify-between " style="width: 100%">
<view class="bnt v12-dark-text v12-dark-border cancel" @click="goRoom(orderInfo)">联系商家</view>
<view class="bnt v12-primary-text v12-primary-border cancel" @click="goGoodsReturn(orderInfo)">申请退款</view>
<view class="bnt v12-primary-text v12-primary-border cancel" @click="goGoodsReturn2(orderInfo)">申请退款</view>
</view>
</template>
<template v-if="status.type == 2">
<view class="v12-justify-between " style="width: 100%">
<view class="bnt v12-dark-text v12-dark-border cancel" @click="goRoom(orderInfo)">联系商家</view>
<view class="v12-justify-between">
<view class="" style="position: relative">
<text class="btn-more v12-font-24 v12-mr-2" @click.stop="showMore = !showMore">更多</text>
<view class="btn-more" style="position: relative">
<view class=" v12-font-24 v12-mr-2" @click.stop="showMore = !showMore">更多</view>
<view class="group-float" v-show="showMore" :style="{top: orderInfo.isExtendedDelivery==0 ? '-60px' : '-32px'}">
<view v-if="orderInfo.isExtendedDelivery==0" class="btn flex jc-center ai-center v12-font-24" @click="toDelay(orderInfo.orderId)">延长收货</view>
<view class="btn flex jc-center ai-center v12-font-24" @click="goGoodsReturn(orderInfo)">申请退款</view>
@@ -362,8 +365,8 @@
<view class="v12-justify-between " style="width: 100%">
<view class="bnt v12-dark-text v12-dark-border cancel" @click="goRoom(orderInfo)">联系商家</view>
<view class="v12-justify-between">
<view class="" style="position: relative">
<text class="btn-more v12-font-24 v12-mr-2" @click="showMore = !showMore">更多</text>
<view class="btn-more" style="position: relative">
<text class=" v12-font-24 v12-mr-2" @click.stop="showMore = !showMore">更多</text>
<view class="group-float" v-show="showMore" :style="{top: '-32px'}">
<view class="btn flex jc-center ai-center v12-font-24" @click="goGoodsReturn(orderInfo)">申请退款</view>
</view>
@@ -546,7 +549,7 @@ export default {
copyClipboard,
handleBody() {
this.showMore = false
this.$forceUpdate()
// this.$forceUpdate()
},
getExpressInfo() {
let params = {
@@ -587,7 +590,7 @@ export default {
const ids = res.data.cartInfo.map(e => e.id)
const addressInfo = res.data.addressInfo
if (ids.length === 0) return
uni.navigateTo({
uni.redirectTo({
url: '/pages/order/OrderSubmission/index?id=' + ids.join(',') + '&address=' + JSON.stringify(addressInfo)
})
})
@@ -726,12 +729,33 @@ export default {
})
},
goGoodsReturn(orderInfo) {
// fix bug#1631 bug#2488
if (parseInt(orderInfo._status._type) === 3) {
if(orderInfo.refundSystemStatus === 0) {
uni.showToast({
title: '订单已收货,如需退款请联系商家',
mask: false,
icon: 'none',
duration: 3000
})
} else {
uni.navigateTo({
url: `/pkg_orders/views/refund?id=${orderInfo.orderId}`
})
}
} else {
uni.navigateTo({
url: `/pkg_orders/views/refund?id=${orderInfo.orderId}`
})
}
},
goGoodsReturn2(orderInfo) {
this.$yrouter.push({
path: "/pages/order/GoodsReturn/index",
query: {
id: orderInfo.orderId
}
});
})
},
goGroupRule(orderInfo) {
this.$yrouter.push({
@@ -917,6 +941,10 @@ export default {
};
</script>
<style scoped lang="less">
.btn-more{
display: flex;
align-items: center;
}
.u-count-down__text{
color: #999 !important;
}
+20 -4
View File
@@ -392,10 +392,26 @@ export default {
let that = this;
uni.$on('chooseAddress', (res) => {
console.log('监听到选择收货地址:' + JSON.stringify(res));
that.addressInfo = res;
//地址切换也要重新计算一下
that.computedPrice('onLoad chooseAddress');
})
setTimeout(() => {
uni.showModal({
title:'确定修改地址吗?',
content: '',
showCancel: true,
cancelText: '取消',
confirmText: '确认',
success:(success)=>{
if(success.confirm) {
that.addressInfo = res;
//地址切换也要重新计算一下
that.computedPrice('onLoad chooseAddress');
}
},
complete:(complete)=>{
uni.hideLoading()
},
})
})
},1000)
if (that.$yroute.query.pinkid !== undefined) {
that.pinkId = that.$yroute.query.pinkid;
}
+7 -2
View File
@@ -211,7 +211,7 @@
<image class="logo" :src="webUrl+'/orderIcon/资质认证.png'" mode="" style="margin-right:0; width: 32rpx; height:32rpx"/>
<text class="v12-ml-1 v12-font-28">资质</text>
</view>
<view class="title v12-secondary-dark-text">{{ storeInfo.merName }}</view>
<view class="store-title title more-t v12-secondary-dark-text">{{ storeInfo.merName }}</view>
</view>
<view class="value v12-justify-end v12-align-center">
<!-- <text class=" v12-dark1-text v12-font-24">库存{{ attr.productSelect.stock }}</text> -->
@@ -1873,7 +1873,12 @@ export default {
font-size: 0.28 * 100rpx;
color: #808080;
}
.after-title{
white-space: nowrap;
}
.store-title{
width: 400rpx;
}
.product-con .store-info .praise .iconfont {
font-size: 0.28 * 100rpx;
}
+5 -1
View File
@@ -2,7 +2,7 @@
<view class="v12-px-3 v12-py-5 qua-card ">
<view class="v12-white v12-radius-16">
<view class="v12-justify-between v12-pa-3">
<text class="v12-font-32 v12-dark-text">
<text class="v12-font-32 v12-dark-text name-wrap">
商家名称
</text>
<text class="v12-font-32 v12-dark-text v12-font-bold">
@@ -48,6 +48,10 @@ export default {
</script>
<style lang="less">
.name-wrap{
white-space: nowrap;
margin-right: 10rpx;
}
.qua-card {
}
.img{
+3 -1
View File
@@ -37,7 +37,9 @@
<textarea
v-model="expect"
placeholder="商品满足你的期待么?说说你的想法,分享给想买的他们吧~"
:maxlength="140"
/>
<view class="v12-px-4 v12-font-24 v12-dark1-text v12-mb-3">{{ expect.length || 0 }} / 140</view>
<view class="upload-title">
添加视频/图片
<text class="txt">
@@ -141,7 +143,7 @@ export default {
this.picArr = uploadList
},
async submit() {
const expect = trim(this.expect)
const expect = trim(this.expect).substring(0, 140)
const product_score = this.scoreList[0].index + 1 === 0 ? "" : this.scoreList[0].index + 1
const service_score = this.scoreList[1].index + 1 === 0 ? "" : this.scoreList[1].index + 1
try {
+1 -1
View File
@@ -63,7 +63,7 @@
</view>
</view>
<view class="v12-px-6" v-if="items.length > 0">
<u-divider text=" · 已经到底啦 · " textPosition="center"></u-divider>
<u-divider text=" · 已经到底啦 · " textPosition="center"></u-divider>
</view>
<image
v-if="items.length === 0"
@@ -117,6 +117,9 @@
</view>
</view>
</view>
<view class="v12-px-6" v-if="spreadList.length === 0">
<u-divider :text=" ' · 此级别未找到会员 · ' " textPosition="center"></u-divider>
</view>
</view>
<Loading :loaded="loaded" :loading="loading"></Loading>
</view>
+975
View File
@@ -0,0 +1,975 @@
<template>
<view class="container">
<hx-navbar
:fixed="true"
:background-color="[[13,197,197],[13,197,197]]"
:pageScroll.sync="scrollData"
statusBarFontColor="#ffffff"
barPlaceholder="hidden"
transparent="auto"
color='#ffffff'
/>
<view>
<view class="cover-box">
<image
v-if="store.coverType === 1"
:src="store.cover"
class="full-img"
mode="widthFix"
/>
<view
v-if="store.coverType === 2"
:style="{
'width': videoStyle.width,
'height': videoStyle.height
}"
class="video-box"
>
<video
:src="store.cover"
:autoplay="true"
:controls="true"
:loop="false"
object-fit="contain"
play-btn-position="center"
class="video"
/>
</view>
</view>
<view class="innInfoWrap">
<view class="bg-color">
<view class="box-mask flex jc-between ai-end">
<view class="inn-name flex-0 more-t">{{ store.name }}</view>
<view
v-if="store.cityName != undefined && store.cityName.length > 0"
class="city-section flex jc-end ai-center"
>
<image
:src="webUrl + '/20210807215539157727.png'"
style="width: 22rpx;height: 22rpx;margin-right: 8rpx;"
/>
<text class="one-t" style="font-size: 24rpx;color: #fff;">{{ store.cityName }}</text>
</view>
</view>
</view>
<view class="body-cont flex ai-end">
<view class="inn-logo flex-0">
<image :src="store.logo" v-if="store.logo"></image>
<image :src="webUrl + '/20230609145651814148.png'" v-else/>
</view>
<view class="a3 flex jc-between ai-end" style="width: 100%">
<view class="inn-owner" style="flex-grow: 1;">{{ store.ownerName }}/店主</view>
<view v-if="store.guaranteeValue" class="guarantee flex jc-center ai-center">
保证金{{ store.guaranteeValue }}
</view>
<view class="flex ai-center zan-favorite-wrap">
<view class="zan-box flex flex-0 ai-end" @click="zanStore">
<template v-if="store.hasZan">
<image :src="webUrl + '/icon/icon-like.png'" class="img" />
<text class="on">{{ store.zan }}</text>
</template>
<template v-else>
<image :src="webUrl + '/icon/icon-like.png'" class="img" />
<text>{{ store.zan }}</text>
</template>
</view>
<view class="flex flex-0 ai-end favorite-box">
<image
v-if="store.hasFavorite"
:src="webUrl + '/icon/icon-star-on.png'"
mode="scaleToFill"
class="icon"
@click="doneFavorite"
/>
<image
v-else
:src="webUrl + '/icon/icon-star-off.png'"
mode="scaleToFill"
class="icon"
@click="doneFavorite"
/>
<text>{{ store.favoriteCount }}</text>
</view>
</view>
</view>
</view>
</view>
<view class="inn-signature-wrap">
<view class="triangle"></view>
<view class="inn-signature">
{{ store.content }}
</view>
<!-- <view class="share-btn" @click="changeShare">
<image :src="webUrl + '/page/hotel/share.png'" class="img" />
</view> -->
</view>
<view v-if="store.name" class="map-wrapper">
<view class="map-cont">
<map
:longitude="store.longitude"
:latitude="store.latitude"
style="width: 750rpx;height: 300rpx;"
/>
</view>
<view class="bg"></view>
<view class="map-info">
<view class="map-info-hd">
<image :src="webUrl + '/page/qianxian/icon-location.png'" class="icon" />
</view>
<view class="map-info-bd more-t">
{{ store.address }}
</view>
<view class="map-info-ft flex">
<view class="item" @click="showLocation">
<image :src="webUrl + '/page/qianxian/icon-addr.png'" class="img" />
<view>导航</view>
</view>
<view class="item" @click="call">
<image :src="webUrl + '/page/qianxian/icon-phone.png'" class="img" />
<view>客服</view>
</view>
</view>
</view>
</view>
<view class="top-tab acea-row row-middle row-center">
<view
v-for="(item, index) in tabList"
:key="index"
:class="item.isShow ? '' : 'hide'"
class="tab"
@click="changeTab(item.value)"
>
<view :class="activeIndex === index ? 'active' : ''" class="tab-item">
{{ item.text }}
</view>
<!-- <view v-show="activeIndex === index" class="btLine"></view> -->
</view>
</view>
<view>
<view v-if="activeIndex === 0" class="goods-section">
<view>
<view v-if="store.products && store.products.length > 0">
<view class="flex flex-wrap">
<view
v-for="(item,index) in store.products"
:key="index"
class="item"
@click="goGoodsConByID(item)"
>
<view>
<image
:src="item.productImage"
class="cover"
/>
<view class="v12-pt-2 v12-font-bold v12-font-28 more-t v12-px-2">{{ item.productName }}</view>
</view>
<view class="" style="padding: 20rpx">
<view class="v12-justify-between v12-align-center v12-pt-2">
<view>
<text class="v12-font-bold v12-primary-text v12-font-24"></text>
<text class="v12-font-bold v12-primary-text v12-font-28">{{ item.productPrice }}</text>
</view>
<view class="btn">
<image
:src="webUrl + '/home/icon-cart.png'"
class="img"
/>
</view>
</view>
<!-- <view class="price-line flex ai-center">
<image
:src="webUrl + '/20221118104357701129.png'"
class="label"
mode="scaleToFill"
/>
<text class="v12-primary-text">{{ item.productPrice }}</text>
</view> -->
</view>
</view>
</view>
</view>
<view v-else class="tab-no-data">
店主还没有上传内容哦~
</view>
</view>
<!-- <goo-skeleton v-else /> -->
</view>
<view
v-if="activeIndex === 1"
class="info-section"
>
<view v-if="isLoadInfoListSuccess">
<view v-if="infoArray.length > 0" class="info-wrapper">
<view class="info-list">
<view
v-for="(item, index) in infoArray"
:key="index"
class="info-item"
>
<view
:class="item.isTop === 1 ? 'info-item-top' : ''"
:style="{
'background-image': item.isTop ? ('url(' + webUrl + '/page/hotel/info-top-bg.png)') : 'none'
}"
class="info-item-header"
@click="infoClick(item)"
>
<view class="name">
{{ item.title }}
</view>
<view class="intro">
{{ item.content }}
</view>
</view>
<view class="info-item-body">
<view v-if="item.type === 2" class="video">
<video
:src="item.video"
:poster="item.video + '?vframe/jpg/offset/1'"
controls
play-btn-position="center"
:show-fullscreen-btn="false"
class="video-item"
@play="infoItemViewClick(item)"
/>
</view>
<view v-else>
<img-box
:imgList="item.images"
:num="item.images.length"
:img-radius="10"
style="width: 100%;"
@click="infoItemViewClick(item)"
/>
</view>
</view>
<view class="info-item-bottom">
<view class="time">
{{ item.createTime ? item.createTime.replace(/-/g, '.').substr(0, 10) : '' }}
</view>
<view class="btns">
<view class="flex">
<image
:src="webUrl + '/20230803152147141807.png'"
class="icon"
/>
<text class="seeNum-txt">{{ item.pv }}</text>
</view>
<view class="flex">
<image
v-if="!item.hasZan"
:src="webUrl + '/page/hotel/zan-off-2.png'"
class="zan-off-icon"
@click="likeInfoItemHandle(item)"
/>
<image
v-else
:src="webUrl + '/page/hotel/zan-on-2.png'"
class="zan-on-icon"
@click="likeInfoItemHandle(item)"
/>
<text
v-show="item.zan > 0"
:class="item.zanVo ? 'on' : ''"
class="seeNum-txt"
>{{ item.zan }}</text>
</view>
</view>
</view>
</view>
</view>
</view>
<view v-else class="tab-no-data">
店主还没有上传内容哦~
</view>
</view>
<goo-skeleton v-else />
</view>
<view
v-if="activeIndex === 2"
class="pics-section"
>
<view
v-if="store.cultureImages.length > 0"
class="pics-list acea-row"
>
<view
v-for="(url, index) in store.cultureImages"
:key="index"
:style="{ width: picColumnWidth }"
class="pics-item"
@click="showPic(index)"
>
<image
:style="{
width: picColumnWidth,
height: picColumnWidth,
borderRadius: '20rpx'
}"
:src="url"
/>
</view>
</view>
<view v-else class="tab-no-data">
店主还没有上传内容哦~
</view>
</view>
<view
v-if="activeIndex === 3"
class="qualifications-section"
>
<view v-if="store.qualifications.length > 0">
<image
v-for="(item, index) in store.qualifications"
:key="index"
:src="item"
class="store-img"
mode="widthFix"
/>
</view>
<view v-else class="tab-no-data">
店主还没有上传内容哦~
</view>
</view>
</view>
</view>
</view>
</template>
<script>
import { getFarmerShop, setFarmerShopZan, getFarmerShopNews, setFarmerShopCollection } from '@/api/farmer'
import { postCountyFamousNewsZan, postCountyFamousNewsView } from '@/api/qianxian'
import imgBox from '@/components/imageTypeSet/imagebox.vue'
export default {
components: {
imgBox
},
data() {
return {
webUrl: this.$VUE_APP_RESOURCES_URL,
videoStyle: {
width: '750rpx',
height: '500rpx'
},
scrollData: {},
id: '',
store: {},
tabList: [
{ text: '商品', value: 0, isShow: true },
{ text: '资讯', value: 1, isShow: true },
{ text: '文化', value: 2, isShow: true },
{ text: '资质', value: 3, isShow: true }
],
activeIndex: 0,
isLoadInfoListSuccess: false,
picColumnWidth: '325rpx',
infoArray: []
}
},
onPageScroll(e) {
this.scrollData = e
},
onLoad(opts) {
this.id = opts.id
this.getFarmerData()
uni.getSystemInfo({
success: (e) => {
// 两列商品宽度
this.picColumnWidth = (e.screenWidth - uni.upx2px(60)) / 2 + 'px'
}
})
},
methods: {
goGoodsConByID(item) {
this.$yrouter.push({
path: '/pages/shop/GoodsCon/index',
query: {
id: item.productId
}
})
},
getNews() {
const params = {
page: 1,
limit: 9999,
farmerShopId: this.id
}
getFarmerShopNews(params).then(res => {
this.infoArray = res.data.records || []
this.isLoadInfoListSuccess = true
})
},
changeTab(index) {
this.activeIndex = index
if(index === 1) {
this.getNews()
} else {
this.isLoadInfoListSuccess = false
}
// this.loading = false
// // 刷新数据
// switch (this.activeIndex) {
// case 1:
// this.fetchInnInfoList()
// break
// case 0:
// this.fetchGoods()
// break
// default:
// break
// }
},
showLocation() {
if (!this.store.latitude || !this.store.longitude) {
this.$dialog.error('暂无位置信息')
} else {
uni.openLocation({
latitude: this.store.latitude,
longitude: this.store.longitude
})
}
},
likeInfoItemHandle(item) {
const params = {
countyFamousNewsId: item.id
}
postCountyFamousNewsZan(params).then(res => {
const {success} = res
if(success) {
this.getNews()
}
})
},
infoItemViewClick(item) {
const params = {
countyFamousNewsId: item.id
}
postCountyFamousNewsView(params).then(res => {
const {success} = res
if(success) {
this.getNews()
}
})
},
call() {
if (this.store.tel != undefined && this.store.tel.length > 0) {
uni.makePhoneCall({
// 手机号
phoneNumber: this.store.tel,
// 成功回调
success: (res) => {
console.log('调用成功!')
},
// 失败回调
fail: (res) => {
console.log('调用失败!')
}
})
} else {
this.$dialog.error('电话为空')
}
},
getFarmerData() {
getFarmerShop(this.id).then(res => {
this.store = res.data || {}
})
},
zanStore() {
const params = {
farmerShopId: this.id
}
setFarmerShopZan(params).then(res => {
const {success} = res
if(success) {
this.getFarmerData()
}
})
},
doneFavorite() {
const params = {
farmerShopId: this.id
}
setFarmerShopCollection(params).then(res => {
this.getFarmerData()
})
},
showPic (index) {
uni.previewImage({
current: index,
urls: this.store.cultureImages
})
},
}
}
</script>
<style lang="less" scoped>
.btn {
.img {
display: block;
width: 48rpx;
height: 48rpx;
}
}
.container{
background: #fff;
}
.store-img {
width: 686rpx;
margin-bottom: 20rpx;
vertical-align: middle;
}
.tab-no-data {
text-align: center;
font-size: 32rpx;
color: #999;
line-height: 200rpx;
}
.btLine {
width: 100%;
height: 6rpx;
margin-top: 18rpx;
background: #0DC5C5;
}
.pics-box {
margin: 36rpx;
background: #fff;
}
.pics-list {
position: relative;
padding: 20rpx 0;
flex-wrap: wrap;
}
.pics-list .pics-item {
margin-left: 20rpx;
margin-top: 10rpx;
}
.info-section,
.pics-section,
.qualifications-section {
position: relative;
background-color: #f6f6f6;
}
.info-section,
.qualifications-section {
padding: 32rpx;
}
.info-section {
word-break: break-all;
.info-item + .info-item {
margin: 32rpx 0 0 0;
}
.info-item {
border-radius: 10rpx;
background-color: #fff;
.info-item-header {
position: relative;
padding: 20rpx 20rpx 0 20rpx;
&.info-item-top {
padding-top: 60rpx;
background-size: 100% 80rpx;
background-repeat: no-repeat;
background-image: url();
}
.name {
font-size: 16px;
color: #333;
font-weight: bold;
}
.intro {
margin: 10rpx 0 0 0;
color: #666;
font-size: 14px;
}
}
.info-item-body {
padding: 0 20rpx 20rpx 20rpx;
.video {
padding: 20rpx 0 0 0;
.video-item {
width: 100%;
height: 400rpx;
border-radius: 10rpx;
}
}
}
.info-item-bottom {
display: flex;
align-items: center;
padding: 20rpx;
border-top: 1px solid #f1f1f1;
font-size: 16px;
.time {
flex: 1;
color: #333;
}
.btns {
display: flex;
color: #999;
.flex + .flex {
margin: 0 0 0 32rpx;
}
.flex {
align-items: center;
}
.icon {
width: 32rpx;
height: 32rpx;
margin: 0 8rpx 0 0;
}
.zan-off-icon {
width: 40rpx;
height: 40rpx;
}
.zan-on-icon {
width: 40rpx;
height: 40rpx;
}
.seeNum-txt {
&.on {
color: #D81E06;
}
}
.more-btn {
position: relative;
.more-wrapper {
position: absolute;
right: -20rpx;
top: -150rpx;
z-index: 5;
width: 200rpx;
border-radius: 12rpx;
background-color: #fff;
box-shadow: 0px 1px 6px rgba(0,0,0,0.16);
.more-item + .more-item {
border-top: 1px solid #d5d5d5;
}
.more-item {
display: flex;
align-items: center;
justify-content: center;
height: 70rpx;
font-size: 14px;
color: #333;
.more-icon {
width: 36rpx;
height: 36rpx;
margin: 0 10rpx 0 0;
}
}
}
}
}
}
}
}
.goods-section {
padding: 40rpx 32rpx 0;
background: #f0f0f0;
.item {
position: relative;
width: 332rpx;
height: 528rpx;
box-sizing: border-box;
margin-right: 22rpx;
margin-bottom: 24rpx;
border-radius: 20rpx;
background: #fff;
overflow: hidden;
display: flex;
flex-direction: column;
justify-content: space-between;
.name {
padding: 8rpx 6rpx;
font-size: 26rpx;
line-height: 36rpx;
color: #333333;
height: 70rpx;
}
.price-line {
padding: 0 10rpx;
font-size: 26rpx;
line-height: 38rpx;
color: #ED0F0F;
}
.sales-line {
position: absolute;
bottom: 0;
width: 100%;
padding: 8rpx 10rpx 18rpx;
border-top: 1rpx solid #EFEFEF;
font-size: 24rpx;
line-height: 34rpx;
color: #8D8D8D;
}
}
.item:nth-child(2n) {
margin-right: 0;
}
.cover {
width: 332rpx;
height: 332rpx;
background: rgba(255, 255, 255, 0.39);
border-radius: 8rpx;
vertical-align: middle;
}
.label {
width: 40rpx;
height: 24rpx;
margin-right: 4rpx;
vertical-align: middle;
}
.icon {
width: 24rpx;
height: 24rpx;
margin-right: 6rpx;
vertical-align: middle;
}
.btn {
display: flex;
justify-content: center;
.btn-cont {
padding: 13rpx 30rpx;
border: 1px solid #0DC5C5;
border-radius: 8rpx;
color: #0DC5C5;
.iconfont {
margin: 0 0 0 20rpx;
font-size: 24rpx;
}
}
}
}
.btLine {
width: 100%;
height: 6rpx;
margin-top: 18rpx;
background: #0DC5C5;
}
.tab-item-active {
color:#080F1A;
font-size:32rpx;
}
.tab-item-no-active {
color:#A6AAB3;
font-size:28rpx;
}
.top-tab {
padding-bottom: 30rpx;
padding-top: 30rpx;
background: #fff;
.tab {
width: 20%;
.tab-item {
height: 52rpx;
padding: 0 30rpx;
border-radius: 26rpx 0 0 26rpx;
font-size: 32rpx;
line-height: 52rpx;
font-weight: bold;
&.active {
color: #fff;
background: linear-gradient(90deg, #C52733 0%, rgba(211,92,101,0.91) 54%, rgba(255,255,255,0.62) 100%);
}
}
&.hide {
display: none;
}
}
}
.map-wrapper {
position: relative;
z-index: 2;
height: 200rpx;
margin: 20rpx 0 0 0;
overflow: hidden;
.map-cont {
position: absolute;
top: 0;
left: 0;
z-index: 2;
width: 100%;
opacity: 0.3;
transform: translateY(-50rpx);
}
.bg {
position: absolute;
top: 0;
left: 0;
right: 0;
z-index: 3;
height: 60rpx;
background: linear-gradient(180deg, #FFFFFF 0%, rgba(255,255,255,1) 0%, rgba(255,255,255,0) 100%);
}
.map-info {
position: relative;
z-index: 5;
display: flex;
align-items: center;
padding: 40rpx 10rpx 40rpx 32rpx;
color: #333;
font-size: 14px;
background: rgba(255,255,255,0.4);
.map-info-hd {
.icon {
display: block;
width: 34rpx;
height: 42rpx;
}
}
.map-info-bd {
flex: 1;
padding: 0 60rpx 0 20rpx;
line-height: 60rpx;
}
.map-info-ft {
.item + .item {
margin: 0 0 0 6rpx;
}
.item {
text-align: center;
font-weight: 500;
.img {
display: block;
width: 64rpx;
height: 64rpx;
margin: 0 0 16rpx 16rpx;
}
}
}
}
}
.inn-signature-wrap {
position: relative;
z-index: 5;
padding: 0 32rpx;
margin: 20rpx 0 0 0;
.triangle {
position: absolute;
top: -20rpx;
left: 72rpx;
width: 0;
height: 0;
border-left: 20rpx solid transparent;
border-right: 20rpx solid transparent;
border-bottom: 24rpx solid rgba(243,243,246,0.39);
}
.inn-signature {
position: relative;
padding: 20rpx;
border-radius: 20rpx;
font-size: 24rpx;
color: #080F1A;
background: #fff;
box-shadow: 0px 0px 7px 1px rgba(0, 0, 0, 0.08);
}
.share-btn {
position: absolute;
right: 0;
bottom: -68rpx;
.img {
width: 212rpx;
height: 60rpx;
}
}
}
.zan-favorite-wrap {
font-size: 32rpx;
color: #333;
.on {
color: #FF564A;
}
}
.zan-box {
.img {
width: 36rpx;
height: 36rpx;
margin: 0 8rpx 0 0;
vertical-align: middle;
}
}
.favorite-box {
.icon {
width: 36rpx;
height: 36rpx;
margin: 0 8rpx 0 30rpx;
}
}
.cover-box {
position: relative;
.full-img {
width: 750rpx;
}
.video {
display: block;
width: 100%;
height: 100%;
}
}
.innInfoWrap {
position: relative;
margin-top: -76rpx;
padding: 20rpx 36rpx;
line-height: 1;
z-index: 1;
.bg-color {
position: absolute;
top: -5px;
left: 0;
right: 0;
z-index: 1;
height: 50%;
background: linear-gradient(-180deg, rgba(0,0,0,0) 0%, rgba(0,0,0,1) 100%);
.box-mask {
position: absolute;
bottom: 0;
right: 0;
left: 166rpx;
box-sizing: border-box;
padding: 0 32rpx 12rpx 0;
.city-section {
width: 256rpx;
margin-left: 20rpx;
}
}
}
.body-cont {
position: relative;
z-index: 2;
}
}
.inn-logo {
flex-shrink: 0;
width: 56*2rpx;
height: 56*2rpx;
margin-right: 18rpx;
}
.inn-logo image {
width: 100%;
height: 100%;
border-radius: 50%;
}
.inn-name {
width: 280rpx;
font-size: 32rpx;
color: #FFFFFF;
line-height: 48rpx;
}
.inn-owner {
font-size: 24rpx;
color: #8B8F99;
}
</style>
+3 -1
View File
@@ -247,7 +247,7 @@
</view>
<view class="info-item-bottom">
<view class="time">
{{ item.createdTime ? item.createdTime.replace(/-/g, '.').substr(0, 10) : '' }}
{{ item.publishTime ? item.publishTime.replace(/-/g, '.').substr(0, 10) : '' }}
</view>
<view class="btns">
<view class="flex">
@@ -1022,6 +1022,7 @@ export default {
font-size: 26rpx;
line-height: 36rpx;
color: #333333;
height: 70rpx;
}
.price-line {
@@ -1326,6 +1327,7 @@ export default {
padding: 32rpx;
}
.info-section {
word-break: break-all;
.info-item + .info-item {
margin: 32rpx 0 0 0;
}
+3 -1
View File
@@ -26,9 +26,11 @@
<textarea
v-model="form.intro"
placeholder="请输入简介..."
maxlength="140"
placeholder-style="font-size:24rpx;color:#C2C5CC;"
class="content-style inp"
/>
<text class="v12-font-24 v12-dark1-text">{{ form.intro.length }} / 140</text>
</view>
<view class="form-item" v-if="showUpload">
<view v-if="uploadType === 'image'" class="title">图片(最多9张):</view>
@@ -152,7 +154,7 @@
postHotelNewsAdd({
hotelId: this.hotelId,
name: this.form.title,
intro: this.form.intro,
intro: this.form.intro.substring(0, 140),
content: this.form.content.replace(/\<img src/g, '<img style="display: block;max-width: 100%;" src'),
type: this.uploadType === 'image' ? 'NT_TEXT' : 'NT_VIDEO',
resources: this.uploadType === 'image' ? this.uploadedFilesArray : [],
+4 -2
View File
@@ -140,7 +140,7 @@
<view class="cell acea-row row-between row-middle">
<view class="cell-title">
店铺图片
<text class="txt">最多上传9张图片</text>
<!-- <text class="txt">最多上传9张图片</text> -->
</view>
</view>
</view>
@@ -149,7 +149,8 @@
<tm-upload
:dataList="picsArray"
:upload_img_wh="(750 - 3 * 16 - 2 * 32) / 3.0"
:upload_max="10"
:upload_max="99999"
:upload_count="9999"
:url="uploadUrl"
:header="uploadHeader"
@change="uploadedImgChange"
@@ -170,6 +171,7 @@
:dataList="qualificationsArray"
:upload_img_wh="(750 - 3 * 16 - 2 * 32) / 3.0"
:upload_max="10"
:url="uploadUrl"
:header="uploadHeader"
@change="uploadedImg2Change"
+6 -3
View File
@@ -93,23 +93,26 @@ export default {
}
const res = await takePrize(item.id)
if (res.success) {
console.log('success', '+++++++++++++');
this.$set(this.list[index], 'status', 2)
uni.showModal({
title: '领取成功',
content: '您的奖品已经领取成功,请前往我的订单查看奖品发货信息',
content: '您的奖品已经领取成功,请前往积分记录查看奖品发货信息',
cancelText: '下次再说',
confirmText: '查看订单',
confirmText: '积分记录',
success: function (res) {
if (res.confirm) {
console.log('用户点击确定')
uni.navigateTo({
url: '/pages/order/MyOrder/index'
url: '/pages/user/Points/index'
})
} else if (res.cancel) {
console.log('用户点击取消')
}
}
})
} else {
console.log('err', '==========');
}
}
}
+7 -1
View File
@@ -117,7 +117,8 @@ export default {
},
imgData: [],
shareAppImg: '',
pageKeyId: Object.freeze('lotteryIndex')
pageKeyId: Object.freeze('lotteryIndex'),
isDisabled: false
}
},
computed: {
@@ -155,6 +156,8 @@ export default {
},
onSubmit() {
if(this.isDisabled) return
this.isDisabled = true
getPrize().then(res => {
if (res.success) {
this.diceResult = res.data
@@ -163,12 +166,15 @@ export default {
setTimeout(() => {
this.showDialog = true
this.showResult = true
this.isDisabled = false
this.diceImg = this.lotteryDetail.diceConfig['diceValues'][diceIndex]['diceValueImage']
setTimeout(() => {
this.init()
}, 1000)
}, 2800)
}
}).catch(() => {
this.isDisabled = false
})
},
+17 -9
View File
@@ -2,24 +2,25 @@
<view class="common-roomLogs">
<view v-if="list.length > 0">
<view
v-for="(item,index) in list"
:key="index"
class="item"
>
<u-swipe-action
class="v12-mt-3"
>
<u-swipe-action-item
:options="options1"
:key="index"
v-for="(item) in list"
:key="item.room_id"
:name="item.room_id"
style="margin-bottom: 10px;"
@click.stop="removeRoom(item.room_id)"
>
<view>
<view class="v12-justify-between v12-pa-3 flex" @click="goRoom(item)">
<view class="item v12-pa-3">
<view class="v12-justify-between flex" @click="goRoom(item)">
<image class="cover flex-0" :src="item.store_avatar || webUrl+'/20221114155314348118.png'"/>
<view>
<view class="flex jc-between ai-center">
<view class="name">{{ item.store_name }}</view>
<view class="name more-t">{{ item.store_name }}</view>
<view class="datetime" v-if="item.last_msg">{{ item.last_msg.created_at }}</view>
</view>
<view class="sub one-t" v-if="item.last_msg">{{ item.last_msg.msg_type===0?item.last_msg.msg_content:'[图片]' }}</view>
@@ -96,7 +97,11 @@ export default {
}
}
</script>
<style>
.common-roomLogs .u-swipe-action-item{
margin-bottom: 20rpx;
}
</style>
<style scoped lang="less">
.common-roomLogs {
min-height: 100vh;
@@ -105,7 +110,6 @@ export default {
background: #F5F5F5;
.item {
margin-bottom: 24rpx;
border-radius: 8rpx;
background: #FFFFFF;
@@ -127,6 +131,10 @@ export default {
font-size: 22rpx;
line-height: 32rpx;
color: #999999;
width: 240rpx;
flex-wrap: nowrap;
white-space: nowrap;
text-align: center;
}
.sub {
+6 -1
View File
@@ -289,7 +289,12 @@
</u-form-item>
<u-form-item prop="pics">
<u-cell title="店铺图片" :border="false">
<template #label>
<template #icon>
<text class="required">*</text>
</template>
</u-cell>
<u-cell :border="false">
<template #title>
<u-upload
:fileList="pics"
:maxCount="9"
@@ -27,6 +27,7 @@
</swiper> -->
<carousel
:img-list="templateProperties.page2Links"
:bg-img="templateProperties.page2BackgroundImage"
url-key="image"
@selected="itemClick"
/>
@@ -9,13 +9,13 @@
<view class="page-wrap">
<view class="page-2">
<image
:src="templateProperties.page3BackgroundImage"
:src="templateProperties.page2BackgroundImage"
class="section-bg"
mode="widthFix"
/>
<view class="list flex flex-wrap">
<image
v-for="(item, index) in templateProperties.page3Links"
v-for="(item, index) in templateProperties.page2Links"
:key="index"
:src="item.image"
class="item"
@@ -5,9 +5,13 @@
next-margin="145rpx"
circular
autoplay
:interval="3000"
indicatorDots="true"
indicator-color="#ddd"
indicator-active-color="#777"
:style="{
'background-image': `url(${bgImg})`
}"
@change="swiperChange"
>
<swiper-item
@@ -41,6 +45,10 @@
return ''
}
},
bgImg: {
type: String,
default: ''
}
},
data() {
return {
@@ -63,6 +71,7 @@
.image-container {
width: 750rpx;
height: 840rpx;
background-size: 750rpx 840rpx;
}
.item-img {
@@ -13,6 +13,7 @@ export const themeTemplateMixins = {
},
methods: {
itemClick(item) {
if (!item.available) return
this.$emit('view', item)
}
}
+133
View File
@@ -0,0 +1,133 @@
<template>
<view class="expo-details">
<view v-if="details.video" class="video-wrap">
<video id="ky-video" class="ky-video" @error="videoErrorCallback" :show-fullscreen-btn="false" :src="details.video" autoplay controls></video>
</view>
<view class="expo-item">
{{ details.name }}
</view>
<view class="details-wrap v12-mt-3">
<view class="v12-font-bold v12-dark-text v12-font-32 v12-mb-3">展会信息</view>
<view class="v12-justify-start v12-mb-3">
<view class="tag v12-success v12-white-text v12-font-20 v12-px-2 v12-py-1 v12-radius-50 v12-nowrap">主题</view>
<view class="v12-font-bold v12-font-28 v12-dark-text v12-ml-3">{{ details.theme }}</view>
</view>
<view class="v12-justify-start v12-mb-3">
<view class="tag v12-success v12-white-text v12-font-20 v12-px-2 v12-py-1 v12-radius-50 v12-nowrap">内容</view>
<rich-text class="v12-ml-3" :nodes="details.content"></rich-text>
</view>
<view class="v12-justify-start v12-mb-3">
<view class="tag v12-success v12-white-text v12-font-20 v12-px-2 v12-py-1 v12-radius-50 v12-nowrap">时间</view>
<view class="v12-font-bold v12-font-28 v12-dark-text v12-ml-3">{{ details.startTime }}--{{ details.endTime }}</view>
</view>
<view class="v12-justify-start v12-mb-3">
<view class="tag v12-success v12-white-text v12-font-20 v12-px-2 v12-py-1 v12-radius-50 v12-nowrap">地点</view>
<view class="v12-font-bold v12-font-28 v12-dark-text v12-ml-3">{{ details.address }}</view>
</view>
</view>
<view class="more-wrap v12-mt-3">
<view class="v12-justify-between v12-align-center v12-mb-3">
<view class="v12-font-bold v12-font-32">其他展会</view>
<view class="v12-font-24 v12-align-center" @click="toMore">
<text>查看更多</text>
<image :src="webUrl + '/orderIcon/arrow.png'" class="arrow-icon v12-ml-1"></image>
</view>
</view>
<view
v-for="(item, index) in moreList"
:key="index"
@click="getOther(item.id)"
class="v12-white v12-dark-text v12-font-bold v12-font-26 v12-mb-3 v12-radius-50 v12-pa-2">
{{ item.name }}
</view>
</view>
</view>
</template>
<script>
import {getWellnessExhibitionDetails, getWellnessExhibition} from '@/api/health'
export default{
data() {
return {
webUrl: this.$VUE_APP_RESOURCES_URL,
keyword: '',
moreList: [],
id: '',
details: {}
}
},
onLoad(opts) {
this.id = opts.id || ''
this.getDetails(this.id)
},
methods: {
videoErrorCallback(error) {
console.log(error);
},
getDetails(id) {
getWellnessExhibitionDetails(id).then(({data}) => {
this.details = data
this.getOthers()
})
},
getOther(id) {
this.id = id
this.getDetails(this.id)
},
getOthers() {
let params = {
page: 1,
limit: 5
}
getWellnessExhibition(params).then(({data}) => {
this.moreList = data.records.filter(e => e.id != this.id)
})
},
toMore() {
uni.navigateTo({
url: '/pkg_product/views/expoList'
})
},
}
}
</script>
<style lang="less" scoped>
.arrow-icon{
width: 25rpx;
height: 25rpx;
}
.ky-video{
height: 400rpx;
width: 100%;
border-radius: 20rpx;
}
.tag{
height: 30rpx;
}
.expo-details{
min-height: 100vh;
box-sizing: border-box;
background: #fff;
padding: 20rpx;
}
.expo-item{
padding: 20rpx;
border-radius: 20rpx;
background-color: #f2f2f2;
font-size: 26rpx;
font-weight: bold;
}
.video-wrap + .expo-item {
padding: 30rpx 20rpx 20rpx;
margin-top: -20rpx;
border-radius: 0 0 20rpx 20rpx;
}
.details-wrap, .more-wrap{
width: 100%;
background: #f2f2f2;
border-radius: 20rpx;
padding: 20rpx;
box-sizing: border-box;
}
</style>
+79
View File
@@ -0,0 +1,79 @@
<template>
<view class="expo-list">
<view class="search-wrap">
<u-search
placeholder="请输入关键字查询"
v-model="keyword"
shape="round"
:showAction="false"
@search="getList"
></u-search>
</view>
<view class="expo-wrap">
<view
class="expo-item"
v-for="(item, index) in expoList"
:key="index"
@click="toDetails(item)"
>
{{ item.name }}
</view>
<view v-if="expoList.length === 0">
<view class="no-data-wrap v12-justify-center v12-mt-6">
<image
:src="webUrl + '/orderIcon/微信图片_20241009220552.png'"
mode="widthFix"
></image>
</view>
</view>
</view>
</view>
</template>
<script>
import { getWellnessExhibition } from '@/api/health'
export default{
data() {
return {
webUrl: this.$VUE_APP_RESOURCES_URL,
keyword: '',
expoList: []
}
},
onLoad() {
this.getList()
},
methods: {
toDetails(item) {
uni.navigateTo({
url: '/pkg_product/views/expoDetails?id='+item.id
})
},
getList() {
const params = {
page: 1,
limit: 9999,
keyword: this.keyword
}
getWellnessExhibition(params).then((res) => {
console.log(res);
this.expoList = res.data.records
})
}
}
}
</script>
<style lang="less" scoped>
.expo-list{
height: 100vh;
background: #fff;
padding: 20rpx;
}
.expo-item{
background-color: #f2f2f2;
border-radius: 100rpx;
font-size: 26rpx;
font-weight: bold;
margin-top: 20rpx;
padding: 20rpx;
}
</style>
+5 -1
View File
@@ -356,7 +356,7 @@ export default {
position: absolute;
left: 30rpx;
right: 30rpx;
bottom: -50rpx;
top: 260rpx;
display: flex;
padding: 30rpx 20rpx 30rpx 30rpx;
border-radius: 20rpx;
@@ -400,6 +400,10 @@ export default {
}
}
}
.list-wrap {
position: relative;
z-index: 2;
}
.list-cont {
position: relative;
z-index: 2;
+165 -31
View File
@@ -93,9 +93,9 @@
<view class="inn-signature">
{{ inn.shopIntro }}
</view>
<!-- <view class="share-btn" @click="changeShare">
<view class="share-btn" @click="changeShare">
<image :src="webUrl + '/page/hotel/share.png'" class="img" />
</view> -->
</view>
</view>
<view v-if="inn.name" class="map-wrapper">
<view class="map-cont">
@@ -146,7 +146,7 @@
v-if="activeIndex === 3"
class="info-section"
>
<view v-if="isLoadInfoListSuccess">
<view>
<view v-if="infoArray.length > 0" class="info-wrapper">
<view class="info-list">
<view
@@ -213,14 +213,11 @@
</view>
</view>
</view>
<image
:src="webUrl + '/home/no-data-bg.png'"
mode="widthFix"
class="no-data-loadend"
/>
</view>
<view class="v12-px-6" >
<u-divider :text="infoArray.length === 0 ? ' · 暂无资讯 · ' : ' · 已经到底啦 · '" textPosition="center"></u-divider>
</view>
</view>
<goo-skeleton v-else />
</view>
<!-- 文化 -->
<view
@@ -293,11 +290,14 @@
</view>
</view>
</view>
<image
<!-- <image
:src="webUrl + '/home/no-data-bg.png'"
mode="widthFix"
class="no-data-loadend"
/>
/> -->
<view class="v12-px-6" >
<u-divider :text="guideList.length === 0 ? ' · 暂无攻略 · ' : ' · 已经到底啦 · '" textPosition="center"></u-divider>
</view>
</view>
<goo-skeleton v-else />
</view>
@@ -505,8 +505,8 @@
v-if="activeIndex === 4"
class="good-list-wrap"
>
<view class="good-list-cont">
<view class="list-cont-left">
<view class="good-list-cont v12-d-grid-columns-2">
<!-- <view class="list-cont-left">
<view
v-for="(item, index) in leftList"
:key="index"
@@ -537,6 +537,47 @@
@view="goGoodsCon(item)"
/>
</view>
</view> -->
<view v-for="(item, index) in leftList" :key="index">
<view v-if="item.productId" class="v12-white v12-radius-20 f-wrap" @click="goGoodsCon(item)" style="height: 516rpx">
<view class="">
<view class="focus-img">
<image :src="item.productImage" class="img"></image>
</view>
<view class="more-t v12-pt-2 v12-font-bold v12-font-28 v12-px-2">{{ item.productName }}</view>
</view>
<view class="v12-justify-between v12-algin-center v12-pa-2">
<view>
<text class="v12-font-bold v12-primary-text v12-font-24"></text>
<text class="v12-font-bold v12-primary-text v12-font-28">{{ item.productPrice }}</text>
</view>
<view class="btn">
<image
:src="webUrl + '/home/icon-cart.png'"
class="img"
/>
</view>
</view>
</view>
<view v-if="!item.productId" class="v12-white v12-radius-20 f-wrap" @click="goStore(item)" style="height: 516rpx">
<view>
<view class="focus-img">
<image :src="item.cover" v-if="item.coverType === 1" class="img" mode="heightFix|widthFix"></image>
<video :src="item.cover" v-else :show-center-play-btn="false" :show-fullscreen-btn="false" :show-play-btn="false" class="img" :poster="item.cover + '?vframe/jpg/offset/1'"></video>
<image
:src="webUrl + '/orderIcon/farmer.png'"
class="mark-img"
/>
</view>
<view class="more-t v12-pt-2 v12-font-bold v12-font-28 v12-px-2" >{{ item.content }}</view>
</view>
<view class="v12-justify-start v12-align-center v12-pt-2 v12-px-2 v12-pb-2">
<view class="btn v12-mr-2">
<image :src="item.logo" class="img v12-radius-100"></image>
</view>
<view class="one-t v12-font-24 v12-dark1-text" style="width:230rpx">{{ item.name }}/{{ inn.name }}</view>
</view>
</view>
</view>
</view>
<image
@@ -559,7 +600,7 @@
<image
:src="webUrl + '/20230608112219219303.png'"
class="icon"
@click="changeShare"
@click="closeShare"
/>
<image :src="posterUrl" class="main-img" />
</view>
@@ -587,7 +628,8 @@ import {
getCountyFamousIndustryProject,
getCountyFamousInvestmentFile,
getCountyFamousVillage,
getCountyFamousCityDetail
getCountyFamousCityDetail,
getShareImg
} from '@/api/qianxian'
import {formatDateTime} from '@/utils'
import {getUrlParam} from '@/utils/common.js'
@@ -677,6 +719,7 @@ export default {
if (options.scene || obj.query.scene) {
const query = options ? decodeURIComponent(options.scene) : decodeURIComponent(obj.query.scene)
this.partnerId = getUrlParam(query, 'partnerId') || null
this.id = getUrlParam(query, 'id') || null
}
}
if (this.partnerId) cookie.set('spread', this.partnerId)
@@ -687,6 +730,19 @@ export default {
this.getListReq()
},
methods: {
goStore(item) {
if (item.farmerShopId === 0) {
uni.showToast({
title: '店铺装修中~',
icon: 'none',
duration: 3000
})
return
}
uni.navigateTo({
url: '/pagesInn/inn/farmerStore?id=' + item.farmerShopId + '&isQianxian=1&from=famous&isFarmer=1'
})
},
load() {
this.$refs.uReadMore.init();
},
@@ -744,20 +800,52 @@ export default {
const { success, data } = res
if (success) {
const products = data.products || []
products.map((item, index) => {
item.isOldBrand = 0
item.isLandmarkGoods = 0
item.isCountyFamous = 1
if (index % 2 === 0) {
this.leftList.push(item)
}
if (index % 2 === 1) {
this.rightList.push(item)
}
})
const farmerShops = data.farmerShops || []
this.leftList = this.sortProductsAndStores(products, farmerShops)
console.log(this.leftList);
// products.map((item, index) => {
// item.isOldBrand = 0
// item.isLandmarkGoods = 0
// if (index % 2 === 0) {
// this.leftList.push(item)
// }
// if (index % 2 === 1) {
// item.isFarmer = 1
// this.rightList.push(item)
// }
// })
}
})
},
shuffleArray(array) {
for (let i = array.length - 1; i > 0; i--) {
let j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
return array;
},
sortProductsAndStores(products, stores) {
const randomMap = []
const randomProduct = products.length === 0 ? null : products.length === 1 ? products[0] : products[Math.floor(Math.random() * products.length)]
const store = stores.length === 0 ? null : stores.length === 1 ? stores[0] : stores[Math.floor(Math.random() * stores.length)]
if(!randomProduct && !store) {
return []
}
if(!randomProduct && store) {
return stores
}
if(randomProduct && !store) {
return products
}
if(randomProduct && store) {
randomMap[0] = randomProduct
randomMap[1] = store
const _p = products.filter(e => e !== randomProduct)
const _s = stores.filter(e => e !== store)
const concatArr = this.shuffleArray(_p.concat(_s))
return randomMap.concat(concatArr);
}
},
getShopDetailReq() {
const _this = this
getCountyFamousCityShop({ countyFamousDataId: _this.id }).then((res) => {
@@ -867,7 +955,16 @@ export default {
}
},
changeShare() {
this.showShare = !this.showShare
uni.showLoading()
getShareImg(this.id).then(res => {
this.posterUrl = res.data
this.showShare = !this.showShare
}).finally(() => {
uni.hideLoading()
})
},
closeShare() {
this.showShare = false
},
saveImg() {
const _this = this
@@ -1056,7 +1153,36 @@ export default {
</script>
<style scoped lang="less">
@import "@/assets/css/store.less";
.focus-img {
position: relative;
.img {
position: relative;
z-index: 2;
display: block;
width: 100%;
height: 345rpx;
border-radius: 20rpx;
}
.mark-img {
position: absolute;
top: 0;
right: 0;
z-index: 3;
display: block;
width: 125rpx;
height: 83rpx;
border-radius: 0 25rpx 0 0;
}
}
.btn {
.img {
display: block;
width: 48rpx;
height: 48rpx;
}
}
.my-inn-page {
padding: 0 0 100rpx 0;
background-color: #f6f6f6;
@@ -1469,7 +1595,7 @@ export default {
.img {
display: block;
width: 320rpx;
height: 360rpx;
height: 320rpx;
border-radius: 16rpx;
}
&:nth-child(2n) {
@@ -1756,9 +1882,11 @@ export default {
.good-list-cont {
position: relative;
z-index: 2;
display: flex;
justify-content: space-between;
padding: 0 21rpx 21rpx 21rpx;
// display: flex;
// justify-content: space-between;
padding: 20rpx;
grid-column-gap: 10px;
grid-row-gap: 10px;
.list-cont-left,
.list-cont-right {
width: calc(50% - 11rpx);
@@ -1777,4 +1905,10 @@ export default {
.no-data-loadend {
width: 100%;
}
.f-wrap{
box-sizing: border-box;
display: flex;
flex-direction: column;
justify-content: space-between;
}
</style>
+264
View File
@@ -0,0 +1,264 @@
<template>
<view class="food-wrap">
<view class="v12-mb-3 banner-wrap">
<image :src="details.topImage" mode="widthFix" class="banner-wrap"></image>
</view>
<view class="inro-wrap v12-mb-3 ">
<view class="v12-font-36 v12-font-bold v12-dark-text v12-mb-3">{{ details.name }}</view>
<view class="v12-font-28 v12-secondary-dark-text">{{ details.content }}</view>
</view>
<view class="details-list-wrap v12-mb-3 ">
<view class="v12-justify-start v12-align-center">
<view class="primary-title">
<text class="v12-font-34 v12-font-bold">食材清单</text>
<view class="slider"></view>
</view>
<view class="v12-dark1-text v12-font-24 v12-ml-3">/ 新鲜的食材决定药膳的作用</view>
</view>
<view class="v12-white v12-radius-20 v12-pa-2 v12-mt-3">
<view class="v12-font-28 v12-font-bold v12-mb-2">主料</view>
<view class="v12-d-grid-columns-2 primary-item-wrap">
<view v-for="(item, index) in details.mainRecipe" :key="index" class="primary-item v12-justify-start v12-align-center">
<view class="primary-img">
<image :src="item.recipeImage" class="primary-img" mode="widthFix"></image>
</view>
<view class="v12-ml-2">
<view class="v12-font-26 one-t v12-mb-1" style="width: 160rpx">{{ item.recipeText }}</view>
<view class="v12-font-20 v12-primary v12-white-text v12-nowrap v12-px-2 v12-py-1 v12-radius-60" @click="toGoods(item.productId)">+购入同款食材</view>
</view>
</view>
</view>
</view>
<view class="v12-white v12-radius-20 v12-pa-2 v12-mt-3">
<view class="v12-font-28 v12-font-bold v12-mb-2">辅料</view>
<view class="v12-d-grid-columns-2 primary-item-wrap">
<view v-for="(item, index) in details.minorRecipe" :key="index" class="primary-item v12-justify-start v12-align-center">
<view class="v12-ml-2">
<view class="v12-font-26 one-t v12-mb-1" style="width: 280rpx">
<text class="v12-mr-2 v12-font-bold">·</text>
<text>{{ item.recipeText }}</text>
</view>
</view>
</view>
</view>
</view>
</view>
<view class="step-wrap v12-mb-3">
<view class="step-title">
<view>烹饪步骤</view>
<view class="slider"></view>
</view>
<view class="steps v12-mt-3">
<view v-for="(item, index) in details.cookingSteps" :key="index" class="step-item">
<image :src="item.stepImage" mode="widthFix" ></image>
<!-- <view class="v12-ml-2 step-info">
<view class="v12-font-26 v12-font-bold v12-mb-3">{{ item.text }}</view>
<view class="v12-font-26 v12-secondary-dark-text">{{ item.text }}</view>
</view> -->
</view>
</view>
</view>
<view class="tips-wrap">
<view class="step-title">
<view>养生小tips</view>
<view class="slider"></view>
</view>
<view class="card-wrap v12-mt-3">
<!-- <view class="card-item">
<view class="card-tag v12-primary v12-white-text">选材</view>
<view class="v12-mt-6">
<view v-for="(item, index) in tips" :key="index">
<text class="v12-primary-text v12-font-bold v12-mr-2">·</text>
<text class="v12-font-26 v12-dark-text">{{ item }}</text>
</view>
</view>
</view>
<view class="card-item v12-mt-3">
<view class="card-tag v12-primary v12-white-text">注意事项</view>
<view class="v12-mt-6">
<view v-for="(item, index) in tips" :key="index">
<text class="v12-primary-text v12-font-bold v12-mr-2">·</text>
<text class="v12-font-26 v12-dark-text">{{ item }}</text>
</view>
</view>
</view> -->
<rich-text class="card-item" :nodes="details.remark">
</rich-text>
</view>
</view>
</view>
</template>
<script>
import { getFoodDetails } from '@/api/health'
export default{
data() {
return {
tips: [
'鹌鹑:选择新鲜的鹌鹑,处理清洗干净,去除内脏和杂质',
'淮山:选择肉质雪白、无黑点的淮山,去皮切块后应立即',
'放入清水中浸泡,以防氧化变黑',
'党参:选择色泽黄亮、无霉变、无虫蛀的党参'
],
primaryList: [
{img: '', label: '鹌鹑(2只)'},
{img: '', label: '鹌鹑(2只)'},
{img: '', label: '鹌鹑(2只)'},
{img: '', label: '鹌鹑(2只)'},
],
secondaryList: [
'枸杞(适量)',
'姜片(适量)',
'盐(适量)',
'清水(适量)'
],
steps: [],
id: '',
topImage: '',
details: {}
}
},
onLoad(opts) {
this.id = opts.id
this.getDetails(opts.id)
},
methods: {
getDetails(id) {
getFoodDetails(id).then(res => {
this.details = res.data
})
},
toGoods(id) {
this.$yrouter.push({
path: '/pages/shop/GoodsCon/index',
query: {
id: id
}
})
},
}
}
</script>
<style lang="less" scoped>
.card-tag{
width: fit-content;
padding: 10rpx 20rpx;
border-radius: 10rpx 0 20rpx 20rpx;
position: absolute;
top: -12rpx;
}
.card-tag::after{
content: '';
width: 0;
height: 0;
border-left: 8rpx solid transparent !important;
border-right: 8rpx solid transparent !important;
right: -4px;
border-top: 8rpx solid #7D393F !important;
border-bottom: 8rpx solid transparent!important;
position: absolute;
top: 4rpx;
rotate: 45deg;
}
.card-item{
position: relative;
background: #fff;
border-radius: 20rpx;
padding: 20rpx;
box-sizing: border-box;
}
.tips-wrap{
background: #F2F2F2;
border-radius: 40rpx;
padding: 20rpx;
box-sizing: border-box;
}
.steps{
display: flex;
overflow: auto;
}
.step-info{
height: inherit;
padding: 20rpx;
box-sizing: border-box;
}
.step-item{
min-width: 482rpx;
width: 482rpx;
height: 232rpx;
background: #fff;
border-radius: 20rpx;
display: flex;
align-items: center;
justify-content: flex-start;
margin-right: 24rpx;
// padding: 20rpx;
overflow: hidden;
}
.step-item:last-child{
margin-right: 0 !important;
}
.step-img{
width: 200rpx;
height: 200rpx;
background: rgba(195,126,126,0.39);
border-radius: 20rpx;
}
.step-title{
position: relative;
width: fit-content;
font-weight: bold;
font-size: 34rpx;
}
.step-wrap{
background: #F2F2F2;
border-radius: 40rpx;
padding: 20rpx 20rpx 30rpx 20rpx;
box-sizing: border-box;
}
.primary-item-wrap{
grid-column-gap: 20rpx;
grid-row-gap: 20rpx;
}
.primary-img{
width: 124rpx;
height: 90rpx;
background: rgba(63,20,20,0.39);
border-radius: 20rpx;
overflow: hidden;
}
.primary-item{
}
.slider{
position: absolute;
width: 100%;
height: 6rpx;
background: linear-gradient(to right, #C52733 0%, rgba(255,255,255,0) 100%);
border-radius: 6rpx;
bottom: 5rpx;
}
.primary-title{
position: relative;
width: fit-content;
}
.food-wrap{
min-height: 100vh;
background: #fff;
padding: 20rpx 20rpx 60rpx 20rpx;
}
.banner-wrap{
width: 100%;
height: 360rpx;
background: rgba(180,18,18,0.39);
border-radius: 40rpx;
box-sizing: border-box;
overflow: hidden;
}
.details-list-wrap{
background: #F2F2F2;
border-radius: 40rpx;
padding: 20rpx 20rpx 30rpx 20rpx;
}
</style>
+480
View File
@@ -0,0 +1,480 @@
<template>
<view class="health-foods">
<view class="v12-justify-center">
<view
class="ys-btn v12-mr-3 v12-radius-100 v12-dark-text v12-px-5 v12-py-2 v12-font-28 v12-font-bold"
@click="toSanctun"
>养生圣地</view
>
<view
class="v12-radius-100 v12-primary v12-white-text v12-px-5 v12-py-2 v12-font-28 v12-font-bold"
>养生食材</view
>
</view>
<view class="v12-justify-between v12-align-center v12-mt-5">
<image
:src="webUrl + '/orderIcon/map_dark.png'"
mode="widthFix"
class="icon"
/>
<view class="v12-font-28 v12-secondary-dark-text v12-mr-3">{{
cityName
}}</view>
<view class="flex-1">
<u-search
:showAction="false"
v-model="keyword"
bgColor="#fff"
searchIconColor="#C52733"
@search="search"
></u-search>
</view>
</view>
<swiper
class="v12-justify-between swap"
autoplay
circular
next-margin="300rpx"
duration="10000"
interval="3000"
>
<swiper-item v-for="(item, index) in foodItems" :key="index">
<view class="trave-item-wrap v12-mr-3 v12-mt-3">
<view class="trave-item">
<image
class="trave-item"
mode="widthFix"
:src="item.indexImage"
></image>
</view>
<view class="trave-btn" @click="toDetails(item.id)">
<text class="one-t">{{ item.name }}</text>
<image
:src="webUrl + '/orderIcon/arrow.png'"
class="arrow-icon v12-ml-1"
></image>
</view>
</view>
</swiper-item>
</swiper>
<view class="v12-justify-center v12-mt-3" v-if="rank.length > 0">
<image
class="title-img"
:src="webUrl + '/orderIcon/rankTitle.png'"
mode="widthFix"
></image>
</view>
<view class="rank-wrap v12-mt-3" v-if="rank.length > 0">
<view class="rank-items-wrap">
<view class="bee-rank" v-for="(k, i) in rank" :key="i">
<image :src="k.backgroundImage" class="bg-img" style="width:inherit" mode="scaleToFill"></image>
<view class="rank-title-wrap">
<view class="rank-title v12-font-34 v12-font-bold one-t">{{
k.title
}}</view>
</view>
<view class="v12-px-2">
<view
class="rank-item v12-justify-start v12-align-center v12-mb-4"
v-for="(item, index) in k.products"
@click="toGoods(item.productId)"
:key="index"
>
<view
class="rank-tag"
:class="{ 'rank2-bg': index === 1, 'rank3-bg': index === 2 }"
>
<view
class="rank-2"
:class="{
'rank2-2-bg': index === 1,
'rank3-3-bg': index === 2,
}"
>
<view
class="rank-3"
:class="{
'rank2-4-bg': index === 1,
'rank3-4-bg': index === 2,
}"
>
<view
class="rank-4"
:class="{
'rank2-bg': index === 1,
'rank3-bg': index === 2,
}"
>{{ index + 1 }}</view
>
</view>
</view>
</view>
<view class="rank-img">
<image
:src="item.productImage"
mode="widthFix"
class="rank-img"
></image>
</view>
<view class="v12-ml-2">
<view
class="v12-font-bold v12-font-24 one-t"
style="width: 160rpx"
>{{ item.productTitle }}</view
>
<view class="v12-font-22 more-t">{{ item.productDesc }}</view>
</view>
</view>
</view>
</view>
</view>
</view>
<view class="recommend-wrap v12-mt-3">
<view class="recommend-title v12-px-3">
<text class="v12-dark-text v12-font-36 v12-font-bold">食补推荐</text>
<view class="slider"></view>
</view>
<view class="recommends v12-mt-3">
<view
class="recommend-item"
v-for="(item, index) in recommendItems"
:key="index"
@click="toGoods(item.productId)"
>
<view>
<view class="recommend-img">
<image class="recommend-img" :src="item.image" mode="aspectFit"></image>
<view class="name-wrap v12-white-text v12-text-center more-t">{{
item.title
}}</view>
</view>
<view class="v12-px-2 v12-font-bold v12-font-28 more-t v12-my-2">{{
item.productName
}}</view>
</view>
<view
class="v12-justify-between v12-primary-text v12-font-bold v12-px-2 v12-pb-2"
>
<view class="">
<text class="v12-font-32"></text>
<text class="v12-font-36">{{ item.price }}</text>
</view>
<view class="cart-img">
<image
class="cart-img"
:src="webUrl + '/orderIcon/组 355.png'"
></image>
</view>
</view>
</view>
</view>
<view v-if="recommendItems.length === 0">
<view class="no-data-wrap v12-justify-center v12-mt-6">
<image
:src="webUrl + '/orderIcon/微信图片_20241009220552.png'"
mode="widthFix"
></image>
</view>
</view>
</view>
</view>
</template>
<script>
import { getRank, getFoods, getRecipe } from "@/api/health";
export default {
data() {
return {
webUrl: this.$VUE_APP_RESOURCES_URL,
foodItems: [],
recommendItems: [],
cityId: "",
cityName: "",
keyword: "",
rank: [],
};
},
onLoad(opts) {
this.cityId = opts.city;
this.cityName = decodeURIComponent(opts.cityName);
this.getRank();
this.getList();
this.getRecipeList();
},
methods: {
toGoods(id) {
this.$yrouter.push({
path: "/pages/shop/GoodsCon/index",
query: {
id: id,
},
});
},
toSanctun() {
uni.redirectTo({
url:
"/pkg_product/views/healthSanctum?city=" +
this.cityId +
"&cityName=" +
this.cityName,
});
},
getRecipeList() {
getRecipe().then((res) => {
// console.log(res);
this.foodItems = res.data.records;
});
},
toDetails(id) {
uni.navigateTo({
url: "/pkg_product/views/healthFoodDetails?id=" + id,
});
},
getRank() {
let params = {
goodCityId: this.cityId,
};
getRank(params).then((res) => {
this.rank = res.data;
});
},
getList() {
const params = {
goodCityId: this.cityId,
keyword: this.keyword,
page: 1,
limit: 9999,
};
getFoods(params).then((res) => {
this.recommendItems = res.data.records;
});
},
search() {
this.getList();
},
},
};
</script>
<style lang="less" scoped>
.arrow-icon {
width: 25rpx;
height: 25rpx;
}
.icon {
width: 30rpx;
height: 30rpx;
}
.rank2-bg {
background-color: #ecdad6 !important;
}
.rank3-bg {
background-color: #dae3f3 !important;
}
.rank3-3-bg {
background-color: #c8c8c8 !important;
}
.rank2-2-bg {
background-color: #d3b9b2 !important;
}
.rank2-4-bg {
background-color: #ad8578 !important;
}
.rank3-4-bg {
background-color: #a5a5a5 !important;
}
.rank-2 {
width: 40rpx; /* 宽度 */
height: 45rpx; /* 高度,根据正六边形的几何特性计算 */
background-color: #ddc76f; /* 背景颜色 */
clip-path: polygon(50% 0%, 100% 25%, 100% 75%, 50% 100%, 0% 75%, 0% 25%);
display: flex;
align-items: center;
justify-content: center;
}
.rank-3 {
width: 30rpx; /* 宽度 */
height: 35rpx; /* 高度,根据正六边形的几何特性计算 */
background-color: #ad9161; /* 背景颜色 */
clip-path: polygon(50% 0%, 100% 25%, 100% 75%, 50% 100%, 0% 75%, 0% 25%);
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
color: #333;
}
.rank-4 {
width: 25rpx; /* 宽度 */
height: 28rpx; /* 高度,根据正六边形的几何特性计算 */
background-color: #fff3c2; /* 背景颜色 */
clip-path: polygon(50% 0%, 100% 25%, 100% 75%, 50% 100%, 0% 75%, 0% 25%);
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
color: #333;
}
.ys-btn {
background: #e2d7c3;
}
.cart-img {
width: 48rpx;
height: 48rpx;
border-radius: 50%;
}
.bg-img {
position: absolute;
height: 100%;
}
.recommends {
display: grid;
grid-template-columns: 1fr 1fr;
grid-column-gap: 20rpx;
grid-row-gap: 20rpx;
}
.recommend-img {
width: 344rpx;
height: 344rpx;
background: #333;
border-radius: 20rpx;
position: relative;
overflow: hidden;
}
.name-wrap {
position: absolute;
background: rgba(0, 0, 0, 0.2);
width: 100%;
padding: 8px 0;
bottom: 0;
height: 70rpx;
}
.recommend-item {
background: #ffffff;
border-radius: 20rpx;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.recommend-title {
position: relative;
width: fit-content;
}
.slider {
position: absolute;
width: 100%;
height: 6rpx;
background: linear-gradient(
to right,
#c52733 0%,
rgba(255, 255, 255, 0) 100%
);
border-radius: 6rpx;
bottom: 5rpx;
}
.rank-wrap {
background: #e2bc8d;
border-radius: 20rpx;
}
.rank-tag {
width: 50rpx; /* 宽度 */
height: 55rpx; /* 高度,根据正六边形的几何特性计算 */
background-color: #fbeeb7; /* 背景颜色 */
clip-path: polygon(50% 0%, 100% 25%, 100% 75%, 50% 100%, 0% 75%, 0% 25%);
position: absolute;
text-align: center;
color: #ffffff;
font-size: 20rpx;
line-height: 55rpx;
top: -22.5rpx;
display: flex;
align-items: center;
justify-content: center;
}
.rank-img {
min-width: 104rpx;
width: 104rpx;
height: 104rpx;
background: #666;
border-radius: 20rpx;
}
.bee-rank,
.drug-rank {
width: 100%;
background: #999;
border-radius: 20rpx;
overflow: hidden;
box-sizing: border-box;
position: relative;
}
.rank-title-wrap {
height: 140rpx;
display: flex;
align-items: center;
}
.rank-title {
width: 200rpx;
padding: 10rpx 5rpx;
background: linear-gradient(
to right,
#ffffff 0%,
rgba(255, 255, 255, 0) 100%
);
z-index: 999;
}
.rank-items-wrap {
display: grid;
grid-template-columns: 1fr 1fr;
grid-column-gap: 20rpx;
padding: 20rpx;
box-sizing: border-box;
}
.rank-item {
width: 100%;
height: 116rpx;
background: #ffffff;
border-radius: 20rpx;
padding: 0 10rpx;
box-sizing: border-box;
position: relative;
}
.title-img {
width: 404rpx;
}
.health-foods {
min-height: 100vh;
background-color: #faeed8;
padding: 20rpx;
box-sizing: border-box;
}
.flex-1 {
flex: 1;
}
.swap {
overflow: auto;
}
.trave-item-wrap {
position: relative;
}
.trave-btn {
position: absolute;
left: 0;
right: 0;
margin: auto;
bottom: 30rpx;
text-align: center;
width: 212rpx;
background: rgba(255, 255, 255, 0.7);
border-radius: 60rpx;
font-size: 28rpx;
font-weight: bold;
padding: 10rpx 20rpx;
display: flex;
align-items: center;
justify-content: center;
}
.trave-item {
width: 382rpx;
height: 272rpx;
background: rgba(230, 178, 178, 0.39);
border-radius: 20rpx;
overflow: hidden;
}
</style>
+129 -54
View File
@@ -1,20 +1,26 @@
<template>
<view class="pkg-product-list">
<view class="first-half">
<view class="search-box flex jc-between ai-center">
<input
v-model="keyword"
type="text"
placeholder="搜索内容"
placeholder-style="color:#949494"
@confirm="searchClick"
>
<image
<view class="v12-px-3 v12-mb-4">
<!-- <image
:src="webUrl+'/20220903142935869059.png'"
class="icon"
class="icon v12-mr-3"
mode="scaleToFill"
@click="searchClick"
/>
<input
v-model="keyword"
type="text"
placeholder-style="color:#949494"
@confirm="searchClick"
> -->
<u-search
:showAction="false"
v-model="keyword"
bgColor="#f2f2f2"
placeholder="搜索城市"
searchIconColor="#C52733"
@search="searchClick"></u-search>
</view>
<swiper
v-if="bannerList.length > 0"
@@ -30,13 +36,13 @@
:key="index"
>
<image
:src="item.pic"
:src="item.image"
mode="scaleToFill"
@click="$global.commonJump(item.uniappUrl)"
@click="toExpo(item.exhibitionId)"
/>
</swiper-item>
</swiper>
<view
<!-- <view
v-show="tabIndex === 0"
class="tab-box flex jc-center ai-center"
>
@@ -65,6 +71,14 @@
class="active"
@click="changeTab(1)"
/>
</view> -->
<view class="v12-justify-start v12-align-center news-wrap v12-radius-100">
<view class="v12-font-bold v12-primary v12-white-text v12-mr-2 v12-pa-2 v12-radius-100 news-title">最新展会</view>
<swiper vertical class="text-swpier " autoplay circular duration="500" interval="3000" >
<swiper-item v-for="(item, index) in sanctum" :key="index" class="v12-align-center" @click="toExpo(item.id)">
<text class="v12-font-28 v12-dark-text one-t" >{{ item.name }}</text>
</swiper-item>
</swiper>
</view>
</view>
<view class="body-box">
@@ -72,50 +86,51 @@
<view
v-for="(item, index) in asideList"
:key="index"
:class="index === asideIndex ? 'active' : ''"
class="item flex jc-center ai-center"
@click="asideTap(index)"
:class="index === asideIndex ? 'actived' : ''"
class="item flex jc-center ai-center "
@click="asideTap(index, item.label)"
>
<text class="one-t">{{ item.label }}</text>
</view>
</view>
<view class="article-box">
<scroll-view
v-if="dataList.length > 0"
:style="{'height':listHeight}"
:enable-flex="true"
:scroll-anchoring="true"
:scroll-y="true"
:scroll-with-animation="true"
:enable-back-to-top="true"
class="list flex flex-wrap"
@scrolltolower="onLower"
>
<view class="v12-mt-2 v12-ml-3 v12-px-1 v12-font-28 v12-dark-text v12-font-bold label-wrap">{{ label }}</view>
<scroll-view
v-if="dataList.length > 0"
:style="{'height':listHeight}"
:enable-flex="true"
:scroll-anchoring="true"
:scroll-y="true"
:scroll-with-animation="true"
:enable-back-to-top="true"
class="list flex flex-wrap"
@scrolltolower="onLower"
>
<view
v-for="(item, index) in dataList"
:key="index"
>
<view
v-if="tabIndex === 0"
class="item item-store"
@click="goStore(item.id)"
class="city-item"
@click="goStore(item.id, item.cityName)"
>
<image
:src="item.cover + (item.coverType === 2 ? '?vframe/jpg/offset/1' : '')"
:src="item.icon"
class="cover"
mode="scaleToFill"
/>
<view class="name one-t">{{ item.content }}</view>
<view class="price store flex ai-center" style="width: 100%;">
<image
<!-- <view class="name one-t">{{ item.content }}</view> -->
<view class="store flex ai-center" style="width: 100%;">
<!-- <image
:src="item.logo"
class="flex-0"
mode="scaleToFill"
/>
<view class="one-t">{{ item.name }}</view>
/> -->
<view class="one-t v12-font-28 v12-mt-3" style="color: #033333">{{ item.cityName }}</view>
</view>
</view>
<view
<!-- <view
v-else
class="item"
@click="goGoods(item.id)"
@@ -127,7 +142,7 @@
/>
<view class="name more-t">{{ item.storeName }}</view>
<view class="price">{{ item.price }}</view>
</view>
</view> -->
</view>
</scroll-view>
<view class="tc" v-else>
@@ -153,6 +168,7 @@
// 康养食材
import { fetchCity } from '@/api/wenwan'
import { wellnessFood, wellnessPlaceList } from '@/api/product'
import { getWellnessPlace, getWellnessExhibition, getBanner } from '@/api/health'
import { getProducts } from '@/api/store'
import { pageListenMixins } from '@/mixins/pageListenMixins'
export default {
@@ -169,7 +185,9 @@ export default {
asideList: [],
asideIndex: 0,
dataList: [],
pageKeyId: Object.freeze('wellnessFoodIndex')
pageKeyId: Object.freeze('wellnessFoodIndex'),
label: '',
sanctum: {}
}
},
onLoad() {
@@ -177,13 +195,23 @@ export default {
},
methods: {
init() {
wellnessFood().then(({data}) => {
this.bannerList = data.banner
getBanner().then(({data}) => {
this.bannerList = data
})
fetchCity(4).then(({data}) => {
this.asideList = data.group
this.label = this.asideList[this.asideIndex].label
this.searchReq()
})
getWellnessExhibition().then(({data}) => {
this.sanctum = data.records
})
},
toExpo(id) {
if(!id) return
uni.navigateTo({
url: '/pkg_product/views/expoDetails?id=' + id
})
},
changeTab(index) {
if (this.asideList.length === 0) return
@@ -198,8 +226,25 @@ export default {
this.searchReq()
},
searchClick() {
this.page = 1
this.searchReq()
// this.page = 1
// this.searchReq()
const searchMap = this.asideList.map((e, i) => {
return e.city.map(_e => {
return {
index: i,
..._e
}
})
}).flat()
// console.log(searchMap);
const filterMap = searchMap.filter(el => el.cityName.includes(this.keyword))
if(filterMap.length === 0) {
return
} else {
this.asideIndex = filterMap[0].index
this.searchReq()
}
},
searchReq() {
let param = {
@@ -209,15 +254,18 @@ export default {
provinceId: this.asideList.length > 0 ? this.asideList[this.asideIndex].provinceId : ''
}
if (this.tabIndex === 0) {
wellnessPlaceList(param).then(({data}) => {
this.handleData(data)
})
} else {
param.isWellnessFood = 1
getProducts(param).then(({data}) => {
this.handleData(data)
})
}
// wellnessPlaceList(param).then(({data}) => {
// this.handleData(data)
// })
this.label = this.asideList[this.asideIndex].label
this.dataList = this.asideList[this.asideIndex].city
}
// else {
// param.isWellnessFood = 1
// getProducts(param).then(({data}) => {
// this.handleData(data)
// })
// }
},
handleData(data) {
const that = this
@@ -239,15 +287,16 @@ export default {
}
})
},
asideTap(index) {
asideTap(index, label) {
this.asideIndex = index
this.page = 1
this.dataList = []
this.label = label
this.searchReq()
},
goStore(id) {
goStore(id, name) {
uni.navigateTo({
url: '/pagesInn/inn/innHome?id=' + id + '&from=wellness'
url: '/pkg_product/views/healthSanctum?city=' + id +'&cityName=' + name
})
},
goGoods(id) {
@@ -261,6 +310,32 @@ export default {
<style scoped lang="less">
@import "@/assets/css/product.less";
.text-swpier{
height: 60rpx;
width: 100%;
}
.cover{
width: 160rpx !important;
height: 160rpx !important;
border-radius: 8rpx;
}
.city-item{
width: 160rpx !important;
margin: 30rpx 0rpx 0rpx 20rpx;
}
.news-title{
white-space: nowrap;
}
.actived, .label-wrap{
border-left: 6rpx solid #C52733;
color: #C52733;
font-weight: bold;
}
.news-wrap{
background: rgba(233,233,233,0.39);
width: 686rpx;
margin: 0 auto;
}
.pkg-product-list {
height: 100vh;
+261
View File
@@ -0,0 +1,261 @@
<template>
<view class="sanctum-wrap">
<view class="v12-justify-center">
<view class="v12-mr-3 v12-radius-100 v12-primary v12-white-text v12-px-5 v12-py-2 v12-font-28 v12-font-bold">养生圣地</view>
<view class="v12-radius-100 v12-grey v12-dark-text v12-px-5 v12-py-2 v12-font-28 v12-font-bold" @click="toFoods">养生食材</view>
</view>
<view class="v12-justify-between v12-align-center v12-mt-5">
<image
:src="webUrl + '/orderIcon/map_dark.png'"
mode="widthFix"
class="icon"
/>
<view class="v12-font-28 v12-secondary-dark-text v12-mr-3">{{ cityName }}</view>
<view class="flex-1" @click="search">
<u-search searchIconColor="#C52733" :disabled="true" :showAction="false" v-model="keyword" ></u-search>
</view>
</view>
<swiper v-if="traveItems.length > 0" class="v12-justify-between swap" autoplay circular next-margin="300rpx" duration="10000" interval="3000" >
<swiper-item v-for="(item, index) in traveItems" :key="index" >
<view class="trave-item v12-mr-3 v12-mt-3">
<view class="trave-item">
<image class="trave-item" :src="item.cover" v-if="item.coverType === 1"></image>
<video class="trave-item" :src="item.cover" :show-fullscreen-btn="false" :show-center-play-btn="flase" :show-play-btn="false" v-if="item.coverType === 2"></video>
</view>
<view class="trave-btn" @click="toStore(item.hotelId)">
<text>旅居预约</text>
<image :src="webUrl + '/orderIcon/arrow.png'" class="arrow-icon v12-ml-1"></image>
</view>
</view>
</swiper-item>
</swiper>
<view class="v12-justify-between v12-align-center v12-px-3 v12-mt-3 tabs-wrap">
<view
class="tab v12-font-34 v12-dark-text v12-mr-5"
v-for="(tab, index) in tabs"
@click="handleTab(index,tab)"
:class="{'v12-font-bold' : index === actived}"
:key="index">
<text>{{ tab.name }}</text>
<view :class="{'slider': index === actived}"></view>
</view>
</view>
<view class="v12-justify-between sanctup-items v12-mt-3">
<view v-for="(item, index) in items" :key="index" class="v12-pa-1" @click="toStore(item.hotelId)">
<view class="img">
<image class="img" :src="item.cover" v-if="item.coverType === 1"></image>
<video
class="img"
:src="item.cover"
:play-btn-position="center"
:show-fullscreen-btn="false"
:show-center-play-btn="flase"
:show-play-btn="false"
:controls="false"
v-if="item.coverType === 2">
</video>
<view class="v12-white-text address">
<image
class="map-icon v12-mr-1"
:src="webUrl + '/orderIcon/map_white.png'"
></image>
<text class="v12-font-28">{{ item.areaName }}</text>
</view>
</view>
<view class="v12-font-bold v12-font-28 v12-mt-1 more-t desc-txt">{{ item.content }}</view>
<view class="v12-justify-start v12-align-center v12-my-1">
<view class="atr">
<image class="atr" :src="item.logo"></image>
</view>
<view class="v12-font-26 v12-ml-2 v12-dark-text one-t">{{ item.hotelName }}</view>
</view>
</view>
</view>
<view v-if="items.length === 0" class="v12-justify-center v12-mt-6">
<view class="no-data-wrap">
<image :src="webUrl + '/orderIcon/微信图片_20241009220552.png'" mode="widthFix"></image>
</view>
</view>
</view>
</template>
<script>
import { getCategory,getWellnessPlace } from '@/api/health'
export default {
data() {
return {
webUrl: this.$VUE_APP_RESOURCES_URL,
tabs: [],
items: [],
traveItems: [],
actived: 0,
cityId: '',
cityName: '',
categoryId: null,
keyword: ''
}
},
onLoad(opts) {
this.cityId = opts.city
this.cityName = decodeURIComponent(decodeURIComponent(opts.cityName))
getCategory().then(res => {
this.tabs = res.data
this.categoryId = res.data[0].id
this.getList()
})
this.getTop()
},
onUnload() {
// uni.navigateBack({
// delta: 0
// })
},
methods: {
handleTab(index, tab) {
this.actived = index
this.categoryId = tab.id
this.getList()
},
toFoods() {
uni.redirectTo({
url: '/pkg_product/views/healthFoods?city=' + this.cityId + '&cityName=' + this.cityName
})
},
getList() {
const params = {
goodCityId: this.cityId,
categoryId: this.categoryId,
keyword: this.keyword,
page: 1,
limit: 9999
}
getWellnessPlace(params).then(res => {
// console.log(res);
this.items = res.data.records
})
},
search() {
uni.navigateTo({
url: '/pkg_product/views/healthSearch?city=' + this.cityId + '&cityName=' + this.cityName
})
},
getTop() {
const params = {
goodCityId: this.cityId,
categoryId: 0,
page: 1,
limit: 9999
}
getWellnessPlace(params).then(res => {
// console.log(res);
this.traveItems = res.data.records
})
},
toStore(id) {
this.$yrouter.push({
path: '/pagesInn/inn/innHome',
query: {
id: id
}
})
},
}
}
</script>
<style lang="less" scoped>
.tabs-wrap{
white-space: nowrap;
overflow: scroll;
.tab:last-child{
margin-right: 0 !important;
}
}
.map-icon{
width: 28rpx;
height: 28rpx;
}
.arrow-icon{
width: 25rpx;
height: 25rpx;
}
.icon{
width: 30rpx;
height: 30rpx;
}
.flex-1{
flex: 1;
}
.swap{
overflow: auto;
}
.trave-item-wrap{
position: relative;
}
.trave-btn{
position: absolute;
left: 0;
right: 0;
margin: auto;
bottom: 30rpx;
text-align: center;
width: 212rpx;
background: rgba(255,255,255,0.7);
border-radius: 60rpx;
font-size: 28rpx;
font-weight: bold;
display: flex;
align-items: center;
justify-content: center;
padding: 10rpx 20rpx;
}
.trave-item{
width: 382rpx;
height: 272rpx;
background: rgba(230,178,178,0.39);
border-radius: 20rpx;
position: relative;
}
.desc-txt {
height: 78rpx;
line-height: 40rpx;
}
.address{
position: absolute;
left: 30rpx;
bottom: 30rpx;
display: flex;
align-items: center;
justify-content: center;
}
.sanctup-items{
display: grid;
grid-template-columns: 50% 50%;
}
.atr{
width: 52rpx;
height: 52rpx;
border-radius: 50%;
}
.img{
width: 320rpx;
height: 320rpx;
// background: #707070;
border-radius: 16rpx;
position: relative;
}
.sanctum-wrap{
min-height: 100vh;
background-color: #fff;
padding: 20rpx ;
}
.tab{
position: relative;
}
.slider{
position: absolute;
width: 100%;
height: 6rpx;
background: linear-gradient(to right, #C52733 0%, rgba(255,255,255,0) 100%);
border-radius: 6rpx;
bottom: 5rpx;
}
</style>
+133
View File
@@ -0,0 +1,133 @@
<template>
<view class="search-page">
<view class="v12-mr-3">
<u-search searchIconColor="#C52733" :showAction="false" v-model="keyword" @search="search"></u-search>
</view>
<view v-if="isSearch" class="search-res-wrap">
<view class="v12-justify-between sanctup-items v12-mt-3">
<view v-for="(item, index) in items" :key="index" class="v12-pa-1" @click="toStore(item.hotelId)">
<view class="img">
<image class="img" :src="item.cover" v-if="item.coverType === 1"></image>
<video
class="img"
:src="item.cover"
:play-btn-position="center"
:show-fullscreen-btn="false"
:show-play-btn="false"
:show-center-play-btn="flase"
v-if="item.coverType === 2">
</video>
<view class="v12-white-text address">
<image
class="map-icon v12-mr-1"
:src="webUrl + '/orderIcon/map_white.png'"
></image>
<text class="v12-font-28">{{ item.areaName }}</text>
</view>
</view>
<view class="v12-font-bold v12-font-28 v12-mt-1 more-t desc-txt">{{ item.content }}</view>
<view class="v12-justify-start v12-align-center v12-my-1">
<view class="atr">
<image class="atr" :src="item.logo"></image>
</view>
<view class="v12-font-26 v12-ml-2 v12-dark-text one-t">{{ item.hotelName }}</view>
</view>
</view>
</view>
<view v-if="items.length === 0 && keyword" class="v12-justify-center v12-mt-16">
<view class="no-data-wrap">
<image :src="webUrl + '/orderIcon/微信图片_20241009220552.png'" mode="widthFix"></image>
</view>
</view>
</view>
</view>
</template>
<script>
import { getCategory,getWellnessPlace } from '@/api/health'
export default {
data() {
return {
webUrl: this.$VUE_APP_RESOURCES_URL,
keyword: '',
goodCityId: '',
isSearch: false,
items: []
}
},
onLoad(opts) {
this.goodCityId = opts.city
},
methods: {
search() {
const params = {
goodCityId: this.goodCityId,
categoryId: '',
keyword: this.keyword,
page: 1,
limit: 9999
}
getWellnessPlace(params).then(res => {
this.items = res.data.records
}).finally(() => {
this.isSearch = true
})
},
toStore(id) {
this.$yrouter.push({
path: '/pagesInn/inn/innHome',
query: {
id: id
}
})
},
}
}
</script>
<style lang="less" scoped>
.map-icon{
width: 28rpx;
height: 28rpx;
}
.arrow-icon{
width: 25rpx;
height: 25rpx;
}
.icon{
width: 30rpx;
height: 30rpx;
}
.address{
position: absolute;
left: 30rpx;
bottom: 30rpx;
display: flex;
align-items: center;
justify-content: center;
}
.desc-txt {
height: 78rpx;
line-height: 40rpx;
}
.search-page{
min-height: 100vh;
background-color: #fff;
padding: 20rpx;
}
.sanctup-items{
display: grid;
grid-template-columns: 50% 50%;
}
.img{
width: 320rpx;
height: 320rpx;
border-radius: 16rpx;
position: relative;
}
.atr{
width: 52rpx;
height: 52rpx;
border-radius: 50%;
}
</style>
+35 -17
View File
@@ -42,19 +42,22 @@ export default {
goDetails(item) {
if (!item.isLink) return
// 1-视频,2-图文(可配置商品),3-商品+视频
if (item.type === 1) {
uni.navigateTo({
url: '/pkg_product/views/heritageVideo?id=' + item.id
})
} else if (item.type === 2) {
uni.navigateTo({
url: '/pkg_product/views/heritageGoods?id=' + item.id
})
} else if (item.type === 3) {
uni.navigateTo({
url: '/pkg_product/views/heritageGoodAndVideo?id=' + item.id
})
}
uni.navigateTo({
url: '/pkg_product/views/heritage/index?id=' + item.id
})
// if (item.type === 1) {
// uni.navigateTo({
// url: '/pkg_product/views/heritage/index?id=' + item.id
// })
// } else if (item.type === 2) {
// uni.navigateTo({
// url: '/pkg_product/views/heritage/index?id=' + item.id
// })
// } else if (item.type === 3) {
// uni.navigateTo({
// url: '/pkg_product/views/heritage/index?id=' + item.id
// })
// }
}
}
}
@@ -67,14 +70,27 @@ export default {
<image style="width: 100%" :src="backgroundImage" mode="widthFix"/>
<view class="item" :style="{'left':item.gx+'rpx','top':item.gy+'rpx'}" v-for="(item,index) in contents"
:key="index" @click="goDetails(item)">
<image class="img-item" :src="item.image"/>
<view class="title flex jc-center">{{ item.title }}</view>
<image class="img-item" :src="item.image" :class="{'unlink': !item.isLink}"/>
<view
class=" v12-text-center v12-white-text v12-radius-80 v12-px-1 title"
:class="{
'v12-primary': item.isLink,
'un-actived': !item.isLink
}">
{{ item.title }}</view>
</view>
</view>
</view>
</template>
<style scoped lang="less">
.unlink{
filter: grayscale(100%);
}
.un-actived{
background: #494949 !important;
background-color: #494949 !important;
}
.pkg-product-heritage {
image {
vertical-align: middle;
@@ -87,10 +103,12 @@ export default {
position: absolute;
width: 160rpx;
font-size: 24rpx;
display: flex;
flex-direction: column;
align-items: center;
.title {
position: relative;
width: 100%;
width: fit-content;
white-space: nowrap;
}
}
+231
View File
@@ -0,0 +1,231 @@
<template>
<view class="details">
<view class="top-img">
<video class="top-img" :show-fullscreen-btn="false" autoplay play-btn-position="center" :src="details.video"></video>
</view>
<view class="title-img">
<view class="img-wrap">
<view class="v12-align-center">
<image class="left-img" :src="webUrl + '/orderIcon/左.png'"></image>
<view class="v12-align-center text-img-wrap">
<image :src="webUrl + '/orderIcon/中.png'"></image>
<view class="v12-font-36 d-title">{{ details.name }}</view>
</view>
<image class="left-img" :src="webUrl + '/orderIcon/右.png'"></image>
</view>
</view>
<view class="v12-font-26 v12-font-bold v12-dark-text intro-text">{{ details.intro }}</view>
</view>
<view class="tabs v12-my-3 v12-font-32 v12-font-bold">
<view class="tab-btn" :class="{ 'v12-primary': actived === 1, 'v12-white-text': actived === 1 }" @click="handleTab(1)">{{details.pageA.title}}</view>
<view class="tab-btn v12-ml-3" :class="{ 'v12-primary': actived === 2, 'v12-white-text': actived === 2 }" @click="handleTab(2)">{{details.pageB.title}}</view>
</view>
<view class="pageA" v-show="actived === 1">
<view class="intro" style="white-space: break-spaces;">
{{ details.pageA.content }}
</view>
<view class="v12-d-grid-columns-2 card-wrap v12-mt-3" v-if="details.pageA && details.pageA.images.length > 0">
<view class="card-item" v-for="(item, i) in details.pageA.images" :key="i" @click="viewImg(item)">
<image class="card-item-img" :src="item.image" mode="aspectFit|aspectFill|widthFix" lazy-load="false"></image>
<view class="v12-justify-center">
<view class="one-t p-name">
{{ item.imageText }}
</view>
</view>
</view>
</view>
<view v-else>
<image
:src="webUrl + '/orderIcon/wu.png'"
mode="scaleToFill"
class="img-nodata"
/>
</view>
</view>
<view class="pageB" v-if="actived === 2">
<view class="v12-d-grid-columns-2 card-wrap v12-mt-3">
<!-- -->
<view class="card-item v12-white v12-radius-20 column-flex" v-for="(item, index) in details.pageB.hotels" :key="index" @click="toStore(item)">
<view>
<image class="card-item-img" :src="item.cover" mode="aspectFit|aspectFill|widthFix" lazy-load="false"></image>
<view class="more-t v12-font-28 v12-font-bold v12-px-2">
{{ item.content || '' }}
</view>
</view>
<view class="card-footer v12-pa-2">
<image class="footer-logo v12-mr-3" :src="item.logo" mode="aspectFit|aspectFill|widthFix" lazy-load="false"> </image>
<view class="v12-font-28 one-t name">{{ item.hotelName }}</view>
</view>
</view>
</view>
<view v-if="details.pageB && details.pageB.hotels.length === 0">
<image
:src="webUrl + '/orderIcon/wu.png'"
mode="scaleToFill"
class="img-nodata"
/>
</view>
</view>
</view>
</template>
<script>
import { getIchContent } from '@/api/heritage'
export default {
data() {
return {
webUrl: this.$VUE_APP_RESOURCES_URL,
actived: 1,
id: '',
details: {}
}
},
onLoad(opts) {
this.id = opts.id
this.getDetails()
},
methods: {
viewImg(item) {
uni.previewImage({
current: item.image, // 当前显示图片的http链接
urls: this.details.pageA.images.map(e => e.image) // 需要预览的图片http链接列表
})
},
handleTab(index) {
this.actived = index
},
getDetails() {
getIchContent(this.id).then(res => {
this.details = res.data || {}
})
},
toStore(item) {
uni.navigateTo({
url: '/pagesInn/inn/innHome?id=' + item.hotelId
})
}
}
}
</script>
<style lang="less" scoped>
.column-flex {
display: flex;
flex-direction: column;
justify-content: space-between;
}
.img-nodata {
position: relative;
top: 0;
left: 50%;
transform: translate(-50%, 0);
width: 352rpx;
height: 338rpx;
margin-bottom: 60rpx;
}
.intro-text{
text-align: center;
margin-top: 20rpx;
}
.text-img-wrap{
width: fit-content;
position: relative;
image{
width: 100%;
height: 70rpx;
position: absolute;
}
}
.img-wrap{
display: flex;
justify-content: center;
align-items: center;
}
.left-img{
height: 70rpx;
width: 60rpx;
}
.d-title{
text-align: center;
font-weight: bold;
z-index: 1;
color: #fff;
line-height: 70rpx;
}
.p-name{
max-width: 200rpx;
text-align: center;
}
.card-footer{
display: flex;
align-items: center;
.name{
width: 200rpx;
}
}
.footer-logo{
width: 60rpx;
height: 60rpx;
background: rgba(255,255,255,0.39);
border-radius: 50%;
border: 2px solid #707070;
}
.card-wrap{
grid-gap: 20rpx 20rpx;
}
.card-item-img{
width: 100%;
height: 320rpx;
border-radius: 20rpx;
}
.intro{
width: 100%;
background: #FAEED8;
border-radius: 20rpx;
padding: 20rpx;
box-sizing: border-box;
}
.pageA{
width: 100%;
background: rgba(255,255,255,1);
border-radius: 20rpx;
padding: 20rpx;
box-sizing: border-box;
}
.tabs{
display: flex;
justify-content: center;
}
.tab-btn{
width: fit-content;
height: 66rpx;
border-radius: 36rpx;
padding: 0 20rpx;
text-align: center;
line-height: 66rpx;
background: rgba(221,210,190,0.39);
}
.details{
min-height: 100vh;
padding: 20rpx;
background: #FAEED8;
}
.top-img{
width: 100%;
height: 400rpx;
background: rgba(255,255,255,0.39);
border-radius: 20rpx;
}
.title-img{
/* height: 146rpx; */
width: 100%;
border-radius: 146rpx;
margin-top: 24rpx;
background: #e0bb8c;
padding: 10rpx 60rpx;
box-sizing: border-box;
}
</style>
+281
View File
@@ -0,0 +1,281 @@
<template>
<view class="heritage">
<view class="search-top">
<swiper class="swiper" disable-touch="true" touchable="false" :autoplay="ichData.banners && ichData.banners.length > 1" circular :current="current">
<swiper-item class="search-bg" v-for="(item, index) in ichData.banners" :key="index" @click="toDetails(item.ichContentId)">
<image class="search-bg" :src="item.image" autoplay mode="aspectFit|aspectFill|widthFix" lazy-load="false"></image>
</swiper-item>
</swiper>
<view class="search-input">
<u-search
:showAction="false"
v-model="keyword"
placeholder="搜索"
searchIconColor="#C52733"
:customStyle="{
backgroundColor: 'rgba(255,255,255,0.7)',
borderRadius: '100rpx'
}"
@search="searchClick"></u-search>
</view>
</view>
<view class="v12-mt-6 tab-wrap">
<view class="tab-item" v-for="(item, index) in ichData.categories" :key="index" @click="handleTab(index, item)">
{{ item.categoryName }}
<view :class="{ 'line': actived === index }"></view>
</view>
</view>
<view class="wiper-wrap v12-mt-3">
<swiper v-if="contents.length > 0" class="" style="width: 100%;height: 400rpx" :autoplay="contents.length > 2" circular next-margin="400rpx">
<swiper-item v-for="(item, index) in contents" :key="index" @click="toDetails(item.ichContentId)">
<view class="wiper-item">
<image class="wiper-item" :src="item.cover" mode="aspectFit|aspectFill|widthFix" lazy-load="false"> </image>
<view class="title-link">
<text class="title-link__text one-t v12-font-bold">
{{ item.name }}
</text>
<u-icon name="arrow-right" color="#fff" size="14"></u-icon>
</view>
</view>
</swiper-item>
</swiper>
<view v-if="contents.length === 0" style="width:100%">
<image
:src="webUrl + '/orderIcon/wu.png'"
mode="scaleToFill"
class="img-nodata"
/>
</view>
</view>
<!-- <swiper style="height: 400rpx;" class="v12-justify-between swap v12-mt-3" autoplay circular next-margin="400rpx" duration="10000" interval="3000" >
<swiper-item v-for="(item, index) in contents" :key="index" >
<view class="wiper-wrap">
<view>
<video :src="item.video" class="wiper-item" object-fit="cover"></video>
<view class="title-link">{{ item.name }}</view>
</view>
</view>
</swiper-item>
</swiper> -->
<view class="v12-font-36 v12-dark-text v12-font-bold v12-mt-3 tab-item">推荐
<view class="line"></view>
</view>
<view class="v12-d-grid-columns-2 v12-mt-3 row-column-gap">
<view class="recommend-item" v-for="(item, index) in recommendList" :key="index" @click="toDetails(item.id)">
<image class="recommend-item image" :src="item.recommendImage" mode="aspectFit|aspectFill|widthFix" lazy-load="false"> </image>
<view class="recommend-item flex-item">
<view class="v12-font-24 v12-white-text"></view>
<view class=" intro-bg">
<view style="padding: 0 20rpx;">
<view class="item-name one-t v12-px-2 v12-white-border v12-white-text v12-font-32 v12-radius-100 v12-mb-2">
<text class="title-link__text one-t v12-font-bold">
{{ item.name }}
</text>
<u-icon name="arrow-right" color="#fff" size="16"></u-icon>
</view>
</view>
<view class="v12-white-text v12-font-26 one-t">{{ item.intro }}</view>
</view>
</view>
</view>
</view>
<view v-if="recommendList.length === 0">
<image
:src="webUrl + '/orderIcon/wu.png'"
mode="scaleToFill"
class="img-nodata"
/>
</view>
</view>
</template>
<script>
import { getIch, getIchRecommend } from '@/api/heritage'
export default {
data() {
return {
webUrl: this.$VUE_APP_RESOURCES_URL,
ichData: {},
id: '',
actived: 0,
contents: [],
recommendList: [],
keyword: '',
categoryId: '',
cacheList: []
}
},
onLoad(opts) {
this.id = opts.id
this.getIchData()
},
methods: {
searchClick() {
this.getRecommend()
this.contents = this.cacheList.filter(e => e.name.indexOf(this.keyword) > -1)
},
getIchData() {
getIch(this.id).then(res => {
this.ichData = res.data
this.cacheList = res.data.categories[0] && res.data.categories[0].contents || []
this.contents = this.cacheList
this.categoryId = res.data.categories[0].ichCategoryId
this.getRecommend()
})
},
getRecommend() {
const params = {
ichDataId: this.ichData.id,
keyword: this.keyword,
categoryId: this.categoryId
}
getIchRecommend(params).then(res => {
this.recommendList = res.data
})
},
handleTab(index, item) {
this.actived = index
this.cacheList = item.contents
this.contents = this.cacheList.filter(e => e.name.indexOf(this.keyword) > -1)
this.categoryId = item.ichCategoryId
this.getRecommend()
},
toDetails(id) {
if(!id) return
uni.navigateTo({
url: '/pkg_product/views/heritage/details?id=' + id
})
}
}
}
</script>
<style lang="less" scoped>
.swiper{
height: 360rpx;
}
.intro-bg{
padding: 10rpx 20rpx;
background: rgba(0,0,0,0.5);
}
.row-column-gap{
grid-column-gap: 20rpx;
grid-row-gap: 20rpx;
}
.title-link__text{
max-width: 200rpx;
}
.search-top{
position: relative;
}
.search-input{
position: absolute;
width: 100%;
bottom: 20rpx;
padding: 0 40rpx;
box-sizing: border-box;
}
.arrow-icon{
width: 24rpx;
height: 24rpx;
}
.item-name{
width: fit-content;
max-width: 100%;
box-sizing: border-box;
display: flex;
align-items: center;
}
.flex-item{
display: flex;
flex-direction: column;
justify-content: space-between;
// padding: 20rpx;
}
.recommend-item{
width: 342rpx;
height: 400rpx;
// background: rgba(255,255,255,0.39);
border-radius: 20rpx;
position: relative;
box-sizing: border-box;
overflow: hidden;
.image{
position: absolute;
left: 0;
right: 0;
}
}
.title-link{
position: absolute;
width: fit-content;
background: rgba(255,255,255,0.5);
border-radius: 100rpx;
bottom: 20rpx;
left: 0;
right: 0;
margin: auto;
padding: 10rpx 20rpx;
color: #fff;
font-size: 28rpx;
display: flex;
}
.wiper-wrap{
display: flex;
overflow-y: scroll;
}
.wiper-item{
width: 280rpx;
height: 400rpx;
border-radius: 20rpx;
position: relative;
margin-right: 20rpx;
}
.tab-wrap{
display: flex;
justify-content: flex-start;
overflow-x: scroll;
}
.tab-item{
font-weight: bold;
display: flex;
white-space: nowrap;
margin-right: 50rpx;
font-size: 36rpx;
width: fit-content;
position: relative;
.line{
width: 100%;
position: absolute;
height: 6rpx;
border-radius: 16rpx;
background: linear-gradient(to right, #C52733 0%, rgba(202,56,67,0.92) 51%, rgba(255,255,255,0) 100%);
bottom: 5rpx;
}
}
.tab-item:last-child{
margin-right: 0rpx;
}
.heritage{
min-height: 100vh;
background: #FAEED8;
padding: 20rpx;
}
.search-bg{
width: 100%;
height: 360rpx;
background: rgba(255,255,255,0.39);
border-radius: 20rpx;
}
.img-nodata {
position: relative;
top: 0;
left: 50%;
transform: translate(-50%, 0);
width: 352rpx;
height: 338rpx;
margin-bottom: 60rpx;
}
</style>
+1 -1
View File
@@ -187,7 +187,7 @@ export default {
.article-box {
width: 100%;
.list {
padding: 0 0 20rpx 0;
padding: 0 0 40rpx 0;
}
.item {
width: 345rpx;
+22 -13
View File
@@ -220,7 +220,7 @@
v-if="tabsIndex === 3"
:class="{'manage-state':isManage}"
class="scroll-box county-scroll"
:style="{background : countyFamousList.length === 0 ? '#f5f5f5' : '#fff !important'}"
:style="{background : countyFamousList.length === 0 ? '#f5f5f5' : '#f5f5f5 !important'}"
scroll-y
@scrolltolower="fetchCountyFamous()"
>
@@ -799,27 +799,35 @@ export default {
}
}
.county-list-wrap {
display: flex;
flex-wrap: wrap;
width: 710rpx;
margin: 0 auto;
background-color: #f0f0f0;
// display: flex;
// flex-wrap: wrap;
// // width: 710rpx;
// justify-content: flex-start;
display: grid;
grid-template-columns: 1fr 1fr;
margin: 0 32rpx;
background-color: #f5f5f5;
grid-column-gap: 10px;
grid-row-gap: 10px;
.county-item {
width: 344rpx;
// width: 320rpx;
height: 440rpx;
padding: 12rpx;
margin: 0 22rpx 22rpx 0;
// margin: 0 22rpx 22rpx 0;
box-sizing: border-box;
border-radius: 16rpx;
background-color: #fff;
&:nth-child(2n) {
margin-right: 0;
}
display: flex;
align-items: center;
flex-direction: column;
// &:nth-child(2n) {
// margin-right: 0;
// }
.img-wrap {
position: relative;
.cover {
display: block;
width: 320rpx;
width: 300rpx;
height: 280rpx;
border-radius: 16rpx;
}
@@ -831,8 +839,9 @@ export default {
}
}
.county-info {
width: 100%;
.name {
width: 100%;
width: 280rpx;
padding: 10rpx 0 0 0;
font-size: 36rpx;
font-weight: bold;
+2 -2
View File
@@ -924,10 +924,10 @@ export const handleLoginFailure = () => {
store.commit("logout");
store.commit("updateAuthorization", false);
let currentPageUrl = getCurrentPageUrl()
const currentPageUrl = getCurrentPageUrl()
// token 失效
// 判断当前是不是已经在登录页面或者授权页,防止二次跳转
if (store.getters.isAuthorizationPage || getCurrentPageUrl() == '/pages/user/Login/index') {
if (store.getters.isAuthorizationPage || currentPageUrl == '/pages/user/Login/index' || currentPageUrl === '/pages/authorization/index') {
return
}
store.commit("updateAuthorizationPage", true);
+100
View File
@@ -0,0 +1,100 @@
import buildURL from '../helpers/buildURL'
import buildFullPath from '../core/buildFullPath'
import settle from '../core/settle'
import {isUndefined} from "../utils"
/**
* 返回可选值存在的配置
* @param {Array} keys - 可选值数组
* @param {Object} config2 - 配置
* @return {{}} - 存在的配置项
*/
const mergeKeys = (keys, config2) => {
let config = {}
keys.forEach(prop => {
if (!isUndefined(config2[prop])) {
config[prop] = config2[prop]
}
})
return config
}
export default (config) => {
return new Promise((resolve, reject) => {
let fullPath = buildURL(buildFullPath(config.baseURL, config.url), config.params, config.paramsSerializer)
const _config = {
url: fullPath,
header: config.header,
complete: (response) => {
config.fullPath = fullPath
response.config = config
response.rawData = response.data
try {
// 对可能字符串不是json 的情况容错
if (typeof response.data === 'string') {
response.data = JSON.parse(response.data)
}
// eslint-disable-next-line no-empty
} catch (e) {
}
settle(resolve, reject, response)
}
}
let requestTask
if (config.method === 'UPLOAD') {
delete _config.header['content-type']
delete _config.header['Content-Type']
let otherConfig = {
// #ifdef MP-ALIPAY
fileType: config.fileType,
// #endif
filePath: config.filePath,
name: config.name
}
const optionalKeys = [
// #ifdef APP-PLUS || H5
'files',
// #endif
// #ifdef H5
'file',
// #endif
// #ifdef H5 || APP-PLUS
'timeout',
// #endif
'formData'
]
requestTask = uni.uploadFile({..._config, ...otherConfig, ...mergeKeys(optionalKeys, config)})
} else if (config.method === 'DOWNLOAD') {
// #ifdef H5 || APP-PLUS
if (!isUndefined(config['timeout'])) {
_config['timeout'] = config['timeout']
}
// #endif
requestTask = uni.downloadFile(_config)
} else {
const optionalKeys = [
'data',
'method',
// #ifdef H5 || APP-PLUS || MP-ALIPAY || MP-WEIXIN
'timeout',
// #endif
'dataType',
// #ifndef MP-ALIPAY
'responseType',
// #endif
// #ifdef APP-PLUS
'sslVerify',
// #endif
// #ifdef H5
'withCredentials',
// #endif
// #ifdef APP-PLUS
'firstIpv4',
// #endif
]
requestTask = uni.request({..._config, ...mergeKeys(optionalKeys, config)})
}
if (config.getTask) {
config.getTask(requestTask, config)
}
})
}
@@ -0,0 +1,51 @@
'use strict'
function InterceptorManager() {
this.handlers = []
}
/**
* Add a new interceptor to the stack
*
* @param {Function} fulfilled The function to handle `then` for a `Promise`
* @param {Function} rejected The function to handle `reject` for a `Promise`
*
* @return {Number} An ID used to remove interceptor later
*/
InterceptorManager.prototype.use = function use(fulfilled, rejected) {
this.handlers.push({
fulfilled: fulfilled,
rejected: rejected
})
return this.handlers.length - 1
}
/**
* Remove an interceptor from the stack
*
* @param {Number} id The ID that was returned by `use`
*/
InterceptorManager.prototype.eject = function eject(id) {
if (this.handlers[id]) {
this.handlers[id] = null
}
}
/**
* Iterate over all the registered interceptors
*
* This method is particularly useful for skipping over any
* interceptors that may have become `null` calling `eject`.
*
* @param {Function} fn The function to call for each interceptor
*/
InterceptorManager.prototype.forEach = function forEach(fn) {
this.handlers.forEach(h => {
if (h !== null) {
fn(h)
}
})
}
export default InterceptorManager
+197
View File
@@ -0,0 +1,197 @@
/**
* @Class Request
* @description luch-request http请求插件
* @Author lu-ch
* @Email webwork.s@qq.com
* 文档: https://www.quanzhan.co/luch-request/
* github: https://github.com/lei-mu/luch-request
* DCloud: http://ext.dcloud.net.cn/plugin?id=392
*/
import dispatchRequest from './dispatchRequest'
import InterceptorManager from './InterceptorManager'
import mergeConfig from './mergeConfig'
import defaults from './defaults'
import { isPlainObject } from '../utils'
import clone from '../utils/clone'
export default class Request {
/**
* @param {Object} arg - 全局配置
* @param {String} arg.baseURL - 全局根路径
* @param {Object} arg.header - 全局header
* @param {String} arg.method = [GET|POST|PUT|DELETE|CONNECT|HEAD|OPTIONS|TRACE] - 全局默认请求方式
* @param {String} arg.dataType = [json] - 全局默认的dataType
* @param {String} arg.responseType = [text|arraybuffer] - 全局默认的responseType。支付宝小程序不支持
* @param {Object} arg.custom - 全局默认的自定义参数
* @param {Number} arg.timeout - 全局默认的超时时间,单位 ms。默认60000。H5(HBuilderX 2.9.9+)、APP(HBuilderX 2.9.9+)、微信小程序(2.10.0)、支付宝小程序
* @param {Boolean} arg.sslVerify - 全局默认的是否验证 ssl 证书。默认true.仅App安卓端支持(HBuilderX 2.3.3+
* @param {Boolean} arg.withCredentials - 全局默认的跨域请求时是否携带凭证(cookies)。默认false。仅H5支持(HBuilderX 2.6.15+
* @param {Boolean} arg.firstIpv4 - 全DNS解析时优先使用ipv4。默认false。仅 App-Android 支持 (HBuilderX 2.8.0+)
* @param {Function(statusCode):Boolean} arg.validateStatus - 全局默认的自定义验证器。默认statusCode >= 200 && statusCode < 300
*/
constructor(arg = {}) {
if (!isPlainObject(arg)) {
arg = {}
console.warn('设置全局参数必须接收一个Object')
}
this.config = clone({...defaults, ...arg})
this.interceptors = {
request: new InterceptorManager(),
response: new InterceptorManager()
}
}
/**
* @Function
* @param {Request~setConfigCallback} f - 设置全局默认配置
*/
setConfig(f) {
this.config = f(this.config)
}
middleware(config) {
config = mergeConfig(this.config, config)
let chain = [dispatchRequest, undefined]
let promise = Promise.resolve(config)
this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
chain.unshift(interceptor.fulfilled, interceptor.rejected)
})
this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
chain.push(interceptor.fulfilled, interceptor.rejected)
})
while (chain.length) {
promise = promise.then(chain.shift(), chain.shift())
}
return promise
}
/**
* @Function
* @param {Object} config - 请求配置项
* @prop {String} options.url - 请求路径
* @prop {Object} options.data - 请求参数
* @prop {Object} [options.responseType = config.responseType] [text|arraybuffer] - 响应的数据类型
* @prop {Object} [options.dataType = config.dataType] - 如果设为 json,会尝试对返回的数据做一次 JSON.parse
* @prop {Object} [options.header = config.header] - 请求header
* @prop {Object} [options.method = config.method] - 请求方法
* @returns {Promise<unknown>}
*/
request(config = {}) {
return this.middleware(config)
}
get(url, options = {}) {
return this.middleware({
url,
method: 'GET',
...options
})
}
post(url, data, options = {}) {
return this.middleware({
url,
data,
method: 'POST',
...options
})
}
// #ifndef MP-ALIPAY || MP-KUAISHOU || MP-JD
put(url, data, options = {}) {
return this.middleware({
url,
data,
method: 'PUT',
...options
})
}
// #endif
// #ifdef APP-PLUS || H5 || MP-WEIXIN || MP-BAIDU
delete(url, data, options = {}) {
return this.middleware({
url,
data,
method: 'DELETE',
...options
})
}
// #endif
// #ifdef H5 || MP-WEIXIN
connect(url, data, options = {}) {
return this.middleware({
url,
data,
method: 'CONNECT',
...options
})
}
// #endif
// #ifdef H5 || MP-WEIXIN || MP-BAIDU
head(url, data, options = {}) {
return this.middleware({
url,
data,
method: 'HEAD',
...options
})
}
// #endif
// #ifdef APP-PLUS || H5 || MP-WEIXIN || MP-BAIDU
options(url, data, options = {}) {
return this.middleware({
url,
data,
method: 'OPTIONS',
...options
})
}
// #endif
// #ifdef H5 || MP-WEIXIN
trace(url, data, options = {}) {
return this.middleware({
url,
data,
method: 'TRACE',
...options
})
}
// #endif
upload(url, config = {}) {
config.url = url
config.method = 'UPLOAD'
return this.middleware(config)
}
download(url, config = {}) {
config.url = url
config.method = 'DOWNLOAD'
return this.middleware(config)
}
}
/**
* setConfig回调
* @return {Object} - 返回操作后的config
* @callback Request~setConfigCallback
* @param {Object} config - 全局默认config
*/
+20
View File
@@ -0,0 +1,20 @@
'use strict'
import isAbsoluteURL from '../helpers/isAbsoluteURL'
import combineURLs from '../helpers/combineURLs'
/**
* Creates a new URL by combining the baseURL with the requestedURL,
* only when the requestedURL is not already an absolute URL.
* If the requestURL is absolute, this function returns the requestedURL untouched.
*
* @param {string} baseURL The base URL
* @param {string} requestedURL Absolute or relative URL to combine
* @returns {string} The combined full path
*/
export default function buildFullPath(baseURL, requestedURL) {
if (baseURL && !isAbsoluteURL(requestedURL)) {
return combineURLs(baseURL, requestedURL)
}
return requestedURL
}
+31
View File
@@ -0,0 +1,31 @@
/**
* 默认的全局配置
*/
export default {
baseURL: '',
header: {},
method: 'GET',
dataType: 'json',
paramsSerializer: null,
// #ifndef MP-ALIPAY
responseType: 'text',
// #endif
custom: {},
// #ifdef H5 || APP-PLUS || MP-ALIPAY || MP-WEIXIN
timeout: 60000,
// #endif
// #ifdef APP-PLUS
sslVerify: true,
// #endif
// #ifdef H5
withCredentials: false,
// #endif
// #ifdef APP-PLUS
firstIpv4: false,
// #endif
validateStatus: function validateStatus(status) {
return status >= 200 && status < 300
}
}
@@ -0,0 +1,6 @@
import adapter from '../adapters/index'
export default (config) => {
return adapter(config)
}
+103
View File
@@ -0,0 +1,103 @@
import {deepMerge, isUndefined} from '../utils'
/**
* 合并局部配置优先的配置,如果局部有该配置项则用局部,如果全局有该配置项则用全局
* @param {Array} keys - 配置项
* @param {Object} globalsConfig - 当前的全局配置
* @param {Object} config2 - 局部配置
* @return {{}}
*/
const mergeKeys = (keys, globalsConfig, config2) => {
let config = {}
keys.forEach(prop => {
if (!isUndefined(config2[prop])) {
config[prop] = config2[prop]
} else if (!isUndefined(globalsConfig[prop])) {
config[prop] = globalsConfig[prop]
}
})
return config
}
/**
*
* @param globalsConfig - 当前实例的全局配置
* @param config2 - 当前的局部配置
* @return - 合并后的配置
*/
export default (globalsConfig, config2 = {}) => {
const method = config2.method || globalsConfig.method || 'GET'
let config = {
baseURL: config2.baseURL || globalsConfig.baseURL || '',
method: method,
url: config2.url || '',
params: config2.params || {},
custom: {...(globalsConfig.custom || {}), ...(config2.custom || {})},
header: deepMerge(globalsConfig.header || {}, config2.header || {})
}
const defaultToConfig2Keys = ['getTask', 'validateStatus', 'paramsSerializer']
config = {...config, ...mergeKeys(defaultToConfig2Keys, globalsConfig, config2)}
// eslint-disable-next-line no-empty
if (method === 'DOWNLOAD') {
// #ifdef H5 || APP-PLUS
if (!isUndefined(config2.timeout)) {
config['timeout'] = config2['timeout']
} else if (!isUndefined(globalsConfig.timeout)) {
config['timeout'] = globalsConfig['timeout']
}
// #endif
} else if (method === 'UPLOAD') {
delete config.header['content-type']
delete config.header['Content-Type']
const uploadKeys = [
// #ifdef APP-PLUS || H5
'files',
// #endif
// #ifdef MP-ALIPAY
'fileType',
// #endif
// #ifdef H5
'file',
// #endif
'filePath',
'name',
// #ifdef H5 || APP-PLUS
'timeout',
// #endif
'formData',
]
uploadKeys.forEach(prop => {
if (!isUndefined(config2[prop])) {
config[prop] = config2[prop]
}
})
// #ifdef H5 || APP-PLUS
if (isUndefined(config.timeout) && !isUndefined(globalsConfig.timeout)) {
config['timeout'] = globalsConfig['timeout']
}
// #endif
} else {
const defaultsKeys = [
'data',
// #ifdef H5 || APP-PLUS || MP-ALIPAY || MP-WEIXIN
'timeout',
// #endif
'dataType',
// #ifndef MP-ALIPAY
'responseType',
// #endif
// #ifdef APP-PLUS
'sslVerify',
// #endif
// #ifdef H5
'withCredentials',
// #endif
// #ifdef APP-PLUS
'firstIpv4',
// #endif
]
config = {...config, ...mergeKeys(defaultsKeys, globalsConfig, config2)}
}
return config
}
+16
View File
@@ -0,0 +1,16 @@
/**
* Resolve or reject a Promise based on response status.
*
* @param {Function} resolve A function that resolves the promise.
* @param {Function} reject A function that rejects the promise.
* @param {object} response The response.
*/
export default function settle(resolve, reject, response) {
const validateStatus = response.config.validateStatus
const status = response.statusCode
if (status && (!validateStatus || validateStatus(status))) {
resolve(response)
} else {
reject(response)
}
}
+64
View File
@@ -0,0 +1,64 @@
'use strict'
import * as utils from './../utils'
function encode(val) {
return encodeURIComponent(val).replace(/%40/gi, '@').replace(/%3A/gi, ':').replace(/%24/g, '$').replace(/%2C/gi, ',').replace(/%20/g, '+').replace(/%5B/gi, '[').replace(/%5D/gi, ']')
}
/**
* Build a URL by appending params to the end
*
* @param {string} url The base of the url (e.g., http://www.google.com)
* @param {object} [params] The params to be appended
* @returns {string} The formatted url
*/
export default function buildURL(url, params, paramsSerializer) {
/*eslint no-param-reassign:0*/
if (!params) {
return url
}
var serializedParams
if (paramsSerializer) {
serializedParams = paramsSerializer(params)
} else if (utils.isURLSearchParams(params)) {
serializedParams = params.toString()
} else {
var parts = []
utils.forEach(params, function serialize(val, key) {
if (val === null || typeof val === 'undefined') {
return
}
if (utils.isArray(val)) {
key = key + '[]'
} else {
val = [val]
}
utils.forEach(val, function parseValue(v) {
if (utils.isDate(v)) {
v = v.toISOString()
} else if (utils.isObject(v)) {
v = JSON.stringify(v)
}
parts.push(encode(key) + '=' + encode(v))
})
})
serializedParams = parts.join('&')
}
if (serializedParams) {
var hashmarkIndex = url.indexOf('#')
if (hashmarkIndex !== -1) {
url = url.slice(0, hashmarkIndex)
}
url += (url.indexOf('?') === -1 ? '?' : '&') + serializedParams
}
return url
}
+14
View File
@@ -0,0 +1,14 @@
'use strict'
/**
* Creates a new URL by combining the specified URLs
*
* @param {string} baseURL The base URL
* @param {string} relativeURL The relative URL
* @returns {string} The combined URL
*/
export default function combineURLs(baseURL, relativeURL) {
return relativeURL
? baseURL.replace(/\/+$/, '') + '/' + relativeURL.replace(/^\/+/, '')
: baseURL
}
@@ -0,0 +1,14 @@
'use strict'
/**
* Determines whether the specified URL is absolute
*
* @param {string} url The URL to test
* @returns {boolean} True if the specified URL is absolute, otherwise false
*/
export default function isAbsoluteURL(url) {
// A URL is considered absolute if it begins with "<scheme>://" or "//" (protocol-relative URL).
// RFC 3986 defines scheme name as a sequence of characters beginning with a letter and followed
// by any combination of letters, digits, plus, period, or hyphen.
return /^([a-z][a-z\d+\-.]*:)?\/\//i.test(url)
}
+120
View File
@@ -0,0 +1,120 @@
type AnyObject = Record<string | number | symbol, any>
type HttpPromise<T> = Promise<HttpResponse<T>>;
type Tasks = UniApp.RequestTask | UniApp.UploadTask | UniApp.DownloadTask
export interface RequestTask {
abort: () => void;
offHeadersReceived: () => void;
onHeadersReceived: () => void;
}
export interface HttpRequestConfig<T = Tasks> {
/** 请求基地址 */
baseURL?: string;
/** 请求服务器接口地址 */
url?: string;
/** 请求查询参数,自动拼接为查询字符串 */
params?: AnyObject;
/** 请求体参数 */
data?: AnyObject;
/** 文件对应的 key */
name?: string;
/** HTTP 请求中其他额外的 form data */
formData?: AnyObject;
/** 要上传文件资源的路径。 */
filePath?: string;
/** 需要上传的文件列表。使用 files 时,filePath 和 name 不生效,App、H5 2.6.15+ */
files?: Array<{
name?: string;
file?: File;
uri: string;
}>;
/** 要上传的文件对象,仅H5(2.6.15+)支持 */
file?: File;
/** 请求头信息 */
header?: AnyObject;
/** 请求方式 */
method?: "GET" | "POST" | "PUT" | "DELETE" | "CONNECT" | "HEAD" | "OPTIONS" | "TRACE" | "UPLOAD" | "DOWNLOAD";
/** 如果设为 json,会尝试对返回的数据做一次 JSON.parse */
dataType?: string;
/** 设置响应的数据类型,支付宝小程序不支持 */
responseType?: "text" | "arraybuffer";
/** 自定义参数 */
custom?: AnyObject;
/** 超时时间,仅微信小程序(2.10.0)、支付宝小程序支持 */
timeout?: number;
/** DNS解析时优先使用ipv4,仅 App-Android 支持 (HBuilderX 2.8.0+) */
firstIpv4?: boolean;
/** 验证 ssl 证书 仅5+App安卓端支持(HBuilderX 2.3.3+ */
sslVerify?: boolean;
/** 跨域请求时是否携带凭证(cookies)仅H5支持(HBuilderX 2.6.15+ */
withCredentials?: boolean;
/** 返回当前请求的task, options。请勿在此处修改options。 */
getTask?: (task: T, options: HttpRequestConfig<T>) => void;
/** 全局自定义验证器 */
validateStatus?: (statusCode: number) => boolean | void;
/** params 参数自定义处理 */
paramsSerializer?: (params: AnyObject) => string | void;
}
export interface HttpResponse<T = any> {
config: HttpRequestConfig;
statusCode: number;
cookies: Array<string>;
data: T;
errMsg: string;
header: AnyObject;
rawData: T;
}
export interface HttpUploadResponse<T = any> {
config: HttpRequestConfig;
statusCode: number;
data: T;
errMsg: string;
rawData: T;
}
export interface HttpDownloadResponse extends HttpResponse {
tempFilePath: string;
}
export interface HttpError {
config: HttpRequestConfig;
statusCode?: number;
cookies?: Array<string>;
data?: any;
errMsg: string;
header?: AnyObject;
}
export interface HttpInterceptorManager<V, E = V> {
use(
onFulfilled?: (config: V) => Promise<V> | V,
onRejected?: (config: E) => Promise<E> | E
): void;
eject(id: number): void;
}
export abstract class HttpRequestAbstract {
constructor(config?: HttpRequestConfig);
config: HttpRequestConfig;
interceptors: {
request: HttpInterceptorManager<HttpRequestConfig, HttpRequestConfig>;
response: HttpInterceptorManager<HttpResponse, HttpError>;
}
middleware<T = any>(config: HttpRequestConfig): HttpPromise<T>;
request<T = any>(config: HttpRequestConfig<UniApp.RequestTask>): HttpPromise<T>;
get<T = any>(url: string, config?: HttpRequestConfig<UniApp.RequestTask>): HttpPromise<T>;
upload<T = any>(url: string, config?: HttpRequestConfig<UniApp.UploadTask>): HttpPromise<T>;
delete<T = any>(url: string, data?: AnyObject, config?: HttpRequestConfig<UniApp.RequestTask>): HttpPromise<T>;
head<T = any>(url: string, data?: AnyObject, config?: HttpRequestConfig<UniApp.RequestTask>): HttpPromise<T>;
post<T = any>(url: string, data?: AnyObject, config?: HttpRequestConfig<UniApp.RequestTask>): HttpPromise<T>;
put<T = any>(url: string, data?: AnyObject, config?: HttpRequestConfig<UniApp.RequestTask>): HttpPromise<T>;
connect<T = any>(url: string, data?: AnyObject, config?: HttpRequestConfig<UniApp.RequestTask>): HttpPromise<T>;
options<T = any>(url: string, data?: AnyObject, config?: HttpRequestConfig<UniApp.RequestTask>): HttpPromise<T>;
trace<T = any>(url: string, data?: AnyObject, config?: HttpRequestConfig<UniApp.RequestTask>): HttpPromise<T>;
download(url: string, config?: HttpRequestConfig<UniApp.DownloadTask>): Promise<HttpDownloadResponse>;
setConfig(onSend: (config: HttpRequestConfig) => HttpRequestConfig): void;
}
declare class HttpRequest extends HttpRequestAbstract { }
export default HttpRequest;
+2
View File
@@ -0,0 +1,2 @@
import Request from './core/Request'
export default Request
+135
View File
@@ -0,0 +1,135 @@
'use strict'
// utils is a library of generic helper functions non-specific to axios
var toString = Object.prototype.toString
/**
* Determine if a value is an Array
*
* @param {Object} val The value to test
* @returns {boolean} True if value is an Array, otherwise false
*/
export function isArray (val) {
return toString.call(val) === '[object Array]'
}
/**
* Determine if a value is an Object
*
* @param {Object} val The value to test
* @returns {boolean} True if value is an Object, otherwise false
*/
export function isObject (val) {
return val !== null && typeof val === 'object'
}
/**
* Determine if a value is a Date
*
* @param {Object} val The value to test
* @returns {boolean} True if value is a Date, otherwise false
*/
export function isDate (val) {
return toString.call(val) === '[object Date]'
}
/**
* Determine if a value is a URLSearchParams object
*
* @param {Object} val The value to test
* @returns {boolean} True if value is a URLSearchParams object, otherwise false
*/
export function isURLSearchParams (val) {
return typeof URLSearchParams !== 'undefined' && val instanceof URLSearchParams
}
/**
* Iterate over an Array or an Object invoking a function for each item.
*
* If `obj` is an Array callback will be called passing
* the value, index, and complete array for each item.
*
* If 'obj' is an Object callback will be called passing
* the value, key, and complete object for each property.
*
* @param {Object|Array} obj The object to iterate
* @param {Function} fn The callback to invoke for each item
*/
export function forEach (obj, fn) {
// Don't bother if no value provided
if (obj === null || typeof obj === 'undefined') {
return
}
// Force an array if not already something iterable
if (typeof obj !== 'object') {
/*eslint no-param-reassign:0*/
obj = [obj]
}
if (isArray(obj)) {
// Iterate over array values
for (var i = 0, l = obj.length; i < l; i++) {
fn.call(null, obj[i], i, obj)
}
} else {
// Iterate over object keys
for (var key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
fn.call(null, obj[key], key, obj)
}
}
}
}
/**
* 是否为boolean 值
* @param val
* @returns {boolean}
*/
export function isBoolean(val) {
return typeof val === 'boolean'
}
/**
* 是否为真正的对象{} new Object
* @param {any} obj - 检测的对象
* @returns {boolean}
*/
export function isPlainObject(obj) {
return Object.prototype.toString.call(obj) === '[object Object]'
}
/**
* Function equal to merge with the difference being that no reference
* to original objects is kept.
*
* @see merge
* @param {Object} obj1 Object to merge
* @returns {Object} Result of all merge properties
*/
export function deepMerge(/* obj1, obj2, obj3, ... */) {
let result = {}
function assignValue(val, key) {
if (typeof result[key] === 'object' && typeof val === 'object') {
result[key] = deepMerge(result[key], val)
} else if (typeof val === 'object') {
result[key] = deepMerge({}, val)
} else {
result[key] = val
}
}
for (let i = 0, l = arguments.length; i < l; i++) {
forEach(arguments[i], assignValue)
}
return result
}
export function isUndefined (val) {
return typeof val === 'undefined'
}
+264
View File
@@ -0,0 +1,264 @@
/* eslint-disable */
var clone = (function() {
'use strict';
function _instanceof(obj, type) {
return type != null && obj instanceof type;
}
var nativeMap;
try {
nativeMap = Map;
} catch(_) {
// maybe a reference error because no `Map`. Give it a dummy value that no
// value will ever be an instanceof.
nativeMap = function() {};
}
var nativeSet;
try {
nativeSet = Set;
} catch(_) {
nativeSet = function() {};
}
var nativePromise;
try {
nativePromise = Promise;
} catch(_) {
nativePromise = function() {};
}
/**
* Clones (copies) an Object using deep copying.
*
* This function supports circular references by default, but if you are certain
* there are no circular references in your object, you can save some CPU time
* by calling clone(obj, false).
*
* Caution: if `circular` is false and `parent` contains circular references,
* your program may enter an infinite loop and crash.
*
* @param `parent` - the object to be cloned
* @param `circular` - set to true if the object to be cloned may contain
* circular references. (optional - true by default)
* @param `depth` - set to a number if the object is only to be cloned to
* a particular depth. (optional - defaults to Infinity)
* @param `prototype` - sets the prototype to be used when cloning an object.
* (optional - defaults to parent prototype).
* @param `includeNonEnumerable` - set to true if the non-enumerable properties
* should be cloned as well. Non-enumerable properties on the prototype
* chain will be ignored. (optional - false by default)
*/
function clone(parent, circular, depth, prototype, includeNonEnumerable) {
if (typeof circular === 'object') {
depth = circular.depth;
prototype = circular.prototype;
includeNonEnumerable = circular.includeNonEnumerable;
circular = circular.circular;
}
// maintain two arrays for circular references, where corresponding parents
// and children have the same index
var allParents = [];
var allChildren = [];
var useBuffer = typeof Buffer != 'undefined';
if (typeof circular == 'undefined')
circular = true;
if (typeof depth == 'undefined')
depth = Infinity;
// recurse this function so we don't reset allParents and allChildren
function _clone(parent, depth) {
// cloning null always returns null
if (parent === null)
return null;
if (depth === 0)
return parent;
var child;
var proto;
if (typeof parent != 'object') {
return parent;
}
if (_instanceof(parent, nativeMap)) {
child = new nativeMap();
} else if (_instanceof(parent, nativeSet)) {
child = new nativeSet();
} else if (_instanceof(parent, nativePromise)) {
child = new nativePromise(function (resolve, reject) {
parent.then(function(value) {
resolve(_clone(value, depth - 1));
}, function(err) {
reject(_clone(err, depth - 1));
});
});
} else if (clone.__isArray(parent)) {
child = [];
} else if (clone.__isRegExp(parent)) {
child = new RegExp(parent.source, __getRegExpFlags(parent));
if (parent.lastIndex) child.lastIndex = parent.lastIndex;
} else if (clone.__isDate(parent)) {
child = new Date(parent.getTime());
} else if (useBuffer && Buffer.isBuffer(parent)) {
if (Buffer.from) {
// Node.js >= 5.10.0
child = Buffer.from(parent);
} else {
// Older Node.js versions
child = new Buffer(parent.length);
parent.copy(child);
}
return child;
} else if (_instanceof(parent, Error)) {
child = Object.create(parent);
} else {
if (typeof prototype == 'undefined') {
proto = Object.getPrototypeOf(parent);
child = Object.create(proto);
}
else {
child = Object.create(prototype);
proto = prototype;
}
}
if (circular) {
var index = allParents.indexOf(parent);
if (index != -1) {
return allChildren[index];
}
allParents.push(parent);
allChildren.push(child);
}
if (_instanceof(parent, nativeMap)) {
parent.forEach(function(value, key) {
var keyChild = _clone(key, depth - 1);
var valueChild = _clone(value, depth - 1);
child.set(keyChild, valueChild);
});
}
if (_instanceof(parent, nativeSet)) {
parent.forEach(function(value) {
var entryChild = _clone(value, depth - 1);
child.add(entryChild);
});
}
for (var i in parent) {
var attrs = Object.getOwnPropertyDescriptor(parent, i);
if (attrs) {
child[i] = _clone(parent[i], depth - 1);
}
try {
var objProperty = Object.getOwnPropertyDescriptor(parent, i);
if (objProperty.set === 'undefined') {
// no setter defined. Skip cloning this property
continue;
}
child[i] = _clone(parent[i], depth - 1);
} catch(e){
if (e instanceof TypeError) {
// when in strict mode, TypeError will be thrown if child[i] property only has a getter
// we can't do anything about this, other than inform the user that this property cannot be set.
continue
} else if (e instanceof ReferenceError) {
//this may happen in non strict mode
continue
}
}
}
if (Object.getOwnPropertySymbols) {
var symbols = Object.getOwnPropertySymbols(parent);
for (var i = 0; i < symbols.length; i++) {
// Don't need to worry about cloning a symbol because it is a primitive,
// like a number or string.
var symbol = symbols[i];
var descriptor = Object.getOwnPropertyDescriptor(parent, symbol);
if (descriptor && !descriptor.enumerable && !includeNonEnumerable) {
continue;
}
child[symbol] = _clone(parent[symbol], depth - 1);
Object.defineProperty(child, symbol, descriptor);
}
}
if (includeNonEnumerable) {
var allPropertyNames = Object.getOwnPropertyNames(parent);
for (var i = 0; i < allPropertyNames.length; i++) {
var propertyName = allPropertyNames[i];
var descriptor = Object.getOwnPropertyDescriptor(parent, propertyName);
if (descriptor && descriptor.enumerable) {
continue;
}
child[propertyName] = _clone(parent[propertyName], depth - 1);
Object.defineProperty(child, propertyName, descriptor);
}
}
return child;
}
return _clone(parent, depth);
}
/**
* Simple flat clone using prototype, accepts only objects, usefull for property
* override on FLAT configuration object (no nested props).
*
* USE WITH CAUTION! This may not behave as you wish if you do not know how this
* works.
*/
clone.clonePrototype = function clonePrototype(parent) {
if (parent === null)
return null;
var c = function () {};
c.prototype = parent;
return new c();
};
// private utility functions
function __objToStr(o) {
return Object.prototype.toString.call(o);
}
clone.__objToStr = __objToStr;
function __isDate(o) {
return typeof o === 'object' && __objToStr(o) === '[object Date]';
}
clone.__isDate = __isDate;
function __isArray(o) {
return typeof o === 'object' && __objToStr(o) === '[object Array]';
}
clone.__isArray = __isArray;
function __isRegExp(o) {
return typeof o === 'object' && __objToStr(o) === '[object RegExp]';
}
clone.__isRegExp = __isRegExp;
function __getRegExpFlags(re) {
var flags = '';
if (re.global) flags += 'g';
if (re.ignoreCase) flags += 'i';
if (re.multiline) flags += 'm';
return flags;
}
clone.__getRegExpFlags = __getRegExpFlags;
return clone;
})();
export default clone
+73 -37
View File
@@ -1,38 +1,61 @@
import Fly from "flyio/dist/npm/wx";
import {handleLoginFailure} from "@/utils";
import {VUE_APP_API_URL} from "@/config";
import store from "@/store/index.js";
import Request from "@/utils/luch-request/index.js";
import cookie from "@/utils/store/cookie";
import { handleLoginFailure } from "@/utils";
import { VUE_APP_API_URL } from "@/config";
const fly = new Fly()
fly.config.baseURL = VUE_APP_API_URL
const http = new Request();
/* 设置全局配置 */
http.setConfig((config) => {
config.baseURL = VUE_APP_API_URL;
config.timeout = 2 * 60 * 1000;
config.method = "POST";
config.responseType = "text";
config.header = {
...config.header,
};
return config;
});
fly.interceptors.response.use(
response => {
// 定时刷新access-token
return response;
},
error => {
if (error.toString() == 'Error: Network Error') {
console.log('————————')
console.log('发送请求失败', error)
console.log('————————')
handleLoginFailure();
return Promise.reject({msg: "未登录", toLogin: true});
} else if (error.status == 401) {
console.log('————————')
console.log('登录失效 401', error)
console.log('————————')
handleLoginFailure();
return Promise.reject({msg: "未登录", toLogin: true});
} else {
uni.showToast({
title: '服务开小差了,请稍后再试',
icon: 'none',
duration: 3000
})
}
return Promise.reject(error);
// /* 请求之前拦截器 */
http.interceptors.request.use((config, cancel) => {
const token = cookie.get('login_status');
config.header = {
...config.header,
Authorization: token ? ('Bearer ' + token) : ''
}
return config;
});
// 必须使用异步函数,注意
http.interceptors.response.use(
(response) => {
return response;
},
(error) => {
console.log(error)
if (error.toString() == "Error: Network Error") {
console.log("————————");
console.log("发送请求失败", error);
console.log("————————");
handleLoginFailure();
return Promise.reject({ msg: "未登录", toLogin: true });
} else if (error.status == 401) {
console.log("————————");
console.log("登录失效 401", error);
console.log("————————");
handleLoginFailure();
return Promise.reject({ msg: "未登录", toLogin: true });
} else {
uni.showToast({
title: "服务开小差了,请稍后再试",
icon: "none",
duration: 3000,
});
}
return Promise.reject(error);
}
);
const defaultOpt = {login: true};
@@ -51,16 +74,29 @@ function baseRequest(options) {
// 结构请求需要的参数
const {url, params, data, login, ...option} = options
console.log(params, '=====params');
console.log(params, '=====data');
console.log(option, '=======option');
let _params = {}
if(['get', 'GET'].includes(options.method)) {
_params = {
params: params
}
} else if (['post', 'POST'].includes(options.method)) {
_params = params || data
}
// 发起请求
return fly.request(url, params || data, {
...option
}).then(res => {
return http[options.method](url, _params).then(res => {
const resData = res.data || {};
if (res.status !== 200) {
if (res.statusCode !== 200) {
return Promise.reject({msg: "请求失败", res, resData});
}
if ([401, 403].indexOf(resData.status) !== -1) {
if (resData.status === 401) {
handleLoginFailure();
return Promise.reject({msg: '未登录', res, resData, toLogin: true});
}
if ([ 403].indexOf(resData.status) !== -1) {
handleLoginFailure();
return Promise.reject({msg: res.data.msg, res, resData, toLogin: true});
} else if (resData.status === 200) {
@@ -117,4 +153,4 @@ const request = ["post", "put", "patch"].reduce((request, method) => {
};
});
export default request;
export default request
+123
View File
@@ -0,0 +1,123 @@
import Fly from "flyio/dist/npm/wx";
import {handleLoginFailure} from "@/utils";
import {VUE_APP_API_URL} from "@/config";
import cookie from "@/utils/store/cookie";
const fly = new Fly()
fly.config.baseURL = VUE_APP_API_URL
fly.interceptors.response.use(
response => {
// 定时刷新access-token
console.log(response);
return response;
},
error => {
if (error.toString() == 'Error: Network Error') {
console.log('————————')
console.log('发送请求失败', error)
console.log('————————')
handleLoginFailure();
return Promise.reject({msg: "未登录", toLogin: true});
} else if (error.status == 401) {
console.log('————————')
console.log('登录失效 401', error)
console.log('————————')
handleLoginFailure();
return Promise.reject({msg: "未登录", toLogin: true});
} else {
uni.showToast({
title: '服务开小差了,请稍后再试',
icon: 'none',
duration: 3000
})
}
return Promise.reject(error);
}
);
const defaultOpt = {login: true};
function baseRequest(options) {
// 从缓存中获取 token 防止 token 失效后还会继续请求的情况
const token = cookie.get('login_status');
// 合并传参过来的 headers
// 如果接口需要登录,携带 token 去请求
options.headers = {
...options.headers,
Authorization: token ? ('Bearer ' + token) : ''
}
// 结构请求需要的参数
const {url, params, data, login, ...option} = options
console.log(params, '=====params');
console.log(params, '=====data');
console.log(option, '=======option');
// 发起请求
return fly.request(url, params || data, {
...option
}).then(res => {
const resData = res.data || {};
if (res.status !== 200) {
return Promise.reject({msg: "请求失败", res, resData});
}
if ([401, 403].indexOf(resData.status) !== -1) {
handleLoginFailure();
return Promise.reject({msg: res.data.msg, res, resData, toLogin: true});
} else if (resData.status === 200) {
return Promise.resolve(resData, res);
} else {
// 埋点数据推送、获取店铺分享图片
if (url.indexOf('api/systemStats/push') < 0 && url.indexOf('api/hotelMain/shareImage') < 0) {
uni.showToast({
title: resData.msg,
icon: 'none',
duration: 3000,
success() {
}
})
}
return Promise.reject({msg: resData.msg, res, data});
}
});
}
/**
* http 请求基础类
* 参考文档 https://www.kancloud.cn/yunye/axios/234845
*
*/
const request = ["post", "put", "patch"].reduce((request, method) => {
/**
*
* @param url string 接口地址
* @param data object get参数
* @param options object axios 配置项
* @returns {AxiosPromise}
*/
request[method] = (url, data = {}, options = {}) => {
return baseRequest(
Object.assign({url, data, method}, defaultOpt, options)
);
};
return request;
}, {});
["get", "delete", "head"].forEach(method => {
/**
*
* @param url string 接口地址
* @param params object get参数
* @param options object axios 配置项
* @returns {AxiosPromise}
*/
request[method] = (url, params = {}, options = {}) => {
return baseRequest(
Object.assign({url, params, method}, defaultOpt, options)
);
};
});
export default request;
+2 -2
View File
@@ -33,8 +33,8 @@ function clearAll() {
// uni.clearStorage()
const res = uni.getStorageInfoSync()
res.keys.map((item) => {
// 不要清除spread和redirect、页面埋点数据
if ([pageViewDataCacheKey, 'redirect', 'spread'].includes(item)) {
// 不要清除spread和redirect、页面埋点和历史搜索数据数据,
if ([pageViewDataCacheKey, 'redirect', 'spread', 'historyKey'].includes(item)) {
return
}
remove(item)