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

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

Tony Kang 10 месяцев назад
Родитель
Сommit
ca7d30fc75
98 измененных файлов с 2123 добавлено и 367 удалено
  1. 6 0
      app/base/base_bills_service.js
  2. 8 0
      app/const/account_permission.js
  3. 2 2
      app/const/deal_pay.js
  4. 1 0
      app/const/tender_info.js
  5. 3 0
      app/controller/dashboard_controller.js
  6. 0 25
      app/controller/deal_bills_controller.js
  7. 78 0
      app/controller/drawing_controller.js
  8. 14 0
      app/controller/ledger_controller.js
  9. 8 6
      app/controller/material_controller.js
  10. 12 0
      app/controller/revise_controller.js
  11. 36 10
      app/controller/setting_controller.js
  12. 2 2
      app/controller/settle_controller.js
  13. 1 1
      app/controller/sub_proj_setting_controller.js
  14. 84 0
      app/controller/wap_controller.js
  15. 12 0
      app/lib/rm/tender.js
  16. 8 5
      app/lib/rm/tender_budget.js
  17. 75 0
      app/lib/rm/tender_financial.js
  18. 2 2
      app/lib/wechat.js
  19. 4 0
      app/middleware/sub_project_check.js
  20. 67 1
      app/public/css/wap/main.css
  21. 11 2
      app/public/js/bootstrap/tree-select.js
  22. 2 2
      app/public/js/change.js
  23. 34 3
      app/public/js/change_revise.js
  24. 2 2
      app/public/js/contract_detail.js
  25. 16 8
      app/public/js/contract_panel.js
  26. 49 0
      app/public/js/drawing_tender.js
  27. 11 3
      app/public/js/file_detail.js
  28. 3 1
      app/public/js/filing_manage.js
  29. 4 1
      app/public/js/filing_template.js
  30. 13 0
      app/public/js/ledger.js
  31. 11 1
      app/public/js/login.js
  32. 13 0
      app/public/js/revise.js
  33. 3 3
      app/public/js/revise_compare.js
  34. 26 0
      app/public/js/se_jgcl.js
  35. 26 0
      app/public/js/se_other.js
  36. 28 6
      app/public/js/se_safe_prod.js
  37. 26 0
      app/public/js/se_temp_land.js
  38. 25 0
      app/public/js/se_yjcl.js
  39. 189 29
      app/public/js/setting.js
  40. 1 0
      app/public/js/setting_manage.js
  41. 1 1
      app/public/js/settle_gather.js
  42. 1 1
      app/public/js/settle_ledger.js
  43. 1 1
      app/public/js/shares/cs_tools.js
  44. 1 1
      app/public/js/shenpi.js
  45. 3 3
      app/public/js/sr_detail.js
  46. 3 3
      app/public/js/sub_project_info.js
  47. 8 0
      app/router.js
  48. 3 7
      app/service/change.js
  49. 13 13
      app/service/change_audit_list.js
  50. 2 2
      app/service/filing.js
  51. 3 3
      app/service/filing_template.js
  52. 41 0
      app/service/material_audit.js
  53. 10 1
      app/service/material_list.js
  54. 2 2
      app/service/phase_pay.js
  55. 8 0
      app/service/project_account.js
  56. 22 0
      app/service/report.js
  57. 2 2
      app/service/stage.js
  58. 35 0
      app/service/stage_jgcl.js
  59. 37 0
      app/service/stage_other.js
  60. 37 0
      app/service/stage_safe_prod.js
  61. 37 0
      app/service/stage_temp_land.js
  62. 36 0
      app/service/stage_yjcl.js
  63. 6 0
      app/service/sub_proj_permission.js
  64. 2 2
      app/service/sub_project.js
  65. 19 0
      app/service/tender.js
  66. 1 1
      app/view/change/index.ejs
  67. 131 105
      app/view/dashboard/workspace.ejs
  68. 0 0
      app/view/drawing/as_built.ejs
  69. 0 0
      app/view/drawing/design.ejs
  70. 55 0
      app/view/drawing/tender.ejs
  71. 0 0
      app/view/drawing/tender_modal.ejs
  72. 1 1
      app/view/file/file_modal.ejs
  73. 0 1
      app/view/ledger/explode.ejs
  74. 1 0
      app/view/login/login.ejs
  75. 26 1
      app/view/material/audit_modal.ejs
  76. 2 2
      app/view/profile/netcasign.ejs
  77. 3 3
      app/view/profile/sign.ejs
  78. 2 0
      app/view/setting/user_permission.ejs
  79. 21 1
      app/view/setting/user_permission_modal.ejs
  80. 19 8
      app/view/setting/user_unit.ejs
  81. 21 0
      app/view/setting/user_unit_modal.ejs
  82. 8 3
      app/view/shares/delete_hint_modal.ejs
  83. 0 1
      app/view/sp_setting/manage.ejs
  84. 1 1
      app/view/stage/deal.ejs
  85. 2 0
      app/view/tender/detail.ejs
  86. 23 18
      app/view/tender/detail_modal.ejs
  87. 1 1
      app/view/tender/index.ejs
  88. 1 1
      app/view/tender/info.ejs
  89. 18 2
      app/view/tender/list_sub_menu_list.ejs
  90. 1 1
      app/view/tender/manage.ejs
  91. 1 1
      app/view/tender/progress.ejs
  92. 36 8
      app/view/wap/dashboard.ejs
  93. 296 0
      app/view/wap/shenpi_material.ejs
  94. 124 29
      app/view/wap/tender.ejs
  95. 38 12
      config/web.js
  96. 2 0
      publish.md
  97. 15 9
      sql/update.sql
  98. 24 0
      sql/update20250613.sql

+ 6 - 0
app/base/base_bills_service.js

@@ -614,6 +614,12 @@ class BaseBillsSerivce extends TreeService {
         await this.db.query(sql, sqlParam);
     }
 
+    async sgfh2deal(tid) {
+        const sql = 'UPDATE ' + this.tableName + ' SET deal_qty = sgfh_qty, deal_tp = sgfh_tp WHERE tender_id = ?';
+        const sqlParam = [tid];
+        await this.db.query(sql, sqlParam);
+    }
+
     _calcExpr(data, field, expr, defaultValue, precision) {
         if (expr) {
             try {

+ 8 - 0
app/const/account_permission.js

@@ -56,6 +56,14 @@ const permission = {
             // { title: '修改材料税税率', value: 3, hint: '开启该选项,可在新材差期修改材料税税率', hintIcon: 'fa-question-circle' },
         ],
     },
+    other: {
+        class: 'fa fa-pencil-square-o',
+        title: '其他权限',
+        type: 'checkbox',
+        children: [
+            { title: '授权单位章', value: 1, text_modal: '选择章', modal: 'sign_permission' },
+        ],
+    },
     construction: {
         class: 'fa fa-pencil-square-o',
         title: '施工日志',

+ 2 - 2
app/const/deal_pay.js

@@ -26,7 +26,7 @@ const payTemplate = [
 const calcBase = [
     {name: '签约合同价', code: 'htj', sort: 10},
     {name: '暂列金额', code: 'zlje', sort: 2},
-    {name: '签约合同价(不含暂列金)', code: 'htjszl', sort: 1},
+    {name: '签约合同价(不含暂列金、计日工)', code: 'htjszl', sort: 1},
     {name: '签约开工预付款', code: 'kgyfk', sort: 2},
     {name: '签约材料预付款', code: 'clyfk', sort: 2},
     {name: '本期完成计量', code: 'bqwc', limit: true, sort: 10},
@@ -45,7 +45,7 @@ const calcBase = [
 const materialCalcBase = [
     {name: '签约合同价', code: 'htj', sort: 10},
     {name: '暂列金额', code: 'zlje', sort: 2},
-    {name: '签约合同价(不含暂列金)', code: 'htjszl', sort: 1},
+    {name: '签约合同价(不含暂列金、计日工)', code: 'htjszl', sort: 1},
     {name: '签约开工预付款', code: 'kgyfk', sort: 2},
     {name: '签约材料预付款', code: 'clyfk', sort: 2},
     {name: '本期完成计量', code: 'bqwc', limit: true, sort: 10},

+ 1 - 0
app/const/tender_info.js

@@ -125,6 +125,7 @@ const defaultInfo = {
     deal_param: {
         contractPrice: 0, // 签约合同价
         zanLiePrice: 0, // 暂列金额
+        jrgPrice: 0, // 计日工
         startAdvance: 0, // 开工预付款
         materialAdvance: 0, // 材料预付款
         safeAdvance: 0, // 安全生产预付款

+ 3 - 0
app/controller/dashboard_controller.js

@@ -144,6 +144,8 @@ module.exports = app => {
             const sysMsgList = await ctx.service.message.getMsgList(ctx.session.sessionProject.id, '', 1, 0, 2);
             // 获取系统维护信息
             const maintainData = await ctx.service.maintain.getDataById(1);
+            // 最近使用项目列表
+            const recentProjects = await ctx.service.subProjPermission.getRecentProjects(ctx.session.sessionUser.accountId);
             const renderData = {
                 dashboardShenpis: ctx.helper._.orderBy(dashboardShenpis, ['start_audit', 'shenpi_time'], ['desc', 'asc']),
                 dashboardStatus,
@@ -172,6 +174,7 @@ module.exports = app => {
                 maintainData,
                 maintainConst,
                 typeColMap,
+                recentProjects,
                 jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.dashboard.workspace),
             };
             await this.layout('dashboard/workspace.ejs', renderData, 'dashboard/modal.ejs');

+ 0 - 25
app/controller/deal_bills_controller.js

@@ -136,31 +136,6 @@ module.exports = app => {
                     } else if (file === '签约清单转换格式.xls') {
                         fileName = this.app.baseDir + '/app/public/deal_bills/转换格式.xls';
                         ctx.body = await fs.readFileSync(fileName);
-                    } else if (file === '签约清单.xlsx') {
-                        const create_time = Date.parse(new Date()) / 1000;
-                        fileName = this.app.baseDir + '/app/public/deal_bills/downloads/' + ctx.tender.id + '-' + create_time + '.xlsx';
-                        await this.ctx.helper.recursiveMkdirSync(this.app.baseDir + '/app/public/deal_bills/downloads');
-                        // todo 导出签约清单Excel
-                        const setting = {
-                            header: ['清单编号', '名称', '单位', '数量', '单价', '金额'],
-                            width: [80, 150, 60, 80, 80, 80],
-                            hAlign: ['left', 'left', 'center', 'right', 'right', 'right'],
-                        };
-                        const dealBills = await ctx.service.dealBills.getAllDataByCondition({ where: { tender_id: ctx.tender.id } });
-                        const data = [];
-                        for (const db of dealBills) {
-                            data.push([db.code, db.name, db.unit, db.quantity, db.unit_price, db.total_price]);
-                        }
-                        const arraySheetData = this.ctx.helper.simpleXlsxSheetData(setting, data);
-                        xlsx.writeFile({
-                            SheetNames: ['Sheet1'],
-                            Sheets: {
-                                'Sheet1': arraySheetData,
-                            },
-                        }, fileName);
-                        ctx.body = await fs.readFileSync(fileName);
-                        // 输出文件后删除
-                        fs.unlinkSync(fileName);
                     }
                 } catch (err) {
                     this.log(err);

+ 78 - 0
app/controller/drawing_controller.js

@@ -0,0 +1,78 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Mai
+ * @date
+ * @version
+ */
+const sendToWormhole = require('stream-wormhole');
+const path = require('path');
+
+module.exports = app => {
+    class DrawingController extends app.BaseController {
+
+        /**
+         * 图纸管理
+         *
+         * @param ctx
+         * @returns {Promise<void>}
+         */
+        async tender(ctx) {
+            try {
+                if (!ctx.subProject.page_show.drawing) throw '该功能已关闭或无法查看';
+
+                const renderData = {
+                    jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.drawing.tender),
+                };
+                renderData.tenderList = await ctx.service.tender.getSpecList(ctx.service.drawingPermission, ctx.session.sessionUser.is_admin ? 'all' : '');
+                renderData.categoryData = await this.ctx.service.category.getAllCategory(ctx.subProject);
+                renderData.selfCategoryLevel = this.ctx.subProject.permission.self_category_level;
+                await this.layout('drawing/tender.ejs', renderData, 'drawing/tender_modal.ejs');
+            } catch (err) {
+                ctx.log(err);
+                ctx.postError(err, '查询图纸管理数据错误');
+                ctx.redirect(this.menu.menu.dashboard.url);
+            }
+        }
+
+        async design() {
+
+        }
+
+        async built() {
+
+        }
+
+        async manageUpdate(ctx) {
+            try {
+                this.checkLock(ctx);
+                const data = JSON.parse(ctx.request.body.data);
+                if (!data.updateType) throw '数据错误';
+                let result;
+                const updateData = JSON.parse(JSON.stringify(data));
+                delete updateData.updateType;
+                if (data.updateType === 'add') {
+                    result = await ctx.service.filing.add(updateData);
+                } else if (data.updateType === 'del') {
+                    result = await ctx.service.filing.del(updateData);
+                } else if (data.updateType === 'save') {
+                    result = await ctx.service.filing.save(updateData);
+                } else if (data.updateType === 'move') {
+                    if (!data.id || !(data.tree_order >= 0)) throw '数据错误';
+                    result = await ctx.service.filing.move(updateData);
+                } else if (data.updateType === 'multi' ) {
+                    result = await ctx.service.filing.multiUpdate(ctx.subProject.id, data.data);
+                }
+                ctx.body = { err: 0, msg: '', data: result };
+            } catch (err) {
+                ctx.log(err);
+                ctx.ajaxErrorBody(err, '修改失败');
+            }
+        }
+    }
+
+    return DrawingController;
+};
+

+ 14 - 0
app/controller/ledger_controller.js

@@ -655,6 +655,20 @@ module.exports = app => {
                 ctx.body = { err: 1, msg: err.toString(), data: null };
             }
         }
+        async sgfh2deal(ctx) {
+            try {
+                if (!ctx.tender.data) throw '标段数据错误';
+                if (ctx.tender.data.user_id !== ctx.session.sessionUser.accountId || this._ledgerReadOnly()) throw '您无权进行该操作';
+                if (this.ctx.tender.measure_type === measureType.tz.value) throw '该功能仅工程量清单模式可用';
+
+                await this.ctx.service.ledger.sgfh2deal(ctx.tender.id);
+                const ledgerData = await ctx.service.ledger.getData(ctx.tender.id);
+                ctx.body = { err: 0, msg: '', data: { bills: ledgerData } };
+            } catch (err) {
+                this.log(err);
+                ctx.body = { err: 1, msg: err.toString(), data: null };
+            }
+        }
 
         /**
          * 下载(清单Excel模板 or 导出项目台账Excel)

+ 8 - 6
app/controller/material_controller.js

@@ -1317,10 +1317,12 @@ module.exports = app => {
                 if (ctx.material.curAuditorIds.indexOf(ctx.session.sessionUser.accountId) < 0) {
                     throw '您无权进行该操作';
                 }
-                const data = {
-                    checkType: parseInt(ctx.request.body.checkType),
-                    opinion: ctx.request.body.opinion,
-                };
+                const data = JSON.parse(ctx.request.body.data);
+                data.checkType = parseInt(data.checkType);
+                // const data = {
+                //     checkType: parseInt(ctx.request.body.checkType),
+                //     opinion: ctx.request.body.opinion,
+                // };
                 if (!data.checkType || isNaN(data.checkType)) {
                     throw '提交数据错误';
                 }
@@ -1332,11 +1334,11 @@ module.exports = app => {
 
                 await ctx.service.materialAudit.check(ctx.material.id, data, ctx.material.times);
 
-                ctx.redirect(ctx.request.header.referer);
+                ctx.body = { err: 0, msg: '', data: [] };
             } catch (err) {
                 this.log(err);
+                ctx.body = this.ajaxErrorBody(err, '提交失败');
                 ctx.session.postError = err.toString();
-                ctx.redirect(ctx.request.header.referer);
             }
         }
 

+ 12 - 0
app/controller/revise_controller.js

@@ -709,6 +709,18 @@ module.exports = app => {
                 ctx.body = { err: 1, msg: err.toString(), data: null };
             }
         }
+        async sgfh2deal(ctx) {
+            try {
+                if (this.ctx.tender.measure_type === measureType.tz.value) throw '该功能仅工程量清单模式可用';
+
+                await this.ctx.service.reviseBills.sgfh2deal(ctx.tender.id);
+                const ledgerData = await ctx.service.reviseBills.getData(ctx.tender.id);
+                ctx.body = { err: 0, msg: '', data: { bills: ledgerData } };
+            } catch (err) {
+                this.log(err);
+                ctx.body = { err: 1, msg: err.toString(), data: null };
+            }
+        }
 
         /**
          * 新增审批人(Ajax)

+ 36 - 10
app/controller/setting_controller.js

@@ -239,7 +239,7 @@ module.exports = app => {
                 // 获取数据规则
                 const page = ctx.page;
                 const pageSize = ctx.pageSize;
-                const columns = ['id', 'account', 'name', 'company', 'role', 'mobile', 'auth_mobile', 'telephone', 'enable', 'is_admin', 'bind', 'account_group', 'permission', 'cooperation', 'notice_again'];
+                const columns = ['id', 'account', 'name', 'company', 'company_id', 'role', 'mobile', 'auth_mobile', 'telephone', 'enable', 'is_admin', 'bind', 'account_group', 'permission', 'cooperation', 'notice_again', 'unit_sign_path'];
                 // 过滤数据
                 ctx.service.projectAccount.searchFilter(ctx.request.query, projectId, columns);
                 ctx.sort = ['id', 'desc'];
@@ -277,6 +277,7 @@ module.exports = app => {
                     company,
                     noticeAgainConst,
                     noticeSet,
+                    fujianOssPath: ctx.app.config.fujianOssPath,
                     jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.setting.user),
                     // rule: JSON.stringify(frontRule),
                 };
@@ -417,11 +418,26 @@ module.exports = app => {
                         break;
                     case 'del-sign':
                         const info = await ctx.service.constructionUnit.getDataById(data.id);
+                        let path = null;
                         if (info.sign_path) {
                             // 不删除地址,只删除数据库数据,防止已签章的报表丢失
                             // await ctx.app.fujianOss.delete(ctx.app.config.fujianOssFolder + info.sign_path);
+                            const paths = info.sign_path.split('&%&');
+                            const newPath = paths.filter(item => item !== data.path);
+                            path = newPath.length !== 0 ? newPath.join('&%&') : null;
                         }
-                        await ctx.service.constructionUnit.update({sign_path: null}, {id: info.id});
+                        await ctx.service.constructionUnit.update({ sign_path: path }, { id: info.id });
+                        // 用户已选章也要判断,存在也要移除
+                        const userList = await ctx.service.projectAccount.getAllDataByCondition({ where: { company: info.name, project_id: projectData.id } });
+                        if (userList.length > 0) {
+                            for (const user of userList) {
+                                if (user.unit_sign_path && user.unit_sign_path.includes(data.path)) {
+                                    const newSignPath = user.unit_sign_path.split('&%&').filter(item => item !== data.path).join('&%&');
+                                    await ctx.service.projectAccount.update({ unit_sign_path: newSignPath ? newSignPath : null }, { id: user.id });
+                                }
+                            }
+                        }
+                        responseData.data = path;
                         break;
                     case 'import-users':
                         responseData.data = await ctx.service.projectAccount.addUsers(projectData.id, data.users);
@@ -440,16 +456,26 @@ module.exports = app => {
             const responseData = {
                 err: 0, msg: '', data: null,
             };
+            let stream;
             try {
-                const stream = await ctx.getFileStream();
-                const create_time = Date.parse(new Date()) / 1000;
-                const fileInfo = path.parse(stream.filename);
-                const filepath = `app/public/upload/sign/unit/qianzhang_${create_time + fileInfo.ext}`;
-                await ctx.app.fujianOss.put(ctx.app.config.fujianOssFolder + filepath, stream);
-                await sendToWormhole(stream);
-                const result = await ctx.service.constructionUnit.update({sign_path: filepath}, {id: stream.fields.id});
+                const parts = ctx.multipart({ autoFields: true });
+                const paths = [];
+                let index = 0;
+                while ((stream = await parts()) !== undefined) {
+                    const create_time = Date.parse(new Date()) / 1000;
+                    const fileInfo = path.parse(stream.filename);
+                    const filepath = `app/public/upload/sign/unit/qianzhang_${create_time + index.toString() + fileInfo.ext}`;
+                    await ctx.app.fujianOss.put(ctx.app.config.fujianOssFolder + filepath, stream);
+                    await sendToWormhole(stream);
+                    paths.push(filepath);
+                    ++index;
+                }
+                const unit = await ctx.service.constructionUnit.getDataById(parts.field.id);
+                const sign_path = unit.sign_path ? unit.sign_path.split('&%&') : [];
+                sign_path.push(...paths);
+                const result = await ctx.service.constructionUnit.update({ sign_path: sign_path.join('&%&') }, { id: unit.id });
                 if (result) {
-                    responseData.data = {sign_path: filepath};
+                    responseData.data = { sign_path: sign_path.join('&%&') };
                 } else {
                     throw '添加数据库失败';
                 }

+ 2 - 2
app/controller/settle_controller.js

@@ -624,8 +624,8 @@ module.exports = app => {
         async loadGatherData(ctx) {
             try {
                 const settle = await this.ctx.service.settle.getLatestCompleteSettle(ctx.tender.id);
-                const bills = await this.ctx.service.settleBills.getAllDataByCondition({ where: { settle_id: settle.id }});
-                const pos = await this.ctx.service.settlePos.getAllDataByCondition({ where: { settle_id: settle.id }});
+                const bills = settle ? await this.ctx.service.settleBills.getAllDataByCondition({ where: { settle_id: settle.id }}) : [];
+                const pos = settle ? await this.ctx.service.settlePos.getAllDataByCondition({ where: { settle_id: settle.id }}) : [];
                 ctx.body = { err: 0, msg: '', data: { bills, pos } };
             } catch(err) {
                 ctx.log(err);

+ 1 - 1
app/controller/sub_proj_setting_controller.js

@@ -572,7 +572,7 @@ module.exports = app => {
                     categoryData,
                     settingConst,
                     measureType: tenderConst.measureType,
-                    jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.setting.manage),
+                    jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.setting.sp_manage),
                     uid: this.ctx.session.sessionUser.accountId,
                     pid: this.ctx.session.sessionProject.id,
                     spid: ctx.subProject.id,

+ 84 - 0
app/controller/wap_controller.js

@@ -10,8 +10,10 @@
 const URL = require('url');
 const maintainConst = require('../const/maintain');
 const auditConst = require('../const/audit');
+const tenderConst = require('../const/tender');
 const changeConst = require('../const/change');
 const advanceConst = require('../const/advance');
+const materialConst = require('../const/material');
 const fs = require('fs');
 const path = require('path');
 const sendToWormhole = require('stream-wormhole');
@@ -221,12 +223,33 @@ module.exports = app => {
             }
             // 获取待审批的预付款
             const auditAdvance = await ctx.service.advanceAudit.getAuditAdvanceByWap(ctx.session.sessionUser.accountId);
+            const materialList = await ctx.service.materialAudit.getAuditMaterialByWap(ctx.session.sessionUser.accountId);
+            const auditMaterials = [];
+            for (const audit of materialList) {
+                audit.decimal = audit.decimal ? JSON.parse(audit.decimal) : materialConst.decimal;
+                let sp = null;
+                if (audit.spid) {
+                    if (ctx.helper._.findIndex(subProjects, { id: audit.spid }) !== -1) {
+                        sp = ctx.helper._.find(subProjects, { id: audit.spid });
+                    } else {
+                        sp = await ctx.service.subProject.getDataById(audit.spid);
+                        subProjects.push(sp);
+                    }
+                }
+                if (sp) {
+                    const pageShow = JSON.parse(sp.page_show);
+                    if (pageShow.openMaterial) {
+                        auditMaterials.push(audit);
+                    }
+                }
+            }
             const renderData = {
                 auditStages,
                 auditChanges,
                 auditChangeProjects,
                 auditChangeApplys,
                 auditChangePlans,
+                auditMaterials,
                 auditRevise,
                 auditAdvance,
                 changeConst,
@@ -362,13 +385,43 @@ module.exports = app => {
                     advanceList.push(advance);
                 }
 
+                // 材料调差
+                const materials = await ctx.service.material.getValidMaterials(ctx.tender.id);
+                let openMaterialTax = ctx.subProject.page_show.openMaterialTax;
+                const tenderMsg = await ctx.service.tender.getTender(ctx.tender.id, ['material_col_show']);
+                const material_col_show = tenderMsg.material_col_show ? JSON.parse(tenderMsg.material_col_show) : ctx.helper._.cloneDeep(tenderConst.materialColShow);
+                let allMaterialTax = true;
+                for (const s of materials) {
+                    if (!s.final_auditor_str || s.status !== auditConst.material.status.checked) {
+                        // 根据期状态返回展示用户
+                        s.curAuditors = await ctx.service.materialAudit.getAuditorsByStatus(s.id, s.status, s.times);
+                        if (s.status === auditConst.material.status.checked && s.curAuditors.length > 0) {
+                            const final_auditor_str = (s.curAuditors[0].audit_type === auditType.key.common)
+                                ? `${s.curAuditors[0].name}${(s.curAuditors[0].role ? '-' + s.curAuditors[0].role : '')}`
+                                : ctx.helper.transFormToChinese(s.curAuditors[0].audit_order) + '审';
+                            await ctx.service.material.defaultUpdate({ id: s.id, final_auditor_str });
+                        }
+                    }
+                    if (allMaterialTax && s.material_tax === 0) {
+                        allMaterialTax = false;
+                    }
+                    if (!openMaterialTax && s.material_tax === 1) {
+                        openMaterialTax = 1;
+                    }
+                    s.decimal = s.decimal ? JSON.parse(s.decimal) : materialConst.decimal;
+                }
                 const renderData = {
                     tender,
                     stages,
                     revises,
                     advanceList,
+                    materials,
+                    openMaterialTax,
+                    allMaterialTax,
+                    materialColShow: material_col_show,
                     auditConst: auditConst.stage,
                     auditReviseConst: auditConst.revise,
+                    auditMaterialConst: auditConst.material,
                     advanceConst,
                     tpUnit: ctx.tender.info.decimal.tp,
                     monthProgress,
@@ -441,6 +494,37 @@ module.exports = app => {
         }
 
         /**
+         * 调差期审批详细页
+         *
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        async material(ctx) {
+            try {
+                const tender = ctx.tender.data;
+                const material = ctx.material;
+                await ctx.service.material.loadMaterialAuditViewData(material);
+                const renderData = {
+                    moment,
+                    tender,
+                    material,
+                    auditConst: auditConst.material,
+                    auditType,
+                };
+                renderData.material.m_tax_tp = renderData.material.m_tax_tp ? renderData.material.m_tax_tp : renderData.material.m_tp;
+                renderData.pre_tp_hs = await ctx.service.material.getPreTpHs(ctx.tender.id, ctx.material.order, ctx.material.decimal.tp);
+                renderData.ex_pre_tp_hs = await ctx.service.material.getExPreTpHs(ctx.tender.id, ctx.material.order, ctx.material.decimal.tp);
+                renderData.months = ctx.material.months ? ctx.material.months.split(',') : [];
+                renderData.material.lastAuditors = await ctx.service.materialAudit.getAuditorsByStatus(material.id, material.status, material.times);
+                renderData.old_had_tax = await ctx.service.material.getOldMaterialTax(ctx.tender.id, ctx.material.order);
+                await ctx.render('wap/shenpi_material.ejs', renderData);
+            } catch (err) {
+                this.log(err);
+                ctx.redirect('/wap/sp/' + ctx.subProject.id + '/list');
+            }
+        }
+
+        /**
          * 工程变更列表页
          *
          * @param {Object} ctx - egg全局变量

+ 12 - 0
app/lib/rm/tender.js

@@ -16,6 +16,7 @@ const bindData = {
 };
 const BudgetSource = require('./tender_budget');
 const MaterialSource = require('./tender_material');
+const FinancialSource = require('./tender_financial');
 const rptCustomData = require('../rptCustomData');
 
 class rptMemPaymentSafe extends RptMemBase {
@@ -40,6 +41,7 @@ class rptMemPaymentSafe extends RptMemBase {
     getCommonData(params, tableName, fields, customDefine, customSelect) {
         const service = this.ctx.service;
         const budgetSource = new BudgetSource(this.ctx);
+        const financialSource = new FinancialSource(this.ctx);
         switch (tableName) {
             case 'project' :
                 return service.project.getProjectById(params.project_id);
@@ -198,6 +200,16 @@ class rptMemPaymentSafe extends RptMemBase {
                 return params.budget_id ? budgetSource.budgetFinal(params.budget_id, true) : budgetSource.tenderFinal(params.tender_id, true);
             case 'mem_pm_deal_pay':
                 return service.reportMemory.getPmDeal();
+            case 'mem_financial_pay':
+                return financialSource.pay(params.tender_id);
+            case 'mem_financial_pay_contract':
+                return financialSource.payContract(params.tender_id);
+            case 'mem_financial_pay_tender':
+                return financialSource.payTender(params.tender_id);
+            case 'mem_financial_transfer':
+                return financialSource.transfer(params.tender_id);
+            case 'mem_financial_transfer_tender':
+                return financialSource.transferTender(params.tender_id);
             default:
                 break;
         }

+ 8 - 5
app/lib/rm/tender_budget.js

@@ -64,11 +64,14 @@ class reportMemoryBudget {
     async _getTenderBudget(tid) {
         if (this.getBudget) return;
 
-        const budgets = await this.ctx.service.budget.getBudget(true);
-        this.budget = budgets.find(x => {
-            const relaTender = x.rela_tender.split(',');
-            return relaTender.indexOf(tid + '') >= 0;
-        });
+        if (this.ctx.subProject) this.budget = await this.ctx.service.budget.getDataById(this.ctx.subProject.id);
+        if (!this.budget) {
+            const budgets = await this.ctx.service.budget.getBudget(true);
+            this.budget = budgets.find(x => {
+                const relaTender = x.rela_tender.split(',');
+                return relaTender.indexOf(tid + '') >= 0;
+            });
+        }
         this.getBudget = true;
     }
 

+ 75 - 0
app/lib/rm/tender_financial.js

@@ -0,0 +1,75 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Mai
+ * @date 20250613
+ * @version
+ */
+
+class reportMemoryFinancial {
+    constructor(ctx) {
+        this.ctx = ctx;
+    }
+
+    async checkTender(tid) {
+        this.tender = this.ctx.tender ? this.ctx.tender : await this.ctx.service.tender.getDataById(tid);
+    }
+
+    async pay(tid) {
+        await this.checkTender(tid);
+        if (!this.tender) return [];
+        return await this.ctx.service.financialPay.getAllDataByCondition({ where: { tid }});
+    }
+
+    async payContract(tid) {
+        await this.checkTender(tid);
+        if (!this.tender) return [];
+        return await this.ctx.service.financialPayContract.getAllDataByCondition({ where: { tid } });
+    }
+
+    async payTender(tid) {
+        await this.checkTender(tid);
+        if (!this.tender) return [];
+        return await this.ctx.service.financialPayTender.getAllDataByCondition({ where: { tid } });
+    }
+
+    async transferTender(tid) {
+        await this.checkTender(tid);
+        if (!this.tender) return [];
+        return await this.ctx.service.financialTransferTender.getAllDataByCondition({ where: { tid } });
+    }
+
+    async projectPay(tid) {
+        await this.checkTender(tid);
+        if (!this.tender || !this.tender.spid) return [];
+        return await this.ctx.service.financialPay.getAllDataByCondition({ where: { spid: this.tender.spid }});
+    }
+
+    async projectPayContract(tid) {
+        await this.checkTender(tid);
+        if (!this.tender || !this.tender.spid) return [];
+        return await this.ctx.service.financialPayContract.getAllDataByCondition({ where: { spid: this.tender.spid } });
+    }
+
+    async projectPayTender(tid) {
+        await this.checkTender(tid);
+        if (!this.tender || !this.tender.spid) return [];
+        return await this.ctx.service.financialPayTender.getAllDataByCondition({ where: { spid: this.tender.spid } });
+    }
+
+    async projectTransfer(tid) {
+        await this.checkTender(tid);
+        if (!this.tender || !this.tender.spid) return [];
+        return await this.ctx.service.financialTransfer.getAllDataByCondition({ where: { spid: this.tender.spid } });
+    }
+
+    async projectTransferTender(tid) {
+        await this.checkTender(tid);
+        if (!this.tender || !this.tender.spid) return [];
+        return await this.ctx.service.financialTransferTender.getAllDataByCondition({ where: { spid: this.tender.spid } });
+    }
+}
+
+module.exports = reportMemoryFinancial;

+ 2 - 2
app/lib/wechat.js

@@ -141,8 +141,8 @@ class WX {
                         break;
                     case wxConst.template.material:
                         templateId = wxConst.templateId.material;
-                        // url = origin_url + '/wx/url2wap?project=' + data.code + '&url=' + sck + data.wap_url;
-                        remark = data.status === wxConst.status.check ? '微信暂无法在线审批' :
+                        url = origin_url + '/wx/url2wap?project=' + data.code + '&url=' + sck + data.wap_url;
+                        remark = data.status === wxConst.status.check ? '微信可快速审批,如需进行详细审批' :
                             (data.status === wxConst.status.success ? '审批已通过,查看审批结果' : '审批被退回,查看退回结果');
                         msgData = {
                             first: {

+ 4 - 0
app/middleware/sub_project_check.js

@@ -92,6 +92,10 @@ module.exports = options => {
             } else {
                 this.redirect(`/sp/${this.subProject.id}/nop/${this.controllerName}`);
             }
+
+            // 更新用户最近使用项目时间
+            const auditInfo = yield this.service.subProjPermission.getDataByCondition({ spid: this.subProject.id, uid: accountInfo.id });
+            if (auditInfo) yield this.service.subProjPermission.defaultUpdate({ id: auditInfo.id, last_time: new Date() });
         } catch (err) {
             this.log(err);
             if (this.helper.isAjax(this.request)) {

+ 67 - 1
app/public/css/wap/main.css

@@ -25,4 +25,70 @@ input.form-control[readonly],textarea.form-control[readonly] {
 	border: none;
 	background: #f1f1f1;
 
-}
+}
+.bg-new-advance{
+    background: rgba(241, 82, 91, 0.08) !important;
+}
+.bg-new-ledger{
+    background: rgba(250, 140, 22, 0.08) !important;
+}
+.bg-new-revise{
+    background: rgba(251, 182, 45, 0.08) !important;
+}
+.bg-new-stage{
+    background: rgba(82, 196, 26, 0.08) !important;
+}
+.bg-new-changeProject{
+    background: rgba(51, 119, 255, 0.08) !important;
+}
+.bg-new-changePlan{
+    background: rgba(114, 46, 209, 0.08) !important;
+}
+.bg-new-change{
+    background: rgba(22, 208, 208, 0.08) !important;
+}
+.bg-new-changeApply{
+    background: rgba(41, 58, 210, 0.08) !important;
+}
+.bg-new-material{
+    background: rgba(187, 41, 210, 0.08) !important;
+}
+.bg-new-payment{
+    background: rgba(128, 128, 0, 0.08) !important;
+}
+.bg-new-financial{
+    background: rgba(58, 88, 50, 0.08) !important;
+}
+.text-new-advance{
+    color: rgba(241, 82, 91, 1) !important;
+}
+.text-new-ledger{
+    color: rgba(250, 140, 22, 1) !important;
+}
+.text-new-revise{
+    color: rgba(251, 182, 45, 1) !important;
+}
+.text-new-stage{
+    color: rgba(82, 196, 26, 1) !important;
+}
+.text-new-changeProject{
+    color: rgba(51, 119, 255, 1) !important;
+}
+.text-new-changePlan{
+    color: rgba(114, 46, 209, 1) !important;
+}
+.text-new-change{
+    color: rgba(22, 208, 208, 1) !important;
+}
+.text-new-changeApply{
+    color: rgba(41, 58, 210, 1) !important;
+}
+.text-new-material{
+    color: rgba(187, 41, 210, 1); !important;
+}
+.text-new-payment{
+    color: rgba(128, 128, 0, 1) !important;
+}
+.text-new-financial{
+    color: rgba(58, 88, 50, 1) !important;
+}

+ 11 - 2
app/public/js/bootstrap/tree-select.js

@@ -32,8 +32,8 @@
 .tree-node:not(.leaf) [data-level="0"] >.tree-label {
   font-weight: bold;
 }
-.tree-node[data-level="0"]::before,
-.tree-node[data-level="0"]::after {
+.tree-node:not(.leaf)[data-level="0"]::before,
+.tree-node:not(.leaf)[data-level="0"]::after {
   display: none !important;
 }
 .tree-node::after {
@@ -45,6 +45,15 @@
   height: 1px;
   background-color: #ccc;
 }
+.tree-node:is(.leaf)[data-level="0"]::before {
+  left: 14px;
+}
+.tree-node:is(.leaf)[data-level="0"]::after {
+  left: 14px;
+}
+.tree-node:is(.leaf)[data-level="0"] {
+  padding-left: 22px;
+}
 .tree-node:last-child::before {
   height: 14px;
 }

+ 2 - 2
app/public/js/change.js

@@ -379,10 +379,10 @@ $(document).ready(() => {
             }, 2000);
             return;
         }
-        if (!openChangePlan && $('#bj-name').val().length > 100) {
+        if (!openChangePlan && $('#bj-name').val().length > 500) {
             $('#bj-name').addClass('is-invalid');
             $('#name_error_msg').show();
-            $('#name_error_msg').text('名称超过100个字,请缩减名称。');
+            $('#name_error_msg').text('名称超过500个字,请缩减名称。');
             $(this).attr('disabled', false);
             setTimeout(function () {
                 $('#bj-name').removeClass('is-invalid');

+ 34 - 3
app/public/js/change_revise.js

@@ -3917,10 +3917,40 @@ $(document).ready(() => {
                         return;
                     }
                     const is_bill = dealBills.kind === 4;
+                    // 判断dealBills.code以-开头,是则向上判断是否需要拼接code名称
+                    function makeCode(dealBills) {
+                        const originalCode = dealBills.code;
+                        if (!originalCode || originalCode.charAt(0) !== '-') {
+                            return originalCode;
+                        }
+                        const parents = dealSheet.zh_tree.getAllParents(dealBills);
+                        if (!parents || parents.length === 0) {
+                            return originalCode;
+                        }
+                        const kind4Parents = _.filter(parents, { kind: 4 });
+                        if (!kind4Parents || kind4Parents.length === 0) {
+                            return originalCode;
+                        }
+                        const firstParentCode = kind4Parents[0].code;
+                        if (firstParentCode.charAt(0) === '-') {
+                            return originalCode;
+                        }
+                        const codeParts = [];
+                        for (const parent of kind4Parents.reverse()) {
+                            codeParts.push(parent.code);
+                            if (parent.code.charAt(0) !== '-') {
+                                break;
+                            }
+                        }
+                        if (codeParts.length > 0) {
+                            return codeParts.reverse().join('') + originalCode;
+                        }
+                        return originalCode;
+                    }
                     const oneBills = {
-                        b_code: is_bill ? dealBills.code : null, code: !is_bill ? dealBills.code : null, name: dealBills.name, unit: dealBills.unit,
-                        unit_price: is_bill ? dealBills.unitPrice : null, quantity: is_bill ? dealBills.quantity : null, total_price: is_bill ? dealBills.totalPrice : null,
-                        sgfh_qty: is_bill ? dealBills.quantity : null, sgfh_tp: is_bill ? dealBills.totalPrice : null,
+                        b_code: is_bill ? makeCode(dealBills) : null, code: !is_bill ? dealBills.code : null, name: dealBills.name, unit: dealBills.unit,
+                        unit_price: is_bill ? dealBills.unitPrice : null, quantity: null, total_price: null,
+                        sgfh_qty: null, sgfh_tp: null,
                     };
                     postData(window.location.pathname + '/update', {
                         postType: 'add-deal',
@@ -4058,6 +4088,7 @@ $(document).ready(() => {
         }
         postData('/profile/dsk/api', { type: 'project_bills', tid: window.location.pathname.split('/')[2], compilationId: projectInfo.compilationId, treeId }, function (result) {
             const tree = dskProjectBills2Tree.convert(result);
+            console.log(tree);
             SpreadJsObj.loadSheetData(dskBills.spread.getActiveSheet(), SpreadJsObj.DataType.Tree, tree);
         });
     }

+ 2 - 2
app/public/js/contract_detail.js

@@ -1671,7 +1671,7 @@ $(document).ready(function() {
                     contractTreeAudits = result;
                     sqTreeSpreadObj.setTreeAudits(sqSheet);
                 });
-            }, '确认移除「当前节点已勾选授权用户」?');
+            }, '确认移除「当前节点已勾选授权用户」?', false);
         });
 
         $('body').on('click', '#empower .del-tree-audit', function () {
@@ -1688,7 +1688,7 @@ $(document).ready(function() {
                     contractTreeAudits = result;
                     sqTreeSpreadObj.setTreeAudits(sqSheet);
                 });
-            }, '确认移除「当前节点授权用户」?');
+            }, '确认移除「当前节点授权用户」?', false);
         });
     }
 

+ 16 - 8
app/public/js/contract_panel.js

@@ -72,6 +72,10 @@ $(document).ready(function() {
     console.log(expensesBarDatas);
 
     const expensesBarChart = echarts.init(document.getElementById('expensesBarChart'));
+    // 判断是万元还是元
+    const yfPrices =  _.map(expensesBarDatas, 'yf_price');
+    const useWang = yfPrices.some(v => v >= 10000000);
+    const displayData = useWang ? yfPrices.map(v => v / 10000) : yfPrices;
     const expensesBarOption = {
         color: ['#b5c4b1','#965454','#9ca8b8','#d8caaf'],
         title : {
@@ -82,7 +86,7 @@ $(document).ready(function() {
         },
         calculable : true,
         legend: {
-            data:['支付金额','占总金额比例']
+            data:[`支付金额${useWang ? '(万元)' : '(元)'}`,'占总金额比例']
         },
         dataZoom: [
             {show: true,start: 0, end: 100}
@@ -100,7 +104,7 @@ $(document).ready(function() {
                 name : '金额',
                 position:'left',
                 axisLabel : {
-                    formatter: '{value} 元'
+                    formatter: value => useWang ? `${value}万元` : `${value}元`
                 },
                 splitArea : {show : true}
             },
@@ -116,11 +120,11 @@ $(document).ready(function() {
         ],
         series : [
             {
-                name:'支付金额',
+                name:`支付金额${useWang ? '(万元)' : '(元)'}`,
                 type:'bar',
                 tooltip : {formatter: "{b}<br/>{a}:{c} %"},
                 stack: '结算金额',
-                data: _.map(expensesBarDatas, 'yf_price'),
+                data: displayData,
             },
             {
                 name:'占总金额比例',
@@ -205,6 +209,10 @@ $(document).ready(function() {
     console.log(incomeBarDatas);
 
     const incomeBarChart = echarts.init(document.getElementById('incomeBarChart'));
+    // 判断是万元还是元
+    const incomeYfPrices =  _.map(incomeBarDatas, 'yf_price');
+    const incomeUseWang = incomeYfPrices.some(v => v >= 10000000);
+    const incomeDisplayData = incomeUseWang ? incomeYfPrices.map(v => v / 10000) : incomeYfPrices;
     const incomeBarOption = {
         color: ['#a27e7e','#656565','#b5c4b1','#d8caaf'],
         title : {
@@ -215,7 +223,7 @@ $(document).ready(function() {
         },
         calculable : true,
         legend: {
-            data:['回款金额','占总金额比例']
+            data:[`回款金额${incomeUseWang ? '(万元)' : '(元)'}`,'占总金额比例']
         },
         dataZoom: [
             {show: true,start: 0, end: 100}
@@ -233,7 +241,7 @@ $(document).ready(function() {
                 name : '金额',
                 position:'left',
                 axisLabel : {
-                    formatter: '{value} 元'
+                    formatter:  value => incomeUseWang ? `${value}万元` : `${value}元`
                 },
                 splitArea : {show : true}
             },
@@ -249,11 +257,11 @@ $(document).ready(function() {
         ],
         series : [
             {
-                name:'回款金额',
+                name:`回款金额${incomeUseWang ? '(万元)' : '(元)'}`,
                 type:'bar',
                 tooltip : {formatter: "{b}<br/>{a}:{c} %"},
                 stack: '回款金额',
-                data: _.map(incomeBarDatas, 'yf_price'),
+                data: incomeDisplayData,
             },
             {
                 name:'占总金额比例',

+ 49 - 0
app/public/js/drawing_tender.js

@@ -0,0 +1,49 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Mai
+ * @date
+ * @version
+ */
+const tenderListSpec = (function(){
+    function getTenderNodeHtml(node, arr, pid) {
+        const html = [];
+        html.push('<tr pid="' + pid + '">');
+        // 名称
+        html.push('<td style="width: 45%" class="in-' + node.level + '">');
+        if (node.cid) {
+            html.push('<span onselectstart="return false" style="{-moz-user-select:none}" class="fold-switch mr-1" title="收起" cid="'+ node.sort_id +'"><i class="fa fa-minus-square-o"></i></span> <i class="fa fa-folder-o"></i> ');
+            html.push((node.level === 1 ? '<b>' : ''), node.name, (node.level === 1 ? '</b>' : ''));
+        } else {
+            html.push('<span class="text-muted mr-2">');
+            html.push(arr.indexOf(node) === arr.length - 1 ? '└' : '├');
+            html.push('</span>');
+            html.push('<a href="javascript: void(0)" name="name" id="' + node.id + '">', node.name, '</a>');
+        }
+        html.push('</td>');
+        // 创建时间
+        html.push('<td style="width: 15%">', node.create_time ? moment(node.create_time).format('YYYY-MM-DD HH:mm:ss') : '', '</td>');
+        // 管理
+        html.push('<td style="width: 20%" tid="' + node.id + '">');
+        if (!node.cid) {
+            html.push('<a href="javascript: void(0)" name="edit" class="btn btn-outline-primary btn-sm">成员管理</a>');
+        }
+        html.push('</td>');
+        html.push('</tr>');
+        return html.join('');
+    }
+    function getTenderTreeHeaderHtml() {
+        const html = [];
+        const left = $('#sub-menu').css('display') === 'none' ? 56 : 176;
+        html.push('<table class="table table-hover table-bordered">');
+        html.push('<thead style="position: sticky;left:'+ left +'px;top: 0;">', '<tr>');
+        html.push('<th class="text-center" style="width: 45%">', '标段名称', tenderListOrder.getOrderButton('name'), '</th>');
+        html.push('<th class="text-center" style="width: 15%">', '创建时间', tenderListOrder.getOrderButton('create_time'), '</th>');
+        html.push('<th class="text-center" style="width: 15%">', '操作', '</th>');
+        html.push('</tr>', '</thead>');
+        return html.join('');
+    }
+    return { getTenderNodeHtml, getTenderTreeHeaderHtml }
+})();

+ 11 - 3
app/public/js/file_detail.js

@@ -556,6 +556,7 @@ $(document).ready(function() {
                 filingObj.filingTree.updateNode(node);
             },
             beforeRemove: function(key, node, isCancel) {
+                filingObj.selectRemoveNode = node;
                 $('#del-filing').modal('show');
                 return false;
             },
@@ -653,7 +654,7 @@ $(document).ready(function() {
     //     $('#del-filing').modal('show');
     // });
     $('#del-filing-ok').click(() => {
-        filingObj.delFiling(filingObj.curFiling, function() {
+        filingObj.delFiling(filingObj.selectRemoveNode, function() {
             $('#del-filing').modal('hide');
         });
     });
@@ -1232,6 +1233,7 @@ $(document).ready(function() {
                 cols: [
                     { title: '名称', colSpan: '1', rowSpan: '1', field: 'name', hAlign: 0, width: 200, formatter: '@', cellType: 'tree'},
                     { title: '选择', colSpan: '1', rowSpan: '1', field: 'check', hAlign: 1, width: 45, cellType: 'checkbox' },
+                    { title: '归档单位', colSpan: '1', rowSpan: '1', field: 'file_company', hAlign: 0, width: 80, formatter: '@' },
                 ],
                 emptyRows: 0,
                 headRows: 1,
@@ -1283,8 +1285,13 @@ $(document).ready(function() {
                 SpreadJsObj.reLoadRowsData(info.sheet, row);
             });
             this.spread.bind(spreadNS.Events.SelectionChanged, function (e, info) {
-                const node = SpreadJsObj.getSelectObject(info.sheet);
-                self.setCurFiling(node.filing_type);
+                if (info.newSelections[0].col === 1) {
+                    info.sheet.setSelection(info.oldSelections[0].row, info.oldSelections[0].col, info.oldSelections[0].rowCount, info.oldSelections[0].colCount);
+                    SpreadJsObj.reloadRowsBackColor(info.sheet, [info.newSelections[0].row, info.oldSelections[0].row]);
+                } else {
+                    const node = SpreadJsObj.getSelectObject(info.sheet);
+                    self.setCurFiling(node.filing_type);
+                }
             });
 
             $(setting.modal).on('show.bs.modal', () => {
@@ -1472,6 +1479,7 @@ $(document).ready(function() {
                     is_fixed: node.is_fixed,
                     filing_type: node.filing_type + '',
                     tips: node.tips,
+                    file_company: node.file_company,
                     file_count: node.file_count,
                 });
             }

+ 3 - 1
app/public/js/filing_manage.js

@@ -269,6 +269,7 @@ $(document).ready(function() {
                     { title: '固定', colSpan: '1', rowSpan: '1', field: 'is_fixed', hAlign: 1, width: 50, cellType: 'checkbox' },
                     { title: '类型', colSpan: '1', rowSpan: '1', field: 'filing_type', hAlign: 1, width: 50, readOnly: true, visible: is_debug, },
                     { title: '新类型', colSpan: '1', rowSpan: '1', field: 'new_filing_type', hAlign: 1, width: 50, readOnly: true, visible: is_debug, },
+                    { title: '归档单位', colSpan: '1', rowSpan: '1', field: 'file_company', hAlign: 0, width: 80, formatter: '@' },
                     { title: '提示', colSpan: '1', rowSpan: '1', field: 'tips', hAlign: 0, width: 280, formatter: '@', wordWrap: 1 },
                 ],
                 emptyRows: 0,
@@ -398,7 +399,7 @@ $(document).ready(function() {
             const data = [];
             const getUpdateData = function(children) {
                 for (const [i, node] of children.entries()) {
-                    data.push({ id: node.id, is_fixed: node.is_fixed, filing_type: node.new_filing_type, tree_order: i + 1, tips: node.tips || '' });
+                    data.push({ id: node.id, is_fixed: node.is_fixed, filing_type: node.new_filing_type, tree_order: i + 1, tips: node.tips || '', file_company: node.file_company || '' });
                     // data.push({ id: node.id, is_fixed: node.is_fixed, filing_type: node.filing_type, file_count: node.file_count, new_filing_type: node.new_filing_type, tree_order: i + 1, tips: node.tips || '' });
                     if (node.children) getUpdateData(node.children);
                 }
@@ -423,6 +424,7 @@ $(document).ready(function() {
                     org_is_fixed: node.is_fixed,
                     is_fixed: node.is_fixed,
                     filing_type: node.filing_type,
+                    file_company: node.file_company,
                     tips: node.tips,
                     file_count: node.file_count,
                     permission_count: node.permission_count,

+ 4 - 1
app/public/js/filing_template.js

@@ -289,6 +289,7 @@ $(document).ready(function() {
                 cols: [
                     { title: '名称', colSpan: '1', rowSpan: '1', field: 'name', hAlign: 0, width: 250, formatter: '@', readOnly: true, cellType: 'tree' },
                     { title: '固定', colSpan: '1', rowSpan: '1', field: 'is_fixed', hAlign: 1, width: 50, cellType: 'checkbox' },
+                    { title: '归档单位', colSpan: '1', rowSpan: '1', field: 'file_company', hAlign: 0, width: 80, formatter: '@' },
                     { title: '提示', colSpan: '1', rowSpan: '1', field: 'tips', hAlign: 0, width: 280, formatter: '@', wordWrap: 1 },
                 ],
                 emptyRows: 0,
@@ -387,7 +388,7 @@ $(document).ready(function() {
                         if (!fixedParent) throw `【${node.name}】查询不到固定信息`;
                         filing_type = fixedParent.source_filing_type;
                     }
-                    data.push({ id: node.id, is_fixed: node.is_fixed, filing_type, tree_order: i + 1, tips: node.tips || '' });
+                    data.push({ id: node.id, is_fixed: node.is_fixed, filing_type, tree_order: i + 1, tips: node.tips || '', file_company: node.file_company || '' });
                     if (node.children) getUpdateData(node.children);
                 }
             };
@@ -410,6 +411,7 @@ $(document).ready(function() {
                     name: node.name,
                     is_fixed: node.is_fixed,
                     filing_type: node.filing_type,
+                    file_company: node.file_company,
                     tips: node.tips,
                 });
             }
@@ -431,6 +433,7 @@ $(document).ready(function() {
                 name: node.name,
                 is_fixed: node.is_fixed,
                 filing_type: node.filing_type,
+                file_company: node.file_company,
                 tips: node.tips,
             }});
             const blob = new Blob([JSON.stringify(exportData, '', '')], { type: 'application/text'});

+ 13 - 0
app/public/js/ledger.js

@@ -61,6 +61,8 @@ const checkOption = {
 };
 
 $(document).ready(function() {
+    const unit = ledgerSpreadSetting.cols.find(x => { return x.field === 'unit'; });
+    if (unit) unit.comboEdit = true;
     let stdXmj, stdGcl, dealBills, searchLedger, featureDisplay;
     autoFlashHeight();
     const changeFeaturesDisplay = function (show, init = false) {
@@ -1651,6 +1653,17 @@ $(document).ready(function() {
                     });
                 }
             };
+            billsContextMenuOptions.items.applySgfh2Deal = {
+                name: '填签约量',
+                icon: 'fa-pencil',
+                callback: function (key, opt) {
+                    postData(window.location.pathname + '/sgfh2deal', null, function (result) {
+                        ledgerTree.loadDatas(result.bills);
+                        treeCalc.calculateAll(ledgerTree);
+                        SpreadJsObj.loadSheetData(ledgerSpread.getActiveSheet(), 'tree', ledgerTree);
+                    });
+                }
+            };
         }
         billsContextMenuOptions.items.sprSort = '----';
     }

+ 11 - 1
app/public/js/login.js

@@ -6,7 +6,11 @@ $(document).ready(function() {
         $('#project').val(lSPCode);
         $('#forget-project').val(lSPCode);
         $('#account').focus();
-
+        if (['Y3T2Q','P0505'].indexOf(lSPCode.toUpperCase()) !== -1) {
+            $('.login-tip').text('当前处于办公网络非涉密网络,严禁传输处理涉密信息!');
+        } else {
+            $('.login-tip').text('');
+        }
         $.ajax({
             type: 'get',
             url: '/project/name',
@@ -46,10 +50,16 @@ $(document).ready(function() {
             $('#forget-project').val('');
             removeLocalCache('project_code');
             removeLocalCache('project_name');
+            $('.login-tip').text('');
         } else {
             const pcode = getLocalCache('project_code');
             if ($(this).val() !== pcode) {
                 const pc = $(this).val();
+                if (['Y3T2Q','P0505'].indexOf(pc.toUpperCase()) !== -1) {
+                    $('.login-tip').text('当前处于办公网络非涉密网络,严禁传输处理涉密信息!');
+                } else {
+                    $('.login-tip').text('');
+                }
                 $.ajax({
                     type: 'get',
                     url: '/project/name',

+ 13 - 0
app/public/js/revise.js

@@ -53,6 +53,8 @@ const checkOption = {
 };
 
 $(document).ready(() => {
+    const unit = billsSpreadSetting.cols.find(x => { return x.field === 'unit'; });
+    if (unit) unit.comboEdit = true;
     let stdXmj, stdGcl, searchLedger, reviseAtt;
     autoFlashHeight();
     // 初始化spread
@@ -1442,6 +1444,17 @@ $(document).ready(() => {
                     });
                 }
             };
+            billsContextMenuOptions.items.applySgfh2Deal = {
+                name: '填签约量',
+                icon: 'fa-pencil',
+                callback: function (key, opt) {
+                    postData(window.location.pathname + '/sgfh2deal', null, function (result) {
+                        billsTree.loadDatas(result.bills);
+                        treeCalc.calculateAll(billsTree);
+                        SpreadJsObj.loadSheetData(billsSheet, 'tree', billsTree);
+                    });
+                }
+            };
             billsContextMenuOptions.items.sprSort = '----';
         }
     }

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

@@ -109,7 +109,7 @@ $(document).ready(() => {
                 for (const p of posRange) {
                     let nP = _.find(node.pos, {id: p.id});
                     if (!nP) {
-                        nP = {id: p.id};
+                        nP = {id: p.id, name: p.name };
                         node.pos.push(nP);
                     }
                     for (const f of posCompareField) {
@@ -130,7 +130,7 @@ $(document).ready(() => {
                 for (const p of posRange) {
                     let nP = _.find(node.pos, {id: p.id});
                     if (!nP) {
-                        nP = {id: p.id};
+                        nP = {id: p.id, name: p.name};
                         node.pos.push(nP);
                     }
                     for (const f of posCompareField) {
@@ -340,7 +340,7 @@ $(document).ready(() => {
                 if (!searchLedger) {
                     searchLedger = $.ledgerSearch({
                         selector: '#search',
-                        ledger: { billsTree: billsTree, getLedgerPos: function (node){ return node.pos; } },
+                        ledger: { billsTree: billsTree, getLedgerPos: function (node){ return node.pos; }, posId: 'id', },
                         resultSpreadSetting: {
                             cols: [
                                 {title: '项目节编号', field: 'code', hAlign: 0, width: 90, formatter: '@', readOnly: true},

+ 26 - 0
app/public/js/se_jgcl.js

@@ -245,6 +245,18 @@ $(document).ready(() => {
                     });
                 }
             },
+            insertJgcl: function(sheet) {
+                if (!sheet.zh_setting || !sheet.zh_data) return;
+
+                const node = SpreadJsObj.getSelectObject(sheet);
+                postData(window.location.pathname + '/update', {insert: { select: node.id, count: 1 }}, function (result) {
+                    jgclObj.loadUpdateData(result);
+                    SpreadJsObj.reLoadSheetData(jgclSheet);
+                    refreshSum();
+                }, function () {
+                    SpreadJsObj.reLoadSheetData(jgclSheet);
+                });
+            },
             editEnded: function (e, info) {
                 if (!info.sheet.zh_setting || !info.sheet.zh_data) return;
 
@@ -426,6 +438,20 @@ $(document).ready(() => {
                     }
                 },
                 sprDel: '------------',
+                insert: {
+                    name: '插入',
+                    icon: 'fa-plus',
+                    callback: function (key, opt) {
+                        jgclOprObj.insertJgcl(jgclSheet);
+                    },
+                    disabled: function (key, opt) {
+                        const node = SpreadJsObj.getSelectObject(jgclSheet);
+                        return !node;
+                    },
+                    visible: function (key, opt) {
+                        return !readOnly;
+                    }
+                },
                 upMove: {
                     name: '上移',
                     icon: 'fa-arrow-up',

+ 26 - 0
app/public/js/se_other.js

@@ -277,6 +277,18 @@ $(document).ready(() => {
                     });
                 }
             },
+            insert: function(sheet) {
+                if (!sheet.zh_setting || !sheet.zh_data) return;
+
+                const node = SpreadJsObj.getSelectObject(sheet);
+                postData(window.location.pathname + '/update', {insert: { select: node.id, count: 1 }}, function (result) {
+                    seOtherObj.loadUpdateData(result);
+                    SpreadJsObj.reLoadSheetData(sheet);
+                    refreshSum();
+                }, function () {
+                    SpreadJsObj.reLoadSheetData(sheet);
+                });
+            },
             editEnded: function (e, info) {
                 if (!info.sheet.zh_setting || !info.sheet.zh_data) return;
 
@@ -455,6 +467,20 @@ $(document).ready(() => {
                     }
                 },
                 sprDel: '------------',
+                insert: {
+                    name: '插入',
+                    icon: 'fa-plus',
+                    callback: function (key, opt) {
+                        seOtherOprObj.insert(otherSheet);
+                    },
+                    disabled: function (key, opt) {
+                        const node = SpreadJsObj.getSelectObject(otherSheet);
+                        return !node;
+                    },
+                    visible: function (key, opt) {
+                        return !readOnly;
+                    }
+                },
                 upMove: {
                     name: '上移',
                     icon: 'fa-arrow-up',

+ 28 - 6
app/public/js/se_safe_prod.js

@@ -228,6 +228,18 @@ $(document).ready(() => {
                     });
                 }
             },
+            insert: function(sheet) {
+                if (!sheet.zh_setting || !sheet.zh_data) return;
+
+                const node = SpreadJsObj.getSelectObject(sheet);
+                postData(window.location.pathname + '/update', {insert: { select: node.id, count: 1 }}, function (result) {
+                    seSafeObj.loadUpdateData(result);
+                    SpreadJsObj.reLoadSheetData(sheet);
+                    refreshSum();
+                }, function () {
+                    SpreadJsObj.reLoadSheetData(sheet);
+                });
+            },
             editEnded: function (e, info) {
                 if (!info.sheet.zh_setting || !info.sheet.zh_data) return;
 
@@ -394,18 +406,28 @@ $(document).ready(() => {
                         seSafeOprObj.delete(safeSheet);
                     },
                     disabled: function (key, opt) {
-                        const sels = safeSheet.getSelections();
-                        if (!sels || !sels[0]) return true;
-
-                        const row = sels[0].row;
-                        const node = seSafeObj.data[row];
-                        return node === undefined || node === null;
+                        const node = SpreadJsObj.getSelectObject(safeSheet);
+                        return !node;
                     },
                     visible: function (key, opt) {
                         return !readOnly;
                     }
                 },
                 sprDel: '------------',
+                insert: {
+                    name: '插入',
+                    icon: 'fa-plus',
+                    callback: function (key, opt) {
+                        seSafeOprObj.insert(safeSheet);
+                    },
+                    disabled: function (key, opt) {
+                        const node = SpreadJsObj.getSelectObject(safeSheet);
+                        return !node;
+                    },
+                    visible: function (key, opt) {
+                        return !readOnly;
+                    }
+                },
                 upMove: {
                     name: '上移',
                     icon: 'fa-arrow-up',

+ 26 - 0
app/public/js/se_temp_land.js

@@ -218,6 +218,18 @@ $(document).ready(() => {
                     });
                 }
             },
+            insert: function(sheet) {
+                if (!sheet.zh_setting || !sheet.zh_data) return;
+
+                const node = SpreadJsObj.getSelectObject(sheet);
+                postData(window.location.pathname + '/update', {insert: { select: node.id, count: 1 }}, function (result) {
+                    seTempObj.loadUpdateData(result);
+                    SpreadJsObj.reLoadSheetData(sheet);
+                    refreshSum();
+                }, function () {
+                    SpreadJsObj.reLoadSheetData(sheet);
+                });
+            },
             editEnded: function (e, info) {
                 if (!info.sheet.zh_setting || !info.sheet.zh_data) return;
 
@@ -395,6 +407,20 @@ $(document).ready(() => {
                     }
                 },
                 sprDel: '------------',
+                insert: {
+                    name: '插入',
+                    icon: 'fa-plus',
+                    callback: function (key, opt) {
+                        seTempOprObj.insert(tempSheet);
+                    },
+                    disabled: function (key, opt) {
+                        const node = SpreadJsObj.getSelectObject(tempSheet);
+                        return !node;
+                    },
+                    visible: function (key, opt) {
+                        return !readOnly;
+                    }
+                },
                 upMove: {
                     name: '上移',
                     icon: 'fa-arrow-up',

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

@@ -209,6 +209,17 @@ $(document).ready(() => {
                     });
                 }
             },
+            insert: function(sheet) {
+                if (!sheet.zh_setting || !sheet.zh_data) return;
+
+                const node = SpreadJsObj.getSelectObject(sheet);
+                postData(window.location.pathname + '/update', {insert: { select: node.id, count: 1 }}, function (result) {
+                    yjclObj.loadUpdateData(result);
+                    SpreadJsObj.reLoadSheetData(sheet);
+                }, function () {
+                    SpreadJsObj.reLoadSheetData(sheet);
+                });
+            },
             editEnded: function (e, info) {
                 if (!info.sheet.zh_setting || !info.sheet.zh_data) return;
 
@@ -398,6 +409,20 @@ $(document).ready(() => {
                     }
                 },
                 sprDel: '------------',
+                insert: {
+                    name: '插入',
+                    icon: 'fa-plus',
+                    callback: function (key, opt) {
+                        yjclOprObj.insert(yjclSheet);
+                    },
+                    disabled: function (key, opt) {
+                        const node = SpreadJsObj.getSelectObject(yjclSheet);
+                        return !node;
+                    },
+                    visible: function (key, opt) {
+                        return !readOnly;
+                    }
+                },
                 upMove: {
                     name: '上移',
                     icon: 'fa-arrow-up',

+ 189 - 29
app/public/js/setting.js

@@ -138,8 +138,10 @@ $(document).ready(() => {
     $('a[aria-controls="edit-user2"]').on('click', function () {
         $('#edit-user2 input:radio').prop('checked', false);
         $('#edit-user2 input:checkbox').prop('checked', false);
+        $('#sign_permission').hide();
         const account = $(this).data('account');
         $('#edit-user2 input[name="id"]').val(account.id);
+        $('#edit-user2 input[name="unit_sign_path"]').val(account.unit_sign_path);
         // 权限赋值
         if (account.permission !== '') {
             const permission = JSON.parse(account.permission);
@@ -162,6 +164,9 @@ $(document).ready(() => {
                 } else if (allPermission[pm].type === 'radio') {
                     $('#edit-user2 input:radio[id="' + pm + '_' + permission[pm] + '"]').prop('checked', true);
                 }
+                if (pm === 'other' && permission[pm].indexOf('1') !== -1) {
+                    $('#sign_permission').show();
+                }
             }
         }
         // 协作赋值
@@ -185,6 +190,56 @@ $(document).ready(() => {
                 $('#edit-user2 input[name="cooperation"]').attr('disabled', true);
             }
         }
+        if ($(this).attr('id') === 'other_1') {
+            if ($(this).is(':checked')) {
+                $('#sign_permission').show();
+                $('#sign_permission').click();
+            } else {
+                $('#sign_permission').hide();
+            }
+        }
+    });
+
+    $('#sign_permission').on('click', function () {
+        const uid = $('#edit-user2 input[name="id"]').val();
+        if (!uid) {
+            toastr.error('请先选择用户');
+            return;
+        }
+        const user = _.find(accountData, { id: parseInt(uid) });
+        if (!user) {
+            toastr.error('用户不存在');
+            return;
+        }
+        let html = '';
+        if (user.company) {
+            const unit = _.find(unitList, { name: user.company });
+            const unit_sign_path = $('#edit-user2 input[name="unit_sign_path"]').val();
+            if (unit) {
+                const paths = unit.sign_path ? unit.sign_path.split('&%&') : [];
+                if (paths.length > 0) {
+                    console.log(paths);
+                    for (const sp of paths) {
+                        const checked = unit_sign_path && unit_sign_path.indexOf(sp) !== -1 ? 'checked' : '';
+                        html += '<div class="col-3 text-center my-2"><img src="'+ fujianOssPath + sp +'" width="120">' +
+                            '<div class="w-100 mt-2"><input type="checkbox" name="signpath[]" '+ (checked ? 'checked' : '') +' data-path="' + sp + '" />' +
+                            '</div></div>';
+                    }
+                }
+            }
+        }
+        $('#unit-sign').html(html);
+        $('#sign_permission_modal').modal('show');
+    });
+
+    $('#set_unit_sign').on('click', function () {
+        $('#sign_permission_modal').modal('hide');
+        const uid = $('#edit-user2 input[name="id"]').val();
+        const signPaths = [];
+        $('#unit-sign input[name="signpath[]"]:checked').each(function () {
+            signPaths.push($(this).data('path'));
+        });
+        $('#edit-user2 input[name="unit_sign_path"]').val(signPaths.join('&%&'));
     });
 
     // 解绑第三方平台
@@ -297,6 +352,20 @@ $(document).ready(() => {
         }
     });
 
+    $('#unit_user_permission').change(function () {
+        const val_name = 'sign_permission';
+        let val = $(this).is(':checked') ? 1 : 0;
+        if (!oneUnit) {
+            toastr.error('所选单位有误,请重新选择');
+            return false;
+        }
+        if(oneUnit[val_name] !== val) {
+            postData('/setting/user/unit/save', {type: 'update', id: oneUnit.id, val_name, val}, function (result) {
+                oneUnit[val_name] = val;
+            });
+        }
+    });
+
     $('#one_unit textarea').blur(function () {
         const val_name = $(this).data('name');
         let val = _.trim($(this).val()) !== '' ? _.trim($(this).val()) : null;
@@ -354,6 +423,14 @@ $(document).ready(() => {
         }
     });
 
+    $('#mseal').on('shown.bs.modal', function () {
+        if (!oneUnit) {
+            toastr.error('所选单位有误,请重新选择');
+            return false;
+        }
+        makeManageSignHtml(oneUnit.sign_path);
+    });
+
     // 删除单位
     $('#delete-unit').click(function () {
         if (!oneUnit) {
@@ -371,40 +448,82 @@ $(document).ready(() => {
             toastr.error('所选单位有误,请重新选择');
             return false;
         }
-        const file = this.files[0];
-        const ext = file.name.toLowerCase().split('.').splice(-1)[0];
-        const imgStr = /(jpg|jpeg|png|bmp|BMP|JPG|PNG|JPEG)$/;
-        if (!imgStr.test(ext)) {
-            toastr.error('请上传正确的图片格式文件');
-            return
+        const files = Array.from(this.files)
+        const valiData = files.map(v => {
+            const ext = v.name.substring(v.name.lastIndexOf('.') + 1)
+            return {
+                size: v.size,
+                ext
+            }
+        });
+        if (validateFiles(valiData)) {
+            if (files.length) {
+                const formData = new FormData();
+                formData.append('id', oneUnit.id);
+                files.forEach(file => {
+                    formData.append('name', file.name);
+                    formData.append('size', file.size);
+                    formData.append('file', file);
+                });
+                postDataWithFile('/setting/user/unit/upload', formData, function (result) {
+                    oneUnit.sign_path = result.sign_path;
+                    makeSignHtml(oneUnit.sign_path);
+                    $('#sign-upload').val('');
+                    toastr.success('上传成功');
+                });
+            }
         }
-        if ($(this).val()) {
-            const formData = new FormData();
-            formData.append('file', this.files[0]);
-            formData.append('id', oneUnit.id);
-            postDataWithFile('/setting/user/unit/upload', formData, function (result) {
-                const html = '<img src="'+ fujianOssPath + result.sign_path +'" width="120">';
-                $('#sign-show').html(html);
-                $('#sign-upload').val('');
-                oneUnit.sign_path = result.sign_path;
-                $('#upload-sign').hide();
-                $('#delete-sign').show();
-                toastr.success('上传成功');
-            });
+    });
+
+    $('#manage-sign-upload').change(function () {
+        if (!oneUnit) {
+            toastr.error('所选单位有误,请重新选择');
+            return false;
+        }
+        const files = Array.from(this.files)
+        const valiData = files.map(v => {
+            const ext = v.name.substring(v.name.lastIndexOf('.') + 1)
+            return {
+                size: v.size,
+                ext
+            }
+        });
+        if (validateFiles(valiData)) {
+            if (files.length) {
+                const formData = new FormData();
+                formData.append('id', oneUnit.id);
+                files.forEach(file => {
+                    formData.append('name', file.name);
+                    formData.append('size', file.size);
+                    formData.append('file', file);
+                });
+                postDataWithFile('/setting/user/unit/upload', formData, function (result) {
+                    oneUnit.sign_path = result.sign_path;
+                    makeManageSignHtml(oneUnit.sign_path);
+                    makeSignHtml(oneUnit.sign_path);
+                    $('#manage-sign-upload').val('');
+                    toastr.success('上传成功');
+                });
+            }
         }
     });
 
     // 移除签章
-    $('#delete-sign').click(function () {
+    $('body').on('click', '#manage-unit-sign .delete-sign', function () {
         if (!oneUnit) {
             toastr.error('所选单位有误,请重新选择');
             return false;
         }
-        postData('/setting/user/unit/save', { type: 'del-sign', id: oneUnit.id }, function (result) {
-            $('#sign-show').html('');
+        const delPath = $(this).attr('data-path');
+        if (!delPath) {
+            toastr.error('所选签章有误,请重新选择');
+            return false;
+        }
+        postData('/setting/user/unit/save', { type: 'del-sign', id: oneUnit.id, path: delPath }, function (result) {
+            oneUnit.sign_path = result;
+            makeManageSignHtml(oneUnit.sign_path);
+            makeSignHtml(oneUnit.sign_path);
             toastr.warning('已移除');
-            oneUnit.sign_path = null;
-            $('#upload-sign').show();
             $('#delete-sign').hide();
         })
     });
@@ -535,6 +654,29 @@ function checkPasswordForm() {
     }
 }
 
+function makeManageSignHtml(signPath) {
+    const paths = signPath ? signPath.split('&%&') : [];
+    let html = '';
+    if (paths.length > 0) {
+        for (const sp of paths) {
+            html += '<div class="col-3 text-center my-2"><img src="'+ fujianOssPath + sp +'" width="120"><div class="w-100"><button class="btn btn-sm btn-outline-danger delete-sign" data-path="' + sp + '">移除</button></div></div>';
+        }
+    }
+
+    $('#manage-unit-sign').html(html);
+}
+
+function makeSignHtml(signPath) {
+    const paths = signPath ? signPath.split('&%&') : [];
+    let html = '';
+    if (paths.length > 0) {
+        for (const sp of paths.slice(0, 5)) {
+            html += '<img src="' + fujianOssPath + sp + '" width="120" class="mr-1">';
+        }
+    }
+    $('#sign-show').html(html);
+}
+
 function setUnitRightHtml(id) {
     const one = _.find(unitList, { id });
     if (one) {
@@ -548,16 +690,15 @@ function setUnitRightHtml(id) {
         $('#unit_basic').val(one.basic);
         $('#unit_type').val(one.type);
         if(one.sign_path) {
-            $('#sign-show').html('<img src="' + fujianOssPath + one.sign_path + '" width="120">');
-            $('#delete-sign').show();
-            $('#upload-sign').hide();
+            makeSignHtml(one.sign_path);
+            // $('#upload-sign').hide();
         } else {
             $('#sign-show').html('');
-            $('#delete-sign').hide();
-            $('#upload-sign').show();
+            // $('#upload-sign').show();
         }
         oneUnit = one;
         $('#add_user_company').val(one.name);
+        $('#unit_user_permission').prop('checked', !!oneUnit.sign_permission);
     }
 }
 
@@ -626,6 +767,25 @@ function checkUserForm(status) {
     }
 }
 
+function validateFiles(files) {
+    if (files.length > 10) {
+        toastr.error('至多同时上传10个文件');
+        return false
+    }
+    return files.every(file => {
+        if (file.size > 1024 * 1024 * 50) {
+            toastr.error('文件大小限制为50MB');
+            return false
+        }
+        const imgStr = /(jpg|jpeg|png|bmp|BMP|JPG|PNG|JPEG)$/;
+        if (!imgStr.test(file.ext)) {
+            toastr.error('请上传正确的图片格式文件');
+            return false
+        }
+        return true
+    })
+}
+
 /**
  * 表单检测
  */

+ 1 - 0
app/public/js/setting_manage.js

@@ -163,6 +163,7 @@ function getFilterTenderTreeHtml() {
 }
 
 $(document).ready(() => {
+    console.log('hello');
     autoFlashHeight();
     function getObjHeight(select) {
         return select.length > 0 ? select.height() : 0;

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

@@ -178,7 +178,7 @@ $(document).ready(() => {
             if (tab.attr('content') === '#search' && !searchLedger) {
                 searchLedger = $.ledgerSearch({
                     selector: '#search',
-                    ledger: { billsTree: settleTree, pos: settlePos },
+                    ledger: { billsTree: settleTree, getLedgerPos: function(node) {return settlePos.getLedgerPos(node.lid)} },
                     searchOver: true,
                     searchEmpty: true,
                     keyId: 'tree_id',

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

@@ -283,7 +283,7 @@ $(document).ready(() => {
             if (tab.attr('content') === '#search' && !searchLedger) {
                 searchLedger = $.ledgerSearch({
                     selector: '#search',
-                    ledger: { billsTree: settleTree, pos: settlePos },
+                    ledger: { billsTree: settleTree, getLedgerPos: function(node) {return settlePos.getLedgerPos(node.lid)} },
                     searchOver: true,
                     searchEmpty: true,
                     keyId: 'tree_id',

+ 1 - 1
app/public/js/shares/cs_tools.js

@@ -869,7 +869,7 @@ const showSelectTab = function(select, spread, afterShow) {
                 if (!posRange || posRange.length === 0) continue;
                 for (const p of posRange) {
                     if (posCheckFun(p, keyword)) {
-                        const convertData = { lid: node[ledger.billsTree.setting.id], pid: p[ledger.pos.setting.id] };
+                        const convertData = { lid: node[ledger.billsTree.setting.id], pid: p[ledger.pos ? ledger.pos.setting.id : ledger.posId] };
                         for (const col of setting.resultSpreadSetting.cols) {
                             convertData[col.field] = p[col.field];
                         }

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

@@ -218,7 +218,7 @@ function getShenpiHtml (this_code) {
             if(aidList.length > 0) {
                 for (const uid of aidList) {
                     const user = _.find(accountList, { id: uid });
-                    nameList.push(user.name);
+                    if (user) nameList.push(user.name);
                 }
             }
             html.push('<i class="fa fa-question-circle text-primary" data-container="body" data-toggle="tooltip" data-placement="bottom" ' +

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

@@ -239,7 +239,7 @@ $(document).ready(() => {
                 if (def && def.color) return def.color;
             }
 
-            return checkUtils.billsOver(data, checkTzMeasureType(), stagePos) ? '#f8d7da' : defaultColor;
+            return checkUtils.billsOver(data, stagePos, tenderInfo.checkOverInfo) ? '#f8d7da' : defaultColor;
         } else {
             return defaultColor;
         }
@@ -317,7 +317,7 @@ $(document).ready(() => {
             }
         }
         if (checkTzMeasureType()) {
-            return checkUtils.posOver(data)  ? '#f8d7da' : defaultColor;
+            return checkUtils.posOver(data, tenderInfo.checkOverInfo)  ? '#f8d7da' : defaultColor;
         }
     };
     sjsSettingObj.setGridSelectStyle(posSpreadSetting);
@@ -1108,7 +1108,7 @@ $(document).ready(() => {
                         }, {
                             key: 'over', title: '超计', valid: true,
                             check: function (node) {
-                                return checkUtils.billsOver(node, checkTzMeasureType(), stagePos);
+                                return checkUtils.billsOver(node, stagePos, tenderInfo.checkOverInfo);
                             }
                         }, {
                             key: 'empty', title: '漏计', valid: false,

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

@@ -99,7 +99,7 @@ $(document).ready(function() {
                 // 0号台账合同
                 if (colSetCache.total_price.show) {
                     html.push('<td style="width: 100px" class="text-right">');
-                    html.push(node.tp_cache.total_price || '');
+                    html.push(node.tp_cache.ledger_tp || '');
                     html.push('</td>');
                 }
                 // 本期完成
@@ -117,7 +117,7 @@ $(document).ready(function() {
                 // 截止本期变更
                 if (colSetCache.end_qc_tp.show) {
                     html.push('<td style="width: 100px" class="text-right">');
-                    html.push(node.end_qc_tp || '');
+                    html.push(node.tp_cache.end_qc_tp || '');
                     html.push('</td>');
                 }
                 // 截止本期完成
@@ -129,7 +129,7 @@ $(document).ready(function() {
                 // 截止上期完成
                 if (colSetCache.pre_gather_tp.show) {
                     html.push('<td style="width: 100px" class="text-right">');
-                    html.push(node.pre_gather_tp || '');
+                    html.push(node.tp_cache.pre_gather_tp || '');
                     html.push('</td>');
                 }
                 // 预付款

+ 8 - 0
app/router.js

@@ -441,6 +441,11 @@ module.exports = app => {
     app.get('/sp/:id/financial/summary', sessionAuth, subProjectCheck, financialCheck, 'financialController.summary');
     app.post('/sp/:id/financial/summary/load', sessionAuth, subProjectCheck, financialCheck, 'financialController.summaryLoad');
 
+    // 图纸管理
+    // app.get('/sp/:id/drawing/tender', sessionAuth, subProjectCheck, 'drawingController.tender');
+    // app.get('/sp/:id/drawing/design', sessionAuth, subProjectCheck, 'drawingController.design');
+    // app.get('/sp/:id/drawing/as-built', sessionAuth, subProjectCheck, 'drawingController.built');
+
     // ------------------------- 项目内部相关 -----------------------------
 
 
@@ -512,6 +517,7 @@ module.exports = app => {
     app.post('/tender/:id/pos/update', sessionAuth, tenderCheck, subProjectCheck, uncheckTenderCheck, 'ledgerController.posUpdate');
     app.post('/tender/:id/pos/paste', sessionAuth, tenderCheck, subProjectCheck, uncheckTenderCheck, 'ledgerController.posPaste');
     app.post('/tender/:id/ledger/deal2sgfh', sessionAuth, tenderCheck, subProjectCheck, uncheckTenderCheck, 'ledgerController.deal2sgfh');
+    app.post('/tender/:id/ledger/sgfh2deal', sessionAuth, tenderCheck, subProjectCheck, uncheckTenderCheck, 'ledgerController.sgfh2deal');
     app.post('/tender/:id/ledger/check', sessionAuth, tenderCheck, subProjectCheck, uncheckTenderCheck, 'ledgerController.check');
     app.post('/tender/:id/measure/ledger/auditors', sessionAuth, tenderCheck, subProjectCheck, uncheckTenderCheck, 'ledgerController.ledgerAuditors');
 
@@ -1012,6 +1018,8 @@ module.exports = app => {
     app.get('/wap/tender/:id/change/project/:cpid/information', sessionAuth, tenderCheck, subProjectCheck, uncheckTenderCheck, changeProjectCheck, changeProjectAuditCheck, 'wapController.changeProject');
     app.get('/wap/tender/:id/change/apply/:caid/information', sessionAuth, tenderCheck, subProjectCheck, uncheckTenderCheck, changeApplyCheck, changeApplyAuditCheck, 'wapController.changeApply');
     app.get('/wap/tender/:id/change/plan/:cpid/information', sessionAuth, tenderCheck, subProjectCheck, uncheckTenderCheck, changePlanCheck, changePlanAuditCheck, 'wapController.changePlan');
+    app.get('/wap/tender/:id/material/:order', sessionAuth, tenderCheck, subProjectCheck, uncheckTenderCheck, materialCheck, 'wapController.material');
+    app.get('/wap/tender/:id/measure/material/:order', sessionAuth, tenderCheck, subProjectCheck, uncheckTenderCheck, materialCheck, 'wapController.material');
 
     // 微信
     app.get('/wx', 'wechatController.index');

+ 3 - 7
app/service/change.js

@@ -998,8 +998,6 @@ module.exports = app => {
                     // 清单数据更新
                     const bills_list = await this.ctx.service.changeAuditList.getList(changeData.cid, changeData.order_by);
                     let total_price = 0;
-                    let valuation_tp = 0;
-                    let unvaluation_tp = 0;
                     const tp_decimal = changeData.tp_decimal ? changeData.tp_decimal : this.ctx.tender.info.decimal.tp;
                     const updateList = [];
                     for (const bl of bills_list) {
@@ -1014,9 +1012,6 @@ module.exports = app => {
                             list_update.samount = bl.spamount || 0;
                             list_update.checked_amount = bl.spamount || 0;
                             list_update.checked_price = this.ctx.helper.mul(bl.unit_price, list_update.checked_amount, tp_decimal);
-                            // 统计计价和不计价金额
-                            valuation_tp = bl.is_valuation ? this.ctx.helper.add(valuation_tp, list_update.checked_price) : valuation_tp;
-                            unvaluation_tp = !bl.is_valuation ? this.ctx.helper.add(unvaluation_tp, list_update.checked_price) : unvaluation_tp;
                         }
                         updateList.push(list_update);
                     }
@@ -1095,14 +1090,13 @@ module.exports = app => {
                         }
                     }
                     change_update.total_price = total_price;
-                    change_update.valuation_tp = valuation_tp;
-                    change_update.unvaluation_tp = unvaluation_tp;
                     const options = {
                         where: {
                             cid: changeData.cid,
                         },
                     };
                     await this.transaction.update(this.tableName, change_update, options);
+                    await this.ctx.service.changeAuditList.calcCamountSum(this.transaction, false, changeData);
                 }
                 await this.transaction.commit();
                 result = true;
@@ -1247,6 +1241,7 @@ module.exports = app => {
                     },
                 };
                 await this.transaction.update(this.tableName, change_update, options);
+                await this.ctx.service.changeAuditList.calcCamountSum(this.transaction, false, changeData);
 
                 await this.transaction.commit();
                 result = true;
@@ -1389,6 +1384,7 @@ module.exports = app => {
                     },
                 };
                 await this.transaction.update(this.tableName, change_update, options);
+                await this.ctx.service.changeAuditList.calcCamountSum(this.transaction, false, changeData);
                 const sms = new SMS(this.ctx);
                 const code = await sms.contentChange(changeData.code);
                 const shenpiUrl = await this.ctx.helper.urlToShort(

+ 13 - 13
app/service/change_audit_list.js

@@ -509,27 +509,27 @@ module.exports = app => {
                     let unitPrice = 0;
                     for (const cl of list) {
                         if (cl.spamount) {
-                            spamount = this.ctx.helper.accAdd(spamount, cl.spamount);
+                            spamount = this.ctx.helper.add(spamount, cl.spamount);
                             unitPrice = cl.unit_price;
                             if (cl.is_valuation) {
-                                valuation_amount = this.ctx.helper.accAdd(valuation_amount, cl.spamount);
+                                valuation_amount = this.ctx.helper.add(valuation_amount, cl.spamount);
                             } else {
-                                unvaluation_amount = this.ctx.helper.accAdd(unvaluation_amount, cl.spamount);
+                                unvaluation_amount = this.ctx.helper.add(unvaluation_amount, cl.spamount);
                             }
                             const posOrNePrice = this.ctx.helper.mul(cl.spamount, this.ctx.helper.round(cl.unit_price, up_decimal), tp_decimal);
                             if (posOrNePrice >= 0) {
-                                positive_tp = this.ctx.helper.accAdd(positive_tp, posOrNePrice);
+                                positive_tp = this.ctx.helper.add(positive_tp, posOrNePrice);
                             } else {
-                                negative_tp = this.ctx.helper.accAdd(negative_tp, posOrNePrice);
+                                negative_tp = this.ctx.helper.add(negative_tp, posOrNePrice);
                             }
                         }
                     }
                     const price = this.ctx.helper.mul(spamount, this.ctx.helper.round(unitPrice, up_decimal), tp_decimal);
                     const valuation_price = this.ctx.helper.mul(valuation_amount, this.ctx.helper.round(unitPrice, up_decimal), tp_decimal) || 0;
                     const unvaluation_price = this.ctx.helper.mul(unvaluation_amount, this.ctx.helper.round(unitPrice, up_decimal), tp_decimal) || 0;
-                    valuation_tp = this.ctx.helper.accAdd(valuation_tp, valuation_price);
-                    unvaluation_tp = this.ctx.helper.accAdd(unvaluation_tp, unvaluation_price);
-                    total_price = this.ctx.helper.accAdd(total_price, price);
+                    valuation_tp = this.ctx.helper.add(valuation_tp, valuation_price);
+                    unvaluation_tp = this.ctx.helper.add(unvaluation_tp, unvaluation_price);
+                    total_price = this.ctx.helper.add(total_price, price);
                     // if (price >= 0) {
                     //     positive_tp = this.ctx.helper.accAdd(positive_tp, price);
                     // } else {
@@ -539,16 +539,16 @@ module.exports = app => {
                     const list = this._.filter(changeList, { gcl_id: g });
                     for (const cl of list) {
                         const price = this.ctx.helper.mul(this.ctx.helper.round(cl.unit_price, up_decimal), cl.spamount, tp_decimal);
-                        total_price = this.ctx.helper.accAdd(total_price, price);
+                        total_price = this.ctx.helper.add(total_price, price);
                         if (price >= 0) {
-                            positive_tp = this.ctx.helper.accAdd(positive_tp, price);
+                            positive_tp = this.ctx.helper.add(positive_tp, price);
                         } else {
-                            negative_tp = this.ctx.helper.accAdd(negative_tp, price);
+                            negative_tp = this.ctx.helper.add(negative_tp, price);
                         }
                         if (cl.is_valuation) {
-                            valuation_tp = this.ctx.helper.accAdd(valuation_tp, price);
+                            valuation_tp = this.ctx.helper.add(valuation_tp, price);
                         } else {
-                            unvaluation_tp = this.ctx.helper.accAdd(unvaluation_tp, price);
+                            unvaluation_tp = this.ctx.helper.add(unvaluation_tp, price);
                         }
                     }
                 }

+ 2 - 2
app/service/filing.js

@@ -94,7 +94,7 @@ module.exports = app => {
                 const newData = {
                     id: f.newId, tree_pid : parent ? parent.newId : rootId, tree_level: f.tree_level, tree_order: f.tree_order,
                     spid, add_user_id: this.ctx.session.sessionUser.accountId, is_fixed: f.is_fixed,
-                    filing_type: f.filing_type, name: f.name, tips: f.tips,
+                    filing_type: f.filing_type, name: f.name, tips: f.tips, file_company: f.file_company,
                 };
                 insertData.push(newData);
             }
@@ -297,7 +297,7 @@ module.exports = app => {
 
             const sourceData = await this.getAllDataByCondition({ where: { spid } });
 
-            const validFields = ['id', 'is_fixed', 'name', 'filing_type', 'tree_order', 'tips'];
+            const validFields = ['id', 'is_fixed', 'name', 'filing_type', 'tree_order', 'tips', 'file_company'];
             const updateData = [];
             for (const d of data) {
                 if (!d.id) throw '提交数据格式错误';

+ 3 - 3
app/service/filing_template.js

@@ -63,7 +63,7 @@ module.exports = app => {
                 insertData.push({
                     id: this.uuid.v4(), temp_id: templateId, add_user_id: this.ctx.session.sessionUser.accountId,
                     tree_pid: parent ? parent.id : rootId, tree_level: parent ? parent.tree_level + 1 : 1, tree_order: d.tree_order,
-                    name: d.name, tips: d.tips, filing_type: d.filing_type, is_fixed: parent ? d.is_fixed : 1, org_id: d.id,
+                    name: d.name, file_company: d.file_company, tips: d.tips, filing_type: d.filing_type, is_fixed: parent ? d.is_fixed : 1, org_id: d.id,
                 });
             }
             insertData.forEach(x => { delete x.org_id; });
@@ -256,7 +256,7 @@ module.exports = app => {
                 insertData.push({
                     id: this.uuid.v4(), temp_id: templateId, add_user_id: this.ctx.session.sessionUser.accountId,
                     tree_pid: parent ? parent.id : rootId, tree_level: parent ? parent.tree_level + 1 : 1, tree_order: d.tree_order,
-                    name: d.name, tips: d.tips, filing_type: d.filing_type, is_fixed: parent ? d.is_fixed : 1, org_id: d.id,
+                    name: d.name, file_company: node.file_company, tips: d.tips, filing_type: d.filing_type, is_fixed: parent ? d.is_fixed : 1, org_id: d.id,
                 });
             }
             insertData.forEach(x => { delete x.org_id; });
@@ -278,7 +278,7 @@ module.exports = app => {
 
             const sourceData = await this.getData(templateId);
 
-            const validFields = ['id', 'is_fixed', 'name', 'filing_type', 'tree_order', 'tips'];
+            const validFields = ['id', 'is_fixed', 'name', 'filing_type', 'tree_order', 'tips', 'file_company'];
             const updateData = [];
             for (const d of data) {
                 if (!d.id) throw '提交数据格式错误';

+ 41 - 0
app/service/material_audit.js

@@ -297,9 +297,11 @@ module.exports = app => {
 
                 // 微信模板通知
                 const materialInfo = await this.ctx.service.material.getDataById(materialId);
+                const shenpiUrl = await this.ctx.helper.urlToShort(this.ctx.protocol + '://' + this.ctx.host + '/wap/tender/' + this.ctx.tender.id + '/measure/material/' + materialInfo.order);
                 const material_decimal = materialInfo && materialInfo.decimal ? JSON.parse(materialInfo.decimal) : materialConst.decimal;
                 const users = this._.map(audits, 'aid');
                 const wechatData = {
+                    wap_url: shenpiUrl,
                     qi: materialInfo.order,
                     status: wxConst.status.check,
                     tips: wxConst.tips.check,
@@ -408,8 +410,10 @@ module.exports = app => {
                             mid: materialId,
                             order: 1,
                         });
+                        const shenpiUrl = await this.ctx.helper.urlToShort(this.ctx.protocol + '://' + this.ctx.host + '/wap/tender/' + this.ctx.tender.id + '/measure/material/' + materialInfo.order);
                         // 微信模板通知
                         const wechatData = {
+                            wap_url: shenpiUrl,
                             qi: materialInfo.order,
                             status: wxConst.status.check,
                             tips: wxConst.tips.check,
@@ -506,7 +510,9 @@ module.exports = app => {
                             order: 1,
                         });
                         const users = this._.uniq(this._.concat(this._.map(auditors, 'aid'), materialInfo.user_id));
+                        const shenpiUrl = await this.ctx.helper.urlToShort(this.ctx.protocol + '://' + this.ctx.host + '/wap/tender/' + this.ctx.tender.id + '/measure/material/' + materialInfo.order);
                         const wechatData = {
+                            wap_url: shenpiUrl,
                             qi: materialInfo.order,
                             status: wxConst.status.success,
                             tips: wxConst.tips.success,
@@ -590,7 +596,9 @@ module.exports = app => {
                 const materialInfo = await this.ctx.service.material.getDataById(materialId);
                 const material_decimal = materialInfo && materialInfo.decimal ? JSON.parse(materialInfo.decimal) : materialConst.decimal;
                 const users = this._.uniq(this._.concat(this._.map(auditors, 'aid'), materialInfo.user_id));
+                const shenpiUrl = await this.ctx.helper.urlToShort(this.ctx.protocol + '://' + this.ctx.host + '/wap/tender/' + this.ctx.tender.id + '/measure/material/' + materialInfo.order);
                 const wechatData = {
+                    wap_url: shenpiUrl,
                     qi: materialInfo.order,
                     status: wxConst.status.back,
                     tips: wxConst.tips.back,
@@ -681,7 +689,9 @@ module.exports = app => {
                 });
                 const materialInfo = await this.ctx.service.material.getDataById(materialId);
                 const material_decimal = materialInfo && materialInfo.decimal ? JSON.parse(materialInfo.decimal) : materialConst.decimal;
+                const shenpiUrl = await this.ctx.helper.urlToShort(this.ctx.protocol + '://' + this.ctx.host + '/wap/tender/' + this.ctx.tender.id + '/measure/material/' + materialInfo.order);
                 const wechatData = {
+                    wap_url: shenpiUrl,
                     qi: materialInfo.order,
                     status: wxConst.status.check,
                     tips: wxConst.tips.check,
@@ -821,7 +831,9 @@ module.exports = app => {
                 });
 
                 // 微信模板通知
+                const shenpiUrl = await this.ctx.helper.urlToShort(this.ctx.protocol + '://' + this.ctx.host + '/wap/tender/' + this.ctx.tender.id + '/measure/material/' + materialInfo.order);
                 const wechatData = {
+                    wap_url: shenpiUrl,
                     qi: materialInfo.order,
                     status: wxConst.status.check,
                     tips: wxConst.tips.check,
@@ -1117,6 +1129,35 @@ module.exports = app => {
             return this.db.query(sql, sqlParam);
         }
 
+        /**
+         * 取待审批期列表(wap用)
+         *
+         * @param auditorId
+         * @return {Promise<*>}
+         */
+        async getAuditMaterialByWap(auditorId) {
+            const sql =
+                'SELECT sa.`aid`, sa.`times`, sa.`begin_time`, sa.`end_time`, sa.`tid`, sa.`mid`,' +
+                // '    s.`order` As `sorder`, s.`status` As `sstatus`, s.`s_time`, s.`contract_tp`, s.`qc_tp`, s.`pre_contract_tp`, s.`pre_qc_tp`, s.`yf_tp`, s.`pre_yf_tp`, ' +
+                '    s.*,' +
+                '    t.`name`, t.`project_id`, t.`type`, t.`user_id`, t.`spid`,' +
+                '    ti.`deal_info` ' +
+                '  FROM ?? AS sa' +
+                '    Left Join ?? AS s On sa.`mid` = s.`id`' +
+                '    Left Join ?? As t On sa.`tid` = t.`id`' +
+                '    Left Join ?? AS ti ON ti.`tid` = t.`id`' +
+                '  WHERE sa.`aid` = ? and sa.`status` = ?';
+            const sqlParam = [
+                this.tableName,
+                this.ctx.service.material.tableName,
+                this.ctx.service.tender.tableName,
+                this.ctx.service.tenderInfo.tableName,
+                auditorId,
+                auditConst.status.checking,
+            ];
+            return await this.db.query(sql, sqlParam);
+        }
+
         async updateNewAuditList(material, newList) {
             const transaction = await this.db.beginTransaction();
             try {

+ 10 - 1
app/service/material_list.js

@@ -673,7 +673,16 @@ module.exports = app => {
         }
 
         async cover() {
-            return await this.db.delete(this.ctx.service.materialList.tableName, { tid: this.ctx.tender.id, mid: this.ctx.material.id, is_self: 0 });
+            const transaction = await this.db.beginTransaction();
+            try {
+                await transaction.delete(this.tableName, { tid: this.ctx.tender.id, order: this.ctx.material.order, is_self: 0 });
+                await transaction.delete(this.ctx.service.materialListGcl.tableName, { tid: this.ctx.tender.id, mid: this.ctx.material.id });
+                await transaction.update(this.ctx.service.materialChecklist.tableName, { had_bills: 0 }, { where: { tid: this.ctx.tender.id, mid: this.ctx.material.id } });
+                await transaction.commit();
+            } catch (err) {
+                await transaction.rollback();
+                throw err;
+            }
         }
 
         /**

+ 2 - 2
app/service/phase_pay.js

@@ -14,7 +14,7 @@ const shenpiConst = require('../const/shenpi');
 const calcBase = [
     {name: '签约合同价', code: 'htj', sort: 10},
     {name: '暂列金额', code: 'zlje', sort: 2},
-    {name: '签约合同价(不含暂列金)', code: 'htjszl', sort: 1},
+    {name: '签约合同价(不含暂列金、计日工)', code: 'htjszl', sort: 1},
     {name: '签约开工预付款', code: 'kgyfk', sort: 2},
     {name: '签约材料预付款', code: 'clyfk', sort: 2},
     {name: '本期完成计量', code: 'bqwc', limit: true, sort: 10, checkStart: 'gather_tp'},
@@ -187,7 +187,7 @@ module.exports = app => {
                         cb.value = tenderInfo.deal_param.zanLiePrice;
                         break;
                     case 'htjszl':
-                        cb.value = this.ctx.helper.sub(tenderInfo.deal_param.contractPrice, tenderInfo.deal_param.zanLiePrice);
+                        cb.value = this.ctx.helper.sub(this.ctx.helper.sub(tenderInfo.deal_param.contractPrice, tenderInfo.deal_param.zanLiePrice), tenderInfo.deal_param.jrgPrice);
                         break;
                     case 'kgyfk':
                         cb.value = tenderInfo.deal_param.startAdvance;

+ 8 - 0
app/service/project_account.js

@@ -423,6 +423,12 @@ module.exports = app => {
                     const companyInfo = await this.ctx.service.constructionUnit.getDataByCondition({ pid: this.ctx.session.sessionProject.id, name: data.company });
                     if (!companyInfo) throw '单位不存在';
                     data.company_id = companyInfo.id;
+                    if (id > 0) {
+                        const user = await this.getDataById(id);
+                        if (user.company_id !== companyInfo.id) {
+                            data.unit_sign_path = null; // 修改单位时,清除原有的单位印章
+                        }
+                    }
                 }
             }
             if (id > 0) {
@@ -748,7 +754,9 @@ module.exports = app => {
                 // 应该暂对应的重新发送的开关通知,并重新
                 await this.ctx.service.noticeAgain.updateUserNoticeAgain(transaction, id, notice_again);
                 updateData.notice_again = JSON.stringify(notice_again);
+                updateData.unit_sign_path = data.unit_sign_path || null;
                 delete data.id;
+                delete data.unit_sign_path;
                 updateData.permission = JSON.stringify(data);
 
                 const operate = await transaction.update(this.tableName, updateData);

+ 22 - 0
app/service/report.js

@@ -10,6 +10,7 @@
 
 const BudgetSource = require('../lib/rm/tender_budget');
 const MaterialSource = require('../lib/rm/tender_material');
+const FinancialSource = require('../lib/rm/tender_financial');
 
 const rptCustomData = require('../lib/rptCustomData');
 const bindData = {
@@ -79,6 +80,7 @@ module.exports = app => {
             const service = this.ctx.service;
             await service.tender.checkTender(params.tender_id);
             const materialSource = new MaterialSource(this.ctx);
+            const financialSource = new FinancialSource(this.ctx);
             const rst = {};
             const runnableRst = [];
             const runnableKey = []; // 这个配合runnableRst用,未来考虑并行查询优化
@@ -439,6 +441,26 @@ module.exports = app => {
                             runnableRst.push(service.constructionUnit.getReportData(this.ctx.session.sessionProject.id));
                             runnableKey.push(filter);
                             break;
+                        case 'mem_financial_pay':
+                            runnableRst.push(financialSource.projectPay(params.tender_id));
+                            runnableKey.push(filter);
+                            break;
+                        case 'mem_financial_pay_contract':
+                            runnableRst.push(financialSource.projectPayContract(params.tender_id));
+                            runnableKey.push(filter);
+                            break;
+                        case 'mem_financial_pay_tender':
+                            runnableRst.push(financialSource.projectPayTender(params.tender_id));
+                            runnableKey.push(filter);
+                            break;
+                        case 'mem_financial_transfer':
+                            runnableRst.push(financialSource.projectTransfer(params.tender_id));
+                            runnableKey.push(filter);
+                            break;
+                        case 'mem_financial_transfer_tender':
+                            runnableRst.push(financialSource.projectTransferTender(params.tender_id));
+                            runnableKey.push(filter);
+                            break;
                         case 'contract_tree_1':
                             runnableRst.push(service.reportMemory.getContractTree(params.tender_id, 1));
                             runnableKey.push(filter);

+ 2 - 2
app/service/stage.js

@@ -680,7 +680,7 @@ module.exports = app => {
                         cb.value = param.zanLiePrice;
                         break;
                     case 'htjszl':
-                        cb.value = this.ctx.helper.sub(param.contractPrice, param.zanLiePrice);
+                        cb.value = this.ctx.helper.sub(this.ctx.helper.sub(param.contractPrice, param.zanLiePrice), param.jrgPrice);
                         break;
                     case 'kgyfk':
                         cb.value = param.startAdvance;
@@ -884,7 +884,7 @@ module.exports = app => {
                         cb.value = param.zanLiePrice;
                         break;
                     case 'htjszl':
-                        cb.value = this.ctx.helper.sub(param.contractPrice, param.zanLiePrice);
+                        cb.value = this.ctx.helper.sub(this.ctx.helper.sub(param.contractPrice, param.zanLiePrice), param.jrgPrice);
                         break;
                     case 'kgyfk':
                         cb.value = param.startAdvance;

+ 35 - 0
app/service/stage_jgcl.js

@@ -111,6 +111,40 @@ module.exports = app => {
             });
         }
 
+        async _insertDatas(data, result) {
+            const select = await this.getDataById(data.select);
+            if (!select) throw '选中的数据项不存在';
+
+            const insertData = [];
+            for (let i = 0; i < data.count; i++) {
+                insertData.push({
+                    uuid: this.uuid.v4(),
+                    add_sid: this.ctx.stage.id,
+                    add_uid: this.ctx.session.sessionUser.accountId,
+                    tid: this.ctx.tender.id,
+                    sid: this.ctx.stage.id,
+                    name: '',
+                    order: select.order + i,
+                });
+            }
+
+            const transaction = await this.db.beginTransaction();
+            try {
+                await transaction.query('Update ?? SET `order` = `order` + ? WHERE sid = ? and `order` >= ?', [this.tableName, data.count, this.ctx.stage.id, select.order]);
+                await transaction.insert(this.tableName, insertData);
+                await transaction.commit();
+            } catch(err) {
+                this.ctx.log(err);
+                await transaction.rollback();
+                throw '插入数据错误';
+            }
+
+            result.add = await this.getAllDataByCondition({
+                where: { sid: this.ctx.stage.id, uuid: this.ctx.helper._.map(insertData, 'uuid') }
+            });
+            result.update = await this.db.query('SELECT * FROM ?? where sid = ? and `order` >= ?', [this.tableName, this.ctx.stage.id, select.order + data.count]);
+        }
+
         async _delDatas (data) {
             const datas = data instanceof Array ? data : [data];
             const orgDatas = await this.getAllDataByCondition({where: {sid: this.ctx.stage.id, id: this.ctx.helper._.map(datas, 'id')} });
@@ -187,6 +221,7 @@ module.exports = app => {
                 if (data.del) {
                     result.del = await this._delDatas(data.del);
                 }
+                if (data.insert) await this._insertDatas(data.insert, result);
                 return result;
             } catch (err) {
                 if (err) result.err = err;

+ 37 - 0
app/service/stage_other.js

@@ -92,6 +92,42 @@ module.exports = app => {
             });
         }
 
+        async _insertDatas(data, result) {
+            const select = await this.getDataById(data.select);
+            if (!select) throw '选中的数据不存在';
+
+            const insertData = [];
+            for (let i = 0; i < data.count; i++) {
+                insertData.push({
+                    uuid: this.uuid.v4(),
+                    add_sid: this.ctx.stage.id,
+                    add_uid: this.ctx.session.sessionUser.accountId,
+                    add_time: new Date(),
+                    tid: this.ctx.tender.id,
+                    sid: this.ctx.stage.id,
+                    sorder: this.ctx.stage.order,
+                    name: '',
+                    order: select.order + i,
+                });
+            }
+
+            const transaction = await this.db.beginTransaction();
+            try {
+                await transaction.query('Update ?? SET `order` = `order` + ? WHERE sid = ? and `order` >= ?', [this.tableName, data.count, this.ctx.stage.id, select.order]);
+                await transaction.insert(this.tableName, insertData);
+                await transaction.commit();
+            } catch(err) {
+                this.ctx.log(err);
+                await transaction.rollback();
+                throw '插入数据错误';
+            }
+
+            result.add = await this.getAllDataByCondition({
+                where: { sid: this.ctx.stage.id, uuid: this.ctx.helper._.map(insertData, 'uuid') }
+            });
+            result.update = await this.db.query('SELECT * FROM ?? where sid = ? and `order` >= ?', [this.tableName, this.ctx.stage.id, select.order + data.count]);
+        }
+
         async _delDatas (data) {
             const datas = data instanceof Array ? data : [data];
             const orgDatas = await this.getAllDataByCondition({where: {sid: this.ctx.stage.id, id: this.ctx.helper._.map(datas, 'id')} });
@@ -153,6 +189,7 @@ module.exports = app => {
                 if (data.del) {
                     result.del = await this._delDatas(data.del);
                 }
+                if (data.insert) await this._insertDatas(data.insert, result);
                 return result;
             } catch (err) {
                 if (err.stack) {

+ 37 - 0
app/service/stage_safe_prod.js

@@ -100,6 +100,42 @@ module.exports = app => {
             });
         }
 
+        async _insertDatas(data, result) {
+            const select = await this.getDataById(data.select);
+            if (!select) throw '选中的数据不存在';
+
+            const insertData = [];
+            for (let i = 0; i < data.count; i++) {
+                insertData.push({
+                    uuid: this.uuid.v4(),
+                    add_sid: this.ctx.stage.id,
+                    add_uid: this.ctx.session.sessionUser.accountId,
+                    add_time: new Date(),
+                    tid: this.ctx.tender.id,
+                    sid: this.ctx.stage.id,
+                    sorder: this.ctx.stage.order,
+                    name: '',
+                    order: select.order + i,
+                });
+            }
+
+            const transaction = await this.db.beginTransaction();
+            try {
+                await transaction.query('Update ?? SET `order` = `order` + ? WHERE sid = ? and `order` >= ?', [this.tableName, data.count, this.ctx.stage.id, select.order]);
+                await transaction.insert(this.tableName, insertData);
+                await transaction.commit();
+            } catch(err) {
+                this.ctx.log(err);
+                await transaction.rollback();
+                throw '插入数据错误';
+            }
+
+            result.add = await this.getAllDataByCondition({
+                where: { sid: this.ctx.stage.id, uuid: this.ctx.helper._.map(insertData, 'uuid') }
+            });
+            result.update = await this.db.query('SELECT * FROM ?? where sid = ? and `order` >= ?', [this.tableName, this.ctx.stage.id, select.order + data.count]);
+        }
+
         async _delDatas (data) {
             const datas = data instanceof Array ? data : [data];
             const orgDatas = await this.getAllDataByCondition({where: {sid: this.ctx.stage.id, id: this.ctx.helper._.map(datas, 'id')} });
@@ -170,6 +206,7 @@ module.exports = app => {
                 if (data.del) {
                     result.del = await this._delDatas(data.del);
                 }
+                if (data.insert) await this._insertDatas(data.insert, result);
                 return result;
             } catch (err) {
                 throw err;

+ 37 - 0
app/service/stage_temp_land.js

@@ -96,6 +96,42 @@ module.exports = app => {
             });
         }
 
+        async _insertDatas(data, result) {
+            const select = await this.getDataById(data.select);
+            if (!select) throw '选中的数据不存在';
+
+            const insertData = [];
+            for (let i = 0; i < data.count; i++) {
+                insertData.push({
+                    uuid: this.uuid.v4(),
+                    add_sid: this.ctx.stage.id,
+                    add_uid: this.ctx.session.sessionUser.accountId,
+                    add_time: new Date(),
+                    tid: this.ctx.tender.id,
+                    sid: this.ctx.stage.id,
+                    sorder: this.ctx.stage.order,
+                    name: '',
+                    order: select.order + i,
+                });
+            }
+
+            const transaction = await this.db.beginTransaction();
+            try {
+                await transaction.query('Update ?? SET `order` = `order` + ? WHERE sid = ? and `order` >= ?', [this.tableName, data.count, this.ctx.stage.id, select.order]);
+                await transaction.insert(this.tableName, insertData);
+                await transaction.commit();
+            } catch(err) {
+                this.ctx.log(err);
+                await transaction.rollback();
+                throw '插入数据错误';
+            }
+
+            result.add = await this.getAllDataByCondition({
+                where: { sid: this.ctx.stage.id, uuid: this.ctx.helper._.map(insertData, 'uuid') }
+            });
+            result.update = await this.db.query('SELECT * FROM ?? where sid = ? and `order` >= ?', [this.tableName, this.ctx.stage.id, select.order + data.count]);
+        }
+
         async _delDatas (data) {
             const datas = data instanceof Array ? data : [data];
             const orgDatas = await this.getAllDataByCondition({where: {sid: this.ctx.stage.id, id: this.ctx.helper._.map(datas, 'id')} });
@@ -161,6 +197,7 @@ module.exports = app => {
                 if (data.del) {
                     result.del = await this._delDatas(data.del);
                 }
+                if (data.insert) await this._insertDatas(data.insert, result);
                 return result;
             } catch (err) {
                 throw err;

+ 36 - 0
app/service/stage_yjcl.js

@@ -91,6 +91,41 @@ module.exports = app => {
             });
         }
 
+        async _insertDatas(data, result) {
+            const select = await this.getDataById(data.select);
+            if (!select) throw '选中的数据不存在';
+
+            const insertData = [];
+            for (let i = 0; i < data.count; i++) {
+                insertData.push({
+                    uuid: this.uuid.v4(),
+                    add_sid: this.ctx.stage.id,
+                    add_uid: this.ctx.session.sessionUser.accountId,
+                    tid: this.ctx.tender.id,
+                    sid: this.ctx.stage.id,
+                    sorder: this.ctx.stage.order,
+                    name: '',
+                    m_order: select.m_order + i,
+                });
+            }
+
+            const transaction = await this.db.beginTransaction();
+            try {
+                await transaction.query('Update ?? SET `m_order` = `m_order` + ? WHERE sid = ? and `m_order` >= ?', [this.tableName, data.count, this.ctx.stage.id, select.m_order]);
+                await transaction.insert(this.tableName, insertData);
+                await transaction.commit();
+            } catch(err) {
+                this.ctx.log(err);
+                await transaction.rollback();
+                throw '插入数据错误';
+            }
+
+            result.add = await this.getAllDataByCondition({
+                where: { sid: this.ctx.stage.id, uuid: this.ctx.helper._.map(insertData, 'uuid') }
+            });
+            result.update = await this.db.query('SELECT * FROM ?? where sid = ? and `m_order` >= ?', [this.tableName, this.ctx.stage.id, select.m_order + data.count]);
+        }
+
         async _delDatas(data) {
             const datas = data instanceof Array ? data : [data];
             const orgDatas = await this.getAllDataByCondition({
@@ -172,6 +207,7 @@ module.exports = app => {
                 if (data.del) {
                     result.del = await this._delDatas(data.del);
                 }
+                if (data.insert) await this._insertDatas(data.insert, result);
                 return result;
             } catch (err) {
                 if (err) result.err = err;

+ 6 - 0
app/service/sub_proj_permission.js

@@ -566,6 +566,12 @@ module.exports = app => {
             if (updateArr.length > 0) transaction ? await transaction.updateRows(this.tableName, updateArr) : await this.db.updateRows(this.tableName, updateArr);
         }
 
+        async getRecentProjects(uid) {
+            const sql = 'SELECT sp.*, spp.last_time FROM ?? as spp LEFT JOIN ?? as sp ON spp.spid = sp.id WHERE spp.`uid` = ? AND spp.`last_time` is not null ORDER BY spp.`last_time` DESC LIMIT 10';
+            const sqlParams = [this.tableName, this.ctx.service.subProject.tableName, uid];
+            const result = await this.db.query(sql, sqlParams);
+            return result;
+        }
     }
 
     return subProjPermission;

+ 2 - 2
app/service/sub_project.js

@@ -699,11 +699,11 @@ module.exports = app => {
             for (const t of tenders) {
                 await this.ctx.service.tenderCache.loadTenderCache(t, this.ctx.session.sessionUser.accountId);
                 tp_cache.contract_price = this.ctx.helper.add(tp_cache.contract_price, t.contract_price);
-                tp_cache.ledger_tp = this.ctx.helper.add(tp_cache.ledger_tp, t.ledger_tp);
+                if (t.ledger_tp) tp_cache.ledger_tp = this.ctx.helper.add(tp_cache.ledger_tp, t.ledger_tp.total_price);
                 tp_cache.advance_tp = this.ctx.helper.add(tp_cache.advance_tp, t.advance_tp);
                 tp_cache.change_tp = this.ctx.helper.add(tp_cache.change_tp, t.change_tp);
 
-
+                if (!t.stage_tp) continue;
                 tp_cache.contract_tp = this.ctx.helper.add(tp_cache.contract_tp, t.stage_tp.contract_tp);
                 tp_cache.qc_tp = this.ctx.helper.add(tp_cache.qc_tp, t.stage_tp.qc_tp);
                 tp_cache.positive_qc_tp = this.ctx.helper.add(tp_cache.positive_qc_tp, t.stage_tp.positive_qc_tp);

+ 19 - 0
app/service/tender.js

@@ -591,6 +591,25 @@ module.exports = app => {
             return await this.db.update(this.tableName, updateData, { where: { id: tid } });
         }
 
+        // 仅要求授权表用tid/uid授权(条件:存在数据行即授权查看,取消授权直接删除行)
+        // filter目前仅支持['self', 'all'],可随时增加过滤类型
+        async getSpecList(specPermissionService, filter = 'self') {
+            const filterSql = filter === 'all' ? '' : ` AND t.id IN (SELECT tid FROM ${specPermissionService.tableName} WHERE uid = ?) `;
+            const sql = 'SELECT t.`id`, t.`project_id`, t.`name`, t.`status`, t.`category`, t.`user_id`, t.`create_time`, t.`spid`,' +
+                '    pa.`name` As `user_name`, pa.`role` As `user_role`, pa.`company` As `user_company` ' +
+                `  FROM ${this.tableName} As t Left Join ${this.ctx.service.projectAccount.tableName} As pa ON t.user_id = pa.id` +
+                '  WHERE t.`spid` = ? ' + filterSql +
+                '  ORDER BY CONVERT(t.`name` USING GBK) ASC';
+            const sqlParam = [this.ctx.subProject.id];
+            if (filter !== 'all') sqlParam.push(this.ctx.session.sessionUser.accountId);
+            const list = await this.db.query(sql, sqlParam);
+            for (const l of list) {
+                l.category = l.category && l.category !== '' ? JSON.parse(l.category) : null;
+            }
+
+            return list;
+        }
+
         /**
          * 获取你所参与的施工标段的列表
          *

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

@@ -188,4 +188,4 @@
     const openChangePlan = parseInt('<%- ctx.subProject.page_show.openChangePlan %>');
     const changePlanList = JSON.parse(unescape('<%- escape(JSON.stringify(changePlanList)) %>'));
 </script>
-<script src="/public/js/change.js"></script>
+<script src="/public/js/change.js?2025062302"></script>

+ 131 - 105
app/view/dashboard/workspace.ejs

@@ -31,9 +31,9 @@
             </div>
         <% } %>
         <div class="dashboard-height mx-3">
-            <div class="row agency-partheight">
+            <div class="row">
                 <div class="col-9 px-0">
-                    <div class="row ml-0">
+                    <div class="row ml-0 agency-partheight">
                         <div class="col-2 px-0">
                             <div class="card ml-3">
                                 <div class="card-header card-white d-flex justify-content-between">
@@ -46,9 +46,9 @@
                                             <h6 class="mt-2 text-muted text-center">参与标段</h6>
                                             <div class="row w-100">
                                                 <div class="col-7 pr-2 text-right">
-                                                    <span class="d-inline-block ws-icon ws-icon-primary">
-                                                    <i class="fa fa-list-ul"></i>
-                                                </span>
+                                            <span class="d-inline-block ws-icon ws-icon-primary">
+                                            <i class="fa fa-list-ul"></i>
+                                        </span>
                                                 </div>
                                                 <h4 class="col-5 pl-2 pr-0 my-0 data_tender_num"><%- dashboardStatus.all %></h4>
                                             </div>
@@ -58,9 +58,9 @@
                                             <h6 class="text-muted text-center">待办处理</h6>
                                             <div class="row w-100">
                                                 <div class="col-7 pr-2 text-right">
-                                                    <span class="d-inline-block ws-icon ws-icon-primary">
-                                                        <i class="fa fa-check-square-o"></i>
-                                                    </span>
+                                            <span class="d-inline-block ws-icon ws-icon-primary">
+                                                <i class="fa fa-check-square-o"></i>
+                                            </span>
                                                 </div>
                                                 <h4 class="col-5 pl-2 pr-0 my-0"><%- dashboardStatus.dashboard %></h4>
                                             </div>
@@ -70,9 +70,9 @@
                                             <h6 class="text-muted text-center">紧急审批</h6>
                                             <div class="row w-100">
                                                 <div class="col-7 pr-2 text-right">
-                                                    <span class="d-inline-block ws-icon ws-icon-danger">
-                                                        <i class="fa fa-exclamation-triangle"></i>
-                                                    </span>
+                                            <span class="d-inline-block ws-icon ws-icon-danger">
+                                                <i class="fa fa-exclamation-triangle"></i>
+                                            </span>
                                                 </div>
                                                 <h4 class="col-5 pl-2 pr-0 my-0 text-danger"><%- dashboardStatus.worry %></h4>
                                             </div>
@@ -82,9 +82,9 @@
                                             <h6 class="text-muted text-center">预警审批</h6>
                                             <div class="row w-100">
                                                 <div class="col-7 pr-2 text-right">
-                                                    <span class="d-inline-block ws-icon ws-icon-warning">
-                                                        <i class="fa fa-bell"></i>
-                                                    </span>
+                                            <span class="d-inline-block ws-icon ws-icon-warning">
+                                                <i class="fa fa-bell"></i>
+                                            </span>
                                                 </div>
                                                 <h4 class="col-5 pl-2 pr-0 my-0 text-warning"><%- dashboardStatus.early %></h4>
                                             </div>
@@ -423,114 +423,139 @@
                             </div>
                         </div>
                     </div>
-                </div>
-                <!--项目消息通知-->
-                <div class="col-3 pl-0">
-                    <div class="card ml-3">
-                        <div class="card-header card-white d-flex justify-content-between">
-                            <div class="card-big-htext"><span class="card-icon mr-2"></span>项目通知</div>
-<!--                            <div class="mt-1"><a class="text-secondary" href="/dashboard/msg">查看全部</a></div>-->
-                        </div>
-                        <div class="card-body p-0">
-                            <div class="contant-height-three">
-                                <ul class="list-group list-group-flush msg-height-list">
-                                    <% if (msgList.length === 0) { %>
-                                        <!--没有通知-->
-                                        <li class="list-group-item text-muted text-center p-5">
-                                            <img src="/public/images/nulllogo.png" />
-                                            <p class="pt-2 text-center mb-0">暂时没有通知</p>
-                                        </li>
-                                    <% } else { %>
-                                        <% for (const msg of msgList) { %>
-                                            <li class="list-group-item text-muted">
-                                                <a href="#view-msg" msg-id="<%= msg.id %>" data-toggle="modal" data-target="#view-msg"<% if (msg.istop !== '0') { %> class="text-danger">
-                                                    <i class="fa fa-exclamation-triangle" ></i> <% } else { %>><% } %><%- msg.title %></a><br><%- moment(msg.release_time*1000).format('YYYY/MM/DD HH:mm') %></li>
-                                        <% } %>
-                                    <% } %>
-                                </ul>
-                            </div>
-                        </div>
-                    </div>
-                </div>
-            </div>
-            <div class="row agency-partheight">
-                <div class="col-6 px-0">
-                    <div class="card ml-3">
-                        <div class="card-header card-white d-flex justify-content-between">
-                            <div class="card-big-htext"><span class="card-icon mr-2"></span>参与的标段进度</div>
-                        </div>
-                        <div class="card-body p-0">
-                            <div class="contant-height-one">
-                                <div id="jlchart" style="height: 100%; width: 100%;overflow: hidden"></div>
+                    <div class="row ml-0 agency-partheight">
+                        <div class="col-8 px-0">
+                            <div class="card ml-3">
+                                <div class="card-header card-white d-flex justify-content-between">
+                                    <div class="card-big-htext"><span class="card-icon mr-2"></span>参与的标段进度</div>
+                                </div>
+                                <div class="card-body p-0">
+                                    <div class="contant-height-one">
+                                        <div id="jlchart" style="height: 100%; width: 100%;overflow: hidden"></div>
+                                    </div>
+                                </div>
                             </div>
                         </div>
-                    </div>
-                </div>
-                <div class="col-3 px-0">
-                    <div class="card ml-3">
-                        <div class="card-header card-white d-flex justify-content-between">
-                            <div class="card-big-htext"><span class="card-icon mr-2"></span>审批日历</div>
-                        </div>
-                        <div class="card-body p-0">
-                            <div class="contant-height-one">
-                                <div class="mx-4 mt-2 text-center">
-                                    <style>
-                                        #calendar .datepicker {
-                                            width: 100%;
-                                            min-width: 250px;
-                                            text-align: center;
-                                        }
+                        <div class="col-4 pl-0 pr-3">
+                            <div class="card ml-3">
+                                <div class="card-header card-white d-flex justify-content-between">
+                                    <div class="card-big-htext"><span class="card-icon mr-2"></span>审批日历</div>
+                                </div>
+                                <div class="card-body p-0">
+                                    <div class="contant-height-one">
+                                        <div class="mx-4 mt-2 text-center">
+                                            <style>
+                                                #calendar .datepicker {
+                                                    width: 100%;
+                                                    min-width: 250px;
+                                                    text-align: center;
+                                                }
 
-                                        #calendar .datepicker--cell {
-                                            height: 45px;
-                                        }
+                                                #calendar .datepicker--cell {
+                                                    height: 45px;
+                                                }
 
-                                        .shenpi-calendar {
-                                            background-color: #ffb8ff;
-                                        }
-                                        .shenpi-calendar:hover {
-                                            background-color: #fda5fd;
-                                        }
-                                    </style>
-                                    <div id="calendar"></div>
+                                                .shenpi-calendar {
+                                                    background-color: #ffb8ff;
+                                                }
+                                                .shenpi-calendar:hover {
+                                                    background-color: #fda5fd;
+                                                }
+                                            </style>
+                                            <div id="calendar"></div>
+                                        </div>
+                                    </div>
                                 </div>
                             </div>
                         </div>
                     </div>
                 </div>
                 <div class="col-3 pl-0">
-                    <div class="card ml-3">
-                        <div class="card-header card-white d-flex justify-content-between">
-                            <div class="card-big-htext"><span class="card-icon mr-2"></span>技术支持</div>
-                        </div>
-                        <div class="card-body p-0">
-                            <!--<h6 class="card-subtitle mb-2 text-muted"><%= salesmanData.username %></h6>-->
-                            <div class="contant-height-one">
-                                <div class="mx-4">
-                                    <ul class="list-group list-group-flush">
-                                        <li class="list-group-item" data-toggle="tooltip" data-placement="bottom" title="腾讯QQ"><i class="fa fa-qq"></i> <%=salesmanData.qq%></li>
-                                        <li class="list-group-item" data-toggle="tooltip" data-placement="bottom" title="手机号码"><i class="fa fa-tablet"></i> <%=salesmanData.telephone%></li>
-                                        <li class="list-group-item" data-toggle="tooltip" data-placement="bottom" title="固定电话"><i class="fa fa-phone"></i> <%=salesmanData.fixedphone%></li>
-                                        <% if (projectData.qrcode_json) { %>
-                                            <li class="list-group-item container-fluid">
-                                                <div class="row qrcode-height">
-                                                    <% for (const qr of JSON.parse(projectData.qrcode_json)) { %>
-                                                        <div class="col-sm text-center"><img src="/<%= qr.path %>" width="100%" style="max-width: 180px"><div class="text-center"><%= qr.name %></div></div>
-                                                    <% } %>
-                                                </div>
+                    <!--项目消息通知-->
+                    <div class="row ml-3 pr-3 pb-3 three-partheight">
+                        <div class="col-12 card px-0">
+                            <div class="card-header card-white d-flex justify-content-between">
+                                <div class="card-big-htext"><span class="card-icon mr-2"></span>最近使用</div>
+                            </div>
+                            <div class="card-body p-0">
+                                <div class="contant-height-three">
+                                    <ul class="list-group list-group-flush msg-height-list">
+                                        <% if (recentProjects.length === 0) { %>
+                                            <li class="list-group-item text-muted text-center p-5">
+                                                <img src="/public/images/nulllogo.png" />
+                                                <p class="pt-2 text-center mb-0">暂无使用</p>
                                             </li>
                                         <% } else { %>
-                                            <li class="list-group-item container-fluid">
-                                                <div class="row qrcode-height">
-                                                    <div class="col-sm text-center"><img src="/public/images/erweima.jpg" width="100%" style="max-width: 180px"><div class="text-center">虾米造价</div></div>
-                                                </div>
+                                            <% for (const p of recentProjects) { %>
+                                                <li class="list-group-item text-muted"><a href="/sp/<%- p.id %>/dashboard" target="_blank" class=""><%- p.name %></a><br><%- moment(p.last_time).format('YYYY/MM/DD HH:mm') %></li>
+                                            <% } %>
+                                        <% } %>
+                                    </ul>
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+                    <div class="row ml-3 pr-3 pb-3 three-partheight">
+                        <div class="col-12 card px-0">
+                            <div class="card-header card-white d-flex justify-content-between">
+                                <div class="card-big-htext"><span class="card-icon mr-2"></span>项目通知</div>
+                                <!--                            <div class="mt-1"><a class="text-secondary" href="/dashboard/msg">查看全部</a></div>-->
+                            </div>
+                            <div class="card-body p-0">
+                                <div class="contant-height-three">
+                                    <ul class="list-group list-group-flush msg-height-list">
+                                        <% if (msgList.length === 0) { %>
+                                            <!--没有通知-->
+                                            <li class="list-group-item text-muted text-center p-5">
+                                                <img src="/public/images/nulllogo.png" />
+                                                <p class="pt-2 text-center mb-0">暂时没有通知</p>
                                             </li>
+                                        <% } else { %>
+                                            <% for (const msg of msgList) { %>
+                                                <li class="list-group-item text-muted">
+                                                    <a href="#view-msg" msg-id="<%= msg.id %>" data-toggle="modal" data-target="#view-msg"<% if (msg.istop !== '0') { %> class="text-danger">
+                                                        <i class="fa fa-exclamation-triangle" ></i> <% } else { %>><% } %><%- msg.title %></a><br><%- moment(msg.release_time*1000).format('YYYY/MM/DD HH:mm') %></li>
+                                            <% } %>
                                         <% } %>
                                     </ul>
                                 </div>
                             </div>
                         </div>
                     </div>
+                    <div class="row ml-3 pr-3 pb-3 three-partheight">
+                            <div class="col-12 card px-0">
+                                <div class="card-header card-white d-flex justify-content-between">
+                                    <div class="card-big-htext"><span class="card-icon mr-2"></span>技术支持</div>
+                                </div>
+                                <div class="card-body p-0">
+                                    <!--<h6 class="card-subtitle mb-2 text-muted"><%= salesmanData.username %></h6>-->
+                                    <div class="contant-height-three">
+                                        <div class="mx-4">
+                                            <ul class="list-group list-group-flush">
+                                                <li class="list-group-item pt-0 pb-2" data-toggle="tooltip" data-placement="bottom" title="腾讯QQ"><i class="fa fa-qq"></i> <%=salesmanData.qq%></li>
+                                                <li class="list-group-item py-2" data-toggle="tooltip" data-placement="bottom" title="手机号码"><i class="fa fa-tablet"></i> <%=salesmanData.telephone%></li>
+                                                <li class="list-group-item py-2" data-toggle="tooltip" data-placement="bottom" title="固定电话"><i class="fa fa-phone"></i> <%=salesmanData.fixedphone%></li>
+                                                <% if (projectData.qrcode_json) { %>
+                                                    <li class="list-group-item py-2 container-fluid">
+                                                        <div class="row qrcode-height">
+                                                            <% for (const qr of JSON.parse(projectData.qrcode_json)) { %>
+                                                                <div class="col-sm text-center"><img src="/<%= qr.path %>" width="100%" style="max-width: 90px"><div class="text-center"><%= qr.name %></div></div>
+                                                            <% } %>
+                                                        </div>
+                                                    </li>
+                                                <% } else { %>
+                                                    <li class="list-group-item py-2 container-fluid">
+                                                        <div class="row qrcode-height">
+                                                            <div class="col-sm text-center"><img src="/public/images/erweima.jpg" width="100%" style="max-width: 90px"><div class="text-center">虾米造价</div></div>
+                                                        </div>
+                                                    </li>
+                                                <% } %>
+                                            </ul>
+                                        </div>
+                                    </div>
+                                </div>
+                            </div>
+                        </div>
                 </div>
             </div>
         </div>
@@ -569,11 +594,12 @@
             var maintainHeight = getObjHeight($(".maintain-height"));
             $('.dashboard-height').height($(window).height()- mobileHeight - maintainHeight - 34 - 16);
             $('.agency-partheight').height($('.dashboard-height').height()/2);
+            $('.three-partheight').height($('.dashboard-height').height()/3 - 18);
             // $('.agency-partheight').eq(1).height($('.sjs-height-0').height() - $('.agency-partheight').eq(0).height());
             $('.contant-height-one').height($('.agency-partheight').height() - 52 - 20);
             $('.contant-height-two').height($('.agency-partheight').height() - 52 - getObjHeight($(".echart-height")) - 20);
-            $('.contant-height-three').height($('.agency-partheight').height() - 52 - getObjHeight($(".addmsg-height")) - 20);
-            $('.qrcode-height').height($('.contant-height-one').height() - 43 - 44 - 44 - 25);
+            $('.contant-height-three').height($('.three-partheight').height() - 52 - 20);
+            $('.qrcode-height').height($('.contant-height-three').height() - 43 - 44 - 44 - 25);
             $('.qrcode-height .col-sm').css('padding-top', ($('.qrcode-height').height()/2 - ($('.qrcode-height img').height()+18)/2) + 'px');
             // resizeChart();
         }

+ 0 - 0
app/view/drawing/as_built.ejs


+ 0 - 0
app/view/drawing/design.ejs


+ 55 - 0
app/view/drawing/tender.ejs

@@ -0,0 +1,55 @@
+<% include ../tender/list_sub_menu.ejs %>
+<div class="panel-content">
+    <div class="panel-title">
+        <div class="title-main d-flex">
+            <% include ../tender/list_sub_mini_menu.ejs %>
+        </div>
+    </div>
+    <div class="content-wrap">
+        <div class="sjs-height-0" style="background-color: #fff">
+            <div class="c-body">
+                <% if (!tenderList || tenderList.length === 0) { %>
+                <div class="jumbotron" id="no-project">
+                    <h3 class="display-6">还没有标段数据</h3>
+                </div>
+                <% } else { %>
+                <table class="table table-bordered">
+                    <tr class="text-center">
+                        <th style="min-width: 200px" class="align-middle">标段名称</th>
+                        <th width="10%" class="align-middle">创建时间</th>
+                        <% if (ctx.session.sessionUser.is_admin) { %>
+                        <th width="10%" class="align-middle">操作</th>
+                        <% } %>
+                    </tr>
+                    <tbody id="projectList">
+                    </tbody>
+                </table>
+                <% } %>
+            </div>
+        </div>
+    </div>
+</div>
+<script>
+    const tenders = JSON.parse(unescape('<%- escape(JSON.stringify(tenderList)) %>'));
+    const category = JSON.parse(unescape('<%- escape(JSON.stringify(categoryData)) %>'));
+    const selfCategoryLevel = '<%- (selfCategoryLevel || '') %>';
+
+    $.subMenu({
+        menu: '#sub-menu', miniMenu: '#sub-mini-menu', miniMenuList: '#mini-menu-list',
+        toMenu: '#to-menu', toMiniMenu: '#to-mini-menu',
+        key: 'list.menu.1.0.0',
+        miniHint: '#sub-mini-hint', hintKey: 'menu.hint.1.0.1', defaultMenu: '<%- ((!ctx.subProject.page_show.openTenderContract && !ctx.subProject.page_show.openConstruction) ? 'miniMenu' : '') %>',
+        callback: function (info) {
+            if (info.mini) {
+                $('.panel-title').addClass('fluid');
+                $('#sub-menu').removeClass('panel-sidebar');
+                $('.c-body table thead').css('left', '56px');
+            } else {
+                $('.panel-title').removeClass('fluid');
+                $('#sub-menu').addClass('panel-sidebar');
+                $('.c-body table thead').css('left', '176px');
+            }
+            autoFlashHeight();
+        }
+    });
+</script>

+ 0 - 0
app/view/drawing/tender_modal.ejs


+ 1 - 1
app/view/file/file_modal.ejs

@@ -1,6 +1,6 @@
 <% if (ctx.session.sessionUser.is_admin) { %>
 <div class="modal" id="filing-permission">
-    <div class="modal-dialog modal-lg" role="document">
+    <div class="modal-dialog modal-lgx" role="document">
         <div class="modal-content">
             <div class="modal-header">
                 <h5 class="modal-title">授权用户</h5>

+ 0 - 1
app/view/ledger/explode.ejs

@@ -169,7 +169,6 @@
                                 <a href="javascript: void(0);" class="btn btn-sm btn-primary disabled">上传签约清单</a>
                                 <% } %>
                                 <a href="#db-full-code" data-toggle="modal" data-target="#db-full-code" class="btn btn-sm btn-primary">转换长编号</a>
-                                <a href="/tender/<%- ctx.tender.id %>/deal/download/签约清单.xlsx" class="btn btn-sm btn-primary" style="display: none">下载签约清单</a>
                             </div>
                             <div class="ml-auto mr-1">
                                 <div class="dropdown">

+ 1 - 0
app/view/login/login.ejs

@@ -26,6 +26,7 @@
     </div>
     <div class="side-border ml-4 mr-4"></div>
     <div class="top-subtitle">管好项目每一分钱</div>
+    <h5 class="ml-auto logo-big-title text-danger mr-3 login-tip"><% if (projectData && ['Y3T2Q','P0505'].indexOf(projectData.code.toUpperCase()) !== -1) { %>当前处于办公网络非涉密网络,严禁传输处理涉密信息!<% } %></h5>
 </div>
 <div class="container">
     <div class="d-flex justify-content-center align-items-center content-center">

+ 26 - 1
app/view/material/audit_modal.ejs

@@ -263,7 +263,7 @@
         <!--审批通过-->
         <div class="modal fade sp-location-list" id="sp-done" data-backdrop="static">
             <div class="modal-dialog modal-lg" role="document">
-                <form class="modal-content" action="<%- preUrl %>/audit/check" method="post" onsubmit="return auditCheck(0);">
+                <form class="modal-content" action="<%- preUrl %>/audit/check" method="post" id="audit-check0">
                     <div class="modal-header">
                         <h5 class="modal-title">审批通过</h5>
                     </div>
@@ -988,6 +988,31 @@
         return false;
     }
 
+    $('#audit-check0').submit(function (e) {
+        if (auditCheck(0)) {
+            const data = {
+                opinion: $('[name=opinion]', this).val(),
+                checkType: parseInt($('[name=checkType]', this).val()),
+            };
+            postData(this.action, data, function () {
+                window.location.reload();
+            });
+        }
+        return false;
+    });
+    $('#audit-check1').submit(function (e) {
+        if (auditCheck(1)) {
+            const data = {
+                opinion: $('[name=opinion]', this).val(),
+                checkType: parseInt($('[name=checkType]:checked', this).val()),
+            };
+            postData(this.action, data, function () {
+                window.location.reload();
+            });
+        }
+        return false;
+    });
+
     // 展开历史审核记录
     $('.modal-body #fold-btn').click(function () {
         const type = $(this).data('target')

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

@@ -15,7 +15,7 @@
                 </nav>
                 <% } %>
                 <div class="row m-0">
-                    <div class="col-9 my-3">
+                    <div class="col-10 my-3">
                         <% if (!signData) { %>
                             <div id="set_ukey" class="form-group">
                                 <label>网证通UKey</label>
@@ -54,7 +54,7 @@
                                 <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>
+                                    <small class="form-text text-danger">建议签名图片大小为160x160px,四边满版裁切、无留白,格式PNG透明背景(适用于报表打印,签章直径为4.2厘米的情况)</small>
                                 </div>
                                 <div class="form-group show-qrcode">
                                     <label>在线手写签名</label>

+ 3 - 3
app/view/profile/sign.ejs

@@ -15,7 +15,7 @@
                 </nav>
                 <% } %>
                 <div class="row m-0">
-                    <div class="col-5 my-2">
+                    <div class="col-10 my-2">
                         <!--账号资料-->
                         <form>
                             <div class="pb-2"><b>签字管理</b></div>
@@ -32,7 +32,7 @@
                             <div class="form-group 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>
+                                <small class="form-text text-danger">建议签名图片大小为160x160px,四边满版裁切、无留白,格式PNG透明背景(适用于报表打印,签章直径为4.2厘米的情况)</small>
                             </div>
                             <div class="form-group show-qrcode">
                                 <label>在线手写签名</label>
@@ -44,7 +44,7 @@
                             <div class="form-group">
                                 <label for="stamp">上传签章(最多上传5个)</label>
                                 <input type="file" class="form-control-file" multiple id="stamp-upload">
-                                <small class="form-text text-danger">图片大小为600x300,格式PNG透明背景。</small>
+                                <small class="form-text text-danger">建议签章图片大小为160x160px,四边满版裁切、无留白,格式PNG透明背景(适用于报表打印,签章直径为4.2厘米的情况)</small>
                                 <small class="form-text text-danger">点击预览签章图片上删除按钮,即可移除签章</small>
                             </div>
                             <!--<button type="button" class="btn btn-sm btn-danger" id="delete-stamp">移除签章</button>-->

+ 2 - 0
app/view/setting/user_permission.ejs

@@ -96,6 +96,8 @@
     const allPermission = JSON.parse('<%- permissionStr %>');
     const noticeAgainConst = JSON.parse(unescape('<%- escape(JSON.stringify(noticeAgainConst)) %>'));
     const unitList = JSON.parse(unescape('<%- escape(JSON.stringify(unitList)) %>'));
+    const accountData = JSON.parse(unescape('<%- escape(JSON.stringify(accountData)) %>'));
+    const fujianOssPath = JSON.parse(unescape('<%- escape(JSON.stringify(fujianOssPath)) %>'));
 </script>
 <script src="/public/js/bootstrap/drawer.js"></script>
 <script src="/public/js/setting.js"></script>

+ 21 - 1
app/view/setting/user_permission_modal.ejs

@@ -22,7 +22,10 @@
                                     <input class="form-check-input" type="<%= permission[pm].type %>" id="<%= pm %>_<%= ip.value %>" name="<%= pm %><% if (permission[pm].type === 'checkbox') { %>[]<% } %>" value="<%= ip.value %>">
                                     <label class="form-check-label" for="<%= pm %>_<%= ip.value %>"><%= ip.title %></label>
                                     <% if (ip.hint && ip.hintIcon) { %>
-                                        <a href="" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="<%- ip.hint%>"><i class="fa <%- ip.hintIcon%>"></i></a>
+                                        <a href="javascript:void(0);" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="<%- ip.hint%>"><i class="fa <%- ip.hintIcon%>"></i></a>
+                                    <% } %>
+                                    <% if (ip.text_modal && ip.modal) { %>
+                                        <a href="javascript:void(0);" id="<%- ip.modal %>" class="ml-2" ><%- ip.text_modal %></a>
                                     <% } %>
                                 </div>
                             <% } %>
@@ -59,6 +62,7 @@
         </div>
         <div class="modal-footer">
             <input type="hidden" name="id" value="">
+            <input type="hidden" name="unit_sign_path" value="">
             <button type="button" class="btn btn-secondary btn-sm" data-drawer-close="">关闭</button>
             <button type="submit" class="btn btn-primary btn-sm">提交修改</button>
         </div>
@@ -155,6 +159,22 @@
         </div>
     </div>
 </div>
+<div class="modal fade" id="sign_permission_modal" data-backdrop="static">
+    <div class="modal-dialog modal-lg" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">选择章</h5>
+            </div>
+            <div class="modal-body">
+                <div class="row" id="unit-sign"></div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">关闭</button>
+                <button type="button" class="btn btn-primary btn-sm" id="set_unit_sign">确定选择</button>
+            </div>
+        </div>
+    </div>
+</div>
 <script>
     $(function (){
        $('input[name="wx_send_type"]').on('click', function () {

+ 19 - 8
app/view/setting/user_unit.ejs

@@ -25,7 +25,7 @@
                 <div class="tab-content m-3">
                     <div class="tab-pane active">
                         <div class="row">
-                            <div class="col-6">
+                            <div class="col-5">
                                 <table class="table table-hover table-bordered table-sm">
                                     <thead id="unit_header">
                                     <tr>
@@ -51,14 +51,22 @@
                                 </table>
                             </div>
                             <% if (unitList.length > 0) { %>
-                            <div class="col-6">
+                            <div class="col-7">
                                 <div>
                                     <div class="row px-3">
                                         <div>
                                             <legend>单位详情</legend>
                                         </div>
                                         <div class="ml-auto">
+                                            <div class="d-inline-block">
+                                                <div class="form-check form-check-inline">
+                                                    <input class="form-check-input" type="checkbox" id="unit_user_permission" value="1" <% if (unitList[0].sign_permission) { %>checked=""<% } %>>
+                                                    <label class="form-check-label" for="unit_user_permission">授权所有人使用单位章</label>
+                                                </div>
+                                            </div>
+                                            <div class="d-inline-block">
                                             <a href="#del-company" data-toggle="modal" data-target="#del-company" id="del-modal-btn" class="btn btn-outline-danger btn-sm btn-block">删除单位</a>
+                                            </div>
                                         </div>
                                     </div>
                                     <table class="table table-hover table-bordered table-sm" id="one_unit">
@@ -101,20 +109,23 @@
                                         </tr>
                                     </table>
                                 </div>
-                                <div <% if (unitList[0].sign_path) { %>style="display: none" <% } %> id="upload-sign" class="form-group">
+                                <div id="upload-sign" class="form-group">
                                     <label for="sign-upload">上传签章</label>
-                                    <input type="file" class="form-control-file" id="sign-upload">
-                                    <small class="form-text text-danger">图片大小为500x500px,格式PNG透明背景。</small>
+                                    <input type="file" class="form-control-file" id="sign-upload" multiple>
+                                    <small class="form-text text-danger">建议签章图片大小为160x160px,四边满版裁切、无留白,格式PNG透明背景(适用于报表打印,签章直径为4.2厘米的情况)</small>
                                 </div>
-                                <button <% if (!unitList[0].sign_path) { %>style="display: none" <% } %> type="button" class="btn btn-sm btn-danger" id="delete-sign">移除签章</button>
+                                <button type="button" data-toggle="modal" data-target="#mseal" class="btn btn-sm btn-primary" id="manage-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:210px">
+                                            <div class="position-absolute fixed-top" id="sign-show" style="left:60px;top:210px">
                                                 <% if (unitList[0].sign_path) { %>
-                                                    <img src="<%- fujianOssPath + unitList[0].sign_path %>" width="120">
+                                                    <% const paths = unitList[0].sign_path.split('&%&') %>
+                                                    <% for (const sp of paths.slice(0, 5)) { %>
+                                                        <img src="<%- fujianOssPath + sp %>" width="120" class="mr-1">
+                                                    <% } %>
                                                 <% } %>
                                             </div>
                                         </div>

+ 21 - 0
app/view/setting/user_unit_modal.ejs

@@ -46,3 +46,24 @@
         </div>
     </div>
 </div>
+<!--管理签章-->
+<div class="modal fade" id="mseal" data-backdrop="static">
+    <div class="modal-dialog modal-lg" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">管理签章</h5>
+            </div>
+            <div class="modal-body">
+                <div id="upload-sign" class="form-group">
+                    <label for="sign-upload">上传签章</label>
+                    <input type="file" class="form-control-file" id="manage-sign-upload" multiple>
+                    <small class="form-text text-danger">建议签章图片大小为160x160px,四边满版裁切、无留白,格式PNG透明背景(适用于报表打印,签章直径为4.2厘米的情况)</small>
+                </div>
+                <div class="row" id="manage-unit-sign"></div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">关闭</button>
+            </div>
+        </div>
+    </div>
+</div>

+ 8 - 3
app/view/shares/delete_hint_modal.ejs

@@ -7,7 +7,7 @@
             </div>
             <div class="modal-body">
                 <h6 id="del-node-hint">确认删除「当前节点及子项」?</h6>
-                <h6>删除后,数据无法恢复,请谨慎操作。</h6>
+                <h6 id="show-del-hint">删除后,数据无法恢复,请谨慎操作。</h6>
             </div>
             <div class="modal-footer">
                 <button type="button" class="btn btn-sm btn-danger" id="del-node-ok" data-dismiss="modal">确定删除</button>
@@ -17,12 +17,17 @@
     </div>
 </div>
 <script>
-    const deleteAfterHint = function (fun, hint = '') {
+    const deleteAfterHint = function (fun, hint = '', show = true) {
         $('#del-node').modal('show');
         $('#del-node-ok').bind('click', fun);
         if (hint) $('#del-node-hint').html(hint);
+        if (show) {
+            $('#show-del-hint').show();
+        } else {
+            $('#show-del-hint').hide();
+        }
         $('#del-node').bind('hidden.bs.modal', function () {
             $('#del-node-ok').unbind('click');
         });
     }
-</script>
+</script>

+ 0 - 1
app/view/sp_setting/manage.ejs

@@ -338,7 +338,6 @@
         </div>
     </div>
 </div>
-<!--<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>-->
 <script>
     const accountGroup = JSON.parse(unescape('<%- escape(JSON.stringify(accountGroup)) %>'));
     const accountList = JSON.parse(unescape('<%- escape(JSON.stringify(accountList)) %>'));

+ 1 - 1
app/view/stage/deal.ejs

@@ -96,7 +96,7 @@
                     <table class="table table-bordered">
                         <tr><th></th><th>可选基数</th><th>计算代号</th></tr>
                         <tr><td>1</td><td>签约合同价</td><td>htj</td></tr>
-                        <tr><td>2</td><td>签约合同价(不含暂列金)</td><td>htjszl</td></tr>
+                        <tr><td>2</td><td>签约合同价(不含暂列金、计日工)</td><td>htjszl</td></tr>
                         <tr><td>3</td><td>签约开工预付款</td><td>kgyfk</td></tr>
                         <tr><td>4</td><td>签约材料预付款</td><td>clyfk</td></tr>
                         <tr><td>5</td><td>本期完成计量</td><td>bqwc</td></tr>

Разница между файлами не показана из-за своего большого размера
+ 2 - 0
app/view/tender/detail.ejs


+ 23 - 18
app/view/tender/detail_modal.ejs

@@ -1455,21 +1455,22 @@
         SpreadJsObj.massOperationSheet(sheet, function () {
             sheet.defaults.rowHeight = 21;
             sheet.setColumnCount(2);
-            sheet.setRowCount(5);
+            sheet.setRowCount(6);
             sheet.setColumnWidth(0, 200);
             sheet.setColumnWidth(1, 200);
-            sheet.getRange(0, 0, 5, 1).vAlign(1).backColor('#e4e7ea').locked(true);
-            sheet.getRange(0, 1, 5, 1).vAlign(1).hAlign(2).locked(false);
+            sheet.getRange(0, 0, 6, 1).vAlign(1).backColor('#e4e7ea').locked(true);
+            sheet.getRange(0, 1, 6, 1).vAlign(1).hAlign(2).locked(false);
             sheet.setText(0, 0, '签约合同价');
             sheet.setText(1, 0, '暂列金额');
-            sheet.setText(2, 0, '签约合同价(不含暂列金)');
-            sheet.setText(3, 0, '签约开工预付款');
-            sheet.setText(4, 0, '签约材料预付款');
-            sheet.setText(5, 0, '安全生产费');
-            sheet.getCell(2, 1).locked(true);
+            sheet.setText(2, 0, '计日工');
+            sheet.setText(3, 0, '签约合同价(不含暂列金、计日工)');
+            sheet.setText(4, 0, '签约开工预付款');
+            sheet.setText(5, 0, '签约材料预付款');
+            sheet.setText(6, 0, '安全生产费');
+            sheet.getCell(3, 1).locked(true);
             const lineBorder = new spreadNS.LineBorder('#dee2e6', spreadNS.LineStyle.thin);
-            sheet.getRange(0, 0, 5, 2).setBorder(lineBorder, {all: true});
-            sheet.getRange(0, 0, 5, 2).formatter('@');
+            sheet.getRange(0, 0, 6, 2).setBorder(lineBorder, {all: true});
+            sheet.getRange(0, 0, 6, 2).formatter('@');
             sheet.setSelection(0, 1, 1, 1);
         });
 
@@ -1504,7 +1505,8 @@
         function calcHtjMinusZlj() {
             const htj = _.toNumber(sheet.getText(0, 1));
             const zlj = _.toNumber(sheet.getText(1, 1));
-            sheet.setValue(2, 1, ZhCalc.sub(htj, zlj));
+            const jrg = _.toNumber(sheet.getText(2, 1));
+            sheet.setValue(3, 1, ZhCalc.sub(ZhCalc.sub(htj, zlj), jrg));
         }
 
         spread.bind(spreadNS.Events.EditEnded, function (e, info) {
@@ -1516,7 +1518,7 @@
                 toastr.warning('请输入不超过万亿的数字');
                 info.sheet.setText(info.row, info.col, '0');
             }
-            if (info.row === 0 || info.row === 1) {
+            if ([0, 1, 2].indexOf(info.row) >= 0) {
                 calcHtjMinusZlj();
             }
         });
@@ -1602,9 +1604,10 @@
         function loadDealProperty() {
             sheet.setValue(0, 1, property.deal_param.contractPrice);
             sheet.setValue(1, 1, property.deal_param.zanLiePrice);
-            sheet.setValue(2, 1, ZhCalc.sub(property.deal_param.contractPrice, property.deal_param.zanLiePrice));
-            sheet.setValue(3, 1, property.deal_param.startAdvance);
-            sheet.setValue(4, 1, property.deal_param.materialAdvance);
+            sheet.setValue(2, 1, property.deal_param.jrgPrice);
+            sheet.setValue(3, 1, ZhCalc.sub(ZhCalc.sub(property.deal_param.contractPrice, property.deal_param.zanLiePrice), property.deal_param.jrgPrice));
+            sheet.setValue(4, 1, property.deal_param.startAdvance);
+            sheet.setValue(5, 1, property.deal_param.materialAdvance);
             // sheet.setValue(5, 1, property.deal_param.safeAdvance);
             cap100sheet.setValue(0, 1, property.deal_param.safeAdvance);
             cap100sheet.setValue(1, 1, property.deal_param.dustAdvance);
@@ -1613,8 +1616,9 @@
         function setReadOnly (readOnly) {
             sheet.getCell(0, 1).locked(readOnly);
             sheet.getCell(1, 1).locked(readOnly);
-            sheet.getCell(3, 1).locked(readOnly);
+            sheet.getCell(2, 1).locked(readOnly);
             sheet.getCell(4, 1).locked(readOnly);
+            sheet.getCell(5, 1).locked(readOnly);
             // sheet.getCell(5, 1).locked(readOnly);
             cap100sheet.getCell(0, 1).locked(readOnly);
             cap100sheet.getCell(1, 1).locked(readOnly);
@@ -1623,8 +1627,9 @@
             const result = {};
             result.contractPrice = _.toNumber(sheet.getText(0, 1));
             result.zanLiePrice = _.toNumber(sheet.getText(1, 1));
-            result.startAdvance = _.toNumber(sheet.getText(3, 1));
-            result.materialAdvance = _.toNumber(sheet.getText(4, 1));
+            result.jrgPrice = _.toNumber(sheet.getText(2, 1));
+            result.startAdvance = _.toNumber(sheet.getText(4, 1));
+            result.materialAdvance = _.toNumber(sheet.getText(5, 1));
             // result.safeAdvance = _.toNumber(sheet.getText(5, 1));
             result.safeAdvance = _.toNumber(cap100sheet.getText(0, 1));
             result.dustAdvance = _.toNumber(cap100sheet.getText(1, 1));

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

@@ -28,7 +28,7 @@
         menu: '#sub-menu', miniMenu: '#sub-mini-menu', miniMenuList: '#mini-menu-list',
         toMenu: '#to-menu', toMiniMenu: '#to-mini-menu',
         key: 'list.menu.1.0.0',
-        miniHint: '#sub-mini-hint', hintKey: 'menu.hint.1.0.1', defaultMenu: '<%- ((!ctx.subProject.page_show.openTenderContract && !ctx.subProject.page_show.openConstruction) ? 'miniMenu' : '') %>',
+        miniHint: '#sub-mini-hint', hintKey: 'menu.hint.1.0.1', defaultMenu: '',
         callback: function (info) {
             if (info.mini) {
                 $('.panel-title').addClass('fluid');

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

@@ -27,7 +27,7 @@
         menu: '#sub-menu', miniMenu: '#sub-mini-menu', miniMenuList: '#mini-menu-list',
         toMenu: '#to-menu', toMiniMenu: '#to-mini-menu',
         key: 'list.menu.1.0.0',
-        miniHint: '#sub-mini-hint', hintKey: 'menu.hint.1.0.1', defaultMenu: '<%- ((!ctx.subProject.page_show.openTenderContract && !ctx.subProject.page_show.openConstruction) ? 'miniMenu' : '') %>',
+        miniHint: '#sub-mini-hint', hintKey: 'menu.hint.1.0.1', defaultMenu: '',
         callback: function (info) {
             if (info.mini) {
                 $('.panel-title').addClass('fluid');

+ 18 - 2
app/view/tender/list_sub_menu_list.ejs

@@ -14,6 +14,15 @@
     </ul>
 </div>
 <% } %>
+<% if (ctx.subProject.page_show.drawing) { %>
+<div class="nav-box">
+    <ul class="nav-list list-unstyled">
+        <li class="<% if (ctx.url === '/sp/' + ctx.subProject.id + '/drawing/tender') { %>active<% } %>">
+            <a href="/sp/<%- ctx.subProject.id %>/drawing/tender"><span class="ml-3">图纸管理</span></a>
+        </li>
+    </ul>
+</div>
+<% } %>
 <% if (ctx.subProject.page_show.openConstruction) { %>
 <div class="nav-box">
     <ul class="nav-list list-unstyled">
@@ -25,7 +34,7 @@
 <% } %>
 <div class="nav-box">
     <ul class="nav-list list-unstyled">
-        <li class=""><a href="#spssCollapse" class="" data-toggle="collapse" aria-expanded="true"><i class="fa fa-angle-down"></i><span class="ml-2">汇总对比</span></a></li>
+        <li class=""><a href="#spssCollapse" class="" data-toggle="collapse" aria-expanded="true"><i class="fa fa-angle-down" id="collapseIcon"></i><span class="ml-2">汇总对比</span></a></li>
         <div class="collapse show" id="spssCollapse" style="">
             <li class="<% if (ctx.url === '/sp/' + ctx.subProject.id + '/spss/info') { %>active<% } %>"><a href="/sp/<%- ctx.subProject.id %>/spss/info"><span class="ml-3">金额汇总</span></a></li>
             <li class="<% if (ctx.url === '/sp/' + ctx.subProject.id + '/spss/gather/ledger') { %>active<% } %>"><a href="/sp/<%- ctx.subProject.id %>/spss/gather/ledger"><span class="ml-3">台账汇总</span></a></li>
@@ -35,4 +44,11 @@
         </div>
     </ul>
 </div>
-
+<script>
+    $('#spssCollapse').on('show.bs.collapse', function() {
+        $('#collapseIcon').removeClass('fa-angle-right').addClass('fa-angle-down');
+    });
+    $('#spssCollapse').on('hide.bs.collapse', function() {
+        $('#collapseIcon').removeClass('fa-angle-down').addClass('fa-angle-right');
+    });
+</script>

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

@@ -27,7 +27,7 @@
         menu: '#sub-menu', miniMenu: '#sub-mini-menu', miniMenuList: '#mini-menu-list',
         toMenu: '#to-menu', toMiniMenu: '#to-mini-menu',
         key: 'list.menu.1.0.0',
-        miniHint: '#sub-mini-hint', hintKey: 'menu.hint.1.0.1', defaultMenu: '<%- ((!ctx.subProject.page_show.openTenderContract && !ctx.subProject.page_show.openConstruction) ? 'miniMenu' : '') %>',
+        miniHint: '#sub-mini-hint', hintKey: 'menu.hint.1.0.1', defaultMenu: '',
         callback: function (info) {
             if (info.mini) {
                 $('.panel-title').addClass('fluid');

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

@@ -26,7 +26,7 @@
         menu: '#sub-menu', miniMenu: '#sub-mini-menu', miniMenuList: '#mini-menu-list',
         toMenu: '#to-menu', toMiniMenu: '#to-mini-menu',
         key: 'list.menu.1.0.0',
-        miniHint: '#sub-mini-hint', hintKey: 'menu.hint.1.0.1', defaultMenu: '<%- ((!ctx.subProject.page_show.openTenderContract && !ctx.subProject.page_show.openConstruction) ? 'miniMenu' : '') %>',
+        miniHint: '#sub-mini-hint', hintKey: 'menu.hint.1.0.1', defaultMenu: '',
         callback: function (info) {
             if (info.mini) {
                 $('.panel-title').addClass('fluid');

+ 36 - 8
app/view/wap/dashboard.ejs

@@ -40,12 +40,12 @@
         </nav>
         <!--待审批期列表-->
         <div class="py-6">
-            <% if (auditStages.length !== 0 || auditChanges.length !== 0 || auditRevise.length !== 0 || auditAdvance.length !== 0 || auditChangeProjects.length !== 0|| auditChangeApplys.length !== 0 || auditChangePlans.length !== 0) { %>
+            <% if (auditStages.length !== 0 || auditChanges.length !== 0 || auditRevise.length !== 0 || auditAdvance.length !== 0 || auditChangeProjects.length !== 0|| auditChangeApplys.length !== 0 || auditChangePlans.length !== 0 || auditMaterials.length !== 0) { %>
                 <% for (const audit of auditStages) { %>
                 <div class="card mb-3">
                     <div class="card-header d-flex justify-content-between">
                         <span><%- JSON.parse(audit.deal_info).buildName %></span>
-                        <span class="badge badge-pill badge-info">计量期</span>
+                        <span class="badge badge-pill bg-new-stage text-new-stage">计量期</span>
                     </div>
                     <div class="bg-light p-2 px-3"><b><%- audit.name %></b></div>
                     <div class="card-body">
@@ -73,7 +73,7 @@
                     <div class="card mb-3">
                         <div class="card-header d-flex justify-content-between">
                             <span><%- JSON.parse(revise.deal_info).buildName %></span>
-                            <span class="badge badge-pill badge-info">台账修订</span>
+                            <span class="badge badge-pill bg-new-revise text-new-revise">台账修订</span>
                         </div>
                         <div class="bg-light p-2 px-3"><b><%- revise.t_name %></b></div>
                         <div class="card-body">
@@ -95,7 +95,7 @@
                     <div class="card mb-3">
                         <div class="card-header d-flex justify-content-between">
                             <span><%- JSON.parse(change.deal_info).buildName %></span>
-                            <span class="badge badge-pill badge-danger">工程变更</span>
+                            <span class="badge badge-pill bg-new-change text-new-change">工程变更</span>
                         </div>
                         <div class="bg-light p-2 px-3"><b><%- change.name %></b></div>
                         <div class="card-body">
@@ -118,7 +118,7 @@
                     <div class="card mb-3">
                         <div class="card-header d-flex justify-content-between">
                             <span><%- JSON.parse(change.deal_info).buildName %></span>
-                            <span class="badge badge-pill badge-danger">变更立项</span>
+                            <span class="badge badge-pill bg-new-changeProject text-new-changeProject">变更立项</span>
                         </div>
                         <div class="bg-light p-2 px-3"><b><%- change.t_name %></b></div>
                         <div class="card-body">
@@ -140,7 +140,7 @@
                     <div class="card mb-3">
                         <div class="card-header d-flex justify-content-between">
                             <span><%- JSON.parse(change.deal_info).buildName %></span>
-                            <span class="badge badge-pill badge-danger">变更申请</span>
+                            <span class="badge badge-pill bg-new-changeApply text-new-changeApply">变更申请</span>
                         </div>
                         <div class="bg-light p-2 px-3"><b><%- change.t_name %></b></div>
                         <div class="card-body">
@@ -162,7 +162,7 @@
                     <div class="card mb-3">
                         <div class="card-header d-flex justify-content-between">
                             <span><%- JSON.parse(change.deal_info).buildName %></span>
-                            <span class="badge badge-pill badge-danger">变更方案</span>
+                            <span class="badge badge-pill bg-new-changePlan text-new-changePlan">变更方案</span>
                         </div>
                         <div class="bg-light p-2 px-3"><b><%- change.t_name %></b></div>
                         <div class="card-body">
@@ -184,7 +184,7 @@
                     <div class="card mb-3">
                         <div class="card-header d-flex justify-content-between">
                             <span><%- JSON.parse(advance.deal_info).buildName %></span>
-                            <span class="badge badge-pill badge-warning">预付款</span>
+                            <span class="badge badge-pill bg-new-advance text-new-advance">预付款</span>
                         </div>
                         <div class="bg-light p-2 px-3"><b><%- advance.name %></b></div>
                         <div class="card-body">
@@ -204,6 +204,34 @@
                         </div>
                     </div>
                 <% } %>
+                <% for (const audit of auditMaterials) { %>
+                    <div class="card mb-3">
+                        <div class="card-header d-flex justify-content-between">
+                            <span><%- JSON.parse(audit.deal_info).buildName %></span>
+                            <span class="badge badge-pill bg-new-material text-new-material">材料调差</span>
+                        </div>
+                        <div class="bg-light p-2 px-3"><b><%- audit.name %></b></div>
+                        <div class="card-body">
+                            <div class="d-flex justify-content-between"><span>第<%- audit.order %>期</span></div>
+                            <div class="my-2">
+                                <table class="table table-sm table-bordered">
+                                    <tr><th>计量期</th><td class="text-right">第<%- audit.s_order %>期</td></tr>
+                                    <tr><th>信息价价差费用</th><td class="text-right"><%- audit.m_tp !== null ? ctx.helper.round(audit.m_tp, audit.decimal.tp) : 0 %></td></tr>
+                                    <% if (audit.material_tax) { %>
+                                        <tr><th>信息价(含材料税)</th><td class="text-right"><%- audit.m_tax_tp !== null ? audit.m_tax_tp : 0 %></td></tr>
+                                    <% } else { %>
+                                        <tr><th>信息价(含建筑税)</th><td class="text-right"><%- audit.rate_tp !== null ? audit.rate_tp : 0 %></td></tr>
+                                    <% } %>
+                                    <tr><th>指数法价差费用</th><td class="text-right"><%- audit.ex_tp !== null ? ctx.helper.round(audit.ex_tp, audit.decimal.tp) : 0 %></td></tr>
+                                    <tr><th>指数法(含建筑税)</th><td class="text-right"><%- audit.ex_tp !== null ? ctx.helper.round(ctx.helper.mul(audit.ex_tp, 1+audit.exponent_rate/100), audit.decimal.tp) : 0 %></td></tr>
+                                </table>
+                            </div>
+                            <div class="">
+                                <a href="/wap/tender/<%- audit.tid %>/measure/material/<%- audit.order %>" class="btn btn-block btn-success">审批</a>
+                            </div>
+                        </div>
+                    </div>
+                <% } %>
             <% } else { %>
                 <h3 class="text-center text-muted">暂无待审批期计量</h3>
             <% } %>

Разница между файлами не показана из-за своего большого размера
+ 296 - 0
app/view/wap/shenpi_material.ejs


+ 124 - 29
app/view/wap/tender.ejs

@@ -25,6 +25,16 @@
         .tab-content > .active,.pill-content > .active {
             height: auto;
         }
+        .nav-tabs-scrollable {
+            overflow-x: auto;
+            white-space: nowrap;
+            -webkit-overflow-scrolling: touch; /* 平滑滚动 */
+            scrollbar-width: none; /* Firefox 隐藏滚动条 */
+        }
+
+        .nav-tabs-scrollable::-webkit-scrollbar {
+            display: none; /* Chrome / Safari 隐藏滚动条 */
+        }
     </style>
 </head>
 <body>
@@ -50,24 +60,32 @@
     </nav>
     <!--标段概况-->
     <div class="py-6">
-        <!--标签-->
-        <ul class="nav nav-tabs nav-fill">
-            <li class="nav-item">
-                <a class="px-1 nav-link active" data-toggle="tab" href="#gaikuang" role="tab">概况</a>
-            </li>
-            <li class="nav-item">
-                <a class="px-1 nav-link" data-toggle="tab" href="#yufukuan" role="tab">预付款</a>
-            </li>
-            <li class="nav-item">
-                <a class="px-1 nav-link" data-toggle="tab" href="#tzxiuding" role="tab">台账修订</a>
-            </li>
-            <li class="nav-item">
-                <a class="px-1 nav-link" data-toggle="tab" href="#jlqi" role="tab">计量期</a>
-            </li>
-            <li class="nav-item">
-                <a class="px-1 nav-link" data-toggle="tab" href="#biangeng" role="tab">工程变更</a>
-            </li>
-        </ul>
+        <div class="nav-tabs-scrollable">
+            <!--标签-->
+            <ul class="nav nav-tabs nav-fill" style="flex-wrap: nowrap;min-width: max-content;">
+                <li class="nav-item">
+                    <a class="px-2 nav-link active" data-toggle="tab" href="#gaikuang" role="tab">概况</a>
+                </li>
+                <li class="nav-item">
+                    <a class="px-2 nav-link" data-toggle="tab" href="#yufukuan" role="tab">预付款</a>
+                </li>
+<!--                <li class="nav-item">-->
+<!--                    <a class="px-2 nav-link" data-toggle="tab" href="#ledger" role="tab">台账分解</a>-->
+<!--                </li>-->
+                <li class="nav-item">
+                    <a class="px-2 nav-link" data-toggle="tab" href="#tzxiuding" role="tab">台账修订</a>
+                </li>
+                <li class="nav-item">
+                    <a class="px-2 nav-link" data-toggle="tab" href="#jlqi" role="tab">计量期</a>
+                </li>
+                <li class="nav-item">
+                    <a class="px-2 nav-link" data-toggle="tab" href="#biangeng" role="tab">工程变更</a>
+                </li>
+                <li class="nav-item">
+                    <a class="px-2 nav-link" data-toggle="tab" href="#material" role="tab">材料调差</a>
+                </li>
+            </ul>
+        </div>
         <div class="tab-content">
             <div class="tab-pane active" id="gaikuang">
                 <!--图表-->
@@ -101,6 +119,8 @@
                 </div>
                 <% } %>
             </div>
+            <div class="tab-pane" id="ledger">
+            </div>
             <div class="tab-pane" id="tzxiuding">
                 <dl class="mb-2 mt-3">
                     <% for (const lr of revises) { %>
@@ -235,6 +255,76 @@
                     </div>
                 </div>
             </div>
+            <div class="tab-pane" id="material">
+                <!--期列表-->
+                <dl class="mb-2 mt-3">
+                    <% for (const m of materials) { %>
+                        <dt class="bg-light p-2 d-flex justify-content-between"><span>第<%- m.order %>期</span>
+                            <span class="<%- auditMaterialConst.auditStringClass[m.status] %>">
+                                <% if (m.curAuditors && m.curAuditors.length > 0 && m.status !== auditMaterialConst.status.checked) { %>
+                                    <% if (m.curAuditors[0].audit_type === auditType.key.common) { %>
+                                        <%- m.curAuditors[0].name %><%if (m.curAuditors[0].role !== '' && m.curAuditors[0].role !== null) { %>-<%- m.curAuditors[0].role %><% } %>
+                                    <% } else { %>
+                                        <%- ctx.helper.transFormToChinese(m.curAuditors[0].audit_order) + '审' %>
+                                    <% } %>
+                                <% } %>
+                                <%- m.status === auditMaterialConst.status.checked ? '审批完成' : auditMaterialConst.auditProgress[m.status] %>
+                        </span>
+                        </dt>
+                        <dd>
+                            <table class="table table-hover">
+                                <tbody><tr>
+                                    <td>
+                                        <p class="mb-0">计量期</p>
+                                        <b>第<%- m.s_order %>期</b>
+                                    </td>
+                                    <% if (materialColShow[0].checked) { %>
+                                    <td>
+                                        <p class="mb-0">信息价价差费用</p>
+                                        <b>¥<%= m.m_tp !== null ? ctx.helper.round(m.m_tp, m.decimal.tp) : 0 %></b>
+                                    </td>
+                                    <% } %>
+                                </tr>
+                                <% if (materialColShow[0].checked) { %>
+                                <tr>
+                                    <% if (openMaterialTax) { %>
+                                    <td>
+                                        <p class="mb-0">信息价(含材料税)</p>
+                                        <b>¥<% if (m.material_tax) { %><% if (m.m_tax_tp) { %><%- m.m_tax_tp || 0 %><% } else { %><%- m.m_tp || 0 %><% } %><% } else { %>0<% } %></b>
+                                    </td>
+                                    <% } %>
+                                    <% if ((openMaterialTax && !allMaterialTax) || !openMaterialTax) { %>
+                                    <td>
+                                        <p class="mb-0">信息价(含建筑税)</p>
+                                        <b>¥<% if (!m.material_tax) { %><%- m.rate_tp || 0 %><% } else { %>0<% } %></b>
+                                    </td>
+                                    <% } %>
+                                </tr>
+                                <% } %>
+                                <% if (materialColShow[1].checked) { %>
+                                <tr>
+                                    <td>
+                                        <p class="mb-0">指数法价差费用</p>
+                                        <b>¥<%= m.ex_tp !== null ? ctx.helper.round(m.ex_tp, m.decimal.tp) : 0 %></b>
+                                    </td>
+                                    <td>
+                                        <p class="mb-0">指数法(含建筑税)</p>
+                                        <b>¥<%= m.ex_tp !== null ? ctx.helper.round(ctx.helper.mul(m.ex_tp, 1+m.exponent_rate/100), m.decimal.tp) : 0 %></b>
+                                    </td>
+                                </tr>
+                                <% } %>
+                                <% if (m.curAuditors && m.curAuditors.length > 0 && m.status == auditMaterialConst.status.checking && m.curAuditors.find(x => { return x.aid === ctx.session.sessionUser.accountId})) { %>
+                                    <tr>
+                                        <td colspan="2">
+                                            <a class="btn btn-block btn-success" href="/wap/tender/<%- m.tid %>/measure/material/<%- m.order %>">审批本期</a>
+                                        </td>
+                                    </tr>
+                                <% } %>
+                                </tbody></table>
+                        </dd>
+                    <% } %>
+                </dl>
+            </div>
         </div>
     </div>
     <!--底栏菜单-->
@@ -482,36 +572,41 @@
 </script>
 <script>
     $(document).ready(function () {
-        if (window.location.hash && window.location.hash === '#yufukuan') {
+        if (window.location.hash && window.location.hash !== '#gaikuang') {
             $('#gaikuang').removeClass('active');
             $('.nav-item a[href="#gaikuang"]').removeClass('active');
-
+        }
+        if (window.location.hash && window.location.hash === '#yufukuan') {
             $('#yufukuan').addClass('active');
             $('.nav-item a[href="#yufukuan"]').addClass('active');
+        } else if (window.location.hash && window.location.hash === '#ledger') {
+            $('#ledger').addClass('active');
+            $('.nav-item a[href="#ledger"]').addClass('active');
         } else if (window.location.hash && window.location.hash === '#tzxiuding') {
-            $('#gaikuang').removeClass('active');
-            $('.nav-item a[href="#gaikuang"]').removeClass('active');
-
             $('#tzxiuding').addClass('active');
             $('.nav-item a[href="#tzxiuding"]').addClass('active');
         } else if (window.location.hash && window.location.hash === '#jlqi') {
-            $('#gaikuang').removeClass('active');
-            $('.nav-item a[href="#gaikuang"]').removeClass('active');
-
             $('#jlqi').addClass('active');
             $('.nav-item a[href="#jlqi"]').addClass('active');
         } else if (window.location.hash && window.location.hash === '#biangeng') {
-            $('#gaikuang').removeClass('active');
-            $('.nav-item a[href="#gaikuang"]').removeClass('active');
-
             $('#biangeng').addClass('active');
             $('.nav-item a[href="#biangeng"]').addClass('active');
+        } else if (window.location.hash && window.location.hash === '#material') {
+            $('#material').addClass('active');
+            $('.nav-item a[href="#material"]').addClass('active');
         }
 
         $('.show-content').on('click', function () {
             $(this).parents('td').html($(this).data('content'));
         });
 
+        const $container = $('.nav-tabs-scrollable');
+        const $activeTab = $container.find('.nav-tabs .nav-link.active');
+        if ($activeTab) {
+            $container.animate({
+                scrollLeft: $activeTab.position().left + $container.scrollLeft() - 20
+            }, 300); // 300 毫秒平滑滚动
+        }
     });
 </script>
 </body>

+ 38 - 12
config/web.js

@@ -192,7 +192,7 @@ const JsFiles = {
                     '/public/js/ctrl_price.js',
                 ],
                 mergeFile: 'ctrl_price',
-            }
+            },
         },
         ledger: {
             explode: {
@@ -736,7 +736,7 @@ const JsFiles = {
                     '/public/js/phase_pay_detail.js',
                 ],
                 mergeFile: 'phase_pay_detail',
-            }
+            },
         },
         measure: {
             compare: {
@@ -1320,7 +1320,7 @@ const JsFiles = {
                     '/public/js/sp_data.js',
                 ],
                 mergeFile: 'sp_data',
-            }
+            },
         },
         file: {
             index: {
@@ -1385,6 +1385,30 @@ const JsFiles = {
                     '/public/js/filing_manage.js',
                 ],
                 mergeFile: 'filing_manage',
+            },
+        },
+        drawing: {
+            tender: {
+                files: ['/public/js/moment/moment.min.js'],
+                mergeFiles: [
+                    '/public/js/sub_menu.js',
+                    '/public/js/PinYinOrder.bundle.js',
+                    '/public/js/shares/tender_list_order.js',
+                    '/public/js/tender_showhide.js',
+                    '/public/js/drawing_tender.js',
+                    '/public/js/tender_list_base.js',
+                ],
+                mergeFile: 'drawing_tender',
+            },
+            design: {
+                files: [],
+                mergeFiles: [],
+                mergeFile: 'drawing_design',
+            },
+            built: {
+                files: [],
+                mergeFiles: [],
+                mergeFile: 'drawing_built',
             }
         },
         budget: {
@@ -1488,7 +1512,7 @@ const JsFiles = {
             },
             list: {
                 files: ['/public/js/moment/moment.min.js',
-                    '/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js',],
+                    '/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js'],
                 mergeFiles: [
                     '/public/js/spreadjs_rela/spreadjs_zh.js',
                     '/public/js/path_tree.js',
@@ -1596,8 +1620,11 @@ const JsFiles = {
             },
         },
         setting: {
-            manage: {
-                files: ['/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js', '/public/js/ztree/jquery.ztree.core.js', '/public/js/ztree/jquery.ztree.exedit.js', '/public/js/decimal.min.js', '/public/js/moment/moment.min.js'],
+            sp_manage: {
+                files: ['/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js',
+                    '/public/js/ztree/jquery.ztree.core.js', '/public/js/ztree/jquery.ztree.exedit.js',
+                    '/public/js/decimal.min.js', '/public/js/moment/moment.min.js',
+                    '/public/js/bootstrap/tree-select.js'],
                 mergeFiles: [
                     '/public/js/sub_menu.js',
                     '/public/js/div_resizer.js',
@@ -1611,14 +1638,13 @@ const JsFiles = {
                     '/public/js/tender_showhide.js',
                     '/public/js/shenpi.js',
                     '/public/js/popper/popper.min.js',
-                    '/public/js/bootstrap/tree-select.js',
-                    '/public/js/setting_manage.js',
                     '/public/js/tender_list_base.js',
+                    '/public/js/setting_manage.js',
                 ],
-                mergeFile: 'setting_manage',
+                mergeFile: 'setting_sp_manage',
             },
             spread: {
-                files: ['/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js',],
+                files: ['/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js'],
                 mergeFiles: [
                     '/public/js/spreadjs_rela/spreadjs_zh.js',
                     '/public/js/project_spread.js',
@@ -1641,7 +1667,7 @@ const JsFiles = {
                     '/public/js/sp_setting_permission.js',
                 ],
                 mergeFile: 'sp_setting_permission',
-            }
+            },
         },
         profile: {
             cert: {
@@ -2051,7 +2077,7 @@ const JsFiles = {
                 ],
                 mergeFile: 'spss_compare_stage',
             },
-        }
+        },
     },
 };
 

+ 2 - 0
publish.md

@@ -11,6 +11,8 @@
 如果没有特殊说明,则在第默认操作的第3步前,执行相关脚本,如果有特殊要求,需特别说明
 
 
+### V3.5.49 2025-06-13 update
+
 ### V3.5.xx 2023-12-15 update
 #### uat
 ```shell

+ 15 - 9
sql/update.sql

@@ -1,24 +1,27 @@
 -- 请按如下分类提交sql!!!
+-- Version V3.5.50.xxxx
+-- uat 2025-xx-xx
+-- prod 2025-xx-xx
 
 ------------------------------------
 -- 表结构
 ------------------------------------
+ALTER TABLE `zh_construction_unit`
+MODIFY COLUMN `sign_path` mediumtext CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL COMMENT '签章图片地址,以逗号分隔' AFTER `basic`,
+ADD COLUMN `sign_permission` tinyint(1) NULL DEFAULT 1 COMMENT '授权所有人使用单位章' AFTER `sign_path`;
 
-ALTER TABLE `zh_sub_project_permission`
-ADD COLUMN `tp_cache` json NULL COMMENT '金额概况缓存' AFTER `payment_permission`,
-ADD COLUMN `tp_cache_time` timestamp NULL COMMENT '缓存时间' AFTER `tp_cache`;
+ALTER TABLE `zh_project_account`
+ADD COLUMN `unit_sign_path` mediumtext NULL COMMENT '单位章图片地址' AFTER `stamp_path`;
+
+ALTER TABLE `zh_filing`
+ADD COLUMN `file_company` varchar(100) NOT NULL DEFAULT '' COMMENT '归档单位' AFTER `tips`;
 
-ALTER TABLE `zh_contract_audit`
-ADD COLUMN `permission_att` tinyint(1) NULL DEFAULT 0 COMMENT '上传附件' AFTER `permission_show_node`;
+ALTER TABLE `zh_filing_template`
+ADD COLUMN `file_company` varchar(100) NOT NULL DEFAULT '' COMMENT '归档单位' AFTER `tips`;
 
-ALTER TABLE `zh_material`
-ADD COLUMN `calc_stage` varchar(255) NULL DEFAULT '' COMMENT '用于生成期工料含量数据,期创建后使用' AFTER `final_auditor_str`,
-COLUMN `calc_tp` tinyint(1) NULL DEFAULT 1 COMMENT '用于判断是否已计算总金额,期创建后使用' AFTER `calc_stage`;
+ALTER TABLE `zh_sub_project_permission`
+ADD COLUMN `last_time` timestamp NULL DEFAULT NULL COMMENT '最近使用时间' AFTER `tp_cache_time`;
 
 ------------------------------------
 -- 表数据
 ------------------------------------
-update `zh_material` set `calc_stage` = `stage_id` where `calc_stage` = '';

+ 24 - 0
sql/update20250613.sql

@@ -0,0 +1,24 @@
+-- 请按如下分类提交sql!!!
+-- Version V3.5.49.0626
+-- uat 2025-06-13
+-- prod 2025-06-13
+
+------------------------------------
+-- 表结构
+------------------------------------
+
+ALTER TABLE `zh_sub_project_permission`
+ADD COLUMN `tp_cache` json NULL COMMENT '金额概况缓存' AFTER `payment_permission`,
+ADD COLUMN `tp_cache_time` timestamp NULL COMMENT '缓存时间' AFTER `tp_cache`;
+
+ALTER TABLE `zh_contract_audit`
+ADD COLUMN `permission_att` tinyint(1) NULL DEFAULT 0 COMMENT '上传附件' AFTER `permission_show_node`;
+
+ALTER TABLE `zh_material`
+ADD COLUMN `calc_stage` varchar(255) NULL DEFAULT '' COMMENT '用于生成期工料含量数据,期创建后使用' AFTER `final_auditor_str`,
+ADD COLUMN `calc_tp` tinyint(1) NULL DEFAULT 1 COMMENT '用于判断是否已计算总金额,期创建后使用' AFTER `calc_stage`;
+
+------------------------------------
+-- 表数据
+------------------------------------
+update `zh_material` set `calc_stage` = `stage_id` where `calc_stage` = '';