花呗主页完成

This commit is contained in:
tangxinyue 2026-01-21 15:26:23 +08:00
parent c6e77c74a8
commit 44625d9e5d
3 changed files with 405 additions and 128 deletions

View File

@ -1,37 +1,37 @@
{
"name": "alipay-emulator",
"appid": "__UNI__D535736",
"description": "",
"versionName": "1.0.0",
"versionCode": "100",
"transformPx": false,
"name" : "alipay-emulator",
"appid" : "__UNI__D535736",
"description" : "",
"versionName" : "1.0.0",
"versionCode" : "100",
"transformPx" : false,
/* 5+App */
"app-plus": {
"darkmode": false,
"usingComponents": true,
"nvueStyleCompiler": "uni-app",
"compilerVersion": 3,
"splashscreen": {
"alwaysShowBeforeRender": true,
"waiting": true,
"autoclose": true,
"delay": 0
"app-plus" : {
"darkmode" : false,
"usingComponents" : true,
"nvueStyleCompiler" : "uni-app",
"compilerVersion" : 3,
"splashscreen" : {
"alwaysShowBeforeRender" : true,
"waiting" : true,
"autoclose" : true,
"delay" : 0
},
"optimization": {
"subPackages": true
"optimization" : {
"subPackages" : true
},
"runmode": "liberate", //
"runmode" : "liberate", //
/* */
"modules": {
"Camera": {},
"Payment": {}
"modules" : {
"Camera" : {},
"Payment" : {}
},
/* */
"distribute": {
"distribute" : {
/* android */
"android": {
"permissions": [
"android" : {
"permissions" : [
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
@ -50,46 +50,46 @@
]
},
/* ios */
"ios": {
"dSYMs": false
"ios" : {
"dSYMs" : false
},
/* SDK */
"sdkConfigs": {
"payment": {
"weixin": {
"__platform__": ["ios", "android"],
"appid": "123456",
"UniversalLinks": "123456"
"sdkConfigs" : {
"payment" : {
"weixin" : {
"__platform__" : [ "ios", "android" ],
"appid" : "123456",
"UniversalLinks" : "https://hhhhh.com/apple-app-site-association/"
},
"alipay": {
"__platform__": ["ios", "android"]
"alipay" : {
"__platform__" : [ "ios", "android" ]
}
}
}
},
"nvueLaunchMode": ""
"nvueLaunchMode" : ""
},
/* */
"quickapp": {},
"quickapp" : {},
/* */
"mp-weixin": {
"appid": "",
"setting": {
"urlCheck": false
"mp-weixin" : {
"appid" : "",
"setting" : {
"urlCheck" : false
},
"usingComponents": true
"usingComponents" : true
},
"mp-alipay": {
"usingComponents": true
"mp-alipay" : {
"usingComponents" : true
},
"mp-baidu": {
"usingComponents": true
"mp-baidu" : {
"usingComponents" : true
},
"mp-toutiao": {
"usingComponents": true
"mp-toutiao" : {
"usingComponents" : true
},
"uniStatistics": {
"enable": false
"uniStatistics" : {
"enable" : false
},
"vueVersion": "3"
"vueVersion" : "3"
}

View File

@ -1,16 +1,23 @@
<template>
<!-- 水印 -->
<view v-if="$isVip()">
<watermark :dark="data.dark" />
<liu-drag-button :canDocking="false" @clickBtn="$goRechargePage('watermark')">
<c-lottie ref="cLottieRef" :src='$watermark()' width="94px" height='74px' :loop="true"></c-lottie>
</liu-drag-button>
</view>
<view class="page-container">
<view class="main-container">
<NavBar title="花呗|信用购" :bgColor="data.navBar.bgColor" :buttonGroup="buttonGroup"
<NavBar v-if="!selectedImage" title="花呗" :bgColor="data.navBar.bgColor" :buttonGroup="buttonGroup"
@button-click="clickTitlePopupButton">
<view class="nav-bar flex-between w100" :class="{ 'ios-nav-bar': $system == 'iOS' }">
<view class="flex-align-center flex-1">
<view class="left">
<view class="left" @click.stop="goBack">
<image class=" back-icon" src="/static/image/nav-bar/back-white.png" mode="">
</image>
</view>
<view class="title ">
花呗|信用购
花呗
</view>
</view>
@ -20,7 +27,10 @@
</view>
</view>
</NavBar>
<view class="current-month">{{ huabeiInfo.mouth }}月应还()</view>
<NavBar v-else title="拼图" bgColor="#EFEFEF" isRightButton @right-click="confirmImage">
</NavBar>
<view v-if="huabeiInfo.styleType == 1" class="current-month">{{ huabeiInfo.mouth }}月应还()</view>
<view v-else class="current-month">{{ huabeiInfo.mouth }}月账单累计中()</view>
<view class="money-box flex-align-center">
<text class="money alipay-font">{{ numberUtil.formatMoneyWithThousand(huabeiInfo.money) }}</text>
<uni-icons type="right" size="16" color="#B9D6FF"></uni-icons>
@ -56,16 +66,35 @@
<view class="info-item">
<view class="label">总计额度</view>
<view class="value">{{
numberUtil.formatMoneyWithThousand(Number(huabeiInfo.totalAmount) - Number(huabeiInfo.money)) }}可用
numberUtil.formatMoneyWithThousand(Number(huabeiInfo.totalAmount) - Number(huabeiInfo.money))
}}可用
</view>
</view>
</view>
</view>
<view class="image-box flex-1">
<view v-if="!selectedImage" class="image-box flex-1 flex-align-center flex-column flex-justify-center"
@longpress="chooseImage">
<view v-if="!huabeiInfo.image" class="flex-align-center flex-column">
<image style="width:92rpx; height: 92rpx;margin-top: 16rpx;"
src="/static/image/common/upload-screenshot.png"></image>
<text style="font-size: 36rpx;color: #1777FF;">长按替换真实截图</text>
</view>
<view v-else class="w100 h100">
<image class="w100 h100" :src="huabeiInfo.image" mode="widthFix"></image>
</view>
</view>
<view v-else class="scroll-image-box flex-1">
<scroll-view class="image-box h100" style="width: 100%;" scroll-y :show-scrollbar="false"
@scroll="onImageScroll">
<image class="crop-image-target" style="width:100%;" :src="selectedImage" mode="widthFix"></image>
</scroll-view>
<view class="dashed-line-box">
<view class="dashed-line-text">我是分割线</view>
</view>
</view>
<canvas canvas-id="crop-canvas"
style="position: fixed; left: -9999px; width: 750rpx; height: 100vh; pointer-events: none;"></canvas>
<!-- 编辑弹窗 -->
<uni-popup ref="popup" type="center" :mask-click="false">
<view class="popup-content">
@ -90,7 +119,7 @@
<input class="input" type="digit" v-model="editHuabeiInfo.totalAmount" placeholder="请输入总计额度" />
</view>
<view class="form-item">
<text class="label">描述文本</text>
<text class="label">气泡文本</text>
<input class="input" type="text" v-model="editHuabeiInfo.descText" placeholder="请输入描述文本" />
</view>
<view class="form-item">
@ -128,14 +157,31 @@
</view>
</view>
</uni-popup>
<!-- 蒙层 -->
<view v-if="showMask" class="mask" @click="closeMask">
<image class="mask-icon" src="/static/image/common/mask-icon.png" mode="widthFix">
</image>
</view>
</view>
</template>
<script setup>
import NavBar from '@/components/nav-bar/nav-bar'
import { numberUtil } from '@/utils/common.js'
import { ref, toRefs } from 'vue';
import { onLoad } from '@dcloudio/uni-app';
import {
numberUtil
} from '@/utils/common.js'
import {
ref,
toRefs,
getCurrentInstance,
reactive
} from 'vue';
import {
onLoad
} from '@dcloudio/uni-app';
const instance = getCurrentInstance();
const buttonGroup = [{
name: "编辑花呗数据",
@ -147,13 +193,20 @@ const buttonGroup = [{
click: () => {
openStyleDialog()
}
}, {
name: "删除当前底部图片",
click: () => {
data.huabeiInfo.image = ""
uni.setStorageSync(data.huabeiInfoStorageKey, data.huabeiInfo)
}
}]
const data = ref({
const data = reactive({
navBar: {
bgColor: "#1777FF",
textColor: "#fff"
},
huabeiInfoStorageKey: 'huabei_info_storage',
huabeiInfo: {
mouth: 1,
money: 100,
@ -162,25 +215,47 @@ const data = ref({
descText: "当前账单进度已超出预期,花超了",
isInstallment: false,
styleType: 1,
installmentBadgeText: '4折起'
installmentBadgeText: '4折起',
image: "",
isOverdue: false
},
huabeiInfoStorageKey: 'huabei_info_storage'
selectedImage: '',
showMask: false
})
let { huabeiInfo } = toRefs(data.value)
let {
huabeiInfo,
selectedImage,
showMask
} = toRefs(data)
//
const editHuabeiInfo = ref({})
const popup = ref(null)
const stylePopup = ref(null)
const styleList = [
{ label: '样式 1 (默认)', value: 1 },
{ label: '样式 2 (纯气泡)', value: 2 },
{ label: '样式 3 (带箭头气泡)', value: 3 }
const scrollTop = ref(0)
const onImageScroll = (e) => {
scrollTop.value = e.detail.scrollTop
}
const styleList = [{
label: '样式 1 (默认)',
value: 1
},
{
label: '样式 2 (纯气泡)',
value: 2
},
{
label: '样式 3 (带箭头气泡)',
value: 3
}
]
const monthRange = Array.from({ length: 12 }, (_, i) => i + 1)
const monthRange = Array.from({
length: 12
}, (_, i) => i + 1)
const onMonthChange = (e) => {
editHuabeiInfo.value.mouth = monthRange[e.detail.value]
}
@ -188,17 +263,23 @@ const onMonthChange = (e) => {
onLoad((option) => {
console.log(option)
//
const savedInfo = uni.getStorageSync(data.value.huabeiInfoStorageKey)
let savedInfo = uni.getStorageSync(data.huabeiInfoStorageKey)
// savedInfo.image = ""
// uni.setStorageSync(data.huabeiInfoStorageKey, savedInfo)
console.log("savedInfo====", savedInfo)
if (savedInfo) {
//
data.value.huabeiInfo = { ...data.value.huabeiInfo, ...savedInfo }
data.huabeiInfo = {
...data.huabeiInfo,
...savedInfo
}
}
})
//
const openDialog = () => {
//
editHuabeiInfo.value = JSON.parse(JSON.stringify(data.value.huabeiInfo))
editHuabeiInfo.value = JSON.parse(JSON.stringify(data.huabeiInfo))
popup.value.open()
}
@ -209,9 +290,9 @@ const closeDialog = () => {
//
const confirmDialog = () => {
data.value.huabeiInfo = JSON.parse(JSON.stringify(editHuabeiInfo.value))
data.huabeiInfo = JSON.parse(JSON.stringify(editHuabeiInfo.value))
//
uni.setStorageSync(data.value.huabeiInfoStorageKey, data.value.huabeiInfo)
uni.setStorageSync(data.huabeiInfoStorageKey, data.huabeiInfo)
popup.value.close()
uni.showToast({
title: '保存成功',
@ -229,18 +310,158 @@ const closeStyleDialog = () => {
stylePopup.value.close()
}
//
const confirmImage = () => {
uni.showLoading({
title: '处理中...'
})
const query = uni.createSelectorQuery().in(instance)
//
query.select('.image-box').boundingClientRect()
query.select('.crop-image-target').boundingClientRect()
query.exec(res => {
if (!res[0] || !res[1]) {
uni.hideLoading()
return
}
console.log('rects', res)
const container = res[0] //
const image = res[1] //
// ( / /?)
// canvas
// canvas drawImage : img, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight
//
uni.getImageInfo({
src: selectedImage.value,
success: (imgInfo) => {
const scale = imgInfo.width / image.width // 图片 原始宽 / 渲染宽
const sTop = scrollTop.value * scale // Y
const sHeight = container.height * scale //
// widthFix
const sWidth = imgInfo.width
// (使)
// canvasContext使 pixelRatio
// uni-app canvas-id (px)
// canvas
const canvasW = container.width
const canvasH = container.height
const ctx = uni.createCanvasContext('crop-canvas', instance)
//
ctx.clearRect(0, 0, canvasW, canvasH)
//
// drawImage(imageResource, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight)
ctx.drawImage(
imgInfo.path,
0, sTop, sWidth, sHeight, //
0, 0, canvasW, canvasH //
)
ctx.draw(false, () => {
uni.canvasToTempFilePath({
canvasId: 'crop-canvas',
width: canvasW,
height: canvasH,
destWidth: canvasW * 2, //
destHeight: canvasH * 2,
success: (res) => {
console.log('crop success (temp)', res
.tempFilePath)
//
uni.saveFile({
tempFilePath: res.tempFilePath,
success: (saveRes) => {
console.log(
'save success (saved)',
saveRes
.savedFilePath)
data.huabeiInfo.image =
saveRes.savedFilePath
selectedImage.value =
'' //
//
uni.setStorageSync(data
.huabeiInfoStorageKey,
data.huabeiInfo)
uni.hideLoading()
},
fail: (err) => {
console.error(
'saveFile fail',
err)
uni.hideLoading()
uni.showToast({
title: '保存失败',
icon: 'none'
})
}
})
},
fail: (err) => {
console.error(err)
uni.hideLoading()
uni.showToast({
title: '裁剪失败',
icon: 'none'
})
}
}, instance)
})
},
fail: () => {
uni.hideLoading()
uni.showToast({
title: '图片加载失败',
icon: 'none'
})
}
})
})
}
//
const confirmStyleDialog = (type) => {
data.value.huabeiInfo.styleType = type
data.huabeiInfo.styleType = type
//
uni.setStorageSync(data.value.huabeiInfoStorageKey, data.value.huabeiInfo)
uni.setStorageSync(data.huabeiInfoStorageKey, data.huabeiInfo)
stylePopup.value.close()
}
//
const clickTitlePopupButton = (button) => {
button.click()
}
//
const chooseImage = () => {
if (selectedImage.value) return
uni.chooseImage({
count: 1,
sourceType: ['album'],
success: (res) => {
selectedImage.value = res.tempFilePaths[0]
data.showMask = true
}
})
}
const closeMask = () => {
data.showMask = false
}
const goBack = () => {
uni.navigateBack()
}
</script>
<style>
@ -249,6 +470,7 @@ const clickTitlePopupButton = (button) => {
<style lang="less" scoped>
.page-container {
position: relative;
display: flex;
flex-direction: column;
background-color: #ffffff;
@ -300,6 +522,11 @@ const clickTitlePopupButton = (button) => {
.main-container {
background-color: #1777FF;
padding-bottom: 32rpx;
// position: absolute;
// top: 0;
// left: 0;
// right: 0;
// z-index: 99;
.current-month {
margin-top: 12rpx;
@ -427,10 +654,60 @@ const clickTitlePopupButton = (button) => {
}
.image-box {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 100%;
overflow: hidden; // scroll-view
}
.scroll-image-box {
width: 100%;
min-height: 0; // flex
// overflow: hidden; // scroll-view
position: relative;
}
.dashed-line-box {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
border: 4rpx dashed #ffffff;
pointer-events: none;
.dashed-line-text {
height: 44rpx;
line-height: 44rpx;
width: 180rpx;
padding: 0 20rpx;
border-radius: 8rpx;
color: #1777FF;
font-size: 24rpx;
font-weight: 500;
background-color: #fff;
position: absolute;
top: 0;
left: 50%;
transform: translate(-50%, -50%);
}
}
.mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.8);
z-index: 999;
.mask-icon {
position: absolute;
top: 50%;
right: 52rpx;
transform: translateY(-25%);
width: 360rpx;
height: 360rpx;
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB