Browse Source

Merge branch 'master' of http://smartcost.f3322.net:3000/SmartCost/ConstructionCost

zhangweicheng 7 năm trước cách đây
mục cha
commit
68a3ff2deb
39 tập tin đã thay đổi với 647 bổ sung135 xóa
  1. 8 1
      modules/main/routes/main_route.js
  2. 2 1
      modules/pm/controllers/pm_controller.js
  3. 6 2
      modules/pm/models/project_model.js
  4. 16 0
      modules/reports/controllers/rpt_controller.js
  5. 1 0
      modules/reports/routes/report_router.js
  6. 136 39
      modules/reports/util/rpt_svg_util.js
  7. 8 4
      public/web/tree_sheet/tree_sheet_helper.js
  8. 37 13
      test/unit/reports/rpt_cfg.js
  9. 100 0
      test/unit/reports/test_svg_data.js
  10. BIN
      web/building_saas/css/logo.png
  11. 21 15
      web/building_saas/css/main.css
  12. 3 0
      web/building_saas/main/html/main.html
  13. 2 2
      web/building_saas/main/js/models/calc_base.js
  14. 13 3
      web/building_saas/main/js/models/calc_program.js
  15. 2 1
      web/building_saas/main/js/models/ration_glj.js
  16. 4 2
      web/building_saas/main/js/views/calc_base_view.js
  17. 14 2
      web/building_saas/main/js/views/calc_program_manage.js
  18. 1 1
      web/building_saas/main/js/views/calc_program_view.js
  19. 9 3
      web/building_saas/main/js/views/character_content_view.js
  20. 11 3
      web/building_saas/main/js/views/fee_rate_view.js
  21. 7 1
      web/building_saas/main/js/views/glj_view.js
  22. 3 0
      web/building_saas/main/js/views/main_tree_col.js
  23. 22 1
      web/building_saas/main/js/views/project_glj_view.js
  24. 4 0
      web/building_saas/main/js/views/project_info.js
  25. 3 0
      web/building_saas/main/js/views/project_property_basicInfo.js
  26. 3 0
      web/building_saas/main/js/views/project_property_bills_quantity_decimal.js
  27. 8 2
      web/building_saas/main/js/views/project_property_labour_coe_view.js
  28. 3 0
      web/building_saas/main/js/views/project_property_projFeature.js
  29. 102 3
      web/building_saas/main/js/views/project_view.js
  30. 3 0
      web/building_saas/main/js/views/side_tools.js
  31. 1 1
      web/building_saas/main/js/views/std_billsGuidance_lib.js
  32. 6 0
      web/building_saas/main/js/views/sub_fee_rate_views.js
  33. 40 22
      web/building_saas/main/js/views/sub_view.js
  34. 3 1
      web/building_saas/main/js/views/tender_price_view.js
  35. 3 0
      web/building_saas/main/js/views/zmhs_view.js
  36. 34 3
      web/building_saas/pm/js/pm_share.js
  37. 2 5
      web/building_saas/report/html/rpt_print.html
  38. 0 2
      web/building_saas/report/js/rpt_print.js
  39. 6 2
      web/common/html/header.html

+ 8 - 1
modules/main/routes/main_route.js

@@ -15,12 +15,19 @@ module.exports =function (app) {
                 // 获取项目信息
                 const projectId = req.query.project;
                 const projectData = await projectModel.project.getProject(projectId);
+                //分享的项目,只读
+                let projectReadOnly = false;
+                if(req.session.sessionUser.id !== projectData.userID){
+                    projectData._doc.readOnly = true;
+                    projectReadOnly = true;
+                }
                 res.render('building_saas/main/html/main.html',
                     {
                         userAccount: req.session.userAccount,
                         userID: req.session.sessionUser.id,
                         projectData: projectData,
-                        versionName: req.session.sessionCompilation.name + '免费版'
+                        versionName: req.session.sessionCompilation.name + '免费版',
+                        projectReadOnly: projectReadOnly
                     });
             } else {
                 res.redirect('/pm');

+ 2 - 1
modules/pm/controllers/pm_controller.js

@@ -44,7 +44,8 @@ module.exports = {
              * result._doc.userID(Number): MongoDB
              * userId(String): Session.userID
              */
-            if (result._doc.userID == userId && result._doc.projType === projType.tender) {
+            //result._doc.userID == userId &&
+            if (result._doc.projType === projType.tender) {
                 callback(true);
             } else {
                 callback(false);

+ 6 - 2
modules/pm/models/project_model.js

@@ -71,10 +71,14 @@ ProjectsDAO.prototype.getUserProjects = async function (userId, compilation, cal
 };
 
 ProjectsDAO.prototype.getUserProject = function (userId, ProjId, callback) {
-    Projects.findOne({userID: userId, ID: ProjId}, '-_id', function (err, template) {
+    Projects.findOne({$or: [{deleteInfo: null}, {'deleteInfo.deleted': false}], ID: ProjId}, '-_id', function (err, template) {
         if (err) {
             callback(1, '找不到标段数据', null);
         } else {
+            //打开分享的项目,只读
+            if(template && userId !== template.userID){
+                template._doc.readOnly = true;
+            }
             callback(0, '', template);
         }
     });
@@ -396,7 +400,7 @@ ProjectsDAO.prototype.getProject = function (key, callback) {
             }
         });
     } else {
-        return Projects.findOne({'ID': key}).exec();
+        return Projects.findOne({$or: [{deleteInfo: null}, {'deleteInfo.deleted': false}], 'ID': key}).exec();
     }
 };
 

+ 16 - 0
modules/reports/controllers/rpt_controller.js

@@ -18,6 +18,7 @@ import JpcEx from "../rpt_component/jpc_ex";
 import rptUtil from "../util/rpt_util";
 import rpt_xl_util from "../util/rpt_excel_util";
 import rpt_pdf_util from "../util/rpt_pdf_util";
+import rpt_svg_util from "../util/rpt_svg_util";
 import fs from "fs";
 import strUtil from "../../../public/stringUtil";
 import rptDataExtractor from "../util/rpt_construct_data_util";
@@ -264,6 +265,21 @@ module.exports = {
         });
     },
 
+    getReportAllPagesSvg: function (req, res) {
+        let params = JSON.parse(req.body.params),
+            rpt_id = params.rpt_tpl_id,
+            prj_id = params.prj_id,
+            pageSize = params.pageSize,
+            orientation = params.orientation,
+            customizeCfg = params.custCfg
+        ;
+        let user_id = req.session.sessionUser.id;
+        getAllPagesCommon(user_id, prj_id, rpt_id, pageSize, orientation, customizeCfg, null, function (err, pageRst) {
+            let svgRstStrArr = rpt_svg_util.exportSvgStr(pageRst, 0, 0);
+            callback(req, res, err, svgRstStrArr);
+        });
+    },
+
     getTestReportAllPages: function(req, res){
         let rpt_id = req.body.ID;
         let pageSize = req.body.pageSize;

+ 1 - 0
modules/reports/routes/report_router.js

@@ -25,6 +25,7 @@ module.exports =function (app) {
     rptRouter.get('/getTestPDF/:id/:size/:rptName', reportController.getTestPDF);
     //now is the real:
     rptRouter.post('/getReport', reportController.getReportAllPages);
+    rptRouter.post('/getReportSvg', reportController.getReportAllPagesSvg);
     rptRouter.get('/getExcel/:prj_id/:rpt_id/:size/:orientation/:rptName/:isOneSheet/:option', reportController.getExcel);
     rptRouter.get('/getPDF/:prj_id/:rpt_id/:size/:orientation/:rptName', reportController.getPDF);
     // rptRouter.get('/getExcelInOneBook/:ids/:size/:rptName/:option', reportController.getExcelInOneBook);

+ 136 - 39
modules/reports/util/rpt_svg_util.js

@@ -4,63 +4,129 @@
  */
 
 let JV = require('../rpt_component/jpc_value_define');
-const SCREEN_DPI = [96,96];
+let pdf = require('pdfkit');
+let jpcCmnHelper = require('../rpt_component/helper/jpc_helper_common');
+let SCREEN_DPI = jpcCmnHelper.getScreenDPI();
 
 module.exports = {
-    exportSvgStr: function (pagesData, callback) {
+    exportSvgStr: function (pagesData, offsetX, offsetY) {
+        let rst = [];
         let styles = pagesData[JV.NODE_STYLE_COLLECTION],
             fonts = pagesData[JV.NODE_FONT_COLLECTION],
             controls = pagesData[JV.NODE_CONTROL_COLLECTION]
         ;
-        for (let page of pagesData.items) {
+        let pdf_doc = new pdf({autoFirstPage: false});
+        for (let idx = 0; idx < pagesData.items.length; idx++) {
+            let page = pagesData.items[idx];
             let svgPageArr = [], pixelSize = getPixelSize(pagesData);
             svgPageArr.push("<svg width='" + pixelSize[0] + "' height='" + pixelSize[1] + "'>");
+            let adjustY = 0.5 * ((idx + 1) % 2);
+            // let cnt = 0;
             for (let cell of page.cells) {
-                svgPageArr.push(buildCellSvg(cell, fonts, styles, controls, page[JV.PROP_PAGE_MERGE_BORDER]));
+                svgPageArr.push(buildCellSvg(cell, fonts, styles, controls, page[JV.PROP_PAGE_MERGE_BORDER], pagesData[JV.BAND_PROP_MERGE_BAND], offsetX, offsetY, adjustY, pdf_doc));
+                // cnt++;
+                // console.log(cnt);
             }
             svgPageArr.push("</svg>");
+            rst.push(svgPageArr);
         }
+        return rst;
     }
 }
 
-function buildCellSvg(cell, fonts, styles, controls, mergeBorder) {
+function getActualBorderStyle(cell, styles, mergeBorderStyle, pageBorderArea, borderStr) {
+    let rst = styles[cell[JV.PROP_STYLE]][borderStr];
+    if (mergeBorderStyle) {
+        if (parseFloat(cell[JV.PROP_AREA][borderStr]) === parseFloat(pageBorderArea[borderStr])) {
+            if (borderStr === JV.PROP_LEFT || borderStr === JV.PROP_RIGHT) {
+                if (parseFloat(cell[JV.PROP_AREA][JV.PROP_TOP]) >= parseFloat(pageBorderArea[JV.PROP_TOP]) &&
+                    parseFloat(cell[JV.PROP_AREA][JV.PROP_BOTTOM]) <= parseFloat(pageBorderArea[JV.PROP_BOTTOM])) {
+                    rst = mergeBorderStyle[borderStr];
+                }
+            } else if (borderStr === JV.PROP_TOP || borderStr === JV.PROP_BOTTOM) {
+                if (parseFloat(cell[JV.PROP_AREA][JV.PROP_LEFT]) >= parseFloat(pageBorderArea[JV.PROP_LEFT]) &&
+                    parseFloat(cell[JV.PROP_AREA][JV.PROP_RIGHT]) <= parseFloat(pageBorderArea[JV.PROP_RIGHT])) {
+                    rst = mergeBorderStyle[borderStr];
+                }
+            }
+        }
+    }
+    return rst;
+}
+
+function buildCellSvg(cell, fonts, styles, controls, pageMergeBorder, rptMergeBorder, offsetX, offsetY, adjustY, pdf_doc) {
     let rst = [];
     let style = styles[cell[JV.PROP_STYLE]];
+    let mergeBandStyle = null;
+    if (rptMergeBorder) {
+        mergeBandStyle = styles[rptMergeBorder[JV.PROP_STYLE][JV.PROP_ID]];
+    }
+    let font = cell[JV.PROP_FONT];
+    if (typeof font === 'string') {
+        font = fonts[cell[JV.PROP_FONT]];
+    }
+    let left = parseInt(cell[JV.PROP_AREA][JV.PROP_LEFT]) + offsetX + 0.5,
+        right = parseInt(cell[JV.PROP_AREA][JV.PROP_RIGHT]) + offsetX + 0.5,
+        top = parseInt(cell[JV.PROP_AREA][JV.PROP_TOP]) + offsetY + adjustY,
+        bottom = parseInt(cell[JV.PROP_AREA][JV.PROP_BOTTOM]) + offsetY + adjustY
+    ;
     if (style) {
-        if (style[JV.PROP_LEFT] && parseFloat(style[JV.PROP_LEFT][JV.PROP_LINE_WEIGHT]) > 0) {
-            rst.push("<line x1='" + cell[JV.PROP_AREA][JV.PROP_LEFT] + "' y1='" + cell[JV.PROP_AREA][JV.PROP_TOP] +
-                "' x2='" + cell[JV.PROP_AREA][JV.PROP_LEFT] + "' y2='" + cell[JV.PROP_AREA][JV.PROP_BOTTOM] +
-                "' style='stroke:rgb(0,0,0);stroke-width:1'/>")
+        let leftBS = getActualBorderStyle(cell, styles, mergeBandStyle, (pageMergeBorder)?pageMergeBorder:rptMergeBorder[JV.PROP_AREA], JV.PROP_LEFT);
+        // if (style[JV.PROP_LEFT] && parseFloat(style[JV.PROP_LEFT][JV.PROP_LINE_WEIGHT]) > 0) {
+        if (leftBS && parseFloat(leftBS[JV.PROP_LINE_WEIGHT]) > 0) {
+            rst.push("<line x1='" + left + "' y1='" + top +
+                "' x2='" + left + "' y2='" + bottom +
+                "' style='stroke:rgb(0,0,0);stroke-width:" + leftBS[JV.PROP_LINE_WEIGHT] +"'/>")
         }
-        if (style[JV.PROP_RIGHT] && parseFloat(style[JV.PROP_RIGHT][JV.PROP_LINE_WEIGHT]) > 0) {
-            rst.push("<line x1='" + cell[JV.PROP_AREA][JV.PROP_RIGHT] + "' y1='" + cell[JV.PROP_AREA][JV.PROP_TOP] +
-                "' x2='" + cell[JV.PROP_AREA][JV.PROP_RIGHT] + "' y2='" + cell[JV.PROP_AREA][JV.PROP_BOTTOM] +
-                "' style='stroke:rgb(0,0,0);stroke-width:1'/>")
+        let rightBS = getActualBorderStyle(cell, styles, mergeBandStyle, (pageMergeBorder)?pageMergeBorder:rptMergeBorder[JV.PROP_AREA], JV.PROP_RIGHT);
+        // if (style[JV.PROP_RIGHT] && parseFloat(style[JV.PROP_RIGHT][JV.PROP_LINE_WEIGHT]) > 0) {
+        if (rightBS && parseFloat(rightBS[JV.PROP_LINE_WEIGHT]) > 0) {
+            rst.push("<line x1='" + right + "' y1='" + top +
+                "' x2='" + right + "' y2='" + bottom +
+                "' style='stroke:rgb(0,0,0);stroke-width:" + rightBS[JV.PROP_LINE_WEIGHT] +"'/>")
         }
-        if (style[JV.PROP_TOP] && parseFloat(style[JV.PROP_TOP][JV.PROP_LINE_WEIGHT]) > 0) {
-            rst.push("<line x1='" + cell[JV.PROP_AREA][JV.PROP_LEFT] + "' y1='" + cell[JV.PROP_AREA][JV.PROP_TOP] +
-                "' x2='" + cell[JV.PROP_AREA][JV.PROP_RIGHT] + "' y2='" + cell[JV.PROP_AREA][JV.PROP_TOP] +
-                "' style='stroke:rgb(0,0,0);stroke-width:1'/>")
+        let topBS = getActualBorderStyle(cell, styles, mergeBandStyle, (pageMergeBorder)?pageMergeBorder:rptMergeBorder[JV.PROP_AREA], JV.PROP_TOP);
+        // if (style[JV.PROP_TOP] && parseFloat(style[JV.PROP_TOP][JV.PROP_LINE_WEIGHT]) > 0) {
+        if (topBS && parseFloat(topBS[JV.PROP_LINE_WEIGHT]) > 0) {
+            rst.push("<line x1='" + left + "' y1='" + top +
+                "' x2='" + right + "' y2='" + top +
+                "' style='stroke:rgb(0,0,0);stroke-width:" + topBS[JV.PROP_LINE_WEIGHT] +"'/>")
         }
-        if (style[JV.PROP_BOTTOM] && parseFloat(style[JV.PROP_BOTTOM][JV.PROP_LINE_WEIGHT]) > 0) {
-            rst.push("<line x1='" + cell[JV.PROP_AREA][JV.PROP_LEFT] + "' y1='" + cell[JV.PROP_AREA][JV.PROP_BOTTOM] +
-                "' x2='" + cell[JV.PROP_AREA][JV.PROP_RIGHT] + "' y2='" + cell[JV.PROP_AREA][JV.PROP_BOTTOM] +
-                "' style='stroke:rgb(0,0,0);stroke-width:1'/>")
+        let bottomBS = getActualBorderStyle(cell, styles, mergeBandStyle, (pageMergeBorder)?pageMergeBorder:rptMergeBorder[JV.PROP_AREA], JV.PROP_BOTTOM);
+        // if (style[JV.PROP_BOTTOM] && parseFloat(style[JV.PROP_BOTTOM][JV.PROP_LINE_WEIGHT]) > 0) {
+        if (bottomBS && parseFloat(bottomBS[JV.PROP_LINE_WEIGHT]) > 0) {
+            rst.push("<line x1='" + left + "' y1='" + bottom +
+                "' x2='" + right + "' y2='" + bottom +
+                "' style='stroke:rgb(0,0,0);stroke-width:" + bottomBS[JV.PROP_LINE_WEIGHT] +"'/>")
         }
     }
-    let font = cell[JV.PROP_FONT];
-    if (typeof font === 'string') {
-        font = fonts[cell[JV.PROP_FONT]];
-    }
-    let fontsize = parseInt(font[JV.FONT_PROPS[JV.FONT_PROP_IDX_HEIGHT]]);
-    let left = parseInt(cell[JV.PROP_AREA][JV.PROP_LEFT]),
-        right = parseInt(cell[JV.PROP_AREA][JV.PROP_RIGHT]),
-        top = parseInt(cell[JV.PROP_AREA][JV.PROP_TOP]),
-        bottom = parseInt(cell[JV.PROP_AREA][JV.PROP_BOTTOM]),
+    buildText(rst, cell, font, controls[cell[JV.PROP_CONTROL]], offsetX, offsetY, adjustY, pdf_doc);
+
+    return rst.join("");
+}
+
+function buildText(destRst, cell, font, control, offsetX, offsetY, adjustY, pdf_doc) {
+    let orgFontHeight = parseInt(font[JV.FONT_PROPS[JV.FONT_PROP_IDX_HEIGHT]]);
+    let fontWeight = (font[JV.FONT_PROPS[JV.FONT_PROP_IDX_BOLD]] === 'T')?"bold":"normal";
+    let fontStyle = (font[JV.FONT_PROPS[JV.FONT_PROP_IDX_ITALIC]] === 'T')?"italic":"normal";
+    let left = parseInt(cell[JV.PROP_AREA][JV.PROP_LEFT]) + offsetX + 0.5,
+        right = parseInt(cell[JV.PROP_AREA][JV.PROP_RIGHT]) + offsetX + 0.5,
+        top = parseInt(cell[JV.PROP_AREA][JV.PROP_TOP]) + offsetY + adjustY,
+        bottom = parseInt(cell[JV.PROP_AREA][JV.PROP_BOTTOM]) + offsetY + adjustY,
         x = left, y = top,
         text_anchor = "start"
     ;
-    let control = controls[cell[JV.PROP_CONTROL]];
+    let value = cell[JV.PROP_VALUE];
+    if (!(value)) {
+        value = "";
+    }
+    let values = null;
+    if (typeof value === "string") {
+        values = value.split("|");
+    } else {
+        values = [value];
+    }
+    let stepHeight = (parseInt(cell[JV.PROP_AREA][JV.PROP_BOTTOM]) - parseInt(cell[JV.PROP_AREA][JV.PROP_TOP])) / values.length;
     if (control) {
         if (control[JV.CONTROL_PROPS[JV.CONTROL_PROP_IDX_HORIZON]] === "left") {
             text_anchor = "start";
@@ -72,16 +138,47 @@ function buildCellSvg(cell, fonts, styles, controls, mergeBorder) {
             text_anchor = "middle";
             x = Math.round((left + right) / 2);
         }
-        if (control[JV.CONTROL_PROPS[JV.CONTROL_PROP_IDX_VERTICAL]] === "top") {
-            y = top + JV.OUTPUT_OFFSET[JV.OFFSET_IDX_TOP];
-        } else if (control[JV.CONTROL_PROPS[JV.CONTROL_PROP_IDX_VERTICAL]] === "bottom") {
-            y = bottom - fontsize - JV.OUTPUT_OFFSET[JV.OFFSET_IDX_BOTTOM];
-        } else if (control[JV.CONTROL_PROPS[JV.CONTROL_PROP_IDX_VERTICAL]] === "center") {
-            y = Math.round((top + bottom + fontsize) / 2 );
+    }
+    for (let vidx = 0; vidx < values.length; vidx++) {
+        //check whether need to adjust the font size
+        let dftFontHeight = orgFontHeight;
+        let dftFontBold = font[JV.FONT_PROPS[3]];
+        let dftFontItalic = font[JV.FONT_PROPS[4]];
+        if (dftFontBold && dftFontBold === 'T') {
+            pdf_doc.font(__dirname+'/pdf_base_files/hwxsb.ttf');
+        }else if(dftFontItalic && dftFontItalic === 'T'){
+            pdf_doc.font(__dirname+'/pdf_base_files/Smart-italic.ttf');
+        }else {
+            pdf_doc.font(__dirname+'/pdf_base_files/Smart.ttf');
+        }
+        pdf_doc.fontSize(dftFontHeight);
+        while ((right - left) <= pdf_doc.widthOfString(values[vidx])) {
+            if (dftFontHeight > 6) {
+                dftFontHeight--;
+                pdf_doc.fontSize(dftFontHeight);
+            } else {
+                break;
+            }
+        }
+        dftFontHeight = (dftFontHeight * 3 / 4); //SVG的字体与canvas的字体大小的切换, 不用考虑取整
+        if (control) {
+            if (control[JV.CONTROL_PROPS[JV.CONTROL_PROP_IDX_VERTICAL]] === "top") {
+                y = Math.round((top + vidx * stepHeight) + JV.OUTPUT_OFFSET[JV.OFFSET_IDX_TOP]);
+            } else if (control[JV.CONTROL_PROPS[JV.CONTROL_PROP_IDX_VERTICAL]] === "bottom") {
+                y = Math.round((top + (vidx + 1) * stepHeight) - JV.OUTPUT_OFFSET[JV.OFFSET_IDX_BOTTOM]);
+            } else if (control[JV.CONTROL_PROPS[JV.CONTROL_PROP_IDX_VERTICAL]] === "center") {
+                y = Math.round(((top + vidx * stepHeight) + (top + (vidx + 1) * stepHeight) + dftFontHeight) / 2 );
+            }
+        }
+        if (font[JV.PROP_NAME] === "宋体") {
+            y--;
         }
+        destRst.push("<text style='fill:black;font-family:" + font[JV.PROP_NAME] +
+            ";font-weight:" + fontWeight +
+            ";font-style:" + fontStyle +
+            ";font-size:" + dftFontHeight + "pt' x='" +
+            x +"' y='" + y + "' text-anchor='" + text_anchor + "' xml:space='preserve'>" + values[vidx] + "</text>");
     }
-    rst.push("<text style='fill:black;font-size:" + fontsize + "pt' x='" + x +"' y='" + y + "'>" + cell[JV.PROP_VALUE] + "</text>");
-    return rst.join();
 }
 
 function getPixelSize(pagesData) {

+ 8 - 4
public/web/tree_sheet/tree_sheet_helper.js

@@ -167,13 +167,17 @@ var TREE_SHEET_HELPER = {
                 if(colSetting.editChecking&&colSetting.editChecking(node)){
                     cell.locked(true);
                 }else if (colSetting.readOnly) {
-                    if (Object.prototype.toString.apply(colSetting.readOnly) === "[object Function]") {
-                        cell.locked(colSetting.readOnly(node));
-                    } else {
+                    if(typeof projectReadOnly !== 'undefined' && projectReadOnly){
                         cell.locked(true);
+                    }else {
+                        if (Object.prototype.toString.apply(colSetting.readOnly) === "[object Function]") {
+                            cell.locked(colSetting.readOnly(node));
+                        } else {
+                            cell.locked(true);
+                        }
                     }
                 } else {
-                    cell.locked(false);
+                    cell.locked(typeof projectReadOnly !== 'undefined' && projectReadOnly ? true : false);
                 }
             });
             if(setting.setAutoFitRow){

+ 37 - 13
test/unit/reports/rpt_cfg.js

@@ -3,8 +3,8 @@ module.exports = {
         {
             "ID" : "ReportTitle_Main",
             "CfgDispName" : "主标题",
-            "Name" : "smartSimSun",
-            "FontHeight" : "27",
+            "Name" : "宋体",
+            "FontHeight" : "32",
             "FontColor" : "BLACK",
             "FontBold" : "T",
             "FontItalic" : "F",
@@ -15,8 +15,20 @@ module.exports = {
         {
             "ID" : "ReportTitle_Vice_1",
             "CfgDispName" : "副标题",
-            "Name" : "smartSimSun",
-            "FontHeight" : "20",
+            "Name" : "宋体",
+            "FontHeight" : "22",
+            "FontColor" : "BLACK",
+            "FontBold" : "T",
+            "FontItalic" : "F",
+            "FontUnderline" : "F",
+            "FontStrikeOut" : "F",
+            "FontAngle" : "0"
+        },
+        {
+            "ID" : "ReportTitle_Vice_2",
+            "CfgDispName" : "副标题2",
+            "Name" : "宋体",
+            "FontHeight" : "18",
             "FontColor" : "BLACK",
             "FontBold" : "T",
             "FontItalic" : "F",
@@ -27,7 +39,7 @@ module.exports = {
         {
             "ID" : "HeaderColumn",
             "CfgDispName" : "栏头",
-            "Name" : "smartSimSun",
+            "Name" : "宋体",
             "FontHeight" : "12",
             "FontColor" : "BLACK",
             "FontBold" : "F",
@@ -39,7 +51,7 @@ module.exports = {
         {
             "ID" : "Header",
             "CfgDispName" : "表头",
-            "Name" : "smartSimSun",
+            "Name" : "宋体",
             "FontHeight" : "12",
             "FontColor" : "BLACK",
             "FontBold" : "F",
@@ -51,7 +63,7 @@ module.exports = {
         {
             "ID" : "FooterColumn",
             "CfgDispName" : "栏尾",
-            "Name" : "smartSimSun",
+            "Name" : "宋体",
             "FontHeight" : "12",
             "FontColor" : "BLACK",
             "FontBold" : "F",
@@ -63,7 +75,7 @@ module.exports = {
         {
             "ID" : "Footer",
             "CfgDispName" : "表尾",
-            "Name" : "smartSimSun",
+            "Name" : "宋体",
             "FontHeight" : "12",
             "FontColor" : "BLACK",
             "FontBold" : "F",
@@ -75,7 +87,7 @@ module.exports = {
         {
             "ID" : "GrandTotal",
             "CfgDispName" : "总合计",
-            "Name" : "smartSimSun",
+            "Name" : "宋体",
             "FontHeight" : "12",
             "FontColor" : "BLACK",
             "FontBold" : "F",
@@ -87,7 +99,7 @@ module.exports = {
         {
             "ID" : "SectionTotal",
             "CfgDispName" : "章合计",
-            "Name" : "smartSimSun",
+            "Name" : "宋体",
             "FontHeight" : "12",
             "FontColor" : "BLACK",
             "FontBold" : "F",
@@ -99,7 +111,19 @@ module.exports = {
         {
             "ID" : "Content",
             "CfgDispName" : "正文内容",
-            "Name" : "smartSimSun",
+            "Name" : "宋体",
+            "FontHeight" : "12",
+            "FontColor" : "BLACK",
+            "FontBold" : "F",
+            "FontItalic" : "F",
+            "FontUnderline" : "F",
+            "FontStrikeOut" : "F",
+            "FontAngle" : "0"
+        },
+        {
+            "ID" : "Content_Narrow",
+            "CfgDispName" : "正文内容-窄体",
+            "Name" : "Arial Narrow",
             "FontHeight" : "12",
             "FontColor" : "BLACK",
             "FontBold" : "F",
@@ -110,7 +134,7 @@ module.exports = {
         },
         {
             "ID" : "Header_V1",
-            "Name" : "smartSimSun",
+            "Name" : "宋体",
             "FontHeight" : "12",
             "FontColor" : "BLACK",
             "FontBold" : "F",
@@ -121,7 +145,7 @@ module.exports = {
         },
         {
             "ID" : "Header_V2",
-            "Name" : "smartSimSun",
+            "Name" : "宋体",
             "FontHeight" : "12",
             "FontColor" : "BLACK",
             "FontBold" : "F",

+ 100 - 0
test/unit/reports/test_svg_data.js

@@ -0,0 +1,100 @@
+/**
+ * Created by Tony on 2018/7/5.
+ */
+
+let test = require('tape');
+import JpcEx from "../../../modules/reports/rpt_component/jpc_ex";
+import JV from "../../../modules/reports/rpt_component/jpc_value_define";
+let config = require("../../../config/config.js");
+config.setupDb(process.env.NODE_ENV);
+let mongoose = require("mongoose");
+let fileUtils = require("../../../modules/common/fileUtils");
+let path = require('path');
+let dbm = require("../../../config/db/db_manager");
+let rpt_cfg = require('./rpt_cfg');
+dbm.connect(process.env.NODE_ENV);
+
+//统一引用models
+fileUtils.getGlobbedFiles('../../../modules/all_models/*.js').forEach(function(modelPath) {
+    require(path.resolve(modelPath));
+});
+
+let cfgCacheUtil = require("../../../config/cacheCfg");
+cfgCacheUtil.setupDftCache();
+
+let fsUtil = require("../../../public/fsUtil");
+
+let demoPrjId = - 1;
+let demoRptId = 232, pagesize = "A4";
+
+// demoPrjId = 720; //QA: DW3
+demoPrjId = 2260; //QA:
+//*/
+// let userId_Leng = "59cdf14a0034a1000ba52b97"; //小冷User Id 换成_id了
+let userId_Leng = "5acac1e885bf55000bd055ba";
+let userId_Dft = userId_Leng;
+/*/
+ let userId_Dft = "595328da1934dc327cad08eb";
+ //*/
+
+let rptTplFacade = require("../../../modules/reports/facade/rpt_template_facade");
+let rptTplDataFacade = require("../../../modules/reports/facade/rpt_tpl_data_facade");
+
+import rptDataExtractor from "../../../modules/reports/util/rpt_construct_data_util";
+import rpt_svg_util from "../../../modules/reports/util/rpt_svg_util";
+
+let fs = require('fs');
+//设置Date Format函数
+fs.readFile(__dirname.slice(0, __dirname.length - 18) + '/public/web/date_util.js', 'utf8', 'r', function (err, data) {
+    eval(data);
+});
+
+test('测试 - 测试模板啦: ', function (t) {
+    rptTplFacade.getRptTemplate(demoRptId).then(function(rptTpl) {
+        let rptDataUtil = new rptDataExtractor();
+        rptDataUtil.initialize(rptTpl._doc);
+        let filter = rptDataUtil.getDataRequestFilter();
+        //正常应该根据报表模板定义的数据类型来请求数据
+        rptTplDataFacade.prepareProjectData(userId_Dft, demoPrjId, filter, function (err, msg, rawDataObj) {
+            if (!err) {
+                try {
+                    let tplData = rptDataUtil.assembleData(rawDataObj);
+                    //it's time to build the report!!!
+                    let printCom = JpcEx.createNew();
+                    rptTpl[JV.NODE_MAIN_INFO][JV.NODE_PAGE_INFO][JV.PROP_PAGE_SIZE] = pagesize;
+                    let defProperties = rpt_cfg;
+                    let dftOption = JV.PAGING_OPTION_NORMAL;
+                    printCom.initialize(rptTpl);
+                    printCom.analyzeData(rptTpl, tplData, defProperties, dftOption);
+                    let maxPages = printCom.totalPages;
+                    let pageRst = printCom.outputAsSimpleJSONPageArray(rptTpl, tplData, 1, maxPages, defProperties);
+                    if (pageRst) {
+                        let svgRstStrArr = rpt_svg_util.exportSvgStr(pageRst, 0, 0);
+                        fsUtil.writeObjToFile(svgRstStrArr, "D:/GitHome/ConstructionCost/tmp/rptPageResult_SVG格式.jsp");
+                    } else {
+                        console.log("oh! no pages were created!");
+                    }
+                } catch (ex) {
+                    console.log(ex);
+                    t.pass('pass with exception!');
+                    t.end();
+                }
+
+                t.pass('pass succeeded!');
+                t.end();
+            } else {
+                console.log(msg);
+                t.pass('pass with error!');
+                t.end();
+            }
+        })
+    });
+});
+
+test('close the connection', function (t) {
+    setTimeout(function () {
+        mongoose.disconnect();
+        t.pass('closing db connection');
+        t.end();
+    }, 3000);
+});

BIN
web/building_saas/css/logo.png


+ 21 - 15
web/building_saas/css/main.css

@@ -15,20 +15,26 @@ body {
 }
 /*自定义css*/
 .header {
-    background: #e1e1e1
+    border-bottom: 1px solid #ccc
 }
 .header .header-logo {
     background: #ff6501;
-    color: #fff;
     float: left;
     margin-right: 1rem;
     font-size: 1rem;
-    line-height:38px;
-    height:38px
+    height:38px;
+    background:url(logo.png) no-repeat 0 0;
+    padding-left:40px;
+}
+.header-logo div.v-title{
+    font-size:12px;
+    color:#aeaeae;
+    line-height: 12px;
+    margin-top:3px;
 }
-.header .header-logo sup{
-    font-size:10px;
-    top:-1.2em
+.header-logo div.p-title{
+    font-size:14px;
+    color:#ff6501;
 }
 .top-msg{
     position: fixed;
@@ -44,7 +50,7 @@ body {
     position: absolute;
     text-align: center;
     z-index: 999;
-    padding: 2px 0 0 2px
+    padding: 30px 0 0 2px
 }
 .main-nav .nav a {
     display: block;
@@ -99,12 +105,12 @@ body {
     background: #fff
 }
 .toolsbar,.toolsbar-f {
+    background: #f7f7f9;
     border-bottom: 1px solid #ccc
 }
 .tools-btn {
     height: 30px;
-    line-height: 30px;
-    background:#fff
+    line-height: 30px
 }
 .toolsbar .tools-btn.btn:hover,.toolsbar-f .tools-btn.btn:hover {
     background: #f7f7f9;
@@ -294,12 +300,13 @@ body {
 }
 .print-toolsbar .panel {
     display:inline-block;
-    vertical-align:top;
-    background:#f7f7f9
+    vertical-align:top
 }
 .print-toolsbar .panel .panel-foot{
     text-align: center;
-    font-size: 12px
+    font-size: 12px;
+    padding-bottom:3px;
+    background:#F2F2F2
 }
 .print-list {
     border-right:1px solid #ccc
@@ -313,7 +320,6 @@ body {
     border-bottom:1px solid #f2f2f2
 }
 .pageContainer {
-    background: #ededed;
     text-align: center
 }
 .pageContainer .page{
@@ -343,7 +349,7 @@ body {
     border-bottom:1px solid #ddd
 }
 .navbar-crumb span{
-    max-width: 200px
+    width: 200px
 }
 .dropdown-item{
     color:#007bff

+ 3 - 0
web/building_saas/main/html/main.html

@@ -25,6 +25,9 @@
         let lockBills = '<%- projectData.property.lockBills %>';
         let userAccount = '<%- userAccount %>';
         let userID = '<%- userID %>';
+        let projectReadOnly = JSON.parse('<%- projectReadOnly %>');
+        console.log(`projectReadOnly`);
+        console.log(projectReadOnly);
     </script>
 </head>
 

+ 2 - 2
web/building_saas/main/js/models/calc_base.js

@@ -948,11 +948,11 @@ let baseFigureTemplate = {
                 }
                 //量人 type 2, subType 1
                 else if(ration.type === rationType.volumePrice && ration.subType === volumePriceMaps['量人']){
-                    rst = parseFloat(rst + ration[quantityType]).toDecimal(decimalObj.glj.quantity);
+                    rst = parseFloat(rst + parseFloat(ration[quantityType])).toDecimal(decimalObj.glj.quantity);
                 }
                 //定额类型的人工工料机,type 3, subType 1
                 else if(ration.type === rationType.gljRation && ration.subType === gljType.LABOUR){
-                    rst = parseFloat(rst + ration[quantityType]).toDecimal(decimalObj.glj.quantity);
+                    rst = parseFloat(rst + parseFloat(ration[quantityType])).toDecimal(decimalObj.glj.quantity);
                 }
             }
         }

+ 13 - 3
web/building_saas/main/js/models/calc_program.js

@@ -489,7 +489,9 @@ let calcTools = {
             (treeNode.data.type == rationType.gljRation) &&
             ((treeNode.data.subType === gljType.LABOUR && baseName === calcBaseNames.RGFJC) ||
                 (baseMaterialTypes.includes(treeNode.data.subType) && baseName === calcBaseNames.CLFJC) ||
-                (treeNode.data.subType === gljType.GENERAL_MACHINE && baseName === calcBaseNames.JXFJC))
+                (treeNode.data.subType === gljType.GENERAL_MACHINE && baseName === calcBaseNames.JXFJC) ||
+                (treeNode.data.subType === gljType.MAIN_MATERIAL && baseName === calcBaseNames.ZCFJC) ||
+                (treeNode.data.subType === gljType.EQUIPMENT && baseName === calcBaseNames.SBFJC))
         ) {
             let aprice = me.uiGLJPrice(treeNode.data.adjustPrice);
             let mprice = me.uiGLJPrice(treeNode.data.marketUnitFee);
@@ -778,6 +780,8 @@ const calcBaseNames = {
     RGFJC: '人工费价差',
     CLFJC: '材料费价差',
     JXFJC: '机械费价差',
+    ZCFJC: '主材费价差',
+    SBFJC: '设备费价差',
     ZCF: '主材费',
     SBF: '设备费',
     RGGR: '人工工日',
@@ -822,11 +826,17 @@ const rationCalcBases = {
     '机械费价差': function (node, isTender) {
         return calcTools.rationBaseFee(node, [gljType.GENERAL_MACHINE], priceTypes.ptDiffPrice, isTender);
     },
+    '主材费价差': function (node, isTender) {
+        return calcTools.rationBaseFee(node, [gljType.MAIN_MATERIAL], priceTypes.ptDiffPrice, isTender);
+    },
+    '设备费价差': function (node, isTender) {
+        return calcTools.rationBaseFee(node, [gljType.EQUIPMENT], priceTypes.ptDiffPrice, isTender);
+    },
     '主材费': function (node, isTender) {
-        return calcTools.rationBaseFee(node, [gljType.MAIN_MATERIAL], priceTypes.ptMarketPrice, isTender);
+        return calcTools.rationBaseFee(node, [gljType.MAIN_MATERIAL], priceTypes.ptBasePrice, isTender);
     },
     '设备费': function (node, isTender) {
-        return calcTools.rationBaseFee(node, [gljType.EQUIPMENT], priceTypes.ptMarketPrice, isTender);
+        return calcTools.rationBaseFee(node, [gljType.EQUIPMENT], priceTypes.ptBasePrice, isTender);
     },
     '人工工日': function (node, isTender) {
         if (!node.data.gljList) return 0;

+ 2 - 1
web/building_saas/main/js/models/ration_glj.js

@@ -121,7 +121,8 @@ var ration_glj = {
                     glj.type = glj.subType;
                 };
 
-                glj.quantity = (glj.quantity / billQuantity).toDecimal(decimalObj.glj.quantity);
+                // glj.quantity = (glj.quantity / billQuantity).toDecimal(decimalObj.glj.quantity);
+                glj.quantity = (glj.quantity / billQuantity).toDecimal(decimalObj.process);  // 广联达这里没有取舍
             };
             return oneBill;
         }

+ 4 - 2
web/building_saas/main/js/views/calc_base_view.js

@@ -338,7 +338,7 @@ let calcBaseView = {
             // ctx.fillText(value,x+w-3,y+h-3);
             GC.Spread.Sheets.CellTypes.Text.prototype.paint.apply(this, arguments);
             // }
-            if(calcBaseView.editingCell){
+            if(calcBaseView.editingCell && !projectReadOnly){
                 if(calcBaseView.editingCell.row==options.row&&calcBaseView.editingCell.col==options.col){
                     var image = document.getElementById('f_btn'),imageMagin = 3;
                     var imageHeight = h-2*imageMagin;
@@ -390,7 +390,9 @@ let calcBaseView = {
                 var imageHeight = hitinfo.cellRect.height-2*imageMagin;
                 var imageWidth = hitinfo.cellRect.width*2/7;
                 if(hitinfo.x<offset&&hitinfo.x>offset-imageWidth){
-                    calcBaseView.initCalctor(type);
+                    if(!projectReadOnly){
+                        calcBaseView.initCalctor(type);
+                    }
                 }
             }
         };

+ 14 - 2
web/building_saas/main/js/views/calc_program_manage.js

@@ -71,8 +71,20 @@ let calcProgramManage = {
         dSheet.getRange(-1, _.findIndex(me.detailSetting.header, {'dataCode': 'dispExprUser'}), -1, 1).cellType(calcBaseView.getCalcBaseCellType('ration'));
         sheetCommonObj.showData(dSheet, me.detailSetting, me.datas[0].calcItems);
 
-        me.loadMainContextMenu();
-        me.loadDetailContextMenu();
+        if(!projectReadOnly){
+            me.loadMainContextMenu();
+            me.loadDetailContextMenu();
+        }
+        else {
+            if(me.mainSetting.view.lockColumns){
+                me.mainSetting.view.lockColumns = null;
+            }
+            if(me.detailSetting.view.lockColumns){
+                me.detailSetting.view.lockColumns = null;
+            }
+            disableSpread(me.mainSpread);
+            disableSpread(me.detailSpread);
+        }
     },
     onMainEnterCell: function(sender, args) {
         var me = calcProgramManage;

+ 1 - 1
web/building_saas/main/js/views/calc_program_view.js

@@ -16,7 +16,7 @@ let calcProgramObj = {
             {headerName: "费率", headerWidth: CP_Col_Width.feeRate, dataCode: "feeRate", dataType: "Number"},
             {headerName: "单价", headerWidth: CP_Col_Width.unitFee, dataCode: "unitFee", dataType: "Number"},
             {headerName: "合价", headerWidth: CP_Col_Width.totalFee, dataCode: "totalFee", dataType: "Number"},
-            {headerName:"费用类别", headerWidth:CP_Col_Width.displayFieldName, dataCode:"displayFieldName", dataType: "String", hAlign: "center"},
+            {headerName: "费用类别", headerWidth:CP_Col_Width.displayFieldName, dataCode:"displayFieldName", dataType: "String", hAlign: "center"},
             {headerName: "基数说明", headerWidth: CP_Col_Width.statement, dataCode: "statement", dataType: "String"},
             {headerName: "备注", headerWidth: CP_Col_Width.memo, dataCode: "memo", dataType: "String"}
         ],

+ 9 - 3
web/building_saas/main/js/views/character_content_view.js

@@ -17,7 +17,9 @@ let contentOprObj = {
         me.workBook.options.allowUserDragDrop = false;
         me.workBook.getSheet(0).setColumnWidth(0, 20, GC.Spread.Sheets.SheetArea.rowHeader);
         me.workBook.options.allowCopyPasteExcelStyle = false;
-        me.onContextmenuOpr();//右键菜单
+        if(!projectReadOnly){
+            me.onContextmenuOpr();//右键菜单
+        }
         me.bindEvents(me.workBook);
     },
     bindEvents: function (workBook) {
@@ -399,7 +401,9 @@ let characterOprObj = {
         me.workBook.options.allowUserDragDrop = false;
         me.workBook.getSheet(0).setColumnWidth(0, 20, GC.Spread.Sheets.SheetArea.rowHeader);
         me.workBook.options.allowCopyPasteExcelStyle = false;
-        me.onContextmenuOpr();
+        if(!projectReadOnly){
+            me.onContextmenuOpr();
+        }
         me.bindEvents(me.workBook);
     },
     bindEvents: function (workBook) {
@@ -917,7 +921,9 @@ let pageCCOprObj = {
     },
     //设置特征及内容currentCache
     setCacheAndShow: function (node) {
-        this.refreshRuleTools(projectObj.project.isBillsLocked());
+        if(!projectReadOnly){
+            this.refreshRuleTools(projectObj.project.isBillsLocked());
+        }
         if(node && node.sourceType === projectObj.project.Bills.getSourceType()){
             let theCont = contentOprObj, theCha = characterOprObj;
             node.data.jobContent = node && typeof node.data.jobContent !== 'undefined' ? node.data.jobContent : [];

+ 11 - 3
web/building_saas/main/js/views/fee_rate_view.js

@@ -265,7 +265,7 @@ var feeRateObject={
                // ctx.fillText(value,x+3+ctx.measureText(value).width,y+h-3);
              GC.Spread.Sheets.CellTypes.Text.prototype.paint.apply(this, arguments);
            // }
-            if(feeRateObject.editingCell){
+            if(feeRateObject.editingCell && !projectReadOnly){
                 if(feeRateObject.editingCell.row==options.row&&feeRateObject.editingCell.col==options.col){
                     var image = document.getElementById('f_btn'),imageMagin = 3;
                     var imageHeight = h-2*imageMagin;
@@ -317,7 +317,9 @@ var feeRateObject={
                 var imageHeight = hitinfo.cellRect.height-2*imageMagin;
                 var imageWidth = hitinfo.cellRect.width*2/7;
                 if(hitinfo.x<offset&&hitinfo.x>offset-imageWidth){
-                    me.showSelectModal(hitinfo);
+                    if(!projectReadOnly){
+                        me.showSelectModal(hitinfo);
+                    }
                 }
             }
         };
@@ -390,7 +392,9 @@ var feeRateObject={
         this.mainFeeRateSpread = sheetCommonObj.buildSheet($('#divFee')[0], this.mainFeeRateSetting,rowCount);
         this.mainFeeRateSpread.options.scrollbarMaxAlign = true;
         this.mainFeeRateSheet = this.mainFeeRateSpread.getSheet(0);
-        sheetCommonObj.lockCells(this.mainFeeRateSheet , this.mainFeeRateSetting);
+        if(!projectReadOnly){
+            sheetCommonObj.lockCells(this.mainFeeRateSheet , this.mainFeeRateSetting);
+        }
         this.mainFeeRateSheet.bind(GC.Spread.Sheets.Events.ValueChanged, this.onMainFeeRateSheetValueChange);
         this.mainFeeRateSheet.bind(GC.Spread.Sheets.Events.SelectionChanged, this.onMainFeeRateSelectChanged);
         this.mainFeeRateSheet.bind(GC.Spread.Sheets.Events.RangeChanged, this.onMainFeeRateRangeChanged);
@@ -402,6 +406,10 @@ var feeRateObject={
         });
         this.mainFeeRateSheet.name('mainFeeRateSheet');
         disableRightMenu("divFee",this.mainFeeRateSpread,this.rightClickCallback);
+        //打开他人分享的项目、只读
+        if(projectReadOnly){
+            disableSpread(this.mainFeeRateSpread);
+        }
     },
     rightClickCallback:function (row) {
         let me = feeRateObject;

+ 7 - 1
web/building_saas/main/js/views/glj_view.js

@@ -111,7 +111,9 @@ var gljOprObj = {
                 args.cancel = true;
             }
         });
-        gljContextMenu.loadGLJSpreadContextMenu();
+        if(!projectReadOnly){
+            gljContextMenu.loadGLJSpreadContextMenu();
+        }
     },
     initDetailSheet: function (sheet) {
         var me = this;
@@ -275,6 +277,7 @@ var gljOprObj = {
         }
     },
     onCellClick: function (sender, args) {
+        console.log('cellClick');
         var me = gljOprObj;
         if (args.row >= me.sheetData.length) {
             return;
@@ -404,6 +407,9 @@ var gljOprObj = {
     },
     showDataIfRationSelect: function (node,selectedNodeId) {
         var isShow = false;
+        if(projectReadOnly && this.setting.view.lockColumns){
+            this.setting.view.lockColumns = null;
+        }
         if(selectedNodeId){
             this.selectedNodeId = selectedNodeId;
         }

+ 3 - 0
web/building_saas/main/js/views/main_tree_col.js

@@ -448,6 +448,9 @@ let colSettingObj = {
 $('#poj-set').on('shown.bs.modal', function (e) {
     if (!colSettingObj.settingSpread) {
         colSettingObj.initSettingSpread();
+        if(projectReadOnly){
+            disableSpread(colSettingObj.settingSpread);
+        }
     }
 });
 

+ 22 - 1
web/building_saas/main/js/views/project_glj_view.js

@@ -51,6 +51,14 @@ projectGljObject={
         this.initProjectGljSheet();
         this.initMaterialTreeSheet();
         disableRightMenu("project_glj_sheet",this.projectGljSpread,this.rightClickCallback);
+        //打开别人分享的项目,只读
+        if(projectReadOnly){
+            //锁定逻辑走disabledSpread里的
+            if(this.projectGljSetting.view.lockColumns){
+                this.projectGljSetting.view.lockColumns = null;
+            }
+            disableSpread(this.projectGljSpread);
+        }
     },
     initProjectGljSheet:function () {
         this.projectGljSheet = this.projectGljSpread .getSheet(0);
@@ -63,7 +71,9 @@ projectGljObject={
         let me = projectGljObject;
         if(me.mixRatioSpread==null){
             me.initMixRatioSpread();
-            me.initRightClick();
+            if(!projectReadOnly){
+                me.initRightClick();
+            }
         }
     },
     initMixRatioSpread:function () {
@@ -72,6 +82,12 @@ projectGljObject={
         this.initSheet(this.mixRatioSheet,this.mixRatioSetting);
         this.mixRatioSheet.name('mixRatioSheet');
         this.mixRatioSheet.bind(GC.Spread.Sheets.Events.RangeChanged, this.onMixRatioRangeChange);
+        if(projectReadOnly){
+            if(this.mixRatioSetting.view.lockColumns){
+                this.mixRatioSetting.view.lockColumns = null;
+            }
+            disableSpread(this.mixRatioSpread);
+        }
     },
     initMaterialTreeSheet:function () {
         this.materialTreeSheet = this.projectGljSpread.getSheet(1);
@@ -82,6 +98,11 @@ projectGljObject={
         this.materialTreeSheet.bind(GC.Spread.Sheets.Events.EditStarting,this.onProjectGljEditStarting);
         this.materialTreeSheet.bind(GC.Spread.Sheets.Events.ValueChanged, this.onProjectGLJValueChange);
         this.materialTreeSheet.name('materialTreeSheet');
+        if(projectReadOnly && this.materialSetting.view.lockColumns){
+            if(this.materialSetting.view.lockColumns){
+                this.materialSetting.view.lockColumns = null;
+            }
+        }
     },
     createMaterialTreeSheetSetting:function () {
         return sheetCommonObj.transferToTreeSetting(this.materialSetting,this.materialTreeSetting);

+ 4 - 0
web/building_saas/main/js/views/project_info.js

@@ -31,6 +31,10 @@ var projectInfoObj = {
         CommonAjax.post('/pm/api/getProject', {"user_id": userID, "proj_id": scUrlUtil.GetQueryString('project')}, function (data) {
             if (data) {
                 that.projectInfo = data;
+                if(!data.engineeringInfo.billsGuidance_lib || data.engineeringInfo.billsGuidance_lib.length === 0){
+                    $('#stdBillsGuidanceTab').addClass('disabled');
+                }
+
                 //init decimal
                 setDecimal(decimalObj, data.property.decimal);
                 billsQuanDecimal.datas = data.property.billsQuantityDecimal || [billsDecimalView.angleDecimal];

+ 3 - 0
web/building_saas/main/js/views/project_property_basicInfo.js

@@ -443,6 +443,9 @@ $(document).ready(function () {
         basicInfoView.initDatas(basicInfoView.orgDatas);
         basicInfoView.buildSheet();
         basicInfoView.showData(basicInfoView.datas);
+        if(projectReadOnly){
+            disableSpread(basicInfoView.workBook);
+        }
     });
 
     $('#poj-set').on('hidden.bs.modal', function (e) {

+ 3 - 0
web/building_saas/main/js/views/project_property_bills_quantity_decimal.js

@@ -351,6 +351,9 @@ $(document).ready(function () {
         }
         billsDecimalView.buildSheet();
         billsDecimalView.showData(billsDecimalView.cache);
+        if(projectReadOnly){
+            disableSpread(billsDecimalView.workBook);
+        }
     });
 
     $('#poj-set').on('hidden.bs.modal', function (e) {

+ 8 - 2
web/building_saas/main/js/views/project_property_labour_coe_view.js

@@ -50,6 +50,9 @@ let labourCoeView = {
         sheet.bind(GC.Spread.Sheets.Events.CellChanged, me.onCellChanged);
         sheet.resumeEvent();
         sheet.resumePaint();
+        if(projectReadOnly){
+            disableSpread(me.spread);
+        }
     },
     loadCrossData(datas){          // 交叉表:树结构转换二维表显示,行列转换
         let me = this;
@@ -70,8 +73,11 @@ let labourCoeView = {
         me.sheet.setColumnCount(libArr.length + 1, GC.Spread.Sheets.SheetArea.viewport);    // 还多一列行名称
         me.sheet.setRowCount(row, GC.Spread.Sheets.SheetArea.viewport);
         me.sheet.setText(0, 0, "定额工种", GC.Spread.Sheets.SheetArea.colHeader);
-        me.sheet.options.isProtected = true;
-        me.sheet.getRange(-1, 1, -1, libArr.length + 1, GC.Spread.Sheets.SheetArea.viewport).locked(false);
+        //项目只读的话,单元格锁定在disableSpread控制
+        if(!projectReadOnly){
+            me.sheet.options.isProtected = true;
+            me.sheet.getRange(-1, 1, -1, libArr.length + 1, GC.Spread.Sheets.SheetArea.viewport).locked(false);
+        }
 
         // 列名称
         for (let c = 0; c <= libArr.length - 1; c++) {

+ 3 - 0
web/building_saas/main/js/views/project_property_projFeature.js

@@ -401,6 +401,9 @@ $(document).ready(function () {
         projFeatureView.initDatas(projFeatureView.orgDatas);
         projFeatureView.buildSheet();
         projFeatureView.showData(projFeatureView.datas);
+        if(projectReadOnly){
+            disableSpread(projFeatureView.workBook);
+        }
     });
 
     $('#poj-set').on('hidden.bs.modal', function (e) {

+ 102 - 3
web/building_saas/main/js/views/project_view.js

@@ -584,6 +584,7 @@ var projectObj = {
             this.mainSpread = SheetDataHelper.createNewSpread($('#billsSpread')[0]);
             this.mainSpread.getActiveSheet().selectionPolicy(GC.Spread.Sheets.SelectionPolicy.muliRange);
             this.mainSpread.getActiveSheet().name('mainSheet');
+            this.mainSpread.getActiveSheet().options.isProtected = true;
         }
     },
     refreshMainSpread: function () {
@@ -683,7 +684,9 @@ var projectObj = {
                 that.mainSpread.bind(GC.Spread.Sheets.Events.CellDoubleClick, that.onCellDoubleClick);
 
                 //let loadOtherStartTime = +new Date();
-                that.loadMainSpreadContextMenu();
+                if(!projectReadOnly){
+                    that.loadMainSpreadContextMenu();
+                }
                 that.loadFocusLocation();
                 socketObject.connect();//连接socket服务器
                 let endTime = +new Date();
@@ -697,6 +700,9 @@ var projectObj = {
                 //定位到会话中的选项
                 let mainTabFocus = sessionStorage.getItem('mainTab') ? sessionStorage.getItem('mainTab') : '#tab_zaojiashu';
                 $(`${mainTabFocus}`).click();
+                if(projectReadOnly){
+                    disableSpread(that.mainSpread);
+                }
                 $.bootstrapLoading.end();
             }
             else {
@@ -1501,7 +1507,9 @@ $("a[name='lockBills']").click(function () {//
         controller.refreshTreeNode(nodes);
         projectObj.mainController.setTreeSelected(selected);//触发树节点选中事件
         projectObj.loadLockBillsButton();
-        pageCCOprObj.refreshRuleTools(lockBills);
+        if(!projectReadOnly){
+            pageCCOprObj.refreshRuleTools(lockBills);
+        }
     });
 });
 $('#ZLFB_btn').click(function () {
@@ -2142,4 +2150,95 @@ $(function () {
             spread?spread.focus():'';
         }
     }
-});
+    //项目为只读
+    if(projectReadOnly){
+        disableTools();
+    }
+});
+
+//项目只读,处理不可操作的工具栏
+function disableTools(){
+    //造价书按钮
+    $('#delete').remove();
+    $('#upLevel').remove();
+    $('#downLevel').remove();
+    $('#upMove').remove();
+    $('#downMove').remove();
+    $('#ZLFB_btn').remove();
+    $('a[name="lockBills"]').remove();
+    //关于计算
+    $('#poj-settings-4').find('input').prop('disabled', 'disabled');
+    //小数位数
+    $('#poj-settings-decimal').find('input').prop('disabled', 'disabled');
+    //人工单价调整
+    $('#std_labour_coe_files').prop('disabled', 'disabled');
+    //呈现选项
+    $('#display-setting').find('input').prop('disabled', 'disabled');
+    //项目属性确定
+    $('#property_ok').addClass('disabled');
+    //特征及内容
+    $('#add-rule').find('select').prop('disabled', 'disabled');
+    $('#use-to-current').addClass('disabled');
+    $('#use-to-all').addClass('disabled');
+    $('.bottom-tools').remove();
+    //导入
+    $('#uploadConfirm').addClass('disabled');
+    //选项
+    $('#generalOpts1').prop('disabled', 'disabled');
+    $('#generalOpts2').prop('disabled', 'disabled');
+    //补充库编辑器
+    $('#compleRationLib').addClass('disabled');
+    $('#compleGljLib').addClass('disabled')
+    //库
+    $('#stdBillsGuidanceTab').addClass('disabled');
+    $('#stdBillsTab').addClass('disabled');
+    $('#stdRationTab').addClass('disabled');
+    //人材机汇总,选择其他、另存使用
+    $('a[data-target="#change-unitFile"]').remove();
+    $('a[data-target="#unitFile-save-as"]').remove();
+    //费率,选择其他、另存使用、重选标准、统一设置相同参数
+    $('a[data-target="#change-lv"]').remove();
+    $('a[data-target="#copy-lv"]').remove();
+    $('#standardSelect').prop('disabled', 'disabled');
+    $('#cascadeSet').prop('disabled', 'disabled');
+    //计算程序标准
+    $('#calcProgramFileSelect').prop('disabled', 'disabled');
+    //调价
+    $('#calcPriceOption').prop('disabled', 'disabled');
+    $('#gljPriceTenderCoe').prop('disabled', 'disabled');
+    $('#tenderGLJQuantity').prop('disabled', 'disabled');
+    $('#tenderRationQuantity').prop('disabled', 'disabled');
+    $('#tenderPrice').prop('disabled', 'disabled');
+    $('#cleanTender').prop('disabled', 'disabled');
+}
+//项目只读,表格只读
+function disableSpread(spread){
+    spread.unbind(GC.Spread.Sheets.Events.ButtonClicked);
+    let sheetCount = spread.getSheetCount();
+    console.log(sheetCount);
+    for(let i = 0; i < sheetCount; i++){
+        let sheet = spread.getSheet(i);
+        sheet.unbind(GC.Spread.Sheets.Events.ButtonClicked);
+        sheet.unbind(GC.Spread.Sheets.Events.EditStarting);
+        sheet.unbind(GC.Spread.Sheets.Events.EditEnded);
+        sheet.unbind(GC.Spread.Sheets.Events.RangeChanged);
+        sheet.unbind(GC.Spread.Sheets.Events.ClipboardChanging);
+        sheet.unbind(GC.Spread.Sheets.Events.ClipboardChanged);
+        sheet.unbind(GC.Spread.Sheets.Events.CellDoubleClick);
+        sheet.unbind(GC.Spread.Sheets.Events.CellClick);
+        sheet.unbind(GC.Spread.Sheets.Events.ValueChanged);
+        console.log(sheet.name());
+        sheet.suspendPaint();
+        sheet.suspendEvent();
+        sheet.options.isProtected = true;
+        let rowCount = sheet.getRowCount();
+        let colCount = sheet.getColumnCount();
+        for(let row = 0; row < rowCount; row++){
+            for(let col = 0; col < colCount; col++){
+                sheet.getCell(row, col).locked(true);
+            }
+        }
+        sheet.resumePaint();
+        sheet.resumeEvent();
+    }
+}

+ 3 - 0
web/building_saas/main/js/views/side_tools.js

@@ -112,6 +112,9 @@ var sideToolsObj = {
 
 $('.side-tabs ul li a').bind('click', function () {
     var tab = $(this), tabPanel = $(tab.attr('relaPanel'));
+    if(tab.hasClass('disabled')){
+        return;
+    }
     if (!tab.hasClass('active')) {
         $('.side-tabs ul li a').removeClass('active');
         tab.addClass('active');

+ 1 - 1
web/building_saas/main/js/views/std_billsGuidance_lib.js

@@ -526,7 +526,7 @@ const billsGuidance = (function () {
     function bindBtn(){
         //打开清单指引库
         $('#stdBillsGuidanceTab').click(function () {
-            if(libSel.children().length === 0){
+            if(libSel.children().length === 0 && !projectReadOnly){
                 initLibs(projectInfoObj.projectInfo.engineeringInfo.billsGuidance_lib);
             }
         });

+ 6 - 0
web/building_saas/main/js/views/sub_fee_rate_views.js

@@ -95,6 +95,12 @@ var subRateObject={
         subRateObject.valueMap=projectObj.project.FeeRate.getValueMap(item);
         subRateObject.showSubRateData();
         disableRightMenu("subRate",this.subRateSpread);
+        if(projectReadOnly){
+            if(this.subRateSetting.view.lockColumns){
+                this.subRateSetting.view.lockColumns = null;
+            }
+            disableSpread(this.subRateSpread);
+        }
     },
     showSubRateData:function () {
         this.subRateSheet.setRowCount(0);

+ 40 - 22
web/building_saas/main/js/views/sub_view.js

@@ -9,35 +9,43 @@
 
 let subSpread = null;
 let subObj = {
-  initSubSpread:function () {
-      contentOprObj.buildSheet($("#jobSpread")[0]);
-      characterOprObj.buildSheet($("#itemSpread")[0]);
-      $("#tzjnrCon").hide();
-      $("#subSpread").show();
-      subSpread = sheetCommonObj.createSpread($("#subSpread")[0], 4);
-      subSpread.options.allowUserDragDrop = false;
-      subSpread.options.allowUserDragFill = false;
-      subSpread.getSheet(2).name('JSCX');
-
-      pageCCOprObj.active = false;
-
-       // 工料机
-      gljOprObj.initSheet(subSpread.getSheet(0));
+    initSubSpread:function () {
+        contentOprObj.buildSheet($("#jobSpread")[0]);
+        characterOprObj.buildSheet($("#itemSpread")[0]);
+        $("#tzjnrCon").hide();
+        $("#subSpread").show();
+        subSpread = sheetCommonObj.createSpread($("#subSpread")[0], 4);
+        subSpread.options.allowUserDragDrop = false;
+        subSpread.options.allowUserDragFill = false;
+        subSpread.getSheet(2).name('JSCX');
+
+        pageCCOprObj.active = false;
+
+        // 工料机
+        gljOprObj.initSheet(subSpread.getSheet(0));
         //sheetCommonObj.shieldAllCells(subSpread.getSheet(0), gljOprObj.setting);
-      SheetDataHelper.protectdSheet(subSpread.getSheet(0));
+        SheetDataHelper.protectdSheet(subSpread.getSheet(0));
         // assistOprObj.initSheet(subSpread.getSheet(1));
         // sheetCommonObj.shieldAllCells(subSpread.getSheet(1), assistOprObj.setting);
 
+        if(!projectReadOnly){
+            gljContextMenu.loadGLJSpreadContextMenu();
+        }
 
         //工程量明细
-      gljOprObj.initDetailSheet(subSpread.getSheet(1));
-      SheetDataHelper.protectdSheet(subSpread.getSheet(1));
+        gljOprObj.initDetailSheet(subSpread.getSheet(1));
+        SheetDataHelper.protectdSheet(subSpread.getSheet(1));
 
         //安装增加费
-      installationFeeObj.initRationInstallSheet(subSpread.getSheet(3));
-      SheetDataHelper.protectdSheet(subSpread.getSheet(3));
-      gljContextMenu.loadGLJSpreadContextMenu();
-  }
+        installationFeeObj.initRationInstallSheet(subSpread.getSheet(3));
+        SheetDataHelper.protectdSheet(subSpread.getSheet(3));
+
+        if(projectReadOnly){
+            disableSpread(subSpread);
+            disableSpread(contentOprObj.workBook);
+            disableSpread(characterOprObj.workBook);
+        }
+    }
 };
 
 
@@ -126,6 +134,9 @@ $("#linkTZJNR").click(function () {
 // 应用到选中清单
 let isSaving = false;
 $("#use-to-current").click(function() {
+    if(projectReadOnly){
+        return false;
+    }
     if (isSaving) {
         return false;
     }
@@ -152,6 +163,9 @@ $("#use-to-current").click(function() {
 });
 // 应用到所有的清单
 $("#use-to-all").click(function() {
+    if(projectReadOnly){
+        return false;
+    }
     let treeNode = projectObj.mainController.tree;
     const setting = getAddRuleSetting();
     if (treeNode.items === undefined || treeNode.items.length <= 0) {
@@ -303,6 +317,10 @@ function setRule(setting) {
     $("#child-display-format").change();
 
     $("#serial-type").val(setting.serialType);
+
+    if(projectReadOnly){
+        disableTools();
+    }
 }
 
 function activeSubSheetIs(idx){
@@ -392,4 +410,4 @@ $('#linkGCLMX').on('shown.bs.tab', function () {
 
 $('#linkAZZJF').on('shown.bs.tab', function () {
     gljOprObj.showDataIfRationSelect(projectObj.project.mainTree.selected, '111111');
-});
+});

+ 3 - 1
web/building_saas/main/js/views/tender_price_view.js

@@ -388,7 +388,9 @@ let tender_obj={
         $('#calcPriceOption').val(calcPriceOption);
         $('#gljPriceTenderCoe').val(gljPriceTenderCoe);
         if(calcPriceOption == 'coeBase'){
-            $('#tenderPrice').removeAttr("disabled");
+            if(!projectReadOnly){
+                $('#tenderPrice').removeAttr("disabled");
+            }
             $('#tenderGLJQuantity').attr("disabled",true);
             $('#tenderRationQuantity').attr("disabled",true);
         }else {

+ 3 - 0
web/building_saas/main/js/views/zmhs_view.js

@@ -46,6 +46,9 @@ let zmhs_obj = {
         this.assSheet.bind(GC.Spread.Sheets.Events.RangeChanged, this.onAssRangeChanged);
         this.assSheet.name('ration_ass');
         SheetDataHelper.protectdSheet(this.coeSheet);
+        if(projectReadOnly){
+            disableSpread(zmhs_obj.coeSpread);
+        }
     },
     showCoeData:function (node) {
         let selected = node?node:projectObj.project.mainTree.selected;

+ 34 - 3
web/building_saas/pm/js/pm_share.js

@@ -300,11 +300,28 @@ const pmShare = (function () {
         TreeNodeCellType.prototype.processMouseDown = function (hitinfo) {
             let offset = -1;
             let node = tree.items[hitinfo.row];
-            tree.selected = node;
             let centerX = hitinfo.cellRect.x + offset + node.depth() * indent + node.depth() * levelIndent + indent / 2;
-            let centerY = (hitinfo.cellRect.y + offset + (hitinfo.cellRect.y + offset + hitinfo.cellRect.height)) / 2;;
+            let centerY = (hitinfo.cellRect.y + offset + (hitinfo.cellRect.y + offset + hitinfo.cellRect.height)) / 2;
+            let text = hitinfo.sheet.getText(hitinfo.row, hitinfo.col);
+            let value = hitinfo.sheet.getValue(hitinfo.row, hitinfo.col);
+            let acStyle = hitinfo.sheet.getActualStyle(hitinfo.row, hitinfo.col),
+                zoom = hitinfo.sheet.zoom();
+            let textLength = this.getAutoFitWidth(value, text, acStyle, zoom, {sheet: hitinfo.sheet, row: hitinfo.row, col: hitinfo.col, sheetArea: GC.Spread.Sheets.SheetArea.viewport});
+            //(图标+名字)区域
+            function withingClickArea(){
+                return hitinfo.x > centerX + halfBoxLength && hitinfo.x < centerX + halfBoxLength + imgWidth + indent/2+3 + textLength;
+            }
+            //点击单位工程
+            if(node.data.projType === projectType.tender && withingClickArea()){
+                let newTab = window.open('about:blank');
+                BeforeOpenProject(node.data.ID, {'fullFolder': GetFullFolder(node.parent)}, function () {
+                    let mainUrl = `/main?project=${node.data.ID}`;
+                    CommonAjax.get(mainUrl, [], function () {
+                        newTab.location.href = mainUrl;
+                    });
+                });
+            }
             if (!node || node.children.length === 0) { return; }
-
             if (hitinfo.x > centerX - halfBoxLength && hitinfo.x < centerX + halfBoxLength && hitinfo.y > centerY - halfBoxLength && hitinfo.y < centerY + halfBoxLength) {
                 node.setExpanded(!node.expanded);
                 TREE_SHEET_HELPER.massOperationSheet(hitinfo.sheet, function () {
@@ -318,6 +335,20 @@ const pmShare = (function () {
                 hitinfo.sheet.repaint();
             }
         };
+        TreeNodeCellType.prototype.processMouseMove = function (hitInfo) {
+            let sheet = hitInfo.sheet;
+            let div = sheet.getParent().getHost();
+            let canvasId = div.id + "vp_vp";
+            let canvas = $(`#${canvasId}`)[0];
+            //改变鼠标图案
+            if (sheet && hitInfo.isReservedLocation) {
+                canvas.style.cursor='pointer';
+                return true;
+            }else{
+                canvas.style.cursor='default';
+            }
+            return false;
+        };
         return new TreeNodeCellType();
     }
     //

+ 2 - 5
web/building_saas/report/html/rpt_print.html

@@ -37,12 +37,10 @@
 <script type="text/javascript" src="/web/building_saas/report/js/jpc_output.js"></script>
 <script type="text/javascript" src="/web/building_saas/report/js/rpt_print.js"></script>
 <SCRIPT type="text/javascript">
-    let canvasArr = [];
     function loading() {
         if (sessionStorage.currentPageData) {
             let pageData = JSON.parse(sessionStorage.currentPageData);
-//            let scaleFactor = parseInt(sessionStorage.scaleFactor);
-            let scaleFactor = 1;
+            let scaleFactor = parseInt(sessionStorage.scaleFactor);
             $(document).attr("title", pageData[JV.NODE_PAGE_INFO][JV.NODE_MAIN_INFO_RPT_NAME]);
             let orgHeight = 793, orgWidth = 1122;
             let pageHeight = orgHeight * scaleFactor, pageWidth = orgWidth * scaleFactor;
@@ -76,13 +74,12 @@
                     elementSvg.setAttribute('width', pageWidth);
                 });
             });
-//            $("body").css({"page": "page"});
             window.print();
-            //document.execCommand("print");
         } else {
             //alert("没有报表数据!");
         }
     }
+
     function closing() {
         //
     }

+ 0 - 2
web/building_saas/report/js/rpt_print.js

@@ -5,7 +5,6 @@
 let rptPrintHelper = {
     preview: function () {
         if (zTreeOprObj.currentRptPageRst) {
-            // window.location.href = '/rpt_print';
             sessionStorage.currentPageData = JSON.stringify(zTreeOprObj.currentRptPageRst);
             sessionStorage.pageSize = rptControlObj.getCurrentPageSize();
             sessionStorage.orientation = rptControlObj.getCurrentOrientation();
@@ -16,7 +15,6 @@ let rptPrintHelper = {
         }
     },
     buildSvgArr: function (pagesData, offsetX, offsetY) {
-        let me = this;
         let styles = pagesData[JV.NODE_STYLE_COLLECTION],
             fonts = pagesData[JV.NODE_FONT_COLLECTION],
             controls = pagesData[JV.NODE_CONTROL_COLLECTION]

+ 6 - 2
web/common/html/header.html

@@ -1,5 +1,9 @@
 <nav class="navbar navbar-expand-lg p-0 d-flex">
-    <a class="header-logo px-2" href="/pm" style="text-decoration: none;">纵横云计价<sup><%= versionName %></sup></a>
+    <div class="header-logo">
+        <div class="v-title">纵横云计价</div>
+        <div class="p-title"><%= versionName %></div>
+    </div>
+    <!--<a class="header-logo px-2" href="/pm" style="text-decoration: none;">纵横云计价<sup><%= versionName %></sup></a>-->
     <ul class="nav navbar-nav px-1" id="header-menu" style="display: none;">
         <li class="nav-item">
             <a class="nav-link" href="#" aria-expanded="false" data-toggle="modal" data-target="#poj-set"><i class="fa fa-cube"></i> 项目属性</a>
@@ -12,7 +16,7 @@
             <div class="dropdown-menu">
                 <!--<a class="dropdown-item" href="/complementaryRation/main">定额库编辑器</a>-->
                 <a class="dropdown-item" href="javascript:void(0);" aria-expanded="false" data-toggle="modal" data-target="#opts-set"><i class="fa fa-sliders"></i> 选项</a>
-                <a class="dropdown-item" href="javascript:void(0);" data-toggle="modal" data-target="#comple-ration">定额库编辑器</a>
+                <a id="compleRationLib" class="dropdown-item" href="javascript:void(0);" data-toggle="modal" data-target="#comple-ration">定额库编辑器</a>
                 <a id="compleGljLib" class="dropdown-item" href="javascript:void(0);">人材机库编辑器</a>
             </div>
         </li>