| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703 |
- <template>
- <view class="screen-container">
- <!-- 头部 -->
- <view class="screen-header">
- <text class="screen-title">心理状态自评</text>
- <text class="screen-subtitle">定期评估,了解自己的心理状态</text>
- </view>
- <!-- 量表选择 -->
- <view class="section" v-if="!currentScreen">
- <view class="scale-card" @click="startScreen('PHQ9')">
- <view class="scale-icon-wrap" style="background:#FFF0F5">
- <text class="scale-icon">🧠</text>
- </view>
- <view class="scale-info">
- <text class="scale-name">PHQ-9 抑郁筛查</text>
- <text class="scale-desc">过去两周,您是否被以下问题困扰?</text>
- </view>
- <text class="scale-arrow">></text>
- </view>
- <view class="scale-card" @click="startScreen('GAD7')">
- <view class="scale-icon-wrap" style="background:#F0F4FF">
- <text class="scale-icon">💨</text>
- </view>
- <view class="scale-info">
- <text class="scale-name">GAD-7 焦虑筛查</text>
- <text class="scale-desc">过去两周,您是否被以下问题困扰?</text>
- </view>
- <text class="scale-arrow">></text>
- </view>
- </view>
- <!-- 问卷页面 -->
- <view class="section" v-if="currentScreen">
- <view class="quiz-header">
- <text class="quiz-title">{{ currentScreen === 'PHQ9' ? 'PHQ-9 抑郁筛查' : 'GAD-7 焦虑筛查' }}</text>
- <text class="quiz-progress">{{ currentStep + 1 }}/{{ questions.length }}</text>
- </view>
- <view class="progress-track">
- <view class="progress-fill" :style="{ width: ((currentStep + 1) / questions.length * 100) + '%' }"></view>
- </view>
- <!-- 当前问题 -->
- <view class="question-card">
- <text class="question-num">Q{{ currentStep + 1 }}</text>
- <text class="question-text">{{ questions[currentStep] }}</text>
- </view>
- <!-- 选项 -->
- <view class="options-list">
- <view
- v-for="(opt, oi) in answerOptions"
- :key="oi"
- :class="['option-btn', answers[currentStep] === oi ? 'option-active' : '']"
- @click="selectAnswer(oi)">
- <view :class="['option-radio', answers[currentStep] === oi ? 'radio-checked' : '']">
- <view v-if="answers[currentStep] === oi" class="radio-dot"></view>
- </view>
- <text class="option-text">{{ opt }}</text>
- </view>
- </view>
- <!-- 导航按钮 -->
- <view class="quiz-nav">
- <button class="nav-btn prev-btn" v-if="currentStep > 0" @click="prevStep">上一题</button>
- <button class="nav-btn next-btn" v-if="currentStep < questions.length - 1" @click="nextStep">下一题</button>
- <button class="nav-btn submit-btn" v-if="currentStep === questions.length - 1" @click="submitScreen">提交问卷</button>
- </view>
- </view>
- <!-- 结果页面 -->
- <view class="section" v-if="result">
- <view class="result-header">
- <text class="result-type">{{ currentScreen === 'PHQ9' ? 'PHQ-9 抑郁筛查' : 'GAD-7 焦虑筛查' }}</text>
- <view :class="['result-score-circle', severityClass]">
- <text class="result-score">{{ result.totalScore }}</text>
- <text class="result-score-label">分</text>
- </view>
- <text :class="['result-severity', severityClass]">{{ severityLabel }}</text>
- </view>
- <!-- 自动建议 -->
- <view class="suggestion-card">
- <text class="suggestion-title">评估建议</text>
- <text class="suggestion-text">{{ result.suggestions }}</text>
- </view>
- <!-- 风险标志 -->
- <view class="risk-section" v-if="result.riskFlags && result.riskFlags.length > 0">
- <view class="risk-item" v-for="flag in result.riskFlags" :key="flag">
- <text class="risk-icon">⚠️</text>
- <text class="risk-text">{{ riskFlagLabel(flag) }}</text>
- </view>
- </view>
- <!-- 危机热线 -->
- <view class="crisis-banner" v-if="showCrisisBanner">
- <text class="crisis-title">🚨 需要帮助?</text>
- <text class="crisis-phone">全国心理危机干预热线</text>
- <text class="crisis-number">400-161-9995</text>
- <text class="crisis-hint">24小时 · 免费 · 专业</text>
- </view>
- <view class="result-actions">
- <button class="action-btn primary-btn" @click="goTrend">查看情绪趋势</button>
- <button class="action-btn secondary-btn" @click="resetScreen">再测一次</button>
- </view>
- </view>
- <!-- 提交中 / 历史记录 -->
- <view class="loading-mask" v-if="submitting">
- <view class="loading-box">
- <text class="loading-text">提交中...</text>
- </view>
- </view>
- </view>
- </template>
- <script>
- import config from '../../config'
- export default {
- data() {
- return {
- currentScreen: null, // null / 'PHQ9' / 'GAD7'
- currentStep: 0,
- answers: [],
- submitting: false,
- result: null,
- // PHQ-9 题目
- phq9Questions: [
- '做事时提不起劲或没有兴趣',
- '感到心情低落、沮丧或绝望',
- '入睡困难、睡不安稳或睡眠过多',
- '感觉疲倦或没有活力',
- '食欲不振或吃太多',
- '觉得自己很糟——或觉得自己很失败,或让自己或家人失望',
- '对事物专注有困难,例如阅读报纸或看电视时',
- '行动或说话速度缓慢到别人已经觉察?或正好相反——比平常更多话',
- '有不如死掉或用某种方式伤害自己的念头'
- ],
- // GAD-7 题目
- gad7Questions: [
- '感到紧张、焦虑或烦躁',
- '无法停止或控制担忧',
- '对各种各样的事情担忧过多',
- '很难放松下来',
- '由于不安而无法静坐',
- '变得容易烦恼或急躁',
- '感到害怕,好像可能发生可怕的事情'
- ],
- answerOptions: [
- '完全不会',
- '好几天',
- '一半以上的天数',
- '几乎每天'
- ],
- riskFlagLabels: {
- 'suicide_ideation': '存在自杀/自伤相关念头,请立即寻求帮助',
- 'core_depression_symptoms': '核心抑郁症状明显,建议关注',
- 'core_anxiety_symptoms': '核心焦虑症状明显,建议关注'
- }
- }
- },
- computed: {
- questions() {
- if (this.currentScreen === 'PHQ9') return this.phq9Questions
- if (this.currentScreen === 'GAD7') return this.gad7Questions
- return []
- },
- severityLabel() {
- if (!this.result) return ''
- var map = {
- 'none': '状态良好',
- 'mild': '轻度',
- 'moderate': '中度',
- 'moderately_severe': '中重度',
- 'severe': '重度'
- }
- return map[this.result.severityLevel] || this.result.severityLevel
- },
- severityClass() {
- if (!this.result) return ''
- var map = {
- 'none': 'severity-good',
- 'mild': 'severity-mild',
- 'moderate': 'severity-moderate',
- 'moderately_severe': 'severity-moderate',
- 'severe': 'severity-severe'
- }
- return map[this.result.severityLevel] || ''
- },
- showCrisisBanner() {
- if (!this.result || !this.result.riskFlags) return false
- return this.result.riskFlags.indexOf('suicide_ideation') !== -1
- }
- },
- onLoad() {
- // 加载历史记录(可选)
- },
- methods: {
- startScreen(type) {
- this.currentScreen = type
- this.currentStep = 0
- this.answers = []
- this.result = null
- var count = type === 'PHQ9' ? 9 : 7
- for (var i = 0; i < count; i++) {
- this.answers.push(-1)
- }
- },
- selectAnswer(oi) {
- var newAnswers = this.answers.slice()
- newAnswers[this.currentStep] = oi
- this.answers = newAnswers
- },
- nextStep() {
- if (this.answers[this.currentStep] === -1) {
- uni.showToast({ title: '请先选择一项', icon: 'none' })
- return
- }
- this.currentStep++
- },
- prevStep() {
- if (this.currentStep > 0) this.currentStep--
- },
- submitScreen() {
- // 检查所有题目是否已回答
- for (var i = 0; i < this.answers.length; i++) {
- if (this.answers[i] === -1) {
- uni.showToast({ title: '请回答所有问题', icon: 'none' })
- this.currentStep = i
- return
- }
- }
- this.submitting = true
- const memberId = uni.getStorageSync('currentChildId')
- uni.request({
- url: config.API_BASE_URL + '/api/mind/screening/submit',
- method: 'POST',
- data: {
- memberId: memberId,
- screenType: this.currentScreen,
- answers: this.answers
- },
- success: (res) => {
- this.submitting = false
- if (res.data && res.data.code === 200) {
- this.result = res.data.data
- } else {
- uni.showToast({ title: res.data && res.data.message || '提交失败', icon: 'none' })
- }
- },
- fail: () => {
- this.submitting = false
- uni.showToast({ title: '网络错误', icon: 'none' })
- }
- })
- },
- resetScreen() {
- this.currentScreen = null
- this.currentStep = 0
- this.answers = []
- this.result = null
- },
- goTrend() {
- uni.navigateTo({ url: '/pages/mind/emotion-trend' })
- },
- riskFlagLabel(flag) {
- return this.riskFlagLabels[flag] || flag
- }
- }
- }
- </script>
- <style scoped>
- .screen-container {
- min-height: 100vh;
- background: linear-gradient(180deg, #F0F4FF 0%, #FFFFFF 100%);
- padding-bottom: 40rpx;
- }
- .screen-header {
- padding: 40rpx 32rpx 20rpx;
- }
- .screen-title {
- font-size: 36rpx;
- font-weight: 700;
- color: #333;
- display: block;
- }
- .screen-subtitle {
- font-size: 26rpx;
- color: #999;
- margin-top: 8rpx;
- display: block;
- }
- .section {
- margin: 20rpx 32rpx;
- background: #fff;
- border-radius: 20rpx;
- padding: 28rpx;
- box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);
- }
- /* ===== 量表选择 ===== */
- .scale-card {
- display: flex;
- align-items: center;
- padding: 24rpx;
- background: #FAFAFA;
- border-radius: 16rpx;
- margin-bottom: 16rpx;
- gap: 20rpx;
- }
- .scale-card:active {
- background: #F0F0F0;
- }
- .scale-icon-wrap {
- width: 72rpx;
- height: 72rpx;
- border-radius: 36rpx;
- display: flex;
- align-items: center;
- justify-content: center;
- flex-shrink: 0;
- }
- .scale-icon {
- font-size: 36rpx;
- }
- .scale-info {
- flex: 1;
- }
- .scale-name {
- font-size: 28rpx;
- font-weight: 600;
- color: #333;
- display: block;
- }
- .scale-desc {
- font-size: 22rpx;
- color: #999;
- margin-top: 4rpx;
- display: block;
- }
- .scale-arrow {
- font-size: 28rpx;
- color: #ccc;
- }
- /* ===== 问卷 ===== */
- .quiz-header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- margin-bottom: 16rpx;
- }
- .quiz-title {
- font-size: 28rpx;
- font-weight: 600;
- color: #333;
- }
- .quiz-progress {
- font-size: 24rpx;
- color: #999;
- }
- .progress-track {
- height: 6rpx;
- background: #F0F0F0;
- border-radius: 3rpx;
- margin-bottom: 24rpx;
- overflow: hidden;
- }
- .progress-fill {
- height: 100%;
- background: linear-gradient(90deg, #6366F1, #8B5CF6);
- border-radius: 3rpx;
- transition: width 0.3s;
- }
- .question-card {
- background: #F8F6FF;
- border-radius: 16rpx;
- padding: 24rpx;
- margin-bottom: 24rpx;
- }
- .question-num {
- font-size: 22rpx;
- color: #8B5CF6;
- font-weight: 600;
- display: block;
- margin-bottom: 8rpx;
- }
- .question-text {
- font-size: 30rpx;
- color: #333;
- line-height: 1.5;
- }
- .options-list {
- display: flex;
- flex-direction: column;
- gap: 12rpx;
- }
- .option-btn {
- display: flex;
- align-items: center;
- padding: 20rpx 24rpx;
- border-radius: 14rpx;
- border: 2rpx solid #F0F0F0;
- gap: 16rpx;
- }
- .option-active {
- border-color: #8B5CF6;
- background: #F8F6FF;
- }
- .option-radio {
- width: 40rpx;
- height: 40rpx;
- border-radius: 20rpx;
- border: 3rpx solid #D0D0D0;
- display: flex;
- align-items: center;
- justify-content: center;
- flex-shrink: 0;
- }
- .radio-checked {
- border-color: #8B5CF6;
- }
- .radio-dot {
- width: 22rpx;
- height: 22rpx;
- border-radius: 11rpx;
- background: #8B5CF6;
- }
- .option-text {
- font-size: 28rpx;
- color: #444;
- }
- .option-active .option-text {
- color: #333;
- font-weight: 500;
- }
- .quiz-nav {
- display: flex;
- gap: 16rpx;
- margin-top: 32rpx;
- }
- .nav-btn {
- flex: 1;
- height: 80rpx;
- line-height: 80rpx;
- border-radius: 40rpx;
- font-size: 28rpx;
- text-align: center;
- }
- .prev-btn {
- background: #F0F0F0;
- color: #666;
- }
- .next-btn {
- background: #6366F1;
- color: #fff;
- }
- .submit-btn {
- background: linear-gradient(135deg, #6366F1, #8B5CF6);
- color: #fff;
- }
- /* ===== 结果 ===== */
- .result-header {
- display: flex;
- flex-direction: column;
- align-items: center;
- padding: 20rpx 0;
- }
- .result-type {
- font-size: 24rpx;
- color: #999;
- margin-bottom: 16rpx;
- }
- .result-score-circle {
- width: 120rpx;
- height: 120rpx;
- border-radius: 60rpx;
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- margin-bottom: 12rpx;
- }
- .result-score {
- font-size: 48rpx;
- font-weight: 700;
- color: #fff;
- line-height: 1;
- }
- .result-score-label {
- font-size: 20rpx;
- color: rgba(255,255,255,0.8);
- margin-top: 2rpx;
- }
- .result-severity {
- font-size: 30rpx;
- font-weight: 600;
- }
- .severity-good { background: #10B981; color: #10B981; }
- .severity-mild { background: #F59E0B; color: #F59E0B; }
- .severity-moderate { background: #FF8C42; color: #FF8C42; }
- .severity-severe { background: #EF4444; color: #EF4444; }
- .result-score-circle.severity-good { background: #10B981; }
- .result-score-circle.severity-mild { background: #F59E0B; }
- .result-score-circle.severity-moderate { background: #FF8C42; }
- .result-score-circle.severity-severe { background: #EF4444; }
- .suggestion-card {
- background: #F0FFF4;
- border-radius: 14rpx;
- padding: 20rpx;
- margin-top: 16rpx;
- }
- .suggestion-title {
- font-size: 24rpx;
- color: #10B981;
- font-weight: 600;
- display: block;
- margin-bottom: 8rpx;
- }
- .suggestion-text {
- font-size: 26rpx;
- color: #555;
- line-height: 1.6;
- }
- .risk-section {
- margin-top: 16rpx;
- }
- .risk-item {
- display: flex;
- align-items: flex-start;
- gap: 8rpx;
- padding: 12rpx 16rpx;
- background: #FFF5F5;
- border-radius: 12rpx;
- margin-bottom: 8rpx;
- }
- .risk-icon {
- font-size: 24rpx;
- flex-shrink: 0;
- }
- .risk-text {
- font-size: 24rpx;
- color: #E53E3E;
- line-height: 1.5;
- }
- .crisis-banner {
- margin-top: 16rpx;
- padding: 24rpx;
- background: linear-gradient(135deg, #FF6B6B, #E53E3E);
- border-radius: 16rpx;
- text-align: center;
- }
- .crisis-title {
- font-size: 28rpx;
- color: #fff;
- font-weight: 700;
- display: block;
- }
- .crisis-phone {
- font-size: 22rpx;
- color: rgba(255,255,255,0.8);
- margin-top: 12rpx;
- display: block;
- }
- .crisis-number {
- font-size: 40rpx;
- color: #fff;
- font-weight: 700;
- margin-top: 4rpx;
- display: block;
- letter-spacing: 2rpx;
- }
- .crisis-hint {
- font-size: 20rpx;
- color: rgba(255,255,255,0.7);
- margin-top: 4rpx;
- display: block;
- }
- .result-actions {
- display: flex;
- gap: 16rpx;
- margin-top: 28rpx;
- }
- .action-btn {
- flex: 1;
- height: 76rpx;
- line-height: 76rpx;
- border-radius: 38rpx;
- font-size: 26rpx;
- text-align: center;
- }
- .primary-btn {
- background: #FF6B9D;
- color: #fff;
- }
- .secondary-btn {
- background: #F0F0F0;
- color: #666;
- }
- .loading-mask {
- position: fixed;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- background: rgba(0, 0, 0, 0.3);
- display: flex;
- align-items: center;
- justify-content: center;
- z-index: 999;
- }
- .loading-box {
- background: #fff;
- padding: 40rpx 60rpx;
- border-radius: 20rpx;
- }
- .loading-text {
- font-size: 28rpx;
- color: #666;
- }
- </style>
|