<template>
|
<view class="hospital-selector">
|
<view class="form-item">
|
<view class="form-label" :class="{ required: required }" v-if="showLabel">{{ label }}</view>
|
<view class="hospital-search-container">
|
<input
|
class="form-input"
|
:placeholder="placeholder"
|
v-model="searchKeyword"
|
@input="onSearch"
|
@focus="onFocus"
|
/>
|
<view class="search-results" v-if="showResults && searchResults.length > 0">
|
<view
|
class="search-result-item"
|
v-for="hospital in searchResults"
|
:key="hospital.hospId"
|
@click="selectHospital(hospital)"
|
>
|
<view class="hospital-name">
|
{{ hospital.hospName }}
|
<text class="hospital-short" v-if="hospital.hospShort">{{ hospital.hospShort }}</text>
|
</view>
|
<view class="hospital-address">{{ buildFullAddress(hospital) }}</view>
|
</view>
|
</view>
|
</view>
|
</view>
|
|
<view class="form-item" v-if="showDepartment">
|
<view class="form-label" :class="{ required: departmentRequired }">科室</view>
|
<picker
|
v-if="selectedHospitalName !== '家中' && departmentOptions.length > 0"
|
mode="selector"
|
:range="departmentOptions"
|
range-key="text"
|
@change="onDepartmentChange"
|
>
|
<view class="form-input picker-input">
|
{{ departmentValue || '请选择科室' }}
|
<uni-icons type="arrowright" size="16" color="#999"></uni-icons>
|
</view>
|
</picker>
|
<input
|
v-else-if="selectedHospitalName !== '家中'"
|
class="form-input"
|
placeholder="请输入科室"
|
:value="departmentValue"
|
@input="onDepartmentInput"
|
/>
|
<view v-else class="form-input picker-input disabled">
|
其它
|
</view>
|
</view>
|
|
<view class="form-item" v-if="showBedNumber">
|
<view class="form-label">床号</view>
|
<input
|
class="form-input"
|
placeholder="请输入床号"
|
:value="bedNumberValue"
|
@input="onBedNumberInput"
|
/>
|
</view>
|
|
<view class="form-item">
|
<view class="form-label" :class="{ required: required }">{{ addressLabel }}</view>
|
<view class="address-input-container" v-if="selectedHospitalName === '家中'">
|
<input
|
class="form-input"
|
placeholder="请输入详细地址"
|
:value="addressValue"
|
@input="onAddressInput"
|
@focus="onAddressFocus"
|
/>
|
<view class="address-suggestions" v-if="showAddressSuggestions && addressSuggestions.length > 0">
|
<view
|
class="address-suggestion-item"
|
v-for="(item, index) in addressSuggestions"
|
:key="index"
|
@click="selectAddressSuggestion(item)"
|
>
|
<view class="suggestion-name">{{ item.name }}</view>
|
<view class="suggestion-address">{{ item.district }}{{ item.address }}</view>
|
</view>
|
</view>
|
</view>
|
<view v-else class="form-input picker-input disabled">
|
{{ addressValue || '选择医院后自动填充' }}
|
</view>
|
</view>
|
</view>
|
</template>
|
|
<script>
|
import { searchHospitals } from "@/api/hospital"
|
import { baiduPlaceSuggestion } from "@/api/map"
|
|
export default {
|
name: 'HospitalSelector',
|
props: {
|
// 标签文本
|
label: {
|
type: String,
|
default: '医院名称'
|
},
|
// 是否显示标签
|
showLabel: {
|
type: Boolean,
|
default: true
|
},
|
// 是否必填
|
required: {
|
type: Boolean,
|
default: false
|
},
|
// 地址标签
|
addressLabel: {
|
type: String,
|
default: '医院地址'
|
},
|
// 占位符
|
placeholder: {
|
type: String,
|
default: '请输入医院名称或地址搜索'
|
},
|
// 当前选中的医院信息
|
value: {
|
type: Object,
|
default: () => ({
|
id: null,
|
name: '',
|
department: '',
|
departmentId: null,
|
bedNumber: '',
|
address: ''
|
})
|
},
|
// 是否显示科室
|
showDepartment: {
|
type: Boolean,
|
default: true
|
},
|
// 科室是否必填
|
departmentRequired: {
|
type: Boolean,
|
default: false
|
},
|
// 科室选项列表(用于 picker)
|
departmentOptions: {
|
type: Array,
|
default: () => []
|
},
|
// 是否显示床号
|
showBedNumber: {
|
type: Boolean,
|
default: true
|
},
|
// 部门ID(用于区域过滤)
|
deptId: {
|
type: [Number, String],
|
default: null
|
},
|
// 当前区域(用于地址搜索)
|
region: {
|
type: String,
|
default: '广州'
|
}
|
},
|
data() {
|
return {
|
searchKeyword: '',
|
searchResults: [],
|
showResults: false,
|
searchTimer: null,
|
selectedHospitalName: '',
|
// 地址建议相关
|
addressSuggestions: [],
|
showAddressSuggestions: false,
|
addressSearchTimer: null,
|
// 默认医院列表(用于首次加载)
|
defaultHospitals: [],
|
hasLoadedDefault: false // 标记是否已加载过默认列表
|
}
|
},
|
computed: {
|
addressValue() {
|
return this.value.address || ''
|
},
|
departmentValue() {
|
return this.value.department || ''
|
},
|
bedNumberValue() {
|
return this.value.bedNumber || ''
|
}
|
},
|
watch: {
|
'value.name': {
|
immediate: true,
|
handler(newVal) {
|
if (newVal) {
|
this.searchKeyword = newVal
|
this.selectedHospitalName = newVal
|
}
|
}
|
},
|
// 监听 deptId 变化,清除已加载的默认列表(但不自动加载)
|
deptId(newVal, oldVal) {
|
if (newVal !== oldVal && newVal) {
|
this.hasLoadedDefault = false
|
this.defaultHospitals = []
|
// 不自动加载,等待用户点击输入框时再加载
|
}
|
}
|
},
|
methods: {
|
// 搜索输入监听
|
onSearch(e) {
|
const keyword = e.detail.value
|
this.searchKeyword = keyword
|
|
if (this.searchTimer) {
|
clearTimeout(this.searchTimer)
|
}
|
|
if (!keyword || keyword.trim() === '') {
|
// 关键词为空时,显示默认医院列表
|
if (this.defaultHospitals.length > 0) {
|
this.searchResults = this.defaultHospitals
|
this.showResults = true
|
} else {
|
this.searchResults = []
|
this.showResults = false
|
}
|
return
|
}
|
|
// 防抖处理
|
this.searchTimer = setTimeout(() => {
|
this.searchHospital(keyword)
|
}, 300)
|
},
|
|
// 搜索医院
|
searchHospital(keyword) {
|
searchHospitals(keyword, this.deptId).then(response => {
|
this.searchResults = response.data || []
|
this.showResults = true
|
}).catch(error => {
|
console.error('搜索医院失败:', error)
|
this.searchResults = []
|
})
|
},
|
|
// 输入框获得焦点
|
onFocus() {
|
// 如果已经有搜索结果,直接显示
|
if (this.searchResults.length > 0) {
|
this.showResults = true
|
return
|
}
|
|
// 如果还没有加载过默认医院列表,则加载
|
if (!this.hasLoadedDefault) {
|
this.loadDefaultHospitals()
|
} else if (this.defaultHospitals.length > 0) {
|
// 如果已经加载过,直接显示默认列表
|
this.searchResults = this.defaultHospitals
|
this.showResults = true
|
}
|
},
|
|
// 加载默认医院列表
|
loadDefaultHospitals() {
|
searchHospitals('', this.deptId).then(response => {
|
this.defaultHospitals = response.data || []
|
this.searchResults = this.defaultHospitals
|
this.showResults = true
|
this.hasLoadedDefault = true
|
console.log('加载默认医院列表,数量:', this.defaultHospitals.length)
|
}).catch(error => {
|
console.error('加载默认医院列表失败:', error)
|
this.defaultHospitals = []
|
})
|
},
|
|
// 选择医院
|
selectHospital(hospital) {
|
this.selectedHospitalName = hospital.hospName
|
this.searchKeyword = hospital.hospName
|
|
const hospitalData = {
|
id: hospital.hospId,
|
name: hospital.hospName,
|
department: this.value.department || '',
|
departmentId: this.value.departmentId || null,
|
bedNumber: this.value.bedNumber || '',
|
address: hospital.hospName === '家中' ? '' : this.buildFullAddress(hospital)
|
}
|
|
// 如果选择的是"家中",科室设置为"其它"
|
if (hospital.hospName === '家中') {
|
hospitalData.department = '其它'
|
hospitalData.departmentId = null
|
}
|
|
this.showResults = false
|
this.searchResults = []
|
|
// 触发更新事件
|
this.$emit('input', hospitalData)
|
this.$emit('change', hospitalData)
|
},
|
|
|
|
// 合并医院地址(省 + 市 + 区 + 详细地址)
|
buildFullAddress(hospital) {
|
const parts = []
|
if (hospital.hopsProvince) {
|
parts.push(hospital.hopsProvince)
|
}
|
if (hospital.hopsCity) {
|
parts.push(hospital.hopsCity)
|
}
|
if (hospital.hopsArea) {
|
parts.push(hospital.hopsArea)
|
}
|
if (hospital.hospAddress) {
|
parts.push(hospital.hospAddress)
|
}
|
return parts.join('')
|
},
|
|
// 地址输入(仅在选择“家中”时可用)
|
onAddressInput(e) {
|
const address = e.detail.value
|
this.$emit('input', {
|
...this.value,
|
address: address
|
})
|
this.$emit('address-change', address)
|
|
// 防抖处理地址搜索
|
if (this.addressSearchTimer) {
|
clearTimeout(this.addressSearchTimer)
|
}
|
|
if (!address || address.trim() === '') {
|
this.addressSuggestions = []
|
this.showAddressSuggestions = false
|
return
|
}
|
|
this.addressSearchTimer = setTimeout(() => {
|
this.searchAddress(address)
|
}, 300)
|
},
|
|
// 搜索地址建议
|
searchAddress(query) {
|
baiduPlaceSuggestion(query, this.region).then(response => {
|
if (response.code === 200 && response.data) {
|
this.addressSuggestions = response.data
|
this.showAddressSuggestions = true
|
} else {
|
this.addressSuggestions = []
|
this.showAddressSuggestions = false
|
}
|
}).catch(error => {
|
console.error('搜索地址失败:', error)
|
this.addressSuggestions = []
|
this.showAddressSuggestions = false
|
})
|
},
|
|
// 地址输入框获得焦点
|
onAddressFocus() {
|
if (this.addressValue && this.addressSuggestions.length > 0) {
|
this.showAddressSuggestions = true
|
}
|
},
|
|
// 选择地址建议
|
selectAddressSuggestion(item) {
|
const fullAddress = item.district + item.address
|
this.$emit('input', {
|
...this.value,
|
address: fullAddress
|
})
|
|
this.showAddressSuggestions = false
|
this.addressSuggestions = []
|
},
|
|
// 科室选择变化(picker)
|
onDepartmentChange(e) {
|
const index = e.detail.value
|
const selected = this.departmentOptions[index]
|
const updatedValue = {
|
...this.value,
|
department: selected.text,
|
departmentId: selected.id
|
}
|
this.$emit('input', updatedValue)
|
this.$emit('department-change', {
|
department: selected.text,
|
departmentId: selected.id
|
})
|
},
|
|
// 科室输入(手动输入)
|
onDepartmentInput(e) {
|
const department = e.detail.value
|
this.$emit('input', {
|
...this.value,
|
department: department
|
})
|
this.$emit('department-change', department)
|
},
|
|
// 床号输入
|
onBedNumberInput(e) {
|
const bedNumber = e.detail.value
|
this.$emit('input', {
|
...this.value,
|
bedNumber: bedNumber
|
})
|
this.$emit('bed-number-change', bedNumber)
|
}
|
}
|
}
|
</script>
|
|
<style lang="scss" scoped>
|
.hospital-selector {
|
.form-item {
|
margin-bottom: 40rpx;
|
|
.form-label {
|
font-size: 28rpx;
|
margin-bottom: 15rpx;
|
color: #333;
|
|
&.required::before {
|
content: '*';
|
color: #ff0000;
|
margin-right: 5rpx;
|
}
|
}
|
|
.hospital-search-container {
|
position: relative;
|
|
.form-input {
|
width: 100%;
|
height: 70rpx;
|
padding: 0 20rpx;
|
border: 1rpx solid #eee;
|
border-radius: 10rpx;
|
font-size: 28rpx;
|
box-sizing: border-box;
|
}
|
|
.search-results {
|
position: absolute;
|
top: 75rpx;
|
left: 0;
|
right: 0;
|
max-height: 400rpx;
|
overflow-y: auto;
|
background-color: white;
|
border: 1rpx solid #eee;
|
border-radius: 10rpx;
|
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1);
|
z-index: 100;
|
|
.search-result-item {
|
padding: 20rpx;
|
border-bottom: 1rpx solid #f0f0f0;
|
|
&:last-child {
|
border-bottom: none;
|
}
|
|
&:active {
|
background-color: #f5f5f5;
|
}
|
|
.hospital-name {
|
font-size: 28rpx;
|
color: #333;
|
margin-bottom: 8rpx;
|
|
.hospital-short {
|
margin-left: 10rpx;
|
font-size: 24rpx;
|
color: #999;
|
}
|
}
|
|
.hospital-address {
|
font-size: 24rpx;
|
color: #999;
|
}
|
}
|
}
|
}
|
|
.address-input-container {
|
position: relative;
|
|
.form-input {
|
width: 100%;
|
height: 70rpx;
|
padding: 0 20rpx;
|
border: 1rpx solid #eee;
|
border-radius: 10rpx;
|
font-size: 28rpx;
|
box-sizing: border-box;
|
}
|
|
.address-suggestions {
|
position: absolute;
|
top: 75rpx;
|
left: 0;
|
right: 0;
|
max-height: 400rpx;
|
overflow-y: auto;
|
background-color: white;
|
border: 1rpx solid #eee;
|
border-radius: 10rpx;
|
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1);
|
z-index: 100;
|
|
.address-suggestion-item {
|
padding: 20rpx;
|
border-bottom: 1rpx solid #f0f0f0;
|
|
&:last-child {
|
border-bottom: none;
|
}
|
|
&:active {
|
background-color: #f5f5f5;
|
}
|
|
.suggestion-name {
|
font-size: 28rpx;
|
color: #333;
|
margin-bottom: 8rpx;
|
}
|
|
.suggestion-address {
|
font-size: 24rpx;
|
color: #999;
|
}
|
}
|
}
|
}
|
|
.form-input {
|
width: 100%;
|
height: 70rpx;
|
padding: 0 20rpx;
|
border: 1rpx solid #eee;
|
border-radius: 10rpx;
|
font-size: 28rpx;
|
box-sizing: border-box;
|
|
&.picker-input {
|
display: flex;
|
align-items: center;
|
justify-content: space-between;
|
}
|
|
&.disabled {
|
background-color: #f5f5f5;
|
color: #999;
|
}
|
}
|
}
|
}
|
</style>
|