370 lines
15 KiB
Vue
370 lines
15 KiB
Vue
<template>
|
||
<view class="page" :class="{ big: isH5 }">
|
||
<view class="card">
|
||
<view class="kv">
|
||
<view class="kv-item"><text class="k">用户ID</text><text class="v">{{ userId || '—' }}</text></view>
|
||
</view>
|
||
<view class="card-title">选择对比记录</view>
|
||
<picker mode="selector" :range="scaleLabels" :value="selectedScaleIndex" @change="onPickScale">
|
||
<view class="picker">量表:{{ selectedScaleLabel }}</view>
|
||
</picker>
|
||
<picker mode="selector" :range="attemptLabels" :value="selectedAttempt1Index" @change="onPickAttempt1">
|
||
<view class="picker">测评记录1:{{ selectedAttempt1Label }}</view>
|
||
</picker>
|
||
<picker mode="selector" :range="attemptLabels" :value="selectedAttempt2Index" @change="onPickAttempt2">
|
||
<view class="picker">测评记录2:{{ selectedAttempt2Label }}</view>
|
||
</picker>
|
||
<button class="btn primary" :disabled="loading" @click="doCompare">开始对比</button>
|
||
<button class="btn ghost" :disabled="loading || !rows.length" @click="copyReport">复制报告</button>
|
||
<button class="btn ghost" :disabled="loading || !canOpenReport1" @click="openReport1">查看测评1报告</button>
|
||
<button class="btn ghost" :disabled="loading || !canOpenReport2" @click="openReport2">查看测评2报告</button>
|
||
<view v-if="errorMsg" class="error">{{ errorMsg }}</view>
|
||
</view>
|
||
|
||
<view class="card">
|
||
<view class="card-title">结果</view>
|
||
<view v-if="summaryText" class="summary">
|
||
<view class="summary-title">对比摘要</view>
|
||
<view class="summary-text">{{ summaryText }}</view>
|
||
</view>
|
||
<view v-if="loading" class="state">
|
||
<uni-icons type="spinner-cycle" size="28" color="#94A3B8"></uni-icons>
|
||
<view class="state-text">加载中...</view>
|
||
</view>
|
||
<view v-else>
|
||
<view v-if="rows.length === 0" class="state">
|
||
<uni-icons type="list" size="34" color="#CBD5E1"></uni-icons>
|
||
<view class="state-text">暂无数据</view>
|
||
</view>
|
||
<view v-else class="list">
|
||
<view class="item" v-for="(r, idx) in rows" :key="idx">
|
||
<view class="item-head">
|
||
<view class="item-title">{{ r.scaleName || ('量表#' + r.scaleId) }}</view>
|
||
<view class="pill" :class="{ up: r.pctChange > 0, down: r.pctChange < 0 }">
|
||
{{ r.pctChange == null ? '—' : (r.pctChange + '%') }}
|
||
</view>
|
||
</view>
|
||
<view class="item-grid">
|
||
<view class="item-desc">得分1:{{ r.totalScore1 == null ? '—' : r.totalScore1 }}</view>
|
||
<view class="item-desc">得分2:{{ r.totalScore2 == null ? '—' : r.totalScore2 }}</view>
|
||
<view class="item-desc">差值:{{ r.diff == null ? '—' : r.diff }}</view>
|
||
<view class="item-desc">测评1:{{ formatTime(r.submitTime1) }}</view>
|
||
<view class="item-desc">测评2:{{ formatTime(r.submitTime2) }}</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
import { compareUserAssessment, compareUserAssessmentReport, getUserAssessmentSummary } from '../../api/psychology/assessment'
|
||
import UniIcons from '@/uni_modules/uni-icons/components/uni-icons/uni-icons.vue'
|
||
|
||
export default {
|
||
components: {
|
||
UniIcons
|
||
},
|
||
data() {
|
||
return {
|
||
isH5: false,
|
||
userId: '',
|
||
summary: null,
|
||
scales: [],
|
||
selectedScaleIndex: 0,
|
||
selectedAttempt1Index: 0,
|
||
selectedAttempt2Index: 0,
|
||
loading: false,
|
||
errorMsg: '',
|
||
summaryText: '',
|
||
summaryLines: [],
|
||
rows: []
|
||
}
|
||
},
|
||
onLoad(options) {
|
||
try {
|
||
const info = uni.getSystemInfoSync()
|
||
const p = info ? (info.uniPlatform || info.platform) : ''
|
||
this.isH5 = p === 'web' || p === 'h5'
|
||
} catch (e) {
|
||
this.isH5 = false
|
||
}
|
||
this.userId = options && options.userId ? options.userId : ''
|
||
this.loadSummary()
|
||
},
|
||
computed: {
|
||
scaleLabels() {
|
||
const list = this.scales || []
|
||
if (!list.length) return ['暂无量表']
|
||
return list.map((s) => (s && s.scaleName) ? s.scaleName : ('量表#' + (s && s.scaleId)))
|
||
},
|
||
selectedScale() {
|
||
const list = this.scales || []
|
||
if (!list.length) return null
|
||
return list[this.selectedScaleIndex] || list[0] || null
|
||
},
|
||
selectedScaleLabel() {
|
||
if (!this.selectedScale) return '请选择'
|
||
return (this.selectedScale.scaleName || ('量表#' + this.selectedScale.scaleId))
|
||
},
|
||
attempts() {
|
||
const s = this.selectedScale
|
||
if (!s || !Array.isArray(s.attempts)) return []
|
||
return s.attempts
|
||
.slice()
|
||
.filter((a) => a && a.assessmentId)
|
||
.sort((a, b) => {
|
||
const ta = a.submitTime ? new Date(a.submitTime).getTime() : 0
|
||
const tb = b.submitTime ? new Date(b.submitTime).getTime() : 0
|
||
return tb - ta
|
||
})
|
||
},
|
||
attemptLabels() {
|
||
const list = this.attempts
|
||
if (!list.length) return ['暂无记录']
|
||
return list.map((a) => {
|
||
const t = a.submitTime || a.startTime
|
||
return `${this.formatTime(t)} · 得分 ${a.totalScore == null ? '—' : a.totalScore} · #${a.assessmentId}`
|
||
})
|
||
},
|
||
selectedAttempt1() {
|
||
const list = this.attempts
|
||
return list[this.selectedAttempt1Index] || null
|
||
},
|
||
selectedAttempt2() {
|
||
const list = this.attempts
|
||
return list[this.selectedAttempt2Index] || null
|
||
},
|
||
selectedAttempt1Label() {
|
||
if (!this.selectedAttempt1) return '请选择'
|
||
return this.attemptLabels[this.selectedAttempt1Index] || '请选择'
|
||
},
|
||
selectedAttempt2Label() {
|
||
if (!this.selectedAttempt2) return '请选择'
|
||
return this.attemptLabels[this.selectedAttempt2Index] || '请选择'
|
||
},
|
||
canOpenReport1() {
|
||
return !!(this.selectedAttempt1 && this.selectedAttempt1.reportId)
|
||
},
|
||
canOpenReport2() {
|
||
return !!(this.selectedAttempt2 && this.selectedAttempt2.reportId)
|
||
}
|
||
},
|
||
methods: {
|
||
loadSummary() {
|
||
if (!this.userId) {
|
||
this.errorMsg = '缺少 userId'
|
||
return
|
||
}
|
||
this.loading = true
|
||
this.errorMsg = ''
|
||
return getUserAssessmentSummary(this.userId)
|
||
.then((res) => {
|
||
this.loading = false
|
||
const data = res && res.data ? res.data : null
|
||
if (!data || data.code !== 200) {
|
||
this.errorMsg = (data && data.msg) ? data.msg : '加载失败'
|
||
return
|
||
}
|
||
this.summary = data.data || null
|
||
this.scales = (this.summary && Array.isArray(this.summary.scales)) ? this.summary.scales : []
|
||
this.selectedScaleIndex = 0
|
||
this.selectedAttempt1Index = 0
|
||
this.selectedAttempt2Index = 0
|
||
})
|
||
.catch((e) => {
|
||
this.loading = false
|
||
this.errorMsg = e && e.message ? e.message : '网络错误'
|
||
})
|
||
},
|
||
toDateTimeString(val) {
|
||
if (!val) return ''
|
||
try {
|
||
const d = new Date(val)
|
||
if (isNaN(d.getTime())) return ''
|
||
const y = d.getFullYear()
|
||
const m = String(d.getMonth() + 1).padStart(2, '0')
|
||
const day = String(d.getDate()).padStart(2, '0')
|
||
const hh = String(d.getHours()).padStart(2, '0')
|
||
const mm = String(d.getMinutes()).padStart(2, '0')
|
||
const ss = String(d.getSeconds()).padStart(2, '0')
|
||
return `${y}-${m}-${day} ${hh}:${mm}:${ss}`
|
||
} catch (e) {
|
||
return ''
|
||
}
|
||
},
|
||
onPickScale(e) {
|
||
const idx = e && e.detail ? Number(e.detail.value) : 0
|
||
this.selectedScaleIndex = isNaN(idx) ? 0 : idx
|
||
this.selectedAttempt1Index = 0
|
||
this.selectedAttempt2Index = 0
|
||
},
|
||
onPickAttempt1(e) {
|
||
const idx = e && e.detail ? Number(e.detail.value) : 0
|
||
this.selectedAttempt1Index = isNaN(idx) ? 0 : idx
|
||
},
|
||
onPickAttempt2(e) {
|
||
const idx = e && e.detail ? Number(e.detail.value) : 0
|
||
this.selectedAttempt2Index = isNaN(idx) ? 0 : idx
|
||
},
|
||
openReport1() {
|
||
if (!this.canOpenReport1) return
|
||
uni.navigateTo({
|
||
url: `/pages/report/detail?reportId=${encodeURIComponent(this.selectedAttempt1.reportId)}&sourceType=assessment`
|
||
})
|
||
},
|
||
openReport2() {
|
||
if (!this.canOpenReport2) return
|
||
uni.navigateTo({
|
||
url: `/pages/report/detail?reportId=${encodeURIComponent(this.selectedAttempt2.reportId)}&sourceType=assessment`
|
||
})
|
||
},
|
||
copyReport() {
|
||
if (!this.userId || !this.selectedScale || !this.selectedAttempt1 || !this.selectedAttempt2) {
|
||
this.errorMsg = '请先选择两次测评记录并完成对比'
|
||
return
|
||
}
|
||
const t1 = this.toDateTimeString(this.selectedAttempt1.submitTime || this.selectedAttempt1.startTime)
|
||
const t2 = this.toDateTimeString(this.selectedAttempt2.submitTime || this.selectedAttempt2.startTime)
|
||
this.loading = true
|
||
compareUserAssessmentReport({ userId: this.userId, t1, t2, scaleId: this.selectedScale.scaleId }).then((res) => {
|
||
this.loading = false
|
||
const data = res && res.data ? res.data : null
|
||
if (!data || data.code !== 200) {
|
||
this.errorMsg = (data && data.msg) ? data.msg : '生成报告失败'
|
||
return
|
||
}
|
||
const payload = data.data || {}
|
||
const text = payload.text || ''
|
||
if (!text) {
|
||
this.errorMsg = '报告为空'
|
||
return
|
||
}
|
||
uni.setClipboardData({
|
||
data: text,
|
||
success: () => {
|
||
uni.showToast({ title: '已复制', icon: 'success' })
|
||
}
|
||
})
|
||
}).catch((e) => {
|
||
this.loading = false
|
||
this.errorMsg = e && e.message ? e.message : '网络错误'
|
||
})
|
||
},
|
||
formatTime(val) {
|
||
if (!val) return '—'
|
||
try {
|
||
const d = new Date(val)
|
||
if (isNaN(d.getTime())) return '—'
|
||
const y = d.getFullYear()
|
||
const m = String(d.getMonth() + 1).padStart(2, '0')
|
||
const day = String(d.getDate()).padStart(2, '0')
|
||
const hh = String(d.getHours()).padStart(2, '0')
|
||
const mm = String(d.getMinutes()).padStart(2, '0')
|
||
return `${y}-${m}-${day} ${hh}:${mm}`
|
||
} catch (e) {
|
||
return '—'
|
||
}
|
||
},
|
||
doCompare() {
|
||
if (!this.userId) {
|
||
this.errorMsg = '缺少 userId'
|
||
return
|
||
}
|
||
if (!this.selectedScale) {
|
||
this.errorMsg = '请选择量表'
|
||
return
|
||
}
|
||
if (!this.selectedAttempt1 || !this.selectedAttempt2) {
|
||
this.errorMsg = '请选择两条测评记录'
|
||
return
|
||
}
|
||
this.loading = true
|
||
this.errorMsg = ''
|
||
this.summaryText = ''
|
||
this.summaryLines = []
|
||
this.rows = []
|
||
const t1 = this.toDateTimeString(this.selectedAttempt1.submitTime || this.selectedAttempt1.startTime)
|
||
const t2 = this.toDateTimeString(this.selectedAttempt2.submitTime || this.selectedAttempt2.startTime)
|
||
compareUserAssessment({ userId: this.userId, t1, t2, scaleId: this.selectedScale.scaleId }).then((res) => {
|
||
this.loading = false
|
||
const data = res && res.data ? res.data : null
|
||
if (!data || data.code !== 200) {
|
||
this.errorMsg = (data && data.msg) ? data.msg : '对比失败'
|
||
return
|
||
}
|
||
const payload = data.data || {}
|
||
this.summaryLines = payload.summaryLines || []
|
||
this.summaryText = payload.summaryText || (Array.isArray(this.summaryLines) ? this.summaryLines.join(';') : '')
|
||
this.rows = payload.items || []
|
||
}).catch((e) => {
|
||
this.loading = false
|
||
this.errorMsg = e && e.message ? e.message : '网络错误'
|
||
})
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style>
|
||
.page { min-height: 100vh; padding: 24rpx 24rpx 120rpx; box-sizing: border-box; background: #F4F6FB; }
|
||
.page.big {
|
||
padding: 14rpx 14rpx 120rpx;
|
||
background-image:
|
||
radial-gradient(1100rpx 520rpx at 50% 14%, rgba(43, 107, 255, 0.30) 0%, rgba(6, 16, 40, 0.0) 65%),
|
||
linear-gradient(180deg, rgba(5, 11, 24, 0.90) 0%, rgba(8, 20, 45, 0.85) 42%, rgba(6, 16, 40, 0.92) 100%),
|
||
url('/static/bg.png');
|
||
background-size: auto, auto, cover;
|
||
background-position: center, center, center;
|
||
background-repeat: no-repeat, no-repeat, no-repeat;
|
||
}
|
||
.card { background: rgba(255, 255, 255, 0.95); border-radius: 18rpx; padding: 24rpx; border: 1px solid rgba(15, 23, 42, 0.06); margin-bottom: 18rpx; box-shadow: 0 10rpx 22rpx rgba(15, 23, 42, 0.05); }
|
||
.card-title { font-size: 28rpx; font-weight: 700; color: #111827; margin-bottom: 14rpx; }
|
||
.picker { background: rgba(15, 23, 42, 0.04); border-radius: 16rpx; padding: 18rpx; font-size: 26rpx; box-sizing: border-box; margin-bottom: 16rpx; border: 1px solid rgba(15,23,42,0.06); }
|
||
.btn { width: 100%; height: 84rpx; line-height: 84rpx; border-radius: 18rpx; font-size: 28rpx; margin-top: 12rpx; }
|
||
.btn.primary { background: #1677ff; color: #FFFFFF; }
|
||
.btn.ghost { background: #FFFFFF; color: #1F2937; border: 1px solid rgba(17,24,39,0.12); }
|
||
.kv-item { display: flex; justify-content: space-between; padding: 12rpx 0; border-bottom: 1px solid rgba(15, 23, 42, 0.06); }
|
||
.kv-item:last-child { border-bottom: 0; }
|
||
.k { color: #6B7280; font-size: 24rpx; }
|
||
.v { color: #111827; font-size: 24rpx; font-weight: 700; }
|
||
.list { margin-top: 8rpx; }
|
||
.item { background: #FFFFFF; border-radius: 20rpx; padding: 22rpx; border: 1px solid rgba(15, 23, 42, 0.06); margin-bottom: 18rpx; }
|
||
.item-head { display: flex; justify-content: space-between; align-items: center; }
|
||
.item-title { font-size: 28rpx; font-weight: 700; color: #111827; }
|
||
.pill { padding: 8rpx 14rpx; border-radius: 999rpx; font-size: 22rpx; color: #2B6BFF; background: rgba(43, 107, 255, 0.12); }
|
||
.pill.up { color: #DC2626; background: rgba(220, 38, 38, 0.12); }
|
||
.pill.down { color: #16A34A; background: rgba(22, 163, 74, 0.12); }
|
||
.item-grid { margin-top: 10rpx; display: flex; flex-wrap: wrap; justify-content: space-between; }
|
||
.item-desc { width: 48%; margin-top: 8rpx; font-size: 24rpx; color: #4B5563; }
|
||
.summary { margin-top: 10rpx; margin-bottom: 14rpx; padding: 18rpx; border-radius: 18rpx; background: rgba(43, 107, 255, 0.06); border: 1px solid rgba(43, 107, 255, 0.12); }
|
||
.summary-title { font-size: 26rpx; font-weight: 700; color: #1F2937; }
|
||
.summary-text { margin-top: 10rpx; font-size: 24rpx; color: #374151; line-height: 38rpx; }
|
||
.state { height: 360rpx; border-radius: 20rpx; background: rgba(255, 255, 255, 0.85); border: 1px solid rgba(15, 23, 42, 0.06); display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 12rpx; }
|
||
.state-text { font-size: 24rpx; color: #94A3B8; font-weight: 700; }
|
||
.error { margin-top: 14rpx; font-size: 24rpx; color: #EF4444; line-height: 36rpx; }
|
||
.page.big .card,
|
||
.page.big .item,
|
||
.page.big .state {
|
||
border: 1px solid rgba(116, 216, 255, 0.22);
|
||
background: linear-gradient(180deg, rgba(10, 18, 38, 0.75) 0%, rgba(5, 10, 22, 0.55) 100%);
|
||
box-shadow: 0 12rpx 24rpx rgba(0, 0, 0, 0.35);
|
||
}
|
||
.page.big .card-title,
|
||
.page.big .item-title,
|
||
.page.big .summary-title,
|
||
.page.big .v { color: rgba(235, 248, 255, 0.92); }
|
||
.page.big .k,
|
||
.page.big .item-desc,
|
||
.page.big .summary-text,
|
||
.page.big .state-text { color: rgba(201, 242, 255, 0.65); }
|
||
.page.big .picker { background: rgba(7, 13, 28, 0.35); border-color: rgba(116, 216, 255, 0.18); color: rgba(235, 248, 255, 0.92); }
|
||
.page.big .btn.primary { background: linear-gradient(90deg, rgba(116, 216, 255, 0.95) 0%, rgba(43, 107, 255, 0.92) 100%); color: #0b1226; font-weight: 900; }
|
||
.page.big .btn.ghost { background: rgba(7, 13, 28, 0.35); color: rgba(201, 242, 255, 0.86); border-color: rgba(116, 216, 255, 0.18); }
|
||
.page.big .summary { background: rgba(116, 216, 255, 0.10); border-color: rgba(116, 216, 255, 0.18); }
|
||
.page.big .pill { color: rgba(201, 242, 255, 0.90); background: rgba(116, 216, 255, 0.10); border: 1px solid rgba(116, 216, 255, 0.18); }
|
||
.page.big .pill.up { color: rgba(255, 72, 92, 0.92); background: rgba(255, 72, 92, 0.12); border-color: rgba(255, 72, 92, 0.22); }
|
||
.page.big .pill.down { color: rgba(34, 197, 94, 0.92); background: rgba(34, 197, 94, 0.12); border-color: rgba(34, 197, 94, 0.22); }
|
||
</style>
|