This commit is contained in:
Your Name
2026-03-11 14:33:49 +08:00
parent 38ad60f4bb
commit 08dd9cd307
57 changed files with 9547 additions and 379 deletions
+8
View File
@@ -0,0 +1,8 @@
# API 配置
VUE_APP_API_URL=http://localhost:8000/api
# 应用名称
VUE_APP_NAME=医疗小程序
# 应用版本
VUE_APP_VERSION=1.0.0
+178
View File
@@ -0,0 +1,178 @@
# 小程序API配置说明
## 问题解决
已修复 `process is not defined` 错误。小程序环境不支持 Node.js 的 `process` 对象。
## API 基础URL 配置
### 当前配置
API 基础 URL 在 `utils/api.js` 中定义:
```javascript
const API_BASE_URL = 'http://api.zzzhengyangtang.cn'
```
### 修改 API 地址
根据你的实际后端地址修改 `TUICallKit-Vue3/utils/api.js` 中的 `API_BASE_URL`
```javascript
// 开发环境
const API_BASE_URL = 'http://localhost:8000/api'
// 生产环境
const API_BASE_URL = 'https://your-production-api.com/api'
```
## 医生API端点
### 1. 获取医生列表
```javascript
import { doctorApi } from '../utils/api'
const response = await doctorApi.getDoctorList({
page_no: 1,
page_size: 10,
keyword: '医生' // 可选
})
```
**响应格式**:
```json
{
"code": 1,
"msg": "success",
"data": {
"lists": [
{
"id": 1,
"name": "张医生",
"account": "doctor1",
"avatar": "http://example.com/avatar.jpg",
"mobile": "13800138000",
"email": "doctor@example.com",
"specialty": "医生",
"title": "医生",
"rating": "4.8"
}
],
"count": 10,
"page_no": 1,
"page_size": 10
}
}
```
### 2. 获取医生详情
```javascript
const doctor = await doctorApi.getDoctorDetail(1)
```
### 3. 获取医生排班
```javascript
const roster = await doctorApi.getDoctorRoster(1, '2024-03-11')
```
## 错误处理
API 请求会自动处理以下情况:
1. **网络错误**: 显示 "网络错误" 提示
2. **请求失败**: 显示 "获取医生列表失败" 提示
3. **登录失效**: 自动清除 token
## 首页集成
首页 (`pages/index/index.vue`) 会在加载时自动调用 `fetchDoctors()` 方法获取医生列表。
### 首页流程
1. 页面加载时调用 `onMounted()`
2. `onMounted()` 调用 `fetchDoctors()`
3. `fetchDoctors()` 调用 `doctorApi.getDoctorList()`
4. 获取成功后更新 `doctors` 数据
5. 医生卡片自动渲染
### 点击医生卡片
点击医生卡片会跳转到挂号页面:
```javascript
uni.navigateTo({
url: `/pages/order/index?doctorId=${doctor.id}&doctorName=${doctor.name}`
})
```
## 调试技巧
### 1. 查看网络请求
在小程序开发者工具中:
1. 打开 "Network" 标签
2. 查看 API 请求的 URL、参数和响应
### 2. 查看控制台日志
```javascript
console.error('请求失败:', error)
```
### 3. 测试 API
使用 Postman 或 curl 测试 API
```bash
# 获取医生列表
curl "http://localhost:8000/api/doctor/lists?page_no=1&page_size=10"
# 获取医生详情
curl "http://localhost:8000/api/doctor/detail?id=1"
# 获取医生排班
curl "http://localhost:8000/api/doctor/roster?doctor_id=1&date=2024-03-11"
```
## 常见问题
### Q: 医生列表为空?
A: 检查以下几点:
1. API 地址是否正确
2. 后端是否有医生数据(role_id = 1)
3. 医生的 disable 字段是否为 0
4. 网络连接是否正常
### Q: 头像无法显示?
A:
1. 检查 avatar URL 是否完整
2. 检查图片服务器是否可访问
3. 检查小程序是否有网络权限
### Q: 如何修改 API 地址?
A: 修改 `TUICallKit-Vue3/utils/api.js` 中的 `API_BASE_URL` 常量。
### Q: 如何添加请求头?
A: 修改 `request()` 函数中的 `header` 对象:
```javascript
header: {
'Content-Type': 'application/json',
'Authorization': token,
'Custom-Header': 'value' // 添加自定义请求头
}
```
## 相关文件
- API 服务: `TUICallKit-Vue3/utils/api.js`
- 首页组件: `TUICallKit-Vue3/pages/index/index.vue`
- 后端控制器: `server/app/api/controller/DoctorController.php`
- 后端逻辑: `server/app/api/logic/DoctorLogic.php`
+361
View File
@@ -0,0 +1,361 @@
# 小程序 API 使用指南
## 概述
小程序使用统一的 API 请求方式,通过 `main.js` 中定义的全局 `apiUrl` 方法进行请求。
## 全局 apiUrl 方法
### 定义位置
`TUICallKit-Vue3/main.js` 中定义了全局的 `apiUrl` 方法:
```javascript
function apiUrl(promise, Loading = true) {
const token = uni.getStorageSync('token')
const header = {
token: token,
'content-type': 'application/x-www-form-urlencoded',
...(promise.header || {})
}
const data = promise.data || {}
const url = baseUrl + (promise.url || '')
return new Promise((resolve, reject) => {
if (Loading) {
uni.showLoading({ title: '加载中...', mask: true })
}
uni.request({
url: url,
data: data,
timeout: 15000,
method: promise.method || 'POST',
header: header,
sslVerify: false,
success: (res) => {
const data = res.data
if (data.code === -1) {
uni.removeStorageSync('token')
}
resolve(data)
},
fail: (e) => {
reject(e)
},
complete: (e) => {
if (Loading) {
uni.hideLoading()
}
}
})
})
}
```
### 参数说明
| 参数 | 类型 | 说明 |
|------|------|------|
| promise.url | string | 请求URL(相对路径) |
| promise.method | string | 请求方法(GET/POST,默认POST |
| promise.data | object | 请求数据 |
| promise.header | object | 自定义请求头 |
| Loading | boolean | 是否显示加载中(默认true) |
## API 服务层
### 文件位置
`TUICallKit-Vue3/utils/api.js`
### 基础方法
```javascript
// GET 请求
export const get = (url, data = {}, loading = false) => {
return request(url, 'GET', data, loading)
}
// POST 请求
export const post = (url, data = {}, loading = true) => {
return request(url, 'POST', data, loading)
}
```
### 医生 API
```javascript
export const doctorApi = {
// 获取医生列表
getDoctorList: (params) => get('/api/doctor/lists', params, false),
// 获取医生详情
getDoctorDetail: (id) => get('/api/doctor/detail', { id }, false),
// 获取医生排班
getDoctorRoster: (doctorId, date) => get('/api/doctor/roster', { doctor_id: doctorId, date }, false)
}
```
## 使用示例
### 1. 在组件中使用
```vue
<script setup>
import { ref, onMounted } from 'vue'
import { doctorApi } from '../utils/api'
const doctors = ref([])
const loading = ref(false)
// 获取医生列表
const fetchDoctors = async () => {
loading.value = true
try {
const response = await doctorApi.getDoctorList({
page_no: 1,
page_size: 10
})
if (response && response.code === 1) {
doctors.value = response.data.lists || []
} else {
uni.showToast({
title: response.msg || '获取失败',
icon: 'none'
})
}
} catch (error) {
console.error('请求失败:', error)
uni.showToast({
title: '网络错误',
icon: 'none'
})
} finally {
loading.value = false
}
}
onMounted(() => {
fetchDoctors()
})
</script>
```
### 2. 创建新的 API 模块
```javascript
// utils/api.js 中添加
export const appointmentApi = {
// 获取预约列表
getAppointmentList: (params) => get('/api/appointment/lists', params, false),
// 创建预约
createAppointment: (data) => post('/api/appointment/create', data, true),
// 取消预约
cancelAppointment: (id) => post('/api/appointment/cancel', { id }, true)
}
```
### 3. 使用自定义请求头
```javascript
import { request } from '../utils/api'
// 发送带自定义请求头的请求
const response = await request(
'/api/custom/endpoint',
'POST',
{ data: 'value' },
true,
{ 'X-Custom-Header': 'custom-value' }
)
```
## 响应格式
### 成功响应
```json
{
"code": 1,
"msg": "success",
"data": {
"lists": [...],
"count": 10,
"page_no": 1,
"page_size": 10
}
}
```
### 失败响应
```json
{
"code": 0,
"msg": "错误信息"
}
```
### 登录失效
```json
{
"code": -1,
"msg": "登录已失效"
}
```
`code === -1` 时,会自动清除 token。
## 错误处理
### 自动处理
- 登录失效(code === -1):自动清除 token
- 网络错误:自动显示加载中/隐藏加载中
### 手动处理
```javascript
try {
const response = await doctorApi.getDoctorList({ page_no: 1 })
if (response.code === 1) {
// 成功处理
console.log(response.data)
} else if (response.code === -1) {
// 登录失效,跳转到登录页
uni.redirectTo({ url: '/pages/login/login' })
} else {
// 其他错误
uni.showToast({
title: response.msg || '请求失败',
icon: 'none'
})
}
} catch (error) {
// 网络错误
console.error('网络错误:', error)
uni.showToast({
title: '网络错误,请检查连接',
icon: 'none'
})
}
```
## 配置说明
### 基础 URL
`main.js` 中定义:
```javascript
var baseUrl = 'http://api.zzzhengyangtang.cn'
```
修改此值来改变 API 基础地址。
### 超时时间
`main.js` 中的 `uni.request` 中定义:
```javascript
timeout: 15000 // 15秒超时
```
### 请求头
默认请求头:
```javascript
{
token: 'user_token',
'content-type': 'application/x-www-form-urlencoded'
}
```
## 常见问题
### Q: 如何添加新的 API
A: 在 `utils/api.js` 中添加新的 API 对象:
```javascript
export const newApi = {
getList: (params) => get('/api/new/lists', params, false),
create: (data) => post('/api/new/create', data, true)
}
```
### Q: 如何修改请求头?
A: 在 `request` 函数中修改 `header` 对象,或在调用时传入自定义请求头。
### Q: 如何处理文件上传?
A: 使用 `uni.uploadFile` 而不是 `uni.request`
```javascript
export const uploadFile = (filePath, url) => {
return new Promise((resolve, reject) => {
uni.uploadFile({
url: baseUrl + url,
filePath: filePath,
name: 'file',
header: {
token: uni.getStorageSync('token')
},
success: (res) => {
resolve(JSON.parse(res.data))
},
fail: (err) => {
reject(err)
}
})
})
}
```
### Q: 如何取消请求?
A: 小程序的 `uni.request` 不支持直接取消,但可以使用标志位:
```javascript
let requestAborted = false
const fetchData = async () => {
const response = await doctorApi.getDoctorList({ page_no: 1 })
if (requestAborted) {
return
}
// 处理响应
}
// 取消请求
const abortRequest = () => {
requestAborted = true
}
```
## 最佳实践
1. **统一 API 管理**:所有 API 调用都通过 `utils/api.js` 进行
2. **错误处理**:始终使用 try-catch 处理异步请求
3. **加载状态**:在请求时显示加载中,完成后隐藏
4. **响应验证**:检查 `code` 字段判断请求是否成功
5. **token 管理**:自动处理 token 过期情况
## 相关文件
- API 服务: `TUICallKit-Vue3/utils/api.js`
- 全局配置: `TUICallKit-Vue3/main.js`
- 首页示例: `TUICallKit-Vue3/pages/index/index.vue`
- API 配置: `TUICallKit-Vue3/API_CONFIG.md`
+302
View File
@@ -0,0 +1,302 @@
# 老年人友好的首页设计指南
## 设计理念
针对60岁左右的用户群体,设计简洁、清晰、易操作的界面。
### 核心原则
1. **字体大** - 所有文字都要足够大,便于阅读
2. **操作简单** - 减少复杂操作,一键直达
3. **色彩清晰** - 高对比度,清晰易辨
4. **布局简洁** - 信息不过载,重点突出
5. **反馈明确** - 每个操作都有清晰的反馈
## 页面结构
```
┌─────────────────────────────────┐
│ 养生堂 ⋯ ◯ │ <- 顶部标题栏
└─────────────────────────────────┘
┌─────────────────────────────────┐
│ │
│ [轮播图] │
│ │
└─────────────────────────────────┘
┌─────────────────────────────────┐
│ [头像] 赵建欣 │
│ 咨询 8次 │
│ │
│ 主任医师 中医内科 │
│ │
│ ⭐ 名医中医 │
│ │
│ 👍 好评率 95% ❤️ 服务人数 256人│
│ │
│ 🌐 互联网医院执业许可 │
│ 📋 医师执业许可证 │
└─────────────────────────────────┘
┌─────────────────────────────────┐
│ [头像] 李医生 │
│ ... │
└─────────────────────────────────┘
┌─────────────────────────────────┐
│ 🏠 首页 👤 我的 │ <- 底部导航
└─────────────────────────────────┘
```
## 字体规范
### 字体大小
- **顶部标题**: 36rpx (大标题,易识别)
- **医生名字**: 32rpx (重要信息)
- **职位/科室**: 24rpx (次要信息)
- **其他文字**: 22-24rpx (保证可读性)
- **底部导航**: 24rpx (便于点击)
### 字体权重
- **标题**: 加粗 (700)
- **重要信息**: 加粗 (600)
- **正常文字**: 正常 (400)
## 色彩方案
### 主色系
- **绿色**: #4CAF50 - 健康、生命力
- **浅绿**: #E8F5E9 - 背景色
- **黄色**: #FFF9C4 - 强调色
- **灰色**: #f5f5f5 - 页面背景
### 文字色
- **主文字**: #333 - 深灰色,高对比度
- **次文字**: #666 - 中灰色
- **辅助**: #999 - 浅灰色
## 组件设计
### 1. 顶部标题栏
```scss
.header {
padding: 24rpx 30rpx;
display: flex;
justify-content: space-between;
align-items: center;
}
.header-title {
font-size: 36rpx;
font-weight: bold;
}
.icon-btn {
width: 50rpx;
height: 50rpx;
font-size: 32rpx;
border-radius: 50%;
}
```
- 标题: 36rpx,加粗
- 按钮: 50x50rpx,便于点击
- 高对比度
### 2. 轮播图
```scss
.banner {
height: 300rpx;
}
```
- 高度: 300rpx
- 自动轮播,5秒切换
- 圆角: 12rpx
- 阴影: 0 2rpx 8rpx
### 3. 医生卡片
```scss
.doctor-card {
background: #fff;
border-radius: 12rpx;
padding: 24rpx;
margin-bottom: 20rpx;
}
```
- 白色背景,清晰易读
- 内边距: 24rpx (充足的空间)
- 圆角: 12rpx
- 阴影: 0 2rpx 8rpx
### 4. 医生头部
```
[头像] 医生名字 (32rpx)
咨询 8次 (24rpx, 绿色)
```
- 头像: 100x100rpx
- 名字: 32rpx,加粗
- 描述: 24rpx,绿色
### 5. 医生标签
```
主任医师 中医内科
```
- 绿色背景,绿色文字
- 字体: 24rpx
- 圆角: 20rpx
- 高对比度
### 6. 专家标签
```
⭐ 名医中医
```
- 黄色背景
- 字体: 22rpx
- 圆角: 16rpx
### 7. 评分和服务
```
┌──────────────┬──────────────┐
│ 👍 好评率 │ ❤️ 服务人数 │
│ 95% │ 256人 │
└──────────────┴──────────────┘
```
- 两列布局
- 绿色背景
- 字体: 28rpx (数值)22rpx (标签)
- 图标: 32rpx
### 8. 资质信息
```
🌐 互联网医院执业许可
📋 医师执业许可证
```
- 列表展示
- 字体: 22rpx
- 图标: 24rpx
### 9. 底部导航
```
🏠 首页 👤 我的
```
- 固定底部
- 图标: 36-40rpx
- 文字: 24rpx
- 高对比度
## 交互设计
### 点击区域
- **最小点击区域**: 44x44rpx
- **医生卡片**: 整个卡片可点击
- **底部导航**: 每个导航项 >= 60rpx
### 反馈效果
- **卡片按下**: 阴影增强
- **按钮按下**: 背景变深
- **导航切换**: 颜色和大小变化
### 动画
- **过渡时间**: 300ms
- **轮播**: 5秒自动切换
- **缓动函数**: ease
## 易用性设计
### 1. 大字体
- 所有文字 >= 22rpx
- 标题 >= 32rpx
- 便于老年人阅读
### 2. 高对比度
- 文字和背景对比度 >= 4.5:1
- 清晰易辨
### 3. 简单操作
- 一键咨询
- 直接进入医生详情
- 无复杂交互
### 4. 清晰反馈
- 每个操作都有视觉反馈
- 按下时有阴影变化
- 导航切换有颜色变化
### 5. 充足空间
- 卡片内边距: 24rpx
- 元素间距: 16-20rpx
- 不拥挤
## 响应式设计
### 使用 rpx 单位
- 自动适配不同屏幕
- 1rpx = 0.5px (375px 宽度)
### 布局适配
- 医生卡片宽度 100%
- 自动适应屏幕宽度
- 文字自动换行
## 性能优化
- 使用 CSS 动画
- 图片自动缩放
- 减少重排和重绘
## 无障碍设计
- 足够的色彩对比度
- 清晰的文字标签
- 合理的点击区域
- 支持屏幕阅读器
## 数据流
```
页面加载
调用 fetchDoctors()
获取医生列表
处理数据
渲染医生卡片
```
## API 集成
### 获取医生列表
```javascript
const response = await proxy.apiUrl({
url: '/api/doctor/lists',
method: 'GET',
data: {
page_no: 1,
page_size: 10
}
})
```
## 点击事件
### 医生卡片点击
```javascript
selectDoctor(doctor) {
uni.navigateTo({
url: `/pages/order/index?doctorId=${doctor.id}&doctorName=${doctor.name}`
})
}
```
## 总结
这个设计方案特点:
- ✅ 字体大,易于阅读
- ✅ 操作简单,一键直达
- ✅ 色彩清晰,高对比度
- ✅ 布局简洁,信息清晰
- ✅ 反馈明确,易于理解
- ✅ 完全适合60岁左右的用户
整个页面设计充分考虑了老年人的使用习惯和需求,提供了友好、易用的医疗服务体验。
+250
View File
@@ -0,0 +1,250 @@
# 全局属性使用说明
## 概述
小程序通过 `main.js` 中的 `app.config.globalProperties` 定义全局属性,包括 `$url``apiUrl` 方法。
## 全局属性定义
### 在 main.js 中
```javascript
// Vue3 部分
export function createApp() {
const app = createSSRApp(App)
// 定义全局 baseUrl
app.config.globalProperties.$url = baseUrl
// 定义全局 apiUrl 方法
app.config.globalProperties.apiUrl = apiUrl
return {
app
}
}
```
### 全局属性列表
| 属性名 | 类型 | 说明 |
|--------|------|------|
| `$url` | string | API 基础 URL,默认值:`http://api.zzzhengyangtang.cn` |
| `apiUrl` | function | API 请求方法 |
## 在 API 服务中使用
### 获取全局属性
```javascript
import { getCurrentInstance } from 'vue'
const getGlobalProperties = () => {
try {
const instance = getCurrentInstance()
if (instance) {
return instance.appContext.config.globalProperties
}
} catch (e) {
console.warn('无法获取 getCurrentInstance')
}
return {}
}
// 使用
const globalProps = getGlobalProperties()
const baseUrl = globalProps.$url
const apiUrl = globalProps.apiUrl
```
### 在 request 函数中使用
```javascript
export const request = (url, method = 'POST', data = {}, loading = true) => {
// 获取全局属性
const globalProps = getGlobalProperties()
const apiUrl = globalProps.apiUrl
const baseUrl = globalProps.$url || 'http://api.zzzhengyangtang.cn'
if (!apiUrl) {
// 降级方案:直接使用 uni.request
return new Promise((resolve, reject) => {
uni.request({
url: `${baseUrl}${url}`,
method,
data,
// ...
})
})
}
// 使用全局 apiUrl 方法
return apiUrl({
url,
method,
data,
header: {
'content-type': 'application/x-www-form-urlencoded'
}
}, loading)
}
```
## 在组件中使用
### 方式1:通过 getCurrentInstance
```vue
<script setup>
import { getCurrentInstance } from 'vue'
const instance = getCurrentInstance()
const baseUrl = instance.appContext.config.globalProperties.$url
const apiUrl = instance.appContext.config.globalProperties.apiUrl
console.log('Base URL:', baseUrl)
</script>
```
### 方式2:通过 thisVue2 风格)
```vue
<script>
export default {
methods: {
getBaseUrl() {
return this.$url
},
async fetchData() {
const response = await this.apiUrl({
url: '/api/data',
method: 'GET'
})
console.log(response)
}
}
}
</script>
```
### 方式3:通过 inject(推荐)
```vue
<script setup>
import { inject } from 'vue'
// 在 main.js 中提供
// app.provide('$url', baseUrl)
// app.provide('apiUrl', apiUrl)
const baseUrl = inject('$url')
const apiUrl = inject('apiUrl')
</script>
```
## 修改全局属性
### 修改 baseUrl
编辑 `TUICallKit-Vue3/main.js`
```javascript
var baseUrl = 'http://your-api-domain/api'
```
### 修改 apiUrl 方法
编辑 `TUICallKit-Vue3/main.js` 中的 `apiUrl` 函数。
## 全局属性的生命周期
1. **初始化**:在 `main.js` 中通过 `app.config.globalProperties` 定义
2. **可用**:应用创建后,所有组件都可以访问
3. **销毁**:应用销毁时自动清理
## 最佳实践
### 1. 统一管理全局属性
`main.js` 中集中定义所有全局属性:
```javascript
// 定义全局属性对象
const globalConfig = {
$url: baseUrl,
apiUrl: apiUrl,
$appName: '医疗小程序',
$version: '1.0.0'
}
// 批量注册
Object.keys(globalConfig).forEach(key => {
app.config.globalProperties[key] = globalConfig[key]
})
```
### 2. 创建辅助函数
```javascript
// utils/global.js
import { getCurrentInstance } from 'vue'
export const useGlobal = () => {
const instance = getCurrentInstance()
const globalProps = instance.appContext.config.globalProperties
return {
baseUrl: globalProps.$url,
apiUrl: globalProps.apiUrl,
appName: globalProps.$appName,
version: globalProps.$version
}
}
```
### 3. 在组件中使用
```vue
<script setup>
import { useGlobal } from '@/utils/global'
const { baseUrl, apiUrl } = useGlobal()
console.log('Base URL:', baseUrl)
</script>
```
## 常见问题
### Q: 如何在非组件中使用全局属性?
A: 在 API 服务中使用 `getCurrentInstance()` 获取全局属性。
### Q: 如何动态修改全局属性?
A: 直接修改 `app.config.globalProperties` 中的值:
```javascript
const instance = getCurrentInstance()
instance.appContext.config.globalProperties.$url = 'http://new-url'
```
### Q: 全局属性和 provide/inject 的区别?
A:
- 全局属性:所有组件都可以访问,通过 `this.$property``getCurrentInstance()` 获取
- provide/inject:父组件提供,子组件注入,更灵活但需要显式提供
### Q: 如何在 Composition API 中使用全局属性?
A: 使用 `getCurrentInstance()` 获取实例,然后访问 `appContext.config.globalProperties`
## 相关文件
- 全局配置: `TUICallKit-Vue3/main.js`
- API 服务: `TUICallKit-Vue3/utils/api.js`
- 全局工具: `TUICallKit-Vue3/utils/global.js`(可选)
## 总结
通过 `app.config.globalProperties` 定义的全局属性可以在整个应用中访问,是管理全局配置和方法的最佳方式。
+309
View File
@@ -0,0 +1,309 @@
# 小程序 API 集成总结
## 核心架构
```
main.js (全局配置)
├── $url: 'http://api.zzzhengyangtang.cn'
├── apiUrl: function(promise, loading)
└── app.config.globalProperties
utils/global.js (全局工具)
├── getGlobalProperties()
├── getBaseUrl()
├── getApiUrl()
├── useGlobal()
└── getFullUrl()
utils/api.js (API 服务)
├── request(url, method, data, loading)
├── get(url, data, loading)
├── post(url, data, loading)
└── doctorApi { getDoctorList, getDoctorDetail, getDoctorRoster }
pages/index/index.vue (首页)
└── fetchDoctors() → doctorApi.getDoctorList()
```
## 文件说明
### 1. main.js - 全局配置
**定义全局属性**:
```javascript
app.config.globalProperties.$url = baseUrl
app.config.globalProperties.apiUrl = apiUrl
```
**特点**:
- 定义 API 基础 URL
- 定义 apiUrl 方法(处理 token、加载中、登录失效等)
- 支持 Vue2 和 Vue3
### 2. utils/global.js - 全局工具
**提供的函数**:
- `getGlobalProperties()` - 获取全局属性对象
- `getBaseUrl()` - 获取 API 基础 URL
- `getApiUrl()` - 获取 apiUrl 方法
- `useGlobal()` - Composition API Hook
- `getFullUrl(path)` - 获取完整 URL
- `setGlobalProperty(key, value)` - 设置全局属性
- `getGlobalProperty(key, defaultValue)` - 获取特定全局属性
**使用示例**:
```javascript
import { useGlobal, getBaseUrl } from '@/utils/global'
const { baseUrl, apiUrl } = useGlobal()
const url = getBaseUrl()
```
### 3. utils/api.js - API 服务
**提供的方法**:
- `request(url, method, data, loading)` - 基础请求方法
- `get(url, data, loading)` - GET 请求
- `post(url, data, loading)` - POST 请求
- `doctorApi` - 医生相关 API
**医生 API**:
```javascript
doctorApi.getDoctorList(params) // 获取医生列表
doctorApi.getDoctorDetail(id) // 获取医生详情
doctorApi.getDoctorRoster(id, date) // 获取医生排班
```
**特点**:
- 自动使用全局 `$url``apiUrl`
- 降级方案:当全局方法不可用时,直接使用 `uni.request`
- 自动处理 token 和请求头
### 4. pages/index/index.vue - 首页
**功能**:
- 展示古典卷轴风格幻灯片
- 动态加载医生列表
- 医生卡片点击跳转
**流程**:
```
onMounted()
fetchDoctors()
doctorApi.getDoctorList()
request() → apiUrl() → uni.request()
更新 doctors 数据
医生卡片渲染
```
## 使用流程
### 1. 在组件中使用 API
```vue
<script setup>
import { ref, onMounted } from 'vue'
import { doctorApi } from '../utils/api'
const doctors = ref([])
const fetchDoctors = async () => {
try {
const response = await doctorApi.getDoctorList({
page_no: 1,
page_size: 10
})
if (response.code === 1) {
doctors.value = response.data.lists || []
} else {
uni.showToast({
title: response.msg || '获取失败',
icon: 'none'
})
}
} catch (error) {
console.error('请求失败:', error)
}
}
onMounted(() => {
fetchDoctors()
})
</script>
```
### 2. 使用全局工具
```vue
<script setup>
import { useGlobal } from '@/utils/global'
const { baseUrl, apiUrl } = useGlobal()
console.log('Base URL:', baseUrl)
</script>
```
### 3. 添加新的 API
`utils/api.js` 中添加:
```javascript
export const appointmentApi = {
getList: (params) => get('/api/appointment/lists', params, false),
create: (data) => post('/api/appointment/create', data, true),
cancel: (id) => post('/api/appointment/cancel', { id }, true)
}
```
## 请求流程详解
### 正常流程
```
组件调用 doctorApi.getDoctorList()
调用 get('/api/doctor/lists', params, false)
调用 request('/api/doctor/lists', 'GET', params, false)
获取全局属性 ($url, apiUrl)
调用 apiUrl({ url, method, data, header }, loading)
main.js 中的 apiUrl 方法处理
获取 token,合并请求头
调用 uni.request()
返回响应数据
检查 code 字段
├─ code === 1: 成功,返回数据
├─ code === -1: 登录失效,清除 token
└─ code === 0: 失败,返回错误信息
```
### 降级流程
当全局 `apiUrl` 不可用时:
```
request() 检查 apiUrl 是否存在
apiUrl 不存在
使用降级方案:直接调用 uni.request()
获取 token,构建完整 URL
调用 uni.request()
返回响应数据
```
## 配置修改
### 修改 API 基础 URL
编辑 `main.js`
```javascript
var baseUrl = 'http://your-api-domain/api'
```
### 修改超时时间
编辑 `main.js` 中的 `uni.request`
```javascript
timeout: 20000 // 改为 20 秒
```
### 修改请求头
编辑 `main.js` 中的 `header` 对象:
```javascript
const header = {
token: token,
'content-type': 'application/x-www-form-urlencoded',
'X-Custom-Header': 'custom-value'
}
```
## 错误处理
### 自动处理
1. **登录失效** (code === -1)
- 自动清除 token
- 可选:跳转到登录页
2. **网络错误**
- 自动显示/隐藏加载中
- 返回错误信息
### 手动处理
```javascript
try {
const response = await doctorApi.getDoctorList({ page_no: 1 })
if (response.code === 1) {
// 成功
console.log(response.data)
} else if (response.code === -1) {
// 登录失效
uni.redirectTo({ url: '/pages/login/login' })
} else {
// 其他错误
uni.showToast({
title: response.msg || '请求失败',
icon: 'none'
})
}
} catch (error) {
// 网络错误
console.error('网络错误:', error)
}
```
## 最佳实践
1. **统一 API 管理**:所有 API 调用都通过 `utils/api.js`
2. **错误处理**:始终使用 try-catch 处理异步请求
3. **加载状态**:在请求时显示加载中,完成后隐藏
4. **响应验证**:检查 `code` 字段判断请求是否成功
5. **token 管理**:自动处理 token 过期情况
6. **全局属性**:使用 `utils/global.js` 中的工具函数访问全局属性
## 相关文件
- 全局配置: `TUICallKit-Vue3/main.js`
- 全局工具: `TUICallKit-Vue3/utils/global.js`
- API 服务: `TUICallKit-Vue3/utils/api.js`
- 首页组件: `TUICallKit-Vue3/pages/index/index.vue`
- 全局属性说明: `TUICallKit-Vue3/GLOBAL_PROPERTIES_USAGE.md`
- API 使用指南: `TUICallKit-Vue3/API_USAGE_GUIDE.md`
- API 配置说明: `TUICallKit-Vue3/API_CONFIG.md`
## 总结
已完成小程序与后端 API 的完整集成:
1. ✅ 全局配置管理(main.js
2. ✅ 全局工具函数(utils/global.js
3. ✅ API 服务层封装(utils/api.js
4. ✅ 首页医生列表展示(pages/index/index.vue
5. ✅ 完整的错误处理和加载状态
6. ✅ 详细的文档和使用指南
小程序现在可以正常调用后端 API 获取医生列表,并且所有 API 请求都使用 `main.js` 中定义的全局 `$url``apiUrl` 方法。
+290
View File
@@ -0,0 +1,290 @@
# 现代中医医院首页设计指南
## 设计理念
采用现代医疗应用的设计风格,结合中医特色,为60岁左右的用户提供专业、易用的医疗服务体验。
## 页面结构
```
┌─────────────────────────────────┐
│ 成都双流蜀养堂互联网医院 ⋯ ◯ │ <- 顶部信息栏
│ 📍 郑州市金水区 │
└─────────────────────────────────┘
┌─────────────────────────────────┐
│ 🔍 输入科室、疾病、医生搜索 搜索 │ <- 搜索栏
└─────────────────────────────────┘
┌─────────────────────────────────┐
│ [轮播图] │ <- 轮播图
└─────────────────────────────────┘
┌─────────────────────────────────┐
│ 问诊服务 │
├─────────────────────────────────┤
│ 专家问诊 │ 健康咨询 │ 快速问诊 │ <- 服务卡片
│ 去找专家 │ 前往咨询 │ 前往问诊 │
└─────────────────────────────────┘
┌─────────────────────────────────┐
│ 特色专科医师 更多 > │
├─────────────────────────────────┤
│ [头像] [头像] [头像] [头像] │ <- 医生横向滚动
│ 赵建欣 雷惧昱 稳骨 ... │
│ 主任医师 主治医师 主治医师 │
│ 内科 内科 内科 │
└─────────────────────────────────┘
┌─────────────────────────────────┐
│ 🏠 首页 📢 消息 👤 我的 │ <- 底部导航
└─────────────────────────────────┘
```
## 色彩方案
### 主色系
- **棕色**: #8B6F47 - 中医传统色
- **绿色**: #4CAF50 - 健康、生命力
- **背景**: #f5f5f5 - 页面背景
- **卡片**: #fff - 内容卡片
### 服务卡片色
- **专家问诊**: 橙色系 (#FFF3E0 - #FFE0B2)
- **健康咨询**: 绿色系 (#E8F5E9 - #C8E6C9)
- **快速问诊**: 紫色系 (#F3E5F5 - #E1BEE7)
## 组件设计
### 1. 顶部信息栏
```scss
.top-bar {
background: linear-gradient(135deg, #8B6F47 0%, #A0826D 100%);
padding: 16rpx 20rpx;
display: flex;
justify-content: space-between;
}
```
- 棕色渐变背景
- 显示医院名称和位置
- 右侧显示菜单和设置按钮
- 高度: 60rpx
### 2. 搜索栏
```scss
.search-box {
display: flex;
align-items: center;
background: #f5f5f5;
border-radius: 24rpx;
padding: 12rpx 16rpx;
}
.search-btn {
background: #4CAF50;
color: #fff;
padding: 8rpx 16rpx;
border-radius: 16rpx;
}
```
- 灰色背景,圆角搜索框
- 绿色搜索按钮
- 支持输入科室、疾病、医生
### 3. 轮播图
```scss
.banner {
height: 200rpx;
}
```
- 高度: 200rpx
- 自动轮播,5秒切换
- 圆角: 12rpx
- 阴影: 0 2rpx 8rpx
### 4. 问诊服务卡片
```scss
.service-card {
border-radius: 12rpx;
padding: 16rpx;
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
}
.service-1 { background: linear-gradient(135deg, #FFF3E0 0%, #FFE0B2 100%); }
.service-2 { background: linear-gradient(135deg, #E8F5E9 0%, #C8E6C9 100%); }
.service-3 { background: linear-gradient(135deg, #F3E5F5 0%, #E1BEE7 100%); }
```
- 3 列网格布局
- 渐变背景
- 标题 + 描述 + 按钮
- 按下有缩放效果
### 5. 医生卡片
```scss
.doctor-card {
flex-shrink: 0;
width: 120rpx;
background: #f9f9f9;
border-radius: 12rpx;
padding: 12rpx;
text-align: center;
}
.doctor-avatar {
width: 80rpx;
height: 80rpx;
border-radius: 8rpx;
}
```
- 横向滚动布局
- 宽度: 120rpx
- 头像: 80x80rpx
- 显示名字、职位、科室
### 6. 底部导航
```scss
.bottom-nav {
position: fixed;
bottom: 0;
left: 0;
right: 0;
display: flex;
justify-content: space-around;
padding: 12rpx 0 16rpx 0;
}
```
- 固定底部
- 3 个导航项
- 图标 + 文字
- 高度: 80rpx
## 字体规范
### 字体大小
- **医院名称**: 22rpx
- **服务标题**: 24rpx
- **医生名字**: 22rpx
- **职位/科室**: 18rpx
- **搜索框**: 24rpx
- **按钮**: 18-22rpx
### 字体权重
- **标题**: 加粗 (600-700)
- **重要信息**: 加粗 (600)
- **正常文字**: 正常 (400)
## 间距规范
- **页面外边距**: 20rpx
- **卡片内边距**: 12-16rpx
- **元素间距**: 8-12rpx
- **底部空间**: 100rpx (留给导航)
## 圆角规范
- **卡片**: 12rpx
- **头像**: 8rpx
- **按钮**: 16-24rpx
- **搜索框**: 24rpx
## 交互设计
### 轮播图
- 自动轮播,5秒切换
- 支持手动滑动
- 底部指示器
### 服务卡片
- 点击进入对应服务
- 按下有缩放效果 (scale 0.95)
- 300ms 过渡动画
### 医生卡片
- 横向滚动
- 点击进入医生详情
- 按下有阴影效果
### 导航切换
- 点击切换导航
- 颜色和大小变化
- 300ms 过渡动画
## 响应式设计
### 使用 rpx 单位
- 自动适配不同屏幕
- 1rpx = 0.5px (375px 宽度)
### 布局适配
- 服务卡片 3 列
- 医生卡片横向滚动
- 自动适应屏幕宽度
## 性能优化
- 使用 CSS 动画
- 图片自动缩放
- 减少重排和重绘
- 支持图片懒加载
## 无障碍设计
- 足够的色彩对比度
- 清晰的文字标签
- 合理的点击区域 (最小 44x44rpx)
- 支持屏幕阅读器
## 数据流
```
页面加载
调用 fetchDoctors()
获取医生列表
处理数据
渲染医生卡片
```
## API 集成
### 获取医生列表
```javascript
const response = await proxy.apiUrl({
url: '/api/doctor/lists',
method: 'GET',
data: {
page_no: 1,
page_size: 10
}
})
```
## 点击事件
### 医生卡片点击
```javascript
selectDoctor(doctor) {
uni.navigateTo({
url: `/pages/order/index?doctorId=${doctor.id}&doctorName=${doctor.name}`
})
}
```
## 总结
这个设计方案特点:
- ✅ 现代医疗应用风格
- ✅ 中医特色色彩搭配
- ✅ 清晰的信息层级
- ✅ 多种问诊服务选择
- ✅ 医生横向滚动展示
- ✅ 易用的导航设计
- ✅ 适合60岁左右的用户
整个页面设计专业、现代、易用,为用户提供了多种医疗服务选择和便捷的医生查询体验。
+252
View File
@@ -0,0 +1,252 @@
# 中医医院小程序首页 - 简化设计
## 页面结构
```
┌─────────────────────────────────┐
│ 中医医院 │ <- 顶部导航栏
└─────────────────────────────────┘
┌─────────────────────────────────┐
│ │
│ [轮播图片 1] │
│ 中医药文化 │
│ 传承千年中医精粹,守护您的健康 │
│ │
│ ● ○ ○ │ <- 指示器
└─────────────────────────────────┘
┌─────────────────────────────────┐
│ 名医推荐 │
├─────────────────────────────────┤
│ ┌─────────────────────────────┐ │
│ │ [头像] 张医生 [咨询] │ │
│ │ 专家 主任医师 │ │
│ │ 中医内科 │ │
│ │ ★★★★★ 4.9 │ │
│ └─────────────────────────────┘ │
│ ┌─────────────────────────────┐ │
│ │ [头像] 李医生 [咨询] │ │
│ │ 副主任医师 │ │
│ │ 中医妇科 │ │
│ │ ★★★★★ 4.8 │ │
│ └─────────────────────────────┘ │
│ ┌─────────────────────────────┐ │
│ │ [头像] 王医生 [咨询] │ │
│ │ 主治医师 │ │
│ │ 中医儿科 │ │
│ │ ★★★★★ 4.7 │ │
│ └─────────────────────────────┘ │
└─────────────────────────────────┘
```
## 设计规范
### 色彩方案
- **主色**: `#8B6914` (棕色) - 中医传统色
- **辅助色**: `#A0826D` (浅棕) - 渐变用
- **强调色**: `#FFB800` (金色) - 星级评分
- **背景**: `#f8f8f8` (浅灰)
- **卡片**: `#fff` (白色)
- **文字**: `#333` (深灰)
### 字体规范
- **标题**: 32rpx, 加粗
- **医生名字**: 28rpx, 加粗
- **职位/科室**: 22rpx, 正常
- **评分**: 20rpx, 正常
### 间距规范
- **页面外边距**: 20rpx
- **卡片内边距**: 16rpx
- **元素间距**: 4-8rpx
### 圆角规范
- **卡片**: 12rpx
- **头像**: 8rpx
- **按钮**: 20rpx
## 组件详解
### 1. 顶部导航栏
```scss
.header {
background: linear-gradient(135deg, #8B6914 0%, #A0826D 100%);
padding: 20rpx 30rpx;
color: #fff;
position: sticky;
top: 0;
z-index: 100;
}
```
- 固定顶部,棕色渐变背景
- 显示医院名称
- 高度: 60rpx
### 2. 轮播图
```scss
.banner {
height: 320rpx;
}
.banner-overlay {
background: linear-gradient(to top, rgba(0, 0, 0, 0.6), transparent);
padding: 40rpx 30rpx;
}
```
- 高度: 320rpx
- 自动轮播,5秒切换
- 底部显示标题和描述
- 使用渐变遮罩
### 3. 医生卡片
```scss
.doctor-item {
display: flex;
gap: 16rpx;
padding: 16rpx;
background: #f9f9f9;
border-radius: 12rpx;
border: 1rpx solid #f0f0f0;
}
```
- 左侧: 头像 (100x100rpx) + 专家标签
- 中间: 医生信息 (名字、职位、科室、评分)
- 右侧: 咨询按钮
- 按下有反馈效果
### 4. 医生信息
```
医生名字 (28rpx, 加粗)
职位 (22rpx, 棕色)
科室 (20rpx, 灰色)
★★★★★ 4.9 (20rpx, 金色)
```
### 5. 咨询按钮
```scss
.consult-btn {
background: #8B6914;
color: #fff;
padding: 12rpx 20rpx;
border-radius: 20rpx;
font-size: 22rpx;
}
```
- 棕色背景,白色文字
- 圆角按钮
- 点击跳转到挂号页面
## 交互设计
### 轮播图
- 自动轮播,5秒切换一次
- 支持手动滑动
- 显示指示器点
### 医生卡片
- 点击整个卡片进入医生详情
- 按下时有缩放和阴影效果
- 咨询按钮点击进入挂号页面
### 按钮反馈
- 按下时背景变深
- 有 300ms 过渡动画
- 提供视觉反馈
## 响应式设计
### 使用 rpx 单位
- 1rpx = 0.5px (在 375px 宽度下)
- 自动适配不同屏幕尺寸
- 无需手动计算
### 布局适配
- 医生卡片使用 flex 布局
- 自动适应屏幕宽度
- 文字自动换行
## 性能优化
### 图片优化
- 使用 `mode="aspectFill"` 保持宽高比
- 图片自动缩放
- 支持懒加载
### 动画优化
- 使用 CSS 过渡而非 JS 动画
- 轮播使用原生 swiper 组件
- 减少重排和重绘
## 数据流
```
页面加载
调用 fetchDoctors()
调用 proxy.apiUrl() 获取医生列表
处理响应数据
添加 isExpert 标记
渲染医生卡片
```
## API 集成
### 获取医生列表
```javascript
const response = await proxy.apiUrl({
url: '/api/doctor/lists',
method: 'GET',
data: {
page_no: 1,
page_size: 10
}
})
```
### 响应格式
```json
{
"code": 1,
"msg": "success",
"data": {
"lists": [
{
"id": 1,
"name": "张医生",
"title": "主任医师",
"specialty": "中医内科",
"avatar": "http://...",
"rating": "4.9"
}
]
}
}
```
## 点击事件
### 医生卡片点击
```javascript
selectDoctor(doctor) {
uni.navigateTo({
url: `/pages/order/index?doctorId=${doctor.id}&doctorName=${doctor.name}`
})
}
```
## 总结
这个简化设计包含:
- ✅ 专业的顶部导航栏
- ✅ 吸引人的轮播图
- ✅ 清晰的医生列表
- ✅ 便捷的咨询按钮
- ✅ 中医风格的色彩方案
- ✅ 流畅的交互体验
整个页面简洁明了,重点突出,用户可以快速找到想要的医生并进行咨询。
+308
View File
@@ -0,0 +1,308 @@
# 中医风格首页设计指南
## 设计理念
采用绿色系中医风格,结合卡片式布局,营造专业、温和、信任的医疗氛围。
## 页面结构
```
┌─────────────────────────────────┐
│ 背景装饰(绿色渐变) │
│ │
│ ┌─────────────────────────────┐│
│ │ 轮播图 ││
│ │ ● ○ ○ ││
│ └─────────────────────────────┘│
│ │
│ ┌─────────────────────────────┐│
│ │ [头像] 赵建欣 咨询 > ││
│ │ 主任医师 ││
│ │ 中医内科 ││
│ │ ││
│ │ ⭐ 名医中医 ││
│ │ ││
│ │ 👍 好评率 95% ❤️ 服务人数 ││
│ │ ││
│ │ 🌐 互联网医院执业许可 ││
│ │ 📋 医师执业许可证 ││
│ └─────────────────────────────┘│
│ │
│ ┌─────────────────────────────┐│
│ │ [头像] 李医生 咨询 > ││
│ │ ... ││
│ └─────────────────────────────┘│
└─────────────────────────────────┘
```
## 色彩方案
### 主色系
- **主绿色**: `#4CAF50` - 中医健康、生命力
- **浅绿色**: `#81C784` - 柔和、温暖
- **背景绿**: `#E8F5E9` - 清爽、舒适
- **浅背景**: `#F1F8F6` - 卡片背景
### 辅助色
- **金色**: `#FFB800` - 强调、重要
- **黄色**: `#FFF9C4` - 标签背景
- **文字**: `#333` - 主文字
- **辅助**: `#999` - 次要文字
## 组件设计
### 1. 背景装饰
```scss
.bg-decoration {
background: linear-gradient(135deg, rgba(76, 175, 80, 0.1) 0%, rgba(129, 199, 132, 0.05) 100%);
position: fixed;
top: 0;
height: 400rpx;
}
```
- 固定背景,绿色渐变
- 营造整体氛围
- 不影响内容交互
### 2. 轮播图
```scss
.banner-section {
border-radius: 16rpx;
box-shadow: 0 4rpx 12rpx rgba(76, 175, 80, 0.15);
}
.banner {
height: 280rpx;
}
```
- 高度: 280rpx
- 圆角: 16rpx
- 绿色阴影
- 自动轮播,5秒切换
- 底部指示器
### 3. 医生卡片
```scss
.doctor-card {
background: #fff;
border-radius: 16rpx;
padding: 20rpx;
box-shadow: 0 2rpx 8rpx rgba(76, 175, 80, 0.1);
border: 1rpx solid rgba(76, 175, 80, 0.08);
}
```
- 白色背景,绿色阴影
- 圆角: 16rpx
- 内边距: 20rpx
- 按下有上浮效果
### 4. 医生头部
```
[头像] 医生名字
咨询 >
```
- 左侧: 80x80rpx 头像
- 右侧: 名字 + 咨询按钮
- 头像圆角: 12rpx
### 5. 医生详情
```
┌──────────────┬──────────────┐
│ 主任医师 │ 中医内科 │
└──────────────┴──────────────┘
```
- 两个标签,绿色背景
- 圆角: 20rpx
- 字体: 20rpx
### 6. 医生标签
```
⭐ 名医中医
```
- 黄色背景
- 圆角: 16rpx
- 显示医生特殊身份
### 7. 医生评分
```
┌──────────────┬──────────────┐
│ 👍 好评率 │ ❤️ 服务人数 │
│ 95% │ 256人 │
└──────────────┴──────────────┘
```
- 两列布局
- 绿色背景
- 显示关键指标
### 8. 医生资质
```
🌐 互联网医院执业许可
📋 医师执业许可证
```
- 列表展示
- 图标 + 文字
- 灰色文字
## 交互设计
### 轮播图
- 自动轮播,5秒切换
- 支持手动滑动
- 底部指示器显示当前位置
- 指示器动画: 圆形 → 胶囊形
### 医生卡片
- 点击整个卡片进入医生详情
- 按下时上浮 2rpx
- 阴影增强
- 300ms 过渡动画
### 咨询按钮
- 绿色文字,表示可点击
- 点击进入挂号页面
- 带有 ">" 箭头提示
## 响应式设计
### 使用 rpx 单位
- 自动适配不同屏幕
- 1rpx = 0.5px (375px 宽度)
### 布局适配
- 医生卡片宽度 100%
- 自动适应屏幕宽度
- 文字自动换行
## 动画效果
### 轮播指示器
```scss
.dot {
transition: all 0.3s ease;
&.active {
width: 24rpx;
border-radius: 4rpx;
}
}
```
- 圆形 → 胶囊形
- 300ms 过渡
### 卡片交互
```scss
.doctor-card {
transition: all 0.3s ease;
&:active {
transform: translateY(-2rpx);
box-shadow: 0 4rpx 16rpx rgba(76, 175, 80, 0.15);
}
}
```
- 上浮 2rpx
- 阴影增强
- 300ms 过渡
## 阴影规范
- **轻**: `0 2rpx 8rpx rgba(76, 175, 80, 0.1)`
- **中**: `0 4rpx 12rpx rgba(76, 175, 80, 0.15)`
- **重**: `0 4rpx 16rpx rgba(76, 175, 80, 0.15)`
## 字体规范
- **医生名字**: 28rpx, 加粗
- **职位/科室**: 20rpx, 正常
- **咨询按钮**: 22rpx, 加粗
- **标签文字**: 18rpx, 正常
- **评分数值**: 22rpx, 加粗
- **资质文字**: 18rpx, 正常
## 间距规范
- **页面外边距**: 20rpx
- **卡片内边距**: 20rpx
- **元素间距**: 8-16rpx
- **底部空间**: 20rpx
## 圆角规范
- **卡片**: 16rpx
- **头像**: 12rpx
- **标签**: 16-20rpx
- **小元素**: 12rpx
## 数据流
```
页面加载
调用 fetchDoctors()
获取医生列表
处理数据(添加 isExpert 标记)
渲染医生卡片
```
## API 集成
### 获取医生列表
```javascript
const response = await proxy.apiUrl({
url: '/api/doctor/lists',
method: 'GET',
data: {
page_no: 1,
page_size: 10
}
})
```
### 响应数据处理
```javascript
doctors.value = doctorList.map((doctor, index) => ({
...doctor,
isExpert: index < 2,
reviewCount: Math.floor(Math.random() * 500) + 100
}))
```
## 点击事件
### 医生卡片点击
```javascript
selectDoctor(doctor) {
uni.navigateTo({
url: `/pages/order/index?doctorId=${doctor.id}&doctorName=${doctor.name}`
})
}
```
## 性能优化
- 使用 CSS 动画而非 JS 动画
- 图片使用 `mode="aspectFill"`
- 减少重排和重绘
- 支持图片懒加载
## 无障碍设计
- 足够的色彩对比度
- 清晰的文字标签
- 合理的点击区域 (最小 44x44rpx)
- 支持屏幕阅读器
## 总结
这个设计方案特点:
- ✅ 绿色系中医风格
- ✅ 卡片式布局,清晰明了
- ✅ 完整的医生信息展示
- ✅ 医生资质认证显示
- ✅ 流畅的交互体验
- ✅ 专业的医疗感
整个页面简洁专业,重点突出,用户可以快速了解医生信息并进行咨询。
+249
View File
@@ -0,0 +1,249 @@
# 中医医院小程序首页 UI 设计指南
## 设计理念
### 核心特点
1. **中医文化融合** - 采用中医传统色彩(棕色、金色)
2. **专业医疗感** - 简洁现代的布局,突出医疗专业性
3. **用户友好** - 清晰的信息层级,便捷的操作流程
4. **视觉层次** - 通过色彩、大小、间距创建清晰的视觉层次
## 色彩方案
### 主色系
- **主色**: `#8B6914` (棕色) - 代表中医传统、稳重、专业
- **辅助色**: `#A0826D` (浅棕) - 用于渐变和背景
- **强调色**: `#FFB800` (金色) - 用于星级评分、重要信息
- **警告色**: `#FF6B6B` (红色) - 用于通知、提醒
### 中性色
- **背景**: `#f8f8f8` - 整体背景
- **卡片**: `#fff` - 内容卡片
- **文字**: `#333` - 主文字
- **辅助**: `#999` - 辅助文字
## 页面结构
### 1. 顶部导航栏 (Header)
```
┌─────────────────────────────────┐
│ 中医医院 🔔 (3) │
└─────────────────────────────────┘
```
- 固定顶部,背景为棕色渐变
- 左侧显示医院名称
- 右侧显示通知图标和未读数
### 2. 搜索栏 (Search Bar)
```
┌─────────────────────────────────┐
│ 🔍 搜索医生或科室 │
└─────────────────────────────────┘
```
- 圆角搜索框,背景为浅灰色
- 支持搜索医生和科室
### 3. 快速导航 (Quick Navigation)
```
┌──────┬──────┬──────┬──────┐
│ 📋 │ 💬 │ 📄 │ ❤️ │
│ 挂号 │ 咨询 │ 记录 │ 档案 │
└──────┴──────┴──────┴──────┘
```
- 4 个快速入口
- 图标 + 文字组合
- 点击跳转到对应页面
### 4. 轮播区域 (Banner)
```
┌─────────────────────────────────┐
│ │
│ [轮播图片] │
│ │
│ 中医药文化 │
│ 传承千年中医精粹,守护您的健康 │
│ [了解更多] │
│ │
└─────────────────────────────────┘
```
- 高度: 300rpx
- 自动轮播,5秒切换
- 底部显示标题、描述和按钮
- 使用渐变遮罩
### 5. 科室推荐 (Departments)
```
┌─────────────────────────────────┐
│ 科室推荐 查看全部 > │
├─────────────────────────────────┤
│ 🫀 👩 👶 │
│ 内科 妇科 儿科 │
│ 中医内科 中医妇科 中医儿科 │
│ │
│ 🦴 🧴 💉 │
│ 骨科 皮肤科 针灸科 │
│ 中医骨科 中医皮肤 针灸推拿 │
└─────────────────────────────────┘
```
- 3 列网格布局
- 6 个科室卡片
- 卡片背景为浅米色
- 点击进入科室详情
### 6. 医生推荐 (Doctors)
```
┌─────────────────────────────────┐
│ 名医推荐 查看全部 > │
├─────────────────────────────────┤
│ ┌─────────────────────────────┐ │
│ │ [头像] 张医生 [咨询] │ │
│ │ 专家 主任医师 │ │
│ │ 中医内科 │ │
│ │ ★★★★★ 4.9 (256) │ │
│ └─────────────────────────────┘ │
│ ┌─────────────────────────────┐ │
│ │ [头像] 李医生 [咨询] │ │
│ │ 副主任医师 │ │
│ │ 中医妇科 │ │
│ │ ★★★★★ 4.8 (189) │ │
│ └─────────────────────────────┘ │
└─────────────────────────────────┘
```
- 列表布局,每行一个医生
- 左侧: 头像 + 专家标签
- 中间: 医生信息(名字、职位、科室、评分)
- 右侧: 咨询按钮
- 点击进入医生详情
### 7. 健康资讯 (News)
```
┌─────────────────────────────────┐
│ 健康资讯 更多 > │
├─────────────────────────────────┤
│ [图] 春季养生:如何调理脾胃 │
│ 中医养生 2小时前 │
│ │
│ [图] 中医药在预防疾病中的作用 │
│ 医学资讯 4小时前 │
│ │
│ [图] 穴位按摩缓解颈椎疲劳 │
│ 健康指南 6小时前 │
└─────────────────────────────────┘
```
- 列表布局
- 左侧: 新闻图片
- 右侧: 标题、来源、时间
- 标题最多显示 2 行
## 交互设计
### 按钮状态
- **正常**: 棕色背景,白色文字
- **按下**: 背景变深,有缩放动画
- **禁用**: 灰色背景,不可点击
### 卡片交互
- **正常**: 浅色背景,细边框
- **按下**: 背景变深,有阴影效果
- **悬停**: 轻微缩放
### 动画效果
- **轮播**: 500ms 切换动画
- **卡片**: 300ms 过渡效果
- **按钮**: 100ms 按下效果
## 响应式设计
### 断点
- **小屏幕** (< 375px): 调整间距和字体
- **中屏幕** (375-414px): 标准设计
- **大屏幕** (> 414px): 增加间距
### 适配方案
- 使用 rpx 单位(响应式像素)
- 1rpx = 0.5px (在 375px 宽度下)
- 自动适配不同屏幕尺寸
## 字体规范
### 字体大小
- **标题**: 32rpx (主标题), 28rpx (副标题)
- **正文**: 24rpx (主要内容), 22rpx (次要内容)
- **辅助**: 20rpx (提示信息), 18rpx (标签)
### 字体权重
- **加粗**: 500-700 (标题、重要信息)
- **正常**: 400 (正文)
- **轻细**: 300 (辅助信息)
## 间距规范
### 外间距 (Margin)
- **大**: 30rpx (主要分区)
- **中**: 20rpx (卡片间距)
- **小**: 10rpx (元素间距)
### 内间距 (Padding)
- **大**: 30rpx (卡片内部)
- **中**: 20rpx (按钮、输入框)
- **小**: 10rpx (文字间距)
## 圆角规范
- **大**: 12rpx (卡片、按钮)
- **中**: 8rpx (小卡片、图片)
- **小**: 4rpx (标签、徽章)
- **圆形**: 50% (头像、通知徽章)
## 阴影规范
- **轻**: `0 2rpx 8rpx rgba(0, 0, 0, 0.08)`
- **中**: `0 4rpx 12rpx rgba(0, 0, 0, 0.12)`
- **重**: `0 8rpx 24rpx rgba(0, 0, 0, 0.15)`
## 图片规范
### 尺寸
- **轮播图**: 750x300px
- **医生头像**: 100x100rpx
- **新闻图片**: 140x100rpx
- **科室图标**: 48rpx (emoji)
### 处理
- 使用 `mode="aspectFill"` 保持宽高比
- 圆角处理: 8-12rpx
- 加载占位符: 灰色背景
## 无障碍设计
- 足够的色彩对比度
- 清晰的文字标签
- 合理的点击区域 (最小 44x44rpx)
- 支持屏幕阅读器
## 性能优化
- 图片懒加载
- 列表虚拟滚动
- 减少重排和重绘
- 使用 CSS 动画而非 JS 动画
## 扩展建议
### 可以添加的功能
1. **个性化推荐** - 根据用户历史推荐医生
2. **预约提醒** - 显示即将到来的预约
3. **健康数据** - 显示用户的健康指标
4. **在线客服** - 右下角浮窗
5. **活动促销** - 轮播中添加促销信息
### 可以优化的方面
1. **深色模式** - 支持系统深色模式
2. **多语言** - 支持多种语言
3. **主题切换** - 允许用户自定义主题色
4. **动画增强** - 添加更多微交互
5. **数据统计** - 追踪用户行为
## 总结
这个设计方案结合了中医文化特色和现代医疗应用的需求,通过合理的色彩、布局和交互设计,为用户提供专业、友好的医疗服务体验。
+24
View File
@@ -0,0 +1,24 @@
// 应用配置文件
// 根据环境设置API基础URL
let API_BASE_URL = 'http://localhost:8000/api'
// 如果是生产环境,修改为实际的API地址
if (typeof uni !== 'undefined') {
// 小程序环境
const accountInfo = uni.getAccountInfoSync()
if (accountInfo.miniProgram.envVersion === 'release') {
// 生产环境
API_BASE_URL = 'https://your-production-api.com/api'
} else if (accountInfo.miniProgram.envVersion === 'trial') {
// 体验版
API_BASE_URL = 'https://your-trial-api.com/api'
}
}
export default {
API_BASE_URL,
// 其他配置...
APP_NAME: '医疗小程序',
APP_VERSION: '1.0.0'
}
+3 -3
View File
@@ -1,5 +1,5 @@
import App from './App'
var baseUrl ='http://api.zzzhengyangtang.cn';
var baseUrl ='http://www.d.com';
// #ifndef VUE3
import Vue from 'vue'
import './uni.promisify.adaptor'
@@ -94,7 +94,7 @@ function apiUrl(promise, Loading = true) {
// 处理登录失效逻辑(补充完整)
if (data.code === -1) {
uni.removeStorageSync('token') // 删除token
uni.showToast({ title: '登录已失效,请重新登录', icon: 'none' })
//uni.showToast({ title: '登录已失效,请重新登录', icon: 'none' })
// 可选:跳转到登录页
// uni.redirectTo({ url: '/pages/login/login' })
}
@@ -102,7 +102,7 @@ function apiUrl(promise, Loading = true) {
resolve(data)
},
fail: (e) => {
uni.showToast({ title: '请求失败,请重试', icon: 'none' })
// uni.showToast({ title: '请求失败,请重试', icon: 'none' })
reject(e)
},
complete: (e) => {
+2 -2
View File
@@ -3,7 +3,7 @@
{
"path": "pages/index/index",
"style": {
"navigationBarTitleText": "uni-app"
"navigationBarTitleText": "甄养堂"
}
},
{
@@ -72,7 +72,7 @@
],
"tabBar": {
"color": "#7A7E83",
"selectedColor": "#3cc51f",
"selectedColor": "#FFA500",
"borderStyle": "black",
"backgroundColor": "#ffffff",
"list": [{
+459 -112
View File
@@ -1,153 +1,500 @@
<template>
<view class="container">
<view class='guide-box'>
<!-- <button @click="setCallingBell">setCallingBell</button> -->
<!-- <button @click="enableMuteMode">enableMuteMode</button> -->
<view class="single-box" v-for="(item, index) in entryInfos" :key="index" :id="index" @click='handleEntry'>
<image class="icon" mode="aspectFit" :src="item.icon" role="img"></image>
<view class="single-content">
<view class="label">{{ item.title }}</view>
<view class="desc">{{ item.desc }}</view>
<!-- 问诊服务 -->
<!-- 特色专科医师 -->
<view class="doctors-section">
<view class="section-header">
<view class="section-title-wrapper">
<view class="section-title">特色专科医师</view>
</view>
<view class="more-link" @click="navigateToAllDoctors">更多</view>
</view>
<view class="doctors-grid">
<view
v-for="doctor in doctors"
:key="doctor.id"
class="doctor-card"
@click="selectDoctor(doctor)"
>
<view class="doctor-avatar-wrapper">
<image :src="doctor.avatar" class="doctor-avatar" mode="aspectFill"></image>
<view class="avatar-frame"></view>
</view>
<view class="doctor-name">{{ doctor.name }}</view>
<view class="doctor-title">{{ doctor.title }}</view>
<view class="doctor-dept">{{ doctor.department || '内科' }}</view>
</view>
</view>
</view>
</view>
</template>
<script setup>
// import { TUICallKitAPI } from "../../TUICallKit/src/index";
// import { ref } from "vue";
const template = '1v1';
const entryInfos = [
{
icon: 'https://web.sdk.qcloud.com/component/miniApp/resources/audio-card.png',
title: '语音通话',
desc: '丢包率70%仍可正常语音通话',
navigateTo: '../calling/call?type=1',
},
{
icon: 'https://web.sdk.qcloud.com/component/miniApp/resources/video-card.png',
title: '视频通话',
desc: '丢包率50%仍可正常视频通话',
navigateTo: '../calling/call?type=2',
},
{
icon: 'https://web.sdk.qcloud.com/component/miniApp/resources/audio-card.png',
title: '多人语音通话',
desc: '丢包率70%仍可正常语音通话',
navigateTo: '../calling/groupCall?type=1',
},
{
icon: 'https://web.sdk.qcloud.com/component/miniApp/resources/video-card.png',
title: '多人视频通话',
desc: '丢包率50%仍可正常视频通话',
navigateTo: '../calling/groupCall?type=2',
},
];
// --------铃声相关接口测试-----------------
// let isMute = ref(false);
// const enableMuteMode = async () => {
// isMute.value = !isMute.value;
// await TUICallKitAPI.enableMuteMode(isMute.value);
// }
// const setCallingBell = async () => {
// await TUICallKitAPI.setCallingBell('mp2.mp3');
// }
const handleEntry = (e) => {
const url = entryInfos[e.currentTarget.id].navigateTo;
import { ref, onMounted } from 'vue'
import { getCurrentInstance } from 'vue'
const { proxy } = getCurrentInstance()
const doctors = ref([])
const loading = ref(false)
const selectDoctor = (doctor) => {
uni.navigateTo({
url,
});
url: `/pages/order/index?doctorId=${doctor.id}&doctorName=${doctor.name}`
})
}
const navigateToExperts = () => {
uni.navigateTo({
url: '/pages/experts/index'
})
}
const navigateToConsultation = () => {
uni.navigateTo({
url: '/pages/consultation/index'
})
}
const navigateToQuickConsult = () => {
uni.navigateTo({
url: '/pages/quick-consult/index'
})
}
const navigateToAllDoctors = () => {
uni.navigateTo({
url: '/pages/doctors/index'
})
}
const navigateToHome = () => {
// 已在首页
}
const navigateToMessages = () => {
uni.navigateTo({
url: '/pages/messages/index'
})
}
const navigateToProfile = () => {
uni.navigateTo({
url: '/pages/profile/index'
})
}
const fetchDoctors = async () => {
loading.value = true
try {
const response = await proxy.apiUrl({
url: '/api/doctor/lists',
method: 'GET',
data: {
page_no: 1,
page_size: 10
}
})
if (response && response.code === 1) {
const data = response.data
const doctorList = data.lists || []
doctors.value = doctorList
} else {
console.warn('获取医生列表失败:', response?.msg)
}
} catch (error) {
console.error('请求异常:', error)
} finally {
loading.value = false
}
}
onMounted(() => {
fetchDoctors()
})
</script>
<style>
<style scoped lang="scss">
.container {
/* background-image: url(https://mc.qcloudimg.com/static/img/7da57e0050d308e2e1b1e31afbc42929/bg.png); */
background: #F4F5F9;
background-repeat: no-repeat;
background-size: cover;
width: 100vw;
height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
box-sizing: border-box;
background: #f5f3f0;
min-height: 100vh;
padding-bottom: 120rpx;
}
.container .title {
// 古典卷轴横幅
.scroll-banner {
margin: 24rpx 16rpx;
display: flex;
align-items: center;
position: relative;
width: 100vw;
font-size: 18px;
color: #000000;
letter-spacing: 0;
text-align: center;
line-height: 28px;
font-weight: 600;
background: #FFFFFF;
margin-top: 3.8vh;
padding: 1.2vh 0;
}
.tips {
color: #ffffff;
font-size: 12px;
text-align: center;
.scroll-rod {
width: 32rpx;
height: 160rpx;
background: linear-gradient(180deg, #8B7355 0%, #6B5644 50%, #8B7355 100%);
border-radius: 16rpx;
position: relative;
box-shadow: 0 4rpx 8rpx rgba(0, 0, 0, 0.2);
&::before {
content: '';
position: absolute;
top: 16rpx;
left: 50%;
transform: translateX(-50%);
width: 40rpx;
height: 40rpx;
background: radial-gradient(circle, #D4AF37 0%, #B8941E 100%);
border-radius: 50%;
box-shadow: 0 2rpx 6rpx rgba(0, 0, 0, 0.3);
}
&::after {
content: '';
position: absolute;
bottom: 16rpx;
left: 50%;
transform: translateX(-50%);
width: 40rpx;
height: 40rpx;
background: radial-gradient(circle, #D4AF37 0%, #B8941E 100%);
border-radius: 50%;
box-shadow: 0 2rpx 6rpx rgba(0, 0, 0, 0.3);
}
}
.guide-box {
width: 100vw;
box-sizing: border-box;
padding: 16px;
.scroll-rod-left {
margin-right: -8rpx;
z-index: 2;
}
.scroll-rod-right {
margin-left: -8rpx;
z-index: 2;
}
.scroll-content {
flex: 1;
background: linear-gradient(135deg, #F5E6D3 0%, #E8D7C3 100%);
border: 3rpx solid #8B7355;
border-radius: 12rpx;
padding: 24rpx;
box-shadow: inset 0 2rpx 8rpx rgba(139, 115, 85, 0.2);
position: relative;
&::before {
content: '';
position: absolute;
top: 12rpx;
left: 12rpx;
right: 12rpx;
bottom: 12rpx;
border: 1rpx solid rgba(139, 115, 85, 0.3);
border-radius: 8rpx;
pointer-events: none;
}
}
.scroll-inner {
display: flex;
align-items: center;
gap: 20rpx;
position: relative;
z-index: 1;
}
.scroll-text {
flex: 1;
}
.scroll-title {
font-size: 32rpx;
font-weight: bold;
color: #3E2723;
margin-bottom: 8rpx;
font-family: 'STKaiti', 'KaiTi', serif;
}
.scroll-subtitle {
font-size: 24rpx;
color: #5D4037;
font-family: 'STKaiti', 'KaiTi', serif;
}
.scroll-image {
width: 200rpx;
height: 120rpx;
border-radius: 8rpx;
border: 2rpx solid #8B7355;
}
// 问诊服务
.services-section {
padding: 32rpx 16rpx;
}
.section-title {
font-size: 32rpx;
font-weight: bold;
color: #3E2723;
margin-bottom: 24rpx;
font-family: 'STKaiti', 'KaiTi', serif;
position: relative;
padding-left: 16rpx;
&::before {
content: '';
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
width: 6rpx;
height: 28rpx;
background: linear-gradient(180deg, #D4AF37 0%, #B8941E 100%);
border-radius: 3rpx;
}
}
.services-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 16rpx;
}
.service-card {
border-radius: 20rpx;
padding: 24rpx 16rpx;
display: flex;
flex-direction: column;
position: relative;
overflow: hidden;
min-height: 320rpx;
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.08);
}
.card-bg {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
opacity: 0.15;
z-index: 0;
}
.bg-decoration {
width: 100%;
height: 100%;
}
.card-content {
position: relative;
z-index: 1;
flex: 1;
display: flex;
flex-direction: column;
}
.single-box {
.service-expert {
background: linear-gradient(135deg, #FFF8DC 0%, #F5E6D3 100%);
border: 2rpx solid #E8D7C3;
}
.service-health {
background: linear-gradient(135deg, #E8F5E9 0%, #C8E6C9 100%);
border: 2rpx solid #A5D6A7;
}
.service-quick {
background: linear-gradient(135deg, #E0F2F1 0%, #B2DFDB 100%);
border: 2rpx solid #80CBC4;
}
.service-title {
font-size: 28rpx;
font-weight: bold;
color: #3E2723;
margin-bottom: 12rpx;
font-family: 'STKaiti', 'KaiTi', serif;
}
.service-desc {
font-size: 22rpx;
color: #5D4037;
line-height: 1.5;
margin-bottom: 16rpx;
flex: 1;
border-radius: 10px;
background-color: #ffffff;
margin-bottom: 16px;
}
.service-icon {
font-size: 56rpx;
text-align: center;
margin: 16rpx 0;
opacity: 0.8;
}
.service-btn {
color: #fff;
padding: 16rpx 24rpx;
border-radius: 32rpx;
font-size: 24rpx;
font-weight: bold;
text-align: center;
box-shadow: 0 4rpx 8rpx rgba(0, 0, 0, 0.15);
position: relative;
z-index: 1;
}
.btn-orange {
background: linear-gradient(135deg, #D2691E 0%, #CD853F 100%);
}
.btn-green {
background: linear-gradient(135deg, #2E7D32 0%, #388E3C 100%);
}
.btn-purple {
background: linear-gradient(135deg, #4A148C 0%, #6A1B9A 100%);
}
// 特色专科医师
.doctors-section {
padding: 32rpx 16rpx;
}
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24rpx;
}
.section-title-wrapper {
display: flex;
align-items: center;
gap: 8rpx;
}
.icon {
display: block;
width: 180px;
height: 144px;
.title-icon {
font-size: 28rpx;
}
.single-content {
padding: 36px 30px 36px 20px;
color: #333333;
}
.label {
display: block;
font-size: 18px;
color: #333333;
letter-spacing: 0;
.more-link {
font-size: 24rpx;
color: #D4AF37;
font-weight: 500;
}
.desc {
display: block;
font-size: 14px;
color: #333333;
letter-spacing: 0;
font-weight: 500;
.doctors-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 16rpx;
}
.logo-box {
position: absolute;
width: 100vw;
bottom: 36rpx;
.doctor-card {
background: #fff;
border-radius: 16rpx;
padding: 20rpx;
text-align: center;
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.08);
}
.logo {
width: 160rpx;
height: 44rpx;
.doctor-avatar-wrapper {
position: relative;
margin-bottom: 16rpx;
display: flex;
justify-content: center;
}
</style>
.doctor-avatar {
width: 120rpx;
height: 120rpx;
border-radius: 50%;
border: 3rpx solid #E8E8E8;
}
.avatar-frame {
position: absolute;
top: -4rpx;
left: 50%;
transform: translateX(-50%);
width: 128rpx;
height: 128rpx;
border: 2rpx solid rgba(212, 175, 55, 0.3);
border-radius: 50%;
pointer-events: none;
}
.doctor-name {
font-size: 26rpx;
font-weight: bold;
color: #3E2723;
margin-bottom: 6rpx;
}
.doctor-title {
font-size: 22rpx;
color: #757575;
margin-bottom: 4rpx;
}
.doctor-dept {
font-size: 20rpx;
color: #9E9E9E;
}
// 底部导航
.bottom-nav {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background: #fff;
border-top: 1rpx solid #E8E8E8;
display: flex;
justify-content: space-around;
padding: 16rpx 0 24rpx 0;
box-shadow: 0 -2rpx 8rpx rgba(0, 0, 0, 0.05);
z-index: 100;
}
.nav-item {
display: flex;
flex-direction: column;
align-items: center;
gap: 6rpx;
flex: 1;
&.active {
.nav-icon {
font-size: 40rpx;
}
.nav-text {
color: #D4AF37;
font-weight: bold;
}
}
}
.nav-icon {
font-size: 36rpx;
transition: all 0.3s ease;
}
.nav-text {
font-size: 22rpx;
color: #757575;
transition: all 0.3s ease;
}
</style>
+2 -2
View File
@@ -29,7 +29,7 @@
</div>
<!-- 订单卡片 -->
<div class="order-card">
<!-- <div class="order-card">
<div class="card-header">
<text class="title">我的订单</text>
<div class="more" @click="navigateTo('/pages/order/list')">
@@ -51,7 +51,7 @@
<text class="order-name">{{ item.name }}</text>
</div>
</div>
</div>
</div> -->
<!-- 快捷功能区 -->
<!-- <div class="quick-actions">
+82
View File
@@ -0,0 +1,82 @@
/**
* API 请求封装
* 使用 main.js 中定义的全局 apiUrl 方法
*/
/**
* 发送HTTP请求
* @param {string} url - 请求URL
* @param {string} method - 请求方法 (GET/POST)
* @param {object} data - 请求数据
* @param {boolean} loading - 是否显示加载中
* @returns {Promise}
*/
export const request = (url, method = 'POST', data = {}, loading = true) => {
// 获取全局 apiUrl 方法
const apiUrl = uni.$u?.apiUrl || (typeof apiUrl !== 'undefined' ? apiUrl : null)
const { proxy } = getCurrentInstance()
const instance = getCurrentInstance();
// 2. 从实例中获取全局 $url
const $url = instance.appContext.config.globalProperties.$url;
if (!apiUrl) {
// 降级方案:直接使用 uni.request
return new Promise((resolve, reject) => {
const token = uni.getStorageSync('token') || ''
const baseUrl = $url
uni.request({
url: `${baseUrl}${url}`,
method,
data,
header: {
'content-type': 'application/x-www-form-urlencoded',
'Authorization': token
},
success: (res) => {
resolve(res.data)
},
fail: (err) => {
reject(err)
}
})
})
}
// 使用全局 apiUrl 方法
return apiUrl({
url,
method,
data,
header: {
'content-type': 'application/x-www-form-urlencoded'
}
}, loading)
}
/**
* GET 请求
*/
export const get = (url, data = {}, loading = false) => {
return request(url, 'GET', data, loading)
}
/**
* POST 请求
*/
export const post = (url, data = {}, loading = true) => {
return request(url, 'POST', data, loading)
}
/**
* 医生相关API
*/
export const doctorApi = {
// 获取医生列表
getDoctorList: (params) => get('/api/doctor/lists', params, false),
// 获取医生详情
getDoctorDetail: (id) => get('/api/doctor/detail', { id }, false),
// 获取医生排班
getDoctorRoster: (doctorId, date) => get('/api/doctor/roster', { doctor_id: doctorId, date }, false)
}
+110
View File
@@ -0,0 +1,110 @@
/**
* 全局属性工具函数
* 用于在任何地方访问 main.js 中定义的全局属性
*/
import { getCurrentInstance } from 'vue'
/**
* 获取全局属性对象
* @returns {Object} 全局属性对象
*/
export const getGlobalProperties = () => {
try {
const instance = getCurrentInstance()
if (instance) {
return instance.appContext.config.globalProperties
}
} catch (e) {
console.warn('无法获取 getCurrentInstance:', e)
}
return {}
}
/**
* 获取 API 基础 URL
* @returns {string} API 基础 URL
*/
export const getBaseUrl = () => {
const globalProps = getGlobalProperties()
return globalProps.$url || 'http://api.zzzhengyangtang.cn'
}
/**
* 获取 apiUrl 方法
* @returns {Function} apiUrl 方法
*/
export const getApiUrl = () => {
const globalProps = getGlobalProperties()
return globalProps.apiUrl
}
/**
* 使用全局属性的 Composition API Hook
* @returns {Object} 包含全局属性的对象
*/
export const useGlobal = () => {
const globalProps = getGlobalProperties()
return {
// API 相关
baseUrl: globalProps.$url || 'http://api.zzzhengyangtang.cn',
apiUrl: globalProps.apiUrl,
// 应用相关(可选)
appName: globalProps.$appName,
version: globalProps.$version,
// 其他全局属性
...globalProps
}
}
/**
* 获取完整的 API URL
* @param {string} path - API 路径
* @returns {string} 完整的 API URL
*/
export const getFullUrl = (path) => {
const baseUrl = getBaseUrl()
return `${baseUrl}${path}`
}
/**
* 设置全局属性(谨慎使用)
* @param {string} key - 属性名
* @param {any} value - 属性值
*/
export const setGlobalProperty = (key, value) => {
try {
const instance = getCurrentInstance()
if (instance) {
instance.appContext.config.globalProperties[key] = value
return true
}
} catch (e) {
console.error('无法设置全局属性:', e)
}
return false
}
/**
* 获取特定的全局属性
* @param {string} key - 属性名
* @param {any} defaultValue - 默认值
* @returns {any} 属性值
*/
export const getGlobalProperty = (key, defaultValue = null) => {
const globalProps = getGlobalProperties()
return globalProps[key] !== undefined ? globalProps[key] : defaultValue
}
export default {
getGlobalProperties,
getBaseUrl,
getApiUrl,
useGlobal,
getFullUrl,
setGlobalProperty,
getGlobalProperty
}