feat: 修改模型数据可修改问题

This commit is contained in:
archer
2023-04-04 13:15:34 +08:00
parent becee69d6a
commit e08e8aa00b
5 changed files with 100 additions and 71 deletions

View File

@@ -57,7 +57,7 @@ export const postModelDataJsonData = (
jsonData: { prompt: string; completion: string; vector?: number[] }[] jsonData: { prompt: string; completion: string; vector?: number[] }[]
) => POST(`/model/data/pushModelDataJson`, { modelId, data: jsonData }); ) => POST(`/model/data/pushModelDataJson`, { modelId, data: jsonData });
export const putModelDataById = (data: { dataId: string; text: string }) => export const putModelDataById = (data: { dataId: string; text: string; q?: string }) =>
PUT('/model/data/putModelData', data); PUT('/model/data/putModelData', data);
export const delOneModelData = (dataId: string) => export const delOneModelData = (dataId: string) =>
DELETE(`/model/data/delModelDataById?dataId=${dataId}`); DELETE(`/model/data/delModelDataById?dataId=${dataId}`);

View File

@@ -65,27 +65,23 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
messages: [ messages: [
{ {
role: 'system', role: 'system',
content: `服务端逻辑生成器。根据用户输入的需求,拆解成代码实现的步骤,并按下面格式返回: content: `服务端逻辑生成器。根据用户输入的需求,拆解成代码实现的步骤,并按格式返回: 1.\n2.\n3.\n ......
1.
2.
3.
....
下面是一些例子: 下面是一些例子:
实现一个手机号注册账号的方法 实现一个手机号注册账号的方法,包含两个函数
发送手机验证码函数: * 发送手机验证码函数:
1. 从 query 中获取 phone 1. 从 query 中获取 phone
2. 校验手机号格式是否正确,不正确返回{error: "手机号格式错误"} 2. 校验手机号格式是否正确,不正确返回{error: "手机号格式错误"}
3. 给 phone 发送一个短信验证码验证码长度为6位字符串内容为你正在注册laf, 验证码为code 3. 给 phone 发送一个短信验证码验证码长度为6位字符串内容为你正在注册laf, 验证码为code
4. 数据库添加数据,表为"codes",内容为 {phone, code} 4. 数据库添加数据,表为"codes",内容为 {phone, code}
注册函数 * 注册函数
1. 从 body 中获取 phone 和 code 1. 从 body 中获取 phone 和 code
2. 校验手机号格式是否正确,不正确返回{error: "手机号格式错误"} 2. 校验手机号格式是否正确,不正确返回{error: "手机号格式错误"}
2. 获取数据库数据,表为"codes",查找是否有符合 phone, code 等于body参数的记录没有的话返回 {error:"验证码不正确"} 2. 获取数据库数据,表为"codes",查找是否有符合 phone, code 等于body参数的记录没有的话返回 {error:"验证码不正确"}
4. 添加数据库数据,表为"users" ,内容为{phone, code, createTime} 4. 添加数据库数据,表为"users" ,内容为{phone, code, createTime}
5. 删除数据库数据,删除 code 记录 5. 删除数据库数据,删除 code 记录
--------------- ---------------
更新客记录。传入blogIdblogTexttags还需要记录更新的时间 更新客记录。传入blogIdblogTexttags还需要记录更新的时间
1. 从 body 中获取 blogIdblogText 和 tags 1. 从 body 中获取 blogIdblogText 和 tags
2. 校验 blogId 是否为空,为空则返回 {error: "博客ID不能为空"} 2. 校验 blogId 是否为空,为空则返回 {error: "博客ID不能为空"}
3. 校验 blogText 是否为空,为空则返回 {error: "博客内容不能为空"} 3. 校验 blogText 是否为空,为空则返回 {error: "博客内容不能为空"}

View File

@@ -2,13 +2,12 @@ import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@/service/response'; import { jsonRes } from '@/service/response';
import { authToken } from '@/service/utils/tools'; import { authToken } from '@/service/utils/tools';
import { connectRedis } from '@/service/redis'; import { connectRedis } from '@/service/redis';
import { ModelDataStatusEnum } from '@/constants/redis';
import { generateVector } from '@/service/events/generateVector';
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) { export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
try { try {
let { dataId, text } = req.body as { const { dataId, text, q } = req.body as { dataId: string; text: string; q?: string };
dataId: string;
text: string;
};
const { authorization } = req.headers; const { authorization } = req.headers;
if (!authorization) { if (!authorization) {
@@ -31,7 +30,17 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
} }
// 更新 // 更新
await redis.hSet(dataId, 'text', text); await redis.sendCommand([
'HMSET',
dataId,
...(q ? ['q', q, 'status', ModelDataStatusEnum.waiting] : []),
'text',
text
]);
if (q) {
generateVector();
}
jsonRes(res); jsonRes(res);
} catch (err) { } catch (err) {

View File

@@ -1,7 +1,6 @@
import React, { useState, useCallback } from 'react'; import React, { useState, useCallback } from 'react';
import { import {
Box, Box,
IconButton,
Flex, Flex,
Button, Button,
Modal, Modal,
@@ -9,37 +8,40 @@ import {
ModalContent, ModalContent,
ModalHeader, ModalHeader,
ModalCloseButton, ModalCloseButton,
Input,
Textarea Textarea
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import { useForm, useFieldArray } from 'react-hook-form'; import { useForm } from 'react-hook-form';
import { postModelDataInput } from '@/api/model'; import { postModelDataInput, putModelDataById } from '@/api/model';
import { useToast } from '@/hooks/useToast'; import { useToast } from '@/hooks/useToast';
import { DeleteIcon } from '@chakra-ui/icons';
import { customAlphabet } from 'nanoid'; import { customAlphabet } from 'nanoid';
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 12); const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 12);
type FormData = { text: string; q: string }; export type FormData = { dataId?: string; text: string; q: string };
const InputDataModal = ({ const InputDataModal = ({
onClose, onClose,
onSuccess, onSuccess,
modelId modelId,
defaultValues = {
text: '',
q: ''
}
}: { }: {
onClose: () => void; onClose: () => void;
onSuccess: () => void; onSuccess: () => void;
modelId: string; modelId: string;
defaultValues?: FormData;
}) => { }) => {
const [importing, setImporting] = useState(false); const [importing, setImporting] = useState(false);
const { toast } = useToast(); const { toast } = useToast();
const { register, handleSubmit, control } = useForm<FormData>({ const { register, handleSubmit } = useForm<FormData>({
defaultValues: { defaultValues
text: '',
q: ''
}
}); });
/**
* 确认导入新数据
*/
const sureImportData = useCallback( const sureImportData = useCallback(
async (e: FormData) => { async (e: FormData) => {
setImporting(true); setImporting(true);
@@ -72,6 +74,26 @@ const InputDataModal = ({
[modelId, onClose, onSuccess, toast] [modelId, onClose, onSuccess, toast]
); );
const updateData = useCallback(
async (e: FormData) => {
if (!e.dataId) return;
if (e.text === defaultValues.text && e.q === defaultValues.q) return;
await putModelDataById({
dataId: e.dataId,
text: e.text,
q: e.q === defaultValues.q ? '' : e.q
});
toast({
title: '修改回答成功',
status: 'success'
});
onClose();
onSuccess();
},
[defaultValues.q, onClose, onSuccess, toast]
);
return ( return (
<Modal isOpen={true} onClose={onClose} isCentered> <Modal isOpen={true} onClose={onClose} isCentered>
<ModalOverlay /> <ModalOverlay />
@@ -125,7 +147,10 @@ const InputDataModal = ({
<Button variant={'outline'} mr={3} onClick={onClose}> <Button variant={'outline'} mr={3} onClick={onClose}>
</Button> </Button>
<Button isLoading={importing} onClick={handleSubmit(sureImportData)}> <Button
isLoading={importing}
onClick={handleSubmit(defaultValues.dataId ? updateData : sureImportData)}
>
</Button> </Button>
</Flex> </Flex>

View File

@@ -1,4 +1,4 @@
import React, { useCallback } from 'react'; import React, { useCallback, useState } from 'react';
import { import {
Box, Box,
TableContainer, TableContainer,
@@ -12,7 +12,6 @@ import {
Flex, Flex,
Button, Button,
useDisclosure, useDisclosure,
Textarea,
Menu, Menu,
MenuButton, MenuButton,
MenuList, MenuList,
@@ -25,22 +24,21 @@ import { usePagination } from '@/hooks/usePagination';
import { import {
getModelDataList, getModelDataList,
delOneModelData, delOneModelData,
putModelDataById,
getModelSplitDataList, getModelSplitDataList,
getExportDataList getExportDataList
} from '@/api/model'; } from '@/api/model';
import { DeleteIcon, RepeatIcon } from '@chakra-ui/icons'; import { DeleteIcon, RepeatIcon, EditIcon } from '@chakra-ui/icons';
import { useToast } from '@/hooks/useToast'; import { useToast } from '@/hooks/useToast';
import { useLoading } from '@/hooks/useLoading'; import { useLoading } from '@/hooks/useLoading';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import { useMutation, useQuery } from '@tanstack/react-query'; import { useMutation, useQuery } from '@tanstack/react-query';
import type { FormData as InputDataType } from './InputDataModal';
const InputModel = dynamic(() => import('./InputDataModal')); const InputModel = dynamic(() => import('./InputDataModal'));
const SelectFileModel = dynamic(() => import('./SelectFileModal')); const SelectFileModel = dynamic(() => import('./SelectFileModal'));
const SelectJsonModel = dynamic(() => import('./SelectJsonModal')); const SelectJsonModel = dynamic(() => import('./SelectJsonModal'));
const ModelDataCard = ({ model }: { model: ModelSchema }) => { const ModelDataCard = ({ model }: { model: ModelSchema }) => {
const { toast } = useToast();
const { Loading } = useLoading(); const { Loading } = useLoading();
const { const {
@@ -58,25 +56,8 @@ const ModelDataCard = ({ model }: { model: ModelSchema }) => {
} }
}); });
const updateAnswer = useCallback( const [editInputData, setEditInputData] = useState<InputDataType>();
async (dataId: string, text: string) => {
await putModelDataById({
dataId,
text
});
toast({
title: '修改回答成功',
status: 'success'
});
},
[toast]
);
const {
isOpen: isOpenInputModal,
onOpen: onOpenInputModal,
onClose: onCloseInputModal
} = useDisclosure();
const { const {
isOpen: isOpenSelectFileModal, isOpen: isOpenSelectFileModal,
onOpen: onOpenSelectFileModal, onOpen: onOpenSelectFileModal,
@@ -151,7 +132,16 @@ const ModelDataCard = ({ model }: { model: ModelSchema }) => {
</MenuButton> </MenuButton>
<MenuList> <MenuList>
<MenuItem onClick={onOpenInputModal}></MenuItem> <MenuItem
onClick={() =>
setEditInputData({
text: '',
q: ''
})
}
>
</MenuItem>
<MenuItem onClick={onOpenSelectFileModal}></MenuItem> <MenuItem onClick={onOpenSelectFileModal}></MenuItem>
<MenuItem onClick={onOpenSelectJsonModal}>JSON导入</MenuItem> <MenuItem onClick={onOpenSelectJsonModal}>JSON导入</MenuItem>
</MenuList> </MenuList>
@@ -170,34 +160,38 @@ const ModelDataCard = ({ model }: { model: ModelSchema }) => {
<Th>Question</Th> <Th>Question</Th>
<Th>Text</Th> <Th>Text</Th>
<Th>Status</Th> <Th>Status</Th>
<Th></Th> <Th></Th>
</Tr> </Tr>
</Thead> </Thead>
<Tbody> <Tbody>
{modelDataList.map((item) => ( {modelDataList.map((item) => (
<Tr key={item.id}> <Tr key={item.id}>
<Td w={'350px'}> <Td>
<Box fontSize={'xs'} w={'100%'} whiteSpace={'pre-wrap'} _notLast={{ mb: 1 }}> <Box fontSize={'xs'} w={'100%'} whiteSpace={'pre-wrap'}>
{item.q} {item.q}
</Box> </Box>
</Td> </Td>
<Td minW={'200px'}> <Td minW={'200px'}>
<Textarea <Box w={'100%'} fontSize={'xs'} whiteSpace={'pre-wrap'}>
w={'100%'} {item.text}
h={'100%'} </Box>
defaultValue={item.text}
fontSize={'xs'}
resize={'both'}
onBlur={(e) => {
const oldVal = modelDataList.find((data) => item.id === data.id)?.text;
if (oldVal !== e.target.value) {
updateAnswer(item.id, e.target.value);
}
}}
></Textarea>
</Td> </Td>
<Td w={'100px'}>{ModelDataStatusMap[item.status]}</Td> <Td>{ModelDataStatusMap[item.status]}</Td>
<Td> <Td>
<IconButton
mr={5}
icon={<EditIcon />}
variant={'outline'}
aria-label={'delete'}
size={'sm'}
onClick={() =>
setEditInputData({
dataId: item.id,
q: item.q,
text: item.text
})
}
/>
<IconButton <IconButton
icon={<DeleteIcon />} icon={<DeleteIcon />}
variant={'outline'} variant={'outline'}
@@ -221,8 +215,13 @@ const ModelDataCard = ({ model }: { model: ModelSchema }) => {
</Box> </Box>
<Loading loading={isLoading} fixed={false} /> <Loading loading={isLoading} fixed={false} />
{isOpenInputModal && ( {editInputData !== undefined && (
<InputModel modelId={model._id} onClose={onCloseInputModal} onSuccess={refetchData} /> <InputModel
modelId={model._id}
defaultValues={editInputData}
onClose={() => setEditInputData(undefined)}
onSuccess={refetchData}
/>
)} )}
{isOpenSelectFileModal && ( {isOpenSelectFileModal && (
<SelectFileModel <SelectFileModel