diff --git a/frontend/src/renderer/src/views/Dashboard.vue b/frontend/src/renderer/src/views/Dashboard.vue
index 354823de..c08ab4f9 100644
--- a/frontend/src/renderer/src/views/Dashboard.vue
+++ b/frontend/src/renderer/src/views/Dashboard.vue
@@ -24,13 +24,15 @@
-
@@ -49,7 +51,9 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ cm
+
+
+
+
+
+
+ kg
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -155,7 +265,83 @@ const userInfo = reactive({
username: '医生',
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(() => {
if (!searchKeyword.value) {
@@ -181,11 +367,71 @@ const selectPatient = (patient) => {
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 = () => {
- router.push(`/patient/profile/${selectedPatient.value.id}`)
+ router.push(`/patient/${selectedPatient.value.id}`)
}
const startDetection = () => {
@@ -310,7 +556,10 @@ const loadPatients = async () => {
]
}
}
-
+const handleClose = () => {
+ patientFormRef.value?.resetFields()
+ dialogVisible.value = false
+}
// 生命周期
onMounted(() => {
// 从认证状态管理中加载用户信息
@@ -330,7 +579,7 @@ function delClick(id) {
cancelButtonText: '取消',
type: 'warning',
})
- .then(async () => {
+ .then(async () => {
try {
const response = await patientAPI.deletePatient(id);
if (response.success) {
@@ -603,7 +852,7 @@ function delClick(id) {
align-items: center;
margin-bottom: 20px;
padding-bottom: 15px;
- border-bottom: 1px solid #e4e7ed;
+ border-bottom: 1px solid #434343;
}
.detail-header h3 {
@@ -689,13 +938,22 @@ function delClick(id) {
display: flex;
align-items: center;
justify-content: center;
- border: 2px solid #ffffff;
- border-image: linear-gradient(to right, rgb(245, 173, 7), rgb(160, 5, 216)) 1;
+ /* border: 2px solid #ffffff; */
+ background: linear-gradient(to right, rgb(245, 173, 7), rgb(160, 5, 216));
border-radius: 20px;
- padding: 1px 10px;
font-size: 16px;
font-weight: bold;
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) {
@@ -763,6 +1021,60 @@ function delClick(id) {
color: #FFFFFF;
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;
+}
@@ -817,15 +1129,45 @@ function delClick(id) {
}
.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;
}
-.dashboard-container.dashboard-container-home .el-table__cell{
+
+.dashboard-container.dashboard-container-home .el-table__cell {
border-bottom: 1px solid #787878;
}
+
.dashboard-container.dashboard-container-home .el-table__header th {
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;
+}
\ No newline at end of file
diff --git a/frontend/src/renderer/src/views/Detection.vue b/frontend/src/renderer/src/views/Detection.vue
index cf5c79ac..79d9898e 100644
--- a/frontend/src/renderer/src/views/Detection.vue
+++ b/frontend/src/renderer/src/views/Detection.vue
@@ -1,37 +1,34 @@
-
-
-
+
-
-
-
+
+

+
实时检测
+
- {{ isConnected ? '开始检测' : '连接中...' }}
+ {{ isConnected ? '开始' : '连接中saveRecording...' }}
+
- 停止检测
+ 结束
+ 保存数据
+
+
+
@@ -331,6 +339,37 @@
+
+
+
+
用户ID:
{{patientInfo.sessionId}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
测试医生:李四
+
+
+
+
+
@@ -340,9 +379,11 @@ import { ElMessage } from 'element-plus'
import { useRouter, useRoute } from 'vue-router'
import { io } from 'socket.io-client'
import html2canvas from 'html2canvas'
+import Header from '@/views/Header.vue'
+import { useAuthStore } from '../stores/index.js'
+const authStore = useAuthStore()
const router = useRouter()
const route = useRoute()
-
const isStart = ref(false)
const isConnected = ref(false)
const rtspImgSrc = ref('')
@@ -378,10 +419,14 @@ let socket = null
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([
{ 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() {
if (dataCollectionLoading.value) return
-
try {
dataCollectionLoading.value = true
@@ -587,8 +631,11 @@ async function handleDataCollection() {
'Content-Type': 'application/json'
},
body: JSON.stringify({
- patient_id: patientInfo.value.id,
- timestamp: Date.now()
+ // patient_id: patientInfo.value.id,
+ // timestamp: Date.now()
+ head_pose:{},
+ body_pose:{},
+ foot_data:{}
})
})
@@ -934,7 +981,6 @@ function stopRecording() {
saveRecording()
}
}
-
if (recordingStream) {
recordingStream.getTracks().forEach(track => track.stop())
recordingStream = null
@@ -955,7 +1001,7 @@ async function saveRecording() {
if (recordedChunks.length === 0) {
throw new Error('没有录制数据')
}
-
+ console.log()
// 验证必需的患者信息
if (!patientInfo.value.id || !patientInfo.value.name || !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}`,
duration: 5000
})
-
+ dialogVisible.value = false
// 更新会话的视频路径
if (patientInfo.value.sessionId) {
try {
@@ -1015,15 +1061,15 @@ async function saveRecording() {
console.error('更新会话视频路径失败:', error)
}
}
-
// 清空录制数据,避免重复保存
recordedChunks.length = 0
console.log('🧹 录像数据已清空')
// 录像保存完成后,清空会话ID,正式结束会话
- patientInfo.value.sessionId = null
+ // patientInfo.value.sessionId = null
console.log('✅ 会话正式结束,会话ID已清空')
} else {
+ dialogVisible.value = false
throw new Error(result.message || '保存失败')
}
@@ -1033,7 +1079,7 @@ async function saveRecording() {
message: `保存录像失败: ${error.message}`,
duration: 5000
})
-
+ dialogVisible.value = false
// 即使保存失败,也要清空会话ID,避免状态混乱
patientInfo.value.sessionId = null
console.log('⚠️ 录像保存失败,但会话已结束,会话ID已清空')
@@ -1059,6 +1105,7 @@ async function saveRecording() {
// 处理开始/停止按钮点击
async function handleStartStop() {
+ patientInfo.value.sessionId = null
if (!isConnected.value) {
ElMessage.warning('WebSocket未连接,无法操作')
return
@@ -1072,17 +1119,14 @@ async function handleStartStop() {
await startDetection()
}
}
-
// 开始检测
async function startDetection() {
try {
console.log('🚀 正在开始检测...')
-
// 验证患者信息
if (!patientInfo.value || !patientInfo.value.id) {
throw new Error('缺少患者信息,无法开始检测')
}
-
// 调用后端API开始检测
const response = await fetch(`${BACKEND_URL}/api/detection/start`, {
method: 'POST',
@@ -1090,31 +1134,31 @@ async function startDetection() {
'Content-Type': 'application/json'
},
body: JSON.stringify({
- patientId: patientInfo.value.id,
+ patient_id: patientInfo.value.id,
// 可以添加其他检测参数
- settings: JSON.stringify({
- frequency: 30, // 采样频率
- // 其他设置参数
- })
+ creator_id:creatorId.value,
+ // settings: JSON.stringify({
+ // frequency: 30, // 采样频率
+ // // 其他设置参数
+ // })
})
})
-
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`)
}
const result = await response.json()
-
+
if (result.success) {
console.log('✅ 检测开始成功')
-
+
// 保存会话ID和检测开始时间
- patientInfo.value.sessionId = result.data.session_id
+ patientInfo.value.sessionId = result.session_id
patientInfo.value.detectionStartTime = Date.now()
console.log('✅ 检测会话创建成功,会话ID:', patientInfo.value.sessionId)
isStart.value = true
-
+ startRecording()
ElMessage.success('检测已开始')
} else {
throw new Error(result.message || '开始检测失败')
@@ -1161,16 +1205,18 @@ async function stopDetection() {
const result = await response.json()
if (result.success) {
console.log('✅ 检测会话已停止')
+ //停止弹出诊断信息
+ dialogVisible.value = true
if (result.duration) {
console.log(`⏱️ 检测持续时间: ${result.duration}秒`)
}
}
}
}
-
+ dialogVisible.value = true
// 清除检测开始时间和会话ID
patientInfo.value.detectionStartTime = null
- patientInfo.value.sessionId = null
+ // patientInfo.value.sessionId = null
ElMessage.success('检测已停止,视频继续播放')
@@ -1233,7 +1279,7 @@ const handleBeforeUnload = () => {
socket.disconnect()
}
}
-
+const creatorId = ref('')
onMounted(() => {
// 加载患者信息
loadPatientInfo()
@@ -1243,6 +1289,10 @@ onMounted(() => {
// 监听页面关闭或刷新事件
window.addEventListener('beforeunload', handleBeforeUnload)
+ if (authStore.currentUser) {
+ console.log(authStore.currentUser)
+ creatorId.value = authStore.currentUser.id
+ }
})
onUnmounted(() => {
@@ -1274,9 +1324,9 @@ onUnmounted(() => {
\ No newline at end of file
diff --git a/frontend/src/renderer/src/views/PatientCreate.vue b/frontend/src/renderer/src/views/PatientCreate.vue
index 137b7e57..3d526fdc 100644
--- a/frontend/src/renderer/src/views/PatientCreate.vue
+++ b/frontend/src/renderer/src/views/PatientCreate.vue
@@ -17,8 +17,11 @@