Files
superlink/static/js/person.js
T

355 lines
19 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* person.js - 人员数据管理逻辑(列表+筛选+分页 / 新增编辑弹窗 / 导入导出 / 详情)
*/
$(function () {
renderShell('人员数据', '数据管理 / 人员数据');
renderPage();
initPage('person', function (user) {
if (!checkPagePermission('person')) return;
loadList(1);
});
var currentPage = 1;
/** 渲染页面内容(工具栏+表格+分页) */
function renderPage() {
$('#page-content').html(
'<div class="card">' +
' <div class="toolbar">' +
' <input type="text" data-filter="keyword" placeholder="姓名/证件号/手机/邮箱关键词">' +
' <select data-filter="gender"><option value="">全部性别</option>' +
' <option value="男">男</option><option value="女">女</option>' +
' <option value="其他">其他</option><option value="保密">保密</option></select>' +
' <input type="text" data-filter="nationality" placeholder="国籍">' +
' <input type="text" data-filter="work_location" placeholder="工作所在地">' +
' <button class="btn btn-primary" id="btn-search">搜索</button>' +
' <button class="btn" id="btn-reset">重置</button>' +
' <span class="spacer"></span>' +
' <button class="btn btn-success" id="btn-add">+ 新增</button>' +
' <button class="btn btn-warning" id="btn-import">导入CSV</button>' +
' <button class="btn" id="btn-export">导出</button>' +
' </div>' +
' <div class="table-wrap"><table class="grid">' +
' <thead><tr><th><input type="checkbox" id="check-all"></th><th>ID</th><th>姓名</th><th>性别</th><th>国籍</th><th>工作所在地</th><th>家乡</th><th>学历</th><th>联系方式</th><th>创建时间</th><th>操作</th></tr></thead>' +
' <tbody id="person-tbody"></tbody>' +
' </table></div>' +
' <div class="pagination" id="pagination"></div>' +
'</div>'
);
$('#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 += '<tr>' +
'<td><input type="checkbox" class="row-check" value="' + r.id + '"></td>' +
'<td>' + r.id + '</td>' +
'<td>' + escHtml(r.full_name) + '</td>' +
'<td>' + escHtml(r.gender || '-') + '</td>' +
'<td>' + escHtml(r.nationality || '-') + '</td>' +
'<td>' + escHtml(r.work_location || '-') + '</td>' +
'<td>' + escHtml(r.hometown || '-') + '</td>' +
'<td>' + escHtml(r.education || '-') + '</td>' +
'<td title="' + escHtml(r.contacts || '') + '">' + escHtml((r.contacts || '-').substring(0, 40)) + '</td>' +
'<td>' + fmtDate(r.created_at) + '</td>' +
'<td class="ops">' +
' <a onclick="viewPerson(' + r.id + ')">详情</a>' +
' <a onclick="viewCompanies(' + r.id + ')">公司</a>' +
' <a onclick="viewPersonProducts(' + r.id + ')">产品</a>' +
' <a onclick="editPerson(' + r.id + ')">编辑</a>' +
'</td></tr>';
});
if (!rows.length) {
html = '<tr><td colspan="11" class="empty-tip" style="padding:40px 0;">暂无数据</td></tr>';
}
$('#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);
});
$('#btn-add').on('click', function () { openForm(null); });
window.editPerson = function (id) {
httpGet('person/detail.php', { id: id }).then(function (d) {
openForm(d.person, d.accounts);
});
};
function contactsInputHtml(accounts) {
accounts = accounts || [];
var html = '<div id="contacts-box">';
if (!accounts.length) {
accounts = [{ platform: 'email', account_id: '', is_primary: 1 }];
}
accounts.forEach(function (a, i) {
html += '<div class="contact-row" style="display:flex;gap:6px;margin-bottom:6px;">' +
'<select name="c-platform" style="width:110px;height:32px;border:1px solid #d9dee8;border-radius:4px;padding:0 6px;">' +
platformOpts(a.platform) + '</select>' +
'<input type="text" name="c-account" placeholder="账号/号码" value="' + escHtml(a.account_id) + '" style="flex:1;height:32px;border:1px solid #d9dee8;border-radius:4px;padding:0 8px;">' +
'<label style="line-height:32px;font-size:12px;"><input type="checkbox" name="c-primary" ' + (a.is_primary ? 'checked' : '') + '> 默认</label>' +
'<button type="button" class="btn btn-sm" onclick="$(this).parent().remove()">删除</button></div>';
});
return html + '</div>' +
'<button type="button" class="btn btn-sm" id="add-contact">+ 添加联系方式</button>';
}
function platformOpts(sel) {
var list = ['phone', 'email', 'wechat', 'douyin', 'linkedin', 'x', 'facebook', 'instagram', 'tiktok', 'other'];
var html = '';
list.forEach(function (p) {
html += '<option value="' + p + '"' + (sel === p ? ' selected' : '') + '>' + p + '</option>';
});
return html;
}
function openForm(person, accounts) {
var isEdit = !!person;
var p = person || {};
var content =
'<form id="person-form" style="padding:18px 22px 4px;">' +
'<div class="form-grid">' +
' <div class="form-item"><label>姓名<span class="req">*</span></label><input type="text" name="full_name" value="' + escHtml(p.full_name) + '"></div>' +
' <div class="form-item"><label>性别</label><select name="gender">' +
' <option value="保密"' + (!p.gender || p.gender === '保密' ? ' selected' : '') + '>保密</option>' +
' <option value="男"' + (p.gender === '男' ? ' selected' : '') + '>男</option>' +
' <option value="女"' + (p.gender === '女' ? ' selected' : '') + '>女</option>' +
' <option value="其他"' + (p.gender === '其他' ? ' selected' : '') + '>其他</option></select></div>' +
' <div class="form-item"><label>国籍</label><input type="text" name="nationality" value="' + escHtml(p.nationality) + '"></div>' +
' <div class="form-item"><label>证件类型</label><input type="text" name="id_type" value="' + escHtml(p.id_type) + '"></div>' +
' <div class="form-item"><label>证件号码</label><input type="text" name="id_number" value="' + escHtml(p.id_number) + '"></div>' +
' <div class="form-item"><label>学历</label><input type="text" name="education" value="' + escHtml(p.education) + '"></div>' +
' <div class="form-item"><label>毕业院校</label><input type="text" name="graduated_from" value="' + escHtml(p.graduated_from) + '"></div>' +
' <div class="form-item"><label>家乡</label><input type="text" name="hometown" value="' + escHtml(p.hometown) + '"></div>' +
' <div class="form-item"><label>工作所在地</label><input type="text" name="work_location" value="' + escHtml(p.work_location) + '"></div>' +
' <div class="form-item full"><label>联系方式</label>' + contactsInputHtml(accounts) + '</div>' +
'</div>' +
'<div class="dialog-footer"><button type="button" class="btn" id="form-cancel">取消</button>' +
'<button type="submit" class="btn btn-primary">保存</button></div>' +
'</form>';
var idx = Dialog.open({
title: isEdit ? '编辑人员' : '新增人员',
area: ['720px', 'auto'],
content: content,
btn: false
});
$('#form-cancel').on('click', function () { Dialog.close(idx); });
$('#add-contact').on('click', function () {
$('#contacts-box').append(
'<div class="contact-row" style="display:flex;gap:6px;margin-bottom:6px;">' +
'<select name="c-platform" style="width:110px;height:32px;border:1px solid #d9dee8;border-radius:4px;padding:0 6px;">' + platformOpts('') + '</select>' +
'<input type="text" name="c-account" placeholder="账号/号码" style="flex:1;height:32px;border:1px solid #d9dee8;border-radius:4px;padding:0 8px;">' +
'<label style="line-height:32px;font-size:12px;"><input type="checkbox" name="c-primary"> 默认</label>' +
'<button type="button" class="btn btn-sm" onclick="$(this).parent().remove()">删除</button></div>'
);
});
$('#person-form').on('submit', function (e) {
e.preventDefault();
var data = {};
$(this).find('input, select').each(function () {
var name = $(this).attr('name');
if (!name) return;
if (name.indexOf('c-') === 0) return;
if ($(this).attr('type') === 'checkbox') {
data[name] = $(this).prop('checked') ? 1 : 0;
} else {
data[name] = $(this).val();
}
});
if (!data.full_name) { Dialog.error('姓名为必填项'); return; }
// 联系方式
var contacts = [];
$('#contacts-box .contact-row').each(function () {
var $row = $(this);
var account = $.trim($row.find('input[name="c-account"]').val());
var platform = $row.find('select[name="c-platform"]').val();
var primary = $row.find('input[name="c-primary"]').prop('checked') ? 1 : 0;
if (account) {
contacts.push({ platform: platform, account_id: account, is_primary: primary });
}
});
data.contacts = JSON.stringify(contacts);
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);
});
});
});
}
/* ---------- 导入 ---------- */
$('#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 accountsHtml = '';
(d.accounts || []).forEach(function (a) {
accountsHtml += '<div style="font-size:13px;padding:3px 0;">' + escHtml(a.platform) + ':' + escHtml(a.account_id) +
(a.is_primary ? ' <span class="tag">默认</span>' : '') + '</div>';
});
var expHtml = '';
(d.experiences || []).forEach(function (e) {
expHtml += '<tr><td>' + escHtml(e.display_name || '-') + '</td><td>' + escHtml(e.position || '-') + '</td>' +
'<td>' + escHtml(e.department || '-') + '</td><td>' + escHtml(e.job_level || '-') + '</td>' +
'<td>' + (e.is_current ? '<span class="tag tag-green">在职</span>' : fmtDate(e.start_date) + ' ~ ' + (e.end_date || '至今')) + '</td></tr>';
});
var html =
'<div style="padding:18px 22px;">' +
' <h3 style="margin-bottom:14px;">' + escHtml(p.full_name) + '</h3>' +
' <div style="display:grid;grid-template-columns:1fr 1fr;gap:8px 24px;font-size:13px;">' +
' <div><b>性别:</b>' + escHtml(p.gender || '-') + '</div>' +
' <div><b>国籍:</b>' + escHtml(p.nationality || '-') + '</div>' +
' <div><b>证件:</b>' + escHtml(p.id_type || '-') + ' / ' + escHtml(p.id_number || '-') + '</div>' +
' <div><b>学历:</b>' + escHtml(p.education || '-') + '</div>' +
' <div><b>毕业院校:</b>' + escHtml(p.graduated_from || '-') + '</div>' +
' <div><b>家乡:</b>' + escHtml(p.hometown || '-') + '</div>' +
' <div><b>工作所在地:</b>' + escHtml(p.work_location || '-') + '</div>' +
' <div><b>union_id:</b>' + escHtml(p.union_id) + '</div>' +
' </div>' +
' <h4 style="margin:16px 0 8px;">联系方式</h4>' + (accountsHtml || '<div style="font-size:13px;color:#b6bfcc;">暂无</div>') +
' <h4 style="margin:16px 0 8px;">工作经历</h4>' +
' <table class="grid" style="font-size:12px;"><tr><th>公司</th><th>职位</th><th>部门</th><th>级别</th><th>时间</th></tr>' +
(expHtml || '<tr><td colspan="5" style="color:#b6bfcc;text-align:center;">暂无</td></tr>') + '</table>' +
'</div>';
Dialog.open({ title: '人员详情', area: ['760px', 'auto'], content: html, btn: ['关闭'] });
});
}
/* ---------- 任职公司弹窗(按人员查看任职公司,分页) ---------- */
var compPersonId = 0;
window.viewCompanies = function (personId) {
compPersonId = personId;
var content =
'<div style="padding:14px 18px 6px;overflow:hidden;height:330px;">' +
' <div class="toolbar" style="margin-bottom:10px;">' +
' <span style="font-size:13px;color:#5a6472;">状态:</span>' +
' <label style="font-size:13px;color:#5a6472;"><input type="checkbox" id="comp-cur" checked> 在岗</label>' +
' <label style="font-size:13px;color:#5a6472;"><input type="checkbox" id="comp-off" checked> 离职</label>' +
' <button class="btn btn-primary btn-sm" id="comp-search">筛选</button>' +
' <button class="btn btn-sm" id="comp-reset">重置</button>' +
' </div>' +
' <div class="table-wrap"><table class="grid" style="font-size:13px;">' +
' <thead><tr><th>公司名称</th><th>部门</th><th>岗位</th><th>职级</th><th>入职日期</th><th>是否在岗</th></tr></thead>' +
' <tbody id="comp-tbody"></tbody>' +
' </table></div>' +
' <div class="pagination" id="comp-pagination"></div>' +
'</div>';
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 += '<tr>' +
'<td>' + escHtml(r.company_name) + '</td>' +
'<td>' + escHtml(r.department || '-') + '</td>' +
'<td>' + escHtml(r.position || '-') + '</td>' +
'<td>' + escHtml(r.job_level || '-') + '</td>' +
'<td>' + escHtml(r.start_date || '-') + '</td>' +
'<td>' + (r.is_current ? '<span class="tag tag-green">在岗</span>' : '<span class="tag tag-gray">离岗</span>') + '</td>' +
'</tr>';
});
if (!rows.length) {
html = '<tr><td colspan="6" class="empty-tip" style="padding:30px 0;">暂无任职记录</td></tr>';
}
$('#comp-tbody').html(html);
renderPagination($('#comp-pagination'), data, loadCompanies);
});
}
/* ---------- 产品品类弹窗(按人员任职公司查看产品,分页) ---------- */
var ppPersonId = 0;
window.viewPersonProducts = function (personId) {
ppPersonId = personId;
var content =
'<div style="padding:14px 18px 6px;overflow:hidden;height:330px;">' +
' <div class="table-wrap"><table class="grid" style="font-size:13px;">' +
' <thead><tr><th>公司</th><th>品类名称</th><th>品类描述</th><th>是否核心</th></tr></thead>' +
' <tbody id="pp-tbody"></tbody>' +
' </table></div>' +
' <div class="pagination" id="pp-pagination"></div>' +
'</div>';
Dialog.open({ title: '产品品类', area: ['760px', 'auto'], content: content, btn: ['关闭'] });
loadPersonProducts(1);
};
function loadPersonProducts(page) {
httpGet('person/products.php', { person_id: ppPersonId, page: page, limit: 5 }).then(function (data) {
var rows = data.list || [];
var html = '';
rows.forEach(function (r) {
html += '<tr>' +
'<td>' + escHtml(r.company_name) + '</td>' +
'<td>' + escHtml(r.category_name) + '</td>' +
'<td>' + escHtml(r.category_description || '-') + '</td>' +
'<td>' + (r.is_core ? '<span class="tag">核心</span>' : '-') + '</td>' +
'</tr>';
});
if (!rows.length) {
html = '<tr><td colspan="4" class="empty-tip" style="padding:30px 0;">暂无产品品类</td></tr>';
}
$('#pp-tbody').html(html);
renderPagination($('#pp-pagination'), data, loadPersonProducts);
});
}
});