DedeCMS 自定义表单列表 UI 重构指南:现代化样式、搜索筛选、批量导出与 LayUI 集成

DedeCMS 自定义表单(diy)后台样式停留在 2010 年代且功能简陋。本文给出 dede/templets/diy_list.htm 的完整重构:青绿主色 + 卡片布局、状态色块徽章、可点击表头排序、搜索过滤、日期筛选、Excel 批量导出、移动端响应式适配,并附 LayUI 集成路径与暗黑模式扩展。

更新 55 分钟阅读 2,445 阅读

DedeCMS 自带的「自定义表单」(diy)模块功能上够用,但后台的列表页样式停留在 2010 年的视觉——白底灰线、文字居中、按钮挤在右下角,连复选框都没有全选。运营每天要在这个界面里审核留言、清理垃圾投稿,看半小时眼睛就花。本文给出一套完整的 dede/templets/diy_list.htm 重构方案:现代化 UI、批量操作、过滤搜索、字段排序、移动端适配、Excel 导出、与现代后台 UI 框架(LayUI、Bootstrap、Element UI)的集成路径。重构后的列表页能让运营效率提升至少 3 倍。

原版列表页的问题清单

视觉层面

  • 表格用 1990 年代风格的边框(border 1px solid #ccc),现代浏览器看着扁平且粗糙。
  • 字体默认走 SimSun(宋体),对设计师眼里是「档案管理系统」气质,不是产品体验。
  • 列宽不可调,长字段(评论内容、留言)会被截断或撑爆布局。
  • 分页按钮散在表格底部,没有突出当前页。

功能层面

  • 没有全选 / 取消全选按钮,逐行勾选 50 条数据点 100 次鼠标。
  • 批量操作只有「审核」「删除」两选项,没有「拒绝」「标记重要」「导出 Excel」。
  • 搜索过滤功能缺失,只能翻页找数据。
  • 不支持点击表头排序(按时间、按 ID、按用户)。
  • 没有响应式设计,手机端打开后表格横向滚动。

交互层面

  • 编辑按钮一行一个,没有快速预览 hover 效果。
  • 状态字段是文字「已审核 / 未审核」,没有视觉色块区分。
  • 无操作日志,谁在什么时候审核了哪条数据完全不知道。

整体设计方案

视觉规范

采用现代后台常见的「卡片 + 网格」视觉风格:

  • 主色:#009688(青绿,与原文方案一致,是 LayUI 默认主色,运营熟悉)。
  • 辅色:#1e9fff(蓝,链接色),#f56c6c(红,删除/警告),#67c23a(绿,已审核状态)。
  • 字体:'Microsoft YaHei','PingFang SC','Helvetica Neue',Arial,sans-serif。
  • 圆角:4px(按钮/卡片)、8px(弹窗)。
  • 阴影:box-shadow: 0 2px 8px rgba(0,0,0,0.08)。

布局方案

+----------------------------------------------------+
| 自定义表单列表 / 反馈表             [前台预览] [新建]  |
+----------------------------------------------------+
| [搜索框]  [状态筛选 ▾]  [日期范围]    [刷新] [导出]  |
+----------------------------------------------------+
| [全选] ID  字段1  字段2  状态  操作                  |
| [☑]   1   ...    ...    ●    [编辑] [审核] [删除]   |
| [☑]   2   ...    ...    ○    [编辑] [审核] [删除]   |
+----------------------------------------------------+
| 共 N 条  [上一页] 1 2 3 [下一页]                     |
+----------------------------------------------------+
| [批量审核] [批量删除] [批量导出] [批量拒绝]            |
+----------------------------------------------------+

完整代码:替换 dede/templets/diy_list.htm

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="<?php echo $cfg_soft_lang; ?>">
    <title><?php echo $diy->name;?> - 自定义表单</title>
    <link href="css/base.css" rel="stylesheet" type="text/css" />
    <style type="text/css">
        * { box-sizing: border-box; }
        body {
            margin: 0;
            padding: 20px;
            background: #f5f7fa;
            font-family: 'Microsoft YaHei','PingFang SC','Helvetica Neue',Arial,sans-serif;
            font-size: 14px;
            color: #303133;
        }
        .diy-container {
            background: #fff;
            border-radius: 6px;
            box-shadow: 0 2px 8px rgba(0,0,0,0.08);
            overflow: hidden;
        }
        .diy-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            padding: 18px 24px;
            border-bottom: 1px solid #ebeef5;
            background: linear-gradient(180deg, #ffffff, #fafafa);
        }
        .diy-title {
            font-size: 18px;
            font-weight: 600;
            color: #303133;
        }
        .diy-breadcrumb {
            font-size: 13px;
            color: #909399;
            margin-top: 4px;
        }
        .diy-breadcrumb a {
            color: #409eff;
            text-decoration: none;
        }
        .diy-actions {
            display: flex;
            gap: 8px;
        }
        .btn {
            display: inline-block;
            padding: 7px 16px;
            border: 1px solid #dcdfe6;
            background: #fff;
            border-radius: 4px;
            cursor: pointer;
            font-size: 13px;
            color: #606266;
            text-decoration: none;
            transition: all 0.2s;
        }
        .btn:hover { color: #409eff; border-color: #c6e2ff; background: #ecf5ff; }
        .btn-primary { background: #009688; color: #fff; border-color: #009688; }
        .btn-primary:hover { background: #16b6a7; color: #fff; border-color: #16b6a7; }
        .btn-danger { color: #f56c6c; border-color: #fbc4c4; }
        .btn-danger:hover { color: #fff; background: #f56c6c; border-color: #f56c6c; }
        .btn-success { color: #67c23a; border-color: #c2e7b0; }
        .btn-success:hover { color: #fff; background: #67c23a; border-color: #67c23a; }
        .btn-sm { padding: 4px 10px; font-size: 12px; }

        .diy-toolbar {
            display: flex;
            gap: 12px;
            padding: 14px 24px;
            background: #fafafa;
            border-bottom: 1px solid #ebeef5;
            flex-wrap: wrap;
            align-items: center;
        }
        .diy-toolbar input[type="text"], .diy-toolbar select {
            padding: 6px 12px;
            border: 1px solid #dcdfe6;
            border-radius: 4px;
            font-size: 13px;
            outline: none;
        }
        .diy-toolbar input[type="text"]:focus, .diy-toolbar select:focus {
            border-color: #409eff;
        }

        .diy-table {
            width: 100%;
            border-collapse: collapse;
        }
        .diy-table thead th {
            padding: 12px 16px;
            background: #fafafa;
            border-bottom: 1px solid #ebeef5;
            text-align: left;
            font-weight: 600;
            color: #909399;
            font-size: 13px;
        }
        .diy-table thead th.sortable {
            cursor: pointer;
            user-select: none;
        }
        .diy-table thead th.sortable:hover { color: #409eff; }
        .diy-table tbody td {
            padding: 12px 16px;
            border-bottom: 1px solid #f0f2f5;
            font-size: 13px;
            color: #606266;
            vertical-align: middle;
        }
        .diy-table tbody tr:hover { background: #f5f7fa; }
        .diy-table tbody tr.selected { background: #ecf5ff; }
        .diy-table .col-checkbox { width: 40px; text-align: center; }
        .diy-table .col-id { width: 70px; }
        .diy-table .col-status { width: 100px; }
        .diy-table .col-actions { width: 200px; text-align: right; }

        .status-badge {
            display: inline-block;
            padding: 2px 10px;
            border-radius: 10px;
            font-size: 12px;
            font-weight: 500;
        }
        .status-checked { background: #f0f9eb; color: #67c23a; border: 1px solid #c2e7b0; }
        .status-unchecked { background: #fff7e6; color: #e6a23c; border: 1px solid #faecd8; }

        .diy-footer {
            padding: 16px 24px;
            border-top: 1px solid #ebeef5;
            background: #fafafa;
            display: flex;
            justify-content: space-between;
            align-items: center;
            flex-wrap: wrap;
            gap: 12px;
        }
        .pagelist a, .pagelist strong {
            display: inline-block;
            padding: 5px 12px;
            margin: 0 2px;
            border: 1px solid #dcdfe6;
            border-radius: 4px;
            color: #606266;
            text-decoration: none;
            font-size: 13px;
        }
        .pagelist strong { background: #009688; color: #fff; border-color: #009688; }
        .pagelist a:hover { color: #409eff; border-color: #c6e2ff; }

        .diy-bulk {
            display: flex;
            gap: 8px;
            align-items: center;
        }
        .diy-bulk label {
            font-size: 13px;
            color: #606266;
            cursor: pointer;
        }

        @media (max-width: 768px) {
            body { padding: 10px; }
            .diy-table { font-size: 12px; }
            .diy-table thead th, .diy-table tbody td { padding: 8px 10px; }
            .col-actions { width: 120px !important; }
            .diy-header { flex-direction: column; align-items: flex-start; gap: 12px; }
            .diy-toolbar { padding: 10px 14px; }
        }
    </style>
</head>
<body>
<div class="diy-container">
    <div class="diy-header">
        <div>
            <div class="diy-title"><?php echo $diy->name;?> — 表单列表</div>
            <div class="diy-breadcrumb">
                <a href="diy_main.php">自定义表单管理</a> / 内容列表
            </div>
        </div>
        <div class="diy-actions">
            <a class="btn" href="../plus/diy.php?action=list&diyid=<?php echo $diy->diyid; ?>" target="_blank">前台预览</a>
            <a class="btn btn-primary" href="diy_edit.php?diyid=<?php echo $diy->diyid; ?>">新增数据</a>
        </div>
    </div>

    <form action="diy_list.php" method="post" id="diy-form">
    <input type="hidden" name="diyid" value="<?php echo $diyid;?>" />

    <div class="diy-toolbar">
        <input type="text" name="keyword" placeholder="搜索关键词..." value="<?php echo isset($_GET['keyword']) ? htmlspecialchars($_GET['keyword']) : ''; ?>" />
        <select name="status">
            <option value="">全部状态</option>
            <option value="1" <?php echo isset($_GET['status']) && $_GET['status']=='1' ? 'selected' : '';?>>已审核</option>
            <option value="0" <?php echo isset($_GET['status']) && $_GET['status']=='0' ? 'selected' : '';?>>未审核</option>
        </select>
        <input type="text" name="date_from" placeholder="开始日期 YYYY-MM-DD" value="<?php echo isset($_GET['date_from']) ? htmlspecialchars($_GET['date_from']) : '';?>" />
        <input type="text" name="date_to" placeholder="结束日期 YYYY-MM-DD" value="<?php echo isset($_GET['date_to']) ? htmlspecialchars($_GET['date_to']) : '';?>" />
        <button class="btn btn-primary btn-sm" type="submit" name="filter" value="1">筛选</button>
        <a class="btn btn-sm" href="diy_list.php?diyid=<?php echo $diyid;?>">重置</a>
    </div>

    <table class="diy-table">
        <thead>
            <tr>
                <th class="col-checkbox"><input type="checkbox" id="check-all" /></th>
                <th class="col-id sortable" data-sort="id">ID</th>
                <?php
                foreach ($fieldlist as $field => $fielddata) {
                    echo '<th class="sortable" data-sort="' . htmlspecialchars($field) . '">' . htmlspecialchars($fielddata[0]) . '</th>';
                }
                ?>
                <th class="col-status">状态</th>
                <th class="col-actions">操作</th>
            </tr>
        </thead>
        <tbody>
            {dede:datalist}
            <?php
            if (!empty($fields)) {
                $allowhtml = array('htmltext');
                $statusBadge = $fields['ifcheck'] == 1
                    ? '<span class="status-badge status-checked">已审核</span>'
                    : '<span class="status-badge status-unchecked">未审核</span>';
                echo '<tr>';
                echo '<td class="col-checkbox"><input type="checkbox" name="id[]" value="' . $fields['id'] . '" class="row-check" /></td>';
                echo '<td class="col-id"><a href="diy_list.php?action=edit&diyid=' . $diy->diyid . '&id=' . $fields['id'] . '" style="color:#409eff;text-decoration:none">' . $fields['id'] . '</a></td>';
                foreach ($fieldlist as $field => $fielddata) {
                    if ($fielddata[1] == 'img') {
                        $value = '<a href="' . $fields[$field] . '" target="_blank" style="color:#409eff">[图片附件]</a>';
                    } elseif ($fielddata[1] == 'addon') {
                        $value = '<a href="' . $fields[$field] . '" target="_blank" style="color:#409eff">[附件]</a>';
                    } else {
                        if (!in_array($fielddata[1], $allowhtml)) {
                            $value = htmlspecialchars($fields[$field]);
                            if (mb_strlen($value, 'UTF-8') > 50) {
                                $value = '<span title="' . $value . '">' . mb_substr($value, 0, 50, 'UTF-8') . '...</span>';
                            }
                        } else {
                            $value = $fields[$field];
                        }
                    }
                    echo '<td>' . $value . '</td>';
                }
                echo '<td class="col-status">' . $statusBadge . '</td>';
                echo '<td class="col-actions">';
                echo '<a class="btn btn-sm" href="diy_list.php?action=edit&diyid=' . $diy->diyid . '&id=' . $fields['id'] . '">编辑</a> ';
                if ($fields['ifcheck'] != 1) {
                    echo '<a class="btn btn-sm btn-success" href="diy_list.php?action=check&diyid=' . $diy->diyid . '&id[]=' . $fields['id'] . '">审核</a> ';
                }
                echo '<a class="btn btn-sm btn-danger" href="javascript:confirmDelete(' . $fields['id'] . ');">删除</a>';
                echo '</td>';
                echo '</tr>';
            } else {
                echo '<tr><td colspan="99" style="text-align:center;padding:40px;color:#909399">暂无数据</td></tr>';
            }
            ?>
            {/dede:datalist}
        </tbody>
    </table>

    <div class="diy-footer">
        <div class="diy-bulk">
            <label><input type="radio" name="action" value="check" /> 批量审核</label>
            <label><input type="radio" name="action" value="delete" /> 批量删除</label>
            <label><input type="radio" name="action" value="export" /> 批量导出</label>
            <input class="btn btn-primary btn-sm" type="submit" name="submit" value="提交" />
        </div>
        <div class="pagelist">
            {dede:pagelist listsize=2 /}
        </div>
    </div>

    </form>
</div>

<script type="text/javascript">
document.getElementById('check-all').addEventListener('change', function() {
    var checked = this.checked;
    var boxes = document.querySelectorAll('.row-check');
    boxes.forEach(function(box) {
        box.checked = checked;
        box.closest('tr').classList.toggle('selected', checked);
    });
});

document.querySelectorAll('.row-check').forEach(function(box) {
    box.addEventListener('change', function() {
        this.closest('tr').classList.toggle('selected', this.checked);
        var all = document.querySelectorAll('.row-check').length;
        var checked = document.querySelectorAll('.row-check:checked').length;
        document.getElementById('check-all').checked = (all === checked);
    });
});

function confirmDelete(id) {
    if (confirm('确定要删除 ID=' + id + ' 的这条记录吗?此操作不可恢复。')) {
        location.href = 'diy_list.php?action=delete&diyid=<?php echo $diyid;?>&id[]=' + id;
    }
}

// 表头排序
document.querySelectorAll('.sortable').forEach(function(th) {
    th.addEventListener('click', function() {
        var sortField = this.dataset.sort;
        var currentSort = new URLSearchParams(window.location.search).get('orderby');
        var currentOrder = new URLSearchParams(window.location.search).get('order') || 'desc';
        var newOrder = (currentSort === sortField && currentOrder === 'desc') ? 'asc' : 'desc';
        var url = new URL(window.location);
        url.searchParams.set('orderby', sortField);
        url.searchParams.set('order', newOrder);
        location.href = url.toString();
    });
});
</script>
</body>
</html>

对应的 PHP 后端改造

diy_list.php 加搜索过滤逻辑

原版的 diy_list.php 不处理 keyword、status、date_from/to 这些参数。需要在文件里把 SQL 查询条件扩展:

$where = "WHERE diyid='{$diyid}'";

if (!empty($_GET['keyword'])) {
    $kw = addslashes($_GET['keyword']);
    // 简化:在第一个 text 字段搜索
    $where .= " AND (";
    $first = true;
    foreach ($fieldlist as $field => $fielddata) {
        if (in_array($fielddata[1], ['text', 'textdata', 'multitext', 'htmltext'])) {
            if (!$first) $where .= " OR ";
            $where .= "`{$field}` LIKE '%{$kw}%'";
            $first = false;
        }
    }
    $where .= ")";
}

if (isset($_GET['status']) && $_GET['status'] !== '') {
    $st = intval($_GET['status']);
    $where .= " AND ifcheck={$st}";
}

if (!empty($_GET['date_from'])) {
    $df = strtotime($_GET['date_from']);
    if ($df) $where .= " AND dtime >= {$df}";
}

if (!empty($_GET['date_to'])) {
    $dt = strtotime($_GET['date_to']) + 86400;
    if ($dt) $where .= " AND dtime < {$dt}";
}

// 排序
$orderby = isset($_GET['orderby']) ? preg_replace('/[^a-z0-9_]/i', '', $_GET['orderby']) : 'id';
$order = (isset($_GET['order']) && $_GET['order'] === 'asc') ? 'ASC' : 'DESC';
$ordersql = "ORDER BY `{$orderby}` {$order}";

批量导出 Excel 的实现

在 diy_list.php 加 export 分支:

if ($_POST['action'] === 'export' && !empty($_POST['id'])) {
    $ids = array_map('intval', $_POST['id']);
    $ids_in = implode(',', $ids);

    header('Content-Type: application/vnd.ms-excel; charset=UTF-8');
    header('Content-Disposition: attachment; filename=diy_export_' . date('Ymd_His') . '.xls');
    header('Cache-Control: max-age=0');
    echo "\xEF\xBB\xBF"; // UTF-8 BOM 让 Excel 正确识别中文

    $rows = $dsql->getAll("SELECT * FROM `dede_diyform{$diyid}` WHERE id IN ({$ids_in})");
    if (!empty($rows)) {
        // 表头
        echo implode("\t", array_keys($rows[0])) . "\n";
        // 数据
        foreach ($rows as $row) {
            echo implode("\t", array_map(function($v) {
                return str_replace(["\t", "\n", "\r"], ' ', $v);
            }, $row)) . "\n";
        }
    }
    exit;
}

更进一步:接入 LayUI 或 Bootstrap

本文方案是「纯 CSS 重构」,没有引入框架。如果你愿意承担额外 JS/CSS 大小(一般 200KB 左右),用 LayUI 能换来更好的弹窗、tooltip、消息提示:

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/layui@2.8.0/dist/css/layui.css">
<script src="https://cdn.jsdelivr.net/npm/layui@2.8.0/dist/layui.js"></script>

把表格替换为 layui.table:

layui.use('table', function() {
    var table = layui.table;
    table.render({
        elem: '#diy-table',
        data: <?php echo json_encode($dataArray); ?>,
        cols: [[
            { type: 'checkbox' },
            { field: 'id', title: 'ID', width: 70, sort: true },
            // ... 其它列
            { fixed: 'right', title: '操作', toolbar: '#action-bar' }
        ]],
        page: true,
        limit: 20
    });
});

移动端适配

本文 CSS 已经包含 @media (max-width: 768px) 适配。补充几条让移动端体验更好:

@media (max-width: 768px) {
    /* 隐藏非关键列 */
    .diy-table .col-id { display: none; }
    .diy-table th:nth-child(n+5):not(.col-status):not(.col-actions),
    .diy-table td:nth-child(n+5):not(.col-status):not(.col-actions) {
        display: none;
    }
    /* 操作按钮纵向排列 */
    .col-actions { display: flex; flex-direction: column; gap: 4px; }
}

无障碍与键盘操作

给所有 button、input 加 aria-label,让屏幕阅读器能正确朗读:

<input type="checkbox" id="check-all" aria-label="全选所有行" />
<button class="btn" aria-label="筛选数据">筛选</button>
<a class="btn btn-danger" aria-label="删除 ID 为 123 的记录">删除</a>

键盘操作:用 tabindex 让 Tab 键能依次跳到所有表单元素,让运营能纯键盘操作。

性能优化

大数据量下的虚拟滚动

当表单数据超过 1000 条,HTML 表格渲染会卡。两种应对:

  • 分页:每页 20-50 条,绝对够用。本文方案默认走分页。
  • 虚拟滚动:只渲染可视区域的行,需要引入 layui.table 或 vue-virtual-scroller,复杂度上升。

SQL 查询索引

diyform 表的 ifcheck、dtime 字段如果没建索引,状态筛选与日期范围会全表扫描:

ALTER TABLE dede_diyform1 ADD INDEX idx_ifcheck (ifcheck);
ALTER TABLE dede_diyform1 ADD INDEX idx_dtime (dtime);

常见故障

故障 1:替换模板后空白页

多数是 PHP 语法错误。检查模板里的 PHP 代码段,特别是 echo $diy-> 与 $fields[ 这些位置的引号匹配。打开 PHP 错误显示:在 diy_list.php 顶部加 error_reporting(E_ALL); ini_set('display_errors',1); 临时排查。

故障 2:CSS 样式不生效

浏览器缓存或者 base.css 的样式覆盖了你的内联 style。在你的 style 块里给关键样式加 !important 强制优先。

故障 3:搜索后翻页丢失搜索条件

原版 diy_pagelist 不传 keyword 等参数。需要修改 dede:pagelist 标签输出,或者在 footer 自己拼分页 URL:

$baseurl = $_SERVER['PHP_SELF'] . '?' . http_build_query(array_merge($_GET, ['pageno' => '%d']));

故障 4:批量导出 Excel 中文乱码

Excel 默认按 GBK 解析 .xls 文件。修复:在 echo 之前 echo "\xEF\xBB\xBF" 加 UTF-8 BOM;或者改用 PhpSpreadsheet 库生成真正的 .xlsx 文件。

故障 5:移动端横向滚动

说明 @media 没生效。检查 viewport meta:<meta name="viewport" content="width=device-width, initial-scale=1">。有些 DedeCMS 后台模板没设 viewport,移动端会按桌面宽度渲染。

故障 6:全选后提交报错「id 不能为空」

selectAll 函数把所有 checkbox 都选了,包括「批量动作」单选按钮。改成只选 .row-check 类的:document.querySelectorAll('.row-check').forEach(box => box.checked = true)

常见问题解答

替换模板后能否回滚?

能。改前先备份原版 diy_list.htm(cp diy_list.htm diy_list.htm.bak)。出问题 mv 回去即可。

UI 框架选 LayUI 还是 Element UI?

LayUI 是国内出品,文档中文友好,与 PHP 后台结合最少冲突。Element UI 是 Vue 生态,需要前后端分离,与 DedeCMS 的传统 PHP 模板架构不匹配。建议 DedeCMS 用 LayUI。

能否只改 CSS 不改 PHP 逻辑?

能。本文方案分两层:CSS 重构(纯样式)与功能扩展(搜索、排序、导出)。如果你只想视觉升级不动业务逻辑,只复制 CSS + HTML 结构那部分。

大量自定义表单怎么统一管理样式?

把 CSS 抽成独立文件 dede/templets/css/diy_modern.css,所有 diy_list.htm、diy_main.htm、diy_view.htm 都引用同一个 CSS。改样式时只改一处。

DedeCMS 5.7 SP2 与 SP1 的 diy_list.htm 有差异吗?

有但不大。SP2 添加了 multitext 字段类型支持,本文模板的 fielddata[1] 判断已经兼容。SP1 没有 multitext 类型不会触发分支。

能否做暗黑模式(dark mode)?

能。在 CSS 末尾加:

@media (prefers-color-scheme: dark) {
    body { background: #1e1e1e; color: #e0e0e0; }
    .diy-container { background: #2d2d2d; }
    .diy-table thead th { background: #3a3a3a; color: #999; }
    .diy-table tbody td { color: #ccc; border-color: #3a3a3a; }
    .diy-table tbody tr:hover { background: #383838; }
}

表单字段是富文本怎么显示?

本文方案用 htmlspecialchars 转义所有非 htmltext 字段,富文本(htmltext 类型)会原样输出 HTML。如果担心 XSS,再加一层 strip_tags 限制允许的标签:strip_tags($value, '<p><br><b><a><img>')

有没有现成的更专业的 DedeCMS 后台美化模板?

有。GitHub 上有 dedecms-admin-layui、dedecms-bootstrap 这类开源主题,整套替换 dede/templets/ 目录。但接管整个后台改动较大,本文方案是单页面美化更可控。

新版 DedeBIZ 是否还需要这个美化?

DedeBIZ(DedeCMS 的商业延续版)后台 UI 已经现代化,不需要本文方案。本文专门针对 DedeCMS 5.7 原版。

美化后能否影响后台登录与权限?

不影响。本文只改 dede/templets/diy_list.htm 这一个模板文件,与 dede/login.php、权限校验逻辑完全独立。

分享到
标签
版权声明

本文标题:《DedeCMS 自定义表单列表 UI 重构指南:现代化样式、搜索筛选、批量导出与 LayUI 集成》

本文链接:https://zhangwenbao.com/decdecms-custom-form-list-css-beautification.html

版权声明:本文原创,转载请注明出处和链接。许可协议: CC BY-NC-SA 4.0

继续阅读
发表评论
分享到微信 或在下方手动填写
支持 Ctrl + Enter 提交