chuangte_bike_newxcx/page_shanghu/gongzuotai/ChargingDetail.vue
2025-12-26 16:56:36 +08:00

1101 lines
26 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 class="page">
<u-navbar title="收费模版编辑" :border-bottom="false" :background="bgc" title-color='#000' title-size='36' back-icon-color="#000"
height='44'></u-navbar>
<view class="content">
<!-- 基本信息卡片 -->
<view class="form-card">
<view class="card-title">基本信息</view>
<view class="form-item">
<view class="label">套餐名称</view>
<input type="text" v-model="data.name" placeholder="请输入套餐名称" class="input" placeholder-style="color:#C7CDD3">
</view>
<view class="form-item">
<view class="label">说明内容</view>
<input type="text" v-model="data.instructions" placeholder="请输入说明内容" class="input" placeholder-style="color:#C7CDD3">
</view>
<view class="form-item">
<view class="label">显示顺序</view>
<input type="number" v-model="data.orderNum" placeholder="请输入显示顺序" class="input" placeholder-style="color:#C7CDD3">
</view>
<view class="tips">数字越小越靠前</view>
</view>
<!-- 收费设置卡片 -->
<view class="form-card">
<view class="card-title">收费设置</view>
<view class="form-item">
<view class="label">租赁单位</view>
<view class="radio-group">
<view class="radio-tag"
v-for="(item, index) in timeList"
:key="index"
:class="{active: timevalue === item.name}"
@click="radioGroupChange(item.name)">
{{item.name}}
</view>
</view>
</view>
<view class="form-item">
<view class="label">计费方式</view>
<view class="radio-group">
<view class="radio-tag"
:class="{active: data.ridingRule == 1}"
@click="chargeTypeChange(1)">
普通计费
</view>
<view class="radio-tag"
:class="{active: data.ridingRule == 2}"
@click="chargeTypeChange(2)">
区间计费
</view>
</view>
</view>
<!-- 普通计费模式 -->
<template v-if="data.ridingRule == 1">
<view class="rule-box normal-rule">
<view class="rule-row">
<text class="rule-label">起步价</text>
<view class="input-wrap">
<input type="digit" inputmode="decimal" v-model="startingPrice" class="input-mini">
<text class="unit">元</text>
</view>
<text class="connect">含</text>
<view class="input-wrap">
<input type="number" v-model="startingTime" class="input-mini">
<text class="unit">{{timevalue}}</text>
</view>
</view>
<view class="divider"></view>
<view class="rule-row">
<text class="rule-label">超出价</text>
<view class="input-wrap">
<input type="digit" inputmode="decimal" v-model="timeoutPrice" class="input-mini">
<text class="unit">元</text>
</view>
<text class="connect">/</text>
<view class="input-wrap">
<input type="number" v-model="timeoutTime" class="input-mini">
<text class="unit">{{timevalue}}</text>
</view>
</view>
</view>
</template>
<!-- 区间计费模式 -->
<template v-if="data.ridingRule == 2">
<view class="interval-list">
<view class="interval-item" v-for="(item, index) in intervalRule" :key="index">
<view class="interval-header">
<text class="interval-name">区间 {{index + 1}}</text>
<view class="delete-btn" v-if="index > 0 && index < intervalRule.length - 1" @click="deleteRule(index)">
<u-icon name="trash" color="#FF4D4F" size="28"></u-icon>
</view>
</view>
<view class="interval-body">
<view class="rule-row">
<text class="rule-label">时长范围</text>
<view class="range-inputs">
<text class="static-val">{{index === 0 ? 0 : intervalRule[index-1].end}}</text>
<text class="separator">-</text>
<block v-if="index === intervalRule.length - 1">
<text class="static-val">不限</text>
</block>
<block v-else>
<input type="number" v-model="item.end" class="input-mini" placeholder="结束时长" @blur="handleEndChange(index)">
</block>
<text class="unit">{{timevalue}}</text>
</view>
</view>
<view class="rule-row">
<text class="rule-label">收费标准</text>
<view class="price-inputs">
<text>每</text>
<input type="number" v-model="item.eachUnit" class="input-mini">
<text>{{timevalue}}</text>
<input type="digit" inputmode="decimal" v-model="item.fee" class="input-mini">
<text>元</text>
</view>
</view>
</view>
</view>
<view class="add-btn" v-if="intervalRule.length < 5" @click="addRule">
<u-icon name="plus" color="#4C97E7" size="28"></u-icon>
<text>添加区间</text>
</view>
</view>
</template>
<view class="form-item mt-20">
<view class="label">押金金额</view>
<view class="input-group">
<input type="digit" inputmode="decimal" v-model="data.depositAmount" placeholder="0.00" class="input text-right">
<text class="suffix">元</text>
</view>
</view>
</view>
<!-- 其他设置卡片 -->
<view class="form-card">
<view class="card-title">其他设置</view>
<view class="form-item">
<view class="label">免费骑行</view>
<view class="input-group">
<input type="number" v-model="data.freeRideTime" placeholder="0" class="input text-right">
<text class="suffix">分钟</text>
</view>
</view>
<view class="tips">前xx分钟内归还车辆不收取费用</view>
<view class="divider margin-tb"></view>
<view class="switch-item">
<view class="switch-info">
<view class="label">用户押金抵扣</view>
<view class="desc">允许用户在订单结束后使用押金抵扣费用</view>
</view>
<u-switch v-model="data.depositDeduction" @change="toggleUseLimits" active-color="#4C97E7" size="40"></u-switch>
</view>
<template>
<view class="divider margin-tb"></view>
<view class="switch-item">
<view class="switch-info">
<view class="label">自动押金抵扣</view>
<view class="desc">订单结束后自动使用押金抵扣欠款</view>
</view>
<u-switch v-model="data.autoDeduct" @change="toggleUseLimitss" active-color="#4C97E7" size="40"></u-switch>
</view>
<view class="form-item bg-gray" v-if="data.autoDeduct">
<view class="label">自动抵扣延迟</view>
<view class="input-group">
<input type="number" v-model="data.deductDelay" placeholder="0" class="input text-right">
<text class="suffix">小时</text>
</view>
</view>
<view class="tips" v-if="data.autoDeduct">设置0将立即抵扣</view>
</template>
<view class="divider margin-tb"></view>
<view class="switch-item">
<view class="switch-info">
<view class="label">超时结束订单</view>
<view class="desc">超出可用时长后自动结束订单</view>
</view>
<u-switch v-model="data.timeoutFinish" @change="toggleUseLimitsss" active-color="#4C97E7" size="40"></u-switch>
</view>
</view>
<!-- 车型选择卡片 -->
<view class="form-card">
<view class="card-title">应用车型</view>
<view class="car-select" @click="showpart = true">
<view class="select-label">选择适用车型</view>
<view class="select-value">
<text v-if="!lists.suitIds || !lists.suitIds.length" class="placeholder">请选择</text>
<u-icon name="arrow-right" color="#999" size="28"></u-icon>
</view>
</view>
<view class="tag-container" v-if="lists.suitIds && lists.suitIds.length">
<view class="car-tag" v-for="id in lists.suitIds" :key="id">
{{ getAccessoryNameById(id) }}
<view class="close-icon" @click.stop="removeSuitId(id)">×</view>
</view>
</view>
</view>
<view style="height: 180rpx;"></view>
</view>
<!-- 底部按钮 -->
<view class="footer-btns">
<view class="btn cancel" @click="backpage()">取消</view>
<view class="btn confirm" @click="sub">保存</view>
</view>
<!-- 车型选择弹窗 -->
<u-popup v-model="showpart" mode="bottom" border-radius="24">
<view class="popup-container">
<view class="popup-header">
<text class="popup-title">选择车型</text>
<u-icon name="close" color="#999" size="28" @click="closepart"></u-icon>
</view>
<scroll-view class="popup-content" scroll-y>
<view class="model-list">
<view class="model-item"
v-for="(item, index) in Accessorylist"
:key="index"
:class="{active: lists.suitIds.includes(item.id)}"
@click="chooseAcc(item.id)">
<text>{{ item.name }}</text>
<u-icon v-if="lists.suitIds.includes(item.id)" name="checkmark-circle-fill" color="#4C97E7" size="36"></u-icon>
</view>
</view>
</scroll-view>
<view class="popup-footer">
<view class="full-btn" @click="subacc">确定</view>
</view>
</view>
</u-popup>
</view>
</template>
<script>
export default {
data() {
return {
lists: {
model: "",
fullVoltage: "",
lowVoltage: "",
fullEndurance: "",
suitIds: [],
lowBatteryReminder: '20',
lowBatteryReminderSwitch: false
},
showpart: false,
bgc: {
backgroundColor: "#F7F7F7",
},
showpart: false,
list: [],
Accessorylist: [],
chooseIdxArr: [], // 存储选中的索引
timevalue: '分钟',
timeList: [{
name: '分钟',
disabled: false
},
{
name: '小时',
disabled: false
},
{
name: '天',
disabled: false
},
],
startingPrice: "",
startingTime: '',
timeoutPrice: '',
timeoutTime: '',
data: {
name: '',
instructions: '',
orderNum:'',
status: "0",
autoRefundDeposit: '0',
orderExceedMinutes: '',
orderExceedWarn: '',
freeRideTime: '5',
rentalUnit: 'minutes',
ridingRule: '1',
chargingCycle: '1',
chargingCycleValue: '24',
depositAmount: '200',
timeoutTime: '',
startingTime: '',
orderNum:0,
userId:'',
startRule:{},
modellds:[],
depositDeduction:false,
autoDeduct:false,
depositAmount:'',
areaId:''
},
ruleId: '',
userinfo: {},
intervalRule: [
{
start: 0,
end: '',
fee: '',
eachUnit: '10'
},
{
start: '',
end: '',
fee: '',
eachUnit: '10'
},
{
start: '',
end: null,
fee: '',
eachUnit: '10'
}
]
}
},
onLoad(e) {
this.data.areaId = uni.getStorageSync('adminAreaid')
if (e.ruleId) {
this.ruleId = e.ruleId
this.getFeeInfo()
}
this.getinfo()
this.getAccessorylist()
},
onShow() {
},
methods: {
getAccessoryNameById(id) {
const item = this.Accessorylist.find(accessory => accessory.id == id)
return item ? item.name : ''
},
removeSuitId(id) {
const index = this.lists.suitIds.indexOf(id)
if (index > -1) {
this.lists.suitIds.splice(index, 1)
}
},
toggleUseLimits(e) {
this.data.depositDeduction = e;
},
toggleUseLimitss(e) {
this.data.autoDeduct = e;
},
toggleUseLimitsss(e) {
if(e == true){
let that = this
uni.showModal({
title: '安全警告!',
content: '开启后,订单超时将自动结束订单并锁车存在安全隐患,是否继续开启!',
showCancel: true,
success: function (res) {
if (res.confirm) {
that.data.timeoutFinish = e
} else if (res.cancel) {
that.data.timeoutFinish = false
}
}
})
}else{
this.data.timeoutFinish = e
}
},
closepart() {
this.showpart = false
},
subacc() {
this.showpart = false
console.log(this.chooseIdxArr)
},
chooseAcc(id) {
const index = this.lists.suitIds.indexOf(id)
if (index > -1) {
// 如果 id 已经存在于 suitIds 中,则从数组中删除
this.lists.suitIds.splice(index, 1)
} else {
// 如果 id 不存在,则添加到 suitIds 中
this.lists.suitIds.push(id)
}
},
getAccessoryNames(accessoryIds) {
const accessoryNames = this.lists.suitIds.map(id => {
const item = this.Accessorylist.find(accessory => accessory.id == id)
return item ? item.name : ''
})
// 过滤掉空值并返回数组
return accessoryNames.filter(name => name)
},
getAccessorylist() {
this.$u.get(`/bst/model/list?pageNum=1&pageSize=999&areaId=${this.data.areaId}`).then((res) => {
if (res.code == 200) {
this.Accessorylist = res.rows
} else {
uni.showToast({
title: res.msg,
icon: 'none',
duration: 1000
})
}
})
},
// 查询个人信息
getinfo() {
this.$u.get("/getInfo").then((res) => {
if (res.code == 200) {
this.userinfo = res.user
this.data.userId = this.userinfo.userId
}
})
},
getFeeInfo() {
this.$u.get(`/bst/suit/${this.ruleId}`).then((res) => {
if (res.code == 200) {
this.data = res.data;
this.lists.suitIds = res.data.modelIds
console.log(this.lists);
if (this.data.rentalUnit == 'minutes') {
this.timevalue='分钟'
} else if (this.data.rentalUnit == 'hours') {
this.timevalue='小时'
} else if (this.data.rentalUnit == 'day') {
this.timevalue='天'
}
if (this.data.area) {
this.returnVerify = this.data.area.returnVerify;
}
if (this.data.ridingRule == 1) {
if (this.data.startRule) {
this.timeoutTime = this.data.startRule.timeoutTime || '';
this.startingPrice = this.data.startRule.startingPrice || '';
this.startingTime = this.data.startRule.startingTime || '';
this.timeoutPrice = this.data.startRule.timeoutPrice || '';
}
} else if (this.data.ridingRule == 2) {
if (this.data.intervalRule && Array.isArray(this.data.intervalRule)) {
this.intervalRule = JSON.parse(JSON.stringify(this.data.intervalRule));
this.more = this.data.intervalRule[this.data.intervalRule.length - 1];
} else {
this.intervalRule = [
{
start: 0,
end: '',
fee: '',
eachUnit: '10'
},
{
start: '',
end: '',
fee: '',
eachUnit: '10'
},
{
start: '',
end: null,
fee: '',
eachUnit: '10'
}
];
}
}
console.log('处理后的数据:', {
data: this.data,
intervalRule: this.intervalRule,
more: this.more
});
} else {
uni.showToast({
title: res.msg,
icon: 'none',
duration: 1000
})
}
})
},
backpage() {
uni.navigateBack()
},
// 选中任一radio时由radio-group触发
radioGroupChange(e) {
this.timevalue = e;
if (e == '分钟') {
this.data.rentalUnit = 'minutes'
} else if(e == '小时'){
this.data.rentalUnit = 'hours'
}else {
this.data.rentalUnit = 'day'
}
},
chargeTypeChange(value) {
this.data.ridingRule = value;
if(value === 2) {
this.intervalRule = [
{
start: 0,
end: '',
fee: '',
eachUnit: '10'
},
{
start: '',
end: '',
fee: '',
eachUnit: '10'
},
{
start: '',
end: null,
fee: '',
eachUnit: '10'
}
];
}
},
handleEndChange(index) {
if(index < this.intervalRule.length - 1) {
this.intervalRule[index + 1].start = this.intervalRule[index].end;
}
},
addRule() {
// 移除最后一个元素
const lastRule = this.intervalRule.pop();
// 添加新规则
this.intervalRule.push({
start: this.intervalRule[this.intervalRule.length - 1].end,
end: '',
fee: '',
eachUnit: '10'
});
// 添加回最后一个不限的规则
this.intervalRule.push(lastRule);
},
deleteRule(index) {
if(index === 0 || index === this.intervalRule.length - 1) return;
this.intervalRule.splice(index, 1);
// 更新后续区间的起始值
for(let i = index; i < this.intervalRule.length - 1; i++) {
this.intervalRule[i].start = this.intervalRule[i-1].end;
}
},
sub() {
console.log(this.lists.suitIds,'000');
let data = {
...this.data,
type: 1,
modelIds:this.lists.suitIds
}
console.log(data,'111');
if (this.data.ridingRule == 2) {
// 区间计费时直接使用intervalRule字段
data.intervalRule = this.intervalRule.map((item, index) => ({
start: index === 0 ? 0 : Number(item.start),
end: index === this.intervalRule.length - 1 ? null : Number(item.end),
fee: Number(item.fee),
eachUnit: Number(item.eachUnit)
}))
// 删除startRule字段
delete data.startRule;
} else {
// 普通计费使用startRule
data.startRule = {
timeoutTime: this.timeoutTime,
startingPrice: this.startingPrice,
startingTime: this.startingTime,
timeoutPrice: this.timeoutPrice
}
}
console.log(data, 'mmmmmmmmmmmmm')
if (this.ruleId != '') {
this.$u.put(`/bst/suit`, data).then((res) => {
if (res.code == 200) {
uni.showToast({
title: '修改成功',
icon: 'success',
duration: 1000
})
setTimeout(() => {
uni.navigateBack()
}, 1100)
} else {
uni.showToast({
title: res.msg,
icon: 'none',
duration: 2000
})
}
})
} else {
this.$u.post(`/bst/suit`, data).then((res) => {
if (res.code == 200) {
uni.showToast({
title: '添加成功',
icon: 'success',
duration: 1000
})
setTimeout(() => {
uni.navigateBack()
}, 1100)
} else {
uni.showToast({
title: res.msg,
icon: 'none',
duration: 2000
})
}
})
}
}
}
}
</script>
<style lang="scss" scoped>
page {
background-color: #F7F7F7;
}
.page {
min-height: 100vh;
}
.content {
padding: 20rpx 30rpx;
}
.form-card {
background: #FFFFFF;
border-radius: 20rpx;
padding: 30rpx;
margin-bottom: 24rpx;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.03);
.card-title {
font-size: 32rpx;
font-weight: 600;
color: #333;
margin-bottom: 30rpx;
padding-left: 16rpx;
border-left: 8rpx solid #4C97E7;
line-height: 1;
}
.form-item {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24rpx;
.label {
font-size: 28rpx;
color: #333;
font-weight: 500;
}
.input {
flex: 1;
text-align: right;
font-size: 28rpx;
color: #333;
// 新增样式
height: 72rpx;
background-color: #F5F7FA;
border: 1rpx solid #E4E7ED;
border-radius: 8rpx;
padding: 0 24rpx;
margin-left: 20rpx;
&.text-right {
text-align: right;
}
&:focus {
background-color: #fff;
border-color: #4C97E7;
}
}
.radio-group {
display: flex;
gap: 20rpx;
.radio-tag {
padding: 10rpx 24rpx;
background: #F5F7FA;
border-radius: 30rpx;
font-size: 26rpx;
color: #666;
border: 1rpx solid transparent;
&.active {
background: #EBF4FF;
color: #4C97E7;
border-color: #4C97E7;
font-weight: 500;
}
}
}
.input-group {
display: flex;
align-items: center;
height: 72rpx;
background-color: #F5F7FA;
border: 1rpx solid #E4E7ED;
border-radius: 8rpx;
padding: 0 24rpx;
margin-left: 20rpx;
flex: 1;
max-width: 300rpx;
justify-content: flex-end;
&:focus-within {
background-color: #fff;
border-color: #4C97E7;
}
.input {
background-color: transparent;
border: none;
padding: 0;
margin-left: 0;
height: 100%;
}
.suffix {
margin-left: 10rpx;
font-size: 28rpx;
color: #333;
}
}
&.mt-20 {
margin-top: 20rpx;
}
&.bg-gray {
background: #F8F9FB;
padding: 20rpx;
border-radius: 12rpx;
margin-top: 20rpx;
.input-group {
background-color: #fff;
}
}
}
.tips {
font-size: 24rpx;
color: #999;
line-height: 1.4;
margin-top: -10rpx;
margin-bottom: 20rpx;
}
.rule-box {
background: #F8F9FB;
border-radius: 12rpx;
padding: 24rpx;
&.normal-rule {
.rule-row {
display: flex;
align-items: center;
justify-content: space-between;
.rule-label {
font-size: 28rpx;
color: #333;
font-weight: 500;
}
.input-wrap {
display: flex;
align-items: center;
background: #fff;
border-radius: 8rpx;
padding: 6rpx 12rpx;
border: 1rpx solid #e0e0e0;
.input-mini {
width: 100rpx;
text-align: center;
font-size: 28rpx;
}
.unit {
font-size: 24rpx;
color: #999;
margin-left: 6rpx;
}
}
.connect {
color: #999;
font-size: 24rpx;
margin: 0 10rpx;
}
}
.divider {
height: 1rpx;
background: #e0e0e0;
margin: 20rpx 0;
}
}
}
.interval-list {
.interval-item {
background: #F8F9FB;
border-radius: 12rpx;
padding: 20rpx;
margin-bottom: 20rpx;
.interval-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16rpx;
.interval-name {
font-size: 28rpx;
font-weight: 600;
color: #333;
}
}
.interval-body {
.rule-row {
display: flex;
align-items: center;
margin-bottom: 16rpx;
&:last-child {
margin-bottom: 0;
}
.rule-label {
width: 120rpx;
font-size: 26rpx;
color: #666;
}
.range-inputs, .price-inputs {
flex: 1;
display: flex;
align-items: center;
font-size: 26rpx;
color: #333;
.input-mini {
background: #fff;
width: 100rpx;
height: 50rpx;
border-radius: 8rpx;
text-align: center;
border: 1rpx solid #e0e0e0;
margin: 0 10rpx;
font-size: 26rpx;
}
.separator {
margin: 0 10rpx;
color: #999;
}
.unit {
margin-left: 10rpx;
color: #666;
}
.static-val {
padding: 0 10rpx;
font-weight: 500;
}
}
}
}
}
.add-btn {
display: flex;
justify-content: center;
align-items: center;
padding: 20rpx;
border: 1rpx dashed #4C97E7;
border-radius: 12rpx;
background: #EBF4FF;
text {
color: #4C97E7;
font-size: 28rpx;
margin-left: 10rpx;
}
}
}
.switch-item {
display: flex;
justify-content: space-between;
align-items: center;
.switch-info {
.label {
font-size: 30rpx;
color: #333;
font-weight: 500;
margin-bottom: 6rpx;
}
.desc {
font-size: 24rpx;
color: #999;
}
}
}
.car-select {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20rpx 0;
.select-label {
font-size: 28rpx;
color: #333;
}
.select-value {
display: flex;
align-items: center;
.placeholder {
color: #999;
font-size: 28rpx;
margin-right: 10rpx;
}
}
}
.tag-container {
display: flex;
flex-wrap: wrap;
gap: 16rpx;
margin-top: 10rpx;
.car-tag {
background: #EBF4FF;
color: #4C97E7;
font-size: 24rpx;
padding: 8rpx 20rpx;
border-radius: 30rpx;
display: flex;
align-items: center;
.close-icon {
margin-left: 10rpx;
font-size: 28rpx;
line-height: 1;
}
}
}
.divider {
height: 1rpx;
background: #F0F0F0;
width: 100%;
&.margin-tb {
margin: 24rpx 0;
}
}
}
.footer-btns {
position: fixed;
bottom: 0;
left: 0;
width: 100%;
background: #fff;
padding: 20rpx 30rpx;
padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
box-shadow: 0 -2rpx 10rpx rgba(0,0,0,0.05);
display: flex;
justify-content: space-between;
z-index: 100;
.btn {
width: 48%;
height: 88rpx;
border-radius: 44rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 32rpx;
font-weight: 500;
&.cancel {
background: #F5F7FA;
color: #666;
}
&.confirm {
background: #4C97E7;
color: #fff;
box-shadow: 0 4rpx 12rpx rgba(76, 151, 231, 0.3);
}
&:active {
opacity: 0.9;
}
}
}
.popup-container {
background: #fff;
.popup-header {
padding: 30rpx;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1rpx solid #F0F0F0;
.popup-title {
font-size: 32rpx;
font-weight: 600;
color: #333;
}
}
.popup-content {
height: 600rpx;
.model-list {
padding: 20rpx;
.model-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 24rpx 30rpx;
border-radius: 12rpx;
margin-bottom: 16rpx;
background: #F8F9FB;
&.active {
background: #EBF4FF;
color: #4C97E7;
font-weight: 500;
}
}
}
}
.popup-footer {
padding: 20rpx 30rpx;
padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
border-top: 1rpx solid #F0F0F0;
.full-btn {
width: 100%;
height: 88rpx;
background: #4C97E7;
color: #fff;
border-radius: 44rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 32rpx;
font-weight: 600;
}
}
}
</style>