Deprecated: Creation of dynamic property Typecho\Widget\Request::$feed is deprecated in /www/wwwroot/blog.iletter.top/var/Widget/Archive.php on line 246
白荼日记 - 技术笔记 https://blog.iletter.top/tag/%E6%8A%80%E6%9C%AF%E7%AC%94%E8%AE%B0/ 1+ACE2系统优化笔记 https://blog.iletter.top/archives/495/ 2025-11-01T21:01:00+08:00 记录一下系统的优化记录系统版本C13.1 (cn01)版本。机型1+ace2系统版本:冻结的应用:其中,发现我的system_server里面一直高负载,时不时跳到150%以上。于是采用adb抓包形式。抓包命令.\adb logcat -v time > D:\UserData\Desktop\logcat.log第一次日志抓取11-01 18:31:44.931 W/ActivityManager( 3189): Unable to start service Intent { act=com.oplus.onet.service.INTENT_SDK_BIND_SERVICE pkg=com.oplus.onet } U=0: not found 11-01 18:31:44.931 W/ContextImpl( 4912): Calling a method in the system process without a qualified user: android.app.ContextImpl.bindService:1973 android.content.ContextWrapper.bindService:863 com.oplus.onet.case$do.run:142 android.os.Handler.handleCallback:942 android.os.Handler.dispatchMessage:99 11-01 18:31:44.932 W/ActivityManager( 3189): Unable to start service Intent { act=com.oplus.onet.service.INTENT_SDK_BIND_SERVICE pkg=com.oplus.onet } U=0: not found 11-01 18:31:44.933 W/ContextImpl( 4912): Calling a method in the system process without a qualified user: android.app.ContextImpl.bindService:1973 android.content.ContextWrapper.bindService:863 com.oplus.onet.case$do.run:142 android.os.Handler.handleCallback:942 android.os.Handler.dispatchMessage:99 11-01 18:31:44.934 W/ActivityManager( 3189): Unable to start service Intent { act=com.oplus.onet.service.INTENT_SDK_BIND_SERVICE pkg=com.oplus.onet } U=0: not found 11-01 18:31:44.935 W/ContextImpl( 4912): Calling a method in the system process without a qualified user: android.app.ContextImpl.bindService:1973 android.content.ContextWrapper.bindService:863 com.oplus.onet.case$do.run:142 android.os.Handler.handleCallback:942 android.os.Handler.dispatchMessage:99 11-01 18:31:44.936 W/ActivityManager( 3189): Unable to start service Intent { act=com.oplus.onet.service.INTENT_SDK_BIND_SERVICE pkg=com.oplus.onet } U=0: not found 11-01 18:31:44.937 W/ContextImpl( 4912): Calling a method in the system process without a qualified user: android.app.ContextImpl.bindService:1973 android.content.ContextWrapper.bindService:863 com.oplus.onet.case$do.run:142 android.os.Handler.handleCallback:942 android.os.Handler.dispatchMessage:99 11-01 18:31:44.938 W/ActivityManager( 3189): Unable to start service Intent { act=com.oplus.onet.service.INTENT_SDK_BIND_SERVICE pkg=com.oplus.onet } U=0: not found 11-01 18:31:44.938 W/ContextImpl( 4912): Calling a method in the system process without a qualified user: android.app.ContextImpl.bindService:1973 android.content.ContextWrapper.bindService:863 com.oplus.onet.case$do.run:142 android.os.Handler.handleCallback:942 android.os.Handler.dispatchMessage:99 11-01 18:31:44.939 W/ActivityManager( 3189): Unable to start service Intent { act=com.oplus.onet.service.INTENT_SDK_BIND_SERVICE pkg=com.oplus.onet } U=0: not found 11-01 18:31:44.939 W/ContextImpl( 4912): Calling a method in the system process without a qualified user: android.app.ContextImpl.bindService:1973 android.content.ContextWrapper.bindService:863 com.oplus.onet.case$do.run:142 android.os.Handler.handleCallback:942 android.os.Handler.dispatchMessage:99 这里疯狂唤醒com.oplus.onet 服务,同时com.oplus.virtualcomm,这个虚拟通信也疯狂调用。于是只好解冻这net服务。第二次抓包11-01 18:48:53.927 E/AdbDebuggingManager( 3189): Caught an exception opening the socket: java.io.IOException: No such file or directory 11-01 18:48:53.956 W/ActivityManager( 3189): Unable to start service Intent { act=com.heytap.accessory.action.BASE_FRAMEWORK_MANAGER pkg=com.heytap.accessory } U=0: not found 11-01 18:48:53.958 W/ActivityManager( 3189): Unable to start service Intent { act=com.heytap.accessory.action.BASE_FRAMEWORK_MANAGER pkg=com.heytap.accessory } U=0: not found 11-01 18:48:53.960 W/ActivityManager( 3189): Unable to start service Intent { act=com.heytap.accessory.ScanService pkg=com.heytap.accessory (has extras) } U=0: not found 11-01 18:48:53.960 W/ActivityManager( 3189): Unable to start service Intent { act=com.heytap.accessory.action.BASE_FRAMEWORK_MANAGER pkg=com.heytap.accessory } U=0: not found 11-01 18:48:53.960 W/ActivityManager( 3189): Unable to start service Intent { act=com.heytap.accessory.ScanService pkg=com.heytap.accessory (has extras) } U=0: not found 11-01 18:48:53.960 W/ActivityManager( 3189): Unable to start service Intent { act=com.heytap.accessory.ScanService pkg=com.heytap.accessory (has extras) } U=0: not found 11-01 18:48:53.962 W/ActivityManager( 3189): Unable to start service Intent { act=com.heytap.accessory.ScanService pkg=com.heytap.accessory (has extras) } U=0: not found 11-01 18:48:53.962 W/ActivityManager( 3189): Unable to start service Intent { act=com.heytap.accessory.action.BASE_FRAMEWORK_MANAGER pkg=com.heytap.accessory } U=0: not found 11-01 18:48:53.963 W/ActivityManager( 3189): Unable to start service Intent { act=com.heytap.accessory.action.BASE_FRAMEWORK_MANAGER pkg=com.heytap.accessory } U=0: not found 11-01 18:48:53.963 W/ActivityManager( 3189): Unable to start service Intent { act=com.heytap.accessory.ScanService pkg=com.heytap.accessory (has extras) } U=0: not found 11-01 18:48:53.964 W/ActivityManager( 3189): Unable to start service Intent { act=com.heytap.accessory.ScanService pkg=com.heytap.accessory (has extras) } U=0: not found 11-01 18:48:53.964 W/ActivityManager( 3189): Unable to start service Intent { act=com.heytap.accessory.action.BASE_FRAMEWORK_MANAGER pkg=com.heytap.accessory } U=0: not found 11-01 18:48:53.965 W/ActivityManager( 3189): Receiver with filter android.content.IntentFilter@56b32fa already registered for pid 16357, callerPackage is com.oplus.onet 11-01 18:48:53.965 W/ActivityManager( 3189): Unable to start service Intent { act=com.heytap.accessory.action.BASE_FRAMEWORK_MANAGER pkg=com.heytap.accessory } U=0: not found 11-01 18:48:53.966 W/ActivityManager( 3189): Unable to start service Intent { act=com.heytap.accessory.ScanService pkg=com.heytap.accessory (has extras) } U=0: not found 11-01 18:48:53.966 W/ActivityManager( 3189): Unable to start service Intent { act=com.heytap.accessory.ScanService pkg=com.heytap.accessory (has extras) } U=0: not found这里疯狂唤醒com.heytap.accessory,于是把这个解冻了。第三次抓包发现一直在唤醒com.nearme.statistics.rom这个玩意,但是频次不是很高。搜索发现这是哪个用户体验计划,索性就不管了。同时在阅读了https://www.coolapk.com/feed/66547164这个大佬的文章之后。也是选择性的禁用软件。删除了olc和反馈工具箱这俩应用。其实从18年入手的第一台小米8开始,就开始了我的root之路。到现在已经七年了。也从小米换到了一加。从一个无脑安装模块的脚本小子,到了开始解读源码写程序的程序员。自己蜕变了好多,心态也老了好多。沉下心来做事,安安静静的学习!加油吧! typecho添加附件功能如果是图片就可以预览 https://blog.iletter.top/archives/492/ 2025-10-28T19:29:00+08:00 定位文件file-upload.php和file-upload-js.php。修改之前记得备份文件。更改了一下排序顺序,刚上传的放在最前面。如果是图片就可以简单预览缩略图。更改file-upload.php<?php if(!defined('__TYPECHO_ADMIN__')) exit; ?> <?php if (isset($post) || isset($page)) { $cid = isset($post) ? $post->cid : $page->cid; if ($cid) { \Widget\Contents\Attachment\Related::alloc(['parentId' => $cid])->to($attachment); } else { \Widget\Contents\Attachment\Unattached::alloc()->to($attachment); } } ?> <div id="upload-panel" class="p"> <div class="upload-area" draggable="true"><?php _e('拖放文件到这里<br>或者 %s选择文件上传%s', '<a href="###" class="upload-file">', '</a>'); ?></div> <ul id="file-list"> <?php while ($attachment->next()): ?> <li data-cid="<?php $attachment->cid(); ?>" data-url="<?php echo $attachment->attachment->url; ?>" data-image="<?php echo $attachment->attachment->isImage ? 1 : 0; ?>"> <input type="hidden" name="attachment[]" value="<?php $attachment->cid(); ?>" /> <a class="insert" title="<?php _e('点击插入文件'); ?>" href="###"><?php $attachment->title(); ?></a> <div class="info"> <?php echo number_format(ceil($attachment->attachment->size / 1024)); ?> Kb <a class="file" target="_blank" href="<?php $options->adminUrl('media.php?cid=' . $attachment->cid); ?>" title="<?php _e('编辑'); ?>"><i class="i-edit"></i>编辑</a> <a href="###" class="delete" title="<?php _e('删除'); ?>"><i class="i-delete"></i>删除</a> </div> <?php if ($attachment->attachment->isImage): ?> <div class="image-preview"> <img src="<?php echo $attachment->attachment->url; ?>" alt="<?php $attachment->title(); ?>" /> </div> <?php endif; ?> </li> <?php endwhile; ?> </ul> </div> <style> /* 为包含图片的列表项设置基础样式 */ #file-list li { margin-bottom: 10px; /* 列表项之间的间距 */ padding: 8px; /* 内边距 */ border: 1px solid #eee; /* 边框 */ border-radius: 4px; /* 圆角 */ background-color: #fafafa; /* 背景色 */ list-style: none; /* 去除默认列表符号 */ } /* 标题样式 */ #file-list li .insert { display: block; /* 使其独占一行 */ font-weight: bold; /* 标题加粗 */ margin-bottom: 4px; /* 与下方 .info 的间距 */ color: #333; /* 标题颜色 */ text-decoration: none; /* 去除下划线 */ } #file-list li .insert:hover { text-decoration: underline; /* 悬停时添加下划线 */ } /* 信息栏样式 */ #file-list li .info { font-size: 0.9em; /* 信息栏字体稍小 */ color: #666; /* 信息栏颜色 */ margin-bottom: 8px; /* 与下方图片的间距 */ line-height: 1.4; /* 行高 */ } /* 信息栏内的链接和图标 */ #file-list li .info a { margin-right: 8px; /* 链接之间的间距 */ color: #999; /* 图标颜色 */ text-decoration: none; } #file-list li .info a:hover { color: #007cba; /* 悬停时的颜色 */ } /* 图片预览容器 */ .image-preview { text-align: center; /* 图片居中 */ margin-top: 5px; /* 与上方 .info 的间距 */ clear: both; /* 清除浮动(如果有的话) */ } /* 图片样式 */ .image-preview img { max-width: 150px; /* 设置最大宽度 */ max-height: 150px; /* 设置最大高度 */ height: auto; /* 保持宽高比 */ border: 1px solid #ddd; /* 图片边框 */ border-radius: 4px; /* 图片圆角 */ padding: 2px; /* 图片内边距 */ background-color: #fff; /* 图片背景色,防止透明图有背景色干扰 */ box-shadow: 0 1px 3px rgba(0,0,0,0.1); /* 添加轻微阴影 */ } /* 加载状态样式 */ #file-list li.loading { color: #999; font-style: italic; } /* 删除按钮悬停效果 */ #file-list li a.delete:hover { color: #e74c3c !important; /* 删除按钮悬停时变为红色 */ } /* 编辑按钮悬停效果 */ #file-list li a.file:hover { color: #3498db !important; /* 编辑按钮悬停时变为蓝色 */ } </style>更改file-upload-js.php<?php if(!defined('__TYPECHO_ADMIN__')) exit; ?> <?php if (isset($post) && $post instanceof \Typecho\Widget && $post->have()) { $fileParentContent = $post; } elseif (isset($page) && $page instanceof \Typecho\Widget && $page->have()) { $fileParentContent = $page; } $phpMaxFilesize = function_exists('ini_get') ? trim(ini_get('upload_max_filesize')) : 0; if (preg_match("/^([0-9]+)([a-z]{1,2})$/i", $phpMaxFilesize, $matches)) { $phpMaxFilesize = strtolower($matches[1] . $matches[2] . (1 == strlen($matches[2]) ? 'b' : '')); } ?> <script src="<?php $options->adminStaticUrl('js', 'moxie.js'); ?>"></script> <script src="<?php $options->adminStaticUrl('js', 'plupload.js'); ?>"></script> <script> $(document).ready(function() { function updateAttacmentNumber () { var btn = $('#tab-files-btn'), balloon = $('.balloon', btn), count = $('#file-list li .insert').length; if (count > 0) { if (!balloon.length) { btn.html($.trim(btn.html()) + ' '); balloon = $('<span class="balloon"></span>').appendTo(btn); } balloon.html(count); } else if (0 == count && balloon.length > 0) { balloon.remove(); } } $('.upload-area').bind({ dragenter : function () { $(this).parent().addClass('drag'); }, dragover : function (e) { $(this).parent().addClass('drag'); }, drop : function () { $(this).parent().removeClass('drag'); }, dragend : function () { $(this).parent().removeClass('drag'); }, dragleave : function () { $(this).parent().removeClass('drag'); } }); updateAttacmentNumber(); function fileUploadStart (file) { $('<li id="' + file.id + '" class="loading">' + file.name + '</li>').appendTo('#file-list'); } function fileUploadError (error) { var file = error.file, code = error.code, word; switch (code) { case plupload.FILE_SIZE_ERROR: word = '<?php _e('文件大小超过限制'); ?>'; break; case plupload.FILE_EXTENSION_ERROR: word = '<?php _e('文件扩展名不被支持'); ?>'; break; case plupload.FILE_DUPLICATE_ERROR: word = '<?php _e('文件已经上传过'); ?>'; break; case plupload.HTTP_ERROR: default: word = '<?php _e('上传出现错误'); ?>'; break; } var fileError = '<?php _e('%s 上传失败'); ?>'.replace('%s', file.name), li, exist = $('#' + file.id); if (exist.length > 0) { li = exist.removeClass('loading').html(fileError); } else { li = $('<li>' + fileError + '<br />' + word + '</li>').appendTo('#file-list'); } li.effect('highlight', {color : '#FBC2C4'}, 2000, function () { $(this).remove(); }); // fix issue #341 this.removeFile(file); } var completeFile = null; function fileUploadComplete (id, url, data) { // 根据是否为图片来构建列表项内容 var itemContent = '<input type="hidden" name="attachment[]" value="' + data.cid + '" />'; // 添加标题和信息 itemContent += '<a class="insert" target="_blank" href="###" title="<?php _e('点击插入文件'); ?>">' + data.title + '</a>' + '<div class="info">' + data.bytes + ' <a class="file" target="_blank" href="<?php $options->adminUrl('media.php'); ?>?cid=' + data.cid + '" title="<?php _e('编辑'); ?>"><i class="i-edit"></i>编辑</a>' + ' <a class="delete" href="###" title="<?php _e('删除'); ?>"><i class="i-delete"></i>删除</a></div>'; // 如果是图片,添加预览图 (注意:这里图片放在 .info 之后,与PHP模板保持一致) if (data.isImage) { itemContent += '<div class="image-preview"><img src="' + data.url + '" alt="' + data.title + '" /></div>'; } // 创建 jQuery 对象 li var li = $('#' + id).removeClass('loading').data('cid', data.cid) .data('url', data.url) .data('image', data.isImage) .html(itemContent); // 先设置内容 // 关键修改:将新 li 插入到 #file-list 的最前面,而不是留在原地或追加到末尾 // 1. 先从当前位置移除(如果它在列表中的话,虽然通常在上传开始时是添加到列表末尾的空li) // 2. 然后插入到 #file-list 的开头 li.prependTo('#file-list'); // prependTo 将元素插入到目标元素的开头 // 绑定事件 attachInsertEvent(li); attachDeleteEvent(li); updateAttacmentNumber(); if (!completeFile) { completeFile = data; } } var uploader = null, tabFilesEl = $('#tab-files').bind('init', function () { uploader = new plupload.Uploader({ browse_button : $('.upload-file').get(0), url : '<?php $security->index('/action/upload' . (isset($fileParentContent) ? '?cid=' . $fileParentContent->cid : '')); ?>', runtimes : 'html5,flash,html4', flash_swf_url : '<?php $options->adminStaticUrl('js', 'Moxie.swf'); ?>', drop_element : $('.upload-area').get(0), filters : { max_file_size : '<?php echo $phpMaxFilesize ?>', mime_types : [{'title' : '<?php _e('允许上传的文件'); ?>', 'extensions' : '<?php echo implode(',', $options->allowedAttachmentTypes); ?>'}], prevent_duplicates : true }, init : { FilesAdded : function (up, files) { for (var i = 0; i < files.length; i ++) { fileUploadStart(files[i]); } completeFile = null; uploader.start(); }, UploadComplete : function () { if (completeFile) { Typecho.uploadComplete(completeFile); } }, FileUploaded : function (up, file, result) { if (200 == result.status) { var data = $.parseJSON(result.response); if (data) { fileUploadComplete(file.id, data[0], data[1]); uploader.removeFile(file); return; } } fileUploadError.call(uploader, { code : plupload.HTTP_ERROR, file : file }); }, Error : function (up, error) { fileUploadError.call(uploader, error); } } }); uploader.init(); }); Typecho.uploadFile = function (file, name) { if (!uploader) { $('#tab-files-btn').parent().trigger('click'); } var timer = setInterval(function () { if (!uploader) { return; } clearInterval(timer); timer = null; uploader.addFile(file, name); }, 50); }; // function attachInsertEvent (el) { // $('.insert', el).click(function () { // var t = $(this), p = t.parents('li'); // Typecho.insertFileToEditor(t.text(), p.data('url'), p.data('image')); // return false; // }); // } // 修改 attachInsertEvent 函数,使其能处理标题链接和图片 function attachInsertEvent (el) { // 为标题链接和图片(或其父容器 .image-preview)绑定点击事件 $('.insert, .image-preview img', el).click(function (e) { // 防止事件冒泡到父级 <a> 标签(如果图片被另一个链接包裹的话) e.stopPropagation(); var t = $(this); // 查找当前点击元素的父级 <li>,然后从中获取数据 var p = t.closest('li'); // 使用 closest 更可靠,可以找到最近的祖先 <li> // 确保找到了包含数据的 <li> 元素 if (p.length > 0) { // 从 <li> 元素获取数据 var url = p.data('url'); var isImage = p.data('image'); var title = p.find('.insert').first().text(); // 获取标题文本 // 调用 Typecho 提供的插入函数 Typecho.insertFileToEditor(title, url, isImage); } return false; // 阻止默认链接行为 }); } function attachDeleteEvent (el) { var file = $('a.insert', el).text(); $('.delete', el).click(function () { if (confirm('<?php _e('确认要删除文件 %s 吗?'); ?>'.replace('%s', file))) { var cid = $(this).parents('li').data('cid'); $.post('<?php $security->index('/action/contents-attachment-edit'); ?>', {'do' : 'delete', 'cid' : cid}, function () { $(el).fadeOut(function () { $(this).remove(); updateAttacmentNumber(); }); }); } return false; }); } $('#file-list li').each(function () { attachInsertEvent(this); attachDeleteEvent(this); }); }); </script> wireguard组网搭建windows远程桌面可用但是无法ssh https://blog.iletter.top/archives/471/ 2025-10-22T15:33:00+08:00 因为wireguard搭建的组网是public的。所以但是open ssh允许的是私有网络的。所以在此需要改一下网络类型。Set-NetConnectionProfile -InterfaceAlias "winServer2025" -NetworkCategory PrivatewinServer2025为我的配置文件名称,你可以根据你的去变更变更完毕之后通过 Get-NetConnectionProfile 进行查看。此时在进行ssh链接即可成功。其实还有一种方式就是直接关闭防火墙简单暴力。。。 开机显示windows无法完成安装,解决方法 https://blog.iletter.top/archives/470/ 2025-10-21T21:41:00+08:00 1.等待报错出现2.按住shift+F10 进入cmd界面3.输入regedit编辑注册表4.找到路径 计算机\HKEY_LOCAL_MACHINE\SYSTEM\Setup5.编辑内部数据OOBEInProgress 设置为 0CmdLine 设置为空,即删除里面的数据SetupPhase 设置为 0SetupType 设置为 0SystemSetupInProgress 设置为 06.再次打开 cmd 窗口 输入以下命令行net user administrator /active:yes然后重启系统即可正常进入系统。 构建AI个人知识库的一些思考 https://blog.iletter.top/archives/468/ 2025-09-25T21:51:00+08:00 最近在使用obsidian+Copilot虽然使用确实不错,但是总感觉差点味道。今天试了试python搞得rag向量数据库+ai模型,跑起来还是不错,但感觉和obsidian+Copilot大差不差。但至于差在哪里,却一眼难尽。单篇文章分析,都是可以的。多文章分析就报废了。所以有个思路针对md文档必须有严格的限制,在此使用markdown文章的yaml标签案例。例如--- 标题: git拆分分支和合并分支 日期: 2025-09-24 作者: Del Levin 分类: - 技术笔记 - 其他 tags: - 技术笔记 - git - 运维 - windows --- 构建数据库->检索 结构化检索方式:指定统计方式,分析方式非结构化检索方式:ai分析检索方式:分类词:{classification[分类词1,分类词2]}标签 :{tags[标签1,标签2]}指定文章:{title[文章名称1,文章名称2]}提问方式为:1.根据{classification[分类词1,分类词2]}分类总结一下我这个人2.根据{tags[标签1,标签2]}标签总结一下我的技术栈3.{title[文章名称1,文章名称2]}总结一下这两篇文章4.{title[文章名称1]}总结一下这一篇文章理想很美好,现实很骨感,知识库的容量参考文件不支持那么多。要想真正实现,只能拿自己的笔记进行模型训练。可是模型数据基数不足,成为一个问题。我也不知道该怎么做了。。。。 利用wireduard实现windows远控macos https://blog.iletter.top/archives/466/ 2025-09-17T23:13:00+08:00 wireduard之前搭建过。在此不做赘述。图简单的话,可以拉docker的wg-easy进行运行。搭建好之后,服务器给windows生成配置。[Interface] PrivateKey = 私钥 Address = 10.8.0.3/24 [Peer] PublicKey = 公钥 PresharedKey = Xtz5vzzicOncQe3Dt8LMFIbdxa/XUepY9hAllrJ3k30= AllowedIPs = 10.8.0.0/24 Endpoint = 152.136.153.72:51820然后开始macos的操作,首先,你需要有一个Homebrew来用于安装软件。macos安装wireguard步骤1. 安装 WireGuard使用 Homebrew 安装 WireGuard 工具包:brew install wireguard-tools3. 配置 WireGuard使用下面的命令创建配置文件:sudo vim /opt/homebrew/etc/wireguard/wg0.conf编辑配置文件,添加以下内容:[Interface] PrivateKey = 私钥 Address = 10.8.0.7/24 MTU = 1540 [Peer] PublicKey = 公钥 PresharedKey = AllowedIPs = 10.8.0.0/24 Endpoint = 152.136.153.72:51820 PersistentKeepalive = 25一切配置好之后,安装图形化界面WireGuardStatusbar去https://github.com/aequitas/macos-menubar-wireguard这个链接下载文件安装。安装完毕之后,启动wg0这个就可以了。不想安装的话可以使用这个命令去开启sudo wg-quick up wg0关闭sudo wg-quick down wg0 让typecho的access插件使用ip2region 地址库 https://blog.iletter.top/archives/459/ 2025-08-28T22:42:00+08:00 旧版本的typecho的access插件记录的ip太过于落后了。判断也有很多问题,所以需要在此更新一下。定位到地址文件是lib下面的ipipfree.ipdb,删掉这个就好,用不到了。这个最近的时间是2019年的。真的很无语的。接下来定位到所在位置是Access_Core.php这个文件的这里代码。(Access_IpDb.php这个也可以删除) try { $ipdb = new Access_IpDb(dirname(__file__).'/lib/ipipfree.ipdb'); $city = $ipdb->findInfo($ip, 'CN'); // 写入日志 error_log("IP: {$ip}\nCity Info: " . print_r($city, true), 3, '/tmp/access_debug.log'); $ip_country = $city->country_name; if($ip_country == '中国') { $ip_province = $city->region_name; $ip_city = $city->city_name; } else { $ip_province = $ip_city = NULL; } } catch(Exception $e) { $ip_country = $ip_province = $ip_city = '未知'; } 然后根据数据格式,改成他的数据样式的。try { // 检查必要的文件是否存在 $dbFile = dirname(__FILE__) . '/lib/ip2region.xdb'; $classFile = dirname(__FILE__) . '/lib/XdbSearcher.php'; if (!file_exists($dbFile)) { throw new Exception("Ip2region database file not found: {$dbFile}"); } if (!file_exists($classFile)) { throw new Exception("XdbSearcher class file not found: {$classFile}"); } require_once $classFile; $searcher = XdbSearcher::newWithFileOnly($dbFile); $region = $searcher->search($ip); if ($region === null) { throw new Exception("IP2Region search failed for IP: {$ip}"); } // 调试日志 error_log("IP: {$ip}\nRegion Info: " . print_r($region, true), 3, '/tmp/access_debug.log'); // 解析数据 (格式: 国家|区域|省份|城市|ISP) $regionArray = explode('|', $region); // 清理数据 $cleanData = function($data) { return (!empty($data) && $data !== '0') ? $data : ''; }; $country = $cleanData($regionArray[0]); $region_info = $cleanData($regionArray[1]); $province = $cleanData($regionArray[2]); $city = $cleanData($regionArray[3]); // 设置最终保存到数据库的字段 $ip_country = !empty($country) ? $country : '未知'; // 无论国内外都保存区域和城市信息 $ip_province = ''; $ip_city = ''; if (!empty($province)) { $ip_province = $province; } elseif (!empty($region_info)) { // 如果省份为空,但区域不为空,可以用区域代替 $ip_province = $region_info; } if (!empty($city)) { $ip_city = $city; } } catch(Exception $e) { error_log("IP解析异常:" . $e->getMessage(), 3, '/tmp/access_debug.log'); $ip_country = '未知'; $ip_province = ''; $ip_city = ''; }这样就可以了。 https嵌套http(修改安全策略) https://blog.iletter.top/archives/455/ 2025-08-26T12:19:00+08:00 因为需要用到https嵌套http,但是这怎么可能啊?还好那个http有https的协议,所以我们只需要再nginx上操作升级CSP就行了。也就是内容安全策略。HTTPS页面 (aaa.com) └── HTTP iframe (bbb.com) └── HTTP frame (ccc.com)大概是这种形式的,所以在第一个a网站是我自己写的,所以我就选择了直接使用http替换成https的了然后配置b网站server { listen 80; server_name www.bbb.com; # 强制跳转到 HTTPS return 301 https://$host$request_uri; } # 处理www.bbb.com 的 HTTPS 请求 server { listen 443 ssl; server_name www.bbb.com; # SSL 证书配置(你需要配置自己的证书) ssl_certificate /path/to/your/certificate.crt; ssl_certificate_key /path/to/your/private.key; # 添加安全头 add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; add_header Content-Security-Policy "upgrade-insecure-requests"; # 代理到后端应用 location / { proxy_pass http://localhost:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } # 特别处理企业查看接口,确保内容中的HTTP链接被替换 location /rotech-xyjq-api/api/enterprise/view { proxy_pass http://localhost:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; # 替换内容中的HTTP链接为HTTPS sub_filter 'http://b-plugin.qixin.com/' 'https://b-plugin.qixin.com/'; sub_filter 'http://b-plugin.qixin.com' 'https://b-plugin.qixin.com'; sub_filter_once off; } }这样就可以再a网站加载c网站的了,可以无限套娃了,前提是每个娃娃都有https这个。 typecho文章编辑界面新增参考文章功能 https://blog.iletter.top/archives/450/ 2025-08-25T22:47:00+08:00 因为再写技术博客的时候总是要很多的参考文章。直接在文章引用感觉怪怪的。所以自己更改了一些数据结构,接口。效果为这样的:增加数据库字段typecho_contents这个表增加一个字段post_links,类型为text修改接口文件路径在var/Widget/Contents/Post下面的Edit.php文件修改编辑文章文件文件地址在admin路径下的write-post.php文件,新增部分。css暂时不写了 <!-- 引用文章开始 --> <section class="typecho-post-option reference-links-section" id="reference-links-section"> <label class="typecho-label toggle-reference-section" style="cursor: pointer;"> <span class="toggle-icon" style="float: left;"><i class="i-caret-right"></i></span> <?php _e('参考文章'); ?> </label> <div class="reference-links-container" id="post-links-container"> <?php // 从数据库获取数据 $links = []; if ($post->have()) { $db = Typecho\Db::get(); $result = $db->fetchRow($db->select('post_links')->from('table.contents')->where('cid = ?', $post->cid)); if (!empty($result) && !empty($result['post_links'])) { $postLinks = $result['post_links']; $links = json_decode($postLinks, true); if (!is_array($links)) $links = []; } } // 如果没有数据,添加一个空行 if (empty($links)) { $links = [['name' => '', 'link' => '']]; } foreach ($links as $index => $link): ?> <div class="reference-link-item"> <div class="reference-link-inputs"> <input type="text" name="post_links[<?php echo $index; ?>][name]" placeholder="<?php _e('链接名称'); ?>" value="<?php echo htmlspecialchars($link['name'] ?? ''); ?>" class="text reference-link-name" /> <input type="url" name="post_links[<?php echo $index; ?>][link]" placeholder="<?php _e('https://'); ?>" value="<?php echo htmlspecialchars($link['link'] ?? ''); ?>" class="text reference-link-url" /> </div> <div class="reference-link-actions"> <button type="button" class="btn btn-xs remove-reference-link" title="<?php _e('删除'); ?>"> <i class="i-delete"></i> </button> </div> </div> <?php endforeach; ?> </div> <div class="reference-links-footer"> <button type="button" id="add-reference-link" class="btn btn-xs"> <i class="i-plus"></i> <?php _e('添加引用'); ?> </button> <span class="reference-links-help"><?php _e('温故而知新,添加相关的参考链接,记忆更牢固。'); ?></span> </div> </section> <!-- 引用文章结束 -->受限于代码长度过长,使得文章无法正常解析,可以点击链接下载。点击下载:代码.zip typecho美化文件管理界面 https://blog.iletter.top/archives/447/ 2025-08-25T15:49:00+08:00 后台界面太丑陋, 我就自己在这里改了改。添加文件预览以及预览图片放大。添加接口因为后台接口少了统计接口,所以这里需要手动后台添加。目录在安装目录下面的/var/Widget/Contents/Attachment这个目录, 然后编辑Admin.php。复制以下代码即可。<?php namespace Widget\Contents\Attachment; use Typecho\Config; use Typecho\Db; use Typecho\Db\Exception; use Typecho\Db\Query; use Typecho\Widget\Helper\PageNavigator\Box; use Widget\Base\Contents; use Typecho\Common; if (!defined('__TYPECHO_ROOT_DIR__')) { exit; } /** * 文件管理列表组件 * * @category typecho * @package Widget * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) * @license GNU General Public License 2.0 */ class Admin extends Contents { /** * 用于计算数值的语句对象 * * @var Query */ private $countSql; /** * 所有文章个数 * * @var integer */ private $total = false; /** * 当前页 * * @var integer */ private $currentPage; /** * 执行函数 * * @return void * @throws Exception|\Typecho\Widget\Exception */ public function execute() { $this->parameter->setDefault('pageSize=20'); $this->currentPage = $this->request->get('page', 1); /** 构建基础查询 */ $select = $this->select()->where('table.contents.type = ?', 'attachment'); /** 如果具有编辑以上权限,可以查看所有文件,反之只能查看自己的文件 */ if (!$this->user->pass('editor', true)) { $select->where('table.contents.authorId = ?', $this->user->uid); } /** 过滤标题 */ if (null != ($keywords = $this->request->filter('search')->keywords)) { $args = []; $keywordsList = explode(' ', $keywords); $args[] = implode(' OR ', array_fill(0, count($keywordsList), 'table.contents.title LIKE ?')); foreach ($keywordsList as $keyword) { $args[] = '%' . $keyword . '%'; } call_user_func_array([$select, 'where'], $args); } /** 给计算数目对象赋值,克隆对象 */ $this->countSql = clone $select; /** 提交查询 */ $select->order('table.contents.created', Db::SORT_DESC) ->page($this->currentPage, $this->parameter->pageSize); $this->db->fetchAll($select, [$this, 'push']); } /** * 输出分页 * * @return void * @throws Exception|\Typecho\Widget\Exception */ public function pageNav() { $query = $this->request->makeUriByRequest('page={page}'); /** 使用盒状分页 */ $nav = new Box( false === $this->total ? $this->total = $this->size($this->countSql) : $this->total, $this->currentPage, $this->parameter->pageSize, $query ); $nav->render('&laquo;', '&raquo;'); } /** * 所属文章 * * @return Config * @throws Exception */ protected function ___parentPost(): Config { return new Config($this->db->fetchRow( $this->select()->where('table.contents.cid = ?', $this->parentId)->limit(1) )); } /** * 获取附件的 URL 地址 * * @return string */ protected function ___attachmentUrl(): string { if (!empty($this->attachment) && !empty($this->attachment->path)) { // 从 path 字段构建 URL $path = $this->attachment->path; // 移除开头的斜杠(如果有的话) $path = ltrim($path, '/'); return $this->options->siteUrl . $path; } // 如果 path 不存在,尝试从 text 字段解析 if (!empty($this->___attachment())) { $attachment = $this->___attachment(); if (!empty($attachment['path'])) { $path = ltrim($attachment['path'], '/'); return $this->options->siteUrl . $path; } } return ''; } /** * 解析附件信息 * * @return array */ protected function ___attachment(): array { if (!empty($this->text)) { $attachment = @unserialize($this->text); if (is_array($attachment)) { return $attachment; } } return []; } /** * 重写 permalink 方法,使用解析后的 URL * * @return string */ protected function ___permalink(): string { return $this->___attachmentUrl(); } /** * 获取统计信息 * * @return array * @throws Exception */ public function getStats(): array { $select = $this->select()->where('table.contents.type = ?', 'attachment'); /** 如果具有编辑以上权限,可以查看所有文件,反之只能查看自己的文件 */ if (!$this->user->pass('editor', true)) { $select->where('table.contents.authorId = ?', $this->user->uid); } // 总文件数 $total = $this->size(clone $select); // 图片文件数 $imageSelect = clone $select; $imageSelect->join('table.fields', 'table.contents.cid = table.fields.cid', Db::LEFT_JOIN) ->where('table.fields.name = ?', 'mime') ->where('table.fields.str_value LIKE ?', 'image/%'); $imageCount = $this->size($imageSelect); // 未归档文件数 $unattachedSelect = clone $select; $unattachedSelect->where('table.contents.parent = ?', 0); $unattachedCount = $this->size($unattachedSelect); return [ 'total' => $total, 'images' => $imageCount, 'unattached' => $unattachedCount ]; } /** * 获取总文件数 * * @return int * @throws Exception */ public function getTotalCount(): int { $select = $this->select()->where('table.contents.type = ?', 'attachment'); /** 如果具有编辑以上权限,可以查看所有文件,反之只能查看自己的文件 */ if (!$this->user->pass('editor', true)) { $select->where('table.contents.authorId = ?', $this->user->uid); } return $this->size($select); } /** * 获取图片文件数 * * @return int * @throws Exception */ public function getImageCount(): int { $select = $this->select()->where('table.contents.type = ?', 'attachment'); /** 如果具有编辑以上权限,可以查看所有文件,反之只能查看自己的文件 */ if (!$this->user->pass('editor', true)) { $select->where('table.contents.authorId = ?', $this->user->uid); } // 通过 MIME 类型过滤图片 $select->where('table.contents.text LIKE ?', '%image/%'); return $this->size($select); } /** * 获取未归档文件数 * * @return int * @throws Exception */ public function getUnattachedCount(): int { $select = $this->select()->where('table.contents.type = ?', 'attachment'); /** 如果具有编辑以上权限,可以查看所有文件,反之只能查看自己的文件 */ if (!$this->user->pass('editor', true)) { $select->where('table.contents.authorId = ?', $this->user->uid); } // 未归档文件(parent = 0) $select->where('table.contents.parent = ?', 0); return $this->size($select); } }更改后台文件界面通过查看后台路径可以得知是manage-medias.php这个文件,所以更改一下这个文件就可以了。更改前注意文件备份。<?php include 'common.php'; include 'header.php'; include 'menu.php'; $stat = \Widget\Stat::alloc(); $attachments = \Widget\Contents\Attachment\Admin::alloc(); // 获取统计信息 $totalCount = $attachments->getTotalCount(); $imageCount = $attachments->getImageCount(); $unattachedCount = $attachments->getUnattachedCount(); // 获取PHP上传限制 $phpMaxFilesize = function_exists('ini_get') ? trim(ini_get('upload_max_filesize')) : 0; if (preg_match("/^([0-9]+)([a-z]{1,2})$/i", $phpMaxFilesize, $matches)) { $phpMaxFilesize = strtolower($matches[1] . $matches[2] . (1 == strlen($matches[2]) ? 'b' : '')); } ?> <style> /* 上传区域样式 */ .upload-section { background: #fff; border: 1px solid #d9d9d9; border-radius: 4px; padding: 20px; margin: 20px 0; display: none; } .upload-section.show { display: block; } .upload-section h3 { margin: 0 0 15px 0; padding: 0; font-size: 16px; color: #333; } .upload-controls { display: flex; gap: 15px; align-items: center; flex-wrap: wrap; margin-bottom: 15px; } /* 改为链接样式 */ .upload-link { color: #007cba; text-decoration: underline; cursor: pointer; font-size: 14px; display: inline-flex; align-items: center; gap: 4px; transition: color 0.2s; } .upload-link:hover { color: #005a87; text-decoration: none; } .upload-link i { font-size: 16px; } .upload-hint { color: #666; font-size: 13px; } .upload-area { border: 2px dashed #ddd; border-radius: 6px; padding: 30px; text-align: center; background: #fafafa; transition: all 0.3s; margin-top: 10px; } .upload-area.dragover { border-color: #007cba; background: #f0f8ff; } .upload-area-text { color: #666; margin-bottom: 10px; } .browse-link { color: #007cba; text-decoration: underline; cursor: pointer; } .browse-link:hover { color: #005a87; } /* 上传进度条 */ .upload-progress { display: none; margin-top: 15px; } .progress-bar { width: 100%; height: 20px; background: #f0f0f0; border-radius: 10px; overflow: hidden; } .progress-fill { height: 100%; background: #007cba; width: 0%; transition: width 0.3s; } .progress-text { text-align: center; font-size: 12px; color: #666; margin-top: 5px; } .media-stats { background: #fff; border: 1px solid #d9d9d9; border-radius: 4px; padding: 15px; margin-bottom: 20px; } .media-stats h3 { margin: 0 0 15px 0; padding: 0; font-size: 16px; color: #666; } .stats-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 15px; } .stat-item { text-align: center; padding: 10px; background: #f8f9fa; border-radius: 4px; } .stat-number { font-size: 24px; font-weight: bold; color: #007cba; margin-bottom: 5px; } .stat-label { font-size: 13px; color: #666; } /* 预览列样式 */ .preview-cell { text-align: center; } .media-preview-img { width: 50px; height: 50px; object-fit: cover; border-radius: 4px; border: 1px solid #ddd; cursor: pointer; transition: transform 0.2s ease; } .media-preview-img:hover { transform: scale(1.1); box-shadow: 0 2px 8px rgba(0,0,0,0.2); } .media-preview-icon { font-size: 24px; color: #999; } /* 保持原有样式 */ .typecho-list-operate { background: #fff; border: 1px solid #d9d9d9; border-radius: 4px; padding: 15px; margin-bottom: 20px; } .typecho-pager { margin-top: 20px; text-align: center; } /* 图片预览模态框样式 */ .modal { display: none; position: fixed; z-index: 10000; left: 0; top: 0; width: 100%; height: 100%; background-color: rgba(0,0,0,0.8); backdrop-filter: blur(5px); } .modal-content { position: relative; margin: 5% auto; padding: 20px; width: 90%; max-width: 900px; max-height: 90vh; text-align: center; } .modal-image { max-width: 100%; max-height: 80vh; object-fit: contain; border-radius: 8px; box-shadow: 0 4px 20px rgba(0,0,0,0.3); } .close { position: absolute; top: 10px; right: 25px; color: #fff; font-size: 35px; font-weight: bold; cursor: pointer; z-index: 10001; } .close:hover { color: #ccc; } .modal-title { color: white; margin-top: 10px; font-size: 16px; font-weight: normal; } /* 上传按钮样式 - 与清理按钮统一 */ .btn-upload { background: #28a745; border-color: #28a745; color: white; margin-left: 10px; } .btn-upload:hover { background: #218838; border-color: #1e7e34; } /* 取消按钮样式 */ #cancelUploadBtn { margin-left: 10px; } /* 隐藏的文件输入框 */ #fileInput { display: none; } /* 响应式设计 */ @media (max-width: 768px) { .stats-grid { grid-template-columns: repeat(2, 1fr); } .modal-content { width: 95%; margin: 10% auto; } .modal-image { max-height: 70vh; } .upload-controls { flex-direction: column; align-items: stretch; } .btn-upload { margin-left: 0; margin-top: 5px; } } </style> <div class="main"> <div class="body container"> <?php include 'page-title.php'; ?> <div class="row typecho-page-main" role="main"> <div class="col-mb-12"> <!-- 统计信息 --> <div class="media-stats"> <h3>媒体文件统计</h3> <div class="stats-grid"> <div class="stat-item"> <div class="stat-number"><?php echo $totalCount; ?></div> <div class="stat-label">总文件数</div> </div> <div class="stat-item"> <div class="stat-number"><?php echo $imageCount; ?></div> <div class="stat-label">图片文件</div> </div> <div class="stat-item"> <div class="stat-number"><?php echo $stat->publishedPostsNum; ?></div> <div class="stat-label">文章数</div> </div> <div class="stat-item"> <div class="stat-number"><?php echo $unattachedCount; ?></div> <div class="stat-label">未归档文件</div> </div> </div> </div> <div class="typecho-list-operate clearfix"> <form method="get"> <div class="operate"> <label><i class="sr-only"><?php _e('全选'); ?></i><input type="checkbox" class="typecho-table-select-all"/></label> <div class="btn-group btn-drop"> <button class="btn dropdown-toggle btn-s" type="button"><i class="sr-only"><?php _e('操作'); ?></i><?php _e('选中项'); ?> <i class="i-caret-down"></i></button> <ul class="dropdown-menu"> <li><a lang="<?php _e('你确认要删除这些文件吗?'); ?>" href="<?php $security->index('/action/contents-attachment-edit?do=delete'); ?>"><?php _e('删除'); ?></a> </li> </ul> <button class="btn btn-s btn-warn btn-operate" href="<?php $security->index('/action/contents-attachment-edit?do=clear'); ?>" lang="<?php _e('您确认要清理未归档的文件吗?'); ?>"><?php _e('清理未归档文件'); ?></button> <!--<button type="button" class="btn btn-s btn-upload" id="toggleUploadBtn">--> <!-- <?php _e('上传文件'); ?>--> <!--</button>--> <!-- 修改这里:添加条件显示 --> <button type="button" class="btn btn-s btn-upload" id="toggleUploadBtn"> <?php _e('上传文件'); ?> </button> <button type="button" class="btn btn-s" id="cancelUploadBtn" style="display: none;"> <?php _e('取消上传'); ?> </button> </div> </div> <div class="search" role="search"> <?php if ('' != $request->keywords): ?> <a href="<?php $options->adminUrl('manage-medias.php'); ?>"><?php _e('&laquo; 取消筛选'); ?></a> <?php endif; ?> <input type="text" class="text-s" placeholder="<?php _e('请输入关键字'); ?>" value="<?php echo $request->filter('html')->keywords; ?>"<?php if ('' == $request->keywords): ?> onclick="value='';name='keywords';" <?php else: ?> name="keywords"<?php endif; ?>/> <button type="submit" class="btn btn-s"><?php _e('筛选'); ?></button> </div> </form> </div><!-- end .typecho-list-operate --> <!-- 上传区域 - 默认隐藏 --> <div class="upload-section" id="uploadSection"> <h3><?php _e('上传文件'); ?></h3> <div class="upload-controls"> <!-- 改为链接形式 --> <a href="javascript:;" class="upload-link" id="uploadButton"> <!--<i class="i-upload"></i> --> <?php _e('选择文件'); ?> </a> <span class="upload-hint"><?php _e('或拖放文件到下方区域'); ?></span> </div> <div class="upload-area" id="uploadArea"> <div class="upload-area-text"> <?php _e('拖放文件到这里'); ?> </div> <div> <?php _e('或者 %s浏览文件%s', '<span class="browse-link" id="browseLink">', '</span>'); ?> </div> </div> <!-- 上传进度条 --> <div class="upload-progress" id="uploadProgress"> <div class="progress-bar"> <div class="progress-fill" id="progressFill"></div> </div> <div class="progress-text" id="progressText">0%</div> </div> <!-- 隐藏的文件输入框 --> <input type="file" id="fileInput" multiple style="display: none;"> </div> <form method="post" name="manage_medias" class="operate-form"> <div class="typecho-table-wrap"> <table class="typecho-list-table draggable"> <colgroup> <col width="20" class="kit-hidden-mb"/> <col width="6%" class="kit-hidden-mb"/> <col width="10%" class="kit-hidden-mb"/> <!-- 预览列 --> <col width="25%"/> <col width="" class="kit-hidden-mb"/> <col width="25%" class="kit-hidden-mb"/> <col width="16%"/> </colgroup> <thead> <tr> <th class="kit-hidden-mb"></th> <th class="kit-hidden-mb"></th> <th class="kit-hidden-mb"><?php _e('预览'); ?></th> <th><?php _e('文件名'); ?></th> <th class="kit-hidden-mb"><?php _e('上传者'); ?></th> <th class="kit-hidden-mb"><?php _e('所属文章'); ?></th> <th><?php _e('发布日期'); ?></th> </tr> </thead> <tbody> <?php if ($attachments->have()): ?> <?php while ($attachments->next()): ?> <?php $mime = \Typecho\Common::mimeIconType($attachments->attachment->mime); $isImage = strpos($attachments->attachment->mime, 'image/') === 0; $fileUrl = $attachments->attachmentUrl; ?> <tr id="<?php $attachments->theId(); ?>"> <td class="kit-hidden-mb"> <input type="checkbox" class="typecho-table-checkbox" value="<?php $attachments->cid(); ?>" name="cid[]"/> </td> <td class="kit-hidden-mb"><a href="<?php $options->adminUrl('manage-comments.php?cid=' . $attachments->cid); ?>" class="balloon-button size-<?php echo \Typecho\Common::splitByCount($attachments->commentsNum, 1, 10, 20, 50, 100); ?>"><?php $attachments->commentsNum(); ?></a> </td> <td class="kit-hidden-mb preview-cell"> <?php if ($isImage && !empty($fileUrl)): ?> <img src="<?php echo $fileUrl; ?>" class="media-preview-img" alt="<?php $attachments->title(); ?>" data-full-src="<?php echo $fileUrl; ?>" data-title="<?php $attachments->title(); ?>"> <?php else: ?> <i class="mime-<?php echo $mime; ?> media-preview-icon"></i> <?php endif; ?> </td> <td> <i class="mime-<?php echo $mime; ?>"></i> <a href="<?php $options->adminUrl('media.php?cid=' . $attachments->cid); ?>"><?php $attachments->title(); ?></a> <?php if (!empty($fileUrl)): ?> <a href="<?php echo $fileUrl; ?>" target="_blank" title="<?php _e('浏览 %s', $attachments->title); ?>"><i class="i-exlink"></i></a> <?php endif; ?> </td> <td class="kit-hidden-mb"><?php $attachments->author(); ?></td> <td class="kit-hidden-mb"> <?php if ($attachments->parentPost->cid): ?> <a href="<?php $options->adminUrl('write-' . (0 === strpos($attachments->parentPost->type, 'post') ? 'post' : 'page') . '.php?cid=' . $attachments->parentPost->cid); ?>"><?php $attachments->parentPost->title(); ?></a> <?php else: ?> <span class="description"><?php _e('未归档'); ?></span> <?php endif; ?> </td> <td><?php $attachments->dateWord(); ?></td> </tr> <?php endwhile; ?> <?php else: ?> <tr> <td colspan="7"><h6 class="typecho-list-table-title"><?php _e('没有任何文件'); ?></h6> </td> </tr> <?php endif; ?> </tbody> </table><!-- end .typecho-list-table --> </div><!-- end .typecho-table-wrap --> </form><!-- end .operate-form --> <div class="typecho-list-operate clearfix"> <form method="get"> <div class="operate"> <label><i class="sr-only"><?php _e('全选'); ?></i><input type="checkbox" class="typecho-table-select-all"/></label> <div class="btn-group btn-drop"> <button class="btn dropdown-toggle btn-s" type="button"><i class="sr-only"><?php _e('操作'); ?></i><?php _e('选中项'); ?> <i class="i-caret-down"></i></button> <ul class="dropdown-menu"> <li><a lang="<?php _e('你确认要删除这些文件吗?'); ?>" href="<?php $security->index('/action/contents-attachment-edit?do=delete'); ?>"><?php _e('删除'); ?></a> </li> </ul> </div> <button class="btn btn-s btn-warn btn-operate" href="<?php $security->index('/action/contents-attachment-edit?do=clear'); ?>" lang="<?php _e('您确认要清理未归档的文件吗?'); ?>"><?php _e('清理未归档文件'); ?></button> </div> <?php if ($attachments->have()): ?> <ul class="typecho-pager"> <?php $attachments->pageNav(); ?> </ul> <?php endif; ?> </form> </div><!-- end .typecho-list-operate --> </div> </div><!-- end .typecho-page-main --> </div> </div> <!-- 图片预览模态框 --> <div id="imageModal" class="modal"> <span class="close">&times;</span> <div class="modal-content"> <img class="modal-image" id="modalImage" src="" alt=""> <div class="modal-title" id="modalTitle"></div> </div> </div> <script src="<?php $options->adminStaticUrl('js', 'moxie.js'); ?>"></script> <script src="<?php $options->adminStaticUrl('js', 'plupload.js'); ?>"></script> <script> document.addEventListener('DOMContentLoaded', function() { // 获取元素 const toggleUploadBtn = document.getElementById('toggleUploadBtn'); const cancelUploadBtn = document.getElementById('cancelUploadBtn'); const uploadSection = document.getElementById('uploadSection'); const uploadButton = document.getElementById('uploadButton'); const browseLink = document.getElementById('browseLink'); const fileInput = document.getElementById('fileInput'); const uploadArea = document.getElementById('uploadArea'); const uploadProgress = document.getElementById('uploadProgress'); const progressFill = document.getElementById('progressFill'); const progressText = document.getElementById('progressText'); // 模态框元素 const modal = document.getElementById('imageModal'); const modalImg = document.getElementById('modalImage'); const modalTitle = document.getElementById('modalTitle'); const closeBtn = document.querySelector('.close'); // 切换上传区域显示/隐藏 // toggleUploadBtn.addEventListener('click', function() { // uploadSection.classList.toggle('show'); // // 滚动到上传区域 // if (uploadSection.classList.contains('show')) { // uploadSection.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); // } // }); // 切换上传区域显示/隐藏 toggleUploadBtn.addEventListener('click', function() { uploadSection.classList.add('show'); // 显示取消按钮,隐藏上传按钮 toggleUploadBtn.style.display = 'none'; cancelUploadBtn.style.display = 'inline-block'; // 滚动到上传区域 uploadSection.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); }); // 取消上传 cancelUploadBtn.addEventListener('click', function() { uploadSection.classList.remove('show'); // 显示上传按钮,隐藏取消按钮 toggleUploadBtn.style.display = 'inline-block'; cancelUploadBtn.style.display = 'none'; }); // 点击链接触发文件选择 uploadButton.addEventListener('click', function(e) { e.preventDefault(); // 触发隐藏的文件输入框 fileInput.click(); }); // 点击浏览链接也触发文件选择 browseLink.addEventListener('click', function() { fileInput.click(); }); // 初始化上传器 var uploader = new plupload.Uploader({ runtimes: 'html5,flash,html4', browse_button: 'fileInput', // 绑定到隐藏的文件输入框 container: 'uploadArea', drop_element: 'uploadArea', url: '<?php $security->index('/action/upload'); ?>', flash_swf_url: '<?php $options->adminStaticUrl('js', 'Moxie.swf'); ?>', filters: { max_file_size: '<?php echo $phpMaxFilesize ?>', mime_types: [{'title': '<?php _e('允许上传的文件'); ?>', 'extensions': '<?php echo implode(',', $options->allowedAttachmentTypes); ?>'}] }, init: { PostInit: function() { console.log('Uploader initialized'); }, FilesAdded: function(up, files) { console.log('Files added:', files); // 显示进度条 uploadProgress.style.display = 'block'; // 开始上传 up.start(); }, UploadProgress: function(up, file) { // 更新进度条 var percent = file.percent; progressFill.style.width = percent + '%'; progressText.textContent = percent + '%'; }, FileUploaded: function(up, file, response) { console.log('File uploaded:', response); if (response.status === 200) { try { var result = JSON.parse(response.response); if (result && result[1]) { // 上传成功,刷新页面显示新文件 location.reload(); } } catch (e) { console.error('Parse error:', e); } } // 隐藏进度条 setTimeout(function() { uploadProgress.style.display = 'none'; progressFill.style.width = '0%'; progressText.textContent = '0%'; // 隐藏上传区域 uploadSection.classList.remove('show'); }, 1000); }, Error: function(up, err) { console.error('Upload error:', err); alert('上传出错: ' + err.message); // 隐藏进度条 uploadProgress.style.display = 'none'; } } }); uploader.init(); // 拖拽事件 uploadArea.addEventListener('dragover', function(e) { e.preventDefault(); this.classList.add('dragover'); }); uploadArea.addEventListener('dragleave', function(e) { e.preventDefault(); this.classList.remove('dragover'); }); uploadArea.addEventListener('drop', function(e) { e.preventDefault(); this.classList.remove('dragover'); }); // 图片预览功能 const previewImages = document.querySelectorAll('.media-preview-img'); previewImages.forEach(img => { img.addEventListener('click', function(e) { e.preventDefault(); e.stopPropagation(); const fullSrc = this.getAttribute('data-full-src'); const title = this.getAttribute('data-title'); if (fullSrc) { modalImg.src = fullSrc; modalTitle.textContent = title || ''; modal.style.display = 'block'; document.body.style.overflow = 'hidden'; } }); }); // 关闭模态框 function closeModal() { modal.style.display = 'none'; document.body.style.overflow = ''; } closeBtn.addEventListener('click', closeModal); modal.addEventListener('click', function(e) { if (e.target === modal) { closeModal(); } }); document.addEventListener('keydown', function(e) { if (e.key === 'Escape' && modal.style.display === 'block') { closeModal(); } }); }); </script> <?php include 'copyright.php'; include 'common-js.php'; include 'table-js.php'; include 'footer.php'; ?>