为数据库增加保存路径数据的存储功能,为选择保存到的文件夹页面增加删除文件和记录功能

This commit is contained in:
x1ao4 2025-05-25 21:14:04 +08:00
parent 831a5cfa24
commit 5a5fa4cdeb
5 changed files with 218 additions and 38 deletions

View File

@ -650,6 +650,55 @@ def delete_file():
account = Quark(config_data["cookie"][0], 0)
if fid := request.json.get("fid"):
response = account.delete([fid])
# 处理delete_records参数
if request.json.get("delete_records") and response.get("code") == 0:
try:
# 初始化数据库
db = RecordDB()
# 获取save_path参数
save_path = request.json.get("save_path", "")
# 如果没有提供save_path则不删除任何记录
if not save_path:
response["deleted_records"] = 0
# logging.info(f">>> 删除文件 {fid} 但未提供save_path不删除任何记录")
return jsonify(response)
# 查询与该文件ID和save_path相关的所有记录
cursor = db.conn.cursor()
# 使用file_id和save_path进行精确匹配
cursor.execute("SELECT id FROM transfer_records WHERE file_id = ? AND save_path = ?", (fid, save_path))
record_ids = [row[0] for row in cursor.fetchall()]
# 如果没有找到匹配的file_id记录尝试通过文件名查找
if not record_ids:
# 获取文件名(如果有的话)
file_name = request.json.get("file_name", "")
if file_name:
# 使用文件名和save_path进行精确匹配
cursor.execute("""
SELECT id FROM transfer_records
WHERE (original_name = ? OR renamed_to = ?)
AND save_path = ?
""", (file_name, file_name, save_path))
record_ids = [row[0] for row in cursor.fetchall()]
# 删除找到的所有记录
deleted_count = 0
for record_id in record_ids:
deleted_count += db.delete_record(record_id)
# 添加删除记录的信息到响应中
response["deleted_records"] = deleted_count
# logging.info(f">>> 删除文件 {fid} 同时删除了 {deleted_count} 条相关记录")
except Exception as e:
logging.error(f">>> 删除记录时出错: {str(e)}")
# 不影响主流程,即使删除记录失败也返回文件删除成功
else:
response = {"success": False, "message": "缺失必要字段: fid"}
return jsonify(response)

View File

@ -31,9 +31,17 @@ class RecordDB:
resolution TEXT,
modify_date INTEGER NOT NULL,
file_id TEXT,
file_type TEXT
file_type TEXT,
save_path TEXT
)
''')
# 检查save_path字段是否存在如果不存在则添加
cursor.execute("PRAGMA table_info(transfer_records)")
columns = [column[1] for column in cursor.fetchall()]
if 'save_path' not in columns:
cursor.execute('ALTER TABLE transfer_records ADD COLUMN save_path TEXT')
self.conn.commit()
def close(self):
@ -41,19 +49,19 @@ class RecordDB:
self.conn.close()
def add_record(self, task_name, original_name, renamed_to, file_size, modify_date,
duration="", resolution="", file_id="", file_type=""):
duration="", resolution="", file_id="", file_type="", save_path=""):
"""添加一条转存记录"""
cursor = self.conn.cursor()
cursor.execute(
"INSERT INTO transfer_records (transfer_time, task_name, original_name, renamed_to, file_size, "
"duration, resolution, modify_date, file_id, file_type) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
"duration, resolution, modify_date, file_id, file_type, save_path) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
(int(time.time()), task_name, original_name, renamed_to, file_size,
duration, resolution, modify_date, file_id, file_type)
duration, resolution, modify_date, file_id, file_type, save_path)
)
self.conn.commit()
return cursor.lastrowid
def update_renamed_to(self, file_id, original_name, renamed_to, task_name=""):
def update_renamed_to(self, file_id, original_name, renamed_to, task_name="", save_path=""):
"""更新最近一条记录的renamed_to字段
Args:
@ -61,6 +69,7 @@ class RecordDB:
original_name: 原文件名
renamed_to: 重命名后的文件名
task_name: 任务名称可选项如提供则作为附加筛选条件
save_path: 保存路径可选项如提供则同时更新保存路径
Returns:
更新的记录数量
@ -97,11 +106,17 @@ class RecordDB:
if result:
record_id = result[0]
# 更新记录
cursor.execute(
"UPDATE transfer_records SET renamed_to = ? WHERE id = ?",
(renamed_to, record_id)
)
# 根据是否提供save_path决定更新哪些字段
if save_path:
cursor.execute(
"UPDATE transfer_records SET renamed_to = ?, save_path = ? WHERE id = ?",
(renamed_to, save_path, record_id)
)
else:
cursor.execute(
"UPDATE transfer_records SET renamed_to = ? WHERE id = ?",
(renamed_to, record_id)
)
self.conn.commit()
return cursor.rowcount
@ -124,7 +139,7 @@ class RecordDB:
# 构建SQL查询
valid_columns = ["transfer_time", "task_name", "original_name", "renamed_to",
"file_size", "duration", "resolution", "modify_date"]
"file_size", "duration", "resolution", "modify_date", "save_path"]
if sort_by not in valid_columns:
sort_by = "transfer_time"
@ -140,7 +155,8 @@ class RecordDB:
params.append(task_name_filter)
if keyword_filter:
where_clauses.append("task_name LIKE ?")
where_clauses.append("(task_name LIKE ? OR original_name LIKE ?)")
params.append(f"%{keyword_filter}%")
params.append(f"%{keyword_filter}%")
where_clause = " AND ".join(where_clauses)
@ -192,4 +208,33 @@ class RecordDB:
cursor = self.conn.cursor()
cursor.execute("DELETE FROM transfer_records WHERE id = ?", (record_id,))
self.conn.commit()
return cursor.rowcount
return cursor.rowcount
def get_records_by_save_path(self, save_path, include_subpaths=False):
"""根据保存路径查询记录
Args:
save_path: 要查询的保存路径
include_subpaths: 是否包含子路径下的文件
Returns:
匹配的记录列表
"""
cursor = self.conn.cursor()
if include_subpaths:
# 如果包含子路径使用LIKE查询
query = "SELECT * FROM transfer_records WHERE save_path LIKE ? ORDER BY transfer_time DESC"
cursor.execute(query, [f"{save_path}%"])
else:
# 精确匹配路径
query = "SELECT * FROM transfer_records WHERE save_path = ? ORDER BY transfer_time DESC"
cursor.execute(query, [save_path])
records = cursor.fetchall()
# 将结果转换为字典列表
if records:
columns = [col[0] for col in cursor.description]
return [dict(zip(columns, row)) for row in records]
return []

View File

@ -1633,10 +1633,11 @@ button.close:focus,
/* 操作列 - 固定宽度 */
#fileSelectModal .table .col-action {
width: 53px;
min-width: 53px;
max-width: 53px;
text-align: center;
width: 188px;
min-width: 188px;
max-width: 188px;
text-align: left;
padding-left: 12px !important;
}
/* 确保单元格内容溢出时正确显示 */
@ -3576,11 +3577,11 @@ input::-moz-list-button {
/* 针对选择保存到的文件夹模式 - 带操作列的表格 */
#fileSelectModal[data-modal-type="target"] .breadcrumb {
min-width: 513px; /* 4列表格总宽度: 230px + 90px + 140px + 53px */
min-width: 648px; /* 4列表格总宽度: 230px + 90px + 140px + 188px */
}
#fileSelectModal[data-modal-type="target"] .table {
width: 513px;
width: 648px;
}
/* 针对命名预览模式 - 2列表格 */

View File

@ -953,7 +953,8 @@
<td class="col-size" v-else style="vertical-align: top;">{{file.size | size}}</td>
<td class="col-date" style="vertical-align: top;">{{file.updated_at | ts2date}}</td>
<td class="col-action" v-if="!fileSelect.selectShare" style="vertical-align: top;">
<a @click.stop.prevent="deleteSelectedFiles(file.fid, file.file_name, file.dir)" style="cursor: pointer;">删除</a>
<a @click.stop.prevent="deleteSelectedFiles(file.fid, file.file_name, file.dir)" style="cursor: pointer;">删除文件</a>
<a @click.stop.prevent="deleteSelectedFiles(file.fid, file.file_name, file.dir, true)" style="cursor: pointer; margin-left: 10px;">删除文件和记录</a>
</td>
</template>
</tr>
@ -2290,12 +2291,23 @@
this.$delete(this.formData.magic_regex, key);
}
},
deleteFile(fid, fname, isDir) {
if (!confirm(`确定要删除此项目吗?`)) {
deleteFile(fid, fname, isDir, deleteRecords = false) {
// 根据是否删除记录显示不同的确认提示
let confirmMessage = deleteRecords
? `确定要删除此项目及其关联记录吗?`
: `确定要删除此项目吗?`;
if (!confirm(confirmMessage)) {
return;
}
axios.post('/delete_file', { fid: fid })
// 获取当前路径作为save_path参数
let save_path = "";
if (this.fileSelect && this.fileSelect.paths) {
save_path = this.fileSelect.paths.map(item => item.name).join("/");
}
axios.post('/delete_file', { fid: fid, file_name: fname, delete_records: deleteRecords, save_path: save_path })
.then(response => {
if (response.data.code === 0) {
// 从列表中移除文件
@ -2311,7 +2323,18 @@
}
}
this.showToast('成功删除 1 个项目');
// 显示成功消息,根据是否删除记录显示不同的消息
if (deleteRecords) {
const deletedRecords = response.data.deleted_records || 0;
this.showToast(`成功删除 1 个项目${deletedRecords > 0 ? `及其关联的 ${deletedRecords} 条记录` : ''}`);
} else {
this.showToast('成功删除 1 个项目');
}
// 如果同时删除了记录,无论当前在哪个页面,都刷新历史记录
if (deleteRecords) {
this.loadHistoryRecords();
}
} else {
alert('删除失败: ' + response.data.message);
}
@ -3403,36 +3426,53 @@
// 阻止事件冒泡
event.stopPropagation();
},
deleteSelectedFiles(clickedFid, clickedFname, isDir) {
deleteSelectedFiles(clickedFid, clickedFname, isDir, deleteRecords = false) {
// 如果是文件夹或者没有选中的文件,则按原来的方式删除单个文件
if (isDir || this.fileSelect.selectedFiles.length === 0) {
this.deleteFile(clickedFid, clickedFname, isDir);
this.deleteFile(clickedFid, clickedFname, isDir, deleteRecords);
return;
}
// 如果点击的文件不在选中列表中,也按原来的方式删除单个文件
if (!this.fileSelect.selectedFiles.includes(clickedFid)) {
this.deleteFile(clickedFid, clickedFname, isDir);
this.deleteFile(clickedFid, clickedFname, isDir, deleteRecords);
return;
}
// 多选删除
const selectedCount = this.fileSelect.selectedFiles.length;
// 根据选中数量使用不同的确认提示
let confirmMessage = selectedCount === 1
? `确定要删除此项目吗?`
: `确定要删除选中的 ${selectedCount} 个项目吗?`;
// 根据选中数量和是否删除记录使用不同的确认提示
let confirmMessage = '';
if (deleteRecords) {
confirmMessage = selectedCount === 1
? `确定要删除此项目及其关联记录吗?`
: `确定要删除选中的 ${selectedCount} 个项目及其关联记录吗?`;
} else {
confirmMessage = selectedCount === 1
? `确定要删除此项目吗?`
: `确定要删除选中的 ${selectedCount} 个项目吗?`;
}
if (confirm(confirmMessage)) {
// 获取当前路径作为save_path参数
let save_path = "";
if (this.fileSelect && this.fileSelect.paths) {
save_path = this.fileSelect.paths.map(item => item.name).join("/");
}
// 创建一个Promise数组来处理所有删除请求
const deletePromises = this.fileSelect.selectedFiles.map(fid => {
return axios.post('/delete_file', { fid: fid })
// 查找对应的文件对象,获取文件名
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 };
return { fid: fid, success: response.data.code === 0, deleted_records: response.data.deleted_records || 0 };
})
.catch(error => {
return { fid: fid, success: false };
return { fid: fid, success: false, deleted_records: 0 };
});
});
@ -3442,6 +3482,8 @@
// 统计成功和失败的数量
const successCount = results.filter(r => r.success).length;
const failCount = results.length - successCount;
// 统计删除的记录数
const totalDeletedRecords = results.reduce((sum, r) => sum + (r.deleted_records || 0), 0);
// 从文件列表中移除成功删除的文件
const successfullyDeletedFids = results.filter(r => r.success).map(r => r.fid);
@ -3455,7 +3497,17 @@
if (failCount > 0) {
alert(`成功删除 ${successCount} 个项目,${failCount} 个项目删除失败`);
} else {
this.showToast(`成功删除 ${successCount} 个项目`);
// 根据是否删除记录显示不同的消息
if (deleteRecords) {
this.showToast(`成功删除 ${successCount} 个项目${totalDeletedRecords > 0 ? `及其关联的 ${totalDeletedRecords} 条记录` : ''}`);
} else {
this.showToast(`成功删除 ${successCount} 个项目`);
}
}
// 如果同时删除了记录,无论当前在哪个页面都刷新历史记录
if (deleteRecords) {
this.loadHistoryRecords();
}
});
}

View File

@ -1146,6 +1146,19 @@ class Quark:
# 目前只是添加占位符,未来可以扩展功能
pass
# 获取保存路径
save_path = task.get("savepath", "")
# 如果file_info中有子目录路径信息则拼接完整路径
subdir_path = file_info.get("subdir_path", "")
if subdir_path:
# 确保路径格式正确,避免双斜杠
if save_path.endswith('/') and subdir_path.startswith('/'):
save_path = save_path + subdir_path[1:]
elif not save_path.endswith('/') and not subdir_path.startswith('/'):
save_path = save_path + '/' + subdir_path
else:
save_path = save_path + subdir_path
# 添加记录到数据库
db.add_record(
task_name=task.get("taskname", ""),
@ -1156,7 +1169,8 @@ class Quark:
duration=duration,
resolution=resolution,
file_id=file_id,
file_type=file_type
file_type=file_type,
save_path=save_path
)
# 关闭数据库连接
@ -1195,12 +1209,26 @@ class Quark:
file_id = file_info.get("fid", "")
task_name = task.get("taskname", "")
# 获取保存路径
save_path = task.get("savepath", "")
# 如果file_info中有子目录路径信息则拼接完整路径
subdir_path = file_info.get("subdir_path", "")
if subdir_path:
# 确保路径格式正确,避免双斜杠
if save_path.endswith('/') and subdir_path.startswith('/'):
save_path = save_path + subdir_path[1:]
elif not save_path.endswith('/') and not subdir_path.startswith('/'):
save_path = save_path + '/' + subdir_path
else:
save_path = save_path + subdir_path
# 更新记录
updated = db.update_renamed_to(
file_id=file_id,
original_name=original_name,
renamed_to=renamed_to,
task_name=task_name
task_name=task_name,
save_path=save_path
)
# 关闭数据库连接
@ -1255,12 +1283,17 @@ class Quark:
# 使用原文件名和任务名查找记录
task_name = task.get("taskname", "")
# 获取保存路径
save_path = task.get("savepath", "")
# 注意:从日志中无法获取子目录信息,只能使用任务的主保存路径
# 更新记录
updated = db.update_renamed_to(
file_id="", # 不使用file_id查询因为在日志中无法获取
original_name=old_name,
renamed_to=new_name,
task_name=task_name
task_name=task_name,
save_path=save_path
)
# 关闭数据库连接