382 lines
9.6 KiB
Vue
382 lines
9.6 KiB
Vue
<template>
|
|
<div class="emotion-analysis">
|
|
<a-card size="small" class="analysis-card">
|
|
<template #title>
|
|
<div class="card-title">
|
|
<HeartOutlined class="title-icon" />
|
|
情绪分析
|
|
</div>
|
|
</template>
|
|
|
|
<div class="analysis-content">
|
|
<!-- 主要情绪 -->
|
|
<div class="primary-emotion" v-if="analysis.primaryEmotion">
|
|
<div class="emotion-label">主要情绪</div>
|
|
<a-tag
|
|
:color="getEmotionColor(analysis.primaryEmotion)"
|
|
class="emotion-tag"
|
|
>
|
|
{{ getEmotionText(analysis.primaryEmotion) }}
|
|
</a-tag>
|
|
<div class="emotion-intensity" v-if="analysis.intensity">
|
|
强度: {{ Math.round(analysis.intensity * 100) }}%
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 情绪极性 -->
|
|
<div class="emotion-polarity" v-if="analysis.polarity">
|
|
<div class="polarity-label">情绪倾向</div>
|
|
<a-tag
|
|
:color="getPolarityColor(analysis.polarity)"
|
|
class="polarity-tag"
|
|
>
|
|
{{ getPolarityText(analysis.polarity) }}
|
|
</a-tag>
|
|
</div>
|
|
|
|
<!-- 情绪分布 -->
|
|
<div class="emotions-distribution" v-if="analysis.emotions && Object.keys(analysis.emotions).length > 0">
|
|
<div class="distribution-label">情绪分布</div>
|
|
<div class="emotion-bars">
|
|
<div
|
|
class="emotion-bar"
|
|
v-for="(value, emotion) in analysis.emotions"
|
|
:key="emotion"
|
|
>
|
|
<div class="bar-label">{{ getEmotionText(emotion) }}</div>
|
|
<div class="bar-container">
|
|
<div
|
|
class="bar-fill"
|
|
:style="{
|
|
width: `${value * 100}%`,
|
|
background: getEmotionGradient(emotion)
|
|
}"
|
|
></div>
|
|
</div>
|
|
<div class="bar-value">{{ Math.round(value * 100) }}%</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 关键词 -->
|
|
<div class="keywords" v-if="analysis.keywords && analysis.keywords.length > 0">
|
|
<div class="keywords-label">关键词</div>
|
|
<div class="keywords-list">
|
|
<a-tag
|
|
v-for="keyword in analysis.keywords"
|
|
:key="keyword"
|
|
class="keyword-tag"
|
|
>
|
|
{{ keyword }}
|
|
</a-tag>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 建议 -->
|
|
<div class="suggestion" v-if="analysis.suggestion">
|
|
<div class="suggestion-label">
|
|
<BulbOutlined class="suggestion-icon" />
|
|
建议
|
|
</div>
|
|
<div class="suggestion-content">{{ analysis.suggestion }}</div>
|
|
</div>
|
|
|
|
<!-- 置信度 -->
|
|
<div class="confidence" v-if="analysis.confidence">
|
|
<div class="confidence-label">分析置信度</div>
|
|
<a-progress
|
|
:percent="Math.round(analysis.confidence * 100)"
|
|
:stroke-color="getConfidenceColor(analysis.confidence)"
|
|
size="small"
|
|
:show-info="true"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</a-card>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { computed } from 'vue'
|
|
import { HeartOutlined, BulbOutlined } from '@ant-design/icons-vue'
|
|
|
|
const props = defineProps({
|
|
analysis: {
|
|
type: Object,
|
|
required: true,
|
|
default: () => ({})
|
|
}
|
|
})
|
|
|
|
// 情绪映射
|
|
const emotionMap = {
|
|
joy: '喜悦',
|
|
sadness: '悲伤',
|
|
anger: '愤怒',
|
|
fear: '恐惧',
|
|
surprise: '惊讶',
|
|
disgust: '厌恶',
|
|
trust: '信任',
|
|
anticipation: '期待',
|
|
anxiety: '焦虑',
|
|
depression: '抑郁',
|
|
excitement: '兴奋',
|
|
calm: '平静',
|
|
stress: '压力',
|
|
happiness: '快乐',
|
|
worry: '担忧',
|
|
relief: '放松',
|
|
frustration: '沮丧',
|
|
hope: '希望',
|
|
love: '爱',
|
|
hate: '恨'
|
|
}
|
|
|
|
// 极性映射
|
|
const polarityMap = {
|
|
positive: '积极',
|
|
negative: '消极',
|
|
neutral: '中性'
|
|
}
|
|
|
|
// 获取情绪文本
|
|
const getEmotionText = (emotion) => {
|
|
return emotionMap[emotion] || emotion
|
|
}
|
|
|
|
// 获取极性文本
|
|
const getPolarityText = (polarity) => {
|
|
return polarityMap[polarity] || polarity
|
|
}
|
|
|
|
// 获取情绪颜色
|
|
const getEmotionColor = (emotion) => {
|
|
const colorMap = {
|
|
joy: 'gold',
|
|
happiness: 'gold',
|
|
excitement: 'orange',
|
|
love: 'magenta',
|
|
trust: 'blue',
|
|
hope: 'cyan',
|
|
calm: 'green',
|
|
relief: 'green',
|
|
sadness: 'blue',
|
|
depression: 'purple',
|
|
worry: 'orange',
|
|
anxiety: 'orange',
|
|
stress: 'red',
|
|
anger: 'red',
|
|
frustration: 'red',
|
|
hate: 'red',
|
|
fear: 'volcano',
|
|
surprise: 'lime',
|
|
anticipation: 'geekblue',
|
|
disgust: 'default'
|
|
}
|
|
return colorMap[emotion] || 'default'
|
|
}
|
|
|
|
// 获取极性颜色
|
|
const getPolarityColor = (polarity) => {
|
|
const colorMap = {
|
|
positive: 'success',
|
|
negative: 'error',
|
|
neutral: 'default'
|
|
}
|
|
return colorMap[polarity] || 'default'
|
|
}
|
|
|
|
// 获取情绪渐变色
|
|
const getEmotionGradient = (emotion) => {
|
|
const gradientMap = {
|
|
joy: 'linear-gradient(90deg, #ffd700, #ffed4e)',
|
|
happiness: 'linear-gradient(90deg, #ff9a9e, #fecfef)',
|
|
excitement: 'linear-gradient(90deg, #ff6b6b, #ffa726)',
|
|
love: 'linear-gradient(90deg, #ff6b9d, #c44569)',
|
|
trust: 'linear-gradient(90deg, #4facfe, #00f2fe)',
|
|
hope: 'linear-gradient(90deg, #43e97b, #38f9d7)',
|
|
calm: 'linear-gradient(90deg, #667eea, #764ba2)',
|
|
relief: 'linear-gradient(90deg, #a8edea, #fed6e3)',
|
|
sadness: 'linear-gradient(90deg, #74b9ff, #0984e3)',
|
|
depression: 'linear-gradient(90deg, #6c5ce7, #a29bfe)',
|
|
worry: 'linear-gradient(90deg, #fdcb6e, #e17055)',
|
|
anxiety: 'linear-gradient(90deg, #fd79a8, #fdcb6e)',
|
|
stress: 'linear-gradient(90deg, #e84393, #fd79a8)',
|
|
anger: 'linear-gradient(90deg, #d63031, #e84393)',
|
|
frustration: 'linear-gradient(90deg, #e17055, #d63031)',
|
|
hate: 'linear-gradient(90deg, #2d3436, #636e72)',
|
|
fear: 'linear-gradient(90deg, #fd79a8, #fdcb6e)',
|
|
surprise: 'linear-gradient(90deg, #00b894, #00cec9)',
|
|
anticipation: 'linear-gradient(90deg, #74b9ff, #0984e3)',
|
|
disgust: 'linear-gradient(90deg, #636e72, #2d3436)'
|
|
}
|
|
return gradientMap[emotion] || 'linear-gradient(90deg, #ddd, #bbb)'
|
|
}
|
|
|
|
// 获取置信度颜色
|
|
const getConfidenceColor = (confidence) => {
|
|
if (confidence >= 0.8) return '#52c41a'
|
|
if (confidence >= 0.6) return '#faad14'
|
|
return '#ff4d4f'
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.emotion-analysis {
|
|
.analysis-card {
|
|
border-radius: var(--border-radius);
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
|
|
:deep(.ant-card-head) {
|
|
border-bottom: 1px solid var(--border-color);
|
|
padding: var(--spacing-sm) var(--spacing-md);
|
|
|
|
.ant-card-head-title {
|
|
padding: 0;
|
|
}
|
|
}
|
|
|
|
:deep(.ant-card-body) {
|
|
padding: var(--spacing-md);
|
|
}
|
|
}
|
|
|
|
.card-title {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--spacing-xs);
|
|
font-size: 14px;
|
|
font-weight: 600;
|
|
color: var(--primary-color);
|
|
|
|
.title-icon {
|
|
font-size: 16px;
|
|
}
|
|
}
|
|
|
|
.analysis-content {
|
|
.primary-emotion,
|
|
.emotion-polarity,
|
|
.emotions-distribution,
|
|
.keywords,
|
|
.suggestion,
|
|
.confidence {
|
|
margin-bottom: var(--spacing-md);
|
|
|
|
&:last-child {
|
|
margin-bottom: 0;
|
|
}
|
|
}
|
|
|
|
.emotion-label,
|
|
.polarity-label,
|
|
.distribution-label,
|
|
.keywords-label,
|
|
.suggestion-label,
|
|
.confidence-label {
|
|
font-size: 12px;
|
|
color: var(--text-secondary);
|
|
margin-bottom: var(--spacing-xs);
|
|
font-weight: 500;
|
|
}
|
|
|
|
.emotion-tag,
|
|
.polarity-tag {
|
|
font-size: 12px;
|
|
border-radius: var(--border-radius-small);
|
|
margin-right: var(--spacing-xs);
|
|
}
|
|
|
|
.emotion-intensity {
|
|
font-size: 11px;
|
|
color: var(--text-secondary);
|
|
margin-top: 2px;
|
|
}
|
|
|
|
.emotion-bars {
|
|
.emotion-bar {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--spacing-xs);
|
|
margin-bottom: var(--spacing-xs);
|
|
|
|
&:last-child {
|
|
margin-bottom: 0;
|
|
}
|
|
|
|
.bar-label {
|
|
font-size: 11px;
|
|
color: var(--text-secondary);
|
|
min-width: 40px;
|
|
text-align: right;
|
|
}
|
|
|
|
.bar-container {
|
|
flex: 1;
|
|
height: 8px;
|
|
background: var(--bg-secondary);
|
|
border-radius: 4px;
|
|
overflow: hidden;
|
|
|
|
.bar-fill {
|
|
height: 100%;
|
|
border-radius: 4px;
|
|
transition: width 0.3s ease;
|
|
}
|
|
}
|
|
|
|
.bar-value {
|
|
font-size: 11px;
|
|
color: var(--text-secondary);
|
|
min-width: 30px;
|
|
text-align: right;
|
|
}
|
|
}
|
|
}
|
|
|
|
.keywords-list {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: var(--spacing-xs);
|
|
|
|
.keyword-tag {
|
|
font-size: 11px;
|
|
border-radius: var(--border-radius-small);
|
|
background: var(--bg-secondary);
|
|
border: 1px solid var(--border-color);
|
|
color: var(--text-secondary);
|
|
}
|
|
}
|
|
|
|
.suggestion-label {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--spacing-xs);
|
|
|
|
.suggestion-icon {
|
|
font-size: 12px;
|
|
color: var(--primary-color);
|
|
}
|
|
}
|
|
|
|
.suggestion-content {
|
|
font-size: 12px;
|
|
color: var(--text-primary);
|
|
line-height: 1.5;
|
|
background: var(--bg-secondary);
|
|
padding: var(--spacing-sm);
|
|
border-radius: var(--border-radius-small);
|
|
border-left: 3px solid var(--primary-color);
|
|
}
|
|
|
|
.confidence {
|
|
:deep(.ant-progress) {
|
|
.ant-progress-text {
|
|
font-size: 11px;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
</style>
|