Files
superlink/static/js/media.js
T

304 lines
17 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.
/**
* media.js - 媒体数据管理逻辑(列表+筛选+分页 / 新增编辑弹窗 / 导入导出 / 详情)
*/
$(function () {
renderShell('媒体数据', '数据管理 / 媒体数据');
renderPage();
initPage('media', function (user) {
if (!checkPagePermission('media')) return;
loadList(1);
// 跳转定位:?id= 打开该媒体账号详情(重复校验跳转用)
var jumpId = parseInt(new URLSearchParams(location.search).get('id') || '0', 10);
if (jumpId > 0) window.viewMedia(jumpId);
});
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();
// 录入自动清洗:平台为 phone 时账号去所有空格(含中间),为 email 时去首尾空格
var bindAccountClean = function () {
var plat = $('#media-platform').val();
if (plat === 'phone') {
$('#media-account').off('blur.clean').on('input.clean', function () {
var v = cleanPhone($(this).val());
if ($(this).val() !== v) $(this).val(v);
});
} else if (plat === 'email') {
$('#media-account').off('input.clean').on('blur.clean', function () {
$(this).val(cleanEmail($(this).val()));
});
} else {
$('#media-account').off('input.clean blur.clean');
}
};
$('#media-platform').on('change', bindAccountClean);
bindAccountClean();
$('#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 (data.platform === 'phone') data.account_id = cleanPhone(data.account_id);
else if (data.platform === 'email') data.account_id = cleanEmail(data.account_id);
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: ['关闭'] });
});
}
});