|
@@ -317,12 +317,12 @@
|
|
|
场景改用环形图,中心读数合计)。扇区与图例按语义 tone 着色(行业行=行业色)。
|
|
场景改用环形图,中心读数合计)。扇区与图例按语义 tone 着色(行业行=行业色)。
|
|
|
带 `action` 时图例行可点(联动写全局筛选,与条形图同一条接缝)。
|
|
带 `action` 时图例行可点(联动写全局筛选,与条形图同一条接缝)。
|
|
|
与上方饼图的分工:饼图是点位状态分布专用(选中高亮 + 地图图例同源)。
|
|
与上方饼图的分工:饼图是点位状态分布专用(选中高亮 + 地图图例同源)。
|
|
|
- 票 43 交互补齐:扇区可键盘聚焦(`tabindex` + 每片自己的可访问名称),
|
|
|
|
|
- focus 与 hover 走**同一个**共享详情浮层、同一档放大反馈;
|
|
|
|
|
- 详情入口只留 `aria-label` 一个(去掉原生 `<title>`,与票 37 给趋势点定的
|
|
|
|
|
- 成例一致——原生提示有延迟且与自绘浮层双重叠加)。
|
|
|
|
|
- 外层 svg 由 `role="img"` 改 `role="group"`:`img` 会把子树对辅助技术整棵
|
|
|
|
|
- 藏掉,扇区名称与焦点就读不出来。
|
|
|
|
|
|
|
+ 票 43(重开)交互修复:可见环带太窄(42/26),真实指针落在中心孔或
|
|
|
|
|
+ 环带外都不触发——「只有悬停图例才出详情」的根因。改画**透明整楔命中区**
|
|
|
|
|
+ (`hitPath`,内半径 0)承接悬停/聚焦,热区与视觉扇区同角度同边界;
|
|
|
|
|
+ 扇区 ⇄ 图例**双向联动**(任一侧悬停/聚焦,两侧同时亮 + 同一个共享浮层),
|
|
|
|
|
+ 手感对齐已验收横条统计(整行即热区)。键盘焦点在命中楔形上,
|
|
|
|
|
+ 可访问名称=「类别:数量」;中心合计 `pointer-events:none` 让位给命中区。
|
|
|
-->
|
|
-->
|
|
|
<div v-else-if="block.kind === 'donut'" class="panel-donut" data-donut-list>
|
|
<div v-else-if="block.kind === 'donut'" class="panel-donut" data-donut-list>
|
|
|
<template v-if="block.total > 0">
|
|
<template v-if="block.total > 0">
|
|
@@ -332,16 +332,25 @@
|
|
|
v-for="slice in block.slices"
|
|
v-for="slice in block.slices"
|
|
|
:key="slice.key"
|
|
:key="slice.key"
|
|
|
class="panel-donut__slice"
|
|
class="panel-donut__slice"
|
|
|
|
|
+ :class="{ 'is-hovered': isChartHovered(chartScope(index, block), slice.key) }"
|
|
|
:d="slice.path"
|
|
:d="slice.path"
|
|
|
:fill="barColor(slice.tone)"
|
|
:fill="barColor(slice.tone)"
|
|
|
:data-donut-slice="slice.key"
|
|
:data-donut-slice="slice.key"
|
|
|
|
|
+ aria-hidden="true"
|
|
|
|
|
+ ></path>
|
|
|
|
|
+ <path
|
|
|
|
|
+ v-for="slice in block.slices"
|
|
|
|
|
+ :key="`hit-${slice.key}`"
|
|
|
|
|
+ class="panel-donut__hit"
|
|
|
|
|
+ :d="slice.hitPath"
|
|
|
|
|
+ :data-donut-hit="slice.key"
|
|
|
tabindex="0"
|
|
tabindex="0"
|
|
|
role="img"
|
|
role="img"
|
|
|
:aria-label="`${slice.label}:${slice.count}`"
|
|
:aria-label="`${slice.label}:${slice.count}`"
|
|
|
- @mouseenter="showChartTip($event, slice.label, slice.count, barColor(slice.tone))"
|
|
|
|
|
- @mouseleave="hideChartTip"
|
|
|
|
|
- @focus="showChartTip($event, slice.label, slice.count, barColor(slice.tone))"
|
|
|
|
|
- @blur="hideChartTip"
|
|
|
|
|
|
|
+ @mouseenter="showChartTip($event, slice.label, slice.count, barColor(slice.tone)); setChartHover(chartScope(index, block), slice.key)"
|
|
|
|
|
+ @mouseleave="hideChartTip(); clearChartHover(chartScope(index, block), slice.key)"
|
|
|
|
|
+ @focus="showChartTip($event, slice.label, slice.count, barColor(slice.tone)); setChartHover(chartScope(index, block), slice.key)"
|
|
|
|
|
+ @blur="hideChartTip(); clearChartHover(chartScope(index, block), slice.key)"
|
|
|
></path>
|
|
></path>
|
|
|
</svg>
|
|
</svg>
|
|
|
<b class="panel-donut__total" data-donut-total>{{ block.total }}</b>
|
|
<b class="panel-donut__total" data-donut-total>{{ block.total }}</b>
|
|
@@ -352,14 +361,14 @@
|
|
|
v-for="row in block.rows"
|
|
v-for="row in block.rows"
|
|
|
:key="row.key"
|
|
:key="row.key"
|
|
|
class="panel-donut__row"
|
|
class="panel-donut__row"
|
|
|
- :class="{ 'panel-donut__row--button': block.action && row.actionKey }"
|
|
|
|
|
|
|
+ :class="{ 'panel-donut__row--button': block.action && row.actionKey, 'is-hovered': isChartHovered(chartScope(index, block), row.key) }"
|
|
|
:type="block.action && row.actionKey ? 'button' : null"
|
|
:type="block.action && row.actionKey ? 'button' : null"
|
|
|
:data-donut-row="row.key"
|
|
:data-donut-row="row.key"
|
|
|
:data-donut-action="block.action || null"
|
|
:data-donut-action="block.action || null"
|
|
|
:title="block.action && row.actionKey ? `把「${row.label}」设为全局筛选条件` : null"
|
|
:title="block.action && row.actionKey ? `把「${row.label}」设为全局筛选条件` : null"
|
|
|
@click="block.action && row.actionKey ? emit('panel-action', { kind: block.action, key: row.actionKey }) : null"
|
|
@click="block.action && row.actionKey ? emit('panel-action', { kind: block.action, key: row.actionKey }) : null"
|
|
|
- @mouseenter="showChartTip($event, row.label, row.count, barColor(row.tone))"
|
|
|
|
|
- @mouseleave="hideChartTip"
|
|
|
|
|
|
|
+ @mouseenter="showChartTip($event, row.label, row.count, barColor(row.tone)); setChartHover(chartScope(index, block), row.key)"
|
|
|
|
|
+ @mouseleave="hideChartTip(); clearChartHover(chartScope(index, block), row.key)"
|
|
|
>
|
|
>
|
|
|
<i class="panel-donut__swatch" :style="{ background: barColor(row.tone) }" aria-hidden="true"></i>
|
|
<i class="panel-donut__swatch" :style="{ background: barColor(row.tone) }" aria-hidden="true"></i>
|
|
|
<EllipsisText class="panel-donut__label" :text="row.label" />
|
|
<EllipsisText class="panel-donut__label" :text="row.label" />
|
|
@@ -374,9 +383,9 @@
|
|
|
票 14:状态分布**饼图**(决策 02 的 Q12/Q19)。消费全局未筛分布(与顶部
|
|
票 14:状态分布**饼图**(决策 02 的 Q12/Q19)。消费全局未筛分布(与顶部
|
|
|
状态栏共用同一份),只**高亮当前选中**——它不是筛选入口,不可点;
|
|
状态栏共用同一份),只**高亮当前选中**——它不是筛选入口,不可点;
|
|
|
全局条件唯一可操作面在地图顶部状态栏。
|
|
全局条件唯一可操作面在地图顶部状态栏。
|
|
|
- 票 43:不可点 ≠ 不可读。扇区给键盘焦点与可访问名称,focus/hover 同一档
|
|
|
|
|
- 放大反馈 + 同一个共享详情浮层(类别与数量);外层 svg 改 `role="group"`
|
|
|
|
|
- 让扇区名称可达(理由见环形图注释)。
|
|
|
|
|
|
|
+ 票 43(重开):与环形图同一修复——透明整楔命中区(`hitPath`)承接
|
|
|
|
|
+ 悬停/聚焦(可见环带太窄是「只有图例触发」的根因),扇区 ⇄ 图例双向
|
|
|
|
|
+ 联动,浮层与放大两侧同步;外层 svg `role="group"` 让名称可达。
|
|
|
-->
|
|
-->
|
|
|
<div v-else-if="block.kind === 'pie'" class="panel-pie" data-pie>
|
|
<div v-else-if="block.kind === 'pie'" class="panel-pie" data-pie>
|
|
|
<template v-if="block.ready && block.total > 0">
|
|
<template v-if="block.ready && block.total > 0">
|
|
@@ -385,18 +394,26 @@
|
|
|
v-for="slice in block.slices"
|
|
v-for="slice in block.slices"
|
|
|
:key="slice.key"
|
|
:key="slice.key"
|
|
|
class="panel-pie__slice"
|
|
class="panel-pie__slice"
|
|
|
- :class="{ 'is-dim': !slice.selected && block.hasSelection }"
|
|
|
|
|
|
|
+ :class="{ 'is-dim': !slice.selected && block.hasSelection, 'is-hovered': isChartHovered(chartScope(index, block), slice.key) }"
|
|
|
:d="slice.path"
|
|
:d="slice.path"
|
|
|
:fill="pieToneColor(slice.tone, slice.key)"
|
|
:fill="pieToneColor(slice.tone, slice.key)"
|
|
|
:data-pie-slice="slice.key"
|
|
:data-pie-slice="slice.key"
|
|
|
:data-pie-selected="slice.selected ? 'yes' : 'no'"
|
|
:data-pie-selected="slice.selected ? 'yes' : 'no'"
|
|
|
|
|
+ aria-hidden="true"
|
|
|
|
|
+ ></path>
|
|
|
|
|
+ <path
|
|
|
|
|
+ v-for="slice in block.slices"
|
|
|
|
|
+ :key="`hit-${slice.key}`"
|
|
|
|
|
+ class="panel-pie__hit"
|
|
|
|
|
+ :d="slice.hitPath"
|
|
|
|
|
+ :data-pie-hit="slice.key"
|
|
|
tabindex="0"
|
|
tabindex="0"
|
|
|
role="img"
|
|
role="img"
|
|
|
:aria-label="`${slice.label}:${slice.count}`"
|
|
:aria-label="`${slice.label}:${slice.count}`"
|
|
|
- @mouseenter="showChartTip($event, slice.label, slice.count, pieToneColor(slice.tone, slice.key))"
|
|
|
|
|
- @mouseleave="hideChartTip"
|
|
|
|
|
- @focus="showChartTip($event, slice.label, slice.count, pieToneColor(slice.tone, slice.key))"
|
|
|
|
|
- @blur="hideChartTip"
|
|
|
|
|
|
|
+ @mouseenter="showChartTip($event, slice.label, slice.count, pieToneColor(slice.tone, slice.key)); setChartHover(chartScope(index, block), slice.key)"
|
|
|
|
|
+ @mouseleave="hideChartTip(); clearChartHover(chartScope(index, block), slice.key)"
|
|
|
|
|
+ @focus="showChartTip($event, slice.label, slice.count, pieToneColor(slice.tone, slice.key)); setChartHover(chartScope(index, block), slice.key)"
|
|
|
|
|
+ @blur="hideChartTip(); clearChartHover(chartScope(index, block), slice.key)"
|
|
|
></path>
|
|
></path>
|
|
|
</svg>
|
|
</svg>
|
|
|
<ul class="panel-pie__legend" data-pie-legend>
|
|
<ul class="panel-pie__legend" data-pie-legend>
|
|
@@ -404,11 +421,11 @@
|
|
|
v-for="row in block.rows"
|
|
v-for="row in block.rows"
|
|
|
:key="row.key"
|
|
:key="row.key"
|
|
|
class="panel-pie__row"
|
|
class="panel-pie__row"
|
|
|
- :class="{ 'is-selected': row.selected }"
|
|
|
|
|
|
|
+ :class="{ 'is-selected': row.selected, 'is-hovered': isChartHovered(chartScope(index, block), row.key) }"
|
|
|
:data-pie-row="row.key"
|
|
:data-pie-row="row.key"
|
|
|
:data-pie-selected="row.selected ? 'yes' : 'no'"
|
|
:data-pie-selected="row.selected ? 'yes' : 'no'"
|
|
|
- @mouseenter="showChartTip($event, row.label, row.count, pieToneColor(row.tone, row.key))"
|
|
|
|
|
- @mouseleave="hideChartTip"
|
|
|
|
|
|
|
+ @mouseenter="showChartTip($event, row.label, row.count, pieToneColor(row.tone, row.key)); setChartHover(chartScope(index, block), row.key)"
|
|
|
|
|
+ @mouseleave="hideChartTip(); clearChartHover(chartScope(index, block), row.key)"
|
|
|
>
|
|
>
|
|
|
<StatusTag :tone="row.tone" :shape="row.shape" :label="row.label" :style="{ '--tag-color': pieToneColor(row.tone, row.key) }" />
|
|
<StatusTag :tone="row.tone" :shape="row.shape" :label="row.label" :style="{ '--tag-color': pieToneColor(row.tone, row.key) }" />
|
|
|
<b class="panel-pie__count" data-pie-count>{{ row.count }}</b>
|
|
<b class="panel-pie__count" data-pie-count>{{ row.count }}</b>
|
|
@@ -538,7 +555,7 @@
|
|
|
</template>
|
|
</template>
|
|
|
|
|
|
|
|
<script setup>
|
|
<script setup>
|
|
|
-import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue'
|
|
|
|
|
|
|
+import { computed, onBeforeUnmount, onMounted, ref, shallowRef, watch } from 'vue'
|
|
|
import { DETAIL_PLACEHOLDER } from '../domain/detailFlow.js'
|
|
import { DETAIL_PLACEHOLDER } from '../domain/detailFlow.js'
|
|
|
import { sectionPending } from '../domain/panelBlocks.js'
|
|
import { sectionPending } from '../domain/panelBlocks.js'
|
|
|
import CockpitIcon from './CockpitIcon.vue'
|
|
import CockpitIcon from './CockpitIcon.vue'
|
|
@@ -631,6 +648,47 @@ function pieToneColor(tone, key) {
|
|
|
/** 当前悬停的图表详情(`{ label, value, color, x, y }`;null = 无悬停)。 */
|
|
/** 当前悬停的图表详情(`{ label, value, color, x, y }`;null = 无悬停)。 */
|
|
|
const chartTip = ref(null)
|
|
const chartTip = ref(null)
|
|
|
|
|
|
|
|
|
|
+/**
|
|
|
|
|
+ * 票 43(第三轮根因):联动状态必须用 **shallowRef**,不能用 `ref`。
|
|
|
|
|
+ * `ref(null)` 存入 `{owner: block}` 后会被 Vue **深层响应式代理**——读回的
|
|
|
|
|
+ * `chartHover.value.owner` 是 block 的 reactive 代理,而模板 v-for 拿到的 `block`
|
|
|
|
|
+ * 是原始对象,`===` 恒 false → `.is-hovered` 永不生效(扇区不放大、图例不亮),
|
|
|
|
|
+ * 而浮层不依赖这个比较所以照常出现——这正是维护者看到的「只有详情、没有联动」。
|
|
|
|
|
+ * `shallowRef` 不做深层包装(整体替换即触发渲染)。
|
|
|
|
|
+ * (第四轮进一步发现:光保持身份还不够,见下方 `chartScope`——键根本不该是对象。)
|
|
|
|
|
+ */
|
|
|
|
|
+const chartHover = shallowRef(null)
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 票 43(第四轮根因):**不能拿对象身份当键**。外壳时钟每秒 `clockText` 变化会重渲染,
|
|
|
|
|
+ * 而 `:view="panelViewOf(panelId)"` 是模板里的**方法调用**——每次重渲染都重算出一批全新的
|
|
|
|
|
+ * block 对象。于是鼠标没动、hover 还在,下一次 tick 的 `block` 已经不是存进去的那个,
|
|
|
|
|
+ * `owner === block` 静默变 false → 放大与联动高亮"自己消失"(浮层存的是标量所以不受影响,
|
|
|
|
|
+ * 症状正是「详情还在、高亮掉了」)。
|
|
|
|
|
+ * 修法=键换成**跨重算稳定的标量 scope**:视图名 + 块序号 + 块类型 + 块标题。
|
|
|
|
|
+ * 这四样在同一面板同一块的数据刷新前后不变,既不会因重渲染漂移,
|
|
|
|
|
+ * 也仍区分并排的同类图(视图名/序号不同)与同块内不同行(key 不同)。
|
|
|
|
|
+ */
|
|
|
|
|
+function chartScope(index, block) {
|
|
|
|
|
+ return `${props.view.name}|${index}|${block.kind}|${block.label || ''}`
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function setChartHover(scope, key) {
|
|
|
|
|
+ chartHover.value = { scope, key }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function clearChartHover(scope, key) {
|
|
|
|
|
+ const current = chartHover.value
|
|
|
|
|
+ if (current && current.scope === scope && current.key === key) {
|
|
|
|
|
+ chartHover.value = null
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function isChartHovered(scope, key) {
|
|
|
|
|
+ const current = chartHover.value
|
|
|
|
|
+ return !!current && current.scope === scope && current.key === key
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
/** 图表元素悬停 → 记录详情与浮层锚点(相对 `.panel-blocks` 根,避免随滚动漂移)。 */
|
|
/** 图表元素悬停 → 记录详情与浮层锚点(相对 `.panel-blocks` 根,避免随滚动漂移)。 */
|
|
|
function showChartTip(event, label, value, color) {
|
|
function showChartTip(event, label, value, color) {
|
|
|
const root = rootRef.value
|
|
const root = rootRef.value
|
|
@@ -1278,6 +1336,9 @@ onBeforeUnmount(() => {
|
|
|
inset: 0;
|
|
inset: 0;
|
|
|
display: grid;
|
|
display: grid;
|
|
|
place-items: center;
|
|
place-items: center;
|
|
|
|
|
+ /* 票 43(重开):中心读数让位给整楔命中区——它盖在圆心上方,
|
|
|
|
|
+ 不关掉指针事件的话「悬停饼图中心」永远触发不了最近的扇区。 */
|
|
|
|
|
+ pointer-events: none;
|
|
|
color: var(--text-strong, #fff);
|
|
color: var(--text-strong, #fff);
|
|
|
font-family: var(--num, monospace);
|
|
font-family: var(--num, monospace);
|
|
|
font-size: var(--fs-body, 14px);
|
|
font-size: var(--fs-body, 14px);
|
|
@@ -1328,13 +1389,17 @@ onBeforeUnmount(() => {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.panel-donut__label {
|
|
.panel-donut__label {
|
|
|
|
|
+ /* 票 43(重开):数值紧跟标签成对。`flex: 0 1 auto` 而不是 `1 1 auto`——
|
|
|
|
|
+ 会生长的标签把自己撑满整行、把数值又顶回行尾(正是维护者说的"离得太远");
|
|
|
|
|
+ 只收缩才能在"标签+数值"之间零间隙成对,同时窄面板里仍可省略。 */
|
|
|
|
|
+ flex: 0 1 auto;
|
|
|
|
|
+ min-width: 0;
|
|
|
color: var(--text-2, #88b7e8);
|
|
color: var(--text-2, #88b7e8);
|
|
|
font-size: var(--fs-caption, 10.5px);
|
|
font-size: var(--fs-caption, 10.5px);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.panel-donut__count {
|
|
.panel-donut__count {
|
|
|
flex: none;
|
|
flex: none;
|
|
|
- margin-left: auto;
|
|
|
|
|
color: var(--text, #d7ecff);
|
|
color: var(--text, #d7ecff);
|
|
|
font-family: var(--num, monospace);
|
|
font-family: var(--num, monospace);
|
|
|
font-size: var(--fs-caption, 10.5px);
|
|
font-size: var(--fs-caption, 10.5px);
|
|
@@ -1419,8 +1484,11 @@ onBeforeUnmount(() => {
|
|
|
.panel-pie__row {
|
|
.panel-pie__row {
|
|
|
display: flex;
|
|
display: flex;
|
|
|
align-items: center;
|
|
align-items: center;
|
|
|
- justify-content: space-between;
|
|
|
|
|
|
|
+ /* 票 43(重开):数值紧跟标签成对(原 space-between 把数字顶到最右,
|
|
|
|
|
+ 296px 面板里标签与数值隔整行宽,没有关联感)。与堆叠图例同一贴法。 */
|
|
|
gap: var(--space-2, 8px);
|
|
gap: var(--space-2, 8px);
|
|
|
|
|
+ min-width: 0;
|
|
|
|
|
+ border-radius: 2px;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.panel-pie__row.is-selected .panel-pie__count {
|
|
.panel-pie__row.is-selected .panel-pie__count {
|
|
@@ -1429,6 +1497,7 @@ onBeforeUnmount(() => {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.panel-pie__count {
|
|
.panel-pie__count {
|
|
|
|
|
+ flex: none;
|
|
|
color: var(--text);
|
|
color: var(--text);
|
|
|
font-family: var(--num, monospace);
|
|
font-family: var(--num, monospace);
|
|
|
font-size: var(--fs-caption, 10.5px);
|
|
font-size: var(--fs-caption, 10.5px);
|
|
@@ -1618,42 +1687,70 @@ onBeforeUnmount(() => {
|
|
|
z-index: 1;
|
|
z-index: 1;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-/* 环形图扇区:沿圆心向外放大(transform-origin 在 SVG path 上按各自中点近似取中心即可,
|
|
|
|
|
- 100×100 视框下 1.06 的放大刚好把扇区从环带里"顶"出来而不遮邻居)。
|
|
|
|
|
- 票 43:键盘焦点与悬停同一档反馈(放大 + 增亮),另加一圈描边让焦点位置可见。 */
|
|
|
|
|
|
|
+/* 环形图扇区:沿圆心向外放大(transform-origin 在 SVG path 上按各自中点近似取中心即可)。
|
|
|
|
|
+ 票 43(重开):放大/增亮改由 `.is-hovered` 驱动(联动状态),不再是扇区自己的 `:hover` ——
|
|
|
|
|
+ 指针事件现在落在透明命中楔形上,可见扇区收不到 hover;命中区与图例行都写同一个联动状态,
|
|
|
|
|
+ 所以「悬停扇区 ⇄ 悬停图例」两侧同时亮(对齐横条统计的整行热区手感)。
|
|
|
|
|
+ 票 43(第三轮):放大档从 1.06 加到 **1.1 + 提亮 1.35 + 白描边**——维护者反馈
|
|
|
|
|
+ 「只出详情、没有放大」;除联动状态本身失效(shallowRef 根因)外,1.06 在 72px 图上
|
|
|
|
|
+ 只有 ~2px 位移也不够"明显"。命中楔形:透明填充、只接收指针;键盘焦点环画在它身上。 */
|
|
|
.panel-donut__slice {
|
|
.panel-donut__slice {
|
|
|
- cursor: default;
|
|
|
|
|
- transition: transform 0.15s;
|
|
|
|
|
|
|
+ transition: transform 0.15s, filter 0.15s;
|
|
|
transform-origin: 50px 50px;
|
|
transform-origin: 50px 50px;
|
|
|
transform-box: view-box;
|
|
transform-box: view-box;
|
|
|
}
|
|
}
|
|
|
-.panel-donut__slice:hover {
|
|
|
|
|
- transform: scale(1.06);
|
|
|
|
|
- filter: brightness(1.2);
|
|
|
|
|
|
|
+.panel-donut__slice.is-hovered {
|
|
|
|
|
+ transform: scale(1.1);
|
|
|
|
|
+ filter: brightness(1.35);
|
|
|
|
|
+ stroke: rgba(255, 255, 255, 0.9);
|
|
|
|
|
+ stroke-width: 1.5;
|
|
|
}
|
|
}
|
|
|
-.panel-donut__slice:focus-visible {
|
|
|
|
|
- transform: scale(1.06);
|
|
|
|
|
- filter: brightness(1.25);
|
|
|
|
|
- stroke: rgba(255, 255, 255, 0.85);
|
|
|
|
|
|
|
+.panel-donut__hit {
|
|
|
|
|
+ fill: transparent;
|
|
|
|
|
+ cursor: default;
|
|
|
|
|
+ stroke: none;
|
|
|
outline: none;
|
|
outline: none;
|
|
|
|
|
+ transform-box: view-box;
|
|
|
|
|
+}
|
|
|
|
|
+.panel-donut__hit:focus-visible {
|
|
|
|
|
+ stroke: rgba(255, 255, 255, 0.85);
|
|
|
|
|
+ stroke-width: 1.5;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-/* 饼图扇区:同环形(票 14 的既有 is-dim 选中高亮不受影响)。 */
|
|
|
|
|
|
|
+/* 饼图扇区:同环形(票 14 的 is-dim 选中降透明保留;opacity 过渡不能丢)。
|
|
|
|
|
+ 票 43(第三轮):放大档同环形加到 1.1 + 提亮 + 白描边(64px 图上 1.05 只有 ~1.5px,不够明显)。 */
|
|
|
.panel-pie__slice {
|
|
.panel-pie__slice {
|
|
|
- cursor: default;
|
|
|
|
|
- transition: transform 0.15s;
|
|
|
|
|
|
|
+ transition: transform 0.15s, filter 0.15s, opacity 0.16s;
|
|
|
transform-origin: 50px 50px;
|
|
transform-origin: 50px 50px;
|
|
|
transform-box: view-box;
|
|
transform-box: view-box;
|
|
|
}
|
|
}
|
|
|
-.panel-pie__slice:hover {
|
|
|
|
|
- transform: scale(1.05);
|
|
|
|
|
- filter: brightness(1.2);
|
|
|
|
|
|
|
+.panel-pie__slice.is-hovered {
|
|
|
|
|
+ transform: scale(1.1);
|
|
|
|
|
+ filter: brightness(1.35);
|
|
|
|
|
+ stroke: rgba(255, 255, 255, 0.9);
|
|
|
|
|
+ stroke-width: 1.5;
|
|
|
}
|
|
}
|
|
|
-.panel-pie__slice:focus-visible {
|
|
|
|
|
- transform: scale(1.05);
|
|
|
|
|
- filter: brightness(1.25);
|
|
|
|
|
- stroke: rgba(255, 255, 255, 0.85);
|
|
|
|
|
|
|
+.panel-pie__hit {
|
|
|
|
|
+ fill: transparent;
|
|
|
|
|
+ cursor: default;
|
|
|
|
|
+ stroke: none;
|
|
|
outline: none;
|
|
outline: none;
|
|
|
|
|
+ transform-box: view-box;
|
|
|
|
|
+}
|
|
|
|
|
+.panel-pie__hit:focus-visible {
|
|
|
|
|
+ stroke: rgba(255, 255, 255, 0.85);
|
|
|
|
|
+ stroke-width: 1.5;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 票 43(重开):图例行联动高亮(与扇区双向)——底色一档 + 文字提亮,
|
|
|
|
|
+ 与横条统计 `.panel-bar:hover .panel-bar__fill` 同一档可感度。 */
|
|
|
|
|
+.panel-donut__row.is-hovered,
|
|
|
|
|
+.panel-pie__row.is-hovered {
|
|
|
|
|
+ background: rgba(1, 171, 234, 0.12);
|
|
|
|
|
+}
|
|
|
|
|
+.panel-donut__row.is-hovered .panel-donut__label,
|
|
|
|
|
+.panel-pie__row.is-hovered .panel-pie__count {
|
|
|
|
|
+ color: var(--text-strong, #fff);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/* 条形图行:填充条增亮(行高太小,不位移)。 */
|
|
/* 条形图行:填充条增亮(行高太小,不位移)。 */
|