Files
----/前端源码/uni-app/components/screens/CompanyNamingDetailDesktop.vue

824 lines
26 KiB
Vue
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view class="company-desktop-detail">
<view class="detail-header">
<view class="detail-header-back" @click="emit('back')">
<text class="back-icon"></text>
<text class="back-text">返回</text>
</view>
<text class="detail-header-title">公司测名详解</text>
<view class="detail-header-placeholder" />
</view>
<scroll-view scroll-y class="detail-content">
<view class="report-body">
<!-- header -->
<view
class="section"
:class="{ 'section--click': hasNodes(header.details?.nodes) }"
@click="hasNodes(header.details?.nodes) && openDetail(header.details?.title || '总分详解', header.details?.nodes)"
>
<text class="section-label">header · 总分</text>
<view class="score-row">
<text class="name">{{ toText(header.name) || '—' }}</text>
<text class="score">{{ num(header.score, 0) }}</text>
</view>
<view class="tag-row">
<text class="pill">{{ toText(header.tagLeft) }}</text>
<text class="pill">{{ toText(header.tagRight) }}</text>
</view>
<text class="body-text">{{ toText(header.quote) }}</text>
<text v-if="hasNodes(header.details?.nodes)" class="section-hint">点击本段查看{{ toText(header.details?.title) || '详解' }}</text>
</view>
<!-- characterAnalysis -->
<view
class="section"
:class="{ 'section--click': hasNodes(characterAnalysis.details?.nodes) }"
@click="
hasNodes(characterAnalysis.details?.nodes) &&
openDetail(characterAnalysis.details?.title || '字义数理详解', characterAnalysis.details?.nodes)
"
>
<text class="section-label">characterAnalysis · 字义数理</text>
<view class="char-grid">
<view v-for="(it, idx) in arr(characterAnalysis.characters)" :key="idx" class="char-box">
<text class="char-single">{{ toText(it?.char) }}</text>
<text class="char-meta">{{ toText(it?.element) }} · {{ num(it?.stroke, 0) }}</text>
<text class="body-text small">{{ toText(it?.meaning) }}</text>
</view>
</view>
<text class="body-text">{{ toText(characterAnalysis.analysis) }}</text>
<text v-if="hasNodes(characterAnalysis.details?.nodes)" class="section-hint">
点击本段查看{{ toText(characterAnalysis.details?.title) || '详细拆解' }}
</text>
</view>
<!-- businessPattern -->
<view class="section">
<text class="section-label">businessPattern · 商业格局</text>
<SixDimensionRadarDesktopEchart
:labels="arr(businessPattern.radar?.labels)"
:values="arr(businessPattern.radar?.values)"
:remark="summaryText"
/>
<view v-if="arr(businessPattern.summary).length" class="kv-inline">
<view v-for="(s, si) in arr(businessPattern.summary)" :key="si" class="summary-chip">
<text class="chip-k">{{ toText(s?.label) }}</text>
<text class="chip-v">{{ toText(s?.value) }}</text>
</view>
</view>
<view
v-if="hasNodes(businessPattern.details?.nodes)"
class="section-link"
:class="{ 'section--click': true }"
@click="openDetail(businessPattern.details?.title || '商业六维详解', businessPattern.details?.nodes)"
>
<text>{{ toText(businessPattern.details?.title) || '商业六维 · 解释与建议' }} </text>
</view>
</view>
<!-- gua -->
<view
class="section"
:class="{ 'section--click': hasNodes(gua.details?.nodes) }"
@click="hasNodes(gua.details?.nodes) && openDetail(gua.details?.title || '卦象解读', gua.details?.nodes)"
>
<text class="section-label">gua · 卦象</text>
<text v-if="toText(gua.bg)" class="gua-bg">卦字{{ toText(gua.bg) }}</text>
<text class="headline">{{ toText(gua.name) }} · {{ toText(gua.badge) }}</text>
<text class="body-text">{{ toText(gua.desc) }}</text>
<view v-if="arr(gua.tags).length" class="tag-list">
<text v-for="(t, ti) in arr(gua.tags)" :key="ti" class="pill pill--soft">{{ toText(t) }}</text>
</view>
<text class="body-text">{{ toText(gua.insight) }}</text>
<text v-if="hasNodes(gua.details?.nodes)" class="section-hint">点击本段查看卦象详解</text>
</view>
<!-- team -->
<view
class="section"
:class="{ 'section--click': hasNodes(team.details?.nodes) }"
@click="hasNodes(team.details?.nodes) && openDetail(team.details?.title || '团队契合', team.details?.nodes)"
>
<text class="section-label">team · 团队契合</text>
<view v-for="(m, mi) in arr(team.members)" :key="mi" class="member-block">
<text class="member-line">
{{ toText(m?.role) }} · {{ num(m?.score, 0) }} · {{ toText(m?.match) }}
</text>
<text v-if="toText(m?.desc)" class="body-text small">{{ toText(m?.desc) }}</text>
</view>
<text v-if="toText(team.note)" class="body-text note">{{ toText(team.note) }}</text>
<text v-if="hasNodes(team.details?.nodes)" class="section-hint">点击本段查看团队说明</text>
</view>
<!-- years -->
<view
class="section"
:class="{ 'section--click': hasNodes(years.details?.nodes) }"
@click="hasNodes(years.details?.nodes) && openDetail(years.details?.title || '流年运势', years.details?.nodes)"
>
<text class="section-label">years · 流年</text>
<view v-for="(y, yi) in arr(years.items)" :key="yi" class="year-row">
<text class="year-key">{{ toText(y?.year) }}</text>
<text class="year-luck">{{ toText(y?.luck) }}</text>
<text class="year-text">{{ toText(y?.text) }}</text>
</view>
<text v-if="hasNodes(years.details?.nodes)" class="section-hint">点击本段查看流年预警/建议</text>
</view>
<!-- wealthTrend -->
<view
class="section"
:class="{ 'section--click': hasNodes(wealthTrend.details?.nodes) }"
@click="hasNodes(wealthTrend.details?.nodes) && openDetail(wealthTrend.details?.title || '财运走势', wealthTrend.details?.nodes)"
>
<text class="section-label">wealthTrend · 财运走势</text>
<view class="bars">
<view v-for="(v, vi) in arr(wealthTrend.bars)" :key="vi" class="bar-wrap">
<view class="bar" :style="{ height: `${Math.max(8, Math.min(100, num(v, 0)))}%` }" />
</view>
</view>
<text class="body-text">{{ toText(wealthTrend.note) }}</text>
<text v-if="hasNodes(wealthTrend.details?.nodes)" class="section-hint">点击本段查看走势建议</text>
</view>
<!-- direction -->
<view
class="section"
:class="{ 'section--click': hasNodes(direction.details?.nodes) }"
@click="hasNodes(direction.details?.nodes) && openDetail(direction.details?.title || '吉凶方位', direction.details?.nodes)"
>
<text class="section-label">direction · 方位</text>
<text class="body-text">{{ toText(direction.note) }}</text>
<text v-if="direction.goodDot && (direction.goodDot.x != null || direction.goodDot.y != null)" class="body-text small">
吉位参考点相对坐标x {{ num(direction.goodDot?.x, 0) }}y {{ num(direction.goodDot?.y, 0) }}
</text>
<text v-if="hasNodes(direction.details?.nodes)" class="section-hint">点击本段查看方位说明</text>
</view>
<!-- layout -->
<view
class="section"
:class="{ 'section--click': hasNodes(layout.details?.nodes) }"
@click="hasNodes(layout.details?.nodes) && openDetail(layout.details?.title || '办公布局', layout.details?.nodes)"
>
<text class="section-label">layout · 办公布局</text>
<view v-for="(it, li) in arr(layout.items)" :key="li" class="layout-line">
<text class="layout-strong">{{ toText(it?.strong) }}</text>
<view class="layout-flow">
<text class="body-text small">{{ toText(it?.textBefore) }}</text>
<text v-for="(h, hi) in arr(it?.highlights)" :key="hi" class="highlight">{{ toText(h) }}</text>
<text class="body-text small">{{ toText(it?.textAfter) }}</text>
</view>
</view>
<text v-if="hasNodes(layout.details?.nodes)" class="section-hint">点击本段查看布局建议</text>
</view>
<!-- execution -->
<view
class="section section--wide"
:class="{ 'section--click': hasNodes(execution.details?.nodes) }"
@click="hasNodes(execution.details?.nodes) && openDetail(execution.details?.title || '执行建议', execution.details?.nodes)"
>
<text class="section-label">execution · 执行建议</text>
<text class="body-text">{{ toText(execution.text) }}</text>
<text v-if="hasNodes(execution.details?.nodes)" class="section-hint">点击本段查看执行条目</text>
</view>
<!-- liuyao -->
<view
v-if="hasLiuyao"
class="section"
:class="{ 'section--click': hasNodes(liuyao.details?.nodes) }"
@click="hasNodes(liuyao.details?.nodes) && openDetail('六爻', liuyao.details?.nodes)"
>
<text class="section-label">liuyao · 六爻</text>
<text class="headline">{{ toText(liuyao.hexagram_title) }}</text>
<text class="body-text">{{ toText(liuyao.changing_summary) }}</text>
<text class="body-text">{{ toText(liuyao.interpretation) }}</text>
<text v-for="(yl, yi) in arr(liuyao.yao_lines)" :key="yi" class="body-text small mono">· {{ toText(yl) }}</text>
<text v-if="hasNodes(liuyao.details?.nodes)" class="section-hint">点击本段查看六爻详解</text>
</view>
<!-- wuxing_bagua -->
<view
v-if="hasWuxingBagua"
class="section"
:class="{ 'section--click': hasNodes(wuxingBagua.details?.nodes) }"
@click="hasNodes(wuxingBagua.details?.nodes) && openDetail('五行八卦', wuxingBagua.details?.nodes)"
>
<text class="section-label">wuxing_bagua · 五行八卦</text>
<text class="body-text">{{ toText(wuxingBagua.wuxing_sketch) }}</text>
<text class="body-text">{{ toText(wuxingBagua.bagua_profile) }}</text>
<text class="body-text">{{ toText(wuxingBagua.mutual_sketch) }}</text>
<text class="body-text strong-end">{{ toText(wuxingBagua.summary) }}</text>
<text v-if="hasNodes(wuxingBagua.details?.nodes)" class="section-hint">点击本段查看详解</text>
</view>
<!-- zodiac_sign -->
<view
v-if="hasZodiac"
class="section"
:class="{ 'section--click': hasNodes(zodiacSign.details?.nodes) }"
@click="hasNodes(zodiacSign.details?.nodes) && openDetail('属相', zodiacSign.details?.nodes)"
>
<text class="section-label">zodiac_sign · 属相</text>
<text class="headline">
{{ toText(zodiacSign.animal_icon) }} {{ toText(zodiacSign.animal) }}{{ toText(zodiacSign.earthly_branch) }}
</text>
<text class="body-text">{{ toText(zodiacSign.trait_summary) }}</text>
<text class="body-text">{{ toText(zodiacSign.name_harmony) }}</text>
<text v-if="hasNodes(zodiacSign.details?.nodes)" class="section-hint">点击本段查看属相详解</text>
</view>
<!-- career_plan -->
<view
v-if="hasCareerPlan"
class="section"
:class="{ 'section--click': hasNodes(careerPlan.details?.nodes) }"
@click="hasNodes(careerPlan.details?.nodes) && openDetail('事业规划', careerPlan.details?.nodes)"
>
<text class="section-label">career_plan · 事业规划</text>
<text class="body-text">{{ toText(careerPlan.summary) }}</text>
<view v-for="(ms, msi) in arr(careerPlan.milestones)" :key="msi" class="milestone">
<text class="milestone-title">{{ toText(ms?.phase) }}{{ toText(ms?.period) ? ' · ' + toText(ms.period) : '' }}</text>
<text v-if="toText(ms?.focus)" class="body-text small">重点{{ toText(ms.focus) }}</text>
<text v-if="toText(ms?.advice)" class="body-text small">建议{{ toText(ms.advice) }}</text>
</view>
<text v-if="hasNodes(careerPlan.details?.nodes)" class="section-hint">点击本段查看规划详解</text>
</view>
<!-- lucky_numbers -->
<view
v-if="hasLuckyNumbers"
class="section"
:class="{ 'section--click': hasNodes(luckyNumbers.details?.nodes) }"
@click="hasNodes(luckyNumbers.details?.nodes) && openDetail('幸运数字', luckyNumbers.details?.nodes)"
>
<text class="section-label">lucky_numbers · 幸运数字</text>
<text class="headline">首推{{ toText(luckyNumbers.primary) }}</text>
<text class="body-text">{{ arr(luckyNumbers.numbers).join('、') }}</text>
<text class="body-text">{{ toText(luckyNumbers.meaning) }}</text>
<text v-if="hasNodes(luckyNumbers.details?.nodes)" class="section-hint">点击本段查看数字详解</text>
</view>
<!-- lucky_colors -->
<view
v-if="hasLuckyColors"
class="section"
:class="{ 'section--click': hasNodes(luckyColors.details?.nodes) }"
@click="hasNodes(luckyColors.details?.nodes) && openDetail('幸运色', luckyColors.details?.nodes)"
>
<text class="section-label">lucky_colors · 幸运色</text>
<text class="headline">主推{{ toText(luckyColors.primary) }}</text>
<view class="color-row">
<view v-for="(c, ci) in arr(luckyColors.colors)" :key="ci" class="color-item">
<view class="color-swatch" :style="{ backgroundColor: toText(c?.hex) || '#333' }" />
<text class="body-text small">{{ toText(c?.name) }}{{ toText(c?.note) ? ' · ' + toText(c.note) : '' }}</text>
</view>
</view>
<text class="body-text">{{ toText(luckyColors.meaning) }}</text>
<text v-if="hasNodes(luckyColors.details?.nodes)" class="section-hint">点击本段查看用色详解</text>
</view>
</view>
</scroll-view>
<view v-if="props.showBusinessFortune !== false" class="footer-action">
<button class="fortune-btn" type="button" @click="emit('businessFortune', detailData)">查看商业运势</button>
</view>
<view v-if="showModal" class="modal-mask" @click="closeModal">
<view class="detail-modal" @click.stop>
<view class="detail-modal-card">
<text class="modal-title">{{ modalTitle }}</text>
<text class="close" @click="closeModal">×</text>
</view>
<scroll-view scroll-y class="detail-modal-body">
<view v-for="(node, idx) in modalNodes" :key="idx" class="node">
<text v-if="node?.type === 'text'" class="line">{{ toText(node.text) }}</text>
<view v-else-if="node?.type === 'list'">
<text v-for="(item, j) in arr(node.items)" :key="j" class="line">- {{ toText(item) }}</text>
</view>
<view v-else-if="node?.type === 'kv'">
<text v-for="(item, j) in arr(node.items)" :key="j" class="line">{{ toText(item?.label) }}{{ toText(item?.value) }}</text>
</view>
</view>
</scroll-view>
</view>
</view>
</view>
</template>
<script setup lang="ts">
import { computed, ref } from "vue";
import SixDimensionRadarDesktopEchart from "../SixDimensionRadarDesktopEchart.vue";
const props = defineProps<{
data: any;
showBusinessFortune?: boolean;
}>();
const emit = defineEmits<{
back: [];
businessFortune: [any];
}>();
const parseMaybeJson = (value: any) => {
if (value && typeof value === "object") return value;
if (typeof value !== "string") return {};
try {
return JSON.parse(value);
} catch {
return {};
}
};
const toText = (v: any) => String(v ?? "").trim();
const num = (v: any, fallback = 0) => {
const n = Number(v);
return Number.isFinite(n) ? n : fallback;
};
const arr = (v: any) => (Array.isArray(v) ? v : []);
const hasNodes = (nodes: any) => arr(nodes).length > 0;
const detailData = computed(() => parseMaybeJson(props.data));
const header = computed(() => detailData.value?.header || {});
const characterAnalysis = computed(() => detailData.value?.characterAnalysis || {});
const businessPattern = computed(() => detailData.value?.businessPattern || {});
const gua = computed(() => detailData.value?.gua || {});
const team = computed(() => detailData.value?.team || {});
const years = computed(() => detailData.value?.years || {});
const wealthTrend = computed(() => detailData.value?.wealthTrend || {});
const direction = computed(() => detailData.value?.direction || {});
const layout = computed(() => detailData.value?.layout || {});
const execution = computed(() => detailData.value?.execution || {});
const liuyao = computed(() => detailData.value?.liuyao || {});
const wuxingBagua = computed(() => detailData.value?.wuxing_bagua || {});
const zodiacSign = computed(() => detailData.value?.zodiac_sign || {});
const careerPlan = computed(() => detailData.value?.career_plan || {});
const luckyNumbers = computed(() => detailData.value?.lucky_numbers || {});
const luckyColors = computed(() => detailData.value?.lucky_colors || {});
const summaryText = computed(() =>
arr(businessPattern.value?.summary)
.map((x: any) => `${toText(x?.label)} ${toText(x?.value)}`)
.filter(Boolean)
.join(" · "),
);
const hasLiuyao = computed(
() =>
!!(
toText(liuyao.value?.hexagram_title) ||
toText(liuyao.value?.changing_summary) ||
toText(liuyao.value?.interpretation) ||
arr(liuyao.value?.yao_lines).length ||
hasNodes(liuyao.value?.details?.nodes)
),
);
const hasWuxingBagua = computed(
() =>
!!(
toText(wuxingBagua.value?.wuxing_sketch) ||
toText(wuxingBagua.value?.bagua_profile) ||
toText(wuxingBagua.value?.mutual_sketch) ||
toText(wuxingBagua.value?.summary) ||
hasNodes(wuxingBagua.value?.details?.nodes)
),
);
const hasZodiac = computed(
() =>
!!(
toText(zodiacSign.value?.animal) ||
toText(zodiacSign.value?.trait_summary) ||
toText(zodiacSign.value?.name_harmony) ||
hasNodes(zodiacSign.value?.details?.nodes)
),
);
const hasCareerPlan = computed(
() =>
!!(
toText(careerPlan.value?.summary) ||
arr(careerPlan.value?.milestones).length ||
hasNodes(careerPlan.value?.details?.nodes)
),
);
const hasLuckyNumbers = computed(
() =>
!!(toText(luckyNumbers.value?.primary) || arr(luckyNumbers.value?.numbers).length || toText(luckyNumbers.value?.meaning)),
);
const hasLuckyColors = computed(
() =>
!!(
toText(luckyColors.value?.primary) ||
arr(luckyColors.value?.colors).length ||
toText(luckyColors.value?.meaning)
),
);
const showModal = ref(false);
const modalTitle = ref("");
const modalNodes = ref<any[]>([]);
const openDetail = (title: string, nodes: any[]) => {
const list = arr(nodes);
if (!list.length) return;
modalTitle.value = toText(title) || "详情";
modalNodes.value = list;
showModal.value = true;
};
const closeModal = () => {
showModal.value = false;
};
</script>
<style scoped>
.company-desktop-detail {
min-height: 100%;
height: 100%;
display: flex;
flex-direction: column;
background: linear-gradient(165deg, #070a12 0%, #12102a 42%, #0a1628 100%);
color: #e8e4dc;
}
.detail-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px 16px;
background: rgba(15, 23, 42, 0.72);
border-bottom: 1px solid rgba(212, 175, 55, 0.2);
flex-shrink: 0;
}
.detail-header-back {
display: inline-flex;
align-items: center;
gap: 6px;
color: #d4af37;
}
.detail-header-title {
font-size: 16px;
font-weight: 700;
color: #f2e6d8;
}
.detail-header-placeholder {
width: 48px;
}
.detail-content {
flex: 1;
min-height: 0;
box-sizing: border-box;
}
.report-body {
max-width: 820px;
margin: 0 auto;
padding: 16px 18px 20px;
box-sizing: border-box;
}
.section {
margin-bottom: 16px;
padding: 14px 16px;
border-radius: 12px;
background: rgba(15, 23, 42, 0.52);
border: 1px solid rgba(212, 175, 55, 0.14);
border-left: 3px solid rgba(212, 175, 55, 0.45);
backdrop-filter: blur(8px);
}
.section--wide {
max-width: 100%;
}
.section--click {
cursor: pointer;
}
.section--click:active {
opacity: 0.92;
}
.section-label {
display: block;
font-size: 11px;
letter-spacing: 0.06em;
color: rgba(212, 175, 55, 0.75);
margin-bottom: 10px;
font-weight: 600;
}
/* 商业六维内嵌 ECharts 组件自带外边距,报告式布局里收紧 */
:deep(.sixdim-section) {
margin-bottom: 0 !important;
}
.score-row {
display: flex;
align-items: baseline;
justify-content: space-between;
margin-bottom: 8px;
}
.name {
font-size: 22px;
font-weight: 800;
color: #f2e6d8;
}
.score {
font-size: 32px;
font-weight: 800;
color: #d4af37;
}
.tag-row,
.tag-list {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-bottom: 10px;
}
.pill {
font-size: 11px;
padding: 4px 10px;
border-radius: 999px;
background: rgba(212, 175, 55, 0.12);
border: 1px solid rgba(212, 175, 55, 0.28);
color: #ede6d8;
}
.pill--soft {
background: rgba(255, 255, 255, 0.06);
border-color: rgba(148, 163, 184, 0.28);
color: rgba(232, 228, 220, 0.9);
}
.body-text {
display: block;
font-size: 13px;
line-height: 1.55;
color: rgba(232, 228, 220, 0.92);
margin-bottom: 8px;
}
.body-text.small {
font-size: 12px;
color: rgba(232, 228, 220, 0.82);
}
.body-text.note {
font-style: italic;
color: rgba(212, 175, 55, 0.65);
}
.mono {
font-family: ui-monospace, monospace;
}
.strong-end {
font-weight: 600;
color: #f0dba9;
}
.headline {
display: block;
font-size: 15px;
font-weight: 700;
color: #f4e5c4;
margin-bottom: 8px;
}
.gua-bg {
display: block;
font-size: 12px;
color: rgba(212, 175, 55, 0.8);
margin-bottom: 6px;
}
.section-hint {
display: block;
margin-top: 8px;
font-size: 11px;
color: rgba(148, 163, 184, 0.85);
}
.section-link {
margin-top: 10px;
padding-top: 10px;
border-top: 1px solid rgba(212, 175, 55, 0.12);
font-size: 12px;
color: #d4af37;
}
.char-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
gap: 10px;
margin-bottom: 12px;
}
.char-box {
padding: 10px;
border-radius: 10px;
background: rgba(2, 6, 23, 0.4);
border: 1px solid rgba(212, 175, 55, 0.12);
}
.char-single {
font-size: 22px;
font-weight: 800;
}
.char-meta {
display: block;
font-size: 11px;
color: rgba(203, 213, 225, 0.8);
margin: 4px 0 6px;
}
.kv-inline {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-top: 12px;
}
.summary-chip {
padding: 6px 10px;
border-radius: 8px;
background: rgba(2, 6, 23, 0.45);
border: 1px solid rgba(148, 163, 184, 0.22);
}
.chip-k {
font-size: 11px;
color: rgba(203, 213, 225, 0.85);
margin-right: 6px;
}
.chip-v {
font-size: 12px;
font-weight: 700;
color: #f0dba9;
}
.member-block {
margin-bottom: 10px;
padding-bottom: 10px;
border-bottom: 1px solid rgba(255, 255, 255, 0.06);
}
.member-block:last-of-type {
border-bottom: none;
}
.member-line {
font-size: 13px;
font-weight: 600;
color: #f2e6d8;
}
.year-row {
display: grid;
grid-template-columns: 56px 52px 1fr;
gap: 8px;
align-items: start;
margin-bottom: 8px;
font-size: 12px;
}
.year-key {
color: #d4af37;
font-weight: 700;
}
.year-luck {
color: #a7f3d0;
}
.layout-line {
margin-bottom: 10px;
}
.layout-strong {
display: block;
font-size: 13px;
font-weight: 700;
color: #f0dba9;
margin-bottom: 4px;
}
.highlight {
color: #fde68a;
margin: 0 2px;
font-size: 12px;
}
.layout-flow {
display: flex;
flex-wrap: wrap;
align-items: baseline;
gap: 2px 6px;
}
.bars {
height: 96px;
display: flex;
align-items: flex-end;
gap: 6px;
margin: 12px 0;
}
.bar-wrap {
flex: 1;
height: 100%;
border-radius: 6px;
background: rgba(255, 255, 255, 0.06);
overflow: hidden;
display: flex;
align-items: flex-end;
}
.bar {
width: 100%;
background: linear-gradient(180deg, rgba(255, 205, 96, 0.9), rgba(230, 129, 38, 0.78));
}
.milestone {
margin-top: 10px;
padding: 10px;
border-radius: 10px;
background: rgba(2, 6, 23, 0.35);
border: 1px solid rgba(212, 175, 55, 0.1);
}
.milestone-title {
display: block;
font-size: 13px;
font-weight: 700;
color: #f4e5c4;
margin-bottom: 6px;
}
.color-row {
display: flex;
flex-wrap: wrap;
gap: 12px;
margin: 10px 0;
}
.color-item {
display: flex;
align-items: center;
gap: 8px;
}
.color-swatch {
width: 28px;
height: 28px;
border-radius: 8px;
border: 1px solid rgba(255, 255, 255, 0.2);
flex-shrink: 0;
}
.footer-action {
padding: 10px 18px 14px;
flex-shrink: 0;
max-width: 820px;
width: 100%;
margin: 0 auto;
box-sizing: border-box;
}
.fortune-btn {
width: 100%;
border-radius: 10px;
padding: 10px 12px;
background: linear-gradient(135deg, rgba(139, 35, 35, 0.95), rgba(90, 20, 20, 0.98));
color: #fdfbf7;
border: 1px solid rgba(212, 175, 55, 0.35);
}
.modal-mask {
position: fixed;
inset: 0;
z-index: 3200;
background: rgba(2, 6, 23, 0.72);
display: flex;
align-items: center;
justify-content: center;
padding: 16px;
box-sizing: border-box;
}
.detail-modal {
width: min(640px, 100%);
max-height: min(82vh, 760px);
background: rgba(10, 12, 20, 0.96);
border: 1px solid rgba(212, 175, 55, 0.2);
border-radius: 12px;
overflow: hidden;
}
.detail-modal-card {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 12px;
border-bottom: 1px solid rgba(212, 175, 55, 0.16);
}
.modal-title {
color: #d4af37;
font-size: 14px;
font-weight: 700;
}
.close {
color: #d4af37;
font-size: 20px;
}
.detail-modal-body {
max-height: calc(min(82vh, 760px) - 48px);
padding: 12px;
box-sizing: border-box;
}
.node {
margin-bottom: 8px;
}
.line {
display: block;
font-size: 13px;
line-height: 1.5;
color: rgba(232, 228, 220, 0.9);
margin-bottom: 6px;
}
</style>