Jelajahi Sumber

Merge branch '1.0.0_online' of http://192.168.1.41:3000/SmartCost/ConstructionCost into 1.0.0_online

zhangweicheng 6 tahun lalu
induk
melakukan
9147ee99c1

+ 7 - 3
modules/pm/facade/pm_facade.js

@@ -1049,11 +1049,15 @@ async function getFullPath(projectID) {
 //获取projectIDs文件下所有子项目(不包括projectIDs本身)
 async function getPosterityProjects(projectIDs) {
     let rst = [];
-    async function getProjects(IDs) {
-        if (IDs.length > 0) {
+    async function getProjects(parentIDs) {
+        if (parentIDs.length > 0) {
             let newIDs = [];
-            let projs = await projectModel.find({ParentID: {$in: IDs}, $or: notDeleted}, '-_id');
+            let projs = await projectModel.find({ParentID: {$in: parentIDs}, $or: notDeleted}, '-_id');
             for (let proj of projs) {
+                // 兼容旧的错误数据,可能ParentID和ID相同
+                if (parentIDs.includes(proj.ID)) {
+                    continue;
+                }
                 rst.push(proj);
                 newIDs.push(proj.ID);
             }

+ 2 - 2
modules/users/models/user_model.js

@@ -322,14 +322,14 @@ class UserModel extends BaseModel {
      * @return {version}
      */
     async getVersionFromUpgrade(ssoId, compilationId){
-        let version = '大司空云计价';
+        let version = '大司空云计价(免费版)';
         let userData = await this.findDataBySsoId(ssoId);
         if (userData.upgrade_list !== undefined) {
             let compilationInfo = userData.upgrade_list.find(function (item) {
                 return item.compilationID === compilationId;
             });
             if (compilationInfo !== undefined && compilationInfo.isUpgrade === true) {
-                version = '大司空云计价';
+                version = '大司空云计价(专业版)';
             }
         }
         return version;

+ 12 - 32
public/web/PerfectLoad.js

@@ -133,47 +133,27 @@ const SCComponent = (() => {
     * 假滚动条,0 - 80% 快, 80% - 95%很慢,95%开始停住,直到调用end方法
     * */
     const InitProgressBar = (() => {
-        function ProgressBar($modal = $('#progress'), $title = $('#progress-title'),
-                             $content = $('#progress-content'), $bar = $('#progressBar')) {
-            this.$modal = $modal;
-            this.$title = $title;
-            this.$content = $content;
-            this.$bar = $bar;
+        function ProgressBar() {
+            this.$modal = $('#progress');
+            this.$title = $('#progress-title');
+            this.$title.text('导出');
+            this.$content = $('#progress-content');
+            this.$content.text('正在导出...');
+            this.$barCover = $('#progressCover');
+            this.animationName = 'progress-cover-move';
         }
         //显示滚动条,自动处理滚动条速度
         ProgressBar.prototype.start = function (title, content) {
             this.$title.text(title);
             this.$content.text(content);
-            this.$bar.css('width', `0%`);
+            this.$barCover.css('transform', 'translate(0)');
+            this.$barCover.addClass(this.animationName);
             this.$modal.modal('show');
-            this.outer = setInterval(() => {
-                let curWidth = parseInt(this.$bar[0].style.width.replace('%', ''));
-                curWidth = parseInt(curWidth + 2);
-                this.$bar.css('width', `${curWidth}%`);
-                if (curWidth >= 80) {
-                    clearInterval(this.outer);
-                }
-            }, 100);
-            this.inner = setInterval(() => {
-                let curWidth = parseFloat(this.$bar[0].style.width.replace('%', ''));
-                if (curWidth >= 80) {
-                    curWidth = parseFloat(curWidth + 0.1);
-                    this.$bar.css('width', `${curWidth}%`);
-                    if (curWidth >= 95) {
-                        clearInterval(this.inner);
-                    }
-                }
-            }, 500);
         };
         //结束显示滚动条,滚动条从当前位置滚到100% 消失
         ProgressBar.prototype.end = function () {
-            if (this.outer) {
-                clearInterval(this.outer);
-            }
-            if (this.inner) {
-                clearInterval(this.inner);
-            }
-            $('#progressBar').css('width', '100%');
+            this.$barCover.removeClass(this.animationName);
+            this.$barCover.css('transform', 'translate(100%)');
             setTimeout(() => {
                 this.$modal.modal('hide');
             }, 500);

+ 49 - 0
web/building_saas/css/custom.css

@@ -373,4 +373,53 @@ input.text-right{
 .annotate-color-7::before{
     color:#ECE0F5 !important;
     -webkit-text-stroke:.5px #ced4da;
+}
+.progress-bar {
+    position: relative;
+    width: 100%;
+}
+.progress-move {
+    position: absolute;
+    left: 0;
+    top: 0;
+    z-index: 999;
+    width: 200%;
+    height: 100%;
+    border-radius: .25rem;
+    animation: progressMove 20s linear infinite;
+}
+@keyframes progressMove {
+    from {
+        transform: translateX(0);
+    }
+    to {
+        transform: translateX(-280px);
+    }
+}
+.progress-cover {
+    position: absolute;
+    left: 0;
+    top: 0;
+    z-index: 1000;
+    width: 100%;
+    height: 100%;
+    background-color: #e9ecef;
+}
+.progress-cover-move {
+    animation: progressCoverMove 40s linear;
+    animation-fill-mode: forwards;
+}
+@keyframes progressCoverMove {
+    0% {
+        transform: translateX(0);
+    }
+    20% {
+        transform: translateX(330px);
+    }
+    40% {
+        transform: translateX(380px);
+    }
+    100% {
+        transform: translateX(420px);
+    }
 }

+ 5 - 1
web/building_saas/main/html/main.html

@@ -2170,7 +2170,11 @@
             <div class="modal-body">
                 <h5 id="progress-content" class="my-3"></h5>
                 <div class="progress mb-3">
-                    <div class="progress-bar progress-bar-striped progress-bar-animated" id="progressBar" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 0%"></div>
+                    <!--<div class="progress-bar progress-bar-striped progress-bar-animated" id="progressBar" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">-->
+                    <div class="progress-bar" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">
+                        <div class="progress-move progress-bar-striped"></div>
+                        <div class="progress-cover" id="progressCover"></div>
+                    </div>
                 </div>
             </div>
         </div>

+ 1 - 1
web/building_saas/main/js/models/exportSEIInterface.js

@@ -30,7 +30,7 @@ async function exportSEI(projectID) {
 
     let tenderProjects = [];
     let result = await ajaxPost("/project/getSEIProjects",{projectID:projectID});
-    let pr = new SCComponent.InitProgressBar($('#progress'), $('#progress-title'), $('#progress-content'), $('#progressBar'));
+    let pr = new SCComponent.InitProgressBar();
     pr.start('导出数据接口', '正在导出文件,请稍候……');
     console.log(result);
     let project = getProject(result);

+ 69 - 67
web/building_saas/main/js/models/exportStdInterfaceBase.js

@@ -121,9 +121,9 @@ const XML_EXPORT_BASE = (() => {
         _cache.failList.push(...this.fail);
     }
     /*
-    * xml字符实体的处理,这些特殊字符不处理会导致xml文件格式出错:""、<>、&
-    * 要先处理&amp
-    * */
+     * xml字符实体的处理,这些特殊字符不处理会导致xml文件格式出错:""、<>、&
+     * 要先处理&amp
+     * */
     let _xmlEntity = {
         '&': '&amp;',
         '\n': '&#xA;',
@@ -177,11 +177,13 @@ const XML_EXPORT_BASE = (() => {
                 data.value = data.value.replace(/\s{1,}/g, ' ');
             }
             /*if (!data.value && !data.minLen && !data.enumeration) {  //值为空,且没有限制最小字符数,且没有限制值,则不需判断
-                rst.filterAttrs.push(data);
-                continue;
-            }*/
-            let isFail = false,
-                tempFail = '';
+             rst.filterAttrs.push(data);
+             continue;
+             }*/
+            let isFail = false;
+            let tempFail = '';
+            // 提示的名称需要处理,有的接口xml属性为英文,需要显示其展示名称
+            const name = data.dName || data.name;
             if (data.minLen && data.value.length < data.minLen){
                 isFail = true;
                 tempFail = data.failHint
@@ -309,27 +311,27 @@ const XML_EXPORT_BASE = (() => {
     function isDef(v) {
         return typeof v !== 'undefined' && v !== null;
     }
-    // v是否有值,不为undefined、null、''
     function hasValue(v) {
+        // v是否有值,不为undefined、null、''
         return typeof v !== 'undefined' && v !== null && v !== '';
     }
     /*
-    * 将节点属性数据(attr数组)转换成简单key-value数据
-    * @param  {Object}ele 元素节点数据Element实例
-    * @return {Object}
-    * */
+     * 将节点属性数据(attr数组)转换成简单key-value数据
+     * @param  {Object}ele 元素节点数据Element实例
+     * @return {Object}
+     * */
     function getPlainAttrs(ele) {
         const obj = {};
         ele.attrs.forEach(attr => obj[attr.name] = attr.value);
         return obj;
     }
     /*
-    * 从fees数组中获取相关费用
-    * @param  {Array}fees 费用数组
-    *         {String}feeFields 费用字段
-    * @return {Number}
-    * @example getFee(source.fees, 'common.totalFee')
-    * */
+     * 从fees数组中获取相关费用
+     * @param  {Array}fees 费用数组
+     *         {String}feeFields 费用字段
+     * @return {Number}
+     * @example getFee(source.fees, 'common.totalFee')
+     * */
     function getFee(fees, feeFields) {
         if (!Array.isArray(fees)) {
             return 0;
@@ -342,12 +344,12 @@ const XML_EXPORT_BASE = (() => {
         return fee[fields[1]] || 0;
     }
     /*
-    * 根据key获取对应的基本信息、工程特征数据
-    * @param  {Array}data
-    *         {String}key
-    * @return {String}
-    * @example getValueByKey(source.basicInformation, 'projectScale')
-    * */
+     * 根据key获取对应的基本信息、工程特征数据
+     * @param  {Array}data
+     *         {String}key
+     * @return {String}
+     * @example getValueByKey(source.basicInformation, 'projectScale')
+     * */
     function getValueByKey(data, key) {
         for (let d of data) {
             if (d.key === key) {
@@ -424,11 +426,11 @@ const XML_EXPORT_BASE = (() => {
         return rst;
     }
     /*
-    * 检测层数
-    * @param  {Number}maxDepth(最大深度)
-    *         {Object}node(需要检测的清单树节点)
-    * @return {Boolean}
-    * */
+     * 检测层数
+     * @param  {Number}maxDepth(最大深度)
+     *         {Object}node(需要检测的清单树节点)
+     * @return {Boolean}
+     * */
     function validDepth(maxDepth, node) {
         let nodeDepth = node.depth();
         let allNodes = node.getPosterity();
@@ -442,13 +444,13 @@ const XML_EXPORT_BASE = (() => {
         return true;
     }
     /*
-    * 检测唯一性,有些属性在规定的数据范围内唯一
-    * @param  {Object}constraints(约束池)
-    *         {All}data(检测的数据)
-    *         {String}hint(提示已存在的内容)
-    *         {String}subHint(额外提示,有额外提示时,不用data提示)
-    * @return {void}
-    * */
+     * 检测唯一性,有些属性在规定的数据范围内唯一
+     * @param  {Object}constraints(约束池)
+     *         {All}data(检测的数据)
+     *         {String}hint(提示已存在的内容)
+     *         {String}subHint(额外提示,有额外提示时,不用data提示)
+     * @return {void}
+     * */
     function checkUnique(constraints, data, hint, subHint) {
         if (constraints.includes(data)) {
             let failHint = subHint
@@ -491,12 +493,12 @@ const XML_EXPORT_BASE = (() => {
     }
 
     /*
-    * 根据粒度获取项目(不包含详细数据)数据
-    * @param  {Number}granularity 导出粒度
-    *         {Number}tenderID 单位工程ID
-    *         {String}userID 用户ID
-    * @return {Object} 返回的数据结构:{children: [{children: []}]} 最外层为建设项目,中间为单项工程,最底层为单位工程
-    * */
+     * 根据粒度获取项目(不包含详细数据)数据
+     * @param  {Number}granularity 导出粒度
+     *         {Number}tenderID 单位工程ID
+     *         {String}userID 用户ID
+     * @return {Object} 返回的数据结构:{children: [{children: []}]} 最外层为建设项目,中间为单项工程,最底层为单位工程
+     * */
     async function getProjectByGranularity(granularity, tenderID, userID) {
         let projectData = _cache.projectData;
         // 没有数据,需要拉取
@@ -508,11 +510,11 @@ const XML_EXPORT_BASE = (() => {
     }
 
     /*
-    * 通过getData接口获取单位工程详细数据
-    * @param  {Number}tenderID 单位工程ID
-    *         {String}userID 用户ID
-    * @return {Object} 跟projectObj.project的数据结构一致
-    * */
+     * 通过getData接口获取单位工程详细数据
+     * @param  {Number}tenderID 单位工程ID
+     *         {String}userID 用户ID
+     * @return {Object} 跟projectObj.project的数据结构一致
+     * */
     async function getTenderDetail(tenderID, userID) {
         // 获取单位工程详细数据
         let tenderDetail = _cache.tenderDetailMap[tenderID];
@@ -710,15 +712,15 @@ const XML_EXPORT_BASE = (() => {
         return srcEle.children.filter(ele => ele.name === eleName);
     }
     /*
-    * 设置完工程编号后,更新原始数据的工程编号
-    * 更新原始数据前需要将编号里的特殊字符进行转换
-    * @param  {Array}exportData 提取出来的需要导出的数据
-    *         {Array}codes 工程编号表中填写的工程编号
-    *         {String}EngineeringName 单项工程元素的名称
-    *         {String}tenderName 单位工程元素的名称
-    *         {String}codeName 编号属性的名称
-    * @return {void}
-    * */
+     * 设置完工程编号后,更新原始数据的工程编号
+     * 更新原始数据前需要将编号里的特殊字符进行转换
+     * @param  {Array}exportData 提取出来的需要导出的数据
+     *         {Array}codes 工程编号表中填写的工程编号
+     *         {String}EngineeringName 单项工程元素的名称
+     *         {String}tenderName 单位工程元素的名称
+     *         {String}codeName 编号属性的名称
+     * @return {void}
+     * */
     function setupCode(exportData, codes, EngineeringName, tenderName, codeName) {
         // 转换xml实体字符
         let parsedCodes = getParsedData(codes);
@@ -824,15 +826,15 @@ const XML_EXPORT_BASE = (() => {
     }
 
     /*
-    * 根据各自费用定额的文件结构,导出文件
-    * 每个费用定额可能导出的结果文件都不同
-    * 比如广东18需要将一个建设项目文件,多个单位工程文件打包成一个zip文件。重庆18就没这种要求
-    * @param  {Array}codes 工程编号数据
-    *         {Array}extractData 提取的数据
-    *         {Function}setCodeFunc 各自费用定额的导出前重设用户输入的工程编号方法
-    *         {Function}saveAsFunc 各自费用定额的导出方法
-    * @return {void}
-    * */
+     * 根据各自费用定额的文件结构,导出文件
+     * 每个费用定额可能导出的结果文件都不同
+     * 比如广东18需要将一个建设项目文件,多个单位工程文件打包成一个zip文件。重庆18就没这种要求
+     * @param  {Array}codes 工程编号数据
+     *         {Array}extractData 提取的数据
+     *         {Function}setCodeFunc 各自费用定额的导出前重设用户输入的工程编号方法
+     *         {Function}saveAsFunc 各自费用定额的导出方法
+     * @return {void}
+     * */
     async function exportFile(codes, extractData, setCodeFunc, saveAsFunc) {
         // 编号重置后将会被导出,需要将编号进行xml字符实体转换
         codes = getParsedData(codes);
@@ -855,7 +857,7 @@ const XML_EXPORT_BASE = (() => {
         // 导出
         await saveAsFunc(fileData);
     }
-    
+
     return {
         CONFIG,
         CACHE,

+ 160 - 138
web/building_saas/main/js/models/exportStdInterface_gd18.js

@@ -16,38 +16,49 @@ const XMLStandard = (function () {
     // 数据类别,辨别提取的数据,哪份是以建设项目为根元素,哪份以单位工程为根元素
     const DATA_TYPE = {
         'project': 1,
-        'tender': 2
+        'tender': 2,
     };
     // 建设性质
-    const CONSTRUCTION_TYPE = {
+    const ConstructionType = {
         '新建': '1',
         '扩建': '2',
         '改建': '3',
         '修缮': '4',
         '修复': '5',
         '维护保养': '6',
-        '其他': '9'
+        '其他': '9',
+    };
+    // 造价类型
+    const CostType = {
+        '投资估算': '1',
+        '设计概算': '2',
+        '施工图预算': '3',
+        '招标工程量清单': '4',
+        '招标控制价(最高投标限价)': '5',
+        '投标报价': '6',
+        '签约合同价': '7',
+        '竣工结算价': '8',
     };
     // 文件类型
-    const FILE_KIND = {
+    const FileKind = {
         '1': '6', // 投标
         '2': '4', // 招标
-        '3': '5' // 控制价
+        '3': '5', // 控制价
     };
     // 计价模式
-    const VALUATION_MODEL = {
+    const ValuationModel = {
         'bill': '1', // 清单计价
         'ration': '2', // 定额计价
     };
     // 地区类别
-    const AREA_KIND = {
+    const AreaKind = {
         '一类地区': '1',
         '二类地区': '2',
         '三类地区': '3',
-        '四类地区': '4'
+        '四类地区': '4',
     };
     // 专业类别
-    const SPECIALTY = {
+    const Specialty = {
         '房屋建筑与装饰': '1',
         '仿古建筑': '2',
         '安装': '3',
@@ -74,19 +85,19 @@ const XMLStandard = (function () {
         '其他': '99',
     };
     // 章节类型
-    const CHAPTER_KIND = {
+    const ChapterKind = {
         '章': '1',
         '节': '2',
         '细目': '3',
-        '其他': '4'
+        '其他': '4',
     };
     // 汇总类型
-    const SUMMARY_KIND = {
+    const SummrayKind = {
         'summary': '1',
-        'notSummary': '2'
+        'notSummary': '2',
     };
     // 清单子目类型
-    const BILL_KIND = {
+    const BillsKind = {
         '分部分项': '1',
         '模板及支架': '2',
         '脚手架': '3',
@@ -95,7 +106,7 @@ const XMLStandard = (function () {
         '其他': '6',
     };
     // 取费类型
-    const CALC_KIND = {
+    const CalcKind = {
         '地上工程': '1',
         '地下明(盖)挖工程': '2',
         '地下暗挖工程': '3',
@@ -109,37 +120,43 @@ const XMLStandard = (function () {
         '其他': '15',
     };
     // 费用归属
-    const COST_KIND = {
+    const CostKind = {
         '建筑工程费': '1',
         '安装工程费': '2',
-        '其他': '3'
+        '其他': '3',
     };
     // 计算方式
-    const CALC_TYPE = {
+    const CalcType = {
         'quantityMulTotalUnitPrice': '1', // 工程量×综合单价
         'formulaAddFeeRate': '2', // 计算基数×费率
-        'quantityMulUnitPrice': '3' // 工程量×单价
+        'quantityMulUnitPrice': '3', // 工程量×单价
     };
     // 降效类型 建筑装饰工程定额超高降效费用计算标记
-    const EFFICIENCY_KIND = {
+    const EfficiencyKind = {
         'baseReduction': '1', // 降效基础定额
         'notReduction': '2', // 非降效定额
         'marketPriceReduction': '3', // 按市场价计算降效费用定额
-        'rationPriceReduction': '4' // 按定额价计算降效费用定额
+        'rationPriceReduction': '4', // 按定额价计算降效费用定额
     };
     // 子目增加费类型 安装工程定额子目增加费的费用计算标记
-    const INC_FEE_KIND = {
+    const IncFeeKind = {
         'subBase': '1', // 子目增加费基础定额
         'notSubBase': '2', // 非子目增加费定额
-        'subCalc': '3' // 子目增加费用计算定额
+        'subCalc': '3', // 子目增加费用计算定额
+    };
+    // 工料机类型
+    const GLJKind = {
     };
+    // 供料方式
+    const Provider ={
+    }
 
     // 通用设置和工具
-    let _base = XML_EXPORT_BASE,
-        _config = _base.CONFIG,
-        _type = _config.TYPE,
-        _util = _base.UTIL,
-        _cache = _base.CACHE;
+    const _base = XML_EXPORT_BASE;
+    const _config = _base.CONFIG;
+    const _type = _config.TYPE;
+    const _util = _base.UTIL;
+    const _cache = _base.CACHE;
 
     /*
      * 提取数据入口
@@ -149,31 +166,36 @@ const XMLStandard = (function () {
      * @return {Array} [{data: Object, exportKind: Number, fileName: String}]
      * */
     async function entry(userID, exportKind, projectData) {
+        // name为导出的xml节点属性名称 dName为可能需要显示给用户属性时使用的名称,比如自检提示的时候用,这里只有required为true时需要设置dName
         // 建设项目
         function ConstructionProject(src) {
-            let attrs = [
+            const attrs = [
                 // 项目编号
-                {name: 'Number', value: '', required: true},
+                {name: 'Number', dName: '项目编号', value: '', required: true},
                 // 项目名称
-                {name: 'Name', value: src.name, required: true},
+                {name: 'Name', dName: '项目名称', value: src.name, required: true},
                 // 工程类别
-                {name: 'ProjectCategory', value: _util.getValueByKey(src.basicInformation, 'projectCategory'), required: true},
-                // 工程类型 todo add key
-                {name: 'ProjectType', value: _util.getValueByKey(src.basicInformation, 'projectType'), required: true},
-                // 建设性质 todo add key
-                {name: 'ConstructionType', value: _util.getValueByKey(src.basicInformation, 'constructionType'), type: _type.INT},
-                // 文件类型
-                {name: 'FileKind', value: src.fileKind, type: _type.INT, required: true},
+                {name: 'ProjectCategory', dName: '工程类别', value: _util.getValueByKey(src.basicInformation, 'projectCategory'), required: true},
+                // 工程类型
+                {name: 'ProjectType', dName: '工程类型', value: src.projectType, required: true},
+                // 建设性质
+                {name: 'ConstructionType', value: ConstructionType[_util.getValueByKey(src.basicInformation, 'projectCategory')], type: _type.INT,
+                 enumeration: Object.keys(ConstructionType)
+                },
+                // 文件类型 todo
+                {name: 'FileKind', dName: '造价类型', value: CostType[_util.getValueByKey(src.basicInformation, 'costType')], type: _type.INT, required: true,
+                 enumeration: Object.keys(CostType)},
                 // 计价模式
-                {name: 'ValuationModel', value: VALUATION_MODEL.bill, type: _type.INT, required: true},
+                {name: 'ValuationModel', dName: '计价模式', value: ValuationModel.bill, type: _type.INT, required: true},
                 // 计税模式
-                {name: 'TaxModel', value: src.taxType, type: _type.INT, required: true},
-                // 地区类别 todo add key
-                {name: 'AreaKind', value: AREA_KIND[_util.getValueByKey(src.basicInformation, 'areaKind')], type: _type.INT, required: true},
+                {name: 'TaxModel', dName: '计税模式', value: src.taxType, type: _type.INT, required: true},
+                // 地区类别 todo
+                {name: 'AreaKind', dName: '地区类别', value: AreaKind[_util.getValueByKey(src.basicInformation, 'areaKind')], type: _type.INT, required: true,
+                 enumeration: Object.keys(AreaKind)},
                 // 工程地点
-                {name: 'ProjectSite', value: _util.getValueByKey(src.basicInformation, 'projAddress')},
+                {name: 'ProjectSite', value: _util.getValueByKey(src.basicInformation, '"projLocation"')},
                 // 建设单位
-                {name: 'BuildUnit', value: _util.getValueByKey(src.basicInformation, 'constructionUnit'), required: true},
+                {name: 'BuildUnit', dName: '建设单位', value: _util.getValueByKey(src.basicInformation, 'constructionUnit'), required: true},
                 // 建设单位法定代表人或其授权人 todo
                 {name: 'BulidAuthorizer', value: _util.getValueByKey(src.basicInformation, 'todo')},
                 // 数据交换标准名称
@@ -183,11 +205,11 @@ const XMLStandard = (function () {
                 // 建设(编制)范围 todo
                 {name: 'RangeOfCompilation', value: ''},
                 // 建设规模
-                {name: 'Scale', value: _util.getValueByKey(src.basicInformation, 'projectScale'), type: _type.DECIMAL, required: true},
+                {name: 'Scale', dName: '工程规模', value: _util.getValueByKey(src.basicInformation, 'projectScale'), type: _type.DECIMAL, required: true},
                 // 建设规模单位 todo add key
-                {name: 'Unit', value: _util.getValueByKey(src.basicInformation, 'unit'), required: true},
-                // 技术经济指标(元)
-                {name: 'TechnicalAndEconomicIndex', value: 'todo', type: _type.DECIMAL},
+                {name: 'Unit', dName: '建设规模单位', value: _util.getValueByKey(src.basicInformation, 'unit'), required: true},
+                // 技术经济指标(元) todo
+                {name: 'TechnicalAndEconomicIndex', value: '0', type: _type.DECIMAL},
                 // 总说明
                 {name: 'Explains', value: _util.getValueByKey(src.basicInformation, 'todo')}
             ];
@@ -195,13 +217,13 @@ const XMLStandard = (function () {
         }
         // 系统信息
         function SystemInfo(src) {
-            let attrs = [
+            const attrs = [
                 // 编制软件信息
-                {name: 'ID1', value: src.ID1},
+                {name: 'ID1', value: src.softInfo},
                 // 编制机器硬件信息,不输出
-                {name: 'ID2', value: ''},
+                {name: 'ID2', value: _util.generateHardwareId()},
                 // 文件生成时间
-                {name: 'MakeDate', value: src.generatedTime, type: _type.DATE_TIME, required: true}
+                {name: 'MakeDate', dName: '文件生成时间', value: src.generatedTime, type: _type.DATE_TIME, required: true}
             ];
             _base.Element.call(this, 'SystemInfo', attrs);
         }
@@ -211,7 +233,7 @@ const XMLStandard = (function () {
         }
         // 费用精度
         function Option() {
-            let attrs = [
+            const attrs = [
                 // 工料机消耗量、含量、用量类小数精度
                 {name: 'ResPrecision', value: 4, type: _type.INT, required: true},
                 // 工程量、数量类小数精度
@@ -225,45 +247,45 @@ const XMLStandard = (function () {
         }
         // 估(概、预、结)算信息
         function ProjectInfo(src) {
-            let attrs = [
+            const attrs = [
                 // 设计单位
-                {name: 'Designer', value: _util.getValueByKey(src.basicInformation, 'designer')},
+                {name: 'Designer', value: _util.getValueByKey(src.basicInformation, 'designUnit')},
                 // 承包单位
-                {name: 'Contractor', value: _util.getValueByKey(src.basicInformation, 'Contractor')},
+                {name: 'Contractor', value: _util.getValueByKey(src.basicInformation, 'buildingUnit')},
                 // 编制单位
-                {name: 'CompileCompany', value: _util.getValueByKey(src.basicInformation, 'CompileCompany'), required: true},
-                // 编制单位法定代表人或其授权人
+                {name: 'CompileCompany', dName: '编制单位', value: _util.getValueByKey(src.basicInformation, 'establishUnit') || '无', required: true},
+                // 编制单位法定代表人或其授权人 todo
                 {name: 'Authorizer', value: _util.getValueByKey(src.basicInformation, 'Authorizer')},
                 // 编制人
-                {name: 'Compiler', value: _util.getValueByKey(src.basicInformation, 'Compiler')},
-                // 编制人资格证书编号
+                {name: 'Compiler', value: _util.getValueByKey(src.basicInformation, 'establishUnitAuthor')},
+                // 编制人资格证书编号 todo
                 {name: 'CompilerCertNo', value: _util.getValueByKey(src.basicInformation, 'CompilerCertNo')},
                 // 编制时间
-                {name: 'CompileDate', value: _util.getValueByKey(src.basicInformation, 'CompileDate'), required: true},
+                {name: 'CompileDate', dName: '编制时间', value: _util.getValueByKey(src.basicInformation, 'establishDate'), required: true},
                 // 审核人
-                {name: 'Examiner', value: _util.getValueByKey(src.basicInformation, 'Examiner')},
-                // 审核人资格证书编号
+                {name: 'Examiner', value: _util.getValueByKey(src.basicInformation, 'auditUnitAuditor')},
+                // 审核人资格证书编号 todo
                 {name: 'ExaminerCertNo', value: _util.getValueByKey(src.basicInformation, 'ExaminerCertNo')},
                 // 审核时间
-                {name: 'ExamineDate', value: _util.getValueByKey(src.basicInformation, 'ExamineDate')},
-                // 审定人
+                {name: 'ExamineDate', value: _util.getValueByKey(src.basicInformation, 'auditDate')},
+                // 审定人 todo
                 {name: 'Approver', value: _util.getValueByKey(src.basicInformation, 'Approver')},
-                // 审定人资格证书编号
+                // 审定人资格证书编号 todo
                 {name: 'ApproverCertNo', value: _util.getValueByKey(src.basicInformation, 'ApproverCertNo')},
-                // 审定时间
+                // 审定时间 todo
                 {name: 'ApproverDate', value: _util.getValueByKey(src.basicInformation, 'ApproverDate')},
                 // 工程总价(元)
-                {name: 'Total', value: src.summaryInfo.engineeringCost, type: _type.DECIMAL, required: true}
+                {name: 'Total', dName: '工程总价', value: src.summaryInfo.engineeringCost || '0', type: _type.DECIMAL, required: true}
             ];
             _base.Element.call(this, 'ProjectInfo', attrs);
         }
-        // 招标信息
+        // 招标信息 工程量清单、招标控制价时输出
         function TendereeInfo(info) {
-            let attrs = [
+            const attrs = [
                 // 招标人
-                {name: 'TendereeName', value: _util.getValueByKey(info, 'TendereeName'), required: true},
+                {name: 'TendereeName', dName: '招标人', value: _util.getValueByKey(info, 'bidInviter'), required: true},
                 // 招标单位法定代表人或其授权人
-                {name: 'TenderAuthorizer', value: _util.getValueByKey(info, 'TenderAuthorizer')},
+                {name: 'TenderAuthorizer', value: _util.getValueByKey(info, 'tenderRepresentative')},
                 // 招标单位编制人
                 {name: 'TenderCompiler', value: _util.getValueByKey(info, 'TenderCompiler')},
                 // 招标单位编制人资格证书编号
@@ -283,7 +305,7 @@ const XMLStandard = (function () {
                 // 招标单位审定时间
                 {name: 'TenderApproveDate', value: _util.getValueByKey(info, 'TenderApproveDate')},
                 // 招标代理 不得为空,如无招标代理,则应填写“无”。
-                {name: 'Proxy', value: _util.getValueByKey(info, 'Proxy'), required: true},
+                {name: 'Proxy', dName: '招标代理', value: _util.getValueByKey(info, 'Proxy'), required: true},
                 // 招标代理资质证书编号
                 {name: 'ProxyCertNo', value: _util.getValueByKey(info, 'ProxyCertNo')},
                 // 招标代理法定代表人或其授权人
@@ -307,7 +329,7 @@ const XMLStandard = (function () {
                 // 招标代理审定时间
                 {name: 'ProxyApproveDate', value: _util.getValueByKey(info, 'ProxyApproveDate')},
                 // 造价咨询 、Consultant(造价咨询):不得为空,如无则应填写“无”。
-                {name: 'Consultant', value: _util.getValueByKey(info, 'Consultant'), required: true},
+                {name: 'Consultant', dName: '造价咨询', value: _util.getValueByKey(info, 'Consultant'), required: true},
                 // 造价咨询资质证书编号
                 {name: 'ConsultantCertNo', value: _util.getValueByKey(info, 'ConsultantCertNo')},
                 // 造价咨询法定代表人或其授权人
@@ -331,19 +353,19 @@ const XMLStandard = (function () {
                 // 造价咨询审定时间
                 {name: 'ConsultantApproveDate', value: _util.getValueByKey(info, 'ConsultantApproveDate')},
                 // 招标控制价(元)
-                {name: 'TenderSumLimit', value: 'todo', type: _type.DECIMAL, required: true}
+                {name: 'TenderSumLimit', dName: '招标控制价', value: 'todo', type: _type.DECIMAL, required: true}
             ];
             _base.Element.call(this, 'TendereeInfo', attrs);
         }
         // 投标信息
         function BidderInfo(src) {
-            let attrs = [
+            const attrs = [
                 // 投标人
-                {name: 'BidName', value: _util.getValueByKey(src.basicInformation, 'bidName'), required: true},
+                {name: 'BidName', dName: '投标人', value: _util.getValueByKey(src.basicInformation, 'bidName'), required: true},
                 // 投标单位法定代表人或其授权人
                 {name: 'BidAuthorizer', value: _util.getValueByKey(src.basicInformation, 'bidAuthorizer')},
                 // 投标总价(元)
-                {name: 'BidAuthorizer', value: src.summaryInfo.engineeringCost, type: _type.DECIMAL, required: true},
+                {name: 'BidAuthorizer', dName: '投标总价', value: src.summaryInfo.engineeringCost, type: _type.DECIMAL, required: true},
                 // 投标单位编制人
                 {name: 'BidCompiler', value: _util.getValueByKey(src.basicInformation, 'bidCompiler')},
                 // 投标单位编制人资格证书编号
@@ -367,13 +389,13 @@ const XMLStandard = (function () {
         }
         // 费用汇总
         function SummaryOfCost(summaryInfo) {
-            let attrs = [
+            const attrs = [
                 // 工程造价(元)
                 {name: 'Total', value: summaryInfo.engineeringCost, type: _type.DECIMAL},
                 // 建筑工程费
-                {name: 'Construction', value: 'todo', type: _type.DECIMAL},
+                {name: 'Construction', value: '0', type: _type.DECIMAL},
                 // 安装工程费
-                {name: 'Installation', value: 'todo', type: _type.DECIMAL},
+                {name: 'Installation', value: '0', type: _type.DECIMAL},
                 // 分部分项工程费
                 {name: 'DivisionalAndElementalWorks', value: summaryInfo.subEngineering, type: _type.DECIMAL},
                 // 措施项目费
@@ -419,15 +441,15 @@ const XMLStandard = (function () {
                 // 设备费
                 {name: 'Equipment', value: 'todo', type: _type.DECIMAL},
                 // 其中:引进部分(美元)
-                {name: 'ForeignCurrency', value: 'todo', type: _type.DECIMAL},
+                //{name: 'ForeignCurrency', value: '0', type: _type.DECIMAL},
                 // 折合人民币
-                {name: 'ConvertedIntoRMB', value: 'todo', type: _type.DECIMAL}
+                //{name: 'ConvertedIntoRMB', value: '0', type: _type.DECIMAL}
             ];
             _base.Element.call(this, 'SummaryOfCost', attrs);
         }
         // 建筑安装工程费
         function ProjectInstallationWorkCost(src) {
-            let attrs = [
+            const attrs = [
                 // 工程编号
                 {name: 'Number', value: ''},
                 // 工程名称
@@ -455,7 +477,7 @@ const XMLStandard = (function () {
         }
         // 单项工程
         function SectionWorks(src) {
-            let attrs = [
+            const attrs = [
                 // 工程编号
                 {name: 'Number', value: ''},
                 // 工程名称
@@ -481,7 +503,7 @@ const XMLStandard = (function () {
         }
         // 单位工程
         function UnitWorks(src) {
-            let attrs = [
+            const attrs = [
                 // 工程编号
                 {name: 'Number', value: '', required: true},
                 // 工程名称
@@ -541,7 +563,7 @@ const XMLStandard = (function () {
         }
         // 工程特征信息明细 AttrInfoItem, 补充信息明细AddiInfoItem
         function InfoItem(eleName, src) {
-            let attrs = [
+            const attrs = [
                 {name: 'Name', value: src.name, required: true},
                 {name: 'Value', value: src.value},
                 {name: 'Code', value: src.code},
@@ -555,7 +577,7 @@ const XMLStandard = (function () {
         }
         // 单位工程费用汇总标题
         function UnitWorksSummaryGroup(src) {
-            let attrs = [
+            const attrs = [
                 // 费用编号
                 {name: 'Number', value: src.code},
                 // 费用名称
@@ -583,7 +605,7 @@ const XMLStandard = (function () {
         }
         // 单位工程费汇总明细
         function UnitWorksSummaryItem(src) {
-            let attrs = [
+            const attrs = [
                 {name: 'Number', value: src.code},
                 {name: 'Name', value: src.name, required: true},
                 {name: 'Unit', value: src.unit},
@@ -615,7 +637,7 @@ const XMLStandard = (function () {
         // 合计费用
         function SummaryOfBasicCost() {
             // 省略了一些
-            let attrs = [
+            const attrs = [
                 {name: 'Labor', value: 'todo', type: _type.DECIMAL},
                 {name: 'Material', value: 'todo', type: _type.DECIMAL},
                 {name: 'Material', value: 'todo', type: _type.DECIMAL},
@@ -638,7 +660,7 @@ const XMLStandard = (function () {
         }
         // 分部工程
         function DivisionalWorks(src) {
-            let attrs = [
+            const attrs = [
                 // 编码
                 {name: 'Number', value: src.code},
                 {name: 'Name', value: src.name, required: true},
@@ -656,7 +678,7 @@ const XMLStandard = (function () {
         }
         // 清单项目
         function WorkElement(src) {
-            let attrs = [
+            const attrs = [
                 // 项目编码
                 {name: 'Number', value: src.code, required: true},
                 // 项目名称
@@ -705,7 +727,7 @@ const XMLStandard = (function () {
         }
         // 工程量计算表
         function ExpressElement(src) {
-            let attrs = [
+            const attrs = [
                 // 序号
                 {name: 'OrderNumber', value: 'todo', required: true},
                 // 工程量计算式
@@ -720,16 +742,16 @@ const XMLStandard = (function () {
         }
         // 工序内容
         function WorkContent(src) {
-            let attrs = [
+            const attrs = [
                 {name: 'Name', value: src.name, required: true},
                 {name: 'Total', value: _util.getFee(src.fees, 'common.totalFee'), type: _type.DECIMAL},
-                {name: 'Remark', value: src.remark}
+                {name: 'Remark', value: ''}
             ];
             _base.Element.call(this, 'WorkContent', attrs);
         }
         // 定额子目
         function Norm(src) {
-            let attrs = [
+            const attrs = [
                 // 定额编码
                 {name: 'Number', value: src.code, required: true},
                 {name: 'Name', value: src.name, required: true},
@@ -783,9 +805,9 @@ const XMLStandard = (function () {
         * */
         async function loadData(projectData) {
             // 提取出的数据
-            let extractData = [];
+            const extractData = [];
             // 建设项目部分
-            let constructionProject = await loadProject(projectData);
+            const constructionProject = await loadProject(projectData);
             extractData.push({
                 data: constructionProject,
                 dataType: DATA_TYPE.project,
@@ -794,19 +816,19 @@ const XMLStandard = (function () {
             });
             // 单位工程部分
             // 提取出单位工程级别的项目数据
-            let tenderData = projectData.children.reduce((acc, cur) => {
+            const tenderData = projectData.children.reduce((acc, cur) => {
                 if (Array.isArray(cur.children)) {
                     acc.push(...cur.children)
                 }
                 return acc;
             }, []);
             // 获取单位工程详细结构
-            let isPlainTender = false;
-            let tenders = await loadTender(projectData.summaryInfo, tenderData, isPlainTender);
+            const isPlainTender = false;
+            const tenders = await loadTender(projectData.summaryInfo, tenderData, isPlainTender);
             tenders.forEach(tender => {
                 // 单位工程文件名为@_单位工程编码_单位工程名称.xml
                 // 因为单位工程编码后续用户可能会改变,这里暂时将fileName设置成单位工程名称.xml
-                let name = tender.attrs.find(attr => attr.name === 'Name');
+                const name = tender.attrs.find(attr => attr.name === 'Name');
                 extractData.push({
                     data: tender,
                     dataType: DATA_TYPE.tender,
@@ -823,31 +845,31 @@ const XMLStandard = (function () {
         * @return {Array}
         * */
         async function loadProject(projectData) {
-            let basicInformation = projectData.property.basicInformation,
-                summaryInfo = projectData.summaryInfo[projectData.ID];
+            const basicInformation = projectData.property.basicInformation;
+            const summaryInfo = projectData.summaryInfo[projectData.ID];
             // 建设项目
-            let constructionProject = new ConstructionProject({
+            const constructionProject = new ConstructionProject({
                 name: projectData.data,
                 basicInformation
             });
             // 系统信息
-            let systemInfo = new SystemInfo({
+            const systemInfo = new SystemInfo({
                 // 软件相关信息进行base64编码
                 ID1: Base64.encode(projectData.softInfo),
                 generatedTime: moment(Date.now()).format('YYYY-MM-DDTHH:mm:ss')
             });
             // 工程信息
-            let constructionInfo = loadConstructionInfo(projectData);
+            const constructionInfo = loadConstructionInfo(projectData);
             // 费用汇总
-            let summaryCost = new SummaryOfCost(summaryInfo);
+            const summaryCost = new SummaryOfCost(summaryInfo);
             // 建筑安装工程费
-            let projectInstall = new ProjectInstallationWorkCost({
+            const projectInstall = new ProjectInstallationWorkCost({
                 name: projectData.name,
                 basicInformation,
                 summaryInfo
             });
             // 单项工程
-            let sectionalWorks = await loadEngineering(summaryInfo, projectData.children);
+            const sectionalWorks = await loadEngineering(summaryInfo, projectData.children);
             projectInstall.children.push(...sectionalWorks);
             constructionProject.children.push(
                 systemInfo,
@@ -860,14 +882,14 @@ const XMLStandard = (function () {
 
         // 加载工程信息相关元素
         function loadConstructionInfo(projectData) {
-            let basicInformation = projectData.property.basicInformation,
-                summaryInfo = projectData.summaryInfo[projectData.ID];
+            const basicInformation = projectData.property.basicInformation;
+            const summaryInfo = projectData.summaryInfo[projectData.ID];
             // 工程信息
-            let constructionInfo = new ConstructionInfo();
+            const constructionInfo = new ConstructionInfo();
             // 费用精度
-            let option = new Option();
+            const option = new Option();
             // 估(概、预、结)算信息
-            let projectInfo = new ProjectInfo({
+            const projectInfo = new ProjectInfo({
                 basicInformation: basicInformation,
                 summaryInfo: summaryInfo
             });
@@ -895,18 +917,18 @@ const XMLStandard = (function () {
         * @return {Array}
         * */
         async function loadEngineering(summaryInfoMap, engineeringData) {
-            let isPlainTender = true;
-            let engineeringTasks = engineeringData.map(async engData => {
-                let summaryInfo = summaryInfoMap[engData.ID];
+            const isPlainTender = true;
+            const engineeringTasks = engineeringData.map(async engData => {
+                const summaryInfo = summaryInfoMap[engData.ID];
                 // 单项工程
-                let sectionalWork = new SectionWorks({
+                const sectionalWork = new SectionWorks({
                     name: engData.name,
                     summaryInfo
                 });
                 // 费用汇总
-                let summaryCost = new SummaryOfCost(summaryInfo);
+                const summaryCost = new SummaryOfCost(summaryInfo);
                 // 简单结构的单位工程
-                let tenders = await loadTender(summaryInfoMap, engData.children, isPlainTender);
+                const tenders = await loadTender(summaryInfoMap, engData.children, isPlainTender);
                 sectionalWork.push(summaryCost, ...tenders);
                 return sectionalWork;
             });
@@ -921,10 +943,10 @@ const XMLStandard = (function () {
          * @return {Array}
          * */
         async function loadTender(summaryInfoMap, tenderData, isPlain) {
-            let tenderTasks = tenderData.map(async tData => {
-                let summaryInfo = summaryInfoMap[tData.ID];
+            const tenderTasks = tenderData.map(async tData => {
+                const summaryInfo = summaryInfoMap[tData.ID];
                 // 单位工程
-                let unitWork = new UnitWorks({
+                const unitWork = new UnitWorks({
                     name: tData.name,
                     summaryInfo: summaryInfo
                 });
@@ -934,11 +956,11 @@ const XMLStandard = (function () {
                 }
                 // 不是简单结构,继续添加各种子元素
                 // 工程特征信息
-                let attrInfo = loadAttrInfo(tData.property.projectFeature);
+                const attrInfo = loadAttrInfo(tData.property.projectFeature);
                 // 费用汇总
-                let summaryOfCost = new SummaryOfCost(summaryInfo);
+                const summaryOfCost = new SummaryOfCost(summaryInfo);
                 // 获取单位工程详细数据
-                let tenderDetail = await _util.getTenderDetail(tData.ID, userID);
+                const tenderDetail = await _util.getTenderDetail(tData.ID, userID);
                 // 单位工程费用汇总
             });
             return await Promise.all(tenderTasks);
@@ -947,7 +969,7 @@ const XMLStandard = (function () {
         // 加载工程特征信息
         // attrList 单位工程的工程特征,来自property.projectFeature
         function loadAttrInfo(attrList) {
-            let attrInfo = new Info('AttrInfo');
+            const attrInfo = new Info('AttrInfo');
             attrInfo.children = attrList.map(attr =>
                 new InfoItem('AttrInfoItem', {
                     name: attr.dispName,
@@ -961,13 +983,13 @@ const XMLStandard = (function () {
         // 加载单位工程费用汇总
         // tenderDetail 单位工程详细数据
         function loadUnitWorksSummary(tenderDetail) {
-            let unitWorksSummary = new UnitWorksSummary();
+            const unitWorksSummary = new UnitWorksSummary();
             return unitWorksSummary;
         }
 
         // 加载分部分项工程
         function loadDivisionWorks(tenderDetail) {
-            let division = new DivisionalAndElementalWorks();
+            const division = new DivisionalAndElementalWorks();
         }
     }
 
@@ -983,26 +1005,26 @@ const XMLStandard = (function () {
         // 1.根元素为建设项目:需要修改建设项目下的单位工程元素的编码和文件名
         // 2.根元素为单位工程:需要修改单位工程的编码和文件名
         // 获取建设项目数据下的单位工程元素
-        let ExtractProjects = extractData.filter(data => data.dataType === DATA_TYPE.project);
-        let tenders1 = ExtractProjects.map(extractP => {
+        const ExtractProjects = extractData.filter(data => data.dataType === DATA_TYPE.project);
+        const tenders = ExtractProjects.map(extractP => {
             // 建设项目元素下建筑安装工程费元素
-            let projectInstallations = _util.getElementFromSrc(extractP.data, 'ProjectInstallationWorkCost');
+            const projectInstallations = _util.getElementFromSrc(extractP.data, 'ProjectInstallationWorkCost');
             // 建筑安装工程费元素下单项工程元素
-            let sectionalWorks = projectInstallations.reduce((acc, cur) => {
-                let sections = _util.getElementFromSrc(cur, 'SectionalWorks');
+            const sectionalWorks = projectInstallations.reduce((acc, cur) => {
+                const sections = _util.getElementFromSrc(cur, 'SectionalWorks');
                 acc.push(...sections);
             }, []);
             // 单项工程元素下单位工程元素
             return sectionalWorks.reduce((acc, cur) => {
-                let tenders = _util.getElementFromSrc(cur, 'UnitWorks');
+                const tenders = _util.getElementFromSrc(cur, 'UnitWorks');
                 acc.push(...tenders);
             }, []);
         });
         // 重置编号
         let idx = 0;
-        for (let tender of tenders1) {
+        for (let tender of tenders) {
             let codeItem = tender.attrs.find(attr => attr.name === '编号');
             codeItem.value = codes[idx++];
         }
     }
-})();
+})();

+ 23 - 11
web/building_saas/pm/html/project-management.html

@@ -102,7 +102,15 @@
                         <div class="toolsbar">
                             <legend class="m-0 px-4">项目管理
                                 <div class="tools-btn btn-group align-top px-3 mt-2">
-                                    <a id="startA" href="javascript:void(0);" data-toggle="modal" data-target="#addPoj3" class="btn btn-sm"><i class="fa fa-sticky-note-o"></i> 新建</a>
+                                    <div class="btn btn-sm">
+                                        <a class="dropdown-toggle" id="addMenuBtn" data-toggle="dropdown" href="#" role="button" aria-haspopup="true" aria-expanded="false"><i class="fa fa-plus"></i> 新建</a>
+                                        <div class="dropdown-menu dropright" id="addMenu">
+                                            <a href="javascript:;" id="addFolderSub" class="dropdown-item" data-toggle="modal" data-target="#add-folder-dialog"><i class="fa fa-folder-open-o" aria-hidden="true"></i> 新建文件夹</a>
+                                            <a href="javascript:;" id="addProjectSub" class="dropdown-item" data-toggle="modal" data-target="#add-project-dialog"><i class="fa fa-cubes" aria-hidden="true"></i> 新建建设项目</a>
+                                            <a href="javascript:;" id="addEngSub" class="dropdown-item" data-toggle="modal" data-target="#add-engineering-dialog"><i class="fa fa-cube" aria-hidden="true"></i> 新建单项工程</a>
+                                            <a href="javascript:;" id="addTenderSub" class="dropdown-item" data-toggle="modal" data-target="#add-tender-dialog"><i class="fa fa-sticky-note-o" aria-hidden="true"></i> 新建单位工程</a>
+                                        </div>
+                                    </div>
                                     <a id="upLevel" href="javascript:void(0);" class="btn btn-light btn-sm disabled" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="升级"><i class="fa fa-arrow-left" aria-hidden="true"></i></a>
                                     <a id="downLevel" href="javascript:void(0);" class="btn btn-light btn-sm" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="降级"><i class="fa fa-arrow-right" aria-hidden="true"></i></a>
                                     <a id="upMove" href="javascript:void(0);" class="btn btn-light btn-sm" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="上移"><i class="fa fa-arrow-up" aria-hidden="true"></i></a>
@@ -235,7 +243,7 @@
                     <div class="form-group row">
                         <label for="staticEmail" class="col-auto col-form-label col-form-label-sm">建设项目</label>
                         <div class="col">
-                            <input type="text" class="form-control form-control-sm" placeholder="输入建设项目名称" id="project-name">
+                            <input type="text" class="form-control form-control-sm" placeholder="输入建设项目名称" id="project-name" autofocus="autofocus">
                             <span class="form-text text-danger" id="project-name-info" style="display: none;">已存在 “建筑工程1”</span>
                         </div>
                     </div>
@@ -303,7 +311,7 @@
                 <form>
                     <div class="form-group">
                         <label>单项工程</label>
-                        <input type="text" class="form-control" placeholder="输入单项工程名称" id="engineering-name">
+                        <input type="text" class="form-control" placeholder="输入单项工程名称" id="engineering-name" autofocus="autofocus">
                         <span class="form-text text-danger" id="engineering-name-info" style="display: none;">已存在 “建筑工程1”</span>
                     </div>
                 </form>
@@ -398,7 +406,7 @@
                     <div class="form-group row">
                         <label for="staticEmail" class="col-auto col-form-label col-form-label-sm">单位工程</label>
                         <div class="col">
-                            <input type="text" class="form-control form-control-sm" placeholder="输入单位工程名称" id="tender-name" autocomplete="off">
+                            <input type="text" class="form-control form-control-sm" placeholder="输入单位工程名称" id="tender-name" autocomplete="off" autofocus="autofocus">
                         </div>
                     </div>
                     <span class="form-text text-danger" id="tender-name-info" style="display: none;">已存在 “建筑工程1”</span>
@@ -508,7 +516,7 @@
                 <form>
                     <div class="form-group">
                         <label>文件夹</label>
-                        <input type="text" class="form-control" placeholder="输入文件夹名称" id="folder-name">
+                        <input type="text" class="form-control" placeholder="输入文件夹名称" id="folder-name" autofocus="autofocus">
                         <span class="form-text text-danger" id="folder-name-info" style="display: none;">已存在 “建筑工程1”</span>
                         <span class="form-text text-muted">目前最多支持3层文件夹。</span>
                     </div>
@@ -534,7 +542,7 @@
             <div class="modal-body">
                 <form>
                     <div class="form-group">
-                        <input type="text" class="form-control" placeholder="输入名称" id="rename-name">
+                        <input type="text" class="form-control" placeholder="输入名称" id="rename-name" autofocus="autofocus">
                         <span class="form-text text-danger" id="rename-name-info" style="display: none;">已存在 “建筑工程1”</span>
                     </div>
                 </form>
@@ -715,7 +723,7 @@
                 <!--添加分享-->
                 <div class="form-group">
                     <div class="input-group input-group-sm">
-                        <input id="sharePhone" type="text" class="form-control" placeholder="输入 手机号 添加分享">
+                        <input id="sharePhone" type="text" class="form-control" placeholder="输入 手机号 添加分享" autofocus="autofocus">
                        <!-- <div class="input-group-append">
                             <button class="btn btn-primary" type="button" id="button-addon2">添加分享</button>
                         </div>-->
@@ -746,17 +754,21 @@
         </div>
     </div>
 </div>
-<!--弹出 新用户第一次进度条-->
+<!--进度条-->
 <div class="modal fade" id="progress" data-backdrop="static">
     <div class="modal-dialog" role="document">
         <div class="modal-content">
             <div class="modal-header">
-                <h5 id="progress-title" class="modal-title">欢迎使用纵横建筑计价</h5>
+                <h5 id="progress-title" class="modal-title"></h5>
             </div>
             <div class="modal-body">
-                <h5 id="progress-content" class="my-3">首次加载例题,请稍候……</h5>
+                <h5 id="progress-content" class="my-3"></h5>
                 <div class="progress mb-3">
-                    <div class="progress-bar progress-bar-striped progress-bar-animated" id="progressBar" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 0%"></div>
+                    <!--<div class="progress-bar progress-bar-striped progress-bar-animated" id="progressBar" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">-->
+                    <div class="progress-bar" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">
+                        <div class="progress-move progress-bar-striped"></div>
+                        <div class="progress-cover" id="progressCover"></div>
+                    </div>
                 </div>
             </div>
         </div>

+ 1 - 1
web/building_saas/pm/js/pm_import.js

@@ -415,7 +415,7 @@ const importView = (() => {
             if (tbcObj && tbcObj.checkValid()) {
                 return;
             }
-            let pr = new SCComponent.InitProgressBar($('#progress'), $('#progress-title'), $('#progress-content'), $('#progressBar'));
+            let pr = new SCComponent.InitProgressBar();
             try {
                 //建设项目设置选择的文件类型和选择的计税方法
                 xmlObj.property.fileKind = tbcObj.fileKind;

+ 72 - 26
web/building_saas/pm/js/pm_newMain.js

@@ -21,9 +21,9 @@ let needfulInfoData = null;
 let featureData = null; //工程特征数据
 let projectType = {
     folder: 'Folder',
-    tender: 'Tender',
     project: 'Project',
-    engineering: 'Engineering'
+    engineering: 'Engineering',
+    tender: 'Tender'
 };
 let fileType = {
     unitPriceFile: 'UnitPriceFile',
@@ -221,52 +221,76 @@ const projTreeObj = {
         }
         return newWorkBook;
     },
+    // 获取新建按钮是否是disabled
+    getAddBtnDisabled: function (action) {
+        switch (action) {
+            case projectType.folder:
+            case projectType.project:
+            case projectType.tender:
+                return false;
+            case projectType.engineering:
+                const node = projTreeObj.tree.selected;
+                return !node || node.data.projType === projectType.folder;
+            default:
+                return true;
+        }
+    },
+    refreshAddMenuItems: function () {
+        const items = [{
+            el: $('#addFolderSub'),
+            action: projectType.folder,
+        }, {
+            el: $('#addProjectSub'),
+            action: projectType.project,
+        }, {
+            el: $('#addEngSub'),
+            action: projectType.engineering,
+        }, {
+            el: $('#addTenderSub'),
+            action: projectType.tender,
+        }];
+        items.forEach(item => {
+            const disabled = this.getAddBtnDisabled(item.action);
+            if (disabled) {
+                item.el.addClass('disabled');
+            } else {
+                item.el.removeClass('disabled');
+            }
+        });
+    },
     //右键菜单项
     contextMenuItems: {
         addFolder: {
             name: "新建文件夹",
             icon: 'fa-folder-o',
             disabled: function () {
-                return false;
+                return projTreeObj.getAddBtnDisabled(projectType.folder);
             },
             callback: function (key, opt) {
                 $("#add-folder-dialog").modal("show");
-                setTimeout(function () {
-                    $('#folder-name')[0].focus();
-                }, 300);
             }
         },
         addProject: {
             name: "新建建设项目",
             icon: 'fa-cubes',
             disabled: function () {
-                return false;
+                return projTreeObj.getAddBtnDisabled(projectType.project);
             },
             callback: function (key, opt) {
                 $('#add-project-dialog').modal('show');
-                setTimeout(function () {
-                    $('#project-name')[0].focus();
-                }, 300);
             },
         },
         addEngineering: {
             name: "新建单项工程",
             icon: 'fa-cube',
             disabled: function () {
-                let node = projTreeObj.tree.selected;
-                if(node && node.data.projType !== projectType.folder){
-                    return false;
-                }
-                return true;
+                return projTreeObj.getAddBtnDisabled(projectType.engineering);
             },
             callback: function (key, opt) {
                 let selectedItem = projTreeObj.tree.selected;
                 try {
                     if(selectedItem !== null  && selectedItem.data.projType !== projectType.folder){
                         $("#add-engineering-dialog").modal("show");
-                        setTimeout(function () {
-                            $('#engineering-name')[0].focus();
-                        }, 300);
                     }
 
                 } catch (error) {
@@ -278,7 +302,7 @@ const projTreeObj = {
             name: "新建单位工程",
             icon: 'fa-sticky-note-o',
             disabled: function () {
-                return false;
+                return projTreeObj.getAddBtnDisabled(projectType.tender);
             },
             callback: function (key, opt) {
                 //弹出新建单位工程之前,判断当前使用版本,且当前使用单位工程数是否已到最大值
@@ -296,7 +320,7 @@ const projTreeObj = {
                 $('#rename-name').val(projTreeObj.tree.selected.data.name ? projTreeObj.tree.selected.data.name : '');
                 $('#rename-dialog').modal('show');
                 setTimeout(function () {
-                    $('#rename-name')[0].focus();
+                    $('#rename-name').focus();
                 }, 300);
             }
         },
@@ -1702,10 +1726,20 @@ $(document).ready(function() {
 
     //绑定新建建设项目、新建单项工程、新建文件夹、重命名Enter键事件
     bindInputs($('#project-name'), $('#engineering-name'), $('#folder-name'), $('#rename-name'));
-
+    //绑定新建窗口显示事件
+    bindModalShown($('#add-folder-dialog'), $('#add-project-dialog'), $('#add-engineering-dialog'), $('#add-tender-dialog'));
     //绑定新建建设项目、新建单项工程、新建文件夹、重命名弹窗隐藏事件
     bindModalsHidden($('#add-project-dialog'), $('#add-engineering-dialog'), $('#add-folder-dialog'), $('#rename-dialog'));
 
+    // 新建子菜单有效性刷新
+    $('#addMenuBtn').click(function () {
+        const menuDisplay = !$('#addMenu').is(':visible');
+        if (!menuDisplay) {
+            return;
+        }
+        projTreeObj.refreshAddMenuItems();
+    });
+
     // 新增建设项目操作
     $('#addProjOk').click(function () {
         if (!validRequiredData($('#projInfoStep'))) {
@@ -1796,6 +1830,21 @@ $(document).ready(function() {
         }
     });
 
+    function bindModalShown(folderDialog, projDialog, engDialog, tenderDialog) {
+        folderDialog.on('shown.bs.modal', function () {
+            $('#folder-name').focus();
+        });
+        projDialog.on('shown.bs.modal', function () {
+            $('#project-name').focus();
+        });
+        engDialog.on('shown.bs.modal', function () {
+            $('#engineering-name').focus();
+        });
+        tenderDialog.on('shown.bs.modal', function () {
+            $('#tender-name').focus();
+        });
+    }
+
     //新建建设项目、新建单项工程、新建文件夹、重命名弹窗隐藏事件
     function bindModalsHidden(projDialog, engDialog, folderDialog, renameDialog){
         projDialog.on('hidden.bs.modal', function () {
@@ -2049,9 +2098,6 @@ $(document).ready(function() {
         if (!$('#firstStep').is(':visible')) {
             showFirstStep();
         }
-        setTimeout(function () {
-            $('#tender-name')[0].focus();
-        }, 300);
     });
     //新建单位工程-文件类型选择变换(影响到基本信息数据)
     $('input[name="fileKind-tender"]').click(function () {
@@ -2796,8 +2842,8 @@ function init(refresh = false) {
     //init spread and pmTree
     if(refresh == false) socketObject.connect('pm');//socket 连接;
     if (isFirst) {
-        let pr = new SCComponent.InitProgressBar($('#progress'), $('#progress-title'), $('#progress-content'), $('#progressBar'));
-        pr.start('欢迎使用纵横建筑计价', '首次加载例题,请稍候……');
+        let pr = new SCComponent.InitProgressBar();
+        pr.start('欢迎使用大司空计价', '首次加载例题,请稍候……');
         CommonAjax.post('/pm/api/prepareInitialData', {user_id: userID}, function () {
             initProjects(function () {
                 pr.end();

+ 23 - 5
web/building_saas/pm/js/pm_tree.js

@@ -4,11 +4,10 @@
 const pmTree = {
     createNew: function (setting, arrData) {
         function sortTreeItems(tree) {
-            var addItems = function (items) {
-                var i;
-                for (i = 0; i < items.length; i++) {
-                    tree.items.push(items[i]);
-                    addItems(items[i].children);
+            const addItems = function (items) {
+                for (const item of items) {
+                    tree.items.push(item);
+                    addItems(item.children);
                 }
             };
             tree.items.splice(0, tree.items.length);
@@ -251,9 +250,14 @@ const pmTree = {
                     node.parent.children.splice(iIndex, 1);
                 }
                 sortTreeItems(this);
+                this.sourceData = this.items.reduce((acc, cur) => {
+                    acc.push(cur.data);
+                    return acc;
+                }, []);
             };
 
             Tree.prototype.loadData = function (arrData) {
+                this.sourceData = arrData;
                 let i, that = this;
                 let nodesIndex = {};
                 function getPreNode(id){
@@ -319,6 +323,7 @@ const pmTree = {
             };
 
             Tree.prototype.addNodeData = function (data, parent, nextSibling, nodeState = null) {
+                this.sourceData.push(data);
                 var node = null;
                 var pNode = parent ? parent : this._root;
                 if (!nextSibling || (nextSibling.parent === pNode && pNode.childIndex(nextSibling) > -1)) {
@@ -380,6 +385,15 @@ const pmTree = {
             };
 
             Tree.prototype.check = function (_root) {
+                if (this.sourceData.length !== this.items.length) {
+                    const data = this.sourceData.filter(item => {
+                        const findData = this.items.find(node => node.data.ID === item.ID);
+                        return !findData;
+                    });
+                    console.log('丢失数据:');
+                    console.log(data);
+                    return false;
+                }
                 return isValid(_root.children);
                 function isValid(nodes) {
                     for (let node of nodes) {
@@ -408,6 +422,10 @@ const pmTree = {
                             console.log(`${node.serialNo() + 1}:${node.data.name} node索引大于next索引`);
                             return false;
                         }
+                        if (node.nextSibling && node.parent !== node.nextSibling.parent) {
+                            console.log(`${node.serialNo() + 1}:${node.data.name} 与兄弟节点 ${node.nextSibling.serialNo() + 1}:${node.nextSibling.data.name} 父节点不同`);
+                            return false;
+                        }
                         if (node.children.length) {
                             let v = isValid(node.children);
                             if (!v) {