alipay-emulator/components/bill-management-popup/bill-management-popup.vue

290 lines
8.9 KiB
Vue

<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
} 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>