355 lines
19 KiB
JavaScript
355 lines
19 KiB
JavaScript
/**
|
||
* 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);
|
||
});
|
||
}
|
||
});
|