649 lines
84 KiB
HTML
649 lines
84 KiB
HTML
<!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')">×</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')">×</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')">×</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')">×</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')">×</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')">×</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')">×</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,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"');}
|
||
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()">×</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})">×</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})">×</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})">×</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> |