diff --git a/webgui/web/assets/js/app.js b/webgui/web/assets/js/app.js index de93e34..1bf3efc 100644 --- a/webgui/web/assets/js/app.js +++ b/webgui/web/assets/js/app.js @@ -6,7 +6,7 @@ import { onRoute } from "./state.js"; import { t, currentLocale, setLocale, onLocale } from "./i18n.js"; import { renderRemotes } from "./views/remotes.js"; import { renderBrowse } from "./views/browser.js"; -import { renderJobs, renderNewJob, stopJobPolling } from "./views/jobs.js"; +import { renderJobs, renderNewJob, stopJobPolling } from "./views/jobs.js?v=jobs-details-1"; import { renderConfigureNew, renderConfigureEdit, diff --git a/webgui/web/assets/js/i18n.js b/webgui/web/assets/js/i18n.js index 47c1f90..a721ffd 100644 --- a/webgui/web/assets/js/i18n.js +++ b/webgui/web/assets/js/i18n.js @@ -47,6 +47,7 @@ const STRINGS = { "error.couldnt_list": "Couldn't list", "error.couldnt_load_jobs": "Couldn't load jobs", "error.no_remotes_selected": "Pick source and destination remotes", + "error.no_paths_selected": "Enter source and destination paths", "error.not_connected": "Couldn't connect to rclone", // footer @@ -156,16 +157,18 @@ const STRINGS = { "jobs.new_btn": "New Job", "jobs.cancel": "Cancel", "jobs.new_title": "New Job", - "jobs.new_subtitle": "Copy, sync, or move between remotes.", + "jobs.new_subtitle": "Copy, sync, or move between remotes and local paths.", "jobs.action": "Action", "jobs.action_copy": "copy (mirror src → dst, keep both)", "jobs.action_sync": "sync (mirror src → dst, delete extras on dst)", "jobs.action_move": "move (mirror src → dst, delete src after)", - "jobs.source_remote": "Source remote", - "jobs.source_path": "Source path (optional)", - "jobs.dest_remote": "Destination remote", - "jobs.dest_path": "Destination path (optional)", - "jobs.path_placeholder": "folder/subfolder", + "jobs.local_option": "Local filesystem", + "jobs.local_path_help": "For Local filesystem, enter a path visible inside the rclone process, such as /data/source in this Docker setup. For remotes, enter a path inside the selected remote.", + "jobs.source_remote": "Source location", + "jobs.source_path": "Source path", + "jobs.dest_remote": "Destination location", + "jobs.dest_path": "Destination path", + "jobs.path_placeholder": "folder/subfolder or /local/path", "jobs.start": "Start Job", "jobs.no_remotes_option": "(no remotes)", "jobs.empty_title": "No jobs yet", @@ -175,6 +178,20 @@ const STRINGS = { "jobs.stop_confirm": (id) => `Stop job #${id}?`, "jobs.stopped": (id) => `Stopped job #${id}`, "jobs.stop_failed": "Stop failed", + "jobs.details": "Details", + "jobs.hide_details": "Hide", + "jobs.delete_record": "Delete", + "jobs.delete_confirm": (id) => `Delete job #${id} from this list? This won't cancel a running transfer.`, + "jobs.detail_id": "Job ID", + "jobs.detail_status": "Status", + "jobs.detail_error": "Error", + "jobs.detail_started": "Started", + "jobs.detail_finished": "Finished", + "jobs.detail_duration": "Duration", + "jobs.detail_group": "Group", + "jobs.detail_execute_id": "Execution", + "jobs.detail_output": "Output", + "jobs.detail_none": "None", "jobs.col.id": "#", "jobs.col.job": "Job", "jobs.col.status": "Status", @@ -209,7 +226,8 @@ const STRINGS = { "error.couldnt_load_remotes": "无法加载远程存储列表", "error.couldnt_list": "无法列出", "error.couldnt_load_jobs": "无法加载任务列表", - "error.no_remotes_selected": "请选择源和目标远程存储", + "error.no_remotes_selected": "请选择源和目标位置", + "error.no_paths_selected": "请输入源路径和目标路径", "error.not_connected": "无法连接到 rclone", "footer.product": "产品", @@ -311,16 +329,18 @@ const STRINGS = { "jobs.new_btn": "新建任务", "jobs.cancel": "取消", "jobs.new_title": "新建任务", - "jobs.new_subtitle": "在远程存储之间复制、同步或移动。", + "jobs.new_subtitle": "在远程存储和本地路径之间复制、同步或移动。", "jobs.action": "操作", "jobs.action_copy": "copy(镜像 源 → 目标,两者都保留)", "jobs.action_sync": "sync(镜像 源 → 目标,删除目标的额外文件)", "jobs.action_move": "move(镜像 源 → 目标,完成后删除源)", - "jobs.source_remote": "源远程存储", - "jobs.source_path": "源路径(可选)", - "jobs.dest_remote": "目标远程存储", - "jobs.dest_path": "目标路径(可选)", - "jobs.path_placeholder": "文件夹/子文件夹", + "jobs.local_option": "本地文件系统", + "jobs.local_path_help": "选择本地文件系统时填写 rclone 进程内可见的路径;当前 Docker 编排里通常是 /data/source。选择远程存储时填写该远程内的路径。", + "jobs.source_remote": "源位置", + "jobs.source_path": "源路径", + "jobs.dest_remote": "目标位置", + "jobs.dest_path": "目标路径", + "jobs.path_placeholder": "文件夹/子文件夹 或 /local/path", "jobs.start": "开始任务", "jobs.no_remotes_option": "(无远程存储)", "jobs.empty_title": "尚无任务", @@ -330,6 +350,20 @@ const STRINGS = { "jobs.stop_confirm": (id) => `停止任务 #${id}?`, "jobs.stopped": (id) => `已停止任务 #${id}`, "jobs.stop_failed": "停止失败", + "jobs.details": "详情", + "jobs.hide_details": "收起", + "jobs.delete_record": "删除", + "jobs.delete_confirm": (id) => `从列表中删除任务 #${id}? 这不会取消正在运行的传输。`, + "jobs.detail_id": "任务 ID", + "jobs.detail_status": "状态", + "jobs.detail_error": "错误", + "jobs.detail_started": "开始时间", + "jobs.detail_finished": "结束时间", + "jobs.detail_duration": "耗时", + "jobs.detail_group": "分组", + "jobs.detail_execute_id": "执行实例", + "jobs.detail_output": "输出", + "jobs.detail_none": "无", "jobs.col.id": "#", "jobs.col.job": "任务", "jobs.col.status": "状态", diff --git a/webgui/web/assets/js/views/jobs.js b/webgui/web/assets/js/views/jobs.js index 487228a..6636ffe 100644 --- a/webgui/web/assets/js/views/jobs.js +++ b/webgui/web/assets/js/views/jobs.js @@ -17,6 +17,8 @@ import { t } from "../i18n.js"; let pollTimer = null; const WEBGUI_JOB_GROUP_PREFIX = "webgui/transfer"; +const LOCAL_FS_VALUE = "__local__"; +const expandedJobs = new Set(); export async function renderJobs() { const app = document.getElementById("app"); @@ -52,8 +54,10 @@ export async function renderNewJob() { toast(`${t("error.couldnt_load_remotes")}: ${e.message}`, "error"); } - const remoteOpts = remotes - .map((r) => ``) + const locationOpts = [ + ...remotes.map((r) => ``), + ``, + ] .join(""); app.innerHTML = ` @@ -77,7 +81,7 @@ export async function renderNewJob() {