Merge branch 'main' into youda

This commit is contained in:
zpc 2025-04-18 00:56:17 +08:00
commit faea2465c6
9 changed files with 2768 additions and 2642 deletions

15
App.vue
View File

@ -222,4 +222,19 @@ button.hide {
.uni-tabbar__icon {
height: 50px !important;
}
@keyframes m-zoom {
0% {
transform: scale(1);
}
50% {
transform: scale(0.9);
}
100% {
transform: scale(1);
}
}
</style>

View File

@ -25,6 +25,14 @@ class RequestManager {
return whitelistUrls.some(whiteItem => url.indexOf(whiteItem) > -1)
}
/**
* 生成唯一的nonce值
* @returns {String} nonce值
*/
static generateNonce() {
return md5(Date.now() + Math.random().toString(36).substring(2, 15));
}
/**
* 发送网络请求
* @param {Object} param 请求参数
@ -43,7 +51,7 @@ class RequestManager {
return new Promise((resolve, reject) => {
// 参数检查
if (!param || typeof param !== 'object') {
reject(new Error('请求参数错误11'))
reject(new Error('请求参数错误'))
return
}
@ -90,9 +98,48 @@ class RequestManager {
console.log('请求URL:', requestUrl)
// 使用正则表达式从URL中提取主机名
const hostRegex = /^(?:https?:\/\/)?([^\/]+)/i
const matches = requestUrl.match(hostRegex)
const host = matches && matches[1] ? matches[1] : 'localhost'
let header = {}
// 添加签名和防重放攻击参数
// 1. 添加时间戳
data.timestamp = Math.floor(Date.now() / 1000)
// 2. 添加nonce随机字符串
data.nonce = RequestManager.generateNonce()
if (method.toUpperCase() == 'POST') {
// 按照键名对参数进行排序
const sortedParams = {}
Object.keys(data).sort().forEach(key => {
sortedParams[key] = data[key]
})
// 组合参数为字符串
let signStr = ''
for (const key in sortedParams) {
if (typeof sortedParams[key] === 'object') {
signStr += key + '=' + JSON.stringify(sortedParams[key]) + '&'
} else {
signStr += key + '=' + sortedParams[key] + '&'
}
}
// 获取时间戳,组合为密钥
const timestamp = data.timestamp
const appSecret = host + timestamp
// 添加密钥并去除最后的&
signStr = signStr.substring(0, signStr.length - 1) + appSecret
console.log('签名字符串:', signStr)
// 使用MD5生成签名
const sign = md5(signStr)
data.sign = sign
header = {
'content-type': 'application/x-www-form-urlencoded',
client: client,
@ -102,41 +149,32 @@ class RequestManager {
}
} else {
// GET请求添加签名
if (data) {
// 添加时间戳参数
data.timestamp = Math.floor(Date.now() / 1000);
// 按照键名对参数进行排序
const sortedParams = {}
Object.keys(data).sort().forEach(key => {
sortedParams[key] = data[key]
})
// 按照键名对参数进行排序
const sortedParams = {};
Object.keys(data).sort().forEach(key => {
sortedParams[key] = data[key];
});
// 组合参数为字符串
let signStr = '';
for (const key in sortedParams) {
signStr += key + '=' + sortedParams[key] + '&';
}
// 获取当前请求的域名和时间戳,组合为密钥
// 使用正则表达式从URL中提取主机名
const hostRegex = /^(?:https?:\/\/)?([^\/]+)/i;
const matches = requestUrl.match(hostRegex);
const host = matches && matches[1] ? matches[1] : 'localhost';
const timestamp = data.timestamp;
const appSecret = host + timestamp;
// 添加密钥并去除最后的&
signStr = signStr.substring(0, signStr.length - 1) + appSecret;
console.log(signStr);
// 使用MD5生成签名
const sign = md5(signStr);
// 添加签名到请求参数
data.sign = sign;
// 组合参数为字符串
let signStr = ''
for (const key in sortedParams) {
signStr += key + '=' + sortedParams[key] + '&'
}
// 获取时间戳,组合为密钥
const timestamp = data.timestamp
const appSecret = host + timestamp
// 添加密钥并去除最后的&
signStr = signStr.substring(0, signStr.length - 1) + appSecret
console.log('签名字符串:', signStr)
// 使用MD5生成签名
const sign = md5(signStr)
// 添加签名到请求参数
data.sign = sign
header = {
'content-type': 'application/json',
token: token,

View File

@ -4,17 +4,17 @@
<view class="banner-slot-container" v-if="$slots.default">
<slot></slot>
</view>
<swiper class="swiper-box" :style="{ height: height + 'rpx' }" :autoplay="true" :indicator-dots="false" :circular="true" :interval="3000"
@change="swChange">
<swiper class="swiper-box" :style="{ height: height + 'rpx' }" :autoplay="true" :indicator-dots="false"
:circular="true" :interval="3000" @change="swChange">
<swiper-item v-for="(v, i) in advert" :key="i" @click="navTo(v)">
<image class="yh_bg" :src="v.imgurl"></image>
<image class="yh_bg" :src="v.imgurl" :style="{ width: imgWidth + '%' }"></image>
</swiper-item>
</swiper>
<!-- 指示器 (可选) -->
<view class="sw-dot relative" v-if="showIndicator">
<view class="sw-dot-item" v-for="(item, i) in advert" :key="i" :class="{act: swCur == i}"></view>
<view class="sw-dot-item" v-for="(item, i) in advert" :key="i" :class="{ act: swCur == i }"></view>
</view>
</view>
</template>
@ -37,7 +37,12 @@ export default {
height: {
type: [Number, String],
default: 465
}
},
//
imgWidth: {
type: Number,
default: 92
},
},
data() {
return {
@ -63,55 +68,17 @@ export default {
}
});
},
//
swChange(e) {
this.swCur = e.detail.current;
},
//
navTo(item) {
/* 领券中心 */
if (item.ttype == 1) {
this.$c.to({
url: '/package/index/coupon-center',
query: {
coupon_id: item.coupon_id
}
})
}
this.$c.navTo(item);
if (item.goods_id > 0) {
if (item.ttype == 2) {
/* 一番赏 */
this.$c.to({
url: '/pages/shouye/detail',
query: {
goods_id: item.goods_id
}
})
}
/* 无限赏 */
if (item.ttype == 3) {
this.$c.to({
url: '/pages/shouye/detail_wuxian',
query: {
goods_id: item.goods_id
}
})
}
/* 连击赏 */
if (item.ttype == 4) {
this.$c.to({
url: '/package/index/lian-ji',
query: {
goods_id: item.goods_id
}
})
}
}
}
}
}
@ -133,7 +100,7 @@ export default {
}
/* 允许插槽内的元素可以被点击 */
.banner-slot-container > * {
.banner-slot-container>* {
pointer-events: auto;
}
@ -157,7 +124,7 @@ export default {
position: absolute;
bottom: 20rpx;
width: 100%;
&-item {
width: 12rpx;
height: 12rpx;
@ -165,7 +132,7 @@ export default {
background-color: rgba(255, 255, 255, 0.5);
margin: 0 5rpx;
transition: all 0.3s;
&.act {
width: 24rpx;
border-radius: 6rpx;
@ -173,4 +140,4 @@ export default {
}
}
}
</style>
</style>

View File

@ -0,0 +1,146 @@
<template>
<view>
<!-- 悬浮球 -->
<template v-if="floatBall">
<view v-for="(item, index) in floatBall" :style="[getBallStyle(item)]" :key="index" class="group-fixed1"
@click="BallClick(item)">
<image :src="item.image"></image>
</view>
</template>
<!-- 悬浮球弹窗 -->
<uni-popup ref="floatBall_popup" type="center" maskBackgroundColor="rgba(0,0,0,0.8)">
<view class="pop-ball"
:style="{ backgroundImage: 'url(' + ballItem.image_bj + ')', backgroundSize: '100% 99.5%', backgroundRepeat: 'no-repeat' }">
<image show-menu-by-longpress v-if="ballItem != null" :src="ballItem.image_details" mode="aspectFit"
:style="[getPopupStyle(ballItem)]">
</image>
</view>
<view class="pop-ball-close flex" @click="$refs.floatBall_popup.close()">
<view style="width: 48rpx;height: 48rpx;border-radius: 50%;opacity: 0.5;">
<image show-menu-by-longpress :src="$img1('common/close.png')" class="img100" />
</view>
</view>
</uni-popup>
</view>
</template>
<script>
export default {
name: 'FloatBall',
data() {
return {
floatBall: [],
ballItem: null
}
},
computed: {
getBallStyle() {
return (item) => {
let s = {
width: item.width,
height: item.height,
top: item.position_y,
};
if (item.position_x.indexOf('-') > -1) {
let position_x = item.position_x.split('-')[1];
s.right = position_x;
} else {
s.left = item.position_x;
}
if (item.effect == 1) {
s.animation = "m-zoom 1.2s ease-in-out infinite";
}
return s;
}
},
getPopupStyle() {
return (item) => {
if (item == null) {
return {};
}
let s = {
width: item.image_details_w,
height: item.image_details_h,
position: 'relative',
};
if (item.image_details_x != "0") {
s.left = item.image_details_x;
}
if (item.image_details_y != "0") {
s.top = item.image_details_y;
}
return s;
};
}
},
methods: {
BallClick(item) {
if (item.type == 2) {
this.$c.nav(item.link_url);
return;
}
if (item.type == 1) {
this.ballItem = item;
this.$refs.floatBall_popup.open();
}
},
async getFloatBall() {
const { status, data, msg } = await this.$request.get("getFloatBall");
if (status == 1) {
this.floatBall = data;
}
}
},
mounted() {
this.getFloatBall();
}
}
</script>
<style lang="scss" scoped>
.group-fixed1 {
width: 52rpx;
height: 120rpx;
position: fixed;
z-index: 10;
right: 0;
top: 21vh;
image {
width: 100%;
height: 100%;
}
}
.pop-ball {
width: 506rpx;
height: 380px;
position: relative;
border-radius: 25rpx;
display: flex;
align-items: center;
justify-content: center;
}
.pop-ball-close {
margin-top: 20rpx;
width: 100%;
height: 50rpx;
display: flex;
align-items: center;
justify-content: center;
}
@keyframes m-zoom {
0% {
transform: scale(1);
}
50% {
transform: scale(0.9);
}
100% {
transform: scale(1);
}
}
</style>

View File

@ -6,8 +6,11 @@
<view class="_rule_pop common_bg">
<view class="_rule_pop_title">{{ ruleData.title }}</view>
<scroll-view scroll-y class="_rule_pop_bd" :class="{'has-check': noticeCheck}">
<view v-html="ruleData.content"></view>
<scroll-view scroll-y class="_rule_pop_bd" :class="{ 'has-check': noticeCheck }">
<view v-if="is_image_optimizer&&images.length>0" class="image-optimizer">
<image show-menu-by-longpress v-for="(image, index) in images" :key="index" :src="image" mode="widthFix" style="width: 100%;"></image>
</view>
<view v-else v-html="ruleData.content"></view>
</scroll-view>
<view v-if="noticeCheck" class="check-btn" @click="todayHide = !todayHide">
@ -28,116 +31,137 @@
</template>
<script>
export default {
props: {
noticeCheck: {
type: Boolean,
default: false
}
},
export default {
props: {
noticeCheck: {
type: Boolean,
default: false
}
},
data() {
return {
ruleData: '',
todayHide: false
}
},
data() {
return {
ruleData: '',
todayHide: false,
is_image_optimizer: false,
images: []
}
},
mounted() {
console.log(this.noticeCheck)
},
mounted() {
console.log(this.noticeCheck)
},
methods: {
popChange({
show
}) {
if (!show) {
if (this.todayHide) {
uni.setStorageSync('_last_notice_date', this.$c.getDateTime())
}
methods: {
popChange({
show
}) {
if (!show) {
if (this.todayHide) {
uni.setStorageSync('_last_notice_date', this.$c.getDateTime())
}
},
}
},
close() {
this.$refs._rule_pop.close()
},
close() {
this.$refs._rule_pop.close()
},
open(opt) {
this.ruleData = opt
open(opt) {
this.ruleData = opt
this.$refs._rule_pop.open()
},
this.$refs._rule_pop.open()
},
async getRule(id, title = '') {
let res = await this.$c.getRule(id, true)
async getRule(id, title = '') {
this.is_image_optimizer = false;
this.images = [];
let { status, data, msg, is_image_optimizer } = await this.$c.getRule(id, true)
if (status) {
if (is_image_optimizer != null && is_image_optimizer == 1) {
console.log('开启图片优化');
this.is_image_optimizer = true;
//
const imgRegex = /<img[^>]+src="([^"]+)"[^>]*>/g;
let match;
while ((match = imgRegex.exec(data)) !== null) {
// images.push(match[1]);
this.images.push(match[1]);
}
console.log('提取的图片地址:', this.images);
//
}
this.open({
title: title,
content: res.data
content: data
})
} else {
this.$c.toast(msg)
}
}
}
}
</script>
<style lang="scss">
._rule_pop {
width: 610rpx;
height: 820rpx;
box-sizing: border-box;
padding: 0 40rpx 0;
background: #FFFFFF;
border-radius: 30rpx;
._rule_pop {
width: 610rpx;
height: 820rpx;
box-sizing: border-box;
padding: 0 40rpx 0;
background: #FFFFFF;
border-radius: 30rpx;
._rule_pop_title {
padding: 40rpx 0;
text-align: center;
font-weight: 500;
font-size: 36rpx;
color: #333333;
// font-family: YouSheBiaoTiHei;
}
._rule_pop_title {
padding: 40rpx 0;
text-align: center;
font-weight: 500;
font-size: 36rpx;
color: #333333;
// font-family: YouSheBiaoTiHei;
}
._rule_pop_bd {
height: 670rpx;
._rule_pop_bd {
height: 670rpx;
font-size: 26rpx;
font-family: Source Han Sans CN;
font-weight: 400;
color: #999999;
font-size: 26rpx;
font-family: Source Han Sans CN;
font-weight: 400;
color: #999999;
&.has-check {
height: 620rpx;
}
}
.check-btn {
display: flex;
justify-content: center;
align-items: center;
padding: 20rpx 0 0;
.icon {
width: 32rpx;
height: 32rpx;
margin-right: 10rpx;
image {
width: 100%;
height: 100%;
}
}
font-size: 24rpx;
color: #888;
}
.close {
width: 48rpx;
height: 48rpx;
position: absolute;
left: 50%;
bottom: 0;
transform: translate(-50%, 150%);
&.has-check {
height: 620rpx;
}
}
.check-btn {
display: flex;
justify-content: center;
align-items: center;
padding: 20rpx 0 0;
.icon {
width: 32rpx;
height: 32rpx;
margin-right: 10rpx;
image {
width: 100%;
height: 100%;
}
}
font-size: 24rpx;
color: #888;
}
.close {
width: 48rpx;
height: 48rpx;
position: absolute;
left: 50%;
bottom: 0;
transform: translate(-50%, 150%);
}
}
</style>

View File

@ -1,87 +1,81 @@
<template>
<view style="padding-bottom: 300rpx; background-color: white;">
<view class="" style="display: flex; flex-direction: column;">
<view style="position: relative;">
<view class="title">
商城好物
</view>
<image v-if="advert != null && advert.length > 0" :src="advert[0].imgurl"
style="width: 100%; margin-top:10rpx ;" mode=""></image>
</view>
<view class="grid-container">
<view v-for="(item, index) in datas" :key="index" class="grid-item" @click="order_money(item)">
<page-container title="商城好物">
<banner :type-id="10" :height="326"></banner>
<view style="padding-bottom: 300rpx; background-color: white;">
<view class="" style="display: flex; flex-direction: column;">
<view class="grid-container">
<view v-for="(item, index) in datas" :key="index" class="grid-item" @click="order_money(item)">
<view class=""
style="background-color: #D8D8D8; height: 324rpx; border-radius: 16rpx 16rpx 0rpx 0rpx;">
<image :src="item.imgurl"
style=" width: 326.39rpx; height: 324rpx; position: absolute; left: 2rpx; border-radius: 16rpx"
mode="">
</image>
</view>
<view class="goods-name hang1" style=" width: 290rpx;">
<text style="color: #333333; font-size: 27rpx;">{{ item.title }}</text>
</view>
<view class="price-box">
<view class="price">
<text><text style="font-size: 24rpx;">{{ item.price }}</text></text>
</view>
</view>
<view class="num-box">
<view class="num ml10"> {{ item.sale_stock }}/{{ item.stock }} </view>
<view class="box icon">
<image :src="$img1('index/box.png')" lazy-load></image>
</view>
</view>
</view>
</view>
</view>
<uni-popup ref="pop" type="bottom">
<view v-if="orderData" class="buy-pop relative">
<view class="buy-pop-hd">
<view style="width: 24rpx;">
</view>
<text class="">确认订单</text>
<view class="close icon" @click="close('pop')">
<image :src="$img('/static/img/close2.png')" lazy-load></image>
</view>
</view>
<view class="buy-card">
<view class="buy-info">
<view class="pic">
<image :src="orderData.goods.imgurl_detail" style="width: 190rpx; height: 190rpx;"
lazy-load>
<view class="" style="background-color: #D8D8D8; height: 324rpx; border-radius: 16rpx 16rpx 0rpx 0rpx;">
<image :src="item.imgurl"
style=" width: 326.39rpx; height: 324rpx; position: absolute; left: 2rpx; border-radius: 16rpx"
mode="">
</image>
</view>
<view class="info-r">
<view class="hang1" style="width: 100%; color: #333333; font-size: 24rpx;">
{{ orderData.goods.title }}
<view class="goods-name hang1" style=" width: 290rpx;">
<text style="color: #333333; font-size: 27rpx;">{{ item.title }}</text>
</view>
<view class="price-box">
<view class="price">
<text><text style="font-size: 24rpx;">{{ item.price }}</text></text>
</view>
<view class="type">类型:明信片</view>
<view class="price-num">
<view class="price" style="font-size: 16rpx;">
¥<text style="font-size: 28rpx;">{{ orderData.goods.price }}</text>
</view>
<view class="num">×{{ orderData.goods.prize_num }}</view>
</view>
<view class="num-box">
<view class="num ml10"> {{ item.sale_stock }}/{{ item.stock }} </view>
<view class="box icon">
<image :src="$img1('index/box.png')" lazy-load></image>
</view>
</view>
</view>
</view>
<view class="br20 mt20" style="background: #FFFFFF;">
<view class="pay-type" @click="changePay(0)">
<view class="" style="width: 100%; color: #333333;">
微信支付
</view>
<view class="icon">
<image v-if="zhifu == 0" :src="$img1('common/check_act.png')" lazy-load></image>
<image v-else :src="$img1('common/check.png')" lazy-load></image>
</view>
<uni-popup ref="pop" type="bottom">
<view v-if="orderData" class="buy-pop relative">
<view class="buy-pop-hd">
<view style="width: 24rpx;">
</view>
<text class="">确认订单</text>
<view class="close icon" @click="close('pop')">
<image :src="$img('/static/img/close2.png')" lazy-load></image>
</view>
</view>
<!-- <view class="pay-type" @click="changePay(1)">
<view class="buy-card">
<view class="buy-info">
<view class="pic">
<image :src="orderData.goods.imgurl_detail" style="width: 190rpx; height: 190rpx;"
lazy-load>
</image>
</view>
<view class="info-r">
<view class="hang1" style="width: 100%; color: #333333; font-size: 24rpx;">
{{ orderData.goods.title }}
</view>
<view class="type">类型:明信片</view>
<view class="price-num">
<view class="price" style="font-size: 16rpx;">
¥<text style="font-size: 28rpx;">{{ orderData.goods.price }}</text>
</view>
<view class="num">×{{ orderData.goods.prize_num }}</view>
</view>
</view>
</view>
</view>
<view class="br20 mt20" style="background: #FFFFFF;">
<view class="pay-type" @click="changePay(0)">
<view class="" style="width: 100%; color: #333333;">
微信支付
</view>
<view class="icon">
<image v-if="zhifu == 0" :src="$img1('common/check_act.png')" lazy-load></image>
<image v-else :src="$img1('common/check.png')" lazy-load></image>
</view>
</view>
<!-- <view class="pay-type" @click="changePay(1)">
<view class="" style="width: 100%; color: #333333;">
{{ $config.getAppSetting('currency2_name') }}
@ -94,48 +88,50 @@
</view>
</view> -->
</view>
<view class="rule">
<scroll-view class="rule-inner" scroll-y>
<view v-html="sendRuleData"></view>
</scroll-view>
</view>
<view class="agree" @click="isAgree = !isAgree">
<view class="icon">
<image v-if="isAgree" :src="$img1('common/check_act.png')" lazy-load></image>
<image v-else :src="$img1('common/check.png')" lazy-load></image>
</view>
我已满18岁,阅读并同意
<view class="rule">
<scroll-view class="rule-inner" scroll-y>
<view v-html="sendRuleData"></view>
</scroll-view>
</view>
<view class="agree" @click="isAgree = !isAgree">
<view class="icon">
<image v-if="isAgree" :src="$img1('common/check_act.png')" lazy-load></image>
<text @click.stop="$c.to({ url: '/pages/guize/guize?type=4' })">
用户协议
</text>
<image v-else :src="$img1('common/check.png')" lazy-load></image>
</view>
<text @click.stop="$c.to({ url: '/pages/guize/guize?type=5' })">
隐私政策
</text>
我已满18岁,阅读并同意
<text @click.stop="$c.to({ url: '/pages/guize/guize?type=4' })">
用户协议
</text>
<text @click.stop="$c.to({ url: '/pages/guize/guize?type=5' })">
隐私政策
</text>
</view>
<view class="pay-btn" v-if="zhifu == 0" @click="$c.noDouble(confirmSubmit(1))">
<text>
{{
` ¥${orderData.price}`
}}
</text>
</view>
<view class="pay-btn" v-if="zhifu == 1" @click="$c.noDouble(pay(1))">
<text>{{ orderData.price * 100 }}{{ $config.getAppSetting('currency2_name') }}</text>
</view>
</view>
</uni-popup>
</view>
</page-container>
<view class="pay-btn" v-if="zhifu == 0" @click="$c.noDouble(confirmSubmit(1))">
<text>
{{
` ¥${orderData.price}`
}}
</text>
</view>
<view class="pay-btn" v-if="zhifu == 1" @click="$c.noDouble(pay(1))">
<text>{{ orderData.price * 100 }}{{ $config.getAppSetting('currency2_name') }}</text>
</view>
</view>
</uni-popup>
</view>
</template>
@ -252,7 +248,7 @@ export default {
if (this.zhifu == 1) {
}
},
confirmSubmit(type) {

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -85,18 +85,17 @@
<rule-pop ref="rulePop"></rule-pop>
<uni-popup ref="posterPopup" type="center">
<view style="width:650rpx;height:950rpx;">
<image :src="getHaiBao()" style="width: 100%;"></image>
<image :src="logo_image" style="width: 100%;"></image>
</view>
<view style="text-align: center; margin-top: 20rpx;">
<image style="width: 48px;height: 48px;" @click="saveImageToPhotosAlbum()" :src="getXiaZai()"></image>
<image show-menu-by-longpress style="width: 48px;height: 48px;" @click="saveImageToPhotosAlbum()" :src="getXiaZai()"></image>
</view>
</uni-popup>
</view>
</template>
<script>
import UQRCode from 'uqrcodejs'; // npm install uqrcodejs
export default {
data() {
var isH5 = false;
@ -118,6 +117,7 @@
onShareAppMessage() {
let that = this
return {
// imageUrl: this.getHaiBao(),
title: "友达赏,正版潮玩手办一番赏",
path: '/pages/shouye/index?pid=' + uni.getStorageSync('userinfo').ID
}
@ -127,7 +127,7 @@
},
methods: {
saveImageToPhotosAlbum() {
const imageUrl = this.getHaiBao(); //
const imageUrl = this.logo_image; //
//
wx.downloadFile({
url: imageUrl,
@ -202,7 +202,7 @@
success(res) {
that.total = res.data.count
that.commission = res.data.money
// that.logo_image = res.data.share_image
that.logo_image = res.data.share_image
that.mescroll.endByPage(res.data.data.length, res.data.last_page)
if (pageNo == 1) {
that.listdata = res.data.data