251 lines
5.9 KiB
Vue
251 lines
5.9 KiB
Vue
|
|
<template>
|
||
|
|
<u-popup v-model="innerVisible" mode="bottom" border-radius="24" safe-area-inset-bottom>
|
||
|
|
<view class="popup-panel">
|
||
|
|
<view class="popup-head">
|
||
|
|
<text class="popup-title">更换车辆</text>
|
||
|
|
<text hover-class="app-tap-hover" class="popup-close" @click="close">关闭</text>
|
||
|
|
</view>
|
||
|
|
<view class="search-bar">
|
||
|
|
<u-icon name="search" color="#9AA7B8" size="32"></u-icon>
|
||
|
|
<input v-model="keyword" placeholder="搜索 SN / 车牌" placeholder-style="color:#C7CDD3" @input="filterDevices" />
|
||
|
|
</view>
|
||
|
|
<scroll-view scroll-y class="popup-scroll">
|
||
|
|
<view
|
||
|
|
v-for="(line, idx) in filteredDeviceList"
|
||
|
|
:key="line.rowDevId"
|
||
|
|
class="popup-item"
|
||
|
|
:class="{ active: line.picked }"
|
||
|
|
hover-class="app-tap-hover"
|
||
|
|
:data-idx="idx"
|
||
|
|
@click="onDeviceItemTap"
|
||
|
|
>
|
||
|
|
<view class="dev-lines">
|
||
|
|
<text class="popup-item-tit">{{ line.rowTitle }}</text>
|
||
|
|
<text class="dev-sub">{{ line.rowSub }}</text>
|
||
|
|
<text class="dev-meta">{{ line.rowMeta }}</text>
|
||
|
|
</view>
|
||
|
|
<u-icon v-if="line.picked" name="checkbox-mark" color="#4C97E7" size="36"></u-icon>
|
||
|
|
</view>
|
||
|
|
<view v-if="showLoading" class="popup-empty">车辆加载中...</view>
|
||
|
|
<view v-else-if="showEmpty" class="popup-empty">暂无待骑行车辆</view>
|
||
|
|
</scroll-view>
|
||
|
|
</view>
|
||
|
|
</u-popup>
|
||
|
|
</template>
|
||
|
|
|
||
|
|
<script>
|
||
|
|
export default {
|
||
|
|
name: 'DeviceSelectorPopup',
|
||
|
|
props: {
|
||
|
|
visible: {
|
||
|
|
type: Boolean,
|
||
|
|
default: false
|
||
|
|
},
|
||
|
|
areaId: {
|
||
|
|
type: [String, Number],
|
||
|
|
default: ''
|
||
|
|
},
|
||
|
|
currentDeviceId: {
|
||
|
|
type: [String, Number],
|
||
|
|
default: ''
|
||
|
|
}
|
||
|
|
},
|
||
|
|
data() {
|
||
|
|
return {
|
||
|
|
deviceList: [],
|
||
|
|
filteredDeviceList: [],
|
||
|
|
keyword: '',
|
||
|
|
loading: false,
|
||
|
|
loadedAreaId: ''
|
||
|
|
}
|
||
|
|
},
|
||
|
|
computed: {
|
||
|
|
innerVisible: {
|
||
|
|
get() {
|
||
|
|
return this.visible
|
||
|
|
},
|
||
|
|
set(val) {
|
||
|
|
this.$emit('update:visible', val)
|
||
|
|
}
|
||
|
|
},
|
||
|
|
showLoading() {
|
||
|
|
return this.loading && this.filteredDeviceList.length === 0
|
||
|
|
},
|
||
|
|
showEmpty() {
|
||
|
|
return !this.loading && this.filteredDeviceList.length === 0
|
||
|
|
}
|
||
|
|
},
|
||
|
|
watch: {
|
||
|
|
visible(val) {
|
||
|
|
if (val) {
|
||
|
|
this.open()
|
||
|
|
}
|
||
|
|
},
|
||
|
|
currentDeviceId() {
|
||
|
|
this.syncPicked()
|
||
|
|
}
|
||
|
|
},
|
||
|
|
methods: {
|
||
|
|
open() {
|
||
|
|
this.keyword = ''
|
||
|
|
if (!this.areaId) {
|
||
|
|
uni.showToast({ title: '缺少运营区,无法选择车辆', icon: 'none' })
|
||
|
|
return
|
||
|
|
}
|
||
|
|
if (!this.deviceList.length || String(this.loadedAreaId) !== String(this.areaId)) {
|
||
|
|
this.loadDeviceList()
|
||
|
|
} else {
|
||
|
|
this.filteredDeviceList = this.deviceList
|
||
|
|
this.syncPicked()
|
||
|
|
}
|
||
|
|
},
|
||
|
|
close() {
|
||
|
|
this.innerVisible = false
|
||
|
|
},
|
||
|
|
loadDeviceList() {
|
||
|
|
this.loading = true
|
||
|
|
const areaId = encodeURIComponent(String(this.areaId))
|
||
|
|
this.$u
|
||
|
|
.get(`/bst/device/all?areaId=${areaId}&status=1&supportLocation=true`)
|
||
|
|
.then((res) => {
|
||
|
|
if (res.code === 200) {
|
||
|
|
const arr = Array.isArray(res.data) ? res.data : []
|
||
|
|
this.deviceList = this.normalizeDeviceList(arr)
|
||
|
|
this.filteredDeviceList = this.deviceList
|
||
|
|
this.loadedAreaId = this.areaId
|
||
|
|
this.syncPicked()
|
||
|
|
} else {
|
||
|
|
uni.showToast({ title: res.msg || '获取车辆失败', icon: 'none' })
|
||
|
|
}
|
||
|
|
})
|
||
|
|
.catch(() => {
|
||
|
|
uni.showToast({ title: '获取车辆失败', icon: 'none' })
|
||
|
|
})
|
||
|
|
.finally(() => {
|
||
|
|
this.loading = false
|
||
|
|
})
|
||
|
|
},
|
||
|
|
normalizeDeviceList(arr) {
|
||
|
|
return (arr || []).map((d, i) => {
|
||
|
|
const did = d.id != null ? d.id : d.deviceId
|
||
|
|
const sn = d.sn || d.deviceSn || ''
|
||
|
|
const plate = d.vehicleNum || ''
|
||
|
|
const title = plate ? plate + (sn ? ' · ' + sn : '') : sn || '车辆' + (i + 1)
|
||
|
|
const sub = (d.modelName || '') + (d.areaName ? ' · ' + d.areaName : '')
|
||
|
|
const meta = `电量:${d.remainingPower == null ? '--' : d.remainingPower + '%'}`
|
||
|
|
return {
|
||
|
|
rowDevId: did,
|
||
|
|
rowTitle: title,
|
||
|
|
rowSub: sub || '暂无车型信息',
|
||
|
|
rowMeta: meta,
|
||
|
|
rowRaw: d,
|
||
|
|
picked: false
|
||
|
|
}
|
||
|
|
}).filter((x) => x.rowDevId != null && x.rowDevId !== '')
|
||
|
|
},
|
||
|
|
syncPicked() {
|
||
|
|
const cur = this.currentDeviceId
|
||
|
|
this.deviceList.forEach((x) => {
|
||
|
|
x.picked = cur !== '' && cur !== null && String(x.rowDevId) === String(cur)
|
||
|
|
})
|
||
|
|
},
|
||
|
|
filterDevices() {
|
||
|
|
const k = (this.keyword || '').trim().toLowerCase()
|
||
|
|
if (!k) {
|
||
|
|
this.filteredDeviceList = this.deviceList
|
||
|
|
this.syncPicked()
|
||
|
|
return
|
||
|
|
}
|
||
|
|
this.filteredDeviceList = this.deviceList.filter((it) => {
|
||
|
|
const r = it.rowRaw || {}
|
||
|
|
const sn = String(r.sn || r.deviceSn || '').toLowerCase()
|
||
|
|
const plate = String(r.vehicleNum || '').toLowerCase()
|
||
|
|
const model = String(r.modelName || '').toLowerCase()
|
||
|
|
return sn.includes(k) || plate.includes(k) || model.includes(k)
|
||
|
|
})
|
||
|
|
this.syncPicked()
|
||
|
|
},
|
||
|
|
onDeviceItemTap(e) {
|
||
|
|
const idx = parseInt(e.currentTarget.dataset.idx, 10)
|
||
|
|
if (Number.isNaN(idx)) return
|
||
|
|
const item = this.filteredDeviceList[idx]
|
||
|
|
if (!item) return
|
||
|
|
this.$emit('select', item.rowRaw)
|
||
|
|
this.close()
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
</script>
|
||
|
|
|
||
|
|
<style lang="scss" scoped>
|
||
|
|
@import './agent-order-theme.scss';
|
||
|
|
|
||
|
|
.popup-panel {
|
||
|
|
@include ao-popup-panel;
|
||
|
|
}
|
||
|
|
|
||
|
|
.popup-head {
|
||
|
|
@include ao-popup-head;
|
||
|
|
height: auto;
|
||
|
|
}
|
||
|
|
|
||
|
|
.popup-title {
|
||
|
|
@include ao-section-title;
|
||
|
|
font-weight: 700;
|
||
|
|
}
|
||
|
|
|
||
|
|
.popup-close {
|
||
|
|
font-size: 28rpx;
|
||
|
|
color: $ao-primary;
|
||
|
|
}
|
||
|
|
|
||
|
|
.search-bar {
|
||
|
|
@include ao-field-box;
|
||
|
|
height: 76rpx;
|
||
|
|
margin: 20rpx 0;
|
||
|
|
padding: 0 22rpx;
|
||
|
|
|
||
|
|
input {
|
||
|
|
flex: 1;
|
||
|
|
margin-left: 12rpx;
|
||
|
|
font-size: 28rpx;
|
||
|
|
color: $ao-text;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
.popup-scroll {
|
||
|
|
max-height: 720rpx;
|
||
|
|
}
|
||
|
|
|
||
|
|
.popup-item {
|
||
|
|
@include ao-option-item;
|
||
|
|
justify-content: space-between;
|
||
|
|
}
|
||
|
|
|
||
|
|
.dev-lines {
|
||
|
|
flex: 1;
|
||
|
|
min-width: 0;
|
||
|
|
display: flex;
|
||
|
|
flex-direction: column;
|
||
|
|
}
|
||
|
|
|
||
|
|
.popup-item-tit {
|
||
|
|
font-size: 30rpx;
|
||
|
|
font-weight: 600;
|
||
|
|
color: $ao-text;
|
||
|
|
line-height: 1.4;
|
||
|
|
}
|
||
|
|
|
||
|
|
.dev-sub,
|
||
|
|
.dev-meta {
|
||
|
|
font-size: 24rpx;
|
||
|
|
color: $ao-text-secondary;
|
||
|
|
margin-top: 8rpx;
|
||
|
|
}
|
||
|
|
|
||
|
|
.popup-empty {
|
||
|
|
@include ao-empty-tip;
|
||
|
|
padding: 48rpx 0;
|
||
|
|
}
|
||
|
|
</style>
|