Compare commits

...

7 Commits

Author SHA1 Message Date
Cp0204
5e8ecd4052 📝 修改插件标志描述
Some checks failed
Docker Publish / build-and-push (push) Has been cancelled
2024-12-13 14:54:36 +08:00
Cp0204
3c4b92160d 🎨 调整任务建议样式 2024-12-13 14:47:04 +08:00
Cp0204
30a0d07b1a ️ 优化任务建议显示逻辑 2024-12-13 14:47:04 +08:00
Cp0204
9593ec4811 🎨 任务建议改为显示在上方 2024-12-13 14:47:04 +08:00
Cp0204
a59af79423 🐛 修复一些交互问题 2024-12-13 14:47:04 +08:00
Cp0204
7d1552eeca 添加任务建议
- 新增 `/task_suggestions` 路由,用于获取任务建议
- 添加任务建议的下拉菜单和搜索按钮
- 实现前端逻辑,支持根据任务名称搜索建议并显示结果
2024-12-13 14:47:04 +08:00
Cp0204
d05db559ab 保存路径和任务名称智能关联填写 2024-12-13 14:47:03 +08:00
3 changed files with 128 additions and 16 deletions

View File

@ -110,7 +110,7 @@ services:
| ---------------- | ---------- | -------- |
| `WEBUI_USERNAME` | `admin` | 管理账号 |
| `WEBUI_PASSWORD` | `admin123` | 管理密码 |
| `PLUGIN_FLAGS` | | 插件标志,示例使用 `-emby,-aria2` 来关闭插件 |
| `PLUGIN_FLAGS` | | 插件标志,`-emby,-aria2` 禁用某些插件 |
#### 一键更新

View File

@ -15,8 +15,10 @@ from flask import (
from apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.triggers.cron import CronTrigger
import subprocess
import requests
import hashlib
import logging
import base64
import json
import sys
import os
@ -139,7 +141,9 @@ def logout():
def index():
if not is_login():
return redirect(url_for("login"))
return render_template("index.html", version=app.config["APP_VERSION"], plugin_flags=PLUGIN_FLAGS)
return render_template(
"index.html", version=app.config["APP_VERSION"], plugin_flags=PLUGIN_FLAGS
)
# 获取配置数据
@ -212,6 +216,21 @@ def run_script_now():
)
@app.route("/task_suggestions")
def get_task_suggestions():
if not is_login():
return jsonify({"error": "未登录"})
base_url = base64.b64decode("aHR0cHM6Ly9zLjkxNzc4OC54eXo=").decode()
query = request.args.get("q", "").lower()
deep = request.args.get("d", "").lower()
url = f"{base_url}/task_suggestions?q={query}&d={deep}"
try:
response = requests.get(url)
return jsonify(response.json())
except Exception as e:
return jsonify({"error": str(e)})
@app.route("/get_share_detail")
def get_share_files():
if not is_login():

View File

@ -36,6 +36,16 @@
max-height: calc(100vh - 200px);
overflow-y: auto;
}
.task-suggestions {
width: 100%;
max-height: 500px;
overflow-y: auto;
transform: translate(0, -100%);
top: 0;
margin-top: -5px;
border: 1px solid #007bff;
}
</style>
</head>
@ -176,10 +186,23 @@
<label class="col-sm-2 col-form-label">任务名称</label>
<div class="col-sm-10">
<div class="input-group">
<input type="text" name="taskname[]" class="form-control" v-model="task.taskname" placeholder="必填">
<div class="input-group-append" v-if="task.taskname">
<div class="input-group-text">
<a target="_blank" :href="`https://www.google.com/search?q=%22pan.quark%22+${task.taskname}`"><i class="bi bi-search"></i></a>
<input type="text" name="taskname[]" class="form-control" v-model="task.taskname" placeholder="必填" @focus="smart_param.showSuggestions=true;focusTaskname(index, task)" @input="changeTaskname(index, task)">
<div class="dropdown-menu show task-suggestions" v-if="smart_param.showSuggestions && smart_param.taskSuggestions.length && smart_param.index === index">
<div class="text-muted text-center" style="font-size: small;">以下资源来自第三方,网络公开搜集,请自行辨识,如有侵权请联系夸克官方</div>
<div v-for="suggestion in smart_param.taskSuggestions" :key="suggestion.taskname" class="dropdown-item" @click.prevent="selectSuggestion(task, suggestion)" style="cursor: pointer;">
<span v-html="suggestion.verify ? '✅': '❔'"></span> {{ suggestion.taskname }}
<small class="text-muted">
<a :href="suggestion.shareurl" target="_blank" @click.stop>{{ suggestion.shareurl }}</a>
</small>
</div>
</div>
<div class="input-group-append" title="深度搜索">
<button class="btn btn-primary" type="button" @click="searchSuggestions(index, task.taskname)">
<i v-if="smart_param.isSearching && smart_param.index === index" class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></i>
<i v-else class="bi bi-search-heart"></i>
</button>
<div class="input-group-text" title="谷歌搜索">
<a target="_blank" :href="`https://www.google.com/search?q=%22pan.quark.cn/s%22+${task.taskname}`"><i class="bi bi-google"></i></a>
</div>
</div>
</div>
@ -202,8 +225,10 @@
<label class="col-sm-2 col-form-label">保存路径</label>
<div class="col-sm-10">
<div class="input-group">
<input type="text" name="savepath[]" class="form-control" v-model="task.savepath" placeholder="必填">
<input type="text" name="savepath[]" class="form-control" v-model="task.savepath" placeholder="必填" @focus="focusTaskname(index, task)">
<div class="input-group-append">
<button class="btn btn-secondary" type="button" v-if="smart_param.savepath && smart_param.index == index && task.savepath != smart_param.origin_savepath" @click="task.savepath = smart_param.origin_savepath"><i class="
bi bi-reply"></i></button>
<button class="btn btn-outline-secondary dropdown-toggle" type="button" @click="getSavepathDirs(task.savepath)" data-toggle="dropdown" aria-expanded="false">选择</button>
<div class="dropdown-menu" style="max-height: 300px; overflow-y: auto;">
<a class="dropdown-item" @click.stop.prevent="selectSavepath(index,getParentDirectory(task.savepath),'..')" href="#">..</a>
@ -391,7 +416,7 @@
newTask: {
taskname: "",
shareurl: "",
savepath: "",
savepath: "/",
pattern: "",
replace: "",
enddate: "",
@ -406,7 +431,16 @@
savepaths: [],
modalLoading: false,
shareFiles: [],
forceTaskIndex: null
forceTaskIndex: null,
smart_param: {
index: null,
savepath: "",
origin_savepath: "",
taskSuggestions: [],
showSuggestions: false,
lastSuggestionsTime: 0,
isSearching: false,
},
},
filters: {
ts2date: function (value) {
@ -429,6 +463,11 @@
this.checkNewVersion();
$('[data-toggle="tooltip"]').tooltip();
document.addEventListener('keydown', this.handleKeyDown);
document.addEventListener('click', (e) => {
if (!e.target.closest('.input-group')) {
this.smart_param.showSuggestions = false;
}
});
},
methods: {
checkNewVersion() {
@ -510,12 +549,18 @@
},
addTask() {
newTask = { ...this.newTask }
newTask.taskname = this.taskNameFilter
lastTask = this.formData.tasklist[this.formData.tasklist.length - 1]
if (this.formData.tasklist.length > 0 && lastTask.taskname) {
newTask.savepath = lastTask.savepath.replace(lastTask.taskname, 'TASKNAME')
} else {
newTask.savepath = this.taskDirSelected + "/" + newTask.taskname
newTask.taskname = this.taskNameFilter;
if (this.formData.tasklist.length > 0) {
lastTask = this.formData.tasklist[this.formData.tasklist.length - 1];
if (this.taskDirSelected) {
newTask.savepath = this.taskDirSelected + '/TASKNAME';
} else {
if (newTask.taskname) {
newTask.savepath = lastTask.savepath.replace(lastTask.taskname, newTask.taskname);
} else {
newTask.savepath = lastTask.taskname ? lastTask.savepath.replace(lastTask.taskname, 'TASKNAME') : lastTask.savepath;
}
}
}
this.formData.tasklist.push(newTask);
// 滚到最下
@ -525,6 +570,23 @@
});
}, 1);
},
focusTaskname(index, task) {
this.smart_param.index = index
this.smart_param.origin_savepath = task.savepath
regex = new RegExp(`/${task.taskname.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}(/|$)`)
if (task.savepath.includes('TASKNAME')) {
this.smart_param.savepath = task.savepath;
} else if (task.savepath.match(regex)) {
this.smart_param.savepath = task.savepath.replace(task.taskname, 'TASKNAME');
} else {
this.smart_param.savepath = undefined;
}
},
changeTaskname(index, task) {
this.searchSuggestions(index, task.taskname, 500);
if (this.smart_param.savepath)
task.savepath = this.smart_param.savepath.replace('TASKNAME', task.taskname);
},
removeTask(index) {
if (confirm("确认删除任务 [#" + (index + 1) + ": " + this.formData.tasklist[index].taskname + "] 吗?"))
this.formData.tasklist.splice(index, 1);
@ -665,7 +727,38 @@
} else {
task.runweek = [1, 2, 3, 4, 5, 6, 7];
}
}
},
searchSuggestions(index, taskname, limit_msec = 0) {
if (taskname.length == 0)
return
if (limit_msec > 0) {
const now = Date.now();
if (now - this.smart_param.lastSuggestionsTime < limit_msec)
return;
this.smart_param.lastSuggestionsTime = now;
}
this.smart_param.isSearching = true
this.smart_param.index = index;
axios.get('/task_suggestions', {
params: {
q: taskname,
d: limit_msec == 0 ? 1 : 0
}
}).then(response => {
this.smart_param.taskSuggestions = response.data;
this.smart_param.showSuggestions = true;
}).catch(error => {
console.error('Error fetching suggestions:', error);
}).finally(() => {
this.smart_param.isSearching = false;
});
},
selectSuggestion(task, suggestion) {
task.taskname = suggestion.taskname;
task.shareurl = suggestion.shareurl;
this.changeShareurl(task);
this.smart_param.showSuggestions = false;
},
}
});
</script>