/** * 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( '
' + '
' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + '
' + '
' + ' ' + ' ' + '
ID姓名性别国籍工作所在地家乡学历手机邮箱创建时间操作
' + ' ' + '
' ); $('#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 += ''; }); $box.find('.area-prov').html(html); } function fillCity($box, provCode) { var html = ''; (childMap[provCode] || []).forEach(function (c) { html += ''; }); $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 += ''; }); return '
' + '' + '' + '' + '' + '' + '' + '' + '' + '
'; } function openForm(person, accounts, phone, email, experiences) { var isEdit = !!person; var p = person || {}; var pPhone = phone || ''; var pEmail = email || ''; // Tab1 基本信息 var baseHtml = '
' + '
' + '
' + '
' + '
' + '
' + '
' + '
' + '
' + '
' + areaSelectsHtml('hometown', p.hometown) + '
' + '
' + areaSelectsHtml('work_location', p.work_location) + '
' + '
' + '
' + '
' + '
' + '
' + '
' + '
'; // Tab2 工作履历 var expRowsHtml = ''; if (!experiences || !experiences.length) { expRowsHtml = expRowHtml(null); } else { experiences.forEach(function (e) { expRowsHtml += expRowHtml(e); }); } var expHtml = '
' + ' 工作履历' + ' ' + '
' + '
' + expRowsHtml + '
'; var content = '
' + '
' + '
' + ' 基本信息' + ' 工作履历' + '
' + '
' + '
' + baseHtml + '
' + ' ' + '
' + '
' + '' + '
'; 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 += ''; }); if (p.source_channel && !hasCur) { scHtml += ''; } $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 += ''; }); $(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; } // 格式校验:身份证 / 手机 / 邮箱 if (data.id_number && !validateIdCard(data.id_number)) { Dialog.error('身份证号码格式不正确(应为 15 或 18 位)'); return; } if (data.phone && !validatePhoneCN(data.phone)) { Dialog.error('手机号码格式不正确(应为国内 11 位手机号,如 13812345678)'); return; } if (data.email && !validateEmail(data.email)) { 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); }); } });