web UI:手动合并的视频文件也能点选,一次可选多个
之前只有弹幕框挂了「浏览…」,视频框只能手填,或者用那个原生多选下拉 (要按 Ctrl、点一下就把手填的内容整段覆盖、手机上根本没法用)。原因是 视频框是「一行一个、可能多个」的形态,而浏览弹窗当时只支持单选。 改成同一个弹窗支持多选: - 「浏览…」多带一个多选开关,视频框用它打开;列表里点文件名即勾选、 再点取消,条目左边有勾选框,底部显示「已选 N 项」并能一键清空。 勾选状态由页面保管、换目录不清,所以可以进别的文件夹继续勾、跨盘攒。 - 选用是**追加**到框里:手填的行和之前选好的行都留着,重复的不加第二遍。 双击某个文件=把这一个一起追加进去并关窗(和单选的「双击即选用」一致)。 - 填入时录播目录下的文件只写文件名(跟着保存目录走),别处的写绝对路径; 单选的弹幕框也按同一规则,顺手让框里干净些。 - 多选框取第一行当起点,不再把整段多行文本当路径去解析。 顺带修一个连带问题:手动合并的视频/弹幕框不进配置,refresh 也不会回填它们, 之前选完却会打上「未保存」标记 —— 顶栏那句「有未保存的修改」永远亮着, 点保存也清不掉。现在只对设置表单里的框打标记。 verify_pick.py 扩到 48 项断言(勾选/取消/跨目录累积/清空/追加不覆盖/填文件名 还是绝对路径/手机端点选/配置全程没动)全部通过;verify_settings.py 与 verify_streamer_br.py 重跑无回归;go test ./...、gofmt、go vet 干净。
This commit is contained in:
@@ -160,6 +160,8 @@ web UI 默认通过`http://localhost:51880`访问(端口对应`webPort`),
|
|||||||
|
|
||||||
目录和文件两条路都行:直接在框里填,或者点框右边的`浏览`逐级点选。弹窗里可以一路点进子文件夹、点`C:`/`D:`这类按钮(Linux 上是`/`、`/home`、`/mnt` 这些常用位置)换根位置,地址栏本身也能粘贴路径回车跳转,随时在点选和手填之间切换;选好点`选用这个文件夹`填回设置框,仍然是按回车或点保存才生效。填的是文件时(比如 ffmpeg 路径)列出的是文件,双击即选中并填入。默认不显示以点开头的隐藏项,需要时勾上`显示隐藏项`。
|
目录和文件两条路都行:直接在框里填,或者点框右边的`浏览`逐级点选。弹窗里可以一路点进子文件夹、点`C:`/`D:`这类按钮(Linux 上是`/`、`/home`、`/mnt` 这些常用位置)换根位置,地址栏本身也能粘贴路径回车跳转,随时在点选和手填之间切换;选好点`选用这个文件夹`填回设置框,仍然是按回车或点保存才生效。填的是文件时(比如 ffmpeg 路径)列出的是文件,双击即选中并填入。默认不显示以点开头的隐藏项,需要时勾上`显示隐藏项`。
|
||||||
|
|
||||||
|
要选的是一组文件时(`手动合并`里的视频框,一行一个)用的是同一套弹窗,区别只是能多选:点一下文件名即勾选、再点一下取消(手机上也一样点,不用按 Ctrl),可以换到别的文件夹继续勾,底部显示`已选 N 项`、点`清空`能重来,双击某个文件则是「把这一个一起追加进去并关窗」。选完点`选用这 N 个文件`是**追加**到框里,手填的内容和其他已经选好的行都不会被清掉,重复的不会加两遍。填入时**录播目录下的文件只写文件名**(跟着保存目录走,以后改了保存目录也还认得),别的文件夹里的文件写绝对路径。这个视频框和旁边的弹幕框都只服务于一次手动合并,不属于设置项,所以选完不会有`有未保存的修改`提示,也不用去点`保存设置`,直接点`开始合并`即可。
|
||||||
|
|
||||||
页面每 3 秒从服务端取一次状态回填表单,但**不会覆盖你改过还没提交的字段**,所以填好的数字不会过几秒又被刷回旧值;有没提交的内容时顶栏会显示`有未保存的修改`(保存按钮右上角也有个红点),此时离开或刷新页面浏览器会再问一次。保存失败(比如成片目录填了个不存在的路径)时提示里会带上原因,并且**输入框里填的内容会保留**,改对了再存即可;开关和下拉框的立即保存不会带上前述还没提交的文本框内容,所以某个框填错也不会连累开关保存失败。
|
页面每 3 秒从服务端取一次状态回填表单,但**不会覆盖你改过还没提交的字段**,所以填好的数字不会过几秒又被刷回旧值;有没提交的内容时顶栏会显示`有未保存的修改`(保存按钮右上角也有个红点),此时离开或刷新页面浏览器会再问一次。保存失败(比如成片目录填了个不存在的路径)时提示里会带上原因,并且**输入框里填的内容会保留**,改对了再存即可;开关和下拉框的立即保存不会带上前述还没提交的文本框内容,所以某个框填错也不会连累开关保存失败。
|
||||||
|
|
||||||
顶栏右上角一直显示当前状态(`录制中 N` / `直播中 N` / `空闲`)。窄屏上按钮收起成短文案、标题居中,手机上也能正常用;页面跟随系统的深浅色设置,深色模式下日志和表单自动变深色,不用额外配置。
|
顶栏右上角一直显示当前状态(`录制中 N` / `直播中 N` / `空闲`)。窄屏上按钮收起成短文案、标题居中,手机上也能正常用;页面跟随系统的深浅色设置,深色模式下日志和表单自动变深色,不用额外配置。
|
||||||
|
|||||||
+1
-1
@@ -110,7 +110,7 @@ web UI(`-webui`,端口看`webPort`)用的是另一套`/api/`接口,全
|
|||||||
|
|
||||||
MeoW 的`status`和封禁规则会反映在`GET /api/status`的`meowBlocked`字段里:昵称没填、格式不对、或已被 MeoW 判过「未注册」时,这里会给出中文原因,非空就说明 MeoW 渠道当前推不出去。注意 MeoW 一天内累计 3 次推给不存在的昵称会封 IP,所以被判过不存在的昵称会被程序记住并停止重试,改昵称或重启程序才恢复。
|
MeoW 的`status`和封禁规则会反映在`GET /api/status`的`meowBlocked`字段里:昵称没填、格式不对、或已被 MeoW 判过「未注册」时,这里会给出中文原因,非空就说明 MeoW 渠道当前推不出去。注意 MeoW 一天内累计 3 次推给不存在的昵称会封 IP,所以被判过不存在的昵称会被程序记住并停止重试,改昵称或重启程序才恢复。
|
||||||
|
|
||||||
`GET /api/browse` 给页面上的`浏览`按钮列目录用。参数:`path`(要展开的目录,不填就用程序当前的工作目录)、`kind=file`(连文件一起列,挑文件时用)、`hidden=1`(连以点开头的隐藏项一起列)。
|
`GET /api/browse` 给页面上的`浏览`按钮列目录用。参数:`path`(要展开的目录,不填就用程序当前的工作目录)、`kind=file`(连文件一起列,挑文件时用)、`hidden=1`(连以点开头的隐藏项一起列)。页面上的`浏览`有单选(保存目录、ffmpeg 路径、弹幕文件)和多选(手动合并的视频文件)两种用法,接口每次只负责列一层,多选的勾选状态由页面自己保管,所以可以换着目录勾、跨文件夹攒;填回框里时,录播目录下的文件写文件名、别的文件夹里的写绝对路径。
|
||||||
|
|
||||||
```
|
```
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -175,6 +175,12 @@ const webuiHTML = `<!DOCTYPE html>
|
|||||||
.pi-n{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
.pi-n{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
||||||
.pi-dir .pi-n{font-weight:600}
|
.pi-dir .pi-n{font-weight:600}
|
||||||
.pi-m{flex:none;font-size:11.5px;color:var(--sub);font-variant-numeric:tabular-nums}
|
.pi-m{flex:none;font-size:11.5px;color:var(--sub);font-variant-numeric:tabular-nums}
|
||||||
|
/* 多选模式(选视频这种可以选多个的框):文件条目左边给个勾选框 */
|
||||||
|
.pi-c{display:none;flex:none;width:15px;height:15px;border:1px solid var(--line);border-radius:4px;background:var(--card)}
|
||||||
|
#pick.multi .pi:not(.pi-dir) .pi-c{display:block}
|
||||||
|
#pick.multi .pi.on .pi-c{border-color:var(--accent);background:var(--accent)}
|
||||||
|
#pick.multi .pi.on .pi-c::after{content:"";display:block;width:4px;height:8px;margin:1px 0 0 4px;border:solid #fff;border-width:0 2px 2px 0;transform:rotate(45deg)}
|
||||||
|
.lnk{color:var(--accent);cursor:pointer;text-decoration:underline}
|
||||||
.only-m{display:none}
|
.only-m{display:none}
|
||||||
@media (max-width:700px){
|
@media (max-width:700px){
|
||||||
/* 手机上标题居中、两侧各留一列等宽(左右各只有一个按钮,视觉上才是正的居中) */
|
/* 手机上标题居中、两侧各留一列等宽(左右各只有一个按钮,视觉上才是正的居中) */
|
||||||
@@ -312,6 +318,10 @@ const webuiHTML = `<!DOCTYPE html>
|
|||||||
<div class="f">
|
<div class="f">
|
||||||
<label>视频文件(一行一个,也可填绝对路径)</label>
|
<label>视频文件(一行一个,也可填绝对路径)</label>
|
||||||
<textarea id="mrgVideos" rows="3" spellcheck="false" placeholder="2026-09-25 01-44-57 Xsphere 电脑自己画画.mp4"></textarea>
|
<textarea id="mrgVideos" rows="3" spellcheck="false" placeholder="2026-09-25 01-44-57 Xsphere 电脑自己画画.mp4"></textarea>
|
||||||
|
<div class="pathf" style="margin-top:8px">
|
||||||
|
<button class="ghost" onclick="openPick('mrgVideos','file',1)">浏览…</button>
|
||||||
|
<span class="hint" style="margin:0;flex:1 1 auto;min-width:0">可多选,能换文件夹继续勾;选完追加到框里,不清掉已填内容</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="f">
|
<div class="f">
|
||||||
<label>弹幕文件(.jsonl 直接用,.ass 会先还原成 .jsonl)</label>
|
<label>弹幕文件(.jsonl 直接用,.ass 会先还原成 .jsonl)</label>
|
||||||
@@ -452,6 +462,7 @@ const webuiHTML = `<!DOCTYPE html>
|
|||||||
<div class="hint" id="pickMsg"></div>
|
<div class="hint" id="pickMsg"></div>
|
||||||
<div class="bar" style="margin-top:12px">
|
<div class="bar" style="margin-top:12px">
|
||||||
<label class="sw" style="margin-right:auto"><input type="checkbox" id="pickHidden"><span class="sw-i"></span>显示隐藏项</label>
|
<label class="sw" style="margin-right:auto"><input type="checkbox" id="pickHidden"><span class="sw-i"></span>显示隐藏项</label>
|
||||||
|
<span class="hint" id="pickSel" style="margin:0"></span>
|
||||||
<button class="ghost" onclick="closePick()">取消</button>
|
<button class="ghost" onclick="closePick()">取消</button>
|
||||||
<button id="pickOk" onclick="pickConfirm()">选用这个文件夹</button>
|
<button id="pickOk" onclick="pickConfirm()">选用这个文件夹</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -862,23 +873,31 @@ var pickField = ""; // 选中的路径填回哪个输入框
|
|||||||
var pickKind = "dir"; // dir 选文件夹,file 选文件
|
var pickKind = "dir"; // dir 选文件夹,file 选文件
|
||||||
var pickCur = ""; // 当前所在目录
|
var pickCur = ""; // 当前所在目录
|
||||||
var pickParent = ""; // 上一级
|
var pickParent = ""; // 上一级
|
||||||
var pickFile = ""; // 选文件时选中的那个
|
var pickFile = ""; // 单选时选中的那个
|
||||||
var recDir = ""; // 录播目录,挑弹幕文件时从它开始
|
var pickMulti = false; // 多选模式(视频框这种一行一个、可以选多个的)
|
||||||
|
var pickSel = []; // 多选已勾选的绝对路径,换目录也不清,所以能跨文件夹/跨盘攒
|
||||||
|
var recDir = ""; // 录播目录,挑弹幕和视频时从它开始
|
||||||
var lastPickKey = "", lastPickAt = 0; // 列表里连点同一项只算一次
|
var lastPickKey = "", lastPickAt = 0; // 列表里连点同一项只算一次
|
||||||
var lastNavAt = 0; // 刚跳转过目录的这段时间里忽略列表上的点击,双击文件夹才不会连进两层
|
var lastNavAt = 0; // 刚跳转过目录的这段时间里忽略列表上的点击,双击文件夹才不会连进两层
|
||||||
|
|
||||||
function openPick(id, kind){
|
function openPick(id, kind, multi){
|
||||||
var el = document.getElementById(id);
|
var el = document.getElementById(id);
|
||||||
pickField = id;
|
pickField = id;
|
||||||
pickKind = kind === "file" ? "file" : "dir";
|
pickKind = kind === "file" ? "file" : "dir";
|
||||||
|
pickMulti = !!multi && pickKind === "file";
|
||||||
pickFile = "";
|
pickFile = "";
|
||||||
document.getElementById("pickTitle").textContent = pickKind === "file" ? "选择文件" : "选择文件夹";
|
pickSel = [];
|
||||||
document.getElementById("pickOk").textContent = pickKind === "file" ? "选用这个文件" : "选用这个文件夹";
|
document.getElementById("pickTitle").textContent = pickMulti ? "选择文件(可多选)" : (pickKind === "file" ? "选择文件" : "选择文件夹");
|
||||||
|
document.getElementById("pickDesc").textContent = pickMulti
|
||||||
|
? "点一下文件名即勾选,再点一下取消;可以进到别的文件夹继续勾。选好后点下面的按钮追加到框里,已经填好的内容不会被清掉。"
|
||||||
|
: "逐级点选即可;也可以直接在地址栏里填或粘贴路径后按回车。选好后点下面的按钮填进设置框。";
|
||||||
document.getElementById("pickHidden").checked = false;
|
document.getElementById("pickHidden").checked = false;
|
||||||
|
document.getElementById("pick").classList.toggle("multi", pickMulti);
|
||||||
document.getElementById("pick").classList.add("on");
|
document.getElementById("pick").classList.add("on");
|
||||||
var start = el ? el.value.trim() : "";
|
// 多行框(视频)取第一行当起点,整段拿去当路径会解析出一个乱七八糟的目录
|
||||||
// 框里是文件名而不是路径时(弹幕名这种)不算路径:弹幕框从录播目录开始,其它从程序所在目录开始
|
var start = el ? el.value.split("\n")[0].trim() : "";
|
||||||
if(start.indexOf("/") < 0 && start.indexOf("\\") < 0) start = id === "mrgDanmu" ? recDir : "";
|
// 框里是文件名而不是路径时(弹幕名、视频名这种)不算路径:这两个框从录播目录开始,其它从程序所在目录开始
|
||||||
|
if(start.indexOf("/") < 0 && start.indexOf("\\") < 0) start = (id === "mrgDanmu" || id === "mrgVideos") ? recDir : "";
|
||||||
pickGo(start);
|
pickGo(start);
|
||||||
}
|
}
|
||||||
function closePick(){ document.getElementById("pick").classList.remove("on"); }
|
function closePick(){ document.getElementById("pick").classList.remove("on"); }
|
||||||
@@ -905,8 +924,8 @@ async function pickGo(path){
|
|||||||
var items = d.entries || [];
|
var items = d.entries || [];
|
||||||
pickCur = d.path || "";
|
pickCur = d.path || "";
|
||||||
pickParent = d.parent || "";
|
pickParent = d.parent || "";
|
||||||
// 换了目录之后之前选中的文件就不在当前视图里了,清掉
|
// 单选时换了目录、之前选中的文件就不在当前视图里了,清掉;多选是故意跨目录攒的,不动
|
||||||
if(pickFile && !items.some(function(x){ return x.path === pickFile; })) pickFile = "";
|
if(!pickMulti && pickFile && !items.some(function(x){ return x.path === pickFile; })) pickFile = "";
|
||||||
document.getElementById("pickPath").value = pickCur;
|
document.getElementById("pickPath").value = pickCur;
|
||||||
document.getElementById("pickUp").disabled = !pickParent;
|
document.getElementById("pickUp").disabled = !pickParent;
|
||||||
document.getElementById("pickRoots").innerHTML = (d.roots || []).map(function(x){
|
document.getElementById("pickRoots").innerHTML = (d.roots || []).map(function(x){
|
||||||
@@ -917,13 +936,17 @@ async function pickGo(path){
|
|||||||
list.innerHTML = '<div class="off">' + (pickKind === "file" ? "这里没有可选项" : "这里没有子文件夹") + '</div>';
|
list.innerHTML = '<div class="off">' + (pickKind === "file" ? "这里没有可选项" : "这里没有子文件夹") + '</div>';
|
||||||
}else{
|
}else{
|
||||||
list.innerHTML = items.map(function(x){
|
list.innerHTML = items.map(function(x){
|
||||||
var on = (!x.dir && x.path === pickFile) ? " on" : "";
|
var on = "";
|
||||||
|
if(!x.dir){
|
||||||
|
on = (pickMulti ? pickSel.indexOf(x.path) >= 0 : x.path === pickFile) ? " on" : "";
|
||||||
|
}
|
||||||
return '<div class="pi' + (x.dir ? " pi-dir" : "") + on + '" data-path="' + esc(x.path) + '" data-dir="' + (x.dir ? "1" : "0") + '">'
|
return '<div class="pi' + (x.dir ? " pi-dir" : "") + on + '" data-path="' + esc(x.path) + '" data-dir="' + (x.dir ? "1" : "0") + '">'
|
||||||
|
+ '<span class="pi-c"></span>'
|
||||||
+ '<span class="pi-n" title="' + esc(x.path) + '">' + esc(x.name) + '</span>'
|
+ '<span class="pi-n" title="' + esc(x.path) + '">' + esc(x.name) + '</span>'
|
||||||
+ '<span class="pi-m">' + (x.dir ? "文件夹" : fmtSize(x.size)) + '</span></div>';
|
+ '<span class="pi-m">' + (x.dir ? "文件夹" : fmtSize(x.size)) + '</span></div>';
|
||||||
}).join("");
|
}).join("");
|
||||||
}
|
}
|
||||||
document.getElementById("pickOk").disabled = pickKind === "file" && !pickFile;
|
updatePickBar();
|
||||||
if(d.error){
|
if(d.error){
|
||||||
msg.textContent = d.error;
|
msg.textContent = d.error;
|
||||||
}else if(fellBack){
|
}else if(fellBack){
|
||||||
@@ -932,11 +955,67 @@ async function pickGo(path){
|
|||||||
msg.textContent = "条目太多,只列出了前 2000 项";
|
msg.textContent = "条目太多,只列出了前 2000 项";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 按钮文案和「已选 N 项」都跟着当前模式与勾选情况走
|
||||||
|
function updatePickBar(){
|
||||||
|
var ok = document.getElementById("pickOk");
|
||||||
|
var sel = document.getElementById("pickSel");
|
||||||
|
if(pickMulti){
|
||||||
|
ok.disabled = !pickSel.length;
|
||||||
|
ok.textContent = pickSel.length ? ("选用这 " + pickSel.length + " 个文件") : "选用选中的文件";
|
||||||
|
sel.innerHTML = pickSel.length
|
||||||
|
? ("已选 " + pickSel.length + " 项(<span class=\"lnk\" onclick=\"pickClearSel()\">清空</span>)")
|
||||||
|
: "点文件名即勾选,可换目录继续勾";
|
||||||
|
}else{
|
||||||
|
ok.disabled = pickKind === "file" && !pickFile;
|
||||||
|
ok.textContent = pickKind === "file" ? "选用这个文件" : "选用这个文件夹";
|
||||||
|
sel.innerHTML = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 只重画高亮,不重新拉目录
|
||||||
|
function paintPickSel(){
|
||||||
|
var all = document.querySelectorAll("#pickList .pi");
|
||||||
|
for(var i = 0; i < all.length; i++){
|
||||||
|
var it = all[i];
|
||||||
|
if(it.dataset.dir === "1") continue;
|
||||||
|
it.classList.toggle("on", pickMulti ? pickSel.indexOf(it.dataset.path) >= 0 : it.dataset.path === pickFile);
|
||||||
|
}
|
||||||
|
updatePickBar();
|
||||||
|
}
|
||||||
|
function pickClearSel(){ pickSel = []; paintPickSel(); }
|
||||||
|
// 录播目录下的文件就只填文件名,框里清爽,保存目录以后改了也还认得;别处的填绝对路径
|
||||||
|
function pickFillValue(p){
|
||||||
|
if(!recDir) return p;
|
||||||
|
var parent = p.replace(/[\\/][^\\/]*$/, "");
|
||||||
|
if(parent.replace(/\\/g, "/").replace(/\/+$/, "").toLowerCase() === recDir.replace(/\\/g, "/").replace(/\/+$/, "").toLowerCase()){
|
||||||
|
return p.split(/[\\/]/).pop();
|
||||||
|
}
|
||||||
|
return p;
|
||||||
|
}
|
||||||
function pickConfirm(){
|
function pickConfirm(){
|
||||||
|
var el = document.getElementById(pickField);
|
||||||
|
if(pickMulti){
|
||||||
|
if(!pickSel.length) return;
|
||||||
|
if(el){
|
||||||
|
// 追加:已有行原样留着,只把这次勾的加进去(同名/同路径不重复)
|
||||||
|
var lines = el.value.split("\n").map(function(s){ return s.trim(); }).filter(function(s){ return s; });
|
||||||
|
pickSel.forEach(function(p){
|
||||||
|
var name = pickFillValue(p);
|
||||||
|
if(lines.indexOf(name) < 0 && lines.indexOf(p) < 0) lines.push(name);
|
||||||
|
});
|
||||||
|
el.value = lines.join("\n");
|
||||||
|
// 手动合并的视频/弹幕框不进配置,标了「未保存」也永远清不掉,所以只对设置表单里的框标记
|
||||||
|
if(settingFields.indexOf(pickField) >= 0) markDirty(pickField);
|
||||||
|
}
|
||||||
|
closePick();
|
||||||
|
return;
|
||||||
|
}
|
||||||
var v = pickKind === "file" ? pickFile : pickCur;
|
var v = pickKind === "file" ? pickFile : pickCur;
|
||||||
if(!v) return;
|
if(!v) return;
|
||||||
var el = document.getElementById(pickField);
|
if(pickKind === "file") v = pickFillValue(v);
|
||||||
if(el){ el.value = v; markDirty(pickField); }
|
if(el){
|
||||||
|
el.value = v;
|
||||||
|
if(settingFields.indexOf(pickField) >= 0) markDirty(pickField);
|
||||||
|
}
|
||||||
closePick();
|
closePick();
|
||||||
}
|
}
|
||||||
document.getElementById("pickList").addEventListener("click", function(e){
|
document.getElementById("pickList").addEventListener("click", function(e){
|
||||||
@@ -948,15 +1027,20 @@ document.getElementById("pickList").addEventListener("click", function(e){
|
|||||||
lastPickKey = it.dataset.path;
|
lastPickKey = it.dataset.path;
|
||||||
lastPickAt = Date.now();
|
lastPickAt = Date.now();
|
||||||
if(it.dataset.dir === "1"){ pickGo(it.dataset.path); return; }
|
if(it.dataset.dir === "1"){ pickGo(it.dataset.path); return; }
|
||||||
pickFile = it.dataset.path;
|
if(pickMulti){
|
||||||
var all = this.querySelectorAll(".pi");
|
var i = pickSel.indexOf(it.dataset.path);
|
||||||
for(var i = 0; i < all.length; i++) all[i].classList.toggle("on", all[i] === it);
|
if(i >= 0) pickSel.splice(i, 1); else pickSel.push(it.dataset.path);
|
||||||
document.getElementById("pickOk").disabled = false;
|
}else{
|
||||||
|
pickFile = it.dataset.path;
|
||||||
|
}
|
||||||
|
paintPickSel();
|
||||||
});
|
});
|
||||||
// 文件双击直接选用
|
// 文件双击直接选用(多选时把双击的这个也算上,再连同已勾的一起追加)
|
||||||
document.getElementById("pickList").addEventListener("dblclick", function(e){
|
document.getElementById("pickList").addEventListener("dblclick", function(e){
|
||||||
var it = e.target.closest(".pi");
|
var it = e.target.closest(".pi");
|
||||||
if(it && it.dataset.dir === "0") pickConfirm();
|
if(!it || it.dataset.dir === "1") return;
|
||||||
|
if(pickMulti && pickSel.indexOf(it.dataset.path) < 0) pickSel.push(it.dataset.path);
|
||||||
|
pickConfirm();
|
||||||
});
|
});
|
||||||
document.getElementById("pickRoots").addEventListener("click", function(e){
|
document.getElementById("pickRoots").addEventListener("click", function(e){
|
||||||
var b = e.target.closest("button");
|
var b = e.target.closest("button");
|
||||||
|
|||||||
Reference in New Issue
Block a user