alipay-emulator/pages/bill/add-bill/add-bill.vue

1569 lines
40 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view style="overflow: hidden;overflow-y: scroll; height: 100vh;">
<navBar isRightButton :title="data.navBar.title" :bgColor="data.navBar.bgColor" @right-click="onRightClick">
</navBar>
<!-- tabbar -->
<view class="tab-bar" v-if="!data.isEdit">
<view class="tab-item" :class="{ blue: billData.selectId == item.selectId }" v-for="item in classifyTabBar"
:key="item.selectId" @click="billData.selectId = Number(item.selectId)">
{{ item.type }}
</view>
</view>
<!-- 账单信息容器 -->
<view class="add-bill-container">
<!-- 随机骰子 -->
<view class="random-dice" v-if="!data.isEdit">
<image class="random-dice-image" src="/static/image/common/random-dice.png" @click="randomBillInfo">
</image>
</view>
<!-- 头像 -->
<view class="avatar-box">
<image class="avatar-image" :src="billData.imgUrl || defaultImage" @click="changeAvatar"></image>
</view>
<!-- 主要信息 -->
<view class="main-info flex-align-center flex-column">
<!-- 名称 -->
<view class="name info-item-input">
<!-- 隐藏的text用于测量宽度 -->
<text class="text-measure">{{ billData.name }}</text>
<input class="text-input" type="text" v-model="billData.name" />
<image class="edit-image" src="/static/image/bill/add-bill/edit.png"></image>
</view>
<!-- 金额 -->
<view class="money-box flex-align-center">
<view class="add money alipay-font">{{ billData.isAdd ? '+' : '-' }}</view>
<!-- 金额 -->
<view class=" money info-item-input alipay-font" style="height: 77rpx;">
<!-- 隐藏的text用于测量宽度 -->
<text class="text-measure font-w500" style="font-size: 64rpx;">{{ billData.money }}</text>
<input class="text-input font-w500" style="font-size: 64rpx;" type="digit"
v-model="billData.money" />
<image class="edit-image" src="/static/image/bill/add-bill/edit.png"></image>
</view>
</view>
<!-- 订单状态 -->
<view class="order-status-info" :class="{ isRefund: billData.merchantOption.refund }">
{{ billData.orderStatus }}
</view>
</view>
<!-- 详情信息列表 -->
<view class="detail-info-container">
<template v-for="item in billData.itemInfoList" :key="item.id">
<view class="info-item-box" v-if="item.key != 'paymentReward'">
<view class="item-label">
{{ item.label }}
</view>
<view v-if="item.type != 'link'" class="info-item-input" @click="onClickItemInfo(item)">
<!-- 隐藏的text用于测量宽度 -->
<text class="text-measure"
:class="{ visibility: item.type == 'time' || item.type == 'select' || (data.type == 1 && item.key == 'payMethod') }">{{
item.value
}}</text>
<input
v-if="(item.type == 'text' || item.type == 'digit' || item.type == 'number') && !(data.type == 1 && item.key == 'payMethod')"
:style="{ color: item.textColor ? item.textColor : '#1a1a1a' }" class="text-input"
:type="item.type" :focus="item.focus" v-model="item.value" @click.stop />
</view>
<image v-if="item.type != 'link' && !(data.type == 1 && item.key == 'payMethod')"
class="edit-image" src="/static/image/bill/add-bill/edit.png"
@click="onClickItemInfo(item)">
</image>
</view>
<view v-if="item.type == 'link'" class="info-item-link">
<view class="img-box" @click="onClickItemInfo(item, 'uploadImage')">
<image class="img w100 h100" :src="item.value.imgUrl || defaultImage"></image>
</view>
<view class="textarea-box flex-1">
<textarea class="textarea w100 h100" name="" v-model="item.value.text" id=""
placeholder="请输入交易商品名称"></textarea>
</view>
<view class="right-input-box flex-align-center">
<!-- <text class="right-text">共{{ item.value.number }}件</text> -->
<input class="right-text" :focus="item.focus" type="text" v-model="item.value.quantity"
@click.stop />
<image class="edit-image" src="/static/image/bill/add-bill/edit.png"
@click="onClickItemInfo(item, 'foucs')">
</image>
</view>
</view>
</template>
<view class="info-item-box" v-if="data.type == 1">
<view class="item-label">
余额
</view>
<view class="info-item-input">
<!-- 隐藏的text用于测量宽度 -->
<text class="text-measure">{{ billData.balance }}</text>
<input class="text-input" type="digit" v-model="billData.balance" />
</view>
<image class="edit-image" src="/static/image/bill/add-bill/edit.png">
</image>
</view>
</view>
</view>
<!-- switch选项列表 -->
<view class="switch-option-container">
<template v-for="option in data.switchOptions" :key="option.id">
<view v-if="option.showTypeIds ? option.showTypeIds.includes(billData.selectId) : true"
class="border-bottom" :class="{ 'no-border-bottom': isNoBorderBottom(option) }">
<view class="switch-option">
<view class="switch-option-text">{{ option.name }}</view>
<switch v-if="option.isSwitch" color="#1676FE" :checked="billData.merchantOption[option.key]"
style="transform:scale(0.8)" @change="(e) => onSwitchChange(e, option)" />
<template v-else>
<!-- 账单分类 -->
<view v-if="option.key == 'billClassify'" class="right-box flex-1 text-align-right"
@click="onClickItemInfo(option)">
<text>{{ billData.merchantOption[option.key] }}</text>
<image class="edit-image" src="/static/image/bill/add-bill/edit.png">
</image>
</view>
<!-- 标签 -->
<view v-if="option.key == 'tag'" class="right-box flex-1 flex-align-center"
@click="onClickItemInfo(option)">
<view v-if="billData.merchantOption[option.key].length > 0"
class="tag-box flex-align-center text-align-right flex-1">
<view v-for="(tag, index) in billData.merchantOption[option.key].slice(0, 3)"
:key="tag" class="tag-item">
<text>{{ index === 2 ? (tag.substring(0, 1) + (tag.length > 1 ? '...' : '')) :
tag }}</text>
</view>
</view>
<view v-else class="add-tag-desc flex-1">
添加
</view>
<image class="edit-image" src="/static/image/bill/add-bill/edit.png">
</image>
</view>
<!-- 备注 -->
<view v-if="option.key == 'note'"
class="right-box flex-1 text-align-right flex-align-center"
@click="onClickItemInfo(option)">
<view
v-if="billData.merchantOption[option.key].text || billData.merchantOption[option.key].isImage"
class="remark-box flex-align-center text-align-right flex-1">
<image v-if="billData.merchantOption[option.key].isImage" class="remark-img"
src="/static/image/bill/bill-detail/bill-remark-img.png">
</image>
<view class="text">
{{ billData.merchantOption[option.key].text }}
</view>
</view>
<view v-else class="add-tag-desc flex-1">
添加
</view>
<image class="edit-image" src="/static/image/bill/add-bill/edit.png">
</image>
</view>
</template>
</view>
<!-- 服务详情 -->
<view class="service-detail"
v-if="option.key == 'serviceDetail' && billData.merchantOption.serviceDetail && option.showTypeIds.includes(billData.selectId)">
<image class="service-detail-image" @click="switchUploadImage('serviceDetail')"
:src="billData.merchantOption.serviceDetailInfo.imgUrl || defaultImage" />
<view class="flex-1 over-hidden">
<view class="service-detail-info info-item-input">
<!-- 隐藏的text用于测量宽度 -->
<text class="text-measure" style="font-size: 22rpx;">{{
billData.merchantOption.serviceDetailInfo.text }}</text>
<input class="text-input" maxlength="20" style="font-size: 22rpx;" type="text"
v-model="billData.merchantOption.serviceDetailInfo.text" />
<image class="edit-image" src="/static/image/bill/add-bill/edit.png"></image>
</view>
</view>
<uni-data-select class="select" style="flex: none;width: 80px;"
v-model="billData.merchantOption.serviceDetailInfo.rightText"
:localdata="serviceDetailRightTextList" @change="changeTips"
:clear="false"></uni-data-select>
<image class="right-icon" src="/static/image/common/right-grey.png" />
</view>
<!-- 推荐服务 -->
<view class="service-detail recommend-service "
v-if="option.key == 'recommendService' && billData.merchantOption.recommendService && option.showTypeIds.includes(billData.selectId)">
<view class="flex-1 over-hidden">
<view class="service-detail-info info-item-input">
<!-- 隐藏的text用于测量宽度 -->
<text class="text-measure" style="font-size: 22rpx;">{{
billData.merchantOption.recommendServiceInfo.text }}</text>
<input class="text-input" maxlength="20" style="font-size: 22rpx;" type="text"
v-model="billData.merchantOption.recommendServiceInfo.text" />
<image class="edit-image" src="/static/image/bill/add-bill/edit.png"></image>
</view>
</view>
<text class="right-text">去看看</text>
<image class="right-icon" src="/static/image/common/right-grey.png" />
</view>
<!-- 服务推荐 -->
<view class="service-detail service-recommend "
v-if="option.key == 'serviceRecommend' && billData.merchantOption.serviceRecommend && option.showTypeIds.includes(billData.selectId)">
<view class="flex-1 over-hidden">
<view class="service-detail-info info-item-input">
<!-- 隐藏的text用于测量宽度 -->
<text class="text-measure" style="font-size: 22rpx;">{{
billData.merchantOption.serviceRecommendInfo.text }}</text>
<input class="text-input" maxlength="20" style="font-size: 22rpx;" type="text"
v-model="billData.merchantOption.serviceRecommendInfo.text" />
<image class="edit-image" src="/static/image/bill/add-bill/edit.png"></image>
</view>
</view>
<image class="right-icon" src="/static/image/common/right-grey.png" />
</view>
</view>
</template>
</view>
<!-- 底部icon配置选项列表 -->
<view class="bill-bottom-icon-options">
<template v-for="option in billBottomIconList" :key="option.id">
<view class="item-option-box" v-show="!billData.bottomIcons.includes(option.hideIncloudId)">
<view class="item-box" :class="{ 'active-item-box': billData.bottomIcons.includes(option.id) }"
@click="onBottomIconClick(option)">
<image class="item-icon"
:src="`/static/image/bill/${billData.bottomIcons.includes(option.id) ? 'white-icon' : 'blue-icon'}/${option.icon}.png`">
</image>
<text class="item-text">{{ option.name }}</text>
</view>
</view>
</template>
</view>
</view>
<!-- 底部弹出层 -->
<uni-popup ref="timepopup" type="bottom">
<!-- 时间选择器 -->
<view v-if="selectItemInfo.type == 'time'" class="timeBox">
<view class="titleBox">
<view class="title" @click="closeTimePicker">
取消
</view>
<view class="btn" @click="settmes">
确定
</view>
</view>
<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>
<script setup>
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 { 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 {
reactive,
toRefs,
ref,
onMounted,
watch,
nextTick
} from 'vue'
import {
onLoad,
onShow,
} from '@dcloudio/uni-app'
// 时间选择器
const timepopup = ref(null)
const {
addBill,
getBillList,
updateBill
} = useStore()
// 分类tabBar
const classifyTabBar = ref(addBillJson.classifyTabBar)
// 账单分类选项
const billClassifyOptions = ref(addBillJson.billClassify)
// 底部icon配置选项列表
const billBottomIconList = ref(addBillJson.billBottomIconList)
// 服务详情右侧选项
const serviceDetailRightTextList = [
{
"value": 0,
"text": "进入小程序"
},
{
"value": 1,
"text": "查看详情"
}
]
// switch选项
const switchOptions = [
{
"id": 7,
"name": "账单分类",
"isSwitch": false,
key: 'billClassify',
},
{
"id": 8,
"name": "标签",
"isSwitch": false,
key: 'tag',
},
{
"id": 9,
"name": "备注",
"isSwitch": false,
key: 'note',
},
{
"id": 1,
"name": "服务详情",
"isSwitch": true,
showTypeIds: [3, 4, 10],
key: 'serviceDetail',
},
{
"id": 2,
"name": "推荐服务",
"isSwitch": true,
showTypeIds: [4],
key: 'recommendService',
},
{
"id": 3,
"name": "服务推荐",
"isSwitch": true,
showTypeIds: [6, 7],
key: 'serviceRecommend',
},
{
"id": 4,
"name": "退款",
"isSwitch": true,
showTypeIds: [12, 13],
key: 'refund',
change: (value) => {
if (billData.value.selectId == 12 || billData.value.selectId == 13) {
if (value) {
billData.value.orderStatus = "已全额退款"
} else {
billData.value.orderStatus = "交易成功"
}
}
}
},
{
"id": 5,
"name": "支付奖励",
"isSwitch": true,
showTypeIds: [1, 3, 4, 13],
key: 'payReward',
},
{
"id": 6,
"name": "计入收支",
"isSwitch": true,
showTypeIds: [1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13],
key: 'countInAndOut',
},
]
//获取全部字段的key值
let allFieldsList = ref([])
const defaultImage = "/static/image/bill/add-bill/add-avatar.png"
const data = reactive({
navBar: {
title: '新增账单',
bgColor: '#F5F5F5',
},
selectItemInfo: {
type: '',
value: '',
label: '',
id: ''
},
currentClassifyId: 0,
//当前底部字段选择
currentBottomIcons: [],
// 时间选择器
datePickerData: {
selectDate: "",
startDate: "",
endDate: "",
},
storeData: {
imgUrl: "",
name: ""
},
popupType: "",
isEdit: false,
type: 0,
payMethod: "",
// 账单数据
billData: {
id: "",
selectId: -1,
imgUrl: "",
name: '',
isAdd: false,
money: "",
balance: 0,
payMethod: "",
orderStatus: '交易成功',
itemInfoList: classifyTabBar.value[0].itemInfoList,
bottomIcons: [0, 1, 2, 3],
merchantOption: {
serviceDetail: false,
serviceDetailInfo: {
imgUrl: '',
rightText: 0,
text: '收钱吧'
},
recommendService: false,
recommendServiceInfo: {
text: '你有一个免费取现机会'
},
serviceRecommend: false,
serviceRecommendInfo: {
text: '恭喜你有6元转账保障福利待领取'
},
refund: false,
countInAndOut: true,
payReward: true,
billClassify: "日用百货",
tag: [],
note: {
isImage: false,
text: ''
}
}
},
tempTags: [],
tempNote: {
isImage: false,
text: ''
},
tagInputValue: "",
showTagInput: false,
// switch选项
switchOptions: switchOptions
})
let { billData, datePickerData, selectItemInfo } = toRefs(data)
onShow(() => {
// #ifdef APP-PLUS
util.setAndroidSystemBarColor('#F5F5F5')
// #endif
})
onLoad((option) => {
uni.$on('addBill', (res) => {
console.log("addBill", res);
billData.value.imgUrl = res.url
billData.value.name = res.name
console.log(billData.value)
});
console.log(option)
if (option.type && option.type == 1) {
data.type = option.type
data.navBar.title = '新增余额明细'
}
if (option.id) {
// 获取当前id账单
const existingBill = getBillList().find(b => b.id === option.id)
if (existingBill) {
//表单赋值
billData.value.id = existingBill.id
billData.value.selectId = existingBill.selectId
billData.value.name = existingBill.name
billData.value.money = existingBill.money
billData.value.imgUrl = existingBill.imgUrl
billData.value.isAdd = existingBill.isAdd
billData.value.orderStatus = existingBill.orderStatus
billData.value.balance = existingBill.balance
billData.value.payMethod = existingBill.payMethod
billData.value.bottomIcons = existingBill.bottomIcons
billData.value.merchantOption = JSON.parse(JSON.stringify(existingBill.merchantOption))
// 等待监听处理selectId更改然后覆盖itemInfoList
nextTick(() => {
billData.value.itemInfoList = JSON.parse(JSON.stringify(existingBill.itemInfoList))
})
}
}
if (option.isEdit) {
data.isEdit = true
data.navBar.title = '编辑账单'
}
if (!data.isEdit) {
billData.value.selectId = 1
randomBillInfo()
}
setAllFieldsList()
})
// 监听selectId
watch(() => billData.value.selectId, (newVal, oldVal) => {
if (data.isEdit) return
// 储存到全部字段的值中
allFieldsList.value.forEach(item => {
billData.value.itemInfoList.forEach(oldItem => {
if (oldItem.key == item.key) {
item.value = oldItem.value
}
})
})
console.log("全部字段的值", allFieldsList.value)
// 获取当前分类的字段
let currentItemInfoList = []
classifyTabBar.value.forEach(item => {
if (item.selectId == newVal) {
currentItemInfoList = item.itemInfoList ? JSON.parse(JSON.stringify(item.itemInfoList)) : []
}
})
// 从全部字段的值中获取当前分类的字段的值
currentItemInfoList.forEach(item => {
allFieldsList.value.forEach(oldItem => {
if (oldItem.key == item.key) {
item.value = item.value || oldItem.value
}
})
})
// 设置当前分类的字段
billData.value.itemInfoList = currentItemInfoList
const orderStatus = classifyTabBar.value.find(item => item.selectId == newVal).orderStatus
console.log("是否退款", billData.value.merchantOption)
// console.log("是否退款", billData.value.merchantOption.refund && (newVal == 12 || newVal == 13))
if (billData.value.merchantOption.refund && (newVal == 12 || newVal == 13)) {
billData.value.orderStatus = "退款成功"
} else {
billData.value.orderStatus = orderStatus
}
// 设置当前分类的底部字段
billData.value.bottomIcons = classifyTabBar.value.find(item => item.selectId == newVal).defaultBottomIcons
console.log("当前分类的底部字段", billData.value.bottomIcons)
// 获取当前选中项
const currentItem = classifyTabBar.value.find(item => item.selectId == newVal)
// 设置当前分类的是否为收入
billData.value.isAdd = currentItem.isAdd
// 设置名称和头像
billData.value.name = currentItem.name || data.storeData.name
if (newVal == 10 || newVal == 11) {
billData.value.imgUrl = currentItem.imgUrl
} else {
billData.value.imgUrl = currentItem.imgUrl || data.storeData.imgUrl
}
console.log("头像图片", billData.value.imgUrl)
setItemInfoValue(newVal)
})
// 设置全部字段列表
function setAllFieldsList() {
const keySet = new Set()
classifyTabBar.value.forEach(item => {
if (item.itemInfoList) {
item.itemInfoList.forEach(info => {
if (info.key == "payMethod") {
if (data.type && data.type == 1) {
info.value = "余额"
} else {
info.value = "招商银行储蓄卡(0123)"
}
} else if (info.key == "tagAndNote") {
info.value = "添加"
}
if (info.key && !keySet.has(info.key)) {
keySet.add(info.key)
allFieldsList.value.push({
key: info.key,
value: info.value ? info.value : "",
type: info.type
})
}
})
}
})
}
/**
* 设置每个字段信息值
*/
const setItemInfoValue = (id) => {
// 设置当前分类的特定字段值
billData.value.itemInfoList.forEach(item => {
if (item.key == 'orderNumber') {
item.value = randomUtil.randomOrderNumber(28)
} else if (item.type == 'time') {
item.value = randomUtil.randomTime()
console.log("item.type == 'time'", item.value)
} else if (item.key == 'householdNumber') {
item.value = randomUtil.randomOrderNumber(10)
} else if (item.key == 'receiverFullName') {
item.value = `${billData.value.name}有限公司`
} else if (item.key == 'productDescription') {
item.value = `xxxxxxx消费`
}
switch (id) {
case 2:
if (item.key == 'receiverFullName') {
item.value = `${billData.value.name}(个人)`
}
break;
case 5:
if (item.key == 'counterAccount') {
item.value = `${billData.value.name}`
}
break;
case 6:
if (item.key == 'counterAccount' && item.value == '') {
item.value = `xxx123654789`
}
break;
case 7:
if (item.key == 'counterAccount' && item.value == '') {
item.value = `xxx123654789`
}
break;
default:
break;
}
})
}
/**
* 切换头像
*/
const changeAvatar = () => {
util.goPage("/pages/common/hot-icon/hot-icon" + "?page=addBill")
}
/**
* 随机生成账单信息
*/
const randomBillInfo = () => {
if (data.type == 1) {
billData.value.balance = randomUtil.randomMoney(100000)
}
// 随机生成金额
billData.value.money = randomUtil.randomMoney()
console.log("allFieldsList", allFieldsList.value)
// 随机抽取头像
const hotIconList = hotIcon.moneyHotIcon.filter(item => item.classify != '系统图标' && item.classify != '自定义' && item.classify != '银行卡')
if (hotIconList.length > 0) {
const randomIcon = hotIconList[randomUtil.random(0, hotIconList.length - 1)]
billData.value.imgUrl = `/static/image/common/hot-icon/${randomIcon.label}.png`
billData.value.name = randomIcon.name
}
// 储蓄随机生成的头像和name
data.storeData = {
imgUrl: billData.value.imgUrl,
name: billData.value.name
}
// 随机骰子生成数据
billData.value.itemInfoList.forEach(item => {
if (item.key == 'orderNumber') {
item.value = randomUtil.randomOrderNumber(28)
} else if (item.type == 'time') {
item.value = randomUtil.randomTime()
console.log("item.type == 'time'", item.value)
} else if (item.key == 'householdNumber') {
item.value = randomUtil.randomOrderNumber(10)
} else if (item.key == 'receiverFullName') {
item.value = `${billData.value.name}有限公司`
}
})
console.log("随机生成账单信息", billData.value)
}
/**
* 校验表单数据
*/
const validateBillData = () => {
if (!billData.value.name) {
uiUtil.showError("请输入名称")
return false
} else if (!billData.value.money) {
uiUtil.showError("请输入金额")
return false
}
// 校验itemInfoList
for (let i = 0; i < billData.value.itemInfoList.length; i++) {
const item = billData.value.itemInfoList[i]
if (item.required && !item.value) {
uiUtil.showError(`请输入${item.label}`)
return false
}
if (item.type == "link") {
if (!item.value.imgUrl || item.value.imgUrl == '') {
uiUtil.showError("请上传商品图片")
return false
} else if (!item.value.text || item.value.text == '') {
uiUtil.showError("请输入商品信息")
return false
} else if (!item.value.quantity || item.value.quantity == '') {
uiUtil.showError("请输入交易数量")
return false
}
}
}
console.log("校验表单数据", billData.value)
const merchantOptionKeys = [{ key: 'serviceDetail', label: '服务详情' }, { key: 'serviceRecommend', label: '服务推荐' }, { key: 'recommendService', label: '推荐服务' }]
for (let i = 0; i < merchantOptionKeys.length; i++) {
const item = merchantOptionKeys[i]
console.log("校验表单数据key", billData.value.merchantOption[item.key], item.key)
if (billData.value.merchantOption[item.key]) {
const infoKey = item.key + "Info"
// Ensure info object exists
if (!billData.value.merchantOption[infoKey]) {
billData.value.merchantOption[infoKey] = {}
}
if (!billData.value.merchantOption[infoKey].text || billData.value.merchantOption[infoKey].text == '') {
uiUtil.showError(`请输入${item.label}`)
return false
}
if (item.key == "serviceDetail") {
console.log("校验表单数据keyInfo", !billData.value.merchantOption[infoKey].imgUrl || billData.value.merchantOption[infoKey].imgUrl == '')
if (!billData.value.merchantOption[infoKey].imgUrl || billData.value.merchantOption[infoKey].imgUrl == '') {
uiUtil.showError("请上传服务详情图片")
return false
}
}
}
}
return true
}
/**
* 保存账单
*/
const onRightClick = async () => {
if (!validateBillData()) return
if (billData.value.merchantOption.serviceDetail && billData.value.merchantOption.serviceDetailInfo && billData.value.merchantOption.serviceDetailInfo.imgUrl) {
const url = await saveAndDisplayImage(billData.value.merchantOption.serviceDetailInfo.imgUrl)
billData.value.merchantOption.serviceDetailInfo.imgUrl = url
}
for (const key in billData.value.itemInfoList) {
const element = billData.value.itemInfoList[key];
if (element.type == "link") {
if (element.value.imgUrl) {
billData.value.itemInfoList[key].value.imgUrl = await saveAndDisplayImage(element.value.imgUrl)
}
}
}
console.log("保存账单", billData.value)
if (billData.value.id) {
// Edit mode
updateBill(billData.value.id, billData.value)
} else {
// Add mode
billData.value.id = stringUtil.uuid()
if (data.type == 1) {
billData.value.payMethod = "余额"
}
addBill(billData.value)
}
uni.showToast({
title: "保存成功",
icon: "none",
duration: 500
})
setTimeout(() => {
uni.navigateBack()
}, 500)
}
// 点击itemInfo
const onClickItemInfo = async (item, action) => {
console.log(item)
if (item.type == 'time') {
datePickerData.value.selectDate = item.value;
datePickerData.value.endDate = formatTime(new Date());
selectItemInfo.value = item
timepopup.value.open()
} else if (item.key == 'billClassify') {
// selectItemInfo.value = item
data.popupType = "billClassify"
timepopup.value.open()
} else if (item.type == 'text' || item.type == 'number' || item.type == "digit") {
item.focus = false
nextTick(() => {
item.focus = true
})
} else if (item.type == 'link') {
if (action == 'uploadImage') {
item.value.imgUrl = await uploadImage()
} else if (action == 'foucs') {
item.focus = false
nextTick(() => {
item.focus = true
})
}
} 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()
}
}
/**
* switch按钮上传图片
* @param key
*/
const switchUploadImage = async (key) => {
if (key == 'serviceDetail') {
billData.value.merchantOption.serviceDetailInfo.imgUrl = await uploadImage()
}
}
/**
* 上传图片
*/
const uploadImage = () => {
return new Promise((resolve, reject) => {
uni.chooseImage({
count: 1,
sizeType: ['original', 'compressed'],
sourceType: ['album'],
success: (res) => {
resolve(res.tempFilePaths[0])
},
fail: (err) => {
reject(err)
}
})
})
}
/**
* @param {Object} file
* 保存图片到本地
*/
const saveAndDisplayImage = (file) => {
// #ifdef H5
return Promise.resolve(file)
// #endif
// #ifndef H5
return new Promise((resolve, reject) => {
uni.saveFile({
tempFilePath: file,
success: (res) => {
resolve(res.savedFilePath)
},
fail: (error) => {
console.error('保存图片失败:', error)
resolve("")
}
})
})
// #endif
}
/**
* 底部icon点击选择
* @param option
*/
const onBottomIconClick = (option) => {
if (billData.value.bottomIcons.includes(option.id)) {
billData.value.bottomIcons = billData.value.bottomIcons.filter(item => item != option.id)
} else {
billData.value.bottomIcons.push(option.id)
}
console.log(billData.value.bottomIcons)
}
/**
* switch切换
*/
const onSwitchChange = (e, option) => {
const value = e.detail.value
billData.value.merchantOption[option.key] = value
console.log(billData.value.merchantOption)
if (option.change) {
option.change(value)
}
}
/**
* 判断是否需要移除下边框
*/
const isNoBorderBottom = (option) => {
const keys = ['serviceDetail', 'recommendService', 'serviceRecommend'];
return billData.value.merchantOption[option.key] && keys.includes(option.key);
}
/**
* 格式化时间
*/
function formatTime(time) {
const newTime = new Date(time)
const year = newTime.getFullYear()
const month = newTime.getMonth() + 1
const date = newTime.getDate()
const hours = newTime.getHours()
const minutes = newTime.getMinutes()
const seconds = newTime.getSeconds()
return `${year}-${month}-${date} ${hours}:${minutes}:${seconds}`
}
/**
* 关闭时间选择器
*/
const closeTimePicker = () => {
timepopup.value.close()
}
/**
* 设置时间
*/
const settmes = () => {
console.log(datePickerData.value.selectDate)
billData.value.itemInfoList.forEach(item => {
if (item.type == 'time' && selectItemInfo.value.key == item.key) {
item.value = datePickerData.value.selectDate
}
})
timepopup.value.close()
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
* 切换时间
*/
function onChangeStartDate(date) {
datePickerData.value.selectDate = date;
}
</script>
<style lang="less">
@import "@/common/main.css";
@import "@/common/specify-style.less";
page {
background-color: #F5F5F5;
}
</style>
<style lang="less" scoped>
.tab-bar {
display: flex;
align-items: center;
flex-wrap: nowrap;
margin: 22rpx 24rpx;
overflow: hidden;
overflow-x: scroll;
&::-webkit-scrollbar {
display: none;
width: 0 !important;
height: 0 !important;
-webkit-appearance: none;
background: transparent;
}
.tab-item {
display: inline-block;
padding: 12rpx 24rpx;
background-color: #ffffff;
border-radius: 28rpx 28rpx 28rpx 28rpx;
width: auto;
font-size: 26rpx;
line-height: 31rpx;
font-weight: 500;
margin-right: 16rpx;
white-space: nowrap;
}
.blue {
color: #1B71F8;
}
}
.add-bill-container {
background-color: #ffffff;
margin: 24rpx;
margin-top: 34rpx;
padding: 24rpx;
border-radius: 16rpx 16rpx 16rpx 16rpx;
.random-dice {
width: 100%;
text-align: right;
.random-dice-image {
width: 64rpx;
height: 64rpx;
}
}
}
.avatar-box {
margin-top: 12rpx;
text-align: center;
margin-bottom: 26rpx;
.avatar-image {
width: 80rpx;
height: 80rpx;
border-radius: 50%;
}
}
.main-info {
color: #1a1a1a;
font-size: 64rpx;
.order-status-info {
font-size: 28rpx;
margin-top: 12rpx;
}
.isRefund {
color: #EA6B48;
}
}
.detail-info-container {
padding-top: 36rpx;
.info-item-input {
font-size: 26rpx;
.text-measure {
font-size: 26rpx;
}
.text-input {
font-size: 26rpx;
}
}
.info-item-link {
display: flex;
align-items: center;
padding: 12rpx 16rpx;
background-color: #F6F7FB;
border-radius: 20rpx 20rpx 20rpx 20rpx;
.img-box {
width: 88rpx;
height: 88rpx;
border-radius: 8rpx;
margin-right: 22rpx;
background-color: #E8EDF2;
.img {
border-radius: 8rpx 8rpx 8rpx 8rpx;
}
}
.textarea-box {
height: 40px;
.textarea {
font-size: 26rpx;
color: #1a1a1a;
}
}
.right-input-box {
.right-text {
color: #969696;
font-size: 22rpx;
max-width: 60rpx;
text-align: right;
margin-left: 8rpx;
}
}
}
}
.info-item-box {
display: flex;
align-items: center;
margin: 20rpx 0;
.item-label {
min-width: 160rpx;
margin-right: 28rpx;
font-size: 26rpx;
color: var(--text-secondary);
}
}
.info-item-input {
display: inline-flex;
align-items: center;
position: relative;
min-width: 20px;
max-width: 100%;
width: auto;
overflow: hidden;
min-height: 20px;
/* 隐藏的测量元素 */
.text-measure {
visibility: hidden;
white-space: nowrap;
color: var(--text-color);
font-family: inherit;
font-size: 28rpx;
width: auto;
min-width: 20px;
height: auto;
}
.visibility {
visibility: visible;
}
.text-input {
font-size: 28rpx;
position: absolute;
width: calc(100%);
}
}
.edit-image {
width: 28rpx;
height: 28rpx;
margin-left: 10rpx;
flex-shrink: 0;
}
.money {
// margin-top: 14rpx;
}
.switch-option-container {
margin: 16rpx 24rpx;
background-color: #ffffff;
border-radius: 16rpx 16rpx 16rpx 16rpx;
.border-bottom {
border-bottom: 1px solid #efefef
}
.no-border-bottom {
border: none;
}
.switch-option {
padding: 24rpx;
// border-top: 1rpx solid #E4E4E4;
display: flex;
justify-content: space-between;
align-items: center;
.switch-option-text {
font-size: 28rpx;
}
.right-box {
font-size: 26rpx;
}
.text-align-right {
text-align: right;
}
}
.tag-box {
justify-content: flex-end;
.tag-item {
background-color: #E6F2FF;
color: #2788D1;
font-size: 24rpx;
padding: 0 16rpx;
border-radius: 4rpx;
height: 52rpx;
line-height: 52rpx;
margin-right: 8rpx;
}
}
.remark-box {
justify-content: flex-end;
color: #969696;
font-size: 26rpx;
.text {
max-width: 280rpx;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.remark-img {
width: 40rpx;
height: 40rpx;
margin-right: 8rpx;
}
}
.add-tag-desc {
text-align: right;
font-size: 26rpx;
color: #969696;
}
.switch-option:first-child {
border-top: none !important;
}
.service-detail {
display: flex;
align-items: center;
background-color: #F6F7FB;
padding: 22rpx 24rpx;
border-radius: 16rpx 16rpx 16rpx 16rpx;
margin: 0 18rpx;
.service-detail-info {
margin-left: 8rpx;
}
.service-detail-image {
width: 44rpx;
height: 44rpx;
}
.enter-mini-program {
font-size: 22rpx;
color: #969696;
margin-left: 6px;
line-height: 22rpx;
}
.right-text {
font-size: 22rpx;
color: #969696;
margin-left: 6px;
line-height: 22rpx;
}
.right-icon {
width: 24rpx;
height: 24rpx;
}
.select {
flex: none;
width: 80px;
::v-deep .uni-select {
font-size: 22rpx;
padding: 0 5px !important;
min-height: 22rpx !important;
}
::v-deep .uni-select__selector-item {
font-size: 22rpx;
padding: 0 5px !important;
}
::v-deep .uni-icons {
font-size: 22rpx !important;
}
}
}
.recommend-service {
background-color: #FFF5F6;
.text-input {
color: #9E2036;
}
}
.service-recommend {
background-color: #F6F7FB;
.text-input {
color: #123D72;
}
}
}
.bill-bottom-icon-options {
display: flex;
flex-wrap: wrap;
margin: 16rpx 24rpx;
background-color: #ffffff;
border-radius: 16rpx 16rpx 16rpx 16rpx;
padding: 22rpx 26rpx;
.item-option-box {
width: calc(100% / 3);
// text-align: center;
margin: 14rpx 0;
.item-box {
display: inline-flex;
font-size: 20rpx;
height: 52rpx;
padding: 0 18rpx;
border-radius: 94rpx 94rpx 94rpx 94rpx;
border: 2rpx solid #EDEDED;
align-items: center;
.item-icon {
width: 20rpx;
height: 20rpx;
margin-right: 12rpx;
}
}
.active-item-box {
background-color: #1B71F8;
color: #ffffff;
border: none;
}
}
}
.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>