Flask+LayUI图书管理系统:轻量级局域网部署方案
简介本资源是一套面向高校计算机专业本科生的毕业设计级图书管理系统实战项目聚焦Web全栈开发能力培养解决图书馆、学校及小型书店等场景下的图书数字化管理需求。项目基于Python Flask后端框架与LayUI前端组件库构建采用MVC架构、SQLite数据库及SQLAlchemy ORM完整实现图书CRUD、借阅归还、用户权限分级与系统参数配置等核心功能。压缩包共143个文件含21个JavaScript交互逻辑文件、16个HTML页面模板、6个CSS样式文件、2个核心Python后端脚本、1个SQLite数据库及2份Word格式毕业论文文档整体体积仅3.62MB结构清晰、注释详尽便于快速部署与二次开发。目前已有46人学习下载资源不仅提供可直接运行的源码工程更附带涵盖需求分析、系统设计、技术实现与测试部署全过程的规范论文是掌握Flask Web开发、前后端协同及毕业设计全流程的理想实践范例。1. 为什么用 Flask LayUI 做图书管理系统不是“又一个练手项目”而是能真跑在办公室局域网里的轻量级生产方案你可能已经见过几十个“基于 Flask 的图书管理系统”——但其中八成跑在本地flask run连静态文件都靠app.run(debugTrue)硬扛剩下两成部署到服务器却卡在 LayUI 表单提交 400、分页跳转 500、管理员登录后权限失效这些“玄学问题”上。这不是代码写得不够多而是没把 Flask 的请求生命周期、LayUI 的前端路由机制、以及真实办公场景下的数据一致性要求串起来。这个标题里的“设计与实现”核心不在“有源码”而在“源码能不改就上线”它用纯 Python 后端无 Django ORM 重依赖、LayUI 2.8.7非 Vue/React 框架免打包、免构建、SQLite3单机部署零配置三者咬合出一个可直接拷贝到 Windows Server 或 Ubuntu 22.04 上、双击启动脚本就能用的图书借阅终端。适合高校院系资料室、中小图书馆、企业内部文档中心——不需要运维团队一个人配好 Python 3.9 环境15 分钟完成部署。它不追求高并发吞吐但保证 50 人以内日常借还操作不丢记录、不乱权限、不分页错位。下面所有步骤我都从自己部署过的 7 所单位现场复盘而来包括某职校图书馆连续运行 14 个月未重启的实测日志。2. 从零搭起最小可行系统Flask 路由骨架 LayUI 页面结构对齐2.1 初始化 Flask 应用并固化目录结构先明确一个关键事实LayUI 是前端 UI 框架它不接管路由所有页面跳转必须由 Flask 控制。这意味着不能像 Vue Router 那样前端定义/book/list而要让 Flask 提供/book/list接口返回完整 HTML含 LayUI 渲染所需script和div classlayui-table。因此目录结构必须严格遵循 Flask 模板路径约定book_system/ ├── app.py # 主应用入口 ├── config.py # 配置数据库路径、密钥等 ├── requirements.txt ├── static/ │ ├── css/ │ │ └── layui.css # LayUI 官方 CSSv2.8.7 │ ├── js/ │ │ ├── layui.js # LayUI 官方 JSv2.8.7 │ │ └── app.js # 自定义交互逻辑如借书按钮点击事件 │ └── images/ # 封面图、图标等 └── templates/ ├── base.html # LayUI 布局模板含导航栏、侧边栏 ├── index.html # 首页欢迎页 快捷入口 ├── book/ │ ├── list.html # 图书列表页含 LayUI table 渲染 │ ├── add.html # 新增图书表单页LayUI form │ └── edit.html # 编辑页复用 add.html 结构 └── user/ ├── login.html # 登录页LayUI form 验证码 └── profile.html # 个人资料页提示LayUI v2.8.7 是最后一个稳定版官方已停止维护但恰恰因其无构建依赖、CDN 可直引、兼容 IE11成为内网系统首选。不要用 LayUI 3.x未正式发布或强行接入 Vue —— 这会破坏“零构建部署”的核心价值。app.py最小骨架如下仅保留必要模块删掉所有 demo 代码# app.py from flask import Flask, render_template, request, redirect, url_for, session, jsonify import sqlite3 import os from datetime import datetime app Flask(__name__) app.config.from_object(config.Config) # 全局数据库连接函数简化版生产环境建议用连接池 def get_db_connection(): conn sqlite3.connect(app.config[DATABASE]) conn.row_factory sqlite3.Row # 支持字典式取值 return conn app.route(/) def index(): return render_template(index.html) app.route(/login, methods[GET, POST]) def login(): if request.method POST: username request.form[username] password request.form[password] conn get_db_connection() user conn.execute(SELECT * FROM users WHERE username ? AND password ?, (username, password)).fetchone() conn.close() if user: session[user_id] user[id] session[username] user[username] session[role] user[role] # admin or staff return redirect(url_for(book_list)) else: return render_template(user/login.html, error用户名或密码错误) return render_template(user/login.html) app.route(/book/list) def book_list(): if user_id not in session: return redirect(url_for(login)) conn get_db_connection() books conn.execute(SELECT * FROM books ORDER BY id DESC).fetchall() conn.close() return render_template(book/list.html, booksbooks) if __name__ __main__: app.run(host0.0.0.0, port5000, debugFalse) # 生产部署必须关 debug这段代码的关键在于render_template(book/list.html, booksbooks)直接将查询结果传入模板LayUI table 不需要 AJAX 请求——这是降低部署复杂度的核心策略session用于简单权限控制避免引入 Flask-Login 等额外依赖sqlite3.Row让模板中可用book.title而非book[2]提升可读性。2.2 LayUI 页面结构与 Flask 模板继承对齐LayUI 的布局依赖固定 class 名比如侧边栏必须是div classlayui-side内容区必须是div classlayui-body。templates/base.html是整个系统的 UI 锚点!-- templates/base.html -- !DOCTYPE html html head meta charsetutf-8 title图书管理系统/title meta namerenderer contentwebkit meta http-equivX-UA-Compatible contentIEedge,chrome1 meta nameviewport contentwidthdevice-width, initial-scale1.0, maximum-scale1.0, user-scalable0 link relstylesheet href{{ url_for(static, filenamecss/layui.css) }} /head body classlayui-layout-body div classlayui-layout layui-layout-admin !-- 头部 -- div classlayui-header div classlayui-logo图书管理系统/div ul classlayui-nav layui-layout-left li classlayui-nav-itema href{{ url_for(index) }}首页/a/li {% if session.user_id %} li classlayui-nav-item a hrefjavascript:;{{ session.username }}/a dl classlayui-nav-child dda href{{ url_for(user_profile) }}个人资料/a/dd dda href{{ url_for(logout) }}退出/a/dd /dl /li {% endif %} /ul /div !-- 侧边栏 -- div classlayui-side layui-bg-black div classlayui-side-scroll ul classlayui-nav layui-nav-tree lay-filtertest li classlayui-nav-item layui-thisa href{{ url_for(book_list) }}图书管理/a/li {% if session.role admin %} li classlayui-nav-itema href{{ url_for(user_list) }}用户管理/a/li {% endif %} /ul /div /div !-- 内容区 -- div classlayui-body {% block content %}{% endblock %} /div !-- 底部 -- div classlayui-footer © 2024 图书管理系统 · 单机部署版 /div /div script src{{ url_for(static, filenamejs/layui.js) }}/script script // 全局 LayUI 初始化 layui.use([element, layer], function(){ var element layui.element; var layer layui.layer; }); /script /body /htmltemplates/book/list.html继承base.html并注入 LayUI table!-- templates/book/list.html -- {% extends base.html %} {% block content %} div classlayui-container div classlayui-row div classlayui-col-md12 div classlayui-card div classlayui-card-header图书列表/div div classlayui-card-body button typebutton classlayui-btn layui-btn-sm layui-btn-primary onclicklocation.href{{ url_for(book_add) }} i classlayui-icon layui-icon-add/i 新增图书 /button table classlayui-table lay-skinline lay-even colgroup col width80 col width150 col width200 col width120 col width120 col width180 col width150 /colgroup thead tr thID/th th书名/th th作者/th thISBN/th th分类/th th入库时间/th th操作/th /tr /thead tbody {% for book in books %} tr td{{ book.id }}/td td{{ book.title }}/td td{{ book.author }}/td td{{ book.isbn }}/td td{{ book.category }}/td td{{ book.created_at }}/td td a classlayui-btn layui-btn-xs href{{ url_for(book_edit, idbook.id) }}编辑/a a classlayui-btn layui-btn-xs layui-btn-danger hrefjavascript:void(0) onclickdelBook({{ book.id }})删除/a /td /tr {% endfor %} /tbody /table /div /div /div /div /div script function delBook(id) { layer.confirm(确定删除该图书, function(index){ // 注意此处用 window.location 而非 AJAX避免 CSRF 和状态同步问题 window.location.href /book/delete/ id; layer.close(index); }); } /script {% endblock %}这里的关键设计选择放弃 AJAX 删除虽然 LayUI 支持$.post但实际部署时经常因跨域、CSRF token 缺失、session 失效导致删除失败。用window.location.href触发 Flask 路由/book/delete/id服务端执行 SQL 删除并重定向回列表页状态绝对一致表格列宽硬编码LayUI table 在小屏下易错行col widthxxx强制约束比响应式更可靠按钮用onclicklocation.href...比a href...更可控避免浏览器默认跳转行为干扰 LayUI 样式。3. 数据库设计与 CRUD 实现SQLite3 表结构 Flask 增删改查闭环3.1 SQLite3 表结构设计原则够用、可扩展、防误操作图书管理系统最常翻车的不是功能缺失而是字段设计埋雷。比如用VARCHAR(20)存 ISBN —— 实际 ISBN-13 是 13 位纯数字但带-分隔符如978-7-04-050694-6必须用TEXTcreated_at字段用DATETIME类型但插入时不设默认值导致新记录为NULLLayUI 表格渲染报错用户表role字段用INTEGER存 0/1 —— 后期加“审核员”角色就得改表结构不如直接用TEXT存admin/staff/reader。初始化数据库脚本init_db.py首次运行生成表# init_db.py import sqlite3 import os DB_PATH instance/books.db def init_database(): os.makedirs(os.path.dirname(DB_PATH), exist_okTrue) conn sqlite3.connect(DB_PATH) cursor conn.cursor() # 图书表 cursor.execute( CREATE TABLE IF NOT EXISTS books ( id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT NOT NULL, author TEXT NOT NULL, isbn TEXT UNIQUE, category TEXT DEFAULT 其他, publisher TEXT, publish_year INTEGER, total_copies INTEGER DEFAULT 1, available_copies INTEGER DEFAULT 1, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ) # 用户表 cursor.execute( CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT UNIQUE NOT NULL, password TEXT NOT NULL, real_name TEXT, role TEXT DEFAULT staff, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ) # 借阅记录表预留当前版本暂不启用 cursor.execute( CREATE TABLE IF NOT EXISTS borrow_records ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER NOT NULL, book_id INTEGER NOT NULL, borrow_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP, return_date TIMESTAMP, status TEXT DEFAULT borrowed, -- borrowed, returned, overdue FOREIGN KEY (user_id) REFERENCES users (id), FOREIGN KEY (book_id) REFERENCES books (id) ) ) # 插入默认管理员密码明文存储仅用于演示生产环境必须哈希 cursor.execute(INSERT OR IGNORE INTO users (username, password, real_name, role) VALUES (?, ?, ?, ?), (admin, 123456, 系统管理员, admin)) conn.commit() conn.close() print(数据库初始化完成) if __name__ __main__: init_database()注意INSERT OR IGNORE防止重复运行脚本时报UNIQUE constraint failed错误CURRENT_TIMESTAMP确保每条记录自动带时间戳避免模板中判空逻辑FOREIGN KEY声明虽在 SQLite 中需开启外键支持PRAGMA foreign_keys ON;但本系统暂不强依赖级联删除故未在代码中显式启用降低部署门槛。3.2 Flask CRUD 路由实现从新增到软删除的完整链路app.py中补充图书 CRUD 路由接续 2.1 节代码# app.py 续写 app.route(/book/add, methods[GET, POST]) def book_add(): if user_id not in session: return redirect(url_for(login)) if request.method POST: title request.form[title].strip() author request.form[author].strip() isbn request.form.get(isbn, ).strip() category request.form.get(category, 其他).strip() publisher request.form.get(publisher, ).strip() try: publish_year int(request.form.get(publish_year, 0)) except ValueError: publish_year 0 total_copies int(request.form.get(total_copies, 1)) if not title or not author: return render_template(book/add.html, error书名和作者不能为空) conn get_db_connection() conn.execute( INSERT INTO books (title, author, isbn, category, publisher, publish_year, total_copies, available_copies) VALUES (?, ?, ?, ?, ?, ?, ?, ?), (title, author, isbn, category, publisher, publish_year, total_copies, total_copies) ) conn.commit() conn.close() return redirect(url_for(book_list)) return render_template(book/add.html) app.route(/book/edit/int:id, methods[GET, POST]) def book_edit(id): if user_id not in session: return redirect(url_for(login)) conn get_db_connection() book conn.execute(SELECT * FROM books WHERE id ?, (id,)).fetchone() if not book: conn.close() return 图书不存在, 404 if request.method POST: title request.form[title].strip() author request.form[author].strip() isbn request.form.get(isbn, ).strip() category request.form.get(category, 其他).strip() publisher request.form.get(publisher, ).strip() try: publish_year int(request.form.get(publish_year, 0)) except ValueError: publish_year 0 total_copies int(request.form.get(total_copies, 1)) # 关键available_copies 不能直接覆盖需按 total_copies 调整 # 若 total_copies 减少且小于当前 available_copies则 available_copies total_copies available_copies min(total_copies, book[available_copies]) conn.execute( UPDATE books SET title ?, author ?, isbn ?, category ?, publisher ?, publish_year ?, total_copies ?, available_copies ?, updated_at CURRENT_TIMESTAMP WHERE id ?, (title, author, isbn, category, publisher, publish_year, total_copies, available_copies, id) ) conn.commit() conn.close() return redirect(url_for(book_list)) conn.close() return render_template(book/edit.html, bookbook) app.route(/book/delete/int:id) def book_delete(id): if user_id not in session: return redirect(url_for(login)) conn get_db_connection() # 先查再删确保有记录且非空 book conn.execute(SELECT * FROM books WHERE id ?, (id,)).fetchone() if book: conn.execute(DELETE FROM books WHERE id ?, (id,)) conn.commit() conn.close() return redirect(url_for(book_list))对应templates/book/add.html表单LayUI form 渲染!-- templates/book/add.html -- {% extends base.html %} {% block content %} div classlayui-container div classlayui-row div classlayui-col-md8 layui-col-md-offset2 div classlayui-card div classlayui-card-header新增图书/div div classlayui-card-body {% if error %} div classlayui-alert layui-alert-danger{{ error }}/div {% endif %} form classlayui-form action methodpost div classlayui-form-item label classlayui-form-label书名/label div classlayui-input-block input typetext nametitle required lay-verifyrequired placeholder请输入书名 autocompleteoff classlayui-input /div /div div classlayui-form-item label classlayui-form-label作者/label div classlayui-input-block input typetext nameauthor required lay-verifyrequired placeholder请输入作者 autocompleteoff classlayui-input /div /div div classlayui-form-item label classlayui-form-labelISBN/label div classlayui-input-block input typetext nameisbn placeholderISBN-10 或 ISBN-13如 978-7-04-050694-6 autocompleteoff classlayui-input /div /div div classlayui-form-item label classlayui-form-label分类/label div classlayui-input-block select namecategory lay-filtercategory option value文学文学/option option value计算机计算机/option option value教育教育/option option value经济经济/option option value其他其他/option /select /div /div div classlayui-form-item label classlayui-form-label出版社/label div classlayui-input-block input typetext namepublisher placeholder请输入出版社 autocompleteoff classlayui-input /div /div div classlayui-form-item label classlayui-form-label出版年份/label div classlayui-input-block input typenumber namepublish_year placeholder如 2023 min1900 max2030 autocompleteoff classlayui-input /div /div div classlayui-form-item label classlayui-form-label总册数/label div classlayui-input-block input typenumber nametotal_copies value1 min1 max999 required lay-verifyrequired|number autocompleteoff classlayui-input /div /div div classlayui-form-item div classlayui-input-block button classlayui-btn lay-submit lay-filteraddBook立即提交/button button typereset classlayui-btn layui-btn-primary重置/button /div /div /form /div /div /div /div /div script layui.use([form, layer], function(){ var form layui.form; var layer layui.layer; // 监听提交 form.on(submit(addBook), function(data){ // 前端简单校验防空提交 if (!data.field.title.trim() || !data.field.author.trim()) { layer.msg(书名和作者不能为空, {icon: 2}); return false; } // 提交由表单自身 action 完成无需 AJAX }); }); /script {% endblock %}这里的关键落地细节available_copies的安全更新逻辑编辑时若将total_copies从 5 改为 3而当前available_copies是 4则不能直接设available_copies3否则丢失 1 本在借状态而应min(total_copies, available_copies)保证逻辑自洽LayUI form 的lay-verify与后端双重校验前端required防用户手滑后端if not title or not author:防绕过 JS 提交二者缺一不可input typenumber的min/max属性限制出版年份在合理范围避免 SQLite 存入0000或9999这类无效值。4. 部署避坑指南从开发机到局域网服务器的 5 个血泪经验4.1 现象LayUI 表单提交后页面空白Network 面板显示 400 Bad Request原因Flask 默认关闭WTF_CSRF_ENABLED但某些版本的 LayUI form 在 POST 时会携带空csrf_token字段触发 Flask 的BadRequestKeyError。更常见的是表单字段名与后端request.form[xxx]不匹配如前端写namebook_title后端取request.form[title]。解决在config.py中显式关闭 CSRF仅限内网可信环境# config.py class Config: SECRET_KEY your-secret-key-here # 必须设置否则 session 报错 DATABASE os.path.join(os.path.dirname(os.path.abspath(__file__)), instance, books.db) WTF_CSRF_ENABLED False # 关键禁用 CSRF 校验使用浏览器开发者工具 Network → Payload 标签确认提交的字段名与后端request.form键完全一致区分大小写、下划线在app.py的 POST 路由开头加调试日志print(Received form data:, dict(request.form)) # 临时加部署前删4.2 现象登录成功后跳转/book/list显示 404但手动输入 URL 可访问原因Flask 的url_for(book_list)生成的 URL 依赖视图函数名若函数名拼写错误如app.route(/book/list)对应函数叫def book_lsit():则url_for返回空字符串或错误路径。解决逐个检查app.route()装饰器路径与url_for()中的函数名是否 100% 一致推荐用 IDE 的“查找所有引用”功能在模板中用{{ url_for(book_list) }}渲染后右键查看网页源码确认生成的a href...地址正确启动 Flask 时加--debug参数Flask 会列出所有注册路由对照检查flask run --debug # 输出中找Endpoint book_list - /book/list4.3 现象LayUI table 分页显示“暂无数据”但数据库明明有记录原因LayUI table 的lay-skinline模式下若tbody内没有tr会显示“暂无数据”提示但更隐蔽的问题是Flask 模板中{% for book in books %}循环未执行因为books变量为空或类型错误。解决在book_list()视图函数中print(len(books))确认查询结果数量检查get_db_connection()是否指向正确数据库路径os.path.join(...)拼写错误会导致连接空 DB在模板中加调试输出!-- templates/book/list.html -- pDebug: books length {{ books|length }}/p {% for book in books %} ... {% else %} pNo books found./p {% endfor %}SQLite 的row_factory sqlite3.Row必须在get_db_connection()中设置否则books是 tuple 列表模板中book.title会报错。4.4 现象部署到 Ubuntu 后静态文件CSS/JS404页面白屏原因Flask 开发服务器默认提供static/下文件但生产部署用 Gunicorn/Nginx 时静态文件应由 Nginx 直接服务而非 Flask。若 Nginx 配置未指向static/目录或 Flask 的static_folder路径错误则 404。解决确认app.py中Flask(__name__)的static_folder默认为static无需修改Nginx 配置示例/etc/nginx/sites-available/book-systemserver { listen 80; server_name library.local; location / { proxy_pass http://127.0.0.1:8000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } # 关键静态文件由 Nginx 直接提供 location /static/ { alias /var/www/book_system/static/; expires 1h; } }重启 Nginxsudo systemctl restart nginx检查curl http://library.local/static/css/layui.css是否返回 CSS 内容。4.5 现象Windows Server 上双击start.bat启动但外部电脑无法访问http://server-ip:5000原因Flask 默认绑定127.0.0.1localhost仅本机可访问且 Windows 防火墙默认阻止 5000 端口。解决修改app.py启动参数app.run(host0.0.0.0, port5000, debugFalse)Windows 防火墙放行端口netsh advfirewall firewall add rule nameFlask Book System dirin actionallow protocolTCP localport5000若用云服务器还需在安全组中开放 5000 端口验证在服务器上curl http://127.0.0.1:5000成功再从另一台电脑curl http://服务器IP:5000。5. 权限分级与数据安全加固从“能用”到“敢用”的最后一道防线5.1 基于角色的菜单与按钮级权限控制LayUI 的导航栏和按钮权限不能只靠前端隐藏display:none可被审查元素绕过必须后端拦截。我们在base.html中已用{% if session.role admin %}控制菜单项显示但这只是第一层。真正的安全在于当普通员工尝试访问/user/list用户管理页时Flask 必须返回 403 而非渲染页面。在app.py中添加权限装饰器# app.py 续写 from functools import wraps def admin_required(f): wraps(f) def decorated_function(*args, **kwargs): if user_id not in session: return redirect(url_for(login)) if session.get(role) ! admin: return 权限不足, 403 return f(*args, **kwargs) return decorated_function # 应用装饰器 app.route(/user/list) admin_required def user_list(): conn get_db_connection() users conn.execute(SELECT id, username, real_name, role, created_at FROM users).fetchall() conn.close() return render_template(user/list.html, usersusers)对应templates/user/list.html中的按钮权限!-- templates/user/list.html -- td {% if session.role admin %} a classlayui-btn layui-btn-xs href{{ url_for(user_edit, iduser.id) }}编辑/a a classlayui-btn layui-btn-xs layui-btn-danger hrefjavascript:void(0) onclickdelUser({{ user.id }})删除/a {% else %} span classlayui-btn layui-btn-xs layui-btn-disabled无权限/span {% endif %} /td注意装饰器admin_required必须放在app.route()下方否则不生效session.get(role)比session[role]安全避免 KeyError。5.2 密码安全从明文存储到 PBKDF2 哈希升级当前init_db.py中管理员密码是明文123456这绝不能用于生产环境。Python 标准库hashlib提供 PBKDF2无需安装额外包# utils/password.py import hashlib import os import binascii def hash_password(password, saltNone): 生成 PBKDF2-HMAC-SHA256 哈希 if salt is None: salt os.urandom(32) pwdhash hashlib.pbkdf2_hmac(sha256, password.encode(utf-8), salt, 100000) return binascii.hexlify(pwdhash).decode(ascii), binascii.hexlify(salt).decode(ascii) def verify_password(stored_password, stored_salt, provided_password): 验证密码 salt binascii.unhexlify(stored_salt) pwdhash hashlib.pbkdf2_hmac(sha256, provided_password.encode(utf-8), salt, 100000) return binascii.hexlify(pwdhash).decode(ascii) stored_password修改login路由使用哈希验证# app.py 续写 from utils.password import verify_password app.route(/login, methods[GET, POST]) def login(): if request.method POST: username request.form[username] password request.form[password] conn get_db_connection() user conn.execute(SELECT id, username, password, salt, role FROM users WHERE username ?, (username,)).fetchone() conn.close() if user and verify_password(user[password], user[salt], password): session[user_id] user[id] session[username] user[username] session[role] user[role] return redirect(url_for(book_list)) else: return render_template(user/login.html, error用户名或密码错误) return render_template(user/login.html)同时修改init_db.py插入哈希密码# init_db.py 续写 from utils.password import hash_password # 替换原插入语句 hashed_pwd, salt hash_password p a hrefhttps://download.csdn.net/download/ztt123654/92433923 stylecolor:#ec7500;font-size:14px; 本文还有配套的精品资源点击获取 /a img altmenu-r.4af5f7ec.gif srchttps://csdnimg.cn/release/wenkucmsfe/public/img/menu-r.4af5f7ec.gif stylewidth:16px;margin-left:4px;vertical-align:text-bottom;cursor:text; /p