Merge branch 'Branch_1' of https://git.u8t.cn/tangxinyue/alipay-emulator into Branch_1

This commit is contained in:
小李 2026-06-18 09:44:42 +08:00
commit bc53f9552e
24 changed files with 2705 additions and 6 deletions

View File

@ -0,0 +1,97 @@
<template>
<text
class="custom-icon"
:class="iconClass"
:style="{ color: color, fontSize: size + 'px' }"
@click="$emit('click')"
></text>
</template>
<script>
export default {
name: "my-icon",
props: {
// 'icon-home'
name: {
type: String,
default: ""
},
//
color: {
type: String,
default: "#333333"
},
// rpx
size: {
type: [Number, String],
default: 16
}
},
computed: {
iconClass() {
// 'custom-icon' font-family
const name = `icon-${this.name}`;
return name;
}
}
}
</script>
<style scoped>
/* 1. 引入并定义字体名称 */
@font-face {
font-family: 'myIconFont';
/* 微信小程序若本地引用报错,可换成 base64 字符串或网络绝对路径 */
src: url('@/static/font/iconfont.ttf') format('truetype');
}
/* 2. 定义基础样式 */
.custom-icon {
font-family: "myIconFont" !important;
font-size: inherit;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
display: inline-block;
}
.icon-sanjiaozhou-maohao:before {
content: "\e630";
}
.icon-sanjiaozhou-fengexian:before {
content: "\e631";
}
.icon-sanjiaozhou-douhao:before {
content: "\e626";
}
.icon-sanjiaozhou-0:before {
content: "\e629";
}
.icon-sanjiaozhou-1:before {
content: "\e627";
}
.icon-sanjiaozhou-2:before {
content: "\e62b";
}
.icon-sanjiaozhou-3:before {
content: "\e62c";
}
.icon-a-sanjiaozhou-4:before {
content: "\e62f";
}
.icon-sanjiaozhou-5:before {
content: "\e62d";
}
.icon-sanjiaozhou-6:before {
content: "\e628";
}
.icon-sanjiaozhou-7:before {
content: "\e625";
}
.icon-sanjiaozhou-8:before {
content: "\e62a";
}
.icon-sanjiaozhou-9:before {
content: "\e62e";
}
</style>

View File

@ -429,6 +429,22 @@
"navigationStyle": "custom" "navigationStyle": "custom"
} }
}, },
{
"path" : "game/sanjiaozhou",
"style" :
{
"navigationBarTitleText" : "三角洲",
"navigationStyle": "custom"
}
},
{
"path" : "game/index",
"style" :
{
"navigationBarTitleText" : "游戏首页",
"navigationStyle": "custom"
}
},
{ {
"path" : "wx-payment-success/wx-payment-success", "path" : "wx-payment-success/wx-payment-success",
"style" : "style" :
@ -476,6 +492,14 @@
"navigationBarTitleText" : "广东", "navigationBarTitleText" : "广东",
"navigationStyle": "custom" "navigationStyle": "custom"
} }
},
{
"path" : "video-chat/video-chat",
"style" :
{
"navigationBarTitleText" : "视频单聊",
"navigationStyle": "custom"
}
} }
] ]
}, },

View File

@ -275,6 +275,11 @@ const otherList = [{
name: "视频群聊", name: "视频群聊",
path: "/pages/other/video-group-chat/video-group-chat" path: "/pages/other/video-group-chat/video-group-chat"
}, },
{
icon: "/static/image/index/qita/shipinqunliao.png",
name: "视频单聊",
path: "/pages/other/video-chat/video-chat"
},
{ {
icon: "/static/image/index/qita/zhifuchenggong-wx.png", icon: "/static/image/index/qita/zhifuchenggong-wx.png",
name: "微信支付", name: "微信支付",
@ -297,8 +302,8 @@ const otherList = [{
}, },
{ {
icon: "/static/image/index/qita/game.png", icon: "/static/image/index/qita/game.png",
name: "王者主页", name: "游戏",
path: "/pages/other/game/honor-of-kings" path: "/pages/other/game/index"
}, },
{ {
icon: "/static/image/other/certificate/certificate.png", icon: "/static/image/other/certificate/certificate.png",

150
pages/other/game/index.vue Normal file
View File

@ -0,0 +1,150 @@
<template>
<view class="container">
<nav-bar title="游戏平台" bgColor="#F0F4F9"></nav-bar>
<view class="menu-container">
<view class="card" v-for="(item, index) in menuList" :key="index" style=" background: #ffffff ">
<text class="card-title">{{ item.name }}</text>
<view class="icon-wrapper">
<view class="icon-shadow" :style="{ background: item.shadowColor }"></view>
<image class="icon-img" :src="`/static/image/other/game/${item.icon}.png`" mode="aspectFit"></image>
</view>
<view class="btn" style="background: linear-gradient( 327deg, #1777FF 0%, #17CDFF 100%);"
@click="goTo(item.url)">立即进入</view>
</view>
</view>
</view>
</template>
<script setup>
import {
ref,
getCurrentInstance
} from 'vue';
import {
onLoad
} from '@dcloudio/uni-app'
const {
proxy
} = getCurrentInstance();
onLoad(() => {
proxy.$apiUserEvent('all', {
type: 'event',
key: 'game',
prefix: '.uni.other.',
value: '游戏'
})
})
const menuList = ref([{
name: "王者荣耀",
icon: "wangzhe",
shadowColor: "#FF604F",
url: "/pages/other/game/honor-of-kings"
}, {
name: "三角洲",
icon: "sanjiaozhou",
shadowColor: "#12C96A",
url: "/pages/other/game/sanjiaozhou"
}, {
name: "和平精英",
icon: "hepingjingying",
shadowColor: "#4992F2",
url: ""
}, {
name: "无畏契约",
icon: "wuweiqiyue",
shadowColor: "#FF3333",
url: ""
}]);
const goTo = (url) => {
if (url) {
uni.navigateTo({
url: url
})
} else {
uni.showToast({
title: "开发中",
icon: "none"
})
}
}
</script>
<style>
page {
background-color: #F0F4F9;
}
</style>
<style lang="less" scoped>
.container {
min-height: 100vh;
}
.menu-container {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
padding: 24rpx;
}
.card {
width: calc(50% - 14rpx);
border-radius: 28rpx 28rpx 28rpx 28rpx;
margin-bottom: 24rpx;
display: flex;
flex-direction: column;
align-items: center;
padding-top: 48rpx;
padding-bottom: 40rpx;
box-sizing: border-box;
border: 1px solid #ffffff;
}
.card-title {
font-size: 28rpx;
color: #1A1A1A;
font-weight: 500;
}
.icon-wrapper {
margin-top: 40rpx;
margin-bottom: 62rpx;
position: relative;
width: 116rpx;
height: 116rpx;
display: flex;
justify-content: center;
align-items: center;
}
.icon-img {
width: 116rpx;
height: 116rpx;
z-index: 2;
}
.icon-shadow {
position: absolute;
bottom: -10rpx;
width: 100rpx;
height: 8rpx;
border-radius: 50%;
filter: blur(5px);
z-index: 1;
opacity: 0.6;
}
.btn {
padding: 0 20rpx;
height: 56rpx;
line-height: 56rpx;
border-radius: 16rpx 16rpx 16rpx 16rpx;
color: #fff;
font-size: 24rpx;
text-align: center;
font-weight: 500;
}
</style>

View File

@ -0,0 +1,833 @@
<template>
<view class="honor-of-kings">
<nav-bar title="三角洲结算页" bgColor="#F5F5F5" isRightButton :rightButtonText="rightButtonText"
@right-click="onRightClick">
</nav-bar>
<view style="padding: 8px;box-sizing: border-box;">
<view class="painter-container" @click="handlePreview"
:style="`width:calc(100vw - 16px) ; height: ${posterContainerHeight}px; overflow: hidden; position: relative; transform: translateZ(0);`">
<!-- 运用 js 算出的无误差纯数字 scale 来实现高清画板的等比缩小彻底兼容所有老旧内核不再被截断 -->
<view
:style="`width: 750px; transform: scale(${posterScaleRatio}); transform-origin: left top; position: absolute; left: 0; top: 0;`">
<l-painter isCanvasToTempFilePath @success="onPainterSuccess" :css="`width:750px;`">
<l-painter-view :css="`width: 750.0px; position: relative;`">
<!-- 直接使用主背景图撑开父容器移除不必要的 opacity: 0 占位图避免在大屏渲染时产生黑块 -->
<l-painter-image :src="`/static/image/other/game/sanjiaozhou/style-${data.type}.jpg`"
css="width: 750.0px; display: block;"></l-painter-image>
<l-painter-view css="position: absolute; left: 73px; top: 113px; white-space: nowrap;">
<!-- 地图 -->
<l-painter-image :src="`/static/image/other/game/sanjiaozhou/line.png`"
css="display: inline-block; vertical-align: middle; height: 22px; width: 1px;"></l-painter-image>
<l-painter-view
css="display: inline-block; vertical-align: middle; padding-left: 4px; min-width: 60px;">
<l-painter-text
css="display: block; color:#CDCDCD; font-size:6px; line-height:6px; margin-bottom: 4px; white-space: nowrap;"
:text="data.map.level"></l-painter-text>
<l-painter-text
css="display: block; color:#CDCDCD; font-size:10px; line-height:10px; white-space: nowrap;"
:text="data.map.name"></l-painter-text>
</l-painter-view>
<!-- 撤离点 -->
<l-painter-image :src="`/static/image/other/game/sanjiaozhou/line.png`"
css="display: inline-block; vertical-align: middle; height: 22px; width: 1px;margin-left: 2px;"></l-painter-image>
<l-painter-view
css="display: inline-block; vertical-align: middle; padding-left: 4px; min-width: 60px;">
<l-painter-text
css="display: block; color:#CDCDCD; font-size:6px; line-height:6px; margin-bottom: 4px; white-space: nowrap;"
text="撤离点"></l-painter-text>
<l-painter-text
css="display: block; color:#CDCDCD; font-size:10px; line-height:10px; white-space: nowrap;"
:text="data.extractionPoint"></l-painter-text>
</l-painter-view>
<!-- 时长 -->
<l-painter-image :src="`/static/image/other/game/sanjiaozhou/line.png`"
css="display: inline-block; vertical-align: middle; height: 22px; width: 1px;margin-left: 2px;"></l-painter-image>
<l-painter-view
css="display: inline-block; vertical-align: middle; padding-left: 4px; min-width: 60px;">
<l-painter-text
css="display: block; color:#CDCDCD; font-size:6px; line-height:6px; margin-bottom: 4px; white-space: nowrap;"
text="时长"></l-painter-text>
<l-painter-text v-for="(char, idx) in String(data.duration).split('')"
:key="'durationNum_' + idx" :css="getTimeCharStyle(char, idx, $system)"
:text="getDefeatIconText(char)"></l-painter-text>
</l-painter-view>
<!-- 对局时间 -->
<l-painter-image :src="`/static/image/other/game/sanjiaozhou/line.png`"
css="display: inline-block; vertical-align: middle; height: 22px; width: 1px;margin-left: 2px;"></l-painter-image>
<l-painter-view
css="display: inline-block; vertical-align: middle; padding-left: 4px; min-width: 60px;">
<l-painter-text
css="display: block; color:#CDCDCD; font-size:6px; line-height:6px; margin-bottom: 4px; white-space: nowrap;"
text="对局时间"></l-painter-text>
<!-- <l-painter-text
css="display: block; color:#CDCDCD; font-size:10px; line-height:10px; white-space: nowrap;"
:text="data.map.name"></l-painter-text> -->
<l-painter-text v-for="(char, idx) in String(data.matchTime).split('')"
:key="'matchTimeNum_' + idx" :css="getTimeCharStyle(char, idx, $system)"
:text="getDefeatIconText(char)"></l-painter-text>
</l-painter-view>
</l-painter-view>
<!-- 本局收获 -->
<l-painter-view
css="position: absolute; left: 73px; top: 175px; white-space: nowrap; display: block;">
<l-painter-text
v-for="(char, idx) in String(data.harvest).replace(/\B(?=(\d{3})+(?!\d))/g, ',').split('')"
:key="'harvestNum_' + idx" :css="getHarvestCharStyle(char, idx, $system)"
:text="getDefeatIconText(char)"></l-painter-text>
</l-painter-view>
<!-- 击败干员 -->
<l-painter-view
css="position: absolute; left: 73px; top: 250px; white-space: nowrap; display: block;">
<l-painter-text v-for="(char, idx) in String(data.defeat).split('')"
:key="'bigNum_' + idx"
:css="`display: inline-block; color:#DBE5E6; font-size:22px; line-height:6px; margin-bottom: 4px; font-family: myIconFont; margin-left: ${idx === 0 ? '0' : '-2px'};`"
:text="getDefeatIconText(char)"></l-painter-text>
</l-painter-view>
<!-- 高光击败 -->
<l-painter-view v-for="(item, index) in data.defeatInfo" :key="index"
:css="`display: inline-block; vertical-align: middle;position: absolute; left: 73px; top: ${295 + index * 16}px; `">
<l-painter-view css="display: inline-block;">
<l-painter-image src="/static/image/other/game/sanjiaozhou/kill.png"
css="width: 12px; height: 12px; display: inline-block;margin-right:2px"></l-painter-image>
<l-painter-text
css="display: inline-block; color:#DBE5E6; font-size:9px; line-height:6px; margin-bottom: 4px; white-space: nowrap;"
:text="`${item.name} [${item.rank}]`"></l-painter-text>
</l-painter-view>
</l-painter-view>
<!-- 玩家名称 -->
<l-painter-text
css="display: block; color:#CDCDCD; font-size:10px; line-height:10px; white-space: nowrap;transform:rotate(10deg);position:absolute;left:424px;top:310px;"
:text="data.name"></l-painter-text>
<l-painter-text
css="display: block; color:#CDCDCD; font-size:7px; line-height:10px; white-space: nowrap;transform:rotate(10deg);position:absolute;left:422px;top:321px;opacity: 0.6;"
text="信守不渝"></l-painter-text>
<!-- 水印 -->
<l-painter-image v-if="$isVip()" src="/static/image/other/shuiying.png"
:css="`position: absolute; left: 18.8px; bottom: 17.1px; width: 145.38px;height:42.76px`"></l-painter-image>
</l-painter-view>
</l-painter>
</view>
</view>
</view>
<!-- 水印 -->
<view v-if="$isVip()">
<liu-drag-button :canDocking="false"
@clickBtn="$goRechargePage('watermark', 'uni_alipay_other_sanjiaozhou')">
<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: data.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="text" v-model="tempData.name" />
</view>
<view class="form-item">
<text class="label">地图级别</text>
<view class="button-group">
<view class="button-item" :class="{ active: tempData.map.level === '普通' }"
@click="tempData.map.level = '普通'">普通</view>
<view class="button-item" :class="{ active: tempData.map.level === '机密' }"
@click="tempData.map.level = '机密'">机密</view>
<view class="button-item" :class="{ active: tempData.map.level === '绝密' }"
@click="tempData.map.level = '绝密'">绝密</view>
</view>
</view>
<view class="form-item">
<text class="label">地图名称</text>
<input class="input" type="text" v-model="tempData.map.name" />
</view>
<view class="form-item">
<text class="label">撤离点</text>
<input class="input" type="text" v-model="tempData.extractionPoint" />
</view>
<view class="form-item">
<text class="label">对局时间</text>
<picker mode="multiSelector" :range="matchTimeRange" :value="matchTimeIndex"
@change="onMatchTimeChange" class="input"
style="height: 36px; line-height: 36px; display: flex; align-items: center;">
<text>{{ tempData.matchTime || '请选择时间' }}</text>
</picker>
</view>
<view class="form-item">
<text class="label">时长</text>
<picker mode="multiSelector" :range="durationRange" :value="durationIndex"
@change="onDurationChange" class="input"
style="height: 36px; line-height: 36px; display: flex; align-items: center;">
<text>{{ tempData.duration || '请选择时长' }}</text>
</picker>
</view>
<view class="form-item">
<text class="label">本局收获</text>
<input class="input" type="text" v-model="tempData.harvest" />
</view>
<view class="form-item">
<text class="label">击败干员</text>
<input class="input" type="text" v-model="tempData.defeat" @input="onDefeatChange" />
</view>
<view style="border: 1px dashed #007aff;padding: 10px;border-radius: 8px;"
v-if="tempData.defeatInfo && tempData.defeatInfo.length > 0">
<view v-for="(item, index) in tempData.defeatInfo" :key="'defeat_' + index"
style="margin-bottom: 10px;">
<view
style="display: flex; justify-content: flex-end; gap: 15px; margin-bottom: 5px; font-size: 12px; height: 16px;"
v-if="tempData.defeatInfo.length > 1">
<text v-if="index > 0" @click="moveDefeatUp(index)"
style="color: #007aff; cursor: pointer;"> 上移</text>
<text v-if="index < tempData.defeatInfo.length - 1" @click="moveDefeatDown(index)"
style="color: #007aff; cursor: pointer;"> 下移</text>
</view>
<view class="form-item">
<text class="label">被击败者{{ index + 1 }}昵称</text>
<input class="input" type="text" v-model="item.name" />
</view>
<view class="form-item" style="margin-bottom: 0;">
<text class="label">被击败者{{ index + 1 }}段位</text>
<input class="input" type="text" v-model="item.rank" />
</view>
</view>
</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 class="preview-overlay" v-if="showPreview" @click="showPreview = false">
<image class="preview-image" :src="finalPosterPath" mode="aspectFit"></image>
</view>
</view>
</template>
<script setup>
import { ref, reactive, onMounted, getCurrentInstance } from 'vue'
import { onLoad, onUnload } from '@dcloudio/uni-app'
const rightButtonText = ref("编辑");
const { proxy } = getCurrentInstance();
const showPreview = ref(false); //
const isMountedReady = ref(false);
// css calc()
const sysInfo = uni.getSystemInfoSync();
const windowWidth = sysInfo.windowWidth || 375;
const posterScaleRatio = windowWidth / 750;
const posterContainerHeight = 430 * posterScaleRatio;
//
const data = reactive({
type: 1, // 1.2.3.4
name: '大坝猛攻哥',
map: {
name: '零号大坝',
level: '机密'
},
extractionPoint: '工业电梯',
duration: "00:25:19",
matchTime: '02-32 21:02',
harvest: '13526559',
defeat: '1',
defeatInfo: [
{
name: '大坝贪吃鼠',
rank: "黑鹰II"
},
]
})
// my-icon iconfont Unicode
const digitIconMap = {
'0': '\ue629', '1': '\ue627', '2': '\ue62b', '3': '\ue62c', '4': '\ue62f',
'5': '\ue62d', '6': '\ue628', '7': '\ue625', '8': '\ue62a', '9': '\ue62e',
',': '\ue626', ':': '\ue630',
};
const getDefeatIconText = (defeatStr) => {
if (!defeatStr && defeatStr !== 0) return '';
return String(defeatStr).split('').map(char => digitIconMap[char] || char).join('');
};
const getTimeCharStyle = (char, idx, system) => {
const fontSize = char === '-' ? 12 : (char === ':' ? 4 : 10);
let marginLeft = '-2px';
if (idx === 0 || system === 'iOS') {
marginLeft = char === '-' ? '2px' : '0px';
}
let marginRight = '0';
if ([' ', '-', ':'].includes(char)) {
if (char === '-' && system === 'Android') {
marginLeft = '0px'
}
if (char === ':' && system === 'Android') {
marginRight = '-1px';
marginLeft = '-1px'
} else if (char === ' ' && system === 'Android') {
marginRight = '2px';
} else {
marginRight = '1rpx';
}
}
return `display: inline-block; vertical-align: bottom; color:#CDCDCD; font-size:${fontSize}px; line-height:6px; margin-bottom: 4px; font-family:myIconFont; margin-left: ${marginLeft}; margin-right: ${marginRight};`;
};
const getHarvestCharStyle = (char, idx, system) => {
const fontSize = char === ',' ? 6 : 22;
let marginLeft = idx === 0 ? '0' : (char === ',' ? (system === 'iOS' ? '3px' : '1px') : (system === 'iOS' ? '2px' : '-3px'));
let marginRight = char === ',' && system === 'Android' ? '2px' : '0';
let marginTop = char === ',' ? '3px' : '0';
return `display: inline-block; vertical-align: bottom; color:#DBE5E6; font-size:${fontSize}px; margin-bottom: 4px; font-family: myIconFont; margin-left: ${marginLeft}; margin-right: ${marginRight}; margin-top: ${marginTop};`;
};
onLoad(() => {
const cachedData = uni.getStorageSync('sanjiaozhouData');
if (cachedData) {
Object.assign(data, cachedData);
}
uni.$on('editFormPhoto', (info) => {
tempData.avatar = info;
// #ifndef H5
if (info && !info.startsWith('/static/')) {
newAvatars.value.push(info);
}
// #endif
});
//
proxy.$apiUserEvent('all', {
type: 'click',
key: 'sanjiaozhou',
prefix: '.uni.other.',
value: "三角洲结算页"
})
//
uni.loadFontFace({
family: 'myIconFont',
source: `url("/static/font/iconfont.ttf")`,
success() {
console.log('myIconFont 字体加载成功');
},
fail(err) {
console.error('myIconFont 字体加载失败', err);
}
});
});
onUnload(() => {
uni.$off('editFormPhoto');
})
const finalPosterPath = ref('');
const editPopup = ref(null);
const tempData = reactive({
map: {},
defeatInfo: [{}]
});
const newAvatars = ref([]);
const onDefeatChange = (e) => {
const val = parseInt(e.detail.value) || 0;
const maxAllowed = Math.min(3, Math.max(0, val));
if (tempData.defeatInfo.length < maxAllowed) {
const toAdd = maxAllowed - tempData.defeatInfo.length;
for (let i = 0; i < toAdd; i++) {
tempData.defeatInfo.push({ name: '', rank: '' });
}
} else if (tempData.defeatInfo.length > maxAllowed) {
tempData.defeatInfo.splice(maxAllowed);
}
};
const moveDefeatUp = (index) => {
if (index > 0) {
const item = tempData.defeatInfo.splice(index, 1)[0];
tempData.defeatInfo.splice(index - 1, 0, item);
}
};
const moveDefeatDown = (index) => {
if (index < tempData.defeatInfo.length - 1) {
const item = tempData.defeatInfo.splice(index, 1)[0];
tempData.defeatInfo.splice(index + 1, 0, item);
}
};
//
const durationRange = ref([
Array.from({ length: 100 }, (_, i) => (i < 10 ? '0' + i : i + '')),
Array.from({ length: 60 }, (_, i) => (i < 10 ? '0' + i : i + '')),
Array.from({ length: 60 }, (_, i) => (i < 10 ? '0' + i : i + ''))
]);
const durationIndex = ref([0, 0, 0]);
const onDurationChange = (e) => {
const val = e.detail.value;
durationIndex.value = val;
tempData.duration = `${durationRange.value[0][val[0]]}:${durationRange.value[1][val[1]]}:${durationRange.value[2][val[2]]}`;
};
//
const matchTimeRange = ref([
Array.from({ length: 12 }, (_, i) => (i + 1 < 10 ? '0' + (i + 1) : (i + 1) + '')),
Array.from({ length: 31 }, (_, i) => (i + 1 < 10 ? '0' + (i + 1) : (i + 1) + '')),
Array.from({ length: 24 }, (_, i) => (i < 10 ? '0' + i : i + '')),
Array.from({ length: 60 }, (_, i) => (i < 10 ? '0' + i : i + ''))
]);
const matchTimeIndex = ref([0, 0, 0, 0]);
const onMatchTimeChange = (e) => {
const val = e.detail.value;
matchTimeIndex.value = val;
tempData.matchTime = `${matchTimeRange.value[0][val[0]]}-${matchTimeRange.value[1][val[1]]} ${matchTimeRange.value[2][val[2]]}:${matchTimeRange.value[3][val[3]]}`;
};
const initPickerIndexes = () => {
if (tempData.duration) {
const parts = tempData.duration.split(':');
if (parts.length === 3) {
durationIndex.value = [parseInt(parts[0]) || 0, parseInt(parts[1]) || 0, parseInt(parts[2]) || 0];
}
}
if (tempData.matchTime) {
const spaceSplit = tempData.matchTime.split(' ');
if (spaceSplit.length === 2) {
const dateParts = spaceSplit[0].split('-');
const timeParts = spaceSplit[1].split(':');
if (dateParts.length === 2 && timeParts.length === 2) {
matchTimeIndex.value = [(parseInt(dateParts[0]) || 1) - 1, (parseInt(dateParts[1]) || 1) - 1, parseInt(timeParts[0]) || 0, parseInt(timeParts[1]) || 0];
}
}
}
};
function handleChangeTheme(typeIndex) {
//
finalPosterPath.value = '';
data.type = typeIndex;
uni.setStorageSync('sanjiaozhouData', data);
}
function onRightClick() {
Object.assign(tempData, JSON.parse(JSON.stringify(data)));
initPickerIndexes();
newAvatars.value = [];
editPopup.value.open();
}
function closeEditPopup() {
// #ifndef H5
newAvatars.value.forEach(path => {
uni.removeSavedFile({ filePath: path });
});
// #endif
editPopup.value.close();
}
function confirmEdit() {
// #ifndef H5
//
newAvatars.value.forEach(path => {
if (path !== tempData.avatar) {
uni.removeSavedFile({ filePath: path });
}
});
//
if (data.avatar !== tempData.avatar && data.avatar && !data.avatar.startsWith('/static/')) {
uni.removeSavedFile({ filePath: data.avatar });
}
// #endif
Object.assign(data, tempData);
uni.setStorageSync('sanjiaozhouData', data);
editPopup.value.close();
//
finalPosterPath.value = '';
}
//
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;
})
</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 {
/* 强制画板缩放到设备屏幕宽度,避免物理 px 超出屏幕 */
padding: 8px;
zoom: calc(100vw / 798);
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;
}
}
}
}
.button-group {
display: flex;
align-items: center;
flex-wrap: nowrap;
.button-item {
padding: 4rpx 16rpx;
border-radius: 30rpx;
font-size: 26rpx;
margin-right: 16rpx;
color: #666;
background-color: #F7F7F7;
border: 2rpx solid transparent;
white-space: nowrap;
cursor: pointer;
transition: all 0.2s;
&.active {
color: #3B7BFF;
background-color: rgba(59, 123, 255, 0.1);
border-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>

File diff suppressed because it is too large Load Diff

View File

@ -2,18 +2,22 @@
<view class="container"> <view class="container">
<nav-bar bgColor="transparent" :isBack="false"> <nav-bar bgColor="transparent" :isBack="false">
<template v-slot:center> <template v-slot:center>
<view class="flex-cneter title-box flex"> <view class="flex-cneter title-box flex" style="margin-top: 24rpx;">
<image class="wx-logo" src="/static/image/other/payment-success/payment-success.png"></image> <image class="wx-logo" src="/static/image/other/payment-success/payment-success.png"></image>
<text class="title">支付成功</text> <text class="title">支付成功</text>
</view> </view>
</template> </template>
</nav-bar> </nav-bar>
<view class="content-box flex-column flex-cneter" @click="editPaymentInfo"> <view class="content-box flex-column flex-cneter" @click="editPaymentInfo">
<image v-if="paymetInfo.userImg" class="user-img" :src="paymetInfo.userImg"></image>
<text class="product-name">{{ paymetInfo.productName }}</text> <text class="product-name">{{ paymetInfo.productName }}</text>
<view class="money flex wx-font-medium"> <view class="money flex wx-font-medium" :style="{ 'margin-top': paymetInfo.userImg ? '78rpx' : '46rpx' }">
<image class="symbol" src="/static/image/other/payment-success/symbol-yuan.png"></image>{{ <image class="symbol" src="/static/image/other/payment-success/symbol-yuan.png"></image>{{
Number(paymetInfo.money).toFixed(2) }} Number(paymetInfo.money).toFixed(2) }}
</view> </view>
<image v-if="paymetInfo.isAd" class="ad-img" src="/static/image/other/payment-success/ad-img.png"
mode="widthFix">
</image>
</view> </view>
<view class="finish-butn" @click="util.goBack()">完成</view> <view class="finish-butn" @click="util.goBack()">完成</view>
@ -21,6 +25,18 @@
<uni-popup ref="editPaymentInfoPopup" type="center"> <uni-popup ref="editPaymentInfoPopup" type="center">
<view class="edit-popup"> <view class="edit-popup">
<view class="title">修改支付信息</view> <view class="title">修改支付信息</view>
<view class="input-item flex-align-center">
<text class="label">用户头像</text>
<view class="avatar-uploader flex-1">
<view class="avatar-preview" v-if="paymetInfo.userImg">
<image class="preview-img" :src="paymetInfo.userImg" mode="aspectFill"></image>
<view class="delete-icon" @click="handleDeleteAvatar">×</view>
</view>
<view class="upload-btn" v-else @click="handleChooseAvatar">
<text class="plus">+</text>
</view>
</view>
</view>
<view class="input-item flex-align-center"> <view class="input-item flex-align-center">
<text class="label"><text style="color: #FF3B30;">*</text>商品名称</text> <text class="label"><text style="color: #FF3B30;">*</text>商品名称</text>
<input class="flex-1" type="text" v-model="paymetInfo.productName" placeholder="请输入商品名称" /> <input class="flex-1" type="text" v-model="paymetInfo.productName" placeholder="请输入商品名称" />
@ -29,6 +45,11 @@
<text class="label"><text style="color: #FF3B30;">*</text>支付金额</text> <text class="label"><text style="color: #FF3B30;">*</text>支付金额</text>
<input class="flex-1" type="digit" v-model="paymetInfo.money" placeholder="请输入支付金额" /> <input class="flex-1" type="digit" v-model="paymetInfo.money" placeholder="请输入支付金额" />
</view> </view>
<view class="input-item flex-align-center">
<text class="label">展示广告</text>
<switch class="flex-1" :checked="paymetInfo.isAd" @change="paymetInfo.isAd = $event.detail.value"
color="#16B264" style="transform:scale(0.8);transform-origin:left center;" />
</view>
<view class="btn-group flex"> <view class="btn-group flex">
<view class="btn cancel flex-1" @click="editPaymentInfoPopup.close()">取消</view> <view class="btn cancel flex-1" @click="editPaymentInfoPopup.close()">取消</view>
<view style="width: 30rpx;"></view> <view style="width: 30rpx;"></view>
@ -64,6 +85,38 @@ const editPaymentInfo = () => {
editPaymentInfoPopup.value.open('center'); editPaymentInfoPopup.value.open('center');
} }
const handleChooseAvatar = () => {
uni.chooseImage({
count: 1,
success: (res) => {
const tempFilePath = res.tempFilePaths[0];
// #ifndef H5
uni.saveFile({
tempFilePath: tempFilePath,
success: (saveRes) => {
if (paymetInfo.value.userImg && !paymetInfo.value.userImg.startsWith('/static/')) {
uni.removeSavedFile({ filePath: paymetInfo.value.userImg });
}
paymetInfo.value.userImg = saveRes.savedFilePath;
}
});
// #endif
// #ifdef H5
paymetInfo.value.userImg = tempFilePath;
// #endif
}
});
};
const handleDeleteAvatar = () => {
// #ifndef H5
if (paymetInfo.value.userImg && !paymetInfo.value.userImg.startsWith('/static/')) {
uni.removeSavedFile({ filePath: paymetInfo.value.userImg });
}
// #endif
paymetInfo.value.userImg = '';
};
const confirmEditPaymentInfo = () => { const confirmEditPaymentInfo = () => {
editPaymentInfoPopup.value.close(); editPaymentInfoPopup.value.close();
storage.set('wx_payment_success_info', paymetInfo.value); storage.set('wx_payment_success_info', paymetInfo.value);
@ -80,8 +133,10 @@ const buttonGroup = [
const paymetInfo = ref({ const paymetInfo = ref({
userImg: '',
productName: '王者荣耀', productName: '王者荣耀',
money: 298 money: 298,
isAd: false,
}); });
onShow(() => { onShow(() => {
@ -95,7 +150,7 @@ onShow(() => {
// #ifdef APP-PLUS&&!APP-HARMONY // #ifdef APP-PLUS&&!APP-HARMONY
util.setAndroidSystemBarColor('#FFFFFF') util.setAndroidSystemBarColor('#FFFFFF')
setTimeout(() => { setTimeout(() => {
plus.navigator.setStatusBarStyle("light"); plus.navigator.setStatusBarStyle("dark");
}, 500) }, 500)
// #endif // #endif
}) })
@ -131,6 +186,15 @@ onLoad(async () => {
.content-box { .content-box {
margin-top: 162rpx; margin-top: 162rpx;
align-items: center;
.user-img {
width: 94rpx;
height: 94rpx;
border-radius: 50%;
background-color: #D8D8D8;
transform: translateY(-50%);
}
.product-name { .product-name {
font-size: 32rpx; font-size: 32rpx;
@ -154,6 +218,11 @@ onLoad(async () => {
margin-right: 20rpx; margin-right: 20rpx;
} }
} }
.ad-img {
margin-top: 100rpx;
width: 654rpx;
}
} }
.finish-butn { .finish-butn {
@ -207,6 +276,57 @@ onLoad(async () => {
font-size: 28rpx; font-size: 28rpx;
color: #1A1A1A; color: #1A1A1A;
} }
.avatar-uploader {
display: flex;
align-items: center;
.upload-btn {
width: 100rpx;
height: 100rpx;
border-radius: 50%;
background-color: #F8F8F8;
display: flex;
justify-content: center;
align-items: center;
border: 2rpx dashed #CCCCCC;
.plus {
font-size: 60rpx;
color: #999;
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;
}
}
}
} }
.btn-group { .btn-group {

BIN
static/font/iconfont.ttf Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 458 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB