279 lines
15 KiB
JavaScript
279 lines
15 KiB
JavaScript
/**
|
||
* media.js - 媒体数据管理逻辑(列表+筛选+分页 / 新增编辑弹窗 / 导入导出 / 详情)
|
||
*/
|
||
$(function () {
|
||
renderShell('媒体数据', '数据管理 / 媒体数据');
|
||
renderPage();
|
||
initPage('media', function (user) {
|
||
if (!checkPagePermission('media')) 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="platform" id="filter-platform"><option value="">全部平台</option></select>' +
|
||
' <select data-filter="certification_type" id="filter-cert"><option value="">全部认证类型</option></select>' +
|
||
' <select data-filter="account_level" id="filter-level"><option value="">全部等级</option></select>' +
|
||
' <select data-filter="owner_type"><option value="">全部归属</option>' +
|
||
' <option value="person">个人</option><option value="company">企业</option></select>' +
|
||
' <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>' +
|
||
' <button class="btn btn-danger" id="btn-del-batch">批量删除</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="media-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('media/list.php', params).then(function (data) {
|
||
var rows = data.list || [];
|
||
var dict = data.dict || {};
|
||
fillDict(dict);
|
||
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.owner_name || r.owner_type + '#' + r.owner_id) + '</td>' +
|
||
'<td>' + escHtml(r.platform || '-') + '</td>' +
|
||
'<td title="' + escHtml(r.account_id) + '">' + escHtml(r.account_id) + '</td>' +
|
||
'<td>' + escHtml(r.certification_type || '-') + '</td>' +
|
||
'<td>' + escHtml(r.account_level || '-') + '</td>' +
|
||
'<td>' + (r.follower_count || 0) + '</td>' +
|
||
'<td>' + (r.avg_read_count || 0) + '</td>' +
|
||
'<td>' + escHtml(r.content_categories || '-') + '</td>' +
|
||
'<td class="ops">' +
|
||
' <a onclick="viewMedia(' + r.id + ')">详情</a>' +
|
||
' <a onclick="editMedia(' + r.id + ')">编辑</a>' +
|
||
'</td></tr>';
|
||
});
|
||
if (!rows.length) {
|
||
html = '<tr><td colspan="11" class="empty-tip" style="padding:40px 0;">暂无数据</td></tr>';
|
||
}
|
||
$('#media-tbody').html(html);
|
||
renderPagination($('#pagination'), data, loadList);
|
||
});
|
||
}
|
||
|
||
function fillDict(dict) {
|
||
if (dict.platforms && dict.platforms.length) {
|
||
var html = '<option value="">全部平台</option>';
|
||
dict.platforms.forEach(function (p) { html += '<option value="' + escHtml(p) + '">' + escHtml(p) + '</option>'; });
|
||
$('#filter-platform').html(html);
|
||
}
|
||
if (dict.certification_types && dict.certification_types.length) {
|
||
var h2 = '<option value="">全部认证类型</option>';
|
||
dict.certification_types.forEach(function (p) { h2 += '<option value="' + escHtml(p) + '">' + escHtml(p) + '</option>'; });
|
||
$('#filter-cert').html(h2);
|
||
}
|
||
if (dict.account_levels && dict.account_levels.length) {
|
||
var h3 = '<option value="">全部等级</option>';
|
||
dict.account_levels.forEach(function (p) { h3 += '<option value="' + escHtml(p) + '">' + escHtml(p) + '</option>'; });
|
||
$('#filter-level').html(h3);
|
||
}
|
||
}
|
||
|
||
$('#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.editMedia = function (id) {
|
||
httpGet('media/detail.php', { id: id }).then(function (d) {
|
||
openForm(d.media);
|
||
});
|
||
};
|
||
|
||
function openForm(media) {
|
||
var isEdit = !!media;
|
||
var m = media || {};
|
||
var content =
|
||
'<form id="media-form" style="padding:18px 22px 4px;">' +
|
||
'<div class="form-grid">' +
|
||
' <div class="form-item"><label>归属类型<span class="req">*</span></label><select name="owner_type" ' + (isEdit ? 'disabled' : '') + '>' +
|
||
' <option value="person"' + (m.owner_type === 'person' ? ' selected' : '') + '>个人</option>' +
|
||
' <option value="company"' + (m.owner_type === 'company' ? ' selected' : '') + '>企业</option></select></div>' +
|
||
' <div class="form-item"><label>归属ID<span class="req">*</span></label><input type="number" name="owner_id" value="' + escHtml(m.owner_id) + '" ' + (isEdit ? 'disabled' : '') + '></div>' +
|
||
' <div class="form-item"><label>平台<span class="req">*</span></label>' +
|
||
' <select name="platform" id="media-platform">' + platformOpts(m.platform) + '</select></div>' +
|
||
' <div class="form-item"><label>账号/号码<span class="req">*</span></label>' +
|
||
' <div><input type="text" name="account_id" id="media-account" value="' + escHtml(m.account_id) + '">' +
|
||
' <div class="geo-hint" id="media-account-geo" style="font-size:12px;color:#2f8f46;margin-top:4px;display:none;"></div></div></div>' +
|
||
' <div class="form-item"><label>主页链接</label><input type="text" name="profile_url" value="' + escHtml(m.profile_url) + '"></div>' +
|
||
' <div class="form-item"><label>是否常用</label><select name="is_defult">' +
|
||
' <option value="0"' + (!m.is_defult ? ' selected' : '') + '>备用</option>' +
|
||
' <option value="1"' + (m.is_defult ? ' selected' : '') + '>常用</option></select></div>' +
|
||
' <div class="form-item"><label>默认联系方式</label><select name="is_primary">' +
|
||
' <option value="0"' + (!m.is_primary ? ' selected' : '') + '>否</option>' +
|
||
' <option value="1"' + (m.is_primary ? ' selected' : '') + '>是</option></select></div>' +
|
||
' <div class="form-item"><label>账号等级</label><select name="account_level">' + levelOpts(m.account_level) + '</select></div>' +
|
||
' <div class="form-item"><label>认证类型</label><select name="certification_type">' + certOpts(m.certification_type) + '</select></div>' +
|
||
' <div class="form-item"><label>粉丝数</label><input type="number" name="follower_count" value="' + escHtml(m.follower_count) + '"></div>' +
|
||
' <div class="form-item"><label>平均阅读量</label><input type="number" name="avg_read_count" value="' + escHtml(m.avg_read_count) + '"></div>' +
|
||
' <div class="form-item full"><label>内容领域(逗号分隔)</label><input type="text" name="content_categories" value="' + escHtml(m.content_categories) + '"></div>' +
|
||
' <div class="form-item full"><label>备注</label><textarea name="remark">' + escHtml(m.remark) + '</textarea></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); });
|
||
|
||
// 手机号归属地自动识别(平台为 phone 时,录入号码自动识别归属地)
|
||
var $geo = $('#media-account-geo');
|
||
var bindGeo = function () {
|
||
if ($('#media-platform').val() !== 'phone') { $geo.text('').hide(); return; }
|
||
$('#media-account').off('blur.geo').on('blur.geo', function () {
|
||
var val = $.trim($(this).val());
|
||
if (!val) { $geo.text('').hide(); return; }
|
||
httpGet('common/phone_geo.php', { phone: val }).then(function (d) {
|
||
if (d && d.label && d.label !== '未知') {
|
||
$geo.text('归属地:' + d.label).show();
|
||
} else {
|
||
$geo.text('归属地:未能识别').show();
|
||
}
|
||
}).catch(function () { $geo.text('').hide(); });
|
||
});
|
||
};
|
||
$('#media-platform').on('change', bindGeo);
|
||
bindGeo();
|
||
|
||
$('#media-form').on('submit', function (e) {
|
||
e.preventDefault();
|
||
var data = {};
|
||
$(this).find('input, select, textarea').each(function () {
|
||
var name = $(this).attr('name');
|
||
if (!name) return;
|
||
if ($(this).attr('type') === 'number') {
|
||
data[name] = $(this).val() === '' ? '' : parseInt($(this).val(), 10);
|
||
} else {
|
||
data[name] = $(this).val();
|
||
}
|
||
});
|
||
if (!data.platform || !data.account_id) { Dialog.error('平台和账号为必填项'); return; }
|
||
if (!isEdit && (!data.owner_type || !data.owner_id)) { Dialog.error('归属类型和归属ID为必填项'); return; }
|
||
if (isEdit) data.id = m.id;
|
||
|
||
httpPost(isEdit ? 'media/update.php' : 'media/add.php', data).then(function () {
|
||
Dialog.success('保存成功', function () {
|
||
Dialog.close(idx);
|
||
loadList(currentPage);
|
||
});
|
||
});
|
||
});
|
||
}
|
||
|
||
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 levelOpts(sel) {
|
||
var list = ['头部', '腰部', '尾部', '达人', '素人', '媒体号'];
|
||
var html = '<option value="">-</option>';
|
||
list.forEach(function (p) {
|
||
html += '<option value="' + p + '"' + (sel === p ? ' selected' : '') + '>' + p + '</option>';
|
||
});
|
||
return html;
|
||
}
|
||
function certOpts(sel) {
|
||
var list = ['个人认证', '企业认证', '媒体机构认证', '未认证'];
|
||
var html = '<option value="">-</option>';
|
||
list.forEach(function (p) {
|
||
html += '<option value="' + p + '"' + (sel === p ? ' selected' : '') + '>' + p + '</option>';
|
||
});
|
||
return html;
|
||
}
|
||
|
||
$('#btn-del-batch').on('click', function () {
|
||
var ids = [];
|
||
$('.row-check:checked').each(function () { ids.push($(this).val()); });
|
||
if (!ids.length) { Dialog.error('请先勾选记录'); return; }
|
||
Dialog.confirm('确定删除选中的 ' + ids.length + ' 条记录吗?', function () {
|
||
httpPost('media/delete.php', { ids: ids.join(',') }).then(function () {
|
||
Dialog.success('删除成功', function () { loadList(currentPage); });
|
||
});
|
||
});
|
||
});
|
||
|
||
$('#btn-import').on('click', function () {
|
||
triggerFileInput('.csv', function (file) {
|
||
var fd = new FormData();
|
||
fd.append('file', file);
|
||
httpPost('media/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('media/export.php', { ids: ids.join(',') });
|
||
});
|
||
|
||
window.viewMedia = function (id) {
|
||
httpGet('media/detail.php', { id: id }).then(function (d) {
|
||
var m = d.media;
|
||
var html =
|
||
'<div style="padding:18px 22px;">' +
|
||
' <h3 style="margin-bottom:14px;">' + escHtml(m.platform) + ':' + escHtml(m.account_id) + '</h3>' +
|
||
' <div style="display:grid;grid-template-columns:1fr 1fr;gap:8px 24px;font-size:13px;">' +
|
||
' <div><b>归属:</b>' + escHtml(m.owner_type === 'person' ? '个人' : '企业') + ' #' + m.owner_id + '</div>' +
|
||
' <div><b>主页:</b>' + (m.profile_url ? '<a href="' + escHtml(m.profile_url) + '" target="_blank">打开</a>' : '-') + '</div>' +
|
||
' <div><b>等级:</b>' + escHtml(m.account_level || '-') + '</div>' +
|
||
' <div><b>认证:</b>' + escHtml(m.certification_type || '-') + '</div>' +
|
||
' <div><b>粉丝数:</b>' + (m.follower_count || 0) + '</div>' +
|
||
' <div><b>平均阅读:</b>' + (m.avg_read_count || 0) + '</div>' +
|
||
' <div><b>内容领域:</b>' + escHtml(m.content_categories || '-') + '</div>' +
|
||
' <div><b>创建时间:</b>' + fmtDate(m.created_at) + '</div>' +
|
||
' </div>' +
|
||
' <h4 style="margin:16px 0 8px;">备注</h4><div style="font-size:13px;color:#5a6472;">' + escHtml(m.remark || '-') + '</div>' +
|
||
' <h4 style="margin:16px 0 8px;">特殊要求</h4><div style="font-size:13px;color:#5a6472;">' + escHtml(m.special_requirements || '-') + '</div>' +
|
||
' <h4 style="margin:16px 0 8px;">内部评估</h4><div style="font-size:13px;color:#5a6472;">' + escHtml(m.media_remark || '-') + '</div>' +
|
||
'</div>';
|
||
Dialog.open({ title: '媒体账号详情', area: ['680px', 'auto'], content: html, btn: ['关闭'] });
|
||
});
|
||
}
|
||
});
|