add、del、edit、drag chaptersItem,add content
This commit is contained in:
parent
6b6761f7bb
commit
1d1fa94983
@ -146,34 +146,6 @@ export function UpdateDetailApi(
|
||||
});
|
||||
}
|
||||
|
||||
// 获取分类列表
|
||||
export function getSoftwareClassApi() {
|
||||
return client.get(`/backend/v1/softwareClass`, {});
|
||||
}
|
||||
// 删除分类
|
||||
export function deleteSoftwareClassApi(idList: any[]) {
|
||||
return client.destroy(`/backend/v1/softwareClass?idList=${idList}`);
|
||||
}
|
||||
|
||||
// 编辑分类
|
||||
export function updateSortClassApi(id: number, className: any, parentId: any, sort: any) {
|
||||
return client.put(`/backend/v1/softwareClass`, {
|
||||
id,
|
||||
className,
|
||||
parentId,
|
||||
sort,
|
||||
});
|
||||
}
|
||||
|
||||
// 获取绑定的学校信息
|
||||
export function getAssignedSchoolApi(softId: number | string) {
|
||||
return client.get(`/backend/v1/softwareInfo/getAllocateInfo`, { softId });
|
||||
}
|
||||
// 进行学校分配
|
||||
export function assignedSchoolApi(softId: number, tenantIds: string) {
|
||||
return client.post(`/backend/v1/softwareInfo/allocateSoft`, { softId, tenantIds });
|
||||
}
|
||||
|
||||
/*
|
||||
* Chapter相关信息
|
||||
* */
|
||||
@ -181,6 +153,13 @@ export function assignedSchoolApi(softId: number, tenantIds: string) {
|
||||
export function GetChapterListApi(params: any) {
|
||||
return client.get('/backend/v1/jc/chapter/index', params);
|
||||
}
|
||||
export function GetChapterTreeApi(params: any) {
|
||||
return client.get(`/backend/v1/jc/chapter/tree`, params);
|
||||
}
|
||||
export function GetChapterDetailApi(id: any, bookId: number) {
|
||||
return client.get(`/backend/v1/jc/chapter/${id}`, { bookId });
|
||||
}
|
||||
|
||||
// 包含嵌套的
|
||||
export function DropDiffClassApi(id: number, parent_id: number, ids: number[], book_id: number) {
|
||||
return client.put(`/backend/v1/jc/chapter/update/parent`, {
|
||||
@ -198,12 +177,12 @@ export function DropSameClassApi(ids: number[], book_id: number) {
|
||||
});
|
||||
}
|
||||
|
||||
export function checkDestroy(id: number) {
|
||||
return client.get(`/backend/v1/department/${id}/destroy`, {});
|
||||
export function GetPreDestroyChapterApi(id: number, bookId: number) {
|
||||
return client.get(`/backend/v1/jc/chapter/${id}/destroy`, { bookId });
|
||||
}
|
||||
|
||||
export function DestroyChapterApi(id: number) {
|
||||
return client.destroy(`/backend/v1/department/${id}`);
|
||||
export function DestroyChapterApi(id: number, bookId: number) {
|
||||
return client.destroy(`/backend/v1/jc/chapter/${id}?bookId=${bookId}`);
|
||||
}
|
||||
|
||||
export function CreateChapterApi(name: string, parentId: number, sort: number, bookId: number) {
|
||||
@ -221,7 +200,7 @@ export function EditChapterApi(
|
||||
sort: number,
|
||||
bookId: number
|
||||
) {
|
||||
return client.post(`/backend/v1/jc/chapter/${id}`, {
|
||||
return client.put(`/backend/v1/jc/chapter/${id}`, {
|
||||
name,
|
||||
parentId,
|
||||
sort,
|
||||
@ -229,6 +208,26 @@ export function EditChapterApi(
|
||||
});
|
||||
}
|
||||
|
||||
/*chapterContent*/
|
||||
export function SaveChapterContentApi(
|
||||
chapterId: number,
|
||||
content: string,
|
||||
resourceIds: any[],
|
||||
knowledgeIds: any[],
|
||||
highlight: string
|
||||
) {
|
||||
return client.post(`/backend/v1/jc/chapter-content/${chapterId}`, {
|
||||
content,
|
||||
resourceIds,
|
||||
knowledgeIds,
|
||||
highlight,
|
||||
});
|
||||
}
|
||||
|
||||
export function GetChapterContentApi(chapterId: number, bookId: number) {
|
||||
return client.get(`/backend/v1/jc/chapter-content/${chapterId}`, { bookId });
|
||||
}
|
||||
|
||||
/*
|
||||
* resource List
|
||||
* */
|
||||
|
||||
@ -2045,7 +2045,12 @@
|
||||
"parent": "所属章节",
|
||||
"parentTip": "请选择所属章节",
|
||||
"tips": "注意:章节目录不可超过三级!",
|
||||
"tips3": "注意:章节目录超过三级,请调整!"
|
||||
"tips3": "注意:章节目录超过三级,请调整!",
|
||||
"delText": "即将删除此章节目录及内容,且无法恢复,确认删除?",
|
||||
"unbindText1": "此章节下包含",
|
||||
"unbindText2": "个子章节",
|
||||
"unbindText3": "请先删除子章节后再删除此项。"
|
||||
|
||||
},
|
||||
"resource": {
|
||||
"pageTitle": "资源管理",
|
||||
@ -2069,6 +2074,7 @@
|
||||
"uploadTips": "点击或拖拽文件到此处上传",
|
||||
"uploadTips2": "支持视频、图片、文档、音频等常见格式",
|
||||
"btnSave": "保存内容",
|
||||
"btnEdit": "编辑内容",
|
||||
"btnPreview": "预览",
|
||||
"knowledge": "知识图谱",
|
||||
"typeList": {
|
||||
|
||||
@ -68,7 +68,7 @@ export const DepartmentCreate: React.FC<PropInterface> = ({ open, onCancel }) =>
|
||||
|
||||
const checkArr = (departments: any[], id: number) => {
|
||||
const arr = [];
|
||||
for (let i = 0; i < departments[id].length; i++) {
|
||||
for (let i = 0; i < departments[id]?.length; i++) {
|
||||
if (!departments[departments[id][i].id]) {
|
||||
arr.push({
|
||||
label: departments[id][i].name,
|
||||
|
||||
@ -88,7 +88,7 @@ const DepartmentPage = () => {
|
||||
|
||||
const checkArr = (departments: DepartmentsBoxModel, id: number) => {
|
||||
const arr = [];
|
||||
for (let i = 0; i < departments[id].length; i++) {
|
||||
for (let i = 0; i < departments[id]?.length; i++) {
|
||||
if (!departments[departments[id][i].id]) {
|
||||
arr.push({
|
||||
title: (
|
||||
|
||||
@ -4,18 +4,38 @@ import React, { useEffect, useState } from 'react';
|
||||
import { ChapterTree } from './compenents/chapterTree';
|
||||
import { BackBartment } from '../../compenents';
|
||||
import styles from './chapter.module.less';
|
||||
import { GetChapterListApi } from '../../api/textbook';
|
||||
import {
|
||||
GetChapterContentApi,
|
||||
GetChapterDetailApi,
|
||||
GetChapterListApi,
|
||||
GetChapterTreeApi,
|
||||
SaveChapterContentApi,
|
||||
} from '../../api/textbook';
|
||||
import TextbookEditor from './compenents/TextEditor/TextbookEditor';
|
||||
import { Empty } from 'antd';
|
||||
|
||||
export interface ChapterItemModel {
|
||||
created_at: string;
|
||||
id: number;
|
||||
bookId: number;
|
||||
chapterCode: string;
|
||||
createTime: string;
|
||||
creator: string;
|
||||
level: null | number;
|
||||
name: string;
|
||||
from_scene: number;
|
||||
parent_chain: string;
|
||||
parent_id: number;
|
||||
sort: number;
|
||||
updated_at: string;
|
||||
id: number;
|
||||
tenantId: number;
|
||||
updateTime: string;
|
||||
updater: string;
|
||||
parentId: number;
|
||||
}
|
||||
export interface ChapterTreeItem {
|
||||
bookId: number;
|
||||
chapterCode: string;
|
||||
name: string;
|
||||
sort: number;
|
||||
id: number;
|
||||
parentId: number;
|
||||
children: ChapterTreeItem[];
|
||||
}
|
||||
|
||||
export interface ChaptersBoxModel {
|
||||
@ -29,35 +49,84 @@ export interface Option {
|
||||
children?: Option[];
|
||||
}
|
||||
|
||||
export interface ChapterContent {
|
||||
title?: any;
|
||||
level?: number;
|
||||
}
|
||||
|
||||
const ChapterManagementPage = () => {
|
||||
const { t } = useTranslation();
|
||||
const params = useParams();
|
||||
const [selectedChapter, setSelectedChapter] = useState<any>(null);
|
||||
const [contentChapterName, setContentChapterName] = useState<any>(null);
|
||||
const [searchParams] = useSearchParams();
|
||||
const title = searchParams.get('title');
|
||||
const { bookId } = params;
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
const [treeData, setTreeData] = useState<ChaptersBoxModel>([]);
|
||||
const [isContentLoading, setIsContentLoading] = useState<boolean>(false);
|
||||
const [chapterListData, setChapterListData] = useState<ChaptersBoxModel>([]);
|
||||
const [selectedChapterId, setSelectedChapterId] = useState<string>();
|
||||
const [parentTitle, setParentTitle] = useState<string>();
|
||||
|
||||
useEffect(() => {
|
||||
getChapterData();
|
||||
}, [bookId]);
|
||||
|
||||
useEffect(() => {
|
||||
getChapterDetail();
|
||||
getChapterParentDetail();
|
||||
}, [selectedChapter, bookId]);
|
||||
|
||||
const getChapterData = () => {
|
||||
setLoading(true);
|
||||
GetChapterListApi({ bookId: bookId }).then((res: any) => {
|
||||
const resData: ChaptersBoxModel = res.data.chapters;
|
||||
setTreeData(resData);
|
||||
setChapterListData(resData);
|
||||
setLoading(false);
|
||||
});
|
||||
};
|
||||
|
||||
const onSave = () => {
|
||||
console.log(selectedChapterId);
|
||||
const refreshData = () => {
|
||||
getChapterData();
|
||||
getChapterDetail();
|
||||
getChapterParentDetail();
|
||||
};
|
||||
|
||||
const getChapterParentDetail = () => {
|
||||
if (selectedChapter) {
|
||||
if (selectedChapter?.parentId === 0) {
|
||||
setParentTitle('根目录');
|
||||
} else {
|
||||
GetChapterDetailApi(selectedChapter?.parentId, Number(bookId)).then((res: any) => {
|
||||
const resData: ChapterItemModel = res.data;
|
||||
setParentTitle(resData.name);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const getChapterDetail = () => {
|
||||
if (selectedChapter) {
|
||||
GetChapterDetailApi(selectedChapter?.id, Number(bookId)).then((res: any) => {
|
||||
const resData: ChapterItemModel = res.data;
|
||||
setContentChapterName(resData.name);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const onSave = (data: string) => {
|
||||
console.log(data, 'string');
|
||||
SaveChapterContentApi(selectedChapter?.id, data, [], [], '').then((res: any) => {
|
||||
console.log(res, 'res');
|
||||
});
|
||||
};
|
||||
const onContentChange = () => {
|
||||
console.log(selectedChapterId);
|
||||
};
|
||||
const handleSelectItem = (data: any) => {
|
||||
setSelectedChapter(data);
|
||||
setSelectedChapterId(data?.id);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={styles['chapter-box']}>
|
||||
@ -68,25 +137,34 @@ const ChapterManagementPage = () => {
|
||||
<div className={styles['left-box']}>
|
||||
<ChapterTree
|
||||
selectedId={selectedChapterId}
|
||||
onSelecet={handleSelectItem}
|
||||
bookId={Number(bookId)}
|
||||
isLoading={loading}
|
||||
title={title}
|
||||
chapterTreeData={treeData}
|
||||
refreshTreeData={getChapterData}
|
||||
chapterData={chapterListData}
|
||||
refreshTreeData={refreshData}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles['right-box']}>
|
||||
{/*{selectedChapter ? (*/}
|
||||
{selectedChapter ? (
|
||||
<TextbookEditor
|
||||
chapterId={selectedChapter?.id || 22}
|
||||
chapterTitle={selectedChapter?.name || '测试数据'}
|
||||
initialContent="请编写内容"
|
||||
isLoading={isContentLoading}
|
||||
chapterId={selectedChapter?.id}
|
||||
parentTitle={parentTitle || '根目录'}
|
||||
chapterTitle={contentChapterName || selectedChapter?.name}
|
||||
onSave={onSave}
|
||||
bookId={Number(bookId)}
|
||||
onContentChange={onContentChange}
|
||||
></TextbookEditor>
|
||||
{/* : (
|
||||
<div>点击左侧目录选择</div>
|
||||
)}*/}
|
||||
) : (
|
||||
<div className="enhanced-textbook-editor empty-state">
|
||||
<div className="empty-content">
|
||||
<div className="empty-icon"></div>
|
||||
<h3>请选择章节</h3>
|
||||
<p>点击左侧目录,选择一个章节进行查看</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -82,7 +82,7 @@
|
||||
|
||||
/*编辑区域*/
|
||||
.editor-main-box{
|
||||
background: #8F8F8F;
|
||||
background: #ffffff;
|
||||
position: relative;
|
||||
margin: 20px;
|
||||
height: 590px;
|
||||
|
||||
@ -1,48 +1,53 @@
|
||||
import React, { useEffect, useState, useCallback } from 'react';
|
||||
import { EditorProps } from '../../../../types/editor';
|
||||
import './TextbookEditor.less';
|
||||
import { Button } from 'antd';
|
||||
import { EyeFilled, ForkOutlined, SaveFilled } from '@ant-design/icons';
|
||||
import { Button, Drawer, Empty, Modal, Spin } from 'antd';
|
||||
import { EditFilled, EyeFilled, ForkOutlined, SaveFilled } from '@ant-design/icons';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import '@wangeditor/editor/dist/css/style.css';
|
||||
import { Editor, Toolbar } from '@wangeditor/editor-for-react';
|
||||
import { IDomEditor, IEditorConfig, IToolbarConfig } from '@wangeditor/editor';
|
||||
import { DomEditor } from '@wangeditor/editor';
|
||||
import { GetChapterContentApi } from '../../../../api/textbook';
|
||||
|
||||
const TextbookEditor: React.FC<EditorProps> = ({
|
||||
chapterId,
|
||||
parentTitle,
|
||||
chapterTitle,
|
||||
initialContent = '',
|
||||
onSave,
|
||||
bookId,
|
||||
}) => {
|
||||
const [isEditing, setIsEditing] = useState(false);
|
||||
const [isEditing, setIsEditing] = useState<boolean>(false);
|
||||
const { t } = useTranslation();
|
||||
// editor 实例
|
||||
const [editor, setEditor] = useState<IDomEditor | null>(null); // TS 语法
|
||||
|
||||
const [editor, setEditor] = useState<IDomEditor | null>(null);
|
||||
// 编辑器内容
|
||||
const [html, setHtml] = useState('<p>hello</p>');
|
||||
const [html, setHtml] = useState<string>();
|
||||
const [loading, setLoading] = useState<boolean>(true);
|
||||
const [isPreviewModalShow, setIsPreviewModalShow] = useState<boolean>(false);
|
||||
const toolbarConfig: Partial<IToolbarConfig> = {};
|
||||
|
||||
toolbarConfig.excludeKeys = ['fullScreen']; //移除不想要的fullScreen
|
||||
|
||||
useEffect(() => {
|
||||
if (editor && initialContent !== editor.getHtml()) {
|
||||
editor.insertText(
|
||||
// 添加内容
|
||||
initialContent ||
|
||||
`
|
||||
<h2>${chapterTitle}</h2>
|
||||
<p>开始编写本章节的内容...</p>
|
||||
`
|
||||
);
|
||||
setIsEditing(false);
|
||||
setLoading(true);
|
||||
const timer = setTimeout(() => {
|
||||
getContent();
|
||||
}, 2000);
|
||||
|
||||
return () => clearTimeout(timer);
|
||||
}, [chapterTitle, chapterId]);
|
||||
|
||||
const getContent = () => {
|
||||
GetChapterContentApi(Number(chapterId), bookId).then((res: any) => {
|
||||
setHtml(res?.data?.content || null);
|
||||
setLoading(false);
|
||||
});
|
||||
};
|
||||
// 更新统计
|
||||
// const text = editor.getText();
|
||||
// setWordCount(text.split(/\s+/).filter((word) => word.length > 0).length);
|
||||
}
|
||||
}, [initialContent, editor, chapterTitle]);
|
||||
|
||||
// 工具栏配置
|
||||
const toolbarConfig: Partial<IToolbarConfig> = {}; // TS 语法
|
||||
// }
|
||||
|
||||
// 编辑器配置
|
||||
const editorConfig: Partial<IEditorConfig> = {
|
||||
@ -56,24 +61,22 @@ const TextbookEditor: React.FC<EditorProps> = ({
|
||||
console.log(curToolbarConfig?.toolbarKeys);
|
||||
}
|
||||
|
||||
toolbarConfig.excludeKeys = ['fullScreen']; //移除不想要的fullScreen
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
if (editor == null) return;
|
||||
editor.destroy();
|
||||
setEditor(null);
|
||||
window.localStorage.removeItem('html');
|
||||
};
|
||||
}, [chapterId, initialContent, chapterTitle, editor]);
|
||||
}, [chapterId, chapterTitle, editor]);
|
||||
|
||||
// 手动保存
|
||||
const handleManualSave = useCallback(() => {
|
||||
if (editor && isEditing) {
|
||||
onSave(chapterId, html);
|
||||
// setLastSaved(new Date());
|
||||
if (editor) {
|
||||
onSave(html);
|
||||
setIsEditing(false);
|
||||
}
|
||||
}, [editor, chapterId, isEditing, onSave]);
|
||||
}, [editor, chapterId, isEditing, onSave, html]);
|
||||
|
||||
// 格式化时间
|
||||
const formatTime = (date: Date | null): string => {
|
||||
@ -86,31 +89,34 @@ const TextbookEditor: React.FC<EditorProps> = ({
|
||||
|
||||
// 预览
|
||||
const handlePreview = () => {
|
||||
alert('preview');
|
||||
setIsPreviewModalShow(true);
|
||||
};
|
||||
|
||||
if (!chapterId) {
|
||||
return (
|
||||
<div className="enhanced-textbook-editor empty-state">
|
||||
<div className="empty-content">
|
||||
<div className="empty-icon"></div>
|
||||
<h3>请选择章节</h3>
|
||||
<p>从左侧目录中选择一个章节开始编辑</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
// 在编辑器创建完成后关闭 loading
|
||||
const handleEditorCreated = (editorInstance: any) => {
|
||||
setEditor(editorInstance);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="enhanced-textbook-editor">
|
||||
{/* 编辑器头部 */}
|
||||
{loading ? (
|
||||
<div style={{ width: '100%', height: 600, textAlign: 'center', lineHeight: '600px' }}>
|
||||
<Spin size="large" tip="Loading..." style={{ color: '#1890ff' }} />
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<div className="editor-header">
|
||||
<div className="header-left">
|
||||
<h2 className="chapter-title">{chapterTitle}</h2>
|
||||
<span className="chapter-id">所属章节: {chapterId}</span>
|
||||
<span className="chapter-id">所属章节: {parentTitle}</span>
|
||||
</div>
|
||||
<div className="header-right">
|
||||
<Button style={{ marginRight: 10 }} onClick={handlePreview}>
|
||||
<Button
|
||||
style={{ marginRight: 10 }}
|
||||
onClick={handlePreview}
|
||||
disabled={!html || html == '<p><br></p>' || html == '<p></p>'}
|
||||
>
|
||||
<EyeFilled />
|
||||
{t('textbook.resource.btnPreview')}
|
||||
</Button>
|
||||
@ -125,30 +131,125 @@ const TextbookEditor: React.FC<EditorProps> = ({
|
||||
<ForkOutlined />
|
||||
{t('textbook.resource.knowledge')}
|
||||
</Button>
|
||||
|
||||
{isEditing ? (
|
||||
<Button type="primary" style={{ marginRight: 5 }} onClick={handleManualSave}>
|
||||
<SaveFilled />
|
||||
{t('textbook.resource.btnSave')}
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
type="primary"
|
||||
style={{ marginRight: 5 }}
|
||||
onClick={() => {
|
||||
setIsEditing(true);
|
||||
}}
|
||||
>
|
||||
<EditFilled />
|
||||
{t('textbook.resource.btnEdit')}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="editor-main-box">
|
||||
{isEditing ? (
|
||||
<>
|
||||
<Toolbar
|
||||
editor={editor}
|
||||
defaultConfig={toolbarConfig}
|
||||
mode="default"
|
||||
style={{ borderBottom: '1px solid #ccc' }}
|
||||
/>
|
||||
|
||||
<Editor
|
||||
defaultConfig={editorConfig}
|
||||
value={html}
|
||||
onCreated={setEditor}
|
||||
onChange={(editor) => setHtml(editor.getHtml())}
|
||||
onCreated={(editor: any) => handleEditorCreated(editor)}
|
||||
onChange={(editor) => {
|
||||
const currentHtml = editor.getHtml();
|
||||
setHtml(currentHtml);
|
||||
window.localStorage.setItem('html', currentHtml);
|
||||
}}
|
||||
mode="default"
|
||||
style={{ height: '550px', overflowY: 'hidden' }}
|
||||
style={{
|
||||
height: '550px',
|
||||
paddingBottom: 15,
|
||||
boxSizing: 'border-box',
|
||||
overflowY: 'hidden',
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<div
|
||||
style={{
|
||||
height: '590px',
|
||||
borderRadius: '6px',
|
||||
padding: '20px',
|
||||
boxSizing: 'border-box',
|
||||
backgroundColor: '#fafafa',
|
||||
overflowY: 'auto',
|
||||
width: '100%',
|
||||
overflowX: 'hidden',
|
||||
}}
|
||||
>
|
||||
{html && html !== '<p><br></p>' && html !== '<p></p>' ? (
|
||||
// @ts-ignore
|
||||
<div style={{ width: '100%' }} dangerouslySetInnerHTML={{ __html: html }} />
|
||||
) : (
|
||||
<div
|
||||
style={{
|
||||
height: '550px',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
textAlign: 'center',
|
||||
color: '#999',
|
||||
fontSize: '16px',
|
||||
}}
|
||||
>
|
||||
<div style={{ fontSize: '48px', marginBottom: '16px' }}>
|
||||
<Empty description={false} />
|
||||
</div>
|
||||
<div style={{ marginBottom: '8px', fontWeight: 500 }}>暂无内容</div>
|
||||
<div style={{ fontSize: '14px', color: '#bfbfbf' }}>
|
||||
请在编辑器中添加内容
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<Drawer
|
||||
key={'previewModal'}
|
||||
title={t('textbook.resource.btnPreview')}
|
||||
onClose={() => setIsPreviewModalShow(false)}
|
||||
width={1000}
|
||||
open={isPreviewModalShow}
|
||||
footer={null}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
height: 'auto',
|
||||
borderRadius: '6px',
|
||||
padding: '8px',
|
||||
margin: '10px',
|
||||
width: 'auto',
|
||||
backgroundColor: '#fafafa',
|
||||
overflowY: 'auto',
|
||||
}}
|
||||
// @ts-ignore
|
||||
dangerouslySetInnerHTML={{ __html: html }}
|
||||
/>
|
||||
</Drawer>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@ -1,44 +1,20 @@
|
||||
import { Modal, Form, Input, Select, message, Spin, Cascader } from 'antd';
|
||||
import { Modal, Form, Input, message, Spin, Cascader } from 'antd';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {
|
||||
addSortClassApi,
|
||||
getSoftwareClassApi,
|
||||
updateSortClassApi,
|
||||
} from '../../virtual/api/virtual';
|
||||
import { CreateChapterApi, EditChapterApi } from '../../../api/textbook';
|
||||
import { CreateChapterApi, EditChapterApi, GetChapterListApi } from '../../../api/textbook';
|
||||
|
||||
interface Option {
|
||||
value: string | number;
|
||||
label: string;
|
||||
children?: Option[];
|
||||
}
|
||||
interface ChapterItem {
|
||||
id: string | number;
|
||||
name: string;
|
||||
level: number;
|
||||
parent_chain: string;
|
||||
parentId: string;
|
||||
sort: number;
|
||||
children?: ChapterItem[];
|
||||
}
|
||||
|
||||
interface ChapterItemModel {
|
||||
created_at: string;
|
||||
id: number;
|
||||
name: string;
|
||||
from_scene: number;
|
||||
parent_chain: string;
|
||||
parentId: number;
|
||||
sort: number;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
interface ChapterModalProps {
|
||||
visible: boolean;
|
||||
bookId: number;
|
||||
isEdit: boolean;
|
||||
editData?: any;
|
||||
parentData?: any;
|
||||
onCancel: () => void;
|
||||
confirmLoading?: boolean;
|
||||
onSuccess: () => void;
|
||||
@ -49,6 +25,7 @@ export const ChapterModal: React.FC<ChapterModalProps> = ({
|
||||
bookId,
|
||||
isEdit,
|
||||
editData,
|
||||
parentData,
|
||||
onCancel,
|
||||
confirmLoading = false,
|
||||
onSuccess,
|
||||
@ -56,32 +33,169 @@ export const ChapterModal: React.FC<ChapterModalProps> = ({
|
||||
const [form] = Form.useForm();
|
||||
const { t } = useTranslation();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [init, setInit] = useState(true);
|
||||
const [chapters, setChapters] = useState<any>([]);
|
||||
|
||||
const [parentId, setParentId] = useState<number>(0);
|
||||
const [editingId, setEditingId] = useState<number>(0); // 存储正在编辑的ID
|
||||
|
||||
// 根据 parentData 生成固定的章节目录选项
|
||||
const generateFixedChapterOptions = (parentData: any): Option[] => {
|
||||
if (!parentData) {
|
||||
return [
|
||||
{
|
||||
label: t('commen.levelCate'),
|
||||
value: 0,
|
||||
},
|
||||
];
|
||||
}
|
||||
const fixedOptions: Option[] = [
|
||||
{
|
||||
label: parentData.name,
|
||||
value: parentData.id,
|
||||
},
|
||||
];
|
||||
|
||||
fixedOptions.unshift({
|
||||
label: t('commen.levelCate'),
|
||||
value: 0,
|
||||
});
|
||||
return fixedOptions;
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setInit(true);
|
||||
if (isEdit && editData) {
|
||||
form.setFieldsValue(editData);
|
||||
} else {
|
||||
setLoading(true);
|
||||
form.resetFields();
|
||||
let defaultLevel = 1;
|
||||
form.setFieldsValue({
|
||||
level: defaultLevel,
|
||||
...editData,
|
||||
if (parentData && parentData.id) {
|
||||
setParentId(parentData.id);
|
||||
}
|
||||
// 如果是编辑模式,设置编辑ID
|
||||
if (isEdit && editData) {
|
||||
setEditingId(editData.id);
|
||||
}
|
||||
getChapterParams();
|
||||
}, [parentData, isEdit, editData]);
|
||||
|
||||
const checkArr = (
|
||||
categories: any[],
|
||||
id: number,
|
||||
currentLevel: number = 1,
|
||||
maxLevel: number = 2,
|
||||
editingId?: number // 添加编辑ID参数
|
||||
) => {
|
||||
const arr: { label: any; value: any; children?: Option[] }[] = [];
|
||||
|
||||
if (currentLevel > maxLevel) {
|
||||
return arr;
|
||||
}
|
||||
|
||||
for (let i = 0; i < categories[id].length; i++) {
|
||||
const currentItem = categories[id][i];
|
||||
const currentItemId = currentItem.id;
|
||||
|
||||
// 排除编辑数据本身
|
||||
if (editingId && currentItemId === editingId) {
|
||||
continue;
|
||||
}
|
||||
// 检查当前项是否包含编辑数据(避免选择子集)
|
||||
const isEditingDataChild = editingId && hasRealChild(categories, currentItemId, editingId);
|
||||
|
||||
if (!categories[currentItemId] || currentLevel === maxLevel || isEditingDataChild) {
|
||||
arr.push({
|
||||
label: currentItem.name,
|
||||
value: currentItemId,
|
||||
});
|
||||
} else {
|
||||
const new_arr: Option[] = checkArr(
|
||||
categories,
|
||||
currentItemId,
|
||||
currentLevel + 1,
|
||||
maxLevel,
|
||||
editingId
|
||||
);
|
||||
arr.push({
|
||||
label: currentItem.name,
|
||||
value: currentItemId,
|
||||
children: new_arr,
|
||||
});
|
||||
}
|
||||
setInit(false);
|
||||
getParams();
|
||||
}, [visible, isEdit, editData, form]);
|
||||
}
|
||||
return arr;
|
||||
};
|
||||
|
||||
const hasRealChild = (categories: any[], parentId: number, targetId: number): boolean => {
|
||||
if (!categories[parentId]) return false;
|
||||
|
||||
for (let i = 0; i < categories[parentId].length; i++) {
|
||||
const childId = categories[parentId][i].id;
|
||||
|
||||
// 跳过直接匹配(编辑数据本身)
|
||||
if (childId === targetId) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 检查子节点是否匹配
|
||||
if (childId === targetId) {
|
||||
return true;
|
||||
}
|
||||
// 递归检查孙节点
|
||||
if (hasRealChild(categories, childId, targetId)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
const getChapterParams = () => {
|
||||
// 使用固定的章节目录
|
||||
if (parentData) {
|
||||
const fixedOptions = generateFixedChapterOptions(parentData);
|
||||
setChapters(fixedOptions);
|
||||
form.setFieldsValue({
|
||||
parentId: parentData.id,
|
||||
});
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
GetChapterListApi({ bookId }).then((res: any) => {
|
||||
const chapters = res.data.chapters;
|
||||
if (JSON.stringify(chapters) !== '{}') {
|
||||
const editingId = isEdit && editData ? editData.id : undefined;
|
||||
const new_arr: Option[] = checkArr(chapters, 0, 1, 2, editingId);
|
||||
new_arr.unshift({
|
||||
label: t('commen.levelCate'),
|
||||
value: 0,
|
||||
});
|
||||
setChapters(new_arr);
|
||||
|
||||
if (isEdit && editData) {
|
||||
const arr = editData.chapterCode.split(',');
|
||||
const p_arr: any[] = [];
|
||||
arr.map((num: any) => {
|
||||
p_arr.push(Number(num));
|
||||
});
|
||||
form.setFieldsValue({
|
||||
name: editData.name,
|
||||
parentId: p_arr,
|
||||
});
|
||||
setParentId(editData.parentId);
|
||||
setLoading(false);
|
||||
}
|
||||
} else {
|
||||
const new_arr: Option[] = [
|
||||
{
|
||||
label: t('commen.levelCate'),
|
||||
value: 0,
|
||||
},
|
||||
];
|
||||
setChapters(new_arr);
|
||||
}
|
||||
setLoading(false);
|
||||
});
|
||||
};
|
||||
|
||||
const handleOk = async () => {
|
||||
try {
|
||||
const values = await form.validateFields();
|
||||
onFinish(values);
|
||||
console.log(values, 'add values');
|
||||
} catch (error) {
|
||||
console.error('表单验证失败:', error);
|
||||
}
|
||||
@ -93,9 +207,11 @@ export const ChapterModal: React.FC<ChapterModalProps> = ({
|
||||
}
|
||||
setLoading(true);
|
||||
|
||||
// 使用固定的 parentId(来自 parentData)或表单中的值
|
||||
const finalParentId = parentData ? parentData.id : parentId || 0;
|
||||
|
||||
if (isEdit && editingId) {
|
||||
// 编辑模式:调用更新接口,保持自己的sort
|
||||
EditChapterApi(editingId, values.name, parentId || 0, editData.sort, bookId)
|
||||
EditChapterApi(editingId, values.name, finalParentId, editData.sort, bookId)
|
||||
.then((res: any) => {
|
||||
setLoading(false);
|
||||
message.success('更新成功');
|
||||
@ -109,7 +225,7 @@ export const ChapterModal: React.FC<ChapterModalProps> = ({
|
||||
});
|
||||
} else {
|
||||
// 新建模式:调用新增接口
|
||||
CreateChapterApi(values.name, parentId || 0, 0, bookId)
|
||||
CreateChapterApi(values.name, finalParentId, 0, bookId)
|
||||
.then((res: any) => {
|
||||
setLoading(false);
|
||||
message.success('创建成功');
|
||||
@ -130,6 +246,10 @@ export const ChapterModal: React.FC<ChapterModalProps> = ({
|
||||
};
|
||||
|
||||
const handleChange = (value: any) => {
|
||||
// 如果有 parentData,不允许更改
|
||||
if (parentData) {
|
||||
return;
|
||||
}
|
||||
if (value !== undefined) {
|
||||
let it = value[value.length - 1];
|
||||
setParentId(it);
|
||||
@ -137,44 +257,7 @@ export const ChapterModal: React.FC<ChapterModalProps> = ({
|
||||
setParentId(0);
|
||||
}
|
||||
};
|
||||
const checkArr = (categories: any[], id: number) => {
|
||||
const arr = [];
|
||||
for (let i = 0; i < categories[id].length; i++) {
|
||||
if (!categories[categories[id][i].id]) {
|
||||
arr.push({
|
||||
label: categories[id][i].className,
|
||||
value: categories[id][i].id,
|
||||
});
|
||||
} else {
|
||||
arr.push({
|
||||
label: categories[id][i].className,
|
||||
value: categories[id][i].id,
|
||||
});
|
||||
}
|
||||
}
|
||||
return arr;
|
||||
};
|
||||
const getParams = () => {
|
||||
getSoftwareClassApi().then((res: any) => {
|
||||
const chapters = res.data;
|
||||
if (JSON.stringify(chapters) !== '{}') {
|
||||
const new_arr: Option[] = checkArr(chapters, 0);
|
||||
new_arr.unshift({
|
||||
label: t('commen.levelCate'),
|
||||
value: 0,
|
||||
});
|
||||
setChapters(new_arr);
|
||||
} else {
|
||||
const new_arr: Option[] = [];
|
||||
new_arr.unshift({
|
||||
label: t('commen.levelCate'),
|
||||
value: 0,
|
||||
});
|
||||
setChapters(new_arr);
|
||||
}
|
||||
setInit(false);
|
||||
});
|
||||
};
|
||||
|
||||
const displayRender = (label: any, selectedOptions: any) => {
|
||||
return label[label.length - 1];
|
||||
};
|
||||
@ -190,12 +273,12 @@ export const ChapterModal: React.FC<ChapterModalProps> = ({
|
||||
confirmLoading={confirmLoading}
|
||||
destroyOnHidden={true}
|
||||
>
|
||||
{init && (
|
||||
{loading && (
|
||||
<div className="float-left text-center mt-30">
|
||||
<Spin></Spin>
|
||||
</div>
|
||||
)}
|
||||
<div className="float-left mt-24" style={{ display: init ? 'none' : 'block' }}>
|
||||
<div className="float-left mt-24" style={{ display: loading ? 'none' : 'block' }}>
|
||||
<Form
|
||||
form={form}
|
||||
name="basic"
|
||||
@ -212,14 +295,14 @@ export const ChapterModal: React.FC<ChapterModalProps> = ({
|
||||
rules={[{ required: true, message: t('textbook.chapter.parentTip') }]}
|
||||
>
|
||||
<Cascader
|
||||
allowClear
|
||||
allowClear={!parentData}
|
||||
placeholder={t('textbook.chapter.parentTip')}
|
||||
onChange={handleChange}
|
||||
options={chapters}
|
||||
changeOnSelect
|
||||
expand-trigger="hover"
|
||||
expandTrigger="hover"
|
||||
displayRender={displayRender}
|
||||
disabled={isEdit && editingId === 0} // 禁止编辑根分类的父级
|
||||
disabled={!!parentData || (isEdit && editingId === 0)} // 有 parentData 时禁用选择
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { Button, Image, Tree, Modal, Form, message, Spin, Tooltip } from 'antd';
|
||||
import { Button, Image, Tree, Modal, Form, message, Spin, Tooltip, Empty } from 'antd';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import styles from './chapterTree.module.less';
|
||||
@ -24,77 +24,74 @@ import type { DataNode, TreeProps } from 'antd/es/tree';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useSelector } from 'react-redux';
|
||||
import textbook from '../index';
|
||||
import { CreateChapterApi, DropDiffClassApi, DropSameClassApi } from '../../../api/textbook';
|
||||
interface ChapterItem {
|
||||
id: string | number;
|
||||
name: string;
|
||||
title?: string;
|
||||
level?: number;
|
||||
parent_chain: string;
|
||||
parent_id: string;
|
||||
sort: number;
|
||||
children?: ChapterItem[];
|
||||
}
|
||||
|
||||
interface ChapterItemModel {
|
||||
created_at: string;
|
||||
id: number;
|
||||
name: string;
|
||||
from_scene: number;
|
||||
parent_chain: string;
|
||||
parent_id: number;
|
||||
sort: number;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
interface ChaptersBoxModel {
|
||||
[key: number]: ChapterItemModel[];
|
||||
}
|
||||
import {
|
||||
CreateChapterApi,
|
||||
DestroyChapterApi,
|
||||
DropDiffClassApi,
|
||||
DropSameClassApi,
|
||||
GetPreDestroyChapterApi,
|
||||
} from '../../../api/textbook';
|
||||
import { ChapterItemModel, ChaptersBoxModel, ChapterTreeItem } from '../chapter';
|
||||
|
||||
interface Option {
|
||||
key: string | number;
|
||||
title: any;
|
||||
children?: Option[];
|
||||
level?: number;
|
||||
sort?: number;
|
||||
rawData?: any;
|
||||
}
|
||||
|
||||
interface PropInterface {
|
||||
chapterTreeData: ChaptersBoxModel;
|
||||
chapterData: ChaptersBoxModel;
|
||||
isLoading: boolean;
|
||||
bookId: number;
|
||||
title: any;
|
||||
selected?: any;
|
||||
selectedId?: any;
|
||||
onSelecet?: any;
|
||||
refreshTreeData: () => void;
|
||||
}
|
||||
|
||||
export const ChapterTree = (props: PropInterface) => {
|
||||
const { chapterTreeData, isLoading, selected, bookId, title, refreshTreeData } = props;
|
||||
const { chapterData, isLoading, selected, bookId, title, onSelecet, refreshTreeData } = props;
|
||||
const permissions = useSelector((state: any) => state.loginUser.value.permissions);
|
||||
|
||||
// 权限
|
||||
const through = (p: string) => {
|
||||
if (!permissions) {
|
||||
return false;
|
||||
}
|
||||
return typeof permissions[p] !== 'undefined';
|
||||
};
|
||||
const navigate = useNavigate();
|
||||
const { t } = useTranslation();
|
||||
const [treeData, setTreeData] = useState<Option[]>([]);
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
const [selectKey, setSelectKey] = useState<number[]>([]);
|
||||
const [selectedNodeId, setSelectedNodeId] = useState<string | number | null>(null);
|
||||
const [modalVisible, setModalVisible] = useState<boolean>(false);
|
||||
const [isEdit, setIsEdit] = useState<boolean>(false);
|
||||
const [editingChapter, setEditingChapter] = useState<ChapterItemModel | null>(null);
|
||||
const [parentChapter, setParentChapter] = useState<ChapterItemModel | null>(null);
|
||||
const [form] = Form.useForm();
|
||||
const [dragEnabled, setDragEnabled] = useState<boolean>(false); // 拖拽状态
|
||||
const [did, setDid] = useState<number>(0);
|
||||
const [modal, contextHolder] = Modal.useModal();
|
||||
const [submitLoading, setSubmitLoading] = useState<boolean>(false);
|
||||
const [refresh, setRefresh] = useState(false);
|
||||
const [isMoreThanThree, setIsMoreThanThree] = useState<boolean>(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (selected && selected.length > 0) {
|
||||
setSelectKey(selected);
|
||||
}
|
||||
}, [selected]);
|
||||
|
||||
useEffect(() => {
|
||||
setTimeout(() => {
|
||||
if (JSON.stringify(chapterData) !== '{}') {
|
||||
const new_arr: Option[] = checkArr(chapterData, 0);
|
||||
setTreeData(new_arr);
|
||||
console.log(new_arr, 'new-arr');
|
||||
}
|
||||
}, 500);
|
||||
}, [chapterData]);
|
||||
|
||||
// 切换拖拽模式
|
||||
const toggleDragMode = () => {
|
||||
const newState = !dragEnabled;
|
||||
@ -251,70 +248,13 @@ export const ChapterTree = (props: PropInterface) => {
|
||||
}
|
||||
};
|
||||
|
||||
// 生成带序号的名称 - 需要更新以处理级别变化
|
||||
const generateChapterName = (chapter: ChapterItem, parent?: ChapterItem | null): string => {
|
||||
// 如果节点成为一级目录,重新生成名称
|
||||
if (chapter.level === 1) {
|
||||
return `第${chapter.sort}章 ${chapter.title}`;
|
||||
} else if (chapter.level === 2) {
|
||||
const parentSort = parent?.sort ?? '?';
|
||||
return `${parentSort}.${chapter.sort} ${chapter.title}`;
|
||||
} else {
|
||||
const parentSort = parent?.sort ?? '?';
|
||||
return `${parentSort}.${chapter.sort} ${chapter.title}`;
|
||||
}
|
||||
};
|
||||
|
||||
// 更新 regenerateChapterNames 以处理级别变化
|
||||
const regenerateChapterNames = (data: { [key: number]: ChapterItem[] }) => {
|
||||
const regenerate = (chapters: ChapterItem[], parent?: ChapterItem): ChapterItem[] => {
|
||||
return chapters.map((chapter, index) => {
|
||||
// 更新排序
|
||||
const updatedChapter = {
|
||||
...chapter,
|
||||
sort: index + 1,
|
||||
};
|
||||
|
||||
// 重新生成名称
|
||||
updatedChapter.name = generateChapterName(updatedChapter, parent);
|
||||
|
||||
// 递归处理子节点
|
||||
if (chapter.children && chapter.children.length > 0) {
|
||||
return {
|
||||
...updatedChapter,
|
||||
children: regenerate(chapter.children, updatedChapter),
|
||||
};
|
||||
}
|
||||
|
||||
return updatedChapter;
|
||||
});
|
||||
};
|
||||
|
||||
return { 0: regenerate(data[0]) };
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (selected && selected.length > 0) {
|
||||
setSelectKey(selected);
|
||||
}
|
||||
}, [selected]);
|
||||
|
||||
useEffect(() => {
|
||||
setTimeout(() => {
|
||||
if (JSON.stringify(chapterTreeData) !== '{}') {
|
||||
const new_arr: Option[] = checkArr(chapterTreeData, 0);
|
||||
setTreeData(new_arr);
|
||||
}
|
||||
}, 500);
|
||||
}, [chapterTreeData]);
|
||||
|
||||
const checkArr = (departments: ChaptersBoxModel, id: number) => {
|
||||
const arr: any = [];
|
||||
if (!departments[id]) return arr;
|
||||
|
||||
for (let i = 0; i < departments[id].length; i++) {
|
||||
const item: ChapterItemModel = departments[id][i];
|
||||
const level = item.parent_chain ? item.parent_chain.split(',').length + 1 : 1;
|
||||
const level = item.chapterCode ? item.chapterCode.split(',').length + 1 : 1;
|
||||
const hasChildren = departments[item.id];
|
||||
|
||||
const name = (
|
||||
@ -333,6 +273,7 @@ export const ChapterTree = (props: PropInterface) => {
|
||||
className="b-link c-red"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
handleEdit(item);
|
||||
}}
|
||||
style={{ padding: '6px', marginRight: 5, minWidth: 'auto' }}
|
||||
>
|
||||
@ -361,7 +302,6 @@ export const ChapterTree = (props: PropInterface) => {
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
if (hasChildren) {
|
||||
const new_arr: Option[] = checkArr(departments, item.id);
|
||||
arr.push({
|
||||
@ -369,86 +309,42 @@ export const ChapterTree = (props: PropInterface) => {
|
||||
key: item.id,
|
||||
children: new_arr,
|
||||
level: level,
|
||||
rowData: item,
|
||||
});
|
||||
} else {
|
||||
arr.push({
|
||||
title: name,
|
||||
key: item.id,
|
||||
level: level,
|
||||
rowData: item,
|
||||
});
|
||||
}
|
||||
}
|
||||
return arr;
|
||||
};
|
||||
const removeItem = (id: number, label: string) => {
|
||||
if (id === 0) {
|
||||
|
||||
const handlePreDeleteItem = () => {
|
||||
if (selectKey.length === 0) {
|
||||
return;
|
||||
}
|
||||
department.checkDestroy(id).then((res: any) => {
|
||||
if (
|
||||
res.data.children &&
|
||||
res.data.children.length === 0 &&
|
||||
res.data.courses &&
|
||||
res.data.courses.length === 0 &&
|
||||
res.data.users &&
|
||||
res.data.users.length === 0
|
||||
) {
|
||||
delUser(id);
|
||||
GetPreDestroyChapterApi(selectKey[0], bookId).then((res: any) => {
|
||||
if (res.data.children && res.data.children.length === 0) {
|
||||
handleDeleteItem();
|
||||
} else {
|
||||
if (res.data.children && res.data.children.length > 0) {
|
||||
console.log(123);
|
||||
modal.warning({
|
||||
title: t('commen.confirmError'),
|
||||
centered: true,
|
||||
okText: t('commen.okText2'),
|
||||
content: (
|
||||
<p>
|
||||
{t('department.unbindText1', {
|
||||
// depName: depNameOBJ[systemLanguage][depDefaultName],
|
||||
})}
|
||||
{t('textbook.chapter.unbindText1')}
|
||||
<span className="c-red">
|
||||
({res.data.children.length}
|
||||
{t('department.unbindText2', {
|
||||
// depName: depNameOBJ[systemLanguage][depDefaultName],
|
||||
})}
|
||||
)
|
||||
{t('textbook.chapter.unbindText2')})
|
||||
</span>
|
||||
,{t('department.unbindText3')}
|
||||
</p>
|
||||
),
|
||||
});
|
||||
} else {
|
||||
modal.warning({
|
||||
title: t('commen.confirmError'),
|
||||
centered: true,
|
||||
okText: t('commen.okText2'),
|
||||
content: (
|
||||
<p>
|
||||
{t('department.unbindText4', {
|
||||
// depName: depNameOBJ[systemLanguage][depDefaultName],
|
||||
})}
|
||||
{res.data.courses && res.data.courses.length > 0 && (
|
||||
<Button
|
||||
style={{ paddingLeft: 4, paddingRight: 4 }}
|
||||
type="link"
|
||||
danger
|
||||
onClick={() => navigate('/course?did=' + id + '&label=' + label)}
|
||||
>
|
||||
({res.data.courses.length}
|
||||
{t('department.unbindText5')}),
|
||||
</Button>
|
||||
)}
|
||||
{res.data.users && res.data.users.length > 0 && (
|
||||
<Button
|
||||
type="link"
|
||||
style={{ paddingLeft: 4, paddingRight: 4 }}
|
||||
danger
|
||||
onClick={() => navigate('/member/index?did=' + id + '&label=' + label)}
|
||||
>
|
||||
({res.data.users.length}
|
||||
{t('department.unbindText6')}),
|
||||
</Button>
|
||||
)}
|
||||
{t('department.unbindText3')}
|
||||
,{t('textbook.chapter.unbindText3')}
|
||||
</p>
|
||||
),
|
||||
});
|
||||
@ -456,26 +352,19 @@ export const ChapterTree = (props: PropInterface) => {
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const resetData = () => {
|
||||
setTreeData([]);
|
||||
setRefresh(!refresh);
|
||||
};
|
||||
|
||||
const delUser = (id: any) => {
|
||||
const handleDeleteItem = () => {
|
||||
confirm({
|
||||
title: t('commen.confirmError'),
|
||||
icon: <ExclamationCircleFilled />,
|
||||
content: t('department.delText', {
|
||||
// depName: depNameOBJ[systemLanguage][depDefaultName],
|
||||
}),
|
||||
content: t('textbook.chapter.delText'),
|
||||
centered: true,
|
||||
okText: t('commen.okText'),
|
||||
cancelText: t('commen.cancelText'),
|
||||
onOk() {
|
||||
department.destroyDepartment(id).then((res: any) => {
|
||||
DestroyChapterApi(selectKey[0], bookId).then(() => {
|
||||
message.success(t('commen.success'));
|
||||
resetData();
|
||||
setTreeData([]);
|
||||
refreshTreeData();
|
||||
});
|
||||
},
|
||||
onCancel() {
|
||||
@ -487,6 +376,7 @@ export const ChapterTree = (props: PropInterface) => {
|
||||
// 打开添加章节弹窗
|
||||
const handleAdd = (parent?: ChapterItemModel) => {
|
||||
setParentChapter(parent || null);
|
||||
setIsEdit(false);
|
||||
setEditingChapter(null);
|
||||
setModalVisible(true);
|
||||
};
|
||||
@ -494,32 +384,15 @@ export const ChapterTree = (props: PropInterface) => {
|
||||
// 打开编辑章节弹窗
|
||||
const handleEdit = (chapter: ChapterItemModel) => {
|
||||
console.log('编辑章节:', chapter);
|
||||
/* const result = getDetailByClassId(treeListData, editSelectId);
|
||||
setEditSelectItem(result);*/
|
||||
setIsEdit(true);
|
||||
setEditingChapter(chapter);
|
||||
setParentChapter(chapter);
|
||||
setModalVisible(true);
|
||||
};
|
||||
|
||||
// 操作完成后清除选中ID
|
||||
const handleOperationComplete = () => {
|
||||
setSelectedNodeId(null);
|
||||
};
|
||||
|
||||
const onSelectTree = (selectedKeys: any, info: any) => {
|
||||
setSelectKey(selectedKeys);
|
||||
};
|
||||
|
||||
const getNodeTitle = (node: any): string => {
|
||||
if (node.title && node.title.props && node.title.props.children) {
|
||||
const titleContent = node.title.props.children;
|
||||
if (typeof titleContent === 'string') {
|
||||
return titleContent;
|
||||
} else if (titleContent.props && titleContent.props.children) {
|
||||
return titleContent.props.children;
|
||||
}
|
||||
}
|
||||
return node.title || '';
|
||||
onSelecet(info.node.rowData);
|
||||
console.log(info);
|
||||
};
|
||||
|
||||
// 关闭弹窗
|
||||
@ -539,6 +412,7 @@ export const ChapterTree = (props: PropInterface) => {
|
||||
|
||||
return (
|
||||
<div className={styles.chapterTree} id={'chapter-tree-container'}>
|
||||
{contextHolder}
|
||||
<div className={styles.chapterTitle}>
|
||||
<div>{title}</div>
|
||||
<div>
|
||||
@ -551,21 +425,12 @@ export const ChapterTree = (props: PropInterface) => {
|
||||
>
|
||||
<PlusOutlined style={{ fontSize: '20px' }} />
|
||||
</Button>
|
||||
<Button
|
||||
type="link"
|
||||
className="b-link c-red mr-8"
|
||||
title={t('textbook.chapter.edit')}
|
||||
disabled={dragEnabled}
|
||||
onClick={(e) => e.preventDefault()}
|
||||
>
|
||||
<FormOutlined style={{ fontSize: '20px' }} />
|
||||
</Button>
|
||||
<Button
|
||||
type="link"
|
||||
className="b-link c-red mr-8"
|
||||
title={t('textbook.chapter.delete')}
|
||||
disabled={dragEnabled}
|
||||
onClick={(e) => e.preventDefault()}
|
||||
disabled={dragEnabled || selectKey.length === 0}
|
||||
onClick={handlePreDeleteItem}
|
||||
>
|
||||
<DeleteOutlined style={{ fontSize: '20px' }} />
|
||||
</Button>
|
||||
@ -600,7 +465,7 @@ export const ChapterTree = (props: PropInterface) => {
|
||||
)}
|
||||
{dragEnabled && <div className={'primary'}> {t('textbook.chapter.tips')}</div>}
|
||||
{isMoreThanThree && <div className={'primary'}> {t('textbook.chapter.tips3')}</div>}
|
||||
{treeData.length > 0 && (
|
||||
{treeData.length > 0 ? (
|
||||
<div className={`${styles[`bottom-tree-box`]}`}>
|
||||
<Tree
|
||||
draggable={
|
||||
@ -633,6 +498,8 @@ export const ChapterTree = (props: PropInterface) => {
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<Empty style={{ marginTop: 40 }}></Empty>
|
||||
)}
|
||||
</div>
|
||||
{modalVisible && !dragEnabled && (
|
||||
@ -641,6 +508,7 @@ export const ChapterTree = (props: PropInterface) => {
|
||||
isEdit={isEdit}
|
||||
editData={editingChapter}
|
||||
bookId={bookId}
|
||||
parentData={parentChapter}
|
||||
onCancel={handleModalCancel}
|
||||
confirmLoading={submitLoading}
|
||||
onSuccess={refreshTreeData}
|
||||
|
||||
@ -9,10 +9,13 @@ export interface Chapter {
|
||||
}
|
||||
|
||||
export interface EditorProps {
|
||||
parentTitle: string;
|
||||
bookId: number;
|
||||
isLoading: boolean;
|
||||
chapterId: string;
|
||||
chapterTitle: string;
|
||||
initialContent?: string;
|
||||
onSave: (chapterId: string, content: string) => void;
|
||||
onSave: (data: any) => void;
|
||||
onContentChange: (chapterId: string, content: string) => void;
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user