You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

757 lines
17 KiB
Vue

<template>
<view class="page-container">
<view class="page-header">
<view class="header-bg"></view>
<view class="header-content">
<view class="header-title">医技预约</view>
<view class="header-subtitle">秦皇岛中医院</view>
</view>
</view>
<view class="user-card" v-if="List.length > 0">
<view class="user-info">
<view class="user-avatar">
<text class="avatar-text">{{ List[0].user_name ? List[0].user_name.substring(0, 1) : '患' }}</text>
</view>
<view class="user-detail">
<view class="user-name">{{ List[0].user_name }}</view>
<view class="user-meta">
<text class="meta-item">{{ List[0].user_sex == 1 ? '男' : '女' }}</text>
<text class="meta-divider">|</text>
<text class="meta-item">{{ List[0].user_phone || '未填写手机号' }}</text>
</view>
</view>
</view>
<view class="user-reg-num">
<text class="reg-label">登记号</text>
<text class="reg-value">{{ List[0].reg_num }}</text>
</view>
</view>
<view class="filter-section">
<view class="date-picker-wrapper">
<uni-icons type="calendar" size="18" color="#2E7D32"></uni-icons>
<view class="date-picker">
<uni-datetime-picker
v-model="SearchInfo.dateRange"
type="daterange"
@change="dateSelecteFunc"
:start="startDate"
:end="endDate"
/>
</view>
</view>
</view>
<view class="list-section" v-if="List.length > 0">
<view class="section-header">
<text class="section-title">检查项目</text>
<text class="section-count">共 {{ List.length }} 项</text>
</view>
<view class="item-card" v-for="(item, index) in List" :key="item.id">
<view class="item-header">
<view class="item-title">
<uni-icons type="medal" size="18" color="#2E7D32"></uni-icons>
<text class="title-text">{{ item.entrust }}</text>
</view>
<view class="item-status" :class="getStatusClass(item.list_status)">
{{ getStatusText(item.list_status) }}
</view>
</view>
<view class="item-body">
<view class="info-grid">
<view class="info-item">
<text class="info-label">医嘱时间</text>
<text class="info-value">{{ item.entrust_date }}</text>
</view>
<view class="info-item">
<text class="info-label">申请科室</text>
<text class="info-value">{{ item.reservation_department || '-' }}</text>
</view>
</view>
<view class="appointment-info" v-if="item.list_status == 1">
<uni-icons type="location" size="14" color="#2E7D32"></uni-icons>
<text class="appointment-text">
预约时段:{{ item.reservation_date }}
{{ item.period_begin_time ? item.period_begin_time.substring(0, 5) : '' }}~{{ item.period_end_time ? item.period_end_time.substring(0, 5) : '' }}
</text>
</view>
</view>
<view class="item-footer">
<view class="detail-btn" @click="openDetail(item.id)">
<uni-icons type="info" size="14" color="#666"></uni-icons>
<text>详情</text>
</view>
<view class="action-btns">
<view v-if="item.list_status == 0" class="action-btn primary" @click="ToPlanList(1, item)">
<uni-icons type="plus" size="14" color="#FFFFFF"></uni-icons>
<text>预约</text>
</view>
<template v-if="item.list_status == 1">
<view class="action-btn danger" @click="Cancel(item)">
<uni-icons type="close" size="14" color="#FFFFFF"></uni-icons>
<text>取消</text>
</view>
<view class="action-btn secondary" @click="ToPlanList(2, item)">
<uni-icons type="refresh" size="14" color="#FFFFFF"></uni-icons>
<text>改约</text>
</view>
</template>
</view>
</view>
</view>
</view>
<view class="empty-state" v-else>
<view class="empty-icon">
<uni-icons type="inbox" size="80" color="#CCCCCC"></uni-icons>
</view>
<text class="empty-text">暂无检查项目</text>
<text class="empty-desc">请在日期范围内查询您的检查项目</text>
</view>
<uni-popup ref="popupDetail" type="center">
<view class="detail-popup" v-if="entrustInfo != null">
<view class="popup-header">
<text class="popup-title">检查详情</text>
<view class="popup-close" @click="popupDetail.close()">
<uni-icons type="close" size="20" color="#999"></uni-icons>
</view>
</view>
<view class="popup-body">
<view class="detail-item">
<text class="detail-label">检查项目</text>
<text class="detail-value">{{ entrustInfo.entrust }}</text>
</view>
<view class="detail-item">
<text class="detail-label">当前状态</text>
<view class="detail-value">
<view class="status-tag" :class="getStatusClass(entrustInfo.list_status)">
{{ getStatusText(entrustInfo.list_status) }}
</view>
</view>
</view>
<view class="detail-item">
<text class="detail-label">医嘱时间</text>
<text class="detail-value">{{ entrustInfo.entrust_date }} {{ entrustInfo.entrust_time || '' }}</text>
</view>
<view class="detail-item">
<text class="detail-label">申请科室</text>
<text class="detail-value">{{ entrustInfo.reservation_department || '-' }}</text>
</view>
<view class="detail-item">
<text class="detail-label">开单医生</text>
<text class="detail-value">{{ entrustInfo.docotr || '-' }}</text>
</view>
<view class="detail-item">
<text class="detail-label">缴费状态</text>
<view class="detail-value">
<view class="pay-tag" :class="entrustInfo.is_pay == 1 ? 'paid' : 'unpaid'">
{{ entrustInfo.is_pay == 1 ? '已缴费' : '未缴费' }}
</view>
</view>
</view>
<view class="detail-item" v-if="entrustInfo.list_status == 1">
<text class="detail-label">预约时段</text>
<text class="detail-value highlight">
{{ entrustInfo.reservation_date }}
{{ entrustInfo.period_begin_time ? entrustInfo.period_begin_time.substring(0, 5) : '' }}~{{ entrustInfo.period_end_time ? entrustInfo.period_end_time.substring(0, 5) : '' }}
</text>
</view>
</view>
<view class="popup-footer">
<view class="popup-btn" @click="popupDetail.close()"></view>
</view>
</view>
</uni-popup>
</view>
</template>
<script setup>
import { ref, computed } from "vue"
import { EntrustGetList, H5_CancelYuYue, GetEntrustDetail } from "@/api"
import { onLoad, onShow } from "@dcloudio/uni-app"
let SearchInfo = ref({
dateRange: []
});
let List = ref([]);
let entrustInfo = ref(null);
let popupDetail = ref(null);
let startDate = ref('2024-01-01');
let endDate = ref('2030-12-31');
const getStatusClass = (status) => {
const classMap = {
0: 'status-pending',
1: 'status-success',
2: 'status-info',
3: 'status-done'
};
return classMap[status] || 'status-pending';
};
const getStatusText = (status) => {
const textMap = {
0: '待预约',
1: '已预约',
2: '已登记',
3: '已完成'
};
return textMap[status] || '未知';
};
const GetList = () => {
uni.showLoading({ title: '加载中' });
EntrustGetList({ searchInfo: SearchInfo.value }).then(res => {
uni.hideLoading();
if (res.status) {
List.value = res.data.list;
}
}).catch(() => {
uni.hideLoading();
});
};
const openDetail = (id) => {
popupDetail.value.open();
GetEntrustDetailFunc(id);
};
const GetEntrustDetailFunc = (id) => {
GetEntrustDetail({ id: id }).then(res => {
if (res.status) {
entrustInfo.value = res.data.info;
}
});
};
const dateSelecteFunc = (e) => {
SearchInfo.value.dateRange = e;
GetList();
};
const ToPlanList = (do_type, item) => {
let data = {
do_type: do_type,
appointment_type: 2,
regnum: item.reg_num,
entrustid: [item.entrust_id],
episodeid: item.episodeid
};
uni.navigateTo({
url: '/pages/PlanList?data=' + encodeURIComponent(JSON.stringify(data))
});
};
const Cancel = (item) => {
uni.showModal({
cancelText: "取消",
confirmText: "确定",
title: '取消预约',
content: '确定要取消该预约吗?取消后需要重新预约。',
confirmColor: '#F44336',
success: function(res) {
if (res.confirm) {
uni.showLoading({ title: '处理中' });
H5_CancelYuYue({
MainListId: item.id,
reg_num: item.reg_num
}).then(res => {
uni.hideLoading();
if (res.status) {
uni.showToast({
title: '取消成功',
icon: 'success'
});
GetList();
}
}).catch(() => {
uni.hideLoading();
});
}
}
});
};
function getDateRange() {
const today = new Date();
const prevMonth = new Date(today.getFullYear(), today.getMonth() - 1, today.getDate());
const nextMonth = new Date(today.getFullYear(), today.getMonth() + 1, today.getDate());
const formatDate = (date) => {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
return `${year}-${month}-${day}`;
};
return [formatDate(prevMonth), formatDate(nextMonth)];
}
onLoad(() => {
SearchInfo.value.dateRange = getDateRange();
GetList();
});
onShow(() => {
GetList();
});
</script>
<style lang="scss" scoped>
.page-container {
min-height: 100vh;
background: #F5F7FA;
padding-bottom: 40rpx;
}
.page-header {
position: relative;
.header-bg {
position: absolute;
top: 0;
left: 0;
right: 0;
height: 200rpx;
background: linear-gradient(135deg, #2E7D32 0%, #4CAF50 100%);
border-radius: 0 0 48rpx 48rpx;
}
.header-content {
position: relative;
z-index: 1;
padding: 60rpx 40rpx 40rpx;
text-align: center;
.header-title {
font-size: 40rpx;
font-weight: 600;
color: #FFFFFF;
letter-spacing: 4rpx;
}
.header-subtitle {
font-size: 24rpx;
color: rgba(255, 255, 255, 0.8);
margin-top: 8rpx;
}
}
}
.user-card {
margin: -40rpx 32rpx 24rpx;
background: #FFFFFF;
border-radius: 24rpx;
padding: 32rpx;
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.08);
position: relative;
z-index: 2;
.user-info {
display: flex;
align-items: center;
.user-avatar {
width: 96rpx;
height: 96rpx;
background: linear-gradient(135deg, #2E7D32 0%, #4CAF50 100%);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
.avatar-text {
font-size: 40rpx;
font-weight: 600;
color: #FFFFFF;
}
}
.user-detail {
flex: 1;
margin-left: 24rpx;
.user-name {
font-size: 36rpx;
font-weight: 600;
color: #333333;
}
.user-meta {
display: flex;
align-items: center;
margin-top: 8rpx;
.meta-item {
font-size: 26rpx;
color: #999999;
}
.meta-divider {
margin: 0 16rpx;
color: #E0E0E0;
}
}
}
}
.user-reg-num {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 20rpx;
padding-top: 20rpx;
border-top: 1rpx solid #F0F0F0;
.reg-label {
font-size: 26rpx;
color: #999999;
}
.reg-value {
font-size: 28rpx;
font-weight: 500;
color: #2E7D32;
}
}
}
.filter-section {
margin: 0 32rpx 24rpx;
.date-picker-wrapper {
display: flex;
align-items: center;
background: #FFFFFF;
border-radius: 16rpx;
padding: 20rpx 24rpx;
.date-picker {
flex: 1;
margin-left: 12rpx;
}
}
}
.list-section {
padding: 0 32rpx;
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20rpx;
.section-title {
font-size: 32rpx;
font-weight: 600;
color: #333333;
}
.section-count {
font-size: 26rpx;
color: #999999;
}
}
.item-card {
background: #FFFFFF;
border-radius: 24rpx;
margin-bottom: 24rpx;
overflow: hidden;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.06);
.item-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 24rpx 28rpx;
background: #FAFAFA;
border-bottom: 1rpx solid #F0F0F0;
.item-title {
display: flex;
align-items: center;
.title-text {
font-size: 30rpx;
font-weight: 600;
color: #333333;
margin-left: 12rpx;
}
}
.item-status {
padding: 8rpx 20rpx;
border-radius: 20rpx;
font-size: 24rpx;
font-weight: 500;
&.status-pending {
background: rgba(255, 152, 0, 0.1);
color: #FF9800;
}
&.status-success {
background: rgba(76, 175, 80, 0.1);
color: #4CAF50;
}
&.status-info {
background: rgba(33, 150, 243, 0.1);
color: #2196F3;
}
&.status-done {
background: rgba(158, 158, 158, 0.1);
color: #9E9E9E;
}
}
}
.item-body {
padding: 24rpx 28rpx;
.info-grid {
display: flex;
gap: 40rpx;
.info-item {
flex: 1;
.info-label {
display: block;
font-size: 24rpx;
color: #999999;
margin-bottom: 8rpx;
}
.info-value {
font-size: 28rpx;
color: #333333;
}
}
}
.appointment-info {
display: flex;
align-items: center;
margin-top: 20rpx;
padding: 16rpx 20rpx;
background: rgba(46, 125, 50, 0.05);
border-radius: 12rpx;
.appointment-text {
font-size: 26rpx;
color: #2E7D32;
margin-left: 8rpx;
}
}
}
.item-footer {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20rpx 28rpx;
border-top: 1rpx solid #F0F0F0;
.detail-btn {
display: flex;
align-items: center;
font-size: 26rpx;
color: #666666;
text {
margin-left: 6rpx;
}
}
.action-btns {
display: flex;
gap: 16rpx;
.action-btn {
display: flex;
align-items: center;
padding: 14rpx 28rpx;
border-radius: 32rpx;
font-size: 26rpx;
text {
margin-left: 6rpx;
}
&.primary {
background: linear-gradient(135deg, #2E7D32 0%, #4CAF50 100%);
color: #FFFFFF;
}
&.secondary {
background: linear-gradient(135deg, #1976D2 0%, #42A5F5 100%);
color: #FFFFFF;
}
&.danger {
background: linear-gradient(135deg, #F44336 0%, #EF5350 100%);
color: #FFFFFF;
}
}
}
}
}
}
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 120rpx 40rpx;
.empty-icon {
margin-bottom: 24rpx;
}
.empty-text {
font-size: 32rpx;
color: #CCCCCC;
margin-bottom: 12rpx;
}
.empty-desc {
font-size: 26rpx;
color: #DDDDDD;
}
}
.detail-popup {
width: 640rpx;
background: #FFFFFF;
border-radius: 24rpx;
overflow: hidden;
.popup-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 28rpx 32rpx;
background: linear-gradient(135deg, #2E7D32 0%, #4CAF50 100%);
.popup-title {
font-size: 34rpx;
font-weight: 600;
color: #FFFFFF;
}
.popup-close {
padding: 8rpx;
}
}
.popup-body {
padding: 28rpx 32rpx;
max-height: 600rpx;
overflow-y: auto;
.detail-item {
display: flex;
justify-content: space-between;
align-items: flex-start;
padding: 16rpx 0;
border-bottom: 1rpx solid #F5F5F5;
&:last-child {
border-bottom: none;
}
.detail-label {
font-size: 28rpx;
color: #999999;
flex-shrink: 0;
width: 160rpx;
}
.detail-value {
font-size: 28rpx;
color: #333333;
text-align: right;
flex: 1;
&.highlight {
color: #2E7D32;
font-weight: 500;
}
}
.status-tag {
padding: 6rpx 16rpx;
border-radius: 16rpx;
font-size: 24rpx;
&.status-pending {
background: rgba(255, 152, 0, 0.1);
color: #FF9800;
}
&.status-success {
background: rgba(76, 175, 80, 0.1);
color: #4CAF50;
}
&.status-info {
background: rgba(33, 150, 243, 0.1);
color: #2196F3;
}
&.status-done {
background: rgba(158, 158, 158, 0.1);
color: #9E9E9E;
}
}
.pay-tag {
padding: 6rpx 16rpx;
border-radius: 16rpx;
font-size: 24rpx;
&.paid {
background: rgba(76, 175, 80, 0.1);
color: #4CAF50;
}
&.unpaid {
background: rgba(255, 152, 0, 0.1);
color: #FF9800;
}
}
}
}
.popup-footer {
padding: 20rpx 32rpx;
border-top: 1rpx solid #F0F0F0;
.popup-btn {
text-align: center;
padding: 20rpx;
background: linear-gradient(135deg, #2E7D32 0%, #4CAF50 100%);
border-radius: 16rpx;
font-size: 30rpx;
color: #FFFFFF;
}
}
}
</style>