From 0e3c7787c44795e1c6fc8f8e3ee55dbfc4475eca Mon Sep 17 00:00:00 2001 From: longdayi <13477510+longdayilongdayi@user.noreply.gitee.com> Date: Thu, 27 Feb 2025 13:09:28 +0800 Subject: [PATCH 01/35] 02271309 --- scripts/git_stats.py | 503 ------------------------------------------- 1 file changed, 503 deletions(-) delete mode 100755 scripts/git_stats.py diff --git a/scripts/git_stats.py b/scripts/git_stats.py deleted file mode 100755 index 374834c..0000000 --- a/scripts/git_stats.py +++ /dev/null @@ -1,503 +0,0 @@ -import statistics -from git import Repo -from collections import defaultdict, Counter -from datetime import datetime, timedelta -import os - - -def get_contributor_stats(repo_path, start_date=None, end_date=None, branch='HEAD'): - """ - 获取仓库贡献者的详细统计信息 - - Args: - repo_path: Git仓库路径 - start_date: 开始日期(可选) - end_date: 结束日期(可选) - branch: 要分析的分支(默认为HEAD) - - Returns: - 包含每个贡献者详细统计信息的字典 - """ - # 初始化仓库对象 - repo = Repo(repo_path) - - # 存储统计结果 - stats = defaultdict(lambda: { - 'additions': 0, # 添加的行数 - 'deletions': 0, # 删除的行数 - 'commits': 0, # 提交次数 - 'files_modified': set(), # 修改过的文件集合 - 'file_types': defaultdict(int),# 各类型文件的修改次数 - 'commit_dates': set(), # 提交日期集合 - 'commit_hours': defaultdict(int), # 提交小时分布 - 'commit_weekdays': defaultdict(int), # 提交工作日分布 - 'largest_commit': 0, # 最大单次提交修改量 - 'first_commit': None, # 首次提交时间 - 'last_commit': None, # 最近提交时间 - 'commit_sizes': [], # 每次提交的大小,用于计算平均值和中位数 - 'commit_messages': [], # 提交消息列表 - 'commit_message_lengths': [], # 提交消息长度列表 - 'directories_modified': set(), # 修改过的目录集合 - 'co_authors': set(), # 合作者集合 - 'impact_score': 0, # 影响力得分 - 'complexity_score': 0, # 复杂度得分 - 'commit_by_month': defaultdict(int), # 按月份统计的提交次数 - 'commit_by_quarter': defaultdict(int), # 按季度统计的提交次数 - 'commit_by_year': defaultdict(int), # 按年份统计的提交次数 - 'commit_by_week': defaultdict(int), # 按周统计的提交次数 - 'file_operations': { # 文件操作统计 - 'created': set(), # 创建的文件 - 'deleted': set(), # 删除的文件 - 'modified': set(), # 修改的文件 - }, - 'review_comments': 0, # 代码审查评论数(如果可用) - 'merge_commits': 0, # 合并提交数 - 'commit_streak': 0, # 最长连续提交天数 - 'current_streak': 0, # 当前连续提交天数 - 'contribution_days': [], # 所有贡献的日期列表(用于热图) - 'code_churn': 0, # 代码周转率(添加后又删除的代码) - 'file_ownership': {}, # 文件所有权百分比 - 'key_files_modified': set(), # 修改过的关键文件 - 'refactoring_commits': 0, # 重构提交数(基于提交消息分析) - 'bug_fix_commits': 0, # 修复bug的提交数 - 'feature_commits': 0, # 新功能提交数 - 'documentation_commits': 0, # 文档相关提交数 - 'commit_size_distribution': defaultdict(int), # 提交大小分布 - 'collaboration_score': 0, # 协作得分 - 'consistency_score': 0, # 一致性得分 - 'expertise_areas': defaultdict(float), # 专业领域(目录/语言) - }) - - # 存储所有文件的修改者,用于计算协作指标 - file_authors = defaultdict(set) - - # 存储项目文件的重要性权重 (基于修改频率) - file_importance = Counter() - - # 存储用于检测关键词的正则表达式 - import re - refactor_pattern = re.compile(r'refactor|重构', re.IGNORECASE) - bugfix_pattern = re.compile(r'fix|修复|bug|问题|issue|错误', re.IGNORECASE) - feature_pattern = re.compile(r'feature|功能|新增|add|实现', re.IGNORECASE) - docs_pattern = re.compile(r'doc|文档|注释|comment', re.IGNORECASE) - - # 记录每位贡献者的提交日期,用于计算连续贡献天数 - author_commit_days = defaultdict(set) - - # 定义关键文件路径模式 (可以根据项目自定义) - key_file_patterns = [ - re.compile(r'package\.json$'), - re.compile(r'docker-compose\.yml$'), - re.compile(r'Dockerfile$'), - re.compile(r'tsconfig\..*\.json$'), - re.compile(r'/src/index\.[jt]s$'), - re.compile(r'README\.md$'), - re.compile(r'\.env'), - re.compile(r'/main\.[jt]s$'), - re.compile(r'/app\.[jt]s$'), - ] - - # 遍历所有提交 - for commit in repo.iter_commits(branch): - # 过滤日期 - commit_date = datetime.fromtimestamp(commit.committed_date) - if start_date and commit_date < start_date: - continue - if end_date and commit_date > end_date: - continue - - author = commit.author.name - stats[author]['commits'] += 1 - - # 记录提交日期和时间 - commit_day = commit_date.date() - stats[author]['commit_dates'].add(commit_day) - stats[author]['contribution_days'].append(commit_day) # 用于热图 - stats[author]['commit_hours'][commit_date.hour] += 1 - stats[author]['commit_weekdays'][commit_date.weekday()] += 1 - - # 添加到作者的提交日集合 - author_commit_days[author].add(commit_day) - - # 按时间段统计 - year = commit_date.year - month = commit_date.month - quarter = (month - 1) // 3 + 1 - week_num = commit_date.isocalendar()[1] - stats[author]['commit_by_year'][year] += 1 - stats[author]['commit_by_month'][f"{year}-{month:02d}"] += 1 - stats[author]['commit_by_quarter'][f"{year}-Q{quarter}"] += 1 - stats[author]['commit_by_week'][f"{year}-W{week_num:02d}"] += 1 - - # 分析提交消息,对提交进行分类 - commit_message = commit.message.strip() - if refactor_pattern.search(commit_message): - stats[author]['refactoring_commits'] += 1 - if bugfix_pattern.search(commit_message): - stats[author]['bug_fix_commits'] += 1 - if feature_pattern.search(commit_message): - stats[author]['feature_commits'] += 1 - if docs_pattern.search(commit_message): - stats[author]['documentation_commits'] += 1 - - # 记录首次和最近提交 - if stats[author]['first_commit'] is None or commit_date < stats[author]['first_commit']: - stats[author]['first_commit'] = commit_date - if stats[author]['last_commit'] is None or commit_date > stats[author]['last_commit']: - stats[author]['last_commit'] = commit_date - - # 记录提交消息 - commit_message = commit.message.strip() - stats[author]['commit_messages'].append(commit_message) - stats[author]['commit_message_lengths'].append(len(commit_message)) - - # 检测是否为合并提交 - if len(commit.parents) > 1: - stats[author]['merge_commits'] += 1 - - # 统计添加和删除的行数 - total_changes = 0 - modified_files = set() - created_files = set() - deleted_files = set() - directories = set() - - # 尝试获取提交前后的差异,以确定文件操作类型 - try: - if commit.parents: - parent = commit.parents[0] - diffs = parent.diff(commit) - for diff_item in diffs: - if diff_item.new_file: - if diff_item.b_path: - created_files.add(diff_item.b_path) - elif diff_item.deleted_file: - if diff_item.a_path: - deleted_files.add(diff_item.a_path) - else: - if diff_item.a_path: - modified_files.add(diff_item.a_path) - else: - # 对于首次提交,所有文件都是新创建的 - for file_path in commit.stats.files: - created_files.add(file_path) - except Exception as e: - # 如果获取差异失败,退回到简单的文件修改统计 - modified_files = set(commit.stats.files.keys()) - - for file_path, item in commit.stats.files.items(): - # 统计文件类型 - _, ext = os.path.splitext(file_path) - if ext: # 确保扩展名不为空 - stats[author]['file_types'][ext] += 1 - else: - stats[author]['file_types']['no_extension'] += 1 - - # 记录目录 - directory = os.path.dirname(file_path) - if directory: - directories.add(directory) - - # 记录修改的文件 - modified_files.add(file_path) - - # 记录文件的修改者,用于计算协作指标 - file_authors[file_path].add(author) - - # 统计添加和删除的行数 - stats[author]['additions'] += item['insertions'] - stats[author]['deletions'] += item['deletions'] - total_changes += item['insertions'] + item['deletions'] - - # 更新修改过的文件和目录集合 - stats[author]['files_modified'].update(modified_files) - stats[author]['directories_modified'].update(directories) - stats[author]['file_operations']['created'].update(created_files) - stats[author]['file_operations']['deleted'].update(deleted_files) - stats[author]['file_operations']['modified'].update(modified_files - created_files - deleted_files) - - # 记录本次提交的修改量 - stats[author]['commit_sizes'].append(total_changes) - - # 记录提交大小分布 - commit_size_category = "小型(1-10行)" if total_changes <= 10 else \ - "中型(11-100行)" if total_changes <= 100 else \ - "大型(101-500行)" if total_changes <= 500 else \ - "超大型(500+行)" - stats[author]['commit_size_distribution'][commit_size_category] += 1 - - # 更新最大单次提交修改量 - if total_changes > stats[author]['largest_commit']: - stats[author]['largest_commit'] = total_changes - - # 检查修改的文件是否为关键文件 - for file_path in modified_files: - for pattern in key_file_patterns: - if pattern.search(file_path): - stats[author]['key_files_modified'].add(file_path) - break - - # 更新文件重要性权重 - for file_path in modified_files: - file_importance[file_path] += 1 - - # 计算影响力得分 (基于修改的文件数和总修改行数) - impact = total_changes * len(modified_files) / 100 if modified_files else 0 - stats[author]['impact_score'] += impact - - # 计算文件协作度和文件所有权 - for file_path, authors in file_authors.items(): - # 如果只有一个作者修改了文件,则该作者100%拥有此文件 - if len(authors) == 1: - author = next(iter(authors)) - if 'file_ownership' not in stats[author]: - stats[author]['file_ownership'] = {} - stats[author]['file_ownership'][file_path] = 100.0 - else: - # 如果多个作者修改了文件,则按照每个作者的修改比例计算所有权 - for author in authors: - # 简化处理:平均分配所有权 - ownership_percent = 100.0 / len(authors) - if 'file_ownership' not in stats[author]: - stats[author]['file_ownership'] = {} - stats[author]['file_ownership'][file_path] = ownership_percent - - # 计算每个作者的连续提交天数 - for author, commit_days in author_commit_days.items(): - if not commit_days: - continue - - # 按日期排序 - sorted_days = sorted(commit_days) - - # 计算最长提交连续天数 - current_streak = 1 - max_streak = 1 - - for i in range(1, len(sorted_days)): - # 如果当前日期与前一天相差正好一天,则增加连续计数 - if (sorted_days[i] - sorted_days[i-1]).days == 1: - current_streak += 1 - else: - # 重置当前连续计数 - current_streak = 1 - - max_streak = max(max_streak, current_streak) - - # 记录最长连续提交天数 - stats[author]['commit_streak'] = max_streak - - # 计算当前连续提交天数 (到最后一个日期) - if sorted_days: - today = datetime.now().date() - days_since_last = (today - sorted_days[-1]).days - - if days_since_last <= 1: # 如果最后提交是今天或昨天 - current_streak = 1 - for i in range(len(sorted_days) - 1, 0, -1): - if (sorted_days[i] - sorted_days[i-1]).days == 1: - current_streak += 1 - else: - break - stats[author]['current_streak'] = current_streak - - # 后处理:计算派生指标并转换集合为计数 - for author, data in stats.items(): - # 将文件集合转换为数量 - data['files_count'] = len(data['files_modified']) - data['active_days'] = len(data['commit_dates']) - data['key_files_count'] = len(data['key_files_modified']) - - # 计算平均每次提交的修改量 - if data['commits'] > 0: - data['avg_commit_size'] = sum(data['commit_sizes']) / data['commits'] - data['median_commit_size'] = statistics.median(data['commit_sizes']) if data['commit_sizes'] else 0 - - # 计算代码复杂度得分 (基于修改量、文件数和一致性) - variability = statistics.stdev(data['commit_sizes']) if len(data['commit_sizes']) > 1 else 0 - data['complexity_score'] = (data['avg_commit_size'] * data['files_count'] * (1 + variability / 1000)) / 100 - - # 计算一致性得分 (提交大小和频率的一致性) - if variability > 0: - data['consistency_score'] = 100 * (1 - min(1, variability / data['avg_commit_size'])) - else: - data['consistency_score'] = 100 - else: - data['avg_commit_size'] = 0 - data['median_commit_size'] = 0 - data['complexity_score'] = 0 - data['consistency_score'] = 0 - - # 计算总修改量 - data['total_changes'] = data['additions'] + data['deletions'] - - # 计算代码周转率 (code churn) - 估算值 - if data['additions'] > 0 and data['deletions'] > 0: - data['code_churn'] = min(data['additions'], data['deletions']) / max(data['additions'], data['deletions']) * 100 - - # 计算活跃时长(天) - if data['first_commit'] and data['last_commit']: - delta = data['last_commit'] - data['first_commit'] - data['active_period_days'] = delta.days + 1 - - # 计算活跃密度 (提交数/活跃天数) - if delta.days > 0: - data['activity_density'] = data['commits'] / delta.days - else: - data['activity_density'] = data['commits'] - else: - data['active_period_days'] = 0 - data['activity_density'] = 0 - - # 计算协作得分 (基于参与修改的共享文件比例) - total_files = len(data['files_modified']) - shared_files = sum(1 for f in data['files_modified'] if len(file_authors[f]) > 1) - if total_files > 0: - data['collaboration_score'] = (shared_files / total_files) * 100 - - # 计算专业领域 (基于文件类型和目录) - if data['file_types']: - primary_type = max(data['file_types'].items(), key=lambda x: x[1])[0] - data['primary_file_type'] = primary_type - data['primary_file_type_percent'] = (data['file_types'][primary_type] / sum(data['file_types'].values())) * 100 - - # 统计目录专业度 - if data['directories_modified']: - dir_counts = Counter() - for directory in data['directories_modified']: - dir_counts[directory] += 1 - - # 检查父目录 - parent = os.path.dirname(directory) - while parent: - dir_counts[parent] += 0.5 # 对父目录给予较低的权重 - parent = os.path.dirname(parent) - - # 找出专业领域(最常修改的目录) - if dir_counts: - primary_dir = max(dir_counts.items(), key=lambda x: x[1])[0] - data['primary_directory'] = primary_dir - data['expertise_areas'][primary_dir] = dir_counts[primary_dir] / sum(dir_counts.values()) - - return stats - -def print_stats(stats): - """打印贡献者统计信息的详细报告""" - # 基本信息表头 - print("\n===== 贡献者基本统计 =====") - print("{:<20} {:<10} {:<10} {:<10} {:<10} {:<15} {:<15}".format( - "作者", "提交数", "添加行数", "删除行数", "总修改行数", "修改文件数", "活跃天数")) - print("-" * 90) - - # 按总修改量排序 - for author, data in sorted(stats.items(), key=lambda x: x[1]['total_changes'], reverse=True): - print("{:<20} {:<10} {:<10} {:<10} {:<10} {:<15} {:<15}".format( - author, - data['commits'], - data['additions'], - data['deletions'], - data['total_changes'], - data['files_count'], - data['active_days'] - )) - - # 为每个贡献者打印详细信息 - for author, data in sorted(stats.items(), key=lambda x: x[1]['total_changes'], reverse=True): - print(f"\n\n===== {author} 的详细贡献统计 =====") - - # 活跃时间信息 - if data['first_commit'] and data['last_commit']: - print(f"首次提交时间: {data['first_commit'].strftime('%Y-%m-%d %H:%M:%S')}") - print(f"最近提交时间: {data['last_commit'].strftime('%Y-%m-%d %H:%M:%S')}") - print(f"活跃时长: {data['active_period_days']} 天") - - # 提交规模信息 - print(f"平均每次提交修改: {data['avg_commit_size']:.2f} 行") - print(f"最大单次提交修改: {data['largest_commit']} 行") - - # 文件类型分布 - if data['file_types']: - print("\n文件类型分布:") - for ext, count in sorted(data['file_types'].items(), key=lambda x: x[1], reverse=True): - print(f" {ext}: {count} 次修改") - - # 提交时间分布 - if data['commit_hours']: - print("\n提交时间分布:") - for hour in range(24): - count = data['commit_hours'].get(hour, 0) - if count > 0: - print(f" {hour:02d}:00-{hour+1:02d}:00: {count} 次提交") - - # 工作日分布 - if data['commit_weekdays']: - weekday_names = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"] - print("\n工作日分布:") - for day in range(7): - count = data['commit_weekdays'].get(day, 0) - if count > 0: - print(f" {weekday_names[day]}: {count} 次提交") - -def get_team_summary(stats): - """生成团队整体统计摘要""" - summary = { - 'total_commits': 0, - 'total_additions': 0, - 'total_deletions': 0, - 'total_files': set(), - 'contributors': len(stats), - 'first_commit': None, - 'last_commit': None, - } - - for author, data in stats.items(): - summary['total_commits'] += data['commits'] - summary['total_additions'] += data['additions'] - summary['total_deletions'] += data['deletions'] - summary['total_files'].update(data['files_modified']) - - # 更新首次和最近提交 - if data['first_commit']: - if summary['first_commit'] is None or data['first_commit'] < summary['first_commit']: - summary['first_commit'] = data['first_commit'] - - if data['last_commit']: - if summary['last_commit'] is None or data['last_commit'] > summary['last_commit']: - summary['last_commit'] = data['last_commit'] - - return summary - -def print_team_summary(summary): - """打印团队整体统计摘要""" - print("\n===== 团队整体统计 =====") - print(f"贡献者数量: {summary['contributors']}") - print(f"总提交次数: {summary['total_commits']}") - print(f"总添加行数: {summary['total_additions']}") - print(f"总删除行数: {summary['total_deletions']}") - print(f"总修改行数: {summary['total_additions'] + summary['total_deletions']}") - print(f"修改的文件数: {len(summary['total_files'])}") - - if summary['first_commit'] and summary['last_commit']: - print(f"项目起始时间: {summary['first_commit'].strftime('%Y-%m-%d')}") - print(f"最近活动时间: {summary['last_commit'].strftime('%Y-%m-%d')}") - delta = summary['last_commit'] - summary['first_commit'] - print(f"项目活跃时长: {delta.days + 1} 天") -if __name__ == "__main__": - # 设置仓库路径(当前目录) - repo_path = '.' - - # 设置日期范围(示例) - # 注意:这里使用的是2025年的日期,可能需要根据实际情况调整 - start_date = datetime(2025, 1, 1) # 修改为更合理的日期范围 - end_date = datetime(2025, 12, 31) - - print(f"分析Git仓库: {os.path.abspath(repo_path)}") - print(f"时间范围: {start_date.strftime('%Y-%m-%d')} 至 {end_date.strftime('%Y-%m-%d')}") - - # 获取统计信息 - stats = get_contributor_stats(repo_path, start_date, end_date) - - # 打印团队摘要 - team_summary = get_team_summary(stats) - print_team_summary(team_summary) - print(stats) \ No newline at end of file From fe9fd1240c5faed61bed27eef078379e26baf6eb Mon Sep 17 00:00:00 2001 From: Li1304553726 <1304553726@qq.com> Date: Thu, 27 Feb 2025 15:15:37 +0800 Subject: [PATCH 02/35] add . --- config/nginx/conf.d/web.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/nginx/conf.d/web.conf b/config/nginx/conf.d/web.conf index 67302b8..2c6260a 100755 --- a/config/nginx/conf.d/web.conf +++ b/config/nginx/conf.d/web.conf @@ -100,7 +100,7 @@ server { # 仅供内部使用 internal; # 代理到认证服务 - proxy_pass http://host.docker.internal:3000/auth/file; + proxy_pass http://host.docker.internal:3001/auth/file; # 请求优化:不传递请求体 proxy_pass_request_body off; From 490947e23163203454ef8ee8c1da01a97afa59e6 Mon Sep 17 00:00:00 2001 From: Rao <1227431568@qq.com> Date: Thu, 27 Feb 2025 16:11:53 +0800 Subject: [PATCH 03/35] rht --- .../components/common/editor/MindEditor.tsx | 34 ++++++++++++++----- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/apps/web/src/components/common/editor/MindEditor.tsx b/apps/web/src/components/common/editor/MindEditor.tsx index 4528b29..bd4c877 100755 --- a/apps/web/src/components/common/editor/MindEditor.tsx +++ b/apps/web/src/components/common/editor/MindEditor.tsx @@ -7,22 +7,24 @@ import { PostDto, PostType, Prisma, + RolePerms, Taxonomy, } from "@nice/common"; import TermSelect from "../../models/term/term-select"; import DepartmentSelect from "../../models/department/department-select"; -import { useEffect, useRef, useState } from "react"; +import { useEffect, useMemo, useRef, useState } from "react"; import toast from "react-hot-toast"; import { MindElixirInstance } from "mind-elixir"; import MindElixir from "mind-elixir"; import { useTusUpload } from "@web/src/hooks/useTusUpload"; import { useNavigate } from "react-router-dom"; +import { useAuth } from "@web/src/providers/auth-provider"; const MIND_OPTIONS = { direction: MindElixir.SIDE, draggable: true, contextMenu: true, toolBar: true, - nodeMenu: true, + //nodeMenu: true, keypress: true, locale: "zh_CN" as const, theme: { @@ -53,6 +55,7 @@ const MIND_OPTIONS = { export default function MindEditor({ id }: { id?: string }) { const containerRef = useRef(null); const [instance, setInstance] = useState(null); + const { isAuthenticated, user, hasSomePermissions } = useAuth(); const { data: post, isLoading }: { data: PostDto; isLoading: boolean } = api.post.findFirst.useQuery({ @@ -61,6 +64,11 @@ export default function MindEditor({ id }: { id?: string }) { }, select: postDetailSelect, }); + const canEdit: boolean = useMemo(() => { + //登录了且是作者、超管、无id新建模式 + const isAuth = isAuthenticated && user?.id == post?.authorId; + return !id || isAuth || hasSomePermissions(RolePerms.MANAGE_ANY_POST); + }, [user]) const navigate = useNavigate(); const { create, update } = usePost(); const { data: taxonomies } = api.taxonomy.getAll.useQuery({ @@ -89,11 +97,21 @@ export default function MindEditor({ id }: { id?: string }) { const mind = new MindElixir({ ...MIND_OPTIONS, el: containerRef.current, + before:{ + beginEdit(){ + return canEdit + } + }, + draggable: canEdit, // 禁用拖拽 + contextMenu: canEdit, // 禁用右键菜单 + toolBar: canEdit, // 禁用工具栏 + nodeMenu: canEdit, // 禁用节点右键菜单 + keypress: canEdit, // 禁用键盘快捷键 }); mind.init(MindElixir.new("新学习路径")); containerRef.current.hidden = true; setInstance(mind); - }, []); + }, [canEdit]); useEffect(() => { if ((!id || post) && instance) { containerRef.current.hidden = false; @@ -160,13 +178,13 @@ export default function MindEditor({ id }: { id?: string }) { } console.log(result); }, - (error) => {}, + (error) => { }, `mind-thumb-${new Date().toString()}` ); }; return ( -
- {taxonomies && ( +
+ {canEdit && taxonomies && (
{ console.log(values); @@ -205,8 +223,8 @@ export default function MindEditor({ id }: { id?: string }) {
)} -
- {instance && } +
e.preventDefault()} /> + {canEdit && instance && } {isLoading && (
Date: Thu, 27 Feb 2025 16:51:47 +0800 Subject: [PATCH 04/35] rht --- .../src/components/common/editor/MindEditor.tsx | 15 ++++++--------- apps/web/src/index.css | 2 +- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/apps/web/src/components/common/editor/MindEditor.tsx b/apps/web/src/components/common/editor/MindEditor.tsx index bd4c877..b1d4a39 100755 --- a/apps/web/src/components/common/editor/MindEditor.tsx +++ b/apps/web/src/components/common/editor/MindEditor.tsx @@ -24,7 +24,7 @@ const MIND_OPTIONS = { draggable: true, contextMenu: true, toolBar: true, - //nodeMenu: true, + nodeMenu: true, keypress: true, locale: "zh_CN" as const, theme: { @@ -67,7 +67,7 @@ export default function MindEditor({ id }: { id?: string }) { const canEdit: boolean = useMemo(() => { //登录了且是作者、超管、无id新建模式 const isAuth = isAuthenticated && user?.id == post?.authorId; - return !id || isAuth || hasSomePermissions(RolePerms.MANAGE_ANY_POST); + return !Boolean(id) || isAuth || hasSomePermissions(RolePerms.MANAGE_ANY_POST); }, [user]) const navigate = useNavigate(); const { create, update } = usePost(); @@ -183,14 +183,11 @@ export default function MindEditor({ id }: { id?: string }) { ); }; return ( -
+
{canEdit && taxonomies && (
{ - console.log(values); - }} form={form} - className=" bg-white p-2 "> + className=" bg-white p-4 ">
{taxonomies.map((tax, index) => ( @@ -217,13 +214,13 @@ export default function MindEditor({ id }: { id?: string }) { />
-
)} -
e.preventDefault()} /> +
e.preventDefault()} /> {canEdit && instance && } {isLoading && (
Date: Thu, 27 Feb 2025 17:02:24 +0800 Subject: [PATCH 05/35] add --- apps/web/src/app/main/layout/UserMenu/UserMenu.tsx | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/apps/web/src/app/main/layout/UserMenu/UserMenu.tsx b/apps/web/src/app/main/layout/UserMenu/UserMenu.tsx index 17c3413..fc63758 100755 --- a/apps/web/src/app/main/layout/UserMenu/UserMenu.tsx +++ b/apps/web/src/app/main/layout/UserMenu/UserMenu.tsx @@ -86,7 +86,7 @@ export function UserMenu() { setModalOpen(true); }, }, - + canManageAnyStaff && { icon: , label: "设置", @@ -159,13 +159,6 @@ export function UserMenu() { aria-hidden="true" />
- - {/* 用户信息,显示在 Avatar 右侧 */} -
- - {user?.showname || user?.username} - -
From 096f28aa0d14e8046db6c912d25b3439b7696e60 Mon Sep 17 00:00:00 2001 From: Rao <1227431568@qq.com> Date: Thu, 27 Feb 2025 19:17:23 +0800 Subject: [PATCH 06/35] rht --- apps/web/src/app/main/layout/MainLayout.tsx | 2 +- apps/web/src/components/common/editor/MindEditor.tsx | 2 +- apps/web/src/components/models/post/SubPost/CourseCard.tsx | 2 +- apps/web/src/components/models/post/SubPost/PathCard.tsx | 1 + 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/web/src/app/main/layout/MainLayout.tsx b/apps/web/src/app/main/layout/MainLayout.tsx index fa6627a..739bcaa 100755 --- a/apps/web/src/app/main/layout/MainLayout.tsx +++ b/apps/web/src/app/main/layout/MainLayout.tsx @@ -11,7 +11,7 @@ export function MainLayout() {
- + diff --git a/apps/web/src/components/common/editor/MindEditor.tsx b/apps/web/src/components/common/editor/MindEditor.tsx index b1d4a39..88d745b 100755 --- a/apps/web/src/components/common/editor/MindEditor.tsx +++ b/apps/web/src/components/common/editor/MindEditor.tsx @@ -66,7 +66,7 @@ export default function MindEditor({ id }: { id?: string }) { }); const canEdit: boolean = useMemo(() => { //登录了且是作者、超管、无id新建模式 - const isAuth = isAuthenticated && user?.id == post?.authorId; + const isAuth = isAuthenticated && user?.id == post?.author.id return !Boolean(id) || isAuth || hasSomePermissions(RolePerms.MANAGE_ANY_POST); }, [user]) const navigate = useNavigate(); diff --git a/apps/web/src/components/models/post/SubPost/CourseCard.tsx b/apps/web/src/components/models/post/SubPost/CourseCard.tsx index 25ce782..6140607 100644 --- a/apps/web/src/components/models/post/SubPost/CourseCard.tsx +++ b/apps/web/src/components/models/post/SubPost/CourseCard.tsx @@ -8,7 +8,7 @@ export default function CourseCard({ post }: { post: PostDto }) { post={post} onClick={() => { navigate(`/course/${post?.id}/detail`); - + window.scrollTo({ top: 0, behavior: "smooth" }); }}> ); } diff --git a/apps/web/src/components/models/post/SubPost/PathCard.tsx b/apps/web/src/components/models/post/SubPost/PathCard.tsx index c6e5a85..9de4ed0 100644 --- a/apps/web/src/components/models/post/SubPost/PathCard.tsx +++ b/apps/web/src/components/models/post/SubPost/PathCard.tsx @@ -9,6 +9,7 @@ export default function PathCard({ post }: { post: PostDto }) { post={post} onClick={() => { navigate(`/path/editor/${post?.id}`); + window.scrollTo({ top: 0, behavior: "smooth" }); }}> ); } From 6e8ba21b17afb80b23f96c4f8eb2b59312f03d79 Mon Sep 17 00:00:00 2001 From: ditiqi Date: Thu, 27 Feb 2025 19:31:48 +0800 Subject: [PATCH 07/35] add --- apps/web/public/logo.svg | 37 +++++++++++++++++++++ apps/web/src/app/main/layout/MainHeader.tsx | 11 +++--- config/nginx/conf.d/web.conf | 2 +- web-dist/index.html | 0 4 files changed, 45 insertions(+), 5 deletions(-) create mode 100755 apps/web/public/logo.svg mode change 100755 => 100644 web-dist/index.html diff --git a/apps/web/public/logo.svg b/apps/web/public/logo.svg new file mode 100755 index 0000000..0f73392 --- /dev/null +++ b/apps/web/public/logo.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/src/app/main/layout/MainHeader.tsx b/apps/web/src/app/main/layout/MainHeader.tsx index a0c9399..3ae9245 100755 --- a/apps/web/src/app/main/layout/MainHeader.tsx +++ b/apps/web/src/app/main/layout/MainHeader.tsx @@ -19,9 +19,10 @@ export function MainHeader() { const { searchValue, setSearchValue } = useMainContext(); return ( -
-
-
+
+
+
+
navigate("/")} className="text-2xl font-bold bg-gradient-to-r from-primary-600 via-primary-500 to-primary-400 bg-clip-text text-transparent hover:scale-105 transition-transform cursor-pointer"> @@ -30,7 +31,7 @@ export function MainHeader() {
-
+
+
+
{isAuthenticated && ( <> diff --git a/config/nginx/conf.d/web.conf b/config/nginx/conf.d/web.conf index 2c6260a..67302b8 100755 --- a/config/nginx/conf.d/web.conf +++ b/config/nginx/conf.d/web.conf @@ -100,7 +100,7 @@ server { # 仅供内部使用 internal; # 代理到认证服务 - proxy_pass http://host.docker.internal:3001/auth/file; + proxy_pass http://host.docker.internal:3000/auth/file; # 请求优化:不传递请求体 proxy_pass_request_body off; diff --git a/web-dist/index.html b/web-dist/index.html old mode 100755 new mode 100644 From ee9df61320259d1f83c0f2845f953f774d9c9c40 Mon Sep 17 00:00:00 2001 From: ditiqi Date: Thu, 27 Feb 2025 19:52:01 +0800 Subject: [PATCH 08/35] add --- apps/web/public/logo.svg | 38 +-------------------- apps/web/public/vite.svg | 2 +- apps/web/src/app/main/layout/MainHeader.tsx | 2 +- 3 files changed, 3 insertions(+), 39 deletions(-) diff --git a/apps/web/public/logo.svg b/apps/web/public/logo.svg index 0f73392..39a6980 100755 --- a/apps/web/public/logo.svg +++ b/apps/web/public/logo.svg @@ -1,37 +1 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + \ No newline at end of file diff --git a/apps/web/public/vite.svg b/apps/web/public/vite.svg index e7b8dfb..39a6980 100755 --- a/apps/web/public/vite.svg +++ b/apps/web/public/vite.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/apps/web/src/app/main/layout/MainHeader.tsx b/apps/web/src/app/main/layout/MainHeader.tsx index 3ae9245..705f68b 100755 --- a/apps/web/src/app/main/layout/MainHeader.tsx +++ b/apps/web/src/app/main/layout/MainHeader.tsx @@ -22,7 +22,7 @@ export function MainHeader() {
- +
navigate("/")} className="text-2xl font-bold bg-gradient-to-r from-primary-600 via-primary-500 to-primary-400 bg-clip-text text-transparent hover:scale-105 transition-transform cursor-pointer"> From 49d3f613fcf4830fae6feeb2aba47dece3afa151 Mon Sep 17 00:00:00 2001 From: ditiqi Date: Thu, 27 Feb 2025 21:45:40 +0800 Subject: [PATCH 09/35] add --- apps/web/public/placeholder.webp | Bin 0 -> 1900 bytes apps/web/public/vite.svg | 2 +- .../main/layout/BasePost/BasePostLayout.tsx | 2 +- apps/web/src/app/main/layout/MainHeader.tsx | 31 +++--- .../src/app/main/layout/NavigationMenu.tsx | 2 +- .../src/app/main/path/components/TermInfo.tsx | 10 +- .../common/container/CollapsibleContent.tsx | 2 +- .../common/uploader/ResourceShower.tsx | 3 +- .../course/detail/CourseDetailContext.tsx | 14 ++- .../course/detail/CourseDetailDescription.tsx | 88 +++++++++--------- .../course/detail/CourseDetailDisplayArea.tsx | 4 +- .../course/detail/CourseDetailTitle.tsx | 29 +++--- .../src/components/models/post/PostCard.tsx | 25 +++-- packages/common/src/models/post.ts | 68 +++++++++++--- packages/common/src/models/select.ts | 14 ++- 15 files changed, 179 insertions(+), 115 deletions(-) create mode 100644 apps/web/public/placeholder.webp diff --git a/apps/web/public/placeholder.webp b/apps/web/public/placeholder.webp new file mode 100644 index 0000000000000000000000000000000000000000..1f474a5eae6104fb2afe57410c2a51245b548765 GIT binary patch literal 1900 zcmWIYbaP8#XJ80-bqWXzuuzBqvJH9|<}zxvFn?sUGnD9XQBv%fvP6KfDJpuuyjuAr z!2?t8GXB3^z2r4p!rcY2DX#=S$H#q}yXTZc(=5TeftR{6+)LfPG#AQVmDO0*QXW#W zXqXBYFUy(v?t{{WV+q38JSwqU1L5 z$$FJlpfAU}6)#;)ly@z3b?e)yxP0O}hBAQ#Z$5j5x$fB-@%*6AHCJoy?&USIxe-UN zbX$pG2B^f6Q}JPWsk5C$rL}4@_Z;Y49{79C#Lo`$)v6AszTL@lfA=pEJzChhBa)JK zUguvPIU(YU)f)Q@ul5&z&jo(`9lJv`eMetbmY5S-q;bt&sn8*%));>3Cd-7#-}~!- zD6G66HhJP6ufN41CINvpoWDW7SiDr^Lf@;tDZw_E1Vze47`S$Ze%5u(zZzDmFf+`6 zyIko;>n@T+wMoRu2pe(SuhVZcFJ-t$BR2t-nDp(&s%%hHF3hbKMIm&Qkrpgg`ejfeG7F7JeXe?&Her%XZgLE ztBS(s#P7;tIy|5ISYQ0d-BGhzC!nQNx2Mz0|B0{7OyUs~JaLzK(IFP!y8b&)uI1KD z3G3q0Z?w-q(O}VZj^py5JncUf2`<-xAsKN*W|~U0>b##^7$$?W#*MNmXweV%7Tmeu zR57fD9T^z@{eQiZ0U0prB${kd^Y2V%m3Na8T3kIZt@>kFc#!|WC0rZV*Uw1tTV?A1 z;O%s2#U!2D>wce(l^4J3y;|`y%=fkWE`#YSExX$rmfmC9H20^dkJy2;jTNUSc{?Np z-V>I)$my|a!hxKN55Y!~dUk;_SqAe!)lnsfl_Dr|!S<_WH{e$;(Sh*v!+;ocyEsh9{Es*@4qb z4_r2H-6n5t&fz|Pr}bnf&I1z4hwQg}=P+EvH?e8TG(+A8kFGFs-+1<6agmB|=lGgS6um!9f7v3qeU03q7o3kzo^9h;`|^+5>O&8?)3X~C9b-hTIleII zJ>JdOGxwKns$pquZ+=SFJG0*NCxzxMI(#+s=J$)&e>$9XKDS%#n8%ShCpI_dJ#6Sd z^!`dbokXh+?; zlDmIeA6@%4{o<0xpQSsi?@xZidccvD@$U0mZf~>#aux;Je)rnBw??GMQeTWw`>(~1 wYQw1>9MY*hLPxlMDkUdymYtitbia3SlgWOoM@|`QlU&#)#~c(uPSj8~0FUs1WB>pF literal 0 HcmV?d00001 diff --git a/apps/web/public/vite.svg b/apps/web/public/vite.svg index 39a6980..78260dd 100755 --- a/apps/web/public/vite.svg +++ b/apps/web/public/vite.svg @@ -1 +1 @@ - \ No newline at end of file + diff --git a/apps/web/src/app/main/layout/BasePost/BasePostLayout.tsx b/apps/web/src/app/main/layout/BasePost/BasePostLayout.tsx index f93ea11..09c7852 100644 --- a/apps/web/src/app/main/layout/BasePost/BasePostLayout.tsx +++ b/apps/web/src/app/main/layout/BasePost/BasePostLayout.tsx @@ -20,7 +20,7 @@ export function BasePostLayout({
-
{children}
+
{children}
diff --git a/apps/web/src/app/main/layout/MainHeader.tsx b/apps/web/src/app/main/layout/MainHeader.tsx index 705f68b..69b2344 100755 --- a/apps/web/src/app/main/layout/MainHeader.tsx +++ b/apps/web/src/app/main/layout/MainHeader.tsx @@ -19,26 +19,27 @@ export function MainHeader() { const { searchValue, setSearchValue } = useMainContext(); return ( -
-
-
- -
navigate("/")} - className="text-2xl font-bold bg-gradient-to-r from-primary-600 via-primary-500 to-primary-400 bg-clip-text text-transparent hover:scale-105 transition-transform cursor-pointer"> - 烽火慕课 -
- +
+ {/* 左侧区域 - 设置为不收缩 */} +
+ +
navigate("/")} + className="text-2xl font-bold bg-gradient-to-r from-primary-600 via-primary-500 to-primary-400 bg-clip-text text-transparent hover:scale-105 transition-transform cursor-pointer whitespace-nowrap"> + 烽火慕课
+
-
+ + {/* 中间搜索区域 - 允许适当收缩但保持可用性 */} +
} placeholder="搜索课程" - className="w-96 rounded-full" + className="w-full md:w-96 rounded-full" value={searchValue} onClick={(e) => { if (!window.location.pathname.startsWith("/search")) { @@ -61,8 +62,10 @@ export function MainHeader() { }} />
-
-
+ + {/* 右侧区域 - 可以灵活收缩 */} +
+
{isAuthenticated && ( <> - - )} - {isAuthenticated && ( + }} + /> +
+ {isAuthenticated && ( + <> - )} - {isAuthenticated ? ( - - ) : ( - - )} -
+ + )} + {isAuthenticated && ( + + )} + {isAuthenticated ? ( + + ) : ( + + )}
+ ); } diff --git a/apps/web/src/index.css b/apps/web/src/index.css index f65bd8b..34de7f4 100755 --- a/apps/web/src/index.css +++ b/apps/web/src/index.css @@ -161,7 +161,3 @@ /* 去除最后一行的底部边框 */ } -.mind-editor { - height: calc(100vh - 285px); - width: 100%; -} \ No newline at end of file From 3d78ee7b3b63cb991413869c27af0d428505aecb Mon Sep 17 00:00:00 2001 From: longdayi <13477510+longdayilongdayi@user.noreply.gitee.com> Date: Thu, 27 Feb 2025 22:01:56 +0800 Subject: [PATCH 11/35] 02272202 --- apps/web/src/app/main/layout/MainHeader.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/web/src/app/main/layout/MainHeader.tsx b/apps/web/src/app/main/layout/MainHeader.tsx index 9e909fc..0d5a283 100755 --- a/apps/web/src/app/main/layout/MainHeader.tsx +++ b/apps/web/src/app/main/layout/MainHeader.tsx @@ -76,8 +76,8 @@ export function MainHeader() { : "/course/editor"; navigate(url); }} - className="flex items-center space-x-1 bg-gradient-to-r from-blue-500 to-blue-600 text-white hover:from-blue-600 hover:to-blue-700 border-none shadow-md hover:shadow-lg transition-all" - icon={}> + type="primary" + > {id ? "编辑课程" : "创建课程"} From 5ab71bdeecef3e9dd50180742c90b77d02a0740f Mon Sep 17 00:00:00 2001 From: longdayi <13477510+longdayilongdayi@user.noreply.gitee.com> Date: Thu, 27 Feb 2025 22:02:19 +0800 Subject: [PATCH 12/35] 02272202 --- apps/web/src/app/main/layout/MainHeader.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/apps/web/src/app/main/layout/MainHeader.tsx b/apps/web/src/app/main/layout/MainHeader.tsx index 0d5a283..1735bbd 100755 --- a/apps/web/src/app/main/layout/MainHeader.tsx +++ b/apps/web/src/app/main/layout/MainHeader.tsx @@ -11,8 +11,6 @@ import { useNavigate, useParams, useSearchParams } from "react-router-dom"; import { UserMenu } from "./UserMenu/UserMenu"; import { NavigationMenu } from "./NavigationMenu"; import { useMainContext } from "./MainProvider"; -import { Header } from "antd/es/layout/layout"; - export function MainHeader() { const { isAuthenticated, user } = useAuth(); const { id } = useParams(); From e6e2050ce108be9044ce3f14a46a970b0ae9bbfb Mon Sep 17 00:00:00 2001 From: ditiqi Date: Thu, 27 Feb 2025 22:06:26 +0800 Subject: [PATCH 13/35] add --- apps/web/src/app/main/layout/MainHeader.tsx | 3 +- .../components/common/editor/MindEditor.tsx | 77 ++++++------------- .../src/components/common/editor/constant.ts | 34 ++++++++ 3 files changed, 60 insertions(+), 54 deletions(-) create mode 100644 apps/web/src/components/common/editor/constant.ts diff --git a/apps/web/src/app/main/layout/MainHeader.tsx b/apps/web/src/app/main/layout/MainHeader.tsx index 69b2344..d5b74aa 100755 --- a/apps/web/src/app/main/layout/MainHeader.tsx +++ b/apps/web/src/app/main/layout/MainHeader.tsx @@ -1,4 +1,4 @@ -import { Input, Layout, Avatar, Button, Dropdown } from "antd"; +import { Input, Button } from "antd"; import { EditFilled, PlusOutlined, @@ -10,7 +10,6 @@ import { useNavigate, useParams, useSearchParams } from "react-router-dom"; import { UserMenu } from "./UserMenu/UserMenu"; import { NavigationMenu } from "./NavigationMenu"; import { useMainContext } from "./MainProvider"; -import { Header } from "antd/es/layout/layout"; export function MainHeader() { const { isAuthenticated, user } = useAuth(); diff --git a/apps/web/src/components/common/editor/MindEditor.tsx b/apps/web/src/components/common/editor/MindEditor.tsx index 88d745b..59843de 100755 --- a/apps/web/src/components/common/editor/MindEditor.tsx +++ b/apps/web/src/components/common/editor/MindEditor.tsx @@ -3,12 +3,11 @@ import NodeMenu from "./NodeMenu"; import { api, usePost } from "@nice/client"; import { ObjectType, + PathDto, postDetailSelect, - PostDto, PostType, Prisma, RolePerms, - Taxonomy, } from "@nice/common"; import TermSelect from "../../models/term/term-select"; import DepartmentSelect from "../../models/department/department-select"; @@ -19,45 +18,12 @@ import MindElixir from "mind-elixir"; import { useTusUpload } from "@web/src/hooks/useTusUpload"; import { useNavigate } from "react-router-dom"; import { useAuth } from "@web/src/providers/auth-provider"; -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", - }, - }, -}; +import { MIND_OPTIONS } from "./constant"; export default function MindEditor({ id }: { id?: string }) { const containerRef = useRef(null); const [instance, setInstance] = useState(null); 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({ where: { id, @@ -66,9 +32,9 @@ export default function MindEditor({ id }: { id?: string }) { }); const canEdit: boolean = useMemo(() => { //登录了且是作者、超管、无id新建模式 - const isAuth = isAuthenticated && user?.id == post?.author.id - return !Boolean(id) || isAuth || hasSomePermissions(RolePerms.MANAGE_ANY_POST); - }, [user]) + const isAuth = isAuthenticated && user?.id === post?.author?.id; + return !!id || isAuth || hasSomePermissions(RolePerms.MANAGE_ANY_POST); + }, [user]); const navigate = useNavigate(); const { create, update } = usePost(); const { data: taxonomies } = api.taxonomy.getAll.useQuery({ @@ -91,16 +57,15 @@ export default function MindEditor({ id }: { id?: string }) { form.setFieldsValue(formData); } }, [post, form, instance, id]); - useEffect(() => { if (!containerRef.current) return; const mind = new MindElixir({ ...MIND_OPTIONS, el: containerRef.current, - before:{ - beginEdit(){ - return canEdit - } + before: { + beginEdit() { + return canEdit; + }, }, draggable: canEdit, // 禁用拖拽 contextMenu: canEdit, // 禁用右键菜单 @@ -116,7 +81,9 @@ export default function MindEditor({ id }: { id?: string }) { if ((!id || post) && instance) { containerRef.current.hidden = false; instance.toCenter(); - instance.refresh((post as any)?.meta); + if (post?.meta?.nodeData) { + instance.refresh(post?.meta); + } } }, [id, post, instance]); const handleSave = async () => { @@ -178,16 +145,14 @@ export default function MindEditor({ id }: { id?: string }) { } console.log(result); }, - (error) => { }, + (error) => {}, `mind-thumb-${new Date().toString()}` ); }; return (
{canEdit && taxonomies && ( -
+
{taxonomies.map((tax, index) => ( @@ -214,13 +179,21 @@ export default function MindEditor({ id }: { id?: string }) { />
-
)} -
e.preventDefault()} /> +
e.preventDefault()} + /> {canEdit && instance && } {isLoading && (
Date: Thu, 27 Feb 2025 22:08:05 +0800 Subject: [PATCH 14/35] rht --- apps/web/src/components/common/editor/MindEditor.tsx | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/apps/web/src/components/common/editor/MindEditor.tsx b/apps/web/src/components/common/editor/MindEditor.tsx index 88d745b..fb07164 100755 --- a/apps/web/src/components/common/editor/MindEditor.tsx +++ b/apps/web/src/components/common/editor/MindEditor.tsx @@ -56,7 +56,6 @@ export default function MindEditor({ id }: { id?: string }) { const containerRef = useRef(null); const [instance, setInstance] = useState(null); const { isAuthenticated, user, hasSomePermissions } = useAuth(); - const { data: post, isLoading }: { data: PostDto; isLoading: boolean } = api.post.findFirst.useQuery({ where: { @@ -182,8 +181,14 @@ export default function MindEditor({ id }: { id?: string }) { `mind-thumb-${new Date().toString()}` ); }; + useEffect(()=>{ + containerRef.current.style.height = `${Math.floor(window.innerHeight/1.25)}px` + },[]) + return ( -
+ +
+ {canEdit && taxonomies && (
)} -
e.preventDefault()} /> +
e.preventDefault()}/> {canEdit && instance && } {isLoading && (
Date: Thu, 27 Feb 2025 22:26:36 +0800 Subject: [PATCH 15/35] update --- apps/web/package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/web/package.json b/apps/web/package.json index 1456e66..f10e338 100755 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -36,7 +36,6 @@ "@nice/iconer": "workspace:^", "@nice/utils": "workspace:^", "mind-elixir": "workspace:^", - "@mind-elixir/node-menu": "workspace:*", "@nice/ui": "workspace:^", "@tanstack/query-async-storage-persister": "^5.51.9", "@tanstack/react-query": "^5.51.21", From a3284f4532d9ac53358b90adf3ebb6ba53627575 Mon Sep 17 00:00:00 2001 From: longdayi <13477510+longdayilongdayi@user.noreply.gitee.com> Date: Thu, 27 Feb 2025 22:37:09 +0800 Subject: [PATCH 16/35] 02272209 --- .../main/layout/BasePost/FilterSection.tsx | 2 +- .../main/layout/BasePost/SearchModeRadio.tsx | 8 +++---- apps/web/src/app/main/layout/MainHeader.tsx | 2 +- .../src/app/main/layout/NavigationMenu.tsx | 4 ++-- .../components/common/editor/MindEditor.tsx | 22 +++++++++---------- 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/apps/web/src/app/main/layout/BasePost/FilterSection.tsx b/apps/web/src/app/main/layout/BasePost/FilterSection.tsx index 25575d1..9742e47 100755 --- a/apps/web/src/app/main/layout/BasePost/FilterSection.tsx +++ b/apps/web/src/app/main/layout/BasePost/FilterSection.tsx @@ -14,7 +14,7 @@ export default function FilterSection() { }); }; return ( -
+
{showSearchMode && } {taxonomies?.map((tax, index) => { const items = Object.entries(selectedTerms).find( diff --git a/apps/web/src/app/main/layout/BasePost/SearchModeRadio.tsx b/apps/web/src/app/main/layout/BasePost/SearchModeRadio.tsx index 692bfe2..507e94f 100644 --- a/apps/web/src/app/main/layout/BasePost/SearchModeRadio.tsx +++ b/apps/web/src/app/main/layout/BasePost/SearchModeRadio.tsx @@ -11,14 +11,14 @@ export default function SearchModeRadio() { return ( -

搜索模式

+

只搜索

- 课程 - 路径 - 全部 + 视频课程 + 思维导图 + 所有资源
); diff --git a/apps/web/src/app/main/layout/MainHeader.tsx b/apps/web/src/app/main/layout/MainHeader.tsx index 1735bbd..c2e383a 100755 --- a/apps/web/src/app/main/layout/MainHeader.tsx +++ b/apps/web/src/app/main/layout/MainHeader.tsx @@ -86,7 +86,7 @@ export function MainHeader() { window.location.href = "/path/editor"; }} icon={}> - 创建学习路径 + 创建思维导图 )} {isAuthenticated ? ( diff --git a/apps/web/src/app/main/layout/NavigationMenu.tsx b/apps/web/src/app/main/layout/NavigationMenu.tsx index d7aa53f..8cca859 100755 --- a/apps/web/src/app/main/layout/NavigationMenu.tsx +++ b/apps/web/src/app/main/layout/NavigationMenu.tsx @@ -11,8 +11,8 @@ export const NavigationMenu = () => { const menuItems = useMemo(() => { const baseItems = [ { key: "home", path: "/", label: "首页" }, - { key: "path", path: "/path", label: "学习路径" }, - { key: "courses", path: "/courses", label: "全部课程" }, + { key: "path", path: "/path", label: "思维导图" }, + { key: "courses", path: "/courses", label: "所有课程" }, ]; if (!isAuthenticated) { diff --git a/apps/web/src/components/common/editor/MindEditor.tsx b/apps/web/src/components/common/editor/MindEditor.tsx index fb07164..162cd60 100755 --- a/apps/web/src/components/common/editor/MindEditor.tsx +++ b/apps/web/src/components/common/editor/MindEditor.tsx @@ -65,7 +65,7 @@ export default function MindEditor({ id }: { id?: string }) { }); const canEdit: boolean = useMemo(() => { //登录了且是作者、超管、无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); }, [user]) const navigate = useNavigate(); @@ -96,8 +96,8 @@ export default function MindEditor({ id }: { id?: string }) { const mind = new MindElixir({ ...MIND_OPTIONS, el: containerRef.current, - before:{ - beginEdit(){ + before: { + beginEdit() { return canEdit } }, @@ -107,7 +107,7 @@ export default function MindEditor({ id }: { id?: string }) { nodeMenu: canEdit, // 禁用节点右键菜单 keypress: canEdit, // 禁用键盘快捷键 }); - mind.init(MindElixir.new("新学习路径")); + mind.init(MindElixir.new("新思维导图")); containerRef.current.hidden = true; setInstance(mind); }, [canEdit]); @@ -181,14 +181,14 @@ export default function MindEditor({ id }: { id?: string }) { `mind-thumb-${new Date().toString()}` ); }; - useEffect(()=>{ - containerRef.current.style.height = `${Math.floor(window.innerHeight/1.25)}px` - },[]) - + useEffect(() => { + containerRef.current.style.height = `${Math.floor(window.innerHeight / 1.25)}px` + }, []) + return ( - +
- + {canEdit && taxonomies && (
)} -
e.preventDefault()}/> +
e.preventDefault()} /> {canEdit && instance && } {isLoading && (
Date: Thu, 27 Feb 2025 22:37:15 +0800 Subject: [PATCH 17/35] addd --- apps/server/src/models/base/base.service.ts | 5 ++++- apps/server/src/models/post/post.service.ts | 3 +++ packages/common/prisma/schema.prisma | 2 ++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/apps/server/src/models/base/base.service.ts b/apps/server/src/models/base/base.service.ts index d1b8b16..ae45e06 100755 --- a/apps/server/src/models/base/base.service.ts +++ b/apps/server/src/models/base/base.service.ts @@ -8,6 +8,7 @@ import { DelegateFuncs, UpdateOrderArgs, TransactionType, + OrderByArgs, SelectArgs, } from './base.type'; import { @@ -450,9 +451,10 @@ export class BaseService< page?: number; pageSize?: number; where?: WhereArgs; + orderBy?: OrderByArgs; select?: SelectArgs; }): Promise<{ items: R['findMany']; totalPages: number }> { - const { page = 1, pageSize = 10, where, select } = args; + const { page = 1, pageSize = 10, where, select, orderBy } = args; try { // 获取总记录数 @@ -461,6 +463,7 @@ export class BaseService< const items = (await this.getModel().findMany({ where, select, + orderBy, skip: (page - 1) * pageSize, take: pageSize, } as any)) as R['findMany']; diff --git a/apps/server/src/models/post/post.service.ts b/apps/server/src/models/post/post.service.ts index 03d89de..57b2962 100755 --- a/apps/server/src/models/post/post.service.ts +++ b/apps/server/src/models/post/post.service.ts @@ -21,6 +21,7 @@ import { BaseTreeService } from '../base/base.tree.service'; import { z } from 'zod'; import { DefaultArgs } from '@prisma/client/runtime/library'; import dayjs from 'dayjs'; +import { OrderByArgs } from '../base/base.type'; @Injectable() export class PostService extends BaseTreeService { @@ -181,6 +182,7 @@ export class PostService extends BaseTreeService { page?: number; pageSize?: number; where?: Prisma.PostWhereInput; + orderBy?: OrderByArgs<(typeof db.post)['findMany']>; select?: Prisma.PostSelect; }): Promise<{ items: { @@ -197,6 +199,7 @@ export class PostService extends BaseTreeService { duration: number | null; rating: number | null; createdAt: Date; + views: number; publishedAt: Date | null; updatedAt: Date; deletedAt: Date | null; diff --git a/packages/common/prisma/schema.prisma b/packages/common/prisma/schema.prisma index 2715faf..6bcd44b 100755 --- a/packages/common/prisma/schema.prisma +++ b/packages/common/prisma/schema.prisma @@ -206,6 +206,7 @@ model Post { rating Int? @default(0) students Staff[] @relation("post_student") depts Department[] @relation("post_dept") + views Int @default(0) @map("views") // 索引 // 日期时间类型字段 createdAt DateTime @default(now()) @map("created_at") @@ -238,6 +239,7 @@ model Post { @@index([type, publishedAt]) @@index([state]) @@index([level]) + @@index([views]) @@index([important]) @@map("post") } From d2877d1a857aa017a16c5d429c87ccf1602543cf Mon Sep 17 00:00:00 2001 From: Li1304553726 <1304553726@qq.com> Date: Thu, 27 Feb 2025 22:39:31 +0800 Subject: [PATCH 18/35] add. LI --- .../src/components/common/editor/MindEditor.tsx | 16 +++++++++++++--- .../editor/context/CourseEditorContext.tsx | 3 ++- .../course/editor/form/CourseBasicForm.tsx | 2 +- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/apps/web/src/components/common/editor/MindEditor.tsx b/apps/web/src/components/common/editor/MindEditor.tsx index 88d745b..44530b1 100755 --- a/apps/web/src/components/common/editor/MindEditor.tsx +++ b/apps/web/src/components/common/editor/MindEditor.tsx @@ -19,6 +19,8 @@ import MindElixir from "mind-elixir"; import { useTusUpload } from "@web/src/hooks/useTusUpload"; import { useNavigate } from "react-router-dom"; import { useAuth } from "@web/src/providers/auth-provider"; + +// 定义 MindElixir 插件 的配置 const MIND_OPTIONS = { direction: MindElixir.SIDE, draggable: true, @@ -53,6 +55,7 @@ const MIND_OPTIONS = { }, }; export default function MindEditor({ id }: { id?: string }) { + //containerRef 容器ref instance 实例 const containerRef = useRef(null); const [instance, setInstance] = useState(null); const { isAuthenticated, user, hasSomePermissions } = useAuth(); @@ -71,14 +74,18 @@ export default function MindEditor({ id }: { id?: string }) { }, [user]) const navigate = useNavigate(); const { create, update } = usePost(); + //拿到slug数组 const { data: taxonomies } = api.taxonomy.getAll.useQuery({ type: ObjectType.COURSE, }); + const { handleFileUpload } = useTusUpload(); + //antd表单自带钩子 const [form] = Form.useForm(); + //post 信息 //instance 实例 useEffect(() => { if (post && form && instance && id) { - console.log(post); + console.log('你好',taxonomies); instance.refresh((post as any).meta); const deptIds = (post?.depts || [])?.map((dept) => dept.id); const formData = { @@ -86,8 +93,8 @@ export default function MindEditor({ id }: { id?: string }) { deptIds: deptIds, }; post.terms?.forEach((term) => { - formData[term.taxonomyId] = term.id; // 假设 taxonomyName 是您在 Form.Item 中使用的 name - }); + formData[term.taxonomyId] = term.id // 假设 taxonomyName是您在 Form.Item 中使用的name + }) form.setFieldsValue(formData); } }, [post, form, instance, id]); @@ -108,8 +115,10 @@ export default function MindEditor({ id }: { id?: string }) { nodeMenu: canEdit, // 禁用节点右键菜单 keypress: canEdit, // 禁用键盘快捷键 }); + //初始化 mind.init(MindElixir.new("新学习路径")); containerRef.current.hidden = true; + //挂载实例 setInstance(mind); }, [canEdit]); useEffect(() => { @@ -119,6 +128,7 @@ export default function MindEditor({ id }: { id?: string }) { instance.refresh((post as any)?.meta); } }, [id, post, instance]); + //保存 按钮 函数 const handleSave = async () => { if (!instance) return; const values = form.getFieldsValue(); diff --git a/apps/web/src/components/models/course/editor/context/CourseEditorContext.tsx b/apps/web/src/components/models/course/editor/context/CourseEditorContext.tsx index bc84b18..90bcd04 100755 --- a/apps/web/src/components/models/course/editor/context/CourseEditorContext.tsx +++ b/apps/web/src/components/models/course/editor/context/CourseEditorContext.tsx @@ -82,7 +82,7 @@ export function CourseFormProvider({ }, [course, form]); const onSubmit = async (values: any) => { - console.log(values); + const sections = values?.sections || []; const deptIds = values?.deptIds || []; const termIds = taxonomies @@ -149,6 +149,7 @@ export function CourseFormProvider({ } }; + return ( ({ label: value, From 1605cea0aabf48c55e12fc0e5dd00c71dabec0e6 Mon Sep 17 00:00:00 2001 From: longdayi <13477510+longdayilongdayi@user.noreply.gitee.com> Date: Thu, 27 Feb 2025 22:58:42 +0800 Subject: [PATCH 19/35] 02272258 --- apps/server/src/models/post/post.service.ts | 27 +------ apps/web/src/app/main/course/detail/page.tsx | 1 - .../app/main/home/components/HeroSection.tsx | 2 +- apps/web/src/app/main/layout/MainHeader.tsx | 70 ++++++++++--------- .../src/app/main/layout/NavigationMenu.tsx | 9 +-- .../components/MyDutyPathContainer.tsx | 30 ++++++++ apps/web/src/app/main/my-duty-path/page.tsx | 17 +++++ .../components/common/editor/MindEditor.tsx | 16 ++++- .../models/course/detail/CourseDetail.tsx | 6 +- .../course/detail/CourseDetailDisplayArea.tsx | 15 ---- .../course/detail/CourseDetailLayout.tsx | 4 -- .../course/detail/CourseDetailTitle.tsx | 2 +- .../course/detail/course-objectives.tsx | 29 -------- apps/web/src/routes/index.tsx | 8 +++ 14 files changed, 116 insertions(+), 120 deletions(-) create mode 100644 apps/web/src/app/main/my-duty-path/components/MyDutyPathContainer.tsx create mode 100755 apps/web/src/app/main/my-duty-path/page.tsx delete mode 100755 apps/web/src/components/models/course/detail/course-objectives.tsx diff --git a/apps/server/src/models/post/post.service.ts b/apps/server/src/models/post/post.service.ts index 57b2962..5e9a116 100755 --- a/apps/server/src/models/post/post.service.ts +++ b/apps/server/src/models/post/post.service.ts @@ -184,32 +184,7 @@ export class PostService extends BaseTreeService { where?: Prisma.PostWhereInput; orderBy?: OrderByArgs<(typeof db.post)['findMany']>; select?: Prisma.PostSelect; - }): Promise<{ - items: { - id: string; - type: string | null; - level: string | null; - state: string | null; - title: string | null; - subTitle: string | null; - content: string | null; - important: boolean | null; - domainId: string | null; - order: number | null; - duration: number | null; - rating: number | null; - createdAt: Date; - views: number; - publishedAt: Date | null; - updatedAt: Date; - deletedAt: Date | null; - authorId: string | null; - parentId: string | null; - hasChildren: boolean | null; - meta: Prisma.JsonValue | null; - }[]; - totalPages: number; - }> { + }) { // super.updateOrder; return super.findManyWithPagination(args); } diff --git a/apps/web/src/app/main/course/detail/page.tsx b/apps/web/src/app/main/course/detail/page.tsx index 16f7173..82dd7ae 100755 --- a/apps/web/src/app/main/course/detail/page.tsx +++ b/apps/web/src/app/main/course/detail/page.tsx @@ -3,6 +3,5 @@ import { useParams } from "react-router-dom"; export function CourseDetailPage() { const { id, lectureId } = useParams(); - console.log("Course ID:", id); return ; } diff --git a/apps/web/src/app/main/home/components/HeroSection.tsx b/apps/web/src/app/main/home/components/HeroSection.tsx index 4073109..8f6b7a8 100755 --- a/apps/web/src/app/main/home/components/HeroSection.tsx +++ b/apps/web/src/app/main/home/components/HeroSection.tsx @@ -57,7 +57,7 @@ const HeroSection = () => { { icon: , value: statistics.reads, - label: "观看次数", + label: "播放次数", }, ]; }, [statistics]); diff --git a/apps/web/src/app/main/layout/MainHeader.tsx b/apps/web/src/app/main/layout/MainHeader.tsx index c2e383a..deb6b62 100755 --- a/apps/web/src/app/main/layout/MainHeader.tsx +++ b/apps/web/src/app/main/layout/MainHeader.tsx @@ -30,44 +30,44 @@ export function MainHeader() {
- {/* 中间搜索区域 - 允许适当收缩但保持可用性 */} -
- - } - placeholder="搜索课程" - className="w-full md:w-96 rounded-full" - value={searchValue} - onClick={(e) => { - if (!window.location.pathname.startsWith("/search")) { - navigate(`/search`); - window.scrollTo({ - top: 0, - behavior: "smooth", - }); - } - }} - onChange={(e) => setSearchValue(e.target.value)} - onPressEnter={(e) => { - if (!window.location.pathname.startsWith("/search")) { - navigate(`/search`); - window.scrollTo({ - top: 0, - behavior: "smooth", - }); - } - }} - /> -
{/* 右侧区域 - 可以灵活收缩 */}
+ + } + placeholder="搜索课程" + className="w-full md:w-96 rounded-full" + value={searchValue} + onClick={(e) => { + if (!window.location.pathname.startsWith("/search")) { + navigate(`/search`); + window.scrollTo({ + top: 0, + behavior: "smooth", + }); + } + }} + onChange={(e) => setSearchValue(e.target.value)} + onPressEnter={(e) => { + if (!window.location.pathname.startsWith("/search")) { + navigate(`/search`); + window.scrollTo({ + top: 0, + behavior: "smooth", + }); + } + }} + /> {isAuthenticated && ( <> @@ -93,8 +96,11 @@ export function MainHeader() { ) : ( diff --git a/apps/web/src/app/main/layout/NavigationMenu.tsx b/apps/web/src/app/main/layout/NavigationMenu.tsx index 8cca859..3b159e2 100755 --- a/apps/web/src/app/main/layout/NavigationMenu.tsx +++ b/apps/web/src/app/main/layout/NavigationMenu.tsx @@ -11,7 +11,7 @@ export const NavigationMenu = () => { const menuItems = useMemo(() => { const baseItems = [ { key: "home", path: "/", label: "首页" }, - { key: "path", path: "/path", label: "思维导图" }, + { key: "path", path: "/path", label: "全部思维导图" }, { key: "courses", path: "/courses", label: "所有课程" }, ]; @@ -20,9 +20,10 @@ export const NavigationMenu = () => { } else { return [ ...baseItems, - { key: "my-duty", path: "/my-duty", label: "我的授课" }, - { key: "my-learning", path: "/my-learning", label: "我的课程" }, - { key: "my-path", path: "/my-path", label: "我的路径" }, + { key: "my-duty", path: "/my-duty", label: "我创建的课程" }, + { key: "my-learning", path: "/my-learning", label: "我学习的课程" }, + { key: "my-duty-path", path: "/my-duty-path", label: "我创建的思维导图" }, + { key: "my-path", path: "/my-path", label: "我学习的思维导图" }, ]; } }, [isAuthenticated]); diff --git a/apps/web/src/app/main/my-duty-path/components/MyDutyPathContainer.tsx b/apps/web/src/app/main/my-duty-path/components/MyDutyPathContainer.tsx new file mode 100644 index 0000000..148706e --- /dev/null +++ b/apps/web/src/app/main/my-duty-path/components/MyDutyPathContainer.tsx @@ -0,0 +1,30 @@ +import PostList from "@web/src/components/models/course/list/PostList"; +import { useAuth } from "@web/src/providers/auth-provider"; +import { useMainContext } from "../../layout/MainProvider"; +import { PostType } from "@nice/common"; +import PathCard from "@web/src/components/models/post/SubPost/PathCard"; + +export default function MyLearningListContainer() { + const { user } = useAuth(); + const { searchCondition, termsCondition } = useMainContext(); + return ( + <> + } + params={{ + pageSize: 12, + where: { + type: PostType.PATH, + students: { + some: { + id: user?.id, + }, + }, + ...termsCondition, + ...searchCondition, + }, + }} + cols={4}> + + ); +} diff --git a/apps/web/src/app/main/my-duty-path/page.tsx b/apps/web/src/app/main/my-duty-path/page.tsx new file mode 100755 index 0000000..24d7931 --- /dev/null +++ b/apps/web/src/app/main/my-duty-path/page.tsx @@ -0,0 +1,17 @@ +import { useEffect } from "react"; +import BasePostLayout from "../layout/BasePost/BasePostLayout"; +import { useMainContext } from "../layout/MainProvider"; +import { PostType } from "@nice/common"; +import MyDutyPathContainer from "./components/MyDutyPathContainer"; + +export default function MyDutyPathPage() { + const { setSearchMode } = useMainContext(); + useEffect(() => { + setSearchMode(PostType.PATH); + }, [setSearchMode]); + return ( + + + + ); +} diff --git a/apps/web/src/components/common/editor/MindEditor.tsx b/apps/web/src/components/common/editor/MindEditor.tsx index 432e3cd..5693436 100755 --- a/apps/web/src/components/common/editor/MindEditor.tsx +++ b/apps/web/src/components/common/editor/MindEditor.tsx @@ -1,6 +1,6 @@ import { Button, Card, Empty, Form, Space, Spin, message, theme } from "antd"; import NodeMenu from "./NodeMenu"; -import { api, usePost } from "@nice/client"; +import { api, usePost, useVisitor } from "@nice/client"; import { ObjectType, PathDto, @@ -8,6 +8,7 @@ import { PostType, Prisma, RolePerms, + VisitType, } from "@nice/common"; import TermSelect from "../../models/term/term-select"; import DepartmentSelect from "../../models/department/department-select"; @@ -23,6 +24,7 @@ export default function MindEditor({ id }: { id?: string }) { const containerRef = useRef(null); const [instance, setInstance] = useState(null); const { isAuthenticated, user, hasSomePermissions } = useAuth(); + const { read } = useVisitor() const { data: post, isLoading }: { data: PathDto; isLoading: boolean } = api.post.findFirst.useQuery({ where: { @@ -42,9 +44,19 @@ export default function MindEditor({ id }: { id?: string }) { }); const { handleFileUpload } = useTusUpload(); const [form] = Form.useForm(); + useEffect(() => { + if (post?.id) { + read.mutateAsync({ + data: { + visitorId: user?.id || null, + postId: post?.id, + type: VisitType.READED, + }, + }); + } + }, [post]); useEffect(() => { if (post && form && instance && id) { - console.log(post); instance.refresh((post as any).meta); const deptIds = (post?.depts || [])?.map((dept) => dept.id); const formData = { diff --git a/apps/web/src/components/models/course/detail/CourseDetail.tsx b/apps/web/src/components/models/course/detail/CourseDetail.tsx index 353ec4f..c33c268 100755 --- a/apps/web/src/components/models/course/detail/CourseDetail.tsx +++ b/apps/web/src/components/models/course/detail/CourseDetail.tsx @@ -8,11 +8,7 @@ export default function CourseDetail({ id?: string; lectureId?: string; }) { - const iframeStyle = { - width: "50%", - height: "100vh", - border: "none", - }; + return ( <> diff --git a/apps/web/src/components/models/course/detail/CourseDetailDisplayArea.tsx b/apps/web/src/components/models/course/detail/CourseDetailDisplayArea.tsx index cfcea17..7d89f1a 100755 --- a/apps/web/src/components/models/course/detail/CourseDetailDisplayArea.tsx +++ b/apps/web/src/components/models/course/detail/CourseDetailDisplayArea.tsx @@ -7,25 +7,10 @@ import { Course, LectureType, PostType } from "@nice/common"; import { CourseDetailContext } from "./CourseDetailContext"; import CollapsibleContent from "@web/src/components/common/container/CollapsibleContent"; import { Skeleton } from "antd"; -import { CoursePreview } from "./CoursePreview/CoursePreview"; import ResourcesShower from "@web/src/components/common/uploader/ResourceShower"; -import { - BookOutlined, - CalendarOutlined, - EditTwoTone, - EyeOutlined, - ReloadOutlined, -} from "@ant-design/icons"; -import dayjs from "dayjs"; import { useNavigate } from "react-router-dom"; import CourseDetailTitle from "./CourseDetailTitle"; -// interface CourseDetailDisplayAreaProps { -// // course: Course; -// // videoSrc?: string; -// // videoPoster?: string; -// // isLoading?: boolean; -// } export const CourseDetailDisplayArea: React.FC = () => { // 创建滚动动画效果 diff --git a/apps/web/src/components/models/course/detail/CourseDetailLayout.tsx b/apps/web/src/components/models/course/detail/CourseDetailLayout.tsx index 56831e4..4ac0d73 100755 --- a/apps/web/src/components/models/course/detail/CourseDetailLayout.tsx +++ b/apps/web/src/components/models/course/detail/CourseDetailLayout.tsx @@ -19,11 +19,7 @@ export default function CourseDetailLayout() { const [isSyllabusOpen, setIsSyllabusOpen] = useState(true); return (
- {/* */} - {/* 添加 Header 组件 */} - {/* 主内容区域 */} - {/* 为了防止 Header 覆盖内容,添加上边距 */}
{" "} {/* 添加这个包装 div */} diff --git a/apps/web/src/components/models/course/detail/CourseDetailTitle.tsx b/apps/web/src/components/models/course/detail/CourseDetailTitle.tsx index cc984e1..90228ad 100755 --- a/apps/web/src/components/models/course/detail/CourseDetailTitle.tsx +++ b/apps/web/src/components/models/course/detail/CourseDetailTitle.tsx @@ -44,7 +44,7 @@ export default function CourseDetailTitle() {
-
{`观看次数${course?.meta?.views || 0}`}
+
{`播放次数${course?.meta?.views || 0}`}
diff --git a/apps/web/src/components/models/course/detail/course-objectives.tsx b/apps/web/src/components/models/course/detail/course-objectives.tsx deleted file mode 100755 index b849eac..0000000 --- a/apps/web/src/components/models/course/detail/course-objectives.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import { CheckOutlined } from '@ant-design/icons'; -import React from 'react'; -interface CourseObjectivesProps { - objectives: string[]; - title?: string; -} -const CourseObjectives: React.FC = ({ - objectives, - title = "您将会学到" -}) => { - return ( -
-

{title}

-
- {objectives.map((objective, index) => ( -
- - {objective} -
- ))} -
-
- ); -}; - -export default CourseObjectives; \ No newline at end of file diff --git a/apps/web/src/routes/index.tsx b/apps/web/src/routes/index.tsx index 0899fd2..aaab203 100755 --- a/apps/web/src/routes/index.tsx +++ b/apps/web/src/routes/index.tsx @@ -86,6 +86,14 @@ export const routes: CustomRouteObject[] = [ ), }, + { + path: "my-duty-path", + element: ( + + + + ), + }, { path: "my-duty", element: ( From 52fa9a031a8efbbcde443d3232a0fa79acb05d8b Mon Sep 17 00:00:00 2001 From: longdayi <13477510+longdayilongdayi@user.noreply.gitee.com> Date: Thu, 27 Feb 2025 23:05:12 +0800 Subject: [PATCH 20/35] 02272305 --- apps/web/src/components/common/editor/MindEditor.tsx | 10 +++++++--- apps/web/src/routes/index.tsx | 4 +++- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/apps/web/src/components/common/editor/MindEditor.tsx b/apps/web/src/components/common/editor/MindEditor.tsx index d49a126..466026f 100755 --- a/apps/web/src/components/common/editor/MindEditor.tsx +++ b/apps/web/src/components/common/editor/MindEditor.tsx @@ -20,6 +20,7 @@ import { useTusUpload } from "@web/src/hooks/useTusUpload"; import { useNavigate } from "react-router-dom"; import { useAuth } from "@web/src/providers/auth-provider"; import { MIND_OPTIONS } from "./constant"; +import { SaveOutlined } from "@ant-design/icons"; export default function MindEditor({ id }: { id?: string }) { //containerRef 容器ref instance 实例 const containerRef = useRef(null); @@ -170,7 +171,7 @@ export default function MindEditor({ id }: { id?: string }) { return (
- {canEdit && taxonomies && ( + {taxonomies && (
@@ -181,6 +182,7 @@ export default function MindEditor({ id }: { id?: string }) { // rules={[{ required: true }]} noStyle>
- + }
)} diff --git a/apps/web/src/routes/index.tsx b/apps/web/src/routes/index.tsx index aaab203..e04740b 100755 --- a/apps/web/src/routes/index.tsx +++ b/apps/web/src/routes/index.tsx @@ -70,7 +70,9 @@ export const routes: CustomRouteObject[] = [ }, { path: "editor/:id?", - element: , + element: + + , }, ], }, From 7aea9426463006eccf990898f07b51aeaa0568dd Mon Sep 17 00:00:00 2001 From: Rao <1227431568@qq.com> Date: Thu, 27 Feb 2025 23:06:17 +0800 Subject: [PATCH 21/35] rht --- .../src/app/main/home/components/CoursesSection.tsx | 11 +++++++---- apps/web/src/app/main/home/page.tsx | 12 ++++++++++++ apps/web/src/components/common/editor/MindEditor.tsx | 2 +- apps/web/src/routes/index.tsx | 4 +++- 4 files changed, 23 insertions(+), 6 deletions(-) diff --git a/apps/web/src/app/main/home/components/CoursesSection.tsx b/apps/web/src/app/main/home/components/CoursesSection.tsx index 3280945..40814f4 100755 --- a/apps/web/src/app/main/home/components/CoursesSection.tsx +++ b/apps/web/src/app/main/home/components/CoursesSection.tsx @@ -1,12 +1,10 @@ -import React, { useState, useMemo } from "react"; +import React, { useState, useMemo, ReactNode } from "react"; import { Typography, Skeleton } from "antd"; import { TaxonomySlug, TermDto } from "@nice/common"; import { api } from "@nice/client"; import { CoursesSectionTag } from "./CoursesSectionTag"; import LookForMore from "./LookForMore"; import PostList from "@web/src/components/models/course/list/PostList"; -import PostCard from "@web/src/components/models/post/PostCard"; -import CourseCard from "@web/src/components/models/post/SubPost/CourseCard"; interface GetTaxonomyProps { categories: string[]; isLoading: boolean; @@ -35,11 +33,15 @@ interface CoursesSectionProps { title: string; description: string; initialVisibleCoursesCount?: number; + postType:string; + render?:(post)=>ReactNode } const CoursesSection: React.FC = ({ title, description, initialVisibleCoursesCount = 8, + postType, + render }) => { const [selectedCategory, setSelectedCategory] = useState("全部"); const gateGory: GetTaxonomyProps = useGetTaxonomy({ @@ -83,7 +85,7 @@ const CoursesSection: React.FC = ({ )}
} + renderItem={(post) => render(post)} params={{ page: 1, pageSize: initialVisibleCoursesCount, @@ -95,6 +97,7 @@ const CoursesSection: React.FC = ({ }, } : {}, + type: postType }, }} showPagination={false} diff --git a/apps/web/src/app/main/home/page.tsx b/apps/web/src/app/main/home/page.tsx index 0cf5123..c7db111 100755 --- a/apps/web/src/app/main/home/page.tsx +++ b/apps/web/src/app/main/home/page.tsx @@ -1,6 +1,9 @@ import HeroSection from "./components/HeroSection"; import CategorySection from "./components/CategorySection"; import CoursesSection from "./components/CoursesSection"; +import { PostType } from "@nice/common"; +import PathCard from "@web/src/components/models/post/SubPost/PathCard"; +import CourseCard from "@web/src/components/models/post/SubPost/CourseCard"; const HomePage = () => { @@ -8,10 +11,19 @@ const HomePage = () => { return (
+ } + /> } /> +
); diff --git a/apps/web/src/components/common/editor/MindEditor.tsx b/apps/web/src/components/common/editor/MindEditor.tsx index fb07164..087830d 100755 --- a/apps/web/src/components/common/editor/MindEditor.tsx +++ b/apps/web/src/components/common/editor/MindEditor.tsx @@ -187,7 +187,7 @@ export default function MindEditor({ id }: { id?: string }) { return ( -
+
{canEdit && taxonomies && (
, + element: + + , }, ], }, From 30032616163ec7d834c50e6b42f56690ff8297da Mon Sep 17 00:00:00 2001 From: longdayi <13477510+longdayilongdayi@user.noreply.gitee.com> Date: Thu, 27 Feb 2025 23:07:57 +0800 Subject: [PATCH 22/35] 02272307 --- .../components/common/editor/MindEditor.tsx | 2 +- .../src/components/common/editor/NodeMenu.tsx | 5 - apps/web/src/components/common/editor/i18n.ts | 152 ------------------ 3 files changed, 1 insertion(+), 158 deletions(-) delete mode 100755 apps/web/src/components/common/editor/i18n.ts diff --git a/apps/web/src/components/common/editor/MindEditor.tsx b/apps/web/src/components/common/editor/MindEditor.tsx index 466026f..c000d38 100755 --- a/apps/web/src/components/common/editor/MindEditor.tsx +++ b/apps/web/src/components/common/editor/MindEditor.tsx @@ -1,4 +1,4 @@ -import { Button, Card, Empty, Form, Space, Spin, message, theme } from "antd"; +import { Button, Empty, Form, Spin } from "antd"; import NodeMenu from "./NodeMenu"; import { api, usePost, useVisitor } from "@nice/client"; import { diff --git a/apps/web/src/components/common/editor/NodeMenu.tsx b/apps/web/src/components/common/editor/NodeMenu.tsx index a73603f..f762967 100755 --- a/apps/web/src/components/common/editor/NodeMenu.tsx +++ b/apps/web/src/components/common/editor/NodeMenu.tsx @@ -35,23 +35,18 @@ const NodeMenu: React.FC = ({ mind }) => { useEffect(() => { const handleSelectNode = (nodeObj: NodeObj) => { setIsOpen(true); - const style = nodeObj.style || {}; setSelectedFontColor(style.color || ''); setSelectedBgColor(style.background || ''); - setSelectedSize(style.fontSize || '24'); setIsBold(style.fontWeight === 'bold'); setUrl(nodeObj.hyperLink || ''); }; - const handleUnselectNode = () => { setIsOpen(false); }; - mind.bus.addListener('selectNode', handleSelectNode); mind.bus.addListener('unselectNode', handleUnselectNode); - }, [mind]); useEffect(() => { diff --git a/apps/web/src/components/common/editor/i18n.ts b/apps/web/src/components/common/editor/i18n.ts deleted file mode 100755 index ad60d5f..0000000 --- a/apps/web/src/components/common/editor/i18n.ts +++ /dev/null @@ -1,152 +0,0 @@ -interface I18n { - addChild: string - addParent: string - addSibling: string - removeNode: string - focus: string - cancelFocus: string - moveUp: string - moveDown: string - link: string - clickTips: string - font: string - background: string - tag: string - icon: string - tagsSeparate: string - iconsSeparate: string - url: string - memo?: string -} - -const cn: I18n = { - addChild: '插入子节点', - addParent: '插入父节点', - addSibling: '插入同级节点', - removeNode: '删除节点', - focus: '专注', - cancelFocus: '取消专注', - moveUp: '上移', - moveDown: '下移', - link: '连接', - clickTips: '请点击目标节点', - font: '文字', - background: '背景', - tag: '标签', - icon: '图标', - tagsSeparate: '多个标签半角逗号分隔', - iconsSeparate: '多个图标半角逗号分隔', - url: 'URL', -} - -interface I18nCollection { - cn: I18n - zh_CN: I18n - zh_TW: I18n - en: I18n - ru: I18n - ja: I18n - pt: I18n -} - -const i18n: I18nCollection = { - cn, - zh_CN: cn, - zh_TW: { - addChild: '插入子節點', - addParent: '插入父節點', - addSibling: '插入同級節點', - removeNode: '刪除節點', - focus: '專注', - cancelFocus: '取消專注', - moveUp: '上移', - moveDown: '下移', - link: '連接', - clickTips: '請點擊目標節點', - font: '文字', - background: '背景', - tag: '標簽', - icon: '圖標', - tagsSeparate: '多個標簽半角逗號分隔', - iconsSeparate: '多個圖標半角逗號分隔', - url: 'URL', - }, - en: { - addChild: 'Add child', - addParent: 'Add parent', - addSibling: 'Add sibling', - removeNode: 'Remove node', - focus: 'Focus Mode', - cancelFocus: 'Cancel Focus Mode', - moveUp: 'Move up', - moveDown: 'Move down', - link: 'Link', - clickTips: 'Please click the target node', - font: 'Font', - background: 'Background', - tag: 'Tag', - icon: 'Icon', - tagsSeparate: 'Separate tags by comma', - iconsSeparate: 'Separate icons by comma', - url: 'URL', - }, - ru: { - addChild: 'Добавить дочерний элемент', - addParent: 'Добавить родительский элемент', - addSibling: 'Добавить на этом уровне', - removeNode: 'Удалить узел', - focus: 'Режим фокусировки', - cancelFocus: 'Отменить режим фокусировки', - moveUp: 'Поднять выше', - moveDown: 'Опустить ниже', - link: 'Ссылка', - clickTips: 'Пожалуйста, нажмите на целевой узел', - font: 'Цвет шрифта', - background: 'Цвет фона', - tag: 'Тег', - icon: 'Иконка', - tagsSeparate: 'Разделяйте теги запятой', - iconsSeparate: 'Разделяйте иконки запятой', - url: 'URL', - }, - ja: { - addChild: '子ノードを追加する', - addParent: '親ノードを追加します', - addSibling: '兄弟ノードを追加する', - removeNode: 'ノードを削除', - focus: '集中', - cancelFocus: '集中解除', - moveUp: '上へ移動', - moveDown: '下へ移動', - link: 'コネクト', - clickTips: 'ターゲットノードをクリックしてください', - font: 'フォント', - background: 'バックグラウンド', - tag: 'タグ', - icon: 'アイコン', - tagsSeparate: '複数タグはカンマ区切り', - iconsSeparate: '複数アイコンはカンマ区切り', - url: 'URL', - }, - pt: { - addChild: 'Adicionar item filho', - addParent: 'Adicionar item pai', - addSibling: 'Adicionar item irmao', - removeNode: 'Remover item', - focus: 'Modo Foco', - cancelFocus: 'Cancelar Modo Foco', - moveUp: 'Mover para cima', - moveDown: 'Mover para baixo', - link: 'Link', - clickTips: 'Favor clicar no item alvo', - font: 'Fonte', - background: 'Cor de fundo', - tag: 'Tag', - icon: 'Icone', - tagsSeparate: 'Separe tags por virgula', - iconsSeparate: 'Separe icones por virgula', - url: 'URL', - }, -} - -export default i18n \ No newline at end of file From f1d4733d2a4bed7ec3e11dc4dd687b638336f316 Mon Sep 17 00:00:00 2001 From: Rao <1227431568@qq.com> Date: Thu, 27 Feb 2025 23:11:37 +0800 Subject: [PATCH 23/35] rht --- apps/web/src/app/main/home/components/CoursesSection.tsx | 8 +++++--- apps/web/src/app/main/home/components/LookForMore.tsx | 5 ++++- apps/web/src/app/main/home/page.tsx | 2 ++ 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/apps/web/src/app/main/home/components/CoursesSection.tsx b/apps/web/src/app/main/home/components/CoursesSection.tsx index 40814f4..cc68bd0 100755 --- a/apps/web/src/app/main/home/components/CoursesSection.tsx +++ b/apps/web/src/app/main/home/components/CoursesSection.tsx @@ -34,14 +34,16 @@ interface CoursesSectionProps { description: string; initialVisibleCoursesCount?: number; postType:string; - render?:(post)=>ReactNode + render?:(post)=>ReactNode; + to:string } const CoursesSection: React.FC = ({ title, description, initialVisibleCoursesCount = 8, postType, - render + render, + to }) => { const [selectedCategory, setSelectedCategory] = useState("全部"); const gateGory: GetTaxonomyProps = useGetTaxonomy({ @@ -102,7 +104,7 @@ const CoursesSection: React.FC = ({ }} showPagination={false} cols={4}> - +
); diff --git a/apps/web/src/app/main/home/components/LookForMore.tsx b/apps/web/src/app/main/home/components/LookForMore.tsx index 88e1b41..2bd74cc 100755 --- a/apps/web/src/app/main/home/components/LookForMore.tsx +++ b/apps/web/src/app/main/home/components/LookForMore.tsx @@ -11,7 +11,10 @@ export default function LookForMore({to}:{to:string}) {
@@ -87,7 +88,8 @@ export function MainHeader() { onClick={() => { window.location.href = "/path/editor"; }} - ghost type="primary" + ghost + type="primary" icon={}> 创建思维导图 @@ -100,7 +102,6 @@ export function MainHeader() { size="large" shape="round" onClick={() => navigate("/login")} - icon={}> 登录 @@ -110,4 +111,3 @@ export function MainHeader() {
); } - diff --git a/apps/web/src/components/models/course/detail/CourseDetailContext.tsx b/apps/web/src/components/models/course/detail/CourseDetailContext.tsx index bb43156..57569f0 100755 --- a/apps/web/src/components/models/course/detail/CourseDetailContext.tsx +++ b/apps/web/src/components/models/course/detail/CourseDetailContext.tsx @@ -79,17 +79,28 @@ export function CourseDetailProvider({ ); useEffect(() => { - if (lecture?.id) { + if (lectureId) { console.log(123); + console.log(lectureId); read.mutateAsync({ data: { visitorId: user?.id || null, - postId: lecture?.id, + postId: lectureId, + type: VisitType.READED, + }, + }); + } else { + console.log(321); + console.log(editId); + read.mutateAsync({ + data: { + visitorId: user?.id || null, + postId: editId, type: VisitType.READED, }, }); } - }, [course]); + }, [editId, lectureId]); useEffect(() => { if (lectureId !== selectedLectureId) { navigate(`/course/${editId}/detail/${selectedLectureId}`); diff --git a/apps/web/src/components/models/course/detail/CourseDetailTitle.tsx b/apps/web/src/components/models/course/detail/CourseDetailTitle.tsx index 940981b..de577eb 100755 --- a/apps/web/src/components/models/course/detail/CourseDetailTitle.tsx +++ b/apps/web/src/components/models/course/detail/CourseDetailTitle.tsx @@ -54,7 +54,9 @@ export default function CourseDetailTitle() {
{`观看次数${ - !selectedLectureId ? course?.views : lecture?.views || 0 + !selectedLectureId + ? course?.views || 0 + : lecture?.views || 0 }`}
diff --git a/packages/common/prisma/schema.prisma b/packages/common/prisma/schema.prisma index 7f5138e..ed02c77 100755 --- a/packages/common/prisma/schema.prisma +++ b/packages/common/prisma/schema.prisma @@ -286,8 +286,8 @@ model Visit { views Int @default(1) @map("views") // sourceIP String? @map("source_ip") // 关联关系 - visitorId String @map("visitor_id") - visitor Staff @relation(fields: [visitorId], references: [id]) + visitorId String? @map("visitor_id") + visitor Staff? @relation(fields: [visitorId], references: [id]) postId String? @map("post_id") post Post? @relation(fields: [postId], references: [id]) message Message? @relation(fields: [messageId], references: [id]) From d8e9b5d21609191175de3b8718d097f1d5f94883 Mon Sep 17 00:00:00 2001 From: Rao <1227431568@qq.com> Date: Fri, 28 Feb 2025 10:45:13 +0800 Subject: [PATCH 28/35] rht --- .../app/main/home/components/CategorySection.tsx | 1 + .../src/app/main/layout/BasePost/FilterSection.tsx | 1 + .../models/course/detail/CourseDetailContext.tsx | 13 ++++++++++--- .../models/course/detail/JoinLearingButton.tsx | 9 ++++++++- .../components/models/term/term-parent-selector.tsx | 2 +- 5 files changed, 21 insertions(+), 5 deletions(-) diff --git a/apps/web/src/app/main/home/components/CategorySection.tsx b/apps/web/src/app/main/home/components/CategorySection.tsx index 4c8141e..7d23030 100755 --- a/apps/web/src/app/main/home/components/CategorySection.tsx +++ b/apps/web/src/app/main/home/components/CategorySection.tsx @@ -35,6 +35,7 @@ const CategorySection = () => { const handleMouseClick = useCallback((categoryId: string) => { setSelectedTerms({ + ...selectedTerms, [TaxonomySlug.CATEGORY]: [categoryId], }); navigate("/courses"); diff --git a/apps/web/src/app/main/layout/BasePost/FilterSection.tsx b/apps/web/src/app/main/layout/BasePost/FilterSection.tsx index 9742e47..d7eccd4 100755 --- a/apps/web/src/app/main/layout/BasePost/FilterSection.tsx +++ b/apps/web/src/app/main/layout/BasePost/FilterSection.tsx @@ -24,6 +24,7 @@ export default function FilterSection() {

{tax?.name} + {/* {JSON.stringify(items)} */}

void; // 新增 canEdit?: boolean; userIsLearning?: boolean; + setUserIsLearning:(learning: boolean) => void; } interface CourseFormProviderProps { @@ -56,9 +57,14 @@ export function CourseDetailProvider({ { enabled: Boolean(editId) } ); - const userIsLearning = useMemo(() => { - return (course?.studentIds || []).includes(user?.id); - }, [user, course, isLoading]); + // const userIsLearning = useMemo(() => { + // return (course?.studentIds || []).includes(user?.id); + // }, [user, course, isLoading]); + const [userIsLearning, setUserIsLearning] = useState(false); + useEffect(()=>{ + console.log(course?.studentIds,user?.id) + setUserIsLearning((course?.studentIds || []).includes(user?.id)); + },[user, course, isLoading]) const canEdit = useMemo(() => { const isAuthor = isAuthenticated && user?.id === course?.authorId; const isRoot = hasSomePermissions(RolePerms?.MANAGE_ANY_POST); @@ -110,6 +116,7 @@ export function CourseDetailProvider({ setIsHeaderVisible, canEdit, userIsLearning, + setUserIsLearning }}> {children} diff --git a/apps/web/src/components/models/course/detail/JoinLearingButton.tsx b/apps/web/src/components/models/course/detail/JoinLearingButton.tsx index 68c4243..942a7de 100644 --- a/apps/web/src/components/models/course/detail/JoinLearingButton.tsx +++ b/apps/web/src/components/models/course/detail/JoinLearingButton.tsx @@ -12,15 +12,17 @@ import { EditTwoTone, LoginOutlined, } from "@ant-design/icons"; +import toast from "react-hot-toast"; export default function CourseOperationBtns() { const { id } = useParams(); const { isAuthenticated, user, hasSomePermissions, hasEveryPermissions } = useAuth(); const navigate = useNavigate(); - const { course, canEdit, userIsLearning } = useContext(CourseDetailContext); + const { course, canEdit, userIsLearning, setUserIsLearning} = useContext(CourseDetailContext); const { update } = useStaff(); const [isHovered, setIsHovered] = useState(false); + const toggleLearning = async () => { if (!userIsLearning) { await update.mutateAsync({ @@ -31,7 +33,10 @@ export default function CourseOperationBtns() { }, }, }); + setUserIsLearning(true) + toast.success("加入学习成功"); } else { + await update.mutateAsync({ where: { id: user?.id }, data: { @@ -42,6 +47,8 @@ export default function CourseOperationBtns() { }, }, }); + toast.success("退出学习成功"); + setUserIsLearning(false) } }; return ( diff --git a/apps/web/src/components/models/term/term-parent-selector.tsx b/apps/web/src/components/models/term/term-parent-selector.tsx index b604390..2b479b6 100755 --- a/apps/web/src/components/models/term/term-parent-selector.tsx +++ b/apps/web/src/components/models/term/term-parent-selector.tsx @@ -37,7 +37,7 @@ export default function TermParentSelector({ {data?.map((category) => (
- + {category.name}
From e796b31024aa6d046d0fbb4128f7efbccdb48d36 Mon Sep 17 00:00:00 2001 From: Li1304553726 <1304553726@qq.com> Date: Fri, 28 Feb 2025 11:45:17 +0800 Subject: [PATCH 29/35] add . --- apps/web/src/app/main/path/components/TermInfo.tsx | 6 +++--- apps/web/src/components/common/uploader/ResourceShower.tsx | 6 +++--- .../models/course/detail/CourseDetailDescription.tsx | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/web/src/app/main/path/components/TermInfo.tsx b/apps/web/src/app/main/path/components/TermInfo.tsx index 718096f..1b592e4 100644 --- a/apps/web/src/app/main/path/components/TermInfo.tsx +++ b/apps/web/src/app/main/path/components/TermInfo.tsx @@ -5,17 +5,17 @@ const TermInfo = ({ post }: { post: PostDto }) => { return (
{post?.terms && post?.terms?.length > 0 ? ( -
+
{post?.terms?.map((term: any) => { return ( -
- +
+ {resource.url .split(".") .pop() ?.slice(0, 4) .toUpperCase()} - + {resource.meta.size && formatFileSize( resource.meta.size diff --git a/apps/web/src/components/models/course/detail/CourseDetailDescription.tsx b/apps/web/src/components/models/course/detail/CourseDetailDescription.tsx index bbbd495..d03eee3 100755 --- a/apps/web/src/components/models/course/detail/CourseDetailDescription.tsx +++ b/apps/web/src/components/models/course/detail/CourseDetailDescription.tsx @@ -1,5 +1,5 @@ import { Course, TaxonomySlug } from "@nice/common"; -import React, { useContext, useMemo } from "react"; +import React, { useContext, useEffect, useMemo } from "react"; import { Image, Typography, Skeleton, Tag } from "antd"; // 引入 antd 组件 import { CourseDetailContext } from "./CourseDetailContext"; import { useNavigate, useParams } from "react-router-dom"; @@ -69,7 +69,7 @@ export const CourseDetailDescription: React.FC = () => {
{"课程简介:"}
- {course?.subTitle &&
{course?.subTitle}
} + {course?.subTitle &&
{course?.subTitle}
}
From 83c9b80b20997fa9275accd8729d65c32fd0d4a1 Mon Sep 17 00:00:00 2001 From: ditiqi Date: Fri, 28 Feb 2025 12:55:53 +0800 Subject: [PATCH 30/35] add --- .../main/layout/BasePost/FilterSection.tsx | 2 +- .../src/app/main/path/components/TermInfo.tsx | 8 +- .../course/detail/CourseDetailDescription.tsx | 2 +- .../src/components/models/post/PostCard.tsx | 3 +- .../models/term/term-parent-selector.tsx | 100 ++++++++++-------- 5 files changed, 60 insertions(+), 55 deletions(-) diff --git a/apps/web/src/app/main/layout/BasePost/FilterSection.tsx b/apps/web/src/app/main/layout/BasePost/FilterSection.tsx index d7eccd4..084878c 100755 --- a/apps/web/src/app/main/layout/BasePost/FilterSection.tsx +++ b/apps/web/src/app/main/layout/BasePost/FilterSection.tsx @@ -28,7 +28,7 @@ export default function FilterSection() { handleTermChange( diff --git a/apps/web/src/app/main/path/components/TermInfo.tsx b/apps/web/src/app/main/path/components/TermInfo.tsx index 718096f..8f7e7ac 100644 --- a/apps/web/src/app/main/path/components/TermInfo.tsx +++ b/apps/web/src/app/main/path/components/TermInfo.tsx @@ -1,12 +1,12 @@ import { Tag } from "antd"; -import { PostDto, TaxonomySlug } from "@nice/common"; +import { PostDto, TaxonomySlug, TermDto } from "@nice/common"; -const TermInfo = ({ post }: { post: PostDto }) => { +const TermInfo = ({ terms = [] }: { terms?: TermDto[] }) => { return (
- {post?.terms && post?.terms?.length > 0 ? ( + {terms && terms?.length > 0 ? (
- {post?.terms?.map((term: any) => { + {terms?.map((term: any) => { return ( {
{course?.subTitle &&
{course?.subTitle}
} - +
- +
); } - diff --git a/apps/web/src/components/models/term/term-parent-selector.tsx b/apps/web/src/components/models/term/term-parent-selector.tsx index 2b479b6..58c1765 100755 --- a/apps/web/src/components/models/term/term-parent-selector.tsx +++ b/apps/web/src/components/models/term/term-parent-selector.tsx @@ -1,50 +1,56 @@ import { api } from "@nice/client/"; -import { Checkbox, Form } from "antd"; +import { Checkbox, Skeleton } from "antd"; import { TermDto } from "@nice/common"; -import { useCallback, useEffect, useState } from "react"; +import React from "react"; export default function TermParentSelector({ - value, - onChange, - className, - placeholder = "选择分类", - multiple = true, - taxonomyId, - domainId, - style, -}: any) { - const [selectedValues, setSelectedValues] = useState<string[]>([]); // 用于存储选中的值 - const { - data, - isLoading, - }: { data: TermDto[]; isLoading: boolean } = api.term.findMany.useQuery({ - where: { - taxonomy: { - id: taxonomyId, - }, - parentId: null - }, - }); - const handleCheckboxChange = (checkedValues: string[]) => { - setSelectedValues(checkedValues); // 更新选中的值 - if (onChange) { - onChange(checkedValues); // 调用外部传入的 onChange 回调 - } - }; - return ( - <div className={className} style={style}> - <Form onFinish={null}> - <Form.Item name="categories"> - <Checkbox.Group onChange={handleCheckboxChange}> - {data?.map((category) => ( - <div className="w-full h-9 p-2 my-1"> - <Checkbox checked={value?.includes(category.id)} className="text-base text-slate-700" key={category.id} value={category.id}> - {category.name} - </Checkbox> - </div> - ))} - </Checkbox.Group> - </Form.Item> - </Form> - </div> - ) -} \ No newline at end of file + value, + onChange, + className, + taxonomyId, + domainId = undefined, + style, +}: { + value?: string[]; + onChange?: (value: string[]) => void; + className?: string; + taxonomyId: string; + domainId?: string; + style?: React.CSSProperties; +}) { + const { data, isLoading }: { data: TermDto[]; isLoading: boolean } = + api.term.findMany.useQuery({ + where: { + taxonomyId: taxonomyId, + parentId: null, + domainId, + }, + }); + const handleCheckboxChange = (checkedValues: string[]) => { + // setSelectedValues(checkedValues); // 更新选中的值 + if (onChange) { + onChange(checkedValues); // 调用外部传入的 onChange 回调 + } + }; + return ( + <div className={className} style={style}> + {isLoading ? ( + <Skeleton + paragraph={{ + rows: 4, + }}></Skeleton> + ) : ( + <Checkbox.Group value={value} onChange={handleCheckboxChange}> + {data?.map((category) => ( + <div className="w-full h-9 p-2 my-1" key={category.id}> + <Checkbox + className="text-base text-slate-700" + value={category.id}> + {category.name} + </Checkbox> + </div> + ))} + </Checkbox.Group> + )} + </div> + ); +} From 3c9609c0a454dccef288e26e640c3780a4041200 Mon Sep 17 00:00:00 2001 From: wfc <2146706290@qq.com> Date: Fri, 28 Feb 2025 15:09:56 +0800 Subject: [PATCH 31/35] add --- apps/web/src/app/main/layout/NavigationMenu.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/web/src/app/main/layout/NavigationMenu.tsx b/apps/web/src/app/main/layout/NavigationMenu.tsx index d7aa53f..971dfe3 100755 --- a/apps/web/src/app/main/layout/NavigationMenu.tsx +++ b/apps/web/src/app/main/layout/NavigationMenu.tsx @@ -27,8 +27,11 @@ export const NavigationMenu = () => { } }, [isAuthenticated]); - const selectedKey = - menuItems.find((item) => item.path === pathname)?.key || ""; + const selectedKey = useMemo(() => { + const normalizePath = (path: string): string => path.replace(/\/$/, ""); + return pathname === '/' ? "home" : menuItems.find((item) => normalizePath(pathname) === item.path)?.key || ""; + }, [pathname]); + return ( <Menu mode="horizontal" From 9e348fcfc3f608a572d5e50e3e3001262b502339 Mon Sep 17 00:00:00 2001 From: ditiqi <ditiqi@163.com> Date: Fri, 28 Feb 2025 15:17:56 +0800 Subject: [PATCH 32/35] add --- apps/server/src/queue/models/post/utils.ts | 4 - .../components/common/editor/MindEditor.tsx | 79 +++++++++---------- .../models/post/PostSelect/PostSelect.tsx | 15 ++++ 3 files changed, 53 insertions(+), 45 deletions(-) create mode 100644 apps/web/src/components/models/post/PostSelect/PostSelect.tsx diff --git a/apps/server/src/queue/models/post/utils.ts b/apps/server/src/queue/models/post/utils.ts index 57129a3..7e3c3f1 100755 --- a/apps/server/src/queue/models/post/utils.ts +++ b/apps/server/src/queue/models/post/utils.ts @@ -66,8 +66,6 @@ export async function updatePostViewCount(id: string, type: VisitType) { where: { id }, select: { id: true, meta: true, type: true }, }); - console.log(post?.type); - console.log('updatePostViewCount'); const metaFieldMap = { [VisitType.READED]: 'views', [VisitType.LIKE]: 'likes', @@ -105,7 +103,6 @@ export async function updatePostViewCount(id: string, type: VisitType) { type: type, }, }); - console.log(courseViews); await db.post.update({ where: { id: course.id }, data: { @@ -126,7 +123,6 @@ export async function updatePostViewCount(id: string, type: VisitType) { type: type, }, }); - console.log('totalViews', totalViews); await db.post.update({ where: { id }, data: { diff --git a/apps/web/src/components/common/editor/MindEditor.tsx b/apps/web/src/components/common/editor/MindEditor.tsx index 6a4e540..00f57e8 100755 --- a/apps/web/src/components/common/editor/MindEditor.tsx +++ b/apps/web/src/components/common/editor/MindEditor.tsx @@ -25,14 +25,17 @@ export default function MindEditor({ id }: { id?: string }) { const containerRef = useRef<HTMLDivElement>(null); const [instance, setInstance] = useState<MindElixirInstance | null>(null); const { isAuthenticated, user, hasSomePermissions } = useAuth(); - const { read } = useVisitor() + const { read } = useVisitor(); const { data: post, isLoading }: { data: PathDto; isLoading: boolean } = - api.post.findFirst.useQuery({ - where: { - id, + api.post.findFirst.useQuery( + { + where: { + id, + }, + select: postDetailSelect, }, - select: postDetailSelect, - }, { enabled: Boolean(id) }); + { enabled: Boolean(id) } + ); const canEdit: boolean = useMemo(() => { const isAuth = isAuthenticated && user?.id === post?.author?.id; return !!id || isAuth || hasSomePermissions(RolePerms.MANAGE_ANY_POST); @@ -64,8 +67,8 @@ export default function MindEditor({ id }: { id?: string }) { deptIds: deptIds, }; post.terms?.forEach((term) => { - formData[term.taxonomyId] = term.id // 假设 taxonomyName是您在 Form.Item 中使用的name - }) + formData[term.taxonomyId] = term.id; // 假设 taxonomyName是您在 Form.Item 中使用的name + }); form.setFieldsValue(formData); } }, [post, form, instance, id]); @@ -159,22 +162,18 @@ export default function MindEditor({ id }: { id?: string }) { } console.log(result); }, - (error) => { }, + (error) => {}, `mind-thumb-${new Date().toString()}` ); }; useEffect(() => { - containerRef.current.style.height = `${Math.floor(window.innerHeight - 271)}px`; }, []); return ( - <div className={` flex-col flex `}> {canEdit && taxonomies && ( - <Form - form={form} - className=" bg-white p-4 border-b"> + <Form form={form} className=" bg-white p-4 border-b"> <div className="flex items-center justify-between gap-4"> <div className="flex items-center gap-4"> {taxonomies.map((tax, index) => ( @@ -203,14 +202,16 @@ export default function MindEditor({ id }: { id?: string }) { /> </Form.Item> </div> - {canEdit && <Button - ghost - type="primary" - icon={<SaveOutlined></SaveOutlined>} - onSubmit={(e) => e.preventDefault()} - onClick={handleSave}> - {id ? "更新" : "保存"} - </Button>} + {canEdit && ( + <Button + ghost + type="primary" + icon={<SaveOutlined></SaveOutlined>} + onSubmit={(e) => e.preventDefault()} + onClick={handleSave}> + {id ? "更新" : "保存"} + </Button> + )} </div> </Form> )} @@ -220,24 +221,20 @@ export default function MindEditor({ id }: { id?: string }) { onContextMenu={(e) => e.preventDefault()} /> {canEdit && instance && <NodeMenu mind={instance} />} - { - isLoading && ( - <div - className="py-64 justify-center flex" - style={{ height: "calc(100vh - 271px)" }}> - <Spin size="large"></Spin> - </div> - ) - } - { - !post && id && !isLoading && ( - <div - className="py-64" - style={{ height: "calc(100vh - 271px)" }}> - <Empty></Empty> - </div> - ) - } - </div > + {isLoading && ( + <div + className="py-64 justify-center flex" + style={{ height: "calc(100vh - 271px)" }}> + <Spin size="large"></Spin> + </div> + )} + {!post && id && !isLoading && ( + <div + className="py-64" + style={{ height: "calc(100vh - 271px)" }}> + <Empty></Empty> + </div> + )} + </div> ); } diff --git a/apps/web/src/components/models/post/PostSelect/PostSelect.tsx b/apps/web/src/components/models/post/PostSelect/PostSelect.tsx new file mode 100644 index 0000000..2af8ec2 --- /dev/null +++ b/apps/web/src/components/models/post/PostSelect/PostSelect.tsx @@ -0,0 +1,15 @@ +import { api } from "@nice/client"; +import { Select } from "antd"; +import { useState } from "react"; + +export default function PostSelect() { + api.post.findMany.useQuery({}); + const [search, setSearch] = useState(""); + return ( + <> + <Select + options={[{ value: "id1", label: <></> }]} + onSearch={(inputValue) => setSearch(inputValue)}></Select> + </> + ); +} From 26b2a93f90c8ef68494009f1f83a489033991044 Mon Sep 17 00:00:00 2001 From: ditiqi <ditiqi@163.com> Date: Fri, 28 Feb 2025 15:45:16 +0800 Subject: [PATCH 33/35] add --- apps/web/package.json | 5 +++-- pnpm-lock.yaml | 32 +++++++++++++++++++++----------- 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/apps/web/package.json b/apps/web/package.json index f10e338..7f6c264 100755 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -34,9 +34,8 @@ "@nice/common": "workspace:^", "@nice/config": "workspace:^", "@nice/iconer": "workspace:^", - "@nice/utils": "workspace:^", - "mind-elixir": "workspace:^", "@nice/ui": "workspace:^", + "@nice/utils": "workspace:^", "@tanstack/query-async-storage-persister": "^5.51.9", "@tanstack/react-query": "^5.51.21", "@tanstack/react-query-persist-client": "^5.51.9", @@ -59,6 +58,7 @@ "framer-motion": "^11.15.0", "hls.js": "^1.5.18", "idb-keyval": "^6.2.1", + "mind-elixir": "workspace:^", "mitt": "^3.0.1", "quill": "2.0.3", "react": "18.2.0", @@ -69,6 +69,7 @@ "react-router-dom": "^6.24.1", "superjson": "^2.2.1", "tailwind-merge": "^2.6.0", + "use-debounce": "^10.0.4", "uuid": "^10.0.0", "yjs": "^13.6.20", "zod": "^3.23.8" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 87760d6..b5a3053 100755 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -12,7 +12,7 @@ importers: dependencies: '@nestjs/bullmq': specifier: ^10.2.0 - version: 10.2.3(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15)(bullmq@5.34.8) + version: 10.2.3(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.15)(@nestjs/websockets@10.4.15)(reflect-metadata@0.2.2)(rxjs@7.8.1))(bullmq@5.34.8) '@nestjs/common': specifier: ^10.3.10 version: 10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1) @@ -33,7 +33,7 @@ importers: version: 10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/websockets@10.4.15)(rxjs@7.8.1) '@nestjs/schedule': specifier: ^4.1.0 - version: 4.1.2(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15) + version: 4.1.2(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.15)(@nestjs/websockets@10.4.15)(reflect-metadata@0.2.2)(rxjs@7.8.1)) '@nestjs/websockets': specifier: ^10.3.10 version: 10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15)(@nestjs/platform-socket.io@10.4.15)(reflect-metadata@0.2.2)(rxjs@7.8.1) @@ -148,7 +148,7 @@ importers: version: 10.2.3(chokidar@3.6.0)(typescript@5.7.2) '@nestjs/testing': specifier: ^10.0.0 - version: 10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15)(@nestjs/platform-express@10.4.15) + version: 10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.15)(@nestjs/websockets@10.4.15)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15)) '@types/exceljs': specifier: ^1.3.0 version: 1.3.2 @@ -284,9 +284,6 @@ importers: '@hookform/resolvers': specifier: ^3.9.1 version: 3.10.0(react-hook-form@7.54.2(react@18.2.0)) - '@mind-elixir/node-menu': - specifier: workspace:* - version: link:../../packages/mind-node-menu '@nice/client': specifier: workspace:^ version: link:../../packages/client @@ -404,6 +401,9 @@ importers: tailwind-merge: specifier: ^2.6.0 version: 2.6.0 + use-debounce: + specifier: ^10.0.4 + version: 10.0.4(react@18.2.0) uuid: specifier: ^10.0.0 version: 10.0.0 @@ -7733,6 +7733,12 @@ packages: url-parse@1.5.10: resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} + use-debounce@10.0.4: + resolution: {integrity: sha512-6Cf7Yr7Wk7Kdv77nnJMf6de4HuDE4dTxKij+RqE9rufDsI6zsbjyAxcH5y2ueJCQAnfgKbzXbZHYlkFwmBlWkw==} + engines: {node: '>= 16.0.0'} + peerDependencies: + react: '*' + use-sync-external-store@1.4.0: resolution: {integrity: sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw==} peerDependencies: @@ -9854,15 +9860,15 @@ snapshots: '@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3': optional: true - '@nestjs/bull-shared@10.2.3(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15)': + '@nestjs/bull-shared@10.2.3(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.15)(@nestjs/websockets@10.4.15)(reflect-metadata@0.2.2)(rxjs@7.8.1))': dependencies: '@nestjs/common': 10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1) '@nestjs/core': 10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.15)(@nestjs/websockets@10.4.15)(reflect-metadata@0.2.2)(rxjs@7.8.1) tslib: 2.8.1 - '@nestjs/bullmq@10.2.3(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15)(bullmq@5.34.8)': + '@nestjs/bullmq@10.2.3(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.15)(@nestjs/websockets@10.4.15)(reflect-metadata@0.2.2)(rxjs@7.8.1))(bullmq@5.34.8)': dependencies: - '@nestjs/bull-shared': 10.2.3(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15) + '@nestjs/bull-shared': 10.2.3(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.15)(@nestjs/websockets@10.4.15)(reflect-metadata@0.2.2)(rxjs@7.8.1)) '@nestjs/common': 10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1) '@nestjs/core': 10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.15)(@nestjs/websockets@10.4.15)(reflect-metadata@0.2.2)(rxjs@7.8.1) bullmq: 5.34.8 @@ -9959,7 +9965,7 @@ snapshots: - supports-color - utf-8-validate - '@nestjs/schedule@4.1.2(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15)': + '@nestjs/schedule@4.1.2(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.15)(@nestjs/websockets@10.4.15)(reflect-metadata@0.2.2)(rxjs@7.8.1))': dependencies: '@nestjs/common': 10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1) '@nestjs/core': 10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.15)(@nestjs/websockets@10.4.15)(reflect-metadata@0.2.2)(rxjs@7.8.1) @@ -9977,7 +9983,7 @@ snapshots: transitivePeerDependencies: - chokidar - '@nestjs/testing@10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15)(@nestjs/platform-express@10.4.15)': + '@nestjs/testing@10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.15)(@nestjs/websockets@10.4.15)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15))': dependencies: '@nestjs/common': 10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1) '@nestjs/core': 10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.15)(@nestjs/websockets@10.4.15)(reflect-metadata@0.2.2)(rxjs@7.8.1) @@ -16106,6 +16112,10 @@ snapshots: querystringify: 2.2.0 requires-port: 1.0.0 + use-debounce@10.0.4(react@18.2.0): + dependencies: + react: 18.2.0 + use-sync-external-store@1.4.0(react@18.2.0): dependencies: react: 18.2.0 From b947ac2e43158c7bebf23dafde77941cd433df44 Mon Sep 17 00:00:00 2001 From: ditiqi <ditiqi@163.com> Date: Fri, 28 Feb 2025 15:45:19 +0800 Subject: [PATCH 34/35] add --- apps/web/src/app/main/layout/MainHeader.tsx | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/apps/web/src/app/main/layout/MainHeader.tsx b/apps/web/src/app/main/layout/MainHeader.tsx index 8598603..29e70d7 100755 --- a/apps/web/src/app/main/layout/MainHeader.tsx +++ b/apps/web/src/app/main/layout/MainHeader.tsx @@ -1,12 +1,7 @@ -import { Input, Layout, Avatar, Button, Dropdown } from "antd"; -import { - EditFilled, - PlusOutlined, - SearchOutlined, - UserOutlined, -} from "@ant-design/icons"; +import { Input, Button } from "antd"; +import { PlusOutlined, SearchOutlined, UserOutlined } from "@ant-design/icons"; import { useAuth } from "@web/src/providers/auth-provider"; -import { useNavigate, useParams, useSearchParams } from "react-router-dom"; +import { useNavigate, useParams } from "react-router-dom"; import { UserMenu } from "./UserMenu/UserMenu"; import { NavigationMenu } from "./NavigationMenu"; import { useMainContext } from "./MainProvider"; From c6a40da7c2e0a04f55539cc9dff9b46efd6116ed Mon Sep 17 00:00:00 2001 From: ditiqi <ditiqi@163.com> Date: Fri, 28 Feb 2025 15:45:21 +0800 Subject: [PATCH 35/35] aad --- apps/web/src/app/main/layout/MainProvider.tsx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/apps/web/src/app/main/layout/MainProvider.tsx b/apps/web/src/app/main/layout/MainProvider.tsx index d928e9d..73db96a 100755 --- a/apps/web/src/app/main/layout/MainProvider.tsx +++ b/apps/web/src/app/main/layout/MainProvider.tsx @@ -6,6 +6,7 @@ import React, { useMemo, useState, } from "react"; +import { useDebounce } from "use-debounce"; interface SelectedTerms { [key: string]: string[]; // 每个 slug 对应一个 string 数组 } @@ -35,7 +36,8 @@ export function MainProvider({ children }: MainProviderProps) { PostType.COURSE | PostType.PATH | "both" >("both"); const [showSearchMode, setShowSearchMode] = useState<boolean>(false); - const [searchValue, setSearchValue] = useState(""); + const [searchValue, setSearchValue] = useState<string>(""); + const [debouncedValue] = useDebounce<string>(searchValue, 500); const [selectedTerms, setSelectedTerms] = useState<SelectedTerms>({}); // 初始化状态 const termFilters = useMemo(() => { return Object.entries(selectedTerms) @@ -60,10 +62,10 @@ export function MainProvider({ children }: MainProviderProps) { }, [termFilters]); const searchCondition: Prisma.PostWhereInput = useMemo(() => { const containTextCondition: Prisma.StringNullableFilter = { - contains: searchValue, + contains: debouncedValue, mode: "insensitive" as Prisma.QueryMode, // 使用类型断言 }; - return searchValue + return debouncedValue ? { OR: [ { title: containTextCondition }, @@ -79,7 +81,7 @@ export function MainProvider({ children }: MainProviderProps) { ], } : {}; - }, [searchValue]); + }, [searchValue, debouncedValue]); return ( <MainContext.Provider value={{