175 lines
7.6 KiB
TypeScript
175 lines
7.6 KiB
TypeScript
import { useEffect, useState } from "react";
|
||
import { useNavigate } from "react-router-dom";
|
||
import { LoadIcon } from "../../../components/bs-icons/loading";
|
||
import { Button } from "../../../components/bs-ui/button";
|
||
import { DialogClose, DialogContent, DialogFooter, DialogHeader, DialogTitle } from "../../../components/bs-ui/dialog";
|
||
import { Input, Textarea } from "../../../components/bs-ui/input";
|
||
import { createAssistantsApi } from "../../../controllers/API/assistant";
|
||
import { captureAndAlertRequestErrorHoc } from "../../../controllers/request";
|
||
import { useTranslation } from "react-i18next";
|
||
import { TitleIconBg } from "@/components/bs-comp/cardComponent";
|
||
import { uploadFileWithProgress, uploadNpcHeaderLibFileWithProgress } from "../../../modals/UploadModal/upload";
|
||
import huifumoren from "../../../assets/npc/huifumoren.png";
|
||
import npcIcon from "../../../assets/npc/npcIcon.png";
|
||
|
||
export default function CreateAssistant() {
|
||
|
||
const { t } = useTranslation()
|
||
|
||
// State for form fields
|
||
const [formData, setFormData] = useState({
|
||
name: '',
|
||
roleAndTasks: `示例:
|
||
示例一
|
||
示例二
|
||
1. XX
|
||
2. XX
|
||
3. …`
|
||
});
|
||
|
||
const [loading, setLoading] = useState(false);
|
||
// State for errors
|
||
const [errors, setErrors] = useState<any>({});
|
||
|
||
// Validate form fields
|
||
const validateField = (name, value) => {
|
||
switch (name) {
|
||
case 'name':
|
||
if (!value) return t('build.nameRequired');
|
||
if (value.length > 50) return t('build.nameMaxLength');
|
||
return '';
|
||
case 'roleAndTasks':
|
||
if (value.length < 20) return t('build.forBetter');
|
||
return '';
|
||
default:
|
||
return '';
|
||
}
|
||
};
|
||
|
||
// Handle field change
|
||
const handleChange = (e) => {
|
||
const { name, value } = e.target;
|
||
const error = validateField(name, value);
|
||
|
||
setFormData(prev => ({ ...prev, [name]: value }));
|
||
setErrors(prev => ({ ...prev, [name]: error }));
|
||
};
|
||
|
||
// Validate entire form
|
||
const validateForm = () => {
|
||
const formErrors = {};
|
||
let isValid = true;
|
||
|
||
Object.keys(formData).forEach(key => {
|
||
const error = validateField(key, formData[key]);
|
||
if (error) {
|
||
formErrors[key] = error;
|
||
isValid = false;
|
||
}
|
||
});
|
||
|
||
setErrors(formErrors);
|
||
return isValid;
|
||
};
|
||
const [avatar_img, setAvatar_img] = useState("")
|
||
const [avatar_color, setAvatar_color] = useState("")
|
||
const randomNum = Math.floor(Math.random()*(4-0+1)+0).toString();
|
||
useEffect(() => {
|
||
if (avatar_color != "") return
|
||
setAvatar_color(randomNum);
|
||
}, [avatar_color]);
|
||
// Handle form submission
|
||
const navigate = useNavigate()
|
||
const handleSubmit = async (e) => {
|
||
e.preventDefault();
|
||
const isValid = validateForm();
|
||
|
||
if (isValid) {
|
||
console.log('Form data:', formData);
|
||
setLoading(true)
|
||
const res = await captureAndAlertRequestErrorHoc(createAssistantsApi(formData.name, formData.roleAndTasks, avatar_img, avatar_color))
|
||
if (res) {
|
||
window.assistantCreate = true // 标记新建助手
|
||
navigate('/assistant/' + res.id)
|
||
}
|
||
setLoading(false)
|
||
}
|
||
};
|
||
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
|
||
// Upload the file
|
||
uploadNpcHeaderLibFileWithProgress(file, (progress) => { }).then(res => {
|
||
setLoading(false);
|
||
setAvatar_img(res);
|
||
})
|
||
};
|
||
// Trigger the file selection dialog
|
||
input.click();
|
||
};
|
||
|
||
return <DialogContent className="sm:max-w-[625px] bg-[#262626]">
|
||
<DialogHeader>
|
||
<DialogTitle className="text-[#fff]">创建NPC</DialogTitle>
|
||
</DialogHeader>
|
||
<div className="flex flex-col gap-8 py-6">
|
||
<div>
|
||
<label htmlFor="name" className="bisheng-label text-[#999999]"><span className="bisheng-tip text-[#FF6060]">* </span>NPC头像</label>
|
||
{/* <TitleIconBg className="w-[40px] h-[40px] min-w-[40px]" img={item.avatar_img} id={item.avatar_color ? item.avatar_color : item.id} ><img src={item.avatar_img ? item.avatar_img : npcIcon} alt="" /></TitleIconBg> */}
|
||
|
||
<div className="flex items-center ml-[7px] mt-[6px]">
|
||
<TitleIconBg className="w-[41px] h-[41px] min-w-[41px]" img={avatar_img} id={avatar_color} ><img onClick={handleButtonClick} src={avatar_img ? avatar_img : npcIcon} alt="" /></TitleIconBg>
|
||
<div className="flex items-center justify-center ml-[20px] w-[95px] h-[27px] bg-[#333333] cursor-pointer" style={{borderRadius:"14px"}} onClick={() => setAvatar_img("")}>
|
||
<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="">
|
||
<label htmlFor="name" className="bisheng-label text-[#999999]"><span className="bisheng-tip text-[#FF6060]">* </span>NPC名称</label>
|
||
<Input id="name" name="name" placeholder="给NPC取一个名字" className="mt-2 npcInput" value={formData.name} onChange={handleChange} />
|
||
{errors.name && <p className="bisheng-tip mt-1 text-[#999999]">名称不可为空</p>}
|
||
</div>
|
||
<div className="">
|
||
<label htmlFor="roleAndTasks" className="bisheng-label text-[#999999]">你希望NPC的角色是什么,具体完成什么任务?</label>
|
||
<Textarea
|
||
id="roleAndTasks"
|
||
name="roleAndTasks"
|
||
placeholder=""
|
||
maxLength={1000}
|
||
className="mt-2 min-h-32 npcInput overflow-auto scrollbar-hide"
|
||
value={formData.roleAndTasks}
|
||
onChange={handleChange}
|
||
/>
|
||
{errors.roleAndTasks && <p className="bisheng-tip mt-1">{errors.roleAndTasks}</p>}
|
||
</div>
|
||
</div>
|
||
{/* <DialogFooter>
|
||
<DialogClose>
|
||
<Button variant="outline" className="px-11 baogao-btn baogao-btn2" type="button" onClick={() => setFormData({ name: '', roleAndTasks: '' })}>取 消</Button>
|
||
</DialogClose>
|
||
<Button disabled={loading} type="submit" className="px-11 baogao-btn baogao-btn2" onClick={handleSubmit}>
|
||
{loading && <LoadIcon className="mr-2" />}
|
||
创 建</Button>
|
||
</DialogFooter> */}
|
||
<div className="flex justify-center ">
|
||
<DialogClose>
|
||
<Button variant="outline" className="px-11 baogao-btn baogao-btn2" type="button" onClick={() => setFormData({ name: '', roleAndTasks: '' })}>取 消</Button>
|
||
</DialogClose>
|
||
<Button disabled={loading} type="submit" className="px-11 baogao-btn baogao-btn2" onClick={handleSubmit}>
|
||
{loading && <LoadIcon className="mr-2" />}
|
||
创 建</Button>
|
||
</div>
|
||
</DialogContent>
|
||
};
|