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

398 lines
9.2 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"
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>
</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
},
onSelectAbility(item) {
if (this.submitting || !item || item.id == null) return
this.selectedAbility = item
this.submitAbility(item)
},
submitAbility(item) {
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) {
this.showPicker = false
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 {
this.selectedAbility = null
uni.showToast({ title: res.msg || '新增失败', icon: 'none' })
}
})
.catch(() => {
this.selectedAbility = null
uni.showToast({ title: '新增失败', icon: 'none' })
})
.finally(() => {
this.submitting = false
uni.hideLoading()
})
}
}
}
</script>
<style lang="scss">
page {
background: #f6f8fa;
}
.page {
min-height: 100vh;
}
.form-container {
padding: 24rpx;
}
.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-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>