Przeglądaj źródła

决策大屏部分提交,收方单功能,调差bug修复

laiguoran 3 lat temu
rodzic
commit
04cb49e2a9

+ 1 - 0
app/base/base_controller.js

@@ -35,6 +35,7 @@ class BaseController extends Controller {
                 }
             }
         }
+        menuList.datacollect.display = ctx.session && ctx.session.sessionProject ? ctx.session.sessionProject.showDataCollect : false;
         // 菜单列表
         ctx.menuList = menuList;
         ctx.showProject = false;

+ 71 - 0
app/controller/datacollect_controller.js

@@ -0,0 +1,71 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author EllisRan
+ * @date 2021/9/26
+ * @version
+ */
+
+const tenderConst = require('../const/tender');
+const codeRuleConst = require('../const/code_rule');
+const settingConst = require('../const/setting.js');
+const tenderMenu = require('../../config/menu').tenderMenu;
+const auditConst = require('../const/audit');
+const shenpiConst = require('../const/shenpi');
+const accountGroup = require('../const/account_group').group;
+const accountPermission = require('../const/account_permission');
+const measureType = require('../const/tender').measureType;
+const billsPosConvert = require('../lib/bills_pos_convert');
+const path = require('path');
+const sendToWormhole = require('stream-wormhole');
+const scheduleConst = require('../const/schedule');
+const changeConst = require('../const/change');
+const tenderInfoModel = require('../lib/tender_info');
+
+module.exports = app => {
+    class DatacollectController extends app.BaseController {
+        /**
+         * 构造函数
+         *
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        constructor(ctx) {
+            super(ctx);
+            ctx.showProject = true;
+            ctx.showTitle = true;
+        }
+
+        /**
+         * 期列表(Get)
+         * @param ctx
+         * @return {Promise<void>}
+         */
+        async index(ctx) {
+            try {
+                // 获取标段审批信息
+                const noticeList = await ctx.service.noticePush.getNotice(ctx.session.sessionProject.id);
+                console.log(noticeList);
+                const renderData = {
+                    noticeList,
+                    acLedger: auditConst.ledger,
+                    acStage: auditConst.stage,
+                    acChange: auditConst.flow,
+                    acRevise: auditConst.revise,
+                    acMaterial: auditConst.material,
+                    acAdvance: auditConst.advance,
+                    pushType: auditConst.pushType,
+                    jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.datacollect.index),
+                };
+                await this.layout('datacollect/index.ejs', renderData);
+            } catch (err) {
+                this.log(err);
+                ctx.redirect(this.menu.menu.dashboard.url);
+            }
+        }
+    }
+
+    return DatacollectController;
+};

+ 66 - 0
app/controller/stage_controller.js

@@ -196,6 +196,14 @@ module.exports = app => {
                 }
                 renderData.attData = attData;
                 renderData.coopwd = ((ctx.stage.status === auditConst.status.uncheck || ctx.stage.status === auditConst.status.checkNo) && ctx.session.sessionUser.accountId === ctx.stage.user_id) || ((ctx.stage.status === auditConst.status.checking || ctx.stage.status === auditConst.status.checkNoPre) && ctx.stage.curAuditor && ctx.stage.curAuditor.aid === ctx.session.sessionUser.accountId);
+                // 获取收方单列表
+                const sfData = await ctx.service.stageShoufang.getAllDataByCondition({ where: { sid: ctx.stage.id }, orders: [['id', 'desc']] });
+                for (const sf of sfData) {
+                    sf.filenum = await ctx.service.stageShoufangAtt.count({ sfid: sf.id });
+                }
+                renderData.sfData = sfData;
+                // 收方单附件删除权限
+                renderData.sfAttDelPower = ctx.session.sessionUser.accountId === ctx.stage.user_id || ctx.helper._.find(ctx.stage.auditors2, { aid: ctx.session.sessionUser.accountId }) !== -1;
                 await this.layout('stage/index.ejs', renderData, 'stage/modal.ejs');
             } catch (err) {
                 this.log(err);
@@ -1871,6 +1879,64 @@ module.exports = app => {
                 ctx.body = { err: 1, msg: err.toString(), data: null };
             }
         }
+
+        /**
+         * 收方单操作
+         * @param ctx
+         * @return {Promise<void>}
+         */
+        async saveShoufang(ctx) {
+            try {
+                const data = JSON.parse(ctx.request.body.data);
+                const responseData = { err: 0, msg: '', data: {} };
+                switch (data.type) {
+                    case 'add':
+                        // 判断收方单是否已存在
+                        const sfData = await ctx.service.stageShoufang.getDataByCondition({ sid: ctx.stage.id, lid: data.postData.lid, pid: data.postData.pid });
+                        if (sfData) {
+                            throw '该收方单已存在,不必重复生成';
+                        }
+                        const result = await ctx.service.stageShoufang.add(data.postData);
+                        // responseData.data.cooperationConfirm = await ctx.service.cooperationConfirm.getValidData(ctx.tender.id, ctx.stage.id, ctx.stage.times, ctx.session.sessionUser.accountId);
+                        responseData.data = await ctx.service.stageShoufang.getDataById(result.insertId);
+                        break;
+                    case 'del':
+                        // 判断收方单是否已存在
+                        const sfData2 = await ctx.service.stageShoufang.getDataById(data.postData.sfid);
+                        if (!sfData2) {
+                            throw '该收方单已删除,请刷新';
+                        }
+                        await ctx.service.stageShoufang.del(data.postData.sfid);
+                        // responseData.data.cooperationConfirm = await ctx.service.cooperationConfirm.getValidData(ctx.tender.id, ctx.stage.id, ctx.stage.times, ctx.session.sessionUser.accountId);
+                        break;
+                    default:
+                        throw '参数有误';
+                }
+
+                ctx.body = responseData;
+            } catch (err) {
+                this.log(err);
+                ctx.body = { err: 1, msg: err.toString(), data: null };
+            }
+        }
+
+        /**
+         * 收方单附件
+         * @param ctx
+         * @return {Promise<void>}
+         */
+        async shoufangAtt(ctx) {
+            try {
+                const data = JSON.parse(ctx.request.body.data);
+                const responseData = { err: 0, msg: '', data: {} };
+                const files = await ctx.service.stageShoufangAtt.getListBySfid(data.sfid);
+                responseData.data = files;
+                ctx.body = responseData;
+            } catch (err) {
+                this.log(err);
+                ctx.body = { err: 1, msg: err.toString(), data: null };
+            }
+        }
     }
 
     return StageController;

+ 206 - 0
app/controller/wap_controller.js

@@ -12,6 +12,10 @@ const maintainConst = require('../const/maintain');
 const auditConst = require('../const/audit');
 const changeConst = require('../const/change');
 const advanceConst = require('../const/advance');
+const fs = require('fs');
+const path = require('path');
+const sendToWormhole = require('stream-wormhole');
+const moment = require('moment');
 
 module.exports = app => {
 
@@ -508,6 +512,208 @@ module.exports = app => {
                 ctx.redirect(ctx.request.header.referer);
             }
         }
+
+        /**
+         * 收方单附件上传页
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        async shoufangUpload(ctx) {
+            try {
+                const tid = parseInt(ctx.query.tid) || 0;
+                const order = parseInt(ctx.query.order) || 0;
+                const sfid = parseInt(ctx.query.sfid) || 0;
+                if (!tid || !order || !sfid) {
+                    throw '参数有误';
+                }
+                const tender = await ctx.service.tender.getDataById(tid);
+                if (!tender) {
+                    throw '该标段不存在';
+                }
+                const sfInfo = await ctx.service.stageShoufang.getDataByCondition({ id: sfid, tid, order });
+                if (!sfInfo) {
+                    throw '该收方单不存在';
+                }
+                // 查找对应的台账或计量单元名称
+
+                const ledger = await ctx.service.ledger.getData(tid);
+                const ledgerInfo = ctx.helper._.find(ledger, { id: sfInfo.lid });
+                let name = ledgerInfo.b_code;
+                if (sfInfo.pid) {
+                    const pos = await ctx.service.pos.getPosData({ tid });
+                    const posInfo = ctx.helper._.find(pos, { lid: sfInfo.lid, id: sfInfo.pid });
+                    name += ' / ' + posInfo.name;
+                }
+                const renderData = {
+                    tender,
+                    order,
+                    name,
+                    sfid,
+                    whiteList: this.ctx.app.config.multipart.whitelist,
+                };
+                await ctx.render('wap/shoufangupload.ejs', renderData);
+            } catch (error) {
+                this.log(error);
+                ctx.redirect('/wap/login');
+            }
+        }
+
+        /**
+         * 上传附件
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        async shoufangUpFile(ctx) {
+            const responseData = {
+                err: 0,
+                msg: '',
+                data: [],
+            };
+            let stream;
+            try {
+                const parts = ctx.multipart({ autoFields: true });
+                const files = [];
+                let index = 0;
+                while ((stream = await parts()) !== undefined) {
+                    // 判断是否存在
+                    const sfInfo = await ctx.service.stageShoufang.getDataById(parts.field.sfid);
+                    if (!sfInfo) {
+                        throw '该清单 / 计量单元下不存在收方单';
+                    }
+                    // 判断用户是否选择上传文件
+                    if (!stream.filename) {
+                        throw '请选择上传的文件!';
+                    }
+                    // const dirName = 'app/public/upload/stage/' + moment().format('YYYYMMDD');
+                    // 判断文件夹是否存在,不存在则直接创建文件夹
+                    // if (!fs.existsSync(path.join(this.app.baseDir, dirName))) {
+                    //     await fs.mkdirSync(path.join(this.app.baseDir, dirName));
+                    // }
+                    const fileInfo = path.parse(stream.filename);
+                    const now_time = new Date();
+                    const create_time = Date.parse(now_time) / 1000;
+                    const filepath = `app/public/upload/${sfInfo.tid}/stage/shoufang/fujian_${create_time + index.toString() + fileInfo.ext}`;
+                    await ctx.helper.saveStreamFile(stream, path.resolve(this.app.baseDir, filepath));
+                    // console.log(await fs.existsSync(path.resolve(this.app.baseDir, 'app', filepath)));
+                    // const fileInfo = path.parse(stream.filename);
+                    // const fileName = 'stage' + create_time + '_' + index + fileInfo.ext;
+                    // 保存文件
+                    // await ctx.helper.saveStreamFile(stream, path.resolve(this.app.baseDir, dirName, fileName));
+
+                    if (stream) {
+                        await sendToWormhole(stream);
+                    }
+
+                    // 保存数据到att表
+                    const fileData = {
+                        tid: sfInfo.tid,
+                        sid: sfInfo.sid,
+                        sfid: sfInfo.id,
+                        in_time: now_time,
+                        filename: fileInfo.name,
+                        fileext: fileInfo.ext,
+                        filesize: Array.isArray(parts.field.size) ? parts.field.size[index] : parts.field.size,
+                        filepath,
+                    };
+                    // if (ctx.reUploadPermission) {
+                    //     fileData.re_upload = 1;
+                    // }
+                    const result = await ctx.service.stageShoufangAtt.save(fileData);
+                    if (!result) {
+                        throw '导入数据库保存失败';
+                    }
+                    const attData = await ctx.service.stageShoufangAtt.getDataByFid(result.insertId);
+                    attData.in_time = moment(create_time * 1000).format('YYYY-MM-DD');
+                    files.length !== 0 ? files.unshift(attData) : files.push(attData);
+                    ++index;
+                }
+                responseData.data = files;
+            } catch (err) {
+                this.log(err);
+                // 失败需要消耗掉stream 以防卡死
+                if (stream) {
+                    await sendToWormhole(stream);
+                }
+                this.setMessage(err.toString(), this.messageType.ERROR);
+                responseData.err = 1;
+                responseData.msg = err.toString();
+            }
+            ctx.body = responseData;
+        }
+
+        /**
+         * 删除附件
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        async shoufangDeleteFile(ctx) {
+            const responseData = {
+                err: 0,
+                msg: '',
+                data: '',
+            };
+            try {
+
+                const data = JSON.parse(ctx.request.body.data);
+                const fileInfo = await ctx.service.stageShoufangAtt.getDataById(data.id);
+                if (!fileInfo || !Object.keys(fileInfo).length) {
+                    throw '该文件不存在';
+                }
+                if (fileInfo !== undefined && fileInfo !== '') {
+                    // 先删除文件
+                    await fs.unlinkSync(path.join(this.app.baseDir, fileInfo.filepath));
+                    // 再删除数据库
+                    await ctx.service.stageShoufangAtt.deleteById(data.id);
+                    responseData.data = '';
+                } else {
+                    throw '不存在该文件';
+                }
+            } catch (err) {
+                responseData.err = 1;
+                responseData.msg = err;
+            }
+
+            ctx.body = responseData;
+        }
+
+        /**
+         * 下载附件
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        async shoufangDownloadFile(ctx) {
+            const id = ctx.params.fid;
+            if (id) {
+                try {
+                    const fileInfo = await ctx.service.stageShoufangAtt.getDataById(id);
+                    if (fileInfo !== undefined && fileInfo !== '') {
+                        const fileName = path.join(this.app.baseDir, fileInfo.filepath);
+                        // 解决中文无法下载问题
+                        const userAgent = (ctx.request.header['user-agent'] || '').toLowerCase();
+                        let disposition = '';
+                        if (userAgent.indexOf('msie') >= 0 || userAgent.indexOf('chrome') >= 0) {
+                            disposition = 'attachment; filename=' + encodeURIComponent(fileInfo.filename + fileInfo.fileext);
+                        } else if (userAgent.indexOf('firefox') >= 0) {
+                            disposition = 'attachment; filename*="utf8\'\'' + encodeURIComponent(fileInfo.filename + fileInfo.fileext) + '"';
+                        } else {
+                            /* safari等其他非主流浏览器只能自求多福了 */
+                            disposition = 'attachment; filename=' + new Buffer(fileInfo.filename + fileInfo.fileext).toString('binary');
+                        }
+                        ctx.response.set({
+                            'Content-Type': 'application/octet-stream',
+                            'Content-Disposition': disposition,
+                            'Content-Length': fileInfo.filesize,
+                        });
+                        ctx.body = await fs.createReadStream(fileName);
+                    } else {
+                        throw '不存在该文件';
+                    }
+                } catch (err) {
+                    this.log(err);
+                    this.setMessage(err.toString(), this.messageType.ERROR);
+                }
+            }
+        }
     }
 
     return WapController;

+ 19 - 0
app/middleware/session_auth.js

@@ -35,7 +35,26 @@ module.exports = options => {
             const projectData = yield this.service.project.getDataById(this.session.sessionProject.id);
             this.session.sessionProject.page_show = yield this.service.projectAccount.getPageShow(projectData.page_show);
             this.session.sessionProject.custom = projectData.custom;
+            this.session.sessionProject.dataCollect = projectData.data_collect;
             this.session.sessionProject.customType = projectData.customType;
+            // 判断是否有权限查看决策大屏
+            let showDataCollect = 0;
+            if (projectData.data_collect) {
+                if (sessionUser.is_admin) {
+                    showDataCollect = 1;
+                } else {
+                    const grounpInfo = yield this.service.datacollectAudit.getGroupInfo(projectData.id, accountInfo.account_group);
+                    if (grounpInfo) {
+                        showDataCollect = 1;
+                    } else {
+                        const auditInfo = yield this.service.datacollectAudit.getDataByCondition({ pid: projectData.id, uid: accountInfo.id });
+                        if (auditInfo) {
+                            showDataCollect = 1;
+                        }
+                    }
+                }
+            }
+            this.session.sessionProject.showDataCollect = showDataCollect;
 
             // 同步消息
             yield this.service.notify.syncNotifyData();

+ 115 - 0
app/public/css/main.css

@@ -1478,4 +1478,119 @@ overflow-y: auto;
 }
 .text-red{
   color: rgba(204,73,80);
+}
+.flex-content{
+  height: 98%;
+}
+.left-content,.center-content,.right-content{
+  height: 100vh;
+}
+.left-card-content,.center-chart-content,.right-chart-content{
+  height: 65%;
+}
+.center-di{
+  height: 49%;
+}
+.center-chart,.right-month,.right-chart{
+  height: 50%;
+}
+.di-content{
+  height: 100%;
+}
+.left-chart,.center-table,.right-biaoduan{
+  height: 30%;
+}
+.card .card-small-body{
+  padding: 0;
+}
+.card-per-body{
+  width: 100%;
+  height: 100%;
+  padding: 0;
+}
+.card .card-approve-title{
+  font-size: 2.80rem;
+  position: relative;
+}
+.card .card-approve-title small{
+  position: absolute;
+  font-size: 0.15rem;
+  bottom: -8px;
+  left: 70%;
+}
+.card-case-title{
+  height: 50%;
+  line-height: 200%;
+  font-size: 1.50rem;
+}
+.card-case-text{
+  height: 50%;
+  line-height: 200%;
+}
+.border-bottom-grey-1{
+  border-bottom: 1px solid #464647;
+}
+.height-100{
+  height: 100%;
+}
+.height-99{
+  height: 99%;
+}
+.height-60{
+  height: 60%;
+}
+.height-50{
+  height: 50%;
+}
+.height-30{
+  height: 30%;
+}
+.height-36{
+  height: 38.7%;
+}
+.height-20{
+  height: 19%;
+}
+#review_box{
+  height: 100%;
+  overflow: hidden;
+}
+.right-bottom-50-fl{
+  width: 50%;
+  float: left;
+}
+.right-month-height{
+  height: -webkit-calc(100% - 53px);
+  height: -moz-calc(100% - 53px);
+  height: calc(100% - 53px);
+}
+#comment1, #comment2{
+  margin: 0;
+  padding: 0;
+}
+.tablebox {
+  width: 100%;
+  height: 100%;
+  overflow: hidden;
+}
+.tablebox table {
+  width:100%;
+}
+.tablebox table th,.tablebox table td {
+  padding: 5px 0;
+}
+.tablebox table th {
+  color:#fff;
+  background-color:#2C3034;
+}
+.tablebox table tbody tr{
+  background-color:#2C3034;
+  color: #fff;
+  border-bottom: 1px solid #464647;
+}
+.left-small-card-content,.right-small-card-content{
+  height: 12%;
+}
+.left-big-chart-content,.right-big-chart-content{
+  height: 83%;
 }

BIN
app/public/images/juecedaping01.png


BIN
app/public/images/juecedaping02.png


BIN
app/public/images/juecedaping03.png


BIN
app/public/images/juecedaping04.png


+ 32 - 0
app/public/js/datacollect_scroll.js

@@ -0,0 +1,32 @@
+window.onload = roll(50);
+function roll(t) {
+    var ul1 = document.getElementById("comment1");
+    var ul2 = document.getElementById("comment2");
+    var ulbox = document.getElementById("review_box");
+    ul2.innerHTML = ul1.innerHTML;
+    ulbox.scrollTop = 0; // 开始无滚动时设为0
+    // var timer = setInterval(rollStart, t); // 设置定时器,参数t用在这为间隔时间(单位毫秒),参数t越小,滚动速度越快
+    var timer = setInterval(rollStart, '40');
+    // 鼠标移入div时暂停滚动
+    ulbox.onmouseover = function () {
+        clearInterval(timer);
+    }
+    // 鼠标移出div后继续滚动
+    ulbox.onmouseout = function () {
+        // timer = setInterval(rollStart, t);
+        timer = setInterval(rollStart, '40');
+    }
+}
+// 开始滚动函数
+function rollStart() {
+    // 上面声明的DOM对象为局部对象需要再次声明
+    var ul1 = document.getElementById("comment1");
+    var ul2 = document.getElementById("comment2");
+    var ulbox = document.getElementById("review_box");
+    // 正常滚动不断给scrollTop的值+1,当滚动高度大于列表内容高度时恢复为0
+    if (ulbox.scrollTop >= ul1.scrollHeight) {
+        ulbox.scrollTop = 0;
+    } else {
+        ulbox.scrollTop++;
+    }
+}

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

@@ -188,6 +188,7 @@ function checkValidForm() {
     const endDate = period.split('~')[1];
     if (startDate.indexOf(date) === -1 && endDate.indexOf(date) === -1) {
         toastr.error('所选日期与当前月份不匹配,请重新选择');
+        $('#add-qi input[name="period"]').parents('.form-group').find('.text-danger').remove();
         $('#add-qi input[name="period"]').parents('.form-group').append('<small class="text-danger">所选日期与当前月份不匹配,请重新选择</small>');
         return false;
     }

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

@@ -223,4 +223,4 @@ const TenderSelect = function (setting) {
     };
 
     return { showSelect }
-};
+};

+ 217 - 2
app/public/js/stage.js

@@ -179,7 +179,7 @@ function getHintMsg () {
     }
 }
 
-
+let ledgerData, posData = '';
 $(document).ready(() => {
     const exportExcelSetting = {
         cols: [
@@ -1568,6 +1568,22 @@ $(document).ready(() => {
                     tenderSelect.showSelect(SpreadJsObj.getSelectObject(slSpread.getActiveSheet()));
                 }
             },
+            shoufangdanSpr: '---',
+            shoufangdan: {
+                name: '生成收方单',
+                disabled: function (key, opt) {
+                    const node = SpreadJsObj.getSelectObject(slSpread.getActiveSheet());
+                    if (!node || !node.b_code) return true;
+                },
+                callback: function (key, opt) {
+                    const node = SpreadJsObj.getSelectObject(slSpread.getActiveSheet());
+                    $('#shoufang-lid').val(node.id);
+                    $('#shoufang-pid').val('');
+                    $('#shoufang-ledger').text('清单:' + node.b_code);
+                    $('#shoufang-pos').text('').hide();
+                    $('#addshoufang').modal('show');
+                }
+            }
         }
     });
 
@@ -1973,7 +1989,6 @@ $(document).ready(() => {
         },
         selectionChanged: function (e, info) {
             stagePosSpreadObj.loadExprToInput(info.sheet);
-            console.log(SpreadJsObj.getSelectObject(info.sheet));
         },
         addPegs: function (pegs) {
             if (!pegs || pegs.length <= 0) return;
@@ -2039,6 +2054,10 @@ $(document).ready(() => {
         errorList.loadHisErrorData();
         checkList.loadHisCheckData();
         sumLoadMiss.loadHisMissData();
+        // 收方单赋值
+        ledgerData = result.ledgerData;
+        posData = result.posData;
+        makeShouFang();
     }, null, true);
     spSpread.bind(spreadNS.Events.EditEnded, stagePosSpreadObj.editEnded);
     spSpread.bind(spreadNS.Events.ClipboardPasting, stagePosSpreadObj.clipboardPasting);
@@ -2184,6 +2203,22 @@ $(document).ready(() => {
                     $('#cbr-pos-list').html(html.join(''));
                     $('#calc-by-ratio').modal('show');
                 }
+            },
+            'shoufangdan': {
+                name: '生成收方单',
+                disabled: function (key, opt) {
+                    const node = SpreadJsObj.getSelectObject(spSpread.getActiveSheet());
+                    if (!node) return true;
+                },
+                callback: function (key, opt) {
+                    const node = SpreadJsObj.getSelectObject(spSpread.getActiveSheet());
+                    const ledgerNode = _.find(ledgerData, { id: node.lid });
+                    $('#shoufang-lid').val(node.lid);
+                    $('#shoufang-pid').val(node.id);
+                    $('#shoufang-ledger').text('清单:' + ledgerNode.b_code);
+                    $('#shoufang-pos').text('计量单元:' + node.name).show();
+                    $('#addshoufang').modal('show');
+                }
             }
         }
     });
@@ -4244,4 +4279,184 @@ $(document).ready(() => {
             reloadCooperationHtml();
         });
     });
+    // 收方单生成
+    $('#setshoufang').click(function () {
+        const lid = $('#shoufang-lid').val();
+        const pid = $('#shoufang-pid').val() !== '' ? $('#shoufang-pid').val() : null;
+        // 修改线上
+        postData(window.location.pathname + '/save/shoufang', { type: 'add', postData: { lid, pid } }, function (result) {
+            // 收方单tab打开并显示在最新
+            if (!$('#shoufang-tab').hasClass('active')) {
+                $('#shoufang-tab').click();
+            }
+            const data = makeOneShouFang(result);
+            data.filenum = 0;
+            sfData = _.concat(data, sfData);
+            getShouFangList();
+        });
+    });
+    $('body').on('click', '.del-shoufang', function () {
+        const id = $(this).attr('data-id');
+        $('#del-shoufang-id').val(id);
+        const name = $(this).parents('tr').children('td').eq(0).text();
+        const ledger_code = name.split(' / ')[0];
+        const pos_name = name.split(' / ')[1] ? name.split(' / ')[1] : '';
+        $('#del-shoufang-ledger').text('清单:' + ledger_code);
+        if (pos_name) {
+            $('#del-shoufang-pos').text('计量单元:' + pos_name).show();
+        } else {
+            $('#del-shoufang-pos').text('').hide();
+        }
+    });
+    // 收方单删除
+    $('#delshoufang').click(function () {
+        const sfid = $('#del-shoufang-id').val();
+        // 修改线上
+        postData(window.location.pathname + '/save/shoufang', { type: 'del', postData: { sfid } }, function (result) {
+            // 删除到sfData中
+            const att_index = sfData.findIndex(function (item) {
+                return item.id === parseInt(sfid);
+            });
+            sfData.splice(att_index, 1);
+            // 重新生成List
+            if ($('#shoufang-table tr').length === 1) {
+                getShouFangList(parseInt($('#shoufang-currentPage').text()) - 1);
+            } else {
+                getShouFangList(parseInt($('#shoufang-currentPage').text()));
+            }
+            $('#shoufangdelete').modal('hide');
+        });
+    });
+    // 切换页数
+    $('.shoufang-page-select').on('click', function () {
+        const totalPageNum = parseInt($('#shoufang-totalPage').text());
+        const lastPageNum = parseInt($('#shoufang-currentPage').text());
+        const status = $(this).attr('content');
+        if (status === 'pre' && lastPageNum > 1) {
+            getShouFangList(lastPageNum-1);
+        } else if (status === 'next' && lastPageNum < totalPageNum) {
+            getShouFangList(lastPageNum+1);
+        }
+    });
+    // 获取单个收方单附件列表
+    $('body').on('click', '.show-shoufang-att', function () {
+        const id = parseInt($(this).attr('data-id'));
+        let html = '';
+        $('#shoufang-flie-list').html(html);
+        postData(window.location.pathname + '/shoufang/file', { sfid: id }, function (result) {
+            for (const att of result) {
+                html += `<tr><td>${att.filename}${att.fileext}</td><td>${moment(att.in_time).format('YYYY-MM-DD HH:mm')}</td><td>` +
+                    `<a href="${att.filepath}" target="_blank"><i class="fa fa-download"></i></a>` + makeDelHtml(att.id) +`</td></tr>`;
+            }
+            $('#shoufang-flie-list').html(html);
+            $('[data-toggle="tooltip"]').tooltip();
+        });
+
+        function makeDelHtml(fid) {
+            return sfAttDelPower ? '<a href="javascript:void(0)" data-sfid="'+ id +'" data-id="'+ fid +'" title="删除" class="del-shoufang-att text-danger ml-3"><i data-toggle="tooltip" data-placement="left" data-original-title="删除" class="fa fa-remove"></i></a>' : '';
+        }
+    });
+    // 删除收方单附件
+    $('body').on('click', '.del-shoufang-att', function () {
+        const id = $(this).data('id');
+        const _self = $(this);
+        const data = { id };
+        const sfid = $(this).data('sfid');
+        postData('/wap/shoufang/delfile', data, function (result) {
+            _self.parents('tr').remove();
+            // 附件个数更新
+            $('#shoufang-table').find('.show-shoufang-att[data-id="'+ sfid +'"]').text($('#shoufang-flie-list tr').length);
+            const sfInfo = _.find(sfData, { id: sfid });
+            sfInfo.filenum = $('#shoufang-flie-list tr').length;
+        });
+    });
+    // 定位到对应的清单或计量单元
+    $('body').on('click', '.show-shoufang-node', function () {
+        const id = parseInt($(this).attr('data-id'));
+        const sfInfo = _.find(sfData, { id });
+        if (sfInfo) {
+            const lData = _.find(ledgerData, { id: sfInfo.lid });
+            SpreadJsObj.locateTreeNode(slSpread.getActiveSheet(), lData.ledger_id, true);
+            stagePosSpreadObj.loadCurPosData();
+            if (sfInfo.pid) {
+                const posData = _.find(spSpread.getActiveSheet().zh_data, { id: sfInfo.pid });
+                SpreadJsObj.locateData(spSpread.getActiveSheet(), posData);
+                stagePosSpreadObj.loadExprToInput(spSpread.getActiveSheet());
+            }
+        }
+    });
+
+    // 定位到对应的清单或计量单元
+    $('body').on('click', '.show-shoufang-report', function () {
+        const id = parseInt($(this).attr('data-id'));
+        const [qrCodePath, replace_key_params] = makeReportData(id);
+        sessionStorage.qrCodePath = qrCodePath;
+        sessionStorage.replace_key_params = JSON.stringify(replace_key_params);
+        window.open('/individualReport/A4');
+    });
 });
+function makeOneShouFang(sf) {
+    const lData = _.find(ledgerData, { id: sf.lid });
+    sf.name = lData.b_code;
+    if (sf.pid) {
+        const pData = _.find(posData, { id: sf.pid });
+        sf.name = lData.b_code + ' / ' + pData.name;
+    }
+    return sf;
+}
+function makeShouFang() {
+    for (const sf of sfData) {
+        makeOneShouFang(sf);
+    }
+    getShouFangList();
+}
+// 生成收方单列表
+function getShouFangList(currPageNum = 1) {
+    // 每页最多几个附件
+    const pageCount = 15;
+    // 附件总数
+    const total = sfData.length;
+    // 总页数
+    const pageNum = Math.ceil(total/pageCount);
+    $('#shoufang-totalPage').text(pageNum);
+    $('#shoufang-currentPage').text(total === 0 ? 0 : currPageNum);
+    // 当前页附件内容
+    const currPageAttData = sfData.slice((currPageNum-1)*pageCount, currPageNum*pageCount);
+    let html = '';
+    for(const att of currPageAttData) {
+        html += '<tr><td>' + att.name + '</td>' +
+            '<td width="40"><a href="#shoufangfile" class="show-shoufang-att" data-id="'+ att.id +'" data-toggle="modal" data-target="#shoufangfile">'+ att.filenum +'</a></td>' +
+            '<td width="100">' +
+            '<a href="javascript:void(0);" data-id="'+ att.id +'" class="show-shoufang-node mr-1" data-toggle="tooltip" data-placement="left" data-original-title="定位"><i class="fa fa-crosshairs"></i></a> ' +
+            '<a href="javascript:void(0);" data-id="'+ att.id +'" class="show-shoufang-report mr-1"><i data-toggle="tooltip" data-placement="left" data-original-title="预览" class="fa fa-eye"></i></a> ' +
+            // '<a href="javascript:void(0);"><i data-toggle="tooltip" data-placement="left" data-original-title="下载" class="fa fa-download"></i></a> ' +
+            '<a href="javascript:void(0);" data-id="'+ att.id +'" data-toggle="modal" data-target="#shoufangdelete" class="ml-3 text-danger del-shoufang"><i data-toggle="tooltip" data-placement="left" data-original-title="删除"  class="fa fa-remove"></i></a>' +
+            '</td></tr>';
+    }
+    $('#shoufang-table').html(html);
+    $('[data-toggle="tooltip"]').tooltip();
+}
+
+function makeReportData(sfid) {
+    const sfInfo = _.find(sfData, { id: sfid });
+    const ledger_code = sfInfo.name.split(' / ')[0];
+    const pos_name = sfInfo.name.split(' / ')[1] ? sfInfo.name.split(' / ')[1] : '';
+    const replace_key_params = {
+        'KEY_标段名称': tender.name,
+        'KEY_总承包单位': tenderInfo.construction_unit.contract1.company,
+        'KEY_合同段': tenderInfo.deal_info.dealCode,
+        'KEY_监理单位': tenderInfo.construction_unit.supervision1.company,
+        'KEY_编号': '',
+    };
+    replace_key_params['KEY_子目号及子目名称'] = ledger_code;
+    replace_key_params['KEY_桩号及工程部位'] = pos_name;
+    if (sfInfo.pid) {
+        const pData = _.find(posData, { id: sfInfo.pid });
+        replace_key_params['KEY_设计工程数量'] = pData.quantity;
+    } else {
+        const lData = _.find(ledgerData, { id: sfInfo.lid });
+        replace_key_params['KEY_设计工程数量'] = lData.quantity;
+    }
+    const qrCodePath = sfInfo.qrcode;
+    return [qrCodePath, replace_key_params];
+}

+ 57 - 0
app/public/js/wap/global.js

@@ -56,6 +56,63 @@ const postData = function (url, data, successCallback, errorCallBack, showWaitin
 };
 
 /**
+ * 动态请求数据
+ * @param {String} url - 请求链接
+ * @param data - 提交数据
+ * @param {function} successCallback - 返回成功回调
+ * @param {function} errorCallBack - 返回失败回调
+ */
+const postDataWithFile = function (url, formData, successCallback, errorCallBack, showWaiting = true) {
+    if (showWaiting) showWaitingView();
+    if (formData.getAll('file[]').length > 10) {
+        toastr.error('文件数量不能多于10个');
+        if (showWaiting) closeWaitingView();
+        return
+    }
+    $.ajax({
+        type:"POST",
+        url: url,
+        data: formData,
+        dataType: 'json',
+        cache: false,
+        // 告诉jQuery不要去设置Content-Type请求头
+        contentType: false,
+        // 告诉jQuery不要去处理发送的数据
+        processData: false,
+        timeout: 60000,
+        beforeSend: function(xhr) {
+            let csrfToken = Cookies.get('csrfToken_j');
+            xhr.setRequestHeader('x-csrf-token', csrfToken);
+        },
+        success: function(result){
+            if (result.err === 0) {
+                if (successCallback) {
+                    successCallback(result.data);
+                }
+            } else if (result.err === 2) {
+                toastr.error('error: ' + result.msg);
+                setTimeout(function () {
+                    window.location.href = '/wap/login';
+                },1000);
+            } else {
+                toastr.error('error: ' + result.msg);
+                if (errorCallBack) {
+                    errorCallBack();
+                }
+            }
+            if (showWaiting) closeWaitingView();
+        },
+        error: function(jqXHR, textStatus, errorThrown){
+            toastr.error('error: ' + textStatus + " " + errorThrown);
+            if (errorCallBack) {
+                errorCallBack();
+            }
+            if (showWaiting) closeWaitingView();
+        }
+    });
+};
+
+/**
  * 提示框
  *
  * @param string message

+ 14 - 0
app/router.js

@@ -95,6 +95,9 @@ module.exports = app => {
     app.get('/setting/api', sessionAuth, 'settingController.s2b');
     app.post('/setting/api/update', sessionAuth, 'settingController.s2bUpdate');
     app.post('/setting/api/update-status', sessionAuth, 'settingController.s2bUpdateStatus');
+    // 决策大屏
+    app.get('/setting/datacollect', sessionAuth, 'settingController.dataCollect');
+    app.post('/setting/datacollect/save', sessionAuth, 'settingController.dataCollectSave');
 
 
     // 项目相关
@@ -265,6 +268,8 @@ module.exports = app => {
     app.post('/tender/:id/measure/stage/:order/save/file', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageController.saveFile');
     app.post('/tender/:id/measure/stage/:order/check/file', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageController.checkFile');
     app.post('/tender/:id/measure/stage/:order/download/compresse-file', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageController.downloadZip');
+    app.post('/tender/:id/measure/stage/:order/save/shoufang', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageController.saveShoufang');
+    app.post('/tender/:id/measure/stage/:order/shoufang/file', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageController.shoufangAtt');
 
     // 中间计量
     app.get('/tender/:id/measure/stage/:order/detail', sessionAuth, tenderCheck, uncheckTenderCheck, stageCheck, 'stageController.detail');
@@ -537,4 +542,13 @@ module.exports = app => {
     app.get('/wxAuth', 'loginController.wxAuth');
     app.get('/wxproject', 'loginController.wxProject');
     app.get('/wx/url2web', 'loginController.url2web');
+
+    // 收方单附件页,免session
+    app.get('/wap/shoufang/upload', 'wapController.shoufangUpload');
+    app.post('/wap/shoufang/upfile', 'wapController.shoufangUpFile');
+    app.post('/wap/shoufang/delfile', 'wapController.shoufangDeleteFile');
+    app.get('/wap/shoufang/download/file/:fid', 'wapController.shoufangDownloadFile');
+
+    // 决策大屏
+    app.get('/datacollect', sessionAuth, 'datacollectController.index');
 };

+ 71 - 0
app/service/datacollect_audit.js

@@ -0,0 +1,71 @@
+'use strict';
+
+/**
+ * 决策大屏用户查看权限-数据模型
+ *
+ * @author ellisran
+ * @date 2021/09/23
+ * @version
+ */
+const accountGroup = require('../const/account_group').group;
+module.exports = app => {
+    class datacollectAudit extends app.BaseService {
+        constructor(ctx) {
+            super(ctx);
+            this.tableName = 'datacollect_audit';
+        }
+
+        async getGroupInfo(pid, groupid) {
+            const sql = 'SELECT * FROM ?? WHERE pid = ? AND groupid = ? AND uid is NULL';
+            const sqlParam = [this.tableName, pid, groupid];
+            return await this.db.queryOne(sql, sqlParam);
+        }
+
+        async saveAudit(pid, groupid, uid) {
+            const data = {
+                pid,
+                groupid,
+                uid,
+                create_time: new Date(),
+            };
+            return await this.db.insert(this.tableName, data);
+        }
+
+        async saveGroup(pid, groupid) {
+            const transaction = await this.db.beginTransaction();
+            try {
+                // 删除所在组的用户
+                await transaction.delete(this.tableName, { pid, groupid });
+                const data = {
+                    pid,
+                    groupid,
+                    create_time: new Date(),
+                };
+                await transaction.insert(this.tableName, data);
+                await transaction.commit();
+                return true;
+            } catch (err) {
+                await transaction.rollback();
+                throw err;
+            }
+        }
+
+        async delAudit(id) {
+            return await this.db.delete(this.tableName, { id });
+        }
+
+        async getList(pid) {
+            const list = await this.db.select(this.tableName, { pid });
+            for (const l of list) {
+                if (l.uid) {
+                    const accountInfo = await this.ctx.service.projectAccount.getDataById(l.uid);
+                    l.name = accountInfo.name;
+                } else {
+                    l.name = accountGroup[l.groupid];
+                }
+            }
+            return list;
+        }
+    }
+    return datacollectAudit;
+};

+ 3 - 3
app/service/material_bills.js

@@ -33,7 +33,7 @@ module.exports = app => {
             if (!this.ctx.tender || !this.ctx.material) {
                 throw '数据错误';
             }
-            const order = await this._getMaxOrder(this.ctx.material.id);
+            const order = await this._getMaxOrder(this.ctx.tender.id);
             const transaction = await this.db.beginTransaction();
             try {
                 const newBills = {
@@ -69,8 +69,8 @@ module.exports = app => {
             }
         }
 
-        async _getMaxOrder(materialId) {
-            const sql = 'SELECT Max(??) As value FROM ?? Where mid = ' + materialId;
+        async _getMaxOrder(tenderId) {
+            const sql = 'SELECT Max(??) As value FROM ?? Where tid = ' + tenderId;
             const sqlParam = ['order', this.tableName];
             const queryResult = await this.db.queryOne(sql, sqlParam);
             return queryResult.value ? queryResult.value : 0;

+ 6 - 2
app/service/notice_push.js

@@ -34,9 +34,13 @@ module.exports = app => {
          * @param {Integer} pid - 项目id
          * @param {Integer} uid - 查询人id
          */
-        async getNotice(pid, uid) {
+        async getNotice(pid, uid = 0) {
+            const wheres = { pid };
+            if (uid !== 0) {
+                wheres.uid = uid;
+            }
             let notice = await this.db.select(this.tableName, {
-                where: { pid, uid },
+                where: wheres,
                 orders: [['create_time', 'desc']],
                 limit: 10,
                 offset: 0

+ 1 - 0
app/service/project_account.js

@@ -188,6 +188,7 @@ module.exports = app => {
                         name: projectData.name,
                         userAccount: projectData.user_account,
                         custom: projectData.custom,
+                        dataCollect: projectData.data_collect,
                         page_show: await this.getPageShow(projectData.page_show),
                     };
 

+ 82 - 0
app/service/stage_shoufang.js

@@ -0,0 +1,82 @@
+'use strict';
+
+/**
+ * 收方单-数据模型
+ *
+ * @author ellisran
+ * @date 2021/09/23
+ * @version
+ */
+const accountGroup = require('../const/account_group').group;
+const fs = require('fs');
+const path = require('path');
+const qr = require('qr-image');
+const sendToWormhole = require('stream-wormhole');
+module.exports = app => {
+    class stageShoufang extends app.BaseService {
+        constructor(ctx) {
+            super(ctx);
+            this.tableName = 'stage_shoufang';
+        }
+
+        async add(datas) {
+            const transaction = await this.db.beginTransaction();
+            try {
+                const data = {
+                    tid: this.ctx.tender.id,
+                    order: this.ctx.stage.order,
+                    sid: this.ctx.stage.id,
+                    lid: datas.lid,
+                    pid: datas.pid ? datas.pid : null,
+                    create_time: new Date(),
+                };
+                const result = await transaction.insert(this.tableName, data);
+                // 生成二维码并保存
+                const size = 5;
+                const margin = 1;
+                const text = this.ctx.protocol + '://' + this.ctx.host + '/wap/shoufang/upload' +
+                    '?tid=' + this.ctx.tender.id + '&order=' + this.ctx.stage.order + '&sfid=' + result.insertId;
+                // 大小默认5,二维码周围间距默认1
+                const qr_png = await qr.image(text || '', { type: 'png', size: size || 5, margin: margin || 1 });
+                const qrcodePath = '/public/upload/' + this.ctx.tender.id + '/stage/shoufang/qrcode/' + result.insertId + '.png';
+                await this.ctx.helper.saveStreamFile(qr_png, path.resolve(this.app.baseDir, 'app/' + qrcodePath));
+                await transaction.update(this.tableName, { id: result.insertId, qrcode: qrcodePath });
+                await transaction.commit();
+                return result;
+            } catch (err) {
+                await transaction.rollback();
+                throw err;
+            }
+        }
+
+        async del(sfid) {
+            const transaction = await this.db.beginTransaction();
+            try {
+                // 删除附件再删除收方单
+                const sfInfo = await this.getDataById(sfid);
+                const sfAttList = await this.ctx.service.stageShoufangAtt.getAllDataByCondition({ where: { sfid } });
+                if (sfAttList) {
+                    if (sfAttList.length !== 0) {
+                        for (const att of sfAttList) {
+                            if (fs.existsSync(path.join(this.app.baseDir, att.filepath))) {
+                                await fs.unlinkSync(path.join(this.app.baseDir, att.filepath));
+                            }
+                        }
+                    }
+                }
+                // 删除二维码
+                if (fs.existsSync(path.join(this.app.baseDir, sfInfo.qrcode))) {
+                    await fs.unlinkSync(path.join(this.app.baseDir, sfInfo.qrcode));
+                }
+                await transaction.delete(this.ctx.service.stageShoufangAtt.tableName, { sfid });
+                await transaction.delete(this.tableName, { id: sfid });
+                await transaction.commit();
+                return true;
+            } catch (err) {
+                await transaction.rollback();
+                throw err;
+            }
+        }
+    }
+    return stageShoufang;
+};

+ 110 - 0
app/service/stage_shoufang_att.js

@@ -0,0 +1,110 @@
+'use strict';
+
+/**
+ *
+ *  收方单附件
+ * @author Ellisran
+ * @date 2021/10/7
+ * @version
+ */
+
+const archiver = require('archiver');
+const path = require('path');
+const fs = require('fs');
+module.exports = app => {
+    class StageShoufangAtt extends app.BaseService {
+        /**
+         * 构造函数
+         *
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        constructor(ctx) {
+            super(ctx);
+            this.tableName = 'stage_shoufang_attachment';
+        }
+
+        /**
+         * 添加附件
+         // * @param {Object} postData - 表单信息
+         * @param {Object} fileData - 文件信息
+         * @param {int} uid - 上传者id
+         * @return {void}
+         */
+        async save(fileData) {
+            // const data = {
+            // };
+            // Object.assign(data, fileData);
+            const result = await this.db.insert(this.tableName, fileData);
+            return result;
+        }
+
+        /**
+         * 添加附件
+         * @param {Object} postData - 表单信息
+         * @param {Object} fileData - 文件信息
+         * @param {int} uid - 上传者id
+         * @return {void}
+         */
+        async updateByID(postData, fileData) {
+            delete postData.size;
+            const data = {};
+            Object.assign(data, fileData);
+            Object.assign(data, postData);
+            const result = await this.db.update(this.tableName, data);
+            return result.affectedRows === 1;
+        }
+
+        /**
+         * 获取所有附件
+         * @param {int} tid - 标段id
+         * @param {int} sid - 当前期数
+         * @return {void}
+         */
+        async getDataByTenderIdAndStageId(tid, sid) {
+            const sql = 'SELECT att.id, att.lid, att.uid, att.filename, att.fileext, att.filesize, att.extra_upload, att.remark, att.in_time,' +
+                ' pa.name as `username`, leg.name as `lname`, leg.code as `code`, leg.ledger_id as `ledger_id`, leg.b_code as `b_code`' +
+                ' FROM ?? AS att,?? AS pa,?? AS leg' +
+                ' WHERE leg.id = att.lid AND pa.id = att.uid AND att.tid = ? AND att.sid = ? ORDER BY att.id DESC';
+            const sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, this.ctx.service.ledger.tableName, tid, sid];
+            return await this.db.query(sql, sqlParam);
+        }
+
+        /**
+         * 获取单个收方单附件列表
+         * @param {int} tid - 标段id
+         * @param {int} sid - 当前期数
+         * @return {void}
+         */
+        async getListBySfid(sfid) {
+            const { ctx } = this;
+            const sql = 'SELECT * FROM ?? AS att WHERE att.sfid = ? ORDER BY att.id DESC';
+            const sqlParam = [this.tableName, sfid];
+            const result = await this.db.query(sql, sqlParam);
+            for (const att of result) {
+                if (!ctx.helper.canPreview(att.fileext)) att.filepath = `/wap/shoufang/download/file/${att.id}`;
+                else att.filepath = att.filepath.replace(/^app|\/app/, '');
+            }
+            return result;
+        }
+
+        /**
+         * 获取单个附件
+         * @param {int} tid - 标段id
+         * @param {int} sid - 当前期数
+         * @return {void}
+         */
+        async getDataByFid(id) {
+            const { ctx } = this;
+            const sql = 'SELECT att.id, att.filepath, att.filename, att.fileext, att.filesize, att.in_time' +
+                ' FROM ?? AS att' +
+                ' WHERE att.id = ? ORDER BY att.id DESC';
+            const sqlParam = [this.tableName, id];
+            const result = await this.db.queryOne(sql, sqlParam);
+            if (!ctx.helper.canPreview(result.fileext)) result.filepath = `/wap/shoufang/download/file/${result.id}`;
+            else result.filepath = result.filepath.replace(/^app|\/app/, '');
+            return result;
+        }
+    }
+    return StageShoufangAtt;
+};

+ 592 - 0
app/view/datacollect/index.ejs

@@ -0,0 +1,592 @@
+<link href="/public/css/bootstrap/bootstrap-colorpicker.min.css" rel="stylesheet">
+<div class="panel-content">
+    <div class="panel-content" style="background:#2c3237 !important">
+        <div class="panel-title fluid" style="background:#2c3237 !important">
+            <div class="title-main d-flex justify-content-between">
+                <div class="d-inline-block mr-2">
+                    <div class="btn-group">
+                        <button type="button" class="btn btn-sm btn-outline-dark text-white dropdown-toggle" data-toggle="dropdown" id="zhankai">分类一</button>
+                        <div class="dropdown-menu" aria-labelledby="zhankai">
+                            <a class="dropdown-item" href="#">分类一1</a>
+                            <a class="dropdown-item" href="#">分类一2</a>
+                        </div>
+                    </div>
+                    <div class="btn-group">
+                        <button type="button" class="btn btn-sm btn-outline-dark text-white dropdown-toggle" data-toggle="dropdown" id="zhankai">分类二</button>
+                        <div class="dropdown-menu" aria-labelledby="zhankai">
+                            <a class="dropdown-item" href="#">分类二1</a>
+                            <a class="dropdown-item" href="#">分类二2</a>
+                        </div>
+                    </div>
+                    <div class="btn-group">
+                        <button type="button" class="btn btn-sm btn-outline-dark text-white dropdown-toggle" data-toggle="dropdown" id="zhankai">分类三</button>
+                        <div class="dropdown-menu" aria-labelledby="zhankai">
+                            <a class="dropdown-item" href="#">分类三1</a>
+                            <a class="dropdown-item" href="#">分类三2</a>
+                        </div>
+                    </div>
+                </div>
+                <div>
+                    <a href="" class="text-white"><i class="fa fa-arrows-alt"></i></a>
+                    <div class="btn-group">
+                        <button type="button" class="btn btn-sm btn-outline-dark text-white dropdown-toggle" data-toggle="dropdown" id="zhankai">决策大屏1</button>
+                        <div class="dropdown-menu" aria-labelledby="zhankai">
+                            <a class="dropdown-item" href="shujudaping-panel-1.html">决策大屏1</a>
+                            <a class="dropdown-item" href="shujudaping-panel-2.html">决策大屏2</a>
+                            <a class="dropdown-item" href="shujudaping-panel-3.html">决策大屏3</a>
+                            <a class="dropdown-item" href="shujudaping-panel-4.html">决策大屏4</a>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </div>
+        <div class="content-wrap">
+            <div class="c-body" style="background:#2c3237 !important">
+                <div class="flex-content">
+                    <div class="row">
+                        <div class="col-3 pr-0">
+                            <div class="left-content">
+                                <div class="left-card-content">
+                                    <div class="height-20 mb-2">
+                                        <div class="card text-center bg-dark text-white ml-2 mr-2 py-1 height-100">
+                                            <div class="card-body card-per-body">
+                                                <div class="row">
+                                                    <div class="col-6">
+                                                        <h5 class="card-title card-case-title">12</h5>
+                                                        <p class="card-text card-case-text text-muted">标段个数</p>
+                                                    </div>
+                                                    <div class="col-6">
+                                                        <h5 class="card-title card-case-title">63.25%</h5>
+                                                        <p class="card-text card-case-text text-muted">计量进度</p>
+                                                    </div>
+                                                </div>
+                                            </div>
+                                        </div>
+                                    </div>
+                                    <div class="height-20 mb-2">
+                                        <div class="card text-center bg-dark text-white ml-2 mr-2 py-2 height-100">
+                                            <div class="card-body card-per-body">
+                                                <h5 class="card-title card-case-title">123,456,789.36</h5>
+                                                <p class="card-text card-case-text text-muted">台帐金额</p>
+                                            </div>
+                                        </div>
+                                    </div>
+                                    <div class="height-20 mb-2">
+                                        <div class="card text-center bg-dark text-white ml-2 mr-2 py-2 height-100">
+                                            <div class="card-body card-per-body">
+                                                <h5 class="card-title card-case-title">163,000.00</h5>
+                                                <p class="card-text text-muted">累计变更金额</p>
+                                            </div>
+                                        </div>
+                                    </div>
+                                    <div class="height-20 mb-2">
+                                        <div class="card text-center bg-dark text-white ml-2 mr-2 py-2 height-100">
+                                            <div class="card-body card-per-body">
+                                                <h5 class="card-title card-case-title">123,456,789.36</h5>
+                                                <p class="card-text text-muted">累计完成金额</p>
+                                            </div>
+                                        </div>
+                                    </div>
+                                    <div class="height-20">
+                                        <div class="card text-center bg-dark text-white ml-2 mr-2 height-100">
+                                            <div class="card-body card-per-body">
+                                                <h5 class="card-title card-case-title">163,000.00</h5>
+                                                <p class="card-text text-muted">材料调差</p>
+                                            </div>
+                                        </div>
+                                    </div>
+                                </div>
+                                <div class="left-chart">
+                                    <div class="card height-100 bg-dark mt-2 ml-2 mr-2">
+                                        <div id="jechart" style="height: 100%; width: 100%;"></div>
+                                    </div>
+                                </div>
+                            </div>
+                        </div>
+                        <div class="col-6 pl-0 pr-0">
+                            <div class="center-content mr-2">
+                                <div class="center-chart-content">
+                                    <div class="center-di">
+                                        <div class="card bg-dark height-100">
+                                            <div class="di-content mb-2" style="background: #343A40;"></div>
+                                        </div>
+                                    </div>
+                                    <div class="center-chart">
+                                        <div class="card height-100 bg-dark mt-2">
+                                            <div id="jlchart" style="height: 100%; width: 100%;"></div>
+                                        </div>
+                                    </div>
+                                </div>
+                                <div class="center-table">
+                                    <div class="card height-100 bg-dark mt-2">
+                                        <h6 class="bg-dark text-center text-white m-0 py-3">标段明细数据</h6>
+                                        <div class="tablebox">
+                                            <table id="tableId">
+                                                <thead>
+                                                <tr>
+                                                    <th>标段</th>
+                                                    <th>期数</th>
+                                                    <th>0号台帐</th>
+                                                    <th>本期完成</th>
+                                                    <th>截至本期完成</th>
+                                                    <th>本期应付</th>
+                                                    <th>截至本期应付</th>
+                                                </tr>
+                                                </thead>
+                                                <tbody>
+                                                <tr>
+                                                    <td>隧道工程一标段</td>
+                                                    <td>第1期</td>
+                                                    <td>1000000</td>
+                                                    <td>80000</td>
+                                                    <td>95000</td>
+                                                    <td>75000</td>
+                                                    <td>85000</td>
+                                                </tr>
+                                                <tr>
+                                                    <td>隧道工程二标段</td>
+                                                    <td>第2期</td>
+                                                    <td>1000000</td>
+                                                    <td>80000</td>
+                                                    <td>95000</td>
+                                                    <td>75000</td>
+                                                    <td>85000</td>
+                                                </tr>
+                                                <tr>
+                                                    <td>第二工地试验室</td>
+                                                    <td>第0期</td>
+                                                    <td>1000000</td>
+                                                    <td>80000</td>
+                                                    <td>95000</td>
+                                                    <td>75000</td>
+                                                    <td>85000</td>
+                                                </tr>
+                                                <tr>
+                                                    <td>桥梁工程二标段</td>
+                                                    <td>第1期</td>
+                                                    <td>1000000</td>
+                                                    <td>80000</td>
+                                                    <td>95000</td>
+                                                    <td>75000</td>
+                                                    <td>85000</td>
+                                                </tr>
+                                                <tr>
+                                                    <td>标段5</td>
+                                                    <td>第2期</td>
+                                                    <td>1000000</td>
+                                                    <td>80000</td>
+                                                    <td>95000</td>
+                                                    <td>75000</td>
+                                                    <td>85000</td>
+                                                </tr>
+                                                <tr>
+                                                    <td>标段6</td>
+                                                    <td>第1期</td>
+                                                    <td>1000000</td>
+                                                    <td>80000</td>
+                                                    <td>95000</td>
+                                                    <td>75000</td>
+                                                    <td>85000</td>
+                                                </tr>
+                                                <tr>
+                                                    <td>隧道工程一标段</td>
+                                                    <td>第1期</td>
+                                                    <td>1000000</td>
+                                                    <td>80000</td>
+                                                    <td>95000</td>
+                                                    <td>75000</td>
+                                                    <td>85000</td>
+                                                </tr>
+                                                <tr>
+                                                    <td>隧道工程二标段</td>
+                                                    <td>第2期</td>
+                                                    <td>1000000</td>
+                                                    <td>80000</td>
+                                                    <td>95000</td>
+                                                    <td>75000</td>
+                                                    <td>85000</td>
+                                                </tr>
+                                                <tr>
+                                                    <td>第二工地试验室</td>
+                                                    <td>第0期</td>
+                                                    <td>1000000</td>
+                                                    <td>80000</td>
+                                                    <td>95000</td>
+                                                    <td>75000</td>
+                                                    <td>85000</td>
+                                                </tr>
+                                                <tr>
+                                                    <td>桥梁工程二标段</td>
+                                                    <td>第1期</td>
+                                                    <td>1000000</td>
+                                                    <td>80000</td>
+                                                    <td>95000</td>
+                                                    <td>75000</td>
+                                                    <td>85000</td>
+                                                </tr>
+                                                <tr>
+                                                    <td>标段5</td>
+                                                    <td>第2期</td>
+                                                    <td>1000000</td>
+                                                    <td>80000</td>
+                                                    <td>95000</td>
+                                                    <td>75000</td>
+                                                    <td>85000</td>
+                                                </tr>
+                                                <tr>
+                                                    <td>标段6</td>
+                                                    <td>第1期</td>
+                                                    <td>1000000</td>
+                                                    <td>80000</td>
+                                                    <td>95000</td>
+                                                    <td>75000</td>
+                                                    <td>85000</td>
+                                                </tr>
+                                                </tbody>
+                                            </table>
+                                            <table id="tableId1"></table>
+                                        </div>
+                                    </div>
+                                </div>
+                            </div>
+                        </div>
+                        <div class="col-3 pl-0">
+                            <div class="right-content">
+                                <div class="right-chart-content">
+                                    <div class="right-month">
+                                        <h6 class="card bg-dark text-center text-white m-0 pt-2 pb-3">本月审批统计</h6>
+                                        <div class="row right-month-height">
+                                            <div class="col-6 pr-0">
+                                                <div class="card text-center bg-dark text-white border-right-0 border-botton-0 height-100">
+                                                    <div class="card-body card-small-body height-100">
+                                                        <h5 class="card-title card-approve-title height-50">5<small>期</small></h5>
+                                                        <p class="card-text text-muted height-50">计量期</p>
+                                                    </div>
+                                                </div>
+                                            </div>
+                                            <div class="col-6 pl-0">
+                                                <div class="card text-center bg-dark text-white border-botton-0 height-100">
+                                                    <div class="card-body card-small-body height-100">
+                                                        <h5 class="card-title card-approve-title height-50">25<small class="">条</small></h5>
+                                                        <p class="card-text text-muted height-50">变更令</p>
+                                                    </div>
+                                                </div>
+                                            </div>
+                                            <div class="col-6 pr-0">
+                                                <div class="card text-center bg-dark text-white border-right-0 border-top-0 height-100">
+                                                    <div class="card-body card-small-body height-100">
+                                                        <h5 class="card-title card-approve-title height-50">3<small class="">次</small></h5>
+                                                        <p class="card-text text-muted height-50">台帐修订</p>
+                                                    </div>
+                                                </div>
+                                            </div>
+                                            <div class="col-6 pl-0">
+                                                <div class="card text-center bg-dark text-white border-top-0 height-100">
+                                                    <div class="card-body card-small-body height-100">
+                                                        <h5 class="card-title card-approve-title height-50">0<small class="">期</small></h5>
+                                                        <p class="card-text text-muted height-50">材料调差</p>
+                                                    </div>
+                                                </div>
+                                            </div>
+                                        </div>
+                                    </div>
+                                    <div class="right-chart">
+                                        <div class="card height-100 bg-dark">
+                                            <div id="jlwcdchart" style="height: 100%; width: 100%;"></div>
+                                        </div>
+                                    </div>
+                                </div>
+                                <div class="right-biaoduan">
+                                    <div class="card height-100 bg-dark mt-2">
+                                        <h6 class="bg-dark text-center text-white m-0 py-3">标段审批信息</h6>
+                                        <div id="review_box" class="m-0 p-3 mb-2">
+                                            <ul class="list-unstyled" id="comment1">
+                                                <% for (const notice of noticeList) { %>
+                                                    <% if(notice.type === pushType.stage) { %>
+                                                        <li class="media pb-3 mb-3 border-bottom-grey-1">
+                                                            <div class="media-body">
+                                                                <div class="row">
+                                                                    <div class="col-2"><span class="badge badge-success">计量审批</span></div>
+                                                                    <div class="col-10 text-white">
+                                                                        <a href="/tender/<%- notice.tid %>" class="text-success"><%- notice.name %></a>
+                                                                        <a href="/tender/<%- notice.tid %>/measure/stage/<%- notice.order %>" class="text-success">第<%- notice.order %>期 </a>
+                                                                        <%- acStage.statusString[notice.status]%>
+                                                                    </div>
+                                                                </div>
+                                                                <p class="mt-1 mb-0 text-white"><%- notice.su_name %><small class="ml-1 text-muted"><%- (notice.su_role ? '- ' + notice.su_role : '') %></small>
+                                                                    <span class="pull-right text-muted"><%- ctx.helper.formatFullDate(notice.create_time) %></span>
+                                                                </p>
+                                                            </div>
+                                                        </li>
+                                                    <% } else if(notice.type === pushType.material) { %>
+                                                        <li class="media pb-3 mb-3 border-bottom-grey-1">
+                                                            <div class="media-body">
+                                                                <div class="row">
+                                                                    <div class="col-2"><span class="badge badge-secondary">材料调差</span></div>
+                                                                    <div class="col-10 text-white">
+                                                                        <a href="/tender/<%- notice.tid %>" class="text-success"><%- notice.name %></a>
+                                                                        <a href="/tender/<%- notice.tid %>/measure/material/<%- notice.order %>" class="text-success">第<%- notice.order %>期 </a>
+                                                                        <%- acMaterial.statusString[notice.status]%>
+                                                                    </div>
+                                                                </div>
+                                                                <p class="mt-1 mb-0 text-white"><%- notice.su_name %><small class="ml-1 text-muted"><%- (notice.su_role ? '- ' + notice.su_role : '') %></small>
+                                                                    <span class="pull-right text-muted"><%- ctx.helper.formatFullDate(notice.create_time) %></span>
+                                                                </p>
+                                                            </div>
+                                                        </li>
+                                                    <% } else if(notice.type === pushType.ledger) { %>
+                                                        <li class="media pb-3 mb-3 border-bottom-grey-1">
+                                                            <div class="media-body">
+                                                                <div class="row">
+                                                                    <div class="col-2"><span class="badge badge-info">台账审批</span></div>
+                                                                    <div class="col-10 text-white">
+                                                                        <a data-id="<%- notice.id %>"href="/tender/<%- notice.tid %>/ledger" class="text-success"><%- notice.name %></a> <%- acLedger.statusString[notice.status]%>
+                                                                    </div>
+                                                                </div>
+                                                                <p class="mt-1 mb-0 text-white"><%- notice.su_name %><small class="ml-1 text-muted"><%- (notice.su_role ? '- ' + notice.su_role : '') %></small>
+                                                                    <span class="pull-right text-muted"><%- ctx.helper.formatFullDate(notice.create_time) %></span>
+                                                                </p>
+                                                            </div>
+                                                        </li>
+                                                    <% } else if(notice.type === pushType.revise) { %>
+                                                        <li class="media pb-3 mb-3 border-bottom-grey-1">
+                                                            <div class="media-body">
+                                                                <div class="row">
+                                                                    <div class="col-2"><span class="badge badge-info">台账修订</span></div>
+                                                                    <div class="col-10 text-white">
+                                                                        <a href="/tender/<%- notice.tid %>" class="text-success"><%- notice.name %></a>
+                                                                        <a href="/tender/<%- notice.tid %>/revise/info" class="text-success">台账修订(第<%- notice.corder %>次)</a>
+                                                                        <%- acRevise.statusString[notice.status]%>
+                                                                    </div>
+                                                                </div>
+                                                                <p class="mt-1 mb-0 text-white"><%- notice.su_name %><small class="ml-1 text-muted"><%- (notice.su_role ? '- ' + notice.su_role : '') %></small>
+                                                                    <span class="pull-right text-muted"><%- ctx.helper.formatFullDate(notice.create_time) %></span>
+                                                                </p>
+                                                            </div>
+                                                        </li>
+                                                    <% } else if(notice.type === pushType.change){ %>
+                                                        <li class="media pb-3 mb-3 border-bottom-grey-1">
+                                                            <div class="media-body">
+                                                                <div class="row">
+                                                                    <div class="col-2"><span class="badge badge-danger">变更审批</span></div>
+                                                                    <div class="col-10 text-white">
+                                                                        <a href="/tender/<%- notice.tid %>" class="text-success"><%- notice.name %></a>
+                                                                        <a href="/tender/<%- notice.tid %>/change/<%- notice.cid %>/information" class="text-success"><%- notice.c_code %> </a>
+                                                                        <%- acChange.statusString[notice.status]%>
+                                                                    </div>
+                                                                </div>
+                                                                <p class="mt-1 mb-0 text-white"><%- notice.su_name %><small class="ml-1 text-muted"><%- (notice.su_role ? '- ' + notice.su_role : '') %></small>
+                                                                    <span class="pull-right text-muted"><%- ctx.helper.formatFullDate(notice.create_time) %></span>
+                                                                </p>
+                                                            </div>
+                                                        </li>
+                                                    <% } else if(notice.type === pushType.advance) { %>
+                                                        <li class="media pb-3 mb-3 border-bottom-grey-1">
+                                                            <div class="media-body">
+                                                                <div class="row">
+                                                                    <div class="col-2"><span class="badge badge-warning">预付款</span></div>
+                                                                    <div class="col-10 text-white">
+                                                                        <a href="/tender/<%- notice.tid %>" class="text-success"><%- notice.name %></a>
+                                                                        <a href="/tender/<%- notice.tid %>/advance/<%- notice.vid %>/detail" class="text-success">第<%- notice.order %>期</a>
+                                                                        <%- acAdvance.statusString[notice.status]%>
+                                                                    </div>
+                                                                </div>
+                                                                <p class="mt-1 mb-0 text-white"><%- notice.su_name %><small class="ml-1 text-muted"><%- (notice.su_role ? '- ' + notice.su_role : '') %></small>
+                                                                    <span class="pull-right text-muted"><%- ctx.helper.formatFullDate(notice.create_time) %></span>
+                                                                </p>
+                                                            </div>
+                                                        </li>
+                                                    <% } %>
+                                                <% } %>
+                                            </ul>
+                                            <ul id="comment2"></ul>
+                                        </div>
+                                    </div>
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
+<script src="/public/js/datacollect_scroll.js"></script>
+<script type="text/javascript">  autoFlashHeight();</script>
+<script type="text/javascript">
+    var myChart1 = echarts.init(document.getElementById('jechart'), 'dark');
+    option = {
+        title: {
+            text: '金额统计图',
+            left: 'center',
+            top:'7%'
+        },
+        color: ['rgba(24,144,255,0.7)','rgba(69,183,149,0.7)','rgba(250,204,20,0.7)','rgba(145,82,225,0.7)','rgba(58,207,221,0.7)','rgba(204,73,80,0.7)','rgba(255,255,225,0.7)'],
+        backgroundColor: '#343a40 ',
+        tooltip: {
+            trigger: 'item'
+        },
+        // legend: {
+        //     orient: 'vertical',
+        //     left: 'center',
+        // },
+        series: [
+            {
+                name: '访问来源',
+                type: 'pie',
+                radius: '60%',
+                top:'15%',
+                data: [
+                    {value: 1048, name: '2021'},
+                    {value: 735, name: '2020'},
+                    {value: 580, name: '2019'},
+                    {value: 484, name: '2018'},
+                    {value: 300, name: '2017'}
+                ],
+                emphasis: {
+                    itemStyle: {
+                        shadowBlur: 10,
+                        shadowOffsetX: 0,
+                        shadowColor: 'rgba(0, 0, 0, 0.5)'
+                    }
+                }
+            }
+        ]
+    };
+    myChart1.setOption(option);
+
+    var myChart2 = echarts.init(document.getElementById('jlchart'), 'dark');
+    option = {
+        title: {
+            text: '计量情况',
+            left: 'center',
+            top:'5%'
+        },
+        color: ['rgba(24,144,255,0.7)','rgba(69,183,149,0.7)','rgba(250,204,20,0.7)','rgba(145,82,225,0.7)','rgba(58,207,221,0.7)','rgba(204,73,80,0.7)','rgba(255,255,225,0.7)'],
+        backgroundColor: '#343a40 ',
+        tooltip: {
+            trigger: 'axis',
+            axisPointer: {            // 坐标轴指示器,坐标轴触发有效
+                type: 'shadow'        // 默认为直线,可选为:'line' | 'shadow'
+            }
+        },
+        legend: {
+            data: ['台帐', '合同计量', '变更计量'],
+            top:'17%'
+        },
+        grid: {
+            top:'35%',
+            left: '3%',
+            right: '4%',
+            bottom: '3%',
+            containLabel: true
+        },
+        xAxis: [
+            {
+                type: 'category',
+                data: ['1标', '2标', '3标', '4标', '5标', '6标', '7标']
+            }
+        ],
+        yAxis: [
+            {
+                type: 'value',
+                name:'金额',
+                position: 'left',
+                axisLabel : {
+                    formatter: '{value} 元'
+                },
+                splitArea : {show : true}
+            },
+            {
+                type: 'value',
+                name:'完成度',
+                position: 'right',
+                min:0,
+                max:100,
+                axisLabel : {
+                    formatter: '{value} %'
+                },
+                splitArea : {show : true}
+            }
+        ],
+        series: [
+            {
+                name: '台帐',
+                type: 'bar',
+                emphasis: {
+                    focus: 'series'
+                },
+                data: [1220, 732, 601, 934, 1590, 730, 1020]
+            },
+            {
+                name: '合同计量',
+                type: 'bar',
+                stack: '计量',
+                emphasis: {
+                    focus: 'series'
+                },
+                data: [120, 132, 101, 134, 90, 230, 210]
+            },
+            {
+                name: '变更计量',
+                type: 'bar',
+                stack: '计量',
+                emphasis: {
+                    focus: 'series'
+                },
+                data: [220, 182, 191, 234, 290, 330, 310]
+            }
+        ]
+    };
+    myChart2.setOption(option);
+
+    var myChart3 = echarts.init(document.getElementById('jlwcdchart'), 'dark');
+    option = {
+        title: {
+            text: '计量完成度统计',
+            left: 'center',
+            top:'5%'
+        },
+        color: ['rgba(24,144,255,0.7)','rgba(69,183,149,0.7)','rgba(250,204,20,0.7)','rgba(145,82,225,0.7)','rgba(58,207,221,0.7)','rgba(204,73,80,0.7)','rgba(255,255,225,0.7)'],
+        backgroundColor: '#343a40 ',
+        tooltip: {
+            trigger: 'axis',
+            axisPointer: {            // Use axis to trigger tooltip
+                type: 'shadow'        // 'shadow' as default; can also be 'line' or 'shadow'
+            }
+        },
+        grid: {
+            left: '3%',
+            right: '4%',
+            bottom: '3%',
+            containLabel: true
+        },
+        xAxis: {
+            type: 'value'
+        },
+        yAxis: {
+            type: 'category',
+            data: ['隧道工程一标段', '隧道工程二标段', '第二工地试验室', '桥梁工程二标段', '标段5', '标段6', '第三工地试验室']
+        },
+        series: [
+            {
+                name: '完成度',
+                type: 'bar',
+                stack: 'total',
+                label: {
+                    show: true
+                },
+                emphasis: {
+                    focus: 'series'
+                },
+                data: [320, 302, 301, 334, 390, 330, 320]
+            }
+        ]
+    };
+    myChart3.setOption(option);
+    function echartsReset() {
+        myChart1.resize();
+        myChart2.resize();
+        myChart3.resize();
+    }
+</script>

+ 1 - 0
app/view/measure/stage_modal.ejs

@@ -145,6 +145,7 @@
         const endDate = period.split('~')[1];
         if (startDate.indexOf(date) === -1 && endDate.indexOf(date) === -1) {
             toastr.error('所选日期与当前月份不匹配,请重新选择');
+            $('#edit-period').parents('.form-group').find('.text-danger').remove();
             $('#edit-period').parents('.form-group').append('<small class="text-danger">所选日期与当前月份不匹配,请重新选择</small>');
             return false;
         }

+ 109 - 0
app/view/setting/datacollect.ejs

@@ -0,0 +1,109 @@
+<% include ./sub_menu.ejs %>
+<div class="panel-content">
+    <div class="panel-title">
+        <div class="title-main">
+            <h2>决策大屏
+                <a href="#power" data-toggle="modal" data-target="#power" class="btn btn-primary btn-sm pull-right">权限管理</a>
+                <a href="#sort" data-toggle="modal" data-target="#sort" class="btn btn-primary btn-sm pull-right mr-1">分类管理</a>
+            </h2>
+        </div>
+    </div>
+    <div class="content-wrap">
+        <div class="c-body">
+            <div class="sjs-height-0">
+                <nav class="nav nav-tabs m-3" id="tablist" role="tablist">
+                    <a class="nav-item nav-link<% if (ctx.session.sessionProject.dataCollect === 0 || ctx.session.sessionProject.dataCollect === 1) { %> active<% } %>" data-datacollect="1" data-toggle="tab" href="#shujudaping-one" role="tab">决策大屏一</a>
+                    <a class="nav-item nav-link<% if (ctx.session.sessionProject.dataCollect === 2) { %> active<% } %>" data-datacollect="2" data-toggle="tab" href="#shujudaping-two" role="tab">决策大屏二</a>
+                    <a class="nav-item nav-link<% if (ctx.session.sessionProject.dataCollect === 3) { %> active<% } %>" data-datacollect="3" data-toggle="tab" href="#shujudaping-three" role="tab">决策大屏三</a>
+                    <a class="nav-item nav-link<% if (ctx.session.sessionProject.dataCollect === 4) { %> active<% } %>" data-datacollect="4" data-toggle="tab" href="#shujudaping-four" role="tab">决策大屏四</a>
+                    <div class="ml-auto">
+                        <div class="form-check form-check-inline">
+                            <input class="form-check-input" type="checkbox" id="show-datacollect" value="option1" <% if (ctx.session.sessionProject.dataCollect) { %>checked<% } %>>
+                            <label class="form-check-label" for="show-datacollect">数据大屏显示</label>
+                        </div>
+                    </div>
+                </nav>
+                <div class="tab-content m-3">
+                    <div id="shujudaping-one" class="tab-pane<% if (ctx.session.sessionProject.dataCollect === 0 || ctx.session.sessionProject.dataCollect === 1) { %> active<% } %>"><img src="/public/images/juecedaping01.png" width="100%"></div>
+                    <div id="shujudaping-two" class="tab-pane<% if (ctx.session.sessionProject.dataCollect === 2) { %> active<% } %>"><img src="/public/images/juecedaping02.png" width="100%"></div>
+                    <div id="shujudaping-three" class="tab-pane<% if (ctx.session.sessionProject.dataCollect === 3) { %> active<% } %>"><img src="/public/images/juecedaping03.png" width="100%"></div>
+                    <div id="shujudaping-four" class="tab-pane<% if (ctx.session.sessionProject.dataCollect === 4) { %> active<% } %>"><img src="/public/images/juecedaping04.png" width="100%"></div>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
+<script src="/public/js/ztree/jquery.ztree.core.js"></script>
+<script src="/public/js/ztree/jquery.ztree.excheck.js"></script>
+<script>
+    const category = JSON.parse(unescape('<%- escape(JSON.stringify(categoryData)) %>'));
+    console.log(category);
+    $(function () {
+        autoFlashHeight();
+        $('#show-datacollect').click(function () {
+            let data_collect = 0;
+            if ($(this).is(':checked')) {
+                data_collect = $('#tablist').find('.active').data('datacollect');
+            }
+            postData('/setting/datacollect/save', { type: 'show', data_collect }, function (result) {
+
+            })
+        });
+        $('#tablist a').click(function () {
+            if ($('#show-datacollect').is(':checked')) {
+                const data_collect = $(this).data('datacollect');
+                postData('/setting/datacollect/save', { type: 'show', data_collect }, function (result) {
+
+                })
+            }
+        });
+
+        const setting = {
+            check: {
+                enable: true
+            },
+            data: {
+                simpleData: {
+                    enable: true
+                }
+            }
+        };
+        const zNodes = [];
+        const newCategory = _.filter(_.orderBy(category, ['level'], ['asc']), function (item) {
+            return item.level !== 0;
+        });
+        let insertid = 1;
+        const level1Array = [];
+        const level2Array = [];
+        for (const c of newCategory) {
+            if (c.level === 1) {
+                for (const v of c.value) {
+                    level1Array.push({id: insertid});
+                    zNodes.push({id: insertid, pId: 0, name: v.value, open: true, checked:true});
+                    insertid++;
+                }
+            } else if (c.level === 2) {
+                for (const l1 of level1Array) {
+                    zNodes.push({id: insertid, pId: l1.id, name: '', checked:true});
+                    insertid++;
+                    for (const v of c.value) {
+                        level2Array.push({id: insertid});
+                        zNodes.push({id: insertid, pId: l1.id, name: v.value, open: true, checked:true});
+                        insertid++;
+                    }
+                }
+            }
+            else if (c.level === 3) {
+                for (const l2 of level2Array) {
+                    zNodes.push({id: insertid, pId: l2.id, name: '', checked:true});
+                    insertid++;
+                    for (const v of c.value) {
+                        zNodes.push({id: insertid, pId: l2.id, name: v.value, checked:true});
+                        insertid++;
+                    }
+                }
+            }
+        }
+        $.fn.zTree.init($("#treeDemo"), setting, zNodes);
+    })
+</script>

+ 194 - 0
app/view/setting/datacollect_modal.ejs

@@ -0,0 +1,194 @@
+<!--分类管理 -->
+<div class="modal fade" id="sort" data-backdrop="static">
+    <div class="modal-dialog" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">决策大屏分类管理</h5>
+            </div>
+            <div class="modal-body">
+                <div class="zTreeDemoBackground left modal-height-500" style="overflow: auto">
+                    <ul id="treeDemo" class="ztree"></ul>
+                </div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">取消</button>
+                <button type="button" class="btn btn-sm btn-primary">确定</button>
+            </div>
+        </div>
+    </div>
+</div>
+<!--权限管理 -->
+<div class="modal fade" id="power" data-backdrop="static">
+    <div class="modal-dialog" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">权限管理</h5>
+            </div>
+            <div class="modal-body">
+                <!--<div class="form-check">-->
+                    <!--<input class="form-check-input" type="radio" name="exampleRadios" id="exampleRadios1" value="option1">-->
+                    <!--<label class="form-check-label" for="exampleRadios1">-->
+                        <!--仅自己可见(只有管理员自己可以看到)-->
+                    <!--</label>-->
+                <!--</div>-->
+                <div class="form-check">
+                    <!--<input class="form-check-input" type="radio" name="exampleRadios" id="exampleRadios2" value="option2" checked>-->
+                    <label class="form-check-label" for="exampleRadios2">
+                        指定成员可见(只有指定的成员可以访问当前数据大屏)
+                    </label>
+                </div>
+                <div class="px-3 py-2">
+                    <div class="">
+                        <!--<div class="input-group">-->
+                            <!--<input class="form-control" placeholder="搜索用户组、成员" type="text">-->
+                            <!--<div class="input-group-append">-->
+                                <!--<button class="btn btn-outline-secondary" type="button"><i class="fa fa-search"></i></button>-->
+                            <!--</div>-->
+                        <!--</div>-->
+                        <button class="btn btn-outline-primary btn-sm dropdown-toggle" type="button" id="dropdownMenuButton"
+                                data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
+                            添加成员
+                        </button>
+                        <div class="dropdown-menu dropdown-menu-left" aria-labelledby="dropdownMenuButton" style="width:220px">
+                            <div class="mb-2 p-2"><input class="form-control form-control-sm" placeholder="姓名/手机 检索"
+                                                         id="gr-search" autocomplete="off"></div>
+                            <dl class="list-unstyled book-list">
+                                <% accountGroup.forEach((group, idx) => { %>
+                                    <dt><a href="javascript: void(0);" class="acc-btn" data-groupid="<%- idx %>"
+                                           data-type="hide"><i class="fa fa-plus-square"></i></a> <%- group.groupName %></dt>
+                                    <div class="dd-content" data-toggleid="<%- idx %>">
+                                        <dd class="border-bottom p-2 mb-0" data-groupid="<%- idx %>><p class="mb-0 d-flex"><span class="text-primary">该单位下所有组员</span></p></dd>
+                                        <% group.groupList.forEach(item => { %>
+                                            <% if (item.id !== ctx.session.sessionUser.accountId) { %>
+                                                <dd class="border-bottom p-2 mb-0 " data-id="<%- item.id %>">
+                                                    <p class="mb-0 d-flex"><span class="text-primary"><%- item.name %></span><span
+                                                                class="ml-auto"><%- item.mobile %></span></p>
+                                                    <span class="text-muted"><%- item.role %></span>
+                                                </dd>
+                                            <% } %>
+                                        <% });%>
+                                    </div>
+                                <% }) %>
+                            </dl>
+                        </div>
+                        <div class="card mt-1">
+                            <ul class="list-unstyled book-list modal-height-500" id="audit-list" style="overflow: auto">
+                                <% for (const audit of dataCollectAudits) { %>
+                                <li class="border-bottom p-2 mb-0"><a href="javascript:void(0)" class="text-danger pull-right" data-id="<%- audit.id %>" data-type="<% if (audit.uid) { %>audit<% } else { %>group<% } %>"><i class="fa fa-remove text-danger remove-audit"></i></a><%- audit.name %><small class="text-muted ml-1">账号<% if (!audit.uid) { %>组<% } %></small></li>
+                                <% } %>
+                                    <!--<li class="border-bottom p-2 mb-0"><a href="" class="text-danger pull-right"><i class="fa fa-remove text-danger"></i></a>梁琪波<small class="text-muted ml-1">账号</small></li>-->
+                            </ul>
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">关闭</button>
+                <!--<button type="button" class="btn btn-sm btn-primary">确定</button>-->
+            </div>
+        </div>
+    </div>
+</div>
+<script>
+    $(function () {
+        let timer = null
+        let oldSearchVal = null
+        const accountGroup = JSON.parse(unescape('<%- escape(JSON.stringify(accountGroup)) %>'));
+        const accountList = JSON.parse(unescape('<%- escape(JSON.stringify(accountList)) %>'));
+        const cur_uid = parseInt(<%- ctx.session.sessionUser.accountId %>);
+        $('#gr-search').bind('input propertychange', function(e) {
+            oldSearchVal = e.target.value
+            timer && clearTimeout(timer)
+            timer = setTimeout(() => {
+                const newVal = $('#gr-search').val()
+                let html = ''
+                if (newVal && newVal === oldSearchVal) {
+                    accountList.filter(item => item && cur_uid !== item.id && (item.name.indexOf(newVal) !== -1 || (item.mobile && item.mobile.indexOf(newVal) !== -1))).forEach(item => {
+                        html += `<dd class="border-bottom p-2 mb-0 " data-id="${item.id}" >
+                        <p class="mb-0 d-flex"><span class="text-primary">${item.name}</span><span
+                                class="ml-auto">${item.mobile || ''}</span></p>
+                        <span class="text-muted">${item.role || ''}</span>
+                    </dd>`
+                    })
+                    $('.book-list').empty()
+                    $('.book-list').append(html)
+                } else {
+                    if (!$('.acc-btn').length) {
+                        accountGroup.forEach((group, idx) => {
+                            if (!group) return
+                            html += `<dt><a href="javascript: void(0);" class="acc-btn" data-groupid="${idx}" data-type="hide"><i class="fa fa-plus-square"></i>
+                        </a> ${group.groupName}</dt>
+                        <div class="dd-content" data-toggleid="${idx}">`
+                            group.groupList.forEach(item => {
+                                if (item.id !== cur_uid) {
+                                    html += `<dd class="border-bottom p-2 mb-0 " data-id="${item.id}" >
+                                    <p class="mb-0 d-flex"><span class="text-primary">${item.name}</span><span
+                                            class="ml-auto">${item.mobile || ''}</span></p>
+                                    <span class="text-muted">${item.role || ''}</span>
+                                </dd>`
+                                }
+                            });
+                            html += '</div>'
+                        })
+                        $('.book-list').empty()
+                        $('.book-list').append(html)
+                    }
+                }
+            }, 400);
+        });
+        // 添加到成员中
+        $('.book-list').on('click', 'dt', function () {
+            const idx = $(this).find('.acc-btn').attr('data-groupid')
+            const type = $(this).find('.acc-btn').attr('data-type')
+            if (type === 'hide') {
+                $(this).parent().find(`div[data-toggleid="${idx}"]`).show(() => {
+                    $(this).children().find('i').removeClass('fa-plus-square').addClass('fa-minus-square-o')
+                    $(this).find('.acc-btn').attr('data-type', 'show')
+
+                })
+            } else {
+                $(this).parent().find(`div[data-toggleid="${idx}"]`).hide(() => {
+                    $(this).children().find('i').removeClass('fa-minus-square-o').addClass('fa-plus-square')
+                    $(this).find('.acc-btn').attr('data-type', 'hide')
+                })
+            }
+            return false
+        });
+        // 添加到成员中
+        $('dl').on('click', 'dd', function () {
+            const id = parseInt($(this).data('id'));
+            const groupid = parseInt($(this).data('groupid'));
+            if (!isNaN(id) && id !== 0) {
+                postData('/setting/datacollect/save', { type: 'add-audit', id }, function (result) {
+                    setList(result);
+                })
+            } else if (!isNaN(groupid) && groupid !== 0) {
+                postData('/setting/datacollect/save', { type: 'add-group', id: groupid }, function (result) {
+                    setList(result);
+                })
+            }
+        });
+        // 删除审批人
+        $('body').on('click', '#audit-list li>a', function () {
+            console.log($(this).attr('data-id'), $(this).attr('data-type'));
+            if ($(this).attr('data-type') === 'audit') {
+                postData('/setting/datacollect/save', { type: 'del-audit', id: $(this).attr('data-id') }, function (result) {
+                    setList(result);
+                })
+            } else {
+                postData('/setting/datacollect/save', { type: 'del-group', id: $(this).attr('data-id') }, function (result) {
+                    setList(result);
+                })
+            }
+        });
+
+        function setList(datas) {
+            let list = '';
+            for (const audit of datas) {
+                list += '<li class="border-bottom p-2 mb-0"><a href="javascript:void(0)" class="text-danger pull-right" data-id="'+ audit.id +'"' + ' data-type="'+ (audit.uid ? 'audit' : 'group') +'">' +
+                    '<i class="fa fa-remove text-danger remove-audit"></i></a>'+ audit.name +'<small class="text-muted ml-1">账号'+ (!audit.uid ? '组' : '') +'</small></li>';
+            }
+            $('#audit-list').html(list);
+        }
+    })
+</script>

+ 19 - 0
app/view/stage/index.ejs

@@ -555,6 +555,20 @@
                     </div>
                     <div id="sum-load-miss" class="tab-pane tab-select-show">
                     </div>
+                    <div id="shoufangdan" class="tab-pane tab-select-show">
+                        <div class="sjs-bar-3 d-flex">
+                            <span>本期收方单</span>
+                            <!--所有附件 翻页-->
+                            <span id="shoufang-showPage" class="ml-auto"><a href="javascript:void(0);" class="shoufang-page-select ml-3" content="pre"><i class="fa fa-chevron-left"></i></a> <span id="shoufang-currentPage">1</span>/<span id="shoufang-totalPage">10</span> <a href="javascript:void(0);" class="shoufang-page-select mr-3" content="next"><i class="fa fa-chevron-right"></i></a></span>
+                        </div>
+                        <div class="sjs-height-3" style="overflow:auto; overflow-x:hidden;">
+                            <table class="table table-sm table-bordered table-hover" style="word-break:break-all; table-layout: fixed">
+                                <tr><th>清单 / 计量单元</th><th width="40">附件</th><th width="100">收方单</th></tr>
+                                <tbody id="shoufang-table" class="list-table">
+                                </tbody>
+                            </table>
+                        </div>
+                    </div>
                 </div>
             </div>
         </div>
@@ -588,6 +602,9 @@
                 <li class="nav-item">
                     <a class="nav-link" content="#sum-load-miss" id="sum-load-miss-tab" href="javascript: void(0);" style="display: none;">导入信息</a>
                 </li>
+                <li class="nav-item">
+                    <a class="nav-link" content="#shoufangdan" id="shoufang-tab" href="javascript: void(0);">收方单</a>
+                </li>
             </ul>
         </div>
     </div>
@@ -629,6 +646,8 @@
     const curAuditor = JSON.parse('<%- JSON.stringify(curAuditor) %>');
     const thirdParty = JSON.parse('<%- JSON.stringify(thirdParty) %>');
     const coopwd = <%- coopwd %>;
+    const sfAttDelPower = <%- sfAttDelPower %>;
+    let sfData = JSON.parse(unescape('<%- escape(JSON.stringify(sfData)) %>'));
 </script>
 <style>
 

+ 63 - 0
app/view/stage/modal.ejs

@@ -517,6 +517,69 @@
         </div>
     </div>
 </div>
+<!--生成收方单-->
+<div class="modal fade" id="addshoufang" data-backdrop="static">
+    <div class="modal-dialog" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">生成收方单</h5>
+            </div>
+            <div class="modal-body">
+                <p id="shoufang-ledger">清单:103-6-e 土方回填</p>
+                <p id="shoufang-pos">计量单元:0#桩</p>
+                <p>生成收方单</p>
+            </div>
+            <div class="modal-footer">
+                <input type="hidden" id="shoufang-lid" />
+                <input type="hidden" id="shoufang-pid" />
+                <button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">关闭</button>
+                <button id="setshoufang" class="btn btn-sm btn-primary" data-dismiss="modal">确定</button>
+            </div>
+        </div>
+    </div>
+</div>
+<!--收方单附件-->
+<div class="modal fade" id="shoufangfile" data-backdrop="static">
+    <div class="modal-dialog" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">收方单附件</h5>
+            </div>
+            <div class="modal-body modal-height-300" style="overflow: auto">
+                <table class="table table-bordered">
+                    <tr><th>文件名</th><th>上传时间</th><th>下载</th></tr>
+                    <tbody id="shoufang-flie-list">
+
+                    </tbody>
+                    <!--<tr><td><a href="">20291029173121.png</a></td><td>2021-08-03</td><td><a href=""><i class="fa fa-download"></i></a></td></tr>-->
+                </table>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">关闭</button>
+            </div>
+        </div>
+    </div>
+</div>
+<!--删除收方单-->
+<div class="modal fade" id="shoufangdelete" data-backdrop="static">
+    <div class="modal-dialog" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">删除收方单</h5>
+            </div>
+            <div class="modal-body">
+                <p id="del-shoufang-ledger">清单:103-6-e 土方回填</p>
+                <p id="del-shoufang-pos">计量单元:0#桩</p>
+                <p class="text-danger">确认删除?</p>
+            </div>
+            <div class="modal-footer">
+                <input type="hidden" id="del-shoufang-id" />
+                <button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">取消</button>
+                <button id="delshoufang" type="button" class="btn btn-sm btn-danger">确定删除</button>
+            </div>
+        </div>
+    </div>
+</div>
 <% include ./audit_modal.ejs %>
 <% include ../shares/merge_peg_modal.ejs %>
 <% include ../shares/check_modal2.ejs %>

+ 174 - 0
app/view/wap/shoufangupload.ejs

@@ -0,0 +1,174 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="utf-8">
+    <meta name="viewport" content="initial-scale=1, maximum-scale=1,user-scalable=no">
+    <meta http-equiv="x-ua-compatible" content="ie=edge">
+    <title>收方单附件上传-计量支付</title>
+    <link rel="stylesheet" href="/public/css/bootstrap/bootstrap.min.css">
+    <link rel="stylesheet" href="/public/css/wap/main.css">
+    <link rel="stylesheet" href="/public/css/toast.css">
+    <link rel="stylesheet" href="/public/css/font-awesome/font-awesome.min.css">
+    <link rel="stylesheet" href="/public/css/toastr.css">
+    <link rel="shortcut icon" href="/public/images/favicon.ico">
+</head>
+<body>
+<div class="container">
+    <!--顶部-->
+    <nav class="fixed-top bg-dark">
+        <div class="my-2 d-flex justify-content-between">
+            <span class="text-white ml-3">收方单附件上传</span>
+            <div class="mr-3">
+            </div>
+        </div>
+    </nav>
+    <!--标段列表-->
+    <div class="py-5">
+        <table class="table table-bordered">
+            <tbody>
+            <tr>
+                <th>标段</th>
+                <td><%- tender.name %></td>
+            </tr>
+            <tr>
+                <th>计量期</th>
+                <td>第 <%- order %> 期</td>
+            </tr>
+            <tr>
+                <th>台帐清单/计量单元</th>
+                <td><%- name %></td>
+            </tr>
+            </tbody>
+        </table>
+        <table class="table table-bordered">
+            <tr><th colspan="2" class="text-center">本次已上传文件</th></tr>
+            <tbody id="file-list">
+            </tbody>
+            <!--<tr><td><a href="">202108098762.png</a></td><td><a href="" class="text-danger">删除</a></td></tr>-->
+        </table>
+    </div>
+    <!--底栏菜单-->
+    <nav class="fixed-bottom navbar-dark bg-light border-top">
+        <ul class="nav nav-fill my-2">
+            <li class="nav-item">
+                <a class="nav-link" href="JavaScript:void(0);"><i class="fa fa-camera"></i> 拍照</a>
+                <input id="uploadPhoto" type="file" accept="image/*" capture="camera" style="display: none">
+            </li>
+            <li class="nav-item">
+                <a class="nav-link" href="JavaScript:void(0);"><i class="fa fa-upload"></i> 上传文件</a>
+                <input id="uploadFile" multiple type="file" style="display: none">
+            </li>
+        </ul>
+    </nav>
+</div>
+
+    <!-- JS. -->
+<script src="/public/js/jquery/jquery-3.2.1.min.js"></script>
+<script src="/public/js/popper/popper.min.js"></script>
+<script src="/public/js/bootstrap/bootstrap.min.js"></script>
+<script src="/public/js/cookies.js"></script>
+<script src="/public/js/toastr.min.js"></script>
+<script src="/public/js/wap/global.js"></script>
+<script type="text/javascript">
+    toastr.options = {
+        "closeButton": false,
+        "debug": false,
+        "newestOnTop": false,
+        "progressBar": false,
+        "positionClass": "toast-top-center",
+        "preventDuplicates": false,
+        "onclick": null,
+        "showDuration": "300",
+        "hideDuration": "1000",
+        "timeOut": "3000",
+        "extendedTimeOut": "1000",
+        "showEasing": "swing",
+        "hideEasing": "linear",
+        "showMethod": "fadeIn",
+        "hideMethod": "fadeOut"
+    };
+</script>
+<script>
+    const whiteList = JSON.parse('<%- JSON.stringify(whiteList) %>');
+    $(function () {
+        $('.nav-link').on('click', function () {
+            $(this).siblings('input').click();
+        });
+        $('#uploadPhoto').change(function () {
+            const files = this.files;
+            const formData = new FormData();
+            formData.append('sfid', <%- sfid %>);
+            for (const file of files) {
+                if (file === undefined) {
+                    toastr.error('未选择上传文件!');
+                    return false;
+                }
+                const filesize = file.size;
+                if (filesize > 30 * 1024 * 1024) {
+                    toastr.error('存在上传文件大小过大!');
+                    return false;
+                }
+                const ext = file.name.toLowerCase().split('.').splice(-1)[0];
+                const imgStr = /(jpg|jpeg|png|bmp|BMP|JPG|PNG|JPEG)$/;
+                if (!imgStr.test(ext)) {
+                    toastr.error('请上传正确的图片格式文件');
+                    return
+                }
+                formData.append('size', filesize);
+                formData.append('file[]', file);
+            }
+            upload(formData);
+            $('#uploadPhoto').val('');
+        });
+        $('#uploadFile').change(function () {
+            const files = this.files;
+            const formData = new FormData();
+            formData.append('sfid', <%- sfid %>);
+            for (const file of files) {
+                if (file === undefined) {
+                    toastr.error('未选择上传文件!');
+                    return false;
+                }
+                const filesize = file.size;
+                if (filesize > 30 * 1024 * 1024) {
+                    toastr.error('存在上传文件大小过大!');
+                    return false;
+                }
+                const fileext = '.' + file.name.toLowerCase().split('.').splice(-1)[0];
+                if (whiteList.indexOf(fileext) === -1) {
+                    toastr.error('只能上传指定格式的附件!');
+                    return false;
+                }
+                formData.append('size', filesize);
+                formData.append('file[]', file);
+            }
+            upload(formData);
+            $('#uploadFile').val('');
+        });
+
+        $('body').on('click', '.del-att', function () {
+            const id = $(this).data('id');
+            const _self = $(this);
+            const data = { id };
+            postData('/wap/shoufang/delfile', data, function (result) {
+                _self.parents('tr').remove();
+            });
+        })
+
+        function upload(formData) {
+            if (formData.length < 1) {
+                return;
+            }
+            postDataWithFile('/wap/shoufang/upfile', formData, function (result) {
+                let html = '';
+                for (const att of result) {
+                    html += `<tr><td><a href="${att.filepath}" target="_blank">${att.filename}${att.fileext}</a></td><td><a href="javascript:void(0);" data-id="${att.id}" class="del-att text-danger">删除</a></td></tr>`;
+                }
+                $('#file-list').append(html);
+            });
+        }
+    })
+
+</script>
+</body>
+</html>

+ 14 - 0
config/menu.js

@@ -17,6 +17,14 @@ const menu = {
         children: null,
         caption: '待办',
     },
+    datacollect: {
+        name: '决策大屏',
+        icon: 'fa-pie-chart',
+        display: false,
+        url: '/datacollect',
+        children: null,
+        caption: '决策大屏',
+    },
     tender: {
         name: '标段管理',
         icon: 'fa-list-ul',
@@ -291,6 +299,12 @@ const settingMenu = {
         url: '/setting/logs',
         caption: '操作日志',
     },
+    datacollect: {
+        name: '决策大屏',
+        display: false,
+        url: '/setting/datacollect',
+        caption: '决策大屏',
+    },
 };
 
 const profileMenu = {

+ 9 - 0
config/web.js

@@ -806,6 +806,15 @@ const JsFiles = {
                 mergeFile: 'information',
             },
         },
+        datacollect: {
+            index: {
+                files: ['/public/js/echarts/echarts.min.js', '/public/js/decimal.min.js', '/public/js/bootstrap/bootstrap-colorpicker.min.js', '/public/js/table/tableSlipe.js'],
+                mergeFiles: [
+                    '/public/js/zh_calc.js',
+                ],
+                mergeFile: 'index',
+            },
+        },
     },
 };
 

+ 49 - 1
sql/update.sql

@@ -115,5 +115,53 @@ CREATE TABLE `zh_s2b_z_tender` (
 
 ALTER TABLE `zh_project` ADD `customType` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '定制项目通知类型参数' AFTER `secret`;
 
-ALTER TABLE `calculation`.`zh_tender_info` 
+ALTER TABLE `calculation`.`zh_tender_info`
 ADD COLUMN `fun_rela` varchar(255) CHARACTER SET utf8 NULL COMMENT '功能设置' AFTER `ledger_check`;
+
+ALTER TABLE `zh_project` ADD `data_collect` TINYINT(1) NOT NULL DEFAULT '0' COMMENT '决策大屏是否显示及对应大屏编号' AFTER `sjs_rela`;
+
+CREATE TABLE `zh_datacollect_audit` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `pid` int(11) NOT NULL COMMENT '项目id',
+  `groupid` int(11) DEFAULT NULL COMMENT '用户组id',
+  `uid` int(11) DEFAULT NULL COMMENT '用户id',
+  `create_time` datetime NOT NULL COMMENT '添加时间',
+   PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='决策大屏用户查看权限表';
+
+ALTER TABLE `zh_material_bills` ADD `origin` VARCHAR(255) NULL DEFAULT NULL COMMENT '来源地' AFTER `pre_tp`;
+ALTER TABLE `zh_material_bills_history` ADD `origin` VARCHAR(255) NULL DEFAULT NULL COMMENT '来源地' AFTER `pre_tp`;
+
+ALTER TABLE `zh_material_bills` ADD `order` INT NULL DEFAULT NULL COMMENT '排序' AFTER `mid`;
+-- 按id排序赋值
+UPDATE `zh_material_bills` SET `order`=`id`
+
+-- 收方单表
+CREATE TABLE `zh_stage_shoufang`  (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `tid` int(11) NOT NULL COMMENT '标段id',
+  `order` tinyint(4) NOT NULL COMMENT '期数',
+  `sid` int(11) NOT NULL COMMENT '期id',
+  `lid` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '台账uuid',
+  `pid` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '计量单元uuid',
+  `qrcode` VARCHAR(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '二维码存放地址',
+  `create_time` datetime NOT NULL COMMENT '创建时间',
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT = '收方单数据';
+
+-- 收方单附件表
+CREATE TABLE `zh_stage_shoufang_attachment`  (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `tid` int(11) NOT NULL COMMENT '标段id',
+  `sid` int(11) NOT NULL COMMENT '期id',
+  `sfid` int(11) NOT NULL COMMENT '收方单id',
+  `filename` varchar(1000) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '文件名称',
+  `fileext` varchar(10) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '文件后缀',
+  `filesize` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '文件大小',
+  `filepath` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '文件存储路径',
+  `in_time` datetime NOT NULL COMMENT '创建时间',
+  PRIMARY KEY (`id`),
+  INDEX `sfid`(`sfid`),
+  INDEX `sid`(`sid`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT = '收方单附件表';
+