Bladeren bron

标段列表,显示期数

MaiXinRong 6 jaren geleden
bovenliggende
commit
0484b55b50

+ 1 - 1
app/const/tender.js

@@ -25,7 +25,7 @@ const type = {
 
 const infoTableCol = [
     { title: '名称', field: 'name', folderCell: true, },
-    { title: '计量期数', field: '', },
+    { title: '计量期数', field: 'stageCount', },
     { title: '审批状态', field: '', },
     { title: '0号台帐合同', field: '', },
     { title: '本期完成', field: '', },

+ 5 - 1
app/controller/tender_controller.js

@@ -34,6 +34,10 @@ module.exports = app => {
         async _list(view, setting, modal = '') {
             try {
                 const tenderList = await this.ctx.service.tender.getList();
+                for (const t of tenderList) {
+                    t.lastStage = await this.ctx.service.stage.getLastestStage(t.id, true);
+                    t.completeStage = await this.ctx.service.stage.getLastestCompleteStage(t.id);
+                }
                 const categoryData = await this.ctx.service.category.getAllCategory(this.ctx.session.sessionProject.id);
                 const renderData = {
                     tenderList,
@@ -71,7 +75,7 @@ module.exports = app => {
          * @return {Promise<void>}
          */
         async listProgress(ctx) {
-            this.jsFiles = this.app.jsFiles.tender.list;
+            this.jsFiles = this.app.jsFiles.tender.progress;
             await this._list('tender/progress.ejs', tenderConst.progressTableCol, 'tender/modal.ejs');
         }
 

+ 47 - 18
app/public/js/tender_list.js

@@ -234,25 +234,54 @@ function initTenderTree () {
 function recursiveGetTenderNodeHtml (node, arr) {
     const html = [];
     html.push('<tr>');
-    for (const c of TenderTableCol) {
-        if (c.folderCell) {
-            html.push('<td class="in-' + node.level + '">');
-            if (node.cid) {
-                html.push('<i class="fa fa-folder-o"></i> ', node[c.field]);
-            } else {
-                html.push('<span class="text-muted mr-2">');
-                html.push(arr.indexOf(node) === arr.length - 1 ? '└' : '├');
-                html.push('</span>');
-                //html.push('<a href="/tender/' + node.id + '">', node[c.field], '</a>');
-                html.push('<a href="javascript: void(0)" id="' + node.id + '">', node[c.field], '</a>');
-            }
-            html.push('</td>');
-        } else {
-            html.push('<td>');
-            html.push(node[c.field] ? node[c.field] : '');
-            html.push('</td>');
-        }
+    // 名称
+    html.push('<td class="in-' + node.level + '">');
+    if (node.cid) {
+        html.push('<i class="fa fa-folder-o"></i> ', node.name);
+    } else {
+        html.push('<span class="text-muted mr-2">');
+        html.push(arr.indexOf(node) === arr.length - 1 ? '└' : '├');
+        html.push('</span>');
+        //html.push('<a href="/tender/' + node.id + '">', node[c.field], '</a>');
+        html.push('<a href="javascript: void(0)" id="' + node.id + '">', node.name, '</a>');
     }
+    html.push('</td>');
+    // 计量期数
+    html.push('<td>');
+    html.push(node.lastStage ? '第' + node.lastStage.order + '期' : '');
+    html.push('</td>');
+    // 审批状态
+    html.push('<td>');
+    html.push(node.lastStage ? auditConst.statusString[node.lastStage.status] : '');
+    html.push('</td>');
+    // 0号台账合同
+    html.push('<td>');
+    //html.push(node[c.field] ? node[c.field] : '');
+    html.push('</td>');
+    // 本期完成
+    html.push('<td>');
+    //html.push(node[c.field] ? node[c.field] : '');
+    html.push('</td>');
+    // 截止本期合同
+    html.push('<td>');
+    //html.push(node[c.field] ? node[c.field] : '');
+    html.push('</td>');
+    // 截止本期变更
+    html.push('<td>');
+    //html.push(node[c.field] ? node[c.field] : '');
+    html.push('</td>');
+    // 截止本期完成
+    html.push('<td>');
+    //html.push(node[c.field] ? node[c.field] : '');
+    html.push('</td>');
+    // 截止上期完成
+    html.push('<td>');
+    //html.push(node[c.field] ? node[c.field] : '');
+    html.push('</td>');
+    // 本期应付
+    html.push('<td>');
+    //html.push(node[c.field] ? node[c.field] : '');
+    html.push('</td>');
     html.push('</tr>');
     if (node.children) {
         for (const c of node.children) {

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

@@ -248,7 +248,7 @@ function recursiveGetTenderNodeHtml (node, arr) {
     // 完成期数
     html.push('<td>');
     if (!node.cid) {
-        html.push(node.completeStage && node.completeStage > 0 ? '第' + node.completeStage + '期' : '第0期');
+        html.push(node.lastStage ? '第' + node.lastStage.order + '期' : '第0期');
     }
     html.push('</td>');
     // 管理

+ 370 - 0
app/public/js/tender_list_progress.js

@@ -0,0 +1,370 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Mai
+ * @date 2018/10/11
+ * @version
+ */
+const EmptyTenderHtml = [
+    '<div class="jumbotron">',
+    '<h3 class="display-6">还没有标段数据</h3>',
+    '</div>'
+];
+// levelTree - setting
+const levelTreeSetting = {
+    view: {
+        selectedMulti: false
+    },
+    data: {
+        simpleData: {
+            idKey: 'lid',
+            pIdKey: 'lpId',
+            rootPId: 0,
+            enable: true,
+        }
+    },
+    edit: {
+        enable: true,
+        showRemoveBtn: false,
+        showRenameBtn: false,
+        drag: {
+            autoExpandTrigger: true,
+            isCopy: false,
+            isMove:  true,
+            prev: false,
+            next: false,
+            inner: true,
+        }
+    },
+    callback: {
+        beforeDrop: beforeDropNode,
+        onDrop: onDropNode,
+    }
+};
+const levelNodes =[];
+const tenderTree = [];
+function createTree() {
+    const zTree = $.fn.zTree.getZTreeObj('treeLevel');
+    if (zTree) {
+        zTree.destroy();
+    }
+    $.fn.zTree.init($("#treeLevel"), levelTreeSetting, levelNodes);
+}
+function beforeDropNode(treeId, treeNodes, targetNode, moveType, isCopy) {
+    if (targetNode.lid !== 1) {
+        const parent = targetNode.getParentNode();
+        if (parent && parent.lid === 1) {
+            return false;
+        }
+    }
+}
+function onDropNode(event, treeId, treeNodes, targetNode, moveType) {
+    const zTree = $.fn.zTree.getZTreeObj(treeId);
+    function resetFixNode(id) {
+        const node = zTree.getNodeByParam('lid', id);
+        node.isParent = true;
+        zTree.updateNode(node, false);
+        zTree.expandNode(node, true);
+    }
+    function moveChildren(children, node) {
+        if (!children || children.length === 0) { return }
+        for (const c of children) {
+            moveChildren(c.children, node);
+            zTree.moveNode(node, c, 'inner');
+        }
+    }
+    resetFixNode(1);
+    resetFixNode(2);
+    if (targetNode.lid === 1 && treeNodes[0].children && treeNodes[0].children.length !== 0) {
+        moveChildren(treeNodes[0].children, zTree.getNodeByParam('lid', 1));
+    } else if (targetNode.lid !== 1) {
+        if (targetNode.children.length >= 2) {
+            for (const c of targetNode.children) {
+                if (c.lid !== treeNodes[0].lid) {
+                    zTree.moveNode(treeNodes[0], c, 'inner');
+                }
+            }
+        }
+    }
+}
+// 查询方法
+function findNode (key, value, arr) {
+    for (const a of arr) {
+        if (a[key] && a[key] === value) {
+            return a;
+        }
+    }
+}
+function getPId(level) {
+    if (level !== 1) {
+        const p = findNode('level', level - 1, levelNodes);
+        if (p) {
+            return p.lid
+        } else {
+            return 1;
+        }
+    } else {
+        return 2;
+    }
+}
+// 分类数据排序
+function sortCategory() {
+    category.sort(function (a, b) {
+        return a.level ? (b.level ? a.level - b.level : -1) : a.id - b.id;
+    });
+}
+// 初始化分类树结构数据
+function initCategoryLevelNode() {
+    levelNodes.splice(0, levelNodes.length);
+    levelNodes.push(
+        { lid:1, lpId:0, name:"可用类别", open:true, isParent: true},
+        { lid:2, lpId:0, name:"已用类别", open:true, isParent: true}
+    );
+    for (const c of category) {
+        const cate = JSON.parse(JSON.stringify(c));
+        cate.lid = levelNodes.length + 1;
+        cate.open = true;
+        if (!cate.level) {
+            cate.lpId = 1;
+            levelNodes.push(cate);
+        } else {
+            cate.lpId = getPId(cate.level);
+            levelNodes.push(cate);
+        }
+    }
+}
+// 新建标段 -- 分类属性选择
+function getCategoryHtml() {
+    function getSelectCategoryHtml (cate) {
+        const html = [];
+        html.push('<div class="form-group" cate-id="' + cate.id + '">');
+        html.push('<lable>', cate.name, '</lable>');
+        html.push('<select class="form-control">');
+        for (const v of cate.value) {
+            html.push('<option value="' + v.id + '">', v.value, '</option>');
+        }
+        html.push('</select>');
+        html.push('</div>');
+        return html.join('');
+    }
+    function getRadioCategoryHtml (cate) {
+        const html = [];
+        html.push('<div class="form-group" cate-id="' + cate.id + '">');
+        html.push('<lable>', cate.name, '</lable>');
+        html.push('<div>');
+        for (const iV in cate.value) {
+            const v = cate.value[iV];
+            html.push('<div class="form-check-inline">');
+            html.push('<input class="form-check-input" type="radio"', 'name="' + cate.name + '" ', 'value="' , v.id, (iV == 0 ? '" checked="' : ''),  '">');
+            html.push('<label class="form-check-label">', v.value, '</label>');
+            html.push('</div>');
+        }
+        html.push('</div>');
+        html.push('</div>');
+        return html.join('');
+    }
+    const html = [];
+    for (const c of category) {
+        if (c.type === categoryType.key.dropDown) {
+            html.push(getSelectCategoryHtml(c));
+        } else if (c.type === categoryType.key.radio) {
+            html.push(getRadioCategoryHtml(c));
+        }
+    }
+    return html.join('');
+}
+// 初始化TenderTree数据
+function initTenderTree () {
+    const levelCategory = category.filter(function (c) {
+        return c.level && c.level > 0;
+    });
+    function findCategoryNode(cid, value, array) {
+        for (const a of array) {
+            if (a.cid === cid && a.vid === value) {
+                return a;
+            }
+        }
+    }
+    function getCategoryNode(category, value, parent) {
+        const array = parent ?  parent.children : tenderTree;
+        let cate = findCategoryNode(category.id, value, array);
+        if (!cate) {
+            const cateValue = findNode('id', value, category.value);
+            cate = {
+                cid: category.id,
+                vid: value,
+                name: cateValue.value,
+                children: [],
+                level: category.level,
+            };
+            array.push(cate);
+        }
+        return cate;
+    }
+    function loadTenderCategory (tender) {
+        let tenderCategory = null;
+        for (const lc of levelCategory) {
+            const tenderCate = findNode('cid', lc.id, tender.category);
+            if (tenderCate) {
+                tenderCategory = getCategoryNode(lc, tenderCate.value, tenderCategory);
+            } else {
+                return tenderCategory;
+            }
+        }
+        return tenderCategory;
+    }
+    tenderTree.splice(0, tenderTree.length);
+    for (const t of tenders) {
+        t.valid = true;
+        if (t.category && levelCategory.length > 0) {
+            const parent = loadTenderCategory(t);
+            if (parent) {
+                t.level = parent.level + 1;
+                parent.children.push(t);
+            } else {
+                tenderTree.push(t);
+            }
+        } else {
+            tenderTree.push(t);
+        }
+    }
+}
+function recursiveGetTenderNodeHtml (node, arr) {
+    const html = [];
+    html.push('<tr>');
+    // 名称
+    html.push('<td class="in-' + node.level + '">');
+    if (node.cid) {
+        html.push('<i class="fa fa-folder-o"></i> ', node.name);
+    } else {
+        html.push('<span class="text-muted mr-2">');
+        html.push(arr.indexOf(node) === arr.length - 1 ? '└' : '├');
+        html.push('</span>');
+        //html.push('<a href="/tender/' + node.id + '">', node[c.field], '</a>');
+        html.push('<a href="javascript: void(0)" id="' + node.id + '">', node.name, '</a>');
+    }
+    html.push('</td>');
+    // 完成期数
+    html.push('<td>');
+    html.push(node.completeStage ? '第' + node.completeStage.order + '期' : '');
+    html.push('</td>');
+    // 累计合同计量
+    html.push('<td>');
+    //html.push(node[c.field] ? node[c.field] : '');
+    html.push('</td>');
+    // 截止本期累计完成/本期完成/未完成
+    html.push('<td>');
+    //html.push(node[c.field] ? node[c.field] : '');
+    html.push('</td>');
+    html.push('</tr>');
+    if (node.children) {
+        for (const c of node.children) {
+            html.push(recursiveGetTenderNodeHtml(c, node.children));
+        }
+    }
+    return html.join('');
+}
+// 根据TenderTree数据获取Html代码
+function getTenderTreeHtml () {
+    if (tenderTree.length > 0) {
+        const html = [];
+        html.push('<table class="table table-bordered">');
+        html.push('<thead>', '<tr>');
+        for (const c of TenderTableCol) {
+            html.push('<th>', c.title, '</th>');
+        }
+        html.push('</tr>', '</thead>');
+        for (const t of tenderTree) {
+            html.push(recursiveGetTenderNodeHtml(t, tenderTree));
+        }
+        html.push('</table>');
+        return html.join('');
+    } else {
+        return EmptyTenderHtml.join('');
+    }
+}
+function bindTenderUrl() {
+    $('a', '.c-body').bind('click', function () {
+        const tenderId = parseInt($(this).attr('id'));
+        const tender = _.find(tenders, function (t) {
+            return t.id === tenderId;
+        });
+        if (tender.measure_type) {
+            window.location.href = '/tender/' + tenderId;
+        } else {
+            for (const a of $('a', '#jlms')) {
+                a.href = '/tender/' + tenderId + '/type?type=' + $(a).attr('mst');
+            }
+            $('#jlms').modal('show');
+        }
+    });
+}
+
+$(document).ready(() => {
+    sortCategory();
+    // 初始化分类数据
+    initCategoryLevelNode();
+    $('.modal-body', '#add-bd').append(getCategoryHtml());
+    // 初始化标段树结构
+    initTenderTree();
+    $('.c-body').html(getTenderTreeHtml());
+    bindTenderUrl();
+    // 分类
+    $('#cate-set').on('show.bs.modal', function () {
+        createTree();
+    });
+    $('#set-cate-ok').click(function () {
+        const data = [];
+        const zTree = $.fn.zTree.getZTreeObj('treeLevel');
+        for (const c of category) {
+            const node = zTree.getNodeByParam('id', c.id);
+            const parent = node.getParentNode();
+            if (parent.lid === 1) {
+                data.push({id: c.id, level: 0});
+            } else {
+                data.push({id: c.id, level: node.getPath().length - 1});
+            }
+        }
+        postData('/setting/category/level', data, function (rst) {
+            for (const d of data) {
+                const c = findNode('id', d.id, category);
+                c.level = d.level;
+            }
+            sortCategory();
+            initCategoryLevelNode();
+            initTenderTree();
+            $('.c-body').html(getTenderTreeHtml());
+            $('#cate-set').modal('hide');
+        });
+    });
+    // 新增标段
+    $('#add-bd-ok').click(function () {
+        const data = {
+            name: $('[name=name]', '#add-bd').val(),
+            category: [],
+        };
+        if (!data.name || data.name === '') {
+            // TODO 提示用户
+            return;
+        }
+        for (const c of category) {
+            const cate = {cid: c.id};
+            if (c.type === categoryType.key.dropDown) {
+                cate.value = parseInt($('select', '[cate-id=' + c.id + ']').val());
+            } else if (c.type === categoryType.key.radio) {
+                cate.value = parseInt($('input:checked', '[cate-id=' + c.id + ']').val());
+            }
+            data.category.push(cate);
+        }
+        postData('/list/add', data, function (result) {
+            tenders.push(result);
+            initTenderTree();
+            $('.c-body').html(getTenderTreeHtml());
+            bindTenderUrl();
+            $('#add-bd').modal('hide');
+            $('[name=name]', '#add-bd').val('');
+        });
+    });
+});

+ 21 - 3
app/service/stage.js

@@ -24,15 +24,33 @@ module.exports = app => {
             this.tableName = 'stage';
         }
 
-        async getLastestStage(tenderId) {
+        async getLastestStage(tenderId, includeUnCheck = false) {
+            this.initSqlBuilder();
+            this.sqlBuilder.setAndWhere('tid', {
+                value: tenderId,
+                operate: '=',
+            });
+            if (!includeUnCheck) {
+                this.sqlBuilder.setAndWhere('status', {
+                    value: auditConst.status.uncheck,
+                    operate: '!=',
+                });
+            }
+            this.sqlBuilder.orderBy = [['order', 'desc']];
+            const [sql, sqlParam] = this.sqlBuilder.build(this.tableName);
+            const stage = await this.db.queryOne(sql, sqlParam);
+            return stage;
+        }
+
+        async getLastestCompleteStage(tenderId) {
             this.initSqlBuilder();
             this.sqlBuilder.setAndWhere('tid', {
                 value: tenderId,
                 operate: '=',
             });
             this.sqlBuilder.setAndWhere('status', {
-                value: auditConst.status.uncheck,
-                operate: '!=',
+                value: auditConst.status.checked,
+                operate: '=',
             });
             this.sqlBuilder.orderBy = [['order', 'desc']];
             const [sql, sqlParam] = this.sqlBuilder.build(this.tableName);

+ 9 - 8
app/service/tender.js

@@ -79,18 +79,19 @@ module.exports = app => {
             // 获取当前项目信息
             const session = this.ctx.session;
             const sql = 'SELECT t.`id`, t.`project_id`, t.`name`, t.`status`, t.`category`, t.`ledger_times`, t.`ledger_status`, t.`measure_type`, t.`user_id` '+
-                        'FROM ?? As t WHERE t.`project_id` = ? AND (' +
+                        '  FROM ?? As t ' +
+                        '  WHERE t.`project_id` = ? AND (' +
                         // 创建的标段
-                        '  t.`user_id` = ?' +
+                        '    t.`user_id` = ?' +
                         // 参与审批 台账 的标段
-                        '  OR (t.`ledger_status` != ' + auditConst.ledger.status.uncheck + ' AND ' +
-                        '      t.id IN ( SELECT la.`tender_id` FROM ?? As la WHERE la.`audit_id` = ? GROUP BY la.`tender_id`))' +
+                        '    OR (t.`ledger_status` != ' + auditConst.ledger.status.uncheck + ' AND ' +
+                        '        t.id IN ( SELECT la.`tender_id` FROM ?? As la WHERE la.`audit_id` = ? GROUP BY la.`tender_id`))' +
                         // 参与审批 计量期 的标段
-                        '  OR (t.`ledger_status` = ' + auditConst.ledger.status.checked + ' AND ' +
-                        '      t.id IN ( SELECT sa.`tid` FROM ?? As sa WHERE sa.`aid` = ? GROUP BY sa.`tid`))' +
+                        '    OR (t.`ledger_status` = ' + auditConst.ledger.status.checked + ' AND ' +
+                        '        t.id IN ( SELECT sa.`tid` FROM ?? As sa WHERE sa.`aid` = ? GROUP BY sa.`tid`))' +
                         // 参与审批 变更令 的标段
-                        '  OR (t.`ledger_status` = ' + auditConst.ledger.status.checked + ' AND ' +
-                        '      t.id IN ( SELECT ca.`tid` FROM ?? AS ca WHERE ca.`uid` = ? GROUP BY ca.`tid`))' +
+                        '    OR (t.`ledger_status` = ' + auditConst.ledger.status.checked + ' AND ' +
+                        '        t.id IN ( SELECT ca.`tid` FROM ?? AS ca WHERE ca.`uid` = ? GROUP BY ca.`tid`))' +
                         // 未参与,但可见的标段
                         ')';
             const sqlParam = [this.tableName, session.sessionProject.id, session.sessionUser.accountId,

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

@@ -10,4 +10,5 @@
     const categoryType = JSON.parse('<%- JSON.stringify(settingConst.cType) %>');
     const category = JSON.parse('<%- JSON.stringify(categoryData) %>');
     const TenderTableCol = JSON.parse('<%- JSON.stringify(tableColSetting) %>');
+    const auditConst = JSON.parse('<%- JSON.stringify(auditConst.stage) %>');
 </script>

+ 9 - 1
config/web.js

@@ -58,13 +58,21 @@ const JsFiles = {
                 mergeFiles: ["/public/js/tender_list.js"],
                 mergeFile: 'tender_list',
             },
+            progress: {
+                files: [
+                    "/public/js/ztree/jquery.ztree.core.js",
+                    "/public/js/ztree/jquery.ztree.exedit.js",
+                ],
+                mergeFiles: ["/public/js/tender_list_progress.js"],
+                mergeFile: 'tender_list_progress',
+            },
             manage: {
                 files: [
                     "/public/js/ztree/jquery.ztree.core.js",
                     "/public/js/ztree/jquery.ztree.exedit.js",
                 ],
                 mergeFiles: ["/public/js/tender_list_manage.js"],
-                mergeFile: 'tender_list',
+                mergeFile: 'tender_list_manage',
             }
         },
         ledger: {