飞牛 Nas 快速部署 Emby
飞牛 Nas 快速部署 Emby
一、准备工作
Docker-Compose-Manager:点我获取部署教程
二、部署过程
打开 Docker-Compose-Manager 的“获取 Gitee 拉取文件”界面,下载好 Emby 的 yml 文件,点击“编辑”

个性化定制(可跳过本步骤继续快速部署)
可更改内容:
1、端口号:20105
2、映射目录:/vol1/1000/ 后,可改为想要的目录(例:/vol1/1000/XXXX:/config)
services:
emby:
image: amilys/embyserver:latest
container_name: embyserver
privileged: true
ports:
- 20105:8096
volumes:
- /vol1/1000/Docker/emby/config:/config
- /vol1/1000/Video:/data
restart: always
network_mode: bridge部署成功后,通过 IP + 端口号访问容器界面,选择语言为:Chinese Simplified

配置登录账号和密码,其余全部下一步即可

跳转到登录界面,输入刚才注册的帐号和密码就可以进入 Emby 的主界面了

进入进入设置,配置媒体库(下面以电视节目为例)

左侧栏选择“媒体库”选项,跳转到媒体库界面,选择“新增媒体库”,弹出配置窗口,按照自己的需求配置即可

等待媒体库刮削完毕即可展示所有影视资源了

三、进阶玩法
更改媒体服务器名称

一键激活付费版

编码设置(注:本教程虚拟机所有没显示硬件,但是已经开启高级模式了自行编辑即可)

插件安装(注:提前重启一遍 Emby 才会显示脚本)

## 媒体库id获取方式:点击媒体库后,复制链接将其后面的 parentId 数字复制到脚本
MediaId="3"
## extmod='["embyLaunchPotplayer","ede.user","actorPlus"]' 替换掉 extmod='[]'
extmod='[]'
## 保存即可并重启 Emby首页封面海报、外部媒体播放器调用、弹幕插件



自动更新媒体库封面,打开 Docker-Compose-Manager 的“获取 Gitee 拉取文件”界面,下载好 jellyfin-library-poster 的 yml 文件,点击“编辑

个性化定制(可跳过本步骤继续快速部署)
可更改内容:
1、映射目录:/vol1/1000/ 后,可改为想要的目录(例:/vol1/1000/XXXX:/config)
services:
jellyfin-library-poster:
image: evanqu/jellyfin-library-poster:latest
container_name: jellyfin-library-poster
volumes:
- /vol1/1000/Docker/jellyfin-library-poster/config:/app/config
- /vol1/1000/Docker/jellyfin-library-poster/poster:/app/poster
- /vol1/1000/Docker/jellyfin-library-poster/output:/app/output
- /vol1/1000/Docker/jellyfin-library-poster/logs:/app/logs
- /vol1/1000/Docker/jellyfin-library-poster/myfont:/app/myfont
restart: always
network_mode: bridge配置完毕创建 config.json 文件(注:按需配置即可)
{
"jellyfin": [
{
"server_name": "MyJellyfin", // 【必填】Jellyfin/Emby 服务器名称,用于标识不同服务器
"server_type": "jellyfin", // 【必填】Jellyfin/Emby 服务器类型,仅支持 jellyfin/emby 两种取值
"base_url": "http://192.168.2.211:8089", // 【必填】Jellyfin/Emby 服务器访问地址(IP+端口)
"user_name": "user", // 【必填】Jellyfin/Emby 登录用户名
"password": "pass", // 【必填】Jellyfin/Emby 登录用户密码
"update_poster": false // 【非必填】是否自动上传更新媒体库海报到服务器(会覆盖原有海报,建议先设为false查看生成效果,满意后改为true重新运行)
},
{
// 第二个Jellyfin服务器配置(未配置server_name和server_type,需注意:这两个字段为必填项,建议补充完整)
"base_url": "http://192.168.2.232:8089", // 【必填】第二个Jellyfin/Emby 服务器访问地址(IP+端口)
"user_name": "user", // 【必填】第二个Jellyfin/Emby 登录用户名
"password": "pass", // 【必填】第二个Jellyfin/Emby 登录用户密码
"update_poster": false // 【非必填】第二个服务器是否自动上传更新媒体库海报,false为关闭(避免覆盖原有海报)
}
],
"cron": "0 1 * * *", // 定时任务配置:Cron表达式,含义为每天凌晨1点执行一次定时任务(格式:分 时 日 月 周)
"exclude_update_library": ["Short", "Playlists", "合集"], // 排除更新的媒体库:此数组列出不需要自动更新海报的媒体库名称,匹配library_name
"style_config": [
{
"style_name": "style1", // 【必填】海报样式名称,固定值为style1
"style_ch_font": "字体名带后缀", // 【必填】海报中文字体名称,需带文件后缀(如 微软雅黑.ttf)
"style_eng_font": "字体名带后缀", // 【必填】海报英文字体名称,需带文件后缀(如 微软雅黑.ttf)
"style_ch_shadow": true, // 【非必填】是否启用中文文字阴影,默认值为false,此处设为true开启
"style_ch_shadow_offset": [2, 2], // 【非必填】中文文字阴影偏移量,格式为[x, y],默认值为[2, 2]
"style_eng_shadow": true, // 【非必填】是否启用英文文字阴影,默认值为false,此处设为true开启
"style_eng_shadow_offset": [2, 2] // 【非必填】英文文字阴影偏移量,格式为[x, y],默认值为[2, 2]
}
],
"template_mapping": [
{
"library_name": "Anime", // 【必填】Jellyfin 中的媒体库名称(与服务器端媒体库名称保持一致)
"library_ch_name": "动漫", // 【必填】海报的中文名称(用于海报上显示的中文标识)
"library_eng_name": "ANIME", // 【必填】海报的英文名称(用于海报上显示的英文标识)
"poster_sort": "DateLastContentAdded" // 【非必填】海报排序方式:按最后添加内容排序(可选值:DateCreated/DateLastContentAdded/Random/SortName/SeriesDatePlayed/PremiereDate)
},
{
"library_name": "Classic TV", // 【必填】Jellyfin 中的媒体库名称
"library_ch_name": "电视剧", // 【必填】海报的中文名称
"library_eng_name": "TV", // 【必填】海报的英文名称
"poster_sort": "Random" // 【非必填】海报排序方式:随机排序
},
{
"library_name": "Movie", // 【必填】Jellyfin 中的媒体库名称
"library_ch_name": "电影", // 【必填】海报的中文名称
"library_eng_name": "MOVIE", // 【必填】海报的英文名称
"poster_sort": "DateCreated" // 【非必填】海报排序方式:按创建时间排序
},
{
"library_name": "Documentary", // 【必填】Jellyfin 中的媒体库名称
"library_ch_name": "纪录片", // 【必填】海报的中文名称
"library_eng_name": "DOC" // 【必填】海报的英文名称
// 未配置poster_sort:使用默认排序规则
},
{
"library_name": "合集", // 【必填】Jellyfin 中的媒体库名称(已加入exclude_update_library,将跳过海报更新)
"library_ch_name": "合集", // 【必填】海报的中文名称
"library_eng_name": "COLLECTIONS" // 【必填】海报的英文名称
// 未配置poster_sort:使用默认排序规则
},
{
"library_name": "Hot Movie", // 【必填】Jellyfin 中的媒体库名称
"library_ch_name": "正在热映", // 【必填】海报的中文名称
"library_eng_name": "HOT MOVIE" // 【必填】海报的英文名称
// 未配置poster_sort:使用默认排序规则
},
{
"library_name": "Hot TV", // 【必填】Jellyfin 中的媒体库名称
"library_ch_name": "正在热播", // 【必填】海报的中文名称
"library_eng_name": "HOT TV", // 【必填】海报的英文名称
"poster_sort": "DateLastContentAdded" // 【非必填】海报排序方式:按最后添加内容排序
},
{
"library_name": "Short", // 【必填】Jellyfin 中的媒体库名称(已加入exclude_update_library,将跳过海报更新)
"library_ch_name": "短剧", // 【必填】海报的中文名称
"library_eng_name": "SHORT" // 【必填】海报的英文名称
// 未配置poster_sort:使用默认排序规则
},
{
"library_name": "TEST TV", // 【必填】Jellyfin 中的媒体库名称
"library_ch_name": "测试电视", // 【必填】海报的中文名称
"library_eng_name": "TEST TV" // 【必填】海报的英文名称
// 未配置poster_sort:使用默认排序规则
}
]
}下载所需要的字体:点我下载
将全部 config.json 和字体文件分别放到,根目录的 config 和 myfont 里并重启容器
完成后会自动更新海报内容

支持前端完全隐藏侧边栏(注:难度较大,需要一定动手能力)
## 进入容器
docker exec -it embyserver /bin/sh
## 进入容器UI目录
cd /system/dashboard-ui/
## 编辑 index.html 文件
vi /system/dashboard-ui/index.html
## 清空 index.html 文件内容
:%d
## 复制下方内容,输入 I 进入编辑模式
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="viewport-fit=cover, width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no">
<link rel="manifest" href="manifest.json">
<meta name="description" content="Emby Server">
<meta name="format-detection" content="telephone=no">
<meta name="msapplication-tap-highlight" content="no">
<meta http-equiv="X-UA-Compatibility" content="IE=Edge">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="mobile-web-app-capable" content="yes">
<meta name="application-name" content="Emby">
<meta name="robots" content="noindex, nofollow, noarchive">
<meta property="og:title" content="Emby">
<meta property="og:site_name" content="Emby">
<meta property="og:url" content="https://emby.media">
<meta property="og:description" content="Energize your media.">
<meta property="og:type" content="article">
<meta property="fb:app_id" content="1618309211750238">
<meta name="apple-itunes-app" content="app-id=992180193">
<link rel="apple-touch-icon" href="images/icon-192x192.png">
<link rel="apple-touch-icon" sizes="72x72" href="images/icon-72x72.png">
<link rel="apple-touch-icon" sizes="144x144" href="images/icon-144x144.png">
<link rel="apple-touch-icon" sizes="152x152" href="images/icon-152x152.png">
<link rel="apple-touch-icon" sizes="192x192" href="images/icon-192x192.png">
<link rel="apple-touch-icon" sizes="384x384" href="images/icon-384x384.png">
<link rel="apple-touch-icon" sizes="512x512" href="images/icon-512x512.png">
<link rel="apple-touch-startup-image" href="images/splash.png">
<link rel="shortcut icon" href="favicon.ico">
<meta name="msapplication-TileImage" content="images/icon-144x144.png">
<meta name="msapplication-TileColor" content="#333333">
<meta name="theme-color" content="#43A047">
<script type="importmap">
{
"imports": {
"medialibraryeditor": "./components/medialibraryeditor/medialibraryeditor.js",
"medialibrarycreator": "./components/medialibrarycreator/medialibrarycreator.js",
"taskButton": "./components/taskbutton.js",
"itemShortcuts": "./modules/shortcuts.js",
"pageJs": "./modules/pagejs/page.js",
"howler": "./modules/howlerjs/howler.core.js",
"skinManager": "./modules/skinmanager.js",
"playQueueManager": "./modules/common/playback/playqueuemanager.js",
"playbackManager": "./modules/common/playback/playbackmanager.js",
"focusManager": "./modules/focusmanager.js",
"browserdeviceprofile": "./modules/browserdeviceprofile.js",
"skinViewManager": "./modules/skinviewmanager.js",
"events": "./modules/emby-apiclient/events.js",
"connectionManager": "./modules/emby-apiclient/connectionmanager.js",
"ApiClient": "./modules/emby-apiclient/apiclient.js",
"urlProvider": "./modules/emby-apiclient/urlprovider.js",
"loading": "./modules/loading/loading.js",
"webvtt": "./modules/webvtt/vtt.js",
"humanedate": "./modules/humanedate/humanedate.js",
"serviceLocator": "./modules/common/servicelocator.js",
"globalize": "./modules/common/globalize.js",
"datetime": "./modules/common/datetime.js",
"backdrop": "./modules/backdrop/backdrop.js",
"qualityOptions": "./modules/common/qualityoptions.js",
"pluginManager": "./modules/common/pluginmanager.js",
"dataFormatter": "./modules/common/dataformatter.js",
"itemHelper": "./modules/common/itemhelper.js",
"itemManager": "./modules/common/itemmanager/itemmanager.js",
"BaseItemController": "./modules/common/itemmanager/baseitemcontroller.js",
"recordingHelper": "./modules/common/recordinghelper.js",
"imageLoader": "./modules/common/imagehelper.js",
"imageHelper": "./modules/common/imagehelper.js",
"gamepadtokey": "./modules/input/gamepadtokey.js",
"virtual-scroller": "./modules/virtual-scroller/virtual-scroller.js",
"commandProcessor": "./modules/commandprocessor.js",
"cssVars": "./modules/css-vars-ponyfill/css-vars-ponyfill.js",
"browser": "./modules/browser.js",
"backMenu": "./modules/backmenu/backmenu.js",
"soundEffectsManager": "./modules/soundeffects/soundeffectsmanager.js",
"soundEffectsPlayer": "./modules/soundeffects/soundeffectsplayer.js",
"currentPlayer": "./modules/playback/currentplayer.js",
"mediaSession": "./modules/playback/mediasession.js",
"mainTabsManager": "./modules/maintabsmanager.js",
"dragDropTouch": "./modules/polyfills/dragdroptouch.js",
"appHeader": "./modules/appheader/appheader.js",
"chromecastHelper": "./modules/chromecast/chromecasthelpers.js",
"directorybrowser": "./modules/directorybrowser/directorybrowser.js",
"metadataEditor": "./modules/metadataeditor/metadataeditor.js",
"personEditor": "./modules/metadataeditor/personeditor.js",
"playerSelectionMenu": "./modules/playback/playerselection.js",
"playerSettingsMenu": "./modules/common/playback/playersettingsmenu.js",
"emby-collapse": "./modules/emby-elements/emby-collapse/emby-collapse.js",
"emby-button": "./modules/emby-elements/emby-button/emby-button.js",
"emby-linkbutton": "./modules/emby-elements/emby-button/emby-button.js",
"emby-itemscontainer": "./modules/emby-elements/emby-itemscontainer/emby-itemscontainer.js",
"alphaNumericShortcuts": "./modules/alphanumericshortcuts/alphanumericshortcuts.js",
"emby-dialogclosebutton": "./modules/emby-elements/emby-dialogclosebutton/emby-dialogclosebutton.js",
"emby-scroller": "./modules/emby-elements/emby-scroller/emby-scroller.js",
"emby-tabs": "./modules/emby-elements/emby-tabs/emby-tabs.js",
"emby-scrollbuttons": "./modules/emby-elements/emby-scrollbuttons/emby-scrollbuttons.js",
"emby-progressring": "./modules/emby-elements/emby-progressring/emby-progressring.js",
"emby-itemrefreshindicator": "./modules/emby-elements/emby-itemrefreshindicator/emby-itemrefreshindicator.js",
"multiSelect": "./modules/multiselect/multiselect.js",
"alphaPicker": "./modules/alphapicker/alphapicker.js",
"paper-icon-button-light": "./modules/emby-elements/emby-button/paper-icon-button-light.js",
"tabbedView": "./modules/tabbedview/tabbedview.js",
"itemsTab": "./modules/tabbedview/itemstab.js",
"baseView": "./modules/viewmanager/baseview.js",
"baseTab": "./modules/tabbedview/basetab.js",
"ListPage": "./list/list.js",
"connectHelper": "./modules/emby-connect/connecthelper.js",
"addToList": "./modules/addtolist/addtolist.js",
"ItemAccessDialog": "./modules/itemaccessdialog/itemaccessdialog.js",
"dom": "./modules/dom.js",
"textEncoding": "./modules/common/textencoding.js",
"playerStats": "./modules/playerstats/playerstats.js",
"subtitleOffsetDialog": "./modules/subtitleoffsetdialog/subtitleoffsetdialog.js",
"subtitleAppearanceHelper": "./modules/common/subtitleappearancehelper.js",
"recordingEditor": "./modules/recordingcreator/recordingeditor.js",
"seriesRecordingEditor": "./modules/recordingcreator/seriesrecordingeditor.js",
"recordingFields": "./modules/recordingcreator/recordingfields.js",
"recordingButton": "./modules/recordingcreator/recordingbutton.js",
"subtitleEditor": "./modules/subtitleeditor/subtitleeditor.js",
"itemIdentifier": "./modules/itemidentifier/itemidentifier.js",
"imageEditor": "./modules/imageeditor/imageeditor.js",
"imageDownloader": "./modules/imagedownloader/imagedownloader.js",
"itemContextMenu": "./modules/itemcontextmenu.js",
"emby-input": "./modules/emby-elements/emby-input/emby-input.js",
"emby-select": "./modules/emby-elements/emby-select/emby-select.js",
"emby-multilineselect": "./modules/emby-elements/emby-multilineselect/emby-multilineselect.js",
"emby-slider": "./modules/emby-elements/emby-slider/emby-slider.js",
"emby-checkbox": "./modules/emby-elements/emby-checkbox/emby-checkbox.js",
"emby-progressbar": "./modules/emby-elements/emby-progressbar/emby-progressbar.js",
"emby-radio": "./modules/emby-elements/emby-radio/emby-radio.js",
"emby-toggle": "./modules/emby-elements/emby-toggle/emby-toggle.js",
"emby-textarea": "./modules/emby-elements/emby-textarea/emby-textarea.js",
"emby-downloadbutton": "./modules/emby-elements/sync/emby-downloadbutton.js",
"emby-playstatebutton": "./modules/emby-elements/userdatabuttons/emby-playstatebutton.js",
"emby-ratingbutton": "./modules/emby-elements/userdatabuttons/emby-ratingbutton.js",
"guide-settings-dialog": "./modules/emby-elements/guide/guide-settings.js",
"tvguide": "./modules/emby-elements/guide/guide.js",
"emby-premierecontainer": "./modules/emby-elements/emby-premierecontainer/emby-premierecontainer.js",
"serverRestartDialog": "./modules/serverrestartdialog/serverrestartdialog.js",
"channelRecordingCreator": "./modules/recordingcreator/channelrecordingcreator.js",
"refreshDialog": "./modules/refreshdialog/refreshdialog.js",
"cardBuilder": "./modules/cardbuilder/cardbuilder.js",
"mouseManager": "./modules/input/mouse.js",
"keyboardManager": "./modules/input/keyboard.js",
"loadingDialog": "./modules/loadingdialog/loadingdialog.js",
"syncDialog": "./modules/sync/sync.js",
"syncJobEditor": "./modules/sync/syncjobeditor.js",
"morphdom": "./modules/morphdom/morphdom.js",
"viewManager": "./modules/viewmanager/viewmanager.js",
"responseHelper": "./modules/common/responsehelper.js",
"formHelper": "./modules/common/responsehelper.js",
"slideshow": "./modules/slideshow/slideshow.js",
"listView": "./modules/listview/listview.js",
"indicators": "./modules/indicators/indicators.js",
"mediaInfo": "./modules/mediainfo/mediainfo.js",
"viewSettings": "./modules/viewsettings/viewsettings.js",
"filterMenu": "./modules/filtermenu/filtermenu.js",
"genericedit": "./modules/genericedit/genericedit.js",
"registrationServices": "./modules/registrationservices/registrationservices.js",
"serversync": "./modules/sync/serversync.js",
"multiserversync": "./modules/sync/multiserversync.js",
"scroller": "./modules/scroller/smoothscroller.js",
"toast": "./modules/toast/toast.js",
"layoutManager": "./modules/layoutmanager.js",
"appSettings": "./modules/common/appsettings.js",
"userSettings": "./modules/common/usersettings/usersettings.js",
"userSettingsBuilder": "./modules/common/usersettings/usersettingsbuilder.js",
"imageUploader": "./modules/imageuploader/imageuploader.js",
"dockedTabs": "./modules/dockedtabs/dockedtabs.js",
"navdrawer": "./modules/navdrawer/navdrawer.js",
"navDrawerContent": "./modules/navdrawer/navdrawercontent.js",
"queryString": "./modules/common/querystring.js",
"alert": "./modules/common/dialogs/alert.js",
"confirm": "./modules/common/dialogs/confirm.js",
"dialog": "./modules/dialog/dialog.js",
"dialogHelper": "./modules/dialoghelper/dialoghelper.js",
"prompt": "./modules/prompt/prompt.js",
"screensaverManager": "./modules/screensavermanager.js",
"serverNotifications": "./modules/common/input/api.js",
"headroom": "./modules/headroom/headroom.js",
"appFooter": "./modules/appfooter/appfooter.js",
"appRouter": "./modules/approuter.js",
"actionsheet": "./modules/actionsheet/actionsheet.js",
"multi-download": "./modules/multidownload.js",
"localassetmanager": "./modules/localdatabase/localassetmanager.js",
"inputManager": "./modules/common/inputmanager.js"
}
}
</script>
<title>Emby</title>
<style>
.app-splash-container {
background-color: #000;
position: fixed;
z-index: 99999;
top: 0;
left: 0;
right: 0;
bottom: 0;
contain: strict;
}
.app-splash {
background-image: url(modules/themes/logowhite.png);
background-position: center top;
background-repeat: no-repeat;
background-size: contain;
position: fixed;
top: 1.8em;
left: .9em;
width: 6.6em;
height: 2em;
contain: strict;
z-index: 99999;
}
.app-splash-expanded {
top: 30%;
left: 30%;
right: 30%;
height: 20%;
width: auto;
}
.hide {
display: none !important;
}
@media (orientation: landscape) {
.app-splash-expanded {
left: 37.5%;
right: 37.5%;
}
}
/* 新增:侧边栏展开/隐藏样式 */
.mainDrawer {
display: block !important;
position: fixed;
top: 60px;
left: 0;
bottom: 0;
width: 0;
background-color: #1a1a1a;
z-index: 9999;
overflow: hidden;
transition: width 0.3s ease;
box-shadow: 2px 0 8px rgba(0,0,0,0.3);
padding: 16px;
color: #fff;
}
.mainDrawer.show {
width: 280px;
}
body.sidebar-show .skinHeader,
body.sidebar-show .backgroundContainer,
body.sidebar-show .app-splash-container {
margin-left: 280px;
transition: margin-left 0.3s ease;
}
#toggleSidebar {
background-color: #43A047;
color: white;
border: none;
border-radius: 4px;
margin: 0 8px;
padding: 4px 8px;
cursor: pointer;
}
#toggleSidebar:hover {
background-color: #388E3C;
}
</style>
<link rel="stylesheet" id="theme-css" href="emby-crx/style.css" type="text/css" media="all" />
<script src="emby-crx/common-utils.js"></script>
<script src="emby-crx/jquery-3.6.0.min.js"></script>
<script src="emby-crx/md5.min.js"></script>
<script src="emby-crx/config.js"></script>
<script src="emby-crx/main.js"></script>
</head>
<body class="mainAnimatedPages skinBody">
<div class="backdropContainer"></div>
<div class="backgroundContainer"></div>
<div class="mainDrawer hide focuscontainer padded-bottom-page" is="emby-scroller" data-miniscrollbar="true" data-horizontal="false" data-focusscroll="true" data-navcommands="card" data-bindheader="false">
<div class="scrollSlider mainDrawerScrollSlider"></div>
</div>
<div class="skinHeader focuscontainer-x focuscontainer-up headroom flex align-items-center flex-grow headerTop">
<div class="headerLeft headerSection focuscontainer-x navout-x">
<!-- 新增侧边栏切换按钮 -->
<button id="toggleSidebar" class="emby-button">☰ 展开侧边栏</button>
</div>
<div class="headerMiddle headerSection sectionTabs">
</div>
<div class="headerRight headerSection focuscontainer-x navout-x">
</div>
</div>
<div class="app-splash-container">
<div class="app-splash app-splash-expanded"></div>
</div>
<script src="apploader.js" defer></script>
<!--script src="danmaku.min.js"></script-->
<script data-main="ext" src="require.js"></script>
<!-- 新增:侧边栏交互脚本 -->
<script>
document.addEventListener('DOMContentLoaded', function() {
const toggleBtn = document.getElementById('toggleSidebar');
const sidebar = document.querySelector('.mainDrawer');
const body = document.body;
// 初始隐藏侧边栏
sidebar.classList.remove('show');
body.classList.remove('sidebar-show');
// 切换按钮逻辑
toggleBtn.addEventListener('click', function() {
if (sidebar.classList.contains('show')) {
sidebar.classList.remove('show');
body.classList.remove('sidebar-show');
toggleBtn.textContent = '☰ 展开侧边栏';
} else {
sidebar.classList.add('show');
body.classList.add('sidebar-show');
toggleBtn.textContent = '✕ 隐藏侧边栏';
}
});
// 点击空白处隐藏侧边栏
document.addEventListener('click', function(e) {
if (!sidebar.contains(e.target) && e.target !== toggleBtn) {
sidebar.classList.remove('show');
body.classList.remove('sidebar-show');
toggleBtn.textContent = '☰ 展开侧边栏';
}
});
// ESC键隐藏侧边栏
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape') {
sidebar.classList.remove('show');
body.classList.remove('sidebar-show');
toggleBtn.textContent = '☰ 展开侧边栏';
}
});
});
</script>
</body>
</html>
## 结束编辑
:wq