online-analysis-button 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,380 @@
1
+ # Online Analysis Button
2
+
3
+ 一个专门适配 Element UI `el-table` 的 Vue 2 组件,可以自动提取表格数据并进行透视分析。
4
+
5
+ ## ✨ 特性
6
+
7
+ - 🚀 **一键分析**:点击按钮即可自动提取 Element UI el-table 中的数据
8
+ - 📊 **透视分析**:基于 PivotTable.js 的强大透视表功能
9
+ - 🎨 **美观界面**:现代化的 UI 设计,支持多种主题
10
+ - 📤 **数据导出**:支持导出为 Excel 和 HTML 格式
11
+ - 🔍 **数据过滤**:支持多种过滤条件
12
+ - 📱 **响应式设计**:完美适配各种屏幕尺寸
13
+ - 🎯 **灵活配置**:支持自定义按钮位置、样式和表格选择器
14
+ - 🖥️ **弹出式展示**:透视分析页面以模态框形式展示,不干扰原页面布局
15
+
16
+ ## 📦 安装
17
+
18
+ ```bash
19
+ npm install online-analysis-button
20
+ ```
21
+
22
+ ## 🚀 快速开始
23
+
24
+ ### 方式一:全局注册
25
+
26
+ ```javascript
27
+ import Vue from 'vue'
28
+ import OnlineAnalysisButton from 'online-analysis-button'
29
+
30
+ Vue.use(OnlineAnalysisButton)
31
+ ```
32
+
33
+ ### 方式二:局部注册
34
+
35
+ ```javascript
36
+ import { OnlineAnalysisButton } from 'online-analysis-button'
37
+
38
+ export default {
39
+ components: {
40
+ OnlineAnalysisButton
41
+ }
42
+ }
43
+ ```
44
+
45
+ ## 📖 使用方法
46
+
47
+ ### 基础用法
48
+
49
+ ```html
50
+ <template>
51
+ <div>
52
+ <!-- 你的 Element UI 表格 -->
53
+ <el-table :data="tableData">
54
+ <el-table-column prop="name" label="姓名" />
55
+ <el-table-column prop="age" label="年龄" />
56
+ <el-table-column prop="city" label="城市" />
57
+ <el-table-column prop="sales" label="销售额" />
58
+ </el-table>
59
+
60
+ <!-- 分析按钮 -->
61
+ <OnlineAnalysisButton />
62
+ </div>
63
+ </template>
64
+
65
+ <script>
66
+ export default {
67
+ data() {
68
+ return {
69
+ tableData: [
70
+ { name: '张三', age: 25, city: '北京', sales: 10000 },
71
+ { name: '李四', age: 30, city: '上海', sales: 15000 },
72
+ { name: '王五', age: 28, city: '广州', sales: 12000 }
73
+ ]
74
+ }
75
+ }
76
+ }
77
+ </script>
78
+ ```
79
+
80
+ ### 自定义配置
81
+
82
+ ```html
83
+ <template>
84
+ <div>
85
+ <el-table id="my-table" :data="tableData">
86
+ <!-- 表格列 -->
87
+ </el-table>
88
+
89
+ <!-- 自定义按钮 -->
90
+ <OnlineAnalysisButton
91
+ position="top-right"
92
+ button-text="开始分析"
93
+ button-class="custom-btn"
94
+ table-selector="#my-table"
95
+ :auto-extract="true"
96
+ />
97
+ </div>
98
+ </template>
99
+
100
+ <style>
101
+ .custom-btn {
102
+ background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
103
+ }
104
+ </style>
105
+ ```
106
+
107
+ ## 🎛️ Props
108
+
109
+ | 参数 | 类型 | 默认值 | 说明 |
110
+ |------|------|--------|------|
111
+ | `position` | String | `'bottom-right'` | 按钮位置:`'bottom-right'`, `'bottom-left'`, `'top-right'`, `'top-left'` |
112
+ | `buttonText` | String | `'📊 透视分析'` | 按钮显示文本 |
113
+ | `buttonClass` | String | `''` | 自定义按钮样式类名 |
114
+ | `tableSelector` | String | `'.el-table'` | Element UI 表格选择器,用于指定要分析的表格 |
115
+ | `autoExtract` | Boolean | `true` | 是否自动提取表格数据 |
116
+
117
+ ## 🎯 方法
118
+
119
+ ### setData(data)
120
+
121
+ 手动设置要分析的数据。
122
+
123
+ ```javascript
124
+ this.$refs.analysisButton.setData([
125
+ { name: '张三', age: 25, city: '北京' },
126
+ { name: '李四', age: 30, city: '上海' }
127
+ ])
128
+ ```
129
+
130
+ ### getData()
131
+
132
+ 获取当前分析的数据。
133
+
134
+ ```javascript
135
+ const data = this.$refs.analysisButton.getData()
136
+ ```
137
+
138
+ ## 📊 功能说明
139
+
140
+ ### 1. 数据提取
141
+
142
+ 组件会自动查找页面中符合 `tableSelector` 的第一个 Element UI `el-table` 组件,并提取其中的数据。支持以下特性:
143
+
144
+ - 自动识别表头(`el-table__header-wrapper`)
145
+ - 自动提取数据行(`el-table__body-wrapper`)
146
+ - 智能数据类型转换(数字、字符串等)
147
+ - 支持多列表格
148
+
149
+ ### 2. 透视分析
150
+
151
+ 提供强大的透视表功能:
152
+
153
+ - **拖拽字段**:将字段拖拽到行、列、值区域
154
+ - **多种聚合方式**:计数、求和、平均值、最大值、最小值等
155
+ - **多种渲染器**:表格、热力图、柱状图、折线图等
156
+ - **数据过滤**:支持多种过滤条件
157
+
158
+ ### 3. 数据导出
159
+
160
+ 支持导出为多种格式:
161
+
162
+ - **Excel (.xlsx)**:透视表数据和原始数据
163
+ - **HTML (.html)**:带样式的 HTML 文件
164
+
165
+ ### 4. 数据过滤
166
+
167
+ 支持多种过滤操作:
168
+
169
+ - 等于、不等于
170
+ - 包含、不包含
171
+ - 大于、小于、大于等于、小于等于
172
+ - 包含于(多选)、不包含于(多选)
173
+
174
+ ### 5. 弹出式展示
175
+
176
+ - 透视分析页面以模态框形式展示
177
+ - 不干扰原页面布局
178
+ - 支持全屏模式
179
+ - 点击遮罩层可关闭
180
+
181
+ ## 🎨 样式定制
182
+
183
+ 组件使用了 scoped CSS,不会影响你的页面样式。如果需要自定义样式,可以通过以下方式:
184
+
185
+ ### 1. 使用 buttonClass 属性
186
+
187
+ ```html
188
+ <OnlineAnalysisButton button-class="my-custom-button" />
189
+ ```
190
+
191
+ ```css
192
+ .my-custom-button {
193
+ background: your-custom-gradient;
194
+ /* 其他样式 */
195
+ }
196
+ ```
197
+
198
+ ### 2. 全局样式覆盖
199
+
200
+ ```css
201
+ /* 覆盖按钮样式 */
202
+ .online-analysis-button .analysis-button {
203
+ /* 你的样式 */
204
+ }
205
+
206
+ /* 覆盖模态框样式 */
207
+ .modal-content {
208
+ /* 你的样式 */
209
+ }
210
+ ```
211
+
212
+ ## 🔧 依赖
213
+
214
+ - Vue 2.6.0+
215
+ - Element UI(用于 el-table)
216
+ - jQuery 3.7.1+
217
+ - jQuery UI 1.14.2+
218
+ - PivotTable.js 2.23.0+
219
+ - XLSX 0.18.5+
220
+ - Bootstrap 4.6.2+
221
+ - Vue Draggable 2.24.3+
222
+
223
+ ## 📝 开发
224
+
225
+ ### 安装依赖
226
+
227
+ ```bash
228
+ npm install
229
+ ```
230
+
231
+ ### 构建库
232
+
233
+ ```bash
234
+ npm run build:lib
235
+ ```
236
+
237
+ ### 预览
238
+
239
+ ```bash
240
+ npm run preview
241
+ ```
242
+
243
+ ## 🚀 发布到 npm
244
+
245
+ ### 1. 准备工作
246
+
247
+ 确保你已经:
248
+
249
+ - [ ] 注册了 [npm](https://www.npmjs.com/) 账号
250
+ - [ ] 完善了 `package.json` 中的信息(作者、仓库等)
251
+ - [ ] 测试了构建产物
252
+
253
+ ### 2. 登录 npm
254
+
255
+ ```bash
256
+ npm login
257
+ ```
258
+
259
+ ### 3. 发布
260
+
261
+ ```bash
262
+ npm publish
263
+ ```
264
+
265
+ ### 4. 更新版本
266
+
267
+ 如果需要更新版本:
268
+
269
+ ```bash
270
+ # 更新版本号(patch/minor/major)
271
+ npm version patch
272
+
273
+ # 重新构建
274
+ npm run build:lib
275
+
276
+ # 发布
277
+ npm publish
278
+ ```
279
+
280
+ ## 📄 License
281
+
282
+ MIT
283
+
284
+ ## 🤝 贡献
285
+
286
+ 欢迎提交 Issue 和 Pull Request!
287
+
288
+ ## 📮 联系方式
289
+
290
+ 如有问题或建议,请提交 Issue。
291
+
292
+ ## 🙏 致谢
293
+
294
+ - [PivotTable.js](https://github.com/nicolaskruchten/pivottable) - 强大的透视表库
295
+ - [Element UI](https://element.eleme.io/) - 优秀的 Vue 2 UI 组件库
296
+ - [Vue.js](https://vuejs.org/) - 渐进式 JavaScript 框架
297
+ - [Vite](https://vitejs.dev/) - 下一代前端构建工具
298
+
299
+ ## 📸 示例
300
+
301
+ ### 完整示例
302
+
303
+ ```html
304
+ <template>
305
+ <div>
306
+ <h1>销售数据分析</h1>
307
+
308
+ <el-table :data="salesData" border style="width: 100%">
309
+ <el-table-column prop="date" label="日期" width="180" />
310
+ <el-table-column prop="name" label="销售员" width="180" />
311
+ <el-table-column prop="product" label="产品" />
312
+ <el-table-column prop="quantity" label="数量" />
313
+ <el-table-column prop="price" label="单价" />
314
+ <el-table-column prop="total" label="总额" />
315
+ </el-table>
316
+
317
+ <!-- 分析按钮 -->
318
+ <OnlineAnalysisButton
319
+ position="bottom-right"
320
+ button-text="📊 数据透视分析"
321
+ />
322
+ </div>
323
+ </template>
324
+
325
+ <script>
326
+ import { OnlineAnalysisButton } from 'online-analysis-button'
327
+
328
+ export default {
329
+ components: {
330
+ OnlineAnalysisButton
331
+ },
332
+ data() {
333
+ return {
334
+ salesData: [
335
+ { date: '2024-01-01', name: '张三', product: '产品A', quantity: 10, price: 100, total: 1000 },
336
+ { date: '2024-01-02', name: '李四', product: '产品B', quantity: 5, price: 200, total: 1000 },
337
+ { date: '2024-01-03', name: '王五', product: '产品A', quantity: 8, price: 100, total: 800 },
338
+ { date: '2024-01-04', name: '张三', product: '产品C', quantity: 3, price: 300, total: 900 },
339
+ { date: '2024-01-05', name: '李四', product: '产品B', quantity: 7, price: 200, total: 1400 }
340
+ ]
341
+ }
342
+ }
343
+ }
344
+ </script>
345
+ ```
346
+
347
+ 点击"📊 数据透视分析"按钮后,会弹出透视分析页面,你可以:
348
+
349
+ 1. 拖拽字段到行、列、值区域
350
+ 2. 选择不同的聚合方式
351
+ 3. 应用数据过滤
352
+ 4. 导出分析结果
353
+
354
+ ## 💡 使用技巧
355
+
356
+ 1. **数据准备**:确保 el-table 的数据格式正确,字段名清晰
357
+ 2. **按钮位置**:根据页面布局选择合适的按钮位置
358
+ 3. **自定义选择器**:如果页面有多个 el-table,使用 `tableSelector` 指定要分析的表格
359
+ 4. **性能优化**:对于大数据量表格,建议先进行数据过滤再分析
360
+
361
+ ## 🔍 常见问题
362
+
363
+ ### Q: 为什么提取不到数据?
364
+
365
+ A: 请检查:
366
+ 1. 页面中是否存在 Element UI 的 el-table 组件
367
+ 2. tableSelector 是否正确
368
+ 3. 表格是否有数据
369
+
370
+ ### Q: 支持哪些 Element UI 版本?
371
+
372
+ A: 支持 Element UI 2.x 版本
373
+
374
+ ### Q: 可以分析多个表格吗?
375
+
376
+ A: 目前默认分析第一个匹配的表格。如需分析其他表格,请使用 `tableSelector` 属性指定。
377
+
378
+ ### Q: 如何自定义透视表的样式?
379
+
380
+ A: 可以通过 CSS 覆盖 `.pivot-output` 相关的样式类。
@@ -0,0 +1,2 @@
1
+ "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const t=require("vue");function e(t){const e=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(t)for(const s in t)if("default"!==s){const a=Object.getOwnPropertyDescriptor(t,s);Object.defineProperty(e,s,a.get?a:{enumerable:!0,get:()=>t[s]})}return e.default=t,Object.freeze(e)}const s=e(require("xlsx"));function a(t,e,s,a,i,n,l,o){var r="function"==typeof t?t.options:t;return e&&(r.render=e,r.staticRenderFns=s,r._compiled=!0),n&&(r._scopeId="data-v-"+n),{exports:t,options:r}}const i=a(t.extend({name:"PivotTable",props:{data:{type:Array,default:()=>[]}},data:()=>({showPivotTable:!1,filters:[],showFilters:!1,pivotInitialized:!1,fileInput:null,isLoading:!1,errorMessage:"",isFullscreen:!1,showExportMenu:!1,sidePanelCollapsed:!1,filterSearchQuery:"",expandedFilters:new Set,displayedRows:50}),computed:{hasData(){return this.data.length>0},columns(){return this.hasData?Object.keys(this.data[0]):[]},filteredData(){return 0===this.filters.length?this.data:this.data.filter(t=>this.filters.every(e=>{const s=t[e.field],a=e.value,i=e.values;switch(e.operator){case"equals":return s==a;case"not_equals":return s!=a;case"contains":return String(s).toLowerCase().includes(String(a).toLowerCase());case"not_contains":return!String(s).toLowerCase().includes(String(a).toLowerCase());case"greater_than":return Number(s)>Number(a);case"less_than":return Number(s)<Number(a);case"greater_equal":return Number(s)>=Number(a);case"less_equal":return Number(s)<=Number(a);case"in":return!(i&&i.length>0)||i.includes(s);case"not_in":return!(i&&i.length>0)||!i.includes(s);default:return!0}}))},uniqueFieldValues(){const t={};return this.columns.forEach(e=>{const s=new Set(this.data.map(t=>t[e]));t[e]=Array.from(s).sort()}),t}},watch:{showPivotTable(t){t&&!this.pivotInitialized&&this.$nextTick(()=>{this.initializePivotTable()})},filteredData:{deep:!0,handler(t,e){this.showPivotTable&&this.pivotInitialized&&this.hasDataChanged(t,e)&&this.$nextTick(()=>{this.updatePivotTable()})}},data(){this.resetDisplayedRows()}},methods:{initializePivotTable(){const t=this.$refs.pivotOutput;if(t&&($(t).empty(),this.filteredData&&0!==this.filteredData.length))try{$(t).pivotUI(this.filteredData,{rows:[],cols:[],vals:[],aggregatorName:"Count",rendererName:"Table",renderers:$.extend($.pivotUtilities.renderers,$.pivotUtilities.c3_renderers,$.pivotUtilities.d3_renderers,$.pivotUtilities.export_renderers),hiddenAttributes:[],menuLimit:500,colsLimit:10,rowsLimit:10,unusedAttrsVertical:!0,autoSortUnusedAttrs:!1,onRefresh:t=>{}}),this.pivotInitialized=!0}catch(e){}},updatePivotTable(){const t=this.$refs.pivotOutput;t&&this.pivotInitialized&&$(t).pivotUI(this.filteredData,{rows:[],cols:[],vals:[],aggregatorName:"Count",rendererName:"Table",renderers:$.extend($.pivotUtilities.renderers,$.pivotUtilities.c3_renderers,$.pivotUtilities.d3_renderers,$.pivotUtilities.export_renderers),hiddenAttributes:[],menuLimit:500,colsLimit:10,rowsLimit:10,unusedAttrsVertical:!0,autoSortUnusedAttrs:!1},!0)},addFilter(){const t=this.filters.length;this.filters.push({field:this.columns[0],operator:"equals",value:""}),this.expandedFilters.add(t)},removeFilter(t){this.filters.splice(t,1)},clearFilters(){this.filters=[]},getOperatorLabel:t=>({equals:"等于",not_equals:"不等于",contains:"包含",not_contains:"不包含",greater_than:"大于",less_than:"小于",greater_equal:"大于等于",less_equal:"小于等于",in:"包含于",not_in:"不包含于"}[t]||t),toggleMultiSelectValue(t,e){t.values||this.$set(t,"values",[]);const s=t.values.indexOf(e);s>-1?t.values.splice(s,1):t.values.push(e),t.value="",this.$forceUpdate()},isMultiSelectValueSelected:(t,e)=>t.values&&t.values.includes(e),addCustomValue(t){if(!t.customValue||""===t.customValue.trim())return;t.values||this.$set(t,"values",[]);const e=t.customValue.trim();t.values.includes(e)||t.values.push(e),this.$set(t,"customValue",""),this.$forceUpdate()},handleFileUpload(t){const e=t.target,s=e.files?.[0];if(s){this.isLoading=!0,this.errorMessage="";try{const t=s.name.split(".").pop()?.toLowerCase();if("csv"===t)this.processCSV(s);else{if("xlsx"!==t&&"xls"!==t)throw new Error("不支持的文件格式。请上传 CSV 或 Excel 文件。");this.processExcel(s)}}catch(a){this.errorMessage=a instanceof Error?a.message:"文件处理失败"}finally{this.isLoading=!1}}},processCSV(t){const e=new FileReader;e.onload=t=>{try{const e=t.target?.result,s=this.parseCSV(e);this.$emit("data-loaded",s)}catch(e){this.errorMessage=e instanceof Error?e.message:"CSV 文件处理失败"}},e.onerror=()=>{this.errorMessage="CSV 文件读取失败"},e.readAsText(t)},parseCSV(t){const e=t.split("\n").filter(t=>t.trim());if(0===e.length)return[];const s=this.parseCSVLine(e[0]),a=[];for(let i=1;i<e.length;i++){const t=this.parseCSVLine(e[i]);if(t.length===s.length){const e={};s.forEach((s,a)=>{e[s]=t[a]}),a.push(e)}}return a},parseCSVLine(t){const e=[];let s="",a=!1;for(let i=0;i<t.length;i++){const n=t[i];'"'===n?a=!a:","!==n||a?s+=n:(e.push(s.trim()),s="")}return e.push(s.trim()),e},processExcel(t){const e=new FileReader;e.onload=t=>{try{const e=t.target?.result,a=s.read(e,{type:"binary"}),i=a.SheetNames[0],n=a.Sheets[i],l=s.utils.sheet_to_json(n,{header:1});if(0===l.length)throw new Error("Excel 文件为空");const o=l[0],r=l.slice(1).filter(t=>t.length>0&&t.some(t=>void 0!==t&&""!==t)).map(t=>{const e={};return o.forEach((s,a)=>{let i=t[a];"string"!=typeof i||isNaN(Number(i))||""===i.trim()||(i=Number(i)),e[s]=void 0!==i?i:""}),e});this.$emit("data-loaded",r)}catch(e){this.errorMessage=e instanceof Error?e.message:"Excel 文件处理失败"}},e.onerror=()=>{this.errorMessage="Excel 文件读取失败"},e.readAsBinaryString(t)},triggerFileInput(){const t=this.$refs.fileInput;t&&t.click()},hasDataChanged(t,e){if(!t||!e)return!0;if(t.length!==e.length)return!0;for(let s=0;s<t.length;s++){const a=Object.keys(t[s]),i=Object.keys(e[s]);if(a.length!==i.length)return!0;for(const n of a)if(t[s][n]!==e[s][n])return!0}return!1},toggleFullscreen(){this.isFullscreen=!this.isFullscreen,this.isFullscreen?(document.body.style.overflow="hidden",document.body.classList.add("fullscreen-mode")):(document.body.style.overflow="",document.body.classList.remove("fullscreen-mode"))},exportPivotTableToExcel(){try{const t=this.$refs.pivotOutput;if(!t)return void alert("透视表未初始化,无法导出");const e=t.querySelector("table.pvtTable");if(!e)return void alert("未找到透视表数据");const a=[];e.querySelectorAll("tr").forEach(t=>{const e=[];t.querySelectorAll("th, td").forEach(t=>{const s=parseInt(t.getAttribute("colspan")||"1"),a=t.textContent?.trim()||"";for(let i=0;i<s;i++)e.push(a)}),a.push(e)});const i=s.utils.book_new(),n=s.utils.aoa_to_sheet(a),l=a[0].map(()=>({wch:15}));n["!cols"]=l,s.utils.book_append_sheet(i,n,"透视表");const o=new Date,r=o.toISOString().slice(0,10).replace(/-/g,""),c=`透视表_${r}_${o.toTimeString().slice(0,8).replace(/:/g,"")}.xlsx`;s.writeFile(i,c)}catch(t){alert("导出透视表失败,请重试")}},exportRawDataToExcel(){try{if(!this.filteredData||0===this.filteredData.length)return void alert("没有数据可导出");const t=s.utils.book_new(),e=s.utils.json_to_sheet(this.filteredData),a=this.columns.map(()=>({wch:15}));e["!cols"]=a,s.utils.book_append_sheet(t,e,"数据");const i=new Date,n=i.toISOString().slice(0,10).replace(/-/g,""),l=`数据_${n}_${i.toTimeString().slice(0,8).replace(/:/g,"")}.xlsx`;s.writeFile(t,l)}catch(t){alert("导出数据失败,请重试")}},exportPivotTableToHTML(){try{const t=this.$refs.pivotOutput;if(!t)return void alert("透视表未初始化,无法导出");const e=t.querySelector("table.pvtTable");if(!e)return void alert("未找到透视表数据");const s=`\n<!DOCTYPE html>\n<html lang="zh-CN">\n<head>\n <meta charset="UTF-8">\n <meta name="viewport" content="width=device-width, initial-scale=1.0">\n <title>透视表导出</title>\n <style>\n * {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n }\n \n body {\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;\n padding: 20px;\n background: #f8fafc;\n }\n \n .container {\n max-width: 100%;\n margin: 0 auto;\n background: white;\n padding: 20px;\n border-radius: 8px;\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);\n }\n \n h1 {\n color: #334155;\n margin-bottom: 20px;\n font-size: 24px;\n }\n \n .export-info {\n color: #64748b;\n margin-bottom: 20px;\n font-size: 14px;\n }\n \n table {\n width: 100%;\n border-collapse: collapse;\n margin: 20px 0;\n font-size: 14px;\n }\n \n th, td {\n border: 1px solid #e2e8f0;\n padding: 12px;\n text-align: center;\n }\n \n th {\n background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);\n color: #334155;\n font-weight: 600;\n }\n \n tr:nth-child(even) {\n background: #f8fafc;\n }\n \n tr:hover {\n background: #e2e8f0;\n }\n \n .pvtTotal, .pvtGrandTotal {\n background: linear-gradient(135deg, #dbeafe 0%, #bfdbfe 100%) !important;\n font-weight: 700;\n color: #1e40af;\n }\n \n .pvtGrandTotal {\n background: linear-gradient(135deg, #fef3c7 0%, #fde68a 100%) !important;\n color: #92400e;\n }\n </style>\n</head>\n<body>\n <div class="container">\n <h1>数据透视表</h1>\n <div class="export-info">\n 导出时间: ${(new Date).toLocaleString("zh-CN")}<br>\n 数据行数: ${this.filteredData.length}<br>\n 字段数: ${this.columns.length}\n </div>\n ${e.outerHTML}\n </div>\n</body>\n</html>`,a=new Date,i=a.toISOString().slice(0,10).replace(/-/g,""),n=`透视表_${i}_${a.toTimeString().slice(0,8).replace(/:/g,"")}.html`,l=new Blob([s],{type:"text/html;charset=utf-8"}),o=URL.createObjectURL(l),r=document.createElement("a");r.href=o,r.download=n,document.body.appendChild(r),r.click(),document.body.removeChild(r),URL.revokeObjectURL(o)}catch(t){alert("导出透视表HTML失败,请重试")}},exportRawDataToHTML(){try{if(!this.filteredData||0===this.filteredData.length)return void alert("没有数据可导出");let t="<table>\n";t+="<thead>\n<tr>\n",this.columns.forEach(e=>{t+=`<th>${e}</th>\n`}),t+="</tr>\n</thead>\n",t+="<tbody>\n",this.filteredData.forEach(e=>{t+="<tr>\n",this.columns.forEach(s=>{t+=`<td>${void 0!==e[s]?e[s]:""}</td>\n`}),t+="</tr>\n"}),t+="</tbody>\n</table>";const e=`\n<!DOCTYPE html>\n<html lang="zh-CN">\n<head>\n <meta charset="UTF-8">\n <meta name="viewport" content="width=device-width, initial-scale=1.0">\n <title>数据导出</title>\n <style>\n * {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n }\n \n body {\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;\n padding: 20px;\n background: #f8fafc;\n }\n \n .container {\n max-width: 100%;\n margin: 0 auto;\n background: white;\n padding: 20px;\n border-radius: 8px;\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);\n }\n \n h1 {\n color: #334155;\n margin-bottom: 20px;\n font-size: 24px;\n }\n \n .export-info {\n color: #64748b;\n margin-bottom: 20px;\n font-size: 14px;\n }\n \n table {\n width: 100%;\n border-collapse: collapse;\n margin: 20px 0;\n font-size: 14px;\n }\n \n th, td {\n border: 1px solid #e2e8f0;\n padding: 12px;\n text-align: left;\n }\n \n th {\n background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);\n color: #334155;\n font-weight: 600;\n position: sticky;\n top: 0;\n }\n \n tr:nth-child(even) {\n background: #f8fafc;\n }\n \n tr:hover {\n background: #e2e8f0;\n }\n </style>\n</head>\n<body>\n <div class="container">\n <h1>原始数据</h1>\n <div class="export-info">\n 导出时间: ${(new Date).toLocaleString("zh-CN")}<br>\n 数据行数: ${this.filteredData.length}<br>\n 字段数: ${this.columns.length}\n </div>\n ${t}\n </div>\n</body>\n</html>`,s=new Date,a=s.toISOString().slice(0,10).replace(/-/g,""),i=`数据_${a}_${s.toTimeString().slice(0,8).replace(/:/g,"")}.html`,n=new Blob([e],{type:"text/html;charset=utf-8"}),l=URL.createObjectURL(n),o=document.createElement("a");o.href=l,o.download=i,document.body.appendChild(o),o.click(),document.body.removeChild(o),URL.revokeObjectURL(l)}catch(t){alert("导出数据HTML失败,请重试")}},toggleSidePanel(){this.sidePanelCollapsed=!this.sidePanelCollapsed},toggleFilterCollapse(t){this.expandedFilters.has(t)?this.expandedFilters.delete(t):this.expandedFilters.add(t)},isFilterExpanded(t){return this.expandedFilters.has(t)},getFilteredUniqueValues(t){const e=this.uniqueFieldValues[t]||[];return this.filterSearchQuery?e.filter(t=>String(t).toLowerCase().includes(this.filterSearchQuery.toLowerCase())):e},clearFilterSearch(){this.filterSearchQuery=""},loadMoreData(){this.displayedRows+=50},resetDisplayedRows(){this.displayedRows=50}}}),function(){var t=this,e=t._self._c;return t._self._setupProxy,e("div",{staticClass:"pivot-table"},[t.hasData?e("div",{staticClass:"data-container"},[e("input",{ref:"fileInput",staticStyle:{display:"none"},attrs:{type:"file",accept:".csv,.xlsx,.xls"},on:{change:t.handleFileUpload}}),e("div",{staticClass:"toolbar"},[e("div",{staticClass:"toolbar-left"},[e("div",{staticClass:"view-toggle"},[e("button",{class:["toggle-btn",{active:!t.showPivotTable}],on:{click:function(e){t.showPivotTable=!1}}},[e("svg",{attrs:{width:"16",height:"16",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2"}},[e("path",{attrs:{d:"M3 3h18v18H3zM3 9h18M3 15h18M9 3v18M15 3v18"}})]),t._v(" 原始数据 ")]),e("button",{class:["toggle-btn",{active:t.showPivotTable}],on:{click:function(e){t.showPivotTable=!0}}},[e("svg",{attrs:{width:"16",height:"16",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2"}},[e("rect",{attrs:{x:"3",y:"3",width:"18",height:"18",rx:"2"}}),e("path",{attrs:{d:"M3 9h18M3 15h18M9 3v18"}})]),t._v(" 透视分析 ")])])]),e("div",{staticClass:"toolbar-right"},[e("div",{staticClass:"data-stats"},[e("div",{staticClass:"stat-item"},[e("span",{staticClass:"stat-label"},[t._v("总行数")]),e("span",{staticClass:"stat-value"},[t._v(t._s(t.data.length.toLocaleString()))])]),e("div",{staticClass:"stat-item"},[e("span",{staticClass:"stat-label"},[t._v("字段数")]),e("span",{staticClass:"stat-value"},[t._v(t._s(t.columns.length))])]),t.filters.length>0?e("div",{staticClass:"stat-item highlighted"},[e("span",{staticClass:"stat-label"},[t._v("过滤后")]),e("span",{staticClass:"stat-value"},[t._v(t._s(t.filteredData.length.toLocaleString()))])]):t._e()]),e("div",{staticClass:"toolbar-actions"},[e("div",{staticClass:"export-dropdown"},[e("button",{staticClass:"export-btn",attrs:{title:t.showPivotTable?"导出透视表":"导出原始数据"},on:{click:function(e){t.showExportMenu=!t.showExportMenu}}},[e("svg",{attrs:{width:"16",height:"16",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2"}},[e("path",{attrs:{d:"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"}}),e("polyline",{attrs:{points:"7 10 12 15 17 10"}}),e("line",{attrs:{x1:"12",y1:"15",x2:"12",y2:"3"}})]),t._v(" "+t._s(t.showPivotTable?"导出透视表":"导出数据")+" "),e("svg",{attrs:{width:"12",height:"12",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2"}},[e("polyline",{attrs:{points:"6 9 12 15 18 9"}})])]),t.showExportMenu?e("div",{staticClass:"export-menu"},[e("button",{staticClass:"export-menu-item",on:{click:function(e){t.showPivotTable?t.exportPivotTableToExcel():t.exportRawDataToExcel(),t.showExportMenu=!1}}},[e("svg",{attrs:{width:"14",height:"14",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2"}},[e("path",{attrs:{d:"M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"}}),e("polyline",{attrs:{points:"14 2 14 8 20 8"}}),e("line",{attrs:{x1:"16",y1:"13",x2:"8",y2:"13"}}),e("line",{attrs:{x1:"16",y1:"17",x2:"8",y2:"17"}}),e("polyline",{attrs:{points:"10 9 9 9 8 9"}})]),t._v(" 导出为 Excel (.xlsx) ")]),e("button",{staticClass:"export-menu-item",on:{click:function(e){t.showPivotTable?t.exportPivotTableToHTML():t.exportRawDataToHTML(),t.showExportMenu=!1}}},[e("svg",{attrs:{width:"14",height:"14",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2"}},[e("path",{attrs:{d:"M12 2L2 7l10 5 10-5-10-5z"}}),e("path",{attrs:{d:"M2 17l10 5 10-5"}}),e("path",{attrs:{d:"M2 12l10 5 10-5"}})]),t._v(" 导出为 HTML (.html) ")])]):t._e()]),e("button",{staticClass:"fullscreen-btn",attrs:{title:t.isFullscreen?"退出全屏":"全屏显示"},on:{click:t.toggleFullscreen}},[t.isFullscreen?e("svg",{attrs:{width:"16",height:"16",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2"}},[e("path",{attrs:{d:"M8 3v3a2 2 0 0 1-2 2H3m18 0h-3a2 2 0 0 1-2-2V3m0 18v-3a2 2 0 0 1 2-2h3M3 16h3a2 2 0 0 1 2 2v3"}})]):e("svg",{attrs:{width:"16",height:"16",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2"}},[e("path",{attrs:{d:"M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3"}})]),t._v(" "+t._s(t.isFullscreen?"退出全屏":"全屏")+" ")])])])]),e("div",{staticClass:"main-content"},[t.showPivotTable?e("div",{staticClass:"side-panel",class:{collapsed:t.sidePanelCollapsed}},[e("button",{staticClass:"panel-collapse-btn",attrs:{title:t.sidePanelCollapsed?"展开过滤面板":"折叠过滤面板"},on:{click:t.toggleSidePanel}},[e("svg",{class:{rotated:t.sidePanelCollapsed},attrs:{width:"16",height:"16",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2"}},[e("polyline",{attrs:{points:"9 18 15 12 9 6"}})])]),e("div",{directives:[{name:"show",rawName:"v-show",value:!t.sidePanelCollapsed,expression:"!sidePanelCollapsed"}],staticClass:"panel-section"},[e("div",{staticClass:"panel-header"},[e("h3",{staticClass:"panel-title"},[t._v("数据过滤")]),e("button",{staticClass:"icon-btn",attrs:{title:"添加过滤"},on:{click:t.addFilter}},[e("svg",{attrs:{width:"16",height:"16",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2"}},[e("line",{attrs:{x1:"12",y1:"5",x2:"12",y2:"19"}}),e("line",{attrs:{x1:"5",y1:"12",x2:"19",y2:"12"}})])])]),0===t.filters.length?e("div",{staticClass:"empty-filters"},[e("p",[t._v("暂无过滤条件")]),e("button",{staticClass:"text-btn",on:{click:t.addFilter}},[t._v("添加过滤")])]):e("div",{staticClass:"filters-list"},[t._l(t.filters,function(s,a){return e("div",{key:a,staticClass:"filter-card"},[e("div",{staticClass:"filter-header"},[e("button",{staticClass:"collapse-btn",attrs:{title:t.isFilterExpanded(a)?"收起":"展开"},on:{click:function(e){return t.toggleFilterCollapse(a)}}},[e("svg",{class:{rotated:t.isFilterExpanded(a)},attrs:{width:"14",height:"14",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2"}},[e("polyline",{attrs:{points:"6 9 12 15 18 9"}})])]),e("select",{directives:[{name:"model",rawName:"v-model",value:s.field,expression:"filter.field"}],staticClass:"filter-field-select",on:{change:function(e){var a=Array.prototype.filter.call(e.target.options,function(t){return t.selected}).map(function(t){return"_value"in t?t._value:t.value});t.$set(s,"field",e.target.multiple?a:a[0])}}},t._l(t.columns,function(s){return e("option",{key:s,domProps:{value:s}},[t._v(t._s(s))])}),0),e("button",{staticClass:"icon-btn small",attrs:{title:"删除"},on:{click:function(e){return t.removeFilter(a)}}},[e("svg",{attrs:{width:"14",height:"14",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2"}},[e("line",{attrs:{x1:"18",y1:"6",x2:"6",y2:"18"}}),e("line",{attrs:{x1:"6",y1:"6",x2:"18",y2:"18"}})])])]),e("div",{directives:[{name:"show",rawName:"v-show",value:t.isFilterExpanded(a),expression:"isFilterExpanded(index)"}],staticClass:"filter-body"},[e("select",{directives:[{name:"model",rawName:"v-model",value:s.operator,expression:"filter.operator"}],staticClass:"filter-operator-select",on:{change:function(e){var a=Array.prototype.filter.call(e.target.options,function(t){return t.selected}).map(function(t){return"_value"in t?t._value:t.value});t.$set(s,"operator",e.target.multiple?a:a[0])}}},[e("option",{attrs:{value:"equals"}},[t._v("等于")]),e("option",{attrs:{value:"not_equals"}},[t._v("不等于")]),e("option",{attrs:{value:"contains"}},[t._v("包含")]),e("option",{attrs:{value:"not_contains"}},[t._v("不包含")]),e("option",{attrs:{value:"greater_than"}},[t._v("大于")]),e("option",{attrs:{value:"less_than"}},[t._v("小于")]),e("option",{attrs:{value:"greater_equal"}},[t._v("大于等于")]),e("option",{attrs:{value:"less_equal"}},[t._v("小于等于")]),e("option",{attrs:{value:"in"}},[t._v("包含于(多选)")]),e("option",{attrs:{value:"not_in"}},[t._v("不包含于(多选)")])]),"in"!==s.operator&&"not_in"!==s.operator?e("div",{staticClass:"filter-value-wrapper"},[e("select",{directives:[{name:"model",rawName:"v-model",value:s.value,expression:"filter.value"}],staticClass:"filter-value-select",on:{change:function(e){var a=Array.prototype.filter.call(e.target.options,function(t){return t.selected}).map(function(t){return"_value"in t?t._value:t.value});t.$set(s,"value",e.target.multiple?a:a[0])}}},[e("option",{attrs:{value:""}},[t._v("选择或输入值")]),t._l(t.uniqueFieldValues[s.field],function(s){return e("option",{key:s,domProps:{value:s}},[t._v(t._s(s))])})],2),e("input",{directives:[{name:"model",rawName:"v-model",value:s.value,expression:"filter.value"}],staticClass:"filter-value-input",attrs:{type:"text",placeholder:"或手动输入"},domProps:{value:s.value},on:{input:function(e){e.target.composing||t.$set(s,"value",e.target.value)}}})]):e("div",{staticClass:"filter-multi-select"},[e("div",{staticClass:"multi-select-header"},[e("span",{staticClass:"selected-count"},[t._v("已选 "+t._s(s.values?s.values.length:0)+" 项")]),s.values&&s.values.length>0?e("button",{staticClass:"clear-selection-btn",on:{click:function(t){s.values=[]}}},[t._v(" 清空 ")]):t._e()]),e("div",{staticClass:"multi-select-search"},[e("input",{directives:[{name:"model",rawName:"v-model",value:t.filterSearchQuery,expression:"filterSearchQuery"}],staticClass:"search-input",attrs:{type:"text",placeholder:"搜索值..."},domProps:{value:t.filterSearchQuery},on:{input:function(e){e.target.composing||(t.filterSearchQuery=e.target.value)}}}),t.filterSearchQuery?e("button",{staticClass:"clear-search-btn",attrs:{title:"清除搜索"},on:{click:t.clearFilterSearch}},[e("svg",{attrs:{width:"14",height:"14",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2"}},[e("line",{attrs:{x1:"18",y1:"6",x2:"6",y2:"18"}}),e("line",{attrs:{x1:"6",y1:"6",x2:"18",y2:"18"}})])]):t._e()]),e("div",{staticClass:"multi-select-list"},[t._l(t.getFilteredUniqueValues(s.field),function(a){return e("label",{key:a,class:["multi-select-item",{selected:t.isMultiSelectValueSelected(s,a)}]},[e("input",{attrs:{type:"checkbox"},domProps:{checked:t.isMultiSelectValueSelected(s,a)},on:{change:function(e){return t.toggleMultiSelectValue(s,a)}}}),e("span",[t._v(t._s(a))])])}),0===t.getFilteredUniqueValues(s.field).length?e("div",{staticClass:"no-results"},[t._v(" 没有找到匹配的值 ")]):t._e()],2),e("div",{staticClass:"multi-select-add"},[e("input",{directives:[{name:"model",rawName:"v-model",value:s.customValue,expression:"filter.customValue"}],staticClass:"filter-custom-input",attrs:{type:"text",placeholder:"输入自定义值"},domProps:{value:s.customValue},on:{keyup:function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"enter",13,e.key,"Enter")?null:t.addCustomValue(s)},input:function(e){e.target.composing||t.$set(s,"customValue",e.target.value)}}}),e("button",{staticClass:"add-value-btn",on:{click:function(e){return t.addCustomValue(s)}}},[t._v(" 添加 ")])])])])])}),t.filters.length>0?e("button",{staticClass:"clear-filters-btn",on:{click:t.clearFilters}},[t._v(" 清除所有过滤 ")]):t._e()],2)])]):t._e(),e("div",{staticClass:"content-area"},[e("div",{directives:[{name:"show",rawName:"v-show",value:!t.showPivotTable,expression:"!showPivotTable"}],staticClass:"data-view"},[e("div",{staticClass:"data-table-wrapper"},[e("table",{staticClass:"modern-table"},[e("thead",[e("tr",t._l(t.columns,function(s){return e("th",{key:s,staticClass:"table-header"},[e("div",{staticClass:"header-content"},[e("span",[t._v(t._s(s))])])])}),0)]),e("tbody",t._l(t.data.slice(0,t.displayedRows),function(s,a){return e("tr",{key:a,staticClass:"table-row"},t._l(t.columns,function(a){return e("td",{key:a,staticClass:"table-cell"},[t._v(" "+t._s(s[a])+" ")])}),0)}),0)])]),t.data.length>t.displayedRows?e("div",{staticClass:"table-footer"},[e("div",{staticClass:"table-footer-content"},[e("span",{staticClass:"table-info"},[t._v("显示 "+t._s(t.displayedRows)+" 行,共 "+t._s(t.data.length.toLocaleString())+" 行数据")]),e("button",{staticClass:"load-more-btn",on:{click:t.loadMoreData}},[e("svg",{attrs:{width:"16",height:"16",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2"}},[e("polyline",{attrs:{points:"6 9 12 15 18 9"}})]),t._v(" 加载更多 ")])])]):t.data.length>0?e("div",{staticClass:"table-footer"},[e("span",{staticClass:"table-info"},[t._v("已显示全部 "+t._s(t.data.length.toLocaleString())+" 行数据")])]):t._e()]),e("div",{directives:[{name:"show",rawName:"v-show",value:t.showPivotTable,expression:"showPivotTable"}],staticClass:"pivot-view"},[e("div",{staticClass:"pivot-wrapper"},[e("div",{ref:"pivotOutput",staticClass:"pivot-output"})])])])])]):e("div",{staticClass:"empty-state"},[e("div",{staticClass:"empty-state-content"},[e("div",{staticClass:"empty-icon"},[e("svg",{attrs:{width:"120",height:"120",viewBox:"0 0 24 24",fill:"none",xmlns:"http://www.w3.org/2000/svg"}},[e("path",{attrs:{d:"M3 3V21H21",stroke:"#E2E8F0","stroke-width":"2","stroke-linecap":"round","stroke-linejoin":"round"}}),e("path",{attrs:{d:"M19 9L14 14L10 10L7 13",stroke:"#CBD5E1","stroke-width":"2","stroke-linecap":"round","stroke-linejoin":"round"}}),e("path",{attrs:{d:"M19 9V13H15",stroke:"#CBD5E1","stroke-width":"2","stroke-linecap":"round","stroke-linejoin":"round"}})])]),e("h2",{staticClass:"empty-title"},[t._v("暂无数据")]),e("p",{staticClass:"empty-description"},[t._v("请先导入数据文件或加载示例数据开始分析")])])])])},[],0,0,"33ade542").exports;const n=a(t.extend({name:"OnlineAnalysisButton",components:{PivotTable:i},data:()=>({showModal:!1,tableData:[],buttonPosition:"bottom-right"}),props:{position:{type:String,default:"bottom-right"},buttonText:{type:String,default:"📊 透视分析"},buttonClass:{type:String,default:""},tableSelector:{type:String,default:".el-table"},autoExtract:{type:Boolean,default:!0}},mounted(){this.buttonPosition=this.position},methods:{openAnalysis(){this.autoExtract&&this.extractTableData(),this.showModal=!0},closeAnalysis(){this.showModal=!1},extractTableData(){try{const t=document.querySelectorAll(this.tableSelector);if(0===t.length)return void(this.tableData=[]);const e=t[0],s=this.parseElTable(e);this.tableData=s}catch(t){this.tableData=[]}},parseElTable(t){const e=[];try{const s=[],a=t.querySelectorAll(".el-table__header-wrapper thead tr th .cell");if(0===a.length)return[];a.forEach(t=>{const e=t.textContent?.trim()||"";e&&s.push(e)});t.querySelectorAll(".el-table__body-wrapper tbody tr").forEach(t=>{const a=t.querySelectorAll("td .cell");if(0===a.length)return;const i={};a.forEach((t,e)=>{if(e<s.length){const a=s[e];let n=t.textContent?.trim()||"";""===n||isNaN(Number(n))||(n=Number(n)),i[a]=n}}),Object.keys(i).length>0&&e.push(i)})}catch(s){}return e},setData(t){this.tableData=t},getData(){return this.tableData}}}),function(){var t=this,e=t._self._c;return t._self._setupProxy,e("div",{staticClass:"online-analysis-button"},[e("button",{class:["analysis-button",t.buttonPosition,t.buttonClass],attrs:{title:"点击进行透视分析"},on:{click:t.openAnalysis}},[t._v(" "+t._s(t.buttonText)+" ")]),e("transition",{attrs:{name:"modal"}},[t.showModal?e("div",{staticClass:"modal-overlay",on:{click:function(e){return e.target!==e.currentTarget?null:t.closeAnalysis.apply(null,arguments)}}},[e("div",{staticClass:"modal-content"},[e("div",{staticClass:"modal-header"},[e("h2",{staticClass:"modal-title"},[t._v("📊 数据透视分析")]),e("button",{staticClass:"close-button",attrs:{title:"关闭"},on:{click:t.closeAnalysis}},[e("svg",{attrs:{width:"24",height:"24",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2"}},[e("line",{attrs:{x1:"18",y1:"6",x2:"6",y2:"18"}}),e("line",{attrs:{x1:"6",y1:"6",x2:"18",y2:"18"}})])])]),e("div",{staticClass:"modal-body"},[e("PivotTable",{attrs:{data:t.tableData}})],1)])]):t._e()])],1)},[],0,0,"7e7b1fe0").exports,l=function(t){l.installed||(l.installed=!0,t.component("OnlineAnalysisButton",n),t.component("PivotTable",i))};"undefined"!=typeof window&&window.Vue&&l(window.Vue);const o={install:l,OnlineAnalysisButton:n,PivotTable:i};exports.OnlineAnalysisButton=n,exports.PivotTable=i,exports.default=o;
2
+ //# sourceMappingURL=online-analysis-button.cjs.js.map