Compare commits
1 Commits
30b6e3144b
...
53c78e8e3c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
53c78e8e3c |
@@ -10,7 +10,7 @@ ENV TZ=Asia/Shanghai
|
||||
|
||||
# 安装 wkhtmltopdf(包含 wkhtmltoimage)与中文字体
|
||||
RUN apt-get update && \
|
||||
apt-get install -y --no-install-recommends wkhtmltopdf fonts-noto-cjk && \
|
||||
apt-get install -y --no-install-recommends wkhtmltopdf curl fonts-noto-cjk && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# 配置 pip 使用国内镜像源
|
||||
|
||||
136
README.md
136
README.md
@@ -1,49 +1,31 @@
|
||||
# 知识管理平台自动化工具 - Docker部署版
|
||||
|
||||
这是一个基于 Docker 的知识管理平台自动化工具,支持多用户、定时任务、代理IP、VIP管理、金山文档集成等功能。
|
||||
这是一个基于 Docker 的知识管理平台自动化工具,支持多用户、定时任务、代理IP、VIP管理等功能。
|
||||
|
||||
---
|
||||
|
||||
## 项目简介
|
||||
|
||||
本项目是一个 **Docker 容器化应用**,使用 Flask + Vue 3 + Requests + wkhtmltoimage + SQLite 构建,提供:
|
||||
本项目是一个 **Docker 容器化应用**,使用 Flask + Requests + wkhtmltopdf + SQLite 构建,提供:
|
||||
|
||||
### 核心功能
|
||||
- 多用户注册登录系统(支持邮箱绑定与验证)
|
||||
- 自动化浏览任务(纯 HTTP API 模拟,速度快)
|
||||
- 智能截图系统(wkhtmltoimage,支持线程池)
|
||||
- 用户自定义定时任务(支持随机延迟)
|
||||
- VIP 用户管理(账号数量限制、优先队列)
|
||||
|
||||
### 集成功能
|
||||
- **金山文档集成** - 自动上传截图到在线表格,支持姓名搜索匹配
|
||||
- **邮件通知** - 任务完成通知、密码重置、邮箱验证
|
||||
- **代理IP支持** - 动态代理API集成
|
||||
|
||||
### 安全功能
|
||||
- 威胁检测引擎(JNDI/SQL注入/XSS/命令注入检测)
|
||||
- IP/用户风险评分系统
|
||||
- 自动黑名单机制
|
||||
- 登录设备指纹追踪
|
||||
|
||||
### 管理功能
|
||||
- 现代化 Vue 3 SPA 后台管理界面
|
||||
- 公告系统(支持图片)
|
||||
- Bug 反馈系统
|
||||
- 任务日志与统计
|
||||
- 多用户注册登录系统
|
||||
- 自动化任务(HTTP 模拟)
|
||||
- 定时任务调度
|
||||
- 截图管理
|
||||
- VIP用户管理
|
||||
- 代理IP支持
|
||||
- 后台管理系统
|
||||
|
||||
---
|
||||
|
||||
## 技术栈
|
||||
|
||||
- **后端**: Python 3.11+, Flask, Flask-SocketIO
|
||||
- **前端**: Vue 3 + Vite + Element Plus (SPA)
|
||||
- **数据库**: SQLite + 连接池
|
||||
- **自动化**: Requests + BeautifulSoup (浏览)
|
||||
- **截图**: wkhtmltoimage
|
||||
- **金山文档**: Playwright (表格操作/上传)
|
||||
- **后端**: Python 3.8+, Flask
|
||||
- **数据库**: SQLite
|
||||
- **自动化**: Requests + BeautifulSoup
|
||||
- **截图**: wkhtmltopdf / wkhtmltoimage
|
||||
- **容器化**: Docker + Docker Compose
|
||||
- **实时通信**: Socket.IO (WebSocket)
|
||||
- **前端**: HTML + JavaScript + Socket.IO
|
||||
|
||||
---
|
||||
|
||||
@@ -53,46 +35,30 @@
|
||||
zsglpt/
|
||||
├── app.py # 启动/装配入口
|
||||
├── routes/ # 路由层(Blueprint)
|
||||
│ ├── api_*.py # API 路由
|
||||
│ ├── admin_api/ # 管理后台 API
|
||||
│ └── pages.py # 页面路由
|
||||
├── services/ # 业务服务层
|
||||
│ ├── tasks.py # 任务调度器
|
||||
│ ├── screenshots.py # 截图服务
|
||||
│ ├── kdocs_uploader.py # 金山文档上传服务
|
||||
│ └── schedule_*.py # 定时任务相关
|
||||
├── security/ # 安全防护模块
|
||||
│ ├── threat_detector.py # 威胁检测引擎
|
||||
│ ├── risk_scorer.py # 风险评分
|
||||
│ ├── blacklist.py # 黑名单管理
|
||||
│ └── middleware.py # 安全中间件
|
||||
├── realtime/ # SocketIO 事件与推送
|
||||
├── database.py # 数据库稳定门面(对外 API)
|
||||
├── db/ # DB 分域实现 + schema/migrations
|
||||
├── db_pool.py # 数据库连接池
|
||||
├── api_browser.py # Requests 自动化(主浏览流程)
|
||||
├── browser_pool_worker.py # wkhtmltoimage 截图线程池
|
||||
├── browser_pool_worker.py # 截图 WorkerPool
|
||||
├── app_config.py # 配置管理
|
||||
├── app_logger.py # 日志系统
|
||||
├── app_security.py # 安全工具函数
|
||||
├── password_utils.py # 密码哈希工具
|
||||
├── app_security.py # 安全模块
|
||||
├── password_utils.py # 密码工具
|
||||
├── crypto_utils.py # 加解密工具
|
||||
├── email_service.py # 邮件服务(SMTP)
|
||||
├── email_service.py # 邮件服务
|
||||
├── requirements.txt # Python依赖
|
||||
├── requirements-dev.txt # 开发依赖(不进生产镜像)
|
||||
├── pyproject.toml # ruff/pytest 配置
|
||||
├── pyproject.toml # ruff/black/pytest 配置
|
||||
├── Dockerfile # Docker镜像构建文件
|
||||
├── docker-compose.yml # Docker编排文件
|
||||
├── templates/ # HTML模板(SPA 入口)
|
||||
│ ├── app.html # 用户端 SPA 入口
|
||||
│ ├── admin.html # 管理端 SPA 入口
|
||||
│ └── email/ # 邮件模板
|
||||
├── app-frontend/ # 用户端 Vue 源码
|
||||
├── admin-frontend/ # 管理端 Vue 源码
|
||||
├── static/ # 前端构建产物
|
||||
│ ├── app/ # 用户端 SPA 资源
|
||||
│ └── admin/ # 管理端 SPA 资源
|
||||
└── tests/ # 测试用例
|
||||
├── templates/ # HTML模板(含 SPA fallback)
|
||||
├── app-frontend/ # 用户端前端源码(可选保留)
|
||||
├── admin-frontend/ # 后台前端源码(可选保留)
|
||||
└── static/ # 前端构建产物(运行时使用)
|
||||
├── app/ # 用户端 SPA
|
||||
└── admin/ # 后台 SPA
|
||||
```
|
||||
|
||||
---
|
||||
@@ -711,9 +677,9 @@ docker logs knowledge-automation-multiuser | grep "数据库"
|
||||
|
||||
---
|
||||
|
||||
**文档版本**: v2.0
|
||||
**更新日期**: 2026-01-08
|
||||
**适用版本**: Docker多用户版 + Vue SPA
|
||||
**文档版本**: v1.0
|
||||
**更新日期**: 2025-10-29
|
||||
**适用版本**: Docker多用户版
|
||||
|
||||
---
|
||||
|
||||
@@ -744,49 +710,3 @@ docker logs -f knowledge-automation-multiuser
|
||||
```
|
||||
|
||||
完成!🎉
|
||||
|
||||
---
|
||||
|
||||
## 更新日志
|
||||
|
||||
### v2.0 (2026-01-08)
|
||||
|
||||
#### 新功能
|
||||
- **金山文档集成**: 自动上传截图到金山文档表格
|
||||
- 支持姓名搜索匹配单元格
|
||||
- 支持配置有效行范围
|
||||
- 支持覆盖已有图片
|
||||
- 离线状态监控与邮件通知
|
||||
- **Vue 3 SPA 前端**: 用户端和管理端全面升级为现代化单页应用
|
||||
- Element Plus UI 组件库
|
||||
- 实时任务状态更新
|
||||
- 响应式设计
|
||||
- **用户自定义定时任务**: 用户可创建自己的定时任务
|
||||
- 支持多时间段配置
|
||||
- 支持随机延迟
|
||||
- 支持选择指定账号
|
||||
- **安全防护系统**:
|
||||
- 威胁检测引擎(JNDI/SQL注入/XSS/命令注入)
|
||||
- IP/用户风险评分
|
||||
- 自动黑名单机制
|
||||
- **邮件通知系统**:
|
||||
- 任务完成通知
|
||||
- 密码重置邮件
|
||||
- 邮箱验证
|
||||
- **公告系统**: 支持图片的系统公告
|
||||
- **Bug反馈系统**: 用户可提交问题反馈
|
||||
|
||||
#### 优化
|
||||
- **截图线程池**: wkhtmltoimage 截图支持多线程并发
|
||||
- 线程池管理,按需启动
|
||||
- 空闲自动释放资源
|
||||
- **二次登录机制**: 刷新"上次登录时间"显示
|
||||
- **API 预热**: 启动时预热连接,减少首次请求延迟
|
||||
- **数据库连接池**: 提高并发性能
|
||||
|
||||
### v1.0 (2025-10-29)
|
||||
- 初始版本
|
||||
- 多用户系统
|
||||
- 基础自动化任务
|
||||
- 定时任务调度
|
||||
- 代理IP支持
|
||||
|
||||
17
admin-frontend/src/api/passwordResets.js
Normal file
17
admin-frontend/src/api/passwordResets.js
Normal file
@@ -0,0 +1,17 @@
|
||||
import { api } from './client'
|
||||
|
||||
export async function fetchPasswordResets() {
|
||||
const { data } = await api.get('/password_resets')
|
||||
return data
|
||||
}
|
||||
|
||||
export async function approvePasswordReset(requestId) {
|
||||
const { data } = await api.post(`/password_resets/${requestId}/approve`)
|
||||
return data
|
||||
}
|
||||
|
||||
export async function rejectPasswordReset(requestId) {
|
||||
const { data } = await api.post(`/password_resets/${requestId}/reject`)
|
||||
return data
|
||||
}
|
||||
|
||||
26
admin-frontend/src/api/update.js
Normal file
26
admin-frontend/src/api/update.js
Normal file
@@ -0,0 +1,26 @@
|
||||
import { api } from './client'
|
||||
|
||||
export async function fetchUpdateStatus() {
|
||||
const { data } = await api.get('/update/status')
|
||||
return data
|
||||
}
|
||||
|
||||
export async function fetchUpdateResult() {
|
||||
const { data } = await api.get('/update/result')
|
||||
return data
|
||||
}
|
||||
|
||||
export async function fetchUpdateLog(params = {}) {
|
||||
const { data } = await api.get('/update/log', { params })
|
||||
return data
|
||||
}
|
||||
|
||||
export async function requestUpdateCheck() {
|
||||
const { data } = await api.post('/update/check', {})
|
||||
return data
|
||||
}
|
||||
|
||||
export async function requestUpdateRun(payload = {}) {
|
||||
const { data } = await api.post('/update/run', payload)
|
||||
return data
|
||||
}
|
||||
@@ -38,8 +38,6 @@ const kdocsSheetName = ref('')
|
||||
const kdocsSheetIndex = ref(0)
|
||||
const kdocsUnitColumn = ref('A')
|
||||
const kdocsImageColumn = ref('D')
|
||||
const kdocsRowStart = ref(0)
|
||||
const kdocsRowEnd = ref(0)
|
||||
const kdocsAdminNotifyEnabled = ref(false)
|
||||
const kdocsAdminNotifyEmail = ref('')
|
||||
const kdocsStatus = ref({})
|
||||
@@ -134,8 +132,6 @@ async function loadAll() {
|
||||
kdocsSheetIndex.value = system.kdocs_sheet_index ?? 0
|
||||
kdocsUnitColumn.value = (system.kdocs_unit_column || 'A').toUpperCase()
|
||||
kdocsImageColumn.value = (system.kdocs_image_column || 'D').toUpperCase()
|
||||
kdocsRowStart.value = system.kdocs_row_start ?? 0
|
||||
kdocsRowEnd.value = system.kdocs_row_end ?? 0
|
||||
kdocsAdminNotifyEnabled.value = (system.kdocs_admin_notify_enabled ?? 0) === 1
|
||||
kdocsAdminNotifyEmail.value = system.kdocs_admin_notify_email || ''
|
||||
kdocsStatus.value = kdocsInfo || {}
|
||||
@@ -257,8 +253,6 @@ async function saveKdocsConfig() {
|
||||
kdocs_sheet_index: Number(kdocsSheetIndex.value) || 0,
|
||||
kdocs_unit_column: kdocsUnitColumn.value.trim().toUpperCase(),
|
||||
kdocs_image_column: kdocsImageColumn.value.trim().toUpperCase(),
|
||||
kdocs_row_start: Number(kdocsRowStart.value) || 0,
|
||||
kdocs_row_end: Number(kdocsRowEnd.value) || 0,
|
||||
kdocs_admin_notify_enabled: kdocsAdminNotifyEnabled.value ? 1 : 0,
|
||||
kdocs_admin_notify_email: kdocsAdminNotifyEmail.value.trim(),
|
||||
}
|
||||
@@ -571,15 +565,6 @@ onMounted(loadAll)
|
||||
<el-input v-model="kdocsImageColumn" placeholder="D" style="max-width: 120px" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="有效行范围">
|
||||
<div style="display: flex; align-items: center; gap: 8px;">
|
||||
<el-input-number v-model="kdocsRowStart" :min="0" :max="10000" placeholder="起始行" style="width: 120px" />
|
||||
<span>至</span>
|
||||
<el-input-number v-model="kdocsRowEnd" :min="0" :max="10000" placeholder="结束行" style="width: 120px" />
|
||||
</div>
|
||||
<div class="help">限制上传的行范围(如 50-100),0 表示不限制。用于防止重名导致误传到其他县区。</div>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="管理员通知">
|
||||
<el-switch v-model="kdocsAdminNotifyEnabled" />
|
||||
</el-form-item>
|
||||
|
||||
@@ -39,8 +39,3 @@ export async function updateKdocsSettings(payload) {
|
||||
const { data } = await publicApi.post('/user/kdocs', payload)
|
||||
return data
|
||||
}
|
||||
|
||||
export async function fetchKdocsStatus() {
|
||||
const { data } = await publicApi.get('/kdocs/status')
|
||||
return data
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ import {
|
||||
updateAccount,
|
||||
updateAccountRemark,
|
||||
} from '../api/accounts'
|
||||
import { fetchKdocsSettings, fetchKdocsStatus, updateKdocsSettings } from '../api/settings'
|
||||
import { fetchKdocsSettings, updateKdocsSettings } from '../api/settings'
|
||||
import { fetchRunStats } from '../api/stats'
|
||||
import { useSocket } from '../composables/useSocket'
|
||||
import { useUserStore } from '../stores/user'
|
||||
@@ -61,14 +61,6 @@ watch(batchEnableScreenshot, (value) => {
|
||||
const kdocsAutoUpload = ref(false)
|
||||
const kdocsSettingsLoading = ref(false)
|
||||
|
||||
// KDocs 在线状态
|
||||
const kdocsStatus = reactive({
|
||||
enabled: false,
|
||||
online: false,
|
||||
message: '',
|
||||
})
|
||||
const kdocsStatusLoading = ref(false)
|
||||
|
||||
const addOpen = ref(false)
|
||||
const editOpen = ref(false)
|
||||
const upgradeOpen = ref(false)
|
||||
@@ -213,22 +205,6 @@ async function loadKdocsSettings() {
|
||||
}
|
||||
}
|
||||
|
||||
async function loadKdocsStatus() {
|
||||
kdocsStatusLoading.value = true
|
||||
try {
|
||||
const data = await fetchKdocsStatus()
|
||||
kdocsStatus.enabled = Boolean(data?.enabled)
|
||||
kdocsStatus.online = Boolean(data?.online)
|
||||
kdocsStatus.message = data?.message || ''
|
||||
} catch {
|
||||
kdocsStatus.enabled = false
|
||||
kdocsStatus.online = false
|
||||
kdocsStatus.message = ''
|
||||
} finally {
|
||||
kdocsStatusLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function onToggleKdocsAutoUpload(value) {
|
||||
kdocsSettingsLoading.value = true
|
||||
try {
|
||||
@@ -566,8 +542,6 @@ watch(shouldPollStats, (running, prevRunning) => {
|
||||
syncStatsPolling(prevRunning)
|
||||
})
|
||||
|
||||
let kdocsStatusTimer = null
|
||||
|
||||
onMounted(async () => {
|
||||
if (!userStore.vipInfo) {
|
||||
userStore.refreshVipInfo().catch(() => {
|
||||
@@ -579,18 +553,13 @@ onMounted(async () => {
|
||||
|
||||
await refreshAccounts()
|
||||
await loadKdocsSettings()
|
||||
await loadKdocsStatus()
|
||||
await refreshStats()
|
||||
syncStatsPolling()
|
||||
|
||||
// 每60秒刷新 KDocs 状态
|
||||
kdocsStatusTimer = window.setInterval(() => loadKdocsStatus(), 60_000)
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
if (unbindSocket) unbindSocket()
|
||||
stopStatsPolling()
|
||||
if (kdocsStatusTimer) window.clearInterval(kdocsStatusTimer)
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -681,9 +650,6 @@ onBeforeUnmount(() => {
|
||||
@change="onToggleKdocsAutoUpload"
|
||||
/>
|
||||
<span class="app-muted">表格(测试)</span>
|
||||
<el-tag v-if="kdocsStatus.enabled" :type="kdocsStatus.online ? 'success' : 'warning'" size="small" effect="plain">
|
||||
{{ kdocsStatus.online ? '✅ 就绪' : '⚠️ 离线' }}
|
||||
</el-tag>
|
||||
</div>
|
||||
|
||||
<div class="toolbar-right">
|
||||
|
||||
214
browser_installer.py
Executable file
214
browser_installer.py
Executable file
@@ -0,0 +1,214 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
浏览器自动下载安装模块
|
||||
检测本地是否有Playwright浏览器,如果没有则自动下载安装
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import shutil
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
# 设置浏览器安装路径(支持Docker和本地环境)
|
||||
# Docker环境: PLAYWRIGHT_BROWSERS_PATH环境变量已设置为 /ms-playwright
|
||||
# 本地环境: 使用Playwright默认路径
|
||||
if 'PLAYWRIGHT_BROWSERS_PATH' in os.environ:
|
||||
BROWSERS_PATH = os.environ['PLAYWRIGHT_BROWSERS_PATH']
|
||||
else:
|
||||
# Windows: %USERPROFILE%\AppData\Local\ms-playwright
|
||||
# Linux: ~/.cache/ms-playwright
|
||||
if sys.platform == 'win32':
|
||||
BROWSERS_PATH = str(Path.home() / "AppData" / "Local" / "ms-playwright")
|
||||
else:
|
||||
BROWSERS_PATH = str(Path.home() / ".cache" / "ms-playwright")
|
||||
os.environ["PLAYWRIGHT_BROWSERS_PATH"] = BROWSERS_PATH
|
||||
|
||||
|
||||
class BrowserInstaller:
|
||||
"""浏览器安装器"""
|
||||
|
||||
def __init__(self, log_callback=None):
|
||||
"""
|
||||
初始化安装器
|
||||
|
||||
Args:
|
||||
log_callback: 日志回调函数
|
||||
"""
|
||||
self.log_callback = log_callback
|
||||
|
||||
def log(self, message):
|
||||
"""输出日志"""
|
||||
if self.log_callback:
|
||||
self.log_callback(message)
|
||||
else:
|
||||
try:
|
||||
print(message)
|
||||
except UnicodeEncodeError:
|
||||
# 如果打印Unicode字符失败,替换特殊字符
|
||||
safe_message = message.replace('✓', '[OK]').replace('✗', '[X]')
|
||||
print(safe_message)
|
||||
|
||||
def check_playwright_installed(self):
|
||||
"""检查Playwright是否已安装"""
|
||||
try:
|
||||
import playwright
|
||||
self.log("✓ Playwright已安装")
|
||||
return True
|
||||
except ImportError:
|
||||
self.log("✗ Playwright未安装")
|
||||
return False
|
||||
|
||||
def check_chromium_installed(self):
|
||||
"""检查Chromium浏览器是否已安装"""
|
||||
try:
|
||||
from playwright.sync_api import sync_playwright
|
||||
|
||||
# 尝试启动浏览器检查是否可用
|
||||
with sync_playwright() as p:
|
||||
try:
|
||||
# 使用超时快速检查
|
||||
browser = p.chromium.launch(headless=True, timeout=5000)
|
||||
browser.close()
|
||||
self.log("✓ Chromium浏览器已安装且可用")
|
||||
return True
|
||||
except Exception as e:
|
||||
error_msg = str(e)
|
||||
self.log(f"✗ Chromium浏览器不可用: {error_msg}")
|
||||
|
||||
# 检查是否是路径不存在的错误
|
||||
if "Executable doesn't exist" in error_msg:
|
||||
self.log("检测到浏览器文件缺失,需要重新安装")
|
||||
|
||||
return False
|
||||
except Exception as e:
|
||||
self.log(f"✗ 检查浏览器时出错: {str(e)}")
|
||||
return False
|
||||
|
||||
def install_chromium(self):
|
||||
"""安装Chromium浏览器"""
|
||||
try:
|
||||
self.log("正在安装 Chromium 浏览器...")
|
||||
|
||||
# 查找 playwright 可执行文件
|
||||
playwright_cli = None
|
||||
possible_paths = [
|
||||
os.path.join(os.path.dirname(sys.executable), "Scripts", "playwright.exe"),
|
||||
os.path.join(os.path.dirname(sys.executable), "playwright.exe"),
|
||||
os.path.join(os.path.dirname(sys.executable), "Scripts", "playwright"),
|
||||
os.path.join(os.path.dirname(sys.executable), "playwright"),
|
||||
"playwright", # 系统PATH中
|
||||
]
|
||||
|
||||
for path in possible_paths:
|
||||
if os.path.exists(path) or shutil.which(path):
|
||||
playwright_cli = path
|
||||
break
|
||||
|
||||
# 如果找到了 playwright CLI,直接调用
|
||||
if playwright_cli:
|
||||
self.log(f"使用 Playwright CLI: {playwright_cli}")
|
||||
result = subprocess.run(
|
||||
[playwright_cli, "install", "chromium"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=300
|
||||
)
|
||||
else:
|
||||
# 检测是否是 Nuitka 编译的程序
|
||||
is_nuitka = hasattr(sys, 'frozen') or '__compiled__' in globals()
|
||||
|
||||
if is_nuitka:
|
||||
self.log("检测到 Nuitka 编译环境")
|
||||
self.log("✗ 无法找到 playwright CLI 工具")
|
||||
self.log("请手动运行: playwright install chromium")
|
||||
return False
|
||||
else:
|
||||
# 使用 python -m
|
||||
result = subprocess.run(
|
||||
[sys.executable, "-m", "playwright", "install", "chromium"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=300
|
||||
)
|
||||
|
||||
if result.returncode == 0:
|
||||
self.log("✓ Chromium浏览器安装成功")
|
||||
return True
|
||||
else:
|
||||
self.log(f"✗ 浏览器安装失败: {result.stderr}")
|
||||
return False
|
||||
|
||||
except subprocess.TimeoutExpired:
|
||||
self.log("✗ 浏览器安装超时")
|
||||
return False
|
||||
except Exception as e:
|
||||
self.log(f"✗ 浏览器安装出错: {str(e)}")
|
||||
return False
|
||||
|
||||
def auto_install(self):
|
||||
"""
|
||||
自动检测并安装所需环境
|
||||
|
||||
Returns:
|
||||
是否成功安装或已安装
|
||||
"""
|
||||
self.log("=" * 60)
|
||||
self.log("检查浏览器环境...")
|
||||
self.log("=" * 60)
|
||||
|
||||
# 1. 检查Playwright是否安装
|
||||
if not self.check_playwright_installed():
|
||||
self.log("✗ Playwright未安装,无法继续")
|
||||
self.log("请确保程序包含 Playwright 库")
|
||||
return False
|
||||
|
||||
# 2. 检查Chromium浏览器是否安装
|
||||
if not self.check_chromium_installed():
|
||||
self.log("\n未检测到Chromium浏览器,开始自动安装...")
|
||||
|
||||
# 安装浏览器
|
||||
if not self.install_chromium():
|
||||
self.log("✗ 浏览器安装失败")
|
||||
self.log("\n您可以尝试以下方法:")
|
||||
self.log("1. 手动执行: playwright install chromium")
|
||||
self.log("2. 检查网络连接后重试")
|
||||
self.log("3. 检查防火墙设置")
|
||||
return False
|
||||
|
||||
self.log("\n" + "=" * 60)
|
||||
self.log("✓ 浏览器环境检查完成,一切就绪!")
|
||||
self.log("=" * 60 + "\n")
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def check_and_install_browser(log_callback=None):
|
||||
"""
|
||||
便捷函数:检查并安装浏览器
|
||||
|
||||
Args:
|
||||
log_callback: 日志回调函数
|
||||
|
||||
Returns:
|
||||
是否成功
|
||||
"""
|
||||
installer = BrowserInstaller(log_callback)
|
||||
return installer.auto_install()
|
||||
|
||||
|
||||
# 测试代码
|
||||
if __name__ == "__main__":
|
||||
print("浏览器自动安装工具")
|
||||
print("=" * 60)
|
||||
|
||||
installer = BrowserInstaller()
|
||||
success = installer.auto_install()
|
||||
|
||||
if success:
|
||||
print("\n✓ 安装成功!您现在可以运行主程序了。")
|
||||
else:
|
||||
print("\n✗ 安装失败,请查看上方错误信息。")
|
||||
|
||||
print("=" * 60)
|
||||
@@ -7,62 +7,48 @@ services:
|
||||
ports:
|
||||
- "51232:51233"
|
||||
volumes:
|
||||
- ./data:/app/data # 数据库持久化
|
||||
- ./logs:/app/logs # 日志持久化
|
||||
- ./截图:/app/截图 # 截图持久化
|
||||
- /etc/localtime:/etc/localtime:ro # 时区同步
|
||||
- ./static:/app/static # 静态文件(实时更新)
|
||||
- ./templates:/app/templates # 模板文件(实时更新)
|
||||
- ./app.py:/app/app.py # 主程序(实时更新)
|
||||
- ./database.py:/app/database.py # 数据库模块(实时更新)
|
||||
- ./data:/app/data
|
||||
- ./logs:/app/logs
|
||||
- ./截图:/app/截图
|
||||
- ./playwright:/ms-playwright
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- ./static:/app/static
|
||||
- ./templates:/app/templates
|
||||
- ./app.py:/app/app.py
|
||||
- ./database.py:/app/database.py
|
||||
# 代码热更新
|
||||
- ./services:/app/services
|
||||
- ./routes:/app/routes
|
||||
- ./db:/app/db
|
||||
- ./security:/app/security
|
||||
- ./realtime:/app/realtime
|
||||
- ./api_browser.py:/app/api_browser.py
|
||||
- ./app_config.py:/app/app_config.py
|
||||
- ./app_logger.py:/app/app_logger.py
|
||||
- ./app_security.py:/app/app_security.py
|
||||
- ./browser_pool_worker.py:/app/browser_pool_worker.py
|
||||
- ./crypto_utils.py:/app/crypto_utils.py
|
||||
- ./db_pool.py:/app/db_pool.py
|
||||
- ./email_service.py:/app/email_service.py
|
||||
- ./password_utils.py:/app/password_utils.py
|
||||
- ./playwright_automation.py:/app/playwright_automation.py
|
||||
- ./task_checkpoint.py:/app/task_checkpoint.py
|
||||
dns:
|
||||
- 223.5.5.5
|
||||
- 114.114.114.114
|
||||
- 119.29.29.29
|
||||
environment:
|
||||
- TZ=Asia/Shanghai
|
||||
- PYTHONUNBUFFERED=1
|
||||
# Flask 配置
|
||||
- PLAYWRIGHT_BROWSERS_PATH=/ms-playwright
|
||||
- FLASK_ENV=production
|
||||
- FLASK_DEBUG=false
|
||||
# 服务器配置
|
||||
- SERVER_HOST=0.0.0.0
|
||||
- SERVER_PORT=51233
|
||||
# 数据库配置
|
||||
- DB_FILE=data/app_data.db
|
||||
- DB_POOL_SIZE=5
|
||||
- SYSTEM_CONFIG_CACHE_TTL_SECONDS=30
|
||||
# 并发控制配置
|
||||
- MAX_CONCURRENT_GLOBAL=2
|
||||
- MAX_CONCURRENT_PER_ACCOUNT=1
|
||||
- MAX_CONCURRENT_CONTEXTS=100
|
||||
# 安全配置
|
||||
- SESSION_LIFETIME_HOURS=24
|
||||
- SESSION_COOKIE_SECURE=false
|
||||
- MAX_CAPTCHA_ATTEMPTS=5
|
||||
- MAX_IP_ATTEMPTS_PER_HOUR=10
|
||||
# 日志配置
|
||||
- LOG_LEVEL=INFO
|
||||
- LOG_FILE=logs/app.log
|
||||
- API_DIAGNOSTIC_LOG=0
|
||||
- API_DIAGNOSTIC_SLOW_MS=0
|
||||
# 状态推送节流(秒)
|
||||
- STATUS_PUSH_INTERVAL_SECONDS=2
|
||||
# wkhtmltoimage 截图配置
|
||||
- WKHTMLTOIMAGE_FULL_PAGE=0
|
||||
# 知识管理平台配置
|
||||
- ZSGL_LOGIN_URL=https://postoa.aidunsoft.com/admin/login.aspx
|
||||
- ZSGL_INDEX_URL_PATTERN=index.aspx
|
||||
- PAGE_LOAD_TIMEOUT=60000
|
||||
restart: unless-stopped
|
||||
shm_size: 2gb # 为Chromium分配共享内存
|
||||
|
||||
# 内存和CPU资源限制
|
||||
mem_limit: 4g # 硬限制:最大4GB内存
|
||||
mem_reservation: 2g # 软限制:预留2GB
|
||||
cpus: '2.0' # 限制使用2个CPU核心
|
||||
|
||||
# 健康检查(可选)
|
||||
shm_size: 2gb
|
||||
mem_limit: 4g
|
||||
mem_reservation: 2g
|
||||
cpus: '2.0'
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "curl -f http://localhost:51233 || exit 1"]
|
||||
interval: 5m
|
||||
|
||||
1591
playwright_automation.py
Executable file
1591
playwright_automation.py
Executable file
File diff suppressed because it is too large
Load Diff
180
routes/admin_api/update.py
Normal file
180
routes/admin_api/update.py
Normal file
@@ -0,0 +1,180 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import uuid
|
||||
|
||||
from flask import jsonify, request, session
|
||||
|
||||
from routes.admin_api import admin_api_bp
|
||||
from routes.decorators import admin_required
|
||||
from services.time_utils import get_beijing_now
|
||||
from services.update_files import (
|
||||
ensure_update_dirs,
|
||||
get_update_job_log_path,
|
||||
get_update_request_path,
|
||||
get_update_result_path,
|
||||
get_update_status_path,
|
||||
load_json_file,
|
||||
sanitize_job_id,
|
||||
tail_text_file,
|
||||
write_json_atomic,
|
||||
)
|
||||
|
||||
|
||||
def _request_ip() -> str:
|
||||
try:
|
||||
return request.headers.get("X-Forwarded-For", "").split(",")[0].strip() or request.remote_addr or ""
|
||||
except Exception:
|
||||
return ""
|
||||
|
||||
|
||||
def _make_job_id(prefix: str = "upd") -> str:
|
||||
now_str = get_beijing_now().strftime("%Y%m%d_%H%M%S")
|
||||
rand = uuid.uuid4().hex[:8]
|
||||
return f"{prefix}_{now_str}_{rand}"
|
||||
|
||||
|
||||
def _has_pending_request() -> bool:
|
||||
try:
|
||||
return os.path.exists(get_update_request_path())
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
|
||||
def _parse_bool_field(data: dict, key: str) -> bool | None:
|
||||
if not isinstance(data, dict) or key not in data:
|
||||
return None
|
||||
value = data.get(key)
|
||||
if isinstance(value, bool):
|
||||
return value
|
||||
if isinstance(value, int):
|
||||
if value in (0, 1):
|
||||
return bool(value)
|
||||
raise ValueError(f"{key} 必须是 0/1 或 true/false")
|
||||
if isinstance(value, str):
|
||||
text = value.strip().lower()
|
||||
if text in ("1", "true", "yes", "y", "on"):
|
||||
return True
|
||||
if text in ("0", "false", "no", "n", "off", ""):
|
||||
return False
|
||||
raise ValueError(f"{key} 必须是 0/1 或 true/false")
|
||||
if value is None:
|
||||
return None
|
||||
raise ValueError(f"{key} 必须是 0/1 或 true/false")
|
||||
|
||||
|
||||
@admin_api_bp.route("/update/status", methods=["GET"])
|
||||
@admin_required
|
||||
def get_update_status_api():
|
||||
"""读取宿主机 Update-Agent 写入的 update/status.json。"""
|
||||
ensure_update_dirs()
|
||||
status_path = get_update_status_path()
|
||||
data, err = load_json_file(status_path)
|
||||
if err:
|
||||
return jsonify({"ok": False, "error": f"读取 status 失败: {err}", "data": data}), 200
|
||||
if not data:
|
||||
return jsonify({"ok": False, "error": "未发现更新状态(Update-Agent 可能未运行)"}), 200
|
||||
data.setdefault("update_available", False)
|
||||
return jsonify({"ok": True, "data": data}), 200
|
||||
|
||||
|
||||
@admin_api_bp.route("/update/result", methods=["GET"])
|
||||
@admin_required
|
||||
def get_update_result_api():
|
||||
"""读取 update/result.json(最近一次更新执行结果)。"""
|
||||
ensure_update_dirs()
|
||||
result_path = get_update_result_path()
|
||||
data, err = load_json_file(result_path)
|
||||
if err:
|
||||
return jsonify({"ok": False, "error": f"读取 result 失败: {err}", "data": data}), 200
|
||||
if not data:
|
||||
return jsonify({"ok": True, "data": None}), 200
|
||||
return jsonify({"ok": True, "data": data}), 200
|
||||
|
||||
|
||||
@admin_api_bp.route("/update/log", methods=["GET"])
|
||||
@admin_required
|
||||
def get_update_log_api():
|
||||
"""读取 update/jobs/<job_id>.log 的末尾内容(用于后台展示进度)。"""
|
||||
ensure_update_dirs()
|
||||
|
||||
job_id = sanitize_job_id(request.args.get("job_id"))
|
||||
if not job_id:
|
||||
# 若未指定,则尝试用 result.json 的 job_id
|
||||
result_data, _ = load_json_file(get_update_result_path())
|
||||
job_id = sanitize_job_id(result_data.get("job_id") if isinstance(result_data, dict) else None)
|
||||
|
||||
if not job_id:
|
||||
return jsonify({"ok": True, "job_id": None, "log": "", "truncated": False}), 200
|
||||
|
||||
max_bytes = request.args.get("max_bytes", "200000")
|
||||
try:
|
||||
max_bytes_i = int(max_bytes)
|
||||
except Exception:
|
||||
max_bytes_i = 200_000
|
||||
max_bytes_i = max(10_000, min(2_000_000, max_bytes_i))
|
||||
|
||||
log_path = get_update_job_log_path(job_id)
|
||||
text, truncated = tail_text_file(log_path, max_bytes=max_bytes_i)
|
||||
return jsonify({"ok": True, "job_id": job_id, "log": text, "truncated": truncated}), 200
|
||||
|
||||
|
||||
@admin_api_bp.route("/update/check", methods=["POST"])
|
||||
@admin_required
|
||||
def request_update_check_api():
|
||||
"""请求宿主机 Update-Agent 立刻执行一次检查更新。"""
|
||||
ensure_update_dirs()
|
||||
if _has_pending_request():
|
||||
return jsonify({"error": "已有更新请求正在处理中,请稍后再试"}), 409
|
||||
|
||||
job_id = _make_job_id(prefix="chk")
|
||||
payload = {
|
||||
"job_id": job_id,
|
||||
"action": "check",
|
||||
"requested_at": get_beijing_now().strftime("%Y-%m-%d %H:%M:%S"),
|
||||
"requested_by": session.get("admin_username") or "",
|
||||
"requested_ip": _request_ip(),
|
||||
}
|
||||
write_json_atomic(get_update_request_path(), payload)
|
||||
return jsonify({"success": True, "job_id": job_id}), 200
|
||||
|
||||
|
||||
@admin_api_bp.route("/update/run", methods=["POST"])
|
||||
@admin_required
|
||||
def request_update_run_api():
|
||||
"""请求宿主机 Update-Agent 执行一键更新并重启服务。"""
|
||||
ensure_update_dirs()
|
||||
if _has_pending_request():
|
||||
return jsonify({"error": "已有更新请求正在处理中,请稍后再试"}), 409
|
||||
|
||||
data = request.json or {}
|
||||
try:
|
||||
build_no_cache = _parse_bool_field(data, "build_no_cache")
|
||||
if build_no_cache is None:
|
||||
build_no_cache = _parse_bool_field(data, "no_cache")
|
||||
build_pull = _parse_bool_field(data, "build_pull")
|
||||
if build_pull is None:
|
||||
build_pull = _parse_bool_field(data, "pull")
|
||||
except ValueError as e:
|
||||
return jsonify({"error": str(e)}), 400
|
||||
|
||||
job_id = _make_job_id(prefix="upd")
|
||||
payload = {
|
||||
"job_id": job_id,
|
||||
"action": "update",
|
||||
"requested_at": get_beijing_now().strftime("%Y-%m-%d %H:%M:%S"),
|
||||
"requested_by": session.get("admin_username") or "",
|
||||
"requested_ip": _request_ip(),
|
||||
"build_no_cache": bool(build_no_cache) if build_no_cache is not None else False,
|
||||
"build_pull": bool(build_pull) if build_pull is not None else False,
|
||||
}
|
||||
write_json_atomic(get_update_request_path(), payload)
|
||||
return jsonify(
|
||||
{
|
||||
"success": True,
|
||||
"job_id": job_id,
|
||||
"message": "已提交更新请求,服务将重启(页面可能短暂不可用),请等待1-2分钟后刷新",
|
||||
}
|
||||
), 200
|
||||
112
services/browser_manager.py
Normal file
112
services/browser_manager.py
Normal file
@@ -0,0 +1,112 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import annotations
|
||||
|
||||
import threading
|
||||
import time
|
||||
from typing import Optional
|
||||
|
||||
from app_logger import get_logger
|
||||
from browser_installer import check_and_install_browser
|
||||
from playwright_automation import PlaywrightBrowserManager
|
||||
|
||||
logger = get_logger("browser_manager")
|
||||
|
||||
_browser_manager: Optional[PlaywrightBrowserManager] = None
|
||||
_lock = threading.Lock()
|
||||
_cond = threading.Condition(_lock)
|
||||
_init_in_progress = False
|
||||
_init_error: Optional[str] = None
|
||||
_init_thread: Optional[threading.Thread] = None
|
||||
|
||||
|
||||
def get_browser_manager() -> Optional[PlaywrightBrowserManager]:
|
||||
return _browser_manager
|
||||
|
||||
|
||||
def is_browser_manager_ready() -> bool:
|
||||
return _browser_manager is not None
|
||||
|
||||
|
||||
def get_browser_manager_init_error() -> Optional[str]:
|
||||
return _init_error
|
||||
|
||||
|
||||
def init_browser_manager(*, block: bool = True, timeout: Optional[float] = None) -> bool:
|
||||
global _browser_manager
|
||||
global _init_in_progress, _init_error
|
||||
|
||||
deadline = time.monotonic() + float(timeout) if timeout is not None else None
|
||||
|
||||
with _cond:
|
||||
if _browser_manager is not None:
|
||||
return True
|
||||
|
||||
if _init_in_progress:
|
||||
if not block:
|
||||
return False
|
||||
while _init_in_progress:
|
||||
if deadline is None:
|
||||
_cond.wait(timeout=0.5)
|
||||
continue
|
||||
remaining = deadline - time.monotonic()
|
||||
if remaining <= 0:
|
||||
break
|
||||
_cond.wait(timeout=min(0.5, remaining))
|
||||
return _browser_manager is not None
|
||||
|
||||
_init_in_progress = True
|
||||
_init_error = None
|
||||
|
||||
ok = False
|
||||
error: Optional[str] = None
|
||||
manager: Optional[PlaywrightBrowserManager] = None
|
||||
|
||||
try:
|
||||
logger.info("正在初始化Playwright浏览器管理器...")
|
||||
if not check_and_install_browser(log_callback=lambda msg, account_id=None: logger.info(str(msg))):
|
||||
error = "浏览器环境检查失败"
|
||||
logger.error("浏览器环境检查失败!")
|
||||
ok = False
|
||||
else:
|
||||
manager = PlaywrightBrowserManager(
|
||||
headless=True,
|
||||
log_callback=lambda msg, account_id=None: logger.info(str(msg)),
|
||||
)
|
||||
ok = True
|
||||
logger.info("Playwright浏览器管理器创建成功!")
|
||||
except Exception as exc:
|
||||
error = f"{type(exc).__name__}: {exc}"
|
||||
logger.exception("初始化Playwright浏览器管理器时发生异常")
|
||||
ok = False
|
||||
|
||||
with _cond:
|
||||
if ok and manager is not None:
|
||||
_browser_manager = manager
|
||||
else:
|
||||
_init_error = error or "初始化失败"
|
||||
_init_in_progress = False
|
||||
_cond.notify_all()
|
||||
|
||||
return ok
|
||||
|
||||
|
||||
def init_browser_manager_async() -> None:
|
||||
"""异步初始化浏览器环境,避免阻塞 Web 请求/服务启动。"""
|
||||
global _init_thread
|
||||
|
||||
def _worker():
|
||||
try:
|
||||
init_browser_manager(block=True)
|
||||
except Exception:
|
||||
logger.exception("异步初始化浏览器管理器失败")
|
||||
|
||||
with _cond:
|
||||
if _browser_manager is not None:
|
||||
return
|
||||
if _init_thread and _init_thread.is_alive():
|
||||
return
|
||||
if _init_in_progress:
|
||||
return
|
||||
_init_thread = threading.Thread(target=_worker, daemon=True, name="browser-manager-init")
|
||||
_init_thread.start()
|
||||
@@ -1,34 +1,34 @@
|
||||
{
|
||||
"_email-C4xyG93p.js": {
|
||||
"file": "assets/email-C4xyG93p.js",
|
||||
"_email-BsKBHU5S.js": {
|
||||
"file": "assets/email-BsKBHU5S.js",
|
||||
"name": "email",
|
||||
"imports": [
|
||||
"index.html"
|
||||
]
|
||||
},
|
||||
"_system-C6kBIFhi.js": {
|
||||
"file": "assets/system-C6kBIFhi.js",
|
||||
"name": "system",
|
||||
"imports": [
|
||||
"index.html"
|
||||
]
|
||||
},
|
||||
"_tasks-dxahzB_w.js": {
|
||||
"file": "assets/tasks-dxahzB_w.js",
|
||||
"_tasks-DpslJtm_.js": {
|
||||
"file": "assets/tasks-DpslJtm_.js",
|
||||
"name": "tasks",
|
||||
"imports": [
|
||||
"index.html"
|
||||
]
|
||||
},
|
||||
"_users-ecMaaAFD.js": {
|
||||
"file": "assets/users-ecMaaAFD.js",
|
||||
"_update-DcFD-YxU.js": {
|
||||
"file": "assets/update-DcFD-YxU.js",
|
||||
"name": "update",
|
||||
"imports": [
|
||||
"index.html"
|
||||
]
|
||||
},
|
||||
"_users-CC9BckjT.js": {
|
||||
"file": "assets/users-CC9BckjT.js",
|
||||
"name": "users",
|
||||
"imports": [
|
||||
"index.html"
|
||||
]
|
||||
},
|
||||
"index.html": {
|
||||
"file": "assets/index-DKH_HvPt.js",
|
||||
"file": "assets/index-CdjS44Uj.js",
|
||||
"name": "index",
|
||||
"src": "index.html",
|
||||
"isEntry": true,
|
||||
@@ -39,16 +39,15 @@
|
||||
"src/pages/LogsPage.vue",
|
||||
"src/pages/AnnouncementsPage.vue",
|
||||
"src/pages/EmailPage.vue",
|
||||
"src/pages/SecurityPage.vue",
|
||||
"src/pages/SystemPage.vue",
|
||||
"src/pages/SettingsPage.vue"
|
||||
],
|
||||
"css": [
|
||||
"assets/index-_5Ec1Hmd.css"
|
||||
"assets/index-EWm4DZW8.css"
|
||||
]
|
||||
},
|
||||
"src/pages/AnnouncementsPage.vue": {
|
||||
"file": "assets/AnnouncementsPage-kpoSCxEP.js",
|
||||
"file": "assets/AnnouncementsPage-Djmq3Wb7.js",
|
||||
"name": "AnnouncementsPage",
|
||||
"src": "src/pages/AnnouncementsPage.vue",
|
||||
"isDynamicEntry": true,
|
||||
@@ -56,24 +55,24 @@
|
||||
"index.html"
|
||||
],
|
||||
"css": [
|
||||
"assets/AnnouncementsPage-BhIwmMSX.css"
|
||||
"assets/AnnouncementsPage-CjcC-aWD.css"
|
||||
]
|
||||
},
|
||||
"src/pages/EmailPage.vue": {
|
||||
"file": "assets/EmailPage-CEtsoP5P.js",
|
||||
"file": "assets/EmailPage-q6nJlTue.js",
|
||||
"name": "EmailPage",
|
||||
"src": "src/pages/EmailPage.vue",
|
||||
"isDynamicEntry": true,
|
||||
"imports": [
|
||||
"_email-C4xyG93p.js",
|
||||
"_email-BsKBHU5S.js",
|
||||
"index.html"
|
||||
],
|
||||
"css": [
|
||||
"assets/EmailPage-BH6ksrcc.css"
|
||||
"assets/EmailPage-BxzHc6tN.css"
|
||||
]
|
||||
},
|
||||
"src/pages/FeedbacksPage.vue": {
|
||||
"file": "assets/FeedbacksPage-ByHln3Ce.js",
|
||||
"file": "assets/FeedbacksPage-Drw6uvSR.js",
|
||||
"name": "FeedbacksPage",
|
||||
"src": "src/pages/FeedbacksPage.vue",
|
||||
"isDynamicEntry": true,
|
||||
@@ -85,13 +84,13 @@
|
||||
]
|
||||
},
|
||||
"src/pages/LogsPage.vue": {
|
||||
"file": "assets/LogsPage-vZFAwgb-.js",
|
||||
"file": "assets/LogsPage-DQd9IS3I.js",
|
||||
"name": "LogsPage",
|
||||
"src": "src/pages/LogsPage.vue",
|
||||
"isDynamicEntry": true,
|
||||
"imports": [
|
||||
"_users-ecMaaAFD.js",
|
||||
"_tasks-dxahzB_w.js",
|
||||
"_users-CC9BckjT.js",
|
||||
"_tasks-DpslJtm_.js",
|
||||
"index.html"
|
||||
],
|
||||
"css": [
|
||||
@@ -99,34 +98,22 @@
|
||||
]
|
||||
},
|
||||
"src/pages/ReportPage.vue": {
|
||||
"file": "assets/ReportPage--ClMBhif.js",
|
||||
"file": "assets/ReportPage-Dnk3wsl3.js",
|
||||
"name": "ReportPage",
|
||||
"src": "src/pages/ReportPage.vue",
|
||||
"isDynamicEntry": true,
|
||||
"imports": [
|
||||
"index.html",
|
||||
"_email-C4xyG93p.js",
|
||||
"_tasks-dxahzB_w.js",
|
||||
"_system-C6kBIFhi.js"
|
||||
"_email-BsKBHU5S.js",
|
||||
"_tasks-DpslJtm_.js",
|
||||
"_update-DcFD-YxU.js"
|
||||
],
|
||||
"css": [
|
||||
"assets/ReportPage-Q8rCsG8A.css"
|
||||
]
|
||||
},
|
||||
"src/pages/SecurityPage.vue": {
|
||||
"file": "assets/SecurityPage-DBhX0IuO.js",
|
||||
"name": "SecurityPage",
|
||||
"src": "src/pages/SecurityPage.vue",
|
||||
"isDynamicEntry": true,
|
||||
"imports": [
|
||||
"index.html"
|
||||
],
|
||||
"css": [
|
||||
"assets/SecurityPage-Dv9jYTtC.css"
|
||||
"assets/ReportPage-TpqQWWvU.css"
|
||||
]
|
||||
},
|
||||
"src/pages/SettingsPage.vue": {
|
||||
"file": "assets/SettingsPage-D91FOriC.js",
|
||||
"file": "assets/SettingsPage-YOW1Apwk.js",
|
||||
"name": "SettingsPage",
|
||||
"src": "src/pages/SettingsPage.vue",
|
||||
"isDynamicEntry": true,
|
||||
@@ -134,33 +121,33 @@
|
||||
"index.html"
|
||||
],
|
||||
"css": [
|
||||
"assets/SettingsPage-DKTq8S2K.css"
|
||||
"assets/SettingsPage-DGdwb4W2.css"
|
||||
]
|
||||
},
|
||||
"src/pages/SystemPage.vue": {
|
||||
"file": "assets/SystemPage-DVj-4Lnp.js",
|
||||
"file": "assets/SystemPage-DCcH_SAQ.js",
|
||||
"name": "SystemPage",
|
||||
"src": "src/pages/SystemPage.vue",
|
||||
"isDynamicEntry": true,
|
||||
"imports": [
|
||||
"_system-C6kBIFhi.js",
|
||||
"_update-DcFD-YxU.js",
|
||||
"index.html"
|
||||
],
|
||||
"css": [
|
||||
"assets/SystemPage-C8GQyKcD.css"
|
||||
"assets/SystemPage-BjTkcmTG.css"
|
||||
]
|
||||
},
|
||||
"src/pages/UsersPage.vue": {
|
||||
"file": "assets/UsersPage-C_vL5-r3.js",
|
||||
"file": "assets/UsersPage-DhTO_5zp.js",
|
||||
"name": "UsersPage",
|
||||
"src": "src/pages/UsersPage.vue",
|
||||
"isDynamicEntry": true,
|
||||
"imports": [
|
||||
"_users-ecMaaAFD.js",
|
||||
"_users-CC9BckjT.js",
|
||||
"index.html"
|
||||
],
|
||||
"css": [
|
||||
"assets/UsersPage-CC4Unpwt.css"
|
||||
"assets/UsersPage-CbiPbpuj.css"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
.page-stack[data-v-cad97d6b]{display:flex;flex-direction:column;gap:12px}.card[data-v-cad97d6b]{border-radius:var(--app-radius);border:1px solid var(--app-border)}.section-title[data-v-cad97d6b]{margin:0 0 12px;font-size:14px;font-weight:800}.help[data-v-cad97d6b]{margin-top:10px;font-size:12px;color:var(--app-muted)}.image-preview[data-v-cad97d6b]{margin:6px 0 2px;display:flex;justify-content:flex-start}.image-preview img[data-v-cad97d6b]{max-width:280px;max-height:160px;border-radius:8px;border:1px solid var(--app-border);object-fit:contain}.image-upload-row[data-v-cad97d6b]{display:flex;align-items:center;gap:10px;flex-wrap:wrap}.image-input[data-v-cad97d6b]{display:none}.image-url[data-v-cad97d6b]{font-size:12px;color:var(--app-muted);word-break:break-all}.announcement-view[data-v-cad97d6b]{display:flex;flex-direction:column;gap:12px}.announcement-view-text[data-v-cad97d6b]{white-space:pre-wrap;line-height:1.6;font-size:14px}.announcement-view-image[data-v-cad97d6b]{max-width:100%;max-height:320px;border-radius:10px;border:1px solid var(--app-border);object-fit:contain}.table-wrap[data-v-cad97d6b]{overflow-x:auto}.ellipsis[data-v-cad97d6b]{display:inline-block;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.actions[data-v-cad97d6b]{display:flex;flex-wrap:wrap;gap:8px}
|
||||
1
static/admin/assets/AnnouncementsPage-CjcC-aWD.css
Normal file
1
static/admin/assets/AnnouncementsPage-CjcC-aWD.css
Normal file
@@ -0,0 +1 @@
|
||||
.page-stack[data-v-a7b3418e]{display:flex;flex-direction:column;gap:12px}.card[data-v-a7b3418e]{border-radius:var(--app-radius);border:1px solid var(--app-border)}.section-title[data-v-a7b3418e]{margin:0 0 12px;font-size:14px;font-weight:800}.help[data-v-a7b3418e]{margin-top:10px;font-size:12px;color:var(--app-muted)}.table-wrap[data-v-a7b3418e]{overflow-x:auto}.ellipsis[data-v-a7b3418e]{display:inline-block;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.actions[data-v-a7b3418e]{display:flex;flex-wrap:wrap;gap:8px}
|
||||
1
static/admin/assets/AnnouncementsPage-Djmq3Wb7.js
Normal file
1
static/admin/assets/AnnouncementsPage-Djmq3Wb7.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
||||
.page-stack[data-v-7a7e1e9d]{display:flex;flex-direction:column;gap:12px}.toolbar[data-v-7a7e1e9d]{display:flex;gap:10px;align-items:center;flex-wrap:wrap}.card[data-v-7a7e1e9d]{border-radius:var(--app-radius);border:1px solid var(--app-border)}.section-head[data-v-7a7e1e9d]{display:flex;align-items:baseline;justify-content:space-between;gap:12px;margin-bottom:12px;flex-wrap:wrap}.section-title[data-v-7a7e1e9d]{margin:0;font-size:14px;font-weight:800}.help[data-v-7a7e1e9d]{margin-top:8px;font-size:12px;color:var(--app-muted)}.table-wrap[data-v-7a7e1e9d]{overflow-x:auto}.stat-card[data-v-7a7e1e9d]{border-radius:var(--app-radius);border:1px solid var(--app-border)}.stat-value[data-v-7a7e1e9d]{font-size:20px;font-weight:900;line-height:1.1}.stat-label[data-v-7a7e1e9d]{margin-top:6px;font-size:12px;color:var(--app-muted)}.ok[data-v-7a7e1e9d]{color:#047857}.err[data-v-7a7e1e9d]{color:#b91c1c}.sub-stats[data-v-7a7e1e9d]{display:flex;flex-wrap:wrap;gap:8px;margin-top:12px}.ellipsis[data-v-7a7e1e9d]{display:inline-block;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.pagination[data-v-7a7e1e9d]{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-top:14px;flex-wrap:wrap}.page-hint[data-v-7a7e1e9d]{font-size:12px}.dialog-actions[data-v-7a7e1e9d]{display:flex;align-items:center;gap:10px;flex-wrap:wrap}.spacer[data-v-7a7e1e9d]{flex:1}
|
||||
1
static/admin/assets/EmailPage-BxzHc6tN.css
Normal file
1
static/admin/assets/EmailPage-BxzHc6tN.css
Normal file
@@ -0,0 +1 @@
|
||||
.page-stack[data-v-ff849557]{display:flex;flex-direction:column;gap:12px}.toolbar[data-v-ff849557]{display:flex;gap:10px;align-items:center;flex-wrap:wrap}.card[data-v-ff849557]{border-radius:var(--app-radius);border:1px solid var(--app-border)}.section-head[data-v-ff849557]{display:flex;align-items:baseline;justify-content:space-between;gap:12px;margin-bottom:12px;flex-wrap:wrap}.section-title[data-v-ff849557]{margin:0;font-size:14px;font-weight:800}.help[data-v-ff849557]{margin-top:8px;font-size:12px;color:var(--app-muted)}.table-wrap[data-v-ff849557]{overflow-x:auto}.stat-card[data-v-ff849557]{border-radius:var(--app-radius);border:1px solid var(--app-border)}.stat-value[data-v-ff849557]{font-size:20px;font-weight:900;line-height:1.1}.stat-label[data-v-ff849557]{margin-top:6px;font-size:12px;color:var(--app-muted)}.ok[data-v-ff849557]{color:#047857}.err[data-v-ff849557]{color:#b91c1c}.sub-stats[data-v-ff849557]{display:flex;flex-wrap:wrap;gap:8px;margin-top:12px}.ellipsis[data-v-ff849557]{display:inline-block;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.pagination[data-v-ff849557]{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-top:14px;flex-wrap:wrap}.page-hint[data-v-ff849557]{font-size:12px}.dialog-actions[data-v-ff849557]{display:flex;align-items:center;gap:10px;flex-wrap:wrap}.spacer[data-v-ff849557]{flex:1}
|
||||
File diff suppressed because one or more lines are too long
1
static/admin/assets/EmailPage-q6nJlTue.js
Normal file
1
static/admin/assets/EmailPage-q6nJlTue.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
static/admin/assets/FeedbacksPage-Drw6uvSR.js
Normal file
1
static/admin/assets/FeedbacksPage-Drw6uvSR.js
Normal file
File diff suppressed because one or more lines are too long
1
static/admin/assets/LogsPage-DQd9IS3I.js
Normal file
1
static/admin/assets/LogsPage-DQd9IS3I.js
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
1
static/admin/assets/ReportPage-Dnk3wsl3.js
Normal file
1
static/admin/assets/ReportPage-Dnk3wsl3.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
static/admin/assets/ReportPage-TpqQWWvU.css
Normal file
1
static/admin/assets/ReportPage-TpqQWWvU.css
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
||||
.page-stack[data-v-22d57053]{display:flex;flex-direction:column;gap:12px}.toolbar[data-v-22d57053]{display:flex;gap:10px;align-items:center;flex-wrap:wrap}.stats-row[data-v-22d57053]{margin-bottom:2px}.card[data-v-22d57053]{border-radius:var(--app-radius);border:1px solid var(--app-border)}.sub-card[data-v-22d57053]{margin-top:12px;border-radius:var(--app-radius);border:1px solid var(--app-border)}.stat-card[data-v-22d57053]{border-radius:var(--app-radius);border:1px solid var(--app-border);box-shadow:var(--app-shadow)}.stat-value[data-v-22d57053]{font-size:22px;font-weight:800;line-height:1.1}.stat-label[data-v-22d57053]{margin-top:6px;font-size:12px;color:var(--app-muted)}.filters[data-v-22d57053]{display:flex;flex-wrap:wrap;gap:10px;align-items:center;margin-bottom:12px}.table-wrap[data-v-22d57053]{overflow-x:auto}.ellipsis[data-v-22d57053]{display:inline-block;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.mono[data-v-22d57053]{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.pagination[data-v-22d57053]{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-top:14px;flex-wrap:wrap}.page-hint[data-v-22d57053]{font-size:12px}.inner-tabs[data-v-22d57053]{margin-top:6px}.risk-head[data-v-22d57053]{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;margin-bottom:12px;flex-wrap:wrap}.risk-title[data-v-22d57053]{display:flex;align-items:center;gap:10px;flex-wrap:wrap}.dialog-actions[data-v-22d57053]{display:flex;align-items:center;gap:10px}.spacer[data-v-22d57053]{flex:1}
|
||||
@@ -1 +0,0 @@
|
||||
import{a as m,_ as B,r as p,f as u,g as T,h as P,j as r,m as a,w as l,q as x,L as i,K as b}from"./index-DKH_HvPt.js";async function C(o){const{data:s}=await m.put("/admin/username",{new_username:o});return s}async function S(o){const{data:s}=await m.put("/admin/password",{new_password:o});return s}async function U(){const{data:o}=await m.post("/logout");return o}const A={class:"page-stack"},E={__name:"SettingsPage",setup(o){const s=p(""),d=p(""),n=p(!1);function k(t){const e=String(t||"");return e.length<8?{ok:!1,message:"密码长度至少8位"}:e.length>128?{ok:!1,message:"密码长度不能超过128个字符"}:!/[a-zA-Z]/.test(e)||!/\d/.test(e)?{ok:!1,message:"密码必须包含字母和数字"}:{ok:!0,message:""}}async function f(){try{await U()}catch{}finally{window.location.href="/yuyx"}}async function V(){const t=s.value.trim();if(!t){i.error("请输入新用户名");return}try{await b.confirm(`确定将管理员用户名修改为「${t}」吗?修改后需要重新登录。`,"修改用户名",{confirmButtonText:"确认修改",cancelButtonText:"取消",type:"warning"})}catch{return}n.value=!0;try{await C(t),i.success("用户名修改成功,请重新登录"),s.value="",setTimeout(f,1200)}catch{}finally{n.value=!1}}async function h(){const t=d.value;if(!t){i.error("请输入新密码");return}const e=k(t);if(!e.ok){i.error(e.message);return}try{await b.confirm("确定修改管理员密码吗?修改后需要重新登录。","修改密码",{confirmButtonText:"确认修改",cancelButtonText:"取消",type:"warning"})}catch{return}n.value=!0;try{await S(t),i.success("密码修改成功,请重新登录"),d.value="",setTimeout(f,1200)}catch{}finally{n.value=!1}}return(t,e)=>{const g=u("el-input"),w=u("el-form-item"),v=u("el-form"),y=u("el-button"),_=u("el-card");return P(),T("div",A,[e[7]||(e[7]=r("div",{class:"app-page-title"},[r("h2",null,"设置"),r("span",{class:"app-muted"},"管理员账号设置")],-1)),a(_,{shadow:"never","body-style":{padding:"16px"},class:"card"},{default:l(()=>[e[3]||(e[3]=r("h3",{class:"section-title"},"修改管理员用户名",-1)),a(v,{"label-width":"120px"},{default:l(()=>[a(w,{label:"新用户名"},{default:l(()=>[a(g,{modelValue:s.value,"onUpdate:modelValue":e[0]||(e[0]=c=>s.value=c),placeholder:"输入新用户名",disabled:n.value},null,8,["modelValue","disabled"])]),_:1})]),_:1}),a(y,{type:"primary",loading:n.value,onClick:V},{default:l(()=>[...e[2]||(e[2]=[x("保存用户名",-1)])]),_:1},8,["loading"])]),_:1}),a(_,{shadow:"never","body-style":{padding:"16px"},class:"card"},{default:l(()=>[e[5]||(e[5]=r("h3",{class:"section-title"},"修改管理员密码",-1)),a(v,{"label-width":"120px"},{default:l(()=>[a(w,{label:"新密码"},{default:l(()=>[a(g,{modelValue:d.value,"onUpdate:modelValue":e[1]||(e[1]=c=>d.value=c),type:"password","show-password":"",placeholder:"输入新密码",disabled:n.value},null,8,["modelValue","disabled"])]),_:1})]),_:1}),a(y,{type:"primary",loading:n.value,onClick:h},{default:l(()=>[...e[4]||(e[4]=[x("保存密码",-1)])]),_:1},8,["loading"]),e[6]||(e[6]=r("div",{class:"help"},"建议使用更强密码(至少8位且包含字母与数字)。",-1))]),_:1})])}}},M=B(E,[["__scopeId","data-v-12a26d11"]]);export{M as default};
|
||||
1
static/admin/assets/SettingsPage-DGdwb4W2.css
Normal file
1
static/admin/assets/SettingsPage-DGdwb4W2.css
Normal file
@@ -0,0 +1 @@
|
||||
.page-stack[data-v-2f4b840f]{display:flex;flex-direction:column;gap:12px}.card[data-v-2f4b840f]{border-radius:var(--app-radius);border:1px solid var(--app-border)}.section-title[data-v-2f4b840f]{margin:0 0 12px;font-size:14px;font-weight:800}.help[data-v-2f4b840f]{margin-top:10px;font-size:12px;color:var(--app-muted)}
|
||||
@@ -1 +0,0 @@
|
||||
.page-stack[data-v-12a26d11]{display:flex;flex-direction:column;gap:12px}.card[data-v-12a26d11]{border-radius:var(--app-radius);border:1px solid var(--app-border)}.section-title[data-v-12a26d11]{margin:0 0 12px;font-size:14px;font-weight:800}.help[data-v-12a26d11]{margin-top:10px;font-size:12px;color:var(--app-muted)}
|
||||
1
static/admin/assets/SettingsPage-YOW1Apwk.js
Normal file
1
static/admin/assets/SettingsPage-YOW1Apwk.js
Normal file
@@ -0,0 +1 @@
|
||||
import{S as m,_ as T,r as p,e as u,f as h,g as k,h as r,j as a,w as s,p as x,L as i,K as b}from"./index-CdjS44Uj.js";async function C(o){const{data:t}=await m.put("/admin/username",{new_username:o});return t}async function P(o){const{data:t}=await m.put("/admin/password",{new_password:o});return t}async function U(){const{data:o}=await m.post("/logout");return o}const E={class:"page-stack"},N={__name:"SettingsPage",setup(o){const t=p(""),d=p(""),n=p(!1);async function f(){try{await U()}catch{}finally{window.location.href="/yuyx"}}async function V(){const l=t.value.trim();if(!l){i.error("请输入新用户名");return}try{await b.confirm(`确定将管理员用户名修改为「${l}」吗?修改后需要重新登录。`,"修改用户名",{confirmButtonText:"确认修改",cancelButtonText:"取消",type:"warning"})}catch{return}n.value=!0;try{await C(l),i.success("用户名修改成功,请重新登录"),t.value="",setTimeout(f,1200)}catch{}finally{n.value=!1}}async function B(){const l=d.value;if(!l){i.error("请输入新密码");return}if(l.length<6){i.error("密码至少6个字符");return}try{await b.confirm("确定修改管理员密码吗?修改后需要重新登录。","修改密码",{confirmButtonText:"确认修改",cancelButtonText:"取消",type:"warning"})}catch{return}n.value=!0;try{await P(l),i.success("密码修改成功,请重新登录"),d.value="",setTimeout(f,1200)}catch{}finally{n.value=!1}}return(l,e)=>{const w=u("el-input"),v=u("el-form-item"),y=u("el-form"),_=u("el-button"),g=u("el-card");return k(),h("div",E,[e[7]||(e[7]=r("div",{class:"app-page-title"},[r("h2",null,"设置"),r("span",{class:"app-muted"},"管理员账号设置")],-1)),a(g,{shadow:"never","body-style":{padding:"16px"},class:"card"},{default:s(()=>[e[3]||(e[3]=r("h3",{class:"section-title"},"修改管理员用户名",-1)),a(y,{"label-width":"120px"},{default:s(()=>[a(v,{label:"新用户名"},{default:s(()=>[a(w,{modelValue:t.value,"onUpdate:modelValue":e[0]||(e[0]=c=>t.value=c),placeholder:"输入新用户名",disabled:n.value},null,8,["modelValue","disabled"])]),_:1})]),_:1}),a(_,{type:"primary",loading:n.value,onClick:V},{default:s(()=>[...e[2]||(e[2]=[x("保存用户名",-1)])]),_:1},8,["loading"])]),_:1}),a(g,{shadow:"never","body-style":{padding:"16px"},class:"card"},{default:s(()=>[e[5]||(e[5]=r("h3",{class:"section-title"},"修改管理员密码",-1)),a(y,{"label-width":"120px"},{default:s(()=>[a(v,{label:"新密码"},{default:s(()=>[a(w,{modelValue:d.value,"onUpdate:modelValue":e[1]||(e[1]=c=>d.value=c),type:"password","show-password":"",placeholder:"输入新密码",disabled:n.value},null,8,["modelValue","disabled"])]),_:1})]),_:1}),a(_,{type:"primary",loading:n.value,onClick:B},{default:s(()=>[...e[4]||(e[4]=[x("保存密码",-1)])]),_:1},8,["loading"]),e[6]||(e[6]=r("div",{class:"help"},"建议使用更强密码(至少8位且包含字母与数字)。",-1))]),_:1})])}}},A=T(N,[["__scopeId","data-v-2f4b840f"]]);export{A as default};
|
||||
1
static/admin/assets/SystemPage-BjTkcmTG.css
Normal file
1
static/admin/assets/SystemPage-BjTkcmTG.css
Normal file
@@ -0,0 +1 @@
|
||||
.page-stack[data-v-d88590f1]{display:flex;flex-direction:column;gap:12px}.card[data-v-d88590f1]{border-radius:var(--app-radius);border:1px solid var(--app-border)}.section-title[data-v-d88590f1]{margin:0 0 12px;font-size:14px;font-weight:800}.help[data-v-d88590f1]{margin-top:6px;font-size:12px;color:var(--app-muted)}.row-actions[data-v-d88590f1]{display:flex;flex-wrap:wrap;gap:10px}
|
||||
@@ -1 +0,0 @@
|
||||
.page-stack[data-v-b359577d]{display:flex;flex-direction:column;gap:12px}.card[data-v-b359577d]{border-radius:var(--app-radius);border:1px solid var(--app-border)}.section-title[data-v-b359577d]{margin:0 0 12px;font-size:14px;font-weight:800}.kdocs-qr[data-v-b359577d]{display:flex;flex-direction:column;align-items:center;gap:12px}.kdocs-qr img[data-v-b359577d]{width:260px;max-width:100%;border:1px solid var(--app-border);border-radius:8px;padding:8px;background:#fff}.help[data-v-b359577d]{margin-top:6px;font-size:12px;color:var(--app-muted)}.row-actions[data-v-b359577d]{display:flex;flex-wrap:wrap;gap:10px}
|
||||
22
static/admin/assets/SystemPage-DCcH_SAQ.js
Normal file
22
static/admin/assets/SystemPage-DCcH_SAQ.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
||||
.page-stack[data-v-d73d2b82]{display:flex;flex-direction:column;gap:12px}.card[data-v-d73d2b82]{border-radius:var(--app-radius);border:1px solid var(--app-border)}.section-title[data-v-d73d2b82]{margin:0 0 12px;font-size:14px;font-weight:800}.help[data-v-d73d2b82]{margin-top:10px;font-size:12px}.table-wrap[data-v-d73d2b82]{overflow-x:auto}.user-block[data-v-d73d2b82]{display:flex;flex-direction:column;gap:2px}.user-main[data-v-d73d2b82]{display:inline-flex;align-items:center;gap:8px;flex-wrap:wrap}.user-sub[data-v-d73d2b82]{font-size:12px}.vip-sub[data-v-d73d2b82]{font-size:12px;color:#7c3aed}.actions[data-v-d73d2b82]{display:flex;flex-wrap:wrap;gap:8px}
|
||||
File diff suppressed because one or more lines are too long
1
static/admin/assets/UsersPage-CbiPbpuj.css
Normal file
1
static/admin/assets/UsersPage-CbiPbpuj.css
Normal file
@@ -0,0 +1 @@
|
||||
.page-stack[data-v-84b2f73a]{display:flex;flex-direction:column;gap:12px}.card[data-v-84b2f73a]{border-radius:var(--app-radius);border:1px solid var(--app-border)}.section-title[data-v-84b2f73a]{margin:0 0 12px;font-size:14px;font-weight:800}.help[data-v-84b2f73a]{margin-top:10px;font-size:12px}.table-wrap[data-v-84b2f73a]{overflow-x:auto}.user-block[data-v-84b2f73a]{display:flex;flex-direction:column;gap:2px}.user-main[data-v-84b2f73a]{display:inline-flex;align-items:center;gap:8px;flex-wrap:wrap}.user-sub[data-v-84b2f73a]{font-size:12px}.vip-sub[data-v-84b2f73a]{font-size:12px;color:#7c3aed}.actions[data-v-84b2f73a]{display:flex;flex-wrap:wrap;gap:8px}
|
||||
1
static/admin/assets/UsersPage-DhTO_5zp.js
Normal file
1
static/admin/assets/UsersPage-DhTO_5zp.js
Normal file
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
||||
import{a as n}from"./index-DKH_HvPt.js";async function i(){const{data:a}=await n.get("/email/settings");return a}async function e(a){const{data:t}=await n.post("/email/settings",a);return t}async function c(){const{data:a}=await n.get("/email/stats");return a}async function o(a){const{data:t}=await n.get("/email/logs",{params:a});return t}async function l(a){const{data:t}=await n.post("/email/logs/cleanup",{days:a});return t}export{o as a,i as b,l as c,c as f,e as u};
|
||||
import{S as n}from"./index-CdjS44Uj.js";async function i(){const{data:a}=await n.get("/email/settings");return a}async function e(a){const{data:t}=await n.post("/email/settings",a);return t}async function c(){const{data:a}=await n.get("/email/stats");return a}async function o(a){const{data:t}=await n.get("/email/logs",{params:a});return t}async function l(a){const{data:t}=await n.post("/email/logs/cleanup",{days:a});return t}export{o as a,i as b,l as c,c as f,e as u};
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
||||
import{a}from"./index-DKH_HvPt.js";async function s(){const{data:t}=await a.get("/system/config");return t}async function c(t){const{data:e}=await a.post("/system/config",t);return e}async function o(){const{data:t}=await a.post("/schedule/execute",{});return t}export{o as e,s as f,c as u};
|
||||
1
static/admin/assets/tasks-DpslJtm_.js
Normal file
1
static/admin/assets/tasks-DpslJtm_.js
Normal file
@@ -0,0 +1 @@
|
||||
import{S as a}from"./index-CdjS44Uj.js";async function c(){const{data:t}=await a.get("/server/info");return t}async function e(){const{data:t}=await a.get("/docker_stats");return t}async function o(){const{data:t}=await a.get("/task/stats");return t}async function r(){const{data:t}=await a.get("/task/running");return t}async function i(t){const{data:s}=await a.get("/task/logs",{params:t});return s}async function f(t){const{data:s}=await a.post("/task/logs/clear",{days:t});return s}export{r as a,c as b,e as c,i as d,f as e,o as f};
|
||||
@@ -1 +0,0 @@
|
||||
import{a}from"./index-DKH_HvPt.js";async function c(){const{data:t}=await a.get("/server/info");return t}async function e(){const{data:t}=await a.get("/docker_stats");return t}async function o(){const{data:t}=await a.get("/task/stats");return t}async function r(){const{data:t}=await a.get("/task/running");return t}async function i(t){const{data:s}=await a.get("/task/logs",{params:t});return s}async function f(t){const{data:s}=await a.post("/task/logs/clear",{days:t});return s}export{r as a,c as b,e as c,i as d,f as e,o as f};
|
||||
1
static/admin/assets/update-DcFD-YxU.js
Normal file
1
static/admin/assets/update-DcFD-YxU.js
Normal file
@@ -0,0 +1 @@
|
||||
import{S as a}from"./index-CdjS44Uj.js";async function s(){const{data:t}=await a.get("/system/config");return t}async function c(t){const{data:e}=await a.post("/system/config",t);return e}async function u(){const{data:t}=await a.post("/schedule/execute",{});return t}async function o(){const{data:t}=await a.get("/update/status");return t}async function r(){const{data:t}=await a.get("/update/result");return t}async function d(t={}){const{data:e}=await a.get("/update/log",{params:t});return e}async function i(){const{data:t}=await a.post("/update/check",{});return t}async function f(t={}){const{data:e}=await a.post("/update/run",t);return e}export{o as a,r as b,d as c,f as d,u as e,s as f,i as r,c as u};
|
||||
@@ -1 +1 @@
|
||||
import{a as t}from"./index-DKH_HvPt.js";async function n(){const{data:s}=await t.get("/users");return s}async function o(s){const{data:a}=await t.post(`/users/${s}/approve`);return a}async function c(s){const{data:a}=await t.post(`/users/${s}/reject`);return a}async function i(s){const{data:a}=await t.delete(`/users/${s}`);return a}async function u(s,a){const{data:e}=await t.post(`/users/${s}/vip`,{days:a});return e}async function p(s){const{data:a}=await t.delete(`/users/${s}/vip`);return a}async function d(s,a){const{data:e}=await t.post(`/users/${s}/reset_password`,{new_password:a});return e}export{o as a,p as b,d as c,i as d,n as f,c as r,u as s};
|
||||
import{S as t}from"./index-CdjS44Uj.js";async function n(){const{data:s}=await t.get("/users");return s}async function o(s){const{data:a}=await t.post(`/users/${s}/approve`);return a}async function c(s){const{data:a}=await t.post(`/users/${s}/reject`);return a}async function i(s){const{data:a}=await t.delete(`/users/${s}`);return a}async function u(s,a){const{data:e}=await t.post(`/users/${s}/vip`,{days:a});return e}async function p(s){const{data:a}=await t.delete(`/users/${s}/vip`);return a}async function d(s,a){const{data:e}=await t.post(`/users/${s}/reset_password`,{new_password:a});return e}export{o as a,p as b,d as c,i as d,n as f,c as r,u as s};
|
||||
@@ -1,20 +1,24 @@
|
||||
{
|
||||
"_accounts-DOetW5YJ.js": {
|
||||
"file": "assets/accounts-DOetW5YJ.js",
|
||||
"_accounts-BXD0We06.js": {
|
||||
"file": "assets/accounts-BXD0We06.js",
|
||||
"name": "accounts",
|
||||
"imports": [
|
||||
"index.html"
|
||||
]
|
||||
},
|
||||
"_auth-Hh1F6LOW.js": {
|
||||
"file": "assets/auth-Hh1F6LOW.js",
|
||||
"_auth-cf7b3Gq2.js": {
|
||||
"file": "assets/auth-cf7b3Gq2.js",
|
||||
"name": "auth",
|
||||
"imports": [
|
||||
"index.html"
|
||||
]
|
||||
},
|
||||
"_password-7ryi82gE.js": {
|
||||
"file": "assets/password-7ryi82gE.js",
|
||||
"name": "password"
|
||||
},
|
||||
"index.html": {
|
||||
"file": "assets/index-7hTgh8K-.js",
|
||||
"file": "assets/index-DhsLPY8p.js",
|
||||
"name": "index",
|
||||
"src": "index.html",
|
||||
"isEntry": true,
|
||||
@@ -28,68 +32,70 @@
|
||||
"src/pages/ScreenshotsPage.vue"
|
||||
],
|
||||
"css": [
|
||||
"assets/index-BVjJVlht.css"
|
||||
"assets/index-CD3NfpmF.css"
|
||||
]
|
||||
},
|
||||
"src/pages/AccountsPage.vue": {
|
||||
"file": "assets/AccountsPage-D8z2pFq6.js",
|
||||
"file": "assets/AccountsPage-38dq1Ex4.js",
|
||||
"name": "AccountsPage",
|
||||
"src": "src/pages/AccountsPage.vue",
|
||||
"isDynamicEntry": true,
|
||||
"imports": [
|
||||
"_accounts-DOetW5YJ.js",
|
||||
"_accounts-BXD0We06.js",
|
||||
"index.html"
|
||||
],
|
||||
"css": [
|
||||
"assets/AccountsPage-CMc3b3Am.css"
|
||||
"assets/AccountsPage-CkDdMK5Q.css"
|
||||
]
|
||||
},
|
||||
"src/pages/LoginPage.vue": {
|
||||
"file": "assets/LoginPage-CdYvfyH1.js",
|
||||
"file": "assets/LoginPage-B_fgHOTT.js",
|
||||
"name": "LoginPage",
|
||||
"src": "src/pages/LoginPage.vue",
|
||||
"isDynamicEntry": true,
|
||||
"imports": [
|
||||
"index.html",
|
||||
"_auth-Hh1F6LOW.js"
|
||||
"_auth-cf7b3Gq2.js",
|
||||
"_password-7ryi82gE.js"
|
||||
],
|
||||
"css": [
|
||||
"assets/LoginPage-CnwOLKJz.css"
|
||||
"assets/LoginPage-8DI6Rf67.css"
|
||||
]
|
||||
},
|
||||
"src/pages/RegisterPage.vue": {
|
||||
"file": "assets/RegisterPage-9AQGZ_pd.js",
|
||||
"file": "assets/RegisterPage-B_Z92PVI.js",
|
||||
"name": "RegisterPage",
|
||||
"src": "src/pages/RegisterPage.vue",
|
||||
"isDynamicEntry": true,
|
||||
"imports": [
|
||||
"index.html",
|
||||
"_auth-Hh1F6LOW.js"
|
||||
"_auth-cf7b3Gq2.js"
|
||||
],
|
||||
"css": [
|
||||
"assets/RegisterPage-BOcNcW5D.css"
|
||||
"assets/RegisterPage-yylt2w7b.css"
|
||||
]
|
||||
},
|
||||
"src/pages/ResetPasswordPage.vue": {
|
||||
"file": "assets/ResetPasswordPage-Dii_hXu1.js",
|
||||
"file": "assets/ResetPasswordPage-2f8v-5j9.js",
|
||||
"name": "ResetPasswordPage",
|
||||
"src": "src/pages/ResetPasswordPage.vue",
|
||||
"isDynamicEntry": true,
|
||||
"imports": [
|
||||
"index.html",
|
||||
"_auth-Hh1F6LOW.js"
|
||||
"_auth-cf7b3Gq2.js",
|
||||
"_password-7ryi82gE.js"
|
||||
],
|
||||
"css": [
|
||||
"assets/ResetPasswordPage-DybfLMAw.css"
|
||||
]
|
||||
},
|
||||
"src/pages/SchedulesPage.vue": {
|
||||
"file": "assets/SchedulesPage-DH01Lsib.js",
|
||||
"file": "assets/SchedulesPage-VLwHd9Sa.js",
|
||||
"name": "SchedulesPage",
|
||||
"src": "src/pages/SchedulesPage.vue",
|
||||
"isDynamicEntry": true,
|
||||
"imports": [
|
||||
"_accounts-DOetW5YJ.js",
|
||||
"_accounts-BXD0We06.js",
|
||||
"index.html"
|
||||
],
|
||||
"css": [
|
||||
@@ -97,7 +103,7 @@
|
||||
]
|
||||
},
|
||||
"src/pages/ScreenshotsPage.vue": {
|
||||
"file": "assets/ScreenshotsPage-omyYT14c.js",
|
||||
"file": "assets/ScreenshotsPage-Dtd_MXUX.js",
|
||||
"name": "ScreenshotsPage",
|
||||
"src": "src/pages/ScreenshotsPage.vue",
|
||||
"isDynamicEntry": true,
|
||||
@@ -109,7 +115,7 @@
|
||||
]
|
||||
},
|
||||
"src/pages/VerifyResultPage.vue": {
|
||||
"file": "assets/VerifyResultPage-C15J2JVk.js",
|
||||
"file": "assets/VerifyResultPage-8_v-5_kc.js",
|
||||
"name": "VerifyResultPage",
|
||||
"src": "src/pages/VerifyResultPage.vue",
|
||||
"isDynamicEntry": true,
|
||||
|
||||
1
static/app/assets/AccountsPage-38dq1Ex4.js
Normal file
1
static/app/assets/AccountsPage-38dq1Ex4.js
Normal file
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
||||
.page[data-v-f1b86f5d]{display:flex;flex-direction:column;gap:12px}.stat-card[data-v-f1b86f5d],.panel[data-v-f1b86f5d]{border-radius:var(--app-radius);border:1px solid var(--app-border)}.stat-label[data-v-f1b86f5d]{font-size:12px}.stat-value[data-v-f1b86f5d]{margin-top:6px;font-size:22px;font-weight:900;letter-spacing:.2px}.stat-suffix[data-v-f1b86f5d]{margin-left:6px;font-size:12px;font-weight:600}.upgrade-banner[data-v-f1b86f5d]{border-radius:var(--app-radius);border:1px solid var(--app-border)}.upgrade-actions[data-v-f1b86f5d]{margin-top:10px}.panel-head[data-v-f1b86f5d]{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:10px;flex-wrap:wrap}.panel-title[data-v-f1b86f5d]{font-size:16px;font-weight:900}.panel-actions[data-v-f1b86f5d]{display:flex;gap:10px;flex-wrap:wrap;justify-content:flex-end}.toolbar[data-v-f1b86f5d]{display:flex;flex-wrap:wrap;align-items:center;gap:12px;padding:10px;border:1px dashed rgba(17,24,39,.14);border-radius:12px;background:#f6f7fb99}.toolbar-left[data-v-f1b86f5d],.toolbar-middle[data-v-f1b86f5d],.toolbar-right[data-v-f1b86f5d]{display:flex;align-items:center;gap:10px;flex-wrap:wrap}.toolbar-right[data-v-f1b86f5d]{margin-left:auto;justify-content:flex-end}.grid[data-v-f1b86f5d]{display:grid;grid-template-columns:repeat(auto-fill,minmax(320px,1fr));gap:12px;align-items:start}.account-card[data-v-f1b86f5d]{border-radius:14px;border:1px solid var(--app-border)}.card-top[data-v-f1b86f5d]{display:flex;gap:10px}.card-check[data-v-f1b86f5d]{padding-top:2px}.card-main[data-v-f1b86f5d]{min-width:0;flex:1}.card-title[data-v-f1b86f5d]{display:flex;align-items:center;justify-content:space-between;gap:10px}.card-name[data-v-f1b86f5d]{font-size:14px;font-weight:900;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.card-sub[data-v-f1b86f5d]{margin-top:6px;font-size:12px;line-height:1.4;word-break:break-word}.progress[data-v-f1b86f5d]{margin-top:12px}.progress-meta[data-v-f1b86f5d]{margin-top:6px;display:flex;justify-content:space-between;gap:10px;font-size:12px}.card-controls[data-v-f1b86f5d]{margin-top:12px;display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap}.card-buttons[data-v-f1b86f5d]{display:flex;align-items:center;gap:8px;flex-wrap:wrap;justify-content:flex-end}.vip-body[data-v-f1b86f5d]{padding:12px 0 0}.vip-tip[data-v-f1b86f5d]{margin-top:10px;font-size:13px;line-height:1.6}@media(max-width:480px){.grid[data-v-f1b86f5d]{grid-template-columns:1fr}}@media(max-width:768px){.panel-actions[data-v-f1b86f5d]{width:100%;justify-content:flex-end}.toolbar-left[data-v-f1b86f5d],.toolbar-middle[data-v-f1b86f5d],.toolbar-right[data-v-f1b86f5d]{width:100%}.toolbar-right[data-v-f1b86f5d]{margin-left:0;justify-content:flex-end}}
|
||||
1
static/app/assets/AccountsPage-CkDdMK5Q.css
Normal file
1
static/app/assets/AccountsPage-CkDdMK5Q.css
Normal file
@@ -0,0 +1 @@
|
||||
.page[data-v-b4e85160]{display:flex;flex-direction:column;gap:12px}.stat-card[data-v-b4e85160],.panel[data-v-b4e85160]{border-radius:var(--app-radius);border:1px solid var(--app-border)}.stat-label[data-v-b4e85160]{font-size:12px}.stat-value[data-v-b4e85160]{margin-top:6px;font-size:22px;font-weight:900;letter-spacing:.2px}.stat-suffix[data-v-b4e85160]{margin-left:6px;font-size:12px;font-weight:600}.upgrade-banner[data-v-b4e85160]{border-radius:var(--app-radius);border:1px solid var(--app-border)}.upgrade-actions[data-v-b4e85160]{margin-top:10px}.panel-head[data-v-b4e85160]{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:10px;flex-wrap:wrap}.panel-title[data-v-b4e85160]{font-size:16px;font-weight:900}.panel-actions[data-v-b4e85160]{display:flex;gap:10px;flex-wrap:wrap;justify-content:flex-end}.toolbar[data-v-b4e85160]{display:flex;flex-wrap:wrap;align-items:center;gap:12px;padding:10px;border:1px dashed rgba(17,24,39,.14);border-radius:12px;background:#f6f7fb99}.toolbar-left[data-v-b4e85160],.toolbar-middle[data-v-b4e85160],.toolbar-right[data-v-b4e85160]{display:flex;align-items:center;gap:10px;flex-wrap:wrap}.toolbar-right[data-v-b4e85160]{margin-left:auto;justify-content:flex-end}.grid[data-v-b4e85160]{display:grid;grid-template-columns:repeat(auto-fill,minmax(320px,1fr));gap:12px;align-items:start}.account-card[data-v-b4e85160]{border-radius:14px;border:1px solid var(--app-border)}.card-top[data-v-b4e85160]{display:flex;gap:10px}.card-check[data-v-b4e85160]{padding-top:2px}.card-main[data-v-b4e85160]{min-width:0;flex:1}.card-title[data-v-b4e85160]{display:flex;align-items:center;justify-content:space-between;gap:10px}.card-name[data-v-b4e85160]{font-size:14px;font-weight:900;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.card-sub[data-v-b4e85160]{margin-top:6px;font-size:12px;line-height:1.4;word-break:break-word}.progress[data-v-b4e85160]{margin-top:12px}.progress-meta[data-v-b4e85160]{margin-top:6px;display:flex;justify-content:space-between;gap:10px;font-size:12px}.card-controls[data-v-b4e85160]{margin-top:12px;display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap}.card-buttons[data-v-b4e85160]{display:flex;align-items:center;gap:8px;flex-wrap:wrap;justify-content:flex-end}.vip-body[data-v-b4e85160]{padding:12px 0 0}.vip-tip[data-v-b4e85160]{margin-top:10px;font-size:13px;line-height:1.6}@media(max-width:480px){.grid[data-v-b4e85160]{grid-template-columns:1fr}}@media(max-width:768px){.panel-actions[data-v-b4e85160]{width:100%;justify-content:flex-end}.toolbar-left[data-v-b4e85160],.toolbar-middle[data-v-b4e85160],.toolbar-right[data-v-b4e85160]{width:100%}.toolbar-right[data-v-b4e85160]{margin-left:0;justify-content:flex-end}}
|
||||
File diff suppressed because one or more lines are too long
1
static/app/assets/LoginPage-8DI6Rf67.css
Normal file
1
static/app/assets/LoginPage-8DI6Rf67.css
Normal file
@@ -0,0 +1 @@
|
||||
.auth-wrap[data-v-50df591d]{min-height:100vh;display:flex;align-items:center;justify-content:center;padding:20px}.auth-card[data-v-50df591d]{width:100%;max-width:420px;border-radius:var(--app-radius);border:1px solid var(--app-border);box-shadow:var(--app-shadow)}.brand[data-v-50df591d]{margin-bottom:14px}.brand-title[data-v-50df591d]{font-size:18px;font-weight:900}.brand-sub[data-v-50df591d]{margin-top:4px;font-size:12px}.links[data-v-50df591d]{display:flex;align-items:center;justify-content:space-between;gap:10px;margin:2px 0 10px;flex-wrap:wrap}.submit-btn[data-v-50df591d]{width:100%}.foot[data-v-50df591d]{margin-top:14px;display:flex;align-items:center;justify-content:center;gap:6px}.dialog-form[data-v-50df591d]{margin-top:10px}.captcha-row[data-v-50df591d]{display:flex;align-items:center;gap:10px;width:100%}.captcha-img[data-v-50df591d]{height:40px;border:1px solid var(--app-border);border-radius:8px;cursor:pointer;-webkit-user-select:none;user-select:none}@media(max-width:480px){.captcha-img[data-v-50df591d]{height:38px}}
|
||||
1
static/app/assets/LoginPage-B_fgHOTT.js
Normal file
1
static/app/assets/LoginPage-B_fgHOTT.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
||||
.auth-wrap[data-v-9eb557e5]{min-height:100vh;display:flex;align-items:center;justify-content:center;padding:20px}.auth-card[data-v-9eb557e5]{width:100%;max-width:420px;border-radius:var(--app-radius);border:1px solid var(--app-border);box-shadow:var(--app-shadow)}.brand[data-v-9eb557e5]{margin-bottom:14px}.brand-title[data-v-9eb557e5]{font-size:18px;font-weight:900}.brand-sub[data-v-9eb557e5]{margin-top:4px;font-size:12px}.links[data-v-9eb557e5]{display:flex;align-items:center;justify-content:space-between;gap:10px;margin:2px 0 10px;flex-wrap:wrap}.submit-btn[data-v-9eb557e5]{width:100%}.foot[data-v-9eb557e5]{margin-top:14px;display:flex;align-items:center;justify-content:center;gap:6px}.dialog-form[data-v-9eb557e5]{margin-top:10px}.captcha-row[data-v-9eb557e5]{display:flex;align-items:center;gap:10px;width:100%}.captcha-img[data-v-9eb557e5]{height:40px;border:1px solid var(--app-border);border-radius:8px;cursor:pointer;-webkit-user-select:none;user-select:none}@media(max-width:480px){.captcha-img[data-v-9eb557e5]{height:38px}}
|
||||
@@ -1 +0,0 @@
|
||||
import{_ as M,r as j,a as d,c as B,o as A,b as U,d as l,w as o,e as v,u as H,f as b,g as n,h as N,i as E,j as P,t as q,k as S,E as c,v as z}from"./index-7hTgh8K-.js";import{g as F,f as G,b as J}from"./auth-Hh1F6LOW.js";const O={class:"auth-wrap"},Q={class:"hint app-muted"},W={class:"captcha-row"},X=["src"],Y={class:"actions"},Z={__name:"RegisterPage",setup($){const T=H(),a=j({username:"",password:"",confirm_password:"",email:"",captcha:""}),f=d(!1),w=d(""),h=d(""),V=d(!1),t=d(""),_=d(""),k=d(""),K=B(()=>f.value?"邮箱 *":"邮箱(可选)"),R=B(()=>f.value?"必填,用于账号验证":"选填,用于找回密码和接收通知");async function y(){try{const u=await F();h.value=u?.session_id||"",w.value=u?.captcha_image||"",a.captcha=""}catch{h.value="",w.value=""}}async function D(){try{const u=await G();f.value=!!u?.register_verify_enabled}catch{f.value=!1}}function I(){t.value="",_.value="",k.value=""}async function C(){I();const u=a.username.trim(),e=a.password,g=a.confirm_password,s=a.email.trim(),i=a.captcha.trim();if(u.length<3){t.value="用户名至少3个字符",c.error(t.value);return}const p=z(e);if(!p.ok){t.value=p.message||"密码格式不正确",c.error(t.value);return}if(e!==g){t.value="两次输入的密码不一致",c.error(t.value);return}if(f.value&&!s){t.value="请填写邮箱地址用于账号验证",c.error(t.value);return}if(s&&!s.includes("@")){t.value="邮箱格式不正确",c.error(t.value);return}if(!i){t.value="请输入验证码",c.error(t.value);return}V.value=!0;try{const m=await J({username:u,password:e,email:s,captcha_session:h.value,captcha:i});_.value=m?.message||"注册成功",k.value=m?.need_verify?"请检查您的邮箱(包括垃圾邮件文件夹)":"",c.success("注册成功"),a.username="",a.password="",a.confirm_password="",a.email="",a.captcha="",setTimeout(()=>{window.location.href="/login"},3e3)}catch(m){const x=m?.response?.data;t.value=x?.error||"注册失败",c.error(t.value),await y()}finally{V.value=!1}}function L(){T.push("/login")}return A(async()=>{await y(),await D()}),(u,e)=>{const g=v("el-alert"),s=v("el-input"),i=v("el-form-item"),p=v("el-button"),m=v("el-form"),x=v("el-card");return b(),U("div",O,[l(x,{shadow:"never",class:"auth-card","body-style":{padding:"22px"}},{default:o(()=>[e[11]||(e[11]=n("div",{class:"brand"},[n("div",{class:"brand-title"},"知识管理平台"),n("div",{class:"brand-sub app-muted"},"用户注册")],-1)),t.value?(b(),N(g,{key:0,type:"error",closable:!1,title:t.value,"show-icon":"",class:"alert"},null,8,["title"])):E("",!0),_.value?(b(),N(g,{key:1,type:"success",closable:!1,title:_.value,description:k.value,"show-icon":"",class:"alert"},null,8,["title","description"])):E("",!0),l(m,{"label-position":"top"},{default:o(()=>[l(i,{label:"用户名 *"},{default:o(()=>[l(s,{modelValue:a.username,"onUpdate:modelValue":e[0]||(e[0]=r=>a.username=r),placeholder:"至少3个字符",autocomplete:"username"},null,8,["modelValue"]),e[5]||(e[5]=n("div",{class:"hint app-muted"},"至少3个字符",-1))]),_:1}),l(i,{label:"密码 *"},{default:o(()=>[l(s,{modelValue:a.password,"onUpdate:modelValue":e[1]||(e[1]=r=>a.password=r),type:"password","show-password":"",placeholder:"至少8位且包含字母和数字",autocomplete:"new-password"},null,8,["modelValue"]),e[6]||(e[6]=n("div",{class:"hint app-muted"},"至少8位且包含字母和数字",-1))]),_:1}),l(i,{label:"确认密码 *"},{default:o(()=>[l(s,{modelValue:a.confirm_password,"onUpdate:modelValue":e[2]||(e[2]=r=>a.confirm_password=r),type:"password","show-password":"",placeholder:"请再次输入密码",autocomplete:"new-password",onKeyup:P(C,["enter"])},null,8,["modelValue"])]),_:1}),l(i,{label:K.value},{default:o(()=>[l(s,{modelValue:a.email,"onUpdate:modelValue":e[3]||(e[3]=r=>a.email=r),placeholder:"name@example.com",autocomplete:"email"},null,8,["modelValue"]),n("div",Q,q(R.value),1)]),_:1},8,["label"]),l(i,{label:"验证码 *"},{default:o(()=>[n("div",W,[l(s,{modelValue:a.captcha,"onUpdate:modelValue":e[4]||(e[4]=r=>a.captcha=r),placeholder:"请输入验证码",onKeyup:P(C,["enter"])},null,8,["modelValue"]),w.value?(b(),U("img",{key:0,class:"captcha-img",src:w.value,alt:"验证码",title:"点击刷新",onClick:y},null,8,X)):E("",!0),l(p,{onClick:y},{default:o(()=>[...e[7]||(e[7]=[S("刷新",-1)])]),_:1})])]),_:1})]),_:1}),l(p,{type:"primary",class:"submit-btn",loading:V.value,onClick:C},{default:o(()=>[...e[8]||(e[8]=[S("注册",-1)])]),_:1},8,["loading"]),n("div",Y,[e[10]||(e[10]=n("span",{class:"app-muted"},"已有账号?",-1)),l(p,{link:"",type:"primary",onClick:L},{default:o(()=>[...e[9]||(e[9]=[S("立即登录",-1)])]),_:1})])]),_:1})])}}},te=M(Z,[["__scopeId","data-v-a9d7804f"]]);export{te as default};
|
||||
@@ -1 +0,0 @@
|
||||
.auth-wrap[data-v-a9d7804f]{min-height:100vh;display:flex;align-items:center;justify-content:center;padding:20px}.auth-card[data-v-a9d7804f]{width:100%;max-width:420px;border-radius:var(--app-radius);border:1px solid var(--app-border);box-shadow:var(--app-shadow)}.brand[data-v-a9d7804f]{margin-bottom:14px}.brand-title[data-v-a9d7804f]{font-size:18px;font-weight:900}.brand-sub[data-v-a9d7804f]{margin-top:4px;font-size:12px}.alert[data-v-a9d7804f]{margin-bottom:12px}.hint[data-v-a9d7804f]{margin-top:6px;font-size:12px}.captcha-row[data-v-a9d7804f]{display:flex;align-items:center;gap:10px;width:100%}.captcha-img[data-v-a9d7804f]{height:40px;border:1px solid var(--app-border);border-radius:8px;cursor:pointer;-webkit-user-select:none;user-select:none}.submit-btn[data-v-a9d7804f]{width:100%;margin-top:4px}.actions[data-v-a9d7804f]{margin-top:14px;display:flex;align-items:center;justify-content:center;gap:6px}
|
||||
1
static/app/assets/RegisterPage-B_Z92PVI.js
Normal file
1
static/app/assets/RegisterPage-B_Z92PVI.js
Normal file
@@ -0,0 +1 @@
|
||||
import{_ as M,r as j,a as p,c as B,o as A,b as S,d as t,w as o,e as m,u as H,f as g,g as n,h as U,i as x,j as N,t as q,k as E,E as d}from"./index-DhsLPY8p.js";import{g as z,f as F,c as G}from"./auth-cf7b3Gq2.js";const J={class:"auth-wrap"},O={class:"hint app-muted"},Q={class:"captcha-row"},W=["src"],X={class:"actions"},Y={__name:"RegisterPage",setup(Z){const T=H(),a=j({username:"",password:"",confirm_password:"",email:"",captcha:""}),v=p(!1),f=p(""),h=p(""),b=p(!1),l=p(""),_=p(""),V=p(""),K=B(()=>v.value?"邮箱 *":"邮箱(可选)"),P=B(()=>v.value?"必填,用于账号验证":"选填,用于找回密码和接收通知");async function w(){try{const u=await z();h.value=u?.session_id||"",f.value=u?.captcha_image||"",a.captcha=""}catch{h.value="",f.value=""}}async function R(){try{const u=await F();v.value=!!u?.register_verify_enabled}catch{v.value=!1}}function D(){l.value="",_.value="",V.value=""}async function k(){D();const u=a.username.trim(),e=a.password,y=a.confirm_password,s=a.email.trim(),i=a.captcha.trim();if(u.length<3){l.value="用户名至少3个字符",d.error(l.value);return}if(e.length<6){l.value="密码至少6个字符",d.error(l.value);return}if(e!==y){l.value="两次输入的密码不一致",d.error(l.value);return}if(v.value&&!s){l.value="请填写邮箱地址用于账号验证",d.error(l.value);return}if(s&&!s.includes("@")){l.value="邮箱格式不正确",d.error(l.value);return}if(!i){l.value="请输入验证码",d.error(l.value);return}b.value=!0;try{const c=await G({username:u,password:e,email:s,captcha_session:h.value,captcha:i});_.value=c?.message||"注册成功",V.value=c?.need_verify?"请检查您的邮箱(包括垃圾邮件文件夹)":"",d.success("注册成功"),a.username="",a.password="",a.confirm_password="",a.email="",a.captcha="",setTimeout(()=>{window.location.href="/login"},3e3)}catch(c){const C=c?.response?.data;l.value=C?.error||"注册失败",d.error(l.value),await w()}finally{b.value=!1}}function I(){T.push("/login")}return A(async()=>{await w(),await R()}),(u,e)=>{const y=m("el-alert"),s=m("el-input"),i=m("el-form-item"),c=m("el-button"),C=m("el-form"),L=m("el-card");return g(),S("div",J,[t(L,{shadow:"never",class:"auth-card","body-style":{padding:"22px"}},{default:o(()=>[e[11]||(e[11]=n("div",{class:"brand"},[n("div",{class:"brand-title"},"知识管理平台"),n("div",{class:"brand-sub app-muted"},"用户注册")],-1)),l.value?(g(),U(y,{key:0,type:"error",closable:!1,title:l.value,"show-icon":"",class:"alert"},null,8,["title"])):x("",!0),_.value?(g(),U(y,{key:1,type:"success",closable:!1,title:_.value,description:V.value,"show-icon":"",class:"alert"},null,8,["title","description"])):x("",!0),t(C,{"label-position":"top"},{default:o(()=>[t(i,{label:"用户名 *"},{default:o(()=>[t(s,{modelValue:a.username,"onUpdate:modelValue":e[0]||(e[0]=r=>a.username=r),placeholder:"至少3个字符",autocomplete:"username"},null,8,["modelValue"]),e[5]||(e[5]=n("div",{class:"hint app-muted"},"至少3个字符",-1))]),_:1}),t(i,{label:"密码 *"},{default:o(()=>[t(s,{modelValue:a.password,"onUpdate:modelValue":e[1]||(e[1]=r=>a.password=r),type:"password","show-password":"",placeholder:"至少6个字符",autocomplete:"new-password"},null,8,["modelValue"]),e[6]||(e[6]=n("div",{class:"hint app-muted"},"至少6个字符",-1))]),_:1}),t(i,{label:"确认密码 *"},{default:o(()=>[t(s,{modelValue:a.confirm_password,"onUpdate:modelValue":e[2]||(e[2]=r=>a.confirm_password=r),type:"password","show-password":"",placeholder:"请再次输入密码",autocomplete:"new-password",onKeyup:N(k,["enter"])},null,8,["modelValue"])]),_:1}),t(i,{label:K.value},{default:o(()=>[t(s,{modelValue:a.email,"onUpdate:modelValue":e[3]||(e[3]=r=>a.email=r),placeholder:"name@example.com",autocomplete:"email"},null,8,["modelValue"]),n("div",O,q(P.value),1)]),_:1},8,["label"]),t(i,{label:"验证码 *"},{default:o(()=>[n("div",Q,[t(s,{modelValue:a.captcha,"onUpdate:modelValue":e[4]||(e[4]=r=>a.captcha=r),placeholder:"请输入验证码",onKeyup:N(k,["enter"])},null,8,["modelValue"]),f.value?(g(),S("img",{key:0,class:"captcha-img",src:f.value,alt:"验证码",title:"点击刷新",onClick:w},null,8,W)):x("",!0),t(c,{onClick:w},{default:o(()=>[...e[7]||(e[7]=[E("刷新",-1)])]),_:1})])]),_:1})]),_:1}),t(c,{type:"primary",class:"submit-btn",loading:b.value,onClick:k},{default:o(()=>[...e[8]||(e[8]=[E("注册",-1)])]),_:1},8,["loading"]),n("div",X,[e[10]||(e[10]=n("span",{class:"app-muted"},"已有账号?",-1)),t(c,{link:"",type:"primary",onClick:I},{default:o(()=>[...e[9]||(e[9]=[E("立即登录",-1)])]),_:1})])]),_:1})])}}},ae=M(Y,[["__scopeId","data-v-75731a6d"]]);export{ae as default};
|
||||
1
static/app/assets/RegisterPage-yylt2w7b.css
Normal file
1
static/app/assets/RegisterPage-yylt2w7b.css
Normal file
@@ -0,0 +1 @@
|
||||
.auth-wrap[data-v-75731a6d]{min-height:100vh;display:flex;align-items:center;justify-content:center;padding:20px}.auth-card[data-v-75731a6d]{width:100%;max-width:420px;border-radius:var(--app-radius);border:1px solid var(--app-border);box-shadow:var(--app-shadow)}.brand[data-v-75731a6d]{margin-bottom:14px}.brand-title[data-v-75731a6d]{font-size:18px;font-weight:900}.brand-sub[data-v-75731a6d]{margin-top:4px;font-size:12px}.alert[data-v-75731a6d]{margin-bottom:12px}.hint[data-v-75731a6d]{margin-top:6px;font-size:12px}.captcha-row[data-v-75731a6d]{display:flex;align-items:center;gap:10px;width:100%}.captcha-img[data-v-75731a6d]{height:40px;border:1px solid var(--app-border);border-radius:8px;cursor:pointer;-webkit-user-select:none;user-select:none}.submit-btn[data-v-75731a6d]{width:100%;margin-top:4px}.actions[data-v-75731a6d]{margin-top:14px;display:flex;align-items:center;justify-content:center;gap:6px}
|
||||
1
static/app/assets/ResetPasswordPage-2f8v-5j9.js
Normal file
1
static/app/assets/ResetPasswordPage-2f8v-5j9.js
Normal file
@@ -0,0 +1 @@
|
||||
import{_ as L,a as n,l as M,r as U,c as j,o as F,m as K,b as v,d as s,w as a,e as l,u as D,f as m,g as w,F as T,k,h as q,i as x,j as z,t as G,E as y}from"./index-DhsLPY8p.js";import{d as H}from"./auth-cf7b3Gq2.js";import{v as J}from"./password-7ryi82gE.js";const O={class:"auth-wrap"},Q={class:"actions"},W={class:"actions"},X={key:0,class:"app-muted"},Y={__name:"ResetPasswordPage",setup(Z){const B=M(),A=D(),r=n(String(B.params.token||"")),i=n(!0),b=n(""),t=U({newPassword:"",confirmPassword:""}),g=n(!1),f=n(""),d=n(0);let u=null;function C(){if(typeof window>"u")return null;const o=window.__APP_INITIAL_STATE__;return!o||typeof o!="object"?null:(window.__APP_INITIAL_STATE__=null,o)}const I=j(()=>!!(i.value&&r.value&&!f.value));function S(){A.push("/login")}function N(){d.value=3,u=window.setInterval(()=>{d.value-=1,d.value<=0&&(window.clearInterval(u),u=null,window.location.href="/login")},1e3)}async function V(){if(!I.value)return;const o=t.newPassword,e=t.confirmPassword,c=J(o);if(!c.ok){y.error(c.message);return}if(o!==e){y.error("两次输入的密码不一致");return}g.value=!0;try{await H({token:r.value,new_password:o}),f.value="密码重置成功!3秒后跳转到登录页面...",y.success("密码重置成功"),N()}catch(p){const _=p?.response?.data;y.error(_?.error||"重置失败")}finally{g.value=!1}}return F(()=>{const o=C();o?.page==="reset_password"?(r.value=String(o?.token||r.value||""),i.value=!!o?.valid,b.value=o?.error_message||(i.value?"":"重置链接无效或已过期,请重新申请密码重置")):r.value||(i.value=!1,b.value="重置链接无效或已过期,请重新申请密码重置")}),K(()=>{u&&window.clearInterval(u)}),(o,e)=>{const c=l("el-alert"),p=l("el-button"),_=l("el-input"),h=l("el-form-item"),R=l("el-form"),E=l("el-card");return m(),v("div",O,[s(E,{shadow:"never",class:"auth-card","body-style":{padding:"22px"}},{default:a(()=>[e[5]||(e[5]=w("div",{class:"brand"},[w("div",{class:"brand-title"},"知识管理平台"),w("div",{class:"brand-sub app-muted"},"重置密码")],-1)),i.value?(m(),v(T,{key:1},[f.value?(m(),q(c,{key:0,type:"success",closable:!1,title:"重置成功",description:f.value,"show-icon":"",class:"alert"},null,8,["description"])):x("",!0),s(R,{"label-position":"top"},{default:a(()=>[s(h,{label:"新密码(至少8位且包含字母和数字)"},{default:a(()=>[s(_,{modelValue:t.newPassword,"onUpdate:modelValue":e[0]||(e[0]=P=>t.newPassword=P),type:"password","show-password":"",placeholder:"请输入新密码",autocomplete:"new-password"},null,8,["modelValue"])]),_:1}),s(h,{label:"确认密码"},{default:a(()=>[s(_,{modelValue:t.confirmPassword,"onUpdate:modelValue":e[1]||(e[1]=P=>t.confirmPassword=P),type:"password","show-password":"",placeholder:"请再次输入新密码",autocomplete:"new-password",onKeyup:z(V,["enter"])},null,8,["modelValue"])]),_:1})]),_:1}),s(p,{type:"primary",class:"submit-btn",loading:g.value,disabled:!I.value,onClick:V},{default:a(()=>[...e[3]||(e[3]=[k(" 确认重置 ",-1)])]),_:1},8,["loading","disabled"]),w("div",W,[s(p,{link:"",type:"primary",onClick:S},{default:a(()=>[...e[4]||(e[4]=[k("返回登录",-1)])]),_:1}),d.value>0?(m(),v("span",X,G(d.value)+" 秒后自动跳转…",1)):x("",!0)])],64)):(m(),v(T,{key:0},[s(c,{type:"error",closable:!1,title:"链接已失效",description:b.value,"show-icon":""},null,8,["description"]),w("div",Q,[s(p,{type:"primary",onClick:S},{default:a(()=>[...e[2]||(e[2]=[k("返回登录",-1)])]),_:1})])],64))]),_:1})])}}},se=L(Y,[["__scopeId","data-v-0bbb511c"]]);export{se as default};
|
||||
@@ -1 +0,0 @@
|
||||
import{_ as L,a as n,l as M,r as U,c as j,o as F,m as K,b as v,d as s,w as a,e as l,u as D,f as w,g as m,F as T,k,h as q,i as x,j as z,t as G,v as H,E as y}from"./index-7hTgh8K-.js";import{c as J}from"./auth-Hh1F6LOW.js";const O={class:"auth-wrap"},Q={class:"actions"},W={class:"actions"},X={key:0,class:"app-muted"},Y={__name:"ResetPasswordPage",setup(Z){const B=M(),A=D(),r=n(String(B.params.token||"")),i=n(!0),b=n(""),t=U({newPassword:"",confirmPassword:""}),g=n(!1),_=n(""),d=n(0);let u=null;function C(){if(typeof window>"u")return null;const o=window.__APP_INITIAL_STATE__;return!o||typeof o!="object"?null:(window.__APP_INITIAL_STATE__=null,o)}const I=j(()=>!!(i.value&&r.value&&!_.value));function S(){A.push("/login")}function N(){d.value=3,u=window.setInterval(()=>{d.value-=1,d.value<=0&&(window.clearInterval(u),u=null,window.location.href="/login")},1e3)}async function V(){if(!I.value)return;const o=t.newPassword,e=t.confirmPassword,c=H(o);if(!c.ok){y.error(c.message);return}if(o!==e){y.error("两次输入的密码不一致");return}g.value=!0;try{await J({token:r.value,new_password:o}),_.value="密码重置成功!3秒后跳转到登录页面...",y.success("密码重置成功"),N()}catch(p){const f=p?.response?.data;y.error(f?.error||"重置失败")}finally{g.value=!1}}return F(()=>{const o=C();o?.page==="reset_password"?(r.value=String(o?.token||r.value||""),i.value=!!o?.valid,b.value=o?.error_message||(i.value?"":"重置链接无效或已过期,请重新申请密码重置")):r.value||(i.value=!1,b.value="重置链接无效或已过期,请重新申请密码重置")}),K(()=>{u&&window.clearInterval(u)}),(o,e)=>{const c=l("el-alert"),p=l("el-button"),f=l("el-input"),h=l("el-form-item"),R=l("el-form"),E=l("el-card");return w(),v("div",O,[s(E,{shadow:"never",class:"auth-card","body-style":{padding:"22px"}},{default:a(()=>[e[5]||(e[5]=m("div",{class:"brand"},[m("div",{class:"brand-title"},"知识管理平台"),m("div",{class:"brand-sub app-muted"},"重置密码")],-1)),i.value?(w(),v(T,{key:1},[_.value?(w(),q(c,{key:0,type:"success",closable:!1,title:"重置成功",description:_.value,"show-icon":"",class:"alert"},null,8,["description"])):x("",!0),s(R,{"label-position":"top"},{default:a(()=>[s(h,{label:"新密码(至少8位且包含字母和数字)"},{default:a(()=>[s(f,{modelValue:t.newPassword,"onUpdate:modelValue":e[0]||(e[0]=P=>t.newPassword=P),type:"password","show-password":"",placeholder:"请输入新密码",autocomplete:"new-password"},null,8,["modelValue"])]),_:1}),s(h,{label:"确认密码"},{default:a(()=>[s(f,{modelValue:t.confirmPassword,"onUpdate:modelValue":e[1]||(e[1]=P=>t.confirmPassword=P),type:"password","show-password":"",placeholder:"请再次输入新密码",autocomplete:"new-password",onKeyup:z(V,["enter"])},null,8,["modelValue"])]),_:1})]),_:1}),s(p,{type:"primary",class:"submit-btn",loading:g.value,disabled:!I.value,onClick:V},{default:a(()=>[...e[3]||(e[3]=[k(" 确认重置 ",-1)])]),_:1},8,["loading","disabled"]),m("div",W,[s(p,{link:"",type:"primary",onClick:S},{default:a(()=>[...e[4]||(e[4]=[k("返回登录",-1)])]),_:1}),d.value>0?(w(),v("span",X,G(d.value)+" 秒后自动跳转…",1)):x("",!0)])],64)):(w(),v(T,{key:0},[s(c,{type:"error",closable:!1,title:"链接已失效",description:b.value,"show-icon":""},null,8,["description"]),m("div",Q,[s(p,{type:"primary",onClick:S},{default:a(()=>[...e[2]||(e[2]=[k("返回登录",-1)])]),_:1})])],64))]),_:1})])}}},oe=L(Y,[["__scopeId","data-v-0bbb511c"]]);export{oe as default};
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
||||
import{_ as U,a as o,c as I,o as E,m as R,b as k,d as i,w as s,e as d,u as W,f as _,g as l,i as B,h as $,k as T,t as v}from"./index-7hTgh8K-.js";const j={class:"auth-wrap"},z={class:"actions"},D={key:0,class:"countdown app-muted"},M={__name:"VerifyResultPage",setup(q){const x=W(),p=o(!1),f=o(""),m=o(""),w=o(""),y=o(""),r=o(""),u=o(""),c=o(""),n=o(0);let a=null;function C(){if(typeof window>"u")return null;const e=window.__APP_INITIAL_STATE__;return!e||typeof e!="object"?null:(window.__APP_INITIAL_STATE__=null,e)}function N(e){const t=!!e?.success;p.value=t,f.value=e?.title||(t?"验证成功":"验证失败"),m.value=e?.message||e?.error_message||(t?"操作已完成,现在可以继续使用系统。":"操作失败,请稍后重试。"),w.value=e?.primary_label||(t?"立即登录":"重新注册"),y.value=e?.primary_url||(t?"/login":"/register"),r.value=e?.secondary_label||(t?"":"返回登录"),u.value=e?.secondary_url||(t?"":"/login"),c.value=e?.redirect_url||(t?"/login":""),n.value=Number(e?.redirect_seconds||(t?5:0))||0}const A=I(()=>!!(r.value&&u.value)),b=I(()=>!!(c.value&&n.value>0));async function g(e){if(e){if(e.startsWith("http://")||e.startsWith("https://")){window.location.href=e;return}await x.push(e)}}function P(){b.value&&(a=window.setInterval(()=>{n.value-=1,n.value<=0&&(window.clearInterval(a),a=null,window.location.href=c.value)},1e3))}return E(()=>{const e=C();N(e),P()}),R(()=>{a&&window.clearInterval(a)}),(e,t)=>{const h=d("el-button"),V=d("el-result"),L=d("el-card");return _(),k("div",j,[i(L,{shadow:"never",class:"auth-card","body-style":{padding:"22px"}},{default:s(()=>[t[2]||(t[2]=l("div",{class:"brand"},[l("div",{class:"brand-title"},"知识管理平台"),l("div",{class:"brand-sub app-muted"},"验证结果")],-1)),i(V,{icon:p.value?"success":"error",title:f.value,"sub-title":m.value,class:"result"},{extra:s(()=>[l("div",z,[i(h,{type:"primary",onClick:t[0]||(t[0]=S=>g(y.value))},{default:s(()=>[T(v(w.value),1)]),_:1}),A.value?(_(),$(h,{key:0,onClick:t[1]||(t[1]=S=>g(u.value))},{default:s(()=>[T(v(r.value),1)]),_:1})):B("",!0)]),b.value?(_(),k("div",D,v(n.value)+" 秒后自动跳转... ",1)):B("",!0)]),_:1},8,["icon","title","sub-title"])]),_:1})])}}},G=U(M,[["__scopeId","data-v-1fc6b081"]]);export{G as default};
|
||||
import{_ as U,a as o,c as I,o as E,m as R,b as k,d as i,w as s,e as d,u as W,f as _,g as l,i as B,h as $,k as T,t as v}from"./index-DhsLPY8p.js";const j={class:"auth-wrap"},z={class:"actions"},D={key:0,class:"countdown app-muted"},M={__name:"VerifyResultPage",setup(q){const x=W(),p=o(!1),f=o(""),m=o(""),w=o(""),y=o(""),r=o(""),u=o(""),c=o(""),n=o(0);let a=null;function C(){if(typeof window>"u")return null;const e=window.__APP_INITIAL_STATE__;return!e||typeof e!="object"?null:(window.__APP_INITIAL_STATE__=null,e)}function N(e){const t=!!e?.success;p.value=t,f.value=e?.title||(t?"验证成功":"验证失败"),m.value=e?.message||e?.error_message||(t?"操作已完成,现在可以继续使用系统。":"操作失败,请稍后重试。"),w.value=e?.primary_label||(t?"立即登录":"重新注册"),y.value=e?.primary_url||(t?"/login":"/register"),r.value=e?.secondary_label||(t?"":"返回登录"),u.value=e?.secondary_url||(t?"":"/login"),c.value=e?.redirect_url||(t?"/login":""),n.value=Number(e?.redirect_seconds||(t?5:0))||0}const A=I(()=>!!(r.value&&u.value)),b=I(()=>!!(c.value&&n.value>0));async function g(e){if(e){if(e.startsWith("http://")||e.startsWith("https://")){window.location.href=e;return}await x.push(e)}}function P(){b.value&&(a=window.setInterval(()=>{n.value-=1,n.value<=0&&(window.clearInterval(a),a=null,window.location.href=c.value)},1e3))}return E(()=>{const e=C();N(e),P()}),R(()=>{a&&window.clearInterval(a)}),(e,t)=>{const h=d("el-button"),V=d("el-result"),L=d("el-card");return _(),k("div",j,[i(L,{shadow:"never",class:"auth-card","body-style":{padding:"22px"}},{default:s(()=>[t[2]||(t[2]=l("div",{class:"brand"},[l("div",{class:"brand-title"},"知识管理平台"),l("div",{class:"brand-sub app-muted"},"验证结果")],-1)),i(V,{icon:p.value?"success":"error",title:f.value,"sub-title":m.value,class:"result"},{extra:s(()=>[l("div",z,[i(h,{type:"primary",onClick:t[0]||(t[0]=S=>g(y.value))},{default:s(()=>[T(v(w.value),1)]),_:1}),A.value?(_(),$(h,{key:0,onClick:t[1]||(t[1]=S=>g(u.value))},{default:s(()=>[T(v(r.value),1)]),_:1})):B("",!0)]),b.value?(_(),k("div",D,v(n.value)+" 秒后自动跳转... ",1)):B("",!0)]),_:1},8,["icon","title","sub-title"])]),_:1})])}}},G=U(M,[["__scopeId","data-v-1fc6b081"]]);export{G as default};
|
||||
@@ -1 +1 @@
|
||||
import{p as c}from"./index-7hTgh8K-.js";async function o(t={}){const{data:a}=await c.get("/accounts",{params:t});return a}async function u(t){const{data:a}=await c.post("/accounts",t);return a}async function r(t,a){const{data:n}=await c.put(`/accounts/${t}`,a);return n}async function e(t){const{data:a}=await c.delete(`/accounts/${t}`);return a}async function i(t,a){const{data:n}=await c.put(`/accounts/${t}/remark`,a);return n}async function p(t,a){const{data:n}=await c.post(`/accounts/${t}/start`,a);return n}async function d(t){const{data:a}=await c.post(`/accounts/${t}/stop`,{});return a}async function f(t){const{data:a}=await c.post("/accounts/batch/start",t);return a}async function w(t){const{data:a}=await c.post("/accounts/batch/stop",t);return a}async function y(){const{data:t}=await c.post("/accounts/clear",{});return t}async function A(t,a={}){const{data:n}=await c.post(`/accounts/${t}/screenshot`,a);return n}export{w as a,f as b,y as c,d,e,o as f,u as g,i as h,p as s,A as t,r as u};
|
||||
import{p as c}from"./index-DhsLPY8p.js";async function o(t={}){const{data:a}=await c.get("/accounts",{params:t});return a}async function u(t){const{data:a}=await c.post("/accounts",t);return a}async function r(t,a){const{data:n}=await c.put(`/accounts/${t}`,a);return n}async function e(t){const{data:a}=await c.delete(`/accounts/${t}`);return a}async function i(t,a){const{data:n}=await c.put(`/accounts/${t}/remark`,a);return n}async function p(t,a){const{data:n}=await c.post(`/accounts/${t}/start`,a);return n}async function d(t){const{data:a}=await c.post(`/accounts/${t}/stop`,{});return a}async function f(t){const{data:a}=await c.post("/accounts/batch/start",t);return a}async function w(t){const{data:a}=await c.post("/accounts/batch/stop",t);return a}async function y(){const{data:t}=await c.post("/accounts/clear",{});return t}async function A(t,a={}){const{data:n}=await c.post(`/accounts/${t}/screenshot`,a);return n}export{w as a,f as b,y as c,d,e,o as f,u as g,i as h,p as s,A as t,r as u};
|
||||
@@ -1 +0,0 @@
|
||||
import{p as s}from"./index-7hTgh8K-.js";async function r(){const{data:a}=await s.get("/email/verify-status");return a}async function o(){const{data:a}=await s.post("/generate_captcha",{});return a}async function e(a){const{data:t}=await s.post("/login",a);return t}async function i(a){const{data:t}=await s.post("/register",a);return t}async function c(a){const{data:t}=await s.post("/resend-verify-email",a);return t}async function f(a){const{data:t}=await s.post("/forgot-password",a);return t}async function u(a){const{data:t}=await s.post("/reset-password-confirm",a);return t}export{f as a,i as b,u as c,r as f,o as g,e as l,c as r};
|
||||
1
static/app/assets/auth-cf7b3Gq2.js
Normal file
1
static/app/assets/auth-cf7b3Gq2.js
Normal file
@@ -0,0 +1 @@
|
||||
import{p as s}from"./index-DhsLPY8p.js";async function r(){const{data:t}=await s.get("/email/verify-status");return t}async function e(){const{data:t}=await s.post("/generate_captcha",{});return t}async function o(t){const{data:a}=await s.post("/login",t);return a}async function i(t){const{data:a}=await s.post("/register",t);return a}async function c(t){const{data:a}=await s.post("/resend-verify-email",t);return a}async function u(t){const{data:a}=await s.post("/forgot-password",t);return a}async function f(t){const{data:a}=await s.post("/reset_password_request",t);return a}async function d(t){const{data:a}=await s.post("/reset-password-confirm",t);return a}export{u as a,c as b,i as c,d,r as f,e as g,o as l,f as r};
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
static/app/assets/index-CD3NfpmF.css
Normal file
1
static/app/assets/index-CD3NfpmF.css
Normal file
File diff suppressed because one or more lines are too long
30
static/app/assets/index-DhsLPY8p.js
Normal file
30
static/app/assets/index-DhsLPY8p.js
Normal file
File diff suppressed because one or more lines are too long
1
static/app/assets/password-7ryi82gE.js
Normal file
1
static/app/assets/password-7ryi82gE.js
Normal file
@@ -0,0 +1 @@
|
||||
function s(t){const e=String(t||"");return e.length<8?{ok:!1,message:"密码长度至少8位"}:!/[a-zA-Z]/.test(e)||!/\d/.test(e)?{ok:!1,message:"密码必须包含字母和数字"}:{ok:!0,message:""}}export{s as v};
|
||||
Reference in New Issue
Block a user