appointment_system/pages/appointment/appointment-page.vue
2025-12-15 23:42:31 +08:00

358 lines
7.7 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="content">
<view class="header-title">
{{ $t('appointment.allServices') }}
</view>
<view class="divider"></view>
<view class="main-container">
<view class="category-sidebar">
<view
class="category-item"
v-for="(category, index) in categories"
:key="category.id"
@click="clickType(index, category.id)"
:class="{ 'category-item-active': currentIndex === index }">
<view class="category-indicator" v-if="currentIndex === index"></view>
<text class="category-text">{{ getCategoryName(category) }}</text>
</view>
</view>
<view class="divider-vertical"></view>
<view class="service-list">
<!-- 加载状态 -->
<view v-if="loading" class="loading-container">
<text class="loading-text">{{ $t('common.loading') || '加载中...' }}</text>
</view>
<!-- 服务列表 -->
<view v-else-if="services.length > 0" class="service-items">
<view class="service-item" v-for="service in services" :key="service.id" @click="goToServiceDetail(service.id)">
<view class="service-image-wrapper">
<image
v-if="service.image"
:src="getImageUrl(service.image)"
class="service-image"
mode="aspectFill"
@error="handleImageError(service)"></image>
<view v-else class="service-image-placeholder">
<text class="placeholder-icon">📷</text>
</view>
</view>
<text class="service-title">{{ getServiceName(service) }}</text>
</view>
</view>
<!-- 空状态 -->
<view v-else class="empty-container">
<text class="empty-text">{{ $t('common.noData') || '暂无服务' }}</text>
</view>
</view>
</view>
</view>
</template>
<script>
import { updateTabBarI18n } from '@/utils/tabbar-i18n.js'
import { AppServer } from '@/modules/api/AppServer.js'
import Config from '@/modules/Config.js'
export default {
data() {
return {
currentIndex: 0,
currentCategoryId: null,
categories: [],
services: [],
loading: false,
currentLanguage: 'zh'
}
},
onLoad() {
// 获取当前语言
this.currentLanguage = uni.getStorageSync('language') || 'zh'
// 加载分类和服务
this.loadCategories()
},
onShow() {
updateTabBarI18n(this)
},
methods: {
// 加载分类列表
async loadCategories() {
try {
const appserver = new AppServer()
const response = await appserver.GetCategories({ language: this.currentLanguage })
console.log('分类列表响应:', response)
if (response.code === 0 && response.data) {
this.categories = response.data.categories || response.data || []
// 默认选中第一个分类
if (this.categories.length > 0) {
this.currentIndex = 0
this.currentCategoryId = this.categories[0].id
this.loadServices(this.currentCategoryId)
}
} else {
console.error('获取分类失败:', response.message)
uni.showToast({
title: response.message || '获取分类失败',
icon: 'none'
})
}
} catch (error) {
console.error('加载分类异常:', error)
uni.showToast({
title: '加载分类失败',
icon: 'none'
})
}
},
// 加载服务列表
async loadServices(categoryId) {
this.loading = true
try {
const appserver = new AppServer()
const params = {
categoryId: categoryId,
language: this.currentLanguage
}
const response = await appserver.GetServices(params)
console.log('服务列表响应:', response)
if (response.code === 0 && response.data) {
this.services = response.data.services || response.data || []
} else {
console.error('获取服务失败:', response.message)
this.services = []
}
} catch (error) {
console.error('加载服务异常:', error)
this.services = []
} finally {
this.loading = false
}
},
// 点击分类
clickType(index, categoryId) {
this.currentIndex = index
this.currentCategoryId = categoryId
this.loadServices(categoryId)
},
// 获取分类名称(根据当前语言)
getCategoryName(category) {
if (!category) return ''
// 后端返回的是单个 name 字段,已经根据语言格式化
return category.name || ''
},
// 获取服务名称(根据当前语言)
getServiceName(service) {
if (!service) return ''
// 后端返回的是 title 字段,已经根据语言格式化
return service.title || ''
},
// 获取完整的图片 URL
getImageUrl(imagePath) {
if (!imagePath) return ''
if (imagePath.startsWith('http')) return imagePath
// 如果是相对路径,拼接 API 基础地址
const baseUrl = Config.API_BASE_URL || 'http://localhost:3000'
return `${baseUrl}${imagePath}`
},
// 跳转到服务详情页
goToServiceDetail(serviceId) {
uni.navigateTo({
url: `/pages/service-detail/service-detail?id=${serviceId}`
})
},
// 处理图片加载错误
handleImageError(service) {
console.error('图片加载失败:', service.image)
// 将图片设置为 null显示占位符
service.image = null
}
}
}
</script>
<style lang="scss">
.content {
display: flex;
height: 100vh;
background-color: #F3F4F8;
flex-direction: column;
overflow: hidden;
}
.header-title {
width: 100%;
margin-top: 120rpx;
font-size: 30rpx;
text-align: center;
}
.divider {
width: 100%;
height: 2rpx;
background-color: #E0E0E0;
margin-top: 32rpx;
}
.divider-vertical {
width: 2rpx;
background-color: #E0E0E0;
}
.main-container {
display: flex;
flex-direction: row;
flex: 1;
height: calc(100vh - 120rpx - 32rpx - 2rpx);
overflow: hidden;
}
.category-sidebar {
display: flex;
flex-direction: column;
width: 218rpx;
min-width: 218rpx;
background-color: #F7F7F7;
overflow-y: auto;
}
.category-item {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
width: 100%;
min-height: 88rpx;
padding: 16rpx 12rpx;
position: relative;
background: #F7F7F7;
color: #000000;
transition: all 0.3s ease;
box-sizing: border-box;
}
.category-item-active {
background: linear-gradient(to right, #E5FBFF, #77F6F4);
color: #00A0BC;
}
.category-indicator {
width: 6rpx;
height: 30rpx;
border-radius: 20rpx;
background-color: #00A0BC;
position: absolute;
left: 12rpx;
}
.category-text {
font-size: 28rpx;
text-align: center;
word-break: break-word;
line-height: 1.4;
flex: 1;
}
.service-list {
display: flex;
flex-direction: column;
flex: 1;
overflow-y: auto;
padding-bottom: 80rpx;
}
.loading-container,
.empty-container {
display: flex;
align-items: center;
justify-content: center;
padding: 100rpx 0;
width: 100%;
}
.loading-text,
.empty-text {
font-size: 28rpx;
color: #999;
}
.service-items {
display: flex;
flex-direction: column;
width: 100%;
}
.service-item {
display: flex;
flex-direction: column;
width: 468rpx;
margin: 40rpx auto 0;
cursor: pointer;
transition: transform 0.2s;
}
.service-item:active {
transform: scale(0.98);
}
.service-image-wrapper {
width: 100%;
height: 180rpx;
border-radius: 10rpx;
overflow: hidden;
background-color: #f0f0f0;
}
.service-image {
width: 100%;
height: 100%;
}
.service-image-placeholder {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
background: linear-gradient(135deg, #e0e0e0 0%, #f5f5f5 100%);
}
.placeholder-icon {
font-size: 60rpx;
opacity: 0.5;
}
.service-title {
font-size: 28rpx;
margin-top: 12rpx;
color: #333;
font-weight: 500;
}
</style>