mirror of
https://gitee.com/niucloud-team/niucloud.git
synced 2026-03-12 19:05:28 +00:00
up
This commit is contained in:
parent
8bf3968007
commit
7900632322
@ -9,7 +9,7 @@
|
||||
@click="diyStore.changeCurrentIndex(index, component)"
|
||||
:class="diyGroup.getComponentClass(index,component)" :style="component.pageStyle"
|
||||
>
|
||||
<view class="relative" :style="{ marginTop : component.margin.top < 0 ? (component.margin.top * 2) + 'rpx' : '0' }">
|
||||
<view class="relative" :style="{ marginTop : component.margin.top < 0 ? (component.margin.top * 2) + 'rpx' : '0', marginBottom : component.margin.bottom < 0 ? (component.margin.bottom * 2) + 'rpx' : '0' }">
|
||||
|
||||
<!-- 装修模式下,设置负上边距后超出的内容,禁止选中设置 -->
|
||||
<view v-if="diyGroup.isShowPlaceHolder(index,component)" class="absolute w-full z-1"
|
||||
@ -122,9 +122,6 @@
|
||||
<diy-form-file ref="diyFormFileRef" :component="component" :global="data.global" :index="index" />
|
||||
</template>
|
||||
|
||||
|
||||
<!-- 以下是addon文件夹下的自定义组件 -->
|
||||
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
@ -135,7 +132,6 @@
|
||||
</view>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
|
||||
import topTabbar from '@/components/top-tabbar/top-tabbar.vue'
|
||||
import popAds from '@/components/pop-ads/pop-ads.vue'
|
||||
import { useDiyGroup } from './useDiyGroup'
|
||||
|
||||
@ -131,3 +131,13 @@ export function bind(data: AnyObject) {
|
||||
export function memberLog(data: AnyObject) {
|
||||
return request.post('member/log', data, { showErrorMessage: false })
|
||||
}
|
||||
|
||||
/**
|
||||
* 微信app授权登录
|
||||
*/
|
||||
export function wxappLogin(data: AnyObject) {
|
||||
if (uni.getStorageSync('pid')) {
|
||||
data.pid = uni.getStorageSync('pid');
|
||||
}
|
||||
return request.post('wxapp/login', data, { showErrorMessage: false })
|
||||
}
|
||||
@ -157,4 +157,11 @@ export function getInitInfo(params: Record<string, any>) {
|
||||
*/
|
||||
export function getMemberMobileExist(params: Record<string, any>) {
|
||||
return request.get('member_mobile_exist', params)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取最新版本信息
|
||||
*/
|
||||
export function getNewVersion(params: Record<string, any>) {
|
||||
return request.get('app/newversion', params)
|
||||
}
|
||||
|
||||
@ -65,7 +65,7 @@
|
||||
<!-- <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="name" :style="{'color': getTabColor(index == currTabIndex)}">{{ item ? item.text : '' }}</view>
|
||||
<!-- <view class="line" :style="{'background-color': getTabColor(index == currTabIndex)}" v-if="index == currTabIndex"></view> -->
|
||||
</view>
|
||||
</scroll-view>
|
||||
@ -148,8 +148,8 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
// 轮播搜索
|
||||
import { ref, reactive, computed, watch, onMounted, nextTick, getCurrentInstance } from 'vue';
|
||||
import { img } from '@/utils/common';
|
||||
import { ref, reactive, computed, watch, onMounted, nextTick, getCurrentInstance, onUnmounted } from 'vue';
|
||||
import { img, getTopFixedStatusName } from '@/utils/common';
|
||||
import useDiyStore from '@/app/stores/diy';
|
||||
import diyGroup from '@/addon/components/diy/group/index.vue';
|
||||
import { getDiyInfo } from '@/app/api/diy';
|
||||
@ -164,6 +164,12 @@ const diyComponent = computed(() => {
|
||||
if (diyStore.mode == 'decorate') {
|
||||
return diyStore.value[props.index];
|
||||
} else {
|
||||
// 解决在电脑端,切换微页面时,装修页与h5页面之间的相互影响
|
||||
if(uni.getStorageSync(getTopFixedStatusName(props.global))){
|
||||
diyStore.topFixedStatus = uni.getStorageSync(getTopFixedStatusName(props.global));
|
||||
}else{
|
||||
diyStore.topFixedStatus = 'home'
|
||||
}
|
||||
return props.component;
|
||||
}
|
||||
})
|
||||
@ -376,8 +382,8 @@ const changeData = (item: any, index: any) => {
|
||||
// 查询微页面数据
|
||||
diyStore.topFixedStatus = 'diy'
|
||||
getDiyInfoFn(item.diy_id);
|
||||
|
||||
}
|
||||
uni.setStorageSync(getTopFixedStatusName(props.global), diyStore.topFixedStatus);
|
||||
}
|
||||
|
||||
const tabAllPopup = ref(false);
|
||||
@ -389,6 +395,11 @@ const navbarInnerStyle = ref('')
|
||||
// 如果是各家小程序,导航栏内部的宽度需要减少右边胶囊的宽度
|
||||
navbarInnerStyle.value += 'padding-top:' + systemStore.menuButtonInfo.top + 'px;';
|
||||
// #endif
|
||||
|
||||
// #ifdef APP-PLUS
|
||||
navbarInnerStyle.value += 'padding-top:' + systemStore.systemInfo.statusBarHeight + 'px;';
|
||||
// #endif
|
||||
|
||||
onMounted(() => {
|
||||
refresh();
|
||||
// 装修模式下刷新
|
||||
@ -397,6 +408,7 @@ onMounted(() => {
|
||||
() => diyComponent.value,
|
||||
(newValue, oldValue) => {
|
||||
if (newValue && newValue.componentName == 'CarouselSearch') {
|
||||
diyStore.topFixedStatus = 'home'; // 装修页的微页面始终展示home
|
||||
refresh();
|
||||
}
|
||||
}
|
||||
@ -427,11 +439,14 @@ onMounted(() => {
|
||||
// #endif
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
uni.removeStorageSync(getTopFixedStatusName(props.global))
|
||||
})
|
||||
|
||||
const refresh = () => {
|
||||
setModuleLocation();
|
||||
// 刷新定位
|
||||
locationVal.refresh();
|
||||
|
||||
changeData({ source: 'home' }, -1)
|
||||
diyComponent.value.swiper.list.forEach((item: any) => {
|
||||
if (item.imageUrl == '') {
|
||||
@ -471,6 +486,7 @@ const getDiyInfoFn = (id: any) => {
|
||||
diyPageData.value = sources.value;
|
||||
|
||||
diyPageData.value.forEach((item: any, index) => {
|
||||
item.componentIsShow = true // 是否显示
|
||||
item.pageStyle = '';
|
||||
if (item.pageStartBgColor) {
|
||||
if (item.pageStartBgColor && item.pageEndBgColor) item.pageStyle += `background:linear-gradient(${ item.pageGradientAngle },${ item.pageStartBgColor },${ item.pageEndBgColor });`;
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<view class="w-screen h-screen flex flex-col" :style="themeColor()">
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<!-- #ifdef MP-WEIXIN || APP-PLUS -->
|
||||
<view :style="{'height':headerHeight}">
|
||||
<top-tabbar :data="param" :scrollBool="topTabarObj.getScrollBool()" class="top-header" />
|
||||
</view>
|
||||
@ -28,7 +28,7 @@
|
||||
</u-form>
|
||||
<view class="mt-[100rpx]">
|
||||
<view v-if="config.agreement_show" class="flex items-center mb-[20rpx] py-[10rpx]" @click.stop="agreeChange">
|
||||
<u-checkbox-group @change="agreeChange">
|
||||
<u-checkbox-group @change="agreeChange">
|
||||
<u-checkbox activeColor="var(--primary-color)" :checked="isAgree" shape="circle" size="24rpx" :customStyle="{ 'marginTop': '4rpx' }" />
|
||||
</u-checkbox-group>
|
||||
<view class="text-[24rpx] text-[var(--text-color-light6)] flex items-center flex-wrap">
|
||||
@ -79,20 +79,24 @@ const isAgree = ref(false)
|
||||
const formData: any = reactive({
|
||||
mobile: '',
|
||||
mobile_code: '',
|
||||
mobile_key: ''
|
||||
mobile_key: '',
|
||||
register_type: ''
|
||||
})
|
||||
|
||||
const real_name_input = ref(true);
|
||||
|
||||
onLoad(() => {
|
||||
onLoad((data) => {
|
||||
// 防止浏览器自动填充
|
||||
setTimeout(() => {
|
||||
real_name_input.value = false;
|
||||
}, 800)
|
||||
|
||||
data.register_type && (Object.assign(formData, { register_type: data.register_type }))
|
||||
uni.getStorageSync('openid') && (Object.assign(formData, { openid: uni.getStorageSync('openid') }))
|
||||
uni.getStorageSync('pid') && (Object.assign(formData, { pid: uni.getStorageSync('pid') }))
|
||||
uni.getStorageSync('unionid') && (Object.assign(formData, { unionid: uni.getStorageSync('unionid') }))
|
||||
uni.getStorageSync('nickname') && (Object.assign(formData, { nickname: uni.getStorageSync('nickname') }))
|
||||
uni.getStorageSync('avatar') && (Object.assign(formData, { avatar: uni.getStorageSync('avatar') }))
|
||||
});
|
||||
|
||||
const rules = {
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<view class="w-screen h-screen" :style="themeColor()">
|
||||
<view class="w-screen h-screen" :style="warpStyle">
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<!-- #ifdef MP-WEIXIN || APP-PLUS -->
|
||||
<view :style="{'height':headerHeight}">
|
||||
<top-tabbar :data="param" :scrollBool="topTabarObj.getScrollBool()" class="top-header" />
|
||||
</view>
|
||||
@ -16,7 +16,7 @@
|
||||
</view>
|
||||
<view v-else class="h-[90rpx] w-[300rpx]"></view>
|
||||
</view>
|
||||
<view class="text-[var(--text-color-light6)]] text-[28rpx] text-center leading-[34rpx] min-h-[34rpx] mt-[40rpx]">{{ loginConfig.desc }}</view>
|
||||
<view class="text-[var(--text-color-light6)]] text-[28rpx] text-center leading-[34rpx] min-h-[34rpx] mt-[40rpx]">{{ loginConfig.desc }}</view>
|
||||
<view class="mt-[181rpx]">
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- 微信公众号快捷登录,开启自动注册的情况下才能使用 -->
|
||||
@ -52,11 +52,11 @@
|
||||
|
||||
<!-- 手机号登录 -->
|
||||
<view v-if="loginConfig.is_mobile" class="mb-[40rpx] w-full flex items-center justify-center">
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifndef MP -->
|
||||
<button class="w-[630rpx] h-[88rpx] !mx-[0] !bg-[#fff] border-[var(--primary-color)] border-solid border-[2rpx] text-[26rpx] rounded-[44rpx] leading-[84rpx] !text-[var(--primary-color)]" @click="redirect({ url: '/app/pages/auth/login',param:{type:'mobile'}})">{{ t('mobileLogin') }}</button>
|
||||
<!-- #endif -->
|
||||
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<!-- #ifdef MP -->
|
||||
<button v-if="authRegisterLogin && loginConfig.is_mobile"
|
||||
class="w-[630rpx] h-[88rpx] !mx-[0] !bg-[#fff] border-[var(--primary-color)] border-solid border-[2rpx] text-[26rpx] rounded-[44rpx] leading-[84rpx] !text-[var(--primary-color)]"
|
||||
@click="redirect({ url: '/app/pages/auth/login',param:{type:'mobile'}})">
|
||||
@ -272,7 +272,7 @@ const oneClickLogin = (callback: any = null, data: any = null) => {
|
||||
mask: true
|
||||
});
|
||||
loginLoading.value = true
|
||||
|
||||
|
||||
if (!callback) {
|
||||
callback = () => {
|
||||
loginLoading.value = false
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<view class="w-screen h-screen flex flex-col " :style="themeColor()" v-if="type">
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<!-- #ifdef MP-WEIXIN || APP-PLUS -->
|
||||
<view :style="{'height':headerHeight}">
|
||||
<top-tabbar :data="param" :scrollBool="topTabarObj.getScrollBool()" class="top-header" />
|
||||
</view>
|
||||
@ -28,9 +28,12 @@
|
||||
class="!bg-transparent" :disabled="real_name_input" fontSize="26rpx"
|
||||
placeholderClass="!text-[var(--text-color-light9)] text-[26rpx]">
|
||||
<template #suffix>
|
||||
<!-- #ifndef APP-PLUS -->
|
||||
<view @click="changePassword" v-if="formData.password">
|
||||
<u-icon :name="isPassword?'eye-off':'eye-fill'" color="#b9b9b9" size="20"></u-icon>
|
||||
<u-icon name="eye-off" color="#b9b9b9" size="20" v-if="isPassword"></u-icon>
|
||||
<u-icon name="eye-fill" color="#b9b9b9" size="20" v-else></u-icon>
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
</template>
|
||||
</u-input>
|
||||
</u-form-item>
|
||||
@ -163,7 +166,7 @@ onLoad(async(option: any) => {
|
||||
// #ifdef MP-WEIXIN
|
||||
uni.getStorageSync('openid') && (Object.assign(formData, { weapp_openid: uni.getStorageSync('openid') }))
|
||||
// #endif
|
||||
|
||||
|
||||
if (option.type) {
|
||||
if (option.type == 'mobile') {
|
||||
if (configStore.login.is_mobile) {
|
||||
@ -205,6 +208,12 @@ onLoad(async(option: any) => {
|
||||
isShowQuickLogin.value = false;
|
||||
}
|
||||
// #endif
|
||||
|
||||
// #ifdef APP-PLUS
|
||||
if (systemStore.appConfig.wechat_app_id) {
|
||||
isShowQuickLogin.value = true;
|
||||
}
|
||||
// #endif
|
||||
})
|
||||
|
||||
const formData = reactive({
|
||||
@ -300,6 +309,7 @@ const handleLogin = () => {
|
||||
}
|
||||
|
||||
const toLink = () => {
|
||||
// #ifndef APP-PLUS
|
||||
const pages = getCurrentPages(); // 获取页面栈
|
||||
if (pages.length > 1) {
|
||||
const currentPage = pages[pages.length - 2].route;
|
||||
@ -314,7 +324,17 @@ const toLink = () => {
|
||||
} else {
|
||||
redirect({ url: '/app/pages/auth/index', mode: 'redirectTo' })
|
||||
}
|
||||
// #endif
|
||||
|
||||
// #ifdef APP-PLUS
|
||||
useLogin().getAuthCode({
|
||||
successCallback: () => {
|
||||
if (!getToken()) {
|
||||
redirect({ url: '/app/pages/auth/bind', param: { 'register_type': 'wechat' }, mode: 'redirectTo' })
|
||||
}
|
||||
}
|
||||
})
|
||||
// #endif
|
||||
}
|
||||
|
||||
const toResetpwd = () =>{
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<view class="w-screen h-screen flex flex-col" :style="themeColor()" v-if="type">
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<!-- #ifdef MP-WEIXIN || APP-PLUS -->
|
||||
<view :style="{'height':headerHeight}">
|
||||
<top-tabbar :data="param" :scrollBool="topTabarObj.getScrollBool()" class="top-header" />
|
||||
</view>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<view class="w-screen h-screen flex flex-col" :style="themeColor()">
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<!-- #ifdef MP-WEIXIN || APP-PLUS -->
|
||||
<view :style="{'height':headerHeight}">
|
||||
<top-tabbar :data="param" :scrollBool="topTabarObj.getScrollBool()" class="top-header" />
|
||||
</view>
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
<view :style="themeColor()">
|
||||
<view class="bg-[var(--page-bg-color)] min-h-screen overflow-hidden" v-if="Object.keys(friendsInfo).length && !loading">
|
||||
<view :style="{background: 'url(' + img('static/resource/images/app/friendpay_money.png') + ') left bottom / cover no-repeat'}" class="pb-[194rpx] overflow-hidden">
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<!-- #ifdef MP-WEIXIN || APP-PLUS -->
|
||||
<view class="sticky top-0 left-0 right-0 z-100">
|
||||
<top-tabbar :data="topTabbarData" :scrollBool="topTabarObj.getScrollBool()" />
|
||||
</view>
|
||||
@ -37,8 +37,8 @@
|
||||
<text class="nc-iconfont nc-icon-jichuxinxiV6xx text-[26rpx] "></text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
|
||||
|
||||
|
||||
</view>
|
||||
<view class="card-template sidebar-margin mb-[var(--top-m)]" v-if="friendsInfo.config.pay_info_switch">
|
||||
<template v-if="JSON.stringify(friendsInfo.trade_info) !== '[]' && friendsInfo.trade_info.item_list.length">
|
||||
@ -52,11 +52,11 @@
|
||||
<view class="border-0 border-solid border-b-[1rpx] border-[#f6f6f6] mb-[20rpx]">
|
||||
<view v-for="(item, index) in friendsInfo.trade_info.item_list" class="flex justify-between" :class="{' mb-[34rpx]': (index + 1) != friendsInfo.trade_info.length }">
|
||||
<view class="w-[170rpx] h-[170rpx] rounded-[var(--goods-rounded-big)] overflow-hidden flex-shrink-0">
|
||||
<u--image class="overflow-hidden" radius="var(--goods-rounded-big)" width="170rpx" height="170rpx" :src="img(item.item_image ? item.item_image : '')" model="aspectFill">
|
||||
<up-image class="overflow-hidden" radius="var(--goods-rounded-big)" width="170rpx" height="170rpx" :src="img(item.item_image ? item.item_image : '')" model="aspectFill">
|
||||
<template #error>
|
||||
<image class="w-[170rpx] h-[170rpx] rounded-[var(--goods-rounded-big)] overflow-hidden" :src="img('static/resource/images/diy/shop_default.jpg')" mode="aspectFill" />
|
||||
</template>
|
||||
</u--image>
|
||||
</up-image>
|
||||
</view>
|
||||
<view class="ml-[20rpx] flex flex-1 flex-col justify-between">
|
||||
<view>
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
<view :style="themeColor()">
|
||||
<view class="bg-[var(--page-bg-color)] min-h-screen overflow-hidden" v-if="Object.keys(friendsInfo).length && !loading">
|
||||
<view :style="{background: 'url(' + img('static/resource/images/app/friendpay_bg.png') + ') left bottom /100% no-repeat'}" class="pb-[168rpx] overflow-hidden">
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<!-- #ifdef MP-WEIXIN || APP-PLUS -->
|
||||
<view class="sticky top-0 left-0 right-0 z-100">
|
||||
<top-tabbar :data="topTabbarData" :scrollBool="topTabarObj.getScrollBool()" />
|
||||
</view>
|
||||
@ -45,11 +45,11 @@
|
||||
<view class="border-0 border-solid border-b-[1rpx] border-[#f6f6f6] mb-[20rpx]">
|
||||
<view v-for="(item, index) in friendsInfo.trade_info.item_list" class="flex justify-between mb-[30rpx]">
|
||||
<view class="w-[170rpx] h-[170rpx] rounded-[var(--goods-rounded-big)] overflow-hidden flex-shrink-0">
|
||||
<u--image class="overflow-hidden" radius="var(--goods-rounded-big)" width="170rpx" height="170rpx" :src="img(item.item_image ? item.item_image : '')" model="aspectFill">
|
||||
<up-image class="overflow-hidden" radius="var(--goods-rounded-big)" width="170rpx" height="170rpx" :src="img(item.item_image ? item.item_image : '')" model="aspectFill">
|
||||
<template #error>
|
||||
<image class="w-[170rpx] h-[170rpx] rounded-[var(--goods-rounded-big)] overflow-hidden" :src="img('static/resource/images/diy/shop_default.jpg')" mode="aspectFill" />
|
||||
</template>
|
||||
</u--image>
|
||||
</up-image>
|
||||
</view>
|
||||
<view class="ml-[20rpx] flex flex-1 flex-col justify-between">
|
||||
<view>
|
||||
|
||||
@ -19,7 +19,10 @@
|
||||
<!-- 小程序隐私协议 -->
|
||||
<wx-privacy-popup ref="wxPrivacyPopupRef"></wx-privacy-popup>
|
||||
<!-- #endif -->
|
||||
|
||||
<!-- 版本更新弹窗 -->
|
||||
<!-- #ifdef APP -->
|
||||
<update-version ref="updateVersionRef"></update-version>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
</template>
|
||||
|
||||
@ -29,6 +32,8 @@ import { useDiy } from '@/hooks/useDiy'
|
||||
import { redirect } from '@/utils/common';
|
||||
import { useShare } from '@/hooks/useShare'
|
||||
import diyGroup from '@/addon/components/diy/group/index.vue'
|
||||
import updateVersion from '@/components/update-version/update-version.vue'
|
||||
import useSystemStore from '@/stores/system';
|
||||
|
||||
const { setShare } = useShare()
|
||||
|
||||
@ -42,6 +47,11 @@ const diyGroupRef = ref(null)
|
||||
|
||||
const wxPrivacyPopupRef: any = ref(null)
|
||||
const collectTipRef: any = ref(null)
|
||||
const updateVersionRef: any = ref(null)
|
||||
|
||||
const systemStore = useSystemStore();
|
||||
const versionInfo = systemStore.versionInfo
|
||||
|
||||
// 监听页面加载
|
||||
diy.onLoad();
|
||||
|
||||
@ -52,8 +62,17 @@ diy.onShow((data: any) => {
|
||||
// title: diyData.title
|
||||
// })
|
||||
} else if (data.page) {
|
||||
// 跳转到设置的启动页
|
||||
redirect({ url: data.page, mode: 'reLaunch' })
|
||||
// 是否需要强制更新 -- app
|
||||
if(versionInfo && versionInfo.is_forced_upgrade){
|
||||
nextTick(() => {
|
||||
if(updateVersionRef.value){
|
||||
updateVersionRef.value.open()
|
||||
}
|
||||
})
|
||||
}else{
|
||||
// 跳转到设置的启动页
|
||||
redirect({ url: data.page, mode: 'reLaunch' })
|
||||
}
|
||||
}
|
||||
let share = data.share ? JSON.parse(data.share) : null;
|
||||
setShare(share);
|
||||
|
||||
@ -80,8 +80,10 @@ 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'
|
||||
import { getAddressByLatlng,getAreatree } from '@/app/api/system'
|
||||
import useSystemStore from '@/stores/system';
|
||||
|
||||
const systemStore = useSystemStore();
|
||||
const formData: any = ref({
|
||||
id: 0,
|
||||
name: '',
|
||||
@ -133,6 +135,7 @@ onLoad((data: any) => {
|
||||
if (selectAddress) {
|
||||
addressType.value = selectAddress.delivery == 'express' ? 'address' : 'locationAddress';
|
||||
}
|
||||
getAreatreeFn()
|
||||
// #ifdef MP
|
||||
nextTick(() => {
|
||||
if (wxPrivacyPopupRef.value) wxPrivacyPopupRef.value.proactive();
|
||||
@ -256,8 +259,13 @@ const save = () => {
|
||||
|
||||
// 选择地址
|
||||
const chooseLocation = () => {
|
||||
// #ifdef MP
|
||||
let latitude = systemStore.diyAddressInfo ? systemStore.diyAddressInfo.latitude : '';
|
||||
let longitude = systemStore.diyAddressInfo ? systemStore.diyAddressInfo.longitude : '';
|
||||
|
||||
// #ifndef H5
|
||||
uni.chooseLocation({
|
||||
latitude,
|
||||
longitude,
|
||||
success: (res) => {
|
||||
res.latitude && (formData.value.lat = res.latitude)
|
||||
res.longitude && (formData.value.lng = res.longitude)
|
||||
@ -286,7 +294,7 @@ const chooseLocation = () => {
|
||||
});
|
||||
// #endif
|
||||
|
||||
// #ifdef H5
|
||||
// #ifndef H5
|
||||
const urlencode = formData.value;
|
||||
uni.setStorageSync('addressInfo', urlencode);
|
||||
let backurl = location.origin + location.pathname + '?source=' + source.value;
|
||||
@ -308,7 +316,6 @@ const getAddress = (latlng: any) => {
|
||||
|
||||
formData.value.address_name = formData.value.full_address.replace(/-/g, '');
|
||||
formData.value.area = (res.data.province + res.data.city + res.data.district) || 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;
|
||||
@ -330,20 +337,46 @@ const getQueryVariable = (variable: any) => {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
const areaTree = ref([]); // 存储地址树数据
|
||||
const getAreatreeFn = () => {
|
||||
getAreatree(3).then(res => {
|
||||
areaTree.value = res.data
|
||||
}).catch()
|
||||
|
||||
}
|
||||
// 递归函数:根据名称和父id,查找对应id
|
||||
const findIdByNameAndParentId = (list, name, parentId = 0) => {
|
||||
for (const item of list) {
|
||||
if (item.name === name && item.pid === parentId) {
|
||||
return item.id;
|
||||
}
|
||||
if (item.child && item.child.length > 0) {
|
||||
const childId = findIdByNameAndParentId(item.child, name, item.id);
|
||||
if (childId) {
|
||||
return childId;
|
||||
}
|
||||
}
|
||||
}
|
||||
return '';
|
||||
};
|
||||
const loadingchoosegAddress = ref(false)
|
||||
const choosegAddress = () =>{
|
||||
loadingchoosegAddress.value = true
|
||||
uni.chooseAddress({
|
||||
success(res) {
|
||||
console.log(7744)
|
||||
loadingchoosegAddress.value = false
|
||||
// 将地址信息格式化为“名字-手机号-地址(详细地址)”
|
||||
// const formattedAddress = `${res.userName}-${res.telNumber}-${res.provinceName}${res.cityName}${res.countyName}${res.detailInfoNew || res.detailInfo}`;
|
||||
console.log(res)
|
||||
formData.value.name = res.userName
|
||||
formData.value.mobile = res.telNumber
|
||||
formData.value.area = res.provinceName + res.cityName + res.countyName
|
||||
formData.value.address = res.detailInfo
|
||||
formData.value.name = res.userName
|
||||
formData.value.mobile = res.telNumber
|
||||
formData.value.area = res.provinceName + res.cityName + res.countyName
|
||||
formData.value.address = res.detailInfo
|
||||
// 匹配省id
|
||||
formData.value.province_id = findIdByNameAndParentId(areaTree.value, res.provinceName);
|
||||
// 匹配市id(基于省id)
|
||||
formData.value.city_id = findIdByNameAndParentId(areaTree.value, res.cityName, formData.value.province_id);
|
||||
// 匹配区id(基于市id)
|
||||
formData.value.district_id = findIdByNameAndParentId(areaTree.value, res.countyName, formData.value.cityId);
|
||||
},
|
||||
fail(err) {
|
||||
loadingchoosegAddress.value = false
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
<view class="min-h-[100vh] !bg-[var(--page-bg-color)]" :style="themeColor()" v-if="memberStore.info">
|
||||
<view class="fixed w-full z-2 !bg-[var(--page-bg-color)]">
|
||||
<view class="pb-[190rpx] text-[#fff] w-full" :style="headerStyle">
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<!-- #ifdef MP-WEIXIN || APP-PLUS -->
|
||||
<top-tabbar :data="param" class="top-header" />
|
||||
<!-- #endif -->
|
||||
<view class="leading-[38rpx] text-[28rpx] pl-[60rpx] pt-[100rpx]">{{ t('accountBalance') }}</view>
|
||||
@ -153,6 +153,9 @@ const mescrollTop = computed(() => {
|
||||
return '718rpx'
|
||||
}
|
||||
} else {
|
||||
// #ifdef APP-PLUS
|
||||
return (pxToRpx(Number(systemStore.systemInfo.statusBarHeight)) + pxToRpx(8) + 632) + 'rpx'
|
||||
// #endif
|
||||
if (Object.keys(systemStore.menuButtonInfo).length) {
|
||||
return (pxToRpx(Number(systemStore.menuButtonInfo.height)) + pxToRpx(systemStore.menuButtonInfo.top) + pxToRpx(8) + 632) + 'rpx'
|
||||
} else {
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
<view class="bg-[var(--page-bg-color)] min-h-[100vh] w-full" :style="themeColor()" v-if="memberStore.info">
|
||||
<view class="fixed w-full z-2 !bg-[var(--page-bg-color)]">
|
||||
<view class="pb-[272rpx]" :style="headerStyle">
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<!-- #ifdef MP-WEIXIN || APP-PLUS -->
|
||||
<top-tabbar :data="param" class="top-header" />
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
|
||||
@ -45,6 +45,9 @@ onMounted(() => {
|
||||
|
||||
try {
|
||||
comp.field.value = JSON.parse(item.field_value)
|
||||
if (comp.componentName=="FormNumber" || comp.componentName=="FormIdentity") {
|
||||
comp.field.value =String(item.field_value)
|
||||
}
|
||||
} catch (error) {
|
||||
comp.field.value = item.field_value
|
||||
}
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
<view :style="themeColor()" class="bg-[var(--page-bg-color)] min-h-[100vh] overflow-hidden">
|
||||
<loading-page :loading="loading && memberInfo"></loading-page>
|
||||
<view v-if="!loading && memberInfo && list && list.length" class="min-h-[100vh] overflow-hidden flex flex-col" :style="{backgroundColor: currLevelInfo.level_style.bg_color }">
|
||||
<!-- #ifdef MP -->
|
||||
<!-- #ifdef MP || APP-PLUS -->
|
||||
<top-tabbar :data="topTabbarData" :scrollBool="topTabarObj.getScrollBool()" />
|
||||
<!-- #endif -->
|
||||
<view>
|
||||
@ -111,7 +111,7 @@
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="!loading && (!list || !list.length)">
|
||||
<!-- #ifdef MP -->
|
||||
<!-- #ifdef MP || APP-PLUS -->
|
||||
<top-tabbar :data="topTabbarDataEmpty" />
|
||||
<!-- #endif -->
|
||||
<view class="empty-page" >
|
||||
@ -119,7 +119,7 @@
|
||||
<text class="desc">暂无会员等级</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
|
||||
</view>
|
||||
</template>
|
||||
|
||||
|
||||
@ -95,6 +95,9 @@ diy.onLoad((data: any) => {
|
||||
if(formDetailData[item.id]){
|
||||
try {
|
||||
item.field.value = JSON.parse(formDetailData[item.id])
|
||||
if (item.componentName=="FormNumber" || item.componentName=="FormIdentity") {
|
||||
item.field.value = String(formDetailData[item.id])
|
||||
}
|
||||
} catch (e) {
|
||||
item.field.value = formDetailData[item.id]
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
<template v-if="!loading">
|
||||
<view class="w-full bg-[var(--page-bg-color)]">
|
||||
<view class="pb-[210rpx] relative" :style="headerStyle">
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<!-- #ifdef MP-WEIXIN || APP-PLUS -->
|
||||
<top-tabbar :data="param" :scrollBool="topTabarObj.getScrollBool()" class="top-header" />
|
||||
<!-- #endif -->
|
||||
<view class="text-[70rpx] leading-[90rpx] text-[#fff] pl-[60rpx] font-500 pt-[77rpx] price-font">{{ pointInfo.point || 0 }}</view>
|
||||
@ -124,7 +124,10 @@ const headerStyle = computed(() => {
|
||||
}
|
||||
})
|
||||
const topStyle = computed(() => {
|
||||
let style = Object.keys(systemStore.menuButtonInfo).length ? (pxToRpx(Number(systemStore.menuButtonInfo.height)) + pxToRpx(systemStore.menuButtonInfo.top) + 50) + 'rpx;' : '50rpx'
|
||||
let style = Object.keys(systemStore.menuButtonInfo).length ? (pxToRpx(Number(systemStore.menuButtonInfo.height)) + pxToRpx(systemStore.menuButtonInfo.top) + 50) + 'rpx' : '50rpx'
|
||||
// #ifdef APP-PLUS
|
||||
style = '120rpx'
|
||||
// #endif
|
||||
return style
|
||||
})
|
||||
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
<view class="min-h-screen overflow-hidden" v-if="Object.values(info).length" :class="{ 'bg-[#F6F6F6]' : info && info.is_use }">
|
||||
<view v-if="info.is_use">
|
||||
<view class="sigin-header">
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<!-- #ifndef H5 -->
|
||||
<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>
|
||||
@ -154,7 +154,7 @@
|
||||
</view>
|
||||
</view>
|
||||
<view class="h-[100vh] w-[100vw] flex justify-center items-center" v-else>
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<!-- #ifdef MP-WEIXIN || APP-PLUS -->
|
||||
<top-tabbar :data="topTabbarData" :scrollBool="topTabarObj.getScrollBool()" class="top-header" />
|
||||
<!-- #endif -->
|
||||
<u-empty text="签到未开启" width="347rpx" height="265rpx" :icon="img('static/resource/images/system/empty.png')" />
|
||||
@ -470,11 +470,22 @@ let topTabbarData = topTabarObj.setTopTabbarParam({ title: '我的签到' })
|
||||
/********* 自定义头部 - end ***********/
|
||||
|
||||
const headStyle = computed(() => {
|
||||
// #ifdef MP
|
||||
let style = pxToRpx(Number(systemStore.menuButtonInfo.height) + systemStore.menuButtonInfo.top + 8) + 382 + 'rpx;'
|
||||
// #endif
|
||||
// #ifdef APP-PLUS
|
||||
let style = pxToRpx(systemStore.systemInfo.statusBarHeight + 8) + 382 + 'rpx'
|
||||
// #endif
|
||||
return style
|
||||
})
|
||||
const topStyle = computed(() => {
|
||||
// #ifdef MP
|
||||
let style = pxToRpx(Number(systemStore.menuButtonInfo.height) + systemStore.menuButtonInfo.top + 8) + 50 + 'rpx;'
|
||||
// #endif
|
||||
// #ifdef APP-PLUS
|
||||
let style = pxToRpx(systemStore.systemInfo.statusBarHeight + 8) + 50 + 'rpx'
|
||||
// #endif
|
||||
console.log(style)
|
||||
return style
|
||||
})
|
||||
</script>
|
||||
|
||||
@ -5,6 +5,14 @@
|
||||
<u-cell :title="t('personalSettings')" :is-link="true" url="/app/pages/member/personal"></u-cell>
|
||||
<u-cell :title="t('switchLang')" :is-link="true" :value="lang" @click="langSheetShow = true"></u-cell>
|
||||
<u-cell :title="t('version')" :value="version"></u-cell>
|
||||
<!-- #ifdef APP -->
|
||||
<u-cell :title="'检测到新版本' + versionInfo.version_name" :is-link="true" @click="checkUpdate" v-if="versionInfo">
|
||||
<template #value>
|
||||
<text class="u-slot-value flex items-center">立即升级<text class="ml-[5px] w-[10rpx] h-[10rpx] bg-[red] rounded-[10rpx]"></text></text>
|
||||
</template>
|
||||
</u-cell>
|
||||
<u-cell title="版本更新" :is-link="true" value="检查版本更新" @click="checkUpdate" v-else/>
|
||||
<!-- #endif -->
|
||||
</u-cell-group>
|
||||
</view>
|
||||
<view class="mb-[var(--top-m)] sidebar-margin card-template !py-[20rpx]">
|
||||
@ -19,6 +27,10 @@
|
||||
<u-action-sheet :actions="langList" :show="langSheetShow" :closeOnClickOverlay="true"
|
||||
:safeAreaInsetBottom="true"
|
||||
@close="langSheetShow = false" @select="switchLang"></u-action-sheet>
|
||||
<!-- 版本更新弹窗 -->
|
||||
<!-- #ifdef APP -->
|
||||
<!-- <update-version ref="updateVersionRef"></update-version> -->
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
</template>
|
||||
|
||||
@ -26,10 +38,30 @@
|
||||
import { ref, reactive, computed } from 'vue'
|
||||
import useMemberStore from '@/stores/member'
|
||||
import { t, language } from '@/locale'
|
||||
import updateVersion from '@/components/update-version/update-version.vue'
|
||||
import useSystemStore from '@/stores/system';
|
||||
|
||||
const memberStore = useMemberStore()
|
||||
|
||||
const systemStore = useSystemStore();
|
||||
const versionInfo = computed(() => {
|
||||
return systemStore.versionInfo
|
||||
})
|
||||
|
||||
const version = ref(import.meta.env.VITE_APP_VERSION)
|
||||
// #ifdef APP
|
||||
plus.runtime.getProperty(plus.runtime.appid, (inf) => {
|
||||
// 获取版本号
|
||||
version.value = inf.version
|
||||
})
|
||||
// #endif
|
||||
// #ifndef APP
|
||||
//获取当前app的版本
|
||||
const systemInfo = uni.getSystemInfoSync();
|
||||
version.value = systemInfo.appVersion;
|
||||
// #endif
|
||||
|
||||
const updateVersionRef: any = ref(null)
|
||||
|
||||
/**
|
||||
* 支持的语言列表
|
||||
@ -52,6 +84,18 @@ const lang = computed(() => {
|
||||
const switchLang = (lang) => {
|
||||
language.loadAllLocaleMessages('app', lang.value)
|
||||
}
|
||||
|
||||
const checkUpdate = ()=>{
|
||||
if(versionInfo.value && versionInfo.value.version_name){
|
||||
systemStore.showUpdateVersion()
|
||||
// uni.$emit('showUpdateVersion',{})
|
||||
}else{
|
||||
uni.showToast({
|
||||
title: '当前版本已是最新版本!',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@ -124,7 +124,7 @@ const checkIsVerifier = () => {
|
||||
}
|
||||
|
||||
const scanCode = () => {
|
||||
// #ifdef MP
|
||||
// #ifndef H5
|
||||
uni.scanCode({
|
||||
onlyFromCamera: true,
|
||||
success: res => {
|
||||
|
||||
@ -21,11 +21,11 @@
|
||||
<text class="text-[#303133] text-[26rpx] font-400 nc-iconfont nc-icon-fuzhiV6xx1 ml-[11rpx]" @click.stop="copy(item.code)"></text>
|
||||
</view>
|
||||
<view class="flex flex-1 mb-2" v-for="(dataItem,dataIndex) in item.value.list" :key="dataIndex">
|
||||
<u--image width="130rpx" height="130rpx" :radius="'var(--goods-rounded-big)'" :src="img(dataItem.cover ? dataItem.cover : '')" mode="aspectFill">
|
||||
<up-image width="130rpx" height="130rpx" :radius="'var(--goods-rounded-big)'" :src="img(dataItem.cover ? dataItem.cover : '')" mode="aspectFill">
|
||||
<template #error>
|
||||
<image class="w-[130rpx] h-[130rpx] rounded-[var(--goods-rounded-big)] overflow-hidden" :src="img('static/resource/images/diy/shop_default.jpg')" mode="aspectFill"/>
|
||||
</template>
|
||||
</u--image>
|
||||
</up-image>
|
||||
<view class="flex flex-col flex-1 ml-[20rpx] py-[4rpx]">
|
||||
<view class="max-w-[490rpx] leading-[1.3] truncate text-[28rpx] text-[#303133]">{{ dataItem.name }}</view>
|
||||
<view class="mt-[14rpx] truncate text-[24rpx] text-[var(--text-color-light9)] max-w-[490rpx] " v-if="item.sub_name">{{ item.sub_name }}</view>
|
||||
|
||||
@ -17,8 +17,8 @@
|
||||
</view>
|
||||
<view class="flex-1 pr-[10rpx]" v-else></view>
|
||||
</view>
|
||||
<scroll-view scroll-y="true" class="h-[50vh]" :scroll-top="scrollTop" scroll-with-animation @touchmove.stop>
|
||||
<view class="flex p-[30rpx] pt-[0] text-sm font-500 h-[50vh]">
|
||||
<scroll-view scroll-y="true" class="h-[700rpx] overflow-y-auto" :scroll-top="scrollTop" scroll-with-animation @touchmove.stop>
|
||||
<view class="flex p-[30rpx] pt-[0] text-sm font-500">
|
||||
<view v-if="areaList.province.length" class="flex-1 pr-[10rpx]" :style="{ opacity: currSelect == 'province' ? 1 : 0, pointerEvents: currSelect == 'province' ? 'auto' : 'none',height: currSelect == 'province' ? 'auto' : '0',overflow: currSelect == 'province' ? 'auto' : 'hidden' }">
|
||||
<view v-for="(item, index) in areaList.province" :key="item.id" class="h-[80rpx] flex items-center" :class="{'text-[var(--primary-color)]': selected.province && selected.province.id == item.id }" @click="handleProvinceClick(item)">{{ item.name }}</view>
|
||||
</view>
|
||||
@ -138,6 +138,7 @@ watch(() => selected.city, (nval) => {
|
||||
}
|
||||
}
|
||||
if (!data.length) {
|
||||
currSelect.value = 'city'
|
||||
emits('complete', selected)
|
||||
show.value = false
|
||||
}
|
||||
@ -155,40 +156,40 @@ const emits = defineEmits(['complete'])
|
||||
// 滚动到选中项位置
|
||||
const scrollToSelected = (type: string, selectedIndex: number) => {
|
||||
// 计算目标位置,让选中项显示在列表的下半部分
|
||||
const itemHeight = 80 // 每项高度 80rpx
|
||||
const itemHeight = 40 // 每项高度 80rpx
|
||||
const targetScrollTop = Math.max(0, (selectedIndex - 2) * itemHeight) // 让选中项显示在第3个位置左右
|
||||
scrollTop.value = targetScrollTop
|
||||
}
|
||||
|
||||
// 重置滚动位置
|
||||
const resetScrollTop = () => {
|
||||
scrollTop.value = 0
|
||||
scrollTop.value = 1;
|
||||
nextTick(() => {
|
||||
scrollTop.value = 0;
|
||||
});
|
||||
}
|
||||
|
||||
// 监听当前选择标签变化,重置滚动位置并滚动到选中项
|
||||
// 监听currSelect变化,触发滚动(改用nextTick确保DOM更新)
|
||||
watch(() => currSelect.value, (newVal) => {
|
||||
// 先重置滚动位置
|
||||
resetScrollTop()
|
||||
|
||||
setTimeout(() => {
|
||||
if (newVal === 'province' && selected.province) {
|
||||
const index = areaList.province.findIndex((item: any) => item.id === selected.province.id)
|
||||
if (index >= 0) {
|
||||
scrollToSelected('province', index)
|
||||
}
|
||||
} else if (newVal === 'city' && selected.city) {
|
||||
const index = areaList.city.findIndex((item: any) => item.id === selected.city.id)
|
||||
if (index >= 0) {
|
||||
scrollToSelected('city', index)
|
||||
}
|
||||
} else if (newVal === 'district' && selected.district) {
|
||||
const index = areaList.district.findIndex((item: any) => item.id === selected.district.id)
|
||||
if (index >= 0) {
|
||||
scrollToSelected('district', index)
|
||||
}
|
||||
}
|
||||
}, 150)
|
||||
})
|
||||
nextTick(() => { // 等待DOM更新后再执行滚动
|
||||
if (newVal === 'province' && selected.province) {
|
||||
const index = areaList.province.findIndex((item: any) => item.id === selected.province.id);
|
||||
if (index >= 0) {
|
||||
scrollToSelected('province', index); // 调用滚动函数
|
||||
}
|
||||
} else if (newVal === 'city' && selected.city) {
|
||||
const index = areaList.city.findIndex((item: any) => item.id === selected.city.id);
|
||||
if (index >= 0) {
|
||||
scrollToSelected('city', index); // 调用滚动函数
|
||||
}
|
||||
} else if (newVal === 'district' && selected.district) {
|
||||
const index = areaList.district.findIndex((item: any) => item.id === selected.district.id);
|
||||
if (index >= 0) {
|
||||
scrollToSelected('district', index); // 调用滚动函数
|
||||
}
|
||||
}
|
||||
});
|
||||
}, { immediate: true }); // 增加immediate,初始化时也触发
|
||||
|
||||
/**
|
||||
* 监听区县变更
|
||||
@ -204,7 +205,6 @@ watch(() => selected.district, (nval) => {
|
||||
// 处理省份点击
|
||||
const handleProvinceClick = (item: any) => {
|
||||
selected.province = item
|
||||
// 立即滚动到顶部,确保新数据可见
|
||||
nextTick(() => {
|
||||
resetScrollTop()
|
||||
})
|
||||
@ -213,7 +213,6 @@ const handleProvinceClick = (item: any) => {
|
||||
// 处理城市点击
|
||||
const handleCityClick = (item: any) => {
|
||||
selected.city = item
|
||||
// 立即滚动到顶部,确保新数据可见
|
||||
nextTick(() => {
|
||||
resetScrollTop()
|
||||
})
|
||||
|
||||
@ -3,12 +3,12 @@
|
||||
<view class="carousel-track" :style="trackStyle">
|
||||
<view
|
||||
v-for="(item, index) in displayList"
|
||||
:key="item.key"
|
||||
:key="item.key"
|
||||
class="carousel-item"
|
||||
:style="itemStyle(index)"
|
||||
:class="{ 'scale-in': item.isNew }"
|
||||
>
|
||||
<u--image width="30rpx" height="30rpx" radius="15rpx" :src="img(item.src)" mode="aspectFill">
|
||||
<up-image width="30rpx" height="30rpx" radius="15rpx" :src="img(item.src)" mode="aspectFill">
|
||||
<template #error>
|
||||
<image
|
||||
class="w-[30rpx] h-[30rpx] rounded-full"
|
||||
@ -16,7 +16,7 @@
|
||||
mode="aspectFill"
|
||||
/>
|
||||
</template>
|
||||
</u--image>
|
||||
</up-image>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@ -39,9 +39,9 @@ const EASING = 'cubic-bezier(0.25, 0.1, 0.25, 1)'; // 平滑曲线
|
||||
const slideDistance = ITEM_WIDTH - OVERLAP;
|
||||
|
||||
// 状态管理
|
||||
const displayList = ref<{
|
||||
src: string;
|
||||
isNew: boolean;
|
||||
const displayList = ref<{
|
||||
src: string;
|
||||
isNew: boolean;
|
||||
key: string; // 唯一key,避免v-for复用导致的闪烁
|
||||
}[]>([]);
|
||||
let currentIndex = 0;
|
||||
@ -82,8 +82,8 @@ const itemStyle = (index: number) => ({
|
||||
*/
|
||||
const trackStyle = computed(() => ({
|
||||
transform: `translateX(${trackOffset.value}rpx)`,
|
||||
transition: isTransitioning.value
|
||||
? `transform ${ANIMATION_DURATION}ms ${EASING}`
|
||||
transition: isTransitioning.value
|
||||
? `transform ${ANIMATION_DURATION}ms ${EASING}`
|
||||
: 'none',
|
||||
display: 'flex',
|
||||
// 硬件加速:减少重绘闪烁
|
||||
@ -179,7 +179,7 @@ onUnmounted(() => {
|
||||
position: relative;
|
||||
flex-shrink: 0;
|
||||
&:not(:first-child) {
|
||||
margin-left: -10rpx; // 固定重叠,避免计算误差
|
||||
margin-left: -15rpx; // 固定重叠,避免计算误差
|
||||
}
|
||||
transform: scale(0); // 初始隐藏
|
||||
}
|
||||
@ -188,4 +188,4 @@ onUnmounted(() => {
|
||||
.carousel-item.scale-in {
|
||||
transform: scale(0.9);
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@ -20,13 +20,13 @@
|
||||
</button>
|
||||
</u-form-item>
|
||||
<u-form-item :label=" t('nickname')" prop="nickname" :border-bottom="true">
|
||||
<input type="nickname" v-model="formData.nickname" :placeholder="t('nicknamePlaceholder')" placeholderClass="text-[28rpx]" class="text-[28rpx]" @blur="bindNickname" @click="checkAuth($event, 'nickname')"/>
|
||||
<input type="nickname" v-model="formData.nickname" :placeholder="t('nicknamePlaceholder')" placeholderClass="text-[28rpx]" placeholder-style="color: #999;" class="text-[28rpx]" @blur="bindNickname" @click="checkAuth($event, 'nickname')"/>
|
||||
</u-form-item>
|
||||
<u-form-item :label="t('mobile')" prop="mobile" :border-bottom="true" v-if="isBindMobile || config.login.is_bind_mobile">
|
||||
<input type="mobile" v-model="formData.mobile" :disabled="true" v-if="formData.mobile">
|
||||
<input type="mobile" v-model="formData.mobile" :disabled="true" v-if="formData.mobile" placeholderClass="text-[28rpx]" class="text-[28rpx]">
|
||||
<template v-else>
|
||||
<u-button v-if="info" :customStyle="{border:'none',color: 'var(--primary-color)',width:'140rpx', textAlign:'left',margin:'0rpx'}" :text="t('getMobile')" open-type="getPhoneNumber" @getphonenumber="memberStore.bindMobile"></u-button>
|
||||
<u-button v-else :customStyle="{border:'none',color: 'var(--primary-color)',width:'140rpx', textAlign:'left',margin:'0rpx'}" :text="t('getMobile')" open-type="getPhoneNumber" @getphonenumber="getPhoneNumber"></u-button>
|
||||
<u-button v-if="info" :customStyle="{border:'none',color: '#999',width:'140rpx', textAlign:'left',margin:'0rpx'}" :text="t('getMobile')" open-type="getPhoneNumber" @getphonenumber="memberStore.bindMobile" class="text-[28rpx]"></u-button>
|
||||
<u-button v-else :customStyle="{border:'none',color: '#999',width:'140rpx', textAlign:'left',margin:'0rpx'}" :text="t('getMobile')" open-type="getPhoneNumber" @getphonenumber="getPhoneNumber" class="text-[28rpx]"></u-button>
|
||||
</template>
|
||||
</u-form-item>
|
||||
</view>
|
||||
|
||||
@ -1,25 +1,29 @@
|
||||
<template>
|
||||
<view class="music-container" :class="{ playing: isPlaying }" :style="musicStyle">
|
||||
<view class="music-control" :style="defaultStyleCss" @click="togglePlay">
|
||||
<view class="music-disc " :class="{ rotate: isPlaying }">
|
||||
<image v-if="discImage" class="disc-image" :src="discImage" mode="aspectFill"></image>
|
||||
<block v-else>
|
||||
<text class="default-style iconfont iconyinfuV6mm" v-if="isPlaying"></text>
|
||||
<text class="default-style disable iconfont iconyinfuV6mm" v-else></text>
|
||||
</block>
|
||||
</view>
|
||||
<view class="music-icon">
|
||||
<text v-if="isPlaying" class="iconfont icon-pause"></text>
|
||||
<text v-else class="iconfont icon-play"></text>
|
||||
</view>
|
||||
<view class="music-container" :class="{ playing: isPlaying }" :style="musicStyle">
|
||||
<!-- 增加透明遮罩层,用于后台返回时捕获用户交互 -->
|
||||
<view class="interactive-mask" v-if="needUserInteraction" @click="handleInteraction"></view>
|
||||
|
||||
<view class="music-control" :style="defaultStyleCss" @click="togglePlay">
|
||||
<view class="music-disc " :class="{ rotate: isPlaying }">
|
||||
<image v-if="discImage" class="disc-image" :src="discImage" mode="aspectFill"></image>
|
||||
<block v-else>
|
||||
<text class="default-style iconfont iconyinfuV6mm" v-if="isPlaying"></text>
|
||||
<text class="default-style disable iconfont iconyinfuV6mm" v-else></text>
|
||||
</block>
|
||||
</view>
|
||||
<view class="music-icon">
|
||||
<text v-if="isPlaying" class="iconfont icon-pause"></text>
|
||||
<text v-else class="iconfont icon-play"></text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, watch, onMounted, onBeforeUnmount, computed } from 'vue';
|
||||
import { img } from '@/utils/common';
|
||||
// 定义组件属性
|
||||
|
||||
// 组件属性
|
||||
const props = defineProps({
|
||||
// 音乐文件地址
|
||||
src: {
|
||||
@ -54,189 +58,253 @@ const props = defineProps({
|
||||
default: 20
|
||||
}
|
||||
});
|
||||
|
||||
// 默认唱片图片
|
||||
const defaultDiscImage = img('/addon/seckill/goods/music1.png');
|
||||
const defaultDiscImage2 = img('/addon/seckill/goods/music2.png');
|
||||
|
||||
// 播放状态
|
||||
const isPlaying = ref(false);
|
||||
// 音频实例
|
||||
|
||||
// 状态管理
|
||||
const isPlaying = ref(false); // 实际播放状态
|
||||
const userWantsPlay = ref(false); // 用户期望状态
|
||||
const lastPlayPosition = ref(0); // 播放进度
|
||||
const needUserInteraction = ref(false); // 是否需要用户交互激活
|
||||
let audioContext: any = null;
|
||||
|
||||
// 创建音频实例
|
||||
const createAudioContext = () => {
|
||||
console.log('创建音频实例,音乐地址:', props.src);
|
||||
let currentPage: any = null;
|
||||
let backgroundTimer: NodeJS.Timeout | null = null;
|
||||
|
||||
// #ifdef MP-WEIXIN
|
||||
audioContext = uni.createInnerAudioContext();
|
||||
// #endif
|
||||
|
||||
// #ifdef APP-PLUS || H5
|
||||
audioContext = uni.createInnerAudioContext();
|
||||
// #endif
|
||||
|
||||
if (!audioContext) {
|
||||
console.error('无法创建音频实例');
|
||||
return;
|
||||
}
|
||||
|
||||
audioContext.src = props.src;
|
||||
audioContext.loop = props.loop;
|
||||
|
||||
// 监听播放事件
|
||||
audioContext.onPlay(() => {
|
||||
console.log('音乐开始播放');
|
||||
isPlaying.value = true;
|
||||
});
|
||||
|
||||
// 监听暂停事件
|
||||
audioContext.onPause(() => {
|
||||
console.log('音乐暂停播放');
|
||||
isPlaying.value = false;
|
||||
});
|
||||
|
||||
// 监听停止事件
|
||||
audioContext.onStop(() => {
|
||||
console.log('音乐停止播放');
|
||||
isPlaying.value = false;
|
||||
});
|
||||
|
||||
// 监听结束事件
|
||||
audioContext.onEnded(() => {
|
||||
console.log('音乐播放结束');
|
||||
isPlaying.value = false;
|
||||
});
|
||||
|
||||
// 监听错误事件
|
||||
audioContext.onError((res: any) => {
|
||||
console.error('音频播放错误:', res.errMsg || res);
|
||||
isPlaying.value = false;
|
||||
});
|
||||
|
||||
// 自动播放
|
||||
if (props.autoplay && props.src) {
|
||||
// #ifdef H5
|
||||
// H5环境下不自动播放
|
||||
console.log('H5环境,不自动播放音乐');
|
||||
isPlaying.value = false;
|
||||
// #endif
|
||||
|
||||
// #ifndef H5
|
||||
// 非H5环境正常自动播放
|
||||
console.log('准备自动播放音乐');
|
||||
// 延迟一下确保各平台兼容性
|
||||
setTimeout(() => {
|
||||
playMusic();
|
||||
}, 300);
|
||||
// #endif
|
||||
}
|
||||
};
|
||||
// 样式计算
|
||||
const defaultStyleCss = computed(() => {
|
||||
let style = '';
|
||||
style += `background:${props.defaultBgColor};`;
|
||||
if(props.defaultTextColor){ style += `color:${props.defaultTextColor};`; }
|
||||
if(props.defaultRounded){
|
||||
style += `border-border-top-left-radius:${props.defaultRounded * 2}rpx;`;
|
||||
style += `border-border-bottom-left-radius:${props.defaultRounded * 2}rpx;`;
|
||||
}
|
||||
return style;
|
||||
});
|
||||
|
||||
// 播放音乐
|
||||
const playMusic = () => {
|
||||
if (!audioContext || !props.src) {
|
||||
console.error('无法播放音乐,audioContext或src为空');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('尝试播放音乐:', props.src);
|
||||
|
||||
// 尝试播放音乐
|
||||
audioContext.play();
|
||||
|
||||
// 强制设置播放状态
|
||||
isPlaying.value = true;
|
||||
|
||||
// 微信小程序环境特殊处理
|
||||
// #ifdef MP-WEIXIN
|
||||
try {
|
||||
const systemInfo = uni.getSystemInfoSync();
|
||||
if (systemInfo.platform === 'ios') {
|
||||
// iOS端需要特殊处理微信小程序音频播放
|
||||
uni.showToast({
|
||||
title: '音乐播放中...',
|
||||
icon: 'none',
|
||||
duration: 1000
|
||||
});
|
||||
let style = '';
|
||||
style += `background:${props.defaultBgColor};`;
|
||||
if (props.defaultTextColor) style += `color:${props.defaultTextColor};`;
|
||||
if (props.defaultRounded) {
|
||||
style += `border-top-left-radius:${props.defaultRounded * 2}rpx;`;
|
||||
style += `border-bottom-left-radius:${props.defaultRounded * 2}rpx;`;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取系统信息失败:', error);
|
||||
}
|
||||
// #endif
|
||||
};
|
||||
|
||||
// 暂停音乐
|
||||
const pauseMusic = () => {
|
||||
if (!audioContext) return;
|
||||
console.log('暂停音乐');
|
||||
audioContext.pause();
|
||||
isPlaying.value = false;
|
||||
};
|
||||
|
||||
// 切换播放状态
|
||||
const togglePlay = () => {
|
||||
console.log('切换播放状态,当前状态:', isPlaying.value);
|
||||
if (isPlaying.value) {
|
||||
pauseMusic();
|
||||
} else {
|
||||
playMusic();
|
||||
}
|
||||
};
|
||||
|
||||
// 监听音乐地址变化
|
||||
watch(() => props.src, (newSrc) => {
|
||||
console.log('音乐地址变化:', newSrc);
|
||||
if (!audioContext) return;
|
||||
|
||||
const wasPlaying = isPlaying.value;
|
||||
|
||||
// 更新音乐地址
|
||||
audioContext.src = newSrc;
|
||||
|
||||
// 如果之前正在播放,则继续播放新音乐
|
||||
if (wasPlaying && newSrc) {
|
||||
playMusic();
|
||||
}
|
||||
return style;
|
||||
});
|
||||
|
||||
// 监听自动播放状态变化
|
||||
watch(() => props.autoplay, (newAutoplay) => {
|
||||
console.log('自动播放状态变化:', newAutoplay);
|
||||
if (newAutoplay && audioContext && !isPlaying.value && props.src) {
|
||||
playMusic();
|
||||
}
|
||||
});
|
||||
|
||||
// 组件挂载时创建音频实例
|
||||
onMounted(() => {
|
||||
console.log('音乐组件挂载,初始化音频');
|
||||
createAudioContext();
|
||||
});
|
||||
|
||||
// 组件卸载前释放资源
|
||||
onBeforeUnmount(() => {
|
||||
console.log('音乐组件卸载,释放资源');
|
||||
if (audioContext) {
|
||||
// 停止播放
|
||||
if (isPlaying.value) {
|
||||
audioContext.stop();
|
||||
|
||||
// 创建音频实例
|
||||
const createAudioContext = () => {
|
||||
audioContext = uni.createInnerAudioContext();
|
||||
if (!audioContext) {
|
||||
console.error('无法创建音频实例');
|
||||
return;
|
||||
}
|
||||
|
||||
// 释放资源
|
||||
audioContext.destroy();
|
||||
audioContext = null;
|
||||
}
|
||||
audioContext.src = props.src;
|
||||
audioContext.loop = props.loop;
|
||||
|
||||
// 监听播放事件
|
||||
audioContext.onPlay(() => {
|
||||
console.log('音乐开始播放');
|
||||
isPlaying.value = true;
|
||||
needUserInteraction.value = false; // 播放成功,不需要用户交互
|
||||
});
|
||||
|
||||
// 监听暂停事件
|
||||
audioContext.onPause(() => {
|
||||
console.log('音乐暂停播放');
|
||||
lastPlayPosition.value = audioContext.currentTime;
|
||||
isPlaying.value = false;
|
||||
});
|
||||
|
||||
// 监听停止事件
|
||||
audioContext.onStop(() => {
|
||||
console.log('音乐停止播放');
|
||||
lastPlayPosition.value = 0;
|
||||
isPlaying.value = false;
|
||||
});
|
||||
|
||||
// 监听结束事件
|
||||
audioContext.onEnded(() => {
|
||||
console.log('音乐播放结束');
|
||||
lastPlayPosition.value = 0;
|
||||
isPlaying.value = false;
|
||||
userWantsPlay.value = false;
|
||||
});
|
||||
|
||||
// 监听错误事件(重点处理系统拦截)
|
||||
audioContext.onError((res: any) => {
|
||||
console.error('音频播放错误:', res.errMsg || res);
|
||||
isPlaying.value = false;
|
||||
|
||||
// 错误码判断:1007表示需要用户交互(iOS常见)
|
||||
if (res.errCode === 1007 || res.errMsg?.includes('user interaction')) {
|
||||
needUserInteraction.value = true; // 标记需要用户交互
|
||||
}
|
||||
|
||||
// 错误重试
|
||||
if (userWantsPlay.value) {
|
||||
setTimeout(() => playMusic(), 1000);
|
||||
}
|
||||
});
|
||||
|
||||
// 初始化自动播放
|
||||
if (props.autoplay && props.src) {
|
||||
/* #ifdef H5 */
|
||||
console.log('H5环境,不自动播放音乐');
|
||||
isPlaying.value = false;
|
||||
userWantsPlay.value = false;
|
||||
/* #endif */
|
||||
|
||||
/* #ifndef H5 */
|
||||
console.log('准备自动播放音乐');
|
||||
userWantsPlay.value = true;
|
||||
setTimeout(() => playMusic(), 300);
|
||||
/* #endif */
|
||||
}
|
||||
};
|
||||
|
||||
// 播放音乐(增强版)
|
||||
const playMusic = () => {
|
||||
if (!audioContext || !props.src) {
|
||||
console.error('无法播放音乐,audioContext或src为空');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('尝试播放音乐,进度:', lastPlayPosition.value);
|
||||
|
||||
// 恢复进度
|
||||
if (lastPlayPosition.value > 0) {
|
||||
audioContext.seek(lastPlayPosition.value);
|
||||
}
|
||||
|
||||
// 播放核心逻辑(带权限判断)
|
||||
const playPromise = audioContext.play();
|
||||
|
||||
// 现代浏览器/小程序支持Promise返回
|
||||
if (playPromise && playPromise.catch) {
|
||||
playPromise.catch((err: any) => {
|
||||
console.warn('播放被系统拦截,需要用户交互:', err);
|
||||
needUserInteraction.value = true; // 标记需要用户点击
|
||||
});
|
||||
}
|
||||
|
||||
isPlaying.value = true;
|
||||
userWantsPlay.value = true;
|
||||
};
|
||||
|
||||
// 暂停音乐
|
||||
const pauseMusic = () => {
|
||||
if (!audioContext) return;
|
||||
audioContext.pause();
|
||||
isPlaying.value = false;
|
||||
userWantsPlay.value = false;
|
||||
needUserInteraction.value = false;
|
||||
};
|
||||
|
||||
// 切换播放状态
|
||||
const togglePlay = () => {
|
||||
if (isPlaying.value) {
|
||||
pauseMusic();
|
||||
} else {
|
||||
playMusic();
|
||||
}
|
||||
};
|
||||
|
||||
// 处理用户交互(用于唤醒被系统拦截的播放)
|
||||
const handleInteraction = () => {
|
||||
console.log('用户交互激活播放');
|
||||
needUserInteraction.value = false;
|
||||
if (userWantsPlay.value) {
|
||||
playMusic();
|
||||
}
|
||||
};
|
||||
|
||||
// 页面生命周期监听(增强版)
|
||||
const initPageLifecycle = () => {
|
||||
currentPage = getCurrentPages()[getCurrentPages().length - 1];
|
||||
const originalOnShow = currentPage.onShow;
|
||||
const originalOnHide = currentPage.onHide;
|
||||
const originalOnUnload = currentPage.onUnload;
|
||||
|
||||
// 切入后台时处理
|
||||
currentPage.onHide = function (...args: any[]) {
|
||||
originalOnHide && originalOnHide.apply(this, args);
|
||||
console.log('页面切入后台');
|
||||
|
||||
// 记录后台停留时间
|
||||
backgroundTimer = setTimeout(() => {
|
||||
// 超过3秒的后台停留,标记为需要用户交互
|
||||
if (userWantsPlay.value) {
|
||||
needUserInteraction.value = true;
|
||||
}
|
||||
}, 3000);
|
||||
};
|
||||
|
||||
// 返回前台时处理
|
||||
currentPage.onShow = function (...args: any[]) {
|
||||
originalOnShow && originalOnShow.apply(this, args);
|
||||
console.log('页面返回前台');
|
||||
|
||||
// 清除后台计时器
|
||||
if (backgroundTimer) {
|
||||
clearTimeout(backgroundTimer);
|
||||
backgroundTimer = null;
|
||||
}
|
||||
|
||||
// 尝试恢复播放
|
||||
if (userWantsPlay.value && audioContext && props.src) {
|
||||
// 先尝试自动恢复
|
||||
setTimeout(() => {
|
||||
playMusic();
|
||||
}, 300);
|
||||
}
|
||||
};
|
||||
|
||||
// 页面卸载时清理
|
||||
currentPage.onUnload = function (...args: any[]) {
|
||||
originalOnUnload && originalOnUnload.apply(this, args);
|
||||
if (backgroundTimer) {
|
||||
clearTimeout(backgroundTimer);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// 监听音乐地址变化
|
||||
watch(() => props.src, (newSrc) => {
|
||||
if (!audioContext) return;
|
||||
const wasPlaying = userWantsPlay.value;
|
||||
audioContext.src = newSrc;
|
||||
lastPlayPosition.value = 0;
|
||||
if (wasPlaying && newSrc) {
|
||||
playMusic();
|
||||
}
|
||||
});
|
||||
|
||||
// 监听自动播放状态变化
|
||||
watch(() => props.autoplay, (newAutoplay) => {
|
||||
if (newAutoplay && audioContext && !isPlaying.value && props.src) {
|
||||
userWantsPlay.value = true;
|
||||
playMusic();
|
||||
}
|
||||
});
|
||||
|
||||
// 组件挂载
|
||||
onMounted(() => {
|
||||
createAudioContext();
|
||||
initPageLifecycle();
|
||||
});
|
||||
|
||||
// 组件卸载
|
||||
onBeforeUnmount(() => {
|
||||
// 恢复页面生命周期
|
||||
if (currentPage) {
|
||||
const originalOnShow = currentPage.onShow;
|
||||
const originalOnHide = currentPage.onHide;
|
||||
const originalOnUnload = currentPage.onUnload;
|
||||
currentPage.onShow = originalOnShow;
|
||||
currentPage.onHide = originalOnHide;
|
||||
currentPage.onUnload = originalOnUnload;
|
||||
}
|
||||
|
||||
// 清理音频资源
|
||||
if (audioContext) {
|
||||
audioContext.stop();
|
||||
audioContext.destroy();
|
||||
audioContext = null;
|
||||
}
|
||||
|
||||
if (backgroundTimer) {
|
||||
clearTimeout(backgroundTimer);
|
||||
}
|
||||
});
|
||||
|
||||
const musicStyle = computed(() => {
|
||||
@ -244,112 +312,125 @@ const musicStyle = computed(() => {
|
||||
'--color': `${props.defaultTextColor}`
|
||||
}
|
||||
})
|
||||
|
||||
// 暴露方法给父组件使用
|
||||
|
||||
// 暴露方法
|
||||
defineExpose({
|
||||
play: playMusic,
|
||||
pause: pauseMusic,
|
||||
toggle: togglePlay,
|
||||
isPlaying: () => isPlaying.value
|
||||
play: playMusic,
|
||||
pause: pauseMusic,
|
||||
toggle: togglePlay,
|
||||
isPlaying: () => isPlaying.value
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.music-container {
|
||||
position: fixed;
|
||||
right: 0;
|
||||
top: calc(120rpx + var(--status-bar-height));
|
||||
z-index: 999;
|
||||
z-index: 999;
|
||||
// 交互遮罩层:覆盖整个组件,用于捕获用户点击激活播放
|
||||
.interactive-mask {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 10; // 确保在按钮上方
|
||||
background: transparent; // 透明不影响视觉
|
||||
}
|
||||
|
||||
.music-control {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
border-top-left-radius: 20rpx;
|
||||
border-bottom-left-radius: 20rpx;
|
||||
z-index: 1; // 低于遮罩层
|
||||
}
|
||||
|
||||
.music-disc {
|
||||
transition: all 0.3s;
|
||||
|
||||
&.rotate {
|
||||
animation: rotate 5s linear infinite;
|
||||
animation-play-state: running;
|
||||
}
|
||||
|
||||
.disc-image {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.music-icon {
|
||||
position: absolute;
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.iconfont {
|
||||
font-size: 40rpx;
|
||||
color: #ffffff;
|
||||
text-shadow: 0 0 5rpx rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.music-control {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
@keyframes rotate {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
/* 平台适配 */
|
||||
/* #ifdef MP-WEIXIN */
|
||||
.music-container {
|
||||
z-index: 900;
|
||||
}
|
||||
/* #endif */
|
||||
|
||||
/* #ifdef APP-PLUS */
|
||||
.music-container {
|
||||
z-index: 999;
|
||||
}
|
||||
/* #endif */
|
||||
|
||||
/* #ifdef H5 */
|
||||
.music-container {
|
||||
position: fixed;
|
||||
z-index: 999;
|
||||
}
|
||||
/* #endif */
|
||||
|
||||
.default-style{
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
border-top-left-radius: 20rpx;
|
||||
border-bottom-left-radius: 20rpx;
|
||||
}
|
||||
|
||||
.music-disc {
|
||||
transition: all 0.3s;
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
font-size: 34rpx;
|
||||
box-sizing: border-box;
|
||||
|
||||
&.rotate {
|
||||
animation: rotate 5s linear infinite;
|
||||
animation-play-state: running;
|
||||
}
|
||||
|
||||
.disc-image {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
// background-color: #ffffff;
|
||||
&.disable:after{
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
height: 50rpx;
|
||||
width: 2rpx;
|
||||
background: var(--color);
|
||||
left: 50%;
|
||||
transform: translateX(-50%) rotate(-45deg);
|
||||
}
|
||||
}
|
||||
|
||||
.music-icon {
|
||||
position: absolute;
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.iconfont {
|
||||
font-size: 40rpx;
|
||||
color: #ffffff;
|
||||
text-shadow: 0 0 5rpx rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes rotate {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
/* 适配不同平台 */
|
||||
/* #ifdef MP-WEIXIN */
|
||||
.music-container {
|
||||
z-index: 900;
|
||||
}
|
||||
/* #endif */
|
||||
|
||||
/* #ifdef APP-PLUS */
|
||||
.music-container {
|
||||
z-index: 999;
|
||||
}
|
||||
/* #endif */
|
||||
|
||||
/* #ifdef H5 */
|
||||
.music-container {
|
||||
position: fixed;
|
||||
z-index: 999;
|
||||
}
|
||||
/* #endif */
|
||||
.default-style{
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
font-size: 34rpx;
|
||||
box-sizing: border-box;
|
||||
&.disable:after{
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
height: 50rpx;
|
||||
width: 2rpx;
|
||||
background: var(--color);
|
||||
left: 50%;
|
||||
transform: translateX(-50%) rotate(-45deg);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@ -58,7 +58,7 @@ const siteInfo: any = computed(() => {
|
||||
const popupShow = ref(false);
|
||||
|
||||
const contactService = () => {
|
||||
// #ifdef H5
|
||||
// #ifndef MP-WEIXIN
|
||||
popupShow.value = true;
|
||||
// #endif
|
||||
}
|
||||
|
||||
@ -145,6 +145,18 @@ const confirmPay = () => {
|
||||
location.href = res.data.url
|
||||
}
|
||||
// #endif
|
||||
// #ifndef H5
|
||||
uni.requestPayment({
|
||||
provider: 'alipay',
|
||||
...res.data,
|
||||
success: (res: any) => {
|
||||
toPayResult()
|
||||
},
|
||||
fail: (res: any) => {
|
||||
loading.value = false
|
||||
}
|
||||
})
|
||||
// #endif
|
||||
break;
|
||||
default:
|
||||
if (res.data.url) {
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
<u-popup :show="sharePopupShow" type="bottom" @close="sharePopupClose" overlayOpacity="0.8">
|
||||
<view @touchmove.prevent.stop>
|
||||
<view class="share-content">
|
||||
<!-- #ifdef MP || APP-PLUS -->
|
||||
<!-- #ifdef MP -->
|
||||
<view class="share-box">
|
||||
<button class="share-btn" :plain="true" open-type="share">
|
||||
<view class="text-[#07c160] iconfont iconweixin11"></view>
|
||||
@ -21,6 +21,22 @@
|
||||
</button>
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
|
||||
<!-- #ifdef APP-PLUS -->
|
||||
<view class="share-box">
|
||||
<button class="share-btn" :plain="true" @click="shareSession">
|
||||
<view class="text-[#07c160] iconfont iconweixin11"></view>
|
||||
<text>分享给好友</text>
|
||||
</button>
|
||||
</view>
|
||||
|
||||
<view class="share-box">
|
||||
<button class="share-btn" :plain="true" @click="shareWechatMoments">
|
||||
<image :src="img('static/resource/images/app/wechat_moments.png')"></image>
|
||||
<text>分享到朋友圈</text>
|
||||
</button>
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
<view class="share-footer" @click="sharePopupClose">
|
||||
<text class="text-[#333]">取消分享</text>
|
||||
@ -46,6 +62,7 @@
|
||||
import { ref } from 'vue';
|
||||
import { img, copy } from '@/utils/common';
|
||||
import useSystemStore from "@/stores/system";
|
||||
import { useShare } from '@/hooks/useShare'
|
||||
|
||||
const props = defineProps({
|
||||
copyUrl: { // 例 "/wap/addon/shop_fenxiao/pages/goods"
|
||||
@ -106,6 +123,16 @@ const sharePopupClose = () => {
|
||||
emits('close');
|
||||
}
|
||||
|
||||
// #ifdef APP-PLUS
|
||||
const shareSession = () => {
|
||||
useShare().onShareAppMessage();
|
||||
}
|
||||
|
||||
const shareWechatMoments = () => {
|
||||
useShare().onShareTimeline();
|
||||
}
|
||||
// #endif
|
||||
|
||||
defineExpose({
|
||||
openShare
|
||||
})
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
<image v-if="isPosterImg" class="poster-img" :src="img(poster)" mode="aspectFit" :show-menu-by-longpress="true"/>
|
||||
</view>
|
||||
<view class="share-content">
|
||||
<!-- #ifdef MP || APP-PLUS -->
|
||||
<!-- #ifdef MP -->
|
||||
<view class="share-box">
|
||||
<button class="share-btn" :plain="true" open-type="share">
|
||||
<view class="text-[#07c160] iconfont iconweixin11"></view>
|
||||
@ -32,6 +32,29 @@
|
||||
</button>
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
|
||||
<!-- #ifdef APP-PLUS -->
|
||||
<view class="share-box">
|
||||
<button class="share-btn" :plain="true" @click="shareSession">
|
||||
<view class="text-[#07c160] iconfont iconweixin11"></view>
|
||||
<text>分享给好友</text>
|
||||
</button>
|
||||
</view>
|
||||
|
||||
<view class="share-box">
|
||||
<button class="share-btn" :plain="true" @click="shareWechatMoments">
|
||||
<image :src="img('static/resource/images/app/wechat_moments.png')"></image>
|
||||
<text>分享到朋友圈</text>
|
||||
</button>
|
||||
</view>
|
||||
|
||||
<view class="share-box">
|
||||
<button class="share-btn" :plain="true" @click="savePoster()">
|
||||
<view class="text-[#07c160] iconfont iconpengyouquan"></view>
|
||||
<text>保存海报</text>
|
||||
</button>
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
<view class="share-footer" @click="sharePopupClose">
|
||||
<text>取消分享</text>
|
||||
@ -58,6 +81,7 @@ import { ref } from 'vue';
|
||||
import { img, copy } from '@/utils/common';
|
||||
import { getPoster } from '@/app/api/system'
|
||||
import useSystemStore from "@/stores/system";
|
||||
import { useShare } from '@/hooks/useShare'
|
||||
|
||||
const props = defineProps({
|
||||
posterId: {
|
||||
@ -203,6 +227,16 @@ const savePoster = () => {
|
||||
}
|
||||
// #endif
|
||||
|
||||
// #ifdef APP-PLUS
|
||||
const shareSession = () => {
|
||||
useShare().onShareAppMessage();
|
||||
}
|
||||
|
||||
const shareWechatMoments = () => {
|
||||
useShare().onShareTimeline();
|
||||
}
|
||||
// #endif
|
||||
|
||||
const shareTop: any = ref(0)
|
||||
/************ 获取微信头部-start ****************/
|
||||
const systemStore = useSystemStore()
|
||||
@ -264,6 +298,11 @@ defineExpose({
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
|
||||
image {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
}
|
||||
|
||||
.iconfont {
|
||||
font-size: 80rpx;
|
||||
|
||||
@ -107,10 +107,11 @@ const topStatusBarData = computed(() => {
|
||||
// 导航栏内部盒子的样式
|
||||
const navbarInnerStyle = computed(() => {
|
||||
let style = '';
|
||||
if (props.isBack) {
|
||||
if (isBackShow) {
|
||||
style += 'padding-left: 30rpx;';//30=>右边留边 44=>箭头宽度 10=>箭头的右maring
|
||||
if (topStatusBarData.value.style == 'style-1') //样式一需要居中需要有右边padding辅助
|
||||
style += 'padding-right:' + (40 + 30 + 10) + 'rpx;'; //30=>左边留边 44=>箭头宽度 10=>箭头的右maring
|
||||
// style += 'padding-right:' + (40 + 30 + 10) + 'rpx;'; //30=>左边留边 44=>箭头宽度 10=>箭头的右maring
|
||||
style += 'padding-right:' + (40 + 30) + 'rpx;'; //30=>左边留边 44=>箭头宽度 10=>箭头的右maring
|
||||
} else {
|
||||
if (topStatusBarData.value.style == 'style-1') //样式一需要居中需要有右边padding辅助
|
||||
style += 'padding-right: 30rpx;'; //右边留边
|
||||
@ -122,6 +123,9 @@ const navbarInnerStyle = computed(() => {
|
||||
style += 'padding-top:' + systemStore.menuButtonInfo.top + 'px;';
|
||||
style += 'padding-bottom: 8px;';
|
||||
// #endif
|
||||
// #ifdef APP-PLUS
|
||||
style += 'padding-top:' + systemStore.systemInfo.statusBarHeight + 'px;';
|
||||
// #endif
|
||||
return style;
|
||||
})
|
||||
|
||||
@ -208,7 +212,13 @@ const goBack = () => {
|
||||
|
||||
// 微信胶囊宽度+right
|
||||
const capsuleWidth = computed(() => {
|
||||
let width = `calc(100vw - ${ systemStore.menuButtonInfo.right }px + ${ systemStore.menuButtonInfo.width }px + 10px)`;
|
||||
let width = '0px'
|
||||
// #ifdef MP
|
||||
width = `calc(100vw - ${ systemStore.menuButtonInfo.right }px + ${ systemStore.menuButtonInfo.width }px + 10px)`;
|
||||
// #endif
|
||||
// #ifdef APP-PLUS
|
||||
width = '10px'
|
||||
// #endif
|
||||
return width;
|
||||
})
|
||||
|
||||
@ -358,6 +368,9 @@ defineExpose({
|
||||
|
||||
&.style-3 {
|
||||
.content-wrap {
|
||||
// #ifdef APP-PLUS
|
||||
padding-bottom: 20rpx;
|
||||
// #endif
|
||||
.title-wrap {
|
||||
height: 46rpx;
|
||||
max-width: 300rpx;
|
||||
|
||||
281
uni-app/src/components/update-version/update-version.vue
Normal file
281
uni-app/src/components/update-version/update-version.vue
Normal file
@ -0,0 +1,281 @@
|
||||
<template>
|
||||
<view @touchmove.prevent.stop class="update-version">
|
||||
<u-popup :show="show" @close="closeFn" mode="center" :round="10" :closeOnClickOverlay="!editionForce">
|
||||
<view class="update-version-wrap flex flex-col" :style="{'background-image': 'url('+ img('static/resource/images/version_update_bg.png') +')'}">
|
||||
<view class="flex flex-col justify-center mx-[10rpx]">
|
||||
<view class="text-[50rpx] font-bold">
|
||||
发现新版本
|
||||
</view>
|
||||
<view class="text-[24rpx] mt-[12rpx] self-start rounded-bl-[14rpx] rounded-tr-[14rpx] py-[4rpx] px-[16rpx] text-[#FA0F1A] bg-[#FFD3D4]">
|
||||
{{ versionInfo.version_name }}
|
||||
</view>
|
||||
</view>
|
||||
<scroll-view scroll-y="true" class="mt-[40rpx] mb-[20rpx] mx-[10rpx] max-h-[290rpx]">
|
||||
<view class="text-[26rpx] text-[#333] text-with-newline">
|
||||
{{versionInfo.version_desc}}
|
||||
</view>
|
||||
</scroll-view>
|
||||
<view class="flex gap-[20rpx] mt-[auto]">
|
||||
<view v-if="!editionForce" class="rounded-[60rpx] h-[76rpx] w-[240rpx] flex items-center justify-center bg-[#F5F5F5] text-[28rpx] text-[#666]" @click="closeFn" :disabled="downloading">下次更新</view>
|
||||
<view class="rounded-[60rpx] h-[76rpx] w-[240rpx] flex items-center justify-center active-btn text-[#fff] text-[28rpx] text-[#999]" @click="confirm" :disabled="downloading">
|
||||
<text v-if="!downloading">下载更新</text>
|
||||
<text v-else>下载中({{downloadProgress}}%)</text>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 下载进度条 -->
|
||||
<view class="mt-[30rpx] mx-[10rpx]" v-if="downloading">
|
||||
<view class="w-full h-[10rpx] bg-[#E0E0E0] rounded-full overflow-hidden">
|
||||
<view class="h-full bg-[#F11C0C] rounded-full" :style="{ width: downloadProgress + '%' }" :class="downloading ? 'downloading-animation' : ''"></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="!editionForce" class="nc-iconfont nc-icon-cuohaoV6xx1 text-[#fff] text-center mt-[40rpx] text-[44rpx]" @click="closeFn"></view>
|
||||
</u-popup>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, version } from 'vue'
|
||||
import {onBackPress, onHide} from "@dcloudio/uni-app";
|
||||
import useSystemStore from '@/stores/system';
|
||||
import { img, getUrl } from '@/utils/common';
|
||||
|
||||
const systemStore = useSystemStore();
|
||||
|
||||
const show = ref(false)
|
||||
const downloading = ref(false)
|
||||
const downloadProgress = ref(0)
|
||||
const versionInfo = ref({})
|
||||
const editionForce = ref(0)
|
||||
|
||||
const open = () => {
|
||||
show.value = true
|
||||
}
|
||||
|
||||
const closeFn = () => {
|
||||
if (!show.value) return
|
||||
// 下载过程中不允许关闭
|
||||
if (!downloading.value) {
|
||||
const lock = uni.getStorageSync('update_version_close_lock') || {}
|
||||
lock[versionInfo.value.version_code] = true
|
||||
uni.setStorageSync('update_version_close_lock', lock)
|
||||
show.value = false
|
||||
systemStore.updateVersionPopup = false
|
||||
}else{
|
||||
uni.showToast({
|
||||
title: '下载过程中不允许关闭',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const initFn = ()=>{
|
||||
useSystemStore().getVersionInfoFn(async (res) => {
|
||||
versionInfo.value = res
|
||||
versionInfo.value.version_desc = versionInfo.value.version_desc.replace(/\\n/g, '\n')
|
||||
editionForce.value = Number(res.is_forced_upgrade)
|
||||
})
|
||||
}
|
||||
initFn()
|
||||
|
||||
|
||||
|
||||
onBackPress(()=>{
|
||||
// 强制更新不允许返回
|
||||
if (editionForce.value) {
|
||||
return true;
|
||||
}
|
||||
})
|
||||
|
||||
onHide(() => {
|
||||
editionForce.value = 0;
|
||||
show.value = false
|
||||
})
|
||||
|
||||
const confirm = () => {
|
||||
if(downloading.value) return false
|
||||
if(versionInfo.value.upgrade_type == 'hot'){
|
||||
uni.downloadFile({
|
||||
url: versionInfo.value.package_path,
|
||||
success: res => {
|
||||
if (res.statusCode === 200) {
|
||||
plus.runtime.install(
|
||||
res.tempFilePath, {
|
||||
force: true //true表示强制安装,不进行版本号的校验;false则需要版本号校验,
|
||||
},
|
||||
function() {
|
||||
uni.showModal({
|
||||
title: '更新提示',
|
||||
content: '新版本已经准备好,请重启应用',
|
||||
showCancel: false,
|
||||
success: function(res) {
|
||||
if (res.confirm) {
|
||||
// console.log('用户点击确定');
|
||||
plus.runtime.restart()
|
||||
}
|
||||
}
|
||||
});
|
||||
// console.log('install success...');
|
||||
},
|
||||
function(e) {
|
||||
console.error('install fail...');
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
}else{
|
||||
//apk整包升级 下载地址必须以.apk结尾
|
||||
if (versionInfo.value.upgrade_type == 'app' && versionInfo.value.package_path.includes('.apk')) {
|
||||
editionForce.value = 0;
|
||||
// #ifdef APP-PLUS
|
||||
plus.runtime.openURL(getUrl(versionInfo.value.package_path));
|
||||
// #endif
|
||||
show.value = false
|
||||
/* downloading.value = true
|
||||
const downloadTask = plus.downloader.createDownload(getUrl(versionInfo.value.package_path), {
|
||||
filename: '_downloads/' // 下载到应用的downloads目录
|
||||
}, function(d, status) {
|
||||
console.log('整包下载 - d',d,d.filename)
|
||||
console.log('整包下载 - status',status)
|
||||
if (status === 200) {
|
||||
// 下载成功后打开APK进行安装
|
||||
plus.runtime.openURL(d.filename, function(error) {
|
||||
console.error('打开文件失败:- error' + error);
|
||||
console.error('打开文件失败:' + error.message);
|
||||
uni.showToast({
|
||||
title: '无法打开此文件',
|
||||
icon: 'none'
|
||||
});
|
||||
})
|
||||
} else {
|
||||
console.log('APK下载失败: ' + status);
|
||||
}
|
||||
downloading.value = false
|
||||
show.value = false
|
||||
downloadProgress.value = 0
|
||||
})
|
||||
|
||||
// 监听下载进度
|
||||
downloadTask.addEventListener('statechanged', function(task, status) {
|
||||
if (task.state === 3) { // 3表示正在下载
|
||||
// 计算下载进度百分比
|
||||
const progress = (task.downloadedSize / task.totalSize) * 100
|
||||
downloadProgress.value = progress.toFixed(0)
|
||||
console.log(`下载进度: ${progress.toFixed(0)}%`);
|
||||
}
|
||||
});
|
||||
|
||||
downloadTask.start()
|
||||
*/
|
||||
} else if(versionInfo.value.upgrade_type == 'market') {
|
||||
//外部下载 一般是手机应用市场或者其他h5页面
|
||||
editionForce.value = 0; // 解决跳转外部链接后,更新提示还在的问题 */
|
||||
// #ifdef APP-PLUS
|
||||
plus.runtime.openURL(getUrl(versionInfo.value.package_path));
|
||||
// #endif
|
||||
show.value = false
|
||||
}else {
|
||||
downloading.value = true
|
||||
//wgt资源包升级 下载地址必须以.wgt结尾
|
||||
download();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const download = () =>{
|
||||
// #ifdef APP-PLUS
|
||||
const downloadTask = uni.downloadFile({
|
||||
url: versionInfo.value.package_path,
|
||||
success: res => {
|
||||
uni.showToast({
|
||||
title: `下载状态${res.statusCode}`,
|
||||
icon:'none'
|
||||
})
|
||||
if (res.statusCode === 200) {
|
||||
plus.runtime.install(
|
||||
res.tempFilePath,
|
||||
{
|
||||
force: true //true表示强制安装,不进行版本号的校验;false则需要版本号校验,
|
||||
},
|
||||
function() {
|
||||
if (versionInfo.value.upgrade_type == 'hot') {
|
||||
plus.runtime.restart();
|
||||
}
|
||||
},
|
||||
function(e) {
|
||||
//提示部分wgt包无法安装的问题
|
||||
editionForce.value = 0;
|
||||
uni.showToast({
|
||||
title:e.message,
|
||||
icon:'none',
|
||||
duration:2500
|
||||
})
|
||||
setTimeout(()=>{
|
||||
show.value = false
|
||||
},2000)
|
||||
|
||||
}
|
||||
);
|
||||
if (versionInfo.value.upgrade_type != 'hot') {
|
||||
// 解决安装app点击取消,更新还在的问题
|
||||
editionForce.value = 0;
|
||||
show.value = false
|
||||
}
|
||||
}
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('下载失败:', err)
|
||||
uni.showToast({
|
||||
title: err,
|
||||
icon: 'none'
|
||||
})
|
||||
downloading.value = false
|
||||
downloadProgress.value = 0
|
||||
}
|
||||
});
|
||||
// 进度条
|
||||
downloadTask.onProgressUpdate(res => {
|
||||
downloadProgress.value = res.progress;
|
||||
});
|
||||
// #endif
|
||||
}
|
||||
defineExpose({
|
||||
open,
|
||||
close: closeFn
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.update-version {
|
||||
:deep(.u-popup__content) {
|
||||
background-color: transparent;
|
||||
.update-version-wrap{
|
||||
background-size: contain;
|
||||
width: 564rpx;
|
||||
height: 686rpx;
|
||||
padding: 130rpx 30rpx 40rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
.active-btn{
|
||||
background: linear-gradient( 90deg, #FBA165 0%, #F11C0C 100%);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
.active-btn:active {
|
||||
opacity: 0.8;
|
||||
}
|
||||
.active-btn:disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
.downloading-animation {
|
||||
transition: width 0.3s ease;
|
||||
}
|
||||
}
|
||||
.text-with-newline {
|
||||
/* 关键样式:让\n生效 */
|
||||
white-space: pre-line;
|
||||
line-height: 1.6;
|
||||
}
|
||||
</style>
|
||||
@ -115,6 +115,11 @@ export function useDiy(params: any = {}) {
|
||||
let sources = JSON.parse(requestData.value); // todo diy的结构应该后台处理好,前端就不需要再转换了
|
||||
|
||||
diyData.global = sources.global;
|
||||
|
||||
diyData.global.id = requestData.id;
|
||||
diyData.global.site_id = requestData.site_id;
|
||||
diyData.global.type = requestData.type;
|
||||
|
||||
// 用于区分微页面之间弹窗的id
|
||||
if (diyData.global.popWindow && diyData.global.popWindow.show) {
|
||||
diyData.global.popWindow.id = requestData.id;
|
||||
@ -147,7 +152,6 @@ export function useDiy(params: any = {}) {
|
||||
uni.setNavigationBarTitle({
|
||||
title: diyData.title
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
loading.value = false;
|
||||
@ -190,9 +194,9 @@ export function useDiy(params: any = {}) {
|
||||
}
|
||||
|
||||
// 监听页面卸载
|
||||
const onUnloadLifeCycle = () => {
|
||||
const onUnloadLifeCycle = (callback: any = null) => {
|
||||
onUnload(() => {
|
||||
|
||||
if (callback) callback()
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -42,15 +42,18 @@ export function useLocation(isOpenLocation: any) {
|
||||
}
|
||||
// #endif
|
||||
|
||||
// #ifdef MP
|
||||
// #ifndef H5
|
||||
if (systemStore.mapConfig.is_open && !uni.getStorageSync('location_address')) {
|
||||
uni.getLocation({
|
||||
// #ifndef APP-PLUS
|
||||
type: 'gcj02',
|
||||
// #endif
|
||||
success: res => {
|
||||
let latlng = res.latitude + ',' + res.longitude; // 纬度(浮点数,范围为90 ~ -90),经度(浮点数,范围为180 ~ -180)
|
||||
getAddressByLatlngFn(latlng);
|
||||
},
|
||||
fail: (res) => {
|
||||
uni.showToast({ title: res.errMsg, icon: 'none', duration: 15000 })
|
||||
systemStore.defaultPositionAddress = '定位失败';
|
||||
if (res.errno) {
|
||||
if (res.errno == 104) {
|
||||
@ -145,7 +148,7 @@ export function useLocation(isOpenLocation: any) {
|
||||
let latitude = systemStore.diyAddressInfo ? systemStore.diyAddressInfo.latitude : '';
|
||||
let longitude = systemStore.diyAddressInfo ? systemStore.diyAddressInfo.longitude : '';
|
||||
|
||||
// #ifdef MP
|
||||
// #ifndef H5
|
||||
uni.chooseLocation({
|
||||
latitude,
|
||||
longitude,
|
||||
|
||||
@ -4,7 +4,9 @@ import {
|
||||
updateWeappOpenid,
|
||||
updateWechatOpenid,
|
||||
wechatUser,
|
||||
wechatUserLogin, updateWechatOpenidByH5
|
||||
wechatUserLogin,
|
||||
updateWechatOpenidByH5,
|
||||
wxappLogin
|
||||
} from '@/app/api/auth'
|
||||
import { getWechatAuthCode } from '@/app/api/system'
|
||||
import useMemberStore from '@/stores/member'
|
||||
@ -21,8 +23,7 @@ export function useLogin() {
|
||||
const config = useConfigStore()
|
||||
const systemStore = useSystemStore()
|
||||
|
||||
// #ifdef MP-WEIXIN
|
||||
|
||||
// #ifdef MP
|
||||
if (!uni.getStorageSync('autoLoginLock') && config.login.is_bind_mobile) {
|
||||
uni.setStorageSync('isBindMobile', true) // 强制绑定手机号标识
|
||||
}
|
||||
@ -36,7 +37,7 @@ export function useLogin() {
|
||||
|
||||
// 如果只开启了账号密码登录,就不需要跳转到登录中间页了,直接进入普通账号密码登录页面
|
||||
|
||||
// #ifdef MP-WEIXIN
|
||||
// #ifdef MP
|
||||
if (config.login.is_username && !config.login.is_mobile && !config.login.is_auth_register) {
|
||||
redirect({ url: '/app/pages/auth/login', param: { type: 'username' } })
|
||||
} else if (systemStore.initStatus == 'finish' && !config.login.is_username && !config.login.is_mobile && !config.login.is_auth_register) {
|
||||
@ -67,6 +68,16 @@ export function useLogin() {
|
||||
}
|
||||
}
|
||||
// #endif
|
||||
|
||||
// #ifdef APP-PLUS
|
||||
if (config.login.is_username && !config.login.is_mobile) {
|
||||
redirect({ url: '/app/pages/auth/login', param: { type: 'username' } })
|
||||
} 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' })
|
||||
}
|
||||
// #endif
|
||||
})
|
||||
}
|
||||
|
||||
@ -103,9 +114,9 @@ export function useLogin() {
|
||||
mobile: params.mobile,
|
||||
mobile_code: params.mobile_code
|
||||
};
|
||||
uni.getStorageSync('pid') && (Object.assign(obj, { pid: uni.getStorageSync('pid') }))
|
||||
|
||||
// #ifdef MP-WEIXIN
|
||||
uni.getStorageSync('pid') && (Object.assign(obj, { pid: uni.getStorageSync('pid') }))
|
||||
weappLogin(obj).then((res: any) => {
|
||||
if (res.data.token) {
|
||||
useMemberStore().setToken(res.data.token, () => {
|
||||
@ -152,7 +163,6 @@ export function useLogin() {
|
||||
// #endif
|
||||
|
||||
// #ifdef H5
|
||||
uni.getStorageSync('pid') && (Object.assign(obj, { pid: uni.getStorageSync('pid') }))
|
||||
wechatUser(obj).then((user_res: any) => {
|
||||
if (user_res.data) {
|
||||
wechatUserLogin(user_res.data).then((res: any) => {
|
||||
@ -221,7 +231,27 @@ export function useLogin() {
|
||||
}
|
||||
})
|
||||
// #endif
|
||||
|
||||
// #ifdef APP-PLUS
|
||||
wxappLogin(obj).then((res) => {
|
||||
if (res.data.token) {
|
||||
useMemberStore().setToken(res.data.token, () => {
|
||||
handleLoginBack();
|
||||
})
|
||||
} else {
|
||||
uni.setStorageSync('openid', res.data.openid)
|
||||
uni.setStorageSync('unionid', res.data.unionid)
|
||||
if(res.data.nickname) uni.setStorageSync('nickname', res.data.nickname)
|
||||
if(res.data.avatar) uni.setStorageSync('avatar', res.data.avatar)
|
||||
}
|
||||
|
||||
if (params.successCallback) params.successCallback(res.data)
|
||||
}).catch((err) => {
|
||||
uni.showToast({ title: err.msg, icon: 'none' })
|
||||
})
|
||||
// #endif
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录普通账号后修改openid
|
||||
* @param code
|
||||
@ -337,6 +367,20 @@ export function useLogin() {
|
||||
})
|
||||
|
||||
// #endif
|
||||
|
||||
// #ifdef APP-PLUS
|
||||
uni.login({
|
||||
"provider": "weixin",
|
||||
"onlyAuthorize": true, // 微信登录仅请求授权认证
|
||||
success: function(event){
|
||||
const { code } = event
|
||||
authLogin({ code })
|
||||
},
|
||||
fail: function (err) {
|
||||
uni.showToast({ title: err.msg, icon: 'none' })
|
||||
}
|
||||
})
|
||||
// #endif
|
||||
}
|
||||
|
||||
const updateWechatOpenidForH5 = (wx_openid: any) => {
|
||||
|
||||
@ -10,6 +10,8 @@ import wechat from '@/utils/wechat'
|
||||
export const useShare = () => {
|
||||
let wechatOptions: any = {};
|
||||
let weappOptions: any = {};
|
||||
|
||||
const systemStore = useSystemStore()
|
||||
|
||||
const wechatInit = () => {
|
||||
if (!isWeixinBrowser()) return;
|
||||
@ -40,53 +42,51 @@ export const useShare = () => {
|
||||
}
|
||||
|
||||
const setShare = (options: any = {}) => {
|
||||
console.log('setShare options',options)
|
||||
if (currRoute() == '' || currRoute().indexOf('app/pages/index/close') != -1 || currRoute().indexOf('app/pages/index/nosite') != -1) return;
|
||||
|
||||
let queryStr = getQuery();
|
||||
|
||||
// #ifdef H5
|
||||
|
||||
let h5Link = location.origin + location.pathname + (queryStr.length > 0 ? '?' + queryStr.join('&') : '');
|
||||
|
||||
wechatOptions = {
|
||||
link: h5Link
|
||||
}
|
||||
|
||||
// #endif
|
||||
|
||||
// #ifdef APP-PLUS
|
||||
let h5Link = systemStore.site.wap_url + currShareRoute().path + (queryStr.length > 0 ? '?' + queryStr.join('&') : '');
|
||||
wechatOptions = {
|
||||
link: h5Link
|
||||
}
|
||||
// #endif
|
||||
|
||||
// #ifdef MP-WEIXIN
|
||||
weappOptions = {
|
||||
path: '/' + currRoute() + (queryStr.length > 0 ? '?' + queryStr.join('&') : ''),
|
||||
query: queryStr.join('&'),
|
||||
}
|
||||
// #endif
|
||||
|
||||
if (options && Object.keys(options).length) {
|
||||
if (options.wechat) {
|
||||
// #ifdef H5
|
||||
wechatOptions.title = options.wechat.title || ''
|
||||
wechatOptions.link = options.wechat.link || h5Link
|
||||
wechatOptions.desc = options.wechat.desc || ''
|
||||
wechatOptions.imgUrl = options.wechat.url ? img(options.wechat.url) : ''
|
||||
// wechatOptions.success = options.wechat.callback || null;
|
||||
// useSystemStore().shareCallback = options.wechat.callback || null;
|
||||
// #ifdef H5
|
||||
wechatShare()
|
||||
// #endif
|
||||
}
|
||||
|
||||
if (options.weapp) {
|
||||
|
||||
// #ifdef MP-WEIXIN
|
||||
weappOptions.title = options.weapp.title || ''
|
||||
if (options.weapp.path) weappOptions.path = options.weapp.path
|
||||
weappOptions.imageUrl = options.weapp.url ? img(options.weapp.url) : ''
|
||||
useSystemStore().shareCallback = options.weapp.callback || null;
|
||||
// #ifdef MP-WEIXIN
|
||||
uni.setStorageSync('weappOptions', weappOptions)
|
||||
// #endif
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
getShareInfo({
|
||||
route: '/' + currRoute(),
|
||||
@ -94,7 +94,6 @@ export const useShare = () => {
|
||||
}).then((res: any) => {
|
||||
let data = res.data;
|
||||
|
||||
// #ifdef H5
|
||||
let wechat = data.wechat;
|
||||
if (wechat) {
|
||||
wechatOptions.title = wechat.title
|
||||
@ -104,50 +103,120 @@ export const useShare = () => {
|
||||
wechatOptions.title = document.title;
|
||||
wechatOptions.desc = ''
|
||||
}
|
||||
// #ifdef H5
|
||||
wechatShare()
|
||||
// #endif
|
||||
|
||||
// #ifdef MP-WEIXIN
|
||||
let weapp = data.weapp;
|
||||
if (weapp) {
|
||||
weappOptions.title = weapp.title
|
||||
weappOptions.imageUrl = weapp.url ? img(weapp.url) : ''
|
||||
}
|
||||
// #endif
|
||||
// #ifdef MP
|
||||
if(!weappOptions.title && !weappOptions.imageUrl){
|
||||
return;
|
||||
uni.setStorageSync('weappOptions', {})
|
||||
return;
|
||||
}
|
||||
uni.setStorageSync('weappOptions', weappOptions)
|
||||
// #endif
|
||||
|
||||
useSystemStore().$patch((state) => {
|
||||
state.shareOptions = {
|
||||
wechatOptions,
|
||||
weappOptions
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
useSystemStore().$patch((state) => {
|
||||
state.shareOptions = {
|
||||
wechatOptions,
|
||||
weappOptions
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
}
|
||||
|
||||
// 小程序分享,分享给好友
|
||||
const shareApp = (options = {}) => {
|
||||
// #ifdef MP
|
||||
return onShareAppMessage(() => {
|
||||
let config: any = uni.getStorageSync('weappOptions')
|
||||
if (!config) config = {}
|
||||
if (useSystemStore().shareCallback) useSystemStore().shareCallback();
|
||||
if (systemStore.shareCallback) systemStore.shareCallback();
|
||||
return {
|
||||
...config,
|
||||
...options
|
||||
}
|
||||
})
|
||||
|
||||
// #endif
|
||||
|
||||
// #ifdef APP-PLUS
|
||||
const weappOptions = systemStore.shareOptions.weappOptions
|
||||
const wechatOptions = systemStore.shareOptions.wechatOptions
|
||||
|
||||
if (!weappOptions.title && !wechatOptions.title) return
|
||||
|
||||
const shareOptions: any = {}
|
||||
if (weappOptions.title && systemStore.appConfig.weapp_original) {
|
||||
shareOptions.type = 5
|
||||
shareOptions.title = weappOptions.title
|
||||
shareOptions.imageUrl = weappOptions.imageUrl
|
||||
shareOptions.miniProgram = {
|
||||
id: systemStore.appConfig.weapp_original,
|
||||
path: weappOptions.path,
|
||||
type: 0,
|
||||
webUrl: wechatOptions.link
|
||||
}
|
||||
} else {
|
||||
shareOptions.type = 0
|
||||
shareOptions.href = wechatOptions.link
|
||||
shareOptions.title = wechatOptions.title
|
||||
shareOptions.summary = wechatOptions.desc
|
||||
shareOptions.imageUrl = wechatOptions.imgUrl
|
||||
}
|
||||
|
||||
uni.share({
|
||||
provider: "weixin",
|
||||
scene: "WXSceneSession",
|
||||
success: () => {
|
||||
if (systemStore.shareCallback) systemStore.shareCallback();
|
||||
},
|
||||
...shareOptions
|
||||
});
|
||||
// #endif
|
||||
}
|
||||
|
||||
// 小程序分享,分享到朋友圈
|
||||
const shareTime = (options = {}) => {
|
||||
// #ifdef MP
|
||||
return onShareTimeline(() => {
|
||||
let config: any = uni.getStorageSync('weappOptions')
|
||||
if (!config) config = {}
|
||||
if (useSystemStore().shareCallback) useSystemStore().shareCallback();
|
||||
if (systemStore.shareCallback) systemStore.shareCallback();
|
||||
return {
|
||||
...config,
|
||||
...options
|
||||
}
|
||||
})
|
||||
// #endif
|
||||
|
||||
// #ifdef APP-PLUS
|
||||
const wechatOptions = systemStore.shareOptions.wechatOptions
|
||||
if (wechatOptions.title) {
|
||||
uni.share({
|
||||
provider: "weixin",
|
||||
scene: "WXSceneTimeline",
|
||||
type: 0,
|
||||
href: wechatOptions.link,
|
||||
title: wechatOptions.title,
|
||||
summary: wechatOptions.desc,
|
||||
imageUrl: wechatOptions.imgUrl,
|
||||
success: () => {
|
||||
if (systemStore.shareCallback) systemStore.shareCallback();
|
||||
}
|
||||
});
|
||||
}
|
||||
// #endif
|
||||
}
|
||||
|
||||
// 禁用当前页面的分享功能(同时支持小程序和公众号)
|
||||
|
||||
25
uni-app/src/layouts/default.vue
Normal file
25
uni-app/src/layouts/default.vue
Normal file
@ -0,0 +1,25 @@
|
||||
<template>
|
||||
<slot />
|
||||
<!-- #ifdef APP-PLUS -->
|
||||
<update-version ref="updateVersionRef"/>
|
||||
<!-- #endif -->
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, watch } from 'vue'
|
||||
import useSystemStore from '@/stores/system'
|
||||
import updateVersion from '@/components/update-version/update-version.vue'
|
||||
|
||||
const systemStore = useSystemStore()
|
||||
|
||||
// #ifdef APP-PLUS
|
||||
const updateVersionRef = ref(null)
|
||||
watch(() => systemStore.updateVersionPopup, () => {
|
||||
if (systemStore.updateVersionPopup) updateVersionRef.value?.open()
|
||||
else updateVersionRef.value?.close()
|
||||
})
|
||||
// #endif
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
</style>
|
||||
@ -10,6 +10,7 @@ const t = (message: string) => {
|
||||
// #ifdef MP
|
||||
route = '/' + (getCurrentInstance()?.root.ctx.$scope.__route__ || useSystemStore().currRoute)
|
||||
// #endif
|
||||
|
||||
const file = language.getFileKey(route)
|
||||
const key = `${ file.fileKey }.${ message }`
|
||||
if (i18n.global.t(message) != message) return i18n.global.t(message)
|
||||
|
||||
@ -14,7 +14,6 @@ class Language {
|
||||
/**
|
||||
*
|
||||
* @param locale 设置语言
|
||||
* @param path
|
||||
*/
|
||||
public setI18nLanguage(locale: string, path: string = '') {
|
||||
if (this.i18n.global.locale == locale) return
|
||||
@ -45,43 +44,54 @@ class Language {
|
||||
this.setI18nLanguage(locale, file)
|
||||
return nextTick()
|
||||
}
|
||||
this.loadLocale.push(`${fileKey}.${locale}`)
|
||||
|
||||
// 引入语言包文件
|
||||
|
||||
// #ifdef APP-PLUS
|
||||
const appLang = import.meta.glob('../app/locale/**/*.json', { eager: true })
|
||||
const addonLang = import.meta.glob('../addon/**/locale/**/*.json', { eager: true })
|
||||
const messages = route == 'app' ? appLang[`../app/locale/${locale}/${file}.json`] : addonLang[`../addon/${route}/locale/${locale}/${file}.json`]
|
||||
// #endif
|
||||
|
||||
// #ifndef APP-PLUS
|
||||
const messages = await import(route == 'app' ? `../${route}/locale/${locale}/${file}.json` : `../addon/${route}/locale/${locale}/${file}.json`)
|
||||
// #endif
|
||||
|
||||
let data: Record<string, string> = {}
|
||||
Object.keys(messages.default).forEach(key => {
|
||||
data[`${fileKey}.${key}`] = messages.default[key]
|
||||
})
|
||||
|
||||
this.i18n.global.mergeLocaleMessage(locale, data)
|
||||
this.setI18nLanguage(locale, file)
|
||||
|
||||
this.loadLocale.push(`${fileKey}.${locale}`)
|
||||
|
||||
return nextTick()
|
||||
} catch (e) {
|
||||
// console.log(e)
|
||||
this.setI18nLanguage(locale)
|
||||
return nextTick()
|
||||
}
|
||||
}
|
||||
|
||||
public getFileKey = (path: string) => {
|
||||
const pathArr = path.split('/')
|
||||
let route = pathArr[1] == 'app' ? pathArr[1] : pathArr[2];
|
||||
try {
|
||||
const pathArr = path.split('/')
|
||||
let route = pathArr[1] == 'app' ? pathArr[1] : pathArr[2];
|
||||
|
||||
let file = path == '/' ? 'pages.index.index' : path.replace('/', '').replaceAll('/', '.')
|
||||
let file = path == '/' ? 'pages.index.index' : path.replace('/', '').replace(/\//g, ".")
|
||||
|
||||
// 如果是系统页面,则移除“app.”
|
||||
let fileKey = ''
|
||||
if (route == 'app') {
|
||||
fileKey = file.replace('app.', '')
|
||||
file = file.replace('app.', '')
|
||||
} else {
|
||||
fileKey = file.replace(`addon.`, '')
|
||||
file = file.replace(`addon.${route}.`, '')
|
||||
// 如果是系统页面,则移除“app.”
|
||||
let fileKey = ''
|
||||
if (route == 'app') {
|
||||
fileKey = file.replace('app.', '')
|
||||
file = file.replace('app.', '')
|
||||
} else {
|
||||
fileKey = file.replace(`addon.`, '')
|
||||
file = file.replace(`addon.${route}.`, '')
|
||||
}
|
||||
return { file, fileKey, route }
|
||||
} catch (e) {
|
||||
return { file: 'pages.index.index', fileKey: 'pages.index.index', route: 'app' }
|
||||
}
|
||||
return { file, fileKey, route }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { getInitInfo, getSiteInfo, getMemberMobileExist } from '@/app/api/system'
|
||||
import { getInitInfo, getSiteInfo, getMemberMobileExist, getNewVersion } from '@/app/api/system'
|
||||
import useConfigStore from '@/stores/config'
|
||||
import useMemberStore from '@/stores/member'
|
||||
import { isWeixinBrowser } from '@/utils/common'
|
||||
@ -24,7 +24,17 @@ interface System {
|
||||
topTabbarInfo: {
|
||||
height: number
|
||||
fullHeight: string
|
||||
}
|
||||
},
|
||||
appConfig: {
|
||||
wechat_app_id: string
|
||||
weapp_original: string
|
||||
},
|
||||
shareOptions: {
|
||||
wechatOptions: any
|
||||
weappOptions: any
|
||||
},
|
||||
versionInfo: any,
|
||||
updateVersionPopup: boolean
|
||||
}
|
||||
|
||||
const useSystemStore = defineStore('system', {
|
||||
@ -56,7 +66,17 @@ const useSystemStore = defineStore('system', {
|
||||
topTabbarInfo: {
|
||||
height: 0,
|
||||
fullHeight: ''
|
||||
}
|
||||
},
|
||||
appConfig: {
|
||||
wechat_app_id: '',
|
||||
weapp_original: ''
|
||||
},
|
||||
shareOptions: {
|
||||
wechatOptions: {},
|
||||
weappOptions: {}
|
||||
},
|
||||
versionInfo: null,
|
||||
updateVersionPopup: false
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
@ -115,6 +135,7 @@ const useSystemStore = defineStore('system', {
|
||||
// 如果会员已存在则小程序端快捷登录时不再弹出授权弹框
|
||||
uni.setStorageSync('member_exist', data.member_exist)
|
||||
|
||||
data.app_config && (this.appConfig = data.app_config)
|
||||
|
||||
this.initStatus = 'finish'; // 初始化完成
|
||||
|
||||
@ -127,6 +148,24 @@ const useSystemStore = defineStore('system', {
|
||||
|
||||
this.getMenuButtonInfoFn();
|
||||
},
|
||||
// 获取版本数据信息
|
||||
getVersionInfoFn(callback: any = null) {
|
||||
// #ifdef APP-PLUS
|
||||
// 获取系统信息
|
||||
const systemInfo = uni.getSystemInfoSync();
|
||||
const platform = systemInfo.osName;
|
||||
const version_code = systemInfo.appVersionCode
|
||||
getNewVersion({
|
||||
version_code,
|
||||
platform
|
||||
}).then((res: any) => {
|
||||
if (res.data) {
|
||||
this.versionInfo = res.data
|
||||
if (callback) callback(res.data)
|
||||
}
|
||||
})
|
||||
// #endif
|
||||
},
|
||||
// 如果已有手机号,就不弹出绑定手机号弹框
|
||||
getMemberMobileExistFn() {
|
||||
getMemberMobileExist({
|
||||
@ -175,6 +214,9 @@ const useSystemStore = defineStore('system', {
|
||||
this.topTabbarInfo = Object.assign({}, this.topTabbarInfo, data);
|
||||
this.topTabbarInfo.height = this.topTabbarInfo.height || 0
|
||||
this.topTabbarInfo.fullHeight = this.topTabbarInfo.height ? this.topTabbarInfo.height+'px' : 0
|
||||
},
|
||||
showUpdateVersion() {
|
||||
this.updateVersionPopup = true
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
@font-face {
|
||||
font-family: "iconfont"; /* Project id 3952239 */
|
||||
src: url('//at.alicdn.com/t/c/font_3952239_5hb6cojxgdi.woff2?t=1755829954368') format('woff2'),
|
||||
url('//at.alicdn.com/t/c/font_3952239_5hb6cojxgdi.woff?t=1755829954368') format('woff'),
|
||||
url('//at.alicdn.com/t/c/font_3952239_5hb6cojxgdi.ttf?t=1755829954368') format('truetype');
|
||||
src: url('https://at.alicdn.com/t/c/font_3952239_yxckpayhvp.woff2?t=1755857288182') format('woff2'),
|
||||
url('https://at.alicdn.com/t/c/font_3952239_yxckpayhvp.woff?t=1755857288182') format('woff'),
|
||||
url('https://at.alicdn.com/t/c/font_3952239_yxckpayhvp.ttf?t=1755857288182') format('truetype');
|
||||
}
|
||||
|
||||
.iconfont {
|
||||
@ -13,6 +13,10 @@
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.iconjiahaoV6xx1:before {
|
||||
content: "\e7cc";
|
||||
}
|
||||
|
||||
.icona-xiangxiaV6xx1:before {
|
||||
content: "\e79e";
|
||||
}
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
@font-face {
|
||||
font-family: "nc-iconfont"; /* Project id 4567203 */
|
||||
src: url('//at.alicdn.com/t/c/font_4567203_r3nkqy7l1w.woff2?t=1750327830437') format('woff2'),
|
||||
url('//at.alicdn.com/t/c/font_4567203_r3nkqy7l1w.woff?t=1750327830437') format('woff'),
|
||||
url('//at.alicdn.com/t/c/font_4567203_r3nkqy7l1w.ttf?t=1750327830437') format('truetype');
|
||||
src: url('https://at.alicdn.com/t/c/font_4567203_r3nkqy7l1w.woff2?t=1750327830437') format('woff2'),
|
||||
url('https://at.alicdn.com/t/c/font_4567203_r3nkqy7l1w.woff?t=1750327830437') format('woff'),
|
||||
url('https://at.alicdn.com/t/c/font_4567203_r3nkqy7l1w.ttf?t=1750327830437') format('truetype');
|
||||
}
|
||||
|
||||
.nc-iconfont {
|
||||
|
||||
97
uni-app/src/uni_modules/rt-uni-update/changelog.md
Normal file
97
uni-app/src/uni_modules/rt-uni-update/changelog.md
Normal file
@ -0,0 +1,97 @@
|
||||
## 1.5.2(2023-09-08)
|
||||
优化文档
|
||||
## 1.5.1(2023-05-26)
|
||||
优化文档
|
||||
## 1.5.0(2023-05-23)
|
||||
优化文档
|
||||
## 1.4.9(2023-05-23)
|
||||
文档新增后台版本管理示例图
|
||||
## 1.4.8(2023-05-23)
|
||||
优化当前版本显示
|
||||
## 1.4.7(2023-05-23)
|
||||
新增当前运行版本名称和新版本名称显示
|
||||
## 1.4.6(2023-05-22)
|
||||
新增显示安装包大小
|
||||
## 1.4.5(2023-04-27)
|
||||
优化页面不透明
|
||||
## 1.4.4(2023-04-25)
|
||||
新增pages_init.json自动注册页面
|
||||
## 1.4.3(2023-04-25)
|
||||
修改app下载链接
|
||||
## 1.4.2(2023-04-25)
|
||||
优化
|
||||
## 1.4.1(2023-04-15)
|
||||
优化bug
|
||||
## 1.4.0(2023-04-14)
|
||||
删除无用代码
|
||||
## 1.3.9(2023-04-14)
|
||||
优化
|
||||
## 1.3.8(2023-04-03)
|
||||
优化文档
|
||||
## 1.3.7(2023-03-23)
|
||||
优化文档
|
||||
## 1.3.6(2023-03-23)
|
||||
优化文档
|
||||
## 1.3.5(2023-03-08)
|
||||
新增常见问题
|
||||
## 1.3.4(2023-03-07)
|
||||
解决应用切换到后台再次打开更新弹窗叠加多个的问题
|
||||
## 1.3.3(2023-03-02)
|
||||
优化提示文档
|
||||
## 1.3.2(2023-02-02)
|
||||
优化部分wgt包无法安装的提示
|
||||
## 1.3.1(2023-01-12)
|
||||
修改示例下载文件地址
|
||||
## 1.3.0(2022-11-17)
|
||||
兼容低版本安卓手机,用户拒绝安装后,去掉自动重启,优化体验
|
||||
## 1.2.9(2022-11-14)
|
||||
优化插件
|
||||
## 1.2.8(2022-11-14)
|
||||
优化整包更新用户体验
|
||||
## 1.2.7(2022-11-14)
|
||||
修复apk整包更新时,点击拒绝安装,更新进度还在的bug
|
||||
## 1.2.6(2022-10-17)
|
||||
优化问题汇总
|
||||
## 1.2.5(2022-10-17)
|
||||
常见问题优化
|
||||
## 1.2.4(2022-09-21)
|
||||
文档新增常见问题汇总,方便更快的解决问题
|
||||
## 1.2.3(2022-09-21)
|
||||
文档新增常见问题汇总,方便更快的解决问题
|
||||
## 1.2.2(2022-09-21)
|
||||
文档新增常见问题汇总,方便更快的解决问题
|
||||
## 1.2.1(2022-09-21)
|
||||
文档新增常见问题汇总,方便更快的解决问题
|
||||
## 1.2.0(2022-08-03)
|
||||
优化插件,wgt升级重启,整包升级不重启
|
||||
## 1.1.9(2022-08-01)
|
||||
新增弹出一个合并页面路由的pages.json修改界面。插件使用者点击确认按钮即可完成插件页面向项目pages.json的注册。HBuilderX 3.5.0+支持
|
||||
## 1.1.8(2022-07-25)
|
||||
1、静默更新后提示用户重启应用,以解决样式错乱的问题
|
||||
2、跳转应用市场下载后,解决更新提示弹窗一直叠加的问题
|
||||
## 1.1.7(2022-07-22)
|
||||
优化示例代码
|
||||
## 1.1.6(2022-07-22)
|
||||
优化文档
|
||||
## 1.1.5(2022-07-19)
|
||||
优化文档
|
||||
## 1.1.4(2022-07-19)
|
||||
优化文档
|
||||
## 1.1.3(2022-07-19)
|
||||
优化文档
|
||||
## 1.1.2(2022-07-18)
|
||||
优化wgt更新文档
|
||||
## 1.1.1(2022-07-17)
|
||||
新增wgt包静默更新
|
||||
## 1.1.0(2022-05-17)
|
||||
优化readme文档
|
||||
## 1.0.9(2022-05-14)
|
||||
优化
|
||||
## 1.0.8(2022-05-05)
|
||||
修复图片不显示的bug
|
||||
## 1.0.7(2022-01-19)
|
||||
1.0.7 优化readme文档
|
||||
## 1.0.6(2022-01-19)
|
||||
正式支持uni_modules
|
||||
## 1.0.5(2022-01-19)
|
||||
测试支持uni_models
|
||||
@ -0,0 +1,303 @@
|
||||
<template>
|
||||
<view class="update-mask flex-center">
|
||||
<view class="content botton-radius">
|
||||
<view class="content-top">
|
||||
<view class="content-top-text">
|
||||
<text class="">发现新版本 v{{data.edition_name}}</text>
|
||||
<text class="version">当前版本:{{version}}</text>
|
||||
</view>
|
||||
<image class="content-top" style="top: 0;" width="100%" height="100%" src="/uni_modules/rt-uni-update/static/bg_top.png"></image>
|
||||
</view>
|
||||
<view class="content-header"></view>
|
||||
<view class="content-body">
|
||||
|
||||
<view class="title"><text>更新内容</text></view>
|
||||
<view class="body">
|
||||
<scroll-view class="box-des-scroll" scroll-y="true"><rich-text :nodes="data.describe"></rich-text></scroll-view>
|
||||
</view>
|
||||
<view class="footer flex-center">
|
||||
<view class="progress-box flex-column" v-if="!updateBtn">
|
||||
<progress class="progress" border-radius="35" :percent="percent" activeColor="#3DA7FF" show-info stroke-width="10" />
|
||||
<!-- <u-line-progress :striped="true" :percent="percent" :striped-active="true"></u-line-progress> -->
|
||||
<view><text class="fs24">正在下载,请稍后 ({{downloadedSize}}/{{packageFileSize}}M)</text></view>
|
||||
</view>
|
||||
|
||||
<button class="content-button" style="border: none;color: #fff;" plain @click="confirm" v-if="updateBtn">立即升级</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<image v-if="cancleBtn" class="close-img" src="/uni_modules/rt-uni-update/static/app_update_close.png" @click.stop="cancel"></image>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
version:'1.0.0',//当前运行版本(打包时manifest里的版本名称)
|
||||
percent: 0, //进度条百分比
|
||||
updateBtn: true, //是否显示立即更新
|
||||
cancleBtn: false, //是否显示取消按钮
|
||||
downloadedSize:0,//当前已下载大小
|
||||
packageFileSize:0,//安装包大小
|
||||
data: {
|
||||
describe: '1. 修复已知问题<br>2. 优化用户体验',
|
||||
edition_url: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-6bef1fe3-e3e3-4909-9f0c-6ed9bd11c93b/aae2360a-6628-4c93-b873-ce1600b9a852.apk', //安装包下载地址或者通用应用市场地址
|
||||
edition_force: 1, //是否强制更新 0代表否 1代表是
|
||||
package_type: 0 ,//0是整包升级 1是wgt升级
|
||||
edition_name:'1.0.1' //后端返回的版本名称
|
||||
}
|
||||
};
|
||||
},
|
||||
onHide() { //解决应用切换到后台再次打开更新弹窗叠加多个的问题
|
||||
// console.log('切换到后台');
|
||||
this.data.edition_force = 0;
|
||||
uni.navigateBack({
|
||||
delta: 1
|
||||
})
|
||||
},
|
||||
onLoad({obj}) {
|
||||
this.data = JSON.parse(obj);
|
||||
if (this.data.edition_force == 0) {
|
||||
this.cancleBtn = true;
|
||||
}
|
||||
plus.runtime.getProperty(plus.runtime.appid,(inf) => {
|
||||
this.version = inf.version;
|
||||
})
|
||||
},
|
||||
|
||||
onBackPress() {
|
||||
// 强制更新不允许返回
|
||||
if (this.data.edition_force == 1) {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
cancel() {
|
||||
//取消升级 返回上一页
|
||||
uni.navigateBack({
|
||||
delta: 1
|
||||
});
|
||||
},
|
||||
confirm() {
|
||||
if (this.data.package_type == 0) {
|
||||
//apk整包升级 下载地址必须以.apk结尾
|
||||
if (this.data.edition_url.includes('.apk')) {
|
||||
this.updateBtn = false;
|
||||
this.cancleBtn = false;
|
||||
this.download();
|
||||
|
||||
} else {
|
||||
//外部下载 一般是手机应用市场或者其他h5页面
|
||||
this.data.edition_force = 0; // 解决跳转外部链接后,更新提示还在的问题
|
||||
plus.runtime.openURL(this.data.edition_url);
|
||||
uni.navigateBack({
|
||||
delta: 1
|
||||
});
|
||||
}
|
||||
} else {
|
||||
this.updateBtn = false;
|
||||
this.cancleBtn = false;
|
||||
//wgt资源包升级 下载地址必须以.wgt结尾
|
||||
this.download();
|
||||
}
|
||||
},
|
||||
download() {
|
||||
let package_type = this.data.package_type;
|
||||
let that = this;
|
||||
const downloadTask = uni.downloadFile({
|
||||
url: this.data.edition_url,
|
||||
success: res => {
|
||||
if (res.statusCode === 200) {
|
||||
plus.runtime.install(
|
||||
res.tempFilePath,
|
||||
{
|
||||
force: true //true表示强制安装,不进行版本号的校验;false则需要版本号校验,
|
||||
},
|
||||
function() {
|
||||
// console.log('success', success);
|
||||
if (package_type == 1) {
|
||||
plus.runtime.restart();
|
||||
}
|
||||
},
|
||||
function(e) {
|
||||
//提示部分wgt包无法安装的问题
|
||||
that.data.edition_force = 0;
|
||||
uni.showToast({
|
||||
title:e.message,
|
||||
icon:'none',
|
||||
duration:2500
|
||||
})
|
||||
setTimeout(()=>{
|
||||
uni.navigateBack()
|
||||
},2000)
|
||||
|
||||
}
|
||||
);
|
||||
if (package_type == 0) {
|
||||
// 解决安装app点击取消,更新还在的问题
|
||||
this.data.edition_force = 0;
|
||||
uni.navigateBack();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
// 进度条
|
||||
downloadTask.onProgressUpdate(res => {
|
||||
this.percent = res.progress;
|
||||
this.downloadedSize = (res.totalBytesWritten / Math.pow(1024, 2)).toFixed(2);
|
||||
this.packageFileSize = (res.totalBytesExpectedToWrite / Math.pow(1024, 2)).toFixed(2);
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
page {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.flex-center {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.update-mask {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: rgba(0, 0, 0, 0.65);
|
||||
}
|
||||
|
||||
.botton-radius {
|
||||
border-bottom-left-radius: 30rpx;
|
||||
border-bottom-right-radius: 30rpx;
|
||||
}
|
||||
|
||||
.content {
|
||||
position: relative;
|
||||
top: 0;
|
||||
width: 600rpx;
|
||||
background-color: #fff;
|
||||
box-sizing: border-box;
|
||||
padding: 0 50rpx;
|
||||
font-family: Source Han Sans CN;
|
||||
}
|
||||
|
||||
.text {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: block;
|
||||
/* #endif */
|
||||
line-height: 200px;
|
||||
text-align: center;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.content-top {
|
||||
position: absolute;
|
||||
top: -195rpx;
|
||||
left: 0;
|
||||
width: 600rpx;
|
||||
height: 270rpx;
|
||||
}
|
||||
|
||||
.content-top-text {
|
||||
font-size: 40rpx;
|
||||
font-weight: bold;
|
||||
color: #f8f8fa;
|
||||
position: absolute;
|
||||
top: 120rpx;
|
||||
left: 50rpx;
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.content-header {
|
||||
height: 70rpx;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 33rpx;
|
||||
font-weight: bold;
|
||||
color: #3da7ff;
|
||||
line-height: 38px;
|
||||
}
|
||||
|
||||
.footer {
|
||||
height: 150rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
.box-des-scroll {
|
||||
box-sizing: border-box;
|
||||
padding: 0 40rpx;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.box-des {
|
||||
font-size: 26rpx;
|
||||
color: #000000;
|
||||
line-height: 50rpx;
|
||||
}
|
||||
|
||||
.progress-box {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.progress {
|
||||
width: 83%;
|
||||
height: 40rpx;
|
||||
border-radius: 35px;
|
||||
}
|
||||
|
||||
.close-img {
|
||||
width: 70rpx;
|
||||
height: 70rpx;
|
||||
z-index: 1000;
|
||||
position: absolute;
|
||||
bottom: -120rpx;
|
||||
left: calc(50% - 70rpx / 2);
|
||||
}
|
||||
|
||||
.content-button {
|
||||
text-align: center;
|
||||
flex: 1;
|
||||
font-size: 30rpx;
|
||||
font-weight: 400;
|
||||
color: #ffffff;
|
||||
border-radius: 40rpx;
|
||||
margin: 0 18rpx;
|
||||
|
||||
height: 80rpx;
|
||||
line-height: 80rpx;
|
||||
|
||||
background: linear-gradient(to right, #1785ff, #3da7ff);
|
||||
}
|
||||
|
||||
.flex-column {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
.fs24{
|
||||
font-size: 24rpx;
|
||||
}
|
||||
.version{
|
||||
font-size: 24rpx;
|
||||
margin-top: 10rpx;
|
||||
color: #eeeeee;
|
||||
text-decoration: underline;
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,31 @@
|
||||
export default function silenceUpdate(url) {
|
||||
uni.downloadFile({
|
||||
url,
|
||||
success: res => {
|
||||
if (res.statusCode === 200) {
|
||||
plus.runtime.install(
|
||||
res.tempFilePath, {
|
||||
force: true //true表示强制安装,不进行版本号的校验;false则需要版本号校验,
|
||||
},
|
||||
function() {
|
||||
uni.showModal({
|
||||
title: '更新提示',
|
||||
content: '新版本已经准备好,请重启应用',
|
||||
showCancel: false,
|
||||
success: function(res) {
|
||||
if (res.confirm) {
|
||||
// console.log('用户点击确定');
|
||||
plus.runtime.restart()
|
||||
}
|
||||
}
|
||||
});
|
||||
// console.log('install success...');
|
||||
},
|
||||
function(e) {
|
||||
console.error('install fail...');
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
80
uni-app/src/uni_modules/rt-uni-update/package.json
Normal file
80
uni-app/src/uni_modules/rt-uni-update/package.json
Normal file
@ -0,0 +1,80 @@
|
||||
{
|
||||
"id": "rt-uni-update",
|
||||
"displayName": "app升级整包更新和热更新支持vue3 支持打开安卓、苹果市场,wgt静默更新",
|
||||
"version": "1.5.2",
|
||||
"description": "app升级、整包更新和热更新组件 支持vue3 支持打开安卓、苹果应用市场,支持wgt静默更新,无感知,支持覆盖原生tabar,原生导航栏",
|
||||
"keywords": [
|
||||
"整包更新",
|
||||
"热更新",
|
||||
"vue3",
|
||||
"静默更新",
|
||||
"app更新升级"
|
||||
],
|
||||
"repository": "",
|
||||
"engines": {
|
||||
},
|
||||
"dcloudext": {
|
||||
"sale": {
|
||||
"regular": {
|
||||
"price": "0.00"
|
||||
},
|
||||
"sourcecode": {
|
||||
"price": "0.00"
|
||||
}
|
||||
},
|
||||
"contact": {
|
||||
"qq": ""
|
||||
},
|
||||
"declaration": {
|
||||
"ads": "无",
|
||||
"data": "无",
|
||||
"permissions": "无"
|
||||
},
|
||||
"npmurl": "",
|
||||
"type": "component-vue"
|
||||
},
|
||||
"uni_modules": {
|
||||
"dependencies": [],
|
||||
"encrypt": [],
|
||||
"platforms": {
|
||||
"cloud": {
|
||||
"tcb": "y",
|
||||
"aliyun": "y"
|
||||
},
|
||||
"client": {
|
||||
"Vue": {
|
||||
"vue2": "y",
|
||||
"vue3": "y"
|
||||
},
|
||||
"App": {
|
||||
"app-vue": "y",
|
||||
"app-nvue": "y"
|
||||
},
|
||||
"H5-mobile": {
|
||||
"Safari": "u",
|
||||
"Android Browser": "u",
|
||||
"微信浏览器(Android)": "u",
|
||||
"QQ浏览器(Android)": "u"
|
||||
},
|
||||
"H5-pc": {
|
||||
"Chrome": "u",
|
||||
"IE": "u",
|
||||
"Edge": "u",
|
||||
"Firefox": "u",
|
||||
"Safari": "u"
|
||||
},
|
||||
"小程序": {
|
||||
"微信": "u",
|
||||
"阿里": "u",
|
||||
"百度": "u",
|
||||
"字节跳动": "u",
|
||||
"QQ": "u"
|
||||
},
|
||||
"快应用": {
|
||||
"华为": "u",
|
||||
"联盟": "u"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
192
uni-app/src/uni_modules/rt-uni-update/readme.md
Normal file
192
uni-app/src/uni_modules/rt-uni-update/readme.md
Normal file
@ -0,0 +1,192 @@
|
||||
## 整包更新和热更新组件 支持vue3 支持打开安卓、苹果应用市场,支持wgt静默更新
|
||||
|
||||
- ui图是采用uniapp官方更新组件的ui,如不满足需要,可自行替换
|
||||
- 一键式检查更新,同时支持整包升级与wgt资源包更新 支持打开安卓自带的应用市场和苹果appstore
|
||||
- 好看、实用、可自定义的客户端提示框
|
||||
- 支持强制更新,无法退出
|
||||
- 支持静默更新,下次启动后更新的内容自动生效
|
||||
- 支持覆盖原生tabar,原生导航栏
|
||||
|
||||
## 安装指引
|
||||
|
||||
1. 在插件市场打开本插件页面,在右侧点击`使用 HBuilderX 导入插件`,选择要导入的项目点击确定(建议使用uni_modules版本 非uni_modules版本不在维护,有需要自行修改)
|
||||
|
||||
2. 在`pages.json`中添加页面路径。注意:一定不要设置为pages.json中第一项(在1.1.9版本新增弹出一个合并页面路由的pages.json修改界面。点击确认按钮即可完成插件页面向项目pages.json的注册。HBuilderX 3.5.0+支持,无需手动添加)
|
||||
|
||||
```
|
||||
"pages": [
|
||||
// ……其他页面配置
|
||||
{
|
||||
"path": "uni_modules/rt-uni-update/components/rt-uni-update/rt-uni-update",
|
||||
"style": {
|
||||
"disableScroll": true,
|
||||
"app-plus": {
|
||||
"backgroundColorTop": "transparent",
|
||||
"background": "transparent",
|
||||
"titleNView": false,
|
||||
"scrollIndicator": false,
|
||||
"popGesture": "none",
|
||||
"animationType": "fade-in",
|
||||
"animationDuration": 200
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
3. 查看显示效果 (注意:这里只是查看显示效果,具体代码需要按照下面的项目使用说明编写)
|
||||
|
||||
```
|
||||
|
||||
// App.vue的onShow中查看效果 如果无法跳转 请在`pages.json`中添加页面路径,参照第二步
|
||||
|
||||
uni.navigateTo({
|
||||
url: '/uni_modules/rt-uni-update/components/rt-uni-update/rt-uni-update'
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
|
||||
## 前言,一般来说,后台都需要有一个app的版本管理系统(可参考下图)
|
||||
|
||||

|
||||

|
||||
|
||||
|
||||
## 项目使用说明 最重要!!!
|
||||
|
||||
- 注意!!!后端返回数据要求 字段如下 (如果后端字段不一样,请在跳转更新页时手动赋值,示例见下面代码)
|
||||
|
||||
```
|
||||
data:{
|
||||
// 版本更新内容 支持<br>自动换行
|
||||
describe: '1. 修复已知问题<br>
|
||||
2. 优化用户体验',
|
||||
edition_url: '', //apk、wgt包下载地址或者应用市场地址 安卓应用市场 market://details?id=xxxx 苹果store itms-apps://itunes.apple.com/cn/app/xxxxxx
|
||||
edition_force: 0, //是否强制更新 0代表否 1代表是
|
||||
package_type: 1, //0是整包升级(apk或者appstore或者安卓应用市场) 1是wgt升级
|
||||
edition_issue:1, //是否发行 0否 1是 为了控制上架应用市场审核时不能弹出热更新框
|
||||
edition_number:100, //版本号 最重要的manifest里的版本号 (检查更新主要以服务器返回的edition_number版本号是否大于当前app的版本号来实现是否更新)
|
||||
edition_name:'1.0.0',// 版本名称 manifest里的版本名称
|
||||
edition_silence:0, // 是否静默更新 0代表否 1代表是
|
||||
}
|
||||
|
||||
// 如果后端返回的字段和上面不一致,请在前端手动赋值(示例)
|
||||
|
||||
data.edition_url = res.data.editionUrl
|
||||
data.edition_force = res.data.editionForce
|
||||
data.package_type = res.data.packageType
|
||||
data.xxx = res.data.xxx
|
||||
|
||||
|
||||
```
|
||||
|
||||
|
||||
## 后端注意!!!
|
||||
|
||||
edition_number传这个参数是为了解决部分用户app长期不使用,第一次打开服务器查到的版本是最新的是wgt包,但是之前app有过整包更新,如果直接更新最新wgt的话,会出现以前的整包添加的原生模块或者安卓权限无法使用,所以后端查询版本必须返回大于当前edition_number版本的最新的整包apk地址或者是应用市场地址,如果没有大于edition_number的整包,就返回最新的wgt包地址就行。
|
||||
|
||||
- 前端示例代码 或者根据实际业务修改 如果需要自动检测新版本,建议写在App.vue的onShow中
|
||||
|
||||
```
|
||||
import silenceUpdate from '@/uni_modules/rt-uni-update/js_sdk/silence-update.js' //引入静默更新
|
||||
|
||||
//#ifdef APP-PLUS
|
||||
|
||||
// 获取本地应用资源版本号
|
||||
plus.runtime.getProperty(plus.runtime.appid, (inf) => {
|
||||
//获取服务器的版本号
|
||||
uni.request({
|
||||
url: 'http://127.0.0.1:8088/edition_manage/get_edition', //示例接口
|
||||
data: {
|
||||
edition_type: plus.runtime.appid,
|
||||
version_type: uni.getSystemInfoSync().platform, //android或者ios
|
||||
edition_number: inf.versionCode // 打包时manifest设置的版本号
|
||||
},
|
||||
success: (res) => {
|
||||
//res.data.xxx根据后台返回的数据决定(我这里后端返回的是data),所以是res.data.data
|
||||
//判断后台返回版本号是否大于当前应用版本号 && 是否发行 (上架应用市场时一定不能弹出更新提示)
|
||||
if (Number(res.data.data.edition_number) > Number(inf.versionCode) && res
|
||||
.data.data.edition_issue == 1) {
|
||||
|
||||
//如果是wgt升级,并且是静默更新 (注意!!! 如果是手动检查新版本,就不用判断静默更新,请直接跳转更新页,不然点击检查新版本后会没反应)
|
||||
if (res.data.data.package_type == 1 && res.data.data.edition_silence == 1) {
|
||||
|
||||
//调用静默更新方法 传入下载地址
|
||||
silenceUpdate(res.data.data.edition_url)
|
||||
|
||||
} else {
|
||||
//跳转更新页面 (注意!!!如果pages.json第一页的代码里有一打开就跳转其他页面的操作,下面这行代码最好写在setTimeout里面设置延时3到5秒再执行)
|
||||
uni.navigateTo({
|
||||
url: '/uni_modules/rt-uni-update/components/rt-uni-update/rt-uni-update?obj=' +
|
||||
JSON.stringify(res.data.data)
|
||||
});
|
||||
}
|
||||
} else {
|
||||
|
||||
// 如果是手动检查新版本 需开启以下注释
|
||||
/* uni.showModal({
|
||||
title: '提示',
|
||||
content: '已是最新版本',
|
||||
showCancel: false
|
||||
}) */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
})
|
||||
|
||||
});
|
||||
|
||||
//#endif
|
||||
|
||||
|
||||
|
||||
```
|
||||
|
||||
# 常见问题汇总!!!
|
||||
|
||||
# 热更新制作wgt包的方法:1、修改manifest.json版本名称和版本号,必须大于当前版本。2、点击菜单的发行——原生App-制作应用wgt包
|
||||
|
||||
# app上传地址:个人建议开通unicloud的阿里云按量付费,方便、便宜,apk或者wgt包直接上传到云存储就行。
|
||||
|
||||
## 1、调试请打包自定义基座测试,否则uni.getSystemInfoSync().platform获取到的可能不是android或者ios,会导致无法跳转更新页
|
||||
|
||||
## 2、进度条不显示,但可以正常安装,原因:99%的情况是因为下载链接为内网链接,内网链接无法监听下载进度,请更换为外网链接
|
||||
|
||||
## 3、进度条显示,下载apk完成后,安卓不会自动弹出安装页面,原因:可能是离线打包未添加安卓安装权限,请添加以下权限或者使用云打包
|
||||
|
||||
```
|
||||
<uses-permission android:name="android.permission.INSTALL_PACKAGES" />
|
||||
|
||||
```
|
||||
```
|
||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
||||
```
|
||||
|
||||
## 4、在app.vue中无法跳转到更新页,原因:第一、在pages.json中忘记注册页面,第二、如果已经注册过页面,一般在app.vue或者首页中会有默认跳转,所以影响到了跳转更新页,解决办法:修改跳转逻辑或者在跳转更新页时加setTimeout,延时几秒在跳转
|
||||
|
||||
## 5、app内下载apk时会跳转外部下载,原因:安卓apk下载链接必须为.apk结尾,如果不是.apk结尾,就会跳转外部下载(比如应用市场链接)。
|
||||
|
||||
## 6、热更新时wgt包可以下载,但是无法安装,控制台提示wgt/wgtu文件格式错误。解决方法:下载地址必须为http://xxxxxx.wgt的格式,就是链接必须以.wgt结尾。2、如果地址是http://xxxxxx.wgt格式,请在浏览器打开这个下载地址,如果无法自动下载,一般可能都是后端下载权限的问题导致的
|
||||
|
||||
## 7、整包更新/热更新成功后,还是一直弹更新弹窗,原因是,打wgt包时未修改manifest.json的版本号,请修改版本号后上传服务器后重试。
|
||||
|
||||
## 8、苹果支持appstore链接和wgt更新,不支持整包ipa更新。
|
||||
|
||||
## 9、wgt更新,进度条100%,苹果无法安装,原因:1、wgt包名不要设置为中文,2、增加原生模块必须上传appstore,不能热更新
|
||||
|
||||
## 10、不能热更新的有:1、如果原项目没有nvue页面,新增nvue后也必须整包更新,2、增加推送、第三方登录、地图、视频播放、支付等模块,或者其他安卓权限。3、修改启动图或者app图标
|
||||
|
||||
## 11、更新弹窗后面的页面一半儿白屏,[官方的bug](https://ask.dcloud.net.cn/question/164141)
|
||||
|
||||
## 12、跳转更新页后无法获取参数,可能是使用了uni-simple-router等第三方路由插件,解决办法:通过eventChannel.$emit等方式传参,在插件里接收赋值
|
||||
|
||||
|
||||
有鼓励,更有动力,如果您认为这个插件帮到了您的开发工作,麻烦给个五星好评鼓励一下,有能力的也可以小小赞赏一下,感谢支持。
|
||||
|
||||
<img src="https://mp-bed742be-5cd0-413d-b7a5-c1bdcda83cd2.cdn.bspapp.com/rookie-ui/34afc1f2862e7579c3cbdd33d23d0de.jpg" width="220" >
|
||||
<img style="margin-left: 100px;" src="https://mp-bed742be-5cd0-413d-b7a5-c1bdcda83cd2.cdn.bspapp.com/rookie-ui/e14de964c6d89008035f651be6fa2c8.jpg" width="220" >
|
||||
|
||||
## 如有问题,请加qq 965969604
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 2.3 KiB |
BIN
uni-app/src/uni_modules/rt-uni-update/static/bg_top.png
Normal file
BIN
uni-app/src/uni_modules/rt-uni-update/static/bg_top.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.6 KiB |
@ -206,7 +206,7 @@ export const currShareRoute = () => {
|
||||
}
|
||||
let currentRoute = pages[pages.length - 1].route //获取当前页面路由
|
||||
|
||||
// #ifdef H5
|
||||
// #ifndef MP
|
||||
let currentParam: any = pages[pages.length - 1].$page.options; //获取路由参数
|
||||
// #endif
|
||||
|
||||
@ -296,6 +296,26 @@ export function img(path: string): string {
|
||||
return isUrl(path) ? path : `${imgDomain}/${path}`
|
||||
}
|
||||
|
||||
/**
|
||||
* 路径输出
|
||||
* @param path
|
||||
* @returns
|
||||
*/
|
||||
export function getUrl(path: string): string {
|
||||
// #ifdef H5
|
||||
let urlDomain = import.meta.env.VITE_IMG_DOMAIN || location.origin
|
||||
// #endif
|
||||
|
||||
// #ifndef H5
|
||||
let urlDomain = import.meta.env.VITE_IMG_DOMAIN
|
||||
// #endif
|
||||
|
||||
if (typeof path == 'string' && path.startsWith('/')) path = path.replace(/^\//, '')
|
||||
if (typeof urlDomain == 'string' && urlDomain.endsWith('/')) urlDomain = urlDomain.slice(0, -1)
|
||||
|
||||
return isUrl(path) ? path : `${urlDomain}/${path}`
|
||||
}
|
||||
|
||||
/**
|
||||
* 手机号隐藏
|
||||
*/
|
||||
@ -407,7 +427,7 @@ export function timeTurnTimeStamp(dateStr: string) {
|
||||
}
|
||||
|
||||
const trimmedDateStr = dateStr.trim();
|
||||
|
||||
|
||||
// 定义支持的日期格式转换规则
|
||||
const formatRules = [
|
||||
// 'YYYY年M月D日' -> 'YYYY-MM-DD'
|
||||
@ -448,7 +468,7 @@ export function timeTurnTimeStamp(dateStr: string) {
|
||||
|
||||
// 创建日期对象并验证
|
||||
const date = new Date(normalizedDateStr);
|
||||
|
||||
|
||||
// 检查日期是否有效
|
||||
if (isNaN(date.getTime())) {
|
||||
return null;
|
||||
@ -788,3 +808,12 @@ export function rgbaToHex (r, g, b, a) {
|
||||
let hex = "#" + componentToHex(rBlend) + componentToHex(gBlend) + componentToHex(bBlend)
|
||||
return hex.toUpperCase()
|
||||
}
|
||||
|
||||
// 获取 topFixedStatus 缓存名称
|
||||
export function getTopFixedStatusName(data: any = {}) {
|
||||
let name = 'topFixedStatus'
|
||||
if (data.id) name += '_' + data.id
|
||||
if (data.site_id) name += '_' + data.site_id
|
||||
if (data.type) name += '_' + data.type
|
||||
return name
|
||||
}
|
||||
|
||||
@ -14,7 +14,7 @@ export const redirectInterceptor = (route: { path: string, query: object }) => {
|
||||
setThemeColor(route.path)
|
||||
|
||||
// 开发模式下,如果未配置站点ID,则跳转到开发环境配置页面
|
||||
// #ifdef H5
|
||||
// #ifndef MP
|
||||
if (process.env.NODE_ENV == 'development') {
|
||||
if ((getSiteId(import.meta.env.VITE_SITE_ID || uni.getStorageSync('wap_site_id')) === '') && route.path != '/app/pages/index/develop') {
|
||||
redirect({ url: '/app/pages/index/develop', mode: 'reLaunch' })
|
||||
@ -72,7 +72,7 @@ export const launchInterceptor = () => {
|
||||
// 加载语言包
|
||||
language.loadAllLocaleMessages('app', uni.getLocale())
|
||||
|
||||
// #ifdef H5
|
||||
// #ifndef MP
|
||||
language.loadAllLocaleMessages('addon', uni.getLocale())
|
||||
// #endif
|
||||
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { watch } from 'vue'
|
||||
import { currRoute } from './common'
|
||||
import { redirectInterceptor } from './interceptor'
|
||||
import useConfigStore from "@/stores/config";
|
||||
@ -49,16 +50,32 @@ export default {
|
||||
|
||||
},
|
||||
onShareAppMessage() {
|
||||
// #ifdef MP
|
||||
return useShare().onShareAppMessage()
|
||||
// #endif
|
||||
},
|
||||
onShareTimeline() {
|
||||
// #ifdef MP
|
||||
return useShare().onShareTimeline()
|
||||
// #endif
|
||||
},
|
||||
methods: {
|
||||
themeColor() {
|
||||
const configStore = useConfigStore()
|
||||
return configStore.getThemeColor();
|
||||
}
|
||||
},
|
||||
onReady(){
|
||||
const systemStore = useSystemStore()
|
||||
// #ifdef APP-PLUS
|
||||
watch(() => systemStore.versionInfo, () => {
|
||||
const newVersion = useSystemStore().versionInfo
|
||||
if (newVersion) {
|
||||
const lock = uni.getStorageSync('update_version_close_lock') || {}
|
||||
if (!lock[ newVersion.version_code ] || newVersion.is_forced_upgrade) useSystemStore().showUpdateVersion()
|
||||
}
|
||||
}, { immediate: true })
|
||||
// #endif
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user