| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860 |
- <!doctype html>
- <html lang="zh-CN">
- <head>
- <meta charset="UTF-8" />
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
- <title>Fresh Mint · 宽弹窗多列布局 Demo</title>
- <link rel="preconnect" href="https://fonts.googleapis.com" />
- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
- <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet" />
- <style>
- :root {
- --primary: #10b981;
- --primary-dark: #059669;
- --primary-bg: #ecfdf5;
- --text: #1e293b;
- --text-muted: #64748b;
- --border: #d9e8e2;
- --surface: #ffffff;
- --table-head: #f8fafc;
- --shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
- --shadow-lg: 0 8px 24px rgba(15, 23, 42, 0.08);
- --radius: 10px;
- --danger: #dc2626;
- }
- * { box-sizing: border-box; }
- body {
- margin: 0;
- min-height: 100vh;
- font-family: Inter, -apple-system, sans-serif;
- font-size: 14px;
- line-height: 1.5;
- color: var(--text);
- background: #f8fafc;
- }
- .page { max-width: 1320px; margin: 0 auto; padding: 32px 24px 80px; }
- .page-head { margin-bottom: 24px; }
- .page-head h1 { margin: 0 0 8px; font-size: 24px; font-weight: 700; }
- .page-head h1 span { color: var(--primary); }
- .page-head p { margin: 0 0 12px; color: var(--text-muted); max-width: 780px; line-height: 1.6; }
- .page-head a { color: var(--primary-dark); }
- .width-bar {
- display: flex;
- flex-wrap: wrap;
- gap: 8px;
- margin-bottom: 24px;
- }
- .width-pill {
- font-size: 12px;
- font-weight: 600;
- padding: 4px 10px;
- border-radius: 6px;
- background: var(--surface);
- border: 1px solid var(--border);
- color: var(--text-muted);
- }
- .width-pill b { color: var(--text); }
- .compare-grid {
- display: grid;
- grid-template-columns: repeat(2, 1fr);
- gap: 20px;
- }
- @media (max-width: 1000px) { .compare-grid { grid-template-columns: 1fr; } }
- .layout-card {
- background: var(--surface);
- border: 1px solid var(--border);
- border-radius: var(--radius);
- overflow: hidden;
- box-shadow: var(--shadow);
- display: flex;
- flex-direction: column;
- }
- .layout-card.rec { border-color: rgba(16, 185, 129, 0.4); }
- .layout-card-head {
- padding: 14px 16px;
- border-bottom: 1px solid var(--border);
- display: flex;
- justify-content: space-between;
- gap: 12px;
- }
- .layout-card-head h2 { margin: 0 0 4px; font-size: 15px; font-weight: 600; }
- .layout-card-head p { margin: 0; font-size: 12px; color: var(--text-muted); line-height: 1.45; }
- .badge {
- flex-shrink: 0;
- font-size: 11px;
- font-weight: 700;
- padding: 3px 8px;
- border-radius: 6px;
- background: var(--primary-bg);
- color: var(--primary-dark);
- white-space: nowrap;
- }
- .badge.blue { background: #eff6ff; color: #2563eb; }
- .badge.amber { background: #fffbeb; color: #b45309; }
- .layout-preview {
- flex: 1;
- padding: 12px;
- background: #f1f5f9;
- min-height: 260px;
- overflow: auto;
- }
- .mini-wrap {
- transform: scale(0.92);
- transform-origin: top center;
- }
- .mini-dialog {
- background: #fff;
- border-radius: 8px;
- border: 1px solid var(--border);
- overflow: hidden;
- font-size: 11px;
- margin: 0 auto;
- }
- .mini-dialog.w920 { max-width: 420px; }
- .mini-dialog.w960 { max-width: 440px; }
- .mini-dialog.w1040 { max-width: 480px; }
- .mini-dialog.w1120 { max-width: 520px; }
- .layout-card-foot {
- padding: 12px 16px;
- border-top: 1px solid var(--border);
- display: flex;
- justify-content: space-between;
- align-items: center;
- background: var(--table-head);
- }
- .layout-card-foot .meta { font-size: 11px; color: var(--text-muted); }
- .btn {
- height: 32px;
- padding: 0 12px;
- border-radius: 8px;
- font-size: 12px;
- font-weight: 600;
- font-family: inherit;
- cursor: pointer;
- border: 1px solid var(--primary);
- background: var(--primary);
- color: #fff;
- }
- /* ── 弹窗通用 ── */
- .backdrop {
- position: fixed;
- inset: 0;
- background: rgba(15, 23, 42, 0.45);
- display: none;
- align-items: center;
- justify-content: center;
- padding: 20px;
- z-index: 1000;
- backdrop-filter: none;
- }
- .backdrop.open { display: flex; }
- .dialog {
- background: #fff;
- border-radius: var(--radius);
- box-shadow: var(--shadow-lg);
- width: 100%;
- max-height: calc(100vh - 40px);
- display: flex;
- flex-direction: column;
- overflow: hidden;
- animation: pop 0.2s ease;
- }
- @keyframes pop {
- from { opacity: 0; transform: scale(0.97); }
- to { opacity: 1; transform: none; }
- }
- .dialog.w920 { max-width: 920px; }
- .dialog.w720 { max-width: 720px; }
- .dialog.w960 { max-width: 960px; }
- .dialog.w1040 { max-width: 1040px; }
- .dialog.w1120 { max-width: 1120px; }
- .dialog-hero {
- padding: 16px 22px;
- background: linear-gradient(135deg, var(--primary-bg), #fff);
- border-bottom: 1px solid var(--border);
- display: flex;
- align-items: flex-start;
- justify-content: space-between;
- gap: 16px;
- }
- .dialog-hero h2 { margin: 0 0 4px; font-size: 18px; font-weight: 700; }
- .dialog-hero p { margin: 0; font-size: 13px; color: var(--text-muted); }
- .width-tag {
- flex-shrink: 0;
- font-size: 11px;
- font-weight: 700;
- padding: 4px 10px;
- border-radius: 6px;
- background: #eff6ff;
- color: #2563eb;
- border: 1px solid rgba(37, 99, 235, 0.2);
- }
- .dialog-scroll { overflow-y: auto; flex: 1; }
- .dialog-foot {
- padding: 12px 22px;
- border-top: 1px solid var(--border);
- display: flex;
- justify-content: flex-end;
- background: var(--table-head);
- }
- .dialog-foot .btn-ghost {
- height: 36px;
- padding: 0 14px;
- border-radius: 8px;
- font-size: 13px;
- font-weight: 600;
- font-family: inherit;
- cursor: pointer;
- border: 1px solid var(--border);
- background: #fff;
- color: var(--text);
- }
- .view-section {
- padding: 18px 22px;
- border-bottom: 1px solid #ebeef5;
- }
- .view-section:last-child { border-bottom: none; }
- .view-section h3 {
- margin: 0 0 14px;
- padding-left: 10px;
- font-size: 14px;
- font-weight: 600;
- border-left: 3px solid var(--primary);
- line-height: 1;
- }
- /* KV 上下堆叠 + 多列 grid */
- .kv-grid {
- display: grid;
- gap: 12px 18px;
- }
- .kv-grid.cols-2 { grid-template-columns: repeat(2, 1fr); }
- .kv-grid.cols-3 { grid-template-columns: repeat(3, 1fr); }
- .kv-grid.cols-4 { grid-template-columns: repeat(4, 1fr); }
- .kv-item { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
- .kv-item dt { margin: 0; font-size: 11px; color: var(--text-muted); }
- .kv-item dd { margin: 0; font-size: 13px; font-weight: 600; word-break: break-word; line-height: 1.45; }
- .kv-item.span-2 { grid-column: span 2; }
- .kv-item.span-3 { grid-column: span 3; }
- .kv-item.span-4 { grid-column: span 4; }
- .kv-item.span-all { grid-column: 1 / -1; }
- .tag {
- display: inline-block;
- padding: 1px 8px;
- border-radius: 5px;
- font-size: 12px;
- font-weight: 600;
- background: var(--primary-bg);
- color: var(--primary-dark);
- border: 1px solid rgba(16, 185, 129, 0.25);
- }
- /* 双区块并排 */
- .section-cols-2 {
- display: grid;
- grid-template-columns: 1fr 1fr;
- gap: 0;
- }
- .section-cols-2 .view-section {
- border-bottom: none;
- border-right: 1px solid #ebeef5;
- }
- .section-cols-2 .view-section:last-child { border-right: none; }
- @media (max-width: 800px) {
- .section-cols-2 { grid-template-columns: 1fr; }
- .section-cols-2 .view-section { border-right: none; border-bottom: 1px solid #ebeef5; }
- }
- /* 左右分栏 */
- .view-split {
- display: flex;
- min-height: 400px;
- }
- .view-split-left {
- flex: 0 0 340px;
- border-right: 1px solid var(--border);
- overflow-y: auto;
- }
- .view-split-right {
- flex: 1;
- overflow-y: auto;
- background: var(--table-head);
- }
- .view-split-left .view-section,
- .view-split-right .view-section { border-bottom: none; }
- @media (max-width: 860px) {
- .view-split { flex-direction: column; }
- .view-split-left { flex: none; border-right: none; border-bottom: 1px solid var(--border); }
- }
- /* 2×2 卡片宫格 */
- .card-grid-2x2 {
- display: grid;
- grid-template-columns: 1fr 1fr;
- gap: 14px;
- }
- @media (max-width: 720px) { .card-grid-2x2 { grid-template-columns: 1fr; } }
- .info-card {
- border: 1px solid var(--border);
- border-radius: var(--radius);
- padding: 14px 16px;
- background: #fff;
- }
- .info-card h4 {
- margin: 0 0 12px;
- font-size: 13px;
- font-weight: 600;
- color: var(--primary-dark);
- display: flex;
- align-items: center;
- gap: 6px;
- }
- .info-card h4::before {
- content: "";
- width: 6px;
- height: 6px;
- border-radius: 50%;
- background: var(--primary);
- }
- .info-card .kv-grid { gap: 10px 12px; }
- /* 宽表双组(一行两组 label-value) */
- .kv-wide-table {
- width: 100%;
- border-collapse: collapse;
- font-size: 13px;
- }
- .kv-wide-table td {
- padding: 9px 12px;
- border-bottom: 1px solid #f1f5f9;
- vertical-align: top;
- }
- .kv-wide-table .lbl {
- width: 11%;
- color: var(--text-muted);
- font-size: 11px;
- font-weight: 500;
- white-space: nowrap;
- }
- .kv-wide-table .val {
- width: 39%;
- font-weight: 600;
- word-break: break-word;
- padding-right: 20px;
- }
- .kv-wide-table tr:last-child td { border-bottom: none; }
- /* ── KV 表格模式(左右 label | value)── */
- .demo-section { margin-top: 32px; }
- .demo-section h2 {
- margin: 0 0 8px;
- font-size: 18px;
- font-weight: 700;
- }
- .demo-section > p {
- margin: 0 0 16px;
- color: var(--text-muted);
- max-width: 820px;
- line-height: 1.6;
- font-size: 13px;
- }
- .kv-table-wrap {
- border: 1px solid var(--border);
- border-radius: 8px;
- overflow: hidden;
- background: #fff;
- }
- .kv-table {
- width: 100%;
- border-collapse: collapse;
- font-size: 13px;
- table-layout: fixed;
- }
- .kv-table th {
- padding: 10px 12px;
- background: var(--table-head);
- color: var(--text-muted);
- font-size: 11px;
- font-weight: 500;
- text-align: left;
- vertical-align: top;
- border-bottom: 1px solid #eef2f6;
- border-right: 1px solid #eef2f6;
- white-space: nowrap;
- }
- .kv-table td {
- padding: 10px 14px;
- font-weight: 600;
- color: var(--text);
- border-bottom: 1px solid #eef2f6;
- vertical-align: top;
- word-break: break-word;
- line-height: 1.45;
- }
- .kv-table tr:last-child th,
- .kv-table tr:last-child td { border-bottom: none; }
- .kv-table .money-val { color: var(--danger); }
- .kv-table .muted-val { color: var(--text-muted); font-weight: 600; }
- /* 每行一组 label | value */
- .kv-table--pair th { width: 128px; }
- .kv-table--pair tr.row-long th { width: 128px; }
- /* 每行两组 label | value | label | value */
- .kv-table--dual th { width: 12%; }
- .kv-table--dual td { width: 38%; }
- .kv-table--dual tr.row-long th { width: 12%; }
- .kv-table--dual tr.row-long td { width: auto; }
- /* 斑马纹:无竖线、行间浅底 */
- .kv-table--zebra { table-layout: auto; }
- .kv-table--zebra th {
- border-right: none;
- background: transparent;
- color: var(--text-muted);
- width: 110px;
- padding-left: 14px;
- }
- .kv-table--zebra td {
- border-bottom: 1px solid #f1f5f9;
- padding-right: 14px;
- }
- .kv-table--zebra tbody tr:nth-child(even) { background: #f8fafc; }
- .kv-table--zebra tr.row-long th { vertical-align: top; padding-top: 12px; }
- .kv-table--zebra.kv-table--dual th { width: 11%; }
- .kv-table--zebra.kv-table--dual td { width: 39%; }
- /* 紧凑:更小行高 */
- .kv-table--compact th,
- .kv-table--compact td { padding: 8px 10px; }
- .kv-table--compact th { font-size: 10px; }
- .kv-table--compact td { font-size: 12px; }
- .mini-dialog .kv-table th { font-size: 8px; padding: 4px 5px; }
- .mini-dialog .kv-table td { font-size: 9px; padding: 4px 6px; }
- .mini-dialog .kv-table .tag { font-size: 8px; padding: 0 4px; }
- /* ── 其他展示模式(非 KV 栅格 / 非 KV 表)── */
- .stat-row {
- display: grid;
- grid-template-columns: repeat(4, 1fr);
- gap: 10px;
- }
- @media (max-width: 720px) { .stat-row { grid-template-columns: repeat(2, 1fr); } }
- .stat-cell {
- padding: 12px 14px;
- border: 1px solid var(--border);
- border-radius: 8px;
- background: #fff;
- box-shadow: var(--shadow);
- }
- .stat-cell .lbl { font-size: 11px; color: var(--text-muted); margin-bottom: 4px; }
- .stat-cell .val { font-size: 15px; font-weight: 700; line-height: 1.3; font-variant-numeric: tabular-nums; }
- .stat-cell .val.money { color: var(--danger); }
- .mini-dialog .stat-row { grid-template-columns: repeat(2, 1fr); gap: 6px; }
- .mini-dialog .stat-cell { padding: 6px 8px; }
- .mini-dialog .stat-cell .val { font-size: 10px; }
- .prop-list {
- border: 1px solid var(--border);
- border-radius: 8px;
- overflow: hidden;
- background: #fff;
- }
- .prop-row {
- display: flex;
- align-items: flex-start;
- gap: 12px;
- padding: 10px 14px;
- border-bottom: 1px solid #f1f5f9;
- font-size: 13px;
- }
- .prop-row:last-child { border-bottom: none; }
- .prop-row .lbl {
- flex: 0 0 108px;
- font-size: 11px;
- color: var(--text-muted);
- font-weight: 500;
- line-height: 1.45;
- }
- .prop-row .val { flex: 1; font-weight: 600; word-break: break-word; line-height: 1.45; }
- .prop-row .val.money { color: var(--danger); }
- .mini-dialog .prop-row { padding: 5px 8px; }
- .mini-dialog .prop-row .lbl { flex-basis: 56px; font-size: 8px; }
- .mini-dialog .prop-row .val { font-size: 9px; }
- .view-tabs {
- display: flex;
- flex-wrap: wrap;
- gap: 8px;
- padding: 0 0 14px;
- margin-bottom: 4px;
- }
- .tab-chip {
- height: 32px;
- padding: 0 14px;
- border-radius: 8px;
- font-size: 12px;
- font-weight: 600;
- font-family: inherit;
- cursor: pointer;
- border: 1px solid var(--border);
- background: #fff;
- color: var(--text-muted);
- transition: background 0.15s, color 0.15s, border-color 0.15s;
- }
- .tab-chip.is-active {
- background: var(--primary);
- border-color: var(--primary);
- color: #fff;
- }
- .tab-panel { display: none; }
- .tab-panel.is-active { display: block; }
- .mini-dialog .tab-chip { height: 20px; padding: 0 8px; font-size: 8px; }
- .view-timeline {
- padding-left: 18px;
- border-left: 2px solid var(--border);
- margin-left: 8px;
- }
- .view-tl-item {
- position: relative;
- padding: 0 0 16px 14px;
- font-size: 13px;
- }
- .view-tl-item:last-child { padding-bottom: 0; }
- .view-tl-item::before {
- content: "";
- position: absolute;
- left: -25px;
- top: 4px;
- width: 10px;
- height: 10px;
- border-radius: 50%;
- background: var(--primary);
- border: 2px solid #fff;
- box-shadow: 0 0 0 1px var(--border);
- }
- .view-tl-item.muted::before { background: #cbd5e1; }
- .view-tl-item.future::before { background: #fff; border-color: var(--primary); }
- .view-tl-time { font-size: 11px; color: var(--text-muted); margin-bottom: 2px; }
- .view-tl-title { font-weight: 600; margin-bottom: 2px; }
- .view-tl-desc { font-size: 12px; color: var(--text-muted); line-height: 1.45; }
- .mini-dialog .view-tl-item { padding-bottom: 10px; font-size: 9px; }
- .mini-dialog .view-tl-time { font-size: 8px; }
- .acc-list { display: flex; flex-direction: column; gap: 8px; }
- .acc-item {
- border: 1px solid var(--border);
- border-radius: 8px;
- overflow: hidden;
- background: #fff;
- }
- .acc-hd {
- width: 100%;
- padding: 11px 14px;
- border: none;
- background: var(--table-head);
- font-size: 13px;
- font-weight: 600;
- font-family: inherit;
- color: var(--text);
- cursor: pointer;
- display: flex;
- align-items: center;
- justify-content: space-between;
- gap: 8px;
- text-align: left;
- }
- .acc-hd::after {
- content: "▾";
- font-size: 12px;
- color: var(--text-muted);
- transition: transform 0.15s;
- }
- .acc-item.is-open .acc-hd::after { transform: rotate(180deg); }
- .acc-bd { display: none; padding: 12px 14px 14px; border-top: 1px solid #f1f5f9; }
- .acc-item.is-open .acc-bd { display: block; }
- .mini-dialog .acc-hd { padding: 6px 8px; font-size: 9px; }
- .mini-dialog .acc-bd { padding: 6px 8px; }
- .meta-chips {
- display: flex;
- flex-wrap: wrap;
- gap: 8px;
- }
- .meta-chip {
- font-size: 12px;
- padding: 6px 10px;
- border-radius: 6px;
- background: var(--table-head);
- border: 1px solid var(--border);
- color: var(--text);
- font-weight: 600;
- }
- .meta-chip b {
- font-weight: 500;
- color: var(--text-muted);
- margin-right: 4px;
- }
- .prose-block {
- padding: 14px 16px;
- background: var(--table-head);
- border: 1px solid var(--border);
- border-radius: 8px;
- font-size: 13px;
- line-height: 1.65;
- color: var(--text);
- word-break: break-word;
- }
- .prose-block h4 {
- margin: 0 0 8px;
- font-size: 12px;
- font-weight: 600;
- color: var(--text-muted);
- }
- .mini-dialog .meta-chip { font-size: 8px; padding: 3px 6px; }
- .mini-dialog .prose-block { font-size: 8px; padding: 6px 8px; }
- .view-route-bar {
- display: flex;
- align-items: center;
- min-height: 88px;
- padding: 16px 20px;
- border-radius: var(--radius);
- border: 1px solid var(--border);
- background: linear-gradient(90deg, #ecfdf5 0%, #d1fae5 40%, #a7f3d0 100%);
- }
- .route-node {
- flex: 0 0 auto;
- text-align: center;
- min-width: 64px;
- }
- .route-node .dot {
- width: 12px;
- height: 12px;
- border-radius: 50%;
- background: #fff;
- border: 2px solid var(--primary);
- margin: 0 auto 6px;
- opacity: 0.55;
- }
- .route-node.is-done .dot,
- .route-node.is-active .dot {
- background: var(--primary);
- opacity: 1;
- box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.2);
- }
- .route-node .lbl { font-size: 11px; font-weight: 600; color: var(--text); }
- .route-node .sub { font-size: 10px; color: var(--text-muted); margin-top: 2px; }
- .view-route-line {
- flex: 1;
- height: 3px;
- background: var(--primary);
- opacity: 0.35;
- border-radius: 2px;
- margin: 0 8px 18px;
- min-width: 12px;
- }
- .view-route-line.is-done { opacity: 0.75; }
- .mini-dialog .view-route-bar { min-height: 48px; padding: 8px 10px; }
- .mini-dialog .route-node .lbl { font-size: 7px; }
- .mini-dialog .route-node .dot { width: 8px; height: 8px; }
- .view-bento {
- display: grid;
- grid-template-columns: 1.2fr 0.8fr;
- gap: 12px;
- }
- @media (max-width: 720px) { .view-bento { grid-template-columns: 1fr; } }
- .view-card {
- border: 1px solid var(--border);
- border-radius: var(--radius);
- padding: 14px 16px;
- background: #fff;
- }
- .view-card h4 {
- margin: 0 0 10px;
- font-size: 12px;
- font-weight: 700;
- color: var(--text-muted);
- letter-spacing: 0.04em;
- }
- .view-card.span-full { grid-column: 1 / -1; }
- .mini-dialog .view-bento { grid-template-columns: 1fr 1fr; gap: 6px; }
- .mini-dialog .view-card { padding: 6px 8px; }
- .mini-dialog .view-card h4 { font-size: 8px; margin-bottom: 4px; }
- .note-bar {
- margin-top: 24px;
- padding: 14px 16px;
- background: var(--primary-bg);
- border: 1px solid rgba(16, 185, 129, 0.25);
- border-radius: var(--radius);
- font-size: 13px;
- color: var(--primary-dark);
- line-height: 1.6;
- }
- /* mini 预览专用缩小 */
- .mini-dialog .kv-grid.cols-3 { grid-template-columns: repeat(3, 1fr); gap: 6px 8px; }
- .mini-dialog .kv-grid.cols-4 { grid-template-columns: repeat(4, 1fr); gap: 5px 6px; }
- .mini-dialog .kv-item dt { font-size: 9px; }
- .mini-dialog .kv-item dd { font-size: 10px; font-weight: 600; }
- .mini-dialog .view-section { padding: 10px 12px; }
- .mini-dialog .view-section h3 { font-size: 11px; margin-bottom: 8px; }
- .mini-dialog .section-cols-2 { grid-template-columns: 1fr 1fr; }
- .mini-dialog .card-grid-2x2 { gap: 8px; }
- .mini-dialog .info-card { padding: 8px 10px; }
- .mini-dialog .info-card h4 { font-size: 10px; margin-bottom: 6px; }
- /* ── 少字段稀疏场景 ── */
- .sparse-section { margin-top: 32px; }
- .kpi-strip {
- display: flex;
- flex-wrap: wrap;
- gap: 12px;
- padding: 14px 22px;
- background: var(--table-head);
- border-bottom: 1px solid var(--border);
- }
- .kpi-chip {
- flex: 1;
- min-width: 140px;
- padding: 12px 14px;
- background: #fff;
- border: 1px solid var(--border);
- border-radius: 8px;
- box-shadow: var(--shadow);
- }
- .kpi-chip .lbl { font-size: 11px; color: var(--text-muted); margin-bottom: 4px; }
- .kpi-chip .val { font-size: 16px; font-weight: 700; color: var(--text); line-height: 1.3; }
- .kpi-chip .val.money { color: var(--danger); }
- .kpi-chip .val.muted { font-size: 14px; font-weight: 600; color: var(--text-muted); }
- .hero-chips {
- display: flex;
- flex-wrap: wrap;
- gap: 8px;
- margin-top: 10px;
- }
- .hero-chip {
- font-size: 12px;
- padding: 4px 10px;
- border-radius: 6px;
- background: rgba(255, 255, 255, 0.75);
- border: 1px solid rgba(16, 185, 129, 0.2);
- color: var(--text);
- font-weight: 500;
- }
- .hero-chip b { font-weight: 600; color: var(--text-muted); margin-right: 4px; }
- .kv-panel-centered {
- max-width: 680px;
- margin: 0 auto;
- padding: 20px 22px 24px;
- }
- .kv-panel-centered .kv-grid { gap: 14px 20px; }
- .bento-single {
- max-width: 760px;
- margin: 0 auto;
- padding: 18px 22px 22px;
- }
- .bento-single .info-card {
- background: linear-gradient(180deg, #fff 0%, var(--primary-bg) 100%);
- border-color: rgba(16, 185, 129, 0.25);
- padding: 18px 20px;
- }
- .sparse-empty-hint {
- margin-top: 14px;
- padding: 12px 14px;
- border-radius: 8px;
- background: var(--table-head);
- border: 1px dashed var(--border);
- font-size: 12px;
- color: var(--text-muted);
- text-align: center;
- line-height: 1.5;
- }
- .dialog-compact.w720 { max-width: 720px; }
- .dialog-compact .view-section { padding: 16px 20px; }
- </style>
- </head>
- <body>
- <div class="page">
- <header class="page-head">
- <h1><span>宽弹窗</span> · 多列 KV 布局 Demo</h1>
- <p>
- 弹窗宽度 ≥920px 时,如何在<strong>不牺牲长值可读性</strong>的前提下提高信息密度。
- 以下 7 种方案使用同一组 18 个字段(含路径、地址、备注等长值),默认 KV 为<strong>上下堆叠</strong>。
- 若偏好<strong>左右表格</strong>(label | value),见「KV 表格模式」;
- 指标卡、Tab、时间轴、手风琴等<strong>其他展示形态</strong>见「其他信息展示模式」。
- 相关:<a href="fresh-mint-view-kv-layout-demos.html">窄弹窗 KV 对比</a> ·
- <a href="fresh-mint-view-dialog-demos.html">弹窗风格 Demo</a> ·
- <a href="fresh-mint-view-sparse-kv-demos.html">少字段稀疏场景</a>
- </p>
- <div class="width-bar">
- <span class="width-pill"><b>720px</b> 双列(基准,不在此页)</span>
- <span class="width-pill"><b>920px</b> 三列起点</span>
- <span class="width-pill"><b>960px</b> 分栏 / 双区块</span>
- <span class="width-pill"><b>1040px</b> 四列 / 2×2 卡片</span>
- <span class="width-pill"><b>1120px</b> 宽表双组</span>
- </div>
- </header>
- <div class="compare-grid" id="grid"></div>
- <section class="demo-section" id="table-section">
- <h2>KV 表格模式(左右 label | value)</h2>
- <p>
- 与上下堆叠栅格相对:每行(或每半行)为<strong>左 label、右 value</strong> 的表格行,类似 <code>el-descriptions</code> 但样式对齐 Fresh Mint。
- 短字段可<strong>一行两组</strong>(四列);长字段<strong>独占整行</strong>。少字段时表格更饱满,不易显空。
- </p>
- <div class="compare-grid" id="table-grid"></div>
- </section>
- <section class="demo-section" id="alt-section">
- <h2>其他信息展示模式</h2>
- <p>
- 除<strong>上下 KV 栅格</strong>与<strong>左右 KV 表格</strong>外,查看弹窗还可采用指标卡、属性行、Tab 分段、时间轴、手风琴、芯片摘要、路线进度、Bento 卡片等形态。
- 样式对齐规范 <code>§9.6</code>(<code>view-timeline</code> / <code>view-route-bar</code> / <code>view-bento</code> 等)。Tab 与手风琴在全屏预览中可点击交互。
- </p>
- <div class="compare-grid" id="alt-grid"></div>
- </section>
- <div class="note-bar">
- <strong>宽弹窗选型建议:</strong>
- 短字段(编码/状态/日期)≥9 个 → 可用 <strong>3~4 列</strong>;
- 含长路径/备注 → 长字段必须 <strong>span 跨整行</strong>(方案 C / G);
- 字段可自然分组 → 优先 <strong>双区块并排</strong>(D)或 <strong>2×2 卡片</strong>(F),比单纯加列更易扫读;
- 需「摘要 + 明细/轨迹」→ 用 <strong>左右分栏</strong>(E),左侧 1 列、右侧 2~3 列。
- 偏好<strong>左右对齐、表格感</strong> → 见 <a href="#table-section" style="color:inherit;font-weight:600">KV 表格模式</a>(M~R,推荐 N 双组四列)。
- 需<strong>流程/进度/折叠/分段</strong> → 见 <a href="#alt-section" style="color:inherit;font-weight:600">其他展示模式</a>(S~Y)。
- <strong>字段 ≤8 个</strong> → 见下方「少字段稀疏场景」或 <a href="fresh-mint-view-sparse-kv-demos.html" style="color:inherit;font-weight:600">独立 Demo 页</a>;表格方案见 Q / R。
- </div>
- <section class="demo-section sparse-section" id="sparse-section">
- <h2>少字段稀疏场景(≤8 个 KV)</h2>
- <p>
- 1040px 四列在字段少时容易「空荡荡」。常用手段:<strong>① 自适应降列</strong> ② <strong>Hero 下沉 KPI</strong>
- ③ <strong>Hero 芯片摘要</strong> ④ <strong>单卡片 Bento</strong> ⑤ <strong>窄弹窗 720px</strong>。
- 实现上可在 <code>SystemViewDialog</code> 按 <code>fields.length</code> 动态设 <code>gridColumns</code> 与 <code>width</code>。
- </p>
- <div class="compare-grid" id="sparse-grid"></div>
- </section>
- </div>
- <div class="backdrop" id="backdrop">
- <div class="dialog w920" id="dialog"></div>
- </div>
- <script>
- var FIELDS = {
- short: [
- { k: "合同编号", v: "HT-2026-SX-00428" },
- { k: "合同类型", v: "年度框架" },
- { k: "签订日期", v: "2026-06-01" },
- { k: "生效日期", v: "2026-06-01" },
- { k: "到期日期", v: "2027-05-31" },
- { k: "合同状态", v: "已生效", tag: true },
- { k: "付款方式", v: "月结 30 天" },
- { k: "交货方式", v: "卖方送货" },
- { k: "销售负责人", v: "陈经理" },
- { k: "归属部门", v: "华东销售一部" },
- { k: "币种", v: "CNY" },
- { k: "税率", v: "13%" }
- ],
- long: [
- { k: "甲方", v: "山峡化工有限公司" },
- { k: "乙方", v: "华东精细化工贸易公司" },
- { k: "交货地址", v: "浙江省杭州市滨江区长河街道江南大道 3588 号 · 山峡化工华东仓储中心(北门)" },
- { k: "质量标准", v: "GB/T 标准 · 每批次附第三方质检报告 · 不合格品 24 小时内退换" },
- { k: "合同金额", v: "¥ 2,580,000.00", money: true },
- { k: "备注", v: "本合同为年度框架,具体订单以 PO 为准;价格锁定至 2026-12-31,逾期重新询价。紧急联系人:刘总 139****8821。" }
- ]
- };
- var ALL_SHORT = FIELDS.short;
- var ALL_LONG = FIELDS.long;
- /** 备用金申请等少字段场景(7 个) */
- var SPARSE_FIELDS = [
- { k: "申请人", v: "张三" },
- { k: "申请原因", v: "项目现场零星采购备用" },
- { k: "申请金额(元)", v: "¥ 5,000.00", money: true },
- { k: "实际发放(元)", v: "—", muted: true },
- { k: "当前节点", v: "部门审批", tag: true },
- { k: "申请时间", v: "2026-06-18 14:32" },
- { k: "冲抵总额(元)", v: "¥ 0.00", money: true }
- ];
- function suggestCols(n) {
- if (n <= 4) return 2;
- if (n <= 8) return 3;
- return 4;
- }
- function suggestWidth(n) {
- if (n <= 6) return "w720";
- if (n <= 10) return "w920";
- return "w1040";
- }
- function esc(s) {
- var d = document.createElement("div");
- d.textContent = s;
- return d.innerHTML;
- }
- function kvItem(f, spanClass) {
- var val = f.tag ? '<span class="tag">' + esc(f.v) + "</span>" : esc(f.v);
- if (f.money) val = '<span style="color:var(--danger)">' + val + "</span>";
- if (f.muted) val = '<span style="color:var(--text-muted);font-weight:600">' + val + "</span>";
- var span = spanClass ? " " + spanClass : "";
- return '<div class="kv-item' + span + '"><dt>' + esc(f.k) + "</dt><dd>" + val + "</dd></div>";
- }
- function kvGrid(items, cols, spanFn) {
- var html = '<dl class="kv-grid cols-' + cols + '">';
- items.forEach(function (f) {
- html += kvItem(f, spanFn ? spanFn(f) : "");
- });
- return html + "</dl>";
- }
- function section(title, body) {
- return '<div class="view-section"><h3>' + esc(title) + "</h3>" + body + "</div>";
- }
- var LONG_LABEL_RE = /备注|地址|描述|说明|条款|原因|摘要|内容|标准|路径|意向/;
- function isLongField(f) {
- var text = f.v == null ? "" : String(f.v);
- if (text.length >= 17) return true;
- if (LONG_LABEL_RE.test(f.k)) return true;
- return false;
- }
- function formatFieldVal(f) {
- if (f.tag) return '<span class="tag">' + esc(f.v) + "</span>";
- if (f.money) return '<span class="money-val">' + esc(f.v) + "</span>";
- if (f.muted) return '<span class="muted-val">' + esc(f.v) + "</span>";
- return esc(f.v);
- }
- function kvTableWrap(inner) {
- return '<div class="kv-table-wrap">' + inner + "</div>";
- }
- /** 每行一组:label | value */
- function kvTablePair(items) {
- var html = '<table class="kv-table kv-table--pair"><tbody>';
- items.forEach(function (f) {
- html +=
- '<tr' +
- (isLongField(f) ? ' class="row-long"' : "") +
- "><th>" +
- esc(f.k) +
- "</th><td>" +
- formatFieldVal(f) +
- "</td></tr>";
- });
- return kvTableWrap(html + "</tbody></table>");
- }
- /** 每行两组短字段;长字段独占一行(value 跨 3 列) */
- function kvTableDual(items) {
- var html = '<table class="kv-table kv-table--dual"><tbody>';
- var i = 0;
- while (i < items.length) {
- var f = items[i];
- if (isLongField(f)) {
- html +=
- '<tr class="row-long"><th>' +
- esc(f.k) +
- '</th><td colspan="3">' +
- formatFieldVal(f) +
- "</td></tr>";
- i += 1;
- continue;
- }
- html += "<tr><th>" + esc(f.k) + "</th><td>" + formatFieldVal(f) + "</td>";
- i += 1;
- if (i < items.length && !isLongField(items[i])) {
- html += "<th>" + esc(items[i].k) + "</th><td>" + formatFieldVal(items[i]) + "</td>";
- i += 1;
- } else {
- html += "<th></th><td></td>";
- }
- html += "</tr>";
- }
- return kvTableWrap(html + "</tbody></table>");
- }
- /** 斑马纹:无竖线,偶数行浅底(两列) */
- function kvTableZebraPair(items) {
- var html = '<table class="kv-table kv-table--zebra kv-table--pair"><tbody>';
- items.forEach(function (f) {
- html +=
- '<tr' +
- (isLongField(f) ? ' class="row-long"' : "") +
- "><th>" +
- esc(f.k) +
- "</th><td>" +
- formatFieldVal(f) +
- "</td></tr>";
- });
- return kvTableWrap(html + "</tbody></table>");
- }
- /** 斑马纹 + 双组四列 */
- function kvTableZebraDual(items) {
- var html = '<table class="kv-table kv-table--zebra kv-table--dual"><tbody>';
- var i = 0;
- while (i < items.length) {
- var f = items[i];
- if (isLongField(f)) {
- html +=
- '<tr class="row-long"><th>' +
- esc(f.k) +
- '</th><td colspan="3">' +
- formatFieldVal(f) +
- "</td></tr>";
- i += 1;
- continue;
- }
- html += "<tr><th>" + esc(f.k) + "</th><td>" + formatFieldVal(f) + "</td>";
- i += 1;
- if (i < items.length && !isLongField(items[i])) {
- html += "<th>" + esc(items[i].k) + "</th><td>" + formatFieldVal(items[i]) + "</td>";
- i += 1;
- } else {
- html += "<th></th><td></td>";
- }
- html += "</tr>";
- }
- return kvTableWrap(html + "</tbody></table>");
- }
- var CONTRACT_TIMELINE = [
- { time: "2026-05-20 10:00", title: "合同起草", desc: "陈经理创建年度框架合同草稿" },
- { time: "2026-05-28 16:30", title: "审批通过", desc: "法务、财务会签完成" },
- { time: "2026-06-01 09:00", title: "签订生效", desc: "双方盖章,合同状态变更为已生效", active: true },
- { time: "2027-05-31", title: "合同到期", desc: "到期前 30 天触发续签提醒", future: true }
- ];
- function renderStatRow(items) {
- var html = '<div class="stat-row">';
- items.forEach(function (f) {
- html +=
- '<div class="stat-cell"><div class="lbl">' +
- esc(f.k) +
- '</div><div class="val' +
- (f.money ? " money" : "") +
- '">' +
- (f.tag ? '<span class="tag">' + esc(f.v) + "</span>" : esc(f.v)) +
- "</div></div>";
- });
- return html + "</div>";
- }
- function renderPropList(items) {
- var html = '<div class="prop-list">';
- items.forEach(function (f) {
- html +=
- '<div class="prop-row"><span class="lbl">' +
- esc(f.k) +
- '</span><span class="val' +
- (f.money ? " money" : "") +
- '">' +
- formatFieldVal(f) +
- "</span></div>";
- });
- return html + "</div>";
- }
- function renderTabs(panels, groupId) {
- var gid = groupId || "tabs";
- var html = '<div class="view-tabs" role="tablist">';
- panels.forEach(function (p, idx) {
- html +=
- '<button type="button" class="tab-chip' +
- (idx === 0 ? " is-active" : "") +
- '" role="tab" data-tab-group="' +
- gid +
- '" data-tab="' +
- idx +
- '">' +
- esc(p.title) +
- "</button>";
- });
- html += "</div>";
- panels.forEach(function (p, idx) {
- html +=
- '<div class="tab-panel' +
- (idx === 0 ? " is-active" : "") +
- '" role="tabpanel" data-tab-group="' +
- gid +
- '" data-tab-panel="' +
- idx +
- '">' +
- p.body +
- "</div>";
- });
- return html;
- }
- function renderTimeline(items) {
- var html = '<div class="view-timeline">';
- items.forEach(function (it) {
- var cls = it.future ? " future" : it.muted ? " muted" : it.active ? " active" : "";
- html +=
- '<div class="view-tl-item' +
- cls +
- '"><div class="view-tl-time">' +
- esc(it.time) +
- '</div><div class="view-tl-title">' +
- esc(it.title) +
- '</div><div class="view-tl-desc">' +
- esc(it.desc) +
- "</div></div>";
- });
- return html + "</div>";
- }
- function renderAccordion(sections) {
- var html = '<div class="acc-list">';
- sections.forEach(function (sec, idx) {
- html +=
- '<div class="acc-item' +
- (idx === 0 ? " is-open" : "") +
- '"><button type="button" class="acc-hd" data-acc-toggle>' +
- esc(sec.title) +
- '</button><div class="acc-bd">' +
- sec.body +
- "</div></div>";
- });
- return html + "</div>";
- }
- function renderMetaChips(items) {
- var html = '<div class="meta-chips">';
- items.forEach(function (f) {
- html += '<span class="meta-chip"><b>' + esc(f.k) + "</b>" + formatFieldVal(f) + "</span>";
- });
- return html + "</div>";
- }
- function renderProseBlock(title, text) {
- return '<div class="prose-block"><h4>' + esc(title) + "</h4>" + esc(text) + "</div>";
- }
- function renderRouteBar(nodes) {
- var html = '<div class="view-route-bar">';
- nodes.forEach(function (node, idx) {
- if (idx > 0) {
- html += '<div class="view-route-line' + (node.done || node.active ? " is-done" : "") + '"></div>';
- }
- html +=
- '<div class="route-node' +
- (node.done ? " is-done" : "") +
- (node.active ? " is-active" : "") +
- '"><div class="dot"></div><div class="lbl">' +
- esc(node.label) +
- '</div><div class="sub">' +
- esc(node.sub || "") +
- "</div></div>";
- });
- return html + "</div>";
- }
- function renderBento(cards) {
- var html = '<div class="view-bento">';
- cards.forEach(function (c) {
- html +=
- '<div class="view-card' +
- (c.spanFull ? " span-full" : "") +
- '"><h4>' +
- esc(c.title) +
- "</h4>" +
- c.body +
- "</div>";
- });
- return html + "</div>";
- }
- var LAYOUTS = [
- {
- id: "cols3",
- name: "A · 三列上下 KV",
- width: "w920",
- widthLabel: "920px",
- badge: "推荐",
- desc: "宽弹窗最常用。18 个短字段 3 列 × 6 行;长字段另开区块 span 整行。",
- meta: "字段 12~24 · 920px",
- previewClass: "w920",
- render: function () {
- var s = kvGrid(ALL_SHORT, 3);
- var l = kvGrid(ALL_LONG, 3, function () { return "span-all"; });
- return section("合同要素", s) + section("主体与条款(长字段整行)", l);
- }
- },
- {
- id: "cols4",
- name: "B · 四列上下 KV",
- width: "w1040",
- widthLabel: "1040px",
- badge: "高密度",
- desc: "仅当字段几乎全是短值(编码/日期/枚举)时使用。1040px 下每格 ~240px。",
- meta: "短字段 ≥16 · 1040px",
- previewClass: "w1040",
- render: function () {
- var mix = ALL_SHORT.concat(ALL_LONG.filter(function (f) { return f.k !== "备注"; }));
- var html = kvGrid(mix, 4);
- var remark = ALL_LONG.find(function (f) { return f.k === "备注"; });
- html = html.replace("</dl>", kvItem(remark, "span-all") + "</dl>");
- return section("合同全量字段", html);
- }
- },
- {
- id: "cols3-span2",
- name: "C · 三列 + 长值跨 2 列",
- width: "w920",
- widthLabel: "920px",
- badge: "折中",
- desc: "中等长度(甲方/乙方/地址)占 2 列宽;超长备注仍整行。比全 span 省高度。",
- meta: "长短混合 · 920px",
- previewClass: "w920",
- render: function () {
- var html = '<dl class="kv-grid cols-3">';
- ALL_SHORT.forEach(function (f) { html += kvItem(f); });
- ALL_LONG.forEach(function (f) {
- var span = f.k === "备注" ? "span-all" : "span-2";
- html += kvItem(f, span);
- });
- html += "</dl>";
- return section("合同信息", html);
- }
- },
- {
- id: "section-2col",
- name: "D · 双区块并排(各 2 列 KV)",
- width: "w960",
- widthLabel: "960px",
- badge: "推荐",
- desc: "按业务语义拆成左右两栏(如「基本信息|商务条款」),每栏内部 2 列 KV。比 4 列更易分组扫读。",
- meta: "可自然分组 · 960px",
- previewClass: "w960",
- render: function () {
- var left = ALL_SHORT.slice(0, 6).concat(ALL_LONG.filter(function (f) { return f.k === "甲方" || f.k === "乙方"; }));
- var right = ALL_SHORT.slice(6).concat(ALL_LONG.filter(function (f) { return f.k !== "甲方" && f.k !== "乙方"; }));
- return (
- '<div class="section-cols-2">' +
- section("基本信息", kvGrid(left, 2, function (f) { return f.k === "备注" || f.k === "交货地址" || f.k === "质量标准" ? "span-2" : ""; })) +
- section("商务与条款", kvGrid(right, 2, function (f) { return f.k === "备注" || f.k === "交货地址" || f.k === "质量标准" ? "span-2" : ""; })) +
- "</div>"
- );
- }
- },
- {
- id: "split",
- name: "E · 左摘要 + 右三列",
- width: "w960",
- widthLabel: "960px",
- badge: "分栏",
- desc: "左 340px 放核心摘要(1 列 KV),右栏 3 列放其余。对应 SystemViewDialog split 布局。",
- meta: "摘要+明细 · 960px",
- previewClass: "w960",
- render: function () {
- var leftFields = [
- { k: "合同编号", v: "HT-2026-SX-00428" },
- { k: "合同状态", v: "已生效", tag: true },
- { k: "合同金额", v: "¥ 2,580,000.00", money: true },
- { k: "签订日期", v: "2026-06-01" },
- { k: "销售负责人", v: "陈经理" },
- { k: "甲方", v: "山峡化工有限公司" },
- { k: "乙方", v: "华东精细化工贸易公司" }
- ];
- var rightFields = ALL_SHORT.slice(1).filter(function (f) { return f.k !== "合同状态"; })
- .concat(ALL_LONG.filter(function (f) { return f.k !== "甲方" && f.k !== "乙方" && f.k !== "合同金额"; }));
- return (
- '<div class="view-split">' +
- '<div class="view-split-left">' + section("核心摘要", kvGrid(leftFields, 1)) + "</div>" +
- '<div class="view-split-right">' + section("完整要素", kvGrid(rightFields, 3, function (f) {
- return f.k === "备注" || f.k === "交货地址" ? "span-all" : f.k === "质量标准" ? "span-2" : "";
- })) + "</div>" +
- "</div>"
- );
- }
- },
- {
- id: "card-2x2",
- name: "F · 2×2 卡片宫格",
- width: "w1040",
- widthLabel: "1040px",
- badge: "分组",
- desc: "4 张主题卡片(身份/时间/商务/条款),卡内 2 列 KV。适合 CRM 客户/合同等多维度档案。",
- meta: "多维度 · 1040px",
- previewClass: "w1040",
- render: function () {
- var groups = [
- { title: "身份标识", items: [ALL_SHORT[0], ALL_SHORT[1], ALL_SHORT[5], ALL_LONG[0], ALL_LONG[1]] },
- { title: "时间周期", items: ALL_SHORT.slice(2, 5) },
- { title: "商务条款", items: [ALL_SHORT[6], ALL_SHORT[7], ALL_SHORT[10], ALL_SHORT[11], ALL_LONG[4]] },
- { title: "交付与备注", items: [ALL_SHORT[8], ALL_SHORT[9], ALL_LONG[2], ALL_LONG[3], ALL_LONG[5]] }
- ];
- var html = '<div class="view-section"><div class="card-grid-2x2">';
- groups.forEach(function (g) {
- html += '<div class="info-card"><h4>' + esc(g.title) + "</h4>" +
- kvGrid(g.items, 2, function (f) {
- return f.k === "备注" || f.k === "交货地址" ? "span-2" : "";
- }) + "</div>";
- });
- return html + "</div></div>";
- }
- },
- {
- id: "wide-table",
- name: "G · 宽表双组(一行 4 格)",
- width: "w1120",
- widthLabel: "1120px",
- badge: "表格感",
- desc: "1120px 下一行展示两组 label-value(共 4 列),label 窄列对齐。同质字段极多时的替代方案。",
- meta: "18+ 同质字段 · 1120px",
- previewClass: "w1120",
- render: function () {
- var all = ALL_SHORT.concat(ALL_LONG);
- var html = '<div class="view-section"><table class="kv-wide-table"><tbody>';
- for (var i = 0; i < all.length; i += 2) {
- html += "<tr>";
- html += '<td class="lbl">' + esc(all[i].k) + '</td><td class="val">' + (all[i].tag ? '<span class="tag">' + esc(all[i].v) + "</span>" : esc(all[i].v)) + "</td>";
- if (all[i + 1]) {
- html += '<td class="lbl">' + esc(all[i + 1].k) + '</td><td class="val">' + (all[i + 1].tag ? '<span class="tag">' + esc(all[i + 1].v) + "</span>" : esc(all[i + 1].v)) + "</td>";
- } else {
- html += '<td class="lbl"></td><td class="val"></td>';
- }
- html += "</tr>";
- }
- return html + "</tbody></table></div>";
- }
- }
- ];
- var TABLE_LAYOUTS = [
- {
- id: "table-pair",
- name: "M · 标准两列表格",
- width: "w920",
- widthLabel: "920px",
- badge: "表格",
- desc: "每行一组:左 label(灰底固定宽)| 右 value。长字段仍独占一行,适合字段不多或需逐行阅读。",
- meta: "18 字段 · 1 组/行",
- previewClass: "w920",
- tableMode: true,
- render: function () {
- return section("合同信息", kvTablePair(ALL_SHORT.concat(ALL_LONG)));
- }
- },
- {
- id: "table-dual",
- name: "N · 双组四列表格",
- width: "w1040",
- widthLabel: "1040px",
- badge: "推荐",
- desc: "一行展示两组 label|value(共四列);备注/地址等长字段自动独占整行。信息密度高且对齐清晰。",
- meta: "18 字段 · 2 组/行",
- previewClass: "w1040",
- tableMode: true,
- render: function () {
- return section("合同全量字段", kvTableDual(ALL_SHORT.concat(ALL_LONG)));
- }
- },
- {
- id: "table-split-section",
- name: "O · 分区 + 双组表",
- width: "w1040",
- widthLabel: "1040px",
- badge: "分组",
- desc: "「合同要素」「主体与条款」各一张表,双组四列。比单表更易扫读分组。",
- meta: "分组表格 · 1040px",
- previewClass: "w1040",
- tableMode: true,
- render: function () {
- return (
- section("合同要素", kvTableDual(ALL_SHORT)) +
- section("主体与条款", kvTableDual(ALL_LONG))
- );
- }
- },
- {
- id: "table-zebra-dual",
- name: "P · 斑马纹双组表",
- width: "w1040",
- widthLabel: "1040px",
- badge: "轻量",
- desc: "无双竖线,偶数行浅灰底;仍为一行两组。视觉比全边框表更轻。",
- meta: "斑马纹 · 1040px",
- previewClass: "w1040",
- tableMode: true,
- render: function () {
- return section("合同全量字段", kvTableZebraDual(ALL_SHORT.concat(ALL_LONG)));
- }
- },
- {
- id: "table-pair-sparse",
- name: "Q · 少字段两列表格",
- width: "w720",
- widthLabel: "720px",
- badge: "推荐",
- desc: "7 个字段用标准两列表格 + 720px 弹窗,行数适中、不空荡,报销/审批简详情首选表格方案。",
- meta: "7 字段 · 720px",
- previewClass: "w920",
- sparse: true,
- tableMode: true,
- render: function () {
- return section("申请信息", kvTablePair(SPARSE_FIELDS));
- }
- },
- {
- id: "table-dual-sparse",
- name: "R · 少字段双组表",
- width: "w920",
- widthLabel: "920px",
- badge: "表格",
- desc: "少字段时双组四列(4 行左右),比四列上下 KV 更饱满。",
- meta: "7 字段 · 双组",
- previewClass: "w920",
- sparse: true,
- tableMode: true,
- render: function () {
- return section("申请信息", kvTableDual(SPARSE_FIELDS));
- }
- }
- ];
- var ALT_LAYOUTS = [
- {
- id: "alt-stat-props",
- name: "S · 指标卡 + 属性行",
- width: "w1040",
- widthLabel: "1040px",
- badge: "推荐",
- desc: "顶部 4 格核心指标(金额/状态/日期),下方属性行列表展示其余字段。适合「先抓重点、再扫明细」。",
- meta: "指标 + 列表 · 1040px",
- previewClass: "w1040",
- altMode: true,
- render: function () {
- var stats = [
- ALL_LONG.find(function (f) { return f.k === "合同金额"; }),
- ALL_SHORT.find(function (f) { return f.k === "合同状态"; }),
- ALL_SHORT.find(function (f) { return f.k === "签订日期"; }),
- ALL_SHORT.find(function (f) { return f.k === "到期日期"; })
- ];
- var rest = ALL_SHORT.filter(function (f) {
- return f.k !== "合同状态" && f.k !== "签订日期" && f.k !== "到期日期";
- }).concat(ALL_LONG.filter(function (f) { return f.k !== "合同金额"; }));
- return section("核心指标", renderStatRow(stats)) + section("合同明细", renderPropList(rest));
- }
- },
- {
- id: "alt-tabs",
- name: "T · Tab 分段切换",
- width: "w1040",
- widthLabel: "1040px",
- badge: "分段",
- desc: "字段按业务语义拆成 Tab(基本信息 / 商务条款 / 主体备注),单屏信息量可控,全屏可点击切换。",
- meta: "3 Tab · 1040px",
- previewClass: "w1040",
- altMode: true,
- interactive: true,
- render: function () {
- return section(
- "合同信息",
- renderTabs(
- [
- { title: "基本信息", body: kvGrid(ALL_SHORT.slice(0, 6), 2) },
- { title: "商务条款", body: kvGrid(ALL_SHORT.slice(6).concat(ALL_LONG.filter(function (f) { return f.k === "合同金额" || f.k === "质量标准"; })), 2, function (f) { return f.k === "质量标准" ? "span-2" : ""; }) },
- { title: "主体备注", body: renderPropList(ALL_LONG.filter(function (f) { return f.k !== "合同金额" && f.k !== "质量标准"; })) }
- ],
- "contract"
- )
- );
- }
- },
- {
- id: "alt-timeline",
- name: "U · 时间轴 + 精简属性",
- width: "w920",
- widthLabel: "920px",
- badge: "流程",
- desc: "左侧时间轴展示合同生命周期节点,右侧或下方属性行补充当前态字段。适合审批/履约类详情。",
- meta: "时间轴 · 920px",
- previewClass: "w920",
- altMode: true,
- render: function () {
- var brief = [
- ALL_SHORT[0],
- ALL_SHORT[5],
- ALL_LONG[0],
- ALL_LONG[1]
- ];
- return (
- section("履约进度", renderTimeline(CONTRACT_TIMELINE)) +
- section("当前摘要", renderPropList(brief))
- );
- }
- },
- {
- id: "alt-accordion",
- name: "V · 手风琴折叠",
- width: "w1040",
- widthLabel: "1040px",
- badge: "折叠",
- desc: "按分组折叠次要信息,默认展开第一组。字段多、分组清晰时减少首屏高度,全屏可展开/收起。",
- meta: "3 组折叠 · 1040px",
- previewClass: "w1040",
- altMode: true,
- interactive: true,
- render: function () {
- return section(
- "合同信息",
- renderAccordion([
- { title: "身份与时间(6 项)", body: kvGrid(ALL_SHORT.slice(0, 6), 2) },
- { title: "商务与交付(6 项)", body: kvGrid(ALL_SHORT.slice(6).concat(ALL_LONG.filter(function (f) { return f.k === "交货地址"; })), 2, function (f) { return f.k === "交货地址" ? "span-2" : ""; }) },
- { title: "主体、金额与备注(6 项)", body: renderPropList(ALL_LONG.filter(function (f) { return f.k !== "交货地址"; })) }
- ])
- );
- }
- },
- {
- id: "alt-chips-prose",
- name: "W · 芯片摘要 + 正文块",
- width: "w920",
- widthLabel: "920px",
- badge: "轻量",
- desc: "短字段收成芯片行(类 Hero 下沉),长文本(备注/条款)用独立正文块展示,阅读感更接近文档。",
- meta: "芯片 + 正文 · 920px",
- previewClass: "w920",
- altMode: true,
- render: function () {
- var chips = ALL_SHORT.filter(function (f) {
- return f.k !== "合同状态";
- }).slice(0, 8);
- return (
- section("关键要素", renderMetaChips(chips.concat([ALL_SHORT[5]]))) +
- section("质量标准", renderProseBlock("质量标准", ALL_LONG.find(function (f) { return f.k === "质量标准"; }).v)) +
- section("备注说明", renderProseBlock("备注", ALL_LONG.find(function (f) { return f.k === "备注"; }).v))
- );
- }
- },
- {
- id: "alt-route",
- name: "X · 路线进度条 + 属性行",
- width: "w1040",
- widthLabel: "1040px",
- badge: "进度",
- desc: "顶部路线条展示合同阶段(起草→审批→签订→履行→到期),下方属性行列关键字段。对齐 view-route-bar。",
- meta: "路线条 · 1040px",
- previewClass: "w1040",
- altMode: true,
- render: function () {
- var route = [
- { label: "起草", sub: "05-20", done: true },
- { label: "审批", sub: "05-28", done: true },
- { label: "签订", sub: "06-01", done: true, active: true },
- { label: "履行", sub: "进行中" },
- { label: "到期", sub: "2027-05" }
- ];
- var props = [
- ALL_SHORT[0],
- ALL_SHORT[5],
- ALL_LONG[4],
- ALL_SHORT[2],
- ALL_SHORT[4],
- ALL_LONG[0],
- ALL_LONG[1]
- ];
- return section("合同阶段", renderRouteBar(route)) + section("合同要素", renderPropList(props));
- }
- },
- {
- id: "alt-bento",
- name: "Y · Bento 不均等卡片",
- width: "w1040",
- widthLabel: "1040px",
- badge: "卡片",
- desc: "左大右小 Bento 布局:左侧主体信息占宽列,右侧堆叠时间/金额小卡;底部长备注横跨整行。",
- meta: "Bento · 1040px",
- previewClass: "w1040",
- altMode: true,
- render: function () {
- return section(
- "合同档案",
- renderBento([
- {
- title: "主体信息",
- body: renderPropList(ALL_LONG.filter(function (f) { return f.k === "甲方" || f.k === "乙方" || f.k === "交货地址"; }))
- },
- {
- title: "金额与周期",
- body:
- renderStatRow([
- ALL_LONG.find(function (f) { return f.k === "合同金额"; }),
- ALL_SHORT.find(function (f) { return f.k === "合同状态"; }),
- ALL_SHORT.find(function (f) { return f.k === "签订日期"; }),
- ALL_SHORT.find(function (f) { return f.k === "到期日期"; })
- ])
- },
- {
- title: "备注",
- spanFull: true,
- body: renderProseBlock("备注", ALL_LONG.find(function (f) { return f.k === "备注"; }).v)
- }
- ])
- );
- }
- },
- {
- id: "alt-stat-sparse",
- name: "Z · 少字段指标 + 属性行",
- width: "w720",
- widthLabel: "720px",
- badge: "推荐",
- desc: "备用金等少字段场景:顶部 3 指标 + 下方属性行,比四列 KV 更饱满,比表格更轻。",
- meta: "7 字段 · 720px",
- previewClass: "w920",
- sparse: true,
- altMode: true,
- render: function () {
- var stats = SPARSE_FIELDS.filter(function (f) {
- return f.k === "申请金额(元)" || f.k === "当前节点" || f.k === "冲抵总额(元)";
- });
- var rest = SPARSE_FIELDS.filter(function (f) {
- return stats.indexOf(f) === -1;
- });
- return section("申请概览", renderStatRow(stats)) + section("申请明细", renderPropList(rest));
- }
- }
- ];
- var SPARSE_LAYOUTS = [
- {
- id: "sparse-auto-cols",
- name: "H · 自适应降列 + 居中窄区",
- width: "w1040",
- widthLabel: "1040 壳 / 2列 KV",
- badge: "推荐",
- desc: "弹窗仍 1040,KV 区 max-width 680 居中,字段 ≤8 时自动 2 列(非 4 列)。视觉不「铺满空格子」。",
- meta: "7 字段 · 2 列",
- previewClass: "w920",
- sparse: true,
- render: function () {
- var cols = suggestCols(SPARSE_FIELDS.length);
- var html = kvGrid(SPARSE_FIELDS, cols, function (f) {
- return f.k === "申请原因" ? "span-all" : "";
- });
- return '<div class="kv-panel-centered">' + section("申请信息", html) + "</div>";
- }
- },
- {
- id: "sparse-kpi",
- name: "I · Hero KPI 条 + 精简 KV",
- width: "w1040",
- widthLabel: "1040px",
- badge: "推荐",
- desc: "金额/状态/节点提到 Hero 下 KPI 三格;正文只留 3~4 个补充字段,2 列 KV。",
- meta: "7 字段 · KPI 下沉",
- previewClass: "w920",
- sparse: true,
- render: function () {
- var kpi =
- '<div class="kpi-strip">' +
- '<div class="kpi-chip"><div class="lbl">申请金额(元)</div><div class="val money">¥ 5,000.00</div></div>' +
- '<div class="kpi-chip"><div class="lbl">当前节点</div><div class="val"><span class="tag">部门审批</span></div></div>' +
- '<div class="kpi-chip"><div class="lbl">冲抵总额(元)</div><div class="val money">¥ 0.00</div></div>' +
- "</div>";
- var rest = SPARSE_FIELDS.filter(function (f) {
- return f.k !== "申请金额(元)" && f.k !== "当前节点" && f.k !== "冲抵总额(元)";
- });
- return kpi + section("其他信息", kvGrid(rest, 2, function (f) {
- return f.k === "申请原因" ? "span-all" : "";
- }));
- }
- },
- {
- id: "sparse-chips",
- name: "J · Hero 芯片摘要",
- width: "w1040",
- widthLabel: "1040px",
- badge: "轻量",
- desc: "次要短字段(时间/节点/冲抵)收成 Hero 下 chip 行;正文仅保留人、原因等 2~3 项。",
- meta: "7 字段 · chip",
- previewClass: "w920",
- sparse: true,
- renderHeroExtra: function () {
- return (
- '<div class="hero-chips">' +
- '<span class="hero-chip"><b>节点</b>部门审批</span>' +
- '<span class="hero-chip"><b>时间</b>2026-06-18 14:32</span>' +
- '<span class="hero-chip"><b>冲抵</b>¥ 0.00</span>' +
- "</div>"
- );
- },
- render: function () {
- var core = SPARSE_FIELDS.filter(function (f) {
- return f.k === "申请人" || f.k === "申请原因" || f.k === "实际发放(元)";
- });
- return section("申请信息", kvGrid(core, 2, function (f) {
- return f.k === "申请原因" ? "span-all" : "";
- }));
- }
- },
- {
- id: "sparse-bento",
- name: "K · 单卡片 Bento",
- width: "w920",
- widthLabel: "920px",
- badge: "卡片",
- desc: "少字段时一张主题卡片包裹全部 KV(2 列),比大面积空白 grid 更有「档案感」。",
- meta: "7 字段 · 单卡",
- previewClass: "w920",
- sparse: true,
- render: function () {
- return (
- '<div class="bento-single">' +
- '<div class="info-card"><h4>备用金申请</h4>' +
- kvGrid(SPARSE_FIELDS, 2, function (f) { return f.k === "申请原因" ? "span-all" : ""; }) +
- "</div></div>"
- );
- }
- },
- {
- id: "sparse-compact",
- name: "L · 窄弹窗 720px",
- width: "w720",
- widthLabel: "720px",
- badge: "紧凑",
- desc: "字段 ≤6 时直接缩弹窗宽度 + 2 列 KV,不强行 1040。报销/审批类简详情最常用。",
- meta: "7 字段 · 720px",
- previewClass: "w920",
- sparse: true,
- dialogClass: "dialog-compact",
- render: function () {
- return section("申请信息", kvGrid(SPARSE_FIELDS, 2, function (f) {
- return f.k === "申请原因" ? "span-all" : "";
- }));
- }
- }
- ];
- var ALL_LAYOUTS = LAYOUTS.concat(TABLE_LAYOUTS).concat(ALT_LAYOUTS).concat(SPARSE_LAYOUTS);
- function renderPreview(layout) {
- return '<div class="mini-wrap"><div class="mini-dialog ' + layout.previewClass + '">' + layout.render() + "</div></div>";
- }
- function renderFull(layout) {
- var title = layout.sparse ? "备用金申请 · BYJ-2026-0618" : "产品销售合同 · 查看";
- var sub = layout.sparse ? "张三 · 申请 ¥5,000.00" : "HT-2026-SX-00428 · 华东精细化工贸易公司";
- var heroExtra = layout.renderHeroExtra ? layout.renderHeroExtra() : "";
- return (
- '<div class="dialog-hero">' +
- "<div><h2>" + title + "</h2><p>" + sub + "</p>" + heroExtra + "</div>" +
- '<span class="width-tag">' + esc(layout.widthLabel) + "</span>" +
- "</div>" +
- '<div class="dialog-scroll">' + layout.render() + "</div>" +
- '<div class="dialog-foot"><button type="button" class="btn-ghost" data-close>关闭</button></div>'
- );
- }
- function mountLayoutCards(targetId, layouts, recBadge) {
- var target = document.getElementById(targetId);
- if (!target) return;
- layouts.forEach(function (layout) {
- var card = document.createElement("article");
- var isRec = layout.badge === recBadge || layout.badge === "推荐";
- card.className = "layout-card" + (isRec ? " rec" : "");
- card.innerHTML =
- '<div class="layout-card-head">' +
- "<div><h2>" + esc(layout.name) + "</h2><p>" + esc(layout.desc) + "</p></div>" +
- '<span class="badge' +
- (layout.badge === "表格感" ? " blue" : layout.altMode ? " amber" : "") +
- '">' +
- esc(layout.widthLabel) +
- "</span>" +
- "</div>" +
- '<div class="layout-preview">' + renderPreview(layout) + "</div>" +
- '<div class="layout-card-foot">' +
- '<span class="meta">' + esc(layout.meta) + "</span>" +
- '<button type="button" class="btn" data-open="' + layout.id + '">全屏预览</button>' +
- "</div>";
- target.appendChild(card);
- });
- }
- function initDialogInteractions(layout) {
- if (!layout || !layout.interactive) return;
- dialog.querySelectorAll("[data-tab]").forEach(function (btn) {
- btn.addEventListener("click", function () {
- var group = btn.getAttribute("data-tab-group");
- var idx = btn.getAttribute("data-tab");
- dialog.querySelectorAll('[data-tab-group="' + group + '"][data-tab]').forEach(function (b) {
- b.classList.toggle("is-active", b === btn);
- });
- dialog.querySelectorAll('[data-tab-group="' + group + '"][data-tab-panel]').forEach(function (p) {
- p.classList.toggle("is-active", p.getAttribute("data-tab-panel") === idx);
- });
- });
- });
- dialog.querySelectorAll("[data-acc-toggle]").forEach(function (btn) {
- btn.addEventListener("click", function () {
- var item = btn.closest(".acc-item");
- if (item) item.classList.toggle("is-open");
- });
- });
- }
- mountLayoutCards("grid", LAYOUTS, "推荐");
- mountLayoutCards("table-grid", TABLE_LAYOUTS, "推荐");
- mountLayoutCards("alt-grid", ALT_LAYOUTS, "推荐");
- mountLayoutCards("sparse-grid", SPARSE_LAYOUTS, "推荐");
- var backdrop = document.getElementById("backdrop");
- var dialog = document.getElementById("dialog");
- document.addEventListener("click", function (e) {
- var btn = e.target.closest("[data-open]");
- if (!btn) return;
- var layout = ALL_LAYOUTS.find(function (l) { return l.id === btn.getAttribute("data-open"); });
- if (!layout) return;
- dialog.className = "dialog " + layout.width + (layout.dialogClass ? " " + layout.dialogClass : "");
- dialog.innerHTML = renderFull(layout);
- initDialogInteractions(layout);
- backdrop.classList.add("open");
- });
- backdrop.addEventListener("click", function (e) {
- if (e.target === backdrop || e.target.closest("[data-close]")) backdrop.classList.remove("open");
- });
- document.addEventListener("keydown", function (e) {
- if (e.key === "Escape") backdrop.classList.remove("open");
- });
- </script>
- </body>
- </html>
|