Files
medical-mall/pages/mall/delivery/service-record/index.uvue

332 lines
10 KiB
Plaintext

<template>
<ServicePageScaffold title="服务记录" fallback-url="/pages/mall/delivery/orders/detail">
<view class="page">
<view class="card">
<text class="section-title">服务时间</text>
<input class="field" v-model="startTime" placeholder="服务开始时间,例如 2026-05-20 15:00" />
<input class="field" v-model="endTime" placeholder="服务结束时间,例如 2026-05-20 16:30" />
<input class="field" v-model="actualDurationText" placeholder="实际服务时长(分钟)" type="number" />
</view>
<view class="card">
<text class="section-title">服务内容</text>
<view v-for="item in serviceItems" :key="item.id" class="item-row">
<view class="item-top">
<text class="item-name">{{ item.name }}</text>
<switch :checked="item.completed" color="#1f7db4" @change="toggleItem(item.id, $event)" />
</view>
<input class="field" v-model="item.remark" placeholder="服务过程说明" />
<input v-if="item.completed == false" class="field" v-model="item.incompleteReason" placeholder="未完成原因" />
</view>
</view>
<view class="card">
<text class="section-title">健康与过程记录</text>
<textarea class="textarea" v-model="processNote" placeholder="服务过程说明"></textarea>
<input class="field" v-model="elderStatus" placeholder="服务对象状态" />
<input class="field" v-model="bloodPressure" placeholder="血压" />
<input class="field" v-model="heartRate" placeholder="心率" />
<input class="field" v-model="bloodSugar" placeholder="血糖" />
<input class="field" v-model="bloodOxygen" placeholder="血氧" />
<input class="field" v-model="materialsUsed" placeholder="耗材使用记录" />
<input class="field" v-model="abnormalNote" placeholder="异常情况说明" />
<input class="field" v-model="staffRemark" placeholder="服务人员备注" />
<view class="placeholder-row">
<view class="placeholder-btn" @click="mockPhoto"><text class="placeholder-btn-text">服务照片占位</text></view>
<text class="placeholder-text">{{ photosText }}</text>
</view>
</view>
<view class="card">
<text class="section-title">家属确认方式</text>
<view class="confirm-row">
<view class="confirm-item" :class="confirmMethod == 'sms_code' ? 'confirm-item-active' : ''" @click="confirmMethod = 'sms_code'"><text class="confirm-text">验证码确认</text></view>
<view class="confirm-item" :class="confirmMethod == 'signature' ? 'confirm-item-active' : ''" @click="confirmMethod = 'signature'"><text class="confirm-text">签字确认</text></view>
</view>
<input v-if="confirmMethod == 'sms_code'" class="field" v-model="confirmCode" placeholder="验证码 mock" />
<input v-else class="field" v-model="signatureName" placeholder="签字人姓名 mock" />
</view>
<view class="action-row">
<view class="outline-btn" @click="goBack"><text class="outline-btn-text">返回详情</text></view>
<view class="primary-btn" @click="submitRecord"><text class="primary-btn-text">提交服务记录</text></view>
</view>
</view>
</ServicePageScaffold>
</template>
<script setup lang="uts">
import { computed, ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import ServicePageScaffold from '@/components/homeService/ServicePageScaffold.uvue'
import type { DeliveryOrderType, DeliveryServiceItemType, DeliveryServiceRecordType } from '@/types/delivery.uts'
import { completeServiceOrder, getServiceOrderDetail, startServiceOrder, submitServiceRecord } from '@/services/deliveryService.uts'
import { requireDeliveryAuth } from '@/utils/deliveryAuth.uts'
import { getDeliveryRouteParam } from '@/utils/deliveryRoute.uts'
const orderId = ref('')
const order = ref<DeliveryOrderType | null>(null)
const serviceItems = ref([] as Array<DeliveryServiceItemType>)
const startTime = ref('')
const endTime = ref('')
const actualDurationText = ref('90')
const processNote = ref('')
const elderStatus = ref('')
const bloodPressure = ref('')
const heartRate = ref('')
const bloodSugar = ref('')
const bloodOxygen = ref('')
const materialsUsed = ref('')
const abnormalNote = ref('')
const staffRemark = ref('')
const confirmMethod = ref('sms_code')
const confirmCode = ref('')
const signatureName = ref('')
const photoCount = ref(0)
const photosText = computed((): string => '已添加占位照片 ' + String(photoCount.value) + ' 张')
function toggleItem(itemId: string, event: any) {
for (let i = 0; i < serviceItems.value.length; i++) {
if (serviceItems.value[i].id == itemId) {
serviceItems.value[i].completed = event.detail.value === true
serviceItems.value[i].updatedAt = new Date().toISOString().replace('T', ' ').substring(0, 19)
if (serviceItems.value[i].completed) {
serviceItems.value[i].incompleteReason = ''
}
}
}
}
function mockPhoto() {
photoCount.value = photoCount.value + 1
uni.showToast({ title: '已添加照片占位', icon: 'success' })
}
async function loadData() {
const authResult = await requireDeliveryAuth({ redirectOnFail: true, toastOnFail: true })
if (!authResult.ok || orderId.value == '') {
return
}
order.value = await getServiceOrderDetail(orderId.value)
if (order.value != null) {
serviceItems.value = order.value.serviceItems
startTime.value = order.value.startServiceTime != null && order.value.startServiceTime != '' ? order.value.startServiceTime : order.value.appointmentTime
endTime.value = order.value.finishTime
processNote.value = order.value.serviceSummary
staffRemark.value = order.value.progressNote
if (order.value.serviceRecord != null) {
elderStatus.value = order.value.serviceRecord!.elderStatus
bloodPressure.value = order.value.serviceRecord!.healthMetrics.bloodPressure
heartRate.value = order.value.serviceRecord!.healthMetrics.heartRate
bloodSugar.value = order.value.serviceRecord!.healthMetrics.bloodSugar
bloodOxygen.value = order.value.serviceRecord!.healthMetrics.bloodOxygen
materialsUsed.value = order.value.serviceRecord!.materialsUsed
abnormalNote.value = order.value.serviceRecord!.abnormalNote
}
}
}
function validateRecord(): boolean {
if (startTime.value == '' || endTime.value == '') {
uni.showToast({ title: '请填写开始和结束时间', icon: 'none' })
return false
}
for (let i = 0; i < serviceItems.value.length; i++) {
if (serviceItems.value[i].completed == false && serviceItems.value[i].incompleteReason == '') {
uni.showToast({ title: '未完成项目需要说明原因', icon: 'none' })
return false
}
}
return true
}
async function submitRecordAction() {
if (!validateRecord()) {
return
}
if (order.value != null && order.value.status != 'in_service' && order.value.status != 'serving') {
await startServiceOrder(orderId.value)
}
const record = {
id: 'record-' + orderId.value,
orderId: orderId.value,
startTime: startTime.value,
endTime: endTime.value,
actualDurationMinutes: parseInt(actualDurationText.value),
serviceItems: serviceItems.value,
serviceContent: serviceItems.value.filter((item) => item.completed).map((item) => item.name),
processNote: processNote.value,
elderStatus: elderStatus.value,
healthMetrics: {
bloodPressure: bloodPressure.value,
heartRate: heartRate.value,
bloodSugar: bloodSugar.value,
bloodOxygen: bloodOxygen.value
},
materialsUsed: materialsUsed.value,
abnormalNote: abnormalNote.value,
photos: photoCount.value > 0 ? ['mock-photo-' + String(photoCount.value)] : [] as Array<string>,
staffRemark: staffRemark.value,
familyConfirmation: {
method: confirmMethod.value == 'sms_code' ? 'sms_code' : 'signature',
code: confirmCode.value,
signatureName: signatureName.value,
signatureUrl: '',
confirmedAt: new Date().toISOString().replace('T', ' ').substring(0, 19)
},
createdAt: new Date().toISOString().replace('T', ' ').substring(0, 19),
updatedAt: new Date().toISOString().replace('T', ' ').substring(0, 19)
} as DeliveryServiceRecordType
await submitServiceRecord(orderId.value, record)
await completeServiceOrder(orderId.value)
uni.showToast({ title: '服务记录已提交,等待验收', icon: 'success' })
setTimeout(() => {
uni.redirectTo({ url: '/pages/mall/delivery/orders/detail?id=' + orderId.value })
}, 300)
}
function submitRecord() {
submitRecordAction()
}
function goBack() {
uni.redirectTo({ url: '/pages/mall/delivery/orders/detail?id=' + orderId.value })
}
onLoad((options) => {
if (options != null) {
orderId.value = getDeliveryRouteParam(options as UTSJSONObject, 'id')
}
loadData()
})
</script>
<style scoped>
.page {
padding-bottom: 32rpx;
}
.card {
margin-bottom: 18rpx;
padding: 24rpx;
border-radius: 24rpx;
background-color: #ffffff;
box-shadow: 0 10rpx 24rpx rgba(15, 35, 55, 0.06);
}
.section-title,
.item-name,
.primary-btn-text,
.confirm-text {
font-weight: 700;
}
.section-title {
font-size: 30rpx;
color: #16324f;
}
.field,
.textarea {
margin-top: 16rpx;
padding: 0 20rpx;
border-radius: 18rpx;
background-color: #f4f8fb;
font-size: 26rpx;
color: #16324f;
box-sizing: border-box;
}
.field {
height: 84rpx;
}
.textarea {
width: 100%;
min-height: 180rpx;
padding-top: 20rpx;
padding-bottom: 20rpx;
}
.item-row {
margin-top: 16rpx;
padding: 18rpx;
border-radius: 18rpx;
background-color: #f7fbfd;
}
.item-top,
.placeholder-row,
.confirm-row,
.action-row {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
}
.item-name {
font-size: 28rpx;
color: #16324f;
}
.placeholder-row,
.action-row {
margin-top: 16rpx;
}
.placeholder-btn,
.confirm-item,
.outline-btn,
.primary-btn {
padding: 18rpx 0;
border-radius: 18rpx;
align-items: center;
justify-content: center;
}
.placeholder-btn,
.confirm-item,
.outline-btn {
background-color: #eef6fa;
}
.placeholder-btn {
width: 42%;
}
.placeholder-btn-text,
.placeholder-text,
.outline-btn-text {
font-size: 24rpx;
color: #5e758c;
}
.confirm-item {
width: 48%;
}
.confirm-item-active {
background-color: #d8eef9;
}
.confirm-text {
font-size: 26rpx;
color: #176e97;
}
.outline-btn,
.primary-btn {
width: 48%;
}
.primary-btn {
background-color: #1f7db4;
}
.primary-btn-text {
font-size: 26rpx;
color: #ffffff;
}
</style>