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

fix 修复深度挖掘样式问题

上级 e25eb5aa
流水线 #216 已通过 于阶段
in 1 分 26 秒
......@@ -2,16 +2,9 @@
<div class="layout-container">
<!-- 导航菜单 -->
<div class="layout-main">
<BillHeader
:billInfo="billInfoGlobal"
:defaultLogo="USALogo"
:tabs="mainHeaderBtnList"
:activeTitle="activeTitle"
:showTabs="showHeaderTabs"
:showActions="showHeaderActions"
@tab-click="handleClickMainHeaderBtn"
@open-analysis="handleAnalysisClick"
/>
<BillHeader :billInfo="billInfoGlobal" :defaultLogo="USALogo" :tabs="mainHeaderBtnList"
:activeTitle="activeTitle" :showTabs="showHeaderTabs" :showActions="showHeaderActions"
@tab-click="handleClickMainHeaderBtn" @open-analysis="handleAnalysisClick" />
<div class="layout-main-center">
<router-view />
......@@ -122,8 +115,7 @@ const handleAnalysisClick = analysisType => {
// 进展预测 -> 法案简介页(法案进展)
if (analysisType === "forsee") {
router.push({
path: "/billLayout/bill/introduction",
query: { billId: String(billId) }
path: `/billLayout/ProgressForecast/${billId}`,
});
return;
}
......@@ -162,11 +154,13 @@ watch(
// height: 1016px;
background: rgba(249, 250, 252, 1);
position: relative;
// margin: 0 auto;
.layout-main {
width: 100%;
height: calc(100vh - 72px);
height: 100vh;
overflow-y: auto;
.layout-main-center {
// height: calc(100% - 137px);
width: 1600px;
......
......@@ -5,7 +5,7 @@
<div class="left" :style="{ width: (maxLineWidth + 250) + 'px' }">
<div class="top">
<div class="top-line" :style="{ width: lineWidth }">
<div class="top-line1"></div>
<div class="top-line1" ref="topLineEndRef"></div>
</div>
<div class="start">
<div class="icon">
......@@ -14,25 +14,34 @@
<div class="name">{{ "参议院" }}</div>
</div>
<div class="content-box" :style="senateBoxStyle">
<div class="item-box" v-for="(item, index) in senateList" :key="item.id"
style="width: 280px; flex-shrink: 0;">
<div
class="item-box"
v-for="slot in senateSlots"
:key="slot.key"
style="width: 280px; flex-shrink: 0;"
>
<template v-if="slot.item">
<div class="item-box-dot">
<img src="./assets/images/top-line-dot.png" alt="" />
</div>
<div class="item-content">
<div class="item-header">
<div class="item-title" :title="item.actionTitle">
{{ item.actionTitle }} <span v-if="item.versionId">({{ item.versionId }})</span>
<div class="item-title" :title="slot.item.actionTitle">
{{ slot.item.actionTitle }} <span v-if="slot.item.versionId">({{ slot.item.versionId }})</span>
</div>
<div class="item-header-icon" @click="handleClickDetail(true, item, $event)">
<div class="item-header-icon" @click="handleClickDetail(true, slot.item, $event)">
<img src="./assets/images/item-header-icon.png" alt="" />
</div>
</div>
<div class="item-info" v-if="item.agreeVote !== null || item.disagreeVote !== null">
{{ (item.agreeVote || 0) + "赞成:" + (item.disagreeVote || 0) + "反对" }}
<div class="item-info" v-if="slot.item.agreeVote !== null || slot.item.disagreeVote !== null">
{{ formatVoteText(slot.item) }}
</div>
<div class="item-main" v-if="item.fynrList && item.fynrList.length">
<div class="item-main-item" v-for="(sub, subIndex) in item.fynrList" :key="subIndex">
<div class="item-main" v-if="slot.item.fynrList && slot.item.fynrList.length">
<div
class="item-main-item"
v-for="(sub, subIndex) in slot.item.fynrList"
:key="`${slot.item.id}-${subIndex}-${sub}`"
>
<div class="icon"></div>
<CommonPrompt :content="sub">
<div class="text">{{ sub }}</div>
......@@ -41,15 +50,16 @@
</div>
</div>
<div class="item-time">
{{ item.actionDate }}
{{ slot.item.actionDate }}
</div>
</template>
</div>
</div>
</div>
<div class="bottom">
<div class="bottom-line" :style="{ width: lineWidth }">
<div class="bottom-line1"></div>
<div class="bottom-line1" ref="bottomLineEndRef"></div>
</div>
<div class="start">
<div class="name">{{ "众议院" }}</div>
......@@ -59,28 +69,37 @@
</div>
</div>
<div class="content-box" :style="houseBoxStyle">
<div class="item-box" v-for="(item, index) in houseList" :key="item.id"
style="width: 280px; flex-shrink: 0;">
<div
class="item-box"
v-for="slot in houseSlots"
:key="slot.key"
style="width: 280px; flex-shrink: 0;"
>
<template v-if="slot.item">
<div class="item-time">
{{ item.actionDate }}
{{ slot.item.actionDate }}
</div>
<div class="item-box-dot">
<img src="./assets/images/bottom-line-dot.png" alt="" />
</div>
<div class="item-content">
<div class="item-header">
<div class="item-title" :title="item.actionTitle">
{{ item.actionTitle }} <span v-if="item.versionId">({{ item.versionId }})</span>
<div class="item-title" :title="slot.item.actionTitle">
{{ slot.item.actionTitle }} <span v-if="slot.item.versionId">({{ slot.item.versionId }})</span>
</div>
<div class="item-header-icon" @click="handleClickDetail(true, item, $event)">
<div class="item-header-icon" @click="handleClickDetail(true, slot.item, $event)">
<img src="./assets/images/item-header-icon.png" alt="" />
</div>
</div>
<div class="item-info" v-if="item.agreeVote !== null || item.disagreeVote !== null">
{{ (item.agreeVote || 0) + "赞成:" + (item.disagreeVote || 0) + "反对" }}
<div class="item-info" v-if="slot.item.agreeVote !== null || slot.item.disagreeVote !== null">
{{ formatVoteText(slot.item) }}
</div>
<div class="item-main" v-if="item.fynrList && item.fynrList.length">
<div class="item-main-item" v-for="(sub, subIndex) in item.fynrList" :key="subIndex">
<div class="item-main" v-if="slot.item.fynrList && slot.item.fynrList.length">
<div
class="item-main-item"
v-for="(sub, subIndex) in slot.item.fynrList"
:key="`${slot.item.id}-${subIndex}-${sub}`"
>
<div class="icon"></div>
<CommonPrompt :content="sub">
<div class="text">{{ sub }}</div>
......@@ -88,10 +107,11 @@
</div>
</div>
</div>
</template>
</div>
</div>
</div>
<div class="right" :style="{ left: rightPos }">
<div class="right" :style="{ left: rightPos, top: rightTop }">
<div class="junction-dot">
<div class="inner-dot"></div>
</div>
......@@ -111,7 +131,7 @@
</template>
<script setup>
import { ref, onMounted, computed } from "vue";
import { ref, onMounted, computed, nextTick } from "vue";
import { getBillDyqkSummary } from "@/api/bill";
import CommonPrompt from "../../commonPrompt/index.vue";
import ProcessOverviewDetailDialog from "../../ProcessOverviewDetailDialog.vue";
......@@ -138,30 +158,81 @@ const getBillDyqkSummaryList = async () => {
}
};
// 总统签署节点
const ORG_SENATE = "参议院";
const ORG_HOUSE = "众议院";
const PRESIDENT_KEYWORD = "呈递给总统";
const TIMELINE_ITEM_WIDTH_PX = 280;
const getTime = (actionDate) => {
const t = new Date(actionDate).getTime();
return Number.isFinite(t) ? t : 0;
};
const formatVoteText = (item) => {
if (!item) return "";
const agree = item.agreeVote ?? 0;
const disagree = item.disagreeVote ?? 0;
return `${agree}赞成:${disagree}反对`;
};
// 总统交汇节点(用于确定两条时间线的汇合位置)
const presidentAction = computed(() => {
return actionList.value.find(item => item.actionTitle && item.actionTitle.includes("呈递给总统")) || null;
return (
actionList.value.find(
(item) => item.actionTitle && item.actionTitle.includes(PRESIDENT_KEYWORD)
) || null
);
});
// 参议院列表
const senateList = computed(() => {
return actionList.value
.filter(item => item.orgName === "参议院" && (!presidentAction.value || item.id !== presidentAction.value.id))
.sort((a, b) => new Date(a.actionDate) - new Date(b.actionDate));
// 全局时间排序后的“时间步”
const sortedTimeline = computed(() => {
return [...actionList.value].sort((a, b) => {
const tA = getTime(a.actionDate);
const tB = getTime(b.actionDate);
if (tA !== tB) return tA - tB;
// 时间相同的情况下用 id 保证稳定排序,避免节点在不同渲染中漂移
const idA = String(a.id ?? "");
const idB = String(b.id ?? "");
return idA.localeCompare(idB);
});
});
// 众议院列表
const houseList = computed(() => {
return actionList.value
.filter(item => item.orgName === "众议院" && (!presidentAction.value || item.id !== presidentAction.value.id))
.sort((a, b) => new Date(a.actionDate) - new Date(b.actionDate));
// 交汇点(总统节点)在全局时间线里的位置:slice endIndexExclusive 用来排除总统节点本身
const mergeIndexExclusive = computed(() => {
if (!sortedTimeline.value.length) return 0;
if (!presidentAction.value) return sortedTimeline.value.length;
const idx = sortedTimeline.value.findIndex((item) => item.id === presidentAction.value.id);
return idx >= 0 ? idx : sortedTimeline.value.length;
});
// 两条时间线共享同一组时间步(每个时间步只展示属于该阵营的事件;其他阵营用空占位对齐)
const timelineSlots = computed(() => {
return sortedTimeline.value.slice(0, mergeIndexExclusive.value);
});
// 计算最大线条宽度数值
const senateSlots = computed(() => {
return timelineSlots.value.map((step) => ({
key: step.id,
item: step.orgName === ORG_SENATE ? step : null
}));
});
const houseSlots = computed(() => {
return timelineSlots.value.map((step) => ({
key: step.id,
item: step.orgName === ORG_HOUSE ? step : null
}));
});
const timelineCount = computed(() => timelineSlots.value.length);
// 计算最大线条宽度数值(两条线共享时间步长度)
const maxLineWidth = computed(() => {
const senateWidth = 254 + senateList.value.length * 280;
const houseWidth = 150 + houseList.value.length * 280;
return Math.max(1100, senateWidth, houseWidth);
const senateWidth = 254 + timelineCount.value * TIMELINE_ITEM_WIDTH_PX;
const houseWidth = 150 + timelineCount.value * TIMELINE_ITEM_WIDTH_PX;
return Math.max(senateWidth, houseWidth);
});
// 绑定给线条的样式
......@@ -173,7 +244,7 @@ const lineWidth = computed(() => {
const senateBoxStyle = computed(() => {
return {
width: (maxLineWidth.value + 110 - 254) + 'px',
justifyContent: 'space-between'
justifyContent: 'flex-start'
};
});
......@@ -181,7 +252,7 @@ const senateBoxStyle = computed(() => {
const houseBoxStyle = computed(() => {
return {
width: (maxLineWidth.value + 110 - 150) + 'px',
justifyContent: 'space-between'
justifyContent: 'flex-start'
};
});
......@@ -190,6 +261,10 @@ const rightPos = computed(() => {
return (maxLineWidth.value + 90) + 'px';
});
const topLineEndRef = ref(null);
const bottomLineEndRef = ref(null);
const rightTop = ref('370px');
const isShowDetailDialog = ref(false);
const currentDetailItem = ref({});
const dialogPos = ref({ left: '0px', top: '0px' });
......@@ -224,9 +299,43 @@ const handleClickDetail = (isShow, item = {}, event = null) => {
};
// 挂载阶段调用
onMounted(() => {
getBillDyqkSummaryList();
onMounted(async () => {
await getBillDyqkSummaryList();
await nextTick();
updateRightTop();
});
const updateRightTop = () => {
// 交汇点需要精确对齐上下两条斜线端点在页面中的 y 坐标
// rightTop 是相对 .process-overview-wrap 的 absolute top
const wrap = document.querySelector('.process-overview-wrap');
if (!wrap) return;
const topLineEndEl = topLineEndRef.value;
const bottomLineEndEl = bottomLineEndRef.value;
if (!topLineEndEl || !bottomLineEndEl) return;
const wrapRect = wrap.getBoundingClientRect();
const topRect = topLineEndEl.getBoundingClientRect();
const bottomRect = bottomLineEndEl.getBoundingClientRect();
// 根据 CSS 的旋转原点:
// - top-line1: rotate(45deg) 且 transform-origin: 0 0,因此左侧“尖端”约等于 rect.top
// - bottom-line1: rotate(-45deg) 且 transform-origin: 0 100%,因此左侧“尖端”约等于 rect.bottom
// 但我们对齐的是“斜线中心线”,而不是外包矩形边缘。
// 该斜线块在样式里高度为 8px,所以中心线偏移 4px
const LINE_THICKNESS_PX = 8;
const topLineCenterY = topRect.top + LINE_THICKNESS_PX / 2;
const bottomLineCenterY = bottomRect.bottom - LINE_THICKNESS_PX / 2;
const desiredCenterY = (topLineCenterY + bottomLineCenterY) / 2;
// .right 里 junction-dot 高度为 24px,flex 会让 right-line 与其垂直居中
const junctionCenterOffsetY = 12;
// 经验补偿:整体向上平移约 49px,使视觉交汇点精确落在两条斜线交点
const VISUAL_OFFSET_Y = -49;
rightTop.value = (desiredCenterY - wrapRect.top - junctionCenterOffsetY + VISUAL_OFFSET_Y) + 'px';
};
</script>
<style lang="scss" scoped>
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论