提交 18368a91 authored 作者: 张伊明's avatar 张伊明

feat 重构深度挖掘-流程概要全部样式

上级 02714a38
流水线 #269 已通过 于阶段
in 1 分 45 秒
...@@ -153,7 +153,7 @@ ...@@ -153,7 +153,7 @@
class="item-box" class="item-box"
v-for="item in sharedEvents" v-for="item in sharedEvents"
:key="`shared-${item.id}`" :key="`shared-${item.id}`"
style="width: 280px; flex-shrink: 0;" :style="{ width: TIMELINE_ITEM_WIDTH_PX + 'px', flexShrink: 0 }"
> >
<div class="item-time">{{ item.actionDate }}</div> <div class="item-time">{{ item.actionDate }}</div>
<div class="item-box-dot"></div> <div class="item-box-dot"></div>
...@@ -211,7 +211,15 @@ const dialogPos = ref({ left: "0px", top: "0px" }); ...@@ -211,7 +211,15 @@ const dialogPos = ref({ left: "0px", top: "0px" });
const ORG_SENATE = "参议院"; const ORG_SENATE = "参议院";
const ORG_HOUSE = "众议院"; const ORG_HOUSE = "众议院";
const PRESIDENT_KEYWORD = "呈递给总统"; const PRESIDENT_KEYWORD = "呈递给总统";
/** 卡片宽度;同一条时间线上相邻圆点间距 ≥ 此值,避免同轨卡片横向重叠 */
const TIMELINE_ITEM_WIDTH_PX = 280; const TIMELINE_ITEM_WIDTH_PX = 280;
/** 时间线锚点为圆点;与样式 .item-box-dot { left: 10px } 一致 */
const DOT_LEFT_IN_ITEM_BOX_PX = 10;
/** 与 scoped 样式中 .top/.bottom .content-box 的 margin-left 一致,用于跨轨圆点与「两密点中点」对齐 */
const SENATE_CONTENT_BOX_MARGIN_LEFT_PX = 134;
const HOUSE_CONTENT_BOX_MARGIN_LEFT_PX = 30;
/** 双轨主线在最后一个圆点/卡片之后保留的空白(原先用整卡宽 W 导致尾部过长) */
const TIMELINE_LINE_TAIL_PADDING_PX = 48;
const ARROW_SEGMENT_TOTAL_PX = 16; const ARROW_SEGMENT_TOTAL_PX = 16;
const DIAGONAL_LINE_WIDTH_PX = 127; const DIAGONAL_LINE_WIDTH_PX = 127;
...@@ -281,35 +289,72 @@ const dualLaneTimeline = computed(() => { ...@@ -281,35 +289,72 @@ const dualLaneTimeline = computed(() => {
.filter((item) => !isDualEvent(item)); .filter((item) => !isDualEvent(item));
}); });
const senateEventsPositioned = computed(() => { /**
const list = []; * 双轨事件已按时间排好(dualLaneTimeline)。依次放置:与前一条同轨则绝对 x + W,
let slotIndex = 0; * 异轨则以前一条圆点绝对位置为锚 + 0.5W(方案 C)。单轨(灰线)仍用 sharedEvents 顺序定宽。
dualLaneTimeline.value.forEach((item) => { */
if (!isSenateEvent(item)) return; const dualLaneLayout = computed(() => {
list.push({ const U = dualLaneTimeline.value;
key: item.id, const W = TIMELINE_ITEM_WIDTH_PX;
const chamberRowSenate = (item) => {
const s = isSenateEvent(item);
const h = isHouseEvent(item);
if (s && !h) return true;
if (h && !s) return false;
return Boolean(s);
};
const idxInU = (item) => U.indexOf(item);
const toSlots = (list, posByItem) =>
list.map((item) => ({
key: `${idxInU(item)}-${item.id ?? "e"}`,
item, item,
left: slotIndex * TIMELINE_ITEM_WIDTH_PX left: (posByItem.get(item) ?? 0) - DOT_LEFT_IN_ITEM_BOX_PX
}); }));
slotIndex += 1;
}); if (!U.length) {
return list; return { senateSlots: [], houseSlots: [], maxDualLaneRightPx: 0 };
}
const posByItem = new Map();
let prevAbs = null;
let prevSenate = null;
for (const item of U) {
const sen = chamberRowSenate(item);
const margin = sen ? SENATE_CONTENT_BOX_MARGIN_LEFT_PX : HOUSE_CONTENT_BOX_MARGIN_LEFT_PX;
const absDot =
prevAbs === null ? margin : prevAbs + (sen === prevSenate ? W : W / 2);
posByItem.set(item, absDot - margin);
prevAbs = absDot;
prevSenate = sen;
}
/** 相对 content-box 左缘:卡片右边界 = layoutDotX - DOT_LEFT + W */
let maxDualLaneRightPx = 0;
for (const item of U) {
const layoutDotX = posByItem.get(item) ?? 0;
const rightPx = layoutDotX - DOT_LEFT_IN_ITEM_BOX_PX + W;
maxDualLaneRightPx = Math.max(maxDualLaneRightPx, rightPx);
}
return {
senateSlots: toSlots(
U.filter((item) => isSenateEvent(item)),
posByItem
),
houseSlots: toSlots(
U.filter((item) => isHouseEvent(item)),
posByItem
),
maxDualLaneRightPx
};
}); });
const houseEventsPositioned = computed(() => { const senateEventsPositioned = computed(() => dualLaneLayout.value.senateSlots);
const list = [];
let slotIndex = 0; const houseEventsPositioned = computed(() => dualLaneLayout.value.houseSlots);
dualLaneTimeline.value.forEach((item) => {
if (!isHouseEvent(item)) return;
list.push({
key: item.id,
item,
left: slotIndex * TIMELINE_ITEM_WIDTH_PX
});
slotIndex += 1;
});
return list;
});
const sharedEvents = computed(() => { const sharedEvents = computed(() => {
return sortedTimeline.value.filter((item, idx) => { return sortedTimeline.value.filter((item, idx) => {
...@@ -317,14 +362,25 @@ const sharedEvents = computed(() => { ...@@ -317,14 +362,25 @@ const sharedEvents = computed(() => {
}); });
}); });
const dualLaneCount = computed(() => { /** 双轨 content-box 内所需宽度:最后卡片右缘 + 尾部留白(与 maxLineWidth 中的 extent 一致) */
return Math.max(senateEventsPositioned.value.length, houseEventsPositioned.value.length); const dualLaneContentExtentPx = computed(() => {
if (!dualLaneTimeline.value.length) return 0;
return (
dualLaneLayout.value.maxDualLaneRightPx + TIMELINE_LINE_TAIL_PADDING_PX
);
}); });
/**
* 双轨横线(top-line / bottom-line 共用)宽度:起点 left:110,需画到与卡片区右缘对齐。
* 右缘 x = 120 + 参院 margin + extent = 254 + extent,故宽度 = 254 + extent - 110 = 144 + extent。
* 旧式 254+extent 多 110px,导致上下线都偏长。
*/
const maxLineWidth = computed(() => { const maxLineWidth = computed(() => {
const senateWidth = 254 + dualLaneCount.value * TIMELINE_ITEM_WIDTH_PX; if (!dualLaneTimeline.value.length) {
const houseWidth = 150 + dualLaneCount.value * TIMELINE_ITEM_WIDTH_PX; return Math.max(254, 150);
return Math.max(senateWidth, houseWidth); }
const extent = dualLaneContentExtentPx.value;
return 120 + SENATE_CONTENT_BOX_MARGIN_LEFT_PX + extent - 110;
}); });
const lineWidth = computed(() => `${maxLineWidth.value}px`); const lineWidth = computed(() => `${maxLineWidth.value}px`);
...@@ -350,22 +406,41 @@ const rightArrowCount = computed(() => { ...@@ -350,22 +406,41 @@ const rightArrowCount = computed(() => {
return Math.max(1, Math.ceil(sharedLineWidth.value / ARROW_SEGMENT_TOTAL_PX)); return Math.max(1, Math.ceil(sharedLineWidth.value / ARROW_SEGMENT_TOTAL_PX));
}); });
/** 参院 content 起点更靠右(margin 134 vs 30),众院 box 需多 104px 才能与参院右缘对齐 */
const CONTENT_BOX_WIDTH_DELTA_SENATE_HOUSE_PX =
SENATE_CONTENT_BOX_MARGIN_LEFT_PX - HOUSE_CONTENT_BOX_MARGIN_LEFT_PX;
const senateBoxStyle = computed(() => ({ const senateBoxStyle = computed(() => ({
width: `${maxLineWidth.value + 110 - 254}px`, width: `${
dualLaneTimeline.value.length
? dualLaneContentExtentPx.value
: 110
}px`,
justifyContent: "flex-start" justifyContent: "flex-start"
})); }));
const houseBoxStyle = computed(() => ({ const houseBoxStyle = computed(() => ({
width: `${maxLineWidth.value + 110 - 150}px`, width: `${
dualLaneTimeline.value.length
? dualLaneContentExtentPx.value + CONTENT_BOX_WIDTH_DELTA_SENATE_HOUSE_PX
: 214
}px`,
justifyContent: "flex-start" justifyContent: "flex-start"
})); }));
const rightPos = computed(() => `${maxLineWidth.value + 90}px`); const rightPos = computed(() => `${maxLineWidth.value + 90}px`);
const sharedBoxStyle = computed(() => ({ const sharedBoxStyle = computed(() => {
left: `${maxLineWidth.value + 219}px`, const rightTopPx = Number.parseFloat(String(rightTop.value)) || 0;
const rightCenterY = rightTopPx + 12;
return {
left: `${maxLineWidth.value + 230}px`,
top: `${rightCenterY}px`,
transform: "translateY(-100%)",
width: `${Math.max(sharedLineWidth.value, sharedEvents.value.length * TIMELINE_ITEM_WIDTH_PX)}px` width: `${Math.max(sharedLineWidth.value, sharedEvents.value.length * TIMELINE_ITEM_WIDTH_PX)}px`
})); };
});
const topLineEndRef = ref(null); const topLineEndRef = ref(null);
const bottomLineEndRef = ref(null); const bottomLineEndRef = ref(null);
...@@ -547,13 +622,18 @@ const updateRightTop = () => { ...@@ -547,13 +622,18 @@ const updateRightTop = () => {
} }
} }
/* .top 高 260px,时间线 top:242px;卡片下缘应对齐线顶:距容器底 18px */
.content-box { .content-box {
position: relative; position: relative;
margin-left: 134px; margin-left: 134px;
margin-bottom: 19px; margin-bottom: 0;
align-self: stretch;
min-height: 0;
.item-box { .item-box {
position: absolute; position: absolute;
top: auto;
bottom: 18px;
} }
} }
} }
...@@ -808,7 +888,7 @@ const updateRightTop = () => { ...@@ -808,7 +888,7 @@ const updateRightTop = () => {
.shared-content-box { .shared-content-box {
position: absolute; position: absolute;
top: 170px; top: 254px;
display: flex; display: flex;
.item-box { .item-box {
......
...@@ -834,6 +834,7 @@ onMounted(() => { ...@@ -834,6 +834,7 @@ onMounted(() => {
flex-shrink: 1; flex-shrink: 1;
max-width: 170px; max-width: 170px;
text-align: center; text-align: center;
margin-bottom: 10px;
} }
.nameItemActive { .nameItemActive {
......
...@@ -46,7 +46,7 @@ ...@@ -46,7 +46,7 @@
<div class="chart-ai-wrap"> <div class="chart-ai-wrap">
<div :class="['right-box2-main', { 'right-box-main--full': !domainFooterText }]" id="chart2"></div> <div :class="['right-box2-main', { 'right-box-main--full': !domainFooterText }]" id="chart2"></div>
<div class="overview-tip-row"> <div class="overview-tip-row">
<TipTab class="overview-tip" /> <TipTab class="overview-tip" :text="'涉华科技法案数量及通过率变化趋势,数据来源:美国国会官网'"/>
<AiButton class="overview-tip-action" @mouseenter="handleShowAiPane('domain')" /> <AiButton class="overview-tip-action" @mouseenter="handleShowAiPane('domain')" />
</div> </div>
<div v-if="aiPaneVisible.domain" class="overview-ai-pane" <div v-if="aiPaneVisible.domain" class="overview-ai-pane"
...@@ -72,7 +72,7 @@ ...@@ -72,7 +72,7 @@
<div class="chart-ai-wrap"> <div class="chart-ai-wrap">
<div :class="['right-box1-main', { 'right-box-main--full': !limitFooterText }]" id="chart1"></div> <div :class="['right-box1-main', { 'right-box-main--full': !limitFooterText }]" id="chart1"></div>
<div class="overview-tip-row"> <div class="overview-tip-row">
<TipTab class="overview-tip" /> <TipTab class="overview-tip" :text="'涉华科技法案数量及通过率变化趋势,数据来源:美国国会官网'"/>
<AiButton class="overview-tip-action" @mouseenter="handleShowAiPane('limit')" /> <AiButton class="overview-tip-action" @mouseenter="handleShowAiPane('limit')" />
</div> </div>
<div v-if="aiPaneVisible.limit" class="overview-ai-pane" <div v-if="aiPaneVisible.limit" class="overview-ai-pane"
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论