diff --git a/client/packages/lowcoder-design/src/icons/icon-mention-comp.svg b/client/packages/lowcoder-design/src/icons/icon-mention-comp.svg
new file mode 100644
index 000000000..4c04c61e2
--- /dev/null
+++ b/client/packages/lowcoder-design/src/icons/icon-mention-comp.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/client/packages/lowcoder-design/src/icons/index.ts b/client/packages/lowcoder-design/src/icons/index.ts
index 05fd271e7..4ac1b051e 100644
--- a/client/packages/lowcoder-design/src/icons/index.ts
+++ b/client/packages/lowcoder-design/src/icons/index.ts
@@ -289,4 +289,5 @@ export { ReactComponent as CompressIcon } from "icons/icon-compress.svg";
export { ReactComponent as TableCellsIcon } from "icons/icon-table-cells.svg"; // Added By Aqib Mirza
export { ReactComponent as TimeLineIcon } from "icons/icon-timeline-comp.svg"
export { ReactComponent as LottieIcon } from "icons/icon-lottie.svg";
-export { ReactComponent as AutoCompleteCompIcon } from "icons/icon-autocomplete-comp.svg";
\ No newline at end of file
+export { ReactComponent as MentionIcon } from "icons/icon-mention-comp.svg";
+export { ReactComponent as AutoCompleteCompIcon } from "icons/icon-autocomplete-comp.svg";
diff --git a/client/packages/lowcoder/src/comps/comps/textInputComp/mentionComp.tsx b/client/packages/lowcoder/src/comps/comps/textInputComp/mentionComp.tsx
new file mode 100644
index 000000000..9184ce39d
--- /dev/null
+++ b/client/packages/lowcoder/src/comps/comps/textInputComp/mentionComp.tsx
@@ -0,0 +1,268 @@
+import { useState, useEffect } from "react";
+import {
+ NameConfig,
+ NameConfigPlaceHolder,
+ NameConfigRequired,
+ withExposingConfigs,
+} from "comps/generators/withExposing";
+import { Section, sectionNames } from "lowcoder-design";
+import { BoolControl } from "../../controls/boolControl";
+import { AutoHeightControl } from "../../controls/autoHeightControl";
+import { UICompBuilder } from "../../generators";
+import { FormDataPropertyView } from "../formComp/formDataConstants";
+import {
+ checkMentionListData,
+ textInputChildren,
+} from "./textInputConstants";
+import {
+ withMethodExposing,
+ refMethods,
+} from "../../generators/withMethodExposing";
+import { styleControl } from "comps/controls/styleControl";
+import styled from "styled-components";
+import {
+ InputLikeStyle,
+ InputLikeStyleType,
+} from "comps/controls/styleControlConstants";
+import {
+ disabledPropertyView,
+ hiddenPropertyView,
+ maxLengthPropertyView,
+ minLengthPropertyView,
+ readOnlyPropertyView,
+ requiredPropertyView,
+} from "comps/utils/propertyUtils";
+import { booleanExposingStateControl } from "comps/controls/codeStateControl";
+import { trans } from "i18n";
+import { RefControl } from "comps/controls/refControl";
+import { TextAreaRef } from "antd/lib/input/TextArea";
+import { Mentions, ConfigProvider } from "antd";
+import { blurMethod, focusWithOptions } from "comps/utils/methodUtils";
+import type { MentionsOptionProps } from "antd/es/mentions";
+import {
+ textInputValidate,
+} from "../textInputComp/textInputConstants";
+import { jsonControl } from "@lowcoder-ee/comps/controls/codeControl";
+// 事件控制
+import {
+ submitEvent,
+ eventHandlerControl,
+ mentionEvent,
+ focusEvent,
+ blurEvent,
+ changeEvent
+} from "comps/controls/eventHandlerControl";
+
+const Wrapper = styled.div<{
+ $style: InputLikeStyleType;
+}>`
+ height: 100%;
+
+ .ant-input-clear-icon {
+ opacity: 0.45;
+ color: ${(props) => props.$style.text};
+ top: 10px;
+
+ &:hover {
+ opacity: 0.65;
+ color: ${(props) => props.$style.text};
+ }
+ }
+`;
+
+const EventOptions = [
+ focusEvent,
+ blurEvent,
+ changeEvent,
+ mentionEvent,
+ submitEvent,
+] as const;
+
+let MentionTmpComp = (function () {
+ const childrenMap = {
+ ...textInputChildren,
+ viewRef: RefControl,
+ allowClear: BoolControl,
+ autoHeight: AutoHeightControl,
+ style: styleControl(InputLikeStyle),
+ mentionList: jsonControl(checkMentionListData, {
+ "@": ["Li Lei", "Han Meimei"],
+ "#": ["123", "456", "789"],
+ }),
+ onEvent: eventHandlerControl(EventOptions),
+ invalid: booleanExposingStateControl("invalid"),
+ };
+
+ return new UICompBuilder(childrenMap, (props) => {
+ const { mentionList } = props;
+ const [validateState, setvalidateState] = useState({});
+ const [activationFlag, setActivationFlag] = useState(false);
+ const [prefix, setPrefix] = useState("@");
+ type PrefixType = "@" | keyof typeof mentionList;
+
+ // 获取提及搜索关键字
+ const onSearch = (_: string, newPrefix: PrefixType) => {
+ setPrefix(newPrefix);
+ };
+ const onChange = (value: string) => {
+ props.value.onChange(value);
+ props.onEvent("change");
+ };
+
+ const onPressEnter = (e: any) => {
+ if (e.shiftKey) {
+ e.preventDefault();
+ props.onEvent("submit");
+ }
+ };
+
+ const onSelect = (option: MentionsOptionProps) => {
+ props.onEvent("mention");
+ };
+ const getValidate = (value: any): "" | "warning" | "error" | undefined => {
+ if (
+ value.hasOwnProperty("validateStatus") &&
+ value["validateStatus"] === "error"
+ )
+ return "error";
+ return "";
+ };
+
+ const getTextInputValidate = () => {
+ return {
+ value: { value: props.value.value },
+ required: props.required,
+ minLength: props?.minLength ?? 0,
+ maxLength: props?.maxLength ?? 0,
+ validationType: props.validationType,
+ regex: props.regex,
+ customRule: props.customRule,
+ };
+ };
+
+ useEffect(() => {
+ if (activationFlag) {
+ const temp = textInputValidate(getTextInputValidate());
+ setvalidateState(temp);
+ props.invalid.onChange(temp.validateStatus !== "");
+ }
+ }, [
+ props.value.value,
+ props.required,
+ props?.minLength,
+ props?.maxLength,
+ props.validationType,
+ props.regex,
+ props.customRule,
+ ]);
+ return props.label({
+ required: props.required,
+ children: (
+
+
+ {
+ setActivationFlag(true);
+ props.onEvent("focus");
+ }}
+ onBlur={() => props.onEvent("blur")}
+ onPressEnter={onPressEnter}
+ onSearch={onSearch}
+ onChange={onChange}
+ onSelect={onSelect}
+ placeholder={props.placeholder}
+ value={props.value.value}
+ disabled={props.disabled}
+ status={getValidate(validateState)}
+ options={(mentionList[prefix] || []).map((value: string) => ({
+ key: value,
+ value,
+ label: value,
+ }))}
+ autoSize={props.autoHeight}
+ style={{ height: "100%", maxHeight: "100%", resize: "none", padding: props.style.padding }}
+ readOnly={props.readOnly}
+ />
+
+
+ ),
+ style: props.style,
+ ...validateState,
+ });
+ })
+ .setPropertyViewFn((children) => (
+ <>
+
+ {children.mentionList.propertyView({
+ label: trans("mention.mentionList"),
+ })}
+ {children.value.propertyView({ label: trans("prop.defaultValue") })}
+ {children.placeholder.propertyView({
+ label: trans("prop.placeholder"),
+ })}
+
+
+ {children.label.getPropertyView()}
+
+
+ {children.onEvent.getPropertyView()}
+ {disabledPropertyView(children)}
+
+
+
+ {readOnlyPropertyView(children)}
+
+
+
+ {requiredPropertyView(children)}
+ {children.validationType.propertyView({
+ label: trans("prop.textType"),
+ })}
+ {minLengthPropertyView(children)}
+ {maxLengthPropertyView(children)}
+ {children.customRule.propertyView({})}
+
+
+
+ {children.autoHeight.getPropertyView()}
+ {hiddenPropertyView(children)}
+
+
+
+ {children.style.getPropertyView()}
+
+ >
+ ))
+ .build();
+})();
+
+MentionTmpComp = class extends MentionTmpComp {
+ override autoHeight(): boolean {
+ return this.children.autoHeight.getView();
+ }
+};
+
+const TextareaTmp2Comp = withMethodExposing(
+ MentionTmpComp,
+ refMethods([focusWithOptions, blurMethod])
+);
+
+export const MentionComp = withExposingConfigs(TextareaTmp2Comp, [
+ new NameConfig("value", trans("export.inputValueDesc")),
+ NameConfigPlaceHolder,
+ NameConfigRequired,
+ new NameConfig("invalid", trans("export.invalidDesc")),
+ new NameConfig("hidden", trans("export.hiddenDesc")),
+ new NameConfig("disabled", trans("export.disabledDesc")),
+]);
diff --git a/client/packages/lowcoder/src/comps/comps/textInputComp/textInputConstants.tsx b/client/packages/lowcoder/src/comps/comps/textInputComp/textInputConstants.tsx
index 0527c5729..785964593 100644
--- a/client/packages/lowcoder/src/comps/comps/textInputComp/textInputConstants.tsx
+++ b/client/packages/lowcoder/src/comps/comps/textInputComp/textInputConstants.tsx
@@ -1,4 +1,5 @@
import { BoolControl } from "comps/controls/boolControl";
+import { check } from "util/convertUtils";
import {
BoolCodeControl,
CustomRuleControl,
@@ -268,3 +269,14 @@ export const inputRefMethods = [
(comp.children.viewRef.viewRef?.input?.setRangeText as any)?.(...params),
},
];
+
+export function checkMentionListData(data: any) {
+ if(data === "") return {}
+ for(const key in data) {
+ check(data[key], ["array"], key,(node)=>{
+ check(node, ["string"], );
+ return node
+ })
+ }
+ return data
+}
diff --git a/client/packages/lowcoder/src/comps/controls/eventHandlerControl.tsx b/client/packages/lowcoder/src/comps/controls/eventHandlerControl.tsx
index f940759f9..426357c92 100644
--- a/client/packages/lowcoder/src/comps/controls/eventHandlerControl.tsx
+++ b/client/packages/lowcoder/src/comps/controls/eventHandlerControl.tsx
@@ -305,6 +305,11 @@ export const successEvent: EventConfigType = {
value: "success",
description: trans("event.successDesc"),
};
+export const mentionEvent: EventConfigType = {
+ label: trans("event.mention"),
+ value: "mention",
+ description: trans("event.mentionDesc"),
+};
export const InputEventHandlerControl = eventHandlerControl([
changeEvent,
diff --git a/client/packages/lowcoder/src/comps/index.tsx b/client/packages/lowcoder/src/comps/index.tsx
index cbecb0fde..5dc0f2923 100644
--- a/client/packages/lowcoder/src/comps/index.tsx
+++ b/client/packages/lowcoder/src/comps/index.tsx
@@ -94,6 +94,7 @@ import {
VideoCompIcon,
TimeLineIcon,
LottieIcon,
+ MentionIcon,
AutoCompleteCompIcon,
} from "lowcoder-design";
@@ -121,7 +122,8 @@ import { RemoteCompInfo } from "types/remoteComp";
import { ScannerComp } from "./comps/buttonComp/scannerComp";
import { SignatureComp } from "./comps/signatureComp";
import { TimeLineComp } from "./comps/timelineComp/timelineComp";
-import { AutoCompleteComp } from "./comps/autoCompleteComp/autoCompleteComp";
+import { MentionComp } from "./comps/textInputComp/mentionComp";
+import { AutoCompleteComp } from "./comps/autoCompleteComp/autoCompleteComp"
//Added by Aqib Mirza
import { JsonLottieComp } from "./comps/jsonComp/jsonLottieComp";
@@ -857,6 +859,15 @@ const uiCompMap: Registry = {
h: 55,
},
},
+ mention: {
+ name: trans("uiComp.mentionCompName"),
+ enName: "mention",
+ description: trans("uiComp.mentionCompDesc"),
+ categories: ["dataInputText"],
+ icon: MentionIcon,
+ keywords: trans("uiComp.mentionCompKeywords"),
+ comp: MentionComp,
+ },
autocomplete: {
name: trans("uiComp.autoCompleteCompName"),
enName: "autoComplete",
diff --git a/client/packages/lowcoder/src/comps/uiCompRegistry.ts b/client/packages/lowcoder/src/comps/uiCompRegistry.ts
index a2fac91c3..6cd63e920 100644
--- a/client/packages/lowcoder/src/comps/uiCompRegistry.ts
+++ b/client/packages/lowcoder/src/comps/uiCompRegistry.ts
@@ -112,6 +112,7 @@ export type UICompType =
| "signature"
| "jsonLottie" //Added By Aqib Mirza
| "timeline"
+ | "mention"
| "autocomplete"
export const uiCompRegistry = {} as Record;
diff --git a/client/packages/lowcoder/src/i18n/locales/en.ts b/client/packages/lowcoder/src/i18n/locales/en.ts
index c4eb204a7..941a77767 100644
--- a/client/packages/lowcoder/src/i18n/locales/en.ts
+++ b/client/packages/lowcoder/src/i18n/locales/en.ts
@@ -262,6 +262,8 @@ export const en = {
parseDesc: "Triggers on parse",
success: "Success",
successDesc: "Triggers on success",
+ mention: "mention",
+ mentionDesc: "Triggers on mention",
},
themeDetail: {
primary: "Brand color",
@@ -845,6 +847,9 @@ export const en = {
timelineCompName: "Time Line",
timelineCompDesc: "Time Line",
timelineCompKeywords: "",
+ mentionCompName: "mention",
+ mentionCompDesc: "mention",
+ mentionCompKeywords: "",
autoCompleteCompName: "autoComplete",
autoCompleteCompDesc: "autoComplete",
autoCompleteCompKeywords: "",
@@ -2471,6 +2476,9 @@ export const en = {
clickedObjectDesc: "clicked item data",
clickedIndexDesc: "clicked item index",
},
+ mention:{
+ mentionList: "mention list",
+ },
autoComplete: {
value: "auto complete value",
checkedValueFrom: "checked value from",
diff --git a/client/packages/lowcoder/src/i18n/locales/zh.ts b/client/packages/lowcoder/src/i18n/locales/zh.ts
index b7a42e38d..7fb2416d5 100644
--- a/client/packages/lowcoder/src/i18n/locales/zh.ts
+++ b/client/packages/lowcoder/src/i18n/locales/zh.ts
@@ -258,7 +258,9 @@ event: {
parse: "解析",
parseDesc: "在解析时触发",
success: "成功",
- successDesc: "在成功时触发"
+ successDesc: "在成功时触发",
+ mention: "提及",
+ mentionDesc: "在提及时触发",
},
themeDetail: {
primary: "颜色主题",
@@ -828,6 +830,9 @@ uiComp: {
timelineCompName: "时间线",
timelineCompDesc: "时间线组件",
timelineCompKeywords: "sjx",
+ mentionCompName: "提及",
+ mentionCompDesc: "提及组件",
+ mentionCompKeywords: "tj",
autoCompleteCompName: "自动完成",
autoCompleteCompDesc: "自动完成",
autoCompleteCompKeywords: "zdwc",
@@ -2461,6 +2466,9 @@ timeLine: {
endlessLoop: "循环播放",
keepLastFrame: "冻结最后一帧",
},
+ mention:{
+ mentionList: "提及列表",
+ },
autoComplete: {
value: "数据",
checkedValueFrom: "选择提示时获取",
diff --git a/client/packages/lowcoder/src/pages/editor/editorConstants.tsx b/client/packages/lowcoder/src/pages/editor/editorConstants.tsx
index 339a15484..a09a9a56e 100644
--- a/client/packages/lowcoder/src/pages/editor/editorConstants.tsx
+++ b/client/packages/lowcoder/src/pages/editor/editorConstants.tsx
@@ -37,6 +37,7 @@ import {
LeftVideo,
LeftSignature,
TimeLineIcon,
+ MentionIcon,
AutoCompleteCompIcon,
} from "lowcoder-design";
@@ -104,5 +105,6 @@ export const CompStateIcon: {
signature: ,
jsonLottie: , //Added By Aqib Mirza
timeline: ,
+ mention: ,
autocomplete: ,
};