460 lines
28 KiB
JavaScript
460 lines
28 KiB
JavaScript
/**
|
||
* 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 === 'optional') 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>' +
|
||
' <input type="text" id="hard-keyword" placeholder="公司/姓名/电话/邮箱关键词">' +
|
||
' <button class="btn btn-primary" id="hard-search">搜索</button>' +
|
||
' <button class="btn" id="hard-reset">重置</button>' +
|
||
' <span class="spacer"></span>' +
|
||
' <button class="btn btn-success" id="hard-add">+ 新增碎片</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="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="optional">待补充</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><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-keyword').val('');
|
||
loadHardList(1);
|
||
});
|
||
$('#hard-add').on('click', function () { openHardAdd(); });
|
||
$('#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 + '/' + 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-teal"><div class="num">' + d.cumulative + '</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(),
|
||
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 class="ops">' +
|
||
' <a onclick="viewHard(' + r.id + ')">查看</a>' +
|
||
' <a onclick="convertHard(' + r.id + ')">继续补全</a>' +
|
||
' <a onclick="convertHardQuick(' + r.id + ')">转软碎片</a>' +
|
||
' <a onclick="discardHard(' + r.id + ')">废弃</a>' +
|
||
'</td></tr>';
|
||
});
|
||
if (!hardRows.length) {
|
||
html = '<tr><td colspan="7" 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('、') || '-';
|
||
var layers = r.layer_rates || {};
|
||
var layerHtml = '核心' + (layers.core !== undefined ? layers.core : '-') + '% / 重要' + (layers.important !== undefined ? layers.important : '-') + '% / 非必要' + (layers.optional !== undefined ? layers.optional : '-') + '%';
|
||
html += '<tr>' +
|
||
'<td>' + completenessHtml(r.overall) + '</td>' +
|
||
'<td>' + levelTagHtml(r.level) + '</td>' +
|
||
'<td>' + escHtml(r.name) + '</td>' +
|
||
'<td style="font-size:12px;color:#5a6472;white-space:nowrap;">' + layerHtml + '</td>' +
|
||
'<td style="max-width:240px;">' + escHtml(missing) + '</td>' +
|
||
'<td>' + escHtml(r.table_label) + '</td>' +
|
||
'<td>' + fmtDate(r.updated_at) + '</td>' +
|
||
'<td class="ops"><a onclick="completeSoft(\'' + r.table + '\',' + r.id + ')">补全</a></td></tr>';
|
||
});
|
||
if (!softRows.length) {
|
||
html = '<tr><td colspan="8" class="empty-tip" style="padding:30px 0;">暂无软碎片</td></tr>';
|
||
}
|
||
$('#soft-tbody').html(html);
|
||
renderPagination($('#soft-pagination'), d, loadSoftList);
|
||
});
|
||
}
|
||
|
||
/* ================= 硬碎片:新增 ================= */
|
||
function openHardAdd() {
|
||
var typeSel = '<select id="ha-type">' +
|
||
'<option value="company">企业</option><option value="person">人员</option>' +
|
||
'<option value="product">产品</option><option value="need">需求</option><option value="mixed">混合</option></select>';
|
||
var content =
|
||
'<form id="ha-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="ha-f-company_name"><label>公司名称</label><input type="text" name="company_name" placeholder="如:深圳XX科技"></div>' +
|
||
' <div class="form-item" id="ha-f-person_name"><label>联系人</label><input type="text" name="person_name" placeholder="如:张三"></div>' +
|
||
' <div class="form-item"><label>联系电话</label><input type="text" name="contact_phone" placeholder="如:13812345678"></div>' +
|
||
' <div class="form-item"><label>联系邮箱</label><input type="text" name="contact_email" placeholder="如:a@b.com"></div>' +
|
||
' <div class="form-item" id="ha-f-idnumber"><label>身份证号码</label><input type="text" name="id_number" placeholder="15 或 18 位"></div>' +
|
||
' <div class="form-item"><label>来源渠道</label><select name="source_channel" id="ha-source"><option value="">-</option></select></div>' +
|
||
' <div class="form-item" id="ha-f-platform"><label>社媒平台</label><select name="platform" id="ha-platform"><option value="">-</option>' +
|
||
' <option value="wechat">微信</option><option value="weibo">微博</option><option value="douyin">抖音</option>' +
|
||
' <option value="bilibili">B站</option><option value="linkedin">领英</option><option value="xiaohongshu">小红书</option>' +
|
||
' <option value="other">其他</option></select></div>' +
|
||
' <div class="form-item" id="ha-f-account"><label>账号ID</label><input type="text" name="account_id" placeholder="社媒账号ID"></div>' +
|
||
' <div class="form-item" id="ha-f-profile"><label>主页链接</label><input type="text" name="profile_url" placeholder="https://..."></div>' +
|
||
' <div class="form-item full"><label>原始描述</label><textarea name="description" style="height:70px;" placeholder="记录原始线索信息"></textarea></div>' +
|
||
'</div>' +
|
||
'<div class="dialog-footer"><button type="button" class="btn" id="ha-cancel">取消</button>' +
|
||
'<button type="submit" class="btn btn-primary">保存碎片</button></div>' +
|
||
'</form>';
|
||
|
||
var idx = Dialog.open({
|
||
title: '新增硬碎片',
|
||
area: ['640px', 'auto'],
|
||
content: content,
|
||
btn: false
|
||
});
|
||
|
||
// 来源渠道选项(与 source_channels 字典保持一致,不额外添加选项)
|
||
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>';
|
||
});
|
||
$('#ha-source').html(html);
|
||
}).catch(function () {});
|
||
|
||
function syncFields() {
|
||
var t = $('#ha-type').val();
|
||
$('#ha-f-company_name').toggle(t !== 'person');
|
||
$('#ha-f-person_name').toggle(t === 'person' || t === 'mixed');
|
||
}
|
||
$('#ha-type').on('change', syncFields);
|
||
syncFields();
|
||
$('#ha-cancel').on('click', function () { Dialog.close(idx); });
|
||
|
||
$('#ha-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.source_type = $('#ha-type').val();
|
||
// 硬数据标准校验:企业/产品/需求 → 企业名称必填;人员 → 手机、邮箱至少一项
|
||
if (fd.source_type !== 'person' && !$.trim(fd.company_name)) { Dialog.error('企业类型硬数据:企业名称为必填项'); return; }
|
||
if (fd.source_type === 'person' && !$.trim(fd.contact_phone) && !$.trim(fd.contact_email)) { Dialog.error('人员类型硬数据:手机号码、邮箱至少填写一项'); return; }
|
||
// 格式校验:手机 / 邮箱 / 身份证
|
||
if (fd.contact_phone && !validatePhoneCN(fd.contact_phone)) { Dialog.error('手机号码格式不正确(应为国内 11 位手机号,如 13812345678)'); return; }
|
||
if (fd.contact_email && !validateEmail(fd.contact_email)) { Dialog.error('邮箱格式不正确'); return; }
|
||
if (fd.id_number && !validateIdCard(fd.id_number)) { Dialog.error('身份证号码格式不正确(应为 15 或 18 位)'); return; }
|
||
if (fd.profile_url && !/^https?:\/\//.test(fd.profile_url)) { Dialog.error('主页链接格式不正确(应以 http:// 或 https:// 开头)'); return; }
|
||
httpPost('fragment/hard_add.php', fd).then(function () {
|
||
Dialog.success('新增成功', function () {
|
||
Dialog.close(idx);
|
||
loadStats();
|
||
loadHardList(1);
|
||
});
|
||
});
|
||
});
|
||
}
|
||
|
||
/* ================= 硬碎片:查看 ================= */
|
||
window.viewHard = function (id) {
|
||
httpGet('fragment/hard_detail.php', { id: id }).then(function (d) {
|
||
var rowHtml = '';
|
||
var fields = [
|
||
['类型', d.source_type_label], ['公司名称', d.company_name], ['联系人', d.person_name],
|
||
['联系电话', d.contact_phone], ['联系邮箱', d.contact_email],
|
||
['来源渠道', 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>';
|
||
});
|
||
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 ex = d.extra_info || {};
|
||
if (typeof ex === 'string') { try { ex = JSON.parse(ex); } catch (e) { ex = {}; } }
|
||
var typeSel = '<select id="cv-type">' +
|
||
'<option value="company"' + (d.source_type === 'company' ? ' 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>' +
|
||
'<option value="mixed"' + (d.source_type === 'mixed' ? ' 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"><label>身份证号码</label><input type="text" name="id_number" value="' + escHtml(ex.id_number || '') + '" placeholder="15 或 18 位"></div>' +
|
||
' <div class="form-item"><label>社媒平台</label><select name="platform" id="cv-platform"><option value="">-</option>' +
|
||
' <option value="wechat">微信</option><option value="weibo">微博</option><option value="douyin">抖音</option>' +
|
||
' <option value="bilibili">B站</option><option value="linkedin">领英</option><option value="xiaohongshu">小红书</option>' +
|
||
' <option value="other">其他</option></select></div>' +
|
||
' <div class="form-item"><label>账号ID</label><input type="text" name="account_id" value="' + escHtml(ex.account_id || '') + '" placeholder="社媒账号ID"></div>' +
|
||
' <div class="form-item"><label>主页链接</label><input type="text" name="profile_url" value="' + escHtml(ex.profile_url || '') + '" placeholder="https://..."></div>' +
|
||
'</div>' +
|
||
'<div style="font-size:12px;color:#8a94a6;margin-top:4px;">补全仅保存到硬数据,点击「转软碎片」后才转换为主表数据。</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
|
||
});
|
||
$('#cv-platform').val(ex.platform || '');
|
||
|
||
function syncFields() {
|
||
var t = $('#cv-type').val();
|
||
$('#cv-f-company_name').toggle(t !== 'person');
|
||
$('#cv-f-person_name').toggle(t === 'person' || t === 'mixed');
|
||
}
|
||
$('#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').each(function () {
|
||
var name = $(this).attr('name');
|
||
if (name) fd[name] = $(this).val();
|
||
});
|
||
fd.id = id;
|
||
// 类型标准校验
|
||
if (fd.source_type !== 'person' && fd.source_type !== 'mixed' && !$.trim(fd.company_name)) { Dialog.error('企业类型硬数据:企业名称为必填项'); return; }
|
||
if (fd.source_type === 'person' && !$.trim(fd.contact_phone) && !$.trim(fd.contact_email)) { Dialog.error('人员类型硬数据:手机号码、邮箱至少填写一项'); return; }
|
||
// 格式校验:手机 / 邮箱 / 身份证 / 主页链接
|
||
if (fd.contact_phone && !validatePhoneCN(fd.contact_phone)) { Dialog.error('手机号码格式不正确(应为国内 11 位手机号,如 13812345678)'); return; }
|
||
if (fd.contact_email && !validateEmail(fd.contact_email)) { Dialog.error('邮箱格式不正确'); return; }
|
||
if (fd.id_number && !validateIdCard(fd.id_number)) { Dialog.error('身份证号码格式不正确(应为 15 或 18 位)'); return; }
|
||
if (fd.profile_url && !/^https?:\/\//.test(fd.profile_url)) { Dialog.error('主页链接格式不正确(应以 http:// 或 https:// 开头)'); return; }
|
||
httpPost('fragment/hard_update.php', fd).then(function () {
|
||
Dialog.success('已保存,点击「转软碎片」完成转换', function () {
|
||
Dialog.close(idx);
|
||
loadHardList(hardPage);
|
||
});
|
||
});
|
||
});
|
||
});
|
||
};
|
||
|
||
/* ================= 硬碎片:转软碎片(不弹窗,直接转换) ================= */
|
||
window.convertHardQuick = function (id) {
|
||
httpGet('fragment/hard_detail.php', { id: id }).then(function (d) {
|
||
var target = d.source_type;
|
||
if (target === 'mixed') {
|
||
target = d.company_name ? 'company' : (d.person_name ? 'person' : '');
|
||
}
|
||
if (!target) {
|
||
Dialog.error('该碎片缺少可转换信息,请使用「继续补全」');
|
||
return;
|
||
}
|
||
Dialog.confirm('确定将该硬数据直接转换为软数据碎片吗?', function () {
|
||
httpPost('fragment/hard_convert.php', { id: id, target_type: target }).then(function () {
|
||
Dialog.success('已转为软碎片', function () {
|
||
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_optional || []);
|
||
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 layers = d.layers || {};
|
||
var layerDesc = '核心' + (layers.core ? layers.core.rate : '-') + '% / 重要' + (layers.important ? layers.important.rate : '-') + '% / 非必要' + (layers.optional ? layers.optional.rate : '-') + '%';
|
||
var content =
|
||
'<form id="soft-form" style="padding:16px 22px 4px;">' +
|
||
'<div style="font-size:12px;color:#8a94a6;margin-bottom:4px;">整体完整度 <b style="color:#d9534f;">' + d.overall + '%</b>,各层:' + layerDesc + '</div>' +
|
||
'<div style="font-size:12px;color:#8a94a6;margin-bottom:10px;">核心层须 100%、重要层 ≥60%、非必要层 ≥40%,补全后自动重新计算。</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();
|
||
});
|
||
// 格式校验:身份证 / 手机 / 邮箱
|
||
if (fd.id_number && !validateIdCard(fd.id_number)) { Dialog.error('身份证号码格式不正确(应为 15 或 18 位)'); return; }
|
||
if (fd.phone && !validatePhoneCN(fd.phone)) { Dialog.error('手机号码格式不正确(应为国内 11 位手机号,如 13812345678)'); return; }
|
||
if (fd.email && !validateEmail(fd.email)) { Dialog.error('邮箱格式不正确'); return; }
|
||
httpPost('fragment/soft_update.php', fd).then(function (res) {
|
||
Dialog.success(res && res.is_complete ? '补全完成' : '已保存(整体完整度 ' + (res && res.overall) + '%)', function () {
|
||
Dialog.close(idx);
|
||
loadStats();
|
||
loadSoftList(softPage);
|
||
loadHardList(hardPage);
|
||
});
|
||
});
|
||
});
|
||
});
|
||
};
|
||
});
|