增加了快捷输入的问题。

This commit is contained in:
root 2025-12-12 17:33:14 +08:00
parent 8928732f0a
commit 01062249e3
12 changed files with 359 additions and 21 deletions

Binary file not shown.

View File

@ -1,6 +1,6 @@
[APP]
name = Body Balance Evaluation System
version = 1.0.0
version = 1.5.0
debug = True
log_level = INFO

View File

@ -21,5 +21,5 @@ __all__ = [
'DeviceCoordinator'
]
__version__ = '1.0.0'
__version__ = '1.5.0'
__author__ = 'Body Balance Detection System'

View File

@ -398,7 +398,7 @@ class LicenseManager:
request_data = {
"product": "BodyBalanceEvaluation",
"version": "1.0.0",
"version": "1.5.0",
"machine_id": machine_id,
"platform": platform.system(),
"request_time": datetime.now(timezone.utc).isoformat(),

View File

@ -351,7 +351,7 @@ class AppServer:
return jsonify({
'status': 'healthy',
'timestamp': datetime.now().isoformat(),
'version': '1.0.0'
'version': '1.5.0'
})
# ==================== 授权API ====================
@ -381,7 +381,80 @@ class AppServer:
except Exception as e:
self.logger.error(f'获取授权信息失败: {e}')
return jsonify({'success': False, 'error': str(e)}), 500
@self.app.route('/api/common-items', methods=['GET'])
def get_common_items():
try:
base_dir = os.path.dirname(sys.executable) if getattr(sys, 'frozen', False) else os.path.dirname(os.path.abspath(__file__))
data_dir = os.path.join(base_dir, 'data')
os.makedirs(data_dir, exist_ok=True)
fpath = os.path.join(data_dir, 'common_items.json')
if not os.path.exists(fpath):
with open(fpath, 'w', encoding='utf-8') as f:
json.dump({'treatment': [], 'suggestion': []}, f, ensure_ascii=False)
with open(fpath, 'r', encoding='utf-8') as f:
data = json.load(f)
return jsonify({'success': True, 'data': data})
except Exception as e:
return jsonify({'success': False, 'error': str(e)}), 500
@self.app.route('/api/common-items', methods=['POST'])
def add_common_item():
try:
payload = flask_request.get_json(force=True) or {}
itype = str(payload.get('type', '')).lower()
label = str(payload.get('label', '')).strip()
if itype not in ['treatment', 'suggestion'] or not label:
return jsonify({'success': False, 'error': '参数无效'}), 400
base_dir = os.path.dirname(sys.executable) if getattr(sys, 'frozen', False) else os.path.dirname(os.path.abspath(__file__))
data_dir = os.path.join(base_dir, 'data')
os.makedirs(data_dir, exist_ok=True)
fpath = os.path.join(data_dir, 'common_items.json')
data = {'treatment': [], 'suggestion': []}
if os.path.exists(fpath):
try:
with open(fpath, 'r', encoding='utf-8') as f:
data = json.load(f)
except Exception:
pass
items = list(data.get(itype, []))
if label not in items:
items.append(label)
data[itype] = items
with open(fpath, 'w', encoding='utf-8') as f:
json.dump(data, f, ensure_ascii=False, indent=2)
return jsonify({'success': True, 'data': {itype: items}})
except Exception as e:
return jsonify({'success': False, 'error': str(e)}), 500
@self.app.route('/api/common-items', methods=['DELETE'])
def delete_common_item():
try:
payload = flask_request.get_json(force=True) or {}
itype = str(payload.get('type', '')).lower()
label = str(payload.get('label', '')).strip()
if itype not in ['treatment', 'suggestion'] or not label:
return jsonify({'success': False, 'error': '参数无效'}), 400
base_dir = os.path.dirname(sys.executable) if getattr(sys, 'frozen', False) else os.path.dirname(os.path.abspath(__file__))
data_dir = os.path.join(base_dir, 'data')
os.makedirs(data_dir, exist_ok=True)
fpath = os.path.join(data_dir, 'common_items.json')
data = {'treatment': [], 'suggestion': []}
if os.path.exists(fpath):
try:
with open(fpath, 'r', encoding='utf-8') as f:
data = json.load(f)
except Exception:
pass
items = [x for x in list(data.get(itype, [])) if x != label]
data[itype] = items
with open(fpath, 'w', encoding='utf-8') as f:
json.dump(data, f, ensure_ascii=False, indent=2)
return jsonify({'success': True, 'data': {itype: items}})
except Exception as e:
return jsonify({'success': False, 'error': str(e)}), 500
@self.app.route('/api/license/activation-request', methods=['POST'])
def generate_activation_request():
"""生成离线激活请求文件"""

View File

@ -46,7 +46,7 @@ class Config:
# 应用配置
self.config['APP'] = {
'name': 'Body Balance Evaluation System',
'version': '1.0.0',
'version': '1.5.0',
'debug': 'false',
'log_level': 'INFO'
}

View File

@ -1,6 +1,6 @@
[APP]
name = Body Balance Evaluation System
version = 1.0.0
version = 1.5.0
debug = false
log_level = INFO

View File

@ -61,7 +61,7 @@
- 功能:健康检查与服务存活状态。
- 示例响应:
```json
{ "status": "healthy", "timestamp": "2024-01-01T12:00:00", "version": "1.0.0" }
{ "status": "healthy", "timestamp": "2024-01-01T12:00:00", "version": "1.5.0" }
```
### GET /api/license/info

View File

@ -278,6 +278,18 @@ export const licenseAPI = {
}
}
export const commonItemsAPI = {
list() {
return api.get('/api/common-items')
},
add(type, label) {
return api.post('/api/common-items', { type, label })
},
remove(type, label) {
return api.delete('/api/common-items', { data: { type, label } })
}
}
// 录制API
export const recordingAPI = {
// 创建录制
@ -673,4 +685,4 @@ export const getBackendUrl = () => {
}
}
export default api
export default api

View File

@ -11,14 +11,27 @@
<el-form-item label="记录">
<el-input v-model="diagnosticForm.diagnosis_info" resize="none" :rows="10" type="textarea" placeholder="记录信息" />
</el-form-item>
<el-form-item label="处理">
<el-select v-model="diagnosticForm.treatment_info" placeholder="请选择">
<el-option :label="'保持观察,不予处理'" :value="'保持观察,不予处理'" />
</el-select>
<!-- <el-input v-model="diagnosticForm.treatment_info" :rows="6" type="textarea" placeholder="请输入" /> -->
</el-form-item>
<el-form-item label="建议">
<el-input v-model="diagnosticForm.suggestion_info" resize="none" :rows="5" type="textarea" placeholder="备注信息" />
<el-form-item label="处理/建议">
<div style="display:flex;gap:6px;align-items:center;">
<el-select v-model="diagnosticForm.treatment_info" placeholder="请选择" filterable allow-create default-first-option clearable style="flex:1;">
<el-option v-for="opt in commonItems.treatment" :key="opt" :label="opt" :value="opt" />
</el-select>
<el-tooltip content="选择处理" placement="top">
<el-button circle :icon="List" size="small" @click="treatmentDialogVisible=true" />
</el-tooltip>
<el-tooltip content="新增到常用项" placement="top">
<el-button circle :icon="Plus" size="small" @click="addTreatmentFromInput" />
</el-tooltip>
</div>
<el-input v-model="diagnosticForm.suggestion_info" resize="none" :rows="5" type="textarea" placeholder="备注信息" style="margin-top:8px;" />
<div style="margin-top:8px;display:flex;gap:6px;align-items:center;">
<el-tooltip content="选择建议" placement="top">
<el-button circle :icon="List" size="small" @click="suggestionDialogVisible=true" />
</el-tooltip>
<el-tooltip content="新增到常用项" placement="top">
<el-button circle :icon="Plus" size="small" @click="addSuggestionFromInput" />
</el-tooltip>
</div>
</el-form-item>
</el-form>
<div class="form-actions-display">
@ -33,6 +46,45 @@
</div>
</div>
<el-dialog v-model="suggestionDialogVisible" title="选择建议" width="600px">
<el-input v-model="suggestionSearch" placeholder="搜索建议" clearable />
<div style="margin-top:8px;max-height:240px;overflow:auto;">
<div v-for="opt in filteredSuggestions10" :key="opt" style="display:flex;align-items:center;justify-content:space-between;padding:6px 8px;border-bottom:1px solid #f0f0f0;color:#fff;">
<span>{{opt}}</span>
<div style="display:flex;gap:6px;">
<el-tooltip content="选中" placement="top">
<el-button circle :icon="CircleCheck" size="small" @click="onPickSuggestion(opt)" />
</el-tooltip>
<el-tooltip content="删除" placement="top">
<el-button circle :icon="Delete" size="small" type="danger" @click="removeCommonItem('suggestion',opt)" />
</el-tooltip>
</div>
</div>
</div>
<template #footer>
<el-button @click="suggestionDialogVisible=false">关闭</el-button>
</template>
</el-dialog>
<el-dialog v-model="treatmentDialogVisible" title="选择处理" width="600px">
<el-input v-model="treatmentSearch" placeholder="搜索处理" clearable />
<div style="margin-top:8px;max-height:240px;overflow:auto;">
<div v-for="opt in filteredTreatments10" :key="opt" style="display:flex;align-items:center;justify-content:space-between;padding:6px 8px;border-bottom:1px solid #f0f0f0;color:#fff;">
<span>{{opt}}</span>
<div style="display:flex;gap:6px;">
<el-tooltip content="选中" placement="top">
<el-button circle :icon="CircleCheck" size="small" @click="onPickTreatment(opt)" />
</el-tooltip>
<el-tooltip content="删除" placement="top">
<el-button circle :icon="Delete" size="small" type="danger" @click="removeCommonItem('treatment',opt)" />
</el-tooltip>
</div>
</div>
</div>
<template #footer>
<el-button @click="treatmentDialogVisible=false">关闭</el-button>
</template>
</el-dialog>
</template>
<script setup>
@ -40,6 +92,8 @@ import { ref, reactive, computed, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { ElMessage, ElMessageBox } from 'element-plus'
import { getBackendUrl, patientAPI } from '../services/api.js'
import { commonItemsAPI } from '../services/api.js'
import { List, Plus, Delete, CircleCheck } from '@element-plus/icons-vue'
const emit = defineEmits([ 'closeDiagnosticMessage']);
const router = useRouter()
@ -59,10 +113,82 @@ const diagnosticForm = ref({
suggestion_info: ''
})
const commonItems = reactive({ treatment: [], suggestion: [] })
const loadCommonItems = async () => {
try {
const res = await commonItemsAPI.list()
if (res && res.success && res.data) {
commonItems.treatment = Array.isArray(res.data.treatment) ? res.data.treatment : []
commonItems.suggestion = Array.isArray(res.data.suggestion) ? res.data.suggestion : []
}
} catch {}
}
const removeCommonItem = async (type, label) => {
const res = await commonItemsAPI.remove(type, label)
if (res && res.success) {
await loadCommonItems()
ElMessage.success('已删除')
}
}
const addSuggestionFromInput = async () => {
const val = (diagnosticForm.value?.suggestion_info || diagnosticForm.suggestion_info || '').trim()
if (!val) return
const res = await commonItemsAPI.add('suggestion', val)
if (res && res.success) {
await loadCommonItems()
ElMessage.success('已新增到常用项')
}
}
const addTreatmentFromInput = async () => {
const val = (diagnosticForm.value?.treatment_info || diagnosticForm.treatment_info || '').trim()
if (!val) return
const res = await commonItemsAPI.add('treatment', val)
if (res && res.success) {
await loadCommonItems()
ElMessage.success('已新增到常用项')
}
}
const suggestionDialogVisible = ref(false)
const suggestionTemp = ref('')
const onPickSuggestion = (val) => {
if (diagnosticForm && diagnosticForm.value) {
diagnosticForm.value.suggestion_info = val
} else {
diagnosticForm.suggestion_info = val
}
suggestionDialogVisible.value = false
}
const suggestionSearch = ref('')
const filteredSuggestions10 = computed(() => {
const arr = Array.isArray(commonItems.suggestion) ? commonItems.suggestion : []
const q = (suggestionSearch.value || '').trim()
const res = q ? arr.filter(s => s.includes(q)) : arr
return res.slice(0, 10)
})
const treatmentDialogVisible = ref(false)
const treatmentSearch = ref('')
const filteredTreatments10 = computed(() => {
const arr = Array.isArray(commonItems.treatment) ? commonItems.treatment : []
const q = (treatmentSearch.value || '').trim()
const res = q ? arr.filter(s => s.includes(q)) : arr
return res.slice(0, 10)
})
const onPickTreatment = (val) => {
if (diagnosticForm && diagnosticForm.value) {
diagnosticForm.value.treatment_info = val
} else {
diagnosticForm.treatment_info = val
}
treatmentDialogVisible.value = false
}
//
onMounted(() => {
})
onMounted(loadCommonItems)
const handleCancel = async () => {
emit('closeDiagnosticMessage',false)

View File

@ -105,7 +105,7 @@ const form = reactive({
const software = reactive({
product: 'BodyBalanceEvaluation',
version: '1.0.0',
version: '1.5.0',
machine_id: ''
})
function handleTabClick(e){

View File

@ -266,13 +266,29 @@
<div style="width: 50%;height: calc(100% - 10px) ">
<div class="patientprofile-container-dataInfobg-rightbox">
<div class="patientprofile-container-dataInfobg-lefttext" style="padding-top:0px">处理</div>
<el-select v-model="profileInfo.treatment_info" placeholder="请选择">
<el-option :label="'保持观察,不予处理'" :value="'保持观察,不予处理'" />
</el-select>
<div style="display:flex;gap:6px;align-items:center;">
<el-select v-model="profileInfo.treatment_info" placeholder="请选择" filterable allow-create default-first-option clearable style="flex:1;">
<el-option v-for="opt in commonItems.treatment" :key="opt" :label="opt" :value="opt" />
</el-select>
<el-tooltip content="选择处理" placement="top">
<el-button circle :icon="List" size="small" @click="treatmentDialogVisible=true" />
</el-tooltip>
<el-tooltip content="新增到常用项" placement="top">
<el-button circle :icon="Plus" size="small" @click="addTreatmentFromInput" />
</el-tooltip>
</div>
</div>
<div class="patientprofile-container-dataInfobg-rightbottombox">
<div class="patientprofile-container-dataInfobg-lefttext" style="padding-top:0px">建议</div>
<el-input type="textarea" v-model="profileInfo.suggestion_info" :resize="'none'" class="patientprofile-container-dataInfobg-textarea"></el-input>
<div style="margin-top:8px;display:flex;gap:6px;align-items:center;">
<el-tooltip content="选择建议" placement="top">
<el-button circle :icon="List" size="small" @click="suggestionDialogVisible=true" />
</el-tooltip>
<el-tooltip content="新增到常用项" placement="top">
<el-button circle :icon="Plus" size="small" @click="addSuggestionFromInput" />
</el-tooltip>
</div>
</div>
</div>
</div>
@ -309,15 +325,55 @@
<AloneReportComparison v-if="isAloneReportComparison" :selectedData="selectedData"
:selectedPatient="selectedPatient" @closeAloneReportComparison="isAloneReportComparison = false"/>
<el-dialog v-model="treatmentDialogVisible" title="选择处理" width="600px">
<el-input v-model="treatmentSearch" placeholder="搜索处理" clearable />
<div style="margin-top:8px;max-height:240px;overflow:auto;">
<div v-for="opt in filteredTreatments10" :key="opt" style="display:flex;align-items:center;justify-content:space-between;padding:6px 8px;border-bottom:1px solid #f0f0f0;color:#fff;">
<span>{{opt}}</span>
<div style="display:flex;gap:6px;">
<el-tooltip content="选中" placement="top">
<el-button circle :icon="CircleCheck" size="small" @click="onPickTreatment(opt)" />
</el-tooltip>
<el-tooltip content="删除" placement="top">
<el-button circle :icon="Delete" size="small" type="danger" @click="removeCommonItem('treatment',opt)" />
</el-tooltip>
</div>
</div>
</div>
<template #footer>
<el-button @click="treatmentDialogVisible=false">关闭</el-button>
</template>
</el-dialog>
<el-dialog v-model="suggestionDialogVisible" title="选择建议" width="600px">
<el-input v-model="suggestionSearch" placeholder="搜索建议" clearable />
<div style="margin-top:8px;max-height:240px;overflow:auto;">
<div v-for="opt in filteredSuggestions10" :key="opt" style="display:flex;align-items:center;justify-content:space-between;padding:6px 8px;border-bottom:1px solid #f0f0f0;color:#fff;">
<span>{{opt}}</span>
<div style="display:flex;gap:6px;">
<el-tooltip content="选中" placement="top">
<el-button circle :icon="CircleCheck" size="small" @click="onPickSuggestion(opt)" />
</el-tooltip>
<el-tooltip content="删除" placement="top">
<el-button circle :icon="Delete" size="small" type="danger" @click="removeCommonItem('suggestion',opt)" />
</el-tooltip>
</div>
</div>
</div>
<template #footer>
<el-button @click="suggestionDialogVisible=false">关闭</el-button>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { ref, reactive, computed, onMounted, isShallow } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { ElMessage, ElMessageBox } from 'element-plus'
import { patientAPI, detectionAPI,historyAPI,getBackendUrl } from '@/services/api.js'
import { commonItemsAPI } from '@/services/api.js'
import { useAuthStore } from '@/stores/index.js'
import { List, Plus, Delete, CircleCheck } from '@element-plus/icons-vue'
import GenerateReport from '@/views/GenerateReport.vue'
import ImageDetailsCompare from '@/views/ImageDetailsCompare.vue'
import ImageDetails from '@/views/ImageDetails.vue'
@ -363,6 +419,76 @@ const profileInfo = ref({
treatment_info: '',
suggestion_info: ''
})
const commonItems = reactive({ treatment: [], suggestion: [] })
const loadCommonItems = async () => {
try {
const res = await commonItemsAPI.list()
if (res && res.success && res.data) {
commonItems.treatment = Array.isArray(res.data.treatment) ? res.data.treatment : []
commonItems.suggestion = Array.isArray(res.data.suggestion) ? res.data.suggestion : []
}
} catch {}
}
const removeCommonItem = async (type, label) => {
const res = await commonItemsAPI.remove(type, label)
if (res && res.success) {
await loadCommonItems()
ElMessage.success('已删除')
}
}
const addSuggestionFromInput = async () => {
const val = (profileInfo.value?.suggestion_info || profileInfo.suggestion_info || '').trim()
if (!val) return
const res = await commonItemsAPI.add('suggestion', val)
if (res && res.success) {
await loadCommonItems()
ElMessage.success('已新增到常用项')
}
}
const addTreatmentFromInput = async () => {
const val = (profileInfo.value?.treatment_info || profileInfo.treatment_info || '').trim()
if (!val) return
const res = await commonItemsAPI.add('treatment', val)
if (res && res.success) {
await loadCommonItems()
ElMessage.success('已新增到常用项')
}
}
const suggestionDialogVisible = ref(false)
const suggestionTemp = ref('')
const onPickSuggestion = (val) => {
if (profileInfo && profileInfo.value) {
profileInfo.value.suggestion_info = val
} else {
profileInfo.suggestion_info = val
}
suggestionDialogVisible.value = false
}
const suggestionSearch = ref('')
const filteredSuggestions10 = computed(() => {
const arr = Array.isArray(commonItems.suggestion) ? commonItems.suggestion : []
const q = (suggestionSearch.value || '').trim()
const res = q ? arr.filter(s => s.includes(q)) : arr
return res.slice(0, 10)
})
const treatmentDialogVisible = ref(false)
const treatmentSearch = ref('')
const filteredTreatments10 = computed(() => {
const arr = Array.isArray(commonItems.treatment) ? commonItems.treatment : []
const q = (treatmentSearch.value || '').trim()
const res = q ? arr.filter(s => s.includes(q)) : arr
return res.slice(0, 10)
})
const onPickTreatment = (val) => {
if (profileInfo && profileInfo.value) {
profileInfo.value.treatment_info = val
} else {
profileInfo.treatment_info = val
}
treatmentDialogVisible.value = false
}
const selectedRecord = ref({})
const recordData =ref([])
const isImageDetailsCompare = ref(false) //
@ -532,6 +658,7 @@ const sessionsById = async (session_id) => {
onMounted(() => {
archiveType.value = props.archiveType
sessionsInit()
loadCommonItems()
})
const deleteId = ref('')