fix: 在线复诊的UI

This commit is contained in:
2026-01-09 11:04:03 +08:00
parent 5eec8b370e
commit 187260164b
21 changed files with 429 additions and 71 deletions

View File

@@ -12,8 +12,8 @@ import { sendMessage } from '#/views/business/chat/utils/request';
// 辅助函数:按需解析消息内容
const getParsedContent = (messageType, messageContent) => {
// 对于需要 JSON 解析的消息类型4, 10, 11, 12, 13
if ([4, 10, 11, 12, 13].includes(messageType)) {
// 对于需要 JSON 解析的消息类型4, 9, 10, 11, 12, 13
if ([4, 9, 10, 11, 12, 13].includes(messageType)) {
try {
return JSON.parse(messageContent);
} catch (e) {
@@ -373,7 +373,8 @@ if (props.message.message_type === 10) {
<!-- 图片消息 -->
<div
v-if="message.message_type === 1"
class="relative cursor-pointer"
class="image-message relative cursor-pointer"
style="width: 400px;"
@click="handlePreviewMedia(parsedContent, 'image')"
>
<Image
@@ -752,14 +753,47 @@ if (props.message.message_type === 10) {
class="price-update-card"
>
<div class="price-update-header">
<i class="fas fa-tag price-icon"></i>
<div class="header-icon">
<i class="fas fa-tag"></i>
</div>
<span class="price-title">订单价格更新</span>
</div>
<div class="price-update-content">
<p class="price-message">{{ parsedContent.message }}</p>
<div class="price-details">
<span class="order-no">订单号{{ parsedContent.order_no }}</span>
<span class="price-amount">¥{{ parsedContent.total_pay_price }}</span>
<!-- 订单信息 -->
<div class="order-info">
<div class="info-item">
<span class="info-label">
<i class="fas fa-hashtag"></i>
订单号
</span>
<span class="info-value">{{ parsedContent.order_no }}</span>
</div>
</div>
<!-- 价格详情 -->
<div class="price-info-section">
<div class="price-info-row">
<span class="price-label">
<i class="fas fa-shipping-fast"></i>
运费
</span>
<span class="price-value shipping-fee">
<span v-if="parsedContent.is_free_shipping === 1 || parsedContent.is_free_shipping === '1'" class="free-shipping-tag">
<i class="fas fa-check-circle"></i>
包邮
</span>
<span v-else>¥{{ parsedContent.trans_expenses || '0.00' }}</span>
</span>
</div>
<div class="price-info-row total-row">
<span class="price-label">
<i class="fas fa-money-bill-wave"></i>
总价
</span>
<span class="price-amount">¥{{ parsedContent.total_pay_price }}</span>
</div>
</div>
</div>
</div>
@@ -1133,34 +1167,96 @@ if (props.message.message_type === 10) {
.price-update-card {
@apply rounded-xl border border-orange-200 bg-gradient-to-r from-orange-50 to-amber-50 p-4 shadow-sm dark:border-orange-700 dark:from-orange-900/30 dark:to-amber-900/30;
transition: all 0.3s ease;
}
.price-update-card:hover {
@apply shadow-md;
transform: translateY(-2px);
}
.message-bubble.sent .price-update-card {
@apply border-orange-200/50 bg-orange-50/30 dark:border-orange-700/70 dark:bg-orange-900/30;
}
.price-update-header {
@apply mb-3 flex items-center gap-2;
@apply mb-3 flex items-center gap-3;
}
.price-icon {
@apply text-orange-500;
.header-icon {
@apply flex h-10 w-10 items-center justify-center rounded-lg bg-orange-100 text-orange-500 dark:bg-orange-900/50 dark:text-orange-300;
}
.price-title {
@apply font-semibold text-orange-700 dark:text-orange-300;
@apply flex-1 text-base font-semibold text-orange-700 dark:text-orange-300;
}
.price-update-content {
@apply space-y-2;
@apply space-y-3;
}
.price-message {
@apply text-sm text-gray-700 dark:text-gray-300;
@apply rounded-lg bg-white/60 p-2 text-sm text-gray-700 dark:bg-gray-800/60 dark:text-gray-300;
}
.price-details {
@apply flex items-center justify-between border-t border-orange-200/50 pt-2 dark:border-orange-700/50;
.order-info {
@apply rounded-lg bg-white/60 p-3 dark:bg-gray-800/60;
}
.order-no {
@apply text-xs text-gray-500 dark:text-gray-400;
.info-item {
@apply flex items-center justify-between;
}
.info-label {
@apply flex items-center gap-1.5 text-xs text-gray-500 dark:text-gray-400;
}
.info-label i {
@apply text-orange-500 dark:text-orange-400;
}
.info-value {
@apply text-sm font-medium text-gray-700 dark:text-gray-200;
}
.price-info-section {
@apply space-y-2 rounded-lg border-t border-orange-200/50 bg-white/60 pt-3 dark:border-orange-700/50 dark:bg-gray-800/60;
}
.price-info-row {
@apply flex items-center justify-between;
}
.price-label {
@apply flex items-center gap-1.5 text-sm text-gray-600 dark:text-gray-300;
}
.price-label i {
@apply text-orange-500 dark:text-orange-400;
}
.price-value {
@apply text-sm font-medium text-gray-700 dark:text-gray-200;
}
.shipping-fee {
@apply flex items-center;
}
.free-shipping-tag {
@apply inline-flex items-center gap-1 rounded-full bg-green-100 px-2 py-0.5 text-xs font-medium text-green-700 dark:bg-green-900/50 dark:text-green-300;
}
.free-shipping-tag i {
@apply text-green-600 dark:text-green-400;
}
.total-row {
@apply border-t border-orange-200/50 pt-2 dark:border-orange-700/50;
}
.total-row .price-label {
@apply text-base font-semibold;
}
.price-amount {
@@ -1170,4 +1266,32 @@ if (props.message.message_type === 10) {
.system-message-text {
@apply rounded-lg bg-gray-100 p-3 text-center text-sm text-gray-600 dark:bg-gray-700 dark:text-gray-300;
}
/* 患者就诊经历卡片发送样式 */
.message-bubble.sent .patient-experience-card {
@apply border-blue-200/50 bg-blue-50/30 dark:border-blue-700/70 dark:bg-blue-900/30;
}
/* 商品卡片发送样式 */
.message-bubble.sent .product-card {
@apply border-purple-200/50 bg-purple-50/30 dark:border-purple-700/70 dark:bg-purple-900/30;
}
/* 结束问诊卡片发送样式 */
.message-bubble.sent .end-consultation-card {
@apply border-gray-200/50 bg-gray-50/30 dark:border-gray-700/70 dark:bg-gray-900/30;
}
/* 图片和视频消息发送样式 */
.message-bubble.sent .image-message,
.message-bubble.sent .video-message {
opacity: 0.9;
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 12px;
}
/* 通话消息发送样式 */
.message-bubble.sent .call-message {
color: white;
}
</style>

View File

@@ -454,19 +454,6 @@ const handleEndConsultation = () => {
reason: '医生主动结束'
});
// 发送结束问诊消息卡片
const endTime = new Date().toLocaleString('zh-CN');
await sendMessage({
roomId: chatStore.currentFriend?.room_id,
senderId: userStore.currentUser?.doctor_id,
receiverId: chatStore.currentFriend?.id,
type: 'end-consultation',
content: JSON.stringify({
end_reason: '医生主动结束',
end_time: endTime
})
});
message.success('结束接诊成功');
emit('end-consultation-success');
} catch (error) {

View File

@@ -76,3 +76,7 @@ export async function updateZoneCategory(data: Record<string, any>) {

View File

@@ -165,3 +165,7 @@ const handleChange = (info: { file: UploadFile }) => {

View File

@@ -105,3 +105,7 @@ const [Modal, modalApi] = useVbenModal({

View File

@@ -24,3 +24,7 @@ export const formOptions: VbenFormProps = {

View File

@@ -91,3 +91,7 @@ export const gridOptions: VxeGridProps<RowType> = {

View File

@@ -194,3 +194,7 @@ const openExcelUploadModal = () => {

View File

@@ -76,3 +76,7 @@ export async function updateZoneCategory(data: Record<string, any>) {

View File

@@ -165,3 +165,7 @@ const handleChange = (info: { file: UploadFile }) => {

View File

@@ -105,3 +105,7 @@ const [Modal, modalApi] = useVbenModal({

View File

@@ -24,3 +24,7 @@ export const formOptions: VbenFormProps = {

View File

@@ -91,3 +91,7 @@ export const gridOptions: VxeGridProps<RowType> = {

View File

@@ -194,3 +194,7 @@ const openExcelUploadModal = () => {

View File

@@ -43,3 +43,7 @@ const [Modal, modalApi] = useVbenModal({
</template>

View File

@@ -1,6 +1,5 @@
<script setup>
import { computed } from 'vue';
import { Card } from 'ant-design-vue';
const props = defineProps({
content: {
@@ -23,57 +22,172 @@ const endData = computed(() => {
</script>
<template>
<Card class="end-consultation-card" :bordered="true" style="max-width: 350px">
<div class="end-consultation-content">
<div class="title">本次问诊已结束</div>
<div v-if="endData.end_reason" class="reason">
<div class="label">结束原因</div>
<div class="value">{{ endData.end_reason }}</div>
<div class="end-consultation-card">
<!-- 卡片头部 -->
<div class="card-header">
<div class="header-icon">
<i class="fas fa-check-circle"></i>
</div>
<div class="header-title">本次问诊已结束</div>
</div>
<!-- 卡片内容 -->
<div class="card-body">
<!-- 结束原因 -->
<div v-if="endData.end_reason" class="info-row">
<div class="info-icon">
<i class="fas fa-comment-alt"></i>
</div>
<div class="info-content">
<div class="info-label">结束原因</div>
<div class="info-value">{{ endData.end_reason }}</div>
</div>
</div>
<div v-if="endData.end_time" class="time">
<div class="label">结束时间</div>
<div class="value">{{ endData.end_time }}</div>
<!-- 结束时间 -->
<div v-if="endData.end_time" class="info-row">
<div class="info-icon">
<i class="fas fa-clock"></i>
</div>
<div class="info-content">
<div class="info-label">结束时间</div>
<div class="info-value">{{ endData.end_time }}</div>
</div>
</div>
</div>
</Card>
</div>
</template>
<style scoped>
.end-consultation-card {
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
background: #f5f5f5;
width: 100%;
min-width: 350px;
max-width: 450px;
background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);
border-radius: 16px;
border: 1px solid #e2e8f0;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
overflow: hidden;
transition: all 0.3s ease;
}
.end-consultation-content {
padding: 12px;
text-align: center;
.end-consultation-card:hover {
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1);
transform: translateY(-2px);
}
.title {
font-size: 16px;
/* 卡片头部 */
.card-header {
display: flex;
align-items: center;
gap: 12px;
padding: 16px 20px;
background: linear-gradient(135deg, #64748b 0%, #475569 100%);
color: white;
}
.header-icon {
width: 40px;
height: 40px;
background: rgba(255, 255, 255, 0.2);
border-radius: 10px;
display: flex;
align-items: center;
justify-content: center;
font-size: 18px;
}
.header-title {
flex: 1;
font-size: 17px;
font-weight: 600;
letter-spacing: 0.5px;
}
/* 卡片内容 */
.card-body {
padding: 20px;
}
.info-row {
display: flex;
align-items: flex-start;
gap: 14px;
padding: 14px 16px;
background: white;
border-radius: 12px;
margin-bottom: 12px;
color: #999;
border: 1px solid #e2e8f0;
transition: all 0.2s ease;
}
.reason,
.time {
margin-top: 8px;
text-align: left;
.info-row:last-child {
margin-bottom: 0;
}
.label {
font-size: 12px;
color: #666;
margin-bottom: 4px;
.info-row:hover {
border-color: #64748b;
background: #f8fafc;
}
.value {
.info-icon {
width: 36px;
height: 36px;
background: linear-gradient(135deg, #f1f5f9 0%, #e2e8f0 100%);
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
color: #64748b;
font-size: 14px;
color: #333;
flex-shrink: 0;
}
.info-content {
flex: 1;
min-width: 0;
}
.info-label {
font-size: 12px;
color: #64748b;
margin-bottom: 4px;
font-weight: 500;
}
.info-value {
font-size: 14px;
color: #1e293b;
line-height: 1.6;
word-break: break-word;
}
/* 暗黑模式适配 */
.dark .end-consultation-card {
background: linear-gradient(135deg, #1e293b 0%, #0f172a 100%);
border-color: #334155;
}
.dark .card-header {
background: linear-gradient(135deg, #475569 0%, #334155 100%);
}
.dark .info-row {
background: #1e293b;
border-color: #334155;
}
.dark .info-row:hover {
background: #334155;
}
.dark .info-value {
color: #e2e8f0;
}
.dark .info-icon {
background: linear-gradient(135deg, #334155 0%, #1e293b 100%);
color: #94a3b8;
}
</style>

View File

@@ -347,25 +347,25 @@ const handleAddToPrescription = async () => {
}
/* 暗黑模式适配 */
:global(.dark) .patient-experience-card {
.dark .patient-experience-card {
background: linear-gradient(135deg, #1e293b 0%, #0f172a 100%);
border-color: #334155;
}
:global(.dark) .info-row {
.dark .info-row {
background: #1e293b;
border-color: #334155;
}
:global(.dark) .info-row:hover {
.dark .info-row:hover {
background: #334155;
}
:global(.dark) .info-value {
.dark .info-value {
color: #e2e8f0;
}
:global(.dark) .card-footer {
.dark .card-footer {
background: #0f172a;
border-color: #334155;
}

View File

@@ -254,10 +254,6 @@ watch(
flex-shrink: 0;
}
.dark .patient-title {
color: #f3f4f6;
}
.patient-descriptions {
margin-bottom: 0;
}
@@ -318,4 +314,23 @@ watch(
font-size: 16px;
font-weight: 500;
}
/* 暗色模式适配 */
.dark .patient-title {
color: #f3f4f6;
}
.dark .illness-info,
.dark .drug-info {
color: #cbd5e1;
}
.dark .illness-info strong,
.dark .drug-info strong {
color: #e2e8f0;
}
.dark .meta-info {
color: #94a3b8;
}
</style>

View File

@@ -214,6 +214,7 @@ defineExpose({
align-items: center;
justify-content: center;
font-size: 14px;
color: #666;
}
.patient-list {
@@ -282,4 +283,37 @@ defineExpose({
.time {
font-size: 12px;
}
/* 暗色模式适配 */
.dark .patient-name {
color: #e2e8f0;
}
.dark .patient-phone {
color: #94a3b8;
}
.dark .order-no {
color: #94a3b8;
}
.dark .status-pending {
background: rgba(245, 108, 108, 0.2);
color: #f87171;
}
.dark .status-consulting {
background: rgba(103, 194, 58, 0.2);
color: #4ade80;
}
.dark .status-ended {
background: rgba(144, 147, 153, 0.2);
color: #9ca3af;
}
.dark .loading-state,
.dark .empty-state {
color: #94a3b8;
}
</style>

View File

@@ -113,5 +113,36 @@ const handleClick = () => {
color: #ff4d4f;
margin-left: 4px;
}
/* 暗色模式适配 */
.dark .product-card {
background: #1e293b;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
}
.dark .product-card:hover {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
}
.dark .product-name {
color: #e2e8f0;
}
.dark .product-spec,
.dark .product-function {
color: #94a3b8;
}
.dark .product-price {
border-top-color: #334155;
}
.dark .price-label {
color: #94a3b8;
}
.dark .price-value {
color: #f87171;
}
</style>

View File

@@ -562,7 +562,8 @@ const handleAddPatientProductsToPrescription = async () => {
}
.dark .left-panel-wrapper {
border-right-color: #333;
border-right-color: #334155;
background: #1e293b;
}
.dark .container-box-grid {