绑定 ESS 双轨治理,拆分超大 H5 页与 Go 引擎,抽出 membership 服务, 并将 star/fortune 重命名为 outlook(JSON 双写兼容);同时修复 /psy API 代理与首页 + 菜单层级。 Co-authored-by: Cursor <cursoragent@cursor.com>
216 lines
4.4 KiB
Vue
216 lines
4.4 KiB
Vue
<template>
|
||
<section class="self-card reveal" style="--d: 80ms" @click="$emit('open-portrait')">
|
||
<div class="self-head">
|
||
<button type="button" class="who-btn" @click.stop="$emit('open-profile')">
|
||
{{ profileLabel }}
|
||
<span class="caret" aria-hidden="true">▾</span>
|
||
</button>
|
||
<router-link class="more" to="/profile" @click.stop>更多</router-link>
|
||
</div>
|
||
|
||
<div class="self-body">
|
||
<div class="mood-col">
|
||
<div class="mood-label">
|
||
今日心情 <em>{{ mood.score }}</em><span>分</span>
|
||
</div>
|
||
<p class="mood-text">{{ mood.text }}</p>
|
||
</div>
|
||
<div class="dims" role="list">
|
||
<div
|
||
v-for="d in mood.dims"
|
||
:key="d.key"
|
||
class="dim"
|
||
role="listitem"
|
||
:style="{ '--bar': d.color, '--h': d.score + '%' }"
|
||
>
|
||
<b class="dim-score">{{ d.score }}</b>
|
||
<div class="dim-track">
|
||
<i class="dim-fill" />
|
||
</div>
|
||
<span class="dim-label">{{ d.label }}</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
defineProps<{
|
||
profileLabel: string
|
||
mood: {
|
||
score: number
|
||
text: string
|
||
dims: { key: string; label: string; score: number; color: string }[]
|
||
}
|
||
}>()
|
||
|
||
defineEmits<{
|
||
'open-portrait': []
|
||
'open-profile': []
|
||
}>()
|
||
</script>
|
||
|
||
<style scoped>
|
||
.self-card {
|
||
margin: 12px 16px 0;
|
||
position: relative;
|
||
z-index: 2;
|
||
padding: 14px 14px 16px;
|
||
background: var(--color-surface);
|
||
border-radius: var(--radius-xl);
|
||
box-shadow: var(--shadow-hero);
|
||
transition: transform var(--duration-fast) ease;
|
||
}
|
||
.self-card:active {
|
||
transform: scale(0.99);
|
||
}
|
||
.self-head {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 12px;
|
||
}
|
||
.who-btn {
|
||
border: none;
|
||
background: transparent;
|
||
font-size: 16px;
|
||
font-weight: 700;
|
||
color: var(--color-text-primary);
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 4px;
|
||
padding: 0;
|
||
font-family: var(--font-sans);
|
||
}
|
||
.caret {
|
||
font-size: 10px;
|
||
color: var(--color-text-tertiary);
|
||
}
|
||
.self-head .more {
|
||
font-size: 12px;
|
||
color: var(--color-text-tertiary);
|
||
}
|
||
.self-head .more::after {
|
||
content: ' ›';
|
||
}
|
||
.self-body {
|
||
display: flex;
|
||
gap: 10px;
|
||
align-items: stretch;
|
||
}
|
||
.mood-col {
|
||
flex: 1;
|
||
min-width: 0;
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: center;
|
||
padding-right: 4px;
|
||
}
|
||
.mood-label {
|
||
font-size: 13px;
|
||
font-weight: 600;
|
||
color: var(--color-text-primary);
|
||
line-height: 1.2;
|
||
}
|
||
.mood-label em {
|
||
font-style: normal;
|
||
font-family: var(--font-display);
|
||
font-size: 34px;
|
||
font-weight: 700;
|
||
color: var(--color-primary);
|
||
margin: 0 1px 0 4px;
|
||
letter-spacing: -0.02em;
|
||
vertical-align: -4px;
|
||
}
|
||
.mood-label span {
|
||
font-size: 12px;
|
||
color: var(--color-text-tertiary);
|
||
font-weight: 500;
|
||
}
|
||
.mood-text {
|
||
margin-top: 8px;
|
||
font-size: 12px;
|
||
color: #777;
|
||
line-height: 1.55;
|
||
display: -webkit-box;
|
||
-webkit-line-clamp: 3;
|
||
-webkit-box-orient: vertical;
|
||
overflow: hidden;
|
||
}
|
||
.dims {
|
||
display: flex;
|
||
gap: 7px;
|
||
align-items: flex-end;
|
||
flex-shrink: 0;
|
||
padding-bottom: 2px;
|
||
}
|
||
.dim {
|
||
width: 28px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
gap: 4px;
|
||
}
|
||
.dim-score {
|
||
font-size: 11px;
|
||
font-weight: 700;
|
||
color: var(--color-text-primary);
|
||
line-height: 1;
|
||
}
|
||
.dim-track {
|
||
width: 12px;
|
||
height: 64px;
|
||
border-radius: var(--radius-pill);
|
||
background: #f3ece8;
|
||
overflow: hidden;
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: flex-end;
|
||
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.04);
|
||
}
|
||
.dim-fill {
|
||
display: block;
|
||
width: 100%;
|
||
height: var(--h);
|
||
border-radius: var(--radius-pill);
|
||
background: linear-gradient(180deg, color-mix(in srgb, var(--bar) 70%, #fff) 0%, var(--bar) 100%);
|
||
box-shadow: 0 -1px 0 rgba(255, 255, 255, 0.35) inset;
|
||
animation: fillUp 0.7s cubic-bezier(0.22, 1, 0.36, 1) both;
|
||
animation-delay: calc(var(--d, 80ms) + 120ms);
|
||
}
|
||
@keyframes fillUp {
|
||
from {
|
||
height: 0;
|
||
}
|
||
to {
|
||
height: var(--h);
|
||
}
|
||
}
|
||
.dim-label {
|
||
font-size: 10px;
|
||
color: var(--color-text-secondary);
|
||
line-height: 1;
|
||
white-space: nowrap;
|
||
}
|
||
.reveal {
|
||
animation: rise 0.45s cubic-bezier(0.22, 1, 0.36, 1) both;
|
||
animation-delay: var(--d, 0ms);
|
||
}
|
||
@keyframes rise {
|
||
from {
|
||
opacity: 0;
|
||
transform: translateY(10px);
|
||
}
|
||
to {
|
||
opacity: 1;
|
||
transform: translateY(0);
|
||
}
|
||
}
|
||
@media (prefers-reduced-motion: reduce) {
|
||
.reveal,
|
||
.dim-fill {
|
||
animation: none !important;
|
||
}
|
||
}
|
||
</style>
|