This commit is contained in:
fanxb 2023-03-07 00:06:59 +08:00
parent 8d78127e4d
commit 2cf41a9c18
13 changed files with 515 additions and 385 deletions

8
openRenamerBackend/.idea/.gitignore generated vendored Normal file
View File

@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

8
openRenamerBackend/.idea/modules.xml generated Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/openRenamerBackend.iml" filepath="$PROJECT_DIR$/.idea/openRenamerBackend.iml" />
</modules>
</component>
</project>

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/dist" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

6
openRenamerBackend/.idea/vcs.xml generated Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
</component>
</project>

View File

@ -1,49 +0,0 @@
import { Context } from "koa";
import FileService from "../service/FileService";
import config from "../config";
const router = {};
/**
*
*/
router["GET /file/query"] = async function (ctx: Context) {
ctx.body = await FileService.readPath(ctx.query.path as string, ctx.query.showHidden === '1');
};
/**
*windows
*/
router['GET /file/isWindows'] = async function (ctx: Context) {
ctx.body = config.isWindows;
}
/**
*
*/
router["GET /file/path/exist"] = async function (ctx: Context) {
ctx.body = await FileService.checkExist(ctx.query.path as string);
};
/**
*
*/
router["POST /file/path/save"] = async function (ctx: Context) {
ctx.body = await FileService.savePath(ctx.request.body);
};
/**
*
*/
router["GET /file/path"] = async function (ctx: Context) {
ctx.body = await FileService.getSaveList();
};
/**
*
*/
router["DELETE /file/path/delete"] = async function (ctx: Context) {
ctx.body = await FileService.deleteOne(ctx.query.id);
};
export default router;

View File

@ -1,4 +1,4 @@
import { Context } from "koa"; import {Context} from "koa";
import FileService from "../service/FileService"; import FileService from "../service/FileService";
import config from "../config"; import config from "../config";
@ -11,6 +11,13 @@ router["GET /file/query"] = async function (ctx: Context) {
ctx.body = await FileService.readPath(ctx.query.path as string, ctx.query.showHidden === '1'); ctx.body = await FileService.readPath(ctx.query.path as string, ctx.query.showHidden === '1');
}; };
/**
*
*/
router["POST /file/recursionQuery"] = async function (ctx: Context) {
ctx.body = await FileService.readRecursion(ctx.request.body);
};
/** /**
*windows *windows
*/ */
@ -46,4 +53,19 @@ router["DELETE /file/path/delete"] = async function (ctx: Context) {
ctx.body = await FileService.deleteOne(ctx.query.id); ctx.body = await FileService.deleteOne(ctx.query.id);
}; };
/**
* delete file batch
*/
router["POST /file/deleteBatch"] = async function (ctx: Context) {
ctx.body = await FileService.deleteBatch(ctx.request.body);
};
/**
* rename file
*/
router["POST /file/rename"] = async function (ctx: Context) {
await FileService.rename(ctx.request.body.source, ctx.request.body.target);
ctx.body = "";
};
export default router; export default router;

View File

@ -4,86 +4,88 @@ import path from 'path';
let pattern = new RegExp(/s(eason)?(\d+)/); let pattern = new RegExp(/s(eason)?(\d+)/);
let eNumPatternArr = [new RegExp(/e[p]?(\d+)/), new RegExp(/[\(\[](\d+)[\)\]]/), new RegExp(/[\.-](\d+)/), new RegExp(/(\d+)/)]; let eNumPatternArr = [new RegExp(/ep?(\d+)/), new RegExp(/[\(\[](\d+)[\)\]]/), new RegExp(/[\.-](\d+)/), new RegExp(/(\d+)/)];
let resolutionPattern = new RegExp(/(\d{3,}[pP])/); let resolutionPattern = new RegExp(/(\d{3,}[pP])/);
let resolutionArr = ['1k', '1K', '2k', '2K', '4k', '4K', '8k', '8K']; let resolutionArr = ['1k', '1K', '2k', '2K', '4k', '4K', '8k', '8K'];
let charSet = new Set([' ', '[', '.', '(', '']); let charSet = new Set([' ', '[', '.', '(', '']);
export default class InsertRule implements RuleInterface { export default class InsertRule implements RuleInterface {
/** /**
* seasonname/ * seasonname/
*/ */
type: string; type: string;
/** /**
* *
*/ */
frontAdd: string; frontAdd: string;
/** /**
* *
*/ */
endAdd: string; endAdd: string;
eNumWidth: number; eNumWidth: number;
constructor(data: any) { constructor(data: any) {
this.type = data.type; this.type = data.type;
this.frontAdd = data.frontAdd; this.frontAdd = data.frontAdd;
this.endAdd = data.endAdd; this.endAdd = data.endAdd;
} }
deal(file: FileObj): void { deal(file: FileObj): void {
//识别到的内容 //识别到的内容
let getStr = null; let getStr = null;
let patternRes = path.basename(file.path).replace(/[ ]+/, "").toLocaleLowerCase().match(pattern); let patternRes = path.basename(file.path).replace(/[ ]+/, "").toLocaleLowerCase().match(pattern);
if (this.type === 'season') { if (this.type === 'season') {
if (patternRes && patternRes[2]) { if (patternRes && patternRes[2]) {
getStr = patternRes[2]; getStr = patternRes[2];
} }
} else if (this.type === 'name') { } else if (this.type === 'name') {
let originName = null; let originName = null;
if (patternRes && patternRes[2]) { if (patternRes && patternRes[2]) {
//说明是剧集,取父文件夹的父文件夹名称 //说明是剧集,取父文件夹的父文件夹名称
originName = path.basename(path.resolve(file.path, '..')); originName = path.basename(path.resolve(file.path, '..'));
} else { } else {
//说明是电影 //说明是电影
originName = path.basename(file.path); originName = path.basename(file.path);
} }
getStr = ''; getStr = '';
for (let i = 0; i < originName.length; i++) { for (let i = 0; i < originName.length; i++) {
let char = originName.charAt(i); let char = originName.charAt(i);
if (charSet.has(char)) { if (charSet.has(char)) {
break; break;
} }
getStr += char; getStr += char;
} }
} else if (this.type === 'eNum') { } else if (this.type === 'eNum') {
let lowName = file.originName.toLocaleLowerCase().replace(/ /g, ''); let lowName = file.originName.toLocaleLowerCase().replace(/ /g, '')
for (let i in eNumPatternArr) { .replace(/\d+[a-z]/g, '')//去除4k,1080p等
let patternRes = lowName.match(eNumPatternArr[i]); .replace(/[xh]\d+/g, '')//去除x264,h264等 ;
if (patternRes && patternRes.length > 1) { for (let i in eNumPatternArr) {
getStr = patternRes[1]; let patternRes = lowName.match(eNumPatternArr[i]);
for (let i = 0; i < this.eNumWidth - getStr.length; i++) { if (patternRes && patternRes.length > 1) {
getStr = '0' + getStr; getStr = patternRes[1];
} for (let i = 0; i < this.eNumWidth - getStr.length; i++) {
break; getStr = '0' + getStr;
} }
} break;
} else if (this.type === 'resolution') { }
let res = file.originName.match(resolutionPattern); }
if (res && res.length > 1) { } else if (this.type === 'resolution') {
getStr = res[1]; let res = file.originName.match(resolutionPattern);
} else { if (res && res.length > 1) {
for (let i = 0; i < resolutionArr.length; i++) { getStr = res[1];
if (file.originName.indexOf(resolutionArr[i]) > -1) { } else {
getStr = resolutionArr[i]; for (let i = 0; i < resolutionArr.length; i++) {
break; if (file.originName.indexOf(resolutionArr[i]) > -1) {
} getStr = resolutionArr[i];
} break;
} }
} }
if (getStr && getStr.length > 0) { }
file.realName = file.realName + this.frontAdd + getStr + this.endAdd; }
file.name = file.realName + file.expandName; if (getStr && getStr.length > 0) {
} file.realName = file.realName + this.frontAdd + getStr + this.endAdd;
} file.name = file.realName + file.expandName;
}
}
} }

View File

@ -9,14 +9,15 @@ import handleError from "./middleware/handleError";
import init from "./middleware/init"; import init from "./middleware/init";
import SqliteUtil from './util/SqliteHelper'; import SqliteUtil from './util/SqliteHelper';
import log from './util/LogUtil'; import log from './util/LogUtil';
import { updateQbInfo } from './util/QbApiUtil'; import {updateQbInfo} from './util/QbApiUtil';
console.log(config); console.log(config);
const app = new koa(); const app = new koa();
let router = new Router({ let router = new Router({
prefix: config.urlPrefix prefix: config.urlPrefix
}); });
app.use(require('koa-static')(path.join(config.rootPath, 'static'))); app.use(require('koa-static')(path.join(config.rootPath, 'static')));
@ -30,12 +31,12 @@ app.use(handleError);
app.use(RouterMW(router, path.join(config.rootPath, "dist/api"))); app.use(RouterMW(router, path.join(config.rootPath, "dist/api")));
(async () => { (async () => {
await SqliteUtil.createPool(); await SqliteUtil.createPool();
await updateQbInfo(null, null); await updateQbInfo(null, null);
app.listen(config.port); app.listen(config.port);
log.info(`server listened `, config.port); log.info(`server listened `, config.port);
})(); })();
app.on("error", (error) => { app.on("error", (error) => {
console.error(error); console.error(error);
}) })

View File

@ -6,136 +6,190 @@ import ProcessHelper from '../util/ProcesHelper';
import FileObj from '../entity/vo/FileObj'; import FileObj from '../entity/vo/FileObj';
import SavePathDao from '../dao/SavePathDao'; import SavePathDao from '../dao/SavePathDao';
import SavePath from '../entity/po/SavePath'; import SavePath from '../entity/po/SavePath';
import ErrorHelper from "../util/ErrorHelper";
let numberSet = new Set(["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]); let numberSet = new Set(["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]);
class FileService { class FileService {
static async readPath(pathStr: string, showHidden: boolean): Promise<Array<FileObj>> { static async readPath(pathStr: string, showHidden: boolean): Promise<Array<FileObj>> {
pathStr = decodeURIComponent(pathStr); pathStr = decodeURIComponent(pathStr);
let fileList = new Array(); let fileList = [];
if (pathStr.trim().length == 0) { if (pathStr.trim().length == 0) {
//获取根目录路径 //获取根目录路径
if (config.isWindows) { if (config.isWindows) {
//windows下 //windows下
let std: string = (await ProcessHelper.exec('wmic logicaldisk get caption')).replace('Caption', ''); let std: string = (await ProcessHelper.exec('wmic logicaldisk get caption')).replace('Caption', '');
fileList = std fileList = std
.split('\r\n') .split('\r\n')
.filter((item) => item.trim().length > 0) .filter((item) => item.trim().length > 0)
.map((item) => item.trim()); .map((item) => item.trim());
} else { } else {
//linux下 //linux下
pathStr = '/'; pathStr = '/';
fileList = await fs.readdir(pathStr); fileList = await fs.readdir(pathStr);
} }
} else { } else {
if (!(fs.pathExists(pathStr))) { if (!(fs.pathExists(pathStr))) {
throw new Error("路径不存在"); throw new Error("路径不存在");
} }
fileList = await fs.readdir(pathStr); fileList = await fs.readdir(pathStr);
} }
let folderList: Array<FileObj> = new Array(); let folderList: Array<FileObj> = [];
let files: Array<FileObj> = new Array(); let files: Array<FileObj> = [];
for (let index in fileList) { for (let index in fileList) {
try { try {
let stat = await fs.stat(path.join(pathStr, fileList[index])); let stat = await fs.stat(path.join(pathStr, fileList[index]));
if (fileList[index].startsWith('.')) { if (fileList[index].startsWith('.')) {
if (showHidden) { if (showHidden) {
(stat.isDirectory() ? folderList : files).push( (stat.isDirectory() ? folderList : files).push(
new FileObj(fileList[index], pathStr, stat.isDirectory(), stat.birthtime.getTime(), stat.mtime.getTime()), new FileObj(fileList[index], pathStr, stat.isDirectory(), stat.birthtime.getTime(), stat.mtime.getTime()),
); );
} }
} else { } else {
(stat.isDirectory() ? folderList : files).push( (stat.isDirectory() ? folderList : files).push(
new FileObj(fileList[index], pathStr, stat.isDirectory(), stat.birthtime.getTime(), stat.mtime.getTime()), new FileObj(fileList[index], pathStr, stat.isDirectory(), stat.birthtime.getTime(), stat.mtime.getTime()),
); );
} }
} catch (e) { } catch (e) {
console.error(e); console.error(e);
} }
} }
folderList.sort((a, b) => FileService.compareStr(a.name, b.name)).push(...files.sort((a, b) => FileService.compareStr(a.name, b.name))); folderList.sort((a, b) => FileService.compareStr(a.name, b.name)).push(...files.sort((a, b) => FileService.compareStr(a.name, b.name)));
return folderList; return folderList;
} }
static async checkExist(pathStr: string) { /**
return await fs.pathExists(pathStr); *
} */
static async readRecursion(folders: Array<FileObj>): Promise<Array<FileObj>> {
let res = [];
await this.readDirRecursion(res, folders, 1);
return res;
}
/** private static async readDirRecursion(res: Array<FileObj>, folders: Array<FileObj>, depth: number): Promise<void> {
* if (depth > 10) {
* @param saveObj throw ErrorHelper.Error400("递归读取超过10层,强制结束");
* @returns }
*/ if (folders == null || folders.length == 0) {
static async savePath(saveObj: SavePath) { return;
await SavePathDao.addOne(saveObj); }
return saveObj; for (let i in folders) {
} let file = folders[i];
if (!file.isFolder) {
res.push(file);
} else {
let filePath = path.join(file.path, file.name);
let temp = (await fs.readdir(filePath)).map(item => {
let stat = fs.statSync(path.join(filePath, item));
return new FileObj(item, filePath, stat.isDirectory(), stat.birthtime.getTime(), stat.mtime.getTime());
});
await FileService.readDirRecursion(res, temp, depth + 1);
}
}
}
/** static async checkExist(pathStr: string) {
* return await fs.pathExists(pathStr);
* @returns }
*/
static async getSaveList() {
return await SavePathDao.getAll();
}
/** /**
* *
* @param id * @param saveObj
* @returns * @returns
*/ */
static async deleteOne(id) { static async savePath(saveObj: SavePath) {
return await SavePathDao.delete(id); await SavePathDao.addOne(saveObj);
} return saveObj;
}
/** /**
* *
* @param a str * @returns
* @param b str */
*/ static async getSaveList() {
static compareStr(a: string, b: string) { return await SavePathDao.getAll();
let an = a.length; }
let bn = b.length;
for (let i = 0; i < an;) {
let charA = FileService.readChar(a, i, an);
let charB = FileService.readChar(b, i, bn);
if (charB.length == 0) {
return 1;
}
if (charA !== charB) {
//读取字符串不相等说明可以得到排序结果
//如果都为数字,按照数字的比较方法,否则按照字符串比较
return numberSet.has(charA.charAt(0)) && numberSet.has(charB.charAt(0)) ? Number(charA) - Number(charB) : charA.localeCompare(charB);
}
i += charA.length;
}
//排到最后都没分结果说明相等
return 0;
}
/** /**
* *
* @param a a * @param id
* @param n * @returns
*/ */
static readChar(a: string, i: number, n: number) { static async deleteOne(id) {
let res = ""; return await SavePathDao.delete(id);
for (; i < n; i++) { }
let char = a.charAt(i);
if (numberSet.has(char)) { /**
//如果当前字符是数字,添加到结果中 *
res += char; * @param a str
} else { * @param b str
//如果不为数字但是为第一个字符直接返回否则返回res */
if (res.length == 0) { static compareStr(a: string, b: string) {
return char; let an = a.length;
} else { let bn = b.length;
return res; for (let i = 0; i < an;) {
} let charA = FileService.readChar(a, i, an);
} let charB = FileService.readChar(b, i, bn);
} if (charB.length == 0) {
return res; return 1;
} }
if (charA !== charB) {
//读取字符串不相等说明可以得到排序结果
//如果都为数字,按照数字的比较方法,否则按照字符串比较
return numberSet.has(charA.charAt(0)) && numberSet.has(charB.charAt(0)) ? Number(charA) - Number(charB) : charA.localeCompare(charB);
}
i += charA.length;
}
//排到最后都没分结果说明相等
return 0;
}
/**
*
* @param a a
* @param n
*/
static readChar(a: string, i: number, n: number) {
let res = "";
for (; i < n; i++) {
let char = a.charAt(i);
if (numberSet.has(char)) {
//如果当前字符是数字,添加到结果中
res += char;
} else {
//如果不为数字但是为第一个字符直接返回否则返回res
if (res.length == 0) {
return char;
} else {
return res;
}
}
}
return res;
}
/**
* delete batch
* @param files files
*/
static async deleteBatch(files: Array<FileObj>): Promise<void> {
if (files == null || files.length == 0) {
return;
}
for (let i in files) {
await fs.remove(path.join(files[i].path, files[i].name));
}
}
/**
* rename file from source to target
* @param source sourceFile
* @param target targetFile
*/
static async rename(source: FileObj, target: FileObj): Promise<void> {
await fs.rename(path.join(source.path, source.name), path.join(target.path, target.name));
}
} }
export default FileService; export default FileService;

View File

@ -1,9 +1,9 @@
<template> <template>
<div class="app"> <div class="app">
<el-menu :default-active="activeIndex" mode="horizontal" background-color="#545c64" text-color="#fff" <el-menu :default-active="activeIndex" mode="horizontal" background-color="#545c64" text-color="#fff"
active-text-color="#ffd04b" router> active-text-color="#ffd04b" router>
<el-menu-item index="/">重命名</el-menu-item> <el-menu-item index="/">重命名</el-menu-item>
<el-menu-item index="/auto">自动化</el-menu-item> <!-- <el-menu-item index="/auto">自动化</el-menu-item>-->
<!-- <el-sub-menu index="/download"> <!-- <el-sub-menu index="/download">
<template #title>bt下载</template> <template #title>bt下载</template>
<el-menu-item index="/download/center">下载中心</el-menu-item> <el-menu-item index="/download/center">下载中心</el-menu-item>
@ -11,7 +11,7 @@
</el-sub-menu> --> </el-sub-menu> -->
</el-menu> </el-menu>
<div class="content"> <div class="content">
<router-view /> <router-view/>
</div> </div>
<div class="footer">版本{{ version }}&nbsp;&nbsp;开源地址:<a <div class="footer">版本{{ version }}&nbsp;&nbsp;开源地址:<a
href="https://github.com/FleyX/open-renamer">open-renamer</a></div> href="https://github.com/FleyX/open-renamer">open-renamer</a></div>
@ -20,22 +20,23 @@
<script> <script>
import httpUtil from "./utils/HttpUtil"; import httpUtil from "./utils/HttpUtil";
export default { export default {
name: "Home", name: "Home",
data () { data() {
return { return {
version: "1.2", version: "1.2",
activeIndex: location.pathname, activeIndex: location.pathname,
}; };
}, },
async created () { async created() {
let token = localStorage.getItem("token"); let token = localStorage.getItem("token");
window.token = token; window.token = token;
window.isWindows = await httpUtil.get("/file/isWindows"); window.isWindows = await httpUtil.get("/file/isWindows");
console.log(this.$route); console.log(this.$route);
console.log(this.activeIndex); console.log(this.activeIndex);
}, },
async mounted () { async mounted() {
console.log(this.$route); console.log(this.$route);
console.log(location); console.log(location);
}, },

View File

@ -11,8 +11,8 @@
<div class="fileList"> <div class="fileList">
<div> <div>
<el-input style="display: inline-block; width: 150px" type="text" size="small" placeholder="关键词过滤" <el-input style="display: inline-block; width: 150px" type="text" size="small" placeholder="关键词过滤"
v-model="filterText" clearable /> v-model="filterText" clearable/>
<template v-if="type == 'file'"> <template v-if="type === 'file'">
<el-button type="primary" @click="selectAll(true)" size="small">全选</el-button> <el-button type="primary" @click="selectAll(true)" size="small">全选</el-button>
<el-button type="primary" @click="selectAll(false)" size="small">全不选</el-button> <el-button type="primary" @click="selectAll(false)" size="small">全不选</el-button>
<el-button type="primary" @click="refresh" size="small">刷新</el-button> <el-button type="primary" @click="refresh" size="small">刷新</el-button>
@ -21,9 +21,10 @@
</template> </template>
</div> </div>
<div v-for="(item, index) in filterFileList" :key="index"> <div v-for="(item, index) in filterFileList" :key="index">
<span class="folder" v-if="item.isFolder" @click="fileClick(item)">{{ item.name }}</span> <el-checkbox style="height: 1.4em" v-model="item.checked" :disabled="type==='folder' && !item.isFolder">
<el-checkbox style="height: 1.4em" v-model="item.checked" v-else-if="type == 'file'">{{ item.name <a v-if="item.isFolder" @click="fileClick(item)" style="color: #289fff">{{ item.name }}</a>
}}</el-checkbox> <span v-else>{{ item.name }}</span>
</el-checkbox>
</div> </div>
</div> </div>
@ -32,7 +33,7 @@
</div> </div>
<el-dialog title="保存路径" v-model="showSave" width="40em"> <el-dialog title="保存路径" v-model="showSave" width="40em">
<el-input type="text" v-model="saveName" placeholder="输入名称" /> <el-input type="text" v-model="saveName" placeholder="输入名称"/>
<el-button type="primary" @click="savePath" style="padding-top: 1em">提交</el-button> <el-button type="primary" @click="savePath" style="padding-top: 1em">提交</el-button>
</el-dialog> </el-dialog>
</div> </div>
@ -41,10 +42,12 @@
<script> <script>
import HttpUtil from "../utils/HttpUtil"; import HttpUtil from "../utils/HttpUtil";
import Bus from "../utils/Bus"; import Bus from "../utils/Bus";
export default { export default {
name: "FileChose", name: "FileChose",
//type:folder:file:
props: ["curChoosePath", "type"], props: ["curChoosePath", "type"],
data () { data() {
return { return {
isWindows: false, isWindows: false,
fileList: [], // fileList: [], //
@ -57,24 +60,24 @@ export default {
}; };
}, },
computed: { computed: {
filterFileList () { filterFileList() {
let text = this.filterText.trim(); let text = this.filterText.trim();
return text === "" ? this.fileList : this.fileList.filter((item) => item.name.indexOf(text) > -1); return text === "" ? this.fileList : this.fileList.filter((item) => item.name.indexOf(text) > -1);
}, },
curSavePathId () { curSavePathId() {
let curPath = JSON.stringify(this.pathList); let curPath = JSON.stringify(this.pathList);
let targetList = this.savePathList.filter((item) => item.content == curPath); let targetList = this.savePathList.filter((item) => item.content === curPath);
return targetList.length > 0 ? targetList[0].id : null; return targetList.length > 0 ? targetList[0].id : null;
}, },
}, },
watch: { watch: {
async curChoosePath (newVal) { async curChoosePath(newVal) {
console.log("变更路径:", newVal); console.log("变更路径:", newVal);
this.pathList = newVal; this.pathList = newVal;
await this.breadcrumbClick(this.pathList.length - 1); await this.breadcrumbClick(this.pathList.length - 1);
}, },
}, },
async created () { async created() {
if (this.curChoosePath && this.curChoosePath.length > 0) { if (this.curChoosePath && this.curChoosePath.length > 0) {
this.pathList = this.curChoosePath; this.pathList = this.curChoosePath;
} }
@ -84,15 +87,15 @@ export default {
}, },
methods: { methods: {
async refresh () { async refresh() {
await this.breadcrumbClick(this.pathList.length - 1); await this.breadcrumbClick(this.pathList.length - 1);
}, },
// //
async refreshSavePathList () { async refreshSavePathList() {
this.savePathList = await HttpUtil.get("/file/path"); this.savePathList = await HttpUtil.get("/file/path");
}, },
// //
async breadcrumbClick (index) { async breadcrumbClick(index) {
this.loading = true; this.loading = true;
try { try {
let path = this.createPath(index); let path = this.createPath(index);
@ -109,7 +112,7 @@ export default {
return false; return false;
}, },
// //
fileClick (item) { fileClick(item) {
if (item.isFolder) { if (item.isFolder) {
this.pathList.push(item.name); this.pathList.push(item.name);
this.breadcrumbClick(this.pathList.length); this.breadcrumbClick(this.pathList.length);
@ -118,14 +121,14 @@ export default {
} }
}, },
// //
selectAll (status) { selectAll(status) {
this.filterFileList.filter((item) => !item.isFolder).forEach((item) => (item.checked = status)); this.filterFileList.filter((item) => !item.isFolder).forEach((item) => (item.checked = status));
}, },
//index //index
createPath (index) { createPath(index) {
console.log("当前路径为:", this.pathList); console.log("当前路径为:", this.pathList);
let path; let path;
if (index == -1) { if (index === -1) {
path = ""; path = "";
this.pathList = []; this.pathList = [];
} else { } else {
@ -137,34 +140,35 @@ export default {
return path; return path;
}, },
// //
submit () { async submit() {
let chosenFiles = this.fileList.filter((item) => item.checked);
if (chosenFiles.length === 0) {
this.$message({message: "未选择文件", type: "warning"});
return;
}
if (this.type === 'file') { if (this.type === 'file') {
let chosedFiles = this.fileList.filter((item) => item.checked); let body = await HttpUtil.post("/file/recursionQuery", null, chosenFiles);
if (chosedFiles.length == 0) { this.$emit("addData", JSON.parse(JSON.stringify(body)));
this.$message({ message: "未选择文件", type: "warning" });
return;
}
this.$emit("addData", JSON.parse(JSON.stringify(chosedFiles)));
this.fileList.forEach((item) => (item.checked = false)); this.fileList.forEach((item) => (item.checked = false));
this.fileList = [...this.fileList]; this.fileList = [...this.fileList];
} else if (this.type === 'folder') { } else if (this.type === 'folder') {
// //
this.$emit("folderChose", this.createPath(this.pathList.length - 1)); this.$emit("folderChose", JSON.parse(JSON.stringify(chosenFiles)));
} }
this.filterText = ""; this.filterText = "";
}, },
// //
async savePath () { async savePath() {
await HttpUtil.post("/file/path/save", null, { name: this.saveName, content: JSON.stringify(this.pathList) }); await HttpUtil.post("/file/path/save", null, {name: this.saveName, content: JSON.stringify(this.pathList)});
Bus.$emit("refreshSavePathList"); Bus.$emit("refreshSavePathList");
this.saveName = ""; this.saveName = "";
this.showSave = false; this.showSave = false;
this.$message.success("操作成功"); this.$message.success("操作成功");
}, },
// //
async cancelSavePath () { async cancelSavePath() {
await HttpUtil.delete("/file/path/delete", { id: this.curSavePathId }); await HttpUtil.delete("/file/path/delete", {id: this.curSavePathId});
Bus.$emit("refreshSavePathList"); Bus.$emit("refreshSavePathList");
this.$message.success("操作成功"); this.$message.success("操作成功");
}, },

View File

@ -11,35 +11,37 @@ import router from '../router/index';
* @param {*} redirect 接口返回未认证是否跳转到登陆 * @param {*} redirect 接口返回未认证是否跳转到登陆
* @returns 数据 * @returns 数据
*/ */
async function request (url, method, params, body, isForm) { async function request(url, method, params, body, isForm) {
let options = { let options = {
url, url,
baseURL: '/openRenamer/api', baseURL: '/openRenamer/api',
method, method,
params, params,
headers: { token: window.token } headers: {token: window.token}
}; };
if (isForm) { if (isForm) {
options.headers['Content-Type'] = 'multipart/form-data'; options.headers['Content-Type'] = 'multipart/form-data';
}
if (body) {
options.data = body;
}
let res;
try {
res = await http.default.request(options);
} catch (err) {
console.log(Object.keys(err));
console.log(err.response);
if (err.response.status == 401) {
window.vueInstance.config.globalProperties.$message.error('密钥验证错误');
router.push("/public/login");
} else {
window.vueInstance.config.globalProperties.$message.error('发生了某些异常问题');
} }
throw err; if (body) {
} options.data = body;
return res.data; }
let res;
try {
res = await http.default.request(options);
} catch (err) {
console.log(Object.keys(err));
console.log(err.response);
if (err.response.status === 401) {
window.vueInstance.config.globalProperties.$message.error('密钥验证错误');
router.push("/public/login");
} else if (err.response.status === 400) {
window.vueInstance.config.globalProperties.$message.error(err.response.data);
} else {
window.vueInstance.config.globalProperties.$message.error('发生了某些异常问题');
}
throw err;
}
return res.data;
} }
/** /**
@ -48,8 +50,8 @@ async function request (url, method, params, body, isForm) {
* @param {*} params url参数 * @param {*} params url参数
* @param {*} redirect 未登陆是否跳转到登陆页 * @param {*} redirect 未登陆是否跳转到登陆页
*/ */
async function get (url, params = null) { async function get(url, params = null) {
return request(url, 'get', params, null, false); return request(url, 'get', params, null, false);
} }
/** /**
@ -60,8 +62,8 @@ async function get (url, params = null) {
* @param {*} isForm 是否表单数据 * @param {*} isForm 是否表单数据
* @param {*} redirect 是否重定向 * @param {*} redirect 是否重定向
*/ */
async function post (url, params, body, isForm = false) { async function post(url, params, body, isForm = false) {
return request(url, 'post', params, body, isForm); return request(url, 'post', params, body, isForm);
} }
/** /**
@ -72,8 +74,8 @@ async function post (url, params, body, isForm = false) {
* @param {*} isForm 是否表单数据 * @param {*} isForm 是否表单数据
* @param {*} redirect 是否重定向 * @param {*} redirect 是否重定向
*/ */
async function put (url, params, body, isForm = false) { async function put(url, params, body, isForm = false) {
return request(url, 'put', params, body, isForm); return request(url, 'put', params, body, isForm);
} }
/** /**
@ -82,13 +84,13 @@ async function put (url, params, body, isForm = false) {
* @param {*} params url参数 * @param {*} params url参数
* @param {*} redirect 是否重定向 * @param {*} redirect 是否重定向
*/ */
async function deletes (url, params = null) { async function deletes(url, params = null) {
return request(url, 'delete', params, null); return request(url, 'delete', params, null);
} }
export default { export default {
get, get,
post, post,
put, put,
delete: deletes, delete: deletes,
}; };

View File

@ -1,36 +1,60 @@
<template> <template>
<div v-loading="loading" element-loading-text="后台处理中,请稍候"> <div v-loading="loading" element-loading-text="后台处理中,请稍候">
<br /> <br/>
<el-button type="success" @click="submit" size="default">开始重命名</el-button> <el-button type="success" @click="submit" size="default">开始重命名</el-button>
<el-divider content-position="left"> <el-divider content-position="left">
<div class="head-text">规则设置</div> <div class="head-text">规则设置</div>
</el-divider> </el-divider>
<!-- 规则列表 --> <!-- 规则列表 -->
<rule-block @ruleUpdate="ruleUpdate" /> <rule-block @ruleUpdate="ruleUpdate"/>
<el-divider content-position="left"> <el-divider content-position="left">
<div class="head-text">文件预览</div> <div class="head-text">文件预览</div>
</el-divider> </el-divider>
<!-- 文件预览列表 --> <!-- 文件预览列表 -->
<div class="fileList"> <div class="fileList">
<div> <div>
<el-button type="primary" @click="showFileAdd" size="small">新增</el-button> <el-tooltip effect="dark" content="添加需要重命名的文件" placement="top">
收藏路径:<el-tag v-for="item in savePathList" :round="true" class="savePath" closable :key="item.id" <el-button type="primary" @click="showFileAdd" size="small">添加</el-button>
@click="clickSavePath(item)" @close="deleteSavePath(item)" text>{{ item.name }}</el-tag> </el-tooltip>
收藏路径:
<el-tag v-for="item in savePathList" :round="true" class="savePath" closable :key="item.id"
@click="clickSavePath(item)" @close="deleteSavePath(item)" text>{{ item.name }}
</el-tag>
</div> </div>
<div> <div style="margin-top: 5px">
<el-button type="primary" size="small" @click="selectAllFiles">反选</el-button> <el-button type="primary" size="small" @click="selectAllFiles">反选</el-button>
<el-button type="danger" size="small" @click="deleteCheckedFiles">删除</el-button> <el-tooltip effect="dark" content="一键选中所有的非视频、字幕文件和小于5MB的视频文件" placement="bottom">
<el-button type="success" size="small" @click="">一键选择</el-button>
</el-tooltip>
<el-tooltip effect="dark" content="移除(非删除)需要重命名的文件" placement="bottom">
<el-button type="warning" size="small" @click="removeCheckedFiles">移除</el-button>
</el-tooltip>
<el-popconfirm width="250" confirm-button-text="确认" cancel-button-text="取消"
title="确认删除勾选的文件(无法恢复)" @confirm="deleteCheckedFiles">
<template #reference>
<el-button type="danger" size="small">删除</el-button>
</template>
</el-popconfirm>
<el-button type="primary" size="small" @click="moveIndex('top')"> <el-button type="primary" size="small" @click="moveIndex('top')">
<el-tooltip effect="dark" content="上移规则" placement="top"> <el-tooltip effect="dark" content="上移规则" placement="top">
<el-icon> <el-icon>
<top /> <top/>
</el-icon> </el-icon>
</el-tooltip> </el-tooltip>
</el-button> </el-button>
<el-button type="primary" size="small" @click="moveIndex('bottom')"> <el-button type="primary" size="small" @click="moveIndex('bottom')">
<el-tooltip effect="dark" content="下移规则" placement="top"><el-icon> <el-tooltip effect="dark" content="下移规则" placement="top">
<bottom /> <el-icon>
</el-icon></el-tooltip> <bottom/>
</el-icon>
</el-tooltip>
</el-button>
<el-button type="primary" size="small" @click="editFile">
<el-tooltip effect="dark" content="修改文件名" placement="top">
<el-icon>
<Edit/>
</el-icon>
</el-tooltip>
</el-button> </el-button>
</div> </div>
<div class="fileBlock"> <div class="fileBlock">
@ -53,66 +77,72 @@
<el-dialog title="新增文件" v-model="dialogVisible" width="70%"> <el-dialog title="新增文件" v-model="dialogVisible" width="70%">
<file-chose ref="fileChose" type="file" :curChoosePath="curChoosePath" @addData="addData" <file-chose ref="fileChose" type="file" :curChoosePath="curChoosePath" @addData="addData"
@refreshSavePathList="refreshSavePathList" /> @refreshSavePathList="refreshSavePathList"/>
</el-dialog>
<el-dialog title="编辑名称" v-model="showNameEditDialog" width="50%">
<el-input type="text" v-model="newName"/>
<div>
<el-button type="primary" @click="doEditFile">确认</el-button>
</div>
</el-dialog> </el-dialog>
</div> </div>
</template> </template>
<script> <script>
// @ is an alias to /src // @ is an alias to /src
import { Top, Bottom } from "@element-plus/icons-vue"; import {Top, Bottom, Edit} from "@element-plus/icons-vue";
import HttpUtil from "../../utils/HttpUtil"; import HttpUtil from "../../utils/HttpUtil";
import FileChose from "@/components/FileChose"; import FileChose from "@/components/FileChose";
import RuleBlock from "@/components/rules/RuleBlock.vue"; import RuleBlock from "@/components/rules/RuleBlock.vue";
import Bus from "../../utils/Bus"; import Bus from "../../utils/Bus";
import Tips from '@/components/Tips';
let numberSet = new Set(["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]); let numberSet = new Set(["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]);
export default { export default {
name: "Home", name: "Home",
components: { components: {
FileChose, FileChose, RuleBlock, Top, Bottom, Tips, Edit
RuleBlock,
Top,
Bottom,
}, },
data () { data() {
return { return {
loading: false, // loading: false, //
dialogVisible: false, // dialogVisible: false, //
ruleList: [], // ruleList: [], //
fileList: [], // fileList: [], //
changedFileList: [], // changedFileList: [], //
needPreview: false, //
applicationRule: null, // applicationRule: null, //
savePathList: [], // savePathList: [], //
curChoosePath: null, // curChoosePath: null, //
timer: null, // timer: null, //
newName: "", //
currentEditFile: null,
showNameEditDialog: false //
}; };
}, },
computed: {}, computed: {},
async created () { async created() {
this.savePathList = await HttpUtil.get("/file/path"); this.savePathList = await HttpUtil.get("/file/path");
window.isWindows = await HttpUtil.get("/file/isWindows"); window.isWindows = await HttpUtil.get("/file/isWindows");
Bus.$on("refreshSavePathList", this.refreshSavePathList); Bus.$on("refreshSavePathList", this.refreshSavePathList);
}, },
methods: { methods: {
// //
async addData (data) { async addData(data) {
let existSet = new Set();
data.forEach((item) => (item.checked = false)); data.forEach((item) => (item.checked = false));
this.fileList.push(...data); this.fileList.forEach(item => existSet.add(item.path + item.name));
this.fileList.push(...data.filter(item => !existSet.has(item.path + item.name)));
this.fileList = [...this.fileList.sort((a, b) => compareStr(a.name, b.name))]; this.fileList = [...this.fileList.sort((a, b) => compareStr(a.name, b.name))];
this.dialogVisible = false; this.dialogVisible = false;
this.needPreview = true;
await this.showResult(); await this.showResult();
}, },
async ruleUpdate (rules) { async ruleUpdate(rules) {
this.ruleList = rules; this.ruleList = rules;
this.needPreview = true;
await this.showResult(); await this.showResult();
}, },
// //
async showResult () { async showResult() {
this.changedFileList = []; this.changedFileList = [];
if (!this.checkRuleAndFile()) { if (!this.checkRuleAndFile()) {
return; return;
@ -124,16 +154,15 @@ export default {
}; };
this.changedFileList = await HttpUtil.post("/renamer/preview", null, body); this.changedFileList = await HttpUtil.post("/renamer/preview", null, body);
this.fileList = [...this.fileList]; this.fileList = [...this.fileList];
this.needPreview = false;
this.loading = false; this.loading = false;
}, },
// //
async submit () { async submit() {
if (!this.checkRuleAndFile()) { if (!this.checkRuleAndFile()) {
return; return;
} }
if (this.changedFileList.filter((item) => item.errorMessage).length > 0) { if (this.changedFileList.filter((item) => item.errorMessage).length > 0) {
this.$message({ message: "存在错误,无法执行操作", type: "error" }); this.$message({message: "存在错误,无法执行操作", type: "error"});
return; return;
} }
this.loading = true; this.loading = true;
@ -143,48 +172,80 @@ export default {
}; };
try { try {
await HttpUtil.post("/renamer/submit", null, body); await HttpUtil.post("/renamer/submit", null, body);
this.$message({ message: "重命名成功", type: "success" }); this.$message({message: "重命名成功", type: "success"});
} finally { } finally {
this.loading = false; this.loading = false;
} }
}, },
// //
async deleteCheckedFiles () { async removeCheckedFiles() {
this.fileList = this.fileList.filter((item) => !item.checked); this.fileList = this.fileList.filter((item) => !item.checked);
this.needPreview = true; await this.showResult();
},
//delete checked item
async deleteCheckedFiles() {
let body = this.fileList.filter((item) => item.checked);
await HttpUtil.post("/file/deleteBatch", null, body);
this.fileList = this.fileList.filter((item) => !item.checked);
await this.showResult();
},
//edit file
async editFile() {
let list = this.fileList.filter((item) => item.checked);
if (list.length === 0 || list.length > 1) {
this.$message({message: "只能选择一个进行编辑", type: "warning"});
return;
}
this.newName = list[0].name;
this.currentEditFile = list[0];
this.showNameEditDialog = true;
await this.showResult();
},
async doEditFile() {
if (!this.newName) {
this.$message({message: "文件名不能为空", type: "warning"});
return;
}
let target = JSON.parse(JSON.stringify(this.currentEditFile));
target.name = this.newName;
await HttpUtil.post("/file/rename", null, {source: this.currentEditFile, target});
this.currentEditFile.name = this.newName;
this.fileList = [...this.fileList];
this.currentEditFile = null;
this.showNameEditDialog = false;
await this.showResult(); await this.showResult();
}, },
// //
selectAllFiles () { selectAllFiles() {
this.fileList.forEach((item) => (item.checked = !item.checked)); this.fileList.forEach((item) => (item.checked = !item.checked));
}, },
// //
checkRuleAndFile () { checkRuleAndFile() {
if (this.fileList.length == 0) { if (this.fileList.length === 0) {
this.$message({ message: "请选择文件", type: "warning" }); this.$message({message: "请选择文件", type: "warning"});
return false; return false;
} }
if (this.ruleList.filter((item) => !item.blocked).length == 0) { if (this.ruleList.filter((item) => !item.blocked).length === 0) {
this.$message({ message: "无生效规则", type: "warning" }); this.$message({message: "无生效规则", type: "warning"});
return false; return false;
} }
return true; return true;
}, },
// //
async moveIndex (type) { async moveIndex(type) {
let temps = this.fileList.filter((item) => item.checked == true); let temps = this.fileList.filter((item) => item.checked === true);
if (temps.length == 0) { if (temps.length === 0) {
this.$message({ type: "warning", message: "未选中文件,无法移动" }); this.$message({type: "warning", message: "未选中文件,无法移动"});
return; return;
} }
if (type == "top") { if (type == "top") {
if (this.fileList.indexOf(temps[0]) == 0) { if (this.fileList.indexOf(temps[0]) == 0) {
this.$message({ type: "warning", message: "无法上移" }); this.$message({type: "warning", message: "无法上移"});
return; return;
} }
} else { } else {
if (this.fileList.indexOf(temps[temps.length - 1]) == this.fileList.length - 1) { if (this.fileList.indexOf(temps[temps.length - 1]) == this.fileList.length - 1) {
this.$message({ type: "warning", message: "无法下移" }); this.$message({type: "warning", message: "无法下移"});
return; return;
} }
temps = temps.reverse(); temps = temps.reverse();
@ -197,7 +258,6 @@ export default {
this.fileList[newIndex] = temp; this.fileList[newIndex] = temp;
} }
this.fileList = [...this.fileList]; this.fileList = [...this.fileList];
this.needPreview = true;
if (this.timer != null) { if (this.timer != null) {
clearTimeout(this.timer); clearTimeout(this.timer);
} }
@ -206,20 +266,20 @@ export default {
this.timer = null; this.timer = null;
}, 1000); }, 1000);
}, },
showFileAdd () { showFileAdd() {
this.dialogVisible = true; this.dialogVisible = true;
}, },
// //
async clickSavePath (item) { async clickSavePath(item) {
this.curChoosePath = JSON.parse(item.content); this.curChoosePath = JSON.parse(item.content);
this.dialogVisible = true; this.dialogVisible = true;
}, },
async deleteSavePath (item) { async deleteSavePath(item) {
console.log(item); console.log(item);
await HttpUtil.delete("/file/path/delete", { id: item.id }); await HttpUtil.delete("/file/path/delete", {id: item.id});
Bus.$emit("refreshSavePathList"); Bus.$emit("refreshSavePathList");
}, },
async refreshSavePathList () { async refreshSavePathList() {
this.savePathList = await HttpUtil.get("/file/path"); this.savePathList = await HttpUtil.get("/file/path");
}, },
}, },
@ -230,13 +290,13 @@ export default {
* @param a str * @param a str
* @param b str * @param b str
*/ */
function compareStr (a, b) { function compareStr(a, b) {
let an = a.length; let an = a.length;
let bn = b.length; let bn = b.length;
for (let i = 0; i < an;) { for (let i = 0; i < an;) {
let charA = readChar(a, i, an); let charA = readChar(a, i, an);
let charB = readChar(b, i, bn); let charB = readChar(b, i, bn);
if (charB.length == 0) { if (charB.length === 0) {
return 1; return 1;
} }
if (charA !== charB) { if (charA !== charB) {
@ -255,7 +315,7 @@ function compareStr (a, b) {
* @param a a * @param a a
* @param n 数字长度 * @param n 数字长度
*/ */
function readChar (a, i, n) { function readChar(a, i, n) {
let res = ""; let res = "";
for (; i < n; i++) { for (; i < n; i++) {
let char = a.charAt(i); let char = a.charAt(i);