|
@@ -13,52 +13,6 @@
|
|
|
<text class="tip-text">上传体检报告或肠道检测报告,以便为您解读并提供个性化营养方案</text>
|
|
<text class="tip-text">上传体检报告或肠道检测报告,以便为您解读并提供个性化营养方案</text>
|
|
|
</view>
|
|
</view>
|
|
|
|
|
|
|
|
- <!-- PDF解析预览(提取到信息时显示) -->
|
|
|
|
|
- <view class="parsed-preview" v-if="parsedInfo">
|
|
|
|
|
- <view class="parsed-title">PDF解析结果</view>
|
|
|
|
|
- <view class="parsed-row" v-if="parsedInfo.personName">
|
|
|
|
|
- <text class="parsed-label">被检测人</text>
|
|
|
|
|
- <text class="parsed-value">{{ parsedInfo.personName }}</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- <view class="parsed-row" v-if="parsedInfo.reportNumber">
|
|
|
|
|
- <text class="parsed-label">报告编号</text>
|
|
|
|
|
- <text class="parsed-value">{{ parsedInfo.reportNumber }}</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- <view class="parsed-row" v-if="parsedInfo.reportDate">
|
|
|
|
|
- <text class="parsed-label">报告日期</text>
|
|
|
|
|
- <text class="parsed-value">{{ parsedInfo.reportDate }}</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- <!-- 匹配状态 -->
|
|
|
|
|
- <view class="match-status" v-if="parsedInfo.needManualSelect">
|
|
|
|
|
- <text class="match-icon">⚠️</text>
|
|
|
|
|
- <text class="match-text">无法自动匹配到家庭成员,请手动选择</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- <view class="match-status success" v-else-if="parsedInfo.matchedMemberName">
|
|
|
|
|
- <text class="match-icon">✅</text>
|
|
|
|
|
- <text class="match-text">已自动匹配: {{ parsedInfo.matchedMemberName }}</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- </view>
|
|
|
|
|
-
|
|
|
|
|
- <!-- 家庭成员选择器(需要手动选择时显示) -->
|
|
|
|
|
- <view class="form-item member-selector" v-if="showMemberSelector">
|
|
|
|
|
- <text class="form-label">选择成员</text>
|
|
|
|
|
- <view class="member-list">
|
|
|
|
|
- <view
|
|
|
|
|
- class="member-item"
|
|
|
|
|
- v-for="member in familyMembers"
|
|
|
|
|
- :key="member.id"
|
|
|
|
|
- :class="{ 'member-selected': selectedMemberId === (member.userId || member.id) }"
|
|
|
|
|
- @click="selectMember(member)"
|
|
|
|
|
- >
|
|
|
|
|
- <text class="member-avatar">{{ member.nickname ? member.nickname.substring(0, 1) : '?' }}</text>
|
|
|
|
|
- <text class="member-name">{{ member.nickname || '未知' }}</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- </view>
|
|
|
|
|
- <view class="add-member-btn" v-if="parsedInfo && parsedInfo.personName" @click="chooseRelationAndAdd">
|
|
|
|
|
- <text class="add-member-text">+ 将「{{ parsedInfo.personName }}」添加为新成员</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- </view>
|
|
|
|
|
-
|
|
|
|
|
<!-- 表单 -->
|
|
<!-- 表单 -->
|
|
|
<view class="form-section">
|
|
<view class="form-section">
|
|
|
<!-- 上传方式:并排卡片 -->
|
|
<!-- 上传方式:并排卡片 -->
|
|
@@ -75,41 +29,13 @@
|
|
|
</view>
|
|
</view>
|
|
|
</view>
|
|
</view>
|
|
|
|
|
|
|
|
- <!-- 已选文件 -->
|
|
|
|
|
- <view class="form-item" v-if="selectedFile || photoPath">
|
|
|
|
|
- <text class="form-label">已选文件</text>
|
|
|
|
|
- <view class="file-info">
|
|
|
|
|
- <text class="file-name">{{ selectedFile ? (selectedFile.name || '文件') : '照片' }}</text>
|
|
|
|
|
- <text class="file-size" v-if="selectedFile && selectedFile.size">{{ formatFileSize(selectedFile.size) }}</text>
|
|
|
|
|
- <text class="file-remove" @click.stop="removeFile">移除</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- </view>
|
|
|
|
|
-
|
|
|
|
|
- <!-- 上传中/分析中 -->
|
|
|
|
|
- <view class="form-item" v-if="parsingPreview">
|
|
|
|
|
- <text class="form-label">AI分析中...</text>
|
|
|
|
|
|
|
+ <!-- 上传中 -->
|
|
|
|
|
+ <view class="form-item" v-if="uploading">
|
|
|
|
|
+ <text class="form-label">上传中...</text>
|
|
|
<view class="analyzing-status">
|
|
<view class="analyzing-status">
|
|
|
- <text class="analyzing-text">正在解析文件内容,请稍候...</text>
|
|
|
|
|
|
|
+ <text class="analyzing-text">正在上传文件,即将进入分析页面...</text>
|
|
|
</view>
|
|
</view>
|
|
|
</view>
|
|
</view>
|
|
|
-
|
|
|
|
|
- <!-- 备注 -->
|
|
|
|
|
- <view class="form-item">
|
|
|
|
|
- <text class="form-label">备注(选填)</text>
|
|
|
|
|
- <textarea
|
|
|
|
|
- class="note-input"
|
|
|
|
|
- v-model="form.description"
|
|
|
|
|
- placeholder="请输入报告摘要或备注信息..."
|
|
|
|
|
- maxlength="500"
|
|
|
|
|
- />
|
|
|
|
|
- </view>
|
|
|
|
|
-
|
|
|
|
|
- <!-- 提交按钮 -->
|
|
|
|
|
- <view class="submit-btn" :class="{ 'btn-disabled': !canSubmit }" @click="doSubmit">
|
|
|
|
|
- <text v-if="parsingPreview">解析中...</text>
|
|
|
|
|
- <text v-else-if="submitting">提交中...</text>
|
|
|
|
|
- <text v-else>提交报告</text>
|
|
|
|
|
- </view>
|
|
|
|
|
</view>
|
|
</view>
|
|
|
|
|
|
|
|
<!-- 底部占位 -->
|
|
<!-- 底部占位 -->
|
|
@@ -141,7 +67,7 @@
|
|
|
</template>
|
|
</template>
|
|
|
|
|
|
|
|
<script>
|
|
<script>
|
|
|
-import { getFamilyMembers, addFamilyMember, getProductsByDomain } from '../../utils/api.js'
|
|
|
|
|
|
|
+import { getProductsByDomain, uploadReportOnly } from '../../utils/api.js'
|
|
|
import config from '@/config.js'
|
|
import config from '@/config.js'
|
|
|
|
|
|
|
|
export default {
|
|
export default {
|
|
@@ -149,61 +75,30 @@ export default {
|
|
|
return {
|
|
return {
|
|
|
memberId: null,
|
|
memberId: null,
|
|
|
familyId: null,
|
|
familyId: null,
|
|
|
- form: {
|
|
|
|
|
- reportDate: '',
|
|
|
|
|
- overallScore: 0,
|
|
|
|
|
- description: ''
|
|
|
|
|
- },
|
|
|
|
|
- submitting: false,
|
|
|
|
|
|
|
+ uploading: false,
|
|
|
selectedFile: null,
|
|
selectedFile: null,
|
|
|
- parsingPreview: false,
|
|
|
|
|
- parsedInfo: null,
|
|
|
|
|
- showMemberSelector: false,
|
|
|
|
|
- selectedMemberId: null,
|
|
|
|
|
- familyMembers: [],
|
|
|
|
|
- // 拍照
|
|
|
|
|
photoPath: null,
|
|
photoPath: null,
|
|
|
// 购买推荐
|
|
// 购买推荐
|
|
|
recommendedProducts: [],
|
|
recommendedProducts: [],
|
|
|
loadingProducts: false
|
|
loadingProducts: false
|
|
|
}
|
|
}
|
|
|
},
|
|
},
|
|
|
- computed: {
|
|
|
|
|
- canSubmit() {
|
|
|
|
|
- if (this.parsingPreview) return false
|
|
|
|
|
- if (this.selectedFile || this.photoPath) {
|
|
|
|
|
- // 文件已选且解析成功(有draftId且不需要手动选成员)
|
|
|
|
|
- return this.parsedInfo && this.parsedInfo.draftId && !this.parsedInfo.needManualSelect
|
|
|
|
|
- }
|
|
|
|
|
- return false
|
|
|
|
|
- }
|
|
|
|
|
- },
|
|
|
|
|
onLoad: function(options) {
|
|
onLoad: function(options) {
|
|
|
this.memberId = options.memberId ? parseInt(options.memberId) : null
|
|
this.memberId = options.memberId ? parseInt(options.memberId) : null
|
|
|
this.loadRecommendProducts()
|
|
this.loadRecommendProducts()
|
|
|
},
|
|
},
|
|
|
onShow: function() {
|
|
onShow: function() {
|
|
|
- // 每次显示时从store获取familyId和家庭成员
|
|
|
|
|
|
|
+ // 每次显示时从store获取familyId
|
|
|
this.loadFamilyInfo()
|
|
this.loadFamilyInfo()
|
|
|
},
|
|
},
|
|
|
methods: {
|
|
methods: {
|
|
|
loadFamilyInfo() {
|
|
loadFamilyInfo() {
|
|
|
- var self = this
|
|
|
|
|
- // 从Vuex获取familyId和家庭成员
|
|
|
|
|
|
|
+ // 从本地存储获取familyId
|
|
|
var userInfo = uni.getStorageSync('userInfo')
|
|
var userInfo = uni.getStorageSync('userInfo')
|
|
|
if (userInfo) {
|
|
if (userInfo) {
|
|
|
try {
|
|
try {
|
|
|
var info = typeof userInfo === 'string' ? JSON.parse(userInfo) : userInfo
|
|
var info = typeof userInfo === 'string' ? JSON.parse(userInfo) : userInfo
|
|
|
this.familyId = info.familyId || null
|
|
this.familyId = info.familyId || null
|
|
|
- // 加载家庭成员列表(从本地存储或请求)
|
|
|
|
|
- var storedMembers = uni.getStorageSync('familyMembers')
|
|
|
|
|
- if (storedMembers) {
|
|
|
|
|
- try {
|
|
|
|
|
- this.familyMembers = typeof storedMembers === 'string' ? JSON.parse(storedMembers) : storedMembers
|
|
|
|
|
- } catch (e) {
|
|
|
|
|
- this.familyMembers = []
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
} catch (e) {
|
|
} catch (e) {
|
|
|
console.log('解析userInfo失败', e)
|
|
console.log('解析userInfo失败', e)
|
|
|
}
|
|
}
|
|
@@ -217,25 +112,14 @@ export default {
|
|
|
sourceType: ['camera', 'album'],
|
|
sourceType: ['camera', 'album'],
|
|
|
success: function(res) {
|
|
success: function(res) {
|
|
|
self.photoPath = res.tempFilePaths[0]
|
|
self.photoPath = res.tempFilePaths[0]
|
|
|
- // 拍照后直接解析
|
|
|
|
|
- if (self.familyId) {
|
|
|
|
|
- self.previewParse()
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ // 拍照后直接上传并进入确认页(familyId 为可选)
|
|
|
|
|
+ self.uploadAndGoConfirm(self.photoPath, 'image')
|
|
|
}
|
|
}
|
|
|
})
|
|
})
|
|
|
},
|
|
},
|
|
|
// ===== 文件上传 =====
|
|
// ===== 文件上传 =====
|
|
|
pickFile() {
|
|
pickFile() {
|
|
|
var self = this
|
|
var self = this
|
|
|
- // 重置解析状态
|
|
|
|
|
- this.parsedInfo = null
|
|
|
|
|
- this.showMemberSelector = false
|
|
|
|
|
- this.selectedMemberId = null
|
|
|
|
|
- this.form.reportDate = ''
|
|
|
|
|
- this.form.overallScore = 0
|
|
|
|
|
- this.photoPath = null
|
|
|
|
|
-
|
|
|
|
|
- // 从微信聊天记录中选择文件
|
|
|
|
|
uni.chooseMessageFile({
|
|
uni.chooseMessageFile({
|
|
|
count: 1,
|
|
count: 1,
|
|
|
type: 'file',
|
|
type: 'file',
|
|
@@ -244,9 +128,8 @@ export default {
|
|
|
var files = res.tempFiles
|
|
var files = res.tempFiles
|
|
|
if (files && files.length > 0) {
|
|
if (files && files.length > 0) {
|
|
|
self.selectedFile = files[0]
|
|
self.selectedFile = files[0]
|
|
|
- if (self.familyId) {
|
|
|
|
|
- self.previewParse()
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ // 直接上传并进入确认页(familyId 为可选,后端会自行处理成员匹配)
|
|
|
|
|
+ self.uploadAndGoConfirm(files[0].path, '')
|
|
|
}
|
|
}
|
|
|
},
|
|
},
|
|
|
fail: function(e) {
|
|
fail: function(e) {
|
|
@@ -254,210 +137,29 @@ export default {
|
|
|
}
|
|
}
|
|
|
})
|
|
})
|
|
|
},
|
|
},
|
|
|
- previewParse() {
|
|
|
|
|
|
|
+ // 上传文件 → 自动跳转确认页(确认页按 draftId 触发解析并自动刷新显示结果)
|
|
|
|
|
+ uploadAndGoConfirm(filePath, type) {
|
|
|
var self = this
|
|
var self = this
|
|
|
- if (!this.familyId) return
|
|
|
|
|
- var filePath = this.selectedFile ? this.selectedFile.path : this.photoPath
|
|
|
|
|
if (!filePath) return
|
|
if (!filePath) return
|
|
|
- this.parsingPreview = true
|
|
|
|
|
-
|
|
|
|
|
- var apiPath = config.API_BASE_URL + '/api/health/report/parse-preview'
|
|
|
|
|
- if (self.selectedFile) {
|
|
|
|
|
- // PDF文件上传
|
|
|
|
|
- uni.uploadFile({
|
|
|
|
|
- url: apiPath + (self.memberId ? '?memberId=' + self.memberId : ''),
|
|
|
|
|
- filePath: filePath,
|
|
|
|
|
- name: 'file',
|
|
|
|
|
- header: {
|
|
|
|
|
- 'Authorization': 'Bearer ' + (uni.getStorageSync('token') || '')
|
|
|
|
|
- },
|
|
|
|
|
- success: function(uploadRes) {
|
|
|
|
|
- var data = JSON.parse(uploadRes.data)
|
|
|
|
|
- if (data.code === 200 && data.data) {
|
|
|
|
|
- self.handleParsedResult(data.data)
|
|
|
|
|
- } else {
|
|
|
|
|
- self.parsingPreview = false
|
|
|
|
|
- uni.showToast({ title: data.message || '解析失败', icon: 'none' })
|
|
|
|
|
- }
|
|
|
|
|
- },
|
|
|
|
|
- fail: function(err) {
|
|
|
|
|
- self.parsingPreview = false
|
|
|
|
|
- console.error('PDF上传失败', err)
|
|
|
|
|
- uni.showToast({ title: '上传失败', icon: 'none' })
|
|
|
|
|
- }
|
|
|
|
|
- })
|
|
|
|
|
- } else if (self.photoPath) {
|
|
|
|
|
- // 图片上传
|
|
|
|
|
- uni.uploadFile({
|
|
|
|
|
- url: apiPath + '?type=image' + (self.memberId ? '&memberId=' + self.memberId : ''),
|
|
|
|
|
- filePath: filePath,
|
|
|
|
|
- name: 'file',
|
|
|
|
|
- header: {
|
|
|
|
|
- 'Authorization': 'Bearer ' + (uni.getStorageSync('token') || '')
|
|
|
|
|
- },
|
|
|
|
|
- success: function(uploadRes) {
|
|
|
|
|
- var data = JSON.parse(uploadRes.data)
|
|
|
|
|
- if (data.code === 200 && data.data) {
|
|
|
|
|
- self.handleParsedResult(data.data)
|
|
|
|
|
- } else {
|
|
|
|
|
- self.parsingPreview = false
|
|
|
|
|
- uni.showToast({ title: data.message || '解析失败', icon: 'none' })
|
|
|
|
|
- }
|
|
|
|
|
- },
|
|
|
|
|
- fail: function(err) {
|
|
|
|
|
- self.parsingPreview = false
|
|
|
|
|
- console.error('图片上传失败', err)
|
|
|
|
|
- uni.showToast({ title: '上传失败', icon: 'none' })
|
|
|
|
|
- }
|
|
|
|
|
- })
|
|
|
|
|
- }
|
|
|
|
|
- },
|
|
|
|
|
- handleParsedResult: function(detail) {
|
|
|
|
|
- this.parsingPreview = false
|
|
|
|
|
- this.parsedInfo = {
|
|
|
|
|
- draftId: detail.draftId,
|
|
|
|
|
- personName: detail.extractedName || '',
|
|
|
|
|
- extractedGender: detail.extractedGender || '',
|
|
|
|
|
- extractedAge: detail.extractedAge || '',
|
|
|
|
|
- reportNumber: detail.extractedReportNumber,
|
|
|
|
|
- reportDate: detail.extractedReportDate,
|
|
|
|
|
- needManualSelect: detail.needBind,
|
|
|
|
|
- payload: detail.payload || {},
|
|
|
|
|
- matchedMemberId: detail.matchedMemberId,
|
|
|
|
|
- confidence: detail.confidence,
|
|
|
|
|
- matchLabel: detail.matchLabel,
|
|
|
|
|
- candidates: detail.candidates || []
|
|
|
|
|
- }
|
|
|
|
|
- if (detail.extractedReportDate) {
|
|
|
|
|
- this.form.reportDate = detail.extractedReportDate
|
|
|
|
|
- }
|
|
|
|
|
- if (detail.needBind) {
|
|
|
|
|
- this.showMemberSelector = true
|
|
|
|
|
- this.loadFamilyMembers()
|
|
|
|
|
- if (this.parsedInfo.personName) {
|
|
|
|
|
- this.promptAddParsedMember()
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- },
|
|
|
|
|
- loadFamilyMembers() {
|
|
|
|
|
- var self = this
|
|
|
|
|
- // 从后端获取真实家庭成员(家长 + 孩子),storage 中并无该数据
|
|
|
|
|
- getFamilyMembers().then(function(res) {
|
|
|
|
|
- var data = (res && res.data) || {}
|
|
|
|
|
- var parents = (data.parents || []).map(function(p) {
|
|
|
|
|
- return { id: p.id, nickname: p.realName || p.nickname, gender: p.gender, role: 'parent' }
|
|
|
|
|
- })
|
|
|
|
|
- var children = (data.children || []).map(function(c) {
|
|
|
|
|
- return { id: c.id, nickname: c.nickname, gender: c.gender, age: c.age, role: 'child' }
|
|
|
|
|
- })
|
|
|
|
|
- self.familyMembers = parents.concat(children)
|
|
|
|
|
- }).catch(function(e) {
|
|
|
|
|
- console.log('加载家庭成员失败', e)
|
|
|
|
|
- self.familyMembers = []
|
|
|
|
|
- })
|
|
|
|
|
- },
|
|
|
|
|
- selectMember(member) {
|
|
|
|
|
- this.selectedMemberId = member.id || member.userId
|
|
|
|
|
- this.parsedInfo = this.parsedInfo || {}
|
|
|
|
|
- // 手动选择的成员通过 matchedMemberId 传递给确认页
|
|
|
|
|
- this.parsedInfo.matchedMemberId = this.selectedMemberId
|
|
|
|
|
- this.parsedInfo.needManualSelect = false
|
|
|
|
|
- this.showMemberSelector = false
|
|
|
|
|
- },
|
|
|
|
|
- genderText(gender) {
|
|
|
|
|
- if (gender === 'male') return '男'
|
|
|
|
|
- if (gender === 'female') return '女'
|
|
|
|
|
- return ''
|
|
|
|
|
- },
|
|
|
|
|
- // 询问是否将解析到的人添加为家庭成员
|
|
|
|
|
- promptAddParsedMember() {
|
|
|
|
|
- var self = this
|
|
|
|
|
- if (!this.parsedInfo || !this.parsedInfo.personName) return
|
|
|
|
|
- var name = this.parsedInfo.personName
|
|
|
|
|
- var desc = name
|
|
|
|
|
- var extra = []
|
|
|
|
|
- var g = this.genderText(this.parsedInfo.extractedGender)
|
|
|
|
|
- if (g) extra.push(g)
|
|
|
|
|
- if (this.parsedInfo.extractedAge) extra.push(this.parsedInfo.extractedAge + '岁')
|
|
|
|
|
- if (extra.length > 0) desc = name + '(' + extra.join(',') + ')'
|
|
|
|
|
- uni.showModal({
|
|
|
|
|
- title: '未匹配到家庭成员',
|
|
|
|
|
- content: '是否将解析到的「' + desc + '」添加为家庭成员?',
|
|
|
|
|
- confirmText: '添加',
|
|
|
|
|
- cancelText: '手动选择',
|
|
|
|
|
- success: function(res) {
|
|
|
|
|
- if (res.confirm) {
|
|
|
|
|
- self.chooseRelationAndAdd()
|
|
|
|
|
- }
|
|
|
|
|
- // 取消则保留下方的手动选择列表
|
|
|
|
|
- }
|
|
|
|
|
- })
|
|
|
|
|
- },
|
|
|
|
|
- // 选择新成员与家庭的关系(后端要求辈分必填)后添加
|
|
|
|
|
- chooseRelationAndAdd() {
|
|
|
|
|
- var self = this
|
|
|
|
|
- var relOptions = [
|
|
|
|
|
- { label: '孩子', generationLevel: 'child' },
|
|
|
|
|
- { label: '配偶', generationLevel: 'peer', peerType: 'spouse' },
|
|
|
|
|
- { label: '父母', generationLevel: 'parent' },
|
|
|
|
|
- { label: '兄弟姐妹', generationLevel: 'peer', peerType: 'sibling' },
|
|
|
|
|
- { label: '祖父母', generationLevel: 'grandparent' }
|
|
|
|
|
- ]
|
|
|
|
|
- uni.showActionSheet({
|
|
|
|
|
- itemList: relOptions.map(function(o) { return o.label }),
|
|
|
|
|
- success: function(res) {
|
|
|
|
|
- var rel = relOptions[res.tapIndex]
|
|
|
|
|
- if (rel) {
|
|
|
|
|
- self.doAddParsedMember(rel)
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- })
|
|
|
|
|
- },
|
|
|
|
|
- // 调用后端添加成员,并将其作为本次报告对象
|
|
|
|
|
- doAddParsedMember(rel) {
|
|
|
|
|
- var self = this
|
|
|
|
|
- var name = this.parsedInfo.personName
|
|
|
|
|
- var dto = {
|
|
|
|
|
- nickname: name,
|
|
|
|
|
- gender: this.parsedInfo.extractedGender || '',
|
|
|
|
|
- generationLevel: rel.generationLevel
|
|
|
|
|
- }
|
|
|
|
|
- if (rel.peerType) dto.peerType = rel.peerType
|
|
|
|
|
- // 由年龄推算大致出生年份(用于展示,用户后续可修改)
|
|
|
|
|
- var age = parseInt(this.parsedInfo.extractedAge)
|
|
|
|
|
- if (!isNaN(age) && age > 0 && age < 130) {
|
|
|
|
|
- dto.birthday = (new Date().getFullYear() - age) + '-01-01'
|
|
|
|
|
- }
|
|
|
|
|
- uni.showLoading({ title: '添加成员...' })
|
|
|
|
|
- addFamilyMember(dto).then(function(res) {
|
|
|
|
|
|
|
+ self.uploading = true
|
|
|
|
|
+ uni.showLoading({ title: '上传中...' })
|
|
|
|
|
+ uploadReportOnly(filePath, type).then(function(res) {
|
|
|
uni.hideLoading()
|
|
uni.hideLoading()
|
|
|
- if (res && res.code === 200) {
|
|
|
|
|
- var newId = res.data && (res.data.id || res.data.userId)
|
|
|
|
|
- self.selectedMemberId = newId
|
|
|
|
|
- self.parsedInfo.matchedMemberId = newId
|
|
|
|
|
- self.parsedInfo.matchedMemberName = name
|
|
|
|
|
- self.parsedInfo.needManualSelect = false
|
|
|
|
|
- self.showMemberSelector = false
|
|
|
|
|
- self.loadFamilyMembers()
|
|
|
|
|
- uni.showToast({ title: '已添加并选中', icon: 'success' })
|
|
|
|
|
- } else {
|
|
|
|
|
- uni.showToast({ title: (res && res.message) || '添加失败', icon: 'none' })
|
|
|
|
|
|
|
+ self.uploading = false
|
|
|
|
|
+ var draftId = res.data && res.data.draftId
|
|
|
|
|
+ if (!draftId) {
|
|
|
|
|
+ uni.showToast({ title: (res && res.message) || '上传失败', icon: 'none' })
|
|
|
|
|
+ return
|
|
|
}
|
|
}
|
|
|
- }).catch(function(e) {
|
|
|
|
|
|
|
+ // 只传 draftId,确认页自行解析
|
|
|
|
|
+ uni.redirectTo({ url: '/pages/health/report-confirm?draftId=' + draftId })
|
|
|
|
|
+ }).catch(function(err) {
|
|
|
uni.hideLoading()
|
|
uni.hideLoading()
|
|
|
- console.log('添加成员失败', e)
|
|
|
|
|
- uni.showToast({ title: '添加失败,请重试', icon: 'none' })
|
|
|
|
|
|
|
+ self.uploading = false
|
|
|
|
|
+ var msg = (err && err.message) || '上传失败,请重试'
|
|
|
|
|
+ uni.showToast({ title: msg, icon: 'none' })
|
|
|
})
|
|
})
|
|
|
},
|
|
},
|
|
|
- removeFile() {
|
|
|
|
|
- this.selectedFile = null
|
|
|
|
|
- this.photoPath = null
|
|
|
|
|
- this.parsedInfo = null
|
|
|
|
|
- this.showMemberSelector = false
|
|
|
|
|
- this.selectedMemberId = null
|
|
|
|
|
- this.form.reportDate = ''
|
|
|
|
|
- this.form.overallScore = 0
|
|
|
|
|
- },
|
|
|
|
|
loadRecommendProducts: function() {
|
|
loadRecommendProducts: function() {
|
|
|
var self = this
|
|
var self = this
|
|
|
self.loadingProducts = true
|
|
self.loadingProducts = true
|
|
@@ -495,54 +197,9 @@ export default {
|
|
|
if (cents === null || cents === undefined) return '¥0'
|
|
if (cents === null || cents === undefined) return '¥0'
|
|
|
return '¥' + (cents / 100).toFixed(2)
|
|
return '¥' + (cents / 100).toFixed(2)
|
|
|
},
|
|
},
|
|
|
- formatFileSize(bytes) {
|
|
|
|
|
- if (!bytes) return ''
|
|
|
|
|
- if (bytes < 1024) return bytes + 'B'
|
|
|
|
|
- if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + 'KB'
|
|
|
|
|
- return (bytes / 1024 / 1024).toFixed(1) + 'MB'
|
|
|
|
|
- },
|
|
|
|
|
goBack() {
|
|
goBack() {
|
|
|
uni.navigateBack()
|
|
uni.navigateBack()
|
|
|
},
|
|
},
|
|
|
- doSubmit() {
|
|
|
|
|
- if (this.submitting) return
|
|
|
|
|
- var self = this
|
|
|
|
|
-
|
|
|
|
|
- if (!self.parsedInfo || !self.parsedInfo.draftId) {
|
|
|
|
|
- uni.showToast({ title: '请先上传文件并等待解析完成', icon: 'none' })
|
|
|
|
|
- return
|
|
|
|
|
- }
|
|
|
|
|
- if (self.parsedInfo.needManualSelect && !self.selectedMemberId) {
|
|
|
|
|
- uni.showToast({ title: '请先选择成员', icon: 'none' })
|
|
|
|
|
- return
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- var payloadStr = encodeURIComponent(JSON.stringify(self.parsedInfo.payload || {}))
|
|
|
|
|
- var candidatesStr = (self.parsedInfo.candidates && self.parsedInfo.candidates.length > 0)
|
|
|
|
|
- ? encodeURIComponent(JSON.stringify(self.parsedInfo.candidates))
|
|
|
|
|
- : ''
|
|
|
|
|
- var params = 'draftId=' + self.parsedInfo.draftId
|
|
|
|
|
- + '&extractedName=' + encodeURIComponent(self.parsedInfo.personName || '')
|
|
|
|
|
- + '&extractedGender=' + encodeURIComponent(self.parsedInfo.extractedGender || '')
|
|
|
|
|
- + '&extractedAge=' + (self.parsedInfo.extractedAge || '')
|
|
|
|
|
- + '&payload=' + payloadStr
|
|
|
|
|
- + '&needBind=' + (self.parsedInfo.needManualSelect ? 'true' : 'false')
|
|
|
|
|
- + '&matchConfidence=' + (self.parsedInfo.confidence || '')
|
|
|
|
|
- + '&matchLabel=' + encodeURIComponent(self.parsedInfo.matchLabel || '')
|
|
|
|
|
- + '&candidates=' + candidatesStr
|
|
|
|
|
- + '&extractedReportDate=' + encodeURIComponent(self.parsedInfo.reportDate || '')
|
|
|
|
|
- if (self.parsedInfo.matchedMemberId) {
|
|
|
|
|
- params += '&matchedMemberId=' + self.parsedInfo.matchedMemberId
|
|
|
|
|
- }
|
|
|
|
|
- if (self.memberId) {
|
|
|
|
|
- params += '&memberId=' + self.memberId
|
|
|
|
|
- }
|
|
|
|
|
- if (self.familyId) {
|
|
|
|
|
- params += '&familyId=' + self.familyId
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- uni.navigateTo({ url: '/pages/health/report-confirm?' + params })
|
|
|
|
|
- },
|
|
|
|
|
getImageUrl: function(path) {
|
|
getImageUrl: function(path) {
|
|
|
return config.API_BASE_URL + path
|
|
return config.API_BASE_URL + path
|
|
|
}
|
|
}
|
|
@@ -621,326 +278,15 @@ export default {
|
|
|
margin-bottom: 16rpx;
|
|
margin-bottom: 16rpx;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-/* 类型选择 */
|
|
|
|
|
-.type-selector {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- flex-direction: row;
|
|
|
|
|
- gap: 20rpx;
|
|
|
|
|
-}
|
|
|
|
|
-.type-option {
|
|
|
|
|
- flex: 1;
|
|
|
|
|
- display: flex;
|
|
|
|
|
- flex-direction: column;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- padding: 24rpx;
|
|
|
|
|
- border-radius: 16rpx;
|
|
|
|
|
- background: #F5F7FA;
|
|
|
|
|
- border: 2rpx solid transparent;
|
|
|
|
|
-}
|
|
|
|
|
-.type-selected {
|
|
|
|
|
- background: #E3F2FD;
|
|
|
|
|
- border-color: #5B9BD5;
|
|
|
|
|
-}
|
|
|
|
|
-.type-icon {
|
|
|
|
|
- font-size: 40rpx;
|
|
|
|
|
- margin-bottom: 8rpx;
|
|
|
|
|
-}
|
|
|
|
|
-.type-name {
|
|
|
|
|
- font-size: 26rpx;
|
|
|
|
|
- color: #333;
|
|
|
|
|
- font-weight: 500;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/* 文件上传 */
|
|
|
|
|
-.file-upload-area {
|
|
|
|
|
- border: 2rpx dashed #CBD5E1;
|
|
|
|
|
- border-radius: 16rpx;
|
|
|
|
|
- padding: 32rpx 20rpx;
|
|
|
|
|
- text-align: center;
|
|
|
|
|
- background: #FAFBFC;
|
|
|
|
|
- cursor: pointer;
|
|
|
|
|
-}
|
|
|
|
|
-.file-upload-area:active {
|
|
|
|
|
- background: #F1F5F9;
|
|
|
|
|
-}
|
|
|
|
|
-.file-upload-placeholder {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- flex-direction: column;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
-}
|
|
|
|
|
-.file-upload-icon {
|
|
|
|
|
- font-size: 48rpx;
|
|
|
|
|
- margin-bottom: 12rpx;
|
|
|
|
|
-}
|
|
|
|
|
-.file-upload-text {
|
|
|
|
|
- font-size: 28rpx;
|
|
|
|
|
- color: #5B9BD5;
|
|
|
|
|
- font-weight: 500;
|
|
|
|
|
-}
|
|
|
|
|
-.file-upload-hint {
|
|
|
|
|
- font-size: 22rpx;
|
|
|
|
|
- color: #94A3B8;
|
|
|
|
|
- margin-top: 8rpx;
|
|
|
|
|
-}
|
|
|
|
|
-.file-info {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- flex-direction: row;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- background: #E3F2FD;
|
|
|
|
|
- border-radius: 12rpx;
|
|
|
|
|
- padding: 16rpx 20rpx;
|
|
|
|
|
- margin-top: 16rpx;
|
|
|
|
|
-}
|
|
|
|
|
-.file-name {
|
|
|
|
|
- flex: 1;
|
|
|
|
|
- font-size: 24rpx;
|
|
|
|
|
- color: #1565C0;
|
|
|
|
|
- overflow: hidden;
|
|
|
|
|
- text-overflow: ellipsis;
|
|
|
|
|
- white-space: nowrap;
|
|
|
|
|
-}
|
|
|
|
|
-.file-size {
|
|
|
|
|
- font-size: 22rpx;
|
|
|
|
|
- color: #64748B;
|
|
|
|
|
- margin: 0 16rpx;
|
|
|
|
|
- flex-shrink: 0;
|
|
|
|
|
-}
|
|
|
|
|
-.file-remove {
|
|
|
|
|
- font-size: 24rpx;
|
|
|
|
|
- color: #EF4444;
|
|
|
|
|
- flex-shrink: 0;
|
|
|
|
|
- padding: 4rpx 8rpx;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/* 日期 */
|
|
|
|
|
-.date-display {
|
|
|
|
|
|
|
+/* 上传中状态 */
|
|
|
|
|
+.analyzing-status {
|
|
|
display: flex;
|
|
display: flex;
|
|
|
flex-direction: row;
|
|
flex-direction: row;
|
|
|
align-items: center;
|
|
align-items: center;
|
|
|
- justify-content: space-between;
|
|
|
|
|
- padding: 16rpx 20rpx;
|
|
|
|
|
- background: #F5F7FA;
|
|
|
|
|
- border-radius: 12rpx;
|
|
|
|
|
- font-size: 28rpx;
|
|
|
|
|
- color: #333;
|
|
|
|
|
-}
|
|
|
|
|
-.date-arrow {
|
|
|
|
|
- font-size: 32rpx;
|
|
|
|
|
- color: #999;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/* 评分滑块 */
|
|
|
|
|
-.score-input-wrap {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- flex-direction: row;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
-}
|
|
|
|
|
-.score-slider {
|
|
|
|
|
- flex: 1;
|
|
|
|
|
-}
|
|
|
|
|
-.score-value {
|
|
|
|
|
- font-size: 28rpx;
|
|
|
|
|
- font-weight: bold;
|
|
|
|
|
- color: #5B9BD5;
|
|
|
|
|
- margin-left: 16rpx;
|
|
|
|
|
- min-width: 80rpx;
|
|
|
|
|
- text-align: center;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/* 备注 */
|
|
|
|
|
-.note-input {
|
|
|
|
|
- width: 100%;
|
|
|
|
|
- min-height: 160rpx;
|
|
|
|
|
- background: #F5F7FA;
|
|
|
|
|
- border-radius: 12rpx;
|
|
|
|
|
- padding: 20rpx;
|
|
|
|
|
- font-size: 26rpx;
|
|
|
|
|
- color: #333;
|
|
|
|
|
- box-sizing: border-box;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/* 提交按钮 */
|
|
|
|
|
-.submit-btn {
|
|
|
|
|
- background: linear-gradient(135deg, #5B9BD5, #3A7CC4);
|
|
|
|
|
- color: #fff;
|
|
|
|
|
- font-size: 30rpx;
|
|
|
|
|
- font-weight: bold;
|
|
|
|
|
- text-align: center;
|
|
|
|
|
- padding: 28rpx;
|
|
|
|
|
- border-radius: 16rpx;
|
|
|
|
|
- margin-top: 10rpx;
|
|
|
|
|
-}
|
|
|
|
|
-.submit-btn:active {
|
|
|
|
|
- opacity: 0.85;
|
|
|
|
|
-}
|
|
|
|
|
-.btn-disabled {
|
|
|
|
|
- opacity: 0.5;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/* ===== PDF解析预览 ===== */
|
|
|
|
|
-.parsed-preview {
|
|
|
|
|
- background: #E8F5E9;
|
|
|
|
|
- border-radius: 16rpx;
|
|
|
|
|
- margin: 0 30rpx 20rpx;
|
|
|
|
|
- padding: 20rpx 24rpx;
|
|
|
|
|
- border: 2rpx solid #A5D6A7;
|
|
|
|
|
-}
|
|
|
|
|
-.parsed-title {
|
|
|
|
|
- font-size: 24rpx;
|
|
|
|
|
- font-weight: bold;
|
|
|
|
|
- color: #2E7D32;
|
|
|
|
|
- margin-bottom: 12rpx;
|
|
|
|
|
-}
|
|
|
|
|
-.parsed-row {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- flex-direction: row;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- margin-bottom: 8rpx;
|
|
|
|
|
-}
|
|
|
|
|
-.parsed-label {
|
|
|
|
|
- font-size: 24rpx;
|
|
|
|
|
- color: #388E3C;
|
|
|
|
|
- width: 140rpx;
|
|
|
|
|
- flex-shrink: 0;
|
|
|
|
|
-}
|
|
|
|
|
-.parsed-value {
|
|
|
|
|
- font-size: 24rpx;
|
|
|
|
|
- color: #1B5E20;
|
|
|
|
|
- font-weight: 500;
|
|
|
|
|
-}
|
|
|
|
|
-.match-status {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- flex-direction: row;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- margin-top: 12rpx;
|
|
|
|
|
- padding: 12rpx 16rpx;
|
|
|
|
|
- background: #FFF3E0;
|
|
|
|
|
- border-radius: 8rpx;
|
|
|
|
|
-}
|
|
|
|
|
-.match-status.success {
|
|
|
|
|
- background: #E8F5E9;
|
|
|
|
|
-}
|
|
|
|
|
-.match-icon {
|
|
|
|
|
- font-size: 24rpx;
|
|
|
|
|
- margin-right: 8rpx;
|
|
|
|
|
-}
|
|
|
|
|
-.match-text {
|
|
|
|
|
- font-size: 24rpx;
|
|
|
|
|
- color: #E65100;
|
|
|
|
|
-}
|
|
|
|
|
-.match-status.success .match-text {
|
|
|
|
|
- color: #2E7D32;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/* ===== 家庭成员选择器 ===== */
|
|
|
|
|
-.member-selector {
|
|
|
|
|
- margin: 0 30rpx 20rpx;
|
|
|
|
|
-}
|
|
|
|
|
-.member-list {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- flex-direction: row;
|
|
|
|
|
- flex-wrap: wrap;
|
|
|
|
|
- gap: 16rpx;
|
|
|
|
|
-}
|
|
|
|
|
-.member-item {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- flex-direction: column;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- padding: 20rpx 24rpx;
|
|
|
|
|
- background: #F5F7FA;
|
|
|
|
|
- border-radius: 16rpx;
|
|
|
|
|
- border: 2rpx solid transparent;
|
|
|
|
|
- min-width: 120rpx;
|
|
|
|
|
-}
|
|
|
|
|
-.member-selected {
|
|
|
|
|
- background: #E3F2FD;
|
|
|
|
|
- border-color: #5B9BD5;
|
|
|
|
|
-}
|
|
|
|
|
-.member-avatar {
|
|
|
|
|
- width: 64rpx;
|
|
|
|
|
- height: 64rpx;
|
|
|
|
|
- border-radius: 50%;
|
|
|
|
|
- background: #5B9BD5;
|
|
|
|
|
- color: #fff;
|
|
|
|
|
- font-size: 28rpx;
|
|
|
|
|
- font-weight: bold;
|
|
|
|
|
- display: flex;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- justify-content: center;
|
|
|
|
|
- margin-bottom: 8rpx;
|
|
|
|
|
-}
|
|
|
|
|
-.member-name {
|
|
|
|
|
- font-size: 24rpx;
|
|
|
|
|
- color: #333;
|
|
|
|
|
-}
|
|
|
|
|
-.add-member-btn {
|
|
|
|
|
- margin-top: 16rpx;
|
|
|
|
|
- padding: 20rpx;
|
|
|
|
|
- border: 1rpx dashed #4caf50;
|
|
|
|
|
- border-radius: 12rpx;
|
|
|
|
|
- text-align: center;
|
|
|
|
|
- background: #f6fbf6;
|
|
|
|
|
-}
|
|
|
|
|
-.add-member-text {
|
|
|
|
|
- font-size: 26rpx;
|
|
|
|
|
- color: #4caf50;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/* ===== 舌诊拍照区 ===== */
|
|
|
|
|
-.photo-area {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- justify-content: center;
|
|
|
|
|
- margin-bottom: 16rpx;
|
|
|
|
|
-}
|
|
|
|
|
-.photo-placeholder {
|
|
|
|
|
- width: 400rpx;
|
|
|
|
|
- height: 400rpx;
|
|
|
|
|
- border: 4rpx dashed #CBD5E1;
|
|
|
|
|
- border-radius: 24rpx;
|
|
|
|
|
- display: flex;
|
|
|
|
|
- flex-direction: column;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- justify-content: center;
|
|
|
|
|
- background: #fff;
|
|
|
|
|
-}
|
|
|
|
|
-.photo-icon {
|
|
|
|
|
- font-size: 80rpx;
|
|
|
|
|
- margin-bottom: 16rpx;
|
|
|
|
|
-}
|
|
|
|
|
-.photo-text {
|
|
|
|
|
- font-size: 28rpx;
|
|
|
|
|
- color: #64748B;
|
|
|
|
|
-}
|
|
|
|
|
-.photo-sub {
|
|
|
|
|
- font-size: 22rpx;
|
|
|
|
|
- color: #94A3B8;
|
|
|
|
|
- margin-top: 12rpx;
|
|
|
|
|
- text-align: center;
|
|
|
|
|
- padding: 0 20rpx;
|
|
|
|
|
-}
|
|
|
|
|
-.photo-preview {
|
|
|
|
|
- width: 400rpx;
|
|
|
|
|
- height: 400rpx;
|
|
|
|
|
- border-radius: 24rpx;
|
|
|
|
|
-}
|
|
|
|
|
-.photo-analyzing {
|
|
|
|
|
- width: 400rpx;
|
|
|
|
|
- height: 400rpx;
|
|
|
|
|
- border-radius: 24rpx;
|
|
|
|
|
- background: #f5f5f5;
|
|
|
|
|
- display: flex;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- justify-content: center;
|
|
|
|
|
}
|
|
}
|
|
|
.analyzing-text {
|
|
.analyzing-text {
|
|
|
- font-size: 28rpx;
|
|
|
|
|
- color: #5B9BD5;
|
|
|
|
|
-}
|
|
|
|
|
-.photo-retake {
|
|
|
|
|
- text-align: center;
|
|
|
|
|
- font-size: 24rpx;
|
|
|
|
|
|
|
+ font-size: 26rpx;
|
|
|
color: #5B9BD5;
|
|
color: #5B9BD5;
|
|
|
- margin-top: 8rpx;
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/* ===== 底部 ===== */
|
|
/* ===== 底部 ===== */
|