初始化询价智能体新仓:同步本地工程并排除密钥与 .env。
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* 分析看板 API。
|
||||
*/
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
|
||||
enum Api {
|
||||
metrics = '/inquiry/dashboard/metrics',
|
||||
}
|
||||
|
||||
export const getDashboardMetrics = (range = 'today') =>
|
||||
defHttp.get({ url: Api.metrics, params: { range } });
|
||||
@@ -0,0 +1,547 @@
|
||||
<script setup lang="ts">
|
||||
/**
|
||||
* 分析看板:图标 / 帮助气泡 / 图表悬停提示对齐原型。
|
||||
* 顶栏工具条用 Teleport defer 挂到布局 #page-topbar-actions。
|
||||
*/
|
||||
import { computed, onMounted, onUnmounted, ref } from "vue";
|
||||
import { message } from "ant-design-vue";
|
||||
import ProtoIcon from "/@/views/inquiry/components/ProtoIcon.vue";
|
||||
import {
|
||||
todayMetrics,
|
||||
inquiryTrend,
|
||||
rankingPeople,
|
||||
rankingDepts,
|
||||
costRoutes,
|
||||
type RankingPerson,
|
||||
} from "/@/views/inquiry/mock/dashboard";
|
||||
|
||||
const dateDisplay = ref("2026-07-01——2026-07-31");
|
||||
const dateFrom = ref("2026-07-01");
|
||||
const dateTo = ref("2026-07-31");
|
||||
const dateFromMonth = ref("2026-07");
|
||||
const dateToMonth = ref("2026-07");
|
||||
const dateFromYear = ref("2026");
|
||||
const dateToYear = ref("2026");
|
||||
const pickerOpen = ref(false);
|
||||
const pickerMode = ref<"day" | "month" | "year">("day");
|
||||
const updatedTime = ref("00:00");
|
||||
const routeFilterOpen = ref(false);
|
||||
const selectedRoutes = ref<string[]>(costRoutes.map((r) => r.id));
|
||||
const rankingRole = ref<"all" | "sales" | "route" | "product">("all");
|
||||
const rankingDept = ref("");
|
||||
const chartReady = ref(false);
|
||||
const chartTip = ref({
|
||||
visible: false,
|
||||
left: "0px",
|
||||
top: "0px",
|
||||
date: "",
|
||||
value: "",
|
||||
lines: [] as string[],
|
||||
});
|
||||
const costTip = ref({
|
||||
visible: false,
|
||||
left: "0px",
|
||||
top: "0px",
|
||||
date: "",
|
||||
value: "",
|
||||
lines: [] as string[],
|
||||
});
|
||||
const activePoint = ref<string | null>(null);
|
||||
|
||||
const INQUIRY_PLOT_TOP = 36;
|
||||
const INQUIRY_PLOT_BOTTOM = 188;
|
||||
|
||||
const inquirySvg = computed(() => {
|
||||
const points = inquiryTrend;
|
||||
const xs = points.map((_, i) => (points.length === 1 ? 380 : 36 + (654 / (points.length - 1)) * i));
|
||||
const values = points.map((p) => p.value);
|
||||
const max = Math.max(...values);
|
||||
const min = Math.min(...values);
|
||||
const range = Math.max(max - min, 1);
|
||||
const plotH = INQUIRY_PLOT_BOTTOM - INQUIRY_PLOT_TOP;
|
||||
const yFor = (v: number) => INQUIRY_PLOT_BOTTOM - ((v - min) / range) * plotH;
|
||||
const coords = points.map((p, i) => ({
|
||||
x: xs[i],
|
||||
y: yFor(p.value),
|
||||
date: p.date,
|
||||
value: p.value,
|
||||
}));
|
||||
const line = coords.map((c) => `${c.x.toFixed(1)} ${c.y.toFixed(1)}`).join(" L");
|
||||
const area = `M${line} L${coords[coords.length - 1].x.toFixed(1)} ${INQUIRY_PLOT_BOTTOM} L${coords[0].x.toFixed(1)} ${INQUIRY_PLOT_BOTTOM} Z`;
|
||||
return { coords, line, area };
|
||||
});
|
||||
|
||||
const filteredRoutes = computed(() => costRoutes.filter((r) => selectedRoutes.value.includes(r.id)));
|
||||
|
||||
const rankingRows = computed(() => {
|
||||
let rows: RankingPerson[] = [...rankingPeople];
|
||||
if (rankingRole.value !== "all") rows = rows.filter((p) => p.role === rankingRole.value);
|
||||
if (rankingDept.value) rows = rows.filter((p) => p.dept === rankingDept.value);
|
||||
return rows.sort((a, b) => b.volumeNum - a.volumeNum || a.name.localeCompare(b.name, "zh-CN"));
|
||||
});
|
||||
|
||||
const costDates = ["06-12", "06-13", "06-14", "06-15", "06-16", "06-17", "06-18"];
|
||||
const costXs = [36, 145, 254, 363, 472, 581, 690];
|
||||
|
||||
function applyDateRange() {
|
||||
if (pickerMode.value === "day") dateDisplay.value = `${dateFrom.value}——${dateTo.value}`;
|
||||
else if (pickerMode.value === "month") dateDisplay.value = `${dateFromMonth.value}——${dateToMonth.value}`;
|
||||
else dateDisplay.value = `${dateFromYear.value}——${dateToYear.value}`;
|
||||
pickerOpen.value = false;
|
||||
}
|
||||
|
||||
function refreshData() {
|
||||
const now = new Date();
|
||||
const pad = (n: number) => String(n).padStart(2, "0");
|
||||
updatedTime.value = `${pad(now.getHours())}:${pad(now.getMinutes())}`;
|
||||
message.success("数据已更新");
|
||||
}
|
||||
|
||||
function toggleRoute(id: string) {
|
||||
const set = new Set(selectedRoutes.value);
|
||||
if (set.has(id)) set.delete(id);
|
||||
else set.add(id);
|
||||
selectedRoutes.value = [...set];
|
||||
}
|
||||
|
||||
function selectAllRoutes() {
|
||||
selectedRoutes.value = costRoutes.map((r) => r.id);
|
||||
}
|
||||
|
||||
function clearRoutes() {
|
||||
selectedRoutes.value = [];
|
||||
}
|
||||
|
||||
function showInquiryTip(date: string, value: number, x: number, y: number, wrapEl: HTMLElement | null) {
|
||||
if (!wrapEl) return;
|
||||
const box = wrapEl.getBoundingClientRect();
|
||||
activePoint.value = date;
|
||||
chartTip.value = {
|
||||
visible: true,
|
||||
left: `${(x / 760) * box.width}px`,
|
||||
top: `${(y / 240) * box.height}px`,
|
||||
date,
|
||||
value: `${value} 单/日`,
|
||||
lines: [],
|
||||
};
|
||||
}
|
||||
|
||||
function hideInquiryTip() {
|
||||
activePoint.value = null;
|
||||
chartTip.value.visible = false;
|
||||
}
|
||||
|
||||
function showCostTip(date: string, x: number, wrapEl: HTMLElement | null) {
|
||||
if (!wrapEl) return;
|
||||
const box = wrapEl.getBoundingClientRect();
|
||||
const routes = filteredRoutes.value;
|
||||
const lines =
|
||||
routes.length === 0
|
||||
? ["未选择线路"]
|
||||
: [
|
||||
...routes.slice(0, 3).map((r) => r.label),
|
||||
...(routes.length > 3 ? [`等 ${routes.length} 条`] : []),
|
||||
];
|
||||
costTip.value = {
|
||||
visible: true,
|
||||
left: `${(x / 760) * box.width}px`,
|
||||
top: `${(36 / 220) * box.height}px`,
|
||||
date,
|
||||
value: "",
|
||||
lines,
|
||||
};
|
||||
}
|
||||
|
||||
function hideCostTip() {
|
||||
costTip.value.visible = false;
|
||||
}
|
||||
|
||||
function exportRanking() {
|
||||
const roleLabels: Record<string, string> = { all: "全部", route: "航线", product: "产品", sales: "销售" };
|
||||
const header = ["排名", "人员", "部门", "岗位", "处理量"];
|
||||
const body = rankingRows.value.map((row, index) => [
|
||||
String(index + 1),
|
||||
row.name,
|
||||
row.dept,
|
||||
roleLabels[row.role] || row.role,
|
||||
`${row.volumeNum}单`,
|
||||
]);
|
||||
const csv = [header, ...body]
|
||||
.map((cols) => cols.map((col) => `"${String(col).replace(/"/g, '""')}"`).join(","))
|
||||
.join("\n");
|
||||
const blob = new Blob([`\ufeff${csv}`], { type: "text/csv;charset=utf-8;" });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const link = document.createElement("a");
|
||||
link.href = url;
|
||||
link.download = `人员排名_${roleLabels[rankingRole.value]}_${new Date().toISOString().slice(0, 10)}.csv`;
|
||||
link.click();
|
||||
URL.revokeObjectURL(url);
|
||||
message.success("已导出排名 CSV");
|
||||
}
|
||||
|
||||
function onDocClick(e: MouseEvent) {
|
||||
const t = e.target as HTMLElement;
|
||||
if (!t.closest(".month-range-picker")) pickerOpen.value = false;
|
||||
if (!t.closest(".ref-route-filter")) routeFilterOpen.value = false;
|
||||
}
|
||||
|
||||
const inquiryWrap = ref<HTMLElement | null>(null);
|
||||
const costWrap = ref<HTMLElement | null>(null);
|
||||
|
||||
onMounted(() => {
|
||||
chartReady.value = true;
|
||||
document.addEventListener("click", onDocClick);
|
||||
});
|
||||
onUnmounted(() => document.removeEventListener("click", onDocClick));
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- defer:等布局挂好 #page-topbar-actions 再传送,避免顶栏工具丢失 -->
|
||||
<Teleport defer to="#page-topbar-actions">
|
||||
<div class="dash-topbar-tools">
|
||||
<div class="month-range-picker" @click.stop>
|
||||
<div class="month-range-input" @click="pickerOpen = !pickerOpen">
|
||||
<span>{{ dateDisplay }}</span>
|
||||
<ProtoIcon name="calendar" :size="14" style="color: #94a3b8" />
|
||||
</div>
|
||||
<div v-show="pickerOpen" class="month-range-dropdown dash-picker-dropdown">
|
||||
<div class="month-range-tabs">
|
||||
<button type="button" class="month-range-tab" :class="{ 'is-active': pickerMode === 'day' }" @click="pickerMode = 'day'">按日</button>
|
||||
<button type="button" class="month-range-tab" :class="{ 'is-active': pickerMode === 'month' }" @click="pickerMode = 'month'">按月</button>
|
||||
<button type="button" class="month-range-tab" :class="{ 'is-active': pickerMode === 'year' }" @click="pickerMode = 'year'">按年</button>
|
||||
</div>
|
||||
<div class="dash-picker-body">
|
||||
<template v-if="pickerMode === 'day'">
|
||||
<input v-model="dateFrom" type="date" class="dash-picker-input" />
|
||||
<input v-model="dateTo" type="date" class="dash-picker-input" />
|
||||
</template>
|
||||
<template v-else-if="pickerMode === 'month'">
|
||||
<input v-model="dateFromMonth" type="month" class="dash-picker-input" />
|
||||
<input v-model="dateToMonth" type="month" class="dash-picker-input" />
|
||||
</template>
|
||||
<template v-else>
|
||||
<input v-model="dateFromYear" type="number" min="2020" max="2035" class="dash-picker-input" placeholder="起始年" />
|
||||
<input v-model="dateToYear" type="number" min="2020" max="2035" class="dash-picker-input" placeholder="结束年" />
|
||||
</template>
|
||||
<button type="button" class="btn btn-primary" style="width: 100%; min-height: 32px; height: 32px" @click="applyDateRange">确定</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dashboard-data-note" aria-live="polite">
|
||||
<span class="dashboard-data-note__icon"><ProtoIcon name="clock" :size="13" /></span>
|
||||
<span class="dashboard-data-note__time">数据更新时间{{ updatedTime }}</span>
|
||||
</div>
|
||||
<button type="button" class="btn btn-primary dash-refresh-btn" @click="refreshData">
|
||||
<ProtoIcon name="refresh" :size="14" />
|
||||
更新数据
|
||||
</button>
|
||||
</div>
|
||||
</Teleport>
|
||||
|
||||
<!-- 1. 九指标 -->
|
||||
<div class="ref-surface ref-surface--metrics">
|
||||
<div class="ref-header ref-header--compact">
|
||||
<div>
|
||||
<h2 class="ref-title">询价运营表现总览</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ref-metrics-grid">
|
||||
<div v-for="m in todayMetrics" :key="m.key" class="ref-metric-card">
|
||||
<div class="ref-metric-card__label">
|
||||
<span>{{ m.label }}</span>
|
||||
<button type="button" class="ref-metric-help" :aria-label="m.label + '计算逻辑'">
|
||||
<ProtoIcon name="help" :size="12" />
|
||||
<span class="ref-metric-tip">{{ m.tip }}</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="ref-metric-card__value" :class="{ 'is-time': m.isTime }">{{ m.value }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 2. 双图 -->
|
||||
<div class="ref-surface">
|
||||
<div class="ref-charts-section">
|
||||
<div class="ref-chart-panel">
|
||||
<div class="ref-chart-panel__head">
|
||||
<div class="ref-section-title dash-chart-title">
|
||||
<ProtoIcon name="lineChart" :size="14" class="dash-chart-title__icon" />
|
||||
询价单量趋势图
|
||||
</div>
|
||||
</div>
|
||||
<div class="ref-chart-panel__body">
|
||||
<div ref="inquiryWrap" class="ref-chart-wrap">
|
||||
<svg v-if="chartReady" class="ref-chart" viewBox="0 0 760 240" role="img" aria-label="近七天询价趋势">
|
||||
<defs>
|
||||
<linearGradient id="refTrendFill" x1="0" x2="0" y1="0" y2="1">
|
||||
<stop offset="0%" stop-color="#155eef" stop-opacity="0.18" />
|
||||
<stop offset="100%" stop-color="#155eef" stop-opacity="0" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<path :d="inquirySvg.area" fill="url(#refTrendFill)" />
|
||||
<path
|
||||
:d="`M${inquirySvg.line}`"
|
||||
fill="none"
|
||||
stroke="#155eef"
|
||||
stroke-width="4"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path d="M36 188 H724 M36 137 H724 M36 86 H724 M36 36 H724" stroke="#e8edf4" stroke-width="1" />
|
||||
<g fill="#64748b" font-size="8" font-weight="800" text-anchor="middle">
|
||||
<text v-for="c in inquirySvg.coords" :key="'d-' + c.date" :x="c.x" y="218">{{ c.date }}</text>
|
||||
</g>
|
||||
<g fill="#0f172a" font-size="8" font-weight="800" text-anchor="middle">
|
||||
<text v-for="c in inquirySvg.coords" :key="'v-' + c.date" :x="c.x" :y="c.y - 11">{{ c.value }}</text>
|
||||
</g>
|
||||
<g
|
||||
v-for="c in inquirySvg.coords"
|
||||
:key="'p-' + c.date"
|
||||
class="ref-chart-point"
|
||||
:class="{ 'is-active': activePoint === c.date }"
|
||||
@mouseenter="showInquiryTip(c.date, c.value, c.x, c.y, inquiryWrap)"
|
||||
@mouseleave="hideInquiryTip"
|
||||
>
|
||||
<!-- 加大热区,方便悬停出提示 -->
|
||||
<circle :cx="c.x" :cy="c.y" r="14" fill="transparent" />
|
||||
<circle :cx="c.x" :cy="c.y" r="4" fill="#155eef" stroke="#ffffff" stroke-width="3" />
|
||||
</g>
|
||||
</svg>
|
||||
<div
|
||||
class="ref-chart-tooltip"
|
||||
:class="{ 'is-visible': chartTip.visible }"
|
||||
:style="{ left: chartTip.left, top: chartTip.top }"
|
||||
>
|
||||
<div class="date">{{ chartTip.date }}</div>
|
||||
<div class="value">{{ chartTip.value }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ref-chart-panel">
|
||||
<div class="ref-chart-panel__head">
|
||||
<div class="ref-section-title dash-chart-title">
|
||||
<ProtoIcon name="trending" :size="14" class="dash-chart-title__icon" />
|
||||
成本趋势图
|
||||
</div>
|
||||
<div class="ref-route-filter" @click.stop>
|
||||
<button
|
||||
type="button"
|
||||
class="ref-route-filter__trigger"
|
||||
:class="{ 'is-open': routeFilterOpen }"
|
||||
@click="routeFilterOpen = !routeFilterOpen"
|
||||
>
|
||||
<ProtoIcon name="route" :size="14" />
|
||||
<span>线路筛选</span>
|
||||
<span class="ref-route-filter__badge">{{ selectedRoutes.length }}</span>
|
||||
<ProtoIcon name="chevron" :size="14" class="ref-route-filter__arrow" />
|
||||
</button>
|
||||
<div v-show="routeFilterOpen" class="ref-route-filter__menu dash-route-menu">
|
||||
<div class="ref-route-filter__menu-head">
|
||||
<div>
|
||||
<div class="ref-route-filter__menu-title">选择对比线路</div>
|
||||
<div class="ref-route-filter__menu-sub">已选 {{ selectedRoutes.length }} / {{ costRoutes.length }}</div>
|
||||
</div>
|
||||
<div class="tabs-pill ref-route-filter__actions">
|
||||
<button type="button" @click="selectAllRoutes">全选</button>
|
||||
<button type="button" @click="clearRoutes">清空</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ref-route-filter__chips">
|
||||
<label
|
||||
v-for="r in costRoutes"
|
||||
:key="r.id"
|
||||
class="ref-route-chip"
|
||||
@click.prevent="toggleRoute(r.id)"
|
||||
>
|
||||
<input type="checkbox" :checked="selectedRoutes.includes(r.id)" />
|
||||
<span class="ref-route-chip__dot" :style="{ '--route-color': r.color }" />
|
||||
<span class="ref-route-chip__text">{{ r.label }}</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ref-chart-panel__body">
|
||||
<div ref="costWrap" id="costChartWrap" class="ref-chart-wrap">
|
||||
<svg class="ref-chart" viewBox="0 0 760 220" role="img" aria-label="成本趋势图">
|
||||
<path d="M36 186 H724 M36 136 H724 M36 86 H724 M36 36 H724" stroke="#e8edf4" stroke-width="1" />
|
||||
<g fill="#94a3b8" font-size="8" font-weight="700" text-anchor="end">
|
||||
<text x="30" y="40">高</text>
|
||||
<text x="30" y="90">—</text>
|
||||
<text x="30" y="140">—</text>
|
||||
<text x="30" y="190">低</text>
|
||||
</g>
|
||||
<path
|
||||
v-for="r in filteredRoutes"
|
||||
:key="r.id"
|
||||
class="cost-line"
|
||||
:d="r.path"
|
||||
fill="none"
|
||||
:stroke="r.color"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<g
|
||||
v-for="(x, i) in costXs"
|
||||
:key="'cx-' + costDates[i]"
|
||||
class="cost-point"
|
||||
@mouseenter="showCostTip(costDates[i], x, costWrap)"
|
||||
@mouseleave="hideCostTip"
|
||||
>
|
||||
<rect :x="x - 40" y="28" width="80" height="165" fill="transparent" style="cursor: pointer" />
|
||||
</g>
|
||||
<g fill="#64748b" font-size="8" font-weight="800" text-anchor="middle">
|
||||
<text v-for="(d, i) in costDates" :key="d" :x="costXs[i]" y="214">{{ d }}</text>
|
||||
</g>
|
||||
</svg>
|
||||
<div
|
||||
class="ref-chart-tooltip is-cost"
|
||||
:class="{ 'is-visible': costTip.visible }"
|
||||
:style="{ left: costTip.left, top: costTip.top }"
|
||||
>
|
||||
<div class="date">{{ costTip.date }}</div>
|
||||
<div class="value">
|
||||
<span v-for="(line, i) in costTip.lines" :key="i" class="value-line">{{ line }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dash-cost-footnote">成本数据来源于 TMS</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 3. 人员排名 -->
|
||||
<div class="ref-surface">
|
||||
<div class="ref-panel-head ref-ranking-panel-head">
|
||||
<div class="ref-section-title ref-ranking-section-title">
|
||||
<ProtoIcon name="gauge" :size="16" class="ref-ranking-icon dash-chart-title__icon" />
|
||||
人员排名
|
||||
</div>
|
||||
<div class="ref-ranking-panel-actions">
|
||||
<select v-model="rankingDept" class="ref-ranking-dept-select" aria-label="部门筛选">
|
||||
<option value="">全部部门</option>
|
||||
<option v-for="d in rankingDepts" :key="d" :value="d">{{ d }}</option>
|
||||
</select>
|
||||
<div class="tabs-pill" role="tablist" aria-label="人员角色">
|
||||
<button type="button" :class="{ 'is-active': rankingRole === 'all' }" @click="rankingRole = 'all'">全部</button>
|
||||
<button type="button" :class="{ 'is-active': rankingRole === 'sales' }" @click="rankingRole = 'sales'">销售</button>
|
||||
<button type="button" :class="{ 'is-active': rankingRole === 'route' }" @click="rankingRole = 'route'">航线</button>
|
||||
<button type="button" :class="{ 'is-active': rankingRole === 'product' }" @click="rankingRole = 'product'">产品</button>
|
||||
</div>
|
||||
<button type="button" class="btn btn-secondary ref-ranking-export-btn" @click="exportRanking">
|
||||
<ProtoIcon name="download" :size="16" />
|
||||
导出
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ref-ranking-table-wrap">
|
||||
<div class="ref-ranking-list">
|
||||
<div class="ref-ranking-head">
|
||||
<div class="ref-ranking-info">
|
||||
<span class="col-rank">排名</span>
|
||||
<span class="col-name">人员</span>
|
||||
<span class="col-dept">部门</span>
|
||||
</div>
|
||||
<span class="col-volume">处理量</span>
|
||||
</div>
|
||||
<div v-if="!rankingRows.length" class="ref-ranking-empty">暂无符合条件的人员数据</div>
|
||||
<div v-for="(row, index) in rankingRows" :key="row.name + row.dept" class="ref-ranking-row">
|
||||
<div class="ref-ranking-info">
|
||||
<span class="col-rank"><span class="ref-rank" :class="{ top: index < 2 }">{{ index + 1 }}</span></span>
|
||||
<span class="col-name">{{ row.name }}</span>
|
||||
<span class="col-dept">{{ row.dept }}</span>
|
||||
</div>
|
||||
<span class="col-volume">{{ row.volumeNum }} 单</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.dash-topbar-tools {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.dash-picker-dropdown {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: calc(100% + 8px);
|
||||
width: 300px;
|
||||
z-index: 80;
|
||||
}
|
||||
|
||||
.dash-picker-body {
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
padding-top: 4px;
|
||||
}
|
||||
|
||||
.dash-picker-input {
|
||||
width: 100%;
|
||||
height: 32px;
|
||||
padding: 0 10px;
|
||||
border: 1px solid #dfe5ee;
|
||||
border-radius: 7px;
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.dash-refresh-btn {
|
||||
height: 34px;
|
||||
min-height: 34px;
|
||||
}
|
||||
|
||||
.dash-chart-title {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
font-size: 11px;
|
||||
font-weight: 900;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.dash-chart-title__icon {
|
||||
color: #1d4ed8;
|
||||
}
|
||||
|
||||
.dash-route-menu {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: calc(100% + 8px);
|
||||
}
|
||||
|
||||
.dash-cost-footnote {
|
||||
margin-top: 12px;
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
color: #94a3b8;
|
||||
}
|
||||
|
||||
.ref-surface--metrics,
|
||||
.ref-surface--metrics .ref-metrics-grid,
|
||||
.ref-surface--metrics .ref-metric-card {
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.ref-surface--metrics .ref-metric-card:hover {
|
||||
z-index: 5;
|
||||
}
|
||||
|
||||
.ref-chart-wrap {
|
||||
position: relative;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
/* 悬停提示改为不撑开文档宽度 */
|
||||
.ref-chart-tooltip {
|
||||
max-width: min(168px, 40vw);
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user