@morya-ui/mcp 0.4.5 → 0.5.0
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/data/catalog.json +422 -244
- package/data/example-coverage.json +51 -39
- package/dist/decisions.js +1299 -6
- package/package.json +1 -1
package/dist/decisions.js
CHANGED
|
@@ -545,8 +545,10 @@ export const componentDecisions = [
|
|
|
545
545
|
avoidWhen: ['整个页面区块的 padding 和垂直 rhythm'],
|
|
546
546
|
avoidWhenEn: ['Padding and vertical rhythm for whole page sections'],
|
|
547
547
|
recipe: recipe([
|
|
548
|
-
['
|
|
549
|
-
['
|
|
548
|
+
['size / gap 控制间距(gap 是 size 的别名,优先用 size)', 'size / gap for spacing (gap is an alias of size; prefer size)'],
|
|
549
|
+
['align 控制交叉轴对齐(start / center / end / baseline / stretch)', 'align controls cross-axis (start / center / end / baseline / stretch)'],
|
|
550
|
+
['justify 控制主轴分布(start / center / end / between / around / evenly)', 'justify controls main-axis distribution'],
|
|
551
|
+
['wrap 允许折行;筛选行、工具栏内组优先 Flex', 'wrap allows wrapping; prefer Flex for filter rows and toolbar groups'],
|
|
550
552
|
['不要用 Flex 代替 PageContent 的页面 padding', 'Do not replace PageContent page padding with Flex'],
|
|
551
553
|
]),
|
|
552
554
|
...anti([['整页垂直节奏用 Flex → PageContent', 'Whole-page vertical rhythm via Flex → PageContent']]),
|
|
@@ -561,11 +563,16 @@ export const componentDecisions = [
|
|
|
561
563
|
avoidWhen: ['页面级标题与主操作两端对齐'],
|
|
562
564
|
avoidWhenEn: ['Page-level title and primary action need space-between alignment'],
|
|
563
565
|
recipe: recipe([
|
|
564
|
-
['
|
|
565
|
-
['
|
|
566
|
-
['
|
|
566
|
+
['size 控制间距;默认 wrap 折行', 'size for spacing; wraps by default'],
|
|
567
|
+
['align 交叉轴对齐(同 Flex);没有 justify 语义', 'align for cross-axis (same as Flex); no justify semantics'],
|
|
568
|
+
['vertical 垂直堆叠控件组', 'vertical stacks the group vertically'],
|
|
569
|
+
['包裹 MButton 组;行内操作常用 size="small" text 按钮', 'Wrap MButton groups; row actions often size="small" text buttons'],
|
|
570
|
+
['Dialog #footer 内可用 Space + style justify-content:flex-end', 'In Dialog #footer: Space + style justify-content:flex-end'],
|
|
571
|
+
]),
|
|
572
|
+
...anti([
|
|
573
|
+
['标题与主按钮两端对齐只用 Space → PageToolbar / Flex justify', 'Title/actions space-between via Space alone → PageToolbar / Flex'],
|
|
574
|
+
['需要精确对齐控制仍用 Space → Flex', 'Need precise alignment control but using Space → Flex'],
|
|
567
575
|
]),
|
|
568
|
-
...anti([['标题与主按钮两端对齐只用 Space → PageToolbar / Flex justify', 'Title/actions space-between via Space alone → PageToolbar / Flex']]),
|
|
569
576
|
},
|
|
570
577
|
{
|
|
571
578
|
component: 'Grid',
|
|
@@ -1369,6 +1376,1292 @@ export const componentDecisions = [
|
|
|
1369
1376
|
},
|
|
1370
1377
|
],
|
|
1371
1378
|
},
|
|
1379
|
+
{
|
|
1380
|
+
id: 'button-icon-choice',
|
|
1381
|
+
title: '按钮图标放在哪里',
|
|
1382
|
+
titleEn: 'Where to put a button icon',
|
|
1383
|
+
question: '按钮需要带图标时,是用 icon prop、#icon 插槽,还是纯图标按钮?',
|
|
1384
|
+
questionEn: 'When a button needs an icon, should it use the icon prop, the #icon slot, or be an icon-only button?',
|
|
1385
|
+
keywords: [
|
|
1386
|
+
'按钮图标',
|
|
1387
|
+
'图标按钮',
|
|
1388
|
+
'纯图标',
|
|
1389
|
+
'button icon',
|
|
1390
|
+
'icon button',
|
|
1391
|
+
'icon-only',
|
|
1392
|
+
'iconPlacement',
|
|
1393
|
+
'loading icon',
|
|
1394
|
+
],
|
|
1395
|
+
options: [
|
|
1396
|
+
{
|
|
1397
|
+
component: 'icon prop',
|
|
1398
|
+
when: ['标准图标与文字按钮', '图标来自 morya-ui 内置图标名'],
|
|
1399
|
+
whenEn: ['Standard icon + label buttons', 'The icon is a built-in MIcon name'],
|
|
1400
|
+
avoidWhen: ['需要自定义 SVG / 组件图标', 'loading 时要换不同图标'],
|
|
1401
|
+
avoidWhenEn: ['Custom SVG / component icons are needed', 'The loading state needs a different icon'],
|
|
1402
|
+
recipe: recipe([
|
|
1403
|
+
['icon="plus" 传内置图标名', 'icon="plus" passes a built-in icon name'],
|
|
1404
|
+
['iconPlacement="end" 图标放文字右侧', 'iconPlacement="end" places icon after the label'],
|
|
1405
|
+
['图标随按钮 size 自动缩放', 'Icon size follows button size automatically'],
|
|
1406
|
+
['loading 时自动替换为转圈', 'Loading swaps the icon to a spinner automatically'],
|
|
1407
|
+
]),
|
|
1408
|
+
...anti([
|
|
1409
|
+
['手动 <MIcon /> 放默认插槽 → icon prop', 'Hand-placed <MIcon /> in default slot → icon prop'],
|
|
1410
|
+
['图标和文字间距手写 margin → 交给组件', 'Hand-written margin between icon and label → let the component handle it'],
|
|
1411
|
+
]),
|
|
1412
|
+
},
|
|
1413
|
+
{
|
|
1414
|
+
component: '#icon slot',
|
|
1415
|
+
when: ['自定义 SVG / 组件图标', 'loading 时要用 #loadingicon 换不同图标'],
|
|
1416
|
+
whenEn: ['Custom SVG / component icons', 'Loading state needs #loadingicon with a different icon'],
|
|
1417
|
+
avoidWhen: ['标准内置图标名即可满足'],
|
|
1418
|
+
avoidWhenEn: ['A built-in icon name is enough'],
|
|
1419
|
+
recipe: recipe([
|
|
1420
|
+
['#icon 放任意 VNode(自定义 SVG / 组件)', '#icon accepts any VNode (custom SVG / component)'],
|
|
1421
|
+
['#loadingicon 自定义 loading 态图标', '#loadingicon customizes the loading icon'],
|
|
1422
|
+
['图标仍随按钮 size 缩放,对齐由组件保证', 'Icon still scales with button size; alignment is handled'],
|
|
1423
|
+
]),
|
|
1424
|
+
...anti([
|
|
1425
|
+
['#icon 和默认插槽混放导致间距错乱 → 只用一个', 'Mixing #icon and default slot breaks spacing → use one'],
|
|
1426
|
+
]),
|
|
1427
|
+
},
|
|
1428
|
+
{
|
|
1429
|
+
component: 'iconOnly',
|
|
1430
|
+
when: ['工具栏 / 卡片头部等空间紧张处', '图标含义对用户足够明确(如 删除/编辑/关闭)'],
|
|
1431
|
+
whenEn: ['Space-constrained areas like toolbars or card headers', 'The icon meaning is unambiguous to users (delete / edit / close)'],
|
|
1432
|
+
avoidWhen: ['用户可能不认识图标含义', '表单提交等需要明确文字的操作'],
|
|
1433
|
+
avoidWhenEn: ['Users may not recognize the icon', 'Actions that need an explicit label like form submit'],
|
|
1434
|
+
recipe: recipe([
|
|
1435
|
+
['iconOnly + icon="…" 或 #icon', 'iconOnly + icon="…" or #icon'],
|
|
1436
|
+
['必须配 ariaLabel(无视觉文字,屏幕阅读器依赖)', 'ariaLabel is required (no visible label for screen readers)'],
|
|
1437
|
+
['shape="circle" 圆形图标按钮', 'shape="circle" for a round icon button'],
|
|
1438
|
+
['仍可用 loading 切换转圈', 'loading still swaps to spinner'],
|
|
1439
|
+
]),
|
|
1440
|
+
...anti([
|
|
1441
|
+
['iconOnly 把图标放默认插槽 → icon prop 或 #icon', 'Icon in default slot with iconOnly → icon prop or #icon'],
|
|
1442
|
+
['iconOnly 不写 ariaLabel → 必填', 'iconOnly without ariaLabel → required'],
|
|
1443
|
+
['含义不明确的图标硬用 iconOnly → 加文字', 'Ambiguous icon with iconOnly → add a label'],
|
|
1444
|
+
]),
|
|
1445
|
+
},
|
|
1446
|
+
],
|
|
1447
|
+
},
|
|
1448
|
+
{
|
|
1449
|
+
id: 'input-addon-choice',
|
|
1450
|
+
title: '输入框前后缀如何选择',
|
|
1451
|
+
titleEn: 'Choosing input prefix / suffix',
|
|
1452
|
+
question: '输入框需要带图标、文字前缀或多个控件合并时,用 MIconField 还是 MInputGroup?',
|
|
1453
|
+
questionEn: 'When an input needs an icon, a text prefix, or merged controls, should it use MIconField or MInputGroup?',
|
|
1454
|
+
keywords: [
|
|
1455
|
+
'输入框图标',
|
|
1456
|
+
'前缀',
|
|
1457
|
+
'后缀',
|
|
1458
|
+
'input icon',
|
|
1459
|
+
'prefix',
|
|
1460
|
+
'suffix',
|
|
1461
|
+
'addon',
|
|
1462
|
+
'input group',
|
|
1463
|
+
'iconfield',
|
|
1464
|
+
],
|
|
1465
|
+
options: [
|
|
1466
|
+
{
|
|
1467
|
+
component: 'MIconField',
|
|
1468
|
+
when: ['输入框内左/右侧放一个图标(搜索、清除提示等)'],
|
|
1469
|
+
whenEn: ['A single icon inside the input on the left or right (search, clear hint, etc.)'],
|
|
1470
|
+
avoidWhen: ['需要文字前缀 / 后缀', '需要合并多个控件(输入框 + 按钮)'],
|
|
1471
|
+
avoidWhenEn: ['A text prefix / suffix is needed', 'Multiple controls need to merge (input + button)'],
|
|
1472
|
+
recipe: recipe([
|
|
1473
|
+
['iconPosition="left"(默认)或 "right"', 'iconPosition="left" (default) or "right"'],
|
|
1474
|
+
['#icon 插槽放 MIcon 或任意图标', '#icon slot holds MIcon or any icon'],
|
|
1475
|
+
['默认插槽放 MInput / MTextarea 等控件', 'Default slot holds MInput / MTextarea etc.'],
|
|
1476
|
+
['图标随控件主题对齐,无需手写定位', 'Icon aligns with control theme; no manual positioning'],
|
|
1477
|
+
], [
|
|
1478
|
+
['#icon 图标内容', '#icon for the icon'],
|
|
1479
|
+
['默认插槽:输入控件', 'Default: the input control'],
|
|
1480
|
+
]),
|
|
1481
|
+
...anti([
|
|
1482
|
+
['手写 absolute 定位图标 → MIconField', 'Hand-written absolute icon → MIconField'],
|
|
1483
|
+
['文字前缀用 MIconField → MInputGroup + MInputGroupAddon', 'Text prefix via MIconField → MInputGroup + MInputGroupAddon'],
|
|
1484
|
+
]),
|
|
1485
|
+
},
|
|
1486
|
+
{
|
|
1487
|
+
component: 'MInputGroup',
|
|
1488
|
+
when: ['输入框需要文字前缀 / 后缀(如 https://、$、单位)', '输入框 + 按钮 / 下拉合并成一个整体'],
|
|
1489
|
+
whenEn: ['Input needs a text prefix / suffix (https://, $, units)', 'Input merged with a button or select into one control'],
|
|
1490
|
+
avoidWhen: ['只是输入框内放一个图标'],
|
|
1491
|
+
avoidWhenEn: ['Only a single icon inside the input'],
|
|
1492
|
+
recipe: recipe([
|
|
1493
|
+
['MInputGroup 包裹 MInput + MInputGroupAddon', 'MInputGroup wraps MInput + MInputGroupAddon'],
|
|
1494
|
+
['MInputGroupAddon 放文字前缀 / 后缀', 'MInputGroupAddon holds the text prefix / suffix'],
|
|
1495
|
+
['可混合 MButton 作为后置操作', 'MButton can mix in as a trailing action'],
|
|
1496
|
+
]),
|
|
1497
|
+
...anti([
|
|
1498
|
+
['输入框内单图标用 MInputGroup → MIconField', 'Single in-input icon via MInputGroup → MIconField'],
|
|
1499
|
+
['手写 flex 拼输入框和按钮 → MInputGroup', 'Hand-flexed input + button → MInputGroup'],
|
|
1500
|
+
]),
|
|
1501
|
+
},
|
|
1502
|
+
],
|
|
1503
|
+
},
|
|
1504
|
+
{
|
|
1505
|
+
id: 'toggle-input-choice',
|
|
1506
|
+
title: '开关、复选框、滑块、标签输入如何选择',
|
|
1507
|
+
titleEn: 'Choosing switch, checkbox, slider, or tag input',
|
|
1508
|
+
question: '这个输入是立即生效的设置、表单提交时的勾选、范围调节,还是自由标签输入?',
|
|
1509
|
+
questionEn: 'Is this an instant setting toggle, a form-submitted checkbox, a range adjustment, or free-form tag entry?',
|
|
1510
|
+
keywords: [
|
|
1511
|
+
'开关',
|
|
1512
|
+
'复选框',
|
|
1513
|
+
'滑块',
|
|
1514
|
+
'标签输入',
|
|
1515
|
+
'多选',
|
|
1516
|
+
'switch',
|
|
1517
|
+
'checkbox',
|
|
1518
|
+
'slider',
|
|
1519
|
+
'inputtags',
|
|
1520
|
+
'tags',
|
|
1521
|
+
'toggle',
|
|
1522
|
+
'range',
|
|
1523
|
+
],
|
|
1524
|
+
options: [
|
|
1525
|
+
{
|
|
1526
|
+
component: 'MSwitch',
|
|
1527
|
+
when: ['设置项立即生效,不需要点保存', '只有开 / 关两种状态'],
|
|
1528
|
+
whenEn: ['A setting takes effect immediately without saving', 'Only on / off states'],
|
|
1529
|
+
avoidWhen: ['表单提交时才确认的勾选协议', '需要从多个选项中选'],
|
|
1530
|
+
avoidWhenEn: ['A terms checkbox confirmed on form submit', 'Selection from multiple options'],
|
|
1531
|
+
recipe: recipe([
|
|
1532
|
+
['v-model 布尔值', 'v-model boolean'],
|
|
1533
|
+
['loading 显示转圈并阻止切换', 'loading shows a spinner and blocks toggles'],
|
|
1534
|
+
['checkedText / uncheckedText 自定义状态文案', 'checkedText / uncheckedText for state copy'],
|
|
1535
|
+
['label 或外部 MFormItem 提供可访问名称', 'label or wrapping MFormItem provides the accessible name'],
|
|
1536
|
+
]),
|
|
1537
|
+
...anti([
|
|
1538
|
+
['协议确认用 MSwitch → MCheckbox', 'Terms confirmation via MSwitch → MCheckbox'],
|
|
1539
|
+
['多选需求用多个 MSwitch → MCheckboxGroup / MSelect', 'Multi-select via stacked MSwitch → MCheckboxGroup / MSelect'],
|
|
1540
|
+
]),
|
|
1541
|
+
},
|
|
1542
|
+
{
|
|
1543
|
+
component: 'MCheckbox',
|
|
1544
|
+
when: ['表单提交时才生效的勾选(协议、批量选择)', '需要从固定选项中选多个'],
|
|
1545
|
+
whenEn: ['A checkbox that takes effect on form submit (terms, bulk select)', 'Selecting multiple from fixed options'],
|
|
1546
|
+
avoidWhen: ['设置项需要立即生效'],
|
|
1547
|
+
avoidWhenEn: ['A setting must take effect immediately'],
|
|
1548
|
+
recipe: recipe([
|
|
1549
|
+
['MCheckboxGroup v-model 数组包多个 MCheckbox', 'MCheckboxGroup v-model array wraps multiple MCheckbox'],
|
|
1550
|
+
['单个勾选用 MCheckbox v-model 布尔值', 'Single checkbox: MCheckbox v-model boolean'],
|
|
1551
|
+
['与 MFormItem 一起用,提交时校验', 'Use with MFormItem for submit-time validation'],
|
|
1552
|
+
]),
|
|
1553
|
+
...anti([['立即生效设置用 MCheckbox → MSwitch', 'Instant setting via MCheckbox → MSwitch']]),
|
|
1554
|
+
},
|
|
1555
|
+
{
|
|
1556
|
+
component: 'MSlider',
|
|
1557
|
+
when: ['范围调节(音量、价格区间、阈值)', '视觉化比例比精确数值更重要'],
|
|
1558
|
+
whenEn: ['Range adjustment (volume, price range, threshold)', 'Visual proportion matters more than exact value'],
|
|
1559
|
+
avoidWhen: ['需要精确输入数值', '选项是离散枚举'],
|
|
1560
|
+
avoidWhenEn: ['Exact numeric input is required', 'Options are discrete enums'],
|
|
1561
|
+
recipe: recipe([
|
|
1562
|
+
['v-model 数值或 range 数值数组', 'v-model number or range number array'],
|
|
1563
|
+
['min / max / step 控制范围', 'min / max / step for the range'],
|
|
1564
|
+
['marks 显示刻度标签', 'marks for tick labels'],
|
|
1565
|
+
['tooltip 拖拽时显示当前值', 'tooltip shows current value while dragging'],
|
|
1566
|
+
]),
|
|
1567
|
+
...anti([
|
|
1568
|
+
['精确数值输入用 MSlider → MInputNumber', 'Exact numeric input via MSlider → MInputNumber'],
|
|
1569
|
+
['离散枚举选择用 MSlider → MSelect / MSelectButton', 'Discrete enum via MSlider → MSelect / MSelectButton'],
|
|
1570
|
+
]),
|
|
1571
|
+
},
|
|
1572
|
+
{
|
|
1573
|
+
component: 'MInputTags',
|
|
1574
|
+
when: ['自由输入并回车生成标签(关键词、邮箱列表)', '标签内容不受固定选项约束'],
|
|
1575
|
+
whenEn: ['Free-form entry generating tags on Enter (keywords, email lists)', 'Tag content is not constrained to fixed options'],
|
|
1576
|
+
avoidWhen: ['只能从预定义选项中选', '单个值输入'],
|
|
1577
|
+
avoidWhenEn: ['Selection must be from predefined options', 'Single value input'],
|
|
1578
|
+
recipe: recipe([
|
|
1579
|
+
['v-model 字符串数组', 'v-model string array'],
|
|
1580
|
+
['separator 支持逗号等额外分隔符', 'separator supports extra delimiters like comma'],
|
|
1581
|
+
['max 限制标签数量', 'max caps the tag count'],
|
|
1582
|
+
['addOnBlur 失焦时自动添加', 'addOnBlur adds on blur'],
|
|
1583
|
+
]),
|
|
1584
|
+
...anti([
|
|
1585
|
+
['固定选项多选用 MInputTags → MSelect mode="multiple"', 'Fixed multi-select via MInputTags → MSelect mode="multiple"'],
|
|
1586
|
+
['允许创建新选项但要从建议中选 → MSelect mode="tags"', 'Create new options from suggestions → MSelect mode="tags"'],
|
|
1587
|
+
]),
|
|
1588
|
+
},
|
|
1589
|
+
],
|
|
1590
|
+
},
|
|
1591
|
+
{
|
|
1592
|
+
id: 'status-display-choice',
|
|
1593
|
+
title: '头像、图片、标签、状态如何选择',
|
|
1594
|
+
titleEn: 'Choosing avatar, image, tag, or status display',
|
|
1595
|
+
question: '这是用户头像、可预览图片、分类标签、可移除实体,还是行内业务状态?',
|
|
1596
|
+
questionEn: 'Is this a user avatar, a previewable image, a category tag, a removable entity, or an inline business status?',
|
|
1597
|
+
keywords: [
|
|
1598
|
+
'头像',
|
|
1599
|
+
'图片',
|
|
1600
|
+
'标签',
|
|
1601
|
+
'状态',
|
|
1602
|
+
'avatar',
|
|
1603
|
+
'image',
|
|
1604
|
+
'tag',
|
|
1605
|
+
'chip',
|
|
1606
|
+
'status',
|
|
1607
|
+
'badge',
|
|
1608
|
+
'预览',
|
|
1609
|
+
'角标',
|
|
1610
|
+
],
|
|
1611
|
+
options: [
|
|
1612
|
+
{
|
|
1613
|
+
component: 'MAvatar',
|
|
1614
|
+
when: ['展示单个用户 / 实体头像', '图片加载失败需要回退到首字母或图标'],
|
|
1615
|
+
whenEn: ['Displaying a single user / entity avatar', 'Fallback to initials or an icon when the image fails'],
|
|
1616
|
+
avoidWhen: ['需要展示多个用户堆叠', '需要点击预览大图'],
|
|
1617
|
+
avoidWhenEn: ['Multiple users need to stack', 'Click-to-preview a larger image is needed'],
|
|
1618
|
+
recipe: recipe([
|
|
1619
|
+
['image 头像图片 URL,优先于 icon 和 label', 'image URL takes priority over icon and label'],
|
|
1620
|
+
['label 回退首字母;icon 回退内置图标', 'label falls back to initials; icon falls back to a built-in icon'],
|
|
1621
|
+
['shape="circle"(默认)或 "square"', 'shape="circle" (default) or "square"'],
|
|
1622
|
+
['size 可选 xlarge 用于个人资料页', 'size up to xlarge for profile pages'],
|
|
1623
|
+
]),
|
|
1624
|
+
...anti([
|
|
1625
|
+
['多用户堆叠用多个 MAvatar → MAvatarGroup', 'Multiple users via stacked MAvatar → MAvatarGroup'],
|
|
1626
|
+
['手写 img 圆形头像 → MAvatar', 'Hand-written circular img → MAvatar'],
|
|
1627
|
+
]),
|
|
1628
|
+
},
|
|
1629
|
+
{
|
|
1630
|
+
component: 'MAvatarGroup',
|
|
1631
|
+
when: ['多个用户头像堆叠展示', '超出数量需要 +N 计数'],
|
|
1632
|
+
whenEn: ['Stacking multiple user avatars', 'Overflow needs a +N count'],
|
|
1633
|
+
avoidWhen: ['单个用户头像'],
|
|
1634
|
+
avoidWhenEn: ['A single user avatar'],
|
|
1635
|
+
recipe: recipe([
|
|
1636
|
+
['包裹多个 MAvatar', 'Wraps multiple MAvatar'],
|
|
1637
|
+
['max 控制最多显示几个,超出显示 +N', 'max caps visible avatars; overflow shows +N'],
|
|
1638
|
+
['size 统一控制组内头像尺寸', 'size applies a uniform avatar size'],
|
|
1639
|
+
]),
|
|
1640
|
+
...anti([['单个头像用 MAvatarGroup → MAvatar', 'Single avatar via MAvatarGroup → MAvatar']]),
|
|
1641
|
+
},
|
|
1642
|
+
{
|
|
1643
|
+
component: 'MImage',
|
|
1644
|
+
when: ['需要点击预览大图', '需要懒加载或 fit 控制'],
|
|
1645
|
+
whenEn: ['Click-to-preview a larger image', 'Lazy loading or fit control is needed'],
|
|
1646
|
+
avoidWhen: ['纯装饰背景图', '不需要预览的普通图片'],
|
|
1647
|
+
avoidWhenEn: ['Purely decorative background images', 'Plain images that do not need preview'],
|
|
1648
|
+
recipe: recipe([
|
|
1649
|
+
['preview 默认开启,点击放大', 'preview is on by default; click to enlarge'],
|
|
1650
|
+
['previewSrc 可指定不同于 src 的大图', 'previewSrc can differ from src'],
|
|
1651
|
+
['fit 控制 object-fit(cover / contain / fill…)', 'fit controls object-fit (cover / contain / fill…)'],
|
|
1652
|
+
['width / height 显式设置避免布局抖动', 'width / height avoids layout shift'],
|
|
1653
|
+
]),
|
|
1654
|
+
...anti([
|
|
1655
|
+
['需要预览却手写 img → MImage', 'Need preview but hand-written img → MImage'],
|
|
1656
|
+
['用户头像用 MImage → MAvatar', 'User avatar via MImage → MAvatar'],
|
|
1657
|
+
]),
|
|
1658
|
+
},
|
|
1659
|
+
{
|
|
1660
|
+
component: 'MTag',
|
|
1661
|
+
when: ['分类、筛选结果或可关闭标签', '需要比 Status 更像芯片的表面'],
|
|
1662
|
+
whenEn: ['A category, filter result, or closable label', 'A chip-like surface rather than an inline status'],
|
|
1663
|
+
avoidWhen: ['只是一行里的轻量状态', '带图片且代表一个可移除实体'],
|
|
1664
|
+
avoidWhenEn: ['A lightweight inline status', 'An entity with an image that can be removed'],
|
|
1665
|
+
recipe: recipe([
|
|
1666
|
+
['value 或默认插槽文案', 'value or default-slot copy'],
|
|
1667
|
+
['type 语义色;closable + @close', 'type semantic color; closable + @close'],
|
|
1668
|
+
['checkable + v-model:checked 可选中切换', 'checkable + v-model:checked for toggleable tags'],
|
|
1669
|
+
['icon 加内置图标;rounded 圆角', 'icon for a built-in icon; rounded for pill shape'],
|
|
1670
|
+
], undefined, [['@close 关闭标签', '@close when the tag is closed']]),
|
|
1671
|
+
...anti([
|
|
1672
|
+
['行内启用/停用状态用 MTag → MStatus', 'Enabled/disabled inline via MTag → MStatus'],
|
|
1673
|
+
['可移除实体(带头像)用 MTag → MChip', 'Removable entity with avatar via MTag → MChip'],
|
|
1674
|
+
]),
|
|
1675
|
+
},
|
|
1676
|
+
],
|
|
1677
|
+
},
|
|
1678
|
+
{
|
|
1679
|
+
id: 'nav-pattern-choice',
|
|
1680
|
+
title: '页面内导航模式如何选择',
|
|
1681
|
+
titleEn: 'Choosing in-page navigation patterns',
|
|
1682
|
+
question: '这是长页面锚点跳转、滚动固定、内容切换、分步流程,还是层级路径?',
|
|
1683
|
+
questionEn: 'Is this anchor jump in a long page, scroll pinning, content switching, a step flow, or a hierarchy path?',
|
|
1684
|
+
keywords: [
|
|
1685
|
+
'锚点',
|
|
1686
|
+
'固钉',
|
|
1687
|
+
'标签页',
|
|
1688
|
+
'步骤条',
|
|
1689
|
+
'面包屑',
|
|
1690
|
+
'anchor',
|
|
1691
|
+
'affix',
|
|
1692
|
+
'tabs',
|
|
1693
|
+
'stepper',
|
|
1694
|
+
'breadcrumb',
|
|
1695
|
+
'分步',
|
|
1696
|
+
'向导',
|
|
1697
|
+
],
|
|
1698
|
+
options: [
|
|
1699
|
+
{
|
|
1700
|
+
component: 'MAnchor',
|
|
1701
|
+
when: ['长页面右侧锚点导航', '内容区块有明确标题层级'],
|
|
1702
|
+
whenEn: ['Anchor navigation on the right of a long page', 'Content sections have clear heading hierarchy'],
|
|
1703
|
+
avoidWhen: ['页面很短不需要跳转', '需要切换显示不同内容面板'],
|
|
1704
|
+
avoidWhenEn: ['The page is too short to need jumps', 'Different content panels need to switch'],
|
|
1705
|
+
recipe: recipe([
|
|
1706
|
+
['items 手动指定,或根据标题自动生成', 'items set manually, or auto-generated from headings'],
|
|
1707
|
+
['与 MLayoutContent 长页面配合', 'Pairs with long MLayoutContent pages'],
|
|
1708
|
+
['点击平滑滚动到对应区块', 'Click smooth-scrolls to the section'],
|
|
1709
|
+
]),
|
|
1710
|
+
...anti([['内容切换用 MAnchor → MTabs', 'Content switching via MAnchor → MTabs']]),
|
|
1711
|
+
},
|
|
1712
|
+
{
|
|
1713
|
+
component: 'MAffix',
|
|
1714
|
+
when: ['元素滚动到某位置后固定(操作栏吸顶、侧栏跟随)'],
|
|
1715
|
+
whenEn: ['An element pins after scrolling to a position (sticky action bar, following sider)'],
|
|
1716
|
+
avoidWhen: ['页面顶部固定导航(那是 MLayoutHeader)', '元素不需要随滚动变化'],
|
|
1717
|
+
avoidWhenEn: ['Top-fixed navigation (that is MLayoutHeader)', 'The element does not change with scroll'],
|
|
1718
|
+
recipe: recipe([
|
|
1719
|
+
['offsetTop 距离顶部多少时固定', 'offsetTop pins at a distance from the top'],
|
|
1720
|
+
['target 指定滚动容器(默认 window)', 'target sets the scroll container (window by default)'],
|
|
1721
|
+
['z-index 按需调整避免被遮挡', 'Adjust z-index to avoid occlusion'],
|
|
1722
|
+
]),
|
|
1723
|
+
...anti([['页面顶部导航用 MAffix → MLayoutHeader', 'Top page nav via MAffix → MLayoutHeader']]),
|
|
1724
|
+
},
|
|
1725
|
+
{
|
|
1726
|
+
component: 'MTabs',
|
|
1727
|
+
when: ['多个内容面板切换显示', '标签数量适中且需要全部可见'],
|
|
1728
|
+
whenEn: ['Switching between multiple content panels', 'A moderate number of tabs all visible'],
|
|
1729
|
+
avoidWhen: ['选项是表单值(用 MSelectButton)', '需要手风琴折叠展开'],
|
|
1730
|
+
avoidWhenEn: ['Options are form values (use MSelectButton)', 'Accordion expand/collapse is needed'],
|
|
1731
|
+
recipe: recipe([
|
|
1732
|
+
['v-model:value 控制当前标签', 'v-model:value controls the active tab'],
|
|
1733
|
+
['closable 可关闭标签(配合 @tab-close)', 'closable for dismissible tabs (with @tab-close)'],
|
|
1734
|
+
['与路由结合时用 to 或 @tab-click 导航', 'Use to or @tab-click for route navigation'],
|
|
1735
|
+
]),
|
|
1736
|
+
...anti([
|
|
1737
|
+
['表单选项用 MTabs → MSelectButton', 'Form options via MTabs → MSelectButton'],
|
|
1738
|
+
['折叠面板用 MTabs → MAccordion', 'Collapse panels via MTabs → MAccordion'],
|
|
1739
|
+
]),
|
|
1740
|
+
},
|
|
1741
|
+
{
|
|
1742
|
+
component: 'MStepper',
|
|
1743
|
+
when: ['多步表单 / 流程向导', '步骤有前后依赖关系'],
|
|
1744
|
+
whenEn: ['Multi-step forms / process wizards', 'Steps have sequential dependencies'],
|
|
1745
|
+
avoidWhen: ['内容无顺序关系的切换', '步骤极少(2 步用对话框即可)'],
|
|
1746
|
+
avoidWhenEn: ['Content switching without order', 'Very few steps (a dialog works for 2)'],
|
|
1747
|
+
recipe: recipe([
|
|
1748
|
+
['v-model 当前步骤索引', 'v-model for the current step index'],
|
|
1749
|
+
['steps 配置每步标题 / 描述 / 图标', 'steps configures title / description / icon per step'],
|
|
1750
|
+
['线性模式禁止跳步;非线性允许点击跳转', 'Linear mode blocks skipping; non-linear allows click jumps'],
|
|
1751
|
+
['与 MForm 分步校验配合', 'Pair with MForm for per-step validation'],
|
|
1752
|
+
]),
|
|
1753
|
+
...anti([
|
|
1754
|
+
['无顺序内容切换用 MStepper → MTabs', 'Unordered content via MStepper → MTabs'],
|
|
1755
|
+
['两步确认也用 MStepper → 对话框', 'Two-step confirm via MStepper → dialog'],
|
|
1756
|
+
]),
|
|
1757
|
+
},
|
|
1758
|
+
{
|
|
1759
|
+
component: 'MBreadcrumb',
|
|
1760
|
+
when: ['展示当前页面在层级中的路径', '层级较深(≥3 层)的后台系统'],
|
|
1761
|
+
whenEn: ['Showing the current page path in a hierarchy', 'Deep hierarchies (≥3 levels) in admin systems'],
|
|
1762
|
+
avoidWhen: ['层级很浅(首页 → 列表页)', '移动端空间紧张'],
|
|
1763
|
+
avoidWhenEn: ['Shallow hierarchy (home → list)', 'Tight mobile space'],
|
|
1764
|
+
recipe: recipe([
|
|
1765
|
+
['model 数组配置路径项(label + to / icon)', 'model array configures path items (label + to / icon)'],
|
|
1766
|
+
['首页项常用 home 图标', 'The home item often uses a home icon'],
|
|
1767
|
+
['与 MPageHeader #breadcrumb 插槽配合', 'Pairs with MPageHeader #breadcrumb slot'],
|
|
1768
|
+
]),
|
|
1769
|
+
...anti([
|
|
1770
|
+
['层级浅硬套 MBreadcrumb → 去掉', 'Shallow hierarchy with MBreadcrumb → remove it'],
|
|
1771
|
+
['用 MBreadcrumb 代替 Tabs 切换 → MTabs', 'Using MBreadcrumb as tab switching → MTabs'],
|
|
1772
|
+
]),
|
|
1773
|
+
},
|
|
1774
|
+
],
|
|
1775
|
+
},
|
|
1776
|
+
{
|
|
1777
|
+
id: 'date-input-choice',
|
|
1778
|
+
title: '日期时间输入如何选择',
|
|
1779
|
+
titleEn: 'Choosing date and time inputs',
|
|
1780
|
+
question: '这是表单内选择日期 / 时间,还是整月视图展示与选择?',
|
|
1781
|
+
questionEn: 'Is this picking a date / time in a form, or displaying and selecting on a full calendar?',
|
|
1782
|
+
keywords: [
|
|
1783
|
+
'日期',
|
|
1784
|
+
'时间',
|
|
1785
|
+
'日历',
|
|
1786
|
+
'范围',
|
|
1787
|
+
'date',
|
|
1788
|
+
'time',
|
|
1789
|
+
'calendar',
|
|
1790
|
+
'datepicker',
|
|
1791
|
+
'timepicker',
|
|
1792
|
+
'range',
|
|
1793
|
+
'月份',
|
|
1794
|
+
'年份',
|
|
1795
|
+
],
|
|
1796
|
+
options: [
|
|
1797
|
+
{
|
|
1798
|
+
component: 'MDatePicker',
|
|
1799
|
+
when: ['表单内选择日期 / 日期时间 / 月份 / 年份', '需要范围选择或快捷选项'],
|
|
1800
|
+
whenEn: ['Picking date / datetime / month / year in a form', 'Range selection or shortcuts are needed'],
|
|
1801
|
+
avoidWhen: ['需要整月视图展示事件', '只需要选时间'],
|
|
1802
|
+
avoidWhenEn: ['A full calendar view is needed for events', 'Only time selection is needed'],
|
|
1803
|
+
recipe: recipe([
|
|
1804
|
+
['type="date"(默认)/ daterange / datetime / datetimerange / month / year', 'type="date" (default) / daterange / datetime / datetimerange / month / year'],
|
|
1805
|
+
['shortcuts 预设快捷选项(今天 / 最近 7 天…)', 'shortcuts for presets (today / last 7 days…)'],
|
|
1806
|
+
['needConfirm 用于 datetime / time,避免误触提交', 'needConfirm for datetime / time to avoid accidental commits'],
|
|
1807
|
+
['mask 允许手动输入并自动补分隔符', 'mask allows manual entry with auto separators'],
|
|
1808
|
+
['fluid 表单内占满宽度', 'fluid for full width in forms'],
|
|
1809
|
+
], undefined, [['@change 提交选择(含 clear 以外的所有变更)', '@change commits the selection (all changes except clear)']]),
|
|
1810
|
+
...anti([
|
|
1811
|
+
['整月事件展示用 MDatePicker → MCalendar', 'Full-month event display via MDatePicker → MCalendar'],
|
|
1812
|
+
['只需要时间用 MDatePicker type="time" → MTimePicker', 'Time-only via MDatePicker type="time" → MTimePicker'],
|
|
1813
|
+
]),
|
|
1814
|
+
},
|
|
1815
|
+
{
|
|
1816
|
+
component: 'MTimePicker',
|
|
1817
|
+
when: ['只需要选择时间(小时 / 分钟 / 秒)'],
|
|
1818
|
+
whenEn: ['Only time selection is needed (hour / minute / second)'],
|
|
1819
|
+
avoidWhen: ['需要同时选日期', '需要整月视图'],
|
|
1820
|
+
avoidWhenEn: ['Date selection is also needed', 'A full calendar view is needed'],
|
|
1821
|
+
recipe: recipe([
|
|
1822
|
+
['复用 MDatePicker props,type 固定为 time', 'Reuses MDatePicker props with type fixed to time'],
|
|
1823
|
+
['showSeconds 显示秒', 'showSeconds for seconds'],
|
|
1824
|
+
['needConfirm 推荐开启,避免滚动误触', 'needConfirm recommended to avoid scroll accidents'],
|
|
1825
|
+
]),
|
|
1826
|
+
...anti([['日期时间混合用 MTimePicker → MDatePicker type="datetime"', 'Date+time via MTimePicker → MDatePicker type="datetime"']]),
|
|
1827
|
+
},
|
|
1828
|
+
{
|
|
1829
|
+
component: 'MCalendar',
|
|
1830
|
+
when: ['整月视图展示事件 / 排班 / 标记日期', '需要自定义日期单元格内容'],
|
|
1831
|
+
whenEn: ['Full-month view for events / schedules / date marking', 'Custom date cell content is needed'],
|
|
1832
|
+
avoidWhen: ['表单内紧凑选择日期', '空间紧张'],
|
|
1833
|
+
avoidWhenEn: ['Compact date picking in a form', 'Space is tight'],
|
|
1834
|
+
recipe: recipe([
|
|
1835
|
+
['v-model 选中日期;mode="month"(默认)或 "year"', 'v-model for the selected date; mode="month" (default) or "year"'],
|
|
1836
|
+
['fullscreen 占满容器;false 为紧凑卡片', 'fullscreen fills the container; false for a compact card'],
|
|
1837
|
+
['#dateCell 自定义日期内容(事件标记)', '#dateCell customizes cell content (event markers)'],
|
|
1838
|
+
['disabledDate / validRange 限制可选范围', 'disabledDate / validRange constrains selectable dates'],
|
|
1839
|
+
], [
|
|
1840
|
+
['#dateCell 自定义日期单元格', '#dateCell for custom date cells'],
|
|
1841
|
+
['#header 自定义头部(含年月切换)', '#header for a custom header (with year/month switch)'],
|
|
1842
|
+
], [['@panelChange 面板切换(年月 / 模式)', '@panelChange on panel switch (year/month / mode)']]),
|
|
1843
|
+
...anti([
|
|
1844
|
+
['表单内紧凑日期选择用 MCalendar → MDatePicker', 'Compact form date via MCalendar → MDatePicker'],
|
|
1845
|
+
['简单日期选择也写 #dateCell → 先用默认', 'Custom #dateCell for simple picking → use default first'],
|
|
1846
|
+
]),
|
|
1847
|
+
},
|
|
1848
|
+
],
|
|
1849
|
+
},
|
|
1850
|
+
{
|
|
1851
|
+
id: 'media-display-choice',
|
|
1852
|
+
title: '轮播、画廊、时间线、评分如何选择',
|
|
1853
|
+
titleEn: 'Choosing carousel, gallery, timeline, or rating',
|
|
1854
|
+
question: '这是内容轮播、图片画廊、事件流,还是用户评分?',
|
|
1855
|
+
questionEn: 'Is this a content carousel, an image gallery, an event stream, or a user rating?',
|
|
1856
|
+
keywords: [
|
|
1857
|
+
'轮播',
|
|
1858
|
+
'画廊',
|
|
1859
|
+
'时间线',
|
|
1860
|
+
'评分',
|
|
1861
|
+
'carousel',
|
|
1862
|
+
'gallery',
|
|
1863
|
+
'timeline',
|
|
1864
|
+
'rating',
|
|
1865
|
+
'图片展示',
|
|
1866
|
+
'事件流',
|
|
1867
|
+
],
|
|
1868
|
+
options: [
|
|
1869
|
+
{
|
|
1870
|
+
component: 'MCarousel',
|
|
1871
|
+
when: ['内容轮播(不限图片,卡片 / 图文混排)', '需要箭头 / 指示点 / 自动播放'],
|
|
1872
|
+
whenEn: ['Content carousel (not just images; cards / mixed content)', 'Arrows / dots / autoplay are needed'],
|
|
1873
|
+
avoidWhen: ['纯图片展示且需要缩略图', '垂直滚动列表'],
|
|
1874
|
+
avoidWhenEn: ['Pure image display with thumbnails', 'A vertical scrolling list'],
|
|
1875
|
+
recipe: recipe([
|
|
1876
|
+
['MCarouselItem 包每页内容', 'MCarouselItem wraps each slide'],
|
|
1877
|
+
['effect="slide"(默认)/ fade / card', 'effect="slide" (default) / fade / card'],
|
|
1878
|
+
['autoplay + interval 自动播放;loop 循环', 'autoplay + interval for auto-play; loop for infinite'],
|
|
1879
|
+
['slidesPerView 一次显示多页', 'slidesPerView for multiple visible slides'],
|
|
1880
|
+
], undefined, [['@update:currentIndex 切换页码', '@update:currentIndex on slide change']]),
|
|
1881
|
+
...anti([
|
|
1882
|
+
['纯图片展示用 MCarousel → MGallery', 'Pure image display via MCarousel → MGallery'],
|
|
1883
|
+
['垂直滚动列表用 MCarousel direction="vertical" → 普通滚动', 'Vertical list via MCarousel → normal scroll'],
|
|
1884
|
+
]),
|
|
1885
|
+
},
|
|
1886
|
+
{
|
|
1887
|
+
component: 'MGallery',
|
|
1888
|
+
when: ['纯图片展示(产品图 / 相册)', '需要缩略图 + 点击预览'],
|
|
1889
|
+
whenEn: ['Pure image display (product photos / albums)', 'Thumbnails + click-to-preview are needed'],
|
|
1890
|
+
avoidWhen: ['内容不限于图片', '需要自动播放'],
|
|
1891
|
+
avoidWhenEn: ['Content is not only images', 'Autoplay is needed'],
|
|
1892
|
+
recipe: recipe([
|
|
1893
|
+
['images 传 URL 数组或 { src, alt, caption } 对象', 'images takes URLs or { src, alt, caption } objects'],
|
|
1894
|
+
['点击主图进入预览模式', 'Click the main image to enter preview'],
|
|
1895
|
+
['缩略图条自动同步', 'The thumbnail strip syncs automatically'],
|
|
1896
|
+
]),
|
|
1897
|
+
...anti([
|
|
1898
|
+
['图文混排轮播用 MGallery → MCarousel', 'Mixed content carousel via MGallery → MCarousel'],
|
|
1899
|
+
['单张图片预览用 MGallery → MImage', 'Single image preview via MGallery → MImage'],
|
|
1900
|
+
]),
|
|
1901
|
+
},
|
|
1902
|
+
{
|
|
1903
|
+
component: 'MTimeline',
|
|
1904
|
+
when: ['事件流 / 物流跟踪 / 操作记录', '需要按时间顺序展示状态变化'],
|
|
1905
|
+
whenEn: ['Event streams / logistics tracking / activity logs', 'Status changes shown in time order'],
|
|
1906
|
+
avoidWhen: ['静态步骤流程(用 MStepper)', '无时间属性的普通列表'],
|
|
1907
|
+
avoidWhenEn: ['Static step flows (use MStepper)', 'Plain lists without time attributes'],
|
|
1908
|
+
recipe: recipe([
|
|
1909
|
+
['value 数组配置事件(status / content / date / icon / type)', 'value array configures events (status / content / date / icon / type)'],
|
|
1910
|
+
['layout="vertical"(默认)或 "horizontal"', 'layout="vertical" (default) or "horizontal"'],
|
|
1911
|
+
['align="alternate" 左右交错', 'align="alternate" for alternating sides'],
|
|
1912
|
+
['pending 显示待续节点', 'pending shows a trailing pending node'],
|
|
1913
|
+
], [['#content 自定义事件内容', '#content for custom event content']]),
|
|
1914
|
+
...anti([
|
|
1915
|
+
['静态步骤流程用 MTimeline → MStepper', 'Static step flow via MTimeline → MStepper'],
|
|
1916
|
+
['普通列表用 MTimeline → MList', 'Plain list via MTimeline → MList'],
|
|
1917
|
+
]),
|
|
1918
|
+
},
|
|
1919
|
+
{
|
|
1920
|
+
component: 'MRating',
|
|
1921
|
+
when: ['用户评分(商品 / 服务 / 内容)', '需要半星或只读展示'],
|
|
1922
|
+
whenEn: ['User ratings (products / services / content)', 'Half-star or read-only display is needed'],
|
|
1923
|
+
avoidWhen: ['需要输入精确数值', '需要调节设置'],
|
|
1924
|
+
avoidWhenEn: ['Exact numeric input is required', 'A setting needs adjustment'],
|
|
1925
|
+
recipe: recipe([
|
|
1926
|
+
['v-model 数值;stars 总星数', 'v-model number; stars for total count'],
|
|
1927
|
+
['allowHalf 允许 0.5 步进', 'allowHalf for 0.5 increments'],
|
|
1928
|
+
['readonly 只读展示(评价列表)', 'readonly for display-only (review lists)'],
|
|
1929
|
+
['allowClear 允许清除评分', 'allowClear to reset the rating'],
|
|
1930
|
+
]),
|
|
1931
|
+
...anti([
|
|
1932
|
+
['精确数值输入用 MRating → MInputNumber', 'Exact numeric input via MRating → MInputNumber'],
|
|
1933
|
+
['设置调节用 MRating → MSlider / MKnob', 'Setting adjustment via MRating → MSlider / MKnob'],
|
|
1934
|
+
]),
|
|
1935
|
+
},
|
|
1936
|
+
],
|
|
1937
|
+
},
|
|
1938
|
+
{
|
|
1939
|
+
id: 'specialized-display-choice',
|
|
1940
|
+
title: '水印、二维码、引导、终端如何选择',
|
|
1941
|
+
titleEn: 'Choosing watermark, QR code, tour, or terminal',
|
|
1942
|
+
question: '这是防泄露水印、扫码链接、新功能引导,还是日志终端?',
|
|
1943
|
+
questionEn: 'Is this a leak-prevention watermark, a scannable link, a feature tour, or a log terminal?',
|
|
1944
|
+
keywords: [
|
|
1945
|
+
'水印',
|
|
1946
|
+
'二维码',
|
|
1947
|
+
'引导',
|
|
1948
|
+
'终端',
|
|
1949
|
+
'watermark',
|
|
1950
|
+
'qrcode',
|
|
1951
|
+
'tour',
|
|
1952
|
+
'terminal',
|
|
1953
|
+
'防泄露',
|
|
1954
|
+
'扫码',
|
|
1955
|
+
'新功能',
|
|
1956
|
+
],
|
|
1957
|
+
options: [
|
|
1958
|
+
{
|
|
1959
|
+
component: 'MWatermark',
|
|
1960
|
+
when: ['页面或区域需要防泄露水印', '内部系统敏感信息保护'],
|
|
1961
|
+
whenEn: ['A page or region needs a leak-prevention watermark', 'Sensitive info protection in internal systems'],
|
|
1962
|
+
avoidWhen: ['公开营销页面', '水印会遮挡核心内容'],
|
|
1963
|
+
avoidWhenEn: ['Public marketing pages', 'The watermark would obscure core content'],
|
|
1964
|
+
recipe: recipe([
|
|
1965
|
+
['content 单行或多行文字水印', 'content for single or multi-line text'],
|
|
1966
|
+
['image 图片水印(logo)', 'image for a logo watermark'],
|
|
1967
|
+
['rotate / gap / offset 控制布局', 'rotate / gap / offset for layout'],
|
|
1968
|
+
['inherit 子元素继承水印', 'inherit lets children inherit the watermark'],
|
|
1969
|
+
]),
|
|
1970
|
+
...anti([
|
|
1971
|
+
['公开页面加水印 → 去掉', 'Watermark on a public page → remove it'],
|
|
1972
|
+
['核心内容被水印遮挡 → 调低透明度或局部使用', 'Core content obscured → lower opacity or scope it'],
|
|
1973
|
+
]),
|
|
1974
|
+
},
|
|
1975
|
+
{
|
|
1976
|
+
component: 'MQRCode',
|
|
1977
|
+
when: ['生成扫码链接 / 分享码', '需要展示二维码状态(过期 / 加载)'],
|
|
1978
|
+
whenEn: ['Generating scannable links / share codes', 'QR code status display is needed (expired / loading)'],
|
|
1979
|
+
avoidWhen: ['需要扫码识别(那是摄像头功能)', '需要条形码'],
|
|
1980
|
+
avoidWhenEn: ['Scanning recognition is needed (that is camera work)', 'A barcode is needed'],
|
|
1981
|
+
recipe: recipe([
|
|
1982
|
+
['value 二维码内容(URL / 文本)', 'value for the QR content (URL / text)'],
|
|
1983
|
+
['errorLevel="H" 高容错(允许 logo 覆盖)', 'errorLevel="H" for high fault tolerance (allows logo overlay)'],
|
|
1984
|
+
['icon 中心 logo', 'icon for a center logo'],
|
|
1985
|
+
['status="expired" 过期遮罩 + @refresh 刷新', 'status="expired" for an expired mask + @refresh'],
|
|
1986
|
+
]),
|
|
1987
|
+
...anti([
|
|
1988
|
+
['需要条形码用 MQRCode → 专用条形码库', 'Barcode via MQRCode → use a dedicated barcode library'],
|
|
1989
|
+
]),
|
|
1990
|
+
},
|
|
1991
|
+
{
|
|
1992
|
+
component: 'MTour',
|
|
1993
|
+
when: ['新功能引导 / 首次使用教学', '需要高亮页面元素并解释'],
|
|
1994
|
+
whenEn: ['New feature onboarding / first-use tutorials', 'Highlighting page elements with explanations'],
|
|
1995
|
+
avoidWhen: ['常规帮助文档', '用户已熟悉的功能'],
|
|
1996
|
+
avoidWhenEn: ['Regular help documentation', 'Features the user already knows'],
|
|
1997
|
+
recipe: recipe([
|
|
1998
|
+
['steps 配置每步标题 / 描述 / target 元素', 'steps configures title / description / target per step'],
|
|
1999
|
+
['v-model:open 控制显示;v-model:current 当前步骤', 'v-model:open for visibility; v-model:current for the step'],
|
|
2000
|
+
['mask 遮罩背景突出聚焦元素', 'mask dims the background to spotlight the target'],
|
|
2001
|
+
['@finish 完成引导;@close 提前关闭', '@finish on completion; @close on early exit'],
|
|
2002
|
+
], undefined, [['@change 步骤切换', '@change on step switch']]),
|
|
2003
|
+
...anti([
|
|
2004
|
+
['常规帮助用 MTour → 文档 / Tooltip', 'Regular help via MTour → docs / Tooltip'],
|
|
2005
|
+
['步骤过多(>5)→ 拆分或改为文档', 'Too many steps (>5) → split or use docs'],
|
|
2006
|
+
]),
|
|
2007
|
+
},
|
|
2008
|
+
{
|
|
2009
|
+
component: 'MTerminal',
|
|
2010
|
+
when: ['实时日志展示 / 命令执行回显', '需要模拟终端外观'],
|
|
2011
|
+
whenEn: ['Real-time log display / command echo', 'A terminal look is needed'],
|
|
2012
|
+
avoidWhen: ['普通代码展示(用 CodePreview / pre)', '需要复杂文本编辑'],
|
|
2013
|
+
avoidWhenEn: ['Plain code display (use CodePreview / pre)', 'Complex text editing is needed'],
|
|
2014
|
+
recipe: recipe([
|
|
2015
|
+
['content 日志文本;支持 ANSI 颜色', 'content for log text; ANSI colors supported'],
|
|
2016
|
+
['readonly 纯展示;非 readonly 可输入命令', 'readonly for display-only; otherwise commands can be entered'],
|
|
2017
|
+
['@command 执行自定义命令处理', '@command for custom command handling'],
|
|
2018
|
+
]),
|
|
2019
|
+
...anti([
|
|
2020
|
+
['普通代码块用 MTerminal → pre / code', 'Plain code block via MTerminal → pre / code'],
|
|
2021
|
+
['复杂编辑用 MTerminal → Monaco / CodeMirror', 'Complex editing via MTerminal → Monaco / CodeMirror'],
|
|
2022
|
+
]),
|
|
2023
|
+
},
|
|
2024
|
+
],
|
|
2025
|
+
},
|
|
2026
|
+
{
|
|
2027
|
+
id: 'form-label-choice',
|
|
2028
|
+
title: '表单标签样式如何选择',
|
|
2029
|
+
titleEn: 'Choosing form label styles',
|
|
2030
|
+
question: '这是传统上方标签、紧凑浮动标签,还是无边框内嵌标签?',
|
|
2031
|
+
questionEn: 'Is this a traditional top label, a compact floating label, or a borderless inline label?',
|
|
2032
|
+
keywords: [
|
|
2033
|
+
'标签',
|
|
2034
|
+
'浮动标签',
|
|
2035
|
+
'floatlabel',
|
|
2036
|
+
'label',
|
|
2037
|
+
'表单布局',
|
|
2038
|
+
'紧凑',
|
|
2039
|
+
'inplace',
|
|
2040
|
+
],
|
|
2041
|
+
options: [
|
|
2042
|
+
{
|
|
2043
|
+
component: 'MFloatLabel',
|
|
2044
|
+
when: ['空间紧张的现代表单', '需要输入框 label 浮动效果'],
|
|
2045
|
+
whenEn: ['Space-constrained modern forms', 'A floating label effect on inputs is needed'],
|
|
2046
|
+
avoidWhen: ['传统后台表单(label 在上更清晰)', '需要帮助文本或错误提示常驻'],
|
|
2047
|
+
avoidWhenEn: ['Traditional admin forms (top label is clearer)', 'Help text or error messages need to persist'],
|
|
2048
|
+
recipe: recipe([
|
|
2049
|
+
['label 属性设置浮动标签文案', 'label prop sets the floating label copy'],
|
|
2050
|
+
['包裹 MInput / MSelect 等控件', 'Wraps MInput / MSelect etc.'],
|
|
2051
|
+
['与 variant="filled" / "outlined" 控件配合', 'Pairs with variant="filled" / "outlined" controls'],
|
|
2052
|
+
]),
|
|
2053
|
+
...anti([
|
|
2054
|
+
['传统后台表单硬套 MFloatLabel → 默认 label', 'Traditional admin form with MFloatLabel → default label'],
|
|
2055
|
+
['需要常驻帮助文本仍用 MFloatLabel → 传统 label + helpText', 'Persistent help text with MFloatLabel → traditional label + helpText'],
|
|
2056
|
+
]),
|
|
2057
|
+
},
|
|
2058
|
+
{
|
|
2059
|
+
component: 'MInplace',
|
|
2060
|
+
when: ['详情页点击编辑单字段', '浏览模式为主,编辑是例外'],
|
|
2061
|
+
whenEn: ['Click-to-edit single fields on detail pages', 'Read mode is primary; edit is the exception'],
|
|
2062
|
+
avoidWhen: ['表单页批量编辑', '需要校验的必填字段'],
|
|
2063
|
+
avoidWhenEn: ['Batch editing on a form page', 'Required fields needing validation'],
|
|
2064
|
+
recipe: recipe([
|
|
2065
|
+
['display 插槽展示态;content 插槽编辑态', 'display slot for read mode; content slot for edit mode'],
|
|
2066
|
+
['closable 编辑态显示关闭按钮', 'closable shows a close button in edit mode'],
|
|
2067
|
+
['@open / @close 控制编辑态切换', '@open / @close for edit mode toggle'],
|
|
2068
|
+
]),
|
|
2069
|
+
...anti([
|
|
2070
|
+
['表单页批量编辑用 MInplace → MForm', 'Batch editing via MInplace → MForm'],
|
|
2071
|
+
['必填校验字段用 MInplace → MFormItem + errorMessage', 'Required validated field via MInplace → MFormItem + errorMessage'],
|
|
2072
|
+
]),
|
|
2073
|
+
},
|
|
2074
|
+
],
|
|
2075
|
+
},
|
|
2076
|
+
{
|
|
2077
|
+
id: 'specialized-input-choice',
|
|
2078
|
+
title: '旋钮、进度输入如何选择',
|
|
2079
|
+
titleEn: 'Choosing knob, meter, or progress inputs',
|
|
2080
|
+
question: '这是仪表盘式调节、多段进度展示,还是确定进度条?',
|
|
2081
|
+
questionEn: 'Is this a dial-style adjustment, a multi-segment progress display, or a determinate progress bar?',
|
|
2082
|
+
keywords: [
|
|
2083
|
+
'旋钮',
|
|
2084
|
+
'仪表盘',
|
|
2085
|
+
'进度',
|
|
2086
|
+
'knob',
|
|
2087
|
+
'meter',
|
|
2088
|
+
'progress',
|
|
2089
|
+
'调节',
|
|
2090
|
+
'阈值',
|
|
2091
|
+
],
|
|
2092
|
+
options: [
|
|
2093
|
+
{
|
|
2094
|
+
component: 'MKnob',
|
|
2095
|
+
when: ['仪表盘式数值调节(阈值 / 音量 / 参数)', '视觉化调节比精确输入更重要'],
|
|
2096
|
+
whenEn: ['Dial-style value adjustment (threshold / volume / params)', 'Visual adjustment matters more than exact input'],
|
|
2097
|
+
avoidWhen: ['需要精确数值输入', '范围选择'],
|
|
2098
|
+
avoidWhenEn: ['Exact numeric input is required', 'A range selection is needed'],
|
|
2099
|
+
recipe: recipe([
|
|
2100
|
+
['v-model 数值;min / max / step 控制范围', 'v-model number; min / max / step for the range'],
|
|
2101
|
+
['diameter 控制表盘大小', 'diameter for the dial size'],
|
|
2102
|
+
['valueTemplate="{value}%" 自定义显示格式', 'valueTemplate="{value}%" for custom display'],
|
|
2103
|
+
['ariaLabel 提供可访问名称', 'ariaLabel for the accessible name'],
|
|
2104
|
+
]),
|
|
2105
|
+
...anti([
|
|
2106
|
+
['精确数值输入用 MKnob → MInputNumber', 'Exact numeric input via MKnob → MInputNumber'],
|
|
2107
|
+
['范围选择用 MKnob → MSlider', 'Range selection via MKnob → MSlider'],
|
|
2108
|
+
]),
|
|
2109
|
+
},
|
|
2110
|
+
{
|
|
2111
|
+
component: 'MMeterGroup',
|
|
2112
|
+
when: ['多段进度展示(磁盘分区 / 预算分配)', '需要图例 + 百分比标签'],
|
|
2113
|
+
whenEn: ['Multi-segment progress display (disk partitions / budget allocation)', 'Legend + percentage labels are needed'],
|
|
2114
|
+
avoidWhen: ['单段进度', '需要交互调节'],
|
|
2115
|
+
avoidWhenEn: ['Single-segment progress', 'Interactive adjustment is needed'],
|
|
2116
|
+
recipe: recipe([
|
|
2117
|
+
['value 数组配置各段(label / value / color)', 'value array configures segments (label / value / color)'],
|
|
2118
|
+
['labelTemplate 自定义标签格式', 'labelTemplate for custom label format'],
|
|
2119
|
+
['max 总量(默认 100)', 'max for the total (default 100)'],
|
|
2120
|
+
]),
|
|
2121
|
+
...anti([
|
|
2122
|
+
['单段进度用 MMeterGroup → MProgressBar', 'Single-segment progress via MMeterGroup → MProgressBar'],
|
|
2123
|
+
['交互调节用 MMeterGroup → MSlider / MKnob', 'Interactive adjustment via MMeterGroup → MSlider / MKnob'],
|
|
2124
|
+
]),
|
|
2125
|
+
},
|
|
2126
|
+
],
|
|
2127
|
+
},
|
|
2128
|
+
{
|
|
2129
|
+
id: 'segmented-tabs-choice',
|
|
2130
|
+
title: '选项切换控件如何选择',
|
|
2131
|
+
titleEn: 'Choosing segmented, tabs, or select button',
|
|
2132
|
+
question: '这是视图/状态切换、内容面板切换,还是表单内选择?',
|
|
2133
|
+
questionEn: 'Is this a view/state switch, a content panel switch, or a form selection?',
|
|
2134
|
+
keywords: [
|
|
2135
|
+
'切换',
|
|
2136
|
+
'选项',
|
|
2137
|
+
'segmented',
|
|
2138
|
+
'tabs',
|
|
2139
|
+
'selectbutton',
|
|
2140
|
+
'视图切换',
|
|
2141
|
+
'状态切换',
|
|
2142
|
+
],
|
|
2143
|
+
options: [
|
|
2144
|
+
{
|
|
2145
|
+
component: 'MSegmented',
|
|
2146
|
+
when: ['选项少(≤5)且需要全部露出', '切换视图 / 状态 / 密度等即时生效'],
|
|
2147
|
+
whenEn: ['Few options (≤5) that should all stay visible', 'Switching views / states / density with instant effect'],
|
|
2148
|
+
avoidWhen: ['选项很多需要滚动', '需要表单提交值'],
|
|
2149
|
+
avoidWhenEn: ['Many options need scrolling', 'A form-submitted value is needed'],
|
|
2150
|
+
recipe: recipe([
|
|
2151
|
+
['v-model 绑定当前值', 'v-model for the current value'],
|
|
2152
|
+
['options 传字符串数组或 { label, value, icon }', 'options as strings or { label, value, icon } objects'],
|
|
2153
|
+
['block 占满容器宽度', 'block for full container width'],
|
|
2154
|
+
['shape="round" 圆角药丸外观', 'shape="round" for pill corners'],
|
|
2155
|
+
]),
|
|
2156
|
+
...anti([
|
|
2157
|
+
['内容面板切换用 MSegmented → MTabs', 'Content panel switch via MSegmented → MTabs'],
|
|
2158
|
+
['表单提交值用 MSegmented → MSelectButton', 'Form value via MSegmented → MSelectButton'],
|
|
2159
|
+
]),
|
|
2160
|
+
},
|
|
2161
|
+
{
|
|
2162
|
+
component: 'MTabs',
|
|
2163
|
+
when: ['多个内容面板切换显示', '标签数量多时可滚动'],
|
|
2164
|
+
whenEn: ['Switching between multiple content panels', 'Many tabs with scrolling support'],
|
|
2165
|
+
avoidWhen: ['选项是表单值', '需要手风琴折叠'],
|
|
2166
|
+
avoidWhenEn: ['Options are form values', 'Accordion expand/collapse is needed'],
|
|
2167
|
+
recipe: recipe([
|
|
2168
|
+
['v-model:value 控制当前标签', 'v-model:value for the active tab'],
|
|
2169
|
+
['closable 可关闭标签', 'closable for dismissible tabs'],
|
|
2170
|
+
['与路由结合用 to 或 @tab-click', 'Use to or @tab-click for routes'],
|
|
2171
|
+
]),
|
|
2172
|
+
...anti([
|
|
2173
|
+
['表单选项用 MTabs → MSelectButton', 'Form options via MTabs → MSelectButton'],
|
|
2174
|
+
['视图状态切换用 MTabs → MSegmented', 'View state switch via MTabs → MSegmented'],
|
|
2175
|
+
]),
|
|
2176
|
+
},
|
|
2177
|
+
{
|
|
2178
|
+
component: 'MSelectButton',
|
|
2179
|
+
when: ['表单内选择(需要提交值)', '选项少且需要全部露出'],
|
|
2180
|
+
whenEn: ['Form selection (value to be submitted)', 'Few options all visible'],
|
|
2181
|
+
avoidWhen: ['选项超过约 5 个', '切换视图状态'],
|
|
2182
|
+
avoidWhenEn: ['More than about 5 options', 'Switching view states'],
|
|
2183
|
+
recipe: recipe([
|
|
2184
|
+
['v-model 绑定选中值', 'v-model for the selected value'],
|
|
2185
|
+
[':options 选项数组', ':options array'],
|
|
2186
|
+
['与 MFormItem 一起用,参与表单校验', 'Use with MFormItem for form validation'],
|
|
2187
|
+
]),
|
|
2188
|
+
...anti([
|
|
2189
|
+
['>5 个选项仍用 MSelectButton → MSelect', '>5 options still MSelectButton → MSelect'],
|
|
2190
|
+
['视图切换用 MSelectButton → MSegmented', 'View switch via MSelectButton → MSegmented'],
|
|
2191
|
+
]),
|
|
2192
|
+
},
|
|
2193
|
+
],
|
|
2194
|
+
},
|
|
2195
|
+
{
|
|
2196
|
+
id: 'split-action-choice',
|
|
2197
|
+
title: '主操作+更多操作如何选择',
|
|
2198
|
+
titleEn: 'Choosing split button or button + dropdown',
|
|
2199
|
+
question: '这是一个主操作配更多操作,还是完全自定义的按钮组?',
|
|
2200
|
+
questionEn: 'Is this one primary action with more options, or a fully custom button group?',
|
|
2201
|
+
keywords: [
|
|
2202
|
+
'主操作',
|
|
2203
|
+
'更多操作',
|
|
2204
|
+
'splitbutton',
|
|
2205
|
+
'dropdown',
|
|
2206
|
+
'button group',
|
|
2207
|
+
'保存并继续',
|
|
2208
|
+
],
|
|
2209
|
+
options: [
|
|
2210
|
+
{
|
|
2211
|
+
component: 'MSplitButton',
|
|
2212
|
+
when: ['一个高频主操作 + 低频相关操作', '如 保存 / 保存并继续 / 另存为'],
|
|
2213
|
+
whenEn: ['One frequent primary action + related低频 actions', 'e.g. Save / Save and continue / Save as'],
|
|
2214
|
+
avoidWhen: ['操作之间无主次关系', '需要完全自定义触发器'],
|
|
2215
|
+
avoidWhenEn: ['Actions have no clear hierarchy', 'A fully custom trigger is needed'],
|
|
2216
|
+
recipe: recipe([
|
|
2217
|
+
['label 主操作文案;model 下拉操作项', 'label for primary action; model for dropdown items'],
|
|
2218
|
+
['主按钮 @click 执行主操作', 'Primary button @click for the main action'],
|
|
2219
|
+
['@command 执行下拉操作', '@command for dropdown actions'],
|
|
2220
|
+
['color / variant / size 与 MButton 一致', 'color / variant / size same as MButton'],
|
|
2221
|
+
], undefined, [['@command(item) 下拉操作回调', '@command(item) for dropdown action']]),
|
|
2222
|
+
...anti([
|
|
2223
|
+
['无主次关系操作用 MSplitButton → MButtonGroup / MSpace', 'No hierarchy via MSplitButton → MButtonGroup / MSpace'],
|
|
2224
|
+
['完全自定义触发器用 MSplitButton → MButton + MDropdown', 'Fully custom trigger via MSplitButton → MButton + MDropdown'],
|
|
2225
|
+
]),
|
|
2226
|
+
},
|
|
2227
|
+
{
|
|
2228
|
+
component: 'MButton + MDropdown',
|
|
2229
|
+
when: ['需要完全自定义按钮组布局', '触发器不是标准按钮(图标 / 头像)'],
|
|
2230
|
+
whenEn: ['Fully custom button group layout is needed', 'The trigger is not a standard button (icon / avatar)'],
|
|
2231
|
+
avoidWhen: ['标准主操作+更多操作模式'],
|
|
2232
|
+
avoidWhenEn: ['Standard primary + more actions pattern'],
|
|
2233
|
+
recipe: recipe([
|
|
2234
|
+
['MButton 放主操作;MDropdown 放更多操作', 'MButton for primary; MDropdown for more actions'],
|
|
2235
|
+
['MDropdown 触发器放默认插槽', 'MDropdown trigger in default slot'],
|
|
2236
|
+
['危险操作走 MConfirmDialog 确认', 'Destructive actions via MConfirmDialog'],
|
|
2237
|
+
]),
|
|
2238
|
+
...anti([
|
|
2239
|
+
['标准主操作+更多用 MButton + MDropdown → MSplitButton', 'Standard primary+more via MButton + MDropdown → MSplitButton'],
|
|
2240
|
+
]),
|
|
2241
|
+
},
|
|
2242
|
+
],
|
|
2243
|
+
},
|
|
2244
|
+
{
|
|
2245
|
+
id: 'detail-display-choice',
|
|
2246
|
+
title: '详情数据展示如何选择',
|
|
2247
|
+
titleEn: 'Choosing descriptions, table, or list for detail display',
|
|
2248
|
+
question: '这是键值对字段展示、行列数据比较,还是自由布局列表?',
|
|
2249
|
+
questionEn: 'Is this key-value field display, row-column data comparison, or a free-layout list?',
|
|
2250
|
+
keywords: [
|
|
2251
|
+
'详情',
|
|
2252
|
+
'键值对',
|
|
2253
|
+
'descriptions',
|
|
2254
|
+
'table',
|
|
2255
|
+
'list',
|
|
2256
|
+
'字段展示',
|
|
2257
|
+
'数据展示',
|
|
2258
|
+
],
|
|
2259
|
+
options: [
|
|
2260
|
+
{
|
|
2261
|
+
component: 'MDescriptions',
|
|
2262
|
+
when: ['详情页字段展示(label: value 键值对)', '需要 bordered / column 控制布局'],
|
|
2263
|
+
whenEn: ['Detail page field display (label: value pairs)', 'Need bordered / column layout control'],
|
|
2264
|
+
avoidWhen: ['需要排序 / 筛选 / 行操作', '需要自由布局列表项'],
|
|
2265
|
+
avoidWhenEn: ['Sorting / filtering / row actions are needed', 'Free-layout list items are needed'],
|
|
2266
|
+
recipe: recipe([
|
|
2267
|
+
['MDescriptions 包裹 MDescriptionsItem', 'MDescriptions wraps MDescriptionsItem'],
|
|
2268
|
+
['column 每行列数;bordered 显示边框', 'column for items per row; bordered for borders'],
|
|
2269
|
+
['layout="horizontal"(默认)或 "vertical"', 'layout="horizontal" (default) or "vertical"'],
|
|
2270
|
+
['#label 自定义标签内容', '#label for custom label content'],
|
|
2271
|
+
], [['MDescriptionsItem label 字段名;span 跨列', 'MDescriptionsItem label for field name; span for colspan']]),
|
|
2272
|
+
...anti([
|
|
2273
|
+
['需要排序筛选用 MDescriptions → MTable', 'Sorting/filtering via MDescriptions → MTable'],
|
|
2274
|
+
['自由布局列表用 MDescriptions → MList', 'Free-layout list via MDescriptions → MList'],
|
|
2275
|
+
]),
|
|
2276
|
+
},
|
|
2277
|
+
{
|
|
2278
|
+
component: 'MTable',
|
|
2279
|
+
when: ['行列数据比较多条记录', '需要排序 / 筛选 / 行操作'],
|
|
2280
|
+
whenEn: ['Comparing multiple records in rows and columns', 'Sorting / filtering / row actions are needed'],
|
|
2281
|
+
avoidWhen: ['单条记录的字段展示', '自由布局列表项'],
|
|
2282
|
+
avoidWhenEn: ['Single record field display', 'Free-layout list items'],
|
|
2283
|
+
recipe: recipe([
|
|
2284
|
+
['columns + rows 定义数据结构', 'columns + rows for data structure'],
|
|
2285
|
+
['#cell-{key} 自定义单元格', '#cell-{key} for custom cells'],
|
|
2286
|
+
['paginator 分页;selectionMode 行选择', 'paginator for paging; selectionMode for selection'],
|
|
2287
|
+
]),
|
|
2288
|
+
...anti([
|
|
2289
|
+
['单条记录字段展示用 MTable → MDescriptions', 'Single record fields via MTable → MDescriptions'],
|
|
2290
|
+
]),
|
|
2291
|
+
},
|
|
2292
|
+
{
|
|
2293
|
+
component: 'MList',
|
|
2294
|
+
when: ['自由布局列表项(头像+标题+描述+操作)', '需要分页或 grid 布局'],
|
|
2295
|
+
whenEn: ['Free-layout list items (avatar + title + description + actions)', 'Pagination or grid layout is needed'],
|
|
2296
|
+
avoidWhen: ['键值对字段展示', '需要排序筛选'],
|
|
2297
|
+
avoidWhenEn: ['Key-value field display', 'Sorting/filtering is needed'],
|
|
2298
|
+
recipe: recipe([
|
|
2299
|
+
['MList 包裹 MListItem;MListItemMeta 带头像标题描述', 'MList wraps MListItem; MListItemMeta for avatar/title/description'],
|
|
2300
|
+
['pagination 配置分页;grid 配置网格布局', 'pagination for paging; grid for grid layout'],
|
|
2301
|
+
['#actions 列表项操作按钮', '#actions for item action buttons'],
|
|
2302
|
+
], [['MListItem #actions 操作区', 'MListItem #actions for action area']]),
|
|
2303
|
+
...anti([
|
|
2304
|
+
['键值对字段展示用 MList → MDescriptions', 'Key-value fields via MList → MDescriptions'],
|
|
2305
|
+
]),
|
|
2306
|
+
},
|
|
2307
|
+
],
|
|
2308
|
+
},
|
|
2309
|
+
{
|
|
2310
|
+
id: 'collapse-container-choice',
|
|
2311
|
+
title: '折叠容器如何选择',
|
|
2312
|
+
titleEn: 'Choosing accordion, panel, or card',
|
|
2313
|
+
question: '这是多面板手风琴、单个可折叠区块,还是独立视觉表面?',
|
|
2314
|
+
questionEn: 'Is this a multi-panel accordion, a single collapsible section, or an independent visual surface?',
|
|
2315
|
+
keywords: [
|
|
2316
|
+
'折叠',
|
|
2317
|
+
'手风琴',
|
|
2318
|
+
'accordion',
|
|
2319
|
+
'panel',
|
|
2320
|
+
'card',
|
|
2321
|
+
'容器',
|
|
2322
|
+
],
|
|
2323
|
+
options: [
|
|
2324
|
+
{
|
|
2325
|
+
component: 'MAccordion',
|
|
2326
|
+
when: ['多面板手风琴展开/收起', 'FAQ / 设置分组 / 多段内容'],
|
|
2327
|
+
whenEn: ['Multi-panel accordion expand/collapse', 'FAQ / settings groups / multi-section content'],
|
|
2328
|
+
avoidWhen: ['单个可折叠区块', '不需要折叠的独立表面'],
|
|
2329
|
+
avoidWhenEn: ['A single collapsible section', 'An independent surface without collapse'],
|
|
2330
|
+
recipe: recipe([
|
|
2331
|
+
['tabs 配置每面板(value / header / disabled)', 'tabs configures each panel (value / header / disabled)'],
|
|
2332
|
+
['v-model 控制展开的面板 key(数组 multiple)', 'v-model for expanded panel keys (array for multiple)'],
|
|
2333
|
+
['multiple 允许多面板同时展开', 'multiple for multiple open panels'],
|
|
2334
|
+
], undefined, [['@update:modelValue 展开状态变化', '@update:modelValue on expand change']]),
|
|
2335
|
+
...anti([
|
|
2336
|
+
['单个折叠区块用 MAccordion → MPanel', 'Single collapsible section via MAccordion → MPanel'],
|
|
2337
|
+
['不需要折叠用 MAccordion → MCard', 'No collapse needed via MAccordion → MCard'],
|
|
2338
|
+
]),
|
|
2339
|
+
},
|
|
2340
|
+
{
|
|
2341
|
+
component: 'MPanel',
|
|
2342
|
+
when: ['单个可折叠区块', '需要标题 + 展开/收起'],
|
|
2343
|
+
whenEn: ['A single collapsible section', 'Need header + expand/collapse'],
|
|
2344
|
+
avoidWhen: ['多面板手风琴', '独立视觉表面'],
|
|
2345
|
+
avoidWhenEn: ['Multi-panel accordion', 'Independent visual surface'],
|
|
2346
|
+
recipe: recipe([
|
|
2347
|
+
['header 面板标题', 'header for the panel title'],
|
|
2348
|
+
['toggleable 开启折叠功能', 'toggleable to enable collapse'],
|
|
2349
|
+
['v-model:collapsed 控制折叠状态', 'v-model:collapsed for collapse state'],
|
|
2350
|
+
], undefined, [['@update:collapsed 折叠状态变化', '@update:collapsed on collapse change']]),
|
|
2351
|
+
...anti([
|
|
2352
|
+
['多面板手风琴用 MPanel → MAccordion', 'Multi-panel via MPanel → MAccordion'],
|
|
2353
|
+
['独立表面用 MPanel → MCard', 'Independent surface via MPanel → MCard'],
|
|
2354
|
+
]),
|
|
2355
|
+
},
|
|
2356
|
+
{
|
|
2357
|
+
component: 'MCard',
|
|
2358
|
+
when: ['独立视觉表面(标题 / 内容 / 页脚)', '不一定需要折叠'],
|
|
2359
|
+
whenEn: ['Independent visual surface (title / content / footer)', 'Collapse is not necessarily needed'],
|
|
2360
|
+
avoidWhen: ['多面板手风琴', '单个可折叠区块'],
|
|
2361
|
+
avoidWhenEn: ['Multi-panel accordion', 'A single collapsible section'],
|
|
2362
|
+
recipe: recipe([
|
|
2363
|
+
['title / subtitle 头部信息', 'title / subtitle for header info'],
|
|
2364
|
+
['shadow="always" / "hover" / "never" 控制阴影', 'shadow="always" / "hover" / "never" for shadow'],
|
|
2365
|
+
['#header / #footer / #title 自定义区域', '#header / #footer / #title for custom areas'],
|
|
2366
|
+
]),
|
|
2367
|
+
...anti([
|
|
2368
|
+
['需要折叠用 MCard → MPanel / MAccordion', 'Need collapse via MCard → MPanel / MAccordion'],
|
|
2369
|
+
]),
|
|
2370
|
+
},
|
|
2371
|
+
],
|
|
2372
|
+
},
|
|
2373
|
+
{
|
|
2374
|
+
id: 'sort-transfer-choice',
|
|
2375
|
+
title: '排序和穿梭选择如何实现',
|
|
2376
|
+
titleEn: 'Choosing order list, pick list, or plain list',
|
|
2377
|
+
question: '这是单列表拖拽排序、左右穿梭选择,还是纯展示列表?',
|
|
2378
|
+
questionEn: 'Is this drag-to-reorder in one list, left-right transfer selection, or a plain display list?',
|
|
2379
|
+
keywords: [
|
|
2380
|
+
'排序',
|
|
2381
|
+
'穿梭',
|
|
2382
|
+
'orderlist',
|
|
2383
|
+
'picklist',
|
|
2384
|
+
'list',
|
|
2385
|
+
'拖拽',
|
|
2386
|
+
'选择',
|
|
2387
|
+
],
|
|
2388
|
+
options: [
|
|
2389
|
+
{
|
|
2390
|
+
component: 'MOrderList',
|
|
2391
|
+
when: ['单列表拖拽排序', '需要上下移动 / 置顶 / 置底'],
|
|
2392
|
+
whenEn: ['Drag-to-reorder in a single list', 'Move up/down / top/bottom actions are needed'],
|
|
2393
|
+
avoidWhen: ['左右穿梭选择', '纯展示列表'],
|
|
2394
|
+
avoidWhenEn: ['Left-right transfer selection', 'Plain display list'],
|
|
2395
|
+
recipe: recipe([
|
|
2396
|
+
['v-model 绑定排序后的数组', 'v-model for the reordered array'],
|
|
2397
|
+
['dragdrop 开启拖拽(默认 true)', 'dragdrop to enable drag (default true)'],
|
|
2398
|
+
['v-model:selection 控制选中项', 'v-model:selection for the selected item'],
|
|
2399
|
+
['@reorder 排序后回调(含 from / to / item)', '@reorder after reorder (with from / to / item)'],
|
|
2400
|
+
], undefined, [['@reorder 排序完成回调', '@reorder after reorder']]),
|
|
2401
|
+
...anti([
|
|
2402
|
+
['左右穿梭用 MOrderList → MPickList', 'Left-right transfer via MOrderList → MPickList'],
|
|
2403
|
+
['纯展示用 MOrderList → MList', 'Plain display via MOrderList → MList'],
|
|
2404
|
+
]),
|
|
2405
|
+
},
|
|
2406
|
+
{
|
|
2407
|
+
component: 'MPickList',
|
|
2408
|
+
when: ['左右穿梭选择(从源到目标)', '如 权限分配 / 字段选择'],
|
|
2409
|
+
whenEn: ['Left-right transfer selection (source to target)', 'e.g. permission assignment / field selection'],
|
|
2410
|
+
avoidWhen: ['单列表排序', '纯展示列表'],
|
|
2411
|
+
avoidWhenEn: ['Single list reorder', 'Plain display list'],
|
|
2412
|
+
recipe: recipe([
|
|
2413
|
+
['v-model:source 源列表;v-model:target 目标列表', 'v-model:source for source; v-model:target for target'],
|
|
2414
|
+
['sourceHeader / targetHeader 列表标题', 'sourceHeader / targetHeader for list titles'],
|
|
2415
|
+
['dataKey 唯一标识字段', 'dataKey for the unique key field'],
|
|
2416
|
+
]),
|
|
2417
|
+
...anti([
|
|
2418
|
+
['单列表排序用 MPickList → MOrderList', 'Single list reorder via MPickList → MOrderList'],
|
|
2419
|
+
['纯展示用 MPickList → MList', 'Plain display via MPickList → MList'],
|
|
2420
|
+
]),
|
|
2421
|
+
},
|
|
2422
|
+
{
|
|
2423
|
+
component: 'MList',
|
|
2424
|
+
when: ['纯展示列表(不支持排序 / 穿梭)', '需要分页或 grid 布局'],
|
|
2425
|
+
whenEn: ['Plain display list (no reorder / transfer)', 'Pagination or grid layout is needed'],
|
|
2426
|
+
avoidWhen: ['需要排序', '需要穿梭选择'],
|
|
2427
|
+
avoidWhenEn: ['Reorder is needed', 'Transfer selection is needed'],
|
|
2428
|
+
recipe: recipe([
|
|
2429
|
+
['items / dataSource 数据数组', 'items / dataSource for the data array'],
|
|
2430
|
+
['MListItem + MListItemMeta 自定义布局', 'MListItem + MListItemMeta for custom layout'],
|
|
2431
|
+
['pagination 分页;grid 网格布局', 'pagination for paging; grid for grid layout'],
|
|
2432
|
+
]),
|
|
2433
|
+
...anti([
|
|
2434
|
+
['需要排序用 MList → MOrderList', 'Need reorder via MList → MOrderList'],
|
|
2435
|
+
['需要穿梭用 MList → MPickList', 'Need transfer via MList → MPickList'],
|
|
2436
|
+
]),
|
|
2437
|
+
},
|
|
2438
|
+
],
|
|
2439
|
+
},
|
|
2440
|
+
{
|
|
2441
|
+
id: 'upload-mode-choice',
|
|
2442
|
+
title: '文件上传模式如何选择',
|
|
2443
|
+
titleEn: 'Choosing file upload mode',
|
|
2444
|
+
question: '这是选择即上传、拖拽+预览+进度,还是完全自定义上传?',
|
|
2445
|
+
questionEn: 'Is this select-and-upload, drag+preview+progress, or fully custom upload?',
|
|
2446
|
+
keywords: [
|
|
2447
|
+
'上传',
|
|
2448
|
+
'文件',
|
|
2449
|
+
'fileupload',
|
|
2450
|
+
'拖拽',
|
|
2451
|
+
'预览',
|
|
2452
|
+
'进度',
|
|
2453
|
+
'basic',
|
|
2454
|
+
'advanced',
|
|
2455
|
+
],
|
|
2456
|
+
options: [
|
|
2457
|
+
{
|
|
2458
|
+
component: 'MFileUpload mode="basic"',
|
|
2459
|
+
when: ['选择文件后立即上传', '不需要预览和进度展示'],
|
|
2460
|
+
whenEn: ['Upload immediately after file selection', 'No preview or progress display needed'],
|
|
2461
|
+
avoidWhen: ['需要拖拽上传', '需要图片预览'],
|
|
2462
|
+
avoidWhenEn: ['Drag-and-drop upload is needed', 'Image preview is needed'],
|
|
2463
|
+
recipe: recipe([
|
|
2464
|
+
['action 上传地址;autoUpload 自动上传', 'action for upload URL; autoUpload for auto upload'],
|
|
2465
|
+
['accept 限制文件类型;maxSize 限制大小', 'accept for file types; maxSize for size limit'],
|
|
2466
|
+
['@success / @error 上传结果回调', '@success / @error for upload result'],
|
|
2467
|
+
], undefined, [['@success 上传成功', '@success on upload success']]),
|
|
2468
|
+
...anti([
|
|
2469
|
+
['需要拖拽预览用 basic → mode="advanced"', 'Need drag/preview via basic → mode="advanced"'],
|
|
2470
|
+
]),
|
|
2471
|
+
},
|
|
2472
|
+
{
|
|
2473
|
+
component: 'MFileUpload mode="advanced"',
|
|
2474
|
+
when: ['拖拽上传 + 文件预览 + 进度展示', '图片上传需要缩略图'],
|
|
2475
|
+
whenEn: ['Drag-and-drop upload + file preview + progress', 'Image upload needs thumbnails'],
|
|
2476
|
+
avoidWhen: ['选择即上传的简单场景'],
|
|
2477
|
+
avoidWhenEn: ['Simple select-and-upload scenarios'],
|
|
2478
|
+
recipe: recipe([
|
|
2479
|
+
['drag 开启拖拽区域', 'drag to enable the drop zone'],
|
|
2480
|
+
['listType="picture-card" 图片缩略图卡片', 'listType="picture-card" for image thumbnails'],
|
|
2481
|
+
['v-model:file-list 绑定文件列表', 'v-model:file-list for the file list'],
|
|
2482
|
+
['autoUpload=false 手动控制;submit() 触发上传', 'autoUpload=false for manual control; submit() to upload'],
|
|
2483
|
+
], undefined, [
|
|
2484
|
+
['@progress 上传进度', '@progress for upload progress'],
|
|
2485
|
+
['@remove 移除文件', '@remove on file removal'],
|
|
2486
|
+
]),
|
|
2487
|
+
...anti([
|
|
2488
|
+
['简单选择即上传用 advanced → mode="basic"', 'Simple select-upload via advanced → mode="basic"'],
|
|
2489
|
+
['完全自定义上传 UI 用 advanced → httpRequest 自定义', 'Fully custom upload UI via advanced → httpRequest'],
|
|
2490
|
+
]),
|
|
2491
|
+
},
|
|
2492
|
+
],
|
|
2493
|
+
},
|
|
2494
|
+
{
|
|
2495
|
+
id: 'suggestion-input-choice',
|
|
2496
|
+
title: '带建议的输入如何选择',
|
|
2497
|
+
titleEn: 'Choosing autocomplete, mentions, or plain input',
|
|
2498
|
+
question: '这是自由输入+下拉建议、@人/#话题提及,还是纯文本输入?',
|
|
2499
|
+
questionEn: 'Is this free input with dropdown suggestions, @mention/#topic tagging, or plain text input?',
|
|
2500
|
+
keywords: [
|
|
2501
|
+
'自动完成',
|
|
2502
|
+
'提及',
|
|
2503
|
+
'autocomplete',
|
|
2504
|
+
'mentions',
|
|
2505
|
+
'input',
|
|
2506
|
+
'建议',
|
|
2507
|
+
'@人',
|
|
2508
|
+
],
|
|
2509
|
+
options: [
|
|
2510
|
+
{
|
|
2511
|
+
component: 'MAutoComplete',
|
|
2512
|
+
when: ['自由输入 + 下拉建议(搜索历史 / 邮箱后缀)', '输入值本身也有意义'],
|
|
2513
|
+
whenEn: ['Free input with dropdown suggestions (search history / email suffixes)', 'The entered value is meaningful itself'],
|
|
2514
|
+
avoidWhen: ['只能从固定选项选', '需要 @人 / #话题'],
|
|
2515
|
+
avoidWhenEn: ['Selection must be from fixed options', '@mention / #topic is needed'],
|
|
2516
|
+
recipe: recipe([
|
|
2517
|
+
['v-model 绑定输入值', 'v-model for the input value'],
|
|
2518
|
+
['suggestions 建议数组(字符串或 { label, value })', 'suggestions array (strings or { label, value })'],
|
|
2519
|
+
['@complete 输入时拉取建议(支持远程)', '@complete to fetch suggestions on input (supports remote)'],
|
|
2520
|
+
['loading 建议加载中状态', 'loading for suggestion loading state'],
|
|
2521
|
+
], undefined, [['@complete 输入变化时拉取建议', '@complete to fetch suggestions on input']]),
|
|
2522
|
+
...anti([
|
|
2523
|
+
['固定选项用 MAutoComplete → MSelect', 'Fixed options via MAutoComplete → MSelect'],
|
|
2524
|
+
['@人 / #话题用 MAutoComplete → MMentions', '@mention/#topic via MAutoComplete → MMentions'],
|
|
2525
|
+
]),
|
|
2526
|
+
},
|
|
2527
|
+
{
|
|
2528
|
+
component: 'MMentions',
|
|
2529
|
+
when: ['@人 / #话题触发下拉选择', '社交 / 协作场景'],
|
|
2530
|
+
whenEn: ['@mention / #topic triggers dropdown selection', 'Social / collaboration scenarios'],
|
|
2531
|
+
avoidWhen: ['自由输入建议', '纯文本输入'],
|
|
2532
|
+
avoidWhenEn: ['Free input suggestions', 'Plain text input'],
|
|
2533
|
+
recipe: recipe([
|
|
2534
|
+
['v-model 绑定输入值', 'v-model for the input value'],
|
|
2535
|
+
['options 提及选项数组', 'options for mention candidates'],
|
|
2536
|
+
['prefix="@" 触发字符(可数组多个)', 'prefix="@" trigger character (array for multiple)'],
|
|
2537
|
+
['split 插入后追加字符(默认空格)', 'split for appended character after selection'],
|
|
2538
|
+
], undefined, [['@select 选中提及项', '@select on mention selection']]),
|
|
2539
|
+
...anti([
|
|
2540
|
+
['自由输入建议用 MMentions → MAutoComplete', 'Free suggestions via MMentions → MAutoComplete'],
|
|
2541
|
+
['纯文本输入用 MMentions → MInput', 'Plain text via MMentions → MInput'],
|
|
2542
|
+
]),
|
|
2543
|
+
},
|
|
2544
|
+
],
|
|
2545
|
+
},
|
|
2546
|
+
{
|
|
2547
|
+
id: 'number-input-choice',
|
|
2548
|
+
title: '数值输入方式如何选择',
|
|
2549
|
+
titleEn: 'Choosing number input, slider, or knob',
|
|
2550
|
+
question: '这是精确数值输入、范围调节,还是仪表盘式调节?',
|
|
2551
|
+
questionEn: 'Is this exact numeric input, range adjustment, or dial-style adjustment?',
|
|
2552
|
+
keywords: [
|
|
2553
|
+
'数值',
|
|
2554
|
+
'数字',
|
|
2555
|
+
'inputnumber',
|
|
2556
|
+
'slider',
|
|
2557
|
+
'knob',
|
|
2558
|
+
'精确',
|
|
2559
|
+
'范围',
|
|
2560
|
+
'调节',
|
|
2561
|
+
],
|
|
2562
|
+
options: [
|
|
2563
|
+
{
|
|
2564
|
+
component: 'MInputNumber',
|
|
2565
|
+
when: ['需要精确数值输入', '需要 min/max/step 约束'],
|
|
2566
|
+
whenEn: ['Exact numeric input is required', 'min/max/step constraints are needed'],
|
|
2567
|
+
avoidWhen: ['范围调节', '仪表盘式调节'],
|
|
2568
|
+
avoidWhenEn: ['Range adjustment', 'Dial-style adjustment'],
|
|
2569
|
+
recipe: recipe([
|
|
2570
|
+
['v-model 绑定数值', 'v-model for the number'],
|
|
2571
|
+
['min / max / step 控制范围步进', 'min / max / step for range and step'],
|
|
2572
|
+
['showButtons 显示加减按钮', 'showButtons for increment/decrement buttons'],
|
|
2573
|
+
['precision 小数位数', 'precision for decimal places'],
|
|
2574
|
+
]),
|
|
2575
|
+
...anti([
|
|
2576
|
+
['范围调节用 MInputNumber → MSlider', 'Range adjustment via MInputNumber → MSlider'],
|
|
2577
|
+
['仪表盘调节用 MInputNumber → MKnob', 'Dial adjustment via MInputNumber → MKnob'],
|
|
2578
|
+
]),
|
|
2579
|
+
},
|
|
2580
|
+
{
|
|
2581
|
+
component: 'MSlider',
|
|
2582
|
+
when: ['范围调节(音量 / 价格区间 / 阈值)', '视觉化比例比精确数值更重要'],
|
|
2583
|
+
whenEn: ['Range adjustment (volume / price range / threshold)', 'Visual proportion matters more than exact value'],
|
|
2584
|
+
avoidWhen: ['需要精确输入', '单点精确值'],
|
|
2585
|
+
avoidWhenEn: ['Exact input is required', 'A single exact value is needed'],
|
|
2586
|
+
recipe: recipe([
|
|
2587
|
+
['v-model 数值或 range 数组', 'v-model number or range array'],
|
|
2588
|
+
['min / max / step 控制范围', 'min / max / step for the range'],
|
|
2589
|
+
['marks 显示刻度标签', 'marks for tick labels'],
|
|
2590
|
+
['tooltip 拖拽时显示当前值', 'tooltip shows value while dragging'],
|
|
2591
|
+
]),
|
|
2592
|
+
...anti([
|
|
2593
|
+
['精确输入用 MSlider → MInputNumber', 'Exact input via MSlider → MInputNumber'],
|
|
2594
|
+
['仪表盘调节用 MSlider → MKnob', 'Dial adjustment via MSlider → MKnob'],
|
|
2595
|
+
]),
|
|
2596
|
+
},
|
|
2597
|
+
{
|
|
2598
|
+
component: 'MKnob',
|
|
2599
|
+
when: ['仪表盘式数值调节(阈值 / 参数)', '视觉化调节比精确输入更重要'],
|
|
2600
|
+
whenEn: ['Dial-style value adjustment (threshold / params)', 'Visual adjustment matters more than exact input'],
|
|
2601
|
+
avoidWhen: ['需要精确输入', '范围选择'],
|
|
2602
|
+
avoidWhenEn: ['Exact input is required', 'Range selection is needed'],
|
|
2603
|
+
recipe: recipe([
|
|
2604
|
+
['v-model 绑定数值', 'v-model for the number'],
|
|
2605
|
+
['min / max / step 控制范围', 'min / max / step for the range'],
|
|
2606
|
+
['diameter 控制表盘大小', 'diameter for the dial size'],
|
|
2607
|
+
['valueTemplate="{value}%" 自定义显示', 'valueTemplate="{value}%" for custom display'],
|
|
2608
|
+
]),
|
|
2609
|
+
...anti([
|
|
2610
|
+
['精确输入用 MKnob → MInputNumber', 'Exact input via MKnob → MInputNumber'],
|
|
2611
|
+
['范围选择用 MKnob → MSlider', 'Range selection via MKnob → MSlider'],
|
|
2612
|
+
]),
|
|
2613
|
+
},
|
|
2614
|
+
],
|
|
2615
|
+
},
|
|
2616
|
+
{
|
|
2617
|
+
id: 'large-data-choice',
|
|
2618
|
+
title: '大数据量列表渲染策略',
|
|
2619
|
+
titleEn: 'Choosing large data rendering strategy',
|
|
2620
|
+
question: '这是万级数据虚拟滚动,还是普通数据量的直接渲染?',
|
|
2621
|
+
questionEn: 'Is this virtual scrolling for 10k+ items, or direct rendering for normal data volume?',
|
|
2622
|
+
keywords: [
|
|
2623
|
+
'大数据',
|
|
2624
|
+
'虚拟滚动',
|
|
2625
|
+
'virtualscroller',
|
|
2626
|
+
'性能',
|
|
2627
|
+
'万级',
|
|
2628
|
+
'渲染优化',
|
|
2629
|
+
],
|
|
2630
|
+
options: [
|
|
2631
|
+
{
|
|
2632
|
+
component: 'MVirtualScroller',
|
|
2633
|
+
when: ['万级以上数据列表', '需要固定 itemSize 优化渲染'],
|
|
2634
|
+
whenEn: ['Lists with 10k+ items', 'Fixed itemSize for render optimization'],
|
|
2635
|
+
avoidWhen: ['数据量小(<1000)', '需要复杂动态高度'],
|
|
2636
|
+
avoidWhenEn: ['Small data volume (<1000)', 'Complex dynamic heights are needed'],
|
|
2637
|
+
recipe: recipe([
|
|
2638
|
+
['items 数据数组;itemSize 固定高度(px)', 'items for data; itemSize for fixed height (px)'],
|
|
2639
|
+
['height 可视区域高度', 'height for the viewport height'],
|
|
2640
|
+
['buffer 上下缓冲数量(减少白屏)', 'buffer for overscan items (reduces blank areas)'],
|
|
2641
|
+
['scrollToIndex 编程滚动到指定项', 'scrollToIndex for programmatic scrolling'],
|
|
2642
|
+
], [['#item 自定义列表项内容', '#item for custom item content']]),
|
|
2643
|
+
...anti([
|
|
2644
|
+
['小数据量用 MVirtualScroller → 普通 v-for / MList', 'Small data via MVirtualScroller → plain v-for / MList'],
|
|
2645
|
+
['动态高度列表用 MVirtualScroller → 需要固定 itemSize', 'Dynamic heights via MVirtualScroller → fixed itemSize required'],
|
|
2646
|
+
]),
|
|
2647
|
+
},
|
|
2648
|
+
{
|
|
2649
|
+
component: '普通 v-for / MList',
|
|
2650
|
+
when: ['数据量小(<1000)', '需要复杂动态高度布局'],
|
|
2651
|
+
whenEn: ['Small data volume (<1000)', 'Complex dynamic height layouts are needed'],
|
|
2652
|
+
avoidWhen: ['万级以上数据', '性能敏感场景'],
|
|
2653
|
+
avoidWhenEn: ['10k+ items', 'Performance-sensitive scenarios'],
|
|
2654
|
+
recipe: recipe([
|
|
2655
|
+
['MList items / dataSource 直接渲染', 'MList items / dataSource for direct rendering'],
|
|
2656
|
+
['v-for 自定义模板渲染', 'v-for for custom template rendering'],
|
|
2657
|
+
['无需 itemSize 约束', 'No itemSize constraint needed'],
|
|
2658
|
+
]),
|
|
2659
|
+
...anti([
|
|
2660
|
+
['万级数据用普通 v-for → MVirtualScroller', '10k+ items via plain v-for → MVirtualScroller'],
|
|
2661
|
+
]),
|
|
2662
|
+
},
|
|
2663
|
+
],
|
|
2664
|
+
},
|
|
1372
2665
|
];
|
|
1373
2666
|
export function findDecision(name) {
|
|
1374
2667
|
const key = name.trim().toLowerCase().replace(/[-_\s]/g, '');
|