From 2f3126e002dc3a6ba7575fe02e1d7e0257a4c38d Mon Sep 17 00:00:00 2001 From: x1ao4 Date: Sat, 5 Jul 2025 00:01:10 +0800 Subject: [PATCH 1/5] =?UTF-8?q?=E5=9C=A8=E6=96=87=E4=BB=B6=E8=A1=A8?= =?UTF-8?q?=E6=A0=BC=E4=B8=AD=E5=A2=9E=E5=8A=A0=E4=BA=86=E6=8C=89=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E7=B1=BB=E5=9E=8B=E6=98=BE=E7=A4=BA=E5=9B=BE=E6=A0=87?= =?UTF-8?q?=E7=9A=84=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 支持 14 种文件类型的专用图标显示,包括视频、音频、图片、文档、代码等,替换原有的统一文件图标,提升用户体验 --- app/static/css/main.css | 45 ++++++++++++++-- app/templates/index.html | 113 +++++++++++++++++++++++++++++++++++---- 2 files changed, 146 insertions(+), 12 deletions(-) diff --git a/app/static/css/main.css b/app/static/css/main.css index e2d2561..5154eb2 100644 --- a/app/static/css/main.css +++ b/app/static/css/main.css @@ -1687,7 +1687,20 @@ button.close:focus, cursor: pointer; } -#fileSelectModal .bi-file-earmark { +#fileSelectModal .bi-file-earmark, +#fileSelectModal .bi-file-earmark-play, +#fileSelectModal .bi-file-earmark-music, +#fileSelectModal .bi-file-earmark-image, +#fileSelectModal .bi-file-earmark-text, +#fileSelectModal .bi-file-earmark-richtext, +#fileSelectModal .bi-file-earmark-zip, +#fileSelectModal .bi-file-earmark-font, +#fileSelectModal .bi-file-earmark-code, +#fileSelectModal .bi-file-earmark-pdf, +#fileSelectModal .bi-file-earmark-word, +#fileSelectModal .bi-file-earmark-excel, +#fileSelectModal .bi-file-earmark-ppt, +#fileSelectModal .bi-file-earmark-medical { color: var(--dark-text-color); font-size: 0.9rem; margin-right: 5px; @@ -3924,7 +3937,20 @@ table.selectable-records .expand-button:hover { } /* 模态框通用文件图标样式 */ -#fileSelectModal .bi-file-earmark { +#fileSelectModal .bi-file-earmark, +#fileSelectModal .bi-file-earmark-play, +#fileSelectModal .bi-file-earmark-music, +#fileSelectModal .bi-file-earmark-image, +#fileSelectModal .bi-file-earmark-text, +#fileSelectModal .bi-file-earmark-richtext, +#fileSelectModal .bi-file-earmark-zip, +#fileSelectModal .bi-file-earmark-font, +#fileSelectModal .bi-file-earmark-code, +#fileSelectModal .bi-file-earmark-pdf, +#fileSelectModal .bi-file-earmark-word, +#fileSelectModal .bi-file-earmark-excel, +#fileSelectModal .bi-file-earmark-ppt, +#fileSelectModal .bi-file-earmark-medical { color: var(--dark-text-color); font-size: 0.95rem; margin-right: 4px !important; @@ -5228,7 +5254,20 @@ body .selectable-files tr.selected-file:has([style*="white-space: normal"]) .fil } /* 文件整理页面的文件图标样式 */ -.selectable-files .bi-file-earmark { +.selectable-files .bi-file-earmark, +.selectable-files .bi-file-earmark-play, +.selectable-files .bi-file-earmark-music, +.selectable-files .bi-file-earmark-image, +.selectable-files .bi-file-earmark-text, +.selectable-files .bi-file-earmark-richtext, +.selectable-files .bi-file-earmark-zip, +.selectable-files .bi-file-earmark-font, +.selectable-files .bi-file-earmark-code, +.selectable-files .bi-file-earmark-pdf, +.selectable-files .bi-file-earmark-word, +.selectable-files .bi-file-earmark-excel, +.selectable-files .bi-file-earmark-ppt, +.selectable-files .bi-file-earmark-medical { font-size: 1.06rem; /* 比模态框的0.95rem大一些 */ margin-right: 7px !important; /* 图标距离文本的距离 */ position: relative; diff --git a/app/templates/index.html b/app/templates/index.html index 347eede..b854762 100644 --- a/app/templates/index.html +++ b/app/templates/index.html @@ -69,18 +69,109 @@ } }); + // 根据文件扩展名获取对应的Bootstrap图标类名 + function getFileIconClass(fileName, isDir = false) { + // 如果是文件夹,返回文件夹图标 + if (isDir) { + return 'bi-folder-fill text-warning'; + } + + // 获取文件扩展名(转为小写) + const ext = fileName.toLowerCase().split('.').pop(); + + // 视频文件 + const videoExts = ['mp4', 'mkv', 'avi', 'mov', 'rmvb', 'flv', 'wmv', 'm4v', 'ts', 'webm', '3gp', 'f4v']; + if (videoExts.includes(ext)) { + return 'bi-file-earmark-play'; + } + + // 音频文件 + const audioExts = ['mp3', 'wav', 'flac', 'aac', 'ogg', 'm4a', 'wma', 'ape', 'ac3', 'dts']; + if (audioExts.includes(ext)) { + return 'bi-file-earmark-music'; + } + + // 图片文件 + const imageExts = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'tiff', 'svg', 'ico', 'raw']; + if (imageExts.includes(ext)) { + return 'bi-file-earmark-image'; + } + + // 文本文件(包括歌词文件和字幕文件) + const textExts = ['txt', 'md', 'rtf', 'log', 'ini', 'cfg', 'conf', 'lrc', 'srt', 'ass', 'ssa', 'vtt', 'sup']; + if (textExts.includes(ext)) { + return 'bi-file-earmark-text'; + } + + // 富文本文件 + const richtextExts = ['rtf', 'odt']; + if (richtextExts.includes(ext)) { + return 'bi-file-earmark-richtext'; + } + + // 压缩文件 + const archiveExts = ['zip', 'rar', '7z', 'tar', 'gz', 'bz2', 'xz', 'lzma', 'cab', 'iso']; + if (archiveExts.includes(ext)) { + return 'bi-file-earmark-zip'; + } + + // 字体文件 + const fontExts = ['ttf', 'otf', 'woff', 'woff2', 'eot']; + if (fontExts.includes(ext)) { + return 'bi-file-earmark-font'; + } + + // 代码文件 + const codeExts = ['js', 'html', 'css', 'py', 'java', 'c', 'cpp', 'php', 'go', 'json', 'xml', 'yml', 'yaml', 'sql', 'sh', 'bat', 'ps1', 'rb', 'swift', 'kt', 'ts', 'jsx', 'tsx', 'vue', 'scss', 'sass', 'less']; + if (codeExts.includes(ext)) { + return 'bi-file-earmark-code'; + } + + // PDF文件 + if (ext === 'pdf') { + return 'bi-file-earmark-pdf'; + } + + // Word文档 + const wordExts = ['doc', 'docx']; + if (wordExts.includes(ext)) { + return 'bi-file-earmark-word'; + } + + // Excel文档 + const excelExts = ['xls', 'xlsx', 'csv']; + if (excelExts.includes(ext)) { + return 'bi-file-earmark-excel'; + } + + // PowerPoint文档 + const pptExts = ['ppt', 'pptx']; + if (pptExts.includes(ext)) { + return 'bi-file-earmark-ppt'; + } + + // 医疗/健康相关文件 + const medicalExts = ['dcm', 'dicom', 'hl7']; + if (medicalExts.includes(ext)) { + return 'bi-file-earmark-medical'; + } + + // 默认文件图标 + return 'bi-file-earmark'; + } + // 添加检测文件整理页面文件名溢出的自定义指令 Vue.directive('check-file-overflow', { inserted: function(el, binding, vnode) { // 检查元素是否溢出 const isOverflowing = el.scrollWidth > el.clientWidth; - + // 如果绑定了值,则绑定到该值对应的文件属性上 if (binding.value) { const indexAndField = binding.value.split('|'); const index = parseInt(indexAndField[0]); const field = indexAndField[1]; - + // 设置文件的_isOverflowing属性 const files = vnode.context.fileManager.fileList; if (files && files[index]) { @@ -97,13 +188,13 @@ update: function(el, binding, vnode) { // 检查元素是否溢出 const isOverflowing = el.scrollWidth > el.clientWidth; - + // 如果绑定了值,则绑定到该值对应的文件属性上 if (binding.value) { const indexAndField = binding.value.split('|'); const index = parseInt(indexAndField[0]); const field = indexAndField[1]; - + // 设置文件的_isOverflowing属性 const files = vnode.context.fileManager.fileList; if (files && files[index]) { @@ -1181,7 +1272,7 @@
- +
- +
@@ -1202,7 +1293,7 @@
- {{ file.file_name }} + {{ file.file_name }}
@@ -1413,13 +1504,13 @@ style="white-space: nowrap; overflow: hidden; text-overflow: ellipsis; padding-right: 25px;" :title="file.file_name" v-check-modal-overflow="key + '|file_name'"> - {{file.file_name}} + {{file.file_name}}
- {{file.file_name}} + {{file.file_name}}
@@ -1975,6 +2066,10 @@ document.removeEventListener('click', this.handleOutsideClick); }, methods: { + // 获取文件图标类名 + getFileIconClass(fileName, isDir = false) { + return getFileIconClass(fileName, isDir); + }, // 拼音排序辅助函数 sortTaskNamesByPinyin(taskNames) { return taskNames.sort((a, b) => { From 08e5a2f6e8a2adebedf28636d95c1558ba009bcf Mon Sep 17 00:00:00 2001 From: x1ao4 Date: Sat, 5 Jul 2025 00:57:17 +0800 Subject: [PATCH 2/5] =?UTF-8?q?=E5=9C=A8=E4=BB=BB=E5=8A=A1=E5=88=97?= =?UTF-8?q?=E8=A1=A8=E9=A1=B5=E9=9D=A2=E6=96=B0=E5=A2=9E=E6=98=BE=E7=A4=BA?= =?UTF-8?q?=E6=9C=80=E8=BF=91=E6=9B=B4=E6=96=B0=E6=97=A5=E6=9C=9F=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在任务名称后显示最新转存记录日期(格式:· 07-04) - 新增显示设置选项:始终显示/悬停显示/禁用 - 添加 /task_latest_records API 获取任务最新记录 - 样式与任务名称保持一致,支持悬停交互 - 完全向后兼容,不影响现有功能 --- app/run.py | 56 +++++++++++++++++++++++++++++++++++++++- app/static/css/main.css | 43 ++++++++++++++++++++++-------- app/templates/index.html | 49 ++++++++++++++++++++++++++++++++--- 3 files changed, 133 insertions(+), 15 deletions(-) diff --git a/app/run.py b/app/run.py index be2f470..2392e3c 100644 --- a/app/run.py +++ b/app/run.py @@ -1358,12 +1358,66 @@ def delete_history_records(): deleted_count += db.delete_record(record_id) return jsonify({ - "success": True, + "success": True, "message": f"成功删除 {deleted_count} 条记录", "deleted_count": deleted_count }) +# 获取任务最新转存记录日期 +@app.route("/task_latest_records") +def get_task_latest_records(): + if not is_login(): + return jsonify({"success": False, "message": "未登录"}) + + try: + # 初始化数据库 + db = RecordDB() + + # 获取所有任务的最新转存记录 + cursor = db.conn.cursor() + query = """ + SELECT task_name, MAX(transfer_time) as latest_transfer_time + FROM transfer_records + WHERE task_name != 'rename' + GROUP BY task_name + """ + cursor.execute(query) + results = cursor.fetchall() + + # 转换为字典格式 + task_latest_records = {} + for row in results: + task_name, latest_time = row + if latest_time: + try: + # 确保时间戳在合理范围内 + timestamp = int(latest_time) + if timestamp > 9999999999: # 检测是否为毫秒级时间戳(13位) + timestamp = timestamp / 1000 # 转换为秒级时间戳 + + if 0 < timestamp < 4102444800: # 从1970年到2100年的合理时间戳范围 + # 格式化为月-日格式 + date_obj = datetime.fromtimestamp(timestamp) + formatted_date = date_obj.strftime("%m-%d") + task_latest_records[task_name] = formatted_date + except (ValueError, TypeError, OverflowError): + pass # 忽略无效的时间戳 + + db.close() + + return jsonify({ + "success": True, + "data": task_latest_records + }) + + except Exception as e: + return jsonify({ + "success": False, + "message": f"获取任务最新记录失败: {str(e)}" + }) + + # 删除单条转存记录 @app.route("/delete_history_record", methods=["POST"]) def delete_history_record(): diff --git a/app/static/css/main.css b/app/static/css/main.css index 5154eb2..27e6fe9 100644 --- a/app/static/css/main.css +++ b/app/static/css/main.css @@ -4055,24 +4055,45 @@ table.selectable-records .expand-button:hover { } } +/* 任务最近更新日期样式 */ +.task-latest-date { + color: var(--dark-text-color); + font-size: 0.95rem; + font-weight: normal; + opacity: 1; + transition: opacity 0.2s ease; +} + +/* 悬停显示模式 */ +.task .btn:not(:hover) .task-latest-date.hover-only { + opacity: 0; +} + +.task .btn:hover .task-latest-date.hover-only { + opacity: 1; +} + +/* 显示设置行样式 */ +.display-setting-row > [class*='col-'] { + padding-left: 4px !important; + padding-right: 4px !important; +} + +.display-setting-row { + margin-left: -4px !important; + margin-right: -4px !important; +} + @media (min-width: 992px) { .display-setting-row > .col-lg-3 { padding-left: 4px !important; padding-right: 4px !important; } - .display-setting-row { - margin-left: -4px !important; - margin-right: -4px !important; - } } -.display-setting-row > [class*='col-'] { - padding-left: 4px !important; - padding-right: 4px !important; -} -.display-setting-row { - margin-left: -4px !important; - margin-right: -4px !important; +/* 调整显示设置第二行的上边距,使其与第一行保持8px间距 */ +.display-setting-row + .display-setting-row { + margin-top: -8px !important; } /* 文件整理性能设置样式 */ diff --git a/app/templates/index.html b/app/templates/index.html index b854762..41dfb67 100644 --- a/app/templates/index.html +++ b/app/templates/index.html @@ -705,6 +705,20 @@
+
+
+
+
+ 最近更新日期 +
+ +
+
+
@@ -781,6 +795,11 @@
# + + · {{ taskLatestRecords[task.taskname] }} +
@@ -1613,7 +1632,8 @@ run_task: "always", delete_task: "always", refresh_plex: "always", - refresh_alist: "always" + refresh_alist: "always", + latest_update_date: "always" }, file_performance: { api_page_size: 200, @@ -1642,6 +1662,7 @@ taskDirs: [""], taskDirSelected: "", taskNameFilter: "", + taskLatestRecords: {}, // 存储每个任务的最新转存记录日期 modalLoading: false, smart_param: { index: null, @@ -2538,9 +2559,14 @@ run_task: "always", delete_task: "always", refresh_plex: "always", - refresh_alist: "always" + refresh_alist: "always", + latest_update_date: "always" }; } + // 确保最近更新日期配置存在(向后兼容) + if (!config_data.button_display.latest_update_date) { + config_data.button_display.latest_update_date = "always"; + } // 确保文件整理性能配置存在 if (!config_data.file_performance) { config_data.file_performance = { @@ -2563,7 +2589,10 @@ setTimeout(() => { this.configModified = false; }, 100); - + + // 加载任务最新记录 + this.loadTaskLatestRecords(); + // 数据加载完成后检查分享链接状态 if (this.activeTab === 'tasklist') { setTimeout(() => { @@ -4138,6 +4167,20 @@ } }); }, + loadTaskLatestRecords() { + // 获取所有任务的最新转存记录日期 + axios.get('/task_latest_records') + .then(response => { + if (response.data.success) { + this.taskLatestRecords = response.data.data; + } else { + console.error('获取任务最新记录失败:', response.data.message); + } + }) + .catch(error => { + console.error('获取任务最新记录失败:', error); + }); + }, openDatePicker(index) { // 使用$refs访问对应的日期选择器并打开它 const dateRef = this.$refs[`enddate_${index}`]; From d61c0ee9cda94a38349e7e87dd9909342efa31aa Mon Sep 17 00:00:00 2001 From: x1ao4 Date: Sat, 5 Jul 2025 02:58:29 +0800 Subject: [PATCH 3/5] =?UTF-8?q?=E5=9C=A8=E4=BB=BB=E5=8A=A1=E5=88=97?= =?UTF-8?q?=E8=A1=A8=E9=A1=B5=E9=9D=A2=E6=96=B0=E5=A2=9E=E6=98=BE=E7=A4=BA?= =?UTF-8?q?=E6=9C=80=E8=BF=91=E8=BD=AC=E5=AD=98=E6=96=87=E4=BB=B6=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=E5=B9=B6=E4=BC=98=E5=8C=96=20API=20=E6=80=A7=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在任务名称后显示最新转存文件名(格式:· 文件名) - 新增显示设置选项:始终显示/悬停显示/禁用 - 合并 /task_latest_records 和 /task_latest_files 为统一的 /task_latest_info API - 自动提取季数集数信息显示(如:乘风2025 - S06E10 → S06E10) - 样式与最近更新日期保持一致,支持悬停交互 - 完全向后兼容,不影响现有功能和配置 --- app/run.py | 101 ++++++++++++++++++++++++++++++++++----- app/static/css/main.css | 22 ++++++--- app/templates/index.html | 45 +++++++++++++---- 3 files changed, 139 insertions(+), 29 deletions(-) diff --git a/app/run.py b/app/run.py index 2392e3c..37abac0 100644 --- a/app/run.py +++ b/app/run.py @@ -40,6 +40,35 @@ from quark_auto_save import Config, format_bytes sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from quark_auto_save import extract_episode_number, sort_file_by_name, chinese_to_arabic, is_date_format + +def process_season_episode_info(filename): + """ + 处理文件名中的季数和集数信息 + + Args: + filename: 文件名(不含扩展名) + + Returns: + 处理后的显示名称 + """ + # 匹配 SxxExx 格式(不区分大小写) + # 支持 S1E1, S01E01, s13e10 等格式 + season_episode_match = re.search(r'[Ss](\d{1,2})[Ee](\d{1,3})', filename) + if season_episode_match: + season = season_episode_match.group(1).zfill(2) # 确保两位数 + episode = season_episode_match.group(2).zfill(2) # 确保两位数 + return f"S{season}E{episode}" + + # 匹配只有 Exx 或 EPxx 格式(不区分大小写) + # 支持 E1, E01, EP1, EP01, e10, ep10 等格式 + episode_only_match = re.search(r'[Ee][Pp]?(\d{1,3})', filename) + if episode_only_match: + episode = episode_only_match.group(1).zfill(2) # 确保两位数 + return f"E{episode}" + + # 如果没有匹配到季数集数信息,返回原文件名 + return filename + # 导入拼音排序工具 try: from utils.pinyin_sort import get_filename_pinyin_sort_key @@ -1364,18 +1393,18 @@ def delete_history_records(): }) -# 获取任务最新转存记录日期 -@app.route("/task_latest_records") -def get_task_latest_records(): +# 获取任务最新转存信息(包括日期和文件) +@app.route("/task_latest_info") +def get_task_latest_info(): if not is_login(): return jsonify({"success": False, "message": "未登录"}) try: # 初始化数据库 db = RecordDB() - - # 获取所有任务的最新转存记录 cursor = db.conn.cursor() + + # 获取所有任务的最新转存时间 query = """ SELECT task_name, MAX(transfer_time) as latest_transfer_time FROM transfer_records @@ -1383,13 +1412,14 @@ def get_task_latest_records(): GROUP BY task_name """ cursor.execute(query) - results = cursor.fetchall() + latest_times = cursor.fetchall() - # 转换为字典格式 - task_latest_records = {} - for row in results: - task_name, latest_time = row + task_latest_records = {} # 存储最新转存日期 + task_latest_files = {} # 存储最新转存文件 + + for task_name, latest_time in latest_times: if latest_time: + # 1. 处理最新转存日期 try: # 确保时间戳在合理范围内 timestamp = int(latest_time) @@ -1404,20 +1434,67 @@ def get_task_latest_records(): except (ValueError, TypeError, OverflowError): pass # 忽略无效的时间戳 + # 2. 处理最新转存文件 + # 获取该任务在最新转存时间附近(同一分钟内)的所有文件 + # 这样可以处理同时转存多个文件但时间戳略有差异的情况 + time_window = 60000 # 60秒的时间窗口(毫秒) + query = """ + SELECT renamed_to, original_name, transfer_time, modify_date + FROM transfer_records + WHERE task_name = ? AND transfer_time >= ? AND transfer_time <= ? + ORDER BY id DESC + """ + cursor.execute(query, (task_name, latest_time - time_window, latest_time + time_window)) + files = cursor.fetchall() + + if files: + if len(files) == 1: + # 如果只有一个文件,直接使用 + best_file = files[0][0] # renamed_to + else: + # 如果有多个文件,使用全局排序函数进行排序 + file_list = [] + for renamed_to, original_name, transfer_time, modify_date in files: + # 构造文件信息字典,模拟全局排序函数需要的格式 + file_info = { + 'file_name': renamed_to, + 'original_name': original_name, + 'updated_at': transfer_time # 使用转存时间而不是文件修改时间 + } + file_list.append(file_info) + + # 使用全局排序函数进行正向排序,最后一个就是最新的 + try: + sorted_files = sorted(file_list, key=sort_file_by_name) + best_file = sorted_files[-1]['file_name'] # 取排序后的最后一个文件 + except Exception as e: + # 如果排序失败,使用第一个文件作为备选 + best_file = files[0][0] + + # 去除扩展名并处理季数集数信息 + if best_file: + file_name_without_ext = os.path.splitext(best_file)[0] + processed_name = process_season_episode_info(file_name_without_ext) + task_latest_files[task_name] = processed_name + db.close() return jsonify({ "success": True, - "data": task_latest_records + "data": { + "latest_records": task_latest_records, + "latest_files": task_latest_files + } }) except Exception as e: return jsonify({ "success": False, - "message": f"获取任务最新记录失败: {str(e)}" + "message": f"获取任务最新信息失败: {str(e)}" }) + # 删除单条转存记录 @app.route("/delete_history_record", methods=["POST"]) def delete_history_record(): diff --git a/app/static/css/main.css b/app/static/css/main.css index 27e6fe9..f2f41f6 100644 --- a/app/static/css/main.css +++ b/app/static/css/main.css @@ -4060,17 +4060,25 @@ table.selectable-records .expand-button:hover { color: var(--dark-text-color); font-size: 0.95rem; font-weight: normal; - opacity: 1; - transition: opacity 0.2s ease; } -/* 悬停显示模式 */ -.task .btn:not(:hover) .task-latest-date.hover-only { - opacity: 0; +/* 任务最近转存文件样式 */ +.task-latest-file { + color: var(--dark-text-color); + font-size: 0.95rem; + font-weight: normal; } -.task .btn:hover .task-latest-date.hover-only { - opacity: 1; +/* 悬停显示模式 - 使用display来避免占用空间,同时保持平滑的悬停效果 */ +.task-latest-date.hover-only, +.task-latest-file.hover-only { + display: none; + transition: all 0.2s ease; +} + +.task .btn:hover .task-latest-date.hover-only, +.task .btn:hover .task-latest-file.hover-only { + display: inline; } /* 显示设置行样式 */ diff --git a/app/templates/index.html b/app/templates/index.html index 41dfb67..d445fbd 100644 --- a/app/templates/index.html +++ b/app/templates/index.html @@ -718,6 +718,18 @@
+
+
+
+ 最近转存文件 +
+ +
+
@@ -800,6 +812,11 @@ :class="{'hover-only': formData.button_display.latest_update_date === 'hover'}"> · {{ taskLatestRecords[task.taskname] }} + + · {{ taskLatestFiles[task.taskname] }} +
@@ -1633,7 +1650,8 @@ delete_task: "always", refresh_plex: "always", refresh_alist: "always", - latest_update_date: "always" + latest_update_date: "always", + latest_transfer_file: "always" }, file_performance: { api_page_size: 200, @@ -1663,6 +1681,7 @@ taskDirSelected: "", taskNameFilter: "", taskLatestRecords: {}, // 存储每个任务的最新转存记录日期 + taskLatestFiles: {}, // 存储每个任务的最近转存文件 modalLoading: false, smart_param: { index: null, @@ -2560,13 +2579,18 @@ delete_task: "always", refresh_plex: "always", refresh_alist: "always", - latest_update_date: "always" + latest_update_date: "always", + latest_transfer_file: "always" }; } // 确保最近更新日期配置存在(向后兼容) if (!config_data.button_display.latest_update_date) { config_data.button_display.latest_update_date = "always"; } + // 确保最近转存文件配置存在(向后兼容) + if (!config_data.button_display.latest_transfer_file) { + config_data.button_display.latest_transfer_file = "always"; + } // 确保文件整理性能配置存在 if (!config_data.file_performance) { config_data.file_performance = { @@ -2590,8 +2614,8 @@ this.configModified = false; }, 100); - // 加载任务最新记录 - this.loadTaskLatestRecords(); + // 加载任务最新信息(包括记录和文件) + this.loadTaskLatestInfo(); // 数据加载完成后检查分享链接状态 if (this.activeTab === 'tasklist') { @@ -4167,18 +4191,19 @@ } }); }, - loadTaskLatestRecords() { - // 获取所有任务的最新转存记录日期 - axios.get('/task_latest_records') + loadTaskLatestInfo() { + // 获取所有任务的最新转存信息(包括日期和文件) + axios.get('/task_latest_info') .then(response => { if (response.data.success) { - this.taskLatestRecords = response.data.data; + this.taskLatestRecords = response.data.data.latest_records; + this.taskLatestFiles = response.data.data.latest_files; } else { - console.error('获取任务最新记录失败:', response.data.message); + console.error('获取任务最新信息失败:', response.data.message); } }) .catch(error => { - console.error('获取任务最新记录失败:', error); + console.error('获取任务最新信息失败:', error); }); }, openDatePicker(index) { From 8ac34dbb51b47f47cecebd8057f3c50982c8d1a3 Mon Sep 17 00:00:00 2001 From: x1ao4 Date: Sat, 5 Jul 2025 16:17:00 +0800 Subject: [PATCH 4/5] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=98=BE=E7=A4=BA?= =?UTF-8?q?=E5=BD=93=E6=97=A5=E6=9B=B4=E6=96=B0=E6=A0=87=E8=AF=86=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=EF=BC=8C=E4=BF=AE=E6=94=B9=E6=96=87=E4=BB=B6=E5=A4=B9?= =?UTF-8?q?=E5=9B=BE=E6=A0=87=E9=A2=9C=E8=89=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 对于当日更新的内容,在任务列表的任务名称、转存记录的转存为名称、文件整理的文件名后方显示当日更新标识(星星图标) - 新增显示设置选项:始终显示/悬停显示/禁用 - 完全向后兼容,不影响现有功能 - 把表格中的文件夹图标由黄色改为蓝色,视觉效果更统一和谐 --- app/run.py | 8 ++- app/static/css/main.css | 62 +++++++++++++------ app/templates/index.html | 130 ++++++++++++++++++++++++++++++++++++--- 3 files changed, 171 insertions(+), 29 deletions(-) diff --git a/app/run.py b/app/run.py index 37abac0..d63b749 100644 --- a/app/run.py +++ b/app/run.py @@ -1427,10 +1427,14 @@ def get_task_latest_info(): timestamp = timestamp / 1000 # 转换为秒级时间戳 if 0 < timestamp < 4102444800: # 从1970年到2100年的合理时间戳范围 - # 格式化为月-日格式 + # 格式化为月-日格式(用于显示)和完整日期(用于今日判断) date_obj = datetime.fromtimestamp(timestamp) formatted_date = date_obj.strftime("%m-%d") - task_latest_records[task_name] = formatted_date + full_date = date_obj.strftime("%Y-%m-%d") + task_latest_records[task_name] = { + "display": formatted_date, # 显示用的 MM-DD 格式 + "full": full_date # 比较用的 YYYY-MM-DD 格式 + } except (ValueError, TypeError, OverflowError): pass # 忽略无效的时间戳 diff --git a/app/static/css/main.css b/app/static/css/main.css index f2f41f6..99bc8db 100644 --- a/app/static/css/main.css +++ b/app/static/css/main.css @@ -1678,7 +1678,7 @@ button.close:focus, } #fileSelectModal .table .text-warning { - color: #ffc107 !important; + color: #098eff !important; } /* 弹窗内表格行悬停效果 */ @@ -3928,7 +3928,7 @@ table.selectable-records .expand-button:hover { /* 模态框通用文件夹图标样式 */ #fileSelectModal .bi-folder-fill { - color: #ffc107; + color: #098eff; font-size: 0.95rem; margin-right: 4px !important; position: relative; @@ -4020,7 +4020,6 @@ table.selectable-records .expand-button:hover { /* 任务按钮悬停显示样式 */ .task-buttons .hover-only { opacity: 0; - transition: opacity 0.2s ease-in-out; position: absolute; right: 0; visibility: hidden; @@ -4087,6 +4086,45 @@ table.selectable-records .expand-button:hover { padding-right: 4px !important; } +/* 当天更新指示器样式 */ +.task-today-indicator { + display: inline-block; + font-size: 0.92rem; + font-weight: bold; +} + +.task-today-indicator i { + background: linear-gradient(45deg, #03d5ff 25%, var(--focus-border-color) 60%); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; + color: var(--focus-border-color); /* 备用颜色,以防渐变不支持 */ +} + +/* 当日更新图标悬停显示样式 */ +.task-today-indicator.hover-only { + opacity: 0; + visibility: hidden; +} + +/* 任务列表页面悬停显示 */ +.task:hover .task-today-indicator.hover-only { + opacity: 1; + visibility: visible; +} + +/* 转存记录页面悬停显示 */ +.table-hover tbody tr:hover .task-today-indicator.hover-only { + opacity: 1; + visibility: visible; +} + +/* 文件整理页面悬停显示 */ +.selectable-files tbody tr:hover .task-today-indicator.hover-only { + opacity: 1; + visibility: visible; +} + .display-setting-row { margin-left: -4px !important; margin-right: -4px !important; @@ -4130,22 +4168,6 @@ table.selectable-records .expand-button:hover { cursor: default; } -/* 任务按钮悬停显示样式 */ -.task-buttons .hover-only { - opacity: 0; - transition: opacity 0.2s ease-in-out; - position: absolute; - right: 0; - visibility: hidden; -} - -/* 修改悬停触发范围到整个任务单元 */ -.task:hover .task-buttons .hover-only { - opacity: 1; - visibility: visible; - position: static; -} - /* 确保按钮容器在悬停时保持宽度 */ .task-buttons { position: relative; @@ -5311,7 +5333,7 @@ body .selectable-files tr.selected-file:has([style*="white-space: normal"]) .fil position: relative; top: 1px; /* 可微调垂直对齐 */ left: -1px; /* 可微调水平对齐 */ - color: #ffc107; /* 保持黄色 */ + color: #098eff; /* 55%接近深蓝色 */ } /* 文件整理页面无法识别剧集编号样式 */ diff --git a/app/templates/index.html b/app/templates/index.html index d445fbd..643f4b7 100644 --- a/app/templates/index.html +++ b/app/templates/index.html @@ -73,7 +73,7 @@ function getFileIconClass(fileName, isDir = false) { // 如果是文件夹,返回文件夹图标 if (isDir) { - return 'bi-folder-fill text-warning'; + return 'bi-folder-fill'; } // 获取文件扩展名(转为小写) @@ -730,6 +730,18 @@
+
+
+
+ 当日更新标识 +
+ +
+
@@ -810,13 +822,18 @@ - · {{ taskLatestRecords[task.taskname] }} + · {{ getTaskLatestRecordDisplay(task.taskname) }} · {{ taskLatestFiles[task.taskname] }} + + +
@@ -1073,17 +1090,27 @@
-
{{ record.renamed_to }} + + +
{{ record.renamed_to }} + + +
@@ -1326,10 +1353,20 @@ :title="file.file_name" v-check-file-overflow="index + '|file_name'"> {{ file.file_name }} + + +
{{ file.file_name }} + + +
@@ -1651,7 +1688,8 @@ refresh_plex: "always", refresh_alist: "always", latest_update_date: "always", - latest_transfer_file: "always" + latest_transfer_file: "always", + today_update_indicator: "always" }, file_performance: { api_page_size: 200, @@ -2580,7 +2618,8 @@ refresh_plex: "always", refresh_alist: "always", latest_update_date: "always", - latest_transfer_file: "always" + latest_transfer_file: "always", + today_update_indicator: "always" }; } // 确保最近更新日期配置存在(向后兼容) @@ -2591,6 +2630,10 @@ if (!config_data.button_display.latest_transfer_file) { config_data.button_display.latest_transfer_file = "always"; } + // 确保当日更新图标配置存在(向后兼容) + if (!config_data.button_display.today_update_indicator) { + config_data.button_display.today_update_indicator = "always"; + } // 确保文件整理性能配置存在 if (!config_data.file_performance) { config_data.file_performance = { @@ -4206,6 +4249,79 @@ console.error('获取任务最新信息失败:', error); }); }, + getTaskLatestRecordDisplay(taskName) { + // 获取任务最新记录的显示文本 + const latestRecord = this.taskLatestRecords[taskName]; + return latestRecord ? latestRecord.display : ''; + }, + isTaskUpdatedToday(taskName) { + // 检查任务是否在今天更新 + const latestRecord = this.taskLatestRecords[taskName]; + if (!latestRecord || !latestRecord.full) { + return false; + } + + // 获取今天的完整日期,格式为 YYYY-MM-DD + const today = new Date(); + const todayFormatted = today.getFullYear() + '-' + + String(today.getMonth() + 1).padStart(2, '0') + '-' + + String(today.getDate()).padStart(2, '0'); + + return latestRecord.full === todayFormatted; + }, + isRecordUpdatedToday(record) { + // 检查转存记录是否在今天更新 + if (!record || !record.transfer_time_readable) { + return false; + } + + // 获取今天的日期,格式为 YYYY-MM-DD + const today = new Date(); + const todayFormatted = today.getFullYear() + '-' + + String(today.getMonth() + 1).padStart(2, '0') + '-' + + String(today.getDate()).padStart(2, '0'); + + // 从 transfer_time_readable 中提取日期部分(格式通常为 "YYYY-MM-DD HH:MM:SS") + const recordDate = record.transfer_time_readable.split(' ')[0]; + + return recordDate === todayFormatted; + }, + isFileUpdatedToday(file) { + // 检查文件是否在今天更新(基于修改日期) + if (!file || !file.updated_at) { + return false; + } + + // 获取今天的日期,格式为 YYYY-MM-DD + const today = new Date(); + const todayFormatted = today.getFullYear() + '-' + + String(today.getMonth() + 1).padStart(2, '0') + '-' + + String(today.getDate()).padStart(2, '0'); + + // 使用与 formatDate 方法相同的逻辑处理时间戳 + try { + const fileDate = new Date(file.updated_at); + const fileDateFormatted = fileDate.getFullYear() + '-' + + String(fileDate.getMonth() + 1).padStart(2, '0') + '-' + + String(fileDate.getDate()).padStart(2, '0'); + + return fileDateFormatted === todayFormatted; + } catch (error) { + console.error('处理文件时间戳时出错:', error, file.updated_at); + return false; + } + }, + shouldShowTodayIndicator() { + // 检查是否应该显示当日更新图标 + return this.formData.button_display.today_update_indicator !== 'disabled'; + }, + getTodayIndicatorClass() { + // 获取当日更新图标的CSS类 + if (this.formData.button_display.today_update_indicator === 'hover') { + return 'hover-only'; + } + return ''; + }, openDatePicker(index) { // 使用$refs访问对应的日期选择器并打开它 const dateRef = this.$refs[`enddate_${index}`]; From da8f29285bf3b3f11d981ce61c7284d06cfe8d75 Mon Sep 17 00:00:00 2001 From: x1ao4 <112841659+x1ao4@users.noreply.github.com> Date: Sat, 5 Jul 2025 16:36:44 +0800 Subject: [PATCH 5/5] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E7=8A=B6=E6=80=81=E6=98=BE=E7=A4=BA=E7=9A=84=E7=9B=B8=E5=85=B3?= =?UTF-8?q?=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 155e479..1e55ef3 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ - **查重逻辑**:支持优先通过历史转存记录查重,对于有转存记录的文件,即使删除网盘文件,也不会重复转存。 - **Aria2**:支持成功添加 Aria2 下载任务后自动删除夸克网盘内对应的文件,清理网盘空间。 - **文件整理**:支持浏览和管理多个夸克账号的网盘文件,支持单项/批量重命名(支持应用完整的命名、过滤规则和撤销重命名等操作)、移动文件、删除文件、新建文件夹等操作。 +- **更新状态**:支持在任务列表页面显示任务的最近更新日期、最近转存文件,支持在任务列表、转存记录、文件整理页面显示当日更新标识(对于当日更新的内容)。 本项目修改后的版本为个人需求定制版,目的是满足我自己的使用需求,某些(我不用的)功能可能会因为修改而出现 BUG,不一定会被修复。若你要使用本项目,请知晓本人不是程序员,我无法保证本项目的稳定性,如果你在使用过程中发现了 BUG,可以在 Issues 中提交,但不保证每个 BUG 都能被修复,请谨慎使用,风险自担。