v1.0.27: 碎片处理中心(完整方案) - 完整度计算引擎(四表分层/阈值)+is_incomplete标记(写操作挂钩)+api/fragment八接口(统计/硬碎片列表详情转换废弃/软碎片列表详情补全)+fragment.html前端(指标卡+硬软碎片双区块)
This commit is contained in:
@@ -0,0 +1,346 @@
|
||||
/**
|
||||
* fragment.js - 碎片处理中心(v1.0.27)
|
||||
* 布局:顶部指标卡 + 【硬碎片】独立区块 + 【软碎片】独立区块(不用 Tab)
|
||||
* 硬碎片:preliminary_data 中未终结记录(完整度/类型/内容摘要/来源/录入人/时间/状态/操作)
|
||||
* 软碎片:四张主表 is_incomplete=1 记录(完整度/缺失层级标签/名称/缺失字段/所属表/时间/操作)
|
||||
*/
|
||||
$(function () {
|
||||
renderShell('碎片处理中心', '数据管理 / 碎片处理');
|
||||
renderPage();
|
||||
initPage('preliminary', function (user) {
|
||||
if (!checkPagePermission('preliminary')) return;
|
||||
loadStats();
|
||||
loadHardList(1);
|
||||
loadSoftList(1);
|
||||
loadDicts();
|
||||
});
|
||||
|
||||
var hardPage = 1, softPage = 1;
|
||||
var hardRows = [], softRows = [];
|
||||
|
||||
/** 缺失层级标签样式 */
|
||||
function levelTagHtml(level) {
|
||||
if (level === 'core') return '<span class="tag tag-red">缺核心</span>';
|
||||
if (level === 'important') return '<span class="tag tag-orange">缺重要</span>';
|
||||
if (level === 'supplementary') return '<span class="tag tag-blue">待补充</span>';
|
||||
return '<span class="tag tag-green">完整</span>';
|
||||
}
|
||||
|
||||
/** 完整度进度条 + 百分比 */
|
||||
function completenessHtml(score) {
|
||||
var color = score >= 80 ? '#2f8f46' : (score >= 65 ? '#e6a23c' : '#d9534f');
|
||||
return '<div style="display:flex;align-items:center;gap:6px;min-width:90px;">' +
|
||||
'<div style="flex:1;height:6px;background:#eef0f4;border-radius:3px;overflow:hidden;">' +
|
||||
'<div style="height:100%;width:' + score + '%;background:' + color + ';border-radius:3px;"></div></div>' +
|
||||
'<span style="font-size:12px;color:' + color + ';white-space:nowrap;">' + score + '%</span></div>';
|
||||
}
|
||||
|
||||
/** 渲染页面内容:指标卡 + 硬碎片 + 软碎片 两个独立区块 */
|
||||
function renderPage() {
|
||||
$('#page-content').html(
|
||||
'<div class="stat-cards" id="stat-cards"></div>' +
|
||||
// ================= 硬碎片 =================
|
||||
'<div class="card" style="margin-top:14px;">' +
|
||||
' <div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:10px;">' +
|
||||
' <b style="font-size:15px;color:#1e2a3a;">【硬碎片】</b>' +
|
||||
' <span style="font-size:12px;color:#8a94a6;">关键字段缺失、未入主表的原始线索(不含已转换/已废弃)</span>' +
|
||||
' </div>' +
|
||||
' <div class="toolbar">' +
|
||||
' <select id="hard-type"><option value="">全部类型</option>' +
|
||||
' <option value="company">企业</option><option value="person">人员</option>' +
|
||||
' <option value="product">产品</option><option value="need">需求</option><option value="mixed">混合</option></select>' +
|
||||
' <select id="hard-source"><option value="">全部来源</option></select>' +
|
||||
' <select id="hard-status"><option value="">全部状态</option>' +
|
||||
' <option value="待处理">待处理</option><option value="待分配">待分配</option>' +
|
||||
' <option value="处理中">处理中</option><option value="已拒收">已拒收</option></select>' +
|
||||
' <input type="text" id="hard-keyword" placeholder="公司/姓名/电话/邮箱/描述关键词">' +
|
||||
' <button class="btn btn-primary" id="hard-search">搜索</button>' +
|
||||
' <button class="btn" id="hard-reset">重置</button>' +
|
||||
' </div>' +
|
||||
' <div class="table-wrap"><table class="grid">' +
|
||||
' <thead><tr><th>完整度</th><th>类型</th><th>内容摘要</th><th>来源</th><th>录入人</th><th>录入时间</th><th>状态</th><th>操作</th></tr></thead>' +
|
||||
' <tbody id="hard-tbody"></tbody>' +
|
||||
' </table></div>' +
|
||||
' <div class="pagination" id="hard-pagination"></div>' +
|
||||
'</div>' +
|
||||
// ================= 软碎片 =================
|
||||
'<div class="card" style="margin-top:14px;">' +
|
||||
' <div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:10px;">' +
|
||||
' <b style="font-size:15px;color:#1e2a3a;">【软碎片】</b>' +
|
||||
' <span style="font-size:12px;color:#8a94a6;">已入主表但完整度低于阈值的记录(is_incomplete=1)</span>' +
|
||||
' </div>' +
|
||||
' <div class="toolbar">' +
|
||||
' <select id="soft-table"><option value="">全部所属表</option>' +
|
||||
' <option value="persons">人员</option><option value="companies">企业</option>' +
|
||||
' <option value="social_accounts">联系方式</option><option value="media">媒体</option></select>' +
|
||||
' <select id="soft-level"><option value="">全部缺失层级</option>' +
|
||||
' <option value="core">缺核心</option><option value="important">缺重要</option>' +
|
||||
' <option value="supplementary">待补充</option></select>' +
|
||||
' <input type="text" id="soft-keyword" placeholder="名称关键词">' +
|
||||
' <button class="btn btn-primary" id="soft-search">搜索</button>' +
|
||||
' <button class="btn" id="soft-reset">重置</button>' +
|
||||
' </div>' +
|
||||
' <div class="table-wrap"><table class="grid">' +
|
||||
' <thead><tr><th>完整度</th><th>缺失层级</th><th>名称</th><th>缺失字段</th><th>所属表</th><th>录入时间</th><th>操作</th></tr></thead>' +
|
||||
' <tbody id="soft-tbody"></tbody>' +
|
||||
' </table></div>' +
|
||||
' <div class="pagination" id="soft-pagination"></div>' +
|
||||
'</div>'
|
||||
);
|
||||
|
||||
$('#hard-search').on('click', function () { loadHardList(1); });
|
||||
$('#hard-keyword').on('keydown', function (e) { if (e.keyCode === 13) loadHardList(1); });
|
||||
$('#hard-reset').on('click', function () {
|
||||
$('#hard-type').val(''); $('#hard-source').val(''); $('#hard-status').val(''); $('#hard-keyword').val('');
|
||||
loadHardList(1);
|
||||
});
|
||||
$('#soft-search').on('click', function () { loadSoftList(1); });
|
||||
$('#soft-keyword').on('keydown', function (e) { if (e.keyCode === 13) loadSoftList(1); });
|
||||
$('#soft-reset').on('click', function () {
|
||||
$('#soft-table').val(''); $('#soft-level').val(''); $('#soft-keyword').val('');
|
||||
loadSoftList(1);
|
||||
});
|
||||
}
|
||||
|
||||
/** 来源渠道下拉选项 */
|
||||
function loadDicts() {
|
||||
httpGet('common/dicts.php', { type: 'all' }).then(function (d) {
|
||||
var html = '<option value="">全部来源</option>';
|
||||
(d.source_channels || []).forEach(function (c) {
|
||||
html += '<option value="' + escHtml(c.name) + '">' + escHtml(c.name) + '</option>';
|
||||
});
|
||||
$('#hard-source').html(html);
|
||||
}).catch(function () {});
|
||||
}
|
||||
|
||||
/* ================= 顶部指标卡 ================= */
|
||||
function loadStats() {
|
||||
httpGet('fragment/stats_overview.php').then(function (d) {
|
||||
$('#stat-cards').html(
|
||||
'<div class="stat-card c-blue"><div class="num">' + d.hard_total + '</div><div class="label">硬碎片总数</div></div>' +
|
||||
'<div class="stat-card c-teal"><div class="num">' + d.soft_total + '</div><div class="label">软碎片总数</div></div>' +
|
||||
'<div class="stat-card c-green"><div class="num">' + d.month_new + '</div><div class="label">本月新增</div></div>' +
|
||||
'<div class="stat-card c-orange"><div class="num">' + d.month_processed + '</div><div class="label">本月处理</div></div>' +
|
||||
'<div class="stat-card c-red"><div class="num">' + d.pending + '</div><div class="label">待处理碎片</div></div>'
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/* ================= 硬碎片列表 ================= */
|
||||
function loadHardList(page) {
|
||||
hardPage = page;
|
||||
var params = {
|
||||
page: page,
|
||||
source_type: $('#hard-type').val(),
|
||||
source_channel: $('#hard-source').val(),
|
||||
status: $('#hard-status').val(),
|
||||
keyword: $.trim($('#hard-keyword').val())
|
||||
};
|
||||
httpGet('fragment/hard_list.php', params).then(function (d) {
|
||||
hardRows = d.list || [];
|
||||
var html = '';
|
||||
hardRows.forEach(function (r) {
|
||||
html += '<tr>' +
|
||||
'<td>' + completenessHtml(r.completeness) + '</td>' +
|
||||
'<td>' + escHtml(r.source_type_label) + '</td>' +
|
||||
'<td title="' + escHtml(r.summary) + '" style="max-width:280px;">' + escHtml(r.summary || '-') + '</td>' +
|
||||
'<td>' + escHtml(r.source_channel || '-') + '</td>' +
|
||||
'<td>' + escHtml(r.recorded_by || '-') + '</td>' +
|
||||
'<td>' + fmtDate(r.created_at) + '</td>' +
|
||||
'<td>' + escHtml(r.status || '-') + '</td>' +
|
||||
'<td class="ops">' +
|
||||
' <a onclick="viewHard(' + r.id + ')">查看</a>' +
|
||||
' <a onclick="convertHard(' + r.id + ')">补全录入</a>' +
|
||||
' <a onclick="discardHard(' + r.id + ')">废弃</a>' +
|
||||
'</td></tr>';
|
||||
});
|
||||
if (!hardRows.length) {
|
||||
html = '<tr><td colspan="8" class="empty-tip" style="padding:30px 0;">暂无硬碎片</td></tr>';
|
||||
}
|
||||
$('#hard-tbody').html(html);
|
||||
renderPagination($('#hard-pagination'), d, loadHardList);
|
||||
});
|
||||
}
|
||||
|
||||
/* ================= 软碎片列表 ================= */
|
||||
function loadSoftList(page) {
|
||||
softPage = page;
|
||||
var params = {
|
||||
page: page,
|
||||
table: $('#soft-table').val(),
|
||||
level: $('#soft-level').val(),
|
||||
keyword: $.trim($('#soft-keyword').val())
|
||||
};
|
||||
httpGet('fragment/soft_list.php', params).then(function (d) {
|
||||
softRows = d.list || [];
|
||||
var html = '';
|
||||
softRows.forEach(function (r) {
|
||||
var missing = (r.missing_fields || []).join('、') || '-';
|
||||
html += '<tr>' +
|
||||
'<td>' + completenessHtml(r.score) + '</td>' +
|
||||
'<td>' + levelTagHtml(r.level) + '</td>' +
|
||||
'<td>' + escHtml(r.name) + '</td>' +
|
||||
'<td style="max-width:260px;">' + escHtml(missing) + '</td>' +
|
||||
'<td>' + escHtml(r.table_label) + '</td>' +
|
||||
'<td>' + fmtDate(r.created_at) + '</td>' +
|
||||
'<td class="ops"><a onclick="completeSoft(\'' + r.table + '\',' + r.id + ')">补全</a></td></tr>';
|
||||
});
|
||||
if (!softRows.length) {
|
||||
html = '<tr><td colspan="7" class="empty-tip" style="padding:30px 0;">暂无软碎片</td></tr>';
|
||||
}
|
||||
$('#soft-tbody').html(html);
|
||||
renderPagination($('#soft-pagination'), d, loadSoftList);
|
||||
});
|
||||
}
|
||||
|
||||
/* ================= 硬碎片:查看 ================= */
|
||||
window.viewHard = function (id) {
|
||||
httpGet('fragment/hard_detail.php', { id: id }).then(function (d) {
|
||||
var missing = [].concat(d.missing.core || [], d.missing.important || [], d.missing.supplementary || []);
|
||||
var rowHtml = '';
|
||||
var fields = [
|
||||
['类型', d.source_type_label], ['公司名称', d.company_name], ['联系人', d.person_name],
|
||||
['联系电话', d.contact_phone], ['联系邮箱', d.contact_email], ['意向品类', d.target_product_category],
|
||||
['来源渠道', d.source_channel], ['录入人', d.recorded_by], ['状态', d.status],
|
||||
['录入时间', d.created_at], ['处理时间', d.processed_at]
|
||||
];
|
||||
fields.forEach(function (f) {
|
||||
rowHtml += '<div style="display:flex;margin:6px 0;"><span style="width:80px;color:#8a94a6;">' + f[0] + '</span>' +
|
||||
'<span>' + escHtml(f[1] || '-') + '</span></div>';
|
||||
});
|
||||
if (d.description) {
|
||||
rowHtml += '<div style="margin:6px 0;"><span style="color:#8a94a6;">原始描述</span><div style="margin-top:4px;padding:8px;background:#f7f8fa;border-radius:4px;font-size:12px;max-height:120px;overflow:auto;">' + escHtml(d.description) + '</div></div>';
|
||||
}
|
||||
Dialog.open({
|
||||
title: '硬碎片详情(完整度 ' + d.completeness + '%)',
|
||||
area: ['560px', 'auto'],
|
||||
content: '<div style="padding:16px 22px;">' + rowHtml + '</div>',
|
||||
btn: ['关闭']
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/* ================= 硬碎片:补全录入(转换) ================= */
|
||||
window.convertHard = function (id) {
|
||||
httpGet('fragment/hard_detail.php', { id: id }).then(function (d) {
|
||||
var typeSel = '<select id="cv-type">' +
|
||||
'<option value="company"' + (d.source_type === 'company' || d.source_type === 'mixed' ? ' selected' : '') + '>企业</option>' +
|
||||
'<option value="person"' + (d.source_type === 'person' ? ' selected' : '') + '>人员</option>' +
|
||||
'<option value="product"' + (d.source_type === 'product' ? ' selected' : '') + '>产品</option>' +
|
||||
'<option value="need"' + (d.source_type === 'need' ? ' selected' : '') + '>需求</option></select>';
|
||||
var content =
|
||||
'<form id="cv-form" style="padding:16px 22px 4px;">' +
|
||||
'<div class="form-grid">' +
|
||||
' <div class="form-item"><label>转换目标<span class="req">*</span></label>' + typeSel + '</div>' +
|
||||
' <div class="form-item" id="cv-f-company_name"><label>公司名称</label><input type="text" name="company_name" value="' + escHtml(d.company_name || '') + '"></div>' +
|
||||
' <div class="form-item" id="cv-f-person_name"><label>联系人</label><input type="text" name="person_name" value="' + escHtml(d.person_name || '') + '"></div>' +
|
||||
' <div class="form-item"><label>联系电话</label><input type="text" name="contact_phone" value="' + escHtml(d.contact_phone || '') + '"></div>' +
|
||||
' <div class="form-item"><label>联系邮箱</label><input type="text" name="contact_email" value="' + escHtml(d.contact_email || '') + '"></div>' +
|
||||
' <div class="form-item" id="cv-f-category"><label>产品品类</label><input type="text" name="target_product_category" value="' + escHtml(d.target_product_category || '') + '"></div>' +
|
||||
' <div class="form-item" id="cv-f-industry"><label>行业</label><input type="text" name="industry" placeholder="转换为企业时填写"></div>' +
|
||||
' <div class="form-item" id="cv-f-needcat"><label>需求类别</label><input type="text" name="need_category" placeholder="如:设备采购"></div>' +
|
||||
' <div class="form-item" id="cv-f-scenario"><label>应用场景</label><input type="text" name="application_scenario" placeholder="如:码垛"></div>' +
|
||||
' <div class="form-item full"><label>描述/补充说明</label><textarea name="description" style="height:60px;">' + escHtml(d.description || '') + '</textarea></div>' +
|
||||
'</div>' +
|
||||
'<div class="dialog-footer"><button type="button" class="btn" id="cv-cancel">取消</button>' +
|
||||
'<button type="submit" class="btn btn-primary">确认转换</button></div>' +
|
||||
'</form>';
|
||||
|
||||
var idx = Dialog.open({
|
||||
title: '硬碎片补全录入',
|
||||
area: ['640px', 'auto'],
|
||||
content: content,
|
||||
btn: false
|
||||
});
|
||||
|
||||
function syncFields() {
|
||||
var t = $('#cv-type').val();
|
||||
$('#cv-f-company_name').toggle(t !== 'person');
|
||||
$('#cv-f-person_name').toggle(t === 'person');
|
||||
$('#cv-f-category').toggle(t === 'product' || t === 'need');
|
||||
$('#cv-f-industry').toggle(t === 'company');
|
||||
$('#cv-f-needcat').toggle(t === 'need');
|
||||
$('#cv-f-scenario').toggle(t === 'need');
|
||||
}
|
||||
$('#cv-type').on('change', syncFields);
|
||||
syncFields();
|
||||
$('#cv-cancel').on('click', function () { Dialog.close(idx); });
|
||||
|
||||
$('#cv-form').on('submit', function (e) {
|
||||
e.preventDefault();
|
||||
var fd = {};
|
||||
$(this).find('input, select, textarea').each(function () {
|
||||
var name = $(this).attr('name');
|
||||
if (name) fd[name] = $(this).val();
|
||||
});
|
||||
fd.id = id;
|
||||
fd.target_type = $('#cv-type').val();
|
||||
httpPost('fragment/hard_convert.php', fd).then(function () {
|
||||
Dialog.success('转换成功', function () {
|
||||
Dialog.close(idx);
|
||||
loadStats();
|
||||
loadHardList(hardPage);
|
||||
loadSoftList(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/* ================= 硬碎片:废弃 ================= */
|
||||
window.discardHard = function (id) {
|
||||
Dialog.confirm('确定废弃该碎片吗?废弃后不可恢复。', function () {
|
||||
httpPost('fragment/hard_discard.php', { id: id }).then(function () {
|
||||
Dialog.success('已废弃', function () {
|
||||
loadStats();
|
||||
loadHardList(hardPage);
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/* ================= 软碎片:补全 ================= */
|
||||
window.completeSoft = function (table, id) {
|
||||
httpGet('fragment/soft_detail.php', { table: table, id: id }).then(function (d) {
|
||||
var allMissing = [].concat(d.missing_core || [], d.missing_important || [], d.missing_supp || []);
|
||||
var inputs = '';
|
||||
allMissing.forEach(function (m) {
|
||||
var isDate = (m.key === 'established_date');
|
||||
inputs += '<div class="form-item"><label>' + escHtml(m.label) + (m.key === 'full_name' || m.key === 'display_name' ? '<span class="req">*</span>' : '') + '</label>' +
|
||||
'<input type="' + (isDate ? 'date' : 'text') + '" name="' + m.key + '"></div>';
|
||||
});
|
||||
var content =
|
||||
'<form id="soft-form" style="padding:16px 22px 4px;">' +
|
||||
'<div style="font-size:12px;color:#8a94a6;margin-bottom:10px;">当前完整度 <b style="color:#d9534f;">' + d.score + '%</b>(阈值 ' + d.threshold + '%),补全后自动重新计算。</div>' +
|
||||
'<div class="form-grid">' + inputs + '</div>' +
|
||||
'<div class="dialog-footer"><button type="button" class="btn" id="soft-cancel">取消</button>' +
|
||||
'<button type="submit" class="btn btn-primary">保存补全</button></div>' +
|
||||
'</form>';
|
||||
|
||||
var idx = Dialog.open({
|
||||
title: '软碎片补全 - ' + escHtml(d.name) + '(' + d.table_label + ')',
|
||||
area: ['640px', 'auto'],
|
||||
content: content,
|
||||
btn: false
|
||||
});
|
||||
$('#soft-cancel').on('click', function () { Dialog.close(idx); });
|
||||
|
||||
$('#soft-form').on('submit', function (e) {
|
||||
e.preventDefault();
|
||||
var fd = { table: table, id: id };
|
||||
$(this).find('input').each(function () {
|
||||
var name = $(this).attr('name');
|
||||
if (name) fd[name] = $(this).val();
|
||||
});
|
||||
httpPost('fragment/soft_update.php', fd).then(function (res) {
|
||||
Dialog.success(res && res.is_complete ? '补全完成' : '已保存(完整度 ' + (res && res.score) + '%)', function () {
|
||||
Dialog.close(idx);
|
||||
loadStats();
|
||||
loadSoftList(softPage);
|
||||
loadHardList(hardPage);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
});
|
||||
Reference in New Issue
Block a user