From 56bc0583890e950b973847c39f329976190216d9 Mon Sep 17 00:00:00 2001 From: Zeke Zhang <958414905@qq.com> Date: Thu, 17 Apr 2025 09:38:18 +0800 Subject: [PATCH] feat: enhance MultipleKeywordsInput with Excel import functionality and add tips for column selection --- .../package.json | 4 +- .../src/client/MultipleKeywordsInput.tsx | 199 ++++++++++++++++-- .../src/locale/en-US.json | 4 +- 3 files changed, 187 insertions(+), 20 deletions(-) diff --git a/packages/plugins/@nocobase/plugin-filter-operator-multiple-keywords/package.json b/packages/plugins/@nocobase/plugin-filter-operator-multiple-keywords/package.json index c18c14c994..f0a21d71e0 100644 --- a/packages/plugins/@nocobase/plugin-filter-operator-multiple-keywords/package.json +++ b/packages/plugins/@nocobase/plugin-filter-operator-multiple-keywords/package.json @@ -11,7 +11,9 @@ "keywords": [ "Multiple keywords" ], - "dependencies": {}, + "dependencies": { + "xlsx": "0.18.5" + }, "peerDependencies": { "@nocobase/client": "1.x", "@nocobase/server": "1.x", diff --git a/packages/plugins/@nocobase/plugin-filter-operator-multiple-keywords/src/client/MultipleKeywordsInput.tsx b/packages/plugins/@nocobase/plugin-filter-operator-multiple-keywords/src/client/MultipleKeywordsInput.tsx index b407c68eee..577a50e863 100644 --- a/packages/plugins/@nocobase/plugin-filter-operator-multiple-keywords/src/client/MultipleKeywordsInput.tsx +++ b/packages/plugins/@nocobase/plugin-filter-operator-multiple-keywords/src/client/MultipleKeywordsInput.tsx @@ -8,31 +8,194 @@ */ import { RemoteSelect, useCollection } from '@nocobase/client'; -import React, { FC } from 'react'; -import { Button, Space } from 'antd'; +import React, { FC, useRef, useState } from 'react'; +import { Button, Space, Modal, message, Select, Alert } from 'antd'; import { useFieldSchema } from '@formily/react'; +import * as XLSX from 'xlsx'; +import { useT } from './locale'; export const MultipleKeywordsInput: FC = (props) => { const collection = useCollection(); const fieldSchema = useFieldSchema(); + const fileInputRef = useRef(null); + const [importLoading, setImportLoading] = useState(false); + const [columnModal, setColumnModal] = useState(false); + const [columns, setColumns] = useState([]); + const [selectedColumns, setSelectedColumns] = useState([]); + const [excelData, setExcelData] = useState([]); + const t = useT(); + + const handleImportButtonClick = () => { + fileInputRef.current?.click(); + }; + + const handleFileChange = async (event: React.ChangeEvent) => { + const file = event.target.files?.[0]; + if (file) { + try { + setImportLoading(true); + + // 读取 Excel 文件 + const data = await readExcel(file); + if (data.length === 0) { + message.error('Excel 文件为空'); + setImportLoading(false); + return; + } + + // 提取所有列名 + const extractedColumns = Object.keys(data[0]); + setColumns(extractedColumns); + setExcelData(data); + + // 如果只有一列,直接导入 + if (extractedColumns.length === 1) { + const keywords = extractKeywordsFromColumn(data, extractedColumns[0]); + handleImportKeywords(keywords); + } else { + // 如果有多列,打开选择对话框 + setColumnModal(true); + } + } catch (error) { + console.error('解析 Excel 文件出错:', error); + message.error('解析 Excel 文件失败'); + } finally { + setImportLoading(false); + // 清空文件输入,以便下次选择同一文件时仍能触发 change 事件 + event.target.value = ''; + } + } + }; + + // 读取 Excel 文件内容 + const readExcel = (file: File): Promise => { + return new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.onload = (e) => { + try { + const data = e.target?.result; + const workbook = XLSX.read(data, { type: 'binary' }); + const firstSheetName = workbook.SheetNames[0]; + const worksheet = workbook.Sheets[firstSheetName]; + const jsonData = XLSX.utils.sheet_to_json(worksheet); + resolve(jsonData); + } catch (error) { + reject(error); + } + }; + reader.onerror = (error) => reject(error); + reader.readAsBinaryString(file); + }); + }; + + // 从指定列提取关键词 + const extractKeywordsFromColumn = (data: any[], columnName: string): string => { + return data + .map((row) => row[columnName]) + .filter((value) => value !== undefined && value !== null && value !== '') + .join(','); + }; + + // 从多个列提取关键词 + const extractKeywordsFromColumns = (data: any[], columnNames: string[]): string => { + const keywordSet = new Set(); + + data.forEach((row) => { + columnNames.forEach((column) => { + const value = row[column]; + if (value !== undefined && value !== null && value !== '') { + keywordSet.add(value.toString()); + } + }); + }); + + return Array.from(keywordSet).join(','); + }; + + // 处理导入关键词到输入框 + const handleImportKeywords = (keywords: string) => { + if (!keywords) { + message.warning('未找到有效关键词'); + return; + } + + // 将关键词设置到输入框 + if (props.onChange) { + const keywordArray = keywords.split(',').filter(Boolean); + props.onChange(keywordArray); + message.success(`成功导入 ${keywordArray.length} 个关键词`); + } + }; + + // 处理列选择确认 + const handleColumnSelectConfirm = () => { + if (selectedColumns.length === 0) { + message.warning('请至少选择一列'); + return; + } + + const keywords = extractKeywordsFromColumns(excelData, selectedColumns); + handleImportKeywords(keywords); + setColumnModal(false); + }; return ( - - + + + + + - - + + {/* 列选择对话框 */} + setColumnModal(false)} + okText="确认" + cancelText="取消" + > + + + + ); }; diff --git a/packages/plugins/@nocobase/plugin-filter-operator-multiple-keywords/src/locale/en-US.json b/packages/plugins/@nocobase/plugin-filter-operator-multiple-keywords/src/locale/en-US.json index 0967ef424b..bc0918509f 100644 --- a/packages/plugins/@nocobase/plugin-filter-operator-multiple-keywords/src/locale/en-US.json +++ b/packages/plugins/@nocobase/plugin-filter-operator-multiple-keywords/src/locale/en-US.json @@ -1 +1,3 @@ -{} +{ + "tips": "选择单列的效果:将导入该列的所有非空值作为关键词\n选择多列的效果:将合并多个列的非空值作为关键词,重复值将被去除" +}