chuangte_bike_newxcx/page_shanghu/guanli/area_ability_add.vue
2026-06-02 10:34:22 +08:00

451 lines
10 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" back-icon-color="#111827" title-color="#111827" title-size="34" height="44"></u-navbar>
<view class="form-container">
<view class="area-tip" v-if="areaName">
<text class="area-tip-label">当前运营区</text>
<text class="area-tip-value">{{ areaName }}</text>
</view>
<view class="form-item">
<view class="form-label">
<text class="label-text">拓展能力</text>
<text class="label-hint">必选</text>
</view>
<view hover-class="app-tap-hover" class="form-select" @click="openPicker">
<view class="select-main" v-if="selectedAbility">
<image class="select-icon" :src="abilityIcon(selectedAbility)" mode="aspectFill" v-if="abilityIcon(selectedAbility)"></image>
<view class="select-icon select-icon--placeholder" v-else>
<text>{{ abilityName(selectedAbility).slice(0, 1) }}</text>
</view>
<view class="select-text">
<text class="select-name">{{ abilityName(selectedAbility) }}</text>
<text class="select-desc">{{ abilityDesc(selectedAbility) }}</text>
</view>
</view>
<text v-else class="placeholder">请选择拓展能力</text>
<u-icon name="arrow-right" size="28" color="#9ca3af"></u-icon>
</view>
</view>
</view>
<view class="ability-popup" v-if="showPicker">
<view hover-class="app-tap-hover" class="popup-mask" @click="closePicker"></view>
<view class="popup-content">
<view class="popup-header">
<text class="popup-title">选择拓展能力</text>
<text hover-class="app-tap-hover" class="popup-close" @click="closePicker">×</text>
</view>
<view class="popup-loading" v-if="listLoading">加载中...</view>
<scroll-view v-else scroll-y class="ability-list">
<view
hover-class="app-tap-hover"
class="ability-row"
:class="{ 'ability-row--active': isAbilitySelected(item) }"
v-for="item in abilityOptions"
:key="item.id"
@click="onSelectAbility(item)"
>
<image class="ability-row-icon" :src="abilityIcon(item)" mode="aspectFill" v-if="abilityIcon(item)"></image>
<view class="ability-row-icon ability-row-icon--placeholder" v-else>
<text>{{ abilityName(item).slice(0, 1) }}</text>
</view>
<view class="ability-row-body">
<text class="ability-row-name">{{ abilityName(item) }}</text>
<text class="ability-row-desc">{{ abilityDesc(item) }}</text>
</view>
<text class="ability-row-code">{{ abilityCode(item) }}</text>
</view>
<view class="popup-empty" v-if="!abilityOptions.length">暂无可用能力</view>
</scroll-view>
</view>
</view>
<view class="page-footer">
<view
hover-class="app-tap-hover"
class="footer-btn"
:class="{ 'footer-btn--disabled': !selectedAbility || submitting }"
@click="confirmAdd"
>确定添加</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
bgc: { backgroundColor: '#fff' },
areaId: '',
areaName: '',
submitting: false,
showPicker: false,
listLoading: false,
abilityOptions: [],
selectedAbility: null
}
},
onLoad(e) {
this.areaId = e.areaId || ''
this.areaName = e.areaName ? decodeURIComponent(e.areaName) : ''
},
methods: {
abilityName(item) {
return item.name || item.abilityName || '--'
},
abilityCode(item) {
return item.code || item.abilityCode || ''
},
abilityDesc(item) {
return item.description || item.abilityDescription || ''
},
abilityIcon(item) {
return item.icon || item.abilityIcon || ''
},
normalizeRows(res) {
if (res.code != 200) return []
return res.rows != null ? res.rows : (Array.isArray(res.data) ? res.data : [])
},
fetchAbilityList() {
this.listLoading = true
return this.$u
.get('/bst/ability/list', { pageNum: 1, pageSize: 99 })
.then((res) => {
this.abilityOptions = this.normalizeRows(res)
if (res.code != 200) {
uni.showToast({ title: res.msg || '加载失败', icon: 'none' })
}
})
.catch(() => {
this.abilityOptions = []
uni.showToast({ title: '加载失败', icon: 'none' })
})
.finally(() => {
this.listLoading = false
})
},
openPicker() {
if (!this.areaId) {
uni.showToast({ title: '缺少运营区信息', icon: 'none' })
return
}
this.showPicker = true
if (!this.abilityOptions.length) {
this.fetchAbilityList()
}
},
closePicker() {
if (this.submitting) return
this.showPicker = false
},
isAbilitySelected(item) {
return this.selectedAbility && item && this.selectedAbility.id === item.id
},
onSelectAbility(item) {
if (this.submitting || !item || item.id == null) return
this.selectedAbility = item
this.showPicker = false
},
confirmAdd() {
if (this.submitting) return
if (!this.areaId) {
uni.showToast({ title: '缺少运营区信息', icon: 'none' })
return
}
if (!this.selectedAbility || this.selectedAbility.id == null) {
uni.showToast({ title: '请先选择拓展能力', icon: 'none' })
return
}
this.submitAbility()
},
submitAbility() {
const item = this.selectedAbility
if (!item || item.id == null) return
this.submitting = true
uni.showLoading({ title: '提交中...', mask: true })
this.$u
.post('/bst/areaAbility', {
areaId: this.areaId,
abilityId: item.id
})
.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;
}
.page {
min-height: 100vh;
padding-bottom: calc(140rpx + env(safe-area-inset-bottom));
box-sizing: border-box;
}
.form-container {
padding: 24rpx;
}
.page-footer {
position: fixed;
left: 0;
right: 0;
bottom: 0;
padding: 20rpx 24rpx calc(20rpx + env(safe-area-inset-bottom));
background: #fff;
box-shadow: 0 -4rpx 24rpx rgba(0, 0, 0, 0.06);
z-index: 10;
}
.footer-btn {
height: 88rpx;
line-height: 88rpx;
text-align: center;
font-size: 32rpx;
font-weight: 600;
color: #fff;
background: #4297F3;
border-radius: 44rpx;
}
.footer-btn--disabled {
background: #cbd5e1;
color: #f8fafc;
}
.area-tip {
background: #fff;
border-radius: 16rpx;
padding: 24rpx 28rpx;
margin-bottom: 24rpx;
display: flex;
align-items: center;
}
.area-tip-label {
font-size: 26rpx;
color: #9ca3af;
margin-right: 16rpx;
}
.area-tip-value {
font-size: 28rpx;
color: #4297F3;
font-weight: 500;
}
.form-item {
background: #fff;
border-radius: 16rpx;
padding: 28rpx;
}
.form-label {
display: flex;
align-items: center;
margin-bottom: 20rpx;
}
.label-text {
font-size: 30rpx;
font-weight: 600;
color: #111827;
}
.label-hint {
margin-left: 12rpx;
font-size: 24rpx;
color: #ef4444;
}
.form-select {
display: flex;
align-items: center;
justify-content: space-between;
padding: 24rpx;
background: #f8fafc;
border-radius: 12rpx;
border: 1rpx solid #e5e7eb;
min-height: 88rpx;
}
.placeholder {
font-size: 28rpx;
color: #9ca3af;
flex: 1;
}
.select-main {
flex: 1;
min-width: 0;
display: flex;
align-items: flex-start;
margin-right: 16rpx;
}
.select-icon {
width: 72rpx;
height: 72rpx;
border-radius: 12rpx;
flex-shrink: 0;
background: #f3f4f6;
}
.select-icon--placeholder {
display: flex;
align-items: center;
justify-content: center;
background: linear-gradient(135deg, #93c5fd 0%, #4297F3 100%);
text {
font-size: 32rpx;
font-weight: 700;
color: #fff;
}
}
.select-text {
flex: 1;
min-width: 0;
margin-left: 20rpx;
}
.select-name {
display: block;
font-size: 30rpx;
font-weight: 600;
color: #111827;
line-height: 1.35;
}
.select-desc {
display: block;
margin-top: 8rpx;
font-size: 24rpx;
color: #6b7280;
line-height: 1.5;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
.ability-popup {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 1000;
}
.popup-mask {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.45);
}
.popup-content {
position: absolute;
left: 0;
right: 0;
bottom: 0;
background: #fff;
border-radius: 32rpx 32rpx 0 0;
max-height: 78vh;
display: flex;
flex-direction: column;
padding-bottom: env(safe-area-inset-bottom);
}
.popup-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 32rpx 32rpx 24rpx;
border-bottom: 1rpx solid #f0f2f5;
flex-shrink: 0;
}
.popup-title {
font-size: 34rpx;
font-weight: 600;
color: #111827;
}
.popup-close {
font-size: 48rpx;
color: #9ca3af;
line-height: 1;
padding: 0 8rpx;
}
.popup-loading,
.popup-empty {
padding: 80rpx 0;
text-align: center;
font-size: 28rpx;
color: #9ca3af;
}
.ability-list {
flex: 1;
min-height: 200rpx;
max-height: calc(78vh - 120rpx);
}
.ability-row {
display: flex;
align-items: flex-start;
padding: 28rpx 32rpx;
border-bottom: 1rpx solid #f3f4f6;
}
.ability-row--active {
background: #eff6ff;
}
.ability-row-icon {
width: 80rpx;
height: 80rpx;
border-radius: 16rpx;
flex-shrink: 0;
background: #f3f4f6;
}
.ability-row-icon--placeholder {
display: flex;
align-items: center;
justify-content: center;
background: linear-gradient(135deg, #fde68a 0%, #f59e0b 100%);
text {
font-size: 34rpx;
font-weight: 700;
color: #fff;
}
}
.ability-row-body {
flex: 1;
min-width: 0;
margin: 0 20rpx;
}
.ability-row-name {
display: block;
font-size: 32rpx;
font-weight: 600;
color: #111827;
line-height: 1.35;
}
.ability-row-desc {
display: block;
margin-top: 10rpx;
font-size: 26rpx;
color: #6b7280;
line-height: 1.55;
}
.ability-row-code {
flex-shrink: 0;
font-size: 24rpx;
color: #9ca3af;
max-width: 180rpx;
text-align: right;
word-break: break-all;
padding-top: 6rpx;
}
</style>