From 1c3af1597876fc0f632cea676d83aad7bfad45e7 Mon Sep 17 00:00:00 2001 From: Li1304553726 <1304553726@qq.com> Date: Mon, 5 May 2025 19:51:44 +0800 Subject: [PATCH] add --- Dockerfile | 2 +- apps/web/public/vite.svg | 0 .../{MoreContent.tsx => ExampleContent.tsx} | 4 +- apps/web/src/app/main/help/MusicContent.tsx | 560 ++++++++++-------- apps/web/src/app/main/help/PsychologyNav.tsx | 68 +-- ...CourseContent.tsx => PublicityContent.tsx} | 4 +- apps/web/src/app/main/help/ScienceContent.tsx | 0 apps/web/src/app/main/help/VideoContent.tsx | 133 +++-- .../models/post/detail/PostCommentCard.tsx | 2 +- 9 files changed, 451 insertions(+), 322 deletions(-) mode change 100644 => 100755 apps/web/public/vite.svg rename apps/web/src/app/main/help/{MoreContent.tsx => ExampleContent.tsx} (52%) mode change 100644 => 100755 mode change 100644 => 100755 apps/web/src/app/main/help/MusicContent.tsx mode change 100644 => 100755 apps/web/src/app/main/help/PsychologyNav.tsx rename apps/web/src/app/main/help/{CourseContent.tsx => PublicityContent.tsx} (52%) mode change 100644 => 100755 mode change 100644 => 100755 apps/web/src/app/main/help/ScienceContent.tsx mode change 100644 => 100755 apps/web/src/app/main/help/VideoContent.tsx diff --git a/Dockerfile b/Dockerfile index 213d413..f89599f 100755 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # 基础镜像 -FROM node:18.17-alpine as base +FROM node:18-alpine as base # 更改 apk 镜像源为阿里云 RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories # 设置 npm 镜像源 diff --git a/apps/web/public/vite.svg b/apps/web/public/vite.svg old mode 100644 new mode 100755 diff --git a/apps/web/src/app/main/help/MoreContent.tsx b/apps/web/src/app/main/help/ExampleContent.tsx old mode 100644 new mode 100755 similarity index 52% rename from apps/web/src/app/main/help/MoreContent.tsx rename to apps/web/src/app/main/help/ExampleContent.tsx index 5ce6cb4..50dc350 --- a/apps/web/src/app/main/help/MoreContent.tsx +++ b/apps/web/src/app/main/help/ExampleContent.tsx @@ -1,7 +1,7 @@ -export function MoreContent() { +export function ExampleContent() { return (
-

更多

+

案例分析

{/* 这里放视频列表内容 */}
); diff --git a/apps/web/src/app/main/help/MusicContent.tsx b/apps/web/src/app/main/help/MusicContent.tsx old mode 100644 new mode 100755 index 646dc29..3a2e814 --- a/apps/web/src/app/main/help/MusicContent.tsx +++ b/apps/web/src/app/main/help/MusicContent.tsx @@ -1,273 +1,341 @@ -import React, { useState, useMemo } from 'react'; -import { Tabs, Button, message, Image, Row, Col, Modal, Input } from 'antd'; +import React, { useState, useMemo } from "react"; +import { + Tabs, + Button, + message, + Image, + Row, + Col, + Modal, + Input, + Alert, + Pagination, +} from "antd"; import { TusUploader } from "@web/src/components/common/uploader/TusUploader"; -import { SendOutlined, DeleteOutlined } from "@ant-design/icons"; +import { SendOutlined, DeleteOutlined, LoginOutlined } from "@ant-design/icons"; import { api } from "@nice/client"; -import dayjs from 'dayjs'; +import dayjs from "dayjs"; import { env } from "@web/src/env"; import { getFileIcon } from "@web/src/components/models/post/detail/utils"; import { formatFileSize, getCompressedImageUrl } from "@nice/utils"; -import { ResourceDto } from 'packages/common/dist'; +import { ResourceDto, RoleName } from "packages/common/dist"; +import { useAuth } from "@web/src/providers/auth-provider"; const { TabPane } = Tabs; export function MusicContent() { - const [fileIds, setFileIds] = useState([]); - const [uploaderKey, setUploaderKey] = useState(0); - // const [description, setDescription] = useState(''); - // 获取资源列表 - const { data: resources,refetch } :{data:ResourceDto[],refetch:()=>void} = api.resource.findMany.useQuery({ - where: { - deletedAt: null, - postId: null, - }, - orderBy: { - createdAt: 'desc' - } + const { isAuthenticated, user, hasSomePermissions } = useAuth(); + const [fileIds, setFileIds] = useState([]); + const [uploaderKey, setUploaderKey] = useState(0); + + // 分页状态 + const [imagePage, setImagePage] = useState(1); + const [filePage, setFilePage] = useState(1); + const pageSize = 12; // 每页显示的数量 + + // 检查是否为域管理员 + const isDomainAdmin = useMemo(() => { + return hasSomePermissions("MANAGE_DOM_STAFF", "MANAGE_ANY_STAFF"); + }, [hasSomePermissions]); + + // 获取资源列表 + const { + data: resources, + refetch, + }: { data: ResourceDto[]; refetch: () => void } = + api.resource.findMany.useQuery({ + where: { + deletedAt: null, + postId: null, + }, + orderBy: { + createdAt: "desc", + }, }); - // 处理资源数据 - const { imageResources, fileResources } = useMemo(() => { - if (!resources) return { imageResources: [], fileResources: [] }; - const isImage = (url: string) => /\.(png|jpg|jpeg|gif|webp)$/i.test(url); - - const processedResources = resources.map(resource => { - if (!resource?.url) return null; - const original = `http://${env.SERVER_IP}:${env.UPLOAD_PORT}/uploads/${resource.url}`; - const isImg = isImage(resource.url); - return { - ...resource, - url: isImg ? getCompressedImageUrl(original) : original, - originalUrl: original, - isImage: isImg, - }; - }).filter(Boolean); + // 处理资源数据 + const { imageResources, fileResources, imagePagination, filePagination } = + useMemo(() => { + if (!resources) return { - imageResources: processedResources.filter(res => res.isImage), - fileResources: processedResources.filter(res => !res.isImage) + imageResources: [], + fileResources: [], + imagePagination: { total: 0, data: [] }, + filePagination: { total: 0, data: [] }, }; - }, [resources]); - const createMutation = api.resource.create.useMutation({}); - const handleSubmit = async () => { - if (!fileIds.length) { - message.error("请先上传文件"); - return; - } + const isImage = (url: string) => /\.(png|jpg|jpeg|gif|webp)$/i.test(url); - // if (!description.trim()) { - // message.error("请输入文件描述"); - // return; - // } + const processedResources = resources + .map((resource) => { + if (!resource?.url) return null; + const original = `http://${env.SERVER_IP}:${env.UPLOAD_PORT}/uploads/${resource.url}`; + const isImg = isImage(resource.url); + return { + ...resource, + url: isImg ? getCompressedImageUrl(original) : original, + originalUrl: original, + isImage: isImg, + }; + }) + .filter(Boolean); + const allImageResources = processedResources.filter((res) => res.isImage); + const allFileResources = processedResources.filter((res) => !res.isImage); + + // 分页处理 + const imageStart = (imagePage - 1) * pageSize; + const fileStart = (filePage - 1) * pageSize; + + return { + imageResources: allImageResources.slice( + imageStart, + imageStart + pageSize + ), + fileResources: allFileResources.slice(fileStart, fileStart + pageSize), + imagePagination: { + total: allImageResources.length, + data: allImageResources, + }, + filePagination: { + total: allFileResources.length, + data: allFileResources, + }, + }; + }, [resources, imagePage, filePage]); + + const createMutation = api.resource.create.useMutation({}); + const handleSubmit = async () => { + if (!isAuthenticated) { + message.error("请先登录"); + return; + } + + if (!isDomainAdmin) { + message.error("只有管理员才能上传文件"); + return; + } + + if (!fileIds.length) { + message.error("请先上传文件"); + return; + } + + try { + // 逐个上传文件,而不是使用 Promise.all + for (const fileId of fileIds) { try { - // 逐个上传文件,而不是使用 Promise.all - for (const fileId of fileIds) { - try { - await createMutation.mutateAsync({ - data: { - fileId, - // description: description.trim(), - isPublic: true, - } - }); - } catch (error) { - if (error instanceof Error && error.message.includes('Unique constraint failed')) { - console.warn(`文件 ${fileId} 已存在,跳过`); - continue; - } - throw error; - } - } - message.success("上传成功!"); - setFileIds([]); - // setDescription(''); - setUploaderKey(prev => prev + 1); - refetch(); // 刷新列表 + await createMutation.mutateAsync({ + data: { + fileId, + isPublic: true, + }, + }); } catch (error) { - console.error("Error uploading:", error); - message.error("上传失败,请稍后重试"); + if ( + error instanceof Error && + error.message.includes("Unique constraint failed") + ) { + console.warn(`文件 ${fileId} 已存在,跳过`); + continue; + } + throw error; } - }; + } + message.success("上传成功!"); + setFileIds([]); + setUploaderKey((prev) => prev + 1); + refetch(); // 刷新列表 + } catch (error) { + console.error("Error uploading:", error); + message.error("上传失败,请稍后重试"); + } + }; + // 删除资源 + const deleteMutation = api.resource.softDeleteByIds.useMutation(); - // 删除资源 - const deleteMutation = api.resource.softDeleteByIds.useMutation(); + const handleDelete = async (id: string) => { + if (!isAuthenticated) { + message.error("请先登录"); + return; + } - const handleDelete = async (id: string) => { - console.log("Delete resource id:", id); - try { - const confirmed = await new Promise(resolve => { - Modal.confirm({ - title: '确认删除', - content: '确定要删除这个文件吗?此操作不可恢复。', - okText: '确认', - cancelText: '取消', - onOk: () => resolve(true), - onCancel: () => resolve(false), - }); - }); - - if (!confirmed) return; - - await deleteMutation.mutateAsync({ - ids: [id] - }); - } catch (error) { - console.error('Delete error:', error); - message.error('删除失败,请重试'); - } + if (!isDomainAdmin) { + message.error("只有域管理员才能删除文件"); + return; + } - refetch(); - message.success('删除成功'); - }; + console.log("Delete resource id:", id); + try { + const confirmed = await new Promise((resolve) => { + Modal.confirm({ + title: "确认删除", + content: "确定要删除这个文件吗?此操作不可恢复。", + okText: "确认", + cancelText: "取消", + onOk: () => resolve(true), + onCancel: () => resolve(false), + }); + }); - return ( -
-
-
-

锦囊资源上传

-

支持视频、图片、文档、PPT等多种格式文件

-
-
- {/* 上传区域 */} -
- - -
- { - console.log('文件IDs:', value); - setFileIds(value); - }} - /> -
-
- {/* -
- setDescription(e.target.value)} - placeholder="请输入文件描述信息" - autoSize={{ minRows: 3, maxRows: 6 }} - className="bg-transparent border-none focus:ring-0" - /> -
-

请添加文件描述,方便后续管理和识别。

-
-
-
*/} -
- {fileIds.length > 0 && ( -
- -
- )} + if (!confirmed) return; - {/* 文件展示区域 */} -
- {/* 图片资源展示 */} - {/* {imageResources?.length > 0 && ( -
-

图片列表

- - - {imageResources.map((resource) => ( - -
- {resource.title} -
-
- {resource.title} -
-
-
- - ))} -
-
-
- )} */} + refetch(); + message.success("删除成功"); + }; - {/* 其他文件资源展示 */} - {fileResources?.length > 0 && ( -
-

文件列表

-
- {fileResources.map((resource) => ( -
-
- {getFileIcon(resource.url)} -
-
-
- {resource.title || '未命名文件'} -
- {resource.description && ( -
- 描述: {resource.description} -
- )} -
- {dayjs(resource.createdAt).format('YYYY-MM-DD')} - {resource.meta?.size && formatFileSize(resource.meta.size)} -
-
-
- -
-
- ))} -
-
- )} -
-
+ return ( +
+
+
+

资源上传

+

支持视频、图片、文档、PPT等多种格式文件

- ); -}; \ No newline at end of file +
+ + {!isAuthenticated && ( + + )} + + {isAuthenticated && !isDomainAdmin && ( + + )} + + {/* 上传区域 */} +
+ + +
+ { + if (!isDomainAdmin) { + message.error("只有管理员才能上传文件"); + return; + } + setFileIds(value); + }} + /> +
+
+
+ {isDomainAdmin && fileIds.length > 0 && ( +
+ +
+ )} + + {/* 文件展示区域 */} +
+ {/* 其他文件资源展示 */} + {filePagination?.total > 0 && ( +
+
+

文件列表

+ + 共 {filePagination.total} 个文件 + +
+ +
+ {fileResources.map((resource) => ( +
+
+ {getFileIcon(resource.url)} +
+
+
+ {resource.title || "未命名文件"} +
+ {resource.description && ( +
+ 描述: {resource.description} +
+ )} +
+ + {dayjs(resource.createdAt).format("YYYY-MM-DD")} + + + {resource.meta?.size && + formatFileSize(resource.meta.size)} + +
+
+
+ + {isDomainAdmin && ( +
+
+ ))} +
+ + {/* 文件分页 */} + {filePagination.total > pageSize && ( +
+ setFilePage(page)} + showSizeChanger={false} + /> +
+ )} +
+ )} +
+
+
+ ); +} diff --git a/apps/web/src/app/main/help/PsychologyNav.tsx b/apps/web/src/app/main/help/PsychologyNav.tsx old mode 100644 new mode 100755 index 56cdde6..2bcc421 --- a/apps/web/src/app/main/help/PsychologyNav.tsx +++ b/apps/web/src/app/main/help/PsychologyNav.tsx @@ -7,46 +7,46 @@ import { BookOutlined } from '@ant-design/icons'; import { VideoContent } from './VideoContent'; -import { CourseContent } from './CourseContent'; import { MusicContent } from './MusicContent'; import { ScienceContent } from './ScienceContent'; -import { MoreContent } from './MoreContent'; +import { PublicityContent } from './PublicityContent'; +import { ExampleContent } from './ExampleContent'; import './pt.css'; export function PsychologyNav() { const items = [ - // { - // key: 'more', - // label: ( - // - // - // 宣传报道 - // - // ), - // children: - // }, - // { - // key: 'music', - // label: ( - // - // < ReadOutlined className="text-lg" /> - // 常识科普 - // - // ), - // children: - // }, - // { - // key: 'courses', - // label: ( - // - // - // 案例分析 - // - // ), - // children: - // }, + { + key: 'publicity', + label: ( + + + 宣传报道 + + ), + children: + }, { key: 'science', + label: ( + + < ReadOutlined className="text-lg" /> + 常识科普 + + ), + children: + }, + { + key: 'example', + label: ( + + + 案例分析 + + ), + children: + }, + { + key: 'video', label: ( < FileTextOutlined className="text-lg" /> @@ -56,7 +56,7 @@ export function PsychologyNav() { children: }, { - key: 'vedio', + key: 'music', label: ( @@ -71,7 +71,7 @@ export function PsychologyNav() { return (
-

心理小课件

+

宣传报道

{/* 这里放课件列表内容 */}
); diff --git a/apps/web/src/app/main/help/ScienceContent.tsx b/apps/web/src/app/main/help/ScienceContent.tsx old mode 100644 new mode 100755 diff --git a/apps/web/src/app/main/help/VideoContent.tsx b/apps/web/src/app/main/help/VideoContent.tsx old mode 100644 new mode 100755 index d5b4f26..74fdaa0 --- a/apps/web/src/app/main/help/VideoContent.tsx +++ b/apps/web/src/app/main/help/VideoContent.tsx @@ -9,6 +9,7 @@ import { Modal, Input, Alert, + Pagination, } from "antd"; import { TusUploader } from "@web/src/components/common/uploader/TusUploader"; import { SendOutlined, DeleteOutlined, LoginOutlined } from "@ant-design/icons"; @@ -25,11 +26,15 @@ export function VideoContent() { const { isAuthenticated, user, hasSomePermissions } = useAuth(); const [fileIds, setFileIds] = useState([]); const [uploaderKey, setUploaderKey] = useState(0); - // const [description, setDescription] = useState(''); + + // 分页状态 + const [imagePage, setImagePage] = useState(1); + const [filePage, setFilePage] = useState(1); + const pageSize = 12; // 每页显示的数量 // 检查是否为域管理员 const isDomainAdmin = useMemo(() => { - return hasSomePermissions("MANAGE_DOM_STAFF", "MANAGE_ANY_STAFF"); // 使用权限检查而不是角色名称 + return hasSomePermissions("MANAGE_DOM_STAFF", "MANAGE_ANY_STAFF"); }, [hasSomePermissions]); // 获取资源列表 @@ -46,30 +51,57 @@ export function VideoContent() { createdAt: "desc", }, }); + // 处理资源数据 - const { imageResources, fileResources } = useMemo(() => { - if (!resources) return { imageResources: [], fileResources: [] }; - const isImage = (url: string) => /\.(png|jpg|jpeg|gif|webp)$/i.test(url); - - const processedResources = resources - .map((resource) => { - if (!resource?.url) return null; - const original = `http://${env.SERVER_IP}:${env.UPLOAD_PORT}/uploads/${resource.url}`; - const isImg = isImage(resource.url); + const { imageResources, fileResources, imagePagination, filePagination } = + useMemo(() => { + if (!resources) return { - ...resource, - url: isImg ? getCompressedImageUrl(original) : original, - originalUrl: original, - isImage: isImg, + imageResources: [], + fileResources: [], + imagePagination: { total: 0, data: [] }, + filePagination: { total: 0, data: [] }, }; - }) - .filter(Boolean); - return { - imageResources: processedResources.filter((res) => res.isImage), - fileResources: processedResources.filter((res) => !res.isImage), - }; - }, [resources]); + const isImage = (url: string) => /\.(png|jpg|jpeg|gif|webp)$/i.test(url); + + const processedResources = resources + .map((resource) => { + if (!resource?.url) return null; + const original = `http://${env.SERVER_IP}:${env.UPLOAD_PORT}/uploads/${resource.url}`; + const isImg = isImage(resource.url); + return { + ...resource, + url: isImg ? getCompressedImageUrl(original) : original, + originalUrl: original, + isImage: isImg, + }; + }) + .filter(Boolean); + + const allImageResources = processedResources.filter((res) => res.isImage); + const allFileResources = processedResources.filter((res) => !res.isImage); + + // 分页处理 + const imageStart = (imagePage - 1) * pageSize; + const fileStart = (filePage - 1) * pageSize; + + return { + imageResources: allImageResources.slice( + imageStart, + imageStart + pageSize + ), + fileResources: allFileResources.slice(fileStart, fileStart + pageSize), + imagePagination: { + total: allImageResources.length, + data: allImageResources, + }, + filePagination: { + total: allFileResources.length, + data: allFileResources, + }, + }; + }, [resources, imagePage, filePage]); const createMutation = api.resource.create.useMutation({}); const handleSubmit = async () => { @@ -95,7 +127,6 @@ export function VideoContent() { await createMutation.mutateAsync({ data: { fileId, - // description: description.trim(), isPublic: true, }, }); @@ -112,7 +143,6 @@ export function VideoContent() { } message.success("上传成功!"); setFileIds([]); - // setDescription(''); setUploaderKey((prev) => prev + 1); refetch(); // 刷新列表 } catch (error) { @@ -166,7 +196,7 @@ export function VideoContent() {
-

锦囊资源上传

+

资源上传

支持视频、图片、文档、PPT等多种格式文件

@@ -209,13 +239,6 @@ export function VideoContent() { setFileIds(value); }} /> - {/* {!isDomainAdmin && ( -
- - 只有管理员才能使用上传功能 - -
- )} */}
@@ -235,9 +258,15 @@ export function VideoContent() { {/* 文件展示区域 */}
{/* 图片资源展示 */} - {imageResources?.length > 0 && ( + {imagePagination?.total > 0 && (
-

图片列表

+
+

图片列表

+ + 共 {imagePagination.total} 张图片 + +
+ {imageResources.map((resource) => ( @@ -275,13 +304,32 @@ export function VideoContent() { ))} + + {/* 图片分页 */} + {imagePagination.total > pageSize && ( +
+ setImagePage(page)} + showSizeChanger={false} + /> +
+ )}
)} {/* 其他文件资源展示 */} - {fileResources?.length > 0 && ( + {filePagination?.total > 0 && (
-

文件列表

+
+

文件列表

+ + 共 {filePagination.total} 个文件 + +
+
{fileResources.map((resource) => (
))}
+ + {/* 文件分页 */} + {filePagination.total > pageSize && ( +
+ setFilePage(page)} + showSizeChanger={false} + /> +
+ )}
)}
diff --git a/apps/web/src/components/models/post/detail/PostCommentCard.tsx b/apps/web/src/components/models/post/detail/PostCommentCard.tsx index 7c06646..8cb78da 100755 --- a/apps/web/src/components/models/post/detail/PostCommentCard.tsx +++ b/apps/web/src/components/models/post/detail/PostCommentCard.tsx @@ -35,7 +35,7 @@ export default function PostCommentCard({