rht
This commit is contained in:
parent
23daab3b3b
commit
1fc1aa368c
|
@ -1,5 +1,5 @@
|
||||||
# 基础镜像
|
# 基础镜像
|
||||||
FROM node:18.17-alpine as base
|
FROM node:18-alpine as base
|
||||||
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories && \
|
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories && \
|
||||||
echo "https://mirrors.aliyun.com/alpine/v3.18/community" >> /etc/apk/repositories
|
echo "https://mirrors.aliyun.com/alpine/v3.18/community" >> /etc/apk/repositories
|
||||||
|
|
||||||
|
|
|
@ -125,5 +125,14 @@ export class PostRouter {
|
||||||
const { staff } = ctx;
|
const { staff } = ctx;
|
||||||
return await this.postService.updateOrderByIds(input.ids);
|
return await this.postService.updateOrderByIds(input.ids);
|
||||||
}),
|
}),
|
||||||
|
softDeletePostDescendant:this.trpc.protectProcedure
|
||||||
|
.input(
|
||||||
|
z.object({
|
||||||
|
ancestorId:z.string()
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.mutation(async ({ input })=>{
|
||||||
|
return await this.postService.softDeletePostDescendant(input)
|
||||||
|
})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -101,6 +101,7 @@ export class PostService extends BaseTreeService<Prisma.PostDelegate> {
|
||||||
},
|
},
|
||||||
params: { staff?: UserProfile; tx?: Prisma.TransactionClient },
|
params: { staff?: UserProfile; tx?: Prisma.TransactionClient },
|
||||||
) {
|
) {
|
||||||
|
|
||||||
const { courseDetail } = args;
|
const { courseDetail } = args;
|
||||||
// If no transaction is provided, create a new one
|
// If no transaction is provided, create a new one
|
||||||
if (!params.tx) {
|
if (!params.tx) {
|
||||||
|
@ -295,40 +296,33 @@ export class PostService extends BaseTreeService<Prisma.PostDelegate> {
|
||||||
staff?.id && {
|
staff?.id && {
|
||||||
authorId: staff.id,
|
authorId: staff.id,
|
||||||
},
|
},
|
||||||
// staff?.id && {
|
|
||||||
// watchableStaffs: {
|
|
||||||
// some: {
|
|
||||||
// id: staff.id,
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// deptId && {
|
|
||||||
// watchableDepts: {
|
|
||||||
// some: {
|
|
||||||
// id: {
|
|
||||||
// in: parentDeptIds,
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
|
|
||||||
// {
|
|
||||||
// AND: [
|
|
||||||
// {
|
|
||||||
// watchableStaffs: {
|
|
||||||
// none: {}, // 匹配 watchableStaffs 为空
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// watchableDepts: {
|
|
||||||
// none: {}, // 匹配 watchableDepts 为空
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// ],
|
|
||||||
// },
|
|
||||||
].filter(Boolean);
|
].filter(Boolean);
|
||||||
|
|
||||||
if (orCondition?.length > 0) return orCondition;
|
if (orCondition?.length > 0) return orCondition;
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
async softDeletePostDescendant(args:{ancestorId?:string}){
|
||||||
|
const { ancestorId } = args
|
||||||
|
const descendantIds = []
|
||||||
|
await db.postAncestry.findMany({
|
||||||
|
where:{
|
||||||
|
ancestorId,
|
||||||
|
},
|
||||||
|
select:{
|
||||||
|
descendantId:true
|
||||||
|
}
|
||||||
|
}).then(res=>{
|
||||||
|
res.forEach(item=>{
|
||||||
|
descendantIds.push(item.descendantId)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
console.log(descendantIds)
|
||||||
|
const result = super.softDeleteByIds([...descendantIds,ancestorId])
|
||||||
|
EventBus.emit('dataChanged', {
|
||||||
|
type: ObjectType.POST,
|
||||||
|
operation: CrudOperation.DELETED,
|
||||||
|
data: result,
|
||||||
|
});
|
||||||
|
return result
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,8 @@ import JoinButton from "../../models/course/detail/CourseOperationBtns/JoinButto
|
||||||
import { CourseDetailContext } from "../../models/course/detail/PostDetailContext";
|
import { CourseDetailContext } from "../../models/course/detail/PostDetailContext";
|
||||||
import ReactDOM from "react-dom";
|
import ReactDOM from "react-dom";
|
||||||
import { createRoot } from "react-dom/client";
|
import { createRoot } from "react-dom/client";
|
||||||
|
import { useQueryClient } from "@tanstack/react-query";
|
||||||
|
import { getQueryKey } from "@trpc/react-query";
|
||||||
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 {
|
const {
|
||||||
|
@ -36,6 +38,7 @@ export default function MindEditor({ id }: { id?: string }) {
|
||||||
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 { read } = useVisitor();
|
const { read } = useVisitor();
|
||||||
|
const queryClient = useQueryClient();
|
||||||
// const { data: post, isLoading }: { data: PathDto; isLoading: boolean } =
|
// const { data: post, isLoading }: { data: PathDto; isLoading: boolean } =
|
||||||
// api.post.findFirst.useQuery(
|
// api.post.findFirst.useQuery(
|
||||||
// {
|
// {
|
||||||
|
@ -46,7 +49,11 @@ export default function MindEditor({ id }: { id?: string }) {
|
||||||
// },
|
// },
|
||||||
// { enabled: Boolean(id) }
|
// { enabled: Boolean(id) }
|
||||||
// );
|
// );
|
||||||
|
const softDeletePostDescendant = api.post.softDeletePostDescendant.useMutation({
|
||||||
|
onSuccess:()=>{
|
||||||
|
queryClient.invalidateQueries({ queryKey: getQueryKey(api.post) });
|
||||||
|
}
|
||||||
|
})
|
||||||
const canEdit: boolean = useMemo(() => {
|
const canEdit: boolean = useMemo(() => {
|
||||||
const isAuth = isAuthenticated && user?.id === post?.author?.id;
|
const isAuth = isAuthenticated && user?.id === post?.author?.id;
|
||||||
return (
|
return (
|
||||||
|
@ -63,7 +70,7 @@ export default function MindEditor({ id }: { id?: string }) {
|
||||||
const { handleFileUpload } = useTusUpload();
|
const { handleFileUpload } = useTusUpload();
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
const handleIcon = () => {
|
const handleIcon = () => {
|
||||||
const hyperLinkElement =document.querySelectorAll(".hyper-link");
|
const hyperLinkElement = document.querySelectorAll(".hyper-link");
|
||||||
console.log("hyperLinkElement", hyperLinkElement);
|
console.log("hyperLinkElement", hyperLinkElement);
|
||||||
hyperLinkElement.forEach((item) => {
|
hyperLinkElement.forEach((item) => {
|
||||||
const hyperLinkDom = createRoot(item);
|
const hyperLinkDom = createRoot(item);
|
||||||
|
@ -212,6 +219,12 @@ export default function MindEditor({ id }: { id?: string }) {
|
||||||
`mind-thumb-${new Date().toString()}`
|
`mind-thumb-${new Date().toString()}`
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
const handleDelete = async () => {
|
||||||
|
await softDeletePostDescendant.mutateAsync({
|
||||||
|
ancestorId: id,
|
||||||
|
});
|
||||||
|
navigate("/path");
|
||||||
|
}
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
containerRef.current.style.height = `${Math.floor(window.innerHeight - 271)}px`;
|
containerRef.current.style.height = `${Math.floor(window.innerHeight - 271)}px`;
|
||||||
}, []);
|
}, []);
|
||||||
|
@ -252,14 +265,28 @@ export default function MindEditor({ id }: { id?: string }) {
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
{canEdit && (
|
{canEdit && (
|
||||||
<Button
|
<>
|
||||||
ghost
|
{
|
||||||
type="primary"
|
id && (
|
||||||
icon={<SaveOutlined></SaveOutlined>}
|
<Button
|
||||||
onSubmit={(e) => e.preventDefault()}
|
danger
|
||||||
onClick={handleSave}>
|
icon={<SaveOutlined></SaveOutlined>}
|
||||||
{id ? "更新" : "保存"}
|
onSubmit={(e) => e.preventDefault()}
|
||||||
</Button>
|
onClick={handleDelete}>
|
||||||
|
删除
|
||||||
|
</Button>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
<Button
|
||||||
|
className="ml-4"
|
||||||
|
ghost
|
||||||
|
type="primary"
|
||||||
|
icon={<SaveOutlined></SaveOutlined>}
|
||||||
|
onSubmit={(e) => e.preventDefault()}
|
||||||
|
onClick={handleSave}>
|
||||||
|
{id ? "更新" : "保存"}
|
||||||
|
</Button>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -41,7 +41,7 @@ export const CourseDetailDisplayArea: React.FC = () => {
|
||||||
}}
|
}}
|
||||||
className="w-full bg-black rounded-lg ">
|
className="w-full bg-black rounded-lg ">
|
||||||
<div className=" w-full cursor-pointer">
|
<div className=" w-full cursor-pointer">
|
||||||
<ReactPlayer
|
{/* <ReactPlayer
|
||||||
url={lecture?.meta?.videoUrl}
|
url={lecture?.meta?.videoUrl}
|
||||||
controls={true}
|
controls={true}
|
||||||
width="100%"
|
width="100%"
|
||||||
|
@ -49,8 +49,8 @@ export const CourseDetailDisplayArea: React.FC = () => {
|
||||||
onError={(error) => {
|
onError={(error) => {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
}}
|
}}
|
||||||
/>
|
/> */}
|
||||||
{/* <VideoPlayer src={lecture?.meta?.videoUrl} /> */}
|
<VideoPlayer src={lecture?.meta?.videoUrl} />
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,97 +1,40 @@
|
||||||
import { useAuth } from "@web/src/providers/auth-provider";
|
import { useContext, useEffect, useState } from "react";
|
||||||
import { useContext, useState } from "react";
|
|
||||||
import { useNavigate, useParams } from "react-router-dom";
|
import { useNavigate, useParams } from "react-router-dom";
|
||||||
import { CourseDetailContext } from "../PostDetailContext";
|
import { CourseDetailContext } from "../PostDetailContext";
|
||||||
import { useStaff } from "@nice/client";
|
import { api } from "@nice/client";
|
||||||
import {
|
import {
|
||||||
CheckCircleOutlined,
|
DeleteTwoTone,
|
||||||
CloseCircleOutlined,
|
|
||||||
EditTwoTone,
|
EditTwoTone,
|
||||||
LoginOutlined,
|
ExclamationCircleFilled,
|
||||||
} from "@ant-design/icons";
|
} from "@ant-design/icons";
|
||||||
import toast from "react-hot-toast";
|
import toast from "react-hot-toast";
|
||||||
import JoinButton from "./JoinButton";
|
import JoinButton from "./JoinButton";
|
||||||
|
import { Modal } from "antd";
|
||||||
|
import { useQueryClient } from "@tanstack/react-query";
|
||||||
|
import { getQueryKey } from "@trpc/react-query";
|
||||||
|
|
||||||
export default function CourseOperationBtns() {
|
export default function CourseOperationBtns() {
|
||||||
// const { isAuthenticated, user } = useAuth();
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { post, canEdit, userIsLearning, setUserIsLearning } =
|
const { post, canEdit } = useContext(CourseDetailContext);
|
||||||
useContext(CourseDetailContext);
|
|
||||||
// const { update } = useStaff();
|
|
||||||
// const [isHovered, setIsHovered] = useState(false);
|
|
||||||
|
|
||||||
// const toggleLearning = async () => {
|
|
||||||
// if (!userIsLearning) {
|
|
||||||
// await update.mutateAsync({
|
|
||||||
// where: { id: user?.id },
|
|
||||||
// data: {
|
|
||||||
// learningPosts: {
|
|
||||||
// connect: { id: course.id },
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// });
|
|
||||||
// setUserIsLearning(true);
|
|
||||||
// toast.success("加入学习成功");
|
|
||||||
// } else {
|
|
||||||
// await update.mutateAsync({
|
|
||||||
// where: { id: user?.id },
|
|
||||||
// data: {
|
|
||||||
// learningPosts: {
|
|
||||||
// disconnect: {
|
|
||||||
// id: course.id,
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// });
|
|
||||||
// toast.success("退出学习成功");
|
|
||||||
// setUserIsLearning(false);
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{/* {isAuthenticated && (
|
|
||||||
<div
|
|
||||||
onClick={toggleLearning}
|
|
||||||
onMouseEnter={() => setIsHovered(true)}
|
|
||||||
onMouseLeave={() => setIsHovered(false)}
|
|
||||||
className={`flex px-1 py-0.5 gap-1 hover:cursor-pointer transition-all ${
|
|
||||||
userIsLearning
|
|
||||||
? isHovered
|
|
||||||
? "text-red-500 border-red-500 rounded-md "
|
|
||||||
: "text-green-500 "
|
|
||||||
: "text-primary "
|
|
||||||
}`}>
|
|
||||||
{userIsLearning ? (
|
|
||||||
isHovered ? (
|
|
||||||
<CloseCircleOutlined />
|
|
||||||
) : (
|
|
||||||
<CheckCircleOutlined />
|
|
||||||
)
|
|
||||||
) : (
|
|
||||||
<LoginOutlined />
|
|
||||||
)}
|
|
||||||
<span>
|
|
||||||
{userIsLearning
|
|
||||||
? isHovered
|
|
||||||
? "退出学习"
|
|
||||||
: "正在学习"
|
|
||||||
: "加入学习"}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
)} */}
|
|
||||||
<JoinButton></JoinButton>
|
<JoinButton></JoinButton>
|
||||||
{canEdit && (
|
{canEdit && (
|
||||||
<div
|
<>
|
||||||
className="flex gap-1 px-1 py-0.5 text-primary hover:cursor-pointer"
|
<div
|
||||||
onClick={() => {
|
className="flex gap-1 px-1 py-0.5 text-primary hover:cursor-pointer"
|
||||||
const url = post?.id
|
onClick={() => {
|
||||||
? `/course/${post?.id}/editor`
|
const url = post?.id
|
||||||
: "/course/editor";
|
? `/course/${post?.id}/editor`
|
||||||
navigate(url);
|
: "/course/editor";
|
||||||
}}>
|
navigate(url);
|
||||||
<EditTwoTone></EditTwoTone>
|
}}>
|
||||||
{"编辑课程"}
|
<EditTwoTone></EditTwoTone>
|
||||||
</div>
|
{"编辑课程"}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
|
||||||
|
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
@ -13,6 +13,8 @@ import { api, usePost } from "@nice/client";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { useAuth } from "@web/src/providers/auth-provider";
|
import { useAuth } from "@web/src/providers/auth-provider";
|
||||||
|
import { getQueryKey } from "@trpc/react-query";
|
||||||
|
import { useQueryClient } from "@tanstack/react-query";
|
||||||
|
|
||||||
export type CourseFormData = {
|
export type CourseFormData = {
|
||||||
title: string;
|
title: string;
|
||||||
|
@ -26,6 +28,7 @@ export type CourseFormData = {
|
||||||
|
|
||||||
interface CourseEditorContextType {
|
interface CourseEditorContextType {
|
||||||
onSubmit: (values: CourseFormData) => Promise<void>;
|
onSubmit: (values: CourseFormData) => Promise<void>;
|
||||||
|
handleDeleteCourse: () => Promise<void>;
|
||||||
editId?: string;
|
editId?: string;
|
||||||
course?: CourseDto;
|
course?: CourseDto;
|
||||||
taxonomies?: Taxonomy[]; // 根据实际类型调整
|
taxonomies?: Taxonomy[]; // 根据实际类型调整
|
||||||
|
@ -45,6 +48,7 @@ export function CourseFormProvider({
|
||||||
}: CourseFormProviderProps) {
|
}: CourseFormProviderProps) {
|
||||||
const [form] = Form.useForm<CourseFormData>();
|
const [form] = Form.useForm<CourseFormData>();
|
||||||
const { create, update, createCourse } = usePost();
|
const { create, update, createCourse } = usePost();
|
||||||
|
const queryClient = useQueryClient();
|
||||||
const { user } = useAuth();
|
const { user } = useAuth();
|
||||||
const { data: course }: { data: CourseDto } = api.post.findFirst.useQuery(
|
const { data: course }: { data: CourseDto } = api.post.findFirst.useQuery(
|
||||||
{
|
{
|
||||||
|
@ -60,6 +64,11 @@ export function CourseFormProvider({
|
||||||
} = api.taxonomy.getAll.useQuery({
|
} = api.taxonomy.getAll.useQuery({
|
||||||
type: ObjectType.COURSE,
|
type: ObjectType.COURSE,
|
||||||
});
|
});
|
||||||
|
const softDeletePostDescendant = api.post.softDeletePostDescendant.useMutation({
|
||||||
|
onSuccess:()=>{
|
||||||
|
queryClient.invalidateQueries({ queryKey: getQueryKey(api.post) });
|
||||||
|
}
|
||||||
|
})
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -92,7 +101,15 @@ export function CourseFormProvider({
|
||||||
form.setFieldsValue(formData);
|
form.setFieldsValue(formData);
|
||||||
}
|
}
|
||||||
}, [course, form]);
|
}, [course, form]);
|
||||||
|
const handleDeleteCourse = async () => {
|
||||||
|
if(editId){
|
||||||
|
await softDeletePostDescendant.mutateAsync({
|
||||||
|
ancestorId: editId,
|
||||||
|
});
|
||||||
|
|
||||||
|
navigate("/courses");
|
||||||
|
}
|
||||||
|
}
|
||||||
const onSubmit = async (values: any) => {
|
const onSubmit = async (values: any) => {
|
||||||
const sections = values?.sections || [];
|
const sections = values?.sections || [];
|
||||||
const deptIds = values?.deptIds || [];
|
const deptIds = values?.deptIds || [];
|
||||||
|
@ -172,6 +189,7 @@ export function CourseFormProvider({
|
||||||
course,
|
course,
|
||||||
taxonomies,
|
taxonomies,
|
||||||
form,
|
form,
|
||||||
|
handleDeleteCourse
|
||||||
}}>
|
}}>
|
||||||
<Form
|
<Form
|
||||||
// requiredMark="optional"
|
// requiredMark="optional"
|
||||||
|
|
|
@ -25,8 +25,11 @@ import { CourseSectionEmpty } from "./CourseSectionEmpty";
|
||||||
import { SortableSection } from "./SortableSection";
|
import { SortableSection } from "./SortableSection";
|
||||||
import { LectureList } from "./LectureList";
|
import { LectureList } from "./LectureList";
|
||||||
import toast from "react-hot-toast";
|
import toast from "react-hot-toast";
|
||||||
|
import { useQueryClient } from "@tanstack/react-query";
|
||||||
|
import { getQueryKey } from "@trpc/react-query";
|
||||||
|
|
||||||
const CourseContentForm: React.FC = () => {
|
const CourseContentForm: React.FC = () => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
const { editId } = useCourseEditor();
|
const { editId } = useCourseEditor();
|
||||||
const sensors = useSensors(
|
const sensors = useSensors(
|
||||||
useSensor(PointerSensor),
|
useSensor(PointerSensor),
|
||||||
|
@ -71,7 +74,11 @@ const CourseContentForm: React.FC = () => {
|
||||||
ids: newItems.map((item) => item.id),
|
ids: newItems.map((item) => item.id),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
const softDeletePostDescendant = api.post.softDeletePostDescendant.useMutation({
|
||||||
|
onSuccess:()=>{
|
||||||
|
queryClient.invalidateQueries({ queryKey: getQueryKey(api.post) });
|
||||||
|
}
|
||||||
|
})
|
||||||
return (
|
return (
|
||||||
<div className="max-w-4xl mx-auto p-6">
|
<div className="max-w-4xl mx-auto p-6">
|
||||||
<CourseContentFormHeader />
|
<CourseContentFormHeader />
|
||||||
|
@ -93,8 +100,8 @@ const CourseContentForm: React.FC = () => {
|
||||||
field={section}
|
field={section}
|
||||||
remove={async () => {
|
remove={async () => {
|
||||||
if (section?.id) {
|
if (section?.id) {
|
||||||
await softDeleteByIds.mutateAsync({
|
await softDeletePostDescendant.mutateAsync({
|
||||||
ids: [section.id],
|
ancestorId: section.id,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
setItems(sections);
|
setItems(sections);
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
import { ArrowLeftOutlined, ClockCircleOutlined } from "@ant-design/icons";
|
import { ArrowLeftOutlined, ClockCircleOutlined, ExclamationCircleFilled } from "@ant-design/icons";
|
||||||
import { Button, Tag, Typography } from "antd";
|
import { Button, Modal, Tag, Typography } from "antd";
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { CourseStatus, CourseStatusLabel } from "@nice/common";
|
import { CourseStatus, CourseStatusLabel } from "@nice/common";
|
||||||
import { useCourseEditor } from "../context/CourseEditorContext";
|
import { useCourseEditor } from "../context/CourseEditorContext";
|
||||||
import { useAuth } from "@web/src/providers/auth-provider";
|
import { useAuth } from "@web/src/providers/auth-provider";
|
||||||
|
import toast from "react-hot-toast";
|
||||||
|
|
||||||
const { Title } = Typography;
|
const { Title } = Typography;
|
||||||
|
|
||||||
|
@ -18,8 +19,8 @@ const courseStatusVariant: Record<CourseStatus, string> = {
|
||||||
export default function CourseEditorHeader() {
|
export default function CourseEditorHeader() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { user, hasSomePermissions } = useAuth();
|
const { user, hasSomePermissions } = useAuth();
|
||||||
|
const { confirm } = Modal;
|
||||||
const { onSubmit, course, form } = useCourseEditor();
|
const { onSubmit, course, form, handleDeleteCourse, editId } = useCourseEditor();
|
||||||
|
|
||||||
const handleSave = () => {
|
const handleSave = () => {
|
||||||
try {
|
try {
|
||||||
|
@ -30,7 +31,26 @@ export default function CourseEditorHeader() {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
const showDeleteConfirm = () => {
|
||||||
|
confirm({
|
||||||
|
title: '确定删除该课程吗',
|
||||||
|
icon: <ExclamationCircleFilled />,
|
||||||
|
content: '',
|
||||||
|
okText: '删除',
|
||||||
|
okType: 'danger',
|
||||||
|
cancelText: '取消',
|
||||||
|
async onOk() {
|
||||||
|
console.log('OK');
|
||||||
|
console.log(editId)
|
||||||
|
await handleDeleteCourse()
|
||||||
|
toast.success('课程已删除')
|
||||||
|
navigate("/courses");
|
||||||
|
},
|
||||||
|
onCancel() {
|
||||||
|
console.log('Cancel');
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<header className="fixed top-0 left-0 right-0 h-16 bg-white border-b border-gray-200 z-10">
|
<header className="fixed top-0 left-0 right-0 h-16 bg-white border-b border-gray-200 z-10">
|
||||||
<div className="h-full flex items-center justify-between px-3 md:px-4">
|
<div className="h-full flex items-center justify-between px-3 md:px-4">
|
||||||
|
@ -70,16 +90,27 @@ export default function CourseEditorHeader() {
|
||||||
)} */}
|
)} */}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Button
|
<div>
|
||||||
type="primary"
|
{editId &&
|
||||||
// size="small"
|
<Button
|
||||||
onClick={handleSave}
|
danger
|
||||||
|
onClick={showDeleteConfirm}
|
||||||
|
>
|
||||||
|
删除课程
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
|
<Button
|
||||||
|
className="ml-4"
|
||||||
|
type="primary"
|
||||||
|
// size="small"
|
||||||
|
onClick={handleSave}
|
||||||
// disabled={form
|
// disabled={form
|
||||||
// .getFieldsError()
|
// .getFieldsError()
|
||||||
// .some(({ errors }) => errors.length)}
|
// .some(({ errors }) => errors.length)}
|
||||||
>
|
>
|
||||||
保存
|
保存
|
||||||
</Button>
|
</Button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
);
|
);
|
||||||
|
|
|
@ -44,7 +44,8 @@ export default function PostList({
|
||||||
|
|
||||||
const posts = useMemo(() => {
|
const posts = useMemo(() => {
|
||||||
if (data && !isLoading) {
|
if (data && !isLoading) {
|
||||||
return data?.items;
|
console.log(data?.items)
|
||||||
|
return data?.items.filter(item=>item.deletedAt === null);
|
||||||
}
|
}
|
||||||
return [];
|
return [];
|
||||||
}, [data, isLoading]);
|
}, [data, isLoading]);
|
||||||
|
|
Binary file not shown.
|
@ -15,7 +15,7 @@ done
|
||||||
if [ -f "/usr/share/nginx/html/index.html" ]; then
|
if [ -f "/usr/share/nginx/html/index.html" ]; then
|
||||||
# Use envsubst to replace environment variable placeholders
|
# Use envsubst to replace environment variable placeholders
|
||||||
echo "Processing /usr/share/nginx/html/index.html"
|
echo "Processing /usr/share/nginx/html/index.html"
|
||||||
envsubst < /usr/share/nginx/html/index.temp > /usr/share/nginx/html/index.html.tmp
|
envsubst < /usr/share/nginx/html/index.template > /usr/share/nginx/html/index.html.tmp
|
||||||
mv /usr/share/nginx/html/index.html.tmp /usr/share/nginx/html/index.html
|
mv /usr/share/nginx/html/index.html.tmp /usr/share/nginx/html/index.html
|
||||||
echo "Processed content:"
|
echo "Processed content:"
|
||||||
cat /usr/share/nginx/html/index.html
|
cat /usr/share/nginx/html/index.html
|
||||||
|
|
|
@ -100,6 +100,7 @@ export const courseDetailSelect: Prisma.PostSelect = {
|
||||||
// isFeatured: true,
|
// isFeatured: true,
|
||||||
createdAt: true,
|
createdAt: true,
|
||||||
updatedAt: true,
|
updatedAt: true,
|
||||||
|
deletedAt: true,
|
||||||
// 关联表选择
|
// 关联表选择
|
||||||
terms: {
|
terms: {
|
||||||
select: {
|
select: {
|
||||||
|
|
Loading…
Reference in New Issue