UNPKG

@happy-table/vue3

Version:

A high-performance Vue 3 table component for B2B systems with TypeScript support

1,556 lines 307 kB
var Al = Object.defineProperty; var zl = (l, t, e) => t in l ? Al(l, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : l[t] = e; var Q = (l, t, e) => zl(l, typeof t != "symbol" ? t + "" : t, e); import { defineComponent as ce, createElementBlock as N, openBlock as B, createElementVNode as V, toDisplayString as Z, createCommentVNode as te, ref as K, normalizeStyle as _e, normalizeClass as re, createBlock as ue, createVNode as $e, renderSlot as he, computed as T, unref as _, mergeProps as ht, watch as ee, onUnmounted as Ne, nextTick as me, onMounted as xe, withCtx as Ce, withDirectives as ve, vModelSelect as pt, withKeys as nt, vModelText as He, vModelCheckbox as lt, Fragment as we, renderList as Oe, onBeforeUnmount as bn, Teleport as Ol, withModifiers as Bl, reactive as Je, h as ct, vModelDynamic as Vl, toRef as pe, resolveDynamicComponent as Ll, createTextVNode as ut, onScopeDispose as Sn, readonly as Ee, shallowRef as Wl, normalizeProps as ql, guardReactiveProps as Hl } from "vue"; import { Icon as Nl } from "@iconify/vue"; const Kl = { class: "table-error-boundary" }, Ul = { class: "error-content" }, jl = { class: "error-title" }, Yl = { class: "error-message" }, Gl = /* @__PURE__ */ ce({ __name: "TableErrorBoundary", props: { error: {}, title: { default: "表格渲染错误" }, retryText: { default: "重试" } }, emits: ["retry"], setup(l, { emit: t }) { const e = t, n = () => { e("retry"); }; return (a, o) => (B(), N("div", Kl, [ V("div", Ul, [ V("h3", jl, Z(a.title), 1), V("p", Yl, Z(a.error.message), 1), V("button", { class: "error-retry-btn", onClick: n }, Z(a.retryText), 1) ]) ])); } }), de = (l, t) => { const e = l.__vccOpts || l; for (const [n, a] of t) e[n] = a; return e; }, Ql = /* @__PURE__ */ de(Gl, [["__scopeId", "data-v-2c488903"]]), Xl = { class: "table-loading" }, Jl = { class: "loading-text" }, Zl = /* @__PURE__ */ ce({ __name: "TableLoadingState", props: { loadingText: { default: "加载中..." } }, setup(l) { return (t, e) => (B(), N("div", Xl, [ e[0] || (e[0] = V("div", { class: "loading-spinner" }, null, -1)), V("p", Jl, Z(t.loadingText), 1) ])); } }), ea = /* @__PURE__ */ de(Zl, [["__scopeId", "data-v-2d5eba26"]]), ta = { key: 0, class: "table-toolbar-placeholder" }, na = { class: "toolbar-title" }, la = /* @__PURE__ */ ce({ __name: "TableToolbar", props: { config: {} }, setup(l) { const t = l, e = () => t.config?.title ? typeof t.config.title == "string" ? t.config.title : t.config.title.text || "" : ""; return (n, a) => n.config ? (B(), N("div", ta, [ V("div", na, Z(e()), 1) ])) : te("", !0); } }), aa = /* @__PURE__ */ de(la, [["__scopeId", "data-v-9b351fa2"]]), oa = ["data-theme", "tabindex"], ia = { key: 2, class: "table-content" }, sa = /* @__PURE__ */ ce({ __name: "TableContainer", props: { config: {}, error: { default: null }, isLoading: { type: Boolean, default: !1 }, containerClasses: {}, containerStyle: {}, currentTheme: {}, isDarkTheme: { type: Boolean }, toolbarConfig: {}, tableWrapperStyle: {}, processedColumns: {}, visibleColumns: {}, isHorizontalVirtualEnabled: { type: Boolean }, processedData: {}, paginationConfig: {}, paginationState: {}, isExcelMode: { type: Function, default: () => !1 } }, emits: ["retry", "keydown", "keyup"], setup(l, { expose: t, emit: e }) { const n = e, a = K(null), o = () => { n("retry"); }, i = (g) => { n("keydown", g); }, v = (g) => { n("keyup", g); }; return t({ tableContainer: a }), (g, d) => (B(), N("div", { ref_key: "tableContainer", ref: a, class: re(["vue-table-container", [ g.containerClasses, { dark: g.isDarkTheme, "table-excel-mode": g.isExcelMode } ]]), "data-theme": g.currentTheme, style: _e(g.containerStyle), tabindex: g.isExcelMode() ? 0 : -1, "data-no-password-manager": "true", "data-form": "false", "data-1p-ignore": "", "data-lpignore": "true", "data-bwignore": "true", onKeydown: i, onKeyup: v }, [ g.error ? (B(), ue(Ql, { key: 0, error: g.error, onRetry: o }, null, 8, ["error"])) : g.isLoading ? (B(), ue(ea, { key: 1 })) : (B(), N("div", ia, [ $e(aa, { config: g.toolbarConfig }, null, 8, ["config"]), he(g.$slots, "table-content", { tableWrapperStyle: g.tableWrapperStyle, processedColumns: g.processedColumns, visibleColumns: g.visibleColumns, isHorizontalVirtualEnabled: g.isHorizontalVirtualEnabled, processedData: g.processedData, paginationConfig: g.paginationConfig, paginationState: g.paginationState }, void 0, !0), he(g.$slots, "pagination", { paginationConfig: g.paginationConfig, paginationState: g.paginationState }, void 0, !0) ])) ], 46, oa)); } }), ra = /* @__PURE__ */ de(sa, [["__scopeId", "data-v-949bbf2e"]]), ca = /* @__PURE__ */ ce({ name: "AppIcon", inheritAttrs: !1, __name: "Icon", props: { icon: {}, size: { default: "default" }, color: { default: "current" }, class: {} }, setup(l) { const t = l, e = { // 排序图标 "sort-asc": "lucide:arrow-up", "sort-desc": "lucide:arrow-down", "sort-both": "lucide:arrow-down-up", // 分页图标 first: "lucide:chevrons-left", prev: "lucide:chevron-left", next: "lucide:chevron-right", last: "lucide:chevrons-right", // 通用图标 empty: "lucide:inbox", expand: "lucide:chevron-right", collapse: "lucide:chevron-down", // 主题切换图标 light: "lucide:sun", dark: "lucide:moon", auto: "lucide:monitor", // 工具栏图标 create: "lucide:plus", edit: "lucide:edit-3", delete: "lucide:trash-2", "bulk-delete": "lucide:trash-2", search: "lucide:search", filter: "lucide:filter", "filter-filled": "lucide:funnel-x", refresh: "lucide:refresh-cw", settings: "lucide:settings", export: "lucide:download", fullscreen: "lucide:maximize", "fullscreen-exit": "lucide:minimize", more: "lucide:more-horizontal" }, n = { xs: "w-3 h-3", sm: "w-4 h-4", default: "w-5 h-5", md: "w-6 h-6", lg: "w-7 h-7", xl: "w-8 h-8", "2xl": "w-10 h-10" }, a = T(() => t?.icon?.includes(":") ? t.icon : e[t.icon] || t.icon), o = T(() => { const v = []; return typeof t.size == "string" && v.push(n[t.size] || n.default), t.color && t.color !== "current" && v.push(`text-${t.color}`), t.class && v.push(t.class), v.join(" "); }), i = T(() => { const v = {}; return typeof t.size == "number" && (v.width = `${t.size}px`, v.height = `${t.size}px`), v; }); return (v, g) => (B(), ue(_(Nl), ht({ icon: a.value, class: o.value, style: i.value }, v.$attrs), null, 16, ["icon", "class", "style"])); } }), qe = /* @__PURE__ */ de(ca, [["__scopeId", "data-v-2713a348"]]); function hr(l, t, e, n = {}) { const { config: a = { enabled: !0, mode: "auto", updateDelay: 50 }, onCascadeUpdate: o, onCascadeInfoChange: i } = n, v = K(a.enabled), g = K(null), d = T(() => { if (!v.value) return Ze(t.value, l).map((S) => ({ value: S, label: Rt(S), count: Tt(t.value, l, S), available: !0, disabled: !1 })); const c = Object.fromEntries( Object.entries(e.value).filter(([S]) => S !== l) ); if (Object.keys(c).length === 0) return Ze(t.value, l).map((u) => ({ value: u, label: Rt(u), count: Tt(t.value, l, u), available: !0, disabled: !1 })); const r = Object.entries(c).reduce((S, [u, C]) => ua(S, u, C), t.value), s = Ze(r, l); return Ze(t.value, l).map((S) => { const u = s.includes(S); return { value: S, label: Rt(S), count: Tt(u ? r : t.value, l, S), available: u, disabled: !u }; }); }), w = T(() => { const c = Ze(t.value, l), s = d.value.filter((u) => u.available).length, p = Object.keys(e.value).filter((u) => u !== l); return { total: c.length, available: s, filtered: c.length - s, hasActiveFilters: p.length > 0, appliedFilters: p }; }), b = () => { g.value && clearTimeout(g.value), g.value = setTimeout(() => { const c = d.value, r = w.value; o?.(l, c), i?.(l, r), g.value = null; }, a.updateDelay || 50); }, h = (c) => { v.value = c, me(() => { b(); }); }; ee( e, (c, r) => { const s = Object.keys({ ...c, ...r }).filter( (p) => p !== l ); s.length > 0 && s.some((S) => { const u = c[S], C = r?.[S]; return JSON.stringify(u) !== JSON.stringify(C); }) && b(); }, { deep: !0 } ), ee( t, () => { b(); }, { deep: !0 } ), ee( w, (c) => { i?.(l, c); }, { deep: !0 } ); const f = () => { g.value && (clearTimeout(g.value), g.value = null); }; return Ne(f), mt(f), { availableOptions: d, cascadeInfo: w, refreshOptions: b, enableCascade: h, isCascadeEnabled: v }; } function Ze(l, t) { return [ ...new Set( l.map((e) => e[t]).filter((e) => e != null) ) ]; } function Rt(l) { return l == null ? "(空值)" : typeof l == "boolean" ? l ? "是" : "否" : l instanceof Date ? l.toLocaleDateString() : String(l); } function Tt(l, t, e) { return l.filter((n) => n[t] === e).length; } function ua(l, t, e) { if (!e) return l; if (Array.isArray(e)) return l.filter((n) => e.includes(n[t])); if (typeof e == "object" && "operator" in e && "value" in e) { const { operator: n, value: a, caseSensitive: o } = e; return l.filter((i) => { const v = o ? String(i[t]) : String(i[t]).toLowerCase(), g = o ? String(a) : String(a).toLowerCase(); switch (n) { case "contains": return v.includes(g); case "notContains": return !v.includes(g); case "equals": return v === g; case "notEquals": return v !== g; case "startsWith": return v.startsWith(g); case "endsWith": return v.endsWith(g); default: return !0; } }); } if (typeof e == "object" && ("min" in e || "max" in e)) { const { min: n, max: a } = e; return l.filter((o) => { const i = parseFloat(o[t]); return isNaN(i) ? !1 : (n === void 0 || i >= n) && (a === void 0 || i <= a); }); } if (typeof e == "object" && ("startDate" in e || "endDate" in e)) { const { startDate: n, endDate: a } = e; return l.filter((o) => { const i = new Date(o[t]); return isNaN(i.getTime()) ? !1 : (!n || i >= new Date(n)) && (!a || i <= new Date(a)); }); } if (typeof e == "object" && "value" in e) { const { value: n } = e; return n === null ? l : l.filter((a) => !!a[t] == !!n); } return l.filter((n) => n[t] === e); } class da { constructor(t = 100) { Q(this, "updateQueues", /* @__PURE__ */ new Map()); Q(this, "batchTimeout", null); Q(this, "batchDelay"); this.batchDelay = t; } // 添加更新任务到队列 scheduleUpdate(t, e) { this.updateQueues.has(t) || this.updateQueues.set(t, /* @__PURE__ */ new Set()), this.updateQueues.get(t).add(e), this.batchTimeout || (this.batchTimeout = setTimeout(() => { this.flushUpdates(); }, this.batchDelay)); } // 立即刷新指定列的更新 flushColumn(t) { const e = this.updateQueues.get(t); e && (e.forEach((n) => { try { n(); } catch { } }), e.clear()); } // 刷新所有待更新的列 flushUpdates() { this.updateQueues.forEach((t, e) => { this.flushColumn(e); }), this.updateQueues.clear(), this.batchTimeout && (clearTimeout(this.batchTimeout), this.batchTimeout = null); } // 清理所有待更新任务 clear() { this.updateQueues.clear(), this.batchTimeout && (clearTimeout(this.batchTimeout), this.batchTimeout = null); } // 获取待更新的列数量 getPendingCount() { return this.updateQueues.size; } } const wn = new da(), Mt = /* @__PURE__ */ new Set(); function mt(l) { Mt.add(l); } function sn() { try { wn.clear(), Mt.forEach((l) => { try { l(); } catch { } }), Mt.clear(); } catch { } } function fa() { typeof window < "u" && (window.addEventListener("beforeunload", sn, { passive: !0 }), window.addEventListener("pagehide", sn, { passive: !0 }), document.addEventListener( "visibilitychange", () => { document.visibilityState === "hidden" && wn.clear(); }, { passive: !0 } )); } typeof window < "u" && !window.process?.env?.NODE_ENV?.includes("test") && fa(); const xn = { sampleSize: 100, dateFormats: ["YYYY-MM-DD", "MM/DD/YYYY", "DD/MM/YYYY", "YYYY/MM/DD", "MM-DD-YYYY", "DD-MM-YYYY"], booleanValues: [ !0, !1, "true", "false", "True", "False", "TRUE", "FALSE", 1, 0, "1", "0", "yes", "no", "Yes", "No", "YES", "NO", "on", "off", "On", "Off", "ON", "OFF", "是", "否", "启用", "禁用", "开启", "关闭" ], selectThreshold: 10, // 唯一值少于10个且占比小于80%时识别为选择类型 confidenceThreshold: 0.6 // 最小置信度阈值 }, it = /* @__PURE__ */ new Map(); function ga(l = {}) { const t = { ...xn, ...l.config }, e = l.cache !== !1, n = (d, w) => a(d, w).type, a = (d, w) => { if (d.filter && typeof d.filter == "object" && "type" in d.filter && d.filter.type !== "auto") return { type: d.filter.type, confidence: 1, sampleSize: 0, reasoning: "显式配置的筛选类型" }; const b = pa(d.key, w); if (e && it.has(b)) return it.get(b); const h = o(d.key, w); return e && h.confidence >= t.confidenceThreshold && it.set(b, h), h; }, o = (d, w) => { const b = va(w, d, t.sampleSize); if (b.length === 0) return { type: "text", confidence: 0.5, sampleSize: 0, reasoning: "无可用数据样本,默认为文本类型" }; const h = [ ma(b), Ca(b, t.dateFormats), // 日期检测优先级高于数字 ya(b), ba(b, w.length, t.selectThreshold), Sa(b) // 文本类型作为兜底 ], f = h.filter( (r) => r.confidence >= t.confidenceThreshold ); return f.length > 0 ? f.sort((s, p) => p.confidence - s.confidence)[0] : h.sort((r, s) => s.confidence - r.confidence)[0] || { type: "text", confidence: 0.5, sampleSize: b.length, reasoning: "所有检测结果置信度不足,默认为文本类型" }; }, i = () => { it.clear(); }, v = () => ({ ...t }), g = (d) => { Object.assign(t, d), i(); }; return mt(i), { detectType: n, detectWithDetails: a, analyzeColumn: o, clearCache: i, getConfig: v, updateConfig: g }; } function ha(l, t) { const n = ga().detectType(l, t); return n && n !== "auto" ? n : "text"; } function va(l, t, e) { return l.slice(0, e).map((n) => n[t]).filter((n) => n != null && n !== ""); } function pa(l, t) { const e = t.length > 0 ? JSON.stringify(t.slice(0, 10).map((n) => n[l])) : "empty"; return `${l}_${e.length}_${t.length}`; } function ma(l) { if (l.length === 0) return { type: "boolean", confidence: 0, sampleSize: 0, reasoning: "无样本数据" }; const t = new Set(xn.booleanValues), e = l.filter((a) => t.has(a)).length; return { type: "boolean", confidence: e / l.length, sampleSize: l.length, reasoning: `${e}/${l.length} 个值为布尔类型值` }; } function ya(l) { if (l.length === 0) return { type: "number", confidence: 0, sampleSize: 0, reasoning: "无样本数据" }; const t = l.filter((n) => { if (typeof n == "number") return !0; if (typeof n == "string") { const a = parseFloat(n); return !isNaN(a) && isFinite(a); } return !1; }).length; return { type: "number", confidence: t / l.length, sampleSize: l.length, reasoning: `${t}/${l.length} 个值为数值类型` }; } function Ca(l, t) { if (l.length === 0) return { type: "date", confidence: 0, sampleSize: 0, reasoning: "无样本数据" }; const e = l.filter((a) => { if (a instanceof Date) return !isNaN(a.getTime()); if (typeof a == "string") { if (t.some((i) => wa(a, i))) return !0; const o = new Date(a); if (!isNaN(o.getTime())) return !(/^\d+$/.test(a) || !/[-/\s:]/.test(a)); } if (typeof a == "number" && a > 9466848e5 && a < 41024448e5) { const o = new Date(a); return !isNaN(o.getTime()); } return !1; }).length; return { type: "date", confidence: e / l.length, sampleSize: l.length, reasoning: `${e}/${l.length} 个值为日期类型` }; } function ba(l, t, e) { if (l.length === 0) return { type: "select", confidence: 0, sampleSize: 0, reasoning: "无样本数据" }; const a = new Set(l).size, o = a / l.length; return { type: "select", confidence: a <= e && o < 0.8 && // 放宽唯一值比例限制 a > 1 ? Math.min(0.8, 1 - o + (e - a) / e) : 0, sampleSize: l.length, reasoning: `唯一值数量: ${a}, 占比: ${(o * 100).toFixed(1)}%, 阈值: ${e}` }; } function Sa(l) { return { type: "text", confidence: 0.6, // 文本作为兜底类型,置信度适中 sampleSize: l.length, reasoning: "默认文本类型,适用于所有字符串数据" }; } function wa(l, t) { const n = { "YYYY-MM-DD": /^\d{4}-\d{2}-\d{2}$/, "MM/DD/YYYY": /^\d{2}\/\d{2}\/\d{4}$/, "DD/MM/YYYY": /^\d{2}\/\d{2}\/\d{4}$/, "YYYY/MM/DD": /^\d{4}\/\d{2}\/\d{2}$/, "MM-DD-YYYY": /^\d{2}-\d{2}-\d{4}$/, "DD-MM-YYYY": /^\d{2}-\d{2}-\d{4}$/ }[t]; if (!n || !n.test(l)) return !1; const a = new Date(l); return !isNaN(a.getTime()); } function Ke(l, t) { return t.split(".").reduce((e, n) => e && e[n] !== void 0 ? e[n] : void 0, l); } const xa = /[¥$€£,\s]/g; function Me(l, t = {}) { const { strict: e = !1, currencyPattern: n = xa } = t; if (typeof l == "number") return isNaN(l) ? null : l; if (e && typeof l != "number") return null; if (typeof l == "string") { const o = l.trim(); if (o === "") return null; const i = o.replace(n, "").trim(); if (i.match(/^-?\d+(\.\d+)?$/)) { const g = parseFloat(i); return isNaN(g) ? null : g; } return null; } if (l == null) return null; const a = Number(l); return isNaN(a) ? null : a; } function ka(l, t, e = {}) { const { includeNulls: n = !1 } = e, a = []; for (const o of l) { const i = Ke(o, t), v = Me(i, e); v !== null ? a.push(v) : n && i == null && a.push(0); } return a; } function Ea(l, t, e = {}) { const n = ka(l, t, e); if (n.length === 0) return { min: 0, max: 0, avg: 0 }; const a = Math.min(...n), o = Math.max(...n), i = n.reduce((v, g) => v + g, 0) / n.length; return { min: a, max: o, avg: i }; } const Ra = { class: "column-filter-panel" }, Ta = { class: "filter-header" }, Da = { class: "filter-title" }, Fa = { class: "filter-content" }, Ma = { class: "filter-actions" }, Pa = { class: "preview-info" }, $a = { key: 0, class: "preview-count" }, Ia = { class: "action-buttons" }, _a = ["disabled"], Aa = ["disabled"], za = /* @__PURE__ */ ce({ __name: "BaseColumnFilter", props: { column: {}, value: {}, cascadeEnabled: { type: Boolean, default: !0 }, totalRecordsCount: { default: 0 }, previewCount: { default: null }, canApply: { type: Boolean, default: !0 } }, emits: ["apply", "close", "reset"], setup(l, { emit: t }) { const e = l, n = t, a = T(() => e.value !== null && e.value !== void 0), o = T(() => !!e.canApply), i = () => { n("reset"); }, v = () => { n("apply", e.value); }; return (g, d) => (B(), N("div", Ra, [ V("div", Ta, [ V("h4", Da, Z(g.column.title), 1), he(g.$slots, "header-extra", {}, void 0, !0), V("button", { class: "close-btn", title: "关闭", onClick: d[0] || (d[0] = (w) => g.$emit("close")) }, " × ") ]), V("div", Fa, [ he(g.$slots, "default", {}, void 0, !0) ]), V("div", Ma, [ V("div", Pa, [ g.previewCount !== null ? (B(), N("span", $a, Z(g.previewCount) + " 条记录 ", 1)) : te("", !0) ]), V("div", Ia, [ V("button", { class: "reset-btn", disabled: !a.value, onClick: i }, " 重置 ", 8, _a), V("button", { class: "apply-btn", disabled: !o.value, onClick: v }, " 应用 ", 8, Aa) ]) ]) ])); } }), at = /* @__PURE__ */ de(za, [["__scopeId", "data-v-17fee5eb"]]), Oa = { class: "main-filter-row" }, Ba = ["placeholder"], Va = { key: 0, class: "preview-count ultra-compact" }, La = { class: "auxiliary-row" }, Wa = { class: "case-option" }, qa = /* @__PURE__ */ ce({ __name: "TextColumnFilter", props: { config: { default: () => ({ operators: ["contains", "notContains", "equals", "notEquals", "startsWith", "endsWith"], caseSensitive: !1 }) }, column: {}, value: {}, availableOptions: {}, onPreview: {} }, emits: ["apply", "preview", "close"], setup(l, { emit: t }) { const e = l, n = t, a = K(), o = K({ operator: "contains", value: "", caseSensitive: e.config?.caseSensitive || !1 }), i = K(null), v = T(() => o.value.value.trim().length > 0), g = () => { const r = e.column.title || "内容"; switch (o.value.operator) { case "contains": case "notContains": return `输入要包含的${r}...`; case "equals": case "notEquals": return `输入完整的${r}...`; case "startsWith": return `输入${r}开头...`; case "endsWith": return `输入${r}结尾...`; default: return `请输入${r}...`; } }, d = () => { h(); }, w = () => { h(); }, b = () => { h(); }, h = () => { try { if (!v.value) { i.value = null; return; } const r = { ...o.value }; e.onPreview && (i.value = e.onPreview(r)), n("preview", r); } catch { i.value = null; } }, f = () => { try { if (!v.value) return; const r = { ...o.value }; n("apply", r); } catch { } }, c = () => { try { o.value = { operator: "contains", value: "", caseSensitive: e.config?.caseSensitive || !1 }, i.value = null, n("apply", null); } catch { } }; return xe(async () => { try { if (e.value && typeof e.value == "object" && "operator" in e.value) { const r = e.value; o.value = { ...r }, h(); } await me(), a.value && a.value.focus(); } catch { } }), ee( () => e.value, (r) => { try { if (r && typeof r == "object" && "operator" in r) { const s = r; o.value = { ...s }, h(); } else r || (o.value = { operator: "contains", value: "", caseSensitive: e.config?.caseSensitive || !1 }, i.value = null); } catch { } }, { deep: !0, immediate: !1 } ), (r, s) => (B(), ue(at, { class: "text-filter-panel ultra-compact", column: r.column, value: o.value, "can-apply": v.value, onClose: s[3] || (s[3] = (p) => r.$emit("close")), onReset: c, onApply: f }, { default: Ce(() => [ V("div", Oa, [ ve(V("select", { "onUpdate:modelValue": s[0] || (s[0] = (p) => o.value.operator = p), class: "operator-select ultra-compact", onChange: d }, [...s[4] || (s[4] = [ V("option", { value: "contains" }, "包含", -1), V("option", { value: "notContains" }, "不包含", -1), V("option", { value: "equals" }, "等于", -1), V("option", { value: "notEquals" }, "不等于", -1), V("option", { value: "startsWith" }, "开头", -1), V("option", { value: "endsWith" }, "结尾", -1) ])], 544), [ [pt, o.value.operator] ]), ve(V("input", { ref_key: "inputRef", ref: a, "onUpdate:modelValue": s[1] || (s[1] = (p) => o.value.value = p), type: "text", placeholder: g(), class: "filter-input ultra-compact", onInput: w, onKeyup: nt(f, ["enter"]) }, null, 40, Ba), [ [He, o.value.value] ]), i.value !== null ? (B(), N("div", Va, Z(i.value), 1)) : te("", !0) ]), V("div", La, [ V("label", Wa, [ ve(V("input", { "onUpdate:modelValue": s[2] || (s[2] = (p) => o.value.caseSensitive = p), type: "checkbox", class: "case-checkbox", onChange: b }, null, 544), [ [lt, o.value.caseSensitive] ]), s[5] || (s[5] = V("span", { class: "case-label" }, "Aa", -1)) ]) ]) ]), _: 1 }, 8, ["column", "value", "can-apply"])); } }), Ha = /* @__PURE__ */ de(qa, [["__scopeId", "data-v-fb937b9b"]]), Na = { class: "mode-tabs" }, Ka = { key: 0, class: "filter-main compact" }, Ua = ["step"], ja = { key: 0, class: "preview-count compact" }, Ya = { key: 1, class: "filter-main compact" }, Ga = ["step"], Qa = ["step"], Xa = { key: 0, class: "preview-count compact" }, Ja = { key: 2, class: "filter-options-row" }, Za = { key: 0, class: "quick-filters compact" }, eo = ["title", "onClick"], to = { key: 1, class: "data-range-info compact" }, no = /* @__PURE__ */ ce({ __name: "NumberColumnFilter", props: { config: { default: () => ({ mode: "both", quickFilters: ["top10%", "bottom10%", "aboveAvg", "belowAvg"], precision: 2 }) }, dataRange: {}, calculateDataRange: {}, column: {}, value: {}, availableOptions: {}, onPreview: {} }, emits: ["apply", "preview", "close"], setup(l, { emit: t }) { const e = l, n = t, a = K({ mode: "range", precision: e.config?.precision || 2 }), o = K(null), i = K({ min: 0, max: 0, avg: 0 }), v = T(() => a.value.mode === "compare" ? a.value.compareValue !== void 0 && a.value.compareValue !== null && typeof a.value.operator == "string" && a.value.operator.length > 0 : a.value.minValue !== void 0 && a.value.minValue !== null || a.value.maxValue !== void 0 && a.value.maxValue !== null), g = T(() => e.config?.quickFilters && e.config.quickFilters.length > 0 && i.value && (i.value.min !== 0 || i.value.max !== 0)), d = T(() => { if (!e.config?.quickFilters || !i.value) return []; const C = []; return e.config.quickFilters.includes("top10%") && C.push({ key: "top10%", label: "Top 10%", description: "最高的 10% 数值", apply: (m) => ({ mode: "range", minValue: b(m.min + (m.max - m.min) * 0.9), maxValue: b(m.max) }) }), e.config.quickFilters.includes("bottom10%") && C.push({ key: "bottom10%", label: "Bottom 10%", description: "最低的 10% 数值", apply: (m) => ({ mode: "range", minValue: b(m.min), maxValue: b(m.min + (m.max - m.min) * 0.1) }) }), e.config.quickFilters.includes("aboveAvg") && C.push({ key: "aboveAvg", label: "平均值以上", description: "大于平均值的数据", apply: (m) => ({ mode: "compare", operator: ">=", compareValue: b(m.avg) }) }), e.config.quickFilters.includes("belowAvg") && C.push({ key: "belowAvg", label: "平均值以下", description: "小于平均值的数据", apply: (m) => ({ mode: "compare", operator: "<", compareValue: b(m.avg) }) }), C; }), w = () => { const C = a.value.precision || 2; return C === 0 ? 1 : (1 / Math.pow(10, C)).toString(); }, b = (C) => { const m = a.value.precision || 2; return m === 0 ? Math.round(C) : Number(C.toFixed(m)); }, h = (C) => { const m = a.value.precision || 2; return e.config?.format ? new Intl.NumberFormat("zh-CN", e.config.format).format(C) : C.toFixed(m); }, f = (C) => { a.value.mode = C, C === "range" ? (a.value.operator = void 0, a.value.compareValue = void 0) : (a.value.minValue = void 0, a.value.maxValue = void 0, a.value.operator || (a.value.operator = ">")), r(); }, c = () => { r(); }, r = () => { if (!v.value) { o.value = null; return; } const C = { ...a.value }; e.onPreview && (o.value = e.onPreview(C)), n("preview", C); }, s = () => { if (!v.value) return; const C = { ...a.value }; n("apply", C); }, p = () => { a.value = { mode: "range", precision: e.config?.precision || 2 }, o.value = null, n("apply", null); }, S = () => { if (e.calculateDataRange) try { i.value = e.calculateDataRange(); return; } catch { } if (e.dataRange && (e.dataRange.min !== 0 || e.dataRange.max !== 0 || e.dataRange.avg !== 0)) { i.value = { ...e.dataRange }; return; } i.value = { min: 5e3, max: 15e3, avg: 1e4 }; }, u = (C) => { const m = i.value; if (m.min === 0 && m.max === 0 && m.avg === 0) return; const R = C.apply(m); a.value = { ...a.value, ...R }, r(); }; return xe(() => { if (S(), e.value && typeof e.value == "object") { const C = e.value; a.value = { ...a.value, ...C }, r(); } a.value.mode === "compare" && !a.value.operator && (a.value.operator = ">"); }), (C, m) => (B(), ue(at, { class: "number-filter-panel compact", column: C.column, value: a.value, "can-apply": v.value, onClose: m[6] || (m[6] = (R) => C.$emit("close")), onReset: p, onApply: s }, { "header-extra": Ce(() => [ V("div", Na, [ V("button", { class: re(["mode-tab", { active: a.value.mode === "range" }]), onClick: m[0] || (m[0] = (R) => f("range")) }, " 范围 ", 2), V("button", { class: re(["mode-tab", { active: a.value.mode === "compare" }]), onClick: m[1] || (m[1] = (R) => f("compare")) }, " 条件 ", 2) ]) ]), default: Ce(() => [ a.value.mode === "compare" ? (B(), N("div", Ka, [ ve(V("select", { "onUpdate:modelValue": m[2] || (m[2] = (R) => a.value.operator = R), class: "operator-select compact", onChange: c }, [...m[7] || (m[7] = [ V("option", { value: ">" }, ">", -1), V("option", { value: "<" }, "<", -1), V("option", { value: "=" }, "=", -1), V("option", { value: "!=" }, "≠", -1), V("option", { value: ">=" }, ">=", -1), V("option", { value: "<=" }, "<=", -1) ])], 544), [ [pt, a.value.operator] ]), ve(V("input", { "onUpdate:modelValue": m[3] || (m[3] = (R) => a.value.compareValue = R), type: "number", step: w(), class: "number-input compact", placeholder: "输入数值", onInput: c, onKeyup: nt(s, ["enter"]) }, null, 40, Ua), [ [ He, a.value.compareValue, void 0, { number: !0 } ] ]), o.value !== null ? (B(), N("div", ja, Z(o.value) + " 条 ", 1)) : te("", !0) ])) : te("", !0), a.value.mode === "range" ? (B(), N("div", Ya, [ ve(V("input", { "onUpdate:modelValue": m[4] || (m[4] = (R) => a.value.minValue = R), type: "number", step: w(), class: "number-input compact range-input", placeholder: "最小值", onInput: c, onKeyup: nt(s, ["enter"]) }, null, 40, Ga), [ [ He, a.value.minValue, void 0, { number: !0 } ] ]), m[8] || (m[8] = V("span", { class: "range-separator" }, "-", -1)), ve(V("input", { "onUpdate:modelValue": m[5] || (m[5] = (R) => a.value.maxValue = R), type: "number", step: w(), class: "number-input compact range-input", placeholder: "最大值", onInput: c, onKeyup: nt(s, ["enter"]) }, null, 40, Qa), [ [ He, a.value.maxValue, void 0, { number: !0 } ] ]), o.value !== null ? (B(), N("div", Xa, Z(o.value) + " 条 ", 1)) : te("", !0) ])) : te("", !0), g.value || C.dataRange ? (B(), N("div", Ja, [ g.value && d.value.length > 0 ? (B(), N("div", Za, [ (B(!0), N(we, null, Oe(d.value, (R) => (B(), N("button", { key: R.key, class: "quick-filter-btn compact", title: R.description, onClick: (P) => u(R) }, Z(R.label), 9, eo))), 128)) ])) : te("", !0), i.value && (i.value.min !== 0 || i.value.max !== 0) ? (B(), N("div", to, " 范围: " + Z(h(i.value.min)) + " - " + Z(h(i.value.max)), 1)) : te("", !0) ])) : te("", !0) ]), _: 1 }, 8, ["column", "value", "can-apply"])); } }), lo = /* @__PURE__ */ de(no, [["__scopeId", "data-v-fb632363"]]), ao = { key: 0, class: "quick-section" }, oo = { class: "quick-options compact" }, io = ["onClick"], so = { class: "date-range-row" }, ro = { key: 0, class: "preview-count compact" }, co = { key: 1, class: "relative-options-row" }, uo = { class: "checkbox-option compact" }, fo = { class: "checkbox-option compact" }, go = { class: "checkbox-option compact" }, ho = { key: 2, class: "date-range-display compact" }, vo = /* @__PURE__ */ ce({ __name: "DateColumnFilter", props: { config: { default: () => ({ quickOptions: ["today", "yesterday", "thisWeek", "thisMonth", "last7days", "last30days"], relative: !0 }) }, column: {}, value: {}, availableOptions: {}, onPreview: {} }, emits: ["apply", "preview", "close"], setup(l, { emit: t }) { const e = l, n = t, a = K({ includeToday: !1, weekdaysOnly: !1, weekendsOnly: !1 }), o = K(null), i = T(() => !!(a.value.startDate || a.value.endDate)), v = T(() => e.config?.quickOptions && e.config.quickOptions.length > 0), g = T(() => { if (!e.config?.quickOptions) return []; const u = /* @__PURE__ */ new Date(), C = []; if (e.config.quickOptions.includes("today") && C.push({ key: "today", label: "今天", description: "今天的数据", apply: () => ({ startDate: d(u), endDate: d(u) }) }), e.config.quickOptions.includes("yesterday")) { const m = new Date(u); m.setDate(u.getDate() - 1), C.push({ key: "yesterday", label: "昨天", description: "昨天的数据", apply: () => ({ startDate: d(m), endDate: d(m) }) }); } if (e.config.quickOptions.includes("thisWeek")) { const m = new Date(u), R = u.getDay(), P = u.getDate() - R + (R === 0 ? -6 : 1); m.setDate(P), C.push({ key: "thisWeek", label: "本周", description: "本周的数据", apply: () => ({ startDate: d(m), endDate: d(u) }) }); } if (e.config.quickOptions.includes("thisMonth")) { const m = new Date(u.getFullYear(), u.getMonth(), 1); C.push({ key: "thisMonth", label: "本月", description: "本月的数据", apply: () => ({ startDate: d(m), endDate: d(u) }) }); } if (e.config.quickOptions.includes("last7days")) { const m = new Date(u); m.setDate(u.getDate() - 7), C.push({ key: "last7days", label: "过去7天", description: "过去7天的数据", apply: () => ({ startDate: d(m), endDate: d(u) }) }); } if (e.config.quickOptions.includes("last30days")) { const m = new Date(u); m.setDate(u.getDate() - 30), C.push({ key: "last30days", label: "过去30天", description: "过去30天的数据", apply: () => ({ startDate: d(m), endDate: d(u) }) }); } if (e.config.quickOptions.includes("thisQuarter")) { const m = Math.floor(u.getMonth() / 3), R = new Date(u.getFullYear(), m * 3, 1); C.push({ key: "thisQuarter", label: "本季度", description: "本季度的数据", apply: () => ({ startDate: d(R), endDate: d(u) }) }); } if (e.config.quickOptions.includes("thisYear")) { const m = new Date(u.getFullYear(), 0, 1); C.push({ key: "thisYear", label: "今年", description: "今年的数据", apply: () => ({ startDate: d(m), endDate: d(u) }) }); } return C; }), d = (u) => u.toISOString().split("T")[0], w = (u) => { if (!u) return ""; const C = typeof u == "string" ? new Date(u) : u; return e.config?.format ? new Intl.DateTimeFormat("zh-CN", e.config.format).format(C) : C.toLocaleDateString("zh-CN"); }, b = () => { const { startDate: u, endDate: C } = a.value; return u && C ? u === C ? w(u) : `${w(u)} - ${w(C)}` : u ? `从 ${w(u)}` : C ? `到 ${w(C)}` : ""; }, h = (u) => a.value.quickOption === u.key, f = () => { a.value.quickOption = void 0, r(); }, c = () => { a.value.weekdaysOnly && a.value.weekendsOnly && (a.value.weekdaysOnly ? a.value.weekendsOnly = !1 : a.value.weekdaysOnly = !1), r(); }, r = () => { if (!i.value) { o.value = null; return; } const u = { ...a.value }; e.onPreview && (o.value = e.onPreview(u)), n("preview", u); }, s = () => { if (!i.value) return; const u = { ...a.value }; n("apply", u); }, p = () => { a.value = { includeToday: !1, weekdaysOnly: !1, weekendsOnly: !1 }, o.value = null, n("apply", null); }, S = (u) => { const C = u.apply(); a.value = { ...a.value, ...C, quickOption: u.key }, r(); }; return xe(() => { if (e.value && typeof e.value == "object") { const u = e.value; a.value = { ...a.value, ...u }, r(); } }), (u, C) => (B(), ue(at, { class: "date-filter-panel compact", column: u.column, value: a.value, "can-apply": i.value, onClose: C[5] || (C[5] = (m) => u.$emit("close")), onReset: p, onApply: s }, { default: Ce(() => [ v.value ? (B(), N("div", ao, [ V("div", oo, [ (B(!0), N(we, null, Oe(g.value, (m) => (B(), N("button", { key: m.key, class: re(["quick-option-btn compact", { active: h(m) }]), onClick: (R) => S(m) }, Z(m.label), 11, io))), 128)) ]) ])) : te("", !0), V("div", so, [ ve(V("input", { "onUpdate:modelValue": C[0] || (C[0] = (m) => a.value.startDate = m), type: "date", class: "date-input compact", placeholder: "开始日期", onChange: f }, null, 544), [ [He, a.value.startDate] ]), C[6] || (C[6] = V("span", { class: "date-separator" }, "-", -1)), ve(V("input", { "onUpdate:modelValue": C[1] || (C[1] = (m) => a.value.endDate = m), type: "date", class: "date-input compact", placeholder: "结束日期", onChange: f }, null, 544), [ [He, a.value.endDate] ]), o.value !== null ? (B(), N("div", ro, Z(o.value) + " 条 ", 1)) : te("", !0) ]), u.config?.relative ? (B(), N("div", co, [ V("label", uo, [ ve(V("input", { "onUpdate:modelValue": C[2] || (C[2] = (m) => a.value.includeToday = m), type: "checkbox", onChange: c }, null, 544), [ [lt, a.value.includeToday] ]), C[7] || (C[7] = V("span", null, "含今天", -1)) ]), V("label", fo, [ ve(V("input", { "onUpdate:modelValue": C[3] || (C[3] = (m) => a.value.weekdaysOnly = m), type: "checkbox", onChange: c }, null, 544), [ [lt, a.value.weekdaysOnly] ]), C[8] || (C[8] = V("span", null, "工作日", -1)) ]), V("label", go, [ ve(V("input", { "onUpdate:modelValue": C[4] || (C[4] = (m) => a.value.weekendsOnly = m), type: "checkbox", onChange: c }, null, 544), [ [lt, a.value.weekendsOnly] ]), C[9] || (C[9] = V("span", null, "周末", -1)) ]) ])) : te("", !0), a.value.startDate || a.value.endDate ? (B(), N("div", ho, Z(b()), 1)) : te("", !0) ]), _: 1 }, 8, ["column", "value", "can-apply"])); } }), po = /* @__PURE__ */ de(vo, [["__scopeId", "data-v-61f6da0d"]]), mo = { class: "boolean-options-row" }, yo = { key: 0, class: "stats-row" }, Co = { class: "stat-item" }, bo = { class: "stat-item" }, So = /* @__PURE__ */ ce({ __name: "BooleanColumnFilter", props: { config: { default: () => ({ labels: ["是", "否"], showStats: !0 }) }, statistics: {}, column: {}, value: {}, availableOptions: {}, onPreview: {} }, emits: ["apply", "preview", "close"], setup(l, { emit: t }) { const e = l, n = t, a = K({ value: null }), o = K(null), i = T(() => e.config?.showStats !== !1 && e.statistics), v = T(() => !0), g = () => e.config?.labels?.[0] || "是", d = () => e.config?.labels?.[1] || "否", w = (c) => { a.value.value = c, b(); }, b = () => { const c = a.value.value === null ? null : { ...a.value }; e.onPreview && (o.value = e.onPreview(c)), n("preview", c); }, h = () => { const c = a.value.value === null ? null : { ...a.value }; n("apply", c); }, f = () => { a.value = { value: null }, o.value = null, n("apply", null); }; return xe(() => { if (e.value && typeof e.value == "object" && "value" in e.value) { const c = e.value; a.value = { ...c }, b(); } b(); }), (c, r) => (B(), ue(at, { column: c.column, value: a.value, "preview-count": o.value, "can-apply": v.value, onClose: r[3] || (r[3] = (s) => c.$emit("close")), onReset: f, onApply: h }, { default: Ce(() => [ V("div", mo, [ V("button", { class: re(["boolean-option-btn", { active: a.value.value === null }]), onClick: r[0] || (r[0] = (s) => w(null)) }, " 全部 ", 2), V("button", { class: re(["boolean-option-btn", { active: a.value.value === !0 }]), onClick: r[1] || (r[1] = (s) => w(!0)) }, Z(g()), 3), V("button", { class: re(["boolean-option-btn", { active: a.value.value === !1 }]), onClick: r[2] || (r[2] = (s) => w(!1)) }, Z(d()), 3) ]), i.value && c.statistics ? (B(), N("div", yo, [ V("span", Co, Z(g()) + ": " + Z(c.statistics.trueCount) + "条 (" + Z(c.statistics.truePercentage) + "%) ", 1), r[4] || (r[4] = V("span", { class: "stat-separator" }, "|", -1)), V("span", bo, Z(d()) + ": " + Z(c.statistics.falseCount) + "条 (" + Z(c.statistics.falsePercentage) + "%) ", 1) ])) : te("", !0) ]), _: 1 }, 8, ["column", "value", "preview-count", "can-apply"])); } }), wo = /* @__PURE__ */ de(So, [["__scopeId", "data-v-1c51fc74"]]), xo = { key: 0, class: "search-input-compact" }, ko = ["placeholder"], Eo = { key: 0, class: "cascade-hint compact" }, Ro = { class: "quick-actions-row" }, To = { class: "selection-count" }, Do = ["value", "disabled"], Fo = { class: "option-label" }, Mo = { key: 0, class: "option-count compact" }, Po = { key: 0, class: "empty-state compact" }, $o = { key: 1, class: "cascade-status compact" }, Io = /* @__PURE__ */ ce({ __name: "SelectColumnFilter", props: { cascadeEnabled: { type: Boolean, default: !0 }, totalRecordsCount: { default: 0 }, config: { default: () => ({ search: !0, quickActions: !0, maxHeight: 300 }) }, availableOptions: { default: () => [] }, cascadeInfo: {}, column: {}, value: {}, onPreview: {} }, emits: ["apply", "preview", "close"], setup(l, { emit: t }) { const e = l, n = t, a = K([]), o = K(""), i = K(null), v = T(() => e.config?.search !== !1), g = T(() => e.cascadeEnabled), d = T(() => e.config?.maxHeight || 300), w = T(() => `在${e.column.title}中搜索...`), b = T(() => e.cascadeEnabled && e.cascadeInfo && e.cascadeInfo.hasActiveFilters && e.cascadeInfo.appliedFilters.length > 0), h = T(() => { let m = e.availableOptions || []; if (o.value.trim()) { const R = o.value.toLowerCase(); m = m.filter((P) => P.label.toLowerCase().includes(R)); } return m.sort((R, P) => e.cascadeEnabled && R.available !== P.available ? R.available ? -1 : 1 : R.label.localeCompare(P.label)); }), f = () => { }, c = () => { o.value = ""; }, r = () => { const m = h.value.filter((R) => R.available !== !1 && !R.disabled).map((R) => R.value); a.value = [.../* @__PURE__ */ new Set([...a.value, ...m])], p(); }, s = () => { a.value = [], p(); }, p = () => { S(); }, S = () => { const m = a.value.length > 0 ? a.value : null; e.onPreview && (i.value = e.onPreview(m)), n("preview", m); }, u = () => { const m = a.value.length > 0 ? [...a.value] : null; n("apply", m); }, C = () => { a.value = [], i.value = null, o.value = "", n("apply", null); }; return ee( () => e.availableOptions, () => { if (e.cascadeEnabled && a.value.length > 0) { const m = new Set( (e.availableOptions || []).filter((P) => P.available !== !1).map((P) => P.value) ), R = a.value.filter((P) => m.has(P)); R.length !== a.value.length && (a.value = R, S()); } }, { deep: !0 } ), xe(() => { e.value && Array.isArray(e.value) && (a.value = [...e.value], S()), (!e.value || Array.isArray(e.value) && e.value.length === 0) && S(); }), (m, R) => (B(), ue(at, { class: "select-filter-panel compact", column: m.column, value: a.value, onClose: R[2] || (R[2] = (P) => m.$emit("close")), onReset: C, onApply: u }, { "header-extra": Ce(() => [ v.value ? (B(), N("div", xo, [ ve(V("input", { "onUpdate:modelValue": R[0] || (R[0] = (P) => o.value = P), type: "text", placeholder: w.value, class: "search-input", onInput: f }, null, 40, ko), [ [He, o.value] ]), o.value ? (B(), N("button", { key: 0, class: "clear-search-btn", onClick: c }, " × ")) : te("", !0) ])) : te("", !0) ]), default: Ce(() => [ b.value ? (B(), N("div", Eo, " 📊 共 " + Z(m.totalRecordsCount) + " 条," + Z(m.cascadeInfo?.appliedFilters.length || 0) + " 个筛选生效 ", 1)) : te("", !0), V("div", Ro, [ V("div", { class: "quick-actions compact" }, [ V("button", { class: "quick-action-btn compact", onClick: r }, " 全选 "), V("button", { class: "quick-action-btn compact", onClick: s }, " 清除 ") ]), V("div", To, Z(a.value.length) + "/" + Z(h.value.length), 1) ]), V("div", { class: "options-list compact", style: _e({ maxHeight: d.value + "px" }) }, [ (B(!0), N(we, null, Oe(h.value, (P) => (B(), N("label", { key: String(P.value), class: re(["option-item compact", { disabled: P.disabled, unavailable: !P.available && m.cascadeEnabled }]) }, [ ve(V("input", { "onUpdate:modelValue": R[1] || (R[1] = (M) => a.value = M), type: "checkbox", value: P.value, disabled: P.disabled, class: "option-checkbox", onChange: p }, null, 40, Do), [ [lt, a.value] ]), V("span", Fo, Z(P.label), 1), g.value && P.count !== void 0 ? (B(), N("span", Mo, Z(P.count), 1)) : te("", !0) ], 2))), 128)), h.value.length === 0 ? (B(), N("div", Po, " 🔍 " + Z(o.value ? "无匹配项" : "无可用选项"), 1)) : te("", !0) ], 4), m.cascadeInfo && m.cascadeInfo.hasActiveFilters ? (B(), N("div", $o, " 可用: " + Z(m.cascadeInfo.available) + "/"