feat:优化上线后的改造

This commit is contained in:
2026-08-26 17:00:58 +08:00
parent d5253e3202
commit 23b30c8f04
11 changed files with 1310 additions and 560 deletions
+115 -10
View File
@@ -1,16 +1,17 @@
<template>
<view class="home_warp" :class="platformType ? '' : 'iosplatformType'" @scroll="onScroll">
<!-- 变色搜索栏 -->
<view class="header_title_warp" :class="{ 'is-scrolled': isScrolled }" :style="mpHeaderStyle">
<view class="language_warp" style="flex: 1; justify-content: flex-start;" @click.stop="jumpSearch">
<view class="header_title_warp" :class="{ 'is-scrolled': isScrolled }" :style="headerContainerStyle">
<view class="language_warp" style="flex: 1; justify-content: flex-start;" :style="languageBoxStyle"
@click.stop="jumpSearch">
<view :style="isMP ? 'display: flex; align-items: center;margin-top: 0;' : 'margin-top: 0;'">
<up-image src="https://static.tbmall.xin/static/home/search_b.png" width="20" height="20"
bgColor="#f1f6ff00"></up-image>
<up-image src="https://static.tbmall.xin/static/home/search_b.png" width="20" height="20" bgColor="#f1f6ff00"
:style="searchIconStyle"></up-image>
</view>
<text class="search_msg_warp" :style="isMP ? 'line-height: 1;' : ''">搜索</text>
<text class="search_msg_warp" :style="searchMsgStyle">搜索</text>
</view>
<view class="message-entry" @click.stop="jumpNewSearch('list')">
<image src="https://static.tbmall.xin/static/chart.png" class="chart_img"></image>
<image src="https://static.tbmall.xin/static/chart.png" class="chart_img" :style="chartImgStyle"></image>
<text v-if="kefuUnreadCount > 0" class="message-entry-badge">{{ formatKefuBadge(kefuUnreadCount) }}</text>
</view>
</view>
@@ -311,7 +312,7 @@ export default {
};
},
onPageScroll(e) {
const top = e ? (e.scrollTop || 0) : 0;
const top = this.getScrollTop(e);
this.scrollTop = top;
this.isScrolled = top > 10;
},
@@ -379,10 +380,59 @@ export default {
if (this.kefuUnreadRefreshTimer) clearTimeout(this.kefuUnreadRefreshTimer);
this.kefuUnreadRefreshTimer = null;
},
mounted() { },
mounted() {
// #ifdef H5
window.addEventListener("scroll", this.handleWindowScroll, true);
const pageBody = document.querySelector("uni-page-body");
if (pageBody) {
pageBody.addEventListener("scroll", this.handleWindowScroll, true);
}
// #endif
},
beforeUnmount() {
// #ifdef H5
window.removeEventListener("scroll", this.handleWindowScroll, true);
const pageBody = document.querySelector("uni-page-body");
if (pageBody) {
pageBody.removeEventListener("scroll", this.handleWindowScroll, true);
}
// #endif
},
methods: {
getScrollTop(e) {
let top = 0;
if (typeof e === "number") {
top = e;
} else if (e && typeof e.scrollTop !== "undefined") {
top = e.scrollTop;
} else if (e && e.detail && typeof e.detail.scrollTop !== "undefined") {
top = e.detail.scrollTop;
}
// #ifdef H5
if (!top) {
const pageBody = document.querySelector("uni-page-body");
const pageWrapper = document.querySelector("uni-page-wrapper");
const homeWarp = document.querySelector(".home_warp");
top = Math.max(
window.pageYOffset || 0,
window.scrollY || 0,
document.documentElement?.scrollTop || 0,
document.body?.scrollTop || 0,
pageBody?.scrollTop || 0,
pageWrapper?.scrollTop || 0,
homeWarp?.scrollTop || 0
);
}
// #endif
return top;
},
handleWindowScroll(e) {
const top = this.getScrollTop(e);
this.scrollTop = top;
this.isScrolled = top > 10;
},
onScroll(e) {
const top = e.detail ? e.detail.scrollTop : (e.target ? e.target.scrollTop : 0);
const top = this.getScrollTop(e);
this.scrollTop = top;
this.isScrolled = top > 10;
},
@@ -846,6 +896,55 @@ export default {
isBlockedDate() {
return useTabber(10);
},
headerContainerStyle() {
const base = this.mpHeaderStyle || {};
if (this.isScrolled) {
return {
...base,
backgroundColor: '#ffffff',
boxShadow: '0 4rpx 16rpx rgba(0, 0, 0, 0.08)',
};
}
return {
...base,
backgroundColor: 'transparent',
boxShadow: 'none',
};
},
languageBoxStyle() {
const base = this.isMP ? { height: this.mpLanguageStyle?.height, boxSizing: 'border-box' } : {};
if (this.isScrolled) {
return {
...base,
backgroundColor: '#f4f5f8',
borderColor: '#e8e8e8',
};
}
return {
...base,
backgroundColor: 'rgba(255, 255, 255, 0.35)',
borderColor: 'rgba(255, 255, 255, 0.4)',
};
},
searchMsgStyle() {
const base = this.isMP ? { lineHeight: '1' } : {};
if (this.isScrolled) {
return { ...base, color: '#888888' };
}
return { ...base, color: 'rgba(255, 255, 255, 0.95)' };
},
searchIconStyle() {
if (this.isScrolled) {
return { filter: 'brightness(0.3)' };
}
return { filter: 'none' };
},
chartImgStyle() {
if (this.isScrolled) {
return { filter: 'brightness(0.2)' };
}
return { filter: 'none' };
},
},
};
</script>
@@ -856,6 +955,9 @@ export default {
min-height: 100vh;
position: relative;
box-sizing: border-box;
width: 100%;
max-width: 100%;
overflow-x: hidden;
&.iosplatformType {
min-height: 100vh;
@@ -980,10 +1082,13 @@ export default {
}
.home_content_z_warp {
position: absolute;
position: relative;
left: 0;
top: 0;
width: 100%;
max-width: 100%;
box-sizing: border-box;
overflow-x: hidden;
}
.logo_banner {
+37 -15
View File
@@ -43,6 +43,21 @@
</up-input>
</view>
<!-- 协议勾选 -->
<view class="policy_warp">
<up-checkbox name="isCheck" usedAlone v-model:checked="isPolicy" shape="circle" activeColor="#7934F6" size="19"
iconSize="18">
<template #label>
<text>已阅读并同意</text>
<text class="policy_a_warp" @click="jumpUserAgreement">《服务协议》</text>
<text>、</text>
<text class="policy_a_warp" @click="jumpPrivacyAgreement">《隐私政策》</text>
<text>、</text>
<text class="policy_a_warp" @click="jumpProductAgreement">《售后保障协议》</text>
</template>
</up-checkbox>
</view>
<view class="login_comm_but login_comm_but_yes" @click="loginFun">
<text style="margin-left: 10rpx">登录</text>
</view>
@@ -91,19 +106,6 @@
</view>
<!-- #endif -->
</view>
<view class="policy_warp">
<up-checkbox name="isCheck" usedAlone v-model:checked="isPolicy" shape="circle" activeColor="#7934F6" size="19"
iconSize="18">
<template #label>
<text>已阅读并同意</text>
<text class="policy_a_warp" @click="jumpUserAgreement">《服务协议》</text>
<text>、</text>
<text class="policy_a_warp" @click="jumpPrivacyAgreement">《隐私政策》</text>
<text>、</text>
<text class="policy_a_warp" @click="jumpProductAgreement">《售后保障协议》</text>
</template>
</up-checkbox>
</view>
<up-toast ref="uToastRef"></up-toast>
<!-- 自定义协议弹窗 -->
@@ -606,8 +608,28 @@ export default {
<style lang="scss" scoped>
.login_warp {
height: calc(100vh - 400rpx);
padding: 150rpx 50rpx 0;
min-height: 100vh;
box-sizing: border-box;
padding: 120rpx 50rpx 40rpx;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.policy_warp {
font-weight: 500;
font-size: 26rpx;
color: #999999;
position: relative !important;
bottom: auto !important;
left: auto !important;
width: 100% !important;
margin-top: 20rpx;
margin-bottom: 28rpx;
.policy_a_warp {
color: $app-policy-color;
}
}
.login_logo_warp {
+44 -13
View File
@@ -6,26 +6,26 @@
</view>
<view class="share-btn-view">
<!-- #ifndef APP-PLUS || H5 -->
<block v-for="(item, index) in btnList" :key="index">
<button v-if="index === 0" open-type="share" hover-class="none" class="share-box share-btn-clean"
@click="onBtn(index)">
<image :src="`https://static.tbmall.xin/static/new/share_${index}.png`" class="share-img">
<block v-for="(item, index) in filteredBtnList" :key="index">
<button v-if="item.originalIndex === 0" open-type="share" hover-class="none" class="share-box share-btn-clean"
@click="onBtn(item.originalIndex)">
<image :src="`https://static.tbmall.xin/static/new/share_${item.originalIndex}.png`" class="share-img">
</image>
<text class="text-24 text-gray">{{ item }}</text>
<text class="text-24 text-gray">{{ item.name }}</text>
</button>
<view v-else class="share-box" @click="onBtn(index)">
<image :src="`https://static.tbmall.xin/static/new/share_${index}.png`" class="share-img">
<view v-else class="share-box" @click="onBtn(item.originalIndex)">
<image :src="`https://static.tbmall.xin/static/new/share_${item.originalIndex}.png`" class="share-img">
</image>
<text class="text-24 text-gray">{{ item }}</text>
<text class="text-24 text-gray">{{ item.name }}</text>
</view>
</block>
<!-- #endif -->
<!-- #ifdef APP-PLUS || H5 -->
<view class="share-box" @click="onShare(index)" v-for="(item, index) in appBtnList" :key="index">
<image :src="`https://cex-pub.s3.ap-southeast-1.amazonaws.com/static/new/share_${index}.png`"
<view class="share-box" @click="onShare(item.originalIndex)" v-for="(item, index) in filteredAppBtnList" :key="index">
<image :src="`https://cex-pub.s3.ap-southeast-1.amazonaws.com/static/new/share_${item.originalIndex}.png`"
class="share-img"></image>
<text class="text-24 text-gray">{{ item }}</text>
<text class="text-24 text-gray">{{ item.name }}</text>
</view>
<!-- #endif -->
@@ -43,10 +43,10 @@
<view class="share-top-image-view">
<image :src="tu" class="tu-img"></image>
<view class="image-view-bottom">
<view class="image-view-bottom-left">
<view class="image-view-bottom-left" :style="{ borderRight: price ? '2rpx solid #EBEBEB' : 'none', flex: price ? 'none' : '1' }">
<text class="text-24 text-gray text-overflow1">{{ text }}</text>
</view>
<view class="image-view-bottom-right">
<view class="image-view-bottom-right" v-if="price">
<text class="text-24 text-zi">¥{{ price }}</text>
</view>
</view>
@@ -86,8 +86,39 @@ export default {
text: {
type: String,
default: ''
},
hideCopyAndQQ: {
type: Boolean,
default: false
}
},
computed: {
isInvitePage() {
if (this.hideCopyAndQQ) return true;
try {
const pages = getCurrentPages();
const current = pages[pages.length - 1];
const route = current?.route || current?.$page?.fullPath || '';
return route.includes('invite_friends');
} catch (e) {
return false;
}
},
filteredBtnList() {
const items = this.btnList.map((name, originalIndex) => ({ name, originalIndex }));
if (this.isInvitePage) {
return items.filter(item => item.name !== '微信');
}
return items;
},
filteredAppBtnList() {
const items = this.appBtnList.map((name, originalIndex) => ({ name, originalIndex }));
if (this.isInvitePage) {
return items.filter(item => item.name !== '复制链接' && item.name !== 'QQ');
}
return items;
}
},
methods: {
@@ -1,166 +1,354 @@
<template>
<view class="invite_friends_warp">
<view class="invite_img">
<image :src="inviteImage" class="invite_imgage" v-if="inviteImage"></image>
<view class="loading" v-else>
<text>加载分享图片中...</text>
</view>
</view>
<view class="invite_but">
<!-- 修改为直接分享图片 -->
<button
class="share-btn"
@click="shareImage"
>
分享好友
</button>
</view>
<up-toast ref="uToastRef"></up-toast>
<qiaobao-assistant page-key="pages/other_package/invite_friends/invite_friends" title="我的推广" />
</view>
</template>
<script>
import { getSharePoster, generateSharePoster } from "@/api/common.js";
export default {
data() {
return {
inviteImage: null,
imageLoading: false,
shareImg:''
};
},
mounted() {
this.getSharePoster();
},
methods: {
// 将base64转换为本地临时文件
async base64ToTempFile(base64Data) {
try {
// 移除base64前缀
const base64 = base64Data.replace(/^data:image\/\w+;base64,/, '');
// 将base64转换为ArrayBuffer
const arrayBuffer = uni.base64ToArrayBuffer(base64);
// 获取文件系统管理器
const fs = uni.getFileSystemManager();
// 生成临时文件路径
const tempFilePath = `${wx.env.USER_DATA_PATH}/share_${Date.now()}.png`;
// 写入文件
fs.writeFileSync(tempFilePath, arrayBuffer, 'binary');
return tempFilePath;
} catch (error) {
console.error('base64转换为临时文件失败:', error);
throw error;
}
},
// 查询分享模版
async getSharePoster() {
this.imageLoading = true;
try {
const resp = await getSharePoster();
if (resp && resp.bizcode === 100) {
const data = resp.data[0];
// 生成分享图
const generateResp = await generateSharePoster({ id: data.id });
if (generateResp && generateResp.bizcode === 100) {
this.inviteImage = generateResp.data;
// 将base64转换为本地临时文件并存储到shareImg
this.shareImg = await this.base64ToTempFile(generateResp.data);
console.log('图片转换成功:', this.shareImg);
}
}
} catch (error) {
console.error("获取分享图失败:", error);
} finally {
this.imageLoading = false;
}
},
// 使用wx.showShareImageMenu分享图片到微信好友
async shareImage() {
// 确保有分享图再执行分享
if (!this.shareImg) {
this.$refs.uToastRef.show({
type: 'warning',
message: "分享图正在加载,请稍后重试",
});
return;
}
try {
// 使用微信原生API wx.showShareImageMenu分享图片
wx.showShareImageMenu({
path: this.shareImg, // 本地临时文件路径
success: () => {
console.log('分享成功');
this.$refs.uToastRef.show({
type: 'success',
message: "分享成功",
});
},
fail: (err) => {
console.error('分享失败:', err);
this.$refs.uToastRef.show({
type: 'error',
message: "分享失败,请稍后重试",
});
}
});
} catch (error) {
console.error('分享过程出错:', error);
this.$refs.uToastRef.show({
type: 'error',
message: "分享失败,请稍后重试",
});
}
}
}
};
</script>
<style lang="scss" scoped>
.invite_friends_warp {
height: calc(100vh - 80rpx);
padding: 10rpx 40rpx 40rpx;
}
.invite_img {
display: flex;
justify-content: center;
align-items: center;
min-height: 920rpx;
}
.invite_imgage {
width: 100%;
height: 920rpx;
}
.loading {
text-align: center;
color: #999;
font-size: 28rpx;
}
.invite_but {
padding: 20rpx 50rpx;
margin-top: 52rpx;
}
.share-btn {
width: 100%;
background: $app-bt-bj;
border-radius: 45rpx;
color: #FFFFFF;
font-weight: 500;
font-size: 32rpx;
// 清除默认按钮样式
border: none;
outline: none;
&:active {
opacity: 0.8;
}
}
<template>
<view class="invite_friends_warp">
<view class="invite_img">
<image :src="inviteImage" class="invite_imgage" v-if="inviteImage"></image>
<view class="loading" v-else>
<text>加载分享图片中...</text>
</view>
</view>
<view class="invite_but">
<button
class="share-btn"
@click="openSharePopup"
>
分享好友
</button>
</view>
<!-- 分享弹窗 -->
<SharePopup
ref="share"
v-if="shareOpen"
:show="shareShow"
@close="onClose"
@wxShare="onWxShare"
@share="onShare"
:id="posterId"
text="我的推广海报"
:tu="inviteImage || shareImg"
></SharePopup>
<up-toast ref="uToastRef"></up-toast>
<qiaobao-assistant page-key="pages/other_package/invite_friends/invite_friends" title="我的推广" />
</view>
</template>
<script>
import { getSharePoster, generateSharePoster } from "@/api/common.js";
import SharePopup from "@/pages/other_package/components/share-popup.vue";
export default {
components: {
SharePopup
},
data() {
return {
inviteImage: null,
imageLoading: false,
shareImg: '',
shareShow: false,
shareOpen: true,
posterId: ''
};
},
mounted() {
this.getSharePoster();
},
onShareAppMessage() {
return {
title: "邀请您体验信任桥TB商城",
path: "/pages/home/home",
imageUrl: this.inviteImage || this.shareImg,
};
},
onShareTimeline() {
return {
title: "邀请您体验信任桥TB商城",
query: "",
imageUrl: this.inviteImage || this.shareImg,
};
},
methods: {
// 打开分享弹窗 / 微信小程序下直接触发朋友圈(图片分享菜单)效果
openSharePopup() {
if (!this.inviteImage && !this.shareImg) {
this.$refs.uToastRef.show({
type: 'warning',
message: "分享图正在加载,请稍后重试",
});
return;
}
// #ifdef MP-WEIXIN
this.onWxShare(1);
return;
// #endif
this.shareShow = true;
},
// 关闭分享弹窗
onClose() {
this.shareShow = false;
},
// 微信小程序环境分享
onWxShare(index) {
console.log('小程序分享', index);
if (index === 0) {
// 微信好友: 已通过 button open-type="share" 触发,关闭弹窗即可
this.onClose();
} else if (index === 1) {
// 朋友圈: 唤起小程序原生分享菜单或系统提示
// #ifdef MP-WEIXIN
if (typeof wx !== 'undefined' && wx.showShareImageMenu && this.shareImg) {
wx.showShareImageMenu({
path: this.shareImg,
fail: () => {
uni.showShareMenu({
withShareTicket: true,
menus: ["shareTimeline"]
});
}
});
} else {
uni.showShareMenu({
withShareTicket: true,
menus: ["shareTimeline"]
});
}
// #endif
this.onClose();
}
},
// App 及 H5 环境分享
onShare(index) {
console.log('App/H5分享', index);
if (index === 0) {
// 微信好友
// #ifdef APP-PLUS
uni.share({
provider: 'weixin',
scene: 'WXSceneSession',
type: 2,
imageUrl: this.shareImg || this.inviteImage,
success: () => {
this.$refs.uToastRef.show({ type: 'success', message: '分享成功' });
},
fail: (err) => {
console.error('App分享好友失败:', err);
this.savePosterToAlbum();
}
});
// #endif
// #ifdef H5
uni.previewImage({ urls: [this.shareImg || this.inviteImage] });
this.$refs.uToastRef.show({ type: 'info', message: '请长按图片发送给好友' });
// #endif
this.onClose();
} else if (index === 1) {
// 微信朋友圈
// #ifdef APP-PLUS
uni.share({
provider: 'weixin',
scene: 'WXSceneTimeline',
type: 2,
imageUrl: this.shareImg || this.inviteImage,
success: () => {
this.$refs.uToastRef.show({ type: 'success', message: '分享成功' });
},
fail: (err) => {
console.error('App分享朋友圈失败:', err);
this.savePosterToAlbum();
}
});
// #endif
// #ifdef H5
uni.previewImage({ urls: [this.shareImg || this.inviteImage] });
this.$refs.uToastRef.show({ type: 'info', message: '请长按图片分享到朋友圈' });
// #endif
this.onClose();
} else if (index === 2) {
// 复制链接
uni.setClipboardData({
data: 'https://m.tbmall.xin/#/pages/home/home',
success: () => {
this.$refs.uToastRef.show({ type: 'success', message: '链接已复制到剪贴板' });
}
});
this.onClose();
} else if (index === 3) {
// 保存图片
this.savePosterToAlbum();
this.onClose();
} else if (index === 4) {
// QQ
this.$refs.uToastRef.show({ type: 'warning', message: '暂未开放' });
this.onClose();
}
},
// 保存海报至相册
savePosterToAlbum() {
const imgPath = this.shareImg || this.inviteImage;
if (!imgPath) {
this.$refs.uToastRef.show({ type: 'warning', message: '海报图片生成中,请稍后重试' });
return;
}
// #ifdef APP-PLUS || MP-WEIXIN
uni.saveImageToPhotosAlbum({
filePath: imgPath,
success: () => {
this.$refs.uToastRef.show({ type: 'success', message: '海报已保存至手机相册' });
},
fail: (err) => {
console.error('保存至相册失败:', err);
if (err && err.errMsg && err.errMsg.includes('auth')) {
uni.showModal({
title: '提示',
content: '需要保存图片到相册权限,请在设置中开启相册权限',
success: (res) => {
if (res.confirm) uni.openSetting();
}
});
} else {
uni.previewImage({ urls: [imgPath] });
}
}
});
// #endif
// #ifdef H5
uni.previewImage({ urls: [imgPath] });
this.$refs.uToastRef.show({ type: 'info', message: '请长按图片保存到手机相册' });
// #endif
},
// 将base64转换为本地临时文件
base64ToTempFile(base64Data) {
return new Promise((resolve, reject) => {
if (!base64Data) {
return reject(new Error('Base64数据为空'));
}
// #ifdef APP-PLUS
try {
const bitmap = new plus.nativeObj.Bitmap('poster_' + Date.now());
bitmap.loadBase64Data(
base64Data,
() => {
const tempPath = `_doc/share_${Date.now()}.png`;
bitmap.save(
tempPath,
{ overwrite: true, format: 'png', quality: 100 },
(e) => {
bitmap.clear();
resolve(e.target);
},
(err) => {
bitmap.clear();
reject(err);
}
);
},
(err) => {
bitmap.clear();
reject(err);
}
);
} catch (err) {
reject(err);
}
// #endif
// #ifdef MP-WEIXIN
try {
const base64 = base64Data.replace(/^data:image\/\w+;base64,/, '');
const arrayBuffer = uni.base64ToArrayBuffer(base64);
const fs = typeof wx !== 'undefined' && wx.getFileSystemManager ? wx.getFileSystemManager() : uni.getFileSystemManager();
const tempFilePath = `${wx.env.USER_DATA_PATH}/share_${Date.now()}.png`;
fs.writeFileSync(tempFilePath, arrayBuffer, 'binary');
resolve(tempFilePath);
} catch (err) {
reject(err);
}
// #endif
// #ifdef H5
resolve(base64Data);
// #endif
});
},
// 查询分享模版
async getSharePoster() {
this.imageLoading = true;
try {
const resp = await getSharePoster();
if (resp && resp.bizcode === 100) {
const data = resp.data[0];
if (data) this.posterId = data.id || '';
// 生成分享图
const generateResp = await generateSharePoster({ id: data.id });
if (generateResp && generateResp.bizcode === 100) {
this.inviteImage = generateResp.data;
// 将base64转换为本地临时文件并存储到shareImg
this.shareImg = await this.base64ToTempFile(generateResp.data);
console.log('图片转换成功:', this.shareImg);
}
}
} catch (error) {
console.error("获取分享图失败:", error);
} finally {
this.imageLoading = false;
}
}
}
};
</script>
<style lang="scss" scoped>
.invite_friends_warp {
height: calc(100vh - 80rpx);
padding: 10rpx 40rpx 40rpx;
}
.invite_img {
display: flex;
justify-content: center;
align-items: center;
min-height: 920rpx;
}
.invite_imgage {
width: 100%;
height: 920rpx;
}
.loading {
text-align: center;
color: #999;
font-size: 28rpx;
}
.invite_but {
padding: 20rpx 50rpx;
margin-top: 52rpx;
}
.share-btn {
width: 100%;
background: $app-bt-bj;
border-radius: 45rpx;
color: #FFFFFF;
font-weight: 500;
font-size: 32rpx;
// 清除默认按钮样式
border: none;
outline: none;
&:active {
opacity: 0.8;
}
}
</style>