react+antd Input回车输入生成tag组件

EditableTagGroup.tsx:

javascript 复制代码
import React, { useState, useRef, useEffect } from 'react'
import { Input, Tag, Tooltip, message } from 'antd'
import { CloseCircleFilled } from '@ant-design/icons'
import isEqual from 'lodash/isEqual'
import { useTranslation } from 'react-i18next'
import './index.scss'

interface ITagProps {
  value?: string[]
  onChange?: (value: string[]) => void
}

const EditableTagGroup: React.FC<ITagProps> = ({ value = [], onChange }: any) => {
  const [tags, setTags] = useState<any>(value)
  const [inputValue, setInputValue] = useState<string>('')
  const inputRef = useRef<any>(null)
  const { t } = useTranslation()

  // Sync props.value to state when it changes
  useEffect(() => {
    if (!isEqual(value, tags)) {
      setTags(value)
    }
  }, [value])

  // Notify parent component when tags change
  // useEffect(() => {
  //   if (onChange && !isEqual(value, tags)) {
  //     console.log(value, tags)
  //     onChange(tags)
  //   }
  // }, [tags])

  const handleClose = (removedTag: any) => {
    const newTags = tags.filter((tag: any) => tag.id !== removedTag.id)
    setTags(newTags)
    onChange(newTags)
  }

  const inputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    setInputValue(e.target.value)
  }

  const isValidEntityToken = (t: string): boolean => {
    const re = /^(?:\p{Script=Han}|[A-Za-z])+$/u
    return t.length >= 1 && t.length <= 64 && re.test(t)
  }

  const addTag = (e: React.KeyboardEvent<HTMLInputElement>) => {
    const value = (e.target as HTMLInputElement).value
    if (value) {
      if (isValidEntityToken(value)) {
        let now = Date.now()
        const newTags = [...tags, { id: now, text: value }]
        setTags(newTags)
        setInputValue('')
        onChange(newTags)
      } else {
        message.info(t('models.entityTypesWarning'))
      }
    }
  }

  const clickEdit = () => {
    inputRef.current?.focus()
  }

  const renderTags = () => {
    return (
      <>
        {tags?.map((tag: any, index: any) => {
          const isLongTag = tag.text.length > 20
          const tagElem = (
            <Tag key={tag.id} onClose={() => handleClose(tag)} closeIcon={<CloseCircleFilled className="close-icon" />}>
              {isLongTag ? `${tag.text.slice(0, 20)}...` : tag.text}
            </Tag>
          )

          return isLongTag ? (
            <Tooltip title={tag.text} key={tag.id}>
              {tagElem}
            </Tooltip>
          ) : (
            tagElem
          )
        })}
      </>
    )
  }

  return (
    <div className="editable" onClick={clickEdit}>
      <div className="show">{renderTags()}</div>
      <Input
        className="input"
        ref={inputRef}
        placeholder={t('models.entityTypesPlaceholder')}
        onPressEnter={addTag}
        value={inputValue}
        onChange={inputChange}
      />
    </div>
  )
}

export default EditableTagGroup

index.scss:

css 复制代码
.editable {
  height: 100px;
  border-radius: 2px;
  background-color: rgba(255, 255, 255, 1);
  border: 1px solid #d9d9d9;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  &:focus-within,
  &:hover {
    border-color: rgba(22, 119, 254, 1);
    box-shadow: 0 0 4px 0 rgba(10, 42, 97, 0.2);
  }
  .ant-input {
    border: none;
    height: 32px;
    &:hover,
    &:focus {
      box-shadow: none;
      border: none;
    }
  }
  .show {
    flex: 1;
    min-height: '47px';
    overflow: auto;
    padding: 5px 5px;
    .ant-tag {
      color: #5a5e66;
      padding: 2px 8px;
      margin: 0 4px 4px 0;
      font-size: 12px;
      border: none;
      border-radius: 3px;
      background-color: rgba(240, 242, 245, 1);
      .close-icon {
        color: rgba(192, 196, 204, 1);
        margin-left: 2px;
        font-size: 12px;
      }
    }
  }
}

参考链接:

https://blog.csdn.net/qq_49464155/article/details/123247090

相关推荐
Deng9452013146 分钟前
Vue + Flask 前后端分离项目实战:从零搭建一个完整博客系统
前端·vue.js·flask
威迪斯特9 分钟前
Flask:轻量级Web框架的技术本质与工程实践
前端·数据库·后端·python·flask·开发框架·核心架构
wuhen_n35 分钟前
JavaScript内置数据结构
开发语言·前端·javascript·数据结构
大鱼前端37 分钟前
为什么我说CSS-in-JS是前端“最佳”的糟粕设计?
前端
不爱吃糖的程序媛39 分钟前
Capacitor:跨平台Web原生应用开发利器,现已全面适配鸿蒙
前端·华为·harmonyos
AC赳赳老秦42 分钟前
2026国产算力新周期:DeepSeek实战适配英伟达H200,引领大模型训练效率跃升
大数据·前端·人工智能·算法·tidb·memcache·deepseek
CHU72903543 分钟前
淘宝扭蛋机抽盒小程序前端功能解析:解锁趣味抽盒新体验
前端·小程序
-凌凌漆-1 小时前
【npm】npm的-D选项介绍
前端·npm·node.js
鹿心肺语1 小时前
前端HTML转PDF的两种主流方案深度解析
前端·javascript
海石2 小时前
去到比北方更北的地方—2025年终总结
前端·ai编程·年终总结