|
@@ -1,431 +0,0 @@
|
|
|
-<template>
|
|
|
|
|
- <div class="unified-reports">
|
|
|
|
|
- <!-- Tabs -->
|
|
|
|
|
- <el-tabs v-model="activeTab" type="border-card" @tab-click="onTabChange">
|
|
|
|
|
- <!-- Tab 1: 报告列表 -->
|
|
|
|
|
- <el-tab-pane label="报告列表" name="list">
|
|
|
|
|
- <div class="filter-bar">
|
|
|
|
|
- <el-select v-model="query.type" placeholder="报告类型" clearable style="width:140px">
|
|
|
|
|
- <el-option label="全部" value=""></el-option>
|
|
|
|
|
- <el-option label="菌群检测" value="gut_flora"></el-option>
|
|
|
|
|
- <el-option label="体检报告" value="physical_exam"></el-option>
|
|
|
|
|
- <el-option label="DAN测评" value="dan"></el-option>
|
|
|
|
|
- <el-option label="保单" value="insurance"></el-option>
|
|
|
|
|
- <el-option label="家庭周报" value="fortune"></el-option>
|
|
|
|
|
- </el-select>
|
|
|
|
|
- <el-input v-model="query.keyword" placeholder="搜索标题/名称" style="width:200px" clearable @keyup.enter.native="loadReportList"></el-input>
|
|
|
|
|
- <el-date-picker v-model="dateRange" type="daterange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" style="width:240px"></el-date-picker>
|
|
|
|
|
- <el-button type="primary" @click="loadReportList">查询</el-button>
|
|
|
|
|
- <el-button @click="doSync">同步数据</el-button>
|
|
|
|
|
- <el-button type="danger" :disabled="selectedRows.length === 0" @click="handleBatchDelete">批量删除({{ selectedRows.length }})</el-button>
|
|
|
|
|
- </div>
|
|
|
|
|
-
|
|
|
|
|
- <div class="adm-table-wrap">
|
|
|
|
|
-<el-table :data="reportList" v-loading="listLoading" border stripe height="100%" style="margin-top:16px"
|
|
|
|
|
- ref="listTable"
|
|
|
|
|
- @selection-change="handleSelectionChange" @row-click="goEdit">
|
|
|
|
|
- <el-table-column type="selection" width="48" :selectable="row => row && row.id ? true : false" />
|
|
|
|
|
- <el-table-column prop="id" label="ID" width="70">
|
|
|
|
|
- <template slot="header">
|
|
|
|
|
- <span class="sort-header" @click.stop="onSortClick('id', $event)">ID{{ sortIcon('id') }}</span>
|
|
|
|
|
- </template>
|
|
|
|
|
- </el-table-column>
|
|
|
|
|
- <el-table-column label="报告类型" width="100">
|
|
|
|
|
- <template slot-scope="{ row }">
|
|
|
|
|
- <el-tag size="small" :type="getTypeTag(row.reportType || row.reportTypeName)">{{ row.reportTypeName || row.reportType }}</el-tag>
|
|
|
|
|
- </template>
|
|
|
|
|
- </el-table-column>
|
|
|
|
|
- <el-table-column prop="title" label="标题" min-width="140" show-overflow-tooltip>
|
|
|
|
|
- <template slot="header">
|
|
|
|
|
- <span class="sort-header" @click.stop="onSortClick('title', $event)">标题{{ sortIcon('title') }}</span>
|
|
|
|
|
- </template>
|
|
|
|
|
- </el-table-column>
|
|
|
|
|
- <el-table-column label="报告对象" width="120">
|
|
|
|
|
- <template slot-scope="{ row }">{{ row.subjectName || row.childName || row.personName || row.userName || '-' }}</template>
|
|
|
|
|
- <template slot="header">
|
|
|
|
|
- <span class="sort-header" @click.stop="onSortClick('subjectName', $event)">报告对象{{ sortIcon('subjectName') }}</span>
|
|
|
|
|
- </template>
|
|
|
|
|
- </el-table-column>
|
|
|
|
|
- <el-table-column label="综合评分" width="90">
|
|
|
|
|
- <template slot-scope="{ row }">
|
|
|
|
|
- <el-tag :type="scoreType(row.overallScore)" size="small">{{ row.overallScore ?? '-' }}</el-tag>
|
|
|
|
|
- </template>
|
|
|
|
|
- <template slot="header">
|
|
|
|
|
- <span class="sort-header" @click.stop="onSortClick('overallScore', $event)">综合评分{{ sortIcon('overallScore') }}</span>
|
|
|
|
|
- </template>
|
|
|
|
|
- </el-table-column>
|
|
|
|
|
- <el-table-column label="肠道" width="70" v-if="false">
|
|
|
|
|
- <template slot-scope="{ row }">{{ row.gutHealthScore || '-' }}</template>
|
|
|
|
|
- </el-table-column>
|
|
|
|
|
- <el-table-column label="营养" width="70" v-if="false">
|
|
|
|
|
- <template slot-scope="{ row }">{{ row.nutritionScore || '-' }}</template>
|
|
|
|
|
- </el-table-column>
|
|
|
|
|
- <el-table-column prop="reportDate" label="报告日期" width="120">
|
|
|
|
|
- <template slot="header">
|
|
|
|
|
- <span class="sort-header" @click.stop="onSortClick('reportDate', $event)">报告日期{{ sortIcon('reportDate') }}</span>
|
|
|
|
|
- </template>
|
|
|
|
|
- </el-table-column>
|
|
|
|
|
- <el-table-column label="状态" width="90">
|
|
|
|
|
- <template slot-scope="{ row }">
|
|
|
|
|
- <el-tag size="small">{{ row.status || '已确认' }}</el-tag>
|
|
|
|
|
- </template>
|
|
|
|
|
- <template slot="header">
|
|
|
|
|
- <span class="sort-header" @click.stop="onSortClick('status', $event)">状态{{ sortIcon('status') }}</span>
|
|
|
|
|
- </template>
|
|
|
|
|
- </el-table-column>
|
|
|
|
|
- <el-table-column label="营养缺乏" width="120">
|
|
|
|
|
- <template slot-scope="{ row }">
|
|
|
|
|
- <el-tag v-if="row.deficiencyCount > 0" type="danger" size="small">{{ row.deficiencyCount }}项缺乏</el-tag>
|
|
|
|
|
- <el-tag v-else type="success" size="small">无缺乏</el-tag>
|
|
|
|
|
- </template>
|
|
|
|
|
- </el-table-column>
|
|
|
|
|
- <el-table-column label="操作" width="120" fixed="right">
|
|
|
|
|
- <template slot-scope="{ row }">
|
|
|
|
|
- <el-button size="mini" type="text" @click.stop="goEdit(row)">编辑</el-button>
|
|
|
|
|
- <el-button size="mini" type="text" @click.stop="handleSingleDelete(row)">删除</el-button>
|
|
|
|
|
- </template>
|
|
|
|
|
- </el-table-column>
|
|
|
|
|
- </el-table>
|
|
|
|
|
- </div>
|
|
|
|
|
- <el-pagination v-if="listTotal > listSize" @current-change="onListPageChange" :current-page="listPage" :page-size="listSize" :total="listTotal" layout="total, prev, pager, next" style="margin-top:16px;text-align:right" />
|
|
|
|
|
- </el-tab-pane>
|
|
|
|
|
-
|
|
|
|
|
- <!-- Tab 2: 代传报告 -->
|
|
|
|
|
- <el-tab-pane label="代传报告" name="proxy">
|
|
|
|
|
- <el-form ref="uploadForm" :model="uploadForm" label-width="100px" class="upload-form">
|
|
|
|
|
- <el-form-item label="选择家庭" prop="familyId" required>
|
|
|
|
|
- <el-select v-model="uploadForm.familyId" placeholder="请选择家庭" filterable style="width:100%" @change="onFamilyChange">
|
|
|
|
|
- <el-option v-for="f in familyList" :key="f.id" :label="f.name" :value="f.id" />
|
|
|
|
|
- </el-select>
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- <el-form-item label="报告对象" prop="subjectId" required>
|
|
|
|
|
- <el-select v-model="uploadForm.subjectId" placeholder="请选择家庭成员" filterable style="width:100%">
|
|
|
|
|
- <el-option v-for="m in memberList" :key="m.id" :label="m.nickname || m.realName" :value="m.id" />
|
|
|
|
|
- </el-select>
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- <el-form-item label="报告文件" prop="file" required>
|
|
|
|
|
- <el-upload ref="upload" action="" :auto-upload="false" :limit="1" accept=".pdf" :on-change="handleFileChange" :on-exceed="handleExceed">
|
|
|
|
|
- <el-button size="small" type="primary">选择PDF文件</el-button>
|
|
|
|
|
- <div slot="tip" class="el-upload__tip">仅支持PDF格式,大小不超过50MB</div>
|
|
|
|
|
- </el-upload>
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- <el-form-item label="代上传备注">
|
|
|
|
|
- <el-input v-model="uploadForm.note" type="textarea" :rows="2" placeholder="选填,备注上传原因" />
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- <el-form-item>
|
|
|
|
|
- <el-button type="primary" :loading="uploading" @click="handleUpload">上传并解析</el-button>
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- </el-form>
|
|
|
|
|
-
|
|
|
|
|
- <!-- 解析结果预览 -->
|
|
|
|
|
- <el-card v-if="previewResult" style="margin-top:20px">
|
|
|
|
|
- <div slot="header" class="card-header">
|
|
|
|
|
- <span class="card-title">📊 解析结果预览</span>
|
|
|
|
|
- <el-tag :type="previewResult.needBind ? 'warning' : 'success'" size="small">
|
|
|
|
|
- {{ previewResult.needBind ? '待绑定成员' : '已匹配成员' }}
|
|
|
|
|
- </el-tag>
|
|
|
|
|
- </div>
|
|
|
|
|
- <el-descriptions :column="2" border>
|
|
|
|
|
- <el-descriptions-item label="被检测人">{{ previewResult.extractedName || '-' }}</el-descriptions-item>
|
|
|
|
|
- <el-descriptions-item label="报告编号">{{ previewResult.extractedReportNumber || '-' }}</el-descriptions-item>
|
|
|
|
|
- <el-descriptions-item label="报告日期">{{ previewResult.extractedReportDate || '-' }}</el-descriptions-item>
|
|
|
|
|
- <el-descriptions-item label="健康总分">{{ previewResult.payload && previewResult.payload.overallScore ? previewResult.payload.overallScore : '-' }}</el-descriptions-item>
|
|
|
|
|
- <el-descriptions-item label="菌群健康分">{{ previewResult.payload && previewResult.payload.gutHealthScore ? previewResult.payload.gutHealthScore : '-' }}</el-descriptions-item>
|
|
|
|
|
- <el-descriptions-item label="肠型">{{ previewResult.payload && previewResult.payload.gutType ? previewResult.payload.gutType : '-' }}</el-descriptions-item>
|
|
|
|
|
- </el-descriptions>
|
|
|
|
|
- <div style="margin-top:20px;text-align:right">
|
|
|
|
|
- <el-button @click="previewResult=null">取消</el-button>
|
|
|
|
|
- <el-button type="primary" :loading="confirming" @click="handleConfirm">确认入库</el-button>
|
|
|
|
|
- </div>
|
|
|
|
|
- </el-card>
|
|
|
|
|
-
|
|
|
|
|
- <!-- AI解读结果 -->
|
|
|
|
|
- <el-card v-if="analysisResult" style="margin-top:20px">
|
|
|
|
|
- <div slot="header" class="card-header">
|
|
|
|
|
- <span class="card-title">🤖 AI解读结果</span>
|
|
|
|
|
- <el-tag type="success" size="small">已完成</el-tag>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div v-html="analysisResult" class="analysis-content"></div>
|
|
|
|
|
- </el-card>
|
|
|
|
|
-
|
|
|
|
|
- <!-- 代传历史 -->
|
|
|
|
|
- <el-card style="margin-top:20px">
|
|
|
|
|
- <div slot="header" class="card-header">
|
|
|
|
|
- <span class="card-title">📁 代上传历史记录</span>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="adm-table-wrap">
|
|
|
|
|
-<el-table :data="proxyReportList" border stripe v-loading="proxyListLoading" height="100%">
|
|
|
|
|
- <el-table-column prop="id" label="ID" width="60" />
|
|
|
|
|
- <el-table-column label="报告对象" width="120">
|
|
|
|
|
- <template slot-scope="{ row }">{{ row.subjectName || row.userName || '-' }}</template>
|
|
|
|
|
- </el-table-column>
|
|
|
|
|
- <el-table-column prop="reportType" label="类型" width="100">
|
|
|
|
|
- <template slot-scope="{ row }">{{ row.reportType === 'gut_flora' ? '菌群检测' : row.reportType === 'physical_exam' ? '体检报告' : row.reportType }}</template>
|
|
|
|
|
- </el-table-column>
|
|
|
|
|
- <el-table-column prop="overallScore" label="健康分" width="80">
|
|
|
|
|
- <template slot-scope="{ row }">{{ row.overallScore || '-' }}</template>
|
|
|
|
|
- </el-table-column>
|
|
|
|
|
- <el-table-column prop="remark" label="备注" show-overflow-tooltip />
|
|
|
|
|
- <el-table-column prop="createdAt" label="上传时间" width="160">
|
|
|
|
|
- <template slot-scope="{ row }">{{ formatTime(row.createdAt) }}</template>
|
|
|
|
|
- </el-table-column>
|
|
|
|
|
- <el-table-column label="操作" width="100" fixed="right">
|
|
|
|
|
- <template slot-scope="{ row }">
|
|
|
|
|
- <el-button size="mini" type="text" @click="viewProxyReport(row)">查看</el-button>
|
|
|
|
|
- </template>
|
|
|
|
|
- </el-table-column>
|
|
|
|
|
- <el-pagination @size-change="onProxySizeChange" @current-change="onProxyPageChange" :current-page="proxyPage" :page-size="proxySize" :total="proxyTotal" layout="total, prev, pager, next" class="pagination-wrap" />
|
|
|
|
|
- </el-card>
|
|
|
|
|
- </el-tab-pane>
|
|
|
|
|
- </el-tabs>
|
|
|
|
|
-
|
|
|
|
|
- <!-- 报告详情弹窗 -->
|
|
|
|
|
- <el-dialog title="报告详情" :visible.sync="detailDialogVisible" width="700px" top="5vh">
|
|
|
|
|
- <el-descriptions v-if="detailReport" :column="2" border>
|
|
|
|
|
- <el-descriptions-item label="报告ID">{{ detailReport.id }}</el-descriptions-item>
|
|
|
|
|
- <el-descriptions-item label="报告类型">{{ detailReport.reportType || detailReport.reportTypeName }}</el-descriptions-item>
|
|
|
|
|
- <el-descriptions-item label="报告编号">{{ detailReport.reportNumber || '-' }}</el-descriptions-item>
|
|
|
|
|
- <el-descriptions-item label="报告日期">{{ detailReport.reportDate || '-' }}</el-descriptions-item>
|
|
|
|
|
- <el-descriptions-item label="健康总分">{{ detailReport.overallScore || '-' }}</el-descriptions-item>
|
|
|
|
|
- <el-descriptions-item label="菌群健康分">{{ detailReport.gutHealthScore || '-' }}</el-descriptions-item>
|
|
|
|
|
- <el-descriptions-item label="肠型">{{ detailReport.gutType || '-' }}</el-descriptions-item>
|
|
|
|
|
- <el-descriptions-item label="肠道年龄">{{ detailReport.gutAge || '-' }}</el-descriptions-item>
|
|
|
|
|
- <el-descriptions-item label="AI解读" :span="2">
|
|
|
|
|
- <div v-html="detailReport.aiAnalysis" class="analysis-content" style="max-height:300px;overflow:auto"></div>
|
|
|
|
|
- </el-descriptions-item>
|
|
|
|
|
- </el-descriptions>
|
|
|
|
|
- <span slot="footer"><el-button @click="detailDialogVisible = false">关闭</el-button></span>
|
|
|
|
|
- </el-dialog>
|
|
|
|
|
- </el-table>
|
|
|
|
|
- </div>
|
|
|
|
|
-
|
|
|
|
|
-</div></template>
|
|
|
|
|
-
|
|
|
|
|
-<script>
|
|
|
|
|
-import axios from 'axios'
|
|
|
|
|
-import { getFamilyList, getFamilyMemberList } from '@/api/admin'
|
|
|
|
|
-import { proxyParseReport, proxyConfirmReport, listHealthReports, getHealthReportDetail, archiveReportsBatch, archiveReport } from '@/api/admin'
|
|
|
|
|
-import SortMixin from '@/mixins/SortMixin'
|
|
|
|
|
-
|
|
|
|
|
-export default {
|
|
|
|
|
- name: 'UnifiedHealthReports',
|
|
|
|
|
- mixins: [SortMixin],
|
|
|
|
|
- data() {
|
|
|
|
|
- return {
|
|
|
|
|
- activeTab: 'list',
|
|
|
|
|
- sortableColumns: { id: 'ID', title: '标题', subjectName: '报告对象', overallScore: '综合评分', reportDate: '报告日期', status: '状态' },
|
|
|
|
|
-
|
|
|
|
|
- // 报告列表
|
|
|
|
|
- reportList: [],
|
|
|
|
|
- listLoading: false,
|
|
|
|
|
- listPage: 1,
|
|
|
|
|
- listSize: 20,
|
|
|
|
|
- listTotal: 0,
|
|
|
|
|
- query: { type: '', keyword: '' },
|
|
|
|
|
- dateRange: [],
|
|
|
|
|
- selectedRows: [],
|
|
|
|
|
-
|
|
|
|
|
- // 代传报告
|
|
|
|
|
- uploading: false,
|
|
|
|
|
- confirming: false,
|
|
|
|
|
- proxyListLoading: false,
|
|
|
|
|
- familyList: [],
|
|
|
|
|
- memberList: [],
|
|
|
|
|
- proxyReportList: [],
|
|
|
|
|
- previewResult: null,
|
|
|
|
|
- analysisResult: null,
|
|
|
|
|
- proxyPage: 1,
|
|
|
|
|
- proxySize: 20,
|
|
|
|
|
- proxyTotal: 0,
|
|
|
|
|
- uploadForm: { familyId: null, subjectId: null, file: null, note: '' },
|
|
|
|
|
-
|
|
|
|
|
- // 详情
|
|
|
|
|
- detailDialogVisible: false,
|
|
|
|
|
- detailReport: null
|
|
|
|
|
- }
|
|
|
|
|
- },
|
|
|
|
|
- computed: {
|
|
|
|
|
- tableHeight() {
|
|
|
|
|
- return window.innerHeight - 280
|
|
|
|
|
- }
|
|
|
|
|
- },
|
|
|
|
|
- mounted() {
|
|
|
|
|
- var tab = this.$route.query.tab
|
|
|
|
|
- if (tab && ['list', 'proxy'].indexOf(tab) >= 0) {
|
|
|
|
|
- this.activeTab = tab
|
|
|
|
|
- }
|
|
|
|
|
- this.loadReportList()
|
|
|
|
|
- this.loadFamilies()
|
|
|
|
|
- this.loadProxyReports()
|
|
|
|
|
- },
|
|
|
|
|
- methods: {
|
|
|
|
|
- // ===== 报告列表 =====
|
|
|
|
|
- loadList() {
|
|
|
|
|
- this.loadReportList()
|
|
|
|
|
- },
|
|
|
|
|
- async loadReportList() {
|
|
|
|
|
- this.listLoading = true
|
|
|
|
|
- try {
|
|
|
|
|
- var params = { page: this.listPage, size: this.listSize, type: this.query.type, keyword: this.query.keyword }
|
|
|
|
|
- if (this.sortState.length > 0) params.sort = this.sortState
|
|
|
|
|
- if (this.dateRange && this.dateRange.length === 2) {
|
|
|
|
|
- params.startDate = this.dateRange[0].format ? this.dateRange[0].format('YYYY-MM-DD') : this.dateRange[0]
|
|
|
|
|
- params.endDate = this.dateRange[1].format ? this.dateRange[1].format('YYYY-MM-DD') : this.dateRange[1]
|
|
|
|
|
- }
|
|
|
|
|
- var baseURL = process.env.VUE_APP_BASE_API || ''
|
|
|
|
|
- var token = localStorage.getItem('token')
|
|
|
|
|
- var res = await axios.post(baseURL + '/api/health/report/admin-list', params, {
|
|
|
|
|
- headers: { Authorization: 'Bearer ' + token }
|
|
|
|
|
- })
|
|
|
|
|
- var data = res.data && res.data.data ? res.data.data : (res.data || [])
|
|
|
|
|
- this.reportList = (data.records || []).map(function(r) { r.deficiencyCount = r.deficiencyCount || 0; return r })
|
|
|
|
|
- this.listTotal = data.total || 0
|
|
|
|
|
- } catch (e) {
|
|
|
|
|
- this.$message.error('加载报告列表失败')
|
|
|
|
|
- } finally {
|
|
|
|
|
- this.listLoading = false
|
|
|
|
|
- }
|
|
|
|
|
- },
|
|
|
|
|
- onListPageChange(val) { this.listPage = val; this.loadReportList() },
|
|
|
|
|
- doSync() {
|
|
|
|
|
- var baseURL = process.env.VUE_APP_BASE_API || ''
|
|
|
|
|
- var token = localStorage.getItem('token')
|
|
|
|
|
- axios.post(baseURL + '/api/admin/report-summary/sync', {}, { headers: { Authorization: 'Bearer ' + token } })
|
|
|
|
|
- .then(function(res) { if (res.data && res.data.code === 200) { this.$message.success('同步完成'); this.loadReportList() } }.bind(this))
|
|
|
|
|
- .catch(function() { this.$message.error('同步失败') }.bind(this))
|
|
|
|
|
- },
|
|
|
|
|
- viewReportDetail(row) {
|
|
|
|
|
- var self = this
|
|
|
|
|
- var baseURL = process.env.VUE_APP_BASE_API || ''
|
|
|
|
|
- var token = localStorage.getItem('token')
|
|
|
|
|
- axios.post(baseURL + '/api/health/report/detail', { reportId: row.id }, { headers: { Authorization: 'Bearer ' + token } })
|
|
|
|
|
- .then(function(res) { if (res.data && res.data.data) { self.detailReport = res.data.data; self.detailDialogVisible = true } })
|
|
|
|
|
- .catch(function() { self.$message.error('加载详情失败') })
|
|
|
|
|
- },
|
|
|
|
|
- triggerAnalysis(row) {
|
|
|
|
|
- var baseURL = process.env.VUE_APP_BASE_API || ''
|
|
|
|
|
- var token = localStorage.getItem('token')
|
|
|
|
|
- axios.post(baseURL + '/api/health/report/analysis', { reportId: row.id }, { headers: { Authorization: 'Bearer ' + token } })
|
|
|
|
|
- .then(function(res) { this.$message.success('分析完成') }.bind(this))
|
|
|
|
|
- .catch(function() { this.$message.error('分析失败') }.bind(this))
|
|
|
|
|
- },
|
|
|
|
|
-
|
|
|
|
|
- handleSelectionChange(rows) {
|
|
|
|
|
- this.selectedRows = rows
|
|
|
|
|
- },
|
|
|
|
|
-
|
|
|
|
|
- goEdit(row, column) {
|
|
|
|
|
- if (!row || !row.id) return
|
|
|
|
|
- // 点击复选框列不触发跳转
|
|
|
|
|
- if (column && (column.type === 'selection' || column.type === 'index')) return
|
|
|
|
|
- this.$router.push({ name: 'HealthReportEdit', params: { id: row.id } })
|
|
|
|
|
- },
|
|
|
|
|
-
|
|
|
|
|
- handleSingleDelete(row) {
|
|
|
|
|
- var self = this
|
|
|
|
|
- this.$confirm('确定要删除这份报告吗?删除后将在列表中消失(软删除)', '提示', {
|
|
|
|
|
- type: 'warning',
|
|
|
|
|
- confirmButtonText: '删除',
|
|
|
|
|
- cancelButtonText: '取消'
|
|
|
|
|
- }).then(function() {
|
|
|
|
|
- archiveReport(row.id).then(function() {
|
|
|
|
|
- self.$message.success('已删除')
|
|
|
|
|
- self.loadReportList()
|
|
|
|
|
- }).catch(function() {})
|
|
|
|
|
- }).catch(function() {})
|
|
|
|
|
- },
|
|
|
|
|
-
|
|
|
|
|
- handleBatchDelete() {
|
|
|
|
|
- var self = this
|
|
|
|
|
- var ids = this.selectedRows.map(function(r) { return r.id })
|
|
|
|
|
- if (ids.length === 0) return
|
|
|
|
|
- this.$confirm('确定要删除选中的 ' + ids.length + ' 份报告吗?删除后将在列表中消失(软删除)', '提示', {
|
|
|
|
|
- type: 'warning',
|
|
|
|
|
- confirmButtonText: '删除',
|
|
|
|
|
- cancelButtonText: '取消'
|
|
|
|
|
- }).then(function() {
|
|
|
|
|
- archiveReportsBatch(ids).then(function(res) {
|
|
|
|
|
- self.$message.success(res.message || ('已删除 ' + ids.length + ' 份报告'))
|
|
|
|
|
- self.selectedRows = []
|
|
|
|
|
- self.$refs.listTable && self.$refs.listTable.clearSelection()
|
|
|
|
|
- self.loadReportList()
|
|
|
|
|
- }).catch(function() {})
|
|
|
|
|
- }).catch(function() {})
|
|
|
|
|
- },
|
|
|
|
|
-
|
|
|
|
|
- // ===== 代传报告 =====
|
|
|
|
|
- async loadFamilies() {
|
|
|
|
|
- try { var res = await getFamilyList({ page: 1, size: 200 }); this.familyList = res.data && res.data.records ? res.data.records : (res.data || []) } catch (e) { console.error(e) }
|
|
|
|
|
- },
|
|
|
|
|
- async onFamilyChange(familyId) {
|
|
|
|
|
- if (!familyId) { this.memberList = []; return }
|
|
|
|
|
- try { var res = await getFamilyMemberList({ familyId }); this.memberList = res.data || []; this.uploadForm.subjectId = null } catch (e) { this.memberList = [] }
|
|
|
|
|
- },
|
|
|
|
|
- handleFileChange(file) { this.uploadForm.file = file.raw },
|
|
|
|
|
- handleExceed() { this.$message.warning('只能上传一个文件') },
|
|
|
|
|
- async handleUpload() {
|
|
|
|
|
- if (!this.uploadForm.familyId) { this.$message.warning('请选择家庭'); return }
|
|
|
|
|
- if (!this.uploadForm.subjectId) { this.$message.warning('请选择报告对象'); return }
|
|
|
|
|
- if (!this.uploadForm.file) { this.$message.warning('请选择PDF文件'); return }
|
|
|
|
|
- this.uploading = true
|
|
|
|
|
- try {
|
|
|
|
|
- var res = await proxyParseReport({ familyId: this.uploadForm.familyId, subjectId: this.uploadForm.subjectId, file: this.uploadForm.file, note: this.uploadForm.note })
|
|
|
|
|
- if (res.code === 200) { this.previewResult = res.data; this.analysisResult = null }
|
|
|
|
|
- else { this.$message.error(res.message || '解析失败') }
|
|
|
|
|
- } catch (e) { this.$message.error('上传失败: ' + (e.message || '')) } finally { this.uploading = false }
|
|
|
|
|
- },
|
|
|
|
|
- async handleConfirm() {
|
|
|
|
|
- if (!this.previewResult || !this.previewResult.draftId) { this.$message.warning('没有可确认的草稿'); return }
|
|
|
|
|
- this.confirming = true
|
|
|
|
|
- try {
|
|
|
|
|
- var res = await proxyConfirmReport({ draftId: this.previewResult.draftId, subjectId: this.uploadForm.subjectId })
|
|
|
|
|
- if (res.code === 200) { this.$message.success('报告入库成功'); this.previewResult = null; this.analysisResult = res.data && res.data.aiAnalysis ? res.data.aiAnalysis : null; this.loadProxyReports() }
|
|
|
|
|
- else { this.$message.error(res.message || '确认失败') }
|
|
|
|
|
- } catch (e) { this.$message.error('确认失败: ' + (e.message || '')) } finally { this.confirming = false }
|
|
|
|
|
- },
|
|
|
|
|
- async loadProxyReports() {
|
|
|
|
|
- this.proxyListLoading = true
|
|
|
|
|
- try {
|
|
|
|
|
- var res = await listHealthReports({ page: this.proxyPage, size: this.proxySize })
|
|
|
|
|
- if (res.code === 200) { this.proxyReportList = res.data && res.data.records ? res.data.records : (res.data || []); this.proxyTotal = res.data && res.data.total ? res.data.total : this.proxyReportList.length }
|
|
|
|
|
- } catch (e) { console.error(e) } finally { this.proxyListLoading = false }
|
|
|
|
|
- },
|
|
|
|
|
- viewProxyReport(row) {
|
|
|
|
|
- var self = this
|
|
|
|
|
- getHealthReportDetail(row.id).then(function(res) { if (res.code === 200) { self.detailReport = res.data; self.detailDialogVisible = true } }).catch(function() { self.$message.error('加载详情失败') })
|
|
|
|
|
- },
|
|
|
|
|
- onProxySizeChange(size) { this.proxySize = size; this.loadProxyReports() },
|
|
|
|
|
- onProxyPageChange(page) { this.proxyPage = page; this.loadProxyReports() },
|
|
|
|
|
-
|
|
|
|
|
- // ===== 通用 =====
|
|
|
|
|
- onTabChange() { },
|
|
|
|
|
- formatTime(t) { if (!t) return '-'; return t.replace ? t.replace('T', ' ').substring(0, 19) : t },
|
|
|
|
|
- getTypeTag(type) { var map = { gut_flora: 'success', physical_exam: 'primary', dan: 'warning', insurance: 'info', fortune: 'danger' }; return map[type] || 'info' },
|
|
|
|
|
- scoreType(score) { if (score == null) return 'info'; if (score >= 80) return 'success'; if (score >= 60) return 'warning'; return 'danger' },
|
|
|
|
|
- scoreColor(score) { if (score == null) return '#909399'; if (score >= 80) return '#67c23a'; if (score >= 60) return '#e6a23c'; return '#f56c6c' }
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-</script>
|
|
|
|
|
-
|
|
|
|
|
-<style scoped>
|
|
|
|
|
-.unified-reports { padding: 16px; }
|
|
|
|
|
-.filter-bar { display: flex; flex-direction: row; align-items: center; gap: 12px; flex-wrap: wrap; }
|
|
|
|
|
-.upload-form { max-width: 600px; }
|
|
|
|
|
-.card-header { display: flex; justify-content: space-between; align-items: center; }
|
|
|
|
|
-.card-title { font-size: 16px; font-weight: bold; }
|
|
|
|
|
-.analysis-content { font-size: 14px; line-height: 1.8; color: #333; }
|
|
|
|
|
-.analysis-content :deep(p) { margin: 8px 0; }
|
|
|
|
|
-.pagination-wrap { margin-top: 16px; text-align: right; }
|
|
|
|
|
-.score-card { border: 2px solid #ebeef5; border-radius: 8px; padding: 12px; text-align: center; background: #fafafa; }
|
|
|
|
|
-.score-label { font-size: 13px; color: #909399; margin-bottom: 6px; }
|
|
|
|
|
-.score-value { font-size: 24px; font-weight: bold; }
|
|
|
|
|
-.sort-header {
|
|
|
|
|
- cursor: pointer;
|
|
|
|
|
- user-select: none;
|
|
|
|
|
- font-weight: 500;
|
|
|
|
|
- font-size: 12px;
|
|
|
|
|
-}
|
|
|
|
|
-.sort-header:hover {
|
|
|
|
|
- color: #409eff;
|
|
|
|
|
-}
|
|
|
|
|
-</style>
|
|
|