|
|
@@ -9,23 +9,77 @@
|
|
|
</view>
|
|
|
</view>
|
|
|
|
|
|
- <!-- 天气选择 -->
|
|
|
+ <!-- ===== Phase 1.1: 2D 效价-激活度网格 ===== -->
|
|
|
<view class="section">
|
|
|
- <text class="section-title">今天心情怎么样?</text>
|
|
|
- <view class="weather-grid">
|
|
|
+ <text class="section-title">在方格中点击你的情绪位置</text>
|
|
|
+ <view class="affect-grid" @tap="onAffectGridTap">
|
|
|
+ <!-- 象限标签 -->
|
|
|
+ <text class="grid-label label-nw">烦躁/紧张</text>
|
|
|
+ <text class="grid-label label-ne">兴奋/愉快</text>
|
|
|
+ <text class="grid-label label-sw">低落/疲惫</text>
|
|
|
+ <text class="grid-label label-se">平静/放松</text>
|
|
|
+ <!-- 坐标轴 -->
|
|
|
+ <text class="axis-label axis-bottom">消极 ← 情绪效价 → 积极</text>
|
|
|
+ <text class="axis-label axis-left">激<br/>活<br/>度</text>
|
|
|
+ <!-- 选中的圆点 -->
|
|
|
+ <view class="affect-dot" :style="{ left: affectDotX + 'rpx', top: affectDotY + 'rpx' }">
|
|
|
+ <view class="affect-dot-inner"></view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ <view class="affect-values">
|
|
|
+ <text class="affect-val">效价: {{ affectValence }}/10</text>
|
|
|
+ <text class="affect-val">激活度: {{ affectArousal }}/10</text>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <!-- ===== Plutchik 情绪轮 ===== -->
|
|
|
+ <view class="section">
|
|
|
+ <text class="section-title">选择你的核心情绪</text>
|
|
|
+ <view class="emotion-wheel">
|
|
|
<view
|
|
|
- v-for="w in weatherOptions"
|
|
|
- :key="w.value"
|
|
|
- :class="['weather-btn', selectedWeather === w.value ? 'weather-active' : '']"
|
|
|
- :style="{ borderColor: selectedWeather === w.value ? w.color : 'transparent' }"
|
|
|
- @click="selectedWeather = w.value">
|
|
|
- <text class="weather-icon">{{ w.icon }}</text>
|
|
|
- <text class="weather-label">{{ w.label }}</text>
|
|
|
+ v-for="em in plutchikEmotions"
|
|
|
+ :key="em.type"
|
|
|
+ :class="['emotion-btn', selectedEmotionType === em.type ? 'emotion-active' : '']"
|
|
|
+ :style="{ borderColor: selectedEmotionType === em.type ? em.color : '#eee' }"
|
|
|
+ @click="selectedEmotionType = em.type">
|
|
|
+ <text class="emotion-icon">{{ em.icon }}</text>
|
|
|
+ <text class="emotion-label">{{ em.label }}</text>
|
|
|
</view>
|
|
|
</view>
|
|
|
</view>
|
|
|
|
|
|
- <!-- 情绪标签 -->
|
|
|
+ <!-- ===== 强度滑条 ===== -->
|
|
|
+ <view class="section">
|
|
|
+ <text class="section-title">情绪强度</text>
|
|
|
+ <view class="slider-row">
|
|
|
+ <text class="slider-label">弱</text>
|
|
|
+ <slider class="custom-slider" min="1" max="10" :value="intensityLevel" @change="onIntensityChange" activeColor="#FF6B9D" block-size="20" />
|
|
|
+ <text class="slider-value">{{ intensityLevel }}</text>
|
|
|
+ </view>
|
|
|
+ <view class="intensity-hint">{{ intensityHint }}</view>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <!-- ===== 精力水平 ===== -->
|
|
|
+ <view class="section">
|
|
|
+ <text class="section-title">精力水平</text>
|
|
|
+ <view class="slider-row">
|
|
|
+ <text class="slider-label">低</text>
|
|
|
+ <slider class="custom-slider" min="1" max="10" :value="energyLevel" @change="onEnergyChange" activeColor="#10B981" block-size="20" />
|
|
|
+ <text class="slider-value">{{ energyLevel }}</text>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <!-- ===== 压力水平 ===== -->
|
|
|
+ <view class="section">
|
|
|
+ <text class="section-title">压力水平</text>
|
|
|
+ <view class="slider-row">
|
|
|
+ <text class="slider-label">无压力</text>
|
|
|
+ <slider class="custom-slider" min="1" max="10" :value="stressLevel" @change="onStressChange" activeColor="#F59E0B" block-size="20" />
|
|
|
+ <text class="slider-value">{{ stressLevel }}</text>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <!-- ===== 情绪标签(精细标签) ===== -->
|
|
|
<view class="section">
|
|
|
<text class="section-title">情绪标签(可多选)</text>
|
|
|
<view class="tag-grid">
|
|
|
@@ -94,6 +148,9 @@
|
|
|
maxlength="500" />
|
|
|
</view>
|
|
|
|
|
|
+ <!-- Phase 1.2: 情绪风险告警 -->
|
|
|
+ <PsychCrisisBanner v-if="currentChildId" :childId="currentChildId" source="checkin" />
|
|
|
+
|
|
|
<!-- 提交按钮 -->
|
|
|
<view class="submit-area">
|
|
|
<button
|
|
|
@@ -117,11 +174,30 @@
|
|
|
|
|
|
<script>
|
|
|
import config from '../../config'
|
|
|
+import PsychCrisisBanner from '../../components/PsychCrisisBanner.vue'
|
|
|
|
|
|
export default {
|
|
|
+ components: { PsychCrisisBanner },
|
|
|
data() {
|
|
|
return {
|
|
|
- selectedWeather: '',
|
|
|
+ currentChildId: null,
|
|
|
+
|
|
|
+ // --- 2D 效价-激活度网格 ---
|
|
|
+ affectValence: 5, // 1-10
|
|
|
+ affectArousal: 5, // 1-10
|
|
|
+ gridWidth: 360, // rpx, 会在 onReady 中动态计算
|
|
|
+ gridHeight: 360,
|
|
|
+
|
|
|
+ // --- Plutchik 情绪轮 ---
|
|
|
+ selectedEmotionType: '',
|
|
|
+
|
|
|
+ // --- 滑条 ---
|
|
|
+ intensityLevel: 5,
|
|
|
+ energyLevel: 6,
|
|
|
+ stressLevel: 4,
|
|
|
+
|
|
|
+ // --- 旧字段保留兼容 ---
|
|
|
+ selectedWeather: 'sunny', // 自动从效价映射
|
|
|
selectedTags: [],
|
|
|
customTag: '',
|
|
|
showCustomTagInput: false,
|
|
|
@@ -141,14 +217,34 @@ export default {
|
|
|
'害怕': '#7C3AED'
|
|
|
},
|
|
|
stats: null,
|
|
|
- weatherOptions: [
|
|
|
- { value: 'sunny', icon: '\u2600\uFE0F', label: '心情很好', color: '#FFB347' },
|
|
|
- { value: 'cloudy', icon: '\u26C5', label: '一般般', color: '#A0A0A0' },
|
|
|
- { value: 'rainy', icon: '\uD83C\uDF27\uFE0F', label: '有点难过', color: '#6B9BD2' },
|
|
|
- { value: 'stormy', icon: '\u26A1', label: '很烦躁', color: '#8B5CF6' },
|
|
|
- { value: 'rainbow', icon: '\uD83C\uDF08', label: '特别开心', color: '#FF6B9D' }
|
|
|
+
|
|
|
+ // Plutchik 8 基本情绪
|
|
|
+ plutchikEmotions: [
|
|
|
+ { type: 'joy', icon: '\uD83D\uDE04', label: '开心', color: '#FFD93D' },
|
|
|
+ { type: 'trust', icon: '\uD83E\uDD1D', label: '信任', color: '#6BCB77' },
|
|
|
+ { type: 'fear', icon: '\uD83D\uDE31', label: '恐惧', color: '#9B59B6' },
|
|
|
+ { type: 'surprise', icon: '\uD83D\uDE32', label: '惊讶', color: '#FF9FF3' },
|
|
|
+ { type: 'sadness', icon: '\uD83D\uDE22', label: '悲伤', color: '#6B9BD2' },
|
|
|
+ { type: 'disgust', icon: '\uD83E\uDD2E', label: '厌恶', color: '#A0A0A0' },
|
|
|
+ { type: 'anger', icon: '\uD83D\uDE21', label: '愤怒', color: '#FF6B6B' },
|
|
|
+ { type: 'anticipation',icon: '\uD83E\uDD29', label: '期待', color: '#FF8C42' }
|
|
|
],
|
|
|
- emotionTags: ['开心', '期待', '平静', '惊喜', '难过', '生气', '紧张', '无聊', '疲惫', '害怕']
|
|
|
+
|
|
|
+ emotionTags: ['开心', '期待', '平静', '惊喜', '难过', '生气', '紧张', '无聊', '疲惫', '害怕'],
|
|
|
+
|
|
|
+ // 强度提示文案
|
|
|
+ intensityHints: {
|
|
|
+ 1: '几乎感觉不到',
|
|
|
+ 2: '很微弱',
|
|
|
+ 3: '有一点',
|
|
|
+ 4: '稍微明显',
|
|
|
+ 5: '中等强度',
|
|
|
+ 6: '比较明显',
|
|
|
+ 7: '相当强烈',
|
|
|
+ 8: '很强烈',
|
|
|
+ 9: '非常强烈',
|
|
|
+ 10: '前所未有的强烈'
|
|
|
+ }
|
|
|
}
|
|
|
},
|
|
|
|
|
|
@@ -163,14 +259,48 @@ export default {
|
|
|
},
|
|
|
|
|
|
canSubmit() {
|
|
|
- return !!this.selectedWeather
|
|
|
+ return this.affectValence > 0 && this.selectedEmotionType
|
|
|
+ },
|
|
|
+
|
|
|
+ // 圆点位置(rpx)
|
|
|
+ affectDotX() {
|
|
|
+ // valence 1-10 → 0 to gridWidth
|
|
|
+ // 留出边距: 从 20 到 gridWidth-20
|
|
|
+ const margin = 20
|
|
|
+ const usable = this.gridWidth - 2 * margin
|
|
|
+ return margin + ((this.affectValence - 1) / 9) * usable
|
|
|
+ },
|
|
|
+
|
|
|
+ affectDotY() {
|
|
|
+ // arousal 1-10 → gridHeight to 0 (Y轴反向)
|
|
|
+ const margin = 20
|
|
|
+ const usable = this.gridHeight - 2 * margin
|
|
|
+ return margin + (1 - (this.affectArousal - 1) / 9) * usable
|
|
|
+ },
|
|
|
+
|
|
|
+ intensityHint() {
|
|
|
+ return this.intensityHints[this.intensityLevel] || ''
|
|
|
}
|
|
|
},
|
|
|
|
|
|
onLoad() {
|
|
|
+ const app = getApp()
|
|
|
+ this.currentChildId = app.globalData.currentChildId || null
|
|
|
this.loadStats()
|
|
|
},
|
|
|
|
|
|
+ onReady() {
|
|
|
+ // 估算 grid 尺寸(基于屏幕宽度)
|
|
|
+ var sysInfo = uni.getSystemInfoSync()
|
|
|
+ // 页面左右margin=32rpx, section padding=28rpx
|
|
|
+ // gridWidth ≈ screenWidth - (32+28)*2 = screenWidth - 120
|
|
|
+ var screenWidth = sysInfo.windowWidth
|
|
|
+ var gridRpx = screenWidth - 120 // 估算 rpx
|
|
|
+ if (gridRpx < 300) gridRpx = 300
|
|
|
+ this.gridWidth = gridRpx
|
|
|
+ this.gridHeight = gridRpx
|
|
|
+ },
|
|
|
+
|
|
|
methods: {
|
|
|
loadStats() {
|
|
|
const app = getApp()
|
|
|
@@ -188,6 +318,95 @@ export default {
|
|
|
})
|
|
|
},
|
|
|
|
|
|
+ // ===== 2D 效价-激活度网格 =====
|
|
|
+ onAffectGridTap(e) {
|
|
|
+ // 获取点击相对位置
|
|
|
+ var rect = e.target
|
|
|
+ // 使用 touches 或 detail
|
|
|
+ var x = e.detail && e.detail.x ? e.detail.x : 0
|
|
|
+ var y = e.detail && e.detail.y ? e.detail.y : 0
|
|
|
+
|
|
|
+ // fallback: 从 pageX/pageY 计算(推荐)
|
|
|
+ if (e.detail && e.detail.x !== undefined) {
|
|
|
+ // uni-app 在 @tap 事件中 detail 有 x/y 表示相对于目标元素的偏移
|
|
|
+ x = e.detail.x
|
|
|
+ y = e.detail.y
|
|
|
+ }
|
|
|
+
|
|
|
+ // px → rpx 转换
|
|
|
+ var sysInfo = uni.getSystemInfoSync()
|
|
|
+ var scale = 750 / sysInfo.windowWidth
|
|
|
+
|
|
|
+ // 直接使用事件中的偏移(px),然后转 rpx
|
|
|
+ // 注意: 如果 e.detail 没有 x/y,尝试从 e.touches 获取
|
|
|
+ if ((x === 0 && y === 0) || !e.detail.x) {
|
|
|
+ var touch = e.touches ? e.touches[0] : null
|
|
|
+ if (touch) {
|
|
|
+ // 需要根据 grid 的位置计算相对偏移
|
|
|
+ x = touch.x || 0
|
|
|
+ y = touch.y || 0
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 直接用 rpx 计算,简化版:click 的位置相对 grid 容器
|
|
|
+ // uni-app 的 @tap 的 detail = {x, y} 是相对于被点击元素的 px 值
|
|
|
+ var pxX = e.detail.x || 0
|
|
|
+ var pxY = e.detail.y || 0
|
|
|
+ var rpxX = pxX * scale
|
|
|
+ var rpxY = pxY * scale
|
|
|
+
|
|
|
+ // 约束在有效范围内
|
|
|
+ var margin = 20
|
|
|
+ var maxRpx = this.gridWidth - margin
|
|
|
+ if (rpxX < margin) rpxX = margin
|
|
|
+ if (rpxX > maxRpx) rpxX = maxRpx
|
|
|
+ if (rpxY < margin) rpxY = margin
|
|
|
+ if (rpxY > this.gridHeight - margin) rpxY = this.gridHeight - margin
|
|
|
+
|
|
|
+ // 映射到 1-10
|
|
|
+ var usable = this.gridWidth - 2 * margin
|
|
|
+ var val = Math.round(1 + ((rpxX - margin) / usable) * 9)
|
|
|
+ var aro = Math.round(10 - ((rpxY - margin) / usable) * 9)
|
|
|
+
|
|
|
+ this.affectValence = Math.max(1, Math.min(10, val))
|
|
|
+ this.affectArousal = Math.max(1, Math.min(10, aro))
|
|
|
+
|
|
|
+ // 自动映射 moodWeather 用于向后兼容
|
|
|
+ this.updateWeatherFromAffect()
|
|
|
+ },
|
|
|
+
|
|
|
+ updateWeatherFromAffect() {
|
|
|
+ // 效价高 + 激活度高 = rainbow
|
|
|
+ if (this.affectValence >= 7 && this.affectArousal >= 7) {
|
|
|
+ this.selectedWeather = 'rainbow'
|
|
|
+ } else if (this.affectValence >= 6) {
|
|
|
+ // 效价高 = sunny
|
|
|
+ this.selectedWeather = 'sunny'
|
|
|
+ } else if (this.affectValence <= 4 && this.affectArousal >= 7) {
|
|
|
+ // 效价低 + 激活度高 = stormy
|
|
|
+ this.selectedWeather = 'stormy'
|
|
|
+ } else if (this.affectValence <= 4 && this.affectArousal <= 4) {
|
|
|
+ // 效价低 + 激活度低 = rainy
|
|
|
+ this.selectedWeather = 'rainy'
|
|
|
+ } else {
|
|
|
+ this.selectedWeather = 'cloudy'
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // ===== 滑条变化 =====
|
|
|
+ onIntensityChange(e) {
|
|
|
+ this.intensityLevel = e.detail.value
|
|
|
+ },
|
|
|
+
|
|
|
+ onEnergyChange(e) {
|
|
|
+ this.energyLevel = e.detail.value
|
|
|
+ },
|
|
|
+
|
|
|
+ onStressChange(e) {
|
|
|
+ this.stressLevel = e.detail.value
|
|
|
+ },
|
|
|
+
|
|
|
+ // ===== 情绪标签 =====
|
|
|
toggleTag(tag) {
|
|
|
const idx = this.selectedTags.indexOf(tag)
|
|
|
if (idx !== -1) {
|
|
|
@@ -206,6 +425,7 @@ export default {
|
|
|
this.showCustomTagInput = false
|
|
|
},
|
|
|
|
|
|
+ // ===== 拍照 & AI =====
|
|
|
takePhoto() {
|
|
|
uni.chooseImage({
|
|
|
count: 1,
|
|
|
@@ -220,33 +440,24 @@ export default {
|
|
|
analyzeEmotion() {
|
|
|
this.aiAnalyzing = true
|
|
|
this.aiResult = null
|
|
|
- // Simulate AI emotion analysis (in production, call a vision API)
|
|
|
var self = this
|
|
|
setTimeout(function() {
|
|
|
self.aiAnalyzing = false
|
|
|
- // Simulated result based on the selected weather/mood as hint
|
|
|
var baseEmotions = [
|
|
|
{ label: '开心', confidence: 45, color: '#10B981' },
|
|
|
{ label: '平静', confidence: 30, color: '#3B82F6' },
|
|
|
{ label: '惊喜', confidence: 15, color: '#8B5CF6' },
|
|
|
{ label: '疲惫', confidence: 10, color: '#9CA3AF' }
|
|
|
]
|
|
|
- // Adjust based on weather selection if available
|
|
|
- if (self.selectedWeather === 'rainy') {
|
|
|
+ // Adjust based on affect valence
|
|
|
+ if (self.affectValence <= 4) {
|
|
|
baseEmotions = [
|
|
|
{ label: '难过', confidence: 55, color: '#6B9BD2' },
|
|
|
{ label: '疲惫', confidence: 25, color: '#9CA3AF' },
|
|
|
{ label: '平静', confidence: 12, color: '#3B82F6' },
|
|
|
{ label: '生气', confidence: 8, color: '#EF4444' }
|
|
|
]
|
|
|
- } else if (self.selectedWeather === 'stormy') {
|
|
|
- baseEmotions = [
|
|
|
- { label: '生气', confidence: 48, color: '#EF4444' },
|
|
|
- { label: '紧张', confidence: 32, color: '#F59E0B' },
|
|
|
- { label: '害怕', confidence: 12, color: '#7C3AED' },
|
|
|
- { label: '开心', confidence: 8, color: '#10B981' }
|
|
|
- ]
|
|
|
- } else if (self.selectedWeather === 'rainbow') {
|
|
|
+ } else if (self.affectValence >= 8) {
|
|
|
baseEmotions = [
|
|
|
{ label: '开心', confidence: 60, color: '#10B981' },
|
|
|
{ label: '惊喜', confidence: 25, color: '#8B5CF6' },
|
|
|
@@ -263,12 +474,10 @@ export default {
|
|
|
var detected = []
|
|
|
for (var i = 0; i < this.aiResult.length; i++) {
|
|
|
var em = this.aiResult[i]
|
|
|
- // Only apply high-confidence emotions as tags
|
|
|
if (em.confidence >= 20 && this.emotionTags.indexOf(em.label) !== -1) {
|
|
|
detected.push(em.label)
|
|
|
}
|
|
|
}
|
|
|
- // Merge with existing selections (no duplicates)
|
|
|
var merged = this.selectedTags.slice()
|
|
|
for (var j = 0; j < detected.length; j++) {
|
|
|
if (merged.indexOf(detected[j]) === -1) {
|
|
|
@@ -279,6 +488,7 @@ export default {
|
|
|
uni.showToast({ title: '已应用情绪标签', icon: 'success' })
|
|
|
},
|
|
|
|
|
|
+ // ===== 提交 =====
|
|
|
submitCheckin() {
|
|
|
if (!this.canSubmit || this.submitting) return
|
|
|
this.submitting = true
|
|
|
@@ -286,23 +496,29 @@ export default {
|
|
|
const app = getApp()
|
|
|
const childId = app.globalData.currentChildId
|
|
|
|
|
|
- // Upload photo first if exists, then submit
|
|
|
const doSubmit = (photoUrl) => {
|
|
|
uni.request({
|
|
|
url: config.API_BASE_URL + '/api/mind/checkin/create',
|
|
|
method: 'POST',
|
|
|
data: {
|
|
|
childId: childId,
|
|
|
+ // 旧字段向后兼容
|
|
|
moodWeather: this.selectedWeather,
|
|
|
emotionTags: JSON.stringify(this.selectedTags),
|
|
|
photoUrl: photoUrl || '',
|
|
|
- note: this.note
|
|
|
+ note: this.note,
|
|
|
+ // Phase 1.1 精细情绪字段
|
|
|
+ moodScore: this.affectValence,
|
|
|
+ emotionType: this.selectedEmotionType,
|
|
|
+ stressLevel: this.stressLevel,
|
|
|
+ arousalLevel: this.affectArousal,
|
|
|
+ energyLevel: this.energyLevel
|
|
|
},
|
|
|
success: (res) => {
|
|
|
this.submitting = false
|
|
|
if (res.data && res.data.code === 200) {
|
|
|
uni.showToast({
|
|
|
- title: '+5 \u5FC3\u80FD\u91CF',
|
|
|
+ title: '+5 心能量',
|
|
|
icon: 'none',
|
|
|
duration: 2000
|
|
|
})
|
|
|
@@ -400,39 +616,182 @@ export default {
|
|
|
display: block;
|
|
|
}
|
|
|
|
|
|
-.weather-grid {
|
|
|
+/* ===== 2D 效价-激活度网格 ===== */
|
|
|
+.affect-grid {
|
|
|
+ position: relative;
|
|
|
+ width: 100%;
|
|
|
+ /* height is set dynamically equal to width via aspect-ratio or inline style */
|
|
|
+ aspect-ratio: 1;
|
|
|
+ background: linear-gradient(135deg, #FFF5F5 0%, #FFF0FF 25%, #F0F0FF 50%, #F0FFF4 75%, #FFFFF0 100%);
|
|
|
+ border-radius: 16rpx;
|
|
|
+ border: 2rpx solid #E8E8E8;
|
|
|
+ margin-bottom: 16rpx;
|
|
|
+ /* Ensure the grid has a defined height — fallback */
|
|
|
+ min-height: 300rpx;
|
|
|
+ /* Overlap grid lines for visual clarity */
|
|
|
+ overflow: hidden;
|
|
|
+}
|
|
|
+
|
|
|
+/* 网格线:用伪元素或背景渐变 */
|
|
|
+.affect-grid::before {
|
|
|
+ content: '';
|
|
|
+ position: absolute;
|
|
|
+ left: 50%;
|
|
|
+ top: 0;
|
|
|
+ bottom: 0;
|
|
|
+ width: 2rpx;
|
|
|
+ background: rgba(0,0,0,0.06);
|
|
|
+ transform: translateX(-50%);
|
|
|
+ pointer-events: none;
|
|
|
+}
|
|
|
+
|
|
|
+.affect-grid::after {
|
|
|
+ content: '';
|
|
|
+ position: absolute;
|
|
|
+ top: 50%;
|
|
|
+ left: 0;
|
|
|
+ right: 0;
|
|
|
+ height: 2rpx;
|
|
|
+ background: rgba(0,0,0,0.06);
|
|
|
+ transform: translateY(-50%);
|
|
|
+ pointer-events: none;
|
|
|
+}
|
|
|
+
|
|
|
+.grid-label {
|
|
|
+ position: absolute;
|
|
|
+ font-size: 22rpx;
|
|
|
+ color: rgba(0,0,0,0.25);
|
|
|
+ pointer-events: none;
|
|
|
+}
|
|
|
+
|
|
|
+.label-nw { top: 20rpx; left: 20rpx; }
|
|
|
+.label-ne { top: 20rpx; right: 20rpx; }
|
|
|
+.label-sw { bottom: 20rpx; left: 20rpx; }
|
|
|
+.label-se { bottom: 20rpx; right: 20rpx; }
|
|
|
+
|
|
|
+.axis-label {
|
|
|
+ position: absolute;
|
|
|
+ font-size: 20rpx;
|
|
|
+ color: rgba(0,0,0,0.2);
|
|
|
+ pointer-events: none;
|
|
|
+}
|
|
|
+
|
|
|
+.axis-bottom {
|
|
|
+ bottom: 4rpx;
|
|
|
+ left: 50%;
|
|
|
+ transform: translateX(-50%);
|
|
|
+ white-space: nowrap;
|
|
|
+}
|
|
|
+
|
|
|
+.axis-left {
|
|
|
+ left: 4rpx;
|
|
|
+ top: 50%;
|
|
|
+ transform: translateY(-50%);
|
|
|
+ font-size: 18rpx;
|
|
|
+ line-height: 1.4;
|
|
|
+ text-align: center;
|
|
|
+}
|
|
|
+
|
|
|
+.affect-dot {
|
|
|
+ position: absolute;
|
|
|
+ width: 44rpx;
|
|
|
+ height: 44rpx;
|
|
|
+ margin-left: -22rpx;
|
|
|
+ margin-top: -22rpx;
|
|
|
+ pointer-events: none;
|
|
|
+ z-index: 2;
|
|
|
+}
|
|
|
+
|
|
|
+.affect-dot-inner {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ border-radius: 50%;
|
|
|
+ background: #FF6B9D;
|
|
|
+ box-shadow: 0 0 12rpx rgba(255, 107, 157, 0.5);
|
|
|
+ border: 4rpx solid #fff;
|
|
|
+ box-sizing: border-box;
|
|
|
+}
|
|
|
+
|
|
|
+.affect-values {
|
|
|
+ display: flex;
|
|
|
+ justify-content: center;
|
|
|
+ gap: 40rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.affect-val {
|
|
|
+ font-size: 24rpx;
|
|
|
+ color: #888;
|
|
|
+}
|
|
|
+
|
|
|
+/* ===== Plutchik 情绪轮 ===== */
|
|
|
+.emotion-wheel {
|
|
|
display: flex;
|
|
|
flex-wrap: wrap;
|
|
|
gap: 16rpx;
|
|
|
justify-content: center;
|
|
|
}
|
|
|
|
|
|
-.weather-btn {
|
|
|
+.emotion-btn {
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
align-items: center;
|
|
|
- width: 120rpx;
|
|
|
+ width: 130rpx;
|
|
|
padding: 16rpx 8rpx;
|
|
|
- border-radius: 16rpx;
|
|
|
- border: 3rpx solid transparent;
|
|
|
+ border-radius: 20rpx;
|
|
|
+ border: 3rpx solid #eee;
|
|
|
background: #FAFAFA;
|
|
|
transition: all 0.2s;
|
|
|
}
|
|
|
|
|
|
-.weather-active {
|
|
|
+.emotion-active {
|
|
|
background: #FFF0F5;
|
|
|
+ transform: scale(1.05);
|
|
|
+}
|
|
|
+
|
|
|
+.emotion-icon {
|
|
|
+ font-size: 48rpx;
|
|
|
+ margin-bottom: 6rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.emotion-label {
|
|
|
+ font-size: 24rpx;
|
|
|
+ color: #555;
|
|
|
}
|
|
|
|
|
|
-.weather-icon {
|
|
|
- font-size: 44rpx;
|
|
|
- margin-bottom: 8rpx;
|
|
|
+/* ===== 滑条 ===== */
|
|
|
+.slider-row {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 16rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.slider-label {
|
|
|
+ font-size: 24rpx;
|
|
|
+ color: #999;
|
|
|
+ white-space: nowrap;
|
|
|
+ width: 64rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.custom-slider {
|
|
|
+ flex: 1;
|
|
|
}
|
|
|
|
|
|
-.weather-label {
|
|
|
+.slider-value {
|
|
|
+ font-size: 28rpx;
|
|
|
+ font-weight: 600;
|
|
|
+ color: #FF6B9D;
|
|
|
+ width: 40rpx;
|
|
|
+ text-align: center;
|
|
|
+}
|
|
|
+
|
|
|
+.intensity-hint {
|
|
|
font-size: 22rpx;
|
|
|
- color: #666;
|
|
|
+ color: #aaa;
|
|
|
+ text-align: center;
|
|
|
+ margin-top: 4rpx;
|
|
|
}
|
|
|
|
|
|
+/* ===== 情绪标签 ===== */
|
|
|
.tag-grid {
|
|
|
display: flex;
|
|
|
flex-wrap: wrap;
|
|
|
@@ -485,6 +844,7 @@ export default {
|
|
|
font-size: 26rpx;
|
|
|
}
|
|
|
|
|
|
+/* ===== 拍照 ===== */
|
|
|
.photo-area {
|
|
|
width: 200rpx;
|
|
|
height: 200rpx;
|
|
|
@@ -534,53 +894,64 @@ export default {
|
|
|
border-radius: 16rpx;
|
|
|
cursor: pointer;
|
|
|
}
|
|
|
+
|
|
|
.ai-btn-icon {
|
|
|
font-size: 32rpx;
|
|
|
margin-right: 10rpx;
|
|
|
}
|
|
|
+
|
|
|
.ai-btn-text {
|
|
|
font-size: 26rpx;
|
|
|
color: #fff;
|
|
|
font-weight: 500;
|
|
|
}
|
|
|
+
|
|
|
.ai-recognize-btn:active {
|
|
|
opacity: 0.8;
|
|
|
}
|
|
|
+
|
|
|
.ai-analyzing {
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
justify-content: center;
|
|
|
padding: 30rpx 0;
|
|
|
}
|
|
|
+
|
|
|
.ai-loading-text {
|
|
|
font-size: 26rpx;
|
|
|
color: #8B5CF6;
|
|
|
}
|
|
|
+
|
|
|
.ai-result {
|
|
|
margin-top: 16rpx;
|
|
|
}
|
|
|
+
|
|
|
.ai-result-title {
|
|
|
font-size: 24rpx;
|
|
|
color: #666;
|
|
|
margin-bottom: 12rpx;
|
|
|
display: block;
|
|
|
}
|
|
|
+
|
|
|
.ai-emotion-list {
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
gap: 12rpx;
|
|
|
}
|
|
|
+
|
|
|
.ai-emotion-item {
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
gap: 12rpx;
|
|
|
}
|
|
|
+
|
|
|
.ai-emotion-label {
|
|
|
font-size: 24rpx;
|
|
|
color: #333;
|
|
|
width: 64rpx;
|
|
|
flex-shrink: 0;
|
|
|
}
|
|
|
+
|
|
|
.ai-emotion-bar-track {
|
|
|
flex: 1;
|
|
|
height: 16rpx;
|
|
|
@@ -588,17 +959,20 @@ export default {
|
|
|
border-radius: 8rpx;
|
|
|
overflow: hidden;
|
|
|
}
|
|
|
+
|
|
|
.ai-emotion-bar-fill {
|
|
|
height: 100%;
|
|
|
border-radius: 8rpx;
|
|
|
transition: width 0.5s;
|
|
|
}
|
|
|
+
|
|
|
.ai-emotion-value {
|
|
|
font-size: 22rpx;
|
|
|
color: #999;
|
|
|
width: 56rpx;
|
|
|
text-align: right;
|
|
|
}
|
|
|
+
|
|
|
.ai-apply-tags {
|
|
|
margin-top: 20rpx;
|
|
|
padding: 16rpx;
|
|
|
@@ -607,11 +981,13 @@ export default {
|
|
|
text-align: center;
|
|
|
cursor: pointer;
|
|
|
}
|
|
|
+
|
|
|
.ai-apply-text {
|
|
|
font-size: 26rpx;
|
|
|
color: #8B5CF6;
|
|
|
font-weight: 500;
|
|
|
}
|
|
|
+
|
|
|
.ai-apply-tags:active {
|
|
|
opacity: 0.7;
|
|
|
}
|