修改检测页

This commit is contained in:
jingna 2025-08-05 17:06:23 +08:00
parent edd6ed78f4
commit 0962146d8b
4 changed files with 562 additions and 101 deletions

View File

@ -24,13 +24,15 @@
<!-- 患者列表区域 --> <!-- 患者列表区域 -->
<div class="patient-section"> <div class="patient-section">
<div class="section-header"> <div class="section-header">
<div class="module-title">患者列表</div> <div class="module-title">
<div class="module-title-text">患者列表</div>
</div>
<div class="search-box"> <div class="search-box">
<el-input v-model="searchKeyword" placeholder="搜索患者姓名" prefix-icon="Search" clearable <el-input v-model="searchKeyword" placeholder="搜索患者姓名" prefix-icon="Search" clearable
@input="handleSearch" /> @input="handleSearch" />
</div> </div>
</div> </div>
<el-table :data="filteredPatients" style="width: 100%" border @cell-click="selectPatient" <el-table ref="tableRef" :data="filteredPatients" style="width: 100%" border @cell-click="selectPatient"
highlight-current-row> highlight-current-row>
<el-table-column prop="name" label="姓名" min-width="60" /> <el-table-column prop="name" label="姓名" min-width="60" />
<el-table-column prop="id" label="测试者ID" min-width="60" /> <el-table-column prop="id" label="测试者ID" min-width="60" />
@ -49,7 +51,9 @@
<div class="patient-detail" v-if="selectedPatient"> <div class="patient-detail" v-if="selectedPatient">
<div class="patient-detail-box"> <div class="patient-detail-box">
<div class="detail-header"> <div class="detail-header">
<div class="module-title">基础信息</div> <div class="module-title">
<div class="module-title-text">基础信息</div>
</div>
<el-button link @click="editPatient"> <el-button link @click="editPatient">
<el-icon> <el-icon>
<Edit /> <Edit />
@ -133,6 +137,112 @@
</div> </div>
</div> </div>
</div> </div>
<el-dialog v-model="dialogVisible" title="编辑基础信息" width="50%" :before-close="handleClose">
<div class="form-box" style="margin-top: 10px;">
<el-form ref="patientFormRef" :model="patientForm" :rules="formRules" label-width="100px" class="patient-form">
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="测试者ID" prop="testerId">
<el-input v-model="patientForm.id" disabled placeholder="请输入测试者ID" clearable />
</el-form-item>
</el-col>
<el-col :span="12">
</el-col>
<el-col :span="12">
<el-form-item label="姓名" prop="name" required>
<el-input v-model="patientForm.name" placeholder="请输入" clearable />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="性别" prop="gender" required>
<el-select v-model="patientForm.gender" placeholder="请选择">
<el-option label="男" value="男" />
<el-option label="女" value="女" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="出生日期" prop="birth_date" required>
<el-date-picker v-model="patientForm.birth_date" type="date" placeholder="请选择" style="width: 100%"
@change="calculateAgeres" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="年龄">
<el-input v-model="calculatedAge" placeholder="自动计算" readonly suffix-icon="Calendar" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="民族" prop="nationality">
<el-select v-model="patientForm.nationality" placeholder="请选择">
<el-option v-for="item in nationalityOptions" :key="item.value" :label="item.label"
:value="item.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="长期居住地" prop="residence">
<el-input v-model="patientForm.residence" placeholder="请输入" clearable />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="身高" prop="height" required>
<el-input v-model="patientForm.height" placeholder="请输入" clearable>
<template #suffix>cm</template>
</el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="体重" prop="weight" required>
<el-input v-model="patientForm.weight" placeholder="请输入" clearable>
<template #suffix>kg</template>
</el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="鞋码" prop="shoe_size">
<el-input v-model="patientForm.shoe_size" placeholder="请输入" clearable />
</el-form-item>
</el-col>
<el-col :span="12">
</el-col>
<el-col :span="12">
<el-form-item label="电话号码" prop="phone" required>
<el-input v-model="patientForm.phone" placeholder="请输入" clearable />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="电子邮箱" prop="email">
<el-input v-model="patientForm.email" placeholder="请输入" clearable />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="职业" prop="occupation">
<el-select v-model="patientForm.occupation" placeholder="请选择">
<el-option v-for="item in occupationOptions" :key="item.value" :label="item.label"
:value="item.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="单位" prop="workplace">
<el-input v-model="patientForm.workplace" placeholder="请输入" clearable />
</el-form-item>
</el-col>
</el-row>
</el-form>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="handleClose" style="margin-right: 20px;">退出</el-button>
<el-button type="primary" @click="handleSave" :loading="saveLoading">
保存
</el-button>
</span>
</template>
</el-dialog>
</div> </div>
</template> </template>
@ -155,7 +265,83 @@ const userInfo = reactive({
username: '医生', username: '医生',
avatar: '' avatar: ''
}) })
const tableRef = ref(null)
const dialogVisible = ref(false)
//
const patientFormRef = ref()
//
const saveLoading = ref(false)
const patientForm = ref({
id: '',
name: '',
gender: '',
birth_date: '',
nationality: '',
residence: '',
height: '',
weight: '',
shoe_size: '',
phone: '',
occupation: '',
workplace: '',
email: ''
})
const occupationOptions = ref([
{ label: '学生', value: '学生' }
])
const residenceOptions = ref([
{ label: '北京', value: '北京' }
])
const nationalityOptions = ref([
{ label: '汉族', value: '汉族' }
])
//
const formRules = {
name: [
{ required: true, message: '请输入患者姓名', trigger: 'blur' },
{ min: 2, max: 20, message: '姓名长度在 2 到 20 个字符', trigger: 'blur' }
],
gender: [
{ required: true, message: '请选择性别', trigger: 'change' }
],
birthDate: [
{ required: true, message: '请选择出生日期', trigger: 'change' }
],
height: [
{ required: true, message: '请输入身高', trigger: 'blur' },
{ pattern: /^\d+(\.\d+)?$/, message: '请输入有效的身高', trigger: 'blur' }
],
weight: [
{ required: true, message: '请输入体重', trigger: 'blur' },
{ pattern: /^\d+(\.\d+)?$/, message: '请输入有效的体重', trigger: 'blur' }
],
phone: [
{ required: true, message: '请输入联系电话', trigger: 'blur' },
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号', trigger: 'blur' }
]
}
const validateForm = async () => {
try {
await patientFormRef.value.validate()
return true
} catch (error) {
ElMessage.error('请完善必填信息')
return false
}
}
const calculatedAge = ref('')
// calculateAgeres
const calculateAgeres = (date) => {
if (!date) return '0'
const today = new Date()
const birthDate = new Date(date)
let age = today.getFullYear() - birthDate.getFullYear()
const monthDiff = today.getMonth() - birthDate.getMonth()
if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birthDate.getDate())) {
age--
}
calculatedAge.value = age
}
// //
const filteredPatients = computed(() => { const filteredPatients = computed(() => {
if (!searchKeyword.value) { if (!searchKeyword.value) {
@ -181,11 +367,71 @@ const selectPatient = (patient) => {
const editPatient = () => { const editPatient = () => {
router.push(`/patient/edit/${selectedPatient.value.id}`) // router.push(`/patient/edit/${selectedPatient.value.id}`)
//
patientForm.value = JSON.parse(JSON.stringify(selectedPatient.value))
if (patientForm.value.birth_date) {
calculatedAge.value = calculateAgeres(patientForm.value.birth_date)
}
dialogVisible.value = true
}
const handleSave = async () => {
if (!(await validateForm())) return
saveLoading.value = true
try {
await savePatient()
ElMessage.success('修改成功')
dialogVisible.value = false
saveLoading.value = false
await loadPatients()
if (patients.value.length > 0 && selectedPatient.value.id) {
for (var i = 0; i < patients.value.length; i++) {
if (patients.value[i].id === selectedPatient.value.id) {
selectedPatient.value = patients.value[i]
break
}
}
}
setTimeout(() => {
tableRef.value.setCurrentRow(selectedPatient.value)
}, 300)
} catch (error) {
ElMessage.error('修改失败:' + error.message)
saveLoading.value = false
} finally {
saveLoading.value = false
}
}
const savePatient = async () => {
const patientData = {
id: patientForm.value.id,
name: patientForm.value.name,
gender: patientForm.value.gender,
age: calculatedAge.value,
birth_date: patientForm.value.birth_date,
height: patientForm.value.height,
weight: patientForm.value.weight,
shoe_size: patientForm.value.shoe_size,
phone: patientForm.value.phone,
occupation: patientForm.value.occupation,
email: patientForm.value.email,
nationality: patientForm.value.nationality,
residence: patientForm.value.residence,
workplace: patientForm.value.workplace
}
try {
const response = await patientAPI.updatePatient(patientForm.value.id, patientData)
if (response.success) {
return response.data
} else {
throw new Error(response.message || '修改失败')
}
} catch (error) {
throw error
}
} }
const viewPatientProfile = () => { const viewPatientProfile = () => {
router.push(`/patient/profile/${selectedPatient.value.id}`) router.push(`/patient/${selectedPatient.value.id}`)
} }
const startDetection = () => { const startDetection = () => {
@ -310,7 +556,10 @@ const loadPatients = async () => {
] ]
} }
} }
const handleClose = () => {
patientFormRef.value?.resetFields()
dialogVisible.value = false
}
// //
onMounted(() => { onMounted(() => {
// //
@ -330,7 +579,7 @@ function delClick(id) {
cancelButtonText: '取消', cancelButtonText: '取消',
type: 'warning', type: 'warning',
}) })
.then(async () => { .then(async () => {
try { try {
const response = await patientAPI.deletePatient(id); const response = await patientAPI.deletePatient(id);
if (response.success) { if (response.success) {
@ -603,7 +852,7 @@ function delClick(id) {
align-items: center; align-items: center;
margin-bottom: 20px; margin-bottom: 20px;
padding-bottom: 15px; padding-bottom: 15px;
border-bottom: 1px solid #e4e7ed; border-bottom: 1px solid #434343;
} }
.detail-header h3 { .detail-header h3 {
@ -689,13 +938,22 @@ function delClick(id) {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
border: 2px solid #ffffff; /* border: 2px solid #ffffff; */
border-image: linear-gradient(to right, rgb(245, 173, 7), rgb(160, 5, 216)) 1; background: linear-gradient(to right, rgb(245, 173, 7), rgb(160, 5, 216));
border-radius: 20px; border-radius: 20px;
padding: 1px 10px;
font-size: 16px; font-size: 16px;
font-weight: bold; font-weight: bold;
color: #ffffff; color: #ffffff;
padding: 2px;
}
.module-title-text {
display: flex;
align-items: center;
width: 116px;
background: #2C2C2C;
border-radius: 20px;
padding: 0px 25px;
} }
:deep(.el-input__wrapper) { :deep(.el-input__wrapper) {
@ -763,6 +1021,60 @@ function delClick(id) {
color: #FFFFFF; color: #FFFFFF;
font-family: 'Arial Normal', 'Arial', sans-serif; font-family: 'Arial Normal', 'Arial', sans-serif;
} }
/* 必填项标识 */
:deep(.el-form-item.is-required .el-form-item__label::before) {
content: '*';
color: #f56c6c;
margin-right: 4px;
}
:deep(.el-input__wrapper) {
background-color: rgba(51, 51, 51, 1);
border-width: 1px;
border-style: solid;
border-color: rgba(127, 127, 127, 1);
border-radius: 4px;
box-shadow: none;
}
:deep(.el-select__wrapper) {
background-color: rgba(51, 51, 51, 1);
border-width: 1px;
border-style: solid;
border-color: rgba(127, 127, 127, 1);
border-radius: 4px;
box-shadow: none;
}
:deep(.el-form-item__label) {
font-size: 14px;
font-family: '苹方 粗体', '苹方 中等', '苹方', sans-serif;
font-weight: 700;
font-style: normal;
color: #FFFFFF;
}
:deep(.el-col-12) {
margin-bottom: 15px;
}
:deep(.el-input__inner) {
color: #ffffff;
}
:deep(.el-select__placeholder) {
color: #ffffff;
}
:deep(.el-input.is-disabled .el-input__wrapper) {
background-color: rgba(127, 127, 127, 1);
box-shadow: none;
border-width: 1px;
border-style: solid;
border-color: rgba(215, 215, 215, 1);
color: #FFFFFF;
}
</style> </style>
@ -817,15 +1129,45 @@ function delClick(id) {
} }
.dashboard-container.dashboard-container-home .el-input__wrapper { .dashboard-container.dashboard-container-home .el-input__wrapper {
box-shadow: 0 0 0 1px #787878; box-shadow: none;
} }
.dashboard-container.dashboard-container-home .el-table__cell{
.dashboard-container.dashboard-container-home .el-table__cell {
border-bottom: 1px solid #787878; border-bottom: 1px solid #787878;
} }
.dashboard-container.dashboard-container-home .el-table__cell{
.dashboard-container.dashboard-container-home .el-table__cell {
border-bottom: 1px solid #787878; border-bottom: 1px solid #787878;
} }
.dashboard-container.dashboard-container-home .el-table__header th { .dashboard-container.dashboard-container-home .el-table__header th {
border-color: #787878; border-color: #787878;
} }
.dashboard-container.dashboard-container-home .el-dialog {
background-color: rgba(85, 85, 85, 0.9);
}
.dashboard-container.dashboard-container-home .el-dialog__title {
color: #ffffff;
}
.dashboard-container.dashboard-container-home .el-dialog__headerbtn .el-dialog__close {
font-size: 25px;
}
.dashboard-container.dashboard-container-home .el-dialog__footer {
text-align: center;
}
.dashboard-container.dashboard-container-home .dialog-footer .el-button {
background: linear-gradient(to right, #F135A6, #A005D8);
border: none;
color: #ffffff;
height: 55px;
width: 190px;
border-radius: 50px;
font-size: 20px;
font-family: 'Arial Normal', 'Arial', sans-serif;
}
</style> </style>

View File

@ -1,37 +1,34 @@
<template> <template>
<div class="dashboard-container"> <div class="dashboard-container">
<!-- 左侧导航栏 --> <Header />
<aside class="sidebar">
<div class="sidebar-item active">
<el-icon class="sidebar-icon"><UserFilled /></el-icon>
<span class="sidebar-text">检测</span>
</div>
</aside>
<!-- 主内容区域 --> <!-- 主内容区域 -->
<div class="main-content"> <div class="main-content">
<!-- 顶部工具栏 --> <!-- 顶部工具栏 -->
<header class="top-bar"> <header class="top-bar">
<div style="display: flex;align-items: center;"> <div style="display: flex;align-items: center;">
<div class="top-bar-left" @click="routeTo('/')"> <div v-if="!isRecording" class="top-bar-left" @click="routeTo('/')">
<el-icon class="back-icon" @click="handleBack"><ArrowLeft /></el-icon> <img src="@/assets/svg/goback.svg" alt="">
<!-- <el-icon class="back-icon" @click="handleBack"><ArrowLeft /></el-icon> -->
<span class="page-title">实时检测</span> <span class="page-title">实时检测</span>
</div> </div>
<!-- <div >
{{ formattedTime }}
</div> -->
<el-button <el-button
v-if="!isStart" v-if="!isRecording"
@click="handleStartStop" @click="handleStartStop"
:disabled="!isConnected" :disabled="!isConnected"
type="primary" type="primary"
class="start-btn" class="start-btn"
style="background-image: linear-gradient(to right, rgb(236, 50, 166), rgb(160, 5, 216)); style="background-image: linear-gradient(to right, rgb(236, 50, 166), rgb(160, 5, 216));
--el-button-border-color: #409EFF; --el-button-border-color: #409EFF;
--el-button-border-color: transparent " --el-button-border-color: transparent;"
> >
{{ isConnected ? '开始检测' : '连接中...' }} {{ isConnected ? '开始' : '连接中saveRecording...' }}
</el-button> </el-button>
<!-- handleStartStop -->
<el-button <el-button
v-if="isStart" v-if="isRecording"
@click="handleStartStop" @click="handleStartStop"
type="primary" type="primary"
class="start-btn" class="start-btn"
@ -39,9 +36,20 @@
--el-button-border-color: #409EFF; --el-button-border-color: #409EFF;
--el-button-border-color: transparent " --el-button-border-color: transparent "
> >
停止检测 结束
</el-button> </el-button>
<el-button <el-button
v-if="isStart && isConnected"
@click="handleDataCollection"
type="primary"
class="start-btn"
style="background-image: linear-gradient(to right, #FBB106, #A817C6);
--el-button-border-color: #409EFF;
--el-button-border-color: transparent "
>
保存数据
</el-button>
<!-- <el-button
@click="handleStartStopRecording" @click="handleStartStopRecording"
:disabled="!isConnected" :disabled="!isConnected"
type="warning" type="warning"
@ -51,8 +59,8 @@
--el-button-border-color: transparent; margin-left: 10px;" --el-button-border-color: transparent; margin-left: 10px;"
> >
{{ isRecording ? '停止录制' : '开始录制' }} {{ isRecording ? '停止录制' : '开始录制' }}
</el-button> </el-button> -->
<el-button <!-- <el-button
v-if="isStart == true" v-if="isStart == true"
@click="handleDataCollection" @click="handleDataCollection"
:loading="dataCollectionLoading" :loading="dataCollectionLoading"
@ -62,7 +70,7 @@
--el-button-border-color: transparent; margin-left: 10px;" --el-button-border-color: transparent; margin-left: 10px;"
> >
检测数据采集 检测数据采集
</el-button> </el-button> -->
</div> </div>
@ -331,6 +339,37 @@
</div> </div>
</div> </div>
</div> </div>
<el-dialog v-model="dialogVisible" center title="诊断信息" width="600" :before-close="handleClose" >
<div>
<div class="dialog-title">
<div class="dialog-title-item"><div>用户ID:</div>{{patientInfo.sessionId}}</div>
<div class="dialog-title-item"><div>姓名:{{patientInfo.name}}</div></div>
</div>
<div>
<el-form :model="diagnosticForm" label-width="50px">
<el-form-item label="记录">
<el-input v-model="diagnosticForm.textarea" :rows="6" type="textarea" placeholder="请输入"/>
</el-form-item>
<el-form-item label="处理">
<el-input v-model="diagnosticForm.textarea" :rows="6" type="textarea" placeholder="请输入"/>
</el-form-item>
<el-form-item label="建议">
<el-input v-model="diagnosticForm.textarea" :rows="6" type="textarea" placeholder="请输入"/>
</el-form-item>
</el-form>
</div>
<div style="display: flex;justify-content: flex-end;">测试医生李四</div>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button @click="handleDataCollection">暂存</el-button>
<el-button type="primary" @click="handleDataCollection">
保存
</el-button>
</span>
</template>
</el-dialog>
</div> </div>
</template> </template>
@ -340,9 +379,11 @@ import { ElMessage } from 'element-plus'
import { useRouter, useRoute } from 'vue-router' import { useRouter, useRoute } from 'vue-router'
import { io } from 'socket.io-client' import { io } from 'socket.io-client'
import html2canvas from 'html2canvas' import html2canvas from 'html2canvas'
import Header from '@/views/Header.vue'
import { useAuthStore } from '../stores/index.js'
const authStore = useAuthStore()
const router = useRouter() const router = useRouter()
const route = useRoute() const route = useRoute()
const isStart = ref(false) const isStart = ref(false)
const isConnected = ref(false) const isConnected = ref(false)
const rtspImgSrc = ref('') const rtspImgSrc = ref('')
@ -378,10 +419,14 @@ let socket = null
let frameCount = 0 let frameCount = 0
// //
const BACKEND_URL = 'http://localhost:5000' const BACKEND_URL = 'http://192.168.1.173:5000'
const formattedTime = ref(0)
const dialogVisible = ref(false)
const handleClose = () => {
dialogVisible.value = false
}
const diagnosticForm = ref({})
// //
const historyData = ref([ const historyData = ref([
{ id: 3, rotLeft: '-55.2°', rotRight: '54.2°', tiltLeft: '-17.7°', tiltRight: '18.2°', pitchDown: '-20.2°', pitchUp: '10.5°' }, { id: 3, rotLeft: '-55.2°', rotRight: '54.2°', tiltLeft: '-17.7°', tiltRight: '18.2°', pitchDown: '-20.2°', pitchUp: '10.5°' },
@ -568,7 +613,6 @@ function handleStartStopRecording() {
// //
async function handleDataCollection() { async function handleDataCollection() {
if (dataCollectionLoading.value) return if (dataCollectionLoading.value) return
try { try {
dataCollectionLoading.value = true dataCollectionLoading.value = true
@ -587,8 +631,11 @@ async function handleDataCollection() {
'Content-Type': 'application/json' 'Content-Type': 'application/json'
}, },
body: JSON.stringify({ body: JSON.stringify({
patient_id: patientInfo.value.id, // patient_id: patientInfo.value.id,
timestamp: Date.now() // timestamp: Date.now()
head_pose:{},
body_pose:{},
foot_data:{}
}) })
}) })
@ -934,7 +981,6 @@ function stopRecording() {
saveRecording() saveRecording()
} }
} }
if (recordingStream) { if (recordingStream) {
recordingStream.getTracks().forEach(track => track.stop()) recordingStream.getTracks().forEach(track => track.stop())
recordingStream = null recordingStream = null
@ -955,7 +1001,7 @@ async function saveRecording() {
if (recordedChunks.length === 0) { if (recordedChunks.length === 0) {
throw new Error('没有录制数据') throw new Error('没有录制数据')
} }
console.log()
// //
if (!patientInfo.value.id || !patientInfo.value.name || !patientInfo.value.sessionId) { if (!patientInfo.value.id || !patientInfo.value.name || !patientInfo.value.sessionId) {
throw new Error(`缺少必需的患者信息: ID=${patientInfo.value.id}, 姓名=${patientInfo.value.name}, 会话ID=${patientInfo.value.sessionId}`) throw new Error(`缺少必需的患者信息: ID=${patientInfo.value.id}, 姓名=${patientInfo.value.name}, 会话ID=${patientInfo.value.sessionId}`)
@ -1006,7 +1052,7 @@ async function saveRecording() {
message: `录像保存成功!文件路径: ${result.filepath}`, message: `录像保存成功!文件路径: ${result.filepath}`,
duration: 5000 duration: 5000
}) })
dialogVisible.value = false
// //
if (patientInfo.value.sessionId) { if (patientInfo.value.sessionId) {
try { try {
@ -1015,15 +1061,15 @@ async function saveRecording() {
console.error('更新会话视频路径失败:', error) console.error('更新会话视频路径失败:', error)
} }
} }
// //
recordedChunks.length = 0 recordedChunks.length = 0
console.log('🧹 录像数据已清空') console.log('🧹 录像数据已清空')
// ID // ID
patientInfo.value.sessionId = null // patientInfo.value.sessionId = null
console.log('✅ 会话正式结束会话ID已清空') console.log('✅ 会话正式结束会话ID已清空')
} else { } else {
dialogVisible.value = false
throw new Error(result.message || '保存失败') throw new Error(result.message || '保存失败')
} }
@ -1033,7 +1079,7 @@ async function saveRecording() {
message: `保存录像失败: ${error.message}`, message: `保存录像失败: ${error.message}`,
duration: 5000 duration: 5000
}) })
dialogVisible.value = false
// 使ID // 使ID
patientInfo.value.sessionId = null patientInfo.value.sessionId = null
console.log('⚠️ 录像保存失败但会话已结束会话ID已清空') console.log('⚠️ 录像保存失败但会话已结束会话ID已清空')
@ -1059,6 +1105,7 @@ async function saveRecording() {
// / // /
async function handleStartStop() { async function handleStartStop() {
patientInfo.value.sessionId = null
if (!isConnected.value) { if (!isConnected.value) {
ElMessage.warning('WebSocket未连接无法操作') ElMessage.warning('WebSocket未连接无法操作')
return return
@ -1072,17 +1119,14 @@ async function handleStartStop() {
await startDetection() await startDetection()
} }
} }
// //
async function startDetection() { async function startDetection() {
try { try {
console.log('🚀 正在开始检测...') console.log('🚀 正在开始检测...')
// //
if (!patientInfo.value || !patientInfo.value.id) { if (!patientInfo.value || !patientInfo.value.id) {
throw new Error('缺少患者信息,无法开始检测') throw new Error('缺少患者信息,无法开始检测')
} }
// API // API
const response = await fetch(`${BACKEND_URL}/api/detection/start`, { const response = await fetch(`${BACKEND_URL}/api/detection/start`, {
method: 'POST', method: 'POST',
@ -1090,31 +1134,31 @@ async function startDetection() {
'Content-Type': 'application/json' 'Content-Type': 'application/json'
}, },
body: JSON.stringify({ body: JSON.stringify({
patientId: patientInfo.value.id, patient_id: patientInfo.value.id,
// //
settings: JSON.stringify({ creator_id:creatorId.value,
frequency: 30, // // settings: JSON.stringify({
// // frequency: 30, //
}) // //
// })
}) })
}) })
if (!response.ok) { if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`) throw new Error(`HTTP ${response.status}: ${response.statusText}`)
} }
const result = await response.json() const result = await response.json()
if (result.success) { if (result.success) {
console.log('✅ 检测开始成功') console.log('✅ 检测开始成功')
// ID // ID
patientInfo.value.sessionId = result.data.session_id patientInfo.value.sessionId = result.session_id
patientInfo.value.detectionStartTime = Date.now() patientInfo.value.detectionStartTime = Date.now()
console.log('✅ 检测会话创建成功会话ID:', patientInfo.value.sessionId) console.log('✅ 检测会话创建成功会话ID:', patientInfo.value.sessionId)
isStart.value = true isStart.value = true
startRecording()
ElMessage.success('检测已开始') ElMessage.success('检测已开始')
} else { } else {
throw new Error(result.message || '开始检测失败') throw new Error(result.message || '开始检测失败')
@ -1161,16 +1205,18 @@ async function stopDetection() {
const result = await response.json() const result = await response.json()
if (result.success) { if (result.success) {
console.log('✅ 检测会话已停止') console.log('✅ 检测会话已停止')
//
dialogVisible.value = true
if (result.duration) { if (result.duration) {
console.log(`⏱️ 检测持续时间: ${result.duration}`) console.log(`⏱️ 检测持续时间: ${result.duration}`)
} }
} }
} }
} }
dialogVisible.value = true
// ID // ID
patientInfo.value.detectionStartTime = null patientInfo.value.detectionStartTime = null
patientInfo.value.sessionId = null // patientInfo.value.sessionId = null
ElMessage.success('检测已停止,视频继续播放') ElMessage.success('检测已停止,视频继续播放')
@ -1233,7 +1279,7 @@ const handleBeforeUnload = () => {
socket.disconnect() socket.disconnect()
} }
} }
const creatorId = ref('')
onMounted(() => { onMounted(() => {
// //
loadPatientInfo() loadPatientInfo()
@ -1243,6 +1289,10 @@ onMounted(() => {
// //
window.addEventListener('beforeunload', handleBeforeUnload) window.addEventListener('beforeunload', handleBeforeUnload)
if (authStore.currentUser) {
console.log(authStore.currentUser)
creatorId.value = authStore.currentUser.id
}
}) })
onUnmounted(() => { onUnmounted(() => {
@ -1274,9 +1324,9 @@ onUnmounted(() => {
<style scoped> <style scoped>
/* 全局容器 */ /* 全局容器 */
.dashboard-container { .dashboard-container {
display: flex; /* display: flex; */
height: 100vh; height: 100vh;
background-color: #1E1E1E; background-color: #000000;
color: #FFFFFF; color: #FFFFFF;
} }
@ -1324,7 +1374,7 @@ onUnmounted(() => {
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
padding: 12px 20px; padding: 12px 20px;
background-color: #2C2C2C; background-color: #000000;
} }
.top-bar-left { .top-bar-left {
@ -1341,6 +1391,7 @@ onUnmounted(() => {
.page-title { .page-title {
font-size: 18px; font-size: 18px;
font-weight: bold; font-weight: bold;
margin-left: 20px;
} }
.start-btn { .start-btn {
@ -1357,7 +1408,7 @@ onUnmounted(() => {
.info-item { .info-item {
font-size: 14px; font-size: 14px;
margin-right: 20px; margin-right: 20px;
color: #CCCCCC; color: #ffffff;
} }
.top-icon { .top-icon {
@ -1682,6 +1733,15 @@ onUnmounted(() => {
align-content: center; align-content: center;
height: 100%; height: 100%;
} }
.dialog-title{
display: flex;
justify-content:space-between ;
margin-bottom: 20px;
}
.dialog-title-item{
display: flex;
width: 200px;
}
</style> </style>
<style> <style>
@ -1704,4 +1764,11 @@ onUnmounted(() => {
.el-table td.el-table__cell, .el-table th.el-table__cell.is-leaf{ .el-table td.el-table__cell, .el-table th.el-table__cell.is-leaf{
background-color: transparent !important; background-color: transparent !important;
} }
.top-bar .el-button{
height: 44px;
border-radius: 25px;
font-size: 28px;
border-radius:20px;
width: 220px;
}
</style> </style>

View File

@ -17,8 +17,11 @@
<div class="form-container"> <div class="form-container">
<el-form ref="patientFormRef" :model="patientForm" :rules="formRules" label-width="120px" class="patient-form"> <el-form ref="patientFormRef" :model="patientForm" :rules="formRules" label-width="120px" class="patient-form">
<div class="form-section"> <div class="form-section">
<!-- <h3 class="section-title">基本信息</h3> --> <div class="section-title">
<div class="section-title">基本信息</div> <div class="section-title-text">
基本信息
</div>
</div>
<el-row :gutter="20"> <el-row :gutter="20">
<el-col :span="12"> <el-col :span="12">
@ -63,10 +66,7 @@
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12">
<el-form-item label="长期居住地" prop="residence"> <el-form-item label="长期居住地" prop="residence">
<el-select v-model="patientForm.residence" placeholder="请选择"> <el-input v-model="patientForm.residence" placeholder="请输入" clearable />
<el-option v-for="item in residenceOptions" :key="item.value" :label="item.label"
:value="item.value" />
</el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12">
@ -334,6 +334,7 @@ const handleSaveAndDetect = async () => {
font-size: 18px; font-size: 18px;
width: calc(100% - 500px); width: calc(100% - 500px);
display: flex; display: flex;
cursor: pointer;
} }
.nav-container-info { .nav-container-info {
@ -391,16 +392,25 @@ const handleSaveAndDetect = async () => {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
border: 2px solid #ffffff; /* border: 2px solid #ffffff; */
border-image: linear-gradient(to right, rgb(245, 173, 7), rgb(160, 5, 216)) 1; background: linear-gradient(to right, rgb(245, 173, 7), rgb(160, 5, 216));
border-radius: 20px; border-radius: 20px;
padding: 1px 10px;
font-size: 16px; font-size: 16px;
font-weight: bold; font-weight: bold;
color: #ffffff; color: #ffffff;
padding: 2px;
margin-bottom: 20px; margin-bottom: 20px;
} }
.section-title-text {
display: flex;
align-items: center;
width: 116px;
background: #2C2C2C;
border-radius: 20px;
padding: 0px 25px;
}
.footer-actions { .footer-actions {
height: 142px; height: 142px;
background: #000000; background: #000000;
@ -412,8 +422,9 @@ const handleSaveAndDetect = async () => {
padding: 0 20px; padding: 0 20px;
box-shadow: 0 -2px 4px rgba(0, 0, 0, 0.1); box-shadow: 0 -2px 4px rgba(0, 0, 0, 0.1);
} }
:deep(.footer-actions .el-button){
background: linear-gradient(to right,#F135A6,#A005D8,#A005D8); :deep(.footer-actions .el-button) {
background: linear-gradient(to right, #F135A6, #A005D8);
border: none; border: none;
color: #ffffff; color: #ffffff;
height: 60px; height: 60px;

View File

@ -1,25 +1,25 @@
<template> <template>
<div class="patient-profile-container"> <div class="patient-profile-container">
<!-- 顶部导航 --> <!-- 顶部导航 -->
<div class="header"> <Header />
<div class="header-left"> <div class="nav-container">
<el-button link @click="goBack" class="back-btn"> <div class="nav-container-title" @click="goBack">
<el-icon><ArrowLeft /></el-icon> <img src="@/assets/svg/goback.svg" alt="">
返回 <div style="margin-left: 20px;">
</el-button> 患者档案
<h1 class="page-title">患者档案</h1> </div>
</div> </div>
<div class="header-right"> <div class="nav-container-info">
<el-button type="primary" @click="startDetection"> <div>测试时间2025-08-03 17:13:18<span></span></div>
<el-icon><Monitor /></el-icon> <div style="margin-left: 15px;">测试医生<span>李医生</span></div>
开始检测
</el-button>
</div> </div>
</div> </div>
<div class="main-content">
<div class="main-content" v-if="patient"> <!-- <div v-for="value in source">
{{}}
</div> -->
<!-- 患者基本信息 --> <!-- 患者基本信息 -->
<div class="patient-info-card"> <div v-if="patient" class="patient-info-card">
<div class="card-header"> <div class="card-header">
<h2>基本信息</h2> <h2>基本信息</h2>
<el-button link @click="editPatient"> <el-button link @click="editPatient">
@ -70,7 +70,6 @@
</div> </div>
</div> </div>
</div> </div>
<!-- 检测记录 --> <!-- 检测记录 -->
<div class="detection-records-card"> <div class="detection-records-card">
<div class="card-header"> <div class="card-header">
@ -142,11 +141,6 @@
</div> </div>
</div> </div>
<!-- 加载状态 -->
<div v-else class="loading-container">
<el-loading-service lock="true" text="加载患者信息中..." />
</div>
<!-- 视频播放对话框 --> <!-- 视频播放对话框 -->
<el-dialog <el-dialog
v-model="videoDialogVisible" v-model="videoDialogVisible"
@ -238,6 +232,7 @@ import { ref, reactive, computed, onMounted } from 'vue'
import { useRouter, useRoute } from 'vue-router' import { useRouter, useRoute } from 'vue-router'
import { ElMessage, ElMessageBox } from 'element-plus' import { ElMessage, ElMessageBox } from 'element-plus'
import { patientAPI, detectionAPI } from '../services/api.js' import { patientAPI, detectionAPI } from '../services/api.js'
import Header from '@/views/Header.vue'
const router = useRouter() const router = useRouter()
const route = useRoute() const route = useRoute()
@ -396,6 +391,7 @@ const loadPatientInfo = async () => {
const response = await patientAPI.getById(patientId) const response = await patientAPI.getById(patientId)
if (response.success) { if (response.success) {
patient.value = response.data patient.value = response.data
console.log('加载患者信息成功:', response.data)
} else { } else {
throw new Error(response.message) throw new Error(response.message)
} }
@ -494,9 +490,32 @@ onMounted(() => {
height: 100vh; height: 100vh;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
background: #f5f7fa; background: #000000;
}
.nav-container {
display: flex;
width: 100%;
padding: 15px 20px 0px;
gap: 20px;
} }
.nav-container-title {
font-family: 'Arial Negreta', 'Arial Normal', 'Arial', sans-serif;
font-weight: 700;
font-style: normal;
color: #FFFFFF;
font-size: 18px;
width: calc(100% - 500px);
display: flex;
cursor: pointer;
}
.nav-container-info {
display: flex;
font-size: 16px;
color: #FFFFFF;
font-family: 'Arial Normal', 'Arial', sans-serif;
}
.header { .header {
height: 60px; height: 60px;
background: #fff; background: #fff;
@ -768,4 +787,26 @@ onMounted(() => {
.data-item:last-child { .data-item:last-child {
margin-bottom: 0; margin-bottom: 0;
} }
.main-dashboard-top {
display: flex;
width: 100%;
padding: 15px 20px 0px;
gap: 20px;
}
.main-dashboard-top-title {
font-family: 'Arial Negreta', 'Arial Normal', 'Arial', sans-serif;
font-weight: 700;
font-style: normal;
color: #FFFFFF;
font-size: 18px;
width: calc(100% - 500px);
}
.main-dashboard-top-info {
display: flex;
font-size: 16px;
color: #FFFFFF;
font-family: 'Arial Normal', 'Arial', sans-serif;
}
</style> </style>