Ver código fonte

台账修订,附件

MaiXinRong 1 ano atrás
pai
commit
3eaf4c5fcd

+ 113 - 1
app/controller/revise_controller.js

@@ -21,6 +21,8 @@ const shenpiConst = require('../const/shenpi');
 const LzString = require('lz-string');
 const LzString = require('lz-string');
 const stdConst = require('../const/standard');
 const stdConst = require('../const/standard');
 const spreadSetting = require('../lib/spread_setting');
 const spreadSetting = require('../lib/spread_setting');
+const path = require('path');
+const sendToWormhole = require('stream-wormhole');
 
 
 module.exports = app => {
 module.exports = app => {
     class ReviseController extends app.BaseController {
     class ReviseController extends app.BaseController {
@@ -459,7 +461,8 @@ module.exports = app => {
                 ]);
                 ]);
                 const ledgerTags = await this.ctx.service.ledgerTag.getDatas(ctx.tender.id);
                 const ledgerTags = await this.ctx.service.ledgerTag.getDatas(ctx.tender.id);
                 const price = !revise.readOnly ? await this.ctx.service.revisePrice.getAllDataByCondition({ where: { rid: revise.id } }) : [];
                 const price = !revise.readOnly ? await this.ctx.service.revisePrice.getAllDataByCondition({ where: { rid: revise.id } }) : [];
-                ctx.body = { err: 0, msg: '', data: { bills: reviseBills, pos: revisePos, tags: ledgerTags, price } };
+                const att = await this.ctx.service.ledgerAtt.getReviseViewData(ctx.tender.id, ctx.revise.id);
+                ctx.body = { err: 0, msg: '', data: { bills: reviseBills, pos: revisePos, tags: ledgerTags, price, att } };
             } catch (err) {
             } catch (err) {
                 ctx.helper.log(err);
                 ctx.helper.log(err);
                 this.ajaxErrorBody(err, '加载台账修订数据错误');
                 this.ajaxErrorBody(err, '加载台账修订数据错误');
@@ -1014,6 +1017,8 @@ module.exports = app => {
                     return await this.ctx.service.revisePrice.getAllDataByCondition({ where: { rid: ctx.revise.id } });
                     return await this.ctx.service.revisePrice.getAllDataByCondition({ where: { rid: ctx.revise.id } });
                 case 'change':
                 case 'change':
                     return await this._loadChange(ctx);
                     return await this._loadChange(ctx);
+                case 'att':
+                    return await this.ctx.service.ledgerAtt.getReviseViewData(ctx.tender.id, ctx.revise.id);
                 default: throw '请求的数据不存在';
                 default: throw '请求的数据不存在';
             }
             }
         }
         }
@@ -1074,6 +1079,113 @@ module.exports = app => {
                 ctx.ajaxErrorBody(err, '保存数据失败');
                 ctx.ajaxErrorBody(err, '保存数据失败');
             }
             }
         }
         }
+
+        async uploadFile(ctx) {
+            let stream;
+            try {
+                const parts = ctx.multipart({ autoFields: true });
+                const files = [];
+                let index = 0;
+                const extra_upload = ctx.revise.audit_status === audit.revise.status.checked;
+                stream = await parts();
+                while (stream) {
+                    // 判断用户是否选择上传文件
+                    if (!stream.filename) {
+                        throw '请选择上传的文件!';
+                    }
+                    const fileInfo = path.parse(stream.filename);
+                    const create_time = Date.parse(new Date()) / 1000;
+                    const filepath = `${ctx.session.sessionProject.id}/${ctx.tender.id}/revise/att_${create_time + index.toString() + fileInfo.ext}`;
+
+                    await ctx.app.fujianOss.put(ctx.app.config.fujianOssFolder + filepath, stream);
+
+                    if (stream) await sendToWormhole(stream);
+
+                    // 保存数据到att表
+                    const fileData = {
+                        tid: ctx.tender.id,
+                        revise_id: ctx.revise.id,
+                        revising: !extra_upload,
+                        in_time: create_time,
+                        filename: fileInfo.name,
+                        fileext: fileInfo.ext,
+                        filesize: Array.isArray(parts.field.size) ? parts.field.size[index] : parts.field.size,
+                        filepath,
+                        extra_upload,
+                    };
+                    const result = await ctx.service.ledgerAtt.save(parts.field, fileData, ctx.session.sessionUser.accountId);
+                    if (!result) throw '保存数据失败';
+                    const attData = await ctx.service.ledgerAtt.getViewDataByFid(result.insertId);
+                    files.length !== 0 ? files.unshift(attData) : files.push(attData);
+                    ++index;
+                    if (Array.isArray(parts.field.size) && index < parts.field.size.length) {
+                        stream = await parts();
+                    } else {
+                        stream = undefined;
+                    }
+                }
+                ctx.body = { err: 0, msg: '', data: files };
+            } catch (err) {
+                ctx.log(err);
+                // 失败需要消耗掉stream 以防卡死
+                if (stream) await sendToWormhole(stream);
+                ctx.ajaxErrorBody(err, '上传文件失败');
+            }
+        }
+        async deleteFile(ctx) {
+            try {
+                const data = JSON.parse(ctx.request.body.data);
+                const fileInfo = await ctx.service.ledgerAtt.getDataById(data.id);
+                if (!fileInfo || !Object.keys(fileInfo).length) throw '该文件不存在';
+                if (!fileInfo.extra_upload && ctx.revise.status === audit.revise.status.checked) throw '无权限删除';
+
+                if (fileInfo !== undefined && fileInfo !== '') {
+                    // 先删除文件
+                    await ctx.app.fujianOss.delete(ctx.app.config.fujianOssFolder + fileInfo.filepath);
+                    // 再删除数据库
+                    await ctx.service.ledgerAtt.deleteById(data.id);
+                } else {
+                    throw '不存在该文件';
+                }
+                ctx.body = { err: 0, msg: '', data: null}
+            } catch (err) {
+                ctx.log(err);
+                ctx.ajaxErrorBody(err, '删除文件失败');
+            }
+        }
+        async saveFile(ctx) {
+            let stream;
+            try {
+                stream = await ctx.getFileStream({ requireFile: false });
+                let fileData = {};
+                if (stream.filename !== undefined) {
+                    const create_time = Date.parse(new Date()) / 1000;
+                    const fileInfo = path.parse(stream.filename);
+                    const filepath = `${ctx.session.sessionProject.id}/${ctx.tender.id}/revise/att_${create_time + fileInfo.ext}`;
+
+                    // 保存文件
+                    await ctx.oss.put(ctx.app.config.fujianOssFolder + filepath, stream);
+                    // 保存数据到att表
+                    fileData = {
+                        filesize: stream.fields.size,
+                        filepath,
+                    };
+                }
+                const org = await ctx.service.ledgerAtt.getDataById(stream.fields.id);
+                const result = await ctx.service.ledgerAtt.updateByID(stream.fields, fileData);
+                if (!result) throw '保存数据失败';
+                // 删除原附件
+                await ctx.app.fujianOss.delete(ctx.app.config.fujianOssFolder + org.filepath);
+                const attData = await ctx.service.ledgerAtt.getViewDataByFid(stream.fields.id);
+                ctx.body = { err: 0, msg: '', data: attData };
+                responseData.data = attData;
+            } catch (err) {
+                ctx.log(err);
+                // 失败需要消耗掉stream 以防卡死
+                if (stream) await sendToWormhole(stream);
+                ctx.ajaxErrorBody(err, '保存数据失败');
+            }
+        }
     }
     }
 
 
     return ReviseController;
     return ReviseController;

+ 22 - 2
app/public/js/revise.js

@@ -51,7 +51,7 @@ const checkOption = {
 };
 };
 
 
 $(document).ready(() => {
 $(document).ready(() => {
-    let stdXmj, stdGcl, searchLedger;
+    let stdXmj, stdGcl, searchLedger, reviseAtt;
     autoFlashHeight();
     autoFlashHeight();
     // 初始化spread
     // 初始化spread
     const billsSpread = SpreadJsObj.createNewSpread($('#bills-spread')[0]);
     const billsSpread = SpreadJsObj.createNewSpread($('#bills-spread')[0]);
@@ -372,6 +372,7 @@ $(document).ready(() => {
             posSpreadObj.loadCurPosData();
             posSpreadObj.loadCurPosData();
             SpreadJsObj.saveTopAndSelect(billsSheet, ckBillsSpread);
             SpreadJsObj.saveTopAndSelect(billsSheet, ckBillsSpread);
             posSearch.search($('#pos-keyword').val());
             posSearch.search($('#pos-keyword').val());
+            reviseAtt.getCurAttHtml(SpreadJsObj.getSelectObject(billsSheet));
         },
         },
         selectionChanged: function (e, info) {
         selectionChanged: function (e, info) {
             if (info.newSelections) {
             if (info.newSelections) {
@@ -2145,6 +2146,25 @@ $(document).ready(() => {
         posSpreadObj.loadCurPosData();
         posSpreadObj.loadCurPosData();
         SpreadJsObj.resetTopAndSelect(posSheet);
         SpreadJsObj.resetTopAndSelect(posSheet);
 
 
+        reviseAtt = $.ledger_att({
+            selector: '#fujian',
+            key: 'id',
+            masterKey: 'lid',
+            uploadUrl: 'file/upload',
+            deleteUrl: 'file/delete',
+            checked: reviseComplete,
+            zipName: `${tenderName}-台账修订-附件.zip`,
+            readOnly: false, // todo fileUploadPermission,
+            locate: function (att) {
+                if (!att) return;
+                SpreadJsObj.locateTreeNode(billsSheet, att.node.tree_id, true);
+                billsTreeSpreadObj.loadRelaAtt();
+                posSpreadObj.loadCurPosData();
+            }
+        });
+        reviseAtt.loadDatas(result.att || []);
+        reviseAtt.getCurAttHtml(SpreadJsObj.getSelectObject(billsSheet));
+
         checkList.loadHisCheckData();
         checkList.loadHisCheckData();
     }, null);
     }, null);
     $.divResizer({
     $.divResizer({
@@ -2867,7 +2887,7 @@ $(document).ready(() => {
             const close = $('.active', '#side-menu').length === 0;
             const close = $('.active', '#side-menu').length === 0;
             $('a', '#side-menu').removeClass('active');
             $('a', '#side-menu').removeClass('active');
             tab.addClass('active');
             tab.addClass('active');
-            $('.tab-content .tab-pane').removeClass('active');
+            $('.tab-content .tab-select-show').removeClass('active');
             tabPanel.addClass('active');
             tabPanel.addClass('active');
             showSideTools(tab.hasClass('active'));
             showSideTools(tab.hasClass('active'));
             if (tab.attr('content') === '#std-xmj') {
             if (tab.attr('content') === '#std-xmj') {

+ 2 - 1
app/public/js/shares/tools_att.js

@@ -12,6 +12,7 @@
 (function($){
 (function($){
     $.ledger_att = function (setting) {
     $.ledger_att = function (setting) {
         if (!setting.selector) return;
         if (!setting.selector) return;
+        if (!setting.masterKey) setting.masterKey = setting.key;
         const obj = $(setting.selector);
         const obj = $(setting.selector);
         const pageLength = 20;
         const pageLength = 20;
         let curNode = null, curPage = 0;
         let curNode = null, curPage = 0;
@@ -267,7 +268,7 @@
         });
         });
 
 
         const _addToNodeIndex = function(att, isTop = false) {
         const _addToNodeIndex = function(att, isTop = false) {
-            const id = att[setting.key];
+            const id = att[setting.masterKey];
             if (!nodeIndexes[id]) {
             if (!nodeIndexes[id]) {
                 nodeIndexes[id] = [];
                 nodeIndexes[id] = [];
             }
             }

+ 3 - 0
app/router.js

@@ -279,6 +279,9 @@ module.exports = app => {
     app.post('/tender/:id/revise/:rid/info/upload-excel/:ueType', sessionAuth, tenderCheck, uncheckTenderCheck, reviseCheck, 'reviseController.uploadExcel');
     app.post('/tender/:id/revise/:rid/info/upload-excel/:ueType', sessionAuth, tenderCheck, uncheckTenderCheck, reviseCheck, 'reviseController.uploadExcel');
     app.post('/tender/:id/revise/:rid/info/check', sessionAuth, tenderCheck, uncheckTenderCheck, reviseCheck, 'reviseController.checkData');
     app.post('/tender/:id/revise/:rid/info/check', sessionAuth, tenderCheck, uncheckTenderCheck, reviseCheck, 'reviseController.checkData');
     app.post('/tender/:id/revise/:rid/info/deal2sgfh', sessionAuth, tenderCheck, uncheckTenderCheck, reviseCheck, 'reviseController.deal2sgfh');
     app.post('/tender/:id/revise/:rid/info/deal2sgfh', sessionAuth, tenderCheck, uncheckTenderCheck, reviseCheck, 'reviseController.deal2sgfh');
+    app.post('/tender/:id/revise/:rid/file/upload', sessionAuth, tenderCheck, uncheckTenderCheck, reviseCheck, 'reviseController.uploadFile');
+    app.post('/tender/:id/revise/:rid/file/delete', sessionAuth, tenderCheck, uncheckTenderCheck, reviseCheck, 'reviseController.deleteFile');
+    app.post('/tender/:id/revise/:rid/file/save', sessionAuth, tenderCheck, uncheckTenderCheck, reviseCheck, 'reviseController.saveFile');
 
 
     app.get('/tender/:id/revise/:rid/compare', sessionAuth, tenderCheck, uncheckTenderCheck, reviseCheck, reviseAuditCheck, 'reviseController.compare');
     app.get('/tender/:id/revise/:rid/compare', sessionAuth, tenderCheck, uncheckTenderCheck, reviseCheck, reviseAuditCheck, 'reviseController.compare');
     app.get('/tender/:id/revise/:rid/gcl-compare', sessionAuth, tenderCheck, uncheckTenderCheck, reviseCheck, reviseAuditCheck, 'reviseController.gclCompare');
     app.get('/tender/:id/revise/:rid/gcl-compare', sessionAuth, tenderCheck, uncheckTenderCheck, reviseCheck, reviseAuditCheck, 'reviseController.gclCompare');

+ 2 - 0
app/service/revise_audit.js

@@ -483,6 +483,8 @@ module.exports = app => {
                             total_price: sum.total_price,
                             total_price: sum.total_price,
                             deal_tp: sum.deal_tp,
                             deal_tp: sum.deal_tp,
                         });
                         });
+                        // 修订附件取消修订中标记
+                        await transaction.update(this.ctx.service.ledgerAtt.tableName, { revising: 0 }, { where: { revise_id: revise.id } });
                         await this.ctx.service.tenderCache.updateStageCache4Revise(transaction, revise.tid, sum, pcTp);
                         await this.ctx.service.tenderCache.updateStageCache4Revise(transaction, revise.tid, sum, pcTp);
                         // 清除变更新增部位maxLid缓存,防止树结构混乱
                         // 清除变更新增部位maxLid缓存,防止树结构混乱
                         await this.ctx.service.changeLedger._removeCacheMaxLid(audit.tender_id);
                         await this.ctx.service.changeLedger._removeCacheMaxLid(audit.tender_id);

+ 16 - 8
app/view/revise/info.ejs

@@ -115,7 +115,7 @@
             <div class="c-body" id="right-view" style="display: none; width: 33%;">
             <div class="c-body" id="right-view" style="display: none; width: 33%;">
                 <div class="resize-x" id="revise-right-spr" r-Type="width" div1="#left-view" div2="#right-view" title="调整大小" a-type="percent"><!--调整左右高度条--></div>
                 <div class="resize-x" id="revise-right-spr" r-Type="width" div1="#left-view" div2="#right-view" title="调整大小" a-type="percent"><!--调整左右高度条--></div>
                 <div class="tab-content">
                 <div class="tab-content">
-                    <div id="xd-content" class="tab-pane">
+                    <div id="xd-content" class="tab-pane tab-select-show">
                         <% if ((revise.status === audit.status.uncheck || revise.status === audit.status.checkNo) && !readOnly) { %>
                         <% if ((revise.status === audit.status.uncheck || revise.status === audit.status.checkNo) && !readOnly) { %>
                         <div class="sjs-bar">
                         <div class="sjs-bar">
                             <div class="d-flex"><a href="javascript: void(0);" class="btn btn-sm btn-outline-success mb-1 ml-auto" id="save">保存</a></div>
                             <div class="d-flex"><a href="javascript: void(0);" class="btn btn-sm btn-outline-success mb-1 ml-auto" id="save">保存</a></div>
@@ -136,13 +136,13 @@
                             </div>
                             </div>
                         </div>
                         </div>
                     </div>
                     </div>
-                    <div id="search" class="tab-pane">
+                    <div id="search" class="tab-pane tab-select-show">
                     </div>
                     </div>
-                    <div id="std-xmj" class="tab-pane">
+                    <div id="std-xmj" class="tab-pane tab-select-show">
                     </div>
                     </div>
-                    <div id="std-gcl" class="tab-pane">
+                    <div id="std-gcl" class="tab-pane tab-select-show">
                     </div>
                     </div>
-                    <div id="deal-bills" class="tab-pane">
+                    <div id="deal-bills" class="tab-pane tab-select-show">
                         <div class="sjs-bar d-flex">
                         <div class="sjs-bar d-flex">
                             <div class="pb-1">签约清单</div>
                             <div class="pb-1">签约清单</div>
                             <div class="ml-auto mr-1">
                             <div class="ml-auto mr-1">
@@ -175,7 +175,7 @@
                         <div id="deal-bills-spread" class="sjs-sh">
                         <div id="deal-bills-spread" class="sjs-sh">
                         </div>
                         </div>
                     </div>
                     </div>
-                    <div id="bg-bills" class="tab-pane">
+                    <div id="bg-bills" class="tab-pane tab-select-show">
                         <div class="sjs-bar">
                         <div class="sjs-bar">
                             变更清单
                             变更清单
                         </div>
                         </div>
@@ -184,9 +184,12 @@
                     </div>
                     </div>
                     <div id="bills-tag" class="tab-pane tab-select-show">
                     <div id="bills-tag" class="tab-pane tab-select-show">
                     </div>
                     </div>
-                    <div id="error-list" class="tab-pane">
+                    <!--附件-->
+                    <div id="fujian" class="tab-pane tab-select-show">
                     </div>
                     </div>
-                    <div id="check-list" class="tab-pane">
+                    <div id="error-list" class="tab-pane tab-select-show">
+                    </div>
+                    <div id="check-list" class="tab-pane tab-select-show">
                     </div>
                     </div>
                     <div id="sum-load-miss" class="tab-pane tab-select-show">
                     <div id="sum-load-miss" class="tab-pane tab-select-show">
                     </div>
                     </div>
@@ -219,6 +222,9 @@
                     <a class="nav-link" content="#bills-tag" href="javascript: void(0);">书签</a>
                     <a class="nav-link" content="#bills-tag" href="javascript: void(0);">书签</a>
                 </li>
                 </li>
                 <li class="nav-item">
                 <li class="nav-item">
+                    <a class="nav-link" content="#fujian" href="javascript: void(0);">附件</a>
+                </li>
+                <li class="nav-item">
                     <a class="nav-link" content="#error-list" id="error-list-tab" href="javascript: void(0);" style="display: none;">错误列表</a>
                     <a class="nav-link" content="#error-list" id="error-list-tab" href="javascript: void(0);" style="display: none;">错误列表</a>
                 </li>
                 </li>
                 <li class="nav-item">
                 <li class="nav-item">
@@ -253,4 +259,6 @@
     });
     });
     const nodeType = JSON.parse('<%- JSON.stringify(nodeType) %>');
     const nodeType = JSON.parse('<%- JSON.stringify(nodeType) %>');
     const settleStatus = JSON.parse('<%- JSON.stringify(settleStatus) %>');
     const settleStatus = JSON.parse('<%- JSON.stringify(settleStatus) %>');
+    const tenderName = '<%- ctx.tender.name %>';
+    const reviseComplete = <%- ctx.revise.status === audit.status.checked %>;
 </script>
 </script>

+ 1 - 0
app/view/revise/info_modal.ejs

@@ -741,6 +741,7 @@
         </div>
         </div>
     </div>
     </div>
 <% include ../shares/batch_replace_modal.ejs %>
 <% include ../shares/batch_replace_modal.ejs %>
+<% include ../shares/upload_att.ejs %>
     <% include ../shares/merge_peg_modal.ejs %>
     <% include ../shares/merge_peg_modal.ejs %>
     <% include ../shares/import_excel_modal.ejs %>
     <% include ../shares/import_excel_modal.ejs %>
     <% include ../shares/delete_hint_modal.ejs %>
     <% include ../shares/delete_hint_modal.ejs %>

+ 3 - 3
app/view/shares/batch_replace_modal.ejs

@@ -8,7 +8,7 @@
                 原清单:
                 原清单:
                 <table class="table table-bordered">
                 <table class="table table-bordered">
                     <tr class="text-center"><th width="25%">编号</th><th width="40%">名称</th><th width="15%">单位</th><th width="20%">单价</th></tr>
                     <tr class="text-center"><th width="25%">编号</th><th width="40%">名称</th><th width="15%">单位</th><th width="20%">单价</th></tr>
-                    <tr><td id="br-org-code"></td><td id="br-org-name"></td><td id="br-org-unit"></td><td id="br-org-up">单价</td></tr>
+                    <tr><td id="br-org-code"></td><td id="br-org-name"></td><td class="text-center" id="br-org-unit"></td><td class="text-right" id="br-org-up">单价</td></tr>
                 </table>
                 </table>
 
 
                 替换为:
                 替换为:
@@ -17,8 +17,8 @@
                     <tr>
                     <tr>
                         <td><input id="br-new-code" type="text" class="form-control form-control-sm"></td>
                         <td><input id="br-new-code" type="text" class="form-control form-control-sm"></td>
                         <td><input id="br-new-name" type="text" class="form-control form-control-sm"></td>
                         <td><input id="br-new-name" type="text" class="form-control form-control-sm"></td>
-                        <td><input id="br-new-unit" type="text" class="form-control form-control-sm"></td>
-                        <td><input id="br-new-up" type="text" class="form-control form-control-sm"></td></tr>
+                        <td><input id="br-new-unit" type="text" class="form-control form-control-sm text-center"></td>
+                        <td><input id="br-new-up" type="text" class="form-control form-control-sm text-right"></td></tr>
                 </table>
                 </table>
             </div>
             </div>
             <div class="modal-footer">
             <div class="modal-footer">

+ 2 - 0
config/web.js

@@ -278,6 +278,7 @@ const JsFiles = {
                     '/public/js/shares/tenders2tree.js',
                     '/public/js/shares/tenders2tree.js',
                     '/public/js/shares/tender_select.js',
                     '/public/js/shares/tender_select.js',
                     '/public/js/ledger_check.js',
                     '/public/js/ledger_check.js',
+                    '/public/js/shares/tools_att.js',
                     '/public/js/revise.js',
                     '/public/js/revise.js',
                 ],
                 ],
                 mergeFile: 'revise',
                 mergeFile: 'revise',
@@ -308,6 +309,7 @@ const JsFiles = {
                     '/public/js/spreadjs_rela/spreadjs_zh.js',
                     '/public/js/spreadjs_rela/spreadjs_zh.js',
                     '/public/js/shares/sjs_setting.js',
                     '/public/js/shares/sjs_setting.js',
                     '/public/js/shares/cs_tools.js',
                     '/public/js/shares/cs_tools.js',
+                    '/public/js/shares/tools_att.js',
                     '/public/js/zh_calc.js',
                     '/public/js/zh_calc.js',
                     '/public/js/path_tree.js',
                     '/public/js/path_tree.js',
                     '/public/js/revise_history.js',
                     '/public/js/revise_history.js',