mirror of
https://gitee.com/niucloud-team/niucloud-admin.git
synced 2026-03-14 07:41:06 +00:00
up
This commit is contained in:
parent
7412775c03
commit
18c9a31761
@ -12,9 +12,18 @@ onLaunch((data: any) => {
|
||||
|
||||
// 添加初始化拦截器
|
||||
launchInterceptor()
|
||||
const systemStore = useSystemStore()
|
||||
|
||||
// 初始化全局数据
|
||||
const initGlobalData = () => {
|
||||
systemStore.systemInfo = uni.getSystemInfoSync();
|
||||
systemStore.getMenuButtonInfoFn()
|
||||
}
|
||||
|
||||
initGlobalData()
|
||||
|
||||
// #ifdef H5
|
||||
uni.getSystemInfoSync().platform == 'ios' && (uni.setStorageSync('initUrl', location.href))
|
||||
systemStore.systemInfo.platform == 'ios' && (uni.setStorageSync('initUrl', location.href))
|
||||
|
||||
// 传输给后台数据
|
||||
window.parent.postMessage(JSON.stringify({
|
||||
@ -38,12 +47,15 @@ onLaunch((data: any) => {
|
||||
type: 'appOnReady',
|
||||
message: '加载完成'
|
||||
}), '*');
|
||||
// 更新全局数据
|
||||
initGlobalData()
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('uni-app App.vue 接受数据错误', e)
|
||||
}
|
||||
}, false);
|
||||
|
||||
|
||||
try {
|
||||
uni.hideTabBar() // 隐藏tabbar
|
||||
} catch (e) {
|
||||
@ -79,7 +91,7 @@ onLaunch((data: any) => {
|
||||
// #endif
|
||||
|
||||
// 获取初始化数据信息
|
||||
useSystemStore().getInitFn(async() => {
|
||||
useSystemStore().getInitFn(async () => {
|
||||
|
||||
const configStore = useConfigStore()
|
||||
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
import { ref, reactive, onMounted } from 'vue';
|
||||
import diyGroup from '@/addon/components/diy/group/index.vue'
|
||||
import { getFormRecord } from '@/app/api/diy_form';
|
||||
import { deepClone } from '@/utils/common'
|
||||
|
||||
const props = defineProps(['record_id', 'completeLayout']);
|
||||
const emits = defineEmits(['callback'])
|
||||
@ -17,20 +18,21 @@ const diyFormData: any = reactive({
|
||||
global: {},
|
||||
value: []
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
getFormRecord({
|
||||
record_id: props.record_id
|
||||
}).then((res: any) => {
|
||||
diyFormData.global.completeLayout = props.completeLayout || 'style-1';
|
||||
if (res.data.recordsFieldList) {
|
||||
let recordsFieldList = deepClone(res.data.recordsFieldList)
|
||||
if (recordsFieldList) {
|
||||
|
||||
res.data.recordsFieldList.forEach((item: any) => {
|
||||
recordsFieldList.forEach((item: any) => {
|
||||
let comp = {
|
||||
id: item.field_key,
|
||||
componentName: item.field_type,
|
||||
pageStyle: '',
|
||||
viewFormDetail: true, // 查看表单详情标识
|
||||
componentIsShow: true,
|
||||
field: {
|
||||
name: item.field_name,
|
||||
value: item.handle_field_value,
|
||||
@ -47,7 +49,7 @@ onMounted(() => {
|
||||
diyFormData.value.push(comp);
|
||||
})
|
||||
}
|
||||
emits('callback', res.data.recordsFieldList)
|
||||
emits('callback', recordsFieldList)
|
||||
loading.value = false;
|
||||
}).catch(() => {
|
||||
loading.value = false;
|
||||
|
||||
@ -1,8 +1,21 @@
|
||||
<template>
|
||||
<view :style="themeColor()">
|
||||
<!-- 自定义组件渲染 -->
|
||||
<view v-show="requestData.status == 1 && !diy.getLoading()" class="diy-template-wrap">
|
||||
<diy-group ref="diyGroupRef" :data="diyFormData" />
|
||||
<view v-show="requestData.status == 1 && requestData.error && requestData.error.length === 0 && !diy.getLoading()" class="diy-template-wrap">
|
||||
<diy-group ref="diyGroupRef" :data="diyFormData"/>
|
||||
</view>
|
||||
<!-- 目前只有自定义表单才展示错误信息,其他类型暂时不控制 -->
|
||||
<view class="flex flex-col" v-if="requestData.error && requestData.error.length > 0 && diy.data == 'DIY_FORM'">
|
||||
<view class="flex-1 flex flex-col items-center pt-[20rpx]" v-for="(item, index) in requestData.error.slice(0, 1)" :key="index">
|
||||
<text class="nc-iconfont nc-icon-tanhaoV6mm text-[#ccc] mb-[30rpx] !text-[100rpx]"></text>
|
||||
<view class="text-[38rpx] font-bold mt-3">{{ item.title }}</view>
|
||||
<view class="p-[30rpx] mt-[20rpx] w-full">
|
||||
<view class="flex w-full">
|
||||
<view class="w-[30%] text-[#999] text-left">{{ item.type }}</view>
|
||||
<view class="w-[70%] text-left">{{ item.desc }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
@ -30,12 +43,12 @@ const diyFormData: any = reactive({})
|
||||
onMounted(() => {
|
||||
diy.getData(() => {
|
||||
diyFormData.status = diy.data.status;
|
||||
if (diyFormData.status) {
|
||||
if (diyFormData.status && requestData.value.error.length == 0) {
|
||||
diyFormData.title = diy.data.title;
|
||||
diyFormData.global = diy.data.global;
|
||||
if (diyFormData.global) {
|
||||
diyFormData.global.topStatusBar.isShow = false; // 顶部导航栏强制隐藏
|
||||
diyFormData.global.bottomTabBarSwitch = false; // 底部导航强制隐藏
|
||||
diyFormData.global.bottomTabBar.isShow = false; // 底部导航强制隐藏
|
||||
}
|
||||
let value: any = [];
|
||||
if (props.form_border == 'none') {
|
||||
@ -47,7 +60,7 @@ onMounted(() => {
|
||||
value.push(item);
|
||||
}
|
||||
})
|
||||
diyFormData.value = value;
|
||||
diyFormData.value = deepClone(value);
|
||||
diyFormData.componentRefs = null;
|
||||
diyGroupRef.value?.refresh();
|
||||
watchFormData();
|
||||
@ -95,6 +108,7 @@ const watchFormData = () => {
|
||||
const verify = () => {
|
||||
if (!diyFormData.status) return true;
|
||||
if (!diyFormData.value) return true;
|
||||
if (!requestData.value || requestData.value.error?.length > 0) return true;
|
||||
let allPass = true; // 是否全部通过验证
|
||||
|
||||
let componentRefs = diyGroupRef.value.getFormRef().componentRefs;
|
||||
|
||||
@ -1,127 +1,130 @@
|
||||
<template>
|
||||
<view class="diy-group" id="componentList">
|
||||
<top-tabbar v-if="data.global && Object.keys(data.global).length && data.global.topStatusBar && data.global.topStatusBar.isShow"
|
||||
:scrollBool="diyGroup.componentsScrollBool.TopTabbar" ref="topTabbarRef" :data="data.global" />
|
||||
:scrollBool="diyGroup.componentsScrollBool.TopTabbar" ref="topTabbarRef" :data="data.global" />
|
||||
<pop-ads v-if="data.global && Object.keys(data.global).length && data.global.popWindow && data.global.popWindow.show" ref="popAbsRef" :data="data.global" />
|
||||
<view v-for="(component, index) in data.value" :key="component.id"
|
||||
@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' }">
|
||||
<template v-for="(component, index) in data.value" :key="component.id">
|
||||
<view
|
||||
v-show="component.componentIsShow"
|
||||
@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 v-if="diyGroup.isShowPlaceHolder(index,component)" class="absolute w-full z-1"
|
||||
:style="{ height : (component.margin.top * 2 * -1) + 'rpx' }"
|
||||
@click.stop="diyGroup.placeholderEvent"></view>
|
||||
|
||||
<template v-if="component.componentName == 'GraphicNav'">
|
||||
<diy-graphic-nav :component="component" :global="data.global" :index="index" />
|
||||
</template>
|
||||
<template v-if="component.componentName == 'HorzBlank'">
|
||||
<diy-horz-blank :component="component" :global="data.global" :index="index" />
|
||||
</template>
|
||||
<template v-if="component.componentName == 'HorzLine'">
|
||||
<diy-horz-line :component="component" :global="data.global" :index="index" />
|
||||
</template>
|
||||
<template v-if="component.componentName == 'HotArea'">
|
||||
<diy-hot-area :component="component" :global="data.global" :index="index" />
|
||||
</template>
|
||||
<template v-if="component.componentName == 'ImageAds'">
|
||||
<diy-image-ads :component="component" :global="data.global" :index="index" />
|
||||
</template>
|
||||
<template v-if="component.componentName == 'MemberInfo'">
|
||||
<diy-member-info :component="component" :global="data.global" :index="index" />
|
||||
</template>
|
||||
<template v-if="component.componentName == 'MemberLevel'">
|
||||
<diy-member-level :component="component" :global="data.global" :index="index" />
|
||||
</template>
|
||||
<template v-if="component.componentName == 'Notice'">
|
||||
<diy-notice :component="component" :global="data.global" :index="index" />
|
||||
</template>
|
||||
<template v-if="component.componentName == 'RubikCube'">
|
||||
<diy-rubik-cube :component="component" :global="data.global" :index="index" />
|
||||
</template>
|
||||
<template v-if="component.componentName == 'Text'">
|
||||
<diy-text :component="component" :global="data.global" :index="index" />
|
||||
</template>
|
||||
<template v-if="component.componentName == 'RichText'">
|
||||
<diy-rich-text :component="component" :global="data.global" :index="index" />
|
||||
</template>
|
||||
<template v-if="component.componentName == 'ActiveCube'">
|
||||
<diy-active-cube :component="component" :global="data.global" :index="index" />
|
||||
</template>
|
||||
<template v-if="component.componentName == 'FloatBtn'">
|
||||
<diy-float-btn :component="component" :global="data.global" :index="index" />
|
||||
</template>
|
||||
<template v-if="component.componentName == 'CarouselSearch'">
|
||||
<diy-carousel-search :scrollBool="diyGroup.componentsScrollBool.CarouselSearch" :component="component" :global="data.global" :index="index" />
|
||||
</template>
|
||||
<template v-if="component.componentName == 'PictureShow'">
|
||||
<diy-picture-show :component="component" :global="data.global" :index="index" />
|
||||
</template>
|
||||
<template v-if="component.componentName == 'FormSubmit'">
|
||||
<diy-form-submit :component="component" :global="data.global" :index="index" />
|
||||
</template>
|
||||
<template v-if="component.componentName == 'FormInput'">
|
||||
<diy-form-input ref="diyFormInputRef" :component="component" :global="data.global" :index="index" />
|
||||
</template>
|
||||
<template v-if="component.componentName == 'FormTextarea'">
|
||||
<diy-form-textarea ref="diyFormTextareaRef" :component="component" :global="data.global" :index="index" />
|
||||
</template>
|
||||
<template v-if="component.componentName == 'FormIdentity'">
|
||||
<diy-form-identity ref="diyFormIdentityRef" :component="component" :global="data.global" :index="index" />
|
||||
</template>
|
||||
<template v-if="component.componentName == 'FormEmail'">
|
||||
<diy-form-email ref="diyFormEmailRef" :component="component" :global="data.global" :index="index" />
|
||||
</template>
|
||||
<template v-if="component.componentName == 'FormMobile'">
|
||||
<diy-form-mobile ref="diyFormMobileRef" :component="component" :global="data.global" :index="index" />
|
||||
</template>
|
||||
<template v-if="component.componentName == 'FormWechatName'">
|
||||
<diy-form-wechat-name ref="diyFormWechatNameRef" :component="component" :global="data.global" :index="index" />
|
||||
</template>
|
||||
<template v-if="component.componentName == 'FormNumber'">
|
||||
<diy-form-number ref="diyFormNumberRef" :component="component" :global="data.global" :index="index" />
|
||||
</template>
|
||||
<template v-if="component.componentName == 'FormRadio'">
|
||||
<diy-form-radio ref="diyFormRadioRef" :component="component" :global="data.global" :index="index" />
|
||||
</template>
|
||||
<template v-if="component.componentName == 'FormCheckbox'">
|
||||
<diy-form-checkbox ref="diyFormCheckboxRef" :component="component" :global="data.global" :index="index" />
|
||||
</template>
|
||||
<template v-if="component.componentName == 'FormTable'">
|
||||
<diy-form-table ref="diyFormTableRef" :component="component" :global="data.global" :index="index" />
|
||||
</template>
|
||||
<template v-if="component.componentName == 'FormDate'">
|
||||
<diy-form-date ref="diyFormDateRef" :component="component" :global="data.global" :index="index" />
|
||||
</template>
|
||||
<template v-if="component.componentName == 'FormDateScope'">
|
||||
<diy-form-date-scope ref="diyFormDateScopeRef" :component="component" :global="data.global" :index="index" />
|
||||
</template>
|
||||
<template v-if="component.componentName == 'FormTime'">
|
||||
<diy-form-time ref="diyFormTimeRef" :component="component" :global="data.global" :index="index" />
|
||||
</template>
|
||||
<template v-if="component.componentName == 'FormTimeScope'">
|
||||
<diy-form-time-scope ref="diyFormTimeScopeRef" :component="component" :global="data.global" :index="index" />
|
||||
</template>
|
||||
<template v-if="component.componentName == 'FormLocation'">
|
||||
<diy-form-location ref="diyFormLocationRef" :component="component" :global="data.global" :index="index" />
|
||||
</template>
|
||||
<template v-if="component.componentName == 'FormAddress'">
|
||||
<diy-form-address ref="diyFormAddressRef" :component="component" :global="data.global" :index="index" />
|
||||
</template>
|
||||
<template v-if="component.componentName == 'FormImage'">
|
||||
<diy-form-image ref="diyFormImageRef" :component="component" :global="data.global" :index="index" />
|
||||
</template>
|
||||
<template v-if="component.componentName == 'FormVideo'">
|
||||
<diy-form-video ref="diyFormVideoRef" :component="component" :global="data.global" :index="index" />
|
||||
</template>
|
||||
<template v-if="component.componentName == 'FormFile'">
|
||||
<diy-form-file ref="diyFormFileRef" :component="component" :global="data.global" :index="index" />
|
||||
</template>
|
||||
<!-- 装修模式下,设置负上边距后超出的内容,禁止选中设置 -->
|
||||
<view v-if="diyGroup.isShowPlaceHolder(index,component)" class="absolute w-full z-1"
|
||||
:style="{ height : (component.margin.top * 2 * -1) + 'rpx' }"
|
||||
@click.stop="diyGroup.placeholderEvent"></view>
|
||||
|
||||
<template v-if="component.componentName == 'GraphicNav'">
|
||||
<diy-graphic-nav :component="component" :global="data.global" :index="index" />
|
||||
</template>
|
||||
<template v-if="component.componentName == 'HorzBlank'">
|
||||
<diy-horz-blank :component="component" :global="data.global" :index="index" />
|
||||
</template>
|
||||
<template v-if="component.componentName == 'HorzLine'">
|
||||
<diy-horz-line :component="component" :global="data.global" :index="index" />
|
||||
</template>
|
||||
<template v-if="component.componentName == 'HotArea'">
|
||||
<diy-hot-area :component="component" :global="data.global" :index="index" />
|
||||
</template>
|
||||
<template v-if="component.componentName == 'ImageAds'">
|
||||
<diy-image-ads :component="component" :global="data.global" :index="index" />
|
||||
</template>
|
||||
<template v-if="component.componentName == 'MemberInfo'">
|
||||
<diy-member-info :component="component" :global="data.global" :index="index" />
|
||||
</template>
|
||||
<template v-if="component.componentName == 'MemberLevel'">
|
||||
<diy-member-level :component="component" :global="data.global" :index="index" />
|
||||
</template>
|
||||
<template v-if="component.componentName == 'Notice'">
|
||||
<diy-notice :component="component" :global="data.global" :index="index" />
|
||||
</template>
|
||||
<template v-if="component.componentName == 'RubikCube'">
|
||||
<diy-rubik-cube :component="component" :global="data.global" :index="index" />
|
||||
</template>
|
||||
<template v-if="component.componentName == 'Text'">
|
||||
<diy-text :component="component" :global="data.global" :index="index" />
|
||||
</template>
|
||||
<template v-if="component.componentName == 'RichText'">
|
||||
<diy-rich-text :component="component" :global="data.global" :index="index" />
|
||||
</template>
|
||||
<template v-if="component.componentName == 'ActiveCube'">
|
||||
<diy-active-cube :component="component" :global="data.global" :index="index" />
|
||||
</template>
|
||||
<template v-if="component.componentName == 'FloatBtn'">
|
||||
<diy-float-btn :component="component" :global="data.global" :index="index" />
|
||||
</template>
|
||||
<template v-if="component.componentName == 'CarouselSearch'">
|
||||
<diy-carousel-search :scrollBool="diyGroup.componentsScrollBool.CarouselSearch" :component="component" :global="data.global" :index="index" />
|
||||
</template>
|
||||
<template v-if="component.componentName == 'PictureShow'">
|
||||
<diy-picture-show :component="component" :global="data.global" :index="index" />
|
||||
</template>
|
||||
<template v-if="component.componentName == 'FormSubmit'">
|
||||
<diy-form-submit :component="component" :global="data.global" :index="index" />
|
||||
</template>
|
||||
<template v-if="component.componentName == 'FormInput'">
|
||||
<diy-form-input ref="diyFormInputRef" :component="component" :global="data.global" :index="index" />
|
||||
</template>
|
||||
<template v-if="component.componentName == 'FormTextarea'">
|
||||
<diy-form-textarea ref="diyFormTextareaRef" :component="component" :global="data.global" :index="index" />
|
||||
</template>
|
||||
<template v-if="component.componentName == 'FormIdentity'">
|
||||
<diy-form-identity ref="diyFormIdentityRef" :component="component" :global="data.global" :index="index" />
|
||||
</template>
|
||||
<template v-if="component.componentName == 'FormEmail'">
|
||||
<diy-form-email ref="diyFormEmailRef" :component="component" :global="data.global" :index="index" />
|
||||
</template>
|
||||
<template v-if="component.componentName == 'FormMobile'">
|
||||
<diy-form-mobile ref="diyFormMobileRef" :component="component" :global="data.global" :index="index" />
|
||||
</template>
|
||||
<template v-if="component.componentName == 'FormWechatName'">
|
||||
<diy-form-wechat-name ref="diyFormWechatNameRef" :component="component" :global="data.global" :index="index" />
|
||||
</template>
|
||||
<template v-if="component.componentName == 'FormNumber'">
|
||||
<diy-form-number ref="diyFormNumberRef" :component="component" :global="data.global" :index="index" />
|
||||
</template>
|
||||
<template v-if="component.componentName == 'FormRadio'">
|
||||
<diy-form-radio ref="diyFormRadioRef" :component="component" :global="data.global" :index="index" />
|
||||
</template>
|
||||
<template v-if="component.componentName == 'FormCheckbox'">
|
||||
<diy-form-checkbox ref="diyFormCheckboxRef" :component="component" :global="data.global" :index="index" />
|
||||
</template>
|
||||
<template v-if="component.componentName == 'FormTable'">
|
||||
<diy-form-table ref="diyFormTableRef" :component="component" :global="data.global" :index="index" />
|
||||
</template>
|
||||
<template v-if="component.componentName == 'FormDate'">
|
||||
<diy-form-date ref="diyFormDateRef" :component="component" :global="data.global" :index="index" />
|
||||
</template>
|
||||
<template v-if="component.componentName == 'FormDateScope'">
|
||||
<diy-form-date-scope ref="diyFormDateScopeRef" :component="component" :global="data.global" :index="index" />
|
||||
</template>
|
||||
<template v-if="component.componentName == 'FormTime'">
|
||||
<diy-form-time ref="diyFormTimeRef" :component="component" :global="data.global" :index="index" />
|
||||
</template>
|
||||
<template v-if="component.componentName == 'FormTimeScope'">
|
||||
<diy-form-time-scope ref="diyFormTimeScopeRef" :component="component" :global="data.global" :index="index" />
|
||||
</template>
|
||||
<template v-if="component.componentName == 'FormLocation'">
|
||||
<diy-form-location ref="diyFormLocationRef" :component="component" :global="data.global" :index="index" />
|
||||
</template>
|
||||
<template v-if="component.componentName == 'FormAddress'">
|
||||
<diy-form-address ref="diyFormAddressRef" :component="component" :global="data.global" :index="index" />
|
||||
</template>
|
||||
<template v-if="component.componentName == 'FormImage'">
|
||||
<diy-form-image ref="diyFormImageRef" :component="component" :global="data.global" :index="index" />
|
||||
</template>
|
||||
<template v-if="component.componentName == 'FormVideo'">
|
||||
<diy-form-video ref="diyFormVideoRef" :component="component" :global="data.global" :index="index" />
|
||||
</template>
|
||||
<template v-if="component.componentName == 'FormFile'">
|
||||
<diy-form-file ref="diyFormFileRef" :component="component" :global="data.global" :index="index" />
|
||||
</template>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<template v-if="diyStore.mode == '' && data.global && data.global.bottomTabBarSwitch">
|
||||
</template>
|
||||
<template v-if="diyStore.mode == '' && data.global && data.global.bottomTabBar && data.global.bottomTabBar.isShow">
|
||||
<view class="pt-[20rpx]"></view>
|
||||
<tabbar />
|
||||
</template>
|
||||
@ -130,7 +133,6 @@
|
||||
<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'
|
||||
import useDiyStore from '@/app/stores/diy';
|
||||
import { ref, getCurrentInstance } from 'vue';
|
||||
|
||||
@ -67,7 +67,7 @@ export function useDiyGroup(params: any = {}) {
|
||||
onMounted(() => {
|
||||
// #ifdef H5
|
||||
if (diyStore.mode == 'decorate') {
|
||||
var el: any = document.getElementById('componentList');
|
||||
const el: any = document.getElementById('componentList');
|
||||
const sortable = Sortable.create(el, {
|
||||
draggable: '.draggable-element',
|
||||
animation: 200,
|
||||
@ -91,7 +91,6 @@ export function useDiyGroup(params: any = {}) {
|
||||
|
||||
nextTick(() => {
|
||||
setTimeout(() => {
|
||||
|
||||
// 初始化组件滚动值
|
||||
scrollVal = uni.getStorageSync('componentsScrollValGroup');
|
||||
if (scrollVal) {
|
||||
|
||||
@ -90,6 +90,13 @@ export function updateWechatOpenid(data: AnyObject) {
|
||||
return request.put('wechat/update_openid', data, { showErrorMessage: false })
|
||||
}
|
||||
|
||||
/**
|
||||
* 微信公众号号修改openid
|
||||
*/
|
||||
export function updateWechatOpenidByH5(data: AnyObject) {
|
||||
return request.put('wechat/update_openid_h5', data, { showErrorMessage: false })
|
||||
}
|
||||
|
||||
/**
|
||||
* 微信小程序授权登录
|
||||
*/
|
||||
@ -123,4 +130,4 @@ export function bind(data: AnyObject) {
|
||||
*/
|
||||
export function memberLog(data: AnyObject) {
|
||||
return request.post('member/log', data, { showErrorMessage: false })
|
||||
}
|
||||
}
|
||||
@ -146,7 +146,7 @@ const diyComponent = computed(() => {
|
||||
})
|
||||
|
||||
const warpCss = computed(() => {
|
||||
var style = '';
|
||||
let style = '';
|
||||
style += 'position:relative;';
|
||||
if (diyComponent.value.componentStartBgColor) {
|
||||
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${ diyComponent.value.componentGradientAngle },${ diyComponent.value.componentStartBgColor },${ diyComponent.value.componentEndBgColor });`;
|
||||
@ -168,7 +168,7 @@ const warpCss = computed(() => {
|
||||
|
||||
// 背景图加遮罩层
|
||||
const maskLayer = computed(() => {
|
||||
var style = '';
|
||||
let style = '';
|
||||
if (diyComponent.value.componentBgUrl) {
|
||||
style += 'position:absolute;top:0;width:100%;';
|
||||
style += `background: rgba(0,0,0,${ diyComponent.value.componentBgAlpha / 10 });`;
|
||||
@ -213,7 +213,7 @@ const setItemStyle4 = () => {
|
||||
|
||||
// 公共模块颜色
|
||||
const commonTempCss = (data: any) => {
|
||||
var style = '';
|
||||
let style = '';
|
||||
if (data.listFrame.startColor && data.listFrame.endColor) {
|
||||
style += `background:linear-gradient(${ data.listFrame.startColor },${ data.listFrame.endColor });`;
|
||||
} else {
|
||||
@ -228,7 +228,7 @@ const commonTempCss = (data: any) => {
|
||||
}
|
||||
|
||||
const btnCss = (item: any) => {
|
||||
var style = '';
|
||||
let style = '';
|
||||
style += `background:linear-gradient(90deg,${ item.startColor },${ item.endColor });`;
|
||||
return style;
|
||||
};
|
||||
|
||||
@ -11,10 +11,11 @@
|
||||
</view>
|
||||
<view class="fixed-wrap" :style="fixedStyle">
|
||||
<view v-if="diyComponent.search.style == 'style-1'" class="diy-search-wrap relative z-10"
|
||||
@click="diyStore.toRedirect(diyComponent.search.link)" :style="navbarInnerStyle">
|
||||
<view class="img-wrap" v-if="diyComponent.search.logo">
|
||||
@click="diyStore.toRedirect(diyComponent.search.link)" :style="!props.global.topStatusBar.isShow ? navbarInnerStyle : ''">
|
||||
<!-- <view class="img-wrap" v-if="diyComponent.search.logo">
|
||||
<image :src="img(diyComponent.search.logo)" mode="aspectFit" />
|
||||
</view>
|
||||
</view> -->
|
||||
<image :src="img(diyComponent.search.logo)" v-if="diyComponent.search.logo" mode="aspectFit" class="max-h-[60rpx] w-[152rpx] mr-[20rpx]" />
|
||||
<view class="search-content" :style="{backgroundColor: diyComponent.search.bgColor }" @click.stop="diyStore.toRedirect(diyComponent.search.link)">
|
||||
<text class="input-content text-[#fff] text-[24rpx] leading-[68rpx]" :style="{color: diyComponent.search.color }">{{ isShowSearchPlaceholder ? diyComponent.search.text : '' }}</text>
|
||||
<text class="nc-iconfont nc-icon-sousuo-duanV6xx1 w-[80rpx] h-[52rpx] flex items-center justify-center rounded-[50rpx] text-[28rpx] text-[#fff]"
|
||||
@ -27,8 +28,9 @@
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="diyComponent.search.style == 'style-2'" class="diy-search-wrap style-2 relative z-10" @click="diyStore.toRedirect(diyComponent.search.link)">
|
||||
<view class="flex items-center" :style="navbarInnerStyle">
|
||||
<view class="img-wrap" v-if="diyComponent.search.logo"><image :src="img(diyComponent.search.logo)" mode="aspectFit" /></view>
|
||||
<view class="flex items-center" :style="!props.global.topStatusBar.isShow ? navbarInnerStyle : ''">
|
||||
<!-- <view class="img-wrap" v-if="diyComponent.search.logo"><image :src="img(diyComponent.search.logo)" mode="aspectFit" /></view> -->
|
||||
<image :src="img(diyComponent.search.logo)" v-if="diyComponent.search.logo" mode="aspectFit" class="max-h-[60rpx] w-[152rpx] mr-[10rpx]" />
|
||||
<view :style="searchSubTitleCss" class="max-w-[360rpx] text-[24rpx] h-[38rpx] rounded-r-[20rpx] rounded-t-[20rpx] rounded-bl-[2rpx]" v-if="diyComponent.search.subTitle.text">
|
||||
<view class="truncate leading-[38rpx] h-[38rpx] px-[12rpx]">{{ diyComponent.search.subTitle.text }}</view>
|
||||
</view>
|
||||
@ -124,7 +126,7 @@
|
||||
|
||||
<!-- 分类展开 -->
|
||||
<u-popup :safeAreaInsetTop="true" :show="tabAllPopup" mode="top" @close="tabAllPopup = false">
|
||||
<view class="text-sm px-[30rpx] pt-3" :style="{'padding-top':(menuButtonInfo.top+'px')}">全部分类</view>
|
||||
<view class="text-sm px-[30rpx] pt-3" :style="{'padding-top':(systemStore.menuButtonInfo.top+'px')}">全部分类</view>
|
||||
<view class="flex flex-wrap pl-[30rpx] pt-[30rpx]">
|
||||
<view @click="changeData({ source : 'home' },-1)" :class="['px-[26rpx] border-[2rpx] border-solid border-transparent h-[60rpx] mr-[30rpx] mb-[30rpx] flex items-center justify-center bg-[#F4F4F4] rounded-[8rpx] text-xs', { 'tab-select-popup': currTabIndex == -1 }]">首页</view>
|
||||
<text @click="changeData(item,index)" v-for="(item, index) in diyComponent.tab.list" :key="index" :class="['px-[26rpx] border-[2rpx] border-solid border-transparent h-[60rpx] mr-[30rpx] mb-[30rpx] flex items-center justify-center bg-[#F4F4F4] rounded-[8rpx] text-xs', { 'tab-select-popup': index == currTabIndex }]">{{ item.text }}</text>
|
||||
@ -155,8 +157,6 @@ import { useLocation } from '@/hooks/useLocation'
|
||||
import useSystemStore from '@/stores/system';
|
||||
|
||||
const systemStore = useSystemStore();
|
||||
const systemInfo = uni.getSystemInfoSync();
|
||||
|
||||
const instance = getCurrentInstance();
|
||||
const props = defineProps(['component', 'index', 'global', 'scrollBool']);
|
||||
const diyStore = useDiyStore();
|
||||
@ -180,7 +180,7 @@ locationVal.init();
|
||||
/************** 定位-end ****************/
|
||||
|
||||
const warpCss = computed(() => {
|
||||
var style = '';
|
||||
let style = '';
|
||||
if (diyComponent.value.componentStartBgColor) {
|
||||
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${ diyComponent.value.componentGradientAngle },${ diyComponent.value.componentStartBgColor },${ diyComponent.value.componentEndBgColor });`;
|
||||
else style += 'background-color:' + diyComponent.value.componentStartBgColor + ';';
|
||||
@ -210,7 +210,7 @@ const setModuleLocation = () => {
|
||||
|
||||
const fixedStyleBg = ref(false);
|
||||
const fixedStyle = computed(() => {
|
||||
var style = '';
|
||||
let style = '';
|
||||
if (diyComponent.value.swiper.swiperStyle == 'style-3') {
|
||||
style += 'position: absolute;z-index: 99;left: 0;right: 0;';
|
||||
}
|
||||
@ -221,7 +221,7 @@ const fixedStyle = computed(() => {
|
||||
}
|
||||
if (diyComponent.value.swiper.swiperStyle == 'style-3') {
|
||||
// h5,上移的像素,采取的是平均值
|
||||
if (systemInfo.platform === 'ios') {
|
||||
if (systemStore.systemInfo.platform === 'ios') {
|
||||
style += 'top: 55px;';
|
||||
} else {
|
||||
style += 'top: 44.5px;';
|
||||
@ -236,14 +236,12 @@ const fixedStyle = computed(() => {
|
||||
style += 'position: fixed;z-index: 99;top: 0;left: 0;right: 0;';
|
||||
}
|
||||
|
||||
// #ifdef MP-WEIXIN || MP-BAIDU || MP-TOUTIAO || MP-QQ
|
||||
menuButtonInfo = uni.getMenuButtonBoundingClientRect();
|
||||
if (props.global.topStatusBar.isShow) {
|
||||
style += 'top:' + diyStore.topTabarHeight + 'px;';
|
||||
}
|
||||
// #endif
|
||||
|
||||
if (props.scrollBool == 1 || props.scrollBool == 2) {
|
||||
// #ifdef MP-WEIXIN || MP-BAIDU || MP-TOUTIAO || MP-QQ
|
||||
if (props.global.topStatusBar.isShow) {
|
||||
style += 'top:' + diyStore.topTabarHeight + 'px;';
|
||||
}
|
||||
// #endif
|
||||
// #ifdef H5
|
||||
if (props.global.topStatusBar.isShow && props.global.topStatusBar.style == 'style-4') {
|
||||
style += 'top:' + diyStore.topTabarHeight + 'px;';
|
||||
@ -273,7 +271,7 @@ const carouselSwiperStyle = () => {
|
||||
if (diyComponent.value.swiper.swiperStyle == 'style-3') {
|
||||
// #ifdef H5
|
||||
// h5,上移的像素,采取的是平均值
|
||||
if (systemInfo.platform === 'ios') {
|
||||
if (systemStore.systemInfo.platform === 'ios') {
|
||||
style = 'margin-top: -55px;';
|
||||
} else {
|
||||
style = 'margin-top: -44.5px;';
|
||||
@ -314,7 +312,7 @@ const isShowSearchPlaceholder = computed(() => {
|
||||
|
||||
// 背景渐变
|
||||
const bgImgBoxStyle = computed(() => {
|
||||
var style = '';
|
||||
let style = '';
|
||||
let str = props.global.pageStartBgColor ? props.global.pageStartBgColor : 'rgba(255,255,255,1)';
|
||||
if (str.indexOf('(') > -1) {
|
||||
let arr = str.split('(')[1].split(')')[0].split(',');
|
||||
@ -329,7 +327,7 @@ const bgImgBoxStyle = computed(() => {
|
||||
|
||||
// 轮播样式二
|
||||
const swiperStyleBool = computed(() => {
|
||||
var style = diyComponent.value.swiper.swiperStyle == 'style-2' || diyComponent.value.swiper.swiperStyle == 'style-3' ? true : false;
|
||||
const style = diyComponent.value.swiper.swiperStyle == 'style-2' || diyComponent.value.swiper.swiperStyle == 'style-3' ? true : false;
|
||||
return style;
|
||||
})
|
||||
|
||||
@ -344,7 +342,7 @@ const swiperChange = e => {
|
||||
};
|
||||
|
||||
const swiperWarpCss = computed(() => {
|
||||
var style = '';
|
||||
let style = '';
|
||||
if (diyComponent.value.swiper.topRounded) style += 'border-top-left-radius:' + diyComponent.value.swiper.topRounded * 2 + 'rpx;';
|
||||
if (diyComponent.value.swiper.topRounded) style += 'border-top-right-radius:' + diyComponent.value.swiper.topRounded * 2 + 'rpx;';
|
||||
if (diyComponent.value.swiper.bottomRounded) style += 'border-bottom-left-radius:' + diyComponent.value.swiper.bottomRounded * 2 + 'rpx;';
|
||||
@ -353,7 +351,7 @@ const swiperWarpCss = computed(() => {
|
||||
})
|
||||
|
||||
const searchSubTitleCss = computed(() => {
|
||||
var style = '';
|
||||
let style = '';
|
||||
if (diyComponent.value.search.subTitle.textColor) style += 'color:' + diyComponent.value.search.subTitle.textColor + ';';
|
||||
if (diyComponent.value.search.subTitle.startColor && diyComponent.value.search.subTitle.endColor) style += `background:linear-gradient(${ diyComponent.value.search.subTitle.startColor }, ${ diyComponent.value.search.subTitle.endColor });`;
|
||||
else style += 'background-color:' + (diyComponent.value.search.subTitle.startColor || diyComponent.value.search.subTitle.endColor) + ';';
|
||||
@ -383,15 +381,13 @@ const changeData = (item: any, index: any) => {
|
||||
}
|
||||
|
||||
const tabAllPopup = ref(false);
|
||||
let menuButtonInfo: any = {};
|
||||
const navbarInnerStyle = ref('')
|
||||
// 如果是小程序,获取右上角胶囊的尺寸信息,避免导航栏右侧内容与胶囊重叠(支付宝小程序非本API,尚未兼容)
|
||||
// #ifdef MP-WEIXIN || MP-BAIDU || MP-TOUTIAO || MP-QQ
|
||||
menuButtonInfo = uni.getMenuButtonBoundingClientRect();
|
||||
// 导航栏内部盒子的样式
|
||||
// 导航栏宽度,如果在小程序下,导航栏宽度为胶囊的左边到屏幕左边的距离
|
||||
// 如果是各家小程序,导航栏内部的宽度需要减少右边胶囊的宽度
|
||||
navbarInnerStyle.value += 'padding-top:' + menuButtonInfo.top + 'px;';
|
||||
navbarInnerStyle.value += 'padding-top:' + systemStore.menuButtonInfo.top + 'px;';
|
||||
// #endif
|
||||
onMounted(() => {
|
||||
refresh();
|
||||
@ -418,14 +414,14 @@ onMounted(() => {
|
||||
if (diyComponent.value.positionWay == 'fixed') {
|
||||
if (props.global.topStatusBar.isShow == false) {
|
||||
navbarInnerStyle.value = ''
|
||||
let rightButtonWidth = menuButtonInfo.width ? menuButtonInfo.width * 2 + 'rpx' : '70rpx';
|
||||
let rightButtonWidth = systemStore.menuButtonInfo.width ? systemStore.menuButtonInfo.width * 2 + 'rpx' : '70rpx';
|
||||
navbarInnerStyle.value += 'padding-right:calc(' + rightButtonWidth + ' + 30rpx);';
|
||||
navbarInnerStyle.value += 'padding-top:' + menuButtonInfo.top + 'px;';
|
||||
navbarInnerStyle.value += 'padding-top:' + systemStore.menuButtonInfo.top + 'px;';
|
||||
} else if (props.global.topStatusBar) {
|
||||
navbarInnerStyle.value = ''
|
||||
}
|
||||
} else {
|
||||
let rightButtonWidth = menuButtonInfo.width ? menuButtonInfo.width * 2 + 'rpx' : '70rpx';
|
||||
let rightButtonWidth = systemStore.menuButtonInfo.width ? systemStore.menuButtonInfo.width * 2 + 'rpx' : '70rpx';
|
||||
navbarInnerStyle.value += 'padding-right:calc(' + rightButtonWidth + ' + 30rpx);';
|
||||
}
|
||||
// #endif
|
||||
@ -471,7 +467,7 @@ const getDiyInfoFn = (id: any) => {
|
||||
let sources = JSON.parse(data.value);
|
||||
diyPageData.global = sources.global;
|
||||
diyPageData.global.topStatusBar.isShow = false; // 子页面不需要展示顶部导航栏
|
||||
diyPageData.global.bottomTabBarSwitch = false; // 子页面不需要展示底部导航
|
||||
diyPageData.global.bottomTabBar.isShow = false; // 子页面不需要展示底部导航
|
||||
diyPageData.value = sources.value;
|
||||
|
||||
diyPageData.value.forEach((item: any, index) => {
|
||||
@ -811,8 +807,7 @@ if (componentsScrollVal && (typeof componentsScrollVal == "object")) {
|
||||
width: 10rpx;
|
||||
height: 10rpx;
|
||||
border-radius: 6rpx;
|
||||
margin: 0;
|
||||
margin-right: 14rpx;
|
||||
margin: 0 14rpx 0 0;
|
||||
|
||||
&.last-of-type {
|
||||
margin-right: 0;
|
||||
|
||||
@ -45,7 +45,7 @@ const diyComponent = computed(() => {
|
||||
})
|
||||
|
||||
const warpCss = computed(() => {
|
||||
var style = '';
|
||||
let style = '';
|
||||
if (diyComponent.value.componentStartBgColor) {
|
||||
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${ diyComponent.value.componentGradientAngle },${ diyComponent.value.componentStartBgColor },${ diyComponent.value.componentEndBgColor });`;
|
||||
else style += 'background-color:' + diyComponent.value.componentStartBgColor + ';';
|
||||
@ -59,7 +59,7 @@ const warpCss = computed(() => {
|
||||
})
|
||||
|
||||
const floatBtnItemCss = computed(() => {
|
||||
var style = '';
|
||||
let style = '';
|
||||
style += 'width:' + diyComponent.value.imageSize * 2 + 'rpx;';
|
||||
style += 'height:' + diyComponent.value.imageSize * 2 + 'rpx;';
|
||||
style += 'border-radius:' + diyComponent.value.aroundRadius * 2 + 'rpx;';
|
||||
@ -92,7 +92,7 @@ const floatBtnWrapCss = computed(() => {
|
||||
const styleTwoRepeat = ref(true)
|
||||
const styleTwoRepeatTime: any = ref(null)
|
||||
const styleTwoWarpCss = computed(() => {
|
||||
var style = '';
|
||||
let style = '';
|
||||
if (diyComponent.value.componentStartBgColor) {
|
||||
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${ diyComponent.value.componentGradientAngle },${ diyComponent.value.componentStartBgColor },${ diyComponent.value.componentEndBgColor });`;
|
||||
else style += 'background-color:' + diyComponent.value.componentStartBgColor + ';';
|
||||
@ -107,7 +107,7 @@ const styleTwoWarpCss = computed(() => {
|
||||
})
|
||||
|
||||
const styleTwoSphere = computed(() => {
|
||||
var style = 'transition: right .25s;background: rgba(0, 0, 0, 0.5);';
|
||||
let style = 'transition: right .25s;background: rgba(0, 0, 0, 0.5);';
|
||||
style += styleTwoRepeat.value ? '' : 'right:-32rpx !important;transition-delay: 0.25s;';
|
||||
return style
|
||||
})
|
||||
|
||||
@ -116,7 +116,7 @@
|
||||
})
|
||||
|
||||
const warpCss = computed(() => {
|
||||
var style = '';
|
||||
let style = '';
|
||||
style += 'position:relative;';
|
||||
if(diyComponent.value.componentStartBgColor) {
|
||||
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${diyComponent.value.componentGradientAngle},${diyComponent.value.componentStartBgColor},${diyComponent.value.componentEndBgColor});`;
|
||||
|
||||
@ -138,7 +138,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
// 表单 多选项组件
|
||||
import { ref, computed, watch, onMounted } from 'vue';
|
||||
import { ref, computed, watch, onMounted, nextTick } from 'vue';
|
||||
import { img } from '@/utils/common';
|
||||
import { t } from '@/locale'
|
||||
import useDiyStore from '@/app/stores/diy';
|
||||
@ -206,7 +206,7 @@ const eventFn = (type: any) => {
|
||||
}
|
||||
|
||||
const warpCss = computed(() => {
|
||||
var style = '';
|
||||
let style = '';
|
||||
style += 'position:relative;';
|
||||
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${ diyComponent.value.componentGradientAngle },${ diyComponent.value.componentStartBgColor },${ diyComponent.value.componentEndBgColor });`;
|
||||
else if (diyComponent.value.componentStartBgColor) style += 'background-color:' + diyComponent.value.componentStartBgColor + ';';
|
||||
@ -237,17 +237,24 @@ onMounted(() => {
|
||||
}
|
||||
)
|
||||
} else {
|
||||
}
|
||||
if (diyComponent.value.style == 'style-3') {
|
||||
pullDownVal.value = diyComponent.value.field.value.map(item => item.id);
|
||||
}
|
||||
if (diyComponent.value.field.value.length > 0) {
|
||||
selectValue.value = diyComponent.value.field.value.map(item => item.id);
|
||||
watch(
|
||||
() => diyComponent.value.field.value,
|
||||
(newValue, oldValue) => {
|
||||
refresh();
|
||||
}
|
||||
)
|
||||
}
|
||||
});
|
||||
|
||||
const refresh = () => {
|
||||
// console.log('diyComponent.value.field.value',diyComponent.value.field.value)
|
||||
nextTick(()=>{
|
||||
if (diyComponent.value.style == 'style-3') {
|
||||
pullDownVal.value = diyComponent.value.field.value.map(item => item.id);
|
||||
}
|
||||
if (diyComponent.value.field.value?.length) {
|
||||
selectValue.value = diyComponent.value.field.value.map(item => item.id);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const checkboxPlaceholder = computed(() => {
|
||||
|
||||
@ -196,7 +196,7 @@ const defaultDate = computed(() => {
|
||||
})
|
||||
|
||||
const warpCss = computed(() => {
|
||||
var style = '';
|
||||
let style = '';
|
||||
style += 'position:relative;';
|
||||
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${ diyComponent.value.componentGradientAngle },${ diyComponent.value.componentStartBgColor },${ diyComponent.value.componentEndBgColor });`;
|
||||
else if (diyComponent.value.componentStartBgColor) style += 'background-color:' + diyComponent.value.componentStartBgColor + ';';
|
||||
|
||||
@ -132,7 +132,7 @@ const eventFn = (type: any) => {
|
||||
|
||||
// 日期
|
||||
const startDate = computed(() => {
|
||||
var date = '';
|
||||
let date = '';
|
||||
if (diyComponent.value.field.value.date) {
|
||||
let timestamp = diyComponent.value.field.value.timestamp;
|
||||
date = getDateFn(timestamp);
|
||||
@ -158,7 +158,7 @@ const startDate = computed(() => {
|
||||
})
|
||||
|
||||
const warpCss = computed(() => {
|
||||
var style = '';
|
||||
let style = '';
|
||||
style += 'position:relative;';
|
||||
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${ diyComponent.value.componentGradientAngle },${ diyComponent.value.componentStartBgColor },${ diyComponent.value.componentEndBgColor });`;
|
||||
else if (diyComponent.value.componentStartBgColor) style += 'background-color:' + diyComponent.value.componentStartBgColor + ';';
|
||||
|
||||
@ -130,7 +130,7 @@ const eventFn = (type: any) => {
|
||||
}
|
||||
|
||||
const warpCss = computed(() => {
|
||||
var style = '';
|
||||
let style = '';
|
||||
style += 'position:relative;';
|
||||
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${ diyComponent.value.componentGradientAngle },${ diyComponent.value.componentStartBgColor },${ diyComponent.value.componentEndBgColor });`;
|
||||
else if (diyComponent.value.componentStartBgColor) style += 'background-color:' + diyComponent.value.componentStartBgColor + ';';
|
||||
|
||||
@ -61,7 +61,7 @@
|
||||
})
|
||||
|
||||
const warpCss = computed(() => {
|
||||
var style = '';
|
||||
let style = '';
|
||||
style += 'position:relative;';
|
||||
if(diyComponent.value.componentStartBgColor) {
|
||||
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${diyComponent.value.componentGradientAngle},${diyComponent.value.componentStartBgColor},${diyComponent.value.componentEndBgColor});`;
|
||||
|
||||
@ -158,7 +158,7 @@ const viewPrivacy = () => {
|
||||
}
|
||||
|
||||
const warpCss = computed(() => {
|
||||
var style = '';
|
||||
let style = '';
|
||||
style += 'position:relative;';
|
||||
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${ diyComponent.value.componentGradientAngle },${ diyComponent.value.componentStartBgColor },${ diyComponent.value.componentEndBgColor });`;
|
||||
else if (diyComponent.value.componentStartBgColor) style += 'background-color:' + diyComponent.value.componentStartBgColor + ';';
|
||||
|
||||
@ -207,7 +207,7 @@ onUnload(() => {
|
||||
})
|
||||
|
||||
const warpCss = computed(() => {
|
||||
var style = '';
|
||||
let style = '';
|
||||
style += 'position:relative;';
|
||||
if (diyComponent.value.componentStartBgColor) {
|
||||
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${ diyComponent.value.componentGradientAngle },${ diyComponent.value.componentStartBgColor },${ diyComponent.value.componentEndBgColor });`;
|
||||
|
||||
@ -128,7 +128,7 @@ const eventFn = (type: any) => {
|
||||
}
|
||||
|
||||
const warpCss = computed(() => {
|
||||
var style = '';
|
||||
let style = '';
|
||||
style += 'position:relative;';
|
||||
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${ diyComponent.value.componentGradientAngle },${ diyComponent.value.componentStartBgColor },${ diyComponent.value.componentEndBgColor });`;
|
||||
else if (diyComponent.value.componentStartBgColor) style += 'background-color:' + diyComponent.value.componentStartBgColor + ';';
|
||||
|
||||
@ -77,10 +77,8 @@
|
||||
import { img } from '@/utils/common';
|
||||
import useDiyStore from '@/app/stores/diy';
|
||||
import { useLocation } from '@/hooks/useLocation'
|
||||
import useSystemStore from '@/stores/system';
|
||||
|
||||
const errorInfo: any = ref(null);
|
||||
const systemStore = useSystemStore();
|
||||
const systemInfo = uni.getSystemInfoSync();
|
||||
const props = defineProps(['component', 'index', 'global']);
|
||||
const diyStore = useDiyStore();
|
||||
|
||||
@ -108,8 +106,8 @@
|
||||
/************** 定位-end ****************/
|
||||
|
||||
const warpCss = computed(() => {
|
||||
var style = '';
|
||||
style += 'position:relative;';
|
||||
let style = '';
|
||||
style += 'position:relative;';
|
||||
if (diyComponent.value.componentStartBgColor) {
|
||||
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${diyComponent.value.componentGradientAngle},${diyComponent.value.componentStartBgColor},${diyComponent.value.componentEndBgColor});`;
|
||||
else style += 'background-color:' + diyComponent.value.componentStartBgColor + ';';
|
||||
|
||||
@ -157,7 +157,7 @@ const viewPrivacy = () => {
|
||||
}
|
||||
|
||||
const warpCss = computed(() => {
|
||||
var style = '';
|
||||
let style = '';
|
||||
style += 'position:relative;';
|
||||
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${ diyComponent.value.componentGradientAngle },${ diyComponent.value.componentStartBgColor },${ diyComponent.value.componentEndBgColor });`;
|
||||
else if (diyComponent.value.componentStartBgColor) style += 'background-color:' + diyComponent.value.componentStartBgColor + ';';
|
||||
|
||||
@ -134,7 +134,7 @@ const eventFn = (type: any) => {
|
||||
}
|
||||
|
||||
const warpCss = computed(() => {
|
||||
var style = '';
|
||||
let style = '';
|
||||
style += 'position:relative;';
|
||||
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${ diyComponent.value.componentGradientAngle },${ diyComponent.value.componentStartBgColor },${ diyComponent.value.componentEndBgColor });`;
|
||||
else if (diyComponent.value.componentStartBgColor) style += 'background-color:' + diyComponent.value.componentStartBgColor + ';';
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
<view class="detail-two-content">
|
||||
<text class="detail-two-content-label">{{ diyComponent.field.name }}</text>
|
||||
<view class="flex items-center justify-end">
|
||||
<view class="detail-two-content-value w-[80%]" v-for="(item,index) in diyComponent.field.value" :key="index">{{ item.text }}</view>
|
||||
<view class="detail-two-content-value" v-for="(item,index) in diyComponent.field.value" :key="index">{{ item.text }}</view>
|
||||
<text v-if="!diyComponent.field.value || !diyComponent.field.value.length">{{ t('notHave') }}</text>
|
||||
<text v-if="diyComponent.isShowArrow" class="iconfont iconfanhui1 text-[#888] !text-[20rpx] ml-[10rpx]"></text>
|
||||
</view>
|
||||
@ -123,7 +123,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
// 表单 单选项组件
|
||||
import { ref, computed, watch, onMounted } from 'vue';
|
||||
import { ref, computed, watch, onMounted, nextTick } from 'vue';
|
||||
import { img } from '@/utils/common';
|
||||
import { t } from '@/locale'
|
||||
import useDiyStore from '@/app/stores/diy';
|
||||
@ -192,7 +192,7 @@ const eventFn = (type: any) => {
|
||||
}
|
||||
|
||||
const warpCss = computed(() => {
|
||||
var style = '';
|
||||
let style = '';
|
||||
style += 'position:relative;';
|
||||
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${ diyComponent.value.componentGradientAngle },${ diyComponent.value.componentStartBgColor },${ diyComponent.value.componentEndBgColor });`;
|
||||
else if (diyComponent.value.componentStartBgColor) style += 'background-color:' + diyComponent.value.componentStartBgColor + ';';
|
||||
@ -222,20 +222,27 @@ onMounted(() => {
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
// 样式三,初始化下拉数据
|
||||
if (diyComponent.value.style == 'style-3' && diyComponent.value.field.value.length > 0) {
|
||||
pullDownVal.value = diyComponent.value.field.value[0].id;
|
||||
}
|
||||
if (diyComponent.value.field.value.length > 0) {
|
||||
selectedRadioId.value = diyComponent.value.field.value[0].id;
|
||||
}else{
|
||||
watch(
|
||||
() => diyComponent.value.field.value,
|
||||
(newValue, oldValue) => {
|
||||
refresh();
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
const refresh = () => {
|
||||
// console.log('diyComponent.value.field.value',diyComponent.value.field.value)
|
||||
nextTick(() => {
|
||||
// 样式三,初始化下拉数据
|
||||
if (diyComponent.value.style == 'style-3' && diyComponent.value.field.value.length > 0) {
|
||||
pullDownVal.value = diyComponent.value.field.value[0].id;
|
||||
}
|
||||
if (diyComponent.value.field.value.length > 0) {
|
||||
selectedRadioId.value = diyComponent.value.field.value[0].id;
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const radioPlaceholder = computed(() => {
|
||||
@ -271,7 +278,7 @@ const pullDownCancelFn = () => {
|
||||
const pullDownConfirmFn = (item: any) => {
|
||||
selectShow.value = false;
|
||||
pullDownVal.value = item.id;
|
||||
;
|
||||
|
||||
diyComponent.value.field.value = [{ id: item.id, text: item.text }];
|
||||
// diyComponent.value.field.value = pullDownVal.value;
|
||||
}
|
||||
@ -311,7 +318,7 @@ const selectRadio = (item) => {
|
||||
return;
|
||||
}
|
||||
selectedRadioId.value = item.id;
|
||||
;
|
||||
|
||||
diyComponent.value.field.value = [{ id: item.id, text: item.text }];
|
||||
};
|
||||
|
||||
|
||||
@ -42,7 +42,7 @@ const diyGlobal = computed(() => {
|
||||
})
|
||||
|
||||
const warpCss = computed(() => {
|
||||
var style = '';
|
||||
let style = '';
|
||||
style += 'position:relative;';
|
||||
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${ diyComponent.value.componentGradientAngle },${ diyComponent.value.componentStartBgColor },${ diyComponent.value.componentEndBgColor });`;
|
||||
else if (diyComponent.value.componentStartBgColor) style += 'background-color:' + diyComponent.value.componentStartBgColor + ';';
|
||||
@ -60,10 +60,10 @@ const warpCss = computed(() => {
|
||||
|
||||
if (diyStore.mode != 'decorate' && diyComponent.value.btnPosition == 'hover_screen_bottom') {
|
||||
style += "position: fixed !important;";
|
||||
var height = tabbarInfo.value ? tabbarInfo.value.height : 0;
|
||||
const height = tabbarInfo.value ? tabbarInfo.value.height : 0;
|
||||
style += `left: 0;`;
|
||||
style += `right: 0;`;
|
||||
if (height && diyGlobal.value.bottomTabBarSwitch) {
|
||||
if (height && diyGlobal.value.bottomTabBar && diyGlobal.value.bottomTabBar.isShow) {
|
||||
style += `bottom: ${ height }px;`;
|
||||
} else {
|
||||
style += `bottom: 0;`;
|
||||
@ -79,7 +79,7 @@ const warpCss = computed(() => {
|
||||
if (diyComponent.value.margin.top > 0) {
|
||||
style += 'padding-top:' + diyComponent.value.margin.top * 2 + 'rpx' + ';';
|
||||
}
|
||||
if (height && diyGlobal.value.bottomTabBarSwitch) {
|
||||
if (height && diyGlobal.value.bottomTabBar && diyGlobal.value.bottomTabBar.isShow) {
|
||||
style += 'padding-bottom:' + diyComponent.value.margin.bottom * 2 + 'rpx' + ';';
|
||||
} else {
|
||||
style += `padding-bottom: ${ (diyComponent.value.margin.bottom + iphoneSecureVal.value) * 2 }rpx;`;
|
||||
@ -100,7 +100,7 @@ const warpCss = computed(() => {
|
||||
})
|
||||
|
||||
const boxCss = computed(() => {
|
||||
var style = '';
|
||||
let style = '';
|
||||
|
||||
if (diyComponent.value.btnPosition == 'hover_screen_bottom') {
|
||||
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${ diyComponent.value.componentGradientAngle },${ diyComponent.value.componentStartBgColor },${ diyComponent.value.componentEndBgColor });`;
|
||||
@ -112,7 +112,7 @@ const boxCss = computed(() => {
|
||||
|
||||
// 背景图加遮罩层
|
||||
const maskLayer = computed(() => {
|
||||
var style = '';
|
||||
let style = '';
|
||||
if (diyComponent.value.componentBgUrl) {
|
||||
style += 'position:absolute;top:0;right:0;left:0;bottom:0;';
|
||||
style += `background: rgba(0,0,0,${ diyComponent.value.componentBgAlpha / 10 });`;
|
||||
@ -128,7 +128,7 @@ const maskLayer = computed(() => {
|
||||
|
||||
// 重置样式
|
||||
const resetItem = computed(() => {
|
||||
var style = '';
|
||||
let style = '';
|
||||
style += `color: ${ diyComponent.value.resetBtn.color };`;
|
||||
style += `background-color: ${ diyComponent.value.resetBtn.bgColor };`;
|
||||
if (diyComponent.value.topElementRounded) style += 'border-top-left-radius:' + diyComponent.value.topElementRounded * 2 + 'rpx;';
|
||||
@ -140,7 +140,7 @@ const resetItem = computed(() => {
|
||||
|
||||
// 保存样式
|
||||
const submitItem = computed(() => {
|
||||
var style = '';
|
||||
let style = '';
|
||||
style += `color: ${ diyComponent.value.submitBtn.color };`;
|
||||
style += `background-color: ${ diyComponent.value.submitBtn.bgColor };`;
|
||||
if (diyComponent.value.topElementRounded) style += 'border-top-left-radius:' + diyComponent.value.topElementRounded * 2 + 'rpx;';
|
||||
|
||||
@ -1,5 +1,129 @@
|
||||
<template>
|
||||
<view :style="warpCss" class="form-item-frame">
|
||||
<view class="relative base-layout-one">
|
||||
<view class="p-[10rpx] flex items-center">
|
||||
<view class="w-[27%] mr-[10rpx] flex items-center">
|
||||
<text class="text-overflow-ellipsis"
|
||||
:style="{'color': diyComponent.textColor,'font-size': (diyComponent.fontSize * 2) + 'rpx' ,'font-weight': diyComponent.fontWeight}">{{ diyComponent.field.name }}</text>
|
||||
<text class="text-[#ec0003]">{{ diyComponent.field.required ? '*' : '' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view>
|
||||
<!-- <uni-table border>
|
||||
<uni-tr v-for="(column, columnIndex) in diyComponent.columnList" :key="columnIndex">
|
||||
<uni-th>{{ column.name }}</uni-th>
|
||||
<uni-td>
|
||||
<view class="layout-two-content"
|
||||
v-if="column.type === 'text' || column.type === 'number' || column.type === 'idcard'">
|
||||
<input :type="column.type" class="layout-one-content"
|
||||
placeholderClass="layout-one-input-placeholder"
|
||||
:placeholder-style="{'font-size': (diyComponent.fontSize * 2) + 'rpx' }"
|
||||
:style="{'color': diyComponent.textColor,'font-size': (diyComponent.fontSize * 2) + 'rpx'}"
|
||||
v-model="column.value" :disabled="isDisabled" />
|
||||
</view>
|
||||
<view class="layout-two-content"
|
||||
v-else-if="column.type === 'mobile'">
|
||||
<input type="tel" class="layout-one-content"
|
||||
placeholderClass="layout-one-input-placeholder"
|
||||
:placeholder-style="{'font-size': (diyComponent.fontSize * 2) + 'rpx' }"
|
||||
:style="{'color': diyComponent.textColor,'font-size': (diyComponent.fontSize * 2) + 'rpx'}"
|
||||
v-model="column.value" :disabled="isDisabled" />
|
||||
</view>
|
||||
<view v-else-if="column.type === 'gender'">
|
||||
<view @click="openSex(column)"
|
||||
class="px-[16rpx] box-border h-[80rpx] flex items-center justify-between border-solid border-[2rpx] border-[#e6e6e6] rounded-[10rpx] w-[100%]">
|
||||
<view>
|
||||
<text class="mr-[10rpx] text-[28rpx]"
|
||||
:style="{'color': diyComponent.textColor,'font-size': (diyComponent.fontSize * 2) + 'rpx'}">
|
||||
{{ getSexName(column) }}
|
||||
</text>
|
||||
</view>
|
||||
<text class="nc-iconfont nc-icon-xiaV6xx pull-down-arrow text-[#666]"
|
||||
:class="{'selected': selectShow[columnIndex]}"
|
||||
:style="{'font-size': (diyComponent.fontSize * 2 + 2) + 'rpx !important'}"></text>
|
||||
</view>
|
||||
</view>
|
||||
<view v-else-if="column.type === 'date'">
|
||||
<view class="layout-one-content" @click="openCalendar(column)">
|
||||
<view
|
||||
class="nc-iconfont nc-icon-a-riliV6xx-36 !text-[32rpx] text-[#999] mr-[16rpx]">
|
||||
</view>
|
||||
<view class="flex-1 text-overflow-ellipsis flex"
|
||||
:class="{'!text-[#999]' : !diyComponent.field.value.date && !diyComponent.defaultControl}"
|
||||
:style="{'color': diyComponent.textColor,'font-size': (diyComponent.fontSize * 2) + 'rpx' }">
|
||||
{{ startDate }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view v-else-if="column.type === 'address'">
|
||||
<view class="flex layout-one-content justify-between items-center">
|
||||
<input type="text" class="flex-1" :placeholder="inputPlaceholder(column)"
|
||||
placeholderClass="layout-one-input-placeholder"
|
||||
:placeholder-style="{'font-size': (diyComponent.fontSize * 2) + 'rpx' }"
|
||||
:style="{'color': diyComponent.textColor,'font-size': (diyComponent.fontSize * 2) + 'rpx'}"
|
||||
v-model="column.value" :disabled="isDisabled" @click="selectArea(column)" />
|
||||
<view class="text-[var(--primary-color)]" v-if="column.value" @click="column.value=''">
|
||||
清除
|
||||
</view>
|
||||
</view>
|
||||
<textarea v-if="column.addressFormat=='province/city/district/address'" type="textarea" class="layout-one-content mt-2 w-full" placeholderClass="layout-one-input-placeholder" :placeholder-style="{'font-size': (diyComponent.fontSize * 2) + 'rpx' }" placeholder="详细地址(如小区门牌号)" :disabled="isDisabled"></textarea>
|
||||
</view>
|
||||
|
||||
<!– 仅当字段类型为 'radio' 时才显示 –>
|
||||
<view class="layout-two-content" v-else-if="column.type === 'radio'">
|
||||
<view @click="openPicker(columnIndex)"
|
||||
class="px-[16rpx] box-border h-[80rpx] flex items-center justify-between border-solid border-[2rpx] border-[#e6e6e6] rounded-[10rpx] w-[100%]">
|
||||
<view v-if="column.value">
|
||||
<text class="mr-[10rpx] text-[28rpx]"
|
||||
:style="{'color': diyComponent.textColor,'font-size': (diyComponent.fontSize * 2) + 'rpx'}">
|
||||
{{ getSelectRadioName(columnIndex) }}
|
||||
</text>
|
||||
</view>
|
||||
<text v-else class="text-[28rpx] text-[#999]"
|
||||
:style="{'font-size': (diyComponent.fontSize * 2) + 'rpx'}">请选择</text>
|
||||
<text class="nc-iconfont nc-icon-xiaV6xx pull-down-arrow text-[#666]"
|
||||
:class="{'selected': selectShow[columnIndex]}"
|
||||
:style="{'font-size': (diyComponent.fontSize * 2 + 2) + 'rpx !important'}"></text>
|
||||
</view>
|
||||
</view>
|
||||
</uni-td>
|
||||
</uni-tr>
|
||||
</uni-table>-->
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 下拉弹窗 -->
|
||||
<u-popup v-for="(column, columnIndex) in diyComponent.columnList" :key="'popup-' + columnIndex"
|
||||
:show="selectShow[columnIndex]" mode="bottom" @close="selectShow[columnIndex] = false">
|
||||
<view class="p-[15rpx]">
|
||||
<scroll-view scroll-y="true" class="max-h-[450rpx] px-[14rpx] box-border">
|
||||
<u-radio-group v-model="column.value" placement="column" @change="groupChange(columnIndex)"
|
||||
iconPlacement="right">
|
||||
<view class="border-solid border-[0] border-b-[2rpx] border-[#e6e6e6] py-[20rpx]"
|
||||
v-for="(option, optionIndex) in column.options" :key="optionIndex"
|
||||
@click.stop="pullDownConfirmFn(columnIndex, option)">
|
||||
<u-radio activeColor="var(--primary-color)" :labelSize="(diyComponent.fontSize * 2) + 'rpx'"
|
||||
:labelColor="diyComponent.textColor" :style="{'width': '100%'}" :label="option.label"
|
||||
:name="option.id">
|
||||
</u-radio>
|
||||
</view>
|
||||
</u-radio-group>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</u-popup>
|
||||
|
||||
<!-- 修改性别 -->
|
||||
<u-action-sheet :actions="sexList" :show="sexSheetShow" :closeOnClickOverlay="true"
|
||||
:safeAreaInsetBottom="true" @close="sexSheetShow = false" @select="updateSex"></u-action-sheet>
|
||||
<area-select ref="areaRef" @complete="areaSelectComplete" :area-id="formData.district_id" />
|
||||
<view class="calendar-wrap">
|
||||
<u-calendar :show="calendarShow" mode="single" @confirm="confirm" @close="calendarShow=false"
|
||||
closeOnClickOverlay="true" :formatter="formatter" confirmDisabledText="禁止选择"
|
||||
color="var(--primary-color)" ref="calendar" :maxDate="maxDate"></u-calendar>
|
||||
<u-datetime-picker :show="show" mode="datetime" @cancel="show=false"
|
||||
closeOnClickOverlay="true" @confirm="calendarConfirm" @close="show=false"
|
||||
:minDate="minDate"></u-datetime-picker>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
@ -8,6 +132,10 @@
|
||||
import { ref, computed, watch, onMounted } from 'vue';
|
||||
import useDiyStore from '@/app/stores/diy';
|
||||
import { img, timeStampTurnTime, timeTurnTimeStamp } from '@/utils/common';
|
||||
// import uniTable from '@/addon/o2o/components/uni-table/components/uni-table/uni-table.vue'
|
||||
// import uniTr from '@/addon/o2o/components/uni-table/components/uni-tr/uni-tr.vue'
|
||||
// import uniTh from '@/addon/o2o/components/uni-table/components/uni-th/uni-th.vue'
|
||||
// import uniTd from '@/addon/o2o/components/uni-table/components/uni-td/uni-td.vue'
|
||||
const props = defineProps(['component', 'index', 'global']);
|
||||
const diyStore = useDiyStore();
|
||||
const formData: any = ref({
|
||||
@ -208,8 +336,8 @@
|
||||
|
||||
|
||||
const warpCss = computed(() => {
|
||||
var style = '';
|
||||
style += 'position:relative;';
|
||||
let style = '';
|
||||
style += 'position:relative;';
|
||||
if (diyComponent.value.componentStartBgColor) {
|
||||
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${diyComponent.value.componentGradientAngle},${diyComponent.value.componentStartBgColor},${diyComponent.value.componentEndBgColor});`;
|
||||
else style += 'background-color:' + diyComponent.value.componentStartBgColor + ';';
|
||||
|
||||
@ -136,7 +136,7 @@ const eventFn = (type: any) => {
|
||||
}
|
||||
|
||||
const warpCss = computed(() => {
|
||||
var style = '';
|
||||
let style = '';
|
||||
style += 'position:relative;';
|
||||
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${ diyComponent.value.componentGradientAngle },${ diyComponent.value.componentStartBgColor },${ diyComponent.value.componentEndBgColor });`;
|
||||
else if (diyComponent.value.componentStartBgColor) style += 'background-color:' + diyComponent.value.componentStartBgColor + ';';
|
||||
|
||||
@ -197,7 +197,7 @@ const endTime = computed(() => {
|
||||
})
|
||||
|
||||
const warpCss = computed(() => {
|
||||
var style = '';
|
||||
let style = '';
|
||||
style += 'position:relative;';
|
||||
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${ diyComponent.value.componentGradientAngle },${ diyComponent.value.componentStartBgColor },${ diyComponent.value.componentEndBgColor });`;
|
||||
else if (diyComponent.value.componentStartBgColor) style += 'background-color:' + diyComponent.value.componentStartBgColor + ';';
|
||||
|
||||
@ -117,7 +117,7 @@ const eventFn = (type: any) => {
|
||||
|
||||
// 开始时间
|
||||
const startTime = computed(() => {
|
||||
var time = '';
|
||||
let time = '';
|
||||
if (diyComponent.value.field.value) {
|
||||
time = diyComponent.value.field.value;
|
||||
diyComponent.value.field.value = time;
|
||||
@ -138,7 +138,7 @@ const startTime = computed(() => {
|
||||
})
|
||||
|
||||
const warpCss = computed(() => {
|
||||
var style = '';
|
||||
let style = '';
|
||||
style += 'position:relative;';
|
||||
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${ diyComponent.value.componentGradientAngle },${ diyComponent.value.componentStartBgColor },${ diyComponent.value.componentEndBgColor });`;
|
||||
else if (diyComponent.value.componentStartBgColor) style += 'background-color:' + diyComponent.value.componentStartBgColor + ';';
|
||||
|
||||
@ -191,8 +191,8 @@
|
||||
return props.global;
|
||||
})
|
||||
const warpCss = computed(() => {
|
||||
var style = '';
|
||||
style += 'position:relative;';
|
||||
let style = '';
|
||||
style += 'position:relative;';
|
||||
if (diyComponent.value.componentStartBgColor) {
|
||||
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${diyComponent.value.componentGradientAngle},${diyComponent.value.componentStartBgColor},${diyComponent.value.componentEndBgColor});`;
|
||||
else style += 'background-color:' + diyComponent.value.componentStartBgColor + ';';
|
||||
|
||||
@ -142,7 +142,7 @@
|
||||
|
||||
|
||||
const warpCss = computed(() => {
|
||||
var style = '';
|
||||
let style = '';
|
||||
style += 'position:relative;';
|
||||
if(diyComponent.value.componentStartBgColor) {
|
||||
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${diyComponent.value.componentGradientAngle},${diyComponent.value.componentStartBgColor},${diyComponent.value.componentEndBgColor});`;
|
||||
|
||||
@ -158,7 +158,7 @@ const shouldShowIndicator = computed(() => {
|
||||
return totalItems > itemsPerPage // 如果总项数大于每页显示的项数,显示指示器
|
||||
})
|
||||
const warpCss = computed(() => {
|
||||
var style = '';
|
||||
let style = '';
|
||||
style += 'position:relative;';
|
||||
if (diyComponent.value.componentStartBgColor) {
|
||||
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${ diyComponent.value.componentGradientAngle },${ diyComponent.value.componentStartBgColor },${ diyComponent.value.componentEndBgColor });`;
|
||||
@ -179,7 +179,7 @@ const warpCss = computed(() => {
|
||||
|
||||
// 背景图加遮罩层
|
||||
const maskLayer = computed(() => {
|
||||
var style = '';
|
||||
let style = '';
|
||||
if (diyComponent.value.componentBgUrl) {
|
||||
style += 'position:absolute;top:0;width:100%;';
|
||||
style += `background: rgba(0,0,0,${ diyComponent.value.componentBgAlpha / 10 });`;
|
||||
@ -217,7 +217,7 @@ const swiperHeight = ref(uni.getStorageSync(storageKey) || '');
|
||||
|
||||
const handleData = () => {
|
||||
if (diyComponent.value.layout == 'horizontal' && diyComponent.value.showStyle == 'pageSlide') {
|
||||
var height = 0;
|
||||
let height = 0;
|
||||
const query = uni.createSelectorQuery().in(instance);
|
||||
query.select('.graphic-nav-item').boundingClientRect((data: any) => {
|
||||
let len = 1;
|
||||
|
||||
@ -20,7 +20,7 @@ const diyComponent = computed(() => {
|
||||
})
|
||||
|
||||
const warpCss = computed(() => {
|
||||
var style = '';
|
||||
let style = '';
|
||||
style += 'height:' + diyComponent.value.height * 2 + 'rpx;';
|
||||
if (diyComponent.value.componentStartBgColor) {
|
||||
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${ diyComponent.value.componentGradientAngle },${ diyComponent.value.componentStartBgColor },${ diyComponent.value.componentEndBgColor });`;
|
||||
|
||||
@ -24,7 +24,7 @@ const diyComponent = computed(() => {
|
||||
})
|
||||
|
||||
const warpCss = computed(() => {
|
||||
var style = '';
|
||||
let style = '';
|
||||
style += 'border-top:' + (diyComponent.value.borderWidth * 2) + 'rpx ' + diyComponent.value.borderStyle + ' ' + diyComponent.value.borderColor + ';';
|
||||
return style;
|
||||
})
|
||||
|
||||
@ -31,7 +31,7 @@ const diyComponent = computed(() => {
|
||||
})
|
||||
|
||||
const warpCss = computed(() => {
|
||||
var style = '';
|
||||
let style = '';
|
||||
if (diyComponent.value.componentStartBgColor) {
|
||||
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${ diyComponent.value.componentGradientAngle },${ diyComponent.value.componentStartBgColor },${ diyComponent.value.componentEndBgColor });`;
|
||||
else style += 'background-color:' + diyComponent.value.componentStartBgColor + ';';
|
||||
@ -40,7 +40,7 @@ const warpCss = computed(() => {
|
||||
})
|
||||
|
||||
const itemCss = computed(() => {
|
||||
var style = '';
|
||||
let style = '';
|
||||
style += 'height:' + diyComponent.value.imgHeight + ';';
|
||||
if (diyComponent.value.topRounded) style += 'border-top-left-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
|
||||
if (diyComponent.value.topRounded) style += 'border-top-right-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<view :style="warpCss">
|
||||
<view class="diy-image-ads" :style="imageAdsTempStyle()">
|
||||
<view class="diy-image-ads" :style="imageAdsTempStyle">
|
||||
<view v-if="diyComponent.list.length == 1" class="leading-0 overflow-hidden" :style="swiperWarpCss">
|
||||
<view @click="diyStore.toRedirect(diyComponent.list[0].link)">
|
||||
<image v-if="diyComponent.list[0].imageUrl" :src="img(diyComponent.list[0].imageUrl)" :style="{height: imgHeight}" mode="heightFix" class="!w-full" :show-menu-by-longpress="true" />
|
||||
@ -24,12 +24,14 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
// 图片广告
|
||||
import { ref, computed, watch, onMounted } from 'vue';
|
||||
import { ref, computed, watch, onMounted, onBeforeUnmount } from 'vue';
|
||||
import useDiyStore from '@/app/stores/diy';
|
||||
import { img } from '@/utils/common';
|
||||
import useSystemStore from "@/stores/system";
|
||||
import { nextTick } from 'process';
|
||||
|
||||
const props = defineProps(['component', 'index']);
|
||||
const systemInfo = uni.getSystemInfoSync();
|
||||
const systemStore = useSystemStore()
|
||||
|
||||
const diyStore = useDiyStore();
|
||||
|
||||
@ -42,28 +44,23 @@ const diyComponent = computed(() => {
|
||||
})
|
||||
|
||||
// 兼容通屏样式
|
||||
const imageAdsTempStyle = () => {
|
||||
const imageAdsTempStyle = computed(() => {
|
||||
let style = "";
|
||||
if (diyComponent.value.isSameScreen && props.index == 0) {
|
||||
// #ifdef H5
|
||||
// h5,上移的像素,采取的是平均值
|
||||
if (systemInfo.platform === 'ios') {
|
||||
if (systemStore.systemInfo.platform === 'ios') {
|
||||
style = 'margin-top: -55px;';
|
||||
} else {
|
||||
style = 'margin-top: -44.5px;';
|
||||
}
|
||||
// #endif
|
||||
|
||||
// #ifdef MP
|
||||
// 图文导航开启沉浸式且导航栏开启时,导航栏不占位
|
||||
uni.setStorageSync('imageAdsSameScreen', true);
|
||||
// #endif
|
||||
}
|
||||
return style;
|
||||
}
|
||||
})
|
||||
|
||||
const warpCss = computed(() => {
|
||||
var style = '';
|
||||
let style = '';
|
||||
style += 'position:relative;';
|
||||
if (diyComponent.value.componentStartBgColor) {
|
||||
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${ diyComponent.value.componentGradientAngle },${ diyComponent.value.componentStartBgColor },${ diyComponent.value.componentEndBgColor });`;
|
||||
@ -78,7 +75,7 @@ const warpCss = computed(() => {
|
||||
})
|
||||
|
||||
const swiperWarpCss = computed(() => {
|
||||
var style = '';
|
||||
let style = '';
|
||||
if (diyComponent.value.topRounded) style += 'border-top-left-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
|
||||
if (diyComponent.value.topRounded) style += 'border-top-right-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
|
||||
if (diyComponent.value.bottomRounded) style += 'border-bottom-left-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
|
||||
@ -111,6 +108,10 @@ onMounted(() => {
|
||||
}
|
||||
});
|
||||
|
||||
onBeforeUnmount(()=>{
|
||||
diyStore.imageAdsSameScreen = false;
|
||||
})
|
||||
|
||||
const refresh = () => {
|
||||
// 装修模式下设置默认图
|
||||
if (diyStore.mode == 'decorate') {
|
||||
@ -121,7 +122,12 @@ const refresh = () => {
|
||||
}
|
||||
});
|
||||
} else {
|
||||
uni.removeStorageSync('imageAdsSameScreen');
|
||||
// #ifdef MP
|
||||
if (diyComponent.value.isSameScreen && props.index == 0) {
|
||||
// 图文导航开启沉浸式且导航栏开启时,导航栏不占位
|
||||
diyStore.imageAdsSameScreen = true;
|
||||
}
|
||||
// #endif
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -79,7 +79,7 @@ const diyComponent = computed(() => {
|
||||
}
|
||||
})
|
||||
const warpCss = computed(() => {
|
||||
var style = '';
|
||||
let style = '';
|
||||
if (diyComponent.value.componentStartBgColor) {
|
||||
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${ diyComponent.value.componentGradientAngle },${ diyComponent.value.componentStartBgColor },${ diyComponent.value.componentEndBgColor });`;
|
||||
else style += 'background-color:' + diyComponent.value.componentStartBgColor + ';';
|
||||
@ -194,11 +194,6 @@ const clickAvatar = () => {
|
||||
}
|
||||
// #endif
|
||||
}
|
||||
let menuButtonInfo: any = {};
|
||||
// 如果是小程序,获取右上角胶囊的尺寸信息,避免导航栏右侧内容与胶囊重叠(支付宝小程序非本API,尚未兼容)
|
||||
// #ifdef MP-WEIXIN || MP-BAIDU || MP-TOUTIAO || MP-QQ
|
||||
menuButtonInfo = uni.getMenuButtonBoundingClientRect();
|
||||
// #endif
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@ -123,7 +123,7 @@ const diyComponent = computed(() => {
|
||||
})
|
||||
|
||||
const warpCss = computed(() => {
|
||||
var style = '';
|
||||
let style = '';
|
||||
if (diyComponent.value.topRounded) style += 'border-top-left-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
|
||||
if (diyComponent.value.topRounded) style += 'border-top-right-radius:' + diyComponent.value.topRounded * 2 + 'rpx;';
|
||||
if (diyComponent.value.bottomRounded) style += 'border-bottom-left-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;';
|
||||
|
||||
@ -78,7 +78,7 @@ const diyComponent = computed(() => {
|
||||
})
|
||||
|
||||
const warpCss = computed(() => {
|
||||
var style = '';
|
||||
let style = '';
|
||||
style += 'position:relative;';
|
||||
if (diyComponent.value.componentStartBgColor) {
|
||||
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${ diyComponent.value.componentGradientAngle },${ diyComponent.value.componentStartBgColor },${ diyComponent.value.componentEndBgColor });`;
|
||||
@ -99,7 +99,7 @@ const warpCss = computed(() => {
|
||||
|
||||
// 背景图加遮罩层
|
||||
const maskLayer = computed(() => {
|
||||
var style = '';
|
||||
let style = '';
|
||||
if (diyComponent.value.componentBgUrl) {
|
||||
style += 'position:absolute;top:0;width:100%;';
|
||||
style += `background: rgba(0,0,0,${ diyComponent.value.componentBgAlpha / 10 });`;
|
||||
|
||||
@ -55,7 +55,7 @@ const diyComponent = computed(() => {
|
||||
})
|
||||
|
||||
const warpCss = computed(() => {
|
||||
var style = '';
|
||||
let style = '';
|
||||
if (diyComponent.value.componentStartBgColor) {
|
||||
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${ diyComponent.value.componentGradientAngle },${ diyComponent.value.componentStartBgColor },${ diyComponent.value.componentEndBgColor });`;
|
||||
else style += 'background-color:' + diyComponent.value.componentStartBgColor + ';';
|
||||
@ -68,7 +68,7 @@ const warpCss = computed(() => {
|
||||
})
|
||||
|
||||
const moduleOneCss = computed(() => {
|
||||
var style = '';
|
||||
let style = '';
|
||||
if (diyComponent.value.moduleOne.listFrame) {
|
||||
if (diyComponent.value.moduleOne.listFrame.startColor && diyComponent.value.moduleOne.listFrame.endColor) style += `background:linear-gradient(${ diyComponent.value.moduleOne.listFrame.startColor },${ diyComponent.value.moduleOne.listFrame.endColor });`;
|
||||
}
|
||||
@ -84,7 +84,7 @@ const moduleOneCss = computed(() => {
|
||||
})
|
||||
|
||||
const moduleTwoCss = computed(() => {
|
||||
var style = '';
|
||||
let style = '';
|
||||
if (diyComponent.value.moduleTwo.listFrame) {
|
||||
if (diyComponent.value.moduleTwo.listFrame.startColor && diyComponent.value.moduleTwo.listFrame.endColor) style += `background:linear-gradient(${ diyComponent.value.moduleTwo.listFrame.startColor },${ diyComponent.value.moduleTwo.listFrame.endColor });`;
|
||||
}
|
||||
@ -99,7 +99,7 @@ const moduleTwoCss = computed(() => {
|
||||
})
|
||||
|
||||
const moduleBtnCss = (data: any) => {
|
||||
var style = '';
|
||||
let style = '';
|
||||
if (data.btnTitle.color) {
|
||||
style += 'color:' + data.btnTitle.color + ';';
|
||||
}
|
||||
|
||||
@ -51,7 +51,7 @@ const diyComponent = computed(() => {
|
||||
})
|
||||
|
||||
const warpCss = computed(() => {
|
||||
var style = '';
|
||||
let style = '';
|
||||
style += 'position:relative;';
|
||||
if (diyComponent.value.componentStartBgColor) {
|
||||
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${ diyComponent.value.componentGradientAngle },${ diyComponent.value.componentStartBgColor },${ diyComponent.value.componentEndBgColor });`;
|
||||
@ -72,7 +72,7 @@ const warpCss = computed(() => {
|
||||
|
||||
// 背景图加遮罩层
|
||||
const maskLayer = computed(() => {
|
||||
var style = '';
|
||||
let style = '';
|
||||
if (diyComponent.value.componentBgUrl) {
|
||||
style += 'position:absolute;top:0;width:100%;';
|
||||
style += `background: rgba(0,0,0,${ diyComponent.value.componentBgAlpha / 10 });`;
|
||||
|
||||
@ -72,6 +72,7 @@
|
||||
import { ref, onMounted, computed, watch, nextTick, getCurrentInstance } from 'vue';
|
||||
import useDiyStore from '@/app/stores/diy';
|
||||
import { img } from '@/utils/common';
|
||||
import useSystemStore from "@/stores/system";
|
||||
|
||||
const props = defineProps(['component', 'index']);
|
||||
|
||||
@ -85,6 +86,9 @@ const diyComponent = computed(() => {
|
||||
}
|
||||
})
|
||||
|
||||
const systemStore = useSystemStore()
|
||||
systemStore.systemInfo = uni.getSystemInfoSync();
|
||||
|
||||
/**
|
||||
* 处理rpx渲染之后变成rem存在小数的问题
|
||||
* @param rpx
|
||||
@ -94,7 +98,7 @@ const upx2px = (rpx: number) => {
|
||||
}
|
||||
|
||||
const warpCss = computed(() => {
|
||||
var style = '';
|
||||
let style = '';
|
||||
style += 'position:relative;';
|
||||
if (diyComponent.value.componentStartBgColor) {
|
||||
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${ diyComponent.value.componentGradientAngle },${ diyComponent.value.componentStartBgColor },${ diyComponent.value.componentEndBgColor });`;
|
||||
@ -115,7 +119,7 @@ const warpCss = computed(() => {
|
||||
|
||||
// 背景图加遮罩层
|
||||
const maskLayer = computed(() => {
|
||||
var style = '';
|
||||
let style = '';
|
||||
if (diyComponent.value.componentBgUrl) {
|
||||
style += 'position:absolute;top:0;width:100%;';
|
||||
style += `background: rgba(0,0,0,${ diyComponent.value.componentBgAlpha / 10 });`;
|
||||
@ -131,11 +135,11 @@ const maskLayer = computed(() => {
|
||||
});
|
||||
|
||||
const countBorderRadius = (type: any, index: any) => {
|
||||
var obj = '';
|
||||
let obj = '';
|
||||
if (diyComponent.value.elementAngle == 'right') {
|
||||
return obj;
|
||||
}
|
||||
var defaultData: any = {
|
||||
const defaultData: any = {
|
||||
'row1-lt-of2-rt': [
|
||||
['border-top-right-radius', 'border-bottom-right-radius'],
|
||||
['border-top-left-radius', 'border-bottom-left-radius', 'border-bottom-right-radius'],
|
||||
@ -229,7 +233,7 @@ const refresh = () => {
|
||||
}
|
||||
|
||||
const handleData = () => {
|
||||
var singleRow: any = {
|
||||
const singleRow: any = {
|
||||
'row1-of2': {
|
||||
ratio: 2,
|
||||
width: 'calc((100% - ' + upx2px(diyComponent.value.imageGap * 2) + 'px) / 2)'
|
||||
@ -269,69 +273,60 @@ const handleData = () => {
|
||||
* 高度:宽度*比例,示例:187.5*0.46=86.25
|
||||
*/
|
||||
const calcSingleRow = (params: any) => {
|
||||
uni.getSystemInfo({
|
||||
success: res => {
|
||||
let maxHeight = 0;
|
||||
let maxHeight = 0;
|
||||
diyComponent.value.list.forEach((item: any, index: any) => {
|
||||
const ratio = item.imgHeight / item.imgWidth;
|
||||
|
||||
diyComponent.value.list.forEach((item: any, index: any) => {
|
||||
var ratio = item.imgHeight / item.imgWidth;
|
||||
|
||||
let width = res.windowWidth - upx2px(diyComponent.value.margin.both * 2); // 减去左右间距
|
||||
if (diyComponent.value.imageGap > 0) {
|
||||
width -= upx2px(params.ratio * diyComponent.value.imageGap * 2); // 减去间隙
|
||||
}
|
||||
item.imgWidth = width / params.ratio;
|
||||
item.imgHeight = item.imgWidth * ratio;
|
||||
|
||||
if (maxHeight == 0 || maxHeight < item.imgHeight) maxHeight = item.imgHeight;
|
||||
})
|
||||
|
||||
diyComponent.value.list.forEach((item: any, index: any) => {
|
||||
item.widthStyle = params.width;
|
||||
item.imgHeight = maxHeight;
|
||||
});
|
||||
let width = systemStore.systemInfo.windowWidth - upx2px(diyComponent.value.margin.both * 2); // 减去左右间距
|
||||
if (diyComponent.value.imageGap > 0) {
|
||||
width -= upx2px(params.ratio * diyComponent.value.imageGap * 2); // 减去间隙
|
||||
}
|
||||
item.imgWidth = width / params.ratio;
|
||||
item.imgHeight = item.imgWidth * ratio;
|
||||
|
||||
if (maxHeight == 0 || maxHeight < item.imgHeight) maxHeight = item.imgHeight;
|
||||
})
|
||||
|
||||
diyComponent.value.list.forEach((item: any, index: any) => {
|
||||
item.widthStyle = params.width;
|
||||
item.imgHeight = maxHeight;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 魔方:四方型,各占50%
|
||||
*/
|
||||
const calcFourSquare = () => {
|
||||
uni.getSystemInfo({
|
||||
success: res => {
|
||||
let maxHeightFirst = 0;
|
||||
let maxHeightTwo = 0;
|
||||
diyComponent.value.list.forEach((item: any, index: any) => {
|
||||
var ratio = item.imgHeight / item.imgWidth;
|
||||
item.imgWidth = res.windowWidth;
|
||||
item.imgWidth -= upx2px(diyComponent.value.margin.both * 4);
|
||||
if (diyComponent.value.imageGap > 0) {
|
||||
item.imgWidth -= upx2px(diyComponent.value.imageGap * 2);
|
||||
}
|
||||
item.imgWidth = item.imgWidth / 2;
|
||||
item.imgHeight = item.imgWidth * ratio;
|
||||
let maxHeightFirst = 0;
|
||||
let maxHeightTwo = 0;
|
||||
diyComponent.value.list.forEach((item: any, index: any) => {
|
||||
const ratio = item.imgHeight / item.imgWidth;
|
||||
item.imgWidth = systemStore.systemInfo.windowWidth;
|
||||
item.imgWidth -= upx2px(diyComponent.value.margin.both * 4);
|
||||
if (diyComponent.value.imageGap > 0) {
|
||||
item.imgWidth -= upx2px(diyComponent.value.imageGap * 2);
|
||||
}
|
||||
item.imgWidth = item.imgWidth / 2;
|
||||
item.imgHeight = item.imgWidth * ratio;
|
||||
|
||||
// 获取每行最大高度
|
||||
if (index <= 1) {
|
||||
if (maxHeightFirst == 0 || maxHeightFirst < item.imgHeight) {
|
||||
maxHeightFirst = item.imgHeight;
|
||||
}
|
||||
} else if (index > 1) {
|
||||
if (maxHeightTwo == 0 || maxHeightTwo < item.imgHeight) {
|
||||
maxHeightTwo = item.imgHeight;
|
||||
}
|
||||
}
|
||||
});
|
||||
diyComponent.value.list.forEach((item: any, index: any) => {
|
||||
item.imgWidth = 'calc((100% - ' + upx2px(diyComponent.value.imageGap * 2) + 'px) / 2)';
|
||||
item.widthStyle = item.imgWidth;
|
||||
if (index <= 1) {
|
||||
item.imgHeight = maxHeightFirst;
|
||||
} else if (index > 1) {
|
||||
item.imgHeight = maxHeightTwo;
|
||||
}
|
||||
});
|
||||
// 获取每行最大高度
|
||||
if (index <= 1) {
|
||||
if (maxHeightFirst == 0 || maxHeightFirst < item.imgHeight) {
|
||||
maxHeightFirst = item.imgHeight;
|
||||
}
|
||||
} else if (index > 1) {
|
||||
if (maxHeightTwo == 0 || maxHeightTwo < item.imgHeight) {
|
||||
maxHeightTwo = item.imgHeight;
|
||||
}
|
||||
}
|
||||
});
|
||||
diyComponent.value.list.forEach((item: any, index: any) => {
|
||||
item.imgWidth = 'calc((100% - ' + upx2px(diyComponent.value.imageGap * 2) + 'px) / 2)';
|
||||
item.widthStyle = item.imgWidth;
|
||||
if (index <= 1) {
|
||||
item.imgHeight = maxHeightFirst;
|
||||
} else if (index > 1) {
|
||||
item.imgHeight = maxHeightTwo;
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -343,21 +338,17 @@ const calcRowOneLeftOfTwoRight = () => {
|
||||
let rightHeight = 0; // 右侧两图平分高度
|
||||
let divide = 'left'; // 划分规则,left:左,right:右
|
||||
if (diyComponent.value.list[1].imgWidth === diyComponent.value.list[2].imgWidth) divide = 'right';
|
||||
uni.getSystemInfo({
|
||||
success: res => {
|
||||
diyComponent.value.list.forEach((item: any, index: any) => {
|
||||
if (index == 0) {
|
||||
var ratio = item.imgHeight / item.imgWidth; // 获取左图的尺寸比例
|
||||
item.imgWidth = res.windowWidth - upx2px(diyComponent.value.margin.both * 4) - upx2px(diyComponent.value.imageGap * 2);
|
||||
item.imgWidth = item.imgWidth / 2;
|
||||
item.imgHeight = item.imgWidth * ratio;
|
||||
rightHeight = (item.imgHeight - upx2px(diyComponent.value.imageGap * 2)) / 2;
|
||||
item.imgWidth += 'px';
|
||||
} else {
|
||||
item.imgWidth = diyComponent.value.list[0].imgWidth;
|
||||
item.imgHeight = rightHeight;
|
||||
}
|
||||
});
|
||||
diyComponent.value.list.forEach((item: any, index: any) => {
|
||||
if (index == 0) {
|
||||
const ratio = item.imgHeight / item.imgWidth; // 获取左图的尺寸比例
|
||||
item.imgWidth = systemStore.systemInfo.windowWidth - upx2px(diyComponent.value.margin.both * 4) - upx2px(diyComponent.value.imageGap * 2);
|
||||
item.imgWidth = item.imgWidth / 2;
|
||||
item.imgHeight = item.imgWidth * ratio;
|
||||
rightHeight = (item.imgHeight - upx2px(diyComponent.value.imageGap * 2)) / 2;
|
||||
item.imgWidth += 'px';
|
||||
} else {
|
||||
item.imgWidth = diyComponent.value.list[0].imgWidth;
|
||||
item.imgHeight = rightHeight;
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -366,31 +357,27 @@ const calcRowOneLeftOfTwoRight = () => {
|
||||
* 魔方:1上2下
|
||||
*/
|
||||
const calcRowOneTopOfTwoBottom = () => {
|
||||
var maxHeight = 0;
|
||||
uni.getSystemInfo({
|
||||
success: res => {
|
||||
diyComponent.value.list.forEach((item: any, index: any) => {
|
||||
let maxHeight = 0;
|
||||
diyComponent.value.list.forEach((item: any, index: any) => {
|
||||
|
||||
var ratio = item.imgHeight / item.imgWidth; // 获取左图的尺寸比例
|
||||
if (index == 0) {
|
||||
item.imgWidth = res.windowWidth - upx2px(diyComponent.value.margin.both * 4);
|
||||
} else if (index > 0) {
|
||||
item.imgWidth = res.windowWidth - upx2px(diyComponent.value.margin.both * 4) - upx2px(diyComponent.value.imageGap * 2);
|
||||
item.imgWidth = item.imgWidth / 2;
|
||||
}
|
||||
|
||||
item.imgHeight = item.imgWidth * ratio;
|
||||
|
||||
// 获取最大高度
|
||||
if (index > 0 && (maxHeight == 0 || maxHeight < item.imgHeight)) maxHeight = item.imgHeight;
|
||||
|
||||
});
|
||||
diyComponent.value.list.forEach((item: any, index: any) => {
|
||||
item.imgWidth += 'px';
|
||||
item.widthStyle = item.imgWidth;
|
||||
if (index > 0) item.imgHeight = maxHeight;
|
||||
});
|
||||
const ratio = item.imgHeight / item.imgWidth; // 获取左图的尺寸比例
|
||||
if (index == 0) {
|
||||
item.imgWidth = systemStore.systemInfo.windowWidth - upx2px(diyComponent.value.margin.both * 4);
|
||||
} else if (index > 0) {
|
||||
item.imgWidth = systemStore.systemInfo.windowWidth - upx2px(diyComponent.value.margin.both * 4) - upx2px(diyComponent.value.imageGap * 2);
|
||||
item.imgWidth = item.imgWidth / 2;
|
||||
}
|
||||
|
||||
item.imgHeight = item.imgWidth * ratio;
|
||||
|
||||
// 获取最大高度
|
||||
if (index > 0 && (maxHeight == 0 || maxHeight < item.imgHeight)) maxHeight = item.imgHeight;
|
||||
|
||||
});
|
||||
diyComponent.value.list.forEach((item: any, index: any) => {
|
||||
item.imgWidth += 'px';
|
||||
item.widthStyle = item.imgWidth;
|
||||
if (index > 0) item.imgHeight = maxHeight;
|
||||
});
|
||||
}
|
||||
|
||||
@ -398,29 +385,25 @@ const calcRowOneTopOfTwoBottom = () => {
|
||||
* 魔方:1左3右
|
||||
*/
|
||||
const calcRowOneLeftOfOneTopOfTwoBottom = () => {
|
||||
uni.getSystemInfo({
|
||||
success: res => {
|
||||
diyComponent.value.list.forEach((item: any, index: any) => {
|
||||
// 左图
|
||||
if (index == 0) {
|
||||
var ratio = item.imgHeight / item.imgWidth; // 获取左图的尺寸比例
|
||||
item.imgWidth = res.windowWidth - upx2px(diyComponent.value.margin.both * 4) - upx2px(diyComponent.value.imageGap * 2);
|
||||
item.imgWidth = item.imgWidth / 2;
|
||||
item.imgHeight = item.imgWidth * ratio;
|
||||
} else if (index == 1) {
|
||||
item.imgWidth = diyComponent.value.list[0].imgWidth;
|
||||
item.imgHeight = (diyComponent.value.list[0].imgHeight - upx2px(diyComponent.value.imageGap * 2)) / 2;
|
||||
} else if (index > 1) {
|
||||
item.imgWidth = (diyComponent.value.list[0].imgWidth - upx2px(diyComponent.value.imageGap * 2)) / 2;
|
||||
item.imgHeight = diyComponent.value.list[1].imgHeight;
|
||||
}
|
||||
});
|
||||
|
||||
diyComponent.value.list.forEach((item: any, index: any) => {
|
||||
item.imgWidth += 'px';
|
||||
});
|
||||
diyComponent.value.list.forEach((item: any, index: any) => {
|
||||
// 左图
|
||||
if (index == 0) {
|
||||
const ratio = item.imgHeight / item.imgWidth; // 获取左图的尺寸比例
|
||||
item.imgWidth = systemStore.systemInfo.windowWidth - upx2px(diyComponent.value.margin.both * 4) - upx2px(diyComponent.value.imageGap * 2);
|
||||
item.imgWidth = item.imgWidth / 2;
|
||||
item.imgHeight = item.imgWidth * ratio;
|
||||
} else if (index == 1) {
|
||||
item.imgWidth = diyComponent.value.list[0].imgWidth;
|
||||
item.imgHeight = (diyComponent.value.list[0].imgHeight - upx2px(diyComponent.value.imageGap * 2)) / 2;
|
||||
} else if (index > 1) {
|
||||
item.imgWidth = (diyComponent.value.list[0].imgWidth - upx2px(diyComponent.value.imageGap * 2)) / 2;
|
||||
item.imgHeight = diyComponent.value.list[1].imgHeight;
|
||||
}
|
||||
});
|
||||
|
||||
diyComponent.value.list.forEach((item: any, index: any) => {
|
||||
item.imgWidth += 'px';
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@ -61,7 +61,7 @@ const textStyle2 = computed(() => {
|
||||
});
|
||||
|
||||
const warpCss = computed(() => {
|
||||
var style = '';
|
||||
let style = '';
|
||||
style += 'position:relative;';
|
||||
if (diyComponent.value.componentStartBgColor) {
|
||||
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${ diyComponent.value.componentGradientAngle },${ diyComponent.value.componentStartBgColor },${ diyComponent.value.componentEndBgColor });`;
|
||||
@ -82,7 +82,7 @@ const warpCss = computed(() => {
|
||||
|
||||
// 背景图加遮罩层
|
||||
const maskLayer = computed(() => {
|
||||
var style = '';
|
||||
let style = '';
|
||||
if (diyComponent.value.componentBgUrl) {
|
||||
style += 'position:absolute;top:0;width:100%;';
|
||||
style += `background: rgba(0,0,0,${ diyComponent.value.componentBgAlpha / 10 });`;
|
||||
|
||||
@ -7,16 +7,16 @@
|
||||
<!-- #endif -->
|
||||
<view class="mx-[60rpx]">
|
||||
<view class="pt-[140rpx] text-[50rpx] text-[#333]">{{ t('bindMobile') }}</view>
|
||||
<view class="text-[26rpx] leading-[39rpx] text-[var(--text-color-light6)] mt-[16rpx] mb-[80rpx]">{{ t('bindMobileTip') }}</view>
|
||||
<view class="text-[26rpx] leading-[39rpx] text-[var(--text-color-light6)] mt-[24rpx] mb-[90rpx]">{{ t('bindMobileTip') }}</view>
|
||||
<u-form labelPosition="left" :model="formData" errorType='toast' :rules="rules" ref="formRef">
|
||||
<view
|
||||
class="h-[90rpx] flex w-full items-center px-[30rpx] rounded-[var(--goods-rounded-mid)] box-border bg-[#F6F6F6]">
|
||||
class="h-[90rpx] flex w-full items-center px-[30rpx] rounded-[40rpx] box-border bg-[#F6F6F6]">
|
||||
<u-form-item label="" prop="mobile" :border-bottom="false">
|
||||
<u-input v-model="formData.mobile" type="number" maxlength="11" border="none" :placeholder="t('mobilePlaceholder')" class="!bg-transparent" :disabled="real_name_input" fontSize="26rpx" placeholderClass="!text-[var(--text-color-light9)] text-[26rpx]" />
|
||||
</u-form-item>
|
||||
</view>
|
||||
<view
|
||||
class="h-[90rpx] flex w-full items-center px-[30rpx] rounded-[var(--goods-rounded-mid)] box-border bg-[#F6F6F6] mt-[40rpx]">
|
||||
class="h-[90rpx] flex w-full items-center px-[30rpx] rounded-[40rpx] box-border bg-[#F6F6F6] mt-[40rpx]">
|
||||
<u-form-item label="" prop="mobile_code" :border-bottom="false">
|
||||
<u-input v-model="formData.mobile_code" type="number" maxlength="4" border="none" :placeholder="t('codePlaceholder')" class="!bg-transparent" :disabled="real_name_input" fontSize="26rpx" placeholderClass="!text-[var(--text-color-light9)]">
|
||||
<template #suffix>
|
||||
@ -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">
|
||||
@ -55,18 +55,15 @@ import { useLogin } from '@/hooks/useLogin'
|
||||
import { redirect, pxToRpx } from '@/utils/common'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import { topTabar } from '@/utils/topTabbar'
|
||||
import useSystemStore from "@/stores/system";
|
||||
|
||||
let menuButtonInfo: any = {};
|
||||
// 如果是小程序,获取右上角胶囊的尺寸信息,避免导航栏右侧内容与胶囊重叠(支付宝小程序非本API,尚未兼容)
|
||||
// #ifdef MP-WEIXIN || MP-BAIDU || MP-TOUTIAO || MP-QQ
|
||||
menuButtonInfo = uni.getMenuButtonBoundingClientRect();
|
||||
// #endif
|
||||
const systemStore = useSystemStore()
|
||||
/********* 自定义头部 - start ***********/
|
||||
const topTabarObj = topTabar()
|
||||
let param = topTabarObj.setTopTabbarParam({ title: '', topStatusBar: { bgColor: '#fff', textColor: '#333' } })
|
||||
/********* 自定义头部 - end ***********/
|
||||
const headerHeight = computed(() => {
|
||||
return Object.keys(menuButtonInfo).length ? pxToRpx(Number(menuButtonInfo.height)) + pxToRpx(menuButtonInfo.top) + pxToRpx(8) + 'rpx' : 'auto'
|
||||
return Object.keys(systemStore.menuButtonInfo).length ? pxToRpx(Number(systemStore.menuButtonInfo.height)) + pxToRpx(systemStore.menuButtonInfo.top) + pxToRpx(8) + 'rpx' : 'auto'
|
||||
})
|
||||
|
||||
const memberStore = useMemberStore()
|
||||
@ -82,27 +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);
|
||||
|
||||
const wxPrivacyPopupRef: any = ref(null)
|
||||
|
||||
onLoad(() => {
|
||||
onLoad((data) => {
|
||||
// 防止浏览器自动填充
|
||||
setTimeout(() => {
|
||||
real_name_input.value = false;
|
||||
}, 800)
|
||||
// #ifdef MP
|
||||
nextTick(() => {
|
||||
if (wxPrivacyPopupRef.value) wxPrivacyPopupRef.value.proactive();
|
||||
})
|
||||
// #endif
|
||||
|
||||
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 = {
|
||||
|
||||
@ -16,9 +16,8 @@
|
||||
</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 -->
|
||||
<!-- 微信公众号快捷登录,开启自动注册的情况下才能使用 -->
|
||||
<view v-if="isWeixinBrowser() && loginConfig.is_auth_register" class="w-full flex items-center justify-center mb-[40rpx]">
|
||||
@ -77,9 +76,9 @@
|
||||
<view v-if="loginConfig.agreement_show" class="w-full flex items-center justify-center mt-[28rpx]">
|
||||
<view class="flex items-center justify-center mt-[28rpx] py-[14rpx] px-[50rpx]" @click.stop="agreeChange">
|
||||
<u-checkbox-group @change="agreeChange">
|
||||
<u-checkbox activeColor="var(--primary-color)" :checked="isAgree" shape="circle" size="30rpx" />
|
||||
<u-checkbox activeColor="var(--primary-color)" :checked="isAgree" shape="circle" size="24rpx" />
|
||||
</u-checkbox-group>
|
||||
<view class="text-[24rpx] text-[var(--text-color-light6)] flex items-center flex-wrap leading-[30rpx]">
|
||||
<view class="text-[24rpx] -ml-[4rpx] text-[var(--text-color-light6)] flex items-center flex-wrap leading-[30rpx]">
|
||||
<text>{{ t('agreeTips') }}</text>
|
||||
<text @click.stop="redirect({ url: '/app/pages/auth/agreement?key=privacy' })" class="text-primary">《{{ t('privacyAgreement') }}》</text>
|
||||
<text>{{ t('and') }}</text>
|
||||
@ -89,11 +88,10 @@
|
||||
</view>
|
||||
|
||||
<view class="footer w-full" v-if="loginConfig.is_mobile && loginConfig.is_username">
|
||||
<view class="text-[26rpx] leading-[36rpx] text-[333] text-center mb-[30rpx] font-400">{{ t('otherLogin') }}</view>
|
||||
<view class="text-[26rpx] leading-[36rpx] text-[#666] text-center mb-[30rpx] font-400">其他登录方式</view>
|
||||
<view class="flex justify-center">
|
||||
<view class="h-[80rpx] w-[80rpx] text-center leading-[78rpx] border-[2rpx] text-[#FF7100] rounded-[50%] border-solid border-[#ddd] nc-iconfont nc-icon-wodeV6mm3 text-[46rpx] overflow-hidden" @click="redirect({ url: '/app/pages/auth/login',param:{type:'username'}})"></view>
|
||||
<text @click="redirect({ url: '/app/pages/auth/login',param:{type:'username'}})" class="w-[66rpx] h-[66rpx] flex items-center justify-center iconfont iconmima6Vmm border-[2rpx] rounded-[50%] border-solid border-[#ddd] !text-[26rpx]"></text>
|
||||
</view>
|
||||
<view class="text-[24rpx] leading-[36rpx] text-[var(--text-color-light9)] text-center font-400 mt-[30rpx]">{{ t('accountLogin') }}</view>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
@ -139,17 +137,13 @@ import { onLoad, onShow } from '@dcloudio/uni-app'
|
||||
import { topTabar } from '@/utils/topTabbar'
|
||||
import useSystemStore from '@/stores/system'
|
||||
|
||||
let menuButtonInfo: any = {};
|
||||
// 如果是小程序,获取右上角胶囊的尺寸信息,避免导航栏右侧内容与胶囊重叠(支付宝小程序非本API,尚未兼容)
|
||||
// #ifdef MP-WEIXIN || MP-BAIDU || MP-TOUTIAO || MP-QQ
|
||||
menuButtonInfo = uni.getMenuButtonBoundingClientRect();
|
||||
// #endif
|
||||
const systemStore = useSystemStore()
|
||||
/********* 自定义头部 - start ***********/
|
||||
const topTabarObj = topTabar()
|
||||
let param = topTabarObj.setTopTabbarParam({ title: '', topStatusBar: { textColor: '#333' } })
|
||||
/********* 自定义头部 - end ***********/
|
||||
const headerHeight = computed(() => {
|
||||
return Object.keys(menuButtonInfo).length ? pxToRpx(Number(menuButtonInfo.height)) + pxToRpx(menuButtonInfo.top) + pxToRpx(8) + 'rpx' : 'auto'
|
||||
return Object.keys(systemStore.menuButtonInfo).length ? pxToRpx(Number(systemStore.menuButtonInfo.height)) + pxToRpx(systemStore.menuButtonInfo.top) + pxToRpx(8) + 'rpx' : 'auto'
|
||||
})
|
||||
const wapMemberMobile = ref('');
|
||||
const isAgree = ref(false)
|
||||
@ -164,7 +158,6 @@ const memberStore = useMemberStore()
|
||||
const memberInfo: any = computed(() => {
|
||||
return useMemberStore().info;
|
||||
})
|
||||
const systemStore = useSystemStore()
|
||||
const openType: any = computed(() => {
|
||||
if (!isAgree.value && configStore.login.agreement_show) return '';
|
||||
return 'getPhoneNumber';
|
||||
@ -230,9 +223,9 @@ onLoad(async() => {
|
||||
}, 100)
|
||||
return;
|
||||
}
|
||||
|
||||
wapMemberMobile.value = uni.getStorageSync('member_mobile_exist');
|
||||
|
||||
|
||||
wapMemberMobile.value = uni.getStorageSync('member_mobile_exist');
|
||||
|
||||
nextTick(() => {
|
||||
if (wxPrivacyPopupRef.value) wxPrivacyPopupRef.value.proactive();
|
||||
})
|
||||
@ -245,7 +238,7 @@ onShow(() => {
|
||||
})
|
||||
|
||||
const warpStyle = computed(() => {
|
||||
var style = '';
|
||||
let style = '';
|
||||
if (configStore.login.bg_url) {
|
||||
style += 'background-image:url(' + img(configStore.login.bg_url) + ');';
|
||||
style += 'background-size: 100%;';
|
||||
@ -274,11 +267,16 @@ const oneClickLogin = (callback: any = null, data: any = null) => {
|
||||
if (checkWxPrivacy()) return;
|
||||
|
||||
if (loginLoading.value) return
|
||||
uni.showLoading({
|
||||
title: '正在登录中',
|
||||
mask: true
|
||||
});
|
||||
loginLoading.value = true
|
||||
|
||||
|
||||
if (!callback) {
|
||||
callback = () => {
|
||||
loginLoading.value = false
|
||||
uni.hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
@ -308,16 +306,25 @@ const wechatLogin = () => {
|
||||
}
|
||||
if (loginConfig.wechat_error) {
|
||||
loginLoading.value = false
|
||||
uni.hideLoading();
|
||||
uni.showToast({ title: loginConfig.wechat_error, icon: 'none' })
|
||||
return;
|
||||
}
|
||||
|
||||
wapMemberMobile.value = uni.getStorageSync('member_mobile_exist');
|
||||
// wapMemberMobile.value = uni.getStorageSync('wap_member_mobile');
|
||||
// if (!wapMemberMobile.value) {
|
||||
// wapMemberMobile.value = uni.getStorageSync('wap_member_not_control_mobile'); // 老用户不控制强制绑定手机号
|
||||
// }
|
||||
let member_exist = uni.getStorageSync('member_exist')
|
||||
let member_exist = uni.getStorageSync('member_exist')
|
||||
|
||||
if (loginConfig.is_auth_register) {
|
||||
if(!wapMemberMobile.value && loginConfig.is_bind_mobile && !member_exist && loginConfig.is_force_access_user_info){
|
||||
// 先进行微信授权获取基础信息,然后绑定手机号
|
||||
if(uni.getStorageSync('openid') && uni.getStorageSync('nickname') && uni.getStorageSync('avatar')){
|
||||
bindMobileFn();
|
||||
uni.removeStorageSync('mandatory_Mobile')
|
||||
}else{
|
||||
uni.setStorageSync('mandatory_Mobile', true)
|
||||
login.getAuthCode({ scopes: 'snsapi_userinfo' })
|
||||
}
|
||||
}else
|
||||
// 开启强制绑定手机号,必须填写才能注册
|
||||
if (!wapMemberMobile.value && loginConfig.is_bind_mobile && !member_exist) {
|
||||
bindMobileFn();
|
||||
@ -329,13 +336,14 @@ const wechatLogin = () => {
|
||||
login.getAuthCode({ scopes: 'snsapi_base' }) // 静默获取
|
||||
}
|
||||
}else{
|
||||
if (!wapMemberMobile.value && loginConfig.is_bind_mobile &&!member_exist) {
|
||||
if (!wapMemberMobile.value && loginConfig.is_bind_mobile && !member_exist) {
|
||||
bindMobileFn();
|
||||
}else {
|
||||
login.getAuthCode({ scopes: 'snsapi_base' }) // 静默获取
|
||||
}
|
||||
}
|
||||
loginLoading.value = false
|
||||
uni.hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
@ -346,6 +354,7 @@ const weappLogin = (successCallback: any, data: any) => {
|
||||
if (loginConfig.is_auth_register && loginConfig.is_force_access_user_info && !member_exist) {
|
||||
infoFill.value.show = true
|
||||
loginLoading.value = false
|
||||
uni.hideLoading();
|
||||
} else {
|
||||
data = data || {};
|
||||
login.getAuthCode({ backFlag: true, successCallback, ...data })
|
||||
@ -365,6 +374,7 @@ const mobileAuth = (e: any) => {
|
||||
uni.setStorageSync('wap_member_mobile', memberInfo.value.mobile) // 存储会员手机号,防止重复请求微信获取手机号接口
|
||||
}
|
||||
loginLoading.value = false
|
||||
uni.hideLoading();
|
||||
}, { mobile_code: e.detail.code });
|
||||
}
|
||||
|
||||
@ -381,7 +391,10 @@ const mobileAuth = (e: any) => {
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.footer {
|
||||
margin-top: 200rpx;
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
padding-bottom: calc(151rpx + constant(safe-area-inset-bottom));
|
||||
padding-bottom: calc(151rpx + env(safe-area-inset-bottom));
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<view class="w-screen h-screen flex flex-col" :style="themeColor()" v-if="type">
|
||||
<view class="w-screen h-screen flex flex-col " :style="themeColor()" v-if="type">
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<view :style="{'height':headerHeight}">
|
||||
<top-tabbar :data="param" :scrollBool="topTabarObj.getScrollBool()" class="top-header" />
|
||||
@ -7,10 +7,13 @@
|
||||
<!-- #endif -->
|
||||
<view class="mx-[60rpx]">
|
||||
<view class="pt-[140rpx] text-[44rpx] font-500 text-[#333]">{{ type == 'username' ? t('accountLogin') : t('mobileLogin') }}</view>
|
||||
<view class="text-[26rpx] leading-[39rpx] text-[var(--text-color-light6)] mt-[16rpx] mb-[80rpx]">{{ type == 'username' ? t('accountLoginTip') : t('mobileLoginTip') }}</view>
|
||||
<view class="text-[26rpx] text-[#333] leading-[34rpx] mt-[24rpx] mb-[90rpx]" @click="redirect({ url: '/app/pages/auth/register',param:{type} })">
|
||||
<text>{{ t('noAccount') }},</text>
|
||||
<text class="text-primary">{{ t('toRegister') }}</text>
|
||||
</view>
|
||||
<u-form labelPosition="left" :model="formData" errorType='toast' :rules="rules" ref="formRef">
|
||||
<template v-if="type == 'username'">
|
||||
<view class="h-[88rpx] flex w-full items-center px-[30rpx] rounded-[var(--goods-rounded-mid)] box-border bg-[#F6F6F6]">
|
||||
<view class="h-[88rpx] flex w-full items-center px-[30rpx] rounded-[40rpx] box-border bg-[#F6F6F6]">
|
||||
<u-form-item label="" prop="username" :border-bottom="false">
|
||||
<u-input v-model="formData.username" border="none" maxlength="40"
|
||||
:placeholder="t('usernamePlaceholder')" autocomplete="off" class="!bg-transparent"
|
||||
@ -18,7 +21,7 @@
|
||||
placeholderClass="!text-[var(--text-color-light9)] text-[26rpx]" />
|
||||
</u-form-item>
|
||||
</view>
|
||||
<view class="h-[88rpx] flex w-full items-center px-[30rpx] rounded-[var(--goods-rounded-mid)] box-border bg-[#F6F6F6] mt-[40rpx]">
|
||||
<view class="h-[88rpx] flex w-full items-center px-[30rpx] rounded-[40rpx] box-border bg-[#F6F6F6] mt-[40rpx]">
|
||||
<u-form-item label="" prop="password" :border-bottom="false">
|
||||
<u-input v-model="formData.password" border="none" :password="isPassword" maxlength="40"
|
||||
:placeholder="t('passwordPlaceholder')" autocomplete="new-password"
|
||||
@ -34,7 +37,7 @@
|
||||
</view>
|
||||
</template>
|
||||
<template v-if="type == 'mobile'">
|
||||
<view class="h-[88rpx] flex w-full items-center px-[30rpx] rounded-[var(--goods-rounded-mid)] box-border bg-[#F6F6F6]">
|
||||
<view class="h-[88rpx] flex w-full items-center px-[30rpx] rounded-[40rpx] box-border bg-[#F6F6F6]">
|
||||
<u-form-item label="" prop="mobile" :border-bottom="false">
|
||||
<u-input v-model="formData.mobile" type="number" maxlength="11" border="none"
|
||||
:placeholder="t('mobilePlaceholder')" autocomplete="off" class="!bg-transparent"
|
||||
@ -42,7 +45,7 @@
|
||||
placeholderClass="!text-[var(--text-color-light9)] text-[26rpx]" />
|
||||
</u-form-item>
|
||||
</view>
|
||||
<view class="h-[88rpx] flex w-full items-center px-[30rpx] rounded-[var(--goods-rounded-mid)] box-border bg-[#F6F6F6] mt-[40rpx] text-[26rpx]">
|
||||
<view class="h-[88rpx] flex w-full items-center px-[30rpx] rounded-[40rpx] box-border bg-[#F6F6F6] mt-[40rpx]">
|
||||
<u-form-item label="" prop="mobile_code" :border-bottom="false">
|
||||
<u-input v-model="formData.mobile_code" type="number" maxlength="4" border="none"
|
||||
class="!bg-transparent" fontSize="26rpx" :disabled="real_name_input"
|
||||
@ -57,11 +60,12 @@
|
||||
</view>
|
||||
</template>
|
||||
</u-form>
|
||||
<view v-if="type == 'username'" class="text-right text-[24rpx] text-[var(--text-color-light9)] leading-[34rpx] mt-[20rpx]" @click="redirect({ url: '/app/pages/auth/resetpwd' })">{{ t('resetpwd') }}</view>
|
||||
<view :class="{'mt-[160rpx]':type != 'username','mt-[106rpx]':type == 'username'}">
|
||||
<view v-if="configStore.login.agreement_show" class="flex items-center mb-[20rpx] py-[14rpx]" @click.stop="agreeChange">
|
||||
<view class="h-[34rpx] text-right text-[24rpx] text-[var(--text-color-light6)] leading-[34rpx] mt-[20rpx] " @click="toResetpwd">{{ type == 'username' ? t('resetpwd') : '' }}</view>
|
||||
<view class="mt-[106rpx]">
|
||||
<button class="w-full h-[80rpx] !bg-[var(--primary-color)] text-[26rpx] rounded-[40rpx] leading-[80rpx] font-500 !text-[#fff] !mx-[0]" :loadingText="t('logining')" @click="handleLogin">{{ t('login') }}</button>
|
||||
<view v-if="configStore.login.agreement_show" class="flex items-center mt-[20rpx] py-[14rpx]" @click.stop="agreeChange">
|
||||
<u-checkbox-group @change="agreeChange">
|
||||
<u-checkbox activeColor="var(--primary-color)" :checked="isAgree" shape="circle" size="30rpx" />
|
||||
<u-checkbox activeColor="var(--primary-color)" :checked="isAgree" shape="circle" size="24rpx" />
|
||||
</u-checkbox-group>
|
||||
<view class="text-[24rpx] text-[var(--text-color-light6)] flex items-center flex-wrap leading-[30rpx]">
|
||||
<text>{{ t('agreeTips') }}</text>
|
||||
@ -70,14 +74,6 @@
|
||||
<text @click.stop="redirect({ url: '/app/pages/auth/agreement?key=service' })" class="text-primary">《{{ t('userAgreement') }}》</text>
|
||||
</view>
|
||||
</view>
|
||||
<button class="w-full h-[80rpx] !bg-[var(--primary-color)] text-[26rpx] rounded-[40rpx] leading-[80rpx] font-500 !text-[#fff] !mx-[0]" :loadingText="t('logining')" @click="handleLogin">{{ t('login') }}</button>
|
||||
<view class="flex items-center justify-between mt-[30rpx]">
|
||||
<view class="text-[26rpx] text-[var(--text-color-light6)] leading-[34rpx]" @click="setType" v-if="(type == 'username' && configStore.login.is_mobile) || (type == 'mobile' && configStore.login.is_username )">{{ type == 'username' ? t('mobileLogin') : t('accountLogin') }}</view>
|
||||
<view class="text-[26rpx] text-[#333] leading-[34rpx]" @click="redirect({ url: '/app/pages/auth/register',param:{type} })">
|
||||
<text>{{ t('noAccount') }},</text>
|
||||
<text class="text-primary">{{ t('toRegister') }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<uni-popup ref="popupRef" type="dialog">
|
||||
@ -99,10 +95,12 @@
|
||||
</view>
|
||||
</view>
|
||||
</uni-popup>
|
||||
<view class="footer w-full" v-if="isShowQuickLogin">
|
||||
<view class="text-[26rpx] leading-[36rpx] text-[#333] text-center mb-[30rpx] font-400">{{ t('oneClicklogin') }}</view>
|
||||
<view class="flex justify-center">
|
||||
<button class="h-[80rpx] w-[80rpx] text-[46rpx] !text-[#1AAB37] text-center !p-0 !bg-transparent leading-[79rpx] border-[2rpx] rounded-[50%] border-solid border-[#ddd] nc-iconfont nc-icon-weixinV6mm overflow-hidden" @click="toLink"></button>
|
||||
<view class="footer w-full" v-if="type == 'mobile' && configStore.login.is_username || type == 'username' && configStore.login.is_mobile || isShowQuickLogin">
|
||||
<view class="text-[26rpx] leading-[36rpx] text-[#666] text-center mb-[30rpx] font-400">其他登录方式</view>
|
||||
<view class="flex justify-center gap-[40rpx]">
|
||||
<text v-if="type == 'mobile' && configStore.login.is_username" @click="setType" class="w-[66rpx] h-[66rpx] flex items-center justify-center iconfont iconmima6Vmm border-[2rpx] rounded-[50%] border-solid border-[#ddd] !text-[26rpx]"></text>
|
||||
<text v-if="type == 'username' && configStore.login.is_mobile" @click="setType" class="w-[66rpx] h-[66rpx] flex items-center justify-center iconfont iconshouji6Vmm border-[2rpx] rounded-[50%] border-solid border-[#ddd] !text-[26rpx]"></text>
|
||||
<text v-if="isShowQuickLogin" @click="toLink" class="w-[66rpx] h-[66rpx] !text-[#1AAB37] flex items-center justify-center iconfont iconweixinV6mm1 border-[2rpx] rounded-[50%] border-solid border-[#ddd] !text-[26rpx]"></text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@ -117,18 +115,15 @@ import { t } from '@/locale'
|
||||
import { redirect, getToken, pxToRpx, isWeixinBrowser } from '@/utils/common'
|
||||
import { onLoad } from '@dcloudio/uni-app';
|
||||
import { topTabar } from '@/utils/topTabbar'
|
||||
import useSystemStore from "@/stores/system";
|
||||
|
||||
let menuButtonInfo: any = {};
|
||||
// 如果是小程序,获取右上角胶囊的尺寸信息,避免导航栏右侧内容与胶囊重叠(支付宝小程序非本API,尚未兼容)
|
||||
// #ifdef MP-WEIXIN || MP-BAIDU || MP-TOUTIAO || MP-QQ
|
||||
menuButtonInfo = uni.getMenuButtonBoundingClientRect();
|
||||
// #endif
|
||||
const systemStore = useSystemStore()
|
||||
/********* 自定义头部 - start ***********/
|
||||
const topTabarObj = topTabar()
|
||||
const param = topTabarObj.setTopTabbarParam({ title: '', topStatusBar: { bgColor: '#fff', textColor: '#333' } })
|
||||
/********* 自定义头部 - end ***********/
|
||||
const headerHeight = computed(() => {
|
||||
return Object.keys(menuButtonInfo).length ? pxToRpx(Number(menuButtonInfo.height)) + pxToRpx(menuButtonInfo.top) + pxToRpx(8) + 'rpx' : 'auto'
|
||||
return Object.keys(systemStore.menuButtonInfo).length ? pxToRpx(Number(systemStore.menuButtonInfo.height)) + pxToRpx(systemStore.menuButtonInfo.top) + pxToRpx(8) + 'rpx' : 'auto'
|
||||
})
|
||||
const real_name_input = ref(true);
|
||||
const memberStore = useMemberStore()
|
||||
@ -161,6 +156,14 @@ onLoad(async(option: any) => {
|
||||
redirect({ url: '/app/pages/index/index', mode: 'reLaunch' })
|
||||
}, 100)
|
||||
}
|
||||
// #ifdef H5
|
||||
uni.getStorageSync('openid') && (Object.assign(formData, { wx_openid: uni.getStorageSync('openid') }))
|
||||
// #endif
|
||||
|
||||
// #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) {
|
||||
@ -282,7 +285,7 @@ const handleLogin = () => {
|
||||
loading.value = true
|
||||
|
||||
const login = type.value == 'username' ? usernameLogin : mobileLogin
|
||||
|
||||
|
||||
login(formData).then((res: any) => {
|
||||
memberStore.setToken(res.data.token)
|
||||
// todo 已注册的会员不受影响
|
||||
@ -311,7 +314,12 @@ const toLink = () => {
|
||||
} else {
|
||||
redirect({ url: '/app/pages/auth/index', mode: 'redirectTo' })
|
||||
}
|
||||
}
|
||||
|
||||
const toResetpwd = () =>{
|
||||
if(type.value == 'username'){
|
||||
redirect({ url: '/app/pages/auth/resetpwd' })
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@ -332,7 +340,12 @@ const toLink = () => {
|
||||
}
|
||||
|
||||
.footer {
|
||||
// position: absolute;
|
||||
// position: fixed;
|
||||
margin-top: 200rpx;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
padding-bottom: calc(151rpx + constant(safe-area-inset-bottom));
|
||||
padding-bottom: calc(151rpx + env(safe-area-inset-bottom));
|
||||
}
|
||||
|
||||
@ -7,10 +7,15 @@
|
||||
<!-- #endif -->
|
||||
<view class="mx-[60rpx]">
|
||||
<view class="pt-[140rpx] text-[44rpx] font-500 text-[#333]">{{ type == 'username' ? t('usernameRegister') : t('mobileRegister') }}</view>
|
||||
<view class="text-[26rpx] leading-[39rpx] text-[var(--text-color-light6)] mt-[16rpx] mb-[80rpx]">{{ type == 'username' ? t('usernameRegisterTip') : t('mobileRegisterTip') }}</view>
|
||||
<view class="flex items-center justify-between mt-[24rpx] mb-[90rpx]">
|
||||
<view class="text-[26rpx] text-[#333] leading-[34rpx]" @click="toLink">
|
||||
<text>{{ t('haveAccount') }},</text>
|
||||
<text class="text-primary">{{ t('toLogin') }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<u-form labelPosition="left" :model="formData" errorType='toast' :rules="rules" ref="formRef">
|
||||
<template v-if="type == 'username'">
|
||||
<view class="h-[88rpx] flex w-full items-center px-[30rpx] rounded-[var(--goods-rounded-mid)] box-border bg-[#F6F6F6]">
|
||||
<view class="h-[88rpx] flex w-full items-center px-[30rpx] rounded-[40rpx] box-border bg-[#F6F6F6]">
|
||||
<u-form-item label="" prop="username" :border-bottom="false">
|
||||
<u-input v-model="formData.username" border="none" maxlength="40"
|
||||
:placeholder="t('usernamePlaceholder')" class="!bg-transparent"
|
||||
@ -18,7 +23,7 @@
|
||||
placeholderClass="!text-[var(--text-color-light9)] text-[26rpx]" />
|
||||
</u-form-item>
|
||||
</view>
|
||||
<view class="h-[88rpx] flex w-full items-center px-[30rpx] rounded-[var(--goods-rounded-mid)] box-border bg-[#F6F6F6] mt-[40rpx]">
|
||||
<view class="h-[88rpx] flex w-full items-center px-[30rpx] rounded-[40rpx] box-border bg-[#F6F6F6] mt-[40rpx]">
|
||||
<u-form-item label="" prop="password" :border-bottom="false">
|
||||
<u-input v-model="formData.password" border="none" :password="isPassword" maxlength="40"
|
||||
:placeholder="t('passwordPlaceholder')" class="!bg-transparent"
|
||||
@ -32,7 +37,7 @@
|
||||
</u-input>
|
||||
</u-form-item>
|
||||
</view>
|
||||
<view class="h-[88rpx] flex w-full items-center px-[30rpx] rounded-[var(--goods-rounded-mid)] box-border bg-[#F6F6F6] mt-[40rpx]">
|
||||
<view class="h-[88rpx] flex w-full items-center px-[30rpx] rounded-[40rpx] box-border bg-[#F6F6F6] mt-[40rpx]">
|
||||
<u-form-item label="" prop="confirm_password" :border-bottom="false">
|
||||
<u-input v-model="formData.confirm_password" border="none" :password="isConfirmPassword"
|
||||
maxlength="40" :placeholder="t('confirmPasswordPlaceholder')"
|
||||
@ -48,7 +53,7 @@
|
||||
</view>
|
||||
</template>
|
||||
<template v-if="type == 'mobile' || configStore.login.is_bind_mobile">
|
||||
<view class="h-[88rpx] flex w-full items-center px-[30rpx] rounded-[var(--goods-rounded-mid)] box-border bg-[#F6F6F6] mt-[40rpx]">
|
||||
<view class="h-[88rpx] flex w-full items-center px-[30rpx] rounded-[40rpx] box-border bg-[#F6F6F6] mt-[40rpx]">
|
||||
<u-form-item label="" prop="mobile" :border-bottom="false">
|
||||
<u-input v-model="formData.mobile" border="none" maxlength="11"
|
||||
:placeholder="t('mobilePlaceholder')" class="!bg-transparent"
|
||||
@ -56,7 +61,7 @@
|
||||
placeholderClass="!text-[var(--text-color-light9)] text-[26rpx]" />
|
||||
</u-form-item>
|
||||
</view>
|
||||
<view class="h-[88rpx] flex w-full items-center px-[30rpx] rounded-[var(--goods-rounded-mid)] box-border bg-[#F6F6F6] mt-[40rpx]">
|
||||
<view class="h-[88rpx] flex w-full items-center px-[30rpx] rounded-[40rpx] box-border bg-[#F6F6F6] mt-[40rpx]">
|
||||
<u-form-item label="" prop="mobile_code" :border-bottom="false">
|
||||
<u-input v-model="formData.mobile_code" border="none" maxlength="4"
|
||||
:placeholder="t('codePlaceholder')" class="!bg-transparent"
|
||||
@ -71,7 +76,7 @@
|
||||
</view>
|
||||
</template>
|
||||
<template v-if="type == 'username'">
|
||||
<view class="h-[88rpx] flex w-full items-center px-[30rpx] rounded-[var(--goods-rounded-mid)] box-border bg-[#F6F6F6] mt-[40rpx]">
|
||||
<view class="h-[88rpx] flex w-full items-center px-[30rpx] rounded-[40rpx] box-border bg-[#F6F6F6] mt-[40rpx]">
|
||||
<u-form-item label="" prop="captcha_code" :border-bottom="false">
|
||||
<u-input v-model="formData.captcha_code" border="none"
|
||||
:placeholder="t('captchaPlaceholder')" class="!bg-transparent"
|
||||
@ -85,25 +90,25 @@
|
||||
</view>
|
||||
</template>
|
||||
</u-form>
|
||||
<view class="mt-[160rpx]">
|
||||
<view v-if="configStore.login.agreement_show" class="flex items-center mb-[20rpx] py-[14rpx]" @click.stop="agreeChange">
|
||||
<view class="mt-[140rpx]">
|
||||
<button class="w-full h-[80rpx] !bg-[var(--primary-color)] text-[26rpx] rounded-[40rpx] leading-[80rpx] font-500 !text-[#fff]" @click="handleRegister">{{ t('register') }}</button>
|
||||
<view v-if="configStore.login.agreement_show" class="flex items-center mt-[10rpx] mb-[40rpx] py-[14rpx]" @click.stop="agreeChange">
|
||||
<u-checkbox-group @change="agreeChange">
|
||||
<u-checkbox activeColor="var(--primary-color)" :checked="isAgree" shape="circle" size="30rpx" />
|
||||
<u-checkbox activeColor="var(--primary-color)" :checked="isAgree" shape="circle" size="24rpx" />
|
||||
</u-checkbox-group>
|
||||
<view class="text-[24rpx] text-[var(--text-color-light6)] flex items-center flex-wrap leading-[30rpx]">
|
||||
<view class="text-[24rpx] -ml-[4rpx] text-[var(--text-color-light6)] flex items-center flex-wrap leading-[30rpx]">
|
||||
<text>{{ t('agreeTips') }}</text>
|
||||
<text @click.stop="redirect({ url: '/app/pages/auth/agreement?key=privacy' })" class="text-primary">《{{ t('privacyAgreement') }}》</text>
|
||||
<text>{{ t('and') }}</text>
|
||||
<text @click.stop="redirect({ url: '/app/pages/auth/agreement?key=service' })" class="text-primary">《{{ t('userAgreement') }}》</text>
|
||||
</view>
|
||||
</view>
|
||||
<button class="w-full h-[80rpx] !bg-[var(--primary-color)] text-[26rpx] rounded-[40rpx] leading-[80rpx] font-500 !text-[#fff]" @click="handleRegister">{{ t('register') }}</button>
|
||||
<view class="flex items-center justify-between mt-[30rpx]">
|
||||
<view class="text-[26rpx] text-[var(--text-color-light6)] leading-[34rpx]" v-if="registerType.length > 1" @click="type = type == 'username' ? 'mobile' : 'username' ">{{ type == 'username' ? t('mobileRegister') : t('usernameRegister') }}</view>
|
||||
<view class="text-[26rpx] text-[#333] leading-[34rpx]" @click="toLink">
|
||||
<text>{{ t('haveAccount') }},</text>
|
||||
<text class="text-primary">{{ t('toLogin') }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="footer w-full" v-if="registerType.length > 1" :class="{'fixed bottom-0 left-0 right-0': type == 'mobile', 'pt-[60rpx]': type == 'username'}">
|
||||
<view class="text-[26rpx] leading-[36rpx] text-[#666] text-center mb-[30rpx] font-400">其他注册方式</view>
|
||||
<view class="flex justify-center gap-[40rpx]">
|
||||
<text v-if="type == 'mobile' && configStore.login.is_mobile" @click="type = 'username'" class="w-[66rpx] h-[66rpx] flex items-center justify-center iconfont iconmima6Vmm border-[2rpx] rounded-[50%] border-solid border-[#ddd] !text-[26rpx]"></text>
|
||||
<text v-if="type == 'username' && configStore.login.is_username" @click="type = 'mobile'" class="w-[66rpx] h-[66rpx] flex items-center justify-center iconfont iconshouji6Vmm border-[2rpx] rounded-[50%] border-solid border-[#ddd] !text-[26rpx]"></text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@ -140,18 +145,15 @@ import { t } from '@/locale'
|
||||
import { redirect, getToken, pxToRpx } from '@/utils/common'
|
||||
import { onLoad } from '@dcloudio/uni-app';
|
||||
import { topTabar } from '@/utils/topTabbar'
|
||||
import useSystemStore from "@/stores/system";
|
||||
|
||||
let menuButtonInfo: any = {};
|
||||
// 如果是小程序,获取右上角胶囊的尺寸信息,避免导航栏右侧内容与胶囊重叠(支付宝小程序非本API,尚未兼容)
|
||||
// #ifdef MP-WEIXIN || MP-BAIDU || MP-TOUTIAO || MP-QQ
|
||||
menuButtonInfo = uni.getMenuButtonBoundingClientRect();
|
||||
// #endif
|
||||
const systemStore = useSystemStore()
|
||||
/********* 自定义头部 - start ***********/
|
||||
const topTabarObj = topTabar()
|
||||
let param = topTabarObj.setTopTabbarParam({ title: '', topStatusBar: { bgColor: '#fff', textColor: '#333' } })
|
||||
/********* 自定义头部 - end ***********/
|
||||
const headerHeight = computed(() => {
|
||||
return Object.keys(menuButtonInfo).length ? pxToRpx(Number(menuButtonInfo.height)) + pxToRpx(menuButtonInfo.top) + pxToRpx(8) + 'rpx' : 'auto'
|
||||
return Object.keys(systemStore.menuButtonInfo).length ? pxToRpx(Number(systemStore.menuButtonInfo.height)) + pxToRpx(systemStore.menuButtonInfo.top) + pxToRpx(8) + 'rpx' : 'auto'
|
||||
})
|
||||
const formData = reactive({
|
||||
username: '',
|
||||
@ -371,4 +373,8 @@ const toLink = () => {
|
||||
:deep(.u-checkbox) {
|
||||
margin: 0 !important;
|
||||
}
|
||||
.footer {
|
||||
padding-bottom: calc(151rpx + constant(safe-area-inset-bottom));
|
||||
padding-bottom: calc(151rpx + env(safe-area-inset-bottom));
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -7,9 +7,9 @@
|
||||
<!-- #endif -->
|
||||
<view class="mx-[60rpx]">
|
||||
<view class="pt-[140rpx] text-[44rpx] font-500 text-[#333]">{{ t('findPassword') }}</view>
|
||||
<view class="text-[26rpx] leading-[39rpx] text-[var(--text-color-light6)] mt-[16rpx] mb-[80rpx]">{{ t('findPasswordTip') }}</view>
|
||||
<view class="text-[26rpx] leading-[39rpx] text-[var(--text-color-light6)] mt-[24rpx] mb-[90rpx]">{{ t('findPasswordTip') }}</view>
|
||||
<u-form labelPosition="left" :model="formData" errorType='toast' :rules="rules" ref="formRef">
|
||||
<view class="h-[88rpx] flex w-full items-center px-[30rpx] rounded-[var(--goods-rounded-mid)] box-border bg-[#F6F6F6]">
|
||||
<view class="h-[88rpx] flex w-full items-center px-[30rpx] rounded-[40rpx] box-border bg-[#F6F6F6]">
|
||||
<u-form-item label="" prop="mobile" :border-bottom="false">
|
||||
<u-input v-model="formData.mobile" type="number" maxlength="11" border="none"
|
||||
:placeholder="t('mobilePlaceholder')" class="!bg-transparent"
|
||||
@ -17,7 +17,7 @@
|
||||
placeholderClass="!text-[var(--text-color-light9)] text-[26rpx]" />
|
||||
</u-form-item>
|
||||
</view>
|
||||
<view class="h-[88rpx] flex w-full items-center px-[30rpx] rounded-[var(--goods-rounded-mid)] box-border bg-[#F6F6F6] mt-[40rpx]">
|
||||
<view class="h-[88rpx] flex w-full items-center px-[30rpx] rounded-[40rpx] box-border bg-[#F6F6F6] mt-[40rpx]">
|
||||
<u-form-item label="" prop="code" :border-bottom="false">
|
||||
<u-input v-model="formData.mobile_code" type="number" maxlength="4" border="none"
|
||||
:placeholder="t('codePlaceholder')" class="!bg-transparent" :disabled="real_name_input"
|
||||
@ -28,7 +28,7 @@
|
||||
</u-input>
|
||||
</u-form-item>
|
||||
</view>
|
||||
<view class="h-[88rpx] flex w-full items-center px-[30rpx] rounded-[var(--goods-rounded-mid)] box-border bg-[#F6F6F6] mt-[40rpx]">
|
||||
<view class="h-[88rpx] flex w-full items-center px-[30rpx] rounded-[40rpx] box-border bg-[#F6F6F6] mt-[40rpx]">
|
||||
<u-form-item label="" prop="password" :border-bottom="false">
|
||||
<u-input v-model="formData.password" border="none" :password="isPassword" maxlength="40"
|
||||
:placeholder="t('passwordPlaceholder')" class="!bg-transparent"
|
||||
@ -42,7 +42,7 @@
|
||||
</u-input>
|
||||
</u-form-item>
|
||||
</view>
|
||||
<view class="h-[88rpx] flex w-full items-center px-[30rpx] rounded-[var(--goods-rounded-mid)] box-border bg-[#F6F6F6] mt-[40rpx]">
|
||||
<view class="h-[88rpx] flex w-full items-center px-[30rpx] rounded-[40rpx] box-border bg-[#F6F6F6] mt-[40rpx]">
|
||||
<u-form-item label="" prop="confirm_password" :border-bottom="false">
|
||||
<u-input v-model="formData.confirm_password" border="none" :password="isConfirmPassword"
|
||||
maxlength="40" :placeholder="t('confirmPasswordPlaceholder')" class="!bg-transparent"
|
||||
@ -70,18 +70,15 @@ import { t } from '@/locale'
|
||||
import { resetPassword } from '@/app/api/system'
|
||||
import { redirect, pxToRpx } from '@/utils/common'
|
||||
import { topTabar } from '@/utils/topTabbar'
|
||||
import useSystemStore from "@/stores/system";
|
||||
|
||||
let menuButtonInfo: any = {};
|
||||
// 如果是小程序,获取右上角胶囊的尺寸信息,避免导航栏右侧内容与胶囊重叠(支付宝小程序非本API,尚未兼容)
|
||||
// #ifdef MP-WEIXIN || MP-BAIDU || MP-TOUTIAO || MP-QQ
|
||||
menuButtonInfo = uni.getMenuButtonBoundingClientRect();
|
||||
// #endif
|
||||
const systemStore = useSystemStore()
|
||||
/********* 自定义头部 - start ***********/
|
||||
const topTabarObj = topTabar()
|
||||
let param = topTabarObj.setTopTabbarParam({ title: '', topStatusBar: { bgColor: '#fff', textColor: '#333' } })
|
||||
/********* 自定义头部 - end ***********/
|
||||
const headerHeight = computed(() => {
|
||||
return Object.keys(menuButtonInfo).length ? pxToRpx(Number(menuButtonInfo.height)) + pxToRpx(menuButtonInfo.top) + pxToRpx(8) + 'rpx' : 'auto'
|
||||
return Object.keys(systemStore.menuButtonInfo).length ? pxToRpx(Number(systemStore.menuButtonInfo.height)) + pxToRpx(systemStore.menuButtonInfo.top) + pxToRpx(8) + 'rpx' : 'auto'
|
||||
})
|
||||
const formData = reactive({
|
||||
mobile: '',
|
||||
|
||||
@ -15,6 +15,7 @@
|
||||
</view>
|
||||
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<collect-tip ref="collectTipRef" ></collect-tip>
|
||||
<!-- 小程序隐私协议 -->
|
||||
<wx-privacy-popup ref="wxPrivacyPopupRef"></wx-privacy-popup>
|
||||
<!-- #endif -->
|
||||
@ -40,7 +41,7 @@ const diy = useDiy({
|
||||
const diyGroupRef = ref(null)
|
||||
|
||||
const wxPrivacyPopupRef: any = ref(null)
|
||||
|
||||
const collectTipRef: any = ref(null)
|
||||
// 监听页面加载
|
||||
diy.onLoad();
|
||||
|
||||
@ -57,9 +58,11 @@ diy.onShow((data: any) => {
|
||||
let share = data.share ? JSON.parse(data.share) : null;
|
||||
setShare(share);
|
||||
diyGroupRef.value?.refresh();
|
||||
|
||||
// #ifdef MP
|
||||
nextTick(() => {
|
||||
if (wxPrivacyPopupRef.value) wxPrivacyPopupRef.value.proactive();
|
||||
if (collectTipRef.value) collectTipRef.value.show();
|
||||
})
|
||||
// #endif
|
||||
});
|
||||
|
||||
@ -44,15 +44,25 @@
|
||||
</u-form-item>
|
||||
</view>
|
||||
</u-form>
|
||||
|
||||
<view class="w-full footer">
|
||||
|
||||
<view
|
||||
class="py-[var(--top-m)] px-[var(--sidebar-m)] footer w-full fixed bottom-0 left-0 right-0 box-border">
|
||||
class="py-[var(--top-m)] px-[var(--sidebar-m)] footer w-full fixed bottom-30 left-0 right-0 box-border">
|
||||
<button hover-class="none"
|
||||
class="primary-btn-bg !text-[#fff] h-[80rpx] leading-[80rpx] rounded-[100rpx] text-[26rpx] font-500"
|
||||
@click="save" :disabled="btnDisabled" :loading="operateLoading"
|
||||
:class="{'opacity-50': btnDisabled}">{{ t('save') }}
|
||||
</button>
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<button hover-class="none"
|
||||
class=" bg-[#fff] !text-[var(--primary-color)] h-[80rpx] leading-[80rpx] rounded-[100rpx] text-[26rpx] font-500 mt-[30rpx] borders"
|
||||
@click="choosegAddress" :disabled="btnDisabled" :loading="loadingchoosegAddress"
|
||||
> <text class="nc-iconfont nc-icon-weixinV6mm"></text> 获取微信地址
|
||||
</button>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
|
||||
</view>
|
||||
<area-select ref="areaRef" @complete="areaSelectComplete" :area-id="formData.district_id || formData.city_id" />
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
@ -115,7 +125,7 @@ onLoad((data: any) => {
|
||||
}
|
||||
formData.value.address = data.name;
|
||||
getAddress(data.latng);
|
||||
var tempArr = getQueryVariable('latng').split(',');
|
||||
const tempArr = getQueryVariable('latng').split(',');
|
||||
formData.value.lat = tempArr[0];
|
||||
formData.value.lng = tempArr[1];
|
||||
}
|
||||
@ -277,7 +287,7 @@ const chooseLocation = () => {
|
||||
// #endif
|
||||
|
||||
// #ifdef H5
|
||||
var urlencode = formData.value;
|
||||
const urlencode = formData.value;
|
||||
uni.setStorageSync('addressInfo', urlencode);
|
||||
let backurl = location.origin + location.pathname + '?source=' + source.value;
|
||||
if (isSelectMap.value) {
|
||||
@ -310,16 +320,36 @@ const getAddress = (latlng: any) => {
|
||||
}
|
||||
|
||||
const getQueryVariable = (variable: any) => {
|
||||
var query = window.location.search.substring(1);
|
||||
var vars = query.split('&');
|
||||
for (var i = 0; i < vars.length; i++) {
|
||||
var pair = vars[i].split('=');
|
||||
const query = window.location.search.substring(1);
|
||||
const vars = query.split('&');
|
||||
for (let i = 0; i < vars.length; i++) {
|
||||
const pair = vars[i].split('=');
|
||||
if (pair[0] == variable) {
|
||||
return pair[1];
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
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
|
||||
},
|
||||
fail(err) {
|
||||
loadingchoosegAddress.value = false
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@ -335,4 +365,7 @@ const getQueryVariable = (variable: any) => {
|
||||
height: calc(100rpx + var(--top-m) + var(--top-m) + constant(safe-area-inset-bottom)) !important;
|
||||
height: calc(100rpx + var(--top-m) + var(--top-m) + env(safe-area-inset-bottom)) !important;
|
||||
}
|
||||
.borders{
|
||||
border: 1px solid var(--primary-color);
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -136,13 +136,6 @@ onShow(() => {
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
// 获取系统状态栏的高度
|
||||
let menuButtonInfo: any = {};
|
||||
// 如果是小程序,获取右上角胶囊的尺寸信息,避免导航栏右侧内容与胶囊重叠(支付宝小程序非本API,尚未兼容)
|
||||
// #ifdef MP-WEIXIN || MP-BAIDU || MP-TOUTIAO || MP-QQ
|
||||
menuButtonInfo = uni.getMenuButtonBoundingClientRect();
|
||||
// #endif
|
||||
const headerStyle = computed(() => {
|
||||
return {
|
||||
backgroundImage: 'url(' + img('static/resource/images/member/balance_bg.png') + ') ',
|
||||
@ -154,14 +147,14 @@ const headerStyle = computed(() => {
|
||||
|
||||
const mescrollTop = computed(() => {
|
||||
if ((cashOutConfigObj.is_open == 1 || rechargeConfigObj.is_use == 1)) {
|
||||
if (Object.keys(menuButtonInfo).length) {
|
||||
return (pxToRpx(Number(menuButtonInfo.height)) + pxToRpx(menuButtonInfo.top) + pxToRpx(8) + 700) + 'rpx'
|
||||
if (Object.keys(systemStore.menuButtonInfo).length) {
|
||||
return (pxToRpx(Number(systemStore.menuButtonInfo.height)) + pxToRpx(systemStore.menuButtonInfo.top) + pxToRpx(8) + 700) + 'rpx'
|
||||
} else {
|
||||
return '718rpx'
|
||||
}
|
||||
} else {
|
||||
if (Object.keys(menuButtonInfo).length) {
|
||||
return (pxToRpx(Number(menuButtonInfo.height)) + pxToRpx(menuButtonInfo.top) + pxToRpx(8) + 632) + 'rpx'
|
||||
if (Object.keys(systemStore.menuButtonInfo).length) {
|
||||
return (pxToRpx(Number(systemStore.menuButtonInfo.height)) + pxToRpx(systemStore.menuButtonInfo.top) + pxToRpx(8) + 632) + 'rpx'
|
||||
} else {
|
||||
return '650rpx'
|
||||
}
|
||||
|
||||
@ -77,9 +77,11 @@ import useMescroll from '@/components/mescroll/hooks/useMescroll.js';
|
||||
import { onPageScroll, onReachBottom } from '@dcloudio/uni-app';
|
||||
import { topTabar } from '@/utils/topTabbar'
|
||||
import selectDate from '@/components/select-date/select-date.vue';
|
||||
import useSystemStore from "@/stores/system";
|
||||
|
||||
const { downCallback, mescrollInit, getMescroll } = useMescroll(onPageScroll, onReachBottom);
|
||||
const memberStore = useMemberStore();
|
||||
const systemStore = useSystemStore()
|
||||
const info = computed(() => memberStore.info)
|
||||
// 提现
|
||||
const applyCashOut = () => {
|
||||
@ -92,12 +94,6 @@ const topTabarObj = topTabar()
|
||||
let param = topTabarObj.setTopTabbarParam({ title: '我的佣金', topStatusBar: { bgColor: '#fff', textColor: '#333' } })
|
||||
/********* 自定义头部 - end ***********/
|
||||
|
||||
// 获取系统状态栏的高度
|
||||
let menuButtonInfo: any = {};
|
||||
// 如果是小程序,获取右上角胶囊的尺寸信息,避免导航栏右侧内容与胶囊重叠(支付宝小程序非本API,尚未兼容)
|
||||
// #ifdef MP-WEIXIN || MP-BAIDU || MP-TOUTIAO || MP-QQ
|
||||
menuButtonInfo = uni.getMenuButtonBoundingClientRect();
|
||||
// #endif
|
||||
const headerStyle = computed(() => {
|
||||
return {
|
||||
backgroundImage: 'url(' + img('static/resource/images/member/commission/commission_bg.png') + ') ',
|
||||
@ -108,7 +104,7 @@ const headerStyle = computed(() => {
|
||||
})
|
||||
// 16为自定头部的padding-bottom
|
||||
const mescrollTop = computed(() => {
|
||||
return Object.keys(menuButtonInfo).length ? (Number(menuButtonInfo.height) * 2 + menuButtonInfo.top * 2 + 470 + 16) + 'rpx' : '470rpx'
|
||||
return Object.keys(systemStore.menuButtonInfo).length ? (Number(systemStore.menuButtonInfo.height) * 2 + systemStore.menuButtonInfo.top * 2 + 470 + 16) + 'rpx' : '470rpx'
|
||||
})
|
||||
|
||||
//来源类型
|
||||
@ -173,7 +169,7 @@ const confirmFn = (data: any) => {
|
||||
<style lang="scss">
|
||||
.member-level {
|
||||
background: linear-gradient(360deg, #F23621 11%, #FF7F71 100%), #D9D9D9;
|
||||
border-radius: 0rpx 20rpx 20rpx 0rpx;
|
||||
border-radius: 0 20rpx 20rpx 0;
|
||||
}
|
||||
|
||||
:deep(.uni-scroll-view) {
|
||||
|
||||
@ -22,6 +22,8 @@ onMounted(() => {
|
||||
|
||||
props.data.formField.forEach((item: any) => {
|
||||
let comp = {
|
||||
|
||||
componentIsShow: true, // 是否显示
|
||||
id: item.field_key,
|
||||
componentName: item.field_type,
|
||||
pageStyle: '',
|
||||
@ -43,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
|
||||
}
|
||||
|
||||
@ -110,7 +110,7 @@
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="" v-if="!loading && (!list || !list.length)">
|
||||
<view v-if="!loading && (!list || !list.length)">
|
||||
<!-- #ifdef MP -->
|
||||
<top-tabbar :data="topTabbarDataEmpty" />
|
||||
<!-- #endif -->
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<view class="w-full min-h-screen bg-page personal-wrap !pb-[20rpx]" v-if="info" :style="themeColor()">
|
||||
<view class="w-full min-h-screen bg-page personal-wrap !pb-[20rpx] overflow-hidden" v-if="info" :style="themeColor()">
|
||||
<view class="my-[var(--top-m)] sidebar-margin overflow-hidden card-template py-[20rpx]">
|
||||
<u-cell-group :border="false" class="cell-group">
|
||||
<u-cell :title="t('headimg')" :titleStyle="{'font-size': '28rpx'}" :is-link="true">
|
||||
@ -217,7 +217,7 @@ page {
|
||||
.u-cell {
|
||||
.u-cell__body {
|
||||
padding: 0;
|
||||
height: 90rpx;
|
||||
height: 80rpx;
|
||||
margin-top: 16rpx;
|
||||
}
|
||||
|
||||
@ -226,7 +226,7 @@ page {
|
||||
}
|
||||
|
||||
.u-cell__title-text {
|
||||
font-size: 28rpx;
|
||||
font-size: 26rpx;
|
||||
line-height: 40rpx;
|
||||
}
|
||||
|
||||
@ -236,7 +236,7 @@ page {
|
||||
|
||||
.u-cell__value {
|
||||
line-height: 1;
|
||||
font-size: 28rpx;
|
||||
font-size: 26rpx;
|
||||
color: #333 !important;
|
||||
}
|
||||
|
||||
@ -264,7 +264,7 @@ page {
|
||||
}
|
||||
|
||||
.u-action-sheet__item-wrap__item__name {
|
||||
font-size: 30rpx !important;
|
||||
font-size: 26rpx !important;
|
||||
}
|
||||
}
|
||||
|
||||
@ -287,8 +287,8 @@ page {
|
||||
}
|
||||
}
|
||||
.detail-two-content{
|
||||
padding-top: 32rpx !important;
|
||||
padding-bottom: 32rpx !important;
|
||||
padding-top: 30rpx !important;
|
||||
padding-bottom: 30rpx !important;
|
||||
margin-bottom: 0 !important;
|
||||
& > view{
|
||||
width: 72% !important;
|
||||
@ -297,7 +297,11 @@ page {
|
||||
height: 136rpx !important;
|
||||
}
|
||||
}
|
||||
& > view, .detail-two-content-label{
|
||||
font-size: 26rpx!important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
@ -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]
|
||||
}
|
||||
|
||||
@ -106,29 +106,25 @@ import { t } from '@/locale'
|
||||
import { redirect, img, pxToRpx } from '@/utils/common';
|
||||
import { getMemberAccountPointcount, getTaskPoint } from '@/app/api/member';
|
||||
import { topTabar } from '@/utils/topTabbar'
|
||||
import useSystemStore from "@/stores/system";
|
||||
|
||||
/********* 自定义头部 - start ***********/
|
||||
const topTabarObj = topTabar()
|
||||
let param = topTabarObj.setTopTabbarParam({ title: '我的积分' })
|
||||
/********* 自定义头部 - end ***********/
|
||||
|
||||
// 获取系统状态栏的高度
|
||||
let menuButtonInfo: any = {};
|
||||
// 如果是小程序,获取右上角胶囊的尺寸信息,避免导航栏右侧内容与胶囊重叠(支付宝小程序非本API,尚未兼容)
|
||||
// #ifdef MP-WEIXIN || MP-BAIDU || MP-TOUTIAO || MP-QQ
|
||||
menuButtonInfo = uni.getMenuButtonBoundingClientRect();
|
||||
// #endif
|
||||
const systemStore = useSystemStore()
|
||||
const headerStyle = computed(() => {
|
||||
return {
|
||||
backgroundImage: 'url(' + img('static/resource/images/member/point/point_bg.png') + ') ',
|
||||
backgroundSize: 'cover',
|
||||
backgroundRepeat: 'no-repeat',
|
||||
backgroundPosition: 'bottom',
|
||||
// paddingTop:Object.keys(menuButtonInfo).length?(Number(menuButtonInfo.height) * 2 + menuButtonInfo.top * 2 + 77)+'rpx':'77rpx',
|
||||
// paddingTop:Object.keys(systemStore.menuButtonInfo).length?(Number(systemStore.menuButtonInfo.height) * 2 + systemStore.menuButtonInfo.top * 2 + 77)+'rpx':'77rpx',
|
||||
}
|
||||
})
|
||||
const topStyle = computed(() => {
|
||||
let style = Object.keys(menuButtonInfo).length ? (pxToRpx(Number(menuButtonInfo.height)) + pxToRpx(menuButtonInfo.top) + 50) + 'rpx;' : '50rpx'
|
||||
let style = Object.keys(systemStore.menuButtonInfo).length ? (pxToRpx(Number(systemStore.menuButtonInfo.height)) + pxToRpx(systemStore.menuButtonInfo.top) + 50) + 'rpx;' : '50rpx'
|
||||
return style
|
||||
})
|
||||
|
||||
|
||||
@ -247,6 +247,7 @@ import { onLoad } from '@dcloudio/uni-app'
|
||||
import { getSignInfo, getSignConfig, setSign, getDayPack } from '@/app/api/member'
|
||||
import useMemberStore from '@/stores/member'
|
||||
import { topTabar } from '@/utils/topTabbar'
|
||||
import useSystemStore from "@/stores/system";
|
||||
|
||||
const state = reactive({
|
||||
dataCount: [], //当月所有天数
|
||||
@ -462,23 +463,18 @@ const filteredDate = (date: any) => {
|
||||
return date > 0 ? date : ''
|
||||
}
|
||||
|
||||
// 获取系统状态栏的高度
|
||||
let menuButtonInfo: any = {};
|
||||
// 如果是小程序,获取右上角胶囊的尺寸信息,避免导航栏右侧内容与胶囊重叠(支付宝小程序非本API,尚未兼容)
|
||||
// #ifdef MP-WEIXIN || MP-BAIDU || MP-TOUTIAO || MP-QQ
|
||||
menuButtonInfo = uni.getMenuButtonBoundingClientRect();
|
||||
// #endif
|
||||
const systemStore = useSystemStore()
|
||||
/********* 自定义头部 - start ***********/
|
||||
const topTabarObj = topTabar()
|
||||
let topTabbarData = topTabarObj.setTopTabbarParam({ title: '我的签到' })
|
||||
/********* 自定义头部 - end ***********/
|
||||
|
||||
const headStyle = computed(() => {
|
||||
let style = pxToRpx(Number(menuButtonInfo.height) + menuButtonInfo.top + 8) + 382 + 'rpx;'
|
||||
let style = pxToRpx(Number(systemStore.menuButtonInfo.height) + systemStore.menuButtonInfo.top + 8) + 382 + 'rpx;'
|
||||
return style
|
||||
})
|
||||
const topStyle = computed(() => {
|
||||
let style = pxToRpx(Number(menuButtonInfo.height) + menuButtonInfo.top + 8) + 50 + 'rpx;'
|
||||
let style = pxToRpx(Number(systemStore.menuButtonInfo.height) + systemStore.menuButtonInfo.top + 8) + 50 + 'rpx;'
|
||||
return style
|
||||
})
|
||||
</script>
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
</u-cell-group>
|
||||
</view>
|
||||
|
||||
<view class="h-[88rpx] flex-center bg-[#fff] mx-[var(--sidebar-m)] rounded-[var(--rounded-big)] text-[28rpx]" @click="memberStore.logout(true)">{{ t('logout') }}</view>
|
||||
<view class="h-[88rpx] flex-center bg-[#fff] mx-[var(--sidebar-m)] rounded-[var(--rounded-big)] text-[26rpx]" @click="memberStore.logout(true)">{{ t('logout') }}</view>
|
||||
|
||||
<u-action-sheet :actions="langList" :show="langSheetShow" :closeOnClickOverlay="true"
|
||||
:safeAreaInsetBottom="true"
|
||||
@ -63,7 +63,7 @@ page {
|
||||
.u-cell {
|
||||
.u-cell__body {
|
||||
padding: 0;
|
||||
height: 90rpx;
|
||||
height: 80rpx;
|
||||
margin-top: 16rpx;
|
||||
}
|
||||
|
||||
@ -72,7 +72,7 @@ page {
|
||||
}
|
||||
|
||||
.u-cell__title-text {
|
||||
font-size: 28rpx;
|
||||
font-size: 26rpx;
|
||||
line-height: 40rpx;
|
||||
}
|
||||
|
||||
@ -82,7 +82,7 @@ page {
|
||||
|
||||
.u-cell__value {
|
||||
line-height: 1;
|
||||
font-size: 28rpx;
|
||||
font-size: 26rpx;
|
||||
}
|
||||
|
||||
.u-line {
|
||||
@ -104,7 +104,7 @@ page {
|
||||
}
|
||||
|
||||
.u-action-sheet__item-wrap__item__name {
|
||||
font-size: 30rpx !important;
|
||||
font-size: 26rpx !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -156,7 +156,7 @@ const scanCode = () => {
|
||||
|
||||
let isLoading = false;
|
||||
const confirm = () => {
|
||||
var reg = /[\S]+/;
|
||||
const reg = /[\S]+/;
|
||||
if (!reg.test(verify_code.value)) {
|
||||
uni.showToast({
|
||||
title: '请输入核销码',
|
||||
|
||||
@ -39,7 +39,7 @@
|
||||
</view>
|
||||
<view class="flex-1">
|
||||
<view class="text-[22rpx] text-[var(--text-color-light9)] mb-[10rpx] leading-[30rpx]">核销员</view>
|
||||
<view class="text-[26rpx] text-[#303133] leading-[36rpx]">{{ item.member ? item.member.nickname : '--' }}</view>
|
||||
<view class="text-[26rpx] text-[#303133] leading-[36rpx]">{{ (item.is_admin == 1 ? '后台核销' : item.member?.nickname) || '--' }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@ -12,7 +12,7 @@ interface Diy {
|
||||
title: string,
|
||||
pageStartBgColor: string, // 页面背景颜色(开始)
|
||||
pageEndBgColor: string, // 页面背景颜色(结束)
|
||||
bottomTabBarSwitch: boolean, // 底部导航开关
|
||||
bottomTabBar: any, // 底部导航
|
||||
bgUrl: string
|
||||
},
|
||||
// 组件集合
|
||||
@ -20,7 +20,8 @@ interface Diy {
|
||||
topFixedStatus: string, // 置顶组件的状态
|
||||
scrollTop: number,
|
||||
topTabarHeight: number,
|
||||
componentRefs: any
|
||||
componentRefs: any,
|
||||
imageAdsSameScreen: any // 图片广告, 是否设置沉浸式
|
||||
}
|
||||
|
||||
const useDiyStore = defineStore('diy', {
|
||||
@ -34,14 +35,18 @@ const useDiyStore = defineStore('diy', {
|
||||
title: "",
|
||||
pageStartBgColor: '', // 页面背景颜色(开始)
|
||||
pageEndBgColor: '', // 页面背景颜色(结束)
|
||||
bottomTabBarSwitch: true,
|
||||
bottomTabBar: {
|
||||
control: true, // 是否允许展示编辑
|
||||
isShow: true, // 是否显示
|
||||
},
|
||||
bgUrl: ''
|
||||
},
|
||||
value: [], // 组件集合
|
||||
topFixedStatus: 'home', // 顶部 置顶组件状态,home:展示首页数据、diy:展示置顶组件定义的子页面
|
||||
scrollTop: 0, // 滚动位置
|
||||
topTabarHeight: 0,
|
||||
componentRefs: null
|
||||
componentRefs: null,
|
||||
imageAdsSameScreen: false
|
||||
}
|
||||
},
|
||||
getters: {},
|
||||
@ -49,7 +54,7 @@ const useDiyStore = defineStore('diy', {
|
||||
// 初始化
|
||||
init() {
|
||||
// #ifdef H5
|
||||
var data = JSON.stringify({
|
||||
const data = JSON.stringify({
|
||||
type: 'init',
|
||||
load: true
|
||||
});
|
||||
@ -68,6 +73,7 @@ const useDiyStore = defineStore('diy', {
|
||||
if (this.value) {
|
||||
this.value.forEach((item, index) => {
|
||||
item.pageStyle = '';
|
||||
item.componentIsShow = true // 是否显示
|
||||
if (item.pageStartBgColor) {
|
||||
if (item.pageStartBgColor && item.pageEndBgColor) item.pageStyle += `background:linear-gradient(${ item.pageGradientAngle },${ item.pageStartBgColor },${ item.pageEndBgColor });`;
|
||||
else item.pageStyle += 'background-color:' + item.pageStartBgColor + ';';
|
||||
@ -118,7 +124,7 @@ const useDiyStore = defineStore('diy', {
|
||||
// 减少重复请求
|
||||
if (this.currentIndex == index) return;
|
||||
this.currentIndex = index;
|
||||
var data = JSON.stringify({
|
||||
const data = JSON.stringify({
|
||||
type: 'change',
|
||||
index,
|
||||
component: toRaw(component)
|
||||
|
||||
@ -15,18 +15,20 @@
|
||||
<view v-if="selected.district">{{ selected.district.name }}</view>
|
||||
<view v-else>请选择</view>
|
||||
</view>
|
||||
<view class="flex-1 pr-[10rpx]" v-else></view>
|
||||
</view>
|
||||
<scroll-view scroll-y="true" class="h-[50vh]">
|
||||
<view class="flex p-[30rpx] pt-0 text-sm">
|
||||
<view v-if="areaList.province.length" v-show="currSelect == 'province'">
|
||||
<view v-for="item in areaList.province" class="h-[80rpx] flex items-center" :class="{'text-[var(--primary-color)]': selected.province && selected.province.id == item.id }" @click="selected.province = item">{{ item.name }}</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]">
|
||||
<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>
|
||||
<view v-if="areaList.city.length" v-show="currSelect == 'city'">
|
||||
<view v-for="item in areaList.city" class="h-[80rpx] flex items-center" :class="{'text-[var(--primary-color)]': selected.city && selected.city.id == item.id }" @click="selected.city = item">{{ item.name }}</view>
|
||||
<view v-if="areaList.city.length" class="flex-1 pr-[10rpx]" :style="{ opacity: currSelect == 'city' ? 1 : 0, pointerEvents: currSelect == 'city' ? 'auto' : 'none',height: currSelect == 'city' ? 'auto' : '0',overflow: currSelect == 'city' ? 'auto' : 'hidden' }">
|
||||
<view v-for="(item, index) in areaList.city" :key="item.id" class="h-[80rpx] flex items-center" :class="{'text-[var(--primary-color)]': selected.city && selected.city.id == item.id }" @click="handleCityClick(item)">{{ item.name }}</view>
|
||||
</view>
|
||||
<view v-if="areaList.district.length" v-show="currSelect == 'district'">
|
||||
<view v-for="item in areaList.district" class="h-[80rpx] flex items-center " :class="{'text-[var(--primary-color)]': selected.district && selected.district.id == item.id }" @click="selected.district = item">{{ item.name }}</view>
|
||||
<view v-if="areaList.district.length" class="flex-1 pr-[10rpx]" :style="{ opacity: currSelect == 'district' ? 1 : 0, pointerEvents: currSelect == 'district' ? 'auto' : 'none',height: currSelect == 'district' ? 'auto' : '0',overflow: currSelect == 'district' ? 'auto' : 'hidden' }">
|
||||
<view v-for="(item, index) in areaList.district" :key="item.id" class="h-[80rpx] flex items-center " :class="{'text-[var(--primary-color)]': selected.district && selected.district.id == item.id }" @click="selected.district = item">{{ item.name }}</view>
|
||||
</view>
|
||||
<view class="flex-1 pr-[10rpx]" v-else></view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
@ -34,7 +36,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, watch } from 'vue'
|
||||
import { ref, reactive, watch, nextTick } from 'vue'
|
||||
import { getAreaListByPid, getAreaByCode } from '@/app/api/system'
|
||||
|
||||
const prop = defineProps({
|
||||
@ -58,6 +60,9 @@ const selected = reactive({
|
||||
district: null
|
||||
})
|
||||
|
||||
// 滚动控制
|
||||
const scrollTop = ref(0)
|
||||
|
||||
getAreaListByPid(0).then(({ data }) => {
|
||||
areaList.province = data
|
||||
}).catch()
|
||||
@ -68,10 +73,6 @@ watch(() => prop.areaId, (nval, oval) => {
|
||||
data.province && (selected.province = data.province)
|
||||
data.city && (selected.city = data.city)
|
||||
data.district && (selected.district = data.district)
|
||||
if (data.city == undefined && data.province && data.district) {
|
||||
selected.city = data.district
|
||||
selected.district=null
|
||||
}
|
||||
})
|
||||
}
|
||||
}, {
|
||||
@ -85,17 +86,24 @@ watch(() => selected.province, () => {
|
||||
getAreaListByPid(selected.province.id).then(({ data }) => {
|
||||
areaList.city = data
|
||||
currSelect.value = 'city'
|
||||
|
||||
|
||||
if (selected.city) {
|
||||
let isExist = false
|
||||
let selectedIndex = -1
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
if (selected.city.id == data[i].id) {
|
||||
isExist = true
|
||||
selectedIndex = i
|
||||
break
|
||||
}
|
||||
}
|
||||
if (!isExist) {
|
||||
selected.city = null
|
||||
} else {
|
||||
// 滚动到选中的城市位置
|
||||
setTimeout(() => {
|
||||
scrollToSelected('city', selectedIndex)
|
||||
}, 100)
|
||||
}
|
||||
}
|
||||
}).catch()
|
||||
@ -112,14 +120,21 @@ watch(() => selected.city, (nval) => {
|
||||
|
||||
if (selected.district) {
|
||||
let isExist = false
|
||||
let selectedIndex = -1
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
if (selected.district.id == data[i].id) {
|
||||
isExist = true
|
||||
selectedIndex = i
|
||||
break
|
||||
}
|
||||
}
|
||||
if (!isExist) {
|
||||
selected.district = null
|
||||
} else {
|
||||
// 滚动到选中的区县位置
|
||||
setTimeout(() => {
|
||||
scrollToSelected('district', selectedIndex)
|
||||
}, 100)
|
||||
}
|
||||
}
|
||||
if (!data.length) {
|
||||
@ -137,6 +152,44 @@ watch(() => selected.city, (nval) => {
|
||||
|
||||
const emits = defineEmits(['complete'])
|
||||
|
||||
// 滚动到选中项位置
|
||||
const scrollToSelected = (type: string, selectedIndex: number) => {
|
||||
// 计算目标位置,让选中项显示在列表的下半部分
|
||||
const itemHeight = 80 // 每项高度 80rpx
|
||||
const targetScrollTop = Math.max(0, (selectedIndex - 2) * itemHeight) // 让选中项显示在第3个位置左右
|
||||
scrollTop.value = targetScrollTop
|
||||
}
|
||||
|
||||
// 重置滚动位置
|
||||
const resetScrollTop = () => {
|
||||
scrollTop.value = 0
|
||||
}
|
||||
|
||||
// 监听当前选择标签变化,重置滚动位置并滚动到选中项
|
||||
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)
|
||||
})
|
||||
|
||||
/**
|
||||
* 监听区县变更
|
||||
*/
|
||||
@ -148,8 +201,29 @@ watch(() => selected.district, (nval) => {
|
||||
}
|
||||
}, { deep: true })
|
||||
|
||||
// 处理省份点击
|
||||
const handleProvinceClick = (item: any) => {
|
||||
selected.province = item
|
||||
// 立即滚动到顶部,确保新数据可见
|
||||
nextTick(() => {
|
||||
resetScrollTop()
|
||||
})
|
||||
}
|
||||
|
||||
// 处理城市点击
|
||||
const handleCityClick = (item: any) => {
|
||||
selected.city = item
|
||||
// 立即滚动到顶部,确保新数据可见
|
||||
nextTick(() => {
|
||||
resetScrollTop()
|
||||
})
|
||||
}
|
||||
|
||||
const open = () => {
|
||||
show.value = true
|
||||
if(prop.areaId){
|
||||
currSelect.value = 'district'
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
|
||||
191
uni-app/src/components/avatar-swiper/avatar-swiper.vue
Normal file
191
uni-app/src/components/avatar-swiper/avatar-swiper.vue
Normal file
@ -0,0 +1,191 @@
|
||||
<template>
|
||||
<view class="carousel-container">
|
||||
<view class="carousel-track" :style="trackStyle">
|
||||
<view
|
||||
v-for="(item, index) in displayList"
|
||||
: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">
|
||||
<template #error>
|
||||
<image
|
||||
class="w-[30rpx] h-[30rpx] rounded-full"
|
||||
:src="img('static/resource/images/default_headimg.png')"
|
||||
mode="aspectFill"
|
||||
/>
|
||||
</template>
|
||||
</u--image>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onUnmounted, computed, nextTick } from 'vue';
|
||||
import { img } from '@/utils/common';
|
||||
|
||||
const props = defineProps<{
|
||||
avatars: string[];
|
||||
interval?: number; // 动画间隔时间(默认2000ms)
|
||||
}>();
|
||||
|
||||
// 配置参数(优化动画连贯性)
|
||||
const VISIBLE_COUNT = 3;
|
||||
const ITEM_WIDTH = 30;
|
||||
const OVERLAP = 10;
|
||||
const ANIMATION_DURATION = 500; // 稍长动画,减少急促感
|
||||
const EASING = 'cubic-bezier(0.25, 0.1, 0.25, 1)'; // 平滑曲线
|
||||
const slideDistance = ITEM_WIDTH - OVERLAP;
|
||||
|
||||
// 状态管理
|
||||
const displayList = ref<{
|
||||
src: string;
|
||||
isNew: boolean;
|
||||
key: string; // 唯一key,避免v-for复用导致的闪烁
|
||||
}[]>([]);
|
||||
let currentIndex = 0;
|
||||
let timer: ReturnType<typeof setInterval> | null = null;
|
||||
const isTransitioning = ref(false);
|
||||
const trackOffset = ref(0);
|
||||
|
||||
/**
|
||||
* 初始化列表:确保初始状态稳定
|
||||
*/
|
||||
const initList = () => {
|
||||
if (props.avatars.length <= VISIBLE_COUNT) {
|
||||
displayList.value = props.avatars.map((src, i) => ({
|
||||
src,
|
||||
isNew: true,
|
||||
key: `init-${i}-${src}` // 唯一key
|
||||
}));
|
||||
} else {
|
||||
displayList.value = props.avatars.slice(0, VISIBLE_COUNT).map((src, i) => ({
|
||||
src,
|
||||
isNew: true,
|
||||
key: `init-${i}-${src}`
|
||||
}));
|
||||
currentIndex = VISIBLE_COUNT;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 单个头像样式:同步动画参数
|
||||
*/
|
||||
const itemStyle = (index: number) => ({
|
||||
zIndex: VISIBLE_COUNT + 1 - index,
|
||||
transition: `transform ${ANIMATION_DURATION}ms ${EASING}`, // 与轨道动画完全同步
|
||||
});
|
||||
|
||||
/**
|
||||
* 轨道样式:启用硬件加速,优化过渡
|
||||
*/
|
||||
const trackStyle = computed(() => ({
|
||||
transform: `translateX(${trackOffset.value}rpx)`,
|
||||
transition: isTransitioning.value
|
||||
? `transform ${ANIMATION_DURATION}ms ${EASING}`
|
||||
: 'none',
|
||||
display: 'flex',
|
||||
// 硬件加速:减少重绘闪烁
|
||||
willChange: isTransitioning.value ? 'transform' : 'auto',
|
||||
backfaceVisibility: 'hidden',
|
||||
perspective: '1000px',
|
||||
}));
|
||||
|
||||
/**
|
||||
* 滑动动画:优化时序,避免闪烁
|
||||
*/
|
||||
const slide = async () => {
|
||||
if (props.avatars.length <= VISIBLE_COUNT || isTransitioning.value) return;
|
||||
isTransitioning.value = true;
|
||||
|
||||
try {
|
||||
// 1. 提前插入新头像(带唯一key,避免复用闪烁)
|
||||
const nextKey = `slide-${currentIndex}-${Date.now()}`; // 用时间戳确保唯一
|
||||
const nextAvatar = {
|
||||
src: props.avatars[currentIndex % props.avatars.length],
|
||||
isNew: false, // 初始隐藏
|
||||
key: nextKey
|
||||
};
|
||||
displayList.value.unshift(nextAvatar);
|
||||
await nextTick(); // 等待DOM稳定渲染
|
||||
|
||||
// 2. 重置轨道位置(无动画,为滑动做准备)
|
||||
trackOffset.value = -slideDistance;
|
||||
isTransitioning.value = false;
|
||||
await nextTick(); // 确保位置重置生效
|
||||
|
||||
// 3. 启动向右滑动动画(核心步骤)
|
||||
isTransitioning.value = true;
|
||||
trackOffset.value = 0; // 向右移动到目标位置
|
||||
|
||||
// 4. 同步显示新头像(与滑动动画同步开始)
|
||||
setTimeout(() => {
|
||||
nextAvatar.isNew = true; // 新头像缩放显示,与滑动同步
|
||||
}, 500); // 轻微延迟,模拟“滑入时逐渐显示”
|
||||
|
||||
// 5. 等待动画完全结束(关键:确保动画结束后再操作)
|
||||
await new Promise(resolve => {
|
||||
setTimeout(resolve, ANIMATION_DURATION);
|
||||
});
|
||||
|
||||
// 6. 无痕移除最右侧旧头像(动画结束后,无视觉闪烁)
|
||||
displayList.value.pop();
|
||||
|
||||
// 7. 更新索引,为下次动画准备
|
||||
currentIndex = (currentIndex + 1) % props.avatars.length;
|
||||
|
||||
} finally {
|
||||
isTransitioning.value = false; // 解锁
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 组件挂载与清理
|
||||
*/
|
||||
onMounted(() => {
|
||||
if (props.avatars?.length) {
|
||||
initList();
|
||||
if (props.avatars.length > VISIBLE_COUNT) {
|
||||
const delay = props.interval || 2000;
|
||||
timer = setInterval(slide, delay);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
if (timer) clearInterval(timer);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.carousel-container {
|
||||
width: 70rpx;
|
||||
height: 30rpx;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
display: flex;
|
||||
justify-content: flex-end; // 不足3个时右对齐
|
||||
}
|
||||
|
||||
.carousel-track {
|
||||
height: 100%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.carousel-item {
|
||||
width: 30rpx;
|
||||
height: 30rpx;
|
||||
position: relative;
|
||||
flex-shrink: 0;
|
||||
&:not(:first-child) {
|
||||
margin-left: -15rpx; // 固定重叠,避免计算误差
|
||||
}
|
||||
transform: scale(0); // 初始隐藏
|
||||
}
|
||||
|
||||
// 缩放动画与滑动动画完全同步
|
||||
.carousel-item.scale-in {
|
||||
transform: scale(0.9);
|
||||
}
|
||||
</style>
|
||||
107
uni-app/src/components/barrage/barrage-item.vue
Normal file
107
uni-app/src/components/barrage/barrage-item.vue
Normal file
@ -0,0 +1,107 @@
|
||||
@ -1,108 +0,0 @@
|
||||
<template>
|
||||
<view :style="itemStyle">
|
||||
<view class="danmu-item" :style="{ backgroundColor: bgColor, borderRadius: rounded, color: textColor }">
|
||||
<image class="w-[40rpx] h-[40rpx] rounded-[100rpx]" :src="data.headimg?img(data.headimg):img('static/resource/images/default_headimg.png')" :mode="'aspectFill'"
|
||||
/>
|
||||
<text class="username max-w-[160rpx] truncate">{{ data.nickname }}</text>
|
||||
<text class="time whitespace-nowrap">{{ data.time }}下单</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
import { img } from '@/utils/common';
|
||||
const props = defineProps({
|
||||
bgColor: {
|
||||
type: String,
|
||||
default: '#B0B0B0'
|
||||
},
|
||||
opacity: {
|
||||
type: String,
|
||||
default: '1'
|
||||
},
|
||||
textColor: {
|
||||
type: String,
|
||||
default: '#FFFFFF'
|
||||
},
|
||||
rounded: {
|
||||
type: Number,
|
||||
default: 20
|
||||
},
|
||||
data: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
delay: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
speed: {
|
||||
type: Number,
|
||||
default: 8
|
||||
}
|
||||
})
|
||||
|
||||
const itemStyle = computed(() => {
|
||||
return {
|
||||
'--speed': `${props.speed}s`,
|
||||
'--delay': `${props.delay}s`,
|
||||
'--height': `-300rpx`
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.danmu-item {
|
||||
position: absolute;
|
||||
left: 30rpx;
|
||||
bottom: 0;
|
||||
min-width: 180rpx;
|
||||
white-space: nowrap;
|
||||
padding: 10rpx;
|
||||
padding-right: 20rpx;
|
||||
background-color: rgba(255, 255, 255, 0.9);
|
||||
border-radius: 40rpx;
|
||||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.1);
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
transform: translateY(0);
|
||||
opacity: 0;
|
||||
animation: moveUp linear var(--speed) forwards;
|
||||
animation-delay: var(--delay);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.username {
|
||||
margin-left: 8rpx;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
.time {
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
@keyframes moveUp {
|
||||
0% {
|
||||
transform: translateY(0);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
10% {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
90% {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translateY(var(--height));
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
176
uni-app/src/components/barrage/container.vue
Normal file
176
uni-app/src/components/barrage/container.vue
Normal file
@ -0,0 +1,176 @@
|
||||
<template>
|
||||
<view class="danmu-container">
|
||||
<barrage-item v-for="(item, index) in danmuList" :key="`${item.id}-${item.timestamp}`" :data="item" :delay="item.delay" :speed="item.speed"
|
||||
:textColor="props.textColor" :bgColor="props.bgColor" :rounded="props.rounded" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, computed,nextTick, watch, onUpdated, onUnmounted } from 'vue'
|
||||
import barrageItem from '@/components/barrage/barrage-item.vue'
|
||||
const props = defineProps({
|
||||
bgColor: {
|
||||
type: String,
|
||||
default: '#B0B0B0'
|
||||
},
|
||||
textColor: {
|
||||
type: String,
|
||||
default: '#FFFFFF'
|
||||
},
|
||||
rounded: {
|
||||
type: Number,
|
||||
default: 20
|
||||
},
|
||||
maxCount: {
|
||||
type: Number,
|
||||
default: 6
|
||||
},
|
||||
speed: {
|
||||
type: Number,
|
||||
default: 6
|
||||
},
|
||||
interval: {
|
||||
type: Number,
|
||||
default: 1.5
|
||||
},
|
||||
data: {
|
||||
type: Object,
|
||||
default: []
|
||||
},
|
||||
play:{ // 是否播放弹幕,true:播放,false:不播放
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
})
|
||||
|
||||
const danmuList = ref([]) // 当前弹幕列表
|
||||
const waitList = ref([]) // 等待弹幕列表
|
||||
const initTimeId = ref([]) // 初始化时间id
|
||||
const loopTimeId = ref(null) // 初始化时间id
|
||||
|
||||
|
||||
// 初始化弹幕数据
|
||||
const initDanmu = () => {
|
||||
nextTick(() => {
|
||||
danmuList.value = props.data.map((item, index) => {
|
||||
let obj = {}
|
||||
const timestamp = Date.now() + Math.random()
|
||||
obj.id = item.id ? `danmu-${item.id}-${timestamp}` : `danmu-${timestamp}`
|
||||
obj.timestamp = timestamp
|
||||
obj.nickname = item.nickname
|
||||
obj.headimg = item.headimg
|
||||
obj.time = formatTimestampToHumanTime(item.time)
|
||||
obj.delay = index * props.interval,
|
||||
obj.speed = props.speed
|
||||
return obj
|
||||
})
|
||||
|
||||
danmuList.value.forEach((item, index) => {
|
||||
initTimeId.value[index] = setTimeout(() => {
|
||||
waitList.value.push(item)
|
||||
danmuList.value.shift();
|
||||
loopTriggerFn()
|
||||
}, (item.delay + item.speed) * 1000)
|
||||
})
|
||||
})
|
||||
}
|
||||
// 循环触发
|
||||
const loopTriggerFn = () => {
|
||||
if (!danmuList.value.length) {
|
||||
loopTimeId.value = setTimeout(() => {
|
||||
// 创建新的数据对象,避免组件复用
|
||||
danmuList.value = waitList.value.map(item => {
|
||||
const timestamp = Date.now() + Math.random()
|
||||
return {
|
||||
...item,
|
||||
id: `${item.id}-loop-${timestamp}`,
|
||||
timestamp: timestamp
|
||||
}
|
||||
})
|
||||
waitList.value = []
|
||||
danmuList.value.forEach((item, index) => {
|
||||
initTimeId.value[index] = setTimeout(() => {
|
||||
waitList.value.push(item)
|
||||
danmuList.value.shift();
|
||||
loopTriggerFn()
|
||||
}, (item.delay + item.speed) * 1000)
|
||||
})
|
||||
}, 1000)
|
||||
}
|
||||
}
|
||||
watch(() => props.play, (newVal, oldVal) => {
|
||||
nextTick(() => {
|
||||
if (newVal) {
|
||||
initTimeId.value.forEach((item, index) => {
|
||||
if (item) {
|
||||
clearTimeout(item)
|
||||
}
|
||||
})
|
||||
clearTimeout(loopTimeId.value)
|
||||
waitList.value = []
|
||||
danmuList.value = []
|
||||
initDanmu()
|
||||
}else{
|
||||
initTimeId.value.forEach((item, index) => {
|
||||
if (item) {
|
||||
clearTimeout(item)
|
||||
}
|
||||
})
|
||||
clearTimeout(loopTimeId.value)
|
||||
waitList.value = []
|
||||
danmuList.value = []
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
initDanmu()
|
||||
})
|
||||
|
||||
/**
|
||||
* 将时间戳转换为最合适的可读时间单位(分钟、小时、天等),并舍去小数部分
|
||||
* @param {number} timestamp - 时间戳(毫秒)
|
||||
* @returns {string} 人类可读的相对时间字符串
|
||||
*/
|
||||
const formatTimestampToHumanTime = (timestamp) => {
|
||||
const now = Date.now();
|
||||
let seconds = Math.round((now - timestamp) / 1000); // 保留时间差为秒数,四舍五入
|
||||
|
||||
// 如果是未来的时间
|
||||
if (seconds < 0) {
|
||||
return '未来';
|
||||
}
|
||||
|
||||
const intervals = [
|
||||
{ label: '年', seconds: 31536000 }, // 60 * 60 * 24 * 365
|
||||
{ label: '个月', seconds: 2592000 }, // 60 * 60 * 24 * 30
|
||||
{ label: '周', seconds: 604800 }, // 60 * 60 * 24 * 7
|
||||
{ label: '天', seconds: 86400 }, // 60 * 60 * 24
|
||||
{ label: '小时', seconds: 3600 }, // 60 * 60
|
||||
{ label: '分钟', seconds: 60 }
|
||||
];
|
||||
|
||||
for (let i = 0; i < intervals.length; i++) {
|
||||
const interval = intervals[i];
|
||||
const count = Math.floor(seconds / interval.seconds); // 舍去小数部分
|
||||
if (count >= 1) {
|
||||
return `${count}${interval.label}前`;
|
||||
}
|
||||
}
|
||||
|
||||
return '刚刚';
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.danmu-container {
|
||||
position: fixed;
|
||||
top: 160rpx;
|
||||
left: 0;
|
||||
height: 500rpx;
|
||||
width: 100%;
|
||||
pointer-events: none;
|
||||
z-index: 9;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
@ -128,7 +128,12 @@ const handleBind = () => {
|
||||
loading.value = true
|
||||
|
||||
const request = info.value ? bindMobile : mobileLogin;
|
||||
|
||||
if(uni.getStorageSync('avatar') && uni.getStorageSync('nickname') && uni.getStorageSync('openid') && uni.getStorageSync('unionid')){
|
||||
formData.avatar = uni.getStorageSync('avatar')
|
||||
formData.nickname = uni.getStorageSync('nickname')
|
||||
formData.openid = uni.getStorageSync('openid')
|
||||
formData.unionid = uni.getStorageSync('unionid')
|
||||
}
|
||||
request(formData).then((res: any) => {
|
||||
if (info.value) {
|
||||
memberStore.getMemberInfo()
|
||||
|
||||
83
uni-app/src/components/collect-tip/collect-tip.vue
Normal file
83
uni-app/src/components/collect-tip/collect-tip.vue
Normal file
@ -0,0 +1,83 @@
|
||||
<template>
|
||||
<view v-if="visible" class="add-tip" :style="style">
|
||||
<view class="bubble">
|
||||
<view>
|
||||
点击 <span class="nc-iconfont nc-icon-xiaochengxu !text-[33rpx] !text-[#333] "></span>添加到我的小程序<br>微信首页下拉即可快速访问店铺
|
||||
</view>
|
||||
|
||||
<span class="nc-iconfont nc-icon-guanbiV6xx !text-[24rpx] !text-[#333] absolute top-[6rpx] right-[10rpx]" @click="hide"></span>
|
||||
<view class="arrow"></view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, nextTick } from 'vue'
|
||||
|
||||
const visible = ref(false)
|
||||
const style = ref({ top: '0px', right: '0px' })
|
||||
|
||||
const show = () => {
|
||||
const closed = uni.getStorageSync('addedTipClosed')
|
||||
if (!closed) {
|
||||
wx.checkIsAddedToMyMiniProgram({
|
||||
success(res) {
|
||||
if (!res.added) {
|
||||
const menuButton = wx.getMenuButtonBoundingClientRect()
|
||||
nextTick(() => {
|
||||
const top = menuButton.bottom + 10
|
||||
const right = wx.getSystemInfoSync().windowWidth - menuButton.right + 4
|
||||
// const right = menuButton.left - 10
|
||||
style.value = {
|
||||
top: `${top}px`,
|
||||
right: `${right}px`
|
||||
}
|
||||
visible.value = true
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const hide = () => {
|
||||
visible.value = false
|
||||
uni.setStorageSync('addedTipClosed', true)
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
show
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.add-tip {
|
||||
position: fixed;
|
||||
z-index: 999999;
|
||||
}
|
||||
|
||||
.bubble {
|
||||
position: relative;
|
||||
background-color: #fff;
|
||||
opacity: 0.9;
|
||||
color: #333;
|
||||
padding: 20rpx 28rpx;
|
||||
border-radius: 20rpx;
|
||||
font-size: 24rpx;
|
||||
line-height: 1.5;
|
||||
width: 330rpx;
|
||||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.arrow {
|
||||
position: absolute;
|
||||
top: -10rpx;
|
||||
right: 28%;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 12rpx solid transparent;
|
||||
border-right: 12rpx solid transparent;
|
||||
border-bottom: 12rpx solid rgba(255, 255, 255, 0.9);
|
||||
}
|
||||
</style>
|
||||
175
uni-app/src/components/easy-image/easy-image.vue
Normal file
175
uni-app/src/components/easy-image/easy-image.vue
Normal file
@ -0,0 +1,175 @@
|
||||
<template>
|
||||
<view class="easy-load-image" :style="imageStyle" :id="uid">
|
||||
<image class="origin-img" :src="img(imageSrc)" :mode="mode"
|
||||
v-if="loadImg && !isLoadError"
|
||||
v-show="showImg"
|
||||
:style="imageStyle"
|
||||
:class="[imageClass, !openTransition ? 'no-transition' : '', showTransition && openTransition ? 'show-transition' : '']"
|
||||
@load="handleImgLoad" @error="handleImgError">
|
||||
</image>
|
||||
<view class="load-fail-img" v-else-if="isLoadError"></view>
|
||||
<view :class="['loading-img','spin-circle',loadingMode]" v-show="!showImg && !isLoadError"></view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, onBeforeUnmount, nextTick, getCurrentInstance } from 'vue'
|
||||
import { img, debounce } from '@/utils/common'
|
||||
import useSystemStore from '@/stores/system'
|
||||
|
||||
// 生成全局唯一id
|
||||
const generateUUID = () => {
|
||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
|
||||
let r = Math.random() * 16 | 0,
|
||||
v = c == 'x' ? r : (r & 0x3 | 0x8);
|
||||
return v.toString(16);
|
||||
})
|
||||
}
|
||||
|
||||
const props = defineProps({
|
||||
imageSrc: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
imageClass:{
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
imageStyle:{
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
mode: {
|
||||
type: String,
|
||||
default: 'aspectFill'
|
||||
},
|
||||
loadingMode: {
|
||||
type: String,
|
||||
default: 'looming-gray'
|
||||
},
|
||||
openTransition: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
viewHeight: {
|
||||
type: Number,
|
||||
default() {
|
||||
return useSystemStore().systemInfo.windowHeight
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const uid = ref('uid-' + generateUUID())
|
||||
const loadImg = ref(false)
|
||||
const showImg = ref(false)
|
||||
const isLoadError = ref(false)
|
||||
const showTransition = ref(false)
|
||||
const instance = getCurrentInstance();
|
||||
|
||||
const scrollFn = debounce(() => {
|
||||
// 加载img时才执行滚动监听判断是否可加载
|
||||
if (loadImg.value || isLoadError.value) return;
|
||||
const id = uid.value
|
||||
const query = uni.createSelectorQuery().in(instance);
|
||||
query.select('#' + id).boundingClientRect((data: any) => {
|
||||
if (!data) return;
|
||||
if (data.top - props.viewHeight < 0) {
|
||||
loadImg.value = !!props.imageSrc
|
||||
isLoadError.value = !loadImg.value
|
||||
}
|
||||
}).exec()
|
||||
}, 200)
|
||||
|
||||
const handleImgLoad = () => {
|
||||
showImg.value = true;
|
||||
setTimeout(() => {
|
||||
showTransition.value = true
|
||||
}, 50)
|
||||
}
|
||||
|
||||
const handleImgError = () => {
|
||||
isLoadError.value = true;
|
||||
}
|
||||
|
||||
const init = () => {
|
||||
nextTick(onScroll)
|
||||
}
|
||||
|
||||
const onScroll = () => {
|
||||
scrollFn();
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
init()
|
||||
uni.$on('scroll', scrollFn)
|
||||
onScroll()
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
uni.$off('scroll', scrollFn)
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.easy-load-image {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* 官方优化图片tips */
|
||||
image {
|
||||
will-change: transform;
|
||||
/* 渐变过渡效果处理 */
|
||||
&.origin-img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
opacity: 0.3;
|
||||
|
||||
&.show-transition {
|
||||
transition: opacity .5s;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
&.no-transition {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 加载失败、加载中的占位图样式控制 */
|
||||
.load-fail-img {
|
||||
height: 100%;
|
||||
background: url('./fail.png') no-repeat center;
|
||||
background-size: 50%;
|
||||
}
|
||||
|
||||
.loading-img {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* 转圈 */
|
||||
.spin-circle {
|
||||
background: url('./loading.png') no-repeat center;
|
||||
background-size: 60%;
|
||||
}
|
||||
|
||||
/* 动态灰色若隐若现 */
|
||||
.looming-gray {
|
||||
animation: looming-gray 1s infinite linear;
|
||||
background-color: #E3E3E3;
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
|
||||
@keyframes looming-gray {
|
||||
0% {
|
||||
background-color: #E3E3E3AA;
|
||||
}
|
||||
|
||||
50% {
|
||||
background-color: #E3E3E3;
|
||||
}
|
||||
|
||||
100% {
|
||||
background-color: #E3E3E3AA;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
BIN
uni-app/src/components/easy-image/fail.png
Normal file
BIN
uni-app/src/components/easy-image/fail.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.4 KiB |
BIN
uni-app/src/components/easy-image/loading.png
Normal file
BIN
uni-app/src/components/easy-image/loading.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.0 KiB |
253
uni-app/src/components/firework-effect/README.md
Normal file
253
uni-app/src/components/firework-effect/README.md
Normal file
@ -0,0 +1,253 @@
|
||||
# 🎆 简单烟花特效组件
|
||||
|
||||
一个轻量级的uni-app烟花特效组件,专注于提供最佳的用户体验。
|
||||
|
||||
## 📁 组件结构
|
||||
|
||||
```
|
||||
firework-effect/
|
||||
├── firework-effect.vue # 主要的烟花特效组件
|
||||
├── simple-firework.vue # 简单烟花特效组件
|
||||
└── README.md # 说明文档
|
||||
```
|
||||
|
||||
## ✨ 特效特点
|
||||
|
||||
### 🎇 简单烟花 (simple-firework)
|
||||
- 从底部发射,空中爆炸成彩色粒子
|
||||
- 自动发射,可定义持续时间
|
||||
- **不阻挡用户操作** - 设置了 `pointer-events: none`
|
||||
- **自动停止** - 默认30秒,可自定义
|
||||
- **无黑色阴影** - 使用透明背景
|
||||
- **兼容性最好** - 使用传统Canvas API
|
||||
|
||||
### 🧧 红包雨 (red-packet-rain)
|
||||
- **新增功能** ⭐
|
||||
- 从屏幕顶部飘落的红包雨效果
|
||||
- **点击获得金额** - 点击红包显示随机金额提示
|
||||
- **不阻挡用户操作** - 设置了 `pointer-events: none`,不会挡住底部按钮
|
||||
- **自定义时间** - 可设置持续时间
|
||||
- **自定义密度** - 可调节红包数量
|
||||
- **自定义速度** - 可调节下落速度
|
||||
- **自定义大小** - 可设置红包大小范围
|
||||
- **自定义金额** - 可设置金额范围
|
||||
- **精美动画** - 金额提示有向上飘动的动画效果
|
||||
- **自动绘制红包** - 无需图片资源,自动绘制精美红包
|
||||
|
||||
## 🎯 使用方法
|
||||
|
||||
### 1. 基本使用
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<view>
|
||||
<!-- 你的页面内容 -->
|
||||
<!-- 基础配置 -->
|
||||
<firework-effect
|
||||
ref="fireworkRef"
|
||||
:duration="20000"
|
||||
:no-trail="true"
|
||||
:red-packet-density="5"
|
||||
:red-packet-speed="3"
|
||||
:red-packet-min-size="80"
|
||||
:red-packet-max-size="120"
|
||||
:red-packet-min-amount="0.1"
|
||||
:red-packet-max-amount="50.0"
|
||||
:red-packet-clickable="true"
|
||||
></firework-effect>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import FireworkEffect from '@/components/firework-effect/firework-effect.vue';
|
||||
|
||||
const fireworkRef = ref(null);
|
||||
|
||||
// 显示简单烟花特效
|
||||
const showFireworks = () => {
|
||||
fireworkRef.value?.handleShowEffect({
|
||||
type: 'simple-firework',
|
||||
duration: 15000 // 15秒后自动停止,可选参数
|
||||
});
|
||||
};
|
||||
|
||||
// 显示红包雨特效(新功能)
|
||||
const showRedPacketRain = () => {
|
||||
fireworkRef.value?.handleShowEffect({
|
||||
type: 'red-packet-rain',
|
||||
duration: 10000 // 10秒后自动停止
|
||||
});
|
||||
};
|
||||
|
||||
// 使用默认30秒
|
||||
const showDefaultFireworks = () => {
|
||||
fireworkRef.value?.handleShowEffect({ type: 'simple-firework' });
|
||||
};
|
||||
</script>
|
||||
```
|
||||
|
||||
### 2. API 说明
|
||||
|
||||
#### FireworkEffect 组件
|
||||
|
||||
**Props:**
|
||||
- `duration`: 特效持续时间(毫秒),默认30000(30秒)
|
||||
- `noTrail`: 是否禁用拖尾效果(避免黑色阴影),默认true
|
||||
- `redPacketImages`: 红包图片数组,如:`['/static/images/hongbao1.png']`
|
||||
- `redPacketUseImages`: 是否使用图片模式,默认false(使用绘制模式)
|
||||
- `redPacketDensity`: 红包密度(每次生成数量),默认3
|
||||
- `redPacketSpeed`: 红包下落速度,默认2
|
||||
- `redPacketMinSize`: 红包最小大小(px),默认60
|
||||
- `redPacketMaxSize`: 红包最大大小(px),默认100
|
||||
- `redPacketMinAmount`: 红包最小金额,默认0.01
|
||||
- `redPacketMaxAmount`: 红包最大金额,默认10.00
|
||||
- `redPacketClickable`: 是否可点击红包,默认true
|
||||
|
||||
**方法:**
|
||||
- `handleShowEffect(params)`: 显示特效
|
||||
- `params.type`: 特效类型
|
||||
- `'simple-firework'`: 简单烟花特效
|
||||
- `'red-packet-rain'` 或 `'hongbao'`: 红包雨特效
|
||||
- `params.duration`: 自定义持续时间(可选)
|
||||
### 🚫 解决黑色阴影问题
|
||||
|
||||
如果您看到黑色阴影,请确保:
|
||||
|
||||
1. **设置 `no-trail="true"`**(默认已启用)
|
||||
2. **避免使用拖尾效果**,这会产生半透明覆盖
|
||||
|
||||
```vue
|
||||
<!-- ✅ 推荐:无阴影版本 -->
|
||||
<firework-effect :no-trail="true"></firework-effect>
|
||||
|
||||
<!-- ❌ 不推荐:可能有阴影 -->
|
||||
<firework-effect :no-trail="false"></firework-effect>
|
||||
```
|
||||
|
||||
### 📋 注意事项
|
||||
|
||||
1. 确保页面有足够的渲染性能
|
||||
2. 在低端设备上可能需要减少粒子数量
|
||||
3. 长时间运行建议定期清理资源
|
||||
4. 某些小程序平台可能对 Canvas API 有限制
|
||||
5. **只支持 `simple-firework` 类型**,不会阻挡用户操作
|
||||
|
||||
### 🎁 红包雨详细配置
|
||||
|
||||
#### 方式一:使用自定义图片
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<!-- 使用自定义红包图片 -->
|
||||
<firework-effect
|
||||
ref="fireworkRef"
|
||||
:red-packet-images="[
|
||||
'/static/images/hongbao1.png',
|
||||
'/static/images/hongbao2.png',
|
||||
'/static/images/hongbao3.png'
|
||||
]"
|
||||
:red-packet-use-images="true" <!-- 启用图片模式 -->
|
||||
:red-packet-min-size="100" <!-- 红包最小100px -->
|
||||
:red-packet-max-size="150" <!-- 红包最大150px -->
|
||||
:red-packet-min-amount="1.0" <!-- 最小1元 -->
|
||||
:red-packet-max-amount="100.0" <!-- 最大100元 -->
|
||||
:red-packet-density="6" <!-- 每次生成6个红包 -->
|
||||
:red-packet-speed="1.5" <!-- 较慢的下落速度 -->
|
||||
:red-packet-clickable="true" <!-- 启用点击功能 -->
|
||||
></firework-effect>
|
||||
</template>
|
||||
```
|
||||
|
||||
#### 方式二:使用代码绘制(默认)
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<!-- 使用代码绘制的红包 -->
|
||||
<firework-effect
|
||||
ref="fireworkRef"
|
||||
:red-packet-use-images="false" <!-- 使用绘制模式(默认) -->
|
||||
:red-packet-min-size="80" <!-- 红包最小80px -->
|
||||
:red-packet-max-size="120" <!-- 红包最大120px -->
|
||||
:red-packet-min-amount="0.1" <!-- 最小0.1元 -->
|
||||
:red-packet-max-amount="50.0" <!-- 最大50元 -->
|
||||
:red-packet-density="4" <!-- 每次生成4个红包 -->
|
||||
:red-packet-speed="2.5" <!-- 下落速度 -->
|
||||
:red-packet-clickable="true" <!-- 启用点击功能 -->
|
||||
></firework-effect>
|
||||
</template>
|
||||
```
|
||||
|
||||
#### 方式三:使用网络图片
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<!-- 使用网络红包图片 -->
|
||||
<firework-effect
|
||||
ref="fireworkRef"
|
||||
:red-packet-images="[
|
||||
'https://example.com/hongbao1.png',
|
||||
'https://example.com/hongbao2.png'
|
||||
]"
|
||||
:red-packet-use-images="true"
|
||||
:red-packet-min-size="100"
|
||||
:red-packet-max-size="150"
|
||||
></firework-effect>
|
||||
</template>
|
||||
```
|
||||
|
||||
### 🖼️ 红包图片要求
|
||||
|
||||
#### 图片格式
|
||||
- **PNG**(推荐,支持透明背景)
|
||||
- **JPG/JPEG**
|
||||
- **WebP**
|
||||
|
||||
#### 尺寸建议
|
||||
- **推荐尺寸**: 200x160px(宽高比5:4)
|
||||
- **最小尺寸**: 100x80px
|
||||
- **最大尺寸**: 400x320px
|
||||
|
||||
#### 文件大小
|
||||
- **建议**: 小于100KB
|
||||
- **最大**: 不超过500KB
|
||||
|
||||
#### 图片准备步骤
|
||||
1. 将红包图片放在 `uni-app/public/static/images/` 目录
|
||||
2. 文件命名建议:`hongbao1.png`, `hongbao2.png` 等
|
||||
3. 确保图片背景透明(PNG格式)
|
||||
4. 图片内容应该是完整的红包设计
|
||||
|
||||
### 📏 红包大小控制
|
||||
|
||||
```vue
|
||||
<!-- 小红包 -->
|
||||
:red-packet-min-size="60"
|
||||
:red-packet-max-size="80"
|
||||
|
||||
<!-- 中等红包 -->
|
||||
:red-packet-min-size="80"
|
||||
:red-packet-max-size="120"
|
||||
|
||||
<!-- 大红包 -->
|
||||
:red-packet-min-size="120"
|
||||
:red-packet-max-size="180"
|
||||
|
||||
<!-- 超大红包 -->
|
||||
:red-packet-min-size="150"
|
||||
:red-packet-max-size="200"
|
||||
```
|
||||
|
||||
### 🎯 红包点击功能
|
||||
|
||||
- **点击检测**: 自动检测用户点击的红包
|
||||
- **金额显示**: 显示随机金额(在设定范围内)
|
||||
- **动画效果**: 金额提示向上飘动并淡出
|
||||
- **防重复**: 已点击的红包变为半透明,不可重复点击
|
||||
- **响应式**: 支持触摸和鼠标点击
|
||||
|
||||
### 🔧 兼容性
|
||||
|
||||
- 支持 Vue 2 和 Vue 3
|
||||
- 支持 uni-app 各平台(H5、小程序、App)
|
||||
- 使用传统 Canvas API,兼容性最佳
|
||||
155
uni-app/src/components/firework-effect/firework-effect.vue
Normal file
155
uni-app/src/components/firework-effect/firework-effect.vue
Normal file
@ -0,0 +1,155 @@
|
||||
<template>
|
||||
<!-- 简单烟花特效 -->
|
||||
<simple-firework ref="simpleFireworkRef" :duration="fireworkDuration" :no-trail="props.noTrail"></simple-firework>
|
||||
<!-- 红包雨特效 -->
|
||||
<red-packet-rain
|
||||
ref="redPacketRainRef"
|
||||
:duration="fireworkDuration"
|
||||
:images="redPacketImages"
|
||||
:density="redPacketDensity"
|
||||
:speed="redPacketSpeed"
|
||||
:min-size="redPacketMinSize"
|
||||
:max-size="redPacketMaxSize"
|
||||
:min-amount="redPacketMinAmount"
|
||||
:max-amount="redPacketMaxAmount"
|
||||
:clickable="redPacketClickable"
|
||||
:use-images="redPacketUseImages"
|
||||
></red-packet-rain>
|
||||
</template>
|
||||
<script setup>
|
||||
// 导入特效组件
|
||||
import SimpleFirework from './simple-firework.vue'
|
||||
import RedPacketRain from './red-packet-rain.vue'
|
||||
// #ifdef VUE3
|
||||
import {
|
||||
ref,
|
||||
computed
|
||||
} from 'vue';
|
||||
// #endif
|
||||
// #ifndef VUE3
|
||||
import {
|
||||
ref,
|
||||
computed
|
||||
} from '@vue/composition-api';
|
||||
// #endif
|
||||
|
||||
// 定义props
|
||||
const props = defineProps({
|
||||
duration: {
|
||||
type: Number,
|
||||
default: 30000 // 默认30秒
|
||||
},
|
||||
noTrail: {
|
||||
type: Boolean,
|
||||
default: true // 默认无拖尾,避免黑色阴影
|
||||
},
|
||||
// 红包雨相关配置
|
||||
redPacketImages: {
|
||||
type: Array,
|
||||
default: () => ['/static/images/hongbao.png']
|
||||
},
|
||||
redPacketDensity: {
|
||||
type: Number,
|
||||
default: 3 // 每次生成的红包数量
|
||||
},
|
||||
redPacketSpeed: {
|
||||
type: Number,
|
||||
default: 2 // 红包下落速度
|
||||
},
|
||||
// 红包大小配置
|
||||
redPacketMinSize: {
|
||||
type: Number,
|
||||
default: 60 // 红包最小大小
|
||||
},
|
||||
redPacketMaxSize: {
|
||||
type: Number,
|
||||
default: 100 // 红包最大大小
|
||||
},
|
||||
// 红包金额配置
|
||||
redPacketMinAmount: {
|
||||
type: Number,
|
||||
default: 0.01 // 最小金额
|
||||
},
|
||||
redPacketMaxAmount: {
|
||||
type: Number,
|
||||
default: 10.00 // 最大金额
|
||||
},
|
||||
// 是否可点击
|
||||
redPacketClickable: {
|
||||
type: Boolean,
|
||||
default: true // 默认可点击
|
||||
},
|
||||
// 是否使用图片模式
|
||||
redPacketUseImages: {
|
||||
type: Boolean,
|
||||
default: false // 默认使用绘制模式
|
||||
}
|
||||
});
|
||||
|
||||
// 特效组件引用
|
||||
const simpleFireworkRef = ref(null)
|
||||
const redPacketRainRef = ref(null)
|
||||
|
||||
// 计算烟花持续时间
|
||||
const fireworkDuration = computed(() => props.duration)
|
||||
|
||||
const handleShowEffect = (params) => {
|
||||
const {
|
||||
type,
|
||||
duration
|
||||
} = params;
|
||||
|
||||
// 支持的特效类型
|
||||
if (type == 'simple-firework') {
|
||||
handleShowSimpleFirework(duration)
|
||||
} else if (type == 'red-packet-rain' || type == 'hongbao') {
|
||||
handleShowRedPacketRain(duration)
|
||||
} else {
|
||||
console.warn('🎆 支持的特效类型: simple-firework, red-packet-rain,当前类型:', type)
|
||||
}
|
||||
}
|
||||
|
||||
// 显示简单烟花特效
|
||||
const handleShowSimpleFirework = (customDuration) => {
|
||||
if (simpleFireworkRef.value) {
|
||||
simpleFireworkRef.value.startFireworks(customDuration);
|
||||
}
|
||||
}
|
||||
|
||||
// 显示红包雨特效
|
||||
const handleShowRedPacketRain = (customDuration) => {
|
||||
if (redPacketRainRef.value) {
|
||||
redPacketRainRef.value.startRedPacketRain(customDuration);
|
||||
}
|
||||
}
|
||||
|
||||
// 停止烟花特效
|
||||
const stopFireworks = () => {
|
||||
if (simpleFireworkRef.value && typeof simpleFireworkRef.value.stopFireworks === 'function') {
|
||||
simpleFireworkRef.value.stopFireworks();
|
||||
}
|
||||
}
|
||||
|
||||
// 停止红包雨特效
|
||||
const stopRedPacketRain = () => {
|
||||
if (redPacketRainRef.value && typeof redPacketRainRef.value.stopRedPacketRain === 'function') {
|
||||
redPacketRainRef.value.stopRedPacketRain();
|
||||
}
|
||||
}
|
||||
|
||||
// 停止所有特效
|
||||
const stopAllEffects = () => {
|
||||
stopFireworks();
|
||||
stopRedPacketRain();
|
||||
}
|
||||
|
||||
// 暴露方法给父组件
|
||||
defineExpose({
|
||||
handleShowEffect,
|
||||
stopFireworks,
|
||||
stopRedPacketRain,
|
||||
stopAllEffects
|
||||
})
|
||||
|
||||
|
||||
</script>
|
||||
682
uni-app/src/components/firework-effect/red-packet-rain.vue
Normal file
682
uni-app/src/components/firework-effect/red-packet-rain.vue
Normal file
@ -0,0 +1,682 @@
|
||||
<template>
|
||||
<view class="red-packet-container" v-if="isVisible">
|
||||
<canvas
|
||||
class="red-packet-canvas"
|
||||
canvas-id="redPacketRain"
|
||||
id="redPacketRain"
|
||||
></canvas>
|
||||
|
||||
|
||||
|
||||
<!-- 红包元素(图片模式或绘制模式) -->
|
||||
<view
|
||||
v-for="packet in redPackets"
|
||||
:key="packet.id"
|
||||
class="red-packet-item"
|
||||
:style="{
|
||||
left: packet.x + 'px',
|
||||
top: packet.y + 'px',
|
||||
width: packet.width + 'px',
|
||||
height: packet.height + 'px',
|
||||
transform: `rotate(${packet.rotation}rad)`,
|
||||
opacity: packet.opacity
|
||||
}"
|
||||
@click="handlePacketClick(packet, $event)"
|
||||
>
|
||||
<!-- 图片模式 -->
|
||||
<image
|
||||
v-if="props.useImages && packet.image"
|
||||
:src="packet.image"
|
||||
class="red-packet-image"
|
||||
mode="aspectFit"
|
||||
@load="onRedPacketImageLoad"
|
||||
@error="onRedPacketImageError"
|
||||
/>
|
||||
<!-- 绘制模式 -->
|
||||
<view
|
||||
v-else
|
||||
class="red-packet-drawn"
|
||||
:style="{
|
||||
width: '100%',
|
||||
height: '100%'
|
||||
}"
|
||||
>
|
||||
<view class="red-packet-body"></view>
|
||||
<view class="red-packet-text">福</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 预加载图片(隐藏) -->
|
||||
<image
|
||||
v-for="(imagePath, index) in props.images"
|
||||
:key="'preload-' + index"
|
||||
:src="imagePath"
|
||||
class="preload-image"
|
||||
@load="onImageLoad"
|
||||
@error="onImageError"
|
||||
/>
|
||||
|
||||
<!-- 金额提示弹窗 -->
|
||||
<view
|
||||
v-for="tip in amountTips"
|
||||
:key="tip.id"
|
||||
class="amount-tip"
|
||||
:style="{
|
||||
left: tip.x + 'px',
|
||||
top: tip.y + 'px',
|
||||
opacity: tip.opacity
|
||||
}"
|
||||
>
|
||||
<view class="tip-content">
|
||||
<text class="amount">+¥{{ tip.amount }}</text>
|
||||
<text class="label">{{ tip.label }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, getCurrentInstance, onUnmounted } from 'vue';
|
||||
|
||||
// 定义props
|
||||
const props = defineProps({
|
||||
duration: {
|
||||
type: Number,
|
||||
default: 30000 // 默认30秒
|
||||
},
|
||||
images: {
|
||||
type: Array,
|
||||
default: () => [
|
||||
// 可以是网络图片或本地图片路径
|
||||
// 'https://example.com/hongbao1.png',
|
||||
// '/static/images/hongbao.png',
|
||||
// 如果为空数组,则使用绘制的红包
|
||||
]
|
||||
},
|
||||
// 是否使用图片模式
|
||||
useImages: {
|
||||
type: Boolean,
|
||||
default: false // 默认使用绘制模式
|
||||
},
|
||||
density: {
|
||||
type: Number,
|
||||
default: 3 // 每次生成的红包数量
|
||||
},
|
||||
speed: {
|
||||
type: Number,
|
||||
default: 2 // 下落速度
|
||||
},
|
||||
// 红包大小配置
|
||||
minSize: {
|
||||
type: Number,
|
||||
default: 60 // 最小红包大小
|
||||
},
|
||||
maxSize: {
|
||||
type: Number,
|
||||
default: 100 // 最大红包大小
|
||||
},
|
||||
// 金额配置
|
||||
minAmount: {
|
||||
type: Number,
|
||||
default: 0.01 // 最小金额
|
||||
},
|
||||
maxAmount: {
|
||||
type: Number,
|
||||
default: 10.00 // 最大金额
|
||||
},
|
||||
// 是否启用点击功能
|
||||
clickable: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
});
|
||||
|
||||
const app = getCurrentInstance();
|
||||
let ctx = null;
|
||||
let animationId = null;
|
||||
let isRunning = false;
|
||||
let canvasWidth = 375;
|
||||
let canvasHeight = 667;
|
||||
let autoStopTimer = null;
|
||||
let createTimer = null;
|
||||
let loadedImages = new Map(); // 存储加载的图片
|
||||
let tipIdCounter = 0; // 提示ID计数器
|
||||
|
||||
const isVisible = ref(false);
|
||||
const amountTips = ref([]); // 金额提示数组
|
||||
const redPackets = ref([]); // 红包数组(响应式)
|
||||
|
||||
// 红包类
|
||||
class RedPacket {
|
||||
constructor(x, y, image) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
// 使用props中的大小范围
|
||||
this.width = props.minSize + Math.random() * (props.maxSize - props.minSize);
|
||||
this.height = this.width * 0.8; // 保持比例
|
||||
this.speed = props.speed + Math.random() * 2; // 随机速度
|
||||
this.rotation = Math.random() * 0.2 - 0.1; // 轻微旋转
|
||||
this.rotationSpeed = (Math.random() - 0.5) * 0.02;
|
||||
this.image = image;
|
||||
this.opacity = 0.9 + Math.random() * 0.1;
|
||||
// 随机金额
|
||||
this.amount = (props.minAmount + Math.random() * (props.maxAmount - props.minAmount)).toFixed(2);
|
||||
this.clicked = false; // 是否已被点击
|
||||
this.id = Date.now() + Math.random(); // 唯一ID
|
||||
}
|
||||
|
||||
update() {
|
||||
this.y += this.speed;
|
||||
this.rotation += this.rotationSpeed;
|
||||
|
||||
// 轻微的左右摆动
|
||||
this.x += Math.sin(this.y * 0.01) * 0.5;
|
||||
}
|
||||
|
||||
draw() {
|
||||
if (!ctx) return;
|
||||
|
||||
ctx.save();
|
||||
ctx.globalAlpha = this.opacity;
|
||||
ctx.translate(this.x + this.width / 2, this.y + this.height / 2);
|
||||
ctx.rotate(this.rotation);
|
||||
|
||||
// 根据模式选择绘制方式
|
||||
if (props.useImages && this.image) {
|
||||
console.log('🧧 使用图片模式绘制红包:', this.image);
|
||||
this.drawImage();
|
||||
} else {
|
||||
console.log('🧧 使用代码绘制红包');
|
||||
this.drawRedPacket();
|
||||
}
|
||||
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
// 绘制图片红包
|
||||
drawImage() {
|
||||
const halfWidth = this.width / 2;
|
||||
const halfHeight = this.height / 2;
|
||||
|
||||
try {
|
||||
if (this.image && typeof this.image === 'string') {
|
||||
console.log('🧧 绘制图片红包:', this.image);
|
||||
|
||||
// 在uni-app中,使用ctx.drawImage绘制图片
|
||||
// 参数:图片路径, x, y, width, height
|
||||
ctx.drawImage(this.image, -halfWidth, -halfHeight, this.width, this.height);
|
||||
|
||||
console.log('🧧 图片绘制完成');
|
||||
} else {
|
||||
console.log('🧧 图片无效,使用绘制模式');
|
||||
this.drawRedPacket();
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('🧧 图片绘制失败,降级到绘制模式:', error);
|
||||
this.drawRedPacket();
|
||||
}
|
||||
}
|
||||
|
||||
drawRedPacket() {
|
||||
const halfWidth = this.width / 2;
|
||||
const halfHeight = this.height / 2;
|
||||
|
||||
// 绘制红包主体(圆角矩形)
|
||||
ctx.setFillStyle('#ff4444');
|
||||
ctx.fillRect(-halfWidth, -halfHeight, this.width, this.height);
|
||||
|
||||
// 绘制金色装饰边框
|
||||
ctx.setStrokeStyle('#ffd700');
|
||||
ctx.setLineWidth(2);
|
||||
ctx.strokeRect(-halfWidth, -halfHeight, this.width, this.height);
|
||||
|
||||
// 绘制中间的"福"字或装饰
|
||||
ctx.setFillStyle('#ffd700');
|
||||
try {
|
||||
ctx.setFontSize(this.width * 0.4);
|
||||
ctx.setTextAlign('center');
|
||||
ctx.fillText('福', 0, this.height * 0.1);
|
||||
} catch (error) {
|
||||
// 如果文本绘制失败,绘制一个圆形装饰
|
||||
ctx.beginPath();
|
||||
ctx.arc(0, 0, this.width * 0.15, 0, 2 * Math.PI);
|
||||
ctx.fill();
|
||||
}
|
||||
|
||||
// 绘制顶部的蝴蝶结
|
||||
ctx.setFillStyle('#ffaa00');
|
||||
ctx.fillRect(-halfWidth * 0.6, -halfHeight, this.width * 0.6, this.height * 0.2);
|
||||
}
|
||||
|
||||
isOffScreen() {
|
||||
return this.y > canvasHeight + this.height;
|
||||
}
|
||||
|
||||
// 检测点击
|
||||
isClicked(touchX, touchY) {
|
||||
if (this.clicked) return false;
|
||||
|
||||
const centerX = this.x + this.width / 2;
|
||||
const centerY = this.y + this.height / 2;
|
||||
const distance = Math.sqrt(
|
||||
Math.pow(touchX - centerX, 2) + Math.pow(touchY - centerY, 2)
|
||||
);
|
||||
|
||||
return distance <= Math.max(this.width, this.height) / 2;
|
||||
}
|
||||
|
||||
// 标记为已点击
|
||||
markAsClicked() {
|
||||
this.clicked = true;
|
||||
// 点击后变为半透明
|
||||
this.opacity = 0.3;
|
||||
}
|
||||
}
|
||||
|
||||
// 图片加载成功事件
|
||||
const onImageLoad = (event) => {
|
||||
console.log('🧧 预加载图片成功:', event.target.src);
|
||||
};
|
||||
|
||||
// 图片加载失败事件
|
||||
const onImageError = (event) => {
|
||||
console.error('🧧 预加载图片失败:', event.target.src);
|
||||
};
|
||||
|
||||
// 红包图片加载成功事件
|
||||
const onRedPacketImageLoad = () => {
|
||||
// 图片加载成功,无需处理
|
||||
};
|
||||
|
||||
// 红包图片加载失败事件
|
||||
const onRedPacketImageError = () => {
|
||||
// 图片加载失败,将使用绘制模式
|
||||
};
|
||||
|
||||
// 加载图片(简化版,依赖image标签预加载)
|
||||
const loadImages = async () => {
|
||||
if (!props.useImages || props.images.length === 0) {
|
||||
loadedImages.set('default', 'default');
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
// 直接将图片路径添加到loadedImages中
|
||||
// 实际的预加载由template中的image标签完成
|
||||
for (const imagePath of props.images) {
|
||||
loadedImages.set(imagePath, imagePath);
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
// 初始化Canvas
|
||||
const initCanvas = () => {
|
||||
try {
|
||||
ctx = uni.createCanvasContext('redPacketRain', app.proxy);
|
||||
|
||||
if (ctx) {
|
||||
// 获取屏幕尺寸
|
||||
const systemInfo = uni.getSystemInfoSync();
|
||||
canvasWidth = systemInfo.windowWidth || 375;
|
||||
canvasHeight = systemInfo.windowHeight || 667;
|
||||
|
||||
// 初始化透明背景
|
||||
ctx.setFillStyle('rgba(0, 0, 0, 0)');
|
||||
ctx.fillRect(0, 0, canvasWidth, canvasHeight);
|
||||
ctx.draw();
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// 创建红包
|
||||
const createRedPackets = () => {
|
||||
if (!isRunning) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (let i = 0; i < props.density; i++) {
|
||||
const x = Math.random() * (canvasWidth - 100);
|
||||
const y = -100 - Math.random() * 200; // 从屏幕上方开始
|
||||
|
||||
let image = null;
|
||||
|
||||
if (props.useImages && loadedImages.size > 0) {
|
||||
// 随机选择一张图片
|
||||
const allKeys = Array.from(loadedImages.keys());
|
||||
const imageKeys = allKeys.filter(key => key !== 'default');
|
||||
|
||||
if (imageKeys.length > 0) {
|
||||
const randomImage = imageKeys[Math.floor(Math.random() * imageKeys.length)];
|
||||
image = loadedImages.get(randomImage);
|
||||
} else {
|
||||
// 如果没有过滤出图片,直接使用第一个
|
||||
if (allKeys.length > 0) {
|
||||
const firstKey = allKeys[0];
|
||||
image = loadedImages.get(firstKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const packet = new RedPacket(x, y, image);
|
||||
redPackets.value.push(packet);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// 动画循环
|
||||
const animate = () => {
|
||||
if (!isRunning) return;
|
||||
|
||||
try {
|
||||
// 更新红包位置(HTML元素会自动重新渲染)
|
||||
redPackets.value = redPackets.value.filter(packet => {
|
||||
packet.update();
|
||||
return !packet.isOffScreen();
|
||||
});
|
||||
|
||||
// 继续动画
|
||||
if (isRunning) {
|
||||
setTimeout(animate, 16); // 约60fps
|
||||
}
|
||||
} catch (error) {
|
||||
if (isRunning) {
|
||||
setTimeout(animate, 50);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 开始动画
|
||||
const startAnimation = () => {
|
||||
isRunning = true;
|
||||
animate();
|
||||
};
|
||||
|
||||
// 停止动画
|
||||
const stopAnimation = () => {
|
||||
isRunning = false;
|
||||
if (animationId) {
|
||||
clearTimeout(animationId);
|
||||
animationId = null;
|
||||
}
|
||||
};
|
||||
|
||||
// 开始创建红包的定时器
|
||||
const startCreating = () => {
|
||||
if (createTimer) {
|
||||
clearInterval(createTimer);
|
||||
}
|
||||
|
||||
// 立即创建一批红包
|
||||
createRedPackets();
|
||||
|
||||
// 每隔一段时间创建新红包
|
||||
createTimer = setInterval(() => {
|
||||
if (isRunning && createTimer) { // 确保在停止创建后不再生成新红包
|
||||
createRedPackets();
|
||||
}
|
||||
}, 1000); // 每秒创建新红包
|
||||
};
|
||||
|
||||
// 停止创建红包
|
||||
const stopCreating = () => {
|
||||
if (createTimer) {
|
||||
clearInterval(createTimer);
|
||||
createTimer = null;
|
||||
}
|
||||
};
|
||||
|
||||
// 启动红包雨
|
||||
const startRedPacketRain = async (customDuration) => {
|
||||
// 显示组件
|
||||
isVisible.value = true;
|
||||
|
||||
// 加载图片
|
||||
await loadImages();
|
||||
|
||||
// 初始化Canvas(可选,仅用于背景)
|
||||
initCanvas();
|
||||
|
||||
// 开始动画和创建红包
|
||||
startAnimation();
|
||||
startCreating();
|
||||
|
||||
// 设置自动停止定时器
|
||||
const duration = customDuration || props.duration;
|
||||
|
||||
if (autoStopTimer) {
|
||||
clearTimeout(autoStopTimer);
|
||||
}
|
||||
|
||||
autoStopTimer = setTimeout(() => {
|
||||
stopRedPacketRain();
|
||||
}, duration);
|
||||
};
|
||||
|
||||
// 停止红包雨
|
||||
const stopRedPacketRain = () => {
|
||||
// 停止创建新红包,但继续动画让现有红包落完
|
||||
stopCreating();
|
||||
|
||||
// 清除自动停止定时器
|
||||
if (autoStopTimer) {
|
||||
clearTimeout(autoStopTimer);
|
||||
autoStopTimer = null;
|
||||
}
|
||||
|
||||
// 等待现有红包全部落下后再停止动画和隐藏组件
|
||||
const waitForPacketsToFall = () => {
|
||||
if (redPackets.value.length === 0) {
|
||||
// 所有红包都落下了,停止动画
|
||||
stopAnimation();
|
||||
amountTips.value = [];
|
||||
|
||||
// 延迟隐藏组件
|
||||
setTimeout(() => {
|
||||
isVisible.value = false;
|
||||
}, 1000);
|
||||
} else {
|
||||
// 还有红包在飘落,继续等待
|
||||
setTimeout(waitForPacketsToFall, 500);
|
||||
}
|
||||
};
|
||||
|
||||
waitForPacketsToFall();
|
||||
};
|
||||
|
||||
|
||||
|
||||
// 处理红包图片点击
|
||||
const handlePacketClick = (packet, event) => {
|
||||
if (!props.clickable || packet.clicked) return;
|
||||
|
||||
console.log('🧧 点击红包图片,金额:', packet.amount);
|
||||
|
||||
// 标记红包为已点击
|
||||
packet.markAsClicked();
|
||||
|
||||
// 获取点击位置
|
||||
const touchX = event.currentTarget.offsetLeft + packet.width / 2;
|
||||
const touchY = event.currentTarget.offsetTop + packet.height / 2;
|
||||
|
||||
// 显示金额提示
|
||||
showAmountTip(touchX, touchY, packet.amount);
|
||||
};
|
||||
|
||||
// 显示金额提示
|
||||
const showAmountTip = (x, y, amount) => {
|
||||
const tipId = ++tipIdCounter;
|
||||
const tip = {
|
||||
id: tipId,
|
||||
x: x - 50, // 居中显示
|
||||
y: y - 30,
|
||||
amount: amount,
|
||||
label: '恭喜获得',
|
||||
opacity: 1
|
||||
};
|
||||
|
||||
amountTips.value.push(tip);
|
||||
|
||||
// 动画效果:向上移动并淡出
|
||||
let animationStep = 0;
|
||||
const animate = () => {
|
||||
animationStep++;
|
||||
const progress = animationStep / 60; // 60帧动画
|
||||
|
||||
if (progress >= 1) {
|
||||
// 动画结束,移除提示
|
||||
const index = amountTips.value.findIndex(t => t.id === tipId);
|
||||
if (index !== -1) {
|
||||
amountTips.value.splice(index, 1);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 更新提示位置和透明度
|
||||
const tipIndex = amountTips.value.findIndex(t => t.id === tipId);
|
||||
if (tipIndex !== -1) {
|
||||
amountTips.value[tipIndex].y = y - 30 - progress * 50; // 向上移动50px
|
||||
amountTips.value[tipIndex].opacity = 1 - progress; // 淡出
|
||||
}
|
||||
|
||||
setTimeout(animate, 16); // 约60fps
|
||||
};
|
||||
|
||||
animate();
|
||||
};
|
||||
|
||||
// 组件卸载时清理资源
|
||||
onUnmounted(() => {
|
||||
console.log('🧧 红包雨组件卸载,清理资源');
|
||||
stopRedPacketRain();
|
||||
});
|
||||
|
||||
// 暴露方法
|
||||
defineExpose({
|
||||
startRedPacketRain,
|
||||
stopRedPacketRain
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.red-packet-container {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 998; /* 比烟花稍低的层级 */
|
||||
pointer-events: none; /* 容器本身不阻挡点击事件 */
|
||||
|
||||
.red-packet-canvas {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none; /* Canvas 背景不阻挡点击事件 */
|
||||
}
|
||||
|
||||
|
||||
|
||||
.test-red-packet {
|
||||
border: 2px solid red;
|
||||
}
|
||||
|
||||
.red-packet-image {
|
||||
position: absolute;
|
||||
pointer-events: auto; /* 红包图片可以被点击 */
|
||||
z-index: 999;
|
||||
transition: opacity 0.3s ease;
|
||||
|
||||
&:active {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
}
|
||||
|
||||
.red-packet-item {
|
||||
position: absolute;
|
||||
pointer-events: auto; /* 红包元素可以被点击 */
|
||||
z-index: 999;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.red-packet-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.red-packet-drawn {
|
||||
position: relative;
|
||||
background: linear-gradient(135deg, #ff4444, #cc0000);
|
||||
border: 2px solid #ffd700;
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -8px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 60%;
|
||||
height: 12px;
|
||||
background: linear-gradient(135deg, #ffaa00, #ff8800);
|
||||
border-radius: 6px;
|
||||
border: 1px solid #ffd700;
|
||||
}
|
||||
}
|
||||
|
||||
.red-packet-text {
|
||||
color: #ffd700;
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.preload-image {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.amount-tip {
|
||||
position: fixed;
|
||||
pointer-events: none; /* 金额提示不阻挡点击事件 */
|
||||
z-index: 1000; /* 确保金额提示在最上层显示 */
|
||||
transition: all 0.3s ease-out;
|
||||
|
||||
.tip-content {
|
||||
background: linear-gradient(135deg, #ff6b6b, #ff8e8e);
|
||||
color: white;
|
||||
padding: 8px 16px;
|
||||
border-radius: 20px;
|
||||
box-shadow: 0 4px 12px rgba(255, 107, 107, 0.4);
|
||||
text-align: center;
|
||||
min-width: 100px;
|
||||
|
||||
.amount {
|
||||
display: block;
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.label {
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
opacity: 0.9;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
378
uni-app/src/components/firework-effect/simple-firework.vue
Normal file
378
uni-app/src/components/firework-effect/simple-firework.vue
Normal file
@ -0,0 +1,378 @@
|
||||
<template>
|
||||
<view class="firework-container" v-if="isVisible">
|
||||
<canvas
|
||||
class="firework-canvas"
|
||||
canvas-id="simpleFirework"
|
||||
id="simpleFirework"
|
||||
></canvas>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
// #ifdef VUE3
|
||||
import {
|
||||
ref,
|
||||
onMounted,
|
||||
onUnmounted,
|
||||
getCurrentInstance
|
||||
} from 'vue';
|
||||
// #endif
|
||||
// #ifndef VUE3
|
||||
import {
|
||||
ref,
|
||||
onMounted,
|
||||
onUnmounted,
|
||||
getCurrentInstance
|
||||
} from '@vue/composition-api';
|
||||
// #endif
|
||||
|
||||
// 定义props
|
||||
const props = defineProps({
|
||||
duration: {
|
||||
type: Number,
|
||||
default: 30000 // 默认30秒
|
||||
},
|
||||
autoStart: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
noTrail: {
|
||||
type: Boolean,
|
||||
default: true // 默认不显示拖尾,避免黑色阴影
|
||||
}
|
||||
});
|
||||
|
||||
const app = getCurrentInstance();
|
||||
let ctx = null;
|
||||
let animationId = null;
|
||||
let fireworks = [];
|
||||
let isRunning = false;
|
||||
let canvasWidth = 375;
|
||||
let canvasHeight = 667;
|
||||
let autoStopTimer = null;
|
||||
let autoLaunchTimer = null;
|
||||
|
||||
const isVisible = ref(false);
|
||||
|
||||
// 获取随机颜色
|
||||
const getRandomColor = () => {
|
||||
const colors = ['#ff0000', '#00ff00', '#0000ff', '#ffff00', '#ff00ff', '#00ffff'];
|
||||
return colors[Math.floor(Math.random() * colors.length)];
|
||||
};
|
||||
|
||||
// 简化的烟花类
|
||||
class SimpleFirework {
|
||||
constructor(startX, startY, targetX, targetY) {
|
||||
this.x = startX;
|
||||
this.y = startY;
|
||||
this.targetX = targetX;
|
||||
this.targetY = targetY;
|
||||
this.color = getRandomColor();
|
||||
this.particles = [];
|
||||
this.exploded = false;
|
||||
this.speed = 5;
|
||||
|
||||
// 计算方向
|
||||
const dx = targetX - startX;
|
||||
const dy = targetY - startY;
|
||||
const distance = Math.sqrt(dx * dx + dy * dy);
|
||||
this.vx = (dx / distance) * this.speed;
|
||||
this.vy = (dy / distance) * this.speed;
|
||||
}
|
||||
|
||||
update() {
|
||||
if (!this.exploded) {
|
||||
this.x += this.vx;
|
||||
this.y += this.vy;
|
||||
|
||||
// 检查是否到达目标
|
||||
const distance = Math.sqrt((this.targetX - this.x) ** 2 + (this.targetY - this.y) ** 2);
|
||||
if (distance < 20 || this.y <= this.targetY) {
|
||||
this.explode();
|
||||
this.exploded = true;
|
||||
}
|
||||
} else {
|
||||
// 更新粒子
|
||||
this.particles = this.particles.filter(particle => {
|
||||
particle.x += particle.vx;
|
||||
particle.y += particle.vy;
|
||||
particle.vy += 0.1; // 重力
|
||||
particle.alpha -= 0.02;
|
||||
return particle.alpha > 0;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
explode() {
|
||||
// 创建粒子
|
||||
for (let i = 0; i < 20; i++) {
|
||||
const angle = (Math.PI * 2 * i) / 20;
|
||||
const speed = Math.random() * 4 + 2;
|
||||
this.particles.push({
|
||||
x: this.x,
|
||||
y: this.y,
|
||||
vx: Math.cos(angle) * speed,
|
||||
vy: Math.sin(angle) * speed,
|
||||
alpha: 1,
|
||||
color: this.color
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
draw() {
|
||||
if (!ctx) return;
|
||||
|
||||
try {
|
||||
if (!this.exploded) {
|
||||
// 绘制发射的烟花
|
||||
if (ctx.setFillStyle) {
|
||||
ctx.setFillStyle(this.color);
|
||||
ctx.beginPath();
|
||||
ctx.arc(this.x, this.y, 3, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
}
|
||||
} else {
|
||||
// 绘制爆炸粒子
|
||||
this.particles.forEach(particle => {
|
||||
if (ctx.setGlobalAlpha && ctx.setFillStyle) {
|
||||
ctx.setGlobalAlpha(particle.alpha);
|
||||
ctx.setFillStyle(particle.color);
|
||||
ctx.beginPath();
|
||||
ctx.arc(particle.x, particle.y, 2, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
}
|
||||
});
|
||||
if (ctx.setGlobalAlpha) {
|
||||
ctx.setGlobalAlpha(1);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
// 绘制错误,忽略
|
||||
}
|
||||
}
|
||||
|
||||
isDead() {
|
||||
return this.exploded && this.particles.length === 0;
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化Canvas
|
||||
const initCanvas = () => {
|
||||
try {
|
||||
// 尝试创建Canvas上下文
|
||||
ctx = uni.createCanvasContext('simpleFirework', app.proxy);
|
||||
|
||||
if (ctx) {
|
||||
// 获取屏幕尺寸
|
||||
const systemInfo = uni.getSystemInfoSync();
|
||||
canvasWidth = systemInfo.windowWidth || 375;
|
||||
canvasHeight = systemInfo.windowHeight || 667;
|
||||
|
||||
// 初始化透明背景
|
||||
ctx.setFillStyle('rgba(255, 255, 255, 0.01)');
|
||||
ctx.fillRect(0, 0, canvasWidth, canvasHeight);
|
||||
|
||||
// 绘制一个测试点来验证Canvas是否工作
|
||||
ctx.setFillStyle('#ff0000');
|
||||
ctx.fillRect(canvasWidth / 2, canvasHeight / 2, 10, 10);
|
||||
|
||||
ctx.draw(); // 立即绘制初始背景
|
||||
|
||||
// 开始动画(延迟更长时间确保Canvas完全准备好)
|
||||
setTimeout(() => {
|
||||
startAnimation();
|
||||
}, 1000);
|
||||
} else {
|
||||
// 重试机制
|
||||
setTimeout(() => {
|
||||
initCanvas();
|
||||
}, 1000);
|
||||
}
|
||||
} catch (error) {
|
||||
// 重试机制
|
||||
setTimeout(() => {
|
||||
initCanvas();
|
||||
}, 1000);
|
||||
}
|
||||
};
|
||||
|
||||
// 动画循环
|
||||
const animate = () => {
|
||||
if (!isRunning || !ctx) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 根据props决定清除方式
|
||||
if (props.noTrail) {
|
||||
// 完全清除,无拖尾效果,无阴影
|
||||
ctx.clearRect && ctx.clearRect(0, 0, canvasWidth, canvasHeight);
|
||||
// 如果不支持clearRect,使用极淡的白色
|
||||
if (!ctx.clearRect) {
|
||||
ctx.setFillStyle('rgba(255, 255, 255, 0.02)');
|
||||
ctx.fillRect(0, 0, canvasWidth, canvasHeight);
|
||||
}
|
||||
} else {
|
||||
// 半透明清除,产生拖尾效果
|
||||
ctx.setFillStyle('rgba(255, 255, 255, 0.08)');
|
||||
ctx.fillRect(0, 0, canvasWidth, canvasHeight);
|
||||
}
|
||||
|
||||
// 更新和绘制烟花
|
||||
fireworks = fireworks.filter(firework => {
|
||||
firework.update();
|
||||
firework.draw();
|
||||
return !firework.isDead();
|
||||
});
|
||||
|
||||
// 绘制到画布
|
||||
ctx.draw();
|
||||
|
||||
// 继续动画
|
||||
setTimeout(animate, 16); // 约60fps
|
||||
} catch (error) {
|
||||
if (isRunning) {
|
||||
setTimeout(animate, 50);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 开始动画
|
||||
const startAnimation = () => {
|
||||
isRunning = true;
|
||||
animate();
|
||||
};
|
||||
|
||||
// 停止动画
|
||||
const stopAnimation = () => {
|
||||
isRunning = false;
|
||||
fireworks = [];
|
||||
};
|
||||
|
||||
// 创建烟花
|
||||
const createFirework = (targetX, targetY) => {
|
||||
if (!ctx) {
|
||||
return;
|
||||
}
|
||||
|
||||
const startX = Math.random() * canvasWidth;
|
||||
const startY = canvasHeight;
|
||||
const finalTargetX = targetX || Math.random() * canvasWidth;
|
||||
const finalTargetY = targetY || Math.random() * canvasHeight * 0.5;
|
||||
|
||||
fireworks.push(new SimpleFirework(startX, startY, finalTargetX, finalTargetY));
|
||||
};
|
||||
|
||||
// 自动发射
|
||||
const startAutoLaunch = () => {
|
||||
if (autoLaunchTimer) {
|
||||
clearInterval(autoLaunchTimer);
|
||||
}
|
||||
|
||||
autoLaunchTimer = setInterval(() => {
|
||||
if (isRunning && autoLaunchTimer) { // 确保在停止发射后不再生成新烟花
|
||||
createFirework();
|
||||
}
|
||||
}, 800); // 每0.8秒发射一个烟花
|
||||
};
|
||||
|
||||
const stopAutoLaunch = () => {
|
||||
if (autoLaunchTimer) {
|
||||
clearInterval(autoLaunchTimer);
|
||||
autoLaunchTimer = null;
|
||||
}
|
||||
};
|
||||
|
||||
// 移除点击和触摸处理,避免阻挡用户操作
|
||||
|
||||
// 启动烟花
|
||||
const startFireworks = (customDuration) => {
|
||||
// 显示组件
|
||||
isVisible.value = true;
|
||||
|
||||
if (!isRunning) {
|
||||
startAnimation();
|
||||
}
|
||||
|
||||
// 立即创建一个测试烟花
|
||||
createFirework();
|
||||
|
||||
// 开始自动发射
|
||||
startAutoLaunch();
|
||||
|
||||
// 设置自动停止定时器
|
||||
const duration = customDuration || props.duration;
|
||||
|
||||
if (autoStopTimer) {
|
||||
clearTimeout(autoStopTimer);
|
||||
}
|
||||
|
||||
autoStopTimer = setTimeout(() => {
|
||||
stopFireworks();
|
||||
}, duration);
|
||||
};
|
||||
|
||||
// 停止烟花
|
||||
const stopFireworks = () => {
|
||||
// 停止自动发射新烟花,但继续动画让现有烟花完成
|
||||
stopAutoLaunch();
|
||||
|
||||
// 清除自动停止定时器
|
||||
if (autoStopTimer) {
|
||||
clearTimeout(autoStopTimer);
|
||||
autoStopTimer = null;
|
||||
}
|
||||
|
||||
// 等待现有烟花全部完成后再停止动画和隐藏组件
|
||||
const waitForFireworksToFinish = () => {
|
||||
if (fireworks.length === 0) {
|
||||
// 所有烟花都完成了,停止动画
|
||||
stopAnimation();
|
||||
|
||||
// 延迟隐藏组件
|
||||
setTimeout(() => {
|
||||
isVisible.value = false;
|
||||
}, 1000);
|
||||
} else {
|
||||
// 还有烟花在进行,继续等待
|
||||
setTimeout(waitForFireworksToFinish, 500);
|
||||
}
|
||||
};
|
||||
|
||||
waitForFireworksToFinish();
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
setTimeout(() => {
|
||||
initCanvas();
|
||||
}, 1000);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
stopFireworks();
|
||||
});
|
||||
|
||||
defineExpose({
|
||||
startFireworks,
|
||||
stopFireworks,
|
||||
createFirework
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.firework-container {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none; /* 不阻挡用户操作 */
|
||||
z-index: 999; /* 降低层级,避免遮挡重要UI */
|
||||
|
||||
.firework-canvas {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none; /* Canvas也不接收事件,完全不阻挡操作 */
|
||||
}
|
||||
}
|
||||
</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] text-[#999]" @blur="bindNickname" @click="checkAuth($event, 'nickname')"/>
|
||||
</u-form-item>
|
||||
<u-form-item :label="t('mobile')" prop="mobile" :border-bottom="true" v-if="isBindMobile">
|
||||
<input type="mobile" v-model="formData.mobile" :disabled="true" v-if="formData.mobile">
|
||||
<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" >
|
||||
<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>
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
// https://uniapp.dcloud.io/frame?id=renderjs
|
||||
|
||||
// 与wxs的me实例一致
|
||||
var me = {}
|
||||
const me = {};
|
||||
|
||||
// 初始化window对象的touch事件 (仅初始化一次)
|
||||
if(window && !window.$mescrollRenderInit){
|
||||
@ -19,20 +19,20 @@ if(window && !window.$mescrollRenderInit){
|
||||
window.addEventListener('touchmove', function(e){
|
||||
if (me.disabled()) return;
|
||||
if (me.getScrollTop() > 0) return; // 需在顶部下拉,才禁止bounce
|
||||
|
||||
var curPoint = me.getPoint(e); // 当前点
|
||||
var moveY = curPoint.y - me.startPoint.y; // 和起点比,移动的距离,大于0向下拉,小于0向上拉
|
||||
|
||||
const curPoint = me.getPoint(e); // 当前点
|
||||
const moveY = curPoint.y - me.startPoint.y; // 和起点比,移动的距离,大于0向下拉,小于0向上拉
|
||||
// 向下拉
|
||||
if (moveY > 0) {
|
||||
// 可下拉的条件
|
||||
if (!me.isDownScrolling && !me.optDown.isLock && (!me.isUpScrolling || (me.isUpScrolling && me.isUpBoth))) {
|
||||
|
||||
// 只有touch在mescroll的view上面,才禁止bounce
|
||||
var el = e.target;
|
||||
var isMescrollTouch = false;
|
||||
while (el && el.tagName && el.tagName !== 'UNI-PAGE-BODY' && el.tagName != "BODY") {
|
||||
var cls = el.classList;
|
||||
if (cls && cls.contains('mescroll-render-touch')) {
|
||||
let el = e.target;
|
||||
let isMescrollTouch = false;
|
||||
while (el && el.tagName && el.tagName !== 'UNI-PAGE-BODY' && el.tagName != "BODY") {
|
||||
const cls = el.classList;
|
||||
if (cls && cls.contains('mescroll-render-touch')) {
|
||||
isMescrollTouch = true
|
||||
break;
|
||||
}
|
||||
|
||||
436
uni-app/src/components/music/music-container.vue
Normal file
436
uni-app/src/components/music/music-container.vue
Normal file
@ -0,0 +1,436 @@
|
||||
<template>
|
||||
<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>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, watch, onMounted, onBeforeUnmount, computed } from 'vue';
|
||||
import { img } from '@/utils/common';
|
||||
|
||||
// 组件属性
|
||||
const props = defineProps({
|
||||
// 音乐文件地址
|
||||
src: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 是否自动播放
|
||||
autoplay: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 是否循环播放
|
||||
loop: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 自定义唱片图片
|
||||
discImage: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
defaultBgColor: {
|
||||
type: String,
|
||||
default: '#B0B0B0'
|
||||
},
|
||||
defaultTextColor: {
|
||||
type: String,
|
||||
default: '#FFFFFF'
|
||||
},
|
||||
defaultRounded: {
|
||||
type: Number,
|
||||
default: 20
|
||||
}
|
||||
});
|
||||
|
||||
// 状态管理
|
||||
const isPlaying = ref(false); // 实际播放状态
|
||||
const userWantsPlay = ref(false); // 用户期望状态
|
||||
const lastPlayPosition = ref(0); // 播放进度
|
||||
const needUserInteraction = ref(false); // 是否需要用户交互激活
|
||||
let audioContext: any = null;
|
||||
let currentPage: any = null;
|
||||
let backgroundTimer: NodeJS.Timeout | null = null;
|
||||
|
||||
// 样式计算
|
||||
const defaultStyleCss = computed(() => {
|
||||
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;`;
|
||||
}
|
||||
return style;
|
||||
});
|
||||
|
||||
// 创建音频实例
|
||||
const createAudioContext = () => {
|
||||
audioContext = uni.createInnerAudioContext();
|
||||
if (!audioContext) {
|
||||
console.error('无法创建音频实例');
|
||||
return;
|
||||
}
|
||||
|
||||
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(() => {
|
||||
return {
|
||||
'--color': `${props.defaultTextColor}`
|
||||
}
|
||||
})
|
||||
|
||||
// 暴露方法
|
||||
defineExpose({
|
||||
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;
|
||||
// 交互遮罩层:覆盖整个组件,用于捕获用户点击激活播放
|
||||
.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@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>
|
||||
|
||||
69
uni-app/src/components/online-service/online-service.vue
Normal file
69
uni-app/src/components/online-service/online-service.vue
Normal file
@ -0,0 +1,69 @@
|
||||
<template>
|
||||
<view @touchmove.prevent.stop>
|
||||
<u-popup class="popup-type" mode="center" :show="servicesDataShow" @close="servicesDataShow = false" bgColor="transparent" overlayOpacity="0.8">
|
||||
<view @touchmove.prevent.stop class="flex flex-colitems-center">
|
||||
<view class="bg-[#ffff] rounded-[34rpx]">
|
||||
<view class="text-center text-[30rpx] mt-[40rpx] mb-[40rpx]">联系在线客服</view>
|
||||
<scroll-view scroll-y="true" class="w-[570rpx] pb-[60rpx] max-h-[580rpx] overflow-y-auto">
|
||||
<view class="flex flex-col items-center justify-center px-[30rpx]" v-if="data.customer_phone">
|
||||
<text class="text-[45rpx]">{{ data.customer_phone }}</text>
|
||||
<view class="rounded-[30rpx] mt-[30rpx] bg-[#EF000C] text-[#fff] h-[60rpx] leading-[60rpx] w-[240rpx] text-center text-[24rpx]" @click="makePhoneCallFn(data.customer_phone)">一键拨打</view>
|
||||
</view>
|
||||
<view v-if="data.customer_phone" class="w-full h-[1rpx] bg-dashed-style my-[40rpx]"></view>
|
||||
<view class="px-[24rpx] flex flex-col items-center justify-center" v-if="data.customer_qrcode">
|
||||
<view class="border border-[#eee] border-solid rounded-[10rpx] border-[1rpx] w-[240rpx] h-[240rpx] flex items-center justify-center">
|
||||
<image class="w-[226rpx] h-[226rpx]" :src="img(data.customer_qrcode)" mode="widthFix" />
|
||||
</view>
|
||||
<view class="text-[26rpx] text-[#333] mt-[20rpx]">扫描二维码添加客服</view>
|
||||
</view>
|
||||
<view class="px-[50rpx] mt-[16rpx] text-[22rpx] text-[#999] leading-[1.5]" style="text-indent: 2em;">{{ data.customer_guided_words }}</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
<!-- <view @click="servicesDataShow = false" class="mt-[50rpx] nc-iconfont nc-icon-cuohaoV6xx1 !text-[50rpx] text-[#fff]"></view> -->
|
||||
</view>
|
||||
</u-popup>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue';
|
||||
import { redirect, img } from '@/utils/common'
|
||||
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
default: {}
|
||||
}
|
||||
})
|
||||
|
||||
const servicesDataShow = ref<boolean>(false)
|
||||
const data = computed(() => {
|
||||
return props.data;
|
||||
})
|
||||
|
||||
const open = () => {
|
||||
servicesDataShow.value = true;
|
||||
}
|
||||
|
||||
const makePhoneCallFn = (data: any)=>{
|
||||
uni.makePhoneCall({
|
||||
phoneNumber: data,
|
||||
success: (res) => {
|
||||
},
|
||||
fail: (res) => {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
open
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.bg-dashed-style {
|
||||
background: linear-gradient(to right, #eee 60%, transparent 40%);
|
||||
background-size: 24rpx 100%; /* 控制线段长度(20rpx)和间隔 */
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
</style>
|
||||
@ -44,6 +44,10 @@ const prop = defineProps({
|
||||
default () {
|
||||
return []
|
||||
}
|
||||
},
|
||||
reformat: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
})
|
||||
|
||||
@ -165,6 +169,7 @@ const confirmPay = () => {
|
||||
uni.$on('checkIsReturnAfterPayment', () => {
|
||||
const data = uni.getStorageSync('paymenting')
|
||||
if (uni.getStorageSync('paymenting')) {
|
||||
|
||||
redirect({
|
||||
url: '/app/pages/pay/result',
|
||||
param: {
|
||||
@ -220,8 +225,18 @@ const open = (tradeType: string, tradeId: number, payReturn: string = '', scene:
|
||||
}
|
||||
|
||||
const emits = defineEmits(['close', 'confirm'])
|
||||
|
||||
const toPayResult = () => {
|
||||
emits('confirm')
|
||||
// console.log(prop.reformat)
|
||||
if(prop.reformat){
|
||||
redirect({
|
||||
url: prop.reformat,
|
||||
mode: 'redirectTo'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
redirect({
|
||||
url: '/app/pages/pay/result',
|
||||
param: { trade_type: payInfo.value?.trade_type, trade_id: payInfo.value?.trade_id },
|
||||
@ -237,7 +252,8 @@ const handleClose = () => {
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
open
|
||||
open,
|
||||
payInfo
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
@ -23,7 +23,7 @@
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
<view class="share-footer" @click="sharePopupClose">
|
||||
<text>取消分享</text>
|
||||
<text class="text-[#333]">取消分享</text>
|
||||
</view>
|
||||
</view>
|
||||
</u-popup>
|
||||
@ -45,6 +45,7 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { img, copy } from '@/utils/common';
|
||||
import useSystemStore from "@/stores/system";
|
||||
|
||||
const props = defineProps({
|
||||
copyUrl: { // 例 "/wap/addon/shop_fenxiao/pages/goods"
|
||||
@ -93,12 +94,10 @@ const closeDialog = () => {
|
||||
|
||||
const shareTop: any = ref(0)
|
||||
/************ 获取微信头部-start ****************/
|
||||
// 获取系统状态栏的高度
|
||||
let menuButtonInfo: any = {};
|
||||
const systemStore = useSystemStore()
|
||||
// 如果是小程序,获取右上角胶囊的尺寸信息,避免导航栏右侧内容与胶囊重叠(支付宝小程序非本API,尚未兼容)
|
||||
// #ifdef MP-WEIXIN || MP-BAIDU || MP-TOUTIAO || MP-QQ
|
||||
menuButtonInfo = uni.getMenuButtonBoundingClientRect();
|
||||
shareTop.value = menuButtonInfo.top + menuButtonInfo.height + 'px';
|
||||
shareTop.value = systemStore.menuButtonInfo.top + systemStore.menuButtonInfo.height + 'px';
|
||||
// #endif
|
||||
/************ 获取微信头部-end ****************/
|
||||
|
||||
|
||||
@ -1,14 +1,14 @@
|
||||
<template>
|
||||
<!-- 分享弹窗 -->
|
||||
<view @touchmove.prevent.stop class="share-popup">
|
||||
<u-popup :show="sharePopupShow" type="bottom" @close="sharePopupClose" overlayOpacity="0.8">
|
||||
<u-popup :show="sharePopupShow" @close="sharePopupClose" overlayOpacity="0.8">
|
||||
<view @touchmove.prevent.stop>
|
||||
<view class="poster-img-wrap" :style="{'top': shareTop}">
|
||||
<image v-if="isPosterAnimation" class="poster-animation" :src="img('addon/shop/poster_animation.gif')" mode="aspectFit"/>
|
||||
<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>
|
||||
@ -57,10 +80,12 @@
|
||||
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: {
|
||||
type: String || Number,
|
||||
type: [String, Number],
|
||||
default: 0
|
||||
},
|
||||
posterType: {
|
||||
@ -78,6 +103,10 @@ const props = defineProps({
|
||||
copyUrlParam: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
isPreload: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
})
|
||||
|
||||
@ -116,7 +145,7 @@ const isPosterImg = ref(false)
|
||||
// 获取分享海报
|
||||
const poster = ref('');
|
||||
const loadPoster = () => {
|
||||
if (poster.value) {
|
||||
if (poster.value && props.isPreload) {
|
||||
// 预加载
|
||||
isPosterAnimation.value = false;
|
||||
isPosterImg.value = true;
|
||||
@ -198,14 +227,22 @@ const savePoster = () => {
|
||||
}
|
||||
// #endif
|
||||
|
||||
// #ifdef APP-PLUS
|
||||
const shareSession = () => {
|
||||
useShare().onShareAppMessage();
|
||||
}
|
||||
|
||||
const shareWechatMoments = () => {
|
||||
useShare().onShareTimeline();
|
||||
}
|
||||
// #endif
|
||||
|
||||
const shareTop: any = ref(0)
|
||||
/************ 获取微信头部-start ****************/
|
||||
// 获取系统状态栏的高度
|
||||
let menuButtonInfo: any = {};
|
||||
const systemStore = useSystemStore()
|
||||
// 如果是小程序,获取右上角胶囊的尺寸信息,避免导航栏右侧内容与胶囊重叠(支付宝小程序非本API,尚未兼容)
|
||||
// #ifdef MP-WEIXIN || MP-BAIDU || MP-TOUTIAO || MP-QQ
|
||||
menuButtonInfo = uni.getMenuButtonBoundingClientRect();
|
||||
shareTop.value = menuButtonInfo.top + menuButtonInfo.height + 'px';
|
||||
shareTop.value = systemStore.menuButtonInfo.top + systemStore.menuButtonInfo.height + 'px';
|
||||
// #endif
|
||||
/************ 获取微信头部-end ****************/
|
||||
|
||||
@ -223,9 +260,10 @@ defineExpose({
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.share-popup {
|
||||
:deep(.u-transition), :deep(.u-popup__content) {
|
||||
background-color: transparent;
|
||||
}
|
||||
// 苹果手机下要白色背景
|
||||
// :deep(.u-transition), :deep(.u-popup__content) {
|
||||
// background-color: transparent;
|
||||
// }
|
||||
|
||||
.share-content {
|
||||
border-top-left-radius: 40rpx;
|
||||
@ -260,6 +298,11 @@ defineExpose({
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
|
||||
image {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
}
|
||||
|
||||
.iconfont {
|
||||
font-size: 80rpx;
|
||||
|
||||
@ -127,6 +127,7 @@ if (!props.addon) {
|
||||
, { deep: true, immediate: true }
|
||||
)
|
||||
}
|
||||
|
||||
watch(
|
||||
() => useConfigStore().tabbarList,
|
||||
(newValue, oldValue) => {
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
<template>
|
||||
<view class="ns-navbar-wrap" v-if="diyStore.mode !='decorate' && topStatusBarData" :class="topStatusBarData.style">
|
||||
<view class="u-navbar" :class="{'fixed': props.scrollBool != -1, 'absolute': props.scrollBool == -1}" :style="{ backgroundColor: bgColor}">
|
||||
<view class="u-navbar z-100" :class="{'fixed': props.scrollBool != -1, 'absolute': props.scrollBool == -1}" :style="{ backgroundColor: bgColor}">
|
||||
<view class="navbar-inner" :style="{ width: '100%', height: placeholderHeight + 'px' }">
|
||||
<view v-if="topStatusBarData.style == 'style-1'" class="content-wrap" :class="[topStatusBarData.textAlign]" :style="navbarInnerStyle">
|
||||
<view v-if="isBack" class="back-wrap -ml-[16rpx] text-[26px] nc-iconfont nc-icon-zuoV6xx" :style="{ color: titleTextColor }" @tap="goBack"></view>
|
||||
<view class="back-wrap -ml-[16rpx] text-[26px] nc-iconfont nc-icon-zuoV6xx" :class="{'!text-transparent': !isBackShow}" :style="{ color: titleTextColor }" @tap="goBack"></view>
|
||||
<view class="title-wrap" :style="styleOneFontSize">{{ data.title }}</view>
|
||||
</view>
|
||||
<view v-if="topStatusBarData.style == 'style-2'" class="content-wrap" :style="navbarInnerStyle" @click="diyStore.toRedirect(topStatusBarData.link)">
|
||||
<view v-if="isBack" class="back-wrap -ml-[16rpx] text-[26px] nc-iconfont nc-icon-zuoV6xx" :style="{ color: titleTextColor }" @tap="goBack"></view>
|
||||
<view class="back-wrap -ml-[16rpx] text-[26px] nc-iconfont nc-icon-zuoV6xx" :style="{ color: titleTextColor }" :class="{'!text-transparent': !isBackShow}" @tap="goBack"></view>
|
||||
<view class="title-wrap" :style="{ color: topStatusBarData.textColor }">
|
||||
<view>
|
||||
<image :src="img(topStatusBarData.imgUrl)" mode="heightFix"/>
|
||||
@ -17,7 +17,7 @@
|
||||
</view>
|
||||
|
||||
<view v-if="topStatusBarData.style == 'style-3'" :style="navbarInnerStyle" class="content-wrap">
|
||||
<view v-if="isBack" class="back-wrap -ml-[16rpx] text-[26px] nc-iconfont nc-icon-zuoV6xx" :style="{ color: titleTextColor }" @tap="goBack"></view>
|
||||
<view v-if="isBackShow" class="back-wrap -ml-[16rpx] text-[26px] nc-iconfont nc-icon-zuoV6xx" :style="{ color: titleTextColor }" @tap="goBack" :class="{'!text-transparent': !isBackShow}"></view>
|
||||
<view class="title-wrap" @click="diyStore.toRedirect(topStatusBarData.link)">
|
||||
<image :src="img(topStatusBarData.imgUrl)" mode="heightFix"/>
|
||||
</view>
|
||||
@ -29,7 +29,7 @@
|
||||
</view>
|
||||
|
||||
<view v-if="topStatusBarData.style == 'style-4'" :style="navbarInnerStyle" class="content-wrap">
|
||||
<view v-if="isBack" class="back-wrap -ml-[16rpx] text-[26px] nc-iconfont nc-icon-zuoV6xx" :style="{ color: titleTextColor }" @tap="goBack"></view>
|
||||
<view class="back-wrap -ml-[16rpx] text-[26px] nc-iconfont nc-icon-zuoV6xx" :style="{ color: titleTextColor }" @tap="goBack" :class="{'!text-transparent': !isBackShow}"></view>
|
||||
<text class="nc-iconfont nc-icon-dizhiguanliV6xx text-[28rpx]" :style="{ color: topStatusBarData.textColor }"></text>
|
||||
<view class="title-wrap" @click.stop="locationVal.reposition()" :style="{ color: topStatusBarData.textColor }" v-if="systemStore.diyAddressInfo">{{ systemStore.diyAddressInfo.community }}</view>
|
||||
<view class="title-wrap" @click.stop="locationVal.reposition()" :style="{ color: topStatusBarData.textColor }" v-else>{{ systemStore.defaultPositionAddress }}</view>
|
||||
@ -50,10 +50,8 @@ import useSystemStore from '@/stores/system';
|
||||
import useDiyStore from '@/app/stores/diy';
|
||||
import { useLocation } from '@/hooks/useLocation'
|
||||
|
||||
// 获取系统状态栏的高度
|
||||
let systemInfo = uni.getSystemInfoSync();
|
||||
let platform = systemInfo.platform;
|
||||
const systemStore = useSystemStore();
|
||||
const systemStore = useSystemStore()
|
||||
let platform = systemStore.systemInfo.platform;
|
||||
|
||||
const diyStore = useDiyStore();
|
||||
|
||||
@ -84,13 +82,20 @@ const props = defineProps({
|
||||
isFill: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 临时处理方法,强制显示
|
||||
mustFill: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
|
||||
// 控制-定位后是否对导航栏进行填充
|
||||
const isFill = computed(() => {
|
||||
let bool = true;
|
||||
if (imageAdsSameScreen.value) {
|
||||
if(props.mustFill){
|
||||
return props.mustFill
|
||||
}else if (diyStore.imageAdsSameScreen) {
|
||||
bool = false;
|
||||
} else {
|
||||
bool = props.isFill;
|
||||
@ -109,11 +114,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;'; //右边留边
|
||||
@ -125,6 +130,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;
|
||||
})
|
||||
|
||||
@ -188,47 +196,36 @@ if (componentsScrollVal) {
|
||||
/******************************* 存储滚动值-end ***********************/
|
||||
|
||||
/******************************* 返回按钮-start ***********************/
|
||||
// const isBackShow = ref(false);
|
||||
let pages = getCurrentPages();
|
||||
const isBackShow = computed(() => {
|
||||
let bool = false;
|
||||
if (props.isBack && pages.length > 1) {
|
||||
bool = true;
|
||||
}
|
||||
return bool;
|
||||
})
|
||||
|
||||
// 返回按钮的函数
|
||||
const goBack = () => {
|
||||
// 兼容小程序,未登录状态下点击某个功能跳转到登录页,不登录无法返回的情况
|
||||
if (pages.length === 1) {
|
||||
if (pages[0].route === 'app/pages/auth/index') {
|
||||
uni.getStorage({
|
||||
key: 'loginBack',
|
||||
success: (res: any) => {
|
||||
res ? redirect(
|
||||
{
|
||||
...res.data,
|
||||
mode: 'redirectTo'
|
||||
}
|
||||
) : redirect({ url: '/app/pages/index/index', mode: 'switchTab' })
|
||||
},
|
||||
fail: (res) => {
|
||||
redirect({ url: '/app/pages/index/index', mode: 'switchTab' })
|
||||
}
|
||||
});
|
||||
} else if (typeof props.customBack === 'function') {
|
||||
props.customBack();
|
||||
} else {
|
||||
redirect({ url: '/app/pages/index/index', mode: 'switchTab' });
|
||||
}
|
||||
if(!isBackShow.value) return;
|
||||
// 如果自定义了点击返回按钮的函数,则执行,否则执行返回逻辑
|
||||
if (typeof props.customBack === 'function') {
|
||||
props.customBack();
|
||||
} else {
|
||||
// 如果自定义了点击返回按钮的函数,则执行,否则执行返回逻辑
|
||||
if (typeof props.customBack === 'function') {
|
||||
props.customBack();
|
||||
} else {
|
||||
uni.navigateBack();
|
||||
}
|
||||
uni.navigateBack();
|
||||
}
|
||||
}
|
||||
/******************************* 返回按钮-end ***********************/
|
||||
|
||||
// 微信胶囊宽度+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;
|
||||
})
|
||||
|
||||
@ -238,12 +235,14 @@ const instance = getCurrentInstance();
|
||||
// #ifdef MP
|
||||
let statusBarHeight = systemStore.menuButtonInfo.height + systemStore.menuButtonInfo.top + 8;
|
||||
placeholderHeight.value = statusBarHeight || 0;
|
||||
systemStore.setTopTabbar({ height: placeholderHeight.value })
|
||||
// #endif
|
||||
const navbarPlaceholderHeight = () => {
|
||||
nextTick(() => {
|
||||
const query = uni.createSelectorQuery().in(instance);
|
||||
query.select('.ns-navbar-wrap .u-navbar .content-wrap').boundingClientRect(data => {
|
||||
placeholderHeight.value = data ? data.height : 0;
|
||||
systemStore.setTopTabbar({ height: placeholderHeight.value })
|
||||
diyStore.$patch((state) => {
|
||||
state.topTabarHeight = placeholderHeight.value
|
||||
})
|
||||
@ -261,20 +260,10 @@ const locationVal = useLocation(isOpenLocation);
|
||||
locationVal.onLoad();
|
||||
locationVal.init();
|
||||
/************** 定位-end ****************/
|
||||
let imageAdsSameScreen = ref(false);
|
||||
onMounted(() => {
|
||||
navbarPlaceholderHeight();
|
||||
// if (pages.length > 1) {
|
||||
// isBackShow.value = true;
|
||||
// // 兼容小程序,未登录状态下点击某个功能跳转到登录页,不登录无法返回的情况
|
||||
// }else if(pages.length == 1 && pages[0].route == 'app/pages/auth/index'){
|
||||
// isBackShow.value = true;
|
||||
// }
|
||||
// 刷新定位
|
||||
locationVal.refresh();
|
||||
|
||||
// 图文导航开启沉浸式且导航栏开启时,导航栏不占位
|
||||
imageAdsSameScreen.value = uni.getStorageSync('imageAdsSameScreen') || false;
|
||||
});
|
||||
|
||||
// 页面onShow调用时,也会触发改方法
|
||||
@ -386,9 +375,12 @@ defineExpose({
|
||||
|
||||
&.style-3 {
|
||||
.content-wrap {
|
||||
// #ifdef APP-PLUS
|
||||
padding-bottom: 20rpx;
|
||||
// #endif
|
||||
.title-wrap {
|
||||
height: 60rpx;
|
||||
max-width: 170rpx;
|
||||
height: 46rpx;
|
||||
max-width: 300rpx;
|
||||
flex: initial;
|
||||
text-align: center;
|
||||
margin-right: 10rpx;
|
||||
|
||||
@ -65,12 +65,12 @@ const props = defineProps({
|
||||
// 骨架的背景色
|
||||
bgColor: {
|
||||
type: String,
|
||||
default: '' // #EAEDF5
|
||||
default: '#EAEDF5' // #EAEDF5
|
||||
},
|
||||
// 骨架的动画高亮背景色
|
||||
highlightBgColor: {
|
||||
type: String,
|
||||
default: '' // #F9FAFF
|
||||
default: '#F9FAFF' // #F9FAFF
|
||||
},
|
||||
// 自定义配置
|
||||
config: {
|
||||
@ -220,7 +220,9 @@ const addUnit = (value = 'auto', unit = 'px') => {
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@mixin background {
|
||||
background: linear-gradient(90deg, var(--bgColor) 25%, var(--highlightBgColor) 37%, var(--bgColor) 50%);
|
||||
//background: linear-gradient(90deg, var(--bgColor) 25%, var(--highlightBgColor) 37%, var(--bgColor) 50%);
|
||||
background: linear-gradient(90deg, #EAEDF5 25%, #F9FAFF 37%, #EAEDF5 50%);
|
||||
|
||||
background-size: 400% 100%;
|
||||
}
|
||||
|
||||
@ -261,7 +263,8 @@ const addUnit = (value = 'auto', unit = 'px') => {
|
||||
|
||||
.fade-out {
|
||||
opacity: 0;
|
||||
animation: fadeOutAnim var(--fadeOutTime);
|
||||
//animation: fadeOutAnim var(--fadeOutTime);
|
||||
animation: fadeOutAnim 0.5s;
|
||||
}
|
||||
|
||||
@keyframes fadeOutAnim {
|
||||
@ -274,7 +277,8 @@ const addUnit = (value = 'auto', unit = 'px') => {
|
||||
}
|
||||
|
||||
.animate {
|
||||
animation: skeletonAnim var(--animateTime) ease infinite;
|
||||
//animation: skeletonAnim var(--animateTime) ease infinite;
|
||||
animation: skeletonAnim 1.8s ease infinite;
|
||||
}
|
||||
|
||||
@keyframes skeletonAnim {
|
||||
|
||||
@ -38,12 +38,12 @@ export function useDiy(params: any = {}) {
|
||||
const isShowTopTabbar = ref(false);
|
||||
|
||||
const pageStyle = () => {
|
||||
var style = '';
|
||||
let style = '';
|
||||
if (data.value.global.pageStartBgColor) {
|
||||
if (data.value.global.pageStartBgColor && data.value.global.pageEndBgColor) style += `background:linear-gradient(${ data.value.global.pageGradientAngle },${ data.value.global.pageStartBgColor },${ data.value.global.pageEndBgColor });`;
|
||||
else style += 'background-color:' + data.value.global.pageStartBgColor + ';';
|
||||
}
|
||||
if (data.value.global.bottomTabBarSwitch) {
|
||||
if (data.value.global.bottomTabBar && data.value.global.bottomTabBar.isShow) {
|
||||
style += 'min-height:calc(100vh - 50px);';
|
||||
} else {
|
||||
style += 'min-height:calc(100vh);';
|
||||
@ -115,6 +115,10 @@ export function useDiy(params: any = {}) {
|
||||
let sources = JSON.parse(requestData.value); // todo diy的结构应该后台处理好,前端就不需要再转换了
|
||||
|
||||
diyData.global = sources.global;
|
||||
|
||||
diyData.global.id = requestData.id;
|
||||
diyData.global.type = requestData.type;
|
||||
|
||||
// 用于区分微页面之间弹窗的id
|
||||
if (diyData.global.popWindow && diyData.global.popWindow.show) {
|
||||
diyData.global.popWindow.id = requestData.id;
|
||||
@ -123,6 +127,7 @@ export function useDiy(params: any = {}) {
|
||||
diyData.value = sources.value;
|
||||
diyData.value.forEach((item: any, index) => {
|
||||
item.pageStyle = '';
|
||||
item.componentIsShow = true // 是否显示
|
||||
if (item.pageStartBgColor) {
|
||||
if (item.pageStartBgColor && item.pageEndBgColor) item.pageStyle += `background:linear-gradient(${ item.pageGradientAngle },${ item.pageStartBgColor },${ item.pageEndBgColor });`;
|
||||
else item.pageStyle += 'background-color:' + item.pageStartBgColor + ';';
|
||||
@ -146,7 +151,6 @@ export function useDiy(params: any = {}) {
|
||||
uni.setNavigationBarTitle({
|
||||
title: diyData.title
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
loading.value = false;
|
||||
@ -189,9 +193,9 @@ export function useDiy(params: any = {}) {
|
||||
}
|
||||
|
||||
// 监听页面卸载
|
||||
const onUnloadLifeCycle = () => {
|
||||
const onUnloadLifeCycle = (callback: any = null) => {
|
||||
onUnload(() => {
|
||||
|
||||
if (callback) callback()
|
||||
})
|
||||
}
|
||||
|
||||
@ -201,6 +205,7 @@ export function useDiy(params: any = {}) {
|
||||
if (e.scrollTop > 0) {
|
||||
diyStore.scrollTop = e.scrollTop;
|
||||
}
|
||||
// uni.$emit('scroll')
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -41,12 +41,12 @@ export function useDiyForm(params: any = {}) {
|
||||
const isShowTopTabbar = ref(false);
|
||||
|
||||
const pageStyle = () => {
|
||||
var style = '';
|
||||
let style = '';
|
||||
if (data.value.global.pageStartBgColor) {
|
||||
if (data.value.global.pageStartBgColor && data.value.global.pageEndBgColor) style += `background:linear-gradient(${ data.value.global.pageGradientAngle },${ data.value.global.pageStartBgColor },${ data.value.global.pageEndBgColor });`;
|
||||
else style += 'background-color:' + data.value.global.pageStartBgColor + ';';
|
||||
}
|
||||
if (data.value.global.bottomTabBarSwitch) {
|
||||
if (data.value.global.bottomTabBar && data.value.global.bottomTabBar.isShow) {
|
||||
style += 'min-height:calc(100vh - 50px);';
|
||||
} else {
|
||||
style += 'min-height:calc(100vh);';
|
||||
@ -140,13 +140,14 @@ export function useDiyForm(params: any = {}) {
|
||||
if (requestData.value) {
|
||||
diyData.pageMode = requestData.mode;
|
||||
diyData.title = requestData.title;
|
||||
diyData.type = requestData.type;
|
||||
|
||||
diyStore.id = requestData.form_id;
|
||||
let sources = requestData.value;
|
||||
// 匹配缓存,赋值
|
||||
let diyFormStorage = uni.getStorageSync('diyFormStorage_' + diyStore.id)
|
||||
if (diyFormStorage) {
|
||||
var date = new Date();
|
||||
const date = new Date();
|
||||
let currentTime: any = parseInt(date.getTime() / 1000); // 存储信息 5分钟内有效,过期后将重新获取定位信息
|
||||
if (diyFormStorage.validTime > currentTime) {
|
||||
if (diyFormStorage.components) {
|
||||
@ -176,6 +177,7 @@ export function useDiyForm(params: any = {}) {
|
||||
diyData.global = sources.global;
|
||||
diyData.value = sources.value;
|
||||
diyData.value.forEach((item: any, index) => {
|
||||
item.componentIsShow = true // 是否显示
|
||||
if (item.isHidden) {
|
||||
// 隐藏组件
|
||||
diyData.value.splice(index, 1)
|
||||
|
||||
@ -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,
|
||||
@ -184,7 +187,7 @@ export function useLocation(isOpenLocation: any) {
|
||||
const locationStorage = () => {
|
||||
let data = uni.getStorageSync('location_address');
|
||||
if (data) {
|
||||
var date = new Date();
|
||||
const date = new Date();
|
||||
if (systemStore.mapConfig.valid_time > 0) {
|
||||
data.is_expired = (date.getTime() / 1000) > data.valid_time; // 是否过期
|
||||
} else {
|
||||
|
||||
@ -4,7 +4,9 @@ import {
|
||||
updateWeappOpenid,
|
||||
updateWechatOpenid,
|
||||
wechatUser,
|
||||
wechatUserLogin
|
||||
wechatUserLogin,
|
||||
updateWechatOpenidByH5
|
||||
|
||||
} from '@/app/api/auth'
|
||||
import { getWechatAuthCode } from '@/app/api/system'
|
||||
import useMemberStore from '@/stores/member'
|
||||
@ -22,13 +24,14 @@ export function useLogin() {
|
||||
const systemStore = useSystemStore()
|
||||
|
||||
// #ifdef MP-WEIXIN
|
||||
if (!uni.getStorageSync('autoLoginLock') && uni.getStorageSync('openid') && config.login.is_bind_mobile) {
|
||||
|
||||
if (!uni.getStorageSync('autoLoginLock') && config.login.is_bind_mobile) {
|
||||
uni.setStorageSync('isBindMobile', true) // 强制绑定手机号标识
|
||||
}
|
||||
// #endif
|
||||
|
||||
// #ifdef H5
|
||||
if (!uni.getStorageSync('autoLoginLock') && isWeixinBrowser() && uni.getStorageSync('openid') && config.login.is_bind_mobile) {
|
||||
if (!uni.getStorageSync('autoLoginLock') && isWeixinBrowser() && config.login.is_bind_mobile) {
|
||||
uni.setStorageSync('isBindMobile', true) // 强制绑定手机号标识
|
||||
}
|
||||
// #endif
|
||||
@ -37,11 +40,11 @@ export function useLogin() {
|
||||
|
||||
// #ifdef MP-WEIXIN
|
||||
if (config.login.is_username && !config.login.is_mobile && !config.login.is_auth_register) {
|
||||
redirect({ url: '/app/pages/auth/login', param: { type: 'username' }, mode: 'redirectTo' })
|
||||
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) {
|
||||
uni.showToast({ title: '商家未开启登录注册', icon: 'none' })
|
||||
} else {
|
||||
redirect({ url: '/app/pages/auth/index', mode: 'redirectTo' })
|
||||
redirect({ url: '/app/pages/auth/index' })
|
||||
}
|
||||
// #endif
|
||||
|
||||
@ -49,20 +52,20 @@ export function useLogin() {
|
||||
if (isWeixinBrowser()) {
|
||||
// 微信浏览器
|
||||
if (config.login.is_username && !config.login.is_mobile && !config.login.is_auth_register) {
|
||||
redirect({ url: '/app/pages/auth/login', param: { type: 'username' }, mode: 'redirectTo' })
|
||||
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) {
|
||||
uni.showToast({ title: '商家未开启登录注册', icon: 'none' })
|
||||
} else {
|
||||
redirect({ url: '/app/pages/auth/index', mode: 'redirectTo' })
|
||||
redirect({ url: '/app/pages/auth/index' })
|
||||
}
|
||||
} else {
|
||||
// 普通浏览器
|
||||
if (config.login.is_username && !config.login.is_mobile) {
|
||||
redirect({ url: '/app/pages/auth/login', param: { type: 'username' }, mode: 'redirectTo' })
|
||||
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', mode: 'redirectTo' })
|
||||
redirect({ url: '/app/pages/auth/index' })
|
||||
}
|
||||
}
|
||||
// #endif
|
||||
@ -102,9 +105,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, () => {
|
||||
@ -151,7 +154,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) => {
|
||||
@ -192,8 +194,12 @@ export function useLogin() {
|
||||
// 强制获取昵称和头像,先存储起来
|
||||
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(res.data.nickname){
|
||||
uni.setStorageSync('nickname', res.data.nickname)
|
||||
}
|
||||
if(res.data.avatar){
|
||||
uni.setStorageSync('avatar', res.data.avatar)
|
||||
}
|
||||
|
||||
// 开启绑定手机号标识
|
||||
if (uni.getStorageSync('isBindMobile')) {
|
||||
@ -209,14 +215,16 @@ export function useLogin() {
|
||||
})
|
||||
}
|
||||
}).catch((err) => {
|
||||
if (err.msg == -1) {
|
||||
if (err.code == -1) {
|
||||
getAuthCode({ scopes: 'snsapi_userinfo' })
|
||||
} else {
|
||||
uni.showToast({ title: err.msg, icon: 'none' })
|
||||
}
|
||||
})
|
||||
// #endif
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录普通账号后修改openid
|
||||
* @param code
|
||||
@ -317,17 +325,30 @@ export function useLogin() {
|
||||
query.code && (delete query.code)
|
||||
Object.keys(query).length && (url += uni.$u.queryParams(query))
|
||||
}
|
||||
|
||||
// // 强制授权加强制绑定手机号定义URL
|
||||
// if(uni.getStorageSync('mandatory_Mobile')){
|
||||
// url = `${ location.origin }${ location.pathname }`
|
||||
// }
|
||||
getWechatAuthCode({
|
||||
url,
|
||||
scopes: params.scopes
|
||||
}).then((res: any) => {
|
||||
uni.setStorageSync('wechat_login_back', true) // 微信公众号手动授权登录回调标识
|
||||
// location.replace(res.data.url);
|
||||
// uni.setStorageSync('mandatory_Mobile', true) // 微信授权登录+强制绑定手机号标识
|
||||
// location.replace(res.data.url);
|
||||
location.href = res.data.url
|
||||
})
|
||||
|
||||
// #endif
|
||||
|
||||
}
|
||||
|
||||
const updateWechatOpenidForH5 = (wx_openid: any) => {
|
||||
if(!wx_openid) return;
|
||||
updateWechatOpenidByH5({ wx_openid }).then(res => {
|
||||
const memberInfo = useMemberStore().info
|
||||
memberInfo && memberInfo.wx_openid && uni.setStorageSync('openid', memberInfo.wx_openid)
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
@ -335,6 +356,7 @@ export function useLogin() {
|
||||
handleLoginBack,
|
||||
authLogin,
|
||||
updateOpenid,
|
||||
getAuthCode
|
||||
getAuthCode,
|
||||
updateWechatOpenidForH5
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,8 +8,10 @@ import wechat from '@/utils/wechat'
|
||||
// #endif
|
||||
|
||||
export const useShare = () => {
|
||||
var wechatOptions: any = {};
|
||||
var weappOptions: any = {};
|
||||
let wechatOptions: any = {};
|
||||
let weappOptions: any = {};
|
||||
|
||||
const systemStore = useSystemStore()
|
||||
|
||||
const wechatInit = () => {
|
||||
if (!isWeixinBrowser()) return;
|
||||
@ -43,116 +45,215 @@ export const useShare = () => {
|
||||
if (currRoute() == '' || currRoute().indexOf('app/pages/index/close') != -1 || currRoute().indexOf('app/pages/index/nosite') != -1) return;
|
||||
|
||||
let queryStr = getQuery();
|
||||
|
||||
let h5Link = '';
|
||||
// #ifdef H5
|
||||
|
||||
let h5Link = location.origin + location.pathname + (queryStr.length > 0 ? '?' + queryStr.join('&') : '');
|
||||
|
||||
h5Link = location.origin + location.pathname + (queryStr.length > 0 ? '?' + queryStr.join('&') : '');
|
||||
wechatOptions = {
|
||||
link: h5Link
|
||||
}
|
||||
// #endif
|
||||
|
||||
// #ifdef APP-PLUS
|
||||
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;
|
||||
uni.setStorageSync('weappOptions', weappOptions)
|
||||
// #endif
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
getShareInfo({
|
||||
route: '/' + currRoute(),
|
||||
params: JSON.stringify(currShareRoute().params)
|
||||
}).then((res: any) => {
|
||||
|
||||
let data = res.data;
|
||||
|
||||
// #ifdef H5
|
||||
let wechat = data.wechat;
|
||||
if (wechat) {
|
||||
wechatOptions.title = wechat.title
|
||||
wechatOptions.desc = wechat.desc
|
||||
wechatOptions.imgUrl = wechat.url ? img(wechat.url) : ''
|
||||
} else {
|
||||
wechatOptions.title = document.title;
|
||||
wechatOptions.desc = ''
|
||||
}
|
||||
wechatShare()
|
||||
// #endif
|
||||
|
||||
// #ifdef MP-WEIXIN
|
||||
let weapp = data.weapp;
|
||||
if (weapp) {
|
||||
weappOptions.title = weapp.title
|
||||
weappOptions.imageUrl = weapp.url ? img(weapp.url) : ''
|
||||
}
|
||||
// #endif
|
||||
|
||||
uni.setStorageSync('weappOptions', weappOptions)
|
||||
// #endif
|
||||
}
|
||||
}
|
||||
getShareInfo({
|
||||
route: '/' + currRoute(),
|
||||
params: JSON.stringify(currShareRoute().params)
|
||||
}).then((res: any) => {
|
||||
let data = res.data;
|
||||
|
||||
let wechat = data.wechat;
|
||||
if (wechat) {
|
||||
wechatOptions.title = wechat.title
|
||||
wechatOptions.desc = wechat.desc
|
||||
wechatOptions.imgUrl = wechat.url ? img(wechat.url) : ''
|
||||
} else {
|
||||
wechatOptions.title = document ? document.title : ''
|
||||
wechatOptions.desc = ''
|
||||
}
|
||||
// #ifdef H5
|
||||
wechatShare()
|
||||
// #endif
|
||||
|
||||
let weapp = data.weapp;
|
||||
if (weapp) {
|
||||
weappOptions.title = weapp.title
|
||||
weappOptions.imageUrl = weapp.url ? img(weapp.url) : ''
|
||||
}
|
||||
// #ifdef MP
|
||||
if(!weappOptions.title && !weappOptions.imageUrl){
|
||||
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
|
||||
}
|
||||
|
||||
// 禁用当前页面的分享功能(同时支持小程序和公众号)
|
||||
const disableShare = () => {
|
||||
// 公众号(H5)禁用分享
|
||||
// #ifdef H5
|
||||
if (isWeixinBrowser()) {
|
||||
// 确保SDK初始化后再禁用
|
||||
wechat.init(() => {
|
||||
wechat.disableShare();
|
||||
});
|
||||
}
|
||||
// #endif
|
||||
|
||||
// 小程序禁用分享
|
||||
// #ifdef MP-WEIXIN
|
||||
// 隐藏分享菜单(转发给朋友、朋友圈)
|
||||
uni.hideShareMenu({
|
||||
menus: ['shareAppMessage', 'shareTimeline'],
|
||||
success: () => {
|
||||
console.log('小程序分享已禁用');
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('小程序禁用分享失败:', err);
|
||||
}
|
||||
});
|
||||
// 覆盖分享方法,返回空对象
|
||||
onShareAppMessage(() => ({}));
|
||||
onShareTimeline(() => ({}));
|
||||
// #endif
|
||||
};
|
||||
|
||||
return {
|
||||
wechatInit: wechatInit,
|
||||
setShare: setShare,
|
||||
onShareAppMessage: shareApp,
|
||||
onShareTimeline: shareTime,
|
||||
disableShare: disableShare,
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -44,6 +44,7 @@ class Language {
|
||||
this.setI18nLanguage(locale, file)
|
||||
return nextTick()
|
||||
}
|
||||
this.loadLocale.push(`${fileKey}.${locale}`)
|
||||
|
||||
// 引入语言包文件
|
||||
const messages = await import(route == 'app' ? `../${route}/locale/${locale}/${file}.json` : `../addon/${route}/locale/${locale}/${file}.json`)
|
||||
@ -55,32 +56,33 @@ class Language {
|
||||
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 }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -41,18 +41,18 @@
|
||||
"register": "注册",
|
||||
"complete": "完成",
|
||||
"close": "关闭",
|
||||
"diyForm":{
|
||||
"back":"返回",
|
||||
"hidden":"已隐藏",
|
||||
"view":"查看",
|
||||
"know":"我知道了",
|
||||
"prompt":"提示",
|
||||
"call":"拨打",
|
||||
"copy":"复制号码",
|
||||
"uploadTips":"请上传图片",
|
||||
"tips":"仅限本人和管理员能查看完整号码:",
|
||||
"viewFillingDetails":"查看填写详情",
|
||||
"detailInformation":"详细信息"
|
||||
"diyForm": {
|
||||
"back": "返回",
|
||||
"hidden": "已隐藏",
|
||||
"view": "查看",
|
||||
"know": "我知道了",
|
||||
"prompt": "提示",
|
||||
"call": "拨打",
|
||||
"copy": "复制号码",
|
||||
"uploadTips": "请上传图片",
|
||||
"tips": "仅限本人和管理员能查看完整号码:",
|
||||
"viewFillingDetails": "查看填写详情",
|
||||
"detailInformation": "详细信息"
|
||||
},
|
||||
"pay": {
|
||||
"orderInfo": "订单信息",
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { getConfig } from '@/app/api/auth'
|
||||
import { isWeixinBrowser } from "@/utils/common";
|
||||
import useSystemStore from "@/stores/system";
|
||||
|
||||
interface loginConfig {
|
||||
is_username: number | boolean,
|
||||
@ -54,7 +55,8 @@ const useConfigStore = defineStore('config', {
|
||||
let url = '';
|
||||
// #ifdef H5
|
||||
if (isWeixinBrowser()) {
|
||||
url = uni.getSystemInfoSync().platform == 'ios' ? uni.getStorageSync('initUrl') : location.href
|
||||
const systemStore = useSystemStore()
|
||||
url = systemStore.systemInfo.platform == 'ios' ? uni.getStorageSync('initUrl') : location.href
|
||||
}
|
||||
// #endif
|
||||
await getConfig({ url }).then((res: any) => {
|
||||
|
||||
@ -46,6 +46,16 @@ const useMemberStore = defineStore('member', {
|
||||
}
|
||||
// #endif
|
||||
|
||||
// #ifdef H5
|
||||
if (this.info && this.info.wx_openid) {
|
||||
// 如果会员已存在则小程序端快捷登录时不再弹出授权弹框
|
||||
uni.setStorageSync('member_exist', 1)
|
||||
} else {
|
||||
const login = useLogin()
|
||||
login.updateWechatOpenidForH5(uni.getStorageSync('openid'));
|
||||
}
|
||||
// #endif
|
||||
|
||||
if (callback) callback();
|
||||
}).catch(() => {
|
||||
this.logout()
|
||||
@ -61,7 +71,7 @@ const useMemberStore = defineStore('member', {
|
||||
// if (useConfigStore().login.is_auth_register) {
|
||||
uni.setStorageSync('autoLoginLock', true) // todo 普通账号退出登录,在进行三方账号登录不会自动登录
|
||||
// }
|
||||
let clearStorage = () =>{
|
||||
let clearStorage = () => {
|
||||
removeToken()
|
||||
uni.removeStorageSync('wap_member_info');
|
||||
// uni.removeStorageSync('openid');
|
||||
@ -69,12 +79,11 @@ const useMemberStore = defineStore('member', {
|
||||
uni.removeStorageSync('isBindMobile');
|
||||
uni.removeStorageSync('nickname');
|
||||
uni.removeStorageSync('avatar');
|
||||
// 可能重复请求微信获取手机号接口
|
||||
uni.removeStorageSync('wap_member_mobile');
|
||||
uni.removeStorageSync('wap_member_id');
|
||||
uni.removeStorageSync('wap_member_not_control_mobile');
|
||||
// 可能重复请求微信获取手机号接口
|
||||
uni.removeStorageSync('wap_member_mobile');
|
||||
uni.removeStorageSync('wap_member_id');
|
||||
uni.removeStorageSync('wap_member_not_control_mobile');
|
||||
isRedirect && redirect({ url: '/app/pages/index/index', mode: 'switchTab' })
|
||||
|
||||
}
|
||||
logout().then(() => {
|
||||
clearStorage()
|
||||
|
||||
@ -19,6 +19,11 @@ interface System {
|
||||
currTabbar: {
|
||||
path: string,
|
||||
query: object
|
||||
},
|
||||
systemInfo: any, // 系统设备信息
|
||||
topTabbarInfo: {
|
||||
height: number
|
||||
fullHeight: string
|
||||
}
|
||||
}
|
||||
|
||||
@ -35,10 +40,10 @@ const useSystemStore = defineStore('system', {
|
||||
},
|
||||
initStatus: 'wait',
|
||||
menuButtonInfo: {
|
||||
height: '',
|
||||
top: '',
|
||||
right: '',
|
||||
width: ''
|
||||
height: 0,
|
||||
top: 0,
|
||||
right: 0,
|
||||
width: 0
|
||||
},
|
||||
shareCallback: null,
|
||||
defaultPositionAddress: '定位中',
|
||||
@ -46,6 +51,11 @@ const useSystemStore = defineStore('system', {
|
||||
currTabbar: {
|
||||
path: '',
|
||||
query: {}
|
||||
},
|
||||
systemInfo: null,
|
||||
topTabbarInfo: {
|
||||
height: 0,
|
||||
fullHeight: ''
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -56,7 +66,7 @@ const useSystemStore = defineStore('system', {
|
||||
let url = '';
|
||||
// #ifdef H5
|
||||
if (isWeixinBrowser()) {
|
||||
url = uni.getSystemInfoSync().platform == 'ios' ? uni.getStorageSync('initUrl') : location.href
|
||||
url = this.systemInfo.platform == 'ios' ? uni.getStorageSync('initUrl') : location.href
|
||||
}
|
||||
// #endif
|
||||
|
||||
@ -152,7 +162,7 @@ const useSystemStore = defineStore('system', {
|
||||
setAddressInfo(data: any = {}) {
|
||||
let addressInfo = cloneDeep(data);
|
||||
// 过期时间
|
||||
var date = new Date();
|
||||
const date = new Date();
|
||||
date.setSeconds(60 * this.mapConfig.valid_time);
|
||||
addressInfo.valid_time = date.getTime() / 1000; // 定位信息 5分钟内有效,过期后将重新获取定位信息
|
||||
|
||||
@ -167,6 +177,11 @@ const useSystemStore = defineStore('system', {
|
||||
} else {
|
||||
uni.removeStorageSync('location_address');
|
||||
}
|
||||
},
|
||||
setTopTabbar(data: any = {}) {
|
||||
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
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@ -552,4 +552,18 @@ button[type='primary'],uni-button[type='primary']{
|
||||
}
|
||||
.information-filling .u-line{
|
||||
border-color: #dddddd !important;
|
||||
}
|
||||
.order-grey-hollow-btn{
|
||||
border: 2rpx solid #ccc;
|
||||
height: 56rpx;
|
||||
font-size: 24rpx;
|
||||
background-color: #fff;
|
||||
border-radius: 30rpx;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
padding: 0 24rpx;
|
||||
min-width: 144rpx;
|
||||
color: #333;
|
||||
}
|
||||
@ -1,8 +1,8 @@
|
||||
@font-face {
|
||||
font-family: "iconfont"; /* Project id 3952239 */
|
||||
src: url('//at.alicdn.com/t/c/font_3952239_w9mu2hnz4eg.woff2?t=1731396621845') format('woff2'),
|
||||
url('//at.alicdn.com/t/c/font_3952239_w9mu2hnz4eg.woff?t=1731396621845') format('woff'),
|
||||
url('//at.alicdn.com/t/c/font_3952239_w9mu2hnz4eg.ttf?t=1731396621845') format('truetype');
|
||||
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,106 @@
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.iconjiahaoV6xx1:before {
|
||||
content: "\e7cc";
|
||||
}
|
||||
|
||||
.icona-xiangxiaV6xx1:before {
|
||||
content: "\e79e";
|
||||
}
|
||||
|
||||
.icona-bijiPC30:before {
|
||||
content: "\e6e8";
|
||||
}
|
||||
|
||||
.icona-chengtuanrenshuPC30:before {
|
||||
content: "\e6e7";
|
||||
}
|
||||
|
||||
.iconpintuanV6xx1:before {
|
||||
content: "\e8cb";
|
||||
}
|
||||
|
||||
.iconxianshiV6xx:before {
|
||||
content: "\e8ad";
|
||||
}
|
||||
|
||||
.iconwenhao:before {
|
||||
content: "\eae2";
|
||||
}
|
||||
|
||||
.icontuihuoV6xx:before {
|
||||
content: "\e8ca";
|
||||
}
|
||||
|
||||
.icontuihuoV6mm:before {
|
||||
content: "\e8c9";
|
||||
}
|
||||
|
||||
.iconyinfuV6mm:before {
|
||||
content: "\e8c3";
|
||||
}
|
||||
|
||||
.iconyouV6xx1:before {
|
||||
content: "\e8be";
|
||||
}
|
||||
|
||||
.iconjishibenV6xx:before {
|
||||
content: "\e6fa";
|
||||
}
|
||||
|
||||
.iconweixinV6mm1:before {
|
||||
content: "\e8b0";
|
||||
}
|
||||
|
||||
.iconshouji6Vmm:before {
|
||||
content: "\e8b1";
|
||||
}
|
||||
|
||||
.iconmima6Vmm:before {
|
||||
content: "\e8b3";
|
||||
}
|
||||
|
||||
.iconxianshiV6mm:before {
|
||||
content: "\e8ac";
|
||||
}
|
||||
|
||||
.iconqianV6xx:before {
|
||||
content: "\e79d";
|
||||
}
|
||||
|
||||
.iconshouyev6mm:before {
|
||||
content: "\e879";
|
||||
}
|
||||
|
||||
.iconweixinV6mm:before {
|
||||
content: "\e6d6";
|
||||
}
|
||||
|
||||
.icondianhua1:before {
|
||||
content: "\e6b9";
|
||||
}
|
||||
|
||||
.iconmofang-yizuoliangyou:before {
|
||||
content: "\e6c7";
|
||||
}
|
||||
|
||||
.iconfenleiV6mm:before {
|
||||
content: "\e66c";
|
||||
}
|
||||
|
||||
.icondaifahuoV6mm:before {
|
||||
content: "\e69d";
|
||||
}
|
||||
|
||||
.iconzhixiang-youjiantouV6xx:before {
|
||||
content: "\e649";
|
||||
}
|
||||
|
||||
.icona-gengduoV6xx-28:before {
|
||||
content: "\e851";
|
||||
}
|
||||
|
||||
.icona-paihangbangpc30:before {
|
||||
content: "\e83a";
|
||||
}
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
@font-face {
|
||||
font-family: "nc-iconfont"; /* Project id 4567203 */
|
||||
src: url('//at.alicdn.com/t/c/font_4567203_90bek9j2oeh.woff2?t=1750242885299') format('woff2'),
|
||||
url('//at.alicdn.com/t/c/font_4567203_90bek9j2oeh.woff?t=1750242885299') format('woff'),
|
||||
url('//at.alicdn.com/t/c/font_4567203_90bek9j2oeh.ttf?t=1750242885299') 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 {
|
||||
@ -13,6 +13,10 @@
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.nc-icon-xiaochengxu:before {
|
||||
content: "\e8a8";
|
||||
}
|
||||
|
||||
.nc-icon-qianV6xx:before {
|
||||
content: "\e784";
|
||||
}
|
||||
|
||||
@ -1,2 +1 @@
|
||||
[
|
||||
]
|
||||
[]
|
||||
@ -132,9 +132,10 @@ export default {
|
||||
})
|
||||
},
|
||||
/**
|
||||
* ref 触发 动画分组
|
||||
* @param {Object} obj
|
||||
*/
|
||||
* ref 触发 动画分组
|
||||
* @param {Object} obj
|
||||
* @param config
|
||||
*/
|
||||
step(obj, config = {}) {
|
||||
if (!this.animation) return
|
||||
for (let i in obj) {
|
||||
|
||||
@ -26,14 +26,12 @@ export const redirect = (redirect: any) => {
|
||||
if (config.login.is_username && !config.login.is_mobile && !config.login.is_auth_register) {
|
||||
url = '/app/pages/auth/login'
|
||||
param = { type: 'username' }
|
||||
mode = 'redirectTo'
|
||||
newLogin = true
|
||||
} else if (systemStore.initStatus == 'finish' && !config.login.is_username && !config.login.is_mobile && !config.login.is_auth_register) {
|
||||
uni.showToast({ title: '商家未开启登录注册', icon: 'none' })
|
||||
return;
|
||||
} else {
|
||||
url = '/app/pages/auth/index'
|
||||
mode = 'redirectTo'
|
||||
newLogin = true
|
||||
}
|
||||
// #endif
|
||||
@ -44,14 +42,12 @@ export const redirect = (redirect: any) => {
|
||||
if (config.login.is_username && !config.login.is_mobile && !config.login.is_auth_register) {
|
||||
url = '/app/pages/auth/login'
|
||||
param = { type: 'username' }
|
||||
mode = 'redirectTo'
|
||||
newLogin = true
|
||||
} else if (systemStore.initStatus == 'finish' && !config.login.is_username && !config.login.is_mobile && !config.login.is_auth_register) {
|
||||
uni.showToast({ title: '商家未开启登录注册', icon: 'none' })
|
||||
return;
|
||||
} else {
|
||||
url = '/app/pages/auth/index'
|
||||
mode = 'redirectTo'
|
||||
newLogin = true
|
||||
}
|
||||
} else {
|
||||
@ -59,14 +55,12 @@ export const redirect = (redirect: any) => {
|
||||
if (config.login.is_username && !config.login.is_mobile) {
|
||||
url = '/app/pages/auth/login'
|
||||
param = { type: 'username' }
|
||||
mode = 'redirectTo'
|
||||
newLogin = true
|
||||
} else if (systemStore.initStatus == 'finish' && !config.login.is_username && !config.login.is_mobile) {
|
||||
uni.showToast({ title: '商家未开启登录注册', icon: 'none' })
|
||||
return;
|
||||
} else {
|
||||
url = '/app/pages/auth/index'
|
||||
mode = 'redirectTo'
|
||||
newLogin = true
|
||||
}
|
||||
}
|
||||
@ -212,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
|
||||
|
||||
@ -356,17 +350,17 @@ export function mobileConceal(mobile: string): string {
|
||||
*/
|
||||
export function timeStampTurnTime(timeStamp: any, type = "") {
|
||||
if (timeStamp != undefined && timeStamp != "" && timeStamp > 0) {
|
||||
var date = new Date();
|
||||
const date = new Date();
|
||||
date.setTime(timeStamp * 1000);
|
||||
var y = date.getFullYear();
|
||||
var m: any = date.getMonth() + 1;
|
||||
const y = date.getFullYear();
|
||||
let m: any = date.getMonth() + 1;
|
||||
m = m < 10 ? ('0' + m) : m;
|
||||
var d: any = date.getDate();
|
||||
let d: any = date.getDate();
|
||||
d = d < 10 ? ('0' + d) : d;
|
||||
var h: any = date.getHours();
|
||||
let h: any = date.getHours();
|
||||
h = h < 10 ? ('0' + h) : h;
|
||||
var minute: any = date.getMinutes();
|
||||
var second: any = date.getSeconds();
|
||||
let minute: any = date.getMinutes();
|
||||
let second: any = date.getSeconds();
|
||||
minute = minute < 10 ? ('0' + minute) : minute;
|
||||
second = second < 10 ? ('0' + second) : second;
|
||||
if (type) {
|
||||
@ -385,56 +379,135 @@ export function timeStampTurnTime(timeStamp: any, type = "") {
|
||||
|
||||
/**
|
||||
* 日期格式转时间戳
|
||||
* @param {Object} date
|
||||
* @param dateStr
|
||||
*/
|
||||
export function timeTurnTimeStamp(dateStr: string) {
|
||||
let timestamp;
|
||||
let date;
|
||||
// 输入验证
|
||||
if (!dateStr || typeof dateStr !== 'string' || dateStr.trim() === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 尝试解析 'YYYY年M月D日'
|
||||
try {
|
||||
let dateStr1 = dateStr.replace('年', '-').replace('月', '-').replace('日', '');
|
||||
date = new Date(dateStr1);
|
||||
timestamp = date.getTime();
|
||||
} catch (e) {
|
||||
// 尝试解析 'YYYY-MM-DD'
|
||||
try {
|
||||
date = new Date(dateStr);
|
||||
timestamp = date.getTime();
|
||||
} catch (e) {
|
||||
// 尝试解析 'YYYY/MM/DD'
|
||||
try {
|
||||
date = new Date(dateStr.replace(/\//g, "-"));
|
||||
timestamp = date.getTime();
|
||||
} catch (e) {
|
||||
// 尝试解析 'YYYY年M月D日 HH时mm分'
|
||||
try {
|
||||
let dateStr1 = dateStr.replace('年', '-').replace('月', '-').replace('日', ' ').replace('时', ':').replace('分', '');
|
||||
date = new Date(dateStr1);
|
||||
timestamp = date.getTime();
|
||||
} catch (e) {
|
||||
// 尝试解析 'YYYY-MM-DD HH:mm'
|
||||
try {
|
||||
date = new Date(dateStr);
|
||||
timestamp = date.getTime();
|
||||
} catch (e) {
|
||||
// 尝试解析 'YYYY/MM/DD HH:mm'
|
||||
try {
|
||||
date = new Date(dateStr.replace(/\//g, "-"));
|
||||
timestamp = date.getTime();
|
||||
} catch (e) {
|
||||
// 如果所有格式都失败,返回null
|
||||
console.error("无法解析日期字符串:", dateStr);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const trimmedDateStr = dateStr.trim();
|
||||
|
||||
// 定义支持的日期格式转换规则
|
||||
const formatRules = [
|
||||
// 'YYYY年M月D日' -> 'YYYY-MM-DD'
|
||||
{
|
||||
pattern: /(\d{4})年(\d{1,2})月(\d{1,2})日/,
|
||||
transform: (str: string) => str.replace(/(\d{4})年(\d{1,2})月(\d{1,2})日/, '$1-$2-$3')
|
||||
},
|
||||
// 'YYYY年M月D日 HH时mm分' -> 'YYYY-MM-DD HH:mm'
|
||||
{
|
||||
pattern: /(\d{4})年(\d{1,2})月(\d{1,2})日\s+(\d{1,2})时(\d{1,2})分/,
|
||||
transform: (str: string) => str.replace(/(\d{4})年(\d{1,2})月(\d{1,2})日\s+(\d{1,2})时(\d{1,2})分/, '$1-$2-$3 $4:$5')
|
||||
},
|
||||
// 'YYYY/MM/DD' -> 'YYYY-MM-DD'
|
||||
{
|
||||
pattern: /^\d{4}\/\d{1,2}\/\d{1,2}(\s+\d{1,2}:\d{1,2}(:\d{1,2})?)?$/,
|
||||
transform: (str: string) => str.replace(/\//g, '-')
|
||||
},
|
||||
// 标准格式,无需转换
|
||||
{
|
||||
pattern: /^\d{4}-\d{1,2}-\d{1,2}(\s+\d{1,2}:\d{1,2}(:\d{1,2})?)?$/,
|
||||
transform: (str: string) => str
|
||||
}
|
||||
];
|
||||
|
||||
// 尝试匹配并转换格式
|
||||
let normalizedDateStr = null;
|
||||
for (const rule of formatRules) {
|
||||
if (rule.pattern.test(trimmedDateStr)) {
|
||||
normalizedDateStr = rule.transform(trimmedDateStr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return (timestamp / 1000);
|
||||
|
||||
// 如果没有匹配的格式,直接尝试原始字符串
|
||||
if (!normalizedDateStr) {
|
||||
normalizedDateStr = trimmedDateStr;
|
||||
}
|
||||
|
||||
// 创建日期对象并验证
|
||||
const date = new Date(normalizedDateStr);
|
||||
|
||||
// 检查日期是否有效
|
||||
if (isNaN(date.getTime())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 返回秒级时间戳
|
||||
return Math.floor(date.getTime() / 1000);
|
||||
}
|
||||
|
||||
/**
|
||||
* 日期格式转时间戳 (兼容 iOS)
|
||||
* @param dateStr
|
||||
*/
|
||||
export function timeTurnTimeStampTwo(dateStr: string) {
|
||||
if (!dateStr || typeof dateStr !== 'string' || dateStr.trim() === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
let trimmedDateStr = dateStr.trim();
|
||||
|
||||
// 定义支持的日期格式转换规则
|
||||
const formatRules = [
|
||||
// 'YYYY年M月D日'
|
||||
{
|
||||
pattern: /(\d{4})年(\d{1,2})月(\d{1,2})日/,
|
||||
transform: (str: string) =>
|
||||
str.replace(/(\d{4})年(\d{1,2})月(\d{1,2})日/, '$1/$2/$3'),
|
||||
},
|
||||
// 'YYYY年M月D日 HH时mm分'
|
||||
{
|
||||
pattern: /(\d{4})年(\d{1,2})月(\d{1,2})日\s+(\d{1,2})时(\d{1,2})分/,
|
||||
transform: (str: string) =>
|
||||
str.replace(
|
||||
/(\d{4})年(\d{1,2})月(\d{1,2})日\s+(\d{1,2})时(\d{1,2})分/,
|
||||
'$1/$2/$3 $4:$5'
|
||||
),
|
||||
},
|
||||
// 'YYYY-MM-DD HH:mm:ss' -> 'YYYY/MM/DD HH:mm:ss' (iOS兼容)
|
||||
{
|
||||
pattern: /^\d{4}-\d{1,2}-\d{1,2}\s+\d{1,2}:\d{1,2}(:\d{1,2})?$/,
|
||||
transform: (str: string) => str.replace(/-/g, '/'),
|
||||
},
|
||||
// 'YYYY-MM-DD' -> 'YYYY/MM/DD' (iOS兼容)
|
||||
{
|
||||
pattern: /^\d{4}-\d{1,2}-\d{1,2}$/,
|
||||
transform: (str: string) => str.replace(/-/g, '/'),
|
||||
},
|
||||
// 'YYYY/MM/DD' / 'YYYY/MM/DD HH:mm:ss'
|
||||
{
|
||||
pattern: /^\d{4}\/\d{1,2}\/\d{1,2}(\s+\d{1,2}:\d{1,2}(:\d{1,2})?)?$/,
|
||||
transform: (str: string) => str,
|
||||
},
|
||||
];
|
||||
|
||||
// 尝试匹配并转换
|
||||
let normalizedDateStr: string | null = null;
|
||||
for (const rule of formatRules) {
|
||||
if (rule.pattern.test(trimmedDateStr)) {
|
||||
normalizedDateStr = rule.transform(trimmedDateStr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!normalizedDateStr) {
|
||||
normalizedDateStr = trimmedDateStr;
|
||||
}
|
||||
|
||||
// 创建日期对象
|
||||
const date = new Date(normalizedDateStr);
|
||||
|
||||
if (isNaN(date.getTime())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return Math.floor(date.getTime() / 1000);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 复制
|
||||
* @param {Object} value
|
||||
@ -442,7 +515,7 @@ export function timeTurnTimeStamp(dateStr: string) {
|
||||
*/
|
||||
export function copy(value: any, callback: any) {
|
||||
// #ifdef H5
|
||||
var oInput = document.createElement('input'); //创建一个隐藏input(重要!)
|
||||
const oInput = document.createElement('input'); //创建一个隐藏input(重要!)
|
||||
oInput.value = value; //赋值
|
||||
oInput.setAttribute("readonly", "readonly");
|
||||
document.body.appendChild(oInput);
|
||||
@ -492,7 +565,7 @@ export function handleOnloadParams(option: any) {
|
||||
|
||||
// 处理小程序扫码进入的场景值参数
|
||||
if (option.scene) {
|
||||
var sceneParams = decodeURIComponent(option.scene).split('&');
|
||||
const sceneParams = decodeURIComponent(option.scene).split('&');
|
||||
if (sceneParams.length) {
|
||||
sceneParams.forEach(item => {
|
||||
let arr = item.split('-');
|
||||
@ -558,7 +631,8 @@ const isArray = (value: any) => {
|
||||
|
||||
// px转rpx
|
||||
export function pxToRpx(px: any) {
|
||||
const screenWidth = uni.getSystemInfoSync().screenWidth;
|
||||
const systemStore = useSystemStore()
|
||||
const screenWidth = systemStore.systemInfo.screenWidth;
|
||||
return (750 * Number.parseInt(px)) / screenWidth;
|
||||
}
|
||||
|
||||
@ -604,7 +678,7 @@ export function getWinxinOpenId() {
|
||||
|
||||
// 获取有效期
|
||||
export function getValidTime(minutes: any = 1) {
|
||||
var date = new Date();
|
||||
const date = new Date();
|
||||
date.setSeconds(60 * minutes);
|
||||
let validTime: any = parseInt(date.getTime() / 1000); // 定位信息 5分钟内有效,过期后将重新获取定位信息
|
||||
return validTime;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user