chuangte_bike_newxcx/page_shanghu/guanli/area_ability_setting.vue

473 lines
13 KiB
Vue
Raw Normal View History

2026-05-29 17:26:45 +08:00
<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>