| | |
| | | <script setup lang="ts"> |
| | | import { ref } from "vue"; |
| | | import { computed, ref } from "vue"; |
| | | import img1 from "@/assets/map-info/img1.png"; |
| | | import img2 from "@/assets/map-info/img2.png"; |
| | | import img3 from "@/assets/map-info/img3.png"; |
| | | import img4 from "@/assets/map-info/img4.png"; |
| | | import img5 from "@/assets/map-info/img5.png"; |
| | | import img6 from "@/assets/map-info/img6.png"; |
| | | import toggleIcon from "@/assets/map-info/toggle-icon.svg"; |
| | | |
| | | interface SummaryItem { |
| | | label: string; |
| | | value: string | number; |
| | | interface OverviewCardInput { |
| | | key: string; |
| | | title: string; |
| | | total?: number; |
| | | available?: number; |
| | | unavailable?: number; |
| | | } |
| | | |
| | | interface DetailCardInput { |
| | | key: string; |
| | | title: string; |
| | | subtitle: string; |
| | | pointCount?: number; |
| | | stationCount?: number; |
| | | total?: number; |
| | | } |
| | | |
| | | const props = withDefaults( |
| | | defineProps<{ |
| | | title?: string; |
| | | total?: string | number; |
| | | defaultExpanded?: boolean; |
| | | availableTitle?: string; |
| | | availableItems?: SummaryItem[]; |
| | | unavailableTitle?: string; |
| | | unavailableItems?: SummaryItem[]; |
| | | overviewCards?: OverviewCardInput[]; |
| | | detailCards?: DetailCardInput[]; |
| | | }>(), |
| | | { |
| | | title: "超出范围点位", |
| | | total: "8888", |
| | | defaultExpanded: true, |
| | | availableTitle: "可用点位", |
| | | availableItems: () => [ |
| | | { label: "可运营", value: "888" }, |
| | | { label: "待生效", value: "888" }, |
| | | { label: "启用中", value: "888" }, |
| | | ], |
| | | unavailableTitle: "不可用点位", |
| | | unavailableItems: () => [ |
| | | { label: "待测试", value: "888" }, |
| | | { label: "测试中", value: "888" }, |
| | | { label: "测试不通过", value: "888" }, |
| | | { label: "已停用", value: "888" }, |
| | | { label: "失效", value: "888" }, |
| | | ], |
| | | title: "围栏影响统计", |
| | | overviewCards: () => [], |
| | | detailCards: () => [], |
| | | } |
| | | ); |
| | | |
| | | const isExpanded = ref(props.defaultExpanded); |
| | | interface OverviewCard extends OverviewCardInput { |
| | | total: number; |
| | | totalColor: string; |
| | | available: number; |
| | | unavailable: number; |
| | | iconSrc: string; |
| | | } |
| | | |
| | | type TransitionElement = HTMLElement; |
| | | interface DetailCard extends DetailCardInput { |
| | | pointCount: number; |
| | | stationCount: number; |
| | | total: number; |
| | | totalColor: string; |
| | | iconSrc: string; |
| | | summary: string; |
| | | } |
| | | |
| | | const defaultOverviewCards: OverviewCardInput[] = [ |
| | | { |
| | | key: "point", |
| | | title: "点位", |
| | | total: 20, |
| | | available: 12, |
| | | unavailable: 8, |
| | | }, |
| | | { |
| | | key: "station", |
| | | title: "场站", |
| | | total: 9, |
| | | available: 6, |
| | | unavailable: 3, |
| | | }, |
| | | ]; |
| | | |
| | | const defaultDetailCards: DetailCardInput[] = [ |
| | | { |
| | | key: "remove", |
| | | title: "移出围栏", |
| | | subtitle: "不在圈内", |
| | | pointCount: 3, |
| | | stationCount: 1, |
| | | total: 4, |
| | | }, |
| | | { |
| | | key: "add", |
| | | title: "新增包含", |
| | | subtitle: "进入圈内", |
| | | pointCount: 5, |
| | | stationCount: 2, |
| | | total: 7, |
| | | }, |
| | | ]; |
| | | |
| | | const overviewCards = computed<OverviewCard[]>(() => { |
| | | const source = props.overviewCards.length |
| | | ? props.overviewCards |
| | | : defaultOverviewCards; |
| | | return source.map((item) => ({ |
| | | ...item, |
| | | total: Number(item.total || 0), |
| | | available: Number(item.available || 0), |
| | | unavailable: Number(item.unavailable || 0), |
| | | totalColor: item.key === "station" ? "#FA8C16" : "#1677FF", |
| | | iconSrc: item.key === "station" ? img3 : img2, |
| | | })); |
| | | }); |
| | | |
| | | const detailCards = computed<DetailCard[]>(() => { |
| | | const source = props.detailCards.length ? props.detailCards : defaultDetailCards; |
| | | return source.map((item) => { |
| | | const pointCount = Number(item.pointCount || 0); |
| | | const stationCount = Number(item.stationCount || 0); |
| | | return { |
| | | ...item, |
| | | pointCount, |
| | | stationCount, |
| | | total: Number(item.total ?? pointCount + stationCount), |
| | | totalColor: item.key === "add" ? "#52C41A" : "#FF4D4F", |
| | | iconSrc: item.key === "add" ? img6 : img5, |
| | | summary: `点位${pointCount}・场站${stationCount}`, |
| | | }; |
| | | }); |
| | | }); |
| | | |
| | | const isExpanded = ref(true); |
| | | |
| | | function toggleExpanded() { |
| | | isExpanded.value = !isExpanded.value; |
| | | } |
| | | |
| | | function finishHeightTransition(el: TransitionElement, done?: () => void) { |
| | | const onTransitionEnd = (event: TransitionEvent) => { |
| | | if (event.target !== el || event.propertyName !== "height") return; |
| | | el.removeEventListener("transitionend", onTransitionEnd); |
| | | done?.(); |
| | | }; |
| | | el.addEventListener("transitionend", onTransitionEnd); |
| | | } |
| | | |
| | | function onBeforeEnter(el: Element) { |
| | | const target = el as TransitionElement; |
| | | target.style.height = "0"; |
| | | target.style.opacity = "0"; |
| | | target.style.transform = "translateY(-10px)"; |
| | | target.style.overflow = "hidden"; |
| | | } |
| | | |
| | | function onEnter(el: Element, done: () => void) { |
| | | const target = el as TransitionElement; |
| | | void target.offsetHeight; |
| | | target.style.transition = |
| | | "height 0.42s cubic-bezier(0.23, 1, 0.32, 1), opacity 0.28s ease, transform 0.42s cubic-bezier(0.23, 1, 0.32, 1)"; |
| | | target.style.height = `${target.scrollHeight}px`; |
| | | target.style.opacity = "1"; |
| | | target.style.transform = "translateY(0)"; |
| | | finishHeightTransition(target, done); |
| | | } |
| | | |
| | | function onAfterEnter(el: Element) { |
| | | const target = el as TransitionElement; |
| | | target.style.height = "auto"; |
| | | target.style.overflow = ""; |
| | | target.style.transition = ""; |
| | | target.style.opacity = ""; |
| | | target.style.transform = ""; |
| | | } |
| | | |
| | | function onBeforeLeave(el: Element) { |
| | | const target = el as TransitionElement; |
| | | target.style.height = `${target.scrollHeight}px`; |
| | | target.style.opacity = "1"; |
| | | target.style.transform = "translateY(0)"; |
| | | target.style.overflow = "hidden"; |
| | | } |
| | | |
| | | function onLeave(el: Element, done: () => void) { |
| | | const target = el as TransitionElement; |
| | | void target.offsetHeight; |
| | | target.style.transition = |
| | | "height 0.38s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.2s ease, transform 0.32s ease"; |
| | | target.style.height = "0"; |
| | | target.style.opacity = "0"; |
| | | target.style.transform = "translateY(-8px)"; |
| | | finishHeightTransition(target, done); |
| | | } |
| | | |
| | | function onAfterLeave(el: Element) { |
| | | const target = el as TransitionElement; |
| | | target.style.transition = ""; |
| | | target.style.height = ""; |
| | | target.style.opacity = ""; |
| | | target.style.transform = ""; |
| | | target.style.overflow = ""; |
| | | } |
| | | </script> |
| | | |
| | | <template> |
| | | <div class="map-info-popup-card"> |
| | | <div class="map-info-popup-card__shell"> |
| | | <button |
| | | type="button" |
| | | class="map-info-popup-card__header" |
| | | @click="toggleExpanded" |
| | | > |
| | | <div class="map-info-popup-card__header-main"> |
| | | <div class="map-info-popup-card__title-bar" /> |
| | | <h3 class="map-info-popup-card__title">{{ title }}</h3> |
| | | <div class="map-info-popup-card__header"> |
| | | <div class="map-info-popup-card__title"> |
| | | {{ props.title }} |
| | | </div> |
| | | <div class="map-info-popup-card__header-side"> |
| | | <span class="map-info-popup-card__total">{{ total }}</span> |
| | | <span class="map-info-popup-card__toggle" :class="{ expanded: isExpanded }"> |
| | | <svg viewBox="0 0 24 24" aria-hidden="true"> |
| | | <path |
| | | d="M7 10l5 5 5-5" |
| | | fill="none" |
| | | stroke="currentColor" |
| | | stroke-linecap="round" |
| | | stroke-linejoin="round" |
| | | stroke-width="2.2" |
| | | /> |
| | | </svg> |
| | | </span> |
| | | </div> |
| | | </button> |
| | | <span |
| | | class="map-info-popup-card__toggle" |
| | | :class="{ 'is-expanded': isExpanded }" |
| | | aria-hidden="true" |
| | | @click="toggleExpanded" |
| | | > |
| | | <img class="map-info-popup-card__toggle-icon" :src="toggleIcon" alt="" /> |
| | | </span> |
| | | </div> |
| | | |
| | | <Transition |
| | | @before-enter="onBeforeEnter" |
| | | @enter="onEnter" |
| | | @after-enter="onAfterEnter" |
| | | @before-leave="onBeforeLeave" |
| | | @leave="onLeave" |
| | | @after-leave="onAfterLeave" |
| | | > |
| | | <div v-if="isExpanded" class="map-info-popup-card__content-wrap"> |
| | | <div class="map-info-popup-card__content"> |
| | | <section |
| | | class="map-info-popup-card__section map-info-popup-card__section--available" |
| | | > |
| | | <div class="map-info-popup-card__section-bg"> |
| | | <svg viewBox="0 0 24 24" aria-hidden="true"> |
| | | <path |
| | | d="M20 6L9 17l-5-5" |
| | | fill="none" |
| | | stroke="currentColor" |
| | | stroke-linecap="round" |
| | | stroke-linejoin="round" |
| | | stroke-width="3" |
| | | /> |
| | | </svg> |
| | | </div> |
| | | <div class="map-info-popup-card__section-head"> |
| | | <div class="map-info-popup-card__section-icon"> |
| | | <svg viewBox="0 0 24 24" aria-hidden="true"> |
| | | <path |
| | | d="M20 6L9 17l-5-5" |
| | | fill="none" |
| | | stroke="currentColor" |
| | | stroke-linecap="round" |
| | | stroke-linejoin="round" |
| | | stroke-width="3" |
| | | /> |
| | | </svg> |
| | | </div> |
| | | <span class="map-info-popup-card__section-title"> |
| | | {{ availableTitle }} |
| | | </span> |
| | | </div> |
| | | <div class="map-info-popup-card__grid map-info-popup-card__grid--three"> |
| | | <div |
| | | v-for="item in availableItems" |
| | | :key="item.label" |
| | | class="map-info-popup-card__metric" |
| | | > |
| | | <span class="map-info-popup-card__metric-label"> |
| | | {{ item.label }} |
| | | </span> |
| | | <span class="map-info-popup-card__metric-value"> |
| | | {{ item.value }} |
| | | </span> |
| | | </div> |
| | | </div> |
| | | </section> |
| | | <div v-show="isExpanded" class="map-info-popup-card__content"> |
| | | <div class="map-info-popup-card__divider" /> |
| | | |
| | | <section |
| | | class="map-info-popup-card__section map-info-popup-card__section--unavailable" |
| | | > |
| | | <div class="map-info-popup-card__section-bg"> |
| | | <svg viewBox="0 0 24 24" aria-hidden="true"> |
| | | <path |
| | | d="M6 6l12 12M18 6L6 18" |
| | | fill="none" |
| | | stroke="currentColor" |
| | | stroke-linecap="round" |
| | | stroke-width="3" |
| | | /> |
| | | </svg> |
| | | </div> |
| | | <div class="map-info-popup-card__section-head"> |
| | | <div class="map-info-popup-card__section-icon"> |
| | | <svg viewBox="0 0 24 24" aria-hidden="true"> |
| | | <path |
| | | d="M6 6l12 12M18 6L6 18" |
| | | fill="none" |
| | | stroke="currentColor" |
| | | stroke-linecap="round" |
| | | stroke-width="3" |
| | | /> |
| | | </svg> |
| | | </div> |
| | | <span class="map-info-popup-card__section-title"> |
| | | {{ unavailableTitle }} |
| | | </span> |
| | | </div> |
| | | <div class="map-info-popup-card__grid map-info-popup-card__grid--mixed"> |
| | | <div |
| | | v-for="item in unavailableItems" |
| | | :key="item.label" |
| | | class="map-info-popup-card__metric" |
| | | > |
| | | <span class="map-info-popup-card__metric-label"> |
| | | {{ item.label }} |
| | | </span> |
| | | <span class="map-info-popup-card__metric-value"> |
| | | {{ item.value }} |
| | | </span> |
| | | </div> |
| | | </div> |
| | | </section> |
| | | <section class="map-info-popup-card__block"> |
| | | <div class="map-info-popup-card__block-title"> |
| | | <img class="map-info-popup-card__block-icon" :src="img1" alt="" /> |
| | | <span>实时概览</span> |
| | | </div> |
| | | </div> |
| | | </Transition> |
| | | |
| | | <div class="map-info-popup-card__overview"> |
| | | <div |
| | | v-for="card in overviewCards" |
| | | :key="card.key" |
| | | class="map-info-popup-card__overview-card" |
| | | > |
| | | <div class="map-info-popup-card__overview-head"> |
| | | <img |
| | | class="map-info-popup-card__entity-icon" |
| | | :src="card.iconSrc" |
| | | alt="" |
| | | /> |
| | | <span class="map-info-popup-card__entity-title"> |
| | | {{ card.title }} |
| | | </span> |
| | | <span |
| | | class="map-info-popup-card__entity-total" |
| | | :style="{ color: card.totalColor }" |
| | | > |
| | | {{ card.total }} |
| | | </span> |
| | | </div> |
| | | |
| | | <div class="map-info-popup-card__overview-body"> |
| | | <div class="map-info-popup-card__metric-column"> |
| | | <span class="map-info-popup-card__metric-label">可用</span> |
| | | <span class="map-info-popup-card__metric-number is-green"> |
| | | {{ card.available }} |
| | | </span> |
| | | </div> |
| | | <div class="map-info-popup-card__metric-column"> |
| | | <span class="map-info-popup-card__metric-label">不可用</span> |
| | | <span class="map-info-popup-card__metric-number is-gray"> |
| | | {{ card.unavailable }} |
| | | </span> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </section> |
| | | |
| | | <div class="map-info-popup-card__divider" /> |
| | | |
| | | <section class="map-info-popup-card__block"> |
| | | <div class="map-info-popup-card__block-title"> |
| | | <img class="map-info-popup-card__block-icon" :src="img4" alt="" /> |
| | | <span>变动明细</span> |
| | | </div> |
| | | |
| | | <div class="map-info-popup-card__detail-list"> |
| | | <div |
| | | v-for="card in detailCards" |
| | | :key="card.key" |
| | | class="map-info-popup-card__detail-card" |
| | | > |
| | | <img |
| | | class="map-info-popup-card__detail-icon" |
| | | :src="card.iconSrc" |
| | | alt="" |
| | | /> |
| | | |
| | | <div class="map-info-popup-card__detail-main"> |
| | | <div class="map-info-popup-card__detail-title"> |
| | | {{ card.title }} |
| | | </div> |
| | | <div class="map-info-popup-card__detail-subtitle"> |
| | | {{ card.subtitle }} |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="map-info-popup-card__detail-side"> |
| | | <div class="map-info-popup-card__detail-summary"> |
| | | {{ card.summary }} |
| | | </div> |
| | | <div |
| | | class="map-info-popup-card__detail-total" |
| | | :style="{ color: card.totalColor }" |
| | | > |
| | | {{ card.total }} |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </section> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </template> |
| | |
| | | .map-info-popup-card { |
| | | position: absolute; |
| | | z-index: 998; |
| | | width: 360px; |
| | | width: 280px; |
| | | max-width: calc(100% - 48px); |
| | | animation: map-info-popup-card-enter 0.45s ease; |
| | | } |
| | | |
| | | .map-info-popup-card__shell { |
| | | overflow: hidden; |
| | | border: 1px solid rgba(255, 255, 255, 0.4); |
| | | border-radius: 28px; |
| | | background: rgba(255, 255, 255, 0.72); |
| | | backdrop-filter: blur(24px); |
| | | box-shadow: 0 32px 64px -16px rgba(0, 0, 0, 0.12); |
| | | padding: 12px; |
| | | border-radius: 8px; |
| | | background: #ffffff; |
| | | box-shadow: 0px 2px 8px 0px rgba(0, 0, 0, 0.1); |
| | | } |
| | | |
| | | .map-info-popup-card__header { |
| | | width: 100%; |
| | | padding: 16px 24px; |
| | | border: none; |
| | | background: transparent; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | cursor: pointer; |
| | | text-align: left; |
| | | } |
| | | |
| | | .map-info-popup-card__header-main { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 12px; |
| | | min-width: 0; |
| | | } |
| | | |
| | | .map-info-popup-card__title-bar { |
| | | width: 4px; |
| | | height: 24px; |
| | | border-radius: 999px; |
| | | background: linear-gradient(180deg, #3b82f6 0%, #4f46e5 100%); |
| | | box-shadow: 0 0 10px rgba(59, 130, 246, 0.4); |
| | | flex-shrink: 0; |
| | | } |
| | | |
| | | .map-info-popup-card__title { |
| | | margin: 0; |
| | | color: #0f172a; |
| | | font-size: 18px; |
| | | font-family: Source Han Sans; |
| | | font-size: 13px; |
| | | font-weight: 900; |
| | | line-height: 1.2; |
| | | letter-spacing: -0.02em; |
| | | } |
| | | |
| | | .map-info-popup-card__header-side { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 12px; |
| | | flex-shrink: 0; |
| | | } |
| | | |
| | | .map-info-popup-card__total { |
| | | color: #2563eb; |
| | | font-size: 24px; |
| | | font-weight: 900; |
| | | line-height: 1; |
| | | font-family: |
| | | "SFMono-Regular", |
| | | "Consolas", |
| | | "Liberation Mono", |
| | | monospace; |
| | | text-shadow: 0 1px 1px rgba(37, 99, 235, 0.12); |
| | | line-height: normal; |
| | | letter-spacing: normal; |
| | | color: #1a1a1a; |
| | | } |
| | | |
| | | .map-info-popup-card__toggle { |
| | | width: 28px; |
| | | height: 28px; |
| | | border-radius: 999px; |
| | | background: rgba(241, 245, 249, 0.85); |
| | | color: #94a3b8; |
| | | display: inline-flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | transition: |
| | | background-color 0.2s ease, |
| | | color 0.2s ease, |
| | | transform 0.2s ease; |
| | | |
| | | svg { |
| | | width: 16px; |
| | | height: 16px; |
| | | transition: transform 0.2s ease; |
| | | } |
| | | |
| | | &.expanded svg { |
| | | transform: rotate(180deg); |
| | | } |
| | | } |
| | | |
| | | .map-info-popup-card__header:hover .map-info-popup-card__toggle { |
| | | background: rgba(239, 246, 255, 0.95); |
| | | color: #2563eb; |
| | | } |
| | | |
| | | .map-info-popup-card__content { |
| | | padding: 0 20px 24px; |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 12px; |
| | | will-change: transform, opacity; |
| | | } |
| | | |
| | | .map-info-popup-card__section { |
| | | position: relative; |
| | | overflow: hidden; |
| | | padding: 16px; |
| | | border-radius: 24px; |
| | | border: 1px solid rgba(0, 0, 0, 0.04); |
| | | } |
| | | |
| | | .map-info-popup-card__section-bg { |
| | | position: absolute; |
| | | top: 0; |
| | | right: 0; |
| | | padding: 12px; |
| | | opacity: 0.08; |
| | | pointer-events: none; |
| | | |
| | | svg { |
| | | width: 40px; |
| | | height: 40px; |
| | | } |
| | | } |
| | | |
| | | .map-info-popup-card__section--available { |
| | | background: linear-gradient(135deg, rgba(236, 253, 245, 0.9) 0%, rgba(240, 253, 250, 0.7) 100%); |
| | | border-color: rgba(167, 243, 208, 0.7); |
| | | box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.4); |
| | | |
| | | .map-info-popup-card__section-bg, |
| | | .map-info-popup-card__metric-label { |
| | | color: rgba(5, 150, 105, 0.6); |
| | | } |
| | | |
| | | .map-info-popup-card__section-icon { |
| | | background: #10b981; |
| | | box-shadow: 0 10px 20px rgba(16, 185, 129, 0.2); |
| | | } |
| | | |
| | | .map-info-popup-card__section-title { |
| | | color: #047857; |
| | | } |
| | | |
| | | .map-info-popup-card__metric-value { |
| | | color: #064e3b; |
| | | } |
| | | } |
| | | |
| | | .map-info-popup-card__section--unavailable { |
| | | background: linear-gradient(135deg, rgba(248, 250, 252, 0.95) 0%, rgba(241, 245, 249, 0.75) 100%); |
| | | border-color: rgba(226, 232, 240, 0.9); |
| | | box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.5); |
| | | |
| | | .map-info-popup-card__section-bg, |
| | | .map-info-popup-card__metric-label { |
| | | color: #94a3b8; |
| | | } |
| | | |
| | | .map-info-popup-card__section-icon { |
| | | background: #94a3b8; |
| | | box-shadow: 0 10px 20px rgba(148, 163, 184, 0.18); |
| | | } |
| | | |
| | | .map-info-popup-card__section-title { |
| | | color: #64748b; |
| | | } |
| | | |
| | | .map-info-popup-card__metric-value { |
| | | color: #1e293b; |
| | | } |
| | | } |
| | | |
| | | .map-info-popup-card__section-head { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 8px; |
| | | margin-bottom: 12px; |
| | | } |
| | | |
| | | .map-info-popup-card__section-icon { |
| | | width: 24px; |
| | | height: 24px; |
| | | border-radius: 8px; |
| | | color: #fff; |
| | | display: inline-flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | flex-shrink: 0; |
| | | cursor: pointer; |
| | | |
| | | svg { |
| | | width: 12px; |
| | | height: 12px; |
| | | &.is-expanded { |
| | | .map-info-popup-card__toggle-icon { |
| | | transform: rotate(180deg); |
| | | } |
| | | } |
| | | } |
| | | |
| | | .map-info-popup-card__section-title { |
| | | font-size: 13px; |
| | | font-weight: 900; |
| | | line-height: 1; |
| | | letter-spacing: 0.14em; |
| | | text-transform: uppercase; |
| | | .map-info-popup-card__toggle-icon { |
| | | width: 10.5px; |
| | | height: 6px; |
| | | object-fit: contain; |
| | | transition: transform 0.2s ease; |
| | | } |
| | | |
| | | .map-info-popup-card__grid { |
| | | display: grid; |
| | | .map-info-popup-card__divider { |
| | | height: 1px; |
| | | margin: 10px 0; |
| | | background: #e8e8e8; |
| | | &:last-child { |
| | | margin: 12px 0; |
| | | } |
| | | } |
| | | |
| | | .map-info-popup-card__block { |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 8px; |
| | | } |
| | | |
| | | .map-info-popup-card__grid--three { |
| | | grid-template-columns: repeat(3, minmax(0, 1fr)); |
| | | .map-info-popup-card__block-title { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 4px; |
| | | font-family: Source Han Sans; |
| | | font-size: 12px; |
| | | font-weight: 900; |
| | | line-height: normal; |
| | | letter-spacing: normal; |
| | | color: #333333; |
| | | } |
| | | |
| | | .map-info-popup-card__grid--mixed { |
| | | grid-template-columns: repeat(3, minmax(0, 1fr)); |
| | | row-gap: 12px; |
| | | .map-info-popup-card__block-icon { |
| | | width: 12px; |
| | | height: 11px; |
| | | flex-shrink: 0; |
| | | margin: 3px 0; |
| | | object-fit: contain; |
| | | } |
| | | |
| | | .map-info-popup-card__metric { |
| | | min-width: 0; |
| | | .map-info-popup-card__overview { |
| | | display: grid; |
| | | grid-template-columns: repeat(2, minmax(0, 1fr)); |
| | | gap: 8px; |
| | | } |
| | | |
| | | .map-info-popup-card__overview-card { |
| | | padding: 8px; |
| | | border-radius: 4px; |
| | | background: #f5f5f5; |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 6px; |
| | | } |
| | | |
| | | .map-info-popup-card__overview-head { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 4px; |
| | | } |
| | | |
| | | .map-info-popup-card__entity-icon { |
| | | width: 8px; |
| | | height: 11px; |
| | | object-fit: contain; |
| | | flex-shrink: 0; |
| | | } |
| | | |
| | | .map-info-popup-card__entity-title { |
| | | flex: 1; |
| | | font-family: Source Han Sans; |
| | | font-size: 11px; |
| | | font-weight: 500; |
| | | line-height: normal; |
| | | letter-spacing: normal; |
| | | color: #595959; |
| | | } |
| | | |
| | | .map-info-popup-card__entity-total { |
| | | font-family: Source Han Sans; |
| | | font-size: 16px; |
| | | font-weight: normal; |
| | | line-height: normal; |
| | | letter-spacing: normal; |
| | | color: #1890ff; |
| | | } |
| | | |
| | | .map-info-popup-card__overview-body { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | } |
| | | |
| | | .map-info-popup-card__metric-column { |
| | | // flex:1; |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 2px; |
| | | } |
| | | |
| | | .map-info-popup-card__metric-label { |
| | | font-size: 9px; |
| | | font-weight: 900; |
| | | line-height: 1.25; |
| | | letter-spacing: 0.08em; |
| | | text-transform: uppercase; |
| | | font-family: Source Han Sans; |
| | | font-size: 10px; |
| | | font-weight: normal; |
| | | line-height: normal; |
| | | letter-spacing: normal; |
| | | color: #999999; |
| | | } |
| | | |
| | | .map-info-popup-card__metric-value { |
| | | font-size: 15px; |
| | | font-weight: 900; |
| | | line-height: 1.2; |
| | | font-family: |
| | | "SFMono-Regular", |
| | | "Consolas", |
| | | "Liberation Mono", |
| | | monospace; |
| | | } |
| | | .map-info-popup-card__metric-number { |
| | | font-family: Source Han Sans; |
| | | font-size: 13px; |
| | | font-weight: normal; |
| | | line-height: normal; |
| | | letter-spacing: normal; |
| | | |
| | | .map-info-popup-card__content-wrap { |
| | | transform-origin: top center; |
| | | will-change: height, opacity, transform; |
| | | } |
| | | |
| | | @keyframes map-info-popup-card-enter { |
| | | from { |
| | | opacity: 0; |
| | | transform: translateY(20px); |
| | | &.is-green { |
| | | color: #52c41a; |
| | | } |
| | | |
| | | to { |
| | | opacity: 1; |
| | | transform: translateY(0); |
| | | &.is-gray { |
| | | color: rgba(23, 31, 38, 0.45); |
| | | } |
| | | } |
| | | |
| | | .map-info-popup-card__detail-list { |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 6px; |
| | | } |
| | | |
| | | .map-info-popup-card__detail-card { |
| | | padding: 8px; |
| | | border-radius: 4px; |
| | | background: #f5f5f5; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | gap: 8px; |
| | | } |
| | | |
| | | .map-info-popup-card__detail-icon { |
| | | width: 14px; |
| | | height: 14px; |
| | | flex-shrink: 0; |
| | | object-fit: contain; |
| | | } |
| | | |
| | | .map-info-popup-card__detail-main { |
| | | flex: 1; |
| | | min-width: 0; |
| | | } |
| | | |
| | | .map-info-popup-card__detail-title { |
| | | font-family: Source Han Sans; |
| | | font-size: 11px; |
| | | font-weight: normal; |
| | | line-height: normal; |
| | | letter-spacing: normal; |
| | | color: #595959; |
| | | } |
| | | |
| | | .map-info-popup-card__detail-subtitle { |
| | | font-family: Source Han Sans; |
| | | font-size: 10px; |
| | | font-weight: normal; |
| | | line-height: normal; |
| | | letter-spacing: normal; |
| | | color: #999999; |
| | | } |
| | | |
| | | .map-info-popup-card__detail-side { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 8px; |
| | | flex-shrink: 0; |
| | | } |
| | | |
| | | .map-info-popup-card__detail-summary { |
| | | font-family: Source Han Sans; |
| | | font-size: 10px; |
| | | font-weight: normal; |
| | | line-height: normal; |
| | | letter-spacing: normal; |
| | | color: #666666; |
| | | } |
| | | |
| | | .map-info-popup-card__detail-total { |
| | | font-family: Source Han Sans; |
| | | font-size: 16px; |
| | | font-weight: normal; |
| | | line-height: normal; |
| | | letter-spacing: normal; |
| | | } |
| | | </style> |