v1.0.20: 需求迭代(0808文档) - 文件管理迁入竞品资料+企业筛选弹窗(多条件且/或)+搜索框内嵌字段+渠道计划费用字段/状态行内编辑
This commit is contained in:
@@ -41,7 +41,7 @@ $total = (int)$stmt->fetchColumn();
|
|||||||
|
|
||||||
$offset = ($page - 1) * $limit;
|
$offset = ($page - 1) * $limit;
|
||||||
$stmt = $pdo->prepare(
|
$stmt = $pdo->prepare(
|
||||||
"SELECT id, channel_type, source_detail, industry, start_date, end_date, remark, status, created_at, updated_at
|
"SELECT id, channel_type, source_detail, industry, start_date, end_date, remark, status, cost, created_at, updated_at
|
||||||
FROM channel_plans
|
FROM channel_plans
|
||||||
WHERE $whereSql
|
WHERE $whereSql
|
||||||
ORDER BY id DESC
|
ORDER BY id DESC
|
||||||
@@ -58,6 +58,8 @@ foreach ($list as &$row) {
|
|||||||
$diff = (int)ceil((strtotime($row['end_date']) - $today) / 86400);
|
$diff = (int)ceil((strtotime($row['end_date']) - $today) / 86400);
|
||||||
$row['remaining_days'] = max(0, $diff);
|
$row['remaining_days'] = max(0, $diff);
|
||||||
}
|
}
|
||||||
|
// cost 转数字(空为 null)
|
||||||
|
$row['cost'] = ($row['cost'] === null || $row['cost'] === '') ? null : (float)$row['cost'];
|
||||||
}
|
}
|
||||||
unset($row);
|
unset($row);
|
||||||
|
|
||||||
|
|||||||
+54
-17
@@ -1,8 +1,9 @@
|
|||||||
<?php
|
<?php
|
||||||
/**
|
/**
|
||||||
* 渠道新增计划保存接口 POST /api/channel/plan_save.php
|
* 渠道新增计划保存接口 POST /api/channel/plan_save.php
|
||||||
* 入参:id(可选,编辑时传)/ channel_type(渠道类别=source_channel 枚举,必填)/
|
* 入参:id(可选,编辑时传)/ channel_type(渠道类别=source_channel 枚举,新增时必填)/
|
||||||
* source_detail / industry / start_date / end_date / remark / status(待启动|已执行|错过)
|
* source_detail / industry / start_date / end_date / remark / status(待启动|已执行|错过)/ cost(费用(元))
|
||||||
|
* 说明:编辑(传 id)时仅更新提交的字段,便于列表行内实时改状态/费用。
|
||||||
*/
|
*/
|
||||||
require_once __DIR__ . '/../common/db.php';
|
require_once __DIR__ . '/../common/db.php';
|
||||||
require_once __DIR__ . '/../common/response.php';
|
require_once __DIR__ . '/../common/response.php';
|
||||||
@@ -13,34 +14,55 @@ require_once __DIR__ . '/../common/logger.php';
|
|||||||
checkAjax();
|
checkAjax();
|
||||||
checkPermission('channel');
|
checkPermission('channel');
|
||||||
|
|
||||||
|
$id = (int)($_POST['id'] ?? 0);
|
||||||
|
|
||||||
|
// 新增时渠道类别必填;编辑时不强制(可能只改状态/费用)
|
||||||
|
if ($id <= 0) {
|
||||||
$channelType = trim($_POST['channel_type'] ?? '');
|
$channelType = trim($_POST['channel_type'] ?? '');
|
||||||
if ($channelType === '') {
|
if ($channelType === '') {
|
||||||
Response::error('渠道类别(channel_type)为必填项', 400);
|
Response::error('渠道类别(channel_type)为必填项', 400);
|
||||||
}
|
}
|
||||||
$status = trim($_POST['status'] ?? '待启动');
|
|
||||||
if (!in_array($status, ['待启动', '已执行', '错过'], true)) {
|
|
||||||
$status = '待启动';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$id = (int)($_POST['id'] ?? 0);
|
$status = trim($_POST['status'] ?? '');
|
||||||
|
if ($status !== '' && !in_array($status, ['待启动', '已执行', '错过'], true)) {
|
||||||
|
Response::error('状态反馈取值不合法', 400);
|
||||||
|
}
|
||||||
|
$cost = trim($_POST['cost'] ?? '');
|
||||||
|
if ($cost !== '' && !is_numeric($cost)) {
|
||||||
|
Response::error('费用(元)必须为数字', 400);
|
||||||
|
}
|
||||||
|
|
||||||
$pdo = DB::getInstance()->getPdo();
|
$pdo = DB::getInstance()->getPdo();
|
||||||
|
|
||||||
$fields = [
|
/** 从 POST 收集需要写入的字段(空串转 null) */
|
||||||
'channel_type' => $channelType,
|
$fields = [];
|
||||||
'source_detail' => trim($_POST['source_detail'] ?? '') !== '' ? trim($_POST['source_detail']) : null,
|
$collect = function ($key) use (&$fields) {
|
||||||
'industry' => trim($_POST['industry'] ?? '') !== '' ? trim($_POST['industry']) : null,
|
if (isset($_POST[$key])) {
|
||||||
'start_date' => trim($_POST['start_date'] ?? '') !== '' ? $_POST['start_date'] : null,
|
$v = trim((string)$_POST[$key]);
|
||||||
'end_date' => trim($_POST['end_date'] ?? '') !== '' ? $_POST['end_date'] : null,
|
$fields[$key] = $v !== '' ? $v : null;
|
||||||
'remark' => trim($_POST['remark'] ?? '') !== '' ? trim($_POST['remark']) : null,
|
}
|
||||||
'status' => $status,
|
};
|
||||||
];
|
|
||||||
|
|
||||||
if ($id > 0) {
|
if ($id > 0) {
|
||||||
$check = $pdo->prepare("SELECT id FROM channel_plans WHERE id = ? AND is_active = 1");
|
$check = $pdo->prepare("SELECT id, status FROM channel_plans WHERE id = ? AND is_active = 1");
|
||||||
$check->execute([$id]);
|
$check->execute([$id]);
|
||||||
if (!$check->fetch()) {
|
$cur = $check->fetch();
|
||||||
|
if (!$cur) {
|
||||||
Response::error('计划不存在');
|
Response::error('计划不存在');
|
||||||
}
|
}
|
||||||
|
// 只更新提交的字段
|
||||||
|
foreach (['channel_type', 'source_detail', 'industry', 'start_date', 'end_date', 'remark', 'status'] as $k) {
|
||||||
|
$collect($k);
|
||||||
|
}
|
||||||
|
if (isset($_POST['cost'])) {
|
||||||
|
$c = trim((string)$_POST['cost']);
|
||||||
|
$fields['cost'] = $c !== '' ? round((float)$c, 2) : null;
|
||||||
|
}
|
||||||
|
// 若只改了状态为「已执行」而原费用为空,前端会单独提交 cost;此处不自动置值
|
||||||
|
if (!$fields) {
|
||||||
|
Response::error('没有需要更新的字段', 400);
|
||||||
|
}
|
||||||
[$sets, $params] = buildUpdate($fields);
|
[$sets, $params] = buildUpdate($fields);
|
||||||
$params[] = $id;
|
$params[] = $id;
|
||||||
$pdo->prepare("UPDATE channel_plans SET $sets WHERE id = ?")->execute($params);
|
$pdo->prepare("UPDATE channel_plans SET $sets WHERE id = ?")->execute($params);
|
||||||
@@ -48,6 +70,21 @@ if ($id > 0) {
|
|||||||
Response::success(null, '更新成功');
|
Response::success(null, '更新成功');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 新增:渠道类别必填
|
||||||
|
$channelType = trim($_POST['channel_type'] ?? '');
|
||||||
|
if ($channelType === '') {
|
||||||
|
Response::error('渠道类别(channel_type)为必填项', 400);
|
||||||
|
}
|
||||||
|
$fields = [
|
||||||
|
'channel_type' => $channelType,
|
||||||
|
'source_detail' => strOrNull($_POST['source_detail'] ?? null),
|
||||||
|
'industry' => strOrNull($_POST['industry'] ?? null),
|
||||||
|
'start_date' => strOrNull($_POST['start_date'] ?? null),
|
||||||
|
'end_date' => strOrNull($_POST['end_date'] ?? null),
|
||||||
|
'remark' => strOrNull($_POST['remark'] ?? null),
|
||||||
|
'status' => $status !== '' ? $status : '待启动',
|
||||||
|
'cost' => $cost !== '' ? round((float)$cost, 2) : null,
|
||||||
|
];
|
||||||
[$sql, $params] = buildInsert($fields);
|
[$sql, $params] = buildInsert($fields);
|
||||||
$pdo->prepare("INSERT INTO channel_plans $sql")->execute($params);
|
$pdo->prepare("INSERT INTO channel_plans $sql")->execute($params);
|
||||||
$newId = (int)$pdo->lastInsertId();
|
$newId = (int)$pdo->lastInsertId();
|
||||||
|
|||||||
@@ -45,6 +45,23 @@ function checkPermission($module)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验当前用户是否拥有多个模块权限中的任意一个(同时校验登录态)
|
||||||
|
* 用途:功能迁移场景(如文件管理内容迁入竞品资料,document 与 competitor_data 任一权限可访问)
|
||||||
|
* @param array $modules 菜单标识数组
|
||||||
|
*/
|
||||||
|
function checkAnyPermission($modules)
|
||||||
|
{
|
||||||
|
requireLogin();
|
||||||
|
$perms = $_SESSION['permissions'] ?? [];
|
||||||
|
foreach ((array)$modules as $m) {
|
||||||
|
if (in_array($m, $perms, true)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Response::error('无权操作', 403);
|
||||||
|
}
|
||||||
|
|
||||||
/** 仅超级管理员可操作(如操作日志查询) */
|
/** 仅超级管理员可操作(如操作日志查询) */
|
||||||
function requireSuperAdmin()
|
function requireSuperAdmin()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -34,6 +34,75 @@ if ($active !== null) {
|
|||||||
$where = ['is_active = ?'];
|
$where = ['is_active = ?'];
|
||||||
$params = [$active];
|
$params = [$active];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 筛选弹窗多字段条件:filters=JSON数组 [{field,op,value}],filter_logic=and|or
|
||||||
|
// 注意:必须在此处($where/$params 初始化后)追加,否则会被上面的赋值覆盖
|
||||||
|
$filtersJson = trim($_REQUEST['filters'] ?? '');
|
||||||
|
$filterLogic = strtoupper(trim($_REQUEST['filter_logic'] ?? 'and')) === 'OR' ? 'OR' : 'AND';
|
||||||
|
$filterConds = [];
|
||||||
|
if ($filtersJson !== '') {
|
||||||
|
$decoded = json_decode($filtersJson, true);
|
||||||
|
if (is_array($decoded)) {
|
||||||
|
foreach ($decoded as $c) {
|
||||||
|
$fField = trim($c['field'] ?? '');
|
||||||
|
$fOp = trim($c['op'] ?? '');
|
||||||
|
$fVal = trim((string)($c['value'] ?? ''));
|
||||||
|
if ($fField === '' || !in_array($fField, $searchableFields, true)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
switch ($fOp) {
|
||||||
|
case 'contains':
|
||||||
|
$filterConds[] = "$fField LIKE ?";
|
||||||
|
$params[] = "%$fVal%";
|
||||||
|
break;
|
||||||
|
case 'eq':
|
||||||
|
$filterConds[] = "$fField = ?";
|
||||||
|
$params[] = $fVal;
|
||||||
|
break;
|
||||||
|
case 'neq':
|
||||||
|
$filterConds[] = "$fField <> ?";
|
||||||
|
$params[] = $fVal;
|
||||||
|
break;
|
||||||
|
case 'starts_with':
|
||||||
|
$filterConds[] = "$fField LIKE ?";
|
||||||
|
$params[] = "$fVal%";
|
||||||
|
break;
|
||||||
|
case 'ends_with':
|
||||||
|
$filterConds[] = "$fField LIKE ?";
|
||||||
|
$params[] = "%$fVal";
|
||||||
|
break;
|
||||||
|
case 'gt':
|
||||||
|
$filterConds[] = "$fField > ?";
|
||||||
|
$params[] = $fVal;
|
||||||
|
break;
|
||||||
|
case 'lt':
|
||||||
|
$filterConds[] = "$fField < ?";
|
||||||
|
$params[] = $fVal;
|
||||||
|
break;
|
||||||
|
case 'gte':
|
||||||
|
$filterConds[] = "$fField >= ?";
|
||||||
|
$params[] = $fVal;
|
||||||
|
break;
|
||||||
|
case 'lte':
|
||||||
|
$filterConds[] = "$fField <= ?";
|
||||||
|
$params[] = $fVal;
|
||||||
|
break;
|
||||||
|
case 'is_empty':
|
||||||
|
$filterConds[] = "($fField IS NULL OR $fField = '')";
|
||||||
|
break;
|
||||||
|
case 'is_not_empty':
|
||||||
|
$filterConds[] = "($fField IS NOT NULL AND $fField <> '')";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($filterConds) {
|
||||||
|
$where[] = '(' . implode(" $filterLogic ", $filterConds) . ')';
|
||||||
|
}
|
||||||
|
|
||||||
if ($keyword !== '') {
|
if ($keyword !== '') {
|
||||||
if ($field !== '' && in_array($field, $searchableFields, true)) {
|
if ($field !== '' && in_array($field, $searchableFields, true)) {
|
||||||
$where[] = "$field LIKE ?";
|
$where[] = "$field LIKE ?";
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ require_once __DIR__ . '/../common/auth.php';
|
|||||||
require_once __DIR__ . '/../common/logger.php';
|
require_once __DIR__ . '/../common/logger.php';
|
||||||
|
|
||||||
checkAjax();
|
checkAjax();
|
||||||
checkPermission('document');
|
checkAnyPermission(['document', 'competitor_data']);
|
||||||
|
|
||||||
$id = (int)($_POST['id'] ?? 0);
|
$id = (int)($_POST['id'] ?? 0);
|
||||||
$ids = trim($_POST['ids'] ?? '');
|
$ids = trim($_POST['ids'] ?? '');
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ require_once __DIR__ . '/../common/db.php';
|
|||||||
require_once __DIR__ . '/../common/response.php';
|
require_once __DIR__ . '/../common/response.php';
|
||||||
require_once __DIR__ . '/../common/auth.php';
|
require_once __DIR__ . '/../common/auth.php';
|
||||||
|
|
||||||
checkPermission('document');
|
checkAnyPermission(['document', 'competitor_data']);
|
||||||
|
|
||||||
[$page, $limit] = pageParams();
|
[$page, $limit] = pageParams();
|
||||||
$keyword = trim($_REQUEST['keyword'] ?? '');
|
$keyword = trim($_REQUEST['keyword'] ?? '');
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ require_once __DIR__ . '/../common/auth.php';
|
|||||||
require_once __DIR__ . '/../common/logger.php';
|
require_once __DIR__ . '/../common/logger.php';
|
||||||
|
|
||||||
checkAjax();
|
checkAjax();
|
||||||
checkPermission('document');
|
checkAnyPermission(['document', 'competitor_data']);
|
||||||
|
|
||||||
if (empty($_FILES['file']) || $_FILES['file']['error'] !== UPLOAD_ERR_OK) {
|
if (empty($_FILES['file']) || $_FILES['file']['error'] !== UPLOAD_ERR_OK) {
|
||||||
Response::error('请选择要上传的文件', 400);
|
Response::error('请选择要上传的文件', 400);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="zh-CN">
|
<html lang="zh-CN">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
@@ -12,7 +12,6 @@
|
|||||||
<script src="static/libs/layer.js"></script>
|
<script src="static/libs/layer.js"></script>
|
||||||
<script src="static/js/config.js"></script>
|
<script src="static/js/config.js"></script>
|
||||||
<script src="static/js/common.js"></script>
|
<script src="static/js/common.js"></script>
|
||||||
<script src="static/js/placeholder.js"></script>
|
<script src="static/js/competitor_data.js"></script>
|
||||||
<script>renderPlaceholder('competitor_data');</script>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
-- ============================================================
|
||||||
|
-- SuperLink Web - v1.0.20 数据库结构变更
|
||||||
|
-- 需求文档:0808---62b20256-32fc-48ce-9164-4d05b6e83f36.docx
|
||||||
|
-- 渠道新增计划增加「费用(元)」字段(仅已执行状态可填写)
|
||||||
|
-- ============================================================
|
||||||
|
SET NAMES utf8mb4;
|
||||||
|
|
||||||
|
ALTER TABLE `channel_plans`
|
||||||
|
ADD COLUMN `cost` DECIMAL(12,2) NULL DEFAULT NULL COMMENT '费用(元),仅已执行状态可填写' AFTER `status`;
|
||||||
@@ -287,6 +287,64 @@ body {
|
|||||||
.toolbar input:focus, .toolbar select:focus { border-color: #4fa3ff; }
|
.toolbar input:focus, .toolbar select:focus { border-color: #4fa3ff; }
|
||||||
.toolbar .spacer { flex: 1; }
|
.toolbar .spacer { flex: 1; }
|
||||||
|
|
||||||
|
/* 搜索框:框内左侧字段选择 + 框内右侧搜索图标 */
|
||||||
|
.toolbar .search-wrap {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
height: 34px;
|
||||||
|
border: 1px solid #d9dee8;
|
||||||
|
border-radius: 4px;
|
||||||
|
background: #fff;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.toolbar .search-wrap:focus-within { border-color: #4fa3ff; }
|
||||||
|
.toolbar .search-wrap select {
|
||||||
|
border: none;
|
||||||
|
height: 100%;
|
||||||
|
min-width: 108px;
|
||||||
|
max-width: 150px;
|
||||||
|
border-right: 1px solid #eef0f4;
|
||||||
|
border-radius: 0;
|
||||||
|
background: #f7f9fc;
|
||||||
|
font-size: 12px;
|
||||||
|
padding: 0 8px;
|
||||||
|
}
|
||||||
|
.toolbar .search-wrap input[type="text"] {
|
||||||
|
border: none;
|
||||||
|
height: 100%;
|
||||||
|
min-width: 220px;
|
||||||
|
border-radius: 0;
|
||||||
|
box-shadow: none;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
.toolbar .search-wrap .search-icon {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 34px;
|
||||||
|
height: 100%;
|
||||||
|
color: #8a94a6;
|
||||||
|
cursor: pointer;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
.toolbar .search-wrap .search-icon:hover { color: #2a5298; }
|
||||||
|
|
||||||
|
/* 筛选弹窗条件行 */
|
||||||
|
.filter-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 渠道效能分析-来源明细:单行显示,过长用…占位(不分行) */
|
||||||
|
.ana-detail .src-cell {
|
||||||
|
max-width: 200px;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
/* ---------- 按钮 ---------- */
|
/* ---------- 按钮 ---------- */
|
||||||
.btn {
|
.btn {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
|||||||
+59
-8
@@ -58,7 +58,6 @@ $(function () {
|
|||||||
' <span style="font-size:12px;color:#8a94a6;">根据历史数据有计划地拓展高效渠道</span>' +
|
' <span style="font-size:12px;color:#8a94a6;">根据历史数据有计划地拓展高效渠道</span>' +
|
||||||
' </div>' +
|
' </div>' +
|
||||||
' <div class="toolbar">' +
|
' <div class="toolbar">' +
|
||||||
' <input type="text" data-filter="keyword" placeholder="来源详情/行业/备注关键词">' +
|
|
||||||
' <select data-filter="channel_type"><option value="">全部渠道类别</option></select>' +
|
' <select data-filter="channel_type"><option value="">全部渠道类别</option></select>' +
|
||||||
' <select data-filter="status"><option value="">全部状态</option>' +
|
' <select data-filter="status"><option value="">全部状态</option>' +
|
||||||
' <option value="待启动">待启动</option><option value="已执行">已执行</option><option value="错过">错过</option></select>' +
|
' <option value="待启动">待启动</option><option value="已执行">已执行</option><option value="错过">错过</option></select>' +
|
||||||
@@ -68,7 +67,7 @@ $(function () {
|
|||||||
' <button class="btn btn-success" id="btn-plan-add">+ 新增计划</button>' +
|
' <button class="btn btn-success" id="btn-plan-add">+ 新增计划</button>' +
|
||||||
' </div>' +
|
' </div>' +
|
||||||
' <div class="table-wrap"><table class="grid">' +
|
' <div class="table-wrap"><table class="grid">' +
|
||||||
' <thead><tr><th>ID</th><th>渠道类别</th><th>来源详情</th><th>所属行业</th><th>时间窗口</th><th>剩余天数</th><th>备注说明</th><th>状态反馈</th><th>创建时间</th><th>操作</th></tr></thead>' +
|
' <thead><tr><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="plan-tbody"></tbody>' +
|
' <tbody id="plan-tbody"></tbody>' +
|
||||||
' </table></div>' +
|
' </table></div>' +
|
||||||
' <div class="pagination" id="plan-pagination"></div>' +
|
' <div class="pagination" id="plan-pagination"></div>' +
|
||||||
@@ -168,8 +167,7 @@ $(function () {
|
|||||||
var html = '';
|
var html = '';
|
||||||
(r.details || []).forEach(function (item) {
|
(r.details || []).forEach(function (item) {
|
||||||
var src = item.source || '';
|
var src = item.source || '';
|
||||||
var short = src.length > 8 ? src.substring(0, 8) + '…' : src;
|
html += '<tr><td class="src-cell" title="' + escHtml(src) + '">' + escHtml(src) + '</td><td>' + (item.count !== undefined ? item.count : item.cnt) + '</td></tr>';
|
||||||
html += '<tr><td title="' + escHtml(src) + '">' + escHtml(short) + '</td><td>' + (item.count !== undefined ? item.count : item.cnt) + '</td></tr>';
|
|
||||||
});
|
});
|
||||||
if (!(r.details || []).length) {
|
if (!(r.details || []).length) {
|
||||||
html = '<tr><td colspan="2" style="color:#b6bfcc;text-align:center;padding:16px;">暂无明细</td></tr>';
|
html = '<tr><td colspan="2" style="color:#b6bfcc;text-align:center;padding:16px;">暂无明细</td></tr>';
|
||||||
@@ -217,6 +215,12 @@ $(function () {
|
|||||||
var win = r.start_date && r.end_date
|
var win = r.start_date && r.end_date
|
||||||
? fmtWin(r.start_date) + ' 至 ' + fmtWin(r.end_date)
|
? fmtWin(r.start_date) + ' 至 ' + fmtWin(r.end_date)
|
||||||
: (r.start_date || r.end_date || '-');
|
: (r.start_date || r.end_date || '-');
|
||||||
|
var statusSel = '<select class="plan-status" data-id="' + r.id + '" style="height:28px;padding:0 6px;font-size:12px;border:1px solid #d9dee8;border-radius:4px;">' +
|
||||||
|
'<option value="待启动"' + (r.status === '待启动' ? ' selected' : '') + '>待启动</option>' +
|
||||||
|
'<option value="已执行"' + (r.status === '已执行' ? ' selected' : '') + '>已执行</option>' +
|
||||||
|
'<option value="错过"' + (r.status === '错过' ? ' selected' : '') + '>错过</option></select>';
|
||||||
|
var costInput = '<input type="text" class="plan-cost" data-id="' + r.id + '" value="' + escHtml(r.cost !== null && r.cost !== undefined ? r.cost : '') + '" placeholder="0.00"' +
|
||||||
|
(r.status === '已执行' ? '' : ' disabled style="background:#f2f4f8;color:#8a94a6;"') + ' style="width:90px;height:28px;padding:0 6px;font-size:12px;border:1px solid #d9dee8;border-radius:4px;text-align:right;">';
|
||||||
html += '<tr>' +
|
html += '<tr>' +
|
||||||
'<td>' + r.id + '</td>' +
|
'<td>' + r.id + '</td>' +
|
||||||
'<td>' + escHtml(r.channel_type) + '</td>' +
|
'<td>' + escHtml(r.channel_type) + '</td>' +
|
||||||
@@ -225,19 +229,55 @@ $(function () {
|
|||||||
'<td>' + escHtml(win) + '</td>' +
|
'<td>' + escHtml(win) + '</td>' +
|
||||||
'<td>' + (r.remaining_days > 0 ? '<span class="tag' + (r.remaining_days <= 7 ? ' tag-red' : '') + '">' + r.remaining_days + ' 天</span>' : '<span class="tag tag-gray">已到期</span>') + '</td>' +
|
'<td>' + (r.remaining_days > 0 ? '<span class="tag' + (r.remaining_days <= 7 ? ' tag-red' : '') + '">' + r.remaining_days + ' 天</span>' : '<span class="tag tag-gray">已到期</span>') + '</td>' +
|
||||||
'<td title="' + escHtml(r.remark || '') + '">' + escHtml((r.remark || '-').substring(0, 20)) + '</td>' +
|
'<td title="' + escHtml(r.remark || '') + '">' + escHtml((r.remark || '-').substring(0, 20)) + '</td>' +
|
||||||
'<td>' + statusTag(r.status) + '</td>' +
|
'<td>' + statusSel + '</td>' +
|
||||||
'<td>' + fmtDate(r.created_at) + '</td>' +
|
'<td>' + costInput + '</td>' +
|
||||||
'<td class="ops"><a onclick="editPlan(' + r.id + ')">编辑</a>' +
|
'<td>' + fmtDate(r.created_at) + '</td></tr>';
|
||||||
'<a class="danger" onclick="delPlan(' + r.id + ')">删除</a></td></tr>';
|
|
||||||
});
|
});
|
||||||
if (!rows.length) {
|
if (!rows.length) {
|
||||||
html = '<tr><td colspan="10" class="empty-tip" style="padding:40px 0;">暂无渠道新增计划</td></tr>';
|
html = '<tr><td colspan="10" class="empty-tip" style="padding:40px 0;">暂无渠道新增计划</td></tr>';
|
||||||
}
|
}
|
||||||
$('#plan-tbody').html(html);
|
$('#plan-tbody').html(html);
|
||||||
|
bindPlanInlineEdit();
|
||||||
renderPagination($('#plan-pagination'), data, loadPlans);
|
renderPagination($('#plan-pagination'), data, loadPlans);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 行内实时编辑:状态下拉随时改;费用(元)仅在「已执行」时激活可输入 */
|
||||||
|
function bindPlanInlineEdit() {
|
||||||
|
$('.plan-status').off('change.plan').on('change.plan', function () {
|
||||||
|
var id = $(this).data('id');
|
||||||
|
var status = $(this).val();
|
||||||
|
var $row = $(this).closest('tr');
|
||||||
|
var $cost = $row.find('.plan-cost');
|
||||||
|
if (status === '已执行') {
|
||||||
|
$cost.prop('disabled', false).css({ background: '#fff', color: '#1e2a3a' });
|
||||||
|
} else {
|
||||||
|
$cost.prop('disabled', true).css({ background: '#f2f4f8', color: '#8a94a6' });
|
||||||
|
}
|
||||||
|
httpPost('channel/plan_save.php', { id: id, status: status }).then(function () {
|
||||||
|
Dialog.toast('状态已更新为「' + status + '」');
|
||||||
|
}).catch(function () {
|
||||||
|
loadPlans(currentPage);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
$('.plan-cost').off('change.plan').on('change.plan', function () {
|
||||||
|
var id = $(this).data('id');
|
||||||
|
var val = $.trim($(this).val());
|
||||||
|
if (val === '') return;
|
||||||
|
if (isNaN(parseFloat(val))) {
|
||||||
|
Dialog.error('费用(元)请输入数字');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var cost = Math.round(parseFloat(val) * 100) / 100;
|
||||||
|
$(this).val(cost);
|
||||||
|
httpPost('channel/plan_save.php', { id: id, cost: cost }).then(function () {
|
||||||
|
Dialog.toast('费用已更新:¥' + cost.toFixed(2));
|
||||||
|
}).catch(function () {
|
||||||
|
loadPlans(currentPage);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/** 时间窗口显示格式:2025-4-1 */
|
/** 时间窗口显示格式:2025-4-1 */
|
||||||
function fmtWin(d) {
|
function fmtWin(d) {
|
||||||
if (!d) return '';
|
if (!d) return '';
|
||||||
@@ -295,6 +335,8 @@ $(function () {
|
|||||||
' <option value="待启动"' + (!r.status || r.status === '待启动' ? ' selected' : '') + '>待启动</option>' +
|
' <option value="待启动"' + (!r.status || r.status === '待启动' ? ' selected' : '') + '>待启动</option>' +
|
||||||
' <option value="已执行"' + (r.status === '已执行' ? ' selected' : '') + '>已执行</option>' +
|
' <option value="已执行"' + (r.status === '已执行' ? ' selected' : '') + '>已执行</option>' +
|
||||||
' <option value="错过"' + (r.status === '错过' ? ' selected' : '') + '>错过</option></select></div>' +
|
' <option value="错过"' + (r.status === '错过' ? ' selected' : '') + '>错过</option></select></div>' +
|
||||||
|
' <div class="form-item"><label>费用(元)</label><input type="text" name="cost" value="' + escHtml(r.cost !== null && r.cost !== undefined ? r.cost : '') + '" placeholder="仅已执行时可填写"' +
|
||||||
|
(r.status === '已执行' ? '' : ' disabled style="background:#f2f4f8;color:#8a94a6;"') + '></div>' +
|
||||||
' <div class="form-item"><label>开始日期</label><input type="date" name="start_date" value="' + escHtml(r.start_date || '') + '"></div>' +
|
' <div class="form-item"><label>开始日期</label><input type="date" name="start_date" value="' + escHtml(r.start_date || '') + '"></div>' +
|
||||||
' <div class="form-item"><label>结束日期</label><input type="date" name="end_date" value="' + escHtml(r.end_date || '') + '"></div>' +
|
' <div class="form-item"><label>结束日期</label><input type="date" name="end_date" value="' + escHtml(r.end_date || '') + '"></div>' +
|
||||||
' <div class="form-item full"><label>备注说明</label><input type="text" name="remark" value="' + escHtml(r.remark || '') + '"></div>' +
|
' <div class="form-item full"><label>备注说明</label><input type="text" name="remark" value="' + escHtml(r.remark || '') + '"></div>' +
|
||||||
@@ -312,6 +354,15 @@ $(function () {
|
|||||||
|
|
||||||
$('#plan-form-cancel').on('click', function () { Dialog.close(idx); });
|
$('#plan-form-cancel').on('click', function () { Dialog.close(idx); });
|
||||||
|
|
||||||
|
// 状态反馈 → 费用(元) 联动:仅「已执行」可填写
|
||||||
|
$('#plan-form select[name="status"]').on('change', function () {
|
||||||
|
var enabled = $(this).val() === '已执行';
|
||||||
|
var $cost = $('#plan-form input[name="cost"]');
|
||||||
|
$cost.prop('disabled', !enabled);
|
||||||
|
$cost.css({ background: enabled ? '#fff' : '#f2f4f8', color: enabled ? '#1e2a3a' : '#8a94a6' });
|
||||||
|
if (!enabled) $cost.val('');
|
||||||
|
});
|
||||||
|
|
||||||
$('#plan-form').on('submit', function (e) {
|
$('#plan-form').on('submit', function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
var data = {};
|
var data = {};
|
||||||
|
|||||||
+108
-5
@@ -28,17 +28,20 @@ $(function () {
|
|||||||
$('#page-content').html(
|
$('#page-content').html(
|
||||||
'<div class="card">' +
|
'<div class="card">' +
|
||||||
' <div class="toolbar">' +
|
' <div class="toolbar">' +
|
||||||
' <select id="search-field"><option value="">全部字段</option>' +
|
' <button class="btn" id="btn-filter" title="按字段设置多个筛选条件(支持 且/或 逻辑)">筛选</button>' +
|
||||||
|
' <div class="search-wrap">' +
|
||||||
|
' <select id="search-field"><option value="">全字段</option>' +
|
||||||
' <option value="name_zh">企业名称</option><option value="name_en">英文名称</option>' +
|
' <option value="name_zh">企业名称</option><option value="name_en">英文名称</option>' +
|
||||||
' <option value="registration_number">注册号</option><option value="legal_representative">法定代表人</option>' +
|
' <option value="registration_number">注册号</option><option value="legal_representative">法定代表人</option>' +
|
||||||
' <option value="address">注册地址</option><option value="industry">行业</option>' +
|
' <option value="address">注册地址</option><option value="industry">行业</option>' +
|
||||||
' <option value="industry_subdivision">行业细分</option><option value="website">官网</option>' +
|
' <option value="industry_subdivision">行业细分</option><option value="website">官网</option>' +
|
||||||
' <option value="source_channel">来源渠道</option><option value="source_detail">来源详情</option>' +
|
' <option value="source_channel">来源渠道</option><option value="source_detail">来源详情</option>' +
|
||||||
' <option value="business_scope">经营范围</option><option value="stock_code">股票代码</option></select>' +
|
' <option value="business_scope">经营范围</option><option value="stock_code">股票代码</option></select>' +
|
||||||
' <input type="text" id="search-keyword" placeholder="输入要检索的内容">' +
|
' <input type="text" id="search-keyword" placeholder="输入关键词,回车搜索">' +
|
||||||
|
' <span class="search-icon" id="btn-search" title="搜索"><svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg></span>' +
|
||||||
|
' </div>' +
|
||||||
' <select id="search-industry" data-filter="industry"><option value="">全部行业</option></select>' +
|
' <select id="search-industry" data-filter="industry"><option value="">全部行业</option></select>' +
|
||||||
' <select id="search-company-type" data-filter="company_type"><option value="">全部企业类型</option></select>' +
|
' <select id="search-company-type" data-filter="company_type"><option value="">全部业务类型</option></select>' +
|
||||||
' <button class="btn btn-primary" id="btn-search">搜索</button>' +
|
|
||||||
' <button class="btn" id="btn-reset">重置</button>' +
|
' <button class="btn" id="btn-reset">重置</button>' +
|
||||||
' <span class="spacer"></span>' +
|
' <span class="spacer"></span>' +
|
||||||
' <button class="btn btn-success" id="btn-add">+ 新增</button>' +
|
' <button class="btn btn-success" id="btn-add">+ 新增</button>' +
|
||||||
@@ -71,6 +74,8 @@ $(function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* ---------- 列表 ---------- */
|
/* ---------- 列表 ---------- */
|
||||||
|
var companyFilters = { logic: 'and', conditions: [] }; // 筛选弹窗条件(多字段 + 且/或)
|
||||||
|
|
||||||
function loadList(page) {
|
function loadList(page) {
|
||||||
currentPage = page;
|
currentPage = page;
|
||||||
var params = {
|
var params = {
|
||||||
@@ -81,6 +86,11 @@ $(function () {
|
|||||||
page: page,
|
page: page,
|
||||||
limit: PAGE_SIZE
|
limit: PAGE_SIZE
|
||||||
};
|
};
|
||||||
|
// 筛选弹窗条件(多字段 + 逻辑关系)
|
||||||
|
if (companyFilters.conditions.length) {
|
||||||
|
params.filters = JSON.stringify(companyFilters.conditions);
|
||||||
|
params.filter_logic = companyFilters.logic;
|
||||||
|
}
|
||||||
// 空值不传
|
// 空值不传
|
||||||
Object.keys(params).forEach(function (k) {
|
Object.keys(params).forEach(function (k) {
|
||||||
if (params[k] === '') delete params[k];
|
if (params[k] === '') delete params[k];
|
||||||
@@ -118,17 +128,110 @@ $(function () {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---------- 筛选 ---------- */
|
/* ---------- 检索:搜索(字段范围+关键词) / 筛选弹窗 / 行业+业务类型快速筛选 ---------- */
|
||||||
$('#btn-search').on('click', function () { loadList(1); });
|
$('#btn-search').on('click', function () { loadList(1); });
|
||||||
$('#search-keyword').on('keydown', function (e) { if (e.keyCode === 13) loadList(1); });
|
$('#search-keyword').on('keydown', function (e) { if (e.keyCode === 13) loadList(1); });
|
||||||
|
$('#search-industry, #search-company-type').on('change', function () { loadList(1); });
|
||||||
$('#btn-reset').on('click', function () {
|
$('#btn-reset').on('click', function () {
|
||||||
$('#search-field').val('');
|
$('#search-field').val('');
|
||||||
$('#search-keyword').val('');
|
$('#search-keyword').val('');
|
||||||
$('#search-industry').val('');
|
$('#search-industry').val('');
|
||||||
$('#search-company-type').val('');
|
$('#search-company-type').val('');
|
||||||
|
companyFilters = { logic: 'and', conditions: [] };
|
||||||
loadList(1);
|
loadList(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/* ---------- 筛选弹窗:多字段条件 + 且/或 逻辑关系 ---------- */
|
||||||
|
var FILTER_FIELDS = [
|
||||||
|
['name_zh', '企业名称'], ['name_en', '英文名称'], ['registration_number', '注册号'],
|
||||||
|
['legal_representative', '法定代表人'], ['address', '注册地址'], ['country', '国家/地区'],
|
||||||
|
['industry', '行业'], ['industry_subdivision', '行业细分'], ['legal_form', '企业类型'],
|
||||||
|
['business_role', '业务角色'], ['website', '官网'], ['source_channel', '来源渠道'],
|
||||||
|
['source_detail', '来源详情'], ['business_scope', '经营范围'], ['stock_code', '股票代码'],
|
||||||
|
['established_date', '成立日期'], ['latest_employee_count', '最新员工数'],
|
||||||
|
['latest_annual_revenue', '最新年营收']
|
||||||
|
];
|
||||||
|
var FILTER_OPS = [
|
||||||
|
['contains', '包含'], ['eq', '等于'], ['neq', '不等于'],
|
||||||
|
['starts_with', '开头是'], ['ends_with', '结尾是'],
|
||||||
|
['gt', '大于'], ['lt', '小于'], ['gte', '大于等于'], ['lte', '小于等于'],
|
||||||
|
['is_empty', '为空'], ['is_not_empty', '不为空']
|
||||||
|
];
|
||||||
|
|
||||||
|
function filterFieldOptions(cur) {
|
||||||
|
var h = '';
|
||||||
|
FILTER_FIELDS.forEach(function (f) {
|
||||||
|
h += '<option value="' + f[0] + '"' + (f[0] === cur ? ' selected' : '') + '>' + f[1] + '</option>';
|
||||||
|
});
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
function filterOpOptions(cur) {
|
||||||
|
var h = '';
|
||||||
|
FILTER_OPS.forEach(function (o) {
|
||||||
|
h += '<option value="' + o[0] + '"' + (o[0] === cur ? ' selected' : '') + '>' + o[1] + '</option>';
|
||||||
|
});
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
function filterRowHtml(c) {
|
||||||
|
c = c || {};
|
||||||
|
var valInput = (c.op === 'is_empty' || c.op === 'is_not_empty')
|
||||||
|
? '<input type="text" value="" disabled placeholder="(无需填值)" style="background:#f2f4f8;color:#8a94a6;width:180px;">'
|
||||||
|
: '<input type="text" value="' + escHtml(c.value || '') + '" placeholder="条件值" style="width:180px;">';
|
||||||
|
return '<div class="filter-row">' +
|
||||||
|
' <select class="f-field" style="width:150px;">' + filterFieldOptions(c.field) + '</select>' +
|
||||||
|
' <select class="f-op" style="width:110px;">' + filterOpOptions(c.op) + '</select>' +
|
||||||
|
valInput +
|
||||||
|
' <button type="button" class="btn btn-sm btn-danger" onclick="$(this).closest(\'.filter-row\').remove()">删除</button>' +
|
||||||
|
'</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
window.openFilterDialog = function () {
|
||||||
|
var logicHtml =
|
||||||
|
'<label style="margin-right:14px;font-size:13px;"><input type="radio" name="f-logic" value="and"' +
|
||||||
|
(companyFilters.logic !== 'or' ? ' checked' : '') + '> 且(同时满足)</label>' +
|
||||||
|
'<label style="font-size:13px;"><input type="radio" name="f-logic" value="or"' +
|
||||||
|
(companyFilters.logic === 'or' ? ' checked' : '') + '> 或(任一满足)</label>';
|
||||||
|
var rowsHtml = '';
|
||||||
|
if (companyFilters.conditions.length) {
|
||||||
|
companyFilters.conditions.forEach(function (c) { rowsHtml += filterRowHtml(c); });
|
||||||
|
} else {
|
||||||
|
rowsHtml = filterRowHtml(null);
|
||||||
|
}
|
||||||
|
var content =
|
||||||
|
'<div style="padding:16px 20px 6px;">' +
|
||||||
|
' <div style="margin-bottom:10px;font-size:13px;color:#5a6472;">每个字段是一个筛选条件,不同条件之间可为「且」「或」关系:</div>' +
|
||||||
|
' <div style="margin-bottom:10px;">' + logicHtml + '</div>' +
|
||||||
|
' <div id="filter-rows">' + rowsHtml + '</div>' +
|
||||||
|
' <button type="button" class="btn btn-sm" id="btn-add-filter" style="margin-top:8px;">+ 添加条件</button>' +
|
||||||
|
'</div>' +
|
||||||
|
'<div class="dialog-footer"><button type="button" class="btn" id="filter-cancel">取消</button>' +
|
||||||
|
'<button type="button" class="btn btn-primary" id="filter-apply">应用筛选</button></div>';
|
||||||
|
var idx = Dialog.open({
|
||||||
|
title: '设置筛选条件',
|
||||||
|
area: ['620px', 'auto'],
|
||||||
|
content: content,
|
||||||
|
btn: false
|
||||||
|
});
|
||||||
|
$('#btn-add-filter').on('click', function () { $('#filter-rows').append(filterRowHtml(null)); });
|
||||||
|
$('#filter-cancel').on('click', function () { Dialog.close(idx); });
|
||||||
|
$('#filter-apply').on('click', function () {
|
||||||
|
var logic = $('input[name="f-logic"]:checked').val() || 'and';
|
||||||
|
var conds = [];
|
||||||
|
$('#filter-rows .filter-row').each(function () {
|
||||||
|
var field = $(this).find('.f-field').val();
|
||||||
|
var op = $(this).find('.f-op').val();
|
||||||
|
var value = $.trim($(this).find('input[type="text"]').val());
|
||||||
|
if (!field || !op) return;
|
||||||
|
if (op !== 'is_empty' && op !== 'is_not_empty' && value === '') return; // 无值且需要值的条件跳过
|
||||||
|
conds.push({ field: field, op: op, value: value });
|
||||||
|
});
|
||||||
|
companyFilters = { logic: logic, conditions: conds };
|
||||||
|
Dialog.close(idx);
|
||||||
|
loadList(1);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
$('#btn-filter').on('click', function () { openFilterDialog(); });
|
||||||
|
|
||||||
/* ---------- 新增/编辑 ---------- */
|
/* ---------- 新增/编辑 ---------- */
|
||||||
$('#btn-add').on('click', function () {
|
$('#btn-add').on('click', function () {
|
||||||
httpGet('company/cert_types.php').then(function (d) {
|
httpGet('company/cert_types.php').then(function (d) {
|
||||||
|
|||||||
@@ -0,0 +1,106 @@
|
|||||||
|
/**
|
||||||
|
* competitor_data.js - 竞品资料页(v1.0.20 起承载原「文件管理」内容)
|
||||||
|
* 需求:左侧导航删掉「文件管理」,其呈现内容迁移到「竞品管理 > 竞品资料」,
|
||||||
|
* 即「竞品资料」=「文件管理」(表格展示 / 上传 / 删除)。
|
||||||
|
*/
|
||||||
|
$(function () {
|
||||||
|
renderShell('竞品资料', '竞品管理 / 竞品资料');
|
||||||
|
renderPage();
|
||||||
|
initPage('competitor_data', function (user) {
|
||||||
|
if (!checkPagePermission('competitor_data')) return;
|
||||||
|
loadList(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
var currentPage = 1;
|
||||||
|
|
||||||
|
/** 渲染页面内容(工具栏+表格+分页) */
|
||||||
|
function renderPage() {
|
||||||
|
$('#page-content').html(
|
||||||
|
'<div class="card">' +
|
||||||
|
' <div class="toolbar">' +
|
||||||
|
' <input type="text" data-filter="keyword" placeholder="文档名称关键词">' +
|
||||||
|
' <input type="text" data-filter="file_type" placeholder="文件类型(如 application/pdf)">' +
|
||||||
|
' <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-upload">上传文件</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></tr></thead>' +
|
||||||
|
' <tbody id="doc-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('document/list.php', params).then(function (data) {
|
||||||
|
var rows = data.list || [];
|
||||||
|
var html = '';
|
||||||
|
rows.forEach(function (r) {
|
||||||
|
var isLocal = (r.storage_path || '').indexOf('static/uploads/') === 0;
|
||||||
|
var link = isLocal ? r.storage_path : (r.storage_path || '#');
|
||||||
|
html += '<tr>' +
|
||||||
|
'<td><input type="checkbox" class="row-check" value="' + r.id + '"></td>' +
|
||||||
|
'<td>' + r.id + '</td>' +
|
||||||
|
'<td title="' + escHtml(r.doc_name) + '">' + escHtml(r.doc_name) + '</td>' +
|
||||||
|
'<td>' + escHtml(r.file_type || '-') + '</td>' +
|
||||||
|
'<td><a href="' + escHtml(link) + '" target="_blank" class="btn btn-sm">查看/下载</a></td>' +
|
||||||
|
'<td>' + (r.is_current ? '<span class="tag tag-green">当前版本</span>' : '<span class="tag tag-gray">历史</span>') + '</td>' +
|
||||||
|
'<td>' + escHtml(r.publish_date || '-') + '</td>' +
|
||||||
|
'<td>' + fmtDate(r.created_at) + '</td>' +
|
||||||
|
'<td class="ops"><a class="danger" onclick="delDoc(' + r.id + ')">删除</a></td></tr>';
|
||||||
|
});
|
||||||
|
if (!rows.length) {
|
||||||
|
html = '<tr><td colspan="9" class="empty-tip" style="padding:40px 0;">暂无文件</td></tr>';
|
||||||
|
}
|
||||||
|
$('#doc-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-upload').on('click', function () {
|
||||||
|
triggerFileInput('.pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx,.txt,.csv,.png,.jpg,.jpeg,.gif,.zip,.rar', function (file) {
|
||||||
|
var fd = new FormData();
|
||||||
|
fd.append('file', file);
|
||||||
|
httpPost('document/upload.php', fd, true).then(function () {
|
||||||
|
Dialog.success('上传成功', function () { loadList(1); });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
/* ---------- 删除(同时删除物理文件) ---------- */
|
||||||
|
window.delDoc = function (id) {
|
||||||
|
Dialog.confirm('确定删除该文件吗?将同时删除物理文件,不可恢复!', function () {
|
||||||
|
httpPost('document/delete.php', { id: id }).then(function () {
|
||||||
|
Dialog.success('删除成功', function () { loadList(currentPage); });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
$('#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('document/delete.php', { ids: ids.join(',') }).then(function () {
|
||||||
|
Dialog.success('删除成功', function () { loadList(currentPage); });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
+1
-2
@@ -5,7 +5,7 @@ var BASE_URL = '/api/';
|
|||||||
var PAGE_SIZE = 20;
|
var PAGE_SIZE = 20;
|
||||||
|
|
||||||
/** 系统版本号(logo旁展示):修改代码后运行 tools/bump_version.php 自动递增 */
|
/** 系统版本号(logo旁展示):修改代码后运行 tools/bump_version.php 自动递增 */
|
||||||
var APP_VERSION = 'v1.0.19';
|
var APP_VERSION = 'v1.0.20';
|
||||||
|
|
||||||
/** 页脚版权/备案信息(在 config.js 中修改) */
|
/** 页脚版权/备案信息(在 config.js 中修改) */
|
||||||
var FOOTER_TEXT = '© 2026 SuperLink 管理系统 版权所有 | 备案号:请替换为真实备案号';
|
var FOOTER_TEXT = '© 2026 SuperLink 管理系统 版权所有 | 备案号:请替换为真实备案号';
|
||||||
@@ -86,7 +86,6 @@ var MENU_GROUPS = [
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{ key: 'channel', name: '渠道管理', icon: 'channel', url: 'channel.html' },
|
{ key: 'channel', name: '渠道管理', icon: 'channel', url: 'channel.html' },
|
||||||
{ key: 'document', name: '文件管理', icon: 'document', url: 'document.html' },
|
|
||||||
{
|
{
|
||||||
key: 'system', name: '权限管理', icon: 'system', children: [
|
key: 'system', name: '权限管理', icon: 'system', children: [
|
||||||
{ key: 'system', name: '用户管理', icon: 'users', url: 'user.html' },
|
{ key: 'system', name: '用户管理', icon: 'users', url: 'user.html' },
|
||||||
|
|||||||
Reference in New Issue
Block a user