npmapps 1.1.5 → 1.2.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.
Files changed (115) hide show
  1. package/app/wujie-wk/t.html +90 -0
  2. package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/components/EventCalendar/README.md +122 -5
  3. package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/components/EventCalendar/example.vue +203 -7
  4. package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/components/EventCalendar/index.vue +1124 -50
  5. package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/components/EventCalendar/mockData.js +126 -0
  6. package/app/wujie-wk/wujie-vue2-child/src/components/EventCalendar/recruitmentAdapter.js +51 -0
  7. package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/components/EventCalendar/utils.js +1 -0
  8. package/package.json +1 -1
  9. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/.eslintrc.js +0 -0
  10. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/README.md +0 -0
  11. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/babel.config.js +0 -0
  12. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/docs/superpowers/plans/2026-05-29-quill-editor.md +0 -0
  13. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/docs/superpowers/specs/2026-05-29-quill-editor-design.md +0 -0
  14. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/jsconfig.json +0 -0
  15. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/package-lock.json +0 -0
  16. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/package.json +0 -0
  17. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/postcss.config.js +0 -0
  18. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/public/favicon.ico +0 -0
  19. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/public/index.html +0 -0
  20. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/public/rili.jpg +0 -0
  21. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/public//344/270/200/351/224/256/351/273/221/346/232/227.html" +0 -0
  22. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/App.vue +0 -0
  23. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/assets/bpmn-camunda.jpg +0 -0
  24. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/assets/css/diagram.less +0 -0
  25. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/assets/icon/Icon.less +0 -0
  26. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/assets/icon/font/app-codes.css +0 -0
  27. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/assets/icon/font/app.eot +0 -0
  28. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/assets/icon/font/app.svg +0 -0
  29. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/assets/icon/font/app.ttf +0 -0
  30. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/assets/icon/font/app.woff +0 -0
  31. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/assets/icon/font/app.woff2 +0 -0
  32. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/assets/icon/font/config.json +0 -0
  33. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/assets/icon/font/source/raw/align-bottom-tool.svg +0 -0
  34. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/assets/icon/font/source/raw/align-horizontal-center-tool.svg +0 -0
  35. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/assets/icon/font/source/raw/align-left-tool.svg +0 -0
  36. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/assets/icon/font/source/raw/align-right-tool.svg +0 -0
  37. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/assets/icon/font/source/raw/align-top-tool.svg +0 -0
  38. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/assets/icon/font/source/raw/align-vertical-center-tool.svg +0 -0
  39. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/assets/icon/font/source/raw/distribute-horizontally-tool.svg +0 -0
  40. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/assets/icon/font/source/raw/distribute-vertically-tool.svg +0 -0
  41. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/assets/icon/font/source/raw/set-color-tool.svg +0 -0
  42. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/assets/icon/font/source/symbols/align-bottom-tool.svg +0 -0
  43. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/assets/icon/font/source/symbols/align-horizontal-center-tool.svg +0 -0
  44. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/assets/icon/font/source/symbols/align-left-tool.svg +0 -0
  45. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/assets/icon/font/source/symbols/align-right-tool.svg +0 -0
  46. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/assets/icon/font/source/symbols/align-top-tool.svg +0 -0
  47. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/assets/icon/font/source/symbols/align-vertical-center-tool.svg +0 -0
  48. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/assets/icon/font/source/symbols/distribute-horizontally-tool.svg +0 -0
  49. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/assets/icon/font/source/symbols/distribute-vertically-tool.svg +0 -0
  50. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/assets/icon/font/source/symbols/set-color-tool.svg +0 -0
  51. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/assets/logo.png +0 -0
  52. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/components/EllTable/README.md +0 -0
  53. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/components/EllTable/article.md +0 -0
  54. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/components/EllTable/index.js +0 -0
  55. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/components/EventCalendar/CalendarSkeleton.vue +0 -0
  56. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/components/EventCalendar/CodeBlock.vue +0 -0
  57. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/components/EventCalendar/MonthSelect.vue +0 -0
  58. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/components/FormulaEditor/FunctionSelector.vue +0 -0
  59. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/components/FormulaEditor/OperatorSelector.vue +0 -0
  60. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/components/FormulaEditor/ParameterSelector.vue +0 -0
  61. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/components/FormulaEditor/api.js +0 -0
  62. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/components/FormulaEditor/index.vue +0 -0
  63. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/components/HelloWorld.vue +0 -0
  64. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/components/PageHeader/index.vue +0 -0
  65. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/components/Splitter/README.md +0 -0
  66. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/components/Splitter/example.vue +0 -0
  67. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/components/Splitter/index.vue +0 -0
  68. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/components/diagram/ToolBar.vue +0 -0
  69. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/components/diagram/customTranslate/customTranslate.js +0 -0
  70. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/components/diagram/customTranslate/translationsGerman.js +0 -0
  71. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/components/diagram/index.vue +0 -0
  72. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/components/diagram/xmlData.js +0 -0
  73. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/directives/filldown.js +0 -0
  74. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/directives/filldownTable.js +0 -0
  75. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/main.js +0 -0
  76. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/router/index.js +0 -0
  77. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/store/index.js +0 -0
  78. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/utils/winBox.js +0 -0
  79. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/views/Extend/A.vue +0 -0
  80. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/views/Extend/B.vue +0 -0
  81. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/views/Extend/MagicalComponentsForELFormItem.vue +0 -0
  82. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/views/Extend/index.vue +0 -0
  83. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/views/Extend/tableMouseHorizontalWheel.vue +0 -0
  84. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/views/Home.vue +0 -0
  85. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/views/RouterJump.vue +0 -0
  86. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/views/calendar/index.vue +0 -0
  87. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/views/css.vue +0 -0
  88. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/views/cssComponents/EllipsisText.vue +0 -0
  89. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/views/cssComponents/HoverCard.vue +0 -0
  90. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/views/cssComponents/TableHover.vue +0 -0
  91. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/views/cssComponents/inputSlo.vue +0 -0
  92. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/views/cssComponents/tableFixed.vue +0 -0
  93. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/views/echarts/echart-dome.vue +0 -0
  94. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/views/echarts/index.vue +0 -0
  95. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/views/echarts/pei3d.vue +0 -0
  96. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/views/element/bpmn/index.vue +0 -0
  97. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/views/element/components/attendanceCycle/index.vue +0 -0
  98. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/views/element/components/attendanceGroup/index.vue +0 -0
  99. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/views/element/components/attendancePersonnel/index.vue +0 -0
  100. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/views/element/components/companyCalendar/index.vue +0 -0
  101. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/views/element/components/shift/index.vue +0 -0
  102. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/views/element/components/shiftRotationSystem/index.vue +0 -0
  103. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/views/element/elTableJsx/columnManagement.vue +0 -0
  104. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/views/element/elTableJsx/dialogInput.vue +0 -0
  105. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/views/element/elTableJsx/elTableJsx.vue +0 -0
  106. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/views/element/elTableJsx/formTable.vue +0 -0
  107. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/views/element/elTableJsx/index.vue +0 -0
  108. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/views/element/elTableJsx/simpleTable.vue +0 -0
  109. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/views/element/elTableJsx.zip +0 -0
  110. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/views/element/index.vue +0 -0
  111. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/views/element/quillEditor/README.md +0 -0
  112. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/views/element/quillEditor/example.vue +0 -0
  113. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/views/element/quillEditor/index.vue +0 -0
  114. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/src/views/element/quillEditor/toolbar.js +0 -0
  115. /package/app/{wujie-vue2-child → wujie-wk/wujie-vue2-child}/vue.config.js +0 -0
@@ -6,6 +6,179 @@
6
6
  @mouseenter="handleMouseEnter"
7
7
  @mouseleave="handleMouseLeave"
8
8
  >
9
+ <!-- 开发/业务配置工具栏:默认关闭;改完点「复制配置」,把 JSON 发给研发即可 -->
10
+ <section
11
+ v-if="showConfigToolbar"
12
+ class="ec__config"
13
+ aria-label="日历布局配置"
14
+ >
15
+ <div class="ec__config-head">
16
+ <div>
17
+ <strong class="ec__config-title">布局配置</strong>
18
+ <span class="ec__config-caption">调整后点「复制配置」,粘贴发给研发即可</span>
19
+ </div>
20
+ <div class="ec__config-actions">
21
+ <button
22
+ type="button"
23
+ class="ec__config-btn is-primary"
24
+ @click="copyLayoutConfig"
25
+ >复制配置</button>
26
+ </div>
27
+ </div>
28
+
29
+ <div class="ec__config-list">
30
+ <div
31
+ v-for="field in configFields"
32
+ :key="field.key"
33
+ class="ec__config-block"
34
+ :class="'ec__config-block--' + field.kind"
35
+ :title="field.kind === 'spacing' ? '当前简写:' + spacingShorthand(field.key) : undefined"
36
+ >
37
+ <!-- 分段选项:条目对齐 / 溢出方式 -->
38
+ <template v-if="field.kind === 'seg'">
39
+ <p class="ec__config-label">
40
+ <span class="ec__config-priority">P{{ field.priority }}</span>{{ field.label }}
41
+ <button
42
+ type="button"
43
+ class="ec__config-reset"
44
+ :title="'还原「' + field.label + '」为初始值'"
45
+ @click="resetField(field.key)"
46
+ >还原</button>
47
+ </p>
48
+ <div class="ec__config-segment">
49
+ <button
50
+ v-for="option in field.options"
51
+ :key="option.value"
52
+ type="button"
53
+ :class="{ 'is-active': configDraft[field.key] === option.value }"
54
+ :aria-pressed="configDraft[field.key] === option.value ? 'true' : 'false'"
55
+ @click="updateLayoutConfig(field.key, option.value)"
56
+ >{{ option.label }}</button>
57
+ </div>
58
+ </template>
59
+
60
+ <!-- 间距:上下 / 左右两块步进 -->
61
+ <template v-else-if="field.kind === 'spacing'">
62
+ <p class="ec__config-label">
63
+ <span class="ec__config-priority">P{{ field.priority }}</span>{{ field.label }}
64
+ <code class="ec__config-shorthand">{{ spacingShorthand(field.key) }}</code>
65
+ <button
66
+ type="button"
67
+ class="ec__config-reset"
68
+ :title="'还原「' + field.label + '」为初始值'"
69
+ @click="resetField(field.key)"
70
+ >还原</button>
71
+ </p>
72
+ <div class="ec__spacing">
73
+ <div v-for="axis in spacingAxes" :key="axis.key" class="ec__spacing-row">
74
+ <span class="ec__spacing-row-label">{{ axis.label }}</span>
75
+ <span class="ec__stepper">
76
+ <button
77
+ type="button"
78
+ class="ec__step-btn"
79
+ :aria-label="field.label + ',' + axis.label + '减 1'"
80
+ title="减 1(按住 Shift ±5)"
81
+ @click="nudgeSpacingAxis(field.key, axis.key, -1, $event)"
82
+ >−</button>
83
+ <label class="ec__stepper-input">
84
+ <input
85
+ type="number"
86
+ min="0"
87
+ step="1"
88
+ :value="spacingAxisValue(field.key, axis.key)"
89
+ :aria-label="field.label + ',' + axis.label + '(px)'"
90
+ @change="updateSpacingAxis(field.key, axis.key, $event)"
91
+ />
92
+ <span class="ec__stepper-unit" aria-hidden="true">px</span>
93
+ </label>
94
+ <button
95
+ type="button"
96
+ class="ec__step-btn"
97
+ :aria-label="field.label + ',' + axis.label + '加 1'"
98
+ title="加 1(按住 Shift ±5)"
99
+ @click="nudgeSpacingAxis(field.key, axis.key, 1, $event)"
100
+ >+</button>
101
+ </span>
102
+ </div>
103
+ </div>
104
+ </template>
105
+
106
+ <!-- 单值数值:输入框左右包 −/+ -->
107
+ <template v-else-if="field.kind === 'number'">
108
+ <p class="ec__config-label">
109
+ <span class="ec__config-priority">P{{ field.priority }}</span>{{ field.label }}
110
+ <button
111
+ type="button"
112
+ class="ec__config-reset"
113
+ :title="'还原「' + field.label + '」为初始值'"
114
+ @click="resetField(field.key)"
115
+ >还原</button>
116
+ </p>
117
+ <span class="ec__stepper">
118
+ <button
119
+ type="button"
120
+ class="ec__step-btn"
121
+ :aria-label="field.label + '减 1'"
122
+ :title="'减 ' + (field.step || 1) + '(按住 Shift ×5)'"
123
+ @click="nudgeNumber(field.key, -1, $event)"
124
+ >−</button>
125
+ <label class="ec__stepper-input">
126
+ <input
127
+ type="number"
128
+ :min="typeof field.min === 'number' ? field.min : 0"
129
+ :step="field.step || 1"
130
+ :value="configDraft[field.key]"
131
+ :aria-label="field.label + '(' + field.unit + ')'"
132
+ @change="updateLayoutConfig(field.key, $event.target.value, 'number')"
133
+ />
134
+ <span v-if="field.unit" class="ec__stepper-unit" aria-hidden="true">{{ field.unit }}</span>
135
+ </label>
136
+ <button
137
+ type="button"
138
+ class="ec__step-btn"
139
+ :aria-label="field.label + '加 1'"
140
+ :title="'加 ' + (field.step || 1) + '(按住 Shift ×5)'"
141
+ @click="nudgeNumber(field.key, 1, $event)"
142
+ >+</button>
143
+ </span>
144
+ </template>
145
+
146
+ <!-- 颜色 -->
147
+ <template v-else-if="field.kind === 'color'">
148
+ <label class="ec__color-swatch" :title="field.label">
149
+ <input
150
+ type="color"
151
+ :value="colorValue(field.key)"
152
+ :aria-label="field.label"
153
+ @input="updateLayoutConfig(field.key, $event.target.value)"
154
+ />
155
+ </label>
156
+ <span class="ec__color-meta">
157
+ <span class="ec__config-label">
158
+ <span class="ec__config-priority">P{{ field.priority }}</span>{{ field.label }}
159
+ </span>
160
+ <code class="ec__color-hex">{{ colorValue(field.key) }}</code>
161
+ </span>
162
+ <button
163
+ type="button"
164
+ class="ec__reset-btn"
165
+ title="还原为默认配色"
166
+ :disabled="!configDraft[field.key]"
167
+ @click="resetColor(field.key)"
168
+ >还原</button>
169
+ </template>
170
+ </div>
171
+ </div>
172
+
173
+ <div class="ec__config-output">
174
+ <pre class="ec__config-json">{{ configJsonText }}</pre>
175
+ </div>
176
+
177
+ <div class="ec__config-feedback" role="status" aria-live="polite">
178
+ {{ copyStatus }}
179
+ </div>
180
+ </section>
181
+
9
182
  <!-- ==================== 顶部区域 ==================== -->
10
183
  <div v-if="showHeader" class="ec__header">
11
184
  <!-- 左侧:完全交给使用者的插槽,未传时回落到 title / subtitle -->
@@ -115,8 +288,8 @@
115
288
  <slot name="loading" v-bind="{ rows: skeletonRows }">
116
289
  <CalendarSkeleton
117
290
  :rows="skeletonRows"
118
- :min-row-height="minRowHeight"
119
- :item-height="itemHeight"
291
+ :min-row-height="cfgMinRowHeight"
292
+ :item-height="cfgItemHeight"
120
293
  />
121
294
  </slot>
122
295
  </div>
@@ -179,15 +352,26 @@
179
352
  :class="{
180
353
  'is-disabled': entry.disabled,
181
354
  'has-badge': !!entry.badge,
182
- 'is-colorful': colorful && entry.type
355
+ 'is-colorful': colorful && entry.type && !cfgItemBgColor && cfgItemBgHover,
356
+ 'is-bg-gradient': cfgItemBgGradient,
357
+ 'is-bg-gradient-h': cfgItemBgGradient && cfgGradientDir === 'to-right'
183
358
  }"
184
359
  :style="itemStyle(entry)"
185
- :title="entry.label"
360
+ :title="itemTitle(entry)"
186
361
  @click.stop="handleItemClick(entry, cell, i, $event)"
187
362
  >
188
363
  <!-- 单条自定义 -->
189
364
  <slot name="item" v-bind="itemScope(entry, cell, i)">
190
- <span class="ec__item-label">{{ entry.label }}</span>
365
+ <div class="ec__item-content">
366
+ <span
367
+ class="ec__item-label"
368
+ :class="{ 'is-long': isLongItemLabel(entry.label) }"
369
+ >{{ entry.label }}</span>
370
+ <span
371
+ v-if="hasText(entry.address)"
372
+ class="ec__item-address"
373
+ >{{ entry.address }}</span>
374
+ </div>
191
375
  <span
192
376
  v-if="entry.badge"
193
377
  class="ec__item-badge"
@@ -253,6 +437,128 @@ const BUILTIN_TYPE_COLORS = {
253
437
  default: { color: '#6b7280', bg: 'rgba(107, 114, 128, 0.10)' }
254
438
  }
255
439
 
440
+ /** 布局配置全部对应 EventCalendar 原生 Props,可直接用于 v-bind。 */
441
+ const DEFAULT_LAYOUT_CONFIG = Object.freeze({
442
+ itemAlign: 'left',
443
+ calendarMargin: 0,
444
+ calendarPadding: 0,
445
+ headerPadding: '4px 4px 16px',
446
+ cellPadding: '12px 10px',
447
+ mobileCellPadding: '8px 6px',
448
+ itemHorizontalPadding: 8,
449
+ itemVerticalPadding: 4,
450
+ itemGap: 6,
451
+ // 溢出 / 行高 / 角标缩放:同样是对应原生 Props
452
+ overflowMode: 'scroll',
453
+ maxVisibleItems: 4,
454
+ itemHeight: 28,
455
+ minRowHeight: 118,
456
+ badgeScale: 0.75,
457
+ // 条目内部排版与外观
458
+ itemLineGap: 2, // 名称与地址之间的行间距(px)
459
+ itemBgGradient: false, // 条目背景是否启用渐变效果
460
+ itemBgGradientDir: 'to-bottom', // 渐变方向:to-bottom 上到下 | to-right 左到右
461
+ itemBgHover: true, // 悬浮时是否在底色上自动加深
462
+ // 条目(.ec__item)颜色覆盖:空串 = 按 type/主题配色走
463
+ itemTextColor: '',
464
+ itemBgColor: ''
465
+ })
466
+
467
+ /**
468
+ * 工具栏统一字段表:按展示顺序排列。
469
+ * kind: seg=分段选项 | spacing=上下/左右两块 | number=单值步进 | color=取色
470
+ */
471
+ const CONFIG_FIELDS = Object.freeze([
472
+ { kind: 'seg', key: 'itemAlign', label: '条目对齐', priority: 1, options: [
473
+ { label: '靠左', value: 'left' },
474
+ { label: '居中', value: 'center' }
475
+ ] },
476
+
477
+ { kind: 'spacing', key: 'cellPadding', label: '日期格内边距', priority: 2 },
478
+ { kind: 'spacing', key: 'mobileCellPadding', label: '窄屏日期格内边距', priority: 3 },
479
+
480
+ { kind: 'number', key: 'itemHorizontalPadding', label: '条目左右内边距', priority: 4, unit: 'px', step: 1 },
481
+ { kind: 'number', key: 'itemVerticalPadding', label: '条目上下内边距', priority: 5, unit: 'px', step: 1 },
482
+ { kind: 'number', key: 'itemGap', label: '条目间距', priority: 6, unit: 'px', step: 1 },
483
+
484
+ { kind: 'number', key: 'maxVisibleItems', label: '单格最多显示', priority: 7, unit: '条', step: 1, min: 1 },
485
+ { kind: 'number', key: 'itemHeight', label: '条目高度', priority: 8, unit: 'px', step: 1 },
486
+ { kind: 'number', key: 'itemLineGap', label: '名称地址间距', priority: 9, unit: 'px', step: 1 },
487
+
488
+ { kind: 'seg', key: 'itemBgGradient', label: '条目背景效果', priority: 10, options: [
489
+ { label: '纯色', value: false },
490
+ { label: '渐变', value: true }
491
+ ] },
492
+ { kind: 'seg', key: 'itemBgGradientDir', label: '渐变方向', priority: 11, options: [
493
+ { label: '上到下', value: 'to-bottom' },
494
+ { label: '左到右', value: 'to-right' }
495
+ ] },
496
+ { kind: 'seg', key: 'itemBgHover', label: '悬浮加深', priority: 12, options: [
497
+ { label: '开启', value: true },
498
+ { label: '关闭', value: false }
499
+ ] },
500
+
501
+ { kind: 'color', key: 'itemTextColor', label: '条目文字色', priority: 13, default: '#606266' },
502
+ { kind: 'color', key: 'itemBgColor', label: '条目背景色', priority: 14, default: '#f5f7fa' }
503
+ ])
504
+
505
+ /** 间距控件的两个轴:vertical=上下(top/bottom),horizontal=左右(left/right) */
506
+ const CONFIG_SPACING_AXES = Object.freeze([
507
+ { key: 'vertical', label: '上下', sides: ['top', 'bottom'] },
508
+ { key: 'horizontal', label: '左右', sides: ['left', 'right'] }
509
+ ])
510
+
511
+ function createLayoutConfig(vm) {
512
+ return Object.keys(DEFAULT_LAYOUT_CONFIG).reduce((config, key) => {
513
+ config[key] = vm[key]
514
+ return config
515
+ }, {})
516
+ }
517
+
518
+ function toCssSpacing(value, fallback) {
519
+ if (typeof value === 'number' && Number.isFinite(value)) return `${Math.max(0, value)}px`
520
+ if (typeof value === 'string' && value.trim()) return value.trim()
521
+ return typeof fallback === 'number' ? `${fallback}px` : fallback
522
+ }
523
+
524
+ /**
525
+ * 把 CSS 简写(或数字)解析成 { top, right, bottom, left } 的 px 数值。
526
+ * 支持 1~4 个值,如 '12px 10px' -> { top:12, right:10, bottom:12, left:10 }。
527
+ * 解析失败返回全 0,避免方向控件拿不到数。
528
+ */
529
+ function parseSpacingToSides(value) {
530
+ const zero = { top: 0, right: 0, bottom: 0, left: 0 }
531
+ let nums = null
532
+ if (typeof value === 'number' && Number.isFinite(value)) {
533
+ nums = [value, value, value, value]
534
+ } else if (typeof value === 'string') {
535
+ const tokens = value.trim().split(/\s+/).filter(Boolean)
536
+ const parsed = tokens.map((token) => {
537
+ const m = /^(-?\d+(?:\.\d+)?)(?:px)?$/i.exec(token)
538
+ return m ? Number(m[1]) : null
539
+ })
540
+ if (parsed.length && parsed.every((n) => n != null && Number.isFinite(n))) nums = parsed
541
+ }
542
+ if (!nums) return zero
543
+ const top = nums[0]
544
+ const right = nums.length >= 2 ? nums[1] : top
545
+ const bottom = nums.length >= 3 ? nums[2] : top
546
+ const left = nums.length >= 4 ? nums[3] : right
547
+ return { top, right, bottom, left }
548
+ }
549
+
550
+ /** 4 个方向的 px 合成 CSS 简写:全等/上下左右两两相等时尽量压缩成 1 项或 2 项 */
551
+ function spacingFromSides(sides) {
552
+ const px = (v) => `${Math.max(0, Math.round(v))}px`
553
+ const top = sides.top
554
+ const right = sides.right
555
+ const bottom = sides.bottom
556
+ const left = sides.left
557
+ if (top === right && right === bottom && bottom === left) return px(top)
558
+ if (top === bottom && right === left) return `${px(top)} ${px(right)}`
559
+ return `${px(top)} ${px(right)} ${px(bottom)} ${px(left)}`
560
+ }
561
+
256
562
  export default {
257
563
  name: 'EventCalendar',
258
564
 
@@ -405,11 +711,86 @@ export default {
405
711
  type: Number,
406
712
  default: 6
407
713
  },
714
+ /** 条目内容对齐方式 */
715
+ itemAlign: {
716
+ type: String,
717
+ default: 'left',
718
+ validator: (v) => ['left', 'center'].includes(v)
719
+ },
720
+ /** 日历根节点外边距;Number 按 px 处理,String 支持 CSS 简写 */
721
+ calendarMargin: {
722
+ type: [Number, String],
723
+ default: DEFAULT_LAYOUT_CONFIG.calendarMargin
724
+ },
725
+ /** 日历根节点内边距;Number 按 px 处理,String 支持 CSS 简写 */
726
+ calendarPadding: {
727
+ type: [Number, String],
728
+ default: DEFAULT_LAYOUT_CONFIG.calendarPadding
729
+ },
730
+ /** 顶部区域内边距;支持 CSS 简写 */
731
+ headerPadding: {
732
+ type: [Number, String],
733
+ default: DEFAULT_LAYOUT_CONFIG.headerPadding
734
+ },
735
+ /** 日期格内边距;支持 CSS 简写 */
736
+ cellPadding: {
737
+ type: [Number, String],
738
+ default: DEFAULT_LAYOUT_CONFIG.cellPadding
739
+ },
740
+ /** 768px 及以下日期格内边距;支持 CSS 简写 */
741
+ mobileCellPadding: {
742
+ type: [Number, String],
743
+ default: DEFAULT_LAYOUT_CONFIG.mobileCellPadding
744
+ },
745
+ /** 条目左右内边距(px),传 0 可去除 */
746
+ itemHorizontalPadding: {
747
+ type: Number,
748
+ default: 8,
749
+ validator: (v) => v >= 0
750
+ },
751
+ /** 条目上下内边距(px) */
752
+ itemVerticalPadding: {
753
+ type: Number,
754
+ default: DEFAULT_LAYOUT_CONFIG.itemVerticalPadding,
755
+ validator: (v) => v >= 0
756
+ },
757
+ /** 显示内置布局配置工具栏;生产环境默认隐藏 */
758
+ showConfigToolbar: {
759
+ type: Boolean,
760
+ default: false
761
+ },
408
762
  /** 单元格最小高度(px),保证空行不塌陷 */
409
763
  minRowHeight: {
410
764
  type: Number,
411
765
  default: 118
412
766
  },
767
+ /** 右上角角标整体缩放倍率(transform scale,越小越紧凑) */
768
+ badgeScale: {
769
+ type: Number,
770
+ default: 0.75
771
+ },
772
+ /** 条目内名称与地址之间的行间距(px) */
773
+ itemLineGap: {
774
+ type: Number,
775
+ default: 2,
776
+ validator: (v) => v >= 0
777
+ },
778
+ /** 条目背景是否启用渐变效果(在所选底色上叠柔和渐变) */
779
+ itemBgGradient: {
780
+ type: Boolean,
781
+ default: false
782
+ },
783
+ /** 渐变方向:to-bottom 上到下 | to-right 左到右(仅渐变开启时生效) */
784
+ itemBgGradientDir: {
785
+ type: String,
786
+ default: 'to-bottom',
787
+ validator: (v) => ['to-bottom', 'to-right'].includes(v)
788
+ },
789
+ /** 悬浮时是否自动把条目背景调淡一档(false 则保持原色) */
790
+ itemBgHover: {
791
+ type: Boolean,
792
+ default: true
793
+ },
413
794
  /** 相邻月份补位格子是否也渲染条目 */
414
795
  showAdjacentItems: {
415
796
  type: Boolean,
@@ -450,6 +831,16 @@ export default {
450
831
  type: Boolean,
451
832
  default: false
452
833
  },
834
+ /** 条目(.ec__item)文字色覆盖,空串 = 按 type/主题配色 */
835
+ itemTextColor: {
836
+ type: String,
837
+ default: ''
838
+ },
839
+ /** 条目(.ec__item)背景色覆盖,空串 = 按 type/主题配色 */
840
+ itemBgColor: {
841
+ type: String,
842
+ default: ''
843
+ },
453
844
  /** 主题变量覆盖,如 { primary: '#10a37f', radius: '12px' } */
454
845
  theme: {
455
846
  type: Object,
@@ -476,7 +867,13 @@ export default {
476
867
  /** 每次切换 +1,用于重置进度条 CSS 动画 */
477
868
  progressKey: 0,
478
869
  /** 骨架屏 -> 内容 的首次切换用淡入,避免莫名其妙的滑动 */
479
- useFadeOnce: true
870
+ useFadeOnce: true,
871
+ /** 配置工具栏只编辑本地草稿,还原时以此为初始快照 */
872
+ configDraft: createLayoutConfig(this),
873
+ /** 初始配置快照:每个「还原」按钮回到这里 */
874
+ configDefaults: createLayoutConfig(this),
875
+ /** 复制配置按钮的即时反馈文案 */
876
+ copyStatus: ''
480
877
  }
481
878
  },
482
879
 
@@ -486,6 +883,63 @@ export default {
486
883
  return { ...DEFAULT_FIELD_NAMES, ...this.fieldNames }
487
884
  },
488
885
 
886
+ externalLayoutConfig() {
887
+ return createLayoutConfig(this)
888
+ },
889
+
890
+ effectiveLayoutConfig() {
891
+ return this.showConfigToolbar ? this.configDraft : this.externalLayoutConfig
892
+ },
893
+
894
+ /** 工具栏统一字段表(seg / spacing / number / color) */
895
+ configFields() {
896
+ // 渐变方向只在启用渐变时才显示
897
+ return CONFIG_FIELDS.filter((f) => !(f.key === 'itemBgGradientDir' && !this.configDraft.itemBgGradient))
898
+ },
899
+
900
+ /** 间距控件的「上下 / 左右」两组方向定义 */
901
+ spacingAxes() {
902
+ return CONFIG_SPACING_AXES
903
+ },
904
+
905
+ /** 实时生成的配置 JSON 文本(复制给用户 / 供展示) */
906
+ configJsonText() {
907
+ return JSON.stringify(this.getLayoutConfig(), null, 2)
908
+ },
909
+
910
+ /** 下面几个值统一从布局配置取:工具栏可见时用草稿,否则回落 Props */
911
+ cfgItemHeight() {
912
+ return this.effectiveLayoutConfig.itemHeight
913
+ },
914
+
915
+ cfgMaxVisibleItems() {
916
+ return this.effectiveLayoutConfig.maxVisibleItems
917
+ },
918
+
919
+ cfgOverflowMode() {
920
+ return this.effectiveLayoutConfig.overflowMode
921
+ },
922
+
923
+ cfgMinRowHeight() {
924
+ return this.effectiveLayoutConfig.minRowHeight
925
+ },
926
+
927
+ cfgItemBgColor() {
928
+ return this.effectiveLayoutConfig.itemBgColor
929
+ },
930
+
931
+ cfgItemBgGradient() {
932
+ return this.effectiveLayoutConfig.itemBgGradient
933
+ },
934
+
935
+ cfgGradientDir() {
936
+ return this.effectiveLayoutConfig.itemBgGradientDir
937
+ },
938
+
939
+ cfgItemBgHover() {
940
+ return this.effectiveLayoutConfig.itemBgHover
941
+ },
942
+
489
943
  /* --------------------------- 数据归一化 --------------------------- */
490
944
  /**
491
945
  * 把外部数据统一整理成 [{ month, label, raw }],
@@ -598,16 +1052,17 @@ export default {
598
1052
  ? String(getByPath(raw, f.itemKey))
599
1053
  : `${cell.dateKey}-${i}`,
600
1054
  label: getByPath(raw, f.itemLabel),
1055
+ address: getByPath(raw, f.itemAddress),
601
1056
  badge: getByPath(raw, f.itemBadge),
602
1057
  type: getByPath(raw, f.itemType),
603
1058
  disabled: !!getByPath(raw, f.itemDisabled),
604
1059
  raw
605
1060
  }))
606
1061
 
607
- const overflowed = items.length > this.maxVisibleItems
608
- const collapse = this.overflowMode === 'collapse' && overflowed
1062
+ const overflowed = items.length > this.cfgMaxVisibleItems
1063
+ const collapse = this.cfgOverflowMode === 'collapse' && overflowed
609
1064
  // collapse 模式留一行给「还有 N 项」
610
- const visibleItems = collapse ? items.slice(0, this.maxVisibleItems - 1) : items
1065
+ const visibleItems = collapse ? items.slice(0, this.cfgMaxVisibleItems - 1) : items
611
1066
 
612
1067
  return {
613
1068
  ...cell,
@@ -616,7 +1071,7 @@ export default {
616
1071
  visibleItems,
617
1072
  moreCount: collapse ? items.length - visibleItems.length : 0,
618
1073
  /** scroll 模式下是否真的会出现滚动条 */
619
- scrollable: this.overflowMode === 'scroll' && overflowed
1074
+ scrollable: this.cfgOverflowMode === 'scroll' && overflowed
620
1075
  }
621
1076
  })
622
1077
  },
@@ -626,6 +1081,7 @@ export default {
626
1081
  return {
627
1082
  'ec--loading': this.loading,
628
1083
  'ec--stagger': this.cellStagger && !this.loading,
1084
+ 'ec--item-center': this.effectiveLayoutConfig.itemAlign === 'center',
629
1085
  [`ec--effect-${this.transitionEffect}`]: true
630
1086
  }
631
1087
  },
@@ -633,8 +1089,9 @@ export default {
633
1089
  /** 主题:全部走 CSS 变量,方便外部覆盖 */
634
1090
  cssVars() {
635
1091
  const t = this.theme || {}
636
- const n = this.maxVisibleItems
637
- return {
1092
+ const n = this.cfgMaxVisibleItems
1093
+ const layout = this.effectiveLayoutConfig
1094
+ const vars = {
638
1095
  '--ec-primary': t.primary || '#3d6cf0',
639
1096
  '--ec-primary-ghost': t.primaryGhost || 'rgba(61, 108, 240, 0.12)',
640
1097
  '--ec-border': t.border || '#e4e7ed',
@@ -643,16 +1100,31 @@ export default {
643
1100
  '--ec-text-secondary': t.textSecondary || '#606266',
644
1101
  '--ec-text-placeholder': t.textPlaceholder || '#c0c4cc',
645
1102
  '--ec-muted': t.muted || '#c8ccd4',
646
- '--ec-item-bg': t.itemBg || '#f5f7fa',
1103
+ // 条目颜色:工具栏配置的条目色优先于 theme,theme 优先于默认
1104
+ '--ec-item-bg': layout.itemBgColor || t.itemBg || '#f5f7fa',
647
1105
  '--ec-cell-hover': t.cellHover || '#fafbfd',
648
1106
  '--ec-radius': t.radius || '8px',
649
1107
  '--ec-duration': this.transitionDuration + 'ms',
650
- '--ec-item-h': this.itemHeight + 'px',
651
- '--ec-item-gap': this.itemGap + 'px',
652
- '--ec-min-row-h': this.minRowHeight + 'px',
1108
+ '--ec-item-h': this.cfgItemHeight + 'px',
1109
+ '--ec-badge-scale': Number.isFinite(Number(layout.badgeScale)) ? Number(layout.badgeScale) : 0.75,
1110
+ '--ec-calendar-margin': toCssSpacing(layout.calendarMargin, DEFAULT_LAYOUT_CONFIG.calendarMargin),
1111
+ '--ec-calendar-padding': toCssSpacing(layout.calendarPadding, DEFAULT_LAYOUT_CONFIG.calendarPadding),
1112
+ '--ec-header-padding': toCssSpacing(layout.headerPadding, DEFAULT_LAYOUT_CONFIG.headerPadding),
1113
+ '--ec-cell-padding': toCssSpacing(layout.cellPadding, DEFAULT_LAYOUT_CONFIG.cellPadding),
1114
+ '--ec-cell-padding-mobile': toCssSpacing(layout.mobileCellPadding, DEFAULT_LAYOUT_CONFIG.mobileCellPadding),
1115
+ '--ec-item-gap': layout.itemGap + 'px',
1116
+ '--ec-item-padding-x': layout.itemHorizontalPadding + 'px',
1117
+ '--ec-item-padding-y': layout.itemVerticalPadding + 'px',
1118
+ '--ec-item-line-gap': layout.itemLineGap + 'px',
1119
+ // 悬浮自动调淡一档(白色叠层);关闭则为透明
1120
+ '--ec-item-hover-mask': layout.itemBgHover === false ? 'transparent' : 'rgba(255, 255, 255, 0.18)',
1121
+ '--ec-min-row-h': this.cfgMinRowHeight + 'px',
653
1122
  // 滚动区域最大高度 = n 个条目 + (n-1) 个间距
654
- '--ec-items-max-h': `${n * this.itemHeight + (n - 1) * this.itemGap}px`
1123
+ '--ec-items-max-h': `${n * this.cfgItemHeight + (n - 1) * layout.itemGap}px`
655
1124
  }
1125
+ // 条目文字色只在显式配置时覆盖;未配置时走 --ec-text-secondary(hover 走 --ec-text)
1126
+ if (layout.itemTextColor) vars['--ec-item-text'] = layout.itemTextColor
1127
+ return vars
656
1128
  },
657
1129
 
658
1130
  stageStyle() {
@@ -661,7 +1133,7 @@ export default {
661
1133
 
662
1134
  itemsStyle() {
663
1135
  // collapse 模式不限制高度(本来就已经截断了),scroll 模式限高触发滚动条
664
- return this.overflowMode === 'scroll'
1136
+ return this.cfgOverflowMode === 'scroll'
665
1137
  ? { maxHeight: 'var(--ec-items-max-h)' }
666
1138
  : {}
667
1139
  },
@@ -737,6 +1209,14 @@ export default {
737
1209
  if (val && val !== this.innerMonth) this.setMonth(val, 'prop')
738
1210
  },
739
1211
 
1212
+ /** 父组件通过 v-bind 更新已保存配置时,同步到工具栏草稿。 */
1213
+ externalLayoutConfig: {
1214
+ deep: true,
1215
+ handler(val) {
1216
+ this.configDraft = { ...val }
1217
+ }
1218
+ },
1219
+
740
1220
  /** 数据变化后校正当前月份(比如接口返回了新的月份列表) */
741
1221
  monthOptions: {
742
1222
  immediate: true,
@@ -794,6 +1274,123 @@ export default {
794
1274
  },
795
1275
 
796
1276
  methods: {
1277
+ /* ========================= 布局配置 ========================= */
1278
+ /** 间距字段某个轴的当前 px 值(vertical 取上下、horizontal 取左右) */
1279
+ spacingAxisValue(key, axisKey) {
1280
+ const sides = parseSpacingToSides(this.configDraft[key])
1281
+ const axis = this.spacingAxes.find((a) => a.key === axisKey)
1282
+ return axis ? sides[axis.sides[0]] : 0
1283
+ },
1284
+
1285
+ /** 间距字段的可读简写,如 12px 10px(也用于标题 tooltip) */
1286
+ spacingShorthand(key) {
1287
+ return spacingFromSides(parseSpacingToSides(this.configDraft[key]))
1288
+ },
1289
+
1290
+ /** 按「上下 / 左右」整轴步进 ±delta px */
1291
+ nudgeSpacingAxis(key, axisKey, delta, event) {
1292
+ const step = event && event.shiftKey ? 5 : 1
1293
+ const sides = parseSpacingToSides(this.configDraft[key])
1294
+ const axis = this.spacingAxes.find((a) => a.key === axisKey)
1295
+ if (!axis) return
1296
+ const current = sides[axis.sides[0]] || 0
1297
+ const next = Math.max(0, Math.round(current + delta * step))
1298
+ if (next === current) return
1299
+ axis.sides.forEach((side) => { sides[side] = next })
1300
+ this.updateLayoutConfig(key, spacingFromSides(sides))
1301
+ },
1302
+
1303
+ /** 直接输入间距字段某个轴(上下/左右)的值,两端一起生效 */
1304
+ updateSpacingAxis(key, axisKey, event) {
1305
+ const raw = Number(event.target.value)
1306
+ const sides = parseSpacingToSides(this.configDraft[key])
1307
+ const axis = this.spacingAxes.find((a) => a.key === axisKey)
1308
+ if (!axis) return
1309
+ const next = Number.isFinite(raw) ? Math.max(0, Math.round(raw)) : 0
1310
+ axis.sides.forEach((side) => { sides[side] = next })
1311
+ this.updateLayoutConfig(key, spacingFromSides(sides))
1312
+ },
1313
+
1314
+ /** 数值字段步进:步长取字段 step(整数 ±1、角标缩放 ±0.05),按住 Shift ×5 */
1315
+ nudgeNumber(key, delta, event) {
1316
+ const field = CONFIG_FIELDS.find((f) => f.key === key)
1317
+ const unit = field && Number(field.step) ? Number(field.step) : 1
1318
+ const multi = event && event.shiftKey ? 5 : 1
1319
+ const current = Number(this.configDraft[key])
1320
+ const base = Number.isFinite(current) ? current : 0
1321
+ this.updateLayoutConfig(key, base + delta * unit * multi, 'number')
1322
+ },
1323
+
1324
+ /** 取色器展示值:未配置(空串)时给一个与默认主题一致的兜底色 */
1325
+ colorValue(key) {
1326
+ const hit = CONFIG_FIELDS.find((f) => f.key === key && f.kind === 'color')
1327
+ return this.configDraft[key] || (hit ? hit.default : '#000000')
1328
+ },
1329
+
1330
+ /** 还原颜色为默认配色(存空串) */
1331
+ resetColor(key) {
1332
+ this.updateLayoutConfig(key, '')
1333
+ },
1334
+
1335
+ /** 还原某个配置项到初始快照(configDefaults) */
1336
+ resetField(key) {
1337
+ if (!Object.prototype.hasOwnProperty.call(this.configDefaults, key)) return
1338
+ this.$set(this.configDraft, key, this.configDefaults[key])
1339
+ this.copyStatus = ''
1340
+ this.$emit('config-change', this.getLayoutConfig())
1341
+ },
1342
+
1343
+ updateLayoutConfig(key, value, inputType) {
1344
+ let nextValue = value
1345
+ if (inputType === 'number') {
1346
+ const parsed = Number(value)
1347
+ const field = CONFIG_FIELDS.find((f) => f.key === key)
1348
+ const min = field && typeof field.min === 'number' ? field.min : 0
1349
+ nextValue = Number.isFinite(parsed)
1350
+ ? Math.max(min, Math.round(parsed * 100) / 100)
1351
+ : min
1352
+ }
1353
+ this.$set(this.configDraft, key, nextValue)
1354
+ this.copyStatus = ''
1355
+ this.$emit('config-change', this.getLayoutConfig())
1356
+ },
1357
+
1358
+ /** 返回可直接 <EventCalendar v-bind="config" /> 的配置对象。 */
1359
+ getLayoutConfig() {
1360
+ return { ...this.effectiveLayoutConfig }
1361
+ },
1362
+
1363
+ /** 复制当前配置 JSON 到剪贴板(用户直接粘贴发回,研发贴到组件上即可) */
1364
+ copyLayoutConfig() {
1365
+ const json = this.configJsonText
1366
+ const fallback = () => {
1367
+ if (typeof document === 'undefined') {
1368
+ this.copyStatus = '已复制,可粘贴发送给研发。'
1369
+ return
1370
+ }
1371
+ const ta = document.createElement('textarea')
1372
+ ta.value = json
1373
+ ta.setAttribute('readonly', '')
1374
+ ta.style.position = 'fixed'
1375
+ ta.style.opacity = '0'
1376
+ document.body.appendChild(ta)
1377
+ ta.select()
1378
+ let ok = false
1379
+ try { ok = document.execCommand('copy') } catch (e) { ok = false }
1380
+ document.body.removeChild(ta)
1381
+ this.copyStatus = ok
1382
+ ? '已复制,可粘贴发送给研发。'
1383
+ : '复制失败,请手动全选下方 JSON 后复制。'
1384
+ }
1385
+ if (typeof navigator !== 'undefined' && navigator.clipboard && window.isSecureContext) {
1386
+ navigator.clipboard.writeText(json).then(() => {
1387
+ this.copyStatus = '已复制,可粘贴发送给研发。'
1388
+ }, fallback)
1389
+ } else {
1390
+ fallback()
1391
+ }
1392
+ },
1393
+
797
1394
  /* ========================= 月份切换 ========================= */
798
1395
  /**
799
1396
  * 切换到指定月份
@@ -1050,6 +1647,7 @@ export default {
1050
1647
  item: entry.raw,
1051
1648
  raw: entry.raw,
1052
1649
  label: entry.label,
1650
+ address: entry.address,
1053
1651
  badge: entry.badge,
1054
1652
  type: entry.type,
1055
1653
  disabled: entry.disabled,
@@ -1082,6 +1680,22 @@ export default {
1082
1680
  this.$emit('more-click', this.cellScope(cell), event)
1083
1681
  },
1084
1682
 
1683
+ hasText(value) {
1684
+ return value !== undefined && value !== null && String(value).trim() !== ''
1685
+ },
1686
+
1687
+ itemTitle(entry) {
1688
+ const label = this.hasText(entry.label) ? String(entry.label) : ''
1689
+ const address = this.hasText(entry.address) ? `\n${entry.address}` : ''
1690
+ return label + address
1691
+ },
1692
+
1693
+ /** 大学名称超过 7 个字符时使用 12px,否则使用 14px。 */
1694
+ isLongItemLabel(label) {
1695
+ const text = label == null ? '' : String(label)
1696
+ return text.length > 7
1697
+ },
1698
+
1085
1699
  /* ========================= 配色 ========================= */
1086
1700
  resolveTypeColor(type) {
1087
1701
  const merged = { ...BUILTIN_TYPE_COLORS, ...this.typeColors }
@@ -1113,13 +1727,415 @@ export default {
1113
1727
  .ec {
1114
1728
  position: relative;
1115
1729
  box-sizing: border-box;
1116
- width: 100%;
1730
+ width: auto;
1731
+ margin: var(--ec-calendar-margin);
1732
+ padding: var(--ec-calendar-padding);
1117
1733
  color: var(--ec-text);
1118
1734
  font-size: 14px;
1119
1735
  line-height: 1.5;
1120
1736
  -webkit-font-smoothing: antialiased;
1121
1737
  }
1122
1738
 
1739
+ /* -------------------------- 布局配置工具栏 -------------------------- */
1740
+ .ec__config {
1741
+ margin-bottom: 16px;
1742
+ padding: 14px;
1743
+ border: 1px solid var(--ec-border);
1744
+ border-radius: calc(var(--ec-radius) + 2px);
1745
+ background: #f8fafc;
1746
+ }
1747
+
1748
+ .ec__config-head,
1749
+ .ec__config-actions,
1750
+ .ec__config-segment {
1751
+ display: flex;
1752
+ align-items: center;
1753
+ }
1754
+
1755
+ .ec__config-head {
1756
+ justify-content: space-between;
1757
+ gap: 12px;
1758
+ margin-bottom: 12px;
1759
+ }
1760
+
1761
+ .ec__config-title {
1762
+ color: var(--ec-text);
1763
+ font-size: 14px;
1764
+ }
1765
+
1766
+ .ec__config-caption {
1767
+ margin-left: 8px;
1768
+ color: var(--ec-text-secondary);
1769
+ font-size: 12px;
1770
+ }
1771
+
1772
+ .ec__config-actions {
1773
+ gap: 8px;
1774
+ }
1775
+
1776
+ .ec__config-btn,
1777
+ .ec__config-segment button {
1778
+ min-height: 34px;
1779
+ border: 1px solid var(--ec-border);
1780
+ border-radius: 6px;
1781
+ background: #fff;
1782
+ color: var(--ec-text-secondary);
1783
+ font: inherit;
1784
+ font-size: 12px;
1785
+ cursor: pointer;
1786
+ transition: color 0.18s, border-color 0.18s, background-color 0.18s;
1787
+ }
1788
+
1789
+ .ec__config-btn {
1790
+ padding: 0 12px;
1791
+ }
1792
+
1793
+ .ec__config-btn.is-primary,
1794
+ .ec__config-segment button.is-active {
1795
+ border-color: var(--ec-primary);
1796
+ background: var(--ec-primary);
1797
+ color: #fff;
1798
+ }
1799
+
1800
+ .ec__config-btn:hover,
1801
+ .ec__config-segment button:hover {
1802
+ border-color: var(--ec-primary);
1803
+ color: var(--ec-primary);
1804
+ }
1805
+
1806
+ .ec__config-btn.is-primary:hover,
1807
+ .ec__config-segment button.is-active:hover {
1808
+ color: #fff;
1809
+ opacity: 0.9;
1810
+ }
1811
+
1812
+ .ec__config-btn:focus-visible,
1813
+ .ec__config-segment button:focus-visible,
1814
+ .ec__stepper:focus-within,
1815
+ .ec__reset-btn:focus-visible,
1816
+ .ec__config input[type='color']:focus-visible {
1817
+ outline: 2px solid var(--ec-primary);
1818
+ outline-offset: 2px;
1819
+ }
1820
+
1821
+ /* 自带 +/− 步进按钮,隐藏数字框原生上下箭头 */
1822
+ .ec__config input[type='number'] {
1823
+ -moz-appearance: textfield;
1824
+ appearance: textfield;
1825
+ }
1826
+
1827
+ .ec__config input[type='number']::-webkit-outer-spin-button,
1828
+ .ec__config input[type='number']::-webkit-inner-spin-button {
1829
+ -webkit-appearance: none;
1830
+ margin: 0;
1831
+ }
1832
+
1833
+ /* 取色器只保留色块 */
1834
+ .ec__config input[type='color'] {
1835
+ appearance: none;
1836
+ -webkit-appearance: none;
1837
+ width: 34px;
1838
+ height: 34px;
1839
+ padding: 0;
1840
+ border: 0;
1841
+ border-radius: 8px;
1842
+ background: transparent;
1843
+ cursor: pointer;
1844
+ }
1845
+
1846
+ .ec__config input[type='color']::-webkit-color-swatch-wrapper {
1847
+ padding: 0;
1848
+ }
1849
+
1850
+ .ec__config input[type='color']::-webkit-color-swatch,
1851
+ .ec__config input[type='color']::-moz-color-swatch {
1852
+ border: 1px solid var(--ec-border);
1853
+ border-radius: 8px;
1854
+ }
1855
+
1856
+ /* 字段网格:默认 5 列一排,卡片更小更矮 */
1857
+ .ec__config-list {
1858
+ display: grid;
1859
+ grid-template-columns: repeat(5, minmax(0, 1fr));
1860
+ gap: 8px;
1861
+ }
1862
+
1863
+ /* 每个字段一张白色小卡片 */
1864
+ .ec__config-block {
1865
+ min-width: 0;
1866
+ margin: 0;
1867
+ padding: 8px;
1868
+ border: 1px solid var(--ec-border);
1869
+ border-radius: 8px;
1870
+ background: #fff;
1871
+ }
1872
+
1873
+ .ec__config-block--seg {
1874
+ display: flex;
1875
+ flex-direction: column;
1876
+ }
1877
+
1878
+ .ec__config-label {
1879
+ display: flex;
1880
+ align-items: baseline;
1881
+ gap: 3px;
1882
+ flex-wrap: wrap;
1883
+ margin: 0 0 6px;
1884
+ color: var(--ec-text-secondary);
1885
+ font-size: 11px;
1886
+ line-height: 16px;
1887
+ }
1888
+
1889
+ .ec__config-block--color .ec__config-label {
1890
+ margin: 0;
1891
+ }
1892
+
1893
+ .ec__config-priority {
1894
+ color: var(--ec-primary);
1895
+ font-weight: 600;
1896
+ }
1897
+
1898
+ .ec__config-shorthand {
1899
+ margin-left: 6px;
1900
+ max-width: 55%;
1901
+ overflow: hidden;
1902
+ color: var(--ec-text-placeholder);
1903
+ font-family: Consolas, Monaco, monospace;
1904
+ font-size: 10px;
1905
+ font-weight: 400;
1906
+ text-overflow: ellipsis;
1907
+ white-space: nowrap;
1908
+ }
1909
+
1910
+ .ec__config-segment button {
1911
+ min-height: 28px;
1912
+ flex: 1;
1913
+ padding: 0 8px;
1914
+ }
1915
+
1916
+ .ec__config-segment button + button {
1917
+ margin-left: 6px;
1918
+ }
1919
+
1920
+ /* ------------ 间距控件:每个字段只拆「上下」「左右」两大块 ------------ */
1921
+ .ec__spacing {
1922
+ display: flex;
1923
+ flex-direction: column;
1924
+ gap: 4px;
1925
+ }
1926
+
1927
+ .ec__spacing-row {
1928
+ display: flex;
1929
+ align-items: center;
1930
+ gap: 6px;
1931
+ }
1932
+
1933
+ .ec__spacing-row-label {
1934
+ flex: none;
1935
+ width: 30px;
1936
+ color: var(--ec-text-secondary);
1937
+ font-size: 11px;
1938
+ }
1939
+
1940
+ .ec__spacing-row .ec__stepper {
1941
+ flex: 1;
1942
+ min-width: 0;
1943
+ }
1944
+
1945
+ /* ------------ 单值数值字段:输入框左右包 −/+ ------------ */
1946
+ .ec__stepper {
1947
+ display: flex;
1948
+ align-items: stretch;
1949
+ height: 30px;
1950
+ border: 1px solid var(--ec-border);
1951
+ border-radius: 6px;
1952
+ background: #fff;
1953
+ }
1954
+
1955
+ .ec__step-btn {
1956
+ flex: none;
1957
+ width: 27px;
1958
+ border: 0;
1959
+ background: transparent;
1960
+ color: var(--ec-text-secondary);
1961
+ font-size: 15px;
1962
+ line-height: 1;
1963
+ cursor: pointer;
1964
+ transition: color 0.16s, background-color 0.16s;
1965
+ }
1966
+
1967
+ .ec__step-btn:first-child {
1968
+ border-right: 1px solid var(--ec-border-light);
1969
+ }
1970
+
1971
+ .ec__step-btn:last-child {
1972
+ border-left: 1px solid var(--ec-border-light);
1973
+ }
1974
+
1975
+ .ec__step-btn:hover {
1976
+ color: var(--ec-primary);
1977
+ background: var(--ec-primary-ghost);
1978
+ }
1979
+
1980
+ .ec__stepper-input {
1981
+ position: relative;
1982
+ flex: 1;
1983
+ min-width: 0;
1984
+ }
1985
+
1986
+ .ec__stepper-input input {
1987
+ width: 100%;
1988
+ height: 100%;
1989
+ border: 0;
1990
+ outline: 0;
1991
+ padding: 0 22px 0 4px;
1992
+ background: transparent;
1993
+ color: var(--ec-text);
1994
+ font: inherit;
1995
+ font-size: 11px;
1996
+ text-align: center;
1997
+ }
1998
+
1999
+ .ec__stepper-unit {
2000
+ position: absolute;
2001
+ top: 50%;
2002
+ right: 6px;
2003
+ transform: translateY(-50%);
2004
+ color: var(--ec-text-placeholder);
2005
+ font-size: 10px;
2006
+ pointer-events: none;
2007
+ }
2008
+
2009
+ /* ------------ 颜色控件(并入字段网格的小卡片) ------------ */
2010
+ .ec__config-block--color {
2011
+ display: flex;
2012
+ align-items: center;
2013
+ gap: 8px;
2014
+ }
2015
+
2016
+ .ec__config-block--color input[type='color'] {
2017
+ width: 28px;
2018
+ height: 28px;
2019
+ border-radius: 6px;
2020
+ }
2021
+
2022
+ .ec__color-swatch {
2023
+ flex: none;
2024
+ width: 30px;
2025
+ height: 30px;
2026
+ }
2027
+
2028
+ .ec__color-meta {
2029
+ display: flex;
2030
+ flex: 1;
2031
+ flex-direction: column;
2032
+ min-width: 0;
2033
+ }
2034
+
2035
+ .ec__color-hex {
2036
+ margin-top: 1px;
2037
+ color: var(--ec-text);
2038
+ font-family: Consolas, Monaco, monospace;
2039
+ font-size: 11px;
2040
+ }
2041
+
2042
+ .ec__reset-btn {
2043
+ flex: none;
2044
+ height: 24px;
2045
+ padding: 0 7px;
2046
+ border: 1px solid var(--ec-border);
2047
+ border-radius: 6px;
2048
+ background: #fff;
2049
+ color: var(--ec-text-secondary);
2050
+ font-size: 10px;
2051
+ cursor: pointer;
2052
+ }
2053
+
2054
+ .ec__reset-btn:disabled {
2055
+ cursor: not-allowed;
2056
+ opacity: 0.45;
2057
+ }
2058
+
2059
+ .ec__reset-btn:hover:not(:disabled) {
2060
+ border-color: var(--ec-primary);
2061
+ color: var(--ec-primary);
2062
+ }
2063
+
2064
+ /* 卡片标题行右上角的「还原」 */
2065
+ .ec__config-reset {
2066
+ flex: none;
2067
+ align-self: center;
2068
+ margin-left: auto;
2069
+ padding: 0 6px;
2070
+ height: 20px;
2071
+ border: 1px solid transparent;
2072
+ border-radius: 5px;
2073
+ background: transparent;
2074
+ color: var(--ec-text-placeholder);
2075
+ font-size: 10px;
2076
+ line-height: 1;
2077
+ cursor: pointer;
2078
+ transition: color 0.16s, border-color 0.16s, background-color 0.16s;
2079
+ }
2080
+
2081
+ .ec__config-reset:hover {
2082
+ border-color: var(--ec-border);
2083
+ background: #fff;
2084
+ color: var(--ec-primary);
2085
+ }
2086
+
2087
+ /* ------------ 配置 JSON 输出区 ------------ */
2088
+ .ec__config-output {
2089
+ margin-top: 10px;
2090
+ }
2091
+
2092
+ .ec__config-json {
2093
+ max-height: 170px;
2094
+ margin: 0;
2095
+ padding: 10px 12px;
2096
+ overflow: auto;
2097
+ border-radius: 8px;
2098
+ background: #111827;
2099
+ color: #e5e7eb;
2100
+ font: 11px/1.6 Consolas, Monaco, monospace;
2101
+ white-space: pre-wrap;
2102
+ word-break: break-word;
2103
+ }
2104
+
2105
+ .ec__config-feedback {
2106
+ min-height: 16px;
2107
+ margin-top: 6px;
2108
+ color: #059669;
2109
+ font-size: 12px;
2110
+ }
2111
+
2112
+ @media (max-width: 1100px) {
2113
+ .ec__config-list {
2114
+ grid-template-columns: repeat(4, minmax(0, 1fr));
2115
+ }
2116
+ }
2117
+
2118
+ @media (max-width: 900px) {
2119
+ .ec__config-list {
2120
+ grid-template-columns: repeat(3, minmax(0, 1fr));
2121
+ }
2122
+ }
2123
+
2124
+ @media (max-width: 640px) {
2125
+ .ec__config-list {
2126
+ grid-template-columns: repeat(2, minmax(0, 1fr));
2127
+ }
2128
+ .ec__spacing-row-label {
2129
+ width: 26px;
2130
+ }
2131
+ }
2132
+
2133
+ @media (max-width: 420px) {
2134
+ .ec__config-list {
2135
+ grid-template-columns: 1fr;
2136
+ }
2137
+ }
2138
+
1123
2139
  .ec *,
1124
2140
  .ec *::before,
1125
2141
  .ec *::after {
@@ -1132,7 +2148,7 @@ export default {
1132
2148
  align-items: center;
1133
2149
  justify-content: space-between;
1134
2150
  gap: 16px;
1135
- padding: 4px 4px 16px;
2151
+ padding: var(--ec-header-padding);
1136
2152
  }
1137
2153
 
1138
2154
  .ec__header-left {
@@ -1263,7 +2279,7 @@ export default {
1263
2279
  display: flex;
1264
2280
  flex-direction: column;
1265
2281
  min-width: 0;
1266
- padding: 12px 10px;
2282
+ padding: var(--ec-cell-padding);
1267
2283
  border-right: 1px solid var(--ec-border-light);
1268
2284
  border-bottom: 1px solid var(--ec-border-light);
1269
2285
  transition: background-color 0.2s;
@@ -1372,31 +2388,52 @@ export default {
1372
2388
  display: flex;
1373
2389
  align-items: center;
1374
2390
  min-height: var(--ec-item-h);
1375
- padding: 4px 8px;
2391
+ padding: var(--ec-item-padding-y) var(--ec-item-padding-x);
1376
2392
  border-radius: 4px;
1377
2393
  background: var(--ec-item-bg);
1378
- color: var(--ec-text-secondary);
1379
- font-size: 13px;
2394
+ /* 未显式配置文字色时沿用次要文字色;hover 回落到主文字色 */
2395
+ color: var(--ec-item-text, var(--ec-text-secondary));
1380
2396
  cursor: pointer;
1381
2397
  transition: background-color 0.18s, color 0.18s, box-shadow 0.18s;
1382
2398
  }
1383
2399
 
1384
- /* 有角标时右侧让位,没有角标则占满,避免留白 */
1385
- .ec__item.has-badge {
1386
- padding-right: 30px;
1387
- }
1388
-
1389
2400
  /* 注意:外层 .ec__items 有 overflow-x: hidden(滚动条需要),
1390
- * 所以 hover 不能用水平位移,否则会被裁切;这里改用内阴影描边。 */
2401
+ * 所以 hover 不能用水平位移,否则会被裁切;这里改用内阴影。
2402
+ * 悬浮背景 = 在底色上叠一层半透明白(--ec-item-hover-mask),自动调淡一档;
2403
+ * 文字画在阴影之上,不受影响。item-bg-hover=false 时 mask 为透明。 */
1391
2404
  .ec__item:hover {
1392
- color: var(--ec-text);
1393
- box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.08);
2405
+ color: var(--ec-item-text, var(--ec-text));
2406
+ box-shadow:
2407
+ inset 0 0 0 9999px var(--ec-item-hover-mask, rgba(255, 255, 255, 0.18)),
2408
+ inset 0 0 0 1px rgba(0, 0, 0, 0.08);
2409
+ }
2410
+
2411
+ /* 渐变背景:纯色底上叠一层白色,默认上→下 */
2412
+ .ec__item.is-bg-gradient {
2413
+ background-image: linear-gradient(
2414
+ 180deg,
2415
+ rgba(255, 255, 255, 0.55) 0%,
2416
+ rgba(255, 255, 255, 0.12) 46%,
2417
+ rgba(255, 255, 255, 0) 74%
2418
+ );
2419
+ }
2420
+
2421
+ /* 渐变方向:左→右 */
2422
+ .ec__item.is-bg-gradient.is-bg-gradient-h {
2423
+ background-image: linear-gradient(
2424
+ 90deg,
2425
+ rgba(255, 255, 255, 0.55) 0%,
2426
+ rgba(255, 255, 255, 0.12) 46%,
2427
+ rgba(255, 255, 255, 0) 74%
2428
+ );
1394
2429
  }
1395
2430
 
1396
2431
  .ec__item.is-colorful:hover {
1397
2432
  background: var(--ec-item-accent-bg, var(--ec-item-bg));
1398
- color: var(--ec-item-accent, var(--ec-text));
1399
- box-shadow: inset 0 0 0 1px var(--ec-item-accent-bg, transparent);
2433
+ color: var(--ec-item-text, var(--ec-item-accent, var(--ec-text)));
2434
+ box-shadow:
2435
+ inset 0 0 0 9999px rgba(255, 255, 255, 0.15),
2436
+ inset 0 0 0 1px var(--ec-item-accent-bg, transparent);
1400
2437
  }
1401
2438
 
1402
2439
  .ec__item.is-disabled {
@@ -1408,14 +2445,48 @@ export default {
1408
2445
  box-shadow: none;
1409
2446
  }
1410
2447
 
1411
- .ec__item-label {
2448
+ .ec__item-content {
2449
+ display: flex;
1412
2450
  flex: 1;
2451
+ flex-direction: column;
2452
+ min-width: 0;
2453
+ }
2454
+
2455
+ .ec--item-center .ec__item {
2456
+ justify-content: center;
2457
+ }
2458
+
2459
+ .ec--item-center .ec__item-content {
2460
+ align-items: center;
2461
+ text-align: center;
2462
+ }
2463
+
2464
+ .ec__item-label,
2465
+ .ec__item-address {
2466
+ display: block;
1413
2467
  min-width: 0;
2468
+ max-width: 100%;
1414
2469
  overflow: hidden;
1415
2470
  white-space: nowrap;
1416
2471
  text-overflow: ellipsis;
1417
2472
  }
1418
2473
 
2474
+ .ec__item-label {
2475
+ font-size: 14px;
2476
+ line-height: 16px;
2477
+ }
2478
+
2479
+ .ec__item-label.is-long {
2480
+ font-size: 12px;
2481
+ }
2482
+
2483
+ .ec__item-address {
2484
+ /* 名称与地址的行间距可配(item-line-gap) */
2485
+ margin-top: var(--ec-item-line-gap, 2px);
2486
+ font-size: 12px;
2487
+ line-height: 16px;
2488
+ }
2489
+
1419
2490
  /* 角标:贴在条目右上角,与设计图一致 */
1420
2491
  .ec__item-badge {
1421
2492
  position: absolute;
@@ -1425,10 +2496,14 @@ export default {
1425
2496
  border-radius: 0 4px 0 4px;
1426
2497
  background: rgba(0, 0, 0, 0.06);
1427
2498
  color: var(--ec-text-placeholder);
1428
- font-size: 11px;
2499
+ font-size: 12px;
1429
2500
  line-height: 16px;
1430
2501
  white-space: nowrap;
1431
2502
  cursor: pointer;
2503
+ /* 字号保持 12px 渲染,scale 倍率由配置 badge-scale 控制(默认 0.75);
2504
+ * 锚点在右上角,缩放后仍紧贴条目右上角,不占内容宽度 */
2505
+ transform: scale(var(--ec-badge-scale, 0.75));
2506
+ transform-origin: top right;
1432
2507
  transition: filter 0.18s;
1433
2508
  }
1434
2509
 
@@ -1618,6 +2693,17 @@ export default {
1618
2693
 
1619
2694
  /* ------------------------------ 响应式 ------------------------------ */
1620
2695
  @media (max-width: 768px) {
2696
+ .ec__config-head {
2697
+ align-items: flex-start;
2698
+ flex-direction: column;
2699
+ }
2700
+ .ec__config-actions {
2701
+ width: 100%;
2702
+ }
2703
+ .ec__config-btn {
2704
+ flex: 1;
2705
+ min-height: 40px;
2706
+ }
1621
2707
  .ec__header {
1622
2708
  flex-direction: column;
1623
2709
  align-items: stretch;
@@ -1627,20 +2713,8 @@ export default {
1627
2713
  justify-content: flex-end;
1628
2714
  }
1629
2715
  .ec__cell {
1630
- padding: 8px 6px;
1631
- }
1632
- .ec__item {
1633
- font-size: 12px;
1634
- }
1635
- /* 窄屏下角标改为常规内联,不再绝对定位,因此收回右侧让位的内边距。
1636
- * 这里必须带 .has-badge,否则选择器权重低于上面的 .ec__item.has-badge。 */
1637
- .ec__item.has-badge {
1638
- padding-right: 8px;
1639
- }
1640
- .ec__item-badge {
1641
- position: static;
1642
- margin-left: 6px;
1643
- border-radius: 3px;
2716
+ padding: var(--ec-cell-padding-mobile);
1644
2717
  }
2718
+ /* 角标在任何屏宽下都绝对定位、悬浮于条目右上角,不占内容宽度 */
1645
2719
  }
1646
2720
  </style>