add
This commit is contained in:
parent
9034e61229
commit
eba90139e4
|
@ -3,10 +3,10 @@ import { Position, getBezierPath } from "reactflow";
|
||||||
|
|
||||||
import { getBasePath } from ".";
|
import { getBasePath } from ".";
|
||||||
import {
|
import {
|
||||||
kBaseMarkerColor,
|
kBaseMarkerColor,
|
||||||
kBaseMarkerColors,
|
kBaseMarkerColors,
|
||||||
kNoMarkerColor,
|
kNoMarkerColor,
|
||||||
kYesMarkerColor,
|
kYesMarkerColor,
|
||||||
} from "../../components/Edges/Marker";
|
} from "../../components/Edges/Marker";
|
||||||
import { isEqual } from "../../utils/diff";
|
import { isEqual } from "../../utils/diff";
|
||||||
import { EdgeLayout, ReactFlowEdgeWithData } from "../../data/types";
|
import { EdgeLayout, ReactFlowEdgeWithData } from "../../data/types";
|
||||||
|
@ -14,187 +14,187 @@ import { kReactFlow } from "../../states/reactflow";
|
||||||
import { getPathWithRoundCorners } from "./edge";
|
import { getPathWithRoundCorners } from "./edge";
|
||||||
|
|
||||||
interface EdgeStyle {
|
interface EdgeStyle {
|
||||||
color: string;
|
color: string;
|
||||||
edgeType: "solid" | "dashed";
|
edgeType: "solid" | "dashed";
|
||||||
pathType: "base" | "bezier";
|
pathType: "base" | "bezier";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the style of the connection line
|
* Get the style of the connection line
|
||||||
*
|
*
|
||||||
* 1. When there are more than 3 edges connecting to both ends of the Node, use multiple colors to distinguish the edges.
|
* 1. When there are more than 3 edges connecting to both ends of the Node, use multiple colors to distinguish the edges.
|
||||||
* 2. When the connection line goes backward or connects to a hub Node, use dashed lines to distinguish the edges.
|
* 2. When the connection line goes backward or connects to a hub Node, use dashed lines to distinguish the edges.
|
||||||
* 3. When the connection line goes from a hub to a Node, use bezier path.
|
* 3. When the connection line goes from a hub to a Node, use bezier path.
|
||||||
*/
|
*/
|
||||||
export const getEdgeStyles = (props: {
|
export const getEdgeStyles = (props: {
|
||||||
id: string;
|
id: string;
|
||||||
isBackward: boolean;
|
isBackward: boolean;
|
||||||
}): EdgeStyle => {
|
}): EdgeStyle => {
|
||||||
const { id, isBackward } = props;
|
const { id, isBackward } = props;
|
||||||
const idx = parseInt(lastOf(id.split("#")) ?? "0", 10);
|
const idx = parseInt(lastOf(id.split("#")) ?? "0", 10);
|
||||||
if (isBackward) {
|
if (isBackward) {
|
||||||
// Use dashed lines to distinguish the edges when the connection line goes backward or connects to a hub Node
|
// Use dashed lines to distinguish the edges when the connection line goes backward or connects to a hub Node
|
||||||
return { color: kNoMarkerColor, edgeType: "dashed", pathType: "base" };
|
return { color: kNoMarkerColor, edgeType: "dashed", pathType: "base" };
|
||||||
}
|
}
|
||||||
const edge: ReactFlowEdgeWithData = kReactFlow.instance!.getEdge(id)!;
|
const edge: ReactFlowEdgeWithData = kReactFlow.instance!.getEdge(id)!;
|
||||||
if (edge.data!.targetPort.edges > 2) {
|
if (edge.data!.targetPort.edges > 2) {
|
||||||
// Use dashed bezier path when the connection line connects to a hub Node
|
// Use dashed bezier path when the connection line connects to a hub Node
|
||||||
return {
|
return {
|
||||||
color: kYesMarkerColor,
|
color: kYesMarkerColor,
|
||||||
edgeType: "dashed",
|
edgeType: "dashed",
|
||||||
pathType: "bezier",
|
pathType: "bezier",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if (edge.data!.sourcePort.edges > 2) {
|
if (edge.data!.sourcePort.edges > 2) {
|
||||||
// Use multiple colors to distinguish the edges when there are more than 3 edges connecting to both ends of the Node
|
// Use multiple colors to distinguish the edges when there are more than 3 edges connecting to both ends of the Node
|
||||||
return {
|
return {
|
||||||
color: kBaseMarkerColors[idx % kBaseMarkerColors.length],
|
color: kBaseMarkerColors[idx % kBaseMarkerColors.length],
|
||||||
edgeType: "solid",
|
edgeType: "solid",
|
||||||
pathType: "base",
|
pathType: "base",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return { color: kBaseMarkerColor, edgeType: "solid", pathType: "base" };
|
return { color: kBaseMarkerColor, edgeType: "solid", pathType: "base" };
|
||||||
};
|
};
|
||||||
|
|
||||||
interface ILayoutEdge {
|
interface ILayoutEdge {
|
||||||
id: string;
|
id: string;
|
||||||
layout?: EdgeLayout;
|
layout?: EdgeLayout;
|
||||||
offset: number;
|
offset: number;
|
||||||
borderRadius: number;
|
borderRadius: number;
|
||||||
pathType: EdgeStyle["pathType"];
|
pathType: EdgeStyle["pathType"];
|
||||||
source: string;
|
source: string;
|
||||||
target: string;
|
target: string;
|
||||||
sourceX: number;
|
sourceX: number;
|
||||||
sourceY: number;
|
sourceY: number;
|
||||||
targetX: number;
|
targetX: number;
|
||||||
targetY: number;
|
targetY: number;
|
||||||
sourcePosition: Position;
|
sourcePosition: Position;
|
||||||
targetPosition: Position;
|
targetPosition: Position;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function layoutEdge({
|
export function layoutEdge({
|
||||||
id,
|
id,
|
||||||
layout,
|
layout,
|
||||||
offset,
|
offset,
|
||||||
borderRadius,
|
borderRadius,
|
||||||
pathType,
|
pathType,
|
||||||
source,
|
source,
|
||||||
target,
|
target,
|
||||||
sourceX,
|
sourceX,
|
||||||
sourceY,
|
sourceY,
|
||||||
targetX,
|
targetX,
|
||||||
targetY,
|
targetY,
|
||||||
sourcePosition,
|
sourcePosition,
|
||||||
targetPosition,
|
targetPosition,
|
||||||
}: ILayoutEdge): EdgeLayout {
|
}: ILayoutEdge): EdgeLayout {
|
||||||
const relayoutDeps = [sourceX, sourceY, targetX, targetY];
|
const relayoutDeps = [sourceX, sourceY, targetX, targetY];
|
||||||
const needRelayout = !isEqual(relayoutDeps, layout?.deps?.relayoutDeps);
|
const needRelayout = !isEqual(relayoutDeps, layout?.deps?.relayoutDeps);
|
||||||
const reBuildPathDeps = layout?.points;
|
const reBuildPathDeps = layout?.points;
|
||||||
const needReBuildPath = !isEqual(
|
const needReBuildPath = !isEqual(
|
||||||
reBuildPathDeps,
|
reBuildPathDeps,
|
||||||
layout?.deps?.reBuildPathDeps
|
layout?.deps?.reBuildPathDeps
|
||||||
);
|
);
|
||||||
let newLayout = layout;
|
let newLayout = layout;
|
||||||
if (needRelayout) {
|
if (needRelayout) {
|
||||||
newLayout = _layoutEdge({
|
newLayout = _layoutEdge({
|
||||||
id,
|
id,
|
||||||
offset,
|
offset,
|
||||||
borderRadius,
|
borderRadius,
|
||||||
pathType,
|
pathType,
|
||||||
source,
|
source,
|
||||||
target,
|
target,
|
||||||
sourceX,
|
sourceX,
|
||||||
sourceY,
|
sourceY,
|
||||||
targetX,
|
targetX,
|
||||||
targetY,
|
targetY,
|
||||||
sourcePosition,
|
sourcePosition,
|
||||||
targetPosition,
|
targetPosition,
|
||||||
});
|
});
|
||||||
} else if (needReBuildPath) {
|
} else if (needReBuildPath) {
|
||||||
newLayout = _layoutEdge({
|
newLayout = _layoutEdge({
|
||||||
layout,
|
layout,
|
||||||
id,
|
id,
|
||||||
offset,
|
offset,
|
||||||
borderRadius,
|
borderRadius,
|
||||||
pathType,
|
pathType,
|
||||||
source,
|
source,
|
||||||
target,
|
target,
|
||||||
sourceX,
|
sourceX,
|
||||||
sourceY,
|
sourceY,
|
||||||
targetX,
|
targetX,
|
||||||
targetY,
|
targetY,
|
||||||
sourcePosition,
|
sourcePosition,
|
||||||
targetPosition,
|
targetPosition,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
newLayout!.deps = deepClone({ relayoutDeps, reBuildPathDeps });
|
newLayout!.deps = deepClone({ relayoutDeps, reBuildPathDeps });
|
||||||
return newLayout!;
|
return newLayout!;
|
||||||
}
|
}
|
||||||
|
|
||||||
function _layoutEdge({
|
function _layoutEdge({
|
||||||
id,
|
id,
|
||||||
layout,
|
layout,
|
||||||
offset,
|
offset,
|
||||||
borderRadius,
|
borderRadius,
|
||||||
pathType,
|
pathType,
|
||||||
source,
|
source,
|
||||||
target,
|
target,
|
||||||
sourceX,
|
sourceX,
|
||||||
sourceY,
|
sourceY,
|
||||||
targetX,
|
targetX,
|
||||||
targetY,
|
targetY,
|
||||||
sourcePosition,
|
sourcePosition,
|
||||||
targetPosition,
|
targetPosition,
|
||||||
}: ILayoutEdge): EdgeLayout {
|
}: ILayoutEdge): EdgeLayout {
|
||||||
const _pathType: EdgeStyle["pathType"] = pathType;
|
const _pathType: EdgeStyle["pathType"] = pathType;
|
||||||
if (_pathType === "bezier") {
|
if (_pathType === "bezier") {
|
||||||
const [path, labelX, labelY] = getBezierPath({
|
const [path, labelX, labelY] = getBezierPath({
|
||||||
sourceX,
|
sourceX,
|
||||||
sourceY,
|
sourceY,
|
||||||
targetX,
|
targetX,
|
||||||
targetY,
|
targetY,
|
||||||
sourcePosition,
|
sourcePosition,
|
||||||
targetPosition,
|
targetPosition,
|
||||||
});
|
});
|
||||||
const points = [
|
const points = [
|
||||||
{
|
{
|
||||||
id: "source-" + id,
|
id: "source-" + id,
|
||||||
x: sourceX,
|
x: sourceX,
|
||||||
y: sourceY,
|
y: sourceY,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "target-" + id,
|
id: "target-" + id,
|
||||||
x: targetX,
|
x: targetX,
|
||||||
y: targetY,
|
y: targetY,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
return {
|
return {
|
||||||
path,
|
path,
|
||||||
points,
|
points,
|
||||||
inputPoints: points,
|
inputPoints: points,
|
||||||
labelPosition: {
|
labelPosition: {
|
||||||
x: labelX,
|
x: labelX,
|
||||||
y: labelY,
|
y: labelY,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((layout?.points?.length ?? 0) > 1) {
|
if ((layout?.points?.length ?? 0) > 1) {
|
||||||
layout!.path = getPathWithRoundCorners(layout!.points, borderRadius);
|
layout!.path = getPathWithRoundCorners(layout!.points, borderRadius);
|
||||||
return layout!;
|
return layout!;
|
||||||
}
|
}
|
||||||
|
|
||||||
return getBasePath({
|
return getBasePath({
|
||||||
id,
|
id,
|
||||||
offset,
|
offset,
|
||||||
borderRadius,
|
borderRadius,
|
||||||
source,
|
source,
|
||||||
target,
|
target,
|
||||||
sourceX,
|
sourceX,
|
||||||
sourceY,
|
sourceY,
|
||||||
targetX,
|
targetX,
|
||||||
targetY,
|
targetY,
|
||||||
sourcePosition,
|
sourcePosition,
|
||||||
targetPosition,
|
targetPosition,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,109 +1,6 @@
|
||||||
|
|
||||||
// import React, { useState } from 'react';
|
|
||||||
// import { Form, Select, Input } from 'antd';
|
|
||||||
// import axios from 'axios';
|
|
||||||
|
|
||||||
// const { Option } = Select;
|
|
||||||
|
|
||||||
// // 假数据
|
|
||||||
// const fakePostsData = Array.from({ length: 15 }, (_, index) => ({
|
|
||||||
// id: index + 1,
|
|
||||||
// type: 'Lecture',
|
|
||||||
// name: `Lecture ${index + 1}`,
|
|
||||||
// description: `This is lecture number ${index + 1}`,
|
|
||||||
// }));
|
|
||||||
|
|
||||||
// // 模拟获取数据的函数
|
|
||||||
// async function fetchPosts(query = '') {
|
|
||||||
// // 在实际应用中,这里应该是一个真实的API调用
|
|
||||||
// return fakePostsData.filter(post =>
|
|
||||||
// post.name.toLowerCase().includes(query.toLowerCase()) ||
|
|
||||||
// post.description.toLowerCase().includes(query.toLowerCase())
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
// const PostSelector = ({ value, onChange }) => {
|
|
||||||
// const [posts, setPosts] = useState([]);
|
|
||||||
// const [searchValue, setSearchValue] = useState('');
|
|
||||||
|
|
||||||
// const handleSearch = async (query) => {
|
|
||||||
// setSearchValue(query);
|
|
||||||
// const result = await fetchPosts(query);
|
|
||||||
// setPosts(result);
|
|
||||||
// };
|
|
||||||
|
|
||||||
// const handlePostChange = (selectedIds) => {
|
|
||||||
// onChange(selectedIds); // 更新父组件的状态
|
|
||||||
// };
|
|
||||||
|
|
||||||
// const renderOption = (post) => (
|
|
||||||
// <Option key={post.id} value={post.id.toString()}>
|
|
||||||
// {post.name} - {post.description}
|
|
||||||
// </Option>
|
|
||||||
// );
|
|
||||||
|
|
||||||
// return (
|
|
||||||
// <Select
|
|
||||||
// mode="multiple" // 支持多选
|
|
||||||
// showSearch
|
|
||||||
// placeholder="请选择或搜索讲座"
|
|
||||||
// optionFilterProp="children"
|
|
||||||
// onSearch={handleSearch}
|
|
||||||
// filterOption={false} // 禁用默认过滤,因为我们已经在 onSearch 中实现了自定义过滤
|
|
||||||
// notFoundContent={''}
|
|
||||||
// style={{ width: '100%' }}
|
|
||||||
// value={value || []}
|
|
||||||
// onChange={handlePostChange}
|
|
||||||
// >
|
|
||||||
// {posts.map(renderOption)}
|
|
||||||
// </Select>
|
|
||||||
// );
|
|
||||||
// };
|
|
||||||
|
|
||||||
// const PostForm = () => {
|
|
||||||
// const [form] = Form.useForm();
|
|
||||||
|
|
||||||
// const onFinish = (values) => {
|
|
||||||
// console.log('Received values of form: ', values);
|
|
||||||
// };
|
|
||||||
|
|
||||||
// return (
|
|
||||||
// <Form
|
|
||||||
// form={form}
|
|
||||||
// name="postForm"
|
|
||||||
// onFinish={onFinish}
|
|
||||||
// initialValues={{
|
|
||||||
// postIds: [], // 初始值为空数组
|
|
||||||
// }}
|
|
||||||
// >
|
|
||||||
// <Form.Item
|
|
||||||
// name="postIds"
|
|
||||||
// label="选择讲座"
|
|
||||||
// rules={[{ required: true, message: '请选择至少一个讲座!' }]}
|
|
||||||
// >
|
|
||||||
// <PostSelector
|
|
||||||
// value={form.getFieldValue('postIds')}
|
|
||||||
// onChange={(selectedIds) => form.setFieldsValue({ postIds: selectedIds })}
|
|
||||||
// />
|
|
||||||
// </Form.Item>
|
|
||||||
|
|
||||||
// <Form.Item>
|
|
||||||
// <button type="submit">提交</button>
|
|
||||||
// </Form.Item>
|
|
||||||
// </Form>
|
|
||||||
// );
|
|
||||||
// };
|
|
||||||
|
|
||||||
// export default PostForm;
|
|
||||||
import { api } from "@nice/client";
|
import { api } from "@nice/client";
|
||||||
import { Button, Select } from "antd";
|
import { Select } from "antd";
|
||||||
import {
|
import { Lecture, postDetailSelect, Prisma } from "@nice/common";
|
||||||
Lecture,
|
|
||||||
lectureDetailSelect,
|
|
||||||
postDetailSelect,
|
|
||||||
postUnDetailSelect,
|
|
||||||
Prisma,
|
|
||||||
} from "@nice/common";
|
|
||||||
import { useMemo, useState } from "react";
|
import { useMemo, useState } from "react";
|
||||||
import PostSelectOption from "./PostSelectOption";
|
import PostSelectOption from "./PostSelectOption";
|
||||||
import { DefaultArgs } from "@prisma/client/runtime/library";
|
import { DefaultArgs } from "@prisma/client/runtime/library";
|
||||||
|
@ -205,4 +102,3 @@ export default function PostSelect({
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,95 +0,0 @@
|
||||||
import {
|
|
||||||
Post,
|
|
||||||
Department,
|
|
||||||
Staff,
|
|
||||||
Enrollment,
|
|
||||||
Taxonomy,
|
|
||||||
Term,
|
|
||||||
} from "@prisma/client";
|
|
||||||
import { StaffDto } from "./staff";
|
|
||||||
import { TermDto } from "./term";
|
|
||||||
import { ResourceDto } from "./resource";
|
|
||||||
import { DepartmentDto } from "./department";
|
|
||||||
|
|
||||||
export type PostComment = {
|
|
||||||
id: string;
|
|
||||||
type: string;
|
|
||||||
title: string;
|
|
||||||
content: string;
|
|
||||||
authorId: string;
|
|
||||||
domainId: string;
|
|
||||||
referenceId: string;
|
|
||||||
resources: string[];
|
|
||||||
createdAt: Date;
|
|
||||||
updatedAt: Date;
|
|
||||||
parentId: string;
|
|
||||||
author: {
|
|
||||||
id: string;
|
|
||||||
showname: string;
|
|
||||||
username: string;
|
|
||||||
avatar: string;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
export type PostDto = Post & {
|
|
||||||
readed: boolean;
|
|
||||||
readedCount: number;
|
|
||||||
author: StaffDto;
|
|
||||||
limitedComments: PostComment[];
|
|
||||||
commentsCount: number;
|
|
||||||
perms?: {
|
|
||||||
delete: boolean;
|
|
||||||
// edit: boolean;
|
|
||||||
};
|
|
||||||
watchableDepts: Department[];
|
|
||||||
watchableStaffs: Staff[];
|
|
||||||
terms: TermDto[];
|
|
||||||
depts: DepartmentDto[];
|
|
||||||
meta?: {
|
|
||||||
thumbnail?: string;
|
|
||||||
views?: number;
|
|
||||||
};
|
|
||||||
studentIds?: string[];
|
|
||||||
};
|
|
||||||
|
|
||||||
export type LectureMeta = {
|
|
||||||
type?: string;
|
|
||||||
views?: number;
|
|
||||||
videoUrl?: string;
|
|
||||||
videoThumbnail?: string;
|
|
||||||
videoIds?: string[];
|
|
||||||
videoThumbnailIds?: string[];
|
|
||||||
};
|
|
||||||
|
|
||||||
export type Lecture = Post & {
|
|
||||||
resources?: ResourceDto[];
|
|
||||||
meta?: LectureMeta;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type SectionMeta = {
|
|
||||||
objectives?: string[];
|
|
||||||
};
|
|
||||||
export type Section = Post & {
|
|
||||||
meta?: SectionMeta;
|
|
||||||
};
|
|
||||||
export type SectionDto = Section & {
|
|
||||||
lectures: Lecture[];
|
|
||||||
};
|
|
||||||
export type CourseMeta = {
|
|
||||||
thumbnail?: string;
|
|
||||||
|
|
||||||
objectives?: string[];
|
|
||||||
views?: number;
|
|
||||||
likes?: number;
|
|
||||||
hates?: number;
|
|
||||||
};
|
|
||||||
export type Course = PostDto & {
|
|
||||||
meta?: CourseMeta;
|
|
||||||
};
|
|
||||||
export type CourseDto = Course & {
|
|
||||||
enrollments?: Enrollment[];
|
|
||||||
sections?: SectionDto[];
|
|
||||||
terms: TermDto[];
|
|
||||||
lectureCount?: number;
|
|
||||||
depts: Department[];
|
|
||||||
studentIds: string[];
|
|
||||||
};
|
|
|
@ -1,9 +0,0 @@
|
||||||
import { useEffect, useRef } from 'react';
|
|
||||||
|
|
||||||
export default function MindMapEditor(): JSX.Element {
|
|
||||||
const containerRef = useRef<HTMLDivElement>(null);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
Loading…
Reference in New Issue