yc-pro-components 0.0.47 → 0.0.49

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 (45) hide show
  1. package/es/components/index.mjs +1 -0
  2. package/es/components/yc-plus-page/index.d.ts +2 -0
  3. package/es/components/yc-plus-page/index.mjs +1 -0
  4. package/es/components/yc-plus-page/src/header-filter-cell.vue.mjs +1 -1
  5. package/es/components/yc-plus-page/src/header-filter-cell.vue2.mjs +55 -31
  6. package/es/components/yc-plus-page/src/use-local-header-filter.d.ts +93 -0
  7. package/es/components/yc-plus-page/src/use-local-header-filter.mjs +151 -0
  8. package/es/index.css +10 -10
  9. package/es/index.mjs +1 -0
  10. package/index.css +13 -7
  11. package/index.js +218 -48
  12. package/index.min.css +1 -1
  13. package/index.min.js +11 -11
  14. package/index.min.mjs +11 -11
  15. package/index.mjs +218 -49
  16. package/lib/components/index.js +2 -0
  17. package/lib/components/yc-plus-page/index.d.ts +2 -0
  18. package/lib/components/yc-plus-page/index.js +2 -0
  19. package/lib/components/yc-plus-page/src/header-filter-cell.vue.js +1 -1
  20. package/lib/components/yc-plus-page/src/header-filter-cell.vue2.js +53 -29
  21. package/lib/components/yc-plus-page/src/use-local-header-filter.d.ts +93 -0
  22. package/lib/components/yc-plus-page/src/use-local-header-filter.js +153 -0
  23. package/lib/index.css +11 -11
  24. package/lib/index.js +2 -0
  25. package/locale/en.js +1 -1
  26. package/locale/en.min.js +1 -1
  27. package/locale/en.min.mjs +1 -1
  28. package/locale/en.mjs +1 -1
  29. package/locale/ja.js +1 -1
  30. package/locale/ja.min.js +1 -1
  31. package/locale/ja.min.mjs +1 -1
  32. package/locale/ja.mjs +1 -1
  33. package/locale/ko.js +1 -1
  34. package/locale/ko.min.js +1 -1
  35. package/locale/ko.min.mjs +1 -1
  36. package/locale/ko.mjs +1 -1
  37. package/locale/zh-cn.js +1 -1
  38. package/locale/zh-cn.min.js +1 -1
  39. package/locale/zh-cn.min.mjs +1 -1
  40. package/locale/zh-cn.mjs +1 -1
  41. package/locale/zh-tw.js +1 -1
  42. package/locale/zh-tw.min.js +1 -1
  43. package/locale/zh-tw.min.mjs +1 -1
  44. package/locale/zh-tw.mjs +1 -1
  45. package/package.json +1 -1
@@ -12,7 +12,8 @@ var useYcConfig = require('../../yc-config-provider/src/useYcConfig.js');
12
12
 
13
13
  const _hoisted_1 = { class: "yc-header-filter-cell__label-text" };
14
14
  const _hoisted_2 = { class: "yc-header-filter-cell__label-text" };
15
- const _hoisted_3 = {
15
+ const _hoisted_3 = { style: { cursor: "pointer", display: "inline-flex", alignItems: "center" } };
16
+ const _hoisted_4 = {
16
17
  class: "yc-header-filter-cell__content",
17
18
  style: {
18
19
  display: "flex",
@@ -22,10 +23,10 @@ const _hoisted_3 = {
22
23
  gap: "4px"
23
24
  }
24
25
  };
25
- const _hoisted_4 = { class: "yc-header-filter-cell__operators" };
26
- const _hoisted_5 = ["onClick"];
27
- const _hoisted_6 = { key: 0 };
28
- const _hoisted_7 = {
26
+ const _hoisted_5 = { class: "yc-header-filter-cell__operators" };
27
+ const _hoisted_6 = ["onClick"];
28
+ const _hoisted_7 = { key: 0 };
29
+ const _hoisted_8 = {
29
30
  class: "yc-header-filter-cell__actions",
30
31
  style: {
31
32
  display: "flex",
@@ -53,7 +54,6 @@ var _sfc_main = /* @__PURE__ */ vue.defineComponent({
53
54
  const emit = __emit;
54
55
  const { getCdnUrl } = useYcConfig.useYcCdnUrl();
55
56
  const filterIconUrl = vue.computed(() => getCdnUrl("images", "table_filter_icon.svg"));
56
- const sortIconUrl = vue.computed(() => getCdnUrl("images", "table_sort_icon.svg"));
57
57
  const state = vue.reactive({
58
58
  selectedOp: "=",
59
59
  inputVal: ""
@@ -197,6 +197,7 @@ var _sfc_main = /* @__PURE__ */ vue.defineComponent({
197
197
  modelValue: state.inputVal,
198
198
  "onUpdate:modelValue": (v) => state.inputVal = v,
199
199
  placeholder: "",
200
+ maxlength: 50,
200
201
  style: "margin-top: 4px; width: 100%"
201
202
  });
202
203
  };
@@ -212,14 +213,22 @@ var _sfc_main = /* @__PURE__ */ vue.defineComponent({
212
213
  alignItems: "center",
213
214
  gap: "4px",
214
215
  cursor: "default"
215
- }
216
+ },
217
+ onMousedown: _cache[1] || (_cache[1] = vue.withModifiers(() => {
218
+ }, ["stop"])),
219
+ onClick: _cache[2] || (_cache[2] = vue.withModifiers(() => {
220
+ }, ["stop"]))
216
221
  },
217
222
  [
223
+ vue.createCommentVNode(" @mousedown.stop / @click.stop \u963B\u6B62 el-table <th> \u7684 handleHeaderClick \u548C\u5217 resize \u4E8B\u4EF6\u7A7F\u900F "),
218
224
  vue.createCommentVNode(" \u6807\u7B7E\u5185\u5BB9 "),
219
225
  vue.createElementVNode(
220
226
  "span",
221
227
  {
222
- class: vue.normalizeClass(["yc-header-filter-cell__label", { "yc-header-filter-cell__label--ellipsis": shouldShowEllipsis.value }]),
228
+ class: vue.normalizeClass(["yc-header-filter-cell__label", {
229
+ "yc-header-filter-cell__label--ellipsis": shouldShowEllipsis.value,
230
+ "yc-header-filter-cell__label--active": isAscActive.value || isDescActive.value || isActive.value
231
+ }]),
223
232
  style: {
224
233
  userSelect: "none",
225
234
  cursor: "pointer"
@@ -318,19 +327,35 @@ var _sfc_main = /* @__PURE__ */ vue.defineComponent({
318
327
  class: "yc-header-filter-cell__sort",
319
328
  style: {
320
329
  display: "inline-flex",
330
+ flexDirection: "column",
321
331
  alignItems: "center",
322
- cursor: "pointer"
332
+ cursor: "pointer",
333
+ lineHeight: 1
323
334
  },
324
335
  onClick: vue.withModifiers(cycleSort, ["stop"])
325
336
  }, [
326
- vue.createVNode(vue.unref(index$1.default), {
327
- src: sortIconUrl.value,
328
- size: 14,
329
- class: vue.normalizeClass({
330
- "yc-header-filter-cell__sort-icon--asc": isAscActive.value,
331
- "yc-header-filter-cell__sort-icon--desc": isDescActive.value
332
- })
333
- }, null, 8, ["src", "class"])
337
+ vue.createCommentVNode(" \u5347\u5E8F\u7BAD\u5934\uFF1A\u6FC0\u6D3B\u65F6\u9AD8\u4EAE "),
338
+ vue.createVNode(vue.unref(elementPlus.ElIcon), {
339
+ size: 10,
340
+ style: vue.normalizeStyle({ color: isAscActive.value ? "var(--el-color-primary)" : "var(--el-text-color-placeholder)", marginBottom: "-4px" })
341
+ }, {
342
+ default: vue.withCtx(() => [
343
+ vue.createVNode(vue.unref(ElementPlusIconsVue.CaretTop))
344
+ ]),
345
+ _: 1
346
+ /* STABLE */
347
+ }, 8, ["style"]),
348
+ vue.createCommentVNode(" \u964D\u5E8F\u7BAD\u5934\uFF1A\u6FC0\u6D3B\u65F6\u9AD8\u4EAE "),
349
+ vue.createVNode(vue.unref(elementPlus.ElIcon), {
350
+ size: 10,
351
+ style: vue.normalizeStyle({ color: isDescActive.value ? "var(--el-color-primary)" : "var(--el-text-color-placeholder)" })
352
+ }, {
353
+ default: vue.withCtx(() => [
354
+ vue.createVNode(vue.unref(ElementPlusIconsVue.CaretBottom))
355
+ ]),
356
+ _: 1
357
+ /* STABLE */
358
+ }, 8, ["style"])
334
359
  ]),
335
360
  vue.createCommentVNode(" \u7B5B\u9009\u56FE\u6807\u548C\u5F39\u7A97 "),
336
361
  !_ctx.disableFilter ? (vue.openBlock(), vue.createBlock(vue.unref(elementPlus.ElPopover), {
@@ -339,25 +364,24 @@ var _sfc_main = /* @__PURE__ */ vue.defineComponent({
339
364
  placement: "bottom-start",
340
365
  width: 220,
341
366
  visible: popVisible.value,
367
+ "onUpdate:visible": _cache[0] || (_cache[0] = ($event) => popVisible.value = $event),
342
368
  "popper-class": "yc-header-filter-pop",
343
369
  "popper-style": { zIndex: 4e3 }
344
370
  }, {
345
371
  reference: vue.withCtx(() => [
346
- vue.createElementVNode("span", {
347
- style: { cursor: "pointer", display: "inline-flex", alignItems: "center" },
348
- onClick: _cache[0] || (_cache[0] = vue.withModifiers(($event) => popVisible.value = !popVisible.value, ["stop"]))
349
- }, [
372
+ vue.createElementVNode("span", _hoisted_3, [
350
373
  vue.createVNode(vue.unref(index$1.default), {
351
374
  src: filterIconUrl.value,
352
375
  size: 14,
376
+ color: isActive.value ? "var(--el-color-primary)" : "currentColor",
353
377
  class: vue.normalizeClass({ "yc-header-filter-cell__filter-icon--active": isActive.value })
354
- }, null, 8, ["src", "class"])
378
+ }, null, 8, ["src", "color", "class"])
355
379
  ])
356
380
  ]),
357
381
  default: vue.withCtx(() => [
358
- vue.createElementVNode("div", _hoisted_3, [
382
+ vue.createElementVNode("div", _hoisted_4, [
359
383
  vue.createCommentVNode(" \u64CD\u4F5C\u7B26\u5217\u8868 "),
360
- vue.createElementVNode("div", _hoisted_4, [
384
+ vue.createElementVNode("div", _hoisted_5, [
361
385
  (vue.openBlock(true), vue.createElementBlock(
362
386
  vue.Fragment,
363
387
  null,
@@ -384,8 +408,8 @@ var _sfc_main = /* @__PURE__ */ vue.defineComponent({
384
408
  1
385
409
  /* TEXT */
386
410
  ),
387
- state.selectedOp === op.code ? (vue.openBlock(), vue.createElementBlock("span", _hoisted_6, "\u2713")) : vue.createCommentVNode("v-if", true)
388
- ], 12, _hoisted_5);
411
+ state.selectedOp === op.code ? (vue.openBlock(), vue.createElementBlock("span", _hoisted_7, "\u2713")) : vue.createCommentVNode("v-if", true)
412
+ ], 12, _hoisted_6);
389
413
  }),
390
414
  128
391
415
  /* KEYED_FRAGMENT */
@@ -394,7 +418,7 @@ var _sfc_main = /* @__PURE__ */ vue.defineComponent({
394
418
  vue.createCommentVNode(" \u503C\u8F93\u5165 "),
395
419
  needValueInput.value ? (vue.openBlock(), vue.createBlock(vue.resolveDynamicComponent(renderValueField), { key: 0 })) : vue.createCommentVNode("v-if", true),
396
420
  vue.createCommentVNode(" \u64CD\u4F5C\u6309\u94AE "),
397
- vue.createElementVNode("div", _hoisted_7, [
421
+ vue.createElementVNode("div", _hoisted_8, [
398
422
  vue.createVNode(vue.unref(elementPlus.ElButton), { onClick: handleReset }, {
399
423
  default: vue.withCtx(() => [
400
424
  vue.createTextVNode("\u91CD\u7F6E")
@@ -419,8 +443,8 @@ var _sfc_main = /* @__PURE__ */ vue.defineComponent({
419
443
  /* STABLE */
420
444
  }, 8, ["visible"])) : vue.createCommentVNode("v-if", true)
421
445
  ],
422
- 512
423
- /* NEED_PATCH */
446
+ 544
447
+ /* NEED_HYDRATION, NEED_PATCH */
424
448
  );
425
449
  };
426
450
  }
@@ -0,0 +1,93 @@
1
+ import { HeaderFiltersState, OrderByItem, OperatorOption, HeaderFilterPayload, HeaderFilterResetPayload } from './type';
2
+ import { Ref, ComputedRef } from 'vue';
3
+
4
+ /**
5
+ * 可筛选列配置
6
+ */
7
+ export interface LocalFilterColumnConfig {
8
+ /** 列字段名(对应 el-table-column 的 prop) */
9
+ prop: string;
10
+ /** 字段类型,决定可用的操作符集合,默认 'string' */
11
+ type?: 'string' | 'number' | 'date' | 'boolean';
12
+ }
13
+ /**
14
+ * useLocalHeaderFilter 配置项
15
+ */
16
+ export interface UseLocalHeaderFilterOptions {
17
+ /** 需要启用筛选的列配置,未配置的列不显示筛选 */
18
+ filterableColumns?: LocalFilterColumnConfig[];
19
+ }
20
+ /**
21
+ * useLocalHeaderFilter 返回值
22
+ */
23
+ export interface UseLocalHeaderFilterReturn<T> {
24
+ /** 经过筛选和排序后的数据,绑定到表格的 :data */
25
+ filteredData: ComputedRef<T[]>;
26
+ /** 当前筛选状态,传给 YcTableHeaderFilterCell 的 :filters */
27
+ headerFiltersState: HeaderFiltersState;
28
+ /** 当前排序状态,传给 YcTableHeaderFilterCell 的 :order-by */
29
+ orderByState: OrderByItem[];
30
+ /** 筛选确认处理,传给 YcTableHeaderFilterCell 的 @confirm */
31
+ handleFilterConfirm: (payload: HeaderFilterPayload) => void;
32
+ /** 筛选重置处理,传给 YcTableHeaderFilterCell 的 @reset */
33
+ handleFilterReset: (payload: HeaderFilterResetPayload) => void;
34
+ /** 排序切换处理,传给 YcTableHeaderFilterCell 的 @sort-change */
35
+ handleSortChange: (payload: {
36
+ field: string;
37
+ direction?: 'asc' | 'desc';
38
+ }) => void;
39
+ /** 根据 prop 获取操作符列表,传给 YcTableHeaderFilterCell 的 :operators */
40
+ getOpsByProp: (prop: string) => OperatorOption[];
41
+ /** 重置所有筛选和排序 */
42
+ resetAllFilters: () => void;
43
+ /** 是否有激活的筛选条件 */
44
+ hasActiveFilters: ComputedRef<boolean>;
45
+ }
46
+ /**
47
+ * 本地数据表头筛选组合式函数
48
+ *
49
+ * @param data - 响应式数据源
50
+ * @param options - 可筛选列配置
51
+ *
52
+ * @example
53
+ * ```ts
54
+ * import { useLocalHeaderFilter, YcTableHeaderFilterCell } from 'yc-pro-components'
55
+ *
56
+ * const {
57
+ * filteredData,
58
+ * headerFiltersState,
59
+ * orderByState,
60
+ * handleFilterConfirm,
61
+ * handleFilterReset,
62
+ * handleSortChange,
63
+ * getOpsByProp
64
+ * } = useLocalHeaderFilter(tableData, {
65
+ * filterableColumns: [
66
+ * { prop: 'userName', type: 'string' },
67
+ * { prop: 'realShortName', type: 'string' },
68
+ * { prop: 'weight', type: 'number' }
69
+ * ]
70
+ * })
71
+ * ```
72
+ *
73
+ * 模板中配合 el-table 使用:
74
+ * ```vue
75
+ * <el-table :data="filteredData">
76
+ * <el-table-column prop="userName">
77
+ * <template #header>
78
+ * <YcTableHeaderFilterCell
79
+ * label="客户名称"
80
+ * :column="{ prop: 'userName' }"
81
+ * :operators="getOpsByProp('userName')"
82
+ * :filters="headerFiltersState.filters"
83
+ * :order-by="orderByState"
84
+ * @confirm="handleFilterConfirm"
85
+ * @reset="handleFilterReset"
86
+ * @sort-change="handleSortChange"
87
+ * />
88
+ * </template>
89
+ * </el-table-column>
90
+ * </el-table>
91
+ * ```
92
+ */
93
+ export declare function useLocalHeaderFilter<T = Record<string, unknown>>(data: Ref<T[]> | ComputedRef<T[]>, options?: UseLocalHeaderFilterOptions): UseLocalHeaderFilterReturn<T>;
@@ -0,0 +1,153 @@
1
+ 'use strict';
2
+
3
+ var vue = require('vue');
4
+ var useHeaderFilter = require('./use-header-filter.js');
5
+
6
+ function compareValues(a, b) {
7
+ if (a == null && b == null) return 0;
8
+ if (a == null) return -1;
9
+ if (b == null) return 1;
10
+ const numA = Number(a);
11
+ const numB = Number(b);
12
+ if (!isNaN(numA) && !isNaN(numB)) return numA - numB;
13
+ return String(a).localeCompare(String(b), "zh-CN");
14
+ }
15
+ function matchFilter(value, op, filterValue) {
16
+ const strValue = String(value != null ? value : "").toLowerCase();
17
+ const strFilter = String(filterValue != null ? filterValue : "").toLowerCase();
18
+ switch (op) {
19
+ case "=":
20
+ return strValue === strFilter;
21
+ case "!=":
22
+ return strValue !== strFilter;
23
+ case "like":
24
+ return strValue.includes(strFilter);
25
+ case "not like":
26
+ return !strValue.includes(strFilter);
27
+ case ">":
28
+ return Number(value) > Number(filterValue);
29
+ case ">=":
30
+ return Number(value) >= Number(filterValue);
31
+ case "<":
32
+ return Number(value) < Number(filterValue);
33
+ case "<=":
34
+ return Number(value) <= Number(filterValue);
35
+ case "is null":
36
+ case "empty":
37
+ return value == null || strValue === "";
38
+ case "is not null":
39
+ case "not_empty":
40
+ return value != null && strValue !== "";
41
+ case "in": {
42
+ const set = strFilter.split(",").map((s) => s.trim().toLowerCase());
43
+ return set.includes(strValue);
44
+ }
45
+ case "not in": {
46
+ const set = strFilter.split(",").map((s) => s.trim().toLowerCase());
47
+ return !set.includes(strValue);
48
+ }
49
+ case "between": {
50
+ let parts = strFilter.split(",").map((s) => Number(s.trim()));
51
+ if (parts.length < 2 || isNaN(parts[0]) || isNaN(parts[1])) {
52
+ const hyphenMatch = strFilter.match(/^(-?\d+(?:\.\d+)?)-(-?\d+(?:\.\d+)?)$/);
53
+ if (hyphenMatch) {
54
+ parts = [Number(hyphenMatch[1]), Number(hyphenMatch[2])];
55
+ }
56
+ }
57
+ const num = Number(value);
58
+ return parts.length === 2 && !isNaN(num) && !isNaN(parts[0]) && !isNaN(parts[1]) && num >= parts[0] && num <= parts[1];
59
+ }
60
+ case "is true":
61
+ return value === true || strValue === "true" || strValue === "1";
62
+ case "is false":
63
+ return value === false || strValue === "false" || strValue === "0";
64
+ default:
65
+ return true;
66
+ }
67
+ }
68
+ function useLocalHeaderFilter(data, options) {
69
+ const fieldCatalog = vue.computed(() => {
70
+ const cols = options == null ? void 0 : options.filterableColumns;
71
+ if (!(cols == null ? void 0 : cols.length)) return {};
72
+ const catalog = {};
73
+ for (const col of cols) {
74
+ catalog[col.prop] = {
75
+ type: col.type || "string",
76
+ filter: true,
77
+ allowedField: col.prop
78
+ };
79
+ }
80
+ return catalog;
81
+ });
82
+ const {
83
+ headerFiltersState,
84
+ orderByState,
85
+ upsertHeaderFilter,
86
+ removeHeaderFilter,
87
+ setOrderBy,
88
+ getOpsByProp,
89
+ isEnabled: _isEnabled
90
+ } = useHeaderFilter.useHeaderFilter({
91
+ headerFilterConfig: () => ({
92
+ enabled: true,
93
+ fieldCatalog: fieldCatalog.value
94
+ }),
95
+ hideHeaderFilter: () => false
96
+ });
97
+ const filteredData = vue.computed(() => {
98
+ let result = [...vue.unref(data) || []];
99
+ if (headerFiltersState.filters.length > 0) {
100
+ result = result.filter((row) => {
101
+ const record = row;
102
+ return headerFiltersState.filters.every(
103
+ (filter) => matchFilter(record[filter.field], filter.op, filter.value)
104
+ );
105
+ });
106
+ }
107
+ if (orderByState.length > 0) {
108
+ result.sort((a, b) => {
109
+ const recA = a;
110
+ const recB = b;
111
+ for (const order of orderByState) {
112
+ const cmp = compareValues(recA[order.field], recB[order.field]);
113
+ if (cmp !== 0) return order.direction === "asc" ? cmp : -cmp;
114
+ }
115
+ return 0;
116
+ });
117
+ }
118
+ return result;
119
+ });
120
+ const handleFilterConfirm = (payload) => {
121
+ var _a;
122
+ const field = String(((_a = payload.column) == null ? void 0 : _a.prop) || "");
123
+ upsertHeaderFilter(field, payload.op, payload.value);
124
+ };
125
+ const handleFilterReset = (payload) => {
126
+ var _a;
127
+ const field = String(((_a = payload.column) == null ? void 0 : _a.prop) || "");
128
+ removeHeaderFilter(field);
129
+ };
130
+ const handleSortChange = (payload) => {
131
+ setOrderBy(payload.field, payload.direction);
132
+ };
133
+ const resetAllFilters = () => {
134
+ headerFiltersState.filters.splice(0);
135
+ orderByState.splice(0);
136
+ };
137
+ const hasActiveFilters = vue.computed(() => {
138
+ return headerFiltersState.filters.length > 0 || orderByState.length > 0;
139
+ });
140
+ return {
141
+ filteredData,
142
+ headerFiltersState,
143
+ orderByState,
144
+ handleFilterConfirm,
145
+ handleFilterReset,
146
+ handleSortChange,
147
+ getOpsByProp,
148
+ resetAllFilters,
149
+ hasActiveFilters
150
+ };
151
+ }
152
+
153
+ exports.useLocalHeaderFilter = useLocalHeaderFilter;