Browse Source

1.资金监管功能改动,2.质量巡检单ai生成功能wap版

ellisran 2 weeks ago
parent
commit
e5b57b3ae8

+ 260 - 104
app/controller/financial_controller.js

@@ -189,54 +189,36 @@ module.exports = app => {
                 if (!transferInfo) {
                     throw '该资金划拨信息不存在';
                 }
+                const categoryData = await this.ctx.service.category.getAllCategory(ctx.subProject);
                 const transferTenderList = await ctx.service.financialTransferTender.getList(trid, true);
-                const total = await ctx.service.financialTransferTender.count({ trid });
-                // 分页相关
-                const page = ctx.page;
-                const pageSize = ctx.pageSize;
-                const pageInfo = {
-                    page,
-                    pageSizeSelect: 1,
-                    pageSize,
-                    total_num: total,
-                    total: Math.ceil(total / pageSize),
-                    queryData: JSON.stringify(ctx.urlInfo.query),
-                };
                 const tenders = await ctx.service.tender.getAllDataByCondition({ where: { spid: ctx.subProject.id, filter_fund: 0 } });
-                for (const tt of transferTenderList) {
-                    const tenderIndex = ctx.helper._.findIndex(tenders, { id: tt.tid });
-                    if (tenderIndex !== -1) {
-                        tenders.splice(tenderIndex, 1);
+                const needTenders = [];
+                for (const t of tenders) {
+                    await ctx.service.tenderCache.loadTenderCache(t, this.ctx.session.sessionUser.accountId);
+                    if (t.stage_complete_count && t.stage_complete_count >= 1) {
+                        t.category = t.category && t.category !== '' ? JSON.parse(t.category) : null;
+                        needTenders.push(t);
                     }
                 }
-                const allTransferTenders = await ctx.service.financialTransferTender.getAllDataByCondition({ where: { spid: ctx.subProject.id } });
-                for (const t of tenders) {
-                    const stages = await ctx.service.stage.getAllDataByCondition({ where: { tid: t.id, status: auditConst.stage.status.checked } });
-                    const allTenderStages = ctx.helper._.filter(allTransferTenders, { tid: t.id });
-                    for (const ts of allTenderStages) {
-                        const sorderArray = ts.sorder ? ts.sorder.split(',') : [];
-                        for (const sorder of sorderArray) {
-                            const stageIndex = ctx.helper._.findIndex(stages, function(item) {
-                                return item.order === parseInt(sorder);
-                            });
-                            if (stageIndex !== -1) {
-                                stages.splice(stageIndex, 1);
-                            }
-                        }
+                for (const tt of transferTenderList) {
+                    const tenderIndex = ctx.helper._.findIndex(needTenders, { id: tt.tid });
+                    if (tenderIndex !== -1) {
+                        // 合并tenders[tenderIndex]到tt
+                        const cloneTender = ctx.helper._.cloneDeep(needTenders[tenderIndex]);
+                        delete cloneTender.id;
+                        Object.assign(tt, cloneTender);
                     }
-                    t.stages = stages;
                 }
-
                 const renderData = {
                     financialPermission,
                     transferInfo,
                     transferTenderList,
-                    tenders,
+                    categoryData,
+                    tenders: needTenders,
                     moment,
                     preUrl: '/sp/' + ctx.subProject.id + '/financial/transfer',
                     jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.financial.transferTender),
                     whiteList: ctx.app.config.multipart.whitelist,
-                    pageInfo,
                 };
                 await this.layout('financial/transfer_tender.ejs', renderData, 'financial/transfer_tender_modal.ejs');
             } catch (err) {
@@ -298,10 +280,13 @@ module.exports = app => {
                         }
                         break;
                     case 'del-tender':
-                        responseData.data = await ctx.service.financialTransferTender.delTenders(data.postData.node);
+                        responseData.data = await ctx.service.financialTransferTender.delTenders(transferInfo, data.postData.node);
                         break;
                     case 'update-hb_tp':
-                        responseData.data = await ctx.service.financialTransferTender.updateHbTp(data.postData.node, data.postData.hb_tp);
+                        responseData.data = await ctx.service.financialTransferTender.updateHbTp(transferInfo, data.postData.node, data.postData.hb_tp);
+                        break;
+                    case 'update-tender':
+                        responseData.data = await ctx.service.financialTransferTender.updateTender(transferInfo, data.postData);
                         break;
                     default:
                         throw '未知操作';
@@ -586,34 +571,15 @@ module.exports = app => {
          */
         async pay(ctx) {
             try {
-                const status = parseInt(ctx.query.status) || 0;
-                const tid = parseInt(ctx.query.tid) || null;
-                const used = ctx.query.used || null;
-                await this._filterPay(ctx, status, tid, used);
+                const company = ctx.query.company || null;
+                await this._filterPay(ctx, company);
             } catch (err) {
                 this.log(err);
                 ctx.redirect(`/sp/${ctx.subProject.id}/dashboard`);
             }
         }
 
-        // /**
-        //  * 变更管理 状态筛选 页面 (Get)
-        //  * @param {Object} ctx - egg全局变量
-        //  * @return {void}
-        //  */
-        // async payStatus(ctx) {
-        //     try {
-        //         const status = parseInt(ctx.query.status);
-        //         const tid = parseInt(ctx.params.tid) || null;
-        //         const used = ctx.params.used || null;
-        //         await this._filterPay(ctx, status, tid, used);
-        //     } catch (err) {
-        //         this.logger.error(err);
-        //         ctx.redirect('/financial');
-        //     }
-        // }
-
-        async _filterPay(ctx, status = 0, tid = null, used = null) {
+        async _filterPay(ctx, company = null) {
             const financialPermission = await ctx.service.subProjPermission.getFinancailPermission(ctx.subProject.permission.fund_trans_permission, ctx.subProject.permission.fund_pay_permission);
             if (!financialPermission.pay_show) {
                 throw '没有查看权限';
@@ -621,7 +587,7 @@ module.exports = app => {
             const fptAudits = await ctx.service.financialPayTenderAudit.getAllDataByCondition({ where: { spid: ctx.subProject.id, uid: ctx.session.sessionUser.accountId } });
             const fptAuditTids = ctx.helper._.map(fptAudits, 'tid');
             const fptReportTids = ctx.helper._.map(ctx.helper._.filter(fptAudits, { is_report: 1 }), 'tid');
-            const filterTids = tid === null ? (ctx.session.sessionUser.is_admin ? null : fptAuditTids) : [tid];
+            const unitList = await ctx.service.constructionUnit.getAllDataByCondition({ where: { pid: ctx.session.sessionProject.id } });
             const tenderCondition = { spid: ctx.subProject.id, filter_fund: 0 };
             let hadTender = false;
             if (ctx.session.sessionUser.is_admin) {
@@ -633,15 +599,18 @@ module.exports = app => {
                 hadTender = false;
                 tenderCondition.id = -1;
             }
-            const tenders = hadTender ? await ctx.service.tender.getAllDataByCondition({ where: tenderCondition, columns: ['id', 'name'] }) : [];
-            const filter = JSON.parse(JSON.stringify(auditConst.financial.filter));
-            filter.count = [];
-            filter.count[filter.status.pending] = await ctx.service.financialPay.getCountByStatus(ctx.subProject.id, filter.status.pending, filterTids, used);// await ctx.service.change.pendingDatas(tender.id, ctx.session.sessionUser.accountId);
-            filter.count[filter.status.uncheck] = await ctx.service.financialPay.getCountByStatus(ctx.subProject.id, filter.status.uncheck, filterTids, used);// await ctx.service.change.checkingDatas(tender.id, ctx.session.sessionUser.accountId);
-            filter.count[filter.status.checking] = await ctx.service.financialPay.getCountByStatus(ctx.subProject.id, filter.status.checking, filterTids, used);// await ctx.service.change.checkedDatas(tender.id, ctx.session.sessionUser.accountId);
-            filter.count[filter.status.checked] = await ctx.service.financialPay.getCountByStatus(ctx.subProject.id, filter.status.checked, filterTids, used);// await ctx.service.change.pendingDatas(tender.id, ctx.session.sessionUser.accountId);
-            const payList = await ctx.service.financialPay.getListByStatus(ctx.subProject.id, status, filterTids, used, 1);
-            const total = await ctx.service.financialPay.getCountByStatus(ctx.subProject.id, status, filterTids, used);
+            const categoryData = await this.ctx.service.category.getAllCategory(ctx.subProject);
+            const tenders = hadTender ? await ctx.service.tender.getAllDataByCondition({ where: tenderCondition, columns: ['id', 'name', 'category'] }) : [];
+            const accountList = await ctx.service.projectAccount.getAllSubProjectAccount(ctx.subProject, ['id', 'account', 'name', 'company', 'company_id', 'role', 'enable', 'is_admin', 'account_group', 'mobile']);
+            const user = accountList.find(item => item.id === ctx.session.sessionUser.accountId) || null;
+            const userCompany = user ? ctx.helper._.find(unitList, { name: user.company }) : null;
+            if (!userCompany) {
+                throw '请联系管理员添加用户所在单位信息';
+            }
+            const userCompanyList = await ctx.service.financialPayCompany.getUserCompanyList(ctx.subProject.id, userCompany ? userCompany.id : 0, unitList);
+            const companyInfo = company ? ctx.helper._.find(unitList, { name: company }) : null;
+            const payList = await ctx.service.financialPayCompany.getListByStatus(ctx.subProject.id, companyInfo ? companyInfo.id : null, userCompany ? userCompany.id : 0);
+            const total = await ctx.service.financialPayCompany.getCountByStatus(ctx.subProject.id, companyInfo ? companyInfo.id : null, userCompany ? userCompany.id : 0);
             // 分页相关
             const page = ctx.page;
             const pageSize = ctx.pageSize;
@@ -653,45 +622,25 @@ module.exports = app => {
                 total: Math.ceil(total / pageSize),
                 queryData: JSON.stringify(ctx.urlInfo.query),
             };
-            // 获取所有项目参与者
-            const accountList = await ctx.service.projectAccount.getAllSubProjectAccount(ctx.subProject, ['id', 'account', 'name', 'company', 'role', 'enable', 'is_admin', 'account_group', 'mobile']);
             const payTenders = await ctx.service.financialPayTender.getAllDataByCondition({ where: { spid: ctx.subProject.id } });
             if (tenders.length > 0) {
-                const allLastPay = await ctx.service.financialPay.getAllDataByCondition({ where: { spid: ctx.subProject.id, tid: ctx.helper._.map(tenders, 'id') }, columns: ['id', 'code'], orders: [['id', 'desc']] });
                 const tenderInfos = await ctx.service.tenderInfo.getAllDataByCondition({ where: { tid: ctx.helper._.map(tenders, 'id') }, columns: ['tid', 'pay_account', 'deal_info'] });
                 for (const t of tenders) {
+                    t.category = t.category && t.category !== '' ? JSON.parse(t.category) : null;
                     const info = ctx.helper._.find(tenderInfos, { tid: t.id });
                     t.pay_account = info && info.pay_account ? JSON.parse(info.pay_account).project : '';
                     const pt = ctx.helper._.find(payTenders, { tid: t.id });
                     t.pt = pt ? pt : { id: 0, tid: t.id, name: '', bank: '', bank_account: '', contact: '', phone: '' };
                     if (ctx.session.sessionUser.is_admin || ctx.helper._.includes(fptReportTids, t.id)) {
                         t.dealCode = info && info.deal_info ? JSON.parse(info.deal_info).dealCode : '';
-                        const lastPay = ctx.helper._.filter(allLastPay, { tid: t.id });
-                        t.startNum = 1;
-                        if (lastPay.length > 0) {
-                            const startArray = lastPay[0].code.split('-');
-                            t.startNum = parseInt(startArray[startArray.length - 1]) + 1;
-                        }
                     }
                 }
             }
             for (const pay of payList) {
-                const t = ctx.helper._.find(tenders, { id: pay.tid });
-                pay.tenderName = t ? t.name : '';
+                const t = ctx.helper._.find(unitList, { id: pay.company_id });
+                pay.company = t ? t.name : '';
                 const userInfo = ctx.helper._.find(accountList, { id: pay.uid });
                 pay.username = userInfo ? userInfo.name : '';
-                if (pay.status !== auditConst.financial.status.checked || !pay.final_auditor_str) {
-                    pay.curAuditors = await ctx.service.financialPayAudit.getAuditorsByStatus(pay.id, pay.status, pay.times);
-                    if (pay.status === auditConst.financial.status.checked && pay.curAuditors.length > 0) {
-                        const final_auditor_str = pay.curAuditors[0].audit_type === auditConst.auditType.key.common
-                            ? pay.curAuditors[0].name + (pay.curAuditors[0].role ? '-' + pay.curAuditors[0].role : '')
-                            : ctx.helper.transFormToChinese(pay.curAuditors[0].audit_order) + '审';
-                        await ctx.service.financialPay.defaultUpdate({ final_auditor_str }, { where: { id: pay.id } });
-                    }
-                }
-                if (pay.status !== auditConst.financial.status.checked) {
-                    pay.entities = await ctx.service.financialPayContract.getEntities(pay.id);
-                }
             }
             if (ctx.session.sessionUser.is_admin && tenders.length > 0) {
                 const allPermissionList = await ctx.service.financialPayTenderAudit.getAllList(ctx.subProject.id, ctx.helper._.map(tenders, 'id'), accountList);
@@ -703,25 +652,24 @@ module.exports = app => {
                 }
             }
             const renderData = {
+                categoryData,
                 tenders,
                 financialPermission,
                 usedList: financialConst.used,
                 auditConst: auditConst.financial,
-                filter,
-                tid,
-                status,
-                used,
+                company,
                 payList,
                 fptAuditTids,
                 fptReportTids,
+                userCompanyList,
+                unitList,
                 moment,
+                user,
                 auditType: auditConst.auditType,
-                // preUrl: '/financial',
                 jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.financial.pay),
                 pageInfo,
             };
             if (ctx.session.sessionUser.is_admin) {
-                const unitList = await ctx.service.constructionUnit.getAllDataByCondition({ where: { pid: ctx.session.sessionProject.id } });
                 renderData.accountList = accountList;
                 renderData.accountGroup = unitList.map(item => {
                     const groupList = accountList.filter(item1 => item1.company === item.name);
@@ -785,11 +733,14 @@ module.exports = app => {
                     case 'copy-shenpi-audit':
                         responseData.data = await ctx.service.financialPayTenderAudit.copyShenpi2otherTender(ctx.subProject.id, data, data.this_tid);
                         break;
-                    case 'add-pay':
-                        responseData.data = await ctx.service.financialPay.addPay(ctx.subProject.id, data.updateData);
+                    case 'copy-tender-bank':
+                        responseData.data = await ctx.service.financialPayTender.copyBank2otherTender(ctx.subProject.id, data.tenders, data.this_tender);
                         break;
-                    case 'del-pay':
-                        responseData.data = await ctx.service.financialPay.delPay(data.postData.node);
+                    case 'add-pay-company':
+                        responseData.data = await ctx.service.financialPayCompany.addPayCompany(ctx.subProject.id, data.updateData);
+                        break;
+                    case 'del-pay-company':
+                        responseData.data = await ctx.service.financialPayCompany.delPayCompany(data.postData.node);
                         break;
                     case 'set-pay-tender':
                         responseData.data = await ctx.service.financialPayTender.savePayTender(ctx.subProject.id, data.updateData);
@@ -810,6 +761,193 @@ module.exports = app => {
         }
 
         /**
+         * 变更管理 页面 (Get)
+         *
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        async payCompany(ctx) {
+            try {
+                const status = parseInt(ctx.query.status) || 0;
+                const tid = parseInt(ctx.query.tid) || null;
+                const used = ctx.query.used || null;
+                await this._filterPayCompany(ctx, status, tid, used);
+            } catch (err) {
+                this.log(err);
+                ctx.redirect(`/sp/${ctx.subProject.id}/dashboard`);
+            }
+        }
+
+        async _filterPayCompany(ctx, status = 0, tid = null, used = null) {
+            const financialPermission = await ctx.service.subProjPermission.getFinancailPermission(ctx.subProject.permission.fund_trans_permission, ctx.subProject.permission.fund_pay_permission);
+            if (!financialPermission.pay_show) {
+                throw '没有查看权限';
+            }
+            const payCompany = await ctx.service.financialPayCompany.getOnePayCompany(ctx.params.fpcid);
+            if (!payCompany) {
+                throw '该支付单位期不存在';
+            }
+            const fptAudits = await ctx.service.financialPayTenderAudit.getAllDataByCondition({ where: { spid: ctx.subProject.id, uid: ctx.session.sessionUser.accountId } });
+            const fptAuditTids = ctx.helper._.map(fptAudits, 'tid');
+            const fptReportTids = ctx.helper._.map(ctx.helper._.filter(fptAudits, { is_report: 1 }), 'tid');
+            const unitList = await ctx.service.constructionUnit.getAllDataByCondition({ where: { pid: ctx.session.sessionProject.id } });
+            const filterTids = tid === null ? (ctx.session.sessionUser.is_admin ? null : fptAuditTids) : [tid];
+            const tenderCondition = { spid: ctx.subProject.id, filter_fund: 0 };
+            let hadTender = false;
+            if (ctx.session.sessionUser.is_admin) {
+                hadTender = true;
+            } else if (fptAuditTids.length !== 0) {
+                hadTender = true;
+                tenderCondition.id = fptAuditTids;
+            } else {
+                hadTender = false;
+                tenderCondition.id = -1;
+            }
+            const categoryData = await this.ctx.service.category.getAllCategory(ctx.subProject);
+            const tenders = hadTender ? await ctx.service.tender.getAllDataByCondition({ where: tenderCondition, columns: ['id', 'name', 'category'] }) : [];
+            const filter = JSON.parse(JSON.stringify(auditConst.financial.filter));
+            filter.count = [];
+            filter.count[filter.status.pending] = await ctx.service.financialPay.getCountByStatus(ctx.subProject.id, payCompany.id, filter.status.pending, filterTids, used);// await ctx.service.change.pendingDatas(tender.id, ctx.session.sessionUser.accountId);
+            filter.count[filter.status.uncheck] = await ctx.service.financialPay.getCountByStatus(ctx.subProject.id, payCompany.id, filter.status.uncheck, filterTids, used);// await ctx.service.change.checkingDatas(tender.id, ctx.session.sessionUser.accountId);
+            filter.count[filter.status.checking] = await ctx.service.financialPay.getCountByStatus(ctx.subProject.id, payCompany.id, filter.status.checking, filterTids, used);// await ctx.service.change.checkedDatas(tender.id, ctx.session.sessionUser.accountId);
+            filter.count[filter.status.checked] = await ctx.service.financialPay.getCountByStatus(ctx.subProject.id, payCompany.id, filter.status.checked, filterTids, used);// await ctx.service.change.pendingDatas(tender.id, ctx.session.sessionUser.accountId);
+            const payList = await ctx.service.financialPay.getListByStatus(ctx.subProject.id, payCompany.id, status, filterTids, used, 1);
+            const total = await ctx.service.financialPay.getCountByStatus(ctx.subProject.id, payCompany.id, status, filterTids, used);
+            // 分页相关
+            const page = ctx.page;
+            const pageSize = ctx.pageSize;
+            const pageInfo = {
+                page,
+                pageSizeSelect: 1,
+                pageSize,
+                total_num: total,
+                total: Math.ceil(total / pageSize),
+                queryData: JSON.stringify(ctx.urlInfo.query),
+            };
+            // 获取所有项目参与者
+            const accountList = await ctx.service.projectAccount.getAllSubProjectAccount(ctx.subProject, ['id', 'account', 'name', 'company', 'company_id', 'role', 'enable', 'is_admin', 'account_group', 'mobile']);
+            const payTenders = await ctx.service.financialPayTender.getAllDataByCondition({ where: { spid: ctx.subProject.id } });
+            if (tenders.length > 0) {
+                const allLastPay = await ctx.service.financialPay.getAllDataByCondition({ where: { spid: ctx.subProject.id, tid: ctx.helper._.map(tenders, 'id') }, columns: ['id', 'tid', 'code'], orders: [['id', 'desc']] });
+                const tenderInfos = await ctx.service.tenderInfo.getAllDataByCondition({ where: { tid: ctx.helper._.map(tenders, 'id') }, columns: ['tid', 'pay_account', 'deal_info'] });
+                for (const t of tenders) {
+                    t.category = t.category && t.category !== '' ? JSON.parse(t.category) : null;
+                    const info = ctx.helper._.find(tenderInfos, { tid: t.id });
+                    t.pay_account = info && info.pay_account ? JSON.parse(info.pay_account).project : '';
+                    const pt = ctx.helper._.find(payTenders, { tid: t.id });
+                    t.pt = pt ? pt : { id: 0, tid: t.id, name: '', bank: '', bank_account: '', contact: '', phone: '' };
+                    if (ctx.session.sessionUser.is_admin || ctx.helper._.includes(fptReportTids, t.id)) {
+                        t.dealCode = info && info.deal_info ? JSON.parse(info.deal_info).dealCode : '';
+                        const lastPay = ctx.helper._.filter(allLastPay, { tid: t.id });
+                        t.startNum = 1;
+                        if (lastPay.length > 0) {
+                            const startArray = lastPay[0].code.split('-');
+                            t.startNum = parseInt(startArray[startArray.length - 1]) + 1;
+                        }
+                    }
+                }
+            }
+            for (const pay of payList) {
+                const t = ctx.helper._.find(tenders, { id: pay.tid });
+                pay.tenderName = t ? t.name : '';
+                const userInfo = ctx.helper._.find(accountList, { id: pay.uid });
+                pay.username = userInfo ? userInfo.name : '';
+                if (pay.status !== auditConst.financial.status.checked || !pay.final_auditor_str) {
+                    pay.curAuditors = await ctx.service.financialPayAudit.getAuditorsByStatus(pay.id, pay.status, pay.times);
+                    if (pay.status === auditConst.financial.status.checked && pay.curAuditors.length > 0) {
+                        const final_auditor_str = pay.curAuditors[0].audit_type === auditConst.auditType.key.common
+                            ? pay.curAuditors[0].name + (pay.curAuditors[0].role ? '-' + pay.curAuditors[0].role : '')
+                            : ctx.helper.transFormToChinese(pay.curAuditors[0].audit_order) + '审';
+                        await ctx.service.financialPay.defaultUpdate({ final_auditor_str }, { where: { id: pay.id } });
+                    }
+                }
+                if (pay.status !== auditConst.financial.status.checked) {
+                    pay.entities = await ctx.service.financialPayContract.getEntities(pay.id);
+                }
+            }
+            if (ctx.session.sessionUser.is_admin && tenders.length > 0) {
+                const allPermissionList = await ctx.service.financialPayTenderAudit.getAllList(ctx.subProject.id, ctx.helper._.map(tenders, 'id'), accountList);
+                const allAuditGroupList = await ctx.service.shenpiAudit.getAllAuditGroupList(ctx.helper._.map(tenders, 'id'), shenpiConst.sp_other_type.financial, shenpiConst.sp_status.gdspl);
+                for (const t of tenders) {
+                    t.permissionList = ctx.helper._.filter(allPermissionList, { tid: t.id }) || [];
+                    // t.auditGroupList = await ctx.service.shenpiAudit.getAuditGroupList(t.id, shenpiConst.sp_other_type.financial, shenpiConst.sp_status.gdspl);
+                    t.auditGroupList = ctx.helper._.find(allAuditGroupList, { tid: t.id }) ? ctx.helper._.find(allAuditGroupList, { tid: t.id }).audits : [];
+                }
+            }
+            const notCompanyPays = await ctx.service.financialPay.getAllDataByCondition({ where: { spid: ctx.subProject.id, fpcid: null } });
+            for (const pay of notCompanyPays) {
+                const t = ctx.helper._.find(tenders, { id: pay.tid });
+                pay.tenderName = t ? t.name : '';
+                const userInfo = ctx.helper._.find(accountList, { id: pay.uid });
+                pay.username = userInfo ? userInfo.name : '';
+            }
+            const renderData = {
+                categoryData,
+                tenders,
+                financialPermission,
+                usedList: financialConst.used,
+                auditConst: auditConst.financial,
+                filter,
+                tid,
+                status,
+                used,
+                payList,
+                payCompany,
+                notCompanyPays,
+                fptAuditTids,
+                fptReportTids,
+                unitList,
+                moment,
+                auditType: auditConst.auditType,
+                // preUrl: '/financial',
+                jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.financial.payCompany),
+                pageInfo,
+            };
+            if (ctx.session.sessionUser.is_admin) {
+                renderData.accountList = accountList;
+                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; });
+                renderData.auditType = auditConst.auditType;
+                renderData.shenpi = shenpiConst;
+            }
+            await this.layout('financial/pay_company.ejs', renderData, 'financial/pay_company_modal.ejs');
+        }
+
+        async payCompanySave(ctx) {
+            try {
+                const responseData = {
+                    err: 0, msg: '', data: {},
+                };
+                const data = JSON.parse(ctx.request.body.data);
+                if (!data.type) {
+                    throw '提交数据错误';
+                }
+                const payCompany = await ctx.service.financialPayCompany.getDataById(ctx.params.fpcid);
+                if (!payCompany) {
+                    throw '资金支付单位期不存在';
+                }
+                switch (data.type) {
+                    case 'batch-old-pays':
+                        await ctx.service.financialPay.batchOldPays(ctx.subProject.id, payCompany, data.postData.payIds);
+                        break;
+                    case 'add-pay':
+                        responseData.data = await ctx.service.financialPay.addPay(ctx.subProject.id, payCompany, data.updateData);
+                        break;
+                    case 'del-pay':
+                        responseData.data = await ctx.service.financialPay.delPay(payCompany, data.postData.node);
+                        break;
+                    default: throw '参数有误';
+                }
+                ctx.body = responseData;
+            } catch (err) {
+                this.log(err);
+                ctx.body = { err: 1, msg: err.toString(), data: {} };
+            }
+        }
+
+        /**
          * 获取审批界面所需的 原报、审批人数据等
          * @param ctx
          * @return {Promise<void>}
@@ -862,6 +1000,9 @@ module.exports = app => {
                 if (!ctx.financialPay) {
                     throw '数据错误';
                 }
+                if (!ctx.financialPay.fpcid || !ctx.financialPay.payCompany) {
+                    throw '未关联资金支付单位期无法进行任何操作';
+                }
                 if (ctx.financialPay.uid !== ctx.session.sessionUser.accountId) {
                     throw '您无权上报该资金支付数据';
                 }
@@ -889,6 +1030,9 @@ module.exports = app => {
                 if (!ctx.financialPay || ctx.financialPay.status !== auditConst.financial.status.checking) {
                     throw '当前资金支付数据有误';
                 }
+                if (!ctx.financialPay.fpcid || !ctx.financialPay.payCompany) {
+                    throw '未关联资金支付单位期无法进行任何操作';
+                }
                 if (ctx.financialPay.curAuditorIds.length === 0 || ctx.financialPay.curAuditorIds.indexOf(ctx.session.sessionUser.accountId) === -1) {
                     throw '您无权进行该操作';
                 }
@@ -916,6 +1060,9 @@ module.exports = app => {
          */
         async checkPayAgain(ctx) {
             try {
+                if (!ctx.financialPay.fpcid || !ctx.financialPay.payCompany) {
+                    throw '未关联资金支付单位期无法进行任何操作';
+                }
                 // 获取终审
                 // const auditInfo = await this.ctx.service.changeProjectAudit.getAuditorByStatus(ctx.change.id, audit.changeProject.status.checked);
                 // if (ctx.change.status !== audit.changeProject.status.checked || ctx.session.sessionUser.accountId !== auditInfo.aid) {
@@ -964,6 +1111,9 @@ module.exports = app => {
                 const responseData = {
                     err: 0, msg: '', data: {},
                 };
+                if (!ctx.financialPay.fpcid || !ctx.financialPay.payCompany) {
+                    throw '未关联资金支付单位期无法进行任何操作';
+                }
                 const data = JSON.parse(ctx.request.body.data);
                 if (!data.type) {
                     throw '提交数据错误';
@@ -994,13 +1144,13 @@ module.exports = app => {
                         responseData.data = await ctx.service.financialPayContract.addContracts(ctx.financialPay, data.contract_ids);
                         break;
                     case 'contract_del':
-                        responseData.data = await ctx.service.financialPayContract.delContract(ctx.financialPay.id, data.ids);
+                        responseData.data = await ctx.service.financialPayContract.delContract(ctx.financialPay.id, ctx.financialPay.payCompany, data.ids);
                         break;
                     case 'contract_update':
-                        responseData.data = await ctx.service.financialPayContract.updateContract(ctx.financialPay.id, data.updateData);
+                        responseData.data = await ctx.service.financialPayContract.updateContract(ctx.financialPay.id, ctx.financialPay.payCompany, data.updateData);
                         break;
                     case 'contract_paste':
-                        responseData.data = await ctx.service.financialPayContract.updateContracts(ctx.financialPay.id, data.updateData);
+                        responseData.data = await ctx.service.financialPayContract.updateContracts(ctx.financialPay.id, ctx.financialPay.payCompany, data.updateData);
                         break;
                     case 'file_bill':
                         responseData.data = await ctx.service.financialPayAtt.updateBill(data.id, data.bill);
@@ -1024,6 +1174,9 @@ module.exports = app => {
                 const parts = this.ctx.multipart({
                     autoFields: true,
                 });
+                if (!ctx.financialPay.fpcid || !ctx.financialPay.payCompany) {
+                    throw '未关联资金支付单位期无法进行任何操作';
+                }
                 const files = [];
                 const create_time = Date.parse(new Date()) / 1000;
                 let idx = 0;
@@ -1083,6 +1236,9 @@ module.exports = app => {
          */
         async payDeleteFile(ctx) {
             try {
+                if (!ctx.financialPay.fpcid || !ctx.financialPay.payCompany) {
+                    throw '未关联资金支付单位期无法进行任何操作';
+                }
                 const { fpcid, id } = JSON.parse(ctx.request.body.data);
                 const fileInfo = await ctx.service.financialPayAtt.getDataById(id);
                 if (fileInfo || Object.keys(fileInfo).length) {

+ 59 - 0
app/controller/wap_controller.js

@@ -19,6 +19,9 @@ const path = require('path');
 const sendToWormhole = require('stream-wormhole');
 const moment = require('moment');
 const auditType = require('../const/audit').auditType;
+const AiInspect = require('../lib/ai_inspect');
+const uuid = require('node-uuid');
+const nlsToken = require('../lib/nls_token');
 
 module.exports = app => {
 
@@ -1176,6 +1179,62 @@ module.exports = app => {
                 ctx.redirect('/wap/dashboard');
             }
         }
+
+        async inspection(ctx) {
+            const renderData = {
+            };
+            await ctx.render('wap/inspection.ejs', renderData);
+        }
+
+        async inspectionAiAsk(ctx) {
+            const responseData = {
+                err: 0,
+                msg: '',
+                data: '',
+            };
+            try {
+                const { user_input, conversationId } = JSON.parse(ctx.request.body.data);
+                if (!user_input) {
+                    throw '参数有误';
+                }
+                const ai = new AiInspect({
+                    chatflowId: conversationId,
+                });
+                const result = await ai.ask(user_input);
+                let answerText = result.answer || '';
+                let jsonPart = null;
+                const jsonMatch = answerText.match(/\{[\s\S]*\}/);
+                if (jsonMatch) {
+                    jsonPart = JSON.parse(jsonMatch[0]);
+                    answerText = answerText.replace(jsonMatch[0], '').trim();
+                }
+                responseData.data = {
+                    answer_text: answerText,
+                    answer_json: jsonPart,
+                    conversation_id: result.conversation_id || '',
+                };
+            } catch (error) {
+                responseData.err = 1;
+                responseData.msg = error.message ? error.message : error;
+            }
+            ctx.body = responseData;
+        }
+
+        async voiceToken(ctx) {
+            const responseData = {
+                err: 0,
+                msg: '',
+                data: '',
+            };
+            try {
+                const token = await nlsToken.getToken();
+                responseData.data = token;
+            } catch (error) {
+                responseData.err = 1;
+                responseData.msg = error.message ? error.message : error;
+            }
+            ctx.body = responseData;
+        }
     }
 
     return WapController;

+ 53 - 0
app/lib/ai_inspect.js

@@ -0,0 +1,53 @@
+'use strict';
+const axios = require('axios');
+
+class AiInspect {
+    /**
+     * 构造函数
+     * @param {object} config
+     * @param {string} config.apiKey - Dify API Key
+     * @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' }) {
+        if (!apiKey) {
+            throw new Error('apiKey 不能为空');
+        }
+
+        this.apiKey = apiKey;
+        this.chatflowId = chatflowId;
+        this.baseUrl = baseUrl;
+    }
+
+    /**
+     * 向 ChatFlow 发送请求
+     * @param {string} userInput - 用户输入内容
+     * @param {string} [userId='user_123'] - 用户 ID
+     * @returns {Promise<object>} - Dify 返回的对象
+     */
+    async ask(userInput, userId = 'user_123') {
+        const url = `${this.baseUrl}/chat-messages`;
+        const headers = {
+            'Authorization': `Bearer ${this.apiKey}`,
+            'Content-Type': 'application/json',
+        };
+
+        const body = {
+            inputs: {}, // 如果你的 ChatFlow 有 schema,可填写 key-value
+            query: userInput,
+            response_mode: 'blocking',
+            user: userId,
+            conversation_id: this.chatflowId,
+        };
+
+        try {
+            const res = await axios.post(url, body, { headers });
+            return res.data;
+        } catch (err) {
+            console.error('Dify 请求失败:', err.response || err.response.data || err.message);
+            throw new Error('Dify 请求失败');
+        }
+    }
+}
+
+module.exports = AiInspect;

+ 41 - 0
app/lib/nls_token.js

@@ -0,0 +1,41 @@
+'use strict';
+
+/**
+ * 阿里云获取语音token接口
+ *
+ * @author CaiAoLin
+ * @date 2018/1/25
+ * @version
+ */
+
+const Core = require('@alicloud/pop-core');
+const smsAli = require('../const/sms_alitemplate.js');
+class NlsToken {
+
+    async getToken() {
+        const client = new Core({
+            accessKeyId: smsAli.accessKey,
+            accessKeySecret: smsAli.accessKeySecret,
+            endpoint: 'https://nls-meta.cn-shanghai.aliyuncs.com', // 注意语音服务用这个endpoint
+            apiVersion: '2019-02-28',
+        });
+
+        const params = {
+            Action: 'CreateToken',
+        };
+
+        const requestOption = {
+            method: 'POST',
+        };
+
+        try {
+            const result = await client.request('CreateToken', params, requestOption);
+            return result.Token;
+        } catch (err) {
+            this.ctx.logger.error('获取语音识别Token失败', err);
+            throw err;
+        }
+    }
+}
+
+module.exports = NlsToken;

+ 210 - 316
app/public/js/financial_pay.js

@@ -1,42 +1,84 @@
+'use strict';
+
+const tenderListSpec = (function(){
+    function getTenderNodeHtml(node, arr, pid) {
+        const html = [];
+        html.push('<tr pid="' + pid + '" data-tid="'+ (!node.cid ? node.id : -1) +'"' + (!node.cid ? 'class="change-tender" style="cursor: pointer;"' : '') + '>');
+        html.push('<td style="width: 60px" class="text-center">');
+        if (!node.cid) {
+            html.push(`<input type="checkbox" class="tender-check" name="tender_id[]" value="${node.id}" ${_.findIndex(tenders, { tid: node.id }) !== -1 ? 'checked disabled' : ''} ${is_admin || (fptReportTids && _.includes(fptReportTids, node.id)) ? '' : 'disabled' } />`);
+        }
+        html.push('</td>');
+        // 名称
+        html.push('<td class="in-' + node.level + '">');
+        if (node.cid) {
+            html.push('<span onselectstart="return false" style="{-moz-user-select:none}" class="fold-switch mr-1" title="收起" cid="'+ node.sort_id +'"><i class="fa fa-minus-square-o"></i></span> <i class="fa fa-folder-o"></i> ');
+            html.push((node.level === 1 ? '<b>' : ''), node.name, (node.level === 1 ? '</b>' : ''));
+        } else {
+            html.push('<span class="text-muted mr-2">');
+            html.push(arr.indexOf(node) === arr.length - 1 ? '└' : '├');
+            html.push('</span>');
+            html.push(node.name);
+        }
+        html.push('</td>');
+        html.push('</tr>');
+        return html.join('');
+    }
+    function getTenderTreeHeaderHtml() {
+        const html = [];
+        const left = $('#sub-menu').css('display') === 'none' ? 56 : 176;
+        html.push('<table class="table table-hover table-bordered" id="progress-table">')
+        html.push('<thead style="position: sticky;left:'+ left +'px;top: 0;">', '<tr>');
+        html.push('<th class="text-center" style="width: 60px;">', '选择', '</th>');
+        html.push('<th class="text-center">', '标段名称', '</th>');
+        html.push('</tr>', '</thead>');
+        return html.join('');
+    }
+    return { getTenderNodeHtml, getTenderTreeHeaderHtml }
+})();
+
+
+
 let auditUtils;
 $(function () {
     autoFlashHeight();
 
-    $('#tid_select').select2({
-        language: 'zh-CN',
-        theme: 'bootstrap4',
-        selectOnClose: true,
-        // width: '150',
-    });
-
-    $('#tid_select').change(function () {
-        const tid = parseInt($(this).val()) || 0;
-        setSelectValue('tid', tid);
+    $('#liucheng').on('shown.bs.modal', function () {
+        tenderListOrder.reOrderTenders();
+        initTenderTree();
+        $('#shenpi-tender-list').html(getTenderTreeHtml());
+        localHideList();
+        $('#shenpi-tender-list tr').removeClass('bg-warning');
+        if (tenders.length > 0) {
+            $('#shenpi-tender-list tr.change-tender').eq(0).addClass('bg-warning');
+            auditUtils.makeReportListHtml(tenders[0]);
+            auditUtils.makeShenpiListHtml(tenders[0]);
+        }
     });
 
-    $('#status_select .to-log-link').click(function () {
-        const status = parseInt($(this).data('val')) || null;
-        setSelectValue('status', status);
+    $('#payaccount').on('shown.bs.modal', function () {
+        tenderListOrder.reOrderTenders();
+        initTenderTree();
+        $('#pay-tender-list').html(getTenderTreeHtml());
+        localHideList();
+        $('#pay-tender-list tr').removeClass('bg-warning');
+        if (tenders.length > 0) {
+            $('#pay-tender-list tr.change-tender').eq(0).addClass('bg-warning');
+            auditUtils.makeBankHtml(tenders[0]);
+        }
     });
 
-    $('#used_select .to-log-link').click(function () {
-        const used = $(this).data('val') || null;
-        setSelectValue('used', used);
+    $('#company_select').change(function () {
+        const company_id = parseInt($(this).val()) || 0;
+        setSelectValue('company', company_id);
     });
 
     function setSelectValue(select, value) {
         const routes = [];
-        const tid = select === 'tid' ? value : $('#tid_select').val();
-        if (tid) {
-            routes.push('tid=' + tid);
-        }
-        const status = select === 'status' ? value : $('#status_selected').data('value');
-        if (status) {
-            routes.push('status=' + status);
-        }
-        const used = select === 'used' ? value : $('#used_selected').data('value');
-        if (used) {
-            routes.push('used=' + used);
+        const company_id = select === 'company' ? value : parseInt($('#company_select').val());
+        if (company_id) {
+            const companyInfo = _.find(userCompanyList, { id: company_id });
+            if (companyInfo) routes.push('company=' + companyInfo.name);
         }
         window.location.href = `/sp/${spid}/financial/pay` + (routes.length ? '?' + routes.join('&') : '');
     }
@@ -83,29 +125,35 @@ $(function () {
         }, 400);
     });
 
-    $('#liucheng').on('show.bs.modal', function (e) {
+    $('body').on('click', '#shenpi-tender-list .change-tender', function () {
+        if ($(this).hasClass('bg-warning')) {
+            return;
+        }
         $('#shenpi-tender-list tr').removeClass('bg-warning');
-        if (tenders.length > 0) {
-            $('#shenpi-tender-list tr').eq(0).addClass('bg-warning');
-            auditUtils. makeReportListHtml(tenders[0]);
-            auditUtils.makeShenpiListHtml(tenders[0]);
+        $(this).addClass('bg-warning');
+        const tid = parseInt($(this).data('tid')) || 0;
+        const tender = tenders.find(t => t.id === tid);
+        if (!tender) {
+            toastr.error('请选择标段');
+            return;
         }
+        auditUtils.makeReportListHtml(tender);
+        auditUtils.makeShenpiListHtml(tender);
     });
 
-    $('#shenpi-tender-list').on('click', '.change-tender', function () {
+    $('body').on('click', '#pay-tender-list .change-tender', function () {
         if ($(this).hasClass('bg-warning')) {
             return;
         }
-        $('#shenpi-tender-list tr').removeClass('bg-warning');
-        $(this).parents('tr').addClass('bg-warning');
-        const tid = parseInt($(this).parents('tr').data('tid')) || 0;
+        $('#pay-tender-list tr').removeClass('bg-warning');
+        $(this).addClass('bg-warning');
+        const tid = parseInt($(this).data('tid')) || 0;
         const tender = tenders.find(t => t.id === tid);
         if (!tender) {
             toastr.error('请选择标段');
             return;
         }
-        auditUtils.makeReportListHtml(tender);
-        auditUtils.makeShenpiListHtml(tender);
+        auditUtils.makeBankHtml(tender);
     });
     auditUtils = {
         makeReportListHtml: function (flow) {
@@ -125,6 +173,29 @@ $(function () {
             addhtml += '</ul>\n';
             $('#shenpi-list').html(addhtml);
         },
+        makeBankHtml: function (tender) {
+            $('#payaccount input[name="name"]').val('');
+            $('#payaccount input[name="bank"]').val('');
+            $('#payaccount input[name="bank_account"]').val('');
+            $('#payaccount input[name="contact"]').val('');
+            $('#payaccount input[name="phone"]').val('');
+            if (tender) {
+                $('#payaccount input[name="name"]').val(tender.pt.name);
+                $('#payaccount input[name="bank"]').val(tender.pt.bank);
+                $('#payaccount input[name="bank_account"]').val(tender.pt.bank_account);
+                $('#payaccount input[name="contact"]').val(tender.pt.contact);
+                $('#payaccount input[name="phone"]').val(tender.pt.phone);
+                if (is_admin || (fptReportTids && _.includes(fptReportTids, tender.id))) {
+                    $('#payaccount table input[type="text"]').attr('readonly', false);
+                    $('#get-form-tender').show();
+                    $('#batch-other-bank').show();
+                } else {
+                    $('#payaccount table input[type="text"]').attr('readonly', true);
+                    $('#get-form-tender').hide();
+                    $('#batch-other-bank').hide();
+                }
+            }
+        },
         getAuditHtml: function(audit, is_report = 0) {
             return '<span class="d-inline-block"><span class="badge badge-light">'+ audit.name +' <span class="dropdown">\n' +
                 '                                                            <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>\n' +
@@ -376,6 +447,43 @@ $(function () {
         });
     });
 
+    $('#batch-other-bank').click(function () {
+        const cur_tenderid = parseInt($('#pay-tender-list tr.bg-warning').data('tid'));
+        const tender = tenders.find(t => t.id === cur_tenderid);
+        if (!tender) {
+            toastr.error('请选择标段');
+            return;
+        }
+        const copyTenders = [];
+        $('#pay-tender-list input:checked').each(function () {
+            if (cur_tenderid !== parseInt($(this).val())) {
+                const one = tenders.find(t => t.id === parseInt($(this).val()));
+                copyTenders.push(one.pt);
+            }
+        });
+        if (copyTenders.length === 0) {
+            toastr.warning('请勾选要同步的标段');
+            return;
+        }
+        const data = {
+            type: 'copy-tender-bank',
+            tenders: copyTenders,
+            this_tender: tender.pt,
+        }
+        postData(`/sp/${spid}/financial/pay/save`,  data, function (result) {
+            toastr.success('已同步信息到其它勾选标段');
+            $('#pay-tender-list input:checked').each(function () {
+                if (cur_tenderid !== parseInt($(this).val())) {
+                    const one = tenders.find(t => t.id === parseInt($(this).val()));
+                    const newOne = result.find(t => t.tid === one.id);
+                    if (newOne) {
+                        one.pt = newOne;
+                    }
+                }
+            });
+        });
+    });
+
     // 选中审批人
     $('body').on('click', '#shenpi-list div[id$="_dropdownMenu"] dl dd', function () {
         const tid = parseInt($('#shenpi-tender-list tr.bg-warning').data('tid'));
@@ -574,117 +682,98 @@ $(function () {
         return false
     });
 
-    $('#add-pay').on('show.bs.modal', function () {
-        let t = null;
-        if (tenders.length > 0) {
-            if (is_admin) {
-                t = tenders[0];
-            } else {
-                const filterTender = tenders.filter(t => _.includes(fptReportTids, t.id));
-                if (filterTender.length > 0) {
-                    t = filterTender[0];
-                }
-            }
-        }
-        $('#add-pay-tender').val(t ? t.id : '');
-        changeTender(t);
-    });
 
-    $('#add-pay-tender').on('change', function () {
-        const tender = tenders.find(t => t.id === parseInt($(this).val()));
-        changeTender(tender);
+    $('#add-company-select').on('change', function () {
+        const company = userCompanyList.find(t => t.name === $(this).val());
+        $('#company_order').val(company ? company.count + 1 : 0);
     });
 
-    $('#add-pay-btn').on('click', function () {
-        const tid = $('#add-pay-tender').val();
-        const tender = tenders.find(t => t.id === parseInt(tid));
-        if (!tender) {
-            toastr.error('请选择支付标段');
+    $('#add-qi-btn').on('click', function () {
+        const companyName = $('#add-company-select').val();
+        const company = userCompanyList.find(t => t.name === companyName);
+        if (!company) {
+            toastr.error('单位不存在');
             return;
         }
-        const code = $('#add-pay-code').val();
-        if (!code) {
-            toastr.error('请先去标段属性,填写合同编号');
+        const order = parseInt($('#company_order').val());
+        if (!order || order < 1) {
+            toastr.error('期数有误');
             return;
         }
         const prop = {
-            tid: tender.id,
-            code: code,
-            used: $('#add-pay-used').val(),
+            company_id: company.id,
+            order,
         };
-        postData(`/sp/${spid}/financial/pay/save`, { type: 'add-pay', updateData: prop }, function (result) {
-            window.location.href = `/sp/${spid}/financial/pay/${result.id}/detail`;
+        postData(`/sp/${spid}/financial/pay/save`, { type: 'add-pay-company', updateData: prop }, function (result) {
+            window.location.href = `/sp/${spid}/financial/pay/company/${result.id}`;
         });
     });
 
     $('body').on('click', '#pay-list .del-pay-btn', function () {
-        const fpid = $(this).data('id');
+        const fpcid = $(this).data('id');
         deleteAfterHint(function () {
-            postData(`/sp/${spid}/financial/pay/save`, {type: 'del-pay', postData: { node: fpid }}, function (result) {
+            postData(`/sp/${spid}/financial/pay/save`, {type: 'del-pay-company', postData: { node: fpcid }}, function (result) {
                 window.location.reload();
             })
-        }, '确认删除该资金支付?');
+        }, '确认删除该资金支付单位期?');
     });
 
-    $('#pay-list tr').on('click', function () {
-        const tid = parseInt($(this).data('tid'));
-        const tender = tenders.find(t => t.id === tid);
-        if (tid && tender) {
-            $(this).siblings().removeClass('alert-warning');
-            $(this).addClass('alert-warning');
-            $('#show-pay-account').show();
-            $('#payaccount input[name="tid"]').val(tender.id);
-        } else {
-            $(this).siblings().removeClass('alert-warning');
-            $('#show-pay-account').hide();
-            $('#payaccount input[name="tid"]').val('');
+    $('#get-form-tender').on('click', function () {
+        const tid = $('#pay-tender-list tr.bg-warning').data('tid');
+        const tender = tenders.find(t => t.id === parseInt(tid));
+        if (!tid || !tender) {
+            toastr.error('标段不存在');
+            return;
         }
-    });
-
-    $('#payaccount').on('show.bs.modal', function () {
-        const tid = parseInt($('#payaccount input[name="tid"]').val());
-        const tender = tenders.find(t => t.id === tid);
-        $('#payaccount .modal-title').text('付款账号()');
-        $('#payaccount input[name="name"]').val('');
-        $('#payaccount input[name="bank"]').val('');
-        $('#payaccount input[name="bank_account"]').val('');
-        $('#payaccount input[name="contact"]').val('');
-        $('#payaccount input[name="phone"]').val('');
-        if (tid && tender) {
-            $('#payaccount .modal-title').text('付款账号(' + tender.name + ')');
-            $('#payaccount input[name="name"]').val(tender.pt.name);
-            $('#payaccount input[name="bank"]').val(tender.pt.bank);
-            $('#payaccount input[name="bank_account"]').val(tender.pt.bank_account);
-            $('#payaccount input[name="contact"]').val(tender.pt.contact);
-            $('#payaccount input[name="phone"]').val(tender.pt.phone);
-            if (is_admin || (fptReportTids && _.includes(fptReportTids, tid))) {
-                $('#payaccount table input').attr('readonly', false);
-                $('#get-form-tender').show();
-                $('#set-pay-btn').show();
-                $('#payaccount input[name="id"]').val(tender.pt.id);
-            } else {
-                $('#payaccount table input').attr('readonly', true);
-                $('#get-form-tender').hide();
-                $('#set-pay-btn').hide();
-                $('#payaccount input[name="id"]').val('');
-                $('#payaccount input[name="tid"]').val('');
+        if (is_admin || (fptReportTids && _.includes(fptReportTids, tender.id))) {
+            console.log(tender);
+            if (tender.pay_account && (tender.pay_account.name || tender.pay_account.bank || tender.pay_account.account || tender.pay_account.contact || tender.pay_account.phone)) {
+                $('#payaccount input[name="name"]').val(tender.pay_account.name);
+                $('#payaccount input[name="bank"]').val(tender.pay_account.bank);
+                $('#payaccount input[name="bank_account"]').val(tender.pay_account.account);
+                $('#payaccount input[name="contact"]').val(tender.pay_account.contact);
+                $('#payaccount input[name="phone"]').val(tender.pay_account.phone);
+                const data = {
+                    id: tender.pt.id,
+                    tid: tender.id,
+                    name: tender.pay_account.name,
+                    bank: tender.pay_account.bank,
+                    bank_account: tender.pay_account.account,
+                    contact: tender.pay_account.contact,
+                    phone: tender.pay_account.phone,
+                }
+                postData(`/sp/${spid}/financial/pay/save`, { type: 'set-pay-tender', updateData: data }, function (result) {
+                    toastr.success('已同步');
+                    tender.pt = result;
+                });
             }
+        } else {
+            toastr.error('无权限操作');
+            return;
         }
     });
 
-    $('#get-form-tender').on('click', function () {
-        const tid = $('#payaccount input[name="tid"]').val();
+    $('#payaccount input[type="text"]').on('change', function () {
+        const tid = $('#pay-tender-list tr.bg-warning').data('tid');
         const tender = tenders.find(t => t.id === parseInt(tid));
         if (!tid || !tender) {
             toastr.error('标段不存在');
             return;
         }
-        $('#payaccount input[name="name"]').val(tender.pay_account.name);
-        $('#payaccount input[name="bank"]').val(tender.pay_account.bank);
-        $('#payaccount input[name="bank_account"]').val(tender.pay_account.account);
-        $('#payaccount input[name="contact"]').val(tender.pay_account.contact);
-        $('#payaccount input[name="phone"]').val(tender.pay_account.phone);
-        toastr.success('已同步');
+        if (is_admin || (fptReportTids && _.includes(fptReportTids, tender.id))) {
+            const data = {
+                id: tender.pt.id,
+                tid: tender.id,
+                name: $('#payaccount input[name="name"]').val(),
+                bank: $('#payaccount input[name="bank"]').val(),
+                bank_account: $('#payaccount input[name="bank_account"]').val(),
+                contact: $('#payaccount input[name="contact"]').val(),
+                phone: $('#payaccount input[name="phone"]').val(),
+            }
+            postData(`/sp/${spid}/financial/pay/save`, { type: 'set-pay-tender', updateData: data }, function (result) {
+                tender.pt = result;
+            });
+        }
     });
 
     $('#set-pay-btn').on('click', function () {
@@ -726,188 +815,6 @@ $(function () {
         }
     });
 
-    $('#pay-list tr').eq(0).click();
-
-    function changeTender(tender) {
-        $('#add-pay-tender').val(tender ? tender.id : '');
-        if (!tender) {
-            toastr.warning('请先为项目添加标段再申请支付');
-            return;
-        }
-        if (tender.dealCode) {
-            $('#add-pay-code').val(tender.dealCode + '-' + moment().format('YYYYMMDD') + '-' + makeNum(tender.startNum));
-            $('#add-pay-code').siblings('span').text('');
-        } else {
-            $('#add-pay-code').val('');
-            $('#add-pay-code').siblings('span').html('请先去<a href="/tender/' + tender.id + '" target="_blank">标段属性</a>,填写合同编号');
-        }
-    }
-
-    function makeNum(num) {
-        let str = num.toString();
-        while (str.length < 3) {
-            str = '0' + str;
-        }
-        return str;
-    }
-
-    $('#audit-list').on('click', 'a', function() {
-        const type = $(this).data('target')
-        const auditCard = $(this).parent().parent()
-        if (type === 'show') {
-            $(this).data('target', 'hide')
-            auditCard.find('.fold-card').slideDown('swing', () => {
-                auditCard.find('#end-target').text($(this).data('idx') + '#')
-                auditCard.find('#fold-btn').text('收起历史审核记录')
-            })
-        } else {
-            $(this).data('target', 'show')
-            auditCard.find('.fold-card').slideUp('swing', () => {
-                auditCard.find('#end-target').text('1#')
-                auditCard.find('#fold-btn').text('展开历史审核记录')
-            })
-        }
-    });
-
-    // 获取审批流程
-    $('a[data-target="#sp-list" ]').on('click', function () {
-        const data = {
-            type: 'get-auditors',
-            id: $(this).attr('c-id'),
-        };
-        postData(`/sp/${spid}/financial/pay/save`, data, function (result) {
-            const { auditHistory, auditors2, user } = result;
-            let auditorsHTML = [];
-            auditors2.forEach((group, idx) => {
-                if (idx === 0) {
-                    auditorsHTML.push(`<li class="list-group-item d-flex justify-content-between align-items-center">
-                        <span class="mr-1"><i class="fa fa fa-play-circle fa-rotate-90"></i></span>
-                    <span class="text-muted">${getGroupAuditHtml(group)}</span>
-                    <span class="badge badge-light badge-pill ml-auto"><small>原报</small></span>
-                    </li>`);
-                } else if(idx === auditors2.length -1 && idx !== 0) {
-                    auditorsHTML.push(`<li class="list-group-item d-flex justify-content-between align-items-center">
-                        <span class="mr-1"><i class="fa fa fa-stop-circle fa-rotate-90"></i></span>
-                    <span class="text-muted">${getGroupAuditHtml(group)}</span>
-                    <div class="d-flex ml-auto">
-                    ${getAuditTypeHtml(group[0].audit_type)}
-                    <span class="badge badge-light badge-pill ml-auto"><small>终审</small></span>
-                    </div>
-                    </li>`);
-                } else {
-                    auditorsHTML.push(`<li class="list-group-item d-flex justify-content-between align-items-center">
-                        <span class="mr-1"><i class="fa fa fa-chevron-circle-down"></i></span>
-                    <span class="text-muted">${getGroupAuditHtml(group)}</span>
-                    <div class="d-flex ml-auto">
-                    ${getAuditTypeHtml(group[0].audit_type)}
-                    <span class="badge badge-light badge-pill"><small>${transFormToChinese(idx)}审</small></span>
-                    </div>
-                    </li>`);
-                }
-            });
-            $('#auditor-list').empty();
-            $('#auditor-list').append(auditorsHTML.join(''));
-
-            let historyHTML = [];
-            auditHistory.forEach((his, idx) => {
-                if (idx === auditHistory.length - 1 && auditHistory.length !== 1) {
-                    historyHTML.push(`<div class="text-right"><a href="javascript: void(0);" id="fold-btn" data-target="show">展开历史审批流程</a></div>`);
-                }
-                historyHTML.push(`<div class="${idx < auditHistory.length - 1 ? 'fold-card' : ''}">`);
-                historyHTML.push(`<div class="text-center text-muted">${idx+1}#</div>`);
-                historyHTML.push(`<ul class="timeline-list list-unstyled mt-2 ${ idx === auditHistory.length - 1 && auditHistory.length !== 1 ? 'last-auditor-list' : '' }">`);
-                his.forEach((group, index) => {
-                    if (index === 0) {
-                        historyHTML.push(`<li class="timeline-list-item pb-2">
-                                            <div class="timeline-item-date">
-                                                ${group.beginYear}
-                                                <span>${group.beginDate}</span>
-                                                <span>${group.beginTime}</span>
-                                            </div>
-                                            <div class="timeline-item-tail"></div>
-                                            <div class="timeline-item-icon bg-success text-light"><i class="fa fa-caret-down"></i></div>
-                                            <div class="timeline-item-content">
-                                                <div class="py-1">
-                                                    <span class="text-black-50">原报</span>
-                                                    <span class="pull-right text-success">${idx !== 0 ? '重新' : '' }上报审批</span>
-                                                </div>
-                                                <div class="card">
-                                                    <div class="card-body px-3 py-0">
-                                                        <div class="card-text p-2 py-3 row">
-                                                            <div class="col">
-                                                                <span class="h6">${user.name}</span>
-                                                                <span class="text-muted ml-1">${user.role}</span>
-                                                            </div>
-                                                            <div class="col">
-                                                                <span class="pull-right text-success"><i class="fa fa-check-circle"></i></span>
-                                                            </div>
-                                                        </div>
-                                                    </div>
-                                                </div>
-                                            </div>
-                                        </li>`);
-                    }
-                    historyHTML.push(`<li class="timeline-list-item pb-2 ${ group.status === auditConst.status.uncheck && idx === auditHistory.length - 1 && auditHistory.length !== 1 ? 'is_uncheck' : ''}">`);
-                    if (group.endYear) {
-                        historyHTML.push(`<div class="timeline-item-date">${group.endYear}<span>${group.endDate}</span><span>${group.endTime}</span></div>`);
-                    }
-                    if (index < his.length - 1) {
-                        historyHTML.push('<div class="timeline-item-tail"></div>');
-                    }
-                    if (group.status === auditConst.status.checked) {
-                        historyHTML.push('<div class="timeline-item-icon bg-success text-light"><i class="fa fa-check"></i></div>');
-                    } else if (group.status === auditConst.status.checkNo) {
-                        historyHTML.push('<div class="timeline-item-icon bg-warning text-light"><i class="fa fa-level-up"></i></div>');
-                    } else if (group.status === auditConst.status.checking) {
-                        historyHTML.push('<div class="timeline-item-icon bg-warning text-light"><i class="fa fa-ellipsis-h"></i></div>');
-                    } else if (group.status === auditConst.status.checkAgain) {
-                        historyHTML.push('<div class="timeline-item-icon bg-warning text-light"><i class="fa fa-check"></i></div>');
-                    } else {
-                        historyHTML.push('<div class="timeline-item-icon bg-secondary text-light"></div>');
-                    }
-
-                    historyHTML.push('<div class="timeline-item-content">');
-                    historyHTML.push('<div class="py-1">');
-                    const statuStr = group.status !== auditConst.status.uncheck ?
-                        `<span class="pull-right ${auditConst.statusClass[group.status]}">${auditConst.statusString[group.status]}</span>` : '';
-                    historyHTML.push(`
-                    <span class="text-black-50">
-                    ${ group.audit_order === 0 ? '原报' : !group.is_final ? group.audit_order + '审' : '终审' } ${getAuditTypeText(group.audit_type)}
-                    </span>
-                    ${statuStr}`);
-                    historyHTML.push('</div>');
-                    historyHTML.push('<div class="card"><div class="card-body px-3 py-0">');
-                    for (const [i, auditor] of group.auditors.entries()) {
-                        historyHTML.push(`<div class="card-text p-2 py-3 row ${ ( i > 0 ? 'border-top' : '') }">`);
-                        historyHTML.push(`<div class="col"><span class="h6">${auditor.name}</span><span class="text-muted ml-1">${auditor.role}</span></div>`);
-                        historyHTML.push('<div class="col">');
-                        if (auditor.status === auditConst.status.checked) {
-                            historyHTML.push('<span class="pull-right text-success"><i class="fa fa-check-circle"></i></span>');
-                        } else if (auditor.status === auditConst.status.checkNo) {
-                            historyHTML.push('<span class="pull-right text-warning"><i class="fa fa-share-square fa-rotate-270"></i></span>');
-                        } else if (auditor.status === auditConst.status.checking) {
-                            historyHTML.push('<span class="pull-right text-warning"><i class="fa fa-commenting"></i></span>');
-                        } else if (auditor.status === auditConst.status.checkAgain) {
-                            historyHTML.push('<span class="pull-right text-warning"><i class="fa fa-check"></i></span>');
-                        }
-                        historyHTML.push('</div>');
-                        if (auditor.opinion) {
-                            historyHTML.push(`<div class="col-12 py-1 bg-light"><i class="fa fa-commenting-o mr-1"></i>${auditor.opinion}</div>`);
-                        }
-                        historyHTML.push('</div>');
-                    }
-                    historyHTML.push('</div></div>');
-                    historyHTML.push('</div>');
-                    historyHTML.push('</li>');
-                });
-                historyHTML.push('</div>');
-                historyHTML.push('</ul>');
-            });
-            $('#audit-list').empty();
-            $('#audit-list').append(historyHTML.join(''));
-        });
-    });
-
     $.subMenu({
         menu: '#sub-menu', miniMenu: '#sub-mini-menu', miniMenuList: '#mini-menu-list',
         toMenu: '#to-menu', toMiniMenu: '#to-mini-menu',
@@ -925,16 +832,3 @@ $(function () {
         }
     });
 });
-const getGroupAuditHtml = function (group) {
-    return group.map(u => { return `<small class="d-inline-block text-dark mx-1" title="${u.role}" data-auditorId="${u.aid}">${u.name}</small>`; }).join('');
-};
-
-const getAuditTypeHtml = function (type) {
-    if (type === auditType.key.common) return '';
-    return `<div class="li-subscript"><span class="badge badge-pill badge-${auditType.info[type].class} p-1 badge-bg-small"><small>${auditType.info[type].short}</small></span></div>`;
-};
-
-const getAuditTypeText = function (type) {
-    if (type === auditType.key.common) return '';
-    return `<span class="text-${auditType.info[type].class}">${auditType.info[type].long}</span>`;
-};

+ 325 - 0
app/public/js/financial_pay_company.js

@@ -0,0 +1,325 @@
+'use strict';
+let auditUtils;
+$(function () {
+    autoFlashHeight();
+
+    $('#tid_select').select2({
+        language: 'zh-CN',
+        theme: 'bootstrap4',
+        selectOnClose: true,
+        // width: '150',
+    });
+
+    $('#tid_select').change(function () {
+        const tid = parseInt($(this).val()) || 0;
+        setSelectValue('tid', tid);
+    });
+
+    $('#status_select .to-log-link').click(function () {
+        const status = parseInt($(this).data('val')) || null;
+        setSelectValue('status', status);
+    });
+
+    $('#used_select .to-log-link').click(function () {
+        const used = $(this).data('val') || null;
+        setSelectValue('used', used);
+    });
+
+    function setSelectValue(select, value) {
+        const routes = [];
+        const tid = select === 'tid' ? value : $('#tid_select').val();
+        if (tid) {
+            routes.push('tid=' + tid);
+        }
+        const status = select === 'status' ? value : $('#status_selected').data('value');
+        if (status) {
+            routes.push('status=' + status);
+        }
+        const used = select === 'used' ? value : $('#used_selected').data('value');
+        if (used) {
+            routes.push('used=' + used);
+        }
+        if (getLocalCache('account-pageSize')) {
+            routes.push('pageSize=' + getLocalCache('account-pageSize'));
+        }
+        window.location.href = `/sp/${spid}/financial/pay/company/${fpcid}` + (routes.length ? '?' + routes.join('&') : '');
+    }
+
+    $('#add-pay').on('show.bs.modal', function () {
+        let t = null;
+        if (tenders.length > 0) {
+            if (is_admin) {
+                t = tenders[0];
+            } else {
+                const filterTender = tenders.filter(t => _.includes(fptReportTids, t.id));
+                if (filterTender.length > 0) {
+                    t = filterTender[0];
+                }
+            }
+        }
+        $('#add-pay-tender').val(t ? t.id : '');
+        changeTender(t);
+    });
+
+    $('#add-pay-tender').on('change', function () {
+        const tender = tenders.find(t => t.id === parseInt($(this).val()));
+        changeTender(tender);
+    });
+
+    $('#add-pay-btn').on('click', function () {
+        const tid = $('#add-pay-tender').val();
+        const tender = tenders.find(t => t.id === parseInt(tid));
+        if (!tender) {
+            toastr.error('请选择支付标段');
+            return;
+        }
+        const code = $('#add-pay-code').val();
+        if (!code) {
+            toastr.error('请先去标段属性,填写合同编号');
+            return;
+        }
+        const prop = {
+            tid: tender.id,
+            code: code,
+            used: $('#add-pay-used').val(),
+        };
+        postData(`/sp/${spid}/financial/pay/company/${fpcid}/save`, { type: 'add-pay', updateData: prop }, function (result) {
+            window.location.href = `/sp/${spid}/financial/pay/${result.id}/detail`;
+        });
+    });
+
+    $('body').on('click', '#pay-list .del-pay-btn', function () {
+        const fpid = $(this).data('id');
+        deleteAfterHint(function () {
+            postData(`/sp/${spid}/financial/pay/company/${fpcid}/save`, {type: 'del-pay', postData: { node: fpid }}, function (result) {
+                window.location.reload();
+            })
+        }, '确认删除该资金支付?');
+    });
+
+    $('#batch-old-pays').click(function () {
+        const pays = [];
+        $('#contract-old-pay input[type="checkbox"]:checked').each(function () {
+            const fpid = parseInt($(this).val());
+            pays.push(fpid);
+        });
+        if (pays.length === 0) {
+            toastr.error('请选择关联的旧数据');
+            return;
+        }
+        console.log(pays);
+        postData(`/sp/${spid}/financial/pay/company/${fpcid}/save`, {type: 'batch-old-pays', postData: { payIds: pays }}, function (result) {
+            window.location.reload();
+        });
+    });
+
+    function changeTender(tender) {
+        $('#add-pay-tender').val(tender ? tender.id : '');
+        if (!tender) {
+            toastr.warning('请先为项目添加标段再申请支付');
+            return;
+        }
+        if (tender.dealCode) {
+            $('#add-pay-code').val(tender.dealCode + '-' + moment().format('YYYYMMDD') + '-' + makeNum(tender.startNum));
+            $('#add-pay-code').siblings('span').text('');
+        } else {
+            $('#add-pay-code').val('');
+            $('#add-pay-code').siblings('span').html('请先去<a href="/tender/' + tender.id + '" target="_blank">标段属性</a>,填写合同编号');
+        }
+    }
+
+    function makeNum(num) {
+        let str = num.toString();
+        while (str.length < 3) {
+            str = '0' + str;
+        }
+        return str;
+    }
+
+    $('#audit-list').on('click', 'a', function() {
+        const type = $(this).data('target')
+        const auditCard = $(this).parent().parent()
+        if (type === 'show') {
+            $(this).data('target', 'hide')
+            auditCard.find('.fold-card').slideDown('swing', () => {
+                auditCard.find('#end-target').text($(this).data('idx') + '#')
+                auditCard.find('#fold-btn').text('收起历史审核记录')
+            })
+        } else {
+            $(this).data('target', 'show')
+            auditCard.find('.fold-card').slideUp('swing', () => {
+                auditCard.find('#end-target').text('1#')
+                auditCard.find('#fold-btn').text('展开历史审核记录')
+            })
+        }
+    });
+
+    // 获取审批流程
+    $('a[data-target="#sp-list" ]').on('click', function () {
+        const data = {
+            type: 'get-auditors',
+            id: $(this).attr('c-id'),
+        };
+        postData(`/sp/${spid}/financial/pay/save`, data, function (result) {
+            const { auditHistory, auditors2, user } = result;
+            let auditorsHTML = [];
+            auditors2.forEach((group, idx) => {
+                if (idx === 0) {
+                    auditorsHTML.push(`<li class="list-group-item d-flex justify-content-between align-items-center">
+                        <span class="mr-1"><i class="fa fa fa-play-circle fa-rotate-90"></i></span>
+                    <span class="text-muted">${getGroupAuditHtml(group)}</span>
+                    <span class="badge badge-light badge-pill ml-auto"><small>原报</small></span>
+                    </li>`);
+                } else if(idx === auditors2.length -1 && idx !== 0) {
+                    auditorsHTML.push(`<li class="list-group-item d-flex justify-content-between align-items-center">
+                        <span class="mr-1"><i class="fa fa fa-stop-circle fa-rotate-90"></i></span>
+                    <span class="text-muted">${getGroupAuditHtml(group)}</span>
+                    <div class="d-flex ml-auto">
+                    ${getAuditTypeHtml(group[0].audit_type)}
+                    <span class="badge badge-light badge-pill ml-auto"><small>终审</small></span>
+                    </div>
+                    </li>`);
+                } else {
+                    auditorsHTML.push(`<li class="list-group-item d-flex justify-content-between align-items-center">
+                        <span class="mr-1"><i class="fa fa fa-chevron-circle-down"></i></span>
+                    <span class="text-muted">${getGroupAuditHtml(group)}</span>
+                    <div class="d-flex ml-auto">
+                    ${getAuditTypeHtml(group[0].audit_type)}
+                    <span class="badge badge-light badge-pill"><small>${transFormToChinese(idx)}审</small></span>
+                    </div>
+                    </li>`);
+                }
+            });
+            $('#auditor-list').empty();
+            $('#auditor-list').append(auditorsHTML.join(''));
+
+            let historyHTML = [];
+            auditHistory.forEach((his, idx) => {
+                if (idx === auditHistory.length - 1 && auditHistory.length !== 1) {
+                    historyHTML.push(`<div class="text-right"><a href="javascript: void(0);" id="fold-btn" data-target="show">展开历史审批流程</a></div>`);
+                }
+                historyHTML.push(`<div class="${idx < auditHistory.length - 1 ? 'fold-card' : ''}">`);
+                historyHTML.push(`<div class="text-center text-muted">${idx+1}#</div>`);
+                historyHTML.push(`<ul class="timeline-list list-unstyled mt-2 ${ idx === auditHistory.length - 1 && auditHistory.length !== 1 ? 'last-auditor-list' : '' }">`);
+                his.forEach((group, index) => {
+                    if (index === 0) {
+                        historyHTML.push(`<li class="timeline-list-item pb-2">
+                                            <div class="timeline-item-date">
+                                                ${group.beginYear}
+                                                <span>${group.beginDate}</span>
+                                                <span>${group.beginTime}</span>
+                                            </div>
+                                            <div class="timeline-item-tail"></div>
+                                            <div class="timeline-item-icon bg-success text-light"><i class="fa fa-caret-down"></i></div>
+                                            <div class="timeline-item-content">
+                                                <div class="py-1">
+                                                    <span class="text-black-50">原报</span>
+                                                    <span class="pull-right text-success">${idx !== 0 ? '重新' : '' }上报审批</span>
+                                                </div>
+                                                <div class="card">
+                                                    <div class="card-body px-3 py-0">
+                                                        <div class="card-text p-2 py-3 row">
+                                                            <div class="col">
+                                                                <span class="h6">${user.name}</span>
+                                                                <span class="text-muted ml-1">${user.role}</span>
+                                                            </div>
+                                                            <div class="col">
+                                                                <span class="pull-right text-success"><i class="fa fa-check-circle"></i></span>
+                                                            </div>
+                                                        </div>
+                                                    </div>
+                                                </div>
+                                            </div>
+                                        </li>`);
+                    }
+                    historyHTML.push(`<li class="timeline-list-item pb-2 ${ group.status === auditConst.status.uncheck && idx === auditHistory.length - 1 && auditHistory.length !== 1 ? 'is_uncheck' : ''}">`);
+                    if (group.endYear) {
+                        historyHTML.push(`<div class="timeline-item-date">${group.endYear}<span>${group.endDate}</span><span>${group.endTime}</span></div>`);
+                    }
+                    if (index < his.length - 1) {
+                        historyHTML.push('<div class="timeline-item-tail"></div>');
+                    }
+                    if (group.status === auditConst.status.checked) {
+                        historyHTML.push('<div class="timeline-item-icon bg-success text-light"><i class="fa fa-check"></i></div>');
+                    } else if (group.status === auditConst.status.checkNo) {
+                        historyHTML.push('<div class="timeline-item-icon bg-warning text-light"><i class="fa fa-level-up"></i></div>');
+                    } else if (group.status === auditConst.status.checking) {
+                        historyHTML.push('<div class="timeline-item-icon bg-warning text-light"><i class="fa fa-ellipsis-h"></i></div>');
+                    } else if (group.status === auditConst.status.checkAgain) {
+                        historyHTML.push('<div class="timeline-item-icon bg-warning text-light"><i class="fa fa-check"></i></div>');
+                    } else {
+                        historyHTML.push('<div class="timeline-item-icon bg-secondary text-light"></div>');
+                    }
+
+                    historyHTML.push('<div class="timeline-item-content">');
+                    historyHTML.push('<div class="py-1">');
+                    const statuStr = group.status !== auditConst.status.uncheck ?
+                        `<span class="pull-right ${auditConst.statusClass[group.status]}">${auditConst.statusString[group.status]}</span>` : '';
+                    historyHTML.push(`
+                    <span class="text-black-50">
+                    ${ group.audit_order === 0 ? '原报' : !group.is_final ? group.audit_order + '审' : '终审' } ${getAuditTypeText(group.audit_type)}
+                    </span>
+                    ${statuStr}`);
+                    historyHTML.push('</div>');
+                    historyHTML.push('<div class="card"><div class="card-body px-3 py-0">');
+                    for (const [i, auditor] of group.auditors.entries()) {
+                        historyHTML.push(`<div class="card-text p-2 py-3 row ${ ( i > 0 ? 'border-top' : '') }">`);
+                        historyHTML.push(`<div class="col"><span class="h6">${auditor.name}</span><span class="text-muted ml-1">${auditor.role}</span></div>`);
+                        historyHTML.push('<div class="col">');
+                        if (auditor.status === auditConst.status.checked) {
+                            historyHTML.push('<span class="pull-right text-success"><i class="fa fa-check-circle"></i></span>');
+                        } else if (auditor.status === auditConst.status.checkNo) {
+                            historyHTML.push('<span class="pull-right text-warning"><i class="fa fa-share-square fa-rotate-270"></i></span>');
+                        } else if (auditor.status === auditConst.status.checking) {
+                            historyHTML.push('<span class="pull-right text-warning"><i class="fa fa-commenting"></i></span>');
+                        } else if (auditor.status === auditConst.status.checkAgain) {
+                            historyHTML.push('<span class="pull-right text-warning"><i class="fa fa-check"></i></span>');
+                        }
+                        historyHTML.push('</div>');
+                        if (auditor.opinion) {
+                            historyHTML.push(`<div class="col-12 py-1 bg-light"><i class="fa fa-commenting-o mr-1"></i>${auditor.opinion}</div>`);
+                        }
+                        historyHTML.push('</div>');
+                    }
+                    historyHTML.push('</div></div>');
+                    historyHTML.push('</div>');
+                    historyHTML.push('</li>');
+                });
+                historyHTML.push('</div>');
+                historyHTML.push('</ul>');
+            });
+            $('#audit-list').empty();
+            $('#audit-list').append(historyHTML.join(''));
+        });
+    });
+
+    $.subMenu({
+        menu: '#sub-menu', miniMenu: '#sub-mini-menu', miniMenuList: '#mini-menu-list',
+        toMenu: '#to-menu', toMiniMenu: '#to-mini-menu',
+        key: 'menu.1.0.0',
+        miniHint: '#sub-mini-hint', hintKey: 'menu.hint.1.0.1',
+        callback: function (info) {
+            if (info.mini) {
+                $('.panel-title').addClass('fluid');
+                $('#sub-menu').removeClass('panel-sidebar');
+            } else {
+                $('.panel-title').removeClass('fluid');
+                $('#sub-menu').addClass('panel-sidebar');
+            }
+            autoFlashHeight();
+        }
+    });
+});
+const getGroupAuditHtml = function (group) {
+    return group.map(u => { return `<small class="d-inline-block text-dark mx-1" title="${u.role}" data-auditorId="${u.aid}">${u.name}</small>`; }).join('');
+};
+
+const getAuditTypeHtml = function (type) {
+    if (type === auditType.key.common) return '';
+    return `<div class="li-subscript"><span class="badge badge-pill badge-${auditType.info[type].class} p-1 badge-bg-small"><small>${auditType.info[type].short}</small></span></div>`;
+};
+
+const getAuditTypeText = function (type) {
+    if (type === auditType.key.common) return '';
+    return `<span class="text-${auditType.info[type].class}">${auditType.info[type].long}</span>`;
+};

+ 365 - 22
app/public/js/financial_transfer_tender.js

@@ -1,20 +1,203 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Mai
+ * @date 2018/10/11
+ * @version
+ */
+
+const tenderListSpec = (function(){
+    function getTenderNodeHtml(node, arr, pid) {
+        const html = [];
+        html.push('<tr pid="' + pid + '">');
+        // 名称
+        html.push('<td width="30%" style="min-width: 300px;" class="in-' + node.level + '">');
+        if (node.cid) {
+            html.push('<span onselectstart="return false" style="{-moz-user-select:none}" class="fold-switch mr-1" title="收起" cid="'+ node.sort_id +'"><i class="fa fa-minus-square-o"></i></span> <i class="fa fa-folder-o"></i> ');
+            html.push((node.level === 1 ? '<b>' : ''), node.name, (node.level === 1 ? '</b>' : ''));
+        } else {
+            html.push('<span class="text-muted mr-2">');
+            html.push(arr.indexOf(node) === arr.length - 1 ? '└' : '├');
+            html.push('</span>');
+            html.push(node.name);
+        }
+        html.push('</td>');
+        // 计量进度
+        html.push('<td style="width: 130px" class="text-center">');
+        if (!node.cid && node.cur_flow) {
+            if (node.progress) {
+                html.push(node.progress.title + ' (' + '<span class="' + node.progress.status_class + '">' + node.progress.status + '</span>' + ')');
+            } else {
+                html.push(node.cur_flow.title + ' (' + '<span class="' + node.cur_flow.status_class + '">' + node.cur_flow.status + '</span>' + ')');
+            }
+        }
+        html.push('</td>');
+        // 资金来源
+        html.push('<td style="width: 200px" class="text-center">');
+        if (!node.cid) {
+            html.push(transferInfo.uid === user_id && !transferInfo.is_lock ? '<input type="text" data-key="source" class="form-control form-control-sm" placeholder="" data-ftid="' + node.id + '" value="'+ node.source +'" />' : node.source);
+        }
+        html.push('</td>');
+        // 合同金额
+        html.push('<td style="width: 100px" class="text-right">');
+        html.push(node.total_price || '');
+        html.push('</td>');
+        // 截止本期合同
+        html.push('<td style="width: 100px" class="text-right">');
+        html.push(node.end_contract_tp || '');
+        html.push('</td>');
+        // 截止本期变更
+        html.push('<td style="width: 100px" class="text-right">');
+        html.push(node.end_qc_tp || '');
+        html.push('</td>');
+        // 截止本期完成
+        html.push('<td style="width: 100px" class="text-right">');
+        html.push(node.end_gather_tp || '');
+        html.push('</td>');
+        // 截止本期实付
+        html.push('<td style="width: 100px" class="text-right">');
+        html.push(node.end_sf_tp || '');
+        html.push('</td>');
+        // 本次划拨
+        html.push('<td style="width: 100px" class="text-right">');
+        if (!node.cid) {
+            html.push(transferInfo.uid === user_id && !transferInfo.is_lock ? '<input type="text" data-key="hb_tp" class="form-control form-control-sm text-right" placeholder="默认等于本期实付" data-ftid="' + node.id + '" value="' + node.hb_tp + '" />' : node.hb_tp);
+        } else {
+            html.push(node.hb_tp || '');
+        }
+        html.push('</td>');
+        // 截止上次划拨
+        html.push('<td style="width: 100px" class="text-right">');
+        html.push(node.pre_hb_tp || '');
+        html.push('</td>');
+        // 截止本次划拨
+        html.push('<td style="width: 100px" class="text-right">');
+        html.push(node.end_hb_tp || '');
+        html.push('</td>');
+        // 附件
+        html.push('<td style="width: 80px" class="text-center">');
+        if (!node.cid) {
+            html.push('<a href="javascript:void(0);" class="text-primary open-tender-files" data-ftid="' + node.id + '"><i class="fa fa-paperclip fa-rotate-90"></i></a> <span class="file-num">'+ (node.files.length > 0 ? node.files.length : '') +'</span>');
+        }
+        html.push('</td>');
+        // 操作
+        if (transferInfo.uid === user_id) {
+            html.push('<td style="width: 80px" class="text-center">');
+            html.push(!node.cid && !transferInfo.is_lock ? '<a class="text-danger del-tender-btn" href="javascript:void(0);" data-id="'+ node.id +'">移除</a>' : '');
+            html.push('</td>');
+        }
+        html.push('</tr>');
+        return html.join('');
+    }
+    function getTenderTreeHeaderHtml() {
+        const html = [];
+        const left = $('#sub-menu').css('display') === 'none' ? 56 : 176;
+        html.push('<table class="table table-hover table-bordered" id="progress-table">')
+        html.push('<thead style="position: sticky;left:'+ left +'px;top: 0;">', '<tr>');
+        html.push('<th style="width: 30%;min-width: 300px" class="text-center">', '标段名称', '</th>');
+        html.push('<th class="text-center" style="width: 130px">', '计量进度', '</th>');
+        html.push('<th class="text-center" style="width: 200px">', '资金来源', '</th>');
+        html.push('<th class="text-center" style="width: 100px">', '合同金额', '</th>');
+        html.push('<th style="width: 100px" class="text-center">', '截止本期合同', '</th>');
+        html.push('<th style="width: 100px" class="text-center">', '截止本期变更', '</th>');
+        html.push('<th style="width: 100px" class="text-center">', '截止本期完成', '</th>');
+        html.push('<th style="width: 100px" class="text-center">', '截止本期实付', '</th>');
+        html.push('<th style="width: 100px" class="text-center">', '本次划拨', '</th>');
+        html.push('<th style="width: 100px" class="text-center">', '截止上次划拨', '</th>');
+        html.push('<th style="width: 100px" class="text-center">', '截止本次划拨', '</th>');
+        html.push('<th style="width: 80px" class="text-center">', '附件', '</th>');
+        if (transferInfo.uid === user_id) {
+            html.push('<th style="width: 80px" class="text-center">', '操作', '</th>');
+        }
+        html.push('</tr>', '</thead>');
+        return html.join('');
+    }
+    function calculateTender(tender) {
+        if (tender.stage_tp) {
+            tender.gather_tp = ZhCalc.sum([tender.stage_tp.contract_tp, tender.stage_tp.qc_tp, tender.stage_tp.pc_tp]);
+            tender.end_contract_tp = ZhCalc.sum([tender.stage_tp.pre_contract_tp, tender.stage_tp.contract_tp, tender.stage_tp.contract_pc_tp]);
+            tender.end_qc_tp = ZhCalc.sum([tender.stage_tp.pre_qc_tp, tender.stage_tp.qc_tp, tender.stage_tp.qc_pc_tp]);
+            tender.end_gather_tp = ZhCalc.add(tender.end_contract_tp, tender.end_qc_tp);
+            tender.pre_gather_tp = ZhCalc.add(tender.stage_tp.pre_contract_tp, tender.stage_tp.pre_qc_tp);
+            tender.yf_tp = ZhCalc.add(tender.stage_tp.yf_tp);
+            tender.end_yf_tp = ZhCalc.add(tender.stage_tp.pre_yf_tp, tender.yf_tp);
+            tender.sf_tp = ZhCalc.add(tender.stage_tp.sf_tp);
+            tender.end_sf_tp = ZhCalc.add(tender.stage_tp.pre_sf_tp, tender.sf_tp);
+            tender.wf_tp = ZhCalc.sub(tender.end_yf_tp, tender.end_sf_tp);
+        } else if (tender.lastStage) {
+            tender.gather_tp = ZhCalc.sum([tender.lastStage.contract_tp, tender.lastStage.qc_tp, tender.lastStage.pc_tp]);
+            tender.end_contract_tp = ZhCalc.sum([tender.lastStage.pre_contract_tp, tender.lastStage.contract_tp, tender.lastStage.contract_pc_tp]);
+            tender.end_qc_tp = ZhCalc.sum([tender.lastStage.pre_qc_tp, tender.lastStage.qc_tp, tender.lastStage.qc_pc_tp]);
+            tender.end_gather_tp = ZhCalc.add(tender.end_contract_tp, tender.end_qc_tp);
+            tender.pre_gather_tp = ZhCalc.add(tender.lastStage.pre_contract_tp, tender.lastStage.pre_qc_tp);
+            tender.yf_tp = ZhCalc.add(tender.lastStage.yf_tp);
+            tender.end_yf_tp = ZhCalc.add(tender.lastStage.pre_yf_tp, tender.yf_tp);
+            tender.sf_tp = ZhCalc.add(tender.lastStage.sf_tp);
+            tender.end_sf_tp = ZhCalc.add(tender.lastStage.pre_sf_tp, tender.sf_tp);
+            tender.wf_tp = ZhCalc.sub(tender.end_yf_tp, tender.end_sf_tp);
+        }
+        tender.end_hb_tp = ZhCalc.add(tender.pre_hb_tp, tender.hb_tp);
+    }
+    function calculateParent(node) {
+        if (node.children && node.cid) {
+            node.total_price = 0;
+            node.gather_tp = 0;
+            node.end_contract_tp = 0;
+            node.end_qc_tp = 0;
+            node.end_gather_tp = 0;
+            node.pre_gather_tp = 0;
+            node.yf_tp = 0;
+            node.end_yf_tp = 0;
+            node.advance_tp = 0;
+            node.contract_price = 0;
+            node.sf_tp = 0;
+            node.end_sf_tp = 0;
+            node.wf_tp = 0;
+            node.hb_tp = 0;
+            node.pre_hb_tp = 0;
+            node.end_hb_tp = 0;
+            for (const c of node.children) {
+                calculateParent(c);
+                node.total_price = ZhCalc.add(node.total_price, c.total_price);
+                node.gather_tp = ZhCalc.add(node.gather_tp, c.gather_tp);
+                node.end_contract_tp = ZhCalc.add(node.end_contract_tp, c.end_contract_tp);
+                node.end_qc_tp = ZhCalc.add(node.end_qc_tp, c.end_qc_tp);
+                node.end_gather_tp = ZhCalc.add(node.end_gather_tp, c.end_gather_tp);
+                node.pre_gather_tp = ZhCalc.add(node.pre_gather_tp, c.pre_gather_tp);
+                node.yf_tp = ZhCalc.add(node.yf_tp, c.yf_tp);
+                node.end_yf_tp = ZhCalc.add(node.end_yf_tp, c.end_yf_tp);
+                node.advance_tp = ZhCalc.add(node.advance_tp, c.advance_tp);
+                node.contract_price = ZhCalc.add(node.contract_price, c.contract_price);
+                node.sf_tp = ZhCalc.add(node.sf_tp, c.sf_tp);
+                node.end_sf_tp = ZhCalc.add(node.end_sf_tp, c.end_sf_tp);
+                node.wf_tp = ZhCalc.add(node.wf_tp, c.wf_tp);
+                node.hb_tp = ZhCalc.add(node.hb_tp, c.hb_tp);
+                node.pre_hb_tp = ZhCalc.add(node.pre_hb_tp, c.pre_hb_tp);
+                node.end_hb_tp = ZhCalc.add(node.end_hb_tp, c.end_hb_tp);
+            }
+        }
+    }
+    return { getTenderNodeHtml, getTenderTreeHeaderHtml, calculateTender, calculateParent }
+})();
+
 $(function () {
     autoFlashHeight();
 
     $('#add-tender-btn').click(function () {
         const addTenders = [];
-        $('#tenders tr').each(function () {
-            if ($(this).find('input').is(':checked') && $(this).find('select').val().length > 0) {
-                addTenders.push({
-                    tid: $(this).find('input').val(),
-                    sorder: $(this).find('select').val(),
-                })
+        $('input[name="tender_id[]"]:checked:not(:disabled)').each(function () {
+            const tenderId = $(this).val();
+            if (tenderId) {
+                addTenders.push(parseInt(tenderId));
             }
         });
         if (addTenders.length === 0) {
-            toastr.warning('请选择添加标段及期数');
+            toastr.warning('请选择添加标段');
             return false;
         }
+        console.log(addTenders);
         postData(window.location.pathname + '/update', {postType: 'add-tender', postData: { tenders: addTenders }}, function (result) {
             window.location.reload();
         });
@@ -41,28 +224,48 @@ $(function () {
         });
     });
 
-    $('#tender-list input[type="text"]').change(function () {
+    $('body').on('change', '#tender-list input[type="text"]', function () {
         const ftid = $(this).data('ftid');
         if (!ftid) {
             toastr.error('获取资金划拨标段信息失败');
             return;
         }
-        const ftInfo = _.find(tenderList, { id: parseInt(ftid) });
+        const ftInfo = _.find(tenders, { id: parseInt(ftid) });
         if (!ftInfo) {
             toastr.error('获取资金划拨信息失败');
             return;
         }
-        const value = $(this).val() || 0;
-        // 判断只能输入数字,支持负数和小数
-        if (!/^-?\d+(\.\d+)?$/.test(value)) {
-            toastr.error('请输入正确的金额');
-            $(this).val(ftInfo.hb_tp);
-            return;
+        const key = $(this).data('key');
+        if (key === 'hb_tp') {
+            const value = $(this).val() || 0;
+            // 判断只能输入数字,支持负数和小数
+            if (!/^-?\d+(\.\d+)?$/.test(value)) {
+                toastr.error('请输入正确的金额');
+                $(this).val(ftInfo.hb_tp);
+                return;
+            }
+            postData(window.location.pathname + '/update', {postType: 'update-hb_tp', postData: { node: ftid, hb_tp: parseFloat(value) }}, function (result) {
+                // window.location.reload();
+                ftInfo.hb_tp = parseFloat(value);
+                // 更新并汇总父节点和截止本次划拨汇总
+                ftInfo.end_hb_tp = ZhCalc.add(ftInfo.pre_hb_tp, ftInfo.hb_tp);
+                for (const t of tenderTree) {
+                    tenderListSpec.calculateParent(t);
+                }
+                $('.c-body').html(getTenderTreeHtml());
+            });
+        } else {
+            const value = $(this).val() || '';
+            if (value.length > 255) {
+                toastr.error('输入内容过长,不能超过255个字符');
+                $(this).val(ftInfo[key]);
+                return;
+            }
+            postData(window.location.pathname + '/update', {postType: 'update-tender', postData: { node: ftInfo.id, key: key, value: value }}, function (result) {
+                // window.location.reload();
+                ftInfo[key] = value;
+            });
         }
-        postData(window.location.pathname + '/update', {postType: 'update-hb_tp', postData: { node: ftid, hb_tp: parseFloat(value) }}, function (result) {
-            // window.location.reload();
-            ftInfo.hb_tp = parseFloat(value);
-        });
     });
 
     $('body').on('click', '.open-tender-files', function () {
@@ -71,7 +274,7 @@ $(function () {
             toastr.error('获取资金划拨标段信息失败');
             return;
         }
-        const ftInfo = _.find(tenderList, { id: parseInt(ftid) });
+        const ftInfo = _.find(tenders, { id: parseInt(ftid) });
         if (!ftInfo) {
             toastr.error('获取资金划拨信息失败');
             return;
@@ -97,7 +300,7 @@ $(function () {
             }
         });
         const ftid = $('#tender-file input[name="ftid"]').val();
-        const ftInfo = _.find(tenderList, { id: parseInt(ftid) });
+        const ftInfo = _.find(tenders, { id: parseInt(ftid) });
         if (!ftInfo) {
             toastr.warning('不存在该资金划拨标段');
             $('#tender-file input[type="file"]').val('');
@@ -123,7 +326,7 @@ $(function () {
 
     $('body').on('click', '#tender-file .file-del', function () {
         const ftid = $('#tender-file input[name="ftid"]').val();
-        const ftInfo = _.find(tenderList, { id: parseInt(ftid) });
+        const ftInfo = _.find(tenders, { id: parseInt(ftid) });
         if (!ftInfo) {
             toastr.warning('不存在该资金划拨标段');
             return;
@@ -179,6 +382,146 @@ $(function () {
             autoFlashHeight();
         }
     });
+
+    // 添加标段树结构加载
+    const EmptyTenderListHtml = [
+        '<div class="jumbotron">',
+        '<h3 class="display-6">还没有标段数据</h3>',
+        '</div>'
+    ];
+    const tenderListTree = [];
+    let tenderListParentId = 0;
+    function initTenderListTree () {
+        const levelCategory = category.filter(function (c) {
+            return c.show_level && c.show_level > 0;
+        });
+        function findCategoryNode(cid, value, array) {
+            for (const a of array) {
+                if (a.cid === cid && a.vid === value) {
+                    return a;
+                }
+            }
+        }
+        function getCategoryNode(category, value, parent, i = null) {
+            const array = parent ?  parent.children : tenderListTree;
+            let cate = findCategoryNode(category.id, value, array);
+            if (!cate) {
+                const cateValue = findNode('id', value, category.value);
+                if (!cateValue) return null;
+                cate = {
+                    cid: category.id,
+                    vid: value,
+                    name: cateValue.value,
+                    children: [],
+                    level: i ? i : category.show_level,
+                    sort_id: ++tenderListParentId,
+                    sort: cateValue.sort,
+                };
+                array.push(cate);
+            }
+            return cate;
+        }
+        function loadTenderCategory (tender) {
+            let tenderCategory = null;
+            for (const [index, lc] of levelCategory.entries()) {
+                const tenderCate = findNode('cid', lc.id, tender.category);
+                if (tenderCate) {
+                    tenderCategory = getCategoryNode(lc, tenderCate.value, tenderCategory);
+                } else {
+                    if (index === 0 && tender.category) {
+                        for (const [i,c] of tender.category.entries()) {
+                            const cate = findNode('id', c.cid, category);
+                            if (cate) {
+                                tenderCategory = getCategoryNode(cate, c.value, tenderCategory, i+1);
+                            }
+                        }
+                    }
+                    return tenderCategory;
+                }
+            }
+            return tenderCategory;
+        }
+        tenderListTree.splice(0, tenderListTree.length);
+        for (const t of tenderList) {
+            t.valid = true;
+            delete t.level;
+            if (t.category && levelCategory.length > 0) {
+                const parent = loadTenderCategory(t);
+                if (parent) {
+                    t.level = parent.level + 1;
+                    parent.children.push(t);
+                } else {
+                    tenderListTree.push(t);
+                }
+            } else {
+                tenderListTree.push(t);
+            }
+        }
+        sortTenderTree(tenderListTree);
+    }
+    function recursiveGetTenderListNodeHtml (node, arr, pid) {
+        const html = [];
+        html.push('<tr pid="' + pid + '">');
+        html.push('<td style="width: 60px" class="text-center">');
+        if (!node.cid) {
+            html.push(`<input type="checkbox" class="tender-check" name="tender_id[]" value="${node.id}" ${_.findIndex(tenders, { tid: node.id }) !== -1 ? 'checked disabled' : ''} />`);
+        }
+        html.push('</td>');
+        // 名称
+        html.push('<td style="width: 65%" class="in-' + node.level + '">');
+        if (node.cid) {
+            html.push('<i class="fa fa-folder-o"></i> ', node.name);
+        } else {
+            html.push('<span class="text-muted mr-2">');
+            html.push(arr.indexOf(node) === arr.length - 1 ? '└' : '├');
+            html.push('</span>');
+            html.push(node.name);
+        }
+        html.push('</td>');
+        // 计量进度
+        html.push('<td style="width: 25%">');
+        if (!node.cid && node.cur_flow) {
+            if (node.progress) {
+                html.push(node.progress.title + ' (' + '<span class="' + node.progress.status_class + '">' + node.progress.status + '</span>' + ')');
+            } else {
+                html.push(node.cur_flow.title + ' (' + '<span class="' + node.cur_flow.status_class + '">' + node.cur_flow.status + '</span>' + ')');
+            }
+        }
+        html.push('</td>');
+        html.push('</tr>');
+        if (node.children) {
+            for (const c of node.children) {
+                html.push(recursiveGetTenderListNodeHtml(c, node.children, node.sort_id));
+            }
+        }
+        return html.join('');
+    }
+    // 根据TenderTree数据获取Html代码
+    function getTenderListTreeHtml () {
+        if (tenderListTree.length > 0) {
+            const html = [];
+            html.push('<table class="table table-hover table-bordered">');
+            html.push('<thead style="position: sticky;left:56px;top: 0px;">', '<tr>');
+            html.push('<th class="text-center" style="width: 60px;">', '选择', '</th>');
+            html.push('<th class="text-center" style="width: 65%">', '标段名称', '</th>');
+            html.push('<th class="text-center" style="width: 25%">', '计量进度', '</th>');
+            html.push('</tr>', '</thead>');
+            tenderListParentId = 0;
+            for (const t of tenderListTree) {
+                html.push(recursiveGetTenderListNodeHtml(t, tenderListTree, ''));
+            }
+            html.push('</table>');
+            return html.join('');
+        } else {
+            return EmptyTenderListHtml.join('');
+        }
+    }
+
+    $('#add-ftt').on('shown.bs.modal', function () {
+        tenderListOrder.reOrderTenders('', '#copyModalContent', false, tenderList);
+        initTenderListTree();
+        $('#add-tender-list').html(getTenderListTreeHtml());
+    });
 })
 /**
  * 校验文件大小、格式

+ 3 - 3
app/public/js/shares/tender_list_order.js

@@ -28,14 +28,14 @@ const tenderListOrder = (function () {
         // }
         return pinyin.compareWord(x, y);
     }
-    function reOrderTenders (orderStr, htmlClass = '.c-body', loadHide = true) {
+    function reOrderTenders (orderStr, htmlClass = '.c-body', loadHide = true, ts = tenders) {
         if (orderStr) {
             orderSetting = orderStr;
             setLocalCache('zh-calc-tender-list-order', orderStr);
         }
         const orders = orderSetting.split('|');
         if (orders[0] === 'name') {
-            tenders.sort(function (a, b) {
+            ts.sort(function (a, b) {
                 // return orders[1] === 'up'
                 //     ? a[orders[0]].localeCompare(b[orders[0]], 'zh')
                 //     : -a[orders[0]].localeCompare(b[orders[0]], 'zh');
@@ -44,7 +44,7 @@ const tenderListOrder = (function () {
                     : CompareStr(b[orders[0]], a[orders[0]]);
             });
         } else if (orders[0] === 'create_time') {
-            tenders.sort(function (a, b){
+            ts.sort(function (a, b){
                 return orders[1] === 'up'
                     ? Date.parse(a[orders[0]]) - Date.parse(b[orders[0]])
                     : Date.parse(b[orders[0]]) - Date.parse(a[orders[0]]);

+ 5 - 0
app/router.js

@@ -440,6 +440,8 @@ module.exports = app => {
     app.get('/sp/:id/financial/transfer/:trid/tender/:ttid/file/:fid/download', sessionAuth, subProjectCheck, financialCheck, 'financialController.transferTenderDownloadFile');
     app.get('/sp/:id/financial/pay', sessionAuth, subProjectCheck, financialCheck, 'financialController.pay');
     app.post('/sp/:id/financial/pay/save', sessionAuth, subProjectCheck, financialCheck, 'financialController.paySave');
+    app.get('/sp/:id/financial/pay/company/:fpcid', sessionAuth, subProjectCheck, financialCheck, 'financialController.payCompany');
+    app.post('/sp/:id/financial/pay/company/:fpcid/save', sessionAuth, subProjectCheck, financialCheck, 'financialController.payCompanySave');
     app.get('/sp/:id/financial/pay/:fpid/detail', sessionAuth, subProjectCheck, financialCheck, financialPayCheck, financialPayAuditCheck, 'financialController.payDetail');
     app.post('/sp/:id/financial/pay/:fpid/save', sessionAuth, subProjectCheck, financialCheck, financialPayCheck, 'financialController.payDetailSave');
     app.post('/sp/:id/financial/pay/:fpid/file/upload', sessionAuth, subProjectCheck, financialCheck, financialPayCheck, 'financialController.payUploadFile');
@@ -1047,6 +1049,9 @@ 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.post('/wap/inspection/ask', 'wapController.inspectionAiAsk');
+    app.post('/wap/voice/token', 'wapController.voiceToken');
 
     // 微信
     app.get('/wx', 'wechatController.index');

+ 76 - 24
app/service/financial_pay.js

@@ -28,6 +28,8 @@ module.exports = app => {
             const pay = await this.getDataById(id);
             const tender = await this.ctx.service.tender.getDataById(pay.tid);
             pay.tenderName = tender.name;
+            const payCompany = pay.fpcid ? await this.ctx.service.financialPayCompany.getOnePayCompany(pay.fpcid) : null;
+            pay.payCompany = payCompany ? payCompany : null;
             return pay;
         }
 
@@ -38,7 +40,7 @@ module.exports = app => {
          * @param {int} hadlimit - 分页
          * @return {object} list - 列表
          */
-        async getListByStatus(spid, status = 0, tid = null, used = null, hadlimit = 0, sortBy = '', orderBy = '') {
+        async getListByStatus(spid, fpcid, status = 0, tid = null, used = null, hadlimit = 0, sortBy = '', orderBy = '') {
             let addSql = '';
             if (tid !== null) {
                 if (tid.length === 0) {
@@ -53,19 +55,19 @@ module.exports = app => {
             let sqlParam = '';
             switch (status) {
                 case 0: // 所有
-                    sql = 'SELECT a.* FROM ?? As a WHERE a.spid = ?' + addSql;
-                    sqlParam = [this.tableName, spid];
+                    sql = 'SELECT a.* FROM ?? As a WHERE a.spid = ? AND a.fpcid = ?' + addSql;
+                    sqlParam = [this.tableName, spid, fpcid];
                     break;
                 case 1: // 待处理(你的)
-                    sql = 'SELECT a.* FROM ?? as a WHERE a.spid = ?' + addSql + ' AND (a.id in(SELECT b.fpid FROM ?? as b WHERE b.spid = ? AND b.aid = ? AND b.status = ?) OR (a.uid = ? AND (a.status = ? OR a.status = ?)))';
-                    sqlParam = [this.tableName, spid, this.ctx.service.financialPayAudit.tableName, spid, this.ctx.session.sessionUser.accountId, auditConst.status.checking, this.ctx.session.sessionUser.accountId, auditConst.status.uncheck, auditConst.status.checkNo];
+                    sql = 'SELECT a.* FROM ?? as a WHERE a.spid = ? AND a.fpcid = ?' + addSql + ' AND (a.id in(SELECT b.fpid FROM ?? as b WHERE b.spid = ? AND b.fpcid = ? AND b.aid = ? AND b.status = ?) OR (a.uid = ? AND (a.status = ? OR a.status = ?)))';
+                    sqlParam = [this.tableName, spid, fpcid, this.ctx.service.financialPayAudit.tableName, spid, fpcid, this.ctx.session.sessionUser.accountId, auditConst.status.checking, this.ctx.session.sessionUser.accountId, auditConst.status.uncheck, auditConst.status.checkNo];
                     break;
                 case 5: // 待上报(所有的)PS:取未上报,退回,修订的变更令
                     sql =
-                        'SELECT a.* FROM ?? as a WHERE a.spid = ?' + addSql + ' AND (a.status = ? OR a.status = ?)';
+                        'SELECT a.* FROM ?? as a WHERE a.spid = ? AND a.fpcid = ?' + addSql + ' AND (a.status = ? OR a.status = ?)';
                     sqlParam = [
                         this.tableName,
-                        spid,
+                        spid, fpcid,
                         auditConst.status.uncheck,
                         auditConst.status.checkNo,
                     ];
@@ -73,14 +75,14 @@ module.exports = app => {
                 case 2: // 进行中(所有的)
                 case 4: // 终止(所有的)
                     sql = 'SELECT a.* FROM ?? as a WHERE ' +
-                        'a.status = ? AND a.spid = ?' + addSql + ' AND (a.uid = ? OR ' +
+                        'a.status = ? AND a.spid = ? AND a.fpcid = ?' + addSql + ' AND (a.uid = ? OR ' +
                         'a.id IN (SELECT b.fpid FROM ?? AS b WHERE b.aid = ? GROUP BY b.fpid))';
-                    sqlParam = [this.tableName, status, spid, this.ctx.session.sessionUser.accountId,
+                    sqlParam = [this.tableName, status, spid, fpcid, this.ctx.session.sessionUser.accountId,
                         this.ctx.service.financialPayAudit.tableName, this.ctx.session.sessionUser.accountId];
                     break;
                 case 3: // 已完成(所有的)
-                    sql = 'SELECT a.* FROM ?? as a WHERE a.status = ? AND a.spid = ?' + addSql;
-                    sqlParam = [this.tableName, status, spid];
+                    sql = 'SELECT a.* FROM ?? as a WHERE a.status = ? AND a.spid = ? AND a.fpcid = ?' + addSql;
+                    sqlParam = [this.tableName, status, spid, fpcid];
                     break;
                 default:
                     break;
@@ -110,7 +112,7 @@ module.exports = app => {
          * @param {int} status - 状态
          * @return {void}
          */
-        async getCountByStatus(spid, status = 0, tid = null, used = null) {
+        async getCountByStatus(spid, fpcid, status = 0, tid = null, used = null) {
             let addSql = '';
             if (tid !== null) {
                 if (tid.length === 0) {
@@ -123,22 +125,22 @@ module.exports = app => {
             }
             switch (status) {
                 case 0: // 所有
-                    const sql5 = 'SELECT count(*) AS count FROM ?? As a WHERE a.spid = ?' + addSql;
-                    const sqlParam5 = [this.tableName, spid];
+                    const sql5 = 'SELECT count(*) AS count FROM ?? As a WHERE a.spid = ? AND a.fpcid = ?' + addSql;
+                    const sqlParam5 = [this.tableName, spid, fpcid];
                     const result5 = await this.db.query(sql5, sqlParam5);
                     return result5[0].count;
                 case 1: // 待处理(你的)
-                    const sql6 = 'SELECT count(*) AS count FROM ?? as a WHERE a.spid = ?' + addSql + ' AND (a.id in(SELECT b.fpid FROM ?? as b WHERE b.spid = ? AND b.aid = ? AND b.status = ?) OR (a.uid = ? AND (a.status = ? OR a.status = ?)))';
-                    const sqlParam6 = [this.tableName, spid, this.ctx.service.financialPayAudit.tableName, spid, this.ctx.session.sessionUser.accountId, auditConst.status.checking, this.ctx.session.sessionUser.accountId, auditConst.status.uncheck, auditConst.status.checkNo];
+                    const sql6 = 'SELECT count(*) AS count FROM ?? as a WHERE a.spid = ? AND a.fpcid = ?' + addSql + ' AND (a.id in(SELECT b.fpid FROM ?? as b WHERE b.spid = ? AND b.fpcid = ? AND b.aid = ? AND b.status = ?) OR (a.uid = ? AND (a.status = ? OR a.status = ?)))';
+                    const sqlParam6 = [this.tableName, spid, fpcid, this.ctx.service.financialPayAudit.tableName, spid, fpcid, this.ctx.session.sessionUser.accountId, auditConst.status.checking, this.ctx.session.sessionUser.accountId, auditConst.status.uncheck, auditConst.status.checkNo];
                     const result6 = await this.db.query(sql6, sqlParam6);
                     return result6[0].count;
                 case 5: // 待上报(所有的)PS:取未上报,退回的变更立项
                     const sql2 =
                         'SELECT count(*) AS count FROM ?? as a WHERE ' +
-                        'a.spid = ?' + addSql + ' AND (a.status = ? OR a.status = ?)';
+                        'a.spid = ? AND a.fpcid = ?' + addSql + ' AND (a.status = ? OR a.status = ?)';
                     const sqlParam2 = [
                         this.tableName,
-                        spid,
+                        spid, fpcid,
                         auditConst.status.uncheck,
                         auditConst.status.checkNo,
                     ];
@@ -148,13 +150,13 @@ module.exports = app => {
                 case 4: // 终止(所有的)
                     const sql3 =
                         'SELECT count(*) AS count FROM ?? as a WHERE ' +
-                        'a.status = ? AND a.spid = ?' + addSql + ' AND (a.uid = ? OR a.id IN (SELECT b.fpid FROM ?? AS b WHERE b.aid = ? GROUP BY b.fpid))';
-                    const sqlParam3 = [this.tableName, status, spid, this.ctx.session.sessionUser.accountId, this.ctx.service.financialPayAudit.tableName, this.ctx.session.sessionUser.accountId];
+                        'a.status = ? AND a.spid = ? AND a.fpcid = ?' + addSql + ' AND (a.uid = ? OR a.id IN (SELECT b.fpid FROM ?? AS b WHERE b.aid = ? GROUP BY b.fpid))';
+                    const sqlParam3 = [this.tableName, status, spid, fpcid, this.ctx.session.sessionUser.accountId, this.ctx.service.financialPayAudit.tableName, this.ctx.session.sessionUser.accountId];
                     const result3 = await this.db.query(sql3, sqlParam3);
                     return result3[0].count;
                 case 3: // 已完成(所有的)
-                    const sql4 = 'SELECT count(*) AS count FROM ?? as a WHERE a.status = ? AND a.spid = ?' + addSql;
-                    const sqlParam4 = [this.tableName, status, spid];
+                    const sql4 = 'SELECT count(*) AS count FROM ?? as a WHERE a.status = ? AND a.spid = ? AND a.fpcid = ?' + addSql;
+                    const sqlParam4 = [this.tableName, status, spid, fpcid];
                     const result4 = await this.db.query(sql4, sqlParam4);
                     return result4[0].count;
                 default:
@@ -162,7 +164,7 @@ module.exports = app => {
             }
         }
 
-        async addPay(spid, data) {
+        async addPay(spid, payCompany, data) {
             if (!data.tid || !data.code || !data.used) {
                 throw '参数错误';
             }
@@ -175,6 +177,7 @@ module.exports = app => {
                 }
                 const insertData = {
                     spid,
+                    fpcid: payCompany.id,
                     tid: data.tid,
                     code: data.code,
                     used: data.used,
@@ -198,6 +201,7 @@ module.exports = app => {
                         spid,
                         tid: data.tid,
                         fpid: result.insertId,
+                        fpcid: payCompany.id,
                         aid: audit.audit_id,
                         order: audit.audit_order,
                         times: 1,
@@ -207,6 +211,7 @@ module.exports = app => {
                     });
                 }
                 if (insertAuditDatas.length > 0) await transaction.insert(this.ctx.service.financialPayAudit.tableName, insertAuditDatas);
+                if (payCompany.can_del === 1) await transaction.update(this.ctx.service.financialPayCompany.tableName, { id: payCompany.id, can_del: 0 });
                 await transaction.commit();
                 return { id: result.insertId };
             } catch (e) {
@@ -215,7 +220,7 @@ module.exports = app => {
             }
         }
 
-        async delPay(fpid) {
+        async delPay(payCompany, fpid) {
             if (!fpid) {
                 throw '参数有误';
             }
@@ -232,6 +237,7 @@ module.exports = app => {
                 await transaction.delete(this.ctx.service.financialPayAtt.tableName, { fpid });
                 await transaction.delete(this.ctx.service.financialPayAudit.tableName, { fpid });
                 await transaction.delete(this.ctx.service.financialPayContract.tableName, { fpid });
+                await this.ctx.service.financialPayCompany.updatePayCompanyAndAfter(transaction, payCompany.spid, payCompany);
                 await transaction.commit();
             } catch (err) {
                 console.log(err);
@@ -303,6 +309,52 @@ module.exports = app => {
             }]);
             pay.finalAuditorIds = pay.userGroups[pay.userGroups.length - 1].map(x => { return x.aid; });
         }
+
+        async batchOldPays(spid, payCompany, payIds) {
+            if (!payCompany || !payIds || payIds.length === 0) {
+                throw '参数有误';
+            }
+            const transaction = await this.db.beginTransaction();
+            try {
+                const updateAuditDatas = [];
+                const updateDatas = [];
+                const oldPays = await this.getAllDataByCondition({ where: { id: payIds } });
+                const oldPayAudits = await this.ctx.service.financialPayAudit.getAllDataByCondition({ where: { fpid: payIds } });
+                for (const node of oldPays) {
+                    if (node.fpcid) {
+                        throw '该支付已绑定单位期';
+                    }
+                    updateDatas.push({
+                        id: node.id,
+                        fpcid: payCompany.id,
+                    });
+                    const oldPayAuditsByFpid = oldPayAudits.filter(x => { return x.fpid === node.id; });
+                    for (const audit of oldPayAuditsByFpid) {
+                        if (!audit.fpcid) {
+                            updateAuditDatas.push({
+                                id: audit.id,
+                                fpcid: payCompany.id,
+                            });
+                        }
+                    }
+                }
+                if (updateDatas.length > 0) {
+                    await transaction.updateRows(this.tableName, updateDatas);
+                }
+                if (updateAuditDatas.length > 0) {
+                    await transaction.updateRows(this.ctx.service.financialPayAudit.tableName, updateAuditDatas);
+                }
+                // 更新本单位期金额和同单位的上期金额
+                await transaction.update(this.ctx.service.financialPayCompany.tableName, { id: payCompany.id, can_del: 0 });
+                await this.ctx.service.financialPayCompany.updatePayCompanyAndAfter(transaction, spid, payCompany);
+                await transaction.commit();
+                return true;
+            } catch (err) {
+                console.log(err);
+                await transaction.rollback();
+                throw err;
+            }
+        }
     }
     return FinancialPay;
 };

+ 236 - 0
app/service/financial_pay_company.js

@@ -0,0 +1,236 @@
+'use strict';
+
+/**
+ * Created by EllisRan on 2020/3/3.
+ */
+
+const BaseService = require('../base/base_service');
+const auditConst = require('../const/audit').financial;
+const auditType = require('../const/audit').auditType;
+const shenpiConst = require('../const/shenpi');
+
+module.exports = app => {
+
+    class FinancialPayCompany extends BaseService {
+
+        /**
+         * 构造函数
+         *
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        constructor(ctx) {
+            super(ctx);
+            this.tableName = 'financial_pay_company';
+        }
+
+        async getOnePayCompany(id) {
+            const sql = 'SELECT a.*, pa.name as username, c.name as company FROM ?? As a LEFT JOIN ?? As pa ON a.uid = pa.id LEFT JOIN ?? As c ON a.company_id = c.id WHERE a.id = ?';
+            const sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, this.ctx.service.constructionUnit.tableName, id];
+            return await this.db.queryOne(sql, sqlParam);
+        }
+
+        async getUserCompanyList(spid, user_company_id, unitList) {
+            let userCompanyList = [];
+            if (this.ctx.session.sessionUser.is_admin) {
+                userCompanyList = unitList;
+            } else {
+                let addSql = '';
+                let notAdminSql = '';
+                if (!this.ctx.session.sessionUser.is_admin) {
+                    notAdminSql += '(a.id in (SELECT fpcid FROM ' + this.ctx.service.financialPay.tableName + ' WHERE uid = ' + this.ctx.session.sessionUser.accountId + ' GROUP BY fpcid)' +
+                        ' OR a.id in (SELECT fpcid FROM ' + this.ctx.service.financialPayAudit.tableName + ' WHERE aid = ' + this.ctx.session.sessionUser.accountId + ' GROUP BY fpcid))';
+                    addSql += ' AND (a.company_id = ' + user_company_id + ' OR ' + notAdminSql + ')';
+                }
+                const sql = 'SELECT a.company_id FROM ?? As a' +
+                    ' WHERE a.spid = ?' + addSql + ' GROUP BY company_id';
+                const sqlParam = [this.tableName, spid];
+                const list = await this.db.query(sql, sqlParam);
+                const companyIds = list.map(item => item.company_id);
+                userCompanyList = unitList.filter(unit => companyIds.includes(unit.id));
+                if (userCompanyList.length === 0 || userCompanyList.findIndex(u => u.id === user_company_id) === -1) {
+                    userCompanyList.push(unitList.find(u => u.id === user_company_id));
+                }
+            }
+            const allList = await this.getAllDataByCondition({ where: { spid } });
+            for (const u of userCompanyList) {
+                const count = allList.filter(item => item.company_id === u.id).length;
+                u.count = count;
+            }
+            return userCompanyList;
+        }
+
+        /**
+         * 获取列表
+         * @param {int} spid - 项目id
+         * @param {int} status - 状态
+         * @param {int} hadlimit - 分页
+         * @return {object} list - 列表
+         */
+        async getListByStatus(spid, company_id = null, user_company_id, hadlimit = 1) {
+            let addSql = '';
+            let notAdminSql = '';
+            if (!this.ctx.session.sessionUser.is_admin) {
+                notAdminSql += '(a.id in (SELECT fpcid FROM ' + this.ctx.service.financialPay.tableName + ' WHERE uid = ' + this.ctx.session.sessionUser.accountId + ' GROUP BY fpcid)' +
+                    ' OR a.id in (SELECT fpcid FROM ' + this.ctx.service.financialPayAudit.tableName + ' WHERE aid = ' + this.ctx.session.sessionUser.accountId + ' GROUP BY fpcid))';
+            }
+            if (company_id !== null) {
+                addSql += ' AND (a.company_id = ' + company_id + (!this.ctx.session.sessionUser.is_admin && company_id !== user_company_id ? ' AND ' + notAdminSql : '') + ')';
+            } else if (company_id === null && !this.ctx.session.sessionUser.is_admin) {
+                addSql += ' AND (a.company_id = ' + user_company_id + ' OR ' + notAdminSql + ')';
+            }
+            let sql = 'SELECT a.*, pa.name as username, c.name as company FROM ?? As a' +
+                ' LEFT JOIN ?? As pa ON a.uid = pa.id LEFT JOIN ?? As c ON a.company_id = c.id' +
+                ' WHERE a.spid = ?' + addSql + ' ORDER BY a.create_time DESC';
+            const sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, this.ctx.service.constructionUnit.tableName, spid];
+            if (hadlimit) {
+                const limit = this.ctx.pageSize ? this.ctx.pageSize : this.app.config.pageSize;
+                const offset = limit * (this.ctx.page - 1);
+                const limitString = offset >= 0 ? offset + ',' + limit : limit;
+                sql += ' LIMIT ' + limitString;
+            }
+            const list = await this.db.query(sql, sqlParam);
+            return list;
+        }
+
+        /**
+         * 获取支付个数
+         * @param {int} spid - 项目id
+         * @param {int} status - 状态
+         * @return {void}
+         */
+        async getCountByStatus(spid, company_id = null, user_company_id) {
+            let addSql = '';
+            let notAdminSql = '';
+            if (!this.ctx.session.sessionUser.is_admin) {
+                notAdminSql += '(a.id in (SELECT fpcid FROM ' + this.ctx.service.financialPay.tableName + ' WHERE uid = ' + this.ctx.session.sessionUser.accountId + ' GROUP BY fpcid)' +
+                    ' OR a.id in (SELECT fpcid FROM ' + this.ctx.service.financialPayAudit.tableName + ' WHERE aid = ' + this.ctx.session.sessionUser.accountId + ' GROUP BY fpcid))';
+            }
+            if (company_id !== null) {
+                addSql += ' AND (a.company_id = ' + company_id + (!this.ctx.session.sessionUser.is_admin && company_id !== user_company_id ? ' AND ' + notAdminSql : '') + ')';
+            } else if (company_id === null && !this.ctx.session.sessionUser.is_admin) {
+                addSql += ' AND (a.company_id = ' + user_company_id + ' OR ' + notAdminSql + ')';
+            }
+            const sql = 'SELECT count(*) AS count FROM ?? As a' +
+                ' LEFT JOIN ?? As pa ON a.uid = pa.id LEFT JOIN ?? As c ON a.company_id = c.id' +
+                ' WHERE a.spid = ?' + addSql + ' ORDER BY a.create_time DESC';
+            const sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, this.ctx.service.constructionUnit.tableName, spid];
+            const result = await this.db.queryOne(sql, sqlParam);
+            return result ? result.count : 0;
+        }
+
+        async addPayCompany(spid, data) {
+            if (!data.company_id || !data.order) {
+                throw '参数错误';
+            }
+            const times = new Date();
+            const info = await this.ctx.service.financialPayCompany.getDataByCondition({ spid, company_id: data.company_id, order: data.order });
+            if (info) {
+                throw '资金支付单位期已存在';
+            }
+            const allCompanyList = await this.ctx.service.financialPayCompany.getAllDataByCondition({ where: { spid, company_id: data.company_id } });
+            const transcation = await this.db.beginTransaction();
+            try {
+                await transcation.update(this.tableName, { can_del: 0 }, { where: { spid, company_id: data.company_id } });
+                const insertData = {
+                    spid,
+                    company_id: data.company_id,
+                    order: data.order,
+                    uid: this.ctx.session.sessionUser.accountId,
+                    pre_tp: this.ctx.helper.sum(this._.map(allCompanyList, 'total_price')) || 0,
+                    create_time: times,
+                };
+                const result = await transcation.insert(this.tableName, insertData);
+                await transcation.commit();
+                return await this.getOnePayCompany(result.insertId);
+            } catch (err) {
+                await transcation.rollback();
+                throw err;
+            }
+        }
+
+        async delPayCompany(fpcid) {
+            if (!fpcid) {
+                throw '参数有误';
+            }
+            const node = await this.getDataById(fpcid);
+            if (!node) {
+                throw '资金支付单位期不存在';
+            }
+            const payCount = await this.ctx.service.financialPay.count({ fpcid }) || 0;
+            if (payCount > 0) {
+                throw '该资金支付单位期下存在资金支付记录,不能删除';
+            }
+            const transaction = await this.db.beginTransaction();
+            let result = false;
+            try {
+                await transaction.delete(this.tableName, { id: node.id });
+                // 判断是否能删除上一期
+                if (node.order > 1) {
+                    const lastPayCompany = await this.getDataByCondition({ spid: node.spid, company_id: node.company_id, order: node.order - 1 });
+                    if (lastPayCompany) {
+                        const companyPays = await transaction.select(this.ctx.service.financialPay.tableName, { where: { fpcid: lastPayCompany.id } });
+                        if (companyPays.length === 0 && lastPayCompany.can_del === 0) {
+                            await transaction.update(this.tableName, { id: lastPayCompany.id, can_del: 1 });
+                        }
+                    }
+                }
+                await transaction.commit();
+                result = true;
+            } catch (error) {
+                await transaction.rollback();
+                result = false;
+            }
+            return result;
+        }
+
+        async savePayCompany(fpcid, postData) {
+            // 初始化事务
+            const transaction = await this.db.beginTransaction();
+            let result = false;
+            try {
+                const updateData = {
+                    id: fpcid,
+                };
+                updateData[postData.name] = postData.val;
+                await transaction.update(this.tableName, updateData);
+                await transaction.commit();
+                result = true;
+            } catch (error) {
+                await transaction.rollback();
+                result = false;
+            }
+            return result;
+        }
+
+        async updatePayCompanyAndAfter(transaction, spid, payCompany) {
+            let total_price = 0;
+            const companyPays = await transaction.select(this.ctx.service.financialPay.tableName, { where: { fpcid: payCompany.id } });
+            for (const pay of companyPays) {
+                total_price = this.ctx.helper.add(total_price, pay.total_price);
+            }
+            await transaction.update(this.tableName, { id: payCompany.id, total_price });
+            const allCompanyPays = await transaction.select(this.tableName, { where: { spid, company_id: payCompany.company_id } });
+            const highestOrder = this._.max(this._.map(allCompanyPays, 'order')) || 0;
+            if (companyPays.length === 0 && highestOrder === payCompany.order) {
+                // 如果当前支付单位期没有支付记录,并且是最高的order,则将其删除
+                await transaction.update(this.tableName, { id: payCompany.id, can_del: 1 });
+            }
+            const afterCompanyPays = allCompanyPays.filter(x => { return x.order > payCompany.order; });
+            if (afterCompanyPays && afterCompanyPays.length > 0) {
+                const pre_tps = [];
+                for (const companyPay of afterCompanyPays) {
+                    const beforeTotalPrice = this.ctx.helper.sum(this._.map(allCompanyPays.filter(x => { return x.order < companyPay.order; }), 'total_price')) || 0;
+                    pre_tps.push({
+                        id: companyPay.id,
+                        pre_tp: beforeTotalPrice,
+                    });
+                }
+                if (pre_tps.length > 0) {
+                    await transaction.updateRows(this.tableName, pre_tps);
+                }
+            }
+        }
+    }
+    return FinancialPayCompany;
+};

+ 8 - 7
app/service/financial_pay_contract.js

@@ -97,7 +97,7 @@ module.exports = app => {
             }
         }
 
-        async delContract(fpid, ids) {
+        async delContract(fpid, payCompany, ids) {
             const transaction = await this.db.beginTransaction();
             try {
                 // 还要删除附件
@@ -107,7 +107,7 @@ module.exports = app => {
                 // 判断是否可删
                 await transaction.delete(this.tableName, { id: ids });
                 // 重新算资金支付总额
-                const tp = await this.calcCamountSum(fpid, transaction);
+                const tp = await this.calcCamountSum(fpid, payCompany, transaction);
                 await transaction.commit();
                 return { tp, contractList: await this.getContractList(fpid) };
             } catch (err) {
@@ -116,11 +116,11 @@ module.exports = app => {
             }
         }
 
-        async updateContract(fpid, data) {
+        async updateContract(fpid, payCompany, data) {
             const transaction = await this.db.beginTransaction();
             try {
                 await transaction.update(this.tableName, data);
-                const tp = await this.calcCamountSum(fpid, transaction);
+                const tp = await this.calcCamountSum(fpid, payCompany, transaction);
                 await transaction.commit();
                 return { tp };
             } catch (err) {
@@ -134,7 +134,7 @@ module.exports = app => {
          * @param {Object} datas 修改内容
          * @return {void}
          */
-        async updateContracts(fpid, datas) {
+        async updateContracts(fpid, payCompany, datas) {
             const transaction = await this.db.beginTransaction();
             try {
                 const updateArray = [];
@@ -144,7 +144,7 @@ module.exports = app => {
                     }
                 }
                 if (updateArray.length > 0) await transaction.updateRows(this.tableName, updateArray);
-                const tp = await this.calcCamountSum(fpid, transaction);
+                const tp = await this.calcCamountSum(fpid, payCompany, transaction);
                 await transaction.commit();
                 return { tp, contractList: await this.getContractList(fpid) };
             } catch (err) {
@@ -153,7 +153,7 @@ module.exports = app => {
             }
         }
 
-        async calcCamountSum(fpid, transaction) {
+        async calcCamountSum(fpid, payCompany, transaction) {
             // 防止小数位不精确,采用取值计算
             const sql = 'SELECT `small_expenses`, `pay_price` FROM ?? WHERE fpid = ?';
             const sqlParam = [this.tableName, fpid];
@@ -171,6 +171,7 @@ module.exports = app => {
                 small_expenses_tp,
             };
             await transaction.update(this.ctx.service.financialPay.tableName, updateData);
+            await this.ctx.service.financialPayCompany.updatePayCompanyAndAfter(transaction, payCompany.spid, payCompany);
             return total_price;
         }
 

+ 45 - 0
app/service/financial_pay_tender.js

@@ -32,6 +32,51 @@ module.exports = app => {
             await this.db.update(this.tableName, data);
             return await this.getDataById(data.id);
         }
+
+        async copyBank2otherTender(spid, tenders, copy_tender) {
+            const insertDatas = [];
+            const updateDatas = [];
+            for (const tender of tenders) {
+                if (tender.tid === copy_tender.tid) {
+                    continue; // 跳过复制的标
+                }
+                if (tender.id === 0) {
+                    delete tender.id;
+                    insertDatas.push({
+                        tid: tender.tid,
+                        spid,
+                        name: copy_tender.name,
+                        bank: copy_tender.bank,
+                        bank_account: copy_tender.bank_account,
+                        contact: copy_tender.contact,
+                        phone: copy_tender.phone,
+                    });
+                } else {
+                    updateDatas.push({
+                        id: tender.id,
+                        name: copy_tender.name,
+                        bank: copy_tender.bank,
+                        bank_account: copy_tender.bank_account,
+                        contact: copy_tender.contact,
+                        phone: copy_tender.phone,
+                    });
+                }
+            }
+            const transaction = await this.db.beginTransaction();
+            try {
+                if (insertDatas.length > 0) {
+                    await transaction.insert(this.tableName, insertDatas);
+                }
+                if (updateDatas.length > 0) {
+                    await transaction.updateRows(this.tableName, updateDatas);
+                }
+                await transaction.commit();
+            } catch (e) {
+                await transaction.rollback();
+                throw e;
+            }
+            return await this.getAllDataByCondition({ where: { spid } });
+        }
     }
     return FinancialPayTender;
 };

+ 11 - 0
app/service/financial_transfer.js

@@ -42,8 +42,14 @@ module.exports = app => {
             if (node) {
                 throw '资金划拨年月已存在,请勿重复';
             }
+            let pre_hb_tp = 0;
+            const ftList = await this.getAllDataByCondition({ where: { spid } });
+            if (ftList.length > 0) {
+                pre_hb_tp = this.ctx.helper.sum(this._.map(ftList, 'total_price'));
+            }
             const insertData = {
                 spid,
+                pre_hb_tp,
                 t_time: date,
                 create_time: new Date(),
                 remark,
@@ -71,7 +77,12 @@ module.exports = app => {
                 const attList = await this.ctx.service.financialTransferAtt.getAllDataByCondition({ where: { trid } });
                 const tenderAttList = await this.ctx.service.financialTransferTenderAtt.getAllDataByCondition({ where: { trid } });
                 await this.ctx.helper.delFiles(this._.concat(attList, tenderAttList));
+                // 重算截止上次划拨金额
+                const fttList = await this.ctx.service.financialTransferTender.getAllDataByCondition({ where: { trid } });
                 await transaction.delete(this.ctx.service.financialTransferTender.tableName, { trid });
+                for (const ftt of fttList) {
+                    await this.ctx.service.financialTransferTender.calcPreHbTp(transaction, node, ftt);
+                }
                 await transaction.commit();
             } catch (err) {
                 await transaction.rollback();

+ 151 - 20
app/service/financial_transfer_tender.js

@@ -22,13 +22,13 @@ module.exports = app => {
         }
 
         async getList(trid, pageLimit = 0) {
-            let sql = 'SELECT ftt.*, t.`name` as name FROM ?? as ftt LEFT JOIN ?? as t ON ftt.`tid` = t.`id` WHERE ftt.`trid` = ?';
-            if (pageLimit) {
-                const limit = this.ctx.pageSize ? this.ctx.pageSize : this.app.config.pageSize;
-                const offset = limit * (this.ctx.page - 1);
-                const limitString = offset >= 0 ? offset + ',' + limit : limit;
-                sql += ' LIMIT ' + limitString;
-            }
+            const sql = 'SELECT ftt.*, t.`name` as name FROM ?? as ftt LEFT JOIN ?? as t ON ftt.`tid` = t.`id` WHERE ftt.`trid` = ?';
+            // if (pageLimit) {
+            //     const limit = this.ctx.pageSize ? this.ctx.pageSize : this.app.config.pageSize;
+            //     const offset = limit * (this.ctx.page - 1);
+            //     const limitString = offset >= 0 ? offset + ',' + limit : limit;
+            //     sql += ' LIMIT ' + limitString;
+            // }
             const sqlParams = [this.tableName, this.ctx.service.tender.tableName, trid];
             const list = await this.db.query(sql, sqlParams);
             for (const l of list) {
@@ -41,27 +41,87 @@ module.exports = app => {
             const transaction = await this.db.beginTransaction();
             try {
                 const insertDatas = [];
+                const needCalc = [];
+                const updateDatas = [];
+                const ftList = await this.ctx.service.financialTransfer.getAllDataByCondition({ where: { spid: transferInfo.spid } });
+                const beforeFt = this._.filter(ftList, function(item) {
+                    return item.id < transferInfo.id;
+                });
+                const afterFt = this._.filter(ftList, function(item) {
+                    return item.id > transferInfo.id;
+                });
                 for (const t of tenders) {
-                    const tender = await this.ctx.service.tender.getDataById(t.tid);
-                    const stages = await this.ctx.service.stage.getAllDataByCondition({ where: { tid: t.tid, order: t.sorder } });
+                    const tender = await this.ctx.service.tender.getDataById(t);
+                    if (!tender) {
+                        throw '标段不存在';
+                    }
+                    const ftt = await this.getDataByCondition({ spid: transferInfo.spid, trid: transferInfo.id, tid: t });
+                    if (ftt) {
+                        throw '资金划拨标段已存在';
+                    }
+                    await this.ctx.service.tenderCache.loadTenderCache(tender, this.ctx.session.sessionUser.accountId);
+                    if (tender.stage_tp) {
+                        tender.end_sf_tp = this.ctx.helper.add(tender.stage_tp.pre_sf_tp, tender.stage_tp.sf_tp);
+                    } else if (tender.lastStage) {
+                        tender.end_sf_tp = this.ctx.helper.add(tender.lastStage.pre_sf_tp, tender.lastStage.sf_tp);
+                    }
+                    // 找出已添加的划拨标段,得出pre_hb_tp
+                    let pre_hb_tp = 0;
+                    if (beforeFt.length > 0) {
+                        const beforeFttList = await this.getAllDataByCondition({ where: { spid: transferInfo.spid, trid: this._.map(beforeFt, 'id'), tid: t } });
+                        if (beforeFttList && beforeFttList.length > 0) {
+                            pre_hb_tp = this._.sumBy(beforeFttList, 'hb_tp');
+                        }
+                    }
+                    // const stages = await this.ctx.service.stage.getAllDataByCondition({ where: { tid: t.tid, order: t.sorder } });
                     const insertData = {
                         spid: transferInfo.spid,
                         trid: transferInfo.id,
-                        tid: t.tid,
-                        sorder: t.sorder.join(','),
+                        tid: t,
                         uid: this.ctx.session.sessionUser.accountId,
-                        total_price: tender.total_price,
-                        contract_tp: this._.sumBy(stages, 'contract_tp'),
-                        qc_tp: this._.sumBy(stages, 'qc_tp'),
-                        pc_tp: this._.sumBy(stages, 'pc_tp'),
-                        yf_tp: this._.sumBy(stages, 'yf_tp'),
-                        sf_tp: this._.sumBy(stages, 'sf_tp'),
-                        hb_tp: this._.sumBy(stages, 'sf_tp'),
+                        hb_tp: tender.end_sf_tp,
+                        pre_hb_tp,
                     };
+                    if (afterFt.length > 0) {
+                        const afterFttList = await this.getAllDataByCondition({ where: { spid: transferInfo.spid, trid: this._.map(afterFt, 'id'), tid: t } });
+                        if (afterFttList && afterFttList.length > 0) {
+                            for (const aftt of afterFttList) {
+                                aftt.pre_hb_tp = this.ctx.helper.add(aftt.pre_hb_tp, insertData.hb_tp);
+                                updateDatas.push({ id: aftt.id, pre_hb_tp: aftt.pre_hb_tp });
+                                needCalc.push(aftt.trid);
+                            }
+                        }
+                    }
                     insertDatas.push(insertData);
                 }
                 await transaction.insert(this.tableName, insertDatas);
+                if (updateDatas.length > 0) {
+                    await transaction.updateRows(this.tableName, updateDatas);
+                }
+                // 计算划拨金额
                 await this.calcHbTp(transferInfo.id, transaction);
+                if (needCalc.length > 0) {
+                    const newFtList = await transaction.select(this.ctx.service.financialTransfer.tableName, { where: { spid: transferInfo.spid } });
+                    // 更新划拨金额
+                    const updatePreHbTp = [];
+                    for (const tr of needCalc) {
+                        // await this.calcHbTp(tr, transaction);
+                        const thisFt = this._.find(newFtList, { id: tr });
+                        let pre_hb_tp = 0;
+                        const beforeFtList = this._.filter(newFtList, function(item) {
+                            return item.id < tr;
+                        });
+                        if (beforeFtList && beforeFtList.length > 0) {
+                            pre_hb_tp = this.ctx.helper.sum(this._.map(beforeFtList, 'total_price'));
+                        }
+                        if (pre_hb_tp !== thisFt.pre_hb_tp) {
+                            updatePreHbTp.push({ id: tr, pre_hb_tp });
+                        }
+                    }
+                    if (updatePreHbTp.length > 0) {
+                        await transaction.updateRows(this.ctx.service.financialTransfer.tableName, updatePreHbTp);
+                    }
+                }
                 await transaction.commit();
                 return true;
             } catch (e) {
@@ -71,7 +131,7 @@ module.exports = app => {
             }
         }
 
-        async delTenders(ftid) {
+        async delTenders(transferInfo, ftid) {
             if (!ftid) {
                 throw '参数有误';
             }
@@ -86,6 +146,7 @@ module.exports = app => {
                 const attList = await this.ctx.service.financialTransferTenderAtt.getAllDataByCondition({ where: { ftid } });
                 await this.ctx.helper.delFiles(attList);
                 await this.calcHbTp(node.trid, transaction);
+                await this.calcPreHbTp(transaction, transferInfo, node);
                 await transaction.commit();
             } catch (err) {
                 await transaction.rollback();
@@ -94,7 +155,7 @@ module.exports = app => {
             return true;
         }
 
-        async updateHbTp(ftid, hb_tp) {
+        async updateHbTp(transferInfo, ftid, hb_tp) {
             if (!ftid) {
                 throw '参数有误';
             }
@@ -110,6 +171,7 @@ module.exports = app => {
             try {
                 await transaction.update(this.tableName, { id: node.id, hb_tp });
                 await this.calcHbTp(node.trid, transaction);
+                await this.calcPreHbTp(transaction, transferInfo, node, hb_tp);
                 await transaction.commit();
             } catch (err) {
                 await transaction.rollback();
@@ -118,6 +180,75 @@ module.exports = app => {
             return true;
         }
 
+        async updateTender(transferInfo, postData) {
+            const keys = ['source'];
+            if (!this._.includes(keys, postData.key)) {
+                throw '参数有误';
+            }
+            const node = await this.getDataById(postData.node);
+            if (!node) {
+                throw '资金划拨标段不存在';
+            }
+            if (node.is_lock) {
+                throw '资金划拨标段已锁定,无法修改';
+            }
+            const transaction = await this.db.beginTransaction();
+            try {
+                const updateData = {
+                    id: node.id,
+                };
+                updateData[postData.key] = postData.value;
+                await transaction.update(this.tableName, updateData);
+                await transaction.commit();
+            } catch (err) {
+                await transaction.rollback();
+                throw err;
+            }
+            return true;
+        }
+
+        async calcPreHbTp(transaction, transferInfo, ftt, newHbTp = 0) {
+            const ftList = await transaction.select(this.ctx.service.financialTransfer.tableName, { where: { spid: transferInfo.spid } });
+            const afterFt = this._.filter(ftList, function(item) {
+                return item.id > transferInfo.id;
+            });
+            if (afterFt.length === 0) return;
+            const needCalc = [];
+            const updateDatas = [];
+            const afterFttList = await this.getAllDataByCondition({ where: { spid: transferInfo.spid, trid: this._.map(afterFt, 'id'), tid: ftt.tid } });
+            if (afterFttList && afterFttList.length > 0) {
+                for (const aftt of afterFttList) {
+                    aftt.pre_hb_tp = this.ctx.helper.add(this.ctx.helper.sub(aftt.pre_hb_tp, ftt.hb_tp), newHbTp);
+                    updateDatas.push({ id: aftt.id, pre_hb_tp: aftt.pre_hb_tp });
+                    needCalc.push(aftt.trid);
+                }
+            }
+            if (updateDatas.length > 0) {
+                await transaction.updateRows(this.tableName, updateDatas);
+            }
+            if (needCalc.length > 0) {
+                // 更新划拨金额
+                const updatePreHbTp = [];
+                for (const tr of needCalc) {
+                    // await this.calcHbTp(tr, transaction);
+                    const thisFt = this._.find(ftList, { id: tr });
+                    let pre_hb_tp = 0;
+                    const beforeFtList = this._.filter(ftList, function(item) {
+                        return item.id < tr;
+                    });
+                    if (beforeFtList && beforeFtList.length > 0) {
+                        pre_hb_tp = this.ctx.helper.sum(this._.map(beforeFtList, 'total_price'));
+                    }
+                    if (pre_hb_tp !== thisFt.pre_hb_tp) {
+                        updatePreHbTp.push({ id: tr, pre_hb_tp });
+                    }
+                }
+                if (updatePreHbTp.length > 0) {
+                    await transaction.updateRows(this.ctx.service.financialTransfer.tableName, updatePreHbTp);
+                }
+            }
+        }
+
         async calcHbTp(trid, transaction = null) {
             const sql = 'SELECT SUM(hb_tp) as hb_tp FROM ?? WHERE `trid` = ?';
             const sqlParams = [this.tableName, trid];

+ 31 - 78
app/view/financial/pay.ejs

@@ -4,48 +4,25 @@
         <div class="title-main  d-flex">
             <% include ./sub_mini_menu.ejs %>
             <div class="col-10 pl-0">
-                <div class="d-inline-block"><span class="mr-3">支付列表</span></div>
+<!--                <div class="btn-group group-tab">-->
+<!--                    <a class="btn btn-sm btn-light active" href="javascript:void(0);">支付列表</a>-->
+<!--                    <a class="btn btn-sm btn-light" href="/financial/pay">汇总列表</a>-->
+<!--                    <a class="btn btn-sm btn-light" href="/financial/pay">标段统计</a>-->
+<!--                </div>-->
                 <div class="d-inline-block col-sm-3">
                     <div class="input-group input-group-sm pr-1">
-                        <select class="form-control form-control-sm col-auto" id="tid_select">
+                        <select class="form-control form-control-sm col-auto" id="company_select">
                             <option value="0">全部</option>
-                            <% for (const t of tenders) { %>
-                            <option value="<%- t.id %>" <% if (t.id === tid) { %>selected<% } %> ><%- t.name %></option>
+                            <% for (const c of userCompanyList) { %>
+                            <option value="<%- c.id %>" <% if (c.name === company) { %>selected<% } %>><%- c.name %></option>
                             <% } %>
                         </select>
                     </div>
                 </div>
                 <div class="d-inline-block">
-                    <div class="input-group input-group-sm pr-1">
-                        <div class="btn-group">
-                            <button type="button" class="btn btn-sm btn-light text-primary dropdown-toggle" data-toggle="dropdown" id="used_selected" data-value="<%- used %>">资金用途:<%- used ? used : '全部' %></button>
-                            <div class="dropdown-menu" aria-labelledby="used_selected" id="used_select">
-                                <% if (used !== null) { %><a class="dropdown-item to-log-link" data-val="" href="javascript:void(0);">全部</a><% } %>
-                                <% for (const u of usedList) { %>
-                                <% if (used !== u) { %>
-                                <a class="dropdown-item to-log-link" href="javascript:void(0)" data-val="<%- u %>"><%- u %></a>
-                                <% } %>
-                                <% } %>
-                            </div>
-                        </div>
-                    </div>
-                </div>
-                <div class="d-inline-block">
-                    <div class="input-group input-group-sm pr-1">
-                        <div class="btn-group">
-                            <button type="button" class="btn btn-sm btn-light text-primary dropdown-toggle" data-toggle="dropdown" id="status_selected" data-value="<%- status %>">审批状态:<% if (status !== 0) { %><%- filter.statusString[status] %>(<%- filter.count[status] %>)<% } else { %>全部<% } %></button>
-                            <div class="dropdown-menu" aria-labelledby="status_selected" id="status_select">
-                                <% if (status !== 0) { %><a class="dropdown-item to-log-link" data-val="0" href="javascript:void(0);">全部</a><% } %>
-                                <% for (const fs in filter.status) { %>
-                                    <% const f = filter.status[fs]; %>
-                                    <% if (f !== status) { %><a class="dropdown-item to-log-link" data-val="<%- f %>" href="javascript:void(0);"><%- filter.statusString[f] %>(<%- filter.count[f] %>)</a><% } %>
-                                <% } %>
-                            </div>
-                        </div>
-                    </div>
-                </div>
-                <div class="d-inline-block">
-                    <a href="#payaccount" data-toggle="modal" data-target="#payaccount" class="btn btn-sm btn-primary mr-2" id="show-pay-account" style="display: none">付款账号</a>
+                    <% if (ctx.session.sessionUser.is_admin || fptReportTids) { %>
+                    <a href="#payaccount" data-toggle="modal" data-target="#payaccount" class="btn btn-sm btn-primary mr-2">付款账号</a>
+                    <% } %>
                     <% if (ctx.session.sessionUser.is_admin) { %>
                     <a href="#liucheng" data-toggle="modal" data-target="#liucheng" class="btn btn-sm btn-primary mr-2">审批流程</a>
                     <% } %>
@@ -53,67 +30,41 @@
             </div>
             <div class="d-inline-block ml-auto">
                 <% if (ctx.session.sessionUser.is_admin || fptReportTids.length > 0) { %>
-                <a href="#add-pay" data-toggle="modal" data-target="#add-pay" class="btn btn-primary btn-sm pull-right">申请支付</a>
+                <a href="#add-payqi" data-toggle="modal" data-target="#add-payqi" class="btn btn-primary btn-sm pull-right">新建支付期</a>
                 <% } %>
 <!--                <a href="#batch-sp" data-toggle="modal" data-target="#batch-sp" class="btn btn-success btn-sm pull-right mr-2">批量审批</a>-->
             </div>
         </div>
     </div>
     <div class="content-wrap">
-        <div class="c-body">
-            <div class="sjs-height-0">
+        <div class="sjs-height-0" style="background-color: #fff">
                 <table class="table table-bordered text-center">
                     <thead>
                     <tr>
-                        <th style="min-width: 150px;">标段名称</th>
-                        <th width="200px">支付编号</th>
+                        <th width="100px">期数</th>
+                        <th width="300px">申请单位</th>
                         <th width="150px">申请时间</th>
                         <th width="100px">申请人</th>
-                        <th width="200px">资金用途</th>
-                        <th width="300px">收款单位</th>
-                        <th width="150px">支付金额</th>
-                        <th width="200px">审批进度</th>
+                        <th width="150px">本期金额</th>
+                        <th width="150px">截止上期金额</th>
+                        <th width="150px">截止本期金额</th>
                         <th width="150px">操作</th>
                     </tr>
                     </thead>
                     <tbody id="pay-list">
                     <% for (const pay of payList) { %>
-                        <tr class="text-center" data-tid="<%- pay.tid %>">
-                            <td class="text-left"><%- pay.tenderName %></td>
-                            <td class=""><a href="/sp/<%- ctx.subProject.id %>/financial/pay/<%- pay.id %>/detail"><%- pay.code %></a></td>
+                        <tr class="text-center" data-tid="<%- pay.order %>">
+                            <td class=""><a href="/sp/<%- ctx.subProject.id %>/financial/pay/company/<%- pay.id %>" class="account-page-size">第<%- pay.order %>期</a></td>
+                            <td class="text-left"><%- pay.company %></td>
                             <td class=""><%- moment(pay.create_time).format('YYYY-MM-DD') %></td>
                             <td class=""><%- pay.username %></td>
-                            <td class=""><%- pay.used %></td>
-                            <td class=""><%- pay.entities %></td>
                             <td class="text-right"><%- pay.total_price %></td>
-                            <td class="text-left <%- auditConst.auditProgressClass[pay.status] %>">
-                                <% if (pay.status === auditConst.status.checked && pay.final_auditor_str) { %>
-                                    <a href="#sp-list" data-toggle="modal" data-target="#sp-list" c-id="<%- pay.id %>"><%- pay.final_auditor_str %></a>
-                                <% } else if (pay.curAuditors.length > 0) { %>
-                                    <% if (pay.curAuditors[0].audit_type === auditType.key.common) { %>
-                                        <a href="#sp-list" data-toggle="modal" data-target="#sp-list" c-id="<%- pay.id %>"><%- pay.curAuditors[0].name %><%if (pay.curAuditors[0].role !== '' && pay.curAuditors[0].role !== null) { %>-<%- pay.curAuditors[0].role %><% } %></a>
-                                    <% } else { %>
-                                        <a href="#sp-list" data-toggle="modal" data-target="#sp-list" c-id="<%- pay.id %>"><%- ctx.helper.transFormToChinese(pay.curAuditors[0].audit_order) + '审' %></a>
-                                    <% } %>
-                                <% } %>
-                                <%- auditConst.auditProgress[pay.status] %>
-                            </td>
+                            <td class="text-right"><%- pay.pre_tp %></td>
+                            <td class="text-right"><%- ctx.helper.add(pay.total_price, pay.pre_tp) %></td>
                             <td>
-                                <% if (pay.status === auditConst.status.uncheck && pay.uid === ctx.session.sessionUser.accountId) { %>
-                                    <a href="/sp/<%- ctx.subProject.id %>/financial/pay/<%- pay.id %>/detail" class="btn <%- auditConst.statusButtonClass[pay.status] %> btn-sm"><%- auditConst.statusButton[pay.status] %></a>
-                                <% } else if (pay.status === auditConst.status.checkNo && pay.curAuditors && pay.uid === ctx.session.sessionUser.accountId) { %>
-                                    <a href="/sp/<%- ctx.subProject.id %>/financial/pay/<%- pay.id %>/detail" class="btn <%- auditConst.statusButtonClass[pay.status] %> btn-sm"><%- auditConst.statusButton[pay.status] %></a>
-                                <% } else if (pay.status === auditConst.status.checking && pay.curAuditors && pay.curAuditors.findIndex(x => { return x.aid === ctx.session.sessionUser.accountId; }) >= 0) { %>
-                                    <% const curAudit = pay.curAuditors.find(x => { return x.aid === ctx.session.sessionUser.accountId; }); %>
-                                    <% if (curAudit.status === auditConst.status.checking) { %>
-                                        <a href="/sp/<%- ctx.subProject.id %>/financial/pay/<%- pay.id %>/detail" class="btn <%- auditConst.statusButtonClass[pay.status] %> btn-sm"><%- auditConst.statusButton[pay.status] %></a>
-                                    <% } else { %>
-                                        <span class="<%- auditConst.auditStringClass[curAudit.status] %>"><%- auditConst.auditString[curAudit.status] %></span>
-                                    <% } %>
-                                <% } else { %>
-                                    <span class="<%- auditConst.auditStringClass[pay.status] %>"><%- auditConst.auditString[pay.status] %></span>
+                                <% if ((ctx.session.sessionUser.is_admin || pay.uid === ctx.session.sessionUser.accountId) && pay.can_del) { %>
+                                <a href="javascript:void(0);" data-id="<%- pay.id %>" class="text-danger del-pay-btn">删除</a>
                                 <% } %>
-                                <% if (pay.uid === ctx.session.sessionUser.accountId && (pay.status === auditConst.status.uncheck || pay.status === auditConst.status.checkNo)) { %><a href="javascript:void(0);" data-id="<%- pay.id %>" class="text-danger del-pay-btn">删除</a><% } %>
                             </td>
                         </tr>
                     <% } %>
@@ -122,17 +73,19 @@
                 <!--翻页-->
                 <% include ../layout/page.ejs %>
             </div>
-        </div>
     </div>
 </div>
-<link href="/public/css/bootstrap/select2.min.css" rel="stylesheet" />
-<link rel="stylesheet" href="/public/css/bootstrap/select2-bootstrap4.min.css">
-<script src="/public/js/bootstrap/select2.min.js"></script>
 <script>
     const user_id = <%- ctx.session.sessionUser.accountId %>;
     const is_admin = <%- ctx.session.sessionUser.is_admin %>;
+    const category = JSON.parse(unescape('<%- escape(JSON.stringify(categoryData)) %>'));
     const tenders = JSON.parse(unescape('<%- escape(JSON.stringify(tenders)) %>'));
+    const userCompanyList = JSON.parse(unescape('<%- escape(JSON.stringify(userCompanyList)) %>'));
     const fptReportTids = JSON.parse(unescape('<%- escape(JSON.stringify(fptReportTids)) %>'));
     const auditConst = JSON.parse(unescape('<%- escape(JSON.stringify(auditConst)) %>'));
     const auditType = JSON.parse(unescape('<%- escape(JSON.stringify(auditType)) %>'));
+    const selfCategoryLevel = '';
+    const pid = '<%- ctx.session.sessionProject.id %>';
+    const subProid = '<%- ctx.subProject.id %>';
+    const uphlname = 'user_' + user_id + '_pro_' + pid + '_sub_' + subProid + '_pay_category_hide_list';
 </script>

+ 142 - 0
app/view/financial/pay_company.ejs

@@ -0,0 +1,142 @@
+<% include ./sub_menu.ejs %>
+<div class="panel-content">
+    <div class="panel-title">
+        <div class="title-main  d-flex">
+            <% include ./sub_mini_menu.ejs %>
+            <div class="col-10 pl-0">
+                <div class="d-inline-block"><a href="/sp/<%- ctx.subProject.id %>/financial/pay" class="account-page-size"><i class="fa fa-chevron-left "></i> <span>返回</span></a><span class="text-muted mx-2">|</span><span>第<%- payCompany.order %>期 <%- payCompany.company %></span><span class="text-muted mx-2">|</span></div>
+                <div class="d-inline-block col-sm-3 pl-0">
+                    <div class="input-group input-group-sm pr-1">
+                        <select class="form-control form-control-sm col-auto" id="tid_select">
+                            <option value="0">全部</option>
+                            <% for (const t of tenders) { %>
+                            <option value="<%- t.id %>" <% if (t.id === tid) { %>selected<% } %> ><%- t.name %></option>
+                            <% } %>
+                        </select>
+                    </div>
+                </div>
+                <div class="d-inline-block">
+                    <div class="input-group input-group-sm pr-1">
+                        <div class="btn-group">
+                            <button type="button" class="btn btn-sm btn-light text-primary dropdown-toggle" data-toggle="dropdown" id="used_selected" data-value="<%- used %>">资金用途:<%- used ? used : '全部' %></button>
+                            <div class="dropdown-menu" aria-labelledby="used_selected" id="used_select">
+                                <% if (used !== null) { %><a class="dropdown-item to-log-link" data-val="" href="javascript:void(0);">全部</a><% } %>
+                                <% for (const u of usedList) { %>
+                                <% if (used !== u) { %>
+                                <a class="dropdown-item to-log-link" href="javascript:void(0)" data-val="<%- u %>"><%- u %></a>
+                                <% } %>
+                                <% } %>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+                <div class="d-inline-block">
+                    <div class="input-group input-group-sm pr-1">
+                        <div class="btn-group">
+                            <button type="button" class="btn btn-sm btn-light text-primary dropdown-toggle" data-toggle="dropdown" id="status_selected" data-value="<%- status %>">审批状态:<% if (status !== 0) { %><%- filter.statusString[status] %>(<%- filter.count[status] %>)<% } else { %>全部<% } %></button>
+                            <div class="dropdown-menu" aria-labelledby="status_selected" id="status_select">
+                                <% if (status !== 0) { %><a class="dropdown-item to-log-link" data-val="0" href="javascript:void(0);">全部</a><% } %>
+                                <% for (const fs in filter.status) { %>
+                                    <% const f = filter.status[fs]; %>
+                                    <% if (f !== status) { %><a class="dropdown-item to-log-link" data-val="<%- f %>" href="javascript:void(0);"><%- filter.statusString[f] %>(<%- filter.count[f] %>)</a><% } %>
+                                <% } %>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+                <% if (notCompanyPays.length > 0 && (ctx.session.sessionUser.is_admin || fptReportTids.length > 0)) { %>
+                <div class="d-inline-block">
+                    <a href="#contract-old-pay" data-toggle="modal" data-target="#contract-old-pay" class="btn btn-sm btn-primary mr-2">关联旧数据</a>
+                </div>
+                <% } %>
+            </div>
+            <div class="d-inline-block ml-auto">
+                <% if (ctx.session.sessionUser.is_admin || fptReportTids.length > 0) { %>
+                <a href="#add-pay" data-toggle="modal" data-target="#add-pay" class="btn btn-primary btn-sm pull-right">申请支付</a>
+                <% } %>
+<!--                <a href="#batch-sp" data-toggle="modal" data-target="#batch-sp" class="btn btn-success btn-sm pull-right mr-2">批量审批</a>-->
+            </div>
+        </div>
+    </div>
+    <div class="content-wrap">
+        <div class="sjs-height-0" style="background-color: #fff">
+                <table class="table table-bordered text-center">
+                    <thead>
+                    <tr>
+                        <th style="min-width: 150px;">标段名称</th>
+                        <th width="200px">支付编号</th>
+                        <th width="150px">申请时间</th>
+                        <th width="100px">申请人</th>
+                        <th width="200px">资金用途</th>
+                        <th width="300px">收款单位</th>
+                        <th width="150px">支付金额</th>
+                        <th width="200px">审批进度</th>
+                        <th width="150px">操作</th>
+                    </tr>
+                    </thead>
+                    <tbody id="pay-list">
+                    <% for (const pay of payList) { %>
+                        <tr class="text-center" data-tid="<%- pay.tid %>">
+                            <td class="text-left"><%- pay.tenderName %></td>
+                            <td class=""><a href="/sp/<%- ctx.subProject.id %>/financial/pay/<%- pay.id %>/detail"><%- pay.code %></a></td>
+                            <td class=""><%- moment(pay.create_time).format('YYYY-MM-DD') %></td>
+                            <td class=""><%- pay.username %></td>
+                            <td class=""><%- pay.used %></td>
+                            <td class=""><%- pay.entities %></td>
+                            <td class="text-right"><%- pay.total_price %></td>
+                            <td class="text-left <%- auditConst.auditProgressClass[pay.status] %>">
+                                <% if (pay.status === auditConst.status.checked && pay.final_auditor_str) { %>
+                                    <a href="#sp-list" data-toggle="modal" data-target="#sp-list" c-id="<%- pay.id %>"><%- pay.final_auditor_str %></a>
+                                <% } else if (pay.curAuditors.length > 0) { %>
+                                    <% if (pay.curAuditors[0].audit_type === auditType.key.common) { %>
+                                        <a href="#sp-list" data-toggle="modal" data-target="#sp-list" c-id="<%- pay.id %>"><%- pay.curAuditors[0].name %><%if (pay.curAuditors[0].role !== '' && pay.curAuditors[0].role !== null) { %>-<%- pay.curAuditors[0].role %><% } %></a>
+                                    <% } else { %>
+                                        <a href="#sp-list" data-toggle="modal" data-target="#sp-list" c-id="<%- pay.id %>"><%- ctx.helper.transFormToChinese(pay.curAuditors[0].audit_order) + '审' %></a>
+                                    <% } %>
+                                <% } %>
+                                <%- auditConst.auditProgress[pay.status] %>
+                            </td>
+                            <td>
+                                <% if (pay.status === auditConst.status.uncheck && pay.uid === ctx.session.sessionUser.accountId) { %>
+                                    <a href="/sp/<%- ctx.subProject.id %>/financial/pay/<%- pay.id %>/detail" class="btn <%- auditConst.statusButtonClass[pay.status] %> btn-sm"><%- auditConst.statusButton[pay.status] %></a>
+                                <% } else if (pay.status === auditConst.status.checkNo && pay.curAuditors && pay.uid === ctx.session.sessionUser.accountId) { %>
+                                    <a href="/sp/<%- ctx.subProject.id %>/financial/pay/<%- pay.id %>/detail" class="btn <%- auditConst.statusButtonClass[pay.status] %> btn-sm"><%- auditConst.statusButton[pay.status] %></a>
+                                <% } else if (pay.status === auditConst.status.checking && pay.curAuditors && pay.curAuditors.findIndex(x => { return x.aid === ctx.session.sessionUser.accountId; }) >= 0) { %>
+                                    <% const curAudit = pay.curAuditors.find(x => { return x.aid === ctx.session.sessionUser.accountId; }); %>
+                                    <% if (curAudit.status === auditConst.status.checking) { %>
+                                        <a href="/sp/<%- ctx.subProject.id %>/financial/pay/<%- pay.id %>/detail" class="btn <%- auditConst.statusButtonClass[pay.status] %> btn-sm"><%- auditConst.statusButton[pay.status] %></a>
+                                    <% } else { %>
+                                        <span class="<%- auditConst.auditStringClass[curAudit.status] %>"><%- auditConst.auditString[curAudit.status] %></span>
+                                    <% } %>
+                                <% } else { %>
+                                    <span class="<%- auditConst.auditStringClass[pay.status] %>"><%- auditConst.auditString[pay.status] %></span>
+                                <% } %>
+                                <% if (pay.uid === ctx.session.sessionUser.accountId && (pay.status === auditConst.status.uncheck || pay.status === auditConst.status.checkNo)) { %><a href="javascript:void(0);" data-id="<%- pay.id %>" class="text-danger del-pay-btn">删除</a><% } %>
+                            </td>
+                        </tr>
+                    <% } %>
+                    </tbody>
+                </table>
+                <!--翻页-->
+                <% include ../layout/page.ejs %>
+            </div>
+    </div>
+</div>
+<link href="/public/css/bootstrap/select2.min.css" rel="stylesheet" />
+<link rel="stylesheet" href="/public/css/bootstrap/select2-bootstrap4.min.css">
+<script src="/public/js/bootstrap/select2.min.js"></script>
+<script>
+    const user_id = <%- ctx.session.sessionUser.accountId %>;
+    const fpcid = <%- payCompany.id %>;
+    const is_admin = <%- ctx.session.sessionUser.is_admin %>;
+    const category = JSON.parse(unescape('<%- escape(JSON.stringify(categoryData)) %>'));
+    const tenders = JSON.parse(unescape('<%- escape(JSON.stringify(tenders)) %>'));
+    console.log(tenders);
+    const fptReportTids = JSON.parse(unescape('<%- escape(JSON.stringify(fptReportTids)) %>'));
+    const auditConst = JSON.parse(unescape('<%- escape(JSON.stringify(auditConst)) %>'));
+    const auditType = JSON.parse(unescape('<%- escape(JSON.stringify(auditType)) %>'));
+    const selfCategoryLevel = '';
+    const pid = '<%- ctx.session.sessionProject.id %>';
+    const subProid = '<%- ctx.subProject.id %>';
+    const uphlname = 'user_' + user_id + '_pro_' + pid + '_sub_' + subProid + '_pay_category_hide_list';
+</script>

+ 163 - 0
app/view/financial/pay_company_modal.ejs

@@ -0,0 +1,163 @@
+<% include ../shares/delete_hint_modal.ejs %>
+<% if (ctx.session.sessionUser.is_admin || fptReportTids.length > 0) { %>
+<!-- 申请支付 -->
+<div class="modal fade show" id="add-pay" 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">
+                <style>
+                    .select2-container {
+                        /*display: inline-block!important;*/
+                        /*height: 27px;*/
+                        width: 100% !important;
+                    }
+                    .select2-container--bootstrap4 .select2-selection--single {
+                        height: calc(1.1em + .75rem + 2px) !important;
+                    }
+                    /*.select2-container--bootstrap4 .select2-selection {*/
+                    /*    background-color: #f8f9fa;*/
+                    /*    !*border-color: #f8f9fa;*!*/
+                    /*    border: 1px solid #f8f9fa;*/
+                    /*}*/
+                    /*.select2-container--bootstrap4 .select2-selection--single .select2-selection__rendered:focus{*/
+                    /*    box-shadow: 0 0 0 0.2rem rgba(216,217,219,.5);*/
+                    /*}*/
+                    /*.select2-container--bootstrap4 .select2-selection--single .select2-selection__rendered:hover {*/
+                    /*    background-color: #e2e6ea;*/
+                    /*    border-color: #dae0e5;*/
+                    /*}*/
+                    .select2-container--bootstrap4 .select2-selection--single .select2-selection__rendered {
+                        line-height: calc(1.1em + .75rem);
+                        /*color: #007bff!important;*/
+                        border-radius: 0.2rem;
+                        /*background-color: #f8f9fa;*/
+                        /*border-color: #f8f9fa;*/
+                    }
+                    .select2-container--bootstrap4 .select2-selection--single .select2-selection__arrow b {
+                        border-color: #007bff transparent transparent transparent;
+                        border-width: 4px 3.7px 0;
+                    }
+                    .select2-search--dropdown .select2-search__field {
+                        padding: 0.175rem 0.5rem;
+                    }
+                </style>
+                <div class="form-group mb-2">
+                    <label>支付标段<strong class="text-danger">*</strong></label>
+                    <select class="form-control form-control-sm" id="add-pay-tender">
+                        <% for (const t of tenders) { %>
+                        <% if (ctx.session.sessionUser.is_admin || ctx.helper._.includes(fptReportTids, t.id)) { %>
+                            <option value="<%- t.id %>"><%- t.name %></option>
+                        <% } %>
+                        <% } %>
+                    </select>
+                </div>
+                <div class="form-group mb-2">
+                    <label>支付编号</label>
+                    <input class="form-control form-control-sm" placeholder="标段编号-年月日-3位流水号" id="add-pay-code" type="text" readonly="">
+                    <span class="text-danger"></span>
+                </div>
+                <div class="form-group mb-2">
+                    <label>支付用途</label>
+                    <select class="form-control form-control-sm" id="add-pay-used">
+                        <% for (const used of usedList) { %>
+                            <option><%- used %></option>
+                        <% } %>
+                    </select>
+                </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" id="add-pay-btn">确定添加</button>
+            </div>
+        </div>
+    </div>
+</div>
+<script>
+    $(function () {
+        $('#add-pay-tender').select2({
+            language: 'zh-CN',
+            theme: 'bootstrap4',
+            selectOnClose: true,
+            // width: '150',
+        });
+    });
+</script>
+<% if (notCompanyPays.length > 0) { %>
+    <div class="modal fade" id="contract-old-pay" 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" style="max-height: 800px;overflow: auto;">
+                    <table class="table table-bordered">
+                        <tr class="text-center"><th>选择</th><th>标段名称</th><th>支付编号</th><th>申请人</th></tr>
+                        <% for (const pay of notCompanyPays) { %>
+                        <tr>
+                            <td class="text-center"><input type="checkbox" name="pays[]" value="<%- pay.id %>"></td>
+                            <td><%- pay.tenderName %></td>
+                            <td><a href="/sp/<%- ctx.subProject.id %>/financial/pay/<%- pay.id %>/detail" target="_blank"><%- pay.code %></a></td>
+                            <td class="text-center"><%- pay.username %></td>
+                        <% } %>
+                    </table>
+                </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" id="batch-old-pays">确定关联到本单位期</button>
+                </div>
+            </div>
+        </div>
+    </div>
+<% } %>
+<% } %>
+<!--批量审批-->
+<div class="modal fade" id="batch-sp" 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">
+                <table class="table table-bordered">
+                    <tr><th>选择</th><th>标段名称</th><th>支付编号</th><th>收款单位</th><th>支付金额</th></tr>
+                    <tr><td><input type="checkbox"></td><td>TJ01</td><td>TJ01-20231207001</td><td>中国交通物资有限公司</td><td class="text-right">12345678</td></tr>
+                    <tr><td><input type="checkbox"></td><td>TJ01</td><td>TJ01-20231207001</td><td>中国交通物资有限公司</td><td class="text-right">12345678</td></tr>
+                    <tr><td><input type="checkbox"></td><td>TJ01</td><td>TJ01-20231207001</td><td>中国交通物资有限公司</td><td class="text-right">12345678</td></tr>
+                    <tr><td><input type="checkbox"></td><td>TJ01</td><td>TJ01-20231207001</td><td>中国交通物资有限公司</td><td class="text-right">12345678</td></tr>
+                </table>
+            </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-success">审批</button>
+            </div>
+        </div>
+    </div>
+</div>
+<!--审批流程/结果-->
+<div class="modal fade" id="sp-list" data-backdrop="static">
+    <div class="modal-dialog modal-lg" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">审批流程</h5>
+            </div>
+            <div class="modal-body">
+                <div class="row">
+                    <div class="col-4 modal-height-500" style="overflow: auto">
+                        <div class="card mt-3">
+                            <ul class="list-group list-group-flush" id="auditor-list">
+                            </ul>
+                        </div>
+                    </div>
+                    <div class="col-8 modal-height-500" style="overflow: auto" id="audit-list">
+                    </div>
+                </div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal">关闭</button>
+            </div>
+        </div>
+    </div>
+</div>

+ 3 - 0
app/view/financial/pay_detail.ejs

@@ -4,6 +4,9 @@
         <div class="title-main  d-flex">
             <% include ./sub_mini_menu.ejs %>
             <div class="col-10 pl-0">
+                <% if (financialPay.payCompany) { %>
+                <div class="d-inline-block"><a href="/sp/<%- ctx.subProject.id %>/financial/pay/company/<%- financialPay.payCompany.id %>" class="account-page-size"><i class="fa fa-chevron-left "></i> <span>返回</span></a><span class="text-muted mx-2">|</span></div>
+                <% } %>
 <!--                <div class="d-inline-block mr-3">-->
 <!--                    <div class="btn-group btn-group-toggle group-tab" data-toggle="buttons">-->
 <!--                        <label class="btn btn-sm btn-light active">-->

+ 37 - 123
app/view/financial/pay_modal.ejs

@@ -1,90 +1,37 @@
 <% include ../shares/delete_hint_modal.ejs %>
 <% if (ctx.session.sessionUser.is_admin || fptReportTids.length > 0) { %>
 <!-- 申请支付 -->
-<div class="modal fade show" id="add-pay" data-backdrop="static">
+<div class="modal fade show" id="add-payqi" data-backdrop="static">
     <div class="modal-dialog" role="document">
         <div class="modal-content">
             <div class="modal-header">
-                <h5 class="modal-title">申请支付</h5>
+                <h5 class="modal-title">新建支付期</h5>
             </div>
             <div class="modal-body">
-                <style>
-                    .select2-container {
-                        /*display: inline-block!important;*/
-                        /*height: 27px;*/
-                        width: 100% !important;
-                    }
-                    .select2-container--bootstrap4 .select2-selection--single {
-                        height: calc(1.1em + .75rem + 2px) !important;
-                    }
-                    /*.select2-container--bootstrap4 .select2-selection {*/
-                    /*    background-color: #f8f9fa;*/
-                    /*    !*border-color: #f8f9fa;*!*/
-                    /*    border: 1px solid #f8f9fa;*/
-                    /*}*/
-                    /*.select2-container--bootstrap4 .select2-selection--single .select2-selection__rendered:focus{*/
-                    /*    box-shadow: 0 0 0 0.2rem rgba(216,217,219,.5);*/
-                    /*}*/
-                    /*.select2-container--bootstrap4 .select2-selection--single .select2-selection__rendered:hover {*/
-                    /*    background-color: #e2e6ea;*/
-                    /*    border-color: #dae0e5;*/
-                    /*}*/
-                    .select2-container--bootstrap4 .select2-selection--single .select2-selection__rendered {
-                        line-height: calc(1.1em + .75rem);
-                        /*color: #007bff!important;*/
-                        border-radius: 0.2rem;
-                        /*background-color: #f8f9fa;*/
-                        /*border-color: #f8f9fa;*/
-                    }
-                    .select2-container--bootstrap4 .select2-selection--single .select2-selection__arrow b {
-                        border-color: #007bff transparent transparent transparent;
-                        border-width: 4px 3.7px 0;
-                    }
-                    .select2-search--dropdown .select2-search__field {
-                        padding: 0.175rem 0.5rem;
-                    }
-                </style>
                 <div class="form-group mb-2">
-                    <label>支付标段<strong class="text-danger">*</strong></label>
-                    <select class="form-control form-control-sm" id="add-pay-tender">
-                        <% for (const t of tenders) { %>
-                        <% if (ctx.session.sessionUser.is_admin || ctx.helper._.includes(fptReportTids, t.id)) { %>
-                            <option value="<%- t.id %>"><%- t.name %></option>
-                        <% } %>
+                    <label>申请单位<strong class="text-danger">*</strong></label>
+                    <% if (ctx.session.sessionUser.is_admin) { %>
+                    <select name="company" class="form-control form-control-sm" id="add-company-select">
+                        <% for (const c of userCompanyList) { %>
+                            <option value="<%- c.name %>" <% if (c.name === company) { %>selected<% } %>><%- c.name %></option>
                         <% } %>
                     </select>
+                    <% } else { %>
+                    <input class="form-control form-control-sm" id="add-company-select" name="company" placeholder="创建人所属单位" value="<%- user.company %>" type="text" readonly="">
+                    <% } %>
                 </div>
                 <div class="form-group mb-2">
-                    <label>支付编号</label>
-                    <input class="form-control form-control-sm" placeholder="标段编号-年月日-3位流水号" id="add-pay-code" type="text" readonly="">
-                    <span class="text-danger"></span>
-                </div>
-                <div class="form-group mb-2">
-                    <label>支付用途</label>
-                    <select class="form-control form-control-sm" id="add-pay-used">
-                        <% for (const used of usedList) { %>
-                            <option><%- used %></option>
-                        <% } %>
-                    </select>
+                    <label>支付期数</label>
+                    <input class="form-control form-control-sm" id="company_order" placeholder="自动根据单位生成的期数" value="<%- (ctx.session.sessionUser.is_admin && company ? userCompanyList.find(item => item.name === company).count + 1 : (user.company ? userCompanyList.find(item => item.name === user.company).count + 1 : 0)) %>" type="text" readonly="">
                 </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" id="add-pay-btn">确定添加</button>
+                <button type="button" class="btn btn-sm  btn-primary" id="add-qi-btn">确定</button>
             </div>
         </div>
     </div>
 </div>
-<script>
-    $(function () {
-        $('#add-pay-tender').select2({
-            language: 'zh-CN',
-            theme: 'bootstrap4',
-            selectOnClose: true,
-            // width: '150',
-        });
-    });
-</script>
 <% } %>
 <!--批量审批-->
 <div class="modal fade" id="batch-sp" data-backdrop="static">
@@ -111,52 +58,37 @@
 </div>
 <!--付款账号-->
 <div class="modal fade" id="payaccount" 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">
-                <table class="table table-bordered">
-                    <tr><td>开户名称<b class="text-danger">*</b></td><td><input type="text" name="name" class="form-control form-control-sm"></td></tr>
-                    <tr><td>开户银行<b class="text-danger">*</b></td><td><input type="text" name="bank" class="form-control form-control-sm"></td></tr>
-                    <tr><td>开户账号<b class="text-danger">*</b></td><td><input type="text" name="bank_account" class="form-control form-control-sm"></td></tr>
-                    <tr><td>联系人</td><td><input type="text" name="contact" class="form-control form-control-sm"></td></tr>
-                    <tr><td>联系电话</td><td><input type="text" name="phone" class="form-control form-control-sm"></td></tr>
-                    </tbody>
-                </table>
-            </div>
-            <div class="modal-footer">
-                <input type="hidden" name="id" value="">
-                <input type="hidden" name="tid" value="">
-                <button type="button" class="btn btn-sm btn-primary mr-auto" id="get-form-tender">从计量标段同步</button>
-                <button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">关闭</button>
-                <button type="button" class="btn btn-sm btn-primary" id="set-pay-btn">确定添加</button>
-            </div>
-        </div>
-    </div>
-</div>
-<!--审批流程/结果-->
-<div class="modal fade" id="sp-list" data-backdrop="static">
     <div class="modal-dialog modal-lg" role="document">
         <div class="modal-content">
             <div class="modal-header">
-                <h5 class="modal-title">审批流程</h5>
+                <h5 class="modal-title">付款账号(标段名称)</h5>
             </div>
             <div class="modal-body">
-                <div class="row">
-                    <div class="col-4 modal-height-500" style="overflow: auto">
-                        <div class="card mt-3">
-                            <ul class="list-group list-group-flush" id="auditor-list">
-                            </ul>
-                        </div>
+                <div class="row" style="min-height: 400px;max-height: 700px;">
+                    <div class="col-5" style="min-height: 400px;max-height: 700px;overflow:auto;">
+                        <div class="p-2">标段列表</div>
+                        <div id="pay-tender-list"></div>
                     </div>
-                    <div class="col-8 modal-height-500" style="overflow: auto" id="audit-list">
+                    <div class="col-7">
+                        <div class="d-flex">
+                            <a href="javascript:void(0);" class="btn btn-sm text-primary pl-0 mb-1" id="get-form-tender">从计量标段同步</a>
+                            <a href="javascript:void(0);" class="btn btn-sm text-primary mb-1" id="batch-other-bank">同步信息到所选标段</a>
+                        </div>
+                        <table class="table table-bordered">
+                            <tr><td>开户名称<b class="text-danger">*</b></td><td><input type="text" name="name" class="form-control form-control-sm"></td></tr>
+                            <tr><td>开户银行<b class="text-danger">*</b></td><td><input type="text" name="bank" class="form-control form-control-sm"></td></tr>
+                            <tr><td>开户账号<b class="text-danger">*</b></td><td><input type="text" name="bank_account" class="form-control form-control-sm"></td></tr>
+                            <tr><td>联系人</td><td><input type="text" name="contact" class="form-control form-control-sm"></td></tr>
+                            <tr><td>联系电话</td><td><input type="text" name="phone" class="form-control form-control-sm"></td></tr>
+                            </tbody>
+                        </table>
                     </div>
                 </div>
             </div>
             <div class="modal-footer">
-                <button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal">关闭</button>
+                <input type="hidden" name="id" value="">
+                <input type="hidden" name="tid" value="">
+                <button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">关闭</button>
             </div>
         </div>
     </div>
@@ -170,18 +102,9 @@
             </div>
             <div class="modal-body pt-0">
                 <div class="row" style="min-height: 400px;max-height: 700px;">
-                    <div class="col-3" style="min-height: 400px;max-height: 700px;overflow:auto;">
+                    <div class="col-4" style="min-height: 400px;max-height: 700px;overflow:auto;">
                         <div class="p-2">标段列表</div>
-                        <table class="table table-bordered">
-                            <thead>
-                            <tr class="text-center"><th width="50px">选择</th><th>标段名称</th></tr>
-                            </thead>
-                            <tbody id="shenpi-tender-list">
-                            <% for (const t of tenders) { %>
-                                <tr data-tid="<%- t.id %>"><td class="text-center"><input type="checkbox"></td><td class="change-tender" style="cursor: pointer;"><%- t.name %></td></tr>
-                            <% } %>
-                            </tbody>
-                        </table>
+                        <div id="shenpi-tender-list"></div>
                     </div>
                     <div class="col-4">
                         <div class="d-flex flex-row bg-graye">
@@ -218,19 +141,10 @@
                             </tbody>
                         </table>
                     </div>
-                    <div class="col-5">
+                    <div class="col-4">
                         <div class="d-flex flex-row bg-graye">
                             <div class="p-2"><a href="javascript:void(0);" id="set-other-tenders">设置流程至其他已勾选标段</a></div>
                         </div>
-<!--                        <div>-->
-<!--                            <ul class="list-unstyled">-->
-<!--                                <li class="d-flex justify-content-start align-items-center">-->
-<!--                                    <span class="col-auto">填报人</span>-->
-<!--                                    <span class="col-10 spr-span" id="report-list">-->
-<!--                                    </span>-->
-<!--                                </li>-->
-<!--                            </ul>-->
-<!--                        </div>-->
                         <div id="shenpi-list">
                         </div>
                     </div>

+ 12 - 8
app/view/financial/transfer.ejs

@@ -17,15 +17,17 @@
                 <table class="table table-hover table-bordered text-center">
                     <thead>
                     <tr>
-                        <th width="100px">次数</th>
+                        <th width="50px">次数</th>
                         <th width="10%">划拨年月</th>
-                        <th width="10%">划拨金额</th>
-                        <th width="10%">填报人</th>
-                        <th>填报单位</th>
+                        <th width="120px">本次划拨金额</th>
+                        <th width="120px">截止上次划拨金额</th>
+                        <th width="120px">截止本次划拨金额</th>
+                        <th width="100px">填报人</th>
+                        <th width="150px">填报单位</th>
                         <th width="150px">填报时间</th>
-                        <th width="15%">填报备注</th>
-                        <th width="150px">附件</th>
-                        <th width="150px">操作</th>
+                        <th width="20%">填报备注</th>
+                        <th width="80px">附件</th>
+                        <th width="80px">操作</th>
                     </tr>
                     </thead>
                     <tbody id="transfer-list">
@@ -33,8 +35,10 @@
                     <% for (const [i, t] of transferList.entries()) { %>
                     <tr class="text-center" data-id="<%- t.id %>">
                         <td class=""><%- (transferList.length - i) %></td>
-                        <td class=""><a href="/sp/<%- ctx.subProject.id %>/financial/transfer/<%- t.id %>/tender" class="account-page-size"><%- t.t_time %></a></td>
+                        <td class=""><a href="/sp/<%- ctx.subProject.id %>/financial/transfer/<%- t.id %>/tender"><%- t.t_time %></a></td>
                         <td class="text-right"><%- t.total_price %></td>
+                        <td class="text-right"><%- t.pre_hb_tp %></td>
+                        <td class="text-right"><%- ctx.helper.add(t.total_price, t.pre_hb_tp) %></td>
                         <td class=""><%- t.username %></td>
                         <td class="text-left" ><%- t.company %></td>
                         <td class="" ><%- moment(t.create_time).format('YYYY-MM-DD HH:mm:ss') %></td>

+ 11 - 46
app/view/financial/transfer_tender.ejs

@@ -11,57 +11,15 @@
                 <a href="#unlock" data-toggle="modal" data-target="#unlock" class="btn btn-danger btn-sm pull-right">解锁数据</a>
                 <% } else { %>
                 <a href="#lock" data-toggle="modal" data-target="#lock" class="btn btn-success btn-sm pull-right mr-2">锁定数据</a>
-                <a href="#add-bd" data-toggle="modal" data-target="#add-bd" class="btn btn-primary btn-sm pull-right mr-2">添加标段</a>
+                <a href="#add-bd" data-toggle="modal" data-target="#add-ftt" class="btn btn-primary btn-sm pull-right mr-2">添加标段</a>
                 <% } %>
                 <% } %>
             </div>
         </div>
     </div>
     <div class="content-wrap">
-        <div class="c-body">
-            <div class="sjs-height-0">
-                <table class="table table-bordered">
-                    <thead class="text-center">
-                    <tr>
-                        <th width="50px">序号</th>
-                        <th width="">标段名称</th>
-                        <th width="130px">计量期数</th>
-                        <th width="100px">合同金额</th>
-                        <th width="100px">本期合同计量</th>
-                        <th width="100px">本期变更计量</th>
-                        <th width="100px">本期完成计量</th>
-                        <th width="100px">本期应付</th>
-                        <th width="100px">本期实付</th>
-                        <th width="100px">本次划拨金额</th>
-                        <th width="80px">附件</th>
-                        <% if (transferInfo.uid === ctx.session.sessionUser.accountId) { %>
-                        <th width="80px">操作</th>
-                        <% } %>
-                    </tr>
-                    </thead>
-                    <tbody id="tender-list">
-                    <% if (transferTenderList.length > 0) { %>
-                    <% for (const [i, t] of transferTenderList.entries()) { %>
-                    <tr class="text-right" data-id="<%- t.id %>">
-                        <td class="text-center"><%- i+1 %></td>
-                        <td class="text-left"><%- t.name %></td>
-                        <td class="text-center">第<%- t.sorder %>期</td>
-                        <td class="" ><%- t.total_price %></td>
-                        <td class="" ><%- t.contract_tp %></td>
-                        <td class="" ><%- t.qc_tp %></td>
-                        <td class="" ><%- ctx.helper.sum([t.contract_tp, t.qc_tp, t.pc_tp]) %></td>
-                        <td class="" ><%- t.yf_tp %></td>
-                        <td class="" ><%- t.sf_tp %></td>
-                        <td><% if (transferInfo.uid === ctx.session.sessionUser.accountId && !transferInfo.is_lock) { %><input type="text" class="form-control form-control-sm text-right" placeholder="默认等于本期实付" data-ftid="<%- t.id %>" value="<%- t.hb_tp %>"><% } else { %><%- t.hb_tp %><% } %></td>
-                        <td class="text-center" ><a href="javascript:void(0);" class="text-primary open-tender-files" data-ftid="<%- t.id %>"><i class="fa fa-paperclip fa-rotate-90"></i></a> <span class="file-num"><%- t.files.length > 0 ? t.files.length : '' %></span></td>
-                        <% if (transferInfo.uid === ctx.session.sessionUser.accountId) { %><td class="text-center"><% if (!transferInfo.is_lock) { %><a class="text-danger del-tender-btn" href="javascript:void(0);" data-id="<%- t.id %>">移除</a><% } %></td><% } %>
-                    </tr>
-                    <% } %>
-                    <% } %>
-                    </tbody>
-                </table>
-                <!--翻页-->
-                <% include ../layout/page.ejs %>
+        <div class="sjs-height-0" style="background-color: #fff">
+            <div class="c-body" id="tender-list">
             </div>
         </div>
     </div>
@@ -71,6 +29,13 @@
     const user_id = <%- ctx.session.sessionUser.accountId %>;
     const trid = '<%- transferInfo.id %>';
     const whiteList = JSON.parse(unescape('<%- escape(JSON.stringify(whiteList)) %>'));
-    const tenderList = JSON.parse(unescape('<%- escape(JSON.stringify(transferTenderList)) %>'));
+    const tenders = JSON.parse(unescape('<%- escape(JSON.stringify(transferTenderList)) %>'));
+    const transferInfo = JSON.parse(unescape('<%- escape(JSON.stringify(transferInfo)) %>'));
     const financialPermission = JSON.parse(unescape('<%- escape(JSON.stringify(financialPermission)) %>'));
+    const category = JSON.parse(unescape('<%- escape(JSON.stringify(categoryData)) %>'));
+    const tenderList = JSON.parse(unescape('<%- escape(JSON.stringify(tenders)) %>'));
+    const selfCategoryLevel = '';
+    const pid = '<%- ctx.session.sessionProject.id %>';
+    const subProid = '<%- ctx.subProject.id %>';
+    const uphlname = 'user_' + user_id + '_pro_' + pid + '_sub_' + subProid + '_transfer_category_hide_list';
 </script>

+ 2 - 26
app/view/financial/transfer_tender_modal.ejs

@@ -38,37 +38,13 @@
 </div>
 <% } %>
 <!--添加标段-->
-<div class="modal fade" id="add-bd" data-backdrop="static">
+<div class="modal fade" id="add-ftt" 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" style="max-height: 500px;overflow: auto;">
-                <table class="table table-bordered">
-                    <thead class="text-center">
-                    <tr>
-                        <th width="40px">选择</th>
-                        <th width="">标段名称</th>
-                        <th width="150px">选择期数</th>
-                    </tr>
-                    </thead>
-                    <tbody id="tenders">
-                    <% for (const t of tenders) { %>
-                    <tr class="text-center">
-                        <td><input type="checkbox" value="<%- t.id %>"></td>
-                        <td class="text-left"><%- t.name %></td>
-                        <td>
-                            <select class="form-control form-control-sm selectpicker" title="选择期" data-width="150px" multiple>
-                                <% for (const s of t.stages) { %>
-                                    <option value="<%- s.order %>">第<%- s.order %>期</option>
-                                <% } %>
-                            </select>
-                        </td>
-                    </tr>
-                    <% } %>
-                    </tbody>
-                </table>
+            <div class="modal-body pt-0 mt-1" style="max-height: 500px;overflow: auto;" id="add-tender-list">
             </div>
             <div class="modal-footer">
                 <button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">取消</button>

+ 423 - 0
app/view/wap/inspection.ejs

@@ -0,0 +1,423 @@
+<!DOCTYPE html>
+<html lang="zh">
+<head>
+    <meta charset="UTF-8">
+    <title>质量巡检单生成助手</title>
+    <meta name="viewport" content="width=device-width, initial-scale=1">
+    <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">
+    <style>
+        body,
+        html {
+            height: 100%;
+            overflow: hidden;
+            margin: 0; /* 去掉默认 margin */
+            padding: 0;
+            -webkit-overflow-scrolling: touch;
+        }
+        body {
+            background: #f1f3f5;
+            font-size: 16px;
+            padding-bottom: 100px;
+        }
+        .chat-box {
+            height: calc(100vh - 100px); /* 减去头部和底部高度,150px 是示例,需根据实际调整 */
+            overflow-y: auto;
+            /*padding: 10px;*/
+            box-sizing: border-box;
+        }
+        .message {
+            display: flex;
+            margin-bottom: 12px;
+            padding: 0 15px;
+        }
+        .message.user {
+            justify-content: flex-end;
+        }
+        .message.ai {
+            justify-content: flex-start;
+        }
+        .bubble {
+            max-width: 95%;
+            padding: 10px 14px;
+            border-radius: 18px;
+            line-height: 1.4;
+            word-wrap: break-word;
+        }
+        .message.user .bubble {
+            background: #007bff;
+            color: white;
+            border-bottom-right-radius: 0;
+        }
+        .message.ai .bubble {
+            background: #dee2e6;
+            color: #333;
+            border-bottom-left-radius: 0;
+        }
+        .chat-footer {
+            position: fixed;
+            bottom: 0;
+            left: 0;
+            right: 0;
+            width: 100%;
+            background: #fff;
+            padding: 22px env(safe-area-inset-right) calc(10px + env(safe-area-inset-bottom)) env(safe-area-inset-left);
+            box-shadow: 0 -2px 6px rgba(0, 0, 0, 0.1);
+            z-index: 999;
+        }
+
+        .chat-footer .input-group {
+            bottom: 22px;
+        }
+
+        .chat-footer input.form-control {
+            border: none;
+            padding: 10px 12px;
+            height: 44px;
+        }
+        .chat-footer .btn {
+            border-radius: 0;
+        }
+
+        .form-preview {
+            background: #fff;
+            border: 1px solid #dee2e6;
+            border-radius: 12px;
+            padding: 15px 20px;
+            margin: 20px 15px 0 15px;
+            box-shadow: 0 2px 6px rgba(0,0,0,0.05);
+        }
+        .form-preview h6 {
+            font-weight: bold;
+            margin-bottom: 16px;
+            border-bottom: 1px solid #dee2e6;
+            padding-bottom: 8px;
+            font-size: 17px;
+        }
+        .form-item {
+            margin-bottom: 12px;
+            font-size: 15px;
+            line-height: 1.5;
+        }
+        .form-item label {
+            font-weight: 500;
+            color: #555;
+        }
+        .form-item span {
+            display: inline-block;
+            margin-left: 5px;
+            color: #333;
+        }
+        .form-actions {
+            border-top: 1px solid #dee2e6;
+            padding-top: 12px;
+            margin-top: 16px;
+            display: flex;
+            justify-content: space-between;
+        }
+        /* 1. WebKit 浏览器(Chrome, Safari, Android) */
+        #chat-box::-webkit-scrollbar {
+            width: 4px;
+        }
+
+        #chat-box::-webkit-scrollbar-track {
+            background: transparent;
+        }
+
+        #chat-box::-webkit-scrollbar-thumb {
+            background-color: rgba(0, 0, 0, 0.3);
+            border-radius: 4px;
+        }
+
+        /* 2. Firefox */
+        #chat-box {
+            scrollbar-width: thin;
+            scrollbar-color: rgba(0, 0, 0, 0.3) transparent;
+        }
+
+        /* 3. 选项:在小屏上彻底隐藏滚动条(可滑动但看不见) */
+        @media (max-width: 768px) {
+            #chat-box::-webkit-scrollbar {
+                display: none;
+            }
+            #chat-box {
+                -ms-overflow-style: none; /* IE 10+ */
+                scrollbar-width: none;    /* Firefox */
+            }
+        }
+        .input-group {
+            position: relative;
+        }
+
+        .input-icon {
+            position: absolute;
+            right: 75px; /* 根据按钮宽度调整 */
+            top: 50%;
+            transform: translateY(-50%);
+            cursor: pointer;
+            color: #888;
+            z-index: 10;
+        }
+
+        #voice-icon.active {
+            color: #007bff;
+        }
+        .xj-title {
+            position: sticky;
+            top: 0;
+            background: #fff;
+        }
+
+        #recording-toast {
+            position: fixed;
+            bottom: 80px;
+            left: 50%;
+            transform: translateX(-50%);
+            background-color: rgba(0, 0, 0, 0.85);
+            color: #fff;
+            padding: 12px 20px;
+            border-radius: 24px;
+            font-size: 14px;
+            display: flex;
+            align-items: center;
+            z-index: 9999;
+            animation: fadeInUp 0.3s ease-out;
+        }
+
+        #recording-toast .fa {
+            margin-right: 8px;
+        }
+
+        @keyframes fadeInUp {
+            from {
+                opacity: 0;
+                transform: translate(-50%, 100%);
+            }
+            to {
+                opacity: 1;
+                transform: translate(-50%, 0);
+            }
+        }
+    </style>
+</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">
+        <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-check_item">—</span>
+            </div>
+            <div class="form-item">
+                <label>现场检查情况:</label>
+                <span id="field-check_situation">—</span>
+            </div>
+            <div class="form-item">
+                <label>处理要求及措施:</label>
+                <span id="field-action">—</span>
+            </div>
+            <div class="form-item">
+                <label>检查日期:</label>
+                <span id="field-date">—</span>
+            </div>
+            <div class="form-item">
+                <label>质检员:</label>
+                <span id="field-inspector">—</span>
+            </div>
+
+            <div class="form-actions">
+                <button id="modify-btn" class="btn btn-outline-secondary btn-sm">
+                    <i class="fa fa-edit"></i> 修改
+                </button>
+                <button id="confirm-btn" class="btn btn-success btn-sm">
+                    <i class="fa fa-check-circle"></i> 确认生成
+                </button>
+            </div>
+        </div>
+    </div>
+    <div class="chat-footer">
+        <div class="input-group">
+            <input type="text" id="user-input" class="form-control" placeholder="请输入...">
+            <div class="input-icon">
+                <i id="voice-icon" class="fa fa-microphone"></i>
+            </div>
+            <div class="input-group-append">
+                <button class="btn btn-primary btn-send" id="send-btn">发送</button>
+            </div>
+        </div>
+    </div>
+</div>
+<div id="recording-toast" style="display: none;">
+    <div class="toast-inner">
+        <i class="fa fa-microphone"></i>
+        <span>正在录音,请讲话...</span>
+    </div>
+</div>
+<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/toastr.min.js"></script>
+<script src="/public/js/cookies.js"></script>
+<script src="/public/js/wap/global.js"></script>
+<script>
+    const csrf = '<%= ctx.csrf %>';
+</script>
+<script>
+    const $chatBox = $('#chat-box');
+    const $formPreview = $('#form-preview');
+    let conversationId = '';
+    $(function () {
+        const appendMessage = (text, sender = 'user', cssClass = '') => {
+            const formatted = text.replace(/\n/g, '<br>');
+            const $message = $('<div class="message"></div>').addClass(sender);
+            if (cssClass) {
+                $message.addClass(cssClass);
+            }
+            const $bubble = $('<div class="bubble"></div>').html(formatted);
+            $message.append($bubble);
+            $('#messages').append($message);
+            $chatBox.scrollTop($chatBox[0].scrollHeight);
+        };
+        appendMessage("👋 嗨,我是质量巡检单生成助手,告诉我项目名称可以帮你快速生成巡检单", "ai", 'mt-3');
+        const updatePreviewForm = (data) => {
+            $('#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 || '—');
+            $formPreview.removeClass('d-none');
+        };
+
+        $('#send-btn').on('click', () => {
+            const input = $('#user-input').val().trim();
+            if (!input) return;
+            appendMessage(input, 'user');
+            $('#user-input').val('');
+            $formPreview.addClass('d-none');
+
+            postData('/wap/inspection/ask', { user_input: input, conversationId }, function (result) {
+                if (result) {
+                    conversationId = result.conversation_id;
+                    if (result.answer_text) {
+                        appendMessage(result.answer_text, 'ai');
+                    }
+                    if (result.answer_json) {
+                        updatePreviewForm(result.answer_json);
+                        $formPreview.removeClass('d-none');
+                    } else {
+                        $formPreview.addClass('d-none');
+                    }
+                } else {
+                    appendMessage('请求出错,请稍后重试。', 'ai');
+                }
+            }, function () {
+                appendMessage('请求出错,请再输入一次。', 'ai');
+            });
+        });
+
+        $('#user-input').on('keypress', function(e) {
+            if (e.which === 13) $('#send-btn').click();
+        });
+
+        $('#confirm-btn').on('click', () => {
+            alert('✅ 表单已提交!你可以跳转页面或保存到数据库');
+            // 可跳转页面如 window.location.href = '/form/preview';
+        });
+
+        $('#modify-btn').on('click', () => {
+            $formPreview.addClass('d-none');
+            appendMessage('请继续修改你想调整的信息。', 'ai');
+        });
+
+        const originalHeight = window.innerHeight;
+        $('#user-input').on('focus', function () {
+            const $input = $(this);
+
+            const checkKeyboardOpen = function () {
+                const newHeight = window.innerHeight;
+                if (newHeight < originalHeight) {
+                    // 键盘已弹出
+                    setTimeout(function () {
+                        $input[0].scrollIntoView({ behavior: 'smooth', block: 'center' });
+                    }, 100);
+                    $(window).off('resize', checkKeyboardOpen);
+                }
+            };
+
+            $(window).on('resize', checkKeyboardOpen);
+        });
+
+        const recognition = new (window.SpeechRecognition || window.webkitSpeechRecognition)();
+        recognition.continuous = false;
+        recognition.lang = 'zh-CN';
+        recognition.interimResults = false;
+
+        let isRecording = false;
+        let pressTimer = null;
+
+        $('#voice-icon').on('touchstart', function (e) {
+            e.preventDefault();
+            $(this).addClass('active');
+            // if (!isRecording) {
+            //     recognition.start();
+            //     $(this).addClass('active');
+            //     $('#recording-toast').fadeIn(); // 👉 显示录音提示
+            //     isRecording = true;
+            // }
+            pressTimer = setTimeout(() => {
+                recognition.start();
+                isRecording = true;
+                $('#recording-toast').fadeIn(); // 👉 显示录音提示
+            }, 300); // 按住 300ms 开始录音,可根据体验调整
+        });
+
+        $('#voice-icon').on('touchend touchcancel', function (e) {
+            e.preventDefault();
+            clearTimeout(pressTimer);
+            if (isRecording) {
+                recognition.stop();
+                isRecording = false;
+                $('#recording-toast').fadeOut(); // 👉 隐藏录音提示
+            }
+            $(this).removeClass('active');
+        });
+
+        recognition.onresult = function (event) {
+            const result = event.results[0][0].transcript.trim();
+            $('#user-input').val(result);
+        };
+
+        recognition.onerror = function (err) {
+            console.error('语音识别出错:', err);
+            $('#voice-icon').removeClass('active');
+            $('#recording-toast').fadeOut(); // 👉 出错时也隐藏
+            isRecording = false;
+        };
+
+        recognition.onend = function () {
+            $('#voice-icon').removeClass('active');
+            $('#recording-toast').fadeOut(); // 👉 识别结束时隐藏
+            isRecording = false;
+        };
+
+        if (isWeChat()) {
+            $('#voice-icon').hide(); // 在微信内隐藏语音按钮
+        }
+    });
+
+    function isWeChat() {
+        const ua = navigator.userAgent.toLowerCase();
+        return /micromessenger/.test(ua);
+    }
+</script>
+</body>
+</html>

+ 27 - 0
config/web.js

@@ -1899,6 +1899,12 @@ const JsFiles = {
                     '/public/js/sub_menu.js',
                     '/public/js/div_resizer.js',
                     '/public/js/zh_calc.js',
+                    '/public/js/PinYinOrder.bundle.js',
+                    '/public/js/colResizable/colResizable-1.6.min.js',
+                    '/public/js/shares/tender_list_order.js',
+                    '/public/js/shares/show_level.js',
+                    '/public/js/tender_showhide.js',
+                    '/public/js/tender_list_base.js',
                     '/public/js/financial_transfer_tender.js',
                 ],
                 mergeFile: 'financial_transfer_tender',
@@ -1914,10 +1920,31 @@ const JsFiles = {
                     '/public/js/sub_menu.js',
                     '/public/js/div_resizer.js',
                     '/public/js/zh_calc.js',
+                    '/public/js/PinYinOrder.bundle.js',
+                    '/public/js/colResizable/colResizable-1.6.min.js',
+                    '/public/js/shares/tender_list_order.js',
+                    '/public/js/shares/show_level.js',
+                    '/public/js/tender_showhide.js',
+                    '/public/js/tender_list_base.js',
                     '/public/js/financial_pay.js',
                 ],
                 mergeFile: 'financial_pay',
             },
+            payCompany: {
+                files: [
+                    '/public/js/decimal.min.js',
+                    '/public/js/math.min.js',
+                    '/public/js/component/menu.js',
+                    '/public/js/moment/moment.min.js',
+                ],
+                mergeFiles: [
+                    '/public/js/sub_menu.js',
+                    '/public/js/div_resizer.js',
+                    '/public/js/zh_calc.js',
+                    '/public/js/financial_pay_company.js',
+                ],
+                mergeFile: 'financial_pay_company',
+            },
             payDetail: {
                 files: [
                     '/public/js/decimal.min.js',

+ 80 - 0
db_script/financial_transfer.js

@@ -0,0 +1,80 @@
+/*
+ 修改脚本,请现在指定项目测试
+ 例如: node db_script/sub_project local T201711273363
+ 没有意外再全部执行
+
+ 所有修改均应考虑脚本二次执行时的兼容,应检查是否已执行过,避免在生产环境运行时出现问题后需要二次执行,参见自定义分类脚本
+  */
+const BaseUtil = require('./baseUtils');
+const querySql = BaseUtil.querySql;
+const _ = require('lodash');
+const ZhCalc = BaseUtil.ZhCalc;
+
+const calcPreHbTP = async function(subProject) {
+    const ftList = await querySql('SELECT * FROM zh_financial_transfer where spid = ? ORDER BY id DESC', [subProject.id]);
+    if (ftList.length > 0) {
+        const updateFtList = [];
+        const updateFttList = [];
+        const fttList = await querySql('SELECT * FROM zh_financial_transfer_tender where spid = ? ORDER BY trid DESC, id ASC', [subProject.id]);
+        if (fttList.length > 0) {
+            for (const ftt of fttList) {
+                const ftts = _.filter(fttList, function(item) {
+                    return item.tid === ftt.tid && item.trid < ftt.trid;
+                });
+                let pre_hb_tp = 0;
+                if (ftts.length > 0) {
+                    pre_hb_tp = ZhCalc.sum(_.map(ftts, 'hb_tp'));
+                }
+                if (pre_hb_tp !== ftt.pre_hb_tp) {
+                    ftt.pre_hb_tp = pre_hb_tp;
+                    updateFttList.push({ id: ftt.id, pre_hb_tp });
+                }
+            }
+            for (const ft of ftList) {
+                const fts = _.filter(ftList, function(item) {
+                    return item.tid === ft.tid && item.id < ft.id;
+                });
+                let pre_hb_tp = 0;
+                if (fts.length > 0) {
+                    pre_hb_tp = ZhCalc.sum(_.map(fts, 'total_price'));
+                }
+                if (pre_hb_tp !== ft.pre_hb_tp) {
+                    ft.pre_hb_tp = pre_hb_tp;
+                    updateFtList.push({ id: ft.id, pre_hb_tp });
+                }
+            }
+        }
+        if (updateFtList.length > 0) {
+            const sql = 'UPDATE zh_financial_transfer SET pre_hb_tp = ? WHERE id = ?';
+            for (const ft of updateFtList) {
+                await querySql(sql, [ft.pre_hb_tp, ft.id]);
+            }
+        }
+        if (updateFttList.length > 0) {
+            const sql = 'UPDATE zh_financial_transfer_tender SET pre_hb_tp = ? WHERE id = ?';
+            for (const ftt of updateFttList) {
+                await querySql(sql, [ftt.pre_hb_tp, ftt.id]);
+            }
+        }
+    }
+};
+
+const doComplete = async function(code) {
+    try {
+        const filter = code ? ` where code = '${code}'` : '';
+        const project = await querySql('Select * From zh_project' + filter);
+        for (const p of project) {
+            console.log(`Update Project ${p.code}(${p.id}):`);
+            const subProj = await querySql('SELECT * FROM zh_sub_project where project_id = ? and is_folder = 0 and is_delete = 0;', [p.id]);
+            for (const sp of subProj) {
+                await calcPreHbTP(sp);
+            }
+            console.log('END Update;');
+        }
+    } catch (err) {
+        console.log(err);
+    }
+    BaseUtil.closePool();
+};
+const projectCode = process.argv[3];
+doComplete(projectCode);

+ 27 - 0
sql/update.sql

@@ -246,6 +246,33 @@ ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量
 ALTER TABLE `zh_ledger_extra_99`
 ADD COLUMN `multi_limit` varchar(36) NOT NULL DEFAULT '' COMMENT '联动计量配置' AFTER `gxby_id`;
 
+ALTER TABLE `zh_financial_transfer_tender`
+MODIFY COLUMN `sorder` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL COMMENT '期数,以逗号分隔' AFTER `tid`,
+ADD COLUMN `source` varchar(255) NULL DEFAULT '' COMMENT '资金来源' AFTER `is_lock`,
+ADD COLUMN `pre_hb_tp` decimal(30, 8) NULL DEFAULT 0 COMMENT '截止上次划拨' AFTER `hb_tp`;
+
+ALTER TABLE `zh_financial_transfer`
+ADD COLUMN `pre_hb_tp` decimal(30, 8) NULL DEFAULT 0 COMMENT '截止上次划拨金额' AFTER `total_price`;
+
+ALTER TABLE `zh_financial_pay`
+ADD COLUMN `fpcid` int NULL COMMENT '资金支付单位期id' AFTER `tid`;
+
+ALTER TABLE `zh_financial_pay_audit`
+ADD COLUMN `fpcid` int NULL COMMENT '资金支付单位期id' AFTER `fpid`;
+
+CREATE TABLE `zh_financial_pay_company`  (
+  `id` int NOT NULL AUTO_INCREMENT,
+  `spid` varchar(100) NULL COMMENT '项目id',
+  `company_id` int NULL DEFAULT NULL COMMENT '单位id',
+  `order` smallint(5) NULL COMMENT '期数',
+  `uid` int NULL COMMENT '申请人id',
+  `total_price` decimal(30, 8) NULL DEFAULT 0 COMMENT '本期金额',
+  `pre_tp` decimal(30, 8) NULL DEFAULT 0 COMMENT '截止上期金额',
+  `can_del` tinyint(1) NULL DEFAULT 1 COMMENT '是否可删除',
+  `create_time` datetime NULL DEFAULT NULL COMMENT '申请时间',
+  PRIMARY KEY (`id`)
+)  ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT = '资金支付单位期数表';
+
 ------------------------------------
 -- 表数据
 ------------------------------------