index.tsx 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. import { useAjax } from "@/Hook/useAjax"
  2. import { txtLength } from "@/utils/utils";
  3. import { Button, Input, InputRef, List, Popover } from "antd";
  4. import React, { useEffect, useRef } from "react"
  5. import { useState } from "react";
  6. import './index.less'
  7. import { getTextApi } from "@/services/adqV3/global";
  8. import { SmileOutlined } from "@ant-design/icons";
  9. import { emojiList } from "./const";
  10. interface Props {
  11. value?: any,
  12. onChange?: (value: any) => void;
  13. style?: React.CSSProperties;
  14. placeholder?: string;
  15. maxTextLength?: number;
  16. isShowAjax?: boolean
  17. isSelectEmoji?: boolean
  18. }
  19. /**
  20. * 文案助手输入框
  21. * @param props
  22. * @returns
  23. */
  24. const TextAideInput: React.FC<Props> = (props) => {
  25. /************************/
  26. const { value, onChange, style, placeholder, maxTextLength = 10, isShowAjax = true, isSelectEmoji = true } = props
  27. const [text, setText] = useState<any>(value)
  28. const [descriptionShow, setDescriptionshow] = useState(false)
  29. const [cursorPosition, setCursorPosition] = useState<number | null>(null);
  30. const inputRef = useRef<InputRef>(null); // 获取 input DOM 元素
  31. const [emojiOpen, setEmojiOpen] = useState<boolean>(false)
  32. const getTextLsit = useAjax((params) => getTextApi(params))
  33. /************************/
  34. useEffect(() => {
  35. setText(value)
  36. }, [value])
  37. // 文案助手
  38. const textList = (keyword?: any) => {
  39. getTextLsit.run({ keyword, maxTextLength })
  40. }
  41. const insertTextAtCursor = (emoji: string) => {
  42. const inputElement = inputRef.current;
  43. if (inputElement) {
  44. if (cursorPosition !== null) {
  45. const newValue = text.slice(0, cursorPosition) + emoji + text.slice(cursorPosition);
  46. setText(newValue);
  47. onChange?.(newValue)
  48. } else {
  49. const newValue = text + emoji
  50. setText(newValue);
  51. onChange?.(newValue)
  52. }
  53. const newCursorPosition = (cursorPosition || text.length) + emoji.length;
  54. setCursorPosition(newCursorPosition);
  55. // 等待状态更新后再设置光标位置
  56. setTimeout(() => {
  57. inputElement.setSelectionRange(newCursorPosition, newCursorPosition);
  58. inputElement.focus();
  59. }, 0);
  60. }
  61. };
  62. return <div style={{ display: 'inline-flex', alignItems: 'center', columnGap: 5 }}>
  63. <Popover
  64. placement="topLeft"
  65. overlayClassName="textAideInputPopover"
  66. style={{ minWidth: 600 }}
  67. open={descriptionShow}
  68. content={<List
  69. loading={getTextLsit?.loading}
  70. size="small"
  71. style={{ maxHeight: 300, overflowX: 'auto', minWidth: 600 }}
  72. dataSource={getTextLsit?.data?.returnTexts}
  73. renderItem={(item: any) => <List.Item onClick={() => {
  74. setText(item.text)
  75. onChange && onChange(item.text)
  76. setTimeout(() => { setDescriptionshow(false) }, 50)
  77. }}><span >{item.text}{item.tag && <span className="crt">{'CTR 高'}</span>}</span></List.Item>}
  78. />}
  79. >
  80. <Input.Group compact>
  81. <Input
  82. ref={inputRef}
  83. placeholder={placeholder}
  84. style={style}
  85. value={text}
  86. onFocus={() => {
  87. if (isShowAjax) {
  88. setDescriptionshow(true)
  89. textList(value)
  90. }
  91. }}
  92. onBlur={(e) => {
  93. setCursorPosition(e.target.selectionStart)
  94. if (!emojiOpen) {
  95. setTimeout(() => { setDescriptionshow(false) }, 0)
  96. }
  97. }}
  98. onChange={(e) => {
  99. let value = e.target.value
  100. setText(value)
  101. if (isShowAjax) {
  102. textList(value)
  103. }
  104. onChange && onChange(value)
  105. }}
  106. allowClear
  107. />
  108. {isSelectEmoji && <Popover
  109. placement="bottomRight"
  110. overlayClassName="emoji"
  111. content={<div className="emoji-list-scroll">
  112. <ul className="emoji-list">{emojiList.map(item => <li key={item.text} onClick={() => insertTextAtCursor(item.text)}><img src={item.url} alt={item.text} /></li>)}</ul>
  113. </div>}
  114. trigger="click"
  115. onOpenChange={(e) => {
  116. setEmojiOpen(e)
  117. }}
  118. >
  119. <Button><SmileOutlined /></Button>
  120. </Popover>}
  121. </Input.Group>
  122. </Popover>
  123. <span>{`${txtLength(text)}/${maxTextLength}`}</span>
  124. </div>
  125. }
  126. export default React.memo(TextAideInput)