支付包第一期完成
63
App.vue
|
|
@ -10,9 +10,6 @@ export default {
|
|||
// 1. 同步初始化配置(必须完成)
|
||||
this.initConfig(options)
|
||||
|
||||
// 2. 异步获取用户数据(不阻塞启动)
|
||||
this.fetchUserDataAsync()
|
||||
|
||||
// 启动完成
|
||||
console.log(`App 启动耗时: ${Date.now() - startTime}ms`)
|
||||
},
|
||||
|
|
@ -119,66 +116,6 @@ export default {
|
|||
// #endif
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* 异步获取用户数据(不阻塞启动)
|
||||
*/
|
||||
fetchUserDataAsync() {
|
||||
// 延迟到下一个事件循环,确保不阻塞启动
|
||||
setTimeout(async () => {
|
||||
const fetchStart = Date.now()
|
||||
try {
|
||||
// 并行获取用户信息和配置
|
||||
const [userResult, configResult] = await Promise.allSettled([
|
||||
this.fetchUserInfo(),
|
||||
this.fetchUserConfig()
|
||||
])
|
||||
|
||||
// 处理用户信息结果
|
||||
if (userResult.status === 'rejected') {
|
||||
console.error('获取用户信息失败:', userResult.reason)
|
||||
}
|
||||
|
||||
// 处理用户配置结果
|
||||
if (configResult.status === 'rejected') {
|
||||
console.error('获取用户配置失败:', configResult.reason)
|
||||
}
|
||||
|
||||
console.log(`用户数据获取耗时: ${Date.now() - fetchStart}ms`)
|
||||
} catch (error) {
|
||||
console.error('获取用户数据异常:', error)
|
||||
// 不显示错误提示,避免打断用户
|
||||
}
|
||||
}, 0)
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取用户信息
|
||||
*/
|
||||
async fetchUserInfo() {
|
||||
const data = await get('', 'api/user', {})
|
||||
if (data.code === 0) {
|
||||
uni.setStorageSync('userInfo', data.data)
|
||||
console.log('用户信息获取成功')
|
||||
return data.data
|
||||
} else {
|
||||
throw new Error(data.message || '获取用户信息失败')
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取用户配置
|
||||
*/
|
||||
async fetchUserConfig() {
|
||||
const data = await get('', 'api/user/config', {})
|
||||
if (data.code === 0) {
|
||||
uni.setStorageSync('config', data.data)
|
||||
console.log('用户配置获取成功')
|
||||
return data.data
|
||||
} else {
|
||||
throw new Error(data.message || '获取用户配置失败')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,293 @@
|
|||
<template>
|
||||
<view>
|
||||
<!-- 底部弹出层 -->
|
||||
<uni-popup ref="timepopup" type="bottom">
|
||||
<!-- 账单分类 -->
|
||||
<view v-if="data.popupType == 'billClassify'" class="bill-classify-box">
|
||||
<view class="title-box">
|
||||
<view class="title">
|
||||
账单分类
|
||||
</view>
|
||||
<view class="btn" @click="closeTimePicker">
|
||||
<image class="close-image" src="/static/image/common/close.png"></image>
|
||||
</view>
|
||||
</view>
|
||||
<view class="bill-classify-content">
|
||||
<view class="bill-classify-item" v-for="item in billClassifyOptions" :key="item.id"
|
||||
@click="data.currentClassifyName = item.name">
|
||||
<view class="bill-classify-item-text"
|
||||
:class="{ 'active-item': data.currentClassifyName == item.name }">
|
||||
{{ item.name }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="confirm-btn" @click="confirmClassify">
|
||||
确定
|
||||
</view>
|
||||
</view>
|
||||
<!-- 标签和备注 -->
|
||||
<view v-if="data.popupType == 'tagAndNote'" class="bill-classify-box">
|
||||
<view class="title-box">
|
||||
<view class="title">
|
||||
标签
|
||||
<text class="text">(最多10个标签)</text>
|
||||
</view>
|
||||
<view class="btn" @click="closeTimePicker">
|
||||
<image class="close-image" src="/static/image/common/close.png"></image>
|
||||
</view>
|
||||
</view>
|
||||
<view class="bill-classify-content tag-content flex-wrap">
|
||||
<view class="tag-item" v-for="(tag, index) in data.tempTags" :key="index">
|
||||
<text>{{ tag }}</text>
|
||||
<view class="delete-tag" @click="deleteTag(index)">
|
||||
<image src="/static/image/common/close.png" mode="widthFix" class="close-icon-img">
|
||||
</image>
|
||||
</view>
|
||||
</view>
|
||||
<view class="add-tag-box" v-if="!data.showTagInput && data.tempTags.length < 10"
|
||||
@click="showTagInputFunc">
|
||||
<text class="add-symbol">+</text>
|
||||
</view>
|
||||
<view class="input-box" v-if="data.showTagInput">
|
||||
<input class="tag-input-field" v-model="data.tagInputValue" :focus="data.showTagInput"
|
||||
@confirm="confirmAddTag" @blur="confirmAddTag" placeholder="请输入标签" maxlength="5" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="title-box">
|
||||
<view class="title">
|
||||
备注
|
||||
</view>
|
||||
</view>
|
||||
<view class="bill-classify-content note-content">
|
||||
<textarea class="note-textarea" auto-height v-model="data.tempNote.text" placeholder="随便说点什么..."
|
||||
maxlength="50" disable-default-padding></textarea>
|
||||
</view>
|
||||
<view class="title-box">
|
||||
<view class="title">
|
||||
备注图片
|
||||
</view>
|
||||
<switch :checked="data.tempNote.isImage" color="#1676FE" style="transform: scale(0.7);"
|
||||
@change="(e) => data.tempNote.isImage = e.detail.value"></switch>
|
||||
</view>
|
||||
<!-- <view class="bill-classify-content" v-if="data.tempNote.isImage"> -->
|
||||
<!-- 此处展示图片上传或其他逻辑,暂时留空或添加提示 -->
|
||||
<!-- </view> -->
|
||||
<view class="confirm-btn" @click="confirmClassify">
|
||||
确定
|
||||
</view>
|
||||
</view>
|
||||
</uni-popup>
|
||||
</view>
|
||||
</template>
|
||||
<script setup>
|
||||
import {
|
||||
reactive,
|
||||
toRefs,
|
||||
ref,
|
||||
defineExpose,
|
||||
defineProps,
|
||||
defineEmits
|
||||
} from 'vue'
|
||||
import addBillJson from '@/static/json/add-bill.json'
|
||||
|
||||
const props = defineProps({
|
||||
billClassifyOptions: {
|
||||
type: Array,
|
||||
default: () => addBillJson.billClassify
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits(['confirm', 'update:show'])
|
||||
|
||||
const timepopup = ref(null)
|
||||
|
||||
const data = reactive({
|
||||
popupType: '', // 'billClassify' or 'tagAndNote'
|
||||
currentClassifyName: '',
|
||||
tempTags: [],
|
||||
tagInputValue: '',
|
||||
showTagInput: false,
|
||||
tempNote: {
|
||||
text: '',
|
||||
isImage: false
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* 打开弹窗
|
||||
* @param {String} type 弹窗类型 'billClassify' | 'tagAndNote'
|
||||
* @param {Object} initData 初始化数据
|
||||
*/
|
||||
const open = (type, initData = {}) => {
|
||||
data.popupType = type
|
||||
if (type === 'billClassify') {
|
||||
data.currentClassifyName = initData.classifyName || ''
|
||||
} else if (type === 'tagAndNote') {
|
||||
// Deep copy to prevent direct mutation of prop data before confirm
|
||||
data.tempTags = initData.tags ? JSON.parse(JSON.stringify(initData.tags)) : []
|
||||
data.tempNote = initData.note ? JSON.parse(JSON.stringify(initData.note)) : { text: '', isImage: false }
|
||||
data.showTagInput = false
|
||||
data.tagInputValue = ''
|
||||
}
|
||||
timepopup.value.open()
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭弹窗
|
||||
*/
|
||||
const closeTimePicker = () => {
|
||||
timepopup.value.close()
|
||||
}
|
||||
|
||||
/**
|
||||
* 确认操作
|
||||
*/
|
||||
const confirmClassify = () => {
|
||||
if (data.popupType === 'billClassify') {
|
||||
const selected = props.billClassifyOptions.find(item => item.name === data.currentClassifyName)
|
||||
emit('confirm', { type: 'billClassify', value: data.currentClassifyName, id: selected ? selected.id : 0 })
|
||||
} else if (data.popupType === 'tagAndNote') {
|
||||
emit('confirm', {
|
||||
type: 'tagAndNote',
|
||||
tags: data.tempTags,
|
||||
note: data.tempNote
|
||||
})
|
||||
}
|
||||
closeTimePicker()
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除标签
|
||||
*/
|
||||
const deleteTag = (index) => {
|
||||
data.tempTags.splice(index, 1)
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示标签输入框
|
||||
*/
|
||||
const showTagInputFunc = () => {
|
||||
data.showTagInput = true
|
||||
}
|
||||
|
||||
/**
|
||||
* 确认添加标签
|
||||
*/
|
||||
const confirmAddTag = () => {
|
||||
if (data.tagInputValue.trim()) {
|
||||
data.tempTags.push(data.tagInputValue.trim())
|
||||
}
|
||||
data.tagInputValue = ''
|
||||
data.showTagInput = false
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
open,
|
||||
close: closeTimePicker
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
@import "@/common/specify-style.less";
|
||||
|
||||
.bill-classify-box {
|
||||
|
||||
.tag-content {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
padding: 20rpx 16rpx 0 16rpx;
|
||||
margin-bottom: 24rpx;
|
||||
|
||||
.tag-item {
|
||||
position: relative;
|
||||
background-color: #E6F2FF;
|
||||
color: #2788D1;
|
||||
font-size: 26rpx;
|
||||
height: 60rpx;
|
||||
line-height: 60rpx;
|
||||
padding: 0 24rpx;
|
||||
border-radius: 8rpx;
|
||||
margin-right: 24rpx;
|
||||
margin-bottom: 24rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.delete-tag {
|
||||
position: absolute;
|
||||
top: -12rpx;
|
||||
right: -12rpx;
|
||||
width: 32rpx;
|
||||
height: 32rpx;
|
||||
background-color: #FE4141;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border: 2rpx solid #ffffff;
|
||||
z-index: 10;
|
||||
|
||||
.close-icon-img {
|
||||
width: 14rpx;
|
||||
height: 14rpx;
|
||||
filter: brightness(0) invert(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.add-tag-box {
|
||||
height: 52rpx;
|
||||
line-height: 52rpx;
|
||||
background-color: #F4F9FF;
|
||||
border-radius: 8rpx;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin-bottom: 24rpx;
|
||||
padding: 0 16rpx;
|
||||
|
||||
.add-symbol {
|
||||
color: #2788D1;
|
||||
font-size: 40rpx;
|
||||
line-height: 52rpx;
|
||||
font-weight: 300;
|
||||
}
|
||||
}
|
||||
|
||||
.input-box {
|
||||
width: 160rpx;
|
||||
height: 60rpx;
|
||||
background-color: #F4F9FF;
|
||||
border-radius: 8rpx;
|
||||
margin-bottom: 24rpx;
|
||||
padding: 0 16rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.tag-input-field {
|
||||
width: 100%;
|
||||
font-size: 26rpx;
|
||||
color: #2788D1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.note-content {
|
||||
|
||||
border-bottom: 1rpx solid #EDEDED;
|
||||
margin-bottom: 24rpx;
|
||||
|
||||
.note-textarea {
|
||||
width: 100%;
|
||||
height: 160rpx;
|
||||
// background-color: #F9F9F9;
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.flex-wrap {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
</style>
|
||||
17
pages.json
|
|
@ -50,6 +50,20 @@
|
|||
"navigationBarTitleText": "账单详情页",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/index/alipay-annual-bill/alipay-annual-bill",
|
||||
"style": {
|
||||
"navigationBarTitleText": "支付宝年度账单",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/common/webview/webview",
|
||||
"style": {
|
||||
"navigationBarTitleText": "webView页面",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
}
|
||||
],
|
||||
"globalStyle": {
|
||||
|
|
@ -61,6 +75,9 @@
|
|||
"navigationBarBackgroundColor": "#00000000", // 完全透明
|
||||
"navigationBarTextStyle": "white", // 虚拟按键图标颜色
|
||||
"backgroundColor": "#00000000" // 背景透明
|
||||
},
|
||||
"app-plus": {
|
||||
"bounce": "none" // 全局关闭回弹效果
|
||||
}
|
||||
},
|
||||
"uniIdRouter": {}
|
||||
|
|
|
|||
|
|
@ -238,6 +238,8 @@
|
|||
</view>
|
||||
|
||||
<!-- 底部弹出层 -->
|
||||
<billManagementPopup ref="billManagementPopupRef" @confirm="handleBillManagementConfirm"></billManagementPopup>
|
||||
|
||||
<uni-popup ref="timepopup" type="bottom">
|
||||
<!-- 时间选择器 -->
|
||||
<view v-if="selectItemInfo.type == 'time'" class="timeBox">
|
||||
|
|
@ -252,82 +254,6 @@
|
|||
<DateTimePicker :defaultDate="datePickerData.selectDate" :minDate="datePickerData.startDate"
|
||||
:maxDate="datePickerData.endDate" :mode="4" @onChange="onChangeStartDate" />
|
||||
</view>
|
||||
<template v-else>
|
||||
<!-- 账单分类 -->
|
||||
<view v-if="data.popupType == 'billClassify'" class="bill-classify-box">
|
||||
<view class="title-box">
|
||||
<view class="title">
|
||||
账单分类
|
||||
</view>
|
||||
<view class="btn" @click="closeTimePicker">
|
||||
<image class="close-image" src="/static/image/common/close.png"></image>
|
||||
</view>
|
||||
</view>
|
||||
<view class="bill-classify-content">
|
||||
<view class="bill-classify-item" v-for="item in billClassifyOptions" :key="item.id"
|
||||
@click="data.currentClassifyId = item.id">
|
||||
<view class="bill-classify-item-text"
|
||||
:class="{ 'active-item': data.currentClassifyId == item.id }">
|
||||
{{ item.name }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="confirm-btn" @click="confirmClassify">
|
||||
确定
|
||||
</view>
|
||||
</view>
|
||||
<!-- 标签和备注 -->
|
||||
<view v-if="data.popupType == 'tagAndNote'" class="bill-classify-box">
|
||||
<view class="title-box">
|
||||
<view class="title">
|
||||
标签
|
||||
<text class="text">(最多10个标签)</text>
|
||||
</view>
|
||||
<view class="btn" @click="closeTimePicker">
|
||||
<image class="close-image" src="/static/image/common/close.png"></image>
|
||||
</view>
|
||||
</view>
|
||||
<view class="bill-classify-content tag-content flex-wrap">
|
||||
<view class="tag-item" v-for="(tag, index) in data.tempTags" :key="index">
|
||||
<text>{{ tag }}</text>
|
||||
<view class="delete-tag" @click="deleteTag(index)">
|
||||
<image src="/static/image/common/close.png" mode="widthFix" class="close-icon-img"></image>
|
||||
</view>
|
||||
</view>
|
||||
<view class="add-tag-box" v-if="!data.showTagInput && data.tempTags.length < 10"
|
||||
@click="showTagInputFunc">
|
||||
<text class="add-symbol">+</text>
|
||||
</view>
|
||||
<view class="input-box" v-if="data.showTagInput">
|
||||
<input class="tag-input-field" v-model="data.tagInputValue" :focus="data.showTagInput"
|
||||
@confirm="confirmAddTag" @blur="confirmAddTag" placeholder="请输入标签" maxlength="5" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="title-box">
|
||||
<view class="title">
|
||||
备注
|
||||
</view>
|
||||
</view>
|
||||
<view class="bill-classify-content note-content">
|
||||
<textarea class="note-textarea" auto-height v-model="data.tempNote.text" placeholder="随便说点什么..."
|
||||
maxlength="50" disable-default-padding></textarea>
|
||||
</view>
|
||||
<view class="title-box">
|
||||
<view class="title">
|
||||
备注图片
|
||||
</view>
|
||||
<switch :checked="data.tempNote.isImage" color="#1676FE" style="transform: scale(0.7);"
|
||||
@change="(e) => data.tempNote.isImage = e.detail.value"></switch>
|
||||
</view>
|
||||
<!-- <view class="bill-classify-content" v-if="data.tempNote.isImage"> -->
|
||||
<!-- 此处展示图片上传或其他逻辑,暂时留空或添加提示 -->
|
||||
<!-- </view> -->
|
||||
<view class="confirm-btn" @click="confirmClassify">
|
||||
确定
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
</uni-popup>
|
||||
</template>
|
||||
|
||||
|
|
@ -335,9 +261,9 @@
|
|||
import navBar from '@/components/nav-bar/nav-bar.vue'
|
||||
import addBillJson from '@/static/json/add-bill.json'
|
||||
import DateTimePicker from '@/components/dengrq-datetime-picker/dateTimePicker/index.vue';
|
||||
import billManagementPopup from '@/components/bill-management-popup/bill-management-popup.vue'
|
||||
import { stringUtil, randomUtil, util, uiUtil } from '@/utils/common.js';
|
||||
import hotIcon from "@/static/json/hot-icon.json"
|
||||
import { storage } from '@/utils/storage.js'
|
||||
import { useStore } from '@/store/index.js'
|
||||
|
||||
import {
|
||||
|
|
@ -357,6 +283,8 @@ import {
|
|||
|
||||
// 时间选择器
|
||||
const timepopup = ref(null)
|
||||
// 账单管理弹窗
|
||||
const billManagementPopupRef = ref(null)
|
||||
|
||||
const {
|
||||
addBill,
|
||||
|
|
@ -884,9 +812,11 @@ const onClickItemInfo = async (item, action) => {
|
|||
selectItemInfo.value = item
|
||||
timepopup.value.open()
|
||||
} else if (item.key == 'billClassify') {
|
||||
// selectItemInfo.value = item
|
||||
data.popupType = "billClassify"
|
||||
timepopup.value.open()
|
||||
// Use bill-management-popup component
|
||||
const initData = {
|
||||
classifyName: billData.value.merchantOption.billClassify
|
||||
}
|
||||
billManagementPopupRef.value.open('billClassify', initData)
|
||||
} else if (item.type == 'text' || item.type == 'number' || item.type == "digit") {
|
||||
item.focus = false
|
||||
nextTick(() => {
|
||||
|
|
@ -902,10 +832,12 @@ const onClickItemInfo = async (item, action) => {
|
|||
})
|
||||
}
|
||||
} else if (item.key == 'tag' || item.key == 'note') {
|
||||
data.tempTags = JSON.parse(JSON.stringify(billData.value.merchantOption.tag))
|
||||
data.tempNote = JSON.parse(JSON.stringify(billData.value.merchantOption.note))
|
||||
data.popupType = "tagAndNote"
|
||||
timepopup.value.open()
|
||||
// Use bill-management-popup component
|
||||
const initData = {
|
||||
tags: billData.value.merchantOption.tag,
|
||||
note: billData.value.merchantOption.note
|
||||
}
|
||||
billManagementPopupRef.value.open('tagAndNote', initData)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1030,46 +962,6 @@ const settmes = () => {
|
|||
data.popupType = ''
|
||||
}
|
||||
|
||||
// 删除标签
|
||||
const deleteTag = (index) => {
|
||||
data.tempTags.splice(index, 1)
|
||||
}
|
||||
|
||||
// 显示标签输入框
|
||||
const showTagInputFunc = () => {
|
||||
data.showTagInput = true
|
||||
data.tagInputValue = ""
|
||||
}
|
||||
|
||||
// 确认添加标签
|
||||
const confirmAddTag = () => {
|
||||
if (data.tagInputValue && data.tagInputValue.trim()) {
|
||||
data.tempTags.push(data.tagInputValue.trim())
|
||||
}
|
||||
data.showTagInput = false
|
||||
data.tagInputValue = ""
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 设置分类选择
|
||||
*/
|
||||
const confirmClassify = () => {
|
||||
if (data.popupType == 'billClassify') {
|
||||
billClassifyOptions.value.forEach(option => {
|
||||
if (option.id == data.currentClassifyId) {
|
||||
billData.value.merchantOption.billClassify = option.name
|
||||
}
|
||||
})
|
||||
} else if (data.popupType == 'tagAndNote') {
|
||||
billData.value.merchantOption.tag = JSON.parse(JSON.stringify(data.tempTags))
|
||||
billData.value.merchantOption.note = JSON.parse(JSON.stringify(data.tempNote))
|
||||
}
|
||||
|
||||
timepopup.value.close()
|
||||
data.popupType = ''
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Object} date
|
||||
* 切换时间
|
||||
|
|
@ -1077,6 +969,20 @@ const confirmClassify = () => {
|
|||
function onChangeStartDate(date) {
|
||||
datePickerData.value.selectDate = date;
|
||||
}
|
||||
|
||||
/**
|
||||
* 确认账单管理弹窗
|
||||
* @param data
|
||||
*/
|
||||
const handleBillManagementConfirm = (data) => {
|
||||
console.log("确认账单管理弹窗", data)
|
||||
if (data.type == 'billClassify') {
|
||||
billData.value.merchantOption.billClassify = data.value
|
||||
} else if (data.type == 'tagAndNote') {
|
||||
billData.value.merchantOption.tag = data.tags
|
||||
billData.value.merchantOption.note = data.note
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
|
|
@ -1472,98 +1378,4 @@ page {
|
|||
|
||||
|
||||
}
|
||||
|
||||
.tag-content {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
padding: 20rpx 16rpx 0 16rpx;
|
||||
margin-bottom: 24rpx;
|
||||
|
||||
.tag-item {
|
||||
position: relative;
|
||||
background-color: #E6F2FF;
|
||||
color: #2788D1;
|
||||
font-size: 26rpx;
|
||||
height: 60rpx;
|
||||
line-height: 60rpx;
|
||||
padding: 0 24rpx;
|
||||
border-radius: 8rpx;
|
||||
margin-right: 24rpx;
|
||||
margin-bottom: 24rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.delete-tag {
|
||||
position: absolute;
|
||||
top: -12rpx;
|
||||
right: -12rpx;
|
||||
width: 32rpx;
|
||||
height: 32rpx;
|
||||
background-color: #FE4141;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border: 2rpx solid #ffffff;
|
||||
z-index: 10;
|
||||
|
||||
.close-icon-img {
|
||||
width: 14rpx;
|
||||
height: 14rpx;
|
||||
filter: brightness(0) invert(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.add-tag-box {
|
||||
height: 52rpx;
|
||||
line-height: 52rpx;
|
||||
background-color: #F4F9FF;
|
||||
border-radius: 8rpx;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin-bottom: 24rpx;
|
||||
padding: 0 16rpx;
|
||||
|
||||
.add-symbol {
|
||||
color: #2788D1;
|
||||
font-size: 40rpx;
|
||||
line-height: 52rpx;
|
||||
font-weight: 300;
|
||||
}
|
||||
}
|
||||
|
||||
.input-box {
|
||||
width: 160rpx;
|
||||
height: 60rpx;
|
||||
background-color: #F4F9FF;
|
||||
border-radius: 8rpx;
|
||||
margin-bottom: 24rpx;
|
||||
padding: 0 16rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.tag-input-field {
|
||||
width: 100%;
|
||||
font-size: 26rpx;
|
||||
color: #2788D1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.note-content {
|
||||
|
||||
border-bottom: 1rpx solid #EDEDED;
|
||||
margin-bottom: 24rpx;
|
||||
|
||||
.note-textarea {
|
||||
width: 100%;
|
||||
height: 160rpx;
|
||||
// background-color: #F9F9F9;
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -154,7 +154,7 @@
|
|||
</view>
|
||||
<view class="bill-classification bill-management-item">
|
||||
<text class="label">账单分类</text>
|
||||
<view class="right-box flex-align-center">
|
||||
<view class="right-box flex-align-center" @click="handleBillManagementClick('billClassify')">
|
||||
<text>{{ billData.merchantOption.billClassify }}</text>
|
||||
<uni-icons style="margin-left: 8rpx;" type="right" size="12" color="#969696"></uni-icons>
|
||||
</view>
|
||||
|
|
@ -163,7 +163,8 @@
|
|||
<text v-if="billData.merchantOption.note.text || billData.merchantOption.note.isImage"
|
||||
class="label">标签</text>
|
||||
<text v-else class="label">标签和备注</text>
|
||||
<view class="tag-box right-box flex-1 flex-align-center">
|
||||
<view class="tag-box right-box flex-1 flex-align-center"
|
||||
@click="handleBillManagementClick('tagAndNote')">
|
||||
<text v-if="billData.merchantOption.tag.length == 0 || !billData.merchantOption.tag">添加</text>
|
||||
<view v-else v-for="(tag, index) in billData.merchantOption.tag.slice(0, 3)" :key="tag"
|
||||
class="tag-item">
|
||||
|
|
@ -178,7 +179,8 @@
|
|||
<view class="bill-management-item"
|
||||
v-if="billData.merchantOption.note.text || billData.merchantOption.note.isImage">
|
||||
<text class="label">备注</text>
|
||||
<view class="tag-box right-box flex-1 flex-align-center">
|
||||
<view class="tag-box right-box flex-1 flex-align-center"
|
||||
@click="handleBillManagementClick('tagAndNote')">
|
||||
<view class="remark-box flex-align-center text-align-right flex-1">
|
||||
<image v-if="billData.merchantOption.note.isImage" class="remark-img"
|
||||
src="/static/image/bill/bill-detail/bill-remark-img.png">
|
||||
|
|
@ -195,7 +197,8 @@
|
|||
<view class="bill-management-item" style="margin-top: 32rpx;">
|
||||
<text class="label">计入收支</text>
|
||||
<view class="right-box flex-align-center" style="transform:scale(0.6); width: 100rpx;">
|
||||
<switch color="#1676FE" :checked="billData.merchantOption.countInAndOut" />
|
||||
<switch color="#1676FE" :checked="billData.merchantOption.countInAndOut"
|
||||
@change="handleCountInAndOutChange" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="bottom-icon-box">
|
||||
|
|
@ -212,10 +215,14 @@
|
|||
|
||||
</view>
|
||||
</view>
|
||||
|
||||
|
||||
<billManagementPopup ref="billManagementPopupRef" @confirm="handleBillManagementConfirm"></billManagementPopup>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import navBar from '@/components/nav-bar/nav-bar.vue'
|
||||
import billManagementPopup from '@/components/bill-management-popup/bill-management-popup.vue'
|
||||
import {
|
||||
billBottomIconList
|
||||
} from '@/static/json/add-bill.json'
|
||||
|
|
@ -233,16 +240,18 @@ import {
|
|||
onLoad,
|
||||
onShow,
|
||||
} from '@dcloudio/uni-app'
|
||||
|
||||
|
||||
import {
|
||||
useStore
|
||||
} from '@/store/index.js'
|
||||
|
||||
|
||||
const {
|
||||
getBillList,
|
||||
updateBill
|
||||
} = useStore()
|
||||
|
||||
const billManagementPopupRef = ref(null)
|
||||
|
||||
const buttonGroup = [{
|
||||
name: "编辑账单",
|
||||
click: () => {
|
||||
|
|
@ -353,6 +362,47 @@ const getServiceDetailRightText = (value) => {
|
|||
const item = serviceDetailRightTextList.find(item => item.value === Number(value))
|
||||
return item?.text || ""
|
||||
}
|
||||
|
||||
// 计入收支
|
||||
function handleCountInAndOutChange(e) {
|
||||
billData.value.merchantOption.countInAndOut = e.detail.value
|
||||
console.log(e, billData.value)
|
||||
|
||||
// Edit mode
|
||||
updateBill(billData.value.id, billData.value)
|
||||
}
|
||||
|
||||
// 账单管理
|
||||
const handleBillManagementClick = (type) => {
|
||||
let initData = {}
|
||||
if (type == 'billClassify') {
|
||||
initData = {
|
||||
classifyName: billData.value.merchantOption.billClassify
|
||||
}
|
||||
} else if (type == 'tagAndNote') {
|
||||
initData = {
|
||||
tags: billData.value.merchantOption.tag,
|
||||
note: billData.value.merchantOption.note
|
||||
}
|
||||
}
|
||||
billManagementPopupRef.value.open(type, initData)
|
||||
}
|
||||
|
||||
/**
|
||||
* 确认账单管理弹窗
|
||||
* @param data
|
||||
*/
|
||||
const handleBillManagementConfirm = (data) => {
|
||||
console.log(data)
|
||||
if (data.type == 'billClassify') {
|
||||
billData.value.merchantOption.billClassify = data.value
|
||||
billData.value.selectId = data.id
|
||||
} else if (data.type == 'tagAndNote') {
|
||||
billData.value.merchantOption.tag = data.tags
|
||||
billData.value.merchantOption.note = data.note
|
||||
}
|
||||
updateBill(billData.value.id, billData.value)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@
|
|||
Number(currentMonthData.outCome).toFixed(2) }}</text></view>
|
||||
<view class="item"><text>收入¥</text><text class="money wx-font-regular">{{
|
||||
Number(currentMonthData.inCome).toFixed(2) }}</text></view>
|
||||
|
||||
</view>
|
||||
<view class="">
|
||||
<text>收支分析</text>
|
||||
|
|
@ -56,28 +57,34 @@
|
|||
:ref="el => cardRefs[index] = el">
|
||||
<view class="list-title-card"
|
||||
:class="{ 'current-month': item.month == new Date().getMonth() + 1 && item.year == new Date().getFullYear() }">
|
||||
<view v-if="item.month == new Date().getMonth() + 1 && item.year == new Date().getFullYear()"
|
||||
class="bg-right-text">
|
||||
<text>贴纸</text>
|
||||
<image class="right-icon" src="/static/image/common/right-blue.png"></image>
|
||||
</view>
|
||||
<view class="list-title">
|
||||
<view>
|
||||
<text class="month alipay-font">{{ item.month }}</text>
|
||||
<text>月</text>
|
||||
</view>
|
||||
<image class="filter-icon down " src="/static/image/bill/bill-list/down-black.png" mode="">
|
||||
</image>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
<view class="flex-between analysis-box">
|
||||
<view class="income-ande-outCome">
|
||||
<view class="income item">
|
||||
<text class="title">收入</text>
|
||||
<text class="amount alipay-font"><text class="font-11 wx-font-regular">¥</text>{{
|
||||
Number(item.inCome).toFixed(2)
|
||||
}}</text>
|
||||
</view>
|
||||
<view class="outCome item">
|
||||
<text class="title">支出</text>
|
||||
<text class="amount alipay-font"><text class="font-11 wx-font-regular">¥</text>{{
|
||||
Number(item.outCome).toFixed(2)
|
||||
}}</text>
|
||||
</view>
|
||||
<view class="income item">
|
||||
<text class="title">收入</text>
|
||||
<text class="amount alipay-font"><text class="font-11 wx-font-regular">¥</text>{{
|
||||
Number(item.inCome).toFixed(2)
|
||||
}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="!(item.month == new Date().getMonth() + 1 && item.year == new Date().getFullYear())"
|
||||
class="analysis-button">
|
||||
|
|
@ -229,6 +236,13 @@ onShow(() => {
|
|||
|
||||
// #ifdef APP-PLUS
|
||||
util.setAndroidSystemBarColor('#F5F5F5')
|
||||
uni.setNavigationBarColor({
|
||||
animation: { // 动画效果
|
||||
duration: 100,
|
||||
timingFunc: 'easeIn'
|
||||
}
|
||||
})
|
||||
plus.navigator.setStatusBarStyle("dark");
|
||||
// #endif
|
||||
})
|
||||
|
||||
|
|
@ -629,7 +643,6 @@ page {
|
|||
.income-ande-outCome {
|
||||
display: flex;
|
||||
font-size: 13px;
|
||||
margin-top: 6px;
|
||||
|
||||
.item {
|
||||
margin-right: 16px;
|
||||
|
|
@ -648,7 +661,7 @@ page {
|
|||
border-radius: 8px;
|
||||
margin: 0 12px;
|
||||
margin-bottom: 12px;
|
||||
background-color: #FFFFFF;
|
||||
// background-color: #FFFFFF;
|
||||
|
||||
.list-title-card {
|
||||
background-color: #FFFFFF;
|
||||
|
|
@ -666,7 +679,6 @@ page {
|
|||
}
|
||||
|
||||
.income-ande-outCome {
|
||||
margin-top: 8px;
|
||||
display: flex;
|
||||
|
||||
.item {
|
||||
|
|
@ -712,13 +724,20 @@ page {
|
|||
}
|
||||
}
|
||||
|
||||
.list-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.current-month {
|
||||
position: relative;
|
||||
padding: 10px 12px 18px;
|
||||
background: url('/static/image/bill/bill-list/current-month-bill-bg.png') no-repeat center center;
|
||||
background-size: 100% 100%;
|
||||
background-color: transparent;
|
||||
|
||||
.income-ande-outCome {
|
||||
margin-top: 8px;
|
||||
margin-top: 8rpx;
|
||||
display: flex;
|
||||
|
||||
.item {
|
||||
|
|
@ -747,7 +766,6 @@ page {
|
|||
}
|
||||
|
||||
.income-ande-outCome-analysis {
|
||||
margin-top: 3px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
|
|
@ -780,5 +798,26 @@ page {
|
|||
}
|
||||
}
|
||||
|
||||
.bg-right-text {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: #2B3841;
|
||||
font-size: 22rpx;
|
||||
position: absolute;
|
||||
right: 18rpx;
|
||||
top: 32rpx;
|
||||
|
||||
.right-icon {
|
||||
width: 16rpx;
|
||||
height: 16rpx;
|
||||
margin-left: 2rpx;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.bill-list {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,132 @@
|
|||
<!-- pages/webview/webview.vue -->
|
||||
<template>
|
||||
<view class="webview-container">
|
||||
<navBar class="nav-bar" :title="pageTitle" :bgColor="data.navBar.bgColor" isBack></navBar>
|
||||
<web-view :src="url" :webview-styles="webviewStyles"></web-view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
// 自定义头部
|
||||
// import ZdyNavbar from "@/components/navbar/navbar.vue"
|
||||
import navBar from '@/components/nav-bar/nav-bar.vue'
|
||||
import { util } from '@/utils/common.js'
|
||||
|
||||
import {
|
||||
ref,
|
||||
reactive,
|
||||
watch,
|
||||
nextTick,
|
||||
getCurrentInstance,
|
||||
onMounted,
|
||||
onBeforeUnmount,
|
||||
toRefs,
|
||||
computed
|
||||
} from "vue";
|
||||
import {
|
||||
onLoad,
|
||||
onReady,
|
||||
onShow
|
||||
} from '@dcloudio/uni-app'
|
||||
|
||||
const data = reactive({
|
||||
navBar: {
|
||||
title: "",
|
||||
bgColor: '#ffffff',
|
||||
},
|
||||
dark: "dark",
|
||||
// 状态栏高度
|
||||
systemBarHeight: "0",
|
||||
})
|
||||
|
||||
let {
|
||||
systemBarHeight
|
||||
} = toRefs(data)
|
||||
|
||||
const url = ref('')
|
||||
const pageTitle = ref('')
|
||||
|
||||
onLoad((options) => {
|
||||
uni.getSystemInfo({
|
||||
success: res => {
|
||||
systemBarHeight.value = res.statusBarHeight; // 获取状态栏高度
|
||||
}
|
||||
})
|
||||
if (options.url) {
|
||||
url.value = decodeURIComponent(options.url)
|
||||
// const videoExps = [/\.mp4$/i, /\.m3u8$/i, /\.flv$/i, /\.avi$/i, /\.mov$/i, /\.wmv$/i, /\.webm$/i,
|
||||
// /\.mkv$/i
|
||||
// ];
|
||||
// const isVideo = videoExps.some(exp => exp.test(url.value))
|
||||
// console.log(isVideo)
|
||||
// if (isVideo) {
|
||||
// plus.navigator.setStatusBarStyle("light");
|
||||
// } else {
|
||||
// plus.navigator.setStatusBarStyle("dark");
|
||||
// }
|
||||
}
|
||||
console.log("options参数", options);
|
||||
if (options.title) {
|
||||
console.log("标题", options.title);
|
||||
pageTitle.value = decodeURIComponent(options.title)
|
||||
}
|
||||
})
|
||||
|
||||
onShow(() => {
|
||||
// #ifdef APP-PLUS
|
||||
util.setAndroidSystemBarColor('#ffffff')
|
||||
plus.navigator.setStatusBarStyle("dark");
|
||||
// #endif
|
||||
})
|
||||
|
||||
const goBack = () => {
|
||||
uni.navigateBack()
|
||||
}
|
||||
|
||||
const isVideo = computed(() => {
|
||||
if (!url.value) return false
|
||||
const videoExps = [/\.mp4$/i, /\.m3u8$/i, /\.flv$/i, /\.avi$/i, /\.mov$/i, /\.wmv$/i, /\.webm$/i,
|
||||
/\.mkv$/i
|
||||
];
|
||||
return videoExps.some(exp => exp.test(url.value));
|
||||
})
|
||||
|
||||
const webviewStyles = computed(() => {
|
||||
return {
|
||||
top: `${Number(systemBarHeight.value) + 40}px`,
|
||||
bottom: '0px',
|
||||
'padding-bottom': '20px'
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.webview-container {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.header {
|
||||
padding: 20rpx;
|
||||
background: #fff;
|
||||
border-bottom: 1rpx solid #eee;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.video-web {
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
background-color: #000;
|
||||
}
|
||||
</style>
|
||||
<style>
|
||||
page {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,934 @@
|
|||
<template>
|
||||
<view class="container">
|
||||
<view class="bg-container"></view>
|
||||
<view class="status-nav-bar" :style="{ height: (statusBarHeight + 44) + 'px' }">
|
||||
<view class="status-bar" :style="{ height: statusBarHeight + 'px' }"></view>
|
||||
<view class="nav-bar">
|
||||
<view class="nav-bar-left" @click="goback()">
|
||||
<image class="nav-bar-left-image" src="@/static/image/alipay-bill/back.png" mode=""></image>
|
||||
</view>
|
||||
<view class="nav-bar-right">
|
||||
<image class="nav-bar-right-image" src="@/static/image/alipay-bill/music-icon.png" mode=""></image>
|
||||
<image class="nav-bar-right-image" src="@/static/image/alipay-bill/share-icon.png" mode=""></image>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<image ref="bgImage" class="bg-image" src="@/static/image/alipay-bill/main-bg.png" mode="widthFix"></image>
|
||||
<image class="eye-image" src="@/static/image/alipay-bill/hide.png" mode="widthFix"></image>
|
||||
<image class="find-more" src="@/static/image/alipay-bill/find-more.png" mode=""></image>
|
||||
|
||||
<view class="total-money">
|
||||
<text>年度总支出</text>
|
||||
<text class="money alipay-font-bold" @click="openPopup">{{ formatMoney(totalMoney) }}</text>
|
||||
<text class="unit">元</text>
|
||||
</view>
|
||||
<view class="frist-money money-type-item">
|
||||
<view class="type-input-box">
|
||||
<!-- <{{ moneyFrist.type }}>支出最多! -->
|
||||
<<view class="percentage-input-box">
|
||||
<text class="ghost-text">{{ moneyFrist.type }}</text>
|
||||
<input class="percentage-input" type="text" v-model="moneyFrist.type"
|
||||
@input="inputPercentage($event, 'moneyFrist')" maxlength="4"></input>
|
||||
</view>>支出最多!
|
||||
</view>
|
||||
<view class="money">
|
||||
占
|
||||
<view class="percentage-input-box">
|
||||
<text class="ghost-text">{{ moneyFrist.percentage }}</text>
|
||||
<input class="percentage-input" type="digit" v-model="moneyFrist.percentage"
|
||||
@input="inputPercentage($event, 'moneyFrist')"></input>
|
||||
</view>
|
||||
<view>%</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="second-money money-type-item">
|
||||
<view class="type-input-box">
|
||||
<!-- {{ moneySecond.type }} -->
|
||||
<view class="percentage-input-box">
|
||||
<text class="ghost-text">{{ moneySecond.type }}</text>
|
||||
<input class="percentage-input" type="text" v-model="moneySecond.type"
|
||||
@input="inputPercentage($event, 'moneySecond')"></input>
|
||||
</view>
|
||||
</view>
|
||||
<view class="money">
|
||||
占
|
||||
<view class="percentage-input-box">
|
||||
<text class="ghost-text">{{ moneySecond.percentage }}</text>
|
||||
<input class="percentage-input" type="digit" v-model="moneySecond.percentage"
|
||||
@input="inputPercentage($event, 'moneySecond')"></input>
|
||||
</view>
|
||||
<view>%</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="third-money money-type-item">
|
||||
<view class="type-input-box">
|
||||
<!-- {{ moneyThird.type }} -->
|
||||
<view class="percentage-input-box">
|
||||
<text class="ghost-text">{{ moneyThird.type }}</text>
|
||||
<input class="percentage-input" type="text" v-model="moneyThird.type"
|
||||
@input="inputPercentage($event, 'moneyThird')"></input>
|
||||
</view>
|
||||
</view>
|
||||
<view class="money">
|
||||
占
|
||||
<view class="percentage-input-box">
|
||||
<text class="ghost-text">{{ moneyThird.percentage }}</text>
|
||||
<input class="percentage-input" type="digit" v-model="moneyThird.percentage"
|
||||
@input="inputPercentage($event, 'moneyThird')"></input>
|
||||
</view>
|
||||
<view>%</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="fourth-money money-type-item">
|
||||
<view class="type-input-box">
|
||||
<!-- {{ moneyFourth.type }} -->
|
||||
<view class="percentage-input-box">
|
||||
<text class="ghost-text">{{ moneyFourth.type }}</text>
|
||||
<input class="percentage-input" type="text" v-model="moneyFourth.type"
|
||||
@input="inputPercentage($event, 'moneyFourth')"></input>
|
||||
</view>
|
||||
</view>
|
||||
<view class="money">
|
||||
占
|
||||
<view class="percentage-input-box">
|
||||
<text class="ghost-text">{{ moneyFourth.percentage }}</text>
|
||||
<input class="percentage-input" type="digit" v-model="moneyFourth.percentage"
|
||||
@input="inputPercentage($event, 'moneyFourth')"></input>
|
||||
</view>
|
||||
<view>%</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="fifth-money money-type-item">
|
||||
<view class="type-input-box">
|
||||
<!-- {{ moneyFifth.type }} -->
|
||||
<view class="percentage-input-box">
|
||||
<text class="ghost-text">{{ moneyFifth.type }}</text>
|
||||
<input class="percentage-input" type="text" v-model="moneyFifth.type"
|
||||
@input="inputPercentage($event, 'moneyFifth')"></input>
|
||||
</view>
|
||||
</view>
|
||||
<view class="money">
|
||||
占
|
||||
<view class="percentage-input-box">
|
||||
<text class="ghost-text">{{ moneyFifth.percentage }}</text>
|
||||
<input class="percentage-input" type="digit" v-model="moneyFifth.percentage"
|
||||
@input="inputPercentage($event, 'moneyFifth')"></input>
|
||||
</view>
|
||||
<view>%</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="time-box" @click="openTimePopup">
|
||||
数据统计截至{{ formatDate(time) }}
|
||||
</view>
|
||||
<image class="footer-image" src="@/static/image/alipay-bill/footer.png" mode="heightFix"></image>
|
||||
|
||||
<!-- 修改金额弹窗 -->
|
||||
<uni-popup ref="dialogPopup" type="dialog">
|
||||
<uni-popup-dialog class="popup-dialog" mode="input" title="修改年度总支出" :before-close="true"
|
||||
@close="dialogPopupClose()" @confirm="confirm">
|
||||
<uni-easyinput v-model="totalMoney" step="0.01" type="digit" :focus="true"
|
||||
placeholder="请输入金额"></uni-easyinput>
|
||||
</uni-popup-dialog>
|
||||
</uni-popup>
|
||||
|
||||
<!-- 修改时间弹窗 -->
|
||||
<uni-popup ref="timeDialogPopup" type="dialog">
|
||||
<uni-popup-dialog class="popup-dialog" mode="input" title="修改时间" :before-close="true"
|
||||
@close="timeDialogPopupClose()" @confirm="confirmTime">
|
||||
<picker mode="date" fields="day" :value="time" @change="bindTimeChange" start="2025-12-29"
|
||||
:end="formatDateISO(new Date())">
|
||||
<view class="picker-view" style="padding: 10px; border-radius: 4px; text-align: center;">
|
||||
{{ formatDate(time) || '请选择时间' }}
|
||||
</view>
|
||||
</picker>
|
||||
</uni-popup-dialog>
|
||||
</uni-popup>
|
||||
</view>
|
||||
|
||||
<!-- 水印 -->
|
||||
<!-- <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> -->
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {
|
||||
ref,
|
||||
reactive,
|
||||
toRefs
|
||||
} from 'vue';
|
||||
import {
|
||||
onLoad,
|
||||
onShow,
|
||||
onUnload
|
||||
} from '@dcloudio/uni-app';
|
||||
const statusBarHeight = ref(0);
|
||||
const dialogPopup = ref(null);
|
||||
const timeDialogPopup = ref(null);
|
||||
|
||||
const data = reactive({
|
||||
totalMoney: 10000000,
|
||||
time: "",
|
||||
moneyFrist: {
|
||||
type: '转账红包',
|
||||
percentage: "83"
|
||||
},
|
||||
moneySecond: {
|
||||
type: '家居家装',
|
||||
percentage: "83"
|
||||
},
|
||||
moneyThird: {
|
||||
type: '餐饮美食',
|
||||
percentage: "83"
|
||||
},
|
||||
moneyFourth: {
|
||||
type: '日用百货',
|
||||
percentage: "83"
|
||||
},
|
||||
moneyFifth: {
|
||||
type: '服饰装扮',
|
||||
percentage: "83"
|
||||
},
|
||||
navBarHeight: 0
|
||||
})
|
||||
|
||||
let {
|
||||
totalMoney,
|
||||
moneyFrist,
|
||||
moneySecond,
|
||||
moneyThird,
|
||||
moneyFourth,
|
||||
moneyFifth,
|
||||
time,
|
||||
navBarHeight
|
||||
} = toRefs(data)
|
||||
|
||||
|
||||
onLoad(() => {
|
||||
time.value = getYesterday();
|
||||
statusBarHeight.value = uni.getSystemInfoSync().statusBarHeight;
|
||||
|
||||
const config = uni.getStorageSync('config')
|
||||
console.log("---config---", config);
|
||||
const font = config.config['client.uniapp.font']
|
||||
|
||||
console.log("字体地址信息", font.alipay2025);
|
||||
// Font loading logic
|
||||
const fontUrl = font.alipay2025;
|
||||
const fontName = 'AlibabaSemiBold';
|
||||
|
||||
const loadFont = (path) => {
|
||||
uni.loadFontFace({
|
||||
family: fontName,
|
||||
source: `url("${path}")`,
|
||||
success() {
|
||||
console.log('字体加载成功');
|
||||
},
|
||||
fail(err) {
|
||||
console.error('字体加载失败', err);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// #ifdef H5
|
||||
// H5 环境直接从 URL 加载字体
|
||||
loadFont(fontUrl);
|
||||
// #endif
|
||||
|
||||
// #ifndef H5
|
||||
// 非 H5 环境使用下载和保存逻辑
|
||||
const savedFontPath = uni.getStorageSync('alipay_font_path');
|
||||
if (savedFontPath) {
|
||||
loadFont(savedFontPath);
|
||||
} else {
|
||||
uni.downloadFile({
|
||||
url: fontUrl,
|
||||
success: (res) => {
|
||||
if (res.statusCode === 200) {
|
||||
uni.saveFile({
|
||||
tempFilePath: res.tempFilePath,
|
||||
success: (saveRes) => {
|
||||
const savedPath = saveRes.savedFilePath;
|
||||
uni.setStorageSync('alipay_font_path', savedPath);
|
||||
console.log("字体保存路径", savedPath);
|
||||
loadFont(savedPath);
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('保存文件失败', err);
|
||||
// Fallback: 尝试加载临时路径
|
||||
loadFont(res.tempFilePath);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('下载字体失败', err);
|
||||
}
|
||||
});
|
||||
}
|
||||
// #endif
|
||||
|
||||
try {
|
||||
data.totalMoney = uni.getStorageSync('totalMoney') || 1000000;
|
||||
data.moneyFrist = uni.getStorageSync('moneyFrist') || {
|
||||
type: '转账红包',
|
||||
percentage: "66.3"
|
||||
};
|
||||
data.moneySecond = uni.getStorageSync('moneySecond') || {
|
||||
type: '家居家装',
|
||||
percentage: "16.8"
|
||||
};
|
||||
data.moneyThird = uni.getStorageSync('moneyThird') || {
|
||||
type: '服饰装备',
|
||||
percentage: "8.3"
|
||||
};
|
||||
data.moneyFourth = uni.getStorageSync('moneyFourth') || {
|
||||
type: '日用百货',
|
||||
percentage: "5.2"
|
||||
};
|
||||
data.moneyFifth = uni.getStorageSync('moneyFifth') || {
|
||||
type: '餐饮美食',
|
||||
percentage: "3.2"
|
||||
};
|
||||
data.time = uni.getStorageSync('alipayBillEndTime') || getYesterday();
|
||||
console.log('数据同步缓存成功');
|
||||
} catch (err) {
|
||||
console.log('数据同步缓存失败', err);
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* 打开修改金额弹窗
|
||||
*/
|
||||
const openPopup = () => {
|
||||
dialogPopup.value.open();
|
||||
}
|
||||
|
||||
const goback = () => {
|
||||
uni.navigateBack();
|
||||
}
|
||||
|
||||
const formatMoney = (val) => {
|
||||
let num = Number(val);
|
||||
if (isNaN(num)) {
|
||||
return '0.00';
|
||||
}
|
||||
let str = num.toFixed(2);
|
||||
let parts = str.split('.');
|
||||
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ',');
|
||||
return parts.join('.');
|
||||
}
|
||||
|
||||
const dialogPopupClose = () => {
|
||||
dialogPopup.value.close();
|
||||
}
|
||||
|
||||
const confirm = () => {
|
||||
uni.setStorageSync('totalMoney', totalMoney.value);
|
||||
dialogPopup.value.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化时间
|
||||
* @param {Object} date
|
||||
*/
|
||||
const formatDate = (date) => {
|
||||
const d = new Date(date);
|
||||
const y = d.getFullYear();
|
||||
const m = d.getMonth() + 1;
|
||||
const day = d.getDate();
|
||||
return `${y}年${m}月${day}日`;
|
||||
}
|
||||
|
||||
const inputPercentage = (e, type) => {
|
||||
console.log('Saving', type, data[type]);
|
||||
uni.setStorageSync(type, data[type]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开修改时间弹窗
|
||||
*/
|
||||
const openTimePopup = () => {
|
||||
timeDialogPopup.value.open();
|
||||
}
|
||||
|
||||
const timeDialogPopupClose = () => {
|
||||
timeDialogPopup.value.close();
|
||||
}
|
||||
|
||||
const bindTimeChange = (e) => {
|
||||
time.value = e.detail.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取昨天的时间
|
||||
*/
|
||||
const getYesterday = () => {
|
||||
const date = new Date();
|
||||
date.setDate(date.getDate() - 1);
|
||||
return formatDateISO(date);
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化时间 2025-12-01
|
||||
* @param {Object} date
|
||||
*/
|
||||
const formatDateISO = (date) => {
|
||||
const d = new Date(date);
|
||||
const y = d.getFullYear();
|
||||
const m = (d.getMonth() + 1).toString().padStart(2, '0');
|
||||
const day = d.getDate().toString().padStart(2, '0');
|
||||
return `${y}-${m}-${day}`;
|
||||
}
|
||||
|
||||
const confirmTime = () => {
|
||||
uni.setStorageSync('alipayBillEndTime', time.value);
|
||||
timeDialogPopup.value.close();
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.container {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background-color: #D3E9FE;
|
||||
// background: linear-gradient(180deg, #6BB8FE 0%, #D3E9FE 100%);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
|
||||
.nav-bar {
|
||||
box-sizing: border-box;
|
||||
width: 100vw;
|
||||
height: 44px;
|
||||
background-color: transparent;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 10px;
|
||||
|
||||
.nav-bar-left-image {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.nav-bar-right-image {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
margin-left: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.bg-container {
|
||||
width: 100vw;
|
||||
height: 50%;
|
||||
background: linear-gradient(180deg, #6BB8FE 0%, #D3E9FE 100%);
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.bg-image {
|
||||
width: 100vw;
|
||||
height: 85% !important;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.total-money {
|
||||
position: absolute;
|
||||
top: calc(15% + 12px);
|
||||
left: 10%;
|
||||
color: #fff;
|
||||
font-size: 30rpx;
|
||||
font-weight: 500;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.money {
|
||||
font-weight: 700;
|
||||
font-size: 44rpx;
|
||||
margin-left: 6rpx;
|
||||
|
||||
|
||||
}
|
||||
|
||||
.percentage-input-box {
|
||||
margin-left: 6rpx;
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.ghost-text {
|
||||
font-size: 44rpx;
|
||||
min-width: 20px;
|
||||
font-weight: 700;
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
white-space: pre;
|
||||
height: 44rpx;
|
||||
line-height: 44rpx;
|
||||
}
|
||||
|
||||
.percentage-input {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
font-size: 44rpx;
|
||||
text-align: center;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.unit {
|
||||
font-size: 44rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.frist-money {
|
||||
position: absolute;
|
||||
top: calc(48%);
|
||||
left: 16%;
|
||||
font-size: 36rpx;
|
||||
font-weight: 500;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
transform: rotate(6deg);
|
||||
font-weight: 700;
|
||||
line-height: 58rpx;
|
||||
|
||||
.type-input-box {
|
||||
.ghost-text {
|
||||
font-size: 36rpx;
|
||||
min-width: 20px;
|
||||
font-weight: 700;
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
white-space: pre;
|
||||
height: 36rpx;
|
||||
line-height: 36rpx;
|
||||
}
|
||||
|
||||
.percentage-input {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
font-size: 36rpx;
|
||||
text-align: center;
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
.money {
|
||||
font-weight: 700;
|
||||
font-size: 44rpx;
|
||||
margin-left: 6rpx;
|
||||
display: flex;
|
||||
|
||||
.percentage-input-box {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.ghost-text {
|
||||
font-size: 44rpx;
|
||||
min-width: 20px;
|
||||
font-weight: 700;
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
white-space: pre;
|
||||
height: 44rpx;
|
||||
line-height: 44rpx;
|
||||
}
|
||||
|
||||
.percentage-input {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
font-size: 44rpx;
|
||||
text-align: center;
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.second-money {
|
||||
position: absolute;
|
||||
top: calc(33% + 8rpx);
|
||||
right: 12%;
|
||||
font-size: 32rpx;
|
||||
font-weight: 500;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
transform: rotate(6deg);
|
||||
font-weight: 700;
|
||||
line-height: 44rpx;
|
||||
|
||||
.type-input-box {
|
||||
height: 44rpx;
|
||||
|
||||
.ghost-text {
|
||||
font-size: 32rpx;
|
||||
min-width: 20px;
|
||||
font-weight: 700;
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
white-space: pre;
|
||||
height: 32rpx;
|
||||
line-height: 32rpx;
|
||||
}
|
||||
|
||||
.percentage-input {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
font-size: 32rpx;
|
||||
text-align: center;
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
|
||||
.money {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 32rpx;
|
||||
margin-left: 6rpx;
|
||||
|
||||
.percentage-input-box {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.ghost-text {
|
||||
font-size: 32rpx;
|
||||
min-width: 16px;
|
||||
font-weight: 700;
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
white-space: pre;
|
||||
height: 32rpx;
|
||||
line-height: 32rpx;
|
||||
}
|
||||
|
||||
.percentage-input {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
font-size: 32rpx;
|
||||
text-align: center;
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.third-money {
|
||||
position: absolute;
|
||||
top: calc(25% + 8rpx);
|
||||
left: 23%;
|
||||
font-size: 32rpx;
|
||||
font-weight: 500;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
transform: rotate(4deg);
|
||||
font-weight: 700;
|
||||
line-height: 42rpx;
|
||||
|
||||
.type-input-box {
|
||||
height: 42rpx;
|
||||
line-height: 42rpx;
|
||||
|
||||
.ghost-text {
|
||||
font-size: 32rpx;
|
||||
min-width: 20px;
|
||||
font-weight: 700;
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
white-space: pre;
|
||||
height: 32rpx;
|
||||
line-height: 32rpx;
|
||||
}
|
||||
|
||||
.percentage-input {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
font-size: 32rpx;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
.money {
|
||||
font-size: 32rpx;
|
||||
margin-left: 6rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.percentage-input-box {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.ghost-text {
|
||||
font-size: 32rpx;
|
||||
min-width: 16px;
|
||||
font-weight: 700;
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
white-space: pre;
|
||||
height: 32rpx;
|
||||
line-height: 32rpx;
|
||||
}
|
||||
|
||||
.percentage-input {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
font-size: 32rpx;
|
||||
text-align: center;
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.fourth-money {
|
||||
position: absolute;
|
||||
top: calc(44% - 4rpx);
|
||||
right: calc(14% + 4px);
|
||||
font-size: 24rpx;
|
||||
font-weight: 500;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
transform: rotate(4deg);
|
||||
line-height: 28rpx;
|
||||
font-weight: 700;
|
||||
|
||||
.type-input-box {
|
||||
height: 28rpx;
|
||||
line-height: 28rpx;
|
||||
|
||||
.ghost-text {
|
||||
font-size: 24rpx;
|
||||
min-width: 20px;
|
||||
font-weight: 700;
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
white-space: pre;
|
||||
height: 24rpx;
|
||||
line-height: 24rpx;
|
||||
}
|
||||
|
||||
.percentage-input {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
font-size: 24rpx;
|
||||
text-align: center;
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
|
||||
.money {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 24rpx;
|
||||
margin-left: 6rpx;
|
||||
|
||||
.percentage-input-box {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.ghost-text {
|
||||
font-size: 24rpx;
|
||||
min-width: 16px;
|
||||
font-weight: 700;
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
white-space: pre;
|
||||
height: 24rpx;
|
||||
line-height: 24rpx;
|
||||
}
|
||||
|
||||
.percentage-input {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
font-size: 24rpx;
|
||||
text-align: center;
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.fifth-money {
|
||||
position: absolute;
|
||||
top: calc(35% - 4rpx);
|
||||
left: 7%;
|
||||
font-size: 30rpx;
|
||||
font-weight: 500;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
transform: rotate(4deg);
|
||||
font-weight: 700;
|
||||
line-height: 42rpx;
|
||||
|
||||
.type-input-box {
|
||||
height: 42rpx;
|
||||
line-height: 42rpx;
|
||||
|
||||
.ghost-text {
|
||||
font-size: 30rpx;
|
||||
min-width: 20px;
|
||||
font-weight: 700;
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
white-space: pre;
|
||||
height: 30rpx;
|
||||
line-height: 30rpx;
|
||||
}
|
||||
|
||||
.percentage-input {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
font-size: 30rpx;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
.money {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 30rpx;
|
||||
margin-left: 6rpx;
|
||||
|
||||
.percentage-input-box {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.ghost-text {
|
||||
font-size: 30rpx;
|
||||
min-width: 16px;
|
||||
font-weight: 700;
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
white-space: pre;
|
||||
height: 30rpx;
|
||||
line-height: 30rpx;
|
||||
}
|
||||
|
||||
.percentage-input {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
font-size: 30rpx;
|
||||
text-align: center;
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.money-type-item {
|
||||
color: #000000;
|
||||
font-weight: 700;
|
||||
|
||||
.type-input-box {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.percentage-input-box {
|
||||
margin-left: 6rpx;
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.find-more {
|
||||
width: 118rpx;
|
||||
height: 50rpx;
|
||||
position: absolute;
|
||||
top: 28%;
|
||||
right: -4%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
.eye-image {
|
||||
width: 98rpx;
|
||||
height: 40rpx;
|
||||
position: absolute;
|
||||
top: 17%;
|
||||
right: 8%;
|
||||
}
|
||||
|
||||
.time-box {
|
||||
padding: 10rpx 56rpx;
|
||||
background-color: #000000;
|
||||
opacity: 0.35;
|
||||
color: #FAC6CA;
|
||||
font-size: 22rpx;
|
||||
line-height: 22rpx;
|
||||
font-weight: 500;
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
border-radius: 286px;
|
||||
position: absolute;
|
||||
white-space: nowrap;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
bottom: 30%;
|
||||
}
|
||||
|
||||
.footer-image {
|
||||
height: 190rpx;
|
||||
position: fixed;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
bottom: 32rpx;
|
||||
}
|
||||
|
||||
.alipay-font-bold {
|
||||
font-family: 'AlibabaSemiBold';
|
||||
}
|
||||
</style>
|
||||
|
|
@ -6,7 +6,7 @@
|
|||
<view class="nav-bar-box">
|
||||
<view class="status-box" :style="{ height: statusBarHeight + 'px' }"></view>
|
||||
<view class="nav-box">
|
||||
<view class="left-box">
|
||||
<view class="left-box" @click="exit">
|
||||
<image style="width: 40rpx; height: 40rpx;" src="/static/image/nav-bar/back-black.png"></image>
|
||||
</view>
|
||||
<view class="title">小宝模拟器</view>
|
||||
|
|
@ -14,7 +14,9 @@
|
|||
</view>
|
||||
|
||||
</view>
|
||||
<view class="content-box">
|
||||
<view class="content-box" :style="{ height: windowHeight + 'px' }">
|
||||
<view>
|
||||
<view style="background-color: transparent;" :style="{ height: (44 + statusBarHeight) + 'px' }"></view>
|
||||
<view class="user-box">
|
||||
<image class="user-bg" :style="{ width: (windowWidth - 32) + 'px' }"
|
||||
:src="`/static/image/index/${userInfo.vip > 1 ? (userInfo.vip == 3 ? 'lifetime-vip-bg' : 'vip-bg') : 'no-vip-bg'}.png`"
|
||||
|
|
@ -34,27 +36,42 @@
|
|||
</view>
|
||||
<view v-if="userInfo.vip && userInfo.vip != 3" class="btn-box">
|
||||
<image class="open-vip-btn"
|
||||
:src="`/static/image/index/${userInfo.vip > 1 ? 'vip-btn' : 'open-vip-btn'}.png`"></image>
|
||||
:src="`/static/image/index/${userInfo.vip > 1 ? 'vip-btn' : 'open-vip-btn'}.png`">
|
||||
</image>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="notice-box">
|
||||
<view class="notice-box" @click="clickNotice">
|
||||
<view class="sound-box">
|
||||
<uni-icons type="sound" size="18" color="#D8D8D8"></uni-icons>
|
||||
<text class="notice-content">欢迎大家使用装样大师,使用中有问题客服为您解答</text>
|
||||
</view>
|
||||
|
||||
<view ref="noticeContainer" class="notice-content-wrapper">
|
||||
<view ref="noticeInner" class="notice-inner">
|
||||
<text ref="noticeBox" class="notice-content" style="margin-right: 30rpx;">{{ noticeInfo.text
|
||||
}}</text>
|
||||
<text class="notice-content" style="margin-right: 30rpx;">{{ noticeInfo.text }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="group-box">
|
||||
<image class="title-img" src="/static/image/index/shipingjiaocheng.png"></image>
|
||||
<view class="video-help-box">
|
||||
|
||||
<view class="video-help-item" v-for="item in videoHelpList" :key="item.id"
|
||||
@click="clickVideoHelp(item)">
|
||||
<image class="video-help-img" :src="item.icon"></image>
|
||||
<text class="video-help-title">{{ item.text }}教程</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="group-box">
|
||||
<image class="title-img" src="/static/image/index/monixiaobao.png"></image>
|
||||
<view class="menu-box">
|
||||
<view class="item-box" v-for="item in menuList" :key="item.name" @click="util.goPage(item.path)">
|
||||
<view class="item-box" v-for="item in menuList" :key="item.name"
|
||||
@click="util.goPage(item.path)">
|
||||
<view class="menu-item" :style="{ width: (windowWidth - 50) / 2 + 'px' }">
|
||||
<!-- <text class="menu-item-name">{{ item.name }}</text> -->
|
||||
<image class="name-img" :src="'/static/image/index/menu-name/' + item.icon + '.png'"
|
||||
|
|
@ -70,11 +87,16 @@
|
|||
|
||||
<view class="activity-box">
|
||||
<image class="alipay-year-bill" :style="{ width: (windowWidth - 32) + 'px' }"
|
||||
src="/static/image/index/alipay-year-bill.png" mode="widthFix"></image>
|
||||
src="/static/image/index/alipay-year-bill.png" mode="widthFix"
|
||||
@click="util.goPage(`/pages/index/alipay-annual-bill/alipay-annual-bill`)"></image>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
|
||||
<view class="footer-box">
|
||||
<text class="vision-text">版本:{{ vision }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
|
||||
</view>
|
||||
</template>
|
||||
|
|
@ -85,6 +107,9 @@ import {
|
|||
import {
|
||||
storage
|
||||
} from '@/utils/storage.js'
|
||||
import {
|
||||
get
|
||||
} from '@/utils/requests.js'
|
||||
import {
|
||||
ref,
|
||||
reactive,
|
||||
|
|
@ -128,20 +153,30 @@ const data = reactive({
|
|||
statusBarHeight: 0,
|
||||
windowWidth: 0,
|
||||
windowHeight: 0,
|
||||
userInfo: {}
|
||||
userInfo: {},
|
||||
videoHelpList: [],
|
||||
noticeInfo: {},
|
||||
vision: "1.0.0"
|
||||
})
|
||||
|
||||
const {
|
||||
statusBarHeight,
|
||||
windowWidth,
|
||||
userInfo
|
||||
windowHeight,
|
||||
userInfo,
|
||||
videoHelpList,
|
||||
noticeInfo,
|
||||
vision
|
||||
} = toRefs(data);
|
||||
|
||||
onLoad(() => {
|
||||
setUserData()
|
||||
// 启动时获取数据
|
||||
fetchUserData()
|
||||
})
|
||||
|
||||
onShow(() => {
|
||||
// 每次显示时刷新数据
|
||||
setUserData()
|
||||
// 获取系统信息
|
||||
const systemInfo = uni.getSystemInfoSync();
|
||||
data.statusBarHeight = systemInfo.statusBarHeight;
|
||||
|
|
@ -151,12 +186,221 @@ onShow(() => {
|
|||
util.setAndroidSystemBarColor('#F0F4F9')
|
||||
// #endif
|
||||
})
|
||||
|
||||
/**
|
||||
* 设置用户数据
|
||||
* 获取用户数据(从服务器)
|
||||
*/
|
||||
const fetchUserData = async () => {
|
||||
try {
|
||||
// 先设置默认值,避免页面显示异常
|
||||
setUserData()
|
||||
|
||||
// 并行获取用户信息和配置
|
||||
const [userResult, configResult] = await Promise.allSettled([
|
||||
fetchUserInfo(),
|
||||
fetchUserConfig()
|
||||
])
|
||||
|
||||
// 处理用户信息结果
|
||||
if (userResult.status === 'fulfilled') {
|
||||
console.log('用户信息获取成功')
|
||||
} else {
|
||||
console.error('获取用户信息失败:', userResult.reason)
|
||||
}
|
||||
|
||||
// 处理用户配置结果
|
||||
if (configResult.status === 'fulfilled') {
|
||||
console.log('用户配置获取成功')
|
||||
} else {
|
||||
console.error('获取用户配置失败:', configResult.reason)
|
||||
}
|
||||
|
||||
// 刷新页面数据
|
||||
setUserData()
|
||||
} catch (error) {
|
||||
console.error('获取用户数据异常:', error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户信息
|
||||
*/
|
||||
const fetchUserInfo = async () => {
|
||||
const data = await get('', 'api/user', {})
|
||||
if (data.code === 0) {
|
||||
uni.setStorageSync('userInfo', data.data)
|
||||
return data.data
|
||||
} else {
|
||||
throw new Error(data.message || '获取用户信息失败')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户配置
|
||||
*/
|
||||
const fetchUserConfig = async () => {
|
||||
const data = await get('', 'api/user/config', {})
|
||||
if (data.code === 0) {
|
||||
uni.setStorageSync('config', data.data)
|
||||
return data.data
|
||||
} else {
|
||||
throw new Error(data.message || '获取用户配置失败')
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 设置用户数据(从本地存储读取)
|
||||
*/
|
||||
const setUserData = () => {
|
||||
data.userInfo = storage.get("userInfo")
|
||||
console.log(data.userInfo)
|
||||
// 用户信息 - 提供默认值
|
||||
const userInfoData = storage.get("userInfo")
|
||||
data.userInfo = userInfoData || {
|
||||
user_id: '加载中...',
|
||||
avater: '/static/default-avatar.png',
|
||||
vip: 0,
|
||||
vip_expire: ''
|
||||
}
|
||||
|
||||
// 配置信息 - 安全访问
|
||||
const configData = storage.get("config")
|
||||
if (configData && configData.config) {
|
||||
data.noticeInfo = configData.config['client.uniapp.notice'] || { text: '加载中...', url: '' }
|
||||
data.noticeInfo = configData.config['client.uniapp.notice'] || { text: '加载中...', url: '' }
|
||||
// 启动走马灯
|
||||
startMarquee();
|
||||
data.videoHelpList = configData.config['client.uniapp.alipay.video_help'] || []
|
||||
} else {
|
||||
data.noticeInfo = { text: '加载中...', url: '' }
|
||||
data.videoHelpList = []
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 点击视频教程
|
||||
* @param item
|
||||
*/
|
||||
const clickVideoHelp = (item) => {
|
||||
const url = item.url
|
||||
util.goPage(`/pages/common/webview/webview?url=${encodeURIComponent(url)}&title=${item.text}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 点击公告
|
||||
*/
|
||||
const clickNotice = () => {
|
||||
if (!noticeInfo.value.url) return
|
||||
const url = noticeInfo.value.url + `&uni_id=${userInfo.value.user_id}`
|
||||
util.goPage(`/pages/common/webview/webview?url=${encodeURIComponent(url)}&title=${noticeInfo.value.title}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 退出模拟器
|
||||
*/
|
||||
const exit = () => {
|
||||
plus.runtime.quit()
|
||||
}
|
||||
|
||||
const noticeContainer = ref(null);
|
||||
const noticeInner = ref(null);
|
||||
const noticeBox = ref(null);
|
||||
|
||||
// #ifndef H5
|
||||
const animation = uni.requireNativePlugin('animation');
|
||||
const dom = uni.requireNativePlugin('dom');
|
||||
// #endif
|
||||
|
||||
let marqueeTimer = null;
|
||||
const currentMarqueeId = ref(0);
|
||||
const lastMarqueeText = ref('');
|
||||
|
||||
/**
|
||||
* 开始走马灯
|
||||
*/
|
||||
const startMarquee = () => {
|
||||
// 避免不必要的重置:如果文本没有变化且正在运行,则忽略
|
||||
if (lastMarqueeText.value === noticeInfo.value.text && currentMarqueeId.value > 0) {
|
||||
return;
|
||||
}
|
||||
lastMarqueeText.value = noticeInfo.value.text;
|
||||
|
||||
// 清除待执行的启动定时器
|
||||
if (marqueeTimer) {
|
||||
clearTimeout(marqueeTimer);
|
||||
marqueeTimer = null;
|
||||
}
|
||||
|
||||
// 增加 ID 以使之前的动画循环失效
|
||||
currentMarqueeId.value++;
|
||||
const myId = currentMarqueeId.value;
|
||||
|
||||
// 清除旧动画状态 (重置位置)
|
||||
if (noticeInner.value) {
|
||||
animation.transition(noticeInner.value, {
|
||||
styles: { transform: 'translateX(0)' },
|
||||
duration: 0,
|
||||
delay: 0
|
||||
});
|
||||
}
|
||||
|
||||
marqueeTimer = setTimeout(() => {
|
||||
if (!noticeContainer.value || !noticeInner.value) return;
|
||||
// 如果ID不匹配,说明有新的启动请求,放弃当前
|
||||
if (myId !== currentMarqueeId.value) return;
|
||||
|
||||
// 获取容器宽度
|
||||
dom.getComponentRect(noticeContainer.value, (resContainer) => {
|
||||
const containerWidth = resContainer.size.width;
|
||||
if (!containerWidth) return;
|
||||
|
||||
// 获取文本/内部容器宽度
|
||||
dom.getComponentRect(noticeInner.value, (resText) => {
|
||||
const textWidth = resText.size.width / 2; // 因为复制了一份
|
||||
if (!textWidth) return;
|
||||
|
||||
// 执行滚动
|
||||
runMarqueeAnimation(containerWidth, textWidth, myId);
|
||||
});
|
||||
});
|
||||
}, 1000); // 增加延时确保渲染
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行滚动动画循环
|
||||
*/
|
||||
const runMarqueeAnimation = (containerWidth, textWidth, myId) => {
|
||||
// ID 校验:如果当前ID不匹配,说明已被新动画取代,停止递归
|
||||
if (myId !== currentMarqueeId.value) return;
|
||||
if (!noticeInner.value) return;
|
||||
|
||||
// 动画的目标:移动 Inner 容器
|
||||
const target = noticeInner.value;
|
||||
const realScrollDistance = textWidth;
|
||||
|
||||
// 1. 重置位置到 0 (无感重置)
|
||||
animation.transition(target, {
|
||||
styles: { transform: `translateX(0)` },
|
||||
duration: 0,
|
||||
delay: 0
|
||||
}, () => {
|
||||
// 再次校验ID
|
||||
if (myId !== currentMarqueeId.value) return;
|
||||
|
||||
// 计算时间
|
||||
const speed = 50; // px/s
|
||||
const duration = (realScrollDistance / speed) * 1000;
|
||||
|
||||
// 2. 向左滚动一个文本宽度的距离
|
||||
animation.transition(target, {
|
||||
styles: { transform: `translateX(-${realScrollDistance}px)` },
|
||||
duration: duration,
|
||||
timingFunction: 'linear',
|
||||
delay: 0
|
||||
}, () => {
|
||||
// 3. 循环
|
||||
runMarqueeAnimation(containerWidth, textWidth, myId);
|
||||
});
|
||||
});
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
|
|
@ -182,11 +426,14 @@ const setUserData = () => {
|
|||
|
||||
.content-box {
|
||||
position: fixed;
|
||||
top: 150rpx;
|
||||
top: 0rpx;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 999;
|
||||
background-color: transparent;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.status-box {
|
||||
|
|
@ -215,6 +462,7 @@ const setUserData = () => {
|
|||
flex: 1;
|
||||
height: 44px;
|
||||
font-size: 32rpx;
|
||||
font-weight: 500;
|
||||
color: #1A1A1A;
|
||||
font-weight: bold;
|
||||
background-color: transparent;
|
||||
|
|
@ -234,7 +482,7 @@ const setUserData = () => {
|
|||
|
||||
.user-box {
|
||||
position: relative;
|
||||
margin: 0 32rpx 0;
|
||||
margin: 24rpx 32rpx 0;
|
||||
height: 120rpx;
|
||||
z-index: 10;
|
||||
}
|
||||
|
|
@ -310,12 +558,33 @@ const setUserData = () => {
|
|||
border-radius: 16rpx 16rpx 16rpx 16rpx;
|
||||
padding: 0 16rpx;
|
||||
height: 64rpx;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.sound-box {
|
||||
height: 64rpx;
|
||||
width: 50rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.notice-content-wrapper {
|
||||
flex: 1;
|
||||
flex-direction: row;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.notice-inner {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.notice-content {
|
||||
font-size: 24rpx;
|
||||
color: #767676;
|
||||
margin-left: 8rpx;
|
||||
}
|
||||
|
||||
.group-box {
|
||||
|
|
@ -329,12 +598,30 @@ const setUserData = () => {
|
|||
}
|
||||
|
||||
.video-help-box {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
background-color: #FFFFFF;
|
||||
padding: 24rpx 32rpx;
|
||||
border-radius: 24rpx;
|
||||
margin-top: 16rpx;
|
||||
}
|
||||
|
||||
.video-help-item {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.video-help-img {
|
||||
width: 96rpx;
|
||||
height: 96rpx;
|
||||
}
|
||||
|
||||
.video-help-title {
|
||||
font-size: 24rpx;
|
||||
color: #1A1A1A;
|
||||
}
|
||||
|
||||
.menu-box {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
|
@ -380,8 +667,8 @@ const setUserData = () => {
|
|||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 72rpx;
|
||||
height: 32rpx;
|
||||
width: 68rpx;
|
||||
height: 30rpx;
|
||||
z-index: 99;
|
||||
}
|
||||
|
||||
|
|
@ -392,4 +679,17 @@ const setUserData = () => {
|
|||
.alipay-year-bill {
|
||||
/* width: 100%; */
|
||||
}
|
||||
|
||||
.footer-box {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-top: 40rpx;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.vision-text {
|
||||
font-size: 24rpx;
|
||||
color: #767676;
|
||||
}
|
||||
</style>
|
||||
|
After Width: | Height: | Size: 34 KiB |
|
After Width: | Height: | Size: 486 B |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 861 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 269 B |
|
Before Width: | Height: | Size: 126 KiB After Width: | Height: | Size: 125 KiB |
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 7.9 KiB |
|
Before Width: | Height: | Size: 6.0 KiB After Width: | Height: | Size: 8.7 KiB |
|
|
@ -383,7 +383,6 @@ export const util = {
|
|||
timingFunc: 'easeIn'
|
||||
}
|
||||
})
|
||||
console.log("状态栏设置完毕!");
|
||||
setTimeout(function () {
|
||||
uni.setNavigationBarColor({
|
||||
frontColor: frontColor,
|
||||
|
|
|
|||