Browse Source

Merge branch 'dev' of http://192.168.1.41:3000/maixinrong/Calculation into dev

Tony Kang 1 week ago
parent
commit
8358a557b4

+ 1 - 1
app/controller/quality_controller.js

@@ -786,7 +786,7 @@ module.exports = app => {
                         if (data.update.rectification_date !== undefined && data.update.rectification_date === '') {
                             throw '请填写整改日期';
                         }
-                        const fields = ['id', 'check_item', 'check_situation', 'action', 'check_date', 'inspector', 'rectification_item', 'rectification_date'];
+                        const fields = ['id', 'code', 'check_item', 'check_situation', 'action', 'check_date', 'inspector', 'rectification_item', 'rectification_date'];
                         if (!this.checkFieldExists(data.update, fields)) {
                             throw '参数有误';
                         }

+ 15 - 1
app/controller/safe_controller.js

@@ -719,6 +719,15 @@ module.exports = app => {
                         }
                         reponseData.data = await ctx.service.safeInspection.add(ctx.tender.id, ctx.session.sessionUser.accountId, data.code, data.check_item, data.check_date);
                         break;
+                    case 'addByWap':
+                        if (!data.code || data.code === '') {
+                            throw '编号不能为空';
+                        }
+                        if (!data.check_item || !data.check_date) {
+                            throw '请填写检查项';
+                        }
+                        reponseData.data = await ctx.service.safeInspection.addByWap(ctx.tender.id, ctx.session.sessionUser.accountId, data.code, data.check_item, data.check_date);
+                        break;
                     default:throw '参数有误';
                 }
                 ctx.body = reponseData;
@@ -786,6 +795,11 @@ module.exports = app => {
                         if (!(!ctx.inspection.readOnly || ctx.inspection.rectificationPower)) {
                             throw '当前状态不可修改';
                         }
+                        if (data.update.code !== undefined) {
+                            if (data.update.code === '') throw '巡检编号不能为空';
+                            const codeExist = await ctx.service.safeInspection.getDataByCondition({ code: data.update.code, tid: ctx.tender.id });
+                            if (codeExist) throw '该巡检编号已存在';
+                        }
                         if (data.update.check_item !== undefined && data.update.check_item === '') {
                             throw '检查项不能为空';
                         }
@@ -798,7 +812,7 @@ module.exports = app => {
                         if (data.update.rectification_date !== undefined && data.update.rectification_date === '') {
                             throw '请填写整改日期';
                         }
-                        const fields = ['id', 'check_item', 'check_situation', 'action', 'check_date', 'inspector', 'rectification_item', 'rectification_date'];
+                        const fields = ['id', 'code', 'check_item', 'check_situation', 'action', 'check_date', 'inspector', 'rectification_item', 'rectification_date'];
                         if (!this.checkFieldExists(data.update, fields)) {
                             throw '参数有误';
                         }

+ 1 - 1
app/controller/spss_controller.js

@@ -172,7 +172,7 @@ module.exports = app => {
         async _loadStagesTpData(info, tender, stages, preStage, endStage) {
             if (preStage) {
                 info.pre_contract_tp = this.ctx.helper.sum([preStage.contract_tp, preStage.pre_contract_tp, preStage.contract_pc_tp]);
-                info.pre_contract_tp = this.ctx.helper.sum([preStage.qc_tp, preStage.pre_qc_tp, preStage.qc_pc_tp]);
+                info.pre_qc_tp = this.ctx.helper.sum([preStage.qc_tp, preStage.pre_qc_tp, preStage.qc_pc_tp]);
                 info.pre_yf_tp = this.ctx.helper.add(preStage.yf_tp, preStage.pre_yf_tp);
                 info.pre_sf_tp = this.ctx.helper.add(preStage.sf_tp, preStage.pre_sf_tp);
             }

+ 7 - 1
app/controller/template_controller.js

@@ -97,7 +97,13 @@ module.exports = app => {
                 for (const f of filter) {
                     switch(f) {
                         case 'detail':
-                            result[f] = await this.ctx.service.calcTmpl.getTemplate(data.id, data.type);
+                            const detail = await this.ctx.service.calcTmpl.getTemplate(data.id, data.type);
+                            const user = await this.ctx.service.projectAccount.getDataById(detail.create_user_id);
+                            detail.user_name = user.name;
+                            detail.user_company = user.company;
+                            detail.user_role = user.role;
+                            detail.create_time_str = this.ctx.moment(detail.create_time).format('YYYY-MM-DD HH:mm:ss');
+                            result[f] = detail;
                             break;
                         default:
                             throw '未知数据类型';

+ 240 - 2
app/controller/wap_controller.js

@@ -14,6 +14,8 @@ const tenderConst = require('../const/tender');
 const changeConst = require('../const/change');
 const advanceConst = require('../const/advance');
 const materialConst = require('../const/material');
+const shenpiConst = require('../const/shenpi');
+const codeRuleConst = require('../const/code_rule');
 const fs = require('fs');
 const path = require('path');
 const sendToWormhole = require('stream-wormhole');
@@ -23,6 +25,7 @@ const AiInspect = require('../lib/ai_inspect');
 const uuid = require('node-uuid');
 const nlsToken = require('../lib/nls_token');
 const streamToArray = require('stream-to-array');
+const PermissionCheck = require('../const/account_permission').PermissionCheck;
 
 module.exports = app => {
 
@@ -247,6 +250,45 @@ module.exports = app => {
                     }
                 }
             }
+            // 获取待审批的质量巡检单
+            const auditQualityInspections = [];
+            const qualityInspectionList = await ctx.service.qualityInspectionAudit.getAuditInspectionByWap(ctx.session.sessionUser.accountId);
+            for (const audit of qualityInspectionList) {
+                let sp = null;
+                if (audit.spid) {
+                    if (ctx.helper._.findIndex(subProjects, { id: audit.spid }) !== -1) {
+                        sp = ctx.helper._.find(subProjects, { id: audit.spid });
+                    } else {
+                        sp = await ctx.service.subProject.getDataById(audit.spid);
+                        subProjects.push(sp);
+                    }
+                }
+                if (sp) {
+                    const pageShow = JSON.parse(sp.page_show);
+                    if (pageShow.qualityInspection) {
+                        auditQualityInspections.push(audit);
+                    }
+                }
+            }
+            const auditSafeInspections = [];
+            const safeInspectionList = await ctx.service.safeInspectionAudit.getAuditInspectionByWap(ctx.session.sessionUser.accountId);
+            for (const audit of safeInspectionList) {
+                let sp = null;
+                if (audit.spid) {
+                    if (ctx.helper._.findIndex(subProjects, { id: audit.spid }) !== -1) {
+                        sp = ctx.helper._.find(subProjects, { id: audit.spid });
+                    } else {
+                        sp = await ctx.service.subProject.getDataById(audit.spid);
+                        subProjects.push(sp);
+                    }
+                }
+                if (sp) {
+                    const pageShow = JSON.parse(sp.page_show);
+                    if (pageShow.safeInspection) {
+                        auditSafeInspections.push(audit);
+                    }
+                }
+            }
             const renderData = {
                 auditStages,
                 auditChanges,
@@ -256,6 +298,8 @@ module.exports = app => {
                 auditMaterials,
                 auditRevise,
                 auditAdvance,
+                auditQualityInspections,
+                auditSafeInspections,
                 changeConst,
                 advanceConst,
                 tpUnit: 2,
@@ -287,10 +331,19 @@ module.exports = app => {
         async list(ctx) {
             try {
                 // 获取用户新建标段权利
+                const type = ctx.params.type || '';
+                let tenderList = '';
                 const accountInfo = await this.ctx.service.projectAccount.getDataById(ctx.session.sessionUser.accountId);
                 const userPermission = accountInfo !== undefined && accountInfo.permission !== '' ? JSON.parse(accountInfo.permission) : null;
-
-                const tenderList = await this.ctx.service.tender.getBuildList('', userPermission);
+                if (type) {
+                    if (type === 'safe' && !ctx.subProject.page_show.safeInspection) throw '该功能已关闭';
+                    if (type === 'quality' && !ctx.subProject.page_show.qualityInspection) throw '该功能已关闭';
+                    const permission = type === 'quality' ? 'inspection' : type === 'safe' ? 'safe_inspection' : '';
+                    if (!permission) throw '参数错误';
+                    tenderList = await ctx.service.tender.getSpecList(ctx.service.tenderPermission, permission, ctx.session.sessionUser.is_admin ? 'all' : '');
+                } else {
+                    tenderList = await this.ctx.service.tender.getBuildList('', userPermission);
+                }
                 for (const t of tenderList) {
                     await this.ctx.service.tenderCache.loadTenderCache(t, this.ctx.session.sessionUser.accountId);
                 }
@@ -304,6 +357,7 @@ module.exports = app => {
                     valuations,
                     uid: this.ctx.session.sessionUser.accountId,
                     pid: this.ctx.session.sessionProject.id,
+                    type,
                 };
                 await ctx.render('wap/list.ejs', renderData);
             } catch (err) {
@@ -1181,12 +1235,196 @@ module.exports = app => {
             }
         }
 
+        /**
+         * 巡检列表页
+         *
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
         async inspection(ctx) {
+            try {
+                const type = ctx.params.type || '';
+                if (type !== 'safe' && type !== 'quality') {
+                    throw '参数有误';
+                }
+                const tableName = type + 'Inspection';
+                // 变更令列表
+                const inspections = await ctx.service[tableName].getListByStatus(ctx.tender.id, 0, 0);
+
+                for (const c of inspections) {
+                    c.showApprovalBtn = false;
+                    c.curAuditors = await ctx.service[tableName + 'Audit'].getAuditorsByStatus(c.id, c.status, c.times);
+                    if ((c.status === auditConst.inspection.status.uncheck || c.status === auditConst.inspection.status.checkNo) && c.uid === ctx.session.sessionUser.accountId) {
+                        c.showApprovalBtn = true;
+                    } else if (c.status === auditConst.inspection.status.checkNoPre) {
+                        c.curAuditors2 = await ctx.service[tableName + 'Audit'].getAuditorsByStatus(c.id, auditConst.inspection.status.checking, c.times);
+                        const curAudit = c.curAuditors2.find(function(x) {
+                            return x.aid === ctx.session.sessionUser.accountId;
+                        });
+                        if (curAudit && (curAudit.status === auditConst.inspection.status.checking || curAudit.status === auditConst.inspection.status.rectification)) {
+                            c.showApprovalBtn = true;
+                        }
+                    } else {
+                        const curAudit = c.curAuditors.find(function(x) {
+                            return x.aid === ctx.session.sessionUser.accountId;
+                        });
+                        if (curAudit && (curAudit.status === auditConst.inspection.status.checking || curAudit.status === auditConst.inspection.status.rectification)) {
+                            c.showApprovalBtn = true;
+                        }
+                    }
+                }
+
+                const renderData = {
+                    tender: ctx.tender.data,
+                    inspections,
+                    auditInspectionConst: auditConst.inspection,
+                    auditType: auditConst.auditType,
+                    type,
+                    moment,
+                    permission: type === 'quality' ? ctx.permission.inspection : ctx.permission.safe_inspection,
+                };
+                await ctx.render('wap/shenpi_inspection_index.ejs', renderData);
+            } catch (err) {
+                console.log(err);
+                this.log(err);
+                ctx.redirect('/wap/sp/' + ctx.subProject.id + '/' + ctx.params.type + '/inspection');
+            }
+        }
+
+        /**
+         * 巡检审批详细页
+         *
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        async qualityInspectionInformation(ctx) {
+            const type = 'quality';
+            await this._inspectionInformation(ctx, type);
+        }
+
+        async safeInspectionInformation(ctx) {
+            const type = 'safe';
+            await this._inspectionInformation(ctx, type);
+        }
+
+        async _inspectionInformation(ctx, type) {
+            try {
+                const whiteList = this.ctx.app.config.multipart.whitelist;
+                const tender = ctx.tender.data;
+                const inspection = ctx.inspection;
+                await ctx.service[type + 'Inspection'].loadAuditViewData(ctx.inspection);
+                // 获取附件列表
+                const fileList = await ctx.service[type + 'InspectionAtt'].getAllAtt(ctx.tender.id, ctx.inspection.id);
+                const renderData = {
+                    moment,
+                    tender,
+                    inspection,
+                    fileList,
+                    whiteList,
+                    deleteFilePermission: PermissionCheck.delFile(this.ctx.session.sessionUser.permission),
+                    auditConst: auditConst.inspection,
+                    shenpiConst,
+                    tpUnit: ctx.tender.info.decimal.tp,
+                    auditType,
+                    type,
+                    preUrl: `/sp/${ctx.subProject.id}/${type}/tender/${ctx.tender.id}/inspection/${ctx.inspection.id}/information`,
+                };
+                const accountList = await ctx.service.projectAccount.getAllSubProjectAccount(ctx.subProject);
+                renderData.accountList = accountList;
+                const unitList = await ctx.service.constructionUnit.getAllDataByCondition({ where: { pid: ctx.session.sessionProject.id } });
+                renderData.accountGroup = unitList.map(item => {
+                    const groupList = accountList.filter(item1 => item1.company === item.name);
+                    return { groupName: item.name, groupList };
+                }).filter(x => { return x.groupList.length > 0; });
+                await ctx.render('wap/shenpi_inspection.ejs', renderData);
+            } catch (err) {
+                this.log(err);
+                ctx.redirect('/wap/sp/' + ctx.subProject.id + '/' + type + '/inspection');
+            }
+        }
+
+        async inspectionAdd(ctx) {
+            const type = ctx.params.type || '';
+            if (type !== 'safe' && type !== 'quality') {
+                throw '参数有误';
+            }
+            const tenderData = await ctx.service.tender.getDataById(ctx.tender.id);
+            const codeType = type === 'quality' ? codeRuleConst.ruleString[codeRuleConst.ruleType.inspection] : codeRuleConst.ruleString[codeRuleConst.ruleType.safe_inspection];
+            const c_code_rules = tenderData.c_code_rules !== null ? JSON.parse(tenderData.c_code_rules) : null;
+            const cCodeRule = c_code_rules && c_code_rules[codeType + '_rule'] ? c_code_rules[codeType + '_rule'] : [
+                { rule_type: 2, text: (type === 'quality' ? '质量' : '安全') + '巡检' + moment().format('YYYYMMDD') },
+                { rule_type: 4, format: 3, start: 1 },
+            ];
+            const cConnector = c_code_rules && c_code_rules[codeType + '_connector'] ? c_code_rules[codeType + '_connector'] : 3;
+            const changeCount = await ctx.service[type + 'Inspection'].count({ tid: ctx.tender.id });
+            const code = [];
+            for (const rule of cCodeRule) {
+                switch (rule.rule_type) {
+                    case codeRuleConst.measure.ruleType.dealCode:
+                        code.push(ctx.tender.info.deal_info.dealCode);
+                        break;
+                    case codeRuleConst.measure.ruleType.tenderName:
+                        code.push(tenderData.name);
+                        break;
+                    case codeRuleConst.measure.ruleType.text:
+                        code.push(rule.text);
+                        break;
+                    case codeRuleConst.measure.ruleType.inDate:
+                        code.push(moment().format('YYYY'));
+                        break;
+                    case codeRuleConst.measure.ruleType.addNo:
+                        let s = '0000000000';
+                        const count = rule.start + changeCount;
+                        s = s + count;
+                        code.push(s.substr(s.length - rule.format));
+                        break;
+                    default: break;
+                }
+            }
+            const newCode = code.join(cConnector !== null && parseInt(cConnector) !== 3 ? codeRuleConst.measure.connectorString[cConnector] : '');
             const renderData = {
+                newCode,
+                tender: ctx.tender.data,
+                type,
             };
             await ctx.render('wap/inspection.ejs', renderData);
         }
 
+        /**
+         * 新增变更申请 (Post)
+         *
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        async inspectionSave(ctx) {
+            try {
+                const type = ctx.params.type || '';
+                if (type !== 'safe' && type !== 'quality') {
+                    throw '参数有误';
+                }
+                const data = JSON.parse(ctx.request.body.data);
+                const reponseData = {
+                    err: 0, msg: '', data: {},
+                };
+                switch (data.type) {
+                    case 'addByWap':
+                        if (!data.insert || !data.insert.code || data.insert.code === '') {
+                            throw '巡检编号不能为空';
+                        }
+                        if (!data.insert.check_item) {
+                            throw '检查项不能为空';
+                        }
+                        reponseData.data = await ctx.service[type + 'Inspection'].addByWap(ctx.tender.id, data.insert);
+                        break;
+                    default:throw '参数有误';
+                }
+                ctx.body = reponseData;
+            } catch (err) {
+                this.log(err);
+                ctx.body = { err: 1, msg: err.toString() };
+            }
+        }
+
         async inspectionAiAsk(ctx) {
             const responseData = {
                 err: 0,

+ 1 - 1
app/lib/ai_inspect.js

@@ -9,7 +9,7 @@ class AiInspect {
      * @param {string} config.chatflowId - ChatFlow ID
      * @param {string} [config.baseUrl=https://api.dify.ai/v1] - Dify API 接口地址
      */
-    constructor({ apiKey = 'app-vc7cLDuIRJngvEgQUMBqT3SX', chatflowId = '', baseUrl = 'http://workflow.smartcost.com.cn/v1' }) {
+    constructor({ apiKey = 'app-fiJr1pNxjdKXpetko8ANawnY', chatflowId = '', baseUrl = 'http://workflow.smartcost.com.cn/v1' }) {
         if (!apiKey) {
             throw new Error('apiKey 不能为空');
         }

+ 20 - 0
app/public/css/wap/main.css

@@ -26,6 +26,26 @@ input.form-control[readonly],textarea.form-control[readonly] {
 	background: #f1f1f1;
 
 }
+.book-list{
+    padding: 0;
+    margin: 0;
+    height: 285px;
+    overflow-y: auto;
+}
+.book-list dt{
+    padding:5px 0 5px 5px;
+    background-color: #f2f2f2;
+}
+.book-list dd{
+    padding-left:15px;
+    cursor: pointer;
+}
+.book-list dd:hover{
+    background-color: #f2f2f2
+}
+.dd-content {
+    display: none;
+}
 .bg-new-advance{
     background: rgba(241, 82, 91, 0.08) !important;
 }

+ 2 - 0
app/public/js/ledger.js

@@ -1571,6 +1571,7 @@ $(document).ready(function() {
 
     sjsSettingObj.setFxTreeStyle(ledgerSpreadSetting, sjsSettingObj.FxTreeStyle.jz);
     if (thousandth) sjsSettingObj.setTpThousandthFormat(ledgerSpreadSetting);
+    ledgerSpreadSetting.tipsSum = true;
     ledgerTreeCol.initSpreadSetting(ledgerSpreadSetting);
     ledgerSpreadSetting.headColWidth = [50];
     ledgerSpreadSetting.rowHeader = [
@@ -2351,6 +2352,7 @@ $(document).ready(function() {
             },
         },
     ];
+    posSpreadSetting.tipsSum = true;
     SpreadJsObj.initSheet(posSpread.getActiveSheet(), posSpreadSetting);
     //绑定计量单元编辑事件
     const posOperationObj = {

+ 5 - 0
app/public/js/ledger_gather.js

@@ -62,6 +62,7 @@ $(document).ready(() => {
             key: 'ledger-gather-gcl',
             colWidth: true,
         },
+        tipsSum: true,
         getColor: function (sheet, data, row, col, defaultColor) {
             return data
                 ? $('#compare-tag')[0].checked && data.compare_differ
@@ -98,6 +99,7 @@ $(document).ready(() => {
             key: 'ledger-gather-leafXmj',
             colWidth: true,
         },
+        tipsSum: true,
     };
     if (thousandth) sjsSettingObj.setTpThousandthFormat(leafXmjSpreadSetting);
     SpreadJsObj.initSheet(leafXmjSpread.getActiveSheet(), leafXmjSpreadSetting);
@@ -125,6 +127,7 @@ $(document).ready(() => {
             key: 'ledger-gather-leafXmj',
             colWidth: true,
         },
+        tipsSum: true,
     };
     if (thousandth) sjsSettingObj.setTpThousandthFormat(gatherLeafXmjSpreadSetting);
     const gatherLeafXmjSheet = gatherLeafXmjSpread.getActiveSheet();
@@ -154,6 +157,7 @@ $(document).ready(() => {
             key: 'ledger-gather-ancGcl',
             colWidth: true,
         },
+        tipsSum: true,
     };
     if (thousandth) sjsSettingObj.setTpThousandthFormat(gatherAncGclSpreadSetting);
     const gatherAncGclSheet = gatherAncGclSpread.getActiveSheet();
@@ -182,6 +186,7 @@ $(document).ready(() => {
             key: 'ledger-gather-change',
             colWidth: true,
         },
+        tipsSum: true,
     };
     if (thousandth) sjsSettingObj.setTpThousandthFormat(changeSpreadSetting);
     const changeSheet = changeSpread.getActiveSheet();

+ 4 - 0
app/public/js/measure_compare.js

@@ -34,6 +34,7 @@ const billsSpreadSetting = {
     defaultRowHeight: 21,
     readOnly: true,
     selectedBackColor: '#fffacd',
+    tipsSum: true,
 };
 const posSpreadSetting = {
     baseCols: [
@@ -55,6 +56,7 @@ const posSpreadSetting = {
     font: '12px 微软雅黑',
     readOnly: true,
     selectedBackColor: '#fffacd',
+    tipsSum: true,
 };
 const exportBillsSpreadSetting = {
     baseCols: [
@@ -112,6 +114,7 @@ const gclSpreadSetting = {
     headerFont: '12px 微软雅黑',
     font: '12px 微软雅黑',
     readOnly: true,
+    tipsSum: true,
 };
 const leafXmjSpreadSetting = {
     baseCols: [
@@ -138,6 +141,7 @@ const leafXmjSpreadSetting = {
     headerFont: '12px 微软雅黑',
     font: '12px 微软雅黑',
     readOnly: true,
+    tipsSum: true,
 };
 const exportGclSpreadSetting = {
     baseCols: [

+ 7 - 2
app/public/js/pos_calc_tmpl.js

@@ -289,16 +289,21 @@ $(document).ready(() => {
 
         const loadTemplateDetail = async function(template) {
             const result = await postDataAsync('load', {filter: 'detail', id: template.id, type: 'posCalc'});
-            if (result && result.detail) template.col_set = result.detail.col_set;
+            if (result && result.detail) {
+                template.col_set = result.detail.col_set;
+                template.user_name = result.detail.user_name;
+                template.create_time_str = result.detail.create_time_str;
+            }
         };
         const refreshTemplate = async function() {
             if (!curTemplate) {
-                // todo 隐藏模板详细界面
+                $('#detail-user-info').html('');
             } else {
                 $('dd[templateId]').removeClass('bg-warning');
                 $(`dd[templateId=${curTemplate.id}]`).addClass('bg-warning');
                 if (!curTemplate.col_set) await loadTemplateDetail(curTemplate);
                 detailObj.loadDetail(curTemplate);
+                $('#detail-user-info').html(`新建人:${curTemplate.user_name}(${curTemplate.create_time_str})`);
             }
         };
         const setCurTemplate = function(template) {

+ 49 - 9
app/public/js/quality_inspection_information.js

@@ -252,6 +252,7 @@ $(document).ready(function () {
                     checkDate.selectDate(inspection.check_date ? new Date(inspection.check_date) : new Date());
                 } else {
                     $(`#check_table textarea[data-key=${field}]`).val(inspection[field] || '');
+                    $(`#check_table input[data-key=${field}]`).val(inspection[field] || '');
                 }
             });
         }
@@ -343,7 +344,14 @@ $(document).ready(function () {
                 return;
             }
             postData(preUrl + '/save', {type: 'del-inspection' }, function (result) {
+                // 判断url是否是wap,是则跳转到wap页面,否则跳转到pc页面
+                const is_wap = window.location.pathname.indexOf('/wap/') !== -1;
                 let link = `/sp/${spid}/quality/tender/${tender_id}/inspection`;
+                if (is_wap) {
+                    link = `/wap` + link;
+                    window.location.href = link;
+                    return;
+                }
                 let orderSetting = getLocalCache('quality-inspection-'+ tender_id +'-list-order');
                 if (!orderSetting) orderSetting = 'time|desc';
                 const orders = orderSetting.split('|');
@@ -633,15 +641,47 @@ $(document).ready(function () {
             }
             return {...file, showDel}
         })
-        let html = inspection.filePermission ? `<tr><td colspan="5"><a href="#addfujian" data-toggle="modal" class="btn btn-sm btn-light text-primary" data-placement="bottom" title="" data-original-title="上传附件"><i class="fa fa-cloud-upload" aria-hidden="true"></i> 上传附件</a></td></tr>` : '';
-        newFiles.forEach((file, idx) => {
-            if (file.showDel) {
-                html += `<tr><td>${idx + 1}</td><td><a href="${file.filepath}" target="_blank">${file.filename}</a></td><td>${file.username}</td><td>${moment(file.upload_time).format('YYYY-MM-DD HH:mm:ss')}</td><td><a href="${preUrl}/file/${file.id}/download" class="mr-2"><i class="fa fa-download"></i></a><a href="javascript: void(0);" class="text-danger file-del" data-id="${file.id}"><i class="fa fa-remove"></i></a></td></tr>`
-            } else {
-                html += `<tr><td width="70">${idx + 1}</td><td><a href="${file.filepath}" target="_blank">${file.filename}</a></td><td>${file.username}</td><td>${moment(file.upload_time).format('YYYY-MM-DD HH:mm:ss')}</td><td><a href="${preUrl}/file/${file.id}/download" class="mr-2"><i class="fa fa-download"></i></a></td></tr>`
-            }
-        })
-        $('#file-content').append(html);
+        const is_wap = window.location.pathname.indexOf('/wap/') !== -1;
+        if (is_wap) {
+            let html = `<div style="display: flex;">
+            <b>附件</b>`;
+            if (inspection.filePermission) {
+                html += `<span style="margin-left: auto;"><a href="#addfujian" data-toggle="modal" class="btn btn-sm btn-light text-primary" data-placement="bottom" title="" data-original-title="上传附件"><i class="fa fa-cloud-upload" aria-hidden="true"></i> 上传附件</a></span>`;
+            }
+            html += `</div><div>`;
+            newFiles.forEach((file, idx) => {
+                html += `<a href="${file.filepath}" target="_blank">
+                    <div class="card my-1" style="cursor: pointer">
+                        <div class="card-body"><i class="fa fa-file"></i> ${file.filename}<small class="pull-right">${file.showDel ? `<span class="badge">
+                                    <span class="dropdown">
+                                <a href="javascript:void(0)" class="btn-sm text-danger px-1" title="移除" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"><i class="fa fa-remove"></i></a>
+                                <div class="dropdown-menu">
+                                  <a class="dropdown-item" href="javascript:void(0);">确认删除附件?</a>
+                                  <div class="dropdown-divider"></div>
+                                  <div class="px-2 py-1 text-center">
+                                    <a href="javascript: void(0);" class="btn btn-sm btn-danger file-del" data-id="${file.id}">删除</a>
+                                    <button class="btn btn-sm btn-secondary">取消</button>
+                                  </div>
+                                </div>
+                              </span>
+                              </span>` : ''}</small> <small class="pull-right text-secondary">${file.filesize}</small>
+                        </div>
+                    </div>
+                </a>`;
+            });
+            html += '</div>';
+            $('#file-content').append(html);
+        } else {
+            let html = inspection.filePermission ? `<tr><td colspan="5"><a href="#addfujian" data-toggle="modal" class="btn btn-sm btn-light text-primary" data-placement="bottom" title="" data-original-title="上传附件"><i class="fa fa-cloud-upload" aria-hidden="true"></i> 上传附件</a></td></tr>` : '';
+            newFiles.forEach((file, idx) => {
+                if (file.showDel) {
+                    html += `<tr><td>${idx + 1}</td><td><a href="${file.filepath}" target="_blank">${file.filename}</a></td><td>${file.username}</td><td>${moment(file.upload_time).format('YYYY-MM-DD HH:mm:ss')}</td><td><a href="${preUrl}/file/${file.id}/download" class="mr-2"><i class="fa fa-download"></i></a><a href="javascript: void(0);" class="text-danger file-del" data-id="${file.id}"><i class="fa fa-remove"></i></a></td></tr>`
+                } else {
+                    html += `<tr><td width="70">${idx + 1}</td><td><a href="${file.filepath}" target="_blank">${file.filename}</a></td><td>${file.username}</td><td>${moment(file.upload_time).format('YYYY-MM-DD HH:mm:ss')}</td><td><a href="${preUrl}/file/${file.id}/download" class="mr-2"><i class="fa fa-download"></i></a></td></tr>`
+                }
+            })
+            $('#file-content').append(html);
+        }
     }
 
     $('#file-content').on('click', 'a', function () {

+ 2 - 0
app/public/js/revise.js

@@ -95,6 +95,7 @@ $(document).ready(() => {
             },
         },
     ];
+    billsSpreadSetting.tipsSum = true;
     billsSpreadSetting.getColor = function(sheet, data, row, col, defaultColor) {
         if (!data) return defaultColor;
         return data.used ? spreadColor.revise.used : defaultColor;
@@ -105,6 +106,7 @@ $(document).ready(() => {
     const posSheet = posSpread.getActiveSheet();
     posSpreadSetting.cols.push({title: '截止本期合同计量|数量', colSpan: '3|1', rowSpan: '1|1', field: 'end_contract_qty', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
         {title: '|完成率(%)', colSpan: '1', rowSpan: '|1', field: 'end_percent', hAlign: 2, width: 80, readOnly: true, type: 'Number'});
+    posSpreadSetting.tipsSum = true;
     sjsSettingObj.setGridSelectStyle(posSpreadSetting);
     if (thousandth) sjsSettingObj.setTpThousandthFormat(posSpreadSetting);
     SpreadJsObj.initSheet(posSheet, posSpreadSetting);

+ 48 - 9
app/public/js/safe_inspection_information.js

@@ -252,6 +252,7 @@ $(document).ready(function () {
                     checkDate.selectDate(inspection.check_date ? new Date(inspection.check_date) : new Date());
                 } else {
                     $(`#check_table textarea[data-key=${field}]`).val(inspection[field] || '');
+                    $(`#check_table input[data-key=${field}]`).val(inspection[field] || '');
                 }
             });
         }
@@ -343,7 +344,13 @@ $(document).ready(function () {
                 return;
             }
             postData(preUrl + '/save', {type: 'del-inspection' }, function (result) {
+                const is_wap = window.location.pathname.indexOf('/wap/') !== -1;
                 let link = `/sp/${spid}/safe/tender/${tender_id}/inspection`;
+                if (is_wap) {
+                    link = `/wap` + link;
+                    window.location.href = link;
+                    return;
+                }
                 let orderSetting = getLocalCache('safe-inspection-'+ tender_id +'-list-order');
                 if (!orderSetting) orderSetting = 'time|desc';
                 const orders = orderSetting.split('|');
@@ -633,15 +640,47 @@ $(document).ready(function () {
             }
             return {...file, showDel}
         })
-        let html = inspection.filePermission ? `<tr><td colspan="5"><a href="#addfujian" data-toggle="modal" class="btn btn-sm btn-light text-primary" data-placement="bottom" title="" data-original-title="上传附件"><i class="fa fa-cloud-upload" aria-hidden="true"></i> 上传附件</a></td></tr>` : '';
-        newFiles.forEach((file, idx) => {
-            if (file.showDel) {
-                html += `<tr><td>${idx + 1}</td><td><a href="${file.filepath}" target="_blank">${file.filename}</a></td><td>${file.username}</td><td>${moment(file.upload_time).format('YYYY-MM-DD HH:mm:ss')}</td><td><a href="${preUrl}/file/${file.id}/download" class="mr-2"><i class="fa fa-download"></i></a><a href="javascript: void(0);" class="text-danger file-del" data-id="${file.id}"><i class="fa fa-remove"></i></a></td></tr>`
-            } else {
-                html += `<tr><td width="70">${idx + 1}</td><td><a href="${file.filepath}" target="_blank">${file.filename}</a></td><td>${file.username}</td><td>${moment(file.upload_time).format('YYYY-MM-DD HH:mm:ss')}</td><td><a href="${preUrl}/file/${file.id}/download" class="mr-2"><i class="fa fa-download"></i></a></td></tr>`
-            }
-        })
-        $('#file-content').append(html);
+        const is_wap = window.location.pathname.indexOf('/wap/') !== -1;
+        if (is_wap) {
+            let html = `<div style="display: flex;">
+            <b>附件</b>`;
+                if (inspection.filePermission) {
+                    html += `<span style="margin-left: auto;"><a href="#addfujian" data-toggle="modal" class="btn btn-sm btn-light text-primary" data-placement="bottom" title="" data-original-title="上传附件"><i class="fa fa-cloud-upload" aria-hidden="true"></i> 上传附件</a></span>`;
+                }
+            html += `</div><div>`;
+            newFiles.forEach((file, idx) => {
+                html += `<a href="${file.filepath}" target="_blank">
+                    <div class="card my-1" style="cursor: pointer">
+                        <div class="card-body"><i class="fa fa-file"></i> ${file.filename}<small class="pull-right">${file.showDel ? `<span class="badge">
+                                    <span class="dropdown">
+                                <a href="javascript:void(0)" class="btn-sm text-danger px-1" title="移除" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"><i class="fa fa-remove"></i></a>
+                                <div class="dropdown-menu">
+                                  <a class="dropdown-item" href="javascript:void(0);">确认删除附件?</a>
+                                  <div class="dropdown-divider"></div>
+                                  <div class="px-2 py-1 text-center">
+                                    <a href="javascript: void(0);" class="btn btn-sm btn-danger file-del" data-id="${file.id}">删除</a>
+                                    <button class="btn btn-sm btn-secondary">取消</button>
+                                  </div>
+                                </div>
+                              </span>
+                              </span>` : ''}</small> <small class="pull-right text-secondary">${file.filesize}</small>
+                        </div>
+                    </div>
+                </a>`;
+            });
+            html += '</div>';
+            $('#file-content').append(html);
+        } else {
+            let html = inspection.filePermission ? `<tr><td colspan="5"><a href="#addfujian" data-toggle="modal" class="btn btn-sm btn-light text-primary" data-placement="bottom" title="" data-original-title="上传附件"><i class="fa fa-cloud-upload" aria-hidden="true"></i> 上传附件</a></td></tr>` : '';
+            newFiles.forEach((file, idx) => {
+                if (file.showDel) {
+                    html += `<tr><td>${idx + 1}</td><td><a href="${file.filepath}" target="_blank">${file.filename}</a></td><td>${file.username}</td><td>${moment(file.upload_time).format('YYYY-MM-DD HH:mm:ss')}</td><td><a href="${preUrl}/file/${file.id}/download" class="mr-2"><i class="fa fa-download"></i></a><a href="javascript: void(0);" class="text-danger file-del" data-id="${file.id}"><i class="fa fa-remove"></i></a></td></tr>`
+                } else {
+                    html += `<tr><td width="70">${idx + 1}</td><td><a href="${file.filepath}" target="_blank">${file.filename}</a></td><td>${file.username}</td><td>${moment(file.upload_time).format('YYYY-MM-DD HH:mm:ss')}</td><td><a href="${preUrl}/file/${file.id}/download" class="mr-2"><i class="fa fa-download"></i></a></td></tr>`
+                }
+            });
+            $('#file-content').append(html);
+        }
     }
 
     $('#file-content').on('click', 'a', function () {

+ 82 - 1
app/public/js/spreadjs_rela/spreadjs_zh.js

@@ -27,6 +27,31 @@ proto.dottedLine = function (x1, y1, x2, y2, interval = 4) {
     }
 };
 
+const textUtils = {
+    getStringListDisplayWidth: function(sheet, font, strList) {
+        const xs = sheet.getParent().xs;
+        const ctx = xs.childNodes[0].getContext("2d");
+        ctx.font = font;
+        const widths = strList.map(x => { return ctx.measureText(x).width; });
+        return Math.max(...widths);
+    },
+    getTextDisplayWidth: function(sheet, font, str) {
+        const xs = sheet.getParent().xs;
+        const ctx = xs.childNodes[0].getContext("2d");
+        ctx.font = font;
+        return ctx.measureText(str).width;
+    },
+    getTextAutoHeight: function(sheet, font, str, displayWidth) {
+        const xs = sheet.getParent().xs;
+        const ctx = xs.childNodes[0].getContext("2d");
+        ctx.font = font;
+        const textMertrices = ctx.measureText(str);
+        const fontHeight = textMertrices.fontBoundingBoxAscent + textMertrices.fontBoundingBoxDescent;
+        const textRow = Math.ceil(textMertrices.width / displayWidth);
+        return textRow * fontHeight + 2 + (textRow - 1) * 1;
+    }
+}
+
 // 简写Spread常量
 const spreadNS = GC.Spread.Sheets;
 // 授权码
@@ -245,6 +270,61 @@ const SpreadJsObj = {
             }
         });
     },
+    selChangedTipsSum: function(sheet) {
+        sheet.bind(spreadNS.Events.SelectionChanged, function(e, info) {
+            let sum = 0, count = 0, average = 0;
+            const sel = info.newSelections[0];
+            if (sel) {
+                for (let iRow = sel.row; iRow < sel.row + sel.rowCount; iRow++) {
+                    if (!info.sheet.getRowVisible(iRow)) continue;
+                    for (let iCol = sel.col; iCol < sel.col + sel.colCount; iCol++) {
+                        if (!info.sheet.getColumnVisible(iCol)) continue;
+                        const text = info.sheet.getText(iRow, iCol);
+                        if (text) {
+                            count++;
+                            const value = _.toNumber(text);
+                            sum = ZhCalc.add(sum, _.isNaN(value) ? 0 : value);
+                        }
+                    }
+                }
+            }
+            if (count > 1) {
+                const maxHintWidth = 200, indent = 10;
+                average = ZhCalc.div(sum, count);
+                const text = [`合计:${sum}`, `计数:${count}`, `平均值:${average}`];
+                let sumTip = $('#spread-sum-tip')[0];
+                if (!sumTip) {
+                    sumTip = document.createElement("div");
+                    $(sumTip).css("position", "absolute")
+                        .css("border", "1px #C0C0C0 solid")
+                        .css("box-shadow", "1px 2px 5px rgba(0,0,0,0.4)")
+                        .css("font", "9pt Arial")
+                        .css("background", "white")
+                        .css("padding", 5)
+                        .css("z-index", 999)
+                        .css("max-width", maxHintWidth)
+                        .css("word-wrap", "break-word")
+                        .attr("id", 'spread-sum-tip');
+                    document.body.insertBefore(sumTip, null);
+                }
+                const hitinfo = info.sheet.getCellRect(sel.row, sel.col + sel.colCount);
+                const pos = SpreadJsObj.getObjPos(info.sheet.getParent().qo);
+                const showTop = Math.min(pos.y + hitinfo.y + indent, window.innerHeight - 72); // 52(w)+10(p)+10(i)
+                let showLeft = pos.x + hitinfo.x + indent;
+                if (showLeft > window.innerWidth - maxHintWidth) {
+                    const textWidth = textUtils.getStringListDisplayWidth(info.sheet, "9pt Arial", text);
+                    showLeft = Math.min(showLeft, window.innerWidth - textWidth - 10 - indent);
+                }
+                $(sumTip).html(text.join('<br/>')).css("top", showTop).css("left", showLeft);
+
+                setTimeout(()=>{ if (sumTip) $(sumTip).show("fast");}, 100);
+                // setTimeout(()=>{ if (sumTip) $(sumTip).hide();}, 5000);
+            } else {
+                const sumTip = $('#spread-sum-tip');
+                if (sumTip) sumTip.hide();
+            }
+        });
+    },
     selChangedRefreshBackColor: function (sheet) {
         sheet.bind(spreadNS.Events.SelectionChanged, function (e, info) {
             const rows = [];
@@ -418,6 +498,7 @@ const SpreadJsObj = {
         if (setting.selectedBackColor) {
             SpreadJsObj.selChangedRefreshBackColor(sheet);
         }
+        if (setting.tipsSum) SpreadJsObj.selChangedTipsSum(sheet);
         this.endMassOperation(sheet);
         if (!setting.invalidCopyFilterHiddenRow) this.copyFilterHiddenRow(sheet);
     },
@@ -972,7 +1053,7 @@ const SpreadJsObj = {
                 for (const [iCol, col] of sheet.zh_setting.cols.entries()) {
                     sheet.getCell(i, iCol).backColor(SpreadJsObj._getBackColor(sheet, data, i, col));
                 }
-            };
+            }
             this.endMassOperation(sheet);
         } catch (err) {
             this.endMassOperation(sheet);

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

@@ -770,6 +770,7 @@ $(document).ready(() => {
     // if (ratioCol) ratioCol.field = tenderInfo.display.stage.correct ? 'end_correct_percent' : 'end_gather_percent';
     const ratioCol = ledgerSpreadSetting.cols.find(x => {return x.field === 'end_final_1_percent' || x.field === 'end_correct_1_percent'});
     if (ratioCol) ratioCol.field = tenderInfo.display.stage.correct ? 'end_correct_1_percent' : 'end_final_1_percent';
+    ledgerSpreadSetting.tipsSum = true;
     ledgerSpreadSetting.imageClick = function (data, hitinfo) {
         const col = hitinfo.sheet.zh_setting.cols[hitinfo.col];
         switch (col.field) {
@@ -869,6 +870,7 @@ $(document).ready(() => {
         { field: 'qc_qty', showImage: posQcColShowImage },
         { field: 'qc_minus_qty', showImage: posQcColShowImage }
     ]);
+    posSpreadSetting.tipsSum = true;
     posSpreadSetting.imageClick = function (data, hitinfo) {
         const col = hitinfo.sheet.zh_setting.cols[hitinfo.col];
         switch (col.field) {

+ 4 - 0
app/public/js/stage_gather.js

@@ -64,14 +64,17 @@ $(document).ready(function () {
     };
     sjsSettingObj.setOrgPriceCol(gclSpreadSetting.cols, [{ field: 'org_price' }]);
     if (thousandth) sjsSettingObj.setTpThousandthFormat(gclSpreadSetting);
+    gclSpreadSetting.tipsSum = true;
     SpreadJsObj.initSheet(gclSpread.getActiveSheet(), gclSpreadSetting);
     // 初始化所属项目节
     const leafXmjSpread = SpreadJsObj.createNewSpread($('#leaf-xmj-spread')[0]);
     if (thousandth) sjsSettingObj.setTpThousandthFormat(leafXmjSpreadSetting);
+    leafXmjSpreadSetting.tipsSum = true;
     SpreadJsObj.initSheet(leafXmjSpread.getActiveSheet(), leafXmjSpreadSetting);
 
     const gatherLeafXmjSpread = SpreadJsObj.createNewSpread($('#leaf-xmj-gather-spread')[0]);
     if (thousandth) sjsSettingObj.setTpThousandthFormat(gatherLeafXmjSpreadSetting);
+    gatherLeafXmjSpreadSetting.tipsSum = true;
     const gatherLeafXmjSheet = gatherLeafXmjSpread.getActiveSheet();
     SpreadJsObj.initSheet(gatherLeafXmjSheet, gatherLeafXmjSpreadSetting);
 
@@ -106,6 +109,7 @@ $(document).ready(function () {
             key: 'ledger-gather-change',
             colWidth: true,
         },
+        tipsSum: true,
     };
     if (thousandth) sjsSettingObj.setTpThousandthFormat(changeSpreadSetting);
     const changeSheet = changeSpread.getActiveSheet();

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

@@ -1,3 +1,6 @@
+function autoFlashHeight(){
+}
+$(window).resize(autoFlashHeight);
 $(function () {
     $('a').on('click', function () {
         if ($(this).hasClass('show-loading')) {
@@ -319,3 +322,21 @@ function showWaitingView() {
     //中间等待图标
     document.getElementById("msgDiv").innerHTML = '<i class="fa fa-spinner fa-pulse fa-3x fa-fw"></i>';
 }
+
+function transFormToChinese(num) {
+    const changeNum = ['零', '一', '二', '三', '四', '五', '六', '七', '八', '九'];
+    const unit = ["", "十", "百", "千", "万"];
+    num = parseInt(num);
+    let getWan = (temp) => {
+        let strArr = temp.toString().split("").reverse();
+        let newNum = "";
+        for (var i = 0; i < strArr.length; i++) {
+            newNum = (i == 0 && strArr[i] == 0 ? "" : (i > 0 && strArr[i] == 0 && strArr[i - 1] == 0 ? "" : changeNum[strArr[i]] + (strArr[i] == 0 ? unit[0] : unit[i]))) + newNum;
+        }
+        return strArr.length === 2 && newNum.indexOf("一十") !== -1 ? newNum.replace('一十', '十') : newNum;
+    }
+    let overWan = Math.floor(num / 10000);
+    let noWan = num % 10000;
+    if (noWan.toString().length < 4) noWan = "0" + noWan;
+    return overWan ? getWan(overWan) + "万" + getWan(noWan) : getWan(num);
+}

+ 22 - 2
app/public/js/wap/list.js

@@ -223,7 +223,25 @@ function getTenderTreeHtml () {
     const html = [];
     html.push('<table class="table">');
     html.push('<thead>', '<tr>');
-    html.push('<th class="text-center">', subProject.name, '</th>');
+    html.push(`<th class="${!safePermission && !qualityPermission ? 'text-center' : ''}">`, '<span style="">', subProject.name, '</span>');
+    if (safePermission || qualityPermission) {
+        html.push(`<div class="float-right btn-group">
+  <button class="btn btn-sm btn-light dropdown-toggle" type="button" data-toggle="dropdown" aria-expanded="false">`);
+        html.push(!type ? '计量管理' : (type === 'quality' ? '质量巡检' : (type === 'safe' ? '安全巡检' : '')));
+  html.push(`</button>`);
+        html.push(`<div class="dropdown-menu">`);
+        if (type) {
+            html.push(`<a class="dropdown-item" href="/wap/sp/${subProject.id}/list">计量管理</a>`);
+        }
+        if (type !== 'quality' && qualityPermission) {
+            html.push(`<a class="dropdown-item" href="/wap/sp/${subProject.id}/quality/inspection">质量巡检</a>`);
+        }
+        if (type !== 'safe' && safePermission) {
+            html.push(`<a class="dropdown-item" href="/wap/sp/${subProject.id}/safe/inspection">安全巡检</a>`);
+        }
+        html.push('</div> </div>');
+    }
+    html.push('</th>');
     // html.push('<th width="120">计量期数</th>');
     // html.push('<th>总价</th>');
     html.push('</tr>', '</thead>');
@@ -244,7 +262,9 @@ function bindTenderUrl() {
         const tender = _.find(tenders, function (t) {
             return t.id === tenderId;
         });
-        if (tender.measure_type) {
+        if (type) {
+            window.location.href = `/wap/sp/${subProject.id}/${type}/tender/${tenderId}/inspection`;
+        } else if (tender.measure_type) {
             window.location.href = '/wap/tender/' + tenderId;
         } else {
             // for (const a of $('a', '#jlms')) {

+ 8 - 1
app/router.js

@@ -1153,7 +1153,14 @@ module.exports = app => {
     app.get('/wap/dashboard/msg/:id', sessionAuth, 'wapController.msg');
     app.get('/wap/subproj', sessionAuth, 'wapController.subproj');
     app.get('/wap/sp/:id/list', sessionAuth, subProjectCheck, 'wapController.list');
+    app.get('/wap/sp/:id/:type/inspection', sessionAuth, subProjectCheck, 'wapController.list');
+    // app.get('/wap/sp/:id/:type/tender/:tid/inspection/:qiid/information', sessionAuth, subProjectCheck, tenderCheck, tenderPermissionCheck, inspectionCheck, 'wapController.inspectionInformation');
+    app.get('/wap/sp/:id/:type/tender/:tid/inspection/add', sessionAuth, subProjectCheck, tenderCheck, tenderPermissionCheck, 'wapController.inspectionAdd');
+    app.post('/wap/sp/:id/:type/tender/:tid/inspection/save', sessionAuth, subProjectCheck, tenderCheck, tenderPermissionCheck, 'wapController.inspectionSave');
+    app.get('/wap/sp/:id/safe/tender/:tid/inspection/:qiid/information', sessionAuth, subProjectCheck, tenderCheck, tenderPermissionCheck, safeInspectionCheck, 'wapController.safeInspectionInformation');
+    app.get('/wap/sp/:id/quality/tender/:tid/inspection/:qiid/information', sessionAuth, subProjectCheck, tenderCheck, tenderPermissionCheck, qualityInspectionCheck, 'wapController.qualityInspectionInformation');
     app.get('/wap/tender/:id', sessionAuth, tenderCheck, subProjectCheck, uncheckTenderCheck, 'wapController.tender');
+    app.get('/wap/sp/:id/:type/tender/:tid/inspection', sessionAuth, subProjectCheck, tenderCheck, tenderPermissionCheck, 'wapController.inspection');
     app.get('/wap/tender/:id/stage/:order', sessionAuth, tenderCheck, subProjectCheck, uncheckTenderCheck, stageCheck, 'wapController.stage');
     app.get('/wap/tender/:id/change/:cid/info', sessionAuth, tenderCheck, subProjectCheck, uncheckTenderCheck, changeCheck, changeAuditCheck, 'wapController.change');
     app.get('/wap/tender/:id/change/plan/:cpid/info', sessionAuth, tenderCheck, subProjectCheck, uncheckTenderCheck, 'wapController.changePlan');
@@ -1170,7 +1177,7 @@ module.exports = app => {
     app.get('/wap/tender/:id/change/plan/:cpid/information', sessionAuth, tenderCheck, subProjectCheck, uncheckTenderCheck, changePlanCheck, changePlanAuditCheck, 'wapController.changePlan');
     app.get('/wap/tender/:id/material/:order', sessionAuth, tenderCheck, subProjectCheck, uncheckTenderCheck, materialCheck, 'wapController.material');
     app.get('/wap/tender/:id/measure/material/:order', sessionAuth, tenderCheck, subProjectCheck, uncheckTenderCheck, materialCheck, 'wapController.material');
-    app.get('/wap/inspection', 'wapController.inspection');
+    // app.get('/wap/inspection', 'wapController.inspectionAdd');
     app.post('/wap/inspection/ask', 'wapController.inspectionAiAsk');
     app.post('/wap/voice/token', 'wapController.voiceToken');
     app.post('/wap/voice/oneshot', 'wapController.voiceOneShot');

+ 4 - 2
app/service/calc_tmpl.js

@@ -48,7 +48,7 @@ const PosCalc = (function(){
             { title: '数量', width: 80, type: 'num', field: 'qty', decimal: 2, },
         ]
     };
-    return { EmptySpreadCache, BaseSpreadColSetting, ValidColInfo, TestData };
+    return { EmptySpreadCache, BaseSpreadColSetting, ValidColInfo, TestData, ValidCount: 50 };
 })();
 const Cost = (function(){
     const EmptySpreadCache = {
@@ -76,7 +76,7 @@ const Cost = (function(){
         { key: 'memo', name: '长文本', fields: ['memo1', 'memo2'], valid: ['title', 'width'], def: { title: '长文本', width: 120, type: 'memo'} },
     ];
     ValidColInfo.forEach(vci => { return vci.count = vci.fields.length; });
-    return { EmptySpreadCache, BaseSpreadColSetting, ValidColInfo };
+    return { EmptySpreadCache, BaseSpreadColSetting, ValidColInfo, ValidCount: 10 };
 })();
 function randomWord(randomFlag, min, max){
     let str = "",
@@ -338,6 +338,8 @@ module.exports = app => {
 
         async _addTemplate(name, type) {
             if (ValidTemplateType.indexOf(type) < 0) throw '新增的模板类型非法';
+            const count = await this.count({pid: this.ctx.session.sessionProject.id, type});
+            if (count > this.TemplateRela[type].ValidCount) throw '已达模板使用上限';
 
             const relaConst = this.TemplateRela[type];
             const insertData = {

+ 51 - 0
app/service/quality_inspection.js

@@ -122,6 +122,57 @@ module.exports = app => {
             return result;
         }
 
+        async addByWap(tid, data) {
+            const sql = 'SELECT COUNT(*) as count FROM ?? WHERE `tid` = ? AND `code` = ?';
+            const sqlParam = [this.tableName, tid, data.code];
+            const codeCount = await this.db.queryOne(sql, sqlParam);
+            const count = codeCount.count;
+            if (count > 0) {
+                throw '巡检编号已存在,请修改巡检编号';
+            }
+
+            // 初始化事务
+            const transaction = await this.db.beginTransaction();
+            let result = false;
+            try {
+                const inspection = {
+                    tid,
+                    uid: this.ctx.session.sessionUser.accountId,
+                    status: auditConst.status.uncheck,
+                    times: 1,
+                    code: data.code,
+                    check_item: data.check_item,
+                    check_date: data.check_date || null,
+                    check_situation: data.check_situation || '',
+                    action: data.action || '',
+                    inspector: data.inspector || this.ctx.session.sessionUser.name,
+                    create_time: new Date(),
+                };
+                const operate = await transaction.insert(this.tableName, inspection);
+
+                if (operate.affectedRows <= 0) {
+                    throw '新建质量巡检数据失败';
+                }
+                inspection.id = operate.insertId;
+                // 先找出标段最近存在的变更令审批人的变更令info
+                const preChangeInfo = await this.getHaveAuditLastInfo(tid);
+                if (preChangeInfo) {
+                    // 并把之前存在的变更令审批人添加到zh_change_audit
+                    const auditResult = await this.ctx.service.qualityInspectionAudit.copyPreAuditors(transaction, preChangeInfo, inspection);
+                    if (!auditResult) {
+                        throw '复制上一次审批流程失败';
+                    }
+                }
+                result = inspection;
+                await transaction.commit();
+            } catch (error) {
+                console.log(error);
+                // 回滚
+                await transaction.rollback();
+            }
+            return result;
+        }
+
         async delInspection(id) {
             const transaction = await this.db.beginTransaction();
             try {

+ 1 - 1
app/service/quality_inspection_att.js

@@ -41,7 +41,7 @@ module.exports = app => {
             return result.map(item => {
                 item.orginpath = this.ctx.app.config.fujianOssPath + item.filepath;
                 if (!ctx.helper.canPreview(item.fileext)) {
-                    item.filepath = `/tender/${ctx.tender.id}/change/plan/${item.cpid}/information/file/${item.id}/download`;
+                    item.filepath = `/sp/${ctx.subProject.id}/quality/tender/${ctx.tender.id}/inspection/${item.qiid}/information/file/${item.id}/download`;
                 } else {
                     item.filepath = this.ctx.app.config.fujianOssPath + item.filepath;
                 }

+ 31 - 0
app/service/quality_inspection_audit.js

@@ -245,6 +245,37 @@ module.exports = app => {
         }
 
         /**
+         * 获取审核人需要审核的期列表
+         *
+         * @param auditorId
+         * @return {Promise<*>}
+         */
+        async getAuditInspectionByWap(auditorId) {
+            const sql = 'SELECT ma.`aid`, ma.`times`, ma.`order`, ma.`begin_time`, ma.`end_time`, ma.`tid`, ma.`qiid`,' +
+                '    m.`code` As `mcode`, m.`status` As `mstatus`, m.`check_item`, m.`check_date`,' +
+                '    t.id As t_id, t.`name` As t_name, t.`project_id` As t_pid, t.`type` As t_type, t.`user_id` As t_uid, t.`status` As t_status, t.`spid`, ' +
+                '    ti.`deal_info`' +
+                '  FROM ?? AS ma' +
+                '  LEFT JOIN ?? AS m On ma.qiid = m.id' +
+                '  LEFT JOIN ?? As t On m.tid = t.id' +
+                '  LEFT JOIN ?? As ti On m.tid = ti.id' +
+                '  WHERE ma.`aid` = ? and (ma.`status` = ? OR ma.`status` = ?)' +
+                '  ORDER BY ma.`begin_time` DESC';
+            const sqlParam = [this.tableName, this.ctx.service.qualityInspection.tableName, this.ctx.service.tender.tableName, this.ctx.service.tenderInfo.tableName, auditorId, auditConst.status.checking, auditConst.status.rectification];
+            const result = await this.db.query(sql, sqlParam);
+            // 过滤result中存在重复sid的值, 保留最新的一条
+            const filterResult = [];
+            const qiidArr = [];
+            for (const r of result) {
+                if (qiidArr.indexOf(r.qiid) === -1) {
+                    filterResult.push(r);
+                    qiidArr.push(r.qiid);
+                }
+            }
+            return filterResult;
+        }
+
+        /**
          * 获取审核人审核的次数
          *
          * @param auditorId

+ 53 - 0
app/service/safe_inspection.js

@@ -81,6 +81,8 @@ module.exports = app => {
             if (count > 0) {
                 throw '编号重复';
             }
+            console.log(check_date);
+            throw 'error';
 
             // 初始化事务
             const transaction = await this.db.beginTransaction();
@@ -122,6 +124,57 @@ module.exports = app => {
             return result;
         }
 
+        async addByWap(tid, data) {
+            const sql = 'SELECT COUNT(*) as count FROM ?? WHERE `tid` = ? AND `code` = ?';
+            const sqlParam = [this.tableName, tid, data.code];
+            const codeCount = await this.db.queryOne(sql, sqlParam);
+            const count = codeCount.count;
+            if (count > 0) {
+                throw '巡检编号已存在,请修改巡检编号';
+            }
+
+            // 初始化事务
+            const transaction = await this.db.beginTransaction();
+            let result = false;
+            try {
+                const inspection = {
+                    tid,
+                    uid: this.ctx.session.sessionUser.accountId,
+                    status: auditConst.status.uncheck,
+                    times: 1,
+                    code: data.code,
+                    check_item: data.check_item,
+                    check_date: data.check_date || null,
+                    check_situation: data.check_situation || '',
+                    action: data.action || '',
+                    inspector: data.inspector || this.ctx.session.sessionUser.name,
+                    create_time: new Date(),
+                };
+                const operate = await transaction.insert(this.tableName, inspection);
+
+                if (operate.affectedRows <= 0) {
+                    throw '新建质量巡检数据失败';
+                }
+                inspection.id = operate.insertId;
+                // 先找出标段最近存在的变更令审批人的变更令info
+                const preChangeInfo = await this.getHaveAuditLastInfo(tid);
+                if (preChangeInfo) {
+                    // 并把之前存在的变更令审批人添加到zh_change_audit
+                    const auditResult = await this.ctx.service.safeInspectionAudit.copyPreAuditors(transaction, preChangeInfo, inspection);
+                    if (!auditResult) {
+                        throw '复制上一次审批流程失败';
+                    }
+                }
+                result = inspection;
+                await transaction.commit();
+            } catch (error) {
+                console.log(error);
+                // 回滚
+                await transaction.rollback();
+            }
+            return result;
+        }
+
         async delInspection(id) {
             const transaction = await this.db.beginTransaction();
             try {

+ 1 - 1
app/service/safe_inspection_att.js

@@ -41,7 +41,7 @@ module.exports = app => {
             return result.map(item => {
                 item.orginpath = this.ctx.app.config.fujianOssPath + item.filepath;
                 if (!ctx.helper.canPreview(item.fileext)) {
-                    item.filepath = `/tender/${ctx.tender.id}/change/plan/${item.cpid}/information/file/${item.id}/download`;
+                    item.filepath = `/sp/${ctx.subProject.id}/safe/tender/${ctx.tender.id}/inspection/${item.qiid}/information/file/${item.id}/download`;
                 } else {
                     item.filepath = this.ctx.app.config.fujianOssPath + item.filepath;
                 }

+ 31 - 0
app/service/safe_inspection_audit.js

@@ -245,6 +245,37 @@ module.exports = app => {
         }
 
         /**
+         * 获取审核人需要审核的期列表
+         *
+         * @param auditorId
+         * @return {Promise<*>}
+         */
+        async getAuditInspectionByWap(auditorId) {
+            const sql = 'SELECT ma.`aid`, ma.`times`, ma.`order`, ma.`begin_time`, ma.`end_time`, ma.`tid`, ma.`qiid`,' +
+                '    m.`code` As `mcode`, m.`status` As `mstatus`, m.`check_item`, m.`check_date`,' +
+                '    t.id As t_id, t.`name` As t_name, t.`project_id` As t_pid, t.`type` As t_type, t.`user_id` As t_uid, t.`status` As t_status, t.`spid`, ' +
+                '    ti.`deal_info`' +
+                '  FROM ?? AS ma' +
+                '  LEFT JOIN ?? AS m On ma.qiid = m.id' +
+                '  LEFT JOIN ?? As t On m.tid = t.id' +
+                '  LEFT JOIN ?? As ti On m.tid = ti.id' +
+                '  WHERE ma.`aid` = ? and (ma.`status` = ? OR ma.`status` = ?)' +
+                '  ORDER BY ma.`begin_time` DESC';
+            const sqlParam = [this.tableName, this.ctx.service.safeInspection.tableName, this.ctx.service.tender.tableName, this.ctx.service.tenderInfo.tableName, auditorId, auditConst.status.checking, auditConst.status.rectification];
+            const result = await this.db.query(sql, sqlParam);
+            // 过滤result中存在重复sid的值, 保留最新的一条
+            const filterResult = [];
+            const qiidArr = [];
+            for (const r of result) {
+                if (qiidArr.indexOf(r.qiid) === -1) {
+                    filterResult.push(r);
+                    qiidArr.push(r.qiid);
+                }
+            }
+            return filterResult;
+        }
+
+        /**
          * 获取审核人审核的次数
          *
          * @param auditorId

+ 6 - 6
app/view/sub_proj/info.ejs

@@ -301,9 +301,9 @@
                                     <div class="input-group">
                                         <input type="text" class="form-control form-control-sm" name="zb_tp" value="<%- info.zb_tp %>" org="<%- info.zb_tp %>" maxlength="50" onblur="changeInfo(this, true)" <% if (readOnly) { %>readOnly<% } %>>
                                         <div class="input-group-append">
-                                            <select class="input-group-text p-0" name="pf_tp_unit" style="font-size:0.75rem" value="<%- info.pf_tp_unit %>" org="<%- info.pf_tp_unit%>" onchange="changeInfo(this)" <% if (readOnly) { %>readOnly<% } %>>
-                                                <option value="元" <%if (info.pf_tp_unit === '元') { %>selected<% } %>>元</option>
-                                                <option value="万元" <%if (info.pf_tp_unit === '万元') { %>selected<% } %>>万元</option>
+                                            <select class="input-group-text p-0" name="pf_tp_unit" style="font-size:0.75rem" value="<%- info.zb_tp_unit %>" org="<%- info.zb_tp_unit%>" onchange="changeInfo(this)" <% if (readOnly) { %>readOnly<% } %>>
+                                                <option value="元" <%if (info.zb_tp_unit === '元') { %>selected<% } %>>元</option>
+                                                <option value="万元" <%if (info.zb_tp_unit === '万元') { %>selected<% } %>>万元</option>
                                             </select>
                                         </div>
                                     </div>
@@ -369,9 +369,9 @@
                                     <div class="input-group">
                                         <input type="text" class="form-control form-control-sm" name="jg_tp" value="<%- info.jg_tp %>" org="<%- info.jg_tp %>" maxlength="50" onblur="changeInfo(this, true)" <% if (readOnly) { %>readOnly<% } %>>
                                         <div class="input-group-append">
-                                            <select class="input-group-text p-0" name="pf_tp_unit" style="font-size:0.75rem" value="<%- info.pf_tp_unit %>" org="<%- info.pf_tp_unit%>" onchange="changeInfo(this)" <% if (readOnly) { %>readOnly<% } %>>
-                                                <option value="元" <%if (info.pf_tp_unit === '元') { %>selected<% } %>>元</option>
-                                                <option value="万元" <%if (info.pf_tp_unit === '万元') { %>selected<% } %>>万元</option>
+                                            <select class="input-group-text p-0" name="pf_tp_unit" style="font-size:0.75rem" value="<%- info.jg_tp_unit %>" org="<%- info.jg_tp_unit%>" onchange="changeInfo(this)" <% if (readOnly) { %>readOnly<% } %>>
+                                                <option value="元" <%if (info.jg_tp_unit === '元') { %>selected<% } %>>元</option>
+                                                <option value="万元" <%if (info.jg_tp_unit === '万元') { %>selected<% } %>>万元</option>
                                             </select>
                                         </div>
                                     </div>

+ 1 - 0
app/view/template/pos_calc.ejs

@@ -14,6 +14,7 @@
                         <a href="javascript: void(0);" class="btn btn-sm btn-light text-primary" id="reset"> 重置</a>
                         <a href="javascript: void(0);" class="btn btn-sm btn-light text-primary" id="save"> 保存</a>
                     </div>
+                    <div id="detail-user-info" class="ml-auto"></div>
                 </div>
             </div>
         </div>

+ 43 - 1
app/view/wap/dashboard.ejs

@@ -40,7 +40,7 @@
         </nav>
         <!--待审批期列表-->
         <div class="py-6">
-            <% if (auditStages.length !== 0 || auditChanges.length !== 0 || auditRevise.length !== 0 || auditAdvance.length !== 0 || auditChangeProjects.length !== 0|| auditChangeApplys.length !== 0 || auditChangePlans.length !== 0 || auditMaterials.length !== 0) { %>
+            <% if (auditStages.length !== 0 || auditChanges.length !== 0 || auditRevise.length !== 0 || auditAdvance.length !== 0 || auditChangeProjects.length !== 0|| auditChangeApplys.length !== 0 || auditChangePlans.length !== 0 || auditMaterials.length !== 0 || auditQualityInspections.length !== 0 || auditSafeInspections.length !== 0) { %>
                 <% for (const audit of auditStages) { %>
                 <div class="card mb-3">
                     <div class="card-header d-flex justify-content-between">
@@ -232,6 +232,48 @@
                         </div>
                     </div>
                 <% } %>
+                <% for (const inspection of auditQualityInspections) { %>
+                    <div class="card mb-3">
+                            <div class="card-header d-flex justify-content-between">
+                                <span><%- JSON.parse(inspection.deal_info).buildName %></span>
+                                <span class="badge badge-pill bg-new-inspection text-new-inspection">质量巡检</span>
+                            </div>
+                            <div class="bg-light p-2 px-3"><b><%- inspection.t_name %></b></div>
+                            <div class="card-body">
+                                <div class="d-flex justify-content-between"><span><%- inspection.mcode %></span></div>
+                                <div class="my-2">
+                                    <table class="table table-sm table-bordered">
+                                        <tr><th width="90">检查项目</th><td><%- inspection.check_item.replace(/\r\n/g, '<br/>').replace(/\n/g, '<br/>').replace(/\s/g, ' ') %></td></tr>
+                                        <tr><th>检查日期</th><td><%- ctx.moment(inspection.check_date).format('YYYY-MM-DD') %></td></tr>
+                                    </table>
+                                </div>
+                                <div class="">
+                                    <a href="/wap/sp/<%- inspection.spid %>/quality/tender/<%- inspection.tid %>/inspection/<%- inspection.qiid %>/information#shenpi" class="btn btn-block btn-success"><%- inspection.mstatus !== 2 ? '整改' : '审批' %></a>
+                                </div>
+                            </div>
+                        </div>
+                <% } %>
+                <% for (const inspection of auditSafeInspections) { %>
+                    <div class="card mb-3">
+                        <div class="card-header d-flex justify-content-between">
+                            <span><%- JSON.parse(inspection.deal_info).buildName %></span>
+                            <span class="badge badge-pill bg-new-inspection text-new-inspection">安全巡检</span>
+                        </div>
+                        <div class="bg-light p-2 px-3"><b><%- inspection.t_name %></b></div>
+                        <div class="card-body">
+                            <div class="d-flex justify-content-between"><span><%- inspection.mcode %></span></div>
+                            <div class="my-2">
+                                <table class="table table-sm table-bordered">
+                                    <tr><th width="90">检查项目</th><td><%- inspection.check_item.replace(/\r\n/g, '<br/>').replace(/\n/g, '<br/>').replace(/\s/g, ' ') %></td></tr>
+                                    <tr><th>检查日期</th><td><%- ctx.moment(inspection.check_date).format('YYYY-MM-DD') %></td></tr>
+                                </table>
+                            </div>
+                            <div class="">
+                                <a href="/wap/sp/<%- inspection.spid %>/safe/tender/<%- inspection.tid %>/inspection/<%- inspection.qiid %>/information#shenpi" class="btn btn-block btn-success"><%- inspection.mstatus !== 2 ? '整改' : '审批' %></a>
+                            </div>
+                        </div>
+                    </div>
+                <% } %>
             <% } else { %>
                 <h3 class="text-center text-muted">暂无待审批期计量</h3>
             <% } %>

+ 59 - 9
app/view/wap/inspection.ejs

@@ -2,8 +2,9 @@
 <html lang="zh">
 <head>
     <meta charset="UTF-8">
-    <title>巡检助手</title>
     <meta name="viewport" content="width=device-width, initial-scale=1">
+    <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">
@@ -25,6 +26,9 @@
             font-size: 16px;
             padding-bottom: 100px;
         }
+        .pt-4 {
+            padding-top: 3.5rem !important;
+        }
         .chat-box {
             height: calc(100vh - 100px); /* 减去头部和底部高度,150px 是示例,需根据实际调整 */
             overflow-y: auto;
@@ -223,13 +227,35 @@
 </head>
 <body>
 <div class="container mb-3 px-0">
-    <h5 class="text-center py-2 xj-title mb-0">巡检助手</h5>
-    <div id="chat-box" class="chat-box mb-3">
+    <!--顶部-->
+    <nav class="fixed-top bg-dark">
+        <div class="my-2 d-flex justify-content-between">
+            <span class="text-white ml-3"><a href="/wap/sp/<%- ctx.subProject.id %>/<%- type %>/tender/<%- tender.id %>/inspection" class="mr-2 text-white show-loading"><i class="fa fa-chevron-left"></i><%- (type === 'quality' ? '质量' : type === 'safe' ? '安全' : '') %>巡检</a></span>
+            <a tabindex="0" href="javascript:void(0)" class="text-white text-truncate text-center"
+               style="width:150px" data-toggle="popover" data-placement="top"
+               data-content="<%- tender.name %>" data-trigger="focus"><%- tender.name %></a>
+            <div class="mr-3">
+                <div class="dropdown">
+                    <button class="btn btn-sm btn-light dropdown-toggle" type="button" data-toggle="dropdown">
+                        <%- ctx.session.sessionUser.name.substr(ctx.session.sessionUser.name.length > 2 ? ctx.session.sessionUser.name.length - 2 : 0) %>
+                    </button>
+                    <div class="dropdown-menu" aria-labelledby="dropdownMenuButton">
+                        <a class="dropdown-item" href="/wap/logout">退出登录</a>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </nav>
+<!--    <h5 class="text-center py-2 xj-title mb-0">巡检助手</h5>-->
+    <div id="chat-box" class="chat-box pt-4 mb-3">
         <div id="messages"></div>
         <!-- 表单预览卡片 -->
         <div id="form-preview" class="form-preview d-none">
             <h6><i class="fa fa-clipboard-check mr-2"></i>巡检单信息确认</h6>
-
+            <div class="form-item">
+                <label>巡检编号:</label>
+                <span id="field-code"><%- newCode %></span>
+            </div>
             <div class="form-item">
                 <label>检查项目:</label>
                 <span id="field-check_item">—</span>
@@ -248,7 +274,7 @@
             </div>
             <div class="form-item">
                 <label>质检员:</label>
-                <span id="field-inspector"></span>
+                <span id="field-inspector"><%- ctx.session.sessionUser.name %></span>
             </div>
 
             <div class="form-actions">
@@ -301,6 +327,11 @@
 <script src="/public/js/wap/global.js"></script>
 <script>
     const csrf = '<%= ctx.csrf %>';
+    const spid = '<%- ctx.subProject.id %>';
+    const tender_id = parseInt('<%- ctx.tender.id %>');
+    const type = '<%- type %>';
+    const inspector = JSON.parse('<%- JSON.stringify(ctx.session.sessionUser.name) %>');
+    const newCode = JSON.parse('<%- JSON.stringify(newCode) %>');
 </script>
 <script>
     const $chatBox = $('#chat-box');
@@ -318,13 +349,14 @@
             $('#messages').append($message);
             $chatBox.scrollTop($chatBox[0].scrollHeight);
         };
-        appendMessage("👋 嗨,我是巡检助手,告诉我项目名称可以帮你快速生成巡检单", "ai", 'mt-3');
+        appendMessage("👋 嗨,我是巡检助手,告诉我检查项目名称可以帮你快速生成巡检单", "ai", 'mt-3');
         const updatePreviewForm = (data) => {
+            $('#field-code').text(data.code || newCode);
             $('#field-check_item').text(data.check_item || '—');
             $('#field-check_situation').text(data.check_situation || '—');
             $('#field-action').text(data.action || '—');
             $('#field-date').text(data.date || '—');
-            $('#field-inspector').text(data.inspector || '—');
+            $('#field-inspector').text(data.inspector || inspector);
             $formPreview.removeClass('d-none');
         };
 
@@ -360,13 +392,31 @@
         });
 
         $('#confirm-btn').on('click', () => {
-            alert('✅ 表单已提交!你可以跳转页面或保存到数据库');
+            if ($('#field-code').text() === '') {
+                toastr.error('巡检编号不能为空');
+                return;
+            }
+            if ($('#field-check_item').text() === '—') {
+                toastr.error('请至少提供检查项目名称');
+                return;
+            }
+            const data = {
+                code: $('#field-code').text() !== '—' ? $('#field-code').text() : newCode,
+                check_item: $('#field-check_item').text() !== '—' ? $('#field-check_item').text() : '',
+                check_situation: $('#field-check_situation').text()  !== '—' ? $('#field-check_situation').text() : '',
+                action: $('#field-action').text() !== '—' ? $('#field-action').text() : '',
+                check_date: $('#field-date').text() !== '—' ? $('#field-date').text() : '',
+                inspector: $('#field-inspector').text() !== '—' ? $('#field-inspector').text() : inspector,
+            }
+            postData(`/wap/sp/${spid}/${type}/tender/${tender_id}/inspection/save`, { type: 'addByWap', insert: data }, function (rst) {
+                window.location.href = `/wap/sp/${spid}/${type}/tender/${tender_id}/inspection/${rst.id}/information`;
+            });
             // 可跳转页面如 window.location.href = '/form/preview';
         });
 
         $('#modify-btn').on('click', () => {
             $formPreview.addClass('d-none');
-            appendMessage('请继续修改你想调整的信息。', 'ai');
+            appendMessage('请告诉我并完善你想调整的信息。', 'ai');
         });
 
         const originalHeight = window.innerHeight;

+ 3 - 0
app/view/wap/list.ejs

@@ -64,6 +64,9 @@
     const uid = '<%- uid %>';
     const pid = '<%- pid %>';
     const uphlname = 'user_' + uid + '_pro_' + pid + '_category_wap_hide_list';
+    const type = '<%- type %>';
+    const safePermission = <%- ctx.subProject.page_show.safeInspection %>;
+    const qualityPermission = <%- ctx.subProject.page_show.qualityInspection %>;
 </script>
 <!-- JS. -->
 <script src="/public/js/jquery/jquery-3.2.1.min.js"></script>

File diff suppressed because it is too large
+ 516 - 0
app/view/wap/shenpi_inspection.ejs


+ 112 - 0
app/view/wap/shenpi_inspection_index.ejs

@@ -0,0 +1,112 @@
+<!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><%- (type === 'quality' ? '质量' : type === 'safe' ? '安全' : '') %>巡检-计量支付</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">
+    <script src=/public/js/echarts/echarts.min.js></script>
+    <link rel="shortcut icon" href="/public/images/favicon.ico">
+    <style>
+        body {
+            padding: 0;
+        }
+    </style>
+</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"><a href="/wap/sp/<%- ctx.subProject.id %>/<%- type %>/inspection" class="mr-2 text-white show-loading"><i class="fa fa-chevron-left"></i><%- (type === 'quality' ? '质量' : type === 'safe' ? '安全' : '') %>巡检</a></span>
+            <a tabindex="0" href="javascript:void(0)" class="text-white text-truncate text-center"
+               style="width:150px" data-toggle="popover" data-placement="top"
+               data-content="<%- tender.name %>" data-trigger="focus"><%- tender.name %></a>
+            <div class="mr-3">
+                <div class="dropdown">
+                    <button class="btn btn-sm btn-light dropdown-toggle" type="button" data-toggle="dropdown">
+                        <%- ctx.session.sessionUser.name.substr(ctx.session.sessionUser.name.length > 2 ? ctx.session.sessionUser.name.length - 2 : 0) %>
+                    </button>
+                    <div class="dropdown-menu" aria-labelledby="dropdownMenuButton">
+                        <a class="dropdown-item" href="/wap/logout">退出登录</a>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </nav>
+    <!--标段概况-->
+    <div class="py-6">
+        <% if (permission.add) { %>
+        <a class="btn btn-sm btn-primary w-100" href="/wap/sp/<%- ctx.subProject.id %>/<%- type %>/tender/<%- ctx.tender.id %>/inspection/add">AI助手新建巡检</a>
+        <% } %>
+        <div class="tab-content">
+            <div class="tab-pane active">
+                <dl class="mb-2 mt-3">
+                    <% for (const c of inspections) { %>
+                        <dt class="bg-light p-2 d-flex justify-content-between"><span><a href="/wap/sp/<%- ctx.subProject.id %>/<%- type %>/tender/<%- tender.id %>/inspection/<%- c.id %>/information"><%- c.code %></a></span></dt>
+                        <dd>
+                            <table class="table table-hover">
+                                <tbody>
+                                <tr>
+                                    <td colspan="2">
+                                        <p class="mb-0">检查项目</p>
+                                        <b><%- c.check_item.replace(/\r\n/g, '<br/>').replace(/\n/g, '<br/>').replace(/\s/g, ' ') %></b>
+                                    </td>
+                                </tr>
+                                <tr>
+                                    <td>
+                                        <p class="mb-0">检查日期</p>
+                                        <b><%- c.check_date ? moment(c.check_date).format('YYYY-MM-DD') : '' %></b>
+                                    </td>
+                                </tr>
+                                <tr>
+                                    <td colspan="2">
+                                        <% if (c.showApprovalBtn){ %>
+                                            <a href="/wap/sp/<%- ctx.subProject.id %>/<%- type %>/tender/<%- tender.id %>/inspection/<%- c.id %>/information#shenpi" class="btn btn-block btn-success"><% if (c.status === auditInspectionConst.status.uncheck || c.status === auditInspectionConst.status.checkNo) { %>上报<% } else { %>审批巡检<% } %></a>
+                                        <% } else if (c.status === auditInspectionConst.status.uncheck) { %>
+                                            <span>待上报</span>
+                                        <% } else { %>
+                                            <% if (c.curAuditors.length > 0) { %>
+                                                <% if (c.curAuditors[0].audit_type === auditType.key.common) { %>
+                                                    <span class="<%- auditInspectionConst.statusClass[c.status] %>"><%- c.curAuditors[0].name %>-<%- c.curAuditors[0].role %> <%- auditInspectionConst.statusString[c.status] %></span>
+                                                <% } else { %>
+                                                    <span class="<%- auditInspectionConst.statusClass[c.status] %>"><%- ctx.helper.transFormToChinese(c.curAuditors[0].audit_order) + '审' %> <%- auditInspectionConst.statusString[c.status] %></span>
+                                                <% } %>
+                                            <% } %>
+                                        <% } %>
+                                    </td>
+                                </tr>
+                                </tbody>
+                            </table>
+                        </dd>
+                    <% } %>
+                </dl>
+            </div>
+        </div>
+    </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 text-muted show-loading" href="/wap/dashboard"><i class="fa fa-check-square-o"></i> 待审批</a>
+            </li>
+            <li class="nav-item">
+                <a class="nav-link active show-loading" href="/wap/subproj"><i class="fa fa-list-ul"></i> 项目</a>
+            </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>
+</body>
+</html>