chuangte_bike_newxcx/page_shanghu/guanli/area_ability_setting.vue
2026-05-29 17:26:45 +08:00

473 lines
13 KiB
Vue
Raw Permalink 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="navTitle" :border-bottom="false" :background="bgc" back-icon-color="#111827" title-color="#111827" title-size="34" height="44"></u-navbar>
<view class="summary-card" v-if="form.abilityName || form.areaName">
<text class="summary-name">{{ form.abilityName || '--' }}</text>
<text class="summary-area">{{ form.areaName || '--' }}</text>
</view>
<view class="page-loading" v-if="loading">加载中...</view>
<scroll-view v-else scroll-y class="form-scroll">
<!-- 基础设置 -->
<view class="section">
<view class="section-head">
<view class="section-bar"></view>
<text class="section-title">基础设置</text>
</view>
<view class="switch-row">
<view class="switch-info">
<text class="switch-title">启用能力</text>
<text class="switch-desc">关闭后该能力不可用</text>
</view>
<switch :checked="!!form.enabled" color="#4297F3" data-field="enabled" @change="onFormSwitch" />
</view>
<!-- <view class="switch-row">
<view class="switch-info">
<text class="switch-title">免费使用</text>
<text class="switch-desc">开启后免费,关闭则按套餐收费</text>
</view>
<switch :checked="!!form.free" color="#4297F3" data-field="free" @change="onFormSwitch" />
</view> -->
<view class="switch-row">
<view class="switch-info">
<text class="switch-title">用量预警</text>
<text class="switch-desc">剩余次数不足时提醒</text>
</view>
<switch :checked="!!form.enableWarn" color="#4297F3" data-field="enableWarn" @change="onFormSwitch" />
</view>
<view class="form-field" v-if="form.enableWarn">
<text class="field-label">预警阈值</text>
<text class="field-tip">剩余次数低于该值时将触发预警</text>
<view class="input-box">
<input class="field-input" type="number" v-model="form.warnCount" placeholder="请输入预警阈值" />
<text class="field-unit">次</text>
</view>
</view>
</view>
<!-- 团购设置 -->
<view class="section" v-if="isCouponVerify">
<view class="section-head">
<view class="section-bar"></view>
<text class="section-title">团购设置</text>
</view>
<view class="form-field">
<text class="field-label">抖音门店</text>
<text class="field-tip">抖音生活服务门店 POI ID登录抖音来客后在门店管理查看</text>
<view class="input-box input-box--block">
<input class="field-input" v-model="form.settings.douyinPoiId" placeholder="请输入抖音门店 POI ID" />
</view>
</view>
<view class="form-field">
<text class="field-label">抖音根账户</text>
<text class="field-tip">抖音根账户 ID登录抖音来客后在右上角账户管理查看</text>
<view class="input-box input-box--block">
<input class="field-input" v-model="form.settings.douyinAccountId" placeholder="请输入抖音根账户 ID" />
</view>
</view>
</view>
<!-- 保障设置 -->
<view class="section" v-if="isInsurance">
<view class="section-head">
<view class="section-bar"></view>
<text class="section-title">保障设置</text>
</view>
<view class="switch-row">
<view class="switch-info">
<text class="switch-title">强制投保</text>
<text class="switch-desc">开启后用户下单强制购买保险</text>
</view>
<switch :checked="!!form.settings.insuranceRequired" color="#4297F3" data-field="insuranceRequired" @change="onSettingsSwitch" />
</view>
<view class="form-field">
<text class="field-label">定价</text>
<text class="field-tip">自定义保险价格</text>
<view class="input-box">
<input class="field-input" type="digit" v-model="form.settings.insurancePrice" placeholder="请输入定价" />
<text class="field-unit">元/次</text>
</view>
</view>
</view>
<!-- 实名设置 -->
<view class="section" v-if="isRealName">
<view class="section-head">
<view class="section-bar"></view>
<text class="section-title">实名设置</text>
</view>
<view class="form-field">
<text class="field-label">最低年龄</text>
<text class="field-tip">若无需校验可填 0</text>
<view class="input-box">
<input class="field-input" type="number" v-model="form.settings.minAge" placeholder="请输入最低用车年龄" />
<text class="field-unit">岁</text>
</view>
</view>
<view class="form-field">
<text class="field-label">最高年龄</text>
<text class="field-tip">若无需校验可填 1000</text>
<view class="input-box">
<input class="field-input" type="number" v-model="form.settings.maxAge" placeholder="请输入最高用车年龄" />
<text class="field-unit">岁</text>
</view>
</view>
</view>
<view class="scroll-bottom-space"></view>
</scroll-view>
<view class="footer-bar" v-if="!loading">
<view hover-class="app-tap-hover" class="footer-btn footer-btn--ghost" @tap="goBack">取消</view>
<view hover-class="app-tap-hover" class="footer-btn footer-btn--primary" @tap="submitForm">
<text>保存</text>
</view>
</view>
</view>
</template>
<script>
import { AbilityCode } from '@/common/enums/ability.js'
const DEFAULT_SETTINGS = {
douyinPoiId: '',
douyinAccountId: '',
insuranceRequired: false,
insurancePrice: '',
minAge: '',
maxAge: ''
}
export default {
data() {
return {
bgc: { backgroundColor: '#fff' },
AbilityCode,
id: '',
loading: false,
submitting: false,
form: {
id: null,
enabled: true,
free: false,
enableWarn: true,
warnCount: 100,
abilityCode: '',
abilityName: '',
areaName: '',
settings: { ...DEFAULT_SETTINGS }
}
}
},
computed: {
navTitle() {
return '能力设置'
},
isCouponVerify() {
return this.form.abilityCode === AbilityCode.COUPON_VERIFY
},
isInsurance() {
return this.form.abilityCode === AbilityCode.INSURANCE
},
isRealName() {
return this.form.abilityCode === AbilityCode.REAL_NAME
}
},
onLoad(e) {
this.id = e.id || e.areaAbilityId || ''
if (e.abilityCode) {
this.form.abilityCode = decodeURIComponent(e.abilityCode)
}
if (e.abilityName) {
this.form.abilityName = decodeURIComponent(e.abilityName)
}
if (e.areaName) {
this.form.areaName = decodeURIComponent(e.areaName)
}
this.fetchDetail()
},
methods: {
goBack() {
uni.navigateBack()
},
normalizeForm(data) {
const row = data || {}
const settings = {
...DEFAULT_SETTINGS,
...(row.settings || {})
}
if (settings.insuranceRequired === 1 || settings.insuranceRequired === '1') {
settings.insuranceRequired = true
}
if (settings.insuranceRequired === 0 || settings.insuranceRequired === '0') {
settings.insuranceRequired = false
}
return {
...row,
id: row.id,
enabled: row.enabled === true || row.enabled === 1 || row.enabled === '1',
free: row.free === true || row.free === 1 || row.free === '1',
enableWarn: row.enableWarn === true || row.enableWarn === 1 || row.enableWarn === '1',
warnCount: row.warnCount != null && row.warnCount !== '' ? row.warnCount : 100,
abilityCode: row.abilityCode || this.form.abilityCode || '',
abilityName: row.abilityName || this.form.abilityName || '',
areaName: row.areaName || this.form.areaName || '',
settings
}
},
fetchDetail() {
if (!this.id) {
uni.showToast({ title: '缺少能力ID', icon: 'none' })
return
}
this.loading = true
this.$u
.get(`/bst/areaAbility/${this.id}`)
.then((res) => {
if (res.code == 200 && res.data) {
this.form = this.normalizeForm(res.data)
} else {
uni.showToast({ title: res.msg || '加载失败', icon: 'none' })
}
})
.catch(() => {
uni.showToast({ title: '加载失败', icon: 'none' })
})
.finally(() => {
this.loading = false
})
},
onFormSwitch(e) {
const field = e.currentTarget.dataset.field
if (!field) return
this.$set(this.form, field, !!(e && e.detail && e.detail.value))
},
onSettingsSwitch(e) {
const field = e.currentTarget.dataset.field
if (!field) return
this.$set(this.form.settings, field, !!(e && e.detail && e.detail.value))
},
validateForm() {
if (this.form.enableWarn) {
const n = Number(this.form.warnCount)
if (!Number.isFinite(n) || this.form.warnCount === '' || this.form.warnCount == null) {
uni.showToast({ title: '请输入用量预警阈值', icon: 'none' })
return false
}
}
return true
},
buildSubmitData() {
const data = {
...this.form,
settings: { ...this.form.settings }
}
if (data.warnCount != null && data.warnCount !== '') {
data.warnCount = Number(data.warnCount)
}
return data
},
submitForm() {
if (this.submitting) return
if (!this.validateForm()) return
this.submitting = true
uni.showLoading({ title: '保存中...', mask: true })
this.$u
.put('/bst/areaAbility', this.buildSubmitData())
.then((res) => {
if (res.code == 200) {
uni.showToast({ title: '保存成功', icon: 'success' })
const pages = getCurrentPages()
const prev = pages[pages.length - 2]
if (prev && prev.$vm) {
prev.$vm._needRefresh = true
}
setTimeout(() => uni.navigateBack(), 500)
} else {
uni.showToast({ title: res.msg || '保存失败', icon: 'none' })
}
})
.catch(() => {
uni.showToast({ title: '保存失败', icon: 'none' })
})
.finally(() => {
this.submitting = false
uni.hideLoading()
})
}
}
}
</script>
<style lang="scss">
page {
background: #f6f8fa;
height: 100%;
}
.page {
height: 100vh;
display: flex;
flex-direction: column;
overflow: hidden;
}
.summary-card {
flex-shrink: 0;
margin: 16rpx 24rpx 0;
padding: 20rpx 24rpx;
background: #fff;
border-radius: 12rpx;
border: 1rpx solid #eef2f7;
}
.summary-name {
display: block;
font-size: 30rpx;
font-weight: 600;
color: #111827;
}
.summary-area {
display: block;
margin-top: 6rpx;
font-size: 24rpx;
color: #9ca3af;
}
.page-loading {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
font-size: 28rpx;
color: #9ca3af;
}
.form-scroll {
flex: 1;
height: 0;
padding: 16rpx 24rpx 0;
box-sizing: border-box;
}
.section {
background: #fff;
border-radius: 12rpx;
padding: 20rpx 24rpx;
margin-bottom: 16rpx;
border: 1rpx solid #eef2f7;
}
.section-head {
display: flex;
align-items: center;
margin-bottom: 16rpx;
padding-bottom: 12rpx;
border-bottom: 1rpx solid #f3f4f6;
}
.section-bar {
width: 6rpx;
height: 28rpx;
background: #4297F3;
border-radius: 4rpx;
margin-right: 12rpx;
}
.section-title {
font-size: 30rpx;
font-weight: 600;
color: #111827;
}
.switch-row {
display: flex;
align-items: center;
justify-content: space-between;
padding: 16rpx 0;
border-bottom: 1rpx solid #f9fafb;
&:last-child {
border-bottom: none;
}
}
.switch-info {
flex: 1;
min-width: 0;
padding-right: 16rpx;
}
.switch-title {
display: block;
font-size: 28rpx;
color: #111827;
font-weight: 500;
}
.switch-desc {
display: block;
margin-top: 4rpx;
font-size: 22rpx;
color: #9ca3af;
line-height: 1.4;
}
.form-field {
padding: 12rpx 0 4rpx;
}
.field-label {
display: block;
font-size: 28rpx;
font-weight: 500;
color: #374151;
}
.field-tip {
display: block;
margin-top: 6rpx;
font-size: 22rpx;
color: #9ca3af;
line-height: 1.45;
}
.input-box {
margin-top: 12rpx;
display: flex;
align-items: center;
background: #f8fafc;
border: 1rpx solid #e5e7eb;
border-radius: 10rpx;
padding: 0 20rpx;
min-height: 72rpx;
}
.input-box--block {
padding: 12rpx 20rpx;
}
.field-input {
flex: 1;
font-size: 28rpx;
color: #111827;
min-height: 48rpx;
}
.field-unit {
flex-shrink: 0;
margin-left: 12rpx;
font-size: 26rpx;
color: #6b7280;
}
.scroll-bottom-space {
height: 140rpx;
}
.footer-bar {
flex-shrink: 0;
display: flex;
gap: 20rpx;
padding: 16rpx 24rpx calc(16rpx + env(safe-area-inset-bottom));
background: #fff;
box-shadow: 0 -4rpx 16rpx rgba(0, 0, 0, 0.06);
}
.footer-btn {
flex: 1;
height: 84rpx;
line-height: 84rpx;
text-align: center;
border-radius: 42rpx;
font-size: 30rpx;
font-weight: 600;
}
.footer-btn--ghost {
background: #f3f4f6;
color: #374151;
}
.footer-btn--primary {
background: linear-gradient(135deg, #4297F3 0%, #2b76e5 100%);
color: #fff;
}
</style>