home.vue 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894
  1. <template>
  2. <div>
  3. <el-container style="height: 100vh;">
  4. <el-header height="45px" style="user-select: none; background-color: #fafafa; padding: 0 10px;">
  5. <soft-header ref="headerRef" @update-soft="updateSoft()" @export-file="exportFile"></soft-header>
  6. </el-header>
  7. <el-main ref="el-main" style="background-color: #fafafa; position: relative;">
  8. <div class="content-top">
  9. <el-row>
  10. <el-button type="primary" size="small" @click="pickDir()">添加文件路径</el-button>
  11. <el-input readonly ref="search-input" size="small" placeholder="搜索目录路径" v-model="searchDir" style="width:400px;" prefix-icon="el-icon-folder"></el-input>
  12. <el-checkbox v-model="subFolder" size="mini">搜索多级子文件夹</el-checkbox>
  13. </el-row>
  14. <el-row type="flex" style="align-items: center;">
  15. <el-button type="info" size="small" style="margin-left: 20px;" @click="clearList()">重置</el-button>
  16. </el-row>
  17. </div>
  18. <div class="soft-content">
  19. <!-- 设置区域 -->
  20. <div class="content-right">
  21. <div class="handle-item">
  22. <b>检索方式:</b>
  23. <el-select size="mini" style="width: 160px;" v-model="findType" placeholder="请选择检索方式">
  24. <el-option value="1" label="按文件名检索"></el-option>
  25. <el-option value="2" label="按创建时间检索"></el-option>
  26. <el-option value="3" label="按修改时间检索"></el-option>
  27. </el-select>
  28. </div>
  29. <template v-if="findType == '1'">
  30. <div class="handle-item" style="margin-bottom: 5px;">
  31. <p><b>文件名检索清单:</b><span style="color: #F56C6C; font-size: 12px;">(一行填一个,首尾空格清除)</span></p>
  32. </div>
  33. <el-input class="find-textarea" type="textarea" placeholder="查找文件名清单,多个请换行显示:
  34. 名称1
  35. 名称2
  36. 名称3" v-model="handleData.findText"></el-input>
  37. </template>
  38. <template v-else>
  39. <div class="handle-item">
  40. <b>开始时间:</b>
  41. <el-date-picker type="datetime" v-model="handleData.startTime" placeholder="开始时间" style="width:160px;" size="mini"></el-date-picker>
  42. </div>
  43. <div class="handle-item">
  44. <b>结束时间:</b>
  45. <el-date-picker type="datetime" v-model="handleData.endTime" placeholder="结束时间" style="width:160px;" size="mini"></el-date-picker>
  46. </div>
  47. <div class="find-textarea" style="height: calc(100% - 258px);">
  48. </div>
  49. </template>
  50. <div style="position: relative; margin: 10px 0;">
  51. <el-button type="danger" size="mini" style="position: absolute; right: 0; top: 0;" :loading="searchLoading" @click="search()">搜索</el-button>
  52. <div style="padding: 10px; font-weight: 600;">检索方式:</div>
  53. <el-radio-group class="line-radio" v-model="handleData.rule" size="small">
  54. <el-radio label="1">精确(文件名相同)</el-radio>
  55. <el-radio label="2">模糊(包含文件名)</el-radio>
  56. <el-radio label="3">精确到格式(文件名和格式都相同)</el-radio>
  57. </el-radio-group>
  58. </div>
  59. </div>
  60. <div class="content-left" v-loading="searchLoading">
  61. <div class="handle-item" style="margin-bottom: 5px; display: flex; flex-wrap: nowrap; justify-content: space-between;">
  62. <p>
  63. <b>文件名检索结果:</b>
  64. <span style="color: #F56C6C; font-size: 12px;" >共有 {{fileList.length}} 个文件,搜索耗时 {{times / 1000}} 秒</span>
  65. </p>
  66. <el-button size="mini" :disabled="fileList.length == 0" title="检索结果导出到excel表格中" :loading="xlsxLoading" @click="exportXlsx()">导出检索结果</el-button>
  67. </div>
  68. <div class="table-scroll">
  69. <vxe-table ref="xTable" :column-config="{resizable: true}" size="mini"
  70. show-overflow class="img-table" max-height="100%" empty-text="没有更多数据了!" :loading="tabLoading"
  71. :loading-config="{icon: 'vxe-icon-indicator roll', text: '任务处理中...'}" :row-config="{isHover: true}"
  72. :edit-config="{trigger: 'click', mode: 'cell'}" :data="fileList" :scroll-y="{enabled: true}">
  73. <vxe-column type="seq" width="60" title="编号" align="center"></vxe-column>
  74. <vxe-column field="match" title="匹配词" width="150" min-width="120" align="center"></vxe-column>
  75. <vxe-column field="path" title="文件位置/名称">
  76. <template #default="{ row }">
  77. <img v-if="row.isDirectory" src="../assets/image/folder.png" style="width: 20px; vertical-align: middle;"/>
  78. <img v-else src="../assets/image/file.png" style="width: 20px; vertical-align: middle;"/>
  79. <span>{{row.path}}</span>
  80. </template>
  81. </vxe-column>
  82. <vxe-column field="status" title="操作" width="50" min-width="50" max-width="50" align="center">
  83. <template #default="{ rowIndex }">
  84. <el-link type="warning" @click="delFile(rowIndex)" style="font-size: 12px;">忽略</el-link>
  85. </template>
  86. </vxe-column>
  87. <vxe-column field="status" title="状态" width="80" max-width="80" min-width="80" align="center">
  88. <template #default="{ row }">
  89. <template v-if="row.status == '1'">
  90. <!-- <i class="el-icon-info" style="font-size: 13px; color: #999;"></i> -->
  91. <span style="font-size: 12px;">待操作</span>
  92. </template>
  93. <template v-if="row.status == '2'">
  94. <!-- <i class="el-icon-success" style="font-size: 12px; color: #19be6b;"></i> -->
  95. <span style="font-size: 12px;color: #19be6b;">复制成功</span>
  96. </template>
  97. <template v-if="row.status == '3'">
  98. <!-- <i class="el-icon-success" style="font-size: 12px; color: #19be6b;"></i> -->
  99. <span style="font-size: 12px;color: #19be6b;">剪切成功</span>
  100. </template>
  101. <template v-if="row.status == '4'">
  102. <!-- <i class="el-icon-success" style="font-size: 12px; color: #19be6b;"></i> -->
  103. <span style="font-size: 12px;color: #19be6b;">删除成功</span>
  104. </template>
  105. <template v-if="row.status == '5'">
  106. <!-- <i class="el-icon-success" style="font-size: 12px; color: #19be6b;"></i> -->
  107. <span style="font-size: 12px;color: #19be6b;">覆盖成功</span>
  108. </template>
  109. <template v-if="row.status == '6'">
  110. <!-- <i class="el-icon-error" style="font-size: 12px; color: #ed4014;"></i> -->
  111. <span style="font-size: 12px;color: #ed4014;">处理失败</span>
  112. </template>
  113. <template v-if="row.status == '7'">
  114. <!-- <i class="el-icon-error" style="font-size: 12px; color: #ed4014;"></i> -->
  115. <span style="font-size: 12px;color: #ed4014;">覆盖失败</span>
  116. </template>
  117. </template>
  118. </vxe-column>
  119. </vxe-table>
  120. </div>
  121. <div style="position: relative; margin-top: 20px;">
  122. <div class="handle-item" v-if="handleData.type != '4'">
  123. <label class="handle-label">存储位置:</label>
  124. <el-input :title="downloadDir" ref="upload-input" readonly size="small" placeholder="选择存储位置" v-model="downloadDir" style="width:291px;" prefix-icon="el-icon-folder"></el-input>
  125. <el-button type="primary" size="small" slot="reference" @click="pickPath()">选择</el-button>
  126. <el-popover placement="bottom" popper-class="popper-open" trigger="hover" content="打开存储位置">
  127. <!-- <i class="el-icon-folder-opened" slot="reference" style="padding-left: 5px; cursor: pointer; font-size: 22px; vertical-align: middle;" @click="openFolder()"></i> -->
  128. <el-button type="primary" size="small" slot="reference" @click="openFolder()">打开</el-button>
  129. </el-popover>
  130. <el-checkbox style="margin-left: 20px;" v-model="macthWords" size="mini">匹配词分类存储</el-checkbox>
  131. </div>
  132. <div class="handle-item" v-if="handleData.type == '4'">
  133. <label class="handle-label">覆盖文件:</label>
  134. <el-input :title="coverFile" ref="cover-input" size="small" @focus="pickFile" placeholder="点击选择覆盖文件" v-model="coverFile" readonly style="width:300px;" prefix-icon="el-icon-document"></el-input>
  135. <el-link :underline="false" type="info" style="font-size: 12px;"> (覆盖文件的名称格式需和搜索结果保持一致,否则将无法覆盖)</el-link>
  136. </div>
  137. <div class="handle-item">
  138. <label class="handle-label">操作方式:</label>
  139. <el-radio-group v-model="handleData.type" size="small">
  140. <el-radio label="1" border>复制</el-radio>
  141. <el-radio label="2" border>剪切</el-radio>
  142. <el-radio label="3" border>删除</el-radio>
  143. <el-radio label="4" border>覆盖</el-radio>
  144. </el-radio-group>
  145. </div>
  146. <div class="handle-item">
  147. <label v-if="handleData.type != '1'" class="handle-label"><el-link icon="el-icon-warning" :underline="false" type="danger"></el-link></label>
  148. <el-link style="font-weight: 600;" v-if="handleData.type == '2'" :underline="false" type="danger">当前为"剪切"操作,剪切后文件无法恢复原来位置,您可以做好备份后操作。</el-link>
  149. <el-link style="font-weight: 600;" v-if="handleData.type == '3'" :underline="false" type="danger">当前为"删除"操作,删除后文件无法恢复原来位置,您可以做好备份后操作。</el-link>
  150. <el-link style="font-weight: 600;" v-if="handleData.type == '4'" :underline="false" type="danger">当前为"覆盖"操作,覆盖后原文件将无法恢复,您可以做好备份后操作。</el-link>
  151. </div>
  152. <el-button type="danger" style="position: absolute; top: 40px; right: 10px;" @click="exportFile()" :loading="exportLoading">开始处理</el-button>
  153. </div>
  154. </div>
  155. </div>
  156. </el-main>
  157. <el-footer height="48px">
  158. <!-- 更新 -->
  159. <soft-update ref="updateRef" :showDowload="dowloadModel"
  160. :dowloadFinish="finishModel"></soft-update>
  161. </el-footer>
  162. </el-container>
  163. </div>
  164. </template>
  165. <script>
  166. import os from 'os';
  167. import fs from 'fs';
  168. import path from 'path';
  169. import electronApi from '@/utils/electronApi';
  170. import softUpdate from './update.vue';
  171. import softHeader from './header.vue';
  172. import pjson from '/package.json';
  173. import fse from 'fs-extra';
  174. const fg = require('fast-glob');
  175. import xlsx from 'node-xlsx';
  176. import { v4 as uuidv4 } from 'uuid';
  177. let separator = '';
  178. if (os.platform == 'linux') {
  179. separator = '/'
  180. } else {
  181. separator = '\\'
  182. }
  183. export default {
  184. name: 'landing-page',
  185. components: {
  186. softUpdate,
  187. softHeader
  188. },
  189. data() {
  190. return {
  191. imgUrl: this.$api.imgUrl,
  192. menuIndex: '1', // 重命名类型 1 or 2
  193. fileList: [], // 文件列表
  194. downloadDir: '', //os.userInfo().homedir + separator + "Downloads",
  195. searchDir: '', //'D:\\桌面\\images',
  196. coverFile: '',
  197. handleData: {
  198. type: '1',
  199. rule: '1',
  200. findText: '',
  201. startTime: '',
  202. endTime: '',
  203. },
  204. findType: '1',
  205. exportLoading: false,
  206. searchLoading: false,
  207. tabLoading: false,
  208. times: 0,
  209. subFolder: true,
  210. macthWords: false,
  211. dowloadModel: false,
  212. finishModel: false,
  213. loadingModal: false,
  214. execlimit: 5,
  215. xlsxLoading: false,
  216. };
  217. },
  218. mounted() {
  219. this.$refs.updateRef.updateSoft(true);
  220. let __dirname = os.userInfo().homedir;
  221. if(fs.existsSync(__dirname + separator + "Desktop")){
  222. this.handleData.newPath = __dirname + separator + "Desktop"
  223. } else{
  224. this.handleData.newPath = __dirname + separator + "Downloads"
  225. }
  226. // 打开浏览器
  227. const { shell } = require('electron');
  228. const links = document.querySelectorAll('a[href]');
  229. links.forEach(link => {
  230. link.addEventListener('click', e => {
  231. const url = link.getAttribute('href');
  232. e.preventDefault();
  233. shell.openExternal(url);
  234. });
  235. });
  236. this.loadingModal = true;
  237. },
  238. methods: {
  239. // 选择导出目录
  240. pickPath() {
  241. this.$refs['upload-input'].blur();
  242. electronApi.call('pickDir', []).then((path) => {
  243. if (path) {
  244. this.downloadDir = path;
  245. }
  246. });
  247. },
  248. // 选择搜索目录
  249. async pickDir(){
  250. const spinLoad = this.$loading();
  251. await electronApi.call('pickDir', []).then((path) =>{
  252. if(path){
  253. this.searchDir = path;
  254. }
  255. spinLoad.close();
  256. }).catch(err => {
  257. spinLoad.close();
  258. });
  259. },
  260. // 打开自定义目录
  261. openFolder(){
  262. let path = this.downloadDir;
  263. electronApi.call('showItemInfolder',[path+'\\tty.tty'])
  264. },
  265. // 清除列表
  266. clearList() {
  267. this.$confirm('确认重置参数并清空搜索结果?', '提示', {
  268. confirmButtonText: '确定',
  269. cancelButtonText: '取消',
  270. type: 'warning'
  271. }).then(() => {
  272. this.searchDir = '';
  273. this.handleData = {
  274. type: '1',
  275. rule: '1',
  276. findText: '',
  277. };
  278. this.fileList = [];
  279. }).catch(() => {
  280. });
  281. },
  282. // 忽略文件
  283. delFile(rowIndex){
  284. this.fileList.splice(rowIndex, 1);
  285. },
  286. async pickFile(){
  287. this.$refs['cover-input'].blur();
  288. const spinLoad = this.$loading();
  289. await electronApi.call('pickFile', ['*', false]).then(async(files) =>{
  290. if(files.length > 0){
  291. this.coverFile = files[0];
  292. }
  293. spinLoad.close();
  294. }).catch(err => {
  295. spinLoad.close();
  296. });
  297. },
  298. // 软件更新
  299. updateSoft(){
  300. this.$refs.updateRef.updateSoft();
  301. },
  302. // 开通会员
  303. openVip(){
  304. this.$refs.headerRef.openVip();
  305. },
  306. // 搜索
  307. search(){
  308. if(!this.searchDir){
  309. this.$message({message: '请先选择搜索目录' , type: 'error'});
  310. return false;
  311. }
  312. if(!this.handleData.findText && this.findType == '1'){
  313. this.$message({message: '请输入要查找的文件搜索清单' , type: 'error'});
  314. return false;
  315. }
  316. if(this.findType != '1' && !this.handleData.startTime && !this.handleData.endTime){ // 根据时间搜索
  317. this.$message({message: '请选择搜索的时间范围' , type: 'error'});
  318. return false;
  319. }
  320. (async () => {
  321. let startTime = new Date().getTime();
  322. this.searchLoading = true;
  323. setTimeout(() => {
  324. this.searchLoading = false;
  325. }, 2000)
  326. let findText = this.handleData.findText.split('\n');
  327. let searchDir = this.searchDir.replace(/\\/g, '/');
  328. let paramArr = []; // 匹配规则1
  329. let paramArr2 = []; // 匹配规则2 - 文件名称中带.
  330. for(let i=0; i< findText.length; i++){
  331. if(findText[i].trim()){
  332. if(findText[i].indexOf('.') > -1){
  333. paramArr2.push(this.filterReg(findText[i].trim()));
  334. }else{
  335. paramArr.push(this.filterReg(findText[i].trim()));
  336. }
  337. }
  338. }
  339. let params = paramArr.join(',');
  340. let params2 = paramArr2.join(',');
  341. let ruleStr = [];
  342. let subStr = '/';
  343. if(this.subFolder){ // 是否匹配子文件夹内文件
  344. subStr = '/**/'
  345. }
  346. switch(this.handleData.rule){
  347. case '1': //相同文件名
  348. if(paramArr.length > 0){
  349. ruleStr.push(searchDir + subStr + '{' + params + ',<}?(.[!.]+)');
  350. }
  351. if(paramArr2.length > 0){
  352. ruleStr.push(searchDir + subStr + '{' + params2 + ',<}.[!.]+');
  353. }
  354. break;
  355. case '2': //包含文件名
  356. if(paramArr.length > 0){
  357. ruleStr.push(searchDir + subStr + '*{' + params + ',<}*.[!.]+');
  358. ruleStr.push(searchDir + subStr + '{' + params + ',<}');
  359. ruleStr.push(searchDir + subStr + '[!.]+{' + params + ',<}');
  360. ruleStr.push(searchDir + subStr + '{' + params + ',<}[!.]+');
  361. ruleStr.push(searchDir + subStr + '[!.]+{' + params + ',<}[!.]+');
  362. }
  363. if(paramArr2.length > 0){
  364. ruleStr.push(searchDir + subStr + '*{' + params2 + ',<}*.[!.]+');
  365. }
  366. break;
  367. case '3': //文件名格式相同
  368. if(paramArr.length > 0){
  369. ruleStr.push(searchDir + subStr + '{' + params + ',<}');
  370. }
  371. if(paramArr2.length > 0){
  372. ruleStr.push(searchDir + subStr + '{' + params2 + ',<}');
  373. }
  374. break;
  375. }
  376. console.log('----验证规则----', ruleStr);
  377. try{
  378. const files = await fg(ruleStr, { dot: true, onlyFiles:true, objectMode: true,markDirectories: true});
  379. let regExp = new RegExp(paramArr.join('|'));
  380. let regExp2 = new RegExp(paramArr2.join('|'));
  381. this.fileList = [];
  382. files.map(item => {
  383. let isDirectory = item.dirent.isDirectory();
  384. let filename = item.name;//item.substr(item.lastIndexOf('/')+1);
  385. let title = filename.substr(0, filename.lastIndexOf('.'));
  386. let suffix = filename.substr(filename.lastIndexOf('.'));
  387. if(filename.indexOf('.') < 0){
  388. title = filename;
  389. suffix = '';
  390. }
  391. let res = regExp.exec(filename);
  392. if(!res){
  393. res = regExp2.exec(filename);
  394. }
  395. this.fileList.push({
  396. title: title,
  397. isDirectory: isDirectory,
  398. suffix: suffix, // 带.
  399. name: filename,
  400. match: res,
  401. path: item.path,
  402. status: '1'
  403. });
  404. });
  405. this.searchLoading = false;
  406. let endTime = new Date().getTime();
  407. this.times = endTime - startTime;
  408. // console.log('----查找结果----', files);
  409. }catch(e){
  410. this.showError(e);
  411. }
  412. })();
  413. },
  414. // 导出检索结果
  415. exportXlsx(){
  416. this.xlsxLoading = true;
  417. let filename = '文件检索结果' + new Date().getTime() +'.xlsx';
  418. let sheet1 = [['编号','匹配词','文件位置/名称','类型']];
  419. let sheet2 = [];
  420. let findText = this.handleData.findText.split('\n');
  421. for(let i = 0; i < this.fileList.length; i++){
  422. let item = this.fileList[i];
  423. let info = [i+1, item.match, item.path, item.isDirectory ? '文件夹' : '文件'];
  424. sheet1.push(info);
  425. if(findText.indexOf(item.match[0]) > -1){ // 匹配到了
  426. let removeIndex = findText.indexOf(item.match[0]);
  427. findText.splice(removeIndex, 1);
  428. }
  429. }
  430. findText.map(fitem => {
  431. sheet2.push([fitem]);
  432. })
  433. let buffer = xlsx.build([
  434. {
  435. name: '检索成功',
  436. data: sheet1
  437. },
  438. {
  439. name: '未检索',
  440. data: sheet2
  441. }
  442. ]);
  443. fs.writeFileSync(this.handleData.newPath + separator + filename, buffer, {'flag':'w'});
  444. this.xlsxLoading = false;
  445. this.$message({message: '文件导出成功!', type: 'success'});
  446. electronApi.call('showItemInfolder',[this.handleData.newPath + separator + filename])
  447. },
  448. // 导出
  449. exportFile(flag) {
  450. let authority = this.$refs.headerRef.authority;
  451. if (this.fileList.length > 0) {
  452. if (!authority.isAuthority && !flag) { // 非会员点击转换弹出提示框
  453. this.$refs.headerRef.memberModel = true;
  454. this.$refs.headerRef.isClick = true;
  455. return false;
  456. } else {
  457. this.$refs.headerRef.memberModel = false;
  458. }
  459. this.exportLoading = true;
  460. setTimeout(() => {
  461. this.exportLoading = false;
  462. }, 3000);
  463. let size = 999999999;
  464. if(!authority.isAuthority){
  465. size = this.execlimit;
  466. }
  467. if(this.handleData.type != '4' && !this.downloadDir){
  468. this.$notify.error({
  469. title: '提示',
  470. message: '请选择文件存储位置后再操作!'
  471. });
  472. return false;
  473. }
  474. let newPath = this.downloadDir;
  475. if(this.handleData.type == '4' && !this.coverFile){
  476. this.$notify.error({
  477. title: '提示',
  478. message: '请选择覆盖文件后再操作!'
  479. });
  480. return false;
  481. }
  482. this.tabLoading = true;
  483. setTimeout(() => {
  484. for(let i = 0; i < this.fileList.length; i++){
  485. if(i < size){
  486. let item = this.fileList[i];
  487. let middlePath = separator;
  488. if(this.macthWords){
  489. middlePath = separator + item.match + separator;
  490. try{
  491. fse.ensureDirSync(newPath + separator + item.match);
  492. }catch(e){
  493. let str = e.toString();
  494. if(str.indexOf('already exists') > -1 && str.indexOf(item.match) > -1){
  495. this.$notify.error({
  496. title: '提示',
  497. message: '无法创建目录'+newPath + separator + item.match+',因为同名文件或目录已存在!'
  498. });
  499. this.tabLoading = false;
  500. return false;
  501. }
  502. }
  503. }
  504. let newFilePath = newPath + middlePath + item.name;
  505. try {
  506. if(this.handleData.type == '1'){ // 复制
  507. try{
  508. fse.copySync(item.path, newFilePath, { overwrite: false, errorOnExist: true });
  509. item.status = '2';
  510. }catch(e){
  511. let str = e.toString();
  512. if(str.indexOf('already exists') > -1 && str.indexOf(item.name) > -1){
  513. newFilePath = newPath + middlePath + item.title + '重复文件' + uuidv4().substr(0, 18) + i.toString() + item.suffix;
  514. fse.copy(item.path, newFilePath).then(() => {
  515. item.status = '2';
  516. }).catch(err => {
  517. item.status = '6';
  518. })
  519. }
  520. }
  521. }
  522. if(this.handleData.type == '2'){ // 剪切
  523. try{
  524. fse.moveSync(item.path, newFilePath);
  525. item.status = '3'
  526. }catch(e){
  527. let str = e.toString();
  528. if(str.indexOf('already exists') > -1){
  529. newFilePath = newPath + middlePath + item.title + '重复文件' + uuidv4().substr(0, 18) + i.toString() + item.suffix;
  530. fse.move(item.path, newFilePath).then(() => {
  531. item.status = '3';
  532. }).catch(err => {
  533. item.status = '6';
  534. })
  535. }
  536. }
  537. }
  538. if(this.handleData.type == '3'){ // 删除
  539. //异步
  540. fse.remove(item.path).then(() => {
  541. item.status = '4';
  542. }).catch(err => {
  543. item.status = '6';
  544. })
  545. }
  546. if(this.handleData.type == '4'){ // 覆盖
  547. let filename = this.coverFile.substr(this.coverFile.lastIndexOf('\\')+1);
  548. if(filename == item.name){
  549. fse.copy(this.coverFile, item.path).then(() => {
  550. item.status = '5';
  551. }).catch(err => {
  552. item.status = '7';
  553. })
  554. }else{
  555. item.status = '7';
  556. }
  557. }
  558. } catch (err) {
  559. console.error(err);
  560. item.status = '6';
  561. }
  562. this.nums++;
  563. }
  564. }
  565. setTimeout(() => {
  566. this.tabLoading = false;
  567. }, 500)
  568. }, 100);
  569. }
  570. },
  571. // 是否包含特殊字符
  572. containsAnyChar(str, charsArray) {
  573. for (let i = 0; i < charsArray.length; i++) {
  574. if (str.includes(charsArray[i])) {
  575. return true;
  576. }
  577. }
  578. return false;
  579. },
  580. // 处理正则表达式特殊字符
  581. filterReg(data){
  582. // let specialChar = ["$", "(", ")", "+", ".", "[", "^", "{", "|"]; // 特殊字符 正则匹配
  583. let specialChar2 = ["$", "(", ")", "^", "*", "+", "?", "[", "]", '|', '/', '\\', ',']; // 特殊字符 正则匹配
  584. let regStr = '';
  585. let expArr = data.split('');
  586. expArr.map(item => {
  587. if(specialChar2.indexOf(item) > -1){
  588. item = '\\' + item;
  589. }
  590. regStr += item;
  591. })
  592. return regStr;
  593. },
  594. // 错误提示
  595. showError(e){
  596. let str = e.toString().substr(0, 100);
  597. this.loading = false;
  598. this.$notify.error({
  599. title: '提示',
  600. message: str
  601. });
  602. },
  603. }
  604. };
  605. </script>
  606. <style lang="scss">
  607. @import "../assets/css/fontx/iconfont.css";
  608. @import "../assets/css/home.scss";
  609. .ivu-input-number-controls-outside-btn i {
  610. font-weight: 800;
  611. }
  612. .update-point {
  613. display: inline-block;
  614. width: 8px;
  615. height: 8px;
  616. border-radius: 8px;
  617. background: #ff0000;
  618. top: 14px;
  619. position: absolute;
  620. left: -13px;
  621. }
  622. .menu-item {
  623. padding: 8px 0;
  624. font-size: 14px;
  625. .iconfont {
  626. font-size: 32px;
  627. }
  628. &:hover,
  629. &.active {
  630. color: #ed4014;
  631. }
  632. }
  633. .ivu-progress-show-info .ivu-progress-outer {
  634. padding-right: 40px !important;
  635. margin-right: -40px !important;
  636. }
  637. .tips {
  638. text-align: center;
  639. padding: 10px 0;
  640. color: #ed4014;
  641. font-size: 12px;
  642. }
  643. .handle-desc {
  644. display: inline-block;
  645. width: calc(100% - 100px);
  646. overflow: hidden;
  647. }
  648. .ivu-menu-submenu-title {
  649. font-weight: 600;
  650. }
  651. .ivu-menu .ivu-menu-item {
  652. line-height: 1;
  653. }
  654. // new-el
  655. .el-menu {
  656. border-right: none !important;
  657. }
  658. .cmenu-item {
  659. margin-bottom: 30px;
  660. .cmenu-title {
  661. font-size: 18px;
  662. font-weight: 600;
  663. padding-bottom: 20px;
  664. }
  665. .citem-nav {
  666. text-align: center;
  667. border-radius: 10px;
  668. min-height: 110px;
  669. padding: 25px 0;
  670. background-color: #fff;
  671. font-size: 15px;
  672. cursor: pointer;
  673. box-shadow: 3px 3px 6px #ccc, -3px -3px 6px #ccc;
  674. border: 1px solid #ffffff00;
  675. &:hover {
  676. transform: scale(1.02);
  677. border: 1px solid #2F74FF;
  678. }
  679. .citem-img {
  680. width: 55%;
  681. margin-bottom: 20px;
  682. }
  683. .citem-name{
  684. font-size: 22px;
  685. font-weight: 600;
  686. padding-bottom: 10px;
  687. }
  688. }
  689. .citem-nav2{
  690. text-align: center;
  691. border-radius: 10px;
  692. min-height: 110px;
  693. padding: 25px 0;
  694. font-size: 15px;
  695. box-shadow: 3px 3px 6px #ccc, -3px -3px 6px #ccc;
  696. .citem-img {
  697. width: 55%;
  698. margin-bottom: 20px;
  699. }
  700. .citem-name{
  701. font-size: 22px;
  702. font-weight: 600;
  703. color: #999;
  704. padding-bottom: 10px;
  705. }
  706. }
  707. }
  708. .popper-open{
  709. text-align: center !important;
  710. padding: 10px !important;
  711. background: #303133 !important;
  712. color: #fff !important;
  713. min-width: 120px !important;
  714. }
  715. .popper-open[x-placement^=bottom] .popper__arrow::after{
  716. border-bottom-color: #303133 !important;
  717. }
  718. .popper-open[x-placement^=left] .popper__arrow::after{
  719. border-left-color: #303133 !important;
  720. }
  721. .el-tabs__header{
  722. margin-bottom: 0 !important;
  723. }
  724. .el-tabs__item{
  725. padding: 0 13px !important;
  726. }
  727. .sort-btn{
  728. padding: 7px !important;
  729. }
  730. .upload-img{
  731. width: 220px;
  732. }
  733. .el-tabs__item.is-active{
  734. color: #F56C6C !important;
  735. }
  736. .el-tabs__active-bar{
  737. background-color: #F56C6C !important;
  738. }
  739. .el-tabs__item:hover{
  740. color: #F56C6C !important;
  741. }
  742. .el-input__inner{
  743. padding: 0 10px !important;
  744. }
  745. .el-input--prefix .el-input__inner{
  746. padding-left: 30px !important;
  747. }
  748. .el-divider__text{
  749. font-weight: 800 !important;
  750. }
  751. .el-input-number .el-input__inner{
  752. text-align: left !important;
  753. }
  754. .el-time-panel{
  755. left: -25px !important;
  756. }
  757. .icon-back{
  758. font-size: 26px;
  759. font-weight: 600 !important;
  760. vertical-align: middle !important;
  761. cursor: pointer;
  762. }
  763. .icon-back-grey{
  764. color: #ccc;
  765. cursor: not-allowed;
  766. }
  767. .tab-img{
  768. width: 20px;
  769. margin-right: 5px;
  770. vertical-align: middle;
  771. }
  772. .eq-tips{
  773. text-align: center;
  774. color: #F56C6C;
  775. font-size: 14px;
  776. margin: 5px 0;
  777. }
  778. .el-icon-question{
  779. color: #888;
  780. margin-left: 5px;
  781. }
  782. .i-tips{
  783. margin-top: 10px;
  784. text-decoration: underline;
  785. cursor: pointer;
  786. color: #777;
  787. text-align: center;
  788. }
  789. .line-radio .el-radio{
  790. line-height: 2;
  791. }
  792. .find-textarea{
  793. height: calc(100% - 216px);
  794. textarea{
  795. height: 100%;
  796. }
  797. }
  798. .vxe-cell{
  799. padding: 0 2px !important;
  800. }
  801. </style>