feat: 患者端挂号预约流程改造与就诊人选择
- 挂号预约:register-info 流程重构、确认步骤/患者选择步骤组件、我的预约(myappiont)页对接 - 接口:register.js 补充挂号相关接口,api.js 调整 - 就诊人:claim-patient-modal 复用弹窗 - 医生信息:doctor-detail/doctor-userinfo 配合排班展示 - 规范:新增 Api-Module-Split.mdc 接口模块拆分规则
This commit is contained in:
37
.cursor/rules/Api-Module-Split.mdc
Normal file
37
.cursor/rules/Api-Module-Split.mdc
Normal file
@@ -0,0 +1,37 @@
|
||||
---
|
||||
description: 接口迁移到 xk-api 后必须拆出独立模块文件,禁止再往 api.js 大文件里塞
|
||||
alwaysApply: true
|
||||
---
|
||||
|
||||
# API 模块拆分规则(xk-api 迁移必读)
|
||||
|
||||
`request/api/api.js`(约 600+ 行大杂烩)是历史遗留的 Yii 接口聚合文件。
|
||||
**任何迁移到 xk-api 的接口、以及所有新增的 xk-api 接口,一律不得写进 `api.js`**,必须提取/新建到 `request/api/` 下的独立业务模块文件。
|
||||
|
||||
## 必须做
|
||||
|
||||
1. **按业务域建独立文件**:`request/api/<module>.js`,小驼峰命名(已有先例:`patient.js`、`order.js`、`register.js`、`product.js`、`medicalRecord.js`、`specialPrescription.js`)
|
||||
2. **统一走 http 封装**:`import { post, get } from './http'`,第三参传 `3`(`HttpUrlMap[3] = '/xkApi'`),不要手拼 `/xkApi` 字符串到 `http()` 老封装上
|
||||
3. **文件头注释标明后端与取值方式**,每个函数注释标 HTTP 方法与路径,例如:
|
||||
|
||||
```js
|
||||
import { post, get } from './http'
|
||||
|
||||
/**
|
||||
* 就诊人相关接口统一走 xk-api(type=3)
|
||||
* 取值:unwrapXkApi → code / result / message
|
||||
*/
|
||||
/** 就诊人列表 xk-api POST /patient/list */
|
||||
export async function getPatientListApi(params = {}) {
|
||||
return await post('/patient/list', params, 3)
|
||||
}
|
||||
```
|
||||
|
||||
4. **迁移某接口时**(Yii → xk-api):把它从 `api.js` 挪到对应模块文件,`api.js` 里原函数直接删除(调用方改 import),确实来不及改完所有调用方时才允许在 `api.js` 保留一行 re-export 并标 `@deprecated`
|
||||
5. 页面取值统一 `unwrapXkApi`(见 Api-Response.mdc)
|
||||
|
||||
## 禁止做
|
||||
|
||||
- 禁止在 `api.js` 中新增任何函数(包括 Yii 接口——新 Yii 接口也应放模块文件)
|
||||
- 禁止新代码再用 `normalizeXkApiResponse` 这类「把 xk-api 响应抹成 errcode/data」的兼容层;那是存量代码的过渡产物
|
||||
- 禁止一个模块文件里混装两个后端的接口而不加注释区分;同一文件确需并存时,每个函数注释必须标明 `// xk-api` 或 `// Yii`
|
||||
@@ -11,3 +11,5 @@ alwaysApply: true
|
||||
6. 微信小程序 `.vue` 模板(会编译成 WXML)禁止使用 `??`、`?.` 等现代运算符,否则会报 `unexpected token '?'`;模板里用 `||` / 显式判断,脚本里可用 `??`/`?.`
|
||||
7. 微信小程序模板里 `:class` / `:style` 禁止写 `fn(arg)` 方法调用(如 `:class="sexClass(p)"` 会编译失败);用对象/数组字面量(如 `:class="{ male: p.sex == 1 }"`),或把结果先算进 data/computed 再绑定
|
||||
8. 接口取值:写代码时先确认接口是 xk-api 还是 Yii,再固定用 `code/result/message` 或 `errcode/data/msg`(可用 `unwrapXkApi` / `unwrapYiiApi`);禁止运行时猜测、禁止 `result || data`(详见 Api-Response.mdc)
|
||||
9. 卡片强调样式:禁止用左侧竖色条(`border-left` 色条/左侧色块)做卡片强调或分类标识,统一用「1px 同色系边框 + 同色低透明度外发光(box-shadow 微光)」;历史页面暂不强制回改,新增/改动的卡片必须遵守
|
||||
10. API 模块拆分:迁移到 xk-api 的接口和所有新增接口必须放 `request/api/<module>.js` 独立模块文件,禁止再往 `request/api/api.js` 大文件里新增(详见 Api-Module-Split.mdc)
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<script>
|
||||
import { claimPatientApi } from '@/request/api/patient.js'
|
||||
import { unwrapXkApi } from '@/utils/api-response.js'
|
||||
import { registePay } from '@/request/api/api.js'
|
||||
import { registePay } from '@/request/api/register.js'
|
||||
|
||||
/**
|
||||
* 扫码认领医生创建的就诊人(居中 u-modal)
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import http from './request'
|
||||
import { get } from './http'
|
||||
import {
|
||||
getPatientListApi,
|
||||
savePatientApi,
|
||||
@@ -113,11 +112,8 @@ export async function commentListTop(params) {
|
||||
return data
|
||||
}
|
||||
|
||||
// 医生 挂号信息
|
||||
export async function registerList(params) {
|
||||
let data = await http('/oldApi/v1/register/doc-register-info', params)
|
||||
return data
|
||||
}
|
||||
// 挂号域接口(registerList/registerCalendar/registe/registeInfo/registePay/registeUnpay/registeCancle)
|
||||
// 已按 Api-Module-Split 规则迁至 ./register.js,调用方请从 '@/request/api/register.js' 导入
|
||||
|
||||
|
||||
/** 就诊人列表(xk-api POST /patient/list) */
|
||||
@@ -174,42 +170,6 @@ export async function userEidtPatient(params) {
|
||||
return normalizeXkApiResponse(await getPatientHealthInfoApi(data))
|
||||
}
|
||||
|
||||
// 挂号
|
||||
export async function registe(params) {
|
||||
let data = await http('/oldApi/v1/register/register', params)
|
||||
return data
|
||||
}
|
||||
|
||||
// 挂号详情
|
||||
export async function registeInfo(params) {
|
||||
let data = await http('/oldApi/v1/register/register-detail', params)
|
||||
return data
|
||||
}
|
||||
|
||||
// 支付挂号(xk-api register/pay,易票联)
|
||||
export async function registePay(params) {
|
||||
let data = await http('/xkApi/register/pay', params)
|
||||
return normalizeXkApiResponse(data)
|
||||
}
|
||||
|
||||
// 取消挂号
|
||||
export async function registeUnpay(params) {
|
||||
let data = await http('/oldApi/v1/register/cancel', params)
|
||||
return data
|
||||
}
|
||||
|
||||
// 挂号退款
|
||||
export async function registeCancle(params) {
|
||||
let data = await http('/oldApi/v1/register/refund', params)
|
||||
return data
|
||||
}
|
||||
|
||||
// 挂号列表
|
||||
export async function registeList(params) {
|
||||
let data = await http('/oldApi/v1/register/list', params)
|
||||
return data
|
||||
}
|
||||
|
||||
// 设置 服务协议
|
||||
export async function service(params) {
|
||||
let data = await http('/oldApi/v1/other/service', params)
|
||||
|
||||
@@ -1,8 +1,30 @@
|
||||
import http from './request'
|
||||
import {post} from './http'
|
||||
import { unwrapXkApi } from '@/utils/api-response.js'
|
||||
|
||||
/**
|
||||
* 获取挂号记录列表
|
||||
* 挂号域接口统一模块(除 registerList 外均为 xk-api)
|
||||
* 取值:unwrapXkApi → code / result / message
|
||||
*
|
||||
* 说明:registe/registeInfo 等函数迁自 api.js,保持「http(url, { method, data })」
|
||||
* 老签名不动,避免改动全部调用方的传参结构;新增接口请用 post(url, params, 3) 新封装。
|
||||
*/
|
||||
|
||||
/**
|
||||
* 存量页面兼容:把 xk-api 的 code/result 抹成旧版 errcode/data
|
||||
* @deprecated 仅供 registePay 的存量调用方(读 res.data.errcode/data)过渡使用,
|
||||
* 新代码一律直接按 code/result 取值或用 unwrapXkApi,禁止再新增本函数的调用
|
||||
*/
|
||||
function normalizeXkApiResponse(res) {
|
||||
if (res && res.data && (res.data.code === 0 || res.data.code === '0')) {
|
||||
res.data.errcode = 0;
|
||||
res.data.data = res.data.result;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取挂号记录列表 xk-api POST /register/list
|
||||
* @param params { patient_id: number, store_id: number, page: number }
|
||||
* @returns {Promise<*>}
|
||||
*/
|
||||
@@ -11,7 +33,7 @@ export async function getRegisterListApi(params) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取待支付挂号列表(首页提醒用)
|
||||
* 获取待支付挂号列表(首页提醒用) xk-api POST /register/list
|
||||
* @param params
|
||||
* @returns {Promise<Array>}
|
||||
*/
|
||||
@@ -30,3 +52,69 @@ export async function fetchUnpaidRegisterListApi(params = {}) {
|
||||
const list = (payload && payload.list) || [];
|
||||
return list.filter((item) => item.status === 0 && item.is_cancel !== 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 医生号源日历 xk-api GET /register/schedule-calendar(登录态)
|
||||
* 返回 { register_status, register_price, book_days, days[] },days 含每天出诊/时段/余号
|
||||
* 读值按 xk-api 形态:res.data.code == 0 → res.data.result
|
||||
*/
|
||||
export async function registerCalendar(params) {
|
||||
let data = await http('/xkApi/register/schedule-calendar', params)
|
||||
return data
|
||||
}
|
||||
|
||||
/**
|
||||
* 医生号源信息(游客态专用)
|
||||
* URL 写老 Yii doc-register-info,但 request.js 会在 guest 模式下重写到
|
||||
* xk-api /guest/guest-doctor/doc-register-info(响应 code/result 形态,result 带 calendar)
|
||||
* @deprecated 登录态请勿使用(会打到老 Yii),登录态一律走 registerCalendar
|
||||
*/
|
||||
export async function registerList(params) {
|
||||
let data = await http('/oldApi/v1/register/doc-register-info', params)
|
||||
return data
|
||||
}
|
||||
|
||||
/**
|
||||
* 挂号建单 xk-api POST /register/create(迁自老 Yii /v1/register/register)
|
||||
* 入参新增 visit_date(Y-m-d) + slot_id(0=全天兜底);返回 result{register_id, isHas}
|
||||
* 读值按 xk-api 形态:res.data.code == 0 → res.data.result
|
||||
*/
|
||||
export async function registe(params) {
|
||||
let data = await http('/xkApi/register/create', params)
|
||||
return data
|
||||
}
|
||||
|
||||
/**
|
||||
* 挂号详情 xk-api POST /register/detail(迁自老 Yii /v1/register/register-detail)
|
||||
* 返回 result 为单对象(老接口是 data.list[0]),附 appointment 预约日期/时段信息
|
||||
*/
|
||||
export async function registeInfo(params) {
|
||||
let data = await http('/xkApi/register/detail', params)
|
||||
return data
|
||||
}
|
||||
|
||||
/**
|
||||
* 挂号支付 xk-api POST /register/pay(易票联)
|
||||
* 存量调用方读 errcode/data,这里保留 normalize 过渡;0 元单返回 data.is_paid=1
|
||||
*/
|
||||
export async function registePay(params) {
|
||||
let data = await http('/xkApi/register/pay', params)
|
||||
return normalizeXkApiResponse(data)
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消挂号 xk-api POST /register/cancel(仅未支付单,迁自老 Yii /v1/register/cancel)
|
||||
*/
|
||||
export async function registeUnpay(params) {
|
||||
let data = await http('/xkApi/register/cancel', params)
|
||||
return data
|
||||
}
|
||||
|
||||
/**
|
||||
* 挂号退款 xk-api POST /register/refund(已支付未接诊单,迁自老 Yii /v1/register/refund)
|
||||
* 注意:老 Yii refund 同时兼容未付取消,xk-api 拆成 cancel/refund 两个接口,页面按 status 分流
|
||||
*/
|
||||
export async function registeCancle(params) {
|
||||
let data = await http('/xkApi/register/refund', params)
|
||||
return data
|
||||
}
|
||||
|
||||
@@ -39,7 +39,42 @@
|
||||
<!-- 到店医生 预约挂号:聊天室只读进入时不展示挂号入口 -->
|
||||
<view class="appoin" v-if="!readonly">
|
||||
<view class="title">预约挂号</view>
|
||||
<view class="list">
|
||||
<!-- 新版排班日历:日期横条 + 时段选择(数据来自 xk-api 排班日历接口) -->
|
||||
<view class="list" v-if="calendarDays.length > 0">
|
||||
<scroll-view scroll-x class="day-scroll" :show-scrollbar="false">
|
||||
<view class="day-strip">
|
||||
<view class="day-item" v-for="(day, idx) in calendarDays" :key="day.date"
|
||||
:class="{ active: idx === selectedDayIndex, disabled: !day.is_work || day.is_full }"
|
||||
@click="selectDay(idx)">
|
||||
<view class="day-week">{{day.week}}</view>
|
||||
<view class="day-date">{{day.date_text}}</view>
|
||||
<view class="day-state" :class="{ rest: !day.is_work, full: day.is_work && day.is_full }">{{day.state_text}}</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
<!-- 时段选择:全天单时段自动选中;上下午/自定义时段展示可选卡片 -->
|
||||
<view class="slot-list" v-if="selectedDay && selectedDay.is_work && selectedDay.slots.length > 1">
|
||||
<view class="slot-item" v-for="slot in selectedDay.slots" :key="slot.slot_id"
|
||||
:class="{ active: slot.slot_id === selectedSlotId, disabled: slot.is_full || slot.is_expired }"
|
||||
@click="selectSlot(slot)">
|
||||
<view class="slot-name">{{slot.slot_name}}</view>
|
||||
<view class="slot-time">{{slot.start_time}}-{{slot.end_time}}</view>
|
||||
<view class="slot-left" :class="{ full: slot.is_full || slot.is_expired }">{{slot.state_text}}</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="rest-tip" v-if="selectedDay && !selectedDay.is_work">该日医生休诊,请选择其他日期</view>
|
||||
<view class="yard book-bar">
|
||||
<view class="stores">
|
||||
<view class="times" v-if="selectedSlotLabel">{{selectedSlotLabel}}</view>
|
||||
<text class="price">{{registList.register_price}}</text>
|
||||
</view>
|
||||
<view class="btn" :class="{ active: !canBook }" @click="goAdd">
|
||||
{{canBook ? '挂号' : '不可约'}}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 旧版回退:日历加载失败或医生停诊时展示今日余号 -->
|
||||
<view class="list" v-else>
|
||||
<view class="yard">
|
||||
<view class="stores">
|
||||
<view class="times">{{registList.time}}</view>
|
||||
@@ -100,10 +135,13 @@
|
||||
import {
|
||||
commentList,
|
||||
doctorDetail,
|
||||
registerList,
|
||||
storeChange,
|
||||
userList
|
||||
} from '@/request/api/api'
|
||||
import {
|
||||
registerCalendar,
|
||||
registerList
|
||||
} from '@/request/api/register.js'
|
||||
import { isGuestMode, isLoggedIn, saveRedirectInfo, buildUrlWithParams } from '@/common/utils/auth.js';
|
||||
export default {
|
||||
data() {
|
||||
@@ -123,8 +161,41 @@
|
||||
rType: '0', // 挂号类型
|
||||
// 聊天室等场景只读查看:隐藏挂号按钮区
|
||||
readonly: false,
|
||||
// 排班日历:未来 N 天出诊/时段/余号(xk-api schedule-calendar / guest doc-register-info 的 calendar)
|
||||
calendarDays: [],
|
||||
selectedDayIndex: 0, // 当前选中日期下标
|
||||
selectedSlotId: -1, // 当前选中时段ID(-1=未选,0=全天兜底)
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
/** 当前选中的日期对象 */
|
||||
selectedDay() {
|
||||
return this.calendarDays[this.selectedDayIndex] || null;
|
||||
},
|
||||
/** 当前选中的时段对象 */
|
||||
selectedSlot() {
|
||||
if (!this.selectedDay || this.selectedSlotId < 0) return null;
|
||||
const list = this.selectedDay.slots || [];
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
if (list[i].slot_id === this.selectedSlotId) return list[i];
|
||||
}
|
||||
return null;
|
||||
},
|
||||
/** 选中信息文案:日期 + 时段名 + 起止时间 */
|
||||
selectedSlotLabel() {
|
||||
if (!this.selectedDay) return '';
|
||||
if (!this.selectedSlot) return this.selectedDay.date_text + ' ' + this.selectedDay.week;
|
||||
const s = this.selectedSlot;
|
||||
const timeText = s.start_time === '00:00' && s.end_time === '23:59' ? '' : ' ' + s.start_time + '-' + s.end_time;
|
||||
return this.selectedDay.date_text + ' ' + this.selectedDay.week + ' ' + s.slot_name + timeText;
|
||||
},
|
||||
/** 是否可预约:选中日出诊且所选时段未满未过时 */
|
||||
canBook() {
|
||||
if (!this.selectedDay || !this.selectedDay.is_work) return false;
|
||||
const s = this.selectedSlot;
|
||||
return !!(s && !s.is_full && !s.is_expired);
|
||||
},
|
||||
},
|
||||
onLoad(e) {
|
||||
console.log(e, 'e');
|
||||
this.readonly = e.readonly === '1' || e.readonly === 1;
|
||||
@@ -185,6 +256,62 @@
|
||||
url: "/subPackages/doctor/doctor-info?id=" + this.id
|
||||
})
|
||||
},
|
||||
// 选择预约日期:切换后默认选中该日第一个可约时段
|
||||
selectDay(idx) {
|
||||
this.selectedDayIndex = idx;
|
||||
this.autoSelectSlot();
|
||||
},
|
||||
// 选择时段:约满/已过时的不可选
|
||||
selectSlot(slot) {
|
||||
if (slot.is_full || slot.is_expired) return;
|
||||
this.selectedSlotId = slot.slot_id;
|
||||
},
|
||||
// 自动选中当前日期下第一个可约时段(全天单时段直接选中)
|
||||
autoSelectSlot() {
|
||||
this.selectedSlotId = -1;
|
||||
const day = this.calendarDays[this.selectedDayIndex];
|
||||
if (!day || !day.is_work) return;
|
||||
const list = day.slots || [];
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
if (!list[i].is_full && !list[i].is_expired) {
|
||||
this.selectedSlotId = list[i].slot_id;
|
||||
return;
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 预处理日历数据:补充展示文案(MM-DD、余号/约满/休诊),
|
||||
* 并默认定位到第一个可约日期。文案预计算是因为小程序模板禁止 :class/插值调方法
|
||||
*/
|
||||
applyCalendar(calendar) {
|
||||
const days = (calendar && calendar.days) || [];
|
||||
const processed = days.map((day) => {
|
||||
const dateParts = (day.date || '').split('-');
|
||||
const dateText = dateParts.length === 3 ? dateParts[1] + '-' + dateParts[2] : day.date;
|
||||
let stateText = '休诊';
|
||||
if (day.is_work) {
|
||||
stateText = day.is_full ? '约满' : '余' + day.left;
|
||||
}
|
||||
const slots = (day.slots || []).map((slot) => {
|
||||
let slotState = '余' + slot.left;
|
||||
if (slot.is_expired) slotState = '已过时';
|
||||
else if (slot.is_full) slotState = '约满';
|
||||
return Object.assign({}, slot, { state_text: slotState });
|
||||
});
|
||||
return Object.assign({}, day, { date_text: dateText, state_text: stateText, slots: slots });
|
||||
});
|
||||
this.calendarDays = processed;
|
||||
// 默认选第一个可约日期(出诊且未约满),都不可约时停在今天
|
||||
let idx = 0;
|
||||
for (let i = 0; i < processed.length; i++) {
|
||||
if (processed[i].is_work && !processed[i].is_full) {
|
||||
idx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.selectedDayIndex = idx;
|
||||
this.autoSelectSlot();
|
||||
},
|
||||
// 挂号
|
||||
goAdd() {
|
||||
// 只读模式不允许挂号
|
||||
@@ -206,8 +333,19 @@
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.num == 0) {
|
||||
|
||||
// 新版日历:校验所选日期时段可约
|
||||
if (this.calendarDays.length > 0) {
|
||||
if (!this.canBook) {
|
||||
uni.showToast({
|
||||
title: '该日期暂无可约号源',
|
||||
duration: 2000,
|
||||
icon: "none"
|
||||
});
|
||||
return
|
||||
}
|
||||
} else if (this.num == 0) {
|
||||
// 旧版回退:仅校验今日余号
|
||||
uni.showToast({
|
||||
title: '暂无号源',
|
||||
duration: 2000,
|
||||
@@ -223,9 +361,13 @@
|
||||
return
|
||||
}
|
||||
|
||||
uni.navigateTo({
|
||||
url: "/subPackages/doctor/doctor-userinfo?id=" + this.doctorID + "&r_type=" + this.rType,
|
||||
})
|
||||
// 携带预约日期+时段跳转建单流程(旧回退场景不带参,后端默认今天全天)
|
||||
let url = "/subPackages/doctor/doctor-userinfo?id=" + this.doctorID + "&r_type=" + this.rType;
|
||||
if (this.calendarDays.length > 0 && this.selectedDay && this.selectedSlot) {
|
||||
url += "&visit_date=" + this.selectedDay.date + "&slot_id=" + this.selectedSlot.slot_id
|
||||
+ "&slot_label=" + encodeURIComponent(this.selectedSlotLabel);
|
||||
}
|
||||
uni.navigateTo({ url: url })
|
||||
|
||||
},
|
||||
// 评价
|
||||
@@ -285,31 +427,70 @@
|
||||
})
|
||||
},
|
||||
|
||||
// 预约挂号
|
||||
/**
|
||||
* 预约挂号号源:登录态走 xk-api 排班日历接口(schedule-calendar),
|
||||
* 游客态仍调 doc-register-info(请求层自动重写到 xk-api guest 版,响应带 calendar 字段)
|
||||
* 两者都返回未来 N 天出诊/时段/余号,用 applyCalendar 统一渲染
|
||||
*/
|
||||
getRegist() {
|
||||
registerList({
|
||||
method: "post",
|
||||
const isGuest = isGuestMode() && !uni.getStorageSync('token');
|
||||
if (isGuest) {
|
||||
// 游客:doc-register-info(xk-api guest 版),code/result 形态,result 带 calendar
|
||||
registerList({
|
||||
method: "post",
|
||||
data: {
|
||||
service_user_id: this.doctorID,
|
||||
store_id: uni.getStorageSync('store_id') || '11001',
|
||||
}
|
||||
}).then((res) => {
|
||||
if (res.data.code === 0 || res.data.code === '0') {
|
||||
const responseData = res.data.result || {};
|
||||
this.registList = {
|
||||
time: responseData.time || '',
|
||||
register_price: responseData.register_price || 0,
|
||||
left_num: parseInt(responseData.left_num) || 0,
|
||||
register_num: parseInt(responseData.register_num) || 0,
|
||||
register_status: responseData.register_status || 1
|
||||
};
|
||||
this.num = this.registList.left_num;
|
||||
if (responseData.register_status == 1 && responseData.calendar) {
|
||||
this.applyCalendar(responseData.calendar);
|
||||
} else {
|
||||
this.calendarDays = [];
|
||||
}
|
||||
}
|
||||
})
|
||||
return;
|
||||
}
|
||||
// 登录:xk-api 排班日历(code/result 形态)
|
||||
registerCalendar({
|
||||
method: "get",
|
||||
data: {
|
||||
service_user_id: this.doctorID,
|
||||
doctor_id: this.doctorID,
|
||||
store_id: uni.getStorageSync('store_id') || '11001',
|
||||
}
|
||||
}).then((res) => {
|
||||
// console.log(res, 'reg');
|
||||
const isGuest = isGuestMode();
|
||||
const isNewFormat = isGuest && (res.data.code === 0 || res.data.code === '0');
|
||||
const isOldFormat = !isGuest && (res.data.errcode === 0 || res.data.errcode === '0');
|
||||
|
||||
if (isNewFormat || isOldFormat) {
|
||||
const responseData = isGuest && res.data.result ? res.data.result : res.data.data;
|
||||
// 确保所有字段都有默认值,避免显示 undefined
|
||||
if (res.data.code === 0 || res.data.code === '0') {
|
||||
const responseData = res.data.result || {};
|
||||
// 老字段兜底:日历模式下 left_num 汇总所有可约天余号,供旧回退区与 goAdd 老校验使用
|
||||
let totalLeft = 0;
|
||||
const days = (responseData.days || []);
|
||||
for (let i = 0; i < days.length; i++) {
|
||||
totalLeft += days[i].left || 0;
|
||||
}
|
||||
this.registList = {
|
||||
time: responseData.time || '',
|
||||
time: '',
|
||||
register_price: responseData.register_price || 0,
|
||||
left_num: parseInt(responseData.left_num) || 0,
|
||||
register_num: parseInt(responseData.register_num) || 0,
|
||||
register_status: responseData.register_status || 1
|
||||
left_num: totalLeft,
|
||||
register_num: 0,
|
||||
register_status: responseData.register_status || 0
|
||||
};
|
||||
this.num = this.registList.left_num;
|
||||
this.num = totalLeft;
|
||||
if (responseData.register_status == 1) {
|
||||
this.applyCalendar(responseData);
|
||||
} else {
|
||||
this.calendarDays = [];
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
@@ -574,6 +755,126 @@
|
||||
background: #E0E1E3;
|
||||
color: #232323;
|
||||
}
|
||||
|
||||
// 日期横条:横向滚动的未来 N 天卡片
|
||||
.day-scroll {
|
||||
width: 670rpx;
|
||||
white-space: nowrap;
|
||||
|
||||
.day-strip {
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.day-item {
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 128rpx;
|
||||
padding: 16rpx 0;
|
||||
margin-right: 16rpx;
|
||||
border-radius: 12rpx;
|
||||
border: 2rpx solid #E2E8F0;
|
||||
background: #FFFFFF;
|
||||
|
||||
.day-week {
|
||||
font-size: 26rpx;
|
||||
font-weight: 600;
|
||||
color: #1E293B;
|
||||
}
|
||||
|
||||
.day-date {
|
||||
font-size: 24rpx;
|
||||
color: #64748B;
|
||||
margin: 6rpx 0;
|
||||
}
|
||||
|
||||
.day-state {
|
||||
font-size: 22rpx;
|
||||
color: #4175EE;
|
||||
|
||||
&.rest {
|
||||
color: #94A3B8;
|
||||
}
|
||||
|
||||
&.full {
|
||||
color: #F44336;
|
||||
}
|
||||
}
|
||||
|
||||
&.active {
|
||||
border-color: #4175EE;
|
||||
background: rgba(65, 117, 238, 0.08);
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
opacity: 0.55;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 时段卡片:上下午/自定义时段单选
|
||||
.slot-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin-top: 20rpx;
|
||||
|
||||
.slot-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
width: 208rpx;
|
||||
padding: 14rpx 0;
|
||||
margin: 0 16rpx 16rpx 0;
|
||||
border-radius: 12rpx;
|
||||
border: 2rpx solid #E2E8F0;
|
||||
background: #FFFFFF;
|
||||
|
||||
.slot-name {
|
||||
font-size: 26rpx;
|
||||
font-weight: 600;
|
||||
color: #1E293B;
|
||||
}
|
||||
|
||||
.slot-time {
|
||||
font-size: 22rpx;
|
||||
color: #64748B;
|
||||
margin: 4rpx 0;
|
||||
}
|
||||
|
||||
.slot-left {
|
||||
font-size: 22rpx;
|
||||
color: #4175EE;
|
||||
|
||||
&.full {
|
||||
color: #94A3B8;
|
||||
}
|
||||
}
|
||||
|
||||
&.active {
|
||||
border-color: #4175EE;
|
||||
background: rgba(65, 117, 238, 0.08);
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
opacity: 0.55;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.rest-tip {
|
||||
margin-top: 20rpx;
|
||||
font-size: 26rpx;
|
||||
color: #94A3B8;
|
||||
text-align: center;
|
||||
padding: 24rpx 0;
|
||||
}
|
||||
|
||||
.book-bar {
|
||||
margin-top: 20rpx;
|
||||
padding-top: 20rpx;
|
||||
border-top: 2rpx solid #F1F5F9;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,9 @@
|
||||
:register-type-prop="registerType"
|
||||
:initial-r-info="rInfo"
|
||||
:delegate-store-id-prop="delegateStoreId"
|
||||
:visit-date-prop="visitDate"
|
||||
:slot-id-prop="slotId"
|
||||
:slot-label-prop="slotLabel"
|
||||
@register-created="onRegisterCreated"
|
||||
/>
|
||||
</view>
|
||||
@@ -69,6 +72,10 @@ export default {
|
||||
stepDirection: 'forward',
|
||||
transitionPhase: '',
|
||||
isStepTransitioning: false,
|
||||
// 预约日期(Y-m-d)+ 时段ID + 展示文案:由医生详情页排班日历选择后带入,建单时传给 xk-api
|
||||
visitDate: '',
|
||||
slotId: 0,
|
||||
slotLabel: '',
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@@ -172,6 +179,10 @@ export default {
|
||||
this.isStepTransitioning = false;
|
||||
this.transitionPhase = '';
|
||||
this.doctorId = e.id || '';
|
||||
// 医生详情页排班日历选择的预约日期/时段(不传则后端默认今天全天)
|
||||
this.visitDate = e.visit_date || '';
|
||||
this.slotId = Number(e.slot_id || 0);
|
||||
this.slotLabel = e.slot_label ? decodeURIComponent(e.slot_label) : '';
|
||||
if (e.r_type) {
|
||||
this.registerType = e.r_type;
|
||||
if (e.r_info) {
|
||||
|
||||
@@ -51,6 +51,10 @@
|
||||
<view class="list_name">预约序号</view>
|
||||
<text>{{item.order_number}}</text>
|
||||
</view>
|
||||
<view class="info" v-if="item.appointment && item.appointment.visit_date">
|
||||
<view class="list_name">预约时间</view>
|
||||
<text>{{ appointmentText(item.appointment) }}</text>
|
||||
</view>
|
||||
<TransferPrescriptionCard
|
||||
v-if="item.is_from_transfer == 1 && item.transfer_prescription"
|
||||
:transfer-data="item.transfer_prescription"
|
||||
@@ -91,6 +95,14 @@
|
||||
if (status == 7) return '已拒诊'
|
||||
return ''
|
||||
},
|
||||
/** 预约时间文案:日期+时段名,全天时段(00:00-23:59)不显示起止时间 */
|
||||
appointmentText(appt) {
|
||||
let text = appt.visit_date + ' ' + (appt.slot_name || '')
|
||||
if (appt.start_time && appt.end_time && !(appt.start_time === '00:00' && appt.end_time === '23:59')) {
|
||||
text += ' ' + appt.start_time + '-' + appt.end_time
|
||||
}
|
||||
return text
|
||||
},
|
||||
getInfo(id) {
|
||||
uni.navigateTo({
|
||||
url: '/subPackages/register/register-info?id=' + id + '&type=' + 1
|
||||
|
||||
@@ -48,9 +48,12 @@
|
||||
</view>
|
||||
<view class="item">
|
||||
<view class="aapoint">
|
||||
预计就诊时间
|
||||
预约时间
|
||||
</view>
|
||||
<view class="detail">
|
||||
<view class="detail" v-if="appointmentText">
|
||||
{{appointmentText}}
|
||||
</view>
|
||||
<view class="detail" v-else>
|
||||
{{registList.created_at}}
|
||||
</view>
|
||||
</view>
|
||||
@@ -150,10 +153,12 @@
|
||||
|
||||
<script>
|
||||
import {
|
||||
registeInfo,
|
||||
registePay,
|
||||
service
|
||||
} from '@/request/api/api';
|
||||
import {
|
||||
registeInfo,
|
||||
registePay
|
||||
} from '@/request/api/register.js';
|
||||
import { createRoomByDoctorIdApi, sendToUserApi } from '@/request/api/im';
|
||||
import { checkDev } from '@/utils/utils';
|
||||
import { genOrderApi } from '@/request/api/order';
|
||||
@@ -209,6 +214,16 @@
|
||||
shouldEnablePageBackGuard() {
|
||||
return !(this.registList && this.registList.is_pay == 1);
|
||||
},
|
||||
/** 预约时间文案:占号信息(日期+时段),全天时段不显示起止时间;老单无占号返回空回退创建时间 */
|
||||
appointmentText() {
|
||||
const appt = this.registList && this.registList.appointment;
|
||||
if (!appt || !appt.visit_date) return '';
|
||||
let text = appt.visit_date + ' ' + (appt.slot_name || '');
|
||||
if (appt.start_time && appt.end_time && !(appt.start_time === '00:00' && appt.end_time === '23:59')) {
|
||||
text += ' ' + appt.start_time + '-' + appt.end_time;
|
||||
}
|
||||
return text;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
'registList.is_pay'(val) {
|
||||
@@ -460,6 +475,7 @@
|
||||
getInfo() {
|
||||
const that = this;
|
||||
if (!this.register_id) return Promise.resolve();
|
||||
// 详情已迁 xk-api(POST /register/detail):result 为单对象(老 Yii 是 data.list[0]),附 appointment 预约信息
|
||||
const p = registeInfo({
|
||||
method: 'post',
|
||||
data: {
|
||||
@@ -467,8 +483,8 @@
|
||||
register_id: that.register_id
|
||||
}
|
||||
}).then((res) => {
|
||||
if (res.data.errcode == 0 && res.data.data.list && res.data.data.list[0]) {
|
||||
that.registList = res.data.data.list[0];
|
||||
if (res.data.code == 0 && res.data.result) {
|
||||
that.registList = res.data.result;
|
||||
that.registerType = that.registerMap[that.registList.register_type];
|
||||
if (that.registList.service_user_id) {
|
||||
that.applyDoctorNumericId(that.registList.service_user_id);
|
||||
|
||||
@@ -10,6 +10,10 @@
|
||||
<text v-if="showDelegateInfo" class="delegate-info">
|
||||
委托诊所:{{ delegateStoreName }} 委托医生:{{ delegateDoctorName }}
|
||||
</text>
|
||||
<!-- 排班日历选择的预约时段(到店挂号带入) -->
|
||||
<text v-if="slotLabelProp" class="delegate-info">
|
||||
预约时段:{{ slotLabelProp }}
|
||||
</text>
|
||||
<text class="sub-title">请选择患者信息,以便医生给出更准确的诊疗建议,患者信息仅医生可见</text>
|
||||
</view>
|
||||
|
||||
@@ -182,10 +186,10 @@
|
||||
import {
|
||||
userList,
|
||||
userEidtPatient,
|
||||
registe,
|
||||
storeInfo,
|
||||
doctorDetail,
|
||||
} from '@/request/api/api.js';
|
||||
import { registe } from '@/request/api/register.js';
|
||||
import { getClinicOnlineConsultationInfoApi } from '@/request/api/product';
|
||||
import { createRegisterInfoApi, saveRegisterInfoApi } from '@/request/api/order';
|
||||
import request from '@/request/api/request';
|
||||
@@ -209,6 +213,21 @@ export default {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
// 预约日期(Y-m-d,医生详情页排班日历选择,空=后端默认今天)
|
||||
visitDateProp: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
// 预约时段ID(0=全天兜底)
|
||||
slotIdProp: {
|
||||
type: [String, Number],
|
||||
default: 0,
|
||||
},
|
||||
// 预约时段展示文案(如「08-13 周四 上午 08:00-12:00」)
|
||||
slotLabelProp: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -570,6 +589,8 @@ export default {
|
||||
this.registerType === '2' ||
|
||||
this.registerType === '3' ||
|
||||
(this.registerType !== '0' && !!this.delegateStoreId);
|
||||
// 建单已迁 xk-api(POST /register/create),响应按 code/result 读取;
|
||||
// visit_date/slot_id 由医生详情页排班日历带入,不传则后端默认今天全天
|
||||
registe({
|
||||
method: 'post',
|
||||
data: {
|
||||
@@ -578,20 +599,22 @@ export default {
|
||||
store_id: storeId,
|
||||
origin_store_id: shouldSendOriginStoreId ? originStoreId : 0,
|
||||
service_user_id: this.Did,
|
||||
register_type: this.registerType
|
||||
register_type: this.registerType,
|
||||
visit_date: this.visitDateProp || '',
|
||||
slot_id: Number(this.slotIdProp || 0)
|
||||
}
|
||||
}).then(async (res) => {
|
||||
if (res.data.errcode == 0) {
|
||||
this.register_id = res.data.data.register_id;
|
||||
if (res.data.code == 0) {
|
||||
this.register_id = res.data.result.register_id;
|
||||
if (this.registerType === '3' || this.registerType === '2') {
|
||||
await this.afterRegisterCreated(followUpRInfo);
|
||||
return;
|
||||
}
|
||||
uni.setStorageSync('register_id', this.register_id);
|
||||
this.emitRegisterCreated(followUpRInfo, res.data.data.isHas == 1);
|
||||
this.emitRegisterCreated(followUpRInfo, res.data.result.isHas == 1);
|
||||
} else {
|
||||
this.$refs.uToast.show({
|
||||
title: res.data.msg,
|
||||
title: res.data.message,
|
||||
type: 'default',
|
||||
icon: false
|
||||
});
|
||||
|
||||
@@ -88,9 +88,12 @@
|
||||
</view>
|
||||
<view class="item">
|
||||
<view class="aapoint">
|
||||
预计就诊时间
|
||||
预约时间
|
||||
</view>
|
||||
<view class="detail">
|
||||
<view class="detail" v-if="appointmentText">
|
||||
{{appointmentText}}
|
||||
</view>
|
||||
<view class="detail" v-else>
|
||||
{{registList.created_at}}
|
||||
</view>
|
||||
</view>
|
||||
@@ -172,8 +175,9 @@
|
||||
import {
|
||||
registeCancle,
|
||||
registeInfo,
|
||||
registePay
|
||||
} from '../../request/api/api';
|
||||
registePay,
|
||||
registeUnpay
|
||||
} from '../../request/api/register.js';
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
@@ -185,6 +189,18 @@
|
||||
type: 0,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
/** 预约时间文案:占号信息(日期+时段),全天时段不显示起止时间;老单无占号回退创建时间 */
|
||||
appointmentText() {
|
||||
const appt = this.registList && this.registList.appointment;
|
||||
if (!appt || !appt.visit_date) return '';
|
||||
let text = appt.visit_date + ' ' + (appt.slot_name || '');
|
||||
if (appt.start_time && appt.end_time && !(appt.start_time === '00:00' && appt.end_time === '23:59')) {
|
||||
text += ' ' + appt.start_time + '-' + appt.end_time;
|
||||
}
|
||||
return text;
|
||||
},
|
||||
},
|
||||
onLoad(e) {
|
||||
this.register_id = e.id
|
||||
this.type = e.type
|
||||
@@ -209,17 +225,21 @@
|
||||
open() {
|
||||
this.show = true;
|
||||
},
|
||||
// 取消预约
|
||||
/**
|
||||
* 取消预约(已迁 xk-api):老 Yii refund 一个接口兼容未付/已付,
|
||||
* xk-api 拆成 cancel(未付取消)与 refund(已付退款),按订单状态分流
|
||||
*/
|
||||
cancel() {
|
||||
registeCancle({
|
||||
const isPaid = this.registList && this.registList.is_pay == 1;
|
||||
const doCancel = isPaid ? registeCancle : registeUnpay;
|
||||
doCancel({
|
||||
method: "post",
|
||||
data: {
|
||||
store_id: uni.getStorageSync('store_id') || '11001',
|
||||
register_id: this.register_id
|
||||
}
|
||||
}).then((res) => {
|
||||
// console.log(res, 'tk');
|
||||
if (res.data.errcode == 0) {
|
||||
if (res.data.code == 0) {
|
||||
this.isshow = true
|
||||
uni.showLoading({
|
||||
title: "加载中"
|
||||
@@ -227,7 +247,7 @@
|
||||
setTimeout(() => {
|
||||
uni.hideLoading();
|
||||
uni.showToast({
|
||||
title: "取消成功",
|
||||
title: isPaid ? "退款申请成功" : "取消成功",
|
||||
duration: 1500,
|
||||
mask: false,
|
||||
icon: 'success'
|
||||
@@ -237,7 +257,7 @@
|
||||
} else {
|
||||
uni.hideLoading();
|
||||
this.$refs.uToast.show({
|
||||
title: res.data.msg,
|
||||
title: res.data.message,
|
||||
type: 'default',
|
||||
icon: false
|
||||
})
|
||||
@@ -338,7 +358,7 @@
|
||||
})
|
||||
},
|
||||
|
||||
// 挂号信息
|
||||
// 挂号信息(已迁 xk-api POST /register/detail:result 为单对象,老 Yii 是 data.list[0])
|
||||
getInfo() {
|
||||
registeInfo({
|
||||
method: "post",
|
||||
@@ -347,9 +367,8 @@
|
||||
register_id: this.register_id
|
||||
}
|
||||
}).then((res) => {
|
||||
// console.log(res, 'ghxq');
|
||||
if (res.data.errcode == 0) {
|
||||
this.registList = res.data.data.list[0]
|
||||
if (res.data.code == 0 && res.data.result) {
|
||||
this.registList = res.data.result
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user