Browse Source

资料归集相关1.0

MaiXinRong 2 years ago
parent
commit
5e7c521a52

+ 93 - 0
app/controller/file_controller.js

@@ -31,6 +31,99 @@ module.exports = app => {
                 ctx.log(err);
             }
         }
+
+        async file(ctx) {
+            try {
+                const renderData = {
+                    jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.file.file),
+                };
+                renderData.filing = await ctx.service.filing.getValidFiling(ctx.params.id, ctx.subProject.permission.filing_type);
+                renderData.categoryData = await ctx.service.category.getAllCategory(ctx.session.sessionProject.id);
+                renderData.canFiling = ctx.subProject.permission.file_permission.indexOf(ctx.service.subProjPermission.PermissionConst.file.filing.value) >= 0;
+                renderData.canUpload = ctx.subProject.permission.file_permission.indexOf(ctx.service.subProjPermission.PermissionConst.file.upload.value) >= 0;
+                renderData.filingTypes = ctx.service.filing.filingType;
+                await this.layout('file/file.ejs', renderData, 'file/file_modal.ejs');
+            } catch (err) {
+                ctx.log(err);
+            }
+        }
+
+        async getFilingTypePermission(ctx) {
+            try {
+                if (ctx.subProject.project_id !== this.ctx.session.sessionProject.id) throw '您无权操作该数据';
+
+                const filingType = await ctx.service.subProjPermission.getFilingType(ctx.subProject.id);
+                ctx.body = { err: 0, msg: '', data: filingType };
+            } catch(err) {
+                ctx.log(err);
+                ctx.ajaxErrorBody(err, '获取授权用户数据错误');
+            }
+        }
+
+        async saveFilingTypePermission(ctx) {
+            try {
+                const data = JSON.parse(ctx.request.body.data);
+                await ctx.service.subProjPermission.saveFilingType(data);
+                ctx.body = { err: 0, msg: '', data: '' };
+            } catch(err) {
+                ctx.log(err);
+                ctx.ajaxErrorBody(err, '保存授权用户信息错误');
+            }
+        }
+
+        async addFiling(ctx) {
+            try {
+                const data = JSON.parse(ctx.request.body.data);
+                const result = await ctx.service.filing.add(data);
+                ctx.body = { err: 0, msg: '', data: result };
+            } catch (err) {
+                ctx.log(err);
+                ctx.ajaxErrorBody(err, '新增分类失败');
+            }
+        }
+        async delFiling(ctx) {
+            try {
+                const data = JSON.parse(ctx.request.body.data);
+                const result = await ctx.service.filing.del(data);
+                ctx.body = { err: 0, msg: '', data: result };
+            } catch (err) {
+                ctx.log(err);
+                ctx.ajaxErrorBody(err, '删除分类失败');
+            }
+        }
+        async saveFiling(ctx) {
+            try {
+                const data = JSON.parse(ctx.request.body.data);
+                const result = await ctx.service.filing.save(data);
+                ctx.body = { err: 0, msg: '', data: result };
+            } catch (err) {
+                ctx.log(err);
+                ctx.ajaxErrorBody(err, '保存分类数据失败');
+            }
+        }
+
+        async loadFile(ctx) {
+            try {
+                const data = JSON.parse(ctx.request.body.data);
+                const result = await ctx.service.file.getAllDataByCondition({
+                    where: { filing_id: data.filing_id, is_deleted: 0 },
+                    orders: [['create_time', 'asc']],
+                    limit: data.count,
+                    offset: (data.page-1)*data.count
+                });
+                ctx.body = { err: 0, msg: '', data: result };
+            } catch (err) {
+                ctx.log(err);
+                ctx.ajaxErrorBody(err, '加载文件失败');
+            }
+        }
+
+        async uploadFile(ctx){
+        }
+        async delFile(ctx) {
+        }
+        async relaFile(ctx) {
+        }
     }
 
     return BudgetController;

+ 3 - 0
app/controller/sub_proj_controller.js

@@ -38,6 +38,7 @@ module.exports = app => {
                 });
                 renderData.permissionConst = ctx.service.subProjPermission.PermissionConst;
                 renderData.categoryData = await this.ctx.service.category.getAllCategory(this.ctx.session.sessionProject.id);
+                renderData.companys = await this.ctx.service.constructionUnit.getAllDataByCondition({ where: { pid: ctx.session.sessionProject.id } });
                 await this.layout('sub_proj/index.ejs', renderData, 'sub_proj/modal.ejs');
             } catch (err) {
                 ctx.log(err);
@@ -104,6 +105,8 @@ module.exports = app => {
                     result = await ctx.service.subProject.setRelaTender({ id: data.id, rela_tender: data.rela_tender });
                 } else if (data.std_id !== undefined) {
                     result = await ctx.service.subProject.setBudgetStd({ id: data.id, std_id: data.std_id });
+                } else if (data.management !== undefined) {
+                    result = await ctx.service.subProject.setManagement({ id: data.id, management: data.management });
                 }
                 ctx.body = { err: 0, msg: '', data: { update: [result] } };
             } catch(err) {

+ 21 - 0
app/extend/helper.js

@@ -1272,6 +1272,19 @@ module.exports = {
         const reg = /(.png)|(.gif)|(.txt)|(.jpg)|(.jpeg)|(.pdf)/i;
         return reg.test(ext);
     },
+    fileExtStr(ext) {
+        const ExtStr = [
+            { text: '图片', ext: ['.png', '.jpg', '.jpeg', '.gif', '.bmp', '.cad', '.dwg',] },
+            { text: '压缩包', ext: ['.zip', '.rar', '.7z',] },
+            { text: '音频', ext: ['.mp3'] },
+            { text: '视频', ext: ['.mp4'] },
+            { text: '文档', ext: ['.json', '.txt', '.xls', '.xlsx', '.doc', '.docx', '.pdf', '.ppt', '.pptx',]},
+        ];
+        for (const es of ExtStr) {
+            if (es.indexOf(ext) >= 0) return es.text;
+        }
+        return '';
+    },
 
     /**
      * 查找数组中某个字符的个数
@@ -1565,4 +1578,12 @@ module.exports = {
         }
         return message;
     },
+
+    mapAllSubField(obj, field) {
+        const result = [];
+        for (const prop in obj) {
+            result.push(obj[prop][field]);
+        }
+        return result;
+    }
 };

+ 48 - 0
app/middleware/sub_project_check.js

@@ -0,0 +1,48 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Mai
+ * @date
+ * @version
+ */
+
+module.exports = options => {
+    /**
+     * 标段校验 中间件
+     * 1. 读取标段数据(包括属性)
+     * 2. 检验用户是否可见标段(不校验具体权限)
+     *
+     * @param {function} next - 中间件继续执行的方法
+     * @return {void}
+     */
+    return function* subProjectCheck(next) {
+        try {
+            // 读取标段数据
+            const id = this.params.id || this.query.id;
+            if (!id) throw '参数错误';
+
+            this.subProject = yield this.service.subProject.getDataById(id);
+            if (!this.subProject) throw '项目不存在';
+
+            if (this.session.sessionUser.is_admin) {
+                this.subProject.readOnly = false;
+                this.subProject.permission = this.service.subProjPermission.adminPermission;
+            } else {
+                const bp = yield this.service.subProjPermission.getSubProjectUserPermission(id, this.session.sessionUser.accountId);
+                if (!bp) throw '您无权查看该项目';
+                this.subProject.permission = bp;
+            }
+            yield next;
+        } catch (err) {
+            this.log(err);
+            if (this.helper.isAjax(this.request)) {
+                this.ajaxErrorBody(err, '未知错误');
+            } else {
+                this.postError(err, '未知错误');
+                this.redirect(this.request.headers.referer);
+            }
+        }
+    };
+};

+ 367 - 0
app/public/js/file_detail.js

@@ -0,0 +1,367 @@
+$(document).ready(function() {
+    class FilingObj {
+        constructor(setting) {
+            // 原始数据整理后的树结构,用来整理zTree显示
+            this.dragTree = createDragTree({
+                id: 'id',
+                pid: 'tree_pid',
+                level: 'tree_level',
+                order: 'tree_order',
+                rootId: '-1'
+            });
+            // 界面显示的zTree
+            this.setting = setting;
+            this.filingTree = null;
+            this.pageCount = 15;
+        }
+        analysisFiling(data) {
+            this.dragTree.loadDatas(data);
+            this.dragTree.recursiveFun(this.dragTree.children, x => {
+                if (x.children && x.children.length > 0) {
+                    x.total_file_count = x.children.map(y => {
+                        return y.total_file_count;
+                    }).reduce((pre, value) => {
+                        return pre + value
+                    }, 0);
+                } else {
+                    x.total_file_count = x.file_count;
+                }
+            });
+            const sortNodes = this.dragTree.nodes.map(x => {
+                return {
+                    id: x.id,
+                    tree_pid: x.tree_pid,
+                    name: x.name + (x.total_file_count > 0 ? `(${x.total_file_count})` : ''),
+                    spid: x.spid,
+                    source_node: x,
+                };
+            });
+            this.filingTree = $.fn.zTree.init($('#filing'), this.setting, sortNodes);
+        }
+        _getFileHtml(file) {
+            const html = [];
+            html.push('<tr>');
+            html.push('<tr><td><input type="checkbox"></td>');
+            const editHtml = file.canEdit ? '<a href="" class="mr-1"><i class="fa fa-pencil fa-fw"></i></a>' : '';
+            const viewHtml = file.viewpath ? `<a href="${file.viewpath}" class="mr-1"><i class="fa fa-eye fa-fw"></i></a>` : '';
+            const downHtml = '<a href="" class="mr-1"><i class="fa fa-download fa-fw"></i></a>';
+            const delHtml = file.canEdit ? '<a href="" class="mr-1"><i class="fa fa-trash-o fa-fw"></i></a>' : '';
+            html.push(`<td><div class="d-flex justify-content-between align-items-center table-file"><div></div>${file.filename}</div><div class="btn-group-table" style="display: none;">${editHtml}${viewHtml}${downHtml}${delHtml}</div></div></td>`);
+            html.push(`<td>${file.user_name}</td>`);
+            html.push(`<td>${moment(file.create_time).format('YYYY-MM-DD HH:mm:ss')}</td>`);
+            html.push('<td>${file.fileext_str}</td>');
+            html.push('</tr>');
+            return html.join('');
+        }
+        refreshFilesTable() {
+            const html = [];
+            const files = this.curFiling.source_node.files;
+            if (!files || files.length === 0) return;
+
+            const startIndex = (this.curPage - 1)*this.pageCount;
+            const endIndex = this.curPage*this.pageCount;
+            for (const [i, f] of files.entries()) {
+                if (i < startIndex || i >= endIndex) continue;
+                html.push(this._getFileHtml(f));
+            }
+            $('#file-list').html(html.join(''));
+        }
+        async loadFiles(node, page) {
+            if (node.source_node.children && node.source_node.children.length > 0) return;
+
+            if (!node.source_node.file_count) return;
+            if (node.source_node.files.length === node.source_node.file_count) return;
+
+            const needFiles = Math.min(page*this.pageCount, node.source_node.file_count);
+            if (needFiles < node.source_node.files.length) return;
+
+            if (!node.source_node.files) node.source_node.files = [];
+            const files = await postDataAsync('file/load', { filing_id: node.id, page, count: this.pageCount });
+            node.source_node.files.push(...files);
+            node.source_node.files.sort((x, y) => {
+                return x.create_time - y.create_time;
+            });
+        }
+        addSiblingFiling(node) {
+            const self = this;
+            postData('filing/add', { tree_pid: node.tree_pid, tree_pre_id: node.id }, function(result) {
+                const refreshData = self.dragTree.loadPostData(result);
+                const newNode = refreshData.create[0];
+                self.filingTree.addNodes(node.getParentNode(), node.getIndex(), [{ id: newNode.id, tree_pid: newNode.tree_pid, name: newNode.name, spid: newNode.spid, source_node: newNode}]);
+            });
+        }
+        addChildFiling(node) {
+            const self = this;
+            postData('filing/add', { tree_pid: node.id }, function(result) {
+                const refreshData = self.dragTree.loadPostData(result);
+                const newNode = refreshData.create[0];
+                self.filingTree.addNodes(node, -1, [{ id: newNode.id, tree_pid: newNode.tree_pid, name: newNode.name, spid: newNode.spid, source_node: newNode}]);
+            });
+        }
+        delFiling(node) {
+            const self = this;
+            postData('filing/del', { id: node.id }, function(result) {
+                self.dragTree.loadPostData(result);
+                self.filingTree.removeNode(node);
+            });
+        }
+        async renameFiling(node, newName) {
+            return await postDataAsync('filing/save', { id: node.id, name: newName });
+        }
+    }
+    const levelTreeSetting = {
+        view: {
+            selectedMulti: false
+        },
+        data: {
+            simpleData: {
+                idKey: 'id',
+                pIdKey: 'tree_pid',
+                rootPId: '-1',
+                enable: true,
+            }
+        },
+        edit: {
+            enable: true,
+            showRemoveBtn: false,
+            showRenameBtn: function(treeId, treeNode) {
+                if (!canFiling) return false;
+                return !treeNode.source_node.is_fixed;
+            },
+            renameTitle: '编辑',
+            drag: {
+                isCopy: false,
+                isMove: false,
+            }
+        },
+        callback: {
+            onClick: async function (e, key, node) {
+                if (filingObj.curFiling && filingObj.curFiling.id === node.id) return;
+
+                filingObj.curFiling = node;
+                filingObj.curPage = 1;
+                if (filingObj.curFiling.source_node.children && filingObj.curFiling.source_node.children.length > 0) {
+                    $('#file-view').hide();
+                } else {
+                    $('#file-view').show();
+                    await filingObj.loadFiles(node, 1);
+                    filingObj.refreshFilesTable();
+                }
+                if (filingObj.curFiling.source_node.filing_type === 5) {
+                    $('#rela-file').parent().show();
+                } else {
+                    $('#rela-file').parent().hide();
+                }
+            },
+            beforeRename: async function(key, node, newName, isCancel) {
+                const result = await filingObj.renameFiling(node, newName);
+                return !result;
+            }
+        }
+    };
+    const filingObj = new FilingObj(levelTreeSetting);
+    filingObj.analysisFiling(filing);
+    $('#add-slibing').click(() => {
+        if (!filingObj.curFiling) return;
+        if (filingObj.curFiling.source_node.is_fixed) {
+            toastr.error('固定分类不可添加同级');
+            return;
+        }
+
+        filingObj.addSiblingFiling(filingObj.curFiling);
+    });
+    $('#add-child').click(() => {
+        if (!filingObj.curFiling) return;
+        if (filingObj.curFiling.source_node.file_count > 0) {
+            toastr.error('该分类下已导入文件,不可添加子级');
+            return;
+        }
+
+        filingObj.addChildFiling(filingObj.curFiling);
+    });
+    $('#del-filing').click(() => {
+        if (!filingObj.curFiling) return;
+        if (filingObj.curFiling.source_node.is_fixed) {
+            toastr.error('固定分类不可删除');
+            return;
+        }
+
+        filingObj.delFiling(filingObj.curFiling);
+    });
+
+    // 授权相关
+    class FilingPermission {
+        constructor (setting) {
+            this.setting = setting;
+            const self = this;
+            $(setting.modal).on('show.bs.modal', () => {
+                self.loadPermission();
+            });
+            $(`${setting.modal}-ok`).click(() => {
+                self.savePermission();
+            });
+            $('[name=ftName]').click(function () {
+                const filingId = this.getAttribute('ftid');
+                self.setCurFiling(filingId);
+            });
+
+            $('.book-list').on('click', 'dt', function () {
+                const idx = $(this).find('.acc-btn').attr('data-groupid');
+                const type = $(this).find('.acc-btn').attr('data-type');
+                if (type === 'hide') {
+                    $(this).parent().find(`div[data-toggleid="${idx}"]`).show(() => {
+                        $(this).children().find('i').removeClass('fa-plus-square').addClass('fa-minus-square-o')
+                        $(this).find('.acc-btn').attr('data-type', 'show')
+
+                    })
+                } else {
+                    $(this).parent().find(`div[data-toggleid="${idx}"]`).hide(() => {
+                        $(this).children().find('i').removeClass('fa-minus-square-o').addClass('fa-plus-square')
+                        $(this).find('.acc-btn').attr('data-type', 'hide')
+                    })
+                }
+                return false;
+            });
+            $('dl').on('click', 'dd', function () {
+                const type = $(this).data('type');
+                if (type === 'all') {
+                    const cid = parseInt($(this).data('id'));
+                    const company = self.company.find(x => { return x.id === cid });
+                    for (const u of company.users) {
+                        if (u.filing_type.indexOf(self.curFiling) < 0) u.filing_type.push(self.curFiling);
+                    }
+                } else {
+                    const uid = $(this).data('id');
+                    const pu = self.permissionUser.find(x => { return x.id === uid });
+                    if (pu.filing_type.indexOf(self.curFiling) < 0) pu.filing_type.push(self.curFiling);
+                }
+                self.loadCurFiling();
+            });
+            $('#sync-filing').click(function() {
+                const selectFiling = $('[name=cbft]:checked');
+                if (selectFiling.length === 0) {
+                    toastr.warning('请先选择文档类别');
+                    return;
+                }
+
+                const selectFilingId = [];
+                selectFiling.each((i, x) => { selectFilingId.push(x.value); });
+                self.syncFiling(self.curFiling, selectFilingId);
+                toastr.success('同步成功');
+                $('[name=cbft]').each((i, x) => { x.checked = false; });
+            });
+            $('#batch-del-filing').click(() => {
+                const selectUser = $('[name=ftu-check]:checked');
+                if (selectUser.length === 0) {
+                    toastr.warning('请先选择用户');
+                    return;
+                }
+                const userId = [];
+                selectUser.each((i, x) => { userId.push(x.getAttribute('uid')); });
+                self.delFiling(self.curFiling, userId);
+                self.loadCurFiling();
+            });
+            $('body').on('click', '[name=del-filing]', function() {
+                const id = this.getAttribute('uid');
+                self.delFiling(self.curFiling, id);
+                self.loadCurFiling();
+            })
+        }
+        analysisFiling(data) {
+            this.permissionUser = data;
+            this.permissionUser.forEach(x => { x.filing_type = x.filing_type ? x.filing_type.split(',') : []; });
+
+            this.company = [];
+            for (const pu of this.permissionUser) {
+                let c = this.company.find(x => { return x.company === pu.company });
+                if (!c) {
+                    c = { id: this.company.length + 1, company: pu.company, users: [] };
+                    this.company.push(c);
+                }
+                c.users.push(pu);
+            }
+        }
+        loadCurFiling() {
+            const html = [];
+            for (const f of this.permissionUser) {
+                if (f.filing_type.indexOf(this.curFiling) < 0) continue;
+                html.push('<tr>');
+                html.push(`<td><input uid="${f.id}" type="checkbox" name="ftu-check"></td>`);
+                html.push(`<td>${f.name}</td>`);
+                html.push(`<td>${moment(f.create_time).format('YYYY-MM-DD HH:mm:ss')}</td>`);
+                html.push(`<td>${f.file_permission}</td>`);
+                html.push(`<td><button class="btn btn-sm btn-outline-danger" uid="${f.id}" name="del-filing">移除</button></td>`);
+                html.push('</tr>');
+            }
+            $(this.setting.list).html(html.join(''));
+        }
+        setCurFiling(filingType) {
+            this.curFiling = filingType;
+            $('[name=ftName]').removeClass('bg-warning-50');
+            $(`[ftid=${filingType}]`).addClass('bg-warning-50');
+            this.loadCurFiling();
+        }
+        loadPermissionUser() {
+            const html = [];
+            for (const c of this.company) {
+                html.push(`<dt><a href="javascript: void(0);" class="acc-btn" data-groupid="${c.id}" data-type="hide"><i class="fa fa-plus-square"></i></a> ${c.company}</dt>`);
+                html.push(`<div class="dd-content" data-toggleid="${c.id}">`);
+                html.push(`<dd class="border-bottom p-2 mb-0 " data-id="${c.id}" data-type="all"><p class="mb-0 d-flex"><span class="text-primary">添加单位下全部用户</span></p></dd>`);
+                for (const u of c.users) {
+                    html.push(`<dd class="border-bottom p-2 mb-0 " data-id="${u.id}" >`);
+                    html.push(`<p class="mb-0 d-flex"><span class="text-primary">${u.name}</span><span class="ml-auto">${u.mobile}</span></p>`);
+                    html.push(`<span class="text-muted">${u.role}</span>`);
+                    html.push(`</dd>`);
+                }
+                html.push('</div>');
+            }
+            $('#puList').html(html.join(''));
+        }
+        loadPermission() {
+            const self = this;
+            postData('permission', {}, function(result) {
+                self.analysisFiling(result);
+                if (!self.curFiling) {
+                    self.setCurFiling($('[name=ftName]').attr('ftid'));
+                } else {
+                    self.loadCurFiling();
+                }
+                self.loadPermissionUser();
+            });
+        }
+        syncFiling(sourceId, targetIds) {
+            for (const pu of this.permissionUser) {
+                if (pu.filing_type.indexOf(sourceId) >= 0) {
+                    targetIds.forEach(id => {
+                        if (pu.filing_type.indexOf(id) < 0) pu.filing_type.push(id);
+                    });
+                } else {
+                    targetIds.forEach(id => {
+                        if (pu.filing_type.indexOf(id) >= 0) pu.filing_type.splice(pu.filing_type.indexOf(id), 1);
+                    })
+                }
+            }
+        }
+        delFiling(filingId, userId) {
+            const userIds = userId instanceof Array ? userId : [userId];
+            for (const id of userIds) {
+                const pu = this.permissionUser.find(x => { return x.id === id });
+                if (!pu) continue;
+                if (pu.filing_type.indexOf(filingId) >= 0) pu.filing_type.splice(pu.filing_type.indexOf(filingId), 1);
+            }
+        }
+        savePermission() {
+            const self = this;
+            const data = this.permissionUser.map(x => {
+                return { id: x.id, filing_type: x.filing_type.join(',') };
+            });
+            postData('permission/save', data, function(result) {
+                $(self.setting.modal).modal('hide');
+            });
+        }
+    }
+    const filingPermission = new FilingPermission({
+        modal: '#filing-permission',
+        list: '#filing-valid',
+    });
+});

+ 1 - 1
app/public/js/file.js

@@ -37,7 +37,7 @@ $(document).ready(() => {
                     }
                 } else {
                     html.push(`<span class="text-muted mr-2">${tree.isLastSibling(node) ? '└' : '├'}</span>`);
-                    html.push(`<a href="/file/${node.id}/compare" name="name" id="${node.id}">`, node.name, '</a>');
+                    html.push(`<a href="/sp/${node.id}/file" name="name" id="${node.id}">`, node.name, '</a>');
                 }
                 html.push('</td>');
                 // 管理单位

+ 10 - 0
app/public/js/shares/drag_tree.js

@@ -439,6 +439,16 @@ const createDragTree = function (setting) {
             }
             return result;
         }
+
+        recursiveFun(children, fun) {
+            if (!fun) return;
+            if (!children || children.length === 0) return;
+
+            for (const c of children) {
+                this.recursiveFun(c.children, fun);
+                fun(c);
+            }
+        }
     }
     return new DragTree(setting);
 };

+ 12 - 2
app/public/js/shenpi.js

@@ -59,7 +59,7 @@ function initTenderTree () {
                 if (index === 0 && tender.category) {
                     for (const [i,c] of tender.category.entries()) {
                         const cate = findNode('id', c.cid, category);
-                        tenderCategory = getCategoryNode(cate, c.value, tenderCategory, i+1);
+                        if (cate) tenderCategory = getCategoryNode(cate, c.value, tenderCategory, i+1);
                     }
                 }
                 return tenderCategory;
@@ -684,6 +684,9 @@ $(document).ready(function () {
             $('#del-audit-ass').click(function () {
                 self.setAuditAssist();
             });
+            $('body').on('click', 'button[asid]', function () {
+                self.removeAuditAss(parseInt(this.getAttribute('asid')));
+            });
         }
         set uid(id) {
             this._uid = parseInt(id);
@@ -697,7 +700,7 @@ $(document).ready(function () {
             const html = [];
             for (const sa of this.showAssList) {
                 const lid = sa.ass_ledger_id ? sa.ass_ledger_id.split(',') : [];
-                html.push(`<tr><td>${sa.name}</td><td>${sa.company}</td><td>${lid.length}</td></tr>`);
+                html.push(`<tr><td>${sa.name}</td><td>${sa.company}</td><td>${lid.length}<button class="ml-2 btn btn-sm btn-outline-danger" asid="${sa.id}">移除</button></td></tr>`);
             }
             $('#coo_table').html(html.join(''));
         }
@@ -784,6 +787,13 @@ $(document).ready(function () {
                 self.refreshOperate();
             });
         }
+        removeAuditAss(assistId) {
+            const self = this;
+            postData('/tender/' + cur_tenderid + '/shenpi/audit/save', { type: 'audit-ass', del: { id: assistId } }, function (result) {
+                self.loadPostData(result);
+                self.refreshOperate();
+            });
+        }
         refreshOperate() {
             const node = SpreadJsObj.getSelectObject(this.sheet);
             if (node.ass_audit_id) {

+ 20 - 3
app/public/js/sub_project.js

@@ -45,7 +45,7 @@ $(document).ready(function() {
                     html.push(`<td class="text-center"></td>`);
                 } else {
                     html.push(`<td class="text-center">${moment(node.create_time).format('YYYY-MM-DD')}</td>`);
-                    html.push(`<td class="text-center">${node.management || ''}</td>`);
+                    html.push(`<td class="text-center">${node.management || ''}<a class="ml-2" href="javascript: void(0)" name="set-management"><i class="fa fa-pencil-square-o "></i></a></td>`);
                 }
                 // 操作
                 html.push(`<td>`);
@@ -160,6 +160,13 @@ $(document).ready(function() {
             $('[name=std_id]').attr('tree_id', treeId);
             $('#set-std').modal('show');
         });
+        $('body').on('click', 'a[name=set-management]', function(e) {
+            const treeId = $(this).parent().parent().attr('tree_id');
+            const node = ProjectTree.getItems(treeId);
+            if (node.is_folder) return;
+            $('#sm-management').attr('tree_id', treeId);
+            $('#set-management').modal('show');
+        });
         return { ProjectTree, TableObj, ...Utils };
     })({
         treeSetting: { id: 'id', pid: 'tree_pid', level: 'tree_level', order: 'tree_order', rootId: '-1' },
@@ -234,6 +241,16 @@ $(document).ready(function() {
             $('[name=std_id]').attr('tree_id', '');
         });
     });
+    $('#set-management-ok').click(function() {
+        const select = $('#sm-management');
+        const id = select.attr('tree_id');
+        const management = select.val();
+        postData('/subproj/save', { id, management }, function(result) {
+            projectTreeObj.refreshRow(result);
+            $('#set-management').modal('hide');
+            $('#sm-management').attr('tree_id', '');
+        });
+    });
 
     let timer = null;
     let oldSearchVal = null;
@@ -319,7 +336,7 @@ $(document).ready(function() {
             html.push(`<td><div class="custom-control custom-checkbox mb-2">
                         <input type="checkbox" ptype="file" sptype="upload" id="fileupload${mem.uid}" uid="${mem.uid}" class="custom-control-input" ${(fileUpload ? 'checked' : '')}>
                         <label class="custom-control-label" for="fileupload${mem.uid}"></label></div></td>`);
-            const fileEdit = mem.file_permission.indexOf(permissionConst.file.edit.value) >= 0;
+            const fileEdit = mem.file_permission.indexOf(permissionConst.file.filing.value) >= 0;
             html.push(`<td><div class="custom-control custom-checkbox mb-2">
                         <input type="checkbox" ptype="file" sptype="eidt" uid="${mem.uid}" id="fileedit${mem.uid}" class="custom-control-input" ${(fileEdit ? 'checked' : '')}>
                         <label class="custom-control-label" for="fileedit${mem.uid}"></label></div></td>`);
@@ -420,7 +437,7 @@ $(document).ready(function() {
             } else if (pType === 'file' && spType === 'upload') {
                 mem.file_permission.splice(mem.file_permission.indexOf(permissionConst.file.upload.value), 1);
             } else if (pType === 'file' && spType === 'edit') {
-                mem.file_permission.splice(mem.file_permission.indexOf(permissionConst.file.edit.value), 1);
+                mem.file_permission.splice(mem.file_permission.indexOf(permissionConst.file.filing.value), 1);
             } else if (pType === 'manage' && spType === 'rela') {
                 mem.manage_permission = [];
             }

+ 8 - 0
app/router.js

@@ -40,6 +40,7 @@ module.exports = app => {
     // 修订
     const reviseCheck = app.middlewares.reviseCheck();
     const budgetCheck = app.middlewares.budgetCheck();
+    const subProjectCheck = app.middlewares.subProjectCheck();
     // 登入登出相关
     app.get('/login', 'loginController.index');
     app.get('/login/:code', 'loginController.index');
@@ -714,6 +715,13 @@ module.exports = app => {
     app.post('/budget/:id/decimal', sessionAuth, budgetCheck, 'budgetController.decimal');
     // 电子档案
     app.get('/file', sessionAuth, 'fileController.index');
+    app.get('/sp/:id/file', sessionAuth, subProjectCheck, 'fileController.file');
+    app.post('/sp/:id/permission', sessionAuth, projectManagerCheck, subProjectCheck, 'fileController.getFilingTypePermission');
+    app.post('/sp/:id/permission/save', sessionAuth, projectManagerCheck, subProjectCheck, 'fileController.saveFilingTypePermission');
+    app.post('/sp/:id/filing/add', sessionAuth, subProjectCheck, 'fileController.addFiling');
+    app.post('/sp/:id/filing/save', sessionAuth, subProjectCheck, 'fileController.saveFiling');
+    app.post('/sp/:id/filing/del', sessionAuth, subProjectCheck, 'fileController.delFiling');
+    app.post('/sp/:id/file/load', sessionAuth, subProjectCheck, 'fileController.loadFile');
 
     // 支付审批
     app.get('/payment', sessionAuth, 'paymentController.index');

+ 56 - 0
app/service/file.js

@@ -0,0 +1,56 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Mai
+ * @date
+ * @version
+ */
+
+
+
+module.exports = app => {
+    class Filing extends app.BaseService {
+
+        /**
+         * 构造函数
+         *
+         * @param {Object} ctx - egg全局变量
+         * @param {String} tableName - 表名
+         * @return {void}
+         */
+        constructor(ctx) {
+            super(ctx);
+            this.tableName = 'file';
+        }
+
+        async getFileList(spid, filing_id) {
+            const result = await this.getAllDataByCondition({
+                where: { spid, filing_id },
+                orders: [['create_time', 'asc']],
+                limit: this.app.config.pageSize, offset: (this.ctx.page - 1) * this.app.config.pageSize
+            });
+            const helper = this.ctx.helper;
+            result.forEach(x => {
+                x.viewpath = helper.canPreview(x.fileext) ? ctx.app.config.fujianOssPath + x.filepath : '';
+                x.fileext_str = helper.fileExtStr(x.fileext);
+            });
+            return result;
+        }
+
+        async addFile(filing_id, fileInfo, user) {
+            const filing = await this.ctx.service.filing.getDataById(filing_id);
+            if (!filing || filing.is_delete) throw '当前分类不存在';
+
+            const insertData = {
+                spid: filing.spid, filing_id: filing.id, filing_type: filing.filing_type,
+                user_id: user.id, user_name: user.name, user_company: user.company, user_role: user.role,
+                filename: fileInfo.filename, fileext: fileInfo.fileext, filesize: fileInfo.fileSize, filepath: fileInfo.filepath,
+            };
+            const result = await this.db.insert(this.tableName, insertData);
+        }
+    }
+
+    return Filing;
+};

+ 167 - 0
app/service/filing.js

@@ -0,0 +1,167 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Mai
+ * @date
+ * @version
+ */
+const rootId = '-1';
+const filingType = [
+    { value: 1, name: '立项文件' },
+    { value: 2, name: '招标投标、合同协议文件' },
+    { value: 3, name: '勘察、设计文件' },
+    { value: 4, name: '征地、拆迁、移民文件' },
+    { value: 5, name: '项目管理文件' },
+    { value: 6, name: '施工文件' },
+    { value: 7, name: '信息系统开发文件' },
+    { value: 8, name: '设备文件' },
+    { value: 9, name: '监理文件' },
+    { value: 10, name: '科研项目文件' },
+    { value: 11, name: '生产技术准备、试运行文件' },
+    { value: 12, name: '竣工验收文件' },
+];
+
+module.exports = app => {
+    class Filing extends app.BaseService {
+
+        /**
+         * 构造函数
+         *
+         * @param {Object} ctx - egg全局变量
+         * @param {String} tableName - 表名
+         * @return {void}
+         */
+        constructor(ctx) {
+            super(ctx);
+            this.tableName = 'filing';
+            this.filingType = filingType;
+        }
+
+        get allFilingType () {
+            return filingType.map(x => { return x.value });
+        }
+
+        async initFiling(spid, transaction) {
+            const count = await this.count({ spid });
+            if (count > 0) return;
+
+            const insertData = [];
+            for (const [i, f] of filingType.entries()) {
+                insertData.push({
+                    id: this.uuid.v4(), tree_pid: -1, tree_level: 1, tree_order: i + 1,
+                    spid, add_user_id: this.ctx.session.sessionUser.accountId, is_fixed: 1,
+                    filing_type: f.value, name: f.name,
+                });
+            }
+            if (transaction) {
+                await transaction.insert(this.tableName, insertData);
+            } else {
+                await this.db.insert(this.tableName, insertData);
+            }
+        }
+
+        async getValidFiling(spid, filingType) {
+            const condition = { spid, is_deleted: 0 };
+            if (filingType !== 'all') condition.filing_type = filingType;
+            return await this.getAllDataByCondition({ where: condition });
+        }
+
+
+        async getPosterityData(id){
+            const result = [];
+            let cur = await this.getAllDataByCondition({ where: { tree_pid: id } });
+            let iLevel = 1;
+            while (cur.length > 0 && iLevel < 6) {
+                result.push(...cur);
+                cur = await this.getAllDataByCondition({ where: { tree_pid: cur.map(x => { return x.id })} });
+                iLevel += 1;
+            }
+            return result;
+        }
+
+        _checkFixed(data) {
+            if (data.is_fixed) throw '固定分类,不可编辑';
+        }
+
+        async add(data) {
+            const parent = await this.getDataById(data.tree_pid);
+            if (!parent) throw '添加数据结构错误';
+            if (parent.file_count > 0) throw `分类【${parent.name}】下存在文件,不可添加子分类`;
+
+            const sibling = await this.getAllDataByCondition({ where: { tree_pid: parent.id }, orders: [['tree_order', 'asc']]});
+            const preChild = data.tree_pre_id ? sibling.find(x => { x.id === data.tree_pre_id; }) : null;
+
+            const conn = await this.db.beginTransaction();
+            try {
+                // 获取当前用户信息
+                const sessionUser = this.ctx.session.sessionUser;
+                // 获取当前项目信息
+                const sessionProject = this.ctx.session.sessionProject;
+
+                const tree_order = preChild ? preChild.tree_order + 1 : (sibling.length > 0 ? sibling[sibling.length - 1].tree_order + 1 : 1);
+                const insertData = {
+                    id: this.uuid.v4(), spid: parent.spid, add_user_id: sessionUser.accountId,
+                    tree_pid: parent.id, tree_level: parent.tree_level + 1, tree_order,
+                    name: 'xx文件', filing_type: parent.filing_type,
+                };
+                const operate = await conn.insert(this.tableName, insertData);
+                if (operate.affectedRows === 0) throw '新增文件夹失败';
+
+                const updateData = [];
+                if (preChild) {
+                    sibling.forEach(x => {
+                        if (x.tree_order >= tree_order) updateData.push({ id: x.id, tree_order: x.tree_order + 1 });
+                    });
+                }
+                if (updateData.length > 0) await conn.updateRows(this.tableName, updateData);
+
+                await conn.commit();
+                return { create: [insertData], update: updateData };
+            } catch (error) {
+                await conn.rollback();
+                throw error;
+            }
+        }
+        async save(data) {
+            const filing = await this.getDataById(data.id);
+            this._checkFixed(filing);
+
+            const result = await this.db.update(this.tableName, data);
+            if (result.affectedRows > 0) {
+                return data;
+            } else {
+                throw '更新数据失败';
+            }
+        }
+
+        async del(data) {
+            const filing = await this.getDataById(data.id);
+            this._checkFixed(filing);
+
+            const posterity = await this.getPosterityData(data.id);
+            const delData = posterity.map(x => {return { id: x.id, is_deleted: 1 }; });
+            delData.push({ id: data.id, is_deleted: 1});
+
+            const sibling = await this.getAllDataByCondition({ where: { tree_pid: filing.tree_pid } });
+            const updateData = [];
+            sibling.forEach(x => {
+                if (x.tree_order > filing.tree_order) updateData.push({ id: x.id, tree_order: x.tree_order - 1});
+            });
+            const conn = await this.db.beginTransaction();
+            try {
+                await conn.updateRows(this.tableName, delData);
+                if (updateData.length > 0) conn.updateRows(this.tableName, updateData);
+
+                await conn.commit();
+                return { delete: delData.map(x => { return x.id }), update: updateData };
+            } catch(err) {
+                await conn.rollback();
+                throw error;
+            }
+        }
+    }
+
+    return Filing;
+};

+ 46 - 2
app/service/sub_proj_permission.js

@@ -29,7 +29,7 @@ module.exports = app => {
                 file: {
                     view: { title: '查看', value: 1 },
                     upload: { title: '上传文件', value: 2 },
-                    edit: { title: '文件类别编辑', value: 3 },
+                    filing: { title: '文件类别编辑', value: 3 },
                 },
                 manage: {
                     rela: { title: '关联标段', value: 1 },
@@ -37,6 +37,15 @@ module.exports = app => {
             };
         }
 
+        get adminPermission () {
+            return {
+                budget_permission: this.ctx.helper.mapAllSubField(this.PermissionConst.budget, 'value'),
+                file_permission: this.ctx.helper.mapAllSubField(this.PermissionConst.file, 'value'),
+                manage_permission: this.ctx.helper.mapAllSubField(this.PermissionConst.manage, 'value'),
+                filing_type: 'all',
+            }
+        }
+
         async showSubTab(uid, type) {
             const sql = `SELECT count(*) FROM ${this.tableName} WHERE ${type}_permission <> '' AND uid = ?`;
             const result = await this.db.queryOne(sql, [uid]);
@@ -56,6 +65,7 @@ module.exports = app => {
                 x.budget_permission = x.budget_permission ? _.map(x.budget_permission.split(','), _.toInteger) : [];
                 x.file_permission = x.file_permission ? _.map(x.file_permission.split(','), _.toInteger) : [];
                 x.manage_permission = x.manage_permission ? _.map(x.manage_permission.split(','), _.toInteger) : [];
+                x.filing_type = x.filing_type ? _.map(x.filing_type.split(','), _.toInteger): [];
             });
         }
 
@@ -90,6 +100,12 @@ module.exports = app => {
             return result;
         }
 
+        async getSubProjectUserPermission(spid, uid) {
+            const result = await this.getDataByCondition({ spid, uid });
+            if (result) this.parsePermission(result);
+            return result;
+        };
+
         async savePermission(subProjectId, member) {
             const orgMember = await this.getAllDataByCondition({ where: { spid: subProjectId } });
             const dm = [], um = [], im = [];
@@ -108,11 +124,12 @@ module.exports = app => {
             }
             for (const m of member) {
                 im.push({
+                    id: this.uuid.v4(),
                     spid: subProjectId, pid: this.ctx.session.sessionProject.id, uid: m.uid,
                     budget_permission: m.budget_permission.join(','),
                     file_permission: m.file_permission.join(','),
                     manage_permission: m.manage_permission.join(',')
-                })
+                });
             }
             const conn = await this.db.beginTransaction();
             try {
@@ -125,6 +142,33 @@ module.exports = app => {
                 throw err;
             }
         }
+
+        async getFilingType(subProjectId) {
+            const permissionConst = {}, prefix = 'f';
+            for (const p in this.PermissionConst.file) {
+                const fp = this.PermissionConst.file[p];
+                permissionConst[prefix + fp.value] = fp.title;
+            }
+            const result = await this.db.query(`SELECT spp.id, p.name, p.role, p.company, p.mobile, spp.file_permission, spp.filing_type 
+                FROM ${this.tableName} spp LEFT JOIN ${this.ctx.service.projectAccount.tableName} p
+                On spp.uid = p.id WHERE spp.spid = ? and file_permission <> ''`, [subProjectId]);
+            result.forEach(x => {
+                const filePermission = x.file_permission.split(',');
+                x.file_permission = filePermission.map(x => {
+                    return permissionConst[prefix + x] || '';
+                }).join(',');
+            });
+            return result;
+        }
+
+        // 资料归集,授权固定分类
+        async saveFilingType(data) {
+            const updateData = [];
+            data.forEach(x => {
+                updateData.push({ id: x.id, filing_type: x.filing_type });
+            });
+            if (updateData.length > 0) await this.db.updateRows(this.tableName, updateData);
+        }
     }
 
     return subProjPermission;

+ 37 - 6
app/service/sub_project.js

@@ -34,7 +34,7 @@ module.exports = app => {
                 const pb = permission.find(y => { return x.id === y.spid});
                 if (!pb) return false;
                 x.user_permission = pb;
-                return x.user_permission.budget_permission.length > 0 || x.user_permission.file_permission.length > 0 || x.manage_permission.length > 0;
+                return x.user_permission.budget_permission.length > 0 || x.user_permission.file_permission.length > 0 || x.user_permission.manage_permission.length > 0;
             });
             return result;
         }
@@ -50,9 +50,7 @@ module.exports = app => {
         }
 
         async getBudgetProject(pid, uid, admin) {
-            const sql = `SELECT sp.*, b.std_id From ${this.tableName} sp LEFT JOIN ${this.ctx.service.budget.tableName} b ON sp.budget_id = b.id  
-                WHERE sp.project_id = ? AND sp.is_delete = 0`;
-            let result = await this.db.query(sql, [pid]);
+            let result = await this.getAllDataByCondition({ where: { project_id: pid, is_delete: 0 } });
 
             const permission = await this.ctx.service.subProjPermission.getUserPermission(pid, uid);
             result = result.filter(x => {
@@ -64,7 +62,6 @@ module.exports = app => {
                 x.manage_permission = pb.manage_permission;
                 return x.budget_permission.length > 0;
             });
-            console.log(result.length);
             return this._filterEmptyFolder(result);
         }
 
@@ -73,12 +70,13 @@ module.exports = app => {
 
             const permission = await this.ctx.service.subProjPermission.getUserPermission(pid, uid);
             result = result.filter(x => {
+                if (!x.is_folder && !x.management) return false;
                 if (x.is_folder || admin) return true;
                 const pb = permission.find(y => { return x.id === y.spid});
                 if (!pb) return false;
                 x.permission = pb.file_permission;
                 x.manage_permission = pb.manage_permission;
-                return x.file_permission.length > 0;
+                return x.permission.length > 0;
             });
             return this._filterEmptyFolder(result);
         }
@@ -251,6 +249,39 @@ module.exports = app => {
                 await conn.rollback();
                 throw error;
             }
+
+        }
+
+        async setManagement(data) {
+            const subProject = await this.getDataById(data.id);
+            if (subProject.management === data.management) return data;
+
+            const users = await this.ctx.service.projectAccount.getAllDataByCondition({ where: { project_id: subProject.project_id, company: data.management }});
+            const orgMember = await this.ctx.service.subProjPermission.getAllDataByCondition({ where: { spid: subProject.id } });
+            const dm = [], um = [], im = [];
+            const filing_type = this.ctx.service.filing.allFilingType, file_permission = '1,2';
+            for (const u of users) {
+                const nm = orgMember.find(x => { return u.id === x.uid; });
+                if (nm) {
+                    if (!nm.file_permission) um.push({ id: nm.id, file_permission, filing_type });
+                } else {
+                    im.push({ id: this.uuid.v4(), spid: subProject.id, pid: subProject.project_id, uid: u.id, file_permission, filing_type });
+                }
+            }
+            const conn = await this.db.beginTransaction();
+            try {
+                await conn.update(this.tableName, { id: subProject.id, management: data.management });
+                await this.ctx.service.filing.initFiling(subProject.id, conn);
+                if (dm.length > 0) await conn.delete(this.ctx.service.subProjPermission.tableName, { id: dm });
+                if (um.length > 0) await conn.updateRows(this.ctx.service.subProjPermission.tableName, um);
+                if (im.length > 0) await conn.insert(this.ctx.service.subProjPermission.tableName, im);
+
+                await conn.commit();
+                return data;
+            } catch (error) {
+                await conn.rollback();
+                throw error;
+            }
         }
     }
 

+ 61 - 0
app/view/file/file.ejs

@@ -0,0 +1,61 @@
+<div class="panel-content">
+    <div class="panel-title fluid">
+        <div class="title-main  d-flex justify-content-between">
+            <div>资料归集</div>
+            <div class="ml-auto">
+                <% if (ctx.session.sessionUser.is_admin) { %>
+                <a class="btn btn-sm btn-outline-primary mr-1" href="#filing-permission" data-toggle="modal" data-target="#filing-permission">授权用户</a>
+                <% } %>
+            </div>
+        </div>
+    </div>
+    <div class="content-wrap">
+        <div class="c-body">
+            <div class="sjs-height-0 row">
+                <div class="col-3">
+                    <div class="d-flex flex-row">
+                        <% if (canFiling) { %>
+                        <div class="p-2"><a href="javascript: void(0);" id="add-slibing">添加同级</a></div>
+                        <div class="p-2"><a href="javascript: void(0);" id="add-child">添加子级</a></div>
+                        <div class="p-2"><a href="javascript: void(0);" id="del-filing" class="text-danger">删除</a></div>
+                        <% } else { %>
+                        <div class="p-2 ml-2">分类目录</div>
+                        <% } %>
+                    </div>
+                    <div>
+                        <ul id="filing" class="ztree"></ul>
+                    </div>
+                </div>
+                <div class="col-9" id="file-view" style="display: none">
+                    <div class="d-flex flex-row">
+                        <% if (canUpload) { %>
+                        <div class="py-2 pr-2"><a href="#add-file" data-toggle="modal" data-target="#add-file">上传文件</a></div>
+                        <div class="p-2"><a href="javascript: void(0)" id="rela-file">引用文件</a></div>
+                        <div class="p-2"><a href="#del-batch-file" data-toggle="modal" data-target="#del-batch-file">批量删除</a></div>
+                        <% } %>
+                        <div class="p-2"><a href="javascript: void(0)" id="batch-download">批量下载</a></div>
+                    </div>
+                    <table class="table table-bordered">
+                        <thead>
+                        <tr>
+                            <th>选择</th>
+                            <th width="40%">文件名称</th>
+                            <th>上传人</th>
+                            <th>上传时间</th>
+                            <th>文件类型</th>
+                        </tr>
+                        </thead>
+                        <tbody id="file-list">
+                        </tbody>
+                    </table>
+                    <!--翻页-->
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
+<script>
+    const canFiling = <%- canFiling %>;
+    const filing = JSON.parse(unescape('<%- escape(JSON.stringify(filing)) %>'));
+    const category = JSON.parse(unescape('<%- escape(JSON.stringify(categoryData)) %>'));
+</script>

+ 61 - 0
app/view/file/file_modal.ejs

@@ -0,0 +1,61 @@
+<% if (ctx.session.sessionUser.is_admin) { %>
+<div class="modal" id="filing-permission">
+    <div class="modal-dialog modal-lg" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">授权用户</h5>
+            </div>
+            <div class="modal-body">
+                <div class="row">
+                    <div class="col-4">
+                        <div class="d-flex justify-content-center bg-graye">
+                            <div class="p-2">文档类别</div>
+                        </div>
+                        <div class="modal-height-400">
+                            <div class="category">
+                                <ul>
+                                    <% for (const ft of filingTypes) { %>
+                                    <li>
+                                        <div class="form-check">
+                                            <input class="form-check-input" name="cbft" type="checkbox" value="<%- ft.value %>" id="ftCheck<%- ft.value %>">
+                                            <label class="form-check-label" for="ftCheck<%- ft.value %>"></label>
+                                            <span name="ftName" ftid="<%- ft.value %>"><%- ft.name %></span>
+                                        </div>
+                                    </li>
+                                    <% } %>
+                                </ul>
+                            </div>
+                        </div>
+                    </div>
+                    <div class="col-8">
+                        <div class="d-flex flex-row bg-graye">
+                            <button class="btn btn-outline-primary btn-sm dropdown-toggle" type="button" id="dropdown-up" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
+                                添加用户
+                            </button>
+                            <div class="dropdown-menu" aria-labelledby="dropdown-up" style="width:220px">
+                                <dl class="list-unstyled book-list" id="puList">
+                                </dl>
+                            </div>
+                            <div class="p-2"><a href="javascript: void(0);" id="batch-del-filing" class="text-danger">批量删除</a></div>
+                            <div class="p-2"><a href="javascript: void(0);" id="sync-filing">同步授权至其他类别</a></div>
+                        </div>
+                        <div class="modal-height-400">
+                            <table class="table table-bordered">
+                                <thead>
+                                <tr><th>选择</th><th>用户名</th><th>授权时间</th><th>权限</th><th>操作</th></tr>
+                                </thead>
+                                <tbody id="filing-valid">
+                                </tbody>
+                            </table>
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">取消</button>
+                <button type="button" class="btn btn-sm btn-primary" id="filing-permission-ok">确定</button>
+            </div>
+        </div>
+    </div>
+</div>
+<% } %>

+ 31 - 0
app/view/sub_proj/modal.ejs

@@ -185,6 +185,37 @@
         </div>
     </div>
 </div>
+<div class="modal" id="set-management" data-backdrop="static">
+    <div class="modal-dialog" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">设置管理单位</h5>
+            </div>
+            <div class="modal-body">
+                <form>
+                    <div class="form-group row">
+                        <label for="text" class="col-sm-2 col-form-labelcol-form-label-sm">管理单位</label>
+                        <div class="col-sm-10">
+                            <select id="sm-management" class="form-control form-control-sm">
+                                <% if (companys.length > 0) { %>
+                                <% for( const c of companys) { %>
+                                <option value="<%- c.name %>"><%- c.name %></option>
+                                <% } %>
+                                <% } else {%>
+                                <option value="">暂无任何参建单位,请先在项目信息-账号设置-参建单位下添加</option>
+                                <% } %>
+                            </select>
+                        </div>
+                    </div>
+                </form>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">取消</button>
+                <button type="button" class="btn btn-sm btn-primary" id="set-management-ok">确定</button>
+            </div>
+        </div>
+    </div>
+</div>
 <script>
     const accountList = JSON.parse('<%- JSON.stringify(accountList) %>');
     const accountGroup = JSON.parse('<%- JSON.stringify(accountGroup) %>');

+ 14 - 2
config/web.js

@@ -1050,9 +1050,21 @@ const JsFiles = {
                     '/public/js/path_tree.js',
                     '/public/js/shares/tenders2tree.js',
                     '/public/js/spreadjs_rela/spreadjs_zh.js',
-                    '/public/js/file.js',
+                    '/public/js/file_list.js',
                 ],
-                mergeFile: 'file',
+                mergeFile: 'file_list',
+            },
+            file: {
+                files: [
+                    '/public/js/moment/moment.min.js', '/public/js/ztree/jquery.ztree.core.js', '/public/js/ztree/jquery.ztree.exedit.js',
+                ],
+                mergeFiles: [
+                    '/public/js/shares/drag_tree.js',
+                    '/public/js/path_tree.js',
+                    '/public/js/shares/tenders2tree.js',
+                    '/public/js/file_detail.js',
+                ],
+                mergeFile: 'file_detail',
             },
         },
         budget: {