解决v2.0.0公测版大家反馈的问题

This commit is contained in:
Aicirou 2020-05-20 02:11:53 +08:00
parent 78ab2ed495
commit 6a63ea8e85
8 changed files with 932 additions and 820 deletions

View File

@ -1,13 +1,13 @@
var authConfig = { var authConfig = {
"siteName": "Achirou's Cloud", // 网站名称 siteName: "Achirou's Cloud", // 网站名称
"version" : "1.1.0", // 程序版本 version: "1.1.0", // 程序版本
"theme" : "acrou", theme: "acrou",
/*"client_id": "202264815644.apps.googleusercontent.com", /*"client_id": "202264815644.apps.googleusercontent.com",
"client_secret": "X4Z3ca8xfWDb1Voo-F9a7ZxJ",*/ "client_secret": "X4Z3ca8xfWDb1Voo-F9a7ZxJ",*/
// 强烈推荐使用自己的 client_id 和 client_secret // 强烈推荐使用自己的 client_id 和 client_secret
"client_id": "", client_id: "",
"client_secret": "", client_secret: "",
"refresh_token": "", // 授权 token refresh_token: "", // 授权 token
/** /**
* 设置要显示的多个云端硬盘按格式添加多个 * 设置要显示的多个云端硬盘按格式添加多个
* [id]: 可以是 团队盘id子文件夹id或者"root"代表个人盘根目录 * [id]: 可以是 团队盘id子文件夹id或者"root"代表个人盘根目录
@ -21,7 +21,7 @@ var authConfig = {
* 不需要 Basic Auth 的盘保持 user pass 同时为空即可直接不设置也可以 * 不需要 Basic Auth 的盘保持 user pass 同时为空即可直接不设置也可以
* 注意对于id设置为为子文件夹id的盘将不支持搜索功能不影响其他盘 * 注意对于id设置为为子文件夹id的盘将不支持搜索功能不影响其他盘
*/ */
"roots": [ roots: [
{ {
id: "", id: "",
name: "TeamDrive", name: "TeamDrive",
@ -30,45 +30,52 @@ var authConfig = {
{ {
id: "root", id: "root",
name: "PrivateDrive", name: "PrivateDrive",
user: '', user: "",
pass: "", pass: "",
protect_file_link: true protect_file_link: true,
}, },
{ {
id: "", id: "",
name: "folder1", name: "folder1",
pass: "", pass: "",
} },
], ],
"default_gd": 0, default_gd: 0,
/** /**
* 文件列表页面每页显示的数量推荐设置值为 100 1000 之间 * 文件列表页面每页显示的数量推荐设置值为 100 1000 之间
* 如果设置大于1000会导致请求 drive api 时出错 * 如果设置大于1000会导致请求 drive api 时出错
* 如果设置的值过小会导致文件列表页面滚动条增量加载分页加载失效 * 如果设置的值过小会导致文件列表页面滚动条增量加载分页加载失效
* 此值的另一个作用是如果目录内文件数大于此设置值即需要多页展示的将会对首次列目录结果进行缓存 * 此值的另一个作用是如果目录内文件数大于此设置值即需要多页展示的将会对首次列目录结果进行缓存
*/ */
"files_list_page_size": 50, files_list_page_size: 50,
/** /**
* 搜索结果页面每页显示的数量推荐设置值为 50 1000 之间 * 搜索结果页面每页显示的数量推荐设置值为 50 1000 之间
* 如果设置大于1000会导致请求 drive api 时出错 * 如果设置大于1000会导致请求 drive api 时出错
* 如果设置的值过小会导致搜索结果页面滚动条增量加载分页加载失效 * 如果设置的值过小会导致搜索结果页面滚动条增量加载分页加载失效
* 此值的大小影响搜索操作的响应速度 * 此值的大小影响搜索操作的响应速度
*/ */
"search_result_list_page_size": 50, search_result_list_page_size: 50,
// 确认有 cors 用途的可以开启 // 确认有 cors 用途的可以开启
"enable_cors_file_down": false, enable_cors_file_down: false,
/** /**
* 上面的 basic auth 已经包含了盘内全局保护的功能所以默认不再去认证 .password 文件内的密码; * 上面的 basic auth 已经包含了盘内全局保护的功能所以默认不再去认证 .password 文件内的密码;
* 如果在全局认证的基础上仍需要给某些目录单独进行 .password 文件内的密码验证的话将此选项设置为 true; * 如果在全局认证的基础上仍需要给某些目录单独进行 .password 文件内的密码验证的话将此选项设置为 true;
* 注意如果开启了 .password 文件密码验证每次列目录都会额外增加查询目录内 .password 文件是否存在的开销 * 注意如果开启了 .password 文件密码验证每次列目录都会额外增加查询目录内 .password 文件是否存在的开销
*/ */
"enable_password_file_verify": true enable_password_file_verify: true,
}; };
var themeOptions = { var themeOptions = {
//可选默认系统语言:en/zh-chs/zh-cht //可选默认系统语言:en/zh-chs/zh-cht
languages: 'en' languages: "en",
} render: {
head_md: false,
readme_md: false,
// 是否显示文件/文件夹描述(默认不显示)
// Show file/folder description or not (not shown by default)
desc: false
},
};
/** /**
* global functions * global functions
@ -81,11 +88,11 @@ const FUNCS = {
let nothing = ""; let nothing = "";
let space = " "; let space = " ";
if (!keyword) return nothing; if (!keyword) return nothing;
return keyword.replace(/(!=)|['"=<>/\\:]/g, nothing) return keyword
.replace(/(!=)|['"=<>/\\:]/g, nothing)
.replace(/[,|(){}]/g, space) .replace(/[,|(){}]/g, space)
.trim() .trim();
} },
}; };
/** /**
@ -93,16 +100,16 @@ const FUNCS = {
* @type {{folder_mime_type: string, default_file_fields: string, gd_root_type: {share_drive: number, user_drive: number, sub_folder: number}}} * @type {{folder_mime_type: string, default_file_fields: string, gd_root_type: {share_drive: number, user_drive: number, sub_folder: number}}}
*/ */
const CONSTS = new (class { const CONSTS = new (class {
default_file_fields = 'parents,id,name,mimeType,modifiedTime,createdTime,fileExtension,size'; default_file_fields =
"parents,id,name,mimeType,modifiedTime,createdTime,fileExtension,size";
gd_root_type = { gd_root_type = {
user_drive: 0, user_drive: 0,
share_drive: 1, share_drive: 1,
sub_folder: 2 sub_folder: 2,
}; };
folder_mime_type = 'application/vnd.google-apps.folder'; folder_mime_type = "application/vnd.google-apps.folder";
})(); })();
// gd instances // gd instances
var gds = []; var gds = [];
@ -118,8 +125,14 @@ function html(current_drive_order = 0, model = {}) {
@import url(https://cloud.jsonpop.cn/go2index/v2.0.0_beta/dist/style.css); @import url(https://cloud.jsonpop.cn/go2index/v2.0.0_beta/dist/style.css);
</style> </style>
<script> <script>
window.gdconfig = JSON.parse('${JSON.stringify({version: authConfig.version,themeOptions: themeOptions})}'); window.gdconfig = JSON.parse('${JSON.stringify({
window.gds = JSON.parse('${JSON.stringify(authConfig.roots.map(it => it.name))}'); version: authConfig.version,
themeOptions: themeOptions,
})}');
window.themeOptions = JSON.parse('${JSON.stringify(themeOptions)}');
window.gds = JSON.parse('${JSON.stringify(
authConfig.roots.map((it) => it.name)
)}');
window.MODEL = JSON.parse('${JSON.stringify(model)}'); window.MODEL = JSON.parse('${JSON.stringify(model)}');
window.current_drive_order = ${current_drive_order}; window.current_drive_order = ${current_drive_order};
</script> </script>
@ -130,9 +143,9 @@ function html(current_drive_order = 0, model = {}) {
</body> </body>
</html> </html>
`; `;
}; }
addEventListener('fetch', event => { addEventListener("fetch", (event) => {
event.respondWith(handleRequest(event.request)); event.respondWith(handleRequest(event.request));
}); });
@ -145,11 +158,11 @@ async function handleRequest(request) {
for (let i = 0; i < authConfig.roots.length; i++) { for (let i = 0; i < authConfig.roots.length; i++) {
const gd = new googleDrive(authConfig, i); const gd = new googleDrive(authConfig, i);
await gd.init(); await gd.init();
gds.push(gd) gds.push(gd);
} }
// 这个操作并行,提高效率 // 这个操作并行,提高效率
let tasks = []; let tasks = [];
gds.forEach(gd => { gds.forEach((gd) => {
tasks.push(gd.initRootType()); tasks.push(gd.initRootType());
}); });
for (let task of tasks) { for (let task of tasks) {
@ -168,13 +181,16 @@ async function handleRequest(request) {
* @returns {Response} * @returns {Response}
*/ */
function redirectToIndexPage() { function redirectToIndexPage() {
return new Response('', {status: 301, headers: {'Location': `/${authConfig.default_gd}:/`}}); return new Response("", {
status: 301,
headers: { Location: `/${authConfig.default_gd}:/` },
});
} }
if (path == '/') return redirectToIndexPage(); if (path == "/") return redirectToIndexPage();
if (path.toLowerCase() == '/favicon.ico') { if (path.toLowerCase() == "/favicon.ico") {
// 后面可以找一个 favicon // 后面可以找一个 favicon
return new Response('', {status: 404}) return new Response("", { status: 404 });
} }
// 特殊命令格式 // 特殊命令格式
@ -187,46 +203,48 @@ async function handleRequest(request) {
if (order >= 0 && order < gds.length) { if (order >= 0 && order < gds.length) {
gd = gds[order]; gd = gds[order];
} else { } else {
return redirectToIndexPage() return redirectToIndexPage();
} }
// basic auth // basic auth
for (const r = gd.basicAuthResponse(request); r; ) return r; for (const r = gd.basicAuthResponse(request); r; ) return r;
command = match.groups.command; command = match.groups.command;
// 搜索 // 搜索
if (command === 'search') { if (command === "search") {
if (request.method === 'POST') { if (request.method === "POST") {
// 搜索结果 // 搜索结果
return handleSearch(request, gd); return handleSearch(request, gd);
} else { } else {
const params = url.searchParams; const params = url.searchParams;
// 搜索页面 // 搜索页面
return new Response(html(gd.order, { return new Response(
q: params.get("q") || '', html(gd.order, {
q: params.get("q") || "",
is_search_page: true, is_search_page: true,
root_type: gd.root_type root_type: gd.root_type,
}), }),
{ {
status: 200, status: 200,
headers: {'Content-Type': 'text/html; charset=utf-8'} headers: { "Content-Type": "text/html; charset=utf-8" },
});
} }
} else if (command === 'id2path' && request.method === 'POST') { );
return handleId2Path(request, gd) }
} else if (command === 'view'){ } else if (command === "id2path" && request.method === "POST") {
return handleId2Path(request, gd);
} else if (command === "view") {
const params = url.searchParams; const params = url.searchParams;
return gd.view(params.get("url"), request.headers.get('Range')); return gd.view(params.get("url"), request.headers.get("Range"));
} else if (command!=='down' && request.method === 'GET'){ } else if (command !== "down" && request.method === "GET") {
return new Response(html(gd.order, { root_type: gd.root_type }), { return new Response(html(gd.order, { root_type: gd.root_type }), {
status: 200, status: 200,
headers: {'Content-Type': 'text/html; charset=utf-8'} headers: { "Content-Type": "text/html; charset=utf-8" },
}); });
} }
} }
const reg = new RegExp(`^(/\\d+:)${command}/`, 'g'); const reg = new RegExp(`^(/\\d+:)${command}/`, "g");
path = path.replace(reg, (p1, p2) => { path = path.replace(reg, (p1, p2) => {
return p2+'/' return p2 + "/";
}) });
// 期望的 path 格式 // 期望的 path 格式
const common_reg = /^\/\d+:\/.*$/g; const common_reg = /^\/\d+:\/.*$/g;
try { try {
@ -238,56 +256,67 @@ async function handleRequest(request) {
if (order >= 0 && order < gds.length) { if (order >= 0 && order < gds.length) {
gd = gds[order]; gd = gds[order];
} else { } else {
return redirectToIndexPage() return redirectToIndexPage();
} }
} catch (e) { } catch (e) {
return redirectToIndexPage() return redirectToIndexPage();
} }
// basic auth // basic auth
// for (const r = gd.basicAuthResponse(request); r;) return r; // for (const r = gd.basicAuthResponse(request); r;) return r;
const basic_auth_res = gd.basicAuthResponse(request); const basic_auth_res = gd.basicAuthResponse(request);
path = path.replace(gd.url_path_prefix, '') || '/'; path = path.replace(gd.url_path_prefix, "") || "/";
if (request.method == 'POST') { if (request.method == "POST") {
return basic_auth_res || apiRequest(request, gd); return basic_auth_res || apiRequest(request, gd);
} }
let action = url.searchParams.get('a'); let action = url.searchParams.get("a");
if (path.substr(-1) == '/' || action != null) { if (path.substr(-1) == "/" || action != null) {
return basic_auth_res || new Response(html(gd.order, {root_type: gd.root_type}), { return (
basic_auth_res ||
new Response(html(gd.order, { root_type: gd.root_type }), {
status: 200, status: 200,
headers: {'Content-Type': 'text/html; charset=utf-8'} headers: { "Content-Type": "text/html; charset=utf-8" },
}); })
);
} else { } else {
if (path.split('/').pop().toLowerCase() == ".password") { if (
path
.split("/")
.pop()
.toLowerCase() == ".password"
) {
return basic_auth_res || new Response("", { status: 404 }); return basic_auth_res || new Response("", { status: 404 });
} }
let file = await gd.file(path); let file = await gd.file(path);
let range = request.headers.get('Range'); let range = request.headers.get("Range");
if (gd.root.protect_file_link && basic_auth_res) return basic_auth_res; if (gd.root.protect_file_link && basic_auth_res) return basic_auth_res;
const is_down = !(command && command=='down'); const is_down = !(command && command == "down");
return gd.down(file.id, range, is_down); return gd.down(file.id, range, is_down);
} }
} }
async function apiRequest(request, gd) { async function apiRequest(request, gd) {
let url = new URL(request.url); let url = new URL(request.url);
let path = url.pathname; let path = url.pathname;
path = path.replace(gd.url_path_prefix, '') || '/'; path = path.replace(gd.url_path_prefix, "") || "/";
let option = {status: 200, headers: {'Access-Control-Allow-Origin': '*'}} let option = { status: 200, headers: { "Access-Control-Allow-Origin": "*" } };
if (path.substr(-1) == '/') { if (path.substr(-1) == "/") {
let deferred_pass = gd.password(path); let deferred_pass = gd.password(path);
let body = await request.text(); let body = await request.text();
body = JSON.parse(body); body = JSON.parse(body);
// 这样可以提升首次列目录时的速度。缺点是如果password验证失败也依然会产生列目录的开销 // 这样可以提升首次列目录时的速度。缺点是如果password验证失败也依然会产生列目录的开销
let deferred_list_result = gd.list(path, body.page_token, Number(body.page_index)); let deferred_list_result = gd.list(
path,
body.page_token,
Number(body.page_index)
);
// check .password file, if `enable_password_file_verify` is true // check .password file, if `enable_password_file_verify` is true
if (authConfig['enable_password_file_verify']) { if (authConfig["enable_password_file_verify"]) {
let password = await gd.password(path); let password = await gd.password(path);
// console.log("dir password", password); // console.log("dir password", password);
if (password && password.replace("\n", "") !== body.password) { if (password && password.replace("\n", "") !== body.password) {
@ -300,18 +329,24 @@ async function apiRequest(request, gd) {
return new Response(JSON.stringify(list_result), option); return new Response(JSON.stringify(list_result), option);
} else { } else {
let file = await gd.file(path); let file = await gd.file(path);
let range = request.headers.get('Range'); let range = request.headers.get("Range");
return new Response(JSON.stringify(file)); return new Response(JSON.stringify(file));
} }
} }
// 处理 search // 处理 search
async function handleSearch(request, gd) { async function handleSearch(request, gd) {
const option = {status: 200, headers: {'Access-Control-Allow-Origin': '*'}}; const option = {
status: 200,
headers: { "Access-Control-Allow-Origin": "*" },
};
let body = await request.text(); let body = await request.text();
body = JSON.parse(body); body = JSON.parse(body);
let search_result = await let search_result = await gd.search(
gd.search(body.q || '', body.page_token, Number(body.page_index)); body.q || "",
body.page_token,
Number(body.page_index)
);
return new Response(JSON.stringify(search_result), option); return new Response(JSON.stringify(search_result), option);
} }
@ -322,11 +357,14 @@ async function handleSearch(request, gd) {
* @returns {Promise<Response>} 注意如果从前台接收的id代表的项目不在目标gd盘下那么response会返回给前台一个空字符串"" * @returns {Promise<Response>} 注意如果从前台接收的id代表的项目不在目标gd盘下那么response会返回给前台一个空字符串""
*/ */
async function handleId2Path(request, gd) { async function handleId2Path(request, gd) {
const option = {status: 200, headers: {'Access-Control-Allow-Origin': '*'}}; const option = {
status: 200,
headers: { "Access-Control-Allow-Origin": "*" },
};
let body = await request.text(); let body = await request.text();
body = JSON.parse(body); body = JSON.parse(body);
let path = await gd.findPathById(body.id); let path = await gd.findPathById(body.id);
return new Response(path || '', option); return new Response(path || "", option);
} }
class googleDrive { class googleDrive {
@ -346,8 +384,8 @@ class googleDrive {
this.passwords = []; this.passwords = [];
// id <-> path // id <-> path
this.id_path_cache = {}; this.id_path_cache = {};
this.id_path_cache[this.root['id']] = '/'; this.id_path_cache[this.root["id"]] = "/";
this.paths["/"] = this.root['id']; this.paths["/"] = this.root["id"];
/*if (this.root['pass'] != "") { /*if (this.root['pass'] != "") {
this.passwords['/'] = this.root['pass']; this.passwords['/'] = this.root['pass'];
}*/ }*/
@ -370,9 +408,9 @@ class googleDrive {
})();*/ })();*/
// 等待 user_drive_real_root_id 只获取1次 // 等待 user_drive_real_root_id 只获取1次
if (authConfig.user_drive_real_root_id) return; if (authConfig.user_drive_real_root_id) return;
const root_obj = await (gds[0] || this).findItemById('root'); const root_obj = await (gds[0] || this).findItemById("root");
if (root_obj && root_obj.id) { if (root_obj && root_obj.id) {
authConfig.user_drive_real_root_id = root_obj.id authConfig.user_drive_real_root_id = root_obj.id;
} }
} }
@ -381,9 +419,9 @@ class googleDrive {
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
async initRootType() { async initRootType() {
const root_id = this.root['id']; const root_id = this.root["id"];
const types = CONSTS.gd_root_type; const types = CONSTS.gd_root_type;
if (root_id === 'root' || root_id === authConfig.user_drive_real_root_id) { if (root_id === "root" || root_id === authConfig.user_drive_real_root_id) {
this.root_type = types.user_drive; this.root_type = types.user_drive;
} else { } else {
const obj = await this.getShareDriveObjById(root_id); const obj = await this.getShareDriveObjById(root_id);
@ -397,65 +435,72 @@ class googleDrive {
* @returns {Response|null} * @returns {Response|null}
*/ */
basicAuthResponse(request) { basicAuthResponse(request) {
const user = this.root.user || '', const user = this.root.user || "",
pass = this.root.pass || '', pass = this.root.pass || "",
_401 = new Response('Unauthorized', { _401 = new Response("Unauthorized", {
headers: {'WWW-Authenticate': `Basic realm="goindex:drive:${this.order}"`}, headers: {
status: 401 "WWW-Authenticate": `Basic realm="goindex:drive:${this.order}"`,
},
status: 401,
}); });
if (user || pass) { if (user || pass) {
const auth = request.headers.get('Authorization') const auth = request.headers.get("Authorization");
if (auth) { if (auth) {
try { try {
const [received_user, received_pass] = atob(auth.split(' ').pop()).split(':'); const [received_user, received_pass] = atob(
return (received_user === user && received_pass === pass) ? null : _401; auth.split(" ").pop()
).split(":");
return received_user === user && received_pass === pass ? null : _401;
} catch (e) {} } catch (e) {}
} }
} else return null; } else return null;
return _401; return _401;
} }
async view(url, range = '', inline = true) { async view(url, range = "", inline = true) {
let requestOption = await this.requestOption(); let requestOption = await this.requestOption();
requestOption.headers['Range'] = range; requestOption.headers["Range"] = range;
let res = await fetch(url, requestOption); let res = await fetch(url, requestOption);
const {headers} = res = new Response(res.body, res) const { headers } = (res = new Response(res.body, res));
this.authConfig.enable_cors_file_down && headers.append('Access-Control-Allow-Origin', '*'); this.authConfig.enable_cors_file_down &&
inline === true && headers.set('Content-Disposition', 'inline'); headers.append("Access-Control-Allow-Origin", "*");
inline === true && headers.set("Content-Disposition", "inline");
return res; return res;
} }
async down(id, range = '', inline = false) { async down(id, range = "", inline = false) {
let url = `https://www.googleapis.com/drive/v3/files/${id}?alt=media`; let url = `https://www.googleapis.com/drive/v3/files/${id}?alt=media`;
let requestOption = await this.requestOption(); let requestOption = await this.requestOption();
requestOption.headers['Range'] = range; requestOption.headers["Range"] = range;
let res = await fetch(url, requestOption); let res = await fetch(url, requestOption);
const {headers} = res = new Response(res.body, res) const { headers } = (res = new Response(res.body, res));
this.authConfig.enable_cors_file_down && headers.append('Access-Control-Allow-Origin', '*'); this.authConfig.enable_cors_file_down &&
inline === true && headers.set('Content-Disposition', 'inline'); headers.append("Access-Control-Allow-Origin", "*");
inline === true && headers.set("Content-Disposition", "inline");
return res; return res;
} }
async file(path) { async file(path) {
if (typeof this.files[path] == 'undefined') { if (typeof this.files[path] == "undefined") {
this.files[path] = await this._file(path); this.files[path] = await this._file(path);
} }
return this.files[path]; return this.files[path];
} }
async _file(path) { async _file(path) {
let arr = path.split('/'); let arr = path.split("/");
let name = arr.pop(); let name = arr.pop();
name = decodeURIComponent(name).replace(/\'/g, "\\'"); name = decodeURIComponent(name).replace(/\'/g, "\\'");
let dir = arr.join('/') + '/'; let dir = arr.join("/") + "/";
// console.log(name, dir); // console.log(name, dir);
let parent = await this.findPathId(dir); let parent = await this.findPathId(dir);
// console.log(parent); // console.log(parent);
let url = 'https://www.googleapis.com/drive/v3/files'; let url = "https://www.googleapis.com/drive/v3/files";
let params = {'includeItemsFromAllDrives': true, 'supportsAllDrives': true}; let params = { includeItemsFromAllDrives: true, supportsAllDrives: true };
params.q = `'${parent}' in parents and name = '${name}' and trashed = false`; params.q = `'${parent}' in parents and name = '${name}' and trashed = false`;
params.fields = "files(id, name, mimeType, size ,createdTime, modifiedTime, iconLink, thumbnailLink)"; params.fields =
url += '?' + this.enQuery(params); "files(id, name, mimeType, size ,createdTime, modifiedTime, iconLink, thumbnailLink)";
url += "?" + this.enQuery(params);
let requestOption = await this.requestOption(); let requestOption = await this.requestOption();
let response = await fetch(url, requestOption); let response = await fetch(url, requestOption);
let obj = await response.json(); let obj = await response.json();
@ -470,15 +515,16 @@ class googleDrive {
this.path_children_cache = {}; this.path_children_cache = {};
} }
if (this.path_children_cache[path] if (
&& this.path_children_cache[path][page_index] this.path_children_cache[path] &&
&& this.path_children_cache[path][page_index].data this.path_children_cache[path][page_index] &&
this.path_children_cache[path][page_index].data
) { ) {
let child_obj = this.path_children_cache[path][page_index]; let child_obj = this.path_children_cache[path][page_index];
return { return {
nextPageToken: child_obj.nextPageToken || null, nextPageToken: child_obj.nextPageToken || null,
curPageIndex: page_index, curPageIndex: page_index,
data: child_obj.data data: child_obj.data,
}; };
} }
@ -488,18 +534,17 @@ class googleDrive {
// 对有多页的,进行缓存 // 对有多页的,进行缓存
if (result.nextPageToken && data.files) { if (result.nextPageToken && data.files) {
if (!Array.isArray(this.path_children_cache[path])) { if (!Array.isArray(this.path_children_cache[path])) {
this.path_children_cache[path] = [] this.path_children_cache[path] = [];
} }
this.path_children_cache[path][Number(result.curPageIndex)] = { this.path_children_cache[path][Number(result.curPageIndex)] = {
nextPageToken: result.nextPageToken, nextPageToken: result.nextPageToken,
data: data data: data,
}; };
} }
return result return result;
} }
async _ls(parent, page_token = null, page_index = 0) { async _ls(parent, page_token = null, page_index = 0) {
// console.log("_ls", parent); // console.log("_ls", parent);
@ -507,17 +552,18 @@ class googleDrive {
return null; return null;
} }
let obj; let obj;
let params = {'includeItemsFromAllDrives': true, 'supportsAllDrives': true}; let params = { includeItemsFromAllDrives: true, supportsAllDrives: true };
params.q = `'${parent}' in parents and trashed = false AND name !='.password'`; params.q = `'${parent}' in parents and trashed = false AND name !='.password'`;
params.orderBy = 'folder,name,modifiedTime desc'; params.orderBy = "folder,name,modifiedTime desc";
params.fields = "nextPageToken, files(id, name, mimeType, size , modifiedTime, thumbnailLink, description)"; params.fields =
"nextPageToken, files(id, name, mimeType, size , modifiedTime, thumbnailLink, description)";
params.pageSize = this.authConfig.files_list_page_size; params.pageSize = this.authConfig.files_list_page_size;
if (page_token) { if (page_token) {
params.pageToken = page_token; params.pageToken = page_token;
} }
let url = 'https://www.googleapis.com/drive/v3/files'; let url = "https://www.googleapis.com/drive/v3/files";
url += '?' + this.enQuery(params); url += "?" + this.enQuery(params);
let requestOption = await this.requestOption(); let requestOption = await this.requestOption();
let response = await fetch(url, requestOption); let response = await fetch(url, requestOption);
obj = await response.json(); obj = await response.json();
@ -525,7 +571,7 @@ class googleDrive {
return { return {
nextPageToken: obj.nextPageToken || null, nextPageToken: obj.nextPageToken || null,
curPageIndex: page_index, curPageIndex: page_index,
data: obj data: obj,
}; };
/*do { /*do {
@ -540,7 +586,6 @@ class googleDrive {
files.push(...obj.files); files.push(...obj.files);
pageToken = obj.nextPageToken; pageToken = obj.nextPageToken;
} while (pageToken);*/ } while (pageToken);*/
} }
async password(path) { async password(path) {
@ -550,7 +595,7 @@ class googleDrive {
// console.log("load", path, ".password", this.passwords[path]); // console.log("load", path, ".password", this.passwords[path]);
let file = await this.file(path + '.password'); let file = await this.file(path + ".password");
if (file == undefined) { if (file == undefined) {
this.passwords[path] = null; this.passwords[path] = null;
} else { } else {
@ -563,7 +608,6 @@ class googleDrive {
return this.passwords[path]; return this.passwords[path];
} }
/** /**
* 通过 id 获取 share drive 信息 * 通过 id 获取 share drive 信息
* @param any_id * @param any_id
@ -571,7 +615,7 @@ class googleDrive {
*/ */
async getShareDriveObjById(any_id) { async getShareDriveObjById(any_id) {
if (!any_id) return null; if (!any_id) return null;
if ('string' !== typeof any_id) return null; if ("string" !== typeof any_id) return null;
let url = `https://www.googleapis.com/drive/v3/drives/${any_id}`; let url = `https://www.googleapis.com/drive/v3/drives/${any_id}`;
let requestOption = await this.requestOption(); let requestOption = await this.requestOption();
@ -579,10 +623,9 @@ class googleDrive {
let obj = await res.json(); let obj = await res.json();
if (obj && obj.id) return obj; if (obj && obj.id) return obj;
return null return null;
} }
/** /**
* 搜索 * 搜索
* @returns {Promise<{data: null, nextPageToken: null, curPageIndex: number}>} * @returns {Promise<{data: null, nextPageToken: null, curPageIndex: number}>}
@ -595,7 +638,7 @@ class googleDrive {
const empty_result = { const empty_result = {
nextPageToken: null, nextPageToken: null,
curPageIndex: page_index, curPageIndex: page_index,
data: null data: null,
}; };
if (!is_user_drive && !is_share_drive) { if (!is_user_drive && !is_share_drive) {
@ -607,15 +650,17 @@ class googleDrive {
return empty_result; return empty_result;
} }
let words = keyword.split(/\s+/); let words = keyword.split(/\s+/);
let name_search_str = `name contains '${words.join("' AND name contains '")}'`; let name_search_str = `name contains '${words.join(
"' AND name contains '"
)}'`;
// corpora 为 user 是个人盘 ,为 drive 是团队盘。配合 driveId // corpora 为 user 是个人盘 ,为 drive 是团队盘。配合 driveId
let params = {}; let params = {};
if (is_user_drive) { if (is_user_drive) {
params.corpora = 'user' params.corpora = "user";
} }
if (is_share_drive) { if (is_share_drive) {
params.corpora = 'drive'; params.corpora = "drive";
params.driveId = this.root.id; params.driveId = this.root.id;
// This parameter will only be effective until June 1, 2020. Afterwards shared drive items will be included in the results. // This parameter will only be effective until June 1, 2020. Afterwards shared drive items will be included in the results.
params.includeItemsFromAllDrives = true; params.includeItemsFromAllDrives = true;
@ -625,12 +670,13 @@ class googleDrive {
params.pageToken = page_token; params.pageToken = page_token;
} }
params.q = `trashed = false AND name !='.password' AND (${name_search_str})`; params.q = `trashed = false AND name !='.password' AND (${name_search_str})`;
params.fields = "nextPageToken, files(id, name, mimeType, size , modifiedTime, thumbnailLink, description)"; params.fields =
"nextPageToken, files(id, name, mimeType, size , modifiedTime, thumbnailLink, description)";
params.pageSize = this.authConfig.search_result_list_page_size; params.pageSize = this.authConfig.search_result_list_page_size;
// params.orderBy = 'folder,name,modifiedTime desc'; // params.orderBy = 'folder,name,modifiedTime desc';
let url = 'https://www.googleapis.com/drive/v3/files'; let url = "https://www.googleapis.com/drive/v3/files";
url += '?' + this.enQuery(params); url += "?" + this.enQuery(params);
// console.log(params) // console.log(params)
let requestOption = await this.requestOption(); let requestOption = await this.requestOption();
let response = await fetch(url, requestOption); let response = await fetch(url, requestOption);
@ -639,11 +685,10 @@ class googleDrive {
return { return {
nextPageToken: res_obj.nextPageToken || null, nextPageToken: res_obj.nextPageToken || null,
curPageIndex: page_index, curPageIndex: page_index,
data: res_obj data: res_obj,
}; };
} }
/** /**
* 一层一层的向上获取这个文件或文件夹的上级文件夹的 file 对象注意会很慢 * 一层一层的向上获取这个文件或文件夹的上级文件夹的 file 对象注意会很慢
* 最多向上寻找到当前 gd 对象的根目录 (root id) * 最多向上寻找到当前 gd 对象的根目录 (root id)
@ -696,7 +741,7 @@ class googleDrive {
} }
await addItsFirstParent(child_obj); await addItsFirstParent(child_obj);
return meet_top ? parent_files : null return meet_top ? parent_files : null;
} }
/** /**
@ -710,20 +755,27 @@ class googleDrive {
} }
const p_files = await this.findParentFilesRecursion(child_id); const p_files = await this.findParentFilesRecursion(child_id);
if (!p_files || p_files.length < 1) return ''; if (!p_files || p_files.length < 1) return "";
let cache = []; let cache = [];
// 把查出来的每一级的path和id都缓存一下 // 把查出来的每一级的path和id都缓存一下
p_files.forEach((value, idx) => { p_files.forEach((value, idx) => {
const is_folder = idx === 0 ? (p_files[idx].mimeType === CONSTS.folder_mime_type) : true; const is_folder =
let path = '/' + p_files.slice(idx).map(it => it.name).reverse().join('/'); idx === 0 ? p_files[idx].mimeType === CONSTS.folder_mime_type : true;
if (is_folder) path += '/'; let path =
cache.push({id: p_files[idx].id, path: path}) "/" +
p_files
.slice(idx)
.map((it) => it.name)
.reverse()
.join("/");
if (is_folder) path += "/";
cache.push({ id: p_files[idx].id, path: path });
}); });
cache.forEach((obj) => { cache.forEach((obj) => {
this.id_path_cache[obj.id] = obj.path; this.id_path_cache[obj.id] = obj.path;
this.paths[obj.path] = obj.id this.paths[obj.path] = obj.id;
}); });
/*const is_folder = p_files[0].mimeType === CONSTS.folder_mime_type; /*const is_folder = p_files[0].mimeType === CONSTS.folder_mime_type;
@ -733,25 +785,26 @@ class googleDrive {
return cache[0].path; return cache[0].path;
} }
// 根据id获取file item // 根据id获取file item
async findItemById(id) { async findItemById(id) {
const is_user_drive = this.root_type === CONSTS.gd_root_type.user_drive; const is_user_drive = this.root_type === CONSTS.gd_root_type.user_drive;
let url = `https://www.googleapis.com/drive/v3/files/${id}?fields=${CONSTS.default_file_fields}${is_user_drive ? '' : '&supportsAllDrives=true'}`; let url = `https://www.googleapis.com/drive/v3/files/${id}?fields=${
CONSTS.default_file_fields
}${is_user_drive ? "" : "&supportsAllDrives=true"}`;
let requestOption = await this.requestOption(); let requestOption = await this.requestOption();
let res = await fetch(url, requestOption); let res = await fetch(url, requestOption);
return await res.json() return await res.json();
} }
async findPathId(path) { async findPathId(path) {
let c_path = '/'; let c_path = "/";
let c_id = this.paths[c_path]; let c_id = this.paths[c_path];
let arr = path.trim('/').split('/'); let arr = path.trim("/").split("/");
for (let name of arr) { for (let name of arr) {
c_path += name + '/'; c_path += name + "/";
if (typeof this.paths[c_path] == 'undefined') { if (typeof this.paths[c_path] == "undefined") {
let id = await this._findDirId(c_id, name); let id = await this._findDirId(c_id, name);
this.paths[c_path] = id; this.paths[c_path] = id;
} }
@ -774,11 +827,11 @@ class googleDrive {
return null; return null;
} }
let url = 'https://www.googleapis.com/drive/v3/files'; let url = "https://www.googleapis.com/drive/v3/files";
let params = {'includeItemsFromAllDrives': true, 'supportsAllDrives': true}; let params = { includeItemsFromAllDrives: true, supportsAllDrives: true };
params.q = `'${parent}' in parents and mimeType = 'application/vnd.google-apps.folder' and name = '${name}' and trashed = false`; params.q = `'${parent}' in parents and mimeType = 'application/vnd.google-apps.folder' and name = '${name}' and trashed = false`;
params.fields = "nextPageToken, files(id, name, mimeType)"; params.fields = "nextPageToken, files(id, name, mimeType)";
url += '?' + this.enQuery(params); url += "?" + this.enQuery(params);
let requestOption = await this.requestOption(); let requestOption = await this.requestOption();
let response = await fetch(url, requestOption); let response = await fetch(url, requestOption);
let obj = await response.json(); let obj = await response.json();
@ -790,7 +843,10 @@ class googleDrive {
async accessToken() { async accessToken() {
console.log("accessToken"); console.log("accessToken");
if (this.authConfig.expires == undefined || this.authConfig.expires < Date.now()) { if (
this.authConfig.expires == undefined ||
this.authConfig.expires < Date.now()
) {
const obj = await this.fetchAccessToken(); const obj = await this.fetchAccessToken();
if (obj.access_token != undefined) { if (obj.access_token != undefined) {
this.authConfig.accessToken = obj.access_token; this.authConfig.accessToken = obj.access_token;
@ -804,19 +860,19 @@ class googleDrive {
console.log("fetchAccessToken"); console.log("fetchAccessToken");
const url = "https://www.googleapis.com/oauth2/v4/token"; const url = "https://www.googleapis.com/oauth2/v4/token";
const headers = { const headers = {
'Content-Type': 'application/x-www-form-urlencoded' "Content-Type": "application/x-www-form-urlencoded",
}; };
const post_data = { const post_data = {
'client_id': this.authConfig.client_id, client_id: this.authConfig.client_id,
'client_secret': this.authConfig.client_secret, client_secret: this.authConfig.client_secret,
'refresh_token': this.authConfig.refresh_token, refresh_token: this.authConfig.refresh_token,
'grant_type': 'refresh_token' grant_type: "refresh_token",
} };
let requestOption = { let requestOption = {
'method': 'POST', method: "POST",
'headers': headers, headers: headers,
'body': this.enQuery(post_data) body: this.enQuery(post_data),
}; };
const response = await fetch(url, requestOption); const response = await fetch(url, requestOption);
@ -836,36 +892,39 @@ class googleDrive {
return response; return response;
} }
async requestOption(headers = {}, method = 'GET') { async requestOption(headers = {}, method = "GET") {
const accessToken = await this.accessToken(); const accessToken = await this.accessToken();
headers['authorization'] = 'Bearer ' + accessToken; headers["authorization"] = "Bearer " + accessToken;
return {'method': method, 'headers': headers}; return { method: method, headers: headers };
} }
enQuery(data) { enQuery(data) {
const ret = []; const ret = [];
for (let d in data) { for (let d in data) {
ret.push(encodeURIComponent(d) + '=' + encodeURIComponent(data[d])); ret.push(encodeURIComponent(d) + "=" + encodeURIComponent(data[d]));
} }
return ret.join('&'); return ret.join("&");
} }
sleep(ms) { sleep(ms) {
return new Promise(function(resolve, reject) { return new Promise(function(resolve, reject) {
let i = 0; let i = 0;
setTimeout(function() { setTimeout(function() {
console.log('sleep' + ms); console.log("sleep" + ms);
i++; i++;
if (i >= 2) reject(new Error('i>=2')); if (i >= 2) reject(new Error("i>=2"));
else resolve(i); else resolve(i);
}, ms); }, ms);
}) });
} }
} }
String.prototype.trim = function(char) { String.prototype.trim = function(char) {
if (char) { if (char) {
return this.replace(new RegExp('^\\' + char + '+|\\' + char + '+$', 'g'), ''); return this.replace(
new RegExp("^\\" + char + "+|\\" + char + "+$", "g"),
""
);
} }
return this.replace(/^\s+|\s+$/g, ''); return this.replace(/^\s+|\s+$/g, "");
}; };

2
package-lock.json generated
View File

@ -12677,7 +12677,7 @@
}, },
"webpack-bundle-analyzer": { "webpack-bundle-analyzer": {
"version": "3.7.0", "version": "3.7.0",
"resolved": "https://registry.npm.taobao.org/webpack-bundle-analyzer/download/webpack-bundle-analyzer-3.7.0.tgz?cache=0&sync_timestamp=1586846559504&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fwebpack-bundle-analyzer%2Fdownload%2Fwebpack-bundle-analyzer-3.7.0.tgz", "resolved": "https://registry.npm.taobao.org/webpack-bundle-analyzer/download/webpack-bundle-analyzer-3.7.0.tgz?cache=0&sync_timestamp=1586846510646&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fwebpack-bundle-analyzer%2Fdownload%2Fwebpack-bundle-analyzer-3.7.0.tgz",
"integrity": "sha1-hNpDTolEKJm4hNmtOORm0NsCpW8=", "integrity": "sha1-hNpDTolEKJm4hNmtOORm0NsCpW8=",
"dev": true, "dev": true,
"requires": { "requires": {

View File

@ -14,7 +14,7 @@
<title><%= htmlWebpackPlugin.options.title %></title> <title><%= htmlWebpackPlugin.options.title %></title>
<script> <script>
var authConfig = { var authConfig = {
version: '1.0.0', version: '1.1.0',
roots: [ roots: [
{ {
id: "0AEofxddwF4bAUk9PVA", id: "0AEofxddwF4bAUk9PVA",
@ -26,18 +26,31 @@
name: "PriveDrive", name: "PriveDrive",
pass: "", pass: "",
}, },
{
id: "1ZUli0boWXTpAedKDzE_IF9CWT10G6V86",
name: "folder1",
pass: "",
}
], ],
}; };
var themeOptions = { var themeOptions = {
// en/zh-chs/zh-cht // en/zh-chs/zh-cht
languages: 'en' languages: 'en',
render: {
head_md: false,
readme_md: false,
// 是否显示文件/文件夹描述(默认不显示)
// Show file/folder description or not (not shown by default)
desc: false,
}
} }
window.gdconfig = JSON.parse(JSON.stringify({ version: authConfig.version, themeOptions: themeOptions })); window.gdconfig = JSON.parse(JSON.stringify({ version: authConfig.version, themeOptions: themeOptions }));
window.themeOptions = themeOptions;
window.gds = JSON.parse( window.gds = JSON.parse(
JSON.stringify(authConfig.roots.map((it) => it.name)) JSON.stringify(authConfig.roots.map((it) => it.name))
); );
window.current_drive_order = 0; window.current_drive_order = 0;
// window.MODEL = { q: "the" }; // window.MODEL = { q: "the",root_type: 1 };
</script> </script>
<!-- 使用 CDN 加速的 JS 文件,配置在 vue.config.js 下 --> <!-- 使用 CDN 加速的 JS 文件,配置在 vue.config.js 下 -->
<% for (var i in htmlWebpackPlugin.options.cdn&&htmlWebpackPlugin.options.cdn.js) { %> <% for (var i in htmlWebpackPlugin.options.cdn&&htmlWebpackPlugin.options.cdn.js) { %>

View File

@ -1,37 +1,34 @@
import axios from "@utils/axios"; import axios from "@utils/axios";
let Base64 = require("js-base64").Base64; let Base64 = require("js-base64").Base64;
// const exts = [ const text_exts = [
// "html", "html",
// "php", "php",
// "css", "css",
// "go", "go",
// "java", "java",
// "js", "js",
// "json", "json",
// "py", "txt",
// "txt", "sh",
// "sh", "md",
// "md", ];
// "mp4", const video_exts = ["mp4", "webm", "mkv", "m3u8"];
// "webm", const image_exts = ["bmp", "jpg", "jpeg", "png", "gif"];
// "mkv", const pdf_exts = ["pdf"];
// "bmp",
// "jpg",
// "jpeg",
// "png",
// "gif",
// ];
export const encodePath = (path) => { export const encodePath = (path) => {
return path.replace(/(.*)/, (p1, p2) => { return path.replace(/(.*)/, (p1, p2) => {
return p2.replace().replace(/\//g, "%2F").replace(/#/g, "%23") return p2
}) .replace()
.replace(/\//g, "%2F")
.replace(/#/g, "%23");
});
//return path.replace().replace("/", "%2F").replace("#", "%23") //return path.replace().replace("/", "%2F").replace("#", "%23")
} };
export const checkoutPath = (path, file) => { export const checkoutPath = (path, file) => {
path = encodePath(path) path = encodePath(path);
if (file.mimeType === "application/vnd.google-apps.folder") { if (file.mimeType === "application/vnd.google-apps.folder") {
if (path.substr(-1) !== "/") { if (path.substr(-1) !== "/") {
path += "/"; path += "/";
@ -47,25 +44,25 @@ export const checkView = (path) => {
.pop() .pop()
.toLowerCase(); .toLowerCase();
let base64Path = encode64(path); let base64Path = encode64(path);
if ("|html|php|css|go|java|js|json|txt|sh|md|".indexOf(`|${ext}|`) >= 0) { if (text_exts.indexOf(`${ext}`) != -1) {
path = path.replace(/\/(\d+:)\/.*/, (p1, p2) => { path = path.replace(/\/(\d+:)\/.*/, (p1, p2) => {
return `/${p2}text/${base64Path}`; return `/${p2}text/${base64Path}`;
}); });
} }
if ("|pdf|".indexOf(`|${ext}|`) >= 0) { if (pdf_exts.indexOf(`${ext}`) != -1) {
path = path.replace(/\/(\d+:)\/.*/, (p1, p2) => { path = path.replace(/\/(\d+:)\/.*/, (p1, p2) => {
return `/${p2}pdf/${base64Path}`; return `/${p2}pdf/${base64Path}`;
}); });
} }
if ("|mp4|webm|mkv|".indexOf(`|${ext}|`) >= 0) { if (video_exts.indexOf(`${ext}`) != -1) {
path = path.replace(/\/(\d+:)\/.*/, (p1, p2) => { path = path.replace(/\/(\d+:)\/.*/, (p1, p2) => {
return `/${p2}video/${base64Path}`; return `/${p2}video/${base64Path}`;
}); });
} }
if ("|bmp|jpg|jpeg|png|gif|".indexOf(`|${ext}|`) >= 0) { if (image_exts.indexOf(`${ext}`) != -1) {
path = path.replace(/\/(\d+:)\/.*/, (p1, p2) => { path = path.replace(/\/(\d+:)\/.*/, (p1, p2) => {
return `/${p2}image/${base64Path}`; return `/${p2}image/${base64Path}`;
}); });
@ -73,27 +70,16 @@ export const checkView = (path) => {
return path; return path;
}; };
export const getQueryString = (path, param) => { export const checkExtends = (path) => {
if (!path) { let name = path.split("/").pop();
return ""; let ext = name
} .split(".")
var args = getURLParameters(path); .pop()
return args[param] ? args[param] : ""; .toLowerCase();
let exts = text_exts.concat(...video_exts,...image_exts,...pdf_exts);
return exts.indexOf(`${ext}`) != -1;
}; };
export const getURLParameters = (url) =>
url
.match(/([^?=&]+)(=([^&]*))/g)
.reduce(
(a, v) => (
(a[v.slice(0, v.indexOf("="))] = v.slice(v.indexOf("=") + 1)), a
), {}
);
// console.log(getURLParameters("/Movies/xx.mp4?a=view&y=123"));
//console.log(getQueryString("/Movies/xx.mp4?a=view&y=123", "y"));
export const encode64 = (str) => { export const encode64 = (str) => {
return Base64.encodeURI(str); return Base64.encodeURI(str);
}; };
@ -182,7 +168,6 @@ export function formatFileSize(bytes) {
return bytes; return bytes;
} }
/** /**
* @param {Number String Date} * @param {Number String Date}
* @param {String} 'YYYY-MM-DD HH:mm:ss EEE' (Y)(M)(D)12小时(h)24小时(H)(m)(s)毫秒(S)(E)季度(q) * @param {String} 'YYYY-MM-DD HH:mm:ss EEE' (Y)(M)(D)12小时(h)24小时(H)(m)(s)毫秒(S)(E)季度(q)
@ -190,42 +175,55 @@ export function formatFileSize(bytes) {
* @example XDate.format(new Date(), "YYYY-MM-DD") ==> 2017-08-23 * @example XDate.format(new Date(), "YYYY-MM-DD") ==> 2017-08-23
*/ */
export function formatDate(date, fmt) { export function formatDate(date, fmt) {
fmt = fmt || 'YYYY-MM-DD HH:mm:ss'; fmt = fmt || "YYYY-MM-DD HH:mm:ss";
if (typeof date === 'string') { if (typeof date === "string") {
// date = new Date(date.replace(/-/g, '/')) // date = new Date(date.replace(/-/g, '/'))
date = new Date(date) date = new Date(date);
} }
if (typeof date === 'number') { if (typeof date === "number") {
date = new Date(date) date = new Date(date);
} }
var o = { var o = {
'M+': date.getMonth() + 1, "M+": date.getMonth() + 1,
'D+': date.getDate(), "D+": date.getDate(),
'h+': date.getHours() % 12 === 0 ? 12 : date.getHours() % 12, "h+": date.getHours() % 12 === 0 ? 12 : date.getHours() % 12,
'H+': date.getHours(), "H+": date.getHours(),
'm+': date.getMinutes(), "m+": date.getMinutes(),
's+': date.getSeconds(), "s+": date.getSeconds(),
'q+': Math.floor((date.getMonth() + 3) / 3), "q+": Math.floor((date.getMonth() + 3) / 3),
'S': date.getMilliseconds() S: date.getMilliseconds(),
} };
var week = { var week = {
'0': '\u65e5', "0": "\u65e5",
'1': '\u4e00', "1": "\u4e00",
'2': '\u4e8c', "2": "\u4e8c",
'3': '\u4e09', "3": "\u4e09",
'4': '\u56db', "4": "\u56db",
'5': '\u4e94', "5": "\u4e94",
'6': '\u516d' "6": "\u516d",
} };
if (/(Y+)/.test(fmt)) { if (/(Y+)/.test(fmt)) {
fmt = fmt.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length)) fmt = fmt.replace(
RegExp.$1,
(date.getFullYear() + "").substr(4 - RegExp.$1.length)
);
} }
if (/(E+)/.test(fmt)) { if (/(E+)/.test(fmt)) {
fmt = fmt.replace(RegExp.$1, ((RegExp.$1.length > 1) ? (RegExp.$1.length > 2 ? '\u661f\u671f' : '\u5468') : '') + week[date.getDay() + '']) fmt = fmt.replace(
RegExp.$1,
(RegExp.$1.length > 1
? RegExp.$1.length > 2
? "\u661f\u671f"
: "\u5468"
: "") + week[date.getDay() + ""]
);
} }
for (var k in o) { for (var k in o) {
if (new RegExp('(' + k + ')').test(fmt)) { if (new RegExp("(" + k + ")").test(fmt)) {
fmt = fmt.replace(RegExp.$1, (RegExp.$1.length === 1) ? (o[k]) : (('00' + o[k]).substr(('' + o[k]).length))) fmt = fmt.replace(
RegExp.$1,
RegExp.$1.length === 1 ? o[k] : ("00" + o[k]).substr(("" + o[k]).length)
);
} }
} }
return fmt; return fmt;

View File

@ -108,6 +108,13 @@ export default {
// height: 100%; // height: 100%;
padding: 10px 0.75em; padding: 10px 0.75em;
} }
.level-left {
width: 95%;
.level-item {
display: initial;
width: 100%;
}
}
.level-right { .level-right {
.level-item { .level-item {
// border-radius: 50%; // border-radius: 50%;

View File

@ -42,7 +42,7 @@
<div class="navbar-end"> <div class="navbar-end">
<!-- is-hidden-desktop --> <!-- is-hidden-desktop -->
<div class="navbar-item"> <div class="navbar-item" v-show="showSearch">
<div class="field is-grouped"> <div class="field is-grouped">
<p class="control has-icons-left is-dark" style="width:100%;"> <p class="control has-icons-left is-dark" style="width:100%;">
<input <input
@ -71,7 +71,10 @@
<i class="fab fa-github"></i> <i class="fab fa-github"></i>
</a> </a>
<header-setting /> <header-setting />
<a class="navbar-item is-hidden-desktop" @click.stop="$refs.viewMode.toggleMode"> <a
class="navbar-item is-hidden-desktop"
@click.stop="$refs.viewMode.toggleMode"
>
<view-mode ref="viewMode" /> <view-mode ref="viewMode" />
</a> </a>
</div> </div>
@ -144,6 +147,10 @@ export default {
getCurrGD() { getCurrGD() {
return this.gds.filter((item) => item.name !== this.currgd.name); return this.gds.filter((item) => item.name !== this.currgd.name);
}, },
showSearch() {
//
return window.MODEL ? window.MODEL.root_type < 2 : true
},
}, },
watch: { watch: {
"$route.params.id": "chooseGD", "$route.params.id": "chooseGD",

View File

@ -1,6 +1,6 @@
<template> <template>
<div> <div>
<headmd :option="headmd" v-if="headmd.display"></headmd> <headmd :option="headmd" v-if="renderHeadMD && headmd.display"></headmd>
<div <div
class="golist" class="golist"
v-loading="loading" v-loading="loading"
@ -8,7 +8,13 @@
infinite-scroll-disabled="busy" infinite-scroll-disabled="busy"
infinite-scroll-distance="10" infinite-scroll-distance="10"
> >
<list-view :data="buildFiles" v-if="mode === 'list'" :icons="getIcon" :go="go" :copy="copy" /> <list-view
:data="buildFiles"
v-if="mode === 'list'"
:icons="getIcon"
:go="go"
:copy="copy"
/>
<grid-view <grid-view
class="g2-content" class="g2-content"
:data="buildFiles" :data="buildFiles"
@ -17,7 +23,10 @@
:go="go" :go="go"
:thum="thum" :thum="thum"
/> />
<div v-show="files.length === 0" class="has-text-centered no-content"></div> <div
v-show="files.length === 0"
class="has-text-centered no-content"
></div>
<center> <center>
<div :class="!busy ? 'is-hidden' : ''"> <div :class="!busy ? 'is-hidden' : ''">
<i class="fa fa-spinner fa-pulse fa-2x fa-fw"></i> <i class="fa fa-spinner fa-pulse fa-2x fa-fw"></i>
@ -28,8 +37,13 @@
</span>--> </span>-->
</center> </center>
</div> </div>
<div class="is-divider" :data-content="$t('list.total')+' '+files.length+' ' + $t('list.item')"></div> <div
<readmemd :option="readmemd" v-if="readmemd.display"></readmemd> class="is-divider"
:data-content="
$t('list.total') + ' ' + files.length + ' ' + $t('list.item')
"
></div>
<readmemd :option="readmemd" v-if="renderReadMeMD && readmemd.display"></readmemd>
<viewer <viewer
v-if="viewer && images && images.length > 0" v-if="viewer && images && images.length > 0"
@ -57,6 +71,7 @@ import {
formatFileSize, formatFileSize,
checkoutPath, checkoutPath,
checkView, checkView,
checkExtends,
} from "@utils/AcrouUtil"; } from "@utils/AcrouUtil";
import axios from "@/utils/axios"; import axios from "@/utils/axios";
import { mapState } from "vuex"; import { mapState } from "vuex";
@ -139,6 +154,12 @@ export default {
(file) => file.mimeType.indexOf("image") != -1 (file) => file.mimeType.indexOf("image") != -1
); );
}, },
renderHeadMD() {
return window.themeOptions.render.head_md || false;
},
renderReadMeMD() {
return window.themeOptions.render.readme_md || false;
},
}, },
created() { created() {
this.render(); this.render();
@ -238,7 +259,7 @@ export default {
if (file.mimeType.indexOf("image") != -1) { if (file.mimeType.indexOf("image") != -1) {
this.viewer = true; this.viewer = true;
this.$nextTick(() => { this.$nextTick(() => {
let index = this.images.findIndex(item => item.path === file.path) let index = this.images.findIndex((item) => item.path === file.path);
this.$viewer.view(index); this.$viewer.view(index);
}); });
return; return;
@ -253,7 +274,7 @@ export default {
window.open(path); window.open(path);
return; return;
} }
if (target === "down") { if (target === "down" || (!checkExtends(path) && !file.isFolder)) {
let temp_path = this.$route.params.path ? this.$route.params.path : ""; let temp_path = this.$route.params.path ? this.$route.params.path : "";
location.href = `/${this.$route.params.id}:down/${temp_path}/${file.name}`; location.href = `/${this.$route.params.id}:down/${temp_path}/${file.name}`;
return; return;

View File

@ -32,7 +32,11 @@
<use :xlink:href="icons(file.mimeType)" /> <use :xlink:href="icons(file.mimeType)" />
</svg> </svg>
{{ file.name }} {{ file.name }}
<span class="has-text-grey g2-file-desc" v-html="file.description"></span> <span
class="has-text-grey g2-file-desc"
v-if="isShowDesc"
v-html="file.description"
></span>
</td> </td>
<td class="is-hidden-mobile is-hidden-touch"> <td class="is-hidden-mobile is-hidden-touch">
{{ file.modifiedTime }} {{ file.modifiedTime }}
@ -107,6 +111,9 @@ export default {
}, },
]; ];
}, },
isShowDesc() {
return window.themeOptions.render.desc || false;
},
}, },
}; };
</script> </script>