Files
GEAR-OA/index.html
2026-06-17 19:31:10 +08:00

649 lines
84 KiB
HTML
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>投诉管理系统 - 嘉祥科伦普重工有限公司</title>
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
<style>
*{margin:0;padding:0;box-sizing:border-box;}
body{font-family:"Microsoft YaHei","SimSun",sans-serif;background:#f0f0f0;color:#000;min-height:100vh;}
.topbar{background:#1a1a1a;color:#fff;height:48px;display:flex;align-items:center;padding:0 20px;position:fixed;top:0;left:0;right:0;z-index:100;}
.topbar .logo{font-size:16px;font-weight:bold;margin-right:40px;letter-spacing:2px;}
.topbar .menu-item{padding:0 16px;height:48px;line-height:48px;cursor:pointer;font-size:13px;transition:background .2s;}
.topbar .menu-item:hover,.topbar .menu-item.active{background:#333;}
.topbar .menu-item.active{border-bottom:3px solid #c00;}
.page-view{display:none;flex-direction:row;height:calc(100vh - 48px);margin-top:48px;}
.page-view.active{display:flex;}
.left-panel{width:300px;min-width:300px;background:#fff;border-right:2px solid #000;display:flex;flex-direction:column;}
.left-header{padding:12px 16px;border-bottom:2px solid #000;display:flex;justify-content:space-between;align-items:center;}
.left-header h3{font-size:15px;}
.btn{padding:6px 16px;border:2px solid #000;background:#fff;color:#000;cursor:pointer;font-size:13px;font-weight:bold;border-radius:2px;transition:all .15s;}
.btn:hover{background:#000;color:#fff;}
.btn-sm{padding:3px 10px;font-size:12px;}
.btn-danger{border-color:#c00;color:#c00;}.btn-danger:hover{background:#c00;color:#fff;}
.btn-primary{background:#1a1a1a;color:#fff;border-color:#1a1a1a;}.btn-primary:hover{background:#333;}
.btn-success{border-color:#090;color:#090;}.btn-success:hover{background:#090;color:#fff;}
.btn-warn{border-color:#c60;color:#c60;}.btn-warn:hover{background:#c60;color:#fff;}
.status-badge{display:inline-block;padding:2px 10px;font-size:11px;font-weight:bold;border:2px solid #000;}
.sb-ur{border-color:#c60;color:#c60;}.sb-rj{border-color:#c00;color:#c00;}.sb-ap{border-color:#090;color:#090;}
.sb-ud{border-color:#c60;color:#c60;}.sb-dn{border-color:#090;color:#090;}
.complaint-list{flex:1;overflow-y:auto;}
.complaint-item{padding:12px 16px;border-bottom:1px solid #ccc;cursor:pointer;transition:background .15s;}
.complaint-item:hover{background:#f5f5f5;}
.complaint-item.active{background:#e8e8e8;border-left:4px solid #000;}
.complaint-item .ci-id{font-weight:bold;font-size:14px;}
.complaint-item .ci-date{font-size:12px;color:#666;margin-top:4px;}
.complaint-item .ci-preview{font-size:12px;color:#333;margin-top:4px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
.complaint-item .ci-status{margin-top:4px;}
.right-panel{flex:1;overflow-y:auto;background:#fff;padding:24px;}
.right-panel .watermark-top{color:#b0b0b0;font-size:13px;margin-bottom:2px;}
.right-panel .watermark-id{color:#b0b0b0;font-size:12px;margin-bottom:16px;}
.right-panel h2{font-size:18px;border-bottom:2px solid #000;padding-bottom:8px;margin-bottom:20px;}
.empty-state{display:flex;align-items:center;justify-content:center;height:100%;font-size:15px;color:#999;}
.section{margin-bottom:20px;border:2px solid #000;}
.section-title{background:#1a1a1a;color:#fff;padding:8px 14px;font-size:14px;font-weight:bold;letter-spacing:1px;}
.section-body{padding:14px;}
.form-grid{display:grid;grid-template-columns:1fr 1fr 1fr;gap:10px 16px;}
.form-grid.cols2{grid-template-columns:1fr 1fr;}
.form-grid.cols4{grid-template-columns:1fr 1fr 1fr 1fr;}
.form-group{display:flex;flex-direction:column;}
.form-group label{font-size:12px;font-weight:bold;color:#333;margin-bottom:3px;}
.form-group label::after{content:'';display:block;width:20px;height:2px;background:#000;margin-top:2px;}
.form-group input,.form-group textarea,.form-group select{border:2px solid #000;padding:6px 8px;font-size:13px;background:#fff;color:#000;font-family:inherit;border-radius:0;outline:none;transition:border-color .15s;}
.form-group input:focus,.form-group textarea:focus{border-color:#444;}
.form-group textarea{resize:vertical;min-height:55px;}
.form-group.full{grid-column:1/-1;}
.sub-table-wrap{margin-top:10px;border:2px solid #000;}
.sub-table-title{background:#ddd;padding:6px 10px;font-size:12px;font-weight:bold;border-bottom:2px solid #000;display:flex;justify-content:space-between;align-items:center;}
.sub-table{width:100%;border-collapse:collapse;font-size:12px;}
.sub-table th{background:#eee;border:1px solid #000;padding:4px;font-size:11px;text-align:center;}
.sub-table td{border:1px solid #000;padding:3px 4px;text-align:center;vertical-align:middle;}
.sub-table td input,.sub-table td textarea{width:100%;border:none;padding:3px;font-size:11px;text-align:center;background:transparent;font-family:inherit;}
.sub-table td input:focus,.sub-table td textarea:focus{outline:1px solid #000;background:#fafafa;}
.sub-table td textarea{resize:vertical;min-height:30px;text-align:left;}
.action-bar{display:flex;gap:10px;margin-top:20px;padding-top:16px;border-top:2px solid #000;flex-wrap:wrap;}
.modal-overlay{display:none;position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,.5);z-index:200;justify-content:center;align-items:center;}
.modal-overlay.show{display:flex;}
.modal{background:#fff;border:2px solid #000;width:720px;max-height:80vh;display:flex;flex-direction:column;}
.modal-header{padding:12px 16px;border-bottom:2px solid #000;display:flex;justify-content:space-between;align-items:center;background:#1a1a1a;color:#fff;}
.modal-header h4{font-size:14px;}
.modal-close{cursor:pointer;font-size:18px;font-weight:bold;background:none;border:none;color:#fff;}
.modal-body{flex:1;overflow-y:auto;padding:16px;}
.modal-search{display:flex;gap:8px;margin-bottom:12px;}
.modal-search input{flex:1;border:2px solid #000;padding:6px 10px;font-size:13px;}
.modal-table{width:100%;border-collapse:collapse;font-size:12px;}
.modal-table th{background:#eee;border:1px solid #000;padding:6px 8px;text-align:left;}
.modal-table td{border:1px solid #000;padding:6px 8px;}
.modal-table tr:hover td{background:#f5f5f5;cursor:pointer;}
.modal-table tr.selected td{background:#d9d9d9;font-weight:bold;}
.modal-footer{padding:12px 16px;border-top:2px solid #000;display:flex;justify-content:flex-end;gap:8px;}
.tag{display:inline-block;padding:2px 8px;border:1px solid #000;font-size:11px;margin:2px 4px 2px 0;background:#f5f5f5;}
.tag .tag-remove{margin-left:6px;cursor:pointer;font-weight:bold;color:#c00;}
.file-upload-area{border:2px dashed #999;padding:18px;text-align:center;color:#999;font-size:13px;cursor:pointer;margin-top:10px;}
.file-upload-area:hover{border-color:#000;color:#000;}
.file-list{margin-top:8px;}
.file-item{display:flex;justify-content:space-between;align-items:center;padding:4px 8px;border:1px solid #ccc;margin-bottom:4px;font-size:12px;}
.file-item .fi-name{flex:1;}
.file-item .fi-remove{cursor:pointer;color:#c00;font-weight:bold;margin-left:8px;}
.info-item{margin-bottom:8px;font-size:13px;}
.info-item .ii-label{font-weight:bold;display:inline-block;width:80px;}
.info-row{font-size:13px;line-height:1.8;}
/* 数据分析样式 */
.stats-page{padding:20px 28px;overflow-y:auto;height:100%;}
.stats-page h2{font-size:20px;border-bottom:2px solid #000;padding-bottom:8px;margin-bottom:20px;}
.stats-page h3{font-size:15px;border-left:4px solid #000;padding-left:10px;margin:20px 0 14px;}
.filter-bar{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin-bottom:20px;padding:12px 16px;border:2px solid #000;background:#fafafa;}
.filter-bar label{font-size:13px;font-weight:bold;}
.filter-bar input[type=date]{border:2px solid #000;padding:5px 8px;font-size:13px;}
.card-row{display:grid;grid-template-columns:repeat(4,1fr);gap:12px;margin-bottom:16px;}
.card-row.col5{grid-template-columns:repeat(5,1fr);}
.stat-card{border:2px solid #000;padding:14px;text-align:center;background:#fff;}
.stat-card .sc-num{font-size:26px;font-weight:bold;margin-bottom:2px;}
.stat-card .sc-label{font-size:11px;color:#555;}
.sc-red{color:#c00;}.sc-green{color:#090;}.sc-orange{color:#c60;}
.chart-row{display:grid;grid-template-columns:1fr 1fr;gap:16px;margin-bottom:16px;}
.chart-row.col1{grid-template-columns:1fr;}
.chart-box{border:2px solid #000;padding:12px;background:#fff;}
.chart-box .chart-title{font-size:13px;font-weight:bold;margin-bottom:8px;padding-bottom:6px;border-bottom:1px solid #ccc;}
.chart-box canvas{max-height:280px;width:100% !important;}
.chart-box.col-full{grid-column:1/-1;}
.chart-box.col-full canvas{max-height:320px;}
</style>
</head>
<body>
<div class="topbar">
<div class="logo">嘉祥科伦普重工有限公司</div>
<div class="menu-item active" data-page="complaint" onclick="switchPage('complaint')">投诉受理</div>
<div class="menu-item" data-page="confirm" onclick="switchPage('confirm')">投诉确认</div>
<div class="menu-item" data-page="analysis" onclick="switchPage('analysis')">投诉分析</div>
<div class="menu-item" data-page="processing" onclick="switchPage('processing')">投诉处理</div>
<div class="menu-item" data-page="followup" onclick="switchPage('followup')">回访确认</div>
<div class="menu-item" data-page="statistics" onclick="switchPage('statistics')">数据分析</div>
</div>
<!-- 投诉受理 -->
<div class="page-view active" id="page-complaint">
<div class="left-panel"><div class="left-header"><h3>投诉受理单</h3><button class="btn" onclick="createNew()">+ 新建</button></div><div class="complaint-list" id="complaint-list"></div></div>
<div class="right-panel"><div class="empty-state" id="comp-empty">← 请选择或新建投诉受理单</div><div id="comp-detail" style="display:none;"></div></div>
</div>
<!-- 投诉确认 -->
<div class="page-view" id="page-confirm">
<div class="left-panel"><div class="left-header"><h3>投诉确认单</h3><button class="btn" onclick="createConfirm()">+ 新建</button></div><div class="complaint-list" id="confirm-list"></div></div>
<div class="right-panel"><div class="empty-state" id="cfm-empty">← 请选择或新建投诉确认单</div><div id="cfm-detail" style="display:none;"></div></div>
</div>
<!-- 投诉分析 -->
<div class="page-view" id="page-analysis">
<div class="left-panel"><div class="left-header"><h3>投诉分析单</h3><button class="btn" onclick="createAnalysis()">+ 新建</button></div><div class="complaint-list" id="analysis-list"></div></div>
<div class="right-panel"><div class="empty-state" id="anl-empty">← 请选择或新建投诉分析单</div><div id="anl-detail" style="display:none;"></div></div>
</div>
<!-- 投诉处理 -->
<div class="page-view" id="page-processing">
<div class="left-panel"><div class="left-header"><h3>投诉处理单</h3><button class="btn" onclick="createProcessing()">+ 新建</button></div><div class="complaint-list" id="processing-list"></div></div>
<div class="right-panel"><div class="empty-state" id="prc-empty">← 请选择或新建投诉处理单</div><div id="prc-detail" style="display:none;"></div></div>
</div>
<!-- 回访确认 -->
<div class="page-view" id="page-followup">
<div class="left-panel"><div class="left-header"><h3>回访确认单</h3><button class="btn" onclick="createFollowup()">+ 新建</button></div><div class="complaint-list" id="followup-list"></div></div>
<div class="right-panel"><div class="empty-state" id="flp-empty">← 请选择或新建回访确认单</div><div id="flp-detail" style="display:none;"></div></div>
</div>
<!-- 数据分析 -->
<div class="page-view" id="page-statistics">
<div class="stats-page" style="flex:1;">
<h2>投诉数据分析</h2>
<div class="filter-bar">
<label>分析时段:</label>
<input type="date" id="stats-start" onchange="refreshStats()">
<span></span>
<input type="date" id="stats-end" onchange="refreshStats()">
<button class="btn btn-sm" onclick="resetStatsDate()">重置</button>
<span style="font-size:11px;color:#999;margin-left:10px;">默认当月1日至今</span>
</div>
<h3>一、基本统计</h3>
<div class="card-row col5" id="stats-basic"></div>
<h3>二、日趋势分析</h3>
<div class="chart-row"><div class="chart-box"><div class="chart-title">投诉量 & 业务员日趋势</div><canvas id="chart-daily-count"></canvas></div><div class="chart-box"><div class="chart-title">投诉金额日趋势</div><canvas id="chart-daily-amount"></canvas></div></div>
<div class="chart-row"><div class="chart-box"><div class="chart-title">各环节单据日新增趋势</div><canvas id="chart-daily-docs"></canvas></div></div>
<h3>三、月均分析近6个月</h3>
<div class="chart-row"><div class="chart-box"><div class="chart-title">月度投诉量 & 业务员数</div><canvas id="chart-monthly-count"></canvas></div><div class="chart-box"><div class="chart-title">月度投诉金额</div><canvas id="chart-monthly-amount"></canvas></div></div>
<div class="chart-row"><div class="chart-box"><div class="chart-title">各环节单据月新增趋势</div><canvas id="chart-monthly-docs"></canvas></div></div>
<h3>四、占比分析</h3>
<div class="card-row" id="stats-proportion-cards"></div>
<div class="chart-row"><div class="chart-box"><div class="chart-title">投诉原因分类占比</div><canvas id="chart-reason-pie"></canvas></div><div class="chart-box"><div class="chart-title">产品类型投诉占比</div><canvas id="chart-product-pie"></canvas></div></div>
<div class="chart-row"><div class="chart-box"><div class="chart-title">各环节单据处理率</div><canvas id="chart-stage-rate"></canvas></div><div class="chart-box"><div class="chart-title">业务员投诉分布</div><canvas id="chart-salesman-bar"></canvas></div></div>
<div class="chart-row"><div class="chart-box col-full"><div class="chart-title">审核通过率</div><canvas id="chart-review-rate"></canvas></div></div>
</div>
</div>
<!-- 弹窗 -->
<div class="modal-overlay" id="modal-contract"><div class="modal"><div class="modal-header"><h4>选择合同</h4><button class="modal-close" onclick="closeModal('modal-contract')">&times;</button></div><div class="modal-body"><div class="modal-search"><input type="text" placeholder="搜索合同..." oninput="filterContracts(this.value)"></div><table class="modal-table" id="contract-table"><thead><tr><th>合同编号</th><th>合同名称</th><th>销售员</th><th>签订时间</th></tr></thead><tbody></tbody></table></div><div class="modal-footer"><button class="btn" onclick="closeModal('modal-contract')">取消</button><button class="btn btn-primary" onclick="confirmContract()">确认选择</button></div></div></div>
<div class="modal-overlay" id="modal-coil"><div class="modal"><div class="modal-header"><h4>选择钢卷</h4><button class="modal-close" onclick="closeModal('modal-coil')">&times;</button></div><div class="modal-body"><div class="modal-search"><input type="text" placeholder="搜索卷号/规格/材质..." oninput="filterCoils(this.value)"></div><table class="modal-table" id="coil-table"><thead><tr><th>入场卷号</th><th>规格</th><th>材质</th><th>重量(t)</th><th>厂家</th></tr></thead><tbody></tbody></table></div><div class="modal-footer"><button class="btn" onclick="closeModal('modal-coil')">取消</button><button class="btn btn-primary" onclick="confirmCoils()">确认选择</button></div></div></div>
<div class="modal-overlay" id="modal-review"><div class="modal" style="width:480px;"><div class="modal-header"><h4>投诉审核</h4><button class="modal-close" onclick="closeModal('modal-review')">&times;</button></div><div class="modal-body"><div style="margin-bottom:12px;"><label style="font-size:13px;font-weight:bold;">审核结果:</label><select id="review-result" style="border:2px solid #000;padding:6px 10px;font-size:13px;width:100%;margin-top:6px;"><option value="approved">已通过</option><option value="rejected">未通过</option></select></div><div><label style="font-size:13px;font-weight:bold;">审核意见:</label><textarea id="review-note" style="border:2px solid #000;padding:6px 8px;font-size:13px;width:100%;min-height:80px;margin-top:6px;font-family:inherit;"></textarea></div></div><div class="modal-footer"><button class="btn" onclick="closeModal('modal-review')">取消</button><button class="btn btn-primary" onclick="doReview()">提交审核</button></div></div></div>
<div class="modal-overlay" id="modal-import-cfm"><div class="modal"><div class="modal-header"><h4>选择已审核通过的投诉受理单</h4><button class="modal-close" onclick="closeModal('modal-import-cfm')">&times;</button></div><div class="modal-body"><table class="modal-table"><thead><tr><th>投诉编号</th><th>投诉日期</th><th>投诉情况</th><th>审核状态</th></tr></thead><tbody id="import-cfm-tbody"></tbody></table></div><div class="modal-footer"><button class="btn" onclick="closeModal('modal-import-cfm')">取消</button></div></div></div>
<div class="modal-overlay" id="modal-import-anl"><div class="modal"><div class="modal-header"><h4>选择已处理的投诉确认单</h4><button class="modal-close" onclick="closeModal('modal-import-anl')">&times;</button></div><div class="modal-body"><table class="modal-table"><thead><tr><th>确认单号</th><th>来源受理单</th><th>处理人</th><th>状态</th></tr></thead><tbody id="import-anl-tbody"></tbody></table></div><div class="modal-footer"><button class="btn" onclick="closeModal('modal-import-anl')">取消</button></div></div></div>
<div class="modal-overlay" id="modal-import-prc"><div class="modal"><div class="modal-header"><h4>选择已处理的投诉分析单</h4><button class="modal-close" onclick="closeModal('modal-import-prc')">&times;</button></div><div class="modal-body"><table class="modal-table"><thead><tr><th>分析单号</th><th>来源确认单</th><th>处理人</th><th>状态</th></tr></thead><tbody id="import-prc-tbody"></tbody></table></div><div class="modal-footer"><button class="btn" onclick="closeModal('modal-import-prc')">取消</button></div></div></div>
<div class="modal-overlay" id="modal-import-flp"><div class="modal"><div class="modal-header"><h4>选择已处理的投诉处理单</h4><button class="modal-close" onclick="closeModal('modal-import-flp')">&times;</button></div><div class="modal-body"><table class="modal-table"><thead><tr><th>处理单号</th><th>来源分析单</th><th>处理人</th><th>状态</th></tr></thead><tbody id="import-flp-tbody"></tbody></table></div><div class="modal-footer"><button class="btn" onclick="closeModal('modal-import-flp')">取消</button></div></div></div>
<script>
/* ===== 模拟数据 ===== */
const mockContracts=[{id:1,name:'热轧钢卷采购合同',no:'HT-2026-001',amount:1280000,salesman:'张伟',signDate:'2026-03-15',deliveryDate:'2026-06-30',signPlace:'山东嘉祥',productName:'热轧钢卷',factory:'宝钢股份',items:[{seq:1,spec:'3.0×1250',material:'Q235B',qty:80,priceTax:5200,rate:1.13,priceNoTax:4601.77,totalTax:416000,totalNoTax:368141.59,taxAmount:47858.41,remark:''},{seq:2,spec:'4.0×1500',material:'Q345B',qty:120,priceTax:5400,rate:1.13,priceNoTax:4778.76,totalTax:648000,totalNoTax:573451.33,taxAmount:74548.67,remark:''}]},{id:2,name:'冷轧板采购合同',no:'HT-2026-002',amount:960000,salesman:'李强',signDate:'2026-04-01',deliveryDate:'2026-07-15',signPlace:'山东济南',productName:'冷轧板',factory:'鞍钢集团',items:[{seq:1,spec:'1.5×1250',material:'DC01',qty:200,priceTax:4800,rate:1.13,priceNoTax:4247.79,totalTax:960000,totalNoTax:849557.52,taxAmount:110442.48,remark:''}]},{id:3,name:'镀锌钢卷采购合同',no:'HT-2026-003',amount:1560000,salesman:'王芳',signDate:'2026-05-10',deliveryDate:'2026-08-20',signPlace:'山东青岛',productName:'镀锌钢卷',factory:'首钢集团',items:[{seq:1,spec:'2.0×1250',material:'DX51D',qty:150,priceTax:5600,rate:1.13,priceNoTax:4955.75,totalTax:840000,totalNoTax:743362.83,taxAmount:96637.17,remark:''},{seq:2,spec:'2.5×1500',material:'DX52D',qty:100,priceTax:7200,rate:1.13,priceNoTax:6371.68,totalTax:720000,totalNoTax:637168.14,taxAmount:82831.86,remark:''}]}];
const mockCoils=[{id:1,entryNo:'RC-2026-001',currentNo:'CC-2026-001',chromeNo:'GC-2026-001',location:'A-3-12',material:'热轧钢卷',spec:'3.0×1250',weight:15.6,texture:'Q235B',factory:'宝钢股份',zone:'A区',createTime:'2026-04-10',quality:'一级',surface:'光面',remark:'',trim:'切边',packaging:'铁皮包装',coatingQuality:'优'},{id:2,entryNo:'RC-2026-002',currentNo:'CC-2026-002',chromeNo:'GC-2026-002',location:'A-3-13',material:'热轧钢卷',spec:'4.0×1500',weight:18.2,texture:'Q345B',factory:'宝钢股份',zone:'A区',createTime:'2026-04-12',quality:'一级',surface:'光面',remark:'',trim:'切边',packaging:'铁皮包装',coatingQuality:'优'},{id:3,entryNo:'RC-2026-003',currentNo:'CC-2026-003',chromeNo:'GC-2026-003',location:'B-5-08',material:'冷轧板',spec:'1.5×1250',weight:12.8,texture:'DC01',factory:'鞍钢集团',zone:'B区',createTime:'2026-04-15',quality:'一级',surface:'光面',remark:'',trim:'不切边',packaging:'塑料膜包装',coatingQuality:'良'},{id:4,entryNo:'RC-2026-004',currentNo:'CC-2026-004',chromeNo:'GC-2026-004',location:'C-7-21',material:'镀锌钢卷',spec:'2.0×1250',weight:20.1,texture:'DX51D',factory:'首钢集团',zone:'C区',createTime:'2026-05-05',quality:'一级',surface:'镀锌',remark:'',trim:'切边',packaging:'铁皮包装',coatingQuality:'优'},{id:5,entryNo:'RC-2026-005',currentNo:'CC-2026-005',chromeNo:'GC-2026-005',location:'C-7-22',material:'镀锌钢卷',spec:'2.5×1500',weight:22.4,texture:'DX52D',factory:'首钢集团',zone:'C区',createTime:'2026-05-08',quality:'一级',surface:'镀锌',remark:'',trim:'切边',packaging:'铁皮包装',coatingQuality:'优'}];
let complaints=[{id:'TS-2026-001',date:'2026-04-05',situation:'热轧钢卷表面出现锈蚀斑点,影响后续加工质量。',demand:'要求退换货或赔偿损失。',remark:'客户已提供照片证据。',contractId:1,coilIds:[1,2],reviewStatus:'approved',reviewNote:'情况属实,同意受理。'},{id:'TS-2026-002',date:'2026-04-08',situation:'冷轧板厚度不均匀,部分区域偏差超过标准范围。',demand:'要求重新检测并补发合格产品。',remark:'已取样送检。',contractId:2,coilIds:[3],reviewStatus:'approved',reviewNote:'经核实,情况属实。'},{id:'TS-2026-003',date:'2026-04-12',situation:'镀锌钢卷镀层厚度不达标,客户要求退货。',demand:'全额退款并承担运费。',remark:'第三方检测报告已出具。',contractId:3,coilIds:[4,5],reviewStatus:'rejected',reviewNote:'证据不足,需补充第三方检测报告原件。'},{id:'TS-2026-004',date:'2026-04-20',situation:'热轧钢卷边部开裂,加工时出现断裂。',demand:'退货处理并补偿加工损失。',remark:'加工厂现场照片已提交。',contractId:1,coilIds:[1],reviewStatus:'approved',reviewNote:'加工断裂属实,同意受理。'},{id:'TS-2026-005',date:'2026-05-05',situation:'冷轧板表面有明显划痕,影响喷涂效果。',demand:'换货或折价处理。',remark:'划痕深度超0.1mm,已拍照。',contractId:2,coilIds:[3],reviewStatus:'approved',reviewNote:'划痕明显,同意受理。'},{id:'TS-2026-006',date:'2026-05-10',situation:'镀锌钢卷锌层附着力不足,客户投诉。',demand:'要求技术检测并赔偿。',remark:'附着力测试不合格报告已出具。',contractId:3,coilIds:[4],reviewStatus:'approved',reviewNote:'附着力不达标,同意受理。'},{id:'TS-2026-007',date:'2026-05-18',situation:'热轧钢卷规格偏差宽度超差3mm。',demand:'按合同约定赔偿。',remark:'客户自检记录已提供。',contractId:1,coilIds:[2],reviewStatus:'approved',reviewNote:'规格偏差属实。'},{id:'TS-2026-008',date:'2026-05-22',situation:'冷轧板包装破损导致运输中进水生锈。',demand:'换货并承担运费。',remark:'物流签收记录显示外包装破损。',contractId:2,coilIds:[3],reviewStatus:'unreviewed',reviewNote:''},{id:'TS-2026-009',date:'2026-06-01',situation:'镀锌钢卷表面有氧化白斑,外观不合格。',demand:'换货处理。',remark:'白斑面积约占20%。',contractId:3,coilIds:[5],reviewStatus:'approved',reviewNote:'白斑明显,同意受理。'},{id:'TS-2026-010',date:'2026-06-05',situation:'热轧钢卷硬度偏高,加工困难。',demand:'退换货。',remark:'硬度检测值超标准12%。',contractId:1,coilIds:[1,2],reviewStatus:'approved',reviewNote:'硬度超标属实。'},{id:'TS-2026-011',date:'2026-06-08',situation:'冷轧板平整度不佳,板型波浪明显。',demand:'折价处理或退货。',remark:'波高约3mm/米。',contractId:2,coilIds:[3],reviewStatus:'rejected',reviewNote:'波高标准在允许范围内,不予受理。'},{id:'TS-2026-012',date:'2026-06-10',situation:'镀锌钢卷运输变形,端部碰撞受损。',demand:'赔偿端部受损部分。',remark:'物流保险已报备。',contractId:3,coilIds:[4,5],reviewStatus:'approved',reviewNote:'运输损坏,保险理赔中。'},{id:'TS-2026-013',date:'2026-06-14',situation:'热轧钢卷氧化皮过厚,酸洗效果差。',demand:'技术指导或换货。',remark:'酸洗线反馈。',contractId:1,coilIds:[1],reviewStatus:'approved',reviewNote:'氧化皮偏厚,同意受理。'},{id:'TS-2026-014',date:'2026-06-16',situation:'冷轧板屈服强度偏低,冲压开裂。',demand:'退货并赔偿模具损失。',remark:'力学性能检测报告已出具。',contractId:2,coilIds:[3],reviewStatus:'unreviewed',reviewNote:''}];
let confirmations=[{id:'QR-2026-001',complaintId:'TS-2026-001',date:'2026-04-07',handler:'张伟',confirmContent:'经现场核实,热轧钢卷表面确有锈蚀,属于运输过程中防护不当所致。已与宝钢股份沟通,同意换货处理。',confirmResult:'换货处理,由供应商承担运费。',status:'done',files:[{name:'现场照片1.jpg',size:'2.3MB'},{name:'检测报告.pdf',size:'1.1MB'}]},{id:'QR-2026-002',complaintId:'TS-2026-002',date:'2026-04-10',handler:'李强',confirmContent:'冷轧板厚度偏差已送第三方检测,结果超出允许偏差范围。',confirmResult:'补发合格产品并扣除该批次货款5%作为违约金。',status:'done',files:[]},{id:'QR-2026-003',complaintId:'TS-2026-004',date:'2026-04-22',handler:'张伟',confirmContent:'热轧钢卷边部开裂确属原材料缺陷。',confirmResult:'退货并补偿加工损失费8000元。',status:'done',files:[{name:'开裂照片.jpg',size:'3.1MB'}]},{id:'QR-2026-004',complaintId:'TS-2026-005',date:'2026-05-07',handler:'李强',confirmContent:'冷轧板表面划痕系开平加工时设备刮擦所致。',confirmResult:'换货处理,加工厂承担运费。',status:'done',files:[]},{id:'QR-2026-005',complaintId:'TS-2026-006',date:'2026-05-12',handler:'王芳',confirmContent:'镀锌钢卷锌层附着力不达标,属涂镀工艺问题。',confirmResult:'退货退款,供应商承担全部费用。',status:'done',files:[{name:'附着力检测报告.pdf',size:'0.8MB'}]},{id:'QR-2026-006',complaintId:'TS-2026-007',date:'2026-05-20',handler:'张伟',confirmContent:'热轧钢卷宽度偏差属实,超出合同约定公差。',confirmResult:'按合同约定赔偿货值3%。',status:'done',files:[]},{id:'QR-2026-007',complaintId:'TS-2026-009',date:'2026-06-03',handler:'王芳',confirmContent:'镀锌钢卷表面氧化白斑属储存环境湿度过高所致。',confirmResult:'换货处理,同时协助客户改善存储条件。',status:'undone',files:[{name:'白斑照片.jpg',size:'1.5MB'}]},{id:'QR-2026-008',complaintId:'TS-2026-010',date:'2026-06-07',handler:'张伟',confirmContent:'热轧钢卷硬度偏高属热处理工艺偏差。',confirmResult:'退货处理,供应商自查热处理工艺。',status:'undone',files:[]},{id:'QR-2026-009',complaintId:'TS-2026-012',date:'2026-06-12',handler:'王芳',confirmContent:'镀锌钢卷运输受损,物流公司已确认责任。',confirmResult:'保险公司理赔中,先行补发。',status:'done',files:[{name:'物流签收单.pdf',size:'0.3MB'}]},{id:'QR-2026-010',complaintId:'TS-2026-013',date:'2026-06-15',handler:'张伟',confirmContent:'热轧钢卷氧化皮偏厚,影响酸洗效率。',confirmResult:'技术团队赴现场指导酸洗工艺调整。',status:'undone',files:[]}];
let analyses=[{id:'FX-2026-001',confirmId:'QR-2026-001',date:'2026-04-09',handler:'张伟',reasons:[{seq:1,reason:'运输途中防锈措施不足,导致钢卷进水',measure:'要求物流公司加强防雨布覆盖,运输签收时拍照留证'},{seq:2,reason:'仓库存储环境湿度过高',measure:'排查仓库除湿设备确保湿度≤60%'}],status:'done'},{id:'FX-2026-002',confirmId:'QR-2026-002',date:'2026-04-14',handler:'李强',reasons:[{seq:1,reason:'轧机辊缝控制精度不足,导致厚度偏差',measure:'建议供应商校准轧机AGC系统'}],status:'done'},{id:'FX-2026-003',confirmId:'QR-2026-003',date:'2026-04-25',handler:'张伟',reasons:[{seq:1,reason:'连铸坯内部存在裂纹缺陷',measure:'加强连铸坯探伤检测,不合格品不入库'}],status:'done'},{id:'FX-2026-004',confirmId:'QR-2026-004',date:'2026-05-10',handler:'李强',reasons:[{seq:1,reason:'开平线输送辊表面有硬质异物',measure:'定期清理输送辊,增加检查频次'}],status:'done'},{id:'FX-2026-005',confirmId:'QR-2026-005',date:'2026-05-15',handler:'王芳',reasons:[{seq:1,reason:'镀锌线锌液温度控制不稳定',measure:'升级温控系统,增加温度监测点'},{seq:2,reason:'基板表面清洗不彻底',measure:'增加清洗段碱液浓度和冲洗压力'}],status:'done'},{id:'FX-2026-006',confirmId:'QR-2026-006',date:'2026-05-25',handler:'张伟',reasons:[{seq:1,reason:'热轧精轧机组宽度控制模型偏差',measure:'重新标定宽度计,优化模型参数'}],status:'done'},{id:'FX-2026-007',confirmId:'QR-2026-009',date:'2026-06-14',handler:'王芳',reasons:[{seq:1,reason:'物流运输途中固定不牢导致碰撞',measure:'要求物流公司使用专用钢卷支架运输'}],status:'done'}];
let processings=[{id:'CL-2026-001',analysisId:'FX-2026-001',date:'2026-04-12',handler:'王工',processingDetail:'已完成换货处理,新批次钢卷已送达客户,无锈蚀问题。运输全程采用防雨布+干燥剂方案。仓库已新增2台除湿机。',processingFiles:[{name:'换货交接单.pdf',size:'0.5MB'}],status:'done'},{id:'CL-2026-002',analysisId:'FX-2026-002',date:'2026-04-18',handler:'赵工',processingDetail:'供应商已完成AGC系统校准补发批次厚度检测合格。',processingFiles:[],status:'done'},{id:'CL-2026-003',analysisId:'FX-2026-003',date:'2026-04-30',handler:'王工',processingDetail:'探伤检测流程已强化,新增在线探伤设备一台。客户已收到合格替换产品。',processingFiles:[{name:'探伤工艺规程.pdf',size:'1.2MB'}],status:'done'},{id:'CL-2026-004',analysisId:'FX-2026-004',date:'2026-05-14',handler:'赵工',processingDetail:'开平线输送辊清洁完成,增加每周一次深度清洁制度。',processingFiles:[],status:'done'},{id:'CL-2026-005',analysisId:'FX-2026-005',date:'2026-05-20',handler:'孙工',processingDetail:'温控系统已升级新增4个温度监测点。清洗段碱液浓度和冲洗压力已调整。',processingFiles:[{name:'温控系统升级方案.pdf',size:'1.8MB'}],status:'done'},{id:'CL-2026-006',analysisId:'FX-2026-006',date:'2026-05-30',handler:'王工',processingDetail:'宽度计已重新标定模型参数优化后宽度控制精度恢复至±1mm。',processingFiles:[],status:'done'}];
let followups=[{id:'HF-2026-001',processingId:'CL-2026-001',date:'2026-04-16',handler:'张伟',followupDetail:'电话回访客户,确认新批次产品已验收合格,对处理结果表示满意。',confirmContent:'客户确认无质量问题,投诉关闭。',status:'done'},{id:'HF-2026-002',processingId:'CL-2026-002',date:'2026-04-22',handler:'李强',followupDetail:'客户反馈补发冷轧板使用正常,厚度均匀性已改善。',confirmContent:'投诉关闭,客户满意度良好。',status:'done'},{id:'HF-2026-003',processingId:'CL-2026-003',date:'2026-05-05',handler:'张伟',followupDetail:'客户对探伤流程强化表示认可,后续合作信心增强。',confirmContent:'投诉关闭。',status:'done'},{id:'HF-2026-004',processingId:'CL-2026-004',date:'2026-05-20',handler:'李强',followupDetail:'清洁制度执行后,后续批次未再出现划痕问题。',confirmContent:'投诉关闭。',status:'done'},{id:'HF-2026-005',processingId:'CL-2026-005',date:'2026-05-26',handler:'王芳',followupDetail:'回访确认温控升级后锌层附着力达标,客户认可改进效果。',confirmContent:'投诉关闭,客户表示满意。',status:'done'}];
let nComplaint=15,nConfirm=11,nAnalysis=8,nProcessing=7,nFollowup=6;
let curComp=null,curCfm=null,curAnl=null,curPrc=null,curFlp=null;
let tempContractId=null,tempCoilIds=[];
/* ===== 工具函数 ===== */
function esc(s){return(s||'').replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');}
function getC(id){return mockContracts.find(c=>c.id===id);}
function getCo(id){return mockCoils.find(c=>c.id===id);}
function getCp(id){return complaints.find(c=>c.id===id);}
function getCf(id){return confirmations.find(c=>c.id===id);}
function getAn(id){return analyses.find(c=>c.id===id);}
function getPr(id){return processings.find(c=>c.id===id);}
function getFl(id){return followups.find(c=>c.id===id);}
function rvLabel(s){return s==='approved'?'已通过':s==='rejected'?'未通过':'未审核';}
function rvCls(s){return s==='approved'?'sb-ap':s==='rejected'?'sb-rj':'sb-ur';}
function stLabel(s){return s==='done'?'已处理':'未处理';}
function stCls(s){return s==='done'?'sb-dn':'sb-ud';}
function closeModal(id){document.getElementById(id).classList.remove('show');}
function today(){return new Date().toISOString().slice(0,10);}
/* 通用列表项HTML */
function listItem(id,date,preview,statusBadge){return `<div class="complaint-item" onclick=""><div class="ci-id">${id}</div><div class="ci-status">${statusBadge}</div><div class="ci-date">${date}</div><div class="ci-preview">${preview}</div></div>`;}
/* ===== 页面切换 ===== */
function switchPage(p){
document.querySelectorAll('.menu-item').forEach(m=>{m.classList.toggle('active',m.dataset.page===p);});
document.querySelectorAll('.page-view').forEach(v=>v.classList.remove('active'));
document.getElementById('page-'+p).classList.add('active');
if(p==='complaint')renderCompList();if(p==='confirm')renderCfmList();
if(p==='analysis')renderAnlList();if(p==='processing')renderPrcList();
if(p==='followup')renderFlpList();
if(p==='statistics')initStatsPage();
}
/* ===== 投诉受理 ===== */
function renderCompList(){
const el=document.getElementById('complaint-list');
el.innerHTML=complaints.length?complaints.map(c=>`<div class="complaint-item${curComp===c.id?' active':''}" onclick="selectComp('${c.id}')"><div class="ci-id">${c.id}</div><div class="ci-status"><span class="status-badge ${rvCls(c.reviewStatus)}">${rvLabel(c.reviewStatus)}</span></div><div class="ci-date">${c.date}</div><div class="ci-preview">${(c.situation||'').substring(0,28)}</div></div>`).join(''):'<div style="padding:20px;text-align:center;color:#999;">暂无投诉受理单</div>';
}
function selectComp(id){curComp=id;renderCompList();renderCompDetail();}
function createNew(){
const id='TS-2026-'+String(nComplaint++).padStart(3,'0');
complaints.push({id,date:today(),situation:'',demand:'',remark:'',contractId:null,coilIds:[],reviewStatus:'unreviewed',reviewNote:''});
curComp=id;renderCompList();renderCompDetail();
}
function deleteComp(id){
if(!confirm('确认删除投诉受理单 '+id+''))return;
complaints=complaints.filter(c=>c.id!==id);
if(curComp===id)curComp=null;
renderCompList();document.getElementById('comp-detail').style.display='none';document.getElementById('comp-empty').style.display='flex';
}
function renderCompDetail(){
const c=getCp(curComp);if(!c)return;
document.getElementById('comp-empty').style.display='none';document.getElementById('comp-detail').style.display='block';
const ct=c.contractId?getC(c.contractId):null;
const cls=(c.coilIds||[]).map(id=>getCo(id)).filter(Boolean);
let h=`<div class="watermark-top">嘉祥科伦普重工有限公司专用</div><div class="watermark-id">投诉编号:${c.id}</div><h2>投诉受理单</h2>
<div style="margin-bottom:16px;display:flex;align-items:center;gap:12px;flex-wrap:wrap;"><span class="status-badge ${rvCls(c.reviewStatus)}" style="font-size:13px;padding:4px 14px;">审核状态:${rvLabel(c.reviewStatus)}</span>${c.reviewStatus!=='unreviewed'?`<span style="font-size:12px;color:#666;">审核意见:${esc(c.reviewNote)||'无'}</span>`:''}<div style="flex:1;"></div>`;
if(c.reviewStatus==='unreviewed')h+=`<button class="btn btn-primary" onclick="openReviewModal()">审核</button>`;
else h+=`<button class="btn" onclick="openReviewModal()">${c.reviewStatus==='rejected'?'重新审核':'查看审核'}</button>`;
h+=`</div>`;
h+=`<div class="section"><div class="section-title">第三部分 — 投诉信息</div><div class="section-body"><div class="form-grid cols2"><div class="form-group"><label>投诉编号</label><input value="${c.id}" disabled style="background:#f0f0f0;"></div><div class="form-group"><label>投诉日期</label><input type="date" value="${c.date}" onchange="updateComp('date',this.value)"></div><div class="form-group full"><label>投诉情况</label><textarea oninput="updateComp('situation',this.value)">${esc(c.situation)}</textarea></div><div class="form-group full"><label>客户诉求</label><textarea oninput="updateComp('demand',this.value)">${esc(c.demand)}</textarea></div><div class="form-group full"><label>备注</label><textarea oninput="updateComp('remark',this.value)">${esc(c.remark)}</textarea></div></div></div></div>`;
h+=`<div class="section"><div class="section-title">第一部分 — 合同基础信息</div><div class="section-body"><div style="margin-bottom:8px;"><span style="font-size:12px;font-weight:bold;">挂接合同:</span>${ct?`<span class="tag">${ct.no} ${ct.name}<span class="tag-remove" onclick="removeContract()">&times;</span></span>`:'<span style="color:#999;font-size:12px;">未选择</span>'}<button class="btn btn-sm" style="margin-left:8px;" onclick="openContractModal()">选择合同</button></div>`;
if(ct){h+=`<div class="form-grid"><div class="form-group"><label>合同名称</label><input value="${esc(ct.name)}" disabled style="background:#f0f0f0;"></div><div class="form-group"><label>合同编号</label><input value="${ct.no}" disabled style="background:#f0f0f0;"></div><div class="form-group"><label>订单金额(元)</label><input value="${ct.amount.toLocaleString()}" disabled style="background:#f0f0f0;"></div><div class="form-group"><label>销售员</label><input value="${ct.salesman}" disabled style="background:#f0f0f0;"></div><div class="form-group"><label>签订时间</label><input value="${ct.signDate}" disabled style="background:#f0f0f0;"></div><div class="form-group"><label>交货日期</label><input value="${ct.deliveryDate}" disabled style="background:#f0f0f0;"></div><div class="form-group"><label>签订地点</label><input value="${ct.signPlace}" disabled style="background:#f0f0f0;"></div><div class="form-group"><label>产品名称</label><input value="${ct.productName}" disabled style="background:#f0f0f0;"></div><div class="form-group"><label>产品厂家</label><input value="${ct.factory}" disabled style="background:#f0f0f0;"></div></div>${renderProductItems(ct.items)}`;}
h+=`</div></div>`;
h+=`<div class="section"><div class="section-title">第二部分 — 钢卷信息</div><div class="section-body"><div style="margin-bottom:8px;"><span style="font-size:12px;font-weight:bold;">挂接钢卷:</span>${cls.length?cls.map(cl=>`<span class="tag">${cl.entryNo}<span class="tag-remove" onclick="removeCoil(${cl.id})">&times;</span></span>`).join(''):'<span style="color:#999;font-size:12px;">未选择</span>'}<button class="btn btn-sm" style="margin-left:8px;" onclick="openCoilModal()">选择钢卷</button></div>${cls.length?renderCoilTable(cls):''}</div></div>`;
h+=`<div class="action-bar"><button class="btn btn-danger" onclick="deleteComp('${c.id}')">删除此单</button><div style="flex:1;"></div><button class="btn btn-primary" onclick="alert('投诉受理单已保存!')">保存</button></div>`;
document.getElementById('comp-detail').innerHTML=h;
}
function updateComp(f,v){const c=getCp(curComp);if(!c)return;c[f]=v;renderCompList();}
/* ===== 审核 ===== */
function openReviewModal(){const c=getCp(curComp);if(!c)return;document.getElementById('review-result').value=c.reviewStatus==='rejected'?'rejected':'approved';document.getElementById('review-note').value=c.reviewNote||'';document.getElementById('modal-review').classList.add('show');}
function doReview(){const c=getCp(curComp);if(!c)return;c.reviewStatus=document.getElementById('review-result').value;c.reviewNote=document.getElementById('review-note').value;closeModal('modal-review');renderCompDetail();renderCompList();alert('审核完成:'+rvLabel(c.reviewStatus));}
/* ===== 投诉确认 ===== */
function renderCfmList(){
const el=document.getElementById('confirm-list');
el.innerHTML=confirmations.length?confirmations.map(c=>`<div class="complaint-item${curCfm===c.id?' active':''}" onclick="selectCfm('${c.id}')"><div class="ci-id">${c.id}</div><div class="ci-status"><span class="status-badge ${stCls(c.status)}">${stLabel(c.status)}</span></div><div class="ci-date">${c.date}</div><div class="ci-preview">来源:${c.complaintId}</div></div>`).join(''):'<div style="padding:20px;text-align:center;color:#999;">暂无投诉确认单</div>';
}
function selectCfm(id){curCfm=id;renderCfmList();renderCfmDetail();}
function createConfirm(){
const approved=complaints.filter(c=>c.reviewStatus==='approved'&&!confirmations.find(q=>q.complaintId===c.id));
if(!approved.length){alert('当前没有已审核通过且未生成确认单的投诉受理单。');return;}
const tb=document.getElementById('import-cfm-tbody');
tb.innerHTML=approved.map(c=>`<tr style="cursor:pointer;" onclick="doImportCfm('${c.id}')"><td>${c.id}</td><td>${c.date}</td><td>${(c.situation||'').substring(0,20)}...</td><td><span class="status-badge sb-ap">已通过</span></td></tr>`).join('');
document.getElementById('modal-import-cfm').classList.add('show');
}
function doImportCfm(cid){closeModal('modal-import-cfm');const id='QR-2026-'+String(nConfirm++).padStart(3,'0');confirmations.push({id,complaintId:cid,date:today(),handler:'',confirmContent:'',confirmResult:'',status:'undone',files:[]});curCfm=id;renderCfmList();renderCfmDetail();}
function deleteCfm(id){if(!confirm('确认删除投诉确认单 '+id+''))return;confirmations=confirmations.filter(c=>c.id!==id);if(curCfm===id)curCfm=null;renderCfmList();document.getElementById('cfm-detail').style.display='none';document.getElementById('cfm-empty').style.display='flex';}
function renderCfmDetail(){
const q=getCf(curCfm);if(!q)return;document.getElementById('cfm-empty').style.display='none';document.getElementById('cfm-detail').style.display='block';
const c=getCp(q.complaintId);const ct=c&&c.contractId?getC(c.contractId):null;const cls=c&&c.coilIds?c.coilIds.map(id=>getCo(id)).filter(Boolean):[];
let h=`<div class="watermark-top">嘉祥科伦普重工有限公司专用</div><div class="watermark-id">确认单号:${q.id} (来源:${q.complaintId}</div><h2>投诉确认单</h2>
<div style="margin-bottom:16px;display:flex;align-items:center;gap:12px;"><span class="status-badge ${stCls(q.status)}" style="font-size:13px;padding:4px 14px;">处理状态:${stLabel(q.status)}</span><div style="flex:1;"></div><button class="btn btn-sm ${q.status==='done'?'':'btn-success'}" onclick="toggleCfmStatus()">${q.status==='done'?'标记为未处理':'标记为已处理'}</button></div>
<div class="section"><div class="section-title">确认信息</div><div class="section-body"><div class="form-grid cols2"><div class="form-group"><label>确认单号</label><input value="${q.id}" disabled style="background:#f0f0f0;"></div><div class="form-group"><label>确认日期</label><input type="date" value="${q.date}" onchange="updateCfm('date',this.value)"></div><div class="form-group"><label>处理人</label><input value="${esc(q.handler)}" oninput="updateCfm('handler',this.value)" placeholder="请输入处理人姓名"></div><div class="form-group full"><label>确认内容</label><textarea oninput="updateCfm('confirmContent',this.value)" placeholder="请填写投诉确认的具体情况和处理方案...">${esc(q.confirmContent)}</textarea></div><div class="form-group full"><label>确认结果</label><textarea oninput="updateCfm('confirmResult',this.value)" placeholder="请填写最终处理结果...">${esc(q.confirmResult)}</textarea></div></div></div></div>`;
if(ct)h+=`<div class="section"><div class="section-title">合同信息(简明)</div><div class="section-body"><div class="form-grid cols4"><div class="form-group"><label>合同编号</label><input value="${ct.no}" disabled style="background:#f0f0f0;"></div><div class="form-group"><label>合同名称</label><input value="${esc(ct.name)}" disabled style="background:#f0f0f0;"></div><div class="form-group"><label>金额(元)</label><input value="${ct.amount.toLocaleString()}" disabled style="background:#f0f0f0;"></div><div class="form-group"><label>销售员</label><input value="${ct.salesman}" disabled style="background:#f0f0f0;"></div></div></div></div>`;
if(cls.length)h+=`<div class="section"><div class="section-title">钢卷信息(简明)</div><div class="section-body"><div class="sub-table-wrap"><table class="sub-table"><thead><tr><th>入场卷号</th><th>规格</th><th>重量(t)</th><th>材质</th><th>厂家</th><th>品质</th></tr></thead><tbody>${cls.map(cl=>`<tr><td>${cl.entryNo}</td><td>${cl.spec}</td><td>${cl.weight}</td><td>${cl.texture}</td><td>${cl.factory}</td><td>${cl.quality}</td></tr>`).join('')}</tbody></table></div></div></div>`;
if(c)h+=`<div class="section"><div class="section-title">投诉信息(简明)</div><div class="section-body"><div class="form-grid cols2"><div class="form-group"><label>投诉编号</label><input value="${c.id}" disabled style="background:#f0f0f0;"></div><div class="form-group"><label>投诉日期</label><input value="${c.date}" disabled style="background:#f0f0f0;"></div><div class="form-group full"><label>投诉情况</label><textarea disabled style="background:#f0f0f0;">${esc(c.situation)}</textarea></div><div class="form-group full"><label>客户诉求</label><textarea disabled style="background:#f0f0f0;">${esc(c.demand)}</textarea></div></div></div></div>`;
h+=`<div class="section"><div class="section-title">附件资料</div><div class="section-body"><div class="file-upload-area" onclick="document.getElementById('cfm-file').click()">点击上传文件<br><span style="font-size:11px;">支持 JPG/PNG/PDF/Word/Excel 等</span></div><input type="file" id="cfm-file" multiple style="display:none;" onchange="uploadCfmFile(this)"><div class="file-list">${(q.files||[]).map((f,i)=>`<div class="file-item"><span class="fi-name">📎 ${f.name}${f.size}</span><span class="fi-remove" onclick="delCfmFile(${i})">&times;</span></div>`).join('')}</div></div></div>`;
h+=`<div class="action-bar"><button class="btn btn-danger" onclick="deleteCfm('${q.id}')">删除</button><div style="flex:1;"></div><button class="btn btn-primary" onclick="alert('已保存!')">保存</button></div>`;
document.getElementById('cfm-detail').innerHTML=h;
}
function updateCfm(f,v){const q=getCf(curCfm);if(!q)return;q[f]=v;renderCfmList();}
function toggleCfmStatus(){const q=getCf(curCfm);if(!q)return;q.status=q.status==='done'?'undone':'done';renderCfmDetail();renderCfmList();}
function uploadCfmFile(inp){const q=getCf(curCfm);if(!q)return;for(const f of inp.files)q.files.push({name:f.name,size:(f.size/1024/1024).toFixed(1)+'MB'});inp.value='';renderCfmDetail();}
function delCfmFile(i){const q=getCf(curCfm);if(!q)return;q.files.splice(i,1);renderCfmDetail();}
/* ===== 投诉分析 ===== */
function renderAnlList(){
const el=document.getElementById('analysis-list');
el.innerHTML=analyses.length?analyses.map(a=>`<div class="complaint-item${curAnl===a.id?' active':''}" onclick="selectAnl('${a.id}')"><div class="ci-id">${a.id}</div><div class="ci-status"><span class="status-badge ${stCls(a.status)}">${stLabel(a.status)}</span></div><div class="ci-date">${a.date}</div><div class="ci-preview">来源:${a.confirmId}</div></div>`).join(''):'<div style="padding:20px;text-align:center;color:#999;">暂无投诉分析单</div>';
}
function selectAnl(id){curAnl=id;renderAnlList();renderAnlDetail();}
function createAnalysis(){
const done=confirmations.filter(c=>c.status==='done'&&!analyses.find(a=>a.confirmId===c.id));
if(!done.length){alert('当前没有已处理且未生成分析单的投诉确认单。');return;}
const tb=document.getElementById('import-anl-tbody');
tb.innerHTML=done.map(c=>`<tr style="cursor:pointer;" onclick="doImportAnl('${c.id}')"><td>${c.id}</td><td>${c.complaintId}</td><td>${c.handler||'-'}</td><td><span class="status-badge sb-dn">已处理</span></td></tr>`).join('');
document.getElementById('modal-import-anl').classList.add('show');
}
function doImportAnl(cfid){closeModal('modal-import-anl');const id='FX-2026-'+String(nAnalysis++).padStart(3,'0');analyses.push({id,confirmId:cfid,date:today(),handler:'',reasons:[],status:'undone'});curAnl=id;renderAnlList();renderAnlDetail();}
function deleteAnl(id){if(!confirm('确认删除投诉分析单 '+id+''))return;analyses=analyses.filter(a=>a.id!==id);if(curAnl===id)curAnl=null;renderAnlList();document.getElementById('anl-detail').style.display='none';document.getElementById('anl-empty').style.display='flex';}
function updateAnl(f,v){const a=getAn(curAnl);if(!a)return;a[f]=v;renderAnlList();}
function toggleAnlStatus(){const a=getAn(curAnl);if(!a)return;a.status=a.status==='done'?'undone':'done';renderAnlDetail();renderAnlList();}
function addAnlReason(){
const a=getAn(curAnl);if(!a)return;a.reasons.push({seq:a.reasons.length+1,reason:'',measure:''});
const tb=document.getElementById('anl-reason-tbody');if(!tb)return renderAnlDetail();
else tb.innerHTML+=`<tr id="anl-r-${a.reasons.length-1}"><td>${a.reasons.length}</td><td><textarea oninput="updateAnlReason(${a.reasons.length-1},'reason',this.value)"></textarea></td><td><textarea oninput="updateAnlReason(${a.reasons.length-1},'measure',this.value)"></textarea></td><td><button class="btn btn-sm btn-danger" onclick="delAnlReason(${a.reasons.length-1})">删除</button></td></tr>`;
}
function updateAnlReason(i,f,v){const a=getAn(curAnl);if(!a||!a.reasons[i])return;a.reasons[i][f]=v;}
function delAnlReason(i){const a=getAn(curAnl);if(!a)return;a.reasons.splice(i,1);a.reasons.forEach((r,idx)=>r.seq=idx+1);renderAnlDetail();}
function renderAnlDetail(){
const a=getAn(curAnl);if(!a)return;document.getElementById('anl-empty').style.display='none';document.getElementById('anl-detail').style.display='block';
const q=getCf(a.confirmId);if(!q)return;
const c=getCp(q.complaintId);const ct=c&&c.contractId?getC(c.contractId):null;const cls=c&&c.coilIds?c.coilIds.map(id=>getCo(id)).filter(Boolean):[];
let h=`<div class="watermark-top">嘉祥科伦普重工有限公司专用</div><div class="watermark-id">分析单号:${a.id} (来源确认单:${a.confirmId},投诉:${q.complaintId}</div><h2>投诉分析单</h2>
<div style="margin-bottom:16px;display:flex;align-items:center;gap:12px;"><span class="status-badge ${stCls(a.status)}" style="font-size:13px;padding:4px 14px;">${stLabel(a.status)}</span><div style="flex:1;"></div><button class="btn btn-sm ${a.status==='done'?'':'btn-success'}" onclick="toggleAnlStatus()">${a.status==='done'?'标记为未处理':'标记为已处理'}</button></div>
<div class="section"><div class="section-title">分析信息</div><div class="section-body"><div class="form-grid cols2"><div class="form-group"><label>分析单号</label><input value="${a.id}" disabled style="background:#f0f0f0;"></div><div class="form-group"><label>分析日期</label><input type="date" value="${a.date}" onchange="updateAnl('date',this.value)"></div><div class="form-group"><label>处理人</label><input value="${esc(a.handler)}" oninput="updateAnl('handler',this.value)"></div></div></div></div>`;
/* 投诉原因及处理措施表格 */
h+=`<div class="section"><div class="section-title">投诉原因及处理措施</div><div class="section-body"><div class="sub-table-wrap"><div class="sub-table-title">原因分析明细 <button class="btn btn-sm" onclick="addAnlReason()">+ 添加行</button></div><table class="sub-table"><thead><tr><th style="width:40px;">序号</th><th>投诉原因</th><th>处理措施</th><th style="width:60px;">操作</th></tr></thead><tbody id="anl-reason-tbody">`;
(a.reasons||[]).forEach((r,i)=>h+=`<tr><td>${r.seq}</td><td><textarea oninput="updateAnlReason(${i},'reason',this.value)">${esc(r.reason)}</textarea></td><td><textarea oninput="updateAnlReason(${i},'measure',this.value)">${esc(r.measure)}</textarea></td><td><button class="btn btn-sm btn-danger" onclick="delAnlReason(${i})">删除</button></td></tr>`);
h+=`</tbody></table></div></div></div>`;
/* 历史信息汇总 */
h+=`<div class="section"><div class="section-title">投诉确认单信息(来源)</div><div class="section-body">
<div class="info-row"><b>确认内容:</b>${esc(q.confirmContent)||'无'}</div><div class="info-row"><b>确认结果:</b>${esc(q.confirmResult)||'无'}</div>
<div class="info-row"><b>附件:</b>${(q.files||[]).map(f=>f.name).join('、')||'无'}</div></div></div>`;
h+=`<div class="section"><div class="section-title">投诉受理及合同钢卷信息(简明)</div><div class="section-body">
<div class="info-row"><b>投诉编号:</b>${c?c.id:'-'} <b>投诉日期:</b>${c?c.date:'-'}</div>
<div class="info-row"><b>投诉情况:</b>${c?esc(c.situation):'-'}</div>
<div class="info-row"><b>合同:</b>${ct?ct.no+' '+ct.name:'无'} <b>金额:</b>${ct?ct.amount.toLocaleString()+'元':'-'}</div>
<div class="info-row"><b>涉及钢卷:</b>${cls.length?cls.map(cl=>cl.entryNo+'('+cl.spec+')').join('、'):'无'}</div></div></div>`;
h+=`<div class="action-bar"><button class="btn btn-danger" onclick="deleteAnl('${a.id}')">删除</button><div style="flex:1;"></div><button class="btn btn-primary" onclick="alert('已保存!')">保存</button></div>`;
document.getElementById('anl-detail').innerHTML=h;
}
/* ===== 投诉处理 ===== */
function renderPrcList(){
const el=document.getElementById('processing-list');
el.innerHTML=processings.length?processings.map(p=>`<div class="complaint-item${curPrc===p.id?' active':''}" onclick="selectPrc('${p.id}')"><div class="ci-id">${p.id}</div><div class="ci-status"><span class="status-badge ${stCls(p.status)}">${stLabel(p.status)}</span></div><div class="ci-date">${p.date}</div><div class="ci-preview">来源:${p.analysisId}</div></div>`).join(''):'<div style="padding:20px;text-align:center;color:#999;">暂无投诉处理单</div>';
}
function selectPrc(id){curPrc=id;renderPrcList();renderPrcDetail();}
function createProcessing(){
const done=analyses.filter(a=>a.status==='done'&&!processings.find(p=>p.analysisId===a.id));
if(!done.length){alert('当前没有已处理且未生成处理单的投诉分析单。');return;}
const tb=document.getElementById('import-prc-tbody');
tb.innerHTML=done.map(a=>{const q=getCf(a.confirmId);return`<tr style="cursor:pointer;" onclick="doImportPrc('${a.id}')"><td>${a.id}</td><td>${q?q.complaintId:'-'}</td><td>${a.handler||'-'}</td><td><span class="status-badge sb-dn">已处理</span></td></tr>`;}).join('');
document.getElementById('modal-import-prc').classList.add('show');
}
function doImportPrc(aid){closeModal('modal-import-prc');const id='CL-2026-'+String(nProcessing++).padStart(3,'0');processings.push({id,analysisId:aid,date:today(),handler:'',processingDetail:'',processingFiles:[],status:'undone'});curPrc=id;renderPrcList();renderPrcDetail();}
function deletePrc(id){if(!confirm('确认删除投诉处理单 '+id+''))return;processings=processings.filter(p=>p.id!==id);if(curPrc===id)curPrc=null;renderPrcList();document.getElementById('prc-detail').style.display='none';document.getElementById('prc-empty').style.display='flex';}
function updatePrc(f,v){const p=getPr(curPrc);if(!p)return;p[f]=v;renderPrcList();}
function togglePrcStatus(){const p=getPr(curPrc);if(!p)return;p.status=p.status==='done'?'undone':'done';renderPrcDetail();renderPrcList();}
function uploadPrcFile(inp){const p=getPr(curPrc);if(!p)return;for(const f of inp.files)p.processingFiles.push({name:f.name,size:(f.size/1024/1024).toFixed(1)+'MB'});inp.value='';renderPrcDetail();}
function delPrcFile(i){const p=getPr(curPrc);if(!p)return;p.processingFiles.splice(i,1);renderPrcDetail();}
function renderPrcDetail(){
const p=getPr(curPrc);if(!p)return;document.getElementById('prc-empty').style.display='none';document.getElementById('prc-detail').style.display='block';
const a=getAn(p.analysisId);if(!a)return;const q=getCf(a.confirmId);if(!q)return;const c=getCp(q.complaintId);
const ct=c&&c.contractId?getC(c.contractId):null;const cls=c&&c.coilIds?c.coilIds.map(id=>getCo(id)).filter(Boolean):[];
let h=`<div class="watermark-top">嘉祥科伦普重工有限公司专用</div><div class="watermark-id">处理单号:${p.id} (来源分析单:${p.analysisId}</div><h2>投诉处理单</h2>
<div style="margin-bottom:16px;display:flex;align-items:center;gap:12px;"><span class="status-badge ${stCls(p.status)}" style="font-size:13px;padding:4px 14px;">${stLabel(p.status)}</span><div style="flex:1;"></div><button class="btn btn-sm ${p.status==='done'?'':'btn-success'}" onclick="togglePrcStatus()">${p.status==='done'?'标记为未处理':'标记为已处理'}</button></div>`;
/* 当前环节编辑区 */
h+=`<div class="section"><div class="section-title">处理信息</div><div class="section-body"><div class="form-grid cols2"><div class="form-group"><label>处理单号</label><input value="${p.id}" disabled style="background:#f0f0f0;"></div><div class="form-group"><label>处理日期</label><input type="date" value="${p.date}" onchange="updatePrc('date',this.value)"></div><div class="form-group"><label>处理人</label><input value="${esc(p.handler)}" oninput="updatePrc('handler',this.value)"></div><div class="form-group full"><label>处理实况</label><textarea oninput="updatePrc('processingDetail',this.value)" placeholder="请填写处理实况...">${esc(p.processingDetail)}</textarea></div></div>
<div class="file-upload-area" onclick="document.getElementById('prc-file').click()">点击上传处理资料<br><span style="font-size:11px;">支持照片、文档等</span></div><input type="file" id="prc-file" multiple style="display:none;" onchange="uploadPrcFile(this)"><div class="file-list">${(p.processingFiles||[]).map((f,i)=>`<div class="file-item"><span class="fi-name">📎 ${f.name}${f.size}</span><span class="fi-remove" onclick="delPrcFile(${i})">&times;</span></div>`).join('')}</div></div></div>`;
/* 历史信息分类展示 */
h+=`<div class="section"><div class="section-title">【投诉分析】原因及措施</div><div class="section-body"><table class="sub-table"><thead><tr><th>序号</th><th>投诉原因</th><th>处理措施</th></tr></thead><tbody>${(a.reasons||[]).map(r=>`<tr><td>${r.seq}</td><td>${esc(r.reason)}</td><td>${esc(r.measure)}</td></tr>`).join('')}</tbody></table></div></div>`;
h+=`<div class="section"><div class="section-title">【投诉确认】确认信息</div><div class="section-body"><div class="info-row"><b>确认内容:</b>${esc(q.confirmContent)||'无'}</div><div class="info-row"><b>确认结果:</b>${esc(q.confirmResult)||'无'}</div><div class="info-row"><b>附件:</b>${(q.files||[]).map(f=>f.name).join('、')||'无'}</div></div></div>`;
h+=`<div class="section"><div class="section-title">【投诉受理】投诉及合同钢卷</div><div class="section-body"><div class="info-row"><b>投诉编号:</b>${c?c.id:'-'} <b>投诉情况:</b>${c?esc(c.situation):'-'}</div><div class="info-row"><b>客户诉求:</b>${c?esc(c.demand):'-'}</div><div class="info-row"><b>合同:</b>${ct?ct.no+' '+ct.name:'无'} <b>金额:</b>${ct?ct.amount.toLocaleString()+'元':'-'} <b>销售员:</b>${ct?ct.salesman:'-'}</div><div class="info-row"><b>涉及钢卷:</b>${cls.length?cls.map(cl=>cl.entryNo+'('+cl.spec+'/'+cl.texture+')').join('、'):'无'}</div></div></div>`;
h+=`<div class="action-bar"><button class="btn btn-danger" onclick="deletePrc('${p.id}')">删除</button><div style="flex:1;"></div><button class="btn btn-primary" onclick="alert('已保存!')">保存</button></div>`;
document.getElementById('prc-detail').innerHTML=h;
}
/* ===== 回访确认 ===== */
function renderFlpList(){
const el=document.getElementById('followup-list');
el.innerHTML=followups.length?followups.map(f=>`<div class="complaint-item${curFlp===f.id?' active':''}" onclick="selectFlp('${f.id}')"><div class="ci-id">${f.id}</div><div class="ci-status"><span class="status-badge ${stCls(f.status)}">${stLabel(f.status)}</span></div><div class="ci-date">${f.date}</div><div class="ci-preview">来源:${f.processingId}</div></div>`).join(''):'<div style="padding:20px;text-align:center;color:#999;">暂无回访确认单</div>';
}
function selectFlp(id){curFlp=id;renderFlpList();renderFlpDetail();}
function createFollowup(){
const done=processings.filter(p=>p.status==='done'&&!followups.find(f=>f.processingId===p.id));
if(!done.length){alert('当前没有已处理且未生成回访单的投诉处理单。');return;}
const tb=document.getElementById('import-flp-tbody');
tb.innerHTML=done.map(p=>{const a=getAn(p.analysisId);return`<tr style="cursor:pointer;" onclick="doImportFlp('${p.id}')"><td>${p.id}</td><td>${a?a.id:'-'}</td><td>${p.handler||'-'}</td><td><span class="status-badge sb-dn">已处理</span></td></tr>`;}).join('');
document.getElementById('modal-import-flp').classList.add('show');
}
function doImportFlp(pid){closeModal('modal-import-flp');const id='HF-2026-'+String(nFollowup++).padStart(3,'0');followups.push({id,processingId:pid,date:today(),handler:'',followupDetail:'',confirmContent:'',status:'undone'});curFlp=id;renderFlpList();renderFlpDetail();}
function deleteFlp(id){if(!confirm('确认删除回访确认单 '+id+''))return;followups=followups.filter(f=>f.id!==id);if(curFlp===id)curFlp=null;renderFlpList();document.getElementById('flp-detail').style.display='none';document.getElementById('flp-empty').style.display='flex';}
function updateFlp(f,v){const fp=getFl(curFlp);if(!fp)return;fp[f]=v;renderFlpList();}
function toggleFlpStatus(){const fp=getFl(curFlp);if(!fp)return;fp.status=fp.status==='done'?'undone':'done';renderFlpDetail();renderFlpList();}
function renderFlpDetail(){
const f=getFl(curFlp);if(!f)return;document.getElementById('flp-empty').style.display='none';document.getElementById('flp-detail').style.display='block';
const p=getPr(f.processingId);if(!p)return;const a=getAn(p.analysisId);if(!a)return;const q=getCf(a.confirmId);if(!q)return;const c=getCp(q.complaintId);
const ct=c&&c.contractId?getC(c.contractId):null;const cls=c&&c.coilIds?c.coilIds.map(id=>getCo(id)).filter(Boolean):[];
let h=`<div class="watermark-top">嘉祥科伦普重工有限公司专用</div><div class="watermark-id">回访单号:${f.id} (来源处理单:${f.processingId}</div><h2>回访确认单</h2>
<div style="margin-bottom:16px;display:flex;align-items:center;gap:12px;"><span class="status-badge ${stCls(f.status)}" style="font-size:13px;padding:4px 14px;">${stLabel(f.status)}</span><div style="flex:1;"></div><button class="btn btn-sm ${f.status==='done'?'':'btn-success'}" onclick="toggleFlpStatus()">${f.status==='done'?'标记为未处理':'标记为已处理'}</button></div>`;
/* 回访编辑区 */
h+=`<div class="section"><div class="section-title">回访信息</div><div class="section-body"><div class="form-grid cols2"><div class="form-group"><label>回访单号</label><input value="${f.id}" disabled style="background:#f0f0f0;"></div><div class="form-group"><label>回访日期</label><input type="date" value="${f.date}" onchange="updateFlp('date',this.value)"></div><div class="form-group"><label>回访人</label><input value="${esc(f.handler)}" oninput="updateFlp('handler',this.value)"></div><div class="form-group full"><label>回访实况</label><textarea oninput="updateFlp('followupDetail',this.value)" placeholder="请填写回访实况...">${esc(f.followupDetail)}</textarea></div><div class="form-group full"><label>处理确认</label><textarea oninput="updateFlp('confirmContent',this.value)" placeholder="请填写处理确认内容...">${esc(f.confirmContent)}</textarea></div></div></div></div>`;
/* 历史信息汇总 */
h+=`<div class="section"><div class="section-title">【投诉处理】处理实况</div><div class="section-body"><div class="info-row"><b>处理实况:</b>${esc(p.processingDetail)||'无'}</div><div class="info-row"><b>处理资料:</b>${(p.processingFiles||[]).map(f=>f.name).join('、')||'无'}</div></div></div>`;
h+=`<div class="section"><div class="section-title">【投诉分析】原因及措施</div><div class="section-body"><table class="sub-table"><thead><tr><th>序号</th><th>投诉原因</th><th>处理措施</th></tr></thead><tbody>${(a.reasons||[]).map(r=>`<tr><td>${r.seq}</td><td>${esc(r.reason)}</td><td>${esc(r.measure)}</td></tr>`).join('')}</tbody></table></div></div>`;
h+=`<div class="section"><div class="section-title">【投诉确认】确认信息</div><div class="section-body"><div class="info-row"><b>确认内容:</b>${esc(q.confirmContent)||'无'}</div><div class="info-row"><b>确认结果:</b>${esc(q.confirmResult)||'无'}</div></div></div>`;
h+=`<div class="section"><div class="section-title">【投诉受理】投诉及合同钢卷</div><div class="section-body"><div class="info-row"><b>投诉编号:</b>${c?c.id:'-'} <b>情况:</b>${c?esc(c.situation):'-'}</div><div class="info-row"><b>诉求:</b>${c?esc(c.demand):'-'}</div><div class="info-row"><b>合同:</b>${ct?ct.no+' '+esc(ct.name):'无'} <b>金额:</b>${ct?ct.amount.toLocaleString()+'元':'-'}</div></div></div>`;
h+=`<div class="action-bar"><button class="btn btn-danger" onclick="deleteFlp('${f.id}')">删除</button><div style="flex:1;"></div><button class="btn btn-primary" onclick="alert('已保存!')">保存</button></div>`;
document.getElementById('flp-detail').innerHTML=h;
}
/* ===== 共用渲染函数 ===== */
function renderProductItems(items){return`<div class="sub-table-wrap"><div class="sub-table-title">产品明细</div><table class="sub-table"><thead><tr><th>序号</th><th>规格(mm)</th><th>材质</th><th>数量(吨)</th><th>含税单价</th><th>税率除数</th><th>无税单价</th><th>含税总额</th><th>无税总额</th><th>税额</th><th>备注</th></tr></thead><tbody>${items.map(it=>`<tr><td>${it.seq}</td><td>${it.spec}</td><td>${it.material}</td><td>${it.qty}</td><td>${it.priceTax}</td><td>${it.rate}</td><td>${it.priceNoTax.toFixed(2)}</td><td>${it.totalTax.toLocaleString()}</td><td>${it.totalNoTax.toFixed(2)}</td><td>${it.taxAmount.toFixed(2)}</td><td>${it.remark||''}</td></tr>`).join('')}</tbody></table></div>`;}
function renderCoilTable(cl){return`<div class="sub-table-wrap" style="overflow-x:auto;"><div class="sub-table-title">钢卷明细</div><table class="sub-table"><thead><tr><th>入场卷号</th><th>当前卷号</th><th>镀铬卷号</th><th>存储位置</th><th>物料</th><th>规格</th><th>重量(t)</th><th>材质</th><th>厂家</th><th>库区</th><th>创建时间</th><th>品质</th><th>表面处理</th><th>备注</th><th>切边</th><th>包装</th><th>镀层质量</th></tr></thead><tbody>${cl.map(c=>`<tr><td>${c.entryNo}</td><td>${c.currentNo}</td><td>${c.chromeNo}</td><td>${c.location}</td><td>${c.material}</td><td>${c.spec}</td><td>${c.weight}</td><td>${c.texture}</td><td>${c.factory}</td><td>${c.zone}</td><td>${c.createTime}</td><td>${c.quality}</td><td>${c.surface}</td><td>${c.remark||''}</td><td>${c.trim}</td><td>${c.packaging}</td><td>${c.coatingQuality}</td></tr>`).join('')}</tbody></table></div>`;}
/* ===== 合同弹窗 ===== */
function openContractModal(){const c=getCp(curComp);tempContractId=c&&c.contractId?c.contractId:null;renderContractTable('');document.getElementById('modal-contract').classList.add('show');}
function filterContracts(k){renderContractTable(k);}
function renderContractTable(k){const tb=document.querySelector('#contract-table tbody');const f=mockContracts.filter(c=>!k||c.name.includes(k)||c.no.includes(k));tb.innerHTML=f.map(c=>`<tr class="${tempContractId===c.id?'selected':''}" onclick="tempContractId=${c.id};renderContractTable(document.querySelector('#modal-contract input').value)"><td>${c.no}</td><td>${c.name}</td><td>${c.salesman}</td><td>${c.signDate}</td></tr>`).join('');}
function confirmContract(){if(!tempContractId){alert('请先选择合同');return;}const c=getCp(curComp);if(!c)return;c.contractId=tempContractId;c.coilIds=[];closeModal('modal-contract');renderCompDetail();}
function removeContract(){const c=getCp(curComp);if(!c)return;c.contractId=null;c.coilIds=[];renderCompDetail();}
/* ===== 钢卷弹窗 ===== */
function openCoilModal(){tempCoilIds=[...(getCp(curComp)?.coilIds||[])];renderCoilModalTable('');document.getElementById('modal-coil').classList.add('show');}
function filterCoils(k){renderCoilModalTable(k);}
function renderCoilModalTable(k){const tb=document.querySelector('#coil-table tbody');const f=mockCoils.filter(c=>!k||c.entryNo.includes(k)||c.spec.includes(k)||c.texture.includes(k));tb.innerHTML=f.map(c=>`<tr class="${tempCoilIds.includes(c.id)?'selected':''}" onclick="toggleCoil(${c.id});renderCoilModalTable(document.querySelector('#modal-coil input').value)"><td>${c.entryNo}</td><td>${c.spec}</td><td>${c.texture}</td><td>${c.weight}</td><td>${c.factory}</td></tr>`).join('');}
function toggleCoil(id){if(tempCoilIds.includes(id))tempCoilIds=tempCoilIds.filter(i=>i!==id);else tempCoilIds.push(id);}
function confirmCoils(){const c=getCp(curComp);if(!c)return;c.coilIds=[...tempCoilIds];closeModal('modal-coil');renderCompDetail();}
function removeCoil(id){const c=getCp(curComp);if(!c)return;c.coilIds=c.coilIds.filter(i=>i!==id);renderCompDetail();}
/* ===== 数据分析 ===== */
let statsCharts={};
function initStatsPage(){
const now=new Date();
if(!document.getElementById('stats-start').value){
document.getElementById('stats-start').value=now.getFullYear()+'-'+String(now.getMonth()+1).padStart(2,'0')+'-01';
document.getElementById('stats-end').value=now.toISOString().slice(0,10);
}
refreshStats();
}
function resetStatsDate(){
const now=new Date();
document.getElementById('stats-start').value=now.getFullYear()+'-'+String(now.getMonth()+1).padStart(2,'0')+'-01';
document.getElementById('stats-end').value=now.toISOString().slice(0,10);
refreshStats();
}
function getStatsRange(){
const s=document.getElementById('stats-start').value;
const e=document.getElementById('stats-end').value;
return{start:s||'2026-01-01',end:e||today()};
}
function inRange(d,range){return d>=range.start&&d<=range.end;}
function dateAdd(d,days){const r=new Date(d);r.setDate(r.getDate()+days);return r.toISOString().slice(0,10);}
function refreshStats(){
Object.values(statsCharts).forEach(c=>c.destroy());
statsCharts={};
const range=getStatsRange();
const fc=complaints.filter(c=>inRange(c.date,range));
const fcf=confirmations.filter(c=>inRange(c.date,range));
const fan=analyses.filter(c=>inRange(c.date,range));
const fpr=processings.filter(c=>inRange(c.date,range));
const ffl=followups.filter(c=>inRange(c.date,range));
/* 金额关联 */
const complaintAmounts=fc.map(c=>{const ct=c.contractId?getC(c.contractId):null;return ct?ct.amount:0;});
const totalAmount=complaintAmounts.reduce((a,b)=>a+b,0);
const avgAmount=fc.length?totalAmount/fc.length:0;
const salesmen=[...new Set(fc.map(c=>{const ct=c.contractId?getC(c.contractId):null;return ct?ct.salesman:null;}).filter(Boolean))];
/* ===== 一、基本统计 ===== */
document.getElementById('stats-basic').innerHTML=[
{v:salesmen.length,l:'涉及业务员数',c:''},{v:fc.length,l:'投诉数量',c:''},
{v:(totalAmount/10000).toFixed(1)+'万',l:'投诉总金额',c:'sc-red'},{v:(avgAmount/10000).toFixed(1)+'万',l:'平均投诉金额',c:'sc-orange'},
{v:fc.length,l:'受理单新增',c:''},{v:fcf.length,l:'确认单新增',c:''},{v:fan.length,l:'分析单新增',c:''},{v:fpr.length,l:'处理单新增',c:''},{v:ffl.length,l:'回访单新增',c:''},
{v:fc.filter(c=>c.reviewStatus!=='unreviewed').length,l:'受理单已处理',c:'sc-green'},{v:fc.filter(c=>c.reviewStatus==='unreviewed').length,l:'受理单未处理',c:'sc-orange'},
{v:fcf.filter(c=>c.status==='done').length,l:'确认单已处理',c:'sc-green'},{v:fcf.filter(c=>c.status==='undone').length,l:'确认单未处理',c:'sc-orange'},
{v:fan.filter(c=>c.status==='done').length,l:'分析单已处理',c:'sc-green'},{v:fan.filter(c=>c.status==='undone').length,l:'分析单未处理',c:'sc-orange'},
{v:fpr.filter(c=>c.status==='done').length,l:'处理单已处理',c:'sc-green'},{v:fpr.filter(c=>c.status==='undone').length,l:'处理单未处理',c:'sc-orange'},
{v:ffl.filter(c=>c.status==='done').length,l:'回访单已处理',c:'sc-green'},{v:ffl.filter(c=>c.status==='undone').length,l:'回访单未处理',c:'sc-orange'}
].map(c=>`<div class="stat-card"><div class="sc-num ${c.c}">${c.v}</div><div class="sc-label">${c.l}</div></div>`).join('');
/* ===== 二、日趋势分析 ===== */
const dailyMap={};
for(let d=range.start;d<=range.end;d=dateAdd(d,1)){
dailyMap[d]={date:d,cnt:0,sales:new Set(),amt:0,comp:0,cfm:0,anl:0,prc:0,flp:0};
}
fc.forEach(c=>{if(dailyMap[c.date]){dailyMap[c.date].cnt++;dailyMap[c.date].comp++;const ct=c.contractId?getC(c.contractId):null;if(ct){dailyMap[c.date].amt+=ct.amount;dailyMap[c.date].sales.add(ct.salesman);}}});
fcf.forEach(c=>{if(dailyMap[c.date])dailyMap[c.date].cfm++;});
fan.forEach(c=>{if(dailyMap[c.date])dailyMap[c.date].anl++;});
fpr.forEach(c=>{if(dailyMap[c.date])dailyMap[c.date].prc++;});
ffl.forEach(c=>{if(dailyMap[c.date])dailyMap[c.date].flp++;});
let dailyArr=Object.values(dailyMap).sort((a,b)=>a.date.localeCompare(b.date));
const maxDaily=30;if(dailyArr.length>maxDaily)dailyArr=dailyArr.slice(-maxDaily);
const dLabels=dailyArr.map(d=>d.date.slice(5));
/* 日趋势图1投诉量&业务员 */
const ctx1=document.getElementById('chart-daily-count').getContext('2d');
statsCharts.dailyCount=new Chart(ctx1,{type:'line',data:{labels:dLabels,datasets:[
{label:'投诉量',data:dailyArr.map(d=>d.cnt),borderColor:'#000',backgroundColor:'rgba(0,0,0,0.05)',fill:true,tension:0.2,pointRadius:3,pointBackgroundColor:'#000'},
{label:'业务员数',data:dailyArr.map(d=>d.sales.size),borderColor:'#c00',backgroundColor:'rgba(200,0,0,0.05)',fill:true,tension:0.2,pointRadius:3,pointBackgroundColor:'#c00',borderDash:[5,3]}
]},options:{responsive:true,plugins:{legend:{position:'bottom',labels:{boxWidth:20,padding:12,font:{size:11}}}}}});
/* 日趋势图2投诉金额 */
const ctx2=document.getElementById('chart-daily-amount').getContext('2d');
statsCharts.dailyAmount=new Chart(ctx2,{type:'bar',data:{labels:dLabels,datasets:[
{label:'日总金额(万元)',data:dailyArr.map(d=>(d.amt/10000)),backgroundColor:'#333',borderColor:'#000',borderWidth:1},
{label:'日均金额(万元)',data:dailyArr.map(d=>d.cnt?(d.amt/d.cnt/10000):0),backgroundColor:'#999',borderColor:'#666',borderWidth:1}
]},options:{responsive:true,plugins:{legend:{position:'bottom',labels:{boxWidth:20,padding:12,font:{size:11}}}}}});
/* 日趋势图3各环节单据 */
const ctx3=document.getElementById('chart-daily-docs').getContext('2d');
statsCharts.dailyDocs=new Chart(ctx3,{type:'line',data:{labels:dLabels,datasets:[
{label:'受理',data:dailyArr.map(d=>d.comp),borderColor:'#000',tension:0.2,pointRadius:2},
{label:'确认',data:dailyArr.map(d=>d.cfm),borderColor:'#c00',tension:0.2,pointRadius:2},
{label:'分析',data:dailyArr.map(d=>d.anl),borderColor:'#090',tension:0.2,pointRadius:2},
{label:'处理',data:dailyArr.map(d=>d.prc),borderColor:'#06c',tension:0.2,pointRadius:2},
{label:'回访',data:dailyArr.map(d=>d.flp),borderColor:'#c60',tension:0.2,pointRadius:2}
]},options:{responsive:true,plugins:{legend:{position:'bottom',labels:{boxWidth:20,padding:12,font:{size:11}}}}}});
/* ===== 三、月均分析近6个月 ===== */
const now=new Date();
let months=[];
for(let i=5;i>=0;i--){
const m=new Date(now.getFullYear(),now.getMonth()-i,1);
months.push({label:m.getFullYear()+'-'+String(m.getMonth()+1).padStart(2,'0'),ym:m.toISOString().slice(0,7),cnt:0,sales:new Set(),amt:0,comp:0,cfm:0,anl:0,prc:0,flp:0});
}
function ym(d){return d.slice(0,7);}
fc.forEach(c=>{const m=months.find(mo=>mo.ym===ym(c.date));if(m){m.cnt++;m.comp++;const ct=c.contractId?getC(c.contractId):null;if(ct){m.amt+=ct.amount;m.sales.add(ct.salesman);}}});
fcf.forEach(c=>{const m=months.find(mo=>mo.ym===ym(c.date));if(m)m.cfm++;});
fan.forEach(c=>{const m=months.find(mo=>mo.ym===ym(c.date));if(m)m.anl++;});
fpr.forEach(c=>{const m=months.find(mo=>mo.ym===ym(c.date));if(m)m.prc++;});
ffl.forEach(c=>{const m=months.find(mo=>mo.ym===ym(c.date));if(m)m.flp++;});
const mLabels=months.map(m=>m.label);
/* 月图1投诉量&业务员 */
const ctx4=document.getElementById('chart-monthly-count').getContext('2d');
statsCharts.monthlyCount=new Chart(ctx4,{type:'bar',data:{labels:mLabels,datasets:[
{label:'投诉量',data:months.map(m=>m.cnt),backgroundColor:'#333',borderColor:'#000',borderWidth:1},
{label:'业务员数',data:months.map(m=>m.sales.size),backgroundColor:'#c00',borderColor:'#900',borderWidth:1}
]},options:{responsive:true,plugins:{legend:{position:'bottom',labels:{boxWidth:20,padding:12,font:{size:11}}}}}});
/* 月图2投诉金额 */
const ctx5=document.getElementById('chart-monthly-amount').getContext('2d');
statsCharts.monthlyAmount=new Chart(ctx5,{type:'bar',data:{labels:mLabels,datasets:[
{label:'月总金额(万元)',data:months.map(m=>(m.amt/10000)),backgroundColor:'#555',borderColor:'#000',borderWidth:1},
{label:'月均金额(万元)',data:months.map(m=>m.cnt?(m.amt/m.cnt/10000):0),backgroundColor:'#aaa',borderColor:'#666',borderWidth:1}
]},options:{responsive:true,plugins:{legend:{position:'bottom',labels:{boxWidth:20,padding:12,font:{size:11}}}}}});
/* 月图3各环节单据 */
const ctx6=document.getElementById('chart-monthly-docs').getContext('2d');
statsCharts.monthlyDocs=new Chart(ctx6,{type:'bar',data:{labels:mLabels,datasets:[
{label:'受理',data:months.map(m=>m.comp),backgroundColor:'#000'},
{label:'确认',data:months.map(m=>m.cfm),backgroundColor:'#c00'},
{label:'分析',data:months.map(m=>m.anl),backgroundColor:'#090'},
{label:'处理',data:months.map(m=>m.prc),backgroundColor:'#06c'},
{label:'回访',data:months.map(m=>m.flp),backgroundColor:'#c60'}
]},options:{responsive:true,plugins:{legend:{position:'bottom',labels:{boxWidth:20,padding:12,font:{size:11}}}}}});
/* ===== 四、占比分析 ===== */
/* 占比卡片 */
const revApproved=fc.filter(c=>c.reviewStatus==='approved').length;
const revRejected=fc.filter(c=>c.reviewStatus==='rejected').length;
const revUnreviewed=fc.filter(c=>c.reviewStatus==='unreviewed').length;
const totalDocs=fcf.length+fan.length+fpr.length+ffl.length;
const doneDocs=fcf.filter(c=>c.status==='done').length+fan.filter(c=>c.status==='done').length+fpr.filter(c=>c.status==='done').length+ffl.filter(c=>c.status==='done').length;
document.getElementById('stats-proportion-cards').innerHTML=[
{v:revApproved,l:'审核通过',c:'sc-green'},{v:revRejected,l:'审核未通过',c:'sc-red'},
{v:revUnreviewed,l:'待审核',c:'sc-orange'},{v:fc.length?(revApproved/fc.length*100).toFixed(0)+'%':'-',l:'审核通过率',c:''},
{v:doneDocs,l:'下游单据已处理',c:'sc-green'},{v:totalDocs-doneDocs,l:'下游单据未处理',c:'sc-orange'},
{v:totalDocs?(doneDocs/totalDocs*100).toFixed(0)+'%':'-',l:'整体处理率',c:''},{v:salesmen.length,l:'涉及业务员',c:''}
].map(c=>`<div class="stat-card"><div class="sc-num ${c.c}">${c.v}</div><div class="sc-label">${c.l}</div></div>`).join('');
/* 饼图1投诉原因分类基于分析单reasons关键词归类 */
const reasonCats={};
const allReasons=fan.flatMap(a=>a.reasons||[]);
const catRules=[
{cat:'表面质量',kw:['表面','锈蚀','划痕','白斑','氧化','斑点','外观']},
{cat:'尺寸偏差',kw:['厚度','宽度','规格','偏差','超差','不均匀']},
{cat:'镀层质量',kw:['镀层','镀锌','锌层','附着力','涂镀','锌液']},
{cat:'材质性能',kw:['硬度','强度','屈服','力学','材质']},
{cat:'板型缺陷',kw:['板型','波浪','平整度','边部','开裂','裂纹']},
{cat:'包装运输',kw:['包装','运输','破损','进水','碰撞','物流','变形']},
{cat:'工艺缺陷',kw:['氧化皮','酸洗','工艺','辊缝']}
];
allReasons.forEach(r=>{
let matched=false;
for(const cr of catRules){if(cr.kw.some(k=>r.reason.includes(k))){reasonCats[cr.cat]=(reasonCats[cr.cat]||0)+1;matched=true;break;}}
if(!matched)reasonCats['其他']=(reasonCats['其他']||0)+1;
});
if(!allReasons.length){reasonCats['(无分析数据)']=1;}
const ctx7=document.getElementById('chart-reason-pie').getContext('2d');
const pieColors=['#333','#666','#999','#c00','#c60','#090','#06c','#c9c'];
statsCharts.reasonPie=new Chart(ctx7,{type:'doughnut',data:{labels:Object.keys(reasonCats),datasets:[{data:Object.values(reasonCats),backgroundColor:pieColors.slice(0,Object.keys(reasonCats).length),borderColor:'#000',borderWidth:2}]},options:{responsive:true,plugins:{legend:{position:'bottom',labels:{boxWidth:14,padding:10,font:{size:11}}}}}});
/* 饼图2产品类型投诉占比 */
const productCats={};
fc.forEach(c=>{const ct=c.contractId?getC(c.contractId):null;const pn=ct?ct.productName:'未知';productCats[pn]=(productCats[pn]||0)+1;});
if(!Object.keys(productCats).length)productCats['无数据']=1;
const ctx8=document.getElementById('chart-product-pie').getContext('2d');
statsCharts.productPie=new Chart(ctx8,{type:'doughnut',data:{labels:Object.keys(productCats),datasets:[{data:Object.values(productCats),backgroundColor:pieColors.slice(0,Object.keys(productCats).length),borderColor:'#000',borderWidth:2}]},options:{responsive:true,plugins:{legend:{position:'bottom',labels:{boxWidth:14,padding:10,font:{size:11}}}}}});
/* 柱状图3各环节单据处理率 */
const stages=[
{n:'投诉受理',t:fc.length,d:fc.filter(c=>c.reviewStatus!=='unreviewed').length},
{n:'投诉确认',t:fcf.length,d:fcf.filter(c=>c.status==='done').length},
{n:'投诉分析',t:fan.length,d:fan.filter(c=>c.status==='done').length},
{n:'投诉处理',t:fpr.length,d:fpr.filter(c=>c.status==='done').length},
{n:'回访确认',t:ffl.length,d:ffl.filter(c=>c.status==='done').length}
];
const ctx9=document.getElementById('chart-stage-rate').getContext('2d');
statsCharts.stageRate=new Chart(ctx9,{type:'bar',data:{labels:stages.map(s=>s.n),datasets:[
{label:'总数',data:stages.map(s=>s.t),backgroundColor:'#999',borderColor:'#000',borderWidth:1},
{label:'已处理',data:stages.map(s=>s.d),backgroundColor:'#090',borderColor:'#060',borderWidth:1}
]},options:{responsive:true,plugins:{legend:{position:'bottom',labels:{boxWidth:14,padding:10,font:{size:11}}}}}});
/* 柱状图4业务员投诉分布 */
const smMap={}; fc.forEach(c=>{const ct=c.contractId?getC(c.contractId):null;const sm=ct?ct.salesman:'未知';if(!smMap[sm])smMap[sm]={cnt:0,amt:0};smMap[sm].cnt++;smMap[sm].amt+=ct?ct.amount:0;});
const smEntries=Object.entries(smMap).sort((a,b)=>b[1].cnt-a[1].cnt);
const ctx10=document.getElementById('chart-salesman-bar').getContext('2d');
statsCharts.salesmanBar=new Chart(ctx10,{type:'bar',data:{labels:smEntries.map(e=>e[0]),datasets:[
{label:'投诉数',data:smEntries.map(e=>e[1].cnt),backgroundColor:'#333',borderColor:'#000',borderWidth:1},
{label:'金额(万元)',data:smEntries.map(e=>(e[1].amt/10000)),backgroundColor:'#c00',borderColor:'#900',borderWidth:1}
]},options:{responsive:true,plugins:{legend:{position:'bottom',labels:{boxWidth:14,padding:10,font:{size:11}}}}}});
/* 审核通过率环形图 */
const ctx11=document.getElementById('chart-review-rate').getContext('2d');
statsCharts.reviewRate=new Chart(ctx11,{type:'doughnut',data:{labels:['已通过','未通过','待审核'],datasets:[{data:[revApproved,revRejected,revUnreviewed],backgroundColor:['#090','#c00','#c60'],borderColor:'#000',borderWidth:2}]},options:{responsive:true,plugins:{legend:{position:'bottom',labels:{boxWidth:14,padding:10,font:{size:11}}}}}});
}
/* ===== 初始化 ===== */
renderCompList();
</script>
</body>
</html>