Explorar o código

公路/建筑数据后台,获取CLD办事处数据,改用接口获取CLD2.0数据(GLY-7398)

zhangweicheng hai 17 horas
pai
achega
001dede1df

+ 4 - 4
modules/all_models/compilation.js

@@ -25,7 +25,7 @@ let childrenSchema = new Schema(
     },
     fileTypes: [Number], //创建项目时可用文件类型 估算,概算,预算 变更预算
   },
-  { _id: false }
+  { _id: false },
 );
 let modelSchema = {
   // 是否发布
@@ -82,8 +82,8 @@ let modelSchema = {
   },
   // cld 办事处id
   categoryID: {
-    type: Number,
-    default: 12, // 总部id
+    type: String,
+    default: "McQlO0O0OdO0O0Oi", // 总部id
   },
   defaultLocation: String, //默认工程所在地
   freeUse: Boolean,
@@ -91,5 +91,5 @@ let modelSchema = {
 };
 mongoose.model(
   collectionName,
-  new Schema(modelSchema, { versionKey: false, collection: collectionName })
+  new Schema(modelSchema, { versionKey: false, collection: collectionName }),
 );

+ 104 - 52
modules/common/const/category_const.js

@@ -1,4 +1,4 @@
-'use strict';
+"use strict";
 
 /**
  * CLD 办事处常量
@@ -7,61 +7,113 @@
  * @date 2018/9/25
  * @version
  */
+const crypto = require("crypto");
+const axios = require("axios");
 
 const category = {
-    // ANHUI: 2,
-    GANSU: 3,
-    GUANGDONG: 4,
-    GUANGXI: 5,
-    JIANGXI: 6,
-    SICHUAN: 7,
-    CHONGQING: 8,
-    NEIMENG: 9,
-    ZHEJIANG: 10,
-    SHANDONG: 11,
-    ZONGBU: 12,
-    // YUNNAN: 13,
-    // GUIZHOU: 14,
-    // BEIJING: 15,
-    // FUJIAN: 16,
-    // HAINAN: 17,
-    // HEBEI: 18,
-    // HENAN: 19,
-    // HEILONGJIANG: 20,
-    // HUBEI: 21,
-    // HUNAN: 22,
-    // JILIN: 23,
-    // JIANGSU: 24,
-    // LIAONING: 25,
-    // NINGXIA: 26
+  // ANHUI: 2,
+  GANSU: 3,
+  GUANGDONG: 4,
+  GUANGXI: 5,
+  JIANGXI: 6,
+  SICHUAN: 7,
+  CHONGQING: 8,
+  NEIMENG: 9,
+  ZHEJIANG: 10,
+  SHANDONG: 11,
+  ZONGBU: 12,
+  // YUNNAN: 13,
+  // GUIZHOU: 14,
+  // BEIJING: 15,
+  // FUJIAN: 16,
+  // HAINAN: 17,
+  // HEBEI: 18,
+  // HENAN: 19,
+  // HEILONGJIANG: 20,
+  // HUBEI: 21,
+  // HUNAN: 22,
+  // JILIN: 23,
+  // JIANGSU: 24,
+  // LIAONING: 25,
+  // NINGXIA: 26
 };
 
 const categoryList = [
-    // {id: category.ANHUI, name: '安徽办'},
-    {id: category.GANSU, name: '甘肃办'},
-    {id: category.GUANGDONG, name: '广东办'},
-    {id: category.GUANGXI, name: '广西办'},
-    {id: category.JIANGXI, name: '江西办'},
-    {id: category.SICHUAN, name: '四川办'},
-    {id: category.CHONGQING, name: '重庆办'},
-    {id: category.NEIMENG, name: '内蒙办'},
-    {id: category.ZHEJIANG, name: '浙江办'},
-    {id: category.SHANDONG, name: '山东办'},
-    {id: category.ZONGBU, name: '总部'},
-    // {id: category.YUNNAN, name: '云南办'},
-    // {id: category.GUIZHOU, name: '贵州办'},
-    // {id: category.BEIJING, name: '北京办'},
-    // {id: category.FUJIAN, name: '福建办'},
-    // {id: category.HAINAN, name: '海南办'},
-    // {id: category.HEBEI, name: '河北办'},
-    // {id: category.HENAN, name: '河南办'},
-    // {id: category.HEILONGJIANG, name: '黑龙江办'},
-    // {id: category.HUBEI, name: '湖北办'},
-    // {id: category.HUNAN, name: '湖南办'},
-    // {id: category.JILIN, name: '吉林办'},
-    // {id: category.JIANGSU, name: '江苏办'},
-    // {id: category.LIAONING, name: '辽宁办'},
-    // {id: category.NINGXIA, name: '宁夏办'}
+  // {id: category.ANHUI, name: '安徽办'},
+  { id: category.GANSU, name: "甘肃办" },
+  { id: category.GUANGDONG, name: "广东办" },
+  { id: category.GUANGXI, name: "广西办" },
+  { id: category.JIANGXI, name: "江西办" },
+  { id: category.SICHUAN, name: "四川办" },
+  { id: category.CHONGQING, name: "重庆办" },
+  { id: category.NEIMENG, name: "内蒙办" },
+  { id: category.ZHEJIANG, name: "浙江办" },
+  { id: category.SHANDONG, name: "山东办" },
+  { id: category.ZONGBU, name: "总部" },
+  // {id: category.YUNNAN, name: '云南办'},
+  // {id: category.GUIZHOU, name: '贵州办'},
+  // {id: category.BEIJING, name: '北京办'},
+  // {id: category.FUJIAN, name: '福建办'},
+  // {id: category.HAINAN, name: '海南办'},
+  // {id: category.HEBEI, name: '河北办'},
+  // {id: category.HENAN, name: '河南办'},
+  // {id: category.HEILONGJIANG, name: '黑龙江办'},
+  // {id: category.HUBEI, name: '湖北办'},
+  // {id: category.HUNAN, name: '湖南办'},
+  // {id: category.JILIN, name: '吉林办'},
+  // {id: category.JIANGSU, name: '江苏办'},
+  // {id: category.LIAONING, name: '辽宁办'},
+  // {id: category.NINGXIA, name: '宁夏办'}
 ];
 
-export {category as default, categoryList as List};
+const SECRET_KEY = "iIxMzQxMTQxNzMyNyIsImNhdGVnb3J5IjoiXHU";
+
+function generateSign(params, timestamp, nonce, secretKey = SECRET_KEY) {
+  // 按字母顺序排序参数
+  const sortedKeys = Object.keys(params).sort();
+
+  // 拼接参数字符串:key1=value1&key2=value2&...
+  const queryString = sortedKeys
+    .map((key) => `${key}=${params[key]}`)
+    .join("&");
+
+  // 添加 timestamp 和 nonce
+  const stringToSign = `${queryString}&timestamp=${timestamp}&nonce=${nonce}`;
+
+  console.log("待签名字符串:", stringToSign);
+
+  // 生成 HMAC-SHA256 签名
+  const sign = crypto
+    .createHmac("sha256", secretKey)
+    .update(stringToSign)
+    .digest("hex");
+
+  return sign;
+}
+
+async function getCategoryList() {
+  try {
+    const timestamp = Math.floor(Date.now() / 1000).toString();
+    const nonce = crypto.randomBytes(16).toString("hex");
+    const params = {};
+    const sign = generateSign(params, timestamp, nonce);
+
+    const response = await axios.get(
+      "https://zhcld.com/external/building/department",
+      {
+        headers: {
+          "X-Building-Timestamp": timestamp,
+          "X-Building-Sign": sign,
+          "X-Building-Nonce": nonce,
+        },
+      },
+    );
+
+    return response.data.data || [];
+  } catch (err) {
+    console.error("getCategoryList error:", err);
+    return [];
+  }
+}
+
+export { category as default, categoryList as List, getCategoryList };

+ 30 - 30
modules/users/controllers/compilation_controller.js

@@ -42,7 +42,7 @@ import overHeightFacade from "../../over_height_lib/facade/over_height_facade";
 import progressiveFacade from "../../progressive_interval_lib/facade/progressive_facade";
 import {
   default as category,
-  List as categoryList,
+  getCategoryList,
 } from "../../common/const/category_const.js";
 import locationList from "../../common/const/locationList";
 
@@ -92,10 +92,11 @@ class CompilationController extends BaseController {
     ) {
       selectedCompilation.example = selectedCompilation.example.join(";");
     }
+    let categoryData = await getCategoryList();
     let renderData = {
       id: id,
       compilationList: compilationList,
-      categoryList: categoryList,
+      categoryList: categoryData,
       locationList: locationList,
       selectedCompilation: selectedCompilation,
       layout: "users/views/layout/layout",
@@ -134,7 +135,7 @@ class CompilationController extends BaseController {
       let valuationId = await compilationModel.addValuation(
         id,
         section,
-        insertData
+        insertData,
       );
       if (!valuationId) {
         throw "新增计价规则失败";
@@ -176,7 +177,7 @@ class CompilationController extends BaseController {
       [valuationData, valuationList] = await compilationModel.getValuation(
         selectedCompilation._id,
         valuationId,
-        section
+        section,
       );
       if (Object.keys(valuationData).length <= 0) {
         throw "不存在数据";
@@ -184,9 +185,8 @@ class CompilationController extends BaseController {
 
       // 获取计价规则中对应的标准库数据
       let engineeringLibModel = new EngineeringLibModel();
-      engineeringList = await engineeringLibModel.getLibsByValuationID(
-        valuationId
-      );
+      engineeringList =
+        await engineeringLibModel.getLibsByValuationID(valuationId);
       engineeringList = _.sortBy(engineeringList, ["seq"]);
     } catch (error) {
       console.log(error);
@@ -263,7 +263,7 @@ class CompilationController extends BaseController {
       // 获取费率标准库
       let stdFeeRateLibsModel = new STDFeeRateLibsModel();
       feeRateList = await stdFeeRateLibsModel.getFeeRateList(
-        selectedCompilation._id
+        selectedCompilation._id,
       );
 
       // 获取人工系数标准库
@@ -273,24 +273,24 @@ class CompilationController extends BaseController {
       // 获取计算程序库
       let stdCalcProgramModel = new STDCalcProgramModel();
       calculationList = await stdCalcProgramModel.getProgramList(
-        selectedCompilation._id
+        selectedCompilation._id,
       );
 
       //获取列设置库
       mainTreeColList = await mainColFacade.getColLibsByCompilationID(
-        selectedCompilation._id
+        selectedCompilation._id,
       );
 
       //获取清单模板库
       billTemplateList = await billTemplateFacade.getTemplateLibByCompilationID(
-        selectedCompilation._id
+        selectedCompilation._id,
       );
 
       // 获取对应的计价规则数据
       [valuationData, valuationList] = await compilationModel.getValuation(
         selectedCompilation._id,
         valuationId,
-        section
+        section,
       );
       if (Object.keys(valuationData).length <= 0) {
         throw "不存在数据";
@@ -306,7 +306,7 @@ class CompilationController extends BaseController {
       let billsTemplateModel = new BillsTemplateModel();
       billsTemplateData = await billsTemplateModel.getTemplateData(
         valuationId,
-        libData.engineering
+        libData.engineering,
       );
 
       //获取清单指引数据
@@ -319,7 +319,7 @@ class CompilationController extends BaseController {
       featureList = await projectFeatureFacade.findByCondition(
         { compilationId: selectedCompilation._id },
         "-feature",
-        false
+        false,
       );
 
       //获取基本信息库
@@ -329,27 +329,27 @@ class CompilationController extends BaseController {
       itemIncreaseList = await itemIncreaseFacade.findByCondition(
         {},
         "-template",
-        false
+        false,
       );
 
       engineerInfoList = await engineerInfoFacade.findByCondition(
         {},
         null,
-        false
+        false,
       );
 
       //获取累进区间库
       progressiveList = await progressiveFacade.findByCondition(
         {},
         null,
-        false
+        false,
       );
 
       //工程特征指标库
       engineerFeatureList = await engineerFeatureFacade.findByCondition(
         {},
         null,
-        false
+        false,
       );
 
       //主要工料指标
@@ -506,7 +506,7 @@ class CompilationController extends BaseController {
         let engineeringLibModel = new EngineeringLibModel();
         result.data = await engineeringLibModel.updateById(
           data.id,
-          data.updateData
+          data.updateData,
         );
       } else {
         throw new Error("提交数据有误");
@@ -536,7 +536,7 @@ class CompilationController extends BaseController {
       let compilationModel = new CompilationModel();
       let result = await compilationModel.saveValuation(
         valuationId,
-        request.body
+        request.body,
       );
       if (!result) {
         throw "保存失败";
@@ -579,7 +579,7 @@ class CompilationController extends BaseController {
       let result = compilationModel.deleteValuation(
         selectedCompilation._id,
         id,
-        section
+        section,
       );
       if (!result) {
         throw "删除失败";
@@ -783,7 +783,7 @@ class CompilationController extends BaseController {
         compilationId,
         overWriteUrl,
         pricePropertiesTemplate,
-        consumeAmtPropertiesTemplate
+        consumeAmtPropertiesTemplate,
       );
       response.json({ err: 0, msg: "", data: null });
     } catch (err) {
@@ -829,21 +829,21 @@ class CompilationController extends BaseController {
       [valuationData, valuationList] = await compilationModel.getValuation(
         selectedCompilation._id,
         valuationId,
-        section
+        section,
       );
       compilationList = await compilationModel.getCompilationList();
 
       // 获取标准清单
       let stdBillLibListsModel = new STDBillLibListsModel();
       billList = await stdBillLibListsModel.getBillList(
-        selectedCompilation._id
+        selectedCompilation._id,
       );
 
       // 获取清单模板数据
       let billsTemplateModel = new BillsTemplateModel();
       billsTemplateData = await billsTemplateModel.getTemplateData(
         valuationId,
-        engineering
+        engineering,
       );
     } catch (error) {
       console.log(error);
@@ -883,7 +883,7 @@ class CompilationController extends BaseController {
     let result = await billsTemplateModel.updateTemplate(
       valuationId,
       engineering,
-      data
+      data,
     );
 
     if (result) {
@@ -899,7 +899,7 @@ class CompilationController extends BaseController {
       const engineeringLibModel = new EngineeringLibModel();
       await engineeringLibModel.copyRationLibsToOthers(
         valuationID,
-        engineeringID
+        engineeringID,
       );
       res.json({ error: 0, message: "复制成功", data: null });
     } catch (err) {
@@ -916,7 +916,7 @@ class CompilationController extends BaseController {
         compilationID,
         valuationType,
         orgValuationID,
-        newName
+        newName,
       );
       res.json({ error: 0, message: "复制成功", data: null });
     } catch (err) {
@@ -945,7 +945,7 @@ class CompilationController extends BaseController {
   async changeCategory(request, response) {
     let valuationId = request.body.id;
     let category = request.body.category;
-    category = parseInt(category);
+    // category = parseInt(category);
     try {
       let compilationModel = new CompilationModel();
       let result = await compilationModel.updateCategory(valuationId, category);
@@ -973,7 +973,7 @@ class CompilationController extends BaseController {
       let compilationModel = new CompilationModel();
       let result = await compilationModel.updateLocation(
         compilationId,
-        location
+        location,
       );
       if (result) {
         response.json({ error: 0, message: "", data: null });

+ 118 - 23
web/users/js/compilation.js

@@ -74,7 +74,7 @@ $(document).ready(function () {
               model +
               "_lib'][data-id='" +
               addLib.id +
-              "']"
+              "']",
           ).length > 0
         ) {
           alert("重复添加数据!");
@@ -166,7 +166,7 @@ $(document).ready(function () {
         } else if (actionType == "modify") {
           let oldIndex = $("#groupIndex").val();
           let parentTr = $("input[data-id = " + oldIndex + "]").parents(
-            ".taxGroup_tr"
+            ".taxGroup_tr",
           );
           parentTr.after(htmlString);
           parentTr.remove();
@@ -449,7 +449,7 @@ $(document).ready(function () {
       })">
           <span class="glyphicon glyphicon-arrow-up"></span>
           <input type="hidden" class="sort_temp_value"  value='${JSON.stringify(
-            { id: lib.id, name: lib.name }
+            { id: lib.id, name: lib.name },
           )}'>
       </a>
       <a class="pull-right stdBillUp"  title="下移" onclick="libDown(${
@@ -478,8 +478,8 @@ $(document).ready(function () {
           lib.id
         }" value='${JSON.stringify({ id: lib.id, name: lib.name })}'>
         ${index === 0 ? '<i class="glyphicon glyphicon-flag"></i>&nbsp;' : ""}${
-        lib.name
-      }
+          lib.name
+        }
       </p>`;
     });
     $(".bill-list").html(html);
@@ -583,7 +583,7 @@ $(document).ready(function () {
         $(this).parent().remove();
     })*/
   $(
-    ".bill-list, .ration-list, .glj-list, .fee-list, .artificial-list, .program-list, .billsGuidance-list,.feature-list,.info-list,.progressive-list,.engineer_info-list,.engineer_feature-list,.material-list,.main_quantity-list,.economic-list,.over_height-list,.item_increase-list"
+    ".bill-list, .ration-list, .glj-list, .fee-list, .artificial-list, .program-list, .billsGuidance-list,.feature-list,.info-list,.progressive-list,.engineer_info-list,.engineer_feature-list,.material-list,.main_quantity-list,.economic-list,.over_height-list,.item_increase-list",
   ).on("click", ".remove-lib", function () {
     $(this).parent().remove();
   });
@@ -827,20 +827,115 @@ $(document).ready(function () {
   });
   //
 
-  // CLD 办事处选择
-  $("#category-select").change(function () {
-    $.ajax({
-      url: "/compilation/changeCategory",
-      type: "post",
-      data: { id: id, category: $(this).val() },
-      dataType: "json",
-      success: function (response) {
-        if (response.error !== 0) {
-          alert("更改失败");
+  // CLD 办事处选择 - 多级子菜单
+  (function () {
+    function buildTree(list) {
+      var map = {};
+      var roots = [];
+      list.forEach(function (item) {
+        map[item.id] = { data: item, children: [] };
+      });
+      list.forEach(function (item) {
+        if (item.parentId && item.parentId !== "0" && map[item.parentId]) {
+          map[item.parentId].children.push(map[item.id]);
+        } else {
+          roots.push(map[item.id]);
         }
-      },
+      });
+      return roots;
+    }
+
+    function renderTree(nodes, depth) {
+      depth = depth || 0;
+      var html = "";
+      nodes.forEach(function (node) {
+        var hasChildren = node.children.length > 0;
+        var paddingLeft = depth * 16 + 10 + "px";
+        html += '<li style="padding:0;">';
+        html +=
+          '<div class="category-node" data-id="' +
+          node.data.id +
+          '" data-name="' +
+          node.data.name +
+          '" style="padding:5px 10px 5px ' +
+          paddingLeft +
+          '; cursor:pointer; white-space:nowrap; display:flex; align-items:center; justify-content:space-between; user-select:none;">';
+        html += "<span>" + node.data.name + "</span>";
+        if (hasChildren) {
+          html +=
+            '<span class="category-toggle" style="margin-left:8px; font-size:12px; color:#555; cursor:pointer;">' +
+            (depth === 0 ? "&#9660;" : "&#9654;") +
+            "</span>";
+        }
+        html += "</div>";
+        if (hasChildren) {
+          html +=
+            '<ul style="list-style:none; padding:0; margin:0; display:' +
+            (depth === 0 ? "block" : "none") +
+            ';">';
+          html += renderTree(node.children, depth + 1);
+          html += "</ul>";
+        }
+        html += "</li>";
+      });
+      return html;
+    }
+
+    if (typeof categoryListData === "undefined") {
+      categoryListData = JSON.parse(
+        document.getElementById("category-list-json").value || "[]",
+      );
+      currentCategoryID =
+        document.getElementById("category-current-id").value || "";
+    }
+    if (categoryListData.length) {
+      var tree = buildTree(categoryListData);
+      $("#category-tree").html(renderTree(tree));
+      if (currentCategoryID) {
+        var found = categoryListData.find(function (item) {
+          return item.id == currentCategoryID;
+        });
+        if (found) $("#category-label").text(found.name);
+      }
+    }
+
+    $(document).on("click", "#category-tree .category-toggle", function (e) {
+      var $children = $(this).closest("li").children("ul");
+      $children.toggle();
+      $(this).html($children.is(":visible") ? "&#9660;" : "&#9654;");
     });
-  });
+
+    $(document).on("click", "#category-tree .category-node", function (e) {
+      if ($(e.target).hasClass("category-toggle")) return;
+      var categoryId = $(this).data("id");
+      var categoryName = $(this).data("name");
+      $("#category-label").text(categoryName);
+      $("#category-select").val(categoryId);
+      $("#category-panel").hide();
+      $.ajax({
+        url: "/compilation/changeCategory",
+        type: "post",
+        data: { id: id, category: categoryId },
+        dataType: "json",
+        success: function (response) {
+          if (response.error !== 0) {
+            alert("更改失败");
+          }
+        },
+      });
+    });
+
+    $("#category-dropdown-btn").on("click", function (e) {
+      e.stopPropagation();
+      $("#category-panel").toggle();
+    });
+
+    $(document).on("click", function (e) {
+      if (!$(e.target).closest("#category-dropdown-wrap").length) {
+        $("#category-panel").hide();
+      }
+    });
+  })();
 
   // 选择默认所在地
   $("#location-select").change(function () {
@@ -1490,7 +1585,7 @@ function deleteEngineerClick(engineerID, element) {
     },
     null,
     ["确定", "取消"],
-    false
+    false,
   );
 }
 
@@ -1509,7 +1604,7 @@ function copyEngineerClick(engineerID) {
     },
     null,
     ["确定", "取消"],
-    false
+    false,
   );
 }
 
@@ -1533,7 +1628,7 @@ function updateEngineer(engineerID, data, callback) {
       if (callback) {
         callback();
       }
-    }
+    },
   );
 }
 
@@ -1546,11 +1641,11 @@ function editTaxGroup(ele) {
   if (!_.isEmpty(groupData)) {
     $("#taxType").val(groupData.taxType ? groupData.taxType : "");
     $("#program_lib").val(
-      groupData.program_lib ? groupData.program_lib.id : ""
+      groupData.program_lib ? groupData.program_lib.id : "",
     );
     $("#norm_lib").val(groupData.norm_lib ? groupData.norm_lib : "");
     $("#template_lib").val(
-      groupData.template_lib ? groupData.template_lib.id : ""
+      groupData.template_lib ? groupData.template_lib.id : "",
     );
     $("#col_lib").val(groupData.col_lib ? groupData.col_lib.id : "");
     $("#fee_lib").val(groupData.fee_lib ? groupData.fee_lib.id : "");

+ 13 - 5
web/users/views/compilation/index.html

@@ -118,11 +118,17 @@
                 <tr><td><span>重写路径:</span><input class="form-control" type="text" id="overWriteUrl" value="<%= selectedCompilation.overWriteUrl%>"></td></tr>
                 <tr><td><span>例题建设项目ID:</span><input class="form-control" type="text" id="example" value="<%= selectedCompilation.example%>"></td></tr>
                 <tr><td><p>显示办事处销售信息</p>
-                    <select class="form-control" style="width:200px" id="category-select">
-                        <% categoryList.forEach(function(category) { %>
-                        <option value="<%= category.id %>" <% if (selectedCompilation.categoryID !== undefined && category.id === selectedCompilation.categoryID) { %>selected="selected"<% } %> ><%= category.name %></option>
-                        <% }) %>
-                    </select>
+                    <div style="position:relative; display:inline-block; width:200px;" id="category-dropdown-wrap">
+                        <button type="button" class="btn btn-default btn-block" id="category-dropdown-btn"
+                            style="text-align:left; padding-right:24px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; position:relative;">
+                            <span id="category-label">请选择</span>
+                            <span class="caret" style="position:absolute; right:10px; top:50%; margin-top:-2px;"></span>
+                        </button>
+                        <div id="category-panel" style="display:none; position:absolute; z-index:1000; background:#fff; border:1px solid #ccc; border-radius:4px; min-width:200px; max-height:400px; overflow-y:auto; box-shadow:0 6px 12px rgba(0,0,0,.175);">
+                            <ul id="category-tree" style="list-style:none; padding:5px 0; margin:0;"></ul>
+                        </div>
+                    </div>
+                    <input type="hidden" id="category-select" value="<%= selectedCompilation.categoryID || '' %>">
                 </td></tr>
               
                 <tr><td><p>默认工程所在地</p>
@@ -161,5 +167,7 @@
         <% } %>
 	</div>
 </div>
+<input type="hidden" id="category-list-json" value="<%- (JSON.stringify(categoryList || [])).replace(/"/g, '&quot;') %>">
+<input type="hidden" id="category-current-id" value="<%= selectedCompilation.categoryID || '' %>">
 <script type="text/javascript" src="/web/users/js/compilation.js"></script>
 <%include ../compilation/modal.html %>