/** * channel.js - 渠道管理逻辑(v1.0.18 重构) * 目标:记录企业/人员数据的来源渠道,并根据以往数据有计划地拓展高效渠道 * 两部分: * 1) 渠道效能分析:企业(左)/人员(右)两个板块,右上角年份下拉(默认全部) * - 按「渠道」字段饼状图按百分比排序;鼠标悬停饼图区域显示该渠道 source_detail TOP10 表格 * - 表格字段:来源(只显示前 8 字,超出用…)、数量;默认显示渠道第一名 * 2) 渠道新增计划:列表(渠道类别/来源详情/所属行业/时间窗口/剩余天数/备注说明/状态反馈) */ $(function () { renderShell('渠道管理', '业务模块 / 渠道管理'); renderPage(); initPage('channel', function (user) { if (!checkPagePermission('channel')) return; loadAnalysis(); loadPlans(1); }); var currentPage = 1; var pageRows = []; var analysisData = null; var charts = { company: null, person: null }; /** 渲染页面内容:渠道效能分析(左右两板块)+ 渠道新增计划 */ function renderPage() { $('#page-content').html( '
' + '
' + ' 渠道效能分析' + ' 按「渠道」字段统计,鼠标悬停饼图查看该渠道来源详情 TOP10' + '
' + '
' + '
' + '
企业数据' + '
' + '
' + '
' + '
渠道明细
' + ' ' + '
来源数量
' + '
' + '
' + '
' + '
人员数据' + '
' + '
' + '
' + '
渠道明细
' + ' ' + '
来源数量
' + '
' + '
' + '
' + '
' + '
' + '
' + ' 渠道新增计划' + ' 根据历史数据有计划地拓展高效渠道' + '
' + '
' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + '
' + '
' + ' ' + ' ' + '
ID渠道类别来源详情所属行业时间窗口剩余天数备注说明状态反馈费用(元)创建时间
' + ' ' + '
' ); } /* ============================================================ * 1) 渠道效能分析 * ============================================================ */ function loadAnalysis() { var year = null; var $c = $('#ana-year-company'); var $p = $('#ana-year-person'); if ($c.length && $p.length) { var yc = $c.val(); var yp = $p.val(); year = (yc === yp) ? yc : ''; // 两板块年份不同时,分别请求 } if (year !== null && year === '' && $c.length && $p.length && $c.val() !== $p.val()) { // 两板块各自请求 httpGet('channel/analysis.php', { year: $c.val() || '' }).then(function (d) { analysisData = d; renderAnalysis(d, 'company'); fillYears(d, 'company'); }); httpGet('channel/analysis.php', { year: $p.val() || '' }).then(function (d) { renderAnalysis(d, 'person'); fillYears(d, 'person'); }); return; } httpGet('channel/analysis.php', { year: year || '' }).then(function (d) { analysisData = d; fillYears(d, 'company'); fillYears(d, 'person'); renderAnalysis(d, 'company'); renderAnalysis(d, 'person'); }); } function fillYears(d, side) { var $sel = side === 'company' ? $('#ana-year-company') : $('#ana-year-person'); var cur = $sel.val(); var html = ''; (d.years || []).forEach(function (y) { html += ''; }); $sel.html(html); } function renderAnalysis(d, side) { var rows = side === 'company' ? (d.company || []) : (d.person || []); var chartId = side === 'company' ? 'ana-chart-company' : 'ana-chart-person'; var detailBodyId = side === 'company' ? 'ana-detail-c' : 'ana-detail-p'; var detailTitleId = side === 'company' ? 'ana-detail-title-c' : 'ana-detail-title-p'; // 饼图数据(按百分比排序) var pieData = rows.map(function (r) { return { name: r.channel, value: r.count, percent: r.percent }; }).sort(function (a, b) { return b.value - a.value; }); if (!charts[side]) { charts[side] = echarts.init(document.getElementById(chartId)); } var chart = charts[side]; chart.clear(); chart.setOption({ tooltip: { trigger: 'item', formatter: function (p) { if (p.data && p.data.percent !== undefined) { return p.data.name + '
数量:' + p.data.value + '(' + p.data.percent + '%)'; } return p.name + '
数量:' + p.value; } }, legend: { bottom: 0, type: 'scroll', textStyle: { fontSize: 11 } }, series: [{ type: 'pie', radius: ['38%', '62%'], center: ['50%', '44%'], data: pieData, label: { show: true, formatter: '{b}\n{d}%', fontSize: 11 }, emphasis: { itemStyle: { shadowBlur: 8, shadowColor: 'rgba(0,0,0,0.2)' } } }] }); // 默认显示第一名渠道的 source_detail TOP10 function renderDetail(index) { var r = rows[index]; if (!r) { $('#' + detailBodyId).html('暂无数据'); return; } $('#' + detailTitleId).text(r.channel + ' 来源明细(TOP' + Math.min(10, (r.details || []).length) + ',共 ' + r.count + ' 条)'); var html = ''; (r.details || []).forEach(function (item) { var src = item.source || ''; html += '' + escHtml(src) + '' + (item.count !== undefined ? item.count : item.cnt) + ''; }); if (!(r.details || []).length) { html = '暂无明细'; } $('#' + detailBodyId).html(html); } renderDetail(0); // 悬停饼图区域 -> 显示该渠道 TOP10 表格 chart.off('mouseover'); chart.on('mouseover', function (p) { if (p.dataIndex !== undefined) { renderDetail(p.dataIndex); } }); chart.off('mouseout'); chart.on('mouseout', function () { renderDetail(0); }); // 空数据提示 if (!rows.length) { $('#' + detailBodyId).html('暂无渠道数据(企业/人员需填写来源渠道)'); } } $('#ana-year-company, #ana-year-person').on('change', function () { loadAnalysis(); }); /* ============================================================ * 2) 渠道新增计划 * ============================================================ */ function loadPlans(page) { currentPage = page; var params = buildFilterParam(); params.page = page; params.limit = PAGE_SIZE; httpGet('channel/plan_list.php', params).then(function (data) { var rows = data.list || []; pageRows = rows; var html = ''; rows.forEach(function (r) { var win = r.start_date && r.end_date ? fmtWin(r.start_date) + ' 至 ' + fmtWin(r.end_date) : (r.start_date || r.end_date || '-'); var statusSel = ''; var costInput = ''; html += '' + '' + r.id + '' + '' + escHtml(r.channel_type) + '' + '' + escHtml((r.source_detail || '-').substring(0, 16)) + '' + '' + escHtml(r.industry || '-') + '' + '' + escHtml(win) + '' + '' + (r.remaining_days > 0 ? '' + r.remaining_days + ' 天' : '已到期') + '' + '' + escHtml((r.remark || '-').substring(0, 20)) + '' + '' + statusSel + '' + '' + costInput + '' + '' + fmtDate(r.created_at) + ''; }); if (!rows.length) { html = '暂无渠道新增计划'; } $('#plan-tbody').html(html); bindPlanInlineEdit(); renderPagination($('#plan-pagination'), data, loadPlans); }); } /** 行内实时编辑:状态下拉随时改;费用(元)仅在「已执行」时激活可输入 */ function bindPlanInlineEdit() { $('.plan-status').off('change.plan').on('change.plan', function () { var id = $(this).data('id'); var status = $(this).val(); var $row = $(this).closest('tr'); var $cost = $row.find('.plan-cost'); if (status === '已执行') { $cost.prop('disabled', false).css({ background: '#fff', color: '#1e2a3a' }); } else { $cost.prop('disabled', true).css({ background: '#f2f4f8', color: '#8a94a6' }); } httpPost('channel/plan_save.php', { id: id, status: status }).then(function () { Dialog.toast('状态已更新为「' + status + '」'); }).catch(function () { loadPlans(currentPage); }); }); $('.plan-cost').off('change.plan').on('change.plan', function () { var id = $(this).data('id'); var val = $.trim($(this).val()); if (val === '') return; if (isNaN(parseFloat(val))) { Dialog.error('费用(元)请输入数字'); return; } var cost = Math.round(parseFloat(val) * 100) / 100; $(this).val(cost); httpPost('channel/plan_save.php', { id: id, cost: cost }).then(function () { Dialog.toast('费用已更新:¥' + cost.toFixed(2)); }).catch(function () { loadPlans(currentPage); }); }); } /** 时间窗口显示格式:2025-4-1 */ function fmtWin(d) { if (!d) return ''; var parts = String(d).split('-'); if (parts.length === 3) { return parseInt(parts[0], 10) + '-' + parseInt(parts[1], 10) + '-' + parseInt(parts[2], 10); } return d; } function statusTag(s) { if (s === '已执行') return '已执行'; if (s === '错过') return '错过'; return '待启动'; } $('#btn-plan-search').on('click', function () { loadPlans(1); }); $('#btn-plan-reset').on('click', function () { $('.toolbar [data-filter]').val(''); loadPlans(1); }); /* ---------- 新增/编辑计划 ---------- */ $('#btn-plan-add').on('click', function () { openPlanForm(null); }); window.editPlan = function (id) { var row = null; pageRows.forEach(function (r) { if (r.id == id) row = r; }); openPlanForm(row); }; function openPlanForm(pl) { var isEdit = !!pl; var r = pl || {}; // 渠道类别下拉(来自 source_channels 字典) var chHtml = ''; var hasCur = false; if (window._channelTypes && window._channelTypes.length) { window._channelTypes.forEach(function (c) { var sel = (c === r.channel_type) ? ' selected' : ''; if (sel) hasCur = true; chHtml += ''; }); } if (r.channel_type && !hasCur) { chHtml += ''; } var content = '
' + '
' + '
' + '
' + '
' + '
' + '
' + '
' + '
' + '
' + '
' + '' + '
'; var idx = Dialog.open({ title: isEdit ? '编辑计划' : '新增计划', area: ['680px', 'auto'], content: content, btn: false }); $('#plan-form-cancel').on('click', function () { Dialog.close(idx); }); // 状态反馈 → 费用(元) 联动:仅「已执行」可填写 $('#plan-form select[name="status"]').on('change', function () { var enabled = $(this).val() === '已执行'; var $cost = $('#plan-form input[name="cost"]'); $cost.prop('disabled', !enabled); $cost.css({ background: enabled ? '#fff' : '#f2f4f8', color: enabled ? '#1e2a3a' : '#8a94a6' }); if (!enabled) $cost.val(''); }); $('#plan-form').on('submit', function (e) { e.preventDefault(); var data = {}; $(this).find('input, select').each(function () { var name = $(this).attr('name'); if (!name) return; data[name] = $(this).val(); }); if (!data.channel_type) { Dialog.error('渠道类别为必填项'); return; } if (data.start_date && data.end_date && data.start_date > data.end_date) { Dialog.error('开始日期不能晚于结束日期'); return; } if (isEdit) data.id = r.id; httpPost('channel/plan_save.php', data).then(function () { Dialog.success('保存成功', function () { Dialog.close(idx); loadPlans(currentPage); }); }); }); } window.delPlan = function (id) { Dialog.confirm('确定删除该计划吗?', function () { httpPost('channel/plan_delete.php', { id: id }).then(function () { Dialog.success('删除成功', function () { loadPlans(currentPage); }); }); }); }; /* ---------- 初始化渠道类别下拉(新增计划用 + 列表筛选) ---------- */ httpGet('common/dicts.php', { type: 'source_channel' }).then(function (d) { var types = (d.source_channels || []).map(function (s) { return s.name; }); window._channelTypes = types; var html = ''; types.forEach(function (t) { html += ''; }); $('.toolbar select[data-filter="channel_type"]').html(html); }).catch(function () {}); });