/**
* 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 () {});
});