/**
* person.js - 人员数据管理逻辑(列表+筛选+分页 / 新增编辑弹窗 / 人脉 / 导入导出 / 详情)
* v1.0.18:
* - 新增「人脉」入口:与本人有交集(同事/老乡/校友)的人员列表,含亲密度百分比;点姓名反查详情
* - 新增/编辑表单分两个 Tab:基本信息 | 工作履历
* - 家乡/工作所在地:省-市两级联动(不细化区县),存储为「省-市」短结构(如 湖南衡阳 / 新疆乌鲁木齐)
* - 国籍不是中国/中国香港/中国澳门/中国台湾(即外国国籍)时,家乡置灰不可填写
*/
$(function () {
renderShell('人员数据', '数据管理 / 人员数据');
renderPage();
initPage('person', function (user) {
if (!checkPagePermission('person')) return;
loadList(1);
// 跳转定位:?id= 打开该人员详情(重复校验跳转用)
var jumpId = parseInt(new URLSearchParams(location.search).get('id') || '0', 10);
if (jumpId > 0) window.viewPerson(jumpId);
});
var currentPage = 1;
/** 渲染页面内容(工具栏+表格+分页) */
function renderPage() {
$('#page-content').html(
'
'
);
$('#check-all').on('change', function () { $('.row-check').prop('checked', this.checked); });
}
function loadList(page) {
currentPage = page;
var params = buildFilterParam();
params.page = page;
params.limit = PAGE_SIZE;
httpGet('person/list.php', params).then(function (data) {
var rows = data.list || [];
var html = '';
rows.forEach(function (r) {
html += '' +
' ' +
'' + r.id + ' ' +
'' + escHtml(r.full_name) + ' ' +
'' + escHtml(r.gender || '-') + ' ' +
'' + escHtml(r.nationality || '-') + ' ' +
'' + escHtml(r.work_location || '-') + ' ' +
'' + escHtml(r.hometown || '-') + ' ' +
'' + escHtml(r.education || '-') + ' ' +
'' + escHtml(r.phone || '-') + ' ' +
'' + escHtml((r.email || '-').substring(0, 30)) + ' ' +
'' + fmtDate(r.created_at) + ' ' +
'' +
' 详情 ' +
' 履历 ' +
' 人脉 ' +
' 编辑 ' +
' ';
});
if (!rows.length) {
html = '暂无数据 ';
}
$('#person-tbody').html(html);
renderPagination($('#pagination'), data, loadList);
});
}
$('#btn-search').on('click', function () { loadList(1); });
$('#btn-reset').on('click', function () {
$('.toolbar [data-filter]').val('');
loadList(1);
});
/* ============================================================
* 新增/编辑弹窗:Tab = 基本信息 | 工作履历
* ============================================================ */
var companyOptions = []; // 任职公司下拉选项
function loadCompanyOptions() {
if (companyOptions.length) return $.Deferred().resolve(companyOptions).promise();
return httpGet('person/company_options.php', { limit: 500 }).then(function (d) {
companyOptions = d.list || [];
return companyOptions;
}).catch(function () { companyOptions = []; return companyOptions; });
}
/** 手机号输入后自动识别归属地(录入手机号码时自动识别:国内-省-市;港澳台-中国香港/澳门/台湾;海外-国籍) */
function bindPhoneGeo($phoneInput, $geoHint) {
var timer = null;
$phoneInput.on('blur', function () {
var val = $.trim($(this).val());
if (!val) { $geoHint.text('').hide(); return; }
clearTimeout(timer);
timer = setTimeout(function () {
httpGet('common/phone_geo.php', { phone: val }).then(function (d) {
if (d && d.label && d.label !== '未知') {
$geoHint.text('归属地:' + d.label).show();
} else {
$geoHint.text('归属地:未能识别').show();
}
}).catch(function () { $geoHint.text('').hide(); });
}, 300);
});
}
/** 名称去行政区后缀:湖南省->湖南,新疆维吾尔自治区->新疆,北京市->北京,石家庄市->石家庄 */
function stripAreaSuffix(name) {
return String(name || '')
.replace(/(维吾尔|壮族|回族)?自治区$/, '')
.replace(/省$/, '')
.replace(/特别行政区$/, '')
.replace(/市$/, '');
}
/** 省-市两级联动下拉(不细化区县;值存储为短结构,如 湖南衡阳 / 新疆乌鲁木齐 / 北京) */
function areaSelectsHtml(target, value) {
return '' +
'省 ' +
'市 ' +
'
' +
' ';
}
/** 构建省/市两级联动:填充选项并按已有值回选(兼容旧格式:湖南衡阳 / 湖南省衡阳市 / 北京市-东城区) */
function initAreaCascade(areas) {
var provMap = {}; // code -> name
var childMap = {}; // parent_code -> [{code,name}]
(areas || []).forEach(function (a) {
if (!a.parent_code) {
provMap[a.code] = a.name;
} else {
(childMap[a.parent_code] = childMap[a.parent_code] || []).push({ code: a.code, name: a.name });
}
});
var provCodes = Object.keys(provMap);
function fillProv($box) {
var html = '省 ';
provCodes.forEach(function (code) {
html += '' + escHtml(provMap[code]) + ' ';
});
$box.find('.area-prov').html(html);
}
function fillCity($box, provCode) {
var html = '市 ';
(childMap[provCode] || []).forEach(function (c) {
html += '' + escHtml(c.name) + ' ';
});
$box.find('.area-city').html(html);
}
function combine($box) {
var target = $box.data('target');
var provName = $box.find('.area-prov option:selected').text();
var cityName = $box.find('.area-city option:selected').text();
var provShort = provName ? stripAreaSuffix(provName) : '';
var cityShort = cityName ? stripAreaSuffix(cityName) : '';
var val = provShort;
// 直辖市(省/市同名)只存省
if (cityShort && cityShort !== provShort) val += cityShort;
$box.closest('.form-item').find('input[name="' + target + '"]').val(val);
}
/** 按短名匹配省(如 湖南 / 新疆 / 北京) */
function matchProv(shortName) {
var sorted = provCodes.slice().sort(function (a, b) { return b.length - a.length; });
for (var i = 0; i < sorted.length; i++) {
if (stripAreaSuffix(provMap[sorted[i]]) === shortName) return sorted[i];
}
return '';
}
/** 按短名匹配市 */
function matchCity(provCode, shortName) {
var list = childMap[provCode] || [];
for (var i = 0; i < list.length; i++) {
if (stripAreaSuffix(list[i].name) === shortName) return list[i].code;
}
return '';
}
/** 解析已有值 -> [省短名, 市短名](兼容 湖南衡阳 / 湖南省衡阳市 / 北京市-东城区 等格式) */
function parseValue(value) {
var v = String(value || '').trim();
if (!v) return ['', ''];
if (v.indexOf('-') >= 0) {
var parts = v.split('-');
return [stripAreaSuffix(parts[0]), parts[1] ? stripAreaSuffix(parts[1]) : ''];
}
// 无分隔符:最长省名前缀匹配
var sorted = provCodes.slice().sort(function (a, b) { return b.length - a.length; });
for (var i = 0; i < sorted.length; i++) {
var pShort = stripAreaSuffix(provMap[sorted[i]]);
if (pShort && v.indexOf(pShort) === 0) {
var rest = v.substring(pShort.length);
return [pShort, rest];
}
}
return ['', v];
}
$('.area-cascade').each(function () {
var $box = $(this);
var target = $box.data('target');
var current = $box.closest('.form-item').find('input[name="' + target + '"]').val() || '';
var parsed = parseValue(current);
var provShort = parsed[0];
var cityShort = parsed[1];
fillProv($box);
var provCode = provShort ? matchProv(provShort) : '';
if (!provCode) return;
$box.find('.area-prov').val(provCode);
fillCity($box, provCode);
// 直辖市:市=省本身
if (cityShort === provShort) {
var muniCode = matchCity(provCode, cityShort);
if (muniCode) $box.find('.area-city').val(muniCode);
return;
}
var cityCode = cityShort ? matchCity(provCode, cityShort) : '';
if (cityCode) $box.find('.area-city').val(cityCode);
});
// 联动事件
$('.area-cascade').off('change.area').on('change.area', '.area-prov', function () {
var $box = $(this).closest('.area-cascade');
fillCity($box, $(this).val());
combine($box);
});
$('.area-cascade').off('change.areaCity').on('change.areaCity', '.area-city', function () {
combine($(this).closest('.area-cascade'));
});
}
/** 国籍是否为中国大陆/港澳台(仅此时家乡可填写;外国国籍时家乡置灰) */
function isCnNationality(n) {
var v = String(n || '').trim();
return v === '中国' || v === '中国香港' || v === '中国澳门' || v === '中国台湾';
}
/** 工作履历行模板 */
function expRowHtml(e) {
e = e || {};
var empty = function (v) { return (v === null || v === undefined) ? '' : v; };
var opts = '请选择公司 ';
companyOptions.forEach(function (c) {
opts += '' + escHtml(c.name) + ' ';
});
return '' +
'' + opts + ' ' +
' ' +
' ' +
' ' +
' ' +
' ' +
' 在职 ' +
'删除 ' +
'
';
}
function openForm(person, accounts, phone, email, experiences) {
var isEdit = !!person;
var p = person || {};
var pPhone = phone || '';
var pEmail = email || '';
// Tab1 基本信息
var baseHtml =
'';
// Tab2 工作履历
var expRowsHtml = '';
if (!experiences || !experiences.length) {
expRowsHtml = expRowHtml(null);
} else {
experiences.forEach(function (e) { expRowsHtml += expRowHtml(e); });
}
var expHtml =
'' +
' 工作履历 ' +
' + 添加履历 ' +
'
' +
'' + expRowsHtml + '
';
var content =
'';
var idx = Dialog.open({
title: isEdit ? '编辑人员' : '新增人员',
area: ['760px', 'auto'],
content: content,
btn: false
});
// 手机号归属地自动识别
bindPhoneGeo($('#person-phone'), $('#person-phone-geo'));
// 录入自动清洗:手机号去所有空格(含中间),邮箱去首尾空格
$('#person-phone').on('input', function () {
var v = cleanPhone($(this).val());
if ($(this).val() !== v) $(this).val(v);
});
$('#person-email').on('blur', function () { $(this).val(cleanEmail($(this).val())); });
// 加载国籍字典 + 行政区划字典(省-市两级) + 来源渠道字典 + 任职公司
httpGet('common/dicts.php', { type: 'all' }).then(function (d) {
var $dl = $('#person-country-list');
var html = '';
(d.countries || []).forEach(function (c) { html += ' '; });
$dl.html(html);
// 省-市两级联动
initAreaCascade(d.areas || []);
var $sc = $('#person-source-channel');
var scHtml = '- ';
var hasCur = false;
(d.source_channels || []).forEach(function (s) {
var sel = (s.name === p.source_channel) ? ' selected' : '';
if (sel) hasCur = true;
scHtml += '' + escHtml(s.name) + ' ';
});
if (p.source_channel && !hasCur) {
scHtml += '' + escHtml(p.source_channel) + '(自定义) ';
}
$sc.html(scHtml);
// 国籍变化 -> 家乡置灰/可用
function syncHometownDisabled() {
var n = $('#person-nationality').val();
var disabled = !isCnNationality(n); // 外国国籍时家乡置灰不可填
var $box = $('.hometown-box');
$box.find('select, input').prop('disabled', disabled);
$box.toggleClass('area-disabled', disabled);
if (disabled) {
$box.closest('.form-item').find('input[name="hometown"]').val('');
}
}
$('#person-nationality').off('input.home').on('input.home', syncHometownDisabled);
syncHometownDisabled();
}).catch(function () {});
// 任职公司选项
loadCompanyOptions().then(function () {
// 已有行重填公司下拉(若首次加载后追加的选项)
$('#exp-rows .exp-row select[name="exp-company"]').each(function () {
if ($(this).find('option').length <= 1) {
var cur = $(this).data('cur') || '';
var opts = '请选择公司 ';
companyOptions.forEach(function (c) {
opts += '' + escHtml(c.name) + ' ';
});
$(this).html(opts);
}
});
});
$('#add-exp-row').on('click', function () { $('#exp-rows').append(expRowHtml(null)); });
$('#form-cancel').on('click', function () { Dialog.close(idx); });
$('#person-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.full_name) { Dialog.error('姓名为必填项'); return; }
// 联系方式(手机/邮箱,均为常用)
data.phone = cleanPhone(data.phone);
data.email = cleanEmail(data.email);
var contacts = [];
if (data.phone) {
contacts.push({ platform: 'phone', account_id: data.phone, is_primary: 1, is_defult: 1 });
}
if (data.email) {
contacts.push({ platform: 'email', account_id: data.email, is_primary: 1, is_defult: 1 });
}
data.contacts = JSON.stringify(contacts);
delete data.phone;
delete data.email;
// 工作履历(整表替换)
var exps = [];
$('#exp-rows .exp-row').each(function () {
var $r = $(this);
var companyId = $r.find('[name="exp-company"]').val();
if (!companyId) return;
exps.push({
company_id: companyId,
position: $r.find('[name="exp-position"]').val(),
department: $r.find('[name="exp-department"]').val(),
job_level: $r.find('[name="exp-level"]').val(),
start_date: $r.find('[name="exp-start"]').val(),
end_date: $r.find('[name="exp-end"]').val(),
is_current: $r.find('[name="exp-current"]').prop('checked') ? 1 : 0
});
});
data.experiences = JSON.stringify(exps);
if (isEdit) data.id = p.id;
httpPost(isEdit ? 'person/update.php' : 'person/add.php', data).then(function () {
Dialog.success('保存成功', function () {
Dialog.close(idx);
loadList(currentPage);
});
});
});
}
/* ---------- 新增 / 编辑入口 ---------- */
window.addPerson = function () {
openForm(null, [], '', '', []);
};
window.editPerson = function (id) {
httpGet('person/detail.php', { id: id }).then(function (d) {
openForm(d.person, d.accounts || [], d.phone || '', d.email || '', d.experiences || []);
}).catch(function () {
Dialog.alert('加载人员详情失败');
});
};
$('#btn-add').on('click', function () { addPerson(); });
/* ---------- 人员弹窗 Tab 切换 ---------- */
window.switchPersonTab = function (el) {
var $tabs = $(el).closest('.edit-tabs');
$tabs.find('.edit-tab').removeClass('active');
$(el).addClass('active');
$tabs.find('.edit-pane').hide();
$('#edit-pane-' + $(el).data('tab')).show();
};
/* ---------- 导入 ---------- */
$('#btn-import').on('click', function () {
triggerFileInput('.csv', function (file) {
var fd = new FormData();
fd.append('file', file);
httpPost('person/import.php', fd, true).then(function (d) {
Dialog.alert('导入结果:成功 ' + d.inserted + ' 条,失败 ' + d.failed + ' 条', function () {
loadList(1);
});
});
});
});
/* ---------- 导出(仅导出勾选的人员,未勾选弹窗提醒) ---------- */
$('#btn-export').on('click', function () {
var ids = [];
$('.row-check:checked').each(function () { ids.push($(this).val()); });
if (!ids.length) {
layer.alert('请先选择需要导出的人员数据', { icon: 2, title: '提示' });
return;
}
download('person/export.php', { ids: ids.join(',') });
});
window.viewPerson = function (id) {
httpGet('person/detail.php', { id: id }).then(function (d) {
var p = d.person;
var expHtml = '';
(d.experiences || []).forEach(function (e) {
expHtml += '' + escHtml(e.display_name || '-') + ' ' + escHtml(e.position || '-') + ' ' +
'' + escHtml(e.department || '-') + ' ' + escHtml(e.job_level || '-') + ' ' +
'' + (e.is_current ? '在职 ' : fmtDate(e.start_date) + ' ~ ' + (e.end_date || '至今')) + ' ';
});
var html =
'' +
'
' + escHtml(p.full_name) + ' ' +
'
' +
'
性别: ' + escHtml(p.gender || '-') + '
' +
'
国籍: ' + escHtml(p.nationality || '-') + '
' +
'
证件: ' + escHtml(p.id_type || '-') + ' / ' + escHtml(p.id_number || '-') + '
' +
'
学历: ' + escHtml(p.education || '-') + '
' +
'
毕业院校: ' + escHtml(p.graduated_from || '-') + '
' +
'
家乡: ' + escHtml(p.hometown || '-') + '
' +
'
工作所在地: ' + escHtml(p.work_location || '-') + '
' +
'
来源渠道: ' + escHtml(p.source_channel || '-') + '
' +
'
来源详情: ' + escHtml(p.source_detail || '-') + '
' +
'
union_id: ' + escHtml(p.union_id) + '
' +
'
' +
'
联系方式 ' +
'
手机: ' + escHtml(d.phone || '-') + '
' +
'
邮箱: ' + escHtml(d.email || '-') + '
' +
'
工作履历 ' +
'
公司 职位 部门 级别 时间 ' +
(expHtml || '暂无 ') + '
' +
'
';
Dialog.open({ title: '人员详情', area: ['760px', 'auto'], content: html, btn: ['关闭'] });
});
}
/* ---------- 人脉弹窗(同事/老乡/校友 + 亲密度) ---------- */
var connPersonId = 0;
window.viewConnections = function (personId) {
connPersonId = personId;
var content =
'' +
'
共 0 条人脉(交集:工作履历/家乡/毕业院校)
' +
'
' +
' 姓名 联系方式 交集 亲密度 ' +
' ' +
'
' +
'
';
Dialog.open({ title: '人脉', area: ['760px', 'auto'], content: content, btn: ['关闭'] });
loadConnections();
};
function loadConnections() {
httpGet('person/connections.php', { person_id: connPersonId, limit: 50 }).then(function (data) {
var rows = data.list || [];
$('#conn-total').text(rows.length);
var html = '';
rows.forEach(function (r) {
var tagHtml = (r.tags || []).map(function (t) {
return '' + escHtml(t) + ' ';
}).join(' ');
html += '' +
'' + escHtml(r.full_name) + ' ' +
'' + escHtml(r.phone || '-') + ' ' +
'' + (tagHtml || '-') + ' ' +
'' +
' ' + r.intimacy + '% ' +
' ' +
' ' +
' ' +
' ' +
' ';
});
if (!rows.length) {
html = '暂无相关人脉 ';
}
$('#conn-tbody').html(html);
});
}
/* ---------- 任职履历弹窗(按人员查看任职公司/履历,分页) ---------- */
var compPersonId = 0;
window.viewResume = function (personId) {
compPersonId = personId;
var content =
'';
Dialog.open({ title: '工作履历', area: ['720px', 'auto'], content: content, btn: ['关闭'] });
loadCompanies(1);
$('#comp-search').on('click', function () { loadCompanies(1); });
$('#comp-reset').on('click', function () {
$('#comp-cur').prop('checked', true);
$('#comp-off').prop('checked', true);
loadCompanies(1);
});
};
function loadCompanies(page) {
var params = { person_id: compPersonId, page: page, limit: 5 };
var cur = $('#comp-cur').prop('checked');
var off = $('#comp-off').prop('checked');
// 状态筛选:只勾一项则按该项过滤;都不勾视为全部
if (cur && !off) params.is_current = 1;
else if (!cur && off) params.is_current = 0;
httpGet('person/companies.php', params).then(function (data) {
var rows = data.list || [];
var html = '';
rows.forEach(function (r) {
html += '' +
'' + escHtml(r.company_name) + ' ' +
'' + escHtml(r.department || '-') + ' ' +
'' + escHtml(r.position || '-') + ' ' +
'' + escHtml(r.job_level || '-') + ' ' +
'' + escHtml(r.start_date || '-') + ' ' +
'' + (r.is_current ? '在岗 ' : '离岗 ') + ' ' +
' ';
});
if (!rows.length) {
html = '暂无任职记录 ';
}
$('#comp-tbody').html(html);
renderPagination($('#comp-pagination'), data, loadCompanies);
});
}
});