From a430284aa21e3ae1f0d5654e55b2ad2852519cc2 Mon Sep 17 00:00:00 2001 From: wwf <yearningwang@iqtogether.com> Date: 星期三, 04 六月 2025 15:17:49 +0800 Subject: [PATCH] 初始化 --- app/components/app/configuration/config-var/index.tsx | 176 ++++++++++++++++++++++++++++++++++++++++++++++++++++------ 1 files changed, 156 insertions(+), 20 deletions(-) diff --git a/app/components/app/configuration/config-var/index.tsx b/app/components/app/configuration/config-var/index.tsx index 612d476..67bc373 100644 --- a/app/components/app/configuration/config-var/index.tsx +++ b/app/components/app/configuration/config-var/index.tsx @@ -3,17 +3,26 @@ import React, { useState } from 'react' import { useTranslation } from 'react-i18next' import { useBoolean } from 'ahooks' +import type { Timeout } from 'ahooks/lib/useRequest/src/types' import { useContext } from 'use-context-selector' import produce from 'immer' +import { + RiDeleteBinLine, +} from '@remixicon/react' import Panel from '../base/feature-panel' import EditModal from './config-modal' -import VarItem from './var-item' +import IconTypeIcon from './input-type-icon' +import type { IInputTypeIconProps } from './input-type-icon' +import s from './style.module.css' import SelectVarType from './select-var-type' +import { BracketsX as VarIcon } from '@/app/components/base/icons/src/vender/line/development' import Tooltip from '@/app/components/base/tooltip' import type { PromptVariable } from '@/models/debug' -import { DEFAULT_VALUE_MAX_LEN } from '@/config' -import { getNewVar } from '@/utils/var' +import { DEFAULT_VALUE_MAX_LEN, getMaxVarNameLength } from '@/config' +import { checkKeys, getNewVar } from '@/utils/var' +import Switch from '@/app/components/base/switch' import Toast from '@/app/components/base/toast' +import { Settings01 } from '@/app/components/base/icons/src/vender/line/general' import Confirm from '@/app/components/base/confirm' import ConfigContext from '@/context/debug-configuration' import { AppType } from '@/types/app' @@ -41,6 +50,8 @@ onPromptVariablesChange?: (promptVariables: PromptVariable[]) => void } +let conflictTimer: Timeout + const ConfigVar: FC<IConfigVarProps> = ({ promptVariables, readonly, onPromptVariablesChange }) => { const { t } = useTranslation() const { @@ -50,6 +61,19 @@ const { eventEmitter } = useEventEmitterContextContext() const hasVar = promptVariables.length > 0 + const updatePromptVariable = (key: string, updateKey: string, newValue: string | boolean) => { + const newPromptVariables = promptVariables.map((item) => { + if (item.key === key) { + return { + ...item, + [updateKey]: newValue, + } + } + + return item + }) + onPromptVariablesChange?.(newPromptVariables) + } const [currIndex, setCurrIndex] = useState<number>(-1) const currItem = currIndex !== -1 ? promptVariables[currIndex] : null const currItemToEdit: InputVar | null = (() => { @@ -78,6 +102,55 @@ if (payload.type !== InputVarType.select) delete draft[currIndex].options + }) + + onPromptVariablesChange?.(newPromptVariables) + } + const updatePromptKey = (index: number, newKey: string) => { + clearTimeout(conflictTimer) + const { isValid, errorKey, errorMessageKey } = checkKeys([newKey], true) + if (!isValid) { + Toast.notify({ + type: 'error', + message: t(`appDebug.varKeyError.${errorMessageKey}`, { key: errorKey }), + }) + return + } + + const newPromptVariables = promptVariables.map((item, i) => { + if (i === index) { + return { + ...item, + key: newKey, + } + } + return item + }) + + conflictTimer = setTimeout(() => { + const isKeyExists = promptVariables.some(item => item.key?.trim() === newKey.trim()) + if (isKeyExists) { + Toast.notify({ + type: 'error', + message: t('appDebug.varKeyError.keyAlreadyExists', { key: newKey }), + }) + } + }, 1000) + + onPromptVariablesChange?.(newPromptVariables) + } + + const updatePromptNameIfNameEmpty = (index: number, newKey: string) => { + if (!newKey) + return + const newPromptVariables = promptVariables.map((item, i) => { + if (i === index && !item.name) { + return { + ...item, + name: newKey, + } + } + return item }) onPromptVariablesChange?.(newPromptVariables) @@ -200,6 +273,9 @@ return ( <Panel className="mt-2" + headerIcon={ + <VarIcon className='w-4 h-4 text-primary-500' /> + } title={ <div className='flex items-center'> <div className='mr-1'>{t('appDebug.variableTitle')}</div> @@ -215,27 +291,87 @@ </div> } headerRight={!readonly ? <SelectVarType onChange={handleAddVar} /> : null} - noBodySpacing > {!hasVar && ( - <div className='mt-1 px-3 pb-3'> - <div className='pb-1 pt-2 text-xs text-text-tertiary'>{t('appDebug.notSetVar')}</div> - </div> + <div className='pt-2 pb-1 text-xs text-gray-500'>{t('appDebug.notSetVar')}</div> )} {hasVar && ( - <div className='mt-1 px-3 pb-3'> - {promptVariables.map(({ key, name, type, required, config, icon, icon_background }, index) => ( - <VarItem - key={index} - readonly={readonly} - name={key} - label={name} - required={!!required} - type={type} - onEdit={() => handleConfig({ type, key, index, name, config, icon, icon_background })} - onRemove={() => handleRemoveVar(index)} - /> - ))} + <div className='rounded-lg border border-gray-200 bg-white overflow-x-auto'> + <table className={`${s.table} min-w-[440px] w-full max-w-full border-collapse border-0 rounded-lg text-sm`}> + <thead className="border-b border-gray-200 text-gray-500 text-xs font-medium"> + <tr className='uppercase'> + <td>{t('appDebug.variableTable.key')}</td> + <td>{t('appDebug.variableTable.name')}</td> + {!readonly && ( + <> + <td>{t('appDebug.variableTable.optional')}</td> + <td>{t('appDebug.variableTable.action')}</td> + </> + )} + + </tr> + </thead> + <tbody className="text-gray-700"> + {promptVariables.map(({ key, name, type, required, config, icon, icon_background }, index) => ( + <tr key={index} className="h-9 leading-9"> + <td className="w-[160px] border-b border-gray-100 pl-3"> + <div className='flex items-center space-x-1'> + <IconTypeIcon type={type as IInputTypeIconProps['type']} className='text-gray-400' /> + {!readonly + ? ( + <input + type="text" + placeholder="key" + value={key} + onChange={e => updatePromptKey(index, e.target.value)} + onBlur={e => updatePromptNameIfNameEmpty(index, e.target.value)} + maxLength={getMaxVarNameLength(name)} + className="h-6 leading-6 block w-full rounded-md border-0 py-1.5 text-gray-900 placeholder:text-gray-400 focus:outline-none focus:ring-1 focus:ring-inset focus:ring-gray-200" + /> + ) + : ( + <div className='h-6 leading-6 text-[13px] text-gray-700'>{key}</div> + )} + </div> + </td> + <td className="py-1 border-b border-gray-100"> + {!readonly + ? ( + <input + type="text" + placeholder={key} + value={name} + onChange={e => updatePromptVariable(key, 'name', e.target.value)} + maxLength={getMaxVarNameLength(name)} + className="h-6 leading-6 block w-full rounded-md border-0 py-1.5 text-gray-900 placeholder:text-gray-400 focus:outline-none focus:ring-1 focus:ring-inset focus:ring-gray-200" + />) + : ( + <div className='h-6 leading-6 text-[13px] text-gray-700'>{name}</div> + )} + </td> + {!readonly && ( + <> + <td className='w-[84px] border-b border-gray-100'> + <div className='flex items-center h-full'> + <Switch defaultValue={!required} size='md' onChange={value => updatePromptVariable(key, 'required', !value)} /> + </div> + </td> + <td className='w-20 border-b border-gray-100'> + <div className='flex h-full items-center space-x-1'> + <div className=' p-1 rounded-md hover:bg-black/5 w-6 h-6 cursor-pointer' onClick={() => handleConfig({ type, key, index, name, config, icon, icon_background })}> + <Settings01 className='w-4 h-4 text-gray-500' /> + </div> + <div className=' p-1 rounded-md hover:bg-black/5 w-6 h-6 cursor-pointer' onClick={() => handleRemoveVar(index)} > + <RiDeleteBinLine className='w-4 h-4 text-gray-500' /> + </div> + </div> + </td> + </> + )} + </tr> + ))} + </tbody> + </table> </div> )} -- Gitblit v1.8.0