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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"online-analysis-button.es.js","sources":["../src/components/PivotTable.vue","../src/components/OnlineAnalysisButton.vue","../src/index.ts"],"sourcesContent":["<script lang=\"ts\">\r\nimport Vue from 'vue'\r\nimport * as XLSX from 'xlsx'\r\n \r\n// jQuery, jQuery UI 和 PivotTable.js 已通过 CDN 在 index.html 中全局引入\r\n// 无需重复导入\r\ndeclare const $: JQueryStatic\r\n\r\ninterface PivotData {\r\n [key: string]: any\r\n}\r\n\r\nexport default Vue.extend({\r\n name: 'PivotTable',\r\n props: {\r\n data: {\r\n type: Array as () => PivotData[],\r\n default: () => []\r\n }\r\n },\r\n data() {\r\n return {\r\n showPivotTable: false,\r\n filters: [] as Array<{ field: string; operator: string; value: any; values?: any[]; customValue?: string; collapsed?: boolean }>,\r\n showFilters: false,\r\n pivotInitialized: false,\r\n fileInput: null as HTMLInputElement | null,\r\n isLoading: false,\r\n errorMessage: '',\r\n isFullscreen: false,\r\n showExportMenu: false,\r\n sidePanelCollapsed: false,\r\n filterSearchQuery: '',\r\n expandedFilters: new Set<number>(),\r\n displayedRows: 50\r\n }\r\n },\r\n computed: {\r\n hasData(): boolean {\r\n return this.data.length > 0\r\n },\r\n columns(): string[] {\r\n return this.hasData ? Object.keys(this.data[0]) : []\r\n },\r\n filteredData(): PivotData[] {\r\n if (this.filters.length === 0) {\r\n return this.data\r\n }\r\n \r\n return this.data.filter(item => {\r\n return this.filters.every(filter => {\r\n const itemValue = item[filter.field]\r\n const filterValue = filter.value\r\n const filterValues = filter.values\r\n \r\n switch (filter.operator) {\r\n case 'equals':\r\n return itemValue == filterValue\r\n case 'not_equals':\r\n return itemValue != filterValue\r\n case 'contains':\r\n return String(itemValue).toLowerCase().includes(String(filterValue).toLowerCase())\r\n case 'not_contains':\r\n return !String(itemValue).toLowerCase().includes(String(filterValue).toLowerCase())\r\n case 'greater_than':\r\n return Number(itemValue) > Number(filterValue)\r\n case 'less_than':\r\n return Number(itemValue) < Number(filterValue)\r\n case 'greater_equal':\r\n return Number(itemValue) >= Number(filterValue)\r\n case 'less_equal':\r\n return Number(itemValue) <= Number(filterValue)\r\n case 'in':\r\n return filterValues && filterValues.length > 0 ? filterValues.includes(itemValue) : true\r\n case 'not_in':\r\n return filterValues && filterValues.length > 0 ? !filterValues.includes(itemValue) : true\r\n default:\r\n return true\r\n }\r\n })\r\n })\r\n },\r\n uniqueFieldValues(): { [key: string]: any[] } {\r\n const result: { [key: string]: any[] } = {}\r\n this.columns.forEach(col => {\r\n const values = new Set(this.data.map(item => item[col]))\r\n result[col] = Array.from(values).sort()\r\n })\r\n return result\r\n }\r\n },\r\n watch: {\r\n showPivotTable(newVal) {\r\n if (newVal && !this.pivotInitialized) {\r\n this.$nextTick(() => {\r\n this.initializePivotTable()\r\n })\r\n }\r\n },\r\n filteredData: {\r\n deep: true,\r\n handler(newData, oldData) {\r\n // 只在数据真正变化时才更新透视表\r\n if (this.showPivotTable && this.pivotInitialized && this.hasDataChanged(newData, oldData)) {\r\n this.$nextTick(() => {\r\n this.updatePivotTable()\r\n })\r\n }\r\n }\r\n },\r\n data() {\r\n // 当数据变化时重置显示行数\r\n this.resetDisplayedRows()\r\n }\r\n },\r\n methods: {\r\n initializePivotTable() {\r\n const pivotElement = this.$refs.pivotOutput as HTMLElement\r\n if (!pivotElement) return\r\n\r\n // Clear existing content\r\n $(pivotElement).empty()\r\n\r\n // Check if data is valid\r\n if (!this.filteredData || this.filteredData.length === 0) {\r\n console.warn('No data available for pivot table')\r\n return\r\n }\r\n\r\n // Initialize PivotTable.js with full UI\r\n try {\r\n $(pivotElement).pivotUI(this.filteredData, {\r\n rows: [],\r\n cols: [],\r\n vals: [],\r\n aggregatorName: 'Count',\r\n rendererName: 'Table',\r\n renderers: $.extend(\r\n $.pivotUtilities.renderers,\r\n $.pivotUtilities.c3_renderers,\r\n $.pivotUtilities.d3_renderers,\r\n $.pivotUtilities.export_renderers\r\n ),\r\n hiddenAttributes: [],\r\n menuLimit: 500,\r\n colsLimit: 10,\r\n rowsLimit: 10,\r\n unusedAttrsVertical: true,\r\n autoSortUnusedAttrs: false,\r\n onRefresh: (config: any) => {\r\n console.log('Pivot table refreshed:', config)\r\n }\r\n })\r\n\r\n this.pivotInitialized = true\r\n } catch (error) {\r\n console.error('Error initializing pivot table:', error)\r\n }\r\n },\r\n\r\n updatePivotTable() {\r\n const pivotElement = this.$refs.pivotOutput as HTMLElement\r\n if (!pivotElement || !this.pivotInitialized) return\r\n\r\n // Update the pivot table with new data\r\n $(pivotElement).pivotUI(this.filteredData, {\r\n rows: [],\r\n cols: [],\r\n vals: [],\r\n aggregatorName: 'Count',\r\n rendererName: 'Table',\r\n renderers: $.extend(\r\n $.pivotUtilities.renderers,\r\n $.pivotUtilities.c3_renderers,\r\n $.pivotUtilities.d3_renderers,\r\n $.pivotUtilities.export_renderers\r\n ),\r\n hiddenAttributes: [],\r\n menuLimit: 500,\r\n colsLimit: 10,\r\n rowsLimit: 10,\r\n unusedAttrsVertical: true,\r\n autoSortUnusedAttrs: false\r\n }, true)\r\n },\r\n\r\n addFilter() {\r\n const newIndex = this.filters.length\r\n this.filters.push({\r\n field: this.columns[0],\r\n operator: 'equals',\r\n value: ''\r\n })\r\n // 新添加的过滤器默认展开\r\n this.expandedFilters.add(newIndex)\r\n },\r\n removeFilter(index: number) {\r\n this.filters.splice(index, 1)\r\n },\r\n clearFilters() {\r\n this.filters = []\r\n },\r\n getOperatorLabel(operator: string): string {\r\n const labels: { [key: string]: string } = {\r\n 'equals': '等于',\r\n 'not_equals': '不等于',\r\n 'contains': '包含',\r\n 'not_contains': '不包含',\r\n 'greater_than': '大于',\r\n 'less_than': '小于',\r\n 'greater_equal': '大于等于',\r\n 'less_equal': '小于等于',\r\n 'in': '包含于',\r\n 'not_in': '不包含于'\r\n }\r\n return labels[operator] || operator\r\n },\r\n \r\n // 切换多选值\r\n toggleMultiSelectValue(filter: any, value: any) {\r\n if (!filter.values) {\r\n this.$set(filter, 'values', [])\r\n }\r\n const index = filter.values.indexOf(value)\r\n if (index > -1) {\r\n filter.values.splice(index, 1)\r\n } else {\r\n filter.values.push(value)\r\n }\r\n // 清空单选值\r\n filter.value = ''\r\n \r\n // 强制更新过滤后的数据\r\n this.$forceUpdate()\r\n },\r\n \r\n // 检查值是否被选中\r\n isMultiSelectValueSelected(filter: any, value: any): boolean {\r\n return filter.values && filter.values.includes(value)\r\n },\r\n \r\n // 添加自定义值\r\n addCustomValue(filter: any) {\r\n if (!filter.customValue || filter.customValue.trim() === '') {\r\n return\r\n }\r\n \r\n if (!filter.values) {\r\n this.$set(filter, 'values', [])\r\n }\r\n \r\n const value = filter.customValue.trim()\r\n if (!filter.values.includes(value)) {\r\n filter.values.push(value)\r\n }\r\n \r\n this.$set(filter, 'customValue', '')\r\n \r\n // 强制更新过滤后的数据\r\n this.$forceUpdate()\r\n },\r\n \r\n // 文件上传相关方法\r\n handleFileUpload(event: Event) {\r\n const target = event.target as HTMLInputElement\r\n const file = target.files?.[0]\r\n \r\n if (!file) return\r\n\r\n this.isLoading = true\r\n this.errorMessage = ''\r\n\r\n try {\r\n const fileExtension = file.name.split('.').pop()?.toLowerCase()\r\n \r\n if (fileExtension === 'csv') {\r\n this.processCSV(file)\r\n } else if (fileExtension === 'xlsx' || fileExtension === 'xls') {\r\n this.processExcel(file)\r\n } else {\r\n throw new Error('不支持的文件格式。请上传 CSV 或 Excel 文件。')\r\n }\r\n } catch (error) {\r\n this.errorMessage = error instanceof Error ? error.message : '文件处理失败'\r\n console.error('文件处理错误:', error)\r\n } finally {\r\n this.isLoading = false\r\n }\r\n },\r\n\r\n processCSV(file: File) {\r\n const reader = new FileReader()\r\n \r\n reader.onload = (e) => {\r\n try {\r\n const text = e.target?.result as string\r\n const data = this.parseCSV(text)\r\n this.$emit('data-loaded', data)\r\n } catch (error) {\r\n this.errorMessage = error instanceof Error ? error.message : 'CSV 文件处理失败'\r\n }\r\n }\r\n \r\n reader.onerror = () => {\r\n this.errorMessage = 'CSV 文件读取失败'\r\n }\r\n reader.readAsText(file)\r\n },\r\n\r\n parseCSV(text: string): any[] {\r\n const lines = text.split('\\n').filter(line => line.trim())\r\n if (lines.length === 0) return []\r\n\r\n const headers = this.parseCSVLine(lines[0])\r\n const data: any[] = []\r\n\r\n for (let i = 1; i < lines.length; i++) {\r\n const values = this.parseCSVLine(lines[i])\r\n if (values.length === headers.length) {\r\n const row: any = {}\r\n headers.forEach((header, index) => {\r\n row[header] = values[index]\r\n })\r\n data.push(row)\r\n }\r\n }\r\n\r\n return data\r\n },\r\n\r\n parseCSVLine(line: string): string[] {\r\n const result: string[] = []\r\n let current = ''\r\n let inQuotes = false\r\n\r\n for (let i = 0; i < line.length; i++) {\r\n const char = line[i]\r\n \r\n if (char === '\"') {\r\n inQuotes = !inQuotes\r\n } else if (char === ',' && !inQuotes) {\r\n result.push(current.trim())\r\n current = ''\r\n } else {\r\n current += char\r\n }\r\n }\r\n \r\n result.push(current.trim())\r\n return result\r\n },\r\n\r\n processExcel(file: File) {\r\n const reader = new FileReader()\r\n \r\n reader.onload = (e) => {\r\n try {\r\n const data = e.target?.result\r\n const workbook = XLSX.read(data, { type: 'binary' })\r\n \r\n // 获取第一个工作表\r\n const firstSheetName = workbook.SheetNames[0]\r\n const worksheet = workbook.Sheets[firstSheetName]\r\n \r\n // 转换为 JSON\r\n const jsonData = XLSX.utils.sheet_to_json(worksheet, { header: 1 })\r\n \r\n console.log('Excel原始数据:', jsonData)\r\n \r\n if (jsonData.length === 0) {\r\n throw new Error('Excel 文件为空')\r\n }\r\n\r\n // 转换为对象数组\r\n const headers = jsonData[0] as string[]\r\n console.log('表头:', headers)\r\n \r\n const dataRows = jsonData.slice(1) as any[][]\r\n console.log('数据行数:', dataRows.length)\r\n \r\n const result: any[] = dataRows\r\n .filter(row => row.length > 0 && row.some(cell => cell !== undefined && cell !== ''))\r\n .map(row => {\r\n const obj: any = {}\r\n headers.forEach((header, index) => {\r\n let value = row[index]\r\n // 尝试转换为数字\r\n if (typeof value === 'string' && !isNaN(Number(value)) && value.trim() !== '') {\r\n value = Number(value)\r\n }\r\n obj[header] = value !== undefined ? value : ''\r\n })\r\n return obj\r\n })\r\n\r\n console.log('解析后的数据:', result)\r\n this.$emit('data-loaded', result)\r\n } catch (error) {\r\n console.error('Excel处理错误:', error)\r\n this.errorMessage = error instanceof Error ? error.message : 'Excel 文件处理失败'\r\n }\r\n }\r\n \r\n reader.onerror = () => {\r\n this.errorMessage = 'Excel 文件读取失败'\r\n }\r\n reader.readAsBinaryString(file)\r\n },\r\n\r\n triggerFileInput() {\r\n const input = this.$refs.fileInput as HTMLInputElement\r\n if (input) {\r\n input.click()\r\n }\r\n },\r\n \r\n // 检查数据是否真正发生了变化\r\n hasDataChanged(newData: PivotData[] | undefined, oldData: PivotData[] | undefined): boolean {\r\n if (!newData || !oldData) return true\r\n if (newData.length !== oldData.length) return true\r\n \r\n // 检查数据内容是否变化\r\n for (let i = 0; i < newData.length; i++) {\r\n const newKeys = Object.keys(newData[i])\r\n const oldKeys = Object.keys(oldData[i])\r\n \r\n if (newKeys.length !== oldKeys.length) return true\r\n \r\n for (const key of newKeys) {\r\n if (newData[i][key] !== oldData[i][key]) return true\r\n }\r\n }\r\n \r\n return false\r\n },\r\n \r\n // 全屏功能\r\n toggleFullscreen() {\r\n this.isFullscreen = !this.isFullscreen\r\n \r\n if (this.isFullscreen) {\r\n // 进入全屏模式\r\n document.body.style.overflow = 'hidden'\r\n document.body.classList.add('fullscreen-mode')\r\n } else {\r\n // 退出全屏模式\r\n document.body.style.overflow = ''\r\n document.body.classList.remove('fullscreen-mode')\r\n }\r\n },\r\n \r\n // 导出透视表数据到Excel\r\n exportPivotTableToExcel() {\r\n try {\r\n const pivotElement = this.$refs.pivotOutput as HTMLElement\r\n if (!pivotElement) {\r\n alert('透视表未初始化,无法导出')\r\n return\r\n }\r\n \r\n // 获取透视表数据\r\n const table = pivotElement.querySelector('table.pvtTable')\r\n if (!table) {\r\n alert('未找到透视表数据')\r\n return\r\n }\r\n \r\n // 解析表格数据\r\n const data: any[][] = []\r\n const rows = table.querySelectorAll('tr')\r\n \r\n rows.forEach(row => {\r\n const rowData: any[] = []\r\n const cells = row.querySelectorAll('th, td')\r\n \r\n cells.forEach(cell => {\r\n // 处理合并单元格\r\n const colSpan = parseInt(cell.getAttribute('colspan') || '1')\r\n const text = cell.textContent?.trim() || ''\r\n \r\n // 如果是合并单元格,填充空值\r\n for (let i = 0; i < colSpan; i++) {\r\n rowData.push(text)\r\n }\r\n })\r\n \r\n data.push(rowData)\r\n })\r\n \r\n // 创建工作簿\r\n const workbook = XLSX.utils.book_new()\r\n const worksheet = XLSX.utils.aoa_to_sheet(data)\r\n \r\n // 设置列宽\r\n const colWidths = data[0].map(() => ({ wch: 15 }))\r\n worksheet['!cols'] = colWidths\r\n \r\n // 添加工作表到工作簿\r\n XLSX.utils.book_append_sheet(workbook, worksheet, '透视表')\r\n \r\n // 生成文件名\r\n const now = new Date()\r\n const timestamp = now.toISOString().slice(0, 10).replace(/-/g, '')\r\n const timeStr = now.toTimeString().slice(0, 8).replace(/:/g, '')\r\n const filename = `透视表_${timestamp}_${timeStr}.xlsx`\r\n \r\n // 下载文件\r\n XLSX.writeFile(workbook, filename)\r\n \r\n console.log('透视表导出成功:', filename)\r\n } catch (error) {\r\n console.error('导出透视表失败:', error)\r\n alert('导出透视表失败,请重试')\r\n }\r\n },\r\n \r\n // 导出原始数据到Excel\r\n exportRawDataToExcel() {\r\n try {\r\n if (!this.filteredData || this.filteredData.length === 0) {\r\n alert('没有数据可导出')\r\n return\r\n }\r\n \r\n // 创建工作簿\r\n const workbook = XLSX.utils.book_new()\r\n \r\n // 将数据转换为工作表\r\n const worksheet = XLSX.utils.json_to_sheet(this.filteredData)\r\n \r\n // 设置列宽\r\n const colWidths = this.columns.map(() => ({ wch: 15 }))\r\n worksheet['!cols'] = colWidths\r\n \r\n // 添加工作表到工作簿\r\n XLSX.utils.book_append_sheet(workbook, worksheet, '数据')\r\n \r\n // 生成文件名\r\n const now = new Date()\r\n const timestamp = now.toISOString().slice(0, 10).replace(/-/g, '')\r\n const timeStr = now.toTimeString().slice(0, 8).replace(/:/g, '')\r\n const filename = `数据_${timestamp}_${timeStr}.xlsx`\r\n \r\n // 下载文件\r\n XLSX.writeFile(workbook, filename)\r\n \r\n console.log('数据导出成功:', filename)\r\n } catch (error) {\r\n console.error('导出数据失败:', error)\r\n alert('导出数据失败,请重试')\r\n }\r\n },\r\n \r\n // 导出透视表到HTML\r\n exportPivotTableToHTML() {\r\n try {\r\n const pivotElement = this.$refs.pivotOutput as HTMLElement\r\n if (!pivotElement) {\r\n alert('透视表未初始化,无法导出')\r\n return\r\n }\r\n \r\n // 获取透视表HTML\r\n const table = pivotElement.querySelector('table.pvtTable')\r\n if (!table) {\r\n alert('未找到透视表数据')\r\n return\r\n }\r\n \r\n // 创建完整的HTML文档\r\n const htmlContent = `\r\n<!DOCTYPE html>\r\n<html lang=\"zh-CN\">\r\n<head>\r\n <meta charset=\"UTF-8\">\r\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\r\n <title>透视表导出</title>\r\n <style>\r\n * {\r\n margin: 0;\r\n padding: 0;\r\n box-sizing: border-box;\r\n }\r\n \r\n body {\r\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;\r\n padding: 20px;\r\n background: #f8fafc;\r\n }\r\n \r\n .container {\r\n max-width: 100%;\r\n margin: 0 auto;\r\n background: white;\r\n padding: 20px;\r\n border-radius: 8px;\r\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);\r\n }\r\n \r\n h1 {\r\n color: #334155;\r\n margin-bottom: 20px;\r\n font-size: 24px;\r\n }\r\n \r\n .export-info {\r\n color: #64748b;\r\n margin-bottom: 20px;\r\n font-size: 14px;\r\n }\r\n \r\n table {\r\n width: 100%;\r\n border-collapse: collapse;\r\n margin: 20px 0;\r\n font-size: 14px;\r\n }\r\n \r\n th, td {\r\n border: 1px solid #e2e8f0;\r\n padding: 12px;\r\n text-align: center;\r\n }\r\n \r\n th {\r\n background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);\r\n color: #334155;\r\n font-weight: 600;\r\n }\r\n \r\n tr:nth-child(even) {\r\n background: #f8fafc;\r\n }\r\n \r\n tr:hover {\r\n background: #e2e8f0;\r\n }\r\n \r\n .pvtTotal, .pvtGrandTotal {\r\n background: linear-gradient(135deg, #dbeafe 0%, #bfdbfe 100%) !important;\r\n font-weight: 700;\r\n color: #1e40af;\r\n }\r\n \r\n .pvtGrandTotal {\r\n background: linear-gradient(135deg, #fef3c7 0%, #fde68a 100%) !important;\r\n color: #92400e;\r\n }\r\n </style>\r\n</head>\r\n<body>\r\n <div class=\"container\">\r\n <h1>数据透视表</h1>\r\n <div class=\"export-info\">\r\n 导出时间: ${new Date().toLocaleString('zh-CN')}<br>\r\n 数据行数: ${this.filteredData.length}<br>\r\n 字段数: ${this.columns.length}\r\n </div>\r\n ${table.outerHTML}\r\n </div>\r\n</body>\r\n</html>`\r\n \r\n // 生成文件名\r\n const now = new Date()\r\n const timestamp = now.toISOString().slice(0, 10).replace(/-/g, '')\r\n const timeStr = now.toTimeString().slice(0, 8).replace(/:/g, '')\r\n const filename = `透视表_${timestamp}_${timeStr}.html`\r\n \r\n // 创建下载链接\r\n const blob = new Blob([htmlContent], { type: 'text/html;charset=utf-8' })\r\n const url = URL.createObjectURL(blob)\r\n const link = document.createElement('a')\r\n link.href = url\r\n link.download = filename\r\n document.body.appendChild(link)\r\n link.click()\r\n document.body.removeChild(link)\r\n URL.revokeObjectURL(url)\r\n \r\n console.log('透视表HTML导出成功:', filename)\r\n } catch (error) {\r\n console.error('导出透视表HTML失败:', error)\r\n alert('导出透视表HTML失败,请重试')\r\n }\r\n },\r\n \r\n // 导出原始数据到HTML\r\n exportRawDataToHTML() {\r\n try {\r\n if (!this.filteredData || this.filteredData.length === 0) {\r\n alert('没有数据可导出')\r\n return\r\n }\r\n \r\n // 创建表格HTML\r\n let tableHTML = '<table>\\n'\r\n \r\n // 表头\r\n tableHTML += '<thead>\\n<tr>\\n'\r\n this.columns.forEach(col => {\r\n tableHTML += `<th>${col}</th>\\n`\r\n })\r\n tableHTML += '</tr>\\n</thead>\\n'\r\n \r\n // 表体\r\n tableHTML += '<tbody>\\n'\r\n this.filteredData.forEach(row => {\r\n tableHTML += '<tr>\\n'\r\n this.columns.forEach(col => {\r\n tableHTML += `<td>${row[col] !== undefined ? row[col] : ''}</td>\\n`\r\n })\r\n tableHTML += '</tr>\\n'\r\n })\r\n tableHTML += '</tbody>\\n</table>'\r\n \r\n // 创建完整的HTML文档\r\n const htmlContent = `\r\n<!DOCTYPE html>\r\n<html lang=\"zh-CN\">\r\n<head>\r\n <meta charset=\"UTF-8\">\r\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\r\n <title>数据导出</title>\r\n <style>\r\n * {\r\n margin: 0;\r\n padding: 0;\r\n box-sizing: border-box;\r\n }\r\n \r\n body {\r\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;\r\n padding: 20px;\r\n background: #f8fafc;\r\n }\r\n \r\n .container {\r\n max-width: 100%;\r\n margin: 0 auto;\r\n background: white;\r\n padding: 20px;\r\n border-radius: 8px;\r\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);\r\n }\r\n \r\n h1 {\r\n color: #334155;\r\n margin-bottom: 20px;\r\n font-size: 24px;\r\n }\r\n \r\n .export-info {\r\n color: #64748b;\r\n margin-bottom: 20px;\r\n font-size: 14px;\r\n }\r\n \r\n table {\r\n width: 100%;\r\n border-collapse: collapse;\r\n margin: 20px 0;\r\n font-size: 14px;\r\n }\r\n \r\n th, td {\r\n border: 1px solid #e2e8f0;\r\n padding: 12px;\r\n text-align: left;\r\n }\r\n \r\n th {\r\n background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);\r\n color: #334155;\r\n font-weight: 600;\r\n position: sticky;\r\n top: 0;\r\n }\r\n \r\n tr:nth-child(even) {\r\n background: #f8fafc;\r\n }\r\n \r\n tr:hover {\r\n background: #e2e8f0;\r\n }\r\n </style>\r\n</head>\r\n<body>\r\n <div class=\"container\">\r\n <h1>原始数据</h1>\r\n <div class=\"export-info\">\r\n 导出时间: ${new Date().toLocaleString('zh-CN')}<br>\r\n 数据行数: ${this.filteredData.length}<br>\r\n 字段数: ${this.columns.length}\r\n </div>\r\n ${tableHTML}\r\n </div>\r\n</body>\r\n</html>`\r\n \r\n // 生成文件名\r\n const now = new Date()\r\n const timestamp = now.toISOString().slice(0, 10).replace(/-/g, '')\r\n const timeStr = now.toTimeString().slice(0, 8).replace(/:/g, '')\r\n const filename = `数据_${timestamp}_${timeStr}.html`\r\n \r\n // 创建下载链接\r\n const blob = new Blob([htmlContent], { type: 'text/html;charset=utf-8' })\r\n const url = URL.createObjectURL(blob)\r\n const link = document.createElement('a')\r\n link.href = url\r\n link.download = filename\r\n document.body.appendChild(link)\r\n link.click()\r\n document.body.removeChild(link)\r\n URL.revokeObjectURL(url)\r\n \r\n console.log('数据HTML导出成功:', filename)\r\n } catch (error) {\r\n console.error('导出数据HTML失败:', error)\r\n alert('导出数据HTML失败,请重试')\r\n }\r\n },\r\n \r\n // 切换侧边面板折叠状态\r\n toggleSidePanel() {\r\n this.sidePanelCollapsed = !this.sidePanelCollapsed\r\n },\r\n \r\n // 切换过滤器折叠状态\r\n toggleFilterCollapse(index: number) {\r\n if (this.expandedFilters.has(index)) {\r\n this.expandedFilters.delete(index)\r\n } else {\r\n this.expandedFilters.add(index)\r\n }\r\n },\r\n \r\n // 检查过滤器是否展开\r\n isFilterExpanded(index: number): boolean {\r\n return this.expandedFilters.has(index)\r\n },\r\n \r\n // 获取过滤后的唯一值(支持搜索)\r\n getFilteredUniqueValues(field: string): any[] {\r\n const values = this.uniqueFieldValues[field] || []\r\n if (!this.filterSearchQuery) {\r\n return values\r\n }\r\n return values.filter(val => \r\n String(val).toLowerCase().includes(this.filterSearchQuery.toLowerCase())\r\n )\r\n },\r\n \r\n // 清空搜索\r\n clearFilterSearch() {\r\n this.filterSearchQuery = ''\r\n },\r\n \r\n // 加载更多数据\r\n loadMoreData() {\r\n this.displayedRows += 50\r\n },\r\n \r\n // 重置显示行数\r\n resetDisplayedRows() {\r\n this.displayedRows = 50\r\n }\r\n }\r\n})\r\n</script>\r\n\r\n <template>\r\n <div class=\"pivot-table\">\r\n <!-- 无数据状态 -->\r\n <div v-if=\"!hasData\" class=\"empty-state\">\r\n <div class=\"empty-state-content\">\r\n <div class=\"empty-icon\">\r\n <svg width=\"120\" height=\"120\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\r\n <path d=\"M3 3V21H21\" stroke=\"#E2E8F0\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\r\n <path d=\"M19 9L14 14L10 10L7 13\" stroke=\"#CBD5E1\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\r\n <path d=\"M19 9V13H15\" stroke=\"#CBD5E1\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\r\n </svg>\r\n </div>\r\n <h2 class=\"empty-title\">暂无数据</h2>\r\n <p class=\"empty-description\">请先导入数据文件或加载示例数据开始分析</p>\r\n </div>\r\n </div>\r\n\r\n <!-- 有数据状态 -->\r\n <div v-else class=\"data-container\">\r\n <!-- 隐藏的文件输入 -->\r\n <input\r\n ref=\"fileInput\"\r\n type=\"file\"\r\n accept=\".csv,.xlsx,.xls\"\r\n @change=\"handleFileUpload\"\r\n style=\"display: none\"\r\n />\r\n \r\n <!-- 顶部工具栏 -->\r\n <div class=\"toolbar\">\r\n <div class=\"toolbar-left\">\r\n <div class=\"view-toggle\">\r\n <button \r\n :class=\"['toggle-btn', { active: !showPivotTable }]\" \r\n @click=\"showPivotTable = false\"\r\n >\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\r\n <path d=\"M3 3h18v18H3zM3 9h18M3 15h18M9 3v18M15 3v18\"/>\r\n </svg>\r\n 原始数据\r\n </button>\r\n <button \r\n :class=\"['toggle-btn', { active: showPivotTable }]\" \r\n @click=\"showPivotTable = true\"\r\n >\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\r\n <rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\"/>\r\n <path d=\"M3 9h18M3 15h18M9 3v18\"/>\r\n </svg>\r\n 透视分析\r\n </button>\r\n </div>\r\n </div>\r\n \r\n <div class=\"toolbar-right\">\r\n <div class=\"data-stats\">\r\n <div class=\"stat-item\">\r\n <span class=\"stat-label\">总行数</span>\r\n <span class=\"stat-value\">{{ data.length.toLocaleString() }}</span>\r\n </div>\r\n <div class=\"stat-item\">\r\n <span class=\"stat-label\">字段数</span>\r\n <span class=\"stat-value\">{{ columns.length }}</span>\r\n </div>\r\n <div v-if=\"filters.length > 0\" class=\"stat-item highlighted\">\r\n <span class=\"stat-label\">过滤后</span>\r\n <span class=\"stat-value\">{{ filteredData.length.toLocaleString() }}</span>\r\n </div>\r\n </div>\r\n \r\n <div class=\"toolbar-actions\">\r\n <div class=\"export-dropdown\">\r\n <button \r\n class=\"export-btn\" \r\n @click=\"showExportMenu = !showExportMenu\"\r\n :title=\"showPivotTable ? '导出透视表' : '导出原始数据'\"\r\n >\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\r\n <path d=\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4\"/>\r\n <polyline points=\"7 10 12 15 17 10\"/>\r\n <line x1=\"12\" y1=\"15\" x2=\"12\" y2=\"3\"/>\r\n </svg>\r\n {{ showPivotTable ? '导出透视表' : '导出数据' }}\r\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\r\n <polyline points=\"6 9 12 15 18 9\"/>\r\n </svg>\r\n </button>\r\n \r\n <div v-if=\"showExportMenu\" class=\"export-menu\">\r\n <button \r\n class=\"export-menu-item\" \r\n @click=\"showPivotTable ? exportPivotTableToExcel() : exportRawDataToExcel(); showExportMenu = false\"\r\n >\r\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\r\n <path d=\"M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z\"/>\r\n <polyline points=\"14 2 14 8 20 8\"/>\r\n <line x1=\"16\" y1=\"13\" x2=\"8\" y2=\"13\"/>\r\n <line x1=\"16\" y1=\"17\" x2=\"8\" y2=\"17\"/>\r\n <polyline points=\"10 9 9 9 8 9\"/>\r\n </svg>\r\n 导出为 Excel (.xlsx)\r\n </button>\r\n <button \r\n class=\"export-menu-item\" \r\n @click=\"showPivotTable ? exportPivotTableToHTML() : exportRawDataToHTML(); showExportMenu = false\"\r\n >\r\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\r\n <path d=\"M12 2L2 7l10 5 10-5-10-5z\"/>\r\n <path d=\"M2 17l10 5 10-5\"/>\r\n <path d=\"M2 12l10 5 10-5\"/>\r\n </svg>\r\n 导出为 HTML (.html)\r\n </button>\r\n </div>\r\n </div>\r\n \r\n <button \r\n class=\"fullscreen-btn\" \r\n @click=\"toggleFullscreen\"\r\n :title=\"isFullscreen ? '退出全屏' : '全屏显示'\"\r\n >\r\n <svg v-if=\"!isFullscreen\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\r\n <path 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\"/>\r\n </svg>\r\n <svg v-else width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\r\n <path 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\"/>\r\n </svg>\r\n {{ isFullscreen ? '退出全屏' : '全屏' }}\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- 主内容区域 -->\r\n <div class=\"main-content\">\r\n <!-- 左侧面板 - 过滤器 -->\r\n <div class=\"side-panel\" :class=\"{ collapsed: sidePanelCollapsed }\" v-if=\"showPivotTable\">\r\n <!-- 面板折叠按钮 -->\r\n <button class=\"panel-collapse-btn\" @click=\"toggleSidePanel\" :title=\"sidePanelCollapsed ? '展开过滤面板' : '折叠过滤面板'\">\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" :class=\"{ rotated: sidePanelCollapsed }\">\r\n <polyline points=\"9 18 15 12 9 6\"/>\r\n </svg>\r\n </button>\r\n \r\n <!-- 过滤器 -->\r\n <div class=\"panel-section\" v-show=\"!sidePanelCollapsed\">\r\n <div class=\"panel-header\">\r\n <h3 class=\"panel-title\">数据过滤</h3>\r\n <button class=\"icon-btn\" @click=\"addFilter\" title=\"添加过滤\">\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\r\n <line x1=\"12\" y1=\"5\" x2=\"12\" y2=\"19\"/>\r\n <line x1=\"5\" y1=\"12\" x2=\"19\" y2=\"12\"/>\r\n </svg>\r\n </button>\r\n </div>\r\n \r\n <div v-if=\"filters.length === 0\" class=\"empty-filters\">\r\n <p>暂无过滤条件</p>\r\n <button class=\"text-btn\" @click=\"addFilter\">添加过滤</button>\r\n </div>\r\n \r\n <div v-else class=\"filters-list\">\r\n <div v-for=\"(filter, index) in filters\" :key=\"index\" class=\"filter-card\">\r\n <div class=\"filter-header\">\r\n <button \r\n class=\"collapse-btn\" \r\n @click=\"toggleFilterCollapse(index)\"\r\n :title=\"isFilterExpanded(index) ? '收起' : '展开'\"\r\n >\r\n <svg \r\n width=\"14\" \r\n height=\"14\" \r\n viewBox=\"0 0 24 24\" \r\n fill=\"none\" \r\n stroke=\"currentColor\" \r\n stroke-width=\"2\"\r\n :class=\"{ rotated: isFilterExpanded(index) }\"\r\n >\r\n <polyline points=\"6 9 12 15 18 9\"/>\r\n </svg>\r\n </button>\r\n <select v-model=\"filter.field\" class=\"filter-field-select\">\r\n <option v-for=\"col in columns\" :key=\"col\" :value=\"col\">{{ col }}</option>\r\n </select>\r\n <button class=\"icon-btn small\" @click=\"removeFilter(index)\" title=\"删除\">\r\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\r\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"/>\r\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"/>\r\n </svg>\r\n </button>\r\n </div>\r\n <div class=\"filter-body\" v-show=\"isFilterExpanded(index)\">\r\n <select v-model=\"filter.operator\" class=\"filter-operator-select\">\r\n <option value=\"equals\">等于</option>\r\n <option value=\"not_equals\">不等于</option>\r\n <option value=\"contains\">包含</option>\r\n <option value=\"not_contains\">不包含</option>\r\n <option value=\"greater_than\">大于</option>\r\n <option value=\"less_than\">小于</option>\r\n <option value=\"greater_equal\">大于等于</option>\r\n <option value=\"less_equal\">小于等于</option>\r\n <option value=\"in\">包含于(多选)</option>\r\n <option value=\"not_in\">不包含于(多选)</option>\r\n </select>\r\n \r\n <!-- 单选模式:下拉选择 + 手动输入 -->\r\n <div v-if=\"filter.operator !== 'in' && filter.operator !== 'not_in'\" class=\"filter-value-wrapper\">\r\n <select \r\n v-model=\"filter.value\" \r\n class=\"filter-value-select\"\r\n >\r\n <option value=\"\">选择或输入值</option>\r\n <option v-for=\"val in uniqueFieldValues[filter.field]\" :key=\"val\" :value=\"val\">{{ val }}</option>\r\n </select>\r\n <input \r\n v-model=\"filter.value\" \r\n type=\"text\" \r\n class=\"filter-value-input\" \r\n placeholder=\"或手动输入\"\r\n >\r\n </div>\r\n \r\n <!-- 多选模式:复选框列表 -->\r\n <div v-else class=\"filter-multi-select\">\r\n <div class=\"multi-select-header\">\r\n <span class=\"selected-count\">已选 {{ filter.values ? filter.values.length : 0 }} 项</span>\r\n <button class=\"clear-selection-btn\" @click=\"filter.values = []\" v-if=\"filter.values && filter.values.length > 0\">\r\n 清空\r\n </button>\r\n </div>\r\n \r\n <!-- 搜索框 -->\r\n <div class=\"multi-select-search\">\r\n <input \r\n v-model=\"filterSearchQuery\" \r\n type=\"text\" \r\n class=\"search-input\"\r\n placeholder=\"搜索值...\"\r\n >\r\n <button \r\n v-if=\"filterSearchQuery\" \r\n class=\"clear-search-btn\" \r\n @click=\"clearFilterSearch\"\r\n title=\"清除搜索\"\r\n >\r\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\r\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"/>\r\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"/>\r\n </svg>\r\n </button>\r\n </div>\r\n \r\n <div class=\"multi-select-list\">\r\n <label \r\n v-for=\"val in getFilteredUniqueValues(filter.field)\" \r\n :key=\"val\"\r\n :class=\"['multi-select-item', { selected: isMultiSelectValueSelected(filter, val) }]\"\r\n >\r\n <input \r\n type=\"checkbox\" \r\n :checked=\"isMultiSelectValueSelected(filter, val)\"\r\n @change=\"toggleMultiSelectValue(filter, val)\"\r\n >\r\n <span>{{ val }}</span>\r\n </label>\r\n <div v-if=\"getFilteredUniqueValues(filter.field).length === 0\" class=\"no-results\">\r\n 没有找到匹配的值\r\n </div>\r\n </div>\r\n <div class=\"multi-select-add\">\r\n <input \r\n v-model=\"filter.customValue\" \r\n type=\"text\" \r\n class=\"filter-custom-input\"\r\n placeholder=\"输入自定义值\"\r\n @keyup.enter=\"addCustomValue(filter)\"\r\n >\r\n <button class=\"add-value-btn\" @click=\"addCustomValue(filter)\">\r\n 添加\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n \r\n <button v-if=\"filters.length > 0\" class=\"clear-filters-btn\" @click=\"clearFilters\">\r\n 清除所有过滤\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- 右侧主区域 -->\r\n <div class=\"content-area\">\r\n <!-- 原始数据表格 -->\r\n <div v-show=\"!showPivotTable\" class=\"data-view\">\r\n <div class=\"data-table-wrapper\">\r\n <table class=\"modern-table\">\r\n <thead>\r\n <tr>\r\n <th v-for=\"col in columns\" :key=\"col\" class=\"table-header\">\r\n <div class=\"header-content\">\r\n <span>{{ col }}</span>\r\n </div>\r\n </th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n <tr v-for=\"(row, index) in data.slice(0, displayedRows)\" :key=\"index\" class=\"table-row\">\r\n <td v-for=\"col in columns\" :key=\"col\" class=\"table-cell\">\r\n {{ row[col] }}\r\n </td>\r\n </tr>\r\n </tbody>\r\n </table>\r\n </div>\r\n <div v-if=\"data.length > displayedRows\" class=\"table-footer\">\r\n <div class=\"table-footer-content\">\r\n <span class=\"table-info\">显示 {{ displayedRows }} 行,共 {{ data.length.toLocaleString() }} 行数据</span>\r\n <button class=\"load-more-btn\" @click=\"loadMoreData\">\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\r\n <polyline points=\"6 9 12 15 18 9\"/>\r\n </svg>\r\n 加载更多\r\n </button>\r\n </div>\r\n </div>\r\n <div v-else-if=\"data.length > 0\" class=\"table-footer\">\r\n <span class=\"table-info\">已显示全部 {{ data.length.toLocaleString() }} 行数据</span>\r\n </div>\r\n </div>\r\n\r\n <!-- 透视表 -->\r\n <div v-show=\"showPivotTable\" class=\"pivot-view\">\r\n <div class=\"pivot-wrapper\">\r\n <div ref=\"pivotOutput\" class=\"pivot-output\"></div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </template>\r\n\r\n<style scoped>\r\n/* 基础样式 */\r\n.pivot-table {\r\n width: 100%;\r\n height: 100%;\r\n background: #ffffff;\r\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;\r\n color: #1e293b;\r\n}\r\n\r\n/* 空状态 */\r\n.empty-state {\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n min-height: 600px;\r\n background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);\r\n}\r\n\r\n.empty-state-content {\r\n text-align: center;\r\n padding: 3rem;\r\n}\r\n\r\n.empty-icon {\r\n margin-bottom: 1.5rem;\r\n opacity: 0.6;\r\n}\r\n\r\n.empty-title {\r\n font-size: 1.5rem;\r\n font-weight: 600;\r\n color: #334155;\r\n margin: 0 0 0.75rem 0;\r\n}\r\n\r\n.empty-description {\r\n font-size: 1rem;\r\n color: #64748b;\r\n margin: 0;\r\n}\r\n\r\n/* 数据容器 */\r\n.data-container {\r\n display: flex;\r\n flex-direction: column;\r\n height: 100%;\r\n min-height: 600px;\r\n}\r\n\r\n/* 工具栏 */\r\n.toolbar {\r\n display: flex;\r\n justify-content: space-between;\r\n align-items: center;\r\n padding: 1rem 1.5rem;\r\n background: #ffffff;\r\n border-bottom: 1px solid #e2e8f0;\r\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);\r\n}\r\n\r\n.toolbar-left {\r\n display: flex;\r\n align-items: center;\r\n gap: 1rem;\r\n}\r\n\r\n/* 文件上传按钮 */\r\n.file-upload-btn {\r\n display: flex;\r\n align-items: center;\r\n gap: 0.5rem;\r\n padding: 0.5rem 1rem;\r\n background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);\r\n color: white;\r\n border: none;\r\n border-radius: 6px;\r\n font-size: 0.875rem;\r\n font-weight: 500;\r\n cursor: pointer;\r\n transition: all 0.2s ease;\r\n box-shadow: 0 2px 4px rgba(59, 130, 246, 0.2);\r\n}\r\n\r\n.file-upload-btn:hover:not(:disabled) {\r\n background: linear-gradient(135deg, #2563eb 0%, #1d4ed8 100%);\r\n box-shadow: 0 4px 8px rgba(59, 130, 246, 0.3);\r\n transform: translateY(-1px);\r\n}\r\n\r\n.file-upload-btn:disabled {\r\n opacity: 0.6;\r\n cursor: not-allowed;\r\n transform: none;\r\n}\r\n\r\n.view-toggle {\r\n display: flex;\r\n background: #f1f5f9;\r\n border-radius: 8px;\r\n padding: 4px;\r\n}\r\n\r\n.toggle-btn {\r\n display: flex;\r\n align-items: center;\r\n gap: 0.5rem;\r\n padding: 0.5rem 1rem;\r\n border: none;\r\n background: transparent;\r\n color: #64748b;\r\n font-size: 0.875rem;\r\n font-weight: 500;\r\n border-radius: 6px;\r\n cursor: pointer;\r\n transition: all 0.2s ease;\r\n}\r\n\r\n.toggle-btn:hover {\r\n color: #334155;\r\n background: rgba(255, 255, 255, 0.5);\r\n}\r\n\r\n.toggle-btn.active {\r\n background: #ffffff;\r\n color: #0f172a;\r\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);\r\n}\r\n\r\n.toolbar-right {\r\n display: flex;\r\n align-items: center;\r\n gap: 1.5rem;\r\n}\r\n\r\n.data-stats {\r\n display: flex;\r\n gap: 1.5rem;\r\n}\r\n\r\n.stat-item {\r\n display: flex;\r\n flex-direction: column;\r\n align-items: flex-end;\r\n}\r\n\r\n.stat-item.highlighted {\r\n background: linear-gradient(135deg, #dbeafe 0%, #bfdbfe 100%);\r\n padding: 0.5rem 1rem;\r\n border-radius: 8px;\r\n border: 1px solid #3b82f6;\r\n}\r\n\r\n.stat-label {\r\n font-size: 0.75rem;\r\n color: #64748b;\r\n font-weight: 500;\r\n text-transform: uppercase;\r\n letter-spacing: 0.05em;\r\n}\r\n\r\n.stat-value {\r\n font-size: 1.125rem;\r\n font-weight: 600;\r\n color: #0f172a;\r\n}\r\n\r\n.stat-item.highlighted .stat-value {\r\n color: #1e40af;\r\n}\r\n\r\n/* 工具栏操作区域 */\r\n.toolbar-actions {\r\n display: flex;\r\n align-items: center;\r\n gap: 0.75rem;\r\n}\r\n\r\n/* 导出下拉菜单 */\r\n.export-dropdown {\r\n position: relative;\r\n}\r\n\r\n/* 导出按钮 */\r\n.export-btn {\r\n display: flex;\r\n align-items: center;\r\n gap: 0.5rem;\r\n padding: 0.5rem 1rem;\r\n background: linear-gradient(135deg, #10b981 0%, #059669 100%);\r\n color: white;\r\n border: none;\r\n border-radius: 6px;\r\n font-size: 0.875rem;\r\n font-weight: 500;\r\n cursor: pointer;\r\n transition: all 0.2s ease;\r\n box-shadow: 0 2px 4px rgba(16, 185, 129, 0.2);\r\n}\r\n\r\n.export-btn:hover {\r\n background: linear-gradient(135deg, #059669 0%, #047857 100%);\r\n box-shadow: 0 4px 8px rgba(16, 185, 129, 0.3);\r\n transform: translateY(-1px);\r\n}\r\n\r\n.export-btn:active {\r\n transform: translateY(0);\r\n}\r\n\r\n/* 导出菜单 */\r\n.export-menu {\r\n position: absolute;\r\n top: calc(100% + 8px);\r\n right: 0;\r\n min-width: 200px;\r\n background: #ffffff;\r\n border: 1px solid #e2e8f0;\r\n border-radius: 8px;\r\n box-shadow: 0 10px 25px rgba(0, 0, 0, 0.15);\r\n z-index: 1000;\r\n overflow: hidden;\r\n animation: slideDown 0.2s ease;\r\n}\r\n\r\n@keyframes slideDown {\r\n from {\r\n opacity: 0;\r\n transform: translateY(-10px);\r\n }\r\n to {\r\n opacity: 1;\r\n transform: translateY(0);\r\n }\r\n}\r\n\r\n.export-menu-item {\r\n display: flex;\r\n align-items: center;\r\n gap: 0.75rem;\r\n width: 100%;\r\n padding: 0.75rem 1rem;\r\n background: transparent;\r\n border: none;\r\n color: #334155;\r\n font-size: 0.875rem;\r\n font-weight: 500;\r\n cursor: pointer;\r\n transition: all 0.2s ease;\r\n text-align: left;\r\n}\r\n\r\n.export-menu-item:hover {\r\n background: #f8fafc;\r\n color: #059669;\r\n}\r\n\r\n.export-menu-item:active {\r\n background: #f1f5f9;\r\n}\r\n\r\n.export-menu-item svg {\r\n flex-shrink: 0;\r\n color: #64748b;\r\n transition: color 0.2s ease;\r\n}\r\n\r\n.export-menu-item:hover svg {\r\n color: #059669;\r\n}\r\n\r\n/* 全屏按钮 */\r\n.fullscreen-btn {\r\n display: flex;\r\n align-items: center;\r\n gap: 0.5rem;\r\n padding: 0.5rem 1rem;\r\n background: linear-gradient(135deg, #6366f1 0%, #4f46e5 100%);\r\n color: white;\r\n border: none;\r\n border-radius: 6px;\r\n font-size: 0.875rem;\r\n font-weight: 500;\r\n cursor: pointer;\r\n transition: all 0.2s ease;\r\n box-shadow: 0 2px 4px rgba(99, 102, 241, 0.2);\r\n}\r\n\r\n.fullscreen-btn:hover {\r\n background: linear-gradient(135deg, #4f46e5 0%, #4338ca 100%);\r\n box-shadow: 0 4px 8px rgba(99, 102, 241, 0.3);\r\n transform: translateY(-1px);\r\n}\r\n\r\n.fullscreen-btn:active {\r\n transform: translateY(0);\r\n}\r\n\r\n/* 主内容区域 */\r\n.main-content {\r\n display: flex;\r\n flex: 1;\r\n overflow: hidden;\r\n}\r\n\r\n/* 侧边面板 */\r\n.side-panel {\r\n width: 320px;\r\n background: #f8fafc;\r\n border-right: 1px solid #e2e8f0;\r\n display: flex;\r\n flex-direction: column;\r\n overflow-y: auto;\r\n position: relative;\r\n transition: width 0.3s ease;\r\n}\r\n\r\n.side-panel.collapsed {\r\n width: 48px;\r\n}\r\n\r\n/* 面板折叠按钮 */\r\n.panel-collapse-btn {\r\n position: absolute;\r\n right: -12px;\r\n top: 50%;\r\n transform: translateY(-50%);\r\n width: 24px;\r\n height: 24px;\r\n border: 1px solid #e2e8f0;\r\n background: #ffffff;\r\n color: #64748b;\r\n border-radius: 50%;\r\n cursor: pointer;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n z-index: 10;\r\n transition: all 0.2s ease;\r\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);\r\n}\r\n\r\n.panel-collapse-btn:hover {\r\n background: #f1f5f9;\r\n color: #334155;\r\n border-color: #cbd5e1;\r\n box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);\r\n}\r\n\r\n.panel-collapse-btn svg {\r\n transition: transform 0.2s ease;\r\n}\r\n\r\n.panel-collapse-btn svg.rotated {\r\n transform: rotate(180deg);\r\n}\r\n\r\n.panel-section {\r\n padding: 1.25rem;\r\n border-bottom: 1px solid #e2e8f0;\r\n}\r\n\r\n.panel-section:last-child {\r\n border-bottom: none;\r\n}\r\n\r\n.panel-header {\r\n display: flex;\r\n justify-content: space-between;\r\n align-items: center;\r\n margin-bottom: 1rem;\r\n}\r\n\r\n.panel-title {\r\n font-size: 0.875rem;\r\n font-weight: 600;\r\n color: #334155;\r\n margin: 0;\r\n text-transform: uppercase;\r\n letter-spacing: 0.05em;\r\n}\r\n\r\n.panel-badge {\r\n background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);\r\n color: white;\r\n font-size: 0.75rem;\r\n font-weight: 600;\r\n padding: 0.25rem 0.625rem;\r\n border-radius: 9999px;\r\n}\r\n\r\n/* 字段网格 */\r\n.fields-grid {\r\n display: grid;\r\n grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));\r\n gap: 0.75rem;\r\n}\r\n\r\n.field-card {\r\n display: flex;\r\n align-items: center;\r\n gap: 0.5rem;\r\n padding: 0.75rem;\r\n background: #ffffff;\r\n border: 1px solid #e2e8f0;\r\n border-radius: 8px;\r\n cursor: pointer;\r\n transition: all 0.2s ease;\r\n}\r\n\r\n.field-card:hover {\r\n border-color: #3b82f6;\r\n box-shadow: 0 2px 8px rgba(59, 130, 246, 0.1);\r\n transform: translateY(-1px);\r\n}\r\n\r\n.field-icon {\r\n color: #64748b;\r\n flex-shrink: 0;\r\n}\r\n\r\n.field-name {\r\n font-size: 0.875rem;\r\n font-weight: 500;\r\n color: #334155;\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n}\r\n\r\n/* 图标按钮 */\r\n.icon-btn {\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 32px;\r\n height: 32px;\r\n border: none;\r\n background: #ffffff;\r\n color: #64748b;\r\n border-radius: 6px;\r\n cursor: pointer;\r\n transition: all 0.2s ease;\r\n border: 1px solid #e2e8f0;\r\n}\r\n\r\n.icon-btn:hover {\r\n background: #f1f5f9;\r\n color: #334155;\r\n border-color: #cbd5e1;\r\n}\r\n\r\n.icon-btn.small {\r\n width: 24px;\r\n height: 24px;\r\n}\r\n\r\n/* 空过滤器 */\r\n.empty-filters {\r\n text-align: center;\r\n padding: 2rem 1rem;\r\n}\r\n\r\n.empty-filters p {\r\n font-size: 0.875rem;\r\n color: #64748b;\r\n margin: 0 0 1rem 0;\r\n}\r\n\r\n.text-btn {\r\n background: transparent;\r\n border: none;\r\n color: #3b82f6;\r\n font-size: 0.875rem;\r\n font-weight: 500;\r\n cursor: pointer;\r\n padding: 0.5rem 1rem;\r\n border-radius: 6px;\r\n transition: all 0.2s ease;\r\n}\r\n\r\n.text-btn:hover {\r\n background: #dbeafe;\r\n}\r\n\r\n/* 过滤器列表 */\r\n.filters-list {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 0.75rem;\r\n}\r\n\r\n.filter-card {\r\n background: #ffffff;\r\n border: 1px solid #e2e8f0;\r\n border-radius: 8px;\r\n overflow: hidden;\r\n transition: all 0.2s ease;\r\n}\r\n\r\n.filter-card:hover {\r\n border-color: #cbd5e1;\r\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);\r\n}\r\n\r\n.filter-header {\r\n display: flex;\r\n align-items: center;\r\n gap: 0.5rem;\r\n padding: 0.75rem;\r\n background: #f8fafc;\r\n border-bottom: 1px solid #e2e8f0;\r\n}\r\n\r\n.filter-field-select {\r\n flex: 1;\r\n padding: 0.5rem;\r\n border: 1px solid #e2e8f0;\r\n border-radius: 6px;\r\n background: #ffffff;\r\n color: #334155;\r\n font-size: 0.875rem;\r\n font-weight: 500;\r\n cursor: pointer;\r\n}\r\n\r\n.filter-field-select:focus {\r\n outline: none;\r\n border-color: #3b82f6;\r\n box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);\r\n}\r\n\r\n.filter-body {\r\n display: flex;\r\n gap: 0.5rem;\r\n padding: 0.75rem;\r\n}\r\n\r\n.filter-operator-select,\r\n.filter-value-select,\r\n.filter-value-input {\r\n flex: 1;\r\n padding: 0.5rem;\r\n border: 1px solid #e2e8f0;\r\n border-radius: 6px;\r\n background: #ffffff;\r\n color: #334155;\r\n font-size: 0.875rem;\r\n}\r\n\r\n.filter-operator-select:focus,\r\n.filter-value-select:focus,\r\n.filter-value-input:focus {\r\n outline: none;\r\n border-color: #3b82f6;\r\n box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);\r\n}\r\n\r\n.clear-filters-btn {\r\n width: 100%;\r\n padding: 0.75rem;\r\n background: #fee2e2;\r\n color: #dc2626;\r\n border: 1px solid #fecaca;\r\n border-radius: 6px;\r\n font-size: 0.875rem;\r\n font-weight: 500;\r\n cursor: pointer;\r\n transition: all 0.2s ease;\r\n margin-top: 0.5rem;\r\n}\r\n\r\n.clear-filters-btn:hover {\r\n background: #fecaca;\r\n border-color: #fca5a5;\r\n}\r\n\r\n/* 过滤器值包装器 */\r\n.filter-value-wrapper {\r\n flex: 1;\r\n display: flex;\r\n flex-direction: column;\r\n gap: 0.5rem;\r\n}\r\n\r\n/* 多选过滤器 */\r\n.filter-multi-select {\r\n flex: 1;\r\n display: flex;\r\n flex-direction: column;\r\n gap: 0.5rem;\r\n max-height: 300px;\r\n}\r\n\r\n.multi-select-header {\r\n display: flex;\r\n justify-content: space-between;\r\n align-items: center;\r\n padding: 0.5rem;\r\n background: #f8fafc;\r\n border-radius: 6px;\r\n border: 1px solid #e2e8f0;\r\n}\r\n\r\n.selected-count {\r\n font-size: 0.75rem;\r\n color: #64748b;\r\n font-weight: 500;\r\n}\r\n\r\n.clear-selection-btn {\r\n background: transparent;\r\n border: none;\r\n color: #dc2626;\r\n font-size: 0.75rem;\r\n font-weight: 500;\r\n cursor: pointer;\r\n padding: 0.25rem 0.5rem;\r\n border-radius: 4px;\r\n transition: all 0.2s ease;\r\n}\r\n\r\n.clear-selection-btn:hover {\r\n background: #fee2e2;\r\n}\r\n\r\n.multi-select-list {\r\n flex: 1;\r\n overflow-y: auto;\r\n border: 1px solid #e2e8f0;\r\n border-radius: 6px;\r\n padding: 0.5rem;\r\n max-height: 150px;\r\n}\r\n\r\n.multi-select-item {\r\n display: flex;\r\n align-items: center;\r\n gap: 0.5rem;\r\n padding: 0.5rem;\r\n cursor: pointer;\r\n border-radius: 4px;\r\n transition: all 0.2s ease;\r\n font-size: 0.875rem;\r\n color: #475569;\r\n}\r\n\r\n.multi-select-item:hover {\r\n background: #f8fafc;\r\n}\r\n\r\n.multi-select-item.selected {\r\n background: #dbeafe;\r\n color: #1e40af;\r\n font-weight: 500;\r\n}\r\n\r\n.multi-select-item input[type=\"checkbox\"] {\r\n cursor: pointer;\r\n accent-color: #3b82f6;\r\n}\r\n\r\n.multi-select-add {\r\n display: flex;\r\n gap: 0.5rem;\r\n}\r\n\r\n.filter-custom-input {\r\n flex: 1;\r\n padding: 0.5rem;\r\n border: 1px solid #e2e8f0;\r\n border-radius: 6px;\r\n background: #ffffff;\r\n color: #334155;\r\n font-size: 0.875rem;\r\n}\r\n\r\n.filter-custom-input:focus {\r\n outline: none;\r\n border-color: #3b82f6;\r\n box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);\r\n}\r\n\r\n.add-value-btn {\r\n padding: 0.5rem 1rem;\r\n background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);\r\n color: white;\r\n border: none;\r\n border-radius: 6px;\r\n font-size: 0.875rem;\r\n font-weight: 500;\r\n cursor: pointer;\r\n transition: all 0.2s ease;\r\n box-shadow: 0 2px 4px rgba(59, 130, 246, 0.2);\r\n}\r\n\r\n.add-value-btn:hover {\r\n background: linear-gradient(135deg, #2563eb 0%, #1d4ed8 100%);\r\n box-shadow: 0 4px 8px rgba(59, 130, 246, 0.3);\r\n transform: translateY(-1px);\r\n}\r\n\r\n.add-value-btn:active {\r\n transform: translateY(0);\r\n}\r\n\r\n/* 内容区域 */\r\n.content-area {\r\n flex: 1;\r\n overflow: hidden;\r\n display: flex;\r\n flex-direction: column;\r\n}\r\n\r\n/* 数据视图 */\r\n.data-view {\r\n flex: 1;\r\n display: flex;\r\n flex-direction: column;\r\n overflow: hidden;\r\n}\r\n\r\n.data-table-wrapper {\r\n flex: 1;\r\n overflow: auto;\r\n background: #ffffff;\r\n}\r\n\r\n.modern-table {\r\n width: 100%;\r\n border-collapse: collapse;\r\n font-size: 0.875rem;\r\n}\r\n\r\n.table-header {\r\n background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);\r\n color: #334155;\r\n font-weight: 600;\r\n text-align: left;\r\n padding: 0.875rem 1rem;\r\n border-bottom: 2px solid #e2e8f0;\r\n position: sticky;\r\n top: 0;\r\n z-index: 10;\r\n}\r\n\r\n.header-content {\r\n display: flex;\r\n align-items: center;\r\n gap: 0.5rem;\r\n}\r\n\r\n.table-row {\r\n border-bottom: 1px solid #f1f5f9;\r\n transition: background-color 0.15s ease;\r\n}\r\n\r\n.table-row:nth-child(even) {\r\n background: #f8fafc;\r\n}\r\n\r\n.table-row:nth-child(odd) {\r\n background: #ffffff;\r\n}\r\n\r\n.table-row:hover {\r\n background: #e2e8f0 !important;\r\n}\r\n\r\n.table-cell {\r\n padding: 0.75rem 1rem;\r\n color: #475569;\r\n border-right: 1px solid #f1f5f9;\r\n white-space: nowrap;\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n min-width: 100px;\r\n}\r\n\r\n.table-cell:last-child {\r\n border-right: none;\r\n}\r\n\r\n.table-footer {\r\n padding: 1rem;\r\n background: #f8fafc;\r\n border-top: 1px solid #e2e8f0;\r\n text-align: center;\r\n color: #64748b;\r\n font-size: 0.875rem;\r\n}\r\n\r\n.table-footer-content {\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n gap: 1rem;\r\n}\r\n\r\n.table-info {\r\n color: #64748b;\r\n font-size: 0.875rem;\r\n}\r\n\r\n.load-more-btn {\r\n display: flex;\r\n align-items: center;\r\n gap: 0.5rem;\r\n padding: 0.5rem 1rem;\r\n background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);\r\n color: white;\r\n border: none;\r\n border-radius: 6px;\r\n font-size: 0.875rem;\r\n font-weight: 500;\r\n cursor: pointer;\r\n transition: all 0.2s ease;\r\n box-shadow: 0 2px 4px rgba(59, 130, 246, 0.2);\r\n}\r\n\r\n.load-more-btn:hover {\r\n background: linear-gradient(135deg, #2563eb 0%, #1d4ed8 100%);\r\n box-shadow: 0 4px 8px rgba(59, 130, 246, 0.3);\r\n transform: translateY(-1px);\r\n}\r\n\r\n.load-more-btn:active {\r\n transform: translateY(0);\r\n}\r\n\r\n.load-more-btn svg {\r\n transition: transform 0.2s ease;\r\n}\r\n\r\n.load-more-btn:hover svg {\r\n transform: translateY(2px);\r\n}\r\n\r\n/* 透视表视图 */\r\n.pivot-view {\r\n flex: 1;\r\n display: flex;\r\n flex-direction: column;\r\n overflow: hidden;\r\n}\r\n\r\n.pivot-wrapper {\r\n flex: 1;\r\n overflow: auto;\r\n background: #ffffff;\r\n padding: 1.5rem;\r\n}\r\n\r\n.pivot-output {\r\n width: 100%;\r\n min-height: 400px;\r\n}\r\n\r\n/* PivotTable.js 样式优化 */\r\n.pivot-output ::v-deep .pvtUi {\r\n color: #334155;\r\n font-family: inherit;\r\n font-size: 0.875rem;\r\n}\r\n\r\n.pivot-output ::v-deep .pvtAxisContainer,\r\n.pivot-output ::v-deep .pvtVals {\r\n border: 1px solid #e2e8f0;\r\n background: #f8fafc;\r\n padding: 0.75rem;\r\n border-radius: 8px;\r\n min-width: 120px;\r\n min-height: 60px;\r\n}\r\n\r\n.pivot-output ::v-deep .pvtAxisContainer li {\r\n padding: 0.5rem 0.75rem;\r\n list-style-type: none;\r\n cursor: move;\r\n background: #ffffff;\r\n border: 1px solid #e2e8f0;\r\n border-radius: 6px;\r\n margin-bottom: 0.5rem;\r\n font-weight: 500;\r\n color: #334155;\r\n transition: all 0.2s ease;\r\n}\r\n\r\n.pivot-output ::v-deep .pvtAxisContainer li:hover {\r\n border-color: #3b82f6;\r\n box-shadow: 0 2px 4px rgba(59, 130, 246, 0.1);\r\n}\r\n\r\n.pivot-output ::v-deep .pvtAxisContainer li.pvtPlaceholder {\r\n background: #f1f5f9;\r\n border: 2px dashed #cbd5e1;\r\n color: #94a3b8;\r\n}\r\n\r\n.pivot-output ::v-deep .pvtAxisContainer li.pvtPlaceholder::before {\r\n content: \"拖放字段到这里\";\r\n font-size: 0.75rem;\r\n}\r\n\r\n.pivot-output ::v-deep .pvtTriangle {\r\n cursor: pointer;\r\n color: #64748b;\r\n transition: color 0.2s ease;\r\n}\r\n\r\n.pivot-output ::v-deep .pvtTriangle:hover {\r\n color: #334155;\r\n}\r\n\r\n.pivot-output ::v-deep .pvtHorizList li {\r\n display: inline-block;\r\n margin-right: 0.5rem;\r\n}\r\n\r\n.pivot-output ::v-deep .pvtVertList {\r\n vertical-align: top;\r\n}\r\n\r\n.pivot-output ::v-deep .pvtFilteredAttribute {\r\n font-style: italic;\r\n color: #64748b;\r\n}\r\n\r\n.pivot-output ::v-deep .pvtFilterBox {\r\n z-index: 1000;\r\n width: 320px;\r\n border: 1px solid #e2e8f0;\r\n background-color: #fff;\r\n position: absolute;\r\n text-align: left;\r\n padding: 1rem;\r\n border-radius: 8px;\r\n box-shadow: 0 10px 25px rgba(0, 0, 0, 0.15);\r\n}\r\n\r\n.pivot-output ::v-deep .pvtFilterBox h4 {\r\n margin: 0 0 0.75rem 0;\r\n font-size: 0.875rem;\r\n font-weight: 600;\r\n color: #334155;\r\n}\r\n\r\n.pivot-output ::v-deep .pvtFilterBox p {\r\n margin: 0.5rem 0;\r\n font-size: 0.875rem;\r\n color: #64748b;\r\n}\r\n\r\n.pivot-output ::v-deep .pvtFilterBox button {\r\n margin: 0.25rem;\r\n padding: 0.5rem 1rem;\r\n border: 1px solid #e2e8f0;\r\n background: #ffffff;\r\n border-radius: 6px;\r\n cursor: pointer;\r\n font-size: 0.875rem;\r\n font-weight: 500;\r\n color: #334155;\r\n transition: all 0.2s ease;\r\n}\r\n\r\n.pivot-output ::v-deep .pvtFilterBox button:hover {\r\n background: #f8fafc;\r\n border-color: #cbd5e1;\r\n}\r\n\r\n.pivot-output ::v-deep .pvtFilterBox label {\r\n font-size: 0.875rem;\r\n color: #475569;\r\n}\r\n\r\n.pivot-output ::v-deep .pvtButton {\r\n color: #334155;\r\n border-radius: 6px;\r\n padding: 0.5rem 1rem;\r\n background: #ffffff;\r\n border: 1px solid #e2e8f0;\r\n cursor: pointer;\r\n font-size: 0.875rem;\r\n font-weight: 500;\r\n transition: all 0.2s ease;\r\n}\r\n\r\n.pivot-output ::v-deep .pvtButton:hover {\r\n background: #f8fafc;\r\n border-color: #cbd5e1;\r\n}\r\n\r\n.pivot-output ::v-deep .pvtButton.selected {\r\n background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);\r\n color: white;\r\n border-color: #3b82f6;\r\n}\r\n\r\n.pivot-output ::v-deep .pvtTable {\r\n width: 100%;\r\n border-spacing: 0;\r\n margin: 1rem 0;\r\n border-radius: 8px;\r\n overflow: hidden;\r\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);\r\n}\r\n\r\n.pivot-output ::v-deep .pvtTable thead tr th,\r\n.pivot-output ::v-deep .pvtTable tbody tr th {\r\n background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);\r\n color: #334155;\r\n font-weight: 600;\r\n border: 1px solid #e2e8f0;\r\n padding: 0.875rem 1rem;\r\n text-align: center;\r\n font-size: 0.875rem;\r\n}\r\n\r\n.pivot-output ::v-deep .pvtTable .pvtColLabel {\r\n text-align: center;\r\n}\r\n\r\n.pivot-output ::v-deep .pvtTable .pvtTotalLabel {\r\n text-align: right;\r\n}\r\n\r\n/* 透视表基础单元格样式 */\r\n.pivot-output ::v-deep .pvtTable tbody tr td {\r\n color: #475569;\r\n padding: 0.75rem 1rem;\r\n border: 1px solid #f1f5f9;\r\n text-align: right;\r\n vertical-align: top;\r\n font-size: 0.875rem;\r\n}\r\n\r\n/* 透视表斑马纹样式 - 必须在基础样式之后 */\r\n.pivot-output ::v-deep .pvtTable tbody tr:nth-child(even) td:not(.pvtRowLabel):not(.pvtTotal):not(.pvtGrandTotal) {\r\n background: #f8fafc;\r\n}\r\n\r\n.pivot-output ::v-deep .pvtTable tbody tr:nth-child(odd) td:not(.pvtRowLabel):not(.pvtTotal):not(.pvtGrandTotal) {\r\n background: #ffffff;\r\n}\r\n\r\n/* 特殊单元格样式 - 覆盖斑马纹 */\r\n.pivot-output ::v-deep .pvtTable tbody tr td.pvtRowLabel {\r\n text-align: left;\r\n background: #f8fafc;\r\n font-weight: 600;\r\n color: #334155;\r\n}\r\n\r\n.pivot-output ::v-deep .pvtTable tbody tr td.pvtTotal {\r\n background: linear-gradient(135deg, #dbeafe 0%, #bfdbfe 100%);\r\n font-weight: 700;\r\n color: #1e40af;\r\n}\r\n\r\n.pivot-output ::v-deep .pvtTable tbody tr td.pvtGrandTotal {\r\n background: linear-gradient(135deg, #fef3c7 0%, #fde68a 100%);\r\n font-weight: 700;\r\n color: #92400e;\r\n}\r\n\r\n/* 鼠标悬停效果 - 覆盖所有其他样式 */\r\n.pivot-output ::v-deep .pvtTable tbody tr:hover td:not(.pvtRowLabel):not(.pvtTotal):not(.pvtGrandTotal) {\r\n background: #e2e8f0 !important;\r\n}\r\n\r\n.pivot-output ::v-deep .pvtTable tbody tr:hover td.pvtRowLabel {\r\n background: #f1f5f9 !important;\r\n}\r\n\r\n.pivot-output ::v-deep .pvtTable tbody tr:hover td.pvtTotal {\r\n background: linear-gradient(135deg, #bfdbfe 0%, #93c5fd 100%) !important;\r\n}\r\n\r\n.pivot-output ::v-deep .pvtTable tbody tr:hover td.pvtGrandTotal {\r\n background: linear-gradient(135deg, #fde68a 0%, #fcd34d 100%) !important;\r\n}\r\n\r\n/* 确保表头不受影响 */\r\n.pivot-output ::v-deep .pvtTable thead tr th {\r\n background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%) !important;\r\n}\r\n\r\n.pivot-output ::v-deep .pvtTotal,\r\n.pivot-output ::v-deep .pvtGrandTotal {\r\n font-weight: 700;\r\n}\r\n\r\n.pivot-output ::v-deep .pvtVals {\r\n text-align: center;\r\n}\r\n\r\n.pivot-output ::v-deep .pvtAggregator {\r\n margin-bottom: 0.75rem;\r\n}\r\n\r\n.pivot-output ::v-deep .pvtAggregator select {\r\n color: #334155;\r\n border: 1px solid #e2e8f0;\r\n border-radius: 6px;\r\n padding: 0.5rem 0.75rem;\r\n font-size: 0.875rem;\r\n background: #ffffff;\r\n cursor: pointer;\r\n}\r\n\r\n.pivot-output ::v-deep .pvtAggregator select:focus {\r\n outline: none;\r\n border-color: #3b82f6;\r\n box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);\r\n}\r\n\r\n.pivot-output ::v-deep .pvtRenderer {\r\n margin-bottom: 0.75rem;\r\n}\r\n\r\n.pivot-output ::v-deep .pvtRenderer select {\r\n color: #334155;\r\n border: 1px solid #e2e8f0;\r\n border-radius: 6px;\r\n padding: 0.5rem 0.75rem;\r\n font-size: 0.875rem;\r\n background: #ffffff;\r\n cursor: pointer;\r\n}\r\n\r\n.pivot-output ::v-deep .pvtRenderer select:focus {\r\n outline: none;\r\n border-color: #3b82f6;\r\n box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);\r\n}\r\n\r\n.pivot-output ::v-deep .pvtAttrDropdown {\r\n display: inline-block;\r\n padding: 0.5rem 0.75rem;\r\n border: 1px solid #e2e8f0;\r\n border-radius: 6px;\r\n background: #fff;\r\n margin: 0.25rem;\r\n font-size: 0.875rem;\r\n color: #334155;\r\n cursor: pointer;\r\n transition: all 0.2s ease;\r\n}\r\n\r\n.pivot-output ::v-deep .pvtAttrDropdown:hover {\r\n border-color: #cbd5e1;\r\n background: #f8fafc;\r\n}\r\n\r\n.pivot-output ::v-deep .pvtAttrDropdown::after {\r\n content: \"▾\";\r\n margin-left: 0.5rem;\r\n color: #64748b;\r\n}\r\n\r\n.pivot-output ::v-deep .pvtCheckContainer {\r\n text-align: left;\r\n font-size: 0.875rem;\r\n max-height: 250px;\r\n overflow: auto;\r\n padding: 0.5rem;\r\n}\r\n\r\n.pivot-output ::v-deep .pvtCheckContainer p {\r\n margin: 0.5rem 0;\r\n color: #64748b;\r\n}\r\n\r\n.pivot-output ::v-deep .pvtCheckContainer label {\r\n display: block;\r\n padding: 0.5rem;\r\n cursor: pointer;\r\n border-radius: 4px;\r\n transition: background-color 0.15s ease;\r\n color: #475569;\r\n}\r\n\r\n.pivot-output ::v-deep .pvtCheckContainer label:hover {\r\n background: #f8fafc;\r\n}\r\n\r\n/* 折叠按钮 */\r\n.collapse-btn {\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 28px;\r\n height: 28px;\r\n min-width: 28px;\r\n min-height: 28px;\r\n border: 1px solid #e2e8f0;\r\n background: #ffffff;\r\n color: #64748b;\r\n border-radius: 4px;\r\n cursor: pointer;\r\n transition: all 0.2s ease;\r\n flex-shrink: 0;\r\n padding: 0;\r\n}\r\n\r\n.collapse-btn:hover {\r\n background: #f1f5f9;\r\n color: #334155;\r\n border-color: #cbd5e1;\r\n}\r\n\r\n.collapse-btn svg {\r\n transition: transform 0.2s ease;\r\n}\r\n\r\n.collapse-btn svg.rotated {\r\n transform: rotate(180deg);\r\n}\r\n\r\n/* 搜索框 */\r\n.multi-select-search {\r\n display: flex;\r\n align-items: center;\r\n gap: 0.5rem;\r\n padding: 0.5rem;\r\n background: #ffffff;\r\n border: 1px solid #e2e8f0;\r\n border-radius: 6px;\r\n}\r\n\r\n.search-input {\r\n flex: 1;\r\n padding: 0.5rem;\r\n border: none;\r\n background: transparent;\r\n color: #334155;\r\n font-size: 0.875rem;\r\n outline: none;\r\n}\r\n\r\n.search-input::placeholder {\r\n color: #94a3b8;\r\n}\r\n\r\n.clear-search-btn {\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 20px;\r\n height: 20px;\r\n border: none;\r\n background: transparent;\r\n color: #94a3b8;\r\n border-radius: 4px;\r\n cursor: pointer;\r\n transition: all 0.2s ease;\r\n}\r\n\r\n.clear-search-btn:hover {\r\n background: #f1f5f9;\r\n color: #64748b;\r\n}\r\n\r\n/* 无结果提示 */\r\n.no-results {\r\n padding: 1rem;\r\n text-align: center;\r\n color: #94a3b8;\r\n font-size: 0.875rem;\r\n font-style: italic;\r\n}\r\n\r\n/* 响应式设计 */\r\n@media (max-width: 1024px) {\r\n .side-panel {\r\n width: 280px;\r\n }\r\n \r\n .fields-grid {\r\n grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));\r\n }\r\n}\r\n\r\n@media (max-width: 768px) {\r\n .toolbar {\r\n flex-direction: column;\r\n gap: 1rem;\r\n align-items: stretch;\r\n }\r\n \r\n .toolbar-left,\r\n .toolbar-right {\r\n justify-content: center;\r\n }\r\n \r\n .side-panel {\r\n width: 100%;\r\n border-right: none;\r\n border-bottom: 1px solid #e2e8f0;\r\n }\r\n \r\n .main-content {\r\n flex-direction: column;\r\n }\r\n}\r\n</style>\r\n","<script lang=\"ts\">\r\nimport Vue from 'vue'\r\nimport PivotTable from './PivotTable.vue'\r\n\r\nexport default Vue.extend({\r\n name: 'OnlineAnalysisButton',\r\n components: {\r\n PivotTable\r\n },\r\n data() {\r\n return {\r\n showModal: false,\r\n tableData: [] as any[],\r\n buttonPosition: 'bottom-right' as 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left'\r\n }\r\n },\r\n props: {\r\n // 按钮位置\r\n position: {\r\n type: String as () => 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left',\r\n default: 'bottom-right'\r\n },\r\n // 按钮文本\r\n buttonText: {\r\n type: String,\r\n default: '📊 透视分析'\r\n },\r\n // 按钮样式类名\r\n buttonClass: {\r\n type: String,\r\n default: ''\r\n },\r\n // 自定义 el-table 选择器\r\n tableSelector: {\r\n type: String,\r\n default: '.el-table'\r\n },\r\n // 是否自动提取数据\r\n autoExtract: {\r\n type: Boolean,\r\n default: true\r\n }\r\n },\r\n mounted() {\r\n this.buttonPosition = this.position\r\n },\r\n methods: {\r\n openAnalysis() {\r\n if (this.autoExtract) {\r\n this.extractTableData()\r\n }\r\n this.showModal = true\r\n },\r\n \r\n closeAnalysis() {\r\n this.showModal = false\r\n },\r\n \r\n extractTableData() {\r\n try {\r\n // 查找页面中的 Element UI el-table 组件\r\n const elTables = document.querySelectorAll(this.tableSelector)\r\n \r\n if (elTables.length === 0) {\r\n console.warn('未找到 Element UI el-table 组件')\r\n this.tableData = []\r\n return\r\n }\r\n \r\n // 提取第一个 el-table 的数据\r\n const elTable = elTables[0]\r\n const data = this.parseElTable(elTable)\r\n \r\n console.log('提取到的 el-table 数据:', data)\r\n this.tableData = data\r\n } catch (error) {\r\n console.error('提取 el-table 数据失败:', error)\r\n this.tableData = []\r\n }\r\n },\r\n \r\n parseElTable(elTable: Element): any[] {\r\n const result: any[] = []\r\n \r\n try {\r\n // 获取表头\r\n const headers: string[] = []\r\n const headerCells = elTable.querySelectorAll('.el-table__header-wrapper thead tr th .cell')\r\n \r\n if (headerCells.length === 0) {\r\n console.warn('未找到表头')\r\n return []\r\n }\r\n \r\n headerCells.forEach(cell => {\r\n const text = cell.textContent?.trim() || ''\r\n if (text) {\r\n headers.push(text)\r\n }\r\n })\r\n \r\n console.log('提取到的表头:', headers)\r\n \r\n // 获取数据行\r\n const bodyRows = elTable.querySelectorAll('.el-table__body-wrapper tbody tr')\r\n \r\n bodyRows.forEach(row => {\r\n const cells = row.querySelectorAll('td .cell')\r\n \r\n if (cells.length === 0) return\r\n \r\n const rowData: any = {}\r\n cells.forEach((cell, index) => {\r\n if (index < headers.length) {\r\n const header = headers[index]\r\n let value = cell.textContent?.trim() || ''\r\n \r\n // 尝试转换为数字\r\n if (value !== '' && !isNaN(Number(value))) {\r\n value = Number(value)\r\n }\r\n \r\n rowData[header] = value\r\n }\r\n })\r\n \r\n // 只添加有数据的行\r\n if (Object.keys(rowData).length > 0) {\r\n result.push(rowData)\r\n }\r\n })\r\n \r\n console.log(`提取到 ${result.length} 行数据`)\r\n \r\n } catch (error) {\r\n console.error('解析 el-table 失败:', error)\r\n }\r\n \r\n return result\r\n },\r\n \r\n // 外部调用的方法:手动设置数据\r\n setData(data: any[]) {\r\n this.tableData = data\r\n },\r\n \r\n // 外部调用的方法:获取当前数据\r\n getData(): any[] {\r\n return this.tableData\r\n }\r\n }\r\n})\r\n</script>\r\n\r\n<template>\r\n <div class=\"online-analysis-button\">\r\n <!-- 分析按钮 -->\r\n <button \r\n :class=\"['analysis-button', buttonPosition, buttonClass]\"\r\n @click=\"openAnalysis\"\r\n :title=\"'点击进行透视分析'\"\r\n >\r\n {{ buttonText }}\r\n </button>\r\n \r\n <!-- 模态框 -->\r\n <transition name=\"modal\">\r\n <div v-if=\"showModal\" class=\"modal-overlay\" @click.self=\"closeAnalysis\">\r\n <div class=\"modal-content\">\r\n <!-- 模态框头部 -->\r\n <div class=\"modal-header\">\r\n <h2 class=\"modal-title\">📊 数据透视分析</h2>\r\n <button class=\"close-button\" @click=\"closeAnalysis\" title=\"关闭\">\r\n <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\r\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"/>\r\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"/>\r\n </svg>\r\n </button>\r\n </div>\r\n \r\n <!-- 模态框内容 -->\r\n <div class=\"modal-body\">\r\n <PivotTable :data=\"tableData\" />\r\n </div>\r\n </div>\r\n </div>\r\n </transition>\r\n </div>\r\n</template>\r\n\r\n<style scoped>\r\n/* 按钮容器 */\r\n.online-analysis-button {\r\n position: relative;\r\n display: inline-block;\r\n}\r\n\r\n/* 分析按钮 */\r\n.analysis-button {\r\n display: inline-flex;\r\n align-items: center;\r\n gap: 0.5rem;\r\n padding: 0.75rem 1.5rem;\r\n background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\r\n color: white;\r\n border: none;\r\n border-radius: 8px;\r\n font-size: 1rem;\r\n font-weight: 600;\r\n cursor: pointer;\r\n transition: all 0.3s ease;\r\n box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);\r\n white-space: nowrap;\r\n z-index: 1000;\r\n}\r\n\r\n.analysis-button:hover {\r\n transform: translateY(-2px);\r\n box-shadow: 0 6px 20px rgba(102, 126, 234, 0.6);\r\n}\r\n\r\n.analysis-button:active {\r\n transform: translateY(0);\r\n}\r\n\r\n/* 按钮位置 */\r\n.analysis-button.bottom-right {\r\n position: fixed;\r\n bottom: 2rem;\r\n right: 2rem;\r\n}\r\n\r\n.analysis-button.bottom-left {\r\n position: fixed;\r\n bottom: 2rem;\r\n left: 2rem;\r\n}\r\n\r\n.analysis-button.top-right {\r\n position: fixed;\r\n top: 2rem;\r\n right: 2rem;\r\n}\r\n\r\n.analysis-button.top-left {\r\n position: fixed;\r\n top: 2rem;\r\n left: 2rem;\r\n}\r\n\r\n/* 模态框遮罩 */\r\n.modal-overlay {\r\n position: fixed;\r\n top: 0;\r\n left: 0;\r\n right: 0;\r\n bottom: 0;\r\n background: rgba(0, 0, 0, 0.6);\r\n backdrop-filter: blur(4px);\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n z-index: 9999;\r\n padding: 1rem;\r\n}\r\n\r\n/* 模态框内容 */\r\n.modal-content {\r\n background: white;\r\n border-radius: 12px;\r\n box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);\r\n width: 100%;\r\n max-width: 95vw;\r\n height: 90vh;\r\n display: flex;\r\n flex-direction: column;\r\n overflow: hidden;\r\n animation: modalSlideIn 0.3s ease;\r\n}\r\n\r\n@keyframes modalSlideIn {\r\n from {\r\n opacity: 0;\r\n transform: scale(0.95) translateY(-20px);\r\n }\r\n to {\r\n opacity: 1;\r\n transform: scale(1) translateY(0);\r\n }\r\n}\r\n\r\n/* 模态框头部 */\r\n.modal-header {\r\n display: flex;\r\n justify-content: space-between;\r\n align-items: center;\r\n padding: 1.25rem 1.5rem;\r\n background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\r\n color: white;\r\n border-bottom: 1px solid #e2e8f0;\r\n flex-shrink: 0;\r\n}\r\n\r\n.modal-title {\r\n margin: 0;\r\n font-size: 1.25rem;\r\n font-weight: 600;\r\n display: flex;\r\n align-items: center;\r\n gap: 0.5rem;\r\n}\r\n\r\n.close-button {\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 36px;\r\n height: 36px;\r\n border: none;\r\n background: rgba(255, 255, 255, 0.2);\r\n color: white;\r\n border-radius: 8px;\r\n cursor: pointer;\r\n transition: all 0.2s ease;\r\n flex-shrink: 0;\r\n}\r\n\r\n.close-button:hover {\r\n background: rgba(255, 255, 255, 0.3);\r\n transform: rotate(90deg);\r\n}\r\n\r\n.close-button:active {\r\n transform: rotate(90deg) scale(0.95);\r\n}\r\n\r\n/* 模态框主体 */\r\n.modal-body {\r\n flex: 1;\r\n overflow: hidden;\r\n display: flex;\r\n flex-direction: column;\r\n}\r\n\r\n/* 模态框过渡动画 */\r\n.modal-enter-active,\r\n.modal-leave-active {\r\n transition: opacity 0.3s ease;\r\n}\r\n\r\n.modal-enter,\r\n.modal-leave-to {\r\n opacity: 0;\r\n}\r\n\r\n.modal-enter-active .modal-content,\r\n.modal-leave-active .modal-content {\r\n transition: transform 0.3s ease, opacity 0.3s ease;\r\n}\r\n\r\n.modal-enter .modal-content,\r\n.modal-leave-to .modal-content {\r\n transform: scale(0.95) translateY(-20px);\r\n opacity: 0;\r\n}\r\n\r\n/* 响应式设计 */\r\n@media (max-width: 768px) {\r\n .analysis-button {\r\n padding: 0.625rem 1.25rem;\r\n font-size: 0.875rem;\r\n }\r\n \r\n .analysis-button.bottom-right,\r\n .analysis-button.bottom-left {\r\n bottom: 1rem;\r\n right: 1rem;\r\n }\r\n \r\n .analysis-button.bottom-left {\r\n right: auto;\r\n left: 1rem;\r\n }\r\n \r\n .modal-content {\r\n height: 95vh;\r\n max-width: 100vw;\r\n border-radius: 0;\r\n }\r\n \r\n .modal-header {\r\n padding: 1rem;\r\n }\r\n \r\n .modal-title {\r\n font-size: 1rem;\r\n }\r\n}\r\n</style>","import Vue from 'vue'\r\nimport OnlineAnalysisButton from './components/OnlineAnalysisButton.vue'\r\nimport PivotTable from './components/PivotTable.vue'\r\n\r\n// 导出组件\r\nexport { OnlineAnalysisButton, PivotTable }\r\n\r\n// 导出类型\r\nexport interface PivotData {\r\n [key: string]: any\r\n}\r\n\r\n// Vue 插件安装方法\r\nconst install = function(vue: typeof Vue) {\r\n if (install.installed) return\r\n install.installed = true\r\n \r\n vue.component('OnlineAnalysisButton', OnlineAnalysisButton)\r\n vue.component('PivotTable', PivotTable)\r\n}\r\n\r\n// 自动安装\r\nif (typeof window !== 'undefined' && window.Vue) {\r\n install(window.Vue)\r\n}\r\n\r\n// 默认导出\r\nexport default {\r\n install,\r\n OnlineAnalysisButton,\r\n PivotTable\r\n}\r\n\r\n// 声明安装状态\r\ndeclare const install: {\r\n installed: boolean\r\n (vue: typeof Vue): void\r\n}"],"names":["Vue","extend","name","props","data","type","Array","default","showPivotTable","filters","showFilters","pivotInitialized","fileInput","isLoading","errorMessage","isFullscreen","showExportMenu","sidePanelCollapsed","filterSearchQuery","expandedFilters","Set","displayedRows","computed","hasData","this","length","columns","Object","keys","filteredData","filter","item","every","itemValue","field","filterValue","value","filterValues","values","operator","String","toLowerCase","includes","Number","uniqueFieldValues","result","forEach","col","map","from","sort","watch","newVal","$nextTick","initializePivotTable","deep","handler","newData","oldData","hasDataChanged","updatePivotTable","resetDisplayedRows","methods","pivotElement","$refs","pivotOutput","$","empty","pivotUI","rows","cols","vals","aggregatorName","rendererName","renderers","pivotUtilities","c3_renderers","d3_renderers","export_renderers","hiddenAttributes","menuLimit","colsLimit","rowsLimit","unusedAttrsVertical","autoSortUnusedAttrs","onRefresh","config","error","addFilter","newIndex","push","add","removeFilter","index","splice","clearFilters","getOperatorLabel","equals","not_equals","contains","not_contains","greater_than","less_than","greater_equal","less_equal","in","not_in","toggleMultiSelectValue","$set","indexOf","$forceUpdate","isMultiSelectValueSelected","addCustomValue","customValue","trim","handleFileUpload","event","target","file","files","fileExtension","split","pop","processCSV","Error","processExcel","message","reader","FileReader","onload","e","text","parseCSV","$emit","onerror","readAsText","lines","line","headers","parseCSVLine","i","row","header","current","inQuotes","char","workbook","XLSX","read","firstSheetName","SheetNames","worksheet","Sheets","jsonData","utils","sheet_to_json","slice","some","cell","obj","isNaN","readAsBinaryString","triggerFileInput","input","click","newKeys","oldKeys","key","toggleFullscreen","document","body","style","overflow","classList","remove","exportPivotTableToExcel","alert","table","querySelector","querySelectorAll","rowData","colSpan","parseInt","getAttribute","textContent","book_new","aoa_to_sheet","colWidths","wch","book_append_sheet","now","Date","timestamp","toISOString","replace","filename","toTimeString","writeFile","exportRawDataToExcel","json_to_sheet","exportPivotTableToHTML","htmlContent","toLocaleString","outerHTML","blob","Blob","url","URL","createObjectURL","link","createElement","href","download","appendChild","removeChild","revokeObjectURL","exportRawDataToHTML","tableHTML","toggleSidePanel","toggleFilterCollapse","has","delete","isFilterExpanded","getFilteredUniqueValues","val","clearFilterSearch","loadMoreData","components","PivotTable","showModal","tableData","buttonPosition","position","buttonText","buttonClass","tableSelector","autoExtract","Boolean","mounted","openAnalysis","extractTableData","closeAnalysis","elTables","elTable","parseElTable","headerCells","cells","setData","getData","install","vue","installed","component","OnlineAnalysisButton","window"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("vue"),require("xlsx")):"function"==typeof define&&define.amd?define(["exports","vue","xlsx"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).OnlineAnalysisButton={},t.Vue,t.XLSX)}(this,function(t,e,s){"use strict";function a(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 i=a(s);function n(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 l=n(e.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,s=i.read(e,{type:"binary"}),a=s.SheetNames[0],n=s.Sheets[a],l=i.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 s=[];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)}),s.push(e)});const a=i.utils.book_new(),n=i.utils.aoa_to_sheet(s),l=s[0].map(()=>({wch:15}));n["!cols"]=l,i.utils.book_append_sheet(a,n,"透视表");const o=new Date,r=o.toISOString().slice(0,10).replace(/-/g,""),c=`透视表_${r}_${o.toTimeString().slice(0,8).replace(/:/g,"")}.xlsx`;i.writeFile(a,c)}catch(t){alert("导出透视表失败,请重试")}},exportRawDataToExcel(){try{if(!this.filteredData||0===this.filteredData.length)return void alert("没有数据可导出");const t=i.utils.book_new(),e=i.utils.json_to_sheet(this.filteredData),s=this.columns.map(()=>({wch:15}));e["!cols"]=s,i.utils.book_append_sheet(t,e,"数据");const a=new Date,n=a.toISOString().slice(0,10).replace(/-/g,""),l=`数据_${n}_${a.toTimeString().slice(0,8).replace(/:/g,"")}.xlsx`;i.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;var o=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)};const r=n(e.extend({name:"OnlineAnalysisButton",components:{PivotTable:l},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}}}),o,[],0,0,"7e7b1fe0").exports,c=function(t){c.installed||(c.installed=!0,t.component("OnlineAnalysisButton",r),t.component("PivotTable",l))};"undefined"!=typeof window&&window.Vue&&c(window.Vue);const d={install:c,OnlineAnalysisButton:r,PivotTable:l};t.OnlineAnalysisButton=r,t.PivotTable=l,t.default=d,Object.defineProperties(t,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})});
2
+ //# sourceMappingURL=online-analysis-button.umd.js.map