| | import React, { useRef } from 'react'; |
| | import { |
| | Button, |
| | Typography, |
| | Toast, |
| | Modal, |
| | Dropdown, |
| | } from '@douyinfe/semi-ui'; |
| | import { |
| | Download, |
| | Upload, |
| | RotateCcw, |
| | Settings2, |
| | } from 'lucide-react'; |
| | import { useTranslation } from 'react-i18next'; |
| | import { exportConfig, importConfig, clearConfig, hasStoredConfig, getConfigTimestamp } from './configStorage'; |
| |
|
| | const ConfigManager = ({ |
| | currentConfig, |
| | onConfigImport, |
| | onConfigReset, |
| | styleState, |
| | messages, |
| | }) => { |
| | const { t } = useTranslation(); |
| | const fileInputRef = useRef(null); |
| |
|
| | const handleExport = () => { |
| | try { |
| | |
| | const configWithTimestamp = { |
| | ...currentConfig, |
| | timestamp: new Date().toISOString(), |
| | }; |
| | localStorage.setItem('playground_config', JSON.stringify(configWithTimestamp)); |
| |
|
| | exportConfig(currentConfig, messages); |
| | Toast.success({ |
| | content: t('配置已导出到下载文件夹'), |
| | duration: 3, |
| | }); |
| | } catch (error) { |
| | Toast.error({ |
| | content: t('导出配置失败: ') + error.message, |
| | duration: 3, |
| | }); |
| | } |
| | }; |
| |
|
| | const handleImportClick = () => { |
| | fileInputRef.current?.click(); |
| | }; |
| |
|
| | const handleFileChange = async (event) => { |
| | const file = event.target.files[0]; |
| | if (!file) return; |
| |
|
| | try { |
| | const importedConfig = await importConfig(file); |
| |
|
| | Modal.confirm({ |
| | title: t('确认导入配置'), |
| | content: t('导入的配置将覆盖当前设置,是否继续?'), |
| | okText: t('确定导入'), |
| | cancelText: t('取消'), |
| | onOk: () => { |
| | onConfigImport(importedConfig); |
| | Toast.success({ |
| | content: t('配置导入成功'), |
| | duration: 3, |
| | }); |
| | }, |
| | }); |
| | } catch (error) { |
| | Toast.error({ |
| | content: t('导入配置失败: ') + error.message, |
| | duration: 3, |
| | }); |
| | } finally { |
| | |
| | event.target.value = ''; |
| | } |
| | }; |
| |
|
| | const handleReset = () => { |
| | Modal.confirm({ |
| | title: t('重置配置'), |
| | content: t('将清除所有保存的配置并恢复默认设置,此操作不可撤销。是否继续?'), |
| | okText: t('确定重置'), |
| | cancelText: t('取消'), |
| | okButtonProps: { |
| | type: 'danger', |
| | }, |
| | onOk: () => { |
| | |
| | Modal.confirm({ |
| | title: t('重置选项'), |
| | content: t('是否同时重置对话消息?选择"是"将清空所有对话记录并恢复默认示例;选择"否"将保留当前对话记录。'), |
| | okText: t('同时重置消息'), |
| | cancelText: t('仅重置配置'), |
| | okButtonProps: { |
| | type: 'danger', |
| | }, |
| | onOk: () => { |
| | clearConfig(); |
| | onConfigReset({ resetMessages: true }); |
| | Toast.success({ |
| | content: t('配置和消息已全部重置'), |
| | duration: 3, |
| | }); |
| | }, |
| | onCancel: () => { |
| | clearConfig(); |
| | onConfigReset({ resetMessages: false }); |
| | Toast.success({ |
| | content: t('配置已重置,对话消息已保留'), |
| | duration: 3, |
| | }); |
| | }, |
| | }); |
| | }, |
| | }); |
| | }; |
| |
|
| | const getConfigStatus = () => { |
| | if (hasStoredConfig()) { |
| | const timestamp = getConfigTimestamp(); |
| | if (timestamp) { |
| | const date = new Date(timestamp); |
| | return t('上次保存: ') + date.toLocaleString(); |
| | } |
| | return t('已有保存的配置'); |
| | } |
| | return t('暂无保存的配置'); |
| | }; |
| |
|
| | const dropdownItems = [ |
| | { |
| | node: 'item', |
| | name: 'export', |
| | onClick: handleExport, |
| | children: ( |
| | <div className="flex items-center gap-2"> |
| | <Download size={14} /> |
| | {t('导出配置')} |
| | </div> |
| | ), |
| | }, |
| | { |
| | node: 'item', |
| | name: 'import', |
| | onClick: handleImportClick, |
| | children: ( |
| | <div className="flex items-center gap-2"> |
| | <Upload size={14} /> |
| | {t('导入配置')} |
| | </div> |
| | ), |
| | }, |
| | { |
| | node: 'divider', |
| | }, |
| | { |
| | node: 'item', |
| | name: 'reset', |
| | onClick: handleReset, |
| | children: ( |
| | <div className="flex items-center gap-2 text-red-600"> |
| | <RotateCcw size={14} /> |
| | {t('重置配置')} |
| | </div> |
| | ), |
| | }, |
| | ]; |
| |
|
| | if (styleState.isMobile) { |
| | |
| | return ( |
| | <> |
| | <Dropdown |
| | trigger="click" |
| | position="bottomLeft" |
| | showTick |
| | menu={dropdownItems} |
| | > |
| | <Button |
| | icon={<Settings2 size={14} />} |
| | theme="borderless" |
| | type="tertiary" |
| | size="small" |
| | className="!rounded-lg !text-gray-600 hover:!text-blue-600 hover:!bg-blue-50" |
| | /> |
| | </Dropdown> |
| | |
| | <input |
| | ref={fileInputRef} |
| | type="file" |
| | accept=".json" |
| | onChange={handleFileChange} |
| | style={{ display: 'none' }} |
| | /> |
| | </> |
| | ); |
| | } |
| |
|
| | |
| | return ( |
| | <div className="space-y-3"> |
| | {/* 配置状态信息和重置按钮 */} |
| | <div className="flex items-center justify-between"> |
| | <Typography.Text className="text-xs text-gray-500"> |
| | {getConfigStatus()} |
| | </Typography.Text> |
| | <Button |
| | icon={<RotateCcw size={12} />} |
| | size="small" |
| | theme="borderless" |
| | type="danger" |
| | onClick={handleReset} |
| | className="!rounded-full !text-xs !px-2" |
| | /> |
| | </div> |
| | |
| | {/* 导出和导入按钮 */} |
| | <div className="flex gap-2"> |
| | <Button |
| | icon={<Download size={12} />} |
| | size="small" |
| | theme="solid" |
| | type="primary" |
| | onClick={handleExport} |
| | className="!rounded-lg flex-1 !text-xs !h-7" |
| | > |
| | {t('导出')} |
| | </Button> |
| | |
| | <Button |
| | icon={<Upload size={12} />} |
| | size="small" |
| | theme="outline" |
| | type="primary" |
| | onClick={handleImportClick} |
| | className="!rounded-lg flex-1 !text-xs !h-7" |
| | > |
| | {t('导入')} |
| | </Button> |
| | </div> |
| | |
| | <input |
| | ref={fileInputRef} |
| | type="file" |
| | accept=".json" |
| | onChange={handleFileChange} |
| | style={{ display: 'none' }} |
| | /> |
| | </div> |
| | ); |
| | }; |
| |
|
| | export default ConfigManager; |