add
This commit is contained in:
parent
f8659b90ca
commit
c9199a7709
|
@ -1,38 +1,42 @@
|
||||||
import React from 'react';
|
import React from "react";
|
||||||
import { useLocation, Link, useMatches } from 'react-router-dom';
|
import { useLocation, Link, useMatches } from "react-router-dom";
|
||||||
import { theme } from 'antd';
|
import { theme } from "antd";
|
||||||
import { RightOutlined } from '@ant-design/icons';
|
import { RightOutlined } from "@ant-design/icons";
|
||||||
|
|
||||||
export default function Breadcrumb() {
|
export default function Breadcrumb() {
|
||||||
let matches = useMatches();
|
const matches = useMatches();
|
||||||
const { token } = theme.useToken()
|
const { token } = theme.useToken();
|
||||||
|
|
||||||
let crumbs = matches
|
const crumbs = matches
|
||||||
// first get rid of any matches that don't have handle and crumb
|
// first get rid of any matches that don't have handle and crumb
|
||||||
.filter((match) => Boolean((match.handle as any)?.crumb))
|
.filter((match) => Boolean((match.handle as any)?.crumb))
|
||||||
// now map them into an array of elements, passing the loader
|
// now map them into an array of elements, passing the loader
|
||||||
// data to each one
|
// data to each one
|
||||||
.map((match) => (match.handle as any).crumb(match.data));
|
.map((match) => (match.handle as any).crumb(match.data));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ol className='flex items-center space-x-2 text-gray-600'>
|
<ol className="flex items-center space-x-2 text-gray-600">
|
||||||
{crumbs.map((crumb, index) => (
|
{crumbs.map((crumb, index) => (
|
||||||
<React.Fragment key={index}>
|
<React.Fragment key={index}>
|
||||||
<li className={`inline-flex items-center `}
|
<li
|
||||||
style={{
|
className={`inline-flex items-center `}
|
||||||
color: (index === crumbs.length - 1) ? token.colorPrimaryText : token.colorTextSecondary,
|
style={{
|
||||||
fontWeight: (index === crumbs.length - 1) ? "bold" : "normal",
|
color:
|
||||||
}}
|
index === crumbs.length - 1
|
||||||
>
|
? token.colorPrimaryText
|
||||||
{crumb}
|
: token.colorTextSecondary,
|
||||||
</li>
|
fontWeight:
|
||||||
{index < crumbs.length - 1 && (
|
index === crumbs.length - 1 ? "bold" : "normal",
|
||||||
<li className='mx-2'>
|
}}>
|
||||||
<RightOutlined></RightOutlined>
|
{crumb}
|
||||||
</li>
|
</li>
|
||||||
)}
|
{index < crumbs.length - 1 && (
|
||||||
</React.Fragment>
|
<li className="mx-2">
|
||||||
))}
|
<RightOutlined></RightOutlined>
|
||||||
</ol>
|
</li>
|
||||||
);
|
)}
|
||||||
|
</React.Fragment>
|
||||||
|
))}
|
||||||
|
</ol>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,10 +3,17 @@ import {
|
||||||
courseDetailSelect,
|
courseDetailSelect,
|
||||||
CourseDto,
|
CourseDto,
|
||||||
Lecture,
|
Lecture,
|
||||||
|
RolePerms,
|
||||||
VisitType,
|
VisitType,
|
||||||
} from "@nice/common";
|
} from "@nice/common";
|
||||||
import { useAuth } from "@web/src/providers/auth-provider";
|
import { useAuth } from "@web/src/providers/auth-provider";
|
||||||
import React, { createContext, ReactNode, useEffect, useState } from "react";
|
import React, {
|
||||||
|
createContext,
|
||||||
|
ReactNode,
|
||||||
|
useEffect,
|
||||||
|
useMemo,
|
||||||
|
useState,
|
||||||
|
} from "react";
|
||||||
import { useNavigate, useParams } from "react-router-dom";
|
import { useNavigate, useParams } from "react-router-dom";
|
||||||
|
|
||||||
interface CourseDetailContextType {
|
interface CourseDetailContextType {
|
||||||
|
@ -19,11 +26,14 @@ interface CourseDetailContextType {
|
||||||
lectureIsLoading?: boolean;
|
lectureIsLoading?: boolean;
|
||||||
isHeaderVisible: boolean; // 新增
|
isHeaderVisible: boolean; // 新增
|
||||||
setIsHeaderVisible: (visible: boolean) => void; // 新增
|
setIsHeaderVisible: (visible: boolean) => void; // 新增
|
||||||
|
canEdit?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface CourseFormProviderProps {
|
interface CourseFormProviderProps {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
editId?: string; // 添加 editId 参数
|
editId?: string; // 添加 editId 参数
|
||||||
}
|
}
|
||||||
|
|
||||||
export const CourseDetailContext =
|
export const CourseDetailContext =
|
||||||
createContext<CourseDetailContextType | null>(null);
|
createContext<CourseDetailContextType | null>(null);
|
||||||
export function CourseDetailProvider({
|
export function CourseDetailProvider({
|
||||||
|
@ -32,8 +42,9 @@ export function CourseDetailProvider({
|
||||||
}: CourseFormProviderProps) {
|
}: CourseFormProviderProps) {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { read } = useVisitor();
|
const { read } = useVisitor();
|
||||||
const { user } = useAuth();
|
const { user, hasSomePermissions } = useAuth();
|
||||||
const { lectureId } = useParams();
|
const { lectureId } = useParams();
|
||||||
|
|
||||||
const { data: course, isLoading }: { data: CourseDto; isLoading: boolean } =
|
const { data: course, isLoading }: { data: CourseDto; isLoading: boolean } =
|
||||||
(api.post as any).findFirst.useQuery(
|
(api.post as any).findFirst.useQuery(
|
||||||
{
|
{
|
||||||
|
@ -45,7 +56,14 @@ export function CourseDetailProvider({
|
||||||
},
|
},
|
||||||
{ enabled: Boolean(editId) }
|
{ enabled: Boolean(editId) }
|
||||||
);
|
);
|
||||||
|
const canEdit = useMemo(() => {
|
||||||
|
const isAuthor = user?.id === course?.authorId;
|
||||||
|
const isDept = course?.depts
|
||||||
|
?.map((dept) => dept.id)
|
||||||
|
.includes(user?.deptId);
|
||||||
|
const isRoot = hasSomePermissions(RolePerms?.MANAGE_ANY_POST);
|
||||||
|
return isAuthor || isDept || isRoot;
|
||||||
|
}, [user, course]);
|
||||||
const [selectedLectureId, setSelectedLectureId] = useState<
|
const [selectedLectureId, setSelectedLectureId] = useState<
|
||||||
string | undefined
|
string | undefined
|
||||||
>(lectureId || undefined);
|
>(lectureId || undefined);
|
||||||
|
@ -57,9 +75,9 @@ export function CourseDetailProvider({
|
||||||
},
|
},
|
||||||
{ enabled: Boolean(editId) }
|
{ enabled: Boolean(editId) }
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (course) {
|
if (course) {
|
||||||
console.log("read");
|
|
||||||
read.mutateAsync({
|
read.mutateAsync({
|
||||||
data: {
|
data: {
|
||||||
visitorId: user?.id || null,
|
visitorId: user?.id || null,
|
||||||
|
@ -85,6 +103,7 @@ export function CourseDetailProvider({
|
||||||
lectureIsLoading,
|
lectureIsLoading,
|
||||||
isHeaderVisible,
|
isHeaderVisible,
|
||||||
setIsHeaderVisible,
|
setIsHeaderVisible,
|
||||||
|
canEdit,
|
||||||
}}>
|
}}>
|
||||||
{children}
|
{children}
|
||||||
</CourseDetailContext.Provider>
|
</CourseDetailContext.Provider>
|
||||||
|
|
|
@ -10,7 +10,7 @@ import { useAuth } from "@web/src/providers/auth-provider";
|
||||||
import { useNavigate, useParams } from "react-router-dom";
|
import { useNavigate, useParams } from "react-router-dom";
|
||||||
import { UserMenu } from "@web/src/app/main/layout/UserMenu/UserMenu";
|
import { UserMenu } from "@web/src/app/main/layout/UserMenu/UserMenu";
|
||||||
import { CourseDetailContext } from "../CourseDetailContext";
|
import { CourseDetailContext } from "../CourseDetailContext";
|
||||||
import { Department, RolePerms } from "@nice/common";
|
|
||||||
|
|
||||||
const { Header } = Layout;
|
const { Header } = Layout;
|
||||||
|
|
||||||
|
@ -20,9 +20,8 @@ export function CourseDetailHeader() {
|
||||||
const { isAuthenticated, user, hasSomePermissions, hasEveryPermissions } =
|
const { isAuthenticated, user, hasSomePermissions, hasEveryPermissions } =
|
||||||
useAuth();
|
useAuth();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { course } = useContext(CourseDetailContext);
|
const { course, canEdit } = useContext(CourseDetailContext);
|
||||||
hasSomePermissions(RolePerms.MANAGE_ANY_POST, RolePerms.MANAGE_DOM_POST);
|
|
||||||
hasEveryPermissions(RolePerms.MANAGE_ANY_POST, RolePerms.MANAGE_DOM_POST);
|
|
||||||
return (
|
return (
|
||||||
<Header className="select-none flex items-center justify-center bg-white shadow-md border-b border-gray-100 fixed w-full z-30">
|
<Header className="select-none flex items-center justify-center bg-white shadow-md border-b border-gray-100 fixed w-full z-30">
|
||||||
<div className="w-full flex items-center justify-between h-full">
|
<div className="w-full flex items-center justify-between h-full">
|
||||||
|
@ -52,7 +51,7 @@ export function CourseDetailHeader() {
|
||||||
onChange={(e) => setSearchValue(e.target.value)}
|
onChange={(e) => setSearchValue(e.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{isAuthenticated && (
|
{canEdit && (
|
||||||
<>
|
<>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
|
|
@ -9,9 +9,7 @@ import { CourseDetailHeader } from "./CourseDetailHeader/CourseDetailHeader";
|
||||||
export default function CourseDetailLayout() {
|
export default function CourseDetailLayout() {
|
||||||
const {
|
const {
|
||||||
course,
|
course,
|
||||||
selectedLectureId,
|
|
||||||
lecture,
|
|
||||||
isLoading,
|
|
||||||
setSelectedLectureId,
|
setSelectedLectureId,
|
||||||
} = useContext(CourseDetailContext);
|
} = useContext(CourseDetailContext);
|
||||||
|
|
||||||
|
@ -38,10 +36,7 @@ export default function CourseDetailLayout() {
|
||||||
}}
|
}}
|
||||||
transition={{ type: "spring", stiffness: 300, damping: 30 }}
|
transition={{ type: "spring", stiffness: 300, damping: 30 }}
|
||||||
className="relative">
|
className="relative">
|
||||||
<CourseDetailDisplayArea
|
<CourseDetailDisplayArea />
|
||||||
// course={course}
|
|
||||||
// isLoading={isLoading}
|
|
||||||
/>
|
|
||||||
</motion.div>
|
</motion.div>
|
||||||
{/* 课程大纲侧边栏 */}
|
{/* 课程大纲侧边栏 */}
|
||||||
<CourseSyllabus
|
<CourseSyllabus
|
||||||
|
|
|
@ -63,16 +63,6 @@ export const routes: CustomRouteObject[] = [
|
||||||
path: "courses",
|
path: "courses",
|
||||||
element: <CoursesPage></CoursesPage>,
|
element: <CoursesPage></CoursesPage>,
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
|
||||||
path: "profiles",
|
|
||||||
},
|
|
||||||
|
|
||||||
// // 课程预览页面
|
|
||||||
// {
|
|
||||||
// path: "coursePreview/:id?",
|
|
||||||
// element: <CoursePreview></CoursePreview>,
|
|
||||||
// },
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -103,12 +93,6 @@ export const routes: CustomRouteObject[] = [
|
||||||
</WithAuth>
|
</WithAuth>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
// {
|
|
||||||
// path: "setting",
|
|
||||||
// element: (
|
|
||||||
// <CourseSettingForm></CourseSettingForm>
|
|
||||||
// ),
|
|
||||||
// },
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue