feat: 按测测功能重构 H5 首页与各子页,并修正顶栏「+」添加档案菜单
对齐测测交互:首页「+」支持邀请填档案/添加档案/邀请合盘;各 Tab 与工具页按同一视觉与功能标准落地;本地对照截图目录显式忽略。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,50 +1,107 @@
|
||||
<template>
|
||||
<main class="yxg-page">
|
||||
<div class="yxg-hero">
|
||||
<BackButton />
|
||||
<PageTitle feature="star" title="星座" :sub="needBirth && !report ? '填写生日(可选出生时/地),生成星盘与运势' : undefined" />
|
||||
</div>
|
||||
<div class="yxg-sheet sheet-pad">
|
||||
<PageShell>
|
||||
<template #hero>
|
||||
<div class="top-row">
|
||||
<BackButton />
|
||||
<button v-if="report" type="button" class="who" @click="resetForm">
|
||||
自己 <span aria-hidden="true">▾</span>
|
||||
</button>
|
||||
<span v-else class="who-static">星座</span>
|
||||
<button v-if="report" type="button" class="share-ico" aria-label="分享" @click="shareOpen = true">↗</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="body">
|
||||
<template v-if="needBirth && !report">
|
||||
<div class="yxg-card">
|
||||
<div class="intro-card">
|
||||
<HomeToolIcon name="star" :size="64" />
|
||||
<h1 class="intro-title">星座性格</h1>
|
||||
<p class="intro-sub">填写生日,看见太阳 · 月亮 · 上升与行星画像</p>
|
||||
</div>
|
||||
<div class="yxg-card form-card">
|
||||
<label class="yxg-label">生日 / 出生时(可选)</label>
|
||||
<div class="birth-row">
|
||||
<BirthDateInputs v-model:year="year" v-model:month="month" v-model:day="day" compact />
|
||||
<input class="bd-time" v-model="birthTime" type="time" aria-label="出生时" />
|
||||
<span class="bd-sep">时</span>
|
||||
</div>
|
||||
<label class="yxg-label">出生地(可选,省 / 市 / 区县)</label>
|
||||
<label class="yxg-label">出生地(可选)</label>
|
||||
<BirthPlaceSelect v-model="birthPlace" />
|
||||
<button class="yxg-btn yxg-btn-block" type="button" :disabled="loading" @click="start">生成星座</button>
|
||||
<button class="cta" type="button" :disabled="loading" @click="start">
|
||||
{{ loading ? '生成中…' : '生成我的星座' }}
|
||||
</button>
|
||||
<p v-if="error" class="yxg-err">{{ error }}</p>
|
||||
</div>
|
||||
</template>
|
||||
<p v-else-if="loading" class="yxg-hint">正在生成…</p>
|
||||
<p v-else-if="error" class="yxg-err">
|
||||
|
||||
<p v-else-if="loading" class="yxg-hint">正在生成星盘…</p>
|
||||
<p v-else-if="error && !report" class="yxg-err">
|
||||
{{ error }}
|
||||
<button type="button" class="yxg-link" @click="load">重试</button>
|
||||
</p>
|
||||
<template v-else-if="report">
|
||||
<p class="yxg-meta head">{{ headline }}</p>
|
||||
<p v-if="oneLiner" class="yxg-lead">{{ oneLiner }}</p>
|
||||
<p v-if="chartNote" class="yxg-meta">{{ chartNote }}</p>
|
||||
|
||||
<div class="panel-tabs" role="tablist">
|
||||
<template v-else-if="report">
|
||||
<div class="mode-tabs" role="tablist">
|
||||
<button
|
||||
v-for="p in panels"
|
||||
v-for="p in viewTabs"
|
||||
:key="p.key"
|
||||
type="button"
|
||||
class="panel-tab"
|
||||
:class="{ on: panel === p.key }"
|
||||
@click="panel = p.key"
|
||||
class="mode-tab"
|
||||
:class="{ on: view === p.key }"
|
||||
@click="view = p.key"
|
||||
>
|
||||
{{ p.label }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<template v-if="panel === 'chart'">
|
||||
<template v-if="view === 'overview'">
|
||||
<h2 class="hl">{{ headline || '我的星座画像' }}</h2>
|
||||
<p v-if="oneLiner" class="lead">{{ oneLiner }}</p>
|
||||
|
||||
<div class="spotlight">
|
||||
<div class="sp-head">
|
||||
<strong>星象速览</strong>
|
||||
<router-link class="sp-ai" to="/ask">AI 解读 ›</router-link>
|
||||
</div>
|
||||
<p class="sp-text">{{ chartNote || '行星落在不同星座,构成独特的你。' }}</p>
|
||||
<div v-if="keywords.length" class="tags">
|
||||
<span v-for="k in keywords.slice(0, 4)" :key="k" class="tag">{{ k }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="planet-grid">
|
||||
<button
|
||||
v-for="(cell, i) in gridCells"
|
||||
:key="cell.key + i"
|
||||
type="button"
|
||||
class="cell"
|
||||
:class="{ center: cell.center, on: signTab === cell.key }"
|
||||
@click="onGridTap(cell)"
|
||||
>
|
||||
<template v-if="cell.center">
|
||||
<HomeToolIcon name="star" :size="40" />
|
||||
</template>
|
||||
<template v-else>
|
||||
<strong class="nick">{{ cell.nick }}</strong>
|
||||
<span class="role">{{ cell.role }}</span>
|
||||
</template>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div v-if="activeCard" class="detail-card">
|
||||
<h3>{{ activeCard.title }} · {{ activeCard.label }}</h3>
|
||||
<p>{{ activeCard.teaser }}</p>
|
||||
<p v-if="activeCard.element" class="yxg-meta">{{ activeCard.element }}象 · {{ activeCard.modality }}</p>
|
||||
</div>
|
||||
|
||||
<router-link class="ask-bar" to="/ask">
|
||||
<span>AI,本周我可以关注什么?</span>
|
||||
<span aria-hidden="true">›</span>
|
||||
</router-link>
|
||||
</template>
|
||||
|
||||
<template v-else-if="view === 'chart'">
|
||||
<div class="wheel-settings">
|
||||
<label><input v-model="showOuter" type="checkbox" /> 显示外行星</label>
|
||||
<label><input v-model="showOuter" type="checkbox" /> 外行星</label>
|
||||
<label><input v-model="tightOrb" type="checkbox" /> 紧容许度</label>
|
||||
</div>
|
||||
<NatalWheel
|
||||
@@ -56,35 +113,19 @@
|
||||
:tight-orb="tightOrb"
|
||||
@select="onWheelSelect"
|
||||
/>
|
||||
<SignCards v-model="signTab" :cards="signCards" />
|
||||
<div v-if="activeCard" class="yxg-card soft tab-body">
|
||||
<h2 class="card-title">{{ activeCard.title }} · {{ activeCard.label }}</h2>
|
||||
<p>{{ activeCard.teaser }}</p>
|
||||
<p v-if="activeCard.element" class="yxg-meta">{{ activeCard.element }}象 · {{ activeCard.modality }}</p>
|
||||
</div>
|
||||
<div v-if="houses.length" class="yxg-card soft">
|
||||
<h2 class="card-title">宫位</h2>
|
||||
<p class="house-row" v-for="h in houses" :key="h.num">第{{ h.num }}宫 · {{ h.sign }}</p>
|
||||
</div>
|
||||
<div v-if="aspectPreview.length" class="yxg-card soft">
|
||||
<h2 class="card-title">相位速览</h2>
|
||||
<p v-for="(a, i) in aspectPreview" :key="i" class="aspect-line">{{ a.label || `${a.a_title}${a.type}${a.b_title}` }}</p>
|
||||
<p v-if="!report.has_deep_access" class="yxg-meta">完整相位表见深度版</p>
|
||||
<ul v-else class="aspect-full">
|
||||
<li v-for="(a, i) in fullAspects" :key="'f' + i">{{ a.label }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div v-if="transits.length" class="yxg-card soft">
|
||||
<h2 class="card-title">今日行运</h2>
|
||||
<div v-for="t in transits" :key="t.key" class="transit">
|
||||
<strong>{{ t.title }}</strong>
|
||||
<span class="yxg-meta">{{ t.aspect }}</span>
|
||||
<p>{{ t.tip }}</p>
|
||||
</div>
|
||||
<p v-for="(a, i) in aspectPreview" :key="i" class="aspect-line">
|
||||
{{ a.label || `${a.a_title}${a.type}${a.b_title}` }}
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template v-else-if="panel === 'fortune'">
|
||||
<template v-else-if="view === 'fortune'">
|
||||
<div class="fortune-tabs">
|
||||
<button
|
||||
v-for="k in fortuneKeys"
|
||||
@@ -97,7 +138,7 @@
|
||||
{{ fortuneLabel(k) }}
|
||||
</button>
|
||||
</div>
|
||||
<div v-if="activeFortune" class="yxg-card daily">
|
||||
<div v-if="activeFortune" class="fortune-card">
|
||||
<p class="daily-title">
|
||||
{{ activeFortune.title }} · {{ activeFortune.label }} · {{ activeFortune.score }}分
|
||||
</p>
|
||||
@@ -116,31 +157,30 @@
|
||||
|
||||
<template v-else>
|
||||
<ul class="planet-list">
|
||||
<li v-for="p in planets" :key="p.key" class="yxg-card soft planet">
|
||||
<li v-for="p in planets" :key="p.key" class="planet-row" @click="signTab = p.key; view = 'overview'">
|
||||
<strong>{{ p.title }}</strong>
|
||||
<span>{{ p.sign }} {{ p.degree }} · 第{{ p.house }}宫</span>
|
||||
<span class="yxg-meta">{{ p.element }}象 · {{ p.modality }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
<ReportRich :summary="summary" :detail="detail" :deep="!!report.has_deep_access" />
|
||||
<div v-if="!report.has_deep_access" class="yxg-card lock">
|
||||
<div v-if="!report.has_deep_access" class="lock-card">
|
||||
<p>完整相位与年运详解可在深度版或成长会员中查看。</p>
|
||||
<button class="yxg-btn" type="button" :disabled="paying" @click="buyDeep">解锁完整分析(模拟支付)</button>
|
||||
<button class="cta soft" type="button" :disabled="paying" @click="buyDeep">解锁完整分析</button>
|
||||
</div>
|
||||
<div class="links">
|
||||
<router-link class="yxg-link-plain" to="/synastry">去做合盘(比较盘)→</router-link>
|
||||
<router-link class="yxg-link-plain" to="/relation">去做人格匹配 →</router-link>
|
||||
<router-link class="yxg-link-plain" to="/ask">去问答聊聊星座 →</router-link>
|
||||
<router-link v-if="report.id" class="yxg-link-plain" :to="`/reports/${report.id}`">在报告页打开 →</router-link>
|
||||
|
||||
<div class="bottom-links">
|
||||
<router-link to="/synastry">合盘 ›</router-link>
|
||||
<router-link to="/relation">人格匹配 ›</router-link>
|
||||
<router-link v-if="report.id" :to="`/reports/${report.id}`">报告 ›</router-link>
|
||||
</div>
|
||||
<button type="button" class="yxg-btn yxg-btn-ghost share-btn" @click="shareOpen = true">生成分享卡</button>
|
||||
</template>
|
||||
|
||||
<p class="yxg-disc">运势与星盘为自我探索参考,请结合现实判断。</p>
|
||||
</div>
|
||||
<ShareSheet :open="shareOpen" :payload="sharePayload" @close="shareOpen = false" />
|
||||
</main>
|
||||
</PageShell>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
@@ -152,11 +192,12 @@ import { api } from '../api/client'
|
||||
import BackButton from '../components/BackButton.vue'
|
||||
import BirthDateInputs from '../components/BirthDateInputs.vue'
|
||||
import BirthPlaceSelect from '../components/BirthPlaceSelect.vue'
|
||||
import HomeToolIcon from '../components/HomeToolIcon.vue'
|
||||
import NatalWheel, { type WheelAspect, type WheelPlanet } from '../components/NatalWheel.vue'
|
||||
import PageTitle from '../components/PageTitle.vue'
|
||||
import PageShell from '../components/PageShell.vue'
|
||||
import ReportRich from '../components/ReportRich.vue'
|
||||
import ShareSheet from '../components/ShareSheet.vue'
|
||||
import SignCards, { type SignCard } from '../components/SignCards.vue'
|
||||
import type { SignCard } from '../components/SignCards.vue'
|
||||
import { AnalyticsEvent, track } from '../lib/analytics'
|
||||
import type { PortraitSharePayload } from '../lib/shareLink'
|
||||
|
||||
@@ -174,12 +215,20 @@ const month = ref(String(route.query.m || ''))
|
||||
const day = ref(String(route.query.d || ''))
|
||||
const birthTime = ref('')
|
||||
const birthPlace = ref('')
|
||||
const panel = ref<'chart' | 'fortune' | 'planets'>('chart')
|
||||
const view = ref<'overview' | 'chart' | 'fortune' | 'planets'>('overview')
|
||||
const signTab = ref('sun')
|
||||
const fortuneKey = ref<'daily' | 'weekly' | 'monthly' | 'yearly' | 'lifetime'>('daily')
|
||||
const showOuter = ref(true)
|
||||
const tightOrb = ref(false)
|
||||
|
||||
const viewTabs = [
|
||||
{ key: 'overview' as const, label: '概览' },
|
||||
{ key: 'chart' as const, label: '星盘' },
|
||||
{ key: 'fortune' as const, label: '运势' },
|
||||
{ key: 'planets' as const, label: '行星' },
|
||||
]
|
||||
const fortuneKeys = ['daily', 'weekly', 'monthly', 'yearly', 'lifetime'] as const
|
||||
|
||||
try {
|
||||
const raw = localStorage.getItem(SETTINGS_KEY)
|
||||
if (raw) {
|
||||
@@ -194,13 +243,6 @@ watch([showOuter, tightOrb], () => {
|
||||
localStorage.setItem(SETTINGS_KEY, JSON.stringify({ showOuter: showOuter.value, tightOrb: tightOrb.value }))
|
||||
})
|
||||
|
||||
const panels = [
|
||||
{ key: 'chart' as const, label: '星盘' },
|
||||
{ key: 'fortune' as const, label: '运势' },
|
||||
{ key: 'planets' as const, label: '行星' },
|
||||
]
|
||||
const fortuneKeys = ['daily', 'weekly', 'monthly', 'yearly', 'lifetime'] as const
|
||||
|
||||
type FortunePeriod = {
|
||||
title?: string
|
||||
label?: string
|
||||
@@ -213,7 +255,6 @@ type FortunePeriod = {
|
||||
}
|
||||
type PlanetRow = WheelPlanet & { element?: string; modality?: string }
|
||||
type AspectRow = WheelAspect & { a_title?: string; b_title?: string; label?: string }
|
||||
type TransitRow = { key: string; title: string; aspect: string; tip: string }
|
||||
type HouseRow = { num: number; sign: string }
|
||||
|
||||
const summary = computed(() => (report.value?.summary || {}) as Record<string, unknown>)
|
||||
@@ -221,7 +262,9 @@ const detail = computed(() => (report.value?.detail || null) as Record<string, u
|
||||
const headline = computed(() => String(summary.value.headline || ''))
|
||||
const oneLiner = computed(() => String(summary.value.one_liner || ''))
|
||||
const keywords = computed(() => (Array.isArray(summary.value.keywords) ? (summary.value.keywords as string[]) : []))
|
||||
const chartObj = computed(() => (summary.value.chart && typeof summary.value.chart === 'object' ? (summary.value.chart as Record<string, unknown>) : {}))
|
||||
const chartObj = computed(() =>
|
||||
summary.value.chart && typeof summary.value.chart === 'object' ? (summary.value.chart as Record<string, unknown>) : {},
|
||||
)
|
||||
const chartNote = computed(() => String(chartObj.value.note || ''))
|
||||
const ascLon = computed(() => (typeof chartObj.value.asc_lon === 'number' ? chartObj.value.asc_lon : null))
|
||||
const houses = computed(() => (Array.isArray(chartObj.value.houses) ? (chartObj.value.houses as HouseRow[]) : []))
|
||||
@@ -263,13 +306,39 @@ const aspectLines = computed<WheelAspect[]>(() => {
|
||||
const src = report.value?.has_deep_access ? fullAspects.value : aspectPreview.value
|
||||
return src.map((a) => ({ a: a.a, b: a.b, type: a.type, orb: a.orb, label: a.label }))
|
||||
})
|
||||
const transits = computed(() => {
|
||||
const fromSummary = summary.value.transits
|
||||
if (Array.isArray(fromSummary)) return fromSummary as TransitRow[]
|
||||
const f = fortuneBundle.value as Record<string, unknown>
|
||||
if (Array.isArray(f.transits)) return f.transits as TransitRow[]
|
||||
return []
|
||||
|
||||
const gridCells = computed(() => {
|
||||
const cards = signCards.value
|
||||
const slots: { key: string; nick: string; role: string; center?: boolean }[] = []
|
||||
const order = ['sun', 'moon', 'rise', 'venus', 'mars', 'mercury', 'north_node', 'juno', 'jupiter']
|
||||
const byKey = new Map(cards.map((c) => [c.key, c]))
|
||||
const planetByKey = new Map(planets.value.map((p) => [p.key, p]))
|
||||
for (let i = 0; i < 9; i++) {
|
||||
if (i === 4) {
|
||||
slots.push({ key: '_center', nick: '', role: '', center: true })
|
||||
continue
|
||||
}
|
||||
const key = order[i < 4 ? i : i - 1] || cards[i]?.key || `p${i}`
|
||||
const c = byKey.get(key)
|
||||
const p = planetByKey.get(key)
|
||||
if (c) {
|
||||
slots.push({
|
||||
key: c.key,
|
||||
nick: (c.teaser || c.label || c.title).slice(0, 6),
|
||||
role: `${c.title}${c.label}`,
|
||||
})
|
||||
} else if (p) {
|
||||
slots.push({ key: p.key, nick: p.sign, role: `${p.title}${p.sign}` })
|
||||
} else if (cards[i < 4 ? i : i - 1]) {
|
||||
const x = cards[i < 4 ? i : i - 1]
|
||||
slots.push({ key: x.key, nick: (x.teaser || x.label).slice(0, 6), role: `${x.title}${x.label}` })
|
||||
} else {
|
||||
slots.push({ key: `empty${i}`, nick: '—', role: '待生成' })
|
||||
}
|
||||
}
|
||||
return slots
|
||||
})
|
||||
|
||||
const sharePayload = computed<PortraitSharePayload | null>(() => {
|
||||
if (!report.value) return null
|
||||
return { type: 'portrait', title: headline.value || '我的星座', line: oneLiner.value, keywords: keywords.value }
|
||||
@@ -284,6 +353,20 @@ function onWheelSelect(key: string) {
|
||||
track(AnalyticsEvent.StarWheelViewed, { planet: key })
|
||||
}
|
||||
|
||||
function onGridTap(cell: { key: string; center?: boolean }) {
|
||||
if (cell.center) {
|
||||
view.value = 'chart'
|
||||
return
|
||||
}
|
||||
if (cell.key.startsWith('empty')) return
|
||||
signTab.value = cell.key
|
||||
}
|
||||
|
||||
function resetForm() {
|
||||
needBirth.value = true
|
||||
report.value = null
|
||||
}
|
||||
|
||||
async function generate(y: number, m: number, d: number) {
|
||||
loading.value = true
|
||||
error.value = ''
|
||||
@@ -299,6 +382,7 @@ async function generate(y: number, m: number, d: number) {
|
||||
birth_place: birthPlace.value || undefined,
|
||||
})
|
||||
report.value = await api.createStar(profile.id)
|
||||
view.value = 'overview'
|
||||
track(AnalyticsEvent.PortraitCompleted, { source: 'star' })
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : '加载失败'
|
||||
@@ -327,8 +411,10 @@ async function load() {
|
||||
needBirth.value = false
|
||||
try {
|
||||
report.value = await api.getReport(reportId)
|
||||
view.value = 'overview'
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : '加载失败'
|
||||
needBirth.value = true
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
@@ -338,6 +424,9 @@ async function load() {
|
||||
const m = Number(route.query.m)
|
||||
const d = Number(route.query.d)
|
||||
if (y && m && d && !validateBirth(y, m, d)) {
|
||||
year.value = String(y)
|
||||
month.value = String(m)
|
||||
day.value = String(d)
|
||||
await generate(y, m, d)
|
||||
}
|
||||
}
|
||||
@@ -363,42 +452,235 @@ onMounted(load)
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.yxg-card{margin:8px 0 12px}
|
||||
.yxg-label{margin-top:12px}
|
||||
.yxg-label:first-child{margin-top:0}
|
||||
.birth-row{
|
||||
display:flex;gap:6px;align-items:center;flex-wrap:wrap;margin-bottom:4px;
|
||||
.top-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
.birth-row :deep(.bd){margin:0;flex-wrap:nowrap}
|
||||
.bd-time{
|
||||
width:108px;padding:10px 6px;border:1.5px solid #eee;border-radius:12px;
|
||||
font-size:14px;text-align:center;outline:none;background:#fdfaf8;color:#333;
|
||||
.who, .who-static {
|
||||
flex: 1;
|
||||
border: none;
|
||||
background: transparent;
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
color: var(--color-text-primary);
|
||||
text-align: center;
|
||||
}
|
||||
.bd-time:focus{border-color:#f0b8b0;background:#fff}
|
||||
.bd-sep{font-size:12px;color:#bbb;flex-shrink:0}
|
||||
:deep(.bp){margin-bottom:10px}
|
||||
.head{margin:4px 0 8px;font-size:13px}
|
||||
.tab-body p{margin:0 0 6px;line-height:1.65}
|
||||
.panel-tabs,.fortune-tabs{display:flex;gap:8px;margin:10px 0 12px;flex-wrap:wrap}
|
||||
.panel-tab,.ftab{
|
||||
flex:1;min-width:56px;border:1px solid #eee;background:#fafafa;border-radius:12px;
|
||||
padding:10px 6px;font-size:13px;color:#666;cursor:pointer;
|
||||
.who-static { pointer-events: none; }
|
||||
.share-ico {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border: none;
|
||||
border-radius: 12px;
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
font-size: 16px;
|
||||
}
|
||||
.panel-tab.on,.ftab.on{border-color:var(--yxg-pri);background:#fff5f4;color:#222;font-weight:600}
|
||||
.daily-title{font-weight:700;color:#222;margin-bottom:6px}
|
||||
.focus{margin:0 0 8px;color:#666}
|
||||
.dims{display:flex;flex-wrap:wrap;gap:10px;margin:10px 0;font-size:12px;color:#555}
|
||||
.planet-list{list-style:none;padding:0;margin:0}
|
||||
.planet{display:flex;flex-direction:column;gap:4px;padding:12px}
|
||||
.lock .yxg-btn,.share-btn{margin-top:12px;width:100%}
|
||||
.links{display:flex;flex-direction:column;gap:8px;margin:12px 0}
|
||||
.wheel-settings{
|
||||
display:flex;gap:16px;font-size:12px;color:#666;margin:4px 0 8px;justify-content:center;
|
||||
.body { padding: 8px 16px 24px; }
|
||||
.intro-card { text-align: center; padding: 20px 12px 8px; }
|
||||
.intro-title {
|
||||
font-family: var(--font-display);
|
||||
font-size: 24px;
|
||||
font-weight: 700;
|
||||
margin-top: 8px;
|
||||
letter-spacing: 0.08em;
|
||||
}
|
||||
.wheel-settings label{display:flex;align-items:center;gap:4px;cursor:pointer}
|
||||
.house-row{font-size:13px;color:#555;margin:4px 0}
|
||||
.aspect-line{font-size:13px;color:#555;margin:4px 0;line-height:1.5}
|
||||
.aspect-full{margin:8px 0 0;padding-left:18px;font-size:12px;color:#666}
|
||||
.transit{margin:8px 0;font-size:13px}
|
||||
.transit p{margin:2px 0 0;color:#555}
|
||||
.intro-sub { font-size: 13px; color: #888; margin-top: 6px; line-height: 1.5; }
|
||||
.form-card { margin-top: 12px; }
|
||||
.cta {
|
||||
margin-top: 14px;
|
||||
width: 100%;
|
||||
padding: 13px;
|
||||
border: none;
|
||||
border-radius: 22px;
|
||||
color: #fff;
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
background: linear-gradient(135deg, #ff7a6e, var(--color-primary));
|
||||
box-shadow: 0 6px 16px rgba(229, 77, 66, 0.28);
|
||||
}
|
||||
.cta.soft {
|
||||
background: #fff;
|
||||
color: var(--color-primary);
|
||||
border: 1.5px solid #f5c4bc;
|
||||
box-shadow: none;
|
||||
}
|
||||
.cta:disabled { opacity: 0.45; }
|
||||
.birth-row { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }
|
||||
.bd-time {
|
||||
width: 108px;
|
||||
padding: 10px 6px;
|
||||
border: 1.5px solid #eee;
|
||||
border-radius: 12px;
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
background: #fdfaf8;
|
||||
}
|
||||
.mode-tabs {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
margin-bottom: 14px;
|
||||
background: #fafafa;
|
||||
border-radius: var(--radius-pill);
|
||||
padding: 4px;
|
||||
}
|
||||
.mode-tab {
|
||||
flex: 1;
|
||||
border: none;
|
||||
background: transparent;
|
||||
padding: 9px 4px;
|
||||
border-radius: var(--radius-pill);
|
||||
font-size: 13px;
|
||||
color: #888;
|
||||
font-weight: 600;
|
||||
}
|
||||
.mode-tab.on {
|
||||
background: #fff;
|
||||
color: var(--color-primary);
|
||||
box-shadow: 0 2px 8px rgba(229, 77, 66, 0.1);
|
||||
}
|
||||
.hl {
|
||||
font-family: var(--font-display);
|
||||
font-size: 20px;
|
||||
font-weight: 700;
|
||||
line-height: 1.35;
|
||||
color: #222;
|
||||
}
|
||||
.lead { margin-top: 8px; font-size: 13px; color: #777; line-height: 1.55; }
|
||||
.spotlight {
|
||||
margin-top: 14px;
|
||||
padding: 14px;
|
||||
border-radius: var(--radius-xl);
|
||||
background: linear-gradient(145deg, #fff0ec, #ffe4dc);
|
||||
box-shadow: var(--shadow-card);
|
||||
}
|
||||
.sp-head {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.sp-head strong { font-size: 14px; color: #222; }
|
||||
.sp-ai { font-size: 12px; color: var(--color-primary); font-weight: 600; }
|
||||
.sp-text { font-size: 12px; color: #666; line-height: 1.5; }
|
||||
.tags { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 10px; }
|
||||
.tag {
|
||||
font-size: 11px;
|
||||
padding: 4px 10px;
|
||||
border-radius: var(--radius-pill);
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
color: #8a4a3a;
|
||||
font-weight: 600;
|
||||
}
|
||||
.planet-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 8px;
|
||||
margin-top: 14px;
|
||||
}
|
||||
.cell {
|
||||
min-height: 88px;
|
||||
border: 1px solid #f0ebe8;
|
||||
background: #fafafa;
|
||||
border-radius: 16px;
|
||||
padding: 12px 8px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 4px;
|
||||
text-align: center;
|
||||
}
|
||||
.cell.on {
|
||||
border-color: #f5c4bc;
|
||||
background: #fff7f4;
|
||||
box-shadow: var(--shadow-card);
|
||||
}
|
||||
.cell.center {
|
||||
background: linear-gradient(160deg, #eef4ff, #ffe8e0);
|
||||
border-color: transparent;
|
||||
}
|
||||
.nick { font-size: 14px; font-weight: 700; color: #222; line-height: 1.2; }
|
||||
.role { font-size: 11px; color: var(--color-primary); font-weight: 600; }
|
||||
.detail-card {
|
||||
margin-top: 12px;
|
||||
padding: 14px;
|
||||
background: #fff;
|
||||
border-radius: var(--radius-lg);
|
||||
box-shadow: var(--shadow-card);
|
||||
}
|
||||
.detail-card h3 { font-size: 15px; margin-bottom: 6px; color: #222; }
|
||||
.detail-card p { font-size: 13px; color: #555; line-height: 1.6; }
|
||||
.ask-bar {
|
||||
margin-top: 14px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 14px 16px;
|
||||
border-radius: var(--radius-pill);
|
||||
background: #fff;
|
||||
border: 1.5px solid #f0ebe8;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: #555;
|
||||
box-shadow: var(--shadow-card);
|
||||
}
|
||||
.fortune-tabs { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 12px; }
|
||||
.ftab {
|
||||
flex: 1;
|
||||
min-width: 52px;
|
||||
border: 1px solid #eee;
|
||||
background: #fafafa;
|
||||
border-radius: 12px;
|
||||
padding: 10px 4px;
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
}
|
||||
.ftab.on {
|
||||
border-color: var(--color-primary);
|
||||
background: #fff5f4;
|
||||
color: #222;
|
||||
font-weight: 600;
|
||||
}
|
||||
.fortune-card { padding: 16px; border-radius: var(--radius-xl); background: #fafafa; }
|
||||
.daily-title { font-weight: 700; color: #222; margin-bottom: 6px; }
|
||||
.focus { margin: 0 0 8px; color: #666; }
|
||||
.dims { display: flex; flex-wrap: wrap; gap: 10px; margin: 10px 0; font-size: 12px; color: #555; }
|
||||
.planet-list { list-style: none; margin: 0; padding: 0; }
|
||||
.planet-row {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
padding: 14px;
|
||||
margin-bottom: 8px;
|
||||
background: #fafafa;
|
||||
border-radius: 14px;
|
||||
}
|
||||
.planet-row strong { font-size: 14px; color: #222; }
|
||||
.planet-row span { font-size: 12px; color: #888; }
|
||||
.lock-card {
|
||||
margin-top: 12px;
|
||||
padding: 16px;
|
||||
background: linear-gradient(145deg, #fff8e8, #ffeec8);
|
||||
border-radius: var(--radius-xl);
|
||||
}
|
||||
.bottom-links {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
margin: 16px 0 8px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: var(--color-primary);
|
||||
}
|
||||
.wheel-settings {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
margin: 4px 0 8px;
|
||||
justify-content: center;
|
||||
}
|
||||
.house-row, .aspect-line { font-size: 13px; color: #555; margin: 4px 0; }
|
||||
.yxg-label { display: block; margin: 12px 0 6px; font-size: 12px; color: #999; }
|
||||
.yxg-label:first-child { margin-top: 0; }
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user