This commit is contained in:
longdayi 2025-02-27 22:37:39 +08:00
commit bc5dd8e9dc
4 changed files with 77 additions and 70 deletions

View File

@ -36,7 +36,6 @@
"@nice/iconer": "workspace:^", "@nice/iconer": "workspace:^",
"@nice/utils": "workspace:^", "@nice/utils": "workspace:^",
"mind-elixir": "workspace:^", "mind-elixir": "workspace:^",
"@mind-elixir/node-menu": "workspace:*",
"@nice/ui": "workspace:^", "@nice/ui": "workspace:^",
"@tanstack/query-async-storage-persister": "^5.51.9", "@tanstack/query-async-storage-persister": "^5.51.9",
"@tanstack/react-query": "^5.51.21", "@tanstack/react-query": "^5.51.21",

View File

@ -3,12 +3,11 @@ import NodeMenu from "./NodeMenu";
import { api, usePost } from "@nice/client"; import { api, usePost } from "@nice/client";
import { import {
ObjectType, ObjectType,
PathDto,
postDetailSelect, postDetailSelect,
PostDto,
PostType, PostType,
Prisma, Prisma,
RolePerms, RolePerms,
Taxonomy,
} from "@nice/common"; } from "@nice/common";
import TermSelect from "../../models/term/term-select"; import TermSelect from "../../models/term/term-select";
import DepartmentSelect from "../../models/department/department-select"; import DepartmentSelect from "../../models/department/department-select";
@ -19,44 +18,12 @@ import MindElixir from "mind-elixir";
import { useTusUpload } from "@web/src/hooks/useTusUpload"; import { useTusUpload } from "@web/src/hooks/useTusUpload";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import { useAuth } from "@web/src/providers/auth-provider"; import { useAuth } from "@web/src/providers/auth-provider";
const MIND_OPTIONS = { import { MIND_OPTIONS } from "./constant";
direction: MindElixir.SIDE,
draggable: true,
contextMenu: true,
toolBar: true,
nodeMenu: true,
keypress: true,
locale: "zh_CN" as const,
theme: {
name: "Latte",
palette: [
"#dd7878",
"#ea76cb",
"#8839ef",
"#e64553",
"#fe640b",
"#df8e1d",
"#40a02b",
"#209fb5",
"#1e66f5",
"#7287fd",
],
cssVar: {
"--main-color": "#444446",
"--main-bgcolor": "#ffffff",
"--color": "#777777",
"--bgcolor": "#f6f6f6",
"--panel-color": "#444446",
"--panel-bgcolor": "#ffffff",
"--panel-border-color": "#eaeaea",
},
},
};
export default function MindEditor({ id }: { id?: string }) { export default function MindEditor({ id }: { id?: string }) {
const containerRef = useRef<HTMLDivElement>(null); const containerRef = useRef<HTMLDivElement>(null);
const [instance, setInstance] = useState<MindElixirInstance | null>(null); const [instance, setInstance] = useState<MindElixirInstance | null>(null);
const { isAuthenticated, user, hasSomePermissions } = useAuth(); const { isAuthenticated, user, hasSomePermissions } = useAuth();
const { data: post, isLoading }: { data: PostDto; isLoading: boolean } = const { data: post, isLoading }: { data: PathDto; isLoading: boolean } =
api.post.findFirst.useQuery({ api.post.findFirst.useQuery({
where: { where: {
id, id,
@ -65,9 +32,9 @@ export default function MindEditor({ id }: { id?: string }) {
}); });
const canEdit: boolean = useMemo(() => { const canEdit: boolean = useMemo(() => {
//登录了且是作者、超管、无id新建模式 //登录了且是作者、超管、无id新建模式
const isAuth = isAuthenticated && user?.id == post?.author.id const isAuth = isAuthenticated && user?.id === post?.author?.id;
return !Boolean(id) || isAuth || hasSomePermissions(RolePerms.MANAGE_ANY_POST); return !!id || isAuth || hasSomePermissions(RolePerms.MANAGE_ANY_POST);
}, [user]) }, [user]);
const navigate = useNavigate(); const navigate = useNavigate();
const { create, update } = usePost(); const { create, update } = usePost();
const { data: taxonomies } = api.taxonomy.getAll.useQuery({ const { data: taxonomies } = api.taxonomy.getAll.useQuery({
@ -90,7 +57,6 @@ export default function MindEditor({ id }: { id?: string }) {
form.setFieldsValue(formData); form.setFieldsValue(formData);
} }
}, [post, form, instance, id]); }, [post, form, instance, id]);
useEffect(() => { useEffect(() => {
if (!containerRef.current) return; if (!containerRef.current) return;
const mind = new MindElixir({ const mind = new MindElixir({
@ -98,8 +64,8 @@ export default function MindEditor({ id }: { id?: string }) {
el: containerRef.current, el: containerRef.current,
before: { before: {
beginEdit() { beginEdit() {
return canEdit return canEdit;
} },
}, },
draggable: canEdit, // 禁用拖拽 draggable: canEdit, // 禁用拖拽
contextMenu: canEdit, // 禁用右键菜单 contextMenu: canEdit, // 禁用右键菜单
@ -115,7 +81,9 @@ export default function MindEditor({ id }: { id?: string }) {
if ((!id || post) && instance) { if ((!id || post) && instance) {
containerRef.current.hidden = false; containerRef.current.hidden = false;
instance.toCenter(); instance.toCenter();
instance.refresh((post as any)?.meta); if (post?.meta?.nodeData) {
instance.refresh(post?.meta);
}
} }
}, [id, post, instance]); }, [id, post, instance]);
const handleSave = async () => { const handleSave = async () => {
@ -182,17 +150,13 @@ export default function MindEditor({ id }: { id?: string }) {
); );
}; };
useEffect(() => { useEffect(() => {
containerRef.current.style.height = `${Math.floor(window.innerHeight / 1.25)}px` containerRef.current.style.height = `${Math.floor(window.innerHeight / 1.25)}px`;
}, []) }, []);
return ( return (
<div className="grid grid-cols-1 flex-col w-[90vw] my-5 h-[80vh] border rounded-lg mx-auto"> <div className="grid grid-cols-1 flex-col w-[90vw] my-5 h-[80vh] border rounded-lg mx-auto">
{canEdit && taxonomies && ( {canEdit && taxonomies && (
<Form <Form form={form} className=" bg-white p-4 ">
form={form}
className=" bg-white p-4 ">
<div className="flex items-center justify-between gap-4"> <div className="flex items-center justify-between gap-4">
<div className="flex items-center gap-4"> <div className="flex items-center gap-4">
{taxonomies.map((tax, index) => ( {taxonomies.map((tax, index) => (
@ -219,28 +183,40 @@ export default function MindEditor({ id }: { id?: string }) {
/> />
</Form.Item> </Form.Item>
</div> </div>
<Button ghost type="primary" onSubmit={(e) => e.preventDefault()} onClick={handleSave}> <Button
ghost
type="primary"
onSubmit={(e) => e.preventDefault()}
onClick={handleSave}>
{id ? "更新" : "保存"} {id ? "更新" : "保存"}
</Button> </Button>
</div> </div>
</Form> </Form>
)} )}
<div ref={containerRef} className="w-full" onContextMenu={(e) => e.preventDefault()} /> <div
ref={containerRef}
className="w-full"
onContextMenu={(e) => e.preventDefault()}
/>
{canEdit && instance && <NodeMenu mind={instance} />} {canEdit && instance && <NodeMenu mind={instance} />}
{isLoading && ( {
<div isLoading && (
className="py-64 justify-center flex" <div
style={{ height: "calc(100vh - 287px)" }}> className="py-64 justify-center flex"
<Spin size="large"></Spin> style={{ height: "calc(100vh - 287px)" }}>
</div> <Spin size="large"></Spin>
)} </div>
{!post && id && !isLoading && ( )
<div }
className="py-64" {
style={{ height: "calc(100vh - 287px)" }}> !post && id && !isLoading && (
<Empty></Empty> <div
</div> className="py-64"
)} style={{ height: "calc(100vh - 287px)" }}>
</div> <Empty></Empty>
</div>
)
}
</div >
); );
} }

View File

@ -0,0 +1,34 @@
import MindElixir from "mind-elixir";
export const MIND_OPTIONS = {
direction: MindElixir.SIDE,
draggable: true,
contextMenu: true,
toolBar: true,
nodeMenu: true,
keypress: true,
locale: "zh_CN" as const,
theme: {
name: "Latte",
palette: [
"#dd7878",
"#ea76cb",
"#8839ef",
"#e64553",
"#fe640b",
"#df8e1d",
"#40a02b",
"#209fb5",
"#1e66f5",
"#7287fd",
],
cssVar: {
"--main-color": "#444446",
"--main-bgcolor": "#ffffff",
"--color": "#777777",
"--bgcolor": "#f6f6f6",
"--panel-color": "#444446",
"--panel-bgcolor": "#ffffff",
"--panel-border-color": "#eaeaea",
},
},
};

View File

@ -226,8 +226,6 @@ model Post {
ancestors PostAncestry[] @relation("DescendantPosts") ancestors PostAncestry[] @relation("DescendantPosts")
descendants PostAncestry[] @relation("AncestorPosts") descendants PostAncestry[] @relation("AncestorPosts")
resources Resource[] // 附件列表 resources Resource[] // 附件列表
// watchableStaffs Staff[] @relation("post_watch_staff")
// watchableDepts Department[] @relation("post_watch_dept") // 可观看的部门列表,关联 Department 模型
meta Json? // 封面url 视频url objectives具体的学习目标 rating评分Int meta Json? // 封面url 视频url objectives具体的学习目标 rating评分Int
// 索引 // 索引