mirror of
https://gitee.com/nocobase/nocobase.git
synced 2025-05-05 05:29:26 +08:00
feat: remove MultipleKeywordsInput plugin and associated files
This commit is contained in:
parent
56bc058389
commit
a6d3c5b70e
@ -1,2 +0,0 @@
|
||||
/node_modules
|
||||
/src
|
@ -1 +0,0 @@
|
||||
# @nocobase/plugin-filter-operator-multiple-keywords
|
@ -1,2 +0,0 @@
|
||||
export * from './dist/client';
|
||||
export { default } from './dist/client';
|
@ -1 +0,0 @@
|
||||
module.exports = require('./dist/client/index.js');
|
@ -1,22 +0,0 @@
|
||||
{
|
||||
"name": "@nocobase/plugin-filter-operator-multiple-keywords",
|
||||
"version": "1.7.0-beta.18",
|
||||
"main": "dist/server/index.js",
|
||||
"displayName": "Multiple keywords",
|
||||
"displayName.zh-CN": "多关键词筛选",
|
||||
"description": "Support for filtering multiple keywords in a single line text field.",
|
||||
"description.zh-CN": "单行文本字段支持多关键词筛选。",
|
||||
"homepage": "https://docs.nocobase.com/handbook/",
|
||||
"homepage.zh-CN": "https://docs-cn.nocobase.com/handbook/",
|
||||
"keywords": [
|
||||
"Multiple keywords"
|
||||
],
|
||||
"dependencies": {
|
||||
"xlsx": "0.18.5"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@nocobase/client": "1.x",
|
||||
"@nocobase/server": "1.x",
|
||||
"@nocobase/test": "1.x"
|
||||
}
|
||||
}
|
@ -1,2 +0,0 @@
|
||||
export * from './dist/server';
|
||||
export { default } from './dist/server';
|
@ -1 +0,0 @@
|
||||
module.exports = require('./dist/server/index.js');
|
@ -1,201 +0,0 @@
|
||||
/**
|
||||
* This file is part of the NocoBase (R) project.
|
||||
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
||||
* Authors: NocoBase Team.
|
||||
*
|
||||
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||
*/
|
||||
|
||||
import { RemoteSelect, useCollection } from '@nocobase/client';
|
||||
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<any> = (props) => {
|
||||
const collection = useCollection();
|
||||
const fieldSchema = useFieldSchema();
|
||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||
const [importLoading, setImportLoading] = useState(false);
|
||||
const [columnModal, setColumnModal] = useState(false);
|
||||
const [columns, setColumns] = useState<string[]>([]);
|
||||
const [selectedColumns, setSelectedColumns] = useState<string[]>([]);
|
||||
const [excelData, setExcelData] = useState<any[]>([]);
|
||||
const t = useT();
|
||||
|
||||
const handleImportButtonClick = () => {
|
||||
fileInputRef.current?.click();
|
||||
};
|
||||
|
||||
const handleFileChange = async (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
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<any[]> => {
|
||||
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<string>();
|
||||
|
||||
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 (
|
||||
<>
|
||||
<Space.Compact block>
|
||||
<RemoteSelect
|
||||
mode="tags"
|
||||
placeholder="支持输入多个关键词,通过逗号或者换行符分割"
|
||||
tokenSeparators={[',', '\n', ',']}
|
||||
fieldNames={{
|
||||
label: fieldSchema.name as string,
|
||||
value: fieldSchema.name as string,
|
||||
}}
|
||||
service={{
|
||||
resource: collection.name,
|
||||
action: 'list',
|
||||
}}
|
||||
{...props}
|
||||
/>
|
||||
<Button onClick={handleImportButtonClick} loading={importLoading}>
|
||||
导入 Excel
|
||||
</Button>
|
||||
</Space.Compact>
|
||||
<input
|
||||
type="file"
|
||||
ref={fileInputRef}
|
||||
style={{ display: 'none' }}
|
||||
accept=".xlsx,.xls"
|
||||
onChange={handleFileChange}
|
||||
/>
|
||||
|
||||
{/* 列选择对话框 */}
|
||||
<Modal
|
||||
title="选择要导入的 Excel 列"
|
||||
open={columnModal}
|
||||
onOk={handleColumnSelectConfirm}
|
||||
onCancel={() => setColumnModal(false)}
|
||||
okText="确认"
|
||||
cancelText="取消"
|
||||
>
|
||||
<Alert
|
||||
type="info"
|
||||
style={{ marginBottom: '10px', whiteSpace: 'pre-line', padding: '4px 8px' }}
|
||||
description={t('tips')}
|
||||
/>
|
||||
<Select
|
||||
mode="multiple"
|
||||
value={selectedColumns}
|
||||
onChange={(values) => setSelectedColumns(values)}
|
||||
style={{ width: '100%' }}
|
||||
placeholder="请选择要导入的列"
|
||||
>
|
||||
{columns.map((column) => (
|
||||
<Select.Option key={column} value={column}>
|
||||
{column}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
};
|
@ -1,249 +0,0 @@
|
||||
/**
|
||||
* This file is part of the NocoBase (R) project.
|
||||
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
||||
* Authors: NocoBase Team.
|
||||
*
|
||||
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||
*/
|
||||
|
||||
// CSS modules
|
||||
type CSSModuleClasses = { readonly [key: string]: string };
|
||||
|
||||
declare module '*.module.css' {
|
||||
const classes: CSSModuleClasses;
|
||||
export default classes;
|
||||
}
|
||||
declare module '*.module.scss' {
|
||||
const classes: CSSModuleClasses;
|
||||
export default classes;
|
||||
}
|
||||
declare module '*.module.sass' {
|
||||
const classes: CSSModuleClasses;
|
||||
export default classes;
|
||||
}
|
||||
declare module '*.module.less' {
|
||||
const classes: CSSModuleClasses;
|
||||
export default classes;
|
||||
}
|
||||
declare module '*.module.styl' {
|
||||
const classes: CSSModuleClasses;
|
||||
export default classes;
|
||||
}
|
||||
declare module '*.module.stylus' {
|
||||
const classes: CSSModuleClasses;
|
||||
export default classes;
|
||||
}
|
||||
declare module '*.module.pcss' {
|
||||
const classes: CSSModuleClasses;
|
||||
export default classes;
|
||||
}
|
||||
declare module '*.module.sss' {
|
||||
const classes: CSSModuleClasses;
|
||||
export default classes;
|
||||
}
|
||||
|
||||
// CSS
|
||||
declare module '*.css' { }
|
||||
declare module '*.scss' { }
|
||||
declare module '*.sass' { }
|
||||
declare module '*.less' { }
|
||||
declare module '*.styl' { }
|
||||
declare module '*.stylus' { }
|
||||
declare module '*.pcss' { }
|
||||
declare module '*.sss' { }
|
||||
|
||||
// Built-in asset types
|
||||
// see `src/node/constants.ts`
|
||||
|
||||
// images
|
||||
declare module '*.apng' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
declare module '*.png' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
declare module '*.jpg' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
declare module '*.jpeg' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
declare module '*.jfif' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
declare module '*.pjpeg' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
declare module '*.pjp' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
declare module '*.gif' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
declare module '*.svg' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
declare module '*.ico' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
declare module '*.webp' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
declare module '*.avif' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
|
||||
// media
|
||||
declare module '*.mp4' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
declare module '*.webm' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
declare module '*.ogg' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
declare module '*.mp3' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
declare module '*.wav' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
declare module '*.flac' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
declare module '*.aac' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
declare module '*.opus' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
declare module '*.mov' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
declare module '*.m4a' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
declare module '*.vtt' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
|
||||
// fonts
|
||||
declare module '*.woff' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
declare module '*.woff2' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
declare module '*.eot' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
declare module '*.ttf' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
declare module '*.otf' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
|
||||
// other
|
||||
declare module '*.webmanifest' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
declare module '*.pdf' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
declare module '*.txt' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
|
||||
// wasm?init
|
||||
declare module '*.wasm?init' {
|
||||
const initWasm: (options?: WebAssembly.Imports) => Promise<WebAssembly.Instance>;
|
||||
export default initWasm;
|
||||
}
|
||||
|
||||
// web worker
|
||||
declare module '*?worker' {
|
||||
const workerConstructor: {
|
||||
new(options?: { name?: string }): Worker;
|
||||
};
|
||||
export default workerConstructor;
|
||||
}
|
||||
|
||||
declare module '*?worker&inline' {
|
||||
const workerConstructor: {
|
||||
new(options?: { name?: string }): Worker;
|
||||
};
|
||||
export default workerConstructor;
|
||||
}
|
||||
|
||||
declare module '*?worker&url' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
|
||||
declare module '*?sharedworker' {
|
||||
const sharedWorkerConstructor: {
|
||||
new(options?: { name?: string }): SharedWorker;
|
||||
};
|
||||
export default sharedWorkerConstructor;
|
||||
}
|
||||
|
||||
declare module '*?sharedworker&inline' {
|
||||
const sharedWorkerConstructor: {
|
||||
new(options?: { name?: string }): SharedWorker;
|
||||
};
|
||||
export default sharedWorkerConstructor;
|
||||
}
|
||||
|
||||
declare module '*?sharedworker&url' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
|
||||
declare module '*?raw' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
|
||||
declare module '*?url' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
|
||||
declare module '*?inline' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
/**
|
||||
* This file is part of the NocoBase (R) project.
|
||||
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
||||
* Authors: NocoBase Team.
|
||||
*
|
||||
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||
*/
|
||||
|
||||
import { Plugin } from '@nocobase/client';
|
||||
import { MultipleKeywordsInput } from './MultipleKeywordsInput';
|
||||
|
||||
export class PluginFilterOperatorMultipleKeywordsClient extends Plugin {
|
||||
async afterAdd() {
|
||||
// await this.app.pm.add()
|
||||
}
|
||||
|
||||
async beforeLoad() {}
|
||||
|
||||
// You can get and modify the app instance here
|
||||
async load() {
|
||||
this.app.addFieldInterfaceOperator('input', {
|
||||
label: '{{t("等于任意一个")}}',
|
||||
value: '$in',
|
||||
schema: {
|
||||
'x-component': 'MultipleKeywordsInput',
|
||||
},
|
||||
});
|
||||
this.app.addFieldInterfaceOperator('input', {
|
||||
label: '{{t("不等于任意一个")}}',
|
||||
value: '$notIn',
|
||||
schema: {
|
||||
'x-component': 'MultipleKeywordsInput',
|
||||
},
|
||||
});
|
||||
|
||||
this.app.addComponents({
|
||||
MultipleKeywordsInput,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default PluginFilterOperatorMultipleKeywordsClient;
|
@ -1,21 +0,0 @@
|
||||
/**
|
||||
* This file is part of the NocoBase (R) project.
|
||||
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
||||
* Authors: NocoBase Team.
|
||||
*
|
||||
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||
*/
|
||||
|
||||
// @ts-ignore
|
||||
import pkg from './../../package.json';
|
||||
import { useApp } from '@nocobase/client';
|
||||
|
||||
export function useT() {
|
||||
const app = useApp();
|
||||
return (str: string) => app.i18n.t(str, { ns: [pkg.name, 'client'] });
|
||||
}
|
||||
|
||||
export function tStr(key: string) {
|
||||
return `{{t(${JSON.stringify(key)}, { ns: ['${pkg.name}', 'client'], nsMode: 'fallback' })}}`;
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
/**
|
||||
* This file is part of the NocoBase (R) project.
|
||||
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
||||
* Authors: NocoBase Team.
|
||||
*
|
||||
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||
*/
|
||||
|
||||
export * from './server';
|
||||
export { default } from './server';
|
@ -1,3 +0,0 @@
|
||||
{
|
||||
"tips": "选择单列的效果:将导入该列的所有非空值作为关键词\n选择多列的效果:将合并多个列的非空值作为关键词,重复值将被去除"
|
||||
}
|
@ -1 +0,0 @@
|
||||
{}
|
@ -1 +0,0 @@
|
||||
{}
|
@ -1,10 +0,0 @@
|
||||
/**
|
||||
* This file is part of the NocoBase (R) project.
|
||||
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
||||
* Authors: NocoBase Team.
|
||||
*
|
||||
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||
*/
|
||||
|
||||
export { default } from './plugin';
|
@ -1,28 +0,0 @@
|
||||
/**
|
||||
* This file is part of the NocoBase (R) project.
|
||||
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
||||
* Authors: NocoBase Team.
|
||||
*
|
||||
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||
*/
|
||||
|
||||
import { Plugin } from '@nocobase/server';
|
||||
|
||||
export class PluginFilterOperatorMultipleKeywordsServer extends Plugin {
|
||||
async afterAdd() {}
|
||||
|
||||
async beforeLoad() {}
|
||||
|
||||
async load() {}
|
||||
|
||||
async install() {}
|
||||
|
||||
async afterEnable() {}
|
||||
|
||||
async afterDisable() {}
|
||||
|
||||
async remove() {}
|
||||
}
|
||||
|
||||
export default PluginFilterOperatorMultipleKeywordsServer;
|
Loading…
x
Reference in New Issue
Block a user