<template>
|
<view class="action-buttons-container">
|
<view class="action-buttons-row">
|
<view
|
v-for="btn in processedButtons"
|
:key="btn.dictValue"
|
class="action-btn-wrapper"
|
>
|
<text v-if="btn.showBadge" class="badge">{{ btn.count }}</text>
|
<button
|
class="action-btn"
|
:class="{
|
selected: selected == btn.dictValue,
|
disabled: btn.isDisabled,
|
success: btn.isSuccess
|
}"
|
@click="$emit('select', btn)"
|
>
|
{{ btn.dictLabel }}
|
</button>
|
</view>
|
</view>
|
</view>
|
</template>
|
|
<script>
|
// Object.freeze 阻止 Vue 对静态数组添加响应式
|
const SUCCESS_TYPES = Object.freeze(['0', '1', '4', '5', '8', '100'])
|
const BADGE_EXCLUDE = Object.freeze(['0', '1', '4', '5', '8', '100'])
|
|
export default {
|
name: 'ActionButtons',
|
props: {
|
buttons: { type: Array, default: () => [] },
|
selected: { type: [String, Number], default: '' },
|
mapList: { type: Object, default: () => ({}) }
|
},
|
computed: {
|
/**
|
* 缓存按钮的 disabled / success / badge 状态
|
* 仅当 buttons 或 mapList 变化时重算(不依赖 selected)
|
* selected 的比较留在模板里,由 Vue 渲染时直接对比 prop
|
*/
|
processedButtons() {
|
const ml = this.mapList
|
const hasBodyCheck = ml['1'] && ml['1'].length > 0
|
const hasDeparture = ml['0'] && ml['0'].length > 0
|
|
return this.buttons.map(btn => {
|
const dv = String(btn.dictValue)
|
const count = btn.count || 0
|
const isSuccessType = SUCCESS_TYPES.includes(dv)
|
|
// 内联 isDisabled 逻辑,避免方法调用开销
|
let disabled = false
|
if (btn.dictValue == 1) {
|
disabled = count > 0
|
} else if (btn.dictValue == 0) {
|
disabled = !hasBodyCheck || count > 0
|
} else {
|
disabled = !hasDeparture || (isSuccessType && count > 0)
|
}
|
|
return {
|
dictValue: btn.dictValue,
|
dictLabel: btn.dictLabel,
|
count,
|
isDisabled: disabled,
|
isSuccess: isSuccessType && count > 0,
|
showBadge: !isSuccessType && count > 0
|
}
|
})
|
}
|
}
|
}
|
</script>
|
|
<style scoped>
|
.action-buttons-container {
|
margin-bottom: 30rpx;
|
}
|
|
.action-buttons-row {
|
display: grid;
|
grid-template-columns: repeat(auto-fill, 150rpx);
|
gap: 20rpx;
|
}
|
|
.action-btn-wrapper {
|
position: relative;
|
}
|
|
.action-btn {
|
width: 150rpx;
|
height: 80rpx;
|
line-height: 80rpx;
|
text-align: center;
|
font-size: 25rpx;
|
border-radius: 8rpx;
|
background-color: #fff;
|
border: 1rpx solid #eee;
|
color: #333;
|
display: flex;
|
justify-content: center;
|
align-items: center;
|
overflow: hidden;
|
white-space: nowrap;
|
text-overflow: ellipsis;
|
}
|
|
.action-btn.selected {
|
background-color: #e8f4f8;
|
border-color: #4285f4;
|
color: #4285f4;
|
font-weight: 500;
|
}
|
|
.action-btn.disabled {
|
background-color: #f7f7f7;
|
color: rgba(0, 0, 0, 0.3);
|
}
|
|
.action-btn.success {
|
background-color: rgba(202, 249, 130, 0.52);
|
color: rgba(0, 0, 0, 0.3);
|
}
|
|
.action-btn::after {
|
border: none;
|
}
|
|
.badge {
|
position: absolute;
|
right: 0;
|
top: -10rpx;
|
font-size: 20rpx;
|
background: #4285f4;
|
color: #fff;
|
padding: 4rpx 8rpx;
|
border-radius: 50%;
|
z-index: 10;
|
min-width: 32rpx;
|
text-align: center;
|
}
|
</style>
|