mirror of
https://github.com/Cp0204/quark-auto-save.git
synced 2026-01-12 07:10:44 +08:00
为文件整理增加多账号支持和性能设置功能
This commit is contained in:
parent
7b019ab1e0
commit
7d4672cb8e
295
app/run.py
295
app/run.py
@ -28,6 +28,8 @@ import re
|
||||
import random
|
||||
import time
|
||||
import treelib
|
||||
from functools import lru_cache
|
||||
from threading import Lock
|
||||
|
||||
parent_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
|
||||
sys.path.insert(0, parent_dir)
|
||||
@ -96,6 +98,45 @@ PORT = int(os.environ.get("PORT", "5005"))
|
||||
config_data = {}
|
||||
task_plugins_config_default = {}
|
||||
|
||||
# 文件列表缓存
|
||||
file_list_cache = {}
|
||||
cache_lock = Lock()
|
||||
|
||||
# 默认性能参数(如果配置中没有设置)
|
||||
DEFAULT_PERFORMANCE_CONFIG = {
|
||||
"api_page_size": 200,
|
||||
"cache_expire_time": 30
|
||||
}
|
||||
|
||||
def get_performance_config():
|
||||
"""获取性能配置参数"""
|
||||
try:
|
||||
if config_data and "file_performance" in config_data:
|
||||
perf_config = config_data["file_performance"]
|
||||
# 确保所有值都是整数类型
|
||||
result = {}
|
||||
for key, default_value in DEFAULT_PERFORMANCE_CONFIG.items():
|
||||
try:
|
||||
result[key] = int(perf_config.get(key, default_value))
|
||||
except (ValueError, TypeError):
|
||||
result[key] = default_value
|
||||
return result
|
||||
except Exception as e:
|
||||
print(f"获取性能配置失败: {e}")
|
||||
return DEFAULT_PERFORMANCE_CONFIG
|
||||
|
||||
def cleanup_expired_cache():
|
||||
"""清理所有过期缓存"""
|
||||
current_time = time.time()
|
||||
perf_config = get_performance_config()
|
||||
cache_expire_time = perf_config.get("cache_expire_time", 30)
|
||||
|
||||
with cache_lock:
|
||||
expired_keys = [k for k, (_, t) in file_list_cache.items() if current_time - t > cache_expire_time]
|
||||
for k in expired_keys:
|
||||
del file_list_cache[k]
|
||||
return len(expired_keys)
|
||||
|
||||
app = Flask(__name__)
|
||||
app.config["APP_VERSION"] = get_app_ver()
|
||||
app.secret_key = "ca943f6db6dd34823d36ab08d8d6f65d"
|
||||
@ -683,7 +724,15 @@ def get_share_detail():
|
||||
def get_savepath_detail():
|
||||
if not is_login():
|
||||
return jsonify({"success": False, "message": "未登录"})
|
||||
account = Quark(config_data["cookie"][0], 0)
|
||||
|
||||
# 获取账号索引参数
|
||||
account_index = int(request.args.get("account_index", 0))
|
||||
|
||||
# 验证账号索引
|
||||
if account_index < 0 or account_index >= len(config_data["cookie"]):
|
||||
return jsonify({"success": False, "message": "账号索引无效"})
|
||||
|
||||
account = Quark(config_data["cookie"][account_index], account_index)
|
||||
paths = []
|
||||
if path := request.args.get("path"):
|
||||
if path == "/":
|
||||
@ -718,7 +767,15 @@ def get_savepath_detail():
|
||||
def delete_file():
|
||||
if not is_login():
|
||||
return jsonify({"success": False, "message": "未登录"})
|
||||
account = Quark(config_data["cookie"][0], 0)
|
||||
|
||||
# 获取账号索引参数
|
||||
account_index = int(request.json.get("account_index", 0))
|
||||
|
||||
# 验证账号索引
|
||||
if account_index < 0 or account_index >= len(config_data["cookie"]):
|
||||
return jsonify({"success": False, "message": "账号索引无效"})
|
||||
|
||||
account = Quark(config_data["cookie"][account_index], account_index)
|
||||
if fid := request.json.get("fid"):
|
||||
response = account.delete([fid])
|
||||
|
||||
@ -805,6 +862,15 @@ def add_task():
|
||||
# 定时任务执行的函数
|
||||
def run_python(args):
|
||||
logging.info(f">>> 定时运行任务")
|
||||
|
||||
# 在定时任务开始前清理过期缓存
|
||||
try:
|
||||
cleaned_count = cleanup_expired_cache()
|
||||
if cleaned_count > 0:
|
||||
logging.info(f">>> 清理了 {cleaned_count} 个过期缓存项")
|
||||
except Exception as e:
|
||||
logging.warning(f">>> 清理缓存时出错: {e}")
|
||||
|
||||
# 检查是否需要随机延迟执行
|
||||
if delay := config_data.get("crontab_delay"):
|
||||
try:
|
||||
@ -816,7 +882,7 @@ def run_python(args):
|
||||
time.sleep(random_delay)
|
||||
except (ValueError, TypeError):
|
||||
logging.warning(f">>> 延迟执行设置无效: {delay}")
|
||||
|
||||
|
||||
os.system(f"{PYTHON_PATH} {args}")
|
||||
|
||||
|
||||
@ -1075,7 +1141,7 @@ def format_records(records):
|
||||
def get_user_info():
|
||||
if not is_login():
|
||||
return jsonify({"success": False, "message": "未登录"})
|
||||
|
||||
|
||||
user_info_list = []
|
||||
for idx, cookie in enumerate(config_data["cookie"]):
|
||||
account = Quark(cookie, idx)
|
||||
@ -1095,26 +1161,101 @@ def get_user_info():
|
||||
"is_active": False,
|
||||
"has_mparam": has_mparam
|
||||
})
|
||||
|
||||
|
||||
return jsonify({"success": True, "data": user_info_list})
|
||||
|
||||
|
||||
@app.route("/get_accounts_detail")
|
||||
def get_accounts_detail():
|
||||
"""获取所有账号的详细信息,包括昵称和空间使用情况"""
|
||||
if not is_login():
|
||||
return jsonify({"success": False, "message": "未登录"})
|
||||
|
||||
accounts_detail = []
|
||||
for idx, cookie in enumerate(config_data["cookie"]):
|
||||
account = Quark(cookie, idx)
|
||||
account_info = account.init()
|
||||
|
||||
# 如果无法获取账号信息,检查是否有移动端参数
|
||||
if not account_info:
|
||||
has_mparam = bool(account.mparam)
|
||||
# 如果只有移动端参数,跳过此账号(不显示在文件整理页面的账号选择栏中)
|
||||
if has_mparam:
|
||||
continue
|
||||
else:
|
||||
# 如果既没有账号信息也没有移动端参数,显示为未登录
|
||||
account_detail = {
|
||||
"index": idx,
|
||||
"nickname": "",
|
||||
"is_active": False,
|
||||
"used_space": 0,
|
||||
"total_space": 0,
|
||||
"usage_rate": 0,
|
||||
"display_text": f"账号{idx + 1}(未登录)"
|
||||
}
|
||||
accounts_detail.append(account_detail)
|
||||
continue
|
||||
|
||||
# 成功获取账号信息的情况
|
||||
account_detail = {
|
||||
"index": idx,
|
||||
"nickname": account_info["nickname"],
|
||||
"is_active": account.is_active,
|
||||
"used_space": 0,
|
||||
"total_space": 0,
|
||||
"usage_rate": 0,
|
||||
"display_text": ""
|
||||
}
|
||||
|
||||
# 检查是否有移动端参数
|
||||
has_mparam = bool(account.mparam)
|
||||
|
||||
if has_mparam:
|
||||
# 同时有cookie和移动端参数,尝试获取空间信息
|
||||
try:
|
||||
growth_info = account.get_growth_info()
|
||||
if growth_info:
|
||||
total_capacity = growth_info.get("total_capacity", 0)
|
||||
account_detail["total_space"] = total_capacity
|
||||
# 显示昵称和总容量
|
||||
total_str = format_bytes(total_capacity)
|
||||
account_detail["display_text"] = f"{account_info['nickname']} · {total_str}"
|
||||
else:
|
||||
# 获取空间信息失败,只显示昵称
|
||||
account_detail["display_text"] = account_info["nickname"]
|
||||
except Exception as e:
|
||||
logging.error(f"获取账号 {idx} 空间信息失败: {str(e)}")
|
||||
# 获取空间信息失败,只显示昵称
|
||||
account_detail["display_text"] = account_info["nickname"]
|
||||
else:
|
||||
# 只有cookie,没有移动端参数,只显示昵称
|
||||
account_detail["display_text"] = account_info["nickname"]
|
||||
|
||||
accounts_detail.append(account_detail)
|
||||
|
||||
return jsonify({"success": True, "data": accounts_detail})
|
||||
|
||||
|
||||
# 重置文件夹(删除文件夹内所有文件和相关记录)
|
||||
@app.route("/reset_folder", methods=["POST"])
|
||||
def reset_folder():
|
||||
if not is_login():
|
||||
return jsonify({"success": False, "message": "未登录"})
|
||||
|
||||
|
||||
# 获取请求参数
|
||||
save_path = request.json.get("save_path", "")
|
||||
task_name = request.json.get("task_name", "")
|
||||
|
||||
account_index = int(request.json.get("account_index", 0)) # 新增账号索引参数
|
||||
|
||||
if not save_path:
|
||||
return jsonify({"success": False, "message": "保存路径不能为空"})
|
||||
|
||||
|
||||
try:
|
||||
# 验证账号索引
|
||||
if account_index < 0 or account_index >= len(config_data["cookie"]):
|
||||
return jsonify({"success": False, "message": "账号索引无效"})
|
||||
|
||||
# 初始化夸克网盘客户端
|
||||
account = Quark(config_data["cookie"][0], 0)
|
||||
account = Quark(config_data["cookie"][account_index], account_index)
|
||||
|
||||
# 1. 获取文件夹ID
|
||||
# 先检查是否已有缓存的文件夹ID
|
||||
@ -1182,18 +1323,24 @@ def reset_folder():
|
||||
def get_file_list():
|
||||
if not is_login():
|
||||
return jsonify({"success": False, "message": "未登录"})
|
||||
|
||||
|
||||
# 获取请求参数
|
||||
folder_id = request.args.get("folder_id", "root")
|
||||
sort_by = request.args.get("sort_by", "file_name")
|
||||
order = request.args.get("order", "asc")
|
||||
page = int(request.args.get("page", 1))
|
||||
page_size = int(request.args.get("page_size", 15))
|
||||
|
||||
account_index = int(request.args.get("account_index", 0))
|
||||
force_refresh = request.args.get("force_refresh", "false").lower() == "true" # 新增账号索引参数
|
||||
|
||||
try:
|
||||
# 验证账号索引
|
||||
if account_index < 0 or account_index >= len(config_data["cookie"]):
|
||||
return jsonify({"success": False, "message": "账号索引无效"})
|
||||
|
||||
# 初始化夸克网盘客户端
|
||||
account = Quark(config_data["cookie"][0], 0)
|
||||
|
||||
account = Quark(config_data["cookie"][account_index], account_index)
|
||||
|
||||
# 获取文件列表
|
||||
if folder_id == "root":
|
||||
folder_id = "0" # 根目录的ID为0
|
||||
@ -1201,43 +1348,92 @@ def get_file_list():
|
||||
else:
|
||||
# 获取当前文件夹的路径
|
||||
paths = account.get_paths(folder_id)
|
||||
|
||||
# 获取文件列表
|
||||
files = account.ls_dir(folder_id)
|
||||
|
||||
# 获取性能配置
|
||||
perf_config = get_performance_config()
|
||||
api_page_size = perf_config.get("api_page_size", 200)
|
||||
cache_expire_time = perf_config.get("cache_expire_time", 30)
|
||||
|
||||
# 缓存键
|
||||
cache_key = f"{account_index}_{folder_id}_{sort_by}_{order}"
|
||||
current_time = time.time()
|
||||
|
||||
# 检查缓存(除非强制刷新)
|
||||
if not force_refresh:
|
||||
with cache_lock:
|
||||
if cache_key in file_list_cache:
|
||||
cache_data, cache_time = file_list_cache[cache_key]
|
||||
if current_time - cache_time < cache_expire_time:
|
||||
# 使用缓存数据
|
||||
cached_files = cache_data
|
||||
total = len(cached_files)
|
||||
start_idx = (page - 1) * page_size
|
||||
end_idx = min(start_idx + page_size, total)
|
||||
paginated_files = cached_files[start_idx:end_idx]
|
||||
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"data": {
|
||||
"list": paginated_files,
|
||||
"total": total,
|
||||
"paths": paths
|
||||
}
|
||||
})
|
||||
else:
|
||||
# 强制刷新时,清除当前目录的缓存
|
||||
with cache_lock:
|
||||
if cache_key in file_list_cache:
|
||||
del file_list_cache[cache_key]
|
||||
|
||||
# 无论分页还是全部模式,都必须获取所有文件才能进行正确的全局排序
|
||||
files = account.ls_dir(folder_id, page_size=api_page_size)
|
||||
|
||||
if isinstance(files, dict) and files.get("error"):
|
||||
return jsonify({"success": False, "message": f"获取文件列表失败: {files.get('error', '未知错误')}"})
|
||||
|
||||
# 检查是否是目录不存在的错误
|
||||
error_msg = files.get('error', '未知错误')
|
||||
if "不存在" in error_msg or "无效" in error_msg or "找不到" in error_msg:
|
||||
return jsonify({"success": False, "message": f"目录不存在或无权限访问: {error_msg}"})
|
||||
else:
|
||||
return jsonify({"success": False, "message": f"获取文件列表失败: {error_msg}"})
|
||||
|
||||
# 计算总数
|
||||
total = len(files)
|
||||
|
||||
# 排序
|
||||
if sort_by == "file_name":
|
||||
files.sort(key=lambda x: x["file_name"].lower())
|
||||
elif sort_by == "file_size":
|
||||
files.sort(key=lambda x: x["size"] if not x["dir"] else 0)
|
||||
else: # updated_at
|
||||
files.sort(key=lambda x: x["updated_at"])
|
||||
|
||||
if order == "desc":
|
||||
files.reverse()
|
||||
# 优化排序:使用更高效的排序方法
|
||||
def get_sort_key(file_item):
|
||||
if sort_by == "file_name":
|
||||
return file_item["file_name"].lower()
|
||||
elif sort_by == "file_size":
|
||||
return file_item["size"] if not file_item["dir"] else 0
|
||||
else: # updated_at
|
||||
return file_item["updated_at"]
|
||||
|
||||
# 根据排序字段决定是否将目录放在前面
|
||||
# 分离文件夹和文件以优化排序
|
||||
if sort_by == "updated_at":
|
||||
# 修改日期排序时严格按照日期排序,不区分文件夹和文件
|
||||
files.sort(key=get_sort_key, reverse=(order == "desc"))
|
||||
sorted_files = files
|
||||
else:
|
||||
# 其他排序时目录始终在前面
|
||||
# 其他排序时目录始终在前面,分别排序以提高效率
|
||||
directories = [f for f in files if f["dir"]]
|
||||
normal_files = [f for f in files if not f["dir"]]
|
||||
|
||||
directories.sort(key=get_sort_key, reverse=(order == "desc"))
|
||||
normal_files.sort(key=get_sort_key, reverse=(order == "desc"))
|
||||
|
||||
sorted_files = directories + normal_files
|
||||
|
||||
|
||||
# 更新缓存
|
||||
with cache_lock:
|
||||
file_list_cache[cache_key] = (sorted_files, current_time)
|
||||
|
||||
# 分页
|
||||
start_idx = (page - 1) * page_size
|
||||
end_idx = min(start_idx + page_size, total)
|
||||
paginated_files = sorted_files[start_idx:end_idx]
|
||||
|
||||
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"success": True,
|
||||
"data": {
|
||||
"list": paginated_files,
|
||||
"total": total,
|
||||
@ -1263,13 +1459,18 @@ def preview_rename():
|
||||
naming_mode = request.args.get("naming_mode", "regex") # regex, sequence, episode
|
||||
include_folders = request.args.get("include_folders", "false") == "true"
|
||||
filterwords = request.args.get("filterwords", "")
|
||||
|
||||
account_index = int(request.args.get("account_index", 0)) # 新增账号索引参数
|
||||
|
||||
if not pattern:
|
||||
pattern = ".*"
|
||||
|
||||
|
||||
try:
|
||||
# 验证账号索引
|
||||
if account_index < 0 or account_index >= len(config_data["cookie"]):
|
||||
return jsonify({"success": False, "message": "账号索引无效"})
|
||||
|
||||
# 初始化夸克网盘客户端
|
||||
account = Quark(config_data["cookie"][0], 0)
|
||||
account = Quark(config_data["cookie"][account_index], account_index)
|
||||
|
||||
# 获取文件列表
|
||||
if folder_id == "root":
|
||||
@ -1415,13 +1616,18 @@ def batch_rename():
|
||||
# 获取请求参数
|
||||
data = request.json
|
||||
files = data.get("files", [])
|
||||
|
||||
account_index = int(data.get("account_index", 0)) # 新增账号索引参数
|
||||
|
||||
if not files:
|
||||
return jsonify({"success": False, "message": "没有文件需要重命名"})
|
||||
|
||||
|
||||
try:
|
||||
# 验证账号索引
|
||||
if account_index < 0 or account_index >= len(config_data["cookie"]):
|
||||
return jsonify({"success": False, "message": "账号索引无效"})
|
||||
|
||||
# 初始化夸克网盘客户端
|
||||
account = Quark(config_data["cookie"][0], 0)
|
||||
account = Quark(config_data["cookie"][account_index], account_index)
|
||||
|
||||
# 批量重命名
|
||||
success_count = 0
|
||||
@ -1481,10 +1687,17 @@ def undo_rename():
|
||||
return jsonify({"success": False, "message": "未登录"})
|
||||
data = request.json
|
||||
save_path = data.get("save_path", "")
|
||||
account_index = int(data.get("account_index", 0)) # 新增账号索引参数
|
||||
|
||||
if not save_path:
|
||||
return jsonify({"success": False, "message": "缺少目录参数"})
|
||||
|
||||
try:
|
||||
account = Quark(config_data["cookie"][0], 0)
|
||||
# 验证账号索引
|
||||
if account_index < 0 or account_index >= len(config_data["cookie"]):
|
||||
return jsonify({"success": False, "message": "账号索引无效"})
|
||||
|
||||
account = Quark(config_data["cookie"][account_index], account_index)
|
||||
# 查询该目录下最近一次重命名(按transfer_time分组,task_name=rename)
|
||||
records = record_db.get_records_by_save_path(save_path)
|
||||
rename_records = [r for r in records if r["task_name"] == "rename"]
|
||||
|
||||
@ -4037,6 +4037,21 @@ table.selectable-records .expand-button:hover {
|
||||
margin-right: -4px !important;
|
||||
}
|
||||
|
||||
/* 文件整理性能设置样式 */
|
||||
.performance-setting-row > [class*='col-'] {
|
||||
padding-left: 4px !important;
|
||||
padding-right: 4px !important;
|
||||
}
|
||||
.performance-setting-row {
|
||||
margin-left: -4px !important;
|
||||
margin-right: -4px !important;
|
||||
}
|
||||
|
||||
/* 性能设置板块标题上移8px,统一与其他设置板块的间距 */
|
||||
.row.title[title*="文件整理页面的请求参数"] {
|
||||
margin-top: 12px !important; /* 从默认的20px减少到12px,上移8px */
|
||||
}
|
||||
|
||||
/* 任务单元基础样式 */
|
||||
.task {
|
||||
position: relative;
|
||||
@ -4187,6 +4202,19 @@ select.task-filter-select,
|
||||
|
||||
.batch-rename-btn {
|
||||
margin-left: 8px;
|
||||
color: var(--dark-text-color) !important;
|
||||
border-color: var(--dark-text-color) !important;
|
||||
}
|
||||
|
||||
.batch-rename-btn:hover {
|
||||
background-color: var(--dark-text-color) !important;
|
||||
border-color: var(--dark-text-color) !important;
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
/* 文件整理页面刷新当前目录缓存按钮图标大小 */
|
||||
.batch-rename-btn .bi-arrow-clockwise {
|
||||
font-size: 1.15rem;
|
||||
}
|
||||
|
||||
/* 文件表格中的展开按钮 */
|
||||
@ -4422,6 +4450,28 @@ tr.selected-file .file-size-cell .delete-record-btn {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* 文件整理账号选择栏样式 */
|
||||
.file-manager-account-selector {
|
||||
margin-bottom: 8px !important;
|
||||
}
|
||||
|
||||
/* 账号选择下拉框样式 */
|
||||
.file-manager-account-select {
|
||||
padding-left: 8px !important;
|
||||
text-indent: 0 !important;
|
||||
display: flex !important;
|
||||
align-items: center !important;
|
||||
line-height: 1.5 !important;
|
||||
padding-right: 24px !important;
|
||||
}
|
||||
|
||||
/* 账号选择框右侧圆角 - 需要更高优先级覆盖file-manager-input的圆角设置 */
|
||||
.file-manager-rule-bar .file-manager-account-select,
|
||||
.file-manager-rule-bar-responsive .file-manager-account-select {
|
||||
border-top-right-radius: 6px !important;
|
||||
border-bottom-right-radius: 6px !important;
|
||||
}
|
||||
|
||||
/* 禁止在表格中选择文本,以便更好地支持点击选择 */
|
||||
table.selectable-files {
|
||||
user-select: none;
|
||||
@ -4860,6 +4910,12 @@ body .selectable-files tr.selected-file .file-size-cell .delete-record-btn {
|
||||
border-radius: 0 !important; /* 去除圆角 */
|
||||
}
|
||||
|
||||
/* 账号选择框例外 - 保持右侧圆角 */
|
||||
.file-manager-rule-bar .file-manager-input.file-manager-account-select {
|
||||
border-top-right-radius: 6px !important;
|
||||
border-bottom-right-radius: 6px !important;
|
||||
}
|
||||
|
||||
/* 文件整理页面重命名配置框输入框相邻边框重叠样式 */
|
||||
.file-manager-rule-bar .file-manager-input:not(:first-child) {
|
||||
margin-left: -1px; /* 向左移动1px,使边框重叠 */
|
||||
@ -5033,6 +5089,15 @@ body .selectable-files tr.selected-file .file-size-cell .delete-record-btn {
|
||||
.file-manager-rule-bar-responsive { display: block !important; }
|
||||
.file-manager-rule-bar-responsive .input-group { width: 100%; }
|
||||
.file-manager-rule-bar-responsive .input-group + .input-group { margin-top: 8px; }
|
||||
|
||||
/* 移动端账号选择栏下边距 - 确保与命名规则栏有8px间距 */
|
||||
.file-manager-account-selector {
|
||||
margin-bottom: 8px !important;
|
||||
}
|
||||
|
||||
.file-manager-account-selector .file-manager-rule-bar-responsive {
|
||||
margin-bottom: 0 !important; /* 移除内部边距,使用外部容器的边距 */
|
||||
}
|
||||
/* 含文件夹样式调整 */
|
||||
.file-manager-rule-bar-responsive .input-group-text.file-folder-rounded {
|
||||
border-top-left-radius: 0 !important; /* 左侧不要圆角 */
|
||||
@ -5067,6 +5132,11 @@ body .selectable-files tr.selected-file .file-size-cell .delete-record-btn {
|
||||
.file-manager-rule-bar-responsive .input-group .batch-rename-btn {
|
||||
margin-left: 8px !important;
|
||||
}
|
||||
|
||||
/* 移动端账号选择栏中的刷新按钮间距 */
|
||||
.file-manager-rule-bar-responsive .d-flex .batch-rename-btn {
|
||||
margin-left: 8px !important;
|
||||
}
|
||||
}
|
||||
@media (min-width: 768px) {
|
||||
.file-manager-rule-bar { display: flex !important; }
|
||||
|
||||
@ -363,7 +363,7 @@
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<p style="margin-top: -4px; margin-bottom: 4px;">所有账号都会进行签到(纯签到只需填写移动端参数),只有第一个账号会进行转存,请自行确认顺序。如需签到,请在 Cookie 后方添加签到参数。</p>
|
||||
<p style="margin-top: -4px; margin-bottom: 4px;">所有账号都会进行签到(纯签到只需填写移动端参数),只有第一个账号会进行转存,请自行确认账号顺序;所有填写了 Cookie 的账号均支持文件整理,如需签到请在 Cookie 后方添加签到参数。</p>
|
||||
<div v-for="(value, index) in formData.cookie" :key="index" class="input-group mb-2">
|
||||
<div class="input-group-prepend" v-if="userInfoList[index]">
|
||||
<span class="input-group-text" :style="userInfoList[index].nickname ? (userInfoList[index].is_active ? 'color: var(--dark-text-color);' : 'color: red;') : 'color: var(--dark-text-color);'">
|
||||
@ -398,7 +398,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6 pl-1">
|
||||
<div class="input-group" title="添加随机延迟时间:定时任务将在0到设定秒数之间随机延迟执行,可设范围:0–3600秒,0表示不延迟">
|
||||
<div class="input-group" title="添加随机延迟时间:定时任务将在0到设定秒数之间随机延迟执行。建议值:0–3600,0表示不延迟">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">延迟执行</span>
|
||||
</div>
|
||||
@ -612,6 +612,40 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 性能设置 -->
|
||||
<div class="row title" title="调整文件整理页面的请求参数和缓存时长,可提升大文件夹的加载速度和数据刷新效率。合理配置可减少API请求次数,同时保证数据及时更新">
|
||||
<div class="col">
|
||||
<h2 style="display: inline-block; font-size: 1.5rem;">性能设置</h2>
|
||||
<span class="badge badge-pill badge-light">
|
||||
<a href="#"><i class="bi bi-question-circle"></i></a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-2 performance-setting-row">
|
||||
<div class="col-lg-6 col-md-6 mb-2">
|
||||
<div class="input-group" title="每次请求夸克API时获取的文件数量,适当增大该数值可减少请求次数,提升大文件夹的加载效率。建议值:100–500">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">单次请求文件数量</span>
|
||||
</div>
|
||||
<input type="text" class="form-control no-spinner" v-model="formData.file_performance.api_page_size" placeholder="200">
|
||||
<div class="input-group-append">
|
||||
<span class="input-group-text square-append">个</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-6 col-md-6 mb-2">
|
||||
<div class="input-group" title="文件列表在本地缓存的持续时间,过期后将自动清除。设置过短会增加API请求次数,设置过长可能无法及时反映最新变动。建议值:0-300,0表示不缓存">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">文件列表缓存时长</span>
|
||||
</div>
|
||||
<input type="text" class="form-control no-spinner" v-model="formData.file_performance.cache_expire_time" placeholder="30">
|
||||
<div class="input-group-append">
|
||||
<span class="input-group-text square-append">秒</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div v-if="activeTab === 'tasklist'">
|
||||
@ -1000,13 +1034,48 @@
|
||||
|
||||
<div v-if="activeTab === 'filemanager'">
|
||||
<div style="height: 20px;"></div>
|
||||
<!-- 账号选择栏 -->
|
||||
<div class="row file-manager-account-selector">
|
||||
<div class="col-12">
|
||||
<!-- 桌面端账号选择 -->
|
||||
<div class="d-flex align-items-center file-manager-rule-bar">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">夸克账号</span>
|
||||
</div>
|
||||
<div class="position-relative" style="flex: 1;">
|
||||
<select class="form-control task-filter-select file-manager-input file-manager-account-select" v-model="fileManager.selectedAccountIndex" @change="onAccountChange">
|
||||
<option v-for="(account, index) in accountsDetail" :key="index" :value="index">{{ account.display_text }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<button type="button" class="btn btn-outline-primary batch-rename-btn" @click="refreshCurrentFolderCache" title="刷新当前目录缓存">
|
||||
<i class="bi bi-arrow-clockwise"></i>
|
||||
</button>
|
||||
</div>
|
||||
<!-- 移动端账号选择 -->
|
||||
<div class="file-manager-rule-bar-responsive">
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="input-group" style="flex: 1;">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">夸克账号</span>
|
||||
</div>
|
||||
<select class="form-control task-filter-select file-manager-account-select" v-model="fileManager.selectedAccountIndex" @change="onAccountChange">
|
||||
<option v-for="(account, index) in accountsDetail" :key="index" :value="index">{{ account.display_text }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<button type="button" class="btn btn-outline-primary batch-rename-btn" @click="refreshCurrentFolderCache" title="刷新当前目录缓存">
|
||||
<i class="bi bi-arrow-clockwise"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 重命名规则设置栏 -->
|
||||
<div class="row mb-3">
|
||||
<div class="col-12">
|
||||
<!-- 桌面端结构(原有) -->
|
||||
<div class="d-flex align-items-center file-manager-rule-bar">
|
||||
<div class="input-group-prepend">
|
||||
<button class="btn btn-outline-secondary" type="button" @click="switchNamingMode"
|
||||
<button class="btn btn-outline-secondary" type="button" @click="previewAndRename"
|
||||
:title="fileManager.use_sequence_naming ? '预览顺序命名效果' : (fileManager.use_episode_naming ? '预览剧集命名效果' : '预览正则命名效果')">
|
||||
{{ fileManager.use_sequence_naming ? '顺序命名' : (fileManager.use_episode_naming ? '剧集命名' : '正则命名') }}
|
||||
</button>
|
||||
@ -1022,7 +1091,7 @@
|
||||
<input type="checkbox" v-model="fileManager.include_folders"> 含文件夹
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-outline-primary batch-rename-btn" @click="switchNamingMode" title="预览并执行重命名">
|
||||
<button type="button" class="btn btn-outline-primary batch-rename-btn" @click="previewAndRename" title="预览并执行重命名">
|
||||
<i class="bi bi-pencil"></i>
|
||||
</button>
|
||||
</div>
|
||||
@ -1031,7 +1100,7 @@
|
||||
<!-- 第一行:命名按钮+表达式+替换表达式 -->
|
||||
<div class="input-group mb-2">
|
||||
<div class="input-group-prepend">
|
||||
<button class="btn btn-outline-secondary" type="button" @click="switchNamingMode"
|
||||
<button class="btn btn-outline-secondary" type="button" @click="previewAndRename"
|
||||
:title="fileManager.use_sequence_naming ? '预览顺序命名效果' : (fileManager.use_episode_naming ? '预览剧集命名效果' : '预览正则命名效果')">
|
||||
{{ fileManager.use_sequence_naming ? '顺序命名' : (fileManager.use_episode_naming ? '剧集命名' : '正则命名') }}
|
||||
</button>
|
||||
@ -1051,7 +1120,7 @@
|
||||
<span class="input-group-text file-folder-rounded">
|
||||
<input type="checkbox" v-model="fileManager.include_folders"> 含文件夹
|
||||
</span>
|
||||
<button type="button" class="btn btn-outline-primary batch-rename-btn" @click="switchNamingMode" title="预览并执行重命名">
|
||||
<button type="button" class="btn btn-outline-primary batch-rename-btn" @click="previewAndRename" title="预览并执行重命名">
|
||||
<i class="bi bi-pencil"></i>
|
||||
</button>
|
||||
</div>
|
||||
@ -1417,9 +1486,14 @@
|
||||
delete_task: "always",
|
||||
refresh_plex: "always",
|
||||
refresh_alist: "always"
|
||||
},
|
||||
file_performance: {
|
||||
api_page_size: 200,
|
||||
cache_expire_time: 30
|
||||
}
|
||||
},
|
||||
userInfoList: [], // 用户信息列表
|
||||
accountsDetail: [], // 账号详细信息列表
|
||||
newTask: {
|
||||
taskname: "",
|
||||
shareurl: "",
|
||||
@ -1497,8 +1571,13 @@
|
||||
lastSelectedRecordIndex: -1, // 记录最后选择的记录索引,用于Shift选择
|
||||
fileManager: {
|
||||
loading: false,
|
||||
// 当前文件夹ID - 根据localStorage决定初始目录
|
||||
currentFolder: localStorage.getItem('quarkAutoSave_fileManagerLastFolder') || 'root',
|
||||
// 当前文件夹ID - 根据localStorage和账号索引决定初始目录
|
||||
currentFolder: (() => {
|
||||
const selectedAccountIndex = parseInt(localStorage.getItem('quarkAutoSave_selectedAccountIndex') || '0');
|
||||
return localStorage.getItem(`quarkAutoSave_fileManagerLastFolder_${selectedAccountIndex}`) || 'root';
|
||||
})(),
|
||||
// 选中的账号索引 - 从localStorage读取,默认为0
|
||||
selectedAccountIndex: parseInt(localStorage.getItem('quarkAutoSave_selectedAccountIndex') || '0'),
|
||||
// 面包屑导航路径
|
||||
paths: [],
|
||||
// 文件列表
|
||||
@ -1691,6 +1770,7 @@
|
||||
activeTab(newValue, oldValue) {
|
||||
// 如果切换到文件整理页面,则加载文件列表
|
||||
if (newValue === 'filemanager') {
|
||||
this.fetchAccountsDetail();
|
||||
this.loadFileListWithoutLoading(this.fileManager.currentFolder);
|
||||
}
|
||||
},
|
||||
@ -1705,7 +1785,11 @@
|
||||
this.fetchData();
|
||||
this.checkNewVersion();
|
||||
this.fetchUserInfo(); // 获取用户信息
|
||||
|
||||
this.fetchAccountsDetail(); // 获取账号详细信息
|
||||
|
||||
// 迁移旧的localStorage数据到新格式(为每个账号单独存储目录)
|
||||
this.migrateFileManagerFolderData();
|
||||
|
||||
// 添加点击事件监听
|
||||
document.addEventListener('click', this.handleOutsideClick);
|
||||
document.addEventListener('click', this.handleModalOutsideClick);
|
||||
@ -1822,6 +1906,7 @@
|
||||
|
||||
// 如果当前标签是文件整理,则加载文件列表
|
||||
if (this.activeTab === 'filemanager') {
|
||||
this.fetchAccountsDetail();
|
||||
this.loadFileListWithoutLoading(this.fileManager.currentFolder);
|
||||
}
|
||||
|
||||
@ -1874,6 +1959,92 @@
|
||||
console.error('获取用户信息失败:', error);
|
||||
});
|
||||
},
|
||||
|
||||
fetchAccountsDetail() {
|
||||
// 获取所有账号的详细信息,包括空间使用情况
|
||||
axios.get('/get_accounts_detail')
|
||||
.then(response => {
|
||||
if (response.data.success) {
|
||||
this.accountsDetail = response.data.data;
|
||||
// 验证当前选中的账号索引是否有效
|
||||
if (this.fileManager.selectedAccountIndex >= this.accountsDetail.length) {
|
||||
this.fileManager.selectedAccountIndex = 0;
|
||||
localStorage.setItem('quarkAutoSave_selectedAccountIndex', '0');
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('获取账号详细信息失败:', error);
|
||||
});
|
||||
},
|
||||
|
||||
// 迁移旧的localStorage数据到新格式(为每个账号单独存储目录)
|
||||
migrateFileManagerFolderData() {
|
||||
const oldFolderKey = 'quarkAutoSave_fileManagerLastFolder';
|
||||
const oldFolderValue = localStorage.getItem(oldFolderKey);
|
||||
|
||||
if (oldFolderValue) {
|
||||
// 如果存在旧数据,将其迁移到当前选中账号的新格式
|
||||
const currentAccountIndex = this.fileManager.selectedAccountIndex;
|
||||
const newFolderKey = `quarkAutoSave_fileManagerLastFolder_${currentAccountIndex}`;
|
||||
|
||||
// 只有当新格式的数据不存在时才进行迁移
|
||||
if (!localStorage.getItem(newFolderKey)) {
|
||||
localStorage.setItem(newFolderKey, oldFolderValue);
|
||||
console.log(`已将文件管理器目录数据迁移到账号 ${currentAccountIndex}: ${oldFolderValue}`);
|
||||
}
|
||||
|
||||
// 删除旧的数据
|
||||
localStorage.removeItem(oldFolderKey);
|
||||
}
|
||||
},
|
||||
|
||||
onAccountChange() {
|
||||
// 保存选中的账号索引到localStorage
|
||||
localStorage.setItem('quarkAutoSave_selectedAccountIndex', this.fileManager.selectedAccountIndex.toString());
|
||||
|
||||
// 获取新账号最后一次访问的目录
|
||||
const newAccountLastFolder = localStorage.getItem(`quarkAutoSave_fileManagerLastFolder_${this.fileManager.selectedAccountIndex}`) || 'root';
|
||||
|
||||
// 重置页码并切换到新账号的最后访问目录
|
||||
this.fileManager.currentPage = 1;
|
||||
this.loadFileListWithFallback(newAccountLastFolder);
|
||||
},
|
||||
|
||||
refreshCurrentFolderCache() {
|
||||
// 刷新当前目录的缓存,强制重新请求最新的文件列表
|
||||
// 调用后端接口,添加强制刷新参数
|
||||
const params = {
|
||||
folder_id: this.fileManager.currentFolder || 'root',
|
||||
sort_by: this.fileManager.sortBy,
|
||||
order: this.fileManager.sortOrder,
|
||||
page_size: this.fileManager.pageSize,
|
||||
page: this.fileManager.currentPage,
|
||||
account_index: this.fileManager.selectedAccountIndex,
|
||||
force_refresh: true // 强制刷新参数
|
||||
};
|
||||
|
||||
axios.get('/file_list', { params })
|
||||
.then(response => {
|
||||
if (response.data.success) {
|
||||
this.fileManager.fileList = response.data.data.list;
|
||||
this.fileManager.total = response.data.data.total;
|
||||
this.fileManager.totalPages = Math.ceil(response.data.data.total / this.fileManager.pageSize);
|
||||
this.fileManager.paths = response.data.data.paths || [];
|
||||
this.fileManager.gotoPage = this.fileManager.currentPage;
|
||||
// 移除成功通知
|
||||
} else {
|
||||
this.showToast('刷新失败:' + response.data.message);
|
||||
}
|
||||
|
||||
// 检测当前的命名模式
|
||||
this.detectFileManagerNamingMode();
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('刷新缓存失败:', error);
|
||||
this.showToast('刷新缓存失败,请稍后重试');
|
||||
});
|
||||
},
|
||||
// 添加一个检查分享链接状态的方法
|
||||
checkShareUrlStatus() {
|
||||
// 只在任务列表页面检查
|
||||
@ -1966,6 +2137,8 @@
|
||||
if (savedFileManagerPageSize) {
|
||||
this.fileManager.pageSize = savedFileManagerPageSize === 'all' ? 99999 : parseInt(savedFileManagerPageSize);
|
||||
}
|
||||
// 获取账号详细信息
|
||||
this.fetchAccountsDetail();
|
||||
this.loadFileListWithoutLoading(this.fileManager.currentFolder);
|
||||
}
|
||||
},
|
||||
@ -2042,6 +2215,24 @@
|
||||
refresh_alist: "always"
|
||||
};
|
||||
}
|
||||
// 确保文件整理性能配置存在
|
||||
if (!config_data.file_performance) {
|
||||
config_data.file_performance = {
|
||||
api_page_size: 200,
|
||||
cache_expire_time: 30
|
||||
};
|
||||
} else {
|
||||
// 确保必要的字段存在,移除废弃的字段
|
||||
if (!config_data.file_performance.api_page_size) {
|
||||
config_data.file_performance.api_page_size = 200;
|
||||
}
|
||||
if (!config_data.file_performance.cache_expire_time) {
|
||||
config_data.file_performance.cache_expire_time = 30;
|
||||
}
|
||||
// 移除废弃的字段
|
||||
delete config_data.file_performance.large_page_size;
|
||||
delete config_data.file_performance.cache_cleanup_interval;
|
||||
}
|
||||
this.formData = config_data;
|
||||
setTimeout(() => {
|
||||
this.configModified = false;
|
||||
@ -2869,6 +3060,22 @@
|
||||
params = { fid: params }
|
||||
}
|
||||
this.modalLoading = true;
|
||||
|
||||
// 根据模态框类型决定使用哪个账号
|
||||
// 任务配置相关的模态框始终使用主账号(索引0)
|
||||
// 文件整理页面的预览模态框使用选中的账号
|
||||
const modalType = document.getElementById('fileSelectModal').getAttribute('data-modal-type');
|
||||
const accountIndex = (modalType === 'preview-filemanager') ? this.fileManager.selectedAccountIndex : 0;
|
||||
|
||||
// 添加账号索引参数
|
||||
if (typeof params === 'object' && params !== null) {
|
||||
params.account_index = accountIndex;
|
||||
} else {
|
||||
params = {
|
||||
fid: params,
|
||||
account_index: accountIndex
|
||||
};
|
||||
}
|
||||
axios.get('/get_savepath_detail', {
|
||||
params: params
|
||||
}).then(response => {
|
||||
@ -4090,7 +4297,7 @@
|
||||
// 查找对应的文件对象,获取文件名
|
||||
const fileObj = this.fileSelect.fileList.find(file => file.fid === fid);
|
||||
const fileName = fileObj ? fileObj.file_name : '';
|
||||
|
||||
|
||||
return axios.post('/delete_file', { fid: fid, file_name: fileName, delete_records: deleteRecords, save_path: save_path })
|
||||
.then(response => {
|
||||
return { fid: fid, success: response.data.code === 0, deleted_records: response.data.deleted_records || 0 };
|
||||
@ -4272,8 +4479,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
// 保存当前目录到localStorage
|
||||
localStorage.setItem('quarkAutoSave_fileManagerLastFolder', folderId);
|
||||
// 保存当前目录到localStorage(为每个账号单独保存)
|
||||
localStorage.setItem(`quarkAutoSave_fileManagerLastFolder_${this.fileManager.selectedAccountIndex}`, folderId);
|
||||
|
||||
// 重置页码并加载新文件夹内容,不显示加载动画
|
||||
this.fileManager.currentPage = 1;
|
||||
@ -4445,15 +4652,18 @@
|
||||
files: [{
|
||||
file_id: file.fid,
|
||||
new_name: newName
|
||||
}]
|
||||
}],
|
||||
// 添加账号索引参数,使用文件整理页面选中的账号
|
||||
account_index: this.fileManager.selectedAccountIndex
|
||||
})
|
||||
.then(response => {
|
||||
if (response.data.success) {
|
||||
this.showToast('重命名成功');
|
||||
// 更新本地文件列表中的文件名
|
||||
const index = this.fileManager.fileList.findIndex(f => f.fid === file.fid);
|
||||
if (index !== -1) {
|
||||
this.fileManager.fileList[index].file_name = newName;
|
||||
// 刷新文件列表以确保缓存同步
|
||||
this.refreshCurrentFolderCache();
|
||||
// 如果命名预览模态框是打开的,也要刷新它
|
||||
if ($('#fileSelectModal').hasClass('show')) {
|
||||
this.showFileManagerNamingPreview(this.fileManager.currentFolder);
|
||||
}
|
||||
} else {
|
||||
alert(response.data.message || '重命名失败');
|
||||
@ -4465,13 +4675,13 @@
|
||||
});
|
||||
}
|
||||
},
|
||||
switchNamingMode() {
|
||||
// 不再切换命名模式,而是显示命名预览模态框
|
||||
previewAndRename() {
|
||||
// 显示文件整理页面的命名预览模态框
|
||||
this.showFileManagerNamingPreview();
|
||||
},
|
||||
|
||||
showFileManagerNamingPreview(folderId) {
|
||||
// 使用文件选择模态框,但设置为预览模式
|
||||
// 显示文件整理页面的命名预览模态框(区别于任务配置的命名预览)
|
||||
this.fileSelect.selectShare = false;
|
||||
this.fileSelect.selectDir = true;
|
||||
this.fileSelect.previewRegex = true;
|
||||
@ -4510,7 +4720,9 @@
|
||||
include_folders: this.fileManager.include_folders,
|
||||
filterwords: this.fileManager.filterwords,
|
||||
// 添加页面大小参数,获取所有文件
|
||||
page_size: 99999
|
||||
page_size: 99999,
|
||||
// 添加账号索引参数,使用文件整理页面选中的账号
|
||||
account_index: this.fileManager.selectedAccountIndex
|
||||
}
|
||||
})
|
||||
.then(response => {
|
||||
@ -4528,7 +4740,11 @@
|
||||
page_size: 99999,
|
||||
page: 1,
|
||||
// 同时添加文件夹包含参数
|
||||
include_folders: this.fileManager.include_folders
|
||||
include_folders: this.fileManager.include_folders,
|
||||
// 添加账号索引参数,使用文件整理页面选中的账号
|
||||
account_index: this.fileManager.selectedAccountIndex,
|
||||
// 强制刷新缓存
|
||||
force_refresh: true
|
||||
}
|
||||
})
|
||||
.then(listResponse => {
|
||||
@ -4599,85 +4815,8 @@
|
||||
this.modalLoading = false;
|
||||
});
|
||||
},
|
||||
showBatchRenameModal() {
|
||||
this.batchRename.loading = true;
|
||||
this.batchRename.results = [];
|
||||
this.batchRename.errors = [];
|
||||
|
||||
// 设置命名模式
|
||||
if (this.fileManager.use_sequence_naming) {
|
||||
this.batchRename.namingMode = 'sequence';
|
||||
} else if (this.fileManager.use_episode_naming) {
|
||||
this.batchRename.namingMode = 'episode';
|
||||
} else {
|
||||
this.batchRename.namingMode = 'regex';
|
||||
}
|
||||
|
||||
// 调用后端API获取预览结果
|
||||
const params = {
|
||||
folder_id: this.fileManager.currentFolder,
|
||||
pattern: this.fileManager.pattern,
|
||||
replace: this.fileManager.replace,
|
||||
naming_mode: this.batchRename.namingMode,
|
||||
include_folders: this.fileManager.include_folders,
|
||||
filterwords: this.fileManager.filterwords
|
||||
};
|
||||
|
||||
axios.get('/preview_rename', { params })
|
||||
.then(response => {
|
||||
if (response.data.success) {
|
||||
this.batchRename.files = response.data.data.map(file => ({
|
||||
oldName: file.original_name,
|
||||
newName: file.new_name,
|
||||
fileId: file.file_id
|
||||
}));
|
||||
} else {
|
||||
alert(response.data.message || '预览重命名失败');
|
||||
}
|
||||
this.batchRename.loading = false;
|
||||
$('#batchRenameModal').modal('show');
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('预览重命名失败:', error);
|
||||
alert('预览重命名失败,请检查命名规则是否正确');
|
||||
this.batchRename.loading = false;
|
||||
});
|
||||
},
|
||||
confirmBatchRename() {
|
||||
this.batchRename.loading = true;
|
||||
|
||||
// 发送批量重命名请求
|
||||
const params = {
|
||||
folder_id: this.fileManager.currentFolder,
|
||||
pattern: this.fileManager.pattern,
|
||||
replace: this.fileManager.replace,
|
||||
naming_mode: this.batchRename.namingMode,
|
||||
include_folders: this.fileManager.include_folders,
|
||||
filterwords: this.fileManager.filterwords,
|
||||
files: this.batchRename.files.filter(file => file.newName).map(file => ({
|
||||
file_id: file.fileId,
|
||||
new_name: file.newName
|
||||
}))
|
||||
};
|
||||
|
||||
axios.post('/batch_rename', params)
|
||||
.then(response => {
|
||||
if (response.data.success) {
|
||||
this.showToast('批量重命名成功');
|
||||
$('#batchRenameModal').modal('hide');
|
||||
// 刷新文件列表
|
||||
this.loadFileList(this.fileManager.currentFolder);
|
||||
} else {
|
||||
alert(response.data.message || '批量重命名失败');
|
||||
}
|
||||
this.batchRename.loading = false;
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('批量重命名失败:', error);
|
||||
alert('批量重命名失败');
|
||||
this.batchRename.loading = false;
|
||||
});
|
||||
},
|
||||
// showBatchRenameModal方法已删除,功能已整合到showFileManagerNamingPreview中
|
||||
// confirmBatchRename方法已删除,功能已整合到applyPreviewRename中
|
||||
deleteFile(file) {
|
||||
if (confirm(`确定要删除${file.dir ? '文件夹' : '文件'} "${file.file_name}" 吗?`)) {
|
||||
// 获取当前路径作为save_path参数
|
||||
@ -4686,21 +4825,22 @@
|
||||
save_path = this.fileManager.paths.map(item => item.name).join("/");
|
||||
}
|
||||
|
||||
axios.post('/delete_file', {
|
||||
fid: file.fid,
|
||||
file_name: file.file_name,
|
||||
delete_records: false,
|
||||
save_path: save_path
|
||||
axios.post('/delete_file', {
|
||||
fid: file.fid,
|
||||
file_name: file.file_name,
|
||||
delete_records: false,
|
||||
save_path: save_path,
|
||||
account_index: this.fileManager.selectedAccountIndex
|
||||
})
|
||||
.then(response => {
|
||||
if (response.data.code === 0) {
|
||||
this.showToast('成功删除 1 个项目');
|
||||
// 从列表中移除已删除的文件
|
||||
this.fileManager.fileList = this.fileManager.fileList.filter(f => f.fid !== file.fid);
|
||||
// 更新总数
|
||||
this.fileManager.total -= 1;
|
||||
// 从选中列表中移除
|
||||
this.fileManager.selectedFiles = this.fileManager.selectedFiles.filter(id => id !== file.fid);
|
||||
// 刷新文件列表以确保缓存同步
|
||||
this.refreshCurrentFolderCache();
|
||||
// 如果命名预览模态框是打开的,也要刷新它
|
||||
if ($('#fileSelectModal').hasClass('show')) {
|
||||
this.showFileManagerNamingPreview(this.fileManager.currentFolder);
|
||||
}
|
||||
} else {
|
||||
alert(response.data.message || '删除失败');
|
||||
}
|
||||
@ -4714,13 +4854,14 @@
|
||||
loadFileList(folderId) {
|
||||
this.fileManager.loading = true;
|
||||
this.fileManager.currentFolder = folderId || 'root';
|
||||
|
||||
|
||||
const params = {
|
||||
folder_id: folderId || 'root',
|
||||
sort_by: this.fileManager.sortBy,
|
||||
order: this.fileManager.sortOrder,
|
||||
page_size: this.fileManager.pageSize,
|
||||
page: this.fileManager.currentPage
|
||||
page: this.fileManager.currentPage,
|
||||
account_index: this.fileManager.selectedAccountIndex
|
||||
};
|
||||
|
||||
axios.get('/file_list', { params })
|
||||
@ -4795,8 +4936,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
// 保存当前目录到localStorage
|
||||
localStorage.setItem('quarkAutoSave_fileManagerLastFolder', folderId);
|
||||
// 保存当前目录到localStorage(为每个账号单独保存)
|
||||
localStorage.setItem(`quarkAutoSave_fileManagerLastFolder_${this.fileManager.selectedAccountIndex}`, folderId);
|
||||
|
||||
// 重置页码并加载新文件夹内容,不显示加载动画
|
||||
this.fileManager.currentPage = 1;
|
||||
@ -4806,22 +4947,23 @@
|
||||
// 添加一个不显示加载动画的文件列表加载方法
|
||||
loadFileListWithoutLoading(folderId) {
|
||||
this.fileManager.currentFolder = folderId || 'root';
|
||||
|
||||
|
||||
// 从localStorage读取分页大小设置(仅在初始化时使用)
|
||||
// 在changeFileManagerPageSize方法中,我们已经确保了正确的执行顺序
|
||||
const savedFileManagerPageSize = localStorage.getItem('quarkAutoSave_fileManagerPageSize');
|
||||
if (savedFileManagerPageSize && this.fileManager.pageSize === 15) { // 只在默认值时读取
|
||||
this.fileManager.pageSize = savedFileManagerPageSize === 'all' ? 99999 : parseInt(savedFileManagerPageSize);
|
||||
}
|
||||
|
||||
|
||||
const params = {
|
||||
folder_id: folderId || 'root',
|
||||
sort_by: this.fileManager.sortBy,
|
||||
order: this.fileManager.sortOrder,
|
||||
page_size: this.fileManager.pageSize,
|
||||
page: this.fileManager.currentPage
|
||||
page: this.fileManager.currentPage,
|
||||
account_index: this.fileManager.selectedAccountIndex
|
||||
};
|
||||
|
||||
|
||||
axios.get('/file_list', { params })
|
||||
.then(response => {
|
||||
if (response.data.success) {
|
||||
@ -4833,7 +4975,7 @@
|
||||
} else {
|
||||
console.error('获取文件列表失败:', response.data.message);
|
||||
}
|
||||
|
||||
|
||||
// 检测当前的命名模式
|
||||
this.detectFileManagerNamingMode();
|
||||
})
|
||||
@ -4841,6 +4983,55 @@
|
||||
console.error('获取文件列表失败:', error);
|
||||
});
|
||||
},
|
||||
|
||||
// 添加带fallback机制的文件列表加载方法,用于账号切换
|
||||
loadFileListWithFallback(folderId) {
|
||||
this.fileManager.currentFolder = folderId || 'root';
|
||||
|
||||
const params = {
|
||||
folder_id: folderId || 'root',
|
||||
sort_by: this.fileManager.sortBy,
|
||||
order: this.fileManager.sortOrder,
|
||||
page_size: this.fileManager.pageSize,
|
||||
page: this.fileManager.currentPage,
|
||||
account_index: this.fileManager.selectedAccountIndex
|
||||
};
|
||||
|
||||
axios.get('/file_list', { params })
|
||||
.then(response => {
|
||||
if (response.data.success) {
|
||||
this.fileManager.fileList = response.data.data.list;
|
||||
this.fileManager.total = response.data.data.total;
|
||||
this.fileManager.totalPages = Math.ceil(response.data.data.total / this.fileManager.pageSize);
|
||||
this.fileManager.paths = response.data.data.paths || [];
|
||||
this.fileManager.gotoPage = this.fileManager.currentPage;
|
||||
|
||||
// 检测当前的命名模式
|
||||
this.detectFileManagerNamingMode();
|
||||
} else {
|
||||
// 如果获取文件列表失败,可能是目录在新账号中不存在
|
||||
console.warn('当前目录在新账号中不存在,自动切换到根目录:', response.data.message);
|
||||
this.fallbackToRoot();
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('获取文件列表失败,自动切换到根目录:', error);
|
||||
this.fallbackToRoot();
|
||||
});
|
||||
},
|
||||
|
||||
// fallback到根目录的方法
|
||||
fallbackToRoot() {
|
||||
this.fileManager.currentFolder = 'root';
|
||||
this.fileManager.paths = [];
|
||||
this.fileManager.currentPage = 1;
|
||||
|
||||
// 更新localStorage中的当前目录(为每个账号单独保存)
|
||||
localStorage.setItem(`quarkAutoSave_fileManagerLastFolder_${this.fileManager.selectedAccountIndex}`, 'root');
|
||||
|
||||
// 重新加载根目录
|
||||
this.loadFileListWithoutLoading('root');
|
||||
},
|
||||
getVisibleFolderPageNumbers() {
|
||||
const current = parseInt(this.fileManager.currentPage) || 1;
|
||||
const total = parseInt(this.fileManager.totalPages) || 1;
|
||||
@ -4876,47 +5067,7 @@
|
||||
|
||||
return range;
|
||||
},
|
||||
cancelBatchRename() {
|
||||
this.batchRename.loading = false;
|
||||
this.batchRename.results = [];
|
||||
this.batchRename.errors = [];
|
||||
$('#batchRenameModal').modal('hide');
|
||||
},
|
||||
applyBatchRename() {
|
||||
this.batchRename.loading = true;
|
||||
|
||||
// 发送批量重命名请求
|
||||
const params = {
|
||||
folder_id: this.fileManager.currentFolder,
|
||||
pattern: this.fileManager.pattern,
|
||||
replace: this.fileManager.replace,
|
||||
naming_mode: this.batchRename.namingMode,
|
||||
include_folders: this.fileManager.include_folders,
|
||||
filterwords: this.fileManager.filterwords,
|
||||
files: this.batchRename.files.filter(file => file.newName).map(file => ({
|
||||
file_id: file.fileId,
|
||||
new_name: file.newName
|
||||
}))
|
||||
};
|
||||
|
||||
axios.post('/batch_rename', params)
|
||||
.then(response => {
|
||||
if (response.data.success) {
|
||||
this.showToast('批量重命名成功');
|
||||
$('#batchRenameModal').modal('hide');
|
||||
// 刷新文件列表
|
||||
this.loadFileList(this.fileManager.currentFolder);
|
||||
} else {
|
||||
alert(response.data.message || '批量重命名失败');
|
||||
}
|
||||
this.batchRename.loading = false;
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('批量重命名失败:', error);
|
||||
alert('批量重命名失败');
|
||||
this.batchRename.loading = false;
|
||||
});
|
||||
},
|
||||
// cancelBatchRename和applyBatchRename方法已删除,功能已整合到fileSelectModal中
|
||||
handleFileManagerOutsideClick(event) {
|
||||
// 如果当前不是文件整理页面或者没有选中的文件,则不处理
|
||||
if (this.activeTab !== 'filemanager' || this.fileManager.selectedFiles.length === 0) {
|
||||
@ -4969,11 +5120,12 @@
|
||||
const fileObj = this.fileManager.fileList.find(file => file.fid === fid);
|
||||
const fileName = fileObj ? fileObj.file_name : '';
|
||||
|
||||
return axios.post('/delete_file', {
|
||||
fid: fid,
|
||||
file_name: fileName,
|
||||
delete_records: deleteRecords,
|
||||
save_path: save_path
|
||||
return axios.post('/delete_file', {
|
||||
fid: fid,
|
||||
file_name: fileName,
|
||||
delete_records: deleteRecords,
|
||||
save_path: save_path,
|
||||
account_index: this.fileManager.selectedAccountIndex
|
||||
})
|
||||
.then(response => {
|
||||
return { fid: fid, success: response.data.code === 0, deleted_records: response.data.deleted_records || 0 };
|
||||
@ -4990,22 +5142,21 @@
|
||||
const successCount = results.filter(r => r.success).length;
|
||||
const failCount = results.length - successCount;
|
||||
|
||||
// 从文件列表中移除成功删除的文件
|
||||
const successfullyDeletedFids = results.filter(r => r.success).map(r => r.fid);
|
||||
this.fileManager.fileList = this.fileManager.fileList.filter(item => !successfullyDeletedFids.includes(item.fid));
|
||||
|
||||
// 更新总数
|
||||
this.fileManager.total -= successCount;
|
||||
|
||||
// 清空选中文件列表
|
||||
this.fileManager.selectedFiles = [];
|
||||
|
||||
// 显示结果
|
||||
if (failCount > 0) {
|
||||
alert(`成功删除 ${successCount} 个项目,${failCount} 个项目删除失败`);
|
||||
} else {
|
||||
this.showToast(`成功删除 ${successCount} 个项目`);
|
||||
}
|
||||
|
||||
// 如果有成功删除的文件,刷新文件列表以确保缓存同步
|
||||
if (successCount > 0) {
|
||||
this.refreshCurrentFolderCache();
|
||||
// 如果命名预览模态框是打开的,也要刷新它
|
||||
if ($('#fileSelectModal').hasClass('show')) {
|
||||
this.showFileManagerNamingPreview(this.fileManager.currentFolder);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
@ -5056,10 +5207,16 @@
|
||||
old_name: f.file_name // 传递原文件名,便于撤销
|
||||
}));
|
||||
const save_path = this.fileManager.currentFolder;
|
||||
const response = await axios.post('/batch_rename', { files, save_path });
|
||||
const response = await axios.post('/batch_rename', {
|
||||
files,
|
||||
save_path,
|
||||
// 添加账号索引参数,使用文件整理页面选中的账号
|
||||
account_index: this.fileManager.selectedAccountIndex
|
||||
});
|
||||
if (response.data.success) {
|
||||
this.showToast(`成功重命名 ${response.data.success_count || files.length} 个项目`);
|
||||
this.loadFileListWithoutLoading(this.fileManager.currentFolder);
|
||||
// 强制刷新文件列表缓存
|
||||
this.refreshCurrentFolderCache();
|
||||
$('#fileSelectModal').modal('hide');
|
||||
this.fileSelect.canUndoRename = true; // 重命名后立即可撤销
|
||||
} else {
|
||||
@ -5075,11 +5232,16 @@
|
||||
this.modalLoading = true;
|
||||
try {
|
||||
const save_path = this.fileManager.currentFolder;
|
||||
const response = await axios.post('/undo_rename', { save_path });
|
||||
const response = await axios.post('/undo_rename', {
|
||||
save_path,
|
||||
// 添加账号索引参数,使用文件整理页面选中的账号
|
||||
account_index: this.fileManager.selectedAccountIndex
|
||||
});
|
||||
if (response.data.success) {
|
||||
this.showToast(`成功撤销 ${response.data.success_count} 个项目重命名`);
|
||||
this.showFileManagerNamingPreview(save_path);
|
||||
this.loadFileListWithoutLoading(this.fileManager.currentFolder);
|
||||
// 强制刷新文件列表缓存
|
||||
this.refreshCurrentFolderCache();
|
||||
// 撤销后如无可撤销项再设为false(由showFileManagerNamingPreview刷新)
|
||||
} else {
|
||||
this.showToast(response.data.message || '撤销失败');
|
||||
|
||||
@ -932,6 +932,9 @@ class Quark:
|
||||
def ls_dir(self, pdir_fid, **kwargs):
|
||||
file_list = []
|
||||
page = 1
|
||||
# 优化:增加每页大小,减少API调用次数
|
||||
page_size = kwargs.get("page_size", 200) # 从50增加到200
|
||||
|
||||
while True:
|
||||
url = f"{self.BASE_URL}/1/clouddrive/file/sort"
|
||||
querystring = {
|
||||
@ -940,7 +943,7 @@ class Quark:
|
||||
"uc_param_str": "",
|
||||
"pdir_fid": pdir_fid,
|
||||
"_page": page,
|
||||
"_size": "50",
|
||||
"_size": str(page_size),
|
||||
"_fetch_total": "1",
|
||||
"_fetch_sub_dirs": "0",
|
||||
"_sort": "file_type:asc,updated_at:desc",
|
||||
|
||||
Loading…
Reference in New Issue
Block a user