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

Merge branch 'dev' of http://192.168.1.41:3000/maixinrong/Calculation into dev

TonyKang 4 лет назад
Родитель
Сommit
75827d1dd8
51 измененных файлов с 4026 добавлено и 162 удалено
  1. 42 0
      app/const/page_show.js
  2. 13 0
      app/controller/material_controller.js
  3. 88 1
      app/controller/profile_controller.js
  4. 8 3
      app/controller/report_archive_controller.js
  5. 0 4
      app/controller/report_controller.js
  6. 1 1
      app/controller/setting_controller.js
  7. 27 12
      app/controller/sign_controller.js
  8. 1 1
      app/controller/stage_controller.js
  9. 1 1
      app/controller/tender_controller.js
  10. 5 4
      app/lib/ledger.js
  11. 1 1
      app/lib/rpt_data_analysis.js
  12. 1 1
      app/middleware/tender_check.js
  13. 0 1
      app/public/js/CalcEval.js
  14. 1 0
      app/public/js/calc/calcEval.min.js
  15. 25 0
      app/public/js/draw.js
  16. 3 3
      app/public/js/ledger_check.js
  17. 12 6
      app/public/js/material_list.js
  18. 5 5
      app/public/js/profile.js
  19. 152 1
      app/public/js/zh_calc.js
  20. 2971 0
      app/public/netcasign/js/appPackage.js
  21. 20 15
      app/public/netcasign/js/netcasealpdf.js
  22. 21 0
      app/public/report/js/rpt_archive.js
  23. 2 2
      app/public/report/js/rpt_signature.js
  24. 4 0
      app/router.js
  25. 56 0
      app/service/netcasign.js
  26. 15 3
      app/service/project_account.js
  27. 3 2
      app/service/s2b_proj.js
  28. 1 1
      app/view/layout/layout.ejs
  29. 16 13
      app/view/material/list.ejs
  30. 151 0
      app/view/profile/netcasign.ejs
  31. 42 36
      app/view/profile/sign.ejs
  32. 62 0
      app/view/profile/sign_modal.ejs
  33. 2 2
      app/view/profile/sub_menu.ejs
  34. 126 4
      app/view/report/archive_pdf.ejs
  35. 1 1
      app/view/report/index.ejs
  36. 17 8
      app/view/report/index_archive.ejs
  37. 63 0
      app/view/report/index_archive_modal.ejs
  38. 5 5
      app/view/report/rpt_all_popup.ejs
  39. 17 5
      app/view/setting/s2b.ejs
  40. 24 5
      app/view/sign/info.ejs
  41. 2 2
      app/view/stage/stage_sub_menu.ejs
  42. 2 2
      app/view/stage/stage_sub_mini_menu.ejs
  43. 1 1
      app/view/tender/detail.ejs
  44. 1 1
      app/view/tender/detail_modal.ejs
  45. 1 1
      app/view/tender/tender_sub_menu.ejs
  46. 1 1
      app/view/tender/tender_sub_mini_menu.ejs
  47. 6 0
      build_min.js
  48. 3 3
      builder_report_index_define.js
  49. 0 1
      config/config.default.js
  50. 1 1
      config/menu.js
  51. 3 2
      config/web.js

+ 42 - 0
app/const/page_show.js

@@ -0,0 +1,42 @@
+'use strict';
+
+/**
+ * 前台页面展示相关
+ *
+ * @author Ellisran
+ * @date
+ * @version
+ */
+
+const pageStatus = {
+    show: 1,
+    hide: 0,
+};
+
+// const pageControl = [
+//     { title: '开启「部位台帐」', name: 'bwtz', value: pageStatus.show, type: 'checkbox' },
+//     { title: '开启「投资进度」功能', name: 'xxjd', value: pageStatus.show, type: 'checkbox' },
+//     { title: '开启「其他台账」功能', name: 'stageExtra', value: pageStatus.show, type: 'checkbox' },
+//     { title: '关闭报表「导出PDF」', name: 'closeExportPdf', value: pageStatus.show, type: 'checkbox' },
+//     { title: '关闭报表「导出Excel」', name: 'closeExportExcel', value: pageStatus.show, type: 'checkbox' },
+//     { title: '关闭报表「水印」', name: 'closeWatermark', value: pageStatus.show, type: 'checkbox' },
+//     { title: '开启报表「跨标段批量签名」', name: 'openSign', value: pageStatus.show, type: 'checkbox' },
+//     { title: '开启签名「网证通电子签名」', name: 'openNetCaSign', value: pageStatus.show, type: 'checkbox' },
+// ];
+
+const defaultSetting = {
+    bwtz: 0,
+    xxjd: 0,
+    stageExtra: 1,
+    closeExportPdf: 0,
+    closeExportExcel: 0,
+    closeWatermark: 0,
+    openSign: 0,
+    openNetCaSign: 0,
+};
+
+
+module.exports = {
+    pageStatus,
+    defaultSetting,
+};

+ 13 - 0
app/controller/material_controller.js

@@ -367,6 +367,19 @@ module.exports = app => {
                 }
                 searchsql.t_type = materialConst.t_type[0].value;
                 renderData.materialBillsData = await ctx.service.materialBills.getAllDataByCondition({ where: searchsql });
+                // 取对应期的截取上期的调差金额和应耗数量
+                if (ctx.material.highOrder !== ctx.material.order) {
+                    for (const [mindex, mb] of renderData.materialBillsData.entries()) {
+                        const result = await ctx.service.materialBillsHistory.getByMbId(ctx.material.id, ctx.material.order, mb.id);
+                        _.forEach(result, function(value, key) {
+                            if (key === 'mb_id') {
+                                renderData.materialBillsData[mindex].id = result ? result[key] : null;
+                            } else {
+                                renderData.materialBillsData[mindex][key] = result ? result[key] : null;
+                            }
+                        });
+                    }
+                }
                 // 取所有已被调用的工料清单表
                 renderData.materialListData = await ctx.service.materialList.getMaterialData(ctx.tender.id, ctx.material.id);
                 renderData.materialNotJoinListData = await ctx.service.materialListNotjoin.getAllDataByCondition({ where: { tid: ctx.tender.id, mid: ctx.material.id } });

+ 88 - 1
app/controller/profile_controller.js

@@ -252,6 +252,90 @@ module.exports = app => {
         }
 
         /**
+         * 网证通电子签名页
+         *
+         * @param {object} ctx - egg全局变量
+         * @return {void}
+         */
+        async netcasign(ctx) {
+            // 获取当前用户数据
+            const sessionUser = ctx.session.sessionUser;
+
+            // 获取账号数据
+            const accountData = await ctx.service.projectAccount.getDataByCondition({ id: sessionUser.accountId });
+            const signData = await ctx.service.netcasign.getDataByCondition({ uid: sessionUser.accountId });
+
+            const renderData = {
+                accountData,
+                signData,
+            };
+            await this.layout('profile/netcasign.ejs', renderData, 'profile/sign_modal.ejs');
+        }
+
+        /**
+         * 网证通电子签名页面操作
+         *
+         * @param {object} ctx - egg全局变量
+         * @return {void}
+         */
+        async signSave(ctx) {
+            const response = {
+                err: 0,
+                msg: '',
+            };
+            try {
+                const sessionUser = ctx.session.sessionUser;
+                const data = JSON.parse(ctx.request.body.data);
+                let signData;
+                switch (data.type) {
+                    case 'bind':
+                        signData = await ctx.service.netcasign.getDataByCondition({ keyId: data.updateData.keyId });
+                        if (signData) {
+                            const accountData = await ctx.service.projectAccount.getDataByCondition({ id: signData.uid });
+                            throw '该Ukey已绑定于 ' + accountData.name + ', 不可重复绑定';
+                        }
+
+                        const result = await ctx.service.netcasign.add(data.updateData, sessionUser.accountId);
+                        if (!result) {
+                            throw '绑定Ukey失败';
+                        }
+                        response.data = await ctx.service.netcasign.getDataByCondition({ uid: sessionUser.accountId });
+                        break;
+                    case 'unbind':
+                        signData = await ctx.service.netcasign.getDataByCondition({ uid: sessionUser.accountId });
+                        if (!signData) {
+                            throw '当前用户不存在绑定证书,解除绑定失败';
+                        }
+                        await ctx.service.netcasign.del(sessionUser.accountId);
+                        break;
+                    case 'savesign':
+                        signData = await ctx.service.netcasign.getDataByCondition({ uid: sessionUser.accountId });
+                        if (!signData) {
+                            throw '当前用户不存在绑定证书';
+                        }
+                        await ctx.service.netcasign.save({ sign_base64: data.sign_base64 }, signData.id);
+                        break;
+                    case 'delsign':
+                        signData = await ctx.service.netcasign.getDataByCondition({ uid: sessionUser.accountId });
+                        if (!signData) {
+                            throw '当前用户不存在绑定证书';
+                        }
+                        if (signData && !signData.sign_base64) {
+                            throw '当前用户不存在签名,移除签名失败';
+                        }
+                        await ctx.service.netcasign.save({ sign_base64: null }, signData.id);
+                        break;
+                    default:throw '参数有误';
+                }
+            } catch (error) {
+                response.err = 1;
+                response.msg = error.toString();
+            }
+
+            ctx.body = response;
+        }
+
+        /**
          * 电子签名删除
          *
          * @param {object} ctx - egg全局变量
@@ -297,7 +381,10 @@ module.exports = app => {
                 // 获取当前用户数据
                 const sessionUser = ctx.session.sessionUser;
 
-                const text = 'http://' + ctx.request.header.host + '/sign?user_id=' + sessionUser.accountId + '&app_token=' + sessionUser.sessionToken;
+                let text = ctx.protocol + '://' + ctx.host + '/sign?user_id=' + sessionUser.accountId + '&app_token=' + sessionUser.sessionToken;
+                if (ctx.query.from === 'netcasign') {
+                    text += '&from=netcasign';
+                }
 
                 // 大小默认5,二维码周围间距默认1
                 const img = qr.image(text || '', { type: 'png', size: size || 5, margin: margin || 1 });

+ 8 - 3
app/controller/report_archive_controller.js

@@ -83,8 +83,9 @@ module.exports = app => {
                 auditConst: auditConst.stage,
                 archiveList,
                 archiveEncryptionList,
+                can_netcasign: ctx.session.sessionProject.page_show.openNetCaSign === 1,
             };
-            await this.layout('report/index_archive.ejs', renderData);
+            await this.layout('report/index_archive.ejs', renderData, 'report/index_archive_modal.ejs');
         }
 
         async getReportArchive(ctx) {
@@ -93,7 +94,8 @@ module.exports = app => {
 
             const archives = await ctx.service.rptArchive.getPrjStgArchive(params.prjId, params.stgId);
             const archiveEncryptions = await ctx.service.rptArchiveEncryption.getPrjStgArchiveEncryption(params.prjId, params.stgId);
-            let archiveList = [], archiveEncryptionList = [];
+            let archiveList = [];
+            let archiveEncryptionList = [];
             if (archives.length > 0) {
                 archiveList = JSON.parse(archives[0].content);
             }
@@ -374,7 +376,10 @@ module.exports = app => {
         }
 
         async pdfShow(ctx) {
-            await ctx.render('report/archive_pdf.ejs');
+            const renderData = {
+                can_netcasign: ctx.session.sessionProject.page_show.openNetCaSign === 1,
+            };
+            await ctx.render('report/archive_pdf.ejs', renderData);
         }
     }
     return ReportArchiveController;

+ 0 - 4
app/controller/report_controller.js

@@ -73,10 +73,6 @@ module.exports = app => {
             try {
                 await this._getStageAuditViewData(ctx);
                 let pageShow = ctx.session.sessionProject.page_show;
-                if (pageShow === null || pageShow === undefined) {
-                    pageShow = {};
-                }
-                // console.log(pageShow);
                 pageShow.showArchive = 0;
                 const tender = ctx.tender;
                 const stage = ctx.stage;

+ 1 - 1
app/controller/setting_controller.js

@@ -821,7 +821,7 @@ module.exports = app => {
                 const responseData = { err: 0, msg: '', data: null };
                 switch (data.type) {
                     case 'gxby':
-                        responseData.data = await this.ctx.service.s2bProj.updateGxbyStatus(projectId, data.status, data.limit);
+                        responseData.data = await this.ctx.service.s2bProj.updateGxbyStatus(projectId, data.status, data.limit, data.ratio);
                         break;
                     case 'dagl':
                         responseData.data = await this.ctx.service.s2bProj.updateDaglStatus(projectId, data.status, data.limit, data.ratio);

+ 27 - 12
app/controller/sign_controller.js

@@ -30,9 +30,11 @@ module.exports = app => {
                 if (userinfo && userinfo.session_token && userinfo.session_token === ctx.query.app_token) {
                     renderData.id = userinfo.id;
                     renderData.name = userinfo.name;
+                    renderData.from = ctx.query.from ? ctx.query.from : '';
                     // renderData.role = userinfo.role;
                 } else {
-                    throw '参数有误, 无法访问本页.';
+                    // throw '参数有误, 无法访问本页';
+                    throw 'token已更新,请重新登录并再次扫码。';
                 }
             } catch (error) {
                 console.log(error);
@@ -49,18 +51,31 @@ module.exports = app => {
          */
         async save(ctx) {
             try {
-                const stream = await ctx.getFileStream({ requireFile: false });
-                const create_time = Date.parse(new Date()) / 1000;
-                const dirName = 'app/public/upload/sign/';
-                const fileName = moment().format('YYYYMMDD') + '_sign_' + create_time + '.png';
-                await ctx.helper.saveStreamFile(stream, path.join(this.app.baseDir, dirName, fileName));
-                await sendToWormhole(stream);
-
-                const result = await ctx.service.projectAccount.update({ sign_path: fileName }, { id: stream.fields.id });
-                if (result) {
-                    ctx.body = { err: 0, msg: '' };
+                if (ctx.request.body && ctx.request.body.id) {
+                    const signData = await ctx.service.netcasign.getDataByCondition({ uid: ctx.request.body.id });
+                    if (!signData) {
+                        throw '当前用户不存在绑定证书';
+                    }
+                    const result = await ctx.service.netcasign.save({ sign_base64: ctx.request.body.sign_base64 }, signData.id);
+                    if (result) {
+                        ctx.body = { err: 0, msg: '' };
+                    } else {
+                        throw '添加数据库失败';
+                    }
                 } else {
-                    throw '添加数据库失败';
+                    const stream = await ctx.getFileStream({ requireFile: false });
+                    const create_time = Date.parse(new Date()) / 1000;
+                    const dirName = 'app/public/upload/sign/';
+                    const fileName = moment().format('YYYYMMDD') + '_sign_' + create_time + '.png';
+                    await ctx.helper.saveStreamFile(stream, path.join(this.app.baseDir, dirName, fileName));
+                    await sendToWormhole(stream);
+
+                    const result = await ctx.service.projectAccount.update({ sign_path: fileName }, { id: stream.fields.id });
+                    if (result) {
+                        ctx.body = { err: 0, msg: '' };
+                    } else {
+                        throw '添加数据库失败';
+                    }
                 }
             } catch (err) {
                 this.log(err);

+ 1 - 1
app/controller/stage_controller.js

@@ -372,7 +372,7 @@ module.exports = app => {
                 projRela.banOver && ctx.tender.info.ledger_check.over && checkData.checkOverRange(['contract_qty', 'qc_qty']);
                 checkData.checkBillsTp([
                     { qty: 'contract_qty', tp: 'contract_tp' }, { qty: 'qc_qty', tp: 'qc_tp' },
-                ], this.ctx.tender.info.decimal);
+                ], this.ctx.tender.info.decimal, x => { return x.is_tp; });
                 ctx.body = { err: 0, msg: '', data: checkData.checkResult };
             } catch (err) {
                 this.log(err);

+ 1 - 1
app/controller/tender_controller.js

@@ -467,7 +467,7 @@ module.exports = app => {
                     renderData.accountList = accountList;
                     renderData.accountGroup = accountGroupList;
                 }
-                if (ctx.session.sessionProject.page_show !== null && parseInt(ctx.session.sessionProject.page_show.xxjd) === 1 && ctx.session.sessionUser.is_admin) {
+                if (ctx.session.sessionProject.page_show.xxjd && ctx.session.sessionUser.is_admin) {
                     // 投资进度内容
                     renderData.scheduleAuditList = await ctx.service.scheduleAudit.getAllDataByCondition({ where: { tid: tender.id } });
                     renderData.scPermission = scheduleConst.permission;

+ 5 - 4
app/lib/ledger.js

@@ -622,9 +622,9 @@ class checkData {
     }
 
     _getRatio(type, status) {
-        if (type === 'gxby') return null;
-        const gs = this.ctx.session.sessionProject.dagl_status.find(x => { return x.value === status });
-        return gs ? gs.ratio : null;
+        const statusConst = type === 'gxby' ? this.ctx.session.sessionProject.gxby_status : this.ctx.session.sessionProject.dagl_status;
+        const sc = statusConst.find(x => { return x.value === status });
+        return sc ? sc.ratio : null;
     }
 
 
@@ -805,9 +805,10 @@ class checkData {
             }
         }
     }
-    checkBillsTp(field, decimal) {
+    checkBillsTp(field, decimal, filter) {
         for (const b of this.checkBills.nodes) {
             if ((b.children && b.children.length > 0) || !b.check_calc) continue;
+            if (filter && filter(b)) continue;
 
             const checkData = {}, calcData = {};
             for (const f of field) {

+ 1 - 1
app/lib/rpt_data_analysis.js

@@ -1516,7 +1516,7 @@ const loadCooperationData = {
     },
 };
 const signSelect = {
-    name: '电子签名',
+    name: '签字',
     hint: '协作模式下,才需使用,其他情况勿需使用',
     defaultSetting: {
         table: 'mem_stage_im_zl',

+ 1 - 1
app/middleware/tender_check.js

@@ -77,7 +77,7 @@ module.exports = options => {
             tender.advanceAuditorsId = advanceAuditorsId;
             tender.ledgerUsers = tender.ledger_status === auditConst.status.uncheck ? [tender.data.user_id] : [tender.data.user_id, ...auditorsId];
             this.tender = tender;
-            this.session.sessionProject.page_show = yield this.service.project.getPageshow(this.session.sessionProject.id);
+            // this.session.sessionProject.page_show = yield this.service.project.getPageshow(this.session.sessionProject.id);
             // 投资进度权限获取
             let schedule_permission = scPermission.no;
             if (this.session.sessionUser.accountId === tender.data.user_id) {

Разница между файлами не показана из-за своего большого размера
+ 0 - 1
app/public/js/CalcEval.js


Разница между файлами не показана из-за своего большого размера
+ 1 - 0
app/public/js/calc/calcEval.min.js


+ 25 - 0
app/public/js/draw.js

@@ -217,6 +217,7 @@ Draw.prototype = {
     const xhr = new XMLHttpRequest();
     xhr.withCredentials = true;
     formData.append('id', id);
+    formData.append('from', from);
     formData.append('image', blob, 'sign');
 
     xhr.open('POST', url, true);
@@ -237,5 +238,29 @@ Draw.prototype = {
     };
     xhr.send(formData);
   },
+  uploadBase64(base64, url, success, failure) {
+    const xhr = new XMLHttpRequest();
+    xhr.withCredentials = true;
+    const data = encodeURIComponent('id') + '=' + encodeURIComponent(id) + '&' + encodeURIComponent('sign_base64') + '=' + encodeURIComponent(base64);
+
+    xhr.open('POST', url, true);
+    xhr.setRequestHeader("x-csrf-token", csrf);
+    xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded; charset=utf-8");
+    xhr.onload = () => {
+      if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
+        success(xhr.responseText);
+      } else {
+        failure();
+      }
+    };
+    xhr.onerror = (e) => {
+      if (typeof failure === 'function') {
+        failure(e);
+      } else {
+        console.log(`upload img error: ${e}`);
+      }
+    };
+    xhr.send(data);
+  },
 };
 // export default Draw;

+ 3 - 3
app/public/js/ledger_check.js

@@ -157,9 +157,9 @@ const ledgerCheckUtil = {
             return list.other;
         };
         const getRatio = function (type, status) {
-            if (type === 'gxby') return null;
-            const gs = option.status.dagl.find(x => { return x.value === status });
-            return gs ? gs.ratio : null;
+            const statusConst = type === 'gxby' ? option.status.gxby : option.status.dagl;
+            const sc = statusConst.find(x => { return x.value === status });
+            return sc ? sc.ratio : null;
         };
         const getValid = function (type, status, limit) {
             if (limit) {

+ 12 - 6
app/public/js/material_list.js

@@ -8,6 +8,10 @@
  * @version
  */
 
+function getStageId() {
+    return window.location.pathname.split('/')[5];
+}
+
 function findNotJoinLeafXmj(x, type = '') {
     if (type === 'index') {
         return notJoinList.findIndex(function (item) {
@@ -23,7 +27,7 @@ function getMpSpreadByMBData(id) {
     const info = materialBillsData.find(function (item) {
         return item.id === parseInt(id);
     });
-    return info.m_spread;
+    return info ? info.m_spread : 0;
 }
 
 function getMaterialListByLeafXmj(gcl_id, xmj_id, mx_id = null) {
@@ -849,8 +853,10 @@ $(document).ready(() => {
                         const [valid, msg] = this._checkExprValid(expr);
                         if (!valid) return [valid, msg];
                         data.expr = expr;
-                        const ce = new CalcEval();
-                        data.quantity = ce.eval(expr);
+                        data.quantity = ZhCalc.calcExpr.calcExprStrRpn(expr);
+                        // const ce = new CalcEvalMin();
+                        // data.quantity = ce.eval(expr);
+                        // console.log(data.quantity);
                     }
                 } else {
                     data.quantity = 0;
@@ -1041,7 +1047,7 @@ $(document).ready(() => {
         select: '#right-spr',
         callback: function () {
             ledgerSpread.refresh();
-            leafXmjSpread.refresh();
+            // leafXmjSpread.refresh();
             materialSpread.refresh();
             const width = (($('#right-view').width()/$('#right-view').parent('div').width())*100).toFixed();
             setLocalCache('material_list_' + materialID, width);
@@ -1069,7 +1075,7 @@ $(document).ready(() => {
             showSideTools(tab.hasClass('active'));
         }
         ledgerSpread.refresh();
-        leafXmjSpread.refresh();
+        // leafXmjSpread.refresh();
         materialSpread.refresh();
     });
     // 根据浏览器记录展开收起
@@ -1082,7 +1088,7 @@ $(document).ready(() => {
         $('#right-view').width(getLocalCache('material_list_' + materialID) + '%');
         showSideTools(tab.hasClass('active'));
         ledgerSpread.refresh();
-        leafXmjSpread.refresh();
+        // leafXmjSpread.refresh();
         materialSpread.refresh();
     }
 });

+ 5 - 5
app/public/js/profile.js

@@ -120,13 +120,13 @@ $(document).ready(function() {
         });
 
         // 签名模式切换
-        $('.sign-type').click(function () {
+        $('.sign-type').on('click', function () {
             if (parseInt($(this).val()) === 1) {
-                $('#show-upload').hide();
-                $('#show-qrcode').show();
+                $(this).parents('.form-group').siblings('.show-upload').hide();
+                $(this).parents('.form-group').siblings('.show-qrcode').show();
             } else {
-                $('#show-upload').show();
-                $('#show-qrcode').hide();
+                $(this).parents('.form-group').siblings('.show-upload').show();
+                $(this).parents('.form-group').siblings('.show-qrcode').hide();
             }
         });
 

+ 152 - 1
app/public/js/zh_calc.js

@@ -122,5 +122,156 @@ const ZhCalc = (function () {
         return value ? new Decimal(value).toDecimalPlaces(decimal).toNumber() : null;
     }
 
-    return {add, sub, mul, div, round, isNonZero: zhBaseCalc.isNonZero}
+    const ExprCalc = {
+        /**
+         * 判断value是否是四则运算符
+         * @param {String} value - 判断字符串
+         * @return {Boolean}
+         */
+        isOperator(value) {
+            const operatorString = "+-*/()";
+            return operatorString.indexOf(value) > -1;
+        },
+        /**
+         * Rpn解析栈
+         * @param exp
+         * @returns {Array}
+         */
+        parse2Rpn(exp) {
+            const getPriority = function (value){
+                switch(value){
+                    case '+':
+                    case '-':
+                        return 1;
+                    case '*':
+                    case '/':
+                        return 2;
+                    default:
+                        return 0;
+                }
+            };
+            const priority = function (o1, o2){
+                return getPriority(o1) <= getPriority(o2);
+            };
+
+            const inputStack = [];
+            const outputStack = [];
+            const outputQueue = [];
+
+            for (let i = 0, len = exp.length; i < len; i++) {
+                const cur = exp[i];
+                if (cur !== ' ' ) {
+                    inputStack.push(cur);
+                }
+            }
+            let num = '', isNumPre = false, isOperatorPre = false;
+            while(inputStack.length > 0){
+                const cur = inputStack.shift();
+                if (this.isOperator(cur) && !(cur === '-' && !isNumPre)) {
+                    if (isNumPre) {
+                        outputQueue.push(parseFloat(num));
+                        num = '';
+                    }
+                    if (cur === '(') {
+                        outputStack.push(cur);
+                    } else if (cur === ')') {
+                        let po = outputStack.pop();
+                        while (po !== '(' && outputStack.length > 0) {
+                            outputQueue.push(po);
+                            po = outputStack.pop();
+                        }
+                        if (po !== '(') {
+                            throw "error: unmatched ()";
+                        }
+                    } else {
+                        while(priority(cur, outputStack[outputStack.length - 1]) && outputStack.length > 0){
+                            outputQueue.push(outputStack.pop());
+                        }
+                        outputStack.push(cur);
+                    }
+                    isNumPre = false;
+                    isOperatorPre = true;
+                } else {
+                    num = num + cur;
+                    isNumPre = true;
+                    isOperatorPre = false;
+                    if (inputStack.length === 0) {
+                        outputQueue.push(parseFloat(num));
+                    }
+                }
+            }
+            if (outputStack.length > 0) {
+                if (outputStack[outputStack.length - 1] === ')' || outputStack[outputStack.length - 1] === '(') {
+                    throw "error: unmatched ()";
+                }
+                while (outputStack.length > 0) {
+                    outputQueue.push(outputStack.pop());
+                }
+            }
+            return outputQueue;
+        },
+        /**
+         * 计算Rpn解析栈
+         * @param rpnQueue
+         * @returns {*}
+         */
+        evalRpn(rpnQueue) {
+            const getResult = function (num1, num2, opera) {
+                switch (opera) {
+                    case '+':
+                        return add(num1, num1);
+                    case '-':
+                        return sub(num1, num2);
+                    case '*':
+                        return mul(num1, num2, 10);
+                    case '/':
+                        return div(num1, num2, 10);
+                    default:
+                        throw '参数错误';
+                }
+            };
+            const outputStack = [];
+            while(rpnQueue.length > 0){
+                const cur = rpnQueue.shift();
+
+                if (!this.isOperator(cur)) {
+                    outputStack.push(cur);
+                } else {
+                    if (outputStack.length < 2) {
+                        throw "unvalid stack length";
+                    }
+                    const sec = outputStack.pop();
+                    const fir = outputStack.pop();
+
+                    outputStack.push(getResult(fir, sec, cur));
+                }
+            }
+
+            if (outputStack.length !== 1) {
+                throw "unvalid expression";
+            } else {
+                return outputStack[0];
+            }
+        },
+        /**
+         * 解析四则运算字符串并计算
+         * @param {String} expr
+         * @returns
+         */
+        calcExprStrRpn(expr) {
+            try {
+                if (!isNaN(Number(expr))) {
+                    return Number(expr);
+                } else {
+                    const rpnArr = this.parse2Rpn(expr);
+                    const result = this.evalRpn(rpnArr);
+                    return result === Infinity ? null : result;
+                }
+            } catch (err) {
+                return null;
+            }
+        },
+    };
+
+    return {add, sub, mul, div, round, isNonZero: zhBaseCalc.isNonZero, calcExpr: ExprCalc}
 })();

Разница между файлами не показана из-за своего большого размера
+ 2971 - 0
app/public/netcasign/js/appPackage.js


+ 20 - 15
app/public/netcasign/js/netcasealpdf.js

@@ -1090,7 +1090,9 @@ var NetcaSignAPI = (function(){
 
     that.getSealConfigInfoFailCallBack = function(res){
         layer.closeAll('loading');
-        alert('获取电子签章客户端配置信息失败:' + res.msg);
+        // alert('获取电子签章客户端配置信息失败:' + res.msg);
+        $('.modal-background').show();
+
     };
     // 本文档找不到可验证的签名
     that.notFoundSignature = function() {
@@ -1269,18 +1271,18 @@ var NetcaSignAPI = (function(){
         if($(".netcafieldInfo").length){
             $(".netcafieldInfo").remove(); //先删除旧的签名域
         }
-        // var params = {
-        //     srcFile: '',
-        //     srcBytes: signBytes,
-		// 	srcStreamId: streamId
-        // };
-        // NetcaPKI.getSignatureFieldInfo(params)
-        //     .Then(function (res) {
-        //         that.getSignatureFieldInfoSuccessCallBack(res,streamId);
-        //     })
-        //     .Catch(function (res) {
-        //         that.getSignatureFieldInfoFailedCallBack(res);
-        //     });
+        var params = {
+            srcFile: '',
+            srcBytes: signBytes,
+			srcStreamId: streamId
+        };
+        NetcaPKI.getSignatureFieldInfo(params)
+            .Then(function (res) {
+                that.getSignatureFieldInfoSuccessCallBack(res,streamId);
+            })
+            .Catch(function (res) {
+                that.getSignatureFieldInfoFailedCallBack(res);
+            });
     };
     that.getSignatureFieldInfoSuccessCallBack = function(res,streamId){
 
@@ -1370,7 +1372,7 @@ var NetcaSignAPI = (function(){
     that.getSignatureFieldInfoFailedCallBack = function(res){
 
         layer.closeAll('loading');
-        alert('签名域信息失败:'+res.msg);
+        // alert('签名域信息失败:'+res.msg);
 
     };
     that.zoomPDFBySelectOptionScale = function(){
@@ -1991,7 +1993,10 @@ var NetcaPDFSeal = (function(){
                             globalPDFViewerApplication.pdfDocument.getData().then(function(data){
                                 // var PDFBytes = NetcaUtils.arrayBufferToBase64(data);
                                 NetcaPDFSeal.setPDFBytes(data);
-                                // NetcaSignAPI.getSealConfigInfo(data);
+                                if (can_netcasign) {
+                                    NetcaSignAPI.getSealConfigInfo(data);
+                                }
+
                             })
                         }
                     }

+ 21 - 0
app/public/report/js/rpt_archive.js

@@ -121,6 +121,27 @@ let rptArchiveObj = {
         let me = rptArchiveObj;
         if (me.currentNode && me.currentArchiveUuid) {
             try {
+                const msgSign = _.find(ARCHIVE_ENCRYPTION_LIST, {rpt_id: me.currentNode.ID});
+                console.log(msgSign);
+                let html = '';
+                let pagetr = '';
+                if (msgSign) {
+                    const rows = 12/msgSign.encryption.length < 3 ? 'col-3' : 'col-' + 12/msgSign.encryption.length;
+                    console.log(rows);
+                    for (const [index,role] of msgSign.encryption.entries()) {
+                        html += '<div class="'+ rows +'">\n' +
+                            '                                <div class="custom-control custom-radio custom-control-inline">\n' +
+                            '                                    <input type="radio" value="'+ index +'" id="customRadioInline'+ index +'" name="customRadioInline" class="custom-control-input">\n' +
+                            '                                    <label class="custom-control-label" for="customRadioInline'+ index +'">'+ role.name +'</label>\n' +
+                            '                                </div>\n' +
+                            '                            </div>';
+                    }
+                    for (let i = 1; i <= msgSign.total_page; i++) {
+                        pagetr += '<tr><td>页'+ i +'</td><td>'+ me.currentNode.name +'</td><td></td></tr>';
+                    }
+                }
+                $('#role-list').html(html);
+                $('#page-list').html(pagetr);
                 // let uuIdUrl =  "/getArchivedFileByUUID/" + me.currentArchiveUuid + "/" + stringUtil.replaceAll(me.currentNode.name, "#", "_");
                 // console.log(uuIdUrl);
                 $('#iframe_made').html('<iframe src="/archive/pdf/show?file=https://measure-sign-pdf.oss-cn-shenzhen.aliyuncs.com/archive/'+ me.currentArchiveUuid +'.PDF" height="750px" width="100%" style="border: none;"></iframe>');

+ 2 - 2
app/public/report/js/rpt_signature.js

@@ -253,7 +253,7 @@ let rptSignatureHelper = {
                     }
                     if (!hasPic) {
                         // 在交互操作中,有可能实际上是没有
-                        elementsStrArr.push('<a href="#add-sign" onclick="rptSignatureHelper.currentSelectedESignAccDom = this.parentNode; rptSignatureHelper.currentSelectedESignAccName = \'' + sCell.signature_name + '\'" data-toggle="modal" data-target="#add-sign"><i class="fa fa-plus"></i> 添加签</a>');
+                        elementsStrArr.push('<a href="#add-sign" onclick="rptSignatureHelper.currentSelectedESignAccDom = this.parentNode; rptSignatureHelper.currentSelectedESignAccName = \'' + sCell.signature_name + '\'" data-toggle="modal" data-target="#add-sign"><i class="fa fa-plus"></i> 添加签</a>');
                     }
                     // if (sCell.path || sCell.pic) {
                     // } else {
@@ -355,7 +355,7 @@ let rptSignatureHelper = {
         let jDom = $(dom.parentNode.parentNode);
         jDom.empty();
         jDom.append('<a href="#add-sign" onclick="rptSignatureHelper.currentSelectedESignAccDom = this.parentNode; rptSignatureHelper.currentSelectedESignAccName = \'' +
-            accTxtName + '\'" data-toggle="modal" data-target="#add-sign"><i class="fa fa-plus"></i> 添加签</a>');
+            accTxtName + '\'" data-toggle="modal" data-target="#add-sign"><i class="fa fa-plus"></i> 添加签</a>');
         // 要记得清空相关pre_path属性
         for (const page of zTreeOprObj.currentRptPageRst.items) {
             if (page.signature_cells) {

+ 4 - 0
app/router.js

@@ -412,6 +412,10 @@ module.exports = app => {
     app.get('/profile/sms', sessionAuth, 'profileController.sms');
     app.post('/profile/sms/type', sessionAuth, 'profileController.smsType');
     app.get('/profile/sign', sessionAuth, 'profileController.sign');
+    app.get('/profile/sign/netca', sessionAuth, 'profileController.netcasign');
+    app.post('/profile/sign/save', sessionAuth, 'profileController.signSave');
+    // app.get('/profile/netcasign/delete', sessionAuth, 'profileController.netcasignDelete');
+    // app.get('/profile/netcasign/upload', sessionAuth, 'profileController.netcasignload');
     app.post('/profile/sign/delete', sessionAuth, 'profileController.signDelete');
     app.post('/profile/sign/upload', sessionAuth, 'profileController.signUpload');
     app.get('/profile/safe', sessionAuth, 'profileController.safe');

+ 56 - 0
app/service/netcasign.js

@@ -0,0 +1,56 @@
+'use strict';
+
+/**
+ * 网证通电子签名数据模型
+ *
+ * @author EllisRan
+ * @date 2021/7/15
+ * @version
+ */
+
+module.exports = app => {
+
+    class Netcasign extends app.BaseService {
+
+        /**
+         * 构造函数
+         *
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        constructor(ctx) {
+            super(ctx);
+            this.tableName = 'netcasign';
+        }
+
+        async add(data, uid) {
+            const insertData = {
+                uid,
+                name: data.name,
+                keyId: data.keyId,
+                create_time: new Date(),
+            };
+            const operate = await this.db.insert(this.tableName, insertData);
+            return operate.affectedRows > 0;
+        }
+
+        async del(uid) {
+            return await this.db.delete(this.tableName, { uid });
+        }
+
+        /**
+         * 保存数据
+         *
+         * @param {Object} data - post过来的数据
+         * @param {Number} id - 用于判断修改还是新增的id
+         * @return {boolean} - 操作结果
+         */
+        async save(data, id = 0) {
+            data.id = id;
+            const operate = await this.db.update(this.tableName, data);
+            return operate.affectedRows > 0;
+        }
+    }
+
+    return Netcasign;
+};

+ 15 - 3
app/service/project_account.js

@@ -15,6 +15,7 @@ const SMS = require('../lib/sms');
 const SmsAliConst = require('../const/sms_alitemplate');
 const loginWay = require('../const/setting').loginWay;
 const smsTypeConst = require('../const/sms_type').type;
+const pageShowConst = require('../const/page_show').defaultSetting;
 module.exports = app => {
 
     class ProjectAccount extends app.BaseService {
@@ -133,7 +134,7 @@ module.exports = app => {
                         code: projectData.code,
                         userAccount: projectData.user_account,
                         custom: projectData.custom,
-                        page_show: projectData.page_show ? JSON.parse(projectData.page_show) : null,
+                        page_show: await this.getPageShow(projectData.page_show),
                     };
 
                     // 查找对应数据
@@ -186,7 +187,7 @@ module.exports = app => {
                         name: projectData.name,
                         userAccount: projectData.user_account,
                         custom: projectData.custom,
-                        page_show: projectData.page_show ? JSON.parse(projectData.page_show) : null,
+                        page_show: await this.getPageShow(projectData.page_show),
                     };
 
                     // 查找对应数据
@@ -245,6 +246,17 @@ module.exports = app => {
             return result;
         }
 
+        async getPageShow(page_show) {
+            const info = page_show ? JSON.parse(page_show) : {};
+            console.log(info);
+            console.log(pageShowConst);
+            for (const pi in pageShowConst) {
+                info[pi] = !info[pi] || info[pi] === '' ? pageShowConst[pi] : parseInt(info[pi]);
+                this.ctx.helper._.defaults(info[pi], pageShowConst[pi]);
+            }
+            return info;
+        }
+
         /**
          * 根据项目id获取用户列表
          *
@@ -682,7 +694,7 @@ module.exports = app => {
                 name: projectData.name,
                 userAccount: projectData.user_account,
                 custom: projectData.custom,
-                page_show: projectData.page_show ? JSON.parse(projectData.page_show) : null,
+                page_show: await this.getPageShow(projectData.page_show),
             };
 
             // 查找对应数据

+ 3 - 2
app/service/s2b_proj.js

@@ -43,11 +43,12 @@ module.exports = app => {
             this.ctx.session.sessionProject.dagl_status = s2bProj ? s2bProj.dagl_status : thirdPartyConst.dagl;
         }
 
-        async updateGxbyStatus(pid, status, limit) {
+        async updateGxbyStatus(pid, status, limit, ratio) {
             const sp = await this.getDataByPid(pid);
             const gs = sp.gxby_status.find(x => { return x.value === status; });
             if (!gs) throw '提交数据错误';
-            gs.limit = limit;
+            if (limit !== undefined && limit !== null) gs.limit = limit;
+            if (ratio !== undefined && ratio !== null) gs.ratio = this.ctx.helper.round(ratio, 2);
             await this.db.update(this.tableName, { gxby_status: JSON.stringify(sp.gxby_status) }, { where: { pid } });
             return gs;
         }

+ 1 - 1
app/view/layout/layout.ejs

@@ -6,7 +6,7 @@
     <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
     <title><%= ctx.menu.name === undefined ? '主页' : ctx.menu.name %>-计量支付</title>
     <link rel="stylesheet" href="/public/css/bootstrap/bootstrap.min.css">
-    <link rel="stylesheet" href="/public/css/main.css">
+    <link rel="stylesheet" href="/public/css/main.css?<%- ctx.app.config.version %>">
     <% if (ctx.request.host.indexOf('jlqa') !== -1 || ctx.request.host.indexOf('jluat') !== -1 || ctx.request.host === '127.0.0.1:7002') { %>
     <link rel="stylesheet" href="/public/css/qa_side.css">
     <% } %>

+ 16 - 13
app/view/material/list.ejs

@@ -24,9 +24,20 @@
         <div class="c-header p-0">
         </div>
         <div class="row w-100 sub-content">
-            <div id="left-view" class="c-body" style="width: 70%">
+            <div class="col-12 c-body">
                 <!--上部分-->
-                <div class="sjs-height-1" id="ledger-spread">
+                <div class="sjs-height-1 row w-100 sub-content">
+                    <div class="c-body" id="left-view" style="width: 70%">
+                        <div class="sjs-height-1" id="ledger-spread"></div>
+                    </div>
+                    <div class="c-body" id="right-view" style="width: 30%">
+                        <div class="resize-x" id="right-spr" r-Type="width" div1="#left-view" div2="#right-view" title="调整大小" a-type="percent"><!--调整左右高度条--></div>
+                        <div class="tab-content">
+                            <div class="tab-pane active" id="material-spread-div" style="position: relative">
+                                <div class="sjs-height-1" id="material-spread"></div>
+                            </div>
+                        </div>
+                    </div>
                 </div>
                 <!--下部分-->
                 <div class="bcontent-wrap">
@@ -36,9 +47,9 @@
                                 <a class="nav-link active" data-toggle="tab" href="#xmujie" role="tab">清单明细</a>
                             </li>
                             <!--<li class="ml-auto">-->
-                                <!--<% if (!material.readOnly) { %>-->
-                                <!--<button class="btn btn-sm btn-primary" id="user_all_material">应用调差工料至其他清单明细</button>-->
-                                <!--<% } %>-->
+                            <!--<% if (!material.readOnly) { %>-->
+                            <!--<button class="btn btn-sm btn-primary" id="user_all_material">应用调差工料至其他清单明细</button>-->
+                            <!--<% } %>-->
                             <!--</li>-->
                         </ul>
                     </div>
@@ -46,14 +57,6 @@
                     </div>
                 </div>
             </div>
-            <div class="c-body" id="right-view" style="width: 30%;">
-                <div class="resize-x" id="right-spr" r-Type="width" div1="#left-view" div2="#right-view" title="调整大小" a-type="percent"><!--调整左右高度条--></div>
-                <div class="tab-content">
-                    <div class="tab-pane active" id="material-spread-div" style="position: relative">
-                        <div class="sjs-height-6" id="material-spread"></div>
-                    </div>
-                </div>
-            </div>
         </div>
         <div class="side-menu">
             <!--右侧菜单-->

+ 151 - 0
app/view/profile/netcasign.ejs

@@ -0,0 +1,151 @@
+<% include ./sub_menu.ejs %>
+<div class="panel-content">
+    <div class="panel-title">
+        <div class="title-main">
+            <h2>签字管理</h2>
+        </div>
+    </div>
+    <div class="content-wrap">
+        <div class="c-body">
+            <div class="sjs-height-0">
+                <% if (ctx.session.sessionProject.page_show.openNetCaSign) { %>
+                <nav class="nav nav-tabs m-3" role="tablist">
+                    <a class="nav-item nav-link active" href="/profile/sign/netca">Ukey认证签名</a>
+                    <a class="nav-item nav-link" href="/profile/sign">签字设置</a>
+                </nav>
+                <% } %>
+                <div class="row m-0">
+                    <div class="col-9 my-3">
+                        <% if (!signData) { %>
+                            <div id="set_ukey" class="form-group">
+                                <label>网证通UKey</label>
+                                <div>
+                                    <button class="btn btn-sm btn-outline-primary" id="getCert">获取证书信息</button>
+                                    <small class="form-text text-danger">请先插入网证通UKey获取证书信息再绑定</small>
+                                    <textarea id="DeviceOutputId" readonly class="form-control mt-1 mb-1" rows="3"></textarea>
+                                    <input type="hidden" id="name" />
+                                    <input type="hidden" id="keyId" />
+                                    <button class="btn btn-sm btn-outline-primary" id="bind_btn">绑定</button>
+                                </div>
+                            </div>
+                        <% } %>
+                        <div id="had_ukey" <% if (!signData) { %>style="display: none"<% } %>>
+                            <div class="form-group">
+                                <label>网证通UKey</label>
+                                <div>
+                                    <small class="form-text text-secondary">已绑证书信息</small>
+                                    <textarea class="form-control mt-1 mb-1" id="had_textarea" readonly rows="3">证书主题:<%- signData ? signData.name : '' %>,keyId:<%- signData ? signData.keyId : '' %></textarea>
+                                    <button class="btn btn-sm btn-outline-danger" data-toggle="modal" data-target="#remove-netcasign">解除绑定</button>
+                                </div>
+                            </div>
+                            <!--账号资料-->
+                            <form>
+                                <div class="form-group">
+                                    <div class="form-check form-check-inline">
+                                        <input class="form-check-input sign-type" type="radio" name="sign" id="sign-type-1" value="1" checked>
+                                        <label class="form-check-label" for="sign-type-1">在线手写签名图</label>
+                                    </div>
+                                    <div class="form-check form-check-inline">
+                                        <input class="form-check-input sign-type" type="radio" name="sign" id="sign-type-2" value="2">
+                                        <label class="form-check-label" for="sign-type-2">上传签名图</label>
+                                    </div>
+                                </div>
+                                <div class="form-group show-upload" style="display: none">
+                                    <label>上传签名图</label>
+                                    <input type="file" class="form-control-file" id="netcasign-upload">
+                                    <small class="form-text text-danger">图片大小为600x300,格式PNG透明背景。</small>
+                                </div>
+                                <div class="form-group show-qrcode">
+                                    <label>在线手写签名</label>
+                                    <div><img src="/profile/qrCode?from=netcasign" width="150"></div>
+                                    <small class="form-text text-danger">微信扫码使用在线手写程序</small>
+                                </div>
+                                <button id="remove-netcasign-sign-btn" type="button" class="btn btn-danger btn-sm" data-toggle="modal" data-target="#remove-netcasign-sign" <% if (!signData || !signData.sign_base64) { %>style="display: none" <% } %>>移除签名</button>
+                                <div class="form-group">
+                                    <label>签名图预览</label>
+                                    <div>
+                                        <div class="position-relative">
+                                            <img src="/public/images/baobiao3.png">
+                                            <div class="position-absolute fixed-top" id="netcasign-show" style="left:290px;top:320px">
+                                                <% if (signData && signData.sign_base64) { %>
+                                                    <img src="data:image/png;base64,<%- signData.sign_base64 %>" width="90">
+                                                <% } %>
+                                            </div>
+                                        </div>
+                                    </div>
+                                </div>
+                            </form>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
+<script type="text/javascript">
+    const csrf = '<%= ctx.csrf %>';
+</script>
+<script type="text/javascript" src="/public/js/profile.js"></script>
+<script src="/public/netcasign/js/base64.min.js"></script>
+<script src="/public/netcasign/js/netcawebsocket.js"></script>
+<script src="/public/netcasign/js/appPackage.js"></script>
+<script>
+    $(function () {
+        $('#getCert').click(function () {
+            getCertList();
+        });
+
+        $('#bind_btn').click(function () {
+            if($('#DeviceOutputId').val() == '') {
+                toastr.error('请获取证书信息再绑定');
+                return;
+            }
+            postData('/profile/sign/save', {type: 'bind', updateData: {name: $('#name').val(), keyId: $('#keyId').val()}}, function (result) {
+                toastr.success('绑定成功');
+                $('#set_ukey').hide();
+                $('#had_ukey').show();
+                $('#had_textarea').val('证书主题:'+ result.name +',keyId:' + result.keyId);
+            })
+        });
+
+        $('#unbind_btn').click(function () {
+            postData('/profile/sign/save', {type: 'unbind'}, function (result) {
+                toastr.success('解绑成功');
+                window.location.href = '/profile/sign/netca';
+            })
+        });
+
+        $('#delsign_btn').click(function () {
+            postData('/profile/sign/save', {type: 'delsign'}, function (result) {
+                toastr.success('签名图已删除成功');
+                $('#remove-netcasign-sign-btn').hide();
+                $('#netcasign-show').html('');
+            })
+        });
+
+        // 上传签名
+        $('#netcasign-upload').change(function () {
+            const file = this.files[0];
+            const ext = file.name.toLowerCase().split('.').splice(-1)[0];
+            const imgStr = /(png|PNG)$/;
+            if (!imgStr.test(ext)) {
+                toast('请上传正确的图片格式文件','error');
+                return
+            }
+            if ($(this).val()) {
+                const reader = new FileReader();
+                reader.onload = function (evt) {
+                    console.log(evt.target.result);
+                    postData('/profile/sign/save', {type: 'savesign', sign_base64: evt.target.result.split(',')[1]}, function (result) {
+                        toastr.success('签名图设置成功');
+                        const html = '<img src="'+ evt.target.result +'" width="90">';
+                        $('#netcasign-show').html(html);
+                        $('#netcasign-upload').val('');
+                        $('#remove-netcasign-sign-btn').show();
+                    })
+                };
+                reader.readAsDataURL(file);
+            }
+        })
+    })
+</script>

+ 42 - 36
app/view/profile/sign.ejs

@@ -2,54 +2,60 @@
 <div class="panel-content">
     <div class="panel-title">
         <div class="title-main">
-            <h2>电子签名</h2>
+            <h2>签字管理</h2>
         </div>
     </div>
     <div class="content-wrap">
         <div class="c-body">
             <div class="sjs-height-0">
-            <div class="row m-0">
-                <div class="col-5 my-3">
-                    <!--账号资料-->
-                    <form>
-                        <div class="form-group">
-                            <div class="form-check form-check-inline">
-                                <input class="form-check-input sign-type" type="radio" name="sign" id="sign-type-1" value="1" checked>
-                                <label class="form-check-label" for="sign-type-1">在线手写签名图</label>
+                <% if (ctx.session.sessionProject.page_show.openNetCaSign) { %>
+                <nav class="nav nav-tabs m-3" role="tablist">
+                    <a class="nav-item nav-link" href="/profile/sign/netca">Ukey认证签名</a>
+                    <a class="nav-item nav-link active" href="/profile/sign">签字设置</a>
+                </nav>
+                <% } %>
+                <div class="row m-0">
+                    <div class="col-5 my-3">
+                        <!--账号资料-->
+                        <form>
+                            <div class="form-group">
+                                <div class="form-check form-check-inline">
+                                    <input class="form-check-input sign-type" type="radio" name="sign" id="sign-type-1" value="1" checked>
+                                    <label class="form-check-label" for="sign-type-1">在线手写签名图</label>
+                                </div>
+                                <div class="form-check form-check-inline">
+                                    <input class="form-check-input sign-type" type="radio" name="sign" id="sign-type-2" value="2">
+                                    <label class="form-check-label" for="sign-type-2">上传签名图</label>
+                                </div>
                             </div>
-                            <div class="form-check form-check-inline">
-                                <input class="form-check-input sign-type" type="radio" name="sign" id="sign-type-2" value="2">
-                                <label class="form-check-label" for="sign-type-2">上传签名图</label>
+                            <div class="form-group" id="show-upload" style="display: none">
+                                <label>上传签名图</label>
+                                <input type="file" class="form-control-file" id="sign-upload">
+                                <small class="form-text text-danger">图片大小为600x300,格式PNG透明背景。</small>
                             </div>
-                        </div>
-                        <div class="form-group" id="show-upload" style="display: none">
-                            <label>上传签名图</label>
-                            <input type="file" class="form-control-file" id="sign-upload">
-                            <small class="form-text text-danger">图片大小为600x300,格式PNG透明背景。</small>
-                        </div>
-                        <div class="form-group" id="show-qrcode">
-                            <label>在线手写签名</label>
-                            <div><img src="/profile/qrCode" width="150"></div>
-                            <small class="form-text text-danger">微信扫码使用在线手写程序</small>
-                        </div>
-                        <button type="button" class="btn btn-danger btn-sm" id="delete-sign">移除签名</button>
-                        <div class="form-group">
-                            <label>签名图预览</label>
-                            <div>
-                                <div class="position-relative">
-                                    <img src="/public/images/baobiao3.png">
-                                    <div class="position-absolute fixed-top" id="sign-show" style="left:290px;top:320px">
-                                        <% if (accountData.sign_path !== '') { %>
-                                            <img src="/public/upload/sign/<%= accountData.sign_path %>" width="90">
-                                        <% } %>
+                            <div class="form-group" id="show-qrcode">
+                                <label>在线手写签名</label>
+                                <div><img src="/profile/qrCode" width="150"></div>
+                                <small class="form-text text-danger">微信扫码使用在线手写程序</small>
+                            </div>
+                            <button type="button" class="btn btn-danger btn-sm" id="delete-sign">移除签名</button>
+                            <div class="form-group">
+                                <label>签名图预览</label>
+                                <div>
+                                    <div class="position-relative">
+                                        <img src="/public/images/baobiao3.png">
+                                        <div class="position-absolute fixed-top" id="sign-show" style="left:290px;top:320px">
+                                            <% if (accountData.sign_path !== '') { %>
+                                                <img src="/public/upload/sign/<%= accountData.sign_path %>" width="90">
+                                            <% } %>
+                                        </div>
                                     </div>
                                 </div>
                             </div>
-                        </div>
-                    </form>
+                        </form>
+                    </div>
                 </div>
             </div>
-            </div>
         </div>
     </div>
 </div>

+ 62 - 0
app/view/profile/sign_modal.ejs

@@ -0,0 +1,62 @@
+<!--短信图示-->
+<div class="modal fade" id="remove-netcasign" >
+    <div class="modal-dialog" role="document">
+        <form class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">解除绑定</h5>
+                <button type="button" class="close" data-dismiss="modal" aria-label="Close">
+                    <span aria-hidden="true">×</span>
+                </button>
+            </div>
+            <div class="modal-body">
+                <h6>确认解绑已绑证书信息并删除签名图?</h6>
+            </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-danger" id="unbind_btn">确定解绑</button>
+            </div>
+        </form>
+    </div>
+</div>
+
+<div class="modal fade" id="remove-netcasign-sign" >
+    <div class="modal-dialog" role="document">
+        <form class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">移除签名</h5>
+                <button type="button" class="close" data-dismiss="modal" aria-label="Close">
+                    <span aria-hidden="true">×</span>
+                </button>
+            </div>
+            <div class="modal-body">
+                <h6>确认移除已绑证书的签名图?</h6>
+            </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-danger" data-dismiss="modal" id="delsign_btn">确定移除</button>
+            </div>
+        </form>
+    </div>
+</div>
+
+<div class="modal fade" id="netcasign-drive" >
+    <div class="modal-dialog" role="document">
+        <form class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">找不到签名驱动</h5>
+                <button type="button" class="close" data-dismiss="modal" aria-label="Close">
+                    <span aria-hidden="true">×</span>
+                </button>
+            </div>
+            <div class="modal-body">
+                <h6>获取电子签章客户端配置信息失败:数字证书服务连接失败,请确认已安装最新的数字证书驱动!</h6>
+                <div class="custom-control mb-2">请下载下列软件解压并依次安装,安装完成后刷新本页重新加载</div>
+                <div class="custom-control mb-2">网证通安全客户端:<a href="http://dl.cnca.net/Drivers/网证通安全客户端.zip" target="_blank">网证通安全客户端</a></div>
+                <div class="custom-control mb-2">网证通电子签章软件:<a href="http://dl.cnca.net/Drivers/NETCA电子签章软件.zip" target="_blank">网证通电子签章软件</a></div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">关闭</button>
+            </div>
+        </form>
+    </div>
+</div>

+ 2 - 2
app/view/profile/sub_menu.ejs

@@ -7,8 +7,8 @@
             <ul class="nav-list list-unstyled">
                 <% for (const index in ctx.subMenu) { %>
                 <% if (ctx.subMenu[index].display === false) { %>
-                <li <% if (ctx.url === ctx.subMenu[index].url) { %>class="active"<% } %>>
-                    <a href="<%- ctx.subMenu[index].url %>">
+                <li <% if (ctx.url.indexOf(ctx.subMenu[index].url) !== -1) { %>class="active"<% } %>>
+                    <a href="<% if ( ctx.subMenu[index].url.indexOf(ctx.subMenu.sign.url) !== -1 && ctx.session.sessionProject.page_show.openNetCaSign) { %>/profile/sign/netca<% } else { %><%- ctx.subMenu[index].url %><% } %>">
                         <span><%- ctx.subMenu[index].name %></span>
                     </a>
                 </li>

+ 126 - 4
app/view/report/archive_pdf.ejs

@@ -10,6 +10,100 @@
     <link rel="stylesheet" href="/public/netcasign/ui/css/netcasignpdf.css">
     <link rel="stylesheet" href="/public/netcasign/ui/css/common.css">
     <link rel="resource" type="application/l10n" href="/public/netcasign/ui/locale/locale.properties">
+    <style>
+
+        .modal-background {
+            display: none;
+            position: fixed;
+            left: 0;
+            top: 0;
+            width: 100%;
+            height: 100%;
+            background-color: rgba(0,0,0,0.5);
+            font-size: 12px;
+            color: #212529;
+        }
+
+        .modal-content {
+            background:#fff;
+            width: 50%;
+            z-index: 100000;
+            margin: 12% auto;
+            overflow: auto;
+            border-radius: .3rem;
+        }
+        .modal-body {
+            /*background:#fff;*/
+            margin: auto;
+            /*height: 300px;*/
+            padding: 1rem;
+        }
+
+        .modal-header {
+            padding: .5rem 1rem;
+            border-bottom: 1px solid #dee2e6;
+            /*background: #5cd31b;*/
+        }
+
+        .modal-title {
+            font-size: 16px;
+            margin-bottom: 0;
+            line-height: 1.5;
+            font-weight: 500;
+        }
+
+        .modal-close-button {
+            float: right;
+            font-size: 22px;
+            cursor: pointer;
+        }
+
+        .modal-footer {
+            padding: .5rem 1rem;
+            border-top: 1px solid #dee2e6;
+            border-bottom-right-radius: .3rem;
+            border-bottom-left-radius: .3rem;
+            display: flex;
+            align-items: center;
+            justify-content: flex-end;
+            /*background: #5cd31b;*/
+            /*color: white;*/
+        }
+        .modal-button {
+            font-size: 12px;
+            padding: 1px 0.6rem;
+            line-height: 1.5;
+            border-radius: .2rem;
+            color: #fff;
+            background-color: #6c757d;
+            border-color: #6c757d;
+            font-weight: 400;
+            cursor: pointer;
+            /*margin-right: .25rem;*/
+            vertical-align: baseline;
+            display: inline-block;
+            text-align: center;
+            border: 1px solid transparent;
+            transition: color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;
+            margin: 0;
+        }
+
+        .form-group {
+            margin-bottom: .5rem;
+        }
+        .mb-2 {
+            margin-bottom: .5rem!important;
+        }
+        .custom-control {
+            margin-bottom: .3rem;
+            color: #757575;
+            min-height: 1.2rem;
+            line-height: 1.2rem;
+            position: relative;
+            display: block;
+            padding-left: 1.5rem;
+        }
+    </style>
 </head>
 <body tabindex="1" class="loadingInProgress" oncontextmenu="return false;">
 <div  id="netcasignpdf">
@@ -396,6 +490,24 @@
     <img src="" alt="" id="NetcaSignMoveImage">
 </div>
 </div>
+<!-- 弹窗内容开始 -->
+<div class="modal-background">
+    <div class="modal-content">
+        <div class="modal-header">
+            <span class="modal-close-button hide-modal">×</span>
+            <h5 class="modal-title">找不到签名驱动</h5>
+        </div>
+        <div class="modal-body">
+            <div class="form-group">
+                <!--<div class="custom-control mb-2">获取电子签章客户端配置信息失败:数字证书服务连接失败,请确认已安装最新的数字证书驱动!</div>-->
+                <div class="custom-control mb-2">请下载下列软件解压并依次安装,安装完成后刷新本页重新加载</div>
+                <div class="custom-control mb-2">网证通安全客户端:<a href="http://dl.cnca.net/Drivers/网证通安全客户端.zip" target="_blank">网证通安全客户端</a></div>
+                <div class="custom-control mb-2">网证通电子签章软件:<a href="http://dl.cnca.net/Drivers/NETCA电子签章软件.zip" target="_blank">网证通电子签章软件</a></div>
+            </div>
+        </div>
+        <div class="modal-footer"><button class="modal-button hide-modal">关闭</button></div>
+    </div>
+</div>
 </body>
 
 </html>
@@ -408,19 +520,23 @@
 <script src="/public/netcasign/js/viewer.js"></script>
 <!--<![endif]-->
 <script src="/public/netcasign/ui/layer/layer.js"></script>
-<!--<script src="/public/netcasign/js/netcawebsocket.js"></script>-->
-<!--<script src="/public/netcasign/js/netcaseal.js"></script>-->
+<% if (can_netcasign) { %>
+<script src="/public/netcasign/js/netcawebsocket.js"></script>
+<script src="/public/netcasign/js/netcaseal.js"></script>
+<% } %>
 <script src="/public/netcasign/js/netcasealpdf.js"></script>
 <script src="/public/netcasign/example/appPackage.js"></script>
 <script>
-
+    const can_netcasign = <%- can_netcasign %>;
     initPDFEvent();
     function  initPDFEvent(){
         try {
 
             NetcaPDFSeal.init();
             NetcaPDFSeal.openPDFBytes('');
-            // demonstrationFn();
+            if (can_netcasign) {
+                demonstrationFn();
+            }
             // NetcaPDFSeal.openPDFWithUrl('https://measure-sign-pdf.oss-cn-shenzhen.aliyuncs.com/archive/072e3a20-c824-11eb-9ea7-3d3c8164295f.PDF');
         }
         catch(e) {
@@ -432,4 +548,10 @@
         NetcaPDFSeal.openPDFWithUrl(val);
     }
 
+    $(function () {
+        $('.hide-modal').on('click', function () {
+            $('.modal-background').hide();
+        });
+    })
+
 </script>

+ 1 - 1
app/view/report/index.ejs

@@ -139,7 +139,7 @@
                                 <div class="panel-body">
                                     <button class="btn btn-outline-primary btn-sm" type="button" data-toggle="modal" data-target="#eSignature" onclick="rptSignatureHelper.resetESignature(zTreeOprObj.currentRptPageRst, 'eSignatureBodyDiv'); rptSignatureHelper.checkAndShowCrossTendersESignature();">
                                         <i class="fa fa-pencil"></i><br>
-                                        签
+                                        签
                                     </button>
                                 </div>
                             </div>

+ 17 - 8
app/view/report/index_archive.ejs

@@ -35,8 +35,8 @@
                 </div>
                 <div class="col-auto" id="main-view" style="width: 83%">
                     <div class="resize-x" id="right-spr" r-Type="width" div1="#tree-view" div2="#main-view" title="调整大小" a-type="percent"><!--调整左右高度条--></div>
-                    <div class="toolsbar-f d-flex justify-content-between">
-                        <div class="print-toolsbar">
+                    <div class="toolsbar-f d-flex justify-content-between position-absolute bg-light m-1">
+                        <div class="print-toolsbar p-0">
                             <div class="panel">
                                 <div class="panel-body" id="print_div">
                                     <div class="btn-group" role="group">
@@ -76,14 +76,23 @@
                                     历史归档
                                 </div>
                             </div>
-                        </div>
-                    </div>                    <div class="sjs-height-4">
-                        <div class="print-view form-view">
-                            <div class="pageContainer">
-                                <div class="page w-100" id="iframe_made">
-                                    <iframe src="/archive/pdf/show" height="750px" width="100%" style="border: none;"></iframe>
+                            <% if (can_netcasign) { %>
+                            <div class="panel">
+                                <div class="panel-body">
+                                    <div class="btn-group" role="group">
+                                        <button class="btn btn-outline-primary btn-sm" type="button" data-toggle="modal" data-target="#sign">
+                                            <i class="fa fa-pencil"></i><br>
+                                            电子签名 <span class="badge badge-primary">0</span>
+                                        </button>
+                                    </div>
                                 </div>
                             </div>
+                            <% } %>
+                        </div>
+                    </div>
+                    <div class="print-view form-view">
+                        <div class="pageContainer" id="iframe_made">
+                            <iframe src="/archive/pdf/show"  class="sjs-height-0 border-0" width="100%"></iframe>
                         </div>
                     </div>
                 </div>

+ 63 - 0
app/view/report/index_archive_modal.ejs

@@ -0,0 +1,63 @@
+<!--弹出批量电子签名-->
+<div class="modal fade" id="sign" data-backdrop="static">
+    <div class="modal-dialog modal-lg" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">电子签名</h5>
+                <button type="button" class="close" data-dismiss="modal" aria-label="Close">
+                    <span aria-hidden="true">&times;</span>
+                </button>
+            </div>
+            <div class="modal-body">
+                <p>当前使用 #3 2021-03-02 归档报表进行签名,签名后其余归档仅能查看。</p>
+                <div class="card mb-3">
+                    <div class="card-header">选择签名位置</div>
+                    <div class="card-body py-3">
+                        <style>
+                            #role-list div:nth-child(n+5) {
+                                margin-top: 1rem;
+                            }
+                        </style>
+                        <div class="row" id="role-list">
+                            <div class="col-auto">
+                                <div class="custom-control custom-radio custom-control-inline">
+                                    <input type="radio" id="customRadioInline1" name="customRadioInline" class="custom-control-input">
+                                    <label class="custom-control-label" for="customRadioInline1">编制</label>
+                                </div>
+                            </div>
+                            <div class="col-auto">
+                                <div class="custom-control custom-radio custom-control-inline">
+                                    <input type="radio" id="customRadioInline2" name="customRadioInline" class="custom-control-input">
+                                    <label class="custom-control-label" for="customRadioInline2">复核</label>
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+                <div class="modal-height-300">
+                    <table class="table table-hover table-bordered">
+                        <thead>
+                        <tr><th colspan="4" class="text-center">签名页</th></tr>
+                        <tr><th width="60">页码</th>
+                            <th >名称</th>
+                            <th >签名</th>
+                            <!--<th width="40"><input type="checkbox"></td></th>-->
+                        </tr></thead>
+                        <tbody id="page-list">
+                        <tr><td>页1</td><td>工程费用表</td><td><span class="text-success"><i class="fa fa-check"></i> 编制(张三)</span></td><td><input type="checkbox"></td></tr>
+                        <tr><td>页2</td><td>工程费用表</td><td><span class="text-success"><i class="fa fa-check"></i> 编制(张三)</span></td><td><input type="checkbox"></td></tr>
+                        <tr><td>页3</td><td>工程费用表</td><td><span class="text-success"><i class="fa fa-check"></i> 编制(张三)</span><span class="text-success"><i class="fa fa-check"></i> 复核(李四)</span></td><td><input type="checkbox"></td></tr>
+                        <tr><td>页4</td><td>工程费用表</td><td></td><td><input type="checkbox"></td></tr>
+                        <tr><td>页5</td><td>工程费用表</td><td></td><td><input type="checkbox"></td></tr>
+                        </tbody>
+                    </table>
+                </div>
+            </div>
+            <div class="modal-footer">
+                <!--<a href="" class="btn btn-sm btn-danger mr-5" data-toggle="modal" data-target="#sign2" data-dismiss="modal">撤销签名</a>-->
+                <button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">取消</button>
+                <a href="" class="btn btn-sm btn-primary">确定</a>
+            </div>
+        </div>
+    </div>
+</div>

+ 5 - 5
app/view/report/rpt_all_popup.ejs

@@ -195,7 +195,7 @@
     <div class="modal-dialog" role="document">
         <div class="modal-content">
             <div class="modal-header">
-                <h5 class="modal-title">设置电子签名(审核通过后才显示)</h5>
+                <h5 class="modal-title">设置签字(审核通过后才显示)</h5>
                 <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                     <span aria-hidden="true">&times;</span>
                 </button>
@@ -221,7 +221,7 @@
     <div class="modal-dialog modal-lg" role="document">
         <div class="modal-content">
             <div class="modal-header">
-                <h5 class="modal-title">批量设置电子签名</h5>
+                <h5 class="modal-title">批量设置签字</h5>
                 <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                     <span aria-hidden="true">&times;</span>
                 </button>
@@ -236,7 +236,7 @@
                         <div class="col-6" id="batch-eSignatureBodyDiv"></div>
                     </div>
                 </div>
-                <div class="alert alert-warning">批量操作会覆盖已设置好的电子签名,请谨慎操作。</div>
+                <div class="alert alert-warning">批量操作会覆盖已设置好的签字,请谨慎操作。</div>
             </div>
             <div class="modal-footer">
                 <button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">取消</button>
@@ -250,7 +250,7 @@
     <div class="modal-dialog modal-sm" role="document">
         <div class="modal-content">
             <div class="modal-header">
-                <h5 class="modal-title">添加签</h5>
+                <h5 class="modal-title">添加签</h5>
                 <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                     <span aria-hidden="true">&times;</span>
                 </button>
@@ -497,7 +497,7 @@
             </div>
             <div class="modal-body">
                 <h6>归档后,本期报表将自动导出PDF格式,不再受新一期数据影响;</h6>
-                <h6>请确认报表数据无误(正确添加电子签名、设置页面格式等),继续归档操作。</h6>
+                <h6>请确认报表数据无误(正确添加签字、设置页面格式等),继续归档操作。</h6>
 
                 <div class="card">
                     <div class="card-body" id="cardArchiveInfo">

+ 17 - 5
app/view/setting/s2b.ejs

@@ -18,7 +18,7 @@
                             <div class="col-4">
                                 <legend>工序报验</legend>
                                 <table class="table table-hover table-bordered">
-                                    <thead><tr><th>值</th><th>名称</th><th>允许计量</th></tr></thead>
+                                    <thead><tr><th>值</th><th>名称</th><th>允许计量</th><th>计量比例</th></tr></thead>
                                     <tbody id="gxby_list">
                                     <% for (const s of ctx.session.sessionProject.gxby_status) { %>
                                     <tr>
@@ -30,6 +30,18 @@
                                                 <label class="form-check-label" for="inlineCheckbox-g<%- s.value %>"></label>
                                             </div>
                                         </td>
+                                        <% if (s.ratio === undefined) { %>
+                                        <td>-</td>
+                                        <% } else { %>
+                                        <td>
+                                            <div class="input-group input-group-sm" style="width:90px">
+                                                <input type="number" class="form-control" max="100" min="0" step="2" value="<%- s.ratio || 0 %>" status="<%- s.value %>" onchange="updateStatusRatio(this, 'gxby');">
+                                                <div class="input-group-append">
+                                                    <span class="input-group-text">%</span>
+                                                </div>
+                                            </div>
+                                        </td>
+                                        <% } %>
                                     </tr>
                                     <% } %>
                                     </tbody>
@@ -49,12 +61,12 @@
                                                 <label class="form-check-label" for="inlineCheckbox-d<%- s.value %>"></label>
                                             </div>
                                         </td>
-                                        <% if (s.value === 0) { %>
+                                        <% if (s.ratio === undefined) { %>
                                         <td>-</td>
                                         <% } else { %>
                                         <td>
                                             <div class="input-group input-group-sm" style="width:90px">
-                                                <input type="number" class="form-control" max="100" min="0" step="2" value="<%- s.ratio || 0 %>" status="<%- s.value %>" onchange="updateStatusRatio(this);">
+                                                <input type="number" class="form-control" max="100" min="0" step="2" value="<%- s.ratio || 0 %>" status="<%- s.value %>" onchange="updateStatusRatio(this, 'dagl');">
                                                 <div class="input-group-append">
                                                     <span class="input-group-text">%</span>
                                                 </div>
@@ -138,8 +150,8 @@
             obj.checked = !obj.checked;
         });
     }
-    const updateStatusRatio = function (obj) {
-        const data = { type: 'dagl' };
+    const updateStatusRatio = function (obj, type) {
+        const data = { type };
         data.status = parseInt(obj.getAttribute('status'));
         try {
             data.ratio = parseFloat(obj.value);

+ 24 - 5
app/view/sign/info.ejs

@@ -5,7 +5,7 @@
     <meta charset="utf-8">
     <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
     <meta http-equiv="x-ua-compatible" content="ie=edge">
-    <title>电子签名-计量支付</title>
+    <title>签字-计量支付</title>
     <style>
         #app {
             position: absolute;
@@ -79,7 +79,7 @@
             <header>
                 <input type="button" value="返回" @click="showBox = false"/>
                 <input type="button" value="上传" @click="upload" :disabled="showSuccess"/>
-                <span v-show="showSuccess">已成功上传!</span>
+                <span v-show="showSuccess">已成功上传,请刷新电脑页面查看签名效果吧。</span>
             </header>
             <img :src="signImage">
         </div>
@@ -92,6 +92,7 @@
 <script>
     const id = '<%- id %>';
     const name = '<%- name %>';
+    const from = '<%- from %>';
     const csrf = '<%= ctx.csrf %>';
     new Vue({
         el: '#app',
@@ -168,8 +169,6 @@
                 if (!this.showSuccess) {
                     const image = this.draw.getPNGImage();
                     const blob = this.draw.dataURLtoBlob(image);
-
-                    const url = '/sign/save';
                     const successCallback = (response) => {
                         // console.log(response);
                         if (JSON.parse(response).err === 0) {
@@ -182,9 +181,29 @@
                         // console.log(error);
                         this.showSuccess = false;
                     };
-                    this.draw.upload(blob, url, successCallback, failureCallback);
+                    const url = '/sign/save';
+                    if (from === 'netcasign') {
+                        this.blobToBase64(blob).then(res => {
+                            this.draw.uploadBase64(res.split(',')[1], url, successCallback, failureCallback);
+                        });
+                    } else {
+                        this.draw.upload(blob, url, successCallback, failureCallback);
+                    }
                 }
             },
+            blobToBase64(blob) {
+                return new Promise((resolve, reject) => {
+                    const fileReader = new FileReader();
+                    fileReader.onload = (e) => {
+                        resolve(e.target.result);
+                    };
+                    // readAsDataURL
+                    fileReader.readAsDataURL(blob);
+                    fileReader.onerror = () => {
+                        reject(new Error('文件流异常'));
+                    };
+                });
+            }
         },
     });
 </script>

+ 2 - 2
app/view/stage/stage_sub_menu.ejs

@@ -15,7 +15,7 @@
                 </li>
             </ul>
         </div>
-        <% if (ctx.session.sessionProject.page_show !== null && parseInt(ctx.session.sessionProject.page_show.stageExtra) === 1) { %>
+        <% if (ctx.session.sessionProject.page_show.stageExtra1) { %>
         <div class="nav-box">
             <ul class="nav-list list-unstyled">
                 <li class="<% if (ctx.url === '/tender/' + ctx.tender.id + '/measure/stage/' + ctx.stage.order + '/extra/jgcl') { %>active<% } %>">
@@ -31,7 +31,7 @@
                 </li>
             </ul>
         </div>
-        <% if (ctx.session.sessionProject.page_show !== null && parseInt(ctx.session.sessionProject.page_show.bwtz) === 1) { %>
+        <% if (ctx.session.sessionProject.page_show.bwtz) { %>
         <div class="nav-box">
             <ul class="nav-list list-unstyled">
                 <li class="<% if (ctx.url === '/tender/' + ctx.tender.id + '/measure/stage/' + ctx.stage.order + '/bwtz') { %>active<% } %>">

+ 2 - 2
app/view/stage/stage_sub_mini_menu.ejs

@@ -17,7 +17,7 @@
                 </li>
             </ul>
         </div>
-        <% if (ctx.session.sessionProject.page_show !== null && parseInt(ctx.session.sessionProject.page_show.stageExtra) === 1) { %>
+        <% if (ctx.session.sessionProject.page_show.stageExtra) { %>
         <div class="nav-box">
             <ul class="nav-list list-unstyled">
                 <li class="<% if (ctx.url === '/tender/' + ctx.tender.id + '/measure/stage/' + ctx.stage.order + '/extra/jgcl') { %>active<% } %>">
@@ -33,7 +33,7 @@
                 </li>
             </ul>
         </div>
-        <% if (ctx.session.sessionProject.page_show !== null && parseInt(ctx.session.sessionProject.page_show.bwtz) === 1) { %>
+        <% if (ctx.session.sessionProject.page_show.bwtz) { %>
         <div class="nav-box">
             <ul class="nav-list list-unstyled">
                 <li class="<% if (ctx.url === '/tender/' + ctx.tender.id + '/measure/stage/' + ctx.stage.order + '/bwtz') { %>active<% } %>">

+ 1 - 1
app/view/tender/detail.ejs

@@ -101,7 +101,7 @@
                                 <a href="#bd-set-6" data-toggle="modal" data-target="#bd-set-6" class="btn btn-sm btn-outline-primary">章节设置</a>
                                 <a href="#bd-set-7" data-toggle="modal" data-target="#bd-set-7" class="btn btn-sm btn-outline-primary">付款账号</a>
                                 <i class="mx-2">|</i>
-                                <% if (ctx.session.sessionProject.page_show !== null && parseInt(ctx.session.sessionProject.page_show.xxjd) === 1 && ctx.session.sessionUser.is_admin) { %>
+                                <% if (ctx.session.sessionProject.page_show.xxjd && ctx.session.sessionUser.is_admin) { %>
                                 <a href="#xxjd-set" data-toggle="modal" data-target="#xxjd-set" class="btn btn-sm btn-outline-primary">投资进度</a>
                                 <% } %>
                                 <a href="javascript: void(0);" class="btn btn-sm btn-outline-primary" id="copyBtn">拷贝设置</a>

+ 1 - 1
app/view/tender/detail_modal.ejs

@@ -1894,7 +1894,7 @@
     });
 </script>
 <% } %>
-<% if (ctx.session.sessionProject.page_show !== null && parseInt(ctx.session.sessionProject.page_show.xxjd) === 1 && ctx.session.sessionUser.is_admin) { %>
+<% if (ctx.session.sessionProject.page_show.xxjd && ctx.session.sessionUser.is_admin) { %>
 <!--标段设置-投资进度-->
 <div class="modal fade" id="xxjd-set" data-backdrop="static">
     <div class="modal-dialog" role="document">

+ 1 - 1
app/view/tender/tender_sub_menu.ejs

@@ -43,7 +43,7 @@
                 <li <% if (ctx.url === '/tender/' + ctx.tender.id + '/measure/material') { %>class="active"<% } %>><a href="/tender/<%- ctx.tender.id %>/measure/material" class="h3"><i class="fa fa-line-chart fa-fw"></i> <span>材料调差</span></a></li>
             </ul>
         </div>
-        <% if (ctx.session.sessionProject.page_show !== null && parseInt(ctx.session.sessionProject.page_show.xxjd) === 1 && (ctx.tender.schedule_permission !== 0 || ctx.tender.isTourist)) { %>
+        <% if (ctx.session.sessionProject.page_show.xxjd && (ctx.tender.schedule_permission !== 0 || ctx.tender.isTourist)) { %>
         <div class="nav-box">
             <h3><i class="fa fa-bar-chart fa-fw"></i> 投资进度</h3>
             <ul class="nav-list list-unstyled sub-list">

+ 1 - 1
app/view/tender/tender_sub_mini_menu.ejs

@@ -45,7 +45,7 @@
                 <li <% if (ctx.url === '/tender/' + ctx.tender.id + '/measure/material') { %>class="active"<% } %>><a href="/tender/<%- ctx.tender.id %>/measure/material" class="h3"><i class="fa fa-line-chart fa-fw"></i> <span>材料调差</span></a></li>
             </ul>
         </div>
-        <% if (ctx.session.sessionProject.page_show !== null && parseInt(ctx.session.sessionProject.page_show.xxjd) === 1 && (ctx.tender.schedule_permission !== 0 || ctx.tender.isTourist)) { %>
+        <% if (ctx.session.sessionProject.page_show.xxjd && (ctx.tender.schedule_permission !== 0 || ctx.tender.isTourist)) { %>
             <div class="nav-box">
                 <h3><i class="fa fa-bar-chart fa-fw"></i> 投资进度</h3>
                 <ul class="nav-list list-unstyled sub-list">

+ 6 - 0
build_min.js

@@ -41,3 +41,9 @@ for (const nm of JsFiles.needMin) {
 const needMinFileName = __dirname + '/app/public/js/web/global.' + version + '.min.js';
 fs.writeFileSync(needMinFileName, Uglyfy.minify(needMinCode, { mangle: true }).code);
 
+const needVersionCss = ['/public/css/main'];
+for (const nvc of needVersionCss) {
+    fs.copyFileSync(nvc + '.css', nvc + '.' + version + '.css');
+}
+
+

+ 3 - 3
builder_report_index_define.js

@@ -59,7 +59,7 @@ const ledger_cooperation = {
         { name: '审批人id', field: 'user_id', type: dataType.int },
         { name: '台账id', field: 'ledger_id', type: dataType.int },
         { name: '密码', field: 'pwd', type: dataType.str },
-        { name: '电子签名地址', field: 'sign_path', type: dataType.str },
+        { name: '签字地址', field: 'sign_path', type: dataType.str },
     ],
 };
 // 其他台账
@@ -1370,11 +1370,11 @@ const stage_audit = {
 };
 
 const sign_select = {
-    name: '电子签名(mem_sign_select)',
+    name: '签字(mem_sign_select)',
     remark: '',
     id: 47,
     key: 'mem_sign_select',
-    prefix: '电子签名',
+    prefix: '签字',
     cols: [
         { name: 'id-1', field: 'sign1.id', type: dataType.int },
         { name: '名字-1', field: 'sign1.name', type: dataType.str },

+ 0 - 1
config/config.default.js

@@ -133,7 +133,6 @@ module.exports = appInfo => {
     config.min = true;
 
     const file = path.join(__dirname, 'version');
-    console.log(file);
 
     if (fs.existsSync(file)) {
         const versionStr = fs.readFileSync(file, 'utf8');

+ 1 - 1
config/menu.js

@@ -310,7 +310,7 @@ const profileMenu = {
         url: '/profile/wechat',
     },
     sign: {
-        name: '电子签名',
+        name: '签字管理',
         display: false,
         url: '/profile/sign',
     },

+ 3 - 2
config/web.js

@@ -552,7 +552,9 @@ const JsFiles = {
                 mergeFile: 'material_exponent',
             },
             list: {
-                files: ['/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js', '/public/js/decimal.min.js', '/public/js/toastr.min.js'],
+                files: ['/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js', '/public/js/decimal.min.js',
+                    // '/public/js/calc/calcEval.min.js'
+                ],
                 mergeFiles: [
                     '/public/js/sub_menu.js',
                     '/public/js/div_resizer.js',
@@ -560,7 +562,6 @@ const JsFiles = {
                     '/public/js/zh_calc.js',
                     '/public/js/path_tree.js',
                     '/public/js/gcl_gather.js',
-                    '/public/js/CalcEval.js', // 计算表达式使用
                     '/public/js/material_list.js',
                     '/public/js/shares/cs_tools.js',
                     '/public/js/material_audit.js',