221 lines
7.0 KiB
JavaScript
221 lines
7.0 KiB
JavaScript
|
|
'use client';
|
|||
|
|
|
|||
|
|
import { useState, useEffect, useMemo } from 'react';
|
|||
|
|
import { useTranslation } from 'react-i18next';
|
|||
|
|
import {
|
|||
|
|
Dialog,
|
|||
|
|
DialogTitle,
|
|||
|
|
DialogContent,
|
|||
|
|
DialogActions,
|
|||
|
|
TextField,
|
|||
|
|
Button,
|
|||
|
|
Box,
|
|||
|
|
Autocomplete,
|
|||
|
|
TextField as MuiTextField,
|
|||
|
|
FormControl,
|
|||
|
|
InputLabel,
|
|||
|
|
Select,
|
|||
|
|
MenuItem
|
|||
|
|
} from '@mui/material';
|
|||
|
|
import axios from 'axios';
|
|||
|
|
|
|||
|
|
export default function QuestionEditDialog({
|
|||
|
|
open,
|
|||
|
|
onClose,
|
|||
|
|
onSubmit,
|
|||
|
|
initialData,
|
|||
|
|
projectId,
|
|||
|
|
tags,
|
|||
|
|
mode = 'create' // 'create' or 'edit'
|
|||
|
|
}) {
|
|||
|
|
const [chunks, setChunks] = useState([]);
|
|||
|
|
const [images, setImages] = useState([]);
|
|||
|
|
const { t } = useTranslation();
|
|||
|
|
|
|||
|
|
// 获取文本块的标题
|
|||
|
|
const getChunkTitle = chunkId => {
|
|||
|
|
const chunk = chunks.find(c => c.id === chunkId);
|
|||
|
|
return chunk?.name || chunkId; // 直接使用文件名
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const [formData, setFormData] = useState({
|
|||
|
|
id: '',
|
|||
|
|
question: '',
|
|||
|
|
sourceType: 'text', // 新增:数据源类型
|
|||
|
|
chunkId: '',
|
|||
|
|
imageId: '', // 新增:图片ID
|
|||
|
|
label: '' // 默认不选中任何标签
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
const getChunks = async projectId => {
|
|||
|
|
// 获取文本块列表
|
|||
|
|
const response = await axios.get(`/api/projects/${projectId}/split`);
|
|||
|
|
if (response.status !== 200) {
|
|||
|
|
throw new Error(t('common.fetchError'));
|
|||
|
|
}
|
|||
|
|
setChunks(response.data.chunks || []);
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const getImages = async projectId => {
|
|||
|
|
// 获取图片列表(只获取ID和名称)
|
|||
|
|
try {
|
|||
|
|
const response = await axios.get(`/api/projects/${projectId}/images?page=1&pageSize=10000&simple=true`);
|
|||
|
|
if (response.status === 200) {
|
|||
|
|
setImages(response.data.data || []);
|
|||
|
|
}
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('Failed to fetch images:', error);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
useEffect(() => {
|
|||
|
|
getChunks(projectId);
|
|||
|
|
getImages(projectId);
|
|||
|
|
if (initialData) {
|
|||
|
|
// 根据 imageId 判断数据源类型
|
|||
|
|
console.log('initialData:', initialData);
|
|||
|
|
const sourceType = initialData.imageId ? 'image' : 'text';
|
|||
|
|
setFormData({
|
|||
|
|
id: initialData.id,
|
|||
|
|
question: initialData.question || '',
|
|||
|
|
sourceType: sourceType,
|
|||
|
|
chunkId: initialData.chunkId || '',
|
|||
|
|
imageId: initialData.imageId || '',
|
|||
|
|
label: initialData.label || 'other' // 改用 label 而不是 label
|
|||
|
|
});
|
|||
|
|
} else {
|
|||
|
|
setFormData({
|
|||
|
|
id: '',
|
|||
|
|
question: '',
|
|||
|
|
sourceType: 'text',
|
|||
|
|
chunkId: '',
|
|||
|
|
imageId: '',
|
|||
|
|
label: ''
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
}, [initialData]);
|
|||
|
|
|
|||
|
|
const handleSubmit = () => {
|
|||
|
|
onSubmit(formData);
|
|||
|
|
onClose();
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const flattenTags = (tags = [], prefix = '') => {
|
|||
|
|
let flatTags = [];
|
|||
|
|
const traverse = node => {
|
|||
|
|
flatTags.push({
|
|||
|
|
id: node.label, // 使用标签名作为 id
|
|||
|
|
label: node.label, // 直接使用原始标签名
|
|||
|
|
originalLabel: node.label
|
|||
|
|
});
|
|||
|
|
if (node.child && node.child.length > 0) {
|
|||
|
|
node.child.forEach(child => traverse(child));
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
tags.forEach(tag => traverse(tag));
|
|||
|
|
flatTags.push({
|
|||
|
|
id: 'other',
|
|||
|
|
label: t('datasets.uncategorized'),
|
|||
|
|
originalLabel: 'other'
|
|||
|
|
});
|
|||
|
|
return flatTags;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const flattenedTags = useMemo(() => flattenTags(tags), [tags, t]);
|
|||
|
|
|
|||
|
|
return (
|
|||
|
|
<Dialog open={open} onClose={onClose} maxWidth="sm" fullWidth>
|
|||
|
|
<DialogTitle>{mode === 'create' ? t('questions.createQuestion') : t('questions.editQuestion')}</DialogTitle>
|
|||
|
|
<DialogContent>
|
|||
|
|
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2, mt: 2 }}>
|
|||
|
|
{/* 数据源类型选择 */}
|
|||
|
|
<FormControl fullWidth>
|
|||
|
|
<InputLabel>{t('questions.sourceType', { defaultValue: '数据源类型' })}</InputLabel>
|
|||
|
|
<Select
|
|||
|
|
value={formData.sourceType}
|
|||
|
|
label={t('questions.sourceType', { defaultValue: '数据源类型' })}
|
|||
|
|
onChange={e => {
|
|||
|
|
setFormData({
|
|||
|
|
...formData,
|
|||
|
|
sourceType: e.target.value,
|
|||
|
|
chunkId: '',
|
|||
|
|
imageId: ''
|
|||
|
|
});
|
|||
|
|
}}
|
|||
|
|
>
|
|||
|
|
<MenuItem value="text">{t('questions.template.sourceType.text')}</MenuItem>
|
|||
|
|
<MenuItem value="image">{t('questions.template.sourceType.image')}</MenuItem>
|
|||
|
|
</Select>
|
|||
|
|
</FormControl>
|
|||
|
|
|
|||
|
|
{/* 问题内容 */}
|
|||
|
|
<TextField
|
|||
|
|
label={t('questions.questionContent')}
|
|||
|
|
multiline
|
|||
|
|
rows={4}
|
|||
|
|
fullWidth
|
|||
|
|
value={formData.question}
|
|||
|
|
onChange={e => setFormData({ ...formData, question: e.target.value })}
|
|||
|
|
/>
|
|||
|
|
|
|||
|
|
{/* 文本块选择(仅当数据源为文本时显示) */}
|
|||
|
|
{formData.sourceType === 'text' && (
|
|||
|
|
<Autocomplete
|
|||
|
|
fullWidth
|
|||
|
|
options={chunks}
|
|||
|
|
getOptionLabel={chunk => getChunkTitle(chunk.id)}
|
|||
|
|
value={chunks.find(chunk => chunk.id === formData.chunkId) || null}
|
|||
|
|
onChange={(e, newValue) => setFormData({ ...formData, chunkId: newValue ? newValue.id : '' })}
|
|||
|
|
renderInput={params => (
|
|||
|
|
<MuiTextField {...params} label={t('questions.selectChunk')} placeholder={t('questions.searchChunk')} />
|
|||
|
|
)}
|
|||
|
|
/>
|
|||
|
|
)}
|
|||
|
|
|
|||
|
|
{/* 图片选择(仅当数据源为图片时显示) */}
|
|||
|
|
{formData.sourceType === 'image' && (
|
|||
|
|
<Autocomplete
|
|||
|
|
fullWidth
|
|||
|
|
options={images}
|
|||
|
|
getOptionLabel={image => image.imageName || ''}
|
|||
|
|
value={images.find(image => image.id === formData.imageId) || null}
|
|||
|
|
onChange={(e, newValue) => setFormData({ ...formData, imageId: newValue ? newValue.id : '' })}
|
|||
|
|
renderInput={params => (
|
|||
|
|
<MuiTextField
|
|||
|
|
{...params}
|
|||
|
|
label={t('questions.selectImage', { defaultValue: '选择图片' })}
|
|||
|
|
placeholder={t('questions.searchImage', { defaultValue: '搜索图片...' })}
|
|||
|
|
/>
|
|||
|
|
)}
|
|||
|
|
/>
|
|||
|
|
)}
|
|||
|
|
|
|||
|
|
{/* 标签选择 */}
|
|||
|
|
{formData.sourceType === 'text' && (
|
|||
|
|
<Autocomplete
|
|||
|
|
fullWidth
|
|||
|
|
options={flattenedTags}
|
|||
|
|
getOptionLabel={tag => tag.label}
|
|||
|
|
value={flattenedTags.find(tag => tag.id === formData.label) || null}
|
|||
|
|
onChange={(e, newValue) => setFormData({ ...formData, label: newValue ? newValue.id : '' })}
|
|||
|
|
renderInput={params => (
|
|||
|
|
<MuiTextField {...params} label={t('questions.selectTag')} placeholder={t('questions.searchTag')} />
|
|||
|
|
)}
|
|||
|
|
/>
|
|||
|
|
)}
|
|||
|
|
</Box>
|
|||
|
|
</DialogContent>
|
|||
|
|
<DialogActions>
|
|||
|
|
<Button onClick={onClose}>{t('common.cancel')}</Button>
|
|||
|
|
<Button
|
|||
|
|
onClick={handleSubmit}
|
|||
|
|
variant="contained"
|
|||
|
|
disabled={!formData.question || (formData.sourceType === 'text' ? !formData.chunkId : !formData.imageId)}
|
|||
|
|
>
|
|||
|
|
{mode === 'create' ? t('common.create') : t('common.save')}
|
|||
|
|
</Button>
|
|||
|
|
</DialogActions>
|
|||
|
|
</Dialog>
|
|||
|
|
);
|
|||
|
|
}
|