@opentiny/vue-docs 3.28.1 → 3.30.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 (285) hide show
  1. package/demos/apis/action-menu.js +22 -0
  2. package/demos/apis/alert.js +19 -22
  3. package/demos/apis/button-group.js +15 -0
  4. package/demos/apis/calendar-view.js +5 -5
  5. package/demos/apis/cascader-view.js +5 -5
  6. package/demos/apis/dialog-box.js +39 -38
  7. package/demos/apis/drawer.js +31 -0
  8. package/demos/apis/dropdown.js +39 -1
  9. package/demos/apis/filter-panel.js +21 -1
  10. package/demos/apis/fluent-editor.js +30 -0
  11. package/demos/apis/form.js +11 -0
  12. package/demos/apis/fullscreen.js +10 -11
  13. package/demos/apis/grid.js +131 -16
  14. package/demos/apis/guide.js +14 -0
  15. package/demos/apis/link.js +11 -0
  16. package/demos/apis/message.js +48 -22
  17. package/demos/apis/modal.js +34 -31
  18. package/demos/apis/notify.js +13 -13
  19. package/demos/apis/rate.js +15 -0
  20. package/demos/apis/search-box.js +343 -0
  21. package/demos/apis/select.js +14 -2
  22. package/demos/apis/tag-input.js +182 -0
  23. package/demos/apis/tree.js +23 -23
  24. package/demos/mobile-first/app/button-group/display-mode.vue +31 -0
  25. package/demos/mobile-first/app/button-group/webdoc/button-group.js +13 -0
  26. package/demos/mobile-first/app/calendar-view/webdoc/calendar-view.js +5 -4
  27. package/demos/mobile-first/app/modal/webdoc/modal.js +2 -1
  28. package/demos/pc/app/alert/center.spec.ts +8 -0
  29. package/demos/pc/app/alert/custom-close.spec.ts +39 -9
  30. package/demos/pc/app/alert/icon.spec.ts +18 -7
  31. package/demos/pc/app/alert/show-icon.spec.ts +1 -1
  32. package/demos/pc/app/anchor/webdoc/anchor.js +1 -0
  33. package/demos/pc/app/base-select/copy-multi.spec.ts +1 -1
  34. package/demos/pc/app/button-group/display-mode-composition-api.vue +23 -0
  35. package/demos/pc/app/button-group/display-mode.spec.ts +0 -0
  36. package/demos/pc/app/button-group/display-mode.vue +31 -0
  37. package/demos/pc/app/button-group/sup-composition-api.vue +1 -0
  38. package/demos/pc/app/button-group/sup.vue +1 -0
  39. package/demos/pc/app/button-group/webdoc/button-group.js +14 -0
  40. package/demos/pc/app/calendar-view/calendar-event.spec.ts +1 -1
  41. package/demos/pc/app/calendar-view/custom-day-bg-color.vue +10 -0
  42. package/demos/pc/app/calendar-view/webdoc/calendar-view.js +4 -4
  43. package/demos/pc/app/cascader/filter-mode-composition-api.vue +2 -3
  44. package/demos/pc/app/checkbox/checked-composition-api.vue +3 -3
  45. package/demos/pc/app/checkbox/checked.vue +3 -3
  46. package/demos/pc/app/date-picker/align.spec.ts +10 -13
  47. package/demos/pc/app/date-picker/clear.spec.ts +3 -3
  48. package/demos/pc/app/date-picker/default-value.spec.ts +4 -4
  49. package/demos/pc/app/date-picker/events.spec.ts +2 -2
  50. package/demos/pc/app/date-picker/format.spec.ts +3 -3
  51. package/demos/pc/app/date-picker/now.spec.ts +4 -4
  52. package/demos/pc/app/date-picker/shortcuts.spec.ts +1 -1
  53. package/demos/pc/app/date-picker/slot.spec.ts +5 -5
  54. package/demos/pc/app/dialog-box/basic-usage.spec.ts +5 -5
  55. package/demos/pc/app/dialog-box/form-in-dialog.spec.ts +1 -2
  56. package/demos/pc/app/drawer/basic-usage.spec.ts +4 -3
  57. package/demos/pc/app/drawer/close-on-press-escape.spec.ts +4 -2
  58. package/demos/pc/app/drawer/placement.spec.ts +4 -4
  59. package/demos/pc/app/drawer/show-header.spec.ts +3 -2
  60. package/demos/pc/app/drawer/webdoc/drawer.js +3 -2
  61. package/demos/pc/app/dropdown/show-icon-composition-api.vue +1 -1
  62. package/demos/pc/app/dropdown/show-icon.vue +1 -1
  63. package/demos/pc/app/dropdown/tip-composition-api.vue +5 -2
  64. package/demos/pc/app/dropdown/tip.vue +5 -2
  65. package/demos/pc/app/dropdown/webdoc/dropdown.js +2 -2
  66. package/demos/pc/app/filter-panel/size-composition-api.vue +58 -0
  67. package/demos/pc/app/filter-panel/size.spec.ts +23 -0
  68. package/demos/pc/app/filter-panel/size.vue +68 -0
  69. package/demos/pc/app/filter-panel/webdoc/filter-panel.js +13 -0
  70. package/demos/pc/app/flowchart/basic-usage-composition-api.vue +10 -260
  71. package/demos/pc/app/flowchart/basic-usage.spec.ts +2 -1
  72. package/demos/pc/app/flowchart/basic-usage.vue +4 -249
  73. package/demos/pc/app/flowchart/slots-composition-api.vue +182 -0
  74. package/demos/pc/app/flowchart/slots.spec.ts +24 -0
  75. package/demos/pc/app/flowchart/slots.vue +193 -0
  76. package/demos/pc/app/flowchart/webdoc/flowchart.js +14 -2
  77. package/demos/pc/app/form/basic-usage.spec.ts +6 -4
  78. package/demos/pc/app/form/custom-validation-string-length-composition-api.vue +38 -0
  79. package/demos/pc/app/form/custom-validation-string-length.spec.ts +18 -0
  80. package/demos/pc/app/form/custom-validation-string-length.vue +28 -13
  81. package/demos/pc/app/form/group-form-composition-api.vue +2 -2
  82. package/demos/pc/app/form/group-form.vue +2 -2
  83. package/demos/pc/app/grid/filter/advanced-filter-composition-api.vue +1 -1
  84. package/demos/pc/app/grid/filter/advanced-filter.spec.js +1 -0
  85. package/demos/pc/app/grid/filter/advanced-filter.vue +1 -1
  86. package/demos/pc/app/grid/filter/custom-filter-composition-api.vue +46 -23
  87. package/demos/pc/app/grid/filter/custom-filter.spec.js +1 -1
  88. package/demos/pc/app/grid/filter/custom-filter.vue +47 -24
  89. package/demos/pc/app/grid/filter/default-filter-composition-api.vue +5 -1
  90. package/demos/pc/app/grid/filter/default-filter.spec.ts +1 -0
  91. package/demos/pc/app/grid/filter/default-filter.vue +4 -1
  92. package/demos/pc/app/grid/filter/filter-label-value-composition-api.vue +71 -0
  93. package/demos/pc/app/grid/filter/filter-label-value.spec.ts +27 -0
  94. package/demos/pc/app/grid/filter/filter-label-value.vue +78 -0
  95. package/demos/pc/app/grid/filter/input-filter-custom-component-composition-api.vue +116 -0
  96. package/demos/pc/app/grid/filter/input-filter-custom-component.spec.ts +20 -0
  97. package/demos/pc/app/grid/filter/input-filter-custom-component.vue +126 -0
  98. package/demos/pc/app/grid/filter/layout-order-filter-composition-api.vue +114 -0
  99. package/demos/pc/app/grid/filter/layout-order-filter.spec.ts +31 -0
  100. package/demos/pc/app/grid/filter/layout-order-filter.vue +123 -0
  101. package/demos/pc/app/grid/toolbar/custom-toolbar.spec.js +1 -1
  102. package/demos/pc/app/grid/webdoc/grid-filter.js +89 -40
  103. package/demos/pc/app/guide/show-close-composition-api.vue +77 -0
  104. package/demos/pc/app/guide/show-close.spec.ts +37 -0
  105. package/demos/pc/app/guide/show-close.vue +84 -0
  106. package/demos/pc/app/guide/webdoc/guide.js +12 -0
  107. package/demos/pc/app/icon/advance-icons.js +108 -2
  108. package/demos/pc/app/icon/advance-usage-composition-api.vue +26 -1
  109. package/demos/pc/app/icon/advance-usage.vue +27 -1
  110. package/demos/pc/app/icon/webdoc/icon.js +5 -3
  111. package/demos/pc/app/input/resize.spec.ts +2 -2
  112. package/demos/pc/app/link/custom-icon-composition-api.vue +1 -1
  113. package/demos/pc/app/link/custom-icon.vue +1 -1
  114. package/demos/pc/app/link/target-composition-api.vue +24 -0
  115. package/demos/pc/app/link/target.spec.ts +41 -0
  116. package/demos/pc/app/link/target.vue +30 -0
  117. package/demos/pc/app/link/webdoc/link.js +14 -0
  118. package/demos/pc/app/message/basic-usage-composition-api.vue +48 -0
  119. package/demos/pc/app/message/basic-usage.spec.ts +27 -0
  120. package/demos/pc/app/message/basic-usage.vue +50 -0
  121. package/demos/pc/app/message/message-close-composition-api.vue +25 -0
  122. package/demos/pc/app/message/message-close.spec.ts +9 -0
  123. package/demos/pc/app/message/message-close.vue +32 -0
  124. package/demos/pc/app/message/message-duration-composition-api.vue +25 -0
  125. package/demos/pc/app/message/message-duration.spec.ts +9 -0
  126. package/demos/pc/app/message/message-duration.vue +32 -0
  127. package/demos/pc/app/message/prevent-duplicate-composition-api.vue +25 -0
  128. package/demos/pc/app/message/prevent-duplicate.spec.ts +9 -0
  129. package/demos/pc/app/message/prevent-duplicate.vue +32 -0
  130. package/demos/pc/app/message/webdoc/message.cn.md +7 -0
  131. package/demos/pc/app/message/webdoc/message.en.md +7 -0
  132. package/demos/pc/app/message/webdoc/message.js +56 -0
  133. package/demos/pc/app/modal/basic-usage.spec.ts +3 -3
  134. package/demos/pc/app/modal/modal-footer-composition-api.vue +3 -3
  135. package/demos/pc/app/modal/modal-footer.spec.ts +9 -4
  136. package/demos/pc/app/modal/modal-footer.vue +3 -3
  137. package/demos/pc/app/modal/modal-header.spec.ts +2 -2
  138. package/demos/pc/app/modal/modal-size.spec.ts +2 -2
  139. package/demos/pc/app/modal/status.spec.ts +22 -15
  140. package/demos/pc/app/nav-menu/overflow.spec.ts +1 -1
  141. package/demos/pc/app/popconfirm/custom-class.spec.ts +0 -1
  142. package/demos/pc/app/popconfirm/slot-message.spec.ts +10 -2
  143. package/demos/pc/app/popconfirm/trigger.spec.ts +16 -12
  144. package/demos/pc/app/popover/basic-usage.spec.js +3 -8
  145. package/demos/pc/app/popover/custom-popper.spec.js +2 -2
  146. package/demos/pc/app/popover/delay.spec.js +2 -2
  147. package/demos/pc/app/popover/disabled.spec.js +2 -2
  148. package/demos/pc/app/popover/events.spec.js +1 -1
  149. package/demos/pc/app/popover/ignore-boundaries.spec.js +2 -3
  150. package/demos/pc/app/popover/offset.spec.js +17 -31
  151. package/demos/pc/app/radio/dynamic-disable-composition-api.vue +3 -3
  152. package/demos/pc/app/radio/dynamic-disable.vue +2 -2
  153. package/demos/pc/app/rate/clearable-composition-api.vue +20 -0
  154. package/demos/pc/app/rate/clearable.spec.js +74 -0
  155. package/demos/pc/app/rate/clearable.vue +28 -0
  156. package/demos/pc/app/rate/webdoc/rate.js +12 -0
  157. package/demos/pc/app/search/events.spec.ts +1 -1
  158. package/demos/pc/app/search-box/append-to-body-composition-api.vue +300 -0
  159. package/demos/pc/app/search-box/append-to-body.vue +308 -0
  160. package/demos/pc/app/search-box/auto-match-composition-api.vue +162 -0
  161. package/demos/pc/app/search-box/auto-match.vue +170 -0
  162. package/demos/pc/app/search-box/basic-usage-composition-api.vue +269 -0
  163. package/demos/pc/app/search-box/basic-usage.vue +277 -0
  164. package/demos/pc/app/search-box/custom-panel-composition-api.vue +63 -0
  165. package/demos/pc/app/search-box/custom-panel.vue +72 -0
  166. package/demos/pc/app/search-box/default-field-composition-api.vue +163 -0
  167. package/demos/pc/app/search-box/default-field.vue +171 -0
  168. package/demos/pc/app/search-box/editable-composition-api.vue +311 -0
  169. package/demos/pc/app/search-box/editable.vue +320 -0
  170. package/demos/pc/app/search-box/empty-placeholder-composition-api.vue +162 -0
  171. package/demos/pc/app/search-box/empty-placeholder.vue +170 -0
  172. package/demos/pc/app/search-box/events-composition-api.vue +169 -0
  173. package/demos/pc/app/search-box/events.vue +173 -0
  174. package/demos/pc/app/search-box/group-key-composition-api.vue +257 -0
  175. package/demos/pc/app/search-box/group-key.vue +266 -0
  176. package/demos/pc/app/search-box/help-composition-api.vue +166 -0
  177. package/demos/pc/app/search-box/help.vue +175 -0
  178. package/demos/pc/app/search-box/id-map-key-composition-api.vue +131 -0
  179. package/demos/pc/app/search-box/id-map-key.vue +140 -0
  180. package/demos/pc/app/search-box/item-placeholder-composition-api.vue +119 -0
  181. package/demos/pc/app/search-box/item-placeholder.vue +127 -0
  182. package/demos/pc/app/search-box/max-length-composition-api.vue +29 -0
  183. package/demos/pc/app/search-box/max-length.vue +38 -0
  184. package/demos/pc/app/search-box/max-time-length-composition-api.vue +53 -0
  185. package/demos/pc/app/search-box/max-time-length.vue +62 -0
  186. package/demos/pc/app/search-box/merge-tag-composition-api.vue +59 -0
  187. package/demos/pc/app/search-box/merge-tag.vue +68 -0
  188. package/demos/pc/app/search-box/panel-max-height-composition-api.vue +162 -0
  189. package/demos/pc/app/search-box/panel-max-height.vue +170 -0
  190. package/demos/pc/app/search-box/potential-match-composition-api.vue +193 -0
  191. package/demos/pc/app/search-box/potential-match.vue +201 -0
  192. package/demos/pc/app/search-box/settings-composition-api.vue +163 -0
  193. package/demos/pc/app/search-box/settings.vue +171 -0
  194. package/demos/pc/app/search-box/size-composition-api.vue +162 -0
  195. package/demos/pc/app/search-box/size.vue +170 -0
  196. package/demos/pc/app/search-box/split-input-value-composition-api.vue +162 -0
  197. package/demos/pc/app/search-box/split-input-value.vue +170 -0
  198. package/demos/pc/app/search-box/suffix-icon-composition-api.vue +209 -0
  199. package/demos/pc/app/search-box/suffix-icon.vue +219 -0
  200. package/demos/pc/app/search-box/v-model-composition-api.vue +174 -0
  201. package/demos/pc/app/search-box/v-model.vue +182 -0
  202. package/demos/pc/app/search-box/webdoc/search-box.cn.md +7 -0
  203. package/demos/pc/app/search-box/webdoc/search-box.en.md +7 -0
  204. package/demos/pc/app/search-box/webdoc/search-box.js +257 -0
  205. package/demos/pc/app/select/copy-multi.spec.ts +1 -1
  206. package/demos/pc/app/select-wrapper/copy-multi.spec.ts +2 -1
  207. package/demos/pc/app/tabs/basic-usage.spec.ts +2 -2
  208. package/demos/pc/app/tabs/more-show-all.spec.ts +2 -1
  209. package/demos/pc/app/tabs/tabs-events-close-composition-api.vue +8 -2
  210. package/demos/pc/app/tabs/tabs-events-close.vue +8 -2
  211. package/demos/pc/app/tag/basic-usage.spec.ts +1 -1
  212. package/demos/pc/app/tag/color-border.spec.ts +2 -2
  213. package/demos/pc/app/tag/max-width.spec.ts +1 -1
  214. package/demos/pc/app/tag/size.spec.ts +3 -3
  215. package/demos/pc/app/tag/slot-default.spec.ts +3 -3
  216. package/demos/pc/app/tag-group/basic-usage.spec.js +5 -5
  217. package/demos/pc/app/tag-group/tag-group-effect.spec.js +10 -10
  218. package/demos/pc/app/tag-group/tag-group-event.spec.js +1 -1
  219. package/demos/pc/app/tag-input/basic-usage-composition-api.vue +29 -0
  220. package/demos/pc/app/tag-input/basic-usage.spec.ts +0 -0
  221. package/demos/pc/app/tag-input/basic-usage.vue +38 -0
  222. package/demos/pc/app/tag-input/clearable-tag-composition-api.vue +20 -0
  223. package/demos/pc/app/tag-input/clearable-tag.vue +27 -0
  224. package/demos/pc/app/tag-input/collapsed-tag-composition-api.vue +20 -0
  225. package/demos/pc/app/tag-input/collapsed-tag.vue +28 -0
  226. package/demos/pc/app/tag-input/disabled-readonly-composition-api.vue +20 -0
  227. package/demos/pc/app/tag-input/disabled-readonly.vue +29 -0
  228. package/demos/pc/app/tag-input/draggable-tag-composition-api.vue +12 -0
  229. package/demos/pc/app/tag-input/draggable-tag.spec.ts +0 -0
  230. package/demos/pc/app/tag-input/draggable-tag.vue +20 -0
  231. package/demos/pc/app/tag-input/max-composition-api.vue +20 -0
  232. package/demos/pc/app/tag-input/max.vue +28 -0
  233. package/demos/pc/app/tag-input/prefix-suffix-composition-api.vue +26 -0
  234. package/demos/pc/app/tag-input/prefix-suffix.vue +34 -0
  235. package/demos/pc/app/tag-input/separator-tag-composition-api.vue +20 -0
  236. package/demos/pc/app/tag-input/separator-tag.vue +28 -0
  237. package/demos/pc/app/tag-input/webdoc/tag-input.cn.md +7 -0
  238. package/demos/pc/app/tag-input/webdoc/tag-input.en.md +7 -0
  239. package/demos/pc/app/tag-input/webdoc/tag-input.js +107 -0
  240. package/demos/pc/app/time-picker/basic-usage.spec.ts +5 -4
  241. package/demos/pc/app/time-picker/clearable.spec.ts +5 -4
  242. package/demos/pc/app/time-picker/default-value.spec.ts +2 -2
  243. package/demos/pc/app/time-picker/disabled.spec.ts +4 -3
  244. package/demos/pc/app/time-picker/format.spec.ts +8 -11
  245. package/demos/pc/app/time-picker/is-range.spec.ts +4 -3
  246. package/demos/pc/app/time-picker/name.spec.ts +1 -1
  247. package/demos/pc/app/time-picker/placeholder.spec.ts +3 -3
  248. package/demos/pc/app/time-picker/popper-class.spec.ts +1 -1
  249. package/demos/pc/app/time-picker/size.spec.ts +2 -1
  250. package/demos/pc/app/time-select/basic-usage.spec.ts +1 -1
  251. package/demos/pc/app/time-select/clear-icon.spec.ts +1 -2
  252. package/demos/pc/app/time-select/focus.spec.ts +1 -1
  253. package/demos/pc/app/time-select/picker-options.spec.ts +2 -1
  254. package/demos/pc/app/time-select/range-placeholder.spec.ts +2 -2
  255. package/demos/pc/app/time-select/size-medium.spec.ts +3 -3
  256. package/demos/pc/app/tooltip/delay.spec.js +7 -9
  257. package/demos/pc/app/tooltip/offset.spec.js +0 -2
  258. package/demos/pc/app/tooltip/popper-options.spec.js +2 -1
  259. package/demos/pc/menus.js +6 -4
  260. package/demos/pc/webdoc/changelog-en.md +209 -86
  261. package/demos/pc/webdoc/changelog.md +176 -257
  262. package/demos/pc/webdoc/develop-demo-en.md +13 -13
  263. package/demos/pc/webdoc/import-components.md +1 -1
  264. package/demos/pc/webdoc/introduce.md +2 -0
  265. package/demos/pc/webdoc/skills-en.md +107 -0
  266. package/demos/pc/webdoc/skills.md +107 -0
  267. package/demos/pc/webdoc/theme-en.md +82 -79
  268. package/demos/pc/webdoc/v3.28.0-release-article.md +943 -0
  269. package/demos/saas/menus.js +1 -0
  270. package/package.json +21 -20
  271. package/playground/App.vue +6 -4
  272. package/playground/shims/vue-search-box.mjs +154 -0
  273. package/public/static/css/mobile-dark-theme.css +1 -0
  274. package/src/i18n/index.js +1 -2
  275. package/src/main.js +8 -5
  276. package/src/style.css +5 -0
  277. package/src/views/components-doc/common.vue +0 -25
  278. package/src/views/components-doc/mobile.vue +49 -2
  279. package/vite.config.ts +3 -2
  280. package/vite.extend.ts +65 -0
  281. package/demos/pc/app/grid/webdoc/grid-ai-agent.js +0 -23
  282. package/demos/pc/webdoc/mcp-en.md +0 -101
  283. package/demos/pc/webdoc/mcp.md +0 -101
  284. package/src/components/mcp-docs.vue +0 -33
  285. package/src/composable/useTinyRemoter.ts +0 -176
@@ -3,28 +3,23 @@
3
3
  <tiny-grid-column type="index" width="60"></tiny-grid-column>
4
4
  <tiny-grid-column field="name" title="名称" :filter="customFilter">
5
5
  <template #filter="data">
6
- <input v-model="customFilterData.input" />
7
- <button @click="data.context.commitFilter(customFilter)">confirm</button>
8
- <button
9
- @click="
10
- () => {
11
- customFilterData.input = ''
12
- data.context.clearFilter()
13
- }
14
- "
15
- >
16
- clear
17
- </button>
18
- <button
19
- @click="
20
- () => {
21
- customFilterData.input = ''
22
- data.context.resetFilter()
23
- }
24
- "
25
- >
26
- reset
27
- </button>
6
+ <ul class="tiny-grid__filter-panel custom-filter-panel">
7
+ <li class="filter-option__input">
8
+ <tiny-input
9
+ v-model="customFilterData.input"
10
+ placeholder="输入名称关键词筛选"
11
+ clearable
12
+ size="small"
13
+ />
14
+ </li>
15
+ <li class="tiny-grid__filter-option filter-option__btns">
16
+ <tiny-button type="primary" size="small" @click="data.context.commitFilter(customFilter)">
17
+ 确定
18
+ </tiny-button>
19
+ <tiny-button size="small" @click="handleReset(data.context)">重置</tiny-button>
20
+ <tiny-button size="small" @click="handleClear(data.context)">清除全部</tiny-button>
21
+ </li>
22
+ </ul>
28
23
  </template>
29
24
  </tiny-grid-column>
30
25
  <tiny-grid-column field="area" title="区域"></tiny-grid-column>
@@ -35,7 +30,7 @@
35
30
 
36
31
  <script setup lang="jsx">
37
32
  import { ref } from 'vue'
38
- import { TinyGrid, TinyGridColumn } from '@opentiny/vue'
33
+ import { TinyGrid, TinyGridColumn, TinyInput, TinyButton } from '@opentiny/vue'
39
34
 
40
35
  const tableData = ref([
41
36
  {
@@ -89,4 +84,32 @@ const customFilter = ref({ method: customFilterMethod })
89
84
  function customFilterMethod({ row }) {
90
85
  return row.name.includes(customFilterData.value.input)
91
86
  }
87
+
88
+ function handleReset(context) {
89
+ customFilterData.value.input = ''
90
+ context.resetFilter()
91
+ }
92
+
93
+ function handleClear(context) {
94
+ customFilterData.value.input = ''
95
+ context.clearFilter()
96
+ }
92
97
  </script>
98
+
99
+ <style scoped>
100
+ .custom-filter-panel {
101
+ padding: 12px 16px;
102
+ min-width: 260px;
103
+ }
104
+ .custom-filter-panel .filter-option__input {
105
+ margin-bottom: 12px;
106
+ }
107
+ .custom-filter-panel .filter-option__input :deep(.tiny-input) {
108
+ width: 100%;
109
+ }
110
+ .custom-filter-panel .filter-option__btns {
111
+ display: flex;
112
+ gap: 8px;
113
+ justify-content: flex-end;
114
+ }
115
+ </style>
@@ -7,6 +7,6 @@ test('列筛选规则', async ({ page }) => {
7
7
  await page.locator('.tiny-grid__filter-wrapper.filter__active input').click()
8
8
  await page.locator('.tiny-grid__filter-wrapper.filter__active input').press('CapsLock')
9
9
  await page.locator('.tiny-grid__filter-wrapper.filter__active input').fill('WWW')
10
- await page.getByRole('button', { name: 'confirm' }).click()
10
+ await page.getByRole('button', { name: '确定' }).click()
11
11
  await expect(page.getByRole('cell', { name: 'WWW 科技 YX 公司' })).toBeVisible()
12
12
  })
@@ -3,28 +3,23 @@
3
3
  <tiny-grid-column type="index" width="60"></tiny-grid-column>
4
4
  <tiny-grid-column field="name" title="名称" :filter="customFilter">
5
5
  <template #filter="data">
6
- <input v-model="customFilterData.input" />
7
- <button @click="data.context.commitFilter(customFilter)">confirm</button>
8
- <button
9
- @click="
10
- () => {
11
- customFilterData.input = ''
12
- data.context.clearFilter()
13
- }
14
- "
15
- >
16
- clear
17
- </button>
18
- <button
19
- @click="
20
- () => {
21
- customFilterData.input = ''
22
- data.context.resetFilter()
23
- }
24
- "
25
- >
26
- reset
27
- </button>
6
+ <ul class="tiny-grid__filter-panel custom-filter-panel">
7
+ <li class="filter-option__input">
8
+ <tiny-input
9
+ v-model="customFilterData.input"
10
+ placeholder="输入名称关键词筛选"
11
+ clearable
12
+ size="small"
13
+ />
14
+ </li>
15
+ <li class="tiny-grid__filter-option filter-option__btns">
16
+ <tiny-button type="primary" size="small" @click="data.context.commitFilter(customFilter)">
17
+ 确定
18
+ </tiny-button>
19
+ <tiny-button size="small" @click="handleReset(data.context)">重置</tiny-button>
20
+ <tiny-button size="small" @click="handleClear(data.context)">清除全部</tiny-button>
21
+ </li>
22
+ </ul>
28
23
  </template>
29
24
  </tiny-grid-column>
30
25
  <tiny-grid-column field="area" title="区域"></tiny-grid-column>
@@ -34,12 +29,14 @@
34
29
  </template>
35
30
 
36
31
  <script lang="jsx">
37
- import { TinyGrid, TinyGridColumn } from '@opentiny/vue'
32
+ import { TinyGrid, TinyGridColumn, TinyInput, TinyButton } from '@opentiny/vue'
38
33
 
39
34
  export default {
40
35
  components: {
41
36
  TinyGrid,
42
- TinyGridColumn
37
+ TinyGridColumn,
38
+ TinyInput,
39
+ TinyButton
43
40
  },
44
41
  data() {
45
42
  return {
@@ -96,7 +93,33 @@ export default {
96
93
  methods: {
97
94
  customFilterMethod({ row }) {
98
95
  return row.name.includes(this.customFilterData.input)
96
+ },
97
+ handleReset(context) {
98
+ this.customFilterData.input = ''
99
+ context.resetFilter()
100
+ },
101
+ handleClear(context) {
102
+ this.customFilterData.input = ''
103
+ context.clearFilter()
99
104
  }
100
105
  }
101
106
  }
102
107
  </script>
108
+
109
+ <style scoped>
110
+ .custom-filter-panel {
111
+ padding: 12px 16px;
112
+ min-width: 260px;
113
+ }
114
+ .custom-filter-panel .filter-option__input {
115
+ margin-bottom: 12px;
116
+ }
117
+ .custom-filter-panel .filter-option__input :deep(.tiny-input) {
118
+ width: 100%;
119
+ }
120
+ .custom-filter-panel .filter-option__btns {
121
+ display: flex;
122
+ gap: 8px;
123
+ justify-content: flex-end;
124
+ }
125
+ </style>
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <tiny-grid :data="tableData" @toolbar-button-click="toolbarButtonClickEvent">
2
+ <tiny-grid :data="tableData" :filter-popper-options="popperConfigs" @toolbar-button-click="toolbarButtonClickEvent">
3
3
  <template #toolbar>
4
4
  <tiny-grid-toolbar :buttons="toolbarButtons"> </tiny-grid-toolbar>
5
5
  </template>
@@ -30,6 +30,10 @@ const filteData = [
30
30
  { label: '韶关', value: '韶关' }
31
31
  ]
32
32
 
33
+ const popperConfigs = {
34
+ bubbling: true
35
+ }
36
+
33
37
  const toolbarButtons = ref([
34
38
  {
35
39
  code: 'clearFilter',
@@ -3,6 +3,7 @@ import { test, expect } from '@playwright/test'
3
3
  test('表格过滤', async ({ page }) => {
4
4
  page.on('pageerror', (exception) => expect(exception).toBeNull())
5
5
  await page.goto('grid-filter#filter-default-filter')
6
+ await page.setViewportSize({ width: 1920, height: 1980 })
6
7
  await page
7
8
  .getByRole('cell', { name: '公司名称鼠标移入可以动态提示 tooltip,公司名称鼠标移入可以动态提示 tooltip' })
8
9
  .getByRole('img')
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <tiny-grid :data="tableData" @toolbar-button-click="toolbarButtonClickEvent">
2
+ <tiny-grid :data="tableData" :filter-popper-options="popperConfigs" @toolbar-button-click="toolbarButtonClickEvent">
3
3
  <template #toolbar>
4
4
  <tiny-grid-toolbar :buttons="toolbarButtons"> </tiny-grid-toolbar>
5
5
  </template>
@@ -37,6 +37,9 @@ export default {
37
37
  },
38
38
  data() {
39
39
  return {
40
+ popperConfigs: {
41
+ bubbling: true
42
+ },
40
43
  toolbarButtons: [
41
44
  {
42
45
  code: 'clearFilter',
@@ -0,0 +1,71 @@
1
+ <template>
2
+ <tiny-grid :data="tableData" @filter-change="filterChangeEvent">
3
+ <tiny-grid-column type="index" width="60"></tiny-grid-column>
4
+ <tiny-grid-column
5
+ field="status"
6
+ title="状态"
7
+ :filter="statusFilter"
8
+ :formatter="statusFormatter"
9
+ ></tiny-grid-column>
10
+ <tiny-grid-column
11
+ field="region"
12
+ title="区域"
13
+ :filter="regionFilter"
14
+ :formatter="regionFormatter"
15
+ ></tiny-grid-column>
16
+ </tiny-grid>
17
+ </template>
18
+
19
+ <script setup>
20
+ import { ref } from 'vue'
21
+ import { TinyGrid, TinyGridColumn, TinyModal } from '@opentiny/vue'
22
+
23
+ const tableData = ref([
24
+ { id: '1', status: 'active', region: 'east' },
25
+ { id: '2', status: 'inactive', region: 'south' },
26
+ { id: '3', status: 'active', region: 'west' },
27
+ { id: '4', status: 'pending', region: 'east' }
28
+ ])
29
+
30
+ const statusFilter = ref({
31
+ multi: true,
32
+ enumable: true,
33
+ inputFilter: false,
34
+ defaultFilter: false,
35
+ label: 'name',
36
+ value: 'code',
37
+ values: [
38
+ { code: 'active', name: '已启用' },
39
+ { code: 'inactive', name: '已停用' },
40
+ { code: 'pending', name: '待审核' }
41
+ ]
42
+ })
43
+
44
+ const regionFilter = ref({
45
+ multi: true,
46
+ enumable: true,
47
+ inputFilter: false,
48
+ defaultFilter: false,
49
+ label: 'regionName',
50
+ value: 'regionCode',
51
+ values: [
52
+ { regionCode: 'east', regionName: '华东区' },
53
+ { regionCode: 'south', regionName: '华南区' },
54
+ { regionCode: 'west', regionName: '华西区' }
55
+ ]
56
+ })
57
+
58
+ function filterChangeEvent({ filters }) {
59
+ TinyModal.message({ message: `筛选条件:${JSON.stringify(filters)}`, status: 'info' })
60
+ }
61
+
62
+ function statusFormatter({ cellValue }) {
63
+ const map = { active: '已启用', inactive: '已停用', pending: '待审核' }
64
+ return map[cellValue] || cellValue
65
+ }
66
+
67
+ function regionFormatter({ cellValue }) {
68
+ const map = { east: '华东区', south: '华南区', west: '华西区' }
69
+ return map[cellValue] || cellValue
70
+ }
71
+ </script>
@@ -0,0 +1,27 @@
1
+ import { test, expect } from '@playwright/test'
2
+
3
+ test('label/value 字段映射 - 状态列筛选', async ({ page }) => {
4
+ page.on('pageerror', (exception) => expect(exception).toBeNull())
5
+ await page.goto('grid-filter#filter-label-value-filter')
6
+
7
+ // 状态列使用 label:name value:code,选项显示 已启用/已停用/待审核
8
+ await page.getByRole('cell', { name: '状态' }).getByRole('img').first().click()
9
+ await page.getByTitle('已启用').click()
10
+ await page.getByRole('button', { name: '确定' }).click()
11
+
12
+ // 筛选 status=active 的行,应显示 2 条
13
+ await expect(page.locator('.tiny-grid-body__row:visible')).toHaveCount(2)
14
+ })
15
+
16
+ test('label/value 字段映射 - 区域列筛选', async ({ page }) => {
17
+ page.on('pageerror', (exception) => expect(exception).toBeNull())
18
+ await page.goto('grid-filter#filter-label-value-filter')
19
+
20
+ // 区域列使用 label:regionName value:regionCode
21
+ await page.getByRole('cell', { name: '区域' }).getByRole('img').first().click()
22
+ await page.getByTitle('华东区').click()
23
+ await page.getByRole('button', { name: '确定' }).click()
24
+
25
+ // 筛选 region=east 的行,应显示 2 条
26
+ await expect(page.locator('.tiny-grid-body__row:visible')).toHaveCount(2)
27
+ })
@@ -0,0 +1,78 @@
1
+ <template>
2
+ <tiny-grid :data="tableData" @filter-change="filterChangeEvent">
3
+ <tiny-grid-column type="index" width="60"></tiny-grid-column>
4
+ <tiny-grid-column
5
+ field="status"
6
+ title="状态"
7
+ :filter="statusFilter"
8
+ :formatter="statusFormatter"
9
+ ></tiny-grid-column>
10
+ <tiny-grid-column
11
+ field="region"
12
+ title="区域"
13
+ :filter="regionFilter"
14
+ :formatter="regionFormatter"
15
+ ></tiny-grid-column>
16
+ </tiny-grid>
17
+ </template>
18
+
19
+ <script>
20
+ import { TinyGrid, TinyGridColumn, TinyModal } from '@opentiny/vue'
21
+
22
+ export default {
23
+ components: {
24
+ TinyGrid,
25
+ TinyGridColumn
26
+ },
27
+ data() {
28
+ return {
29
+ tableData: [
30
+ { id: '1', status: 'active', region: 'east' },
31
+ { id: '2', status: 'inactive', region: 'south' },
32
+ { id: '3', status: 'active', region: 'west' },
33
+ { id: '4', status: 'pending', region: 'east' }
34
+ ],
35
+ // 使用 label/value 字段映射:后端返回 code/name,前端显示 name
36
+ statusFilter: {
37
+ multi: true,
38
+ enumable: true,
39
+ inputFilter: false,
40
+ defaultFilter: false,
41
+ label: 'name',
42
+ value: 'code',
43
+ values: [
44
+ { code: 'active', name: '已启用' },
45
+ { code: 'inactive', name: '已停用' },
46
+ { code: 'pending', name: '待审核' }
47
+ ]
48
+ },
49
+ regionFilter: {
50
+ multi: true,
51
+ enumable: true,
52
+ inputFilter: false,
53
+ defaultFilter: false,
54
+ label: 'regionName',
55
+ value: 'regionCode',
56
+ values: [
57
+ { regionCode: 'east', regionName: '华东区' },
58
+ { regionCode: 'south', regionName: '华南区' },
59
+ { regionCode: 'west', regionName: '华西区' }
60
+ ]
61
+ }
62
+ }
63
+ },
64
+ methods: {
65
+ filterChangeEvent({ filters }) {
66
+ TinyModal.message({ message: `筛选条件:${JSON.stringify(filters)}`, status: 'info' })
67
+ },
68
+ statusFormatter({ cellValue }) {
69
+ const map = { active: '已启用', inactive: '已停用', pending: '待审核' }
70
+ return map[cellValue] || cellValue
71
+ },
72
+ regionFormatter({ cellValue }) {
73
+ const map = { east: '华东区', south: '华南区', west: '华西区' }
74
+ return map[cellValue] || cellValue
75
+ }
76
+ }
77
+ }
78
+ </script>
@@ -0,0 +1,116 @@
1
+ <template>
2
+ <tiny-grid :data="tableData" @filter-change="filterChangeEvent">
3
+ <tiny-grid-column type="index" width="60"></tiny-grid-column>
4
+ <tiny-grid-column field="name" title="公司名称"></tiny-grid-column>
5
+ <tiny-grid-column field="employees" title="员工数" :filter="employeesFilter"></tiny-grid-column>
6
+ <tiny-grid-column field="city" title="城市"></tiny-grid-column>
7
+ </tiny-grid>
8
+ </template>
9
+
10
+ <script setup>
11
+ import { h, ref } from 'vue'
12
+ import { TinyGrid, TinyGridColumn, TinyInput, TinyModal } from '@opentiny/vue'
13
+
14
+ // 范围输入组件:左最小值、右最大值,与 demo 同文件(使用 render 避免运行时编译)
15
+ const RangeInput = {
16
+ name: 'RangeInput',
17
+ components: { TinyInput },
18
+ props: {
19
+ modelValue: {
20
+ type: [Object, String],
21
+ default: () => ({ min: '', max: '' })
22
+ }
23
+ },
24
+ emits: ['update:modelValue'],
25
+ computed: {
26
+ rangeValue() {
27
+ const v = this.modelValue
28
+ if (!v || typeof v !== 'object') {
29
+ return { min: '', max: '' }
30
+ }
31
+ return { min: v.min ?? '', max: v.max ?? '' }
32
+ }
33
+ },
34
+ render() {
35
+ const { rangeValue } = this
36
+ return h('div', { class: 'range-input-wrapper' }, [
37
+ h(TinyInput, {
38
+ modelValue: rangeValue.min,
39
+ placeholder: '最小值',
40
+ size: 'small',
41
+ clearable: true,
42
+ 'onUpdate:modelValue': (val) => {
43
+ this.$emit('update:modelValue', { ...rangeValue, min: val })
44
+ }
45
+ }),
46
+ h('span', { class: 'range-separator' }, '~'),
47
+ h(TinyInput, {
48
+ modelValue: rangeValue.max,
49
+ placeholder: '最大值',
50
+ size: 'small',
51
+ clearable: true,
52
+ 'onUpdate:modelValue': (val) => {
53
+ this.$emit('update:modelValue', { ...rangeValue, max: val })
54
+ }
55
+ })
56
+ ])
57
+ }
58
+ }
59
+
60
+ const tableData = ref([
61
+ { id: '1', name: 'GFD 科技', city: '福州', employees: 200 },
62
+ { id: '2', name: 'WWW 科技', city: '深圳', employees: 500 },
63
+ { id: '3', name: 'RFV 公司', city: '中山', employees: 800 },
64
+ { id: '4', name: 'TGB 科技', city: '福州', employees: 350 },
65
+ { id: '5', name: 'YHN 科技', city: '韶关', employees: 1200 }
66
+ ])
67
+
68
+ const employeesFilter = ref({
69
+ layout: 'input,base',
70
+ inputFilter: {
71
+ component: RangeInput,
72
+ attrs: {},
73
+ relation: 'range',
74
+ relations: [
75
+ {
76
+ label: '范围内',
77
+ value: 'range',
78
+ method: ({ value, input }) => {
79
+ if (!input || typeof input !== 'object') return true
80
+ const num = Number(value)
81
+ const minVal = input.min === '' || input.min === undefined ? null : Number(input.min)
82
+ const maxVal = input.max === '' || input.max === undefined ? null : Number(input.max)
83
+ if (isNaN(num)) return false
84
+ if (minVal !== null && !isNaN(minVal) && num < minVal) return false
85
+ if (maxVal !== null && !isNaN(maxVal) && num > maxVal) return false
86
+ return true
87
+ }
88
+ }
89
+ ]
90
+ }
91
+ })
92
+
93
+ function filterChangeEvent({ filters }) {
94
+ TinyModal.message({ message: `筛选条件:${JSON.stringify(filters)}`, status: 'info' })
95
+ }
96
+ </script>
97
+
98
+ <style>
99
+ /* 范围输入样式:筛选面板使用 popper 渲染,需非 scoped */
100
+ .tiny-grid__filter-wrapper .range-input-wrapper {
101
+ display: flex;
102
+ align-items: center;
103
+ gap: 10px;
104
+ padding: 4px 0 8px;
105
+ }
106
+ .tiny-grid__filter-wrapper .range-input-wrapper .tiny-input {
107
+ flex: 1;
108
+ min-width: 90px;
109
+ }
110
+ .tiny-grid__filter-wrapper .range-input-wrapper .range-separator {
111
+ font-size: 12px;
112
+ color: var(--ti-common-color-text-placeholder, #adb0b8);
113
+ white-space: nowrap;
114
+ flex-shrink: 0;
115
+ }
116
+ </style>
@@ -0,0 +1,20 @@
1
+ import { test, expect } from '@playwright/test'
2
+
3
+ test('自定义输入组件 - 范围筛选', async ({ page }) => {
4
+ page.on('pageerror', (exception) => expect(exception).toBeNull())
5
+ await page.goto('grid-filter#filter-input-custom-component')
6
+
7
+ // 点击员工数列筛选图标
8
+ await page.getByRole('cell', { name: '员工数' }).getByRole('img').first().click()
9
+
10
+ // 等待筛选面板打开并填写范围:最小值 300,最大值 600
11
+ const filterPanel = page.locator('.tiny-grid__filter-wrapper.filter__active')
12
+ await filterPanel.waitFor({ state: 'visible' })
13
+ await filterPanel.getByPlaceholder('最小值').nth(1).fill('300')
14
+ await filterPanel.getByPlaceholder('最大值').nth(1).fill('600')
15
+
16
+ await filterPanel.getByRole('button', { name: '确定' }).click()
17
+
18
+ // 应显示员工数在 300-600 之间的行:500(WWW)、350(TGB),使用 :visible 只统计可见行
19
+ await expect(page.locator('.tiny-grid-body__row:visible')).toHaveCount(2)
20
+ })
@@ -0,0 +1,126 @@
1
+ <template>
2
+ <tiny-grid :data="tableData" @filter-change="filterChangeEvent">
3
+ <tiny-grid-column type="index" width="60"></tiny-grid-column>
4
+ <tiny-grid-column field="name" title="公司名称"></tiny-grid-column>
5
+ <tiny-grid-column field="employees" title="员工数" :filter="employeesFilter"></tiny-grid-column>
6
+ <tiny-grid-column field="city" title="城市"></tiny-grid-column>
7
+ </tiny-grid>
8
+ </template>
9
+
10
+ <script>
11
+ import { h } from 'vue'
12
+ import { TinyGrid, TinyGridColumn, TinyInput, TinyModal } from '@opentiny/vue'
13
+
14
+ // 范围输入组件:左最小值、右最大值,与 demo 同文件(使用 render 避免运行时编译)
15
+ const RangeInput = {
16
+ name: 'RangeInput',
17
+ components: { TinyInput },
18
+ props: {
19
+ modelValue: {
20
+ type: [Object, String],
21
+ default: () => ({ min: '', max: '' })
22
+ }
23
+ },
24
+ emits: ['update:modelValue'],
25
+ computed: {
26
+ rangeValue() {
27
+ const v = this.modelValue
28
+ if (!v || typeof v !== 'object') {
29
+ return { min: '', max: '' }
30
+ }
31
+ return { min: v.min ?? '', max: v.max ?? '' }
32
+ }
33
+ },
34
+ render() {
35
+ const { rangeValue } = this
36
+ return h('div', { class: 'range-input-wrapper' }, [
37
+ h(TinyInput, {
38
+ modelValue: rangeValue.min,
39
+ placeholder: '最小值',
40
+ size: 'small',
41
+ clearable: true,
42
+ 'onUpdate:modelValue': (val) => {
43
+ this.$emit('update:modelValue', { ...rangeValue, min: val })
44
+ }
45
+ }),
46
+ h('span', { class: 'range-separator' }, '~'),
47
+ h(TinyInput, {
48
+ modelValue: rangeValue.max,
49
+ placeholder: '最大值',
50
+ size: 'small',
51
+ clearable: true,
52
+ 'onUpdate:modelValue': (val) => {
53
+ this.$emit('update:modelValue', { ...rangeValue, max: val })
54
+ }
55
+ })
56
+ ])
57
+ }
58
+ }
59
+
60
+ export default {
61
+ components: {
62
+ TinyGrid,
63
+ TinyGridColumn
64
+ },
65
+ data() {
66
+ return {
67
+ tableData: [
68
+ { id: '1', name: 'GFD 科技', city: '福州', employees: 200 },
69
+ { id: '2', name: 'WWW 科技', city: '深圳', employees: 500 },
70
+ { id: '3', name: 'RFV 公司', city: '中山', employees: 800 },
71
+ { id: '4', name: 'TGB 科技', city: '福州', employees: 350 },
72
+ { id: '5', name: 'YHN 科技', city: '韶关', employees: 1200 }
73
+ ],
74
+ employeesFilter: {
75
+ layout: 'input,base',
76
+ inputFilter: {
77
+ component: RangeInput,
78
+ attrs: {},
79
+ relation: 'range',
80
+ relations: [
81
+ {
82
+ label: '范围内',
83
+ value: 'range',
84
+ method: ({ value, input }) => {
85
+ if (!input || typeof input !== 'object') return true
86
+ const num = Number(value)
87
+ const minVal = input.min === '' || input.min === undefined ? null : Number(input.min)
88
+ const maxVal = input.max === '' || input.max === undefined ? null : Number(input.max)
89
+ if (isNaN(num)) return false
90
+ if (minVal !== null && !isNaN(minVal) && num < minVal) return false
91
+ if (maxVal !== null && !isNaN(maxVal) && num > maxVal) return false
92
+ return true
93
+ }
94
+ }
95
+ ]
96
+ }
97
+ }
98
+ }
99
+ },
100
+ methods: {
101
+ filterChangeEvent({ filters }) {
102
+ TinyModal.message({ message: `筛选条件:${JSON.stringify(filters)}`, status: 'info' })
103
+ }
104
+ }
105
+ }
106
+ </script>
107
+
108
+ <style>
109
+ /* 范围输入样式:筛选面板使用 popper 渲染,需非 scoped */
110
+ .tiny-grid__filter-wrapper .range-input-wrapper {
111
+ display: flex;
112
+ align-items: center;
113
+ gap: 10px;
114
+ padding: 4px 0 8px;
115
+ }
116
+ .tiny-grid__filter-wrapper .range-input-wrapper .tiny-input {
117
+ flex: 1;
118
+ min-width: 90px;
119
+ }
120
+ .tiny-grid__filter-wrapper .range-input-wrapper .range-separator {
121
+ font-size: 12px;
122
+ color: var(--ti-common-color-text-placeholder, #adb0b8);
123
+ white-space: nowrap;
124
+ flex-shrink: 0;
125
+ }
126
+ </style>