Browse Source

1.图书管理页优化,添加图书分类检索条件

1 2 days ago
parent
commit
12a6497a23

+ 1 - 0
imwork-windows/imwork-silos/src/main/java/top/imwork/window/silos/convert/InfoConvert.java

@@ -162,6 +162,7 @@ public class InfoConvert {
         }
         InfoListDTO infoListDTO = new InfoListDTO();
         infoListDTO.setInfoName(infoListParamVO.getInfoName());
+        infoListDTO.setCategoryCode(infoListParamVO.getCategoryCode());
         infoListDTO.setPageNo(infoListParamVO.getPageNo());
         infoListDTO.setPageRows(infoListParamVO.getPageRows());
         infoListDTO.setDelFlag(infoListParamVO.getDelFlag());

+ 9 - 0
imwork-windows/imwork-silos/src/main/java/top/imwork/window/silos/pojo/dto/InfoListDTO.java

@@ -13,6 +13,15 @@ import top.imwork.commons.core.pojo.BaseParams;
 @EqualsAndHashCode(callSuper = true)
 public class InfoListDTO extends BaseParams {
     private String infoName;
+    private String categoryCode;
+
+    public String getCategoryCode() {
+        return categoryCode;
+    }
+
+    public void setCategoryCode(String categoryCode) {
+        this.categoryCode = categoryCode;
+    }
 
     public String getInfoName() {
         return infoName;

+ 9 - 0
imwork-windows/imwork-silos/src/main/java/top/imwork/window/silos/pojo/po/InfoListParamVO.java

@@ -13,6 +13,15 @@ import top.imwork.commons.core.pojo.BaseParams;
 @EqualsAndHashCode(callSuper = true)
 public class InfoListParamVO extends BaseParams {
     private String infoName;
+    private String categoryCode;
+
+    public String getCategoryCode() {
+        return categoryCode;
+    }
+
+    public void setCategoryCode(String categoryCode) {
+        this.categoryCode = categoryCode;
+    }
 
     public String getInfoName() {
         return infoName;

+ 4 - 0
imwork-windows/imwork-silos/src/main/java/top/imwork/window/silos/service/impl/InfoServiceImpl.java

@@ -75,6 +75,10 @@ public class InfoServiceImpl extends ServiceImpl<InfoDao,Info> implements IInfoS
             queryWrapper.like("title", infoListDTO.getInfoName());
         }
 
+        if (!StringUtils.isEmpty(infoListDTO.getCategoryCode())) {
+            queryWrapper.like("category_code", infoListDTO.getCategoryCode());
+        }
+
         if (!StringUtils.isEmpty(infoListDTO.getCreateBeginTime())) {
             queryWrapper.apply("create_time >= '" + infoListDTO.getCreateBeginTime() + "'");
         }

+ 2 - 0
imwork-windows/imwork-silos/src/main/resources/application.yml

@@ -1,6 +1,8 @@
 server:
   port: 7002
 spring:
+  main:
+    allow-bean-definition-overriding: true
   application:
     name: imwork-silos
   config:

+ 244 - 0
imwork-windows/imwork-silos/src/main/resources/static/assets/silos/js/SelectRenderer.js

@@ -0,0 +1,244 @@
+/**
+ * Select渲染器
+ */
+class SelectRenderer {
+    constructor(config = {}) {
+        this.config = $.extend({
+            placeholder: true,
+            placeholderText: '请选择',
+            allowClear: false,
+            searchable: false,
+            ajax: false,
+            data: [],
+            valueField: 'value',
+            textField: 'text',
+            disabledField: 'disabled',
+            selectedField: 'selected',
+            groupField: 'group'
+        }, config);
+    }
+
+    /**
+     * 渲染单个select
+     * @param {jQuery|string} selector - 选择器或jQuery对象
+     * @param {Array|Object} data - 数据
+     * @param {Object} options - 选项
+     */
+    render(selector, data = null, options = {}) {
+        var $select = $(selector);
+        if (!$select.length) return;
+
+        var config = $.extend({}, this.config, options);
+        var renderData = data || config.data;
+
+        // 清空现有选项
+        $select.empty();
+
+        // 添加占位符
+        if (config.placeholder) {
+            var $placeholder = $('<option>', {
+                value: '',
+                text: config.placeholderText
+            });
+
+            if (config.allowClear) {
+                $placeholder.attr('data-allow-clear', 'true');
+            }
+
+            $select.append($placeholder);
+        }
+
+        // 渲染选项
+        if ($.isArray(renderData)) {
+            this._renderOptions($select, renderData, config);
+        } else if (renderData && renderData.url) {
+            this._renderWithAjax($select, renderData, config);
+        }
+
+        // 触发渲染完成事件
+        $select.trigger('select:rendered');
+
+        return $select;
+    }
+
+    /**
+     * 渲染多个select
+     * @param {string} className - 类选择器
+     * @param {Array} data - 数据
+     * @param {Object} options - 选项
+     */
+    renderAll(className, data = null, options = {}) {
+        var self = this;
+        var results = [];
+
+        $(className).each(function() {
+            var $select = $(this);
+            var specificOptions = $.extend({}, options);
+
+            // 从data-*属性读取配置
+            var dataAttrs = self._getDataAttributes($select);
+            $.extend(specificOptions, dataAttrs);
+
+            results.push(self.render($select, data, specificOptions));
+        });
+
+        return results;
+    }
+
+    /**
+     * 内部方法:渲染选项
+     */
+    _renderOptions($select, data, config) {
+        var groups = {};
+
+        $.each(data, function(index, item) {
+            // 分组处理
+            if (config.groupField && item[config.groupField]) {
+                var groupName = item[config.groupField];
+                if (!groups[groupName]) {
+                    groups[groupName] = [];
+                }
+                groups[groupName].push(item);
+            } else {
+                var $option = $('<option>', {
+                    value: item[config.valueField] || item.value || '',
+                    text: item[config.textField] || item.text || '',
+                    disabled: item[config.disabledField] || item.disabled || false,
+                    selected: item[config.selectedField] || item.selected || false
+                });
+
+                // 添加自定义属性
+                $.each(item, function(key, value) {
+                    if (!['value', 'text', 'disabled', 'selected'].includes(key)) {
+                        $option.attr('data-' + key, value);
+                    }
+                });
+
+                $select.append($option);
+            }
+        });
+
+        // 渲染分组
+        this._renderOptionGroups($select, groups, config);
+    }
+
+    /**
+     * 内部方法:渲染分组
+     */
+    _renderOptionGroups($select, groups, config) {
+        $.each(groups, function(groupName, items) {
+            var $optgroup = $('<optgroup>', {
+                label: groupName
+            });
+
+            $.each(items, function(index, item) {
+                var $option = $('<option>', {
+                    value: item[config.valueField] || item.value || '',
+                    text: item[config.textField] || item.text || '',
+                    disabled: item[config.disabledField] || item.disabled || false,
+                    selected: item[config.selectedField] || item.selected || false
+                });
+
+                $optgroup.append($option);
+            });
+
+            $select.append($optgroup);
+        });
+    }
+
+    /**
+     * 内部方法:通过AJAX渲染
+     */
+    _renderWithAjax($select, ajaxConfig, config) {
+        var self = this;
+
+        // 显示加载状态
+        $select.prop('disabled', true).addClass('loading');
+
+        $.ajax($.extend({
+            type: 'GET',
+            dataType: 'json',
+            success: function(response) {
+                // 处理响应数据
+                var data = response;
+                if (ajaxConfig.dataProcessor && typeof ajaxConfig.dataProcessor === 'function') {
+                    data = ajaxConfig.dataProcessor(response);
+                } else if (response.data) {
+                    data = response.data;
+                }
+
+                // 渲染数据
+                self._renderOptions($select, data, config);
+
+                // 恢复select状态
+                $select.prop('disabled', false).removeClass('loading');
+
+                // 触发完成事件
+                $select.trigger('select:ajax:complete', [response]);
+            },
+            error: function(xhr, status, error) {
+                console.error('加载下拉框数据失败:', error);
+
+                // 添加错误选项
+                $select.empty();
+                $select.append($('<option>', {
+                    value: '',
+                    text: '数据加载失败',
+                    disabled: true
+                }));
+
+                // 恢复状态
+                $select.prop('disabled', false).removeClass('loading');
+
+                // 触发错误事件
+                $select.trigger('select:ajax:error', [xhr, status, error]);
+            }
+        }, ajaxConfig));
+    }
+
+    /**
+     * 获取data-*属性
+     */
+    _getDataAttributes($element) {
+        var attrs = {};
+        var data = $element.data();
+
+        // 转换属性名
+        $.each(data, function(key, value) {
+            if (key.startsWith('select')) {
+                var newKey = key.replace(/^select/, '');
+                newKey = newKey.charAt(0).toLowerCase() + newKey.slice(1);
+                attrs[newKey] = value;
+            }
+        });
+
+        return attrs;
+    }
+
+    /**
+     * 设置选中值
+     */
+    setValue($select, value) {
+        $select.val(value).trigger('change');
+        return this;
+    }
+
+    /**
+     * 启用/禁用
+     */
+    setDisabled($select, disabled) {
+        $select.prop('disabled', disabled);
+        return this;
+    }
+
+    /**
+     * 重新加载数据
+     */
+    reload($select) {
+        var dataAttrs = this._getDataAttributes($select);
+        if (dataAttrs.url) {
+            this._renderWithAjax($select, { url: dataAttrs.url }, dataAttrs);
+        }
+        return this;
+    }
+}

+ 3 - 3
imwork-windows/imwork-silos/src/main/resources/static/business/cms/book/category/list.js

@@ -11,7 +11,7 @@ layui.config({
     //用户列表
     var tableIns = table.render({
         elem: '#list',
-        url: '/silos/cms/books/categories/queryPage',
+        url: '/silos/cms/book/categories/queryPage',
         method: 'POST',
         dataType: 'json',
         contentType: 'application/json;charset=utf-8',
@@ -133,7 +133,7 @@ layui.config({
         const index = layui.layer.open({
             title: title,
             type: 2,
-            content: "/silos/cms/books/category/edit.html",
+            content: "/silos/cms/book/category/edit.html",
             scrollbar: false,
             success: function (layero, index) {
                 var body = layui.layer.getChildFrame('body', index);
@@ -192,7 +192,7 @@ layui.config({
     function renderCategoriesGrid() {
         const categoriesGrid = document.getElementById('categoriesGrid');
         $.ajax({
-            url: "/silos/cms/books/categories/queryPage",
+            url: "/silos/cms/book/categories/queryPage",
             type: "POST",
             dataType: "json",
             async: false,

+ 86 - 0
imwork-windows/imwork-silos/src/main/resources/static/business/cms/book/info/list.css

@@ -493,4 +493,90 @@ header {
     .filters {
         grid-template-columns: 1fr;
     }
+}
+
+/* 添加视图切换样式 */
+.view-switcher {
+    margin-bottom: 15px;
+    text-align: right;
+}
+
+.view-btn {
+    background: #f8f9fa;
+    border: 1px solid #dee2e6;
+    padding: 6px 12px;
+    cursor: pointer;
+    margin-left: 5px;
+}
+
+.view-btn.active {
+    background: #3498db;
+    color: white;
+    border-color: #3498db;
+}
+
+/* 网格视图 */
+#booksGrid {
+    grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
+    gap: 20px;
+    padding: 20px;
+}
+
+#booksGrid.active {
+    display: grid;
+}
+
+/* 表格视图 */
+#booksTable {
+    display: none;
+}
+
+#booksTable.active {
+    display: block;
+}
+
+/* 空状态样式 */
+.empty-state {
+    text-align: center;
+    padding: 40px;
+    color: #6c757d;
+    grid-column: 1 / -1;
+}
+
+.empty-state i {
+    font-size: 48px;
+    margin-bottom: 15px;
+    color: #adb5bd;
+}
+
+/* 加载状态 */
+select.loading {
+    background-repeat: no-repeat;
+    background-position: right 10px center;
+    background-size: 16px 16px;
+    padding-right: 35px;
+}
+
+/* 错误状态 */
+select.error {
+    border-color: #dc3545;
+    background-color: #fff5f5;
+}
+
+/* 禁用状态 */
+select:disabled {
+    background-color: #e9ecef;
+    cursor: not-allowed;
+}
+
+/* 分组样式 */
+optgroup {
+    font-weight: bold;
+    font-style: normal;
+    color: #495057;
+}
+
+optgroup option {
+    font-weight: normal;
+    padding-left: 20px;
 }

+ 370 - 205
imwork-windows/imwork-silos/src/main/resources/static/business/cms/book/info/list.js

@@ -7,250 +7,316 @@ layui.config({
         laytpl = layui.laytpl,
         table = layui.table,
         treeTable = layui.treeTable;
+    // 缓存DOM元素
+    const domCache = {
+        booksGrid: $('#booksGrid'),
+        searchInput: $('#search_value'),
+        searchBtn: $('.search_btn'),
+        addBtn: $('.add_btn'),
+        refreshBtn: $('.search_refresh'),
+        categoryCode: $('#book_category')
+    };
 
-    //用户列表
-    var tableIns = table.render({
-        elem: '#list',
-        url: '/silos/cms/book/info/queryPage',
-        method: 'POST',
-        dataType: 'json',
-        contentType: 'application/json;charset=utf-8',
-        headers: {token: ''},
-        cellMinWidth: 95,
-        page: true,
-        height: "full-125",
-        limits: [10, 15, 20, 25],
-        limit: 15,
-        id: "listTable",
-        request: {
-            pageName: "pageNo",//重新定义当前页码参数名称
-            limitName: "pageRows"//重新定义每页大小参数名称
-        },
-        response: {
-            statusName: 'code', //数据状态的字段名称,默认:code
-            statusCode: 200, //成功的状态码,默认:0
-            msgName: 'msg',  //状态信息的字段名称,默认:msg
-            countName: 'totalRows',  //数据总数的字段名称,默认:count
-            dataName: 'data' //数据列表的字段名称,默认:data
-        },
-        where: {
-            //请求的参数写在where
-        },
-        parseData: function (res) {
-            return {
-                "code": res.code,
-                "msg": res.msg, //解析提示文本
-                "totalRows": res.data.totalRows, //解析数据长度
-                "data": res.data.dataList, //解析数据列表
-            };
-        },
-        cols: [[
-            {type: "checkbox", fixed: "left", width: 50},
-            {field: 'isbn', title: '国际标准书号', minWidth: 100, align: "center"},
-            {field: 'title', title: '图书正题名', minWidth: 100, align: "center"},
-            {field: 'subtitle', title: '副题名', minWidth: 100, align: "center"},
-            {
-                field: 'parentId', title: '父级分类', align: 'center', templet: function (d) {
-                    if (d.parentId == "0") {
-                        return "顶级分类";
-                    } else if (d.parentId == "1") {
-                        return "一级分类";
-                    } else if (d.parentId == "2") {
-                        return "二级分类";
-                    } else {
-                        return "三级分类";
-                    }
-                }
+    // 配置常量
+    const CONFIG = {
+        table: {
+            elem: '#list',
+            url: '/silos/cms/book/info/queryPage',
+            method: 'POST',
+            contentType: 'application/json;charset=utf-8',
+            page: true,
+            limits: [10, 15, 20, 25],
+            limit: 15,
+            id: "listTable",
+            request: {
+                pageName: "pageNo",
+                limitName: "pageRows"
             },
-            {field: 'author', title: '第一作者', minWidth: 100, align: "center"},
-            {field: 'otherAuthors', title: '其他责任者', minWidth: 100, align: "center"},
-            {field: 'publisher', title: '出版社', align: 'center'},
-
-            {field: 'edition', title: '版次', minWidth: 100, align: "center"},
-            /*{field: 'userAvatar', title: '用户头像', minWidth:100, align:"center",templet:function(d){
-                    return "<img src='"+d.userAvatar+"' style='width: 30px;height: 30px'>";
-            }},*/
-            {field: 'categoryCode', title: '分类代码', minWidth: 100, align: "center"},
-            {field: 'language', title: '正文语种', minWidth: 100, align: "center"},
-            {field: 'coverImage', title: '封面图片路径', minWidth: 100, align: "center"},
-            {
-                field: 'createdAt', title: '创建时间', minWidth: 100, align: "center", templet: function (d) {
-                    return showTime(d.createTime);
-                }
-            },
-            {
-                field: 'updatedAt', title: '修改时间', minWidth: 100, align: "center", templet: function (d) {
-                    return showTime(d.updateTime);
-                }
-            },
-            {title: '操作', minWidth: 200, templet: '#listBar', fixed: "right", align: "center", toolBar: '#listBar'}
-        ]]
-    });
-    $(".search_btn").click(function () {
-        const infoName = $("#search_value").val();
-        renderCategoriesGrid();
-        table.reload("listTable", {
-            page: {
-                curr: 1 //重新从第 1 页开始
-            },
-            where: {
-                infoName: infoName
+            response: {
+                statusName: 'code',
+                statusCode: 200,
+                msgName: 'msg',
+                countName: 'totalRows',
+                dataName: 'data'
             }
-        })
-    })
-
-    $(".add_btn").click(function () {
-        edit();
-    })
-    // 添加/修改
-    function edit(edit) {
-        let title = "添加图书";
-        if (edit != null) {
-            title = "添加图书";
+        },
+        grid: {
+            pageNo: 1,
+            pageRows: 100
         }
-        const index = layui.layer.open({
+    };
+
+    // 添加/编辑图书
+    function editBook(bookData) {
+        var title = bookData ? "编辑图书" : "添加图书";
+        var index = layui.layer.open({
             title: title,
             type: 2,
             content: "/silos/cms/book/info/edit.html",
-            scrollbar: false,
+            area: ['90%', '90%'],
+            maxmin: true,
             success: function (layero, index) {
-                var body = layui.layer.getChildFrame('body', index);
-                if (edit) {
-                    body.find(".uid").val(BigInt(edit.id).toString());
-                    body.find(".userName").val(edit.userName);  //用户名
-                    body.find(".loginName").val(edit.loginName);  //登录名
-                    body.find(".userMobilePhone").val(edit.userMobilePhone);
-                    body.find(".userNickName").val(edit.userNickName);
-                    body.find(".userAddress").val(edit.userAddress);
-                    body.find(".userEmail").val(edit.userEmail);  //邮箱
-                    body.find(".userSex input[value=" + edit.userSex + "]").prop("checked", "checked");  //性别
-                    body.find(".allowedNetworkType").val(edit.allowedNetworkType);
-                    body.find(".userType").val(edit.userType);  //会员等级
-                    body.find(".userStatus").val(edit.userStatus);    //用户状态
-                    body.find(".userDescription").text(edit.userDescription);    //用户简介
-                    body.find(".remark").text(edit.remark);
+                if (bookData) {
+                    var body = layer.getChildFrame('body', index);
+                    // 填充图书数据
+                    Object.keys(bookData).forEach(function(key) {
+                        var input = body.find('.' + key);
+                        if (input.length) {
+                            if (input.is(':radio')) {
+                                input.filter('[value="' + bookData[key] + '"]').prop('checked', true);
+                            } else {
+                                input.val(bookData[key]);
+                            }
+                        }
+                    });
                     form.render();
                 }
-                setTimeout(function () {
-                    layui.layer.tips('点击此处返回分类列表', '.layui-layer-setwin .layui-layer-close', {
-                        tips: 3
-                    });
-                }, 500)
             }
         });
-        layui.layer.full(index);
-        window.sessionStorage.setItem("index", index);
-        //改变窗口大小时,重置弹窗的宽高,防止超出可视区域(如F12调出debug的操作)
-        $(window).on("resize", function () {
-            layui.layer.full(window.sessionStorage.getItem("index"));
-        })
-    }
-    //时间转换函数
-    function showTime(tempDate) {
-        var d = new Date(tempDate);
-        var year = d.getFullYear();
-        var month = d.getMonth();
-        month++;
-        var day = d.getDate();
-        var hours = d.getHours();
-
-        var minutes = d.getMinutes();
-        var seconds = d.getSeconds();
-        month = month < 10 ? "0" + month : month;
-        day = day < 10 ? "0" + day : day;
-        hours = hours < 10 ? "0" + hours : hours;
-        minutes = minutes < 10 ? "0" + minutes : minutes;
-        seconds = seconds < 10 ? "0" + seconds : seconds;
-
-
-        var time = year + "-" + month + "-" + day + " " + hours + ":" + minutes + ":" + seconds;
-        return time;
     }
-    // 生成分类卡片(网格视图)
-    function renderCategoriesGrid() {
-        const booksGrid = document.getElementById('booksGrid');
+
+    // 渲染图书网格视图
+    function renderBooksGrid(infoName,categoryCode) {
+        var requestData = $.extend({}, CONFIG.grid, {
+            infoName: infoName,
+            categoryCode: categoryCode
+        });
+
         $.ajax({
             url: "/silos/cms/book/info/queryPage",
             type: "POST",
             dataType: "json",
-            async: false,
             contentType: 'application/json;charset=UTF-8',
-            data: JSON.stringify({
-                pageNo:1,
-                pageRows:100,
-                infoName:$("#search_value").val()
-            }),
+            data: JSON.stringify(requestData),
             success: function (res) {
-                const data = res.data.dataList;
-                if (data.length === 0) {
-                    booksGrid.innerHTML = `
-                    <div class="empty-state">
-                        <i class="fas fa-book-open"></i>
-                        <h3>暂无图书</h3>
-                        <p>点击"添加图书"按钮开始建立您的图书馆</p>
-                    </div>
-                `;
-                    return;
+                if (res.code === 200) {
+                    renderGridItems(res.data.dataList);
+                } else {
+                    showError("加载失败", res.msg);
                 }
+            },
+            error: function (xhr, status, error) {
+                console.error("请求失败:", error);
+                showError("请求失败", "网络错误或服务器异常");
+            }
+        });
+    }
 
-                booksGrid.innerHTML = data.map(book => `
-                <div class="book-card" onclick="readChapter(${book.id},${book.title})">
-                    <div class="book-cover" style="background-color: ${book.coverColor}">
-                        <span style="color: white; font-weight: bold; font-size: 1.5rem;">${book.title.substring(0, 2)}</span>
+    // 渲染网格项目
+    function renderGridItems(books) {
+        if (!books || books.length === 0) {
+            domCache.booksGrid.html(`
+                <div class="empty-state">
+                    <i class="fas fa-book-open"></i>
+                    <h3>暂无图书</h3>
+                    <p>点击"添加图书"按钮开始建立您的图书馆</p>
+                </div>
+            `);
+            return;
+        }
+
+        var html = books.map(function(book) {
+            var coverColor = book.coverColor || getRandomColor();
+            var initials = book.title ? book.title.substring(0, 2) : '??';
+
+            return `
+                <div class="book-card" data-id="${book.id}" data-title="${book.title || ''}">
+                    <div class="book-cover" style="background-color: ${coverColor}">
+                        <span style="color: white; font-weight: bold; font-size: 1.5rem;">${initials}</span>
                     </div>
                     <div class="book-info">
-                        <h3 class="book-title">${book.title}</h3>
-                        <p class="book-author">${book.author}</p>
+                        <h3 class="book-title">${book.title || '未命名'}</h3>
+                        <p class="book-author">${book.author || '未知作者'}</p>
                         <div class="book-meta">
-                            <span>${book.publisher}</span>
-                            <span>${book.pages}页</span>
+                            <span>${book.publisher || '未知出版社'}</span>
+                            <span>${book.pages || 0}页</span>
                         </div>
-                        <span class="book-category">${book.category}</span>
+                        <span class="book-category">${book.category || '未分类'}</span>
                         <div class="book-actions">
-                            <button class="action-btn edit-btn">
+                            <button class="action-btn edit-btn" data-action="edit">
                                 <i class="fas fa-edit"></i> 编辑
                             </button>
-                            <button class="action-btn delete-btn">
+                            <button class="action-btn delete-btn" data-action="delete">
                                 <i class="fas fa-trash"></i> 删除
                             </button>
                         </div>
                     </div>
                 </div>
-            `).join('');
+            `;
+        }).join('');
+
+        domCache.booksGrid.html(html);
+    }
+
+    //初始化分类
+    function initCategory() {
+        var requestData = $.extend({}, CONFIG.grid);
+        $.ajax({
+            url: "/silos/cms/book/categories/queryPage",
+            type: "POST",
+            dataType: "json",
+            contentType: 'application/json;charset=UTF-8',
+            data: JSON.stringify(requestData),
+            success: function (res) {
+                if (res.code === 200) {
+                    setCategory(res.data.dataList);
+                } else {
+                    showError("加载失败", res.msg);
+                }
+            },
+            error: function (xhr, status, error) {
+                console.error("请求失败:", error);
+                showError("请求失败", "网络错误或服务器异常");
+            }
+        });
+    }
+
+    function setCategory(categories){
+        if (!categories || categories.length === 0) {
+            return;
+        }
+        const staticData = [];
+        categories.forEach(category => {
+            staticData.push({value:category.categoryCode, text:category.categoryName}); // 使用id作为键,整个对象作为值
+        });
+        // 创建渲染器实例
+        var selectRenderer = new SelectRenderer({
+            placeholderText: '请选择',
+            valueField: 'id',
+            textField: 'name'
+        });
+        // 渲染单个select
+        selectRenderer.render('#book_category', staticData);
+    }
+
+    // 初始化表格
+    function initTable() {
+        table.render($.extend(true, {}, CONFIG.table, {
+            elem: '#list',
+            dataType: 'json',
+            headers: {token: ''},
+            id: "listTable",
+            cellMinWidth: 95,
+            height: "full-125",
+            parseData: parseTableData,
+            cols: getTableColumns()
+        }));
+    }
+
+    // 解析表格数据
+    function parseTableData(res) {
+        return {
+            "code": res.code,
+            "msg": res.msg,
+            "totalRows": res.data.totalRows,
+            "data": res.data.dataList
+        };
+    }
+
+    // 获取表格列配置
+    function getTableColumns() {
+        return [[
+            {type: "checkbox", fixed: "left", width: 50},
+            {field: 'isbn', title: '国际标准书号', minWidth: 100, align: "center"},
+            {field: 'title', title: '图书正题名', minWidth: 100, align: "center"},
+            {field: 'subtitle', title: '副题名', minWidth: 100, align: "center"},
+            {
+                field: 'parentId', title: '父级分类', align: 'center', templet: function (d) {
+                    return getParentCategoryName(d.parentId);
+                }
             },
-            error: function (e) {
-                console.log(e);
+            {field: 'author', title: '第一作者', minWidth: 100, align: "center"},
+            {field: 'otherAuthors', title: '其他责任者', minWidth: 100, align: "center"},
+            {field: 'publisher', title: '出版社', align: 'center'},
+            {field: 'edition', title: '版次', minWidth: 100, align: "center"},
+            {field: 'categoryCode', title: '分类代码', minWidth: 100, align: "center"},
+            {field: 'language', title: '正文语种', minWidth: 100, align: "center"},
+            {field: 'coverImage', title: '封面图片路径', minWidth: 100, align: "center"},
+            {
+                field: 'createdAt', title: '创建时间', minWidth: 100, align: "center", templet: function (d) {
+                    return formatTime(d.createTime);
+                }
+            },
+            {
+                field: 'updatedAt', title: '修改时间', minWidth: 100, align: "center", templet: function (d) {
+                    return formatTime(d.updateTime);
+                }
+            },
+            {title: '操作', minWidth: 200, templet: '#listBar', fixed: "right", align: "center"}
+        ]];
+    }
+
+    // 获取父级分类名称
+    function getParentCategoryName(parentId) {
+        var categoryMap = {
+            "0": "顶级分类",
+            "1": "一级分类",
+            "2": "二级分类"
+        };
+        return categoryMap[parentId] || "三级分类";
+    }
+
+    // 搜索功能
+    function searchBooks() {
+        const infoName = domCache.searchInput.val().trim();
+        const categoryCode = domCache.categoryCode.val().trim();
+        // 同时更新表格和网格视图
+        reloadTable(infoName,categoryCode);
+        renderBooksGrid(infoName,categoryCode);
+    }
+    // 重新加载表格
+    function reloadTable(infoName,categoryCode) {
+        table.reload("listTable", {
+            page: {curr: 1},
+            where: {
+                infoName: infoName,
+                categoryCode: categoryCode
             }
-        })
+        });
     }
-    function readChapter(id,title){
-        layer.open({
+    // 打开章节页面
+    function openChapterPage(bookId, bookTitle) {
+        //如果写成layer.open,将会在所有页面最上层 而不是当前iframe展开
+        layui.layer.open({
             type: 2,
-            title: title,
+            title: bookTitle ? bookTitle + ' - 章节管理' : '章节管理',
             shadeClose: true,
             shade: 0.8,
             area: ['100%', '100%'],
-            content: '/silos/cms/book/chapter/chapter.html' // iframe 的 url
+            content: '/silos/cms/book/chapter/chapter.html'
         });
     }
-    // 自定义绑定事件方法
-    function bind_event(dom, fn, type, child,) {
-        if (typeof dom == 'object' && dom != 'undefined') {
-            type = type == null ? "click" : type;
-            if (child != "" && child != 'object') {
-                return dom.on(type, child, fn);
-            } else {
-                return dom.on(type, fn);
-            }
-        }
-    };
-    // 刷新页面
-    bind_event($(".search_refresh"), function () {
-        window.location.reload();
-    });
-    $(function () {
-        renderCategoriesGrid();
-        //切换tab页的显示
+
+    // 获取随机颜色
+    function getRandomColor() {
+        var colors = ['#3498db', '#2ecc71', '#e74c3c', '#f39c12', '#9b59b6'];
+        return colors[Math.floor(Math.random() * colors.length)];
+    }
+
+    // 显示错误信息
+    function showError(title, msg) {
+        layer.msg(msg || '操作失败', {icon: 2});
+    }
+
+    // 时间格式化
+    function formatTime(timestamp) {
+        if (!timestamp) return '';
+
+        var date = new Date(timestamp);
+        if (isNaN(date.getTime())) return timestamp;
+
+        var year = date.getFullYear();
+        var month = (date.getMonth() + 1).toString().padStart(2, '0');
+        var day = date.getDate().toString().padStart(2, '0');
+        var hours = date.getHours().toString().padStart(2, '0');
+        var minutes = date.getMinutes().toString().padStart(2, '0');
+        var seconds = date.getSeconds().toString().padStart(2, '0');
+
+        return year + "-" + month + "-" + day + " " + hours + ":" + minutes + ":" + seconds;
+    }
+
+    // 视图切换功能
+    function initViewToggle() {
         $(document).on('click', '.view-btn', function (e) {
             // 视图切换功能
             const viewButtons = document.querySelectorAll('.view-btn');
@@ -270,7 +336,106 @@ layui.config({
                     }
                 });
             });
-        })
+        });
+    }
+
+    // 网格视图事件委托
+    function initGridEvents() {
+        domCache.booksGrid.on('click', function(e) {
+            var target = $(e.target);
+            var bookCard = target.closest('.book-card');
+
+            if (!bookCard.length) return;
+
+            var bookId = bookCard.data('id');
+            var bookTitle = bookCard.data('title');
+
+            // 卡片点击(非按钮区域)
+            if (!target.closest('.book-actions').length) {
+                openChapterPage(bookId, bookTitle);
+                return;
+            }
+
+            // 按钮点击
+            var actionBtn = target.closest('.action-btn');
+            if (!actionBtn.length) return;
+
+            var action = actionBtn.data('action');
+
+            switch(action) {
+                case 'edit':
+                    editBook({id: bookId, title: bookTitle});
+                    break;
+                case 'delete':
+                    layer.confirm('确定删除这本图书吗?', {
+                        title: '删除确认',
+                        btn: ['确定', '取消']
+                    }, function(index) {
+                        // 这里添加删除逻辑
+                        layer.close(index);
+                    });
+                    break;
+            }
+        });
+    }
+    // 表格操作事件监听
+    function initTableEvents() {
+        table.on('tool(listTable)', function(obj) {
+            var data = obj.data;
+            var event = obj.event;
+
+            switch(event) {
+                case 'edit':
+                    editBook(data);
+                    break;
+                case 'delete':
+                    layer.confirm('确定删除这本图书吗?', {
+                        title: '删除确认',
+                        btn: ['确定', '取消']
+                    }, function(index) {
+                        // 这里添加删除逻辑
+                        layer.close(index);
+                    });
+                    break;
+            }
+        });
+    }
+
+
+    // 初始化事件绑定
+    function initEvents() {
+        // 搜索按钮
+        domCache.searchBtn.on('click', searchBooks);
+
+        // 回车搜索
+        domCache.searchInput.on('keypress', function(e) {
+            if (e.keyCode === 13) {
+                searchBooks();
+            }
+        });
+
+        // 添加按钮
+        domCache.addBtn.on('click', function() {
+            editBook();
+        });
+
+        // 刷新按钮
+        domCache.refreshBtn.on('click', function() {
+            location.reload();
+        });
+    }
+    // 初始化
+    function init() {
+        initCategory();
+        initEvents();
+        initViewToggle();
+        initGridEvents();
+        initTableEvents();
+        // 初始加载数据
+        renderBooksGrid('','');
+        initTable();
+    }
 
-    });
-})
+    // 启动初始化
+    init();
+});

+ 0 - 9
imwork-windows/imwork-silos/src/main/resources/templates/cms/book/category/list.html

@@ -27,15 +27,6 @@
 <body>
 <div class="container">
     <section class="stats-section">
-        <div class="stat-card">
-            <div class="stat-icon" style="background-color: #e0e7ff; color: #4f46e5;">
-                <i class="fas fa-tags logo-icon"></i>
-            </div>
-            <div class="stat-info">
-                <h1>图书分类管理</h1>
-                <p>全面管理您的图书分类</p>
-            </div>
-        </div>
         <div class="stat-card">
             <div class="stat-icon" style="background-color: #e0e7ff; color: #4f46e5;">
                 <i class="fas fa-tags"></i>

+ 2 - 1
imwork-windows/imwork-silos/src/main/resources/templates/cms/book/info/list.html

@@ -32,7 +32,7 @@
         <div class="filters">
             <div class="filter-group">
                 <label>分类</label>
-                <select>
+                <select class="form-select" id="book_category">
                     <option>全部</option>
                     <option>文学</option>
                     <option>科技</option>
@@ -108,6 +108,7 @@
 </div>
 <script src="../../../../static/assets/lib/jquery/jquery.js" th:src="@{/assets/lib/jquery/jquery.js}"></script>
 <script src="../../../../static/assets/lib/layui/layui.js" th:src="@{/assets/lib/layui/layui.js}"></script>
+<script src="../../../../static/assets/silos/js/SelectRenderer.js" th:src="@{/assets/silos/js/SelectRenderer.js}"></script>
 <script src="../../../../static/business/cms/book/info/list.js" th:src="@{/business/cms/book/info/list.js}"></script>
 </body>
 </html>

+ 418 - 0
配套资料/工具使用说明/使用 jQuery 根据类选择器渲染 select 说明.txt

@@ -0,0 +1,418 @@
+js SelectRenderer封装类
+/**
+ * Select渲染器
+ */
+class SelectRenderer {
+    constructor(config = {}) {
+        this.config = $.extend({
+            placeholder: true,
+            placeholderText: '请选择',
+            allowClear: false,
+            searchable: false,
+            ajax: false,
+            data: [],
+            valueField: 'value',
+            textField: 'text',
+            disabledField: 'disabled',
+            selectedField: 'selected',
+            groupField: 'group'
+        }, config);
+    }
+
+    /**
+     * 渲染单个select
+     * @param {jQuery|string} selector - 选择器或jQuery对象
+     * @param {Array|Object} data - 数据
+     * @param {Object} options - 选项
+     */
+    render(selector, data = null, options = {}) {
+        var $select = $(selector);
+        if (!$select.length) return;
+
+        var config = $.extend({}, this.config, options);
+        var renderData = data || config.data;
+
+        // 清空现有选项
+        $select.empty();
+
+        // 添加占位符
+        if (config.placeholder) {
+            var $placeholder = $('<option>', {
+                value: '',
+                text: config.placeholderText
+            });
+
+            if (config.allowClear) {
+                $placeholder.attr('data-allow-clear', 'true');
+            }
+
+            $select.append($placeholder);
+        }
+
+        // 渲染选项
+        if ($.isArray(renderData)) {
+            this._renderOptions($select, renderData, config);
+        } else if (renderData && renderData.url) {
+            this._renderWithAjax($select, renderData, config);
+        }
+
+        // 触发渲染完成事件
+        $select.trigger('select:rendered');
+
+        return $select;
+    }
+
+    /**
+     * 渲染多个select
+     * @param {string} className - 类选择器
+     * @param {Array} data - 数据
+     * @param {Object} options - 选项
+     */
+    renderAll(className, data = null, options = {}) {
+        var self = this;
+        var results = [];
+
+        $(className).each(function() {
+            var $select = $(this);
+            var specificOptions = $.extend({}, options);
+
+            // 从data-*属性读取配置
+            var dataAttrs = self._getDataAttributes($select);
+            $.extend(specificOptions, dataAttrs);
+
+            results.push(self.render($select, data, specificOptions));
+        });
+
+        return results;
+    }
+
+    /**
+     * 内部方法:渲染选项
+     */
+    _renderOptions($select, data, config) {
+        var groups = {};
+
+        $.each(data, function(index, item) {
+            // 分组处理
+            if (config.groupField && item[config.groupField]) {
+                var groupName = item[config.groupField];
+                if (!groups[groupName]) {
+                    groups[groupName] = [];
+                }
+                groups[groupName].push(item);
+            } else {
+                var $option = $('<option>', {
+                    value: item[config.valueField] || item.value || '',
+                    text: item[config.textField] || item.text || '',
+                    disabled: item[config.disabledField] || item.disabled || false,
+                    selected: item[config.selectedField] || item.selected || false
+                });
+
+                // 添加自定义属性
+                $.each(item, function(key, value) {
+                    if (!['value', 'text', 'disabled', 'selected'].includes(key)) {
+                        $option.attr('data-' + key, value);
+                    }
+                });
+
+                $select.append($option);
+            }
+        });
+
+        // 渲染分组
+        this._renderOptionGroups($select, groups, config);
+    }
+
+    /**
+     * 内部方法:渲染分组
+     */
+    _renderOptionGroups($select, groups, config) {
+        $.each(groups, function(groupName, items) {
+            var $optgroup = $('<optgroup>', {
+                label: groupName
+            });
+
+            $.each(items, function(index, item) {
+                var $option = $('<option>', {
+                    value: item[config.valueField] || item.value || '',
+                    text: item[config.textField] || item.text || '',
+                    disabled: item[config.disabledField] || item.disabled || false,
+                    selected: item[config.selectedField] || item.selected || false
+                });
+
+                $optgroup.append($option);
+            });
+
+            $select.append($optgroup);
+        });
+    }
+
+    /**
+     * 内部方法:通过AJAX渲染
+     */
+    _renderWithAjax($select, ajaxConfig, config) {
+        var self = this;
+
+        // 显示加载状态
+        $select.prop('disabled', true).addClass('loading');
+
+        $.ajax($.extend({
+            type: 'GET',
+            dataType: 'json',
+            success: function(response) {
+                // 处理响应数据
+                var data = response;
+                if (ajaxConfig.dataProcessor && typeof ajaxConfig.dataProcessor === 'function') {
+                    data = ajaxConfig.dataProcessor(response);
+                } else if (response.data) {
+                    data = response.data;
+                }
+
+                // 渲染数据
+                self._renderOptions($select, data, config);
+
+                // 恢复select状态
+                $select.prop('disabled', false).removeClass('loading');
+
+                // 触发完成事件
+                $select.trigger('select:ajax:complete', [response]);
+            },
+            error: function(xhr, status, error) {
+                console.error('加载下拉框数据失败:', error);
+
+                // 添加错误选项
+                $select.empty();
+                $select.append($('<option>', {
+                    value: '',
+                    text: '数据加载失败',
+                    disabled: true
+                }));
+
+                // 恢复状态
+                $select.prop('disabled', false).removeClass('loading');
+
+                // 触发错误事件
+                $select.trigger('select:ajax:error', [xhr, status, error]);
+            }
+        }, ajaxConfig));
+    }
+
+    /**
+     * 获取data-*属性
+     */
+    _getDataAttributes($element) {
+        var attrs = {};
+        var data = $element.data();
+
+        // 转换属性名
+        $.each(data, function(key, value) {
+            if (key.startsWith('select')) {
+                var newKey = key.replace(/^select/, '');
+                newKey = newKey.charAt(0).toLowerCase() + newKey.slice(1);
+                attrs[newKey] = value;
+            }
+        });
+
+        return attrs;
+    }
+
+    /**
+     * 设置选中值
+     */
+    setValue($select, value) {
+        $select.val(value).trigger('change');
+        return this;
+    }
+
+    /**
+     * 启用/禁用
+     */
+    setDisabled($select, disabled) {
+        $select.prop('disabled', disabled);
+        return this;
+    }
+
+    /**
+     * 重新加载数据
+     */
+    reload($select) {
+        var dataAttrs = this._getDataAttributes($select);
+        if (dataAttrs.url) {
+            this._renderWithAjax($select, { url: dataAttrs.url }, dataAttrs);
+        }
+        return this;
+    }
+}
+
+使用示例
+HTML结构
+<!-- 基础select -->
+<select class="form-select" id="category1"></select>
+
+<!-- 带配置的select -->
+<select class="form-select"
+        data-select-url="/api/categories"
+        data-select-value-field="id"
+        data-select-text-field="name"
+        data-select-placeholder="选择分类">
+</select>
+
+<!-- 多个相同配置的select -->
+<select class="department-select" data-department-id="1"></select>
+<select class="department-select" data-department-id="2"></select>
+<select class="department-select" data-department-id="3"></select>
+
+JavaScript使用
+$(function() {
+    // 创建渲染器实例
+    var selectRenderer = new SelectRenderer({
+        placeholderText: '请选择',
+        valueField: 'id',
+        textField: 'name'
+    });
+
+    // 示例1:渲染静态数据
+    var staticData = [
+        {id: '1', name: '技术部'},
+        {id: '2', name: '市场部', disabled: true},
+        {id: '3', name: '人事部'},
+        {id: '4', name: '财务部'}
+    ];
+
+    selectRenderer.render('#category1', staticData);
+
+    // 示例2:通过AJAX渲染单个select
+    selectRenderer.render('.form-select[data-select-url]', {
+        url: '/api/departments',
+        dataProcessor: function(response) {
+            // 处理返回数据
+            return response.list || response.data || [];
+        }
+    });
+
+    // 示例3:批量渲染相同class的select
+    $('.department-select').each(function() {
+        var departmentId = $(this).data('department-id');
+        var url = '/api/employees?deptId=' + departmentId;
+
+        selectRenderer.render(this, {
+            url: url,
+            placeholderText: '选择员工'
+        });
+    });
+
+    // 示例4:使用事件监听
+    $('.form-select').on('select:rendered', function() {
+        console.log('Select已渲染:', $(this).attr('id'));
+    }).on('change', function() {
+        console.log('选择的值:', $(this).val());
+    });
+});
+
+扩展:集成Select2(如果需要增强功能)
+/**
+ * 集成Select2的渲染器
+ */
+class Select2Renderer extends SelectRenderer {
+    constructor(config = {}) {
+        var defaultSelect2Config = {
+            theme: 'bootstrap',
+            width: '100%',
+            allowClear: true,
+            placeholder: '请选择'
+        };
+
+        super($.extend({
+            select2: defaultSelect2Config
+        }, config));
+    }
+
+    render(selector, data = null, options = {}) {
+        var $select = super.render(selector, data, options);
+
+        // 初始化Select2
+        if (options.select2 || this.config.select2) {
+            var select2Config = $.extend({}, this.config.select2, options.select2);
+
+            // 设置placeholder
+            if (options.placeholderText) {
+                select2Config.placeholder = options.placeholderText;
+            }
+
+            $select.select2(select2Config);
+
+            // 监听销毁事件,防止内存泄漏
+            $(window).on('unload', function() {
+                $select.select2('destroy');
+            });
+        }
+
+        return $select;
+    }
+
+    setValue($select, value) {
+        if ($select.data('select2')) {
+            $select.val(value).trigger('change.select2');
+        } else {
+            super.setValue($select, value);
+        }
+        return this;
+    }
+}
+
+// 使用Select2Renderer
+$(function() {
+    var select2Renderer = new Select2Renderer();
+
+    select2Renderer.render('.select2-enhanced', {
+        url: '/api/data',
+        select2: {
+            theme: 'bootstrap4',
+            ajax: {
+                url: '/api/search',
+                dataType: 'json',
+                delay: 250,
+                data: function(params) {
+                    return {
+                        q: params.term,
+                        page: params.page
+                    };
+                }
+            }
+        }
+    });
+});
+
+CSS样式建议
+/* 加载状态 */
+select.loading {
+    background-image: url('loading-spinner.svg');
+    background-repeat: no-repeat;
+    background-position: right 10px center;
+    background-size: 16px 16px;
+    padding-right: 35px;
+}
+
+/* 错误状态 */
+select.error {
+    border-color: #dc3545;
+    background-color: #fff5f5;
+}
+
+/* 禁用状态 */
+select:disabled {
+    background-color: #e9ecef;
+    cursor: not-allowed;
+}
+
+/* 分组样式 */
+optgroup {
+    font-weight: bold;
+    font-style: normal;
+    color: #495057;
+}
+
+optgroup option {
+    font-weight: normal;
+    padding-left: 20px;
+}

+ 277 - 0
配套资料/模块/book/list.js

@@ -0,0 +1,277 @@
+layui.config({
+    base: '/assets/lib/jplus/treeTable/'
+}).use(['form', 'layer', 'table', 'laytpl', 'treeTable'], function () {
+    var form = layui.form,
+        layer = parent.layer === undefined ? layui.layer : top.layer,
+        $ = layui.jquery,
+        laytpl = layui.laytpl,
+        table = layui.table,
+        treeTable = layui.treeTable;
+
+    //列表
+    var tableIns = table.render({
+        elem: '#list',
+        url: '/silos/cms/book/info/queryPage',
+        method: 'POST',
+        dataType: 'json',
+        contentType: 'application/json;charset=utf-8',
+        headers: {token: ''},
+        cellMinWidth: 95,
+        page: true,
+        height: "full-125",
+        limits: [10, 15, 20, 25],
+        limit: 15,
+        id: "listTable",
+        request: {
+            pageName: "pageNo",//重新定义当前页码参数名称
+            limitName: "pageRows"//重新定义每页大小参数名称
+        },
+        response: {
+            statusName: 'code', //数据状态的字段名称,默认:code
+            statusCode: 200, //成功的状态码,默认:0
+            msgName: 'msg',  //状态信息的字段名称,默认:msg
+            countName: 'totalRows',  //数据总数的字段名称,默认:count
+            dataName: 'data' //数据列表的字段名称,默认:data
+        },
+        where: {
+            //请求的参数写在where
+        },
+        parseData: function (res) {
+            return {
+                "code": res.code,
+                "msg": res.msg, //解析提示文本
+                "totalRows": res.data.totalRows, //解析数据长度
+                "data": res.data.dataList, //解析数据列表
+            };
+        },
+        cols: [[
+            {type: "checkbox", fixed: "left", width: 50},
+            {field: 'isbn', title: '国际标准书号', minWidth: 100, align: "center"},
+            {field: 'title', title: '图书正题名', minWidth: 100, align: "center"},
+            {field: 'subtitle', title: '副题名', minWidth: 100, align: "center"},
+            {
+                field: 'parentId', title: '父级分类', align: 'center', templet: function (d) {
+                    if (d.parentId == "0") {
+                        return "顶级分类";
+                    } else if (d.parentId == "1") {
+                        return "一级分类";
+                    } else if (d.parentId == "2") {
+                        return "二级分类";
+                    } else {
+                        return "三级分类";
+                    }
+                }
+            },
+            {field: 'author', title: '第一作者', minWidth: 100, align: "center"},
+            {field: 'otherAuthors', title: '其他责任者', minWidth: 100, align: "center"},
+            {field: 'publisher', title: '出版社', align: 'center'},
+
+            {field: 'edition', title: '版次', minWidth: 100, align: "center"},
+            /*{field: 'userAvatar', title: '用户头像', minWidth:100, align:"center",templet:function(d){
+                    return "<img src='"+d.userAvatar+"' style='width: 30px;height: 30px'>";
+            }},*/
+            {field: 'categoryCode', title: '分类代码', minWidth: 100, align: "center"},
+            {field: 'language', title: '正文语种', minWidth: 100, align: "center"},
+            {field: 'coverImage', title: '封面图片路径', minWidth: 100, align: "center"},
+            {
+                field: 'createdAt', title: '创建时间', minWidth: 100, align: "center", templet: function (d) {
+                    return showTime(d.createTime);
+                }
+            },
+            {
+                field: 'updatedAt', title: '修改时间', minWidth: 100, align: "center", templet: function (d) {
+                    return showTime(d.updateTime);
+                }
+            },
+            {title: '操作', minWidth: 200, templet: '#listBar', fixed: "right", align: "center", toolBar: '#listBar'}
+        ]]
+    });
+    $(".search_btn").click(function () {
+        const infoName = $("#search_value").val();
+        renderCategoriesGrid();
+        table.reload("listTable", {
+            page: {
+                curr: 1 //重新从第 1 页开始
+            },
+            where: {
+                infoName: infoName
+            }
+        })
+    })
+
+    $('.book-card').click(function (e) {
+        layer.open({
+            type: 2,
+            title: title,
+            shadeClose: true,
+            shade: 0.8,
+            area: ['100%', '100%'],
+            content: '/silos/cms/book/chapter/chapter.html' // iframe 的 url
+        })
+    })
+
+    $(".add_btn").click(function () {
+        edit();
+    })
+    // 添加/修改
+    function edit(edit) {
+        let title = "添加图书";
+        if (edit != null) {
+            title = "添加图书";
+        }
+        const index = layui.layer.open({
+            title: title,
+            type: 2,
+            content: "/silos/cms/book/info/edit.html",
+            scrollbar: false,
+            success: function (layero, index) {
+                var body = layui.layer.getChildFrame('body', index);
+                if (edit) {
+                    body.find(".uid").val(BigInt(edit.id).toString());
+                    body.find(".userName").val(edit.userName);  //用户名
+                    body.find(".loginName").val(edit.loginName);  //登录名
+                    body.find(".userMobilePhone").val(edit.userMobilePhone);
+                    body.find(".userNickName").val(edit.userNickName);
+                    body.find(".userAddress").val(edit.userAddress);
+                    body.find(".userEmail").val(edit.userEmail);  //邮箱
+                    body.find(".userSex input[value=" + edit.userSex + "]").prop("checked", "checked");  //性别
+                    body.find(".allowedNetworkType").val(edit.allowedNetworkType);
+                    body.find(".userType").val(edit.userType);  //会员等级
+                    body.find(".userStatus").val(edit.userStatus);    //用户状态
+                    body.find(".userDescription").text(edit.userDescription);    //用户简介
+                    body.find(".remark").text(edit.remark);
+                    form.render();
+                }
+                setTimeout(function () {
+                    layui.layer.tips('点击此处返回分类列表', '.layui-layer-setwin .layui-layer-close', {
+                        tips: 3
+                    });
+                }, 500)
+            }
+        });
+        layui.layer.full(index);
+        window.sessionStorage.setItem("index", index);
+        //改变窗口大小时,重置弹窗的宽高,防止超出可视区域(如F12调出debug的操作)
+        $(window).on("resize", function () {
+            layui.layer.full(window.sessionStorage.getItem("index"));
+        })
+    }
+    //时间转换函数
+    function showTime(tempDate) {
+        var d = new Date(tempDate);
+        var year = d.getFullYear();
+        var month = d.getMonth();
+        month++;
+        var day = d.getDate();
+        var hours = d.getHours();
+
+        var minutes = d.getMinutes();
+        var seconds = d.getSeconds();
+        month = month < 10 ? "0" + month : month;
+        day = day < 10 ? "0" + day : day;
+        hours = hours < 10 ? "0" + hours : hours;
+        minutes = minutes < 10 ? "0" + minutes : minutes;
+        seconds = seconds < 10 ? "0" + seconds : seconds;
+
+
+        var time = year + "-" + month + "-" + day + " " + hours + ":" + minutes + ":" + seconds;
+        return time;
+    }
+    // 生成分类卡片(网格视图)
+    function renderCategoriesGrid() {
+        const booksGrid = document.getElementById('booksGrid');
+        $.ajax({
+            url: "/silos/cms/book/info/queryPage",
+            type: "POST",
+            dataType: "json",
+            async: false,
+            contentType: 'application/json;charset=UTF-8',
+            data: JSON.stringify({
+                pageNo:1,
+                pageRows:100,
+                infoName:$("#search_value").val()
+            }),
+            success: function (res) {
+                const data = res.data.dataList;
+                if (data.length === 0) {
+                    booksGrid.innerHTML = `
+                    <div class="empty-state">
+                        <i class="fas fa-book-open"></i>
+                        <h3>暂无图书</h3>
+                        <p>点击"添加图书"按钮开始建立您的图书馆</p>
+                    </div>
+                `;
+                    return;
+                }
+
+                booksGrid.innerHTML = data.map(book => `
+                <div class="book-card" bookid="${book.id}" bookTitle="${book.title}">
+                    <div class="book-cover" style="background-color: ${book.coverColor}">
+                        <span style="color: white; font-weight: bold; font-size: 1.5rem;">${book.title.substring(0, 2)}</span>
+                    </div>
+                    <div class="book-info">
+                        <h3 class="book-title">${book.title}</h3>
+                        <p class="book-author">${book.author}</p>
+                        <div class="book-meta">
+                            <span>${book.publisher}</span>
+                            <span>${book.pages}页</span>
+                        </div>
+                        <span class="book-category">${book.category}</span>
+                        <div class="book-actions">
+                            <button class="action-btn edit-btn">
+                                <i class="fas fa-edit"></i> 编辑
+                            </button>
+                            <button class="action-btn delete-btn">
+                                <i class="fas fa-trash"></i> 删除
+                            </button>
+                        </div>
+                    </div>
+                </div>
+            `).join('');
+            },
+            error: function (e) {
+                console.log(e);
+            }
+        })
+    }
+
+    // 自定义绑定事件方法
+    function bind_event(dom, fn, type, child,) {
+        if (typeof dom == 'object' && dom != 'undefined') {
+            type = type == null ? "click" : type;
+            if (child != "" && child != 'object') {
+                return dom.on(type, child, fn);
+            } else {
+                return dom.on(type, fn);
+            }
+        }
+    };
+    // 刷新页面
+    bind_event($(".search_refresh"), function () {
+        window.location.reload();
+    });
+    $(function () {
+        renderCategoriesGrid();
+        //切换tab页的显示
+        $(document).on('click', '.view-btn', function (e) {
+            // 视图切换功能
+            const viewButtons = document.querySelectorAll('.view-btn');
+            viewButtons.forEach(btn => {
+                btn.addEventListener('click', function() {
+                    const viewType = this.getAttribute('data-view');
+
+                    viewButtons.forEach(b => b.classList.remove('active'));
+                    this.classList.add('active');
+
+                    if (viewType === 'grid') {
+                        document.getElementById('booksGrid').classList.add('active');
+                        document.getElementById('booksTable').classList.remove('active');
+                    } else {
+                        document.getElementById('booksGrid').classList.remove('active');
+                        document.getElementById('booksTable').classList.add('active');
+                    }
+                });
+            });
+        })
+    });
+})