241 lines
5.4 KiB
Vue
241 lines
5.4 KiB
Vue
<template>
|
||
<view class="hospital-page">
|
||
<view class="top-card">
|
||
<view>
|
||
<text class="top-title">附近医院</text>
|
||
<text class="top-desc">保存常去医院,紧急情况更快找到联系方式。</text>
|
||
</view>
|
||
<TnIcon name="safe-fill" :size="52" color="#F97316" />
|
||
</view>
|
||
|
||
<view v-if="loading" class="loading-state">
|
||
<TnLoading :show="true" mode="circle" color="#F97316" :size="54" />
|
||
<text class="muted-text">正在查找医院...</text>
|
||
</view>
|
||
|
||
<view v-else-if="hospitalList.length === 0" class="empty-state">
|
||
<TnEmpty mode="data" color="#F97316" size="xl" />
|
||
<text class="empty-title">暂无医院数据</text>
|
||
<text class="muted-text">可以稍后再试,或在地图 App 中搜索附近宠物医院。</text>
|
||
</view>
|
||
|
||
<scroll-view v-else scroll-y class="hospital-scroll">
|
||
<view class="hospital-card" v-for="hospital in hospitalList" :key="hospital._id">
|
||
<view class="card-head">
|
||
<view class="hospital-icon">
|
||
<TnIcon name="safe-fill" :size="34" color="#F97316" />
|
||
</view>
|
||
<view class="hospital-main">
|
||
<view class="title-row">
|
||
<text class="hospital-name">{{ hospital.name }}</text>
|
||
<TnTag v-if="hospital.verified" type="success" size="sm" shape="round">认证</TnTag>
|
||
<TnTag v-if="hospital.is24Hour" type="danger" size="sm" shape="round">24h</TnTag>
|
||
</view>
|
||
<text class="hospital-address">{{ hospital.address || '暂无地址' }}</text>
|
||
</view>
|
||
</view>
|
||
<view class="meta-row">
|
||
<text class="rating">评分 {{ hospital.rating || '-' }}</text>
|
||
<text class="hours">{{ hospital.hours || '营业时间待确认' }}</text>
|
||
</view>
|
||
<view class="tag-row" v-if="hospital.tags?.length">
|
||
<TnTag v-for="tag in hospital.tags" :key="tag" bg-color="#FFF7ED" text-color="#F97316" size="sm" shape="round">
|
||
{{ tag }}
|
||
</TnTag>
|
||
</view>
|
||
<TnButton v-if="hospital.phone" text-color="#FFFFFF" shape="round" :bold="true"
|
||
:custom-style="primaryButtonStyle" width="100%" height="76" @click="callHospital(hospital.phone)">
|
||
<TnIcon name="tel" :size="24" />
|
||
<text class="btn-text">拨打电话</text>
|
||
</TnButton>
|
||
</view>
|
||
</scroll-view>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { ref } from 'vue'
|
||
import { onLoad } from '@dcloudio/uni-app'
|
||
import TnButton from '@/uni_modules/tuniaoui-vue3/components/button/src/button.vue'
|
||
import TnEmpty from '@/uni_modules/tuniaoui-vue3/components/empty/src/empty.vue'
|
||
import TnIcon from '@/uni_modules/tuniaoui-vue3/components/icon/src/icon.vue'
|
||
import TnLoading from '@/uni_modules/tuniaoui-vue3/components/loading/src/loading.vue'
|
||
import TnTag from '@/uni_modules/tuniaoui-vue3/components/tag/src/tag.vue'
|
||
|
||
interface Hospital {
|
||
_id: string
|
||
name: string
|
||
address?: string
|
||
phone?: string
|
||
hours?: string
|
||
is24Hour?: boolean
|
||
tags?: string[]
|
||
rating?: number
|
||
verified?: boolean
|
||
}
|
||
|
||
const loading = ref(true)
|
||
const hospitalList = ref<Hospital[]>([])
|
||
const primaryButtonStyle = {
|
||
background: 'linear-gradient(135deg, #F97316, #FB923C)',
|
||
}
|
||
|
||
onLoad(() => {
|
||
loadHospitals()
|
||
})
|
||
|
||
async function loadHospitals() {
|
||
loading.value = true
|
||
try {
|
||
const db = uniCloud.database()
|
||
const res = await db.collection('hospitals').where({ status: 'active' }).limit(20).get()
|
||
hospitalList.value = (res.result?.data || res.data || []) as Hospital[]
|
||
} catch (e) {
|
||
console.warn('加载医院列表失败', e)
|
||
hospitalList.value = []
|
||
} finally {
|
||
loading.value = false
|
||
}
|
||
}
|
||
|
||
function callHospital(phone: string) {
|
||
uni.makePhoneCall({ phoneNumber: phone })
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
$text-dark: #0F172A;
|
||
$text-body: #475569;
|
||
$text-muted: #64748B;
|
||
$card-bg: #FFFFFF;
|
||
$shadow: 0 8rpx 24rpx rgba(15, 23, 42, 0.06);
|
||
|
||
.hospital-page {
|
||
min-height: 100vh;
|
||
background: #F8FAFC;
|
||
padding: 24rpx 24rpx 48rpx;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.top-card {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
gap: 24rpx;
|
||
padding: 28rpx;
|
||
border-radius: 28rpx;
|
||
background: #FFFFFF;
|
||
box-shadow: $shadow;
|
||
}
|
||
|
||
.top-title {
|
||
display: block;
|
||
font-size: 38rpx;
|
||
font-weight: 900;
|
||
color: $text-dark;
|
||
}
|
||
|
||
.top-desc {
|
||
display: block;
|
||
margin-top: 8rpx;
|
||
font-size: 25rpx;
|
||
line-height: 1.5;
|
||
color: $text-muted;
|
||
}
|
||
|
||
.hospital-scroll {
|
||
height: calc(100vh - 190rpx);
|
||
margin-top: 22rpx;
|
||
}
|
||
|
||
.hospital-card {
|
||
margin-bottom: 18rpx;
|
||
padding: 24rpx;
|
||
border-radius: 24rpx;
|
||
background: $card-bg;
|
||
box-shadow: $shadow;
|
||
}
|
||
|
||
.card-head {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
gap: 18rpx;
|
||
}
|
||
|
||
.hospital-icon {
|
||
width: 72rpx;
|
||
height: 72rpx;
|
||
border-radius: 20rpx;
|
||
background: #FFF7ED;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.hospital-main {
|
||
flex: 1;
|
||
min-width: 0;
|
||
}
|
||
|
||
.title-row {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10rpx;
|
||
}
|
||
|
||
.hospital-name {
|
||
flex: 1;
|
||
font-size: 30rpx;
|
||
font-weight: 800;
|
||
color: $text-dark;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.hospital-address,
|
||
.muted-text {
|
||
font-size: 25rpx;
|
||
color: $text-muted;
|
||
line-height: 1.55;
|
||
}
|
||
|
||
.meta-row,
|
||
.tag-row {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 10rpx;
|
||
margin-top: 16rpx;
|
||
}
|
||
|
||
.rating,
|
||
.hours {
|
||
padding: 8rpx 14rpx;
|
||
border-radius: 999rpx;
|
||
background: #F8FAFC;
|
||
font-size: 22rpx;
|
||
color: $text-body;
|
||
}
|
||
|
||
.btn-text {
|
||
margin-left: 8rpx;
|
||
}
|
||
|
||
.loading-state,
|
||
.empty-state {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
gap: 18rpx;
|
||
padding: 140rpx 48rpx 0;
|
||
text-align: center;
|
||
}
|
||
|
||
.empty-title {
|
||
font-size: 32rpx;
|
||
font-weight: 800;
|
||
color: $text-dark;
|
||
}
|
||
</style>
|