Merge branch 'Branch_1' of https://git.u8t.cn/tangxinyue/alipay-emulator into Branch_1
|
|
@ -420,6 +420,14 @@
|
|||
"navigationBarTitleText" : "建设银行",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path" : "game/honor-of-kings",
|
||||
"style" :
|
||||
{
|
||||
"navigationBarTitleText" : "王者荣耀",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
|
|||
|
|
@ -583,8 +583,6 @@
|
|||
import {
|
||||
ref,
|
||||
reactive,
|
||||
watch,
|
||||
nextTick,
|
||||
getCurrentInstance
|
||||
} from "vue";
|
||||
import {
|
||||
|
|
@ -595,16 +593,8 @@
|
|||
onReachBottom
|
||||
} from "@dcloudio/uni-app";
|
||||
const {
|
||||
appContext,
|
||||
proxy
|
||||
} = getCurrentInstance();
|
||||
import {
|
||||
useStore
|
||||
} from '@/store/index.js'
|
||||
const {
|
||||
getCallDetail,
|
||||
setCallDetail
|
||||
} = useStore()
|
||||
let recordPopup = ref()
|
||||
let timePickerPopup = ref()
|
||||
let editTimePopup = ref()
|
||||
|
|
@ -779,24 +769,24 @@
|
|||
onShow(() => {
|
||||
|
||||
})
|
||||
onPageScroll((e) => {
|
||||
if(data.type=='ios'){
|
||||
onPageScroll((e) => {
|
||||
if (data.type == 'ios') {
|
||||
const y = e.scrollTop;
|
||||
const max = 200;
|
||||
const max = 200;
|
||||
let p = Math.min(y / max, 1);
|
||||
data.contentY = -p * (data.info.avatar?65:90);
|
||||
data.contentY = -p * (data.info.avatar ? 65 : 90);
|
||||
data.avatarY = -p * 0;
|
||||
data.text2Y = -p * (data.info.avatar?75:130);
|
||||
data.text2Y = -p * (data.info.avatar ? 75 : 130);
|
||||
data.avatarScale = 1 - p * 0.55; // 1 → 0.6 textScale
|
||||
data.textScale = 1 - p * 0.5; // 1 → 0.6
|
||||
data.textScale = 1 - p * 0.5; // 1 → 0.6
|
||||
|
||||
data.scale = 1 - p * 1; // 1 → 0.6
|
||||
data.headerHeight=Math.max(206,(data.info.avatar?519:325) - y);
|
||||
console.log(data.headerHeight )
|
||||
}else{
|
||||
|
||||
}
|
||||
})
|
||||
data.scale = 1 - p * 1; // 1 → 0.6
|
||||
data.headerHeight = Math.max(206, (data.info.avatar ? 519 : 325) - y);
|
||||
console.log(data.headerHeight)
|
||||
} else {
|
||||
// 其他类型的处理
|
||||
}
|
||||
})
|
||||
onReachBottom(() => {
|
||||
|
||||
})
|
||||
|
|
|
|||
|
|
@ -115,7 +115,8 @@
|
|||
<image class="title-img" src="/static/image/index/qita.png"></image>
|
||||
<view class="video-help-box">
|
||||
<template v-for="item in otherList" :key="item.id">
|
||||
<view class="video-help-item" :style="{ width: (windowWidth - 32) / 4 + 'px' }" @click="clickMenu(item)" v-if="isShowBankIdCard(item)">
|
||||
<view class="video-help-item" :style="{ width: (windowWidth - 32) / 4 + 'px' }"
|
||||
@click="clickMenu(item)" v-if="isShowBankIdCard(item)">
|
||||
<image class="video-help-img" :src="item.icon"></image>
|
||||
<text class="video-help-title">{{ item.name }}</text>
|
||||
</view>
|
||||
|
|
@ -304,6 +305,11 @@ const otherList = [{
|
|||
name: "银行卡",
|
||||
path: "/pages/other/bank/index"
|
||||
},
|
||||
{
|
||||
icon: "/static/image/index/qita/game.png",
|
||||
name: "王者主页",
|
||||
path: "/pages/other/game/honor-of-kings"
|
||||
},
|
||||
]
|
||||
|
||||
const data = reactive({
|
||||
|
|
@ -318,8 +324,8 @@ const data = reactive({
|
|||
vision: "",
|
||||
platform: '', // 添加平台信息,
|
||||
qqgroup: {},
|
||||
idcard:false,
|
||||
bank:false
|
||||
idcard: false,
|
||||
bank: false
|
||||
})
|
||||
|
||||
const {
|
||||
|
|
@ -392,20 +398,20 @@ onShow(() => {
|
|||
}, 800);
|
||||
// #endif
|
||||
})
|
||||
function isShowBankIdCard(item){
|
||||
if(item.name=='银行卡'||item.name=='身份证'){
|
||||
console.log(item.name!='银行卡'||item.name!='身份证')
|
||||
console.log(item.name=='银行卡'&&data.bank)
|
||||
console.log(item.name=='银行卡'&&data.bank)
|
||||
function isShowBankIdCard(item) {
|
||||
if (item.name == '银行卡' || item.name == '身份证') {
|
||||
console.log(item.name != '银行卡' || item.name != '身份证')
|
||||
console.log(item.name == '银行卡' && data.bank)
|
||||
console.log(item.name == '银行卡' && data.bank)
|
||||
}
|
||||
|
||||
if(item.name!='银行卡'&&item.name!='身份证'){
|
||||
|
||||
if (item.name != '银行卡' && item.name != '身份证') {
|
||||
return true
|
||||
}else if(item.name=='银行卡'&&data.bank){
|
||||
} else if (item.name == '银行卡' && data.bank) {
|
||||
return true
|
||||
}else if(item.name=='身份证'&&data.idcard){
|
||||
} else if (item.name == '身份证' && data.idcard) {
|
||||
return true
|
||||
}else{
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
|
@ -422,7 +428,7 @@ const fetchUserData = async () => {
|
|||
fetchUserInfo(),
|
||||
fetchUserConfig()
|
||||
])
|
||||
|
||||
|
||||
// 处理用户信息结果
|
||||
if (userResult.status === 'fulfilled') {
|
||||
console.log('用户信息获取成功')
|
||||
|
|
@ -432,16 +438,16 @@ const fetchUserData = async () => {
|
|||
|
||||
// 处理用户配置结果
|
||||
if (configResult.status === 'fulfilled') {
|
||||
console.log('用户配置获取成功',configResult)
|
||||
console.log('用户配置获取成功', configResult)
|
||||
} else {
|
||||
console.error('获取用户配置失败:', configResult.reason)
|
||||
}
|
||||
|
||||
if(configResult.value.config['client.uniapp.bank']||false){
|
||||
data.bank=true
|
||||
|
||||
if (configResult.value.config['client.uniapp.bank'] || false) {
|
||||
data.bank = true
|
||||
}
|
||||
if(configResult.value.config['client.uniapp.idcard']||false){
|
||||
data.idcard=true
|
||||
if (configResult.value.config['client.uniapp.idcard'] || false) {
|
||||
data.idcard = true
|
||||
}
|
||||
// 刷新页面数据
|
||||
setUserData()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,812 @@
|
|||
<template>
|
||||
<view class="honor-of-kings">
|
||||
<nav-bar title="王者主页" bgColor="#F5F5F5" isRightButton :rightButtonText="rightButtonText"
|
||||
@right-click="onRightClick">
|
||||
</nav-bar>
|
||||
<view>
|
||||
<view class="painter-container" @click="handlePreview">
|
||||
<l-painter isCanvasToTempFilePath @success="onPainterSuccess" :css="`width:750rpx; padding: 24rpx; `">
|
||||
<l-painter-view :css="`width: 100%; position: relative;`">
|
||||
<!-- 直接使用主背景图撑开父容器,移除不必要的 opacity: 0 占位图,避免在大屏渲染时产生黑块 -->
|
||||
<l-painter-image :src="`/static/image/other/game/wangzhe/style-${honorData.type}.jpg`"
|
||||
css="width: 100%; display: block;"></l-painter-image>
|
||||
<!-- 头像 (纯百分比绝对定位,完美适配所有大屏分辨率) -->
|
||||
<l-painter-image :src="honorData.avatar"
|
||||
css="position: absolute; left: 15.6%; top: 22.5%; width: 8.5%; height: 20%; border-radius: 200rpx; object-fit: cover; display: block;"></l-painter-image>
|
||||
|
||||
<!-- 头像框 (压在头像上方) -->
|
||||
<l-painter-image :src="`/static/image/other/game/wangzhe/avatar-frame-${honorData.type}.png`"
|
||||
css="position: absolute; left: 0; top: 0; width: 24.3%; height: 9.15%;"></l-painter-image>
|
||||
|
||||
<!-- 性别图标 -->
|
||||
<l-painter-image :src="`/static/image/other/game/wangzhe/${honorData.gender}.png`"
|
||||
:css="`position: absolute; left: ${honorData.type == 3 ? '29%' : '27%'}; top: 25.5%; width: 1.57%; height: 3.44%;`"></l-painter-image>
|
||||
|
||||
<!-- 昵称渐变图片 -->
|
||||
<l-painter-image v-if="nicknameImage" :src="nicknameImage"
|
||||
:css="`position: absolute; left: ${honorData.type == 3 ? '30.7%' : '28.7%'}; top: 24.5%; width: ${honorData.nickname.length * 1.99}%;`"></l-painter-image>
|
||||
<!-- 地区 -->
|
||||
<l-painter-text :text="honorData.receivedFlowers"
|
||||
css="position: absolute; left: 47.8%; top: 38.5%; font-size: 9rpx; color: #DAF2FF;transform: translateX(-50%);"></l-painter-text>
|
||||
<!-- 收到花束 -->
|
||||
<l-painter-text :text="honorData.receivedFlowers"
|
||||
css="position: absolute; left: 47.8%; top: 38.5%; font-size: 9rpx; color: #DAF2FF;transform: translateX(-50%);font-family: WangZheFont2;"></l-painter-text>
|
||||
<!-- 热度数 -->
|
||||
<l-painter-text :text="honorData.popularityCount"
|
||||
css="position: absolute; left: 51.5%; top: 38.5%; font-size: 9rpx; color: #DAF2FF;transform: translateX(-50%);font-family: WangZheFont2;"></l-painter-text>
|
||||
<!-- 点赞数 -->
|
||||
<l-painter-text :text="honorData.likeCount"
|
||||
css="position: absolute; left: 55.2%; top: 38.5%; font-size: 9rpx; color: #DAF2FF;transform: translateX(-50%);font-family: WangZheFont2;"></l-painter-text>
|
||||
<!-- 荣誉一 -->
|
||||
<l-painter-text :text="honorData.honor1"
|
||||
css="position: absolute; left: 33%; top: 42.67%; font-size: 9rpx; color: #71B1D3;transform: translateX(-50%);"></l-painter-text>
|
||||
<!-- 荣誉二 -->
|
||||
<l-painter-text :text="honorData.honor2"
|
||||
css="position: absolute; left: 62.5%; top: 52.2%; font-size: 8rpx; color: #71B1D3;transform: scale(0.85),translateX(-50%);"></l-painter-text>
|
||||
<!-- 巅峰万强 -->
|
||||
<l-painter-text v-if="honorData.type == 4" :text="honorData.peakStrong"
|
||||
css="position: absolute; left: 34.3%; top: 56.5%; font-size: 8rpx; color: #E9F5FB;transform: translateX(-50%);"></l-painter-text>
|
||||
<!-- 对战场次 -->
|
||||
<l-painter-text :text="honorData.matchCount"
|
||||
css="position: absolute; left: 18.4%; top: 71%; font-size: 12rpx; color: #71B1D3;transform: translateX(-50%);"></l-painter-text>
|
||||
<!-- 对战被赞 -->
|
||||
<l-painter-text :text="honorData.matchLikeCount"
|
||||
css="position: absolute; left: 26.6%; top: 71%; font-size: 12rpx; color: #71B1D3;transform: translateX(-50%);"></l-painter-text>
|
||||
<!-- 图鉴等级 -->
|
||||
<l-painter-text :text="honorData.pokedexLevel"
|
||||
css="position: absolute; left: 42.6%; top: 70%; font-size:9rpx; color: #71B1D3;transform: translateX(-50%);"></l-painter-text>
|
||||
<!-- 皮肤数 -->
|
||||
<l-painter-text :text="honorData.skinCount"
|
||||
css="position: absolute; left: 50.3%; top: 70%; font-size:9rpx; color: #71B1D3;transform: translateX(-50%);"></l-painter-text>
|
||||
<!-- 游戏天数 -->
|
||||
<l-painter-text v-if="honorData.type == 1 || honorData.type == 2" :text="honorData.gameDays"
|
||||
css="position: absolute; left: 26.95%; top: 82.7%; font-size:10rpx; color: #71B1D3;transform: translateX(-50%);"></l-painter-text>
|
||||
<!-- 峡谷对战局数 -->
|
||||
<l-painter-text v-if="honorData.type == 1 || honorData.type == 2"
|
||||
:text="honorData.riftMatchCount"
|
||||
css="position: absolute; left: 42.96%; top: 82.7%; font-size:10rpx; color: #71B1D3;transform: translateX(-50%);"></l-painter-text>
|
||||
<!-- 大乱斗对战局数 -->
|
||||
<l-painter-text v-if="honorData.type == 1 || honorData.type == 2"
|
||||
:text="honorData.aramMatchCount"
|
||||
css="position: absolute; left: 50.65%; top: 82.7%; font-size:10rpx; color: #71B1D3;transform: translateX(-50%);"></l-painter-text>
|
||||
<!-- 亲密度 -->
|
||||
<l-painter-text v-if="honorData.type == 3 || honorData.type == 4" :text="honorData.intimacy"
|
||||
css="position: absolute; left: 46.4%; top: 86.8%; font-size:10rpx; color: #71B1D3;transform: translateX(-50%);font-family: WangZheFont2;"></l-painter-text>
|
||||
|
||||
<!-- 水印 -->
|
||||
<l-painter-image v-if="$isVip()" src="/static/image/other/shuiying.png"
|
||||
:css="`position: absolute; left: 2.5%; bottom: 5%; width: 145.38rpx;height:42.76rpx`"></l-painter-image>
|
||||
</l-painter-view>
|
||||
</l-painter>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 水印 -->
|
||||
<view v-if="$isVip()">
|
||||
<liu-drag-button :canDocking="false" @clickBtn="$goRechargePage('watermark', 'uni_alipay_other_wangzhe')">
|
||||
<c-lottie ref="cLottieRef" :src='$watermark()' width="94px" height='74px' :loop="true"></c-lottie>
|
||||
</liu-drag-button>
|
||||
</view>
|
||||
|
||||
<view class="save-action">
|
||||
<button class="save-btn" @click="handleSave">保存</button>
|
||||
</view>
|
||||
|
||||
<!-- 主题选择区域 -->
|
||||
<view class="theme-selector">
|
||||
<scroll-view scroll-x="true" class="theme-scroll" :show-scrollbar="false">
|
||||
<view class="theme-list">
|
||||
<view class="theme-item" v-for="(item, index) in ['样式一', '样式二', '样式三', '样式四']" :key="index + 1"
|
||||
:class="{ active: honorData.type == index + 1 }" @click="handleChangeTheme(index + 1)">
|
||||
<text class="theme-text">{{ item }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
|
||||
<!-- 数据编辑弹窗 -->
|
||||
<uni-popup ref="editPopup" type="center">
|
||||
<view class="edit-popup-content">
|
||||
<view class="popup-header">
|
||||
<text class="title">编辑主页数据</text>
|
||||
</view>
|
||||
<scroll-view scroll-y class="popup-scroll">
|
||||
<!-- <view class="form-item">
|
||||
<text class="label">样式类型</text>
|
||||
<input class="input" type="number" v-model="tempData.type" placeholder="1-4" />
|
||||
</view> -->
|
||||
<view class="form-item avatar-form-item">
|
||||
<text class="label">头像</text>
|
||||
<view class="avatar-uploader">
|
||||
<view class="avatar-preview" v-if="tempData.avatar">
|
||||
<image class="preview-img" :src="tempData.avatar" mode="aspectFill"></image>
|
||||
<view class="delete-icon" @click="handleDeleteAvatar">
|
||||
<text>×</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="upload-btn" v-else @click="handleChooseAvatar">
|
||||
<text class="plus">+</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<text class="label">昵称</text>
|
||||
<input class="input" type="text" v-model="tempData.nickname" />
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<text class="label">性别</text>
|
||||
<view class="radio-group">
|
||||
<view class="radio-item" @click="tempData.gender = 'man'">
|
||||
<view class="radio-icon" :class="{ active: tempData.gender === 'man' }"></view>
|
||||
<text class="radio-text" :class="{ active: tempData.gender === 'man' }">男</text>
|
||||
</view>
|
||||
<view class="radio-item" @click="tempData.gender = 'female'">
|
||||
<view class="radio-icon" :class="{ active: tempData.gender === 'female' }"></view>
|
||||
<text class="radio-text" :class="{ active: tempData.gender === 'female' }">女</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<text class="label">荣誉一</text>
|
||||
<input class="input" type="text" v-model="tempData.honor1" />
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<text class="label">荣誉二</text>
|
||||
<input class="input" type="text" v-model="tempData.honor2" />
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<text class="label">巅峰万强</text>
|
||||
<input class="input" type="text" v-model="tempData.peakStrong" />
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<text class="label">收到花束</text>
|
||||
<input class="input" type="text" v-model="tempData.receivedFlowers" />
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<text class="label">热度</text>
|
||||
<input class="input" type="text" v-model="tempData.popularityCount" />
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<text class="label">点赞数</text>
|
||||
<input class="input" type="text" v-model="tempData.likeCount" />
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<text class="label">对战场次</text>
|
||||
<input class="input" type="number" v-model="tempData.matchCount" />
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<text class="label">对战被赞</text>
|
||||
<input class="input" type="number" v-model="tempData.matchLikeCount" />
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<text class="label">图鉴等级</text>
|
||||
<input class="input" type="number" v-model="tempData.pokedexLevel" />
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<text class="label">皮肤数</text>
|
||||
<input class="input" type="number" v-model="tempData.skinCount" />
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<text class="label">游戏天数</text>
|
||||
<input class="input" type="number" v-model="tempData.gameDays" />
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<text class="label">峡谷对战局数</text>
|
||||
<input class="input" type="number" v-model="tempData.riftMatchCount" />
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<text class="label">大乱斗局数</text>
|
||||
<input class="input" type="number" v-model="tempData.aramMatchCount" />
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<text class="label">亲密度</text>
|
||||
<input class="input" type="number" v-model="tempData.intimacy" />
|
||||
</view>
|
||||
</scroll-view>
|
||||
<view class="popup-footer">
|
||||
<button class="cancel-btn" @click="closeEditPopup">取消</button>
|
||||
<button class="confirm-btn" @click="confirmEdit">确定</button>
|
||||
</view>
|
||||
</view>
|
||||
</uni-popup>
|
||||
|
||||
<!-- 隐藏的画布:用于原生绘制带有纯正渐变色的文本,再转成图片给到海报插件 -->
|
||||
<view style="position: absolute; left: -9999px; top: -9999px; width: 0; height: 0; overflow: hidden;">
|
||||
<canvas canvas-id="gradientTextCanvas" id="gradientTextCanvas" style="width: 300px; height: 60px;"></canvas>
|
||||
</view>
|
||||
|
||||
<!-- 横向全屏放大预览层 -->
|
||||
<view class="preview-overlay" v-if="showPreview" @click="showPreview = false">
|
||||
<image class="preview-image" :src="finalPosterPath" mode="aspectFill"></image>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, getCurrentInstance } from 'vue'
|
||||
import { onLoad, onReady } from '@dcloudio/uni-app'
|
||||
const rightButtonText = ref("编辑");
|
||||
const instance = getCurrentInstance();
|
||||
const { proxy } = getCurrentInstance();
|
||||
const nicknameImage = ref('');
|
||||
const showPreview = ref(false); // 控制全屏预览
|
||||
const isMountedReady = ref(false);
|
||||
const isFontLoaded = ref(false);
|
||||
|
||||
// 页面表单数据字段
|
||||
const honorData = reactive({
|
||||
type: 1, // 样式类型 1.2.3.4
|
||||
avatar: '/static/image/shopping/pdd/avatars/avatars1.jpg', // 头像
|
||||
nickname: '甜喵小贝', // 昵称
|
||||
gender: 'female', // 性别
|
||||
region: '重庆市两江新区', // 地区
|
||||
honor1: '国服第一小乔', // 荣誉1
|
||||
honor2: '888级大师收藏家·珍耀无双', // 荣誉2
|
||||
matchCount: 58965, // 对战场次
|
||||
matchLikeCount: 9746, // 对战被赞数
|
||||
gameDays: 3127, // 游戏天数
|
||||
pokedexLevel: 104, // 图鉴等级
|
||||
skinCount: 444, // 皮肤数
|
||||
riftMatchCount: 7368, // 峡谷对战局数
|
||||
aramMatchCount: 5193, // 大乱斗对战局数
|
||||
receivedFlowers: 520, // 收到花束
|
||||
popularityCount: '6.0W', // 热度数
|
||||
likeCount: 4883, // 点赞数
|
||||
intimacy: '25147',//亲密度
|
||||
peakStrong: '5632'
|
||||
})
|
||||
|
||||
onLoad(() => {
|
||||
const cachedData = uni.getStorageSync('wangzheHonorData');
|
||||
if (cachedData) {
|
||||
Object.assign(honorData, cachedData);
|
||||
}
|
||||
|
||||
// 进入页面埋点
|
||||
proxy.$apiUserEvent('all', {
|
||||
type: 'click',
|
||||
key: 'wangzhe',
|
||||
prefix: '.uni.other.',
|
||||
value: "王者主页"
|
||||
})
|
||||
const config = uni.getStorageSync('config')
|
||||
console.log("---config---", config);
|
||||
let fontConfig = config.config['client.uniapp.font'];
|
||||
try {
|
||||
if (typeof fontConfig === 'string') {
|
||||
fontConfig = JSON.parse(fontConfig);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('字体配置解析失败', e);
|
||||
}
|
||||
|
||||
console.log("字体地址信息", fontConfig?.wangzhe);
|
||||
// Font loading logic
|
||||
const fontUrl = fontConfig?.wangzhe;
|
||||
const fontUrl2 = fontConfig?.wangzhe2;
|
||||
|
||||
let loadedCount = 0;
|
||||
const totalFonts = 2;
|
||||
const checkAllLoaded = () => {
|
||||
loadedCount++;
|
||||
if (loadedCount >= totalFonts) {
|
||||
isFontLoaded.value = true;
|
||||
if (isMountedReady.value) drawGradientText();
|
||||
}
|
||||
};
|
||||
|
||||
const loadSingleFont = (url, name, storageKey, onComplete) => {
|
||||
if (!url) {
|
||||
console.warn(`未获取到 ${name} 字体地址,回退使用系统默认字体`);
|
||||
onComplete && onComplete();
|
||||
return;
|
||||
}
|
||||
|
||||
const doLoad = (path) => {
|
||||
uni.loadFontFace({
|
||||
family: name,
|
||||
source: `url("${path}")`,
|
||||
success() {
|
||||
console.log(`${name} 字体加载成功`);
|
||||
onComplete && onComplete();
|
||||
},
|
||||
fail(err) {
|
||||
console.error(`${name} 字体加载失败`, err);
|
||||
onComplete && onComplete();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// #ifdef H5
|
||||
// H5 环境直接从 URL 加载字体
|
||||
doLoad(url);
|
||||
// #endif
|
||||
|
||||
// #ifndef H5
|
||||
// 非 H5 环境使用下载和保存逻辑
|
||||
const savedPath = uni.getStorageSync(storageKey);
|
||||
if (savedPath) {
|
||||
doLoad(savedPath);
|
||||
} else {
|
||||
uni.downloadFile({
|
||||
url: url,
|
||||
success: (res) => {
|
||||
if (res.statusCode === 200) {
|
||||
uni.saveFile({
|
||||
tempFilePath: res.tempFilePath,
|
||||
success: (saveRes) => {
|
||||
const saved = saveRes.savedFilePath;
|
||||
uni.setStorageSync(storageKey, saved);
|
||||
console.log(`${name} 字体保存路径`, saved);
|
||||
doLoad(saved);
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error(`保存 ${name} 文件失败`, err);
|
||||
// Fallback: 尝试加载临时路径
|
||||
doLoad(res.tempFilePath);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
onComplete && onComplete();
|
||||
}
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error(`下载 ${name} 字体失败`, err);
|
||||
onComplete && onComplete();
|
||||
}
|
||||
});
|
||||
}
|
||||
// #endif
|
||||
};
|
||||
|
||||
loadSingleFont(fontUrl, 'WangZheFont', 'wangzhe_font_path', checkAllLoaded);
|
||||
loadSingleFont(fontUrl2, 'WangZheFont2', 'wangzhe_font2_path', checkAllLoaded);
|
||||
})
|
||||
|
||||
const finalPosterPath = ref('');
|
||||
|
||||
const editPopup = ref(null);
|
||||
const tempData = reactive({});
|
||||
const newAvatars = ref([]);
|
||||
|
||||
function handleChangeTheme(typeIndex) {
|
||||
honorData.type = typeIndex;
|
||||
uni.setStorageSync('wangzheHonorData', honorData);
|
||||
}
|
||||
|
||||
function onRightClick() {
|
||||
Object.assign(tempData, honorData);
|
||||
newAvatars.value = [];
|
||||
editPopup.value.open();
|
||||
}
|
||||
|
||||
const handleChooseAvatar = () => {
|
||||
uni.chooseImage({
|
||||
count: 1,
|
||||
crop: {
|
||||
quality: 100,
|
||||
width: 400,
|
||||
height: 400
|
||||
},
|
||||
success: (res) => {
|
||||
const tempPath = res.tempFilePaths[0];
|
||||
// #ifndef H5
|
||||
uni.saveFile({
|
||||
tempFilePath: tempPath,
|
||||
success: (saveRes) => {
|
||||
tempData.avatar = saveRes.savedFilePath;
|
||||
newAvatars.value.push(saveRes.savedFilePath);
|
||||
}
|
||||
});
|
||||
// #endif
|
||||
// #ifdef H5
|
||||
tempData.avatar = tempPath;
|
||||
// #endif
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const handleDeleteAvatar = () => {
|
||||
tempData.avatar = '';
|
||||
};
|
||||
|
||||
function closeEditPopup() {
|
||||
// #ifndef H5
|
||||
newAvatars.value.forEach(path => {
|
||||
uni.removeSavedFile({ filePath: path });
|
||||
});
|
||||
// #endif
|
||||
editPopup.value.close();
|
||||
}
|
||||
|
||||
function confirmEdit() {
|
||||
const isNicknameChanged = honorData.nickname !== tempData.nickname;
|
||||
|
||||
// #ifndef H5
|
||||
// 删除多余的新上传文件
|
||||
newAvatars.value.forEach(path => {
|
||||
if (path !== tempData.avatar) {
|
||||
uni.removeSavedFile({ filePath: path });
|
||||
}
|
||||
});
|
||||
// 删除被替换掉的原本地头像
|
||||
if (honorData.avatar !== tempData.avatar && honorData.avatar && !honorData.avatar.startsWith('/static/')) {
|
||||
uni.removeSavedFile({ filePath: honorData.avatar });
|
||||
}
|
||||
// #endif
|
||||
|
||||
Object.assign(honorData, tempData);
|
||||
uni.setStorageSync('wangzheHonorData', honorData);
|
||||
editPopup.value.close();
|
||||
|
||||
// 如果昵称改变了,需要重新绘制原生的渐变文字
|
||||
if (isNicknameChanged && isFontLoaded.value) {
|
||||
drawGradientText();
|
||||
}
|
||||
}
|
||||
|
||||
// 点击画布触发横向预览
|
||||
const handlePreview = () => {
|
||||
if (!finalPosterPath.value) {
|
||||
uni.showToast({ title: '海报仍在生成中,请稍候', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
showPreview.value = true;
|
||||
}
|
||||
|
||||
// 画布生成成功后触发,保存最终的海报路径
|
||||
const onPainterSuccess = (path) => {
|
||||
finalPosterPath.value = path;
|
||||
}
|
||||
|
||||
// 点击保存按钮,将带有渐变字体的最终图片存入相册
|
||||
const handleSave = () => {
|
||||
if (!finalPosterPath.value) {
|
||||
uni.showToast({ title: '图片仍在生成中,请稍候', icon: 'none' })
|
||||
return
|
||||
}
|
||||
uni.saveImageToPhotosAlbum({
|
||||
filePath: finalPosterPath.value,
|
||||
success: () => {
|
||||
uni.showToast({ title: '保存成功', icon: 'success' })
|
||||
},
|
||||
fail: () => {
|
||||
uni.showToast({ title: '保存失败', icon: 'none' })
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
isMountedReady.value = true;
|
||||
if (isFontLoaded.value) {
|
||||
drawGradientText();
|
||||
}
|
||||
})
|
||||
|
||||
// 原生 Canvas 绘制渐变文本导出为图片
|
||||
const drawGradientText = () => {
|
||||
// 以较大的像素绘制以保证生成的图片清晰
|
||||
const fontSize = 40;
|
||||
const textLen = honorData.nickname.length;
|
||||
// 文本的实际像素宽度
|
||||
const canvasWidth = fontSize * textLen;
|
||||
|
||||
const ctx = uni.createCanvasContext('gradientTextCanvas', instance.proxy);
|
||||
ctx.clearRect(0, 0, 300, 60);
|
||||
|
||||
// 设置粗体与字号,使用指定的 WangZheFont 字体
|
||||
ctx.font = `${fontSize}px "WangZheFont", sans-serif`;
|
||||
|
||||
// 原生创建横向线性渐变
|
||||
const grd = ctx.createLinearGradient(0, 0, canvasWidth, 0);
|
||||
grd.addColorStop(0, '#F9D577');
|
||||
grd.addColorStop(1, '#FFF5C4');
|
||||
|
||||
ctx.setFillStyle(grd);
|
||||
ctx.setTextBaseline('top');
|
||||
// 绘制文字
|
||||
ctx.fillText(honorData.nickname, 0, 0);
|
||||
|
||||
ctx.draw(false, () => {
|
||||
// 稍微延迟确保绘制完毕
|
||||
setTimeout(() => {
|
||||
uni.canvasToTempFilePath({
|
||||
canvasId: 'gradientTextCanvas',
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: canvasWidth,
|
||||
height: fontSize + 10,
|
||||
destWidth: canvasWidth * 2, // 2倍图保证海报中的清晰度
|
||||
destHeight: (fontSize + 10) * 2,
|
||||
success: (res) => {
|
||||
nicknameImage.value = res.tempFilePath;
|
||||
}
|
||||
}, instance.proxy)
|
||||
}, 200)
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.preview-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: #000;
|
||||
z-index: 99999;
|
||||
/* 抛弃 flex 布局,防止 width: 100vh 被父级限制而发生坍缩 */
|
||||
}
|
||||
|
||||
.preview-overlay .preview-image {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 100vh;
|
||||
height: 100vw;
|
||||
/* 先拉回自身中心点,再围绕中心点旋转 90 度 */
|
||||
transform: translate(-50%, -50%) rotate(90deg);
|
||||
}
|
||||
|
||||
.honor-of-kings {
|
||||
width: 100%;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.painter-container {
|
||||
width: 100%;
|
||||
max-width: 1000px;
|
||||
/* 限制PC/iPad端的最大宽度 */
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.save-action {
|
||||
margin-top: 60rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding-bottom: 220rpx;
|
||||
/* 留出底部主题固定栏的安全空间 */
|
||||
|
||||
.save-btn {
|
||||
margin-top: 60rpx;
|
||||
width: 316rpx;
|
||||
background: #1777FF;
|
||||
color: #fff;
|
||||
border-radius: 56rpx;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.theme-selector {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
padding-top: 20rpx;
|
||||
padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
|
||||
z-index: 99;
|
||||
|
||||
.theme-scroll {
|
||||
width: 100%;
|
||||
white-space: nowrap;
|
||||
|
||||
::-webkit-scrollbar {
|
||||
display: none;
|
||||
width: 0 !important;
|
||||
height: 0 !important;
|
||||
-webkit-appearance: none;
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
.theme-list {
|
||||
display: inline-block;
|
||||
padding: 0 20rpx;
|
||||
|
||||
.theme-item {
|
||||
display: inline-flex;
|
||||
vertical-align: top;
|
||||
margin: 0 10rpx;
|
||||
padding: 0 40rpx;
|
||||
height: 84rpx;
|
||||
border-radius: 12rpx;
|
||||
background-color: #FFFFFF;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border: 2rpx solid transparent;
|
||||
box-sizing: border-box;
|
||||
transition: all 0.3s;
|
||||
|
||||
&.active {
|
||||
background-color: #fff;
|
||||
border-color: #3B7BFF;
|
||||
|
||||
.theme-text {
|
||||
color: #3B7BFF;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
.theme-text {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.edit-popup-content {
|
||||
background-color: #fff;
|
||||
border-radius: 20rpx;
|
||||
width: 85vw;
|
||||
|
||||
.popup-header {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 40rpx 0 20rpx 0;
|
||||
|
||||
.title {
|
||||
font-size: 34rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
|
||||
.popup-scroll {
|
||||
height: 55vh;
|
||||
padding: 20rpx 40rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.form-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 16rpx;
|
||||
padding-bottom: 16rpx;
|
||||
|
||||
.label {
|
||||
width: 200rpx;
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.avatar-uploader {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.upload-btn {
|
||||
width: 100rpx;
|
||||
height: 100rpx;
|
||||
border-radius: 50%;
|
||||
background-color: #999DA7;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
.plus {
|
||||
font-size: 60rpx;
|
||||
color: #fff;
|
||||
font-weight: 300;
|
||||
margin-top: -6rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.avatar-preview {
|
||||
position: relative;
|
||||
width: 100rpx;
|
||||
height: 100rpx;
|
||||
|
||||
.preview-img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.delete-icon {
|
||||
position: absolute;
|
||||
top: -4rpx;
|
||||
right: -4rpx;
|
||||
width: 32rpx;
|
||||
height: 32rpx;
|
||||
background-color: red;
|
||||
color: #fff;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: 30rpx;
|
||||
line-height: 28rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.input {
|
||||
flex: 1;
|
||||
font-size: 28rpx;
|
||||
background-color: #F7F7F7;
|
||||
border-radius: 12rpx;
|
||||
height: 70rpx;
|
||||
line-height: 70rpx;
|
||||
padding: 0 20rpx;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.radio-group {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 40rpx;
|
||||
|
||||
.radio-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
|
||||
.radio-icon {
|
||||
width: 28rpx;
|
||||
height: 28rpx;
|
||||
border-radius: 50%;
|
||||
border: 2rpx solid #d9d9d9;
|
||||
margin-right: 12rpx;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
|
||||
&.active {
|
||||
border-color: #3B7BFF;
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
width: 14rpx;
|
||||
height: 14rpx;
|
||||
border-radius: 50%;
|
||||
background-color: #3B7BFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.radio-text {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
|
||||
&.active {
|
||||
color: #3B7BFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.popup-footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 20rpx 40rpx 40rpx;
|
||||
|
||||
button {
|
||||
width: 46%;
|
||||
height: 76rpx;
|
||||
line-height: 76rpx;
|
||||
font-size: 30rpx;
|
||||
border-radius: 12rpx;
|
||||
margin: 0;
|
||||
|
||||
&::after {
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
||||
.cancel-btn {
|
||||
background-color: #F4F4F4;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.confirm-btn {
|
||||
background-color: #3B7BFF;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
After Width: | Height: | Size: 7.3 KiB |
|
After Width: | Height: | Size: 278 KiB |
|
After Width: | Height: | Size: 290 KiB |
|
After Width: | Height: | Size: 266 KiB |
|
After Width: | Height: | Size: 249 KiB |
|
After Width: | Height: | Size: 523 B |
|
After Width: | Height: | Size: 1012 B |
|
After Width: | Height: | Size: 128 KiB |
|
After Width: | Height: | Size: 139 KiB |
|
After Width: | Height: | Size: 153 KiB |
|
After Width: | Height: | Size: 160 KiB |