Compare commits

...

38 Commits

Author SHA1 Message Date
QiuChenly
f6fbfcc8aa 更新介绍 2025-11-20 01:08:12 +08:00
秋城落叶
111822c480 Delete QiuChenAppStore-v1.0.15-2025-11-18.dmg 2025-11-19 17:17:36 +08:00
秋城落叶
e769e01c5a Update for 26.1.2 and more app 2025-11-18 16:20:02 +08:00
QiuChenly
d761536bc2 Update to v1.0.15 2025-11-18 01:29:17 +08:00
秋城落叶
7d4ba63782 更新描述和版本 2025-11-17 10:01:15 +08:00
QiuChenly
e4dda7929c 全新应用商店,视频教程:https://t.me/c/2311066000/540318 2025-11-16 19:36:24 +08:00
QiuChenly
cc6027f126 res/Install.md 查阅新版安装说明 2025-11-16 05:39:11 +08:00
QiuChenly
34c2e46567 优化性能 修复卡顿 修复各种bug 2025-11-14 04:00:32 +08:00
秋城落叶
f0f79f04df 更新修复错误 2025-11-13 18:28:21 +08:00
QiuChenly
8394c93e6c 性能优化、错误修复
### 后端更新

性能优化
- Redis、数据库索引优化, 复杂联表查询查询速度从10秒降低到500ms

功能增强
- 统一文件上传 API
- 论坛权限管理优化

### 前端更新

问题修复
- 统一错误提示、组件复用、图标本地化、密码加密传输、大文件分片上传

新增功能
- 全局通知系统、WebSocket 实时消息、图标批量同步、IP 封禁管理、应用版本批量查询、头像上传、TypeScript 类型完善、讨论区功能增强

代码优化
- 删除冗余组件、统一 API 错误处理、优化代码结构、统一 UI 风格
2025-11-13 03:02:01 +08:00
QiuChenly
29caaa1505 支持破解开发者手动上传预破解二进制包到 QiuChenly 的弹性计算云存储中,并提供简单的操作让开发者触达用户下载端。
*核心功能*
• 预破解包发布功能:开发者可上传预破解包到云存储,一键绑定到应用并自定义版本号和 Beta 状态
• 用户个人信息更新和密码修改:支持编辑个人信息、修改密码,新增设备配置管理功能
• 标签页组件和分页组件:新增通用组件,提升界面一致性和用户体验

*管理员功能*
• 讨论区管理增强:支持批量管理、状态筛选、一键批准申请
• 管理员面板扩展:新增文件管理、App管理、系统设置等模块

*讨论区功能*
• 讨论区权限批量查询优化:减少多次请求,提升页面加载速度
• 讨论区申请开通功能:用户可申请开放讨论区,管理员审核通过后自动开放

*通信与通知*
• 私信系统:支持一对一私信,可发送文本、图片和文件
• 系统通知中心:集中管理所有重要通知,支持展开查看详情和标记已读

*性能优化*
• 网络请求性能大幅提升:支持多请求并发处理
• 可自定义网络请求并发数:根据网络环境和设备性能灵活调整
2025-11-12 00:48:11 +08:00
秋城落叶
e04b511bdf fix: webui中验证下载文件夹字段写错无法验证的问题 2025-11-11 17:32:30 +08:00
秋城落叶
2468013148 增加了对macOS 10.15系统的API兼容修复,避免崩溃。修复了证书自签名Surge 6最新版上可能会遇到的定时退出问题。 2025-11-11 17:22:13 +08:00
秋城落叶
9d7bea897c 最新提交包含多个修复,从样式到布局,后端接口的补充,以及大量的逻辑错误修复。
重构前端组件,新增评论表单和回复项组件,优化评论区功能,支持楼中楼回复和用户信息卡片展示。更新API以获取用户列表和申请状态,增强用户交互体验。调整样式以提升界面一致性和可用性,同时更新设置页面以支持网络请求并发线程数配置。
2025-11-10 17:48:37 +08:00
QiuChenly
1e049cc16e https://mac-qiuchenly.yltfspace.com 已经上线。 2025-11-10 04:23:24 +08:00
秋城落叶
507b7476bd 修复 Navicat 17.x.5版本注入打开崩溃的问题。 2025-11-04 15:28:58 +08:00
QiuChenly
cc19f8713b 合并更新主程序代码 2025-11-03 22:36:31 +08:00
QiuChenly
01136bb561 修复Adobe PS2026 27.0 激活因内部使用了错误产品代码导致提示错误的问题。 2025-11-03 22:29:04 +08:00
QiuChenly
3523f0442c 修复WebUI BUG与性能优化,增加几个重点功能和重构底层请求接口。 2025-11-01 03:55:46 +08:00
QiuChenly
3960b72796 撤销了会导致sqlite3操作崩溃异常的库,现在注入后打开崩溃的软件将不会遇到崩溃问题。 2025-10-30 00:05:34 +08:00
QiuChenly
5686d2a6ad Surge 2838/Surge 6 版本中, 因为了屏蔽官方在线网络激活验证机制,屏蔽了URL路径包含“surge”,导致订阅路径中存在surge的也reject掉的bug。 2025-10-28 23:28:00 +08:00
QiuChenly
496dcbc125 修复ARM Mac上因系统API返回数据指针内存数据结构写入错误导致的偶发性崩溃。(Senplayer) 2025-10-28 22:32:50 +08:00
QiuChenly
d7cf508a3d 修复前端卡顿问题与适配更多app更新获取。 2025-10-27 22:11:36 +08:00
秋城落叶
e5aea02566 Add More Update 2025-10-27 18:36:31 +08:00
QiuChenly
2eb7c034d9 一些微小的改动
QiuChenly应用商店做了一些微小的改动。
1. 试验性支持App检查更新。
2. 试验性支持App下载与保存。
3. 增加了高斯模糊。
2025-10-27 02:31:45 +08:00
QiuChenly
e4f70dc100 项目增加超级牛力并hack了媒体三幻神。 2025-10-26 07:17:09 +08:00
QiuChenly
dd3dc4e241 合并来自Atb的通杀代码 2025-10-25 02:18:14 +08:00
秋城落叶
49ba98f86f fix: SenPlayer, 修正了崩溃问题。 2025-10-23 17:40:34 +08:00
QiuChenly
18c3b44d70 更新 config.json 中的下载链接,修正 Parallels Desktop 直接发布免注入直装版 dmg 文件的相关信息。 2025-10-19 22:21:10 +08:00
QiuChenly
3b5ab2631a 更新描述信息 2025-10-19 22:20:08 +08:00
QiuChenly
0295cca3a2 更新 config.json,添加 Parallels Desktop 直接发布免注入直装版 dmg 文件的通知信息,删除不再使用的清理脚本和 entitlements 文件,修正 readme.md 中的格式和描述。 2025-10-19 22:17:29 +08:00
秋城落叶
3349360fe2 更新错误描述 2025-10-17 17:29:57 +08:00
秋城落叶
e951b07cfe fix imazing https://downloads.imazing.com/mac/iMazing/3.4.0.23266/iMazing_3.4.0.23266.dmg 2025-10-17 17:26:38 +08:00
QiuChenly
33bcc15db1 本次提交改进了一直以来存在于Parallels Desktop 上的vTPM机密信息访问与TouchID解锁错误、引发崩溃的问题。 2025-10-14 22:02:10 +08:00
秋城落叶
f7568cd746 Support Paste 6.1.0 Intel ARM. 2025-10-14 18:17:32 +08:00
秋城落叶
7e4250d76d 更正卸载脚本代码 合并来自xiaohe的Paste 6 ARM修复代码 2025-10-14 16:40:06 +08:00
秋城落叶
a07cb064cf 增加新的setapp支持 2025-10-14 16:30:35 +08:00
秋城落叶
0000058cfd 重构安装机制,将软链接改为文件复制方式,所有文件独立运行,更新LaunchDaemon配置,确保服务正常启动。文档更新以反映新安装流程和权限配置要求。 2025-10-14 16:24:27 +08:00
95 changed files with 6545 additions and 521 deletions

4
.gitignore vendored
View File

@@ -17,4 +17,6 @@ __pycache__
*.pyd
*.pyw
*.pyz
*.bak
*.bak
*.db
cache

BIN
InjectLib

Binary file not shown.

1
InjectLib Symbolic link
View File

@@ -0,0 +1 @@
res/HayakuDaemon.app/Contents/MacOS/InjectLib

View File

@@ -28,6 +28,11 @@
"basePublicConfig": {
"bridgeFile": "/Contents/Frameworks/"
},
"backend": {
"enabled": true,
"baseUrl": "https://mac-qiuchenly.yltfspace.com/api/v1",
"baseUrl-local": "http://localhost:4000/api/v1"
},
"AppList": [
{
"packageName": [
@@ -38,22 +43,22 @@
{
"packageName": "com.parallels.desktop.console",
"supportVersion": [
"18.3.3",
"20.3.0",
"20.3.1",
"20.3.2",
"20.4.0",
"20.4.1",
"26.0.0",
"26.0.1",
"26.1.0",
"26.1.1"
"publishfile"
],
"notice": [
{
"language": "zh-CN",
"title": "Parallels Desktop 直接发布免注入直装版dmg文件",
"downloadLocation": "https://t.me/qiuchenlymac/929",
"description": "Parallels Desktop 直接发布免注入直装版dmg文件\n,下载dmg文件后, 执行: xattr -cr xxxx.dmg && codesign -fs - xxxx.dmg\n, 然后双击dmg挂载, 双击运行里面的 '安装' App即可。不必手动注入, 这可能会引起不必要的混淆。\n修复问题: \n1. Windows 11 安装时提示 “未知错误”导致无法启动。\n2. ARM电脑上运行PD直接安装macOS虚拟机的时候无法正常启动macOS虚拟机的问题。\n3. 安装Windows 10/11 的时候底层TPM信息丢失无法启动的问题。Intel Mac & ARM Mac 均适用)"
},
{
"language": "en-US",
"title": "Parallels Desktop Provide Direct Install Dmg File",
"downloadLocation": "https://t.me/qiuchenlymac/929",
"description": "Parallels Desktop Provide Direct Install Dmg File\n, Download the dmg file and execute: xattr -cr xxxx.dmg && codesign -fs - xxxx.dmg\n, then double click the dmg file to mount, double click the 'Install' App inside to install. No need to manually inject, this may cause unnecessary confusion.\nFix problems: \n1. Windows 11 installation prompts “Unknown error” and cannot start.\n2. ARM computers running PD directly install macOS virtual machines cannot start macOS virtual machines normally.\n3. The underlying TPM information is lost and cannot start when installing Windows 10/11. (Both Intel Mac & ARM Mac are applicable)"
}
],
"deepSignApp": true,
"entitlements": "VM.entitlements",
"extraShell": "cleanup_pd.sh",
"feedUrlAll": "https://desktop.parallels.cn/api/v1/product_permissions?license_flags=1&license_edition=0&product_locale=zh_CN&os=mac&product_type=pdfm&os_version=26.1.0&product_arch=x86_64&product_version=20.4.1-55996&is_beta=0",
"feedUrl": "https://update.parallels.com/desktop/v26/parallels/parallels_updates.xml",
"downloadUrl": [
{
"url": "https://download.parallels.com/desktop/v20/20.4.0-55980/ParallelsDesktop-20.4.0-55980.dmg",
@@ -75,17 +80,6 @@
"url": "https://download.parallels.com/desktop/v26/26.1.0-57287/ParallelsDesktop-26.1.0-57287.dmg",
"version": "26.1.0-57287"
}
],
"childApp": [
{
"packageName": "com.parallels.desktop.console",
"appBaseLocate": "/Contents/MacOS/Parallels Service.app"
},
{
"packageName": "com.parallels.desktop.console",
"appBaseLocate": "/Contents/MacOS/Parallels VM.app",
"entitlements": "VM.entitlements"
}
]
},
{
@@ -131,19 +125,6 @@
}
]
},
{
"packageName": "com.cryptic-apps.hopper-web-4不支持",
"bridgeFile": "/Contents/MacOS/",
"injectFile": "Hopper Disassembler v4",
"dylibSelect": "CoreInject.dylib",
"needCopyToAppDir": true,
"supportVersion": [
"5.16.0",
"5.17.0",
"5.18.1"
],
"forQiuChenly": true
},
{
"packageName": "codes.rambo.AirBuddy",
"helperFile": [
@@ -161,7 +142,6 @@
"helperFile": [
"/Contents/Library/LaunchServices/com.macpaw.CleanMyMac5.Agent"
],
"deepSignApp": true,
"childApp": [
{
"packageName": "com.macpaw.CleanMyMac5.Menu",
@@ -191,12 +171,17 @@
},
{
"packageName": "com.DigiDNA.iMazing3Mac",
"feedUrl": "https://downloads.imazing.com/com.DigiDNA.iMazing3Mac.xml",
"supportVersion": [
"3.2.1",
"3.3.0",
"3.3.1",
"3.4.0"
],
"downloadUrl": [
{
"version": "3.4.0.23266",
"url": "https://downloads.imazing.com/mac/iMazing/3.4.0.23266/iMazing_3.4.0.23266.dmg"
}
]
},
{
@@ -422,33 +407,13 @@
"supportVersion": [
"3.1.0",
"4.0.0"
],
"feedUrl": "https://mac-release.stash.ws/appcast.xml"
]
},
{
"packageName": "com.firecore.infuse"
},
{
"packageName": "com.coderforart.MWeb3",
"injectFile": "Sparkle.framework/Versions/B/Sparkle",
"needCopyToAppDir": true,
"deepSignApp": true,
"dylibSelect": "CoreInject.dylib"
},
{
"packageName": "com.nektony.App-Cleaner-SIII",
"needCopyToAppDir": true,
"deepSignApp": true,
"bridgeFile": "/Contents/MacOS/",
"injectFile": "App Cleaner 8",
"tccutil": [
"All",
"AddressBook",
"Reminders",
"Photos",
"AppleEvents"
],
"dylibSelect": "CoreInject.dylib"
"packageName": "com.coderforart.MWeb3"
},
{
"packageName": [
@@ -466,28 +431,29 @@
{
"packageName": "com.nektony.MacCleaner-PRO-SIII",
"appBaseLocate": "/Applications/MacCleaner 3 Pro/MacCleaner Pro 3.app",
"needCopyToAppDir": true,
"deepSignApp": true,
"injectFile": "Sparkle.framework/Versions/A/Sparkle",
"tccutil": [
"All",
"AddressBook",
"Calendar",
"Reminders",
"Photos",
"AppleEvents"
],
"dylibSelect": "CoreInject.dylib"
"downloadUrl": [
{
"version": "最新版",
"url": "https://download.nektony.com/download/mac-cleaner-pro/dmg/mac-cleaner-pro.dmg"
}
]
},
{
"packageName": "com.nektony.Disk-Expert-SIII"
"packageName": "com.nektony.Disk-Expert-SIII",
"downloadUrl": [
{
"version": "最新版",
"url": "#"
}
]
},
{
"packageName": "com.nektony.Disk-Expert-SIII",
"appBaseLocate": "/Applications/MacCleaner 3 Pro/Disk Expert 5.app"
},
{
"packageName": "com.nektony.Duplicates-Finder"
"packageName": "com.nektony.Duplicates-Finder",
"feedUrl": "https://download.nektony.com/pro-support/v3/duplicates-finder-site/update/update.xml"
},
{
"packageName": [
@@ -668,7 +634,10 @@
"noDeep": true
},
{
"packageName": ["com.adobe.lightroomCC","com.adobe.mas.lightroomCC"]
"packageName": [
"com.adobe.lightroomCC",
"com.adobe.mas.lightroomCC"
]
},
{
"packageName": "com.adobe.LightroomClassicCC7",
@@ -883,35 +852,13 @@
"deepSignApp": true
},
{
"packageName": "com.bandisoft.mac.bandizip",
"needCopyToAppDir": true,
"deepSignApp": true,
"bridgeFile": "/Contents/MacOS/",
"injectFile": "Bandizip"
"packageName": "com.bandisoft.mac.bandizip"
},
{
"packageName": "com.bandisoft.mac.bandizip365",
"supportVersion": [
"7.34"
]
"packageName": "com.bandisoft.mac.bandizip365"
},
{
"packageName": "com.wuziqi.SenPlayer",
"supportVersion": [
"5.6.0",
"5.6.1",
"5.6.2",
"5.6.3",
"5.6.4",
"5.6.5",
"5.7.0"
]
},
{
"packageName": "com.gitswift.SwiftServer",
"supportVersion": [
"1.1.2都不支持"
]
"packageName": "com.wuziqi.SenPlayer"
},
{
"packageName": "org.cindori.Sensei",
@@ -932,7 +879,7 @@
"/Contents/Library/LaunchServices/com.proxyman.NSProxy.HelperTool"
],
"description": "Proxyman 是一款 macOS 平台的网络抓包工具,支持 HTTP/HTTPS/WebSocket 等协议的抓包和分析。官网版本目前是通杀的。",
"feedUrl":"https://proxyman.com/osx/version.xml",
"feedUrl": "https://proxyman.com/osx/version.xml",
"downloadUrl": [
{
"version": "5.23.0",
@@ -1033,11 +980,7 @@
"injectFile": "BFPageControl.framework/Versions/A/BFPageControl"
},
{
"packageName": "com.wiheads.paste",
"supportVersion": [
"6.0.1",
"6.0.3"
]
"packageName": "com.wiheads.paste"
},
{
"packageName": "cn.better365.ishot",
@@ -1443,7 +1386,8 @@
"co.podzim.PDFPals-setapp",
"com.lowtechguys.Clop-setapp",
"com.dreamer.habits-setapp",
"com.smallwhite-media.SwiftyLaunch-setapp"
"com.smallwhite-media.SwiftyLaunch-setapp",
"com.imobie.FocuSee-setapp"
]
},
{

1
frontend/dist/assets/0jLa3ujY.css vendored Normal file
View File

@@ -0,0 +1 @@
.pagination[data-v-fde42dc3]{display:flex;justify-content:center;align-items:center;gap:16px;margin-top:24px;padding:16px;border-top:1px solid var(--border-light)}.pagination .pagination-info[data-v-fde42dc3]{font-size:14px;color:var(--text-secondary);white-space:nowrap}.pagination .btn[data-v-fde42dc3]{min-width:80px;padding:8px 16px;background:var(--bg-secondary);border:1px solid var(--border-color);color:var(--text-primary);border-radius:6px;font-size:14px;transition:all .2s ease}.pagination .btn[data-v-fde42dc3]:hover:not(:disabled){background:var(--bg-hover);border-color:var(--primary-color);color:var(--primary-color)}.pagination .btn[data-v-fde42dc3]:disabled{opacity:.5;cursor:not-allowed;pointer-events:none}

209
frontend/dist/assets/0wkerNym.js vendored Normal file
View File

@@ -0,0 +1,209 @@
import{d as e,c as l,a as t,b as a,m as s,F as i,B as n,n as r,e as o,z as u,u as d,h as c,r as p,w as v,l as m,R as y,p as h,q as g,t as f,g as w,H as k,o as R,Q as b,I,C as N,P as C,S as U}from"./CdD4XvnD.js"
import{_ as E,a as S,b as A,c as F,g as L,A as x,i as T,j as P,W as D,u as z,f as _,M,T as $}from"./BaSQ3xJt.js"
import{R as j,u as B,a as O}from"./COGASUq6.js"
import{U as q}from"./Birl3CuV.js"
import{I as H}from"./j0cGtmjd.js"
import{P as Y}from"./CU438sNO.js"
import{P as V}from"./JHpP7yB4.js"
import{I as W}from"./JespKOZ3.js"
import{a as Q}from"./CFhT0fBm.js"
import{c as G,d as J,e as K,f as X,a as Z,g as ee,b as le}from"./BqTCaadz.js"
import{E as te}from"./CNsXoenb.js"
import{u as ae}from"./4xYhABhf.js"
import{u as se}from"./C_POYLAU.js"
import{u as ie}from"./lyDeIg7R.js"
import{g as ne}from"./6K6b4Qy_.js"
const re={key:0,class:"post-attachments"},oe={key:0,class:"post-images"},ue={key:1,class:"post-files"},de=["onClick"],ce={class:"attachment-name"},pe={class:"attachment-size"},ve=S(e({__name:"PostAttachmentList",props:{attachments:{},type:{default:"post"}},emits:["imageClick","fileClick"],setup(e,{emit:p}){const v=e,m=p,y=l(()=>G(v.attachments)),h=l(()=>J(v.attachments)),g=l(()=>K(v.attachments)),f=e=>{m("imageClick",e)}
return(l,p)=>e.attachments&&e.attachments.length>0?(c(),t("div",re,[y.value.length>0?(c(),t("div",oe,[s(W,{"image-ids":g.value,"show-index":!1,columns:4,"max-width":120,onImageClick:f},null,8,["image-ids"])])):a("",!0),h.value.length>0?(c(),t("div",ue,[(c(!0),t(i,null,n(h.value,e=>(c(),t("div",{key:e.id,class:r(["attachment-item",{clickable:!0}]),onClick:l=>(e=>{m("fileClick",e)})(e)},[s(E,{icon:"mdi:file",class:"attachment-icon"}),o("span",ce,u(e.name),1),o("span",pe,u(d(Q)(e.size)),1),s(E,{icon:"mdi:download",class:"attachment-action-icon",title:"下载文件"})],8,de))),128))])):a("",!0)])):a("",!0)}}),[["__scopeId","data-v-7d2f6224"]]),me={class:"reply-avatar"},ye={class:"reply-content-wrapper"},he={class:"reply-header"},ge={class:"reply-author clickable"},fe={key:1,class:"reply-to"},we={class:"reply-time"},ke={class:"reply-content"},Re={key:0,class:"reply-edit-form"},be=["value","rows"],Ie={key:1,class:"edit-new-attachments"},Ne=["src"],Ce=["onClick"],Ue=["onClick"],Ee={class:"edit-attachment-actions"},Se={class:"reply-actions"},Ae=["disabled"],Fe=["disabled"],Le={key:0,class:"nested-replies"},xe=S(e({__name:"PostReplyItem",props:{reply:{},isAuthor:{type:Boolean},isAdmin:{type:Boolean},userId:{},userName:{},isNew:{type:Boolean,default:!1},isEditing:{type:Boolean,default:!1},editingContent:{default:""},editingAttachments:{default:()=>[]},editingNewImages:{default:()=>[]},editingNewFiles:{default:()=>[]},showNestedForm:{type:Boolean,default:!1},replyingToNestedReplyId:{},nestedReplyToUserName:{default:""},isSubmittingNestedReply:{type:Boolean,default:!1},editingNestedReplyId:{default:null},editingNestedReplyContent:{default:""},editingNestedReplyAttachments:{default:()=>[]},editingNestedReplyNewAttachments:{default:()=>[]},editingNestedReplyNewFiles:{default:()=>[]},likeLoading:{type:[Boolean,String,null],default:!1},size:{default:"medium"},showNestedReplies:{type:Boolean,default:!0}},emits:["edit","save","cancel","delete","report","reply","like","dislike","nestedSubmit","nestedCancel","nestedEdit","nestedSave","nestedEditCancel","nestedDelete","nestedReport","nestedReplyClick","nestedLike","nestedDislike","authorHover","authorLeave","imageClick","fileClick","nestedImageClick","removeAttachment","removeNewImage","removeNewFile","editImageSelect","editFileSelect","nestedRemoveAttachment","nestedRemoveNewImage","nestedRemoveNewFile","nestedEditImageSelect","nestedEditFileSelect"],setup(e,{emit:l}){const k=e,R=l,{isUserOnline:b}=ae(),I=p(null),N=p(null),C=X,U=p(k.editingContent||"")
v(()=>k.editingContent,e=>{U.value=e||""})
const S=e=>{R("authorHover",e,k.reply.replyUser.id,k.reply.replyUser.nickName||k.reply.replyUser.username,k.reply.replyUser.username||"",k.reply.replyUser.avatar)},F=e=>{k.reply.replyToUser&&R("authorHover",e,k.reply.replyToUser.id,k.reply.replyToUser.nickName||k.reply.replyToUser.username,k.reply.replyToUser.username||"",k.reply.replyToUser.avatar)},L=()=>{R("authorLeave")},x=e=>{R("imageClick",e)},T=e=>{if(k.reply.attachments){const l=k.reply.attachments.filter(e=>"image"===e.type).findIndex(l=>l.id===e)
l>=0&&R("imageClick",l)}},P=e=>{R("fileClick",e)},D=()=>{R("edit")},z=()=>{R("save")},_=()=>{R("cancel")},M=()=>{R("delete")},$=()=>{R("report")},B=()=>{R("reply")},O=(e,l,t)=>{R("nestedSubmit",e,l,t)},q=()=>{R("nestedCancel")},H=()=>{R("nestedEditCancel")},Y=()=>{R("like")},V=()=>{R("dislike")}
return(l,p)=>(c(),m(y("small"===e.size?"div":h),{name:"small"===e.size?void 0:"reply-fade",appear:"small"!==e.size},{default:g(()=>[o("div",{class:r(["reply-item",{"reply-new":e.isNew&&"medium"===e.size,"reply-small":"small"===e.size,"reply-medium":"medium"===e.size}])},[o("div",me,[s(A,{"avatar-id":e.reply.replyUser.avatar,"user-name":e.reply.replyUser.nickName||e.reply.replyUser.username,userId:e.reply.replyUser.id,size:e.size},null,8,["avatar-id","user-name","userId","size"]),o("div",{class:r(["online-indicator",{online:!0===d(b)(e.reply.replyUser.id).value,offline:!1===d(b)(e.reply.replyUser.id).value}])},null,2)]),o("div",ye,[o("div",he,[o("div",{class:"author-wrapper",onMouseenter:f(S,["stop"]),onMouseleave:f(L,["stop"])},[o("span",ge,u(e.reply.replyUser.nickName||e.reply.replyUser.username),1),"admin"===e.reply.replyUser.role||"qiuchenly"===e.reply.replyUser.role?(c(),t("span",{key:0,class:r(["role-tag",{"role-admin":"admin"===e.reply.replyUser.role,"role-qiuchenly":"qiuchenly"===e.reply.replyUser.role}])},u("admin"===e.reply.replyUser.role?"管理员":"qiuchenly"),3)):a("",!0),e.reply.replyToUser?(c(),t("span",fe,[w(u("small"===e.size?"回复":"回复了")+" ",1),o("span",{class:"reply-to-name clickable",onMouseenter:f(F,["stop"]),onMouseleave:f(L,["stop"]),onClick:p[0]||(p[0]=f(l=>"small"===e.size?void R("reply"):void 0,["stop"]))}," @"+u(e.reply.replyToUser.nickName||e.reply.replyToUser.username),33)])):a("",!0)],32),o("span",we,u(d(C)(e.reply.date)),1)]),o("div",ke,[e.isEditing?(c(),t("div",Re,[o("textarea",{value:U.value,onInput:p[1]||(p[1]=e=>U.value=e.target.value),class:"reply-textarea",rows:"small"===e.size?3:4,maxlength:"5000"},null,40,be),e.editingAttachments.length>0?(c(),m(te,{key:0,attachments:e.editingAttachments,onRemove:p[2]||(p[2]=e=>R("removeAttachment",e)),onImageClick:T},null,8,["attachments"])):a("",!0),e.editingNewImages.length>0||e.editingNewFiles.length>0?(c(),t("div",Ie,[(c(!0),t(i,null,n(e.editingNewImages,(e,l)=>(c(),t("div",{key:`img-${l}`,class:"edit-attachment-preview"},[e.preview?(c(),t("img",{key:0,src:e.preview,alt:"预览"},null,8,Ne)):a("",!0),o("button",{class:"remove-attachment-btn",onClick:e=>R("removeNewImage",l)},[s(E,{icon:"mdi:close"})],8,Ce)]))),128)),(c(!0),t(i,null,n(e.editingNewFiles,(e,l)=>(c(),t("div",{key:`file-${l}`,class:"edit-attachment-preview file"},[s(E,{icon:"mdi:file"}),o("span",null,u(e.file.name),1),o("button",{class:"remove-attachment-btn",onClick:e=>R("removeNewFile",l)},[s(E,{icon:"mdi:close"})],8,Ue)]))),128))])):a("",!0),o("div",Ee,[o("input",{ref_key:"editImageInput",ref:I,type:"file",accept:"image/*",multiple:"",onChange:p[3]||(p[3]=e=>R("editImageSelect",e)),style:{display:"none"}},null,544),o("input",{ref_key:"editFileInput",ref:N,type:"file",multiple:"",onChange:p[4]||(p[4]=e=>R("editFileSelect",e)),style:{display:"none"}},null,544),o("button",{class:"upload-btn small",onClick:p[5]||(p[5]=e=>{var l
return null==(l=I.value)?void 0:l.click()})},[s(E,{icon:"mdi:image-outline"}),p[12]||(p[12]=w(" 添加图片 ",-1))]),o("button",{class:"upload-btn small",onClick:p[6]||(p[6]=e=>{var l
return null==(l=N.value)?void 0:l.click()})},[s(E,{icon:"mdi:file-outline"}),p[13]||(p[13]=w(" 添加文件 ",-1))])]),o("div",{class:"edit-reply-actions"},[o("button",{class:"cancel-btn",onClick:_},"取消"),o("button",{class:"submit-reply-btn small",onClick:z},"保存")])])):(c(),t(i,{key:1},[o("p",null,u(e.reply.content),1),e.reply.attachments&&e.reply.attachments.length>0?(c(),m(ve,{key:0,attachments:e.reply.attachments,type:"small"===e.size?"nested":"reply",onImageClick:x,onFileClick:P},null,8,["attachments","type"])):a("",!0)],64))]),o("div",Se,[o("button",{class:r(["action-btn small",{active:"liked"===e.reply.userLikeStatus}]),onClick:Y,disabled:!0===e.likeLoading||e.likeLoading===e.reply.id},[s(E,{icon:"liked"===e.reply.userLikeStatus?"mdi:thumb-up":"mdi:thumb-up-outline",class:"action-icon"},null,8,["icon"]),o("span",null,u(e.reply.likeCount||0),1)],10,Ae),o("button",{class:r(["action-btn small",{active:"disliked"===e.reply.userLikeStatus}]),onClick:V,disabled:!0===e.likeLoading||e.likeLoading===e.reply.id},[s(E,{icon:"disliked"===e.reply.userLikeStatus?"mdi:thumb-down":"mdi:thumb-down-outline",class:"action-icon"},null,8,["icon"]),o("span",null,u(e.reply.dislikeCount||0),1)],10,Fe),o("button",{class:"action-btn small",onClick:B},[s(E,{icon:"mdi:reply-outline",class:"action-icon"}),p[14]||(p[14]=o("span",null,"回复",-1))]),e.isAuthor||e.isAdmin?(c(),t("button",{key:0,class:"action-btn small edit-btn",onClick:D},[s(E,{icon:"mdi:pencil",class:"action-icon"}),p[15]||(p[15]=o("span",null,"编辑",-1))])):a("",!0),e.isAuthor||e.isAdmin?(c(),t("button",{key:1,class:"action-btn small delete-btn",onClick:M},[s(E,{icon:"mdi:delete-outline",class:"action-icon"}),p[16]||(p[16]=o("span",null,"删除",-1))])):a("",!0),o("button",{class:"action-btn small report-btn",onClick:$},[s(E,{icon:"mdi:flag-outline",class:"action-icon"}),p[17]||(p[17]=o("span",null,"举报",-1))])]),e.showNestedReplies&&e.reply.nestedReplies&&e.reply.nestedReplies.length>0?(c(),t("div",Le,[(c(!0),t(i,null,n(e.reply.nestedReplies,l=>(c(),t(i,{key:l.id},[s(xe,{reply:l,size:"small","is-author":l.replyUser&&("object"==typeof l.replyUser?l.replyUser.id:l.replyUser)===e.userId,"is-admin":e.isAdmin,"is-editing":e.editingNestedReplyId===l.id,"editing-content":e.editingNestedReplyContent,"editing-attachments":e.editingNestedReplyAttachments,"editing-new-images":e.editingNestedReplyNewAttachments,"editing-new-files":e.editingNestedReplyNewFiles,"like-loading":"string"==typeof e.likeLoading?e.likeLoading===l.id:e.likeLoading,"show-nested-replies":!1,onEdit:t=>((e,l)=>{R("nestedEdit",e,l)})(e.reply.id,l),onSave:e=>{return t=l.id,void R("nestedSave",t)
var t},onCancel:H,onDelete:t=>{return a=e.reply.id,s=l.id,void R("nestedDelete",a,s)
var a,s},onReport:t=>{return a=e.reply.id,s=l.id,void R("nestedReport",a,s)
var a,s},onReply:t=>{return a=e.reply.id,s=l.replyUser.id,i=l.replyUser.nickName||l.replyUser.username,n=l.id,void R("nestedReplyClick",a,s,i,n)
var a,s,i,n},onLike:t=>R("nestedLike",e.reply.id,l.id),onDislike:t=>R("nestedDislike",e.reply.id,l.id),onAuthorHover:S,onAuthorLeave:L,onImageClick:e=>R("nestedImageClick",l,e),onFileClick:P,onRemoveAttachment:p[7]||(p[7]=e=>R("nestedRemoveAttachment",e)),onRemoveNewImage:p[8]||(p[8]=e=>R("nestedRemoveNewImage",e)),onRemoveNewFile:p[9]||(p[9]=e=>R("nestedRemoveNewFile",e)),onEditImageSelect:p[10]||(p[10]=e=>R("nestedEditImageSelect",e)),onEditFileSelect:p[11]||(p[11]=e=>R("nestedEditFileSelect",e))},null,8,["reply","is-author","is-admin","is-editing","editing-content","editing-attachments","editing-new-images","editing-new-files","like-loading","onEdit","onSave","onDelete","onReport","onReply","onLike","onDislike","onImageClick"]),e.showNestedForm&&e.replyingToNestedReplyId===l.id?(c(),m(j,{key:0,mode:"nested",visible:!0,"reply-to-user-name":e.nestedReplyToUserName,"user-id":e.userId,"user-name":e.userName,"show-cancel":!0,"is-submitting":e.isSubmittingNestedReply,onSubmit:O,onCancel:q},null,8,["reply-to-user-name","user-id","user-name","is-submitting"])):a("",!0)],64))),128))])):a("",!0),e.showNestedReplies&&e.showNestedForm&&!e.replyingToNestedReplyId?(c(),m(j,{key:1,mode:"nested",visible:e.showNestedForm,"reply-to-user-name":e.nestedReplyToUserName,"user-id":e.userId,"user-name":e.userName,"show-cancel":!0,"is-submitting":e.isSubmittingNestedReply,onSubmit:O,onCancel:q},null,8,["visible","reply-to-user-name","user-id","user-name","is-submitting"])):a("",!0)])],2)]),_:1},8,["name","appear"]))}}),[["__scopeId","data-v-08055a37"]]),Te=e=>"string"!=typeof e||"user"!==e&&"admin"!==e&&"qiuchenly"!==e?{0:"user",1:"admin",2:"qiuchenly"}["number"==typeof e?e:parseInt(String(e||0))]||"user":e,Pe=e=>"string"!=typeof e||"user"!==e&&"admin"!==e&&"qiuchenly"!==e?{0:"user",1:"admin",2:"qiuchenly"}["number"==typeof e?e:parseInt(String(e||0))]||"user":e,De=e=>{e.replyUser&&void 0!==e.replyUser.role&&(e.replyUser.role=Pe(e.replyUser.role)),e.replyToUser&&void 0!==e.replyToUser.role&&(e.replyToUser.role=Pe(e.replyToUser.role)),e.nestedReplies&&Array.isArray(e.nestedReplies)&&e.nestedReplies.forEach(e=>{e.replyUser&&void 0!==e.replyUser.role&&(e.replyUser.role=Pe(e.replyUser.role)),e.replyToUser&&void 0!==e.replyToUser.role&&(e.replyToUser.role=Pe(e.replyToUser.role))})}
function ze(e,l,t,a){const s=F(),i=L(),n=()=>void 0!==a||!(!t.value||!("parentReplyId"in t.value))
return{handleLike:async()=>{var r,o
if(!t.value||!e.value||!l.value)return
const u="liked"===t.value.userLikeStatus?"cancel":"like"
i.show("cancel"===u?"取消点赞中...":"点赞中...")
try{let i
if(n()){const s=a||t.value.id
i=await x.likeForumReply(e.value,l.value,s,u)}else i=await x.likeForumPost(e.value,l.value,u)
i.success&&t.value?(t.value.userLikeStatus=i.userLikeStatus,t.value.likeCount=i.likeCount,t.value.dislikeCount=i.dislikeCount,"cancel"===u?s.success("已取消点赞"):s.success("点赞成功")):s.error("点赞失败")}catch(d){s.error((null==(o=null==(r=d.response)?void 0:r.data)?void 0:o.message)||d.message||"点赞失败")}finally{i.hide()}},handleDislike:async()=>{var r,o
if(!t.value||!e.value||!l.value)return
const u="disliked"===t.value.userLikeStatus?"cancel":"dislike"
i.show("cancel"===u?"取消点踩中...":"点踩中...")
try{let i
if(n()){const s=a||t.value.id
i=await x.likeForumReply(e.value,l.value,s,u)}else i=await x.likeForumPost(e.value,l.value,u)
i.success&&t.value?(t.value.userLikeStatus=i.userLikeStatus,t.value.likeCount=i.likeCount,t.value.dislikeCount=i.dislikeCount,"cancel"===u?s.success("已取消点踩"):s.success("点踩成功")):s.error("点踩失败")}catch(d){s.error((null==(o=null==(r=d.response)?void 0:r.data)?void 0:o.message)||d.message||"点踩失败")}finally{i.hide()}}}}const _e={class:"post-detail-wrapper"},Me={class:"post-detail-page"},$e={class:"page-container"},je={class:"page-header"},Be={class:"header-buttons"},Oe={key:0,class:"loading"},qe={key:1,class:"error"},He={key:2,class:"post-detail-content"},Ye={key:0,class:"forum-breadcrumb"},Ve={class:"forum-icon-wrapper"},We={class:"forum-icon"},Qe=["src","alt"],Ge=["src","alt"],Je={class:"forum-name"},Ke={class:"post-breadcrumb-title"},Xe={class:"post-main"},Ze={class:"post-header"},el={class:"post-title"},ll={class:"post-meta"},tl={class:"author-avatar"},al={class:"post-author clickable"},sl={class:"post-time"},il={class:"post-content"},nl={class:"post-actions"},rl=["disabled"],ol=["disabled"],ul={class:"replies-section"},dl={class:"replies-header"},cl={key:0,class:"replies-list"},pl={key:2,class:"empty-replies"},vl=S(e({__name:"PostDetail",setup(e){const y=k(),h=I(),g=F(),w=L(),S=T(),{user:W}=z(),{isAdmin:Q}=_(),{isUserOnline:G,queryUsersOnlineStatus:J}=ae(),K=p(null),te=p(null),re=l(()=>{if(!K.value)return null
const e=ee(K.value)
return e?G(e).value:null}),oe=p(!0),ue=p(""),de=p(!1),ce=p(!1),pe=l(()=>h.params.bundleId),me=l(()=>h.params.postId),ye=p(1),he=p(50),ge=p(0),fe=l(()=>Math.ceil(ge.value/he.value)),we=p(null),ke=p(null),Re=l(()=>{var e
return"builtin"===(null==(e=we.value)?void 0:e.type)}),be=l(()=>{var e
return"created"===(null==(e=we.value)?void 0:e.type)}),Ie=l(()=>we.value?ne(we.value.bundleId):"mdi:forum-outline"),Ne=l(()=>te.value?Re.value?"":be.value?ke.value||"":x.getAppIconUrl(te.value.bundleId):""),Ce=p(5),Ue=p(5),Ee=p(20971520),Se=p(20971520),Ae=p(null),Fe=p(null),Le=p(null),vl=p(null),ml=ie(),{previewVisible:yl,previewImagesInfo:hl,previewIndex:gl,preloadPostImages:fl,handleImageClick:wl,closePreview:kl}=ml,Rl=B({maxImageCount:Ce.value,maxFileCount:Ue.value,maxImageSize:Ee.value,maxFileSize:Se.value}),{loadPost:bl}=function(e,l,t,a,s,i,n,r,o,u,d,c,p,v,m,y,h,g){const{queryUsersOnlineStatus:f}=ae()
return{loadPost:async()=>{var w,k,R,b
if(!l.value||!e.value)return h.value="无效的帖子ID或应用ID",void(y.value=!1)
y.value=!0,h.value=""
try{const R=await x.getForumByBundleId(e.value)
if(!R||"builtin"!==R.type&&"created"!==R.type){R&&(o.value=R.maxImageCount||5,u.value=R.maxFileCount||5,d.value=R.maxImageSize||20971520,c.value=R.maxFileSize||20971520)
const l=await(async e=>{try{return await x.getForumPermission(e)||{bundleId:e,appName:"",enabled:!1,postCount:0,createdAt:(new Date).toISOString(),updatedAt:(new Date).toISOString()}}catch(l){return{bundleId:e,appName:"",enabled:!1,postCount:0,createdAt:(new Date).toISOString(),updatedAt:(new Date).toISOString()}}})(e.value)
if(!l||!l.enabled)return h.value="该讨论区暂未开放,请等待管理员启用",void(y.value=!1)
try{a.value=await x.getApp(e.value),n.value=!1,r.value=!1}catch(I){}}else if(s.value=R,a.value={bundleId:R.bundleId,name:R.name,version:"",path:"",status:"unsupported",injection:null,update:null,hasIcon:!1},o.value=R.maxImageCount||5,u.value=R.maxFileCount||5,d.value=R.maxImageSize||20971520,c.value=R.maxFileSize||20971520,"created"===R.type&&R.iconId)try{const e=await x.getIcon(R.iconId)
i.value=e,n.value=!0,r.value=!1}catch(I){r.value=!0,n.value=!0}else n.value=!0,r.value=!1
try{const a=await x.getForumPost(e.value,l.value,p.value,v.value)
if(a){if(a.author&&void 0!==a.author.role&&(a.author.role=Pe(a.author.role)),a.replies&&Array.isArray(a.replies)&&a.replies.forEach(e=>{De(e)}),t.value=a,void 0!==a.replyTotal&&(m.value=a.replyTotal),void 0!==a.replyPage&&(p.value=a.replyPage),a.attachments&&g(a.attachments).catch(e=>{}),a.replies)for(const e of a.replies)if(e.attachments&&g(e.attachments).catch(e=>{}),e.nestedReplies)for(const l of e.nestedReplies)l.attachments&&g(l.attachments).catch(e=>{})
if(a&&t.value){const e=[],l=ee(t.value)
l&&e.push(l),a.replies&&Array.isArray(a.replies)&&a.replies.forEach(l=>{l.replyUser&&l.replyUser.id&&e.push(l.replyUser.id),l.nestedReplies&&Array.isArray(l.nestedReplies)&&l.nestedReplies.forEach(l=>{l.replyUser&&l.replyUser.id&&e.push(l.replyUser.id)})})
const s=Array.from(new Set(e)).filter(e=>e)
s.length>0&&f(s).catch(e=>{})}}else h.value="帖子不存在"}catch(I){const e=(null==(k=null==(w=I.response)?void 0:w.data)?void 0:k.message)||I.message||"加载帖子失败"
e.includes("锁定")||e.includes("无法查看")?h.value="帖子已锁定,无法查看":h.value=e,t.value=null}}catch(I){const e=(null==(b=null==(R=I.response)?void 0:R.data)?void 0:b.message)||I.message||"加载帖子失败"
e.includes("锁定")||e.includes("无法查看")?h.value="帖子已锁定,无法查看":h.value=e,t.value=null}finally{y.value=!1}}}}(pe,me,K,te,we,ke,de,ce,Ce,Ue,Ee,Se,ye,he,ge,oe,ue,fl),{handleLike:Il,handleDislike:Nl}=ze(pe,me,K),{showPostEditDialog:Cl,postEditData:Ul,postEditLoading:El,handleEditPost:Sl,handleDeletePost:Al,handlePostEditConfirm:Fl,handlePostEditCancel:Ll}=function(e,l,t,a,s){const i=k(),n=F(),r=L(),o=p(!1),u=p(null),d=p(!1)
return{showPostEditDialog:o,postEditData:u,postEditLoading:d,handleEditPost:()=>{if(!t.value)return
d.value=!0,o.value=!0
const l=t.value.author,a="object"==typeof l&&null!==l?l:null
u.value={id:t.value.id,bundleId:e.value,title:t.value.title,content:t.value.content,attachments:(t.value.attachments||[]).map(e=>({id:e.id,name:e.name,type:"image"===e.type?"image":"file",size:e.size})),author:a?{id:a.id||"",name:a.nickName||a.username||"",avatar:a.avatar}:void 0},d.value=!1},handleDeletePost:async()=>{var a,o
if(t.value&&e.value&&l.value&&await s.show({title:"删除帖子",message:`确定要删除帖子 "${t.value.title}" 吗?删除后无法恢复。`,type:"danger",confirmText:"删除",cancelText:"取消"})){r.show("删除中...")
try{const t=await x.deleteForumPost(e.value,l.value)
t.success?(n.success("帖子删除成功",2e3),i.push(`/discussion/${e.value}`)):n.error(t.message||"删除帖子失败",3e3)}catch(u){n.error((null==(o=null==(a=u.response)?void 0:a.data)?void 0:o.message)||u.message||"删除帖子失败",3e3)}finally{r.hide()}}},handlePostEditConfirm:async s=>{var i,d
if(t.value&&e.value&&l.value){r.show("保存中...")
try{const t=await x.updateForumPost(e.value,l.value,s.title,s.content,s.attachmentIds)
t.success?(n.success("帖子更新成功",2e3),o.value=!1,u.value=null,await a()):n.error(t.message||"更新帖子失败",3e3)}catch(c){n.error((null==(d=null==(i=c.response)?void 0:i.data)?void 0:d.message)||c.message||"更新帖子失败",3e3)}finally{r.hide()}}},handlePostEditCancel:()=>{o.value=!1,u.value=null}}}(pe,me,K,bl,S),{handleAttachmentClick:xl}={handleAttachmentClick:async e=>{if("image"===e.type)return
const l=await x.getAttachmentDownloadUrl(e.id)
if(!l)return
const t=document.createElement("a")
t.href=l.url,t.download=l.name,t.target="_blank",document.body.appendChild(t),t.click(),document.body.removeChild(t)}},Tl=p(null),Pl=function(e,l){const t=F(),a=L(),s=p(!1),i=p(null)
return{showReportDialog:s,reportInfo:i,handleReportPost:e=>{i.value={userId:ee(e),userName:Z(e),content:e.content,type:"post",postId:e.id},s.value=!0},handleReportReply:e=>{i.value={userId:e.replyUser.id,userName:e.replyUser.nickName||e.replyUser.username,content:e.content,type:"reply",postId:l,replyId:e.id},s.value=!0},handleReportNestedReply:e=>{i.value={userId:e.replyUser.id,userName:e.replyUser.nickName||e.replyUser.username,content:e.content,type:"nestedReply",postId:l,replyId:e.id},s.value=!0},handleReportSubmit:async(i,n,r)=>{var o,u
if(e&&l&&r.type){a.show("提交举报中...")
try{let a=""
if("post"===r.type?a=r.postId||l:("reply"===r.type||"nestedReply"===r.type)&&(a=r.replyId||""),!a)return
const o=await x.submitReport(r.type,a,e,l,i,r.content,n||void 0)
o.success?(s.value=!1,t.success("举报提交成功")):t.error(o.message||"举报失败")}catch(d){t.error((null==(u=null==(o=d.response)?void 0:o.data)?void 0:u.message)||d.message||"举报失败")}finally{a.hide()}}}}}(pe.value,me.value),{showReportDialog:Dl,reportInfo:zl,handleReportPost:_l,handleReportReply:Ml,handleReportNestedReply:$l,handleReportSubmit:jl}=Pl,Bl=async()=>{if(pe.value&&me.value&&K.value)try{const e=await x.getForumPost(pe.value,me.value,ye.value,he.value)
if(e){e.replies&&Array.isArray(e.replies)&&e.replies.forEach(e=>{De(e)}),K.value.replies=e.replies
const l=[],t=ee(K.value)
t&&l.push(t),e.replies&&Array.isArray(e.replies)&&e.replies.forEach(e=>{e.replyUser&&e.replyUser.id&&l.push(e.replyUser.id),e.nestedReplies&&Array.isArray(e.nestedReplies)&&e.nestedReplies.forEach(e=>{e.replyUser&&e.replyUser.id&&l.push(e.replyUser.id)})})
const a=Array.from(new Set(l)).filter(e=>e)
if(a.length>0&&J(a).catch(e=>{}),K.value.replyCount=e.replyCount,void 0!==e.replyTotal&&(ge.value=e.replyTotal),void 0!==e.replyPage&&(ye.value=e.replyPage),e.replies)for(const s of e.replies)if(s.attachments&&fl(s.attachments).catch(e=>{}),s.nestedReplies)for(const e of s.nestedReplies)e.attachments&&fl(e.attachments).catch(e=>{})}}catch(e){}},Ol=function(e,l,t){const a=F(),s=L(),i=p(null),n=p(""),r=p([]),o=p([]),u=p([]),d=p(null),c=p(null),v=p(null),m=p(null),y=p(""),h=p([]),g=p([]),f=p([]),w=p(null),k=p(null),R=()=>{i.value=null,n.value="",r.value=[],o.value=[],u.value=[]},b=()=>{v.value=null,m.value=null,y.value="",h.value=[],g.value=[],f.value=[]}
return{editingReplyId:i,editingReplyContent:n,editingReplyAttachments:r,editingReplyNewAttachments:o,editingReplyNewFiles:u,editReplyImageInput:d,editReplyFileInput:c,handleEditReply:e=>{i.value=e.id,n.value=e.content,r.value=(e.attachments||[]).map(e=>({id:e.id,name:e.name,type:"image"===e.type?"image":"file",size:e.size})),o.value=[],u.value=[]},cancelEditReply:R,handleEditReplyImageSelect:e=>{const l=e.target,t=Array.from(l.files||[])
for(const s of t){if(o.value.length>=3){a.error("图片数量不能超过3张")
break}if(s.size>10485760){a.error("单张图片大小不能超过10MB")
continue}const e=new FileReader
e.onload=e=>{var l
const t=o.value.findIndex(e=>e.file===s)
t>=0&&(o.value[t].preview=null==(l=e.target)?void 0:l.result)},e.readAsDataURL(s),o.value.push({file:s,preview:""})}l.value=""},handleEditReplyFileSelect:e=>{const l=e.target,t=Array.from(l.files||[])
for(const s of t){if(u.value.length>=3){a.error("文件数量不能超过3个")
break}s.size>20971520?a.error("单个文件大小不能超过20MB"):u.value.push({file:s})}l.value=""},handleSaveReply:async i=>{var d,c
if(e&&l&&n.value.trim()){s.show("保存中...")
try{const d=[...r.value.map(e=>e.id)]
for(const e of o.value){s.show(`上传图片中... (${o.value.indexOf(e)+1}/${o.value.length})`)
const l=await x.uploadImageAttachment(e.file)
if(!l.success||!l.attachmentId)throw new Error(l.message||`图片上传失败: ${e.file.name}`)
d.push(l.attachmentId)}for(const e of u.value){s.show(`上传文件中... (${u.value.indexOf(e)+1}/${u.value.length})`)
const l=await x.uploadFileAttachment(e.file)
if(!l.success||!l.attachmentId)throw new Error(l.message||`文件上传失败: ${e.file.name}`)
d.push(l.attachmentId)}s.show("更新回复中...")
const c=await x.updateForumReply(e,l,i,n.value.trim(),d.length>0?d:void 0)
c.success?(a.success("回复更新成功",2e3),R(),await t()):a.error(c.message||"更新回复失败",3e3)}catch(p){a.error(p.message||(null==(c=null==(d=p.response)?void 0:d.data)?void 0:c.message)||"更新回复失败",3e3)}finally{s.hide()}}else a.error("回复内容不能为空",2e3)},editingNestedReplyId:v,editingNestedReplyParentId:m,editingNestedReplyContent:y,editingNestedReplyAttachments:h,editingNestedReplyNewAttachments:g,editingNestedReplyNewFiles:f,editNestedReplyImageInput:w,editNestedReplyFileInput:k,handleEditNestedReply:(e,l)=>{v.value=l.id,m.value=e,y.value=l.content,h.value=(l.attachments||[]).map(e=>({id:e.id,name:e.name,type:"image"===e.type?"image":"file",size:e.size})),g.value=[],f.value=[]},cancelEditNestedReply:b,handleEditNestedReplyImageSelect:e=>{const l=e.target,t=Array.from(l.files||[])
for(const s of t){if(g.value.length>=3){a.error("图片数量不能超过3张")
break}if(s.size>10485760){a.error("单张图片大小不能超过10MB")
continue}const e=new FileReader
e.onload=e=>{var l
const t=g.value.findIndex(e=>e.file===s)
t>=0&&(g.value[t].preview=null==(l=e.target)?void 0:l.result)},e.readAsDataURL(s),g.value.push({file:s,preview:""})}l.value=""},handleEditNestedReplyFileSelect:e=>{const l=e.target,t=Array.from(l.files||[])
for(const s of t){if(f.value.length>=3){a.error("文件数量不能超过3个")
break}s.size>20971520?a.error("单个文件大小不能超过20MB"):f.value.push({file:s})}l.value=""},handleSaveNestedReply:async i=>{var n,r
if(e&&l&&m.value&&y.value.trim()){s.show("保存中...")
try{const n=[...h.value.map(e=>e.id)]
for(const e of g.value){s.show(`上传图片中... (${g.value.indexOf(e)+1}/${g.value.length})`)
const l=await x.uploadImageAttachment(e.file)
if(!l.success||!l.attachmentId)throw new Error(l.message||`图片上传失败: ${e.file.name}`)
n.push(l.attachmentId)}for(const e of f.value){s.show(`上传文件中... (${f.value.indexOf(e)+1}/${f.value.length})`)
const l=await x.uploadFileAttachment(e.file)
if(!l.success||!l.attachmentId)throw new Error(l.message||`文件上传失败: ${e.file.name}`)
n.push(l.attachmentId)}s.show("更新回复中...")
const r=await x.updateForumReply(e,l,i,y.value.trim(),n.length>0?n:void 0)
r.success?(a.success("回复更新成功",2e3),b(),await t()):a.error(r.message||"更新回复失败",3e3)}catch(o){a.error(o.message||(null==(r=null==(n=o.response)?void 0:n.data)?void 0:r.message)||"更新回复失败",3e3)}finally{s.hide()}}else a.error("回复内容不能为空",2e3)}}}(pe.value,me.value,Bl),{editingReplyId:ql,editingReplyContent:Hl,editingReplyAttachments:Yl,editingReplyNewAttachments:Vl,editingReplyNewFiles:Wl,handleEditReply:Ql,cancelEditReply:Gl,handleSaveReply:Jl,handleEditReplyImageSelect:Kl,handleEditReplyFileSelect:Xl,editingNestedReplyId:Zl,editingNestedReplyContent:et,editingNestedReplyAttachments:lt,editingNestedReplyNewAttachments:tt,editingNestedReplyNewFiles:at,handleEditNestedReply:st,cancelEditNestedReply:it,handleSaveNestedReply:nt,handleEditNestedReplyImageSelect:rt,handleEditNestedReplyFileSelect:ot}=Ol,ut=function(e,l,t,a,s){const i=F(),n=L(),r=s||T(),o=p(!1),u=p(!1),d=()=>e(),c=()=>l()
return{isSubmittingReply:o,isSubmittingNestedReply:u,submitReply:async e=>{const l=e||""
if((l.trim()||a.canSubmit.value)&&a.validateAttachments()){o.value=!0,n.show("发表回复中...")
try{const e=await a.uploadAttachments(),s=await x.createForumReply(d(),c(),l.trim(),void 0,void 0,e.length>0?e:void 0)
s.success?(await t(),a.clearAttachments(),i.success("回复成功"),setTimeout(()=>{const e=document.querySelector(".replies-section")
e&&e.scrollIntoView({behavior:"smooth",block:"start"})},100)):i.error(s.message||"回复失败")}catch(s){i.error(s.message||"回复失败")}finally{o.value=!1,n.hide()}}},submitNestedReply:async(e,l,a,s,r)=>{if(l.trim()||r.canSubmit.value){u.value=!0,n.show("发送回复中...")
try{const n=await r.uploadAttachments(),o=await x.createNestedReply(d(),c(),e,l.trim(),a,s,n.length>0?n:void 0)
o.success?(await t(),r.clearAttachments(),i.success("回复成功"),setTimeout(()=>{const l=document.querySelector(`[data-reply-id="${e}"]`)
l&&l.scrollIntoView({behavior:"smooth",block:"nearest"})},100)):i.error(o.message||"回复失败")}catch(o){i.error(o.message||"回复失败")}finally{u.value=!1,n.hide()}}},deleteReply:async e=>{var l,a
const s=d(),o=c()
if(s&&o){if(await r.show({title:"删除回复",message:"确定要删除这条回复吗?删除后无法恢复。",type:"danger",confirmText:"删除",cancelText:"取消"})){n.show("删除中...")
try{const l=await x.deleteForumReply(s,o,e)
l.success?(i.success("回复删除成功",2e3),await t()):i.error(l.message||"删除回复失败",3e3)}catch(u){i.error((null==(a=null==(l=u.response)?void 0:l.data)?void 0:a.message)||u.message||"删除回复失败",3e3)}finally{n.hide()}}}else i.error("参数错误,无法删除回复",2e3)},deleteNestedReply:async e=>{var l,a
const s=d(),o=c()
if(s&&o&&await r.show({title:"删除回复",message:"确定要删除这条回复吗?删除后无法恢复。",type:"danger",confirmText:"删除",cancelText:"取消"})){n.show("删除中...")
try{const l=await x.deleteForumReply(s,o,e)
l.success?(i.success("回复删除成功",2e3),await t()):i.error(l.message||"删除回复失败",3e3)}catch(u){i.error((null==(a=null==(l=u.response)?void 0:l.data)?void 0:a.message)||u.message||"删除回复失败",3e3)}finally{n.hide()}}}}}(()=>pe.value,()=>me.value,Bl,Rl,S),{isSubmittingReply:dt,isSubmittingNestedReply:ct,submitReply:pt,submitNestedReply:vt,deleteReply:mt,deleteNestedReply:yt}=ut,ht=e=>{Vl.value.splice(e,1)},gt=e=>{Wl.value.splice(e,1)},ft=e=>{tt.value.splice(e,1)},wt=e=>{at.value.splice(e,1)},kt=(e,l)=>{yt(l)},Rt=(e,l,t,a)=>{var s,i
if(a)Fe.value=Fe.value===a?null:a,Ae.value=Fe.value===a?e:null,Fe.value===a?(Le.value=l||null,vl.value=t||null):(Le.value=null,vl.value=null)
else if(Ae.value=Ae.value===e?null:e,Fe.value=null,Ae.value===e){const l=null==(i=null==(s=K.value)?void 0:s.replies)?void 0:i.find(l=>l.id===e)
l&&(Le.value=l.replyUser.id,vl.value=l.replyUser.nickName||l.replyUser.username)}else Le.value=null,vl.value=null},bt=()=>{Ae.value=null,Fe.value=null,Le.value=null,vl.value=null},It=e=>{ye.value=e,bl()},Nt=se(),{showUserMenu:Ct,userMenuPosition:Ut,selectedUserId:Et,selectedUserName:St,selectedUserUsername:At,userAvatarId:Ft,handleAuthorHover:Lt,handleAuthorLeave:xt,handleCardMouseEnter:Tt,handleCloseUserMenu:Pt}=Nt,Dt=()=>{y.push(`/discussion/${pe.value}`)},zt=()=>{y.push("/")},_t=X,Mt=()=>{K.value&&_l(K.value)},$t=(e,l)=>{var t,a
if(!K.value)return
const s=null==(t=K.value.replies)?void 0:t.find(l=>l.id===e)
if(!s)return
const i=null==(a=s.nestedReplies)?void 0:a.find(e=>e.id===l)
i&&$l(i)},jt=p(null),Bt=async(e,l)=>{var t
if(!K.value)return
const a=null==(t=K.value.replies)?void 0:t.find(l=>l.id===e)
if(!a||!a.nestedReplies)return
const s=a.nestedReplies.find(e=>e.id===l)
if(!s)return
jt.value=l
const i=p(s),{handleLike:n}=ze(pe,me,i,l)
await n(),jt.value=null},Ot=async(e,l)=>{var t
if(!K.value)return
const a=null==(t=K.value.replies)?void 0:t.find(l=>l.id===e)
if(!a||!a.nestedReplies)return
const s=a.nestedReplies.find(e=>e.id===l)
if(!s)return
jt.value=l
const i=p(s),{handleDislike:n}=ze(pe,me,i,l)
await n(),jt.value=null},qt=l(()=>!(!W.value||!K.value)&&ee(K.value)===W.value._id),Ht=e=>!(!W.value||!e.replyUser)&&("object"==typeof e.replyUser&&null!==e.replyUser?e.replyUser.id:"string"==typeof e.replyUser?e.replyUser:"")===W.value._id,Yt=(e,l)=>{y.push(`/messages?userId=${e}&userName=${encodeURIComponent(l)}`)},Vt=(e,l)=>{}
return function(e,l,t,a){const s=k(),i=F(),{subscribeForum:n,unsubscribeForum:r,subscribePost:o,unsubscribePost:u,isConnected:d}=P(),{queryUsersOnlineStatus:c}=ae(),m=p(null),y=p(null),h=p(!1),g=p(!1),f=e=>{var s
const i=e.detail
if(!i||!i.reply||!i.postId)return
const n=i.reply
if(i.postId===l&&t.value){if(t.value.replies&&t.value.replies.findIndex(e=>e.id===n.id)>=0)return
var r;(r=n).replyUser&&void 0!==r.replyUser.role&&(r.replyUser.role=Te(r.replyUser.role)),r.replyToUser&&void 0!==r.replyToUser.role&&(r.replyToUser.role=Te(r.replyToUser.role)),r.nestedReplies&&Array.isArray(r.nestedReplies)&&r.nestedReplies.forEach(e=>{e.replyUser&&void 0!==e.replyUser.role&&(e.replyUser.role=Te(e.replyUser.role)),e.replyToUser&&void 0!==e.replyToUser.role&&(e.replyToUser.role=Te(e.replyToUser.role))}),n.isNew=!0,t.value.replies||(t.value.replies=[]),t.value.replies.push(n),setTimeout(()=>{var e,l
const a=null==(l=null==(e=t.value)?void 0:e.replies)?void 0:l.find(e=>e.id===n.id)
a&&(a.isNew=!1)},3e3),a.value=(a.value||0)+1,(null==(s=n.replyUser)?void 0:s.id)&&c([n.replyUser.id]).catch(e=>{})}},w=a=>{const n=a.detail
if(!n||!n.postId||!n.bundleId)return
const r=n.postId
n.bundleId===e&&r===l&&t.value&&(i.info("帖子已被删除",2e3),s.push(`/discussion/${e}`))},I=s=>{const i=s.detail
if(!(i&&i.replyId&&i.postId&&i.bundleId))return
const n=i.replyId,r=i.postId
if(i.bundleId===e&&r===l&&t.value){if(t.value.replies){const e=t.value.replies.findIndex(e=>e.id===n)
if(e>=0)return t.value.replies.splice(e,1),void(a.value=Math.max(0,a.value-1))
for(const l of t.value.replies)if(l.nestedReplies){const e=l.nestedReplies.findIndex(e=>e.id===n)
if(e>=0)return l.nestedReplies.splice(e,1),void(a.value=Math.max(0,a.value-1))}}a.value=Math.max(0,a.value-1)}},N=async()=>{if(e&&m.value!==e&&!h.value&&d.value){h.value=!0
try{await n(e),m.value=e}catch(ue){}finally{h.value=!1}}},C=async()=>{if(m.value)try{await r(m.value),m.value=null}catch(ue){}},U=async()=>{if(l&&y.value!==l&&!g.value&&d.value){g.value=!0
try{await o(l),y.value=l}catch(ue){}finally{g.value=!1}}},E=async()=>{if(y.value)try{await u(y.value),y.value=null}catch(ue){}},S=()=>{m.value=null,y.value=null,h.value=!1,g.value=!1},A=()=>{m.value&&d.value}
v(()=>d.value,t=>{t&&setTimeout(()=>{d.value&&(e&&N(),l&&U())},500)}),R(()=>{window.addEventListener(D.NEW_REPLY,f),window.addEventListener(D.DELETE_POST,w),window.addEventListener(D.DELETE_REPLY,I),window.addEventListener(D.DISCONNECTED,S),window.addEventListener("beforeunload",A),d.value&&(e&&N(),l&&U())}),b(async()=>{if(m.value)try{await Promise.race([C(),new Promise(e=>setTimeout(e,2e3))])}catch(ue){}if(y.value)try{await Promise.race([E(),new Promise(e=>setTimeout(e,2e3))])}catch(ue){}window.removeEventListener(D.NEW_REPLY,f),window.removeEventListener(D.DELETE_POST,w),window.removeEventListener(D.DELETE_REPLY,I),window.removeEventListener(D.DISCONNECTED,S),window.removeEventListener("beforeunload",A)})}(pe.value,me.value,K,ge),R(()=>{bl()}),b(async()=>{}),(e,l)=>{var v,y,h
return c(),t("div",_e,[o("div",Me,[o("div",$e,[o("div",je,[o("div",Be,[o("button",{class:"back-button",onClick:Dt},[s(E,{icon:"mdi:arrow-left",class:"back-icon"}),l[20]||(l[20]=o("span",null,"返回讨论区",-1))]),o("button",{class:"home-button",onClick:zt},[s(E,{icon:"fluent-color:home-48",class:"home-icon"}),l[21]||(l[21]=o("span",null,"首页",-1))])])]),oe.value?(c(),t("div",Oe,[...l[22]||(l[22]=[o("div",{class:"spinner"},null,-1),o("p",null,"加载中...",-1)])])):ue.value?(c(),t("div",qe,[o("p",null,u(ue.value),1),o("button",{onClick:l[0]||(l[0]=(...e)=>d(bl)&&d(bl)(...e)),class:"btn btn-primary"},"重试")])):K.value?(c(),t("div",He,[te.value?(c(),t("div",Ye,[o("div",{class:"forum-info",onClick:Dt},[o("div",Ve,[o("div",We,[!Ne.value||Re.value||be.value?a("",!0):N((c(),t("img",{key:0,src:Ne.value,alt:te.value.name,onLoad:l[1]||(l[1]=e=>de.value=!0),onError:l[2]||(l[2]=e=>ce.value=!0)},null,40,Qe)),[[C,de.value&&!ce.value]]),be.value&&ke.value?N((c(),t("img",{key:1,src:ke.value,alt:te.value.name,onLoad:l[3]||(l[3]=e=>de.value=!0),onError:l[4]||(l[4]=e=>ce.value=!0)},null,40,Ge)),[[C,de.value&&!ce.value]]):a("",!0),Re.value?(c(),m(E,{key:2,icon:Ie.value,class:"builtin-forum-icon"},null,8,["icon"])):a("",!0),N(o("div",{class:"icon-placeholder"},u(te.value.name.charAt(0).toUpperCase()),513),[[C,(!Ne.value||!de.value||ce.value)&&!Re.value&&!be.value]])])]),o("span",Je,u(te.value.name),1),s(E,{icon:"mdi:chevron-right",class:"breadcrumb-separator"})]),o("span",Ke,u(K.value.title),1)])):a("",!0),o("div",Xe,[o("div",Ze,[o("h1",el,u(K.value.title),1),o("div",ll,[o("div",{class:"author-wrapper",onMouseenter:l[5]||(l[5]=f(e=>d(Lt)(e,d(ee)(K.value),d(Z)(K.value),d(le)(K.value)||"",void 0),["stop"])),onMouseleave:l[6]||(l[6]=f((...e)=>d(xt)&&d(xt)(...e),["stop"]))},[o("div",tl,[s(A,{userId:d(ee)(K.value),width:"36px",height:"36px"},null,8,["userId"]),o("div",{class:r(["online-indicator",{online:!0===re.value,offline:!1===re.value}])},null,2)]),o("span",al,u(d(Z)(K.value)),1)],32),o("span",sl,u(d(_t)(K.value.publishTime)),1)])]),o("div",il,[o("p",null,u(K.value.content),1),K.value&&K.value.attachments&&K.value.attachments.length>0?(c(),m(ve,{key:0,attachments:K.value.attachments||[],type:"post",onImageClick:l[7]||(l[7]=e=>{var l
return d(wl)((null==(l=K.value)?void 0:l.attachments)||[],e)}),onFileClick:d(xl)},null,8,["attachments","onFileClick"])):a("",!0)]),o("div",nl,[o("button",{class:r(["action-btn",{active:"liked"===K.value.userLikeStatus}]),onClick:l[8]||(l[8]=(...e)=>d(Il)&&d(Il)(...e)),disabled:d(w).loadingState.value.visible},[s(E,{icon:"mdi:thumb-up",class:"action-icon"}),o("span",null,u(K.value.likeCount),1)],10,rl),o("button",{class:r(["action-btn",{active:"disliked"===K.value.userLikeStatus}]),onClick:l[9]||(l[9]=(...e)=>d(Nl)&&d(Nl)(...e)),disabled:d(w).loadingState.value.visible},[s(E,{icon:"mdi:thumb-down",class:"action-icon"}),o("span",null,u(K.value.dislikeCount),1)],10,ol),qt.value||d(Q)?(c(),t("button",{key:0,class:"action-btn edit-btn",onClick:l[10]||(l[10]=f((...e)=>d(Sl)&&d(Sl)(...e),["stop"]))},[s(E,{icon:"mdi:pencil",class:"action-icon"}),l[23]||(l[23]=o("span",null,"编辑",-1))])):a("",!0),qt.value||d(Q)?(c(),t("button",{key:1,class:"action-btn delete-btn",onClick:l[11]||(l[11]=f((...e)=>d(Al)&&d(Al)(...e),["stop"]))},[s(E,{icon:"mdi:delete-outline",class:"action-icon"}),l[24]||(l[24]=o("span",null,"删除",-1))])):a("",!0),o("button",{class:"action-btn report-btn",onClick:Mt},[s(E,{icon:"mdi:flag-outline",class:"action-icon"}),l[25]||(l[25]=o("span",null,"举报",-1))])])]),o("div",ul,[o("div",dl,[o("h2",null,"回帖 ("+u(K.value.replyCount)+")",1)]),s(j,{ref_key:"replyFormRef",ref:Tl,"user-id":null==(v=d(W))?void 0:v._id,"user-name":(null==(y=d(W))?void 0:y.nickName)||(null==(h=d(W))?void 0:h.username),"max-image-count":Ce.value,"max-file-count":Ue.value,"max-image-size":Ee.value,"max-file-size":Se.value,"is-submitting":d(dt),onSubmit:l[12]||(l[12]=(e,l,t)=>(async(e,l,t)=>{l&&l.length>0&&(Rl.selectedImages.value=l),t&&t.length>0&&(Rl.selectedFiles.value=t),await pt(e||""),Tl.value&&Tl.value.clear()})(e,l,t))},null,8,["user-id","user-name","max-image-count","max-file-count","max-image-size","max-file-size","is-submitting"]),K.value.replies&&K.value.replies.length>0?(c(),t("div",cl,[(c(!0),t(i,null,n(K.value.replies,e=>{var t,a,s
return c(),m(xe,{key:e.id,reply:e,"is-author":Ht(e),"is-admin":d(Q),"user-id":null==(t=d(W))?void 0:t._id,"user-name":(null==(a=d(W))?void 0:a.nickName)||(null==(s=d(W))?void 0:s.username),"is-new":e.isNew,"is-editing":d(ql)===e.id,"editing-content":d(Hl),"editing-attachments":d(Yl),"editing-new-images":d(Vl),"editing-new-files":d(Wl),"show-nested-form":Ae.value===e.id,"replying-to-nested-reply-id":Fe.value,"nested-reply-to-user-name":vl.value||e.replyUser.nickName||e.replyUser.username,"is-submitting-nested-reply":d(ct),"editing-nested-reply-id":d(Zl),"editing-nested-reply-content":d(et),"editing-nested-reply-attachments":d(lt),"editing-nested-reply-new-attachments":d(tt),"editing-nested-reply-new-files":d(at),onEdit:l=>d(Ql)(e),onSave:l=>d(Jl)(e.id),onCancel:d(Gl),onDelete:l=>{return t=e.id,void(pe.value&&me.value?mt(t):g.error("参数错误,无法删除回复",2e3))
var t},onReport:l=>(e=>{var l
if(!K.value)return
const t=null==(l=K.value.replies)?void 0:l.find(l=>l.id===e)
t&&Ml(t)})(e.id),onReply:l=>Rt(e.id),onLike:l=>(async e=>{var l
if(!K.value)return
const t=null==(l=K.value.replies)?void 0:l.find(l=>l.id===e)
if(!t)return
jt.value=e
const a=p(t),{handleLike:s}=ze(pe,me,a,e)
await s(),jt.value=null})(e.id),onDislike:l=>(async e=>{var l
if(!K.value)return
const t=null==(l=K.value.replies)?void 0:l.find(l=>l.id===e)
if(!t)return
jt.value=e
const a=p(t),{handleDislike:s}=ze(pe,me,a,e)
await s(),jt.value=null})(e.id),"like-loading":jt.value===e.id?e.id:jt.value,onNestedSubmit:(l,t,a)=>(async(e,l,t,a)=>{const s=B({maxImageCount:3,maxFileCount:3,maxImageSize:10485760,maxFileSize:20971520})
s.selectedImages.value=t,s.selectedFiles.value=a,await vt(e,l,Le.value||void 0,vl.value||void 0,s),bt()})(e.id,l,t,a),onNestedCancel:bt,onNestedEdit:d(st),onNestedSave:d(nt),onNestedEditCancel:d(it),onNestedDelete:kt,onNestedReport:$t,onNestedReplyClick:l[13]||(l[13]=(e,l,t,a)=>Rt(e,l,t,a)),onNestedLike:Bt,onNestedDislike:Ot,onAuthorHover:d(Lt),onAuthorLeave:d(xt),onImageClick:l=>d(wl)(e.attachments||[],l),onFileClick:d(xl),onNestedImageClick:l[14]||(l[14]=(e,l)=>d(wl)(e.attachments||[],l)),onRemoveAttachment:l[15]||(l[15]=e=>Yl.value=d(Yl).filter(l=>l.id!==e)),onRemoveNewImage:ht,onRemoveNewFile:gt,onEditImageSelect:d(Kl),onEditFileSelect:d(Xl),onNestedRemoveAttachment:l[16]||(l[16]=e=>lt.value=d(lt).filter(l=>l.id!==e)),onNestedRemoveNewImage:ft,onNestedRemoveNewFile:wt,onNestedEditImageSelect:d(rt),onNestedEditFileSelect:d(ot)},null,8,["reply","is-author","is-admin","user-id","user-name","is-new","is-editing","editing-content","editing-attachments","editing-new-images","editing-new-files","show-nested-form","replying-to-nested-reply-id","nested-reply-to-user-name","is-submitting-nested-reply","editing-nested-reply-id","editing-nested-reply-content","editing-nested-reply-attachments","editing-nested-reply-new-attachments","editing-nested-reply-new-files","onEdit","onSave","onCancel","onDelete","onReport","onReply","onLike","onDislike","like-loading","onNestedSubmit","onNestedEdit","onNestedSave","onNestedEditCancel","onAuthorHover","onAuthorLeave","onImageClick","onFileClick","onEditImageSelect","onEditFileSelect","onNestedEditImageSelect","onNestedEditFileSelect"])}),128))])):a("",!0),K.value.replies&&K.value.replies.length>0&&fe.value>1?(c(),m(Y,{key:1,"current-page":ye.value,"total-pages":fe.value,total:ge.value,onPageChange:It},null,8,["current-page","total-pages","total"])):a("",!0),K.value.replies&&0!==K.value.replies.length?a("",!0):(c(),t("div",pl,[...l[26]||(l[26]=[o("p",null,"还没有回帖,快来抢沙发吧!",-1)])]))])])):a("",!0)])]),s(H,{visible:d(yl),"images-info":d(hl),"initial-index":d(gl),"show-navigation":!0,"show-info":!0,onClose:d(kl),onIndexChange:l[17]||(l[17]=e=>gl.value=e)},null,8,["visible","images-info","initial-index","onClose"]),s(O,{visible:d(Dl),"report-info":d(zl),onClose:l[18]||(l[18]=e=>Dl.value=!1),onSubmit:d(jl)},null,8,["visible","report-info","onSubmit"]),s(V,{visible:d(Cl),"onUpdate:visible":l[19]||(l[19]=e=>U(Cl)?Cl.value=e:null),postData:d(Ul),loading:d(El),onConfirm:d(Fl),onCancel:d(Ll)},null,8,["visible","postData","loading","onConfirm","onCancel"]),s(M,{visible:d(S).visible.value,title:d(S).config.value.title||"",message:d(S).config.value.message||"",type:"danger"===d(S).config.value.type?"error":d(S).config.value.type||"warning","confirm-text":d(S).config.value.confirmText,"cancel-text":d(S).config.value.cancelText,onConfirm:d(S).handleConfirm,onCancel:d(S).handleCancel},null,8,["visible","title","message","type","confirm-text","cancel-text","onConfirm","onCancel"]),s(q,{visible:d(Ct),"user-id":d(Et),"user-name":d(St),username:d(At)||"","avatar-id":d(Ft)||void 0,position:d(Ut),onClose:d(Pt),onSendMessage:Yt,onBlockUser:Vt,onMouseEnter:d(Tt)},null,8,["visible","user-id","user-name","username","avatar-id","position","onClose","onMouseEnter"]),s($,{toasts:d(g).toasts.value,onRemove:d(g).removeToast},null,8,["toasts","onRemove"])])}}}),[["__scopeId","data-v-30aee564"]])
export{vl as default}

15
frontend/dist/assets/4xYhABhf.js vendored Normal file
View File

@@ -0,0 +1,15 @@
import{u as e,j as t,W as s,k as a}from"./BaSQ3xJt.js"
import{r as n,c as r}from"./CdD4XvnD.js"
const i=n(0),u=new Map
let o=!1
function l(){const{user:l}=e(),{queryUsersOnlineStatus:c,isConnected:v}=t()
o||(o=!0,window.addEventListener(s.USER_ONLINE_STATUS_UPDATED,e=>{const t=e,{userId:s,userIds:a,batch:n}=t.detail
i.value++}))
const U=n(new Map),d=e=>a.getUserOnlineStatus(e),S=e=>a.getUserLastActiveAt(e)
return{userOnlineStatusMap:U,getUserOnlineStatus:d,getUserLastActiveAt:S,isUserOnline:e=>{if(u.has(e))return u.get(e)
const t=r(()=>(i.value,!(!l.value||e!==l.value._id)||d(e)))
return u.set(e,t),t},isUserOnlineSync:e=>!(!l.value||e!==l.value._id)||d(e),formatLastActiveTime:e=>{const t=S(e)
if(!t)return"未知"
const s=(new Date).getTime()-t.getTime(),a=Math.floor(s/1e3),n=Math.floor(a/60),r=Math.floor(n/60),i=Math.floor(r/24)
return a<60?"刚刚":n<60?`${n}分钟前`:r<24?`${r}小时前`:i<7?`${i}天前`:t.toLocaleDateString("zh-CN")},queryUsersOnlineStatus:async e=>{var t
await a.queryUsersOnlineStatus(e,c,v.value,null==(t=l.value)?void 0:t._id)}}}export{l as u}

1
frontend/dist/assets/5GyXuXDJ.css vendored Normal file

File diff suppressed because one or more lines are too long

4
frontend/dist/assets/6K6b4Qy_.js vendored Normal file
View File

@@ -0,0 +1,4 @@
var l=(l=>(l.PENDING="pending",l.APPROVED="approved",l.REJECTED="rejected",l))(l||{})
const o={"com.hayaku.builtin.product-issues":"mdi:bug-outline","com.hayaku.builtin.chat":"mdi:chat-outline","com.hayaku.builtin.adult":"mdi:alert-circle-outline"}
function a(l){return o[l]||"mdi:forum-outline"}const i={pornography:{label:"涉黄",icon:"mdi:eye-off-outline",colorClass:"type-pornography"},fraud:{label:"诈骗",icon:"mdi:shield-alert-outline",colorClass:"type-fraud"},malicious_drainage:{label:"恶意引流",icon:"mdi:link-variant-off",colorClass:"type-drainage"},abuse:{label:"侮辱谩骂",icon:"mdi:emoticon-angry-outline",colorClass:"type-abuse"},trolling:{label:"引战",icon:"mdi:fire",colorClass:"type-trolling"},against_values:{label:"不符合社会核心主义价值观",icon:"mdi:flag-outline",colorClass:"type-values"}}
function e(l){return i[l]||{label:l,icon:"mdi:help-circle-outline",colorClass:""}}function n(l){return e(l).label}function t(l){return e(l).colorClass}export{l as F,i as R,t as a,n as b,a as g}

150
frontend/dist/assets/BFOgUDhg.js vendored Normal file
View File

@@ -0,0 +1,150 @@
import{d as e,r as a,w as t,o as n,k as i,i as o,a as l,e as r,b as s,m as u,F as d,B as c,t as m,n as p,z as v,h,c as f,g,l as y,T as b,p as w,q as k,v as M,C as I,P as A,D as C,V as D,u as W}from"./CdD4XvnD.js"
import{_ as V,a as x,A as P,U as S,d as T,C as F}from"./BaSQ3xJt.js"
const _={class:"tabs-container"},U={class:"tabs-wrapper"},E=["onClick"],X={key:0,class:"tab-badge"},N=x(e({__name:"Tabs",props:{tabs:{},activeTab:{}},emits:["update:activeTab"],setup(e){const f=e,g=a(null),y=a(!1),b=a(!1),w=()=>{if(!g.value)return
const e=g.value,a=e.scrollLeft,t=e.scrollWidth,n=e.clientWidth,i=t>n
y.value=i&&a>1,b.value=i&&a+n<t-1},k=()=>{if(!g.value)return
const e=g.value,a=.8*e.clientWidth
e.scrollBy({left:-a,behavior:"smooth"}),setTimeout(()=>{w()},300)},M=()=>{if(!g.value)return
const e=g.value,a=.8*e.clientWidth
e.scrollBy({left:a,behavior:"smooth"}),setTimeout(()=>{w()},300)},I=()=>{w()}
let A=null
return t(()=>f.tabs,()=>{i(()=>{w()})},{deep:!0}),n(()=>{i(()=>{w(),g.value&&(A=new ResizeObserver(()=>{w()}),A.observe(g.value))})}),o(()=>{A&&A.disconnect()}),(a,t)=>(h(),l("div",_,[r("div",U,[y.value?(h(),l("button",{key:0,class:"scroll-arrow left",onClick:k,type:"button"},[u(V,{icon:"mdi:chevron-left"})])):s("",!0),r("div",{ref_key:"tabsRef",ref:g,class:"tabs",onScroll:I},[(h(!0),l(d,null,c(e.tabs,t=>(h(),l("button",{key:t.id,class:p(["tab-button",{active:e.activeTab===t.id}]),onClick:m(e=>a.$emit("update:activeTab",t.id),["stop"]),type:"button"},[u(V,{icon:t.icon,class:"tab-icon"},null,8,["icon"]),r("span",null,v(t.label),1),t.badge?(h(),l("span",X,v(t.badge),1)):s("",!0)],10,E))),128))],544),b.value?(h(),l("button",{key:1,class:"scroll-arrow right",onClick:M,type:"button"},[u(V,{icon:"mdi:chevron-right"})])):s("",!0)])]))}}),[["__scopeId","data-v-e285743c"]]),j={class:"array-field"},z={class:"field-header"},Y=["onClick"],O={key:0,class:"empty-state"},R={key:1,class:"array-items"},q={key:0,class:"selected-item-label"},H=["value","onInput","placeholder"],L=["onClick"],$=x(e({__name:"ArrayField",props:{label:{},modelValue:{},placeholder:{default:""},emptyIcon:{default:"mdi:information-outline"},emptyText:{default:"暂无信息"},options:{default:void 0}},emits:["update:modelValue"],setup(e,{emit:I}){const A=e,C=I,D=a(!1),W=a(null),x=a(null),P=a({top:"0px",left:"0px",width:"200px"}),S=f(()=>A.options?A.options.filter(e=>!A.modelValue.includes(e.value)):[]),T=e=>{if(!A.options)return e
const a=A.options.find(a=>a.value===e)
return a?a.label:e},F=()=>{D.value&&W.value&&i(()=>{const e=W.value.getBoundingClientRect(),a=Math.min(48*S.value.length+16,300),t=window.innerHeight-e.bottom,n=e.top
let i=e.bottom+8,o=e.left,l=Math.max(e.width,200)
t<a&&n>t&&(i=e.top-a-8),o+l>window.innerWidth&&(o=window.innerWidth-l-16),o<16&&(o=16),i<16&&(i=16),i+a>window.innerHeight-16&&(i=window.innerHeight-a-16),P.value={top:`${i}px`,left:`${o}px`,width:`${l}px`}})},_=()=>{D.value=!D.value,D.value&&F()},U=e=>{const a=e.target
W.value&&W.value.contains(a)||x.value&&x.value.contains(a)||(D.value=!1)}
t(D,e=>{e?(F(),window.addEventListener("scroll",F,!0),window.addEventListener("resize",F)):(window.removeEventListener("scroll",F,!0),window.removeEventListener("resize",F))}),t(()=>S.value.length,()=>{D.value&&F()})
const E=()=>{const e=[...A.modelValue,""]
C("update:modelValue",e)}
return n(()=>{A.options&&document.addEventListener("click",U)}),o(()=>{A.options&&(document.removeEventListener("click",U),window.removeEventListener("scroll",F,!0),window.removeEventListener("resize",F))}),(a,t)=>(h(),l("div",j,[r("div",z,[r("label",null,v(e.label),1),e.options?(h(),l("div",{key:1,class:"select-wrapper",ref_key:"wrapperRef",ref:W},[r("button",{onClick:_,class:"btn-select"},[u(V,{icon:"mdi:plus"}),t[2]||(t[2]=g(" 选择分类 ",-1)),u(V,{icon:"fluent-color:chevron-down-24",class:p(["select-arrow",{rotated:D.value}])},null,8,["class"])]),(h(),y(b,{to:"body"},[u(w,{name:"dropdown-fade"},{default:k(()=>[D.value?(h(),l("div",{key:0,ref_key:"dropdownRef",ref:x,class:"select-dropdown",style:M(P.value),onClick:t[0]||(t[0]=m(()=>{},["stop"]))},[(h(!0),l(d,null,c(S.value,e=>(h(),l("div",{key:e.value,class:"select-option",onClick:a=>(e=>{if(!A.modelValue.includes(e)){const a=[...A.modelValue,e]
C("update:modelValue",a)}D.value=!1})(e.value)},[r("span",null,v(e.label),1)],8,Y))),128))],4)):s("",!0)]),_:1})]))],512)):(h(),l("button",{key:0,onClick:E,class:"btn-icon-text"},[u(V,{icon:"mdi:plus"}),t[1]||(t[1]=g(" 添加 ",-1))]))]),0===e.modelValue.length?(h(),l("div",O,[u(V,{icon:e.emptyIcon,class:"empty-icon"},null,8,["icon"]),r("p",null,v(e.emptyText),1)])):(h(),l("div",R,[(h(!0),l(d,null,c(e.modelValue,(a,t)=>(h(),l("div",{key:t,class:"array-item-row"},[e.options?(h(),l("span",q,v(T(a)),1)):(h(),l("input",{key:1,value:a,onInput:e=>((e,a)=>{const t=[...A.modelValue]
t[e]=a,C("update:modelValue",t)})(t,e.target.value),type:"text",placeholder:e.placeholder},null,40,H)),r("button",{onClick:e=>(e=>{const a=A.modelValue.filter((a,t)=>t!==e)
C("update:modelValue",a)})(t),class:"btn-icon-danger"},[u(V,{icon:"mdi:delete"})],8,L)]))),128))]))]))}}),[["__scopeId","data-v-26f699b0"]]),B={class:"screenshot-upload"},J={class:"upload-hint"},Q={key:1,class:"screenshots-grid"},G=["src","alt"],K=["onClick"],Z={key:0,class:"thumbnail-badge"},ee={class:"thumbnail-number"},ae=x(e({__name:"ScreenshotUpload",props:{modelValue:{},maxCount:{default:5}},emits:["update:modelValue"],setup(e,{emit:t}){const n=e,i=t,o=a(null),m=()=>{var e
null==(e=o.value)||e.click()},p=e=>{const a=e.target.files
if(!a)return
const t=n.maxCount-n.modelValue.length,l=Array.from(a).slice(0,t),r=[...n.modelValue]
l.forEach(e=>{const a=new FileReader
a.onload=a=>{var t
r.push({file:e,preview:null==(t=a.target)?void 0:t.result}),i("update:modelValue",[...r])},a.readAsDataURL(e)}),o.value&&(o.value.value="")}
return(a,t)=>(h(),l("div",B,[r("input",{ref_key:"fileInputRef",ref:o,type:"file",accept:"image/*",multiple:"",onChange:p,class:"file-input",style:{display:"none"}},null,544),0===e.modelValue.length?(h(),l("div",{key:0,class:"upload-area",onClick:m},[u(V,{icon:"mdi:image-multiple-outline",class:"upload-icon"}),t[0]||(t[0]=r("p",{class:"upload-text"},"点击选择截图",-1)),r("p",J,"最多可选择"+v(e.maxCount)+"张图片",1)])):(h(),l("div",Q,[(h(!0),l(d,null,c(e.modelValue,(e,a)=>(h(),l("div",{key:a,class:"thumbnail-box"},[r("img",{src:e.thumbnailUrl||e.preview,alt:`截图${a+1}`},null,8,G),r("button",{onClick:e=>(e=>{const a=n.modelValue.filter((a,t)=>t!==e)
i("update:modelValue",a)})(a),class:"thumbnail-remove"},[u(V,{icon:"mdi:close"})],8,K),e.fileId?(h(),l("div",Z,[u(V,{icon:"mdi:check-circle"})])):s("",!0),r("div",ee,v(a+1),1)]))),128)),e.modelValue.length<e.maxCount?(h(),l("div",{key:0,class:"thumbnail-box add-more",onClick:m},[u(V,{icon:"mdi:plus",class:"add-icon"}),t[1]||(t[1]=r("p",null,"添加更多",-1))])):s("",!0)]))]))}}),[["__scopeId","data-v-c75f593a"]]),te={class:"app-icon-upload"},ne={class:"icon-section"},ie={class:"app-icon-display"},oe=["src","alt"],le={class:"icon-upload-control"},re={key:0,class:"icon-status"},se=x(e({__name:"AppIconUpload",props:{modelValue:{},currentIconUrl:{},alt:{}},emits:["update:modelValue"],setup(e,{emit:t}){const n=e,i=t,o=a(null),d=f(()=>{var e
return(null==(e=n.modelValue)?void 0:e.preview)?n.modelValue.preview:n.currentIconUrl||""}),c=()=>{var e
null==(e=o.value)||e.click()},m=e=>{var a
const t=null==(a=e.target.files)?void 0:a[0]
if(!t)return
const n=new FileReader
n.onload=e=>{var a
const n=null==(a=e.target)?void 0:a.result
i("update:modelValue",{file:t,preview:n})},n.readAsDataURL(t)}
return(a,t)=>(h(),l("div",te,[r("div",ne,[r("div",ie,[r("img",{src:d.value,alt:e.alt||"应用图标"},null,8,oe)]),r("div",le,[r("input",{ref_key:"fileInputRef",ref:o,type:"file",accept:"image/*",onChange:m,class:"file-input",style:{display:"none"}},null,544),r("button",{onClick:c,class:"btn-icon-upload"},[u(V,{icon:"mdi:image-edit",class:"btn-icon"}),g(" "+v(e.modelValue?"更换图标":"上传图标"),1)]),e.modelValue?(h(),l("div",re,[u(V,{icon:"mdi:check-circle",class:"status-icon success"}),t[0]||(t[0]=r("span",null,"已选择图标",-1))])):s("",!0)])])]))}}),[["__scopeId","data-v-c4ff9b80"]]),ue={class:"dialog-content"},de={class:"tab-content"},ce={class:"upload-card"},me={class:"card-header"},pe={class:"card-badge"},ve={class:"card-body"},he={class:"tab-content"},fe={class:"form-card"},ge={class:"card-header"},ye={class:"card-body"},be={class:"app-header-section"},we={class:"name-section"},ke={class:"form-group"},Me={class:"form-group"},Ie={class:"form-group"},Ae={class:"form-group"},Ce={class:"checkbox-label"},De={class:"tab-content"},We={class:"form-card"},Ve={class:"card-header"},xe={class:"card-body"},Pe={key:0,class:"upload-progress-bar"},Se={class:"progress-info"},Te={class:"progress-text"},Fe={class:"progress-percent"},_e={class:"progress-bar"},Ue={key:1,class:"error-banner"},Ee={class:"dialog-footer"},Xe=["disabled"],Ne=["disabled"],je=x(e({__name:"AppInfoUploadDialog",props:{visible:{type:Boolean},app:{}},emits:["close","success"],setup(e,{emit:n}){const i=e,o=n,d=[{id:"images",label:"图片",icon:"mdi:image"},{id:"basic",label:"基本信息",icon:"mdi:information"},{id:"details",label:"详细信息",icon:"mdi:format-list-bulleted"}],c=a("images"),m=a({bundleId:"",appName:"",description:"",permissions:[],compatibility:[],architecture:[],language:[],category:[],developer:[],isDMCAed:!1}),p=a(null),b=a([]),w=a([]),x=a(!1),{fillFormFromApp:_,loadScreenshotsFromImageIds:U,loadIconFromIconId:E}={fillFormFromApp:(e,a)=>{e&&(a.value.bundleId=e.bundleId||"",a.value.appName=e.name||"",a.value.description=e.description||"",e.category&&Array.isArray(e.category)&&e.category.length>0&&(a.value.category=[...e.category]),e.developer&&(Array.isArray(e.developer)?a.value.developer=[...e.developer]:"string"==typeof e.developer&&(a.value.developer=e.developer.split(",").map(e=>e.trim()).filter(e=>e))),e.permissions&&Array.isArray(e.permissions)&&(a.value.permissions=[...e.permissions]),e.compatibility&&Array.isArray(e.compatibility)&&(a.value.compatibility=[...e.compatibility]),e.architecture&&Array.isArray(e.architecture)&&(a.value.architecture=[...e.architecture]),e.language&&Array.isArray(e.language)&&(a.value.language=[...e.language]),"boolean"==typeof e.isDMCAed&&(a.value.isDMCAed=e.isDMCAed))},loadScreenshotsFromImageIds:async e=>{if(!e||0===e.length)return[]
const a=e.map(async e=>{try{const a=await P.getImage(e)
return{preview:a,fileId:e,thumbnailUrl:a}}catch(Y){return null}})
return(await Promise.all(a)).filter(e=>null!==e)},loadIconFromIconId:async e=>{if(!e)return null
try{const a=await P.getImage(e)
return{preview:a,fileId:e,thumbnailUrl:a}}catch(Y){return null}}},{progress:X,status:j,uploading:z,error:Y,upload:O,reset:R}=function(){const e=a(!1),t=a(0),n=a(""),i=a("")
return{progress:t,status:n,uploading:e,error:i,upload:async(a,l,r,s)=>{var u,d,c
e.value=!0,t.value=0,n.value="准备上传...",i.value=""
try{let e=""
if(null==l?void 0:l.fileId)e=l.fileId,t.value=10
else if(null==l?void 0:l.file){n.value="正在上传图标...",t.value=10
const a=await P.uploadFile(S.APP_ICON,l.file)
if(!a.success||!(null==(u=a.data)?void 0:u.fileId))throw new Error(a.message||"上传图标失败")
e=a.data.fileId,t.value=30}else{if(!s)throw new Error("请先选择应用图标或确保当前应用存在")
n.value="正在获取应用图标...",t.value=5
try{const a=P.getAppIconUrl(s.bundleId),i=await fetch(a)
if(!i.ok)throw new Error("获取应用图标失败")
const o=await i.blob(),l=new File([o],`${s.bundleId}.png`,{type:o.type||"image/png"})
n.value="正在上传图标...",t.value=10
const r=await P.uploadFile(S.APP_ICON,l)
if(!r.success||!(null==(d=r.data)?void 0:d.fileId))throw new Error(r.message||"上传图标失败")
e=r.data.fileId,t.value=30}catch(m){throw new Error(`获取应用图标失败: ${m.message||"未知错误"}`)}}if(!e)throw new Error("无法获取图标ID")
n.value="正在处理截图..."
const i=[],p=(r.filter(e=>!e.fileId),r.length)
for(let a=0;a<r.length;a++){const e=r[a]
if(t.value=30+a/p*30,e.fileId)i.push(e.fileId)
else if(e.file){n.value=`正在上传截图 ${a+1}/${p}...`
const t=await P.uploadFile(S.APP_IMAGE,e.file)
t.success&&(null==(c=t.data)?void 0:c.fileId)&&i.push(t.data.fileId)}}t.value=60,n.value="正在提交应用信息..."
const v=(new Date).toISOString(),h={bundleId:a.bundleId,appName:a.appName.trim(),iconId:e,imageId:i,description:a.description.trim(),permissions:a.permissions.filter(e=>""!==e.trim()),releaseDate:v,updateDate:v,compatibility:a.compatibility.filter(e=>""!==e.trim()),architecture:a.architecture.filter(e=>""!==e.trim()),language:a.language.filter(e=>""!==e.trim()),category:a.category.filter(e=>""!==e.trim()),developer:a.developer.filter(e=>""!==e.trim()),isDMCAed:a.isDMCAed}
t.value=80
const f=await P.uploadAppInfo(h)
if(!f.success)throw new Error(f.message||"上传应用信息失败")
t.value=100,n.value="上传成功!",setTimeout(()=>{o("success"),Q()},500)}catch(m){const e=m.message||"上传失败,请重试"
throw i.value=e,m}finally{e.value=!1}},reset:()=>{e.value=!1,t.value=0,n.value="",i.value=""}}}(),q=f(()=>!z.value&&""!==m.value.appName.trim()),H=f(()=>{var e
return(null==(e=p.value)?void 0:e.preview)?p.value.preview:i.app?P.getAppIconUrl(i.app.bundleId):""})
t(()=>i.app,e=>{e&&_(e,m)},{immediate:!0})
const L=async()=>{if(!x.value){x.value=!0
try{const e=await F.getCategoryDictionaries()
e.success&&e.data&&(w.value=e.data.map(e=>({value:e.key,label:`${e.name} (${e.key})`})))}catch(e){}finally{x.value=!1}}},B=async e=>{try{const a=await P.getAppInfoFromServer(e)
a&&(a.description&&(m.value.description=a.description),a.category&&Array.isArray(a.category)&&a.category.length>0&&(m.value.category=a.category),a.developer&&(Array.isArray(a.developer)?m.value.developer=a.developer:"string"==typeof a.developer&&(m.value.developer=a.developer.split(",").map(e=>e.trim()).filter(e=>e))),a.permissions&&Array.isArray(a.permissions)&&a.permissions.length>0&&(m.value.permissions=a.permissions),a.compatibility&&Array.isArray(a.compatibility)&&a.compatibility.length>0&&(m.value.compatibility=a.compatibility),a.architecture&&Array.isArray(a.architecture)&&a.architecture.length>0&&(m.value.architecture=a.architecture),a.language&&Array.isArray(a.language)&&a.language.length>0&&(m.value.language=a.language),"boolean"==typeof a.isDMCAed&&(m.value.isDMCAed=a.isDMCAed))}catch(a){}}
t(()=>i.visible,async e=>{var a,t
if(e){c.value="images"
const e=[L(),i.app?B(i.app.bundleId):Promise.resolve()];(null==(a=i.app)?void 0:a.imageId)&&Array.isArray(i.app.imageId)&&i.app.imageId.length>0&&e.push(U(i.app.imageId).then(e=>{b.value=e}).catch(e=>{})),(null==(t=i.app)?void 0:t.iconId)&&e.push(E(i.app.iconId).then(e=>{e&&(p.value=e)}).catch(e=>{})),await Promise.all(e)}})
const J=async()=>{if(q.value)try{await O(m.value,p.value,b.value,i.app)}catch(e){}},Q=()=>{var e,a
z.value||(o("close"),m.value={bundleId:(null==(e=i.app)?void 0:e.bundleId)||"",appName:(null==(a=i.app)?void 0:a.name)||"",description:"",permissions:[],compatibility:[],architecture:[],language:[],category:[],developer:[],isDMCAed:!1},p.value=null,b.value=[],R(),c.value="images")}
return(a,t)=>(h(),y(T,{visible:e.visible,"max-width":900,"close-on-overlay-click":!W(z),"show-close-button":!0,"close-button-position":"header",loading:W(z),onClose:Q},{header:k(()=>[...t[13]||(t[13]=[r("h2",{class:"dialog-title"},"上传应用信息",-1)])]),footer:k(()=>[r("div",Ee,[r("button",{onClick:Q,class:"btn btn-secondary",disabled:W(z)}," 取消 ",8,Xe),r("button",{onClick:J,class:"btn btn-primary",disabled:W(z)||!q.value},[W(z)?(h(),y(V,{key:0,icon:"mdi:loading",class:"btn-icon spinning"})):s("",!0),g(" "+v(W(z)?"上传中...":"确认上传"),1)],8,Ne)])]),default:k(()=>[r("div",ue,[u(N,{tabs:d,"active-tab":c.value,"onUpdate:activeTab":t[0]||(t[0]=e=>c.value=e)},null,8,["active-tab"]),I(r("div",de,[r("div",ce,[r("div",me,[u(V,{icon:"mdi:image-multiple",class:"card-icon"}),t[14]||(t[14]=r("h3",null,"应用截图",-1)),r("span",pe,v(b.value.length)+"/5",1)]),r("div",ve,[u(ae,{modelValue:b.value,"onUpdate:modelValue":t[1]||(t[1]=e=>b.value=e),"max-count":5},null,8,["modelValue"])])])],512),[[A,"images"===c.value]]),I(r("div",he,[r("div",fe,[r("div",ge,[u(V,{icon:"mdi:information",class:"card-icon"}),t[15]||(t[15]=r("h3",null,"基本信息",-1))]),r("div",ye,[r("div",be,[u(se,{modelValue:p.value,"onUpdate:modelValue":t[2]||(t[2]=e=>p.value=e),"current-icon-url":H.value,alt:"应用图标"},null,8,["modelValue","current-icon-url"]),r("div",we,[r("div",ke,[t[16]||(t[16]=r("label",{class:"form-label"},[g(" 应用名称 "),r("span",{class:"required"},"*")],-1)),I(r("input",{"onUpdate:modelValue":t[3]||(t[3]=e=>m.value.appName=e),type:"text",placeholder:"请输入应用名称",required:"",class:"form-input"},null,512),[[C,m.value.appName]])])])]),r("div",Me,[t[17]||(t[17]=r("label",{class:"form-label"},"Bundle ID",-1)),I(r("input",{"onUpdate:modelValue":t[4]||(t[4]=e=>m.value.bundleId=e),type:"text",readonly:"",class:"form-input readonly-input"},null,512),[[C,m.value.bundleId]])]),r("div",Ie,[t[18]||(t[18]=r("label",{class:"form-label"},"应用描述",-1)),I(r("textarea",{"onUpdate:modelValue":t[5]||(t[5]=e=>m.value.description=e),placeholder:"请输入应用描述...",rows:"5",class:"form-textarea"},null,512),[[C,m.value.description]])]),r("div",Ae,[r("label",Ce,[I(r("input",{"onUpdate:modelValue":t[6]||(t[6]=e=>m.value.isDMCAed=e),type:"checkbox"},null,512),[[D,m.value.isDMCAed]]),t[19]||(t[19]=r("span",null,"是否被DMCA",-1))])])])])],512),[[A,"basic"===c.value]]),I(r("div",De,[r("div",We,[r("div",Ve,[u(V,{icon:"mdi:format-list-bulleted",class:"card-icon"}),t[20]||(t[20]=r("h3",null,"详细信息",-1))]),r("div",xe,[u($,{modelValue:m.value.permissions,"onUpdate:modelValue":t[7]||(t[7]=e=>m.value.permissions=e),label:"权限列表",placeholder:"权限","empty-icon":"mdi:shield-off","empty-text":"暂无权限信息"},null,8,["modelValue"]),u($,{modelValue:m.value.compatibility,"onUpdate:modelValue":t[8]||(t[8]=e=>m.value.compatibility=e),label:"系统兼容性",placeholder:"例如: macOS 10.15","empty-icon":"mdi:desktop-classic","empty-text":"暂无兼容性信息"},null,8,["modelValue"]),u($,{modelValue:m.value.architecture,"onUpdate:modelValue":t[9]||(t[9]=e=>m.value.architecture=e),label:"系统架构",placeholder:"例如: x86_64, arm64","empty-icon":"mdi:cpu-64-bit","empty-text":"暂无架构信息"},null,8,["modelValue"]),u($,{modelValue:m.value.language,"onUpdate:modelValue":t[10]||(t[10]=e=>m.value.language=e),label:"支持语言",placeholder:"例如: English, Chinese","empty-icon":"mdi:translate","empty-text":"暂无语言信息"},null,8,["modelValue"]),u($,{modelValue:m.value.category,"onUpdate:modelValue":t[11]||(t[11]=e=>m.value.category=e),label:"应用分类",placeholder:"例如: Productivity, Games","empty-icon":"mdi:folder","empty-text":"暂无分类信息",options:w.value},null,8,["modelValue","options"]),u($,{modelValue:m.value.developer,"onUpdate:modelValue":t[12]||(t[12]=e=>m.value.developer=e),label:"开发者",placeholder:"例如: Apple Inc.","empty-icon":"mdi:account","empty-text":"暂无开发者信息"},null,8,["modelValue"])])])],512),[[A,"details"===c.value]]),W(z)?(h(),l("div",Pe,[r("div",Se,[u(V,{icon:"mdi:cloud-upload",class:"progress-icon"}),r("span",Te,v(W(j)),1),r("span",Fe,v(W(X))+"%",1)]),r("div",_e,[r("div",{class:"progress-fill",style:M({width:`${W(X)}%`})},null,4)])])):s("",!0),W(Y)&&!W(z)?(h(),l("div",Ue,[u(V,{icon:"mdi:alert-circle",class:"error-icon"}),r("span",null,v(W(Y)),1)])):s("",!0)])]),_:1},8,["visible","close-on-overlay-click","loading"]))}}),[["__scopeId","data-v-38b47a32"]]),ze=43200,Ye=Symbol.for("constructDateFrom")
function Oe(e,a){return"function"==typeof e?e(a):e&&"object"==typeof e&&Ye in e?e[Ye](a):e instanceof Date?new e.constructor(a):new Date(a)}function Re(e,a){return Oe(a||e,e)}let qe={}
function He(){return qe}function Le(e,a){var t,n,i,o
const l=He(),r=(null==a?void 0:a.weekStartsOn)??(null==(n=null==(t=null==a?void 0:a.locale)?void 0:t.options)?void 0:n.weekStartsOn)??l.weekStartsOn??(null==(o=null==(i=l.locale)?void 0:i.options)?void 0:o.weekStartsOn)??0,s=Re(e,null==a?void 0:a.in),u=s.getDay(),d=(u<r?7:0)+u-r
return s.setDate(s.getDate()-d),s.setHours(0,0,0,0),s}function $e(e){const a=Re(e),t=new Date(Date.UTC(a.getFullYear(),a.getMonth(),a.getDate(),a.getHours(),a.getMinutes(),a.getSeconds(),a.getMilliseconds()))
return t.setUTCFullYear(a.getFullYear()),+e-+t}function Be(e,...a){const t=Oe.bind(null,e||a.find(e=>"object"==typeof e))
return a.map(t)}function Je(e,a){const t=+Re(e)-+Re(a)
return t<0?-1:t>0?1:t}const Qe={lessThanXSeconds:{one:"less than a second",other:"less than {{count}} seconds"},xSeconds:{one:"1 second",other:"{{count}} seconds"},halfAMinute:"half a minute",lessThanXMinutes:{one:"less than a minute",other:"less than {{count}} minutes"},xMinutes:{one:"1 minute",other:"{{count}} minutes"},aboutXHours:{one:"about 1 hour",other:"about {{count}} hours"},xHours:{one:"1 hour",other:"{{count}} hours"},xDays:{one:"1 day",other:"{{count}} days"},aboutXWeeks:{one:"about 1 week",other:"about {{count}} weeks"},xWeeks:{one:"1 week",other:"{{count}} weeks"},aboutXMonths:{one:"about 1 month",other:"about {{count}} months"},xMonths:{one:"1 month",other:"{{count}} months"},aboutXYears:{one:"about 1 year",other:"about {{count}} years"},xYears:{one:"1 year",other:"{{count}} years"},overXYears:{one:"over 1 year",other:"over {{count}} years"},almostXYears:{one:"almost 1 year",other:"almost {{count}} years"}}
function Ge(e){return(a={})=>{const t=a.width?String(a.width):e.defaultWidth
return e.formats[t]||e.formats[e.defaultWidth]}}const Ke={date:Ge({formats:{full:"EEEE, MMMM do, y",long:"MMMM do, y",medium:"MMM d, y",short:"MM/dd/yyyy"},defaultWidth:"full"}),time:Ge({formats:{full:"h:mm:ss a zzzz",long:"h:mm:ss a z",medium:"h:mm:ss a",short:"h:mm a"},defaultWidth:"full"}),dateTime:Ge({formats:{full:"{{date}} 'at' {{time}}",long:"{{date}} 'at' {{time}}",medium:"{{date}}, {{time}}",short:"{{date}}, {{time}}"},defaultWidth:"full"})},Ze={lastWeek:"'last' eeee 'at' p",yesterday:"'yesterday at' p",today:"'today at' p",tomorrow:"'tomorrow at' p",nextWeek:"eeee 'at' p",other:"P"}
function ea(e){return(a,t)=>{let n
if("formatting"===((null==t?void 0:t.context)?String(t.context):"standalone")&&e.formattingValues){const a=e.defaultFormattingWidth||e.defaultWidth,i=(null==t?void 0:t.width)?String(t.width):a
n=e.formattingValues[i]||e.formattingValues[a]}else{const a=e.defaultWidth,i=(null==t?void 0:t.width)?String(t.width):e.defaultWidth
n=e.values[i]||e.values[a]}return n[e.argumentCallback?e.argumentCallback(a):a]}}function aa(e){return(a,t={})=>{const n=t.width,i=n&&e.matchPatterns[n]||e.matchPatterns[e.defaultMatchWidth],o=a.match(i)
if(!o)return null
const l=o[0],r=n&&e.parsePatterns[n]||e.parsePatterns[e.defaultParseWidth],s=Array.isArray(r)?function(e,a){for(let t=0;t<e.length;t++)if(a(e[t]))return t}(r,e=>e.test(l)):function(e,a){for(const t in e)if(Object.prototype.hasOwnProperty.call(e,t)&&a(e[t]))return t}(r,e=>e.test(l))
let u
return u=e.valueCallback?e.valueCallback(s):s,u=t.valueCallback?t.valueCallback(u):u,{value:u,rest:a.slice(l.length)}}}function ta(e){return(a,t={})=>{const n=a.match(e.matchPattern)
if(!n)return null
const i=n[0],o=a.match(e.parsePattern)
if(!o)return null
let l=e.valueCallback?e.valueCallback(o[0]):o[0]
return l=t.valueCallback?t.valueCallback(l):l,{value:l,rest:a.slice(i.length)}}}const na={code:"en-US",formatDistance:(e,a,t)=>{let n
const i=Qe[e]
return n="string"==typeof i?i:1===a?i.one:i.other.replace("{{count}}",a.toString()),(null==t?void 0:t.addSuffix)?t.comparison&&t.comparison>0?"in "+n:n+" ago":n},formatLong:Ke,formatRelative:(e,a,t,n)=>Ze[e],localize:{ordinalNumber:(e,a)=>{const t=Number(e),n=t%100
if(n>20||n<10)switch(n%10){case 1:return t+"st"
case 2:return t+"nd"
case 3:return t+"rd"}return t+"th"},era:ea({values:{narrow:["B","A"],abbreviated:["BC","AD"],wide:["Before Christ","Anno Domini"]},defaultWidth:"wide"}),quarter:ea({values:{narrow:["1","2","3","4"],abbreviated:["Q1","Q2","Q3","Q4"],wide:["1st quarter","2nd quarter","3rd quarter","4th quarter"]},defaultWidth:"wide",argumentCallback:e=>e-1}),month:ea({values:{narrow:["J","F","M","A","M","J","J","A","S","O","N","D"],abbreviated:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],wide:["January","February","March","April","May","June","July","August","September","October","November","December"]},defaultWidth:"wide"}),day:ea({values:{narrow:["S","M","T","W","T","F","S"],short:["Su","Mo","Tu","We","Th","Fr","Sa"],abbreviated:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],wide:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"]},defaultWidth:"wide"}),dayPeriod:ea({values:{narrow:{am:"a",pm:"p",midnight:"mi",noon:"n",morning:"morning",afternoon:"afternoon",evening:"evening",night:"night"},abbreviated:{am:"AM",pm:"PM",midnight:"midnight",noon:"noon",morning:"morning",afternoon:"afternoon",evening:"evening",night:"night"},wide:{am:"a.m.",pm:"p.m.",midnight:"midnight",noon:"noon",morning:"morning",afternoon:"afternoon",evening:"evening",night:"night"}},defaultWidth:"wide",formattingValues:{narrow:{am:"a",pm:"p",midnight:"mi",noon:"n",morning:"in the morning",afternoon:"in the afternoon",evening:"in the evening",night:"at night"},abbreviated:{am:"AM",pm:"PM",midnight:"midnight",noon:"noon",morning:"in the morning",afternoon:"in the afternoon",evening:"in the evening",night:"at night"},wide:{am:"a.m.",pm:"p.m.",midnight:"midnight",noon:"noon",morning:"in the morning",afternoon:"in the afternoon",evening:"in the evening",night:"at night"}},defaultFormattingWidth:"wide"})},match:{ordinalNumber:ta({matchPattern:/^(\d+)(th|st|nd|rd)?/i,parsePattern:/\d+/i,valueCallback:e=>parseInt(e,10)}),era:aa({matchPatterns:{narrow:/^(b|a)/i,abbreviated:/^(b\.?\s?c\.?|b\.?\s?c\.?\s?e\.?|a\.?\s?d\.?|c\.?\s?e\.?)/i,wide:/^(before christ|before common era|anno domini|common era)/i},defaultMatchWidth:"wide",parsePatterns:{any:[/^b/i,/^(a|c)/i]},defaultParseWidth:"any"}),quarter:aa({matchPatterns:{narrow:/^[1234]/i,abbreviated:/^q[1234]/i,wide:/^[1234](th|st|nd|rd)? quarter/i},defaultMatchWidth:"wide",parsePatterns:{any:[/1/i,/2/i,/3/i,/4/i]},defaultParseWidth:"any",valueCallback:e=>e+1}),month:aa({matchPatterns:{narrow:/^[jfmasond]/i,abbreviated:/^(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)/i,wide:/^(january|february|march|april|may|june|july|august|september|october|november|december)/i},defaultMatchWidth:"wide",parsePatterns:{narrow:[/^j/i,/^f/i,/^m/i,/^a/i,/^m/i,/^j/i,/^j/i,/^a/i,/^s/i,/^o/i,/^n/i,/^d/i],any:[/^ja/i,/^f/i,/^mar/i,/^ap/i,/^may/i,/^jun/i,/^jul/i,/^au/i,/^s/i,/^o/i,/^n/i,/^d/i]},defaultParseWidth:"any"}),day:aa({matchPatterns:{narrow:/^[smtwf]/i,short:/^(su|mo|tu|we|th|fr|sa)/i,abbreviated:/^(sun|mon|tue|wed|thu|fri|sat)/i,wide:/^(sunday|monday|tuesday|wednesday|thursday|friday|saturday)/i},defaultMatchWidth:"wide",parsePatterns:{narrow:[/^s/i,/^m/i,/^t/i,/^w/i,/^t/i,/^f/i,/^s/i],any:[/^su/i,/^m/i,/^tu/i,/^w/i,/^th/i,/^f/i,/^sa/i]},defaultParseWidth:"any"}),dayPeriod:aa({matchPatterns:{narrow:/^(a|p|mi|n|(in the|at) (morning|afternoon|evening|night))/i,any:/^([ap]\.?\s?m\.?|midnight|noon|(in the|at) (morning|afternoon|evening|night))/i},defaultMatchWidth:"any",parsePatterns:{any:{am:/^a/i,pm:/^p/i,midnight:/^mi/i,noon:/^no/i,morning:/morning/i,afternoon:/afternoon/i,evening:/evening/i,night:/night/i}},defaultParseWidth:"any"})},options:{weekStartsOn:0,firstWeekContainsDate:1}}
function ia(e,a){return function(e,a,t){const n=He(),i=(null==t?void 0:t.locale)??n.locale??na,o=Je(e,a)
if(isNaN(o))throw new RangeError("Invalid time value")
const l=Object.assign({},t,{addSuffix:null==t?void 0:t.addSuffix,comparison:o}),[r,s]=Be(null==t?void 0:t.in,...o>0?[a,e]:[e,a]),u=function(e,a){const t=function(e,a){return+Re(e)-+Re(a)}(e,a)/1e3
return(e=>{const a=(0,Math.trunc)(e)
return 0===a?0:a})(t)}(s,r),d=($e(s)-$e(r))/1e3,c=Math.round((u-d)/60)
let m
if(c<2)return(null==t?void 0:t.includeSeconds)?u<5?i.formatDistance("lessThanXSeconds",5,l):u<10?i.formatDistance("lessThanXSeconds",10,l):u<20?i.formatDistance("lessThanXSeconds",20,l):u<40?i.formatDistance("halfAMinute",0,l):u<60?i.formatDistance("lessThanXMinutes",1,l):i.formatDistance("xMinutes",1,l):0===c?i.formatDistance("lessThanXMinutes",1,l):i.formatDistance("xMinutes",c,l)
if(c<45)return i.formatDistance("xMinutes",c,l)
if(c<90)return i.formatDistance("aboutXHours",1,l)
if(c<1440){const e=Math.round(c/60)
return i.formatDistance("aboutXHours",e,l)}if(c<2520)return i.formatDistance("xDays",1,l)
if(c<ze){const e=Math.round(c/1440)
return i.formatDistance("xDays",e,l)}if(c<86400)return m=Math.round(c/ze),i.formatDistance("aboutXMonths",m,l)
if(m=function(e,a,t){const[n,i,o]=Be(null==t?void 0:t.in,e,e,a),l=Je(i,o),r=Math.abs(function(e,a){const[t,n]=Be(void 0,e,a)
return 12*(t.getFullYear()-n.getFullYear())+(t.getMonth()-n.getMonth())}(i,o))
if(r<1)return 0
1===i.getMonth()&&i.getDate()>27&&i.setDate(30),i.setMonth(i.getMonth()-l*r)
let s=Je(i,o)===-l;(function(e){const a=Re(e,void 0)
return+function(e){const a=Re(e,void 0)
return a.setHours(23,59,59,999),a}(a)===+function(e){const a=Re(e,void 0),t=a.getMonth()
return a.setFullYear(a.getFullYear(),t+1,0),a.setHours(23,59,59,999),a}(a)})(n)&&1===r&&1===Je(n,o)&&(s=!1)
const u=l*(r-+s)
return 0===u?0:u}(s,r),m<12){const e=Math.round(c/ze)
return i.formatDistance("xMonths",e,l)}{const e=m%12,a=Math.trunc(m/12)
return e<3?i.formatDistance("aboutXYears",a,l):e<9?i.formatDistance("overXYears",a,l):i.formatDistance("almostXYears",a+1,l)}}(e,function(e){return Oe(e,Date.now())}(e),a)}const oa={lessThanXSeconds:{one:"不到 1 秒",other:"不到 {{count}} 秒"},xSeconds:{one:"1 秒",other:"{{count}} 秒"},halfAMinute:"半分钟",lessThanXMinutes:{one:"不到 1 分钟",other:"不到 {{count}} 分钟"},xMinutes:{one:"1 分钟",other:"{{count}} 分钟"},xHours:{one:"1 小时",other:"{{count}} 小时"},aboutXHours:{one:"大约 1 小时",other:"大约 {{count}} 小时"},xDays:{one:"1 天",other:"{{count}} 天"},aboutXWeeks:{one:"大约 1 个星期",other:"大约 {{count}} 个星期"},xWeeks:{one:"1 个星期",other:"{{count}} 个星期"},aboutXMonths:{one:"大约 1 个月",other:"大约 {{count}} 个月"},xMonths:{one:"1 个月",other:"{{count}} 个月"},aboutXYears:{one:"大约 1 年",other:"大约 {{count}} 年"},xYears:{one:"1 年",other:"{{count}} 年"},overXYears:{one:"超过 1 年",other:"超过 {{count}} 年"},almostXYears:{one:"将近 1 年",other:"将近 {{count}} 年"}},la={date:Ge({formats:{full:"y'年'M'月'd'日' EEEE",long:"y'年'M'月'd'日'",medium:"yyyy-MM-dd",short:"yy-MM-dd"},defaultWidth:"full"}),time:Ge({formats:{full:"zzzz a h:mm:ss",long:"z a h:mm:ss",medium:"a h:mm:ss",short:"a h:mm"},defaultWidth:"full"}),dateTime:Ge({formats:{full:"{{date}} {{time}}",long:"{{date}} {{time}}",medium:"{{date}} {{time}}",short:"{{date}} {{time}}"},defaultWidth:"full"})}
function ra(e,a,t){const n="eeee p"
return function(e,a,t){const[n,i]=Be(null==t?void 0:t.in,e,a)
return+Le(n,t)===+Le(i,t)}(e,a,t)?n:e.getTime()>a.getTime()?"'下个'"+n:"'上个'"+n}const sa={lastWeek:ra,yesterday:"'昨天' p",today:"'今天' p",tomorrow:"'明天' p",nextWeek:ra,other:"PP p"},ua={code:"zh-CN",formatDistance:(e,a,t)=>{let n
const i=oa[e]
return n="string"==typeof i?i:1===a?i.one:i.other.replace("{{count}}",String(a)),(null==t?void 0:t.addSuffix)?t.comparison&&t.comparison>0?n+"内":n+"前":n},formatLong:la,formatRelative:(e,a,t,n)=>{const i=sa[e]
return"function"==typeof i?i(a,t,n):i},localize:{ordinalNumber:(e,a)=>{const t=Number(e)
switch(null==a?void 0:a.unit){case"date":return t.toString()+"日"
case"hour":return t.toString()+"时"
case"minute":return t.toString()+"分"
case"second":return t.toString()+"秒"
default:return"第 "+t.toString()}},era:ea({values:{narrow:["前","公元"],abbreviated:["前","公元"],wide:["公元前","公元"]},defaultWidth:"wide"}),quarter:ea({values:{narrow:["1","2","3","4"],abbreviated:["第一季","第二季","第三季","第四季"],wide:["第一季度","第二季度","第三季度","第四季度"]},defaultWidth:"wide",argumentCallback:e=>e-1}),month:ea({values:{narrow:["一","二","三","四","五","六","七","八","九","十","十一","十二"],abbreviated:["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月"],wide:["一月","二月","三月","四月","五月","六月","七月","八月","九月","十月","十一月","十二月"]},defaultWidth:"wide"}),day:ea({values:{narrow:["日","一","二","三","四","五","六"],short:["日","一","二","三","四","五","六"],abbreviated:["周日","周一","周二","周三","周四","周五","周六"],wide:["星期日","星期一","星期二","星期三","星期四","星期五","星期六"]},defaultWidth:"wide"}),dayPeriod:ea({values:{narrow:{am:"上",pm:"下",midnight:"凌晨",noon:"午",morning:"早",afternoon:"下午",evening:"晚",night:"夜"},abbreviated:{am:"上午",pm:"下午",midnight:"凌晨",noon:"中午",morning:"早晨",afternoon:"中午",evening:"晚上",night:"夜间"},wide:{am:"上午",pm:"下午",midnight:"凌晨",noon:"中午",morning:"早晨",afternoon:"中午",evening:"晚上",night:"夜间"}},defaultWidth:"wide",formattingValues:{narrow:{am:"上",pm:"下",midnight:"凌晨",noon:"午",morning:"早",afternoon:"下午",evening:"晚",night:"夜"},abbreviated:{am:"上午",pm:"下午",midnight:"凌晨",noon:"中午",morning:"早晨",afternoon:"中午",evening:"晚上",night:"夜间"},wide:{am:"上午",pm:"下午",midnight:"凌晨",noon:"中午",morning:"早晨",afternoon:"中午",evening:"晚上",night:"夜间"}},defaultFormattingWidth:"wide"})},match:{ordinalNumber:ta({matchPattern:/^(第\s*)?\d+(日|时|分|秒)?/i,parsePattern:/\d+/i,valueCallback:e=>parseInt(e,10)}),era:aa({matchPatterns:{narrow:/^(前)/i,abbreviated:/^(前)/i,wide:/^(公元前|公元)/i},defaultMatchWidth:"wide",parsePatterns:{any:[/^(前)/i,/^(公元)/i]},defaultParseWidth:"any"}),quarter:aa({matchPatterns:{narrow:/^[1234]/i,abbreviated:/^第[一二三四]刻/i,wide:/^第[一二三四]刻钟/i},defaultMatchWidth:"wide",parsePatterns:{any:[/(1|一)/i,/(2|二)/i,/(3|三)/i,/(4|四)/i]},defaultParseWidth:"any",valueCallback:e=>e+1}),month:aa({matchPatterns:{narrow:/^(一|二|三|四|五|六|七|八|九|十[二一])/i,abbreviated:/^(一|二|三|四|五|六|七|八|九|十[二一]|\d|1[12])月/i,wide:/^(一|二|三|四|五|六|七|八|九|十[二一])月/i},defaultMatchWidth:"wide",parsePatterns:{narrow:[/^一/i,/^二/i,/^三/i,/^四/i,/^五/i,/^六/i,/^七/i,/^八/i,/^九/i,/^十(?!(一|二))/i,/^十一/i,/^十二/i],any:[/^一|1/i,/^二|2/i,/^三|3/i,/^四|4/i,/^五|5/i,/^六|6/i,/^七|7/i,/^八|8/i,/^九|9/i,/^十(?!(一|二))|10/i,/^十一|11/i,/^十二|12/i]},defaultParseWidth:"any"}),day:aa({matchPatterns:{narrow:/^[一二三四五六日]/i,short:/^[一二三四五六日]/i,abbreviated:/^周[一二三四五六日]/i,wide:/^星期[一二三四五六日]/i},defaultMatchWidth:"wide",parsePatterns:{any:[/日/i,/一/i,/二/i,/三/i,/四/i,/五/i,/六/i]},defaultParseWidth:"any"}),dayPeriod:aa({matchPatterns:{any:/^(上午?|下午?|午夜|[中正]午|早上?|下午|晚上?|凌晨|)/i},defaultMatchWidth:"any",parsePatterns:{any:{am:/^上午?/i,pm:/^下午?/i,midnight:/^午夜/i,noon:/^[中正]午/i,morning:/^早上/i,afternoon:/^下午/i,evening:/^晚上?/i,night:/^凌晨/i}},defaultParseWidth:"any"})},options:{weekStartsOn:1,firstWeekContainsDate:4}}
export{je as A,N as T,ia as f,ua as z}

1
frontend/dist/assets/BPdAwp87.css vendored Normal file

File diff suppressed because one or more lines are too long

86
frontend/dist/assets/BWNjK7RB.js vendored Normal file

File diff suppressed because one or more lines are too long

1
frontend/dist/assets/BWVVIzHX.css vendored Normal file
View File

@@ -0,0 +1 @@
.modal-overlay[data-v-f5d3fd24]{position:fixed;top:0;left:0;right:0;bottom:0;background:#000000d9;display:flex;align-items:center;justify-content:center;z-index:9999;padding:var(--spacing-lg)}.modal-container[data-v-f5d3fd24]{background:var(--bg-primary);border-radius:var(--radius-xl);box-shadow:var(--shadow-2xl);width:100%;max-width:800px;max-height:90vh;display:flex;flex-direction:column;overflow:hidden;border:1px solid var(--border-light)}.modal-header[data-v-f5d3fd24]{padding:var(--spacing-lg) var(--spacing-xl);border-bottom:1px solid var(--border-light);display:flex;align-items:center;justify-content:space-between;background:var(--bg-secondary)}.modal-header .modal-title[data-v-f5d3fd24]{margin:0;font-size:1.3rem;font-weight:600;color:var(--text-primary);display:flex;align-items:center;gap:var(--spacing-sm)}.modal-header .modal-title .modal-icon[data-v-f5d3fd24]{font-size:1.5rem;color:var(--primary-color)}.modal-header .modal-close[data-v-f5d3fd24]{background:transparent;border:none;cursor:pointer;padding:var(--spacing-xs);border-radius:var(--radius-sm);color:var(--text-secondary);font-size:1.5rem;display:flex;align-items:center;justify-content:center;transition:all .2s ease}.modal-header .modal-close[data-v-f5d3fd24]:hover{background:var(--bg-primary);color:var(--text-primary)}.modal-body[data-v-f5d3fd24]{flex:0 0 auto;overflow:hidden;padding:var(--spacing-md)}.log-container[data-v-f5d3fd24]{height:500px;min-height:500px;max-height:60vh;overflow-y:auto;overflow-x:hidden;background:var(--bg-tertiary, #1a1a1a);border-radius:var(--radius-md);padding:var(--spacing-md);font-family:SF Mono,Monaco,Cascadia Code,Roboto Mono,Consolas,Courier New,monospace;font-size:.9rem;line-height:1.6;scroll-behavior:smooth}.log-container[data-v-f5d3fd24]::-webkit-scrollbar{width:8px}.log-container[data-v-f5d3fd24]::-webkit-scrollbar-track{background:var(--bg-secondary);border-radius:4px}.log-container[data-v-f5d3fd24]::-webkit-scrollbar-thumb{background:var(--border-color);border-radius:4px}.log-container[data-v-f5d3fd24]::-webkit-scrollbar-thumb:hover{background:var(--text-tertiary)}.log-line[data-v-f5d3fd24]{display:flex;gap:var(--spacing-sm);margin-bottom:var(--spacing-xs);padding:var(--spacing-xs) 0;align-items:flex-start}.log-line.loading[data-v-f5d3fd24]{color:var(--primary-color)}.log-line .log-time[data-v-f5d3fd24]{color:var(--text-tertiary);flex-shrink:0;font-size:.85rem}.log-line .log-message[data-v-f5d3fd24]{color:var(--text-secondary);flex:1;word-break:break-word;white-space:pre-wrap}.log-line.log-info .log-message[data-v-f5d3fd24]{color:var(--text-secondary)}.log-line.log-success .log-message[data-v-f5d3fd24]{color:var(--success-color)}.log-line.log-warning .log-message[data-v-f5d3fd24]{color:var(--warning-color)}.log-line.log-error .log-message[data-v-f5d3fd24]{color:var(--error-color)}.modal-footer[data-v-f5d3fd24]{padding:var(--spacing-lg) var(--spacing-xl);border-top:1px solid var(--border-light);background:var(--bg-secondary);display:flex;align-items:center;justify-content:space-between}.modal-footer .footer-status[data-v-f5d3fd24]{display:flex;align-items:center;gap:var(--spacing-sm);font-size:1rem;font-weight:500}.modal-footer .footer-status .status-success[data-v-f5d3fd24]{color:var(--success-color);display:flex;align-items:center;gap:var(--spacing-xs)}.modal-footer .footer-status .status-error[data-v-f5d3fd24]{color:var(--error-color);display:flex;align-items:center;gap:var(--spacing-xs)}.modal-footer .footer-status .status-loading[data-v-f5d3fd24]{color:var(--primary-color);display:flex;align-items:center;gap:var(--spacing-xs)}.modal-footer .footer-actions[data-v-f5d3fd24]{display:flex;gap:var(--spacing-sm)}.spinner-small[data-v-f5d3fd24]{width:16px;height:16px;border:2px solid var(--border-color);border-top:2px solid var(--primary-color);border-radius:50%;animation:spin-f5d3fd24 1s linear infinite}@keyframes spin-f5d3fd24{to{transform:rotate(360deg)}}.modal-enter-active[data-v-f5d3fd24],.modal-leave-active[data-v-f5d3fd24]{transition:opacity .3s ease}.modal-enter-from[data-v-f5d3fd24],.modal-leave-to[data-v-f5d3fd24]{opacity:0}.modal-enter-active .modal-container[data-v-f5d3fd24],.modal-leave-active .modal-container[data-v-f5d3fd24]{transition:transform .3s ease}.modal-enter-from .modal-container[data-v-f5d3fd24],.modal-leave-to .modal-container[data-v-f5d3fd24]{transform:scale(.9) translateY(-20px)}@media (max-width: 768px){.modal-overlay[data-v-f5d3fd24]{padding:0}.modal-container[data-v-f5d3fd24]{max-width:100%;max-height:100vh;border-radius:0}.log-container[data-v-f5d3fd24]{height:400px;min-height:400px;max-height:50vh}.modal-footer[data-v-f5d3fd24]{flex-direction:column;gap:var(--spacing-md);align-items:stretch}.modal-footer .footer-actions[data-v-f5d3fd24]{width:100%}.modal-footer .footer-actions .btn[data-v-f5d3fd24]{flex:1}}

111
frontend/dist/assets/BXq_Ubfy.js vendored Normal file
View File

@@ -0,0 +1,111 @@
import{d as e,r as s,c as a,w as t,o as n,I as l,i,a as o,e as c,m as u,g as d,b as r,z as v,l as m,q as g,F as p,B as h,t as f,n as y,u as k,A as w,C as b,D as C,p as I,G as E,k as M,H as T,h as x}from"./CdD4XvnD.js"
import{c as S,i as N,g as A,u as D,f as $,j as L,W as _,_ as B,b as O,M as z,T as R,A as F,a as U}from"./BaSQ3xJt.js"
import{I as j}from"./j0cGtmjd.js"
import{I as W}from"./JespKOZ3.js"
import{u as G}from"./4xYhABhf.js"
import{u as q}from"./lyDeIg7R.js"
import"./BqTCaadz.js"
const V={class:"messages-page"},K={class:"page-container"},H={class:"page-header"},P={class:"page-title"},J={key:0,class:"title-badge"},Q={class:"messages-layout"},X={class:"conversations-sidebar"},Y={class:"sidebar-header"},Z={class:"conversations-list"},ee={key:0,class:"conversations-loading"},se=["onClick","onContextmenu"],ae={class:"conversation-avatar"},te=["title"],ne={key:0,class:"unread-badge"},le={class:"conversation-info"},ie={class:"conversation-header"},oe={class:"conversation-name"},ce={class:"conversation-time"},ue={class:"conversation-preview"},de={class:"preview-text"},re={key:0,class:"unread-indicator"},ve=["onClick"],me={key:2,class:"empty-conversations"},ge={class:"messages-content"},pe={key:0,class:"user-selection-view"},he={class:"selection-header"},fe={class:"selection-search"},ye={class:"search-input-wrapper"},ke={class:"user-list-container"},we={key:0,class:"users-loading"},be={key:1,class:"users-error"},Ce={key:2,class:"users-empty"},Ie={key:3,class:"users-list"},Ee=["onClick"],Me={class:"user-item-info"},Te={class:"user-item-name"},xe={key:0,class:"user-item-username"},Se={key:1,class:"user-item-username"},Ne={key:1,class:"empty-conversation-view"},Ae={key:2,class:"conversation-view"},De={class:"conversation-header-bar"},$e={class:"header-user-info"},Le={class:"user-details"},_e={class:"user-name"},Be={class:"user-status"},Oe={key:0,class:"status-badge blocked"},ze={key:1,class:"status-badge muted"},Re={key:2,class:"status-badge online"},Fe={key:3,class:"status-badge offline"},Ue={key:4,class:"status-badge offline"},je={class:"header-actions-group"},We={key:0,class:"messages-loading"},Ge={key:0,class:"message-item system-message"},qe={class:"system-message-content"},Ve={key:0,class:"message-avatar"},Ke={class:"message-content-wrapper"},He={class:"message-content"},Pe={key:0,class:"message-text"},Je={key:1,class:"message-attachments"},Qe={key:0,class:"message-images"},Xe={key:1,class:"message-files"},Ye=["onClick"],Ze={class:"attachment-name"},es={class:"attachment-size"},ss={class:"message-footer"},as={class:"message-time"},ts={key:0,class:"message-status sending"},ns={key:1,class:"message-status sent"},ls={key:2,class:"message-status failed"},is={key:3,class:"message-actions"},os=["onClick"],cs=["onClick"],us={key:1,class:"message-avatar"},ds={class:"message-input-area"},rs={key:0,class:"input-disabled-notice"},vs={key:1},ms={class:"input-toolbar"},gs=["disabled"],ps=["disabled"],hs=["onKeydown"],fs={key:2,class:"attachments-preview"},ys=["src"],ks=["onClick"],ws={class:"file-name"},bs=["onClick"],Cs={key:3,class:"input-footer"},Is={class:"char-count"},Es=["disabled"],Ms=U(e({__name:"Messages",setup(e){const U=T(),Ms=l(),{toasts:Ts,error:xs,success:Ss,info:Ns,removeToast:As}=S(),Ds=N(),{loadingState:$s,show:Ls,hide:_s}=A(),{user:Bs}=D()
$()
const Os=L(),{isConnected:zs,getConversations:Rs,deleteConversation:Fs,getMessages:Us,sendMessage:js,recallMessage:Ws,deleteMessage:Gs}=Os,{isUserOnline:qs,queryUsersOnlineStatus:Vs}=G(),Ks=s([]),Hs=s(!1),Ps=s(!1),Js=s(null),Qs=s(""),Xs=s([]),Ys=s([]),Zs=s(null),ea=s(null),sa=s(null),aa=s(!1),ta=q(),na=s(null),la=s(!1),ia=s(null),oa=s(!1),ca=s(""),ua=s([]),da=s(!1),ra=s(""),va=s(1),ma=s(50),ga=s(0),pa=a(()=>Ks.value.find(e=>e.id===Js.value)||null),ha=a(()=>{var e
return(null==(e=pa.value)?void 0:e.userId)?qs(pa.value.userId).value:null}),fa=a(()=>Ks.value.reduce((e,s)=>e+(s.unreadCount||0),0)),ya=a(()=>Qs.value.trim().length>0||Xs.value.length>0||Ys.value.length>0),ka=async e=>{oa.value=!1,Js.value=e
const s=Ks.value.find(s=>s.id===e)
s&&(s.unreadCount=0,s.messages&&0!==s.messages.length||await ba(e)),M(()=>{qa()})},wa=(e,s=!1)=>{if(e&&e.conversations){const s=e.conversations.map(e=>{var s
return{id:e.id,userId:e.userId,userName:e.userName,userAvatar:e.userAvatar||e.avatar,lastMessage:e.lastMessage,lastMessageTime:e.lastMessageTime,unreadCount:e.unreadCount||0,messages:(null==(s=Ks.value.find(s=>s.id===e.id))?void 0:s.messages)||[],blocked:e.blocked||!1,muted:e.muted||!1}}),a=new Map(Ks.value.map(e=>[e.id,e])),t=s.filter(e=>!a.has(e.id)&&e.lastMessage&&e.lastMessage.trim().length>0),n=s.filter(e=>!(a.has(e.id)||e.lastMessage&&0!==e.lastMessage.trim().length)),l=[],i=[]
s.forEach(e=>{const s=a.get(e.id)
if(s){const a=s.lastMessageTime!==e.lastMessageTime&&e.lastMessage&&e.lastMessage.trim().length>0,t=s.unreadCount!==e.unreadCount
a||t?l.push({...e,messages:s.messages||[]}):i.push(s)}}),0===Ks.value.length?Ks.value=s:Ks.value=[...l,...t,...i,...n]}else s||(Ks.value=[])
if(window.dispatchEvent(new CustomEvent("messages:conversations-updated")),e&&e.conversations&&e.conversations.length>0){const s=e.conversations.map(e=>e.userId).filter(Boolean)
s.length>0&&zs.value&&Vs(s).catch(e=>{})}},ba=async e=>{if(zs.value){Ps.value=!0
try{const s=await Us(e,1,100)
if(s&&s.messages){const a=Ks.value.find(s=>s.id===e)
a&&(a.messages=s.messages.map(e=>({id:e.id,content:e.content,sendTime:e.sendTime,sent:e.sent,status:e.sent?"sent":void 0,attachments:e.attachments,recalled:e.recalled||!1,deleted:e.deleted||!1})))}}catch(s){if(s.message&&s.message.includes("WebSocket未连接"))return
xs(s.message||"加载消息列表失败",2e3)}finally{Ps.value=!1}}},Ca=()=>{oa.value=!0,Js.value=null,ca.value="",va.value=1,Ea()},Ia=()=>{oa.value=!1,ca.value="",va.value=1},Ea=async()=>{da.value=!0,ra.value=""
try{const e=await F.getUsersForChat(va.value,ma.value,ca.value||void 0)
e.users&&Array.isArray(e.users)?(ua.value=e.users.map(e=>{var s
return{id:e.id||e._id||(null==(s=e._id)?void 0:s.toString()),username:e.username||"",nickName:e.nickName||"",avatar:e.avatar||"",age:e.age}}),ga.value=e.total||0):ra.value="加载用户列表失败"}catch(e){ra.value=e.message||"加载用户列表失败"}finally{da.value=!1}},Ma=()=>{clearTimeout(Ma.timer),Ma.timer=setTimeout(()=>{va.value=1,Ea()},300)},Ta=()=>{ca.value="",va.value=1,Ea()},xa=a(()=>{if(!ca.value.trim())return ua.value
const e=ca.value.toLowerCase()
return ua.value.filter(s=>s.username&&s.username.toLowerCase().includes(e)||s.nickName&&s.nickName.toLowerCase().includes(e))}),Sa=async e=>{Ls("创建会话...")
try{const a=await F.getOrCreateConversationByUserId(e)
if(a.success&&a.data){const{conversationId:t,userName:n}=a.data
if(Ks.value.find(e=>e.id===t))Js.value=t,await ka(t)
else{let a
try{const s=await F.getUserInfo(e)
s&&(a=s.avatar)}catch(s){}const l={id:t,userId:e,userName:n,userAvatar:a,lastMessage:"",lastMessageTime:(new Date).toISOString(),unreadCount:0,messages:[],blocked:!1,muted:!1}
Ks.value.push(l),Js.value=t,await ka(t)}U.replace({path:"/messages",query:{}})}else xs(a.message||"创建会话失败",2e3)}catch(s){xs("创建会话失败",2e3)}finally{_s()}},Na=()=>{pa.value&&(na.value={userId:pa.value.userId,userName:pa.value.userName,content:"",type:void 0},aa.value=!0,la.value=!1)},Aa=async()=>{if(pa.value){Ls("设置拒收...")
try{const e=await F.setConversationMute(pa.value.id,!0)
e.success?(pa.value.muted=!0,la.value=!1):xs(e.message||"操作失败",2e3)}catch(e){xs("设置拒收失败",2e3)}finally{_s()}}},Da=async()=>{if(pa.value){Ls("取消拒收...")
try{const e=await F.setConversationMute(pa.value.id,!1)
e.success?(pa.value.muted=!1,la.value=!1):xs(e.message||"操作失败",2e3)}catch(e){xs("取消拒收失败",2e3)}finally{_s()}}},$a=async()=>{if(!pa.value)return
const e=await Ds.show({title:"拉黑用户",message:`确定要拉黑用户"${pa.value.userName}"吗?拉黑后将无法接收该用户的消息。`,type:"warning",confirmText:"拉黑",cancelText:"取消"})
if(e&&null!==e){Ls("拉黑用户...")
try{const e=await F.setConversationBlock(pa.value.id,!0)
e.success?(pa.value.blocked=!0,pa.value.muted=!0,la.value=!1):xs(e.message||"操作失败",2e3)}catch(s){xs("拉黑用户失败",2e3)}finally{_s()}}},La=async()=>{if(pa.value){Ls("取消拉黑...")
try{const e=await F.setConversationBlock(pa.value.id,!1)
e.success?(pa.value.blocked=!1,la.value=!1):xs(e.message||"操作失败",2e3)}catch(e){xs("取消拉黑失败",2e3)}finally{_s()}}},_a=e=>{const s=e.target
ia.value&&!ia.value.contains(s)&&(la.value=!1)},Ba=async()=>{if(!ya.value||!pa.value)return
const e=Qs.value.trim()
if(!e&&0===Xs.value.length&&0===Ys.value.length)return
if(!zs.value)return void xs("WebSocket未连接无法发送消息",2e3)
const s=`temp-${Date.now()}-${Math.random()}`,a={id:s,content:e||"",sendTime:(new Date).toISOString(),sent:!0,status:"sending",attachments:[...Xs.value.map((e,s)=>({id:`temp-img-${s}`,name:e.file.name,type:"image",size:e.file.size})),...Ys.value.map((e,s)=>({id:`temp-file-${s}`,name:e.file.name,type:"file",size:e.file.size}))],recalled:!1,deleted:!1}
pa.value.messages.push(a)
const t=[...Xs.value],n=[...Ys.value]
Qs.value="",Xs.value=[],Ys.value=[],M(()=>{qa()})
try{const a=[]
for(let e=0;e<t.length;e++){const s=t[e],n=await F.uploadImageAttachment(s.file)
if(!n.success||!n.attachmentId)throw new Error(n.message||"图片上传失败")
a.push(n.attachmentId)}for(let e=0;e<n.length;e++){const s=n[e],t=await F.uploadFileAttachment(s.file)
if(!t.success||!t.attachmentId)throw new Error(t.message||"文件上传失败")
a.push(t.attachmentId)}const l=await js(pa.value.id,e||"",a.length>0?a:void 0),i=pa.value.messages.findIndex(e=>e.id===s),o=l.sendTime instanceof Date?l.sendTime.toISOString():l.sendTime;-1!==i&&(pa.value.messages[i]={id:l.messageId,content:l.content,sendTime:o,sent:!0,status:"sent",attachments:l.attachments||[],recalled:!1,deleted:!1}),pa.value.lastMessage=e||"[附件]",pa.value.lastMessageTime=o,window.dispatchEvent(new CustomEvent("messages:conversations-updated"))}catch(l){const e=pa.value.messages.findIndex(e=>e.id===s);-1!==e&&(pa.value.messages[e].status="failed"),xs(l.message||"发送消息失败",2e3)}},Oa=()=>{var e
null==(e=Zs.value)||e.click()},za=()=>{var e
null==(e=ea.value)||e.click()},Ra=e=>{const s=e.target
Array.from(s.files||[]).forEach(e=>{if(Xs.value.length>=5)return
if(e.size>10485760)return void xs("图片大小不能超过10MB",2e3)
const s=new FileReader
s.onload=s=>{var a
Xs.value.push({file:e,preview:null==(a=s.target)?void 0:a.result})},s.readAsDataURL(e)}),s.value=""},Fa=e=>{const s=e.target
Array.from(s.files||[]).forEach(e=>{Ys.value.length>=5||(e.size>20971520?xs("文件大小不能超过20MB",2e3):Ys.value.push({file:e}))}),s.value=""},Ua=async e=>{const s=e.clipboardData
if(!s)return
const a=Array.from(s.items).find(e=>e.type.startsWith("image/"))
if(!a)return
if(e.preventDefault(),Xs.value.length>=5)return void xs("图片数量不能超过5张",2e3)
const t=a.getAsFile()
if(!t)return
if(t.size>10485760)return void xs("图片大小不能超过10MB",2e3)
const n=Date.now(),l=t.type.split("/")[1]||"png",i=new File([t],`paste-${n}.${l}`,{type:t.type}),o=new FileReader
o.onload=e=>{var s
Xs.value.push({file:i,preview:null==(s=e.target)?void 0:s.result})},o.readAsDataURL(i),Ss("图片已粘贴",2e3)},ja=e=>e.filter(e=>"image"===e.type),Wa=e=>e.filter(e=>"image"!==e.type),Ga=e=>{const s=new Date(e),a=(new Date).getTime()-s.getTime(),t=Math.floor(a/6e4),n=Math.floor(a/36e5),l=Math.floor(a/864e5)
return t<1?"刚刚":t<60?`${t}分钟前`:n<24?`${n}小时前`:l<7?`${l}天前`:s.toLocaleDateString("zh-CN")},qa=()=>{sa.value&&(sa.value.scrollTop=sa.value.scrollHeight)},Va=e=>{const s=e.detail
Hs.value&&(Hs.value=!1),wa(s,!0)},Ka=e=>{var s
const a=e.detail,{conversationId:t,messageId:n,content:l,sendTime:i,senderId:o,attachments:c}=a,u=Ks.value.find(e=>e.id===t)
if(u){Js.value===t?u.messages.some(e=>e.id===n)||(u.messages.push({id:n,content:l||"",sendTime:i,sent:o===(null==(s=Bs.value)?void 0:s._id),status:"sent",attachments:c||[],recalled:!1,deleted:!1}),M(()=>{qa()})):u.unreadCount=(u.unreadCount||0)+1,u.lastMessage=l.length>50?l.substring(0,50)+"...":l,u.lastMessageTime=i
const e=Ks.value.findIndex(e=>e.id===t)
e>0&&(Ks.value.splice(e,1),Ks.value.unshift(u))}window.dispatchEvent(new CustomEvent("messages:conversations-updated"))},Ha=e=>{const s=e,{messageId:a,conversationId:t,senderName:n}=s.detail,l=Ks.value.find(e=>e.id===t)
if(l){const e=l.messages.findIndex(e=>e.id===a)
if(-1!==e&&(l.messages.splice(e,1),n)){const s={id:`system-${Date.now()}-${Math.random()}`,content:`${n} 已撤回消息`,sendTime:(new Date).toISOString(),sent:!1,type:"system"}
l.messages.splice(e,0,s)}}},Pa=e=>{const s=e,{messageId:a,conversationId:t,senderName:n,deleteForBoth:l}=s.detail,i=Ks.value.find(e=>e.id===t)
if(i){const e=i.messages.findIndex(e=>e.id===a)
if(-1!==e){if(i.messages.splice(e,1),n&&l){const s={id:`system-${Date.now()}-${Math.random()}`,content:`${n} 已删除消息`,sendTime:(new Date).toISOString(),sent:!1,type:"system"}
i.messages.splice(e,0,s)}}else if(!i.messages.some(e=>"system"===e.type&&e.content.includes("已删除消息")&&e.content.includes(n))&&n&&l){const e={id:`system-${Date.now()}-${Math.random()}`,content:`${n} 已删除消息`,sendTime:(new Date).toISOString(),sent:!1,type:"system"}
i.messages.push(e)}}},Ja=async(e=0,s=3)=>{if(zs.value&&(!Hs.value||0!==e)){Hs.value=!0
try{const e=await Rs(1,50)
e&&e.conversations&&wa(e,!1)}catch(a){const t=a.message||"",n=t.includes("超时")||t.includes("timeout")||t.includes("请求超时")
if(n&&e<s)return Hs.value=!1,void setTimeout(()=>{Ja(e+1,s)},1e3*(e+1))
n||!t||t.includes("WebSocket未连接")||xs(t||"加载会话列表失败",2e3)}finally{0!==e&&Hs.value||(Hs.value=!1)}}}
return t(zs,e=>{e?setTimeout(()=>{zs.value&&Ja()},300):Hs.value&&(Hs.value=!1)}),n(async()=>{zs.value?setTimeout(async()=>{zs.value&&await Ja()},300):0===Ks.value.length&&(Hs.value=!0)
const e=Ms.query.userId
e&&await Sa(e),document.addEventListener("click",_a),window.addEventListener(_.CONVERSATIONS,Va),window.addEventListener(_.NEW_MESSAGE,Ka),window.addEventListener(_.MESSAGE_RECALLED,Ha),window.addEventListener(_.MESSAGE_DELETED,Pa)}),t(()=>Ms.query.userId,async e=>{e&&"string"==typeof e&&await Sa(e)}),i(()=>{document.removeEventListener("click",_a),window.removeEventListener(_.CONVERSATIONS,Va),window.removeEventListener(_.NEW_MESSAGE,Ka),window.removeEventListener(_.MESSAGE_RECALLED,Ha),window.removeEventListener(_.MESSAGE_DELETED,Pa)}),(e,s)=>(x(),o(p,null,[c("div",V,[c("div",K,[c("div",H,[c("h1",P,[s[5]||(s[5]=d(" 私信 ",-1)),fa.value>0?(x(),o("span",J,v(fa.value>99?"99+":fa.value),1)):r("",!0)]),s[6]||(s[6]=c("p",{class:"page-description"},"与用户进行一对一私信交流",-1))]),c("div",Q,[c("div",X,[c("div",Y,[s[8]||(s[8]=c("h2",null,"会话列表",-1)),c("button",{class:"new-conversation-btn",onClick:Ca},[u(B,{icon:"mdi:plus",class:"btn-icon"}),s[7]||(s[7]=d(" 新会话 ",-1))])]),c("div",Z,[Hs.value&&0===Ks.value.length?(x(),o("div",ee,[u(B,{icon:"mdi:loading",class:"loading-icon spinning"}),s[9]||(s[9]=c("span",null,"加载会话列表...",-1))])):(x(),m(w,{key:1,name:"conversation-list",tag:"div",class:"conversations-transition-wrapper"},{default:g(()=>[(x(!0),o(p,null,h(Ks.value,e=>(x(),o("div",{key:e.id,class:y(["conversation-item",{active:Js.value===e.id}]),onClick:s=>ka(e.id),onContextmenu:f(e=>{},["prevent"])},[c("div",ae,[u(O,{"user-id":e.userId,"user-name":e.userName,width:40,height:40},null,8,["user-id","user-name"]),c("div",{class:y(["conversation-online-indicator",{online:!0===k(qs)(e.userId).value,offline:!1===k(qs)(e.userId).value}]),title:!0===k(qs)(e.userId).value?"在线":!1===k(qs)(e.userId).value?"离线":"状态未知"},null,10,te),e.unreadCount>0?(x(),o("span",ne,v(e.unreadCount),1)):r("",!0)]),c("div",le,[c("div",ie,[c("span",oe,v(e.userName),1),c("span",ce,v(Ga(e.lastMessageTime)),1)]),c("div",ue,[c("span",de,v(e.lastMessage),1),e.unreadCount>0?(x(),o("span",re)):r("",!0)])]),c("button",{class:"conversation-delete-btn",onClick:f(s=>(async e=>{const s=await Ds.show({title:"隐藏会话",message:`确定要隐藏与"${e.userName}"的会话吗?\n\n隐藏后会话将从列表中移除,但所有消息记录都会保留。重新建立会话时可以恢复。`,type:"warning",confirmText:"隐藏",cancelText:"取消"})
if(s&&null!==s)if(zs.value)try{await Fs(e.id),Js.value===e.id&&(Js.value=null)
const s=Ks.value.findIndex(s=>s.id===e.id);-1!==s&&Ks.value.splice(s,1),window.dispatchEvent(new CustomEvent("messages:conversations-updated")),Ss("隐藏会话成功",2e3)}catch(a){xs(a.message||"隐藏会话失败",2e3)}else xs("WebSocket未连接无法隐藏会话",2e3)})(e),["stop"]),title:"隐藏会话"},[u(B,{icon:"mdi:delete-outline"})],8,ve)],42,se))),128))]),_:1})),Hs.value||0!==Ks.value.length?r("",!0):(x(),o("div",me,[u(B,{icon:"fluent-color:comment-multiple-32",class:"empty-icon"}),s[10]||(s[10]=c("p",null,"暂无会话",-1))]))])]),c("div",ge,[oa.value?(x(),o("div",pe,[c("div",he,[s[11]||(s[11]=c("h2",null,"选择用户开始聊天",-1)),c("button",{class:"close-btn",onClick:Ia},[u(B,{icon:"mdi:close"})])]),c("div",fe,[c("div",ye,[u(B,{icon:"mdi:magnify",class:"search-icon"}),b(c("input",{"onUpdate:modelValue":s[0]||(s[0]=e=>ca.value=e),type:"text",placeholder:"搜索用户名或昵称...",class:"search-input",onInput:Ma},null,544),[[C,ca.value]]),ca.value?(x(),o("button",{key:0,class:"clear-search-btn",onClick:Ta},[u(B,{icon:"mdi:close-circle"})])):r("",!0)])]),c("div",ke,[da.value?(x(),o("div",we,[u(B,{icon:"mdi:loading",class:"loading-icon spinning"}),s[12]||(s[12]=c("span",null,"加载用户列表...",-1))])):ra.value?(x(),o("div",be,[u(B,{icon:"mdi:alert-circle",class:"error-icon"}),c("p",null,v(ra.value),1),c("button",{class:"retry-btn",onClick:Ea},"重试")])):0===xa.value.length?(x(),o("div",Ce,[u(B,{icon:"mdi:account-off",class:"empty-icon"}),c("p",null,v(ca.value?"未找到匹配的用户":"暂无用户"),1)])):(x(),o("div",Ie,[(x(!0),o(p,null,h(xa.value,e=>(x(),o("div",{key:e.id,class:"user-item",onClick:s=>(async e=>{Ia(),await Sa(e.id)})(e)},[u(O,{"user-id":e.id,"user-name":e.nickName||e.username,width:48,height:48},null,8,["user-id","user-name"]),c("div",Me,[c("div",Te,v(e.nickName||e.username||"未知用户"),1),e.username?(x(),o("div",xe,"@"+v(e.username),1)):e.age?(x(),o("div",Se,"年龄: "+v(e.age),1)):r("",!0)]),u(B,{icon:"mdi:chevron-right",class:"chevron-icon"})],8,Ee))),128))]))])])):pa.value?(x(),o("div",Ae,[c("div",De,[c("div",$e,[u(O,{"user-id":pa.value.userId,"user-name":pa.value.userName,width:40,height:40},null,8,["user-id","user-name"]),c("div",Le,[c("h3",_e,v(pa.value.userName),1),c("span",Be,[pa.value.blocked?(x(),o("span",Oe,"已拉黑")):pa.value.muted?(x(),o("span",ze,"已拒收")):!0===ha.value?(x(),o("span",Re,"在线")):!1===ha.value?(x(),o("span",Fe,"离线")):(x(),o("span",Ue,"状态未知"))])])]),c("div",je,[c("button",{class:"header-action-btn report-btn",onClick:Na},[u(B,{icon:"mdi:flag-outline"})]),c("div",{class:"more-actions-wrapper",ref_key:"moreActionsRef",ref:ia},[c("button",{class:"header-action-btn",onClick:s[1]||(s[1]=e=>la.value=!la.value)},[u(B,{icon:"mdi:dots-vertical"})]),u(I,{name:"menu-fade"},{default:g(()=>[la.value?(x(),o("div",{key:0,class:"more-actions-menu",onClick:s[2]||(s[2]=f(()=>{},["stop"]))},[pa.value.muted?(x(),o("div",{key:1,class:"menu-item",onClick:Da},[u(B,{icon:"mdi:bell-outline",class:"menu-icon"}),s[15]||(s[15]=c("span",null,"接收消息",-1))])):(x(),o("div",{key:0,class:"menu-item",onClick:Aa},[u(B,{icon:"mdi:bell-off-outline",class:"menu-icon"}),s[14]||(s[14]=c("span",null,"拒收消息",-1))])),pa.value.blocked?(x(),o("div",{key:3,class:"menu-item",onClick:La},[u(B,{icon:"mdi:account-check-outline",class:"menu-icon"}),s[17]||(s[17]=c("span",null,"取消拉黑",-1))])):(x(),o("div",{key:2,class:"menu-item danger",onClick:$a},[u(B,{icon:"mdi:block-helper",class:"menu-icon"}),s[16]||(s[16]=c("span",null,"拉黑用户",-1))]))])):r("",!0)]),_:1})],512)])]),c("div",{class:"messages-list",ref_key:"messagesListRef",ref:sa},[Ps.value?(x(),o("div",We,[u(B,{icon:"mdi:loading",class:"loading-icon spinning"}),s[18]||(s[18]=c("span",null,"加载消息...",-1))])):(x(!0),o(p,{key:1},h(pa.value.messages,e=>{var s,a,t,n,l
return x(),o(p,{key:e.id},["system"===e.type?(x(),o("div",Ge,[c("div",qe,[u(B,{icon:"mdi:information-outline",class:"system-icon"}),c("span",null,v(e.content),1)])])):(x(),o("div",{key:1,class:y(["message-item",{sent:e.sent,received:!e.sent}])},[e.sent?r("",!0):(x(),o("div",Ve,[u(O,{"user-id":pa.value.userId,"user-name":pa.value.userName,width:40,height:40},null,8,["user-id","user-name"])])),c("div",Ke,[c("div",He,[e.content?(x(),o("p",Pe,v(e.content),1)):r("",!0),e.attachments&&e.attachments.length>0?(x(),o("div",Je,[ja(e.attachments).length>0?(x(),o("div",Qe,[u(W,{"image-ids":(l=e.attachments,ja(l).map(e=>e.id)),"show-index":!1,columns:4,"max-width":120,onImageClick:s=>(async(e,s)=>{await ta.handleImageClick(e,s)})(e.attachments,s)},null,8,["image-ids","onImageClick"])])):r("",!0),Wa(e.attachments).length>0?(x(),o("div",Xe,[(x(!0),o(p,null,h(Wa(e.attachments),e=>{return x(),o("div",{key:e.id,class:"attachment-item clickable",onClick:s=>(async e=>{const s=await F.getAttachmentDownloadUrl(e.id)
if(!s)return
const a=document.createElement("a")
a.href=s.url,a.download=s.name,a.target="_blank",document.body.appendChild(a),a.click(),document.body.removeChild(a)})(e)},[u(B,{icon:"mdi:file",class:"attachment-icon"}),c("span",Ze,v(e.name),1),c("span",es,v((s=e.size,s<1024?`${s} B`:s<1048576?`${(s/1024).toFixed(1)} KB`:`${(s/1048576).toFixed(1)} MB`)),1),u(B,{icon:"mdi:download",class:"attachment-action-icon",title:"下载文件"})],8,Ye)
var s}),128))])):r("",!0)])):r("",!0),c("div",ss,[c("span",as,v(Ga(e.sendTime)),1),e.sent&&"sending"===e.status?(x(),o("span",ts,[u(B,{icon:"mdi:loading",class:"status-icon spinning"})])):e.sent&&"sent"===e.status?(x(),o("span",ns,[u(B,{icon:"mdi:check-circle",class:"status-icon"})])):e.sent&&"failed"===e.status?(x(),o("span",ls,[u(B,{icon:"mdi:alert-circle",class:"status-icon",title:"发送失败"})])):r("",!0),!e.sent||e.recalled||e.deleted||"sending"===e.status?r("",!0):(x(),o("div",is,[c("button",{class:"message-action-btn",onClick:f(s=>(async e=>{if(!Ds.show)return
const s=await Ds.show({title:"撤回消息",message:"确定要撤回这条消息吗?",confirmText:"撤回",cancelText:"取消"})
if(s&&null!==s)try{await Ws(e.id)
const s=pa.value
if(s){const a=s.messages.findIndex(s=>s.id===e.id);-1!==a&&s.messages.splice(a,1)}Ss("消息已撤回",2e3)}catch(a){xs(a.message||"撤回消息失败",2e3)}})(e),["stop"]),title:"撤回"},[u(B,{icon:"mdi:undo"})],8,os),c("button",{class:"message-action-btn",onClick:f(s=>(async e=>{if(!Ds.show)return
const s=await Ds.show({title:"删除消息",message:"确定要删除这条消息吗?删除后双方都看不到这条消息。",confirmText:"删除",cancelText:"取消"})
if(s&&null!==s)try{await Gs(e.id,!0),Ss("消息已删除",2e3)}catch(a){xs(a.message||"删除消息失败",2e3)}})(e),["stop"]),title:"删除"},[u(B,{icon:"mdi:delete-outline"})],8,cs)]))])])]),e.sent?(x(),o("div",us,[u(O,{"user-id":(null==(s=k(Bs))?void 0:s.id)||(null==(a=k(Bs))?void 0:a._id),"user-name":(null==(t=k(Bs))?void 0:t.nickName)||(null==(n=k(Bs))?void 0:n.username)||"我",width:40,height:40},null,8,["user-id","user-name"])])):r("",!0)],2))],64)}),128))],512),c("div",ds,[pa.value.blocked||pa.value.muted?(x(),o("div",rs,[u(B,{icon:pa.value.blocked?"mdi:block-helper":"mdi:bell-off-outline",class:"notice-icon"},null,8,["icon"]),c("span",null,v(pa.value.blocked?"该用户已被拉黑,无法发送消息":"已拒收该用户的消息"),1)])):(x(),o("div",vs,[c("div",ms,[c("button",{class:"toolbar-btn",onClick:Oa,disabled:Xs.value.length>=5,title:"添加图片最多5张每张10M以内"},[u(B,{icon:"mdi:image-outline"})],8,gs),c("button",{class:"toolbar-btn",onClick:za,disabled:Ys.value.length>=5,title:"添加文件最多5个每个20M以内"},[u(B,{icon:"mdi:file-outline"})],8,ps)]),b(c("textarea",{"onUpdate:modelValue":s[3]||(s[3]=e=>Qs.value=e),class:"message-textarea",placeholder:"输入消息...",rows:"3",maxlength:"2000",onKeydown:E(f(Ba,["ctrl"]),["enter"]),onPaste:Ua},null,40,hs),[[C,Qs.value]])])),pa.value.blocked||pa.value.muted||!(Xs.value.length>0||Ys.value.length>0)?r("",!0):(x(),o("div",fs,[(x(!0),o(p,null,h(Xs.value,(e,s)=>(x(),o("div",{key:`img-${s}`,class:"attachment-preview-item"},[c("img",{src:e.preview,alt:"预览"},null,8,ys),c("button",{class:"remove-attachment-btn",onClick:e=>(e=>{Xs.value.splice(e,1)})(s)},[u(B,{icon:"mdi:close"})],8,ks)]))),128)),(x(!0),o(p,null,h(Ys.value,(e,s)=>(x(),o("div",{key:`file-${s}`,class:"attachment-preview-item file"},[u(B,{icon:"mdi:file"}),c("span",ws,v(e.file.name),1),c("button",{class:"remove-attachment-btn",onClick:e=>(e=>{Ys.value.splice(e,1)})(s)},[u(B,{icon:"mdi:close"})],8,bs)]))),128))])),pa.value.blocked||pa.value.muted?r("",!0):(x(),o("div",Cs,[c("span",Is,v(Qs.value.length)+"/2000",1),c("button",{class:"send-btn",onClick:Ba,disabled:!ya.value},[u(B,{icon:"mdi:send",class:"btn-icon"}),s[19]||(s[19]=d(" 发送 ",-1))],8,Es)])),c("input",{ref_key:"imageInput",ref:Zs,type:"file",accept:"image/*",multiple:"",onChange:Ra,style:{display:"none"}},null,544),c("input",{ref_key:"fileInput",ref:ea,type:"file",multiple:"",onChange:Fa,style:{display:"none"}},null,544)])])):(x(),o("div",Ne,[u(B,{icon:"fluent-color:chat-32",class:"empty-icon"}),s[13]||(s[13]=c("p",{class:"empty-text"},"选择一个会话开始聊天",-1))]))])])]),u(j,{visible:k(ta).previewVisible.value,"images-info":k(ta).previewImagesInfo.value,"initial-index":k(ta).previewIndex.value,onClose:k(ta).closePreview,onIndexChange:s[4]||(s[4]=e=>k(ta).previewIndex.value=e)},null,8,["visible","images-info","initial-index","onClose"])]),u(z,{visible:k(Ds).visible.value,title:k(Ds).config.value.title||"",message:k(Ds).config.value.message||"",type:"danger"===k(Ds).config.value.type?"error":k(Ds).config.value.type||"warning","confirm-text":k(Ds).config.value.confirmText,"cancel-text":k(Ds).config.value.cancelText,"show-close-button":!0,"close-button-position":"top-right",onConfirm:k(Ds).handleConfirm,onCancel:k(Ds).handleCancel,onClose:k(Ds).handleClose},null,8,["visible","title","message","type","confirm-text","cancel-text","onConfirm","onCancel","onClose"]),u(R,{toasts:k(Ts),onRemove:k(As)},null,8,["toasts","onRemove"]),u(z,{visible:k($s).visible,title:k($s).text,loading:!0,simple:!0,"close-on-overlay-click":!1,"enable-keyboard":!1},null,8,["visible","title"])],64))}}),[["__scopeId","data-v-60e089c3"]])
export{Ms as default}

26
frontend/dist/assets/BYwQvuzJ.js vendored Normal file
View File

@@ -0,0 +1,26 @@
import{A as s}from"./BaSQ3xJt.js"
async function e(e){if(localStorage.getItem("token"))try{const t=e.filter(s=>s.hasIcon&&s.bundleId)
if(0===t.length)return
const n=t.map(s=>s.bundleId),c=50,a=[]
for(let s=0;s<n.length;s+=c)a.push(n.slice(s,s+c))
const i=await Promise.all(a.map(async(e,t)=>{var n
try{const t=await s.batchCheckAppIconsExist(e)
return t.success?{success:!0,missingBundleIds:(null==(n=t.data)?void 0:n.missingBundleIds)||[]}:{success:!1,missingBundleIds:[]}}catch(c){return{success:!1,missingBundleIds:[]}}})),r=[]
if(i.forEach((s,e)=>{s.success&&r.push(...s.missingBundleIds)}),0===r.length)return
const l=5
let o=0,u=0
const d=[],h=new Map
t.forEach(s=>{h.set(s.bundleId,s)})
for(let e=0;e<r.length;e+=l){const t=r.slice(e,e+l);(await Promise.allSettled(t.map(async e=>{try{const t=h.get(e),n=(null==t?void 0:t.name)||void 0,c=s.getAppIconUrl(e),a=await fetch(c)
if(!a.ok)throw new Error(`获取本地图标失败: HTTP ${a.status}`)
const i=await a.blob()
if(0===i.size)throw new Error("图标文件为空")
const r=new File([i],`${e}.png`,{type:i.type||"image/png"}),l=await s.syncAppIconToCloud(e,r,n)
if(l.success)return d.push(e),{bundleId:e,success:!0}
throw new Error(l.message||"上传失败")}catch(t){return{bundleId:e,success:!1,error:t.message||String(t)}}}))).forEach(s=>{"fulfilled"===s.status&&s.value.success?o++:u++})}if(d.length>0){await new Promise(s=>setTimeout(s,500))
const e=[]
for(let s=0;s<d.length;s+=c)e.push(d.slice(s,s+c))
const t=await Promise.all(e.map(async(e,t)=>{var n
try{const t=await s.batchCheckAppIconsExist(e)
return t.success?{success:!0,missingBundleIds:(null==(n=t.data)?void 0:n.missingBundleIds)||[]}:{success:!1,missingBundleIds:[]}}catch(c){return{success:!1,missingBundleIds:[]}}})),n=[]
t.forEach((s,e)=>{s.success&&n.push(...s.missingBundleIds)}),n.length}}catch(t){}}export{e as b}

1026
frontend/dist/assets/BaSQ3xJt.js vendored Normal file

File diff suppressed because one or more lines are too long

36
frontend/dist/assets/Bh_hyNGy.js vendored Normal file
View File

@@ -0,0 +1,36 @@
import{d as e,r as a,c as s,o as n,a as o,e as t,m as l,b as c,z as i,F as d,B as u,l as r,u as p,n as m,C as v,P as b,t as y,H as f,h as I}from"./CdD4XvnD.js"
import{c as h,_ as g,T as C,A as k,a as E}from"./BaSQ3xJt.js"
import{P}from"./CU438sNO.js"
import{g as w,F as q}from"./6K6b4Qy_.js"
const U={class:"discussion-forums-page"},L={class:"page-container"},A={key:0,class:"loading"},F={key:1,class:"error"},N={key:2,class:"builtin-forums-section"},_={key:0,class:"forums-grid builtin-grid"},j=["onClick"],R={class:"forum-icon-wrapper"},D={class:"forum-icon builtin-icon"},M=["src","alt","onError"],S={key:2,class:"icon-placeholder"},B={class:"forum-info"},G={class:"forum-name"},T={key:0,class:"forum-description"},$={key:1,class:"forum-stats"},x={class:"stat-item"},z={class:"stat-item"},H={key:1,class:"empty-state"},O={key:3,class:"app-forums-section"},J={key:0,class:"empty"},K={key:1},Q={class:"forums-grid"},V=["onClick"],W={class:"forum-icon-wrapper"},X={class:"forum-icon"},Y=["src","alt","onLoad","onError"],Z={class:"forum-info"},ee={class:"forum-name"},ae={class:"forum-bundle-id"},se={key:0,class:"forum-status-uninstalled"},ne={key:1,class:"forum-stats"},oe={class:"stat-item"},te={class:"stat-item"},le={key:2,class:"forum-status-disabled"},ce=["onClick","disabled"],ie=E(e({__name:"DiscussionForums",setup(e){const E=f(),ie=a([]),de=a([]),ue=a(!0),re=a(""),pe=h(),me=a(1),ve=a(20),be=s(()=>ie.value.length),ye=s(()=>Math.ceil(be.value/ve.value)),fe=s(()=>{const e=(me.value-1)*ve.value,a=e+ve.value
return ie.value.slice(e,a)}),Ie=async()=>{try{const e=(await k.getAllForums()).filter(e=>"builtin"===e.type||"created"===e.type)
e.sort((e,a)=>"builtin"===e.type&&"created"===a.type?-1:"created"===e.type&&"builtin"===a.type?1:e.name.localeCompare(a.name)),de.value=e,de.value.forEach(e=>{"created"===e.type&&e.iconId&&he(e.bundleId,e.iconId)})}catch(e){de.value=[]}},he=async(e,a)=>{const s=de.value.find(a=>a.bundleId===e)
if(s)try{const e=await k.getIcon(a,80,128,128)
s.iconUrl=e,s.iconLoaded=!0,s.iconError=!1}catch(n){s.iconError=!0,s.iconLoaded=!0}},ge=async(e=!1)=>{if(e){ue.value=!0,re.value=""
try{const e=(await k.getApps()).filter(e=>e.path&&e.path.length>0),a=new Set(e.map(e=>e.bundleId)),s=new Map
e.forEach(e=>{s.set(e.bundleId,e)})
const n=e.map(e=>e.bundleId),o=await k.getForumPermissionsBatch(n),t=new Map
o.forEach(e=>{t.set(e.bundleId,e)})
const l=[]
e.forEach(e=>{const a=t.get(e.bundleId)
a&&!a.enabled&&l.push({bundleId:e.bundleId,name:e.name,iconUrl:k.getAppIconUrl(e.bundleId),iconLoaded:!1,iconError:!1,enabled:!1,installed:!0,requested:a.hasRequest&&a.requestStatus===q.PENDING,todayPostCount:a.todayPostCount??0,totalPostCount:a.totalPostCount??0})})
let c=[],i=1,d=!0
for(;d;){const e=await k.getEnabledForums(i,ve.value)
c.push(...e.permissions),e.permissions.length<ve.value||0===e.permissions.length?d=!1:i++}const u=[]
c.forEach(e=>{const n=a.has(e.bundleId),o=s.get(e.bundleId)
u.push({bundleId:e.bundleId,name:e.appName,iconUrl:n&&o?k.getAppIconUrl(e.bundleId):"",iconLoaded:!1,iconError:!1,enabled:e.enabled,installed:n,iconId:e.iconId,requested:e.hasRequest&&e.requestStatus===q.PENDING,todayPostCount:e.todayPostCount??0,totalPostCount:e.totalPostCount??0})}),u.sort((e,a)=>e.installed&&!a.installed?-1:!e.installed&&a.installed?1:e.name.localeCompare(a.name))
const r=[...u,...l]
ie.value=r,r.forEach(e=>{!e.installed&&e.iconId&&(async(e,a)=>{const s=ie.value.find(a=>a.bundleId===e)
if(s)try{const e=await k.getIcon(a,80,128,128)
s.iconUrl=e,s.iconLoaded=!0,s.iconError=!1}catch(n){s.iconError=!0,s.iconLoaded=!0}})(e.bundleId,e.iconId)})}catch(a){re.value="加载应用列表失败,请检查服务是否运行",ie.value=[]}finally{ue.value=!1}}},Ce=e=>{const a=de.value.find(a=>a.bundleId===e)
if(a&&("builtin"===a.type||"created"===a.type))return void E.push(`/discussion/${e}`)
const s=ie.value.find(a=>a.bundleId===e)
s&&s.enabled&&E.push(`/discussion/${e}`)},ke=e=>{me.value=e}
return n(async()=>{await Ie(),await ge(!0)}),(e,a)=>(I(),o(d,null,[t("div",U,[t("div",L,[a[7]||(a[7]=t("div",{class:"page-header"},[t("h1",{class:"page-title"},"讨论区"),t("p",{class:"page-description"},"浏览和参与已安装应用的讨论")],-1)),ue.value?(I(),o("div",A,[...a[1]||(a[1]=[t("div",{class:"spinner"},null,-1),t("p",null,"加载板块中...",-1)])])):re.value?(I(),o("div",F,[t("p",null,i(re.value),1),t("button",{onClick:a[0]||(a[0]=()=>{Ie(),ge(!0)}),class:"btn btn-primary"},"重试")])):c("",!0),ue.value||re.value?c("",!0):(I(),o("div",N,[a[3]||(a[3]=t("h2",{class:"section-title"},"内置板块",-1)),de.value.length>0?(I(),o("div",_,[(I(!0),o(d,null,u(de.value,e=>(I(),o("div",{key:e.bundleId,class:"forum-card builtin-forum-card",onClick:a=>Ce(e.bundleId)},[t("div",R,[t("div",D,["builtin"===e.type?(I(),r(g,{key:0,icon:p(w)(e.bundleId),class:"builtin-icon-svg"},null,8,["icon"])):"created"===e.type&&e.iconUrl?(I(),o("img",{key:1,src:e.iconUrl,alt:e.name,class:"created-icon-img",onError:a=>e.iconError=!0},null,40,M)):(I(),o("div",S,i(e.name.charAt(0).toUpperCase()),1))])]),t("div",B,[t("h3",G,i(e.name),1),e.description?(I(),o("p",T,i(e.description),1)):c("",!0),void 0!==e.todayPostCount||void 0!==e.totalPostCount?(I(),o("div",$,[t("span",x,[l(g,{icon:"fluent-color:news-28",class:"stat-icon"}),t("span",null,"今日发帖:"+i(e.todayPostCount??0),1)]),t("span",z,[l(g,{icon:"fluent-color:reward-24",class:"stat-icon"}),t("span",null,"总发帖数:"+i(e.totalPostCount??0),1)])])):c("",!0)])],8,j))),128))])):(I(),o("div",H,[l(g,{icon:"mdi:forum-outline",class:"empty-icon"}),a[2]||(a[2]=t("p",null,"暂无内置板块",-1))]))])),ue.value||re.value?c("",!0):(I(),o("div",O,[a[6]||(a[6]=t("h2",{class:"section-title"},"应用板块",-1)),0===ie.value.length?(I(),o("div",J,[l(g,{icon:"fluent-color:chat-bubbles-question-24",class:"empty-icon"}),a[4]||(a[4]=t("p",null,"没有找到讨论板块",-1)),a[5]||(a[5]=t("p",{class:"empty-hint"},"系统中没有已安装的应用",-1))])):(I(),o("div",K,[t("div",Q,[(I(!0),o(d,null,u(fe.value,e=>(I(),o("div",{key:e.bundleId,class:m(["forum-card",{disabled:!e.enabled}]),onClick:a=>e.enabled?Ce(e.bundleId):null},[t("div",W,[t("div",X,[e.iconUrl?v((I(),o("img",{key:0,src:e.iconUrl,alt:e.name,onLoad:a=>(e=>{const a=ie.value.find(a=>a.bundleId===e)
a&&(a.iconLoaded=!0)})(e.bundleId),onError:a=>(e=>{const a=ie.value.find(a=>a.bundleId===e)
a&&(a.iconError=!0,a.iconLoaded=!0)})(e.bundleId)},null,40,Y)),[[b,e.iconLoaded&&!e.iconError]]):c("",!0),v(t("div",{class:"icon-placeholder"},i(e.name.charAt(0).toUpperCase()),513),[[b,!e.iconUrl||!e.iconLoaded||e.iconError]])])]),t("div",Z,[t("h3",ee,i(e.name),1),t("p",ae,i(e.bundleId),1),e.installed?c("",!0):(I(),o("p",se,"未安装")),!e.enabled||void 0===e.todayPostCount&&void 0===e.totalPostCount?c("",!0):(I(),o("div",ne,[t("span",oe,[l(g,{icon:"fluent-color:news-28",class:"stat-icon"}),t("span",null,"今日发帖:"+i(e.todayPostCount??0),1)]),t("span",te,[l(g,{icon:"fluent-color:reward-24",class:"stat-icon"}),t("span",null,"总发帖数:"+i(e.totalPostCount??0),1)])])),e.enabled?c("",!0):(I(),o("p",le,"未开放")),!e.enabled&&e.installed?(I(),o("button",{key:3,class:"request-open-btn",onClick:y(a=>(async e=>{var a,s
const n=ie.value.find(a=>a.bundleId===e)
if(n&&!n.requested)try{const a=await k.requestForumOpen(e,n.name)
a.success?(n.requested=!0,pe.success("申请提交成功,等待管理员审核")):pe.error(a.message||"申请失败,请稍后重试")}catch(o){const e=(null==(s=null==(a=o.response)?void 0:a.data)?void 0:s.message)||o.message||"申请失败,请稍后重试"
pe.error(e)}})(e.bundleId),["stop"]),disabled:e.requested},i(e.requested?"等待申请结果中":"申请开放"),9,ce)):c("",!0)])],10,V))),128))]),ye.value>1?(I(),r(P,{key:0,"current-page":me.value,"total-pages":ye.value,total:be.value,onPageChange:ke},null,8,["current-page","total-pages","total"])):c("",!0)]))]))])]),l(C,{toasts:p(pe).toasts.value,onRemove:p(pe).removeToast},null,8,["toasts","onRemove"])],64))}}),[["__scopeId","data-v-2d729ae4"]])
export{ie as default}

1
frontend/dist/assets/Bhvo_zKD.css vendored Normal file

File diff suppressed because one or more lines are too long

19
frontend/dist/assets/Birl3CuV.js vendored Normal file
View File

@@ -0,0 +1,19 @@
import{d as e,r as a,c as l,w as s,Q as u,l as t,m as n,p as i,q as o,a as r,b as v,e as c,t as d,v as m,n as b,z as p,T as f,h as k,k as y}from"./CdD4XvnD.js"
import{b as g,_ as h,a as I}from"./BaSQ3xJt.js"
import{u as U}from"./4xYhABhf.js"
const w={key:0,class:"background-avatar-blur"},R=["src"],x={class:"card-content"},T={class:"content-layout"},L={class:"avatar-wrapper"},j={class:"user-info"},C={class:"user-name-row"},_={class:"user-name"},O={class:"user-username"},N={class:"action-buttons"},M=I(e({__name:"UserProfileCard",props:{visible:{type:Boolean,default:!1},userId:{default:""},userName:{default:""},username:{default:""},position:{default:void 0},avatarUrl:{default:void 0},avatarId:{default:void 0}},emits:["close","sendMessage","blockUser","mouseEnter"],setup(e,{emit:I}){const M=e,W=I,$=a(null),q=a(null),z=a(""),B=a(!1),{isUserOnline:E,queryUsersOnlineStatus:H}=U(),P=l(()=>M.userId?E(M.userId).value:null),Q=l(()=>B.value&&""!==z.value),S=a(!1)
let A=null
const D=l(()=>M.username||M.userId||"未知用户"),F=l(()=>S.value?"加载中...":!0===P.value?"在线":!1===P.value?"离线":"未知")
s(()=>P.value,e=>{S.value&&null!==e&&(S.value=!1,A&&(clearTimeout(A),A=null))}),s(()=>[M.visible,M.userId],async()=>{if(M.visible&&M.userId){if(S.value=!1,A&&(clearTimeout(A),A=null),null===P.value){S.value=!0
try{await H([M.userId]),A=window.setTimeout(()=>{P.value,S.value=!1,A=null},3e3)}catch(e){S.value=!1}}}else A&&(clearTimeout(A),A=null)},{immediate:!0})
const G=e=>{B.value=!0,z.value&&z.value.startsWith("blob:")&&URL.revokeObjectURL(z.value),z.value=URL.createObjectURL(e)}
s(()=>M.visible,e=>{e?B.value=!1:(B.value=!1,z.value&&z.value.startsWith("blob:")&&(URL.revokeObjectURL(z.value),z.value=""))})
const J=l(()=>M.position?q.value?{left:`${q.value.x}px`,top:`${q.value.y}px`}:{left:`${M.position.x}px`,top:`${M.position.y}px`}:{})
s(()=>[M.visible,M.position],()=>{q.value=null,M.visible&&M.position&&y(()=>{(()=>{if(!M.position||!$.value)return
const e=$.value.getBoundingClientRect(),a=e.width||260,l=e.height||240,s=16
let u=M.position.x,t=M.position.y
const n=window.innerWidth,i=window.innerHeight
u+a>n-s&&(u=n-a-s),u<s&&(u=s),t+l>i-s&&(t=M.position.y-s>=l?M.position.y-l-8:i-l-s),t<s&&(t=s),q.value={x:u,y:t}})()})},{immediate:!0})
const K=a(null),V=()=>{K.value&&(clearTimeout(K.value),K.value=null),W("close")},X=()=>{K.value&&(clearTimeout(K.value),K.value=null),W("mouseEnter")},Y=()=>{K.value=window.setTimeout(()=>{V()},300)},Z=()=>{W("sendMessage",M.userId,M.userName),V()},ee=()=>{W("blockUser",M.userId,M.userName),V()}
return u(()=>{z.value&&z.value.startsWith("blob:")&&URL.revokeObjectURL(z.value),A&&(clearTimeout(A),A=null)}),(a,l)=>(k(),t(f,{to:"body"},[n(i,{name:"card-fade"},{default:o(()=>[e.visible?(k(),r("div",{key:0,class:"card-overlay",onClick:V},[c("div",{ref_key:"cardContainerRef",ref:$,class:"card-container",style:m(J.value),onClick:l[0]||(l[0]=d(()=>{},["stop"])),onMouseenter:X,onMouseleave:Y},[c("div",{class:b(["avatar-background",{"has-avatar":Q.value}])},[Q.value?(k(),r("div",w,[z.value?(k(),r("img",{key:0,src:z.value,alt:"",class:"background-image"},null,8,R)):v("",!0)])):v("",!0),l[1]||(l[1]=c("div",{class:"background-overlay"},null,-1))],2),c("div",x,[c("div",T,[c("div",L,[n(g,{size:"large",userId:e.userId,"user-name":e.userName,"avatar-id":e.avatarId,width:80,height:80,"on-avatar-loaded":G},null,8,["userId","user-name","avatar-id"]),c("div",{class:b(["online-indicator",{online:P.value,offline:!1===P.value}])},null,2)]),c("div",j,[c("div",C,[c("div",_,p(e.userName),1),c("span",{class:b(["user-status",{online:P.value,offline:!1===P.value}])},[l[2]||(l[2]=c("span",{class:"status-dot"},null,-1)),c("span",null,p(F.value),1)],2)]),c("div",O,"@"+p(D.value),1),l[3]||(l[3]=c("div",{class:"user-info-backdrop"},null,-1))])]),c("div",N,[c("button",{class:"action-btn message-btn",onClick:Z},[n(h,{icon:"fluent-color:comment-multiple-32",class:"btn-icon"}),l[4]||(l[4]=c("span",null,"私信",-1))]),c("button",{class:"action-btn block-btn",onClick:ee},[n(h,{icon:"mdi:block-helper",class:"btn-icon"}),l[5]||(l[5]=c("span",null,"拉黑",-1))])])])],36)])):v("",!0)]),_:1})]))}}),[["__scopeId","data-v-83379e1d"]])
export{M as U}

13
frontend/dist/assets/BjWRJRwL.js vendored Normal file
View File

@@ -0,0 +1,13 @@
import{A as a}from"./RX4g3b5N.js"
import{_ as s,A as e,a as l}from"./BaSQ3xJt.js"
import{b as t}from"./BYwQvuzJ.js"
import{g as c,m as p,f as n}from"./Bt6cakcM.js"
import{d as r,r as o,o as i,a as u,e as d,b as v,z as m,m as y,F as g,B as h,l as f,h as b}from"./CdD4XvnD.js"
const k={class:"apps-page"},A={class:"page-container"},j={key:0,class:"loading"},_={key:1,class:"error"},w={key:2,class:"empty"},I={key:3,class:"apps-grid"},C={key:4,class:"list-footer"},x={class:"footer-stats"},z={class:"stat-item"},B={class:"stat-value"},F=l(r({__name:"Apps",setup(l){const r=o([]),F=o(!0),G=o(""),U=async()=>{F.value=!0,G.value=""
try{const s=await e.getApps()
let l=s
if(s.length>0)try{const a=s.map(a=>{let s=[]
return s=a.category?Array.isArray(a.category)?a.category.length>0?a.category:[c(a.bundleId)]:[a.category]:[c(a.bundleId)],{...a,category:s}}),t=await e.batchGetAppVersions(a)
t.success&&t.data&&(l=p(s,t.data)),l=await n(l)}catch(a){}r.value=l.sort((a,s)=>a.name.localeCompare(s.name)),t(r.value).catch(a=>{})}catch(a){G.value="加载应用列表失败,请检查服务是否运行",r.value=[]}finally{F.value=!1}},V=async a=>{await U()}
return i(()=>{U()}),(e,l)=>(b(),u("div",k,[d("div",A,[l[4]||(l[4]=d("div",{class:"page-header"},[d("h1",{class:"page-title"},"所有应用"),d("p",{class:"page-description"},"系统中所有已安装的应用")],-1)),F.value?(b(),u("div",j,[...l[0]||(l[0]=[d("div",{class:"spinner"},null,-1),d("p",null,"加载应用中...",-1)])])):G.value?(b(),u("div",_,[d("p",null,m(G.value),1),d("button",{onClick:U,class:"btn btn-primary"},"重试")])):0===r.value.length?(b(),u("div",w,[y(s,{icon:"fluent-color:apps-24",class:"empty-icon"}),l[1]||(l[1]=d("p",null,"没有找到应用",-1)),l[2]||(l[2]=d("p",{class:"empty-hint"},"系统中没有已安装的应用",-1))])):(b(),u("div",I,[(b(!0),u(g,null,h(r.value,s=>(b(),f(a,{key:s.bundleId,app:s,mode:"list",onUpdated:V},null,8,["app"]))),128))])),r.value.length>0?(b(),u("div",C,[d("div",x,[d("span",z,[l[3]||(l[3]=d("span",{class:"stat-label"},"总计:",-1)),d("span",B,m(r.value.length),1)])])])):v("",!0)])]))}}),[["__scopeId","data-v-39145265"]])
export{F as default}

1
frontend/dist/assets/BpjKJzQb.css vendored Normal file

File diff suppressed because one or more lines are too long

3
frontend/dist/assets/BqTCaadz.js vendored Normal file
View File

@@ -0,0 +1,3 @@
function t(t){const n=new Date(t),e=(new Date).getTime()-n.getTime(),r=Math.floor(e/6e4),o=Math.floor(e/36e5),a=Math.floor(e/864e5)
return r<1?"刚刚":r<60?`${r}分钟前`:o<24?`${o}小时前`:a<7?`${a}天前`:n.toLocaleDateString("zh-CN")}function n(t){return t.author.id}function e(t){return t.author.nickName||t.author.username||""}function r(t){return t.author.username}function o(t){return t.filter(t=>"image"===t.type)}function a(t){return t.filter(t=>"image"!==t.type)}function i(t){var n,e
return"string"==typeof t?t:t&&"object"==typeof t?(null==(n=t._id)?void 0:n.toString())||(null==(e=t.id)?void 0:e.toString())||t.toString():String(t)}function u(t){return o(t).map(t=>i(t))}export{e as a,r as b,o as c,a as d,u as e,t as f,n as g,i as h}

1
frontend/dist/assets/BrtFiXAq.css vendored Normal file

File diff suppressed because one or more lines are too long

18
frontend/dist/assets/Bt6cakcM.js vendored Normal file

File diff suppressed because one or more lines are too long

1
frontend/dist/assets/BtDwneFd.css vendored Normal file

File diff suppressed because one or more lines are too long

17
frontend/dist/assets/BvE56hIT.js vendored Normal file
View File

@@ -0,0 +1,17 @@
import{A as e}from"./BaSQ3xJt.js"
import"./CdD4XvnD.js"
const s=5242880
async function t(s,t,a){return await e.initChunkUpload(s,t,a)}async function a(s,t,a,n){return await e.uploadFileChunk(s,t,a,n)}async function n(s,t){return await e.completeChunkUpload(s,t)}async function c(e){const{file:c,chunkSize:o=s,onProgress:r,onChunkProgress:i}=e
if(c.size<=o)try{const e=encodeURIComponent(c.name),s=new File([c],e,{type:c.type}),t=new FormData
t.append("file",s)
const a=await fetch("/api/proxy",{method:"POST",headers:{"x-forward-by-qiuchenly":"/api/v1/attachments/file","x-forward-method-by-qiuchenly":"POST",Authorization:`Bearer ${localStorage.getItem("token")||""}`},body:t}),n=await a.json()
return n.success&&n.data?{success:!0,attachmentId:n.data.attachmentId}:{success:!1,message:n.message||"上传失败"}}catch(d){return{success:!1,message:d.message||"上传失败"}}const u=Math.ceil(c.size/o)
let m=0
try{const e=encodeURIComponent(c.name),s=await t(e,c.size,u)
if(!s.success||!s.uploadId)return{success:!1,message:s.message||"初始化分片上传失败"}
const d=s.uploadId
for(let t=0;t<u;t++){const e=t*o,s=Math.min(e+o,c.size),n=c.slice(e,s),h=await a(d,t,n,e=>{if(i&&i(t,e),r){const s=Math.round((m+e/100)/u*100)
r(s)}})
if(!h.success)return{success:!1,message:`分片 ${t+1}/${u} 上传失败: ${h.message}`}
m++,r&&r(Math.round(m/u*100))}const h=await n(d,e)
return h.success?{success:!0,attachmentId:h.attachmentId}:{success:!1,message:h.message||"完成分片上传失败"}}catch(d){return{success:!1,message:d.message||"分片上传失败"}}}export{c as chunkUpload,n as completeChunkUpload,t as initChunkUpload,a as uploadChunk}

1
frontend/dist/assets/BypKMn6v.css vendored Normal file
View File

@@ -0,0 +1 @@
.avatar-upload[data-v-8cef3395]{position:relative;display:inline-block}.avatar-preview[data-v-8cef3395]{width:var(--v6174d950);height:var(--v6174d950);border-radius:50%;overflow:hidden;border:2px solid var(--border-light);background:var(--bg-secondary);cursor:pointer;transition:all .2s ease;display:flex;align-items:center;justify-content:center}.avatar-preview[data-v-8cef3395]:hover{border-color:var(--primary-color);box-shadow:0 0 0 4px rgba(var(--primary-rgb),.1)}.avatar-preview[data-v-8cef3395] .avatar-wrapper{width:100%!important;height:100%!important;min-width:100%!important;min-height:100%!important;max-width:100%!important;max-height:100%!important;border-radius:50%!important;border:none!important}.avatar-preview .preview-image[data-v-8cef3395]{width:100%;height:100%;object-fit:cover;border-radius:50%}.avatar-preview .cloud-image[data-v-8cef3395]{display:none}.avatar-menu[data-v-8cef3395]{position:fixed;z-index:10002;background:var(--bg-card);border:1px solid var(--border-light);border-radius:var(--radius-md);box-shadow:0 4px 16px #00000026,0 2px 8px #0000001a;padding:6px;display:flex;flex-direction:column;gap:4px}.avatar-menu-item[data-v-8cef3395]{display:flex;align-items:center;justify-content:center;padding:10px;border:none;border-radius:var(--radius-sm);background:transparent;color:var(--text-primary);cursor:pointer;transition:all .2s ease;width:40px;height:40px;min-width:40px;min-height:40px}.avatar-menu-item[data-v-8cef3395]:hover:not(:disabled):not(.uploading){background:var(--bg-secondary);color:var(--primary-color)}.avatar-menu-item[data-v-8cef3395]:disabled{opacity:.5;cursor:not-allowed}.avatar-menu-item.danger[data-v-8cef3395]{color:#ef4444}.avatar-menu-item.danger[data-v-8cef3395]:hover:not(:disabled){background:#ef44441a;color:#ef4444}.avatar-menu-item.uploading[data-v-8cef3395]{justify-content:center;color:var(--text-secondary);cursor:default}.avatar-menu-item[data-v-8cef3395] svg{font-size:20px;width:20px;height:20px}.spinner-small[data-v-8cef3395]{width:16px;height:16px;border:2px solid var(--border-light);border-top-color:var(--primary-color);border-radius:50%;animation:spin-8cef3395 .6s linear infinite}@keyframes spin-8cef3395{to{transform:rotate(360deg)}}.avatar-menu-fade-enter-active[data-v-8cef3395],.avatar-menu-fade-leave-active[data-v-8cef3395]{transition:opacity .2s ease,transform .2s ease}.avatar-menu-fade-enter-from[data-v-8cef3395],.avatar-menu-fade-leave-to[data-v-8cef3395]{opacity:0;transform:scale(.95) translateY(-4px)}

16
frontend/dist/assets/C2sz-FFd.js vendored Normal file
View File

@@ -0,0 +1,16 @@
import{d as s,r as a,w as l,l as e,m as o,p as t,q as n,a as i,b as c,e as d,g as r,z as u,F as g,B as m,n as v,t as p,T as f,h,k as y}from"./CdD4XvnD.js"
import{_ as b,a as k,A as S}from"./BaSQ3xJt.js"
const w={class:"modal-header"},C={class:"modal-title"},$={class:"modal-body"},M={class:"log-time"},_={class:"log-message"},B={key:0,class:"log-line loading"},I={class:"modal-footer"},T={class:"footer-status"},L={key:0,class:"status-success"},j={key:1,class:"status-error"},E={key:2,class:"status-loading"},V={class:"footer-actions"},A=k(s({__name:"LogModal",props:{visible:{type:Boolean},title:{default:"操作日志"},logs:{default:()=>[]},loading:{type:Boolean,default:!1},status:{default:"idle"},canClose:{type:Boolean,default:!0}},emits:["close"],setup(s,{emit:k}){const S=s,A=k,D=a(null),H=()=>{D.value&&(D.value.scrollTop=D.value.scrollHeight)}
l(()=>S.logs,async()=>{await y(),H()},{deep:!0,flush:"post"}),l(()=>S.loading,async()=>{await y(),H()})
const R=()=>{switch(S.status){case"success":return"mdi:check-circle"
case"error":return"mdi:alert-circle"
case"loading":return"mdi:loading"
default:return"mdi:information"}},q=s=>s.level?`log-${s.level}`:"log-info",x=()=>{A("close")},z=()=>{S.canClose&&x()}
return(a,l)=>(h(),e(f,{to:"body"},[o(t,{name:"modal"},{default:n(()=>[s.visible?(h(),i("div",{key:0,class:"modal-overlay",onClick:z},[d("div",{class:"modal-container",onClick:l[0]||(l[0]=p(()=>{},["stop"]))},[d("div",w,[d("h2",C,[o(b,{icon:R(),class:"modal-icon"},null,8,["icon"]),r(" "+u(s.title),1)]),s.canClose?(h(),i("button",{key:0,onClick:x,class:"modal-close","aria-label":"关闭"},[o(b,{icon:"mdi:close"})])):c("",!0)]),d("div",$,[d("div",{ref_key:"logContainer",ref:D,class:"log-container"},[(h(!0),i(g,null,m(s.logs,(s,a)=>(h(),i("div",{key:a,class:v(["log-line",q(s)])},[d("span",M,u(s.time),1),d("span",_,u(s.message),1)],2))),128)),s.loading?(h(),i("div",B,[...l[1]||(l[1]=[d("div",{class:"spinner-small"},null,-1),d("span",{class:"log-message"},"处理中...",-1)])])):c("",!0)],512)]),d("div",I,[d("div",T,["success"===s.status?(h(),i("span",L,[o(b,{icon:"mdi:check-circle"}),l[2]||(l[2]=r(" 操作成功 ",-1))])):"error"===s.status?(h(),i("span",j,[o(b,{icon:"mdi:alert-circle"}),l[3]||(l[3]=r(" 操作失败 ",-1))])):s.loading?(h(),i("span",E,[...l[4]||(l[4]=[d("div",{class:"spinner-small"},null,-1),r(" 处理中... ",-1)])])):c("",!0)]),d("div",V,[s.canClose?(h(),i("button",{key:0,onClick:x,class:"btn btn-outline"}," 关闭 ")):c("",!0)])])])])):c("",!0)]),_:1})]))}}),[["__scopeId","data-v-f5d3fd24"]])
function D(s){const l=a(!1),{onRefresh:e}=s,{modalVisible:o,modalTitle:t,logs:n,modalStatus:i,addLog:c,showModal:d,hideModal:r,setSuccess:u,setError:g}=function(){const s=a(!1),l=a(""),e=a([]),o=a("idle")
return{modalVisible:s,modalTitle:l,logs:e,modalStatus:o,addLog:(s,a="info")=>{const l=new Date,o=`${l.getHours().toString().padStart(2,"0")}:${l.getMinutes().toString().padStart(2,"0")}:${l.getSeconds().toString().padStart(2,"0")}`
e.value.push({time:o,message:s,level:a})},showModal:a=>{l.value=a,e.value=[],o.value="loading",s.value=!0},hideModal:()=>{s.value=!1},setSuccess:()=>{o.value="success"},setError:()=>{o.value="error"}}}()
return{updating:l,modalVisible:o,modalTitle:t,logs:n,modalStatus:i,handleUpdate:async s=>{l.value=!0,d("injected"===s.status?"更新应用":"注入应用"),c(`开始处理应用: ${s.name}`),c(`Bundle ID: ${s.bundleId}`),c(`当前版本: ${s.version}`)
try{c("正在连接到服务器...")
const a=await S.updateApp(s.bundleId)
a.logs&&a.logs.length>0&&a.logs.forEach(s=>{c(s,"info")}),a.success?(c("操作成功完成!","success"),u(),c("正在后台刷新应用状态..."),e(s.bundleId).catch(s=>{c("后台刷新应用状态失败,但操作已成功完成","warning")})):(c(`操作失败: ${a.errorMessage||"更新失败"}`,"error"),g())}catch(a){c(`网络错误: ${a}`,"error"),g()}finally{l.value=!1}},handleReinstall:async()=>{},hideModal:r}}export{A as L,D as u}

1
frontend/dist/assets/C4ACk0IZ.css vendored Normal file

File diff suppressed because one or more lines are too long

251
frontend/dist/assets/C78NAjFy.js vendored Normal file

File diff suppressed because one or more lines are too long

1
frontend/dist/assets/C7H6_04j.css vendored Normal file

File diff suppressed because one or more lines are too long

4
frontend/dist/assets/CFhT0fBm.js vendored Normal file
View File

@@ -0,0 +1,4 @@
function t(t){if(!t||0===t||isNaN(t))return"0 B"
if(t>=Math.pow(1024,5)||t>Number.MAX_SAFE_INTEGER)return"未知"
const o=["B","KB","MB","GB","TB"],r=Math.floor(Math.log(t)/Math.log(1024)),a=Math.min(r,o.length-1)
return`${(t/Math.pow(1024,a)).toFixed(2)} ${o[a]}`}function o(o){return o<=0?"--":`${t(o)}/s`}function r(t){return t<=0?"完成":t>=3600?`${Math.floor(t/3600)}小时${Math.floor(t%3600/60)}分钟`:t>=60?`${Math.floor(t/60)}${Math.floor(t%60)}`:`${Math.floor(t)}`}function a(t){return isNaN(t)||t<0?"0.00%":t>=100?"100.00%":`${t.toFixed(2)}%`}export{t as a,o as b,r as c,a as f}

1
frontend/dist/assets/CGP9h12b.css vendored Normal file

File diff suppressed because one or more lines are too long

9
frontend/dist/assets/CNsXoenb.js vendored Normal file
View File

@@ -0,0 +1,9 @@
import{d as a,r as t,w as e,o as s,a as i,F as c,B as n,n as m,e as l,t as o,m as r,z as d,h as v}from"./CdD4XvnD.js"
import{_ as p,A as h,a as k}from"./BaSQ3xJt.js"
const g={class:"edit-attachments-list"},y=["onClick"],u=["src","alt"],C={key:1,class:"image-loading"},f=["onClick"],b={class:"attachment-name"},w=["onClick"],_=k(a({__name:"EditAttachmentPreview",props:{attachments:{}},emits:["remove","imageClick"],setup(a,{emit:k}){const _=a,A=k,E=t({}),j=async()=>{const a=_.attachments.filter(a=>"image"===a.type)
await Promise.all(a.map(a=>(async a=>{if(!E.value[a])try{const t=await h.getAttachmentImage(a)
t&&(E.value[a]=t)}catch(t){}})(a.id)))}
e(()=>_.attachments,()=>{E.value={},j()},{immediate:!0}),s(()=>{j()})
const I=a=>{a.target.style.display="none"}
return(t,e)=>(v(),i("div",g,[(v(!0),i(c,null,n(a.attachments,a=>(v(),i("div",{key:a.id,class:m(["edit-attachment-item",{"image-preview":"image"===a.type}])},["image"===a.type?(v(),i("div",{key:0,class:"edit-attachment-preview",onClick:()=>A("imageClick",a.id)},[E.value[a.id]?(v(),i("img",{key:0,src:E.value[a.id]||"",alt:a.name,onError:I,class:"clickable-image"},null,40,u)):(v(),i("div",C,[...e[0]||(e[0]=[l("div",{class:"spinner"},null,-1)])])),l("button",{class:"remove-attachment-btn",onClick:o(t=>A("remove",a.id),["stop"])},[r(p,{icon:"mdi:close"})],8,f)],8,y)):(v(),i(c,{key:1},[r(p,{icon:"mdi:file",class:"attachment-icon"}),l("span",b,d(a.name),1),l("button",{class:"remove-attachment-btn",onClick:e=>t.$emit("remove",a.id)},[r(p,{icon:"mdi:close"})],8,w)],64))],2))),128))]))}}),[["__scopeId","data-v-b3def6f7"]])
export{_ as E}

45
frontend/dist/assets/COGASUq6.js vendored Normal file

File diff suppressed because one or more lines are too long

1
frontend/dist/assets/CQzF1NmX.css vendored Normal file
View File

@@ -0,0 +1 @@
.apps-page[data-v-39145265]{width:100%;min-height:100%}.page-container[data-v-39145265]{width:100%}.page-header[data-v-39145265]{margin-bottom:32px}.page-title[data-v-39145265]{margin:0 0 8px;font-size:28px;font-weight:600;color:var(--text-primary)}.page-description[data-v-39145265]{margin:0;font-size:14px;color:var(--text-secondary)}.apps-grid[data-v-39145265]{display:grid;grid-template-columns:repeat(auto-fill,200px);gap:20px;margin-bottom:40px;justify-content:center}.loading[data-v-39145265],.error[data-v-39145265],.empty[data-v-39145265]{text-align:center;padding:60px 0}.loading .spinner[data-v-39145265],.error .spinner[data-v-39145265],.empty .spinner[data-v-39145265]{width:40px;height:40px;border:3px solid var(--border-light);border-top:3px solid var(--primary-color);border-radius:50%;animation:spin-39145265 .8s linear infinite;margin:0 auto 16px}.loading p[data-v-39145265],.error p[data-v-39145265],.empty p[data-v-39145265]{color:var(--text-secondary);font-size:14px;margin-bottom:8px}.loading .empty-hint[data-v-39145265],.error .empty-hint[data-v-39145265],.empty .empty-hint[data-v-39145265]{font-size:12px;color:var(--text-tertiary)}@keyframes spin-39145265{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.empty-icon[data-v-39145265]{font-size:64px;margin-bottom:16px;opacity:.3;color:var(--text-tertiary)}.list-footer[data-v-39145265]{padding:20px 0;border-top:1px solid var(--border-light)}.footer-stats[data-v-39145265]{display:flex;gap:24px;justify-content:center}.footer-stats .stat-item[data-v-39145265]{display:flex;gap:8px}.footer-stats .stat-item .stat-label[data-v-39145265]{color:var(--text-secondary);font-size:14px}.footer-stats .stat-item .stat-value[data-v-39145265]{color:var(--text-primary);font-weight:600;font-size:14px}.btn[data-v-39145265]{padding:8px 16px;border:none;border-radius:4px;font-size:14px;font-weight:600;cursor:pointer;transition:all .2s ease}.btn.btn-primary[data-v-39145265]{background:var(--primary-color);color:#fff}.btn.btn-primary[data-v-39145265]:hover{opacity:.9}

5
frontend/dist/assets/CU438sNO.js vendored Normal file
View File

@@ -0,0 +1,5 @@
import{d as a,a as t,b as e,e as s,g as n,z as o,F as r,h as g}from"./CdD4XvnD.js"
import{a as c}from"./BaSQ3xJt.js"
const i={key:0,class:"pagination"},l=["disabled"],d={class:"pagination-info"},P=["disabled"],p=c(a({__name:"Pagination",props:{currentPage:{},totalPages:{},total:{}},emits:["page-change"],setup(a,{emit:c}){const p=a,u=c,b=()=>{p.currentPage>1&&u("page-change",p.currentPage-1)},m=()=>{p.currentPage<p.totalPages&&u("page-change",p.currentPage+1)}
return(c,p)=>a.totalPages>1?(g(),t("div",i,[s("button",{onClick:b,disabled:1===a.currentPage,class:"btn btn-sm"}," 上一页 ",8,l),s("span",d,[n(" 第 "+o(a.currentPage)+" 页,共 "+o(a.totalPages)+" 页",1),void 0!==a.total?(g(),t(r,{key:0},[n("(共 "+o(a.total)+" 条)",1)],64)):e("",!0)]),s("button",{onClick:m,disabled:a.currentPage>=a.totalPages,class:"btn btn-sm"}," 下一页 ",8,P)])):e("",!0)}}),[["__scopeId","data-v-fde42dc3"]])
export{p as P}

4
frontend/dist/assets/C_POYLAU.js vendored Normal file
View File

@@ -0,0 +1,4 @@
import{r as e,c as l}from"./CdD4XvnD.js"
function u(){const u=e(!1),a=e(void 0),v=e(""),t=e(""),o=e(""),r=e(void 0),n=e(null),s=e(null),i=l(()=>r.value)
return{showUserMenu:u,userMenuPosition:a,selectedUserId:v,selectedUserName:t,selectedUserUsername:o,userAvatarId:i,handleAuthorHover:(e,l,s,i,d)=>{n.value&&clearTimeout(n.value),v.value=l,t.value=s,o.value=i,d&&""!==d.trim()?r.value=d:r.value=void 0,n.value=window.setTimeout(()=>{const l=e.target.getBoundingClientRect()
a.value={x:l.left-100,y:l.bottom+8},u.value=!0},200)},handleAuthorLeave:()=>{n.value&&(clearTimeout(n.value),n.value=null),s.value&&(clearTimeout(s.value),s.value=null),s.value=window.setTimeout(()=>{u.value&&(u.value=!1)},200)},handleCardMouseEnter:()=>{s.value&&(clearTimeout(s.value),s.value=null)},handleCloseUserMenu:()=>{n.value&&(clearTimeout(n.value),n.value=null),s.value&&(clearTimeout(s.value),s.value=null),u.value=!1,r.value=void 0,o.value=""}}}export{u}

1
frontend/dist/assets/Cax_altD.css vendored Normal file

File diff suppressed because one or more lines are too long

1171
frontend/dist/assets/CdD4XvnD.js vendored Normal file

File diff suppressed because it is too large Load Diff

1
frontend/dist/assets/CiNNZePN.css vendored Normal file
View File

@@ -0,0 +1 @@
.image-item[data-v-3d7a38bc]{position:relative;width:100%;aspect-ratio:1;border-radius:6px;overflow:hidden;background:var(--bg-secondary);border:1px solid var(--border-light);cursor:pointer;transition:all .2s ease;min-width:0;align-self:start}.image-item[data-v-3d7a38bc]:hover{transform:translateY(-2px);box-shadow:0 4px 12px #00000026;border-color:var(--primary-color)}.image-wrapper[data-v-3d7a38bc]{width:100%;height:100%;position:relative;display:flex;align-items:center;justify-content:center;overflow:hidden}.image-content[data-v-3d7a38bc]{width:100%;height:100%;object-fit:cover;transition:transform .3s ease}.image-item:hover .image-content[data-v-3d7a38bc]{transform:scale(1.05)}.image-loading[data-v-3d7a38bc]{position:absolute;top:0;left:0;right:0;bottom:0;display:flex;align-items:center;justify-content:center;background:var(--bg-secondary)}.image-loading .spinner[data-v-3d7a38bc]{width:32px;height:32px;border:3px solid var(--border-light);border-top:3px solid var(--primary-color);border-radius:50%;animation:spin-3d7a38bc .8s linear infinite}.image-error[data-v-3d7a38bc]{position:absolute;top:0;left:0;right:0;bottom:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:4px;background:var(--bg-secondary);color:var(--text-tertiary);font-size:11px}.image-error[data-v-3d7a38bc] svg{font-size:24px;opacity:.5}.image-index[data-v-3d7a38bc]{position:absolute;bottom:4px;right:4px;background:#0009;color:#fff;font-size:11px;padding:2px 6px;border-radius:4px;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px)}@keyframes spin-3d7a38bc{to{transform:rotate(360deg)}}.fade-enter-active[data-v-3d7a38bc],.fade-leave-active[data-v-3d7a38bc]{transition:opacity .3s ease}.fade-enter-from[data-v-3d7a38bc],.fade-leave-to[data-v-3d7a38bc]{opacity:0}.image-list[data-v-1bd3035f]{display:grid;grid-template-columns:var(--dd592ab8);gap:6px;margin-top:8px;justify-content:start}@media (max-width: 768px){.image-list[data-v-1bd3035f]{grid-template-columns:repeat(2,1fr);gap:6px}}

113
frontend/dist/assets/CntMLwD8.js vendored Normal file

File diff suppressed because one or more lines are too long

37
frontend/dist/assets/CqahAvH1.js vendored Normal file
View File

@@ -0,0 +1,37 @@
import{d as e,r as s,c as a,o as l,i as t,a as o,e as n,m as i,F as c,B as r,n as u,g as d,z as v,b as m,v as p,u as g,t as w,h as f}from"./CdD4XvnD.js"
import{c as y,A as k,_ as b,M as h,T as C,a as x}from"./BaSQ3xJt.js"
import{f as T,a as S,b as D,c as M}from"./CFhT0fBm.js"
const z={class:"download-manager"},_={class:"filter-bar"},E=["onClick"],N={class:"filter-count"},$={class:"recent-updates-section"},A={class:"download-list"},F={key:0,class:"empty-state"},I={class:"item-icon"},j={class:"item-content"},R={class:"item-name-row"},L={class:"item-name"},P={class:"item-meta-row"},q={class:"item-type"},B={key:0,class:"item-progress-section"},G={class:"progress-bar"},X={class:"progress-info"},Y={class:"progress-text"},H={class:"progress-stats"},J={class:"progress-speed"},K={key:0,class:"progress-time"},O={key:1,class:"item-progress-section"},Q={class:"progress-info"},U={class:"progress-stats"},V={key:2,class:"item-error"},W={class:"item-right"},Z={key:0,class:"item-status-text"},ee={class:"item-actions"},se=["onClick"],ae={class:"btn-more-wrapper"},le=["onClick"],te=["onClick"],oe=["onClick"],ne=["onClick"],ie=["onClick"],ce=x(e({__name:"DownloadManager",setup(e){const{toasts:x,success:ce,error:re,removeToast:ue}=y(),de=s([]),ve=s("all")
let me=null
const pe=s(!1),ge=s({title:"",message:"",type:"warning",confirmText:"确认",cancelText:"取消"})
let we=null
const fe=s(!1),ye=s(null),ke=[{label:"全部",value:"all"},{label:"下载中",value:"downloading"},{label:"已完成",value:"completed"},{label:"已暂停",value:"paused"},{label:"失败",value:"error"}],be=a(()=>"all"===ve.value?de.value:de.value.filter(e=>e.status===ve.value)),he=e=>"all"===e?de.value.length:de.value.filter(s=>s.status===e).length,Ce=e=>({downloading:"fluent-color:arrow-square-down-32",completed:"fluent-color:checkmark-circle-24",paused:"fluent-color:clock-24",error:"fluent-color:error-circle-24",pending:"fluent-color:hourglass-24"}[e.status]||"fluent-color:document-24"),xe=e=>!!e&&e<Math.pow(1024,5)&&e<=Number.MAX_SAFE_INTEGER,Te=e=>{if("downloading"===e.status)return"下载中"
if("completed"===e.status&&e.updateTime){const s=new Date(1e3*e.updateTime),a=(new Date).getTime()-s.getTime()
return 0===Math.floor(a/864e5)?"今天更新":`已于${s.getFullYear()}/${String(s.getMonth()+1).padStart(2,"0")}/${String(s.getDate()).padStart(2,"0")}更新`}return""},Se=e=>{const s=e.target
!fe.value||s.closest(".menu-popup")||s.closest(".btn-more")||(fe.value=!1,ye.value=null)}
l(()=>{De(),me=setInterval(De,1e3),document.addEventListener("click",Se)}),t(()=>{me&&clearInterval(me),document.removeEventListener("click",Se)})
const De=async()=>{try{de.value=await k.getAllDownloads()}catch(e){}},Me=()=>{De()},ze=(e,s,a="warning")=>(fe.value=!1,ye.value=null,new Promise(l=>{ge.value={title:e,message:s,type:a,confirmText:"确认",cancelText:"取消"},pe.value=!0,we=l})),_e=()=>{pe.value=!1,we&&(we(!0),we=null)},Ee=()=>{pe.value=!1,we&&(we(!1),we=null)}
return(e,s)=>(f(),o("div",z,[n("div",{class:"page-header"},[s[1]||(s[1]=n("div",{class:"header-content"},[n("h1",{class:"page-title"},"更新和下载"),n("p",{class:"page-subtitle"},' 通过单击"检查更新"、"全部更新"或"更新"下载并安装应用更新 ')],-1)),n("button",{class:"btn-check-updates",onClick:Me}," 检查更新 ")]),n("div",_,[(f(),o(c,null,r(ke,e=>n("button",{key:e.value,class:u(["filter-btn",{active:ve.value===e.value}]),onClick:s=>ve.value=e.value},[d(v(e.label)+" ",1),n("span",N,v(he(e.value)),1)],10,E)),64))]),n("div",$,[s[12]||(s[12]=n("h2",{class:"section-title"},"最近的更新",-1)),n("div",A,[0===be.value.length?(f(),o("div",F,[i(b,{icon:"fluent-color:cloud-24",class:"empty-icon"}),s[2]||(s[2]=n("p",{class:"empty-text"},"暂无下载任务",-1))])):m("",!0),(f(!0),o(c,null,r(be.value,e=>{var a,l
return f(),o("div",{key:e.id,class:"download-item"},[n("div",I,[i(b,{icon:Ce(e),class:"icon"},null,8,["icon"])]),n("div",j,[n("div",R,[n("span",L,v(e.fileName),1)]),n("div",P,[s[3]||(s[3]=n("span",{class:"item-publisher"},"下载任务",-1)),s[4]||(s[4]=n("span",{class:"item-separator"},"•",-1)),n("span",q,v((l=e.status,{pending:"等待中",downloading:"下载中",paused:"已暂停",completed:"应用",error:"失败"}[l]||"下载任务")),1)]),"downloading"===e.status?(f(),o("div",B,[n("div",G,[n("div",{class:"progress-fill",style:p({width:xe(e.totalSize)?`${Math.min(100,Math.max(0,100*(e.progress||0)))}%`:"0%"})},null,4)]),n("div",X,[n("span",Y,v(xe(e.totalSize)?g(T)(100*(e.progress||0)):"--"),1),n("span",H,v(g(S)(e.downloadedSize||0))+" / "+v(g(S)(e.totalSize||0)),1),n("span",J,v(g(D)(e.downloadSpeed||0)),1),xe(e.totalSize)?(f(),o("span",K," 剩余 "+v(g(M)(e.remainingTime||0)),1)):m("",!0)])])):"completed"===e.status?(f(),o("div",O,[s[6]||(s[6]=n("div",{class:"progress-bar"},[n("div",{class:"progress-fill completed",style:{width:"100%"}})],-1)),n("div",Q,[s[5]||(s[5]=n("span",{class:"progress-text"},"100%",-1)),n("span",U,v(g(S)(e.totalSize)),1)])])):m("",!0),e.errorMsg?(f(),o("div",V,[i(b,{icon:"fluent-color:error-circle-24",class:"error-icon"}),d(" "+v(e.errorMsg),1)])):m("",!0)]),n("div",W,[Te(e)?(f(),o("div",Z,v(Te(e)),1)):m("",!0),n("div",ee,["completed"===e.status?(f(),o("button",{key:0,class:"btn-action btn-open",onClick:s=>(async e=>{var s,a
try{const s=await k.openDownloadFile(e)
s.success?ce(s.message||"打开文件成功",2e3):re(s.message||"打开文件失败",3e3)}catch(l){const e=(null==(a=null==(s=l.response)?void 0:s.data)?void 0:a.message)||l.message||"打开文件失败"
re(e,3e3)}})(e.id)},[i(b,{icon:"mdi:folder-open",class:"btn-icon"}),s[7]||(s[7]=d(" 打开 ",-1))],8,se)):m("",!0),n("div",ae,[n("button",{class:"btn-more",onClick:w(s=>((e,s)=>{var a
null==s||s.stopPropagation(),fe.value&&(null==(a=ye.value)?void 0:a.id)===e.id?(fe.value=!1,ye.value=null):(ye.value=e,fe.value=!0)})(e,s),["stop"])},[i(b,{icon:"mdi:dots-vertical"})],8,le),fe.value&&(null==(a=ye.value)?void 0:a.id)===e.id?(f(),o("div",{key:0,class:"menu-popup",onClick:s[0]||(s[0]=w(()=>{},["stop"]))},["downloading"===e.status?(f(),o("button",{key:0,class:"menu-item",onClick:w(s=>{(async e=>{var s,a
try{const s=await k.pauseDownload(e)
if(!s.success)return void re(s.message||"暂停下载失败",3e3)
ce(s.message||"暂停下载成功",2e3),await De()}catch(l){const e=(null==(a=null==(s=l.response)?void 0:s.data)?void 0:a.message)||l.message||"暂停下载失败"
re(e,3e3)}})(e.id),fe.value=!1},["stop"])},[i(b,{icon:"fluent-color:clock-24"}),s[8]||(s[8]=d(" 暂停 ",-1))],8,te)):m("",!0),"paused"===e.status?(f(),o("button",{key:1,class:"menu-item",onClick:w(s=>{(async e=>{var s,a
try{const s=await k.resumeDownload(e)
if(!s.success)return void re(s.message||"恢复下载失败",3e3)
ce(s.message||"恢复下载成功",2e3),await De()}catch(l){const e=(null==(a=null==(s=l.response)?void 0:s.data)?void 0:a.message)||l.message||"恢复下载失败"
re(e,3e3)}})(e.id),fe.value=!1},["stop"])},[i(b,{icon:"fluent-color:arrow-sync-24"}),s[9]||(s[9]=d(" 继续 ",-1))],8,oe)):m("",!0),"completed"===e.status||"error"===e.status?(f(),o("button",{key:2,class:"menu-item",onClick:w(s=>{(async e=>{var s,a
if(await ze("重新下载",`确定要重新下载 "${e.fileName}" 吗?这将删除并重新下载该文件。`,"warning"))try{await k.deleteDownload(e.id)
const s=await k.startDownload(e.url,e.fileName)
if(!s.success)throw new Error(s.message||"重新下载失败")
await De()}catch(l){const e=(null==(a=null==(s=l.response)?void 0:s.data)?void 0:a.message)||l.message||"重新下载失败"
re(e,3e3)}})(e),fe.value=!1},["stop"])},[i(b,{icon:"fluent-color:arrow-sync-24"}),s[10]||(s[10]=d(" 重新下载 ",-1))],8,ne)):m("",!0),n("button",{class:"menu-item danger",onClick:w(s=>{(async e=>{var s,a
if(await ze("删除下载任务","确定要删除这个下载任务吗?","danger"))try{const s=await k.deleteDownload(e)
if(!s.success)return void re(s.message||"删除下载任务失败",3e3)
ce(s.message||"删除下载任务成功",2e3),await De()}catch(l){const e=(null==(a=null==(s=l.response)?void 0:s.data)?void 0:a.message)||l.message||"删除下载任务失败"
re(e,3e3)}})(e.id),fe.value=!1},["stop"])},[i(b,{icon:"fluent-color:dismiss-circle-24"}),s[11]||(s[11]=d(" 删除 ",-1))],8,ie)])):m("",!0)])])])])}),128))])]),i(h,{visible:pe.value,title:ge.value.title,message:ge.value.message,type:"danger"===ge.value.type?"error":ge.value.type||"warning","confirm-text":ge.value.confirmText,"cancel-text":ge.value.cancelText,onConfirm:_e,onCancel:Ee},null,8,["visible","title","message","type","confirm-text","cancel-text"]),i(C,{toasts:g(x),onRemove:g(ue)},null,8,["toasts","onRemove"])]))}}),[["__scopeId","data-v-f3ad47eb"]])
export{ce as default}

1
frontend/dist/assets/D-9v_IOF.css vendored Normal file

File diff suppressed because one or more lines are too long

1
frontend/dist/assets/D-zEMyzR.css vendored Normal file

File diff suppressed because one or more lines are too long

1
frontend/dist/assets/D6PQWc4l.css vendored Normal file

File diff suppressed because one or more lines are too long

19
frontend/dist/assets/DGth4-BB.js vendored Normal file
View File

@@ -0,0 +1,19 @@
import{a as e,_ as l,A as a,h as t,c as s,i as o,T as n,M as i}from"./BaSQ3xJt.js"
import{d,c as u,a as c,e as r,v as p,z as m,h as v,b,m as f,x as g,r as _,w as h,g as y,n as V,p as k,q as x,u as w}from"./CdD4XvnD.js"
const C={class:"slider-wrapper"},I=["value","min","max","step"],B={class:"slider-value"},U=e(d({__name:"Slider",props:{modelValue:{},min:{default:0},max:{default:100},step:{default:1},suffix:{default:""}},emits:["update:modelValue"],setup(e,{emit:l}){const a=e,t=l,s=u(()=>a.suffix?`${a.modelValue} ${a.suffix}`:a.modelValue.toString()),o=u(()=>{let e=a.modelValue
return e<a.min?e=a.min:e>a.max&&(e=a.max),e}),n=u(()=>{const e=(o.value-a.min)/(a.max-a.min)*100
return{"--progress-percent":`${Math.max(0,Math.min(100,e))}%`}}),i=e=>{const l=e.target
t("update:modelValue",Number(l.value))}
return(l,a)=>(v(),c("div",C,[r("input",{value:o.value,type:"range",class:"setting-slider",style:p(n.value),min:e.min,max:e.max,step:e.step,onInput:i},null,44,I),r("div",B,m(s.value),1)]))}}),[["__scopeId","data-v-0979b282"]]),S={class:"toggle-wrapper"},D=["checked"],T={class:"toggle-slider"},N={key:0,class:"toggle-text"},$=e(d({__name:"Toggle",props:{modelValue:{type:Boolean},showLabel:{type:Boolean,default:!0}},emits:["update:modelValue"],setup(e,{emit:l}){const a=l,t=e=>{const l=e.target
a("update:modelValue",l.checked)}
return(l,a)=>(v(),c("label",S,[r("input",{checked:e.modelValue,type:"checkbox",class:"toggle-input",onChange:t},null,40,D),r("span",T,[e.showLabel?(v(),c("span",N,m(e.modelValue?"开":"关"),1)):b("",!0)])]))}}),[["__scopeId","data-v-c85b3e46"]]),j={class:"setting-item"},L={class:"setting-icon"},M={class:"setting-content"},Q={class:"setting-title"},R={key:0,class:"setting-description"},q={key:0,class:"setting-action"},z=e(d({__name:"SettingItem",props:{icon:{},title:{},description:{}},setup:e=>(a,t)=>(v(),c("div",j,[r("div",L,[f(l,{icon:e.icon},null,8,["icon"])]),r("div",M,[r("h3",Q,m(e.title),1),e.description?(v(),c("p",R,m(e.description),1)):b("",!0),g(a.$slots,"default",{},void 0)]),a.$slots.action?(v(),c("div",q,[g(a.$slots,"action",{},void 0)])):b("",!0)]))}),[["__scopeId","data-v-7d058930"]]),A={class:"setting-input-wrapper"},H=["value","placeholder"],P={key:0,class:"setting-error"},E=e(d({__name:"PathInput",props:{modelValue:{},placeholder:{default:""},icon:{default:"fluent-color:document-folder-24"}},emits:["update:modelValue","error"],setup(e,{emit:t}){const s=e,o=t,n=_(""),i=async e=>{const l=e.target.value
o("update:modelValue",l)
const t=l.trim()
if(!t)return n.value="",void o("error","")
try{const e=await a.validatePath(t)
e.exists?e.isDirectory?(n.value="",o("error","")):(n.value="路径不是目录",o("error","路径不是目录")):(n.value="路径不存在",o("error","路径不存在"))}catch(s){n.value="验证路径失败",o("error","验证路径失败")}}
return h(()=>s.modelValue,e=>{e||(n.value="",o("error",""))}),(a,t)=>(v(),c("div",null,[r("div",A,[f(l,{icon:e.icon,class:"input-icon"},null,8,["icon"]),r("input",{value:e.modelValue,type:"text",class:"setting-input",placeholder:e.placeholder,onInput:i},null,40,H)]),n.value?(v(),c("p",P,[f(l,{icon:"fluent-color:error-circle-24",class:"error-icon"}),y(" "+m(n.value),1)])):b("",!0)]))}}),[["__scopeId","data-v-332babe5"]]),G={class:"theme-options"},F=e(d({__name:"ThemeSelector",props:{modelValue:{}},emits:["update:modelValue"],setup(e,{emit:a}){const t=a,s=e=>{t("update:modelValue",e)}
return(a,t)=>(v(),c("div",G,[r("button",{class:V(["theme-option",{active:"light"===e.modelValue}]),onClick:t[0]||(t[0]=e=>s("light"))},[f(l,{icon:"fluent-color:weather-sunny-low-24"}),t[3]||(t[3]=r("span",null,"浅色",-1))],2),r("button",{class:V(["theme-option",{active:"dark"===e.modelValue}]),onClick:t[1]||(t[1]=e=>s("dark"))},[f(l,{icon:"fluent-emoji-flat:crescent-moon"}),t[4]||(t[4]=r("span",null,"深色",-1))],2),r("button",{class:V(["theme-option",{active:"auto"===e.modelValue}]),onClick:t[2]||(t[2]=e=>s("auto"))},[f(l,{icon:"fluent-emoji-flat:a-button-blood-type"}),t[5]||(t[5]=r("span",null,"自动",-1))],2)]))}}),[["__scopeId","data-v-22e00bad"]]),J={class:"about-section"},K={class:"about-icon"},O={class:"version-info"},W={key:0,class:"version-text"},X={key:1,class:"beta-badge"},Y={key:2,class:"release-badge"},Z={key:0,class:"about-details"},ee={class:"detail-section"},le={class:"detail-content"},ae={class:"detail-item"},te={class:"detail-value"},se={key:0},oe={key:1,class:"beta-badge"},ne={key:2,class:"release-badge"},ie={key:0,class:"detail-item"},de={class:"detail-value"},ue={key:1,class:"detail-item"},ce={class:"detail-value"},re={class:"detail-item"},pe={class:"detail-value"},me=e(d({__name:"AboutSection",props:{systemVersion:{},buildNumber:{},buildVersion:{},isBeta:{type:Boolean},buildDate:{}},setup(e){const a=_(!1),t=()=>{a.value=!a.value}
return(s,o)=>(v(),c("div",J,[o[9]||(o[9]=r("h2",{class:"section-title"},"关于",-1)),r("div",{class:"about-item",onClick:t},[r("div",K,[f(l,{icon:"fluent-color:apps-24"})]),o[0]||(o[0]=r("div",{class:"about-content"},[r("h3",{class:"about-title"},"秋城落叶 应用商店"),r("p",{class:"about-description"},"由 QiuChenly 发布")],-1)),r("div",O,[e.isBeta?(v(),c("span",W,"v"+m(e.buildVersion),1)):b("",!0),e.isBeta?(v(),c("span",X,"Beta")):(v(),c("span",Y,"v"+m(e.buildVersion),1)),f(l,{icon:"mdi:chevron-down",class:V(["version-arrow",{expanded:a.value}])},null,8,["class"])])]),f(k,{name:"slide-down"},{default:x(()=>[a.value?(v(),c("div",Z,[r("div",ee,[o[8]||(o[8]=r("h4",{class:"detail-title"},"个人信息",-1)),r("div",le,[o[5]||(o[5]=r("div",{class:"detail-item"},[r("span",{class:"detail-label"},"开发者:"),r("span",{class:"detail-value"},"QiuChenly")],-1)),o[6]||(o[6]=r("div",{class:"detail-item"},[r("span",{class:"detail-label"},"邮箱:"),r("span",{class:"detail-value"},"qiuchenly@outlook.com")],-1)),o[7]||(o[7]=r("div",{class:"detail-item"},[r("span",{class:"detail-label"},"GitHub"),r("span",{class:"detail-value"},"github.com/QiuChenly")],-1)),r("div",ae,[o[1]||(o[1]=r("span",{class:"detail-label"},"版本号:",-1)),r("span",te,[e.isBeta?(v(),c("span",se,"v"+m(e.buildVersion),1)):b("",!0),e.isBeta?(v(),c("span",oe,"Beta")):(v(),c("span",ne,"v"+m(e.buildVersion),1))])]),e.systemVersion?(v(),c("div",ie,[o[2]||(o[2]=r("span",{class:"detail-label"},"系统版本:",-1)),r("span",de,m(e.systemVersion),1)])):b("",!0),e.buildNumber?(v(),c("div",ue,[o[3]||(o[3]=r("span",{class:"detail-label"},"构建号:",-1)),r("span",ce,m(e.buildNumber),1)])):b("",!0),r("div",re,[o[4]||(o[4]=r("span",{class:"detail-label"},"构建日期:",-1)),r("span",pe,m(e.buildDate),1)])])])])):b("",!0)]),_:1})]))}}),[["__scopeId","data-v-069b1c40"]]),ve={class:"settings-page"},be={class:"settings-container"},fe={class:"settings-list"},ge={class:"settings-actions"},_e=["disabled"],he=["disabled"],ye=e(d({__name:"Settings",setup(e){const{settings:a,saving:d,systemVersion:p,buildNumber:b,saveSettings:g,resetToDefaults:h}=t(),{toasts:V,removeToast:k}=s(),C=o(),I=_(""),B=(new Date).toLocaleDateString("zh-CN",{year:"numeric",month:"long",day:"numeric"}),S=u(()=>"20251115"),D=u(()=>!0),T=e=>{I.value=e},N=()=>{g(I.value)}
return(e,t)=>(v(),c("div",ve,[r("div",be,[t[9]||(t[9]=r("h1",{class:"page-title"},"设置",-1)),r("div",fe,[f(z,{icon:"fluent-color:document-folder-24",title:"下载路径",description:"请输入完整的绝对路径,不能使用 ~ 符号"},{default:x(()=>[f(E,{"model-value":w(a).download_path||"",placeholder:"/Users/你的用户名/Downloads/Hayaku/","onUpdate:modelValue":t[0]||(t[0]=e=>w(a).download_path=e),onError:T},null,8,["model-value"])]),_:1}),f(z,{icon:"fluent-color:send-24",title:"最大并发下载数",description:"同时进行的下载任务数量"},{default:x(()=>[f(U,{"model-value":w(a).concurrent_downloads||3,min:1,max:16,step:1,"onUpdate:modelValue":t[1]||(t[1]=e=>w(a).concurrent_downloads=e)},null,8,["model-value"])]),_:1}),f(z,{icon:"fluent-color:link-multiple-24",title:"下载并发线程数",description:"每个下载任务使用的并发线程数默认16"},{default:x(()=>[f(U,{"model-value":w(a).download_concurrent_threads||16,min:1,max:64,step:1,"onUpdate:modelValue":t[2]||(t[2]=e=>w(a).download_concurrent_threads=e)},null,8,["model-value"])]),_:1}),f(z,{icon:"fluent-color:arrow-sync-24",title:"自动检查更新",description:"是否在后台自动检查应用更新"},{action:x(()=>[f($,{"model-value":w(a).auto_check_update??!0,"onUpdate:modelValue":t[3]||(t[3]=e=>w(a).auto_check_update=e)},null,8,["model-value"])]),_:1}),f(z,{icon:"fluent-color:clock-24",title:"更新检查间隔(分钟)",description:"后台自动检查更新的时间间隔默认10分钟"},{default:x(()=>[f(U,{"model-value":w(a).update_check_interval||10,min:1,max:1440,step:1,suffix:"分钟","onUpdate:modelValue":t[4]||(t[4]=e=>w(a).update_check_interval=e)},null,8,["model-value"])]),_:1}),f(z,{icon:"fluent-color:link-multiple-24",title:"更新检查并发线程数",description:"同时检查更新的URL数量默认16"},{default:x(()=>[f(U,{"model-value":w(a).update_check_threads||16,min:1,max:64,step:1,"onUpdate:modelValue":t[5]||(t[5]=e=>w(a).update_check_threads=e)},null,8,["model-value"])]),_:1}),f(z,{icon:"fluent-color:options-24",title:"主题",description:"选择应用主题"},{default:x(()=>[f(F,{"model-value":w(a).theme||"auto","onUpdate:modelValue":t[6]||(t[6]=e=>w(a).theme=e)},null,8,["model-value"])]),_:1})]),r("div",ge,[r("button",{class:"btn btn-secondary",onClick:t[7]||(t[7]=(...e)=>w(h)&&w(h)(...e)),disabled:w(d)},[f(l,{icon:"fluent-color:arrow-sync-24"}),t[8]||(t[8]=y(" 重置为默认值 ",-1))],8,_e),r("button",{class:"btn btn-primary",onClick:N,disabled:w(d)},[f(l,{icon:"fluent-color:document-add-24"}),y(" "+m(w(d)?"保存中...":"保存设置"),1)],8,he)]),f(me,{"system-version":w(p),"build-number":w(b),"build-version":S.value,"is-beta":D.value,"build-date":w(B)},null,8,["system-version","build-number","build-version","is-beta","build-date"])]),f(n,{toasts:w(V),onRemove:w(k)},null,8,["toasts","onRemove"]),f(i,{visible:w(C).visible.value,title:w(C).config.value.title||"",message:w(C).config.value.message||"",type:"danger"===w(C).config.value.type?"error":w(C).config.value.type||"warning","confirm-text":w(C).config.value.confirmText,"cancel-text":w(C).config.value.cancelText,onConfirm:w(C).handleConfirm,onCancel:w(C).handleCancel},null,8,["visible","title","message","type","confirm-text","cancel-text","onConfirm","onCancel"])]))}}),[["__scopeId","data-v-849ddff8"]])
export{ye as default}

30
frontend/dist/assets/DJTzdxEM.js vendored Normal file
View File

@@ -0,0 +1,30 @@
import{d as e,r as a,w as l,o as t,i as u,a as s,e as i,l as n,b as v,m as r,p as o,q as d,T as c,W as m,h as f,k as p}from"./CdD4XvnD.js"
import{b as g,_ as w,A as y,U as h,a as b}from"./BaSQ3xJt.js"
const k=["src"],L=["src"],R=["src"],_=["disabled"],z=["disabled"],I={key:1,class:"avatar-menu-item uploading"},U=b(e({__name:"AvatarUpload",props:{modelValue:{},size:{default:80},disabled:{type:Boolean,default:!1}},emits:["update:modelValue","change"],setup(e,{emit:b}){m(e=>({v6174d950:U.size+"px"}))
const U=e,V=b,E=a(null),T=a(null),A=a(null),C=a(null),M=a(""),W=a(""),j=a(!1),x=a(!1),B=a(!1),H=a(null),O=()=>{var e
U.disabled||null==(e=E.value)||e.click()},P=async e=>{var a
const l=null==(a=e.target.files)?void 0:a[0]
if(!l)return
if(!l.type.startsWith("image/"))return void alert("请选择图片文件")
if(l.size>10485760)return void alert("图片大小不能超过10MB")
const t=new FileReader
t.onload=e=>{var a
M.value=null==(a=e.target)?void 0:a.result},t.readAsDataURL(l),C.value=l,await F(l)},F=async e=>{var a,l,t
x.value=!0,j.value=!1
try{const l=await y.uploadFile(h.APP_ICON,e)
if(!l.success||!(null==(a=l.data)?void 0:a.fileId))throw new Error(l.message||"上传失败")
{const e=l.data.fileId
V("update:modelValue",e),V("change",e)
try{const a=await y.getIcon(e)
W.value=a}catch(u){W.value=""}}}catch(s){const e=(null==(t=null==(l=s.response)?void 0:l.data)?void 0:t.message)||s.message||"上传失败"
alert(e),C.value=null,M.value="",W.value=""}finally{x.value=!1}},$=()=>{U.disabled||(C.value=null,M.value="",W.value="",j.value=!1,V("update:modelValue",""),V("change",""),E.value&&(E.value.value=""),B.value=!1)},q=()=>{},D=()=>{j.value=!0,M.value&&M.value.startsWith("blob:")&&URL.revokeObjectURL(M.value),M.value=""},N=()=>{W.value="",j.value=!1}
l(()=>U.modelValue,e=>{!e||M.value||x.value?e||(W.value="",j.value=!1):(async e=>{if(!e)return W.value="",void(j.value=!1)
if(!M.value&&!x.value)try{const a=await y.getIcon(e)
W.value=a,j.value=!0}catch(a){W.value="",j.value=!1}})(e)},{immediate:!0})
const G=()=>{H.value&&(clearTimeout(H.value),H.value=null),B.value=!0},J=e=>{const a=e.relatedTarget
a&&A.value&&A.value.contains(a)||(H.value&&clearTimeout(H.value),H.value=window.setTimeout(()=>{B.value=!1},200))},K=()=>{if(!B.value||!T.value||!A.value)return
const e=T.value.getBoundingClientRect(),a=A.value,l=a.offsetHeight
let t=e.right+12,u=e.top
t+52>window.innerWidth-20&&(t=e.left-52-12),u+l>window.innerHeight-20&&(u=window.innerHeight-l-20),t<20&&(t=20),u<20&&(u=20),a.style.left=`${t}px`,a.style.top=`${u}px`}
return l(B,e=>{e&&p(()=>{K()})}),t(()=>{window.addEventListener("scroll",K,!0),window.addEventListener("resize",K)}),u(()=>{window.removeEventListener("scroll",K,!0),window.removeEventListener("resize",K),H.value&&clearTimeout(H.value),M.value&&M.value.startsWith("blob:")&&URL.revokeObjectURL(M.value)}),(a,l)=>(f(),s("div",{class:"avatar-upload",onMouseenter:G,onMouseleave:J},[i("div",{ref_key:"avatarPreviewRef",ref:T,class:"avatar-preview"},[j.value&&W.value?(f(),s("img",{key:0,src:W.value,alt:"云端图标",class:"preview-image"},null,8,k)):M.value?(f(),s("img",{key:1,src:M.value,alt:"图标预览",class:"preview-image"},null,8,L)):(f(),n(g,{key:2,"avatar-id":e.modelValue,width:e.size,height:e.size,onLoad:q},null,8,["avatar-id","width","height"])),W.value&&!j.value?(f(),s("img",{key:3,src:W.value,alt:"云端图标",class:"cloud-image",onLoad:D,onError:N},null,40,R)):v("",!0)],512),i("input",{ref_key:"fileInputRef",ref:E,type:"file",accept:"image/*",onChange:P,class:"file-input",style:{display:"none"}},null,544),(f(),n(c,{to:"body"},[r(o,{name:"avatar-menu-fade"},{default:d(()=>[B.value?(f(),s("div",{key:0,ref_key:"menuRef",ref:A,class:"avatar-menu",onMouseenter:G,onMouseleave:J},[i("button",{type:"button",class:"avatar-menu-item",onClick:O,disabled:x.value,title:"更换"},[r(w,{icon:"fluent:image-edit-24-regular"})],8,_),C.value||e.modelValue?(f(),s("button",{key:0,type:"button",class:"avatar-menu-item danger",onClick:$,disabled:x.value,title:"清除"},[r(w,{icon:"fluent:delete-24-regular"})],8,z)):v("",!0),x.value?(f(),s("div",I,[...l[0]||(l[0]=[i("div",{class:"spinner-small"},null,-1)])])):v("",!0)],544)):v("",!0)]),_:1})]))],32))}}),[["__scopeId","data-v-8cef3395"]])
export{U as A}

481
frontend/dist/assets/DJlm-vlP.js vendored Normal file

File diff suppressed because one or more lines are too long

1
frontend/dist/assets/DSZEclwA.css vendored Normal file
View File

@@ -0,0 +1 @@
.official-apps-page[data-v-5e899d81]{width:100%;min-height:100%}.page-container[data-v-5e899d81]{width:100%}.page-header[data-v-5e899d81]{margin-bottom:32px}.page-title[data-v-5e899d81]{margin:0 0 8px;font-size:28px;font-weight:600;color:var(--text-primary)}.page-description[data-v-5e899d81]{margin:0;font-size:14px;color:var(--text-secondary)}.app-section[data-v-5e899d81]{margin-bottom:48px}.app-section[data-v-5e899d81]:last-child{margin-bottom:0}.section-title[data-v-5e899d81]{margin:0 0 20px;font-size:20px;font-weight:600;color:var(--text-primary)}.apps-grid[data-v-5e899d81]{display:grid;grid-template-columns:repeat(auto-fill,200px);gap:20px;margin-bottom:0;justify-content:center}.loading[data-v-5e899d81],.error[data-v-5e899d81],.empty[data-v-5e899d81]{text-align:center;padding:60px 0}.loading .spinner[data-v-5e899d81],.error .spinner[data-v-5e899d81],.empty .spinner[data-v-5e899d81]{width:40px;height:40px;border:3px solid var(--border-light);border-top:3px solid var(--primary-color);border-radius:50%;animation:spin-5e899d81 .8s linear infinite;margin:0 auto 16px}.loading p[data-v-5e899d81],.error p[data-v-5e899d81],.empty p[data-v-5e899d81]{color:var(--text-secondary);font-size:14px;margin-bottom:8px}.loading .empty-hint[data-v-5e899d81],.error .empty-hint[data-v-5e899d81],.empty .empty-hint[data-v-5e899d81]{font-size:12px;color:var(--text-tertiary)}@keyframes spin-5e899d81{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.empty-icon[data-v-5e899d81]{font-size:64px;margin-bottom:16px;opacity:.3;color:var(--text-tertiary)}.list-footer[data-v-5e899d81]{padding:20px 0;border-top:1px solid var(--border-light)}.footer-stats[data-v-5e899d81]{display:flex;gap:24px;justify-content:center}.footer-stats .stat-item[data-v-5e899d81]{display:flex;gap:8px}.footer-stats .stat-item .stat-label[data-v-5e899d81]{color:var(--text-secondary);font-size:14px}.footer-stats .stat-item .stat-value[data-v-5e899d81]{color:var(--text-primary);font-weight:600;font-size:14px}.btn[data-v-5e899d81]{padding:8px 16px;border:none;border-radius:4px;font-size:14px;font-weight:600;cursor:pointer;transition:all .2s ease}.btn.btn-primary[data-v-5e899d81]{background:var(--primary-color);color:#fff}.btn.btn-primary[data-v-5e899d81]:hover{opacity:.9}

1
frontend/dist/assets/DTTm7QTi.css vendored Normal file
View File

@@ -0,0 +1 @@
.new-features-page[data-v-5d89dd00]{margin:-32px -40px;padding:32px 40px;width:calc(100% + 80px);min-height:calc(100% + 64px);box-sizing:border-box;background:transparent}.features-container[data-v-5d89dd00]{position:relative}.page-header[data-v-5d89dd00]{margin-bottom:48px}.page-title[data-v-5d89dd00]{font-size:32px;font-weight:600;color:var(--text-primary);margin:0 0 12px;padding:0}.page-subtitle[data-v-5d89dd00]{font-size:16px;color:var(--text-secondary);margin:0;padding:0;line-height:1.6}.features-list[data-v-5d89dd00]{display:flex;flex-direction:column;gap:32px}.feature-item[data-v-5d89dd00]{padding:24px;background:var(--bg-card);border:1px solid var(--border-light);border-radius:12px;transition:all .2s ease;position:relative;cursor:pointer}.feature-item[data-v-5d89dd00]:hover{background:var(--bg-secondary);border-color:var(--border-color);box-shadow:var(--shadow-md);transform:translateY(-2px)}.feature-item.feature-type-new[data-v-5d89dd00]{background:linear-gradient(to right,rgba(0,122,255,.05) 0%,var(--bg-card) 100%)}.feature-item.feature-type-fix[data-v-5d89dd00]{background:linear-gradient(to right,rgba(52,199,89,.05) 0%,var(--bg-card) 100%)}.feature-header[data-v-5d89dd00]{display:flex;align-items:flex-start;justify-content:space-between;margin-bottom:12px;gap:16px}.feature-title[data-v-5d89dd00]{font-size:20px;font-weight:600;color:var(--text-primary);margin:0;padding:0;flex:1;line-height:1.4}.new-badge[data-v-5d89dd00]{display:inline-flex;align-items:center;justify-content:center;padding:4px 8px;font-size:12px;font-weight:600;border-radius:4px;flex-shrink:0;text-transform:uppercase;letter-spacing:.5px;transition:all .2s ease}.new-badge.badge-feature[data-v-5d89dd00]{background:var(--primary-color);color:#fff}.new-badge.badge-fix[data-v-5d89dd00]{background:#34c759;color:#fff}.feature-description[data-v-5d89dd00]{font-size:15px;color:var(--text-secondary);margin:0 0 16px;padding:0;line-height:1.7}.feature-action[data-v-5d89dd00]{margin-top:16px}.action-button[data-v-5d89dd00]{display:inline-flex;align-items:center;padding:10px 20px;background:var(--bg-secondary);color:var(--text-primary);border:1px solid var(--border-color);border-radius:6px;font-size:14px;font-weight:500;cursor:pointer;transition:all .2s ease}.action-button[data-v-5d89dd00]:hover{background:var(--bg-hover);border-color:var(--primary-color);color:var(--primary-color)}.action-button[data-v-5d89dd00]:active{transform:scale(.98)}[data-theme=dark] .feature-item[data-v-5d89dd00]{background:var(--bg-card);border-color:var(--border-light)}[data-theme=dark] .feature-item.feature-type-new[data-v-5d89dd00]{background:linear-gradient(to right,rgba(0,122,255,.1) 0%,var(--bg-card) 100%)}[data-theme=dark] .feature-item.feature-type-fix[data-v-5d89dd00]{background:linear-gradient(to right,rgba(52,199,89,.1) 0%,var(--bg-card) 100%)}[data-theme=dark] .action-button[data-v-5d89dd00]{background:var(--bg-secondary);border-color:var(--border-color)}[data-theme=dark] .action-button[data-v-5d89dd00]:hover{background:var(--bg-hover);border-color:var(--primary-color)}@media (max-width: 768px){.new-features-page[data-v-5d89dd00]{padding:16px}.page-title[data-v-5d89dd00]{font-size:28px}.feature-item[data-v-5d89dd00]{padding:20px}.feature-title[data-v-5d89dd00]{font-size:18px}.feature-description[data-v-5d89dd00]{font-size:14px}}

1
frontend/dist/assets/DUqG9cND.css vendored Normal file

File diff suppressed because one or more lines are too long

21
frontend/dist/assets/Dcev5rw-.js vendored Normal file
View File

@@ -0,0 +1,21 @@
import{A as a}from"./RX4g3b5N.js"
import{_ as s,A as e,a as l}from"./BaSQ3xJt.js"
import{u as t,L as n}from"./C2sz-FFd.js"
import{g as i,m as p,f as c}from"./Bt6cakcM.js"
import{d as o,r as d,c as u,o as r,a as v,e as g,m as h,b as m,z as y,F as f,B as b,l as I,u as k,h as A}from"./CdD4XvnD.js"
const j={class:"official-apps-page"},w={class:"page-container"},V={key:0,class:"loading"},C={key:1,class:"error"},M={key:2,class:"empty"},U={key:3},_={key:0,class:"app-section"},G={class:"apps-grid"},x={key:1,class:"app-section"},z={class:"apps-grid"},B={key:4,class:"list-footer"},F={class:"footer-stats"},L={class:"stat-item"},N={class:"stat-value"},O={class:"stat-item"},Q={class:"stat-value"},R={class:"stat-item"},S={class:"stat-value"},T=l(o({__name:"OfficialApps",setup(l){const o=d([]),T=d(!0),q=d(""),D=u(()=>o.value.sort((a,s)=>a.name.localeCompare(s.name))),E=u(()=>D.value.filter(a=>a.path&&a.path.length>0)),H=u(()=>D.value.filter(a=>!a.path||0===a.path.length)),J=async()=>{T.value=!0,q.value=""
try{const s=await e.getApps()
if(0===s.length){const a=await e.batchGetAppVersions([])
if(a.success&&a.data){const s=a.data.notInstalledApps||[]
o.value=s.map(a=>K(a))}else o.value=[]}else{const l=s.map(a=>{let s=[]
return s=a.category?Array.isArray(a.category)?a.category.length>0?a.category:[i(a.bundleId)]:[a.category]:[i(a.bundleId)],{...a,category:s}}),t=await e.batchGetAppVersions(l)
if(t.success&&t.data){const l=t.data,n=p(s,l).filter(a=>null!==a.update),i=(l.notInstalledApps||[]).map(a=>K(a)),d=[...n,...i]
o.value=await c(d)
const u=i.map(a=>a.bundleId)
if(u.length>0)try{const a=await e.batchGetAppIconIds(u)
a.success&&a.iconIdMap&&(o.value=o.value.map(s=>!s.path&&a.iconIdMap[s.bundleId]?{...s,iconId:a.iconIdMap[s.bundleId]}:s))}catch(a){}}else o.value=[]}}catch(a){q.value="加载应用列表失败,请检查服务是否运行",o.value=[]}finally{T.value=!1}}
function K(a){return{bundleId:a.bundleId,name:a.appName,version:"",bundleVersion:"",path:"",status:"supported",injection:null,update:a.versions.length>0?{hasUpdate:!1,latestVersion:a.latestVersion,feedUrl:"",versions:a.versions}:null}}const{updating:P,modalVisible:W,modalTitle:X,logs:Y,modalStatus:Z,handleUpdate:$,hideModal:aa}=t({onRefresh:async a=>{try{const s=await e.getApp(a),l=o.value.findIndex(s=>s.bundleId===a)
if(-1!==l){const a=o.value[l]
o.value[l]={...s,update:a.update||s.update}}}catch(s){}}}),sa=a=>{$(a)},ea=async a=>{await J()}
return r(()=>{J()}),(e,l)=>(A(),v("div",j,[g("div",w,[l[8]||(l[8]=g("div",{class:"page-header"},[g("h1",{class:"page-title"},"来自官方"),g("p",{class:"page-description"},"浏览和下载经过 QiuChenly 认证的可信来源应用")],-1)),T.value?(A(),v("div",V,[...l[0]||(l[0]=[g("div",{class:"spinner"},null,-1),g("p",null,"加载应用中...",-1)])])):q.value?(A(),v("div",C,[g("p",null,y(q.value),1),g("button",{onClick:J,class:"btn btn-primary"},"重试")])):0===D.value.length?(A(),v("div",M,[h(s,{icon:"fluent-color:shield-checkmark-24",class:"empty-icon"}),l[1]||(l[1]=g("p",null,"没有找到来自官方的应用",-1)),l[2]||(l[2]=g("p",{class:"empty-hint"},"当前没有可用的应用更新",-1))])):(A(),v("div",U,[E.value.length>0?(A(),v("div",_,[l[3]||(l[3]=g("h3",{class:"section-title"},"已安装的应用",-1)),g("div",G,[(A(!0),v(f,null,b(E.value,s=>(A(),I(a,{key:s.bundleId,app:s,mode:"list",updating:k(P),onInject:sa,onUpdated:ea},null,8,["app","updating"]))),128))])])):m("",!0),H.value.length>0?(A(),v("div",x,[l[4]||(l[4]=g("h3",{class:"section-title"},"未在此Mac上安装",-1)),g("div",z,[(A(!0),v(f,null,b(H.value,s=>(A(),I(a,{key:s.bundleId,app:s,mode:"list",updating:k(P),onInject:sa,onUpdated:ea},null,8,["app","updating"]))),128))])])):m("",!0)])),D.value.length>0?(A(),v("div",B,[g("div",F,[g("span",L,[l[5]||(l[5]=g("span",{class:"stat-label"},"总计:",-1)),g("span",N,y(D.value.length),1)]),g("span",O,[l[6]||(l[6]=g("span",{class:"stat-label"},"已安装:",-1)),g("span",Q,y(E.value.length),1)]),g("span",R,[l[7]||(l[7]=g("span",{class:"stat-label"},"未安装:",-1)),g("span",S,y(H.value.length),1)])])])):m("",!0)]),h(n,{visible:k(W),title:k(X),logs:k(Y),loading:k(P),status:k(Z),"can-close":"loading"!==k(Z),onClose:k(aa)},null,8,["visible","title","logs","loading","status","can-close","onClose"])]))}}),[["__scopeId","data-v-5e899d81"]])
export{T as default}

1
frontend/dist/assets/DjM3KHR-.css vendored Normal file
View File

@@ -0,0 +1 @@
.image-preview-modal[data-v-a54a074d]{position:fixed;top:0;left:0;right:0;bottom:0;background:#000000f2;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);display:flex;align-items:center;justify-content:center;z-index:10001;cursor:pointer;padding:var(--spacing-lg);outline:none}.image-preview-modal .preview-content[data-v-a54a074d]{position:relative;width:100%;height:100%;max-width:95vw;max-height:95vh;cursor:default;display:flex;align-items:center;justify-content:center}.image-container[data-v-a54a074d]{position:relative;width:100%;height:100%;display:flex;align-items:center;justify-content:center}.image-container img[data-v-a54a074d]{max-width:100%;max-height:95vh;border-radius:var(--radius-lg);box-shadow:0 20px 60px #00000080;object-fit:contain;-webkit-user-select:none;user-select:none}.loading-state[data-v-a54a074d],.error-state[data-v-a54a074d]{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:16px;color:#fffc;font-size:16px}.loading-state .spinner[data-v-a54a074d],.error-state .spinner[data-v-a54a074d]{width:48px;height:48px;border:4px solid rgba(255,255,255,.2);border-top:4px solid white;border-radius:50%;animation:spin-a54a074d .8s linear infinite}.loading-state p[data-v-a54a074d],.error-state p[data-v-a54a074d]{margin:0}.loading-state[data-v-a54a074d] svg,.error-state[data-v-a54a074d] svg{font-size:48px;opacity:.6}.nav-btn[data-v-a54a074d]{position:absolute;top:50%;transform:translateY(-50%);width:56px;height:56px;background:#ffffff26;-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);border:1px solid rgba(255,255,255,.2);border-radius:50%;color:#fff;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all .3s cubic-bezier(.4,0,.2,1);box-shadow:0 4px 12px #0000004d;z-index:10}.nav-btn[data-v-a54a074d]:hover{background:#ffffff40;border-color:#fff6;transform:translateY(-50%) scale(1.1);box-shadow:0 6px 20px #0006}.nav-btn[data-v-a54a074d]:active{transform:translateY(-50%) scale(.95)}.nav-btn[data-v-a54a074d] svg,.nav-btn[data-v-a54a074d] .iconify{font-size:28px;width:28px;height:28px}.nav-btn.prev-btn[data-v-a54a074d]{left:20px}.nav-btn.next-btn[data-v-a54a074d]{right:20px}.close-preview-btn[data-v-a54a074d]{position:absolute;top:20px;right:20px;width:44px;height:44px;background:#ffffff26;-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);border:1px solid rgba(255,255,255,.2);border-radius:50%;color:#fff;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all .3s cubic-bezier(.4,0,.2,1);box-shadow:0 4px 12px #0000004d;z-index:10}.close-preview-btn[data-v-a54a074d]:hover{background:#ffffff40;border-color:#fff6;transform:scale(1.1) rotate(90deg);box-shadow:0 6px 20px #0006}.close-preview-btn[data-v-a54a074d]:active{transform:scale(.95) rotate(90deg)}.close-preview-btn[data-v-a54a074d] svg,.close-preview-btn[data-v-a54a074d] .iconify{font-size:24px;width:24px;height:24px}.image-info[data-v-a54a074d]{position:absolute;bottom:20px;left:50%;transform:translate(-50%);background:#0009;-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);color:#fff;padding:8px 16px;border-radius:20px;font-size:14px;font-weight:500;display:flex;align-items:center;gap:12px}.image-info .image-index[data-v-a54a074d]{font-weight:500}.image-info .image-size[data-v-a54a074d]{font-size:12px;opacity:.8}.preview-fade-enter-active[data-v-a54a074d],.preview-fade-leave-active[data-v-a54a074d]{transition:opacity .3s ease}.preview-fade-enter-active .preview-content[data-v-a54a074d]{transition:transform .3s cubic-bezier(.4,0,.2,1),opacity .3s ease}.preview-fade-leave-active .preview-content[data-v-a54a074d]{transition:transform .2s ease,opacity .2s ease}.preview-fade-enter-from[data-v-a54a074d]{opacity:0}.preview-fade-enter-from .preview-content[data-v-a54a074d]{transform:scale(.9)}.preview-fade-leave-to[data-v-a54a074d]{opacity:0}.preview-fade-leave-to .preview-content[data-v-a54a074d]{transform:scale(.9)}@keyframes spin-a54a074d{to{transform:rotate(360deg)}}@media (max-width: 768px){.image-preview-modal[data-v-a54a074d]{padding:var(--spacing-md)}.image-preview-modal .nav-btn[data-v-a54a074d]{width:44px;height:44px}.image-preview-modal .nav-btn.prev-btn[data-v-a54a074d]{left:10px}.image-preview-modal .nav-btn.next-btn[data-v-a54a074d]{right:10px}.image-preview-modal .nav-btn[data-v-a54a074d] svg{font-size:24px}.image-preview-modal .close-preview-btn[data-v-a54a074d]{top:10px;right:10px;width:36px;height:36px}.image-preview-modal .image-info[data-v-a54a074d]{bottom:10px;font-size:12px;padding:6px 12px}}

1
frontend/dist/assets/DmWTyp24.css vendored Normal file

File diff suppressed because one or more lines are too long

47
frontend/dist/assets/DxIoCIem.js vendored Normal file

File diff suppressed because one or more lines are too long

41
frontend/dist/assets/JHpP7yB4.js vendored Normal file
View File

@@ -0,0 +1,41 @@
import{d as a,r as e,c as t,w as l,a as s,F as n,l as i,m as o,T as c,p as u,q as d,b as r,t as m,e as v,z as p,C as h,g as b,D as f,B as g,n as y,h as k}from"./CdD4XvnD.js"
import{c as D,_ as C,b as w,A as I,a as z}from"./BaSQ3xJt.js"
import{I as _}from"./j0cGtmjd.js"
const A={class:"post-edit-dialog"},B={class:"dialog-header"},M={class:"dialog-body"},U={key:0,class:"loading-state"},j={key:1,class:"post-form"},x={key:0,class:"form-group author-info-section"},F={class:"author-card"},R={class:"author-avatar"},q={class:"author-details"},P={class:"author-name"},E={key:0,class:"author-id"},L={class:"form-group"},V=["disabled"],$={class:"form-group"},G=["disabled"],K={class:"form-group"},O={key:0,class:"attachments-list"},T=["onClick","disabled"],W=["onClick"],H={class:"attachment-name"},J={class:"attachment-size"},N={key:1,class:"no-attachments"},Q={class:"form-group"},S={class:"attachment-upload"},X=["disabled"],Y=["disabled"],Z=["disabled"],aa=["disabled"],ea={key:0,class:"upload-progress"},ta={class:"dialog-actions"},la=["disabled"],sa=["disabled"],na={key:0},ia={key:1},oa=z(a({__name:"PostEditDialog",props:{visible:{type:Boolean},postData:{},loading:{type:Boolean,default:!1}},emits:["confirm","cancel"],setup(a,{emit:z}){const oa=a,ca=z,ua=D(),da=e({title:"",content:"",attachments:[]}),ra=e(!1),ma=e(!1),va=e(null),pa=e(null),ha=e(null)
e("")
const ba=t(()=>da.value.title.trim().length>0&&da.value.content.trim().length>0),fa=a=>{if(0===a)return"0 B"
const e=Math.floor(Math.log(a)/Math.log(1024))
return Math.round(a/Math.pow(1024,e)*100)/100+" "+["B","KB","MB","GB"][e]},ga=()=>{oa.postData?da.value={title:oa.postData.title||"",content:oa.postData.content||"",attachments:oa.postData.attachments||[]}:da.value={title:"",content:"",attachments:[]}}
l(()=>oa.visible,a=>{a&&oa.postData&&ga()}),l(()=>oa.postData,a=>{oa.visible&&a&&ga()},{deep:!0}),l(()=>oa.loading,a=>{})
const ya=()=>{var a
null==(a=va.value)||a.click()},ka=()=>{var a
null==(a=pa.value)||a.click()},Da=async a=>{var e,t,l
const s=a.target,n=null==(e=s.files)?void 0:e[0]
if(n){ma.value=!0
try{const a=await I.uploadImageAttachment(n)
a.success&&a.attachmentId?(da.value.attachments.push({id:a.attachmentId,name:n.name,type:"image",size:n.size}),ua.success("图片上传成功")):ua.error(a.message||"图片上传失败")}catch(i){ua.error((null==(l=null==(t=i.response)?void 0:t.data)?void 0:l.message)||i.message||"图片上传失败")}finally{ma.value=!1,s&&(s.value="")}}},Ca=async a=>{var e,t,l
const s=a.target,n=null==(e=s.files)?void 0:e[0]
if(n){ma.value=!0
try{const a=await I.uploadFileAttachment(n)
a.success&&a.attachmentId?(da.value.attachments.push({id:a.attachmentId,name:n.name,type:"file",size:n.size}),ua.success("文件上传成功")):ua.error(a.message||"文件上传失败")}catch(i){ua.error((null==(l=null==(t=i.response)?void 0:t.data)?void 0:l.message)||i.message||"文件上传失败")}finally{ma.value=!1,s&&(s.value="")}}},wa=async a=>{var e,t
if(ra.value||ma.value)return
const l=a.clipboardData
if(!l)return
const s=Array.from(l.items).find(a=>a.type.startsWith("image/"))
if(!s)return
a.preventDefault()
const n=s.getAsFile()
if(!n)return
const i=Date.now(),o=n.type.split("/")[1]||"png",c=new File([n],`paste-${i}.${o}`,{type:n.type})
ma.value=!0
try{const a=await I.uploadImageAttachment(c)
a.success&&a.attachmentId?(da.value.attachments.push({id:a.attachmentId,name:c.name,type:"image",size:c.size}),ua.success("图片已粘贴并上传")):ua.error(a.message||"图片上传失败")}catch(u){ua.error((null==(t=null==(e=u.response)?void 0:e.data)?void 0:t.message)||u.message||"图片上传失败")}finally{ma.value=!1}},Ia=()=>{if(!ba.value)return
const a=da.value.attachments.map(a=>a.id)
ca("confirm",{title:da.value.title.trim(),content:da.value.content.trim(),attachmentIds:a})},za=()=>{ca("cancel")},_a=()=>{ha.value&&(URL.revokeObjectURL(ha.value),ha.value=null)}
return(e,t)=>(k(),s(n,null,[(k(),i(c,{to:"body"},[o(u,{name:"fade"},{default:d(()=>{var e
return[a.visible?(k(),s("div",{key:0,class:"post-edit-dialog-overlay",onClick:m(za,["self"])},[v("div",A,[v("div",B,[t[2]||(t[2]=v("h3",{class:"dialog-title"},"编辑帖子",-1)),v("button",{class:"close-btn",onClick:za,type:"button"},[o(C,{icon:"mdi:close"})])]),v("div",M,[oa.loading?(k(),s("div",U,[...t[3]||(t[3]=[v("div",{class:"spinner"},null,-1),v("p",null,"加载帖子详情中...",-1)])])):(k(),s("div",j,[(null==(e=oa.postData)?void 0:e.author)?(k(),s("div",x,[t[4]||(t[4]=v("label",{class:"form-label"},"发帖人",-1)),v("div",F,[v("div",R,[o(w,{"avatar-id":oa.postData.author.avatar,"user-name":oa.postData.author.name,width:36,height:36},null,8,["avatar-id","user-name"])]),v("div",q,[v("div",P,p(oa.postData.author.name),1),oa.postData.author.id?(k(),s("div",E," ID: "+p(oa.postData.author.id),1)):r("",!0)])])])):r("",!0),v("div",L,[t[5]||(t[5]=v("label",{class:"form-label"},[b("标题 "),v("span",{class:"required"},"*")],-1)),h(v("input",{"onUpdate:modelValue":t[0]||(t[0]=a=>da.value.title=a),type:"text",placeholder:"请输入帖子标题",class:"form-input",disabled:ra.value},null,8,V),[[f,da.value.title]])]),v("div",$,[t[6]||(t[6]=v("label",{class:"form-label"},[b("内容 "),v("span",{class:"required"},"*")],-1)),h(v("textarea",{"onUpdate:modelValue":t[1]||(t[1]=a=>da.value.content=a),placeholder:"请输入帖子内容",class:"form-textarea",rows:"15",disabled:ra.value,onPaste:wa},null,40,G),[[f,da.value.content]])]),v("div",K,[t[8]||(t[8]=v("label",{class:"form-label"},"附件",-1)),da.value.attachments&&da.value.attachments.length>0?(k(),s("div",O,[(k(!0),s(n,null,g(da.value.attachments,a=>(k(),s("div",{key:a.id,class:y(["attachment-item",{clickable:!0}])},[v("button",{class:"remove-attachment-btn-left",onClick:m(e=>{return t=a.id,void(da.value.attachments=da.value.attachments.filter(a=>a.id!==t))
var t},["stop"]),disabled:ra.value,type:"button",title:"删除附件"},[o(C,{icon:"mdi:close"})],8,T),v("div",{class:"attachment-info",onClick:m(e=>(async a=>{if("image"===a.type){if(a.id){const e=await I.getAttachmentImage(a.id)
e?ha.value=e:ua.error("图片加载失败")}}else await(async a=>{try{const e=await I.getAttachmentDownloadUrl(a.id)
if(e){const a=document.createElement("a")
a.href=e.url,a.download=e.name,a.target="_blank",document.body.appendChild(a),a.click(),document.body.removeChild(a),ua.success("文件下载已开始")}else ua.error("无法获取下载链接")}catch(e){ua.error("下载文件失败: "+(e.message||"未知错误"))}})(a)})(a),["stop"])},[o(C,{icon:"image"===a.type?"mdi:image":"mdi:file",class:"attachment-icon"},null,8,["icon"]),v("span",H,p(a.name),1),v("span",J,p(fa(a.size)),1),"image"===a.type?(k(),i(C,{key:0,icon:"mdi:eye-outline",class:"attachment-action-icon",title:"预览图片"})):(k(),i(C,{key:1,icon:"mdi:download",class:"attachment-action-icon",title:"下载文件"}))],8,W)]))),128))])):(k(),s("div",N,[...t[7]||(t[7]=[v("p",null,"暂无附件",-1)])]))]),v("div",Q,[t[12]||(t[12]=v("label",{class:"form-label"},"添加附件",-1)),v("div",S,[v("input",{ref_key:"imageInputRef",ref:va,type:"file",accept:"image/*",onChange:Da,class:"file-input",style:{display:"none"},disabled:ra.value},null,40,X),v("input",{ref_key:"fileInputRef",ref:pa,type:"file",onChange:Ca,class:"file-input",style:{display:"none"},disabled:ra.value},null,40,Y),v("button",{class:"btn btn-outline",onClick:ya,disabled:ra.value||ma.value,type:"button"},[o(C,{icon:"mdi:image",class:"btn-icon"}),t[9]||(t[9]=b(" 添加图片 ",-1))],8,Z),v("button",{class:"btn btn-outline",onClick:ka,disabled:ra.value||ma.value,type:"button"},[o(C,{icon:"mdi:file",class:"btn-icon"}),t[10]||(t[10]=b(" 添加文件 ",-1))],8,aa)]),ma.value?(k(),s("div",ea,[...t[11]||(t[11]=[v("div",{class:"spinner-small"},null,-1),v("span",null,"上传中...",-1)])])):r("",!0)])]))]),v("div",ta,[v("button",{class:"btn btn-outline",onClick:za,disabled:ra.value||ma.value}," 取消 ",8,la),v("button",{class:"btn btn-primary",onClick:Ia,disabled:!ba.value||ra.value||ma.value},[ra.value?(k(),s("span",na,"保存中...")):(k(),s("span",ia,"保存"))],8,sa)])])])):r("",!0)]}),_:1})])),o(_,{visible:!!ha.value,"image-url":ha.value,onClose:_a},null,8,["visible","image-url"])],64))}}),[["__scopeId","data-v-595b5a78"]])
export{oa as P}

11
frontend/dist/assets/JespKOZ3.js vendored Normal file
View File

@@ -0,0 +1,11 @@
import{d as a,r as e,o as i,a as l,e as s,b as t,m,p as d,q as n,z as u,h as o,c as g,F as c,B as r,l as v,W as p}from"./CdD4XvnD.js"
import{_ as I,A as f,a as x}from"./BaSQ3xJt.js"
const h={class:"image-wrapper"},k={key:0,class:"image-loading"},_=["src"],y={key:0,class:"image-error"},w={key:0,class:"image-index"},C=x(a({__name:"ImageListItem",props:{imageId:{},imageSource:{default:"attachment"},showIndex:{type:Boolean,default:!1},index:{default:0},total:{default:0}},emits:["imageClick"],setup(a,{emit:g}){const c=a,r=g,v=e(!0),p=e(!1),x=e(null),C=()=>{v.value=!1},b=()=>{p.value=!0,v.value=!1},S=()=>{x.value&&r("imageClick",c.imageId,x.value)}
return i(()=>{(async()=>{if(!c.imageId)return p.value=!0,void(v.value=!1)
v.value=!0,p.value=!1,x.value=null
try{const a="app"===c.imageSource?await f.getImage(c.imageId):await f.getAttachmentImage(c.imageId)
a?x.value=a:(p.value=!0,v.value=!1)}catch(a){p.value=!0,v.value=!1}})()}),(e,i)=>(o(),l("div",{class:"image-item",onClick:S},[s("div",h,[m(d,{name:"fade"},{default:n(()=>[v.value?(o(),l("div",k,[...i[0]||(i[0]=[s("div",{class:"spinner"},null,-1)])])):t("",!0)]),_:1}),m(d,{name:"fade"},{default:n(()=>[x.value&&!p.value?(o(),l("img",{key:0,src:x.value,alt:"图片",onLoad:C,onError:b,class:"image-content"},null,40,_)):t("",!0)]),_:1}),m(d,{name:"fade"},{default:n(()=>[p.value?(o(),l("div",y,[m(I,{icon:"mdi:image-broken"}),i[1]||(i[1]=s("span",null,"加载失败",-1))])):t("",!0)]),_:1})]),a.showIndex?(o(),l("div",w,u(a.index)+"/"+u(a.total),1)):t("",!0)]))}}),[["__scopeId","data-v-3d7a38bc"]]),b={key:0,class:"image-list"},S=x(a({__name:"ImageList",props:{imageIds:{default:()=>[]},imageSource:{default:"attachment"},showIndex:{type:Boolean,default:!1},columns:{default:3},maxWidth:{default:void 0}},emits:["imageClick"],setup(a,{emit:e}){p(a=>({dd592ab8:m.value}))
const i=a,s=e,m=g(()=>i.maxWidth?`repeat(auto-fill, minmax(0, min(100%, ${i.maxWidth}px)))`:`repeat(${i.columns}, minmax(0, 1fr))`),d=(a,e)=>{const l=i.imageIds.indexOf(a)
s("imageClick",l,e)}
return(e,i)=>a.imageIds.length>0?(o(),l("div",b,[(o(!0),l(c,null,r(a.imageIds,(e,i)=>(o(),v(C,{key:`image-${e}-${i}`,"image-id":e,"image-source":a.imageSource,"show-index":a.showIndex,index:i+1,total:a.imageIds.length,onImageClick:d},null,8,["image-id","image-source","show-index","index","total"]))),128))])):t("",!0)}}),[["__scopeId","data-v-1bd3035f"]])
export{S as I}

10
frontend/dist/assets/Nkv5t_uh.js vendored Normal file
View File

@@ -0,0 +1,10 @@
import{d as e,r as l,c as o,w as n,o as a,i as t,a as s,e as i,l as c,n as d,m as r,z as u,p as v,q as p,b as w,t as m,v as f,F as h,B as k,T as E,k as L,h as _}from"./CdD4XvnD.js"
import{_ as g,a as x}from"./BaSQ3xJt.js"
const y={class:"select-selected"},C=["onClick"],b=x(e({__name:"CustomSelect",props:{modelValue:{},options:{},placeholder:{default:"请选择"}},emits:["update:modelValue"],setup(e,{emit:x}){const b=e,V=x,z=l(!1),H=l(null),R=l(null),$=l({top:"0px",left:"0px",width:"200px"}),j=o(()=>b.options.find(e=>e.value===b.modelValue)||null),B=()=>{z.value&&H.value&&L(()=>{const e=H.value.getBoundingClientRect(),l=48*b.options.length+16,o=window.innerHeight-e.bottom,n=e.top
let a=e.bottom+8,t=e.left,s=e.width
o<l&&n>o&&(a=e.top-l-8),t+s>window.innerWidth&&(t=window.innerWidth-s-16),t<16&&(t=16),a<16&&(a=16),a+l>window.innerHeight-16&&(a=window.innerHeight-l-16),$.value={top:`${a}px`,left:`${t}px`,width:`${s}px`}})},W=()=>{z.value=!z.value,z.value&&B()},q=e=>{const l=e.target
H.value&&H.value.contains(l)||R.value&&R.value.contains(l)||(z.value=!1)}
return n(z,e=>{e?(B(),window.addEventListener("scroll",B,!0),window.addEventListener("resize",B)):(window.removeEventListener("scroll",B,!0),window.removeEventListener("resize",B))}),a(()=>{document.addEventListener("click",q)}),t(()=>{document.removeEventListener("click",q),window.removeEventListener("scroll",B,!0),window.removeEventListener("resize",B)}),(l,o)=>{var n,a
return _(),s("div",{class:"custom-select-wrapper",ref_key:"wrapperRef",ref:H},[i("div",{class:d(["custom-select",{open:z.value}]),onClick:W},[i("div",y,[r(g,{icon:(null==(n=j.value)?void 0:n.icon)||"",class:"select-icon"},null,8,["icon"]),i("span",null,u((null==(a=j.value)?void 0:a.label)||e.placeholder),1),r(g,{icon:"fluent-color:chevron-down-24",class:d(["select-arrow",{rotated:z.value}])},null,8,["class"])])],2),(_(),c(E,{to:"body"},[r(v,{name:"dropdown-fade"},{default:p(()=>[z.value?(_(),s("div",{key:0,ref_key:"dropdownRef",ref:R,class:"select-dropdown",style:f($.value),onClick:o[0]||(o[0]=m(()=>{},["stop"]))},[(_(!0),s(h,null,k(e.options,l=>(_(),s("div",{key:l.value,class:d(["select-option",{active:e.modelValue===l.value}]),onClick:e=>{return o=l.value,V("update:modelValue",o),void(z.value=!1)
var o}},[r(g,{icon:l.icon,class:"option-icon"},null,8,["icon"]),i("span",null,u(l.label),1),e.modelValue===l.value?(_(),c(g,{key:0,icon:"fluent-color:checkmark-24",class:"option-check"})):w("",!0)],10,C))),128))],4)):w("",!0)]),_:1})]))],512)}}}),[["__scopeId","data-v-3ceed415"]])
export{b as C}

15
frontend/dist/assets/RX4g3b5N.js vendored Normal file
View File

@@ -0,0 +1,15 @@
import{d as a,r as p,w as s,o as e,c as t,a as n,n as l,b as o,e as i,m as u,C as d,P as r,z as c,g as v,t as m,l as h,u as g,H as y,h as b}from"./CdD4XvnD.js"
import{c as k,A as w,_ as f,T as j,a as I}from"./BaSQ3xJt.js"
import{u as C}from"./Bt6cakcM.js"
const V={key:0,class:"status-badge"},_={key:0,class:"badge upgrade"},A={key:1,class:"badge injected"},E={class:"card-content"},D={class:"app-icon-wrapper"},U={class:"app-icon"},$=["src","alt"],B={class:"app-info"},R={class:"app-name"},T={key:0,class:"app-rating"},x={key:1,class:"category-tag"},z={key:2,class:"version-info"},H={key:0,class:"version-item"},L={class:"version-value"},N={key:0,class:"version-full"},P={key:1,class:"version-item latest"},q={class:"version-label"},F={class:"version-value"},G={key:0,class:"version-full"},J={key:2,class:"version-item bundle-id"},K={class:"version-value bundle-id-value"},M={key:3,class:"app-description"},O={class:"app-actions"},Q=["disabled","title"],S={key:4,class:"button-tooltip"},W=I(a({__name:"AppCard",props:{app:{},mode:{default:"list"},showDescription:{type:Boolean,default:!1},updating:{type:Boolean,default:!1}},emits:["updated","openVersionDialog","inject"],setup(a,{emit:I}){const W=a,X=I,Y=y(),Z=p(!1),aa=p(!1),pa=p(null),{toasts:sa,warning:ea,removeToast:ta}=k(),{loadCategoryDictionary:na,getCategoryNames:la}=C(),oa=async()=>{if(W.app.iconId)try{const a=await w.getIcon(W.app.iconId,80,128,128)
pa.value=a,aa.value=!1}catch(a){pa.value=null,aa.value=!0}else pa.value=null}
s(()=>W.app.iconId,()=>{oa()},{immediate:!0}),e(async()=>{await na(),W.app.iconId&&oa()})
const ia=t(()=>W.app.iconId&&pa.value?pa.value:W.app.path&&W.app.hasIcon?w.getAppIconUrl(W.app.bundleId):null),ua=t(()=>W.app.category&&W.app.category.length>0?la(W.app.category).join(", "):""),da=t(()=>{if(!W.app.path||0===W.app.path.length)return!1
const a="upgrade_available"===W.app.status,p=null!==W.app.update&&!0===W.app.update.hasUpdate
return a||p}),ra=t(()=>{var a
return!(W.app.path||(null==(a=W.app.update)?void 0:a.versions)&&0!==W.app.update.versions.length)}),ca=()=>{var a
return W.updating?"处理中...":W.app.path?da.value?"更新":"injected"===W.app.status?"已安装":"supported"===W.app.status?"一键激活":"unsupported"===W.app.status?"不支持":"一键激活":((null==(a=W.app.update)?void 0:a.versions)&&W.app.update.versions.length,"下载")},va=()=>{W.app.path&&Y.push(`/app/${W.app.bundleId}`)},ma=()=>{var a,p
W.app.path?da.value&&(null==(p=W.app.update)?void 0:p.versions)&&W.app.update.versions.length>0?window.dispatchEvent(new CustomEvent("open-version-dialog",{detail:W.app})):da.value||"supported"===W.app.status?X("inject",W.app):"injected"!==W.app.status&&W.app.status:(null==(a=W.app.update)?void 0:a.versions)&&W.app.update.versions.length>0?window.dispatchEvent(new CustomEvent("open-version-dialog",{detail:W.app})):ea("该应用暂无可用的下载版本",3e3)},ha=()=>{Z.value=!0,aa.value=!1},ga=()=>{Z.value=!1,aa.value=!0}
return(p,s)=>{var e,t,y
return b(),n("div",{class:l(["app-card",[`mode-${a.mode}`,{"not-installed":!a.app.path}]]),onClick:va},["list"===a.mode?(b(),n("div",V,[da.value?(b(),n("div",_,[...s[0]||(s[0]=[i("span",null,"可更新",-1)])])):"injected"===a.app.status?(b(),n("div",A,[...s[1]||(s[1]=[i("span",null,"已注入",-1)])])):o("",!0)])):o("",!0),i("div",E,[i("div",D,[i("div",U,[ia.value?d((b(),n("img",{key:0,src:ia.value,alt:a.app.name,onLoad:ha,onError:ga},null,40,$)),[[r,Z.value&&!aa.value]]):o("",!0),d(i("div",{class:"icon-placeholder"},c(a.app.name.charAt(0).toUpperCase()),513),[[r,!ia.value||!Z.value||aa.value]])])]),i("div",B,[i("h3",R,c(a.app.name),1),"search"===a.mode?(b(),n("div",T,[u(f,{icon:"fluent-color:star-24",class:"star-icon"}),s[2]||(s[2]=i("span",null,"4.5",-1))])):o("",!0),ua.value?(b(),n("div",x,[i("span",null,c(ua.value),1)])):o("",!0),"list"===a.mode?(b(),n("div",z,[a.app.path?(b(),n("div",H,[s[3]||(s[3]=i("span",{class:"version-label"},"当前",-1)),i("span",L,[v(c(a.app.version||"未知")+" ",1),a.app.bundleVersion?(b(),n("span",N,"("+c(a.app.bundleVersion)+")",1)):o("",!0)])])):o("",!0),(null==(e=a.app.update)?void 0:e.latestVersion)?(b(),n("div",P,[i("span",q,c(a.app.path?"最新":"版本"),1),i("span",F,[v(c(a.app.update.latestVersion)+" ",1),(null==(y=null==(t=a.app.update.versions)?void 0:t[0])?void 0:y.fullVersion)?(b(),n("span",G,"("+c(a.app.update.versions[0].fullVersion)+")",1)):o("",!0)])])):o("",!0),a.app.path?o("",!0):(b(),n("div",J,[s[4]||(s[4]=i("span",{class:"version-label"},"包名",-1)),i("span",K,c(a.app.bundleId),1)]))])):o("",!0),"search"===a.mode&&a.showDescription?(b(),n("div",M,c(`${W.app.name}是一款优秀的应用程序,提供丰富的功能和良好的用户体验。`),1)):o("",!0)]),i("div",O,[i("button",{class:l(["action-button",!W.app.path&&ra.value?"disabled":da.value?"warning":"injected"===W.app.status?"success":"supported"===W.app.status?"primary":"unsupported"===W.app.status?"disabled":"primary"]),onClick:m(ma,["stop"]),disabled:a.updating||ra.value,title:!W.app.path&&ra.value?"该应用暂无可用的下载版本":W.updating?"正在处理中...":""},[da.value?(b(),h(f,{key:0,icon:"mdi:arrow-down-circle",class:"button-icon"})):"supported"===W.app.status?(b(),h(f,{key:1,icon:"mdi:key",class:"button-icon"})):"injected"===W.app.status?(b(),h(f,{key:2,icon:"mdi:check-circle",class:"button-icon"})):W.app.path?o("",!0):(b(),h(f,{key:3,icon:"mdi:download",class:"button-icon"})),i("span",null,c(ca()),1),ra.value&&!W.app.path?(b(),n("span",S," 该应用暂无可用的下载版本 ")):o("",!0)],10,Q)])]),u(j,{toasts:g(sa),onRemove:g(ta)},null,8,["toasts","onRemove"])],2)}}}),[["__scopeId","data-v-8a1a07a3"]])
export{W as A}

1
frontend/dist/assets/U1EmyDQg.css vendored Normal file
View File

@@ -0,0 +1 @@
.reply-textarea[data-v-b3def6f7]{width:100%;padding:12px;border:1px solid var(--border-light);border-radius:8px;background:var(--bg-card);color:var(--text-primary);font-size:14px;font-family:inherit;resize:vertical;min-height:60px}.reply-textarea[data-v-b3def6f7]:focus{outline:none;border-color:var(--primary-color)}.edit-attachments-list[data-v-b3def6f7]{display:flex;flex-wrap:wrap;gap:8px;margin-top:8px}.edit-attachment-item[data-v-b3def6f7]{display:flex;align-items:center;gap:8px;padding:8px 12px;background:var(--bg-secondary);border-radius:6px;font-size:13px}.edit-new-attachments[data-v-b3def6f7]{display:flex;flex-wrap:wrap;gap:8px;margin-top:8px}.edit-attachment-preview[data-v-b3def6f7]{position:relative;width:80px;height:80px;border-radius:6px;overflow:hidden;background:var(--bg-secondary);cursor:pointer}.edit-attachment-preview img[data-v-b3def6f7]{width:100%;height:100%;object-fit:cover;-webkit-user-select:none;user-select:none;pointer-events:auto}.edit-attachment-preview img.clickable-image[data-v-b3def6f7]{cursor:pointer;transition:transform .2s ease}.edit-attachment-preview:hover img.clickable-image[data-v-b3def6f7]{transform:scale(1.05)}.edit-attachment-preview .remove-attachment-btn[data-v-b3def6f7]{pointer-events:auto}.edit-attachment-preview .image-loading[data-v-b3def6f7]{width:100%;height:100%;display:flex;align-items:center;justify-content:center;background:var(--bg-secondary)}.edit-attachment-preview .image-loading .spinner[data-v-b3def6f7]{width:24px;height:24px;border:2px solid var(--border-light);border-top-color:var(--primary-color);border-radius:50%;animation:spin-b3def6f7 .8s linear infinite}.edit-attachment-preview.file[data-v-b3def6f7]{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:8px;width:auto;height:auto;min-width:120px}.edit-attachment-actions[data-v-b3def6f7]{display:flex;gap:8px;margin-top:8px}.upload-btn[data-v-b3def6f7]{display:flex;align-items:center;gap:6px;padding:4px 8px;background:transparent;border:1px solid var(--border-light);border-radius:6px;color:var(--text-secondary);font-size:12px;cursor:pointer;transition:all .2s ease}.upload-btn[data-v-b3def6f7]:hover{color:var(--text-primary);border-color:var(--primary-color);background:var(--bg-secondary)}.upload-btn.small[data-v-b3def6f7]{padding:4px 8px;font-size:12px}.edit-reply-actions[data-v-b3def6f7]{display:flex;gap:8px;margin-top:8px}.cancel-btn[data-v-b3def6f7]{padding:6px 12px;background:transparent;border:1px solid var(--border-light);border-radius:6px;color:var(--text-secondary);font-size:13px;cursor:pointer;transition:all .2s ease}.cancel-btn[data-v-b3def6f7]:hover{color:var(--text-primary);border-color:var(--primary-color)}.submit-reply-btn[data-v-b3def6f7]{display:flex;align-items:center;gap:6px;padding:6px 12px;background:var(--primary-color);border:none;border-radius:6px;color:#fff;font-size:13px;font-weight:500;cursor:pointer;transition:all .2s ease}.submit-reply-btn[data-v-b3def6f7]:hover:not(:disabled){opacity:.9;transform:translateY(-1px)}.submit-reply-btn[data-v-b3def6f7]:disabled{opacity:.5;cursor:not-allowed}.submit-reply-btn.small[data-v-b3def6f7]{padding:6px 12px;font-size:13px}.attachment-preview-list[data-v-b3def6f7]{display:flex;flex-wrap:wrap;gap:8px;margin-top:12px}.attachment-preview-item[data-v-b3def6f7]{position:relative;width:80px;height:80px;border-radius:6px;overflow:hidden;background:var(--bg-secondary)}.attachment-preview-item img[data-v-b3def6f7]{width:100%;height:100%;object-fit:cover}.attachment-preview-item.file[data-v-b3def6f7]{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:8px;width:auto;height:auto;min-width:120px}.attachment-preview-item.file .file-icon[data-v-b3def6f7]{font-size:24px;color:var(--text-secondary);margin-bottom:4px}.attachment-preview-item.file .file-name[data-v-b3def6f7]{font-size:11px;color:var(--text-primary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:100px}.attachment-preview-item.file .file-size[data-v-b3def6f7]{font-size:10px;color:var(--text-tertiary)}.remove-attachment-btn[data-v-b3def6f7]{position:absolute;top:4px;right:4px;width:20px;height:20px;background:#0009;border:none;border-radius:50%;color:#fff;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all .2s ease;z-index:10}.remove-attachment-btn[data-v-b3def6f7]:hover{background:#000c}.remove-attachment-btn[data-v-b3def6f7] svg{font-size:12px}.attachment-error[data-v-b3def6f7]{position:absolute;bottom:0;left:0;right:0;background:#f00c;color:#fff;font-size:10px;padding:2px 4px;text-align:center}@keyframes spin-b3def6f7{to{transform:rotate(360deg)}}

1
frontend/dist/assets/_hNxiUA0.css vendored Normal file

File diff suppressed because one or more lines are too long

4
frontend/dist/assets/_iI2y1H4.js vendored Normal file
View File

@@ -0,0 +1,4 @@
import{r as e,c as l}from"./CdD4XvnD.js"
function n(){const n=e(!1),o=e({title:"",type:"info",confirmText:"确定"})
let u=null
return{visible:l(()=>n.value),config:l(()=>o.value),show:e=>(o.value={...o.value,...e},n.value=!0,new Promise(e=>{u=e})),handleConfirm:()=>{n.value=!1,u&&(u(),u=null)}}}export{n as u}

1
frontend/dist/assets/ftZEZk-2.css vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

11
frontend/dist/assets/j0cGtmjd.js vendored Normal file
View File

@@ -0,0 +1,11 @@
import{d as e,r as a,c as l,w as i,o as n,l as s,m as o,p as t,q as u,a as v,b as r,e as d,t as m,z as c,T as g,h as p,k as f}from"./CdD4XvnD.js"
import{_ as h,a as k}from"./BaSQ3xJt.js"
const b={class:"image-container"},y=["src","alt"],w={key:1,class:"loading-state"},I={key:2,class:"error-state"},x={key:2,class:"image-info"},C={class:"image-index"},B={key:0,class:"image-size"},_=k(e({__name:"ImagePreviewModal",props:{visible:{type:Boolean},imageUrl:{default:null},images:{default:()=>[]},imagesInfo:{default:()=>[]},initialIndex:{default:0},showNavigation:{type:Boolean,default:!0},showInfo:{type:Boolean,default:!0}},emits:["close","indexChange"],setup(e,{emit:k}){const _=e,$=k,z=a(_.initialIndex),E=a(null),N=a(!1),U=a(!1),j=l(()=>_.imagesInfo&&_.imagesInfo.length>0?_.imagesInfo.map(e=>e.url):_.images&&_.images.length>0?_.images:_.imageUrl?[_.imageUrl]:[]),q=l(()=>{if(_.imagesInfo&&_.imagesInfo.length>0){const e=_.imagesInfo[z.value]
return null==e?void 0:e.size}}),A=()=>{if(0===j.value.length)return void(E.value=null)
const e=j.value[z.value]
if(!e)return U.value=!0,void(E.value=null)
N.value=!1,U.value=!1,E.value=e},F=()=>{z.value>0&&(z.value--,$("indexChange",z.value))},K=()=>{z.value<j.value.length-1&&(z.value++,$("indexChange",z.value))},L=()=>{$("close")},M=e=>{"Escape"===e.key?L():"ArrowLeft"===e.key?F():"ArrowRight"===e.key&&K()},S=()=>{U.value=!1},P=()=>{U.value=!0,E.value=null}
return i(()=>_.visible,e=>{e?(z.value=_.initialIndex,A(),f(()=>{const e=document.querySelector(".image-preview-modal")
e&&e.focus()})):(E.value=null,N.value=!1,U.value=!1)},{immediate:!0}),i(z,()=>{A()}),i(()=>_.images,()=>{_.visible&&A()}),n(()=>{_.visible&&A()}),(a,l)=>(p(),s(g,{to:"body"},[o(t,{name:"preview-fade"},{default:u(()=>{return[e.visible?(p(),v("div",{key:0,class:"image-preview-modal",onClick:L,onKeydown:M,tabindex:"-1"},[d("div",{class:"preview-content",onClick:l[0]||(l[0]=m(()=>{},["stop"]))},[e.showNavigation&&z.value>0?(p(),v("button",{key:0,class:"nav-btn prev-btn",onClick:m(F,["stop"]),title:"上一张 (←)"},[o(h,{icon:"mdi:chevron-left"})])):r("",!0),d("div",b,[E.value?(p(),v("img",{key:0,src:E.value,alt:`图片 ${z.value+1}`,onLoad:S,onError:P},null,40,y)):N.value?(p(),v("div",w,[...l[1]||(l[1]=[d("div",{class:"spinner"},null,-1),d("p",null,"加载中...",-1)])])):U.value?(p(),v("div",I,[o(h,{icon:"mdi:image-broken"}),l[2]||(l[2]=d("p",null,"图片加载失败",-1))])):r("",!0)]),e.showNavigation&&z.value<j.value.length-1?(p(),v("button",{key:1,class:"nav-btn next-btn",onClick:m(K,["stop"]),title:"下一张 (→)"},[o(h,{icon:"mdi:chevron-right"})])):r("",!0),d("button",{class:"close-preview-btn",onClick:m(L,["stop"]),title:"关闭 (ESC)"},[o(h,{icon:"mdi:close"})]),e.showInfo&&j.value.length>0?(p(),v("div",x,[d("span",C,c(z.value+1)+" / "+c(j.value.length),1),q.value?(p(),v("span",B,c((a=q.value,a?a<1024?`${a} B`:a<1048576?`${(a/1024).toFixed(1)} KB`:`${(a/1048576).toFixed(1)} MB`:"")),1)):r("",!0)])):r("",!0)])],32)):r("",!0)]
var a}),_:1})]))}}),[["__scopeId","data-v-a54a074d"]])
export{_ as I}

108
frontend/dist/assets/kWK-Q7gJ.js vendored Normal file
View File

@@ -0,0 +1,108 @@
import{d as e,r as a,c as l,w as t,Q as s,a as o,e as u,l as n,m as i,C as r,b as v,P as c,z as d,p,q as m,v as f,T as h,h as b,n as g,t as y,u as k,H as w,F as I,B as C,A as P,o as x,I as S}from"./CdD4XvnD.js"
import{_ as E,A as L,a as F,b as _,d as T,c as $,j as M,W as N,k as U,T as A,g as R}from"./BaSQ3xJt.js"
import{g as D}from"./6K6b4Qy_.js"
import{C as j}from"./Nkv5t_uh.js"
import{U as z}from"./Birl3CuV.js"
import{u as B}from"./4xYhABhf.js"
import{g as O,a as Y,f as W,b as H}from"./BqTCaadz.js"
import{u as q,R as X,a as Q}from"./COGASUq6.js"
import"./CFhT0fBm.js"
const V={class:"page-header"},G={class:"header-buttons"},J={key:0,class:"header-content"},K={class:"app-header"},Z={class:"app-icon-wrapper"},ee=["src","alt"],ae={class:"app-title-info"},le={class:"page-title"},te={key:0,class:"page-description"},se={key:1,class:"page-description"},oe={key:2,class:"forum-stats"},ue={class:"stat-item"},ne={class:"stat-item"},ie={key:1,class:"header-content"},re=["src","alt"],ve={key:1,class:"preview-loading"},ce=F(e({__name:"ForumHeader",props:{bundleId:{},appInfo:{},forumPermission:{},builtinForum:{}},emits:["back","home"],setup(e){const g=e,y=a(!1),k=a(!1),w=l(()=>{var e
return"builtin"===(null==(e=g.builtinForum)?void 0:e.type)}),I=l(()=>{var e
return"created"===(null==(e=g.builtinForum)?void 0:e.type)}),C=l(()=>g.builtinForum?D(g.builtinForum.bundleId):"mdi:forum-outline"),P=a(null),x=a(!1),S=a(!1),F=a(null),_=a(!1),T=a(!1),$=l(()=>{var e
return(null==(e=g.builtinForum)?void 0:e.description)??""}),M=l(()=>g.appInfo?w.value?"":I.value?P.value||"":F.value||"":""),N=a(!1),U=a(null),A=a(!1),R=a(null),j=a({top:"0px",left:"0px"}),z=a({x:0,y:0}),B=e=>{const a=e.target
R.value={width:a.naturalWidth,height:a.naturalHeight},z.value.x&&z.value.y&&O(z.value.x,z.value.y)},O=(e,a)=>{var l,t
const s=20,o=(null==(l=R.value)?void 0:l.width)||400,u=(null==(t=R.value)?void 0:t.height)||400,n=window.innerWidth,i=window.innerHeight,r=Math.min(o,.9*n),v=Math.min(u,.9*i),c=Math.min(o,r),d=Math.min(u,v)
let p=e+s,m=a+s
p+c/2>n&&(p=e-s-c/2),p-c/2<0&&(p=e+s+c/2),m+d>i&&(m=a-s-d),m<0&&(m=a+s),p=Math.max(c/2,Math.min(p,n-c/2)),m=Math.max(0,Math.min(m,i-d)),j.value={top:`${m}px`,left:`${p}px`}},Y=e=>{N.value&&(z.value={x:e.clientX,y:e.clientY},O(e.clientX,e.clientY))},W=async e=>{if(M.value&&y.value&&!k.value&&!w.value){z.value={x:e.clientX,y:e.clientY},O(e.clientX,e.clientY),N.value=!0,A.value=!0,U.value=null,document.addEventListener("mousemove",Y)
try{const e=await(async()=>{var e,a
if(!g.appInfo)return null
if(I.value&&(null==(e=g.builtinForum)?void 0:e.iconId))try{return await L.getIcon(g.builtinForum.iconId,void 0,400,400)}catch(l){return P.value}if(null==(a=g.forumPermission)?void 0:a.iconId)try{return await L.getIcon(g.forumPermission.iconId,void 0,400,400)}catch(l){return F.value}return null})()
e?(U.value=e,A.value=!1):(N.value=!1,document.removeEventListener("mousemove",Y))}catch(a){N.value=!1,document.removeEventListener("mousemove",Y)}}},H=()=>{N.value=!1,U.value=null,R.value=null,document.removeEventListener("mousemove",Y)}
return t(()=>[g.appInfo,g.builtinForum,g.forumPermission],()=>{(async()=>{var e,a
if(g.appInfo)if(I.value&&(null==(e=g.builtinForum)?void 0:e.iconId))try{const e=await L.getIcon(g.builtinForum.iconId,80,128,128)
P.value=e,x.value=!0,S.value=!1,y.value=!0,k.value=!1}catch(l){S.value=!0,x.value=!0,y.value=!0,k.value=!0}else if(w.value)y.value=!0,k.value=!1
else if(null==(a=g.forumPermission)?void 0:a.iconId)try{const e=await L.getIcon(g.forumPermission.iconId,80,128,128)
F.value=e,_.value=!0,T.value=!1,y.value=!0,k.value=!1}catch(l){T.value=!0,_.value=!0,y.value=!0,k.value=!0}else y.value=!0,k.value=!1})()},{immediate:!0}),s(()=>{document.removeEventListener("mousemove",Y)}),(a,l)=>{var t,s,g,P,x,S
return b(),o("div",V,[u("div",G,[u("button",{class:"back-button",onClick:l[0]||(l[0]=e=>a.$emit("back"))},[i(E,{icon:"mdi:arrow-left",class:"back-icon"}),l[4]||(l[4]=u("span",null,"返回讨论区",-1))]),u("button",{class:"home-button",onClick:l[1]||(l[1]=e=>a.$emit("home"))},[i(E,{icon:"fluent-color:home-48",class:"home-icon"}),l[5]||(l[5]=u("span",null,"首页",-1))])]),e.appInfo?(b(),o("div",J,[u("div",K,[u("div",Z,[u("div",{class:"app-icon",onMouseenter:W,onMouseleave:H},[M.value&&!w.value?r((b(),o("img",{key:0,src:M.value,alt:(null==(t=e.forumPermission)?void 0:t.appName)||(null==(s=e.appInfo)?void 0:s.name)||e.bundleId,onLoad:l[2]||(l[2]=e=>y.value=!0),onError:l[3]||(l[3]=e=>k.value=!0)},null,40,ee)),[[c,y.value&&!k.value]]):v("",!0),w.value?(b(),n(E,{key:1,icon:C.value,class:"builtin-forum-icon"},null,8,["icon"])):v("",!0),r(u("div",{class:"icon-placeholder"},d(null==(P=(null==(g=e.forumPermission)?void 0:g.appName)||e.bundleId)?void 0:P.charAt(0).toUpperCase()),513),[[c,(!M.value||!y.value||k.value)&&!w.value&&!I.value]])],32)]),u("div",ae,[u("h1",le,d((null==(x=e.forumPermission)?void 0:x.appName)||(null==(S=e.appInfo)?void 0:S.name)||e.bundleId),1),w.value?v("",!0):(b(),o("p",te,d(e.bundleId),1)),w.value&&$.value?(b(),o("p",se,d($.value),1)):v("",!0),e.forumPermission?(b(),o("div",oe,[u("span",ue,[i(E,{icon:"fluent-color:news-28",class:"stat-icon"}),u("span",null,"今日发帖:"+d(e.forumPermission.todayPostCount??0),1)]),u("span",ne,[i(E,{icon:"fluent-color:reward-24",class:"stat-icon"}),u("span",null,"总发帖数:"+d(e.forumPermission.totalPostCount??0),1)])])):v("",!0)])])])):(b(),o("div",ie,[...l[6]||(l[6]=[u("h1",{class:"page-title"},"讨论区",-1)])])),(b(),n(h,{to:"body"},[i(p,{name:"icon-preview-fade"},{default:m(()=>{var a
return[N.value?(b(),o("div",{key:0,class:"icon-preview-overlay",style:f(j.value)},[U.value&&!A.value?(b(),o("img",{key:0,src:U.value,alt:(null==(a=e.appInfo)?void 0:a.name)||"图标预览",class:"preview-image",onLoad:B},null,40,re)):(b(),o("div",ve,[...l[7]||(l[7]=[u("div",{class:"loading-spinner"},null,-1),u("p",null,"加载中...",-1)])]))],4)):v("",!0)]}),_:1})]))])}}}),[["__scopeId","data-v-b7457a0d"]]),de={class:"post-header"},pe={class:"post-title"},me={class:"post-meta"},fe={class:"author-avatar"},he={class:"post-author clickable"},be={class:"post-time"},ge={class:"post-content-preview"},ye={class:"post-footer"},ke={class:"post-stats"},we={class:"stat-item"},Ie={class:"post-actions"},Ce=["disabled"],Pe=["disabled"],xe=F(e({__name:"PostCard",props:{post:{},likeLoading:{type:Boolean,default:!1}},emits:["click","like","dislike","report"],setup(e,{emit:t}){const s=e,n=w(),{isUserOnline:r}=B(),v=l(()=>O(s.post)),c=l(()=>{const e=v.value
return e?r(e).value:null}),p=a(!1),m=a(void 0),f=a(""),h=a(""),I=a(""),C=a(void 0),P=a(null),x=a(null),S=l(()=>!!s.post.isNew),L=()=>{P.value&&(clearTimeout(P.value),P.value=null),x.value&&(clearTimeout(x.value),x.value=null),x.value=window.setTimeout(()=>{p.value&&(p.value=!1)},200)},F=()=>{x.value&&(clearTimeout(x.value),x.value=null)},T=()=>{P.value&&(clearTimeout(P.value),P.value=null),x.value&&(clearTimeout(x.value),x.value=null),p.value=!1,C.value=void 0,I.value=""},$=(e,a)=>{n.push(`/messages?userId=${e}&userName=${encodeURIComponent(a)}`)},M=(e,a)=>{}
return(a,l)=>{return b(),o("div",{class:g(["post-card",{"post-new":S.value}]),onClick:l[4]||(l[4]=l=>a.$emit("click",e.post.id))},[u("div",de,[u("h3",pe,d(e.post.title),1),u("div",me,[u("div",{class:"author-wrapper",onMouseenter:l[0]||(l[0]=y(a=>((e,a)=>{P.value&&(clearTimeout(P.value),P.value=null)
const l=O(a),t=Y(a),s=H(a)
f.value=l,h.value=t,I.value=s||"",C.value=a.author.avatar,P.value=window.setTimeout(()=>{const a=e.target.getBoundingClientRect()
m.value={x:a.left-100,y:a.bottom+8},p.value=!0},200)})(a,e.post),["stop"])),onMouseleave:y(L,["stop"])},[u("div",fe,[i(_,{"avatar-id":e.post.author.avatar,"user-name":k(Y)(e.post),size:"small"},null,8,["avatar-id","user-name"]),u("div",{class:g(["online-indicator",{online:!0===c.value,offline:!1===c.value}])},null,2)]),u("span",he,d(k(Y)(e.post)),1)],32),u("span",be,d((s=e.post.publishTime,W(s))),1)])]),u("div",ge,[u("p",null,d((t=e.post.content,t.length<=150?t:t.substring(0,150)+"...")),1)]),u("div",ye,[u("div",ke,[u("span",we,[i(E,{icon:"mdi:comment-outline",class:"stat-icon"}),u("span",null,d(e.post.replyCount)+" 回复",1)])]),u("div",Ie,[u("button",{class:g(["action-btn",{active:"liked"===e.post.userLikeStatus}]),onClick:l[1]||(l[1]=y(l=>a.$emit("like",e.post.id),["stop"])),disabled:e.likeLoading},[i(E,{icon:"liked"===e.post.userLikeStatus?"mdi:thumb-up":"mdi:thumb-up-outline",class:"action-icon"},null,8,["icon"]),u("span",null,d(e.post.likeCount),1)],10,Ce),u("button",{class:g(["action-btn",{active:"disliked"===e.post.userLikeStatus}]),onClick:l[2]||(l[2]=y(l=>a.$emit("dislike",e.post.id),["stop"])),disabled:e.likeLoading},[i(E,{icon:"disliked"===e.post.userLikeStatus?"mdi:thumb-down":"mdi:thumb-down-outline",class:"action-icon"},null,8,["icon"]),u("span",null,d(e.post.dislikeCount),1)],10,Pe),u("button",{class:"action-btn report-btn",onClick:l[3]||(l[3]=y(l=>a.$emit("report",e.post.id),["stop"]))},[i(E,{icon:"mdi:flag-outline",class:"action-icon"}),l[5]||(l[5]=u("span",null,"举报",-1))])])]),i(z,{visible:p.value,"user-id":f.value,"user-name":h.value,username:I.value,"avatar-id":C.value,position:m.value,onClose:T,onSendMessage:$,onBlockUser:M,onMouseEnter:F},null,8,["visible","user-id","user-name","username","avatar-id","position"])],2)
var t,s}}}),[["__scopeId","data-v-ba1a863f"]]),Se={class:"post-list-container"},Ee={class:"create-post-section"},Le={class:"section-header"},Fe={class:"sort-selector"},_e={key:1,class:"empty-discussion"},Te=F(e({__name:"PostList",props:{posts:{},sortBy:{},showPostForm:{type:Boolean},likeLoadingPostId:{}},emits:["show-post-form","sort-change","post-click","like","dislike","report"],setup(e){const a=[{value:"time",label:"按时间",icon:"fluent-color:clock-24"},{value:"lastReply",label:"按最后回复",icon:"fluent-color:mail-clock-32"}]
return(l,t)=>(b(),o("div",Se,[u("div",Ee,[u("div",Le,[e.showPostForm?v("",!0):(b(),o("button",{key:0,class:"create-post-btn",onClick:t[0]||(t[0]=e=>l.$emit("show-post-form"))},[i(E,{icon:"mdi:plus",class:"btn-icon"}),t[6]||(t[6]=u("span",null,"发帖",-1))])),u("div",Fe,[t[7]||(t[7]=u("label",{class:"sort-label"},"排序方式:",-1)),i(j,{"model-value":e.sortBy,options:a,"onUpdate:modelValue":t[1]||(t[1]=e=>l.$emit("sort-change",e))},null,8,["model-value"])])])]),e.posts.length>0?(b(),n(P,{key:0,name:"post-fade",tag:"div",class:"posts-list"},{default:m(()=>[(b(!0),o(I,null,C(e.posts,a=>(b(),n(xe,{key:a.id,post:a,"like-loading":e.likeLoadingPostId===a.id,onClick:t[2]||(t[2]=e=>l.$emit("post-click",e)),onLike:t[3]||(t[3]=e=>l.$emit("like",e)),onDislike:t[4]||(t[4]=e=>l.$emit("dislike",e)),onReport:t[5]||(t[5]=e=>l.$emit("report",e))},null,8,["post","like-loading"]))),128))]),_:1})):(b(),o("div",_e,[i(E,{icon:"fluent-color:chat-bubbles-question-24",class:"empty-icon"}),t[8]||(t[8]=u("p",null,"暂无讨论内容",-1)),t[9]||(t[9]=u("p",{class:"empty-hint"},"还没有人发布帖子",-1))]))]))}}),[["__scopeId","data-v-9a486f50"]]),$e={class:"upload-progress-content"},Me={class:"progress-info"},Ne={class:"progress-text"},Ue={class:"progress-bar-container"},Ae={class:"progress-bar"},Re={class:"progress-percent"},De={key:0,class:"error-message"},je={class:"dialog-footer"},ze=F(e({__name:"UploadProgressDialog",props:{visible:{type:Boolean},progress:{},title:{default:"上传中..."},statusText:{default:"正在上传文件"},error:{},cancellable:{type:Boolean,default:!0},completed:{type:Boolean,default:!1}},emits:["cancel","close"],setup(e,{emit:a}){const l=e,t=a,s=()=>{l.cancellable&&!l.completed&&t("cancel")},r=()=>{t("close")}
return(a,l)=>(b(),n(T,{visible:e.visible,"max-width":400,"close-on-overlay-click":e.cancellable&&!e.completed,"show-close-button":e.cancellable&&!e.completed,"close-button-position":"header","enable-keyboard":e.cancellable&&!e.completed,onClose:s},{header:m(()=>[u("h3",null,d(e.title),1)]),footer:m(()=>[u("div",je,[e.cancellable&&!e.completed?(b(),o("button",{key:0,class:"btn btn-secondary",onClick:s}," 取消 ")):v("",!0),e.completed||e.error?(b(),o("button",{key:1,class:"btn btn-primary",onClick:r}," 确定 ")):v("",!0)])]),default:m(()=>[u("div",$e,[u("div",Me,[i(E,{icon:"mdi:cloud-upload",class:"progress-icon"}),u("span",Ne,d(e.statusText),1)]),u("div",Ue,[u("div",Ae,[u("div",{class:"progress-fill",style:f({width:`${e.progress}%`})},null,4)]),u("span",Re,d(e.progress)+"%",1)]),e.error?(b(),o("div",De,[i(E,{icon:"mdi:alert-circle",class:"error-icon"}),u("span",null,d(e.error),1)])):v("",!0)])]),_:1},8,["visible","close-on-overlay-click","show-close-button","enable-keyboard"]))}}),[["__scopeId","data-v-a922ff57"]]),Be={class:"discussion-forum-page"},Oe={class:"page-container"},Ye={key:0,class:"loading"},We={key:1,class:"error"},He={key:2,class:"discussion-content"},qe=F(e({__name:"DiscussionForum",setup(e){const r=w(),c=S(),f=$(),h=R(),g=l(()=>c.params.bundleId),y=a(null),I=a(!0),C=a(""),P=a(null),E=a([]),F=a(null),_=a("time"),T=a(!1),D=a(null),j=a([]),z=l(()=>te.showUploadProgress&&te.uploadProgress.value>0),Y=l(()=>te.showUploadProgress&&te.uploadProgress.value>0),W=l(()=>100===te.uploadProgress.value&&!te.uploadError.value),H=l(()=>te.uploadProgress.value<100&&!te.uploadError.value),V=l(()=>te.uploadProgress.value),G=l(()=>te.uploadStatusText.value),J=l(()=>te.uploadError.value),K=l(()=>te.showUploadProgress.value),Z=a(5),ee=a(5),ae=a(20971520),le=a(20971520),te=function(e,t,s,o,u,n){const i=$(),r=a(""),v=a(""),c=a(!1),d=a(0),p=a("正在上传..."),m=a(""),f=q({maxImageCount:t.value,maxFileCount:s.value,maxImageSize:o.value,maxFileSize:u.value}),{selectedImages:h,selectedFiles:b,uploadAttachments:g,clearAttachments:y,validateAttachments:k}=f,w=l(()=>r.value.trim().length>0&&(v.value.trim().length>0||h.value.length>0||b.value.length>0))
return{title:r,content:v,selectedImages:h,selectedFiles:b,...f,canSubmit:w,submitPost:async()=>{var a,l,t,s,o,u,f,g
if(w.value&&e.value&&k()){c.value=!0,d.value=0,p.value="正在上传文件...",m.value=""
try{const f=[],g=h.value.length+b.value.length
let k=0
for(let e=0;e<h.value.length;e++){const t=h.value[e]
p.value=`正在上传图片 ${e+1}/${h.value.length}...`
const s=await L.uploadImageAttachment(t.file,e=>{const a=e/100
d.value=Math.round((k+a)/g*100)})
if(!s.success||!(null==(a=s.data)?void 0:a.attachmentId))throw new Error(s.message||(null==(l=s.data)?void 0:l.message)||"图片上传失败")
f.push(s.data.attachmentId),k++}for(let e=0;e<b.value.length;e++){const a=b.value[e]
p.value=`正在上传文件 ${e+1}/${b.value.length}...`
const l=await L.uploadFileAttachment(a.file,e=>{const a=e/100
d.value=Math.round((k+a)/g*100)})
if(!l.success||!(null==(t=l.data)?void 0:t.attachmentId))throw new Error(l.message||(null==(s=l.data)?void 0:s.message)||"文件上传失败")
f.push(l.data.attachmentId),k++}p.value="正在发布帖子...",d.value=90
const w=await L.createForumPost(e.value,r.value.trim(),v.value.trim(),f.length>0?f:void 0)
w.success&&(null==(o=w.data)?void 0:o.postId)?(d.value=100,p.value="发布成功!",await new Promise(e=>setTimeout(e,500)),r.value="",v.value="",y(),c.value=!1,n&&await n(),i.success("帖子发布成功")):(m.value=w.message||(null==(u=w.data)?void 0:u.message)||"发帖失败,请稍后重试",p.value="发布失败")}catch(I){m.value=(null==(g=null==(f=I.response)?void 0:f.data)?void 0:g.message)||I.message||"发帖失败,请稍后重试",p.value="发布失败"}}},showUploadProgress:c,uploadProgress:d,uploadStatusText:p,uploadError:m,cancelUpload:()=>{c.value=!1,d.value=0,m.value=""},closeProgress:()=>{c.value=!1,d.value=0,m.value=""},resetForm:()=>{r.value="",v.value="",y()}}}(g,Z,ee,ae,le,async()=>{await ne(),T.value=!1}),se=a(!1),oe=a(null),{queryUsersOnlineStatus:ue}=B(),ne=async()=>{if(g.value)try{const e=await L.getForumPosts(g.value,1,20,_.value)
if(E.value=e.posts||[],E.value.length>0){const e=E.value.map(e=>O(e)).filter(e=>e),a=Array.from(new Set(e))
j.value=a,a.length>0&&ue(a).catch(e=>{})}else j.value=[]}catch(e){E.value=[],j.value=[]}},ie=e=>{_.value=e,ne()},re=async()=>{if(!g.value)return C.value="无效的应用标识符",void(I.value=!1)
I.value=!0,C.value=""
try{const e=await L.getForumByBundleId(g.value)
if(!e||"builtin"!==e.type&&"created"!==e.type){if(await(async e=>{try{const a=await L.getForumPermission(e)
F.value=a||{bundleId:e,appName:"",enabled:!1,postCount:0,todayPostCount:0,totalPostCount:0,createdAt:(new Date).toISOString(),updatedAt:(new Date).toISOString()}}catch(a){F.value={bundleId:e,appName:"",enabled:!1,postCount:0,todayPostCount:0,totalPostCount:0,createdAt:(new Date).toISOString(),updatedAt:(new Date).toISOString()}}})(g.value),!F.value||!F.value.enabled)return C.value="该讨论区暂未开放,请等待管理员启用",void(I.value=!1)
e&&(Z.value=e.maxImageCount||5,ee.value=e.maxFileCount||5,ae.value=e.maxImageSize||20971520,le.value=e.maxFileSize||20971520),y.value={bundleId:g.value,name:F.value.appName,version:"",path:"",status:"unsupported",injection:null,update:null,hasIcon:!!F.value.iconId,iconId:F.value.iconId}}else P.value=e,y.value={bundleId:e.bundleId,name:e.name,version:"",path:"",status:"unsupported",injection:null,update:null,hasIcon:!1},F.value={bundleId:g.value,appName:e.name,enabled:!0,postCount:0,todayPostCount:e.todayPostCount??0,totalPostCount:e.totalPostCount??0,createdAt:(new Date).toISOString(),updatedAt:(new Date).toISOString()},Z.value=e.maxImageCount||5,ee.value=e.maxFileCount||5,ae.value=e.maxImageSize||20971520,le.value=e.maxFileSize||20971520
await ne()}catch(e){C.value="加载讨论区信息失败,请检查服务是否运行",y.value=null}finally{I.value=!1}},ve=()=>{r.push("/discussion")},de=()=>{r.push("/")},pe=e=>{r.push(`/discussion/${g.value}/post/${e}`)},me=async e=>{var a,l
const t=E.value.find(a=>a.id===e)
if(!t||!g.value||D.value===e)return
const s="liked"===t.userLikeStatus?"cancel":"like"
D.value=e,h.show("cancel"===s?"取消点赞中...":"点赞中...")
try{const a=await L.likeForumPost(g.value,e,s)
a.success?(t.userLikeStatus=a.userLikeStatus,t.likeCount=a.likeCount,t.dislikeCount=a.dislikeCount,"cancel"===s?f.success("已取消点赞"):f.success("点赞成功")):f.error("点赞失败")}catch(o){f.error((null==(l=null==(a=o.response)?void 0:a.data)?void 0:l.message)||o.message||"点赞失败")}finally{D.value=null,h.hide()}},fe=async e=>{var a,l
const t=E.value.find(a=>a.id===e)
if(!t||!g.value||D.value===e)return
const s="disliked"===t.userLikeStatus?"cancel":"dislike"
D.value=e,h.show("cancel"===s?"取消点踩中...":"点踩中...")
try{const a=await L.likeForumPost(g.value,e,s)
a.success?(t.userLikeStatus=a.userLikeStatus,t.likeCount=a.likeCount,t.dislikeCount=a.dislikeCount,"cancel"===s?f.success("已取消点踩"):f.success("点踩成功")):f.error("点踩失败")}catch(o){f.error((null==(l=null==(a=o.response)?void 0:a.data)?void 0:l.message)||o.message||"点踩失败")}finally{D.value=null,h.hide()}},he=async(e,a,l,t)=>{t?(te.title.value=t,te.content.value=e,te.selectedImages.value=a,te.selectedFiles.value=l,await te.submitPost()):f.error("请输入帖子标题")},be=()=>{T.value=!1,te.resetForm()},ge=e=>{const a=E.value.find(a=>a.id===e)
a&&(oe.value={userId:a.author.id,userName:a.author.nickName||a.author.username,content:a.content,type:"post",postId:a.id},se.value=!0)},ye=async(e,a,l)=>{var t,s
if(g.value&&l.type&&l.postId){h.show("提交举报中...")
try{let t=""
if("post"===l.type?t=l.postId:("reply"===l.type||"nestedReply"===l.type)&&(t=l.replyId||""),!t)return
const s=await L.submitReport(l.type,t,g.value,l.postId,e,l.content,a||void 0)
s.success?(se.value=!1,f.success("举报提交成功")):f.error(s.message||"举报失败")}catch(o){f.error((null==(s=null==(t=o.response)?void 0:t.data)?void 0:s.message)||o.message||"举报失败")}finally{h.hide()}}}
return function(e,l,o,u,n){const{subscribeForum:i,unsubscribeForum:r,isConnected:v}=M(),{queryUsersOnlineStatus:c}=B(),d=a(null),p=a(!1),m=e=>{const a=e.detail
if(!a||!a.post)return
const t=a.post
if(l.value.findIndex(e=>e.id===t.id)>=0)return
t.isNew=!0,l.value.unshift(t),setTimeout(()=>{const e=l.value.find(e=>e.id===t.id)
e&&(e.isNew=!1)},3e3),o.value&&(o.value.totalPostCount=(o.value.totalPostCount||0)+1,o.value.todayPostCount=(o.value.todayPostCount||0)+1)
const s=n(t)
s&&c([s]).catch(e=>{})},f=e=>{const a=e.detail
if(!a||!a.reply||!a.postId)return
const t=a.reply,s=a.postId,o=l.value.findIndex(e=>e.id===s)
if(o<0)return
const n=l.value[o]
n.replies&&n.replies.findIndex(e=>e.id===t.id)>=0||(n.replyCount=(n.replyCount||0)+1,"lastReply"===u.value&&(l.value.splice(o,1),l.value.unshift(n)))},h=a=>{const t=a.detail
if(!t||!t.postId||!t.bundleId)return
const s=t.postId
if(t.bundleId!==e.value)return
const u=l.value.findIndex(e=>e.id===s)
u>=0&&(l.value.splice(u,1),o.value&&(o.value.totalPostCount=Math.max(0,(o.value.totalPostCount||0)-1)))},b=a=>{const t=a.detail
if(!(t&&t.replyId&&t.postId&&t.bundleId))return
const s=t.replyId,o=t.postId
if(t.bundleId!==e.value)return
const u=l.value.findIndex(e=>e.id===o)
if(u<0)return
const n=l.value[u]
if(n.replies){const e=n.replies.findIndex(e=>e.id===s)
e>=0&&n.replies.splice(e,1)}n.replyCount=Math.max(0,(n.replyCount||0)-1)},g=async()=>{if(e.value&&d.value!==e.value&&!p.value&&v.value){p.value=!0
try{await i(e.value),d.value=e.value}catch(C){}finally{p.value=!1}}},y=async()=>{if(d.value)try{await r(d.value),d.value=null}catch(C){}}
t(()=>v.value,a=>{a&&e.value&&setTimeout(()=>{v.value&&e.value&&g()},500)}),t(()=>e.value,async(e,a)=>{a&&a!==e&&d.value===a&&(await r(a),d.value=null),e&&v.value&&await g()}),x(()=>{window.addEventListener(N.NEW_POST,m),window.addEventListener(N.NEW_REPLY,f),window.addEventListener(N.DELETE_POST,h),window.addEventListener(N.DELETE_REPLY,b),v.value&&e.value&&g()}),s(async()=>{if(d.value)try{await Promise.race([y(),new Promise(e=>setTimeout(e,2e3))])}catch(C){}window.removeEventListener(N.NEW_POST,m),window.removeEventListener(N.NEW_REPLY,f),window.removeEventListener(N.DELETE_POST,h),window.removeEventListener(N.DELETE_REPLY,b)})}(g,E,F,_,O),x(()=>{re()}),s(()=>{j.value.length>0&&(U.removeUsers(j.value),j.value=[])}),(e,a)=>(b(),o("div",Be,[u("div",Oe,[i(ce,{"bundle-id":g.value,"app-info":y.value,"forum-permission":F.value,"builtin-forum":P.value,onBack:ve,onHome:de},null,8,["bundle-id","app-info","forum-permission","builtin-forum"]),I.value?(b(),o("div",Ye,[...a[2]||(a[2]=[u("div",{class:"spinner"},null,-1),u("p",null,"加载中...",-1)])])):C.value?(b(),o("div",We,[u("p",null,d(C.value),1),u("button",{onClick:re,class:"btn btn-primary"},"重试")])):y.value?(b(),o("div",He,[i(p,{name:"post-form-slide"},{default:m(()=>[T.value?(b(),n(X,{key:0,mode:"post","max-image-count":Z.value,"max-file-count":ee.value,"max-image-size":ae.value,"max-file-size":le.value,"is-submitting":z.value,onSubmit:he,onCancel:be},null,8,["max-image-count","max-file-count","max-image-size","max-file-size","is-submitting"])):v("",!0)]),_:1}),i(Te,{posts:E.value,"sort-by":_.value,"show-post-form":T.value,"like-loading-post-id":D.value,onShowPostForm:a[0]||(a[0]=e=>T.value=!0),onSortChange:ie,onPostClick:pe,onLike:me,onDislike:fe,onReport:ge},null,8,["posts","sort-by","show-post-form","like-loading-post-id"])])):v("",!0)]),i(Q,{visible:se.value,"report-info":oe.value,onClose:a[1]||(a[1]=e=>se.value=!1),onSubmit:ye},null,8,["visible","report-info"]),Y.value?(b(),n(ze,{key:0,visible:K.value,progress:V.value,title:G.value,"status-text":G.value,error:J.value,completed:W.value,cancellable:H.value,onCancel:k(te).cancelUpload,onClose:k(te).closeProgress},null,8,["visible","progress","title","status-text","error","completed","cancellable","onCancel","onClose"])):v("",!0),i(A,{toasts:k(f).toasts.value,onRemove:k(f).removeToast},null,8,["toasts","onRemove"])]))}}),[["__scopeId","data-v-a16ca9d9"]])
export{qe as default}

12
frontend/dist/assets/lyDeIg7R.js vendored Normal file
View File

@@ -0,0 +1,12 @@
import{A as e}from"./BaSQ3xJt.js"
import{c as a,h as t}from"./BqTCaadz.js"
import{r as s}from"./CdD4XvnD.js"
function o(){const o=s(!1),c=s([]),n=s(0),r=s(new Map)
return{previewVisible:o,previewImagesInfo:c,previewIndex:n,imageCache:r,preloadPostImages:async s=>{const o=a(s)
for(const a of o){const s=t(a)
if(!r.value.has(s))try{const a=await e.getAttachmentImage(s)
a&&r.value.set(s,a)}catch(c){}}},handleImageClick:async(s,l)=>{const i=a(s),u=[]
for(const a of i){const s=t(a)
let o=r.value.get(s)
if(!o)try{const a=await e.getAttachmentImage(s)
a&&(o=a,r.value.set(s,a))}catch(v){continue}o&&u.push({url:o,size:a.size})}u.length>0&&(c.value=u,n.value=Math.min(l,u.length-1),o.value=!0)},closePreview:()=>{o.value=!1,c.value=[],n.value=0},clearImageCache:()=>{r.value.forEach(e=>{e.startsWith("blob:")&&URL.revokeObjectURL(e)}),r.value.clear()}}}export{o as u}

5
frontend/dist/assets/nAC6k4LG.js vendored Normal file

File diff suppressed because one or more lines are too long

1
frontend/dist/assets/pjFLbtwi.css vendored Normal file
View File

@@ -0,0 +1 @@
.custom-select-wrapper[data-v-3ceed415]{position:relative;min-width:160px;z-index:1}.custom-select[data-v-3ceed415]{position:relative;cursor:pointer;-webkit-user-select:none;user-select:none}.custom-select.open .select-selected[data-v-3ceed415]{border-color:var(--primary-color);box-shadow:0 0 0 3px rgba(var(--primary-color-rgb),.1)}.custom-select.open .select-arrow[data-v-3ceed415]{transform:rotate(180deg)}.select-selected[data-v-3ceed415]{display:flex;align-items:center;gap:8px;padding:10px 14px;border:1px solid var(--border-light);border-radius:8px;background:var(--bg-secondary);color:var(--text-primary);font-size:14px;font-weight:500;transition:all .2s ease;height:40px;box-sizing:border-box}.select-selected[data-v-3ceed415]:hover{border-color:var(--primary-color);background:var(--bg-hover)}.select-icon[data-v-3ceed415]{font-size:18px;color:var(--text-secondary);flex-shrink:0}.select-arrow[data-v-3ceed415]{margin-left:auto;font-size:16px;color:var(--text-tertiary);transition:transform .2s ease;flex-shrink:0}.select-arrow.rotated[data-v-3ceed415]{transform:rotate(180deg)}.select-dropdown[data-v-3ceed415]{position:fixed;background:var(--bg-card);background-color:var(--bg-card);border:1px solid var(--border-light);border-radius:8px;box-shadow:0 4px 16px #00000026,0 2px 8px #0000001a;z-index:10001;overflow:hidden;max-height:300px;overflow-y:auto}.select-option[data-v-3ceed415]{display:flex;align-items:center;gap:10px;padding:12px 14px;cursor:pointer;transition:all .2s ease;color:var(--text-primary);font-size:14px}.select-option[data-v-3ceed415]:hover{background:var(--bg-secondary)}.select-option.active[data-v-3ceed415]{background:rgba(var(--primary-color-rgb),.1);color:var(--primary-color);font-weight:500}.select-option.active .option-icon[data-v-3ceed415]{color:var(--primary-color)}.select-option .option-icon[data-v-3ceed415]{font-size:18px;color:var(--text-secondary);flex-shrink:0}.select-option .option-check[data-v-3ceed415]{margin-left:auto;font-size:16px;color:var(--primary-color);flex-shrink:0}.dropdown-fade-enter-active[data-v-3ceed415],.dropdown-fade-leave-active[data-v-3ceed415]{transition:opacity .2s ease,transform .2s ease}.dropdown-fade-enter-from[data-v-3ceed415],.dropdown-fade-leave-to[data-v-3ceed415]{opacity:0;transform:translateY(-8px)}

View File

@@ -5,8 +5,9 @@
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>QiuChenly 应用商店</title>
<script type="module" crossorigin src="/assets/index-C9emEJxD.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-gCKjaa0F.css">
<script type="module" crossorigin src="/assets/BaSQ3xJt.js"></script>
<link rel="modulepreload" crossorigin href="/assets/CdD4XvnD.js">
<link rel="stylesheet" crossorigin href="/assets/DmWTyp24.css">
</head>
<body>
<div id="app"></div>

Binary file not shown.

View File

@@ -1,15 +1,17 @@
# 注入IPA
## 先决条件
1. brew需要安装unzip包
2. chmod +x ../tool/GenShineImpactStarter
# 注入 IPA
## iOS设备支持
## 先决条件
1. brew 需要安装 unzip 包
## iOS 设备支持
iOS 12.0+
支持自签与巨魔。
## 准备砸壳包
自行寻找。
## 自动注入
@@ -17,27 +19,29 @@ iOS 12.0+
使用原神,启动!二进制即可:
```bash
../tool/GenShineImpactStarter packipa "ipa文件路径" ./CoreInject.dylib
./InjectLib genshine packipa /Users/qiuchenly/Downloads/媒体三幻神/SenPlayer5.7.2.ipa ./CoreInject.dylib
```
最终在"ipa文件路径"处找到一个 "ipa文件路径"_inject.ipa 文件即为最终产物。
最终在"ipa 文件路径"处找到一个 "ipa 文件路径"\_inject.ipa 文件即为最终产物。
## 目前支持状态:
|模式|状态|说明|
|-|-|-|
|自签|支持|无法使用iCloud同步|
|巨魔|支持|暂未编译 但支持 可用iCloud同步 有需要自己到频道找|
|App|状态|说明|
|-|-|-|
|Infuse|支持 8.1.9 以及后续所有版本|自签无法使用iCloud同步 https://t.me/qiuchenlymac/738|
| 模式 | 状态 | 说明 |
| ---- | ---- | --------------------------------------------------- |
| 自签 | 支持 | 无法使用 iCloud 同步 |
| 巨魔 | 支持 | 暂未编译 但支持 可用 iCloud 同步 有需要自己到频道找 |
部分ipa提供了注入好的ipa包下载即可安装。请访问https://t.me/qiuchenlymac 查看更多细节。
| App | 状态 | 说明 |
| --------- | --------------------------- | ------------------------------------------------------ |
| Infuse | 支持 8.1.9 以及后续所有版本 | 自签无法使用 iCloud 同步 https://t.me/qiuchenlymac/738 |
| SenPlayer | 当前支持所有版本 | 自签无法使用 iCloud 同步 |
部分 ipa 提供了注入好的 ipa 包下载即可安装。请访问https://t.me/qiuchenlymac 查看更多细节。
## 开发者
QiuChenly
20250708
20250708
美国东部时间下午16:18分于洛杉矶
美国东部时间下午 16:18 分于洛杉矶

BIN
imgs/17635702720772.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

210
readme.md
View File

@@ -3,12 +3,6 @@
</a>
## 荣誉贡献榜
![GitHub contributors](https://img.shields.io/github/contributors/QiuChenly/InjectLib.svg?style=flat-square)
<a href="https://github.com/QiuChenly/InjectLib/graphs/contributors">
<img src="https://contrib.rocks/image?repo=QiuChenly/InjectLib" />
</a>
<br />
<br />
<p align="center">🔞全球各❤️地❤️服❤️务🔞<br/>
🔞①线至①⑧线城市齐全🔞<br/>
@@ -16,89 +10,169 @@
🔞 一❤️个❤️人独自在家❤️火❤️热❤️难❤️耐 玩🔞逆🔞向 🔞<br/>
🔞找Qiu❤Chen❤l❤y❤Open❤Source🔞<br/></p>
# 你这玩意我怎么使用?
![GitHub contributors](https://img.shields.io/github/contributors/QiuChenly/CoreInject.svg?style=flat-square)
下载仓库zip解压后终端cd到这个目录执行"python3 main.py" 选择你中意的程序注入即可。
<a href="https://github.com/QiuChenly/CoreInject/graphs/contributors">
<img src="https://contrib.rocks/image?repo=QiuChenly/CoreInject" />
</a>
仍然遇到问题?你有两种选择。
---
1. 加群询问获取帮助: **https://t.me/+VvqTr-2EFaZhYzA1**
# 隆重推出 QiuChenly AppStore 体验测试版
拒绝翻墙从我做起, QQ群也可反馈问题:
![QiuChenly AppStore](imgs/17635702720772.jpg)
一群 1042610918 (已满)
接下来向各位隆重宣布,来自 QiuChenly AppStore 的全新版本。
二群 1049674046
2. 继续向下阅读并访问文档说明使用:
我们封装了整个 App 和集成权限操作 Api现在用户只需要下载 App并简单配置一下即可使用来自 QiuChenly 带给你的无上伟力。
商店不仅仅适用于破解,也是交流、分享所在之处。
找不到资源?没有一手消息?来 QiuChenly 公益应用商店,你可以找到任何正版和第三方正版以及志同道合的朋友进行交流、心得分享。
对于原创破解发布人员,我们提供了管理员后台,您现在可以把打包好的预破解包上传到我们的中央服务器,每一个用户都可以在 App 详情中看到您发布的最新作品。
我宣布,盗版资源站实质上已经死了。
站在你们面前的是一个全新、自由、没有限制的社区、商店、工具集合。QiuChenly 凭借数年以来持续无偿贡献的海内外良好声誉网络资源与号召力绝非等闲资源站可堪一碰。QiuChenly 将在各位无私帮助的粉丝拥护下,继往开来,稳扎稳打,紧密围绕在 QiuChenly AppStore 周围,打造出一个令人叹为观止的庞大生态体系。
图标来自苹果应用商店UI 设计风格来自微软应用商店。苹果看了沉默,微软看了流泪。
耗时 12 天,前后端共计 8 万 6000 多行代码QiuChenly 为您从 0 开始构建出了整套自由软件世界的框架。
**QiuChenly致力于人人平等的自由软件生态构建。**
**JOIN USJOIN FREE。**
程序还在快速开发中, 请注意及时更新和关注。
所有注入库、脚本未来全部集成在App中更新,不再采取Git分发的方式。
QiuChenly 拥有不计其数的网友友情提供不限量的服务器资源、带宽,所以不必担心找不到我。
App 宣传视频: https://t.me/qiuchenlymac/1000
最新更新文件请访问最新消息获取: https://t.me/qiuchenlymac
---
## 致谢
感谢以下网友为 QiuChenly AppStore 提供的服务器资源支持:
- **部分台湾网友**:提供不限流量、无限空间的中央存储服务器
- **部分香港、中国大陆网友**:提供域名解析服务及服务器资源
- **部分中国大陆网友**:无偿自费购买并赠送给 QiuChenly 的域名服务
特别致谢QiuChenly AppStore 的云服务、文件存储服务以及高性能弹性存储所承载的主要流量,均来自于一家高端 IDC 机房老板无偿赠送的顶级高配高带宽机器。该 IDC 承诺能够为 QiuChenly 提供最大程度的国内流量加速和企业级优选加速服务。
IDC 主机官网: https://whmcs.as211392.com/index.php?rp=/login该 IDC 提供的机器主要面向高端客户,费用较高)
> **声明**: QiuChenly 与任何人均无任何形式的经济往来,所有致谢、介绍均出于感谢,没有任何带货、赚取广告费的目的。
---
# 旧版终端命令行使用方式
## 如何使用?
下载仓库 zip双击运行"秋城落叶_启动.command"。
## 遇到问题?
### 方式一:查看文档
点下面的链接进入在线文档,有操作指南。
目前还没有写完如果有没有的、看不懂的内容可以提Issues。
目前还没有写完,如果有没有的、看不懂的内容可以提 Issues。
点我查看➡️[使用文档](https://qiuchenlyopensource.github.io/Documentaions/)
**点我查看** ➡️ [使用文档](https://qiuchenlyopensource.github.io/Documentaions/)
# 加入小团体
### 方式二:获取帮助
- **Telegram 群组**: https://t.me/+VvqTr-2EFaZhYzA1
- **QQ 群**: 1049674046拒绝翻墙从我做起
---
# 加入社区
关注我的频道,进群获取最新的推送资讯。
頻道: **<https://t.me/qiuchenlymac>**
- **Telegram 频道**: https://t.me/qiuchenlymac
- **Telegram 群组**: https://t.me/+VvqTr-2EFaZhYzA1
- **Twitter**: https://twitter.com/QiuChenly
- **QQ 群**: 1049674046仅为方便国内用户反馈问题与交流加群者请在遵守当地法律法规的前提下进行交流
群組: **https://t.me/+VvqTr-2EFaZhYzA1**
关注 QiuChenly 喵,关注落葉的 Twitter 喵。謝謝大家喵。
Twitter: https://twitter.com/QiuChenly
---
QQ群:
仅为方便国内用户反馈问题与交流。
加群者请在遵守当地法律法规的前提下进行交流。
# 原神!
<br>
關注QiuChenly喵關注落葉的Twitter喵。謝謝大家喵。
# 原神!
點擊圖片進入新世界.
點擊圖片進入新世界。
[![启动](https://i2.hdslb.com/bfs/archive/966fe6fe2c1329919bb8972d69fb8c09d17047cc.jpg@100w_100h_1c.png)](imgs/bengbuzhule.mp4)
# 现已支持IPA注入
---
# 现已支持 IPA 注入
查看[文档](./iOSHijack/readme.md)了解更多细节。
# 操作系统要求 & 代码编译环境要求
---
# 系统要求
## 操作系统要求
- 最低运行 macOS High Sierra 10.13
- 编译SDK macOS 14.0
- 编译 SDK macOS 14.0
- 目标部署平台 macOS 10.13
- CMakeLists 环境变量
- set(CMAKE_OSX_DEPLOYMENT_TARGET "10.13")
- 检查二进制文件的最低macOS版本兼容性
- ```find . -name "*.*" | xargs otool -l | grep -E "(minos|sdk)"```
***
## 代码编译环境要求
### CMakeLists 环境变量
```cmake
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.13")
```
### 检查二进制文件的最低 macOS 版本兼容性
```bash
find . -name "*.*" | xargs otool -l | grep -E "(minos|sdk)"
```
---
# 项目存在的目的
本项目是Free的、开源的、基于互联网最原始的共享精神的、不接受任何打赏的、无所不包的、令人感叹的、无与伦比的、精妙绝伦的、化腐朽为神奇的、逆天的、养生的、抽象的、二次元的、OP的。
本项目是 Free 的、开源的、基于互联网最原始的共享精神的、不接受任何打赏的、无所不包的、令人感叹的、无与伦比的、精妙绝伦的、化腐朽为神奇的、逆天的、养生的、抽象的、二次元的、OP 的。
在2023年所有人都逐渐觉得打赏、付费才是理所应当的哪怕是某些人只做了一件从外网搬运到国内的工作也应该得到鼓励。
我不能说这种行为是完全错误的,只能说有些人恬不知耻见利忘义。哪怕是打赏也应该基于双方意愿的基础上,而不是用“打赏后才能下载”这种理由强奸用户的使用习惯,把用户变成必须付费的蠢驴,并辅以几十元的超低价注册会员费用钝刀割肉式的强奸用户。
2023 年,所有人都逐渐觉得打赏、付费才是理所应当的,哪怕是某些人只做了一件从外网搬运到国内的工作,也应该得到鼓励。我不能说这种行为是完全错误的,只能说有些人恬不知耻见利忘义。哪怕是打赏也应该基于双方意愿的基础上,而不是用"打赏后才能下载"这种理由强奸用户的使用习惯,把用户变成必须付费的蠢驴,并辅以几十元的超低价注册会员费用钝刀割肉式的强奸用户。
当然这种用户也确实是个蠢货。有这种钱你买正版得了别跟我说太贵你出去跟朋友吃一顿好点的饭200起步大部分好软件正版才不到100块钱。抽包烟软中煊赫门起步面对19.9年费会员时却面露难色,好像杀了你的🐎一样。相信我,你也并不是真的需要这些软件,只是人云亦云盲目从众罢了。
当然,这种用户也确实是个蠢货。有这种钱你买正版得了,别跟我说太贵,你出去跟朋友吃一顿好点的饭 200 起步,大部分好软件正版才不到 100 块钱。抽包烟软中煊赫门起步,面对 19.9 年费会员时却面露难色,好像杀了你的 🐎 一样。相信我,你也并不是真的需要这些软件,只是人云亦云盲目从众罢了。
我认为,共享精神不应该建立在物质上,我深刻的理解金钱对人的吸引和动力,但这种精神本身就超越了物质。
# ~~免责声明~~wo ze ni ma de b
---
# 免责声明
致来自中国大陆的各位学习研究爱好者:
根据大陆中华人民共和国《计算机软件保护条例》第十七条规定:"为了学习和研究软件内含的设计思想和原理,通过安装、显示、传输或者存储软件等方式使用软件的,可以不经软件著作权人许可,不向其支付报酬。"您需知晓本仓库所有内容资源均来源于网络,仅供用户交流学习与研究使用,版权归属原版权方所有,版权争议与本仓库本作者无关,用户本人下载后不能用作商业或非法用途,需在 24 小时之内删除,否则后果均由用户承担责任。
致来自中国大陆的各位学习研究爱好者:<br>
根据大陆中华人民共和国《计算机软件保护条例》第十七条规定“为了学习和研究软件内含的设计思想和原理通过安装、显示、传输或者存储软件等方式使用软件的可以不经软件著作权人许可不向其支付报酬。”您需知晓本仓库所有内容资源均来源于网络仅供用户交流学习与研究使用版权归属原版权方所有版权争议与本仓库本作者无关用户本人下载后不能用作商业或非法用途需在24小时之内删除否则后果均由用户承担责任。
如果你不删那就让这些喜欢发律师函的事务所一对一指导你。
我是來自北美的獨立IOS應用程式開發者,專注於開發有趣又富有創意的應用。對於法律問題,我只能說明技術原理,不能提供任何法律意見。希望大家都能以和平、理性的態度來探討各種課題。
我是來自北美的獨立 IOS 應用程式開發者專注於開發有趣又富有創意的應用。對於法律問題我只能說明技術原理不能提供任何法律意見。希望大家都能以和平、理性的態度來探討各種課題。
同時, 我也是二次元南桐. 从台灣國立大學毕业的那一天, 我的青春永遠留在了高雄.
同時我也是二次元南桐从台灣國立大學毕业的那一天我的青春永遠留在了高雄
對於肆意濫用法律的組織和個人,請將律師函發送至: 华盛顿特区第35大道林肯大街15号-501, John Albet收.
對於肆意濫用法律的組織和個人請將律師函發送至: 华盛顿特区第 35 大道林肯大街 15 号-501, John Albet 收。
---
## WHO IS QIUCHENLY?
@@ -108,36 +182,44 @@ QQ群:
原来我重生到了一个同名的人身上,在我醒来的时候,一道灵光从天灵盖中发出,脑海中听到了机械声音在提示我:已激活全球最强程序员系统。
我哈哈大笑 终于、终于...!
我哈哈大笑 终于、终于...!
秋城时代 沸腾期待!
上一世,同学弃我,老师欺我,女友绿我,这一世,我将亲手夺回属于我的一切!
是夜我梦到300年前我被连夜召入宫慈溪病榻临终之际给我下达任务誓要洗刷被八国联军掳掠的耻辱如今我孤身一人只能安静蛰伏静待良机。如今最强程序员系统在手虽千万人吾往矣百年之约已到我代表大清回来向列强复仇了
是夜,我梦到 300 年前我被连夜召入宫,慈溪病榻临终之际,给我下达任务,誓要洗刷被八国联军掳掠的耻辱,如今我孤身一人,只能安静蛰伏,静待良机。如今最强程序员系统在手,虽千万人吾往矣!百年之约已到,我代表大清,回来向列强复仇了!
我是重生之人大脑意外觉醒绑定了全球最强程序员系统所以破解App不在话下因为都是系统帮我的。 然后慈溪托遗让我夺回八国联军抢走属于大清的一切,洗刷历史耻辱,所以破的大多都是外国软件,目前居住在华盛顿州 户籍是香港华裔。
我是重生之人,大脑意外觉醒绑定了全球最强程序员系统,所以破解 App 不在话下,因为都是系统帮我的。然后慈溪托遗让我夺回八国联军抢走属于大清的一切,洗刷历史耻辱,所以破的大多都是外国软件,目前居住在华盛顿州 户籍是香港华裔。
---
# ~~停更~~
~~最近想追个19岁的小妹妹。<br>
项目基本上不会更新了,增加的新项目基本上是工作💻需要才做的。<br>
也不会去维护下面App的新版本了等我追到手🧑🤝🧑再说罢<br>~~
~~最近想追个 19 岁的小妹妹。~~
~~为了追💗妹妹👧MD跟米哈游原神铁道星穹崩坏王者荣耀蛋仔二次元拼了😡👊<br>
这下不得不成为农P/原P/穹P了🙏🙏<br>~~
~~项目基本上不会更新了,增加的新项目基本上是工作 💻 需要才做的。~~
~~无知时诋毁原神🫤🙏<br>
成熟时理解原神😭🙏<br>
恋爱时成为原神😋🙏<br>~~
~~也不会去维护下面 App 的新版本了,等我追到手 🧑‍🤝‍🧑 再说罢!~~
~~原神助我!喝唉!🖐大荒天陨!️<br>
任何邪恶!终将绳之以法👮!<br>~~
~~为了追 💗 妹妹 👧MD跟米哈游原神铁道星穹崩坏王者荣耀蛋仔二次元拼了 😡👊!~~
~~这下不得不成为农 P/原 P/穹 P 了 🙏🙏~~
~~无知时诋毁原神 🫤🙏~~
~~成熟时理解原神 😭🙏~~
~~恋爱时成为原神 😋🙏~~
~~原神助我!喝唉!🖐 大荒天陨!️~~
~~任何邪恶!终将绳之以法 👮!~~
~~原神,启动!~~
失败了,大家别问了。 <br>
她不是不喜欢玩游戏,她只是不想和不喜欢的人玩游戏。<br/>
失败了,大家别问了。
这段Repo不会删警钟长鸣。但是你要问我如果再给我一次机会还会不会选18岁妹妹我的回答是“yes i do.”
她不是不喜欢玩游戏,她只是不想和不喜欢的人玩游戏。
这段 Repo 不会删,警钟长鸣。但是你要问我如果再给我一次机会还会不会选 18 岁妹妹,我的回答是"yes i do."

View File

@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>InjectLib</string>
<key>CFBundleIdentifier</key>
<string>com.qiuchenly.hayaku.daemon</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>HayakuDaemon</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>3.0.0</string>
<key>CFBundleVersion</key>
<string>3.0.0</string>
<key>LSBackgroundOnly</key>
<true/>
<key>LSUIElement</key>
<true/>
<key>LSMinimumSystemVersion</key>
<string>10.15</string>
<key>NSHighResolutionCapable</key>
<true/>
<key>CFBundleDisplayName</key>
<string>Hayaku Daemon</string>
<key>CFBundleGetInfoString</key>
<string>Hayaku Background Service</string>
<key>CFBundleLongVersionString</key>
<string>Hayaku Daemon v3.0.0</string>
<key>CFBundleCopyright</key>
<string>Copyright © 2025 QiuChenly</string>
</dict>
</plist>

Binary file not shown.

View File

@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTDPLIST1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.qiuchenly.hayaku.daemon</string>
<key>ProgramArguments</key>
<array>
<string>/Library/Application Support/Hayaku/HayakuDaemon.app/Contents/MacOS/InjectLib</string>
<string>--daemon</string>
<string>/Library/Application Support/Hayaku/config.json</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>StandardOutPath</key>
<string>/var/log/hayaku_daemon.log</string>
<key>StandardErrorPath</key>
<string>/var/log/hayaku_daemon_error.log</string>
<key>UserName</key>
<string>root</string>
<key>GroupName</key>
<string>wheel</string>
<key>WorkingDirectory</key>
<string>/Library/Application Support/Hayaku</string>
<key>ProcessType</key>
<string>Background</string>
<key>ThrottleInterval</key>
<integer>1</integer>
</dict>
</plist>

File diff suppressed because it is too large Load Diff

51
res/Install.md Normal file
View File

@@ -0,0 +1,51 @@
## 安装说明
## 检查权限问题
安装完后一定要执行一次命令看是否提示你没有权限。
![](assets/17632032930473.jpg)
如果你发现你输出的error.log中出现了:
```bash
⚠️ 完全磁盘访问权限测试失败 - 复制备份失败: “1Capture” couldnt be copied because you dont have permission to access “MacOS”.
```
那就需要许可一下完全磁盘访问权限。
打开: 设置 - 隐私与安全性 - 完全磁盘访问权限, 可以看到:
![](assets/17632034297710.jpg)
HayakuDaemon.app 赫然位列其中,点击开关允许即可。
### 重要:
为确保生效, 请手动停止(不是卸载 卸载重新安装会重置权限)并启用, 在终端用sudo执行以下命令即可:
```bash
BUNDLE_ID="com.qiuchenly.hayaku.daemon"
PLIST_NAME="com.qiuchenly.hayaku.daemon.plist"
echo "重启服务..."
launchctl stop "$BUNDLE_ID" 2>/dev/null || true
launchctl unload "/Library/LaunchDaemons/$PLIST_NAME" 2>/dev/null || true
sleep 1
launchctl load "/Library/LaunchDaemons/$PLIST_NAME"
launchctl start "$BUNDLE_ID"
```
看到这个提示说明这把稳了:
![](assets/17632038106204.jpg)
```bash
找到测试应用: /Applications/1Capture.app/Contents/MacOS/1Capture
✓ 完全磁盘访问权限测试通过
```
## 注入App提示失败
![](assets/17632151291488.jpg)
我们已经完全支持了macOS的安全机制, 当失败时, 完全磁盘访问权限必然会显示"HayakuDaemon", 只需要打开后再次注入即可.
![](assets/17632152063531.jpg)
## 无法连接Daemon程序
用某些清理软件卸载后提示如下:
![](assets/17633019904584.jpg)
解决方案:
启动程序这里的HayakuDaemon被禁止掉了,打开即可。
![](assets/17633019379229.jpg)

Binary file not shown.

After

Width:  |  Height:  |  Size: 605 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 387 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 508 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 599 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 496 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 486 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 519 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 KiB

View File

@@ -1,31 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.qiuchenly.hayaku.daemon</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/bin/InjectLib</string>
<string>--daemon</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>StandardOutPath</key>
<string>/var/log/hayaku_daemon.log</string>
<key>StandardErrorPath</key>
<string>/var/log/hayaku_daemon_error.log</string>
<key>UserName</key>
<string>root</string>
<key>GroupName</key>
<string>wheel</string>
<key>WorkingDirectory</key>
<string>/usr/local/bin</string>
<key>ProcessType</key>
<string>Background</string>
<key>ThrottleInterval</key>
<integer>1</integer>
</dict>
</plist>

View File

@@ -1,53 +0,0 @@
#!/bin/bash
# Hayaku HTTP守护进程安装脚本
set -e
echo "安装Hayaku HTTP守护进程..."
# 检查是否以root权限运行
if [ "$EUID" -ne 0 ]; then
echo "请使用sudo运行此脚本"
exit 1
fi
# 获取脚本所在目录
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
BINARY_NAME="InjectLib"
PLIST_NAME="com.qiuchenly.hayaku.daemon.plist"
# 检查二进制文件是否存在(在脚本同级目录)
if [ ! -f "$SCRIPT_DIR/../InjectLib" ]; then
echo "错误: 找不到 $BINARY_NAME 文件"
echo "请先编译项目"
exit 1
fi
# 软连接tool和config.json到二进制同级目录
ln -fs "$SCRIPT_DIR/../tool" "/usr/local/bin/tool"
ln -fs "$SCRIPT_DIR/../config.json" "/usr/local/bin/config.json"
ln -fs "$SCRIPT_DIR/../frontend" "/usr/local/bin/frontend"
# 复制二进制文件到系统目录
echo "复制二进制文件到 /usr/local/bin/"
cp "$SCRIPT_DIR/../InjectLib" "/usr/local/bin/"
chmod +x "/usr/local/bin/$BINARY_NAME"
# 复制plist文件到LaunchDaemons目录
echo "安装LaunchDaemon配置..."
cp "$SCRIPT_DIR/$PLIST_NAME" "/Library/LaunchDaemons/"
chmod 644 "/Library/LaunchDaemons/$PLIST_NAME"
# 加载并启动服务
echo "启动服务..."
launchctl load "/Library/LaunchDaemons/$PLIST_NAME"
launchctl start "com.qiuchenly.hayaku.daemon"
echo "安装完成!"
echo "服务状态:"
launchctl list | grep com.qiuchenly.hayaku.daemon || echo "服务未运行"
echo ""
echo "访问 http://localhost:15200 查看应用商店"
echo "查看日志: tail -f /var/log/hayaku_daemon*"

View File

@@ -1,32 +0,0 @@
#!/bin/bash
# Hayaku HTTP守护进程卸载脚本
set -e
echo "卸载Hayaku HTTP守护进程..."
# 检查是否以root权限运行
if [ "$EUID" -ne 0 ]; then
echo "请使用sudo运行此脚本"
exit 1
fi
PLIST_NAME="com.qiuchenly.hayaku.daemon.plist"
# 停止并卸载服务
echo "停止服务..."
launchctl stop "com.qiuchenly.hayaku.daemon" 2>/dev/null || true
launchctl unload "/Library/LaunchDaemons/$PLIST_NAME" 2>/dev/null || true
# 删除文件
echo "删除文件..."
rm -f "/usr/local/bin/InjectLib"
rm -f "/usr/local/bin/tool"
rm -f "/usr/local/bin/config.json"
rm -f "/usr/local/bin/frontend"
rm -f "/Library/LaunchDaemons/$PLIST_NAME"
rm -f "/var/log/hayaku_daemon.log"
rm -f "/var/log/hayaku_daemon_error.log"
echo "卸载完成!"

View File

@@ -4,6 +4,8 @@
QiuChenly 应用商店是一个基于 HTTP 服务的 macOS 应用注入工具商店,以系统守护进程方式运行,提供 Web 界面管理和使用各种 macOS 应用注入脚本。
文档篇幅较长,很详细,善用搜索,记得看完。
## 预览
![main](imgs/浅色模式.png)
@@ -16,6 +18,12 @@ QiuChenly 应用商店是一个基于 HTTP 服务的 macOS 应用注入工具商
- 系统守护进程方式运行,开机自动启动
- Web 界面访问,方便直观
- 支持数百款 macOS 应用的注入和破解
- **支持临时运行**:无需安装系统服务,可直接命令行启动测试
**使用方式:**
- **临时运行**:适合测试,无需安装,命令行启动即可
- **安装服务**:推荐长期使用,开机自启,自动重启
---
@@ -42,7 +50,7 @@ QiuChenly 应用商店是一个基于 HTTP 服务的 macOS 应用注入工具商
在安装前,请确保项目目录包含以下关键文件:
```
InjectShell/
InjectLib/
├── InjectLib # 主程序(编译生成)
├── config.json # 配置文件
├── tool/ # 工具目录
@@ -55,7 +63,69 @@ InjectShell/
---
## 详细安装步骤
## 临时运行(无需安装)
如果您只是想测试或临时使用,可以不必安装系统服务,直接使用命令行临时启动:
### 如何临时运行
1. **打开终端**
- 按下快捷键 **Command ⌘ + 空格**
- 输入 **终端** 并打开
2. **进入项目目录**
```bash
cd /Volumes/MySSD/InjectLib
# 或者你的项目实际路径
```
3. **直接运行程序**
```bash
sudo ./InjectLib --daemon config.json
```
4. **保持终端窗口打开**
- 服务会在当前终端窗口中运行
- 不要关闭终端窗口
- 按 **Ctrl + C** 可以停止服务
5. **访问 Web 界面**
- 打开浏览器访问:`http://localhost:15200`
### 临时运行的优缺点
**优点:**
- 无需安装,快速测试
- 不修改系统配置
- 停止后不留任何痕迹
- 适合临时使用或测试
**缺点:**
- 关闭终端窗口后服务会停止
- 不会开机自动启动
- 进程异常退出后不会自动重启
- 需要手动管理进程
- 仍然需要配置系统权限(完全磁盘访问 + App管理
### 重要提示
**即使临时运行,也必须授予系统权限:**
1. **配置完全磁盘访问权限**
- 系统设置 → 隐私与安全性 → 完全磁盘访问权限
- 添加项目路径下的 `InjectLib` 程序
2. **配置 App 管理权限**
- 系统设置 → 隐私与安全性 → App管理
- 添加项目路径下的 `InjectLib` 程序
详细的权限配置方法请参考后面的"安装后配置"章节。
---
## 详细安装步骤(推荐:开机自启)
如果您希望服务开机自动启动、异常退出后自动重启,建议按照以下步骤安装系统服务。
### 步骤 1进入项目目录
@@ -87,8 +157,12 @@ sudo bash install_daemon.sh
安装过程会自动执行以下操作:
1. 创建软链接:`tool``config.json``frontend``/usr/local/bin/`
2. 复制二进制:`InjectLib``/usr/local/bin/InjectLib`
1. 创建安装目录:`/usr/local/bin/QiuChenly/`
2. 复制所有文件到系统目录:
- `tool/` → `/usr/local/bin/QiuChenly/tool/`
- `config.json` → `/usr/local/bin/QiuChenly/config.json`
- `frontend/` → `/usr/local/bin/QiuChenly/frontend/`
- `InjectLib` → `/usr/local/bin/QiuChenly/InjectLib`
3. 安装守护进程配置:`com.qiuchenly.hayaku.daemon.plist` → `/Library/LaunchDaemons/`
4. 加载并启动服务
@@ -96,7 +170,7 @@ sudo bash install_daemon.sh
```
安装Hayaku HTTP守护进程...
复制二进制文件到 /usr/local/bin/
复制二进制文件到 /usr/local/bin/QiuChenly/
安装LaunchDaemon配置...
启动服务...
安装完成!
@@ -109,108 +183,255 @@ xxxxx 0 com.qiuchenly.hayaku.daemon
---
## 重要警告 - 请务必仔细阅读
## 安装后配置(必须执行)
### 安装后绝对不能删除项目源文件夹!
### ⚠️ 授予程序必要的系统权限
**这是最重要的注意事项!**
**这是最重要的步骤!如果不授权,服务将无法正常工作!**
#### 为什么不能删除
#### 为什么需要这些权限
安装脚本使用了**软链接Symbolic Link**机制,而不是复制文件
QiuChenly 应用商店的主程序 `InjectLib` 需要以下权限才能正常运行
| 文件/目录 | 源路径 | 目标路径 | 链接方式 |
| ---------------- | ---------------------- | ---------------------------- | ---------- |
| tool 工具目录 | `项目目录/tool/` | `/usr/local/bin/tool` | **软链接** |
| config.json 配置 | `项目目录/config.json` | `/usr/local/bin/config.json` | **软链接** |
| frontend 前端 | `项目目录/frontend/` | `/usr/local/bin/frontend` | **软链接** |
| InjectLib 程序 | `项目目录/InjectLib` | `/usr/local/bin/InjectLib` | 复制 |
- **完全磁盘访问权限**:用于读取和修改应用程序包内的文件
- **App管理权限**:用于管理和注入目标应用程序
#### 什么是软链接?
#### 如何授予权限
软链接类似于 Windows 的"快捷方式",它只是一个指向原始文件的指针,**不是真实的文件拷贝**。
**注意:必须授权给安装后的程序路径 `/usr/local/bin/QiuChenly/InjectLib`**
```
系统中的软链接 → 指向 → 项目源文件夹中的实际文件
```
---
如果您删除了项目源文件夹:
**步骤 1配置完全磁盘访问权限**
- 软链接会失效(变成"死链接"
- 服务启动时找不到 `tool``config.json``frontend` 目录
- 应用商店无法正常工作
- 所有功能完全失效
##### 方法 A图形界面操作推荐
#### 示例说明
1. **打开系统设置**
- 点击屏幕左上角的 **苹果图标**
- 选择 **系统设置**(或 **系统偏好设置**,取决于 macOS 版本)
**正常情况:**
2. **进入隐私与安全性**
- 在左侧边栏中找到并点击 **隐私与安全性**
- 在右侧向下滚动,找到 **完全磁盘访问权限**
- 点击进入
3. **解锁设置**
- 点击左下角的 **锁图标**
- 输入您的 **管理员密码**
- 点击 **解锁**
4. **添加 InjectLib 程序**
- 点击应用列表下方的 **+** 按钮
- 在弹出的文件选择窗口中,按下键盘快捷键 **Command ⌘ + Shift ⇧ + G**
- 在弹出的"前往文件夹"输入框中输入:`/usr/local/bin/QiuChenly/`
- 点击 **前往**
- 选中 **InjectLib** 文件
- 点击 **打开**
5. **确认开关状态**
- 在应用列表中找到 **InjectLib**
- 确保它旁边的开关是 **✓ 蓝色/绿色开启状态**
- 如果是关闭状态,点击开关将其打开
6. **锁定设置**
- 点击左下角的 **锁图标** 锁定设置
##### 方法 B使用终端命令快速打开
如果您熟悉终端,可以直接执行以下命令打开对应设置页面:
```bash
/usr/local/bin/tool → InjectShell/tool/
# 软链接指向源文件夹,一切正常
open "x-apple.systempreferences:com.apple.preference.security?Privacy_AllFiles"
```
**删除源文件夹后:**
然后按照方法 A 的步骤 3-6 操作即可。
---
**步骤 2配置 App 管理权限**
##### 方法 A图形界面操作推荐
1. **打开系统设置**
- 点击屏幕左上角的 **苹果图标**
- 选择 **系统设置**(或 **系统偏好设置**
2. **进入隐私与安全性**
- 在左侧边栏中找到并点击 **隐私与安全性**
- 在右侧向下滚动,找到 **App管理**(或 **自动化**,取决于 macOS 版本)
- 点击进入
3. **解锁设置**
- 点击左下角的 **锁图标**
- 输入您的 **管理员密码**
- 点击 **解锁**
4. **添加 InjectLib 程序**
- 点击应用列表下方的 **+** 按钮
- 在弹出的文件选择窗口中,按下键盘快捷键 **Command ⌘ + Shift ⇧ + G**
- 在弹出的"前往文件夹"输入框中输入:`/usr/local/bin/QiuChenly/`
- 点击 **前往**
- 选中 **InjectLib** 文件
- 点击 **打开**
5. **确认开关状态**
- 在应用列表中找到 **InjectLib**
- 确保它旁边的开关是 **✓ 蓝色/绿色开启状态**
- 如果是关闭状态,点击开关将其打开
6. **锁定设置**
- 点击左下角的 **锁图标** 锁定设置
##### 方法 B使用终端命令快速打开
如果您熟悉终端,可以直接执行以下命令打开对应设置页面:
```bash
/usr/local/bin/tool → InjectShell/tool/
# 源文件夹已被删除,软链接失效
# 服务启动报错No such file or directory
open "x-apple.systempreferences:com.apple.preference.security?Privacy_AppManagement"
```
### 推荐做法
然后按照方法 A 的步骤 3-6 操作即可。
#### 1. 选择永久存放位置
---
在安装**之前**,将项目文件夹移动到一个不会被删除的位置,例如:
**步骤 3重启服务使权限生效**
配置完权限后,需要重启服务才能使权限生效。
##### 方法 A图形界面操作推荐
1. **打开"活动监视器"**
- 按下键盘快捷键 **Command ⌘ + 空格** 打开聚焦搜索
- 输入 **活动监视器**
- 按下 **回车键** 打开
2. **查找并结束 InjectLib 进程**
- 在右上角的搜索框中输入:**InjectLib**
- 找到 **InjectLib** 进程
- 双击该进程(或选中后点击上方的关闭按钮)
- 点击 **退出** 或 **强制退出**
3. **等待服务自动重启**
- LaunchDaemon 会自动重启服务(大约 1-2 秒)
- 您可以在活动监视器中看到 InjectLib 进程再次出现
##### 方法 B使用终端命令
如果您熟悉终端,可以执行以下命令:
```bash
# 推荐位置1用户主目录
~/InjectShell
# 推荐位置2当前位置如果您确定不会删除
/Volumes/MySSD/InjectShell/
sudo launchctl stop com.qiuchenly.hayaku.daemon
sudo launchctl start com.qiuchenly.hayaku.daemon
```
#### 2. 移动项目文件夹
#### 验证权限和服务
如果需要移动,请**先卸载,再移动,然后重新安装**
##### 方法 A图形界面验证推荐
**1. 检查服务是否运行**
- 打开 **活动监视器**Command ⌘ + 空格,输入"活动监视器"
- 在右上角搜索框输入:**InjectLib**
- 如果能看到 **InjectLib** 进程在运行,说明服务已启动
**2. 访问 Web 界面**
- 打开您的浏览器Safari、Chrome 等)
- 在地址栏输入:`http://localhost:15200`
- 如果能看到应用商店界面,说明服务正常运行
**3. 检查日志文件(可选)**
方法一:使用控制台应用
- 按下快捷键 **Command ⌘ + 空格**,输入 **控制台** 并打开
- 在左侧边栏选择 **系统日志**
- 在右上角搜索框输入:**hayaku_daemon**
- 查看日志中是否有 `Operation not permitted` 或 `Permission denied` 等权限错误
- 如果没有权限错误,说明配置成功
方法二:使用终端查看
```bash
# 查看错误日志
sudo tail -n 20 /var/log/hayaku_daemon_error.log
# 实时查看日志(按 Ctrl+C 停止)
sudo tail -f /var/log/hayaku_daemon_error.log
```
##### 方法 B使用终端验证
如果您熟悉终端,可以执行以下命令:
```bash
# 1. 卸载当前安装
cd /当前项目路径/res
sudo bash uninstall_daemon.sh
# 查看服务状态
sudo launchctl list | grep com.qiuchenly.hayaku.daemon
# 2. 移动项目到新位置
mv /当前项目路径 /新的永久路径
# 测试运行主程序
sudo /usr/local/bin/QiuChenly/InjectLib --daemon /usr/local/bin/QiuChenly/config.json
# 如果能正常启动说明权限配置正确,按 Ctrl+C 停止
# 3. 在新位置重新安装
cd /新的永久路径/res
sudo bash install_daemon.sh
# 检查日志是否有权限错误
tail -n 20 /var/log/hayaku_daemon_error.log
# 在浏览器中打开 Web 界面
open http://localhost:15200
```
### 如何验证软链接是否正常
##### 验证成功的标志
安装完成后,可以执行以下命令验证软链接
如果一切正常,您应该能够
- 在活动监视器中看到 InjectLib 进程运行
- 访问 `http://localhost:15200` 看到 Web 界面
- 日志文件中没有权限相关的错误
- 成功注入应用程序
#### 重要提示
- **必须授权给 `/usr/local/bin/QiuChenly/InjectLib`(安装后的路径)**
- **两个权限都必须配置**:完全磁盘访问 + App管理
- **每次更新程序后都需要重新授权**
- 授权后必须重启服务才能生效
- 权限授予后会永久生效,除非手动撤销
---
## 重要说明
### 安装后可以安全删除项目源文件夹
**安装采用文件复制方式**,所有必要的文件都会复制到 `/usr/local/bin/QiuChenly/` 目录中:
| 文件/目录 | 源路径 | 目标路径 | 方式 |
| ---------------- | ---------------------- | ----------------------------------------- | ------ |
| tool 工具目录 | `项目目录/tool/` | `/usr/local/bin/QiuChenly/tool/` | 复制 |
| config.json 配置 | `项目目录/config.json` | `/usr/local/bin/QiuChenly/config.json` | 复制 |
| frontend 前端 | `项目目录/frontend/` | `/usr/local/bin/QiuChenly/frontend/` | 复制 |
| InjectLib 程序 | `项目目录/InjectLib` | `/usr/local/bin/QiuChenly/InjectLib` | 复制 |
安装完成后,**服务完全独立运行**,不再依赖项目源文件夹。您可以:
- 安全地删除项目源文件夹
- 移动项目文件夹到其他位置
- 重命名项目文件夹
### 如何验证安装
安装完成后,可以执行以下命令验证文件:
```bash
ls -la /usr/local/bin/ | grep -E 'tool|config.json|frontend|InjectLib'
ls -la /usr/local/bin/QiuChenly/
```
正常输出应该类似
正常输出应该包含
```
lrwxr-xr-x tool -> /Volumes/data/macOS_HookWorkSpace/InjectShell/tool
lrwxr-xr-x config.json -> /Volumes/data/macOS_HookWorkSpace/InjectShell/config.json
lrwxr-xr-x frontend -> /Volumes/data/macOS_HookWorkSpace/InjectShell/frontend
drwxr-xr-x tool/
-rw-r--r-- config.json
drwxr-xr-x frontend/
-rwxr-xr-x InjectLib
```
**说明:**
- `l` 开头表示软链接link
- `->` 后面是指向的源文件路径
- `InjectLib` 是实际复制的文件,不是链接
所有文件都是实际复制的文件,不是符号链接
---
@@ -283,28 +504,47 @@ http://localhost:15200
### 查看运行日志
系统会自动记录服务的运行日志和错误日志
系统会自动记录服务的运行日志和错误日志
#### 方法 A使用控制台应用推荐
1. **打开控制台应用**
- 按下快捷键 **Command ⌘ + 空格**
- 输入 **控制台** 并打开
2. **查看系统日志**
- 在左侧边栏选择 **系统日志**
- 在右上角搜索框输入:**hayaku_daemon**
- 可以实时查看日志更新
3. **筛选日志**
- 点击搜索框旁边的筛选按钮
- 可以按照时间、级别(错误、警告、信息)筛选
#### 方法 B使用终端命令
```bash
# 实时查看标准输出日志
tail -f /var/log/hayaku_daemon.log
sudo tail -f /var/log/hayaku_daemon.log
# 实时查看错误日志
tail -f /var/log/hayaku_daemon_error.log
sudo tail -f /var/log/hayaku_daemon_error.log
# 查看最近100行日志
tail -n 100 /var/log/hayaku_daemon.log
sudo tail -n 100 /var/log/hayaku_daemon.log
# 查看所有日志
cat /var/log/hayaku_daemon.log
sudo cat /var/log/hayaku_daemon.log
```
**注意:** 查看 `/var/log/` 目录下的文件需要使用 `sudo` 管理员权限。
### 日志文件说明
| 日志文件 | 路径 | 内容 |
| -------- | ---------------------------------- | ---------------------- |
| 标准输出 | `/var/log/hayaku_daemon.log` | 正常运行信息、调试信息 |
| 错误输出 | `/var/log/hayaku_daemon_error.log` | 错误信息、异常堆栈 |
| 日志文件 | 路径 | 内容 | 查看方式 |
| -------- | ---------------------------------- | ---------------------- | --------------------- |
| 标准输出 | `/var/log/hayaku_daemon.log` | 正常运行信息、调试信息 | 控制台应用或终端+sudo |
| 错误输出 | `/var/log/hayaku_daemon_error.log` | 错误信息、异常堆栈 | 控制台应用或终端+sudo |
---
@@ -398,17 +638,24 @@ sudo launchctl list | grep com.qiuchenly.hayaku.daemon
# 应该没有任何输出
# 检查系统文件是否已删除
ls -la /usr/local/bin/ | grep -E 'InjectLib|tool|config.json|frontend'
ls -la /usr/local/bin/QiuChenly/
# 应该提示目录不存在
# 或者检查整个安装路径
ls -d /usr/local/bin/QiuChenly 2>/dev/null
# 应该没有任何输出
```
### 卸载后可以做什么
### 卸载说明
卸载完成后,项目源文件夹**不会被删除**,您可以
卸载操作会
- 安全地删除项目源文件
- 移动项目到其他位置后重新安装
- 保留项目供以后使用
- 删除 `/usr/local/bin/QiuChenly/` 目录及所有文件
- 删除 LaunchDaemon 配置文件
- 删除日志文件
- 项目源文件夹**不会被删除**(如果您还保留着的话)
卸载完成后,项目源文件夹可以保留供以后重新安装使用
---
@@ -422,7 +669,120 @@ ls -la /usr/local/bin/ | grep -E 'InjectLib|tool|config.json|frontend'
## 故障排查
### 问题 1执行安装脚本提示权限不足
### 问题 1服务无法正常工作或注入失败
**症状:**
- 服务启动了,但无法注入应用
- 日志中显示权限错误
- 提示"Operation not permitted"
- 无法访问应用程序包
**原因:** 主程序 `InjectLib` 没有必要的系统权限
**解决方案:**
#### 步骤 1检查当前权限状态
```bash
# 检查服务状态
sudo launchctl list | grep com.qiuchenly.hayaku.daemon
# 查看错误日志
tail -n 50 /var/log/hayaku_daemon_error.log
```
如果日志中出现以下关键词,说明是权限问题:
- `Operation not permitted`
- `Permission denied`
- `sandbox`
- `TCC`
#### 步骤 2为安装后的程序授权
**重要:安装后程序位于 `/usr/local/bin/QiuChenly/InjectLib`,需要给这个位置的程序授权!**
**A. 配置完全磁盘访问权限**
图形界面操作:
1. 点击屏幕左上角的 **苹果图标** → **系统设置**
2. 点击 **隐私与安全性** → **完全磁盘访问权限**
3. 点击左下角 **🔒** 输入密码解锁
4. 点击 **+** 按钮
5. 按 **Command ⌘ + Shift ⇧ + G**,输入:`/usr/local/bin/QiuChenly/`
6. 选择 **InjectLib** 并添加
7. 确保开关是 **✓ 开启状态**
终端快捷方式(可选):
```bash
# 打开系统设置(然后按照上面的步骤操作)
open "x-apple.systempreferences:com.apple.preference.security?Privacy_AllFiles"
```
**B. 配置 App 管理权限**
图形界面操作:
1. 点击屏幕左上角的 **苹果图标** → **系统设置**
2. 点击 **隐私与安全性** → **App管理**
3. 点击左下角 **🔒** 输入密码解锁
4. 点击 **+** 按钮
5. 按 **Command ⌘ + Shift ⇧ + G**,输入:`/usr/local/bin/QiuChenly/`
6. 选择 **InjectLib** 并添加
7. 确保开关是 **✓ 开启状态**
终端快捷方式(可选):
```bash
# 打开系统设置(然后按照上面的步骤操作)
open "x-apple.systempreferences:com.apple.preference.security?Privacy_AppManagement"
```
#### 步骤 3重启服务
图形界面操作:
1. 打开 **活动监视器**Command ⌘ + 空格,输入"活动监视器"
2. 搜索 **InjectLib**
3. 选中进程,点击 **退出**
4. 等待服务自动重启1-2秒
终端命令(可选):
```bash
sudo launchctl stop com.qiuchenly.hayaku.daemon
sudo launchctl start com.qiuchenly.hayaku.daemon
```
#### 步骤 4验证权限
图形界面验证:
1. 打开浏览器访问 `http://localhost:15200`
2. 在活动监视器中确认 InjectLib 进程运行正常
3. 打开"控制台"应用Command ⌘ + 空格,输入"控制台"),搜索 **hayaku_daemon** 检查是否有权限错误
终端验证(可选):
```bash
# 测试运行
sudo /usr/local/bin/QiuChenly/InjectLib --daemon /usr/local/bin/QiuChenly/config.json
# 按 Ctrl+C 停止
# 如果能正常启动且没有权限错误,说明配置成功
```
#### 常见权限错误示例
```
# 错误1没有完全磁盘访问权限
Error: Failed to access application bundle
Operation not permitted
# 错误2没有 App 管理权限
Error: Failed to inject into process
sandbox: denied(1) process-exec
# 解决方法:按照上面的步骤授权即可
```
---
### 问题 2执行安装脚本提示权限不足
**错误信息:**
@@ -439,7 +799,7 @@ sudo bash install_daemon.sh
---
### 问题 2:找不到 InjectLib 文件
### 问题 3:找不到 InjectLib 文件
**错误信息:**
@@ -459,7 +819,7 @@ sudo bash install_daemon.sh
---
### 问题 3:服务无法启动
### 问题 4:服务无法启动
**检查方法:**
@@ -482,26 +842,24 @@ lsof -i :15200
# 如果是其他程序,关闭该程序或修改配置使用其他端口
```
#### 原因 2软链接失效(删除了源文件夹)
#### 原因 2文件缺失或损坏
```bash
# 检查软链接状态
ls -la /usr/local/bin/tool
ls -la /usr/local/bin/config.json
ls -la /usr/local/bin/frontend
# 检查安装文件是否完整
ls -la /usr/local/bin/QiuChenly/
# 如果显示红色或"No such file or directory",说明源文件夹被删除了
# 解决方案:恢复源文件夹,或重新安装
# 应该包含InjectLib, tool/, config.json, frontend/
# 如果文件缺失,需要重新安装
```
#### 原因 3权限问题
```bash
# 检查文件权限
ls -la /usr/local/bin/InjectLib
ls -la /usr/local/bin/QiuChenly/InjectLib
# 确保InjectLib有执行权限
sudo chmod +x /usr/local/bin/InjectLib
sudo chmod +x /usr/local/bin/QiuChenly/InjectLib
# 重启服务
sudo launchctl stop com.qiuchenly.hayaku.daemon
@@ -510,7 +868,7 @@ sudo launchctl start com.qiuchenly.hayaku.daemon
---
### 问题 4:无法访问 http://localhost:15200
### 问题 5:无法访问 http://localhost:15200
**检查清单:**
@@ -543,50 +901,44 @@ lsof -i :15200
---
### 问题 5意外删除了项目源文件夹
### 问题 6修改配置文件或工具后不生效
**症状:**
- 服务无法启动
- 访问 Web 界面显示 404 或无法连接
- 日志中显示"No such file or directory"
- 修改了项目源文件夹中的 `config.json` 或 `tool/` 目录,但服务没有变化
**原因:**
安装时使用的是文件复制,服务运行的是 `/usr/local/bin/QiuChenly/` 中的文件,不是源文件夹中的文件。
**解决方案:**
#### 方案 1从备份恢复
如果您有备份,恢复到原位置即可
#### 方案 2重新下载项目
修改配置后需要重新安装:
```bash
# 1. 先卸载残留的配置(如果卸载脚本也丢失,手动清理)
sudo launchctl unload /Library/LaunchDaemons/com.qiuchenly.hayaku.daemon.plist
sudo rm -f /Library/LaunchDaemons/com.qiuchenly.hayaku.daemon.plist
sudo rm -f /usr/local/bin/InjectLib
sudo rm -f /usr/local/bin/tool
sudo rm -f /usr/local/bin/config.json
sudo rm -f /usr/local/bin/frontend
# 1. 停止服务
sudo launchctl stop com.qiuchenly.hayaku.daemon
# 2. 重新克隆或下载项目
cd /path/to/install
git clone <项目地址>
# 或解压下载的zip文件
# 2. 手动更新文件
sudo cp /path/to/InjectShell/config.json /usr/local/bin/QiuChenly/config.json
# 或者更新工具目录
sudo cp -r /path/to/InjectShell/tool /usr/local/bin/QiuChenly/
# 3. 编译项目
cd InjectShell
mkdir build && cd build
cmake ..
make
# 3. 启动服务
sudo launchctl start com.qiuchenly.hayaku.daemon
```
# 4. 重新安装
cd ../res
或者完全重新安装
```bash
cd /path/to/InjectShell/res
sudo bash uninstall_daemon.sh
sudo bash install_daemon.sh
```
---
### 问题 6:卸载后仍能访问 Web 界面
### 问题 7:卸载后仍能访问 Web 界面
**原因:** 服务可能还在运行,或浏览器缓存
@@ -605,14 +957,14 @@ sudo kill -9 <PID>
---
### 问题 7:更新项目后功能异常
### 问题 8:更新项目后功能异常
**原因:** 服务仍在使用旧的二进制文件
**原因:** 服务使用的是安装时复制的文件,不会自动更新
**解决方案:**
```bash
# 1. 重新编译项目
# 1. 重新编译项目(如果修改了源代码)
cd /path/to/InjectShell/build
make clean
cmake ..
@@ -621,15 +973,26 @@ make
# 2. 停止服务
sudo launchctl stop com.qiuchenly.hayaku.daemon
# 3. 更新系统中的二进制文件
sudo cp ../InjectLib /usr/local/bin/InjectLib
sudo chmod +x /usr/local/bin/InjectLib
# 3. 更新系统中的文件
sudo cp /path/to/InjectShell/InjectLib /usr/local/bin/QiuChenly/InjectLib
sudo chmod +x /usr/local/bin/QiuChenly/InjectLib
# 如果修改了配置或工具,也需要更新
sudo cp /path/to/InjectShell/config.json /usr/local/bin/QiuChenly/config.json
sudo cp -r /path/to/InjectShell/tool /usr/local/bin/QiuChenly/
sudo cp -r /path/to/InjectShell/frontend /usr/local/bin/QiuChenly/
# 4. 启动服务
sudo launchctl start com.qiuchenly.hayaku.daemon
```
**注意:** `tool``config.json``frontend` 是软链接,修改源文件会自动生效,无需手动更新
**或者更简单的方法:重新安装**
```bash
cd /path/to/InjectShell/res
sudo bash uninstall_daemon.sh
sudo bash install_daemon.sh
```
---
@@ -653,17 +1016,28 @@ sudo launchctl start com.qiuchenly.hayaku.daemon
### 当前版本功能
- LaunchDaemon 守护进程安装
- 软链接管理tool、config.json、frontend
- 文件复制安装(所有文件独立运行
- 安装后可安全删除源文件夹
- 系统级服务运行
- Web 界面访问(端口 15200
- 自动启动和崩溃重启
- 日志记录功能
- 完整的卸载功能
### 最近更新v2.0
**安装机制重大变更:**
- 从软链接改为文件复制方式
- 统一安装目录到 `/usr/local/bin/QiuChenly/`
- 安装后完全独立运行,不再依赖源文件夹
- 简化了部署和维护流程
### 待开发功能
- 一键重新安装功能
- 配置文件热更新
- 在线更新功能
---
@@ -685,8 +1059,9 @@ sudo launchctl start com.qiuchenly.hayaku.daemon
---
**文档版本:** v1.0
**最后更新:** 2025-10-14
**适用项目版本:** InjectShell 4.0
**文档版本:** v2.0
**最后更新:** 2025-10-14
**适用项目版本:** InjectShell 4.0+
**主要变更:** 安装机制从软链接改为文件复制
---

Binary file not shown.

View File

@@ -1,16 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.smartcard</key>
<true/>
<key>com.apple.security.network.server</key>
<true/>
<key>com.apple.security.hypervisor</key>
<true/>
<key>com.apple.security.network.client</key>
<true/>
<key>com.apple.security.device.usb</key>
<true/>
</dict>
</plist>

View File

@@ -1,33 +0,0 @@
#!/bin/bash
set -e
echo "[*] 开始清理 Parallels Desktop..."
if pgrep -x "prl_client_app" &>/dev/null; then
pkill -f "prl_client_app" 2>/dev/null || true
fi
if pgrep -x "prl_disp_service" &>/dev/null; then
pkill -f "prl_disp_service" 2>/dev/null || true
if [ -f "/Library/LaunchDaemons/com.parallels.desktop.launchdaemon.plist" ]; then
launchctl stop /Library/LaunchDaemons/com.parallels.desktop.launchdaemon.plist 2>/dev/null || true
fi
echo "[*] 清理 /var/run 目录下的 prl_ 文件..."
if ls /var/run/prl_* 1>/dev/null 2>&1; then
rm -f /var/run/prl_*
fi
fi
echo "[*] Parallels Desktop 主程序已停止"
license_file="/Library/Preferences/Parallels/licenses.json"
if [ -f "$license_file" ]; then
echo "[*] 清理许可证文件..."
chflags -R 0 "$license_file" 2>/dev/null || true
rm -f "$license_file"
echo "[*] 许可证文件已删除"
fi
exit 0