mirror of
https://gitee.com/niucloud-team/niucloud-admin.git
synced 2026-01-26 04:58:10 +00:00
up
This commit is contained in:
parent
62e22e626a
commit
5457b86e0a
@ -118,7 +118,20 @@
|
||||
if (memberInfo && memberInfo.wx_openid) {
|
||||
uni.setStorageSync('openid', memberInfo.wx_openid)
|
||||
} else {
|
||||
data.query.code ? login.updateOpenid(data.query.code) : login.getAuthCode({ scopes: 'snsapi_userinfo' })
|
||||
if (data.query.code) {
|
||||
// 检测身份是否合法(当前登录的账号是不是我的),openid有效后才能更新登录
|
||||
login.updateOpenid(data.query.code, () => {
|
||||
login.authLogin({ code: data.query.code })
|
||||
})
|
||||
} else {
|
||||
if (loginConfig.is_force_access_user_info) {
|
||||
// 强制获取用户信息
|
||||
login.getAuthCode({ scopes: 'snsapi_userinfo' })
|
||||
} else {
|
||||
// 静默获取
|
||||
login.getAuthCode({ scopes: 'snsapi_base' })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// #endif
|
||||
@ -160,12 +173,22 @@
|
||||
if (uni.getStorageSync('autoLoginLock') && !uni.getStorageSync('wechat_login_back')) return;
|
||||
if (loginConfig.is_auth_register || uni.getStorageSync('wechat_login_back')) {
|
||||
uni.removeStorageSync('wechat_login_back') // 删除微信公众号手动授权登录回调标识
|
||||
data.query.code ? login.authLogin({ code: data.query.code }) : login.getAuthCode({ scopes: 'snsapi_userinfo' })
|
||||
if (data.query.code) {
|
||||
login.authLogin({ code: data.query.code })
|
||||
} else {
|
||||
if (loginConfig.is_force_access_user_info) {
|
||||
// 强制获取用户信息
|
||||
login.getAuthCode({ scopes: 'snsapi_userinfo' })
|
||||
} else {
|
||||
// 静默获取
|
||||
login.getAuthCode({ scopes: 'snsapi_base' })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// #endif
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
|
||||
@ -49,6 +49,9 @@
|
||||
<template v-if="component.componentName == 'CarouselSearch'">
|
||||
<diy-carousel-search :scrollBool="diyGroup.componentsScrollBool.CarouselSearch" :component="component" :global="data.global" :index="index" :pullDownRefreshCount="props.pullDownRefreshCount" />
|
||||
</template>
|
||||
<template v-if="component.componentName == 'PictureShow'">
|
||||
<diy-picture-show :component="component" :global="data.global" :index="index" :pullDownRefreshCount="props.pullDownRefreshCount" />
|
||||
</template>
|
||||
</view>
|
||||
</view>
|
||||
<template v-if="diyStore.mode == '' && data.global.bottomTabBarSwitch">
|
||||
@ -91,4 +94,4 @@
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@import './index.scss';
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@ -3,7 +3,6 @@ import Sortable from 'sortablejs';
|
||||
import { range } from 'lodash-es';
|
||||
import { onPageScroll, onHide, onShow } from '@dcloudio/uni-app';
|
||||
import useDiyStore from '@/app/stores/diy';
|
||||
import { getLocation } from '@/utils/common';
|
||||
|
||||
export function useDiyGroup(params: any = {}) {
|
||||
|
||||
@ -89,11 +88,7 @@ export function useDiyGroup(params: any = {}) {
|
||||
|
||||
nextTick(() => {
|
||||
setTimeout(() => {
|
||||
if (data.value.global && data.value.global.topStatusBar && data.value.global.topStatusBar.style == 'style-4') {
|
||||
// 第一次获取经纬度
|
||||
getLocation()
|
||||
}
|
||||
|
||||
|
||||
// 初始化组件滚动值
|
||||
scrollVal = uni.getStorageSync('componentsScrollValGroup');
|
||||
if (scrollVal) {
|
||||
|
||||
@ -4,6 +4,9 @@ import request from '@/utils/request'
|
||||
* 用户名登录
|
||||
*/
|
||||
export function usernameLogin(data : AnyObject) {
|
||||
if(uni.getStorageSync('pid')){
|
||||
data.pid = uni.getStorageSync('pid');
|
||||
}
|
||||
return request.get('login', data, { showErrorMessage: true })
|
||||
}
|
||||
|
||||
@ -11,14 +14,17 @@ export function usernameLogin(data : AnyObject) {
|
||||
* 手机验证码登录
|
||||
*/
|
||||
export function mobileLogin(data : AnyObject) {
|
||||
if(uni.getStorageSync('pid')){
|
||||
data.pid = uni.getStorageSync('pid');
|
||||
}
|
||||
return request.post('login/mobile', data, { showErrorMessage: true })
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取登录配置
|
||||
*/
|
||||
export function getConfig() {
|
||||
return request.get('login/config')
|
||||
export function getConfig(params: Record<string, any>) {
|
||||
return request.get('login/config', params)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -47,6 +47,9 @@ export function modifyMember(data : AnyObject) {
|
||||
* 登录会员绑定手机号
|
||||
*/
|
||||
export function bindMobile(data : AnyObject) {
|
||||
if(uni.getStorageSync('pid')){
|
||||
data.pid = uni.getStorageSync('pid');
|
||||
}
|
||||
return request.put('member/mobile', data, { showErrorMessage: true })
|
||||
}
|
||||
|
||||
|
||||
@ -45,7 +45,7 @@ export function sendSms(data: AnyObject) {
|
||||
/**
|
||||
* 获取微信jssdk config
|
||||
*/
|
||||
export function getWechatSkdConfig(data: AnyObject) {
|
||||
export function getWechatSdkConfig(data: AnyObject) {
|
||||
return request.get('wechat/jssdkconfig', data, { showErrorMessage: false })
|
||||
}
|
||||
|
||||
@ -142,6 +142,6 @@ export function getMsgJumpPath(params: Record<string, any>) {
|
||||
/**
|
||||
* 获取初始化数据信息
|
||||
*/
|
||||
export function getInitInfo() {
|
||||
return request.get('init')
|
||||
export function getInitInfo(params: Record<string, any>) {
|
||||
return request.get('init', params)
|
||||
}
|
||||
@ -3,7 +3,7 @@
|
||||
<view :style="maskLayer"></view>
|
||||
|
||||
<view class="diy-active-cube relative">
|
||||
<view class="active-cube-wrap p-[20rpx] pb-[24rpx]">
|
||||
<view class="active-cube-wrap pt-[28rpx] px-[20rpx] pb-[24rpx]">
|
||||
<view class="flex items-center" v-if="diyComponent.titleStyle.value == 'style-1'">
|
||||
<view class="mr-[10rpx] font-500 text-[30rpx]" :style="{color: diyComponent.titleColor }" @click="diyStore.toRedirect(diyComponent.textLink)">{{ diyComponent.text }}</view>
|
||||
<view v-if="diyComponent.subTitle.text" @click="diyStore.toRedirect(diyComponent.subTitle.link)" class="text-center text-[22rpx] rounded-[40rpx] rounded-tl-[10rpx] py-[6rpx] px-[14rpx]" :style="{'color': diyComponent.subTitle.textColor, background: 'linear-gradient(90deg, '+ diyComponent.subTitle.startColor + ', '+ diyComponent.subTitle.endColor + ')'}">{{ diyComponent.subTitle.text }}</view>
|
||||
@ -27,10 +27,17 @@
|
||||
<text class="nc-iconfont nc-icon-youV6xx !text-[26rpx]"></text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="flex items-center" v-if="diyComponent.titleStyle.value == 'style-5'">
|
||||
<view class="h-[32rpx] flex items-center" v-if="diyComponent.textImg" @click="diyStore.toRedirect(diyComponent.textLink)">
|
||||
<image class="h-[100%] w-[auto]" :src="img(diyComponent.textImg)" mode="heightFix" />
|
||||
</view>
|
||||
<view v-if="diyComponent.subTitle.text && diyComponent.textImg" class="mx-[16rpx] w-[2rpx] h-[24rpx]" :style="{'background': diyComponent.subTitle.textColor}"></view>
|
||||
<view v-if="diyComponent.subTitle.text" @click="diyStore.toRedirect(diyComponent.subTitle.link)" class="text-center text-[22rpx] py-[6rpx]" :style="{'color': diyComponent.subTitle.textColor, background: 'linear-gradient(90deg, '+ diyComponent.subTitle.startColor + ', '+ diyComponent.subTitle.endColor + ')'}">{{ diyComponent.subTitle.text }}</view>
|
||||
</view>
|
||||
|
||||
<view class="bd flex flex-wrap justify-between">
|
||||
<template v-for="item in diyComponent.list" :key="item.id">
|
||||
<view v-if="diyComponent.blockStyle.value == 'style-1'" @click="diyStore.toRedirect(item.link)" class="item flex justify-between px-[20rpx] py-[30rpx] bg-white mt-[20rpx] rounded-[var(--rounded-mid)]" :style="{ backgroundColor : diyComponent.elementBgColor }">
|
||||
<view v-if="diyComponent.blockStyle.value == 'style-1'" @click="diyStore.toRedirect(item.link)" class="item flex justify-between px-[20rpx] py-[30rpx] bg-white mt-[20rpx] rounded-[var(--rounded-mid)]" :style="commonTempCss(item)">
|
||||
<view class="flex-1 flex items-baseline flex-col">
|
||||
<view class="text-[28rpx] pb-[10rpx] text-[#333]" :style="{ fontWeight : diyComponent.blockStyle.fontWeight }">{{ item.title.text }}</view>
|
||||
<view class="text-[22rpx] text-[#999] pb-[30rpx]">{{ item.subTitle.text }}</view>
|
||||
@ -47,12 +54,13 @@
|
||||
<u-icon name="photo" color="#999" size="50"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="diyComponent.blockStyle.value == 'style-2'" @click="diyStore.toRedirect(item.link)" class="item flex justify-between p-[20rpx] bg-white mt-[20rpx] rounded-[var(--rounded-mid)]" :style="{ backgroundColor : diyComponent.elementBgColor }">
|
||||
|
||||
<view v-if="diyComponent.blockStyle.value == 'style-2'" @click="diyStore.toRedirect(item.link)" class="item h-[150rpx] flex justify-between p-[20rpx] bg-white mt-[20rpx] rounded-[var(--rounded-mid)]" :style="commonTempCss(item)">
|
||||
<view class="flex-1 flex items-baseline flex-col">
|
||||
<view class="text-[26rpx] pb-[20rpx]" :style="{ fontWeight : diyComponent.blockStyle.fontWeight }">{{ item.title.text }}</view>
|
||||
<view class="text-[22rpx] text-gray-500 pb-[20rpx]">{{ item.subTitle.text }}</view>
|
||||
<view class="text-[26rpx] mt-[10rpx] pb-[16rpx]" :style="{ fontWeight : diyComponent.blockStyle.fontWeight }">{{ item.title.text }}</view>
|
||||
<view class="text-[22rpx] text-gray-500 pb-[26rpx]">{{ item.subTitle.text }}</view>
|
||||
<view class="link relative text-[22rpx] leading-[40rpx] flex items-center text-white rounded-[20rpx] h-[40rpx] pl-[20rpx] pr-[10rpx]" :style="btnCss(item.moreTitle)" v-if="item.moreTitle.text">
|
||||
<text class="mr-[8rpx]">{{ item.moreTitle.text }}</text>
|
||||
<text class="mr-[8rpx]" :class="{'italic': diyComponent.blockStyle.btnText == 'italics'}">{{ item.moreTitle.text }}</text>
|
||||
<text class="iconfont iconjiantou-you-cuxiantiao-fill !text-[20rpx] text-[#fff]"></text>
|
||||
</view>
|
||||
</view>
|
||||
@ -67,7 +75,7 @@
|
||||
</view>
|
||||
<scroll-view :scroll-x="true" class="whitespace-nowrap" :id="'warpStyle3-'+diyComponent.id" v-if="diyComponent.blockStyle.value == 'style-3'">
|
||||
<view v-for="(item,index) in diyComponent.list" :key="item.id" class="inline-flex">
|
||||
<view :id="'item'+index+diyComponent.id" @click="diyStore.toRedirect(item.link)" class="flex flex-col items-center justify-between p-[10rpx] bg-white mt-[20rpx] w-[157rpx] h-[200rpx] rounded-[var(--rounded-mid)] box-border" :style="itemStyle3" :class="{'!mr-[0rpx]': index+1 === diyComponent.list.length}">
|
||||
<view :id="'item'+index+diyComponent.id" @click="diyStore.toRedirect(item.link)" class="flex flex-col items-center justify-between p-[10rpx] bg-white mt-[20rpx] w-[157rpx] h-[200rpx] rounded-[var(--rounded-mid)] box-border" :style="itemStyle3 + commonTempCss(item)" :class="{'!mr-[0rpx]': index+1 === diyComponent.list.length}">
|
||||
<view class="w-[141rpx] h-[141rpx] rounded-[var(--rounded-small)] overflow-hidden" v-if="item.imageUrl">
|
||||
<image class="w-[141rpx] h-[141rpx]" :src="img(item.imageUrl)" mode="aspectFit" />
|
||||
</view>
|
||||
@ -83,7 +91,7 @@
|
||||
|
||||
<scroll-view scroll-x="true" class="whitespace-nowrap" :id="'warpStyle4-'+diyComponent.id" v-if="diyComponent.blockStyle.value == 'style-4'">
|
||||
<view v-for="(item,index) in diyComponent.list" :key="item.id" class="inline-flex">
|
||||
<view :id="'item'+index+diyComponent.id" @click="diyStore.toRedirect(item.link)" class="flex flex-col items-center justify-between p-[4rpx] bg-[#F93D02] mt-[20rpx] rounded-[var(--rounded-mid)] box-border" :class="{'!mr-[0rpx]': index+1 === diyComponent.list.length}" :style="'background :linear-gradient('+ item.listFrame.startColor +','+ item.listFrame.endColor + ');'+itemStyle4">
|
||||
<view :id="'item'+index+diyComponent.id" @click="diyStore.toRedirect(item.link)" class="flex flex-col items-center justify-between p-[4rpx] bg-[#F93D02] mt-[20rpx] rounded-[var(--rounded-mid)] box-border" :class="{'!mr-[0rpx]': index+1 === diyComponent.list.length}" :style="commonTempCss(item) + itemStyle4">
|
||||
<view class="w-[149rpx] h-[149rpx] box-border px-[18rpx] pt-[16rpx] pb-[6rpx] bg-[#fff] flex flex-col items-center rounded-[var(--rounded-small)]">
|
||||
<view class="w-[112rpx] h-[102rpx]" v-if="item.imageUrl">
|
||||
<image class="w-[112rpx] h-[102rpx]" :src="img(item.imageUrl)" mode="aspectFit" />
|
||||
@ -188,6 +196,18 @@
|
||||
itemStyle4.value= 'margin-right:14rpx;'
|
||||
// #endif
|
||||
};
|
||||
|
||||
// 公共模块颜色
|
||||
const commonTempCss = (data: any)=>{
|
||||
var style = '';
|
||||
if(data.listFrame.startColor && data.listFrame.endColor){
|
||||
style += `background:linear-gradient(${data.listFrame.startColor},${data.listFrame.endColor});`;
|
||||
}else{
|
||||
style += `background:${data.listFrame.startColor || data.listFrame.endColor};`;
|
||||
}
|
||||
return style;
|
||||
}
|
||||
|
||||
const btnCss = (item:any) => {
|
||||
var style = '';
|
||||
style += `background:linear-gradient(90deg,${item.startColor},${item.endColor});`;
|
||||
@ -202,18 +222,25 @@
|
||||
)
|
||||
|
||||
onMounted(() => {
|
||||
refresh();
|
||||
// 装修模式下刷新
|
||||
if (diyStore.mode == 'decorate') {
|
||||
watch(
|
||||
() => diyComponent.value,
|
||||
(newValue, oldValue) => {
|
||||
if (newValue && newValue.componentName == 'ActiveCube') {
|
||||
refresh();
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
refresh();
|
||||
// 装修模式下刷新
|
||||
if (diyStore.mode == 'decorate') {
|
||||
watch(
|
||||
() => diyComponent.value,
|
||||
(newValue, oldValue) => {
|
||||
if (newValue && newValue.componentName == 'ActiveCube') {
|
||||
refresh();
|
||||
}
|
||||
}
|
||||
)
|
||||
} else {
|
||||
watch(
|
||||
() => diyComponent.value,
|
||||
(newValue, oldValue) => {
|
||||
refresh();
|
||||
}
|
||||
)
|
||||
}
|
||||
});
|
||||
|
||||
const instance = getCurrentInstance();
|
||||
|
||||
@ -2,62 +2,89 @@
|
||||
<view :style="warpCss" class="goods-carousel-search-wrap">
|
||||
<view class="relative pb-[20rpx]">
|
||||
<view class="bg-img" :class="{'!-bottom-[200rpx]': diyComponent.bgGradient == true}">
|
||||
<image v-if="diyComponent.swiper.list && diyComponent.swiper.list[swiperIndex].imageUrl" :src="img(diyComponent.swiper.list[swiperIndex].imageUrl)" mode="scaleToFill" class="w-full h-full" :show-menu-by-longpress="true"/>
|
||||
<view v-else class="w-full h-full bg-[#ccc]"></view>
|
||||
<image v-if="diyComponent.swiper.control && diyComponent.swiper.list && diyComponent.swiper.list[swiperIndex].imageUrl" :src="img(diyComponent.swiper.list[swiperIndex].imageUrl)" mode="scaleToFill" class="w-full h-full" :show-menu-by-longpress="true"/>
|
||||
<view v-else class="w-full h-full bg-[#fff]"></view>
|
||||
<view class="bg-img-box" :style="bgImgBoxStyle"></view>
|
||||
</view>
|
||||
<view class="fixed-wrap" :style="fixedStyle">
|
||||
<view class="diy-search-wrap relative z-10" @click="diyStore.toRedirect(diyComponent.search.link)" :style="navbarInnerStyle">
|
||||
<view v-if="diyComponent.search.style == 'style-1'" class="diy-search-wrap relative z-10" @click="diyStore.toRedirect(diyComponent.search.link)" :style="navbarInnerStyle">
|
||||
<view class="img-wrap" v-if="diyComponent.search.logo">
|
||||
<image :src="img(diyComponent.search.logo)" mode="aspectFit"/>
|
||||
</view>
|
||||
<view class="search-content" @click.stop="diyStore.toRedirect(diyComponent.search.link)">
|
||||
<text class="input-content text-[#fff] text-[24rpx] leading-[68rpx]">{{isShowSearchPlaceholder ? diyComponent.search.text : ''}}</text>
|
||||
<text class="nc-iconfont nc-icon-sousuo-duanV6xx1 text-[28rpx] text-[#fff]"></text>
|
||||
|
||||
<view class="search-content" :style="{backgroundColor: diyComponent.search.bgColor }" @click.stop="diyStore.toRedirect(diyComponent.search.link)">
|
||||
<text class="input-content text-[#fff] text-[24rpx] leading-[68rpx]" :style="{color: diyComponent.search.color }">{{isShowSearchPlaceholder ? diyComponent.search.text : ''}}</text>
|
||||
<text class="nc-iconfont nc-icon-sousuo-duanV6xx1 w-[80rpx] h-[52rpx] flex items-center justify-center rounded-[50rpx] text-[28rpx] text-[#fff]" :style="{backgroundColor: diyComponent.search.btnBgColor, color: diyComponent.search.btnColor }"></text>
|
||||
<swiper class="swiper-wrap" :interval="diyComponent.search.hotWord.interval * 1000" autoplay="true" vertical="true" circular="true" v-if="!isShowSearchPlaceholder">
|
||||
<swiper-item class="swiper-item" v-for="(item) in diyComponent.search.hotWord.list" :key="item.id">
|
||||
<view class=" leading-[64rpx] text-[24rpx]">{{ item.text }}</view>
|
||||
<view class="leading-[64rpx] text-[24rpx]" :style="{color: diyComponent.search.color }">{{ item.text }}</view>
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="diyComponent.search.style == 'style-2'" class="diy-search-wrap style-2 relative z-10" @click="diyStore.toRedirect(diyComponent.search.link)" >
|
||||
<view class="flex items-center" :style="navbarInnerStyle">
|
||||
<view class="img-wrap" v-if="diyComponent.search.logo">
|
||||
<image :src="img(diyComponent.search.logo)" mode="aspectFit"/>
|
||||
</view>
|
||||
<view :style="searchSubTitleCss" class="text-[24rpx] h-[38rpx] flex items-center px-[12rpx] rounded-r-[20rpx] rounded-t-[20rpx] rounded-bl-[2rpx]">{{diyComponent.search.subTitle.text}}</view>
|
||||
</view>
|
||||
<view class="flex items-center w-full mt-[16rpx]">
|
||||
<view @click.stop="locationVal.reposition()" v-if="systemStore.diyAddressInfo" :style="{color: diyComponent.search.positionColor}" class="mr-[30rpx]">
|
||||
<view class="flex items-baseline font-500">
|
||||
<text class="text-[24rpx] mr-[2rpx]">{{systemStore.diyAddressInfo.city}}</text>
|
||||
<text class="iconfont iconxiaV6xx !text-[24rpx]"></text>
|
||||
</view>
|
||||
<view class="text-[18rpx] mt-[10rpx] truncate max-w-[160rpx]" v-if="systemStore.diyAddressInfo.community">{{systemStore.diyAddressInfo.community}}</view>
|
||||
</view>
|
||||
<view @click.stop="locationVal.reposition()" class="text-[24rpx] mr-[30rpx] truncate max-w-[160rpx]" :style="{color: diyComponent.search.positionColor}" v-else>{{ systemStore.defaultPositionAddress }}</view>
|
||||
|
||||
<view class="search-content" :style="{backgroundColor: diyComponent.search.bgColor }" @click.stop="diyStore.toRedirect(diyComponent.search.link)">
|
||||
<text class="input-content text-[#fff] text-[24rpx] leading-[68rpx]" :style="{color: diyComponent.search.color }">{{isShowSearchPlaceholder ? diyComponent.search.text : ''}}</text>
|
||||
<text class="nc-iconfont nc-icon-sousuo-duanV6xx1 w-[80rpx] h-[52rpx] flex items-center justify-center rounded-[50rpx] text-[28rpx] text-[#fff]" :style="{backgroundColor: diyComponent.search.btnBgColor, color: diyComponent.search.btnColor }"></text>
|
||||
<swiper class="swiper-wrap" :interval="diyComponent.search.hotWord.interval * 1000" autoplay="true" vertical="true" circular="true" v-if="!isShowSearchPlaceholder">
|
||||
<swiper-item class="swiper-item" v-for="(item) in diyComponent.search.hotWord.list" :key="item.id">
|
||||
<view class="leading-[64rpx] text-[24rpx]" :style="{color: diyComponent.search.color }">{{ item.text }}</view>
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="tab-list-wrap relative z-10" v-if="diyComponent.tab.control">
|
||||
<scroll-view scroll-x="true" class="scroll-wrap" :scroll-into-view="'a' + currTabIndex">
|
||||
<view @click="changeData({ source : 'home' },-1)" class="scroll-item" :class="[{ active: currTabIndex == -1 }]">
|
||||
<view class="name" :style="{'color': getTabColor(currTabIndex == -1)}">首页</view>
|
||||
<view class="line" :style="{'background-color': getTabColor(currTabIndex == -1)}" v-if="currTabIndex == -1"></view>
|
||||
<!-- <view class="line" :style="{'background-color': getTabColor(currTabIndex == -1)}" v-if="currTabIndex == -1"></view> -->
|
||||
</view>
|
||||
<view v-for="(item, index) in diyComponent.tab.list" class="scroll-item" :class="[{ active: index == currTabIndex }]" @click="changeData(item,index)" :id="'a' + index" :key="index">
|
||||
<view class="name" :style="{'color': getTabColor(index == currTabIndex)}">{{ item.text }}</view>
|
||||
<view class="line" :style="{'background-color': getTabColor(index == currTabIndex)}" v-if="index == currTabIndex"></view>
|
||||
<!-- <view class="line" :style="{'background-color': getTabColor(index == currTabIndex)}" v-if="index == currTabIndex"></view> -->
|
||||
</view>
|
||||
</scroll-view>
|
||||
<view v-if="diyComponent.tab.list.length" class="absolute tab-btn iconfont icona-yingyongliebiaoV6xx-32" @click="tabAllPopup = true"></view>
|
||||
</view>
|
||||
|
||||
|
||||
<view class="bg-img" v-if="fixedStyleBg">
|
||||
<image v-if="diyComponent.swiper.list && diyComponent.swiper.list[swiperIndex].imageUrl" :src="img(diyComponent.swiper.list[swiperIndex].imageUrl)" mode="scaleToFill" class="w-full h-full" :show-menu-by-longpress="true"/>
|
||||
<view v-else class="w-full h-full bg-[#ccc]"></view>
|
||||
<image v-if="diyComponent.swiper.control && diyComponent.swiper.list && diyComponent.swiper.list[swiperIndex].imageUrl" :src="img(diyComponent.swiper.list[swiperIndex].imageUrl)" mode="widthFix" class="w-full h-full" :show-menu-by-longpress="true"/>
|
||||
<view v-else class="w-full h-full bg-[#fff]"></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
|
||||
<!-- 解决fixed定位后导航栏塌陷的问题 -->
|
||||
<template v-if="diyStore.mode != 'decorate'">
|
||||
<view v-if="diyComponent.positionWay == 'fixed' && props.scrollBool != undefined && props.scrollBool != -1" class="u-navbar-placeholder" :style="{ width: '100%', paddingTop: moduleHeight }"></view>
|
||||
</template>
|
||||
|
||||
|
||||
<!-- 轮播图 -->
|
||||
<view class="relative" :class="{'mx-[20rpx]': swiperStyle2}">
|
||||
<view class="relative" :class="{'mx-[20rpx]': swiperStyleBool && diyComponent.swiper.swiperStyle != 'style-3', 'swiper-style-3': diyComponent.swiper.swiperStyle == 'style-3'}" :style="carouselSwiperStyle()">
|
||||
<swiper v-if="diyComponent.swiper.control" class="swiper" :style="{ height: imgHeight }" autoplay="true" circular="true" @change="swiperChange"
|
||||
:class="{
|
||||
'swiper-left': diyComponent.swiper.indicatorAlign == 'left',
|
||||
'swiper-right': diyComponent.swiper.indicatorAlign == 'right',
|
||||
'ns-indicator-dots': diyComponent.swiper.indicatorStyle == 'style-2'
|
||||
'ns-indicator-dots': diyComponent.swiper.indicatorStyle == 'style-2',
|
||||
'ns-indicator-dots-three': diyComponent.swiper.indicatorStyle == 'style-3'
|
||||
}"
|
||||
:previous-margin="swiperStyle2 ? 0 : '26rpx'" :next-margin="swiperStyle2 ? 0 : '26rpx'"
|
||||
:previous-margin="swiperStyleBool ? 0 : '26rpx'" :next-margin="swiperStyleBool ? 0 : '26rpx'"
|
||||
:interval="diyComponent.swiper.interval * 1000" :indicator-dots="isShowDots"
|
||||
:indicator-color="diyComponent.swiper.indicatorColor" :indicator-active-color="diyComponent.swiper.indicatorActiveColor">
|
||||
<swiper-item class="swiper-item" v-for="(item,index) in diyComponent.swiper.list" :key="item.id" :style="swiperWarpCss">
|
||||
@ -73,6 +100,7 @@
|
||||
<view v-if="diyComponent.swiper.list.length > 1" :class="[
|
||||
'swiper-dot-box',
|
||||
{ 'straightLine': diyComponent.swiper.indicatorStyle == 'style-2' },
|
||||
{ 'straightLineStyle2': diyComponent.swiper.indicatorStyle == 'style-3' },
|
||||
{ 'swiper-left': diyComponent.swiper.indicatorAlign == 'left' },
|
||||
{ 'swiper-right': diyComponent.swiper.indicatorAlign == 'right' }
|
||||
]">
|
||||
@ -80,7 +108,7 @@
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
|
||||
|
||||
<!-- 分类展开 -->
|
||||
<u-popup :safeAreaInsetTop="true" :show="tabAllPopup" mode="top" @close="tabAllPopup = false">
|
||||
<view class="text-sm px-[30rpx] pt-3" :style="{'padding-top':(menuButtonInfo.top+'px')}">全部分类</view>
|
||||
@ -113,8 +141,11 @@
|
||||
import { ref, reactive, computed, watch, onMounted, nextTick, getCurrentInstance } from 'vue';
|
||||
import { img } from '@/utils/common';
|
||||
import useDiyStore from '@/app/stores/diy';
|
||||
import diyGroup from '@/addon/components/diy/group/index.vue'
|
||||
import diyGroup from '@/addon/components/diy/group/index.vue';
|
||||
import { getDiyInfo } from '@/app/api/diy';
|
||||
import {useLocation} from '@/hooks/useLocation'
|
||||
import useSystemStore from '@/stores/system';
|
||||
const systemStore = useSystemStore();
|
||||
|
||||
const instance = getCurrentInstance();
|
||||
const props = defineProps(['component', 'index', 'pullDownRefreshCount', 'global', 'scrollBool']);
|
||||
@ -126,7 +157,18 @@
|
||||
return props.component;
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
/************** 定位-start ****************/
|
||||
let isOpenLocation = false;
|
||||
if(diyComponent.value && diyComponent.value.search.style == 'style-2' && diyStore.mode != 'decorate') {
|
||||
isOpenLocation = true;
|
||||
}
|
||||
|
||||
const locationVal = useLocation(isOpenLocation);
|
||||
locationVal.onLoad();
|
||||
locationVal.init();
|
||||
/************** 定位-end ****************/
|
||||
|
||||
const warpCss = computed(() => {
|
||||
var style = '';
|
||||
if(diyComponent.value.componentStartBgColor) {
|
||||
@ -140,6 +182,7 @@
|
||||
return style;
|
||||
})
|
||||
|
||||
|
||||
watch(
|
||||
() => props.pullDownRefreshCount,
|
||||
(newValue, oldValue) => {
|
||||
@ -151,36 +194,44 @@
|
||||
const setModuleLocation = ()=> {
|
||||
nextTick(() => {
|
||||
setTimeout(()=>{
|
||||
const query = uni.createSelectorQuery().in(instance);
|
||||
query.select('.fixed-wrap').boundingClientRect((data:any) => {
|
||||
moduleHeight.value = (data.height || 0) + 'px';
|
||||
}).exec();
|
||||
if(diyComponent.value.swiper.swiperStyle != 'style-3'){
|
||||
const query = uni.createSelectorQuery().in(instance);
|
||||
query.select('.fixed-wrap').boundingClientRect((data:any) => {
|
||||
moduleHeight.value = (data.height || 0) + 'px';
|
||||
}).exec();
|
||||
}else{
|
||||
moduleHeight.value = '';
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const fixedStyleBg = ref(false);
|
||||
const fixedStyle = computed(()=>{
|
||||
if (diyStore.mode == 'decorate') return '';
|
||||
var style = '';
|
||||
var style = '';
|
||||
if(diyComponent.value.swiper.swiperStyle == 'style-3'){
|
||||
style += 'position: absolute;z-index: 10;left: 0;right: 0;';
|
||||
}
|
||||
if (diyStore.mode == 'decorate') return style;
|
||||
|
||||
// #ifdef H5
|
||||
if(props.global.topStatusBar.isShow && props.global.topStatusBar.style == 'style-4') {
|
||||
style += 'top:' + diyStore.topTabarHeight + 'px;';
|
||||
}
|
||||
// #endif
|
||||
|
||||
|
||||
if(diyComponent.value.positionWay == 'fixed') {
|
||||
if (props.scrollBool != undefined && props.scrollBool != -1) {
|
||||
style += 'position: fixed;z-index: 10;left: 0;right: 0;';
|
||||
}
|
||||
|
||||
|
||||
// #ifdef MP-WEIXIN || MP-BAIDU || MP-TOUTIAO || MP-QQ
|
||||
menuButtonInfo = uni.getMenuButtonBoundingClientRect();
|
||||
if(props.global.topStatusBar.isShow) {
|
||||
style += 'top:' + diyStore.topTabarHeight + 'px;';
|
||||
}
|
||||
// #endif
|
||||
|
||||
|
||||
fixedStyleBg.value = false;
|
||||
if (props.scrollBool == 1) {
|
||||
let str = diyComponent.value.fixedBgColor || "";
|
||||
@ -197,6 +248,17 @@
|
||||
return style;
|
||||
})
|
||||
|
||||
// 轮播样式
|
||||
const carouselSwiperStyle = ()=> {
|
||||
let style = "";
|
||||
if(diyComponent.value.swiper.swiperStyle == 'style-3'){
|
||||
// #ifdef MP
|
||||
style = 'padding-top:' + menuButtonInfo.top + 'px;';
|
||||
// #endif
|
||||
}
|
||||
return style;
|
||||
}
|
||||
|
||||
const getTabColor = (flag:any)=>{
|
||||
let color = '';
|
||||
if(flag){
|
||||
@ -242,8 +304,8 @@
|
||||
});
|
||||
|
||||
// 轮播样式二
|
||||
const swiperStyle2 = computed(()=>{
|
||||
var style = diyComponent.value.swiper.swiperStyle == 'style-2' ? true : false;
|
||||
const swiperStyleBool = computed(()=>{
|
||||
var style = diyComponent.value.swiper.swiperStyle == 'style-2' || diyComponent.value.swiper.swiperStyle == 'style-3' ? true : false;
|
||||
return style;
|
||||
})
|
||||
|
||||
@ -266,6 +328,14 @@
|
||||
return style;
|
||||
})
|
||||
|
||||
const searchSubTitleCss = computed(() => {
|
||||
var style = '';
|
||||
if (diyComponent.value.search.subTitle.textColor) style += 'color:' + diyComponent.value.search.subTitle.textColor + ';';
|
||||
if (diyComponent.value.search.subTitle.startColor && diyComponent.value.search.subTitle.endColor) style += `background:linear-gradient(${diyComponent.value.search.subTitle.startColor}, ${diyComponent.value.search.subTitle.endColor});`;
|
||||
else style += 'background-color:' + (diyComponent.value.search.subTitle.startColor || diyComponent.value.search.subTitle.endColor) + ';';
|
||||
return style;
|
||||
})
|
||||
|
||||
const currTabIndex = ref(-1)
|
||||
|
||||
const currentSource = ref('')
|
||||
@ -312,6 +382,10 @@
|
||||
}
|
||||
)
|
||||
}
|
||||
// 判断让轮播指示器是否出现
|
||||
// #ifdef H5
|
||||
isShowDots.value = diyComponent.value.swiper.list.length > 1 ? true : false;
|
||||
// #endif
|
||||
|
||||
// 如果是小程序,获取右上角胶囊的尺寸信息,避免导航栏右侧内容与胶囊重叠(支付宝小程序非本API,尚未兼容)
|
||||
// #ifdef MP-WEIXIN || MP-BAIDU || MP-TOUTIAO || MP-QQ
|
||||
@ -324,15 +398,16 @@
|
||||
}else if(props.global.topStatusBar){
|
||||
navbarInnerStyle.value = ''
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
// #endif
|
||||
|
||||
});
|
||||
|
||||
|
||||
const refresh = ()=> {
|
||||
setModuleLocation();
|
||||
|
||||
// 刷新定位
|
||||
locationVal.refresh();
|
||||
|
||||
changeData({ source : 'home' },-1)
|
||||
diyComponent.value.swiper.list.forEach((item : any) => {
|
||||
if (item.imageUrl == '') {
|
||||
@ -345,9 +420,9 @@
|
||||
const diyPageData = reactive({
|
||||
pageMode: 'diy',
|
||||
title: '',
|
||||
global: <any>{},
|
||||
global: {},
|
||||
value: []
|
||||
})
|
||||
});
|
||||
|
||||
const getDiyInfoFn = (id:any) => {
|
||||
if(!id){
|
||||
@ -393,17 +468,17 @@
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// 轮播指示器
|
||||
let isShowDots = ref(true)
|
||||
// #ifdef H5
|
||||
isShowDots.value = true;
|
||||
// #endif
|
||||
|
||||
|
||||
// #ifdef MP-WEIXIN
|
||||
isShowDots.value = false;
|
||||
// #endif
|
||||
|
||||
|
||||
/******************************* 存储滚动值-start ***********************/
|
||||
// 键名和组件名一致即可
|
||||
let componentsScrollVal = uni.getStorageSync('componentsScrollValGroup')
|
||||
@ -416,8 +491,7 @@
|
||||
}
|
||||
uni.setStorageSync('componentsScrollValGroup', obj);
|
||||
}
|
||||
/******************************* 存储滚动值-end ***********************/
|
||||
|
||||
/******************************* 存储滚动值-end ***********************/
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@ -434,8 +508,8 @@
|
||||
uni-image, image{
|
||||
-webkit-filter: blur(15px);
|
||||
filter: blur(15px);
|
||||
-webkit-transform: scale(1.5);
|
||||
transform: scale(1.5);
|
||||
-webkit-transform: scale(2) translateY(15%);
|
||||
transform: scale(2) translateY(15%);
|
||||
}
|
||||
.bg-img-box{
|
||||
position: absolute;
|
||||
@ -446,7 +520,7 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.fixed-wrap {
|
||||
&.fixed {
|
||||
position: fixed;
|
||||
@ -462,14 +536,14 @@
|
||||
display: flex;
|
||||
position: relative;
|
||||
align-items: center;
|
||||
padding:20rpx;
|
||||
padding:16rpx;
|
||||
.img-wrap{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 140rpx;
|
||||
height: 60rpx;
|
||||
margin-right: 30rpx;
|
||||
margin-right: 20rpx;
|
||||
image{
|
||||
width: 100%;
|
||||
height:100%;
|
||||
@ -479,7 +553,8 @@
|
||||
.search-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 32rpx;
|
||||
padding-left: 32rpx;
|
||||
padding-right: 6rpx;
|
||||
border-radius: 50rpx;
|
||||
background-color: rgba(255,255,255,.2);
|
||||
flex: 1;
|
||||
@ -493,6 +568,7 @@
|
||||
padding-right: 20rpx;
|
||||
color: #fff;
|
||||
background: none;
|
||||
flex: 1;
|
||||
}
|
||||
.iconfont {
|
||||
font-size: 30rpx;
|
||||
@ -508,6 +584,13 @@
|
||||
}
|
||||
}
|
||||
|
||||
&.style-2{
|
||||
flex-direction: column;
|
||||
align-items: baseline;
|
||||
.img-wrap{
|
||||
margin-right: 10rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tab-list-wrap {
|
||||
@ -518,7 +601,8 @@
|
||||
width: 100%;
|
||||
white-space: nowrap;
|
||||
box-sizing: border-box;
|
||||
padding: 20rpx 80rpx 20rpx 20rpx;
|
||||
padding: 24rpx 80rpx 26rpx 20rpx;
|
||||
line-height: 1;
|
||||
}
|
||||
.scroll-item {
|
||||
display: inline-block;
|
||||
@ -527,35 +611,23 @@
|
||||
width: auto;
|
||||
position: relative;
|
||||
padding: 0 20rpx;
|
||||
height:54rpx;
|
||||
.name {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
line-height: 38rpx;
|
||||
margin-bottom: 15rpx;
|
||||
line-height: 32rpx;
|
||||
}
|
||||
|
||||
|
||||
&.active {
|
||||
position: relative;
|
||||
.name {
|
||||
font-size: 28rpx;
|
||||
line-height: 38rpx;
|
||||
font-weight: 700;
|
||||
}
|
||||
.line{
|
||||
position: absolute;
|
||||
bottom: 0rpx;
|
||||
width: 34rpx;
|
||||
height: 4rpx;
|
||||
border-radius: 2rpx;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
font-size: 30rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
.tab-btn{
|
||||
font-size: 34rpx;
|
||||
/* #ifdef H5 */
|
||||
/* #ifdef H5 */
|
||||
top: 22rpx;
|
||||
right: 20rpx;
|
||||
line-height: 1;
|
||||
@ -607,10 +679,13 @@
|
||||
left: 80rpx;
|
||||
transform: translate(0);
|
||||
}
|
||||
// 指示器样式一
|
||||
.swiper :deep(.uni-swiper-dot) {
|
||||
width: 12rpx;
|
||||
height: 12rpx;
|
||||
}
|
||||
|
||||
// 指示器样式二
|
||||
.swiper.ns-indicator-dots :deep(.uni-swiper-dot) {
|
||||
width: 18rpx;
|
||||
height: 6rpx;
|
||||
@ -619,6 +694,21 @@
|
||||
.swiper.ns-indicator-dots :deep(.uni-swiper-dot-active) {
|
||||
width: 36rpx;
|
||||
}
|
||||
|
||||
// 指示器样式三
|
||||
.swiper.ns-indicator-dots-three :deep(.uni-swiper-dot) {
|
||||
width: 8rpx;
|
||||
height: 8rpx !important;
|
||||
border-radius: 6rpx;
|
||||
margin-right: 14rpx;
|
||||
}
|
||||
.swiper.ns-indicator-dots-three :deep(.uni-swiper-dot):last-of-type {
|
||||
margin-right: 0;
|
||||
}
|
||||
.swiper.ns-indicator-dots-three :deep(.uni-swiper-dot-active) {
|
||||
width: 30rpx;
|
||||
}
|
||||
|
||||
.swiper-dot-box {
|
||||
position: absolute;
|
||||
bottom: 20rpx;
|
||||
@ -628,15 +718,15 @@
|
||||
justify-content: center;
|
||||
padding: 0 80rpx 8rpx;
|
||||
box-sizing: border-box;
|
||||
|
||||
|
||||
&.swiper-left {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
|
||||
&.swiper-right {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
|
||||
.swiper-dot {
|
||||
background-color: #b2b2b2;
|
||||
width: 12rpx;
|
||||
@ -644,17 +734,41 @@
|
||||
height: 12rpx;
|
||||
margin: 8rpx;
|
||||
}
|
||||
|
||||
|
||||
&.straightLine {
|
||||
.swiper-dot {
|
||||
width: 18rpx;
|
||||
height: 6rpx;
|
||||
border-radius: 4rpx;
|
||||
|
||||
border-radius: 6rpx;
|
||||
|
||||
&.active {
|
||||
width: 36rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
&.straightLineStyle2{
|
||||
.swiper-dot {
|
||||
width: 10rpx;
|
||||
height: 10rpx;
|
||||
border-radius: 6rpx;
|
||||
margin: 0;
|
||||
margin-right: 14rpx;
|
||||
&.last-of-type {
|
||||
margin-right: 0;
|
||||
}
|
||||
&.active {
|
||||
width: 30rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
.swiper-style-3{
|
||||
:deep(.uni-swiper-dots-horizontal){
|
||||
bottom: 46rpx !important;
|
||||
}
|
||||
.swiper-dot-box{
|
||||
bottom: 38rpx !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -33,69 +33,33 @@
|
||||
</view>
|
||||
|
||||
</view>
|
||||
<swiper v-else-if="diyComponent.layout == 'horizontal' && diyComponent.showStyle == 'pageSlide'"
|
||||
class="graphic-nav box-border relative" circular :indicator-dots="false"
|
||||
:style="{ height: swiperHeight }" @change="swiperChange">
|
||||
<swiper-item class="graphic-nav-wrap flex flex-wrap" v-for="(numItem, numIndex) in Math.ceil(diyComponent.list.length / (diyComponent.pageCount * diyComponent.rowCount))">
|
||||
|
||||
<template v-for="(item, index) in diyComponent.list">
|
||||
<view class="pt-[10rpx]" v-else-if="diyComponent.layout == 'horizontal' && diyComponent.showStyle == 'pageSlide'">
|
||||
<swiper class="graphic-nav swiper relative" :style="{ height: swiperHeight, width: '95%', margin: '0 auto',opacity : swiperHeight ? 1 : 0 }" circular @change="swiperChange">
|
||||
<swiper-item class="graphic-nav-wrap flex flex-wrap" v-for="(numItem, numIndex) in Math.ceil(diyComponent.list.length / (diyComponent.pageCount * diyComponent.rowCount))">
|
||||
|
||||
<view :class="[diyComponent.mode]" :key="item.id" v-if="swiperCondition(index,numItem)" :style="{ width: 100 / diyComponent.rowCount + '%' }">
|
||||
<template v-for="(item, index) in diyComponent.list">
|
||||
|
||||
<view @click="diyStore.toRedirect(item.link)" class="graphic-nav-item flex flex-col items-center box-border py-2">
|
||||
<view :class="[diyComponent.mode]" :key="item.id" v-if="swiperCondition(index,numItem)" :style="{ width: 100 / diyComponent.rowCount + '%' }">
|
||||
|
||||
<view class="graphic-img relative flex items-center justify-center w-10 h-10"
|
||||
v-if="diyComponent.mode != 'text'"
|
||||
:style="{ width: diyComponent.imageSize * 2 + 'rpx', height: diyComponent.imageSize * 2 + 'rpx' }">
|
||||
<image v-if="item.imageUrl" :src="img(item.imageUrl)" mode="aspectFill"
|
||||
:style="{ maxWidth: diyComponent.imageSize * 2 + 'rpx', maxHeight: diyComponent.imageSize * 2 + 'rpx', borderRadius: diyComponent.aroundRadius * 2 + 'rpx' }"/>
|
||||
<image v-else :src="img('static/resource/images/diy/figure.png')" mode="aspectFill"
|
||||
:style="{ maxWidth: diyComponent.imageSize * 2 + 'rpx', maxHeight: diyComponent.imageSize * 2 + 'rpx', borderRadius: diyComponent.aroundRadius * 2 + 'rpx' }"/>
|
||||
<view @click="diyStore.toRedirect(item.link)" class="graphic-nav-item flex flex-col items-center py-2">
|
||||
|
||||
<text
|
||||
class="tag absolute -top-[10rpx] -right-[24rpx] text-white rounded-[24rpx] rounded-bl-none transform scale-80 py-1 px-2 text-xs"
|
||||
v-if="item.label.control"
|
||||
:style="{ color: item.label.textColor, backgroundImage: 'linear-gradient(' + item.label.bgColorStart + ',' + item.label.bgColorEnd + ')' }">
|
||||
{{ item.label.text }}
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<text v-if="diyComponent.mode != 'img'"
|
||||
class="graphic-text w-full text-center truncate leading-normal"
|
||||
:class="{ 'pt-[16rpx]' : diyComponent.mode != 'text' }"
|
||||
:style="{ fontSize: diyComponent.font.size * 2 + 'rpx', fontWeight: diyComponent.font.weight, color: diyComponent.font.color }">
|
||||
{{ item.title }}
|
||||
</text>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
</template>
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
|
||||
<scroll-view v-else-if="diyComponent.layout == 'horizontal' && diyComponent.pageCount == 2 && diyComponent.showStyle == 'singleSlide'" :scroll-x="diyComponent.showStyle == 'singleSlide'" :class="['graphic-nav','graphic-nav-' + diyComponent.showStyle]" class="py-[10rpx]">
|
||||
<!-- #ifdef MP -->
|
||||
<view class="uni-scroll-view-content">
|
||||
<!-- #endif -->
|
||||
<view :style="horizontalSingleSlideStyle" class="flex">
|
||||
<view class="graphic-nav-wrap flex flex-wrap" :style="horizontalSingleSlideBoxStyle(numIndex)" v-for="(numItem, numIndex) in Math.ceil(diyComponent.list.length / (diyComponent.pageCount * diyComponent.rowCount))">
|
||||
<template v-for="(item, index) in diyComponent.list">
|
||||
|
||||
<view v-if="swiperCondition(index,numItem)" @click="diyStore.toRedirect(item.link)" :style="horizontalSingleSlideItemStyle(numIndex)" class="graphic-nav-item flex flex-col items-center box-border py-2">
|
||||
<view class="graphic-img relative flex items-center justify-center w-10 h-10"
|
||||
v-if="diyComponent.mode != 'text'"
|
||||
:style="{ width: diyComponent.imageSize * 2 + 'rpx', height: diyComponent.imageSize * 2 + 'rpx' }">
|
||||
<image v-if="item.imageUrl" :src="img(item.imageUrl)" mode="aspectFill"
|
||||
:style="{ maxWidth: diyComponent.imageSize * 2 + 'rpx', maxHeight: diyComponent.imageSize * 2 + 'rpx', borderRadius: diyComponent.aroundRadius * 2 + 'rpx' }"/>
|
||||
<image v-else :src="img('static/resource/images/diy/figure.png')" mode="aspectFill"
|
||||
:style="{ maxWidth: diyComponent.imageSize * 2 + 'rpx', maxHeight: diyComponent.imageSize * 2 + 'rpx', borderRadius: diyComponent.aroundRadius * 2 + 'rpx' }"/>
|
||||
:style="{ maxWidth: diyComponent.imageSize * 2 + 'rpx', maxHeight: diyComponent.imageSize * 2 + 'rpx', borderRadius: diyComponent.aroundRadius * 2 + 'rpx' }"/>
|
||||
|
||||
<text
|
||||
:class="['tag absolute -top-[10rpx] -right-[24rpx] text-white rounded-[24rpx] rounded-bl-none transform scale-80 py-1 px-2 text-xs']"
|
||||
class="tag absolute -top-[10rpx] -right-[24rpx] text-white rounded-[24rpx] rounded-bl-none transform scale-80 py-1 px-2 text-xs"
|
||||
v-if="item.label.control"
|
||||
:style="{ color: item.label.textColor, backgroundImage: 'linear-gradient(' + item.label.bgColorStart + ',' + item.label.bgColorEnd + ')' }">
|
||||
{{ item.label.text }}
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<text v-if="diyComponent.mode != 'img'"
|
||||
class="graphic-text w-full text-center truncate leading-normal"
|
||||
:class="{ 'pt-[16rpx]' : diyComponent.mode != 'text' }"
|
||||
@ -103,14 +67,56 @@
|
||||
{{ item.title }}
|
||||
</text>
|
||||
</view>
|
||||
</template>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
</template>
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
<view class="graphic-nav-indicator-dot" v-if="shouldShowIndicator && swiperHeight">
|
||||
<div class="dots-wrap" :class="[diyComponent.swiper.indicatorAlign]">
|
||||
<div v-for="(item,index) in Math.ceil(diyComponent.list.length / (diyComponent.pageCount * diyComponent.rowCount))"
|
||||
:class="['dot',index == swiperIndex ? 'dot-active' : '',diyComponent.swiper.indicatorStyle]"
|
||||
:style="{ background : index == swiperIndex ? diyComponent.swiper.indicatorActiveColor : diyComponent.swiper.indicatorColor}"></div>
|
||||
</div>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view :style="{width: '98%', margin: '0 auto' }" v-else-if="diyComponent.layout == 'horizontal' && diyComponent.pageCount == 2 && diyComponent.showStyle == 'singleSlide'" :class="['graphic-nav multiple-lines','graphic-nav-' + diyComponent.showStyle]" class="py-[10rpx]">
|
||||
<!-- #ifdef MP -->
|
||||
<view class="uni-scroll-view-content">
|
||||
<!-- #endif -->
|
||||
<scroll-view class="graphic-nav-wrap whitespace-nowrap" :scroll-x="diyComponent.showStyle == 'singleSlide'" v-for="(numItem, numIndex) in getSlideRowNum()">
|
||||
<template v-for="(item, index) in diyComponent.list" >
|
||||
|
||||
<view v-if="isShowslideTemp(index,numItem)" @click="diyStore.toRedirect(item.link)" :style="horizontalSingleSlideItemStyle(numIndex)" class="graphic-nav-item inline-flex flex-col items-center box-border py-2">
|
||||
<view class="graphic-img relative flex items-center justify-center w-10 h-10"
|
||||
v-if="diyComponent.mode != 'text'"
|
||||
:style="{ width: diyComponent.imageSize * 2 + 'rpx', height: diyComponent.imageSize * 2 + 'rpx' }">
|
||||
<image v-if="item.imageUrl" :src="img(item.imageUrl)" mode="aspectFill"
|
||||
:style="{ maxWidth: diyComponent.imageSize * 2 + 'rpx', maxHeight: diyComponent.imageSize * 2 + 'rpx', borderRadius: diyComponent.aroundRadius * 2 + 'rpx' }"/>
|
||||
<image v-else :src="img('static/resource/images/diy/figure.png')" mode="aspectFill"
|
||||
:style="{ maxWidth: diyComponent.imageSize * 2 + 'rpx', maxHeight: diyComponent.imageSize * 2 + 'rpx', borderRadius: diyComponent.aroundRadius * 2 + 'rpx' }"/>
|
||||
<text
|
||||
:class="['tag absolute -top-[10rpx] -right-[24rpx] text-white rounded-[24rpx] rounded-bl-none transform scale-80 py-1 px-2 text-xs']"
|
||||
v-if="item.label.control"
|
||||
:style="{ color: item.label.textColor, backgroundImage: 'linear-gradient(' + item.label.bgColorStart + ',' + item.label.bgColorEnd + ')' }">
|
||||
{{ item.label.text }}
|
||||
</text>
|
||||
</view>
|
||||
<text v-if="diyComponent.mode != 'img'"
|
||||
class="graphic-text w-full text-center truncate leading-normal"
|
||||
:class="{ 'pt-[16rpx]' : diyComponent.mode != 'text' }"
|
||||
:style="{ fontSize: diyComponent.font.size * 2 + 'rpx', fontWeight: diyComponent.font.weight, color: diyComponent.font.color }">
|
||||
{{ item.title }}
|
||||
</text>
|
||||
</view>
|
||||
</template>
|
||||
</scroll-view>
|
||||
<!-- #ifdef MP -->
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
|
||||
</scroll-view>
|
||||
|
||||
</view>
|
||||
|
||||
<scroll-view v-else :scroll-x="diyComponent.showStyle == 'singleSlide'" :class="['graphic-nav','graphic-nav-' + diyComponent.showStyle]" class=" py-[10rpx]">
|
||||
<!-- #ifdef MP -->
|
||||
@ -172,7 +178,11 @@
|
||||
return props.component;
|
||||
}
|
||||
})
|
||||
|
||||
const shouldShowIndicator = computed(() => {
|
||||
const totalItems = diyComponent.value.list.length // 总项数
|
||||
const itemsPerPage = diyComponent.value.pageCount * diyComponent.value.rowCount // 每页可显示的项数
|
||||
return totalItems > itemsPerPage // 如果总项数大于每页显示的项数,显示指示器
|
||||
})
|
||||
const warpCss = computed(() => {
|
||||
var style = '';
|
||||
style += 'position:relative;';
|
||||
@ -209,46 +219,10 @@
|
||||
|
||||
return style;
|
||||
});
|
||||
|
||||
// 多行,单行滑动样式
|
||||
const horizontalSingleSlideStyle = computed(()=>{
|
||||
let style = {width: ""};
|
||||
let widthStr = 100 / diyComponent.value.rowCount; // 表示每项宽度
|
||||
let itemLen = (parseInt(diyComponent.value.list.length / (diyComponent.value.rowCount*2))*diyComponent.value.rowCount) + (diyComponent.value.list.length%(diyComponent.value.rowCount*2)); // 表示展示几列
|
||||
let marginLen = diyComponent.value.margin.both*4
|
||||
style.width = `calc(${widthStr * itemLen}vw - ${marginLen}rpx)`;
|
||||
return style;
|
||||
})
|
||||
|
||||
const horizontalSingleSlideBoxStyle = (index: any)=>{
|
||||
let style = {width: ""};
|
||||
let widthStr = 100 / diyComponent.value.rowCount; // 表示每项宽度
|
||||
let marginLen = diyComponent.value.margin.both * 4 / diyComponent.value.rowCount;
|
||||
if(parseInt(diyComponent.value.list.length / (diyComponent.value.rowCount*2)) >= (index+1)){
|
||||
style.width = `calc(${widthStr * diyComponent.value.rowCount}vw - ${marginLen*diyComponent.value.rowCount}rpx)`;
|
||||
}else{
|
||||
let len = diyComponent.value.list.length%(diyComponent.value.rowCount*2);
|
||||
if(len > diyComponent.value.rowCount){ // 满足了一行,但没有满足于一页
|
||||
style.width = `calc(${widthStr * diyComponent.value.rowCount}vw - ${marginLen*diyComponent.value.rowCount}rpx)`;
|
||||
}else{
|
||||
style.width = `calc(${widthStr * len}vw - ${marginLen * len}rpx)`; // 未满足了一行
|
||||
}
|
||||
}
|
||||
return style;
|
||||
}
|
||||
|
||||
const horizontalSingleSlideItemStyle = (index: any)=>{
|
||||
let style = {width: ""};
|
||||
if(parseInt(diyComponent.value.list.length / (diyComponent.value.rowCount*2)) >= (index+1)){
|
||||
style.width = `${100 / diyComponent.value.rowCount}%`;
|
||||
}else{
|
||||
let len = diyComponent.value.list.length%(diyComponent.value.rowCount*2);
|
||||
if(len > diyComponent.value.rowCount){ // 满足了一行,但没有满足于一页
|
||||
style.width = `${100 / diyComponent.value.rowCount}%`;
|
||||
}else{
|
||||
style.width = `${100 / len}%`; // 未满足了一行
|
||||
}
|
||||
}
|
||||
style.width = `${100 / diyComponent.value.rowCount}%`;
|
||||
return style;
|
||||
}
|
||||
|
||||
@ -268,27 +242,29 @@
|
||||
const swiperCondition = (index, numItem) => {
|
||||
let count = diyComponent.value.pageCount * diyComponent.value.rowCount;
|
||||
let result = true;
|
||||
|
||||
|
||||
result = index >= [(numItem - 1) * (count)] && index < [numItem * (count)];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
const swiperHeight = ref('');
|
||||
const storageKey = 'graphic_nav_horizontal_page_slide_swiperheight_' + props.index + '_' + diyComponent.value.list.length;
|
||||
const swiperHeight = ref(uni.getStorageSync(storageKey) || '');
|
||||
|
||||
const handleData = () => {
|
||||
if(diyComponent.value.layout == 'horizontal' && diyComponent.value.showStyle == 'pageSlide') {
|
||||
var height = 0;
|
||||
const query = uni.createSelectorQuery().in(instance);
|
||||
query.select('.graphic-nav-item').boundingClientRect((data: any) => {
|
||||
if (diyComponent.value.layout == 'horizontal' && diyComponent.value.showStyle == 'pageSlide') {
|
||||
var height = 0;
|
||||
const query = uni.createSelectorQuery().in(instance);
|
||||
query.select('.graphic-nav-item').boundingClientRect((data: any) => {
|
||||
let len = 1;
|
||||
if(diyComponent.value.pageCount == 2){
|
||||
len = (diyComponent.value.list.length / diyComponent.value.rowCount) > 1 ? 2 : 1 ;
|
||||
if (diyComponent.value.pageCount == 2) {
|
||||
len = (diyComponent.value.list.length / diyComponent.value.rowCount) > 1 ? 2 : 1;
|
||||
}
|
||||
height = data.height * len;
|
||||
swiperHeight.value = (height * 2) + 'rpx';
|
||||
}).exec();
|
||||
}
|
||||
height = data.height * len;
|
||||
swiperHeight.value = height + 'px';
|
||||
uni.setStorageSync(storageKey, swiperHeight.value);
|
||||
}).exec();
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
@ -318,6 +294,35 @@
|
||||
}).exec();
|
||||
})
|
||||
}
|
||||
// 获取滑动行数
|
||||
const getSlideRowNum = ()=>{
|
||||
let num = 1;
|
||||
if(diyComponent.value.pageCount == 2){
|
||||
num = diyComponent.value.list.length > diyComponent.value.rowCount ? 2 : 1;
|
||||
}
|
||||
return num;
|
||||
}
|
||||
const isShowslideTemp = (index, numItem) => {
|
||||
let result = true;
|
||||
let indent = index+1;
|
||||
if(diyComponent.value.pageCount == 2){
|
||||
let num = Math.ceil(diyComponent.value.list.length / diyComponent.value.rowCount)
|
||||
for(let i = 1; i <= num; i++){
|
||||
if(numItem == 1 && (i % 2) != 0){
|
||||
if(indent > ((i-1)*diyComponent.value.rowCount) && indent <= (i*diyComponent.value.rowCount)){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if(numItem == 2 && (i % 2) == 0){
|
||||
if(indent > ((i-1)*diyComponent.value.rowCount) && indent <= (i*diyComponent.value.rowCount)){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
@ -326,11 +331,73 @@
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
/* 单行滑动 */
|
||||
.graphic-nav-singleSlide>>>.uni-scroll-view-content {
|
||||
display: flex;
|
||||
}
|
||||
/* 多行滑动 */
|
||||
.multiple-lines.graphic-nav-singleSlide>>>.uni-scroll-view-content {
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
<style lang="scss" scoped>
|
||||
.graphic-nav-indicator-dot{
|
||||
width: 95%;
|
||||
margin: auto;
|
||||
|
||||
.dots-wrap{
|
||||
white-space: nowrap;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
&.left{
|
||||
justify-content: flex-start;
|
||||
padding-left: 30rpx;
|
||||
}
|
||||
&.right {
|
||||
justify-content: flex-end;
|
||||
padding-right: 30rpx;
|
||||
}
|
||||
.dot{
|
||||
display: inline-block;
|
||||
width: 12rpx;
|
||||
height: 12rpx;
|
||||
cursor: pointer;
|
||||
transition-property: background-color;
|
||||
transition-timing-function: ease;
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
border-radius: 50%;
|
||||
margin-right: 16rpx;
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
|
||||
// 指示器样式二
|
||||
&.style-2 {
|
||||
width: 18rpx;
|
||||
height: 6rpx;
|
||||
border-radius: 4rpx;
|
||||
}
|
||||
|
||||
&dot-active.style-2 {
|
||||
width: 36rpx;
|
||||
}
|
||||
|
||||
// 指示器样式三
|
||||
&.style-3{
|
||||
width: 10rpx;
|
||||
height: 10rpx !important;
|
||||
border-radius: 12rpx;
|
||||
margin-right: 14rpx;
|
||||
&:last-of-type{
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
&.dot-active.style-3{
|
||||
width: 32rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
<text class="text-[30rpx] text-[#FFDAA8] ml-[10rpx] font-500 max-w-[440rpx] truncate">{{info.member_level_name}}</text>
|
||||
</view>
|
||||
<view class="flex items-center justify-center rounded-[30rpx] box-border style-btn w-[140rpx] h-[56rpx]" @click="toLink('/app/pages/member/level')">
|
||||
<text class="text-[24rpx] text-[#333]">{{ info.member_level ? (upgradeGrowth > 0 ? '去升级' : '去查看') : '去解锁' }}</text>
|
||||
<text class="text-[24rpx] text-[#333]">{{ info.member_level ? (upgradeGrowth > 0 ? '做任务' : '点击查看') : '去解锁' }}</text>
|
||||
<text class="iconfont iconxiayibu1 ml-[4rpx] -mb-[2rpx] !text-[14rpx] text-[#333]"></text>
|
||||
</view>
|
||||
</view>
|
||||
@ -19,7 +19,7 @@
|
||||
<text class="text-[#FFE3B1] opacity-80 text-[24rpx] mt-[10rpx] leading-[32rpx]" v-if="benefits_arr && benefits_arr.length">{{info.member_level_name}}购物享{{benefits_arr[0].title}}</text>
|
||||
</view>
|
||||
<view class="flex items-center justify-center rounded-[30rpx] box-border style-btn w-[140rpx] h-[56rpx]" @click="toLink('/app/pages/member/level')">
|
||||
<text class="text-[24rpx] text-[#333]">{{ info.member_level ? (upgradeGrowth > 0 ? '去升级' : '去查看') : '去解锁' }}</text>
|
||||
<text class="text-[24rpx] text-[#333]">{{ info.member_level ? (upgradeGrowth > 0 ? '做任务' : '点击查看') : '去解锁' }}</text>
|
||||
<text class="iconfont iconxiayibu1 ml-[4rpx] -mb-[2rpx] !text-[14rpx] text-[#333]"></text>
|
||||
</view>
|
||||
</view>
|
||||
@ -58,6 +58,50 @@
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="diyComponent.style == 'style-4'" class="flex items-center justify-between style-4 px-[24rpx] py-[20rpx]" :style="{'backgroundImage': 'url('+img('static/resource/images/diy/member/style4_bg.jpg')+')'}">
|
||||
<view class="flex flex-col">
|
||||
<view class="flex items-center">
|
||||
<image :src="img('static/resource/images/diy/member/style4_vip.png')" mode="aspectFit" class="w-[70rpx] h-[32rpx] pt-[1rpx]" />
|
||||
<text class="text-[30rpx] text-[#FFEFB0] leading-[normal] ml-[8rpx] font-500 max-w-[420rpx] truncate">{{info.member_level_name}}</text>
|
||||
</view>
|
||||
<view class="text-[#B0B0B0] text-[24rpx] mt-[10rpx] leading-[32rpx]" v-if="benefits_arr && benefits_arr.length">
|
||||
<text>{{info.member_level_name}}购物享</text>
|
||||
<text class="text-[#FFEFB0]">{{benefits_arr[0].title}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="flex items-center justify-center rounded-[30rpx] box-border style-btn w-[150rpx] h-[50rpx]" @click="toLink('/app/pages/member/level')">
|
||||
<text class="text-[22rpx] text-[#333] mr-[8rpx]">{{ info.member_level ? (upgradeGrowth > 0 ? '做任务' : '点击查看') : '去解锁' }}</text>
|
||||
<image :src="img('static/resource/images/diy/member/style4_arrow.png')" mode="aspectFit" class="w-[26rpx] h-[26rpx] pt-[2rpx]" />
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="diyComponent.style == 'style-5'" class="rounded-[var(--rounded-big)] style-5" :style="{'backgroundImage': 'url('+img('static/resource/images/diy/member/style5_bg.jpg')+')'}">
|
||||
<view class="content-head pt-[16rpx] pb-[10rpx] px-[24rpx] flex items-center justify-between">
|
||||
<view class="flex items-center">
|
||||
<image :src="img('static/resource/images/diy/member/style5_vip.png')" mode="aspectFit" class="w-[40rpx] h-[40rpx]" />
|
||||
<text class="text-[#FFFBE2] ml-[10rpx] text-[30rpx] font-500 max-w-[470rpx] truncate">{{info.member_level_name}}</text>
|
||||
</view>
|
||||
<view class="flex items-center rounded-[30rpx] pl-[16rpx] pr-[12rpx] h-[44rpx] leading-normal style-btn" @click="toLink('/app/pages/member/level')">
|
||||
<text class="text-[22rpx] text-[#333] font-500 pb-[2rpx]">
|
||||
{{info.member_level ? (upgradeGrowth > 0 ? '做任务' : '点击查看') : '去解锁'}}
|
||||
</text>
|
||||
<image :src="img('static/resource/images/diy/member/style5_arrow_01.png')" mode="aspectFit" class="w-[22rpx] h-[22rpx] pb-[1rpx]" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="flex flex-col pt-[28rpx] pb-[30rpx] px-[24rpx]">
|
||||
<view class="flex items-center justify-between pb-[16rpx]">
|
||||
<text class="text-[22rpx] ml-[2rpx] leading-[1.4] text-[#FFFBE2]" v-if="upgradeGrowth > 0">还差{{upgradeGrowth}}成长值即可升级为{{ list[afterCurrIndex].level_name }}</text>
|
||||
<text class="text-[22rpx] ml-[2rpx] text-[#FFFBE2]" v-else>恭喜您升级为最高等级</text>
|
||||
<view class="flex items-center" @click="toLink('/app/pages/member/level')">
|
||||
<text class="nc-iconfont nc-icon-a-bangzhuV6xx-36 !text-[22rpx] text-[#FFFBE2]"></text>
|
||||
<text class="text-[22rpx] text-[#FFFBE2] ml-[6rpx] leading-[24rpx]">规则</text>
|
||||
<view class="ml-[2rpx] -mb-[4rpx] text-[#FFFBE2] !text-[24rpx] nc-iconfont nc-icon-youV6xx"></view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="overflow-hidden rounded-[20rpx]">
|
||||
<progress :percent="progress()" activeColor="#fff" backgroundColor="rgba(255,255,255,0.4)" stroke-width="4" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
@ -162,7 +206,7 @@
|
||||
}else{
|
||||
// 当前会员没有会员等级,则展示会员等级中的最后一个等级
|
||||
info.value.member_level_name = list[0].level_name;
|
||||
upgradeGrowth.value = list[0].growth;
|
||||
upgradeGrowth.value = list[0].growth - info.value.growth;
|
||||
afterCurrIndex.value = 0;
|
||||
currIndex.value = 1;
|
||||
}
|
||||
@ -215,4 +259,22 @@
|
||||
right: 0;
|
||||
}
|
||||
}
|
||||
.style-4{
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
.style-btn{
|
||||
background: linear-gradient(#FFF3C1, #FFEFB0);
|
||||
}
|
||||
}
|
||||
|
||||
.style-5{
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
.content-head{
|
||||
background: linear-gradient(to right, rgba(255,255,255,0), rgba(255,255,255,0.14));
|
||||
}
|
||||
.style-btn{
|
||||
background: linear-gradient(#FFFFFF, #FFF8CC);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -3,13 +3,13 @@
|
||||
<view :style="maskLayer"></view>
|
||||
<view class="diy-notice relative overflow-hidden">
|
||||
<view class="flex items-center pl-[28rpx] p-[22rpx]">
|
||||
<template v-if="diyComponent.noticeType == 'img'">
|
||||
<view v-if="diyComponent.noticeType == 'img'" class="min-w-[60rpx] flex items-center">
|
||||
<template v-if="diyComponent.imgType == 'system'">
|
||||
<image v-if="diyComponent.systemUrl == 'style_1'" :src="img(`static/resource/images/diy/notice/${diyComponent.systemUrl}.png`)" class="h-[40rpx] max-w-[130rpx] mr-[20rpx] flex-shrink-0" mode="heightFix"/>
|
||||
<image v-if="diyComponent.systemUrl == 'style_1'" :src="img(`static/resource/images/diy/notice/${diyComponent.systemUrl}.png`)" class="h-[40rpx] w-[auto] mr-[20rpx] flex-shrink-0" mode="heightFix"/>
|
||||
<image v-else-if="diyComponent.systemUrl == 'style_2'" :src="img(`static/resource/images/diy/notice/${diyComponent.systemUrl}.png`)" class="w-[200rpx] mr-[20rpx] h-[30rpx] flex-shrink-0" mode="heightFix" />
|
||||
</template>
|
||||
<image v-else-if="diyComponent.imgType == 'diy'" :src="img(diyComponent.imageUrl || '')" class="w-[200rpx] h-[30rpx] mr-[20rpx] flex-shrink-0" mode="heightFix"/>
|
||||
</template>
|
||||
</view>
|
||||
<view v-if="diyComponent.noticeType == 'text' && diyComponent.noticeTitle" class="max-w-[128rpx] px-[12rpx] text-[26rpx] h-[40rpx] leading-[40rpx] text-[var(--primary-color)] bg-[var(--primary-color-light)] truncate rounded-[8rpx] mr-[20rpx] flex-shrink-0">{{ diyComponent.noticeTitle }}</view>
|
||||
<view class="flex-1 flex overflow-hidden horizontal-body" :id="'horizontal-body-'+diyComponent.id" :class="{'items-center':diyComponent.scrollWay == 'upDown'}">
|
||||
<!-- 横向滚动 -->
|
||||
@ -26,7 +26,7 @@
|
||||
<template v-if="diyComponent.scrollWay == 'upDown'">
|
||||
<swiper :vertical="true" :duration="500" autoplay="true" circular="true" class="flex-1">
|
||||
<swiper-item v-for="(item, index) in diyComponent.list" :key="index" @touchmove.prevent.stop>
|
||||
<text @click="toRedirect(item)" class="beyond-hiding using-hidden" :style="{ color: diyComponent.textColor, fontSize: diyComponent.fontSize * 2 + 'rpx', fontWeight: diyComponent.fontWeight }">
|
||||
<text @click="toRedirect(item)" class="beyond-hiding truncate" :style="{ color: diyComponent.textColor, fontSize: diyComponent.fontSize * 2 + 'rpx', fontWeight: diyComponent.fontWeight }">
|
||||
{{ item.text }}
|
||||
</text>
|
||||
</swiper-item>
|
||||
@ -37,18 +37,21 @@
|
||||
</view>
|
||||
|
||||
</view>
|
||||
|
||||
<u-popup :show="noticeShow" @close="noticeShow = false" mode="center" round="var(--rounded-big)" :safeAreaInsetBottom="false">
|
||||
<view @touchmove.prevent.stop>
|
||||
<view class="pt-[30rpx] pb-[24rpx] text-sm leading-none border-0 border-solid border-b-[2rpx] border-[#eee] flex items-center justify-between">
|
||||
<text class="ml-[30rpx] text-[#333] text-[30rpx] font-500">公告</text>
|
||||
<text class="mr-[20rpx] nc-iconfont nc-icon-guanbiV6xx text-[35rpx]" @click="noticeShow = false"></text>
|
||||
<view @touchmove.prevent.stop>
|
||||
<u-popup :show="noticeShow" @close="noticeShow = false" mode="center" round="var(--rounded-big)" :safeAreaInsetBottom="false">
|
||||
<view class="w-[570rpx] px-[32rpx] popup-common center">
|
||||
<view class="title">公告</view>
|
||||
<scroll-view :scroll-y="true" class="px-[30rpx] box-border h-[260rpx]">
|
||||
<block v-for="(item) in noticeContent.split('\n')">
|
||||
<view class="text-[28rpx] leading-[40rpx] mb-[20rpx]">{{ item }}</view>
|
||||
</block>
|
||||
</scroll-view>
|
||||
<view class="btn-wrap !pt-[40rpx]">
|
||||
<button class="primary-btn-bg w-[480rpx] h-[70rpx] text-[26rpx] leading-[70rpx] rounded-[35rpx] !text-[#fff] font-500" @click="noticeShow = false">我知道了</button>
|
||||
</view>
|
||||
</view>
|
||||
<scroll-view scroll-y="true" class="px-[30rpx] py-[30rpx] w-[580rpx] box-border h-[480rpx] text-[26rpx] text-[#333]">{{ noticeContent }}</scroll-view>
|
||||
<button @click="noticeShow = false" class="!mx-[80rpx] !mb-[60rpx] !w-auto !h-[70rpx] text-[26rpx] leading-[70rpx] rounded-full text-white !bg-[#ff4500] !text-[#fff]">我知道了</button>
|
||||
</view>
|
||||
</u-popup>
|
||||
|
||||
</u-popup>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
135
uni-app/src/app/components/diy/picture-show/index.vue
Normal file
135
uni-app/src/app/components/diy/picture-show/index.vue
Normal file
@ -0,0 +1,135 @@
|
||||
<template>
|
||||
<view :style="warpCss" class="flex justify-between">
|
||||
<view class="p-[20rpx] box-border overflow-hidden" :style="moduleOneCss">
|
||||
<view class="flex items-center pb-[30rpx] pt-[6rpx]" v-if="diyComponent.moduleOne.head.textImg || diyComponent.moduleOne.head.subText">
|
||||
<image class="h-[28rpx]" v-if="diyComponent.moduleOne.head.textImg" :src="img(diyComponent.moduleOne.head.textImg)" mode="heightFix"></image>
|
||||
<text class="w-[2rpx] mx-[10rpx] h-[22rpx]" v-if="diyComponent.moduleOne.head.textImg && diyComponent.moduleOne.head.subText" :style="{'backgroundColor': diyComponent.moduleOne.head.subTextColor}"></text>
|
||||
<text class="text-[22rpx] truncate max-w-[164rpx]" v-if="diyComponent.moduleOne.head.subText" :style="{color: diyComponent.moduleOne.head.subTextColor}">{{diyComponent.moduleOne.head.subText}}</text>
|
||||
</view>
|
||||
<view class="flex items-center">
|
||||
<view v-for="(item,index) in diyComponent.moduleOne.list" :key="index" class="flex flex-col items-center" :class="{'mr-[10rpx]': index == 0}" @click="diyStore.toRedirect(item.link)">
|
||||
<view class="bg-[#fff] flex items-center justify-center w-[148rpx] h-[148rpx] rounded-[12rpx] mb-[16rpx]">
|
||||
<image v-if="item.imageUrl" class="w-[102rpx] h-[102rpx]" :src="img(item.imageUrl)" mode="aspectFill"></image>
|
||||
<u-icon v-else name="photo" color="#999" size="50"></u-icon>
|
||||
</view>
|
||||
<view class="w-[132rpx] h-[44rpx] rounded-[30rpx] flex items-center justify-center text-[22rpx]" :style="moduleBtnCss(item)">{{item.btnTitle.text}}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="p-[20rpx] box-border overflow-hidden" :style="moduleTwoCss">
|
||||
<view class="flex items-center pb-[30rpx] pt-[6rpx]" v-if="diyComponent.moduleTwo.head.textImg || diyComponent.moduleTwo.head.subText">
|
||||
<image class="h-[28rpx] w-[auto]" v-if="diyComponent.moduleTwo.head.textImg" :src="img(diyComponent.moduleTwo.head.textImg)" mode="heightFix"></image>
|
||||
<text class="w-[2rpx] mx-[10rpx] h-[22rpx]" v-if="diyComponent.moduleTwo.head.textImg && diyComponent.moduleTwo.head.subText" :style="{'backgroundColor': diyComponent.moduleTwo.head.subTextColor}"></text>
|
||||
<text class="text-[22rpx] truncate max-w-[164rpx]" v-if="diyComponent.moduleTwo.head.subText" :style="{color: diyComponent.moduleTwo.head.subTextColor}">{{diyComponent.moduleTwo.head.subText}}</text>
|
||||
</view>
|
||||
<view class="flex items-center">
|
||||
<view v-for="(item,index) in diyComponent.moduleTwo.list" :key="index" class="flex flex-col items-center" :class="{'mr-[10rpx]': index == 0}" @click="diyStore.toRedirect(item.link)">
|
||||
<view class="bg-[#fff] flex items-center justify-center w-[148rpx] h-[148rpx] rounded-[12rpx] mb-[16rpx]">
|
||||
<image v-if="item.imageUrl" class="w-[102rpx] h-[102rpx]" :src="img(item.imageUrl)" mode="aspectFill"></image>
|
||||
<u-icon v-else name="photo" color="#999" size="50"></u-icon>
|
||||
</view>
|
||||
<view class="w-[132rpx] h-[44rpx] rounded-[30rpx] flex items-center justify-center text-[22rpx]" :style="moduleBtnCss(item)">{{item.btnTitle.text}}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
// 图片展播
|
||||
import { ref, reactive, computed, watch, onMounted, nextTick, getCurrentInstance } from 'vue';
|
||||
import { img } from '@/utils/common';
|
||||
import useDiyStore from '@/app/stores/diy';
|
||||
|
||||
const props = defineProps(['component', 'index', 'pullDownRefreshCount', 'global', 'scrollBool']);
|
||||
const diyStore = useDiyStore();
|
||||
const diyComponent = computed(() => {
|
||||
if (diyStore.mode == 'decorate') {
|
||||
return diyStore.value[props.index];
|
||||
} else {
|
||||
return props.component;
|
||||
}
|
||||
})
|
||||
|
||||
const warpCss = computed(() => {
|
||||
var style = '';
|
||||
if(diyComponent.value.componentStartBgColor) {
|
||||
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${diyComponent.value.componentGradientAngle},${diyComponent.value.componentStartBgColor},${diyComponent.value.componentEndBgColor});`;
|
||||
else style += 'background-color:' + diyComponent.value.componentStartBgColor + ';';
|
||||
}
|
||||
if (diyComponent.value.topRounded) style += 'border-top-left-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
|
||||
if (diyComponent.value.topRounded) style += 'border-top-right-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
|
||||
if (diyComponent.value.bottomRounded) style += 'border-bottom-left-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
|
||||
if (diyComponent.value.bottomRounded) style += 'border-bottom-right-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
|
||||
return style;
|
||||
})
|
||||
|
||||
const moduleOneCss = computed(() => {
|
||||
var style = '';
|
||||
if(diyComponent.value.moduleOne.listFrame) {
|
||||
if (diyComponent.value.moduleOne.listFrame.startColor && diyComponent.value.moduleOne.listFrame.endColor) style += `background:linear-gradient(${diyComponent.value.moduleOne.listFrame.startColor},${diyComponent.value.moduleOne.listFrame.endColor});`;
|
||||
}
|
||||
|
||||
if (diyComponent.value.moduleRounded.topRounded) style += 'border-top-left-radius:' + diyComponent.value.moduleRounded.topRounded * 2 + 'rpx;';
|
||||
if (diyComponent.value.moduleRounded.topRounded) style += 'border-top-right-radius:' + diyComponent.value.moduleRounded.topRounded * 2 + 'rpx;';
|
||||
if (diyComponent.value.moduleRounded.bottomRounded) style += 'border-bottom-left-radius:' + diyComponent.value.moduleRounded.bottomRounded * 2 + 'rpx;';
|
||||
if (diyComponent.value.moduleRounded.bottomRounded) style += 'border-bottom-right-radius:' + diyComponent.value.moduleRounded.bottomRounded * 2 + 'rpx;';
|
||||
|
||||
if(diyComponent.value.margin && diyComponent.value.margin.both) style += 'width: calc((100vw - ' + (diyComponent.value.margin.both*4) + 'rpx - 20rpx) / 2);'
|
||||
else style += 'width: calc((100vw - 20rpx) / 2 );'
|
||||
return style;
|
||||
})
|
||||
|
||||
const moduleTwoCss = computed(() => {
|
||||
var style = '';
|
||||
if(diyComponent.value.moduleTwo.listFrame) {
|
||||
if (diyComponent.value.moduleTwo.listFrame.startColor && diyComponent.value.moduleTwo.listFrame.endColor) style += `background:linear-gradient(${diyComponent.value.moduleTwo.listFrame.startColor},${diyComponent.value.moduleTwo.listFrame.endColor});`;
|
||||
}
|
||||
if (diyComponent.value.moduleRounded.topRounded) style += 'border-top-left-radius:' + diyComponent.value.moduleRounded.topRounded * 2 + 'rpx;';
|
||||
if (diyComponent.value.moduleRounded.topRounded) style += 'border-top-right-radius:' + diyComponent.value.moduleRounded.topRounded * 2 + 'rpx;';
|
||||
if (diyComponent.value.moduleRounded.bottomRounded) style += 'border-bottom-left-radius:' + diyComponent.value.moduleRounded.bottomRounded * 2 + 'rpx;';
|
||||
if (diyComponent.value.moduleRounded.bottomRounded) style += 'border-bottom-right-radius:' + diyComponent.value.moduleRounded.bottomRounded * 2 + 'rpx;';
|
||||
|
||||
if(diyComponent.value.margin && diyComponent.value.margin.both) style += 'width: calc((100vw - ' + (diyComponent.value.margin.both*4) + 'rpx - 20rpx) / 2);'
|
||||
else style += 'width: calc((100vw - 20rpx) / 2 );'
|
||||
return style;
|
||||
})
|
||||
|
||||
const moduleBtnCss = (data: any)=>{
|
||||
var style = '';
|
||||
if(data.btnTitle.color) {
|
||||
style += 'color:' + data.btnTitle.color + ';';
|
||||
}
|
||||
if (diyComponent.value.moduleTwo.listFrame.startColor && diyComponent.value.moduleTwo.listFrame.endColor) style += `background:linear-gradient(${data.btnTitle.startColor},${data.btnTitle.endColor});`;
|
||||
return style;
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.pullDownRefreshCount,
|
||||
(newValue, oldValue) => {
|
||||
// 处理下拉刷新业务
|
||||
}
|
||||
)
|
||||
|
||||
onMounted(() => {
|
||||
refresh();
|
||||
// 装修模式下刷新
|
||||
if (diyStore.mode == 'decorate') {
|
||||
watch(
|
||||
() => diyComponent.value,
|
||||
(newValue, oldValue) => {
|
||||
if (newValue && newValue.componentName == 'PictureShow') {
|
||||
refresh();
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
});
|
||||
|
||||
const refresh = ()=> {
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
@ -115,7 +115,7 @@
|
||||
// #endif
|
||||
/********* 自定义头部 - start ***********/
|
||||
const topTabarObj = topTabar()
|
||||
let param = topTabarObj.setTopTabbarParam({title:''})
|
||||
let param = topTabarObj.setTopTabbarParam({title:'',topStatusBar:{textColor: '#333'}})
|
||||
/********* 自定义头部 - end ***********/
|
||||
const headerHeight = computed(()=>{
|
||||
return Object.keys(menuButtonInfo).length ? pxToRpx(Number(menuButtonInfo.height)) + pxToRpx(menuButtonInfo.top) + pxToRpx(8)+'rpx':'auto'
|
||||
|
||||
@ -114,8 +114,8 @@
|
||||
if (option.type == 'mobile') {
|
||||
if (configStore.login.is_mobile) {
|
||||
type.value = option.type
|
||||
uni.getStorageSync('pid') && (Object.assign(formData, { pid: uni.getStorageSync('pid') }))
|
||||
}
|
||||
|
||||
} else if (option.type == 'username' && configStore.login.is_username) {
|
||||
type.value = option.type
|
||||
}
|
||||
|
||||
@ -324,7 +324,6 @@
|
||||
}
|
||||
.address-edit :deep(.u-form-item__body__right){
|
||||
display:flex;
|
||||
align-items: center;
|
||||
}
|
||||
.footer{
|
||||
height: calc(100rpx + var(--top-m) + var(--top-m) + constant(safe-area-inset-bottom)) !important;
|
||||
|
||||
@ -288,7 +288,8 @@
|
||||
|
||||
cashOutApply(applyData).then(res => {
|
||||
loading.value = false
|
||||
redirect({ url: '/app/pages/member/cash_out' })
|
||||
memberStore.getMemberInfo(()=>{redirect({ url: '/app/pages/member/cash_out' })})
|
||||
|
||||
}).catch(() => {
|
||||
loading.value = false
|
||||
})
|
||||
|
||||
@ -1,247 +0,0 @@
|
||||
<template>
|
||||
<view :style="themeColor()">
|
||||
<scroll-view scroll-y="true" class="bg-page h-screen">
|
||||
<!-- <view class="h-[88rpx]">
|
||||
<u-navbar title="添加地址" @rightClick="rightClick" :autoBack="true"></u-navbar>
|
||||
</view> -->
|
||||
<view class="h-[30rpx]"></view>
|
||||
<view class="m-[30rpx] mt-0 p-[30rpx] pt-[10rpx] rounded-md bg-white">
|
||||
<u-form labelPosition="left" :model="formData" labelWidth="200rpx" errorType='toast' :rules="rules" ref="formRef">
|
||||
<view class="mt-[10rpx]">
|
||||
<u-form-item :label="t('name')" prop="name" :border-bottom="true">
|
||||
<u-input v-model.trim="formData.name" border="none" clearable maxlength="25" :placeholder="t('namePlaceholder')"/>
|
||||
</u-form-item>
|
||||
</view>
|
||||
<view class="mt-[10rpx]">
|
||||
<u-form-item :label="t('mobile')" prop="mobile" :border-bottom="true">
|
||||
<u-input v-model="formData.mobile" border="none" clearable :placeholder="t('mobilePlaceholder')"/>
|
||||
</u-form-item>
|
||||
</view>
|
||||
<view class="mt-[10rpx]">
|
||||
<u-form-item :label="t('deliveryAddress')" prop="address_name" :border-bottom="true">
|
||||
<view class="flex justify-between flex-1" @click="chooseLocation">
|
||||
<view class="text-[15px]" :class="{ 'text-[#303133]': formData.area,'text-[#c3c4d5]':!formData.area }">{{formData.area ? formData.address_name : t('selectAddressPlaceholder')}}</view>
|
||||
<u-icon name="arrow-right" color="#c3c4d5"></u-icon>
|
||||
</view>
|
||||
</u-form-item>
|
||||
</view>
|
||||
<view class="mt-[10rpx]">
|
||||
<u-form-item :label="t('address')" prop="address" :border-bottom="true">
|
||||
<u-input v-model.trim="formData.address" border="none" clearable maxlength="120" :placeholder="t('addressPlaceholder')"/>
|
||||
</u-form-item>
|
||||
</view>
|
||||
<view class="mt-[10rpx]">
|
||||
<u-form-item :label="t('defaultAddress')" prop="name" :border-bottom="true" >
|
||||
<u-switch v-model="formData.is_default" size="20" :activeValue="1" :inactiveValue="0" activeColor="var(--primary-color)"/>
|
||||
</u-form-item>
|
||||
</view>
|
||||
<view class="mt-[40rpx]">
|
||||
<button class="!bg-[var(--primary-color)] !text-[#fff] h-[80rpx] leading-[80rpx] rounded-[100rpx] text-[28rpx]" :class="{'opacity-50': btnDisabled}" @click="save" :disabled="btnDisabled" :loading="operateLoading">{{t('save')}}</button>
|
||||
</view>
|
||||
</u-form>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<!-- 小程序隐私协议 -->
|
||||
<wx-privacy-popup ref="wxPrivacyPopupRef"></wx-privacy-popup>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import { redirect } from '@/utils/common'
|
||||
import { t } from '@/locale'
|
||||
import { addAddress, editAddress, getAddressInfo } from '@/app/api/member'
|
||||
import manifestJson from '@/manifest.json'
|
||||
import { getAddressByLatlng } from '@/app/api/system'
|
||||
|
||||
const type = ref('')
|
||||
const source = ref('')
|
||||
const btnDisabled = ref(false)
|
||||
|
||||
const formData = ref({
|
||||
id: 0,
|
||||
name: '',
|
||||
mobile: '',
|
||||
province_id: 0,
|
||||
city_id: 0,
|
||||
district_id: 0,
|
||||
lat: '',
|
||||
lng: '',
|
||||
address: '',
|
||||
address_name: '',
|
||||
full_address: '',
|
||||
is_default: 0,
|
||||
area: '',
|
||||
type: 'location_address'
|
||||
})
|
||||
|
||||
onLoad((option:any) => {
|
||||
if (option.id) {
|
||||
getAddressInfo(option.id).then(({ data }) => {
|
||||
if (data) {
|
||||
Object.assign(formData.value, data)
|
||||
formData.value.area = formData.value.full_address.replace(formData.value.address, '').replace(formData.value.address_name, '')
|
||||
}
|
||||
}).catch()
|
||||
} else if (option.name) {
|
||||
if (uni.getStorageSync('addressInfo')) {
|
||||
Object.assign(formData.value, uni.getStorageSync('addressInfo'))
|
||||
}
|
||||
formData.value.address = option.name;
|
||||
getAddress(option.latng);
|
||||
var tempArr = getQueryVariable('latng').split(',');
|
||||
formData.value.lat = tempArr[0];
|
||||
formData.value.lng = tempArr[1];
|
||||
}
|
||||
|
||||
type.value = option.type || ''
|
||||
source.value = option.source || ''
|
||||
})
|
||||
|
||||
const formRef = ref(null)
|
||||
|
||||
const rules = computed(() => {
|
||||
return {
|
||||
'address': {
|
||||
type: 'string',
|
||||
required: true,
|
||||
message: t('addressError'),
|
||||
trigger: ['blur', 'change'],
|
||||
},
|
||||
'name': {
|
||||
type: 'string',
|
||||
required: true,
|
||||
message: t('namePlaceholder'),
|
||||
trigger: ['blur', 'change'],
|
||||
},
|
||||
'mobile': [
|
||||
{
|
||||
type: 'string',
|
||||
required: true,
|
||||
message: t('mobilePlaceholder'),
|
||||
trigger: ['blur', 'change'],
|
||||
},
|
||||
{
|
||||
validator(rule, value, callback) {
|
||||
let mobile = /^1[3-9]\d{9}$/;
|
||||
if (!mobile.test(value)){
|
||||
callback(new Error(t('mobileError')))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
|
||||
const getQueryVariable = (variable:any)=> {
|
||||
var query = window.location.search.substring(1);
|
||||
var vars = query.split('&');
|
||||
for (var i = 0; i < vars.length; i++) {
|
||||
var pair = vars[i].split('=');
|
||||
if (pair[0] == variable) {
|
||||
return pair[1];
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取详细地址
|
||||
const getAddress = (latlng:any)=> {
|
||||
getAddressByLatlng({latlng}).then((res: any) => {
|
||||
if (res.data) {
|
||||
formData.value.full_address = '';
|
||||
formData.value.full_address += res.data.province != undefined ? res.data.province : '';
|
||||
formData.value.full_address += res.data.city != undefined ? '' + res.data.city : '';
|
||||
formData.value.full_address += res.data.district != undefined ? '' + res.data.district : '';
|
||||
|
||||
formData.value.address_name = formData.value.full_address.replace(/-/g,'');
|
||||
formData.value.area = res.data.full_address;
|
||||
|
||||
formData.value.province_id = res.data.province_id != undefined ? res.data.province_id : 0;
|
||||
formData.value.city_id = res.data.city_id != undefined ? res.data.city_id : 0;
|
||||
formData.value.district_id = res.data.district_id != undefined ? res.data.district_id : 0;
|
||||
|
||||
} else {
|
||||
uni.showToast({title: res.msg, icon: 'none'})
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
const operateLoading = ref(false)
|
||||
const save = ()=> {
|
||||
if (uni.$u.test.isEmpty(formData.value.area)) {
|
||||
uni.showToast({title: t('selectAddressPlaceholder'), icon: 'none'})
|
||||
return
|
||||
}
|
||||
|
||||
const save = formData.value.id ? editAddress : addAddress
|
||||
|
||||
formRef.value.validate().then(() => {
|
||||
if (operateLoading.value) return
|
||||
operateLoading.value = true
|
||||
|
||||
btnDisabled.value = true
|
||||
|
||||
formData.value.full_address = `${formData.value.area}${formData.value.address_name}${formData.value.address}`
|
||||
|
||||
save(formData.value).then((res) => {
|
||||
operateLoading.value = false
|
||||
uni.removeStorageSync('addressInfo');
|
||||
setTimeout(() => {
|
||||
btnDisabled.value = false
|
||||
redirect({
|
||||
url: '/app/pages/member/address',
|
||||
mode: 'redirectTo',
|
||||
param: {type: type.value, source: source.value}
|
||||
})
|
||||
}, 1000)
|
||||
}).catch(() => {
|
||||
operateLoading.value = false
|
||||
btnDisabled.value = false
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const chooseLocation = ()=> {
|
||||
// #ifdef MP
|
||||
uni.chooseLocation({
|
||||
success: (res) => {
|
||||
res.latitude && (formData.value.lat = res.latitude)
|
||||
res.longitude && (formData.value.lng = res.longitude)
|
||||
res.address && (formData.value.area = res.address)
|
||||
res.name && (formData.value.address_name = res.name)
|
||||
},
|
||||
fail: (res)=>{
|
||||
// 在隐私协议中没有声明chooseLocation:fail api作用域
|
||||
if(res.errMsg && res.errno) {
|
||||
if(res.errno == 104){
|
||||
let msg = '用户未授权隐私权限,选择位置失败';
|
||||
uni.showToast({title: msg, icon: 'none'})
|
||||
}else if(res.errno == 112){
|
||||
let msg = '隐私协议中未声明,打开地图选择位置失败';
|
||||
uni.showToast({title: msg, icon: 'none'})
|
||||
}else {
|
||||
uni.showToast({title: res.errMsg, icon: 'none'})
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
// #endif
|
||||
|
||||
// #ifdef H5
|
||||
var urlencode = formData.value;
|
||||
uni.setStorageSync('addressInfo', urlencode);
|
||||
let backurl = location.origin + location.pathname + '?type=' + type.value + '&source=' + source.value;
|
||||
window.location.href = 'https://apis.map.qq.com/tools/locpicker?search=1&type=0&backurl=' + encodeURIComponent(backurl) + '&key=' + manifestJson.h5.sdkConfigs.maps.qqmap.key + '&referer=myapp';
|
||||
// #endif
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
@ -4,7 +4,7 @@
|
||||
<view v-if="info.is_use">
|
||||
<view class="sigin-header">
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<view class="side-tab" :style="{top: topStyle}" @click="signPopup = true">
|
||||
<view v-if="info.rule_explain" class="side-tab" :style="{top: topStyle}" @click="signPopup = true">
|
||||
<text class="nc-iconfont nc-icon-a-meiriqiandaoV6xx-36 icon"></text>
|
||||
<text class="desc">签到规则</text>
|
||||
</view>
|
||||
@ -319,7 +319,7 @@ const getDayCounts = () => {
|
||||
}
|
||||
// 获取7天的日期签到
|
||||
const getWeekCounts = () =>{
|
||||
let now = `${state.curYear}-${state.curMonth+1 > 10 ? state.curMonth+1 : '0'+(state.curMonth+1)}-${state.curDate > 10 ? state.curDate : '0'+state.curDate }`
|
||||
let now = `${state.curYear}-${state.curMonth+1 > 9 ? state.curMonth+1 : '0'+(state.curMonth+1)}-${state.curDate > 9 ? state.curDate : '0'+state.curDate }`
|
||||
for (let i = state.curWeek - 1; i >= 0; i --) {
|
||||
const day = new Date(now).getDate() - i
|
||||
state.weekCount.push(day)
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<view class="w-screen h-screen bg-[#424040] text-right" :style="themeColor()">
|
||||
<image :src="img('static/resource/images/pay/invite_friends_share.png')" mode="heightFix" class="pt-[30rpx] pr-[30rpx] h-[200rpx]"/>
|
||||
<image :src="img('static/resource/images/pay/invite_friends_share.png')" mode="heightFix" class="pt-[30rpx] pr-[30rpx] h-[200rpx] w-[auto]"/>
|
||||
<view class="text-white font-bold pt-[30rpx] pr-[30rpx]">点击右上角跳转到浏览器打开</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
@ -1,56 +0,0 @@
|
||||
import { ref } from 'vue';
|
||||
|
||||
// 小程序无法在hook中使用页面级别生命周期,需单独传入: https://ask.dcloud.net.cn/question/161173
|
||||
// import { onPageScroll, onReachBottom, onPullDownRefresh} from '@dcloudio/uni-app';
|
||||
|
||||
/**
|
||||
* mescroll-body写在子组件时,需通过useMescrollComp补充子组件缺少的生命周期, 相当于vue2的mescroll-comp.js文件
|
||||
* 必须传入onPageScroll, onReachBottom
|
||||
* 当down.native为true时,需传入onPullDownRefresh
|
||||
*/
|
||||
function useMescrollComp(onPageScroll, onReachBottom, onPullDownRefresh){
|
||||
// 因为子组件无onPageScroll和onReachBottom的页面生命周期,需在页面传递进到子组件
|
||||
onPageScroll(e=>{
|
||||
handlePageScroll(e)
|
||||
})
|
||||
|
||||
onReachBottom(()=>{
|
||||
handleReachBottom()
|
||||
})
|
||||
|
||||
// 当down的native: true时, 还需传递此方法进到子组件
|
||||
onPullDownRefresh && onPullDownRefresh(()=>{
|
||||
handlePullDownRefresh()
|
||||
})
|
||||
|
||||
const mescrollItem = ref(null)
|
||||
|
||||
const handlePageScroll = (e)=>{
|
||||
const mescroll = getMescroll()
|
||||
mescroll && mescroll.onPageScroll(e);
|
||||
}
|
||||
|
||||
const handleReachBottom = ()=>{
|
||||
const mescroll = getMescroll()
|
||||
mescroll && mescroll.onReachBottom();
|
||||
}
|
||||
|
||||
const handlePullDownRefresh = ()=>{
|
||||
const mescroll = getMescroll()
|
||||
mescroll && mescroll.onPullDownRefresh();
|
||||
}
|
||||
|
||||
const getMescroll = ()=>{
|
||||
if(mescrollItem.value && mescrollItem.value.getMescroll){
|
||||
return mescrollItem.value.getMescroll()
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
return {
|
||||
mescrollItem,
|
||||
getMescroll
|
||||
}
|
||||
}
|
||||
|
||||
export default useMescrollComp
|
||||
@ -1,69 +0,0 @@
|
||||
import { ref } from 'vue';
|
||||
|
||||
// 小程序无法在hook中使用页面级别生命周期,需单独传入: https://ask.dcloud.net.cn/question/161173
|
||||
// import { onPageScroll, onReachBottom, onPullDownRefresh} from '@dcloudio/uni-app';
|
||||
|
||||
/** mescroll-more示例写在子组件时,需通过useMescrollMore补充子组件缺少的生命周期, 相当于vue2的mescroll-more.js文件 */
|
||||
function useMescrollMore(mescrollItems, onPageScroll, onReachBottom, onPullDownRefresh){
|
||||
// 当前tab下标
|
||||
const tabIndex = ref(0)
|
||||
|
||||
// 因为子组件无onPageScroll和onReachBottom的页面生命周期,需在页面传递进到子组件
|
||||
onPageScroll && onPageScroll(e=>{
|
||||
handlePageScroll(e)
|
||||
})
|
||||
|
||||
onReachBottom && onReachBottom(()=>{
|
||||
handleReachBottom()
|
||||
})
|
||||
|
||||
// 当down的native: true时, 还需传递此方法进到子组件
|
||||
onPullDownRefresh && onPullDownRefresh(()=>{
|
||||
handlePullDownRefresh()
|
||||
})
|
||||
|
||||
const handlePageScroll = (e)=>{
|
||||
let mescroll = getMescroll(tabIndex.value);
|
||||
mescroll && mescroll.onPageScroll(e);
|
||||
}
|
||||
const handleReachBottom = ()=>{
|
||||
let mescroll = getMescroll(tabIndex.value);
|
||||
mescroll && mescroll.onReachBottom();
|
||||
}
|
||||
|
||||
const handlePullDownRefresh = ()=>{
|
||||
let mescroll = getMescroll(tabIndex.value);
|
||||
mescroll && mescroll.onPullDownRefresh();
|
||||
}
|
||||
|
||||
// 根据下标获取对应子组件的mescroll
|
||||
const getMescroll = (i)=>{
|
||||
if (mescrollItems && mescrollItems[i]) {
|
||||
return mescrollItems[i].value.getMescroll()
|
||||
} else{
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
// 切换tab,恢复滚动条位置
|
||||
const scrollToLastY = ()=>{
|
||||
let mescroll = getMescroll(tabIndex.value);
|
||||
if(mescroll){
|
||||
// 恢复上次滚动条的位置
|
||||
let y = mescroll.getScrollTop()
|
||||
mescroll.scrollTo(y, 0)
|
||||
// 再次恢复上次滚动条的位置, 确保元素已渲染
|
||||
setTimeout(()=>{
|
||||
mescroll.scrollTo(y, 0)
|
||||
},20)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
tabIndex,
|
||||
getMescroll,
|
||||
scrollToLastY
|
||||
}
|
||||
}
|
||||
|
||||
export default useMescrollMore
|
||||
@ -1,47 +0,0 @@
|
||||
/*下拉刷新--标语*/
|
||||
.mescroll-downwarp .downwarp-slogan{
|
||||
display: block;
|
||||
width: 420rpx;
|
||||
height: 168rpx;
|
||||
margin: auto;
|
||||
}
|
||||
/*下拉刷新--向下进度动画*/
|
||||
.mescroll-downwarp .downwarp-progress{
|
||||
display: inline-block;
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
border: none;
|
||||
margin: auto;
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
background-image: url(https://www.mescroll.com/img/beibei/mescroll-progress.png);
|
||||
transition: all 300ms;
|
||||
}
|
||||
/*下拉刷新--进度条*/
|
||||
.mescroll-downwarp .downwarp-loading{
|
||||
display: inline-block;
|
||||
width: 32rpx;
|
||||
height: 32rpx;
|
||||
border-radius: 50%;
|
||||
border: 2rpx solid #FF8095;
|
||||
border-bottom-color: transparent;
|
||||
}
|
||||
/*下拉刷新--吉祥物*/
|
||||
.mescroll-downwarp .downwarp-mascot{
|
||||
position: absolute;
|
||||
right: 16rpx;
|
||||
bottom: 0;
|
||||
width: 100rpx;
|
||||
height: 100rpx;
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
animation: animMascot .6s steps(1,end) infinite;
|
||||
}
|
||||
@keyframes animMascot {
|
||||
0% {background-image: url(https://www.mescroll.com/img/beibei/mescroll-bb1.png)}
|
||||
25% {background-image: url(https://www.mescroll.com/img/beibei/mescroll-bb2.png)}
|
||||
50% {background-image: url(https://www.mescroll.com/img/beibei/mescroll-bb3.png)}
|
||||
75% {background-image: url(https://www.mescroll.com/img/beibei/mescroll-bb4.png)}
|
||||
100% {background-image: url(https://www.mescroll.com/img/beibei/mescroll-bb1.png)}
|
||||
}
|
||||
@ -1,39 +0,0 @@
|
||||
<!-- 下拉刷新区域 -->
|
||||
<template>
|
||||
<view v-if="mOption.use" class="mescroll-downwarp" :style="{'background':mOption.bgColor,'color':mOption.textColor}">
|
||||
<view class="downwarp-content">
|
||||
<image class="downwarp-slogan" src="https://www.mescroll.com/img/beibei/mescroll-slogan.jpg?v=1" mode="widthFix"/>
|
||||
<view v-if="isDownLoading" class="downwarp-loading mescroll-rotate"></view>
|
||||
<view v-else class="downwarp-progress" :style="{'transform':downRotate}"></view>
|
||||
<view class="downwarp-mascot"></view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
option: Object , // down的配置项
|
||||
type: Number // 下拉状态(inOffset:1, outOffset:2, showLoading:3, endDownScroll:4)
|
||||
},
|
||||
computed: {
|
||||
// 支付宝小程序需写成计算属性,prop定义default仍报错
|
||||
mOption(){
|
||||
return this.option || {}
|
||||
},
|
||||
// 是否在加载中
|
||||
isDownLoading(){
|
||||
return this.type === 3
|
||||
},
|
||||
// 旋转的角度
|
||||
downRotate(){
|
||||
return this.type === 2 ? 'rotate(180deg)' : 'rotate(0deg)'
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
@import "../../../mescroll-uni/components/mescroll-down.css";
|
||||
@import "./mescroll-down.css";
|
||||
</style>
|
||||
@ -1,360 +0,0 @@
|
||||
<template>
|
||||
<view
|
||||
class="mescroll-body mescroll-render-touch"
|
||||
:style="{'minHeight':minHeight, 'padding-top': padTop, 'padding-bottom': padBottom}"
|
||||
:class="{'mescorll-sticky': sticky}"
|
||||
@touchstart="wxsBiz.touchstartEvent"
|
||||
@touchmove="wxsBiz.touchmoveEvent"
|
||||
@touchend="wxsBiz.touchendEvent"
|
||||
@touchcancel="wxsBiz.touchendEvent"
|
||||
:change:prop="wxsBiz.propObserver"
|
||||
:prop="wxsProp"
|
||||
>
|
||||
|
||||
<!-- 状态栏 -->
|
||||
<view v-if="topbar&&statusBarHeight" class="mescroll-topbar" :style="{height: statusBarHeight+'px', background: topbar}"></view>
|
||||
|
||||
<view class="mescroll-body-content mescroll-wxs-content" :style="{ transform: translateY, transition: transition }" :change:prop="wxsBiz.callObserver" :prop="callProp">
|
||||
<!-- 下拉加载区域 (支付宝小程序子组件传参给子子组件仍报单项数据流的异常,暂时不通过mescroll-down组件实现)-->
|
||||
<!-- <mescroll-down :option="mescroll.optDown" :type="downLoadType"></mescroll-down> -->
|
||||
<view v-if="mescroll.optDown.use" class="mescroll-downwarp" :style="{'background':mescroll.optDown.bgColor,'color':mescroll.optDown.textColor}">
|
||||
<view class="downwarp-content">
|
||||
<image class="downwarp-slogan" src="https://www.mescroll.com/img/beibei/mescroll-slogan.jpg?v=1" mode="widthFix"/>
|
||||
<view v-if="isDownLoading" class="downwarp-loading mescroll-rotate"></view>
|
||||
<view v-else class="downwarp-progress" :style="{'transform':downRotate}"></view>
|
||||
<view class="downwarp-mascot"></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 列表内容 -->
|
||||
<slot></slot>
|
||||
|
||||
<!-- 空布局 -->
|
||||
<mescroll-empty v-if="isShowEmpty" :option="mescroll.optUp.empty" @emptyclick="emptyClick"></mescroll-empty>
|
||||
|
||||
<!-- 上拉加载区域 (下拉刷新时不显示, 支付宝小程序子组件传参给子子组件仍报单项数据流的异常,暂时不通过mescroll-up组件实现)-->
|
||||
<!-- <mescroll-up v-if="mescroll.optUp.use && !isDownLoading && upLoadType!==3" :option="mescroll.optUp" :type="upLoadType"></mescroll-up> -->
|
||||
<view v-if="mescroll.optUp.use && !isDownLoading && upLoadType!==3" class="mescroll-upwarp" :style="{'background':mescroll.optUp.bgColor,'color':mescroll.optUp.textColor}">
|
||||
<!-- 加载中 (此处不能用v-if,否则android小程序快速上拉可能会不断触发上拉回调) -->
|
||||
<view v-show="upLoadType===1">
|
||||
<view class="upwarp-progress mescroll-rotate" :style="{'border-color':mescroll.optUp.textColor}"></view>
|
||||
<view class="upwarp-tip">{{ mescroll.optUp.textLoading }}</view>
|
||||
</view>
|
||||
<!-- 无数据 -->
|
||||
<view v-if="upLoadType===2" class="upwarp-nodata">{{ mescroll.optUp.textNoMore }}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部是否偏移TabBar的高度(仅H5端生效) -->
|
||||
<!-- #ifdef H5 -->
|
||||
<view v-if="bottombar && windowBottom>0" class="mescroll-bottombar" :style="{height: windowBottom+'px'}"></view>
|
||||
<!-- #endif -->
|
||||
|
||||
<!-- 适配iPhoneX -->
|
||||
<view v-if="safearea" class="mescroll-safearea"></view>
|
||||
|
||||
<!-- 回到顶部按钮 (fixed元素需写在transform外面,防止降级为absolute)-->
|
||||
<mescroll-top v-model="isShowToTop" :option="mescroll.optUp.toTop" @click="toTopClick"></mescroll-top>
|
||||
|
||||
<!-- #ifdef MP-WEIXIN || MP-QQ || APP-PLUS || H5 -->
|
||||
<!-- renderjs的数据载体,不可写在mescroll-downwarp内部,避免use为false时,载体丢失,无法更新数据 -->
|
||||
<view :change:prop="renderBiz.propObserver" :prop="wxsProp"></view>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<!-- 微信小程序, QQ小程序, app, h5使用wxs -->
|
||||
<!-- #ifdef MP-WEIXIN || MP-QQ || APP-PLUS || H5 -->
|
||||
<script src="../../mescroll-uni/wxs/wxs.wxs" module="wxsBiz" lang="wxs"></script>
|
||||
<!-- #endif -->
|
||||
|
||||
<!-- app, h5使用renderjs -->
|
||||
<!-- #ifdef APP-PLUS || H5 -->
|
||||
<script module="renderBiz" lang="renderjs">
|
||||
import renderBiz from '../../mescroll-uni/wxs/renderjs.js';
|
||||
export default {
|
||||
mixins: [renderBiz]
|
||||
}
|
||||
</script>
|
||||
<!-- #endif -->
|
||||
|
||||
<script>
|
||||
import MeScroll from '../../mescroll-uni/mescroll-uni.js';
|
||||
import MescrollTop from '../../mescroll-uni/components/mescroll-top.vue';
|
||||
import WxsMixin from '../../mescroll-uni/wxs/mixins.js';
|
||||
import mescrollI18n from '../../mescroll-uni/mescroll-i18n.js';
|
||||
import GlobalOption from './mescroll-uni-option.js';
|
||||
|
||||
export default {
|
||||
mixins: [WxsMixin],
|
||||
components: {
|
||||
MescrollTop
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
mescroll: null, // mescroll实例
|
||||
downHight: 0, //下拉刷新: 容器高度
|
||||
downLoadType: 0, // 下拉刷新状态: 0(loading前), 1(inOffset), 2(outOffset), 3(showLoading), 4(endDownScroll)
|
||||
upLoadType: 0, // 上拉加载状态:0(loading前),1(loading中),2(没有更多了,显示END文本提示),3(没有更多了,不显示END文本提示)
|
||||
isShowEmpty: false, // 是否显示空布局
|
||||
isShowToTop: false, // 是否显示回到顶部按钮
|
||||
windowHeight: 0, // 可使用窗口的高度
|
||||
windowBottom: 0, // 可使用窗口的底部位置
|
||||
statusBarHeight: 0 // 状态栏高度
|
||||
};
|
||||
},
|
||||
props: {
|
||||
down: Object, // 下拉刷新的参数配置
|
||||
up: Object, // 上拉加载的参数配置
|
||||
i18n: Object, // 国际化的参数配置
|
||||
top: [String, Number], // 下拉布局往下的偏移量 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx, 百分比则相对于windowHeight)
|
||||
topbar: [Boolean, String], // top的偏移量是否加上状态栏高度, 默认false (使用场景:取消原生导航栏时,配置此项可留出状态栏的占位, 支持传入字符串背景,如色值,背景图,渐变)
|
||||
bottom: [String, Number], // 上拉布局往上的偏移量 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx, 百分比则相对于windowHeight)
|
||||
safearea: Boolean, // bottom的偏移量是否加上底部安全区的距离, 默认false (需要适配iPhoneX时使用)
|
||||
height: [String, Number], // 指定mescroll最小高度,默认windowHeight,使列表不满屏仍可下拉
|
||||
bottombar:{ // 底部是否偏移TabBar的高度(默认仅在H5端的tab页生效)
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
sticky: Boolean // 是否支持sticky,默认false; 当值配置true时,需避免在mescroll-body标签前面加非定位的元素,否则下拉区域无法会隐藏
|
||||
},
|
||||
computed: {
|
||||
// mescroll最小高度,默认windowHeight,使列表不满屏仍可下拉
|
||||
minHeight(){
|
||||
return this.toPx(this.height || '100%') + 'px'
|
||||
},
|
||||
// 下拉布局往下偏移的距离 (px)
|
||||
numTop() {
|
||||
return this.toPx(this.top)
|
||||
},
|
||||
padTop() {
|
||||
return this.numTop + 'px';
|
||||
},
|
||||
// 上拉布局往上偏移 (px)
|
||||
numBottom() {
|
||||
return this.toPx(this.bottom);
|
||||
},
|
||||
padBottom() {
|
||||
return this.numBottom + 'px';
|
||||
},
|
||||
// 是否为重置下拉的状态
|
||||
isDownReset() {
|
||||
return this.downLoadType === 3 || this.downLoadType === 4;
|
||||
},
|
||||
// 过渡
|
||||
transition() {
|
||||
return this.isDownReset ? 'transform 300ms' : '';
|
||||
},
|
||||
translateY() {
|
||||
return this.downHight > 0 ? 'translateY(' + this.downHight + 'px)' : ''; // transform会使fixed失效,需注意把fixed元素写在mescroll之外
|
||||
},
|
||||
// 是否在加载中
|
||||
isDownLoading(){
|
||||
return this.downLoadType === 3
|
||||
},
|
||||
// 旋转的角度
|
||||
downRotate(){
|
||||
return this.downLoadType === 2 ? 'rotate(180deg)' : 'rotate(0deg)'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
//number,rpx,upx,px,% --> px的数值
|
||||
toPx(num) {
|
||||
if (typeof num === 'string') {
|
||||
if (num.indexOf('px') !== -1) {
|
||||
if (num.indexOf('rpx') !== -1) {
|
||||
// "10rpx"
|
||||
num = num.replace('rpx', '');
|
||||
} else if (num.indexOf('upx') !== -1) {
|
||||
// "10upx"
|
||||
num = num.replace('upx', '');
|
||||
} else {
|
||||
// "10px"
|
||||
return Number(num.replace('px', ''));
|
||||
}
|
||||
} else if (num.indexOf('%') !== -1) {
|
||||
// 传百分比,则相对于windowHeight,传"10%"则等于windowHeight的10%
|
||||
let rate = Number(num.replace('%', '')) / 100;
|
||||
return this.windowHeight * rate;
|
||||
}
|
||||
}
|
||||
return num ? uni.upx2px(Number(num)) : 0;
|
||||
},
|
||||
// 点击空布局的按钮回调
|
||||
emptyClick() {
|
||||
this.$emit('emptyclick', this.mescroll);
|
||||
},
|
||||
// 点击回到顶部的按钮回调
|
||||
toTopClick() {
|
||||
this.mescroll.scrollTo(0, this.mescroll.optUp.toTop.duration); // 执行回到顶部
|
||||
this.$emit('topclick', this.mescroll); // 派发点击回到顶部按钮的回调
|
||||
}
|
||||
},
|
||||
// 使用created初始化mescroll对象; 如果用mounted部分css样式编译到H5会失效
|
||||
created() {
|
||||
let vm = this;
|
||||
|
||||
let diyOption = {
|
||||
// 下拉刷新的配置
|
||||
down: {
|
||||
inOffset() {
|
||||
vm.downLoadType = 1; // 下拉的距离进入offset范围内那一刻的回调 (自定义mescroll组件时,此行不可删)
|
||||
},
|
||||
outOffset() {
|
||||
vm.downLoadType = 2; // 下拉的距离大于offset那一刻的回调 (自定义mescroll组件时,此行不可删)
|
||||
},
|
||||
onMoving(mescroll, rate, downHight) {
|
||||
// 下拉过程中的回调,滑动过程一直在执行;
|
||||
vm.downHight = downHight; // 设置下拉区域的高度 (自定义mescroll组件时,此行不可删)
|
||||
},
|
||||
showLoading(mescroll, downHight) {
|
||||
vm.downLoadType = 3; // 显示下拉刷新进度的回调 (自定义mescroll组件时,此行不可删)
|
||||
vm.downHight = downHight; // 设置下拉区域的高度 (自定义mescroll组件时,此行不可删)
|
||||
},
|
||||
endDownScroll() {
|
||||
vm.downLoadType = 4; // 结束下拉 (自定义mescroll组件时,此行不可删)
|
||||
vm.downHight = 0; // 设置下拉区域的高度 (自定义mescroll组件时,此行不可删)
|
||||
if(vm.downResetTimer) {clearTimeout(vm.downResetTimer); vm.downResetTimer = null} // 移除重置倒计时
|
||||
vm.downResetTimer = setTimeout(()=>{ // 过渡动画执行完毕后,需重置为0的状态,避免下次inOffset不及时显示textInOffset
|
||||
if(vm.downLoadType === 4) vm.downLoadType = 0
|
||||
},300)
|
||||
},
|
||||
// 派发下拉刷新的回调
|
||||
callback: function(mescroll) {
|
||||
vm.$emit('down', mescroll);
|
||||
}
|
||||
},
|
||||
// 上拉加载的配置
|
||||
up: {
|
||||
// 显示加载中的回调
|
||||
showLoading() {
|
||||
vm.upLoadType = 1;
|
||||
},
|
||||
// 显示无更多数据的回调
|
||||
showNoMore() {
|
||||
vm.upLoadType = 2;
|
||||
},
|
||||
// 隐藏上拉加载的回调
|
||||
hideUpScroll(mescroll) {
|
||||
vm.upLoadType = mescroll.optUp.hasNext ? 0 : 3;
|
||||
},
|
||||
// 空布局
|
||||
empty: {
|
||||
onShow(isShow) {
|
||||
// 显示隐藏的回调
|
||||
vm.isShowEmpty = isShow;
|
||||
}
|
||||
},
|
||||
// 回到顶部
|
||||
toTop: {
|
||||
onShow(isShow) {
|
||||
// 显示隐藏的回调
|
||||
vm.isShowToTop = isShow;
|
||||
}
|
||||
},
|
||||
// 派发上拉加载的回调
|
||||
callback: function(mescroll) {
|
||||
vm.$emit('up', mescroll);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let i18nType = mescrollI18n.getType() // 当前语言类型
|
||||
let i18nOption = {type: i18nType} // 国际化配置
|
||||
MeScroll.extend(i18nOption, vm.i18n) // 具体页面的国际化配置
|
||||
MeScroll.extend(i18nOption, GlobalOption.i18n) // 全局的国际化配置
|
||||
MeScroll.extend(diyOption, i18nOption[i18nType]); // 混入国际化配置
|
||||
MeScroll.extend(diyOption, {down:GlobalOption.down, up:GlobalOption.up}); // 混入全局的配置
|
||||
let myOption = JSON.parse(JSON.stringify({down: vm.down,up: vm.up})); // 深拷贝,避免对props的影响
|
||||
MeScroll.extend(myOption, diyOption); // 混入具体界面的配置
|
||||
|
||||
// 初始化MeScroll对象
|
||||
vm.mescroll = new MeScroll(myOption, true); // 传入true,标记body为滚动区域
|
||||
// 挂载语言包
|
||||
vm.mescroll.i18n = i18nOption;
|
||||
// init回调mescroll对象
|
||||
vm.$emit('init', vm.mescroll);
|
||||
|
||||
// 设置高度
|
||||
const sys = uni.getSystemInfoSync();
|
||||
if (sys.windowHeight) vm.windowHeight = sys.windowHeight;
|
||||
if (sys.windowBottom) vm.windowBottom = sys.windowBottom;
|
||||
if (sys.statusBarHeight) vm.statusBarHeight = sys.statusBarHeight;
|
||||
// 使down的bottomOffset生效
|
||||
vm.mescroll.setBodyHeight(sys.windowHeight);
|
||||
|
||||
// 因为使用的是page的scroll,这里需自定义scrollTo
|
||||
vm.mescroll.resetScrollTo((y, t) => {
|
||||
if(typeof y === 'string'){
|
||||
// 滚动到指定view (y为css选择器)
|
||||
setTimeout(()=>{ // 延时确保view已渲染; 不使用$nextTick
|
||||
let selector;
|
||||
if(y.indexOf('#')==-1 && y.indexOf('.')==-1){
|
||||
selector = '#'+y // 不带#和. 则默认为id选择器
|
||||
}else{
|
||||
selector = y
|
||||
// #ifdef APP-PLUS || H5 || MP-ALIPAY || MP-DINGTALK
|
||||
if(y.indexOf('>>>')!=-1){ // 不支持跨自定义组件的后代选择器 (转为普通的选择器即可跨组件查询)
|
||||
selector = y.split('>>>')[1].trim()
|
||||
}
|
||||
// #endif
|
||||
}
|
||||
uni.createSelectorQuery().select(selector).boundingClientRect(function(rect){
|
||||
if (rect) {
|
||||
let top = rect.top
|
||||
top += vm.mescroll.getScrollTop()
|
||||
uni.pageScrollTo({
|
||||
scrollTop: top,
|
||||
duration: t
|
||||
})
|
||||
} else{
|
||||
console.error(selector + ' does not exist');
|
||||
}
|
||||
}).exec()
|
||||
},30)
|
||||
} else{
|
||||
// 滚动到指定位置 (y必须为数字)
|
||||
uni.pageScrollTo({
|
||||
scrollTop: y,
|
||||
duration: t
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
// 具体的界面如果不配置up.toTop.safearea,则取本vue的safearea值
|
||||
if (vm.up && vm.up.toTop && vm.up.toTop.safearea != null) {} else {
|
||||
vm.mescroll.optUp.toTop.safearea = vm.safearea;
|
||||
}
|
||||
|
||||
// 全局配置监听
|
||||
uni.$on("setMescrollGlobalOption", options=>{
|
||||
if(!options) return;
|
||||
let i18nType = options.i18n ? options.i18n.type : null
|
||||
if(i18nType && vm.mescroll.i18n.type != i18nType){
|
||||
vm.mescroll.i18n.type = i18nType
|
||||
mescrollI18n.setType(i18nType)
|
||||
MeScroll.extend(options, vm.mescroll.i18n[i18nType])
|
||||
}
|
||||
if(options.down){
|
||||
let down = MeScroll.extend({}, options.down)
|
||||
vm.mescroll.optDown = MeScroll.extend(down, vm.mescroll.optDown)
|
||||
}
|
||||
if(options.up){
|
||||
let up = MeScroll.extend({}, options.up)
|
||||
vm.mescroll.optUp = MeScroll.extend(up, vm.mescroll.optUp)
|
||||
}
|
||||
})
|
||||
},
|
||||
destroyed() {
|
||||
// 注销全局配置监听
|
||||
uni.$off("setMescrollGlobalOption")
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
@import "../../mescroll-body/mescroll-body.css";
|
||||
@import "../../mescroll-uni/components/mescroll-down.css";
|
||||
@import "../../mescroll-uni/components/mescroll-up.css";
|
||||
@import "./components/mescroll-down.css";
|
||||
</style>
|
||||
@ -1,51 +0,0 @@
|
||||
import { img } from '@/utils/common';
|
||||
|
||||
// mescroll-uni和mescroll-body 的全局配置
|
||||
const GlobalOption = {
|
||||
down: {
|
||||
// 其他down的配置参数也可以写,这里只展示了常用的配置:
|
||||
offset: uni.upx2px(140), // 在列表顶部,下拉大于140upx,松手即可触发下拉刷新的回调
|
||||
native: false // 是否使用系统自带的下拉刷新; 默认false; 仅在mescroll-body生效 (值为true时,还需在pages配置enablePullDownRefresh:true;详请参考mescroll-native的案例)
|
||||
},
|
||||
up: {
|
||||
// 其他up的配置参数也可以写,这里只展示了常用的配置:
|
||||
offset: 150, // 距底部多远时,触发upCallback
|
||||
toTop: {
|
||||
// 回到顶部按钮,需配置src才显示
|
||||
src: "https://www.mescroll.com/img/mescroll-totop.png", // 图片路径 (建议放入static目录, 如 /static/img/mescroll-totop.png )
|
||||
offset: 1000, // 列表滚动多少距离才显示回到顶部按钮,默认1000px
|
||||
right: 20, // 到右边的距离, 默认20 (支持"20rpx", "20px", "20%"格式的值, 纯数字则默认单位rpx)
|
||||
bottom: 120, // 到底部的距离, 默认120 (支持"20rpx", "20px", "20%"格式的值, 纯数字则默认单位rpx)
|
||||
width: 72 // 回到顶部图标的宽度, 默认72 (支持"20rpx", "20px", "20%"格式的值, 纯数字则默认单位rpx)
|
||||
},
|
||||
empty: {
|
||||
use: true, // 是否显示空布局
|
||||
icon: img("static/resource/images/system/empty.png") // 图标路径 (建议放入static目录, 如 /static/img/mescroll-empty.png )
|
||||
}
|
||||
},
|
||||
// 国际化配置
|
||||
i18n: {
|
||||
// 中文
|
||||
zh: {
|
||||
up: {
|
||||
textLoading: '加载中 ...', // 加载中的提示文本
|
||||
textNoMore: '', // 没有更多数据的提示文本
|
||||
empty: {
|
||||
tip: '~ 暂无相关数据 ~' // 空提示
|
||||
}
|
||||
}
|
||||
},
|
||||
// 英文
|
||||
en: {
|
||||
up: {
|
||||
textLoading: 'loading ...',
|
||||
textNoMore: '',
|
||||
empty: {
|
||||
tip: '~ absolutely empty ~'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default GlobalOption
|
||||
@ -1,437 +0,0 @@
|
||||
<template>
|
||||
<view class="mescroll-uni-warp">
|
||||
<scroll-view :id="viewId" class="mescroll-uni" :class="{'mescroll-uni-fixed':isFixed}" :style="{'height':scrollHeight,'padding-top':padTop,'padding-bottom':padBottom,'top':fixedTop,'bottom':fixedBottom}" :scroll-top="scrollTop" :scroll-with-animation="scrollAnim" @scroll="scroll" :scroll-y='scrollable' :enable-back-to-top="true" :throttle="false">
|
||||
<view class="mescroll-uni-content mescroll-render-touch"
|
||||
@touchstart="wxsBiz.touchstartEvent"
|
||||
@touchmove="wxsBiz.touchmoveEvent"
|
||||
@touchend="wxsBiz.touchendEvent"
|
||||
@touchcancel="wxsBiz.touchendEvent"
|
||||
:change:prop="wxsBiz.propObserver"
|
||||
:prop="wxsProp">
|
||||
|
||||
<!-- 状态栏 -->
|
||||
<view v-if="topbar&&statusBarHeight" class="mescroll-topbar" :style="{height: statusBarHeight+'px', background: topbar}"></view>
|
||||
|
||||
<view class="mescroll-wxs-content" :style="{'transform': translateY, 'transition': transition}" :change:prop="wxsBiz.callObserver" :prop="callProp">
|
||||
<!-- 下拉加载区域 (支付宝小程序子组件传参给子子组件仍报单项数据流的异常,暂时不通过mescroll-down组件实现)-->
|
||||
<!-- <mescroll-down :option="mescroll.optDown" :type="downLoadType"></mescroll-down> -->
|
||||
<view v-if="mescroll.optDown.use" class="mescroll-downwarp" :style="{'background':mescroll.optDown.bgColor,'color':mescroll.optDown.textColor}">
|
||||
<view class="downwarp-content">
|
||||
<image class="downwarp-slogan" src="https://www.mescroll.com/img/beibei/mescroll-slogan.jpg?v=1" mode="widthFix"/>
|
||||
<view v-if="isDownLoading" class="downwarp-loading mescroll-rotate"></view>
|
||||
<view v-else class="downwarp-progress" :style="{'transform':downRotate}"></view>
|
||||
<view class="downwarp-mascot"></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 列表内容 -->
|
||||
<slot></slot>
|
||||
|
||||
<!-- 空布局 -->
|
||||
<mescroll-empty v-if="isShowEmpty" :option="mescroll.optUp.empty" @emptyclick="emptyClick"></mescroll-empty>
|
||||
|
||||
<!-- 上拉加载区域 (下拉刷新时不显示, 支付宝小程序子组件传参给子子组件仍报单项数据流的异常,暂时不通过mescroll-up组件实现)-->
|
||||
<!-- <mescroll-up v-if="mescroll.optUp.use && !isDownLoading && upLoadType!==3" :option="mescroll.optUp" :type="upLoadType"></mescroll-up> -->
|
||||
<view v-if="mescroll.optUp.use && !isDownLoading && upLoadType!==3" class="mescroll-upwarp" :style="{'background':mescroll.optUp.bgColor,'color':mescroll.optUp.textColor}">
|
||||
<!-- 加载中 (此处不能用v-if,否则android小程序快速上拉可能会不断触发上拉回调) -->
|
||||
<view v-show="upLoadType===1">
|
||||
<view class="upwarp-progress mescroll-rotate" :style="{'border-color':mescroll.optUp.textColor}"></view>
|
||||
<view class="upwarp-tip">{{ mescroll.optUp.textLoading }}</view>
|
||||
</view>
|
||||
<!-- 无数据 -->
|
||||
<view v-if="upLoadType===2" class="upwarp-nodata">{{ mescroll.optUp.textNoMore }}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部是否偏移TabBar的高度(仅H5端生效) -->
|
||||
<!-- #ifdef H5 -->
|
||||
<view v-if="bottombar && windowBottom>0" class="mescroll-bottombar" :style="{height: windowBottom+'px'}"></view>
|
||||
<!-- #endif -->
|
||||
|
||||
<!-- 适配iPhoneX -->
|
||||
<view v-if="safearea" class="mescroll-safearea"></view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<!-- 回到顶部按钮 (fixed元素,需写在scroll-view外面,防止滚动的时候抖动)-->
|
||||
<mescroll-top v-model="isShowToTop" :option="mescroll.optUp.toTop" @click="toTopClick"></mescroll-top>
|
||||
|
||||
<!-- #ifdef MP-WEIXIN || MP-QQ || APP-PLUS || H5 -->
|
||||
<!-- renderjs的数据载体,不可写在mescroll-downwarp内部,避免use为false时,载体丢失,无法更新数据 -->
|
||||
<view :change:prop="renderBiz.propObserver" :prop="wxsProp"></view>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<!-- 微信小程序, QQ小程序, app, h5使用wxs -->
|
||||
<!-- #ifdef MP-WEIXIN || MP-QQ || APP-PLUS || H5 -->
|
||||
<script src="../../mescroll-uni/wxs/wxs.wxs" module="wxsBiz" lang="wxs"></script>
|
||||
<!-- #endif -->
|
||||
|
||||
<!-- app, h5使用renderjs -->
|
||||
<!-- #ifdef APP-PLUS || H5 -->
|
||||
<script module="renderBiz" lang="renderjs">
|
||||
import renderBiz from '../../mescroll-uni/wxs/renderjs.js';
|
||||
export default {
|
||||
mixins: [renderBiz]
|
||||
}
|
||||
</script>
|
||||
<!-- #endif -->
|
||||
|
||||
<script>
|
||||
import MeScroll from '../../mescroll-uni/mescroll-uni.js';
|
||||
import MescrollTop from '../../mescroll-uni/components/mescroll-top.vue';
|
||||
import WxsMixin from '../../mescroll-uni/wxs/mixins.js';
|
||||
import mescrollI18n from '../../mescroll-uni/mescroll-i18n.js';
|
||||
import GlobalOption from './mescroll-uni-option.js';
|
||||
|
||||
export default {
|
||||
mixins: [WxsMixin],
|
||||
components: {
|
||||
MescrollTop
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
mescroll: null, // mescroll实例
|
||||
viewId: 'id_' + Math.random().toString(36).substr(2,16), // 随机生成mescroll的id(不能数字开头,否则找不到元素)
|
||||
downHight: 0, //下拉刷新: 容器高度
|
||||
downLoadType: 0, // 下拉刷新状态: 0(loading前), 1(inOffset), 2(outOffset), 3(showLoading), 4(endDownScroll)
|
||||
upLoadType: 0, // 上拉加载状态: 0(loading前), 1loading中, 2没有更多了,显示END文本提示, 3(没有更多了,不显示END文本提示)
|
||||
isShowEmpty: false, // 是否显示空布局
|
||||
isShowToTop: false, // 是否显示回到顶部按钮
|
||||
scrollTop: 0, // 滚动条的位置
|
||||
scrollAnim: false, // 是否开启滚动动画
|
||||
windowTop: 0, // 可使用窗口的顶部位置
|
||||
windowBottom: 0, // 可使用窗口的底部位置
|
||||
windowHeight: 0, // 可使用窗口的高度
|
||||
statusBarHeight: 0 // 状态栏高度
|
||||
}
|
||||
},
|
||||
props: {
|
||||
down: Object, // 下拉刷新的参数配置
|
||||
up: Object, // 上拉加载的参数配置
|
||||
i18n: Object, // 国际化的参数配置
|
||||
top: [String, Number], // 下拉布局往下的偏移量 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx, 百分比则相对于windowHeight)
|
||||
topbar: [Boolean, String], // top的偏移量是否加上状态栏高度, 默认false (使用场景:取消原生导航栏时,配置此项可留出状态栏的占位, 支持传入字符串背景,如色值,背景图,渐变)
|
||||
bottom: [String, Number], // 上拉布局往上的偏移量 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx, 百分比则相对于windowHeight)
|
||||
safearea: Boolean, // bottom的偏移量是否加上底部安全区的距离, 默认false (需要适配iPhoneX时使用)
|
||||
fixed: { // 是否通过fixed固定mescroll的高度, 默认true
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
height: [String, Number], // 指定mescroll的高度, 此项有值,则不使用fixed. (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx, 百分比则相对于windowHeight)
|
||||
bottombar:{ // 底部是否偏移TabBar的高度(默认仅在H5端的tab页生效)
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
disableScroll: Boolean // 是否禁止滚动
|
||||
},
|
||||
computed: {
|
||||
// 是否使用fixed定位 (当height有值,则不使用)
|
||||
isFixed(){
|
||||
return !this.height && this.fixed
|
||||
},
|
||||
// mescroll的高度
|
||||
scrollHeight(){
|
||||
if (this.isFixed) {
|
||||
return "auto"
|
||||
} else if(this.height){
|
||||
return this.toPx(this.height) + 'px'
|
||||
}else{
|
||||
return "100%"
|
||||
}
|
||||
},
|
||||
// 下拉布局往下偏移的距离 (px)
|
||||
numTop() {
|
||||
return this.toPx(this.top)
|
||||
},
|
||||
fixedTop() {
|
||||
return this.isFixed ? (this.numTop + this.windowTop) + 'px' : 0
|
||||
},
|
||||
padTop() {
|
||||
return !this.isFixed ? this.numTop + 'px' : 0
|
||||
},
|
||||
// 上拉布局往上偏移 (px)
|
||||
numBottom() {
|
||||
return this.toPx(this.bottom)
|
||||
},
|
||||
fixedBottom() {
|
||||
return this.isFixed ? (this.numBottom + this.windowBottom) + 'px' : 0
|
||||
},
|
||||
padBottom() {
|
||||
return !this.isFixed ? this.numBottom + 'px' : 0
|
||||
},
|
||||
// 是否为重置下拉的状态
|
||||
isDownReset(){
|
||||
return this.downLoadType===3 || this.downLoadType===4
|
||||
},
|
||||
// 过渡
|
||||
transition() {
|
||||
return this.isDownReset ? 'transform 300ms' : ''
|
||||
},
|
||||
translateY() {
|
||||
return this.downHight > 0 ? 'translateY(' + this.downHight + 'px)' : '' // transform会使fixed失效,需注意把fixed元素写在mescroll之外
|
||||
},
|
||||
// 列表是否可滑动
|
||||
scrollable(){
|
||||
if(this.disableScroll) return false
|
||||
return this.downLoadType===0 || this.isDownReset
|
||||
},
|
||||
// 是否在加载中
|
||||
isDownLoading(){
|
||||
return this.downLoadType === 3
|
||||
},
|
||||
// 旋转的角度
|
||||
downRotate(){
|
||||
return this.downLoadType === 2 ? 'rotate(180deg)' : 'rotate(0deg)'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
//number,rpx,upx,px,% --> px的数值
|
||||
toPx(num){
|
||||
if(typeof num === "string"){
|
||||
if (num.indexOf('px') !== -1) {
|
||||
if(num.indexOf('rpx') !== -1) { // "10rpx"
|
||||
num = num.replace('rpx', '');
|
||||
} else if(num.indexOf('upx') !== -1) { // "10upx"
|
||||
num = num.replace('upx', '');
|
||||
} else { // "10px"
|
||||
return Number(num.replace('px', ''))
|
||||
}
|
||||
}else if (num.indexOf('%') !== -1){
|
||||
// 传百分比,则相对于windowHeight,传"10%"则等于windowHeight的10%
|
||||
let rate = Number(num.replace("%","")) / 100
|
||||
return this.windowHeight * rate
|
||||
}
|
||||
}
|
||||
return num ? uni.upx2px(Number(num)) : 0
|
||||
},
|
||||
//注册列表滚动事件,用于下拉刷新和上拉加载
|
||||
scroll(e) {
|
||||
this.mescroll.scroll(e.detail, () => {
|
||||
this.$emit('scroll', this.mescroll) // 此时可直接通过 this.mescroll.scrollTop获取滚动条位置; this.mescroll.isScrollUp获取是否向上滑动
|
||||
})
|
||||
},
|
||||
// 点击空布局的按钮回调
|
||||
emptyClick() {
|
||||
this.$emit('emptyclick', this.mescroll)
|
||||
},
|
||||
// 点击回到顶部的按钮回调
|
||||
toTopClick() {
|
||||
this.mescroll.scrollTo(0, this.mescroll.optUp.toTop.duration); // 执行回到顶部
|
||||
this.$emit('topclick', this.mescroll); // 派发点击回到顶部按钮的回调
|
||||
},
|
||||
// 更新滚动区域的高度 (使内容不满屏和到底,都可继续翻页)
|
||||
setClientHeight() {
|
||||
if (this.mescroll.getClientHeight(true) === 0 && !this.isExec) {
|
||||
this.isExec = true; // 避免多次获取
|
||||
this.$nextTick(() => { // 确保dom已渲染
|
||||
this.getClientInfo(data=>{
|
||||
this.isExec = false;
|
||||
if (data) {
|
||||
this.mescroll.setClientHeight(data.height);
|
||||
} else if (this.clientNum != 3) { // 极少部分情况,可能dom还未渲染完毕,递归获取,最多重试3次
|
||||
this.clientNum = this.clientNum == null ? 1 : this.clientNum + 1;
|
||||
setTimeout(() => {
|
||||
this.setClientHeight()
|
||||
}, this.clientNum * 100)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
},
|
||||
// 获取滚动区域的信息
|
||||
getClientInfo(success){
|
||||
let query = uni.createSelectorQuery();
|
||||
// #ifndef MP-ALIPAY || MP-DINGTALK
|
||||
query = query.in(this) // 支付宝小程序不支持in(this),而字节跳动小程序必须写in(this), 否则都取不到值
|
||||
// #endif
|
||||
let view = query.select('#' + this.viewId);
|
||||
view.boundingClientRect(data => {
|
||||
success(data)
|
||||
}).exec();
|
||||
}
|
||||
},
|
||||
// 使用created初始化mescroll对象; 如果用mounted部分css样式编译到H5会失效
|
||||
created() {
|
||||
let vm = this;
|
||||
|
||||
let diyOption = {
|
||||
// 下拉刷新的配置
|
||||
down: {
|
||||
inOffset() {
|
||||
vm.downLoadType = 1; // 下拉的距离进入offset范围内那一刻的回调 (自定义mescroll组件时,此行不可删)
|
||||
},
|
||||
outOffset() {
|
||||
vm.downLoadType = 2; // 下拉的距离大于offset那一刻的回调 (自定义mescroll组件时,此行不可删)
|
||||
},
|
||||
onMoving(mescroll, rate, downHight) {
|
||||
// 下拉过程中的回调,滑动过程一直在执行;
|
||||
vm.downHight = downHight; // 设置下拉区域的高度 (自定义mescroll组件时,此行不可删)
|
||||
},
|
||||
showLoading(mescroll, downHight) {
|
||||
vm.downLoadType = 3; // 显示下拉刷新进度的回调 (自定义mescroll组件时,此行不可删)
|
||||
vm.downHight = downHight; // 设置下拉区域的高度 (自定义mescroll组件时,此行不可删)
|
||||
},
|
||||
endDownScroll() {
|
||||
vm.downLoadType = 4; // 结束下拉 (自定义mescroll组件时,此行不可删)
|
||||
vm.downHight = 0; // 设置下拉区域的高度 (自定义mescroll组件时,此行不可删)
|
||||
vm.downResetTimer && clearTimeout(vm.downResetTimer)
|
||||
vm.downResetTimer = setTimeout(()=>{ // 过渡动画执行完毕后,需重置为0的状态,以便置空this.transition,避免iOS小程序列表渲染不完整
|
||||
if(vm.downLoadType===4) vm.downLoadType = 0
|
||||
},300)
|
||||
},
|
||||
// 派发下拉刷新的回调
|
||||
callback: function(mescroll) {
|
||||
vm.$emit('down', mescroll)
|
||||
}
|
||||
},
|
||||
// 上拉加载的配置
|
||||
up: {
|
||||
// 显示加载中的回调
|
||||
showLoading() {
|
||||
vm.upLoadType = 1;
|
||||
},
|
||||
// 显示无更多数据的回调
|
||||
showNoMore() {
|
||||
vm.upLoadType = 2;
|
||||
},
|
||||
// 隐藏上拉加载的回调
|
||||
hideUpScroll(mescroll) {
|
||||
vm.upLoadType = mescroll.optUp.hasNext ? 0 : 3;
|
||||
},
|
||||
// 空布局
|
||||
empty: {
|
||||
onShow(isShow) { // 显示隐藏的回调
|
||||
vm.isShowEmpty = isShow;
|
||||
}
|
||||
},
|
||||
// 回到顶部
|
||||
toTop: {
|
||||
onShow(isShow) { // 显示隐藏的回调
|
||||
vm.isShowToTop = isShow;
|
||||
}
|
||||
},
|
||||
// 派发上拉加载的回调
|
||||
callback: function(mescroll) {
|
||||
vm.$emit('up', mescroll);
|
||||
// 更新容器的高度 (多mescroll的情况)
|
||||
vm.setClientHeight()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let i18nType = mescrollI18n.getType() // 当前语言类型
|
||||
let i18nOption = {type: i18nType} // 国际化配置
|
||||
MeScroll.extend(i18nOption, vm.i18n) // 具体页面的国际化配置
|
||||
MeScroll.extend(i18nOption, GlobalOption.i18n) // 全局的国际化配置
|
||||
MeScroll.extend(diyOption, i18nOption[i18nType]); // 混入国际化配置
|
||||
MeScroll.extend(diyOption, {down:GlobalOption.down, up:GlobalOption.up}); // 混入全局的配置
|
||||
let myOption = JSON.parse(JSON.stringify({'down': vm.down,'up': vm.up})) // 深拷贝,避免对props的影响
|
||||
MeScroll.extend(myOption, diyOption); // 混入具体界面的配置
|
||||
|
||||
// 初始化MeScroll对象
|
||||
vm.mescroll = new MeScroll(myOption);
|
||||
vm.mescroll.viewId = vm.viewId; // 附带id
|
||||
// 挂载语言包
|
||||
vm.mescroll.i18n = i18nOption;
|
||||
// init回调mescroll对象
|
||||
vm.$emit('init', vm.mescroll);
|
||||
|
||||
// 设置高度
|
||||
const sys = uni.getSystemInfoSync();
|
||||
if(sys.windowTop) vm.windowTop = sys.windowTop;
|
||||
if(sys.windowBottom) vm.windowBottom = sys.windowBottom;
|
||||
if(sys.windowHeight) vm.windowHeight = sys.windowHeight;
|
||||
if(sys.statusBarHeight) vm.statusBarHeight = sys.statusBarHeight;
|
||||
// 使down的bottomOffset生效
|
||||
vm.mescroll.setBodyHeight(sys.windowHeight);
|
||||
|
||||
// 因为使用的是scrollview,这里需自定义scrollTo
|
||||
vm.mescroll.resetScrollTo((y, t) => {
|
||||
vm.scrollAnim = (t !== 0); // t为0,则不使用动画过渡
|
||||
if(typeof y === 'string'){
|
||||
// 小程序不支持slot里面的scroll-into-view, 统一使用计算的方式实现
|
||||
vm.getClientInfo(function(rect){
|
||||
let mescrollTop = rect.top // mescroll到顶部的距离
|
||||
let selector;
|
||||
if(y.indexOf('#')==-1 && y.indexOf('.')==-1){
|
||||
selector = '#'+y // 不带#和. 则默认为id选择器
|
||||
}else{
|
||||
selector = y
|
||||
// #ifdef APP-PLUS || H5 || MP-ALIPAY || MP-DINGTALK
|
||||
if(y.indexOf('>>>')!=-1){ // 不支持跨自定义组件的后代选择器 (转为普通的选择器即可跨组件查询)
|
||||
selector = y.split('>>>')[1].trim()
|
||||
}
|
||||
// #endif
|
||||
}
|
||||
uni.createSelectorQuery().select(selector).boundingClientRect(function(rect){
|
||||
if (rect) {
|
||||
let curY = vm.mescroll.getScrollTop()
|
||||
let top = rect.top - mescrollTop
|
||||
top += curY
|
||||
if(!vm.isFixed) top -= vm.numTop
|
||||
vm.scrollTop = curY;
|
||||
vm.$nextTick(function() {
|
||||
vm.scrollTop = top
|
||||
})
|
||||
} else{
|
||||
console.error(selector + ' does not exist');
|
||||
}
|
||||
}).exec()
|
||||
})
|
||||
return;
|
||||
}
|
||||
let curY = vm.mescroll.getScrollTop()
|
||||
if (t === 0 || t === 300) { // 当t使用默认配置的300时,则使用系统自带的动画过渡
|
||||
vm.scrollTop = curY;
|
||||
vm.$nextTick(function() {
|
||||
vm.scrollTop = y
|
||||
})
|
||||
} else {
|
||||
vm.mescroll.getStep(curY, y, step => { // 此写法可支持配置t
|
||||
vm.scrollTop = step
|
||||
}, t)
|
||||
}
|
||||
})
|
||||
|
||||
// 具体的界面如果不配置up.toTop.safearea,则取本vue的safearea值
|
||||
if (vm.up && vm.up.toTop && vm.up.toTop.safearea != null) {} else {
|
||||
vm.mescroll.optUp.toTop.safearea = vm.safearea;
|
||||
}
|
||||
// 全局配置监听
|
||||
uni.$on("setMescrollGlobalOption", options=>{
|
||||
if(!options) return;
|
||||
let i18nType = options.i18n ? options.i18n.type : null
|
||||
if(i18nType && vm.mescroll.i18n.type != i18nType){
|
||||
vm.mescroll.i18n.type = i18nType
|
||||
mescrollI18n.setType(i18nType)
|
||||
MeScroll.extend(options, vm.mescroll.i18n[i18nType])
|
||||
}
|
||||
if(options.down){
|
||||
let down = MeScroll.extend({}, options.down)
|
||||
vm.mescroll.optDown = MeScroll.extend(down, vm.mescroll.optDown)
|
||||
}
|
||||
if(options.up){
|
||||
let up = MeScroll.extend({}, options.up)
|
||||
vm.mescroll.optUp = MeScroll.extend(up, vm.mescroll.optUp)
|
||||
}
|
||||
})
|
||||
},
|
||||
mounted() {
|
||||
// 设置容器的高度
|
||||
this.setClientHeight()
|
||||
},
|
||||
destroyed() {
|
||||
// 注销全局配置监听
|
||||
uni.$off("setMescrollGlobalOption")
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
@import "../../mescroll-uni/mescroll-uni.css";
|
||||
@import "../../mescroll-uni/components/mescroll-down.css";
|
||||
@import "../../mescroll-uni/components/mescroll-up.css";
|
||||
@import "./components/mescroll-down.css";
|
||||
</style>
|
||||
@ -1,44 +0,0 @@
|
||||
/*下拉刷新--上下箭头*/
|
||||
.mescroll-downwarp .downwarp-arrow {
|
||||
display: inline-block;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin: 10px;
|
||||
background-image: url(https://www.mescroll.com/img/xinlang/mescroll-arrow.png);
|
||||
background-size: contain;
|
||||
vertical-align: middle;
|
||||
transition: all 300ms;
|
||||
}
|
||||
|
||||
/*下拉刷新--旋转进度条*/
|
||||
.mescroll-downwarp .downwarp-progress{
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border: none;
|
||||
margin: auto;
|
||||
background-size: contain;
|
||||
animation: progressRotate 0.6s steps(6, start) infinite;
|
||||
}
|
||||
@keyframes progressRotate {
|
||||
0% {
|
||||
background-image: url(https://www.mescroll.com/img/xinlang/mescroll-progress1.png);
|
||||
}
|
||||
16% {
|
||||
background-image: url(https://www.mescroll.com/img/xinlang/mescroll-progress2.png);
|
||||
}
|
||||
32% {
|
||||
background-image: url(https://www.mescroll.com/img/xinlang/mescroll-progress3.png);
|
||||
}
|
||||
48% {
|
||||
background-image: url(https://www.mescroll.com/img/xinlang/mescroll-progress4.png);
|
||||
}
|
||||
64% {
|
||||
background-image: url(https://www.mescroll.com/img/xinlang/mescroll-progress5.png);
|
||||
}
|
||||
80% {
|
||||
background-image: url(https://www.mescroll.com/img/xinlang/mescroll-progress6.png);
|
||||
}
|
||||
100% {
|
||||
background-image: url(https://www.mescroll.com/img/xinlang/mescroll-progress1.png);
|
||||
}
|
||||
}
|
||||
@ -1,53 +0,0 @@
|
||||
<!-- 下拉刷新区域 -->
|
||||
<template>
|
||||
<view v-if="mOption.use" class="mescroll-downwarp" :style="{'background':mOption.bgColor,'color':mOption.textColor}">
|
||||
<view class="downwarp-content">
|
||||
<view v-if="isDownLoading" class="downwarp-progress"></view>
|
||||
<view v-else class="downwarp-arrow" :style="{ transform: downRotate }"></view>
|
||||
<view class="downwarp-tip">{{ downText }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
option: Object, // down的配置项
|
||||
type: Number // 下拉状态(inOffset:1, outOffset:2, showLoading:3, endDownScroll:4)
|
||||
},
|
||||
computed: {
|
||||
// 支付宝小程序需写成计算属性,prop定义default仍报错
|
||||
mOption() {
|
||||
return this.option || {};
|
||||
},
|
||||
// 是否在加载中
|
||||
isDownLoading() {
|
||||
return this.type === 3;
|
||||
},
|
||||
// 旋转的角度
|
||||
downRotate() {
|
||||
return this.type === 2 ? 'rotate(-180deg)' : 'rotate(0deg)';
|
||||
},
|
||||
// 文本提示
|
||||
downText() {
|
||||
switch (this.type) {
|
||||
case 1:
|
||||
return this.mOption.textInOffset;
|
||||
case 2:
|
||||
return this.mOption.textOutOffset;
|
||||
case 3:
|
||||
return this.mOption.textLoading;
|
||||
case 4:
|
||||
return this.mOption.textLoading;
|
||||
default:
|
||||
return this.mOption.textInOffset;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
@import '../../../mescroll-uni/components/mescroll-down.css';
|
||||
@import './mescroll-down.css';
|
||||
</style>
|
||||
@ -1,32 +0,0 @@
|
||||
/*上拉加载--旋转进度条*/
|
||||
.mescroll-upwarp .upwarp-progress {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border: none;
|
||||
margin: auto;
|
||||
background-size: contain;
|
||||
animation: progressRotate 0.6s steps(6, start) infinite;
|
||||
}
|
||||
@keyframes progressRotate {
|
||||
0% {
|
||||
background-image: url(https://www.mescroll.com/img/xinlang/mescroll-progress1.png);
|
||||
}
|
||||
16% {
|
||||
background-image: url(https://www.mescroll.com/img/xinlang/mescroll-progress2.png);
|
||||
}
|
||||
32% {
|
||||
background-image: url(https://www.mescroll.com/img/xinlang/mescroll-progress3.png);
|
||||
}
|
||||
48% {
|
||||
background-image: url(https://www.mescroll.com/img/xinlang/mescroll-progress4.png);
|
||||
}
|
||||
64% {
|
||||
background-image: url(https://www.mescroll.com/img/xinlang/mescroll-progress5.png);
|
||||
}
|
||||
80% {
|
||||
background-image: url(https://www.mescroll.com/img/xinlang/mescroll-progress6.png);
|
||||
}
|
||||
100% {
|
||||
background-image: url(https://www.mescroll.com/img/xinlang/mescroll-progress1.png);
|
||||
}
|
||||
}
|
||||
@ -1,40 +0,0 @@
|
||||
<!-- 上拉加载区域 -->
|
||||
<template>
|
||||
<view class="mescroll-upwarp" :style="{'background':mOption.bgColor,'color':mOption.textColor}">
|
||||
<!-- 加载中 (此处不能用v-if,否则android小程序快速上拉可能会不断触发上拉回调) -->
|
||||
<view v-show="isUpLoading">
|
||||
<view class="upwarp-progress mescroll-rotate"></view>
|
||||
<view class="upwarp-tip">{{ mOption.textLoading }}</view>
|
||||
</view>
|
||||
<!-- 无数据 -->
|
||||
<view v-if="isUpNoMore" class="upwarp-nodata">{{ mOption.textNoMore }}</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
option: Object, // up的配置项
|
||||
type: Number // 上拉加载的状态:0(loading前),1(loading中),2(没有更多了,显示END文本提示),3(没有更多了,不显示END文本提示)
|
||||
},
|
||||
computed: {
|
||||
// 支付宝小程序需写成计算属性,prop定义default仍报错
|
||||
mOption() {
|
||||
return this.option || {};
|
||||
},
|
||||
// 加载中
|
||||
isUpLoading() {
|
||||
return this.type === 1;
|
||||
},
|
||||
// 没有更多了
|
||||
isUpNoMore() {
|
||||
return this.type === 2;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
@import '../../../mescroll-uni/components/mescroll-up.css';
|
||||
@import './mescroll-up.css';
|
||||
</style>
|
||||
@ -1,380 +0,0 @@
|
||||
<template>
|
||||
<view
|
||||
class="mescroll-body mescroll-render-touch"
|
||||
:style="{'minHeight':minHeight, 'padding-top': padTop, 'padding-bottom': padBottom}"
|
||||
:class="{'mescorll-sticky': sticky}"
|
||||
@touchstart="wxsBiz.touchstartEvent"
|
||||
@touchmove="wxsBiz.touchmoveEvent"
|
||||
@touchend="wxsBiz.touchendEvent"
|
||||
@touchcancel="wxsBiz.touchendEvent"
|
||||
:change:prop="wxsBiz.propObserver"
|
||||
:prop="wxsProp"
|
||||
>
|
||||
|
||||
<!-- 状态栏 -->
|
||||
<view v-if="topbar&&statusBarHeight" class="mescroll-topbar" :style="{height: statusBarHeight+'px', background: topbar}"></view>
|
||||
|
||||
<view class="mescroll-body-content mescroll-wxs-content" :style="{ transform: translateY, transition: transition }" :change:prop="wxsBiz.callObserver" :prop="callProp">
|
||||
<!-- 下拉加载区域 (支付宝小程序子组件传参给子子组件仍报单项数据流的异常,暂时不通过mescroll-down组件实现)-->
|
||||
<!-- <mescroll-down :option="mescroll.optDown" :type="downLoadType"></mescroll-down> -->
|
||||
<view v-if="mescroll.optDown.use" class="mescroll-downwarp" :style="{'background':mescroll.optDown.bgColor,'color':mescroll.optDown.textColor}">
|
||||
<view class="downwarp-content">
|
||||
<view v-if="isDownLoading" class="downwarp-progress"></view>
|
||||
<view v-else class="downwarp-arrow" :style="{ transform: downRotate }"></view>
|
||||
<view class="downwarp-tip">{{ downText }}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 列表内容 -->
|
||||
<slot></slot>
|
||||
|
||||
<!-- 空布局 -->
|
||||
<mescroll-empty v-if="isShowEmpty" :option="mescroll.optUp.empty" @emptyclick="emptyClick"></mescroll-empty>
|
||||
|
||||
<!-- 上拉加载区域 (下拉刷新时不显示,支付宝小程序子组件传参给子子组件仍报单项数据流的异常,暂时不通过mescroll-up组件实现)-->
|
||||
<!-- <mescroll-up v-if="mescroll.optUp.use && downLoadType !== 3" :option="mescroll.optUp" :type="upLoadType"></mescroll-up> -->
|
||||
<view class="mescroll-upwarp" :style="{'background':mescroll.optUp.bgColor,'color':mescroll.optUp.textColor}">
|
||||
<!-- 加载中 (此处不能用v-if,否则android小程序快速上拉可能会不断触发上拉回调) -->
|
||||
<view v-show="upLoadType===1">
|
||||
<view class="upwarp-progress mescroll-rotate"></view>
|
||||
<view class="upwarp-tip">{{ mescroll.optUp.textLoading }}</view>
|
||||
</view>
|
||||
<!-- 无数据 -->
|
||||
<view v-if="upLoadType===2" class="upwarp-nodata">{{ mescroll.optUp.textNoMore }}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部是否偏移TabBar的高度(仅H5端生效) -->
|
||||
<!-- #ifdef H5 -->
|
||||
<view v-if="bottombar && windowBottom>0" class="mescroll-bottombar" :style="{height: windowBottom+'px'}"></view>
|
||||
<!-- #endif -->
|
||||
|
||||
<!-- 适配iPhoneX -->
|
||||
<view v-if="safearea" class="mescroll-safearea"></view>
|
||||
|
||||
<!-- 回到顶部按钮 (fixed元素需写在transform外面,防止降级为absolute)-->
|
||||
<mescroll-top v-model="isShowToTop" :option="mescroll.optUp.toTop" @click="toTopClick"></mescroll-top>
|
||||
|
||||
<!-- #ifdef MP-WEIXIN || MP-QQ || APP-PLUS || H5 -->
|
||||
<!-- renderjs的数据载体,不可写在mescroll-downwarp内部,避免use为false时,载体丢失,无法更新数据 -->
|
||||
<view :change:prop="renderBiz.propObserver" :prop="wxsProp"></view>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<!-- 微信小程序, QQ小程序, app, h5使用wxs -->
|
||||
<!-- #ifdef MP-WEIXIN || MP-QQ || APP-PLUS || H5 -->
|
||||
<script src="../../mescroll-uni/wxs/wxs.wxs" module="wxsBiz" lang="wxs"></script>
|
||||
<!-- #endif -->
|
||||
|
||||
<!-- app, h5使用renderjs -->
|
||||
<!-- #ifdef APP-PLUS || H5 -->
|
||||
<script module="renderBiz" lang="renderjs">
|
||||
import renderBiz from '../../mescroll-uni/wxs/renderjs.js';
|
||||
export default {
|
||||
mixins: [renderBiz]
|
||||
}
|
||||
</script>
|
||||
<!-- #endif -->
|
||||
|
||||
<script>
|
||||
import MeScroll from '../../mescroll-uni/mescroll-uni.js';
|
||||
import MescrollTop from '../../mescroll-uni/components/mescroll-top.vue';
|
||||
import WxsMixin from '../../mescroll-uni/wxs/mixins.js';
|
||||
import mescrollI18n from '../../mescroll-uni/mescroll-i18n.js';
|
||||
import GlobalOption from './mescroll-uni-option.js';
|
||||
|
||||
export default {
|
||||
mixins: [WxsMixin],
|
||||
components: {
|
||||
MescrollTop
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
mescroll: null, // mescroll实例
|
||||
downHight: 0, //下拉刷新: 容器高度
|
||||
downLoadType: 0, // 下拉刷新状态: 0(loading前), 1(inOffset), 2(outOffset), 3(showLoading), 4(endDownScroll)
|
||||
upLoadType: 0, // 上拉加载状态:0(loading前),1(loading中),2(没有更多了,显示END文本提示),3(没有更多了,不显示END文本提示)
|
||||
isShowEmpty: false, // 是否显示空布局
|
||||
isShowToTop: false, // 是否显示回到顶部按钮
|
||||
windowHeight: 0, // 可使用窗口的高度
|
||||
windowBottom: 0, // 可使用窗口的底部位置
|
||||
statusBarHeight: 0 // 状态栏高度
|
||||
};
|
||||
},
|
||||
props: {
|
||||
down: Object, // 下拉刷新的参数配置
|
||||
up: Object, // 上拉加载的参数配置
|
||||
i18n: Object, // 国际化的参数配置
|
||||
top: [String, Number], // 下拉布局往下的偏移量 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx, 百分比则相对于windowHeight)
|
||||
topbar: [Boolean, String], // top的偏移量是否加上状态栏高度, 默认false (使用场景:取消原生导航栏时,配置此项可留出状态栏的占位, 支持传入字符串背景,如色值,背景图,渐变)
|
||||
bottom: [String, Number], // 上拉布局往上的偏移量 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx, 百分比则相对于windowHeight)
|
||||
safearea: Boolean, // bottom的偏移量是否加上底部安全区的距离, 默认false (需要适配iPhoneX时使用)
|
||||
height: [String, Number], // 指定mescroll最小高度,默认windowHeight,使列表不满屏仍可下拉
|
||||
bottombar:{ // 底部是否偏移TabBar的高度(默认仅在H5端的tab页生效)
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
sticky: Boolean // 是否支持sticky,默认false; 当值配置true时,需避免在mescroll-body标签前面加非定位的元素,否则下拉区域无法会隐藏
|
||||
},
|
||||
computed: {
|
||||
// mescroll最小高度,默认windowHeight,使列表不满屏仍可下拉
|
||||
minHeight(){
|
||||
return this.toPx(this.height || '100%') + 'px'
|
||||
},
|
||||
// 下拉布局往下偏移的距离 (px)
|
||||
numTop() {
|
||||
return this.toPx(this.top)
|
||||
},
|
||||
padTop() {
|
||||
return this.numTop + 'px';
|
||||
},
|
||||
// 上拉布局往上偏移 (px)
|
||||
numBottom() {
|
||||
return this.toPx(this.bottom);
|
||||
},
|
||||
padBottom() {
|
||||
return this.numBottom + 'px';
|
||||
},
|
||||
// 是否为重置下拉的状态
|
||||
isDownReset() {
|
||||
return this.downLoadType === 3 || this.downLoadType === 4;
|
||||
},
|
||||
// 过渡
|
||||
transition() {
|
||||
return this.isDownReset ? 'transform 300ms' : '';
|
||||
},
|
||||
translateY() {
|
||||
return this.downHight > 0 ? 'translateY(' + this.downHight + 'px)' : ''; // transform会使fixed失效,需注意把fixed元素写在mescroll之外
|
||||
},
|
||||
// 是否在加载中
|
||||
isDownLoading() {
|
||||
return this.downLoadType === 3;
|
||||
},
|
||||
// 旋转的角度
|
||||
downRotate() {
|
||||
return this.downLoadType === 2 ? 'rotate(-180deg)' : 'rotate(0deg)';
|
||||
},
|
||||
// 文本提示
|
||||
downText() {
|
||||
if(!this.mescroll) return "";
|
||||
switch (this.downLoadType) {
|
||||
case 1:
|
||||
return this.mescroll.optDown.textInOffset;
|
||||
case 2:
|
||||
return this.mescroll.optDown.textOutOffset;
|
||||
case 3:
|
||||
return this.mescroll.optDown.textLoading;
|
||||
case 4:
|
||||
return this.mescroll.isDownEndSuccess ? this.mescroll.optDown.textSuccess : this.mescroll.isDownEndSuccess==false ? this.mescroll.optDown.textErr : this.mescroll.optDown.textInOffset;
|
||||
default:
|
||||
return this.mescroll.optDown.textInOffset;
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
//number,rpx,upx,px,% --> px的数值
|
||||
toPx(num) {
|
||||
if (typeof num === 'string') {
|
||||
if (num.indexOf('px') !== -1) {
|
||||
if (num.indexOf('rpx') !== -1) {
|
||||
// "10rpx"
|
||||
num = num.replace('rpx', '');
|
||||
} else if (num.indexOf('upx') !== -1) {
|
||||
// "10upx"
|
||||
num = num.replace('upx', '');
|
||||
} else {
|
||||
// "10px"
|
||||
return Number(num.replace('px', ''));
|
||||
}
|
||||
} else if (num.indexOf('%') !== -1) {
|
||||
// 传百分比,则相对于windowHeight,传"10%"则等于windowHeight的10%
|
||||
let rate = Number(num.replace('%', '')) / 100;
|
||||
return this.windowHeight * rate;
|
||||
}
|
||||
}
|
||||
return num ? uni.upx2px(Number(num)) : 0;
|
||||
},
|
||||
// 点击空布局的按钮回调
|
||||
emptyClick() {
|
||||
this.$emit('emptyclick', this.mescroll);
|
||||
},
|
||||
// 点击回到顶部的按钮回调
|
||||
toTopClick() {
|
||||
this.mescroll.scrollTo(0, this.mescroll.optUp.toTop.duration); // 执行回到顶部
|
||||
this.$emit('topclick', this.mescroll); // 派发点击回到顶部按钮的回调
|
||||
}
|
||||
},
|
||||
// 使用created初始化mescroll对象; 如果用mounted部分css样式编译到H5会失效
|
||||
created() {
|
||||
let vm = this;
|
||||
|
||||
let diyOption = {
|
||||
// 下拉刷新的配置
|
||||
down: {
|
||||
inOffset() {
|
||||
vm.downLoadType = 1; // 下拉的距离进入offset范围内那一刻的回调 (自定义mescroll组件时,此行不可删)
|
||||
},
|
||||
outOffset() {
|
||||
vm.downLoadType = 2; // 下拉的距离大于offset那一刻的回调 (自定义mescroll组件时,此行不可删)
|
||||
},
|
||||
onMoving(mescroll, rate, downHight) {
|
||||
// 下拉过程中的回调,滑动过程一直在执行;
|
||||
vm.downHight = downHight; // 设置下拉区域的高度 (自定义mescroll组件时,此行不可删)
|
||||
},
|
||||
showLoading(mescroll, downHight) {
|
||||
vm.downLoadType = 3; // 显示下拉刷新进度的回调 (自定义mescroll组件时,此行不可删)
|
||||
vm.downHight = downHight; // 设置下拉区域的高度 (自定义mescroll组件时,此行不可删)
|
||||
},
|
||||
beforeEndDownScroll(mescroll){
|
||||
vm.downLoadType = 4;
|
||||
return mescroll.optDown.beforeEndDelay // 延时结束的时长
|
||||
},
|
||||
endDownScroll() {
|
||||
vm.downLoadType = 4; // 结束下拉 (自定义mescroll组件时,此行不可删)
|
||||
vm.downHight = 0; // 设置下拉区域的高度 (自定义mescroll组件时,此行不可删)
|
||||
if(vm.downResetTimer) {clearTimeout(vm.downResetTimer); vm.downResetTimer = null} // 移除重置倒计时
|
||||
vm.downResetTimer = setTimeout(()=>{ // 过渡动画执行完毕后,需重置为0的状态,避免下次inOffset不及时显示textInOffset
|
||||
if(vm.downLoadType === 4) vm.downLoadType = 0
|
||||
},300)
|
||||
},
|
||||
// 派发下拉刷新的回调
|
||||
callback: function(mescroll) {
|
||||
vm.$emit('down', mescroll);
|
||||
}
|
||||
},
|
||||
// 上拉加载的配置
|
||||
up: {
|
||||
// 显示加载中的回调
|
||||
showLoading() {
|
||||
vm.upLoadType = 1;
|
||||
},
|
||||
// 显示无更多数据的回调
|
||||
showNoMore() {
|
||||
vm.upLoadType = 2;
|
||||
},
|
||||
// 隐藏上拉加载的回调
|
||||
hideUpScroll(mescroll) {
|
||||
vm.upLoadType = mescroll.optUp.hasNext ? 0 : 3;
|
||||
},
|
||||
// 空布局
|
||||
empty: {
|
||||
onShow(isShow) {
|
||||
// 显示隐藏的回调
|
||||
vm.isShowEmpty = isShow;
|
||||
}
|
||||
},
|
||||
// 回到顶部
|
||||
toTop: {
|
||||
onShow(isShow) {
|
||||
// 显示隐藏的回调
|
||||
vm.isShowToTop = isShow;
|
||||
}
|
||||
},
|
||||
// 派发上拉加载的回调
|
||||
callback: function(mescroll) {
|
||||
vm.$emit('up', mescroll);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let i18nType = mescrollI18n.getType() // 当前语言类型
|
||||
let i18nOption = {type: i18nType} // 国际化配置
|
||||
MeScroll.extend(i18nOption, vm.i18n) // 具体页面的国际化配置
|
||||
MeScroll.extend(i18nOption, GlobalOption.i18n) // 全局的国际化配置
|
||||
MeScroll.extend(diyOption, i18nOption[i18nType]); // 混入国际化配置
|
||||
MeScroll.extend(diyOption, {down:GlobalOption.down, up:GlobalOption.up}); // 混入全局的配置
|
||||
let myOption = JSON.parse(JSON.stringify({down: vm.down,up: vm.up})); // 深拷贝,避免对props的影响
|
||||
MeScroll.extend(myOption, diyOption); // 混入具体界面的配置
|
||||
|
||||
// 初始化MeScroll对象
|
||||
vm.mescroll = new MeScroll(myOption, true); // 传入true,标记body为滚动区域
|
||||
// 挂载语言包
|
||||
vm.mescroll.i18n = i18nOption;
|
||||
// init回调mescroll对象
|
||||
vm.$emit('init', vm.mescroll);
|
||||
|
||||
// 设置高度
|
||||
const sys = uni.getSystemInfoSync();
|
||||
if (sys.windowHeight) vm.windowHeight = sys.windowHeight;
|
||||
if (sys.windowBottom) vm.windowBottom = sys.windowBottom;
|
||||
if (sys.statusBarHeight) vm.statusBarHeight = sys.statusBarHeight;
|
||||
// 使down的bottomOffset生效
|
||||
vm.mescroll.setBodyHeight(sys.windowHeight);
|
||||
|
||||
// 因为使用的是page的scroll,这里需自定义scrollTo
|
||||
vm.mescroll.resetScrollTo((y, t) => {
|
||||
if(typeof y === 'string'){
|
||||
// 滚动到指定view (y为css选择器)
|
||||
setTimeout(()=>{ // 延时确保view已渲染; 不使用$nextTick
|
||||
let selector;
|
||||
if(y.indexOf('#')==-1 && y.indexOf('.')==-1){
|
||||
selector = '#'+y // 不带#和. 则默认为id选择器
|
||||
}else{
|
||||
selector = y
|
||||
// #ifdef APP-PLUS || H5 || MP-ALIPAY || MP-DINGTALK
|
||||
if(y.indexOf('>>>')!=-1){ // 不支持跨自定义组件的后代选择器 (转为普通的选择器即可跨组件查询)
|
||||
selector = y.split('>>>')[1].trim()
|
||||
}
|
||||
// #endif
|
||||
}
|
||||
uni.createSelectorQuery().select(selector).boundingClientRect(function(rect){
|
||||
if (rect) {
|
||||
let top = rect.top
|
||||
top += vm.mescroll.getScrollTop()
|
||||
uni.pageScrollTo({
|
||||
scrollTop: top,
|
||||
duration: t
|
||||
})
|
||||
} else{
|
||||
console.error(selector + ' does not exist');
|
||||
}
|
||||
}).exec()
|
||||
},30)
|
||||
} else{
|
||||
// 滚动到指定位置 (y必须为数字)
|
||||
uni.pageScrollTo({
|
||||
scrollTop: y,
|
||||
duration: t
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
// 具体的界面如果不配置up.toTop.safearea,则取本vue的safearea值
|
||||
if (vm.up && vm.up.toTop && vm.up.toTop.safearea != null) {} else {
|
||||
vm.mescroll.optUp.toTop.safearea = vm.safearea;
|
||||
}
|
||||
|
||||
// 全局配置监听
|
||||
uni.$on("setMescrollGlobalOption", options=>{
|
||||
if(!options) return;
|
||||
let i18nType = options.i18n ? options.i18n.type : null
|
||||
if(i18nType && vm.mescroll.i18n.type != i18nType){
|
||||
vm.mescroll.i18n.type = i18nType
|
||||
mescrollI18n.setType(i18nType)
|
||||
MeScroll.extend(options, vm.mescroll.i18n[i18nType])
|
||||
}
|
||||
if(options.down){
|
||||
let down = MeScroll.extend({}, options.down)
|
||||
vm.mescroll.optDown = MeScroll.extend(down, vm.mescroll.optDown)
|
||||
}
|
||||
if(options.up){
|
||||
let up = MeScroll.extend({}, options.up)
|
||||
vm.mescroll.optUp = MeScroll.extend(up, vm.mescroll.optUp)
|
||||
}
|
||||
})
|
||||
},
|
||||
destroyed() {
|
||||
// 注销全局配置监听
|
||||
uni.$off("setMescrollGlobalOption")
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
@import "../../mescroll-uni/mescroll-uni.css";
|
||||
@import "../../mescroll-uni/components/mescroll-down.css";
|
||||
@import "../../mescroll-uni/components/mescroll-up.css";
|
||||
@import "./components/mescroll-down.css";
|
||||
@import "./components/mescroll-up.css";
|
||||
</style>
|
||||
@ -1,66 +0,0 @@
|
||||
import { img } from '@/utils/common';
|
||||
|
||||
// 全局配置
|
||||
// mescroll-body 和 mescroll-uni 通用
|
||||
const GlobalOption = {
|
||||
down: {
|
||||
// 其他down的配置参数也可以写,这里只展示了常用的配置:
|
||||
offset: 80, // 在列表顶部,下拉大于80px,松手即可触发下拉刷新的回调
|
||||
native: false // 是否使用系统自带的下拉刷新; 默认false; 仅在mescroll-body生效 (值为true时,还需在pages配置enablePullDownRefresh:true;详请参考mescroll-native的案例)
|
||||
},
|
||||
up: {
|
||||
// 其他up的配置参数也可以写,这里只展示了常用的配置:
|
||||
offset: 150, // 距底部多远时,触发upCallback,仅mescroll-uni生效 ( mescroll-body配置的是pages.json的 onReachBottomDistance )
|
||||
toTop: {
|
||||
// 回到顶部按钮,需配置src才显示
|
||||
src: "https://www.mescroll.com/img/mescroll-totop.png", // 图片路径 (建议放入static目录, 如 /static/img/mescroll-totop.png )
|
||||
offset: 1000, // 列表滚动多少距离才显示回到顶部按钮,默认1000px
|
||||
right: 20, // 到右边的距离, 默认20 (支持"20rpx", "20px", "20%"格式的值, 纯数字则默认单位rpx)
|
||||
bottom: 120, // 到底部的距离, 默认120 (支持"20rpx", "20px", "20%"格式的值, 纯数字则默认单位rpx)
|
||||
width: 72 // 回到顶部图标的宽度, 默认72 (支持"20rpx", "20px", "20%"格式的值, 纯数字则默认单位rpx)
|
||||
},
|
||||
empty: {
|
||||
use: true, // 是否显示空布局
|
||||
icon: img("static/resource/images/system/empty.png") // 图标路径 (建议放入static目录, 如 /static/img/mescroll-empty.png )
|
||||
}
|
||||
},
|
||||
// 国际化配置
|
||||
i18n: {
|
||||
// 中文
|
||||
zh: {
|
||||
down: {
|
||||
textInOffset: '下拉刷新', // 下拉的距离在offset范围内的提示文本
|
||||
textOutOffset: '释放更新', // 下拉的距离大于offset范围的提示文本
|
||||
textLoading: '加载中 ...', // 加载中的提示文本
|
||||
textSuccess: '加载成功', // 加载成功的文本
|
||||
textErr: '加载失败', // 加载失败的文本
|
||||
},
|
||||
up: {
|
||||
textLoading: '加载中 ...', // 加载中的提示文本
|
||||
textNoMore: '', // 没有更多数据的提示文本
|
||||
empty: {
|
||||
tip: '暂无相关数据' // 空提示
|
||||
}
|
||||
}
|
||||
},
|
||||
// 英文
|
||||
en: {
|
||||
down: {
|
||||
textInOffset: 'drop down refresh',
|
||||
textOutOffset: 'release updates',
|
||||
textLoading: 'loading ...',
|
||||
textSuccess: 'loaded successfully',
|
||||
textErr: 'loading failed'
|
||||
},
|
||||
up: {
|
||||
textLoading: 'loading ...',
|
||||
textNoMore: '',
|
||||
empty: {
|
||||
tip: '~ absolutely empty ~'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default GlobalOption
|
||||
@ -1,462 +0,0 @@
|
||||
<template>
|
||||
<view class="mescroll-uni-warp">
|
||||
<scroll-view :id="viewId" class="mescroll-uni" :class="{'mescroll-uni-fixed':isFixed}" :style="{'height':scrollHeight,'padding-top':padTop,'padding-bottom':padBottom,'top':fixedTop,'bottom':fixedBottom}" :scroll-top="scrollTop" :scroll-with-animation="scrollAnim" @scroll="scroll" :scroll-y='scrollable' :enable-back-to-top="true" :throttle="false">
|
||||
<view class="mescroll-uni-content mescroll-render-touch"
|
||||
@touchstart="wxsBiz.touchstartEvent"
|
||||
@touchmove="wxsBiz.touchmoveEvent"
|
||||
@touchend="wxsBiz.touchendEvent"
|
||||
@touchcancel="wxsBiz.touchendEvent"
|
||||
:change:prop="wxsBiz.propObserver"
|
||||
:prop="wxsProp">
|
||||
|
||||
<!-- 状态栏 -->
|
||||
<view v-if="topbar&&statusBarHeight" class="mescroll-topbar" :style="{height: statusBarHeight+'px', background: topbar}"></view>
|
||||
|
||||
<view class="mescroll-wxs-content" :style="{'transform': translateY, 'transition': transition}" :change:prop="wxsBiz.callObserver" :prop="callProp">
|
||||
<!-- 下拉加载区域 (支付宝小程序子组件传参给子子组件仍报单项数据流的异常,暂时不通过mescroll-down组件实现)-->
|
||||
<!-- <mescroll-down :option="mescroll.optDown" :type="downLoadType"></mescroll-down> -->
|
||||
<view v-if="mescroll.optDown.use" class="mescroll-downwarp" :style="{'background':mescroll.optDown.bgColor,'color':mescroll.optDown.textColor}">
|
||||
<view class="downwarp-content">
|
||||
<view v-if="isDownLoading" class="downwarp-progress"></view>
|
||||
<view v-else class="downwarp-arrow" :style="{ transform: downRotate }"></view>
|
||||
<view class="downwarp-tip">{{ downText }}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 列表内容 -->
|
||||
<slot></slot>
|
||||
|
||||
<!-- 空布局 -->
|
||||
<mescroll-empty v-if="isShowEmpty" :option="mescroll.optUp.empty" @emptyclick="emptyClick"></mescroll-empty>
|
||||
|
||||
<!-- 上拉加载区域 (下拉刷新时不显示,支付宝小程序子组件传参给子子组件仍报单项数据流的异常,暂时不通过mescroll-up组件实现)-->
|
||||
<!-- <mescroll-up v-if="mescroll.optUp.use && downLoadType !== 3" :option="mescroll.optUp" :type="upLoadType"></mescroll-up> -->
|
||||
<view class="mescroll-upwarp" :style="{'background':mescroll.optUp.bgColor,'color':mescroll.optUp.textColor}">
|
||||
<!-- 加载中 (此处不能用v-if,否则android小程序快速上拉可能会不断触发上拉回调) -->
|
||||
<view v-show="upLoadType===1">
|
||||
<view class="upwarp-progress mescroll-rotate"></view>
|
||||
<view class="upwarp-tip">{{ mescroll.optUp.textLoading }}</view>
|
||||
</view>
|
||||
<!-- 无数据 -->
|
||||
<view v-if="upLoadType===2" class="upwarp-nodata">{{ mescroll.optUp.textNoMore }}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部是否偏移TabBar的高度(仅H5端生效) -->
|
||||
<!-- #ifdef H5 -->
|
||||
<view v-if="bottombar && windowBottom>0" class="mescroll-bottombar" :style="{height: windowBottom+'px'}"></view>
|
||||
<!-- #endif -->
|
||||
|
||||
<!-- 适配iPhoneX -->
|
||||
<view v-if="safearea" class="mescroll-safearea"></view>
|
||||
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<!-- 回到顶部按钮 (fixed元素,需写在scroll-view外面,防止滚动的时候抖动)-->
|
||||
<mescroll-top v-model="isShowToTop" :option="mescroll.optUp.toTop" @click="toTopClick"></mescroll-top>
|
||||
|
||||
<!-- #ifdef MP-WEIXIN || MP-QQ || APP-PLUS || H5 -->
|
||||
<!-- renderjs的数据载体,不可写在mescroll-downwarp内部,避免use为false时,载体丢失,无法更新数据 -->
|
||||
<view :change:prop="renderBiz.propObserver" :prop="wxsProp"></view>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<!-- 微信小程序, QQ小程序, app, h5使用wxs -->
|
||||
<!-- #ifdef MP-WEIXIN || MP-QQ || APP-PLUS || H5 -->
|
||||
<script src="../../mescroll-uni/wxs/wxs.wxs" module="wxsBiz" lang="wxs"></script>
|
||||
<!-- #endif -->
|
||||
|
||||
<!-- app, h5使用renderjs -->
|
||||
<!-- #ifdef APP-PLUS || H5 -->
|
||||
<script module="renderBiz" lang="renderjs">
|
||||
import renderBiz from '../../mescroll-uni/wxs/renderjs.js';
|
||||
export default {
|
||||
mixins: [renderBiz]
|
||||
}
|
||||
</script>
|
||||
<!-- #endif -->
|
||||
|
||||
<script>
|
||||
import MeScroll from '../../mescroll-uni/mescroll-uni.js';
|
||||
import MescrollTop from '../../mescroll-uni/components/mescroll-top.vue';
|
||||
import WxsMixin from '../../mescroll-uni/wxs/mixins.js';
|
||||
import mescrollI18n from '../../mescroll-uni/mescroll-i18n.js';
|
||||
import GlobalOption from './mescroll-uni-option.js';
|
||||
|
||||
export default {
|
||||
mixins: [WxsMixin],
|
||||
components: {
|
||||
MescrollTop
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
mescroll: null, // mescroll实例
|
||||
viewId: 'id_' + Math.random().toString(36).substr(2,16), // 随机生成mescroll的id(不能数字开头,否则找不到元素)
|
||||
downHight: 0, //下拉刷新: 容器高度
|
||||
downLoadType: 0, // 下拉刷新状态: 0(loading前), 1(inOffset), 2(outOffset), 3(showLoading), 4(endDownScroll)
|
||||
upLoadType: 0, // 上拉加载状态: 0(loading前), 1loading中, 2没有更多了,显示END文本提示, 3(没有更多了,不显示END文本提示)
|
||||
isShowEmpty: false, // 是否显示空布局
|
||||
isShowToTop: false, // 是否显示回到顶部按钮
|
||||
scrollTop: 0, // 滚动条的位置
|
||||
scrollAnim: false, // 是否开启滚动动画
|
||||
windowTop: 0, // 可使用窗口的顶部位置
|
||||
windowBottom: 0, // 可使用窗口的底部位置
|
||||
windowHeight: 0, // 可使用窗口的高度
|
||||
statusBarHeight: 0 // 状态栏高度
|
||||
}
|
||||
},
|
||||
props: {
|
||||
down: Object, // 下拉刷新的参数配置
|
||||
up: Object, // 上拉加载的参数配置
|
||||
i18n: Object, // 国际化的参数配置
|
||||
top: [String, Number], // 下拉布局往下的偏移量 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx, 百分比则相对于windowHeight)
|
||||
topbar: [Boolean, String], // top的偏移量是否加上状态栏高度, 默认false (使用场景:取消原生导航栏时,配置此项可留出状态栏的占位, 支持传入字符串背景,如色值,背景图,渐变)
|
||||
bottom: [String, Number], // 上拉布局往上的偏移量 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx, 百分比则相对于windowHeight)
|
||||
safearea: Boolean, // bottom的偏移量是否加上底部安全区的距离, 默认false (需要适配iPhoneX时使用)
|
||||
fixed: { // 是否通过fixed固定mescroll的高度, 默认true
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
height: [String, Number], // 指定mescroll的高度, 此项有值,则不使用fixed. (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx, 百分比则相对于windowHeight)
|
||||
bottombar:{ // 底部是否偏移TabBar的高度(默认仅在H5端的tab页生效)
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
disableScroll: Boolean // 是否禁止滚动
|
||||
},
|
||||
computed: {
|
||||
// 是否使用fixed定位 (当height有值,则不使用)
|
||||
isFixed(){
|
||||
return !this.height && this.fixed
|
||||
},
|
||||
// mescroll的高度
|
||||
scrollHeight(){
|
||||
if (this.isFixed) {
|
||||
return "auto"
|
||||
} else if(this.height){
|
||||
return this.toPx(this.height) + 'px'
|
||||
}else{
|
||||
return "100%"
|
||||
}
|
||||
},
|
||||
// 下拉布局往下偏移的距离 (px)
|
||||
numTop() {
|
||||
return this.toPx(this.top)
|
||||
},
|
||||
fixedTop() {
|
||||
return this.isFixed ? (this.numTop + this.windowTop) + 'px' : 0
|
||||
},
|
||||
padTop() {
|
||||
return !this.isFixed ? this.numTop + 'px' : 0
|
||||
},
|
||||
// 上拉布局往上偏移 (px)
|
||||
numBottom() {
|
||||
return this.toPx(this.bottom)
|
||||
},
|
||||
fixedBottom() {
|
||||
return this.isFixed ? (this.numBottom + this.windowBottom) + 'px' : 0
|
||||
},
|
||||
padBottom() {
|
||||
return !this.isFixed ? this.numBottom + 'px' : 0
|
||||
},
|
||||
// 是否为重置下拉的状态
|
||||
isDownReset(){
|
||||
return this.downLoadType===3 || this.downLoadType===4
|
||||
},
|
||||
// 过渡
|
||||
transition() {
|
||||
return this.isDownReset ? 'transform 300ms' : ''
|
||||
},
|
||||
translateY() {
|
||||
return this.downHight > 0 ? 'translateY(' + this.downHight + 'px)' : '' // transform会使fixed失效,需注意把fixed元素写在mescroll之外
|
||||
},
|
||||
// 列表是否可滑动
|
||||
scrollable(){
|
||||
if(this.disableScroll) return false
|
||||
return this.downLoadType===0 || this.isDownReset
|
||||
},
|
||||
// 是否在加载中
|
||||
isDownLoading() {
|
||||
return this.downLoadType === 3;
|
||||
},
|
||||
// 旋转的角度
|
||||
downRotate() {
|
||||
return this.downLoadType === 2 ? 'rotate(-180deg)' : 'rotate(0deg)';
|
||||
},
|
||||
// 文本提示
|
||||
downText() {
|
||||
if(!this.mescroll) return "";
|
||||
switch (this.downLoadType) {
|
||||
case 1:
|
||||
return this.mescroll.optDown.textInOffset;
|
||||
case 2:
|
||||
return this.mescroll.optDown.textOutOffset;
|
||||
case 3:
|
||||
return this.mescroll.optDown.textLoading;
|
||||
case 4:
|
||||
return this.mescroll.isDownEndSuccess ? this.mescroll.optDown.textSuccess : this.mescroll.isDownEndSuccess==false ? this.mescroll.optDown.textErr : this.mescroll.optDown.textInOffset;
|
||||
default:
|
||||
return this.mescroll.optDown.textInOffset;
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
//number,rpx,upx,px,% --> px的数值
|
||||
toPx(num){
|
||||
if(typeof num === "string"){
|
||||
if (num.indexOf('px') !== -1) {
|
||||
if(num.indexOf('rpx') !== -1) { // "10rpx"
|
||||
num = num.replace('rpx', '');
|
||||
} else if(num.indexOf('upx') !== -1) { // "10upx"
|
||||
num = num.replace('upx', '');
|
||||
} else { // "10px"
|
||||
return Number(num.replace('px', ''))
|
||||
}
|
||||
}else if (num.indexOf('%') !== -1){
|
||||
// 传百分比,则相对于windowHeight,传"10%"则等于windowHeight的10%
|
||||
let rate = Number(num.replace("%","")) / 100
|
||||
return this.windowHeight * rate
|
||||
}
|
||||
}
|
||||
return num ? uni.upx2px(Number(num)) : 0
|
||||
},
|
||||
//注册列表滚动事件,用于下拉刷新和上拉加载
|
||||
scroll(e) {
|
||||
this.mescroll.scroll(e.detail, () => {
|
||||
this.$emit('scroll', this.mescroll) // 此时可直接通过 this.mescroll.scrollTop获取滚动条位置; this.mescroll.isScrollUp获取是否向上滑动
|
||||
})
|
||||
},
|
||||
// 点击空布局的按钮回调
|
||||
emptyClick() {
|
||||
this.$emit('emptyclick', this.mescroll)
|
||||
},
|
||||
// 点击回到顶部的按钮回调
|
||||
toTopClick() {
|
||||
this.mescroll.scrollTo(0, this.mescroll.optUp.toTop.duration); // 执行回到顶部
|
||||
this.$emit('topclick', this.mescroll); // 派发点击回到顶部按钮的回调
|
||||
},
|
||||
// 更新滚动区域的高度 (使内容不满屏和到底,都可继续翻页)
|
||||
setClientHeight() {
|
||||
if (this.mescroll.getClientHeight(true) === 0 && !this.isExec) {
|
||||
this.isExec = true; // 避免多次获取
|
||||
this.$nextTick(() => { // 确保dom已渲染
|
||||
this.getClientInfo(data=>{
|
||||
this.isExec = false;
|
||||
if (data) {
|
||||
this.mescroll.setClientHeight(data.height);
|
||||
} else if (this.clientNum != 3) { // 极少部分情况,可能dom还未渲染完毕,递归获取,最多重试3次
|
||||
this.clientNum = this.clientNum == null ? 1 : this.clientNum + 1;
|
||||
setTimeout(() => {
|
||||
this.setClientHeight()
|
||||
}, this.clientNum * 100)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
},
|
||||
// 获取滚动区域的信息
|
||||
getClientInfo(success){
|
||||
let query = uni.createSelectorQuery();
|
||||
// #ifndef MP-ALIPAY || MP-DINGTALK
|
||||
query = query.in(this) // 支付宝小程序不支持in(this),而字节跳动小程序必须写in(this), 否则都取不到值
|
||||
// #endif
|
||||
let view = query.select('#' + this.viewId);
|
||||
view.boundingClientRect(data => {
|
||||
success(data)
|
||||
}).exec();
|
||||
}
|
||||
},
|
||||
// 使用created初始化mescroll对象; 如果用mounted部分css样式编译到H5会失效
|
||||
created() {
|
||||
let vm = this;
|
||||
|
||||
let diyOption = {
|
||||
// 下拉刷新的配置
|
||||
down: {
|
||||
inOffset() {
|
||||
vm.downLoadType = 1; // 下拉的距离进入offset范围内那一刻的回调 (自定义mescroll组件时,此行不可删)
|
||||
},
|
||||
outOffset() {
|
||||
vm.downLoadType = 2; // 下拉的距离大于offset那一刻的回调 (自定义mescroll组件时,此行不可删)
|
||||
},
|
||||
onMoving(mescroll, rate, downHight) {
|
||||
// 下拉过程中的回调,滑动过程一直在执行;
|
||||
vm.downHight = downHight; // 设置下拉区域的高度 (自定义mescroll组件时,此行不可删)
|
||||
},
|
||||
showLoading(mescroll, downHight) {
|
||||
vm.downLoadType = 3; // 显示下拉刷新进度的回调 (自定义mescroll组件时,此行不可删)
|
||||
vm.downHight = downHight; // 设置下拉区域的高度 (自定义mescroll组件时,此行不可删)
|
||||
},
|
||||
beforeEndDownScroll(mescroll){
|
||||
vm.downLoadType = 4;
|
||||
return mescroll.optDown.beforeEndDelay // 延时结束的时长
|
||||
},
|
||||
endDownScroll() {
|
||||
vm.downLoadType = 4; // 结束下拉 (自定义mescroll组件时,此行不可删)
|
||||
vm.downHight = 0; // 设置下拉区域的高度 (自定义mescroll组件时,此行不可删)
|
||||
vm.downResetTimer && clearTimeout(vm.downResetTimer)
|
||||
vm.downResetTimer = setTimeout(()=>{ // 过渡动画执行完毕后,需重置为0的状态,以便置空this.transition,避免iOS小程序列表渲染不完整
|
||||
if(vm.downLoadType===4) vm.downLoadType = 0
|
||||
},300)
|
||||
},
|
||||
// 派发下拉刷新的回调
|
||||
callback: function(mescroll) {
|
||||
vm.$emit('down', mescroll)
|
||||
}
|
||||
},
|
||||
// 上拉加载的配置
|
||||
up: {
|
||||
// 显示加载中的回调
|
||||
showLoading() {
|
||||
vm.upLoadType = 1;
|
||||
},
|
||||
// 显示无更多数据的回调
|
||||
showNoMore() {
|
||||
vm.upLoadType = 2;
|
||||
},
|
||||
// 隐藏上拉加载的回调
|
||||
hideUpScroll(mescroll) {
|
||||
vm.upLoadType = mescroll.optUp.hasNext ? 0 : 3;
|
||||
},
|
||||
// 空布局
|
||||
empty: {
|
||||
onShow(isShow) { // 显示隐藏的回调
|
||||
vm.isShowEmpty = isShow;
|
||||
}
|
||||
},
|
||||
// 回到顶部
|
||||
toTop: {
|
||||
onShow(isShow) { // 显示隐藏的回调
|
||||
vm.isShowToTop = isShow;
|
||||
}
|
||||
},
|
||||
// 派发上拉加载的回调
|
||||
callback: function(mescroll) {
|
||||
vm.$emit('up', mescroll);
|
||||
// 更新容器的高度 (多mescroll的情况)
|
||||
vm.setClientHeight()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let i18nType = mescrollI18n.getType() // 当前语言类型
|
||||
let i18nOption = {type: i18nType} // 国际化配置
|
||||
MeScroll.extend(i18nOption, vm.i18n) // 具体页面的国际化配置
|
||||
MeScroll.extend(i18nOption, GlobalOption.i18n) // 全局的国际化配置
|
||||
MeScroll.extend(diyOption, i18nOption[i18nType]); // 混入国际化配置
|
||||
MeScroll.extend(diyOption, {down:GlobalOption.down, up:GlobalOption.up}); // 混入全局的配置
|
||||
let myOption = JSON.parse(JSON.stringify({
|
||||
'down': vm.down,
|
||||
'up': vm.up
|
||||
})) // 深拷贝,避免对props的影响
|
||||
MeScroll.extend(myOption, diyOption); // 混入具体界面的配置
|
||||
|
||||
// 初始化MeScroll对象
|
||||
vm.mescroll = new MeScroll(myOption);
|
||||
vm.mescroll.viewId = vm.viewId; // 附带id
|
||||
// 挂载语言包
|
||||
vm.mescroll.i18n = i18nOption;
|
||||
// init回调mescroll对象
|
||||
vm.$emit('init', vm.mescroll);
|
||||
|
||||
// 设置高度
|
||||
const sys = uni.getSystemInfoSync();
|
||||
if(sys.windowTop) vm.windowTop = sys.windowTop;
|
||||
if(sys.windowBottom) vm.windowBottom = sys.windowBottom;
|
||||
if(sys.windowHeight) vm.windowHeight = sys.windowHeight;
|
||||
if(sys.statusBarHeight) vm.statusBarHeight = sys.statusBarHeight;
|
||||
// 使down的bottomOffset生效
|
||||
vm.mescroll.setBodyHeight(sys.windowHeight);
|
||||
|
||||
// 因为使用的是scrollview,这里需自定义scrollTo
|
||||
vm.mescroll.resetScrollTo((y, t) => {
|
||||
vm.scrollAnim = (t !== 0); // t为0,则不使用动画过渡
|
||||
if(typeof y === 'string'){
|
||||
// 小程序不支持slot里面的scroll-into-view, 统一使用计算的方式实现
|
||||
vm.getClientInfo(function(rect){
|
||||
let mescrollTop = rect.top // mescroll到顶部的距离
|
||||
let selector;
|
||||
if(y.indexOf('#')==-1 && y.indexOf('.')==-1){
|
||||
selector = '#'+y // 不带#和. 则默认为id选择器
|
||||
}else{
|
||||
selector = y
|
||||
// #ifdef APP-PLUS || H5 || MP-ALIPAY || MP-DINGTALK
|
||||
if(y.indexOf('>>>')!=-1){ // 不支持跨自定义组件的后代选择器 (转为普通的选择器即可跨组件查询)
|
||||
selector = y.split('>>>')[1].trim()
|
||||
}
|
||||
// #endif
|
||||
}
|
||||
uni.createSelectorQuery().select(selector).boundingClientRect(function(rect){
|
||||
if (rect) {
|
||||
let curY = vm.mescroll.getScrollTop()
|
||||
let top = rect.top - mescrollTop
|
||||
top += curY
|
||||
if(!vm.isFixed) top -= vm.numTop
|
||||
vm.scrollTop = curY;
|
||||
vm.$nextTick(function() {
|
||||
vm.scrollTop = top
|
||||
})
|
||||
} else{
|
||||
console.error(selector + ' does not exist');
|
||||
}
|
||||
}).exec()
|
||||
})
|
||||
return;
|
||||
}
|
||||
let curY = vm.mescroll.getScrollTop()
|
||||
if (t === 0 || t === 300) { // 当t使用默认配置的300时,则使用系统自带的动画过渡
|
||||
vm.scrollTop = curY;
|
||||
vm.$nextTick(function() {
|
||||
vm.scrollTop = y
|
||||
})
|
||||
} else {
|
||||
vm.mescroll.getStep(curY, y, step => { // 此写法可支持配置t
|
||||
vm.scrollTop = step
|
||||
}, t)
|
||||
}
|
||||
})
|
||||
|
||||
// 具体的界面如果不配置up.toTop.safearea,则取本vue的safearea值
|
||||
if (vm.up && vm.up.toTop && vm.up.toTop.safearea != null) {} else {
|
||||
vm.mescroll.optUp.toTop.safearea = vm.safearea;
|
||||
}
|
||||
|
||||
// 全局配置监听
|
||||
uni.$on("setMescrollGlobalOption", options=>{
|
||||
if(!options) return;
|
||||
let i18nType = options.i18n ? options.i18n.type : null
|
||||
if(i18nType && vm.mescroll.i18n.type != i18nType){
|
||||
vm.mescroll.i18n.type = i18nType
|
||||
mescrollI18n.setType(i18nType)
|
||||
MeScroll.extend(options, vm.mescroll.i18n[i18nType])
|
||||
}
|
||||
if(options.down){
|
||||
let down = MeScroll.extend({}, options.down)
|
||||
vm.mescroll.optDown = MeScroll.extend(down, vm.mescroll.optDown)
|
||||
}
|
||||
if(options.up){
|
||||
let up = MeScroll.extend({}, options.up)
|
||||
vm.mescroll.optUp = MeScroll.extend(up, vm.mescroll.optUp)
|
||||
}
|
||||
})
|
||||
},
|
||||
mounted() {
|
||||
// 设置容器的高度
|
||||
this.setClientHeight()
|
||||
},
|
||||
destroyed() {
|
||||
// 注销全局配置监听
|
||||
uni.$off("setMescrollGlobalOption")
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
@import "../../mescroll-uni/mescroll-uni.css";
|
||||
@import "../../mescroll-uni/components/mescroll-down.css";
|
||||
@import "../../mescroll-uni/components/mescroll-up.css";
|
||||
@import "./components/mescroll-down.css";
|
||||
@import "./components/mescroll-up.css";
|
||||
</style>
|
||||
@ -13,15 +13,15 @@
|
||||
</view>
|
||||
</view>
|
||||
<view class="gradation-picker">
|
||||
<picker-view indicator-class="!h-[80rpx] !bg-[var(--temp-bg)]" :value="dateList.curIndex" @change="bindChange" class="w-[750rpx] px-[60rpx] h-[396rpx] box-border">
|
||||
<picker-view indicator-class="!h-[80rpx] !bg-[var(--temp-bg)]" :value="dateList.curIndex" @change="bindChange" class="w-[750rpx] px-[60rpx] h-[396rpx] box-border">
|
||||
<picker-view-column>
|
||||
<view class="text-center leading-[80rpx] text-[28rpx]" v-for="(item,index) in dateList.years" :key="index">{{item}}年</view>
|
||||
<view class="text-center leading-[80rpx] text-[28rpx]" v-for="(item,index) in dateList.years" :key="index">{{item}}年</view>
|
||||
</picker-view-column>
|
||||
<picker-view-column>
|
||||
<view class="text-center leading-[80rpx] text-[28rpx]" v-for="(item,index) in dateList.months" :key="index">{{item}}月</view>
|
||||
</picker-view-column>
|
||||
<picker-view-column>
|
||||
<view class="text-center leading-[80rpx] text-[28rpx]" v-for="(item,index) in dateList.days" :key="index">{{item}}日</view>
|
||||
<view class="text-center leading-[80rpx] text-[28rpx]" v-for="(item,index) in dateList.days" :key="index">{{item}}日</view>
|
||||
</picker-view-column>
|
||||
</picker-view>
|
||||
</view>
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
<u-modal :show="show" :title="t('captchaTitle')" :confirm-text="t('confirm')" :cancel-text="t('cancel')" :show-cancel-button="true" @cancel="show = false" @confirm="handleConfirm" confirmColor="var(--primary-color)">
|
||||
<view class="flex mt-[20rpx]">
|
||||
<u-input :placeholder="t('captchaPlaceholder')" border="surround" v-model="formData.captcha_code"></u-input>
|
||||
<image :src="captcha.image.value" class="h-[76rpx] ml-[20rpx]" mode="heightFix" @click="captcha.refresh()"></image>
|
||||
<image :src="captcha.image.value" class="h-[76rpx] w-[auto] ml-[20rpx]" mode="heightFix" @click="captcha.refresh()"></image>
|
||||
</view>
|
||||
</u-modal>
|
||||
</template>
|
||||
|
||||
@ -17,7 +17,7 @@
|
||||
<view :style="{ color: topStatusBarData.textColor }">{{ data.title }}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
|
||||
<view v-if="topStatusBarData.style == 'style-3'" :style="navbarInnerStyle" class="content-wrap">
|
||||
<view v-if="isBack && isBackShow" class="back-wrap -ml-[16rpx] text-[27px] nc-iconfont nc-icon-zuoV6xx" :style="{ color: titleTextColor }" @tap="goBack"></view>
|
||||
<view class="title-wrap" @click="diyStore.toRedirect(topStatusBarData.link)">
|
||||
@ -29,29 +29,28 @@
|
||||
</view>
|
||||
<view :style="{ 'width': capsuleWidth }"></view>
|
||||
</view>
|
||||
|
||||
|
||||
<view v-if="topStatusBarData.style == 'style-4'" :style="navbarInnerStyle" class="content-wrap">
|
||||
<view v-if="isBack && isBackShow" class="back-wrap -ml-[16rpx] text-[27px] nc-iconfont nc-icon-zuoV6xx" :style="{ color: titleTextColor }" @tap="goBack"></view>
|
||||
<text class="nc-iconfont nc-icon-dizhiguanliV6xx text-[28rpx]" :style="{ color: topStatusBarData.textColor }"></text>
|
||||
<view class="title-wrap" @click="reposition()" :style="{ color: topStatusBarData.textColor }">{{ currentPosition }}</view>
|
||||
<text class="nc-iconfont nc-icon-youV6xx text-[26rpx]" @click="reposition()" :style="{ color: topStatusBarData.textColor }"></text>
|
||||
<view class="title-wrap" @click.stop="locationVal.reposition()" :style="{ color: topStatusBarData.textColor }" v-if="systemStore.diyAddressInfo">{{ systemStore.diyAddressInfo.community }}</view>
|
||||
<view class="title-wrap" @click.stop="locationVal.reposition()" :style="{ color: topStatusBarData.textColor }" v-else>{{ systemStore.defaultPositionAddress }}</view>
|
||||
<text class="nc-iconfont nc-icon-youV6xx text-[26rpx]" @click.stop="locationVal.reposition()" :style="{ color: topStatusBarData.textColor }"></text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
|
||||
<!-- 解决fixed定位后导航栏塌陷的问题 -->
|
||||
<view class="u-navbar-placeholder" :style="{ width: '100%', paddingTop: placeholderHeight + 'px' }"></view>
|
||||
<view v-if="props.isFill" class="u-navbar-placeholder" :style="{ width: '100%', paddingTop: placeholderHeight + 'px' }"></view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, watch, onMounted, getCurrentInstance, nextTick } from 'vue';
|
||||
import { img, getLocation, locationStorage } from '@/utils/common';
|
||||
import { getAddressByLatlng } from '@/app/api/system';
|
||||
import { ref, computed, onMounted, getCurrentInstance, nextTick } from 'vue';
|
||||
import { redirect, img } from '@/utils/common';
|
||||
import useSystemStore from '@/stores/system';
|
||||
import useDiyStore from '@/app/stores/diy';
|
||||
import manifestJson from '@/manifest.json'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
import {useLocation} from '@/hooks/useLocation'
|
||||
|
||||
// 获取系统状态栏的高度
|
||||
let systemInfo = uni.getSystemInfoSync();
|
||||
@ -83,9 +82,12 @@ const props = defineProps({
|
||||
isBack: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
isFill: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
})
|
||||
|
||||
const data = computed(() => {
|
||||
return props.data;
|
||||
});
|
||||
@ -97,7 +99,7 @@ const topStatusBarData = computed(() => {
|
||||
// 导航栏内部盒子的样式
|
||||
const navbarInnerStyle = computed(() => {
|
||||
let style = '';
|
||||
|
||||
|
||||
if(props.isBack && isBackShow.value){
|
||||
style += 'padding-left: 30rpx;';//30=>右边留边 44=>箭头宽度 10=>箭头的右maring
|
||||
if(topStatusBarData.value.style == 'style-1') //样式一需要居中需要有右边padding辅助
|
||||
@ -119,7 +121,7 @@ const navbarInnerStyle = computed(() => {
|
||||
// 样式一的字体大小
|
||||
const styleOneFontSize = computed(() => {
|
||||
let style = '';
|
||||
|
||||
|
||||
// #ifdef H5
|
||||
style += 'font-size: 28rpx;';
|
||||
// #endif
|
||||
@ -161,7 +163,7 @@ const bgColor = computed(() => {
|
||||
return color;
|
||||
})
|
||||
|
||||
/******************************* 存储滚动值-start ***********************/
|
||||
/******************************* 存储滚动值-start ***********************/
|
||||
// 键名和组件名一致即可
|
||||
let componentsScrollVal = uni.getStorageSync('componentsScrollValGroup')
|
||||
if(componentsScrollVal){
|
||||
@ -173,7 +175,7 @@ if(componentsScrollVal){
|
||||
}
|
||||
uni.setStorageSync('componentsScrollValGroup', obj);
|
||||
}
|
||||
/******************************* 存储滚动值-end ***********************/
|
||||
/******************************* 存储滚动值-end ***********************/
|
||||
|
||||
/******************************* 返回按钮-start ***********************/
|
||||
const isBackShow = ref(false);
|
||||
@ -181,14 +183,32 @@ let pages = getCurrentPages();
|
||||
|
||||
// 返回按钮的函数
|
||||
const goBack = () => {
|
||||
// 如果自定义了点击返回按钮的函数,则执行,否则执行返回逻辑
|
||||
if (typeof props.customBack === 'function') {
|
||||
props.customBack();
|
||||
} else {
|
||||
uni.navigateBack();
|
||||
// 兼容小程序,未登录状态下点击某个功能跳转到登录页,不登录无法返回的情况
|
||||
if(pages.length == 1 && pages[0].route == 'app/pages/auth/index'){
|
||||
uni.getStorage({
|
||||
key: 'loginBack',
|
||||
success: (res: any) => {
|
||||
res ? redirect(
|
||||
{
|
||||
...res.data,
|
||||
mode: 'redirectTo'
|
||||
}
|
||||
) : redirect({ url: '/app/pages/index/index', mode: 'switchTab' })
|
||||
},
|
||||
fail: (res) => {
|
||||
redirect({ url: '/app/pages/index/index', mode: 'switchTab' })
|
||||
}
|
||||
})
|
||||
}else{
|
||||
// 如果自定义了点击返回按钮的函数,则执行,否则执行返回逻辑
|
||||
if (typeof props.customBack === 'function') {
|
||||
props.customBack();
|
||||
} else {
|
||||
uni.navigateBack();
|
||||
}
|
||||
}
|
||||
}
|
||||
/******************************* 返回按钮-end ***********************/
|
||||
/******************************* 返回按钮-end ***********************/
|
||||
|
||||
// 微信胶囊宽度+right
|
||||
const capsuleWidth = computed(() => {
|
||||
@ -213,120 +233,32 @@ const navbarPlaceholderHeight = () => {
|
||||
})
|
||||
}
|
||||
|
||||
/******************************* 定位-start ***********************/
|
||||
// 获取地图配置
|
||||
const currentPosition = ref('定位中...')
|
||||
let mapConfig = uni.getStorageSync('mapConfig');
|
||||
|
||||
const initPosition = () =>{
|
||||
// #ifdef H5
|
||||
if (getQueryVariable('latng')) {
|
||||
currentPosition.value = "定位中..."
|
||||
let locationInfo: any = systemStore.location;
|
||||
var tempArr = getQueryVariable('latng').split(',');
|
||||
locationInfo.latitude = tempArr[0];
|
||||
locationInfo.longitude = tempArr[1];
|
||||
systemStore.setLocation(locationInfo);
|
||||
/************** 定位-start ****************/
|
||||
let isOpenLocation = false;
|
||||
if(topStatusBarData.value && topStatusBarData.value.style == 'style-4') {
|
||||
isOpenLocation = true;
|
||||
}
|
||||
// #endif
|
||||
currentPosition.value = '定位中...';
|
||||
if(uni.getStorageSync('addressByLatlng')){
|
||||
currentPosition.value = uni.getStorageSync('addressByLatlng').formatted_addresses.recommend;
|
||||
}
|
||||
// 定位信息过期后,重新获取定位
|
||||
if(mapConfig.is_open == 1 && locationStorage() && locationStorage().is_expired) {
|
||||
getLocation({
|
||||
fail: (res: any) => {
|
||||
// 拒绝定位,进入默认总店
|
||||
currentPosition.value = "定位中..."
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
watch(() => systemStore.location, (nval: any, oval)=> {
|
||||
if (nval.latitude && nval.longitude) {
|
||||
getAddressByLatlngFn()
|
||||
}else{
|
||||
currentPosition.value = "定位中..."
|
||||
}
|
||||
},{deep:true})
|
||||
|
||||
// 根据经纬度获取位置
|
||||
const getAddressByLatlngFn = () => {
|
||||
let data = {
|
||||
latlng: ''
|
||||
};
|
||||
data.latlng = systemStore.location.latitude + ',' + systemStore.location.longitude ;
|
||||
getAddressByLatlng(data).then((res: any) => {
|
||||
if (res.data && Object.keys(res.data).length) {
|
||||
currentPosition.value = res.data.formatted_addresses.recommend; // 结合知名地点形成的描述性地址,更具人性化特点
|
||||
uni.setStorageSync('addressByLatlng', res.data);
|
||||
} else {
|
||||
currentPosition.value = '定位中...';
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 打开地图重新选择位置
|
||||
const reposition = () => {
|
||||
// #ifdef MP
|
||||
uni.chooseLocation({
|
||||
success: (res) => {
|
||||
var urlencode = cloneDeep(systemStore.location);
|
||||
urlencode = Object.assign(urlencode,res)
|
||||
systemStore.setLocation(urlencode);
|
||||
},
|
||||
fail: (res)=>{
|
||||
// 在隐私协议中没有声明chooseLocation:fail api作用域
|
||||
if(res.errMsg && res.errno) {
|
||||
if(res.errno == 104){
|
||||
let msg = '用户未授权隐私权限,选择位置失败';
|
||||
uni.showToast({title: msg, icon: 'none'})
|
||||
}else if(res.errno == 112){
|
||||
let msg = '隐私协议中未声明,打开地图选择位置失败';
|
||||
uni.showToast({title: msg, icon: 'none'})
|
||||
}else {
|
||||
uni.showToast({title: res.errMsg, icon: 'none'})
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
// #endif
|
||||
|
||||
// #ifdef H5
|
||||
let backurl = location.origin + location.pathname;
|
||||
window.location.href = 'https://apis.map.qq.com/tools/locpicker?search=1&type=0&backurl=' + encodeURIComponent(backurl) + '&key=' + manifestJson.h5.sdkConfigs.maps.qqmap.key + '&referer=myapp';
|
||||
// #endif
|
||||
}
|
||||
|
||||
const getQueryVariable = (variable:any)=> {
|
||||
var query = window.location.search.substring(1);
|
||||
var vars = query.split('&');
|
||||
for (var i = 0; i < vars.length; i++) {
|
||||
var pair = vars[i].split('=');
|
||||
if (pair[0] == variable) {
|
||||
return pair[1];
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/******************************* 定位-end ***********************/
|
||||
const locationVal = useLocation(isOpenLocation);
|
||||
locationVal.onLoad();
|
||||
locationVal.init();
|
||||
/************** 定位-end ****************/
|
||||
onMounted(() => {
|
||||
navbarPlaceholderHeight();
|
||||
if (pages.length > 1) {
|
||||
isBackShow.value = true;
|
||||
// 兼容小程序,未登录状态下点击某个功能跳转到登录页,不登录无法返回的情况
|
||||
}else if(pages.length == 1 && pages[0].route == 'app/pages/auth/index'){
|
||||
isBackShow.value = true;
|
||||
}
|
||||
if(topStatusBarData.value && topStatusBarData.value.style == 'style-4') {
|
||||
initPosition();
|
||||
}
|
||||
// 刷新定位
|
||||
locationVal.refresh();
|
||||
});
|
||||
|
||||
// 页面onShow调用时,也会触发改方法
|
||||
const refresh = ()=>{
|
||||
if(topStatusBarData.value && topStatusBarData.value.style == 'style-4') {
|
||||
initPosition();
|
||||
}
|
||||
// 刷新定位
|
||||
locationVal.refresh();
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
@ -503,4 +435,4 @@ defineExpose({
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
207
uni-app/src/hooks/useLocation.ts
Normal file
207
uni-app/src/hooks/useLocation.ts
Normal file
@ -0,0 +1,207 @@
|
||||
import { onLoad } from '@dcloudio/uni-app';
|
||||
import { isWeixinBrowser } from '@/utils/common';
|
||||
import { getAddressByLatlng } from '@/app/api/system';
|
||||
import manifestJson from '@/manifest.json';
|
||||
import wechat from '@/utils/wechat'
|
||||
import useSystemStore from '@/stores/system';
|
||||
|
||||
export function useLocation(isOpenLocation: any) {
|
||||
const systemStore = useSystemStore();
|
||||
|
||||
// 表示,定位是否开启
|
||||
let isOpen = isOpenLocation;
|
||||
if (systemStore.mapConfig.is_open != 1) {
|
||||
isOpen = false;
|
||||
}
|
||||
|
||||
// 监听页面加载
|
||||
const onLoadLifeCycle = (callback: any = '') => {
|
||||
onLoad((option: any) => {
|
||||
if (option && option.latng) {
|
||||
getAddressByLatlngFn(option.latng)
|
||||
}
|
||||
|
||||
uni.removeStorageSync('manually_select_location_from_map');
|
||||
typeof callback == 'function' && callback(option);
|
||||
});
|
||||
}
|
||||
|
||||
const init = () => {
|
||||
if (!isOpen) return false;
|
||||
// 排除手动地图选择位置的情况
|
||||
if (!uni.getStorageSync('manually_select_location_from_map')) {
|
||||
// #ifdef H5
|
||||
if (isWeixinBrowser() && systemStore.mapConfig.is_open && !uni.getStorageSync('location_address')) {
|
||||
wechat.init(()=>{
|
||||
wechat.getLocation(res => {
|
||||
let latlng = res.latitude + ',' + res.longitude; // 纬度(浮点数,范围为90 ~ -90),经度(浮点数,范围为180 ~ -180)
|
||||
getAddressByLatlngFn(latlng)
|
||||
})
|
||||
});
|
||||
|
||||
}
|
||||
// #endif
|
||||
|
||||
// #ifdef MP
|
||||
if (systemStore.mapConfig.is_open && !uni.getStorageSync('location_address')) {
|
||||
uni.getLocation({
|
||||
type: 'gcj02',
|
||||
success: res => {
|
||||
let latlng = res.latitude + ',' + res.longitude; // 纬度(浮点数,范围为90 ~ -90),经度(浮点数,范围为180 ~ -180)
|
||||
getAddressByLatlngFn(latlng);
|
||||
},
|
||||
fail: (res) => {
|
||||
systemStore.defaultPositionAddress = '定位失败';
|
||||
if (res.errno) {
|
||||
if (res.errno == 104) {
|
||||
let msg = '用户未授权隐私权限,获取位置失败';
|
||||
uni.showToast({ title: msg, icon: 'none' })
|
||||
} else if (res.errno == 112) {
|
||||
let msg = '隐私协议中未声明,获取位置失败';
|
||||
uni.showToast({ title: msg, icon: 'none' })
|
||||
}
|
||||
}
|
||||
if (res.errMsg) {
|
||||
if (res.errMsg.indexOf('getLocation:fail') != -1 || res.errMsg.indexOf('deny') != -1 || res.errMsg.indexOf('denied') != -1) {
|
||||
let msg = '用户未授权获取位置权限,将无法提供服务';
|
||||
uni.showToast({ title: msg, icon: 'none' })
|
||||
} else {
|
||||
uni.showToast({ title: res.errMsg, icon: 'none' })
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
// #endif
|
||||
}
|
||||
}
|
||||
|
||||
// 刷新页面时需要调用的
|
||||
const refreshLocation = () => {
|
||||
if (!isOpen) return false;
|
||||
if (!uni.getStorageSync('manually_select_location_from_map') && uni.getStorageSync('location_address')) {
|
||||
if(locationStorage() && !locationStorage().is_expired){
|
||||
systemStore.setAddressInfo(uni.getStorageSync('location_address'))
|
||||
}else{
|
||||
uni.removeStorageSync('location_address');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 定位信息过期后,重新获取定位
|
||||
if (!uni.getStorageSync('manually_select_location_from_map') && locationStorage() && locationStorage().is_expired) {
|
||||
init();
|
||||
}
|
||||
}
|
||||
|
||||
// 通过经纬度获取地址信息
|
||||
const getAddressByLatlngFn = (latlng: any = '') => {
|
||||
if (!isOpen) return false;
|
||||
let data = { latlng: '' };
|
||||
if (latlng) {
|
||||
data.latlng = latlng;
|
||||
} else {
|
||||
data.latlng = systemStore.diyAddressInfo.latitude + ',' + systemStore.diyAddressInfo.longitude;
|
||||
}
|
||||
getAddressByLatlng(data).then((res: any) => {
|
||||
if (res.data && Object.keys(res.data).length) {
|
||||
let obj: any = {};
|
||||
|
||||
let latlngArr = data.latlng.split(',');
|
||||
|
||||
obj.latitude = latlngArr[0];
|
||||
obj.longitude = latlngArr[1];
|
||||
obj.full_address = res.data.province != undefined ? res.data.province : '';
|
||||
obj.full_address += res.data.city != undefined ? res.data.city : '';
|
||||
obj.full_address += res.data.district != undefined ? res.data.district : '';
|
||||
obj.full_address += res.data.community != undefined ? res.data.community : '';
|
||||
|
||||
obj.province_id = res.data.province_id;
|
||||
obj.province = res.data.province;
|
||||
obj.city_id = res.data.city_id;
|
||||
obj.city = res.data.city;
|
||||
obj.district_id = res.data.district_id;
|
||||
obj.district = res.data.district;
|
||||
obj.community = res.data.community;
|
||||
obj.formatted_addresses = res.data.formatted_addresses;
|
||||
systemStore.setAddressInfo(obj)
|
||||
} else {
|
||||
systemStore.setAddressInfo()
|
||||
}
|
||||
|
||||
// 手动选择地图位置后,清除标识
|
||||
setTimeout(() => {
|
||||
// 由于异步请求速度影响,需要延迟删除
|
||||
uni.removeStorageSync('manually_select_location_from_map');
|
||||
}, 500);
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// 打开地图重新选择位置
|
||||
const reposition = () => {
|
||||
if (!isOpen) return false;
|
||||
|
||||
let latitude = systemStore.diyAddressInfo ? systemStore.diyAddressInfo.latitude : '';
|
||||
let longitude = systemStore.diyAddressInfo ? systemStore.diyAddressInfo.longitude : '';
|
||||
|
||||
// #ifdef MP
|
||||
uni.chooseLocation({
|
||||
latitude,
|
||||
longitude,
|
||||
success: (res) => {
|
||||
uni.setStorageSync('manually_select_location_from_map', true)
|
||||
let latng = res.latitude + ',' + res.longitude;
|
||||
getAddressByLatlngFn(latng)
|
||||
},
|
||||
fail: (res) => {
|
||||
// 在隐私协议中没有声明chooseLocation:fail api作用域
|
||||
if (res.errMsg && res.errno) {
|
||||
if (res.errno == 104) {
|
||||
let msg = '用户未授权隐私权限,选择位置失败';
|
||||
uni.showToast({ title: msg, icon: 'none' })
|
||||
} else if (res.errno == 112) {
|
||||
let msg = '隐私协议中未声明,打开地图选择位置失败';
|
||||
uni.showToast({ title: msg, icon: 'none' })
|
||||
} else {
|
||||
uni.showToast({ title: res.errMsg, icon: 'none' })
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
// #endif
|
||||
|
||||
// #ifdef H5
|
||||
uni.setStorageSync('manually_select_location_from_map', true)
|
||||
let backurl = location.origin + location.pathname;
|
||||
window.location.href = 'https://apis.map.qq.com/tools/locpicker?search=1&type=0&backurl=' + encodeURIComponent(backurl) + '&key=' + manifestJson.h5.sdkConfigs.maps.qqmap.key + '&referer=myapp';
|
||||
// #endif
|
||||
}
|
||||
|
||||
/**
|
||||
* 定位信息(缓存)
|
||||
*/
|
||||
const locationStorage = () => {
|
||||
let data = uni.getStorageSync('location_address');
|
||||
if (data) {
|
||||
var date = new Date();
|
||||
if (systemStore.mapConfig.valid_time > 0) {
|
||||
data.is_expired = (date.getTime() / 1000) > data.valid_time; // 是否过期
|
||||
} else {
|
||||
data.is_expired = false;
|
||||
}
|
||||
}else{
|
||||
data = {
|
||||
is_expired : false
|
||||
};
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
return {
|
||||
init: init,
|
||||
onLoad: onLoadLifeCycle,
|
||||
refresh: refreshLocation,
|
||||
reposition: reposition
|
||||
}
|
||||
}
|
||||
@ -2,7 +2,6 @@ import { redirect, isWeixinBrowser, urlDeconstruction } from '@/utils/common'
|
||||
import {
|
||||
weappLogin,
|
||||
updateWeappOpenid,
|
||||
wechatLogin,
|
||||
updateWechatOpenid,
|
||||
wechatUser,
|
||||
wechatUserLogin
|
||||
@ -10,6 +9,7 @@ import {
|
||||
import { getWechatAuthCode } from '@/app/api/system'
|
||||
import useMemberStore from '@/stores/member'
|
||||
import useConfigStore from '@/stores/config'
|
||||
import useSystemStore from '@/stores/system'
|
||||
|
||||
export function useLogin() {
|
||||
/**
|
||||
@ -19,6 +19,7 @@ export function useLogin() {
|
||||
uni.setStorage({ key: 'loginBack', data })
|
||||
setTimeout(() => {
|
||||
const config = useConfigStore()
|
||||
const systemStore = useSystemStore()
|
||||
|
||||
// #ifdef MP-WEIXIN
|
||||
if (!uni.getStorageSync('autoLoginLock') && uni.getStorageSync('openid') && config.login.is_bind_mobile) {
|
||||
@ -39,7 +40,7 @@ export function useLogin() {
|
||||
// #ifdef MP-WEIXIN
|
||||
if (config.login.is_username && !config.login.is_mobile && !config.login.is_auth_register) {
|
||||
redirect({ url: '/app/pages/auth/login', param: { type: 'username' }, mode: 'redirectTo' })
|
||||
} else if (!config.login.is_username && !config.login.is_mobile && !config.login.is_auth_register) {
|
||||
} else if (systemStore.initStatus == 'finish' && !config.login.is_username && !config.login.is_mobile && !config.login.is_auth_register) {
|
||||
uni.showToast({ title: '商家未开启登录注册', icon: 'none' })
|
||||
} else {
|
||||
redirect({ url: '/app/pages/auth/index', mode: 'redirectTo' })
|
||||
@ -51,7 +52,7 @@ export function useLogin() {
|
||||
// 微信浏览器
|
||||
if (config.login.is_username && !config.login.is_mobile && !config.login.is_auth_register) {
|
||||
redirect({ url: '/app/pages/auth/login', param: { type: 'username' }, mode: 'redirectTo' })
|
||||
} else if (!config.login.is_username && !config.login.is_mobile && !config.login.is_auth_register) {
|
||||
} else if (systemStore.initStatus == 'finish' && !config.login.is_username && !config.login.is_mobile && !config.login.is_auth_register) {
|
||||
uni.showToast({ title: '商家未开启登录注册', icon: 'none' })
|
||||
} else {
|
||||
redirect({ url: '/app/pages/auth/index', mode: 'redirectTo' })
|
||||
@ -60,7 +61,7 @@ export function useLogin() {
|
||||
// 普通浏览器
|
||||
if (config.login.is_username && !config.login.is_mobile) {
|
||||
redirect({ url: '/app/pages/auth/login', param: { type: 'username' }, mode: 'redirectTo' })
|
||||
} else if (!config.login.is_username && !config.login.is_mobile) {
|
||||
} else if (systemStore.initStatus == 'finish' && !config.login.is_username && !config.login.is_mobile) {
|
||||
uni.showToast({ title: '商家未开启登录注册', icon: 'none' })
|
||||
} else {
|
||||
redirect({ url: '/app/pages/auth/index', mode: 'redirectTo' })
|
||||
@ -131,6 +132,7 @@ export function useLogin() {
|
||||
}
|
||||
}).catch((err) => {
|
||||
uni.showToast({ title: err.msg, icon: 'none' })
|
||||
if (params.successCallback) params.successCallback()
|
||||
})
|
||||
// #endif
|
||||
|
||||
@ -170,8 +172,10 @@ export function useLogin() {
|
||||
}
|
||||
/**
|
||||
* 登录普通账号后修改openid
|
||||
* @param code
|
||||
* @param callback
|
||||
*/
|
||||
const updateOpenid = (code: string | null) => {
|
||||
const updateOpenid = (code: string | null, callback: any = null) => {
|
||||
let obj: any = {
|
||||
code
|
||||
};
|
||||
@ -190,6 +194,7 @@ export function useLogin() {
|
||||
useMemberStore().getMemberInfo(() => {
|
||||
const memberInfo = useMemberStore().info
|
||||
memberInfo && memberInfo.wx_openid && uni.setStorageSync('openid', memberInfo.wx_openid)
|
||||
if (callback) callback();
|
||||
})
|
||||
})
|
||||
// #endif
|
||||
|
||||
@ -71,6 +71,7 @@
|
||||
"shop.pages.goods.category": "商品分类",
|
||||
"shop.pages.goods.detail": "商品详情",
|
||||
"shop.pages.goods.list": "商品列表",
|
||||
"shop.pages.goods.rank": "排行榜",
|
||||
"shop.pages.member.index": "个人中心",
|
||||
"shop.pages.member.my_coupon": "我的优惠券",
|
||||
"shop.pages.order.list": "订单列表",
|
||||
@ -92,6 +93,7 @@
|
||||
"shop.pages.point.detail":"积分商品详情",
|
||||
"shop.pages.point.payment":"待付款订单",
|
||||
"shop.pages.point.order_list":"积分兑换记录",
|
||||
"shop.pages.newcomer.list":"新人专享列表",
|
||||
"cms.pages.list": "资讯中心",
|
||||
"cms.pages.detail": "文章详情",
|
||||
"shop_fenxiao.pages.index": "分销中心",
|
||||
|
||||
@ -86,7 +86,13 @@
|
||||
"key": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"async" : {
|
||||
"loading" : "",
|
||||
"error" : "",
|
||||
"delay" : 0,
|
||||
"timeout" : 3000
|
||||
}
|
||||
},
|
||||
"fallbackLocale": "zh-Hans"
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,10 +1,12 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { getConfig } from '@/app/api/auth'
|
||||
import { isWeixinBrowser } from "@/utils/common";
|
||||
|
||||
interface loginConfig {
|
||||
is_username: number | boolean,
|
||||
is_mobile: number | boolean,
|
||||
is_auth_register: number | boolean,
|
||||
is_force_access_user_info: number | boolean,
|
||||
is_bind_mobile: number | boolean,
|
||||
agreement_show: number | boolean,
|
||||
bg_url: string,
|
||||
@ -34,6 +36,7 @@ const useConfigStore = defineStore('config', {
|
||||
is_username: 0,
|
||||
is_mobile: 0,
|
||||
is_auth_register: 0,
|
||||
is_force_access_user_info: 0,
|
||||
is_bind_mobile: 0,
|
||||
agreement_show: 0,
|
||||
bg_url: '',
|
||||
@ -48,10 +51,18 @@ const useConfigStore = defineStore('config', {
|
||||
},
|
||||
actions: {
|
||||
async getLoginConfig() {
|
||||
await getConfig().then((res: any) => {
|
||||
|
||||
let url = '';
|
||||
// #ifdef H5
|
||||
if (isWeixinBrowser()) {
|
||||
url = uni.getSystemInfoSync().platform == 'ios' ? uni.getStorageSync('initUrl') : location.href
|
||||
}
|
||||
// #endif
|
||||
await getConfig({ url }).then((res: any) => {
|
||||
this.login.is_username = res.data.is_username
|
||||
this.login.is_mobile = res.data.is_mobile
|
||||
this.login.is_auth_register = parseInt(res.data.is_auth_register)
|
||||
this.login.is_force_access_user_info = parseInt(res.data.is_force_access_user_info)
|
||||
this.login.is_bind_mobile = parseInt(res.data.is_bind_mobile)
|
||||
this.login.agreement_show = parseInt(res.data.agreement_show)
|
||||
this.login.bg_url = res.data.bg_url // 背景图
|
||||
|
||||
@ -2,17 +2,20 @@ import { defineStore } from 'pinia'
|
||||
import { getInitInfo, getSiteInfo } from '@/app/api/system'
|
||||
import useConfigStore from '@/stores/config'
|
||||
import useMemberStore from '@/stores/member'
|
||||
import { isWeixinBrowser } from '@/utils/common'
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
|
||||
interface System {
|
||||
site: AnyObject | null,
|
||||
siteApps: string[],
|
||||
siteAddons: string[],
|
||||
currRoute: string,
|
||||
location: Object | null, // 定位信息
|
||||
mapConfig: any,
|
||||
initStatus: any, // 初始化状态
|
||||
menuButtonInfo: any, // 如果是小程序,获取右上角胶囊的尺寸信息
|
||||
shareCallback: any // 分享回调
|
||||
shareCallback: any, // 分享回调
|
||||
defaultPositionAddress:any,
|
||||
diyAddressInfo: any // 定位信息
|
||||
}
|
||||
|
||||
const useSystemStore = defineStore('system', {
|
||||
@ -22,7 +25,6 @@ const useSystemStore = defineStore('system', {
|
||||
siteApps: [],
|
||||
siteAddons: [],
|
||||
currRoute: '',
|
||||
location: null,
|
||||
mapConfig: {
|
||||
is_open: 1,
|
||||
valid_time: 0
|
||||
@ -34,13 +36,25 @@ const useSystemStore = defineStore('system', {
|
||||
right: '',
|
||||
width: ''
|
||||
},
|
||||
shareCallback: null
|
||||
shareCallback: null,
|
||||
defaultPositionAddress:'定位中',
|
||||
diyAddressInfo: null
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
// 获取初始化数据信息
|
||||
getInitFn(callback: any) {
|
||||
getInitInfo().then((res: any) => {
|
||||
|
||||
let url = '';
|
||||
// #ifdef H5
|
||||
if (isWeixinBrowser()) {
|
||||
url = uni.getSystemInfoSync().platform == 'ios' ? uni.getStorageSync('initUrl') : location.href
|
||||
}
|
||||
// #endif
|
||||
|
||||
getInitInfo({
|
||||
url
|
||||
}).then((res: any) => {
|
||||
if (res.data) {
|
||||
let data = res.data;
|
||||
|
||||
@ -71,6 +85,7 @@ const useSystemStore = defineStore('system', {
|
||||
configStore.login.is_username = data.login_config.is_username
|
||||
configStore.login.is_mobile = data.login_config.is_mobile
|
||||
configStore.login.is_auth_register = parseInt(data.login_config.is_auth_register)
|
||||
configStore.login.is_force_access_user_info = parseInt(data.login_config.is_force_access_user_info)
|
||||
configStore.login.is_bind_mobile = parseInt(data.login_config.is_bind_mobile)
|
||||
configStore.login.agreement_show = parseInt(data.login_config.agreement_show)
|
||||
configStore.login.bg_url = data.login_config.bg_url // 背景图
|
||||
@ -104,13 +119,26 @@ const useSystemStore = defineStore('system', {
|
||||
}).catch((err) => {
|
||||
})
|
||||
},
|
||||
setLocation(value: any) {
|
||||
var date = new Date();
|
||||
date.setSeconds(60 * this.mapConfig.valid_time);
|
||||
value.valid_time = date.getTime() / 1000; // 定位信息 5分钟内有效,过期后将重新获取定位信息
|
||||
this.location = value;
|
||||
uni.setStorageSync('location', value); // 初始化数据调用
|
||||
}
|
||||
// 当前选择的收货地址信息[经纬度,当前定位地址,定位过期时间]
|
||||
setAddressInfo(data:any = {}) {
|
||||
let addressInfo = cloneDeep(data);
|
||||
// 过期时间
|
||||
var date = new Date();
|
||||
date.setSeconds(60 * this.mapConfig.valid_time);
|
||||
addressInfo.valid_time = date.getTime() / 1000; // 定位信息 5分钟内有效,过期后将重新获取定位信息
|
||||
|
||||
if(this.diyAddressInfo){
|
||||
this.diyAddressInfo = Object.assign(this.diyAddressInfo, addressInfo);
|
||||
}else{
|
||||
this.diyAddressInfo = addressInfo;
|
||||
}
|
||||
|
||||
if(Object.keys(data).length){
|
||||
uni.setStorageSync('location_address', addressInfo);
|
||||
}else{
|
||||
uni.removeStorageSync('location_address');
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@ -474,4 +474,4 @@ button[type='primary'],uni-button[type='primary']{
|
||||
.mescroll-upwarp{
|
||||
opacity: 0;
|
||||
}
|
||||
/******************** mescroll state **********************/
|
||||
/******************** mescroll state **********************/
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
@font-face {
|
||||
font-family: "iconfont"; /* Project id 3952239 */
|
||||
src: url('//at.alicdn.com/t/c/font_3952239_urcc1eq7dmn.woff2?t=1726020629322') format('woff2'),
|
||||
url('//at.alicdn.com/t/c/font_3952239_urcc1eq7dmn.woff?t=1726020629322') format('woff'),
|
||||
url('//at.alicdn.com/t/c/font_3952239_urcc1eq7dmn.ttf?t=1726020629322') format('truetype');
|
||||
src: url('//at.alicdn.com/t/c/font_3952239_w9mu2hnz4eg.woff2?t=1731396621845') format('woff2'),
|
||||
url('//at.alicdn.com/t/c/font_3952239_w9mu2hnz4eg.woff?t=1731396621845') format('woff'),
|
||||
url('//at.alicdn.com/t/c/font_3952239_w9mu2hnz4eg.ttf?t=1731396621845') format('truetype');
|
||||
}
|
||||
|
||||
.iconfont {
|
||||
@ -13,6 +13,26 @@
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.icona-paihangbangpc30:before {
|
||||
content: "\e83a";
|
||||
}
|
||||
|
||||
.iconbiaoqianV6xx2:before {
|
||||
content: "\e824";
|
||||
}
|
||||
|
||||
.iconxinrenV6xx:before {
|
||||
content: "\e821";
|
||||
}
|
||||
|
||||
.iconhuodongV6xx:before {
|
||||
content: "\e820";
|
||||
}
|
||||
|
||||
.iconxuanzhongde-miaobian:before {
|
||||
content: "\e81e";
|
||||
}
|
||||
|
||||
.iconshengyinpc:before {
|
||||
content: "\e7d1";
|
||||
}
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
@font-face {
|
||||
font-family: "nc-iconfont"; /* Project id 4567203 */
|
||||
src: url('//at.alicdn.com/t/c/font_4567203_p1l3213jbpf.woff2?t=1725850647351') format('woff2'),
|
||||
url('//at.alicdn.com/t/c/font_4567203_p1l3213jbpf.woff?t=1725850647351') format('woff'),
|
||||
url('//at.alicdn.com/t/c/font_4567203_p1l3213jbpf.ttf?t=1725850647351') format('truetype');
|
||||
src: url('//at.alicdn.com/t/c/font_4567203_upzww1i5qjn.woff2?t=1731549702579') format('woff2'),
|
||||
url('//at.alicdn.com/t/c/font_4567203_upzww1i5qjn.woff?t=1731549702579') format('woff'),
|
||||
url('//at.alicdn.com/t/c/font_4567203_upzww1i5qjn.ttf?t=1731549702579') format('truetype');
|
||||
}
|
||||
|
||||
.nc-iconfont {
|
||||
@ -13,6 +13,14 @@
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.nc-icon-gouwuche1:before {
|
||||
content: "\e76e";
|
||||
}
|
||||
|
||||
.nc-icon-gouwuche:before {
|
||||
content: "\e6ea";
|
||||
}
|
||||
|
||||
.nc-icon-kabao:before {
|
||||
content: "\e7ec";
|
||||
}
|
||||
|
||||
@ -18,13 +18,14 @@ export const redirect = (redirect: any) => {
|
||||
if (!getToken() && getNeedLoginPages().indexOf(url) != -1) {
|
||||
|
||||
const config = useConfigStore()
|
||||
const systemStore = useSystemStore()
|
||||
|
||||
// #ifdef MP-WEIXIN
|
||||
if (config.login.is_username && !config.login.is_mobile && !config.login.is_auth_register) {
|
||||
url = '/app/pages/auth/login'
|
||||
param = { type: 'username' }
|
||||
mode = 'redirectTo'
|
||||
} else if (!config.login.is_username && !config.login.is_mobile && !config.login.is_auth_register) {
|
||||
} else if (systemStore.initStatus == 'finish' && !config.login.is_username && !config.login.is_mobile && !config.login.is_auth_register) {
|
||||
uni.showToast({ title: '商家未开启登录注册', icon: 'none' })
|
||||
return;
|
||||
} else {
|
||||
@ -40,7 +41,7 @@ export const redirect = (redirect: any) => {
|
||||
url = '/app/pages/auth/login'
|
||||
param = { type: 'username' }
|
||||
mode = 'redirectTo'
|
||||
} else if (!config.login.is_username && !config.login.is_mobile && !config.login.is_auth_register) {
|
||||
} else if (systemStore.initStatus == 'finish' && !config.login.is_username && !config.login.is_mobile && !config.login.is_auth_register) {
|
||||
uni.showToast({ title: '商家未开启登录注册', icon: 'none' })
|
||||
return;
|
||||
} else {
|
||||
@ -53,7 +54,7 @@ export const redirect = (redirect: any) => {
|
||||
url = '/app/pages/auth/login'
|
||||
param = { type: 'username' }
|
||||
mode = 'redirectTo'
|
||||
} else if (!config.login.is_username && !config.login.is_mobile) {
|
||||
} else if (systemStore.initStatus == 'finish' && !config.login.is_username && !config.login.is_mobile) {
|
||||
uni.showToast({ title: '商家未开启登录注册', icon: 'none' })
|
||||
return;
|
||||
} else {
|
||||
@ -456,42 +457,6 @@ export function handleOnloadParams(option: any) {
|
||||
return params;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取定位信息
|
||||
*/
|
||||
export function getLocation(param: any = {}) {
|
||||
uni.getLocation({
|
||||
type: param.type || 'gcj02',
|
||||
success: res => {
|
||||
const systemStore = useSystemStore()
|
||||
systemStore.setLocation(res);
|
||||
typeof param.success == 'function' && param.success(res);
|
||||
},
|
||||
fail: res => {
|
||||
typeof param.fail == 'function' && param.fail(res);
|
||||
},
|
||||
complete: res => {
|
||||
typeof param.complete == 'function' && param.complete(res);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 定位信息(缓存)
|
||||
*/
|
||||
export function locationStorage() {
|
||||
let data = uni.getStorageSync('location');
|
||||
let mapConfig = uni.getStorageSync('mapConfig');
|
||||
if (data) {
|
||||
var date = new Date();
|
||||
if (mapConfig.valid_time > 0) {
|
||||
data.is_expired = (date.getTime() / 1000) > data.valid_time; // 是否过期
|
||||
} else {
|
||||
data.is_expired = false;
|
||||
}
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 深度克隆
|
||||
@ -566,4 +531,4 @@ export function goback(data: any) {
|
||||
});
|
||||
}
|
||||
}, 600);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import useMemberStore from '@/stores/member'
|
||||
import { t } from '@/locale'
|
||||
import { getToken, getAppChannel, redirect, currRoute } from './common'
|
||||
import { getToken, getAppChannel, redirect, currRoute, isUrl } from './common'
|
||||
import qs from 'qs'
|
||||
|
||||
interface RequestConfig {
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
// #ifdef H5
|
||||
import wx from 'weixin-js-sdk'
|
||||
// #endif
|
||||
import { getWechatSkdConfig } from '@/app/api/system'
|
||||
import { getWechatSdkConfig } from '@/app/api/system'
|
||||
import { isWeixinBrowser } from '@/utils/common'
|
||||
|
||||
class Wechat {
|
||||
@ -11,8 +11,8 @@ class Wechat {
|
||||
// #endif
|
||||
}
|
||||
|
||||
public init() {
|
||||
getWechatSkdConfig({
|
||||
public init(callback:any = null) {
|
||||
getWechatSdkConfig({
|
||||
url: uni.getSystemInfoSync().platform == 'ios' ? uni.getStorageSync('initUrl') : location.href
|
||||
}).then((res: any) => {
|
||||
const { data } = res
|
||||
@ -22,8 +22,9 @@ class Wechat {
|
||||
timestamp: data.timestamp, // 必填,生成签名的时间戳
|
||||
nonceStr: data.nonceStr, // 必填,生成签名的随机串
|
||||
signature: data.signature,// 必填,签名
|
||||
jsApiList: ['chooseWXPay', 'updateAppMessageShareData', 'updateTimelineShareData', 'scanQRCode'] // 必填,需要使用的JS接口列表
|
||||
jsApiList: ['chooseWXPay', 'updateAppMessageShareData', 'updateTimelineShareData', 'scanQRCode', 'getLocation'] // 必填,需要使用的JS接口列表
|
||||
});
|
||||
if(callback) callback();
|
||||
})
|
||||
}
|
||||
|
||||
@ -63,6 +64,21 @@ class Wechat {
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取地理位置接口
|
||||
* @param {Object} callback
|
||||
*/
|
||||
public getLocation(callback: any) {
|
||||
wx.ready(function() {
|
||||
wx.getLocation({
|
||||
type: 'gcj02',
|
||||
success: function(res) {
|
||||
typeof callback == 'function' && callback(res);
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export default new Wechat()
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user