Просмотр исходного кода

阶段进度,附件,重命名

MaiXinRong 7 часов назад
Родитель
Сommit
9482fb4963

+ 23 - 0
app/controller/sub_proj_controller.js

@@ -487,6 +487,29 @@ module.exports = app => {
             }
         }
 
+        async moveFile(ctx) {
+            try {
+                const data = JSON.parse(ctx.request.body.data);
+                if (!data.id || !data.rela_id) throw '缺少参数';
+                const result = await ctx.service.subProjFile.moveFile(data.id, data.rela_id);
+                ctx.body = { err: 0, msg: '', data: result };
+            } catch (error) {
+                this.log(error);
+                ctx.ajaxErrorBody(error, '编辑附件失败');
+            }
+        }
+        async saveFile(ctx) {
+            try {
+                const data = JSON.parse(ctx.request.body.data);
+                if (!data.id || !data.filename) throw '缺少参数';
+                const result = await ctx.service.subProjFile.saveFile(data.id, data.filename);
+                ctx.body = { err: 0, msg: '', data: result };
+            } catch (error) {
+                this.log(error);
+                ctx.ajaxErrorBody(error, '编辑附件失败');
+            }
+        }
+
         async noPermission(ctx) {
             ctx.controllerName = ctx.params.block;
             await this.layout('sub_proj/no_permission.ejs');

+ 118 - 18
app/public/js/shares/tools_att.js

@@ -22,8 +22,9 @@
             '    <ul class="nav nav-tabs">\n' +
             '        <li class="nav-item"><a class="nav-link active" data-toggle="tab" href="#att-cur" role="tab" att-type="cur" id="att-cur-button">当前节点</a></li>\n' +
             '        <li class="nav-item"><a class="nav-link" data-toggle="tab" href="#att-all" role="tab" att-type="all">所有附件</a></li>\n' +
+            (setting.search ? '        <li class="nav-item"><a class="nav-link" data-toggle="tab" href="#att-search" role="tab" att-type="search">搜索</a></li>\n' : '') +
             '        <li class="nav-item ml-auto pt-1">\n' +
-            '            <a href="javascript:void(0);" id="batch-download" class="btn btn-sm btn-primary" type="curr">批量下载</a>\n' +
+            '            <a href="javascript:void(0);" id="batch-download" class="btn btn-sm btn-primary" type="cur">批量下载</a>\n' +
             '            <span id="showPage" style="display: none"><a href="javascript:void(0);" class="page-select ml-3" content="pre"><i class="fa fa-chevron-left"></i></a> <span id="att-cur-page">1</span>/<span id="att-total-page">10</span> <a href="javascript:void(0);" class="page-select mr-3" content="next"><i class="fa fa-chevron-right"></i></a></span>\n' +
             (setting.readOnly ? '' : '            <a href="#upload" data-toggle="modal" data-target="#upload"  class="btn btn-sm btn-outline-primary ml-2">上传</a>\n') +
             (setting.readOnly || !setting.bigValid ? '' : '            <a href="#add-big-file" data-toggle="modal" data-target="#add-big-file"  class="btn btn-sm btn-outline-primary ml-2">上传大文件</a>\n') +
@@ -35,7 +36,7 @@
             '        <div style="overflow:auto; overflow-x:hidden; height: 100%">\n' +
             '            <div class="mt-1 mb-1" id="att-cur-hint"></div>\n' +
             '            <table class="table table-sm table-bordered table-hover" style="word-break:break-all; table-layout: fixed">\n' +
-            '                <thead><tr><th width="25"><input type="checkbox" class="check-all-file"><th>文件名</th><th width="80">上传</th><th width="80">操作</th></tr></thead>\n' +
+            '                <thead><tr><th width="25"><input type="checkbox" class="check-all-file"><th>文件名</th><th width="80">上传</th><th width="100">操作</th></tr></thead>\n' +
             '                <tbody id="cur-att-list" class="list-table"></tbody>\n' +
             '            </table>\n' +
             '        </div>\n' +
@@ -43,11 +44,23 @@
             '    <div class="tab-pane" id="att-all" style="height: 100%">\n' +
             '        <div style="overflow:auto; overflow-x:hidden; height: 100%">\n' +
             '            <table class="table table-sm table-bordered table-hover" style="word-break:break-all; table-layout: fixed">\n' +
-            '                <thead><tr><th width="25"><input type="checkbox" class="check-all-file"></th><th>文件名</th><th width="80">上传</th><th width="80">操作</th></tr></thead>\n' +
+            '                <thead><tr><th width="25"><input type="checkbox" class="check-all-file"></th><th>文件名</th><th width="80">上传</th><th width="100">操作</th></tr></thead>\n' +
             '                <tbody id="all-att-list" class="list-table"></tbody>\n' +
             '           </table>\n' +
             '        </div>\n' +
             '    </div>\n' +
+            '    <div class="tab-pane" id="att-search" style="height: 100%">\n' +
+            '        <div style="overflow:auto; overflow-x:hidden; height: 100%">\n' +
+            '            <div class="input-group input-group-sm pb-1 pt-1">\n' +
+            '                <input id="att-search-keyword" type="text" class="form-control" autocomplete="off" placeholder="请输入 名称 查询" aria-label="Recipient\'s username" aria-describedby="button-addon2">\n' +
+            '                <div class="input-group-append"><button id="att-search-btn" class="btn btn-outline-secondary" type="button">搜索</button></div>\n' +
+            '            </div>\n' +
+            '            <table class="table table-sm table-bordered table-hover" style="word-break:break-all; table-layout: fixed">\n' +
+            '                <thead><tr><th width="25"><input type="checkbox" class="check-all-file"></th><th>文件名</th><th width="80">上传</th><th width="100">操作</th></tr></thead>\n' +
+            '                <tbody id="search-att-list" class="list-table"></tbody>\n' +
+            '           </table>\n' +
+            '        </div>\n' +
+            '    </div>\n' +
             '</div>'
         );
         autoFlashHeight();
@@ -66,14 +79,16 @@
             if (tipNode && att.node) nodeInfo = getNodeHint(att.node);
             const tipHtml = nodeInfo ? `${nodeInfo}\n${att[fileInfo.create_time]}` : att[fileInfo.create_time];
             const tipType = 'title='; //'data-toggle="tooltip" data-html="true" data-placement="left" data-original-title=';
-            html.push(`<td><div class="d-flex"><a href="javascript:void(0)" ${tipType}"${tipHtml}" class="pl-0 col-11" file-id=${att.id}>${att.filename}${att.fileext}</a></div></td>`);
+            html.push(`<td file-id="${att.id}"><div class="d-flex"><a href="javascript:void(0)" ${tipType}"${tipHtml}" class="pl-0 col-11" file-id=${att.id}>${att.filename}${att.fileext}</a></div></td>`);
             html.push(`<td>${att[fileInfo.user_name]}</td>`);
             const canDel = setting.readOnly ? false : att[fileInfo.user_id] === userID && (!setting.checked || att.extra_upload);
+            const moveHtml = setting.moveUrl ? `<a href="javascript: void(0);" class="ml-1 text-primary" name="att-move" file-id="${att.id}" ${tipType}"移动"><i class="fa fa-exchange fa-fw"></i></a>` : '';
+            const editHtml = setting.saveUrl ? `<a href="javascript: void(0);" class="ml-1 text-primary" name="att-edit" file-id="${att.id}" ${tipType}"重命名"><i class="fa fa-pencil fa-fw"></i></a>` : '';
             html.push('<td width="80">',
                 `<a class="ml-1" href="javascript:void(0)" ${tipType}"定位" name="att-locate" file-id="${att.id}"><i class="fa fa-crosshairs"></i></a>`,
                 att.viewpath ? `<a class="ml-1" href="${att.viewpath}" ${tipType}"预览"  target="_blank"><i class="fa fa-eye"></i></a>` : '',
                 `<a class="ml-1" href="javascript:void(0)" ${tipType}"下载" onclick="AliOss.downloadFile('${att.filepath}', '${att.filename}${att.fileext}')"><i class="fa fa-download"></i></a>`,
-                canDel ? `<a class="ml-1 text-danger" href="javascript:void(0)" name="att-delete" file-id="${att.id}"><i class="fa fa-close" ${tipType}"删除"></i></a>` : '',
+                canDel ? `<a class="ml-1 text-danger" href="javascript:void(0)" name="att-delete" file-id="${att.id}"><i class="fa fa-close" ${tipType}"删除"></i></a>` : '', moveHtml, editHtml,
                 '</td>');
             html.push('</tr>');
             return html.join('');
@@ -94,9 +109,9 @@
             $('#att-cur-page').text(curPage);
             const pageNum = Math.ceil(allAtts.length/pageLength);
             $('#att-total-page').text(pageNum);
-            const currPageAttData = allAtts.slice((curPage-1)*pageLength, curPage*pageLength);
+            const curPageAttData = allAtts.slice((curPage-1)*pageLength, curPage*pageLength);
             const html = [];
-            for(const att of currPageAttData) {
+            for(const att of curPageAttData) {
                 html.push(getAttHtml(att, true));
             }
             $('#all-att-list').html(html.join());
@@ -116,6 +131,41 @@
             }
             refreshCurAttHtml();
         };
+        const searchObj = {
+            result: [],
+            refreshSearchAttHtml: function() {
+                const html = [];
+                for (const att of this.result) {
+                    html.push(getAttHtml(att));
+                }
+                $('#search-att-list').html(html.join());
+                $('[data-toggle="tooltip"]').tooltip();
+            },
+            searchAtt: function() {
+                const keyword = $('#att-search-keyword').val();
+                this.result = allAtts.filter(x => { return x.filename.indexOf(keyword) >= 0; });
+                this.refreshSearchAttHtml();
+            },
+            removeSearchAtt: function(fid) {
+                const ri = this.result.findIndex(x => { return x.id === fid; });
+                if (ri >= 0) {
+                    this.result.splice(ri, 1);
+                    this.refreshSearchAttHtml();
+                }
+            },
+            getEditFileNameHtml(att) {
+                const inputHtml = `<input type="text" class="form-control form-control-sm" maxlength="100" name="new-att-filename" value="${att.filename + att.fileext}" file-id="${att.id}">`;
+                const btnHtml = `<div class="btn-group-table" style="display: none;"><a href="javascript: void(0)" class="mr-1" name="edit-att-ok"><i class="fa fa-check fa-fw"></i></a><a href="javascript: void(0)" class="mr-1" name="edit-att-cancel"><i class="fa fa-remove fa-fw"></i></a></div>`;
+                return `<div class="d-flex justify-content-between align-items-center table-file"><div>${inputHtml}</div>${btnHtml}</div>`;
+            },
+            getFileNameHtml(att, tipNode = false) {
+                let nodeInfo = '';
+                if (tipNode && att.node) nodeInfo = getNodeHint(att.node);
+                const tipHtml = nodeInfo ? `${nodeInfo}\n${att[fileInfo.create_time]}` : att[fileInfo.create_time];
+                const tipType = 'title='; //'data-toggle="tooltip" data-html="true" data-placement="left" data-original-title=';
+                return `<div class="d-flex"><a href="javascript:void(0)" ${tipType}"${tipHtml}" class="pl-0 col-11" file-id=${att.id}>${att.filename}${att.fileext}</a></div>`;
+            }
+        };
         const findFile = setting.fileIdType === 'string'
             ? function (list, id) { return list.find(item => item.id === id); }
             : function (list, id) { return list.find(item => item.id === parseInt(id)); };
@@ -135,13 +185,12 @@
         // 切换 当前节点/所有附件
         $('#fujian .nav-link').on('click', function () {
             const tabPanel = $(this).attr('att-type');
-            if (tabPanel !== 'all') {
-                $('#showPage').hide();
-                $('#batch-download').prop('type', 'curr');
-            } else {
+            if (tabPanel === 'all') {
                 $('#showPage').show();
-                $('#batch-download').prop('type', 'all')
+            } else {
+                $('#showPage').hide();
             }
+            $('#batch-download').prop('type', 'all')
         });
         // 切换页数
         $('.page-select').on('click', function () {
@@ -163,12 +212,7 @@
             const self = this;
             const files = [];
             const type = $(this).prop('type');
-            let node = '';
-            if (type === 'curr') {
-                node = '#cur-att-list .check-file:checked'
-            } else {
-                node = '#all-att-list .check-file:checked'
-            }
+            let node = `#${type}-att-list .check-file:checked`;
             $(node).each(function() {
                 const fid = $(this).attr('file-id');
                 const att = findFile(allAtts, fid);
@@ -293,8 +337,57 @@
                     refreshAllAttHtml();
                 }
                 refreshCurAttHtml();
+                searchObj.removeSearchAtt(fid);
             });
         });
+        $('body').on('click', "a[name=att-edit]", function() {
+            const check = $('[name=new-att-filename] input[file-id]');
+            if (check.length > 0 && check[0].getAttribute('file-id') === this.getAttribute('file-id')) {
+                toastr.warning('请勿多个分类下编辑同一个文件');
+                return;
+            }
+
+            const id = this.getAttribute('file-id');
+            const att = findFile(allAtts, id);
+            const tbody = $(this).closest('tbody');
+            $(`td[file-id=${id}]`, tbody).html(searchObj.getEditFileNameHtml(att));
+        });
+        $('body').on('mouseenter', ".table-file", function(){
+            $(this).children(".btn-group-table").css("display","block");
+        });
+        $('body').on('mouseleave', ".table-file", function(){
+            $(this).children(".btn-group-table").css("display","none");
+        });
+        $('body').on('click', "a[name=edit-att-ok]", function() {
+            const td = $(this).parent().parent().parent();
+            const id = td.attr('file-id');
+            const att = findFile(allAtts, id);
+            if (!att) return;
+
+            const filename = $('input', td).val();
+            if (filename === att.filename + att.fileext) {
+                td.html(searchObj.getFileNameHtml(att));
+                return;
+            }
+
+            postData(setting.saveUrl, {id, filename}, function (result) {
+                const att = findFile(allAtts, id);
+                att.filename = result.filename;
+                att.fileext = result.fileext;
+                // 刷新3个table
+                refreshAllAttHtml();
+                refreshCurAttHtml();
+                searchObj.refreshSearchAttHtml();
+            });
+        });
+        $('body').on('click', "a[name=edit-att-cancel]", function() {
+            const td = $(this).parent().parent().parent();
+            const id = td.attr('file-id');
+            const att = findFile(allAtts, id);
+            if (!att) return;
+
+            td.html(searchObj.getFileNameHtml(att));
+        });
         // 监听附件check是否选中
         $('.list-table').on('click', '.check-file', function() {
             const checkedList = $(this).parents('.list-table').children().find('input:checked');
@@ -312,6 +405,13 @@
                 $(this).find('input:checkbox').prop("checked", isCheck);
             })
         });
+        // 搜索
+        $('body').on('click', '#att-search-btn', function() {
+            searchObj.searchAtt();
+        });
+        $('body').on('keydown', '#att-search-keyword', function(e) {
+            if (e.keyCode == 13) searchObj.searchAtt();
+        });
 
         const _addToNodeIndex = function(att, isTop = false) {
             const id = att[setting.masterKey];

+ 3 - 0
app/public/js/sp_progress.js

@@ -503,6 +503,8 @@ $(document).ready(() => {
         uploadUrl: 'progress/file/upload',
         deleteUrl: 'progress/file/delete',
         uploadBigUrl: 'progress/file/upload/big',
+        moveUrl: 'progress/file/move',
+        saveUrl: 'progress/file/save',
         checked: false,
         zipName: `阶段进度-附件.zip`,
         readOnly: readOnly,
@@ -513,6 +515,7 @@ $(document).ready(() => {
             user_id: 'user_id',
             create_time: 'create_time',
         },
+        search: true,
         locate: function (att) {
             if (!att) return;
             SpreadJsObj.locateTreeNode(progressObj.sheet, att.node.tree_id, true);

+ 4 - 0
app/router.js

@@ -268,12 +268,16 @@ module.exports = app => {
     app.post('/sp/:id/progress/file/upload', sessionAuth, subProjectCheck, 'subProjController.uploadFile');
     app.post('/sp/:id/progress/file/upload/big', sessionAuth, subProjectCheck, 'subProjController.uploadBigFile');
     app.post('/sp/:id/progress/file/delete', sessionAuth, subProjectCheck, 'subProjController.deleteFile');
+    app.post('/sp/:id/progress/file/move', sessionAuth, subProjectCheck, 'subProjController.moveFile');
+    app.post('/sp/:id/progress/file/save', sessionAuth, subProjectCheck, 'subProjController.saveFile');
     // 推进记录
     app.get('/sp/:id/push', sessionAuth, subProjectCheck, 'subProjController.push');
     app.post('/sp/:id/push/update', sessionAuth, subProjectCheck, 'subProjController.pushUpdate');
     app.post('/sp/:id/push/file/upload', sessionAuth, subProjectCheck, 'subProjController.uploadFile');
     app.post('/sp/:id/push/file/upload/big', sessionAuth, subProjectCheck, 'subProjController.uploadBigFile');
     app.post('/sp/:id/push/file/delete', sessionAuth, subProjectCheck, 'subProjController.deleteFile');
+    app.post('/sp/:id/push/file/move', sessionAuth, subProjectCheck, 'subProjController.moveFile');
+    app.post('/sp/:id/push/file/save', sessionAuth, subProjectCheck, 'subProjController.saveFile');
 
     // **标段管理
     // 金额概况

+ 23 - 0
app/service/sub_proj_file.js

@@ -7,6 +7,7 @@
  * @date 2019/1/11
  * @version
  */
+const path = require('path');
 
 module.exports = app => {
     class SubProjFile extends app.BaseService {
@@ -88,6 +89,28 @@ module.exports = app => {
             }
             return result;
         }
+
+        async saveFile(id, filename){
+            const file = await this.getDataById(id);
+            if (!file) throw '文件不存在';
+            // if (file.user_id !== this.ctx.session.sessionUser.accountId && this.ctx.subProject.permission.file_permission.indexOf(this.ctx.service.subProjPermission.PermissionConst.file.editfile.value) < 0) throw '您无权编辑该文件';
+            if (file.user_id !== this.ctx.session.sessionUser.accountId) throw '您无权编辑该文件';
+
+            const info = path.parse(filename);
+            const updateData = { id, filename: info.name, fileext: info.ext};
+            await this.defaultUpdate(updateData);
+            return updateData;
+        }
+
+        async moveFile(id, rela_id) {
+            const file = await this.getDataById(id);
+            if (!file) throw '文件不存在';
+            // if (file.user_id !== this.ctx.session.sessionUser.accountId && this.ctx.subProject.permission.file_permission.indexOf(this.ctx.service.subProjPermission.PermissionConst.file.editfile.value) < 0) throw '您无权编辑该文件';
+            if (file.user_id !== this.ctx.session.sessionUser.accountId) throw '您无权编辑该文件';
+            const updateData = { id, rela_id };
+            await this.defaultUpdate(updateData);
+            return updateData;
+        }
     }
 
     return SubProjFile;