| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359 |
- import { ArrowLeft, ChevronUp } from "lucide-react";
- import { useContext, useEffect, useMemo, useRef, useState } from "react";
- import { useTranslation } from "react-i18next";
- import { useNavigate, useParams } from "react-router-dom";
- import L2ParameterComponent from "../../CustomNodes/GenericNode/components/parameterComponent/l2Index";
- import ShadTooltip from "../../components/ShadTooltipComponent";
- import { Button } from "../../components/ui/button";
- import { Input } from "../../components/ui/input";
- import { Label } from "../../components/ui/label";
- import { Textarea } from "../../components/ui/textarea";
- import { alertContext } from "../../contexts/alertContext";
- import { TabsContext } from "../../contexts/tabsContext";
- import { userContext } from "../../contexts/userContext";
- import { createCustomFlowApi, getFlowApi } from "../../controllers/API/flow";
- import { useHasForm } from "../../util/hook";
- import FormSet from "./components/FormSet";
- import { captureAndAlertRequestErrorHoc } from "../../controllers/request";
- import shangchuan from "../../assets/npc/shangchuan.png";
- import shou from "../../assets/npc/shou.png";
- import huifumoren from "../../assets/npc/huifumoren.png";
- import nengliIcon from "../../assets/npc/nengliIcon.png";
- import robot from "../../assets/robot.png";
- import { uploadFileWithProgress, uploadNpcHeaderLibFileWithProgress } from "../../modals/UploadModal/upload";
- import { TitleIconBg, gradients } from "@/components/bs-comp/cardComponent";
- export default function l2Edit() {
- const { t } = useTranslation()
- const { id } = useParams()
- const { flow: nextFlow, setFlow, saveFlow } = useContext(TabsContext);
- const { setErrorData, setSuccessData } = useContext(alertContext);
- const flow = useMemo(() => {
- return id ? nextFlow : null
- }, [nextFlow])
- console.log(flow,'flow')
- const [isL2, setIsL2] = useState(false)
- const [loading, setLoading] = useState(false)
- const nameRef = useRef(null)
- const descRef = useRef(null)
- const guideRef = useRef(null)
- const [logo, setLogo] = useState("")
- const randomNum = Math.floor(Math.random()*(4-0+1)+0);
- useEffect(() => {
- // 无id不再请求
- if (!id) return
- // 已有flow 数据时,不再请求
- if (flow?.id === id) {
- setIsL2(true)
- nameRef.current.value = flow.name
- descRef.current.value = flow.description
- guideRef.current.value = flow.guide_word
- setLogo(flow.logo)
- return
- }
- // 无flow从db获取
- getFlowApi(id).then(_flow => {
- // 回填flow
- setFlow('l2 flow init', _flow)
- setIsL2(true)
- nameRef.current.value = _flow.name
- descRef.current.value = _flow.description
- guideRef.current.value = _flow.guide_word
- setLogo(_flow.logo)
- })
- }, [id])
- // 校验
- const { user } = useContext(userContext);
- const [error, setError] = useState({ name: false, desc: false }) // 表单error信息展示
- const isParamError = (name, desc, showErrorConfirm = false) => {
- const errorlist = [];
- if (!name) errorlist.push(t('skills.skillNameRequired'));
- if (name.length > 30) errorlist.push(t('skills.skillNameTooLong'));
- // Duplicate name validation
- const nameErrors = errorlist.length;
- if (!desc) errorlist.push(t('skills.skillDescRequired'));
- if (desc.length > 200) errorlist.push(t('skills.skillDescTooLong'));
- if (errorlist.length && showErrorConfirm) setErrorData({
- title: t('skills.errorTitle'),
- list: errorlist,
- });
- setError({ name: !!nameErrors, desc: errorlist.length > nameErrors });
- return !!errorlist.length;
- }
- const navigate = useNavigate()
- // 创建新技能
- const handleCreateNewSkill = async () => {
- const name = nameRef.current.value
- const guideWords = guideRef.current.value
- const description = descRef.current.value
- const avatar_img = logo
- const avatar_color = gradients[randomNum]
- if (isParamError(name, description, true)) return
- setLoading(true)
- await captureAndAlertRequestErrorHoc(createCustomFlowApi({
- name,
- description,
- guide_word: guideWords,
- avatar_img,
- avatar_color
- }, user.user_name).then(newFlow => {
- setFlow('l2 create flow', newFlow)
- navigate("/flow/" + newFlow.id, { replace: true }); // l3
- }))
- setLoading(false)
- }
- const formRef = useRef(null)
- // 编辑回填参数
- const handleJumpFlow = async () => {
- const name = nameRef.current.value
- const description = descRef.current.value
- const guideWords = guideRef.current.value
- // 高级配置信息有误直接跳转L3
- if (isParamError(name, description)) return navigate('/flow/' + id, { replace: true })
- // 保存在跳
- setLoading(true)
- formRef.current?.save()
- await saveFlow({...flow, name, description, guide_word: guideWords}, true)
- setLoading(false)
- navigate('/flow/' + id, { replace: true })
- }
- const handleSave = async () => {
- const name = nameRef.current.value
- const description = descRef.current.value
- const guideWords = guideRef.current.value
- const avatar_img = logo
- const avatar_color = gradients[randomNum]
- // const logo = flow.logo
- if (isParamError(name, description)) return
- setLoading(true)
- formRef.current?.save()
- console.log(flow,name,description,guideWords,logo)
- await saveFlow({...flow, name, description, guide_word: guideWords, avatar_img, avatar_color}, true)
- setLoading(false)
- setSuccessData({ title: t('success') });
- setTimeout(() => /^\/skill\/[\w\d-]+/.test(location.pathname) && navigate(-1), 2000);
- }
- // 表单收缩
- const showContent = (e) => {
- console.log(e,e.target.tagName)
- const target = e.target.tagName === 'IMG' ? e.target.parentNode : e.target
- const contentDom = target.nextSibling
- console.log(target)
- target.children[1].style.transform = contentDom.clientHeight ? 'rotate(180deg)' : 'rotate(0deg)'
- contentDom.style.display = contentDom.clientHeight ? 'none' : 'block'
- }
- // isForm
- const isForm = useHasForm(flow)
- const handleButtonClick = () => {
- // Create a file input element
- const input = document.createElement("input");
- input.type = "file";
- input.accept = "image/*";
- input.style.display = "none"; // Hidden from view
- input.multiple = false; // Allow only one file selection
-
- input.onchange = (e: Event) => {
- setLoading(true);
-
- // Get the selected file
- const file = (e.target as HTMLInputElement).files?.[0];
-
- // Check if the file type is correct
- // if (file && checkFileType(file.name)) {
- // Upload the file
- uploadNpcHeaderLibFileWithProgress(file, (progress) => { }).then(res => {
- // isSSO ? uploadFileWithProgress(file, (progress) => { }).then(res => {
- setLoading(false);
- if (typeof res === 'string') return setErrorData({ title: "Error", list: [res] })
- const { file_path } = res;
- setLogo(file_path);
- // logo = file_path;
- // setMyValue(file.name);
- // onChange(file.name);
- // sets the value that goes to the backend
- // onFileChange(file_path);
- })
- // uploadFile(file, flow.id)
- // .then((data) => {
- // console.log("File uploaded successfully");
- // // Get the file name from the response
- // const { file_path } = data;
-
- // // Update the state and callback with the name of the file
- // // sets the value to the user
- // setMyValue(file.name);
- // onChange(file.name);
- // // sets the value that goes to the backend
- // onFileChange(file_path);
- // setLoading(false);
- // })
- // .catch(() => {
- // console.error("Error occurred while uploading file");
- // setLoading(false);
- // });
- // } else {
- // // Show an error if the file type is not allowed
- // setErrorData({
- // title:
- // "请选择有效文件。只允许使用这些文件类型:",
- // list: fileTypes,
- // });
- // setLoading(false);
- // }
- };
-
- // Trigger the file selection dialog
- input.click();
- };
- return <div className="relative box-border">
- <div className="p-6 pb-48 h-screen overflow-y-auto">
- <div className="flex justify-between w-full">
- <ShadTooltip content={t('back')} side="right">
- <button className="extra-side-bar-buttons w-[36px]" onClick={() => window.history.length < 3 ? navigate('/skills') : navigate(-1)}>
- <ArrowLeft strokeWidth={1.5} className="side-bar-button-size" />
- </button>
- </ShadTooltip>
- {/* <ShadTooltip content="接口信息" side="left">
- <button className="extra-side-bar-buttons w-[36px]" onClick={() => openPopUp(<ApiModal flow={flows.find((f) => f.id === tabId)} />)} >
- <TerminalSquare strokeWidth={1.5} className="side-bar-button-size " ></TerminalSquare>
- </button>
- </ShadTooltip> */}
- </div>
- {/* form */}
- <div className="pt-6 skillSettings">
- {/* <p className="text-center text-2xl">{t('skills.skillSettings')}</p> */}
- <div className="skillSettingsTitle" onClick={showContent}>
- <p>基础信息</p>
- <img src={shou} alt="" />
- </div>
- <div className="skillSettingsDiv">
- <div className="pt-[20px] pr-[14px] pl-[14px]">
- <p>能力头像</p>
- <div className="flex items-center ml-[7px] mt-[10px]">
- {!logo ? <TitleIconBg className="w-[41px] h-[41px] min-w-[41px]" id={randomNum} ><img onClick={handleButtonClick} src={nengliIcon} alt="" /></TitleIconBg> : <img src={logo} className="w-[41px] h-[41px]" onClick={handleButtonClick} alt="" />}
-
- <div className="flex items-center justify-center ml-[20px] w-[95px] h-[27px] bg-[#333333] cursor-pointer" style={{borderRadius:"14px"}} onClick={() => setLogo(robot)}>
- <img src={huifumoren} className="w-[12px] h-[11px]" alt="" />
- <span className="ml-[5px] text-[#999999] text-[12px] mt-[1px]">恢复默认</span>
- </div>
- </div>
- </div>
- <div className="pt-[20px] pr-[14px] pl-[14px]">
- <p>能力名称</p>
- <Input ref={nameRef} placeholder={t('skills.skillName')} className={`mt-2 ${error.name && 'border-red-400'}`} />
- </div>
- <div className="pt-[20px] pr-[14px] pl-[14px]">
- <p>描述</p>
- <Textarea ref={descRef} id="name" placeholder={t('skills.description')} className={`mt-2 ${error.desc && 'border-red-400'}`} />
- </div>
- <div className="pt-[20px] pr-[14px] pl-[14px] pb-[20px]">
- <p>引导词</p>
- <Textarea ref={guideRef} maxLength={1000} id="name" placeholder={t('skills.guideWords')} className={`mt-2 ${error.desc && 'border-red-400'}`} />
- </div>
- </div>
- {isL2 && <div>
- <div className="skillSettingsTitle mt-[20px]" onClick={showContent}>
- <p>参数信息</p>
- <img src={shou} alt="" />
- </div>
- <div className="skillSettingsDiv skillSettingsInput">
- {flow.data.nodes.map(({ data, id }) => (
- <div key={id} className="w-full">
- <div className="only:hidden mt-6">
- <span className="jiedian">
- {data.node ? (data.node.l2_name || data.node.display_name) : ''}
- </span>
- </div>
- {
- // 自定义组件
- data.node && Object.keys(data.node.template).map(k => (
- data.node.template[k].l2 && <div className="w-full mt-4 px-1" key={k}>
- <Label htmlFor="name" className="text-right ml-[14px]">
- {data.node.template[k].l2_name || data.node.template[k].name}
- </Label>
- <L2ParameterComponent data={data} type={data.node.template[k].type} name={k} />
- </div>
- ))
- }
- </div>
- ))}
- </div>
- </div>}
- {isForm && <FormSet ref={formRef} id={id}></FormSet>}
- {/* <div className="w-[50%] max-w-2xl mx-auto">
- {
- // L2 form
- isL2 && <div className="w-full mt-8">
- <p className="text-center text-gray-400 cursor-pointer flex justify-center" onClick={showContent}>
- {t('skills.parameterInfo')}
- <ChevronUp />
- </p>
- <div className="w-full overflow-hidden transition-all px-1">
- {flow.data.nodes.map(({ data }) => (
- <div key={data.id} className="w-full">
- <div className="only:hidden mt-6">
- <span className="p-2 font-bold text-gray-400 text-base">
- {data.node ? (data.node.l2_name || data.node.display_name) : ''}
- </span>
- </div>
- {
- // 自定义组件
- data.node && Object.keys(data.node.template).map(k => (
- data.node.template[k].l2 && <div className="w-full mt-4 px-1" key={k}>
- <Label htmlFor="name" className="text-right">
- {data.node.template[k].l2_name || data.node.template[k].name}
- </Label>
- <L2ParameterComponent data={data} type={data.node.template[k].type} name={k} />
- </div>
- ))
- }
- </div>
- ))}
- </div>
- </div>
- } */}
- {/* 表单设置 */}
- {/* {isForm && <FormSet ref={formRef} id={id}></FormSet>}
- </div> */}
- </div>
- {/* footer */}
- <div className="skillSettingsBtn">
- {/* <div className="absolute flex bottom-0 w-full py-8 justify-center bg-[#fff] border-t dark:bg-gray-900"> */}
- {
- isL2 ?
- <div className="flex gap-4">
- <Button disabled={loading} className="skillSettingsBtn1 w-[200px] h-[27px] rounded-full" onClick={handleSave}>
- {t('save')}
- </Button>
- <Button disabled={loading} className="skillSettingsBtn2 w-[200px] h-[27px] rounded-full ml-[40px]" variant="outline" onClick={() => handleJumpFlow()}>
- {t('skills.advancedConfiguration')}
- </Button>
- </div>
- :
- <div className="flex justify-center">
- <Button disabled={loading} className="skillSettingsBtn1 extra-side-bar-save-disable w-[200px] h-[27px] rounded-full" onClick={handleCreateNewSkill}>
- {t('skills.nextStep')}
- </Button>
- </div>
- }
- </div>
- </div>
- </div>
- };
|