@wycm9527/morganite 0.0.0-stage → 0.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +171 -0
- package/LICENSE +33 -0
- package/README.md +62 -2
- package/bridge/base.css +138 -0
- package/bridge/echarts.d.ts +44 -0
- package/bridge/echarts.js +217 -0
- package/bridge/element-plus.css +785 -0
- package/bridge/fonts.css +124 -0
- package/bridge/icon-merge.d.ts +5 -0
- package/bridge/icon-merge.js +103 -0
- package/bridge/iconpark.config.ts +75 -0
- package/bridge/iconpark.css +29 -0
- package/bridge/react/ConfirmBar.tsx +43 -0
- package/bridge/react/EChart.tsx +45 -0
- package/bridge/react/PageHeader.tsx +24 -0
- package/bridge/react/SectionHeader.tsx +17 -0
- package/bridge/react/StatCard.tsx +33 -0
- package/bridge/react/TableSkeleton.tsx +11 -0
- package/bridge/react/TrendChart.tsx +8 -0
- package/bridge/recipes.css +444 -0
- package/bridge/shadcn-globals.css +6 -0
- package/bridge/shadcn.css +238 -0
- package/bridge/vue/ConfirmBar.vue +23 -0
- package/bridge/vue/EChart.vue +43 -0
- package/bridge/vue/KitchenSink.vue +507 -0
- package/bridge/vue/PageHeader.vue +23 -0
- package/bridge/vue/SectionHeader.vue +15 -0
- package/bridge/vue/StatCard.vue +33 -0
- package/bridge/vue/TableSkeleton.vue +14 -0
- package/bridge/vue/TrendChart.vue +11 -0
- package/bridge/vue/confirm.js +18 -0
- package/bridge/vue/inlineConfirm.js +21 -0
- package/design-system/AUDIT.md +277 -0
- package/design-system/DESIGN.md +176 -0
- package/design-system/contrast.md +71 -0
- package/design-system/preview/drawer.html +38 -0
- package/design-system/preview/form.html +40 -0
- package/design-system/preview/index.html +21 -0
- package/design-system/preview/preview.css +190 -0
- package/design-system/preview/preview.local.css +17 -0
- package/design-system/preview/shell.html +50 -0
- package/design-system/scope-map.json +4 -0
- package/design-system/style-dictionary.config.mjs +172 -0
- package/design-system/theme-map.json +16 -0
- package/design-system/themes/dark/THEME.md +29 -0
- package/design-system/themes/dark/tokens/semantic.tokens.json +354 -0
- package/design-system/token-board.html +137 -0
- package/design-system/tokens/primitives.tokens.json +1898 -0
- package/design-system/tokens/semantic.tokens.json +1080 -0
- package/design-system.json +217 -0
- package/migration/roles.json +743 -0
- package/package.json +82 -4
- package/templates/AGENTS.md +13 -0
- package/templates/KitchenSink.tsx +179 -0
- package/templates/accept.config.mjs +28 -0
- package/templates/entry-css.css +6 -0
- package/templates/entry-element-plus.css +9 -0
- package/templates/entry-shadcn.css +7 -0
- package/templates/notes-css.md +4 -0
- package/templates/notes-element-plus.md +7 -0
- package/templates/notes-shadcn.md +7 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// 板块标题(种子配方组件 · React 版,与 bridge/vue/SectionHeader.vue 同形):看板页在 H1 下分板块的二级标题,actions 放只作用于这一板块的筛选与操作。
|
|
2
|
+
// 样式来自 recipes.css 的 .section-head。页面级操作放 PageHeader 的 actions;不值一个标题的次要分组用配方类 .section-label。
|
|
3
|
+
import * as React from 'react'
|
|
4
|
+
|
|
5
|
+
export interface SectionHeaderProps {
|
|
6
|
+
title: React.ReactNode
|
|
7
|
+
actions?: React.ReactNode
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function SectionHeader({ title, actions }: SectionHeaderProps) {
|
|
11
|
+
return (
|
|
12
|
+
<div className="section-head">
|
|
13
|
+
<h2>{title}</h2>
|
|
14
|
+
{actions ? <div className="right">{actions}</div> : null}
|
|
15
|
+
</div>
|
|
16
|
+
)
|
|
17
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// 统计卡(种子配方组件 · React 版,与 bridge/vue/StatCard.vue 同形):数据大字 + 涨跌 + 口径说明。样式来自 recipes.css 的 .stat-card。
|
|
2
|
+
// up 是箭头方向,positive 是颜色(好坏);卡片与普通卡片同一形态,不加品牌指示条。
|
|
3
|
+
import * as React from 'react'
|
|
4
|
+
|
|
5
|
+
export interface StatCardProps {
|
|
6
|
+
label: string
|
|
7
|
+
value: string | number
|
|
8
|
+
delta?: string
|
|
9
|
+
up?: boolean
|
|
10
|
+
positive?: boolean
|
|
11
|
+
note?: string
|
|
12
|
+
hint?: string
|
|
13
|
+
labelExtra?: React.ReactNode
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function StatCard({ label, value, delta, up = true, positive, note, hint, labelExtra }: StatCardProps) {
|
|
17
|
+
const good = positive === undefined ? up : positive
|
|
18
|
+
return (
|
|
19
|
+
<div className="stat-card">
|
|
20
|
+
<div className="stat-card__label">
|
|
21
|
+
<span>{label}</span>
|
|
22
|
+
{hint && <button type="button" className="stat-card__hint" title={hint} aria-label={`${label}的统计口径说明`}>
|
|
23
|
+
<svg className="lucide lucide-info" viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10" /><path d="M12 16v-4M12 8h.01" /></svg>
|
|
24
|
+
</button>}
|
|
25
|
+
{labelExtra}
|
|
26
|
+
</div>
|
|
27
|
+
<div className="stat-card__value num">{value}</div>
|
|
28
|
+
{delta ? (
|
|
29
|
+
<div className={`stat-card__delta num ${good ? 'up' : 'down'}`}>{up ? '▲' : '▼'} {delta}{note && <span className="stat-card__note"> · {note}</span>}</div>
|
|
30
|
+
) : note ? <div className="stat-card__note">{note}</div> : null}
|
|
31
|
+
</div>
|
|
32
|
+
)
|
|
33
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// 表格骨架(种子配方组件 · React 版):按目标表格的形状占位(表头 + 等高行 + 列宽),加载完成后不发生版面跳动。样式来自 recipes.css 的 .tskel。
|
|
2
|
+
export function TableSkeleton({ rows = 6, cols = [200, 160, 120, 120, 100] }: { rows?: number; cols?: number[] }) {
|
|
3
|
+
return (
|
|
4
|
+
<div className="tskel" role="status" aria-live="polite" aria-label="正在加载">
|
|
5
|
+
<div className="tskel-head">{cols.map((w, i) => <span key={i} className="cell" style={{ flex: `0 0 ${w}px` }}><i className="bar head" /></span>)}</div>
|
|
6
|
+
{Array.from({ length: rows }, (_, r) => (
|
|
7
|
+
<div key={r} className="tskel-row">{cols.map((w, i) => <span key={i} className="cell" style={{ flex: `0 0 ${w}px` }}><i className={`bar${(r + i) % 3 === 0 ? ' short' : ''}`} /></span>)}</div>
|
|
8
|
+
))}
|
|
9
|
+
</div>
|
|
10
|
+
)
|
|
11
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// 趋势折线(种子配方组件 · React 版):桥接 trendLine 配方——计数型自动整数刻度、单序列渐变面积、多序列不填面积。
|
|
2
|
+
import * as React from 'react'
|
|
3
|
+
import { EChart } from './EChart'
|
|
4
|
+
import { trendLine, areaGradient } from '../echarts.js'
|
|
5
|
+
|
|
6
|
+
export function TrendChart({ days, series, count }: { days: string[]; series: { name: string; data: number[] }[]; count?: boolean }) {
|
|
7
|
+
return <EChart deps={[days, series, count]} option={(t, ec) => trendLine({ days, series, count, area: series.length === 1 ? areaGradient(ec) : undefined })} />
|
|
8
|
+
}
|
|
@@ -0,0 +1,444 @@
|
|
|
1
|
+
/* 配方层(recipes):DESIGN.md「页面骨架 / 组件配方」的可复制实现。
|
|
2
|
+
与组件库无关,只引用 design-system/dist 的 CSS 变量;引入顺序在桥接之后、项目自己的 app.css 之前。
|
|
3
|
+
项目只写业务页面,不重写这些类;需要新配方时先向种子提提案,批准后改这里,各项目升级后拿到。
|
|
4
|
+
效果预览(视觉基准)直接引用这份文件:预览页里出现的页面形态都应在这里有对应的类,预览自己的样式表只放静态组件与示意图表。
|
|
5
|
+
元素外边距写全:Element 的样式入口不引 base.css,h1 / p / ul 带着浏览器默认外边距。 */
|
|
6
|
+
|
|
7
|
+
/* ── 基础 ── */
|
|
8
|
+
*, *::before, *::after { box-sizing: border-box; }
|
|
9
|
+
[hidden] { display: none !important; } /* 模板项目用 hidden 属性切换空态 / 表格时,配方类的 display 不得把它压掉 */
|
|
10
|
+
/* 根字号保持浏览器默认 16px:Tailwind 的 rem 刻度(size-4 = 1rem)依赖它,改在 html 上会让 shadcn 的每个尺寸缩 12.5%;正文字号落在 body */
|
|
11
|
+
html { font-family: var(--font-family-body); line-height: var(--text-body-line-height); }
|
|
12
|
+
body { margin: 0; font-size: var(--text-body-size); background: var(--color-bg-page); color: var(--color-text-primary); }
|
|
13
|
+
a { color: inherit; text-decoration: none; }
|
|
14
|
+
.num { font-family: var(--text-numeric-family); font-variant-numeric: var(--text-numeric-variant); } /* 数字数据:正文字体 + 等宽数字(text.numeric.*;数据大字才用 D-DIN 系字体);单号、代码用下面的 .mono */
|
|
15
|
+
.mono { font-family: var(--font-family-code); font-variant-numeric: var(--text-numeric-variant); }
|
|
16
|
+
|
|
17
|
+
/* ── 图标尺寸六档:IconPark 走 font-size(见 iconpark.css);lucide 这类 SVG 图标库走 width / height,同一组类名 ── */
|
|
18
|
+
svg.i-icon--xs { width: var(--size-icon-xs); height: var(--size-icon-xs); flex: none; }
|
|
19
|
+
svg.i-icon--sm { width: var(--size-icon-sm); height: var(--size-icon-sm); flex: none; }
|
|
20
|
+
svg.i-icon--md { width: var(--size-icon-md); height: var(--size-icon-md); flex: none; }
|
|
21
|
+
svg.i-icon--lg { width: var(--size-icon-lg); height: var(--size-icon-lg); flex: none; }
|
|
22
|
+
svg.i-icon--xl { width: var(--size-icon-xl); height: var(--size-icon-xl); flex: none; }
|
|
23
|
+
svg.i-icon--2xl { width: var(--size-icon-2xl); height: var(--size-icon-2xl); flex: none; }
|
|
24
|
+
svg.i-icon--muted { color: var(--color-icon-muted); }
|
|
25
|
+
/* 面包屑(没有组件库时):父级 text.secondary、当前页 text.primary,只有真实路由是 <a>。
|
|
26
|
+
分隔符是细箭头:标记里仍写 <span class="sep">/</span>,span 本身 visibility: hidden(「/」从视觉与无障碍树里消失,文字扫描也不再把它当小字),箭头在 ::before 上设回 visible、用遮罩画在 text.muted 上,
|
|
27
|
+
icon.size.sm 见方,描边 1.4 与 icon.stroke.width 同值(遮罩里写不了 var());Element 的 .el-breadcrumb__separator 在桥接里同一画法 */
|
|
28
|
+
.crumbs { display: flex; align-items: center; gap: var(--spacing-2); font-size: var(--text-body-sm-size); color: var(--color-text-secondary); }
|
|
29
|
+
.crumbs .sep { display: inline-flex; flex: none; width: var(--size-icon-sm); height: var(--size-icon-sm); font-size: 0; color: var(--color-text-muted); visibility: hidden; }
|
|
30
|
+
.crumbs .sep::before { content: ""; visibility: visible; display: block; width: 100%; height: 100%; background: currentColor; -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M4.5 2.5 8 6l-3.5 3.5' fill='none' stroke='%23000' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / contain no-repeat; mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M4.5 2.5 8 6l-3.5 3.5' fill='none' stroke='%23000' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / contain no-repeat; }
|
|
31
|
+
.crumbs span:last-child { color: var(--color-text-primary); }
|
|
32
|
+
.crumbs a:hover { color: var(--color-text-link-hover); }
|
|
33
|
+
.secondary { color: var(--color-text-secondary); }
|
|
34
|
+
|
|
35
|
+
/* ── 链接四层(DESIGN 视觉语言·链接) ── */
|
|
36
|
+
/* 内容型:订单号、名称——下划线 + 斜体;等宽数字型不斜体 */
|
|
37
|
+
/* 配方类一律单类选择器:不加 td / .topbar 等后代前缀抬特异性(2.11.7 汉堡按钮事故的根因),后面的状态 / 三端规则靠书写顺序覆盖 */
|
|
38
|
+
.link { color: var(--color-text-link); text-decoration: var(--text-link-decoration); font-style: var(--text-link-style); cursor: pointer; }
|
|
39
|
+
.link:hover { color: var(--color-text-link-hover); }
|
|
40
|
+
.link.danger { color: var(--color-text-danger); }
|
|
41
|
+
.link.danger:hover { color: var(--color-text-danger); }
|
|
42
|
+
.link.mono, .mono.link { font-style: normal; }
|
|
43
|
+
/* 操作型:动词(编辑 / 详情 / 改派 / 催单…)——同色胶囊描边,行内胶囊档 control.height.xs、text.body-sm、400;<a> 与 <button> 同形 */
|
|
44
|
+
.act { color: var(--color-text-link); font-family: inherit; font-size: var(--text-body-sm-size); font-weight: var(--font-weight-regular); text-decoration: none; font-style: normal; cursor: pointer; display: inline-flex; align-items: center; justify-content: center; gap: var(--spacing-1); height: var(--control-height-xs); min-height: 0; padding: 0 var(--spacing-2); border: var(--border-width-default) solid currentColor; border-radius: var(--radius-full); line-height: 1; white-space: nowrap; }
|
|
45
|
+
.act:hover { color: var(--color-text-link-hover); background: var(--color-bg-hover); }
|
|
46
|
+
.act.danger, .act.danger:hover { color: var(--color-text-danger); }
|
|
47
|
+
.act.danger:hover { background: var(--color-status-error-bg); }
|
|
48
|
+
.act + .act { margin-left: var(--space-inline); }
|
|
49
|
+
/* 操作组:一组 .act 由容器 gap 留间距——不可用的动作要外包一层带 title 的 span 说明原因,相邻选择器 .act + .act 就断了 */
|
|
50
|
+
.act-group { display: flex; align-items: center; gap: var(--space-inline); }
|
|
51
|
+
.act-group .act + .act { margin-left: 0; }
|
|
52
|
+
button.act { background: transparent; margin: 0; } /* 字体三项由 .act 声明;不要写 font: inherit,简写会把字重行高一并重置 */
|
|
53
|
+
.act:disabled, .act.is-disabled { opacity: var(--opacity-disabled); cursor: not-allowed; } /* 不可用的动作保留位置、压淡——列宽不跳;button 靠原生 disabled 挡点击,title 仍能悬停出原因 */
|
|
54
|
+
.act.is-disabled { pointer-events: none; } /* <a> 没有原生禁用,只能切断事件(要给原因时外包一层带 title 的 span) */
|
|
55
|
+
/* 标题型:列表项标题本身可点(待办、公告、供应商名)——近黑 + 500,hover 出下划线;<a> / <button> 同形 */
|
|
56
|
+
.title-link { color: var(--color-text-primary); font-weight: var(--text-weight-label); text-decoration: none; cursor: pointer; font-family: inherit; font-size: inherit; }
|
|
57
|
+
.title-link:hover { color: var(--color-text-link-hover); text-decoration: underline; }
|
|
58
|
+
button.title-link, button.link { background: transparent; border: 0; padding: 0; margin: 0; text-align: left; } /* 用 button 承载时去掉默认外观;不用 font 简写 */
|
|
59
|
+
/* 导览型:查看全部 / 查看列表——文字 + 右箭头图标,不描边不斜体 */
|
|
60
|
+
.go { display: inline-flex; align-items: center; gap: var(--spacing-1); color: var(--color-text-link); font-weight: var(--text-weight-label); text-decoration: none; font-style: normal; cursor: pointer; min-height: var(--control-hit-min); }
|
|
61
|
+
.go:hover { color: var(--color-text-link-hover); }
|
|
62
|
+
.go:hover .i-icon { transform: translateX(var(--spacing-0-5)); }
|
|
63
|
+
.go .i-icon { transition: transform var(--motion-duration-fast) var(--motion-easing-standard); }
|
|
64
|
+
/* 表格、卡片页脚、批量条、空态里的链接不是句中链接:命中区不低于 control.hit-min,视觉字号不变 */
|
|
65
|
+
td .link, .card-head .link, .batch .link, .empty .link, .more-link { display: inline-flex; align-items: center; min-height: var(--control-hit-min); }
|
|
66
|
+
.more-link { min-width: var(--control-hit-min); justify-content: center; }
|
|
67
|
+
|
|
68
|
+
/* ── 壳层:侧栏 + 顶栏 + 内容区(DESIGN 页面骨架·页面框架) ── */
|
|
69
|
+
.app { display: grid; grid-template-columns: var(--layout-sidebar-width) 1fr; min-height: 100vh; transition: grid-template-columns var(--motion-duration-normal) var(--motion-easing-standard); }
|
|
70
|
+
.app.is-collapsed { grid-template-columns: var(--layout-sidebar-collapsed-width) 1fr; }
|
|
71
|
+
.sidebar { background: var(--color-bg-sidebar); border-right: var(--border-width-default) solid var(--color-border-sidebar); position: sticky; top: 0; height: 100vh; display: flex; flex-direction: column; overflow: hidden; }
|
|
72
|
+
.app-brand { display: flex; align-items: center; gap: var(--spacing-2-5); height: var(--layout-topbar-height); padding: 0 var(--spacing-4); font-weight: var(--text-weight-brand); font-size: var(--text-title-size); color: var(--color-text-sidebar-strong); white-space: nowrap; }
|
|
73
|
+
.app-brand .logo { width: var(--avatar-size-sm); height: var(--avatar-size-sm); border-radius: var(--radius-md); background: var(--color-action-primary); color: var(--color-text-on-primary); display: grid; place-items: center; font-weight: var(--text-weight-logo); font-size: var(--text-title-sm-size); flex: none; overflow: hidden; }
|
|
74
|
+
.app-brand .logo img { width: 100%; height: 100%; object-fit: cover; display: block; }
|
|
75
|
+
.is-collapsed .app-brand { padding: 0; justify-content: center; }
|
|
76
|
+
.is-collapsed .app-brand > :not(.logo) { display: none; } /* 纯 CSS 壳层(无框架 v-if)折叠时隐藏品牌文字 */
|
|
77
|
+
.sidebar .el-menu { border-right: 0; flex: 1; padding-top: var(--spacing-2); overflow: hidden auto; } /* 侧栏是 100vh 且裁切溢出:窗口不够高时菜单区自己滚动,否则底部的项被裁掉、点不到 */
|
|
78
|
+
/* 不用 Element 菜单时的侧栏导航(React / 静态页):分组标题 + 链接,当前项亮色主色底块 + 白字白图标、暗色金色暗底 + 亮金字,不加指示条,与 el-menu 桥接同一形态 */
|
|
79
|
+
.nav { flex: 1; padding: var(--spacing-2); display: flex; flex-direction: column; overflow: hidden auto; }
|
|
80
|
+
.nav :is(a, button) + :is(a, button) { margin-top: var(--layout-sidebar-item-gap); } /* 间距只给同组相邻项,分组标题靠自己的上下留白,与 el-menu 一致 */
|
|
81
|
+
.nav-group { font-size: var(--text-caption-size); color: var(--color-text-sidebar-muted); padding: var(--spacing-3-5) var(--spacing-3) var(--spacing-1-5); white-space: nowrap; }
|
|
82
|
+
.nav a, .nav button { display: flex; align-items: center; gap: var(--spacing-2-5); min-height: var(--layout-sidebar-item-height); padding: 0 var(--spacing-3); border-radius: var(--radius-md); color: var(--color-text-sidebar); position: relative; text-decoration: none; background: transparent; border: 0; font: inherit; font-weight: var(--text-weight-nav); width: 100%; text-align: left; cursor: pointer; white-space: nowrap; }
|
|
83
|
+
.nav a:hover, .nav button:hover { background: var(--color-bg-sidebar-hover); }
|
|
84
|
+
.nav a.active, .nav button.active { background: var(--color-bg-sidebar-selected); color: var(--color-text-sidebar-selected); font-weight: var(--text-weight-nav); } /* 图标随文字色;与其他项同字重、不加指示条 */
|
|
85
|
+
.is-collapsed .nav-group { display: none; }
|
|
86
|
+
.is-collapsed .nav .nav-group:not(:first-child) + :is(a, button) { margin-top: var(--layout-sidebar-item-gap); } /* 标题隐藏后相邻两组的项会贴在一起:组与组之间也留同样的项间距 */
|
|
87
|
+
.is-collapsed .nav a, .is-collapsed .nav button { justify-content: center; padding: 0; }
|
|
88
|
+
.is-collapsed .nav .nav-label { display: none; }
|
|
89
|
+
.sidebar-foot { padding: var(--spacing-3) var(--spacing-4); border-top: var(--border-width-default) solid var(--color-border-sidebar); font-size: var(--text-small-size); color: var(--color-text-sidebar-muted); white-space: nowrap; overflow: hidden; }
|
|
90
|
+
.main { min-width: 0; display: flex; flex-direction: column; }
|
|
91
|
+
.topbar { height: var(--layout-topbar-height); display: flex; align-items: center; gap: var(--spacing-3); padding: 0 var(--space-gutter); background: var(--color-bg-surface); border-bottom: var(--border-width-default) solid var(--color-border-default); position: sticky; top: 0; z-index: var(--layer-sticky); }
|
|
92
|
+
.topbar .search { width: var(--layout-search-width); margin-left: auto; font-size: var(--text-body-sm-size); --el-input-border-radius: var(--radius-full); }
|
|
93
|
+
.topbar .search input { border-radius: var(--radius-full); } /* 顶栏搜索是胶囊;表单里的输入框保持控件圆角 */
|
|
94
|
+
/* 纯 CSS / shadcn 项目的顶栏搜索:<label class="search"> + 图标 + <input>,填充式胶囊(Element 用 el-input.search,走上面两条) */
|
|
95
|
+
.topbar label.search { display: flex; align-items: center; gap: var(--spacing-2); height: var(--control-height-md); padding: 0 var(--spacing-3); background: var(--color-bg-input); border: var(--border-width-default) solid var(--color-border-input); border-radius: var(--radius-full); color: var(--color-icon-muted); }
|
|
96
|
+
.topbar label.search:hover { border-color: var(--color-border-strong); }
|
|
97
|
+
.topbar label.search:focus-within { border-color: var(--color-border-focus); }
|
|
98
|
+
.topbar label.search input { flex: 1; min-width: 0; height: 100%; border: 0; border-radius: 0; background: transparent; outline: none; color: var(--color-text-primary); font-size: var(--text-body-sm-size); }
|
|
99
|
+
.topbar .spacer { margin-left: auto; }
|
|
100
|
+
.iconbtn { position: relative; width: var(--control-height-md); height: var(--control-height-md); border-radius: var(--radius-md); display: grid; place-items: center; color: var(--color-icon-default); cursor: pointer; background: transparent; border: 0; padding: 0; }
|
|
101
|
+
.iconbtn:hover { background: var(--color-bg-hover); color: var(--color-text-primary); }
|
|
102
|
+
.iconbtn.is-on { background: var(--color-bg-selected); color: var(--color-text-selected); }
|
|
103
|
+
/* 未读计数:贴图标右上角;底色 action.danger(亮暗都是深红,白字够对比),不用 status.error */
|
|
104
|
+
.iconbtn .unread { position: absolute; top: 0; right: 0; transform: translate(25%, -15%); min-width: var(--size-icon-md); height: var(--size-icon-md); padding: 0 var(--spacing-1); border-radius: var(--radius-full); background: var(--color-action-danger); color: var(--color-text-on-danger); font-size: var(--text-caption-size); line-height: var(--size-icon-md); font-weight: var(--text-weight-strong); text-align: center; pointer-events: none; }
|
|
105
|
+
/* 头像:不透明的奶金浅底 bg.selected-subtle + 深金字 text.selected(亮 4.8:1、暗 6.6:1),放在页面底、卡片、悬停底上都过 AA */
|
|
106
|
+
.avatar { width: var(--avatar-size-md); height: var(--avatar-size-md); border-radius: var(--radius-full); background: var(--color-bg-selected-subtle); color: var(--color-text-selected); display: grid; place-items: center; font-weight: var(--text-weight-brand); font-size: var(--text-body-sm-size); cursor: pointer; flex: none; }
|
|
107
|
+
.topbar .user { display: inline-flex; align-items: center; gap: var(--spacing-2); cursor: pointer; padding: var(--spacing-1); border-radius: var(--radius-md); }
|
|
108
|
+
.topbar .user:hover { background: var(--color-bg-hover); }
|
|
109
|
+
button.user { background: var(--color-action-quiet); border: 0; font: inherit; color: inherit; } /* 头像区用 <button> 承载时去掉默认外观 */
|
|
110
|
+
.topbar .user-name { font-size: var(--text-body-sm-size); color: var(--color-text-primary); white-space: nowrap; }
|
|
111
|
+
.user-caret { color: var(--color-icon-muted); }
|
|
112
|
+
/* 顶栏左侧统一是面包屑(.crumbs / el-breadcrumb),工作台也不例外;问候语放进工作台的页头副标题(DESIGN「页面骨架」) */
|
|
113
|
+
.content { padding: var(--space-gutter); display: flex; flex-direction: column; gap: var(--space-stack); container-type: inline-size; } /* 看板 / 两栏按内容区宽度(容器查询)切单栏,侧栏宽度变了也成立 */
|
|
114
|
+
|
|
115
|
+
/* ── 页头(bridge/vue/PageHeader.vue 与 bridge/react/PageHeader.tsx 输出这组类):每个一级页面一个 H1 text.heading × strong;副标题 text.body × text.secondary,与标题相距 spacing.1-5;
|
|
116
|
+
右侧是页面级操作(主操作金底),与标题顶部对齐——表格、图表自己的筛选留在各自的卡片里 ── */
|
|
117
|
+
.page-head { display: flex; align-items: flex-start; gap: var(--space-stack); }
|
|
118
|
+
.page-head h1 { margin: 0; font-size: var(--text-heading-size); line-height: var(--text-heading-line-height); font-weight: var(--text-weight-strong); }
|
|
119
|
+
.page-head p { margin: var(--spacing-1-5) 0 0; color: var(--color-text-secondary); }
|
|
120
|
+
.page-head .actions { margin-left: auto; display: flex; gap: var(--space-inline); align-items: center; flex: none; }
|
|
121
|
+
/* 详情页标题行:标题 + 状态胶囊同一行 */
|
|
122
|
+
.page-head .title-row { display: flex; align-items: center; gap: var(--spacing-3); flex-wrap: wrap; }
|
|
123
|
+
/* 板块标题(bridge/vue/SectionHeader.vue 与 bridge/react/SectionHeader.tsx):看板页在 H1 下分板块——二级标题 text.title-sm × strong × text.primary,
|
|
124
|
+
右侧放只作用于这一板块的筛选与操作(周期切换、导出日报),与标题垂直居中;页面级操作不放这里 */
|
|
125
|
+
.section-head { display: flex; align-items: center; gap: var(--space-stack); }
|
|
126
|
+
.section-head h2 { margin: 0; font-size: var(--text-title-sm-size); font-weight: var(--text-weight-strong); color: var(--color-text-primary); }
|
|
127
|
+
.section-head .right { margin-left: auto; display: flex; align-items: center; gap: var(--space-inline); }
|
|
128
|
+
/* 次要分组的小标签(小号、半粗、次要色、大写字距):表单分组、侧栏区块这类不值一个标题的地方;板块标题用上面的 .section-head */
|
|
129
|
+
.section-label { font-size: var(--text-small-size); font-weight: var(--text-weight-strong); color: var(--color-text-secondary); letter-spacing: var(--text-tracking-caps); text-transform: uppercase; }
|
|
130
|
+
|
|
131
|
+
/* ── 内容区网格:看板(主栏 : 侧栏 = 1.8 : 1)、两栏详情(1.6 : 1)、纵向堆叠;内容区窄于 880px 收成单栏(见文件末的容器查询) ── */
|
|
132
|
+
.board { display: grid; grid-template-columns: minmax(0, 1.8fr) minmax(0, 1fr); gap: var(--space-stack); align-items: stretch; }
|
|
133
|
+
.two-col { display: grid; grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr); gap: var(--space-stack); align-items: start; }
|
|
134
|
+
.stack { display: flex; flex-direction: column; gap: var(--space-stack); min-width: 0; }
|
|
135
|
+
|
|
136
|
+
/* ── 卡片区块 ── */
|
|
137
|
+
.card-head { display: flex; align-items: center; gap: var(--spacing-3); padding: var(--spacing-4) var(--space-card); border-bottom: var(--border-width-default) solid var(--color-border-default); }
|
|
138
|
+
.card-head h2 { margin: 0; font-size: var(--text-body-size); font-weight: var(--text-weight-strong); color: var(--color-text-card-title); } /* 不设行高:沿用 h2 的标题比例(14px 时约 18.7px),与同行 12px 说明的行高相当,带不带说明的卡片头一样高 */
|
|
139
|
+
.card-head .right { margin-left: auto; display: flex; gap: var(--space-inline); align-items: center; }
|
|
140
|
+
.card-body { padding: var(--space-card); } /* flat 卡里直接放图表 / 自由内容时的留白(表格、筛选栏等配方自带内边距,不需要它) */
|
|
141
|
+
.el-card.flat { --el-card-padding: 0; }
|
|
142
|
+
/* 看板卡:卡片标题下不画分隔线、说明与标题同行;正文纵向排布、块间距 spacing.4;卡里的表格首尾列与标题对齐。
|
|
143
|
+
用 el-card 承载(class="flat panel")时卡片外观来自桥接,这里的外观声明值相同 */
|
|
144
|
+
.panel { background: var(--color-bg-surface); border: var(--border-width-default) solid var(--color-border-default); border-radius: var(--radius-lg); box-shadow: 0 var(--elevation-card-y) var(--elevation-card-blur) var(--elevation-card-color); min-width: 0; display: flex; flex-direction: column; overflow: hidden; }
|
|
145
|
+
.panel .card-head { border-bottom: 0; padding-bottom: var(--spacing-1); }
|
|
146
|
+
.panel .card-head .sub { display: inline; margin-left: var(--spacing-2); }
|
|
147
|
+
.panel .card-body { flex: 1; display: flex; flex-direction: column; gap: var(--spacing-4); }
|
|
148
|
+
.panel .table-wrap { margin-top: var(--spacing-2); }
|
|
149
|
+
.panel .table th:first-child, .panel .table td:first-child { padding-left: var(--space-card); }
|
|
150
|
+
.panel .table th:last-child, .panel .table td:last-child { padding-right: var(--space-card); }
|
|
151
|
+
/* 卡片栅格(商品 / 活动 / 优惠券这类卡片视图):列宽下限 layout.search-width、自动填充,放在 flat 卡里四周留 space.card */
|
|
152
|
+
.card-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(var(--layout-search-width), 1fr)); gap: var(--space-stack); padding: var(--space-card); }
|
|
153
|
+
|
|
154
|
+
/* ── 状态胶囊:文字 + 同色浅底,400 字重;.status 与 .badge 同物,两个名字只为语义 ── */
|
|
155
|
+
.status, .badge { display: inline-flex; align-items: center; gap: var(--spacing-1-5); padding: var(--spacing-0-5) var(--spacing-2); border-radius: var(--radius-full); font-size: var(--text-small-size); font-weight: var(--font-weight-regular); line-height: var(--text-body-line-height); white-space: nowrap; }
|
|
156
|
+
.status.success, .badge.success { color: var(--color-status-success); background: var(--color-status-success-bg); }
|
|
157
|
+
.status.warning, .badge.warning { color: var(--color-status-warning); background: var(--color-status-warning-bg); }
|
|
158
|
+
.status.error, .badge.error { color: var(--color-status-error); background: var(--color-status-error-bg); }
|
|
159
|
+
.status.info, .badge.info { color: var(--color-status-info); background: var(--color-status-info-bg); }
|
|
160
|
+
.status.neutral, .badge.neutral { color: var(--color-status-neutral); background: var(--color-status-neutral-bg); }
|
|
161
|
+
.badge.brand { color: var(--color-text-selected); background: var(--color-bg-selected-subtle); } /* 强调徽标(置顶、本机、管理员):奶金浅底 + 深金字,与表格选中行同一套;action.selected 在本系统是白块,不能当底色 */
|
|
162
|
+
/* 标签(类别 / 属性,中性):radius.sm 的小方标签,不做胶囊(DESIGN「圆角」「徽标 / 提示条」);表达状态用上面的 .status */
|
|
163
|
+
.tag { display: inline-flex; align-items: center; height: var(--control-height-xs); padding: 0 var(--spacing-2); border-radius: var(--radius-sm); background: var(--color-status-neutral-bg); color: var(--color-status-neutral); font-size: var(--text-small-size); white-space: nowrap; }
|
|
164
|
+
.sub { display: block; font-size: var(--text-small-size); color: var(--color-text-muted); }
|
|
165
|
+
.up { color: var(--color-data-increase); } .down { color: var(--color-data-decrease); }
|
|
166
|
+
|
|
167
|
+
/* ── 列表页:筛选栏 / 条件标签 / 批量条 / 分页 ── */
|
|
168
|
+
.filter { display: flex; flex-wrap: wrap; gap: var(--spacing-3); padding: var(--spacing-4) var(--space-card); border-bottom: var(--border-width-default) solid var(--color-border-default); align-items: center; }
|
|
169
|
+
.filter .conds { display: flex; flex-wrap: wrap; gap: var(--spacing-3); align-items: center; flex: 1 1 auto; min-width: 0; }
|
|
170
|
+
.filter .acts { display: flex; gap: var(--spacing-3); align-items: center; margin-left: auto; flex: none; }
|
|
171
|
+
.filter.solo { border-bottom: 0; } /* 筛选栏单独成一张卡(报表页)时不要再画底线,否则与卡片边线叠成双线 */
|
|
172
|
+
/* 控件宽度档(筛选栏、设置行):搜索框 layout.search-width,选择器 0.6 倍,日期范围 1.15 倍。Element 的选择器默认撑满父容器,要给档位 */
|
|
173
|
+
.w-search { width: var(--layout-search-width); }
|
|
174
|
+
.w-select { width: calc(var(--layout-search-width) * 0.6); }
|
|
175
|
+
.w-date { width: calc(var(--layout-search-width) * 1.15); }
|
|
176
|
+
.nowrap { white-space: nowrap; }
|
|
177
|
+
.chips { display: flex; gap: var(--spacing-2); flex-wrap: wrap; padding: var(--spacing-3) var(--space-card) 0; }
|
|
178
|
+
.chips:last-child, .chips + .el-table, .chips + .tskel { padding-bottom: var(--spacing-3); } /* 后面没有批量条时也与表头留出间距 */
|
|
179
|
+
.chip { display: inline-flex; align-items: center; gap: var(--spacing-1-5); padding: 0 var(--spacing-2-5); height: var(--control-height-sm); border-radius: var(--radius-full); background: var(--color-bg-chip); color: var(--color-text-selected); font-size: var(--text-small-size); } /* 筹码:字色同色 10% 浅底(color.bg.chip) */
|
|
180
|
+
.chip .x { display: inline-flex; align-items: center; justify-content: center; cursor: pointer; opacity: var(--opacity-on-primary-muted); }
|
|
181
|
+
/* × 用 <button class="x" aria-label="移除条件 …">:命中区拉到 control.hit-min,负外边距抵消,胶囊视觉宽度不变 */
|
|
182
|
+
.chip button.x { background: transparent; border: 0; padding: 0; color: inherit; min-width: var(--control-hit-min); height: var(--control-height-sm); margin: 0 calc(var(--spacing-2-5) * -1) 0 calc(var(--spacing-1) * -1); border-radius: var(--radius-full); }
|
|
183
|
+
.chip button.x:hover { opacity: 1; }
|
|
184
|
+
.batch { display: flex; align-items: center; gap: var(--spacing-3); padding: var(--spacing-2-5) var(--space-card); margin-top: var(--spacing-3); background: var(--color-bg-selected); border-top: var(--border-width-default) solid var(--color-border-default); border-bottom: var(--border-width-default) solid var(--color-border-default); color: var(--color-text-primary); font-size: var(--text-body-sm-size); }
|
|
185
|
+
.batch .right { margin-left: auto; display: flex; gap: var(--space-inline); }
|
|
186
|
+
.pager { display: flex; justify-content: flex-end; padding: var(--spacing-3) var(--space-card); }
|
|
187
|
+
/* 分页(纯 CSS / shadcn 项目;Element 用 el-pagination,桥接已对齐同一套):页码透明、悬停浅底,当前页金底白字 */
|
|
188
|
+
.pagination { display: flex; align-items: center; gap: var(--spacing-1); flex-wrap: wrap; }
|
|
189
|
+
.pagination .total { margin-right: var(--spacing-3); color: var(--color-text-secondary); font-size: var(--text-body-sm-size); }
|
|
190
|
+
.page { display: inline-grid; place-items: center; min-width: var(--control-height-md); height: var(--control-height-md); padding: 0 var(--spacing-2); border: var(--border-width-default) solid var(--color-action-quiet); border-radius: var(--radius-md); background: var(--color-action-quiet); color: var(--color-text-primary); font-size: var(--text-body-sm-size); font-variant-numeric: var(--text-numeric-variant); cursor: pointer; }
|
|
191
|
+
.page:hover:not(:disabled):not([aria-current="page"]):not(.ellipsis) { background: var(--color-bg-hover); }
|
|
192
|
+
.page[aria-current="page"] { background: var(--color-action-primary); border-color: var(--color-action-primary); color: var(--color-text-on-primary); font-weight: var(--text-weight-strong); }
|
|
193
|
+
.page:disabled { opacity: var(--opacity-disabled); cursor: not-allowed; }
|
|
194
|
+
.page.ellipsis { color: var(--color-text-muted); cursor: default; }
|
|
195
|
+
.page svg { width: var(--size-icon-md); height: var(--size-icon-md); }
|
|
196
|
+
|
|
197
|
+
/* ── 表单页 ── */
|
|
198
|
+
.form-page { max-width: var(--layout-form-max-width); }
|
|
199
|
+
.form-grid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 0 var(--spacing-6); padding: var(--space-card) var(--space-card) 0; }
|
|
200
|
+
.form-grid .span2 { grid-column: 1 / -1; }
|
|
201
|
+
.form-foot { display: flex; justify-content: flex-end; gap: var(--space-inline); padding: var(--spacing-3-5) var(--space-card); border-top: var(--border-width-default) solid var(--color-border-default); }
|
|
202
|
+
/* 长表单的操作条贴底跟随:浮在内容上时要有边界(发丝边 + 向上的卡片阴影) */
|
|
203
|
+
.form-foot.is-sticky { position: sticky; bottom: 0; z-index: var(--layer-sticky); background: var(--color-bg-surface); border: var(--border-width-default) solid var(--color-border-default); border-radius: var(--radius-lg); box-shadow: 0 calc(var(--elevation-card-y) * -1) var(--elevation-card-blur) var(--elevation-card-color); }
|
|
204
|
+
.el-form--label-right .el-form-item__label { width: var(--layout-form-label-width); }
|
|
205
|
+
.help { display: block; margin-top: var(--spacing-1); color: var(--color-text-muted); font-size: var(--text-small-size); line-height: var(--text-body-line-height); }
|
|
206
|
+
.el-form-item.is-error .help { display: none; } /* 出错时错误文案占同一位置,不与帮助文字叠加(DESIGN 表单页) */
|
|
207
|
+
/* 只读字段:不可聚焦的文本块,不是 readonly 输入框(后者会进 Tab 序列却不可改) */
|
|
208
|
+
.ro { display: flex; align-items: center; min-height: var(--control-height-md); padding: 0 var(--spacing-3); border-radius: var(--radius-md); background: var(--color-bg-readonly); color: var(--color-text-secondary); }
|
|
209
|
+
/* 抽屉 / 弹窗内的分组小标题 */
|
|
210
|
+
.drawer-h { margin: var(--spacing-5) 0 var(--spacing-3); font-size: var(--text-title-sm-size); font-weight: var(--text-weight-label); color: var(--color-text-primary); }
|
|
211
|
+
.drawer-h:first-child { margin-top: 0; }
|
|
212
|
+
/* 表单内的可编辑表格(采购明细这类行内录入):单元格内边距收到 spacing.3 / spacing.2,控件 small,行内错误静态渲染在控件下,删除胶囊与 small 控件对齐 */
|
|
213
|
+
.table-editable .el-table__cell { padding: var(--spacing-2) var(--spacing-3); vertical-align: top; }
|
|
214
|
+
.table-editable .el-form-item { margin-bottom: 0; }
|
|
215
|
+
.table-editable .el-form-item__error { position: static; padding-top: var(--spacing-0-5); }
|
|
216
|
+
.table-editable th.el-table__cell .req::before { content: "*"; color: var(--color-status-error); margin-right: var(--spacing-1); }
|
|
217
|
+
/* 上传区:虚线框 + 浅底,hover 换底,拖入时边线换焦点色,键盘焦点画在整块上 */
|
|
218
|
+
.upload-zone { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: var(--spacing-1); min-height: calc(var(--control-height-md) * 3); padding: var(--spacing-4); border: var(--border-width-default) dashed var(--color-border-strong); border-radius: var(--radius-md); background: var(--color-bg-subtle); color: var(--color-text-secondary); font-size: var(--text-body-size); cursor: pointer; text-align: center; }
|
|
219
|
+
.upload-zone:hover { background: var(--color-bg-hover); }
|
|
220
|
+
.upload-zone.is-over { border-color: var(--color-border-focus); background: var(--color-bg-selected); }
|
|
221
|
+
.upload-zone:has(:focus-visible) { outline: var(--border-width-active) solid var(--color-border-focus); outline-offset: var(--spacing-0-5); }
|
|
222
|
+
.upload-zone.is-full { opacity: var(--opacity-disabled); cursor: not-allowed; }
|
|
223
|
+
.upload-zone input[type="file"] { position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; pointer-events: none; } /* 透明铺满:点 label 触发选择,Tab 仍可到达 */
|
|
224
|
+
.upload-zone { position: relative; }
|
|
225
|
+
.upload-zone b { color: var(--color-text-primary); font-weight: var(--text-weight-label); }
|
|
226
|
+
/* 附件行:类型标签 / 图标 + 文件名与说明 + 操作,行间发丝线;紧跟在上传区后面时首行不画线 */
|
|
227
|
+
.file-row { display: flex; align-items: center; gap: var(--spacing-3); padding: var(--spacing-3) 0; border-top: var(--border-width-default) solid var(--color-border-default); }
|
|
228
|
+
.file-row:first-child { border-top: 0; padding-top: 0; }
|
|
229
|
+
.file-row .text { flex: 1; min-width: 0; }
|
|
230
|
+
.upload-zone + .file-row { margin-top: var(--spacing-3); border-top: 0; }
|
|
231
|
+
|
|
232
|
+
/* ── 设置页:横向表单 + 设置行 ── */
|
|
233
|
+
/* 横向表单:标签列 layout.form.label-width、标签右对齐;Element 用 el-form label-position="right",下面的 .field 规则给纯 CSS 表单 */
|
|
234
|
+
.hform { max-width: calc(var(--layout-form-label-width) + var(--layout-form-max-width)); padding: var(--space-card); }
|
|
235
|
+
.hform .field { display: grid; grid-template-columns: var(--layout-form-label-width) minmax(0, 1fr); column-gap: var(--spacing-4); row-gap: var(--spacing-1-5); align-items: start; }
|
|
236
|
+
.hform .field > label, .hform .field > .label { display: flex; align-items: center; justify-content: flex-end; min-height: var(--control-height-md); text-align: right; }
|
|
237
|
+
.hform .field > :not(label):not(.label) { grid-column: 2; }
|
|
238
|
+
.hform .foot { display: flex; gap: var(--space-inline); padding-left: calc(var(--layout-form-label-width) + var(--spacing-4)); }
|
|
239
|
+
.avatar-row { display: flex; align-items: center; gap: var(--spacing-3); min-height: var(--control-height-md); }
|
|
240
|
+
/* 设置行:标题 + 说明(.sub)在左,开关 / 选择 / 操作在右;行间发丝线 */
|
|
241
|
+
.setting { display: flex; align-items: center; gap: var(--spacing-4); padding: var(--spacing-4) var(--space-card); border-top: var(--border-width-default) solid var(--color-border-default); }
|
|
242
|
+
.setting:first-child { border-top: 0; }
|
|
243
|
+
.setting .text { flex: 1; min-width: 0; }
|
|
244
|
+
.setting .text b { display: block; font-weight: var(--text-weight-label); }
|
|
245
|
+
|
|
246
|
+
/* ── 浮层内原位确认条(bridge/vue/ConfirmBar.vue):替换抽屉 / 弹窗页脚,不叠第二层弹窗;底色中性、只有确认按钮带颜色(彩色文字不落灰底) ── */
|
|
247
|
+
.confirm-bar { display: flex; align-items: center; gap: var(--space-inline); padding: var(--spacing-3) var(--space-card); background: var(--color-bg-subtle); border-top: var(--border-width-default) solid var(--color-border-default); }
|
|
248
|
+
.confirm-bar .msg { flex: 1; min-width: 0; color: var(--color-text-primary); font-size: var(--text-body-sm-size); line-height: var(--text-body-line-height); }
|
|
249
|
+
/* 没有组件库按钮时(React 版未传 renderButton)的兜底按钮:次要 = 描边、主 = 金底白字、危险 = 红底白字 */
|
|
250
|
+
.confirm-bar__btn { height: var(--control-height-md); padding: 0 var(--spacing-4); border-radius: var(--radius-md); font: inherit; font-weight: var(--text-weight-label); cursor: pointer; border: var(--border-width-control) solid var(--color-border-strong); background: var(--color-bg-surface); color: var(--color-text-primary); }
|
|
251
|
+
.confirm-bar__btn.is-primary { background: var(--color-action-primary); border-color: var(--color-action-primary); color: var(--color-text-on-primary); }
|
|
252
|
+
.confirm-bar__btn.is-danger { background: var(--color-action-danger); border-color: var(--color-action-danger); color: var(--color-text-on-danger); }
|
|
253
|
+
.el-drawer__footer .confirm-bar { margin: calc(var(--spacing-3) * -1) calc(var(--space-card) * -1); border-top: 0; } /* 撑满 Element 抽屉页脚(spacing.3 × space.card)的内边距,页脚自带的上边线保留 */
|
|
254
|
+
.el-dialog__footer .confirm-bar { margin: calc(var(--space-card) * -1); margin-top: 0; border-radius: 0 0 var(--radius-lg) var(--radius-lg); } /* 弹窗页脚是 space.card 内边距、无边线:成为带圆角的完整底栏 */
|
|
255
|
+
|
|
256
|
+
/* ── 结果页(403 / 404 / 500):内容区垂直居中,插图 two-tone ── */
|
|
257
|
+
.result { text-align: center; padding: var(--spacing-10) var(--spacing-5); max-width: var(--layout-form-max-width); margin: auto; min-height: calc(100vh - var(--layout-topbar-height) - var(--space-gutter) * 2); display: flex; flex-direction: column; justify-content: center; align-items: center; }
|
|
258
|
+
.result .illu { width: var(--illustration-size-md); height: calc(var(--illustration-size-md) * 0.6); border-radius: var(--radius-lg); background: var(--color-bg-subtle); border: var(--border-width-default) dashed var(--color-border-strong); display: grid; place-items: center; margin-bottom: var(--spacing-5); }
|
|
259
|
+
.result .code { font-size: var(--text-hero-size); line-height: var(--text-hero-line-height); font-weight: var(--text-weight-brand); color: var(--color-text-selected); } /* 主色不当文字色(DESIGN Don't):深一档的 text.selected,页面底上 4.8:1 */
|
|
260
|
+
.result h3 { margin: var(--spacing-2) 0 var(--spacing-1); font-size: var(--text-title-size); font-weight: var(--text-weight-strong); }
|
|
261
|
+
.result p { margin: 0 0 var(--spacing-5); color: var(--color-text-secondary); line-height: var(--text-paragraph-line-height); }
|
|
262
|
+
.result .acts { display: flex; justify-content: center; gap: var(--space-inline); }
|
|
263
|
+
|
|
264
|
+
/* ── 空态 / 加载失败 ── */
|
|
265
|
+
.empty { display: flex; flex-direction: column; align-items: center; gap: var(--spacing-2); padding: var(--spacing-10) var(--space-card); color: var(--color-text-muted); text-align: center; }
|
|
266
|
+
.empty .illu { width: var(--illustration-size-md); height: calc(var(--illustration-size-md) * 0.6); border-radius: var(--radius-md); background: var(--color-bg-subtle); border: var(--border-width-default) dashed var(--color-border-strong); display: grid; place-items: center; margin-bottom: var(--spacing-2); }
|
|
267
|
+
.empty b { color: var(--color-text-secondary); font-weight: var(--text-weight-label); }
|
|
268
|
+
.error-box { display: flex; align-items: center; gap: var(--spacing-3); margin: var(--space-card); padding: var(--spacing-3) var(--spacing-4); border-radius: var(--radius-lg); background: var(--color-status-error-bg); color: var(--color-status-error); font-size: var(--text-body-sm-size); }
|
|
269
|
+
.error-box .el-button { margin-left: auto; }
|
|
270
|
+
|
|
271
|
+
/* ── 登录页:左侧品牌面 bg.brand + text.on-brand(正文用 ≥ 24px 的大字),右侧表单卡 ── */
|
|
272
|
+
.login { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); min-height: 100vh; background: var(--color-bg-page); }
|
|
273
|
+
.login-brand { display: flex; flex-direction: column; justify-content: space-between; gap: var(--space-stack); padding: var(--space-gutter); background: var(--color-bg-brand); color: var(--color-text-on-brand); }
|
|
274
|
+
.login-brand .app-brand { padding: 0; color: inherit; }
|
|
275
|
+
.login-brand .app-brand .logo { background: var(--color-bg-surface); color: var(--color-text-selected); } /* 金底上的 logo 块:表面色底 + 深一档的 text.selected(主色不当文字色),亮暗都过 AA */
|
|
276
|
+
.login-brand .slogan { font-size: var(--text-hero-size); line-height: var(--text-hero-line-height); font-weight: var(--text-weight-strong); }
|
|
277
|
+
.login-brand .lead { margin: var(--spacing-3) 0 0; font-size: var(--text-heading-size); line-height: var(--text-heading-line-height); }
|
|
278
|
+
.login-panel { display: grid; place-items: center; padding: var(--space-gutter); }
|
|
279
|
+
.login-card { width: min(100%, var(--layout-modal-width-sm)); }
|
|
280
|
+
.login-card h1 { margin: 0; font-size: var(--text-heading-size); line-height: var(--text-heading-line-height); }
|
|
281
|
+
.login-card .secondary { margin: 0; }
|
|
282
|
+
.login-card .tabs, .login-card .el-tabs { margin: var(--spacing-5) 0 var(--spacing-6); }
|
|
283
|
+
.login-card .el-tabs > .el-tabs__header { margin-bottom: 0; }
|
|
284
|
+
.login-card .alert, .login-card .el-alert { margin-top: var(--spacing-5); }
|
|
285
|
+
.login-row { display: flex; align-items: center; justify-content: space-between; margin: calc(var(--spacing-2) * -1) 0 var(--spacing-5); }
|
|
286
|
+
.divider-text { display: flex; align-items: center; gap: var(--spacing-3); margin: var(--spacing-6) 0 var(--spacing-4); color: var(--color-text-muted); font-size: var(--text-small-size); }
|
|
287
|
+
.divider-text::before, .divider-text::after { content: ""; flex: 1; height: var(--border-width-default); background: var(--color-border-default); }
|
|
288
|
+
.login-foot { margin: var(--spacing-6) 0 0; color: var(--color-text-muted); font-size: var(--text-small-size); text-align: center; }
|
|
289
|
+
.login-alt { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-inline); }
|
|
290
|
+
.code-row { display: flex; gap: var(--space-inline); width: 100%; }
|
|
291
|
+
.code-row .input, .code-row .el-input { flex: 1; }
|
|
292
|
+
|
|
293
|
+
/* ── 优惠券券面:左侧面额块是品牌面 bg.brand + text.on-brand(面额用展示字;门槛小字属「金底白字」例外),右侧放名称、状态与操作;已结束 / 过期 / 草稿的券加 .is-off,面额块变灰 ── */
|
|
294
|
+
.coupon { display: grid; grid-template-columns: var(--illustration-size-md) minmax(0, 1fr); border: var(--border-width-default) solid var(--color-border-default); border-radius: var(--radius-lg); overflow: hidden; }
|
|
295
|
+
.coupon-value { display: grid; place-content: center; gap: var(--spacing-1); text-align: center; padding: var(--spacing-3); background: var(--color-bg-brand); color: var(--color-text-on-brand); font-size: var(--text-small-size); }
|
|
296
|
+
.coupon-value b { font-family: var(--text-display-family); font-size: var(--text-heading-size); font-weight: var(--text-display-weight); line-height: var(--text-heading-line-height); }
|
|
297
|
+
.coupon.is-off .coupon-value { background: var(--color-bg-subtle); color: var(--color-text-muted); }
|
|
298
|
+
.coupon-body { display: grid; gap: var(--spacing-2); padding: var(--spacing-3) var(--spacing-4); min-width: 0; }
|
|
299
|
+
.coupon-foot { display: flex; align-items: center; justify-content: space-between; }
|
|
300
|
+
|
|
301
|
+
/* ── 图表容器:高度用 token 表达(控件高度 × 7 / × 9) ── */
|
|
302
|
+
.chart { height: calc(var(--control-height-md) * 7); width: 100%; }
|
|
303
|
+
.chart.tall { height: calc(var(--control-height-md) * 9); }
|
|
304
|
+
/* 分类色:.c1–.c6 只设 --c(chart.1–6),图例色块与占比列表的色块读它 */
|
|
305
|
+
.c1 { --c: var(--color-chart-1); } .c2 { --c: var(--color-chart-2); } .c3 { --c: var(--color-chart-3); }
|
|
306
|
+
.c4 { --c: var(--color-chart-4); } .c5 { --c: var(--color-chart-5); } .c6 { --c: var(--color-chart-6); }
|
|
307
|
+
/* 图例:spacing.2.5 见方的色块 + text.small × text.secondary */
|
|
308
|
+
.legend { display: flex; flex-wrap: wrap; gap: var(--spacing-2) var(--spacing-4); font-size: var(--text-small-size); color: var(--color-text-secondary); }
|
|
309
|
+
.legend i { display: inline-block; width: var(--spacing-2-5); height: var(--spacing-2-5); border-radius: var(--radius-xs); margin-right: var(--spacing-1-5); vertical-align: -1px; background: var(--c); }
|
|
310
|
+
.legend i.is-bench { background: var(--color-chart-sequential-1); box-shadow: inset 0 0 0 var(--border-width-default) var(--color-chart-4); }
|
|
311
|
+
/* 占比列表(环形图旁):色块 + 名称 + 占比(strong)+ 金额(muted,可省);列按子元素个数自动排 */
|
|
312
|
+
.alloc-list { display: grid; gap: var(--spacing-3); margin: 0; padding: 0; list-style: none; }
|
|
313
|
+
.alloc-list li { display: grid; grid-template-columns: auto minmax(0, 1fr); grid-auto-flow: column; grid-auto-columns: auto; gap: var(--spacing-2-5); align-items: center; list-style: none; font-size: var(--text-body-sm-size); }
|
|
314
|
+
.alloc-list i { display: inline-block; width: var(--spacing-2-5); height: var(--spacing-2-5); border-radius: var(--radius-xs); background: var(--c); }
|
|
315
|
+
.alloc-list .pct { font-family: var(--text-numeric-family); font-weight: var(--text-weight-strong); font-variant-numeric: var(--text-numeric-variant); color: var(--color-text-primary); }
|
|
316
|
+
.alloc-list .amt { font-family: var(--text-numeric-family); color: var(--color-text-muted); font-variant-numeric: var(--text-numeric-variant); min-width: 6em; text-align: right; }
|
|
317
|
+
/* 目标进度列表:名称 + 数值一行,下面是进度条(纯 CSS 用 .track / .fill,Element 用 el-progress);填充同进度条:左深右浅,品牌金用 chart.gradient.start → end,中性序列是同色饱和度渐变 */
|
|
318
|
+
.goals { display: grid; gap: var(--spacing-5); padding: 0; margin: 0; }
|
|
319
|
+
.goals li { list-style: none; display: grid; gap: var(--spacing-2); }
|
|
320
|
+
.goals .top { display: flex; align-items: baseline; justify-content: space-between; font-size: var(--text-body-sm-size); }
|
|
321
|
+
.goals .top b { font-family: var(--text-numeric-family); font-weight: var(--text-weight-strong); color: var(--color-text-secondary); font-variant-numeric: var(--text-numeric-variant); }
|
|
322
|
+
.goals .track { height: var(--spacing-1-5); border-radius: var(--radius-full); background: var(--color-bg-selected); overflow: hidden; }
|
|
323
|
+
.goals .fill { height: 100%; border-radius: var(--radius-full); background: var(--color-chart-gradient-start); background: linear-gradient(to right, var(--color-chart-gradient-start), var(--color-chart-gradient-end)); }
|
|
324
|
+
.goals .fill.is-neutral { --fill: var(--color-chart-4); background: var(--fill); background: linear-gradient(to right, var(--fill), hwb(from var(--fill) h calc(w + (100 - w - b) * (1 - var(--chart-gradient-saturation))) b)); }
|
|
325
|
+
|
|
326
|
+
/* ── 纯 CSS 表格 `.table`(没有组件库的模板项目用;Element / shadcn 走各自桥接):表头 bg.subtle + text.secondary,单元格 table.cell.padding,行 hover bg.hover,发丝线 ── */
|
|
327
|
+
.table-wrap { overflow-x: auto; } /* 手机上表格内部横向滚动,文档级不溢出 */
|
|
328
|
+
.table { width: 100%; border-collapse: collapse; font-size: var(--text-body-sm-size); }
|
|
329
|
+
.table th, .table td { padding: var(--table-cell-padding-y) var(--table-cell-padding-x); border-bottom: var(--border-width-default) solid var(--color-border-default); text-align: left; vertical-align: middle; white-space: nowrap; }
|
|
330
|
+
.table th { background: var(--color-bg-subtle); color: var(--color-text-secondary); font-weight: var(--text-weight-label); font-size: var(--text-small-size); }
|
|
331
|
+
.table tbody tr:hover { background: var(--color-bg-hover); }
|
|
332
|
+
.table tbody tr.is-selected { background: var(--color-bg-hover); } /* 勾选行与悬停同色(DESIGN 表格「选中行」),再悬停深一档 */
|
|
333
|
+
.table tbody tr.is-selected:hover { background: var(--color-bg-selected-hover); }
|
|
334
|
+
.table tbody tr:last-child td { border-bottom: 0; }
|
|
335
|
+
.table .num-col { text-align: right; font-family: var(--text-numeric-family); }
|
|
336
|
+
|
|
337
|
+
/* ── 统计卡(bridge/vue/StatCard.vue 与 bridge/react/StatCard.tsx 共用这组类):数据大字 + 涨跌 + 口径说明;与普通卡片同一形态(细边线 + 柔阴影),不加品牌指示条 ── */
|
|
338
|
+
.stats { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--space-stack); }
|
|
339
|
+
.stat-card { background: var(--color-bg-surface); border: var(--border-width-default) solid var(--color-border-default); border-radius: var(--radius-lg); box-shadow: 0 var(--elevation-card-y) var(--elevation-card-blur) var(--elevation-card-color); padding: var(--spacing-5) var(--space-card); min-width: 0; }
|
|
340
|
+
.stat-card__label { display: flex; align-items: center; gap: var(--spacing-1); color: var(--color-text-secondary); font-size: var(--text-body-sm-size); }
|
|
341
|
+
.stat-card__hint { display: inline-grid; place-items: center; width: var(--control-hit-min); height: var(--control-hit-min); margin: calc((var(--control-hit-min) - var(--size-icon-sm)) / -2) 0; padding: 0; border: 0; border-radius: var(--radius-full); background: transparent; color: var(--color-icon-muted); cursor: help; font: inherit; }
|
|
342
|
+
.stat-card__hint:hover { color: var(--color-text-primary); background: var(--color-bg-hover); }
|
|
343
|
+
.stat-card__value { font-family: var(--text-display-family); font-size: var(--text-display-size); font-weight: var(--text-display-weight); line-height: var(--text-display-line-height); margin: var(--spacing-1-5) 0 var(--spacing-1); letter-spacing: var(--text-display-tracking); color: var(--color-text-primary); }
|
|
344
|
+
.stat-card__delta { font-family: var(--text-numeric-family); font-size: var(--text-small-size); } /* 颜色交给 .up / .down */
|
|
345
|
+
.stat-card__note { color: var(--color-text-muted); font-size: var(--text-small-size); }
|
|
346
|
+
|
|
347
|
+
/* ── 表格骨架(bridge/vue/TableSkeleton.vue 与 bridge/react/TableSkeleton.tsx 共用):表头 + 等高行 + 列宽,加载完成不跳版 ── */
|
|
348
|
+
.tskel { overflow: hidden; }
|
|
349
|
+
.tskel-head, .tskel-row { display: flex; align-items: center; border-bottom: var(--border-width-default) solid var(--color-border-default); }
|
|
350
|
+
.tskel-head { background: var(--color-bg-subtle); height: calc(var(--table-cell-padding-y) * 2 + var(--text-body-line-height) * var(--text-small-size)); }
|
|
351
|
+
.tskel-row { height: calc(var(--table-cell-padding-y) * 2 + var(--text-body-line-height) * var(--text-body-size) + var(--text-body-line-height) * var(--text-small-size)); }
|
|
352
|
+
.tskel .cell { padding: 0 var(--table-cell-padding-x); min-width: 0; }
|
|
353
|
+
/* 贴边放在卡片里时(同 el-table 与 .panel .table 的首尾列),首尾列让出卡片内边距,加载完成不跳版 */
|
|
354
|
+
:is(.el-card.flat > .el-card__body, .el-card.flat .el-tab-pane, .panel, .panel > .table-wrap) > .tskel .cell:first-child { padding-left: var(--space-card); }
|
|
355
|
+
:is(.el-card.flat > .el-card__body, .el-card.flat .el-tab-pane, .panel, .panel > .table-wrap) > .tskel .cell:last-child { padding-right: var(--space-card); }
|
|
356
|
+
.tskel .bar { display: block; height: var(--text-small-size); border-radius: var(--radius-xs); width: 70%; background: linear-gradient(90deg, var(--color-bg-skeleton) 25%, var(--color-bg-skeleton-highlight) 50%, var(--color-bg-skeleton) 75%); background-size: 400% 100%; animation: tskel-shimmer 1.4s var(--motion-easing-standard) infinite; }
|
|
357
|
+
.tskel .bar.head { width: 45%; }
|
|
358
|
+
.tskel .bar.short { width: 40%; }
|
|
359
|
+
@keyframes tskel-shimmer { 0% { background-position: 100% 50%; } 100% { background-position: 0 50%; } }
|
|
360
|
+
|
|
361
|
+
/* ── 状态条 ── */
|
|
362
|
+
.stat-strip { display: inline-flex; flex-wrap: wrap; gap: var(--space-inline); margin-bottom: var(--space-stack); }
|
|
363
|
+
.content > .stat-strip { margin-bottom: 0; } /* 直接放在内容区时间距交给内容区的 gap */
|
|
364
|
+
.stat-strip .cell { display: inline-flex; align-items: center; gap: var(--spacing-2); height: var(--control-height-lg); padding: 0 var(--spacing-4); background: var(--color-bg-surface); border: var(--border-width-default) solid var(--color-border-default); border-radius: var(--radius-md); color: var(--color-text-secondary); font-size: var(--text-body-sm-size); }
|
|
365
|
+
.stat-strip .cell.is-clickable { cursor: pointer; }
|
|
366
|
+
.stat-strip .cell.is-clickable:hover { background: var(--color-bg-hover); }
|
|
367
|
+
.stat-strip .cell.is-active { border-color: var(--color-border-focus); color: var(--color-text-primary); }
|
|
368
|
+
.stat-strip .cell .value { font-family: var(--text-display-family); font-size: var(--text-title-size); font-weight: var(--text-weight-strong); color: var(--color-text-primary); font-variant-numeric: var(--text-numeric-variant); }
|
|
369
|
+
|
|
370
|
+
/* ── 打印:隐藏导航与按钮,色底转边线 ── */
|
|
371
|
+
/* ── 键盘焦点兜底与减少动态效果:与 element-plus.css / shadcn.css 同一份约定。有桥接时规则重复无害;纯 CSS 渠道(Flask 等无组件库)靠这里拿到焦点色(border.focus)2px 焦点环而不是浏览器默认蓝环。
|
|
372
|
+
shadcn 组件(data-slot)走桥接的 ring 模型,这里不叠。 ── */
|
|
373
|
+
:where(a, button, [role="button"], [role="menuitem"], [role="tab"], summary, [tabindex]:not([tabindex="-1"]):not(input, textarea, select)):not([data-slot]):focus-visible { outline: var(--border-width-active) solid var(--color-border-focus); outline-offset: 1px; }
|
|
374
|
+
@media (prefers-reduced-motion: reduce) {
|
|
375
|
+
:root { --motion-duration-fast: 0ms; --motion-duration-normal: 0ms; --motion-duration-slow: 0ms; }
|
|
376
|
+
*, *::before, *::after { transition-duration: 0s !important; animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; scroll-behavior: auto !important; }
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
/* ── 内容区窄于 880px(右栏放得下 KPI 大字约 300px,按 1.8 : 1 折算)时看板与两栏收成单栏;按内容区而不是视口判断,侧栏折叠与否都成立 ── */
|
|
380
|
+
@container (max-width: 880px) {
|
|
381
|
+
.board, .two-col { grid-template-columns: minmax(0, 1fr); }
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/* ── 三端(值是 layout.breakpoint.narrow / mobile = 992 / 768 的字面镜像,见 DESIGN「布局与响应式」;CSS 媒体查询不能用 var(),改 token 必须同步) ── */
|
|
385
|
+
/* 手机(≤ 768):侧栏离屏抽屉。项目 JS 只需维护 .is-nav-open(汉堡开 / 遮罩与选中菜单关) */
|
|
386
|
+
.menu-btn { display: none; }
|
|
387
|
+
.sidebar-mask { display: none; position: fixed; inset: 0; background: var(--color-bg-overlay); z-index: calc(var(--layer-sticky) + 1); border: 0; padding: 0; }
|
|
388
|
+
@media (max-width: 992px) {
|
|
389
|
+
.stats { grid-template-columns: repeat(2, 1fr); }
|
|
390
|
+
}
|
|
391
|
+
@media (max-width: 768px) {
|
|
392
|
+
.app, .app.is-collapsed { display: block; }
|
|
393
|
+
.sidebar { position: fixed; left: 0; top: 0; bottom: 0; height: 100dvh; width: var(--layout-sidebar-width); z-index: calc(var(--layer-sticky) + 2); transform: translateX(-100%); transition: transform var(--motion-duration-normal) var(--motion-easing-standard); box-shadow: none; }
|
|
394
|
+
.is-nav-open .sidebar { transform: none; box-shadow: 0 var(--elevation-modal-y) var(--elevation-modal-blur) var(--elevation-modal-color); }
|
|
395
|
+
.is-nav-open .sidebar-mask { display: block; }
|
|
396
|
+
.is-collapsed .app-brand { padding: 0 var(--spacing-4); justify-content: flex-start; } /* 抽屉里永远是展开形态:折叠态样式全部还原 */
|
|
397
|
+
.is-collapsed .app-brand > :not(.logo) { display: inline; }
|
|
398
|
+
.is-collapsed .nav-group { display: block; }
|
|
399
|
+
.is-collapsed .nav .nav-group:not(:first-child) + :is(a, button) { margin-top: 0; }
|
|
400
|
+
.is-collapsed .nav a, .is-collapsed .nav button { justify-content: flex-start; padding: 0 var(--spacing-3); }
|
|
401
|
+
.is-collapsed .nav .nav-label { display: inline; }
|
|
402
|
+
.menu-btn { display: inline-grid; }
|
|
403
|
+
.collapse-btn { display: none; } /* 折叠切换在手机上无意义(侧栏是抽屉) */
|
|
404
|
+
.content { padding: var(--space-stack); gap: var(--spacing-3); }
|
|
405
|
+
.topbar { padding: 0 var(--space-stack); }
|
|
406
|
+
.topbar .search, .topbar label.search { display: none; } /* 顶栏全局搜索在手机上收起(页面内搜索仍在筛选栏);label 写法的特异性更高,要单独列出 */
|
|
407
|
+
.page-head { flex-wrap: wrap; }
|
|
408
|
+
.page-head .actions { flex: 1 1 100%; flex-wrap: wrap; margin-left: 0; justify-content: flex-start; } /* 覆盖桌面的 flex:none:动作区整行换下、内部可换行 */
|
|
409
|
+
.card-head { flex-wrap: wrap; }
|
|
410
|
+
.card-head .right, .batch .right { flex-wrap: wrap; min-width: 0; justify-content: flex-start; }
|
|
411
|
+
.batch { flex-wrap: wrap; }
|
|
412
|
+
.batch > * { white-space: nowrap; } /* 「已选 N 项」不竖排 */
|
|
413
|
+
.batch .right { flex: 1 1 100%; } /* 批量动作整行换下 */
|
|
414
|
+
.crumbs span:not(.sep):not(:last-child), .crumbs .sep { display: none; } /* 面包屑只留当前页 */
|
|
415
|
+
.stats { grid-template-columns: 1fr; }
|
|
416
|
+
.filter .conds { flex-wrap: wrap; width: 100%; }
|
|
417
|
+
.filter .conds > * { flex: 1 1 auto; min-width: 0; }
|
|
418
|
+
.filter .acts { width: 100%; justify-content: flex-end; }
|
|
419
|
+
.form-grid { grid-template-columns: minmax(0, 1fr); } /* minmax(0,…):行内录入表格等宽内容不得撑破网格 */
|
|
420
|
+
.form-grid .span2 { grid-column: auto; }
|
|
421
|
+
.two-col { grid-template-columns: 1fr; }
|
|
422
|
+
.pager { flex-wrap: wrap; justify-content: center; }
|
|
423
|
+
.topbar .user-name, .topbar .user-caret { display: none; } /* 顶栏只留当前页面包屑与头像 */
|
|
424
|
+
.section-head { flex-wrap: wrap; }
|
|
425
|
+
.section-head .right { flex: 1 1 100%; flex-wrap: wrap; margin-left: 0; } /* 板块的筛选整行换下 */
|
|
426
|
+
.setting { flex-wrap: wrap; }
|
|
427
|
+
.login { grid-template-columns: minmax(0, 1fr); }
|
|
428
|
+
.login-brand { padding: var(--spacing-6) var(--spacing-4); }
|
|
429
|
+
.login-brand .slogan { font-size: var(--text-heading-size); line-height: var(--text-heading-line-height); }
|
|
430
|
+
.login-brand .lead { font-size: var(--text-title-size); }
|
|
431
|
+
.login-panel { padding: var(--spacing-6) var(--spacing-4); }
|
|
432
|
+
}
|
|
433
|
+
/* 触屏:命中区升到 control.hit-touch,视觉不变(min-height 拉高由 flex 居中吸收) */
|
|
434
|
+
@media (pointer: coarse) {
|
|
435
|
+
.act, .go, .link, .more-link, .iconbtn, .title-link { min-height: var(--control-hit-touch); }
|
|
436
|
+
.chip button.x { min-width: var(--control-hit-touch); height: var(--control-hit-touch); }
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
@media print {
|
|
440
|
+
.sidebar, .topbar, .el-button, .no-print { display: none !important; }
|
|
441
|
+
.app { grid-template-columns: 1fr; }
|
|
442
|
+
.el-card, .badge, .status { box-shadow: none !important; }
|
|
443
|
+
.badge, .chip, .batch { background: transparent !important; border: var(--border-width-default) solid currentColor; }
|
|
444
|
+
}
|