Compare commits

53 Commits

Author SHA1 Message Date
a6a6198eaa fix(优化) 2026-08-26 14:01:04 +08:00
zxr
9fbfe54f92 Merge branch 'main' of https://git.apinb.com/ops/front 2026-08-24 22:06:19 +08:00
zxr
fb9c5b9255 fix 2026-08-24 22:04:01 +08:00
73247053dd fix(优化) 2026-08-24 22:02:55 +08:00
zxr
695755546f fix: 机柜图形化 2026-08-20 18:06:05 +08:00
zxr
43ce3433ce fix: 机柜使用率浮点精度 2026-08-19 19:27:12 +08:00
zxr
3e60e699ce fix 2026-08-18 11:11:46 +08:00
zxr
5cbfb36fbf fix 2026-08-17 18:28:58 +08:00
zxr
87ec4dff70 fix 2026-08-10 20:55:56 +08:00
zxr
46559fe955 docs: 清理旧文档 2026-08-07 17:36:24 +08:00
zxr
c550369a4b Merge branch 'feature/report-pdf' 2026-08-04 14:23:26 +08:00
zxr
a9c2a499ce feat: 接入资产文件上传服务 2026-08-04 01:02:40 +08:00
zxr
24dd0c17da fix: 修复历史报表交互状态 2026-08-04 01:02:17 +08:00
zxr
913e9e63ad fix: 卸载时终止历史报告状态提交 2026-08-03 22:14:19 +08:00
zxr
3abdc187fb fix: 隔离历史报告列表请求状态 2026-08-03 22:06:35 +08:00
zxr
809dc883f5 feat: 建立历史报告档案中心 2026-08-03 21:52:48 +08:00
zxr
7c505f8169 fix: 补齐报表文件名安全边界 2026-08-03 21:42:30 +08:00
zxr
3654ccb9db fix: 安全处理报表下载文件名 2026-08-03 21:30:36 +08:00
zxr
195aa7ccff feat: 增加报表 PDF 下载 2026-08-03 21:13:07 +08:00
zxr
7353c4eede fix: 资产相关页面微调 2026-08-01 16:24:52 +08:00
zxr
dbda9f76fb refactor: 统一知识库接口公共类型 2026-07-22 04:55:36 +08:00
zxr
c2c5afa82d fix: 避免文档收藏响应串写 2026-07-22 04:19:54 +08:00
zxr
f5c7ebc59c fix: 收紧知识库页面交互状态 2026-07-22 04:01:25 +08:00
zxr
98e1baf1bb feat: 整合知识库管理页面 2026-07-22 03:26:22 +08:00
zxr
51f938fbfc fix: 防止 FAQ 表单重复提交 2026-07-22 02:54:26 +08:00
zxr
e43ada5c7f fix: 修正 FAQ 表单与分类交互 2026-07-22 02:46:50 +08:00
zxr
f2f4390ac2 feat: 增加知识库 FAQ 管理页面 2026-07-22 02:30:50 +08:00
zxr
52a0346c4c fix: 统一前端菜单类型判断 2026-07-22 00:00:19 +08:00
zxr
4b9a87638c feat: 兼容前端按钮权限数据 2026-07-21 23:51:18 +08:00
zxr
b4dba77229 fix: preserve alert configuration intent 2026-07-21 22:15:59 +08:00
zxr
7e61f330dc fix: restrict dynamic rule operators 2026-07-21 21:21:47 +08:00
zxr
8c1772c784 fix: guard alert policy user response types 2026-07-21 16:18:26 +08:00
zxr
c9da54829d fix: 移除告警客户端身份字段 2026-07-21 08:33:33 +08:00
zxr
db767de8bb feat: expose alert suppression configuration 2026-07-21 04:58:48 +08:00
zxr
9cbacf5998 feat: configure static and dynamic alert rules 2026-07-20 21:59:57 +08:00
zxr
947fa2f9b9 fix: 消息展示修正 2026-07-15 13:06:09 +08:00
zxr
e5df404aaa fix: 标签栏图标修正;概览页展示数据切换;消息通知异常显示修复 2026-07-15 12:40:38 +08:00
zxr
8a6ec3ef96 fix: 更换logo及oid自定义弹窗修改 2026-07-14 10:13:20 +08:00
zxr
922f7dfc4b Merge branch 'feat/snmp-oid-table-editor' 2026-07-05 23:30:41 +08:00
zxr
ee101984f0 refactor: reuse snmp oid editor in storage form 2026-07-05 23:06:18 +08:00
zxr
3f4d50b395 refactor: reuse snmp oid editor in security form 2026-07-05 23:04:21 +08:00
zxr
58365870ef refactor: reuse snmp oid editor in device collect form 2026-07-05 23:02:09 +08:00
zxr
aa6db97457 fix: validate snmp oid json field types 2026-07-05 22:18:52 +08:00
zxr
f5d0bae89b feat: add shared snmp oid editor 2026-07-05 22:13:39 +08:00
zxr
7a20329594 fix: validate advanced snmp oid json rows 2026-07-05 18:35:01 +08:00
zxr
83b713890b fix: preserve advanced snmp oid json fields 2026-07-05 18:31:47 +08:00
zxr
c3f38f5e2a fix: preserve snmp oid validation row numbers 2026-07-05 18:22:25 +08:00
zxr
c6d0df6fd2 fix: normalize blank snmp oid json 2026-07-05 18:18:47 +08:00
zxr
93e8f3349f feat: validate snmp oid table rows 2026-07-05 18:15:45 +08:00
zxr
fc5d653433 feat: add snmp oid table editor ui 2026-07-05 18:07:51 +08:00
zxr
fafdb0d830 feat: initialize snmp oid editor state 2026-07-05 18:00:17 +08:00
zxr
99a5f8bd16 fix: trim parsed snmp oid rows 2026-07-05 17:54:23 +08:00
zxr
9f1c3eb660 feat: add snmp oid editor state helpers 2026-07-05 17:48:49 +08:00
218 changed files with 2980298 additions and 4836 deletions

View File

@@ -8,16 +8,17 @@ VITE_APP_TITLE=标准管理系统
VITE_APP_DESCRIPTION="default standard template"
VITE_USE_MOCK=false
# API 基础URL
# VITE_API_BASE_URL=https://ops-api.apinb.com
# 开发环境走 Vite 同源代理,避免依赖本机 80/443 nginx
VITE_API_BASE_URL=
# 开发环境通过 Vite 同源代理访问 API避免浏览器跨域
VITE_API_BASE_URL=/api
VITE_API_PROXY_TARGET=https://ops-api.apinb.com
# 海康 WebSDK 开发代理(按实际摄像头/NVR 地址填写,不要提交账号密码)
VITE_HIKVISION_PROXY_TARGET=http://192.168.1.101:80
VITE_HIKVISION_WS_PROXY_TARGET=
# Logs 本地调试地址(仅 logs 模块使用)
VITE_LOGS_API_BASE_URL=http://127.0.0.1:12440
# 应用版本
VITE_APP_VERSION=1.0.0

File diff suppressed because it is too large Load Diff

View File

@@ -1,203 +0,0 @@
# 官网首页和帮助中心页面说明
## 新增页面
本次为系统添加了两个全新的对外页面,无需登录即可访问:
### 1. 官网首页 (`/home`)
**功能特点:**
- 🎨 现代化的渐变背景设计
- 📱 响应式布局,适配各种设备
- 🎯 清晰的功能特性展示区域
- 📊 数据中心关键指标展示
- 🔗 快速导航到登录系统和帮助中心
- 💬 完整的页脚信息
**主要板块:**
1. **顶部导航栏** - 包含 Logo、导航链接和登录按钮
2. **Banner 区域** - 醒目的标题和行动号召按钮
3. **功能特性** - 6 个核心功能卡片展示
4. **数据中心** - 关键运行指标统计
5. **行动号召** - 引导用户登录或查看帮助
6. **页脚** - 版权信息和快速链接
**访问方式:**
- 直接访问:`http://your-domain/#/home`
- 根路径自动重定向到首页
### 2. 帮助中心页面 (`/help`)
**功能特点:**
- 🔍 文档搜索功能
- 📑 左侧分类导航菜单
- ❓ 常见问题 FAQ折叠面板
- 📖 使用指南卡片
- 📥 操作手册下载
- 📞 联系方式展示
**主要内容:**
1. **常见问题** - 8 个典型问题的问答
2. **使用指南** - 6 个分类的详细教程
- 快速入门
- 仪表盘
- 告警管理
- 数据中心
- 报表分析
- 系统设置
3. **操作手册** - 可下载的文档资料
4. **联系我们** - 电话、邮件、在线客服
**访问方式:**
- 直接访问:`http://your-domain/#/help`
- 从首页导航点击"帮助中心"
## 技术实现
### 文件结构
```
src/
├── views/
│ ├── home/ # 官网首页
│ │ └── index.vue
│ └── help/ # 帮助中心
│ └── index.vue
├── layout/
│ └── standalone-layout.vue # 独立布局(无侧边栏)
└── router/
├── index.ts # 路由配置
└── constants.ts # 路由常量(白名单)
```
### 路由配置
```typescript
// 根路径配置
{
path: '/',
name: 'root',
component: () => import('@/layout/standalone-layout.vue'),
redirect: '/home',
children: [
{
path: '/home',
name: 'Home',
component: () => import('@/views/home/index.vue'),
meta: { requiresAuth: false }
},
{
path: '/help',
name: 'Help',
component: () => import('@/views/help/index.vue'),
meta: { requiresAuth: false }
}
]
}
```
### 权限控制
- 两个页面都设置了 `requiresAuth: false`,无需登录即可访问
- 已添加到路由白名单 `WHITE_LIST`
- 使用独立的 `standalone-layout`,不显示系统的导航栏和侧边栏
## 自定义内容
### 修改公司信息
在两个页面的页脚部分,可以修改:
- 版权信息
- 联系方式(电话、邮箱)
- 公司地址
### 添加更多帮助文档
编辑 `help/index.vue` 中的数据:
```typescript
// 添加 FAQ
const faqs = ref([
{
question: '您的问题',
answer: '您的答案',
category: '分类',
},
])
// 添加使用指南
const guides = ref([
{
id: 1,
title: '指南标题',
description: '描述',
icon: '图标',
category: '分类',
categoryName: '分类名称',
updatedAt: '更新日期',
},
])
```
### 修改配色方案
页面使用了 Arco Design 的设计令牌Design Tokens可以通过修改主题变量来调整配色
```less
// 主要颜色
rgb(var(--primary-6)) // 主色调
rgb(var(--success-6)) // 成功色
rgb(var(--warning-6)) // 警告色
```
## 登录页面优化
在登录页面右上角添加了"访问官网首页"按钮,方便用户在登录前了解系统功能。
## 下一步建议
1. **添加真实数据** - 将演示数据替换为实际的帮助文档
2. **集成客服系统** - 接入第三方在线客服工具
3. **添加视频教学** - 在帮助中心增加视频教程
4. **SEO 优化** - 添加 meta 标签、title 等 SEO 元素
5. **多语言支持** - 如果需要,可以添加国际化支持
6. **访问统计** - 集成网站分析工具如百度统计、Google Analytics
## 运行项目
```bash
# 安装依赖
pnpm install
# 启动开发服务器
pnpm dev
# 构建生产版本
pnpm build
```
## 浏览器兼容性
- ✅ Chrome (推荐)
- ✅ Firefox
- ✅ Edge
- ✅ Safari
- ⚠️ IE11需要额外 polyfill
## 注意事项
1. 确保 logo 文件路径正确(`@/assets/logo.svg`
2. 如果部署在子目录,需要调整路由的 base 配置
3. 如需启用 SSR 或 SEO建议使用 Vue Router 的 history 模式
4. 生产环境部署时,确保服务器正确处理 hash 路由
---
如有任何问题或需要进一步的技术支持,请联系开发团队。

View File

@@ -1,89 +0,0 @@
# 快速启动指南
## 🎉 新增功能
已成功为您的智能运维管理系统添加了两个全新的对外页面:
### ✅ 已完成的工作
1. **官网首页** (`/#/home`)
- 现代化的 UI 设计
- 功能特性展示6 个核心功能)
- 数据中心关键指标
- 响应式布局
2. **帮助中心** (`/#/help`)
- 常见问题 FAQ
- 使用指南分类
- 操作手册下载
- 联系方式展示
3. **登录页面优化**
- 添加"访问官网首页"按钮
4. **路由配置**
- 无需登录即可访问
- 独立布局(无侧边栏)
- 根路径自动重定向到首页
## 🚀 立即查看效果
### 方式一:直接访问 URL
在浏览器中访问:
```
http://localhost:5173/#/home
http://localhost:5173/#/help
```
### 方式二:从登录页跳转
访问登录页面,点击右上角的"首页"图标即可跳转到官网首页。
## 📝 下一步自定义建议
### 1. 修改 Logo
替换 `src/assets/logo.svg` 文件为您公司的 logo
### 2. 更新联系信息
编辑以下文件中的联系方式:
- `src/views/home/index.vue` - 页脚联系方式
- `src/views/help/index.vue` - 联系我们板块
### 3. 添加真实的帮助文档
编辑 `src/views/help/index.vue` 中的:
```typescript
const faqs = ref([...]) // 常见问题
const guides = ref([...]) // 使用指南
const manuals = ref([...]) // 操作手册
```
### 4. 调整配色方案
页面使用了 Arco Design 主题色,可以通过 CSS 变量调整:
```less
rgb(var(--primary-6)) // 主色调
```
## 💡 提示
- 所有页面都已经适配移动端,可以在手机上查看效果
- 页面使用了 Vue Router 的 hash 模式,部署简单
- 无需后端支持,纯静态页面,可直接部署
## 📦 技术栈
- Vue 3 + TypeScript
- Arco Design 组件库
- Vite 构建工具
- Vue Router 路由管理
---
享受您的新页面!🎊

View File

@@ -0,0 +1,376 @@
# 资产设备源地址前端对接文档
## 1. 变更说明
设备资产新增可选字段 `source_address`,界面名称为“源地址”。该字段适用于所有设备分类,可用于保存 RTSP、HTTP、HTTPS、HLS 等形式的播放源地址。
| 属性 | 内容 |
| --- | --- |
| JSON 字段 | `source_address` |
| TypeScript 类型 | `string` |
| 是否必填 | 否 |
| 最大长度 | 1000 个字符 |
| 协议限制 | 无 |
| 数据库字段 | `assets_assets.source_address` |
| 数据库类型 | `varchar(1000)` |
前端不需要根据设备分类判断是否展示该字段。
## 2. 接口公共约定
资产接口基础路径:
```text
/Assets/v1/asset
```
请求头:
```http
Authorization: Bearer <token>
Content-Type: application/json
```
统一响应结构:
```ts
interface ApiResponse<T> {
code: number
message: string
details: T
timeseq: number
}
```
`code === 0` 表示请求成功,业务数据位于 `details`
## 3. 前端类型
在资产表单类型中增加 `source_address`
```ts
export interface AssetForm {
id?: number
asset_name: string
asset_code: string
source_address?: string
}
```
当前项目已经在 `src/api/ops/asset.ts``AssetForm` 中加入该字段。其他以资产对象为基础的详情、列表和批量导入数据可以直接复用该字段。
## 4. 创建设备资产
### 4.1 接口
```http
POST /Assets/v1/asset/create
```
### 4.2 请求示例
```json
{
"asset_name": "东门监控摄像头",
"asset_code": "CAMERA-001",
"category_id": 12,
"model": "DS-2CD3T",
"manufacturer": "海康威视",
"serial_number": "SN20260817001",
"source_address": "rtsp://192.168.1.100:554/Streaming/Channels/101",
"status": "in_use"
}
```
未填写源地址时可以省略 `source_address`,也可以传空字符串:
```json
{
"asset_name": "普通设备",
"asset_code": "DEVICE-001",
"source_address": ""
}
```
### 4.3 成功响应
```json
{
"code": 0,
"message": "",
"details": {
"id": 101,
"asset_name": "东门监控摄像头",
"asset_code": "CAMERA-001",
"source_address": "rtsp://192.168.1.100:554/Streaming/Channels/101",
"status": "in_use"
},
"timeseq": 1786932000000
}
```
### 4.4 调用方式
```ts
import { createAsset, type AssetForm } from '@/api/ops/asset'
const data: AssetForm = {
asset_name: '东门监控摄像头',
asset_code: 'CAMERA-001',
source_address: 'rtsp://192.168.1.100:554/Streaming/Channels/101',
status: 'in_use',
}
const response = await createAsset(data)
if (response.code !== 0) {
throw new Error(response.message || '创建设备失败')
}
```
## 5. 更新设备资产
### 5.1 接口
```http
PUT /Assets/v1/asset/update
```
### 5.2 请求示例
```json
{
"id": 101,
"asset_name": "东门监控摄像头",
"asset_code": "CAMERA-001",
"source_address": "https://video.example.com/live/camera-001.m3u8",
"status": "in_use"
}
```
更新接口需要同时传入:
- `id`
- `asset_name`
- `asset_code`
`source_address` 仍为选填字段。传入非空值时更新源地址;不传时保留原值。
当前后端使用 GORM 结构体更新,空字符串属于零值,因此传入 `source_address: ""` 不会清空已有源地址。前端暂时不要把“传空字符串”作为清空操作。
### 5.3 调用方式
```ts
import { updateAsset, type AssetForm } from '@/api/ops/asset'
const data: AssetForm = {
id: 101,
asset_name: '东门监控摄像头',
asset_code: 'CAMERA-001',
source_address: 'https://video.example.com/live/camera-001.m3u8',
status: 'in_use',
}
const response = await updateAsset(data)
if (response.code !== 0) {
throw new Error(response.message || '更新设备失败')
}
```
## 6. 查询设备详情
### 6.1 接口
```http
GET /Assets/v1/asset/detail/{id}
```
### 6.2 成功响应
```json
{
"code": 0,
"message": "",
"details": {
"id": 101,
"asset_name": "东门监控摄像头",
"asset_code": "CAMERA-001",
"source_address": "rtsp://192.168.1.100:554/Streaming/Channels/101",
"status": "in_use"
},
"timeseq": 1786932000000
}
```
未填写源地址时,`source_address` 返回空字符串。
### 6.3 表单回填
当前编辑页面使用详情对象整体回填:
```ts
const response = await fetchAssetDetail(deviceId)
if (response.code === 0) {
form.value = { ...response.details } as AssetForm
}
```
`source_address` 会随其他资产字段自动回填,不需要单独请求。
## 7. 分页查询设备资产
### 7.1 接口
```http
POST /Assets/v1/asset/list
```
### 7.2 请求示例
```json
{
"page": 1,
"page_size": 20,
"keyword": "",
"status": "in_use"
}
```
### 7.3 响应示例
```json
{
"code": 0,
"message": "",
"details": {
"total": 1,
"page": 1,
"page_size": 20,
"data": [
{
"id": 101,
"asset_name": "东门监控摄像头",
"asset_code": "CAMERA-001",
"source_address": "rtsp://192.168.1.100:554/Streaming/Channels/101"
}
]
},
"timeseq": 1786932000000
}
```
以下接口返回的资产对象同样包含 `source_address`
```http
GET /Assets/v1/asset/page
GET /Assets/v1/asset/all
GET /Assets/v1/asset/export
```
## 8. 批量导入
### 8.1 接口
```http
POST /Assets/v1/asset/bulk_import
```
### 8.2 请求示例
```json
{
"dry_run": false,
"items": [
{
"asset_name": "西门监控摄像头",
"asset_code": "CAMERA-002",
"source_address": "rtsp://192.168.1.101:554/live",
"status": "in_use"
}
]
}
```
每个 `items` 元素都可以单独填写或省略 `source_address`
## 9. 表单接入
### 9.1 默认值
```ts
const form = ref<AssetForm>({
asset_name: '',
asset_code: '',
source_address: '',
})
```
### 9.2 表单组件
```vue
<a-form-item label="源地址" field="source_address">
<a-input
v-model="form.source_address"
placeholder="请输入源地址"
:max-length="1000"
allow-clear
/>
</a-form-item>
```
该字段不设置 `required` 规则,也不校验具体协议格式。
### 9.3 提交
新增和编辑直接复用现有资产提交逻辑:
```ts
const data = { ...form.value }
if (isEdit.value && deviceId.value) {
await updateAsset({ ...data, id: deviceId.value })
} else {
await createAsset(data)
}
```
## 10. 详情展示
```vue
<a-descriptions-item label="源地址" :span="2">
<span class="source-address">{{ device?.source_address || '-' }}</span>
</a-descriptions-item>
```
长地址需要允许换行:
```css
.source-address {
word-break: break-all;
}
```
## 11. 播放功能使用建议
获取播放地址时直接读取资产对象的 `source_address`
```ts
const sourceAddress = record.source_address?.trim()
if (!sourceAddress) {
Message.warning('该设备未配置源地址')
return
}
```
前端不要自动补全协议、转换大小写或修改地址内容。具体播放器需要支持对应的流媒体协议,例如浏览器不能直接原生播放 RTSP 地址。
## 12. 对接检查项
- 新增设备时可以不填写源地址。
- 新增设备时填写源地址后可以正常保存。
- 编辑设备时能够正确回填源地址。
- 编辑设备时修改源地址后可以正常保存。
- 详情接口和详情弹窗能够显示源地址。
- 列表接口返回的每条资产数据包含 `source_address`
- 超长地址能够在详情区域正常换行。
- 接口失败时优先显示响应中的 `message`

View File

@@ -1,54 +1,132 @@
import { mergeConfig } from 'vite'
import type { IncomingMessage } from 'node:http'
import { loadEnv, mergeConfig, type ConfigEnv, type ProxyOptions } from 'vite'
// import eslint from 'vite-plugin-eslint'
import baseConfig from './vite.config.base'
const HIKVISION_PROXY_TIMEOUT = 8000
const proxyTarget = (port: number) => ({
target: `http://127.0.0.1:${port}`,
changeOrigin: true,
})
export default mergeConfig(
{
mode: 'development',
server: {
open: true,
host: '0.0.0.0',
fs: {
strict: true,
},
proxy: {
'/rbac2': proxyTarget(10001),
'/Alert': proxyTarget(12427),
'/alert': proxyTarget(12427),
'/DC-Control': proxyTarget(3031),
'/dc-control': proxyTarget(3031),
'/dc-network': proxyTarget(12429),
'/DC-Hardware': proxyTarget(12450),
'/dc-hardware': proxyTarget(12450),
'/dc-host': proxyTarget(9030),
'/dc-middleware': proxyTarget(12428),
'/dc-database': proxyTarget(12580),
'/Feedback': proxyTarget(12432),
'/feedback': proxyTarget(12432),
'/Assets': proxyTarget(12430),
'/assets': proxyTarget(12430),
'/Logs': proxyTarget(12440),
'/logs': proxyTarget(12440),
'/Kb': proxyTarget(12434),
'/kb': proxyTarget(12434),
'/Mgt': proxyTarget(12436),
'/mgt': proxyTarget(12436),
'/Visual': proxyTarget(12438),
'/visual': proxyTarget(12438),
},
/** 读取海康 WebSDK 写入的代理目标 Cookie。 */
function getProxyCookie(request: IncomingMessage, name: string): string {
const cookie = request.headers.cookie || ''
const matched = cookie.match(new RegExp(`(?:^|;\\s*)${name}=([^;]+)`))
if (!matched) return ''
try {
return decodeURIComponent(matched[1])
} catch (_error) {
return ''
}
}
/** 从 SDK Cookie 解析 WebSocket 端口,并限制只能代理到已配置的录像机。 */
function resolveHikvisionWebSocketTarget(request: IncomingMessage, allowedHost: string, fallbackTarget: string): string {
const secureAddress = getProxyCookie(request, 'webVideoCtrlProxyWss')
const address = secureAddress || getProxyCookie(request, 'webVideoCtrlProxyWs')
if (!address) return fallbackTarget
try {
const target = new URL(`${secureAddress ? 'wss' : 'ws'}://${address}`)
return target.hostname.toLowerCase() === allowedHost.toLowerCase() ? target.toString() : fallbackTarget
} catch (_error) {
return fallbackTarget
}
}
/** 创建开发环境 Vite 配置。 */
export default ({ mode }: ConfigEnv) => {
const env = loadEnv(mode, process.cwd(), '')
const apiProxyTarget = env.VITE_API_PROXY_TARGET || 'https://ops-api.apinb.com'
const proxy: Record<string, ProxyOptions> = {
'/api': {
target: apiProxyTarget,
changeOrigin: true,
ws: true,
rewrite: (path) => path.replace(/^\/api/, ''),
},
plugins: [
// eslint({
// cache: false,
// include: ['src/**/*.ts', 'src/**/*.tsx', 'src/**/*.vue'],
// exclude: ['node_modules'],
// }),
],
},
baseConfig
)
'/rbac2': proxyTarget(10001),
'/Alert': proxyTarget(12427),
'/alert': proxyTarget(12427),
'/DC-Control': proxyTarget(3031),
'/dc-control': proxyTarget(3031),
'/dc-network': proxyTarget(12429),
'/DC-Hardware': proxyTarget(12450),
'/dc-hardware': proxyTarget(12450),
'/dc-host': proxyTarget(9030),
'/dc-middleware': proxyTarget(12428),
'/dc-database': proxyTarget(12580),
'/Feedback': proxyTarget(12432),
'/feedback': proxyTarget(12432),
'/Assets': proxyTarget(12430),
'/assets': proxyTarget(12430),
'/Logs': proxyTarget(12440),
'/logs': proxyTarget(12440),
'/Kb': proxyTarget(12434),
'/kb': proxyTarget(12434),
'/Mgt': proxyTarget(12436),
'/mgt': proxyTarget(12436),
'/Visual': proxyTarget(12438),
'/visual': proxyTarget(12438),
}
if (env.VITE_HIKVISION_PROXY_TARGET) {
const hikvisionHttpTarget = new URL(env.VITE_HIKVISION_PROXY_TARGET)
const hikvisionWebSocketTarget =
env.VITE_HIKVISION_WS_PROXY_TARGET || `${hikvisionHttpTarget.protocol === 'https:' ? 'wss' : 'ws'}://${hikvisionHttpTarget.host}`
proxy['/ISAPI'] = {
target: env.VITE_HIKVISION_PROXY_TARGET,
changeOrigin: true,
secure: false,
timeout: HIKVISION_PROXY_TIMEOUT,
proxyTimeout: HIKVISION_PROXY_TIMEOUT,
}
proxy['/SDK'] = {
target: env.VITE_HIKVISION_PROXY_TARGET,
changeOrigin: true,
secure: false,
timeout: HIKVISION_PROXY_TIMEOUT,
proxyTimeout: HIKVISION_PROXY_TIMEOUT,
}
proxy['/webSocketVideoCtrlProxy'] = {
target: hikvisionWebSocketTarget,
changeOrigin: true,
secure: false,
ws: true,
timeout: HIKVISION_PROXY_TIMEOUT,
proxyTimeout: HIKVISION_PROXY_TIMEOUT,
router: env.VITE_HIKVISION_WS_PROXY_TARGET
? undefined
: (request) => resolveHikvisionWebSocketTarget(request, hikvisionHttpTarget.hostname, hikvisionWebSocketTarget),
rewrite: (path) => path.replace(/^\/webSocketVideoCtrlProxy/, '/'),
}
}
return mergeConfig(
{
mode: 'development',
server: {
open: true,
host: '0.0.0.0',
headers: {
'Cross-Origin-Embedder-Policy': 'require-corp',
'Cross-Origin-Opener-Policy': 'same-origin',
'Cross-Origin-Resource-Policy': 'cross-origin',
},
fs: {
strict: true,
},
proxy,
},
plugins: [
// eslint({
// cache: false,
// include: ['src/**/*.ts', 'src/**/*.tsx', 'src/**/*.vue'],
// exclude: ['node_modules'],
// }),
],
},
baseConfig
)
}

24
dev/components.d.ts vendored Normal file
View File

@@ -0,0 +1,24 @@
/* eslint-disable */
// @ts-nocheck
// biome-ignore lint: disable
// oxlint-disable
// ------
// Generated by unplugin-vue-components
// Read more: https://github.com/vuejs/core/pull/3399
import { GlobalComponents } from 'vue'
export {}
/* prettier-ignore */
declare module 'vue' {
export interface GlobalComponents {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}
// For TSX support
declare global {
const RouterLink: typeof import('vue-router')['RouterLink']
const RouterView: typeof import('vue-router')['RouterView']
}

View File

@@ -2,7 +2,7 @@
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<link rel="shortcut icon" type="image/x-icon" href="https://unpkg.byted-static.com/latest/byted/arco-config/assets/favicon.ico" />
<link rel="icon" type="image/svg+xml" href="%BASE_URL%favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>智能运维管理系统</title>
</head>

15673
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -19,6 +19,7 @@
"dependencies": {
"@arco-design/web-vue": "^2.57.0",
"@tabler/icons-vue": "^3.40.0",
"@tweenjs/tween.js": "18.6.0",
"@vue-flow/background": "^1.3.2",
"@vue-flow/controls": "^1.1.3",
"@vue-flow/core": "^1.48.2",
@@ -33,6 +34,7 @@
"pinia": "^3.0.4",
"query-string": "^9.3.1",
"sortablejs": "^1.15.7",
"three": "0.115.0",
"uuid": "^13.0.0",
"vue": "^3.5.29",
"vue-echarts": "^8.0.1",
@@ -72,7 +74,6 @@
"rollup": "^4.59.0",
"rollup-plugin-visualizer": "^7.0.1",
"stylelint": "^17.4.0",
"stylelint-config-prettier": "^9.0.5",
"stylelint-config-rational-order": "^0.1.2",
"stylelint-config-recommended-vue": "^1.6.1",
"stylelint-config-standard": "^40.0.0",

30
pnpm-lock.yaml generated
View File

@@ -13,6 +13,9 @@ importers:
'@tabler/icons-vue':
specifier: ^3.40.0
version: 3.40.0(vue@3.5.29(typescript@5.9.3))
'@tweenjs/tween.js':
specifier: 18.6.0
version: 18.6.0
'@vue-flow/background':
specifier: ^1.3.2
version: 1.3.2(@vue-flow/core@1.48.2(vue@3.5.29(typescript@5.9.3)))(vue@3.5.29(typescript@5.9.3))
@@ -55,6 +58,9 @@ importers:
sortablejs:
specifier: ^1.15.7
version: 1.15.7
three:
specifier: 0.115.0
version: 0.115.0
uuid:
specifier: ^13.0.0
version: 13.0.0
@@ -167,9 +173,6 @@ importers:
stylelint:
specifier: ^17.4.0
version: 17.4.0(typescript@5.9.3)
stylelint-config-prettier:
specifier: ^9.0.5
version: 9.0.5(stylelint@17.4.0(typescript@5.9.3))
stylelint-config-rational-order:
specifier: ^0.1.2
version: 0.1.2
@@ -205,6 +208,9 @@ importers:
version: 3.2.5(typescript@5.9.3)
packages:
'@tweenjs/tween.js@18.6.0':
resolution: { integrity: sha512-z45HU0G0e/SenbvGdAlTpUR5Hur5zwZXQcqfI+f7EnVHdeb2oMI2rQghEePu7uXuvBC0nuKWG5YtZ1nWbuvqzQ== }
'@arco-design/color@0.4.0':
resolution: { integrity: sha512-s7p9MSwJgHeL8DwcATaXvWT3m2SigKpxx4JA1BGPHL4gfvaQsmQfrLBDpjOJFJuJ2jG2dMt3R3P8Pm9E65q18g== }
@@ -3968,13 +3974,6 @@ packages:
postcss-html: ^1.0.0
stylelint: '>=14.0.0'
stylelint-config-prettier@9.0.5:
resolution: { integrity: sha512-U44lELgLZhbAD/xy/vncZ2Pq8sh2TnpiPvo38Ifg9+zeioR+LAkHu0i6YORIOxFafZoVg0xqQwex6e6F25S5XA== }
engines: { node: '>= 12' }
hasBin: true
peerDependencies:
stylelint: '>= 11.x < 15'
stylelint-config-rational-order@0.1.2:
resolution: { integrity: sha512-Qo7ZQaihCwTqijfZg4sbdQQHtugOX/B1/fYh018EiDZHW+lkqH9uHOnsDwDPGZrYJuB6CoyI7MZh2ecw2dOkew== }
@@ -4110,6 +4109,9 @@ packages:
trough@1.0.5:
resolution: { integrity: sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA== }
three@0.115.0:
resolution: { integrity: sha512-mAV2Ky3RdcbdSbR9capI+tKLvRldWYxd4151PZTT/o7+U2jh9Is3a4KmnYwzyUAhB2ZA3pXSgCd2DOY4Tj5kow== }
ts-api-utils@2.4.0:
resolution: { integrity: sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA== }
engines: { node: '>=18.12' }
@@ -4502,6 +4504,8 @@ packages:
resolution: { integrity: sha512-41dFXEEXuJpNecuUQq6JlbybmnHaqqpGlbH1yxnA5V9MMP4SbohSVZsJIwz+zdjQXSSlR1Vc34EgH1zxyTDvhg== }
snapshots:
'@tweenjs/tween.js@18.6.0': {}
'@arco-design/color@0.4.0':
dependencies:
color: 3.2.1
@@ -8473,10 +8477,6 @@ snapshots:
postcss-html: 1.8.1
stylelint: 17.4.0(typescript@5.9.3)
stylelint-config-prettier@9.0.5(stylelint@17.4.0(typescript@5.9.3)):
dependencies:
stylelint: 17.4.0(typescript@5.9.3)
stylelint-config-rational-order@0.1.2:
dependencies:
stylelint: 9.10.1
@@ -8667,6 +8667,8 @@ snapshots:
text-table@0.2.0: {}
three@0.115.0: {}
tiny-emitter@2.1.0: {}
tinyexec@1.0.2: {}

4
public/favicon.svg Normal file
View File

@@ -0,0 +1,4 @@
<svg class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" width="33" height="33">
<path d="M0 240.941176a240.941176 240.941176 0 0 1 240.941176-240.941176h542.117648a240.941176 240.941176 0 0 1 240.941176 240.941176v542.117648a240.941176 240.941176 0 0 1-240.941176 240.941176H240.941176a240.941176 240.941176 0 0 1-240.941176-240.941176V240.941176z" fill="#287BF7"></path>
<path d="M340.931765 388.969412a120.621176 120.621176 0 1 0 115.260235 209.799529l70.776471 97.490824a241.091765 241.091765 0 1 1 82.522353-283.587765c15.751529 39.936 14.516706 83.546353 13.763764 110.290824-0.150588 4.879059-0.271059 9.216-0.271059 12.860235 0 22.648471 5.812706 48.519529 18.221177 66.831059 10.24 15.058824 26.654118 28.310588 61.108706 28.310588 31.804235 0 49.152-10.721882 59.331764-22.40753 11.264-12.950588 18.974118-33.370353 18.974118-59.452235 0-18.642824-9.396706-39.574588-27.166118-53.067294-16.022588-12.137412-43.550118-21.594353-86.768941-8.734118l-34.42447-115.440941c75.926588-22.618353 144.112941-9.637647 193.987764 28.190118 48.128 36.502588 74.842353 93.093647 74.842353 149.082353 0 48.489412-14.245647 99.087059-48.609882 138.541176-35.418353 40.719059-87.341176 63.728941-150.166588 63.728941-75.294118 0-128.662588-33.792-160.768-81.106823-29.936941-44.062118-39.032471-95.864471-39.032471-134.475294 0-9.396706 0.150588-17.588706 0.240941-24.847059 0.421647-25.057882 0.632471-38.972235-5.330823-54.091294a120.621176 120.621176 0 0 0-156.491294-67.915294z" fill="#FFFFFF"></path>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 469 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 276 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 285 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 586 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 612 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 766 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 603 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 840 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 451 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

View File

@@ -0,0 +1,962 @@
# 3ds Max Wavefront OBJ Exporter v0.97b - (c)2007 guruware
# <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ļ<EFBFBD>:04.08.2026 13:21:15
newmtl Mat3d66_10994759_12_33214
Ns 10.0000
Ni 1.5000
d 1.0000
Tr 0.0000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 0.5880 0.5880 0.5880
Kd 0.3686 0.3686 0.3686
Ks 0.0000 0.0000 0.0000
Ke 0.0000 0.0000 0.0000
newmtl Mat3d66_10994759_11_76789
Ns 10.0000
Ni 1.5000
d 1.0000
Tr 0.0000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 1.0000 1.0000 1.0000
Kd 1.0000 1.0000 1.0000
Ks 0.0000 0.0000 0.0000
Ke 0.0000 0.0000 0.0000
map_Ka maps\Map__107_Noise.tga
map_Kd maps\Map__107_Noise.tga
map_bump -bm 0.2000 maps\Map__106_Noise.tga
newmtl Mat3d66_10994759_30_54055
Ns 10.0000
Ni 1.5000
d 1.0000
Tr 0.0000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 0.5880 0.5880 0.5880
Kd 0.6667 0.0000 0.0000
Ks 0.0000 0.0000 0.0000
Ke 0.0000 0.0000 0.0000
map_Ka maps\3d66_17938251_color_VR-<2D><>ɫ.tga
map_Kd maps\3d66_17938251_color_VR-<2D><>ɫ.tga
map_bump -bm 0.0500 maps\3d66_17938251_color_VR-<2D><>ɫ.tga
newmtl Mat3d66_11807706_26_30152
Ns 10.0000
Ni 1.5000
d 1.0000
Tr 0.0000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 0.5880 0.5880 0.5880
Kd 0.0196 0.0196 0.0196
Ks 0.0000 0.0000 0.0000
Ke 0.0000 0.0000 0.0000
map_bump maps\3d66Model-11807706-files-51.png
bump maps\3d66Model-11807706-files-51.png
newmtl Mat3d66_10994759_29_37910
Ns 10.0000
Ni 1.5000
d 1.0000
Tr 0.0000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 0.5880 0.5880 0.5880
Kd 0.6078 0.0000 0.0000
Ks 0.0000 0.0000 0.0000
Ke 0.0000 0.0000 0.0000
newmtl Mat3d66_10994759_22_2dad4012
Ns 10.0000
Ni 1.5000
d 1.0000
Tr 0.0000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 0.5880 0.5880 0.5880
Kd 0.1373 0.1373 0.1373
Ks 0.0000 0.0000 0.0000
Ke 0.0000 0.0000 0.0000
newmtl Mat3d66_10994759_34_15870
Ns 10.0000
Ni 1.5000
d 1.0000
Tr 0.0000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 0.5880 0.5880 0.5880
Kd 0.6392 0.6392 0.6392
Ks 0.0000 0.0000 0.0000
Ke 0.0000 0.0000 0.0000
map_Ka maps\3d66Model-10994759-files-1.jpg
map_Kd maps\3d66Model-10994759-files-1.jpg
newmtl Mat3d66_10994759_22_24012
Ns 10.0000
Ni 1.5000
d 1.0000
Tr 0.0000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 0.5880 0.5880 0.5880
Kd 0.0039 0.0039 0.0039
Ks 0.0000 0.0000 0.0000
Ke 0.0000 0.0000 0.0000
newmtl Mat3d66_10994759_37_59834
Ns 10.0000
Ni 1.5000
d 1.0000
Tr 0.0000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 0.5880 0.5880 0.5880
Kd 0.0000 0.0000 0.0000
Ks 0.0000 0.0000 0.0000
Ke 0.0000 0.0000 0.0000
newmtl wire_198225087
Ns 32
d 1
Tr 0
Tf 1 1 1
illum 2
Ka 0.7765 0.8824 0.3412
Kd 0.7765 0.8824 0.3412
Ks 0.3500 0.3500 0.3500
newmtl Mat3d66_10994759_35_21635
Ns 10.0000
Ni 1.5000
d 1.0000
Tr 0.0000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 0.5880 0.5880 0.5880
Kd 0.0588 0.0588 0.0588
Ks 0.0000 0.0000 0.0000
Ke 0.0000 0.0000 0.0000
newmtl wire_115115115
Ns 32
d 1
Tr 0
Tf 1 1 1
illum 2
Ka 0.4510 0.4510 0.4510
Kd 0.4510 0.4510 0.4510
Ks 0.3500 0.3500 0.3500
newmtl Mat3d66_10994759_25_25429
Ns 10.0000
Ni 1.5000
d 1.0000
Tr 0.0000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 0.5880 0.5880 0.5880
Kd 0.8549 0.9098 0.9412
Ks 0.0000 0.0000 0.0000
Ke 0.0000 0.0000 0.0000
map_Ka maps\3d66Model-10994759-files-13.jpg
map_Kd maps\3d66Model-10994759-files-13.jpg
map_bump maps\3d66Model-10994759-files-13.jpg
bump maps\3d66Model-10994759-files-13.jpg
newmtl wire_000000000
Ns 32
d 1
Tr 0
Tf 1 1 1
illum 2
Ka 0.0000 0.0000 0.0000
Kd 0.0000 0.0000 0.0000
Ks 0.3500 0.3500 0.3500
newmtl 3d66_VRayMtl_23563919_021
Ns 10.0000
Ni 1.5000
d 1.0000
Tr 0.0000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 0.5880 0.5880 0.5880
Kd 0.7529 0.7647 0.7294
Ks 0.0000 0.0000 0.0000
Ke 0.0000 0.0000 0.0000
newmtl 3d66_VRayMtl_23563919_022
Ns 10.0000
Ni 1.5000
d 1.0000
Tr 0.0000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 0.5880 0.5880 0.5880
Kd 0.5961 0.6039 0.5765
Ks 0.0000 0.0000 0.0000
Ke 0.0000 0.0000 0.0000
newmtl 3d66_VRayMtl_23563919_018
Ns 10.0000
Ni 1.5000
d 1.0000
Tr 0.0000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 0.5880 0.5880 0.5880
Kd 0.6314 0.0392 0.0392
Ks 0.0000 0.0000 0.0000
Ke 0.0000 0.0000 0.0000
newmtl 3d66_VRayMtl_23563919_019
Ns 10.0000
Ni 1.5000
d 1.0000
Tr 0.0000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 0.5880 0.5880 0.5880
Kd 0.0000 0.0000 0.0000
Ks 0.0000 0.0000 0.0000
Ke 0.0000 0.0000 0.0000
newmtl 3d66_VRayMtl_23563919_020
Ns 10.0000
Ni 1.5000
d 1.0000
Tr 0.0000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 0.5880 0.5880 0.5880
Kd 0.0275 0.0275 0.0275
Ks 0.0000 0.0000 0.0000
Ke 0.0000 0.0000 0.0000
newmtl <20><>ҵ<EFBFBD><D2B5><EFBFBD>Ұ<EFBFBD>ɫ<EFBFBD>߹<EFBFBD><DFB9><EFBFBD><E2BBAC>ƺ<EFBFBD><C6BA>_Ϳ<5F><CDBF>_VR
Ns 10.0000
Ni 1.5000
d 1.0000
Tr 0.0000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 0.5880 0.5880 0.5880
Kd 0.3922 0.3922 0.3922
Ks 0.0000 0.0000 0.0000
Ke 0.0000 0.0000 0.0000
map_Ka maps\3d66Mat-14871359-maps-3.jpg
map_Kd maps\3d66Mat-14871359-maps-3.jpg
map_bump -bm 0.0150 maps\<5C><>ͼ__123_Noise.tga
newmtl <20>ִ<EFBFBD><D6B4><EFBFBD>ɫ<EFBFBD>ƹ<EFBFBD><C6B9><EFBFBD><E2BBAC><EFBFBD><EFBFBD>_<EFBFBD><5F>_VR
Ns 10.0000
Ni 1.5000
d 1.0000
Tr 0.0000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 0.5880 0.5880 0.5880
Kd 0.0196 0.0196 0.0196
Ks 0.0000 0.0000 0.0000
Ke 0.0000 0.0000 0.0000
newmtl Mat3d66_11807706_4_39763
Ns 10.0000
Ni 1.5000
d 1.0000
Tr 0.0000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 0.5880 0.5880 0.5880
Kd 0.0196 0.0196 0.0196
Ks 0.0000 0.0000 0.0000
Ke 0.0000 0.0000 0.0000
newmtl Mat3d66_11807706_5_89594
Ns 41.0000
Ni 1.5000
d 0.8000
Tr 0.2000
Tf 0.8000 0.8000 0.8000
illum 2
Ka 0.9725 0.0000 0.0000
Kd 0.9725 0.0000 0.0000
Ks 0.3960 0.3960 0.3960
Ke 0.0000 0.0000 0.0000
newmtl wire_225198087
Ns 32
d 1
Tr 0
Tf 1 1 1
illum 2
Ka 0.8824 0.7765 0.3412
Kd 0.8824 0.7765 0.3412
Ks 0.3500 0.3500 0.3500
newmtl Mat3d66_11807706_7_95731
Ns 10.0000
Ni 1.5000
d 1.0000
Tr 0.0000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 0.5880 0.5880 0.5880
Kd 0.5333 0.5294 0.5255
Ks 0.0000 0.0000 0.0000
Ke 0.0000 0.0000 0.0000
newmtl Mat3d66_11807706_8_21117
Ns 76.0000
Ni 1.5000
d 0.4000
Tr 0.6000
Tf 0.4000 0.4000 0.4000
illum 2
Ka 0.1608 0.1569 0.1843
Kd 0.1608 0.1569 0.1843
Ks 0.7920 0.7920 0.7920
Ke 0.0000 0.0000 0.0000
map_refl maps\Map__614327418_VR-<2D><>ͼ.tga
newmtl Mat3d66_11807706_9_83349
Ns 10.0000
Ni 1.5000
d 1.0000
Tr 0.0000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 0.5880 0.5880 0.5880
Kd 0.0863 0.0745 0.0667
Ks 0.0000 0.0000 0.0000
Ke 0.0000 0.0000 0.0000
newmtl Mat3d66_11807706_10_61673
Ns 76.0000
Ni 1.5000
d 0.0500
Tr 0.9500
Tf 0.0500 0.0500 0.0500
illum 2
Ka 0.1608 0.1569 0.1843
Kd 0.1608 0.1569 0.1843
Ks 0.7920 0.7920 0.7920
Ke 0.0000 0.0000 0.0000
map_refl maps\Map__614327418_VR-<2D><>ͼ.tga
newmtl Mat3d66_11807706_11_67368
Ns 10.0000
Ni 1.5000
d 1.0000
Tr 0.0000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 0.5880 0.5880 0.5880
Kd 0.0863 0.0745 0.0667
Ks 0.0000 0.0000 0.0000
Ke 0.0000 0.0000 0.0000
map_Ka maps\3d66Model-11807706-files-3.jpg
map_Kd maps\3d66Model-11807706-files-3.jpg
newmtl 22___Default
Ns 10.0000
Ni 1.5000
d 1.0000
Tr 0.0000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 0.5880 0.5880 0.5880
Kd 0.0039 0.0039 0.0039
Ks 0.0000 0.0000 0.0000
Ke 0.0000 0.0000 0.0000
newmtl Mat3d66_11807706_29_27585
Ns 38.0000
Ni 1.5000
d 1.0000
Tr 0.0000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 0.5882 0.5882 0.5882
Kd 0.5882 0.5882 0.5882
Ks 0.1800 0.1800 0.1800
Ke 0.0706 0.0706 0.0706
map_Ka maps\3d66Model-11807706-files-60.jpg
map_Kd maps\3d66Model-11807706-files-60.jpg
map_bump maps\3d66Model-11807706-files-60.jpg
bump maps\3d66Model-11807706-files-60.jpg
newmtl Mat3d66_11807706_120_47020
Ns 10.0000
Ni 1.5000
d 1.0000
Tr 0.0000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 0.5880 0.5880 0.5880
Kd 0.5000 0.5000 0.5000
Ks 0.0000 0.0000 0.0000
Ke 0.0000 0.0000 0.0000
map_Ka maps\Map__1_<31><5F>ɫУ<C9AB><D0A3>.tga
map_Kd maps\Map__1_<31><5F>ɫУ<C9AB><D0A3>.tga
map_bump maps\Map__1_<31><5F>ɫУ<C9AB><D0A3>.tga
newmtl wire_010140195
Ns 32
d 1
Tr 0
Tf 1 1 1
illum 2
Ka 0.0392 0.5490 0.7647
Kd 0.0392 0.5490 0.7647
Ks 0.3500 0.3500 0.3500
newmtl Mat3d66_11807706_28_26251
Ns 10.0000
Ni 1.5000
d 1.0000
Tr 0.0000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 0.5880 0.5880 0.5880
Kd 0.8941 0.8941 0.8941
Ks 0.0000 0.0000 0.0000
Ke 0.0000 0.0000 0.0000
newmtl Mat3d66_15185da96_31_5088
Ns 10.0000
Ni 1.5000
d 1.0000
Tr 0.0000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 0.5880 0.5880 0.5880
Kd 0.0275 0.0275 0.0235
Ks 0.0000 0.0000 0.0000
Ke 0.0000 0.0000 0.0000
newmtl Mat3d66_10994759_49_69999
Ns 10.0000
Ni 1.5000
d 1.0000
Tr 0.0000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 0.5880 0.5880 0.5880
Kd 0.1373 0.1373 0.1373
Ks 0.0000 0.0000 0.0000
Ke 0.0000 0.0000 0.0000
newmtl Mat3d66_10994759_31_26985
Ns 10.0000
Ni 1.5000
d 1.0000
Tr 0.0000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 0.5880 0.5880 0.5880
Kd 0.0392 0.0392 0.0392
Ks 0.0000 0.0000 0.0000
Ke 0.0000 0.0000 0.0000
map_bump maps\3d66Model-10994759-files-15.JPG
bump maps\3d66Model-10994759-files-15.JPG
newmtl 24___Default
Ns 10.0000
Ni 1.5000
d 1.0000
Tr 0.0000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 0.5880 0.5880 0.5880
Kd 0.5000 0.5000 0.5000
Ks 0.0000 0.0000 0.0000
Ke 0.0000 0.0000 0.0000
map_Ka maps\t7.jpg
map_Kd maps\t7.jpg
newmtl 3d66_VRayMtl_20092954_028
Ns 10.0000
Ni 1.5000
d 1.0000
Tr 0.0000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 0.5880 0.5880 0.5880
Kd 0.9216 0.9216 0.9216
Ks 0.0000 0.0000 0.0000
Ke 0.0000 0.0000 0.0000
newmtl 3d66_VRayMtl_20092954_034
Ns 10.0000
Ni 1.5000
d 1.0000
Tr 0.0000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 0.5880 0.5880 0.5880
Kd 0.1176 0.1176 0.1176
Ks 0.0000 0.0000 0.0000
Ke 0.0000 0.0000 0.0000
newmtl Mat3d66_7566050_1_94391
Ns 10.0000
Ni 1.5000
d 1.0000
Tr 0.0000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 0.5880 0.5880 0.5880
Kd 0.9020 0.9020 0.9020
Ks 0.0000 0.0000 0.0000
Ke 0.0000 0.0000 0.0000
newmtl Mat3d66_1518596_31_5088
Ns 10.0000
Ni 1.5000
d 1.0000
Tr 0.0000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 0.5880 0.5880 0.5880
Kd 0.6471 0.6235 0.6000
Ks 0.0000 0.0000 0.0000
Ke 0.0000 0.0000 0.0000
newmtl 3d66_VRayMtl_23605226_030
Ns 10.0000
Ni 1.5000
d 1.0000
Tr 0.0000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 0.5880 0.5880 0.5880
Kd 0.5882 0.5882 0.5882
Ks 0.0000 0.0000 0.0000
Ke 0.0000 0.0000 0.0000
newmtl 3d66_VRayMtl_23605226_031
Ns 10.0000
Ni 1.5000
d 1.0000
Tr 0.0000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 0.5880 0.5880 0.5880
Kd 0.5000 0.5000 0.5000
Ks 0.0000 0.0000 0.0000
Ke 0.0000 0.0000 0.0000
map_Ka maps\3d66Model-23605226-files-005.png
map_Kd maps\3d66Model-23605226-files-005.png
map_bump maps\3d66_VRayNormalMap_23605226_023_VR-<2D><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ͼ.tga
newmtl Mat3d66_7859414_4_42689
Ns 10.0000
Ni 1.5000
d 1.0000
Tr 0.0000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 0.5880 0.5880 0.5880
Kd 0.3529 0.3529 0.3529
Ks 0.0000 0.0000 0.0000
Ke 0.0000 0.0000 0.0000
newmtl Mat3d66_7859414_3_41489
Ns 10.0000
Ni 1.5000
d 1.0000
Tr 0.0000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 0.5880 0.5880 0.5880
Kd 0.8627 0.8549 0.8471
Ks 0.0000 0.0000 0.0000
Ke 0.0000 0.0000 0.0000
newmtl Mat3d66_7859414_7_21754
Ns 10.0000
Ni 1.5000
d 1.0000
Tr 0.0000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 0.5880 0.5880 0.5880
Kd 0.1765 0.1765 0.1765
Ks 0.0000 0.0000 0.0000
Ke 0.0000 0.0000 0.0000
newmtl Mat3d66_7859414_5_93165
Ns 10.0000
Ni 1.5000
d 1.0000
Tr 0.0000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 0.5880 0.5880 0.5880
Kd 0.8235 0.8196 0.8157
Ks 0.0000 0.0000 0.0000
Ke 0.0000 0.0000 0.0000
newmtl wire_135006006
Ns 32
d 1
Tr 0
Tf 1 1 1
illum 2
Ka 0.5294 0.0235 0.0235
Kd 0.5294 0.0235 0.0235
Ks 0.3500 0.3500 0.3500
newmtl 3d66_VRayMtl_22568095_046
Ns 10.0000
Ni 1.5000
d 1.0000
Tr 0.0000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 0.5880 0.5880 0.5880
Kd 0.0000 0.0000 0.0000
Ks 0.0000 0.0000 0.0000
Ke 0.0000 0.0000 0.0000
newmtl 3d66_VRayMtl_22568095_047
Ns 10.0000
Ni 1.5000
d 1.0000
Tr 0.0000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 0.5880 0.5880 0.5880
Kd 0.0275 0.0275 0.0275
Ks 0.0000 0.0000 0.0000
Ke 0.0000 0.0000 0.0000
newmtl 3d66_VRayMtl_22568095_045
Ns 10.0000
Ni 1.5000
d 1.0000
Tr 0.0000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 0.5880 0.5880 0.5880
Kd 0.6314 0.0392 0.0392
Ks 0.0000 0.0000 0.0000
Ke 0.0000 0.0000 0.0000
newmtl 3d66_VRayMtl_22568095_048
Ns 10.0000
Ni 1.5000
d 1.0000
Tr 0.0000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 0.5880 0.5880 0.5880
Kd 0.7529 0.7647 0.7294
Ks 0.0000 0.0000 0.0000
Ke 0.0000 0.0000 0.0000
newmtl 3d66_VRayMtl_22568095_049
Ns 10.0000
Ni 1.5000
d 1.0000
Tr 0.0000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 0.5880 0.5880 0.5880
Kd 0.5961 0.6039 0.5765
Ks 0.0000 0.0000 0.0000
Ke 0.0000 0.0000 0.0000
newmtl 3d66_VRayMtl_22568095_052
Ns 10.0000
Ni 1.5000
d 1.0000
Tr 0.0000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 0.5880 0.5880 0.5880
Kd 0.0510 0.0510 0.0510
Ks 0.0000 0.0000 0.0000
Ke 0.0000 0.0000 0.0000
newmtl 3d66_VRayMtl_22568095_069
Ns 10.0000
Ni 1.5000
d 1.0000
Tr 0.0000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 0.5880 0.5880 0.5880
Kd 1.0000 1.0000 1.0000
Ks 0.0000 0.0000 0.0000
Ke 0.0000 0.0000 0.0000
newmtl 3d66_VRayMtl_22568095_070
Ns 10.0000
Ni 1.5000
d 1.0000
Tr 0.0000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 0.5880 0.5880 0.5880
Kd 0.7176 0.7882 0.8510
Ks 0.0000 0.0000 0.0000
Ke 0.0000 0.0000 0.0000
newmtl 3d66_VRayMtl_22568095_071
Ns 10.0000
Ni 1.5000
d 1.0000
Tr 0.0000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 0.5880 0.5880 0.5880
Kd 0.0000 0.0000 0.0000
Ks 0.0000 0.0000 0.0000
Ke 0.0000 0.0000 0.0000
newmtl 3d66_VRayMtl_22568095_074
Ns 10.0000
Ni 1.5000
d 1.0000
Tr 0.0000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 0.5880 0.5880 0.5880
Kd 0.5000 0.5000 0.5000
Ks 0.0000 0.0000 0.0000
Ke 0.0000 0.0000 0.0000
map_Ka maps\3d66_Bricks_22568095_021_Tiles.tga
map_Kd maps\3d66_Bricks_22568095_021_Tiles.tga
newmtl 3d66_VRayMtl_22568095_025
Ns 10.0000
Ni 1.5000
d 1.0000
Tr 0.0000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 0.5880 0.5880 0.5880
Kd 0.0902 0.0902 0.0902
Ks 0.0000 0.0000 0.0000
Ke 0.0000 0.0000 0.0000
newmtl 3d66_Standardmaterial_22568095_004
Ns 10.0000
Ni 1.5000
d 1.0000
Tr 0.0000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 0.5882 0.5882 0.5882
Kd 0.5882 0.5882 0.5882
Ks 0.0000 0.0000 0.0000
Ke 0.0000 0.0000 0.0000
newmtl 3d66_VRayMtl_22568095_066
Ns 10.0000
Ni 1.5000
d 1.0000
Tr 0.0000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 0.5880 0.5880 0.5880
Kd 0.0392 0.0392 0.0392
Ks 0.0000 0.0000 0.0000
Ke 0.0000 0.0000 0.0000
newmtl 17___Default
Ns 10.0000
Ni 1.5000
d 1.0000
Tr 0.0000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 0.5880 0.5880 0.5880
Kd 0.5000 0.5000 0.5000
Ks 0.0000 0.0000 0.0000
Ke 0.0000 0.0000 0.0000
map_Ka maps\t3.jpg
map_Kd maps\t3.jpg
newmtl 18___Default
Ns 10.0000
Ni 1.5000
d 1.0000
Tr 0.0000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 0.5880 0.5880 0.5880
Kd 0.5000 0.5000 0.5000
Ks 0.0000 0.0000 0.0000
Ke 0.0000 0.0000 0.0000
map_Ka maps\t5.jpg
map_Kd maps\t5.jpg
newmtl 16___Default
Ns 10.0000
Ni 1.5000
d 1.0000
Tr 0.0000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 0.5880 0.5880 0.5880
Kd 0.5000 0.5000 0.5000
Ks 0.0000 0.0000 0.0000
Ke 0.0000 0.0000 0.0000
map_Ka maps\t2.jpg
map_Kd maps\t2.jpg
newmtl 15___Default
Ns 10.0000
Ni 1.5000
d 1.0000
Tr 0.0000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 0.5880 0.5880 0.5880
Kd 0.5000 0.5000 0.5000
Ks 0.0000 0.0000 0.0000
Ke 0.0000 0.0000 0.0000
map_Ka maps\t1.jpg
map_Kd maps\t1.jpg
newmtl Mat3d66_14662737_11_91260
Ns 10.0000
Ni 1.5000
d 1.0000
Tr 0.0000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 0.5880 0.5880 0.5880
Kd 1.0000 1.0000 1.0000
Ks 0.0000 0.0000 0.0000
Ke 0.0000 0.0000 0.0000
map_Ka maps\3d66Model-14662737-files-5.jpg
map_Kd maps\3d66Model-14662737-files-5.jpg
newmtl Mat3d66_14662737_12_48868
Ns 10.0000
Ni 1.5000
d 1.0000
Tr 0.0000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 0.5880 0.5880 0.5880
Kd 1.0000 1.0000 1.0000
Ks 0.0000 0.0000 0.0000
Ke 0.0000 0.0000 0.0000
map_Ka maps\3d66Model-14662737-files-5.jpg
map_Kd maps\3d66Model-14662737-files-5.jpg
newmtl wire_154185229
Ns 32
d 1
Tr 0
Tf 1 1 1
illum 2
Ka 0.6039 0.7255 0.8980
Kd 0.6039 0.7255 0.8980
Ks 0.3500 0.3500 0.3500
newmtl Glass
Ns 10.0000
Ni 1.5000
d 1.0000
Tr 0.0000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 0.5880 0.5880 0.5880
Kd 0.0000 0.0000 0.0000
Ks 0.0000 0.0000 0.0000
Ke 0.0000 0.0000 0.0000
newmtl Mat3d66_8099876_13_28564
Ns 10.0000
Ni 1.5000
d 1.0000
Tr 0.0000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 0.5880 0.5880 0.5880
Kd 0.4667 0.4510 0.4353
Ks 0.0000 0.0000 0.0000
Ke 0.0000 0.0000 0.0000
newmtl Material__53
Ns 10.0000
Ni 1.5000
d 1.0000
Tr 0.0000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 0.5880 0.5880 0.5880
Kd 0.0000 0.0000 0.0000
Ks 0.0000 0.0000 0.0000
Ke 0.0000 0.0000 0.0000
newmtl Material__52
Ns 10.0000
Ni 1.5000
d 1.0000
Tr 0.0000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 0.5880 0.5880 0.5880
Kd 0.1451 0.1451 0.1451
Ks 0.0000 0.0000 0.0000
Ke 0.0000 0.0000 0.0000
map_Ka maps\3d66Model-8099876-files-4.jpg
map_Kd maps\3d66Model-8099876-files-4.jpg
newmtl Material__109
Ns 10.0000
Ni 1.5000
d 1.0000
Tr 0.0000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 0.5880 0.5880 0.5880
Kd 0.0000 0.0000 0.0000
Ks 0.0000 0.0000 0.0000
Ke 0.0000 0.0000 0.0000
newmtl <20><><EFBFBD>ݧ<EFBFBD><DDA7>ާڧߧڧ<DFA7>_<EFBFBD><5F><EFBFBD><EFBFBD><EFBFBD>ݧڧ<DDA7><DAA7><EFBFBD><EFBFBD>ӧѧߧߧ<DFA7><DFA7><EFBFBD>
Ns 10.0000
Ni 1.5000
d 1.0000
Tr 0.0000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 0.5880 0.5880 0.5880
Kd 0.1872 0.1872 0.1872
Ks 0.0000 0.0000 0.0000
Ke 0.0000 0.0000 0.0000
newmtl <20><><EFBFBD><EFBFBD><EFBFBD>ݧ<EFBFBD>
Ns 10.0000
Ni 1.5000
d 1.0000
Tr 0.0000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 0.5880 0.5880 0.5880
Kd 0.7529 0.7529 0.7529
Ks 0.0000 0.0000 0.0000
Ke 0.0000 0.0000 0.0000
map_d maps\Map__19_Falloff.tga
newmtl wire_154215229
Ns 32
d 1
Tr 0
Tf 1 1 1
illum 2
Ka 0.6039 0.8431 0.8980
Kd 0.6039 0.8431 0.8980
Ks 0.3500 0.3500 0.3500

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,621 @@
(function() {
var Nr = 10;
// convert two-dimensional indicies to one-dim array indices
var I00 = 0;
var I01 = 1;
var I02 = 2;
var I03 = 3;
var I10 = 4;
var I11 = 5;
var I12 = 6;
var I13 = 7;
var I20 = 8;
var I21 = 9;
var I22 = 10;
var I23 = 11;
var I30 = 12;
var I31 = 13;
var I32 = 14;
var I33 = 15;
// S-Box substitution table
var S_enc = new Array(
0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5,
0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0,
0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc,
0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a,
0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0,
0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b,
0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85,
0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5,
0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17,
0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88,
0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c,
0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9,
0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6,
0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e,
0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94,
0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68,
0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16);
// inverse S-Box for decryptions
var S_dec = new Array(
0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38,
0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87,
0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d,
0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2,
0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16,
0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda,
0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a,
0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02,
0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea,
0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85,
0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89,
0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20,
0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31,
0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d,
0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0,
0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26,
0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d);
function cvt_hex8 (val) {
var vh = (val>>>4)&0x0f;
return vh.toString(16) + (val&0x0f).toString(16);
}
function cvt_byte (str) {
// get the first hex digit
var val1 = str.charCodeAt(0);
// do some error checking
if (val1 >= 48 && val1 <= 57) {
// have a valid digit 0-9
val1 -= 48;
} else if (val1 >= 65 && val1 <= 70) {
// have a valid digit A-F
val1 -= 55;
} else if (val1 >= 97 && val1 <= 102) {
// have a valid digit A-F
val1 -= 87;
} else {
// not 0-9 or A-F, complain
console.log( str.charAt(1)+" is not a valid hex digit" );
return -1;
}
// get the second hex digit
var val2 = str.charCodeAt(1);
// do some error checking
if ( val2 >= 48 && val2 <= 57 ) {
// have a valid digit 0-9
val2 -= 48;
} else if ( val2 >= 65 && val2 <= 70 ) {
// have a valid digit A-F
val2 -= 55;
} else if ( val2 >= 97 && val2 <= 102 ) {
// have a valid digit A-F
val2 -= 87;
} else {
// not 0-9 or A-F, complain
console.log( str.charAt(2)+" is not a valid hex digit" );
return -1;
}
// all is ok, return the value
return val1*16 + val2;
}
// conversion function for non-constant subscripts
// assume subscript range 0..3
function I(x,y) {
return (x*4) + y;
}
// remove spaces from input
function remove_spaces(instr) {
var i;
var outstr = "";
for(i=0; i<instr.length; i++) {
if ( instr.charAt(i) != " " )
// not a space, include it
outstr += instr.charAt(i);
}
return outstr;
}
// get the message to encrypt/decrypt or the key
// return as a 16-byte array
function get_value(str, isASCII) {
var dbyte = new Array(16);
var i;
var val; // one hex digit
if (isASCII) {
// check length of data
if (str.length > 16) {
console.log("is too long, using the first 16 ASCII characters" );
}
// have ASCII data
// 16 characters?
if (str.length >= 16) {
// 16 or more characters
for(i=0; i<16; i++) {
dbyte[i] = str.charCodeAt(i);
}
} else {
// less than 16 characters - fill with NULLs
for(i=0; i<str.length; i++) {
dbyte[i] = str.charCodeAt(i);
}
for( i=str.length; i<16; i++) {
dbyte[i] = 0;
}
}
} else {
// have hex data - remove any spaces they used, then convert
//str = remove_spaces(str);
// check length of data
if ( str.length != 32 ) {
//console.log("\tget_value:\tstr = " + str + "\tisASCII = " + isASCII); //isASCII = false
console.log("length wrong: Is " + str.length + " hex digits, but must be 128 bits (32 hex digits)");
dbyte[0] = -1;
return dbyte;
}
for( i=0; i<16; i++ ) {
// isolate and convert this substring
dbyte[i] = cvt_byte( str.substr(i*2,2) );
if( dbyte[i] < 0 ) {
// have an error
dbyte[0] = -1;
return dbyte;
}
}
}
// return successful conversion
return dbyte;
}
//do the AES GF(2**8) multiplication
// do this by the shift-and-"add" approach
function aes_mul(a, b) {
var res = 0;
while(a > 0) {
if((a&1) != 0)
res = res ^ b; // "add" to the result
a >>>= 1; // shift a to get next higher-order bit
b <<= 1; // shift multiplier also
}
// now reduce it modulo x**8 + x**4 + x**3 + x + 1
var hbit = 0x10000; // bit to test if we need to take action
var modulus = 0x11b00; // modulus - XOR by this to change value
while(hbit >= 0x100) {
if ((res & hbit) != 0) {
res ^= modulus; // XOR with the modulus
}
// prepare for the next loop
hbit >>= 1;
modulus >>= 1;
}
return res;
}
// apply the S-box substitution to the key expansion
function SubWord(word_ary) {
var i;
for(i=0; i<16; i++) {
word_ary[i] = S_enc[word_ary[i]];
}
return word_ary;
}
// rotate the bytes in a word
function RotWord(word_ary) {
return new Array(word_ary[1], word_ary[2], word_ary[3], word_ary[0]);
}
// calculate the first item Rcon[i] = { x^(i-1), 0, 0, 0 }
// note we only return the first item
function Rcon(exp) {
var val = 2;
var result = 1;
// remember to calculate x^(exp-1)
exp--;
// process the exponent using normal shift and multiply
while ( exp > 0 )
{
if ( (exp & 1) != 0 )
result = aes_mul( result, val );
// square the value
val = aes_mul( val, val );
// move to the next bit
exp >>= 1;
}
return result;
}
// round key generation
// return a byte array with the expanded key information
function key_expand( key )
{
var temp = new Array(4);
var i, j;
var w = new Array(4*(Nr+1));
// copy initial key stuff
for( i=0; i<16; i++ )
{
w[i] = key[i];
}
// generate rest of key schedule using 32-bit words
i = 4;
while ( i < 4*(Nr+1)) // blocksize * ( rounds + 1 )
{
// copy word W[i-1] to temp
for( j=0; j<4; j++ )
temp[j] = w[(i-1)*4+j];
if ( i % 4 == 0)
{
// temp = SubWord(RotWord(temp)) ^ Rcon[i/4];
temp = RotWord( temp );
temp = SubWord( temp );
temp[0] ^= Rcon( i>>>2 );
}
// word = word ^ temp
for( j=0; j<4; j++ )
w[i*4+j] = w[(i-4)*4+j] ^ temp[j];
i++;
}
return w;
}
// do S-Box substitution
function SubBytes(state, Sbox)
{
var i;
for( i=0; i<16; i++ )
state[i] = Sbox[ state[i] ];
return state;
}
// shift each row as appropriate
function ShiftRows(state)
{
var t0, t1, t2, t3;
// top row (row 0) isn't shifted
// next row (row 1) rotated left 1 place
t0 = state[I10];
t1 = state[I11];
t2 = state[I12];
t3 = state[I13];
state[I10] = t1;
state[I11] = t2;
state[I12] = t3;
state[I13] = t0;
// next row (row 2) rotated left 2 places
t0 = state[I20];
t1 = state[I21];
t2 = state[I22];
t3 = state[I23];
state[I20] = t2;
state[I21] = t3;
state[I22] = t0;
state[I23] = t1;
// bottom row (row 3) rotated left 3 places
t0 = state[I30];
t1 = state[I31];
t2 = state[I32];
t3 = state[I33];
state[I30] = t3;
state[I31] = t0;
state[I32] = t1;
state[I33] = t2;
return state;
}
// inverset shift each row as appropriate
function InvShiftRows(state)
{
var t0, t1, t2, t3;
// top row (row 0) isn't shifted
// next row (row 1) rotated left 1 place
t0 = state[I10];
t1 = state[I11];
t2 = state[I12];
t3 = state[I13];
state[I10] = t3;
state[I11] = t0;
state[I12] = t1;
state[I13] = t2;
// next row (row 2) rotated left 2 places
t0 = state[I20];
t1 = state[I21];
t2 = state[I22];
t3 = state[I23];
state[I20] = t2;
state[I21] = t3;
state[I22] = t0;
state[I23] = t1;
// bottom row (row 3) rotated left 3 places
t0 = state[I30];
t1 = state[I31];
t2 = state[I32];
t3 = state[I33];
state[I30] = t1;
state[I31] = t2;
state[I32] = t3;
state[I33] = t0;
return state;
}
// process column info
function MixColumns(state)
{
var col;
var c0, c1, c2, c3;
for( col=0; col<4; col++ )
{
c0 = state[I(0,col)];
c1 = state[I(1,col)];
c2 = state[I(2,col)];
c3 = state[I(3,col)];
// do mixing, and put back into array
state[I(0,col)] = aes_mul(2,c0) ^ aes_mul(3,c1) ^ c2 ^ c3;
state[I(1,col)] = c0 ^ aes_mul(2,c1) ^ aes_mul(3,c2) ^ c3;
state[I(2,col)] = c0 ^ c1 ^ aes_mul(2,c2) ^ aes_mul(3,c3);
state[I(3,col)] = aes_mul(3,c0) ^ c1 ^ c2 ^ aes_mul(2,c3);
}
return state;
}
// inverse process column info
function InvMixColumns(state)
{
var col;
var c0, c1, c2, c3;
for( col=0; col<4; col++ )
{
c0 = state[I(0,col)];
c1 = state[I(1,col)];
c2 = state[I(2,col)];
c3 = state[I(3,col)];
// do inverse mixing, and put back into array
state[I(0,col)] = aes_mul(0x0e,c0) ^ aes_mul(0x0b,c1)
^ aes_mul(0x0d,c2) ^ aes_mul(0x09,c3);
state[I(1,col)] = aes_mul(0x09,c0) ^ aes_mul(0x0e,c1)
^ aes_mul(0x0b,c2) ^ aes_mul(0x0d,c3);
state[I(2,col)] = aes_mul(0x0d,c0) ^ aes_mul(0x09,c1)
^ aes_mul(0x0e,c2) ^ aes_mul(0x0b,c3);
state[I(3,col)] = aes_mul(0x0b,c0) ^ aes_mul(0x0d,c1)
^ aes_mul(0x09,c2) ^ aes_mul(0x0e,c3);
}
return state;
}
// insert subkey information
function AddRoundKey( state, w, base )
{
var col;
for( col=0; col<4; col++ )
{
state[I(0,col)] ^= w[base+col*4];
state[I(1,col)] ^= w[base+col*4+1];
state[I(2,col)] ^= w[base+col*4+2];
state[I(3,col)] ^= w[base+col*4+3];
}
return state;
}
// return a transposed array
function transpose( msg )
{
var row, col;
var state = new Array( 16 );
for( row=0; row<4; row++ )
for( col=0; col<4; col++ )
state[I(row,col)] = msg[I(col,row)];
return state;
}
// final AES state
var AES_output = new Array(16);
// format AES output
// -- uses the global array DES_output
function format_AES_output(bASCII)
{
var i;
var bits;
var str="";
// what type of data do we have to work with?
if (bASCII)
{
// convert each set of bits back to ASCII
for( i=0; i<16; i++ )
str += String.fromCharCode( AES_output[i] );
}
else
{
// output hexdecimal data (insert spaces)
str = cvt_hex8( AES_output[0] );
for( i=1; i<16; i++ )
{
str += "" + cvt_hex8( AES_output[i] );
}
}
return str;
}
// do encrytion
function aes_encrypt(str, key, bASCII)
{
//console.log(" aes_encrypt:\tstr = " + str + "\tkey = " + key + "\t bASCII = " + bASCII);
var w = new Array( 4*(Nr+1) ); // subkey information
var state = new Array( 16 ); // working state
var round;
//accumulated_output_info = "";
// get the message from the user
// also check if it is ASCII or hex
var msg = get_value(str, bASCII);
// problems??
if ( msg[0] < 0 )
{
return;
}
// get the key from the user
var key = get_value(key, false);
// problems??
if ( key[0] < 0 )
{
return;
}
// expand the key
w = key_expand( key );
// initial state = message in columns (transposed from what we input)
state = transpose( msg );
// display the round key - Transpose due to the way it is stored/used
state = AddRoundKey(state, w, 0);
for( round=1; round<Nr; round++ )
{
state = SubBytes(state, S_enc);
state = ShiftRows(state);
state = MixColumns(state);
// display the round key - Transpose due to the way it is stored/used
// note here the spec uses 32-bit words, we are using bytes, so an extra *4
state = AddRoundKey(state, w, round*4*4);
}
SubBytes(state, S_enc);
ShiftRows(state);
AddRoundKey(state, w, Nr*4*4);
// process output
AES_output = transpose( state );
var szOutput = format_AES_output(!bASCII);
return szOutput;
}
// do decryption
function aes_decrypt(str, key, bASCII)
{
//console.log(" aes_decrypt:\tstr = " + str + "\tkey = " + key + "\tbASCII = " + bASCII);
var w = new Array( 4*(Nr+1) ); // subkey information
var state = new Array( 16 ); // working state
var round;
//accumulated_output_info = "";
// get the message from the user
// also check if it is ASCII or hex
var msg = get_value(str, bASCII);
// problems??
if ( msg[0] < 0 )
{
return;
}
// get the key from the user
var key = get_value(key, false);
// problems??
if ( key[0] < 0 )
{
return;
}
// expand the key
w = key_expand( key );
// initial state = message
state = transpose( msg );
// display the round key - Transpose due to the way it is stored/used
state = AddRoundKey(state, w, Nr*4*4);
for( round=Nr-1; round>=1; round-- )
{
state = InvShiftRows(state);
state = SubBytes(state, S_dec);
// display the round key - Transpose due to the way it is stored/used
// note here the spec uses 32-bit words, we are using bytes, so an extra *4
state = AddRoundKey(state, w, round*4*4);
state = InvMixColumns(state);
}
InvShiftRows(state);
SubBytes(state, S_dec);
AddRoundKey(state, w, 0);
// process output
AES_output = transpose( state );
var szOutput = format_AES_output(!bASCII);
return szOutput;
}
window.aes_encrypt = aes_encrypt;
window.aes_decrypt = aes_decrypt;
window.console = window.console || {
log: function() {}
};
}());

View File

@@ -0,0 +1,106 @@
var dbits,canary=244837814094590,j_lm=(canary&16777215)==15715070;function BigInteger(a,b,c){a!=null&&("number"==typeof a?this.fromNumber(a,b,c):b==null&&"string"!=typeof a?this.fromString(a,256):this.fromString(a,b))}function nbi(){return new BigInteger(null)}function am1(a,b,c,d,e,g){for(;--g>=0;){var h=b*this[a++]+c[d]+e,e=Math.floor(h/67108864);c[d++]=h&67108863}return e}
function am2(a,b,c,d,e,g){var h=b&32767;for(b>>=15;--g>=0;){var f=this[a]&32767,o=this[a++]>>15,p=b*f+o*h,f=h*f+((p&32767)<<15)+c[d]+(e&1073741823),e=(f>>>30)+(p>>>15)+b*o+(e>>>30);c[d++]=f&1073741823}return e}function am3(a,b,c,d,e,g){var h=b&16383;for(b>>=14;--g>=0;){var f=this[a]&16383,o=this[a++]>>14,p=b*f+o*h,f=h*f+((p&16383)<<14)+c[d]+e,e=(f>>28)+(p>>14)+b*o;c[d++]=f&268435455}return e}
j_lm&&navigator.appName=="Microsoft Internet Explorer"?(BigInteger.prototype.am=am2,dbits=30):j_lm&&navigator.appName!="Netscape"?(BigInteger.prototype.am=am1,dbits=26):(BigInteger.prototype.am=am3,dbits=28);BigInteger.prototype.DB=dbits;BigInteger.prototype.DM=(1<<dbits)-1;BigInteger.prototype.DV=1<<dbits;var BI_FP=52;BigInteger.prototype.FV=Math.pow(2,BI_FP);BigInteger.prototype.F1=BI_FP-dbits;BigInteger.prototype.F2=2*dbits-BI_FP;var BI_RM="0123456789abcdefghijklmnopqrstuvwxyz",BI_RC=[],rr,vv;
rr="0".charCodeAt(0);for(vv=0;vv<=9;++vv)BI_RC[rr++]=vv;rr="a".charCodeAt(0);for(vv=10;vv<36;++vv)BI_RC[rr++]=vv;rr="A".charCodeAt(0);for(vv=10;vv<36;++vv)BI_RC[rr++]=vv;function int2char(a){return BI_RM.charAt(a)}function intAt(a,b){var c=BI_RC[a.charCodeAt(b)];return c==null?-1:c}function bnpCopyTo(a){for(var b=this.t-1;b>=0;--b)a[b]=this[b];a.t=this.t;a.s=this.s}function bnpFromInt(a){this.t=1;this.s=a<0?-1:0;a>0?this[0]=a:a<-1?this[0]=a+DV:this.t=0}
function nbv(a){var b=nbi();b.fromInt(a);return b}
function bnpFromString(a,b){var c;if(b==16)c=4;else if(b==8)c=3;else if(b==256)c=8;else if(b==2)c=1;else if(b==32)c=5;else if(b==4)c=2;else{this.fromRadix(a,b);return}this.s=this.t=0;for(var d=a.length,e=!1,g=0;--d>=0;){var h=c==8?a[d]&255:intAt(a,d);h<0?a.charAt(d)=="-"&&(e=!0):(e=!1,g==0?this[this.t++]=h:g+c>this.DB?(this[this.t-1]|=(h&(1<<this.DB-g)-1)<<g,this[this.t++]=h>>this.DB-g):this[this.t-1]|=h<<g,g+=c,g>=this.DB&&(g-=this.DB))}if(c==8&&(a[0]&128)!=0)this.s=-1,g>0&&(this[this.t-1]|=(1<<
this.DB-g)-1<<g);this.clamp();e&&BigInteger.ZERO.subTo(this,this)}function bnpClamp(){for(var a=this.s&this.DM;this.t>0&&this[this.t-1]==a;)--this.t}
function bnToString(a){if(this.s<0)return"-"+this.negate().toString(a);if(a==16)a=4;else if(a==8)a=3;else if(a==2)a=1;else if(a==32)a=5;else if(a==64)a=6;else if(a==4)a=2;else return this.toRadix(a);var b=(1<<a)-1,c,d=!1,e="",g=this.t,h=this.DB-g*this.DB%a;if(g-- >0){if(h<this.DB&&(c=this[g]>>h)>0)d=!0,e=int2char(c);for(;g>=0;)h<a?(c=(this[g]&(1<<h)-1)<<a-h,c|=this[--g]>>(h+=this.DB-a)):(c=this[g]>>(h-=a)&b,h<=0&&(h+=this.DB,--g)),c>0&&(d=!0),d&&(e+=int2char(c))}return d?e:"0"}
function bnNegate(){var a=nbi();BigInteger.ZERO.subTo(this,a);return a}function bnAbs(){return this.s<0?this.negate():this}function bnCompareTo(a){var b=this.s-a.s;if(b!=0)return b;var c=this.t,b=c-a.t;if(b!=0)return b;for(;--c>=0;)if((b=this[c]-a[c])!=0)return b;return 0}function nbits(a){var b=1,c;if((c=a>>>16)!=0)a=c,b+=16;if((c=a>>8)!=0)a=c,b+=8;if((c=a>>4)!=0)a=c,b+=4;if((c=a>>2)!=0)a=c,b+=2;a>>1!=0&&(b+=1);return b}
function bnBitLength(){return this.t<=0?0:this.DB*(this.t-1)+nbits(this[this.t-1]^this.s&this.DM)}function bnpDLShiftTo(a,b){var c;for(c=this.t-1;c>=0;--c)b[c+a]=this[c];for(c=a-1;c>=0;--c)b[c]=0;b.t=this.t+a;b.s=this.s}function bnpDRShiftTo(a,b){for(var c=a;c<this.t;++c)b[c-a]=this[c];b.t=Math.max(this.t-a,0);b.s=this.s}
function bnpLShiftTo(a,b){var c=a%this.DB,d=this.DB-c,e=(1<<d)-1,g=Math.floor(a/this.DB),h=this.s<<c&this.DM,f;for(f=this.t-1;f>=0;--f)b[f+g+1]=this[f]>>d|h,h=(this[f]&e)<<c;for(f=g-1;f>=0;--f)b[f]=0;b[g]=h;b.t=this.t+g+1;b.s=this.s;b.clamp()}
function bnpRShiftTo(a,b){b.s=this.s;var c=Math.floor(a/this.DB);if(c>=this.t)b.t=0;else{var d=a%this.DB,e=this.DB-d,g=(1<<d)-1;b[0]=this[c]>>d;for(var h=c+1;h<this.t;++h)b[h-c-1]|=(this[h]&g)<<e,b[h-c]=this[h]>>d;d>0&&(b[this.t-c-1]|=(this.s&g)<<e);b.t=this.t-c;b.clamp()}}
function bnpSubTo(a,b){for(var c=0,d=0,e=Math.min(a.t,this.t);c<e;)d+=this[c]-a[c],b[c++]=d&this.DM,d>>=this.DB;if(a.t<this.t){for(d-=a.s;c<this.t;)d+=this[c],b[c++]=d&this.DM,d>>=this.DB;d+=this.s}else{for(d+=this.s;c<a.t;)d-=a[c],b[c++]=d&this.DM,d>>=this.DB;d-=a.s}b.s=d<0?-1:0;d<-1?b[c++]=this.DV+d:d>0&&(b[c++]=d);b.t=c;b.clamp()}
function bnpMultiplyTo(a,b){var c=this.abs(),d=a.abs(),e=c.t;for(b.t=e+d.t;--e>=0;)b[e]=0;for(e=0;e<d.t;++e)b[e+c.t]=c.am(0,d[e],b,e,0,c.t);b.s=0;b.clamp();this.s!=a.s&&BigInteger.ZERO.subTo(b,b)}function bnpSquareTo(a){for(var b=this.abs(),c=a.t=2*b.t;--c>=0;)a[c]=0;for(c=0;c<b.t-1;++c){var d=b.am(c,b[c],a,2*c,0,1);if((a[c+b.t]+=b.am(c+1,2*b[c],a,2*c+1,d,b.t-c-1))>=b.DV)a[c+b.t]-=b.DV,a[c+b.t+1]=1}a.t>0&&(a[a.t-1]+=b.am(c,b[c],a,2*c,0,1));a.s=0;a.clamp()}
function bnpDivRemTo(a,b,c){var d=a.abs();if(!(d.t<=0)){var e=this.abs();if(e.t<d.t)b!=null&&b.fromInt(0),c!=null&&this.copyTo(c);else{c==null&&(c=nbi());var g=nbi(),h=this.s,a=a.s,f=this.DB-nbits(d[d.t-1]);f>0?(d.lShiftTo(f,g),e.lShiftTo(f,c)):(d.copyTo(g),e.copyTo(c));d=g.t;e=g[d-1];if(e!=0){var o=e*(1<<this.F1)+(d>1?g[d-2]>>this.F2:0),p=this.FV/o,o=(1<<this.F1)/o,q=1<<this.F2,n=c.t,k=n-d,j=b==null?nbi():b;g.dlShiftTo(k,j);c.compareTo(j)>=0&&(c[c.t++]=1,c.subTo(j,c));BigInteger.ONE.dlShiftTo(d,
j);for(j.subTo(g,g);g.t<d;)g[g.t++]=0;for(;--k>=0;){var l=c[--n]==e?this.DM:Math.floor(c[n]*p+(c[n-1]+q)*o);if((c[n]+=g.am(0,l,c,k,0,d))<l){g.dlShiftTo(k,j);for(c.subTo(j,c);c[n]<--l;)c.subTo(j,c)}}b!=null&&(c.drShiftTo(d,b),h!=a&&BigInteger.ZERO.subTo(b,b));c.t=d;c.clamp();f>0&&c.rShiftTo(f,c);h<0&&BigInteger.ZERO.subTo(c,c)}}}}function bnMod(a){var b=nbi();this.abs().divRemTo(a,null,b);this.s<0&&b.compareTo(BigInteger.ZERO)>0&&a.subTo(b,b);return b}function Classic(a){this.m=a}
function cConvert(a){return a.s<0||a.compareTo(this.m)>=0?a.mod(this.m):a}function cRevert(a){return a}function cReduce(a){a.divRemTo(this.m,null,a)}function cMulTo(a,b,c){a.multiplyTo(b,c);this.reduce(c)}function cSqrTo(a,b){a.squareTo(b);this.reduce(b)}Classic.prototype.convert=cConvert;Classic.prototype.revert=cRevert;Classic.prototype.reduce=cReduce;Classic.prototype.mulTo=cMulTo;Classic.prototype.sqrTo=cSqrTo;
function bnpInvDigit(){if(this.t<1)return 0;var a=this[0];if((a&1)==0)return 0;var b=a&3,b=b*(2-(a&15)*b)&15,b=b*(2-(a&255)*b)&255,b=b*(2-((a&65535)*b&65535))&65535,b=b*(2-a*b%this.DV)%this.DV;return b>0?this.DV-b:-b}function Montgomery(a){this.m=a;this.mp=a.invDigit();this.mpl=this.mp&32767;this.mph=this.mp>>15;this.um=(1<<a.DB-15)-1;this.mt2=2*a.t}
function montConvert(a){var b=nbi();a.abs().dlShiftTo(this.m.t,b);b.divRemTo(this.m,null,b);a.s<0&&b.compareTo(BigInteger.ZERO)>0&&this.m.subTo(b,b);return b}function montRevert(a){var b=nbi();a.copyTo(b);this.reduce(b);return b}
function montReduce(a){for(;a.t<=this.mt2;)a[a.t++]=0;for(var b=0;b<this.m.t;++b){var c=a[b]&32767,d=c*this.mpl+((c*this.mph+(a[b]>>15)*this.mpl&this.um)<<15)&a.DM,c=b+this.m.t;for(a[c]+=this.m.am(0,d,a,b,0,this.m.t);a[c]>=a.DV;)a[c]-=a.DV,a[++c]++}a.clamp();a.drShiftTo(this.m.t,a);a.compareTo(this.m)>=0&&a.subTo(this.m,a)}function montSqrTo(a,b){a.squareTo(b);this.reduce(b)}function montMulTo(a,b,c){a.multiplyTo(b,c);this.reduce(c)}Montgomery.prototype.convert=montConvert;
Montgomery.prototype.revert=montRevert;Montgomery.prototype.reduce=montReduce;Montgomery.prototype.mulTo=montMulTo;Montgomery.prototype.sqrTo=montSqrTo;function bnpIsEven(){return(this.t>0?this[0]&1:this.s)==0}function bnpExp(a,b){if(a>4294967295||a<1)return BigInteger.ONE;var c=nbi(),d=nbi(),e=b.convert(this),g=nbits(a)-1;for(e.copyTo(c);--g>=0;)if(b.sqrTo(c,d),(a&1<<g)>0)b.mulTo(d,e,c);else var h=c,c=d,d=h;return b.revert(c)}
function bnModPowInt(a,b){var c;c=a<256||b.isEven()?new Classic(b):new Montgomery(b);return this.exp(a,c)}BigInteger.prototype.copyTo=bnpCopyTo;BigInteger.prototype.fromInt=bnpFromInt;BigInteger.prototype.fromString=bnpFromString;BigInteger.prototype.clamp=bnpClamp;BigInteger.prototype.dlShiftTo=bnpDLShiftTo;BigInteger.prototype.drShiftTo=bnpDRShiftTo;BigInteger.prototype.lShiftTo=bnpLShiftTo;BigInteger.prototype.rShiftTo=bnpRShiftTo;BigInteger.prototype.subTo=bnpSubTo;
BigInteger.prototype.multiplyTo=bnpMultiplyTo;BigInteger.prototype.squareTo=bnpSquareTo;BigInteger.prototype.divRemTo=bnpDivRemTo;BigInteger.prototype.invDigit=bnpInvDigit;BigInteger.prototype.isEven=bnpIsEven;BigInteger.prototype.exp=bnpExp;BigInteger.prototype.toString=bnToString;BigInteger.prototype.negate=bnNegate;BigInteger.prototype.abs=bnAbs;BigInteger.prototype.compareTo=bnCompareTo;BigInteger.prototype.bitLength=bnBitLength;BigInteger.prototype.mod=bnMod;BigInteger.prototype.modPowInt=bnModPowInt;
BigInteger.ZERO=nbv(0);BigInteger.ONE=nbv(1);function bnClone(){var a=nbi();this.copyTo(a);return a}function bnIntValue(){if(this.s<0)if(this.t==1)return this[0]-this.DV;else{if(this.t==0)return-1}else if(this.t==1)return this[0];else if(this.t==0)return 0;return(this[1]&(1<<32-this.DB)-1)<<this.DB|this[0]}function bnByteValue(){return this.t==0?this.s:this[0]<<24>>24}function bnShortValue(){return this.t==0?this.s:this[0]<<16>>16}
function bnpChunkSize(a){return Math.floor(Math.LN2*this.DB/Math.log(a))}function bnSigNum(){return this.s<0?-1:this.t<=0||this.t==1&&this[0]<=0?0:1}function bnpToRadix(a){a==null&&(a=10);if(this.signum()==0||a<2||a>36)return"0";var b=this.chunkSize(a),b=Math.pow(a,b),c=nbv(b),d=nbi(),e=nbi(),g="";for(this.divRemTo(c,d,e);d.signum()>0;)g=(b+e.intValue()).toString(a).substr(1)+g,d.divRemTo(c,d,e);return e.intValue().toString(a)+g}
function bnpFromRadix(a,b){this.fromInt(0);b==null&&(b=10);for(var c=this.chunkSize(b),d=Math.pow(b,c),e=!1,g=0,h=0,f=0;f<a.length;++f){var o=intAt(a,f);o<0?a.charAt(f)=="-"&&this.signum()==0&&(e=!0):(h=b*h+o,++g>=c&&(this.dMultiply(d),this.dAddOffset(h,0),h=g=0))}g>0&&(this.dMultiply(Math.pow(b,g)),this.dAddOffset(h,0));e&&BigInteger.ZERO.subTo(this,this)}
function bnpFromNumber(a,b,c){if("number"==typeof b)if(a<2)this.fromInt(1);else{this.fromNumber(a,c);this.testBit(a-1)||this.bitwiseTo(BigInteger.ONE.shiftLeft(a-1),op_or,this);for(this.isEven()&&this.dAddOffset(1,0);!this.isProbablePrime(b);)this.dAddOffset(2,0),this.bitLength()>a&&this.subTo(BigInteger.ONE.shiftLeft(a-1),this)}else{var c=[],d=a&7;c.length=(a>>3)+1;b.nextBytes(c);d>0?c[0]&=(1<<d)-1:c[0]=0;this.fromString(c,256)}}
function bnToByteArray(){var a=this.t,b=[];b[0]=this.s;var c=this.DB-a*this.DB%8,d,e=0;if(a-- >0){if(c<this.DB&&(d=this[a]>>c)!=(this.s&this.DM)>>c)b[e++]=d|this.s<<this.DB-c;for(;a>=0;)if(c<8?(d=(this[a]&(1<<c)-1)<<8-c,d|=this[--a]>>(c+=this.DB-8)):(d=this[a]>>(c-=8)&255,c<=0&&(c+=this.DB,--a)),(d&128)!=0&&(d|=-256),e==0&&(this.s&128)!=(d&128)&&++e,e>0||d!=this.s)b[e++]=d}return b}function bnEquals(a){return this.compareTo(a)==0}function bnMin(a){return this.compareTo(a)<0?this:a}
function bnMax(a){return this.compareTo(a)>0?this:a}function bnpBitwiseTo(a,b,c){var d,e,g=Math.min(a.t,this.t);for(d=0;d<g;++d)c[d]=b(this[d],a[d]);if(a.t<this.t){e=a.s&this.DM;for(d=g;d<this.t;++d)c[d]=b(this[d],e);c.t=this.t}else{e=this.s&this.DM;for(d=g;d<a.t;++d)c[d]=b(e,a[d]);c.t=a.t}c.s=b(this.s,a.s);c.clamp()}function op_and(a,b){return a&b}function bnAnd(a){var b=nbi();this.bitwiseTo(a,op_and,b);return b}function op_or(a,b){return a|b}
function bnOr(a){var b=nbi();this.bitwiseTo(a,op_or,b);return b}function op_xor(a,b){return a^b}function bnXor(a){var b=nbi();this.bitwiseTo(a,op_xor,b);return b}function op_andnot(a,b){return a&~b}function bnAndNot(a){var b=nbi();this.bitwiseTo(a,op_andnot,b);return b}function bnNot(){for(var a=nbi(),b=0;b<this.t;++b)a[b]=this.DM&~this[b];a.t=this.t;a.s=~this.s;return a}function bnShiftLeft(a){var b=nbi();a<0?this.rShiftTo(-a,b):this.lShiftTo(a,b);return b}
function bnShiftRight(a){var b=nbi();a<0?this.lShiftTo(-a,b):this.rShiftTo(a,b);return b}function lbit(a){if(a==0)return-1;var b=0;(a&65535)==0&&(a>>=16,b+=16);(a&255)==0&&(a>>=8,b+=8);(a&15)==0&&(a>>=4,b+=4);(a&3)==0&&(a>>=2,b+=2);(a&1)==0&&++b;return b}function bnGetLowestSetBit(){for(var a=0;a<this.t;++a)if(this[a]!=0)return a*this.DB+lbit(this[a]);return this.s<0?this.t*this.DB:-1}function cbit(a){for(var b=0;a!=0;)a&=a-1,++b;return b}
function bnBitCount(){for(var a=0,b=this.s&this.DM,c=0;c<this.t;++c)a+=cbit(this[c]^b);return a}function bnTestBit(a){var b=Math.floor(a/this.DB);return b>=this.t?this.s!=0:(this[b]&1<<a%this.DB)!=0}function bnpChangeBit(a,b){var c=BigInteger.ONE.shiftLeft(a);this.bitwiseTo(c,b,c);return c}function bnSetBit(a){return this.changeBit(a,op_or)}function bnClearBit(a){return this.changeBit(a,op_andnot)}function bnFlipBit(a){return this.changeBit(a,op_xor)}
function bnpAddTo(a,b){for(var c=0,d=0,e=Math.min(a.t,this.t);c<e;)d+=this[c]+a[c],b[c++]=d&this.DM,d>>=this.DB;if(a.t<this.t){for(d+=a.s;c<this.t;)d+=this[c],b[c++]=d&this.DM,d>>=this.DB;d+=this.s}else{for(d+=this.s;c<a.t;)d+=a[c],b[c++]=d&this.DM,d>>=this.DB;d+=a.s}b.s=d<0?-1:0;d>0?b[c++]=d:d<-1&&(b[c++]=this.DV+d);b.t=c;b.clamp()}function bnAdd(a){var b=nbi();this.addTo(a,b);return b}function bnSubtract(a){var b=nbi();this.subTo(a,b);return b}
function bnMultiply(a){var b=nbi();this.multiplyTo(a,b);return b}function bnSquare(){var a=nbi();this.squareTo(a);return a}function bnDivide(a){var b=nbi();this.divRemTo(a,b,null);return b}function bnRemainder(a){var b=nbi();this.divRemTo(a,null,b);return b}function bnDivideAndRemainder(a){var b=nbi(),c=nbi();this.divRemTo(a,b,c);return[b,c]}function bnpDMultiply(a){this[this.t]=this.am(0,a-1,this,0,0,this.t);++this.t;this.clamp()}
function bnpDAddOffset(a,b){if(a!=0){for(;this.t<=b;)this[this.t++]=0;for(this[b]+=a;this[b]>=this.DV;)this[b]-=this.DV,++b>=this.t&&(this[this.t++]=0),++this[b]}}function NullExp(){}function nNop(a){return a}function nMulTo(a,b,c){a.multiplyTo(b,c)}function nSqrTo(a,b){a.squareTo(b)}NullExp.prototype.convert=nNop;NullExp.prototype.revert=nNop;NullExp.prototype.mulTo=nMulTo;NullExp.prototype.sqrTo=nSqrTo;function bnPow(a){return this.exp(a,new NullExp)}
function bnpMultiplyLowerTo(a,b,c){var d=Math.min(this.t+a.t,b);c.s=0;for(c.t=d;d>0;)c[--d]=0;var e;for(e=c.t-this.t;d<e;++d)c[d+this.t]=this.am(0,a[d],c,d,0,this.t);for(e=Math.min(a.t,b);d<e;++d)this.am(0,a[d],c,d,0,b-d);c.clamp()}function bnpMultiplyUpperTo(a,b,c){--b;var d=c.t=this.t+a.t-b;for(c.s=0;--d>=0;)c[d]=0;for(d=Math.max(b-this.t,0);d<a.t;++d)c[this.t+d-b]=this.am(b-d,a[d],c,0,0,this.t+d-b);c.clamp();c.drShiftTo(1,c)}
function Barrett(a){this.r2=nbi();this.q3=nbi();BigInteger.ONE.dlShiftTo(2*a.t,this.r2);this.mu=this.r2.divide(a);this.m=a}function barrettConvert(a){if(a.s<0||a.t>2*this.m.t)return a.mod(this.m);else if(a.compareTo(this.m)<0)return a;else{var b=nbi();a.copyTo(b);this.reduce(b);return b}}function barrettRevert(a){return a}
function barrettReduce(a){a.drShiftTo(this.m.t-1,this.r2);if(a.t>this.m.t+1)a.t=this.m.t+1,a.clamp();this.mu.multiplyUpperTo(this.r2,this.m.t+1,this.q3);for(this.m.multiplyLowerTo(this.q3,this.m.t+1,this.r2);a.compareTo(this.r2)<0;)a.dAddOffset(1,this.m.t+1);for(a.subTo(this.r2,a);a.compareTo(this.m)>=0;)a.subTo(this.m,a)}function barrettSqrTo(a,b){a.squareTo(b);this.reduce(b)}function barrettMulTo(a,b,c){a.multiplyTo(b,c);this.reduce(c)}Barrett.prototype.convert=barrettConvert;
Barrett.prototype.revert=barrettRevert;Barrett.prototype.reduce=barrettReduce;Barrett.prototype.mulTo=barrettMulTo;Barrett.prototype.sqrTo=barrettSqrTo;
function bnModPow(a,b){var c=a.bitLength(),d,e=nbv(1),g;if(c<=0)return e;else d=c<18?1:c<48?3:c<144?4:c<768?5:6;g=c<8?new Classic(b):b.isEven()?new Barrett(b):new Montgomery(b);var h=[],f=3,o=d-1,p=(1<<d)-1;h[1]=g.convert(this);if(d>1){c=nbi();for(g.sqrTo(h[1],c);f<=p;)h[f]=nbi(),g.mulTo(c,h[f-2],h[f]),f+=2}for(var q=a.t-1,n,k=!0,j=nbi(),c=nbits(a[q])-1;q>=0;){c>=o?n=a[q]>>c-o&p:(n=(a[q]&(1<<c+1)-1)<<o-c,q>0&&(n|=a[q-1]>>this.DB+c-o));for(f=d;(n&1)==0;)n>>=1,--f;if((c-=f)<0)c+=this.DB,--q;if(k)h[n].copyTo(e),
k=!1;else{for(;f>1;)g.sqrTo(e,j),g.sqrTo(j,e),f-=2;f>0?g.sqrTo(e,j):(f=e,e=j,j=f);g.mulTo(j,h[n],e)}for(;q>=0&&(a[q]&1<<c)==0;)g.sqrTo(e,j),f=e,e=j,j=f,--c<0&&(c=this.DB-1,--q)}return g.revert(e)}
function bnGCD(a){var b=this.s<0?this.negate():this.clone(),a=a.s<0?a.negate():a.clone();if(b.compareTo(a)<0)var c=b,b=a,a=c;var c=b.getLowestSetBit(),d=a.getLowestSetBit();if(d<0)return b;c<d&&(d=c);d>0&&(b.rShiftTo(d,b),a.rShiftTo(d,a));for(;b.signum()>0;)(c=b.getLowestSetBit())>0&&b.rShiftTo(c,b),(c=a.getLowestSetBit())>0&&a.rShiftTo(c,a),b.compareTo(a)>=0?(b.subTo(a,b),b.rShiftTo(1,b)):(a.subTo(b,a),a.rShiftTo(1,a));d>0&&a.lShiftTo(d,a);return a}
function bnpModInt(a){if(a<=0)return 0;var b=this.DV%a,c=this.s<0?a-1:0;if(this.t>0)if(b==0)c=this[0]%a;else for(var d=this.t-1;d>=0;--d)c=(b*c+this[d])%a;return c}
function bnModInverse(a){var b=a.isEven();if(this.isEven()&&b||a.signum()==0)return BigInteger.ZERO;for(var c=a.clone(),d=this.clone(),e=nbv(1),g=nbv(0),h=nbv(0),f=nbv(1);c.signum()!=0;){for(;c.isEven();){c.rShiftTo(1,c);if(b){if(!e.isEven()||!g.isEven())e.addTo(this,e),g.subTo(a,g);e.rShiftTo(1,e)}else g.isEven()||g.subTo(a,g);g.rShiftTo(1,g)}for(;d.isEven();){d.rShiftTo(1,d);if(b){if(!h.isEven()||!f.isEven())h.addTo(this,h),f.subTo(a,f);h.rShiftTo(1,h)}else f.isEven()||f.subTo(a,f);f.rShiftTo(1,
f)}c.compareTo(d)>=0?(c.subTo(d,c),b&&e.subTo(h,e),g.subTo(f,g)):(d.subTo(c,d),b&&h.subTo(e,h),f.subTo(g,f))}if(d.compareTo(BigInteger.ONE)!=0)return BigInteger.ZERO;if(f.compareTo(a)>=0)return f.subtract(a);if(f.signum()<0)f.addTo(a,f);else return f;return f.signum()<0?f.add(a):f}
var lowprimes=[2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509,521,523,541,547,557,563,569,571,577,587,593,599,601,607,613,617,619,631,641,643,647,653,659,661,673,677,683,691,701,709,719,727,
733,739,743,751,757,761,769,773,787,797,809,811,821,823,827,829,839,853,857,859,863,877,881,883,887,907,911,919,929,937,941,947,953,967,971,977,983,991,997],lplim=67108864/lowprimes[lowprimes.length-1];
function bnIsProbablePrime(a){var b,c=this.abs();if(c.t==1&&c[0]<=lowprimes[lowprimes.length-1]){for(b=0;b<lowprimes.length;++b)if(c[0]==lowprimes[b])return!0;return!1}if(c.isEven())return!1;for(b=1;b<lowprimes.length;){for(var d=lowprimes[b],e=b+1;e<lowprimes.length&&d<lplim;)d*=lowprimes[e++];for(d=c.modInt(d);b<e;)if(d%lowprimes[b++]==0)return!1}return c.millerRabin(a)}
function bnpMillerRabin(a){var b=this.subtract(BigInteger.ONE),c=b.getLowestSetBit();if(c<=0)return!1;var d=b.shiftRight(c),a=a+1>>1;if(a>lowprimes.length)a=lowprimes.length;for(var e=nbi(),g=0;g<a;++g){e.fromInt(lowprimes[Math.floor(Math.random()*lowprimes.length)]);var h=e.modPow(d,this);if(h.compareTo(BigInteger.ONE)!=0&&h.compareTo(b)!=0){for(var f=1;f++<c&&h.compareTo(b)!=0;)if(h=h.modPowInt(2,this),h.compareTo(BigInteger.ONE)==0)return!1;if(h.compareTo(b)!=0)return!1}}return!0}
BigInteger.prototype.chunkSize=bnpChunkSize;BigInteger.prototype.toRadix=bnpToRadix;BigInteger.prototype.fromRadix=bnpFromRadix;BigInteger.prototype.fromNumber=bnpFromNumber;BigInteger.prototype.bitwiseTo=bnpBitwiseTo;BigInteger.prototype.changeBit=bnpChangeBit;BigInteger.prototype.addTo=bnpAddTo;BigInteger.prototype.dMultiply=bnpDMultiply;BigInteger.prototype.dAddOffset=bnpDAddOffset;BigInteger.prototype.multiplyLowerTo=bnpMultiplyLowerTo;BigInteger.prototype.multiplyUpperTo=bnpMultiplyUpperTo;
BigInteger.prototype.modInt=bnpModInt;BigInteger.prototype.millerRabin=bnpMillerRabin;BigInteger.prototype.clone=bnClone;BigInteger.prototype.intValue=bnIntValue;BigInteger.prototype.byteValue=bnByteValue;BigInteger.prototype.shortValue=bnShortValue;BigInteger.prototype.signum=bnSigNum;BigInteger.prototype.toByteArray=bnToByteArray;BigInteger.prototype.equals=bnEquals;BigInteger.prototype.min=bnMin;BigInteger.prototype.max=bnMax;BigInteger.prototype.and=bnAnd;BigInteger.prototype.or=bnOr;
BigInteger.prototype.xor=bnXor;BigInteger.prototype.andNot=bnAndNot;BigInteger.prototype.not=bnNot;BigInteger.prototype.shiftLeft=bnShiftLeft;BigInteger.prototype.shiftRight=bnShiftRight;BigInteger.prototype.getLowestSetBit=bnGetLowestSetBit;BigInteger.prototype.bitCount=bnBitCount;BigInteger.prototype.testBit=bnTestBit;BigInteger.prototype.setBit=bnSetBit;BigInteger.prototype.clearBit=bnClearBit;BigInteger.prototype.flipBit=bnFlipBit;BigInteger.prototype.add=bnAdd;BigInteger.prototype.subtract=bnSubtract;
BigInteger.prototype.multiply=bnMultiply;BigInteger.prototype.divide=bnDivide;BigInteger.prototype.remainder=bnRemainder;BigInteger.prototype.divideAndRemainder=bnDivideAndRemainder;BigInteger.prototype.modPow=bnModPow;BigInteger.prototype.modInverse=bnModInverse;BigInteger.prototype.pow=bnPow;BigInteger.prototype.gcd=bnGCD;BigInteger.prototype.isProbablePrime=bnIsProbablePrime;BigInteger.prototype.square=bnSquare;
(function(a,b,c,d,e,g,h){function f(a){var b,d,e=this,g=a.length,f=0,h=e.i=e.j=e.m=0;e.S=[];e.c=[];for(g||(a=[g++]);f<c;)e.S[f]=f++;for(f=0;f<c;f++)b=e.S[f],h=h+b+a[f%g]&c-1,d=e.S[h],e.S[f]=d,e.S[h]=b;e.g=function(a){var b=e.S,d=e.i+1&c-1,g=b[d],f=e.j+g&c-1,h=b[f];b[d]=h;b[f]=g;for(var k=b[g+h&c-1];--a;)d=d+1&c-1,g=b[d],f=f+g&c-1,h=b[f],b[d]=h,b[f]=g,k=k*c+b[g+h&c-1];e.i=d;e.j=f;return k};e.g(c)}function o(a,b,c,d,e){c=[];e=typeof a;if(b&&e=="object")for(d in a)if(d.indexOf("S")<5)try{c.push(o(a[d],
b-1))}catch(g){}return c.length?c:a+(e!="string"?"\x00":"")}function p(a,b,d,e){a+="";for(e=d=0;e<a.length;e++){var g=b,f=e&c-1,h=(d^=b[e&c-1]*19)+a.charCodeAt(e);g[f]=h&c-1}a="";for(e in b)a+=String.fromCharCode(b[e]);return a}b.seedrandom=function(q,n){var k=[],j,q=p(o(n?[q,a]:arguments.length?q:[(new Date).getTime(),a,window],3),k);j=new f(k);p(j.S,a);b.random=function(){for(var a=j.g(d),b=h,f=0;a<e;)a=(a+f)*c,b*=c,f=j.g(1);for(;a>=g;)a/=2,b/=2,f>>>=1;return(a+f)/b};return q};h=b.pow(c,d);e=b.pow(2,
e);g=e*2;p(b.random(),a)})([],Math,256,6,52);function SeededRandom(){}function SRnextBytes(a){var b;for(b=0;b<a.length;b++)a[b]=Math.floor(Math.random()*256)}SeededRandom.prototype.nextBytes=SRnextBytes;function Arcfour(){this.j=this.i=0;this.S=[]}function ARC4init(a){var b,c,d;for(b=0;b<256;++b)this.S[b]=b;for(b=c=0;b<256;++b)c=c+this.S[b]+a[b%a.length]&255,d=this.S[b],this.S[b]=this.S[c],this.S[c]=d;this.j=this.i=0}
function ARC4next(){var a;this.i=this.i+1&255;this.j=this.j+this.S[this.i]&255;a=this.S[this.i];this.S[this.i]=this.S[this.j];this.S[this.j]=a;return this.S[a+this.S[this.i]&255]}Arcfour.prototype.init=ARC4init;Arcfour.prototype.next=ARC4next;function prng_newstate(){return new Arcfour}var rng_psize=256,rng_state,rng_pool,rng_pptr;
function rng_seed_int(a){rng_pool[rng_pptr++]^=a&255;rng_pool[rng_pptr++]^=a>>8&255;rng_pool[rng_pptr++]^=a>>16&255;rng_pool[rng_pptr++]^=a>>24&255;rng_pptr>=rng_psize&&(rng_pptr-=rng_psize)}function rng_seed_time(){rng_seed_int((new Date).getTime())}
if(rng_pool==null){rng_pool=[];rng_pptr=0;var t;if(navigator.appName=="Netscape"&&navigator.appVersion<"5"&&window.crypto){var z=window.crypto.random(32);for(t=0;t<z.length;++t)rng_pool[rng_pptr++]=z.charCodeAt(t)&255}for(;rng_pptr<rng_psize;)t=Math.floor(65536*Math.random()),rng_pool[rng_pptr++]=t>>>8,rng_pool[rng_pptr++]=t&255;rng_pptr=0;rng_seed_time()}
function rng_get_byte(){if(rng_state==null){rng_seed_time();rng_state=prng_newstate();rng_state.init(rng_pool);for(rng_pptr=0;rng_pptr<rng_pool.length;++rng_pptr)rng_pool[rng_pptr]=0;rng_pptr=0}return rng_state.next()}function rng_get_bytes(a){var b;for(b=0;b<a.length;++b)a[b]=rng_get_byte()}function SecureRandom(){}SecureRandom.prototype.nextBytes=rng_get_bytes;
function SHA256(a){function b(a,b){var c=(a&65535)+(b&65535);return(a>>16)+(b>>16)+(c>>16)<<16|c&65535}function c(a,b){return a>>>b|a<<32-b}a=function(a){for(var a=a.replace(/\r\n/g,"\n"),b="",c=0;c<a.length;c++){var h=a.charCodeAt(c);h<128?b+=String.fromCharCode(h):(h>127&&h<2048?b+=String.fromCharCode(h>>6|192):(b+=String.fromCharCode(h>>12|224),b+=String.fromCharCode(h>>6&63|128)),b+=String.fromCharCode(h&63|128))}return b}(a);return function(a){for(var b="",c=0;c<a.length*4;c++)b+="0123456789abcdef".charAt(a[c>>
2]>>(3-c%4)*8+4&15)+"0123456789abcdef".charAt(a[c>>2]>>(3-c%4)*8&15);return b}(function(a,e){var g=[1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,
2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298],h=[1779033703,3144134277,1013904242,2773480762,1359893119,2600822924,528734635,1541459225],f=Array(64),o,p,q,n,k,j,l,m,s,r,u,w;a[e>>5]|=128<<24-e%32;a[(e+64>>9<<4)+15]=e;for(s=0;s<a.length;s+=16){o=h[0];p=h[1];q=h[2];n=h[3];
k=h[4];j=h[5];l=h[6];m=h[7];for(r=0;r<64;r++)f[r]=r<16?a[r+s]:b(b(b(c(f[r-2],17)^c(f[r-2],19)^f[r-2]>>>10,f[r-7]),c(f[r-15],7)^c(f[r-15],18)^f[r-15]>>>3),f[r-16]),u=b(b(b(b(m,c(k,6)^c(k,11)^c(k,25)),k&j^~k&l),g[r]),f[r]),w=b(c(o,2)^c(o,13)^c(o,22),o&p^o&q^p&q),m=l,l=j,j=k,k=b(n,u),n=q,q=p,p=o,o=b(u,w);h[0]=b(o,h[0]);h[1]=b(p,h[1]);h[2]=b(q,h[2]);h[3]=b(n,h[3]);h[4]=b(k,h[4]);h[5]=b(j,h[5]);h[6]=b(l,h[6]);h[7]=b(m,h[7])}return h}(function(a){for(var b=[],c=0;c<a.length*8;c+=8)b[c>>5]|=(a.charCodeAt(c/
8)&255)<<24-c%32;return b}(a),a.length*8))}var sha256={hex:function(a){return SHA256(a)}};
function SHA1(a){function b(a,b){return a<<b|a>>>32-b}function c(a){var b="",c,d;for(c=7;c>=0;c--)d=a>>>c*4&15,b+=d.toString(16);return b}var d,e,g=Array(80),h=1732584193,f=4023233417,o=2562383102,p=271733878,q=3285377520,n,k,j,l,m,a=function(a){for(var a=a.replace(/\r\n/g,"\n"),b="",c=0;c<a.length;c++){var d=a.charCodeAt(c);d<128?b+=String.fromCharCode(d):(d>127&&d<2048?b+=String.fromCharCode(d>>6|192):(b+=String.fromCharCode(d>>12|224),b+=String.fromCharCode(d>>6&63|128)),b+=String.fromCharCode(d&
63|128))}return b}(a);n=a.length;var s=[];for(d=0;d<n-3;d+=4)e=a.charCodeAt(d)<<24|a.charCodeAt(d+1)<<16|a.charCodeAt(d+2)<<8|a.charCodeAt(d+3),s.push(e);switch(n%4){case 0:d=2147483648;break;case 1:d=a.charCodeAt(n-1)<<24|8388608;break;case 2:d=a.charCodeAt(n-2)<<24|a.charCodeAt(n-1)<<16|32768;break;case 3:d=a.charCodeAt(n-3)<<24|a.charCodeAt(n-2)<<16|a.charCodeAt(n-1)<<8|128}for(s.push(d);s.length%16!=14;)s.push(0);s.push(n>>>29);s.push(n<<3&4294967295);for(a=0;a<s.length;a+=16){for(d=0;d<16;d++)g[d]=
s[a+d];for(d=16;d<=79;d++)g[d]=b(g[d-3]^g[d-8]^g[d-14]^g[d-16],1);e=h;n=f;k=o;j=p;l=q;for(d=0;d<=19;d++)m=b(e,5)+(n&k|~n&j)+l+g[d]+1518500249&4294967295,l=j,j=k,k=b(n,30),n=e,e=m;for(d=20;d<=39;d++)m=b(e,5)+(n^k^j)+l+g[d]+1859775393&4294967295,l=j,j=k,k=b(n,30),n=e,e=m;for(d=40;d<=59;d++)m=b(e,5)+(n&k|n&j|k&j)+l+g[d]+2400959708&4294967295,l=j,j=k,k=b(n,30),n=e,e=m;for(d=60;d<=79;d++)m=b(e,5)+(n^k^j)+l+g[d]+3395469782&4294967295,l=j,j=k,k=b(n,30),n=e,e=m;h=h+e&4294967295;f=f+n&4294967295;o=o+k&4294967295;
p=p+j&4294967295;q=q+l&4294967295}m=c(h)+c(f)+c(o)+c(p)+c(q);return m.toLowerCase()}
var sha1={hex:function(a){return SHA1(a)}},MD5=function(a){function b(a,b){var c,d,e,f,g;e=a&2147483648;f=b&2147483648;c=a&1073741824;d=b&1073741824;g=(a&1073741823)+(b&1073741823);return c&d?g^2147483648^e^f:c|d?g&1073741824?g^3221225472^e^f:g^1073741824^e^f:g^e^f}function c(a,c,d,e,f,g,h){a=b(a,b(b(c&d|~c&e,f),h));return b(a<<g|a>>>32-g,c)}function d(a,c,d,e,f,g,h){a=b(a,b(b(c&e|d&~e,f),h));return b(a<<g|a>>>32-g,c)}function e(a,c,d,e,f,g,h){a=b(a,b(b(c^d^e,f),h));return b(a<<g|a>>>32-g,c)}function g(a,
c,d,e,f,g,h){a=b(a,b(b(d^(c|~e),f),h));return b(a<<g|a>>>32-g,c)}function h(a){var b="",c="",d;for(d=0;d<=3;d++)c=a>>>d*8&255,c="0"+c.toString(16),b+=c.substr(c.length-2,2);return b}var f=[],o,p,q,n,k,j,l,m,a=function(a){for(var a=a.replace(/\r\n/g,"\n"),b="",c=0;c<a.length;c++){var d=a.charCodeAt(c);d<128?b+=String.fromCharCode(d):(d>127&&d<2048?b+=String.fromCharCode(d>>6|192):(b+=String.fromCharCode(d>>12|224),b+=String.fromCharCode(d>>6&63|128)),b+=String.fromCharCode(d&63|128))}return b}(a),
f=function(a){var b,c=a.length;b=c+8;for(var d=((b-b%64)/64+1)*16,e=Array(d-1),f=0,g=0;g<c;)b=(g-g%4)/4,f=g%4*8,e[b]|=a.charCodeAt(g)<<f,g++;e[(g-g%4)/4]|=128<<g%4*8;e[d-2]=c<<3;e[d-1]=c>>>29;return e}(a);k=1732584193;j=4023233417;l=2562383102;m=271733878;for(a=0;a<f.length;a+=16)o=k,p=j,q=l,n=m,k=c(k,j,l,m,f[a+0],7,3614090360),m=c(m,k,j,l,f[a+1],12,3905402710),l=c(l,m,k,j,f[a+2],17,606105819),j=c(j,l,m,k,f[a+3],22,3250441966),k=c(k,j,l,m,f[a+4],7,4118548399),m=c(m,k,j,l,f[a+5],12,1200080426),l=c(l,
m,k,j,f[a+6],17,2821735955),j=c(j,l,m,k,f[a+7],22,4249261313),k=c(k,j,l,m,f[a+8],7,1770035416),m=c(m,k,j,l,f[a+9],12,2336552879),l=c(l,m,k,j,f[a+10],17,4294925233),j=c(j,l,m,k,f[a+11],22,2304563134),k=c(k,j,l,m,f[a+12],7,1804603682),m=c(m,k,j,l,f[a+13],12,4254626195),l=c(l,m,k,j,f[a+14],17,2792965006),j=c(j,l,m,k,f[a+15],22,1236535329),k=d(k,j,l,m,f[a+1],5,4129170786),m=d(m,k,j,l,f[a+6],9,3225465664),l=d(l,m,k,j,f[a+11],14,643717713),j=d(j,l,m,k,f[a+0],20,3921069994),k=d(k,j,l,m,f[a+5],5,3593408605),
m=d(m,k,j,l,f[a+10],9,38016083),l=d(l,m,k,j,f[a+15],14,3634488961),j=d(j,l,m,k,f[a+4],20,3889429448),k=d(k,j,l,m,f[a+9],5,568446438),m=d(m,k,j,l,f[a+14],9,3275163606),l=d(l,m,k,j,f[a+3],14,4107603335),j=d(j,l,m,k,f[a+8],20,1163531501),k=d(k,j,l,m,f[a+13],5,2850285829),m=d(m,k,j,l,f[a+2],9,4243563512),l=d(l,m,k,j,f[a+7],14,1735328473),j=d(j,l,m,k,f[a+12],20,2368359562),k=e(k,j,l,m,f[a+5],4,4294588738),m=e(m,k,j,l,f[a+8],11,2272392833),l=e(l,m,k,j,f[a+11],16,1839030562),j=e(j,l,m,k,f[a+14],23,4259657740),
k=e(k,j,l,m,f[a+1],4,2763975236),m=e(m,k,j,l,f[a+4],11,1272893353),l=e(l,m,k,j,f[a+7],16,4139469664),j=e(j,l,m,k,f[a+10],23,3200236656),k=e(k,j,l,m,f[a+13],4,681279174),m=e(m,k,j,l,f[a+0],11,3936430074),l=e(l,m,k,j,f[a+3],16,3572445317),j=e(j,l,m,k,f[a+6],23,76029189),k=e(k,j,l,m,f[a+9],4,3654602809),m=e(m,k,j,l,f[a+12],11,3873151461),l=e(l,m,k,j,f[a+15],16,530742520),j=e(j,l,m,k,f[a+2],23,3299628645),k=g(k,j,l,m,f[a+0],6,4096336452),m=g(m,k,j,l,f[a+7],10,1126891415),l=g(l,m,k,j,f[a+14],15,2878612391),
j=g(j,l,m,k,f[a+5],21,4237533241),k=g(k,j,l,m,f[a+12],6,1700485571),m=g(m,k,j,l,f[a+3],10,2399980690),l=g(l,m,k,j,f[a+10],15,4293915773),j=g(j,l,m,k,f[a+1],21,2240044497),k=g(k,j,l,m,f[a+8],6,1873313359),m=g(m,k,j,l,f[a+15],10,4264355552),l=g(l,m,k,j,f[a+6],15,2734768916),j=g(j,l,m,k,f[a+13],21,1309151649),k=g(k,j,l,m,f[a+4],6,4149444226),m=g(m,k,j,l,f[a+11],10,3174756917),l=g(l,m,k,j,f[a+2],15,718787259),j=g(j,l,m,k,f[a+9],21,3951481745),k=b(k,o),j=b(j,p),l=b(l,q),m=b(m,n);return(h(k)+h(j)+h(l)+
h(m)).toLowerCase()};function parseBigInt(a,b){return new BigInteger(a,b)}function linebrk(a,b){for(var c="",d=0;d+b<a.length;)c+=a.substring(d,d+b)+"\n",d+=b;return c+a.substring(d,a.length)}function byte2Hex(a){return a<16?"0"+a.toString(16):a.toString(16)}
function pkcs1pad2(a,b){if(b<a.length+11)throw"Message too long for RSA (n="+b+", l="+a.length+")";for(var c=[],d=a.length-1;d>=0&&b>0;){var e=a.charCodeAt(d--);e<128?c[--b]=e:e>127&&e<2048?(c[--b]=e&63|128,c[--b]=e>>6|192):(c[--b]=e&63|128,c[--b]=e>>6&63|128,c[--b]=e>>12|224)}c[--b]=0;d=new SecureRandom;for(e=[];b>2;){for(e[0]=0;e[0]==0;)d.nextBytes(e);c[--b]=e[0]}c[--b]=2;c[--b]=0;return new BigInteger(c)}
function RSAKey(){this.n=null;this.e=0;this.coeff=this.dmq1=this.dmp1=this.q=this.p=this.d=null}function RSASetPublic(a,b){a!=null&&b!=null&&a.length>0&&b.length>0?(this.n=parseBigInt(a,16),this.e=parseInt(b,16)):alert("Invalid RSA public key")}function RSADoPublic(a){return a.modPowInt(this.e,this.n)}function RSAEncrypt(a){a=pkcs1pad2(a,this.n.bitLength()+7>>3);if(a==null)return null;a=this.doPublic(a);if(a==null)return null;a=a.toString(16);return(a.length&1)==0?a:"0"+a}
RSAKey.prototype.doPublic=RSADoPublic;RSAKey.prototype.setPublic=RSASetPublic;RSAKey.prototype.encrypt=RSAEncrypt;function pkcs1unpad2(a,b){for(var c=a.toByteArray(),d=0;d<c.length&&c[d]==0;)++d;if(c.length-d!=b-1||c[d]!=2)return null;for(++d;c[d]!=0;)if(++d>=c.length)return null;for(var e="";++d<c.length;){var g=c[d]&255;g<128?e+=String.fromCharCode(g):g>191&&g<224?(e+=String.fromCharCode((g&31)<<6|c[d+1]&63),++d):(e+=String.fromCharCode((g&15)<<12|(c[d+1]&63)<<6|c[d+2]&63),d+=2)}return e}
function RSASetPrivate(a,b,c){a!=null&&b!=null&&a.length>0&&b.length>0?(this.n=parseBigInt(a,16),this.e=parseInt(b,16),this.d=parseBigInt(c,16)):alert("Invalid RSA private key")}
function RSASetPrivateEx(a,b,c,d,e,g,h,f){a!=null&&b!=null&&a.length>0&&b.length>0?(this.n=parseBigInt(a,16),this.e=parseInt(b,16),this.d=parseBigInt(c,16),this.p=parseBigInt(d,16),this.q=parseBigInt(e,16),this.dmp1=parseBigInt(g,16),this.dmq1=parseBigInt(h,16),this.coeff=parseBigInt(f,16)):alert("Invalid RSA private key")}
function RSAGenerate(a,b){var c=new SeededRandom,d=a>>1;this.e=parseInt(b,16);for(var e=new BigInteger(b,16);;){for(;;)if(this.p=new BigInteger(a-d,1,c),this.p.subtract(BigInteger.ONE).gcd(e).compareTo(BigInteger.ONE)==0&&this.p.isProbablePrime(10))break;for(;;)if(this.q=new BigInteger(d,1,c),this.q.subtract(BigInteger.ONE).gcd(e).compareTo(BigInteger.ONE)==0&&this.q.isProbablePrime(10))break;if(this.p.compareTo(this.q)<=0){var g=this.p;this.p=this.q;this.q=g}var g=this.p.subtract(BigInteger.ONE),
h=this.q.subtract(BigInteger.ONE),f=g.multiply(h);if(f.gcd(e).compareTo(BigInteger.ONE)==0){this.n=this.p.multiply(this.q);this.d=e.modInverse(f);this.dmp1=this.d.mod(g);this.dmq1=this.d.mod(h);this.coeff=this.q.modInverse(this.p);break}}}
function RSADoPrivate(a){if(this.p==null||this.q==null)return a.modPow(this.d,this.n);for(var b=a.mod(this.p).modPow(this.dmp1,this.p),a=a.mod(this.q).modPow(this.dmq1,this.q);b.compareTo(a)<0;)b=b.add(this.p);return b.subtract(a).multiply(this.coeff).mod(this.p).multiply(this.q).add(a)}function RSADecrypt(a){a=this.doPrivate(parseBigInt(a,16));return a==null?null:pkcs1unpad2(a,this.n.bitLength()+7>>3)}RSAKey.prototype.doPrivate=RSADoPrivate;RSAKey.prototype.setPrivate=RSASetPrivate;
RSAKey.prototype.setPrivateEx=RSASetPrivateEx;RSAKey.prototype.generate=RSAGenerate;RSAKey.prototype.decrypt=RSADecrypt;var _RSASIGN_DIHEAD=[];_RSASIGN_DIHEAD.sha1="3021300906052b0e03021a05000414";_RSASIGN_DIHEAD.sha256="3031300d060960864801650304020105000420";var _RSASIGN_HASHHEXFUNC=[];_RSASIGN_HASHHEXFUNC.sha1=sha1.hex;_RSASIGN_HASHHEXFUNC.sha256=sha256.hex;
function _rsasign_getHexPaddedDigestInfoForString(a,b,c){b/=4;for(var a=(0,_RSASIGN_HASHHEXFUNC[c])(a),c="00"+_RSASIGN_DIHEAD[c]+a,a="",b=b-4-c.length,d=0;d<b;d+=2)a+="ff";return sPaddedMessageHex="0001"+a+c}function _rsasign_signString(a,b){var c=_rsasign_getHexPaddedDigestInfoForString(a,this.n.bitLength(),b);return this.doPrivate(parseBigInt(c,16)).toString(16)}
function _rsasign_signStringWithSHA1(a){a=_rsasign_getHexPaddedDigestInfoForString(a,this.n.bitLength(),"sha1");return this.doPrivate(parseBigInt(a,16)).toString(16)}function _rsasign_signStringWithSHA256(a){a=_rsasign_getHexPaddedDigestInfoForString(a,this.n.bitLength(),"sha256");return this.doPrivate(parseBigInt(a,16)).toString(16)}function _rsasign_getDecryptSignatureBI(a,b,c){var d=new RSAKey;d.setPublic(b,c);return d.doPublic(a)}
function _rsasign_getHexDigestInfoFromSig(a,b,c){return _rsasign_getDecryptSignatureBI(a,b,c).toString(16).replace(/^1f+00/,"")}function _rsasign_getAlgNameAndHashFromHexDisgestInfo(a){for(var b in _RSASIGN_DIHEAD){var c=_RSASIGN_DIHEAD[b],d=c.length;if(a.substring(0,d)==c)return[b,a.substring(d)]}return[]}
function _rsasign_verifySignatureWithArgs(a,b,c,d){b=_rsasign_getHexDigestInfoFromSig(b,c,d);c=_rsasign_getAlgNameAndHashFromHexDisgestInfo(b);if(c.length==0)return!1;b=c[1];a=(0,_RSASIGN_HASHHEXFUNC[c[0]])(a);return b==a}function _rsasign_verifyHexSignatureForMessage(a,b){var c=parseBigInt(a,16);return _rsasign_verifySignatureWithArgs(b,c,this.n.toString(16),this.e.toString(16))}
function _rsasign_verifyString(a,b){var b=b.replace(/[ \n]+/g,""),c=this.doPublic(parseBigInt(b,16)).toString(16).replace(/^1f+00/,""),d=_rsasign_getAlgNameAndHashFromHexDisgestInfo(c);if(d.length==0)return!1;c=d[1];d=(0,_RSASIGN_HASHHEXFUNC[d[0]])(a);return c==d}RSAKey.prototype.signString=_rsasign_signString;RSAKey.prototype.signStringWithSHA1=_rsasign_signStringWithSHA1;RSAKey.prototype.signStringWithSHA256=_rsasign_signStringWithSHA256;RSAKey.prototype.verifyString=_rsasign_verifyString;
RSAKey.prototype.verifyHexSignatureForMessage=_rsasign_verifyHexSignatureForMessage;
var aes=function(){var a={Sbox:[99,124,119,123,242,107,111,197,48,1,103,43,254,215,171,118,202,130,201,125,250,89,71,240,173,212,162,175,156,164,114,192,183,253,147,38,54,63,247,204,52,165,229,241,113,216,49,21,4,199,35,195,24,150,5,154,7,18,128,226,235,39,178,117,9,131,44,26,27,110,90,160,82,59,214,179,41,227,47,132,83,209,0,237,32,252,177,91,106,203,190,57,74,76,88,207,208,239,170,251,67,77,51,133,69,249,2,127,80,60,159,168,81,163,64,143,146,157,56,245,188,182,218,33,16,255,243,210,205,12,19,236,
95,151,68,23,196,167,126,61,100,93,25,115,96,129,79,220,34,42,144,136,70,238,184,20,222,94,11,219,224,50,58,10,73,6,36,92,194,211,172,98,145,149,228,121,231,200,55,109,141,213,78,169,108,86,244,234,101,122,174,8,186,120,37,46,28,166,180,198,232,221,116,31,75,189,139,138,112,62,181,102,72,3,246,14,97,53,87,185,134,193,29,158,225,248,152,17,105,217,142,148,155,30,135,233,206,85,40,223,140,161,137,13,191,230,66,104,65,153,45,15,176,84,187,22],ShiftRowTab:[0,5,10,15,4,9,14,3,8,13,2,7,12,1,6,11]};a.Init=
function(){a.Sbox_Inv=Array(256);for(var b=0;b<256;b++)a.Sbox_Inv[a.Sbox[b]]=b;a.ShiftRowTab_Inv=Array(16);for(b=0;b<16;b++)a.ShiftRowTab_Inv[a.ShiftRowTab[b]]=b;a.xtime=Array(256);for(b=0;b<128;b++)a.xtime[b]=b<<1,a.xtime[128+b]=b<<1^27};a.Done=function(){delete a.Sbox_Inv;delete a.ShiftRowTab_Inv;delete a.xtime};a.ExpandKey=function(b){var c=b.length,d,e=1;switch(c){case 16:d=176;break;case 24:d=208;break;case 32:d=240;break;default:alert("my.ExpandKey: Only key lengths of 16, 24 or 32 bytes allowed!")}for(var g=
c;g<d;g+=4){var h=b.slice(g-4,g);if(g%c==0){if(h=[a.Sbox[h[1]]^e,a.Sbox[h[2]],a.Sbox[h[3]],a.Sbox[h[0]]],(e<<=1)>=256)e^=283}else c>24&&g%c==16&&(h=[a.Sbox[h[0]],a.Sbox[h[1]],a.Sbox[h[2]],a.Sbox[h[3]]]);for(var f=0;f<4;f++)b[g+f]=b[g+f-c]^h[f]}};a.Encrypt=function(b,c){var d=c.length;a.AddRoundKey(b,c.slice(0,16));for(var e=16;e<d-16;e+=16)a.SubBytes(b,a.Sbox),a.ShiftRows(b,a.ShiftRowTab),a.MixColumns(b),a.AddRoundKey(b,c.slice(e,e+16));a.SubBytes(b,a.Sbox);a.ShiftRows(b,a.ShiftRowTab);a.AddRoundKey(b,
c.slice(e,d))};a.Decrypt=function(b,c){var d=c.length;a.AddRoundKey(b,c.slice(d-16,d));a.ShiftRows(b,a.ShiftRowTab_Inv);a.SubBytes(b,a.Sbox_Inv);for(d-=32;d>=16;d-=16)a.AddRoundKey(b,c.slice(d,d+16)),a.MixColumns_Inv(b),a.ShiftRows(b,a.ShiftRowTab_Inv),a.SubBytes(b,a.Sbox_Inv);a.AddRoundKey(b,c.slice(0,16))};a.SubBytes=function(a,c){for(var d=0;d<16;d++)a[d]=c[a[d]]};a.AddRoundKey=function(a,c){for(var d=0;d<16;d++)a[d]^=c[d]};a.ShiftRows=function(a,c){for(var d=[].concat(a),e=0;e<16;e++)a[e]=d[c[e]]};
a.MixColumns=function(b){for(var c=0;c<16;c+=4){var d=b[c+0],e=b[c+1],g=b[c+2],h=b[c+3],f=d^e^g^h;b[c+0]^=f^a.xtime[d^e];b[c+1]^=f^a.xtime[e^g];b[c+2]^=f^a.xtime[g^h];b[c+3]^=f^a.xtime[h^d]}};a.MixColumns_Inv=function(b){for(var c=0;c<16;c+=4){var d=b[c+0],e=b[c+1],g=b[c+2],h=b[c+3],f=d^e^g^h,o=a.xtime[f],p=a.xtime[a.xtime[o^d^g]]^f;f^=a.xtime[a.xtime[o^e^h]];b[c+0]^=p^a.xtime[d^e];b[c+1]^=f^a.xtime[e^g];b[c+2]^=p^a.xtime[g^h];b[c+3]^=f^a.xtime[h^d]}};return a}(),cryptico=function(){var a={};aes.Init();
a.b256to64=function(a){var c,d,e,g="",h=0,f=0,o=a.length;for(e=0;e<o;e++)d=a.charCodeAt(e),f==0?(g+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt(d>>2&63),c=(d&3)<<4):f==1?(g+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt(c|d>>4&15),c=(d&15)<<2):f==2&&(g+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt(c|d>>6&3),h+=1,g+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt(d&63)),h+=1,f+=1,f==3&&
(f=0);f>0&&(g+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt(c),g+="=");f==1&&(g+="=");return g};a.b64to256=function(a){var c,d,e="",g=0,h=0,f=a.length;for(d=0;d<f;d++)c="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".indexOf(a.charAt(d)),c>=0&&(g&&(e+=String.fromCharCode(h|c>>6-g&255)),g=g+2&7,h=c<<g&255);return e};a.b16to64=function(a){var c,d,e="";a.length%2==1&&(a="0"+a);for(c=0;c+3<=a.length;c+=3)d=parseInt(a.substring(c,c+3),16),e+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt(d>>
6)+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt(d&63);c+1==a.length?(d=parseInt(a.substring(c,c+1),16),e+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt(d<<2)):c+2==a.length&&(d=parseInt(a.substring(c,c+2),16),e+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt(d>>2)+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt((d&3)<<4));for(;(e.length&3)>0;)e+="=";return e};a.b64to16=function(a){var c="",
d,e=0,g;for(d=0;d<a.length;++d){if(a.charAt(d)=="=")break;v="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".indexOf(a.charAt(d));v<0||(e==0?(c+=int2char(v>>2),g=v&3,e=1):e==1?(c+=int2char(g<<2|v>>4),g=v&15,e=2):e==2?(c+=int2char(g),c+=int2char(v>>2),g=v&3,e=3):(c+=int2char(g<<2|v>>4),c+=int2char(v&15),e=0))}e==1&&(c+=int2char(g<<2));return c};a.string2bytes=function(a){for(var c=[],d=0;d<a.length;d++)c.push(a.charCodeAt(d));return c};a.bytes2string=function(a){for(var c="",d=0;d<
a.length;d++)c+=String.fromCharCode(a[d]);return c};a.blockXOR=function(a,c){for(var d=Array(16),e=0;e<16;e++)d[e]=a[e]^c[e];return d};a.blockIV=function(){var a=new SecureRandom,c=Array(16);a.nextBytes(c);return c};a.pad16=function(a){var c=a.slice(0),d=(16-a.length%16)%16;for(i=a.length;i<a.length+d;i++)c.push(0);return c};a.depad=function(a){for(a=a.slice(0);a[a.length-1]==0;)a=a.slice(0,a.length-1);return a};a.encryptAESCBC=function(b,c){var d=c.slice(0);aes.ExpandKey(d);for(var e=a.string2bytes(b),
e=a.pad16(e),g=a.blockIV(),h=0;h<e.length/16;h++){var f=e.slice(h*16,h*16+16),o=g.slice(h*16,h*16+16),f=a.blockXOR(o,f);aes.Encrypt(f,d);g=g.concat(f)}d=a.bytes2string(g);return a.b256to64(d)};a.decryptAESCBC=function(b,c){var d=c.slice(0);aes.ExpandKey(d);for(var b=a.b64to256(b),e=a.string2bytes(b),g=[],h=1;h<e.length/16;h++){var f=e.slice(h*16,h*16+16),o=e.slice((h-1)*16,(h-1)*16+16);aes.Decrypt(f,d);f=a.blockXOR(o,f);g=g.concat(f)}g=a.depad(g);return a.bytes2string(g)};a.wrap60=function(a){for(var c=
"",d=0;d<a.length;d++)d%60==0&&d!=0&&(c+="\n"),c+=a[d];return c};a.generateAESKey=function(){var a=Array(16);(new SecureRandom).nextBytes(a);return a};a.generateRSAKey=function(a,c){Math.seedrandom(sha256.hex(a));var d=new RSAKey;d.generate(c,"10001");return d};a.publicKeyString=function(b){return pubkey=b.n.toString(16)};a.publicKeyID=function(a){return MD5(a)};a.publicKeyFromString=function(b){var b=b.split("|")[0],c=new RSAKey;c.setPublic(b,"10001");return c};a.encrypt=function(b,
c,d){var e="";try{var h=a.publicKeyFromString(c);e+=h.encrypt(b)+"?"}catch(f){return{status:"Invalid public key"}};return{status:"success",cipher:e}};a.decrypt=function(b,c){var d=b.split("?"),e=c.decrypt(d[0]);return{status:"success",plaintext:e,signature:"unsigned"}};return a}();

View File

@@ -0,0 +1,35 @@
/*
CryptoJS v3.1.2
code.google.com/p/crypto-js
(c) 2009-2013 by Jeff Mott. All rights reserved.
code.google.com/p/crypto-js/wiki/License
*/
var CryptoJS=CryptoJS||function(u,p){var d={},l=d.lib={},s=function(){},t=l.Base={extend:function(a){s.prototype=this;var c=new s;a&&c.mixIn(a);c.hasOwnProperty("init")||(c.init=function(){c.$super.init.apply(this,arguments)});c.init.prototype=c;c.$super=this;return c},create:function(){var a=this.extend();a.init.apply(a,arguments);return a},init:function(){},mixIn:function(a){for(var c in a)a.hasOwnProperty(c)&&(this[c]=a[c]);a.hasOwnProperty("toString")&&(this.toString=a.toString)},clone:function(){return this.init.prototype.extend(this)}},
r=l.WordArray=t.extend({init:function(a,c){a=this.words=a||[];this.sigBytes=c!=p?c:4*a.length},toString:function(a){return(a||v).stringify(this)},concat:function(a){var c=this.words,e=a.words,j=this.sigBytes;a=a.sigBytes;this.clamp();if(j%4)for(var k=0;k<a;k++)c[j+k>>>2]|=(e[k>>>2]>>>24-8*(k%4)&255)<<24-8*((j+k)%4);else if(65535<e.length)for(k=0;k<a;k+=4)c[j+k>>>2]=e[k>>>2];else c.push.apply(c,e);this.sigBytes+=a;return this},clamp:function(){var a=this.words,c=this.sigBytes;a[c>>>2]&=4294967295<<
32-8*(c%4);a.length=u.ceil(c/4)},clone:function(){var a=t.clone.call(this);a.words=this.words.slice(0);return a},random:function(a){for(var c=[],e=0;e<a;e+=4)c.push(4294967296*u.random()|0);return new r.init(c,a)}}),w=d.enc={},v=w.Hex={stringify:function(a){var c=a.words;a=a.sigBytes;for(var e=[],j=0;j<a;j++){var k=c[j>>>2]>>>24-8*(j%4)&255;e.push((k>>>4).toString(16));e.push((k&15).toString(16))}return e.join("")},parse:function(a){for(var c=a.length,e=[],j=0;j<c;j+=2)e[j>>>3]|=parseInt(a.substr(j,
2),16)<<24-4*(j%8);return new r.init(e,c/2)}},b=w.Latin1={stringify:function(a){var c=a.words;a=a.sigBytes;for(var e=[],j=0;j<a;j++)e.push(String.fromCharCode(c[j>>>2]>>>24-8*(j%4)&255));return e.join("")},parse:function(a){for(var c=a.length,e=[],j=0;j<c;j++)e[j>>>2]|=(a.charCodeAt(j)&255)<<24-8*(j%4);return new r.init(e,c)}},x=w.Utf8={stringify:function(a){try{return decodeURIComponent(escape(b.stringify(a)))}catch(c){throw Error("Malformed UTF-8 data");}},parse:function(a){return b.parse(unescape(encodeURIComponent(a)))}},
q=l.BufferedBlockAlgorithm=t.extend({reset:function(){this._data=new r.init;this._nDataBytes=0},_append:function(a){"string"==typeof a&&(a=x.parse(a));this._data.concat(a);this._nDataBytes+=a.sigBytes},_process:function(a){var c=this._data,e=c.words,j=c.sigBytes,k=this.blockSize,b=j/(4*k),b=a?u.ceil(b):u.max((b|0)-this._minBufferSize,0);a=b*k;j=u.min(4*a,j);if(a){for(var q=0;q<a;q+=k)this._doProcessBlock(e,q);q=e.splice(0,a);c.sigBytes-=j}return new r.init(q,j)},clone:function(){var a=t.clone.call(this);
a._data=this._data.clone();return a},_minBufferSize:0});l.Hasher=q.extend({cfg:t.extend(),init:function(a){this.cfg=this.cfg.extend(a);this.reset()},reset:function(){q.reset.call(this);this._doReset()},update:function(a){this._append(a);this._process();return this},finalize:function(a){a&&this._append(a);return this._doFinalize()},blockSize:16,_createHelper:function(a){return function(b,e){return(new a.init(e)).finalize(b)}},_createHmacHelper:function(a){return function(b,e){return(new n.HMAC.init(a,
e)).finalize(b)}}});var n=d.algo={};return d}(Math);
(function(){var u=CryptoJS,p=u.lib.WordArray;u.enc.Base64={stringify:function(d){var l=d.words,p=d.sigBytes,t=this._map;d.clamp();d=[];for(var r=0;r<p;r+=3)for(var w=(l[r>>>2]>>>24-8*(r%4)&255)<<16|(l[r+1>>>2]>>>24-8*((r+1)%4)&255)<<8|l[r+2>>>2]>>>24-8*((r+2)%4)&255,v=0;4>v&&r+0.75*v<p;v++)d.push(t.charAt(w>>>6*(3-v)&63));if(l=t.charAt(64))for(;d.length%4;)d.push(l);return d.join("")},parse:function(d){var l=d.length,s=this._map,t=s.charAt(64);t&&(t=d.indexOf(t),-1!=t&&(l=t));for(var t=[],r=0,w=0;w<
l;w++)if(w%4){var v=s.indexOf(d.charAt(w-1))<<2*(w%4),b=s.indexOf(d.charAt(w))>>>6-2*(w%4);t[r>>>2]|=(v|b)<<24-8*(r%4);r++}return p.create(t,r)},_map:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="}})();
(function(u){function p(b,n,a,c,e,j,k){b=b+(n&a|~n&c)+e+k;return(b<<j|b>>>32-j)+n}function d(b,n,a,c,e,j,k){b=b+(n&c|a&~c)+e+k;return(b<<j|b>>>32-j)+n}function l(b,n,a,c,e,j,k){b=b+(n^a^c)+e+k;return(b<<j|b>>>32-j)+n}function s(b,n,a,c,e,j,k){b=b+(a^(n|~c))+e+k;return(b<<j|b>>>32-j)+n}for(var t=CryptoJS,r=t.lib,w=r.WordArray,v=r.Hasher,r=t.algo,b=[],x=0;64>x;x++)b[x]=4294967296*u.abs(u.sin(x+1))|0;r=r.MD5=v.extend({_doReset:function(){this._hash=new w.init([1732584193,4023233417,2562383102,271733878])},
_doProcessBlock:function(q,n){for(var a=0;16>a;a++){var c=n+a,e=q[c];q[c]=(e<<8|e>>>24)&16711935|(e<<24|e>>>8)&4278255360}var a=this._hash.words,c=q[n+0],e=q[n+1],j=q[n+2],k=q[n+3],z=q[n+4],r=q[n+5],t=q[n+6],w=q[n+7],v=q[n+8],A=q[n+9],B=q[n+10],C=q[n+11],u=q[n+12],D=q[n+13],E=q[n+14],x=q[n+15],f=a[0],m=a[1],g=a[2],h=a[3],f=p(f,m,g,h,c,7,b[0]),h=p(h,f,m,g,e,12,b[1]),g=p(g,h,f,m,j,17,b[2]),m=p(m,g,h,f,k,22,b[3]),f=p(f,m,g,h,z,7,b[4]),h=p(h,f,m,g,r,12,b[5]),g=p(g,h,f,m,t,17,b[6]),m=p(m,g,h,f,w,22,b[7]),
f=p(f,m,g,h,v,7,b[8]),h=p(h,f,m,g,A,12,b[9]),g=p(g,h,f,m,B,17,b[10]),m=p(m,g,h,f,C,22,b[11]),f=p(f,m,g,h,u,7,b[12]),h=p(h,f,m,g,D,12,b[13]),g=p(g,h,f,m,E,17,b[14]),m=p(m,g,h,f,x,22,b[15]),f=d(f,m,g,h,e,5,b[16]),h=d(h,f,m,g,t,9,b[17]),g=d(g,h,f,m,C,14,b[18]),m=d(m,g,h,f,c,20,b[19]),f=d(f,m,g,h,r,5,b[20]),h=d(h,f,m,g,B,9,b[21]),g=d(g,h,f,m,x,14,b[22]),m=d(m,g,h,f,z,20,b[23]),f=d(f,m,g,h,A,5,b[24]),h=d(h,f,m,g,E,9,b[25]),g=d(g,h,f,m,k,14,b[26]),m=d(m,g,h,f,v,20,b[27]),f=d(f,m,g,h,D,5,b[28]),h=d(h,f,
m,g,j,9,b[29]),g=d(g,h,f,m,w,14,b[30]),m=d(m,g,h,f,u,20,b[31]),f=l(f,m,g,h,r,4,b[32]),h=l(h,f,m,g,v,11,b[33]),g=l(g,h,f,m,C,16,b[34]),m=l(m,g,h,f,E,23,b[35]),f=l(f,m,g,h,e,4,b[36]),h=l(h,f,m,g,z,11,b[37]),g=l(g,h,f,m,w,16,b[38]),m=l(m,g,h,f,B,23,b[39]),f=l(f,m,g,h,D,4,b[40]),h=l(h,f,m,g,c,11,b[41]),g=l(g,h,f,m,k,16,b[42]),m=l(m,g,h,f,t,23,b[43]),f=l(f,m,g,h,A,4,b[44]),h=l(h,f,m,g,u,11,b[45]),g=l(g,h,f,m,x,16,b[46]),m=l(m,g,h,f,j,23,b[47]),f=s(f,m,g,h,c,6,b[48]),h=s(h,f,m,g,w,10,b[49]),g=s(g,h,f,m,
E,15,b[50]),m=s(m,g,h,f,r,21,b[51]),f=s(f,m,g,h,u,6,b[52]),h=s(h,f,m,g,k,10,b[53]),g=s(g,h,f,m,B,15,b[54]),m=s(m,g,h,f,e,21,b[55]),f=s(f,m,g,h,v,6,b[56]),h=s(h,f,m,g,x,10,b[57]),g=s(g,h,f,m,t,15,b[58]),m=s(m,g,h,f,D,21,b[59]),f=s(f,m,g,h,z,6,b[60]),h=s(h,f,m,g,C,10,b[61]),g=s(g,h,f,m,j,15,b[62]),m=s(m,g,h,f,A,21,b[63]);a[0]=a[0]+f|0;a[1]=a[1]+m|0;a[2]=a[2]+g|0;a[3]=a[3]+h|0},_doFinalize:function(){var b=this._data,n=b.words,a=8*this._nDataBytes,c=8*b.sigBytes;n[c>>>5]|=128<<24-c%32;var e=u.floor(a/
4294967296);n[(c+64>>>9<<4)+15]=(e<<8|e>>>24)&16711935|(e<<24|e>>>8)&4278255360;n[(c+64>>>9<<4)+14]=(a<<8|a>>>24)&16711935|(a<<24|a>>>8)&4278255360;b.sigBytes=4*(n.length+1);this._process();b=this._hash;n=b.words;for(a=0;4>a;a++)c=n[a],n[a]=(c<<8|c>>>24)&16711935|(c<<24|c>>>8)&4278255360;return b},clone:function(){var b=v.clone.call(this);b._hash=this._hash.clone();return b}});t.MD5=v._createHelper(r);t.HmacMD5=v._createHmacHelper(r)})(Math);
(function(){var u=CryptoJS,p=u.lib,d=p.Base,l=p.WordArray,p=u.algo,s=p.EvpKDF=d.extend({cfg:d.extend({keySize:4,hasher:p.MD5,iterations:1}),init:function(d){this.cfg=this.cfg.extend(d)},compute:function(d,r){for(var p=this.cfg,s=p.hasher.create(),b=l.create(),u=b.words,q=p.keySize,p=p.iterations;u.length<q;){n&&s.update(n);var n=s.update(d).finalize(r);s.reset();for(var a=1;a<p;a++)n=s.finalize(n),s.reset();b.concat(n)}b.sigBytes=4*q;return b}});u.EvpKDF=function(d,l,p){return s.create(p).compute(d,
l)}})();
CryptoJS.lib.Cipher||function(u){var p=CryptoJS,d=p.lib,l=d.Base,s=d.WordArray,t=d.BufferedBlockAlgorithm,r=p.enc.Base64,w=p.algo.EvpKDF,v=d.Cipher=t.extend({cfg:l.extend(),createEncryptor:function(e,a){return this.create(this._ENC_XFORM_MODE,e,a)},createDecryptor:function(e,a){return this.create(this._DEC_XFORM_MODE,e,a)},init:function(e,a,b){this.cfg=this.cfg.extend(b);this._xformMode=e;this._key=a;this.reset()},reset:function(){t.reset.call(this);this._doReset()},process:function(e){this._append(e);return this._process()},
finalize:function(e){e&&this._append(e);return this._doFinalize()},keySize:4,ivSize:4,_ENC_XFORM_MODE:1,_DEC_XFORM_MODE:2,_createHelper:function(e){return{encrypt:function(b,k,d){return("string"==typeof k?c:a).encrypt(e,b,k,d)},decrypt:function(b,k,d){return("string"==typeof k?c:a).decrypt(e,b,k,d)}}}});d.StreamCipher=v.extend({_doFinalize:function(){return this._process(!0)},blockSize:1});var b=p.mode={},x=function(e,a,b){var c=this._iv;c?this._iv=u:c=this._prevBlock;for(var d=0;d<b;d++)e[a+d]^=
c[d]},q=(d.BlockCipherMode=l.extend({createEncryptor:function(e,a){return this.Encryptor.create(e,a)},createDecryptor:function(e,a){return this.Decryptor.create(e,a)},init:function(e,a){this._cipher=e;this._iv=a}})).extend();q.Encryptor=q.extend({processBlock:function(e,a){var b=this._cipher,c=b.blockSize;x.call(this,e,a,c);b.encryptBlock(e,a);this._prevBlock=e.slice(a,a+c)}});q.Decryptor=q.extend({processBlock:function(e,a){var b=this._cipher,c=b.blockSize,d=e.slice(a,a+c);b.decryptBlock(e,a);x.call(this,
e,a,c);this._prevBlock=d}});b=b.CBC=q;q=(p.pad={}).Pkcs7={pad:function(a,b){for(var c=4*b,c=c-a.sigBytes%c,d=c<<24|c<<16|c<<8|c,l=[],n=0;n<c;n+=4)l.push(d);c=s.create(l,c);a.concat(c)},unpad:function(a){a.sigBytes-=a.words[a.sigBytes-1>>>2]&255}};d.BlockCipher=v.extend({cfg:v.cfg.extend({mode:b,padding:q}),reset:function(){v.reset.call(this);var a=this.cfg,b=a.iv,a=a.mode;if(this._xformMode==this._ENC_XFORM_MODE)var c=a.createEncryptor;else c=a.createDecryptor,this._minBufferSize=1;this._mode=c.call(a,
this,b&&b.words)},_doProcessBlock:function(a,b){this._mode.processBlock(a,b)},_doFinalize:function(){var a=this.cfg.padding;if(this._xformMode==this._ENC_XFORM_MODE){a.pad(this._data,this.blockSize);var b=this._process(!0)}else b=this._process(!0),a.unpad(b);return b},blockSize:4});var n=d.CipherParams=l.extend({init:function(a){this.mixIn(a)},toString:function(a){return(a||this.formatter).stringify(this)}}),b=(p.format={}).OpenSSL={stringify:function(a){var b=a.ciphertext;a=a.salt;return(a?s.create([1398893684,
1701076831]).concat(a).concat(b):b).toString(r)},parse:function(a){a=r.parse(a);var b=a.words;if(1398893684==b[0]&&1701076831==b[1]){var c=s.create(b.slice(2,4));b.splice(0,4);a.sigBytes-=16}return n.create({ciphertext:a,salt:c})}},a=d.SerializableCipher=l.extend({cfg:l.extend({format:b}),encrypt:function(a,b,c,d){d=this.cfg.extend(d);var l=a.createEncryptor(c,d);b=l.finalize(b);l=l.cfg;return n.create({ciphertext:b,key:c,iv:l.iv,algorithm:a,mode:l.mode,padding:l.padding,blockSize:a.blockSize,formatter:d.format})},
decrypt:function(a,b,c,d){d=this.cfg.extend(d);b=this._parse(b,d.format);return a.createDecryptor(c,d).finalize(b.ciphertext)},_parse:function(a,b){return"string"==typeof a?b.parse(a,this):a}}),p=(p.kdf={}).OpenSSL={execute:function(a,b,c,d){d||(d=s.random(8));a=w.create({keySize:b+c}).compute(a,d);c=s.create(a.words.slice(b),4*c);a.sigBytes=4*b;return n.create({key:a,iv:c,salt:d})}},c=d.PasswordBasedCipher=a.extend({cfg:a.cfg.extend({kdf:p}),encrypt:function(b,c,d,l){l=this.cfg.extend(l);d=l.kdf.execute(d,
b.keySize,b.ivSize);l.iv=d.iv;b=a.encrypt.call(this,b,c,d.key,l);b.mixIn(d);return b},decrypt:function(b,c,d,l){l=this.cfg.extend(l);c=this._parse(c,l.format);d=l.kdf.execute(d,b.keySize,b.ivSize,c.salt);l.iv=d.iv;return a.decrypt.call(this,b,c,d.key,l)}})}();
(function(){for(var u=CryptoJS,p=u.lib.BlockCipher,d=u.algo,l=[],s=[],t=[],r=[],w=[],v=[],b=[],x=[],q=[],n=[],a=[],c=0;256>c;c++)a[c]=128>c?c<<1:c<<1^283;for(var e=0,j=0,c=0;256>c;c++){var k=j^j<<1^j<<2^j<<3^j<<4,k=k>>>8^k&255^99;l[e]=k;s[k]=e;var z=a[e],F=a[z],G=a[F],y=257*a[k]^16843008*k;t[e]=y<<24|y>>>8;r[e]=y<<16|y>>>16;w[e]=y<<8|y>>>24;v[e]=y;y=16843009*G^65537*F^257*z^16843008*e;b[k]=y<<24|y>>>8;x[k]=y<<16|y>>>16;q[k]=y<<8|y>>>24;n[k]=y;e?(e=z^a[a[a[G^z]]],j^=a[a[j]]):e=j=1}var H=[0,1,2,4,8,
16,32,64,128,27,54],d=d.AES=p.extend({_doReset:function(){for(var a=this._key,c=a.words,d=a.sigBytes/4,a=4*((this._nRounds=d+6)+1),e=this._keySchedule=[],j=0;j<a;j++)if(j<d)e[j]=c[j];else{var k=e[j-1];j%d?6<d&&4==j%d&&(k=l[k>>>24]<<24|l[k>>>16&255]<<16|l[k>>>8&255]<<8|l[k&255]):(k=k<<8|k>>>24,k=l[k>>>24]<<24|l[k>>>16&255]<<16|l[k>>>8&255]<<8|l[k&255],k^=H[j/d|0]<<24);e[j]=e[j-d]^k}c=this._invKeySchedule=[];for(d=0;d<a;d++)j=a-d,k=d%4?e[j]:e[j-4],c[d]=4>d||4>=j?k:b[l[k>>>24]]^x[l[k>>>16&255]]^q[l[k>>>
8&255]]^n[l[k&255]]},encryptBlock:function(a,b){this._doCryptBlock(a,b,this._keySchedule,t,r,w,v,l)},decryptBlock:function(a,c){var d=a[c+1];a[c+1]=a[c+3];a[c+3]=d;this._doCryptBlock(a,c,this._invKeySchedule,b,x,q,n,s);d=a[c+1];a[c+1]=a[c+3];a[c+3]=d},_doCryptBlock:function(a,b,c,d,e,j,l,f){for(var m=this._nRounds,g=a[b]^c[0],h=a[b+1]^c[1],k=a[b+2]^c[2],n=a[b+3]^c[3],p=4,r=1;r<m;r++)var q=d[g>>>24]^e[h>>>16&255]^j[k>>>8&255]^l[n&255]^c[p++],s=d[h>>>24]^e[k>>>16&255]^j[n>>>8&255]^l[g&255]^c[p++],t=
d[k>>>24]^e[n>>>16&255]^j[g>>>8&255]^l[h&255]^c[p++],n=d[n>>>24]^e[g>>>16&255]^j[h>>>8&255]^l[k&255]^c[p++],g=q,h=s,k=t;q=(f[g>>>24]<<24|f[h>>>16&255]<<16|f[k>>>8&255]<<8|f[n&255])^c[p++];s=(f[h>>>24]<<24|f[k>>>16&255]<<16|f[n>>>8&255]<<8|f[g&255])^c[p++];t=(f[k>>>24]<<24|f[n>>>16&255]<<16|f[g>>>8&255]<<8|f[h&255])^c[p++];n=(f[n>>>24]<<24|f[g>>>16&255]<<16|f[h>>>8&255]<<8|f[k&255])^c[p++];a[b]=q;a[b+1]=s;a[b+2]=t;a[b+3]=n},keySize:8});u.AES=p._createHelper(d)})();

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

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
"use strict";var Module={};var initializedJS=false;function threadPrintErr(){var text=Array.prototype.slice.call(arguments).join(" ");console.error(text)}function threadAlert(){var text=Array.prototype.slice.call(arguments).join(" ");postMessage({cmd:"alert",text:text,threadId:Module["_pthread_self"]()})}var err=threadPrintErr;self.alert=threadAlert;Module["instantiateWasm"]=(info,receiveInstance)=>{var module=Module["wasmModule"];Module["wasmModule"]=null;var instance=new WebAssembly.Instance(module,info);return receiveInstance(instance)};self.onunhandledrejection=e=>{throw e.reason??e};function handleMessage(e){try{if(e.data.cmd==="load"){let messageQueue=[];self.onmessage=e=>messageQueue.push(e);self.startWorker=instance=>{Module=instance;postMessage({"cmd":"loaded"});for(let msg of messageQueue){handleMessage(msg)}self.onmessage=handleMessage};Module["wasmModule"]=e.data.wasmModule;for(const handler of e.data.handlers){Module[handler]=(...args)=>{postMessage({cmd:"callHandler",handler:handler,args:args})}}Module["wasmMemory"]=e.data.wasmMemory;Module["buffer"]=Module["wasmMemory"].buffer;Module["ENVIRONMENT_IS_PTHREAD"]=true;if(typeof e.data.urlOrBlob=="string"){importScripts(e.data.urlOrBlob)}else{var objectUrl=URL.createObjectURL(e.data.urlOrBlob);importScripts(objectUrl);URL.revokeObjectURL(objectUrl)}JSPlayerModule(Module)}else if(e.data.cmd==="run"){Module["__emscripten_thread_init"](e.data.pthread_ptr,0,0,1);Module["__emscripten_thread_mailbox_await"](e.data.pthread_ptr);Module["establishStackSpace"]();Module["PThread"].receiveObjectTransfer(e.data);Module["PThread"].threadInitTLS();if(!initializedJS){Module["__embind_initialize_bindings"]();initializedJS=true}try{Module["invokeEntryPoint"](e.data.start_routine,e.data.arg)}catch(ex){if(ex!="unwind"){throw ex}}}else if(e.data.cmd==="cancel"){if(Module["_pthread_self"]()){Module["__emscripten_thread_exit"](-1)}}else if(e.data.target==="setimmediate"){}else if(e.data.cmd==="checkMailbox"){if(initializedJS){Module["checkMailbox"]()}}else if(e.data.cmd){err("worker.js received unknown command "+e.data.cmd);err(e.data)}}catch(ex){if(Module["__emscripten_thread_crashed"]){Module["__emscripten_thread_crashed"]()}throw ex}}self.onmessage=handleMessage;

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
"use strict";var Module={};var initializedJS=false;function threadPrintErr(){var text=Array.prototype.slice.call(arguments).join(" ");console.error(text)}function threadAlert(){var text=Array.prototype.slice.call(arguments).join(" ");postMessage({cmd:"alert",text:text,threadId:Module["_pthread_self"]()})}var err=threadPrintErr;self.alert=threadAlert;Module["instantiateWasm"]=(info,receiveInstance)=>{var module=Module["wasmModule"];Module["wasmModule"]=null;var instance=new WebAssembly.Instance(module,info);return receiveInstance(instance)};self.onunhandledrejection=e=>{throw e.reason??e};function handleMessage(e){try{if(e.data.cmd==="load"){let messageQueue=[];self.onmessage=e=>messageQueue.push(e);self.startWorker=instance=>{Module=instance;postMessage({"cmd":"loaded"});for(let msg of messageQueue){handleMessage(msg)}self.onmessage=handleMessage};Module["wasmModule"]=e.data.wasmModule;for(const handler of e.data.handlers){Module[handler]=(...args)=>{postMessage({cmd:"callHandler",handler:handler,args:args})}}Module["wasmMemory"]=e.data.wasmMemory;Module["buffer"]=Module["wasmMemory"].buffer;Module["ENVIRONMENT_IS_PTHREAD"]=true;if(typeof e.data.urlOrBlob=="string"){importScripts(e.data.urlOrBlob)}else{var objectUrl=URL.createObjectURL(e.data.urlOrBlob);importScripts(objectUrl);URL.revokeObjectURL(objectUrl)}JSPlayerModule(Module)}else if(e.data.cmd==="run"){Module["__emscripten_thread_init"](e.data.pthread_ptr,0,0,1);Module["__emscripten_thread_mailbox_await"](e.data.pthread_ptr);Module["establishStackSpace"]();Module["PThread"].receiveObjectTransfer(e.data);Module["PThread"].threadInitTLS();if(!initializedJS){Module["__embind_initialize_bindings"]();initializedJS=true}try{Module["invokeEntryPoint"](e.data.start_routine,e.data.arg)}catch(ex){if(ex!="unwind"){throw ex}}}else if(e.data.cmd==="cancel"){if(Module["_pthread_self"]()){Module["__emscripten_thread_exit"](-1)}}else if(e.data.target==="setimmediate"){}else if(e.data.cmd==="checkMailbox"){if(initializedJS){Module["checkMailbox"]()}}else if(e.data.cmd){err("worker.js received unknown command "+e.data.cmd);err(e.data)}}catch(ex){if(Module["__emscripten_thread_crashed"]){Module["__emscripten_thread_crashed"]()}throw ex}}self.onmessage=handleMessage;

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 it is too large Load Diff

Binary file not shown.

View File

@@ -0,0 +1,240 @@
importScripts('libSystemTransform.js');
const RECORDRTP = 0; //录制一份未经过转封装的码流原始数据,用于定位问题
let dataType = 1;
// 字母字符串转byte数组
function stringToBytes (str) {
var ch;
var st;
var re = [];
for (var i = 0; i < str.length; i++) {
ch = str.charCodeAt(i); // get char
st = []; // set up "stack"
do {
st.push(ch & 0xFF); // push byte to stack
ch = ch >> 8; // shift value down by 1 byte
}
while (ch);
// add stack contents to result
// done because chars have "wrong" endianness
re = re.concat(st.reverse());
}
// return an array of bytes
return re;
}
// 转封装库回调函数
self.STCallBack = function (fileIndex, indexLen, data, dataLen) {
//stFrameInfo的类型见DETAIL_FRAME_INFO
let stFrameInfo = Module._GetDetialFrameInfo();
let nIsMp4Index = stFrameInfo.nIsMp4Index;
//console.log("FrameType is " , stFrameInfo);
//console.log("nIsMp4Index is " + nIsMp4Index);
//debugger
var pData = null;
pData = new Uint8Array(dataLen);
pData.set(Module.HEAPU8.subarray(data, data + dataLen));
if (dataType === 1) {
postMessage({ type: "outputData",
buf: pData.buffer,
dType: 1,
frameInfo: stFrameInfo }, [pData.buffer]);
dataType = 2;
} else {
if (nIsMp4Index) {
postMessage({ type: "outputData",
buf: pData.buffer,
dType: 6,
frameInfo: stFrameInfo }, [pData.buffer]); //6索引类型
} else {
postMessage({ type: "outputData",
buf: pData.buffer,
dType: 2,
frameInfo: stFrameInfo }, [pData.buffer]); //2:码流
}
}
//stFrameInfo的类型见DETAIL_FRAME_INFO
//let stFrameInfo = Module._GetDetialFrameInfo();
//let stFrameType = stFrameInfo.nFrameType;
//let nFrameNum = stFrameInfo.nFrameNum;
//let nTimeStamp = stFrameInfo.nTimeStamp;
//let nIsMp4Index = stFrameInfo.nIsMp4Index;
//console.log("FrameType is " + stFrameType);
//console.log("nIsMp4Index is " + nIsMp4Index);
};
// self.Module = { memoryInitializerRequest: loadMemInitFile(), TOTAL_MEMORY: 128*1024*1024 };
// importScripts('SystemTransform.js');
self.Module['onRuntimeInitialized'] = function () {
postMessage({type: "loaded"});
};
onmessage = function (e) {
var data = e.data;
if ("create" === data.type) {
if (RECORDRTP) {
postMessage({ type: "created" });
postMessage({ type: "outputData",
buf: data.buf,
dType: 1 }, [data.buf]);
} else {
var iHeadLen = data.len;
var pHead = Module._malloc(iHeadLen);
if (pHead === null) {
console.log("inputdata malloc failed!!!");
return -1;
}
var iTransType = data.packType;//目标格式
var iRet = 0;
var buf = new Uint8Array(data.buf);
//PS流(只有ps支持探测)并且编码格式异常正常是265和26411位和10位 不可能全是0全0 并且是ps就探测策略的情况下使用探测策略
if (buf[9] === 0 && buf[8] === 2 && buf[11] === 0 && buf[10] === 0) {
iRet = Module._CreatHandle(0, iTransType, iHeadLen); //用探测的策略
} else {
self.writeArrayToMemory(buf, pHead);
iRet = Module._CreatHandle(pHead, iTransType, iHeadLen);
//-2147483645代表的是参数错误此种情况大概率发生在头信息错误例如大华设备的情况此时用 无头探测的策略
//其他情况 按海康标准处理流程,不要做任何特殊处理
if (iRet == -2147483645) {
iRet = Module._CreatHandle(0, iTransType, iHeadLen); //失败了,用探测的策略再试一次
}
}
if (iRet != 0) {
if (iRet == -2147483647) {
postMessage({ type: "outputData",
dType: 1501 }); //标记为格式不支持
} else {
postMessage({ type: "outputData",
dType: 1501 }); //转封装创建失败,也同样提示码流格式不支持,如果后续要细化再区分
}
console.log("_CreatHandle failed!" + iRet);
} else {
if (data.options && typeof data.options.pKeyData !== "undefined" && data.options.pKeyData !== null) {
if ((2 === iTransType && "" === data.options.pKeyData)) {
//转ps的时候如果密码是空是允许的即使码流加密了导出加密后的码流就行
//此时不要设置密码否则反而会提示密码错误
} else {
var secretInfo = data.options;
var keyLen = secretInfo.nKeyLen;
var pKeyData = Module._malloc(keyLen);
if (pKeyData === null) {
console.log("setEncryptKey malloc failed!!!");
return -1;
}
var nKeySize = secretInfo.pKeyData.length;
var bufData = stringToBytes(secretInfo.pKeyData);
let inputData = new Uint8Array(bufData);
Module.writeArrayToMemory(inputData, pKeyData);
inputData = null;
iRet = Module._SysTransSetEncryptKey(secretInfo.nKeyType, pKeyData, keyLen, nKeySize);
if (iRet != 0) {
console.log("_SysTransSetEncryptKey failed!");
}
if (pKeyData != null) {
Module._free(pKeyData);
pKeyData = null;
}
}
}
//带samplingParam参数代表需要 用到 音频替换功能
if (data.options && typeof data.options.samplingParam !== "undefined") {
var oParam = data.options.samplingParam;
var nCapacityType = 1; //写死1 代表 剔除音频
var nType = 3; //写死3 代表 修改输出目标的海康头配置,内部包含视频参数、音频参数
var nAudioEnable = 1; //音频参数修改使能开关0=不启用1=启用
var nAudioFormat = oParam.iAudioType; //音频编码类型 对应关系参考海康媒体头规范PCM 0x7001 G711_U 0x7110 G711_A 0x7111 AAC 0x2001
var nAudioChannels = oParam.iChannel; //音频通道数直接设置为
var nAudioBitsPerSample = oParam.iAudioBitWidth; //音频位样率
var nAudioSamplesrate = oParam.iAudioSamplingRate; //音频采样率
var nAudioBitrate = oParam.iAudioBitRate; //音频比特率
iRet = Module._SysTransConfig(nCapacityType, nType, nAudioEnable, nAudioFormat,
nAudioChannels, nAudioBitsPerSample, nAudioSamplesrate, nAudioBitrate);
if (iRet != 0) {
console.log("_SysTransConfig Failed:" + iRet);
}
} else {
iRet = Module._SysTransConfig(128, 0, 0, 0, 0, 0, 0, 0); //nCapacityType = 0x00000080 代表开启私有信息回调 解决转mp4后私有信息丢失问题
if (iRet != 0) {
console.log("_SysTransConfig Failed:" + iRet);
}
}
iRet = Module._SysTransRegisterDataCallBack();
if (iRet != 0) {
console.log("_SysTransRegisterDataCallBack Failed:" + iRet);
}
iRet = Module._SysTransStart(null, null);
if (iRet != 0) {
console.log("_SysTransStart Failed:" + iRet);
}
postMessage({type: "created"});
}
if (pHead != null) {
Module._free(pHead);
pHead = null;
}
}
} else if ("inputData" === data.type) {
if (RECORDRTP) {
var aFileData = new Uint8Array(data.buf); // 拷贝一份
var iBufferLen = aFileData.length;
var szBufferLen = iBufferLen.toString(16);
if (szBufferLen.length === 1) {
szBufferLen = "000" + szBufferLen;
} else if (szBufferLen.length === 2) {
szBufferLen = "00" + szBufferLen;
} else if (szBufferLen.length === 3) {
szBufferLen = "0" + szBufferLen;
}
var aData = [0, 0, parseInt(szBufferLen.substring(0, 2), 16), parseInt(szBufferLen.substring(2, 4), 16)];
for (var iIndex = 0, iDataLength = aFileData.length; iIndex < iDataLength; iIndex++) {
aData[iIndex + 4] = aFileData[iIndex];
}
var dataUint8 = new Uint8Array(aData);
postMessage({type: "outputData",
buf: dataUint8.buffer,
dType: 2});
} else {
let inputMode = 0; //代表输入原始数据
if (data.samplingParam) {
iRet = Module._SysTransInputAudioPara(5, data.samplingParam.iChannel, data.samplingParam.iAudioBitWidth,
data.samplingParam.iAudioSamplingRate, data.samplingParam.iTimeStamp, data.samplingParam.iAudioBitRate); //参数含义和_SysTransConfig类似
if (iRet != 0) {
console.log("_SysTransInputAudioPara Failed:" + iRet);
}
inputMode = 2; //输入替换的音频
}
var pInputDataBuf = Module._malloc(data.len);
var idataLen = data.len;
self.writeArrayToMemory(new Uint8Array(data.buf), pInputDataBuf);
// 输入数据每次最多2m
let pp = Module._SysTransInputData(inputMode, pInputDataBuf, idataLen);
if (pp == -2147483627) {
//-2147483627 对应十六进制的80000015
postMessage({ type: "outputData",
dType: 1500 }); //标记为密码错误
} else if (pp == -2147483647) {
postMessage({ type: "outputData",
dType: 1501 }); //标记为格式不支持
} else if (pp != 0) {
console.log("InputData Failed:" + pp);
}
Module._free(pInputDataBuf);
}
} else if ("release" === data.type) {
var iRet = Module._SysTransStop();
if (iRet != 0) {
console.log("_SysTransStop failed!");
}
Module._SysTransRelease();
if (iRet != 0) {
console.log("_SysTransRelease failed!");
}
close();
}
};

File diff suppressed because one or more lines are too long

View File

@@ -1,24 +0,0 @@
import { AxiosProgressEvent } from 'axios'
import { request } from '@/api/request'
/** 上传文件 */
const FtsUpload = (data: FormData, onUploadProgress?: (progress: number) => void) => {
data.append('provider', 'local')
data.append('bucket', 'visual')
return request.post(`/Assets/v1/fts/uploader`, data, {
headers: {
'Content-Type': 'multipart/form-data',
},
onUploadProgress: onUploadProgress
? (progressEvent: AxiosProgressEvent) => {
if (progressEvent.total) {
const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total)
onUploadProgress(percentCompleted)
}
}
: undefined,
})
}
export default FtsUpload

View File

@@ -1,93 +1,86 @@
import { request } from '@/api/request'
import type { KbReply } from './types'
/** 分类类型 */
/** 公共分类状态。 */
export type CategoryStatus = 'active' | 'inactive'
/** 公共分类。 */
export interface Category {
id: number
created_at: string
updated_at: string
name: string
description: string
type: string
type: 'general'
icon: string
color: string
parent_id: number
level: number
level: 1 | 2 | 3
path: string
sort_order: number
status: string
status: CategoryStatus
creator_id: number
creator_name: string
doc_count: number
faq_count: number
metadata: string | null
metadata: string
remarks: string
}
/** API响应包装类型 */
export interface ApiResponse<T = any> {
code: number
message: string
data: T
/** 公共分类树节点;详情字段需通过详情接口获取。 */
export interface CategoryTreeNode {
id: number
name: string
description: string
type: 'general'
icon: string
color: string
parent_id: number
level: 1 | 2 | 3
path: string
sort_order: number
status: CategoryStatus
doc_count: number
faq_count: number
children: CategoryTreeNode[]
}
/** 创建分类请求参数 */
/** 创建分类参数 */
export interface CreateCategoryParams {
name: string
description?: string
type?: string
icon?: string
color?: string
parent_id?: number
sort_order?: number
remarks?: string
description: string
icon: string
color: string
parent_id: number
sort_order: number
status: CategoryStatus
remarks: string
}
/** 更新分类请求参数 */
export interface UpdateCategoryParams {
/** 更新分类参数 */
export interface UpdateCategoryParams extends CreateCategoryParams {
id: number
name?: string
description?: string
icon?: string
color?: string
sort_order?: number
status?: string
remarks?: string
}
/** 获取分类列表参数 */
/** 分类列表参数 */
export interface FetchCategoryListParams {
type?: string
parent_id?: number
}
/** 创建分类 */
export const createCategory = (data: CreateCategoryParams) => {
return request.post<ApiResponse<Category>>('/Kb/v1/category/create', data)
}
/** 创建公共分类 */
export const createCategory = (data: CreateCategoryParams) => request.post<KbReply<Category>>('/Kb/v1/category/create', data)
/** 更新分类 */
export const updateCategory = (data: UpdateCategoryParams) => {
return request.post<ApiResponse<Category>>('/Kb/v1/category/update', data)
}
/** 更新公共分类 */
export const updateCategory = (data: UpdateCategoryParams) => request.post<KbReply<Category>>('/Kb/v1/category/update', data)
/** 删除分类 */
export const deleteCategory = (id: number) => {
return request.delete<ApiResponse<string>>(`/Kb/v1/category/${id}`)
}
/** 删除公共分类 */
export const deleteCategory = (id: number) => request.delete<KbReply<string>>(`/Kb/v1/category/${id}`)
/** 获取分类详情 */
export const fetchCategoryDetail = (id: number) => {
return request.get<ApiResponse<Category>>(`/Kb/v1/category/${id}`)
}
/** 获取公共分类详情 */
export const fetchCategoryDetail = (id: number) => request.get<KbReply<Category>>(`/Kb/v1/category/${id}`)
/** 获取分类列表 */
export const fetchCategoryList = (params?: FetchCategoryListParams) => {
return request.get<ApiResponse<Category[]>>('/Kb/v1/category/list', { params })
}
/** 获取公共分类列表 */
export const fetchCategoryList = (params?: FetchCategoryListParams) => request.get<KbReply<Category[]>>('/Kb/v1/category/list', { params })
/** 获取分类树 */
export const fetchCategoryTree = (type?: string) => {
return request.get<ApiResponse<Category[]>>('/Kb/v1/category/tree', {
params: type ? { type } : undefined,
})
}
/** 获取完整公共分类树,包含停用节点。 */
export const fetchCategoryTree = () => request.get<KbReply<CategoryTreeNode[]>>('/Kb/v1/category/tree')

View File

@@ -1,12 +1,14 @@
import { request } from '@/api/request'
import type { KbReply } from './types'
/** 文档状态 */
/** 文档状态 */
export type DocumentStatus = 'draft' | 'published' | 'reviewed' | 'rejected'
/** 文档类型 */
/** 文档类型。 */
export type DocumentType = 'common' | 'guide' | 'solution' | 'troubleshoot' | 'process' | 'technical'
/** 文档列表范围。 */
export type DocumentScope = 'my' | 'all'
/** 文档接口类型 */
/** 文档资源。 */
export interface Document {
id: number
created_at: string
@@ -33,71 +35,54 @@ export interface Document {
version: string
version_notes: string
tags: string
attachments: string | null
related_docs: string | null
detection_point_ids: string | null
metadata: string | null
attachments: string
related_docs: string
detection_point_ids: string
metadata: string
keywords: string
remarks: string
is_favorited?: boolean
is_favorited: boolean
}
/** API响应包装类型 */
export interface ApiResponse<T = any> {
code: number
message: string
data: T
}
/** 分页响应类型 */
export interface PaginatedResponse<T> {
/** 文档分页结果。 */
export interface DocumentPage {
total: number
page: number
page_size: number
data: T[]
data: Document[]
}
/** 创建文档请求参数 */
/** 文档创建字段。 */
export interface CreateDocumentParams {
title: string
description?: string
description: string
content: string
type?: DocumentType
category_id?: number
sub_category?: string
keywords?: string
tags?: string
detection_point_ids?: string
remarks?: string
type: DocumentType
category_id: number
sub_category: string
keywords: string
tags: string
detection_point_ids: string
remarks: string
}
/** 更新文档请求参数 */
export interface UpdateDocumentParams {
/** 文档编辑字段。 */
export interface UpdateDocumentParams extends CreateDocumentParams {
id: number
title?: string
description?: string
content?: string
type?: DocumentType
category_id?: number
sub_category?: string
keywords?: string
tags?: string
detection_point_ids?: string
remarks?: string
}
/** 获取文档列表参数 */
/** 文档列表参数 */
export interface FetchDocumentListParams {
page?: number
page_size?: number
scope?: DocumentScope
keyword?: string
type?: DocumentType
status?: DocumentStatus
category_id?: number
}
/** 文档类型选项 */
export const documentTypeOptions = [
export const documentTypeOptions: Array<{ label: string; value: DocumentType }> = [
{ label: '通用文档', value: 'common' },
{ label: '操作指南', value: 'guide' },
{ label: '解决方案', value: 'solution' },
@@ -106,115 +91,28 @@ export const documentTypeOptions = [
{ label: '技术文档', value: 'technical' },
]
/** 文档状态选项 */
export const documentStatusOptions = [
{ label: '草稿', value: 'draft' },
{ label: '已发布', value: 'published' },
{ label: '已审核', value: 'reviewed' },
{ label: '未通过审核', value: 'rejected' },
]
/** 创建文档草稿。 */
export const createDocument = (data: CreateDocumentParams) => request.post<KbReply<Document>>('/Kb/v1/document/create', data)
/** 获取文档状态文本 */
export const getDocumentStatusText = (status: DocumentStatus): string => {
const statusMap: Record<DocumentStatus, string> = {
draft: '草稿',
published: '已发布',
reviewed: '已审核',
rejected: '未通过审核',
}
return statusMap[status] || status
}
/** 更新文档。 */
export const updateDocument = (data: UpdateDocumentParams) => request.post<KbReply<Document>>('/Kb/v1/document/update', data)
/** 获取文档状态颜色 */
export const getDocumentStatusColor = (status: DocumentStatus): string => {
const colorMap: Record<DocumentStatus, string> = {
draft: 'gray',
published: 'blue',
reviewed: 'green',
rejected: 'red',
}
return colorMap[status] || 'gray'
}
/** 删除文档并移入回收站。 */
export const deleteDocument = (id: number) => request.delete<KbReply<string>>(`/Kb/v1/document/${id}`)
/** 获取文档类型文本 */
export const getDocumentTypeText = (type: DocumentType): string => {
const typeMap: Record<DocumentType, string> = {
common: '通用文档',
guide: '操作指南',
solution: '解决方案',
troubleshoot: '故障排查',
process: '流程规范',
technical: '技术文档',
}
return typeMap[type] || type
}
/** 获取文档详情。 */
export const fetchDocumentDetail = (id: number) => request.get<KbReply<Document>>(`/Kb/v1/document/${id}`)
/** 创建文档 */
export const createDocument = (data: CreateDocumentParams) => {
return request.post<ApiResponse<Document>>('/Kb/v1/document/create', data)
}
/** 获取指定可见范围的文档列表。 */
export const fetchDocumentList = (params: FetchDocumentListParams) => request.get<KbReply<DocumentPage>>('/Kb/v1/document/list', { params })
/** 更新文档 */
export const updateDocument = (data: UpdateDocumentParams) => {
return request.post<ApiResponse<Document>>('/Kb/v1/document/update', data)
}
/** 提交文档审核。 */
export const publishDocument = (id: number) => request.post<KbReply<string>>('/Kb/v1/document/publish', { id })
/** 删除文档(移入回收站) */
export const deleteDocument = (id: number) => {
return request.delete<ApiResponse<string>>(`/Kb/v1/document/${id}`)
}
/** 收藏文档。 */
export const favoriteDocument = (id: number) =>
request.post<KbReply<unknown>>('/Kb/v1/favorite/collect', { resource_type: 'document', resource_id: id })
/** 获取文档详情 */
export const fetchDocumentDetail = (id: number) => {
return request.get<ApiResponse<Document>>(`/Kb/v1/document/${id}`)
}
/** 获取文档列表 */
export const fetchDocumentList = (params?: FetchDocumentListParams) => {
return request.get<ApiResponse<PaginatedResponse<Document>>>('/Kb/v1/document/list', { params })
}
/** 发布文档 */
export const publishDocument = (id: number) => {
return request.post<ApiResponse<string>>('/Kb/v1/document/publish', { id })
}
/** 移入回收站 */
export const moveToTrash = (resourceId: number, resourceType: string) => {
return request.post<ApiResponse<string>>('/Kb/v1/trash/move', {
resource_id: resourceId,
resource_type: resourceType,
})
}
/** 获取我的文档列表(由我创建的所有文档) */
export const fetchMyDocumentList = (params?: FetchDocumentListParams) => {
return request.get<ApiResponse<PaginatedResponse<Document>>>('/Kb/v1/review/publish/list', { params })
}
/** 获取已审核通过的文档列表 */
export const fetchApprovedDocumentList = (params?: FetchDocumentListParams) => {
return request.get<ApiResponse<PaginatedResponse<Document>>>('/Kb/v1/review/approved/list', { params })
}
/** 收藏文档 */
export const favoriteDocument = (id: number, remarks?: string) => {
return request.post<ApiResponse<string>>('/Kb/v1/favorite/collect', {
resource_type: 'document',
resource_id: id,
remarks,
})
}
/** 取消收藏文档 */
export const unfavoriteDocument = (id: number) => {
return request.post<ApiResponse<string>>('/Kb/v1/favorite/uncollect', {
resource_type: 'document',
resource_id: id,
})
}
/** 下载文档 */
export const downloadDocument = (id: number) => {
return request.get<Blob>(`/Kb/v1/document/${id}/download`, { responseType: 'blob' })
}
/** 取消收藏文档。 */
export const unfavoriteDocument = (id: number) =>
request.post<KbReply<string>>('/Kb/v1/favorite/uncollect', { resource_type: 'document', resource_id: id })

177
src/api/kb/faq.ts Normal file
View File

@@ -0,0 +1,177 @@
import { request } from '@/api/request'
import type { KbReply } from './types'
export { fetchCategoryTree as fetchGeneralCategoryTree } from './category'
export type { CategoryTreeNode } from './category'
export type { KbReply } from './types'
/** FAQ 审核状态。 */
export type FaqStatus = 'draft' | 'published' | 'reviewed' | 'rejected'
/** FAQ 优先级。 */
export type FaqPriority = 'low' | 'medium' | 'high'
/** FAQ 列表可见范围。 */
export type FaqScope = 'my' | 'all'
/** FAQ 资源。 */
export interface Faq {
id: number
created_at: string
updated_at: string
faq_no: string
question: string
answer: string
status: FaqStatus
priority: FaqPriority
category_id: number
sub_category: string
problem_type: string
solution: string
process_steps: string
prerequisites: string
author_id: number
author_name: string
reviewer_id: number
reviewer_name: string
reviewed_at: string | null
published_at: string | null
view_count: number
use_count: number
helpful_count: number
useless_count: number
tags: string
related_faqs: string
related_docs: string
related_links: string
detection_point_ids: string
attachments: string
keywords: string
applicable_scope: string
remarks: string
is_favorited: boolean
}
/** FAQ 分页结果。 */
export interface FaqPage {
total: number
page: number
page_size: number
data: Faq[]
}
/** FAQ 列表查询参数。 */
export interface FaqListParams {
page?: number
page_size?: number
scope?: FaqScope
keyword?: string
category_id?: number
status?: FaqStatus
priority?: FaqPriority
problem_type?: string
}
/** FAQ 创建和编辑字段。 */
export interface FaqFormData {
question: string
answer: string
priority: FaqPriority
category_id: number
sub_category: string
problem_type: string
solution: string
process_steps: string
prerequisites: string
keywords: string
tags: string
detection_point_ids: string
applicable_scope: string
remarks: string
}
/** FAQ 编辑请求。 */
export interface UpdateFaqData extends FaqFormData {
id: number
}
/** FAQ 状态筛选项。 */
export const faqStatusOptions: Array<{ label: string; value: FaqStatus }> = [
{ label: '草稿', value: 'draft' },
{ label: '待审核', value: 'published' },
{ label: '已审核', value: 'reviewed' },
{ label: '已拒绝', value: 'rejected' },
]
/** FAQ 优先级筛选项。 */
export const faqPriorityOptions: Array<{ label: string; value: FaqPriority }> = [
{ label: '低', value: 'low' },
{ label: '中', value: 'medium' },
{ label: '高', value: 'high' },
]
/** 常用问题类型;接口仍允许自由字符串。 */
export const faqProblemTypeOptions = [
{ label: '故障', value: '故障' },
{ label: '咨询', value: '咨询' },
{ label: '请求', value: '请求' },
{ label: '其他', value: '其他' },
]
const faqStatusMeta: Record<FaqStatus, { text: string; color: string }> = {
draft: { text: '草稿', color: 'gray' },
published: { text: '待审核', color: 'orange' },
reviewed: { text: '已审核', color: 'green' },
rejected: { text: '已拒绝', color: 'red' },
}
const faqPriorityMeta: Record<FaqPriority, { text: string; color: string }> = {
low: { text: '低', color: 'gray' },
medium: { text: '中', color: 'blue' },
high: { text: '高', color: 'orange' },
}
/** 获取 FAQ 状态文案。 */
export const getFaqStatusText = (status: FaqStatus | string) => faqStatusMeta[status as FaqStatus]?.text || status || '-'
/** 获取 FAQ 状态标签颜色。 */
export const getFaqStatusColor = (status: FaqStatus | string) => faqStatusMeta[status as FaqStatus]?.color || 'gray'
/** 获取 FAQ 优先级文案。 */
export const getFaqPriorityText = (priority: FaqPriority | string) => faqPriorityMeta[priority as FaqPriority]?.text || priority || '-'
/** 获取 FAQ 优先级标签颜色。 */
export const getFaqPriorityColor = (priority: FaqPriority | string) => faqPriorityMeta[priority as FaqPriority]?.color || 'gray'
/** 获取 FAQ 列表。 */
export const fetchFaqList = (params: FaqListParams) =>
request.get<KbReply<FaqPage>>('/Kb/v1/faq/list', {
params,
})
/** 获取 FAQ 详情。 */
export const fetchFaqDetail = (id: number) => request.get<KbReply<Faq>>(`/Kb/v1/faq/${id}`)
/** 创建 FAQ 草稿。 */
export const createFaq = (data: FaqFormData) => request.post<KbReply<Faq>>('/Kb/v1/faq/create', data)
/** 更新 FAQ。 */
export const updateFaq = (data: UpdateFaqData) => request.post<KbReply<Faq>>('/Kb/v1/faq/update', data)
/** 提交 FAQ 审核。 */
export const publishFaq = (id: number) => request.post<KbReply<string>>('/Kb/v1/faq/publish', { id })
/** 删除 FAQ 并移入回收站。 */
export const deleteFaq = (id: number) => request.delete<KbReply<string>>(`/Kb/v1/faq/${id}`)
/** 收藏 FAQ。 */
export const favoriteFaq = (id: number) =>
request.post<KbReply<unknown>>('/Kb/v1/favorite/collect', {
resource_type: 'faq',
resource_id: id,
})
/** 取消收藏 FAQ。 */
export const unfavoriteFaq = (id: number) =>
request.post<KbReply<string>>('/Kb/v1/favorite/uncollect', {
resource_type: 'faq',
resource_id: id,
})

View File

@@ -1,9 +1,11 @@
import { request } from '@/api/request'
import type { Document } from './document'
import type { Faq } from './faq'
import type { KbReply } from './types'
/** 资源类型 */
export type ResourceType = 'document' | 'faq'
/** 收藏记录接口 */
/** 收藏记录 */
export interface Favorite {
id: number
created_at: string
@@ -12,69 +14,36 @@ export interface Favorite {
resource_name: string
remarks: string
is_deleted: boolean
resource_data?: any
resource_data?: Document | Faq
}
/** 收藏列表响应 */
export interface FavoriteListResponse {
export interface FavoritePage {
total: number
page: number
page_size: number
data: Favorite[]
}
/** 获取收藏列表参数 */
export interface FetchFavoriteListParams {
page?: number
page_size?: number
resource_type?: ResourceType
}
/** 收藏请求参数 */
export interface CollectParams {
resource_type: ResourceType
resource_id: number
remarks?: string
}
/** 取消收藏参数 */
export interface UncollectParams {
resource_type: ResourceType
resource_id: number
}
export type UncollectParams = Omit<CollectParams, 'remarks'>
/** API响应包装类型 */
export interface ApiResponse<T = any> {
code: number
message: string
data: T
}
/** 获取收藏列表。 */
export const fetchFavoriteList = (params: FetchFavoriteListParams = {}) =>
request.get<KbReply<FavoritePage>>('/Kb/v1/favorite/list', { params })
/**
* 获取收藏列表
*/
export async function fetchFavoriteList(params: FetchFavoriteListParams = {}): Promise<ApiResponse<FavoriteListResponse>> {
return request.get<ApiResponse<FavoriteListResponse>>('/Kb/v1/favorite/list', {
params,
})
}
/** 收藏资源。 */
export const collectResource = (data: CollectParams) => request.post<KbReply<Favorite>>('/Kb/v1/favorite/collect', data)
/**
* 收藏资源
*/
export async function collectResource(data: CollectParams): Promise<ApiResponse<Favorite>> {
return request.post<ApiResponse<Favorite>>('/Kb/v1/favorite/collect', data)
}
/**
* 取消收藏
*/
export async function uncollectResource(data: UncollectParams): Promise<ApiResponse<string>> {
return request.post<ApiResponse<string>>('/Kb/v1/favorite/uncollect', data)
}
/** 资源类型选项 */
export const resourceTypeOptions = [
{ label: '文档', value: 'document' },
{ label: 'FAQ', value: 'faq' },
]
/** 取消收藏。 */
export const uncollectResource = (data: UncollectParams) => request.post<KbReply<string>>('/Kb/v1/favorite/uncollect', data)

View File

@@ -1,169 +1,71 @@
import { request } from '@/api/request'
import type { Document } from './document'
import type { Faq } from './faq'
import type { KbReply } from './types'
export type ReviewStatsResourceType = 'all' | 'document' | 'faq'
export type ReviewResourceType = 'all' | 'document' | 'faq'
/** 审核统计接口返回的 data 字段 */
export interface ReviewStatsPayload {
need_my_review_document?: number
need_my_review_faq?: number
need_my_review_total?: number
need_my_review_unreviewed_document?: number
need_my_review_unreviewed_faq?: number
need_my_review_unreviewed_total?: number
}
/** 文档资源类型 */
export interface DocumentResource {
id: number
created_at: string
updated_at: string
doc_no: string
title: string
description: string
content: string
type: string
status: string
category_id: number
sub_category: string
author_id: number
author_name: string
reviewer_id: number
reviewer_name: string
reviewed_at: string | null
published_at: string | null
publisher_id: number
view_count: number
like_count: number
comment_count: number
download_count: number
version: string
version_notes: string
tags: string
attachments: string | null
related_docs: string | null
metadata: string | null
keywords: string
remarks: string
}
/** FAQ资源类型 */
export interface FaqResource {
id: number
created_at: string
updated_at: string
faq_no: string
question: string
answer: string
status: string
priority: string
category_id: number
sub_category: string
problem_type: string
solution: string
process_steps: string
prerequisites: string
author_id: number
author_name: string
reviewer_id: number
reviewer_name: string
reviewed_at: string | null
published_at: string | null
view_count: number
use_count: number
helpful_count: number
useless_count: number
tags: string
related_faqs: string | null
related_docs: string | null
related_links: string | null
attachments: string | null
keywords: string
applicable_scope: string
remarks: string
}
/** 审核列表项resource_type 为 all 时) */
/** 统一审核列表项。 */
export interface ReviewListItem {
type: 'document' | 'faq'
resource: DocumentResource | FaqResource
resource: Document | Faq
}
/** 分页响应类型 */
export interface PaginatedResponse<T> {
/** 审核分页结果。 */
export interface ReviewPage {
total: number
page: number
page_size: number
data: T[]
data: ReviewListItem[]
}
/** API响应包装类型 */
export interface ApiResponse<T = any> {
code: number
message: string
data: T
/** 当前审核人的待审数量。 */
export interface ReviewStats {
need_my_review_document: number
need_my_review_faq: number
need_my_review_total: number
need_my_review_unreviewed_document: number
need_my_review_unreviewed_faq: number
need_my_review_unreviewed_total: number
}
/** 获取审核列表参数 */
/** 审核列表参数 */
export interface FetchReviewListParams {
page?: number
page_size?: number
resource_type?: ReviewStatsResourceType
resource_type?: ReviewResourceType
}
/** 审核通过参数 */
export interface ApproveParams {
resource_type: 'document' | 'faq'
id: number
}
/** 审核拒绝参数 */
export interface RejectParams {
resource_type: 'document' | 'faq'
id: number
reason?: string
export interface RejectParams extends ApproveParams {
reason: string
}
/** 按当前登录用户统计需要本人审核的数量(不含本人为作者的稿件) */
export const fetchReviewStats = (params?: { resource_type?: ReviewStatsResourceType }) =>
request.get('/Kb/v1/review/stats', params ? { params } : undefined)
/** 获取当前用户可审核的待审资源。 */
export const fetchReviewList = (params: FetchReviewListParams) => request.get<KbReply<ReviewPage>>('/Kb/v1/review/list', { params })
/** 获取待审核列表 */
export const fetchReviewList = (params?: FetchReviewListParams) =>
request.get<ApiResponse<PaginatedResponse<ReviewListItem | DocumentResource | FaqResource>>>('/Kb/v1/review/list', { params })
/** 获取当前审核人的待审统计。 */
export const fetchReviewStats = (params: { resource_type?: ReviewResourceType } = {}) =>
request.get<KbReply<ReviewStats>>('/Kb/v1/review/stats', { params })
/** 审核通过 */
export const approveReview = (data: ApproveParams) => request.post<ApiResponse<string>>('/Kb/v1/review/approve', data)
/** 审核通过 */
export const approveReview = (data: ApproveParams) => request.post<KbReply<string>>('/Kb/v1/review/approve', data)
/** 审核拒绝 */
export const rejectReview = (data: RejectParams) => request.post<ApiResponse<string>>('/Kb/v1/review/reject', data)
/** 审核拒绝 */
export const rejectReview = (data: RejectParams) => request.post<KbReply<string>>('/Kb/v1/review/reject', data)
/** 获取文档详情 */
export const fetchDocumentDetail = (id: number) => request.get<ApiResponse<DocumentResource>>(`/Kb/v1/document/${id}`)
/** 获取FAQ详情 */
export const fetchFaqDetail = (id: number) => request.get<ApiResponse<FaqResource>>(`/Kb/v1/faq/${id}`)
/** 资源类型选项 */
export const resourceTypeOptions = [
export const resourceTypeOptions: Array<{ label: string; value: ReviewResourceType }> = [
{ label: '全部', value: 'all' },
{ label: '文档', value: 'document' },
{ label: 'FAQ', value: 'faq' },
]
/** 获取资源类型文 */
export const getResourceTypeText = (type: string): string => {
const typeMap: Record<string, string> = {
document: '文档',
faq: 'FAQ',
}
return typeMap[type] || type
}
/** 获取审核资源类型文案。 */
export const getResourceTypeText = (type: string) => ({ document: '文档', faq: 'FAQ' })[type] || type
/** 获取资源类型颜色 */
export const getResourceTypeColor = (type: string): string => {
const colorMap: Record<string, string> = {
document: 'arcoblue',
faq: 'green',
}
return colorMap[type] || 'gray'
}
/** 获取审核资源类型颜色 */
export const getResourceTypeColor = (type: string) => ({ document: 'arcoblue', faq: 'green' })[type] || 'gray'

View File

@@ -1,26 +1,14 @@
import { request } from '@/api/request'
import type { KbReply } from './types'
/** API响应包装类型 */
export interface ApiResponse<T = any> {
code: number
message: string
data: T
}
export type TrashResourceType = 'document' | 'faq'
/** 分页响应类型 */
export interface PaginatedResponse<T> {
total: number
page: number
page_size: number
data: T[]
}
/** 回收站记录 */
/** 回收站记录。 */
export interface TrashRecord {
id: number
created_at: string
updated_at: string
resource_type: 'document' | 'faq'
resource_type: TrashResourceType
resource_id: number
resource_name: string
deleted_by: number
@@ -31,58 +19,35 @@ export interface TrashRecord {
remarks: string
}
/** 获取回收站列表参数 */
export interface TrashPage {
total: number
page: number
page_size: number
data: TrashRecord[]
}
export interface FetchTrashListParams {
page?: number
page_size?: number
resource_type?: 'document' | 'faq'
resource_type?: TrashResourceType
}
/** 恢复资源请求参数 */
export interface RestoreTrashParams {
id: number
}
/** 彻底删除请求参数 */
export interface DeleteTrashParams {
id: number
}
/** 资源类型选项 */
export const resourceTypeOptions = [
export const resourceTypeOptions: Array<{ label: string; value: TrashResourceType }> = [
{ label: '文档', value: 'document' },
{ label: '常见问题', value: 'faq' },
{ label: 'FAQ', value: 'faq' },
]
/** 获取资源类型文 */
export const getResourceTypeText = (type: string): string => {
const typeMap: Record<string, string> = {
document: '文档',
faq: '常见问题',
}
return typeMap[type] || type
}
/** 获取回收站资源类型文案。 */
export const getResourceTypeText = (type: string) => ({ document: '文档', faq: 'FAQ' })[type] || type
/** 获取资源类型颜色 */
export const getResourceTypeColor = (type: string): string => {
const colorMap: Record<string, string> = {
document: 'blue',
faq: 'green',
}
return colorMap[type] || 'gray'
}
/** 获取回收站资源类型颜色 */
export const getResourceTypeColor = (type: string) => ({ document: 'blue', faq: 'green' })[type] || 'gray'
/** 获取回收站列表 */
export const fetchTrashList = (params?: FetchTrashListParams) => {
return request.get<ApiResponse<PaginatedResponse<TrashRecord>>>('/Kb/v1/trash/list', { params })
}
/** 获取回收站列表 */
export const fetchTrashList = (params: FetchTrashListParams) => request.get<KbReply<TrashPage>>('/Kb/v1/trash/list', { params })
/** 恢复资源 */
export const restoreTrash = (data: RestoreTrashParams) => {
return request.post<ApiResponse<string>>('/Kb/v1/trash/restore', data)
}
/** 恢复资源 */
export const restoreTrash = (id: number) => request.post<KbReply<string>>('/Kb/v1/trash/restore', { id })
/** 彻底删除 */
export const deleteTrash = (data: DeleteTrashParams) => {
return request.post<ApiResponse<string>>('/Kb/v1/trash/delete', data)
}
/** 彻底删除资源。 */
export const deleteTrash = (id: number) => request.post<KbReply<string>>('/Kb/v1/trash/delete', { id })

7
src/api/kb/types.ts Normal file
View File

@@ -0,0 +1,7 @@
/** 知识库服务统一响应。 */
export interface KbReply<T> {
code: number
message: string
details: T
timeseq: number
}

View File

@@ -18,6 +18,10 @@ export interface MessageRecord {
incidentId?: number
alertRecordId?: number
nextRetryAt?: string
occurrenceCount?: number
firstOccurredAt?: string
lastOccurredAt?: string
lastDeliveryId?: number
}
export type MessageListType = MessageRecord[]

View File

@@ -68,10 +68,11 @@ export const createRule = (data: {
severity_id: number
enabled?: boolean
metric_name?: string
query_expr?: string
data_source?: string
threshold?: number
compare_op?: string
duration?: number
eval_interval?: number
baseline_config?: string
labels?: string
annotations?: string
@@ -86,10 +87,11 @@ export const updateRule = (data: {
severity_id?: number
enabled?: boolean
metric_name?: string
query_expr?: string
data_source?: string
threshold?: number
compare_op?: string
duration?: number
eval_interval?: number
baseline_config?: string
labels?: string
annotations?: string

View File

@@ -28,8 +28,6 @@ export const fetchAlertRecordDetail = (id: number) => request.get(`/Alert/v1/rec
export const createAlertProcess = (data: {
alert_record_id: number
action: string // ack / resolve / silence / comment / assign / escalate / close
operator: string
operator_id?: string
comment?: string
assign_to?: string
assign_to_id?: string

View File

@@ -20,12 +20,13 @@ export interface AlertRule {
name: string
data_source: string
metric_name: string
rule_type: 'static' | 'dynamic' | 'promql'
rule_type: 'static' | 'dynamic'
compare_op: '>' | '>=' | '<' | '<=' | '==' | '!='
threshold: string | number
duration: number
eval_interval: number
severity_code: string
baseline_config?: string
labels?: Record<string, string>
annotations?: Record<string, string>
}
@@ -105,12 +106,11 @@ export const TEMPLATE_CATEGORIES = [
// 数据源选项
export const DATA_SOURCES = [
{ value: 'dc-host', label: '主机/操作系统指标' },
{ value: 'dc-hardware', label: '服务器硬件指标' },
{ value: 'dc-network', label: '网络设备指标' },
{ value: 'dc-database', label: '数据库指标' },
{ value: 'dc-middleware', label: '中间件指标' },
{ value: 'dc-virtualization', label: '虚拟化指标' },
{ value: 'dc-env', label: '动力/环境/安防指标' },
{ value: 'dc-security', label: '安全设备指标' },
{ value: 'dc-storage', label: '存储设备指标' },
{ value: 'dc-room-device', label: '机房设备指标' },
]
// 比较运算符选项
@@ -127,7 +127,6 @@ export const COMPARE_OPERATORS = [
export const RULE_TYPES = [
{ value: 'static', label: '静态阈值' },
{ value: 'dynamic', label: '动态阈值' },
{ value: 'promql', label: 'PromQL 表达式' },
]
// ==================== 告警模板接口 ====================

View File

@@ -42,13 +42,6 @@ export interface AssetListParams {
page_size?: number
keyword?: string
status?: string
category_id?: number
supplier_id?: number
datacenter_id?: number
floor_id?: number
room_id?: number
rack_id?: number
department?: string
sort?: string
order?: string
}
@@ -62,10 +55,11 @@ export interface AssetForm {
model?: string
manufacturer?: string
serial_number?: string
source_address?: string
purchase_date?: string
original_value?: number
supplier_id?: number
warranty_period?: string
warranty_period?: number
warranty_expiry?: string
department?: string
user?: string
@@ -74,9 +68,6 @@ export interface AssetForm {
datacenter_id?: number
floor_id?: number
room_id?: number
rack_id?: number
unit_start?: number
unit_end?: number
qr_code?: string
rfid_tag?: string
asset_tag?: string
@@ -137,21 +128,6 @@ export const fetchSupplierOptions = () => {
return request.get('/Assets/v1/supplier/all')
}
/** 获取数据中心列表(下拉) */
export const fetchDatacenterOptions = () => {
return request.get('/Assets/v1/datacenter/list')
}
/** 根据数据中心获取楼层列表 */
export const fetchFloorOptions = (datacenterId: number) => {
return request.get(`/Assets/v1/datacenter/${datacenterId}`)
}
/** 获取机柜列表(下拉) */
export const fetchRackOptions = (params?: { datacenter_id?: number; floor_id?: number }) => {
return request.post('/Assets/v1/rack/list', params || {})
}
/** 绑定资产和监控资源 */
export const linkAssetResource = (data: {
asset_id: number

View File

@@ -61,8 +61,3 @@ export const fetchDatacenterByCity = (cityId: number) => {
export const fetchDatacenterTree = () => {
return request.get('/Assets/v1/datacenter/tree')
}
/** 获取3D机房后端导出契约数据 */
export const fetchDatacenterThreeDExport = (params?: { datacenter_ids?: string; room_ids?: string }) => {
return request.get('/Assets/v1/three-d/export', { params })
}

View File

@@ -1,22 +1,27 @@
import { request } from '@/api/request'
/** 许可证配置(与 DC-Control `LicenceConfig` / 接口 `data` 一致;字段按实际响应可能部分缺失) */
export interface LicenceConfig {
title?: string
version?: string
company_name?: string
create_time?: string
expire_time?: string
machine_code?: string
max_database?: number
max_middleware?: number
max_pc?: number
max_server?: number
max_client?: number
max_user?: number
max_role?: number
max_permission?: number
max_menu?: number
export interface LicenceQuotas {
max_database: number
max_middleware: number
max_network_device: number
max_security: number
max_storage: number
max_pc: number
max_server: number
max_user: number
max_role: number
max_permission: number
max_menu: number
}
export interface LicenceInfo {
id: string
platform_name: string
workspace: string
issued_on: string
valid_from: string
expires_on: string
quotas: LicenceQuotas
}
/** 获取 采集器 */
@@ -39,7 +44,8 @@ export const updateCollector = (data: any) => request.put(`/DC-Control/v1/collec
export const fetchCollectorStatistics = () => request.get('/DC-Control/v1/statistics')
/** 获取 许可证信息 */
export const fetchLicenseInfo = () => request.get<{ code?: number; data?: LicenceConfig; message?: string }>('/DC-Control/v1/license')
export const fetchLicenseInfo = () =>
request.get<{ code: number; details: LicenceInfo; message: string; timeseq: number }>('/DC-Control/v1/license')
export interface PageResult<T> {
total: number
@@ -182,7 +188,9 @@ export const fetchControlResources = (params?: ResourceListParams) =>
request.get<{ code?: number; details?: PageResult<ControlResource>; message?: string }>('/DC-Control/v1/resources', { params })
export const fetchControlResourceOptions = (params?: { resource_category?: string }) =>
request.get<{ code?: number; details?: { list: OptionItem[]; count: number }; message?: string }>('/DC-Control/v1/resources/options', { params })
request.get<{ code?: number; details?: { list: OptionItem[]; count: number }; message?: string }>('/DC-Control/v1/resources/options', {
params,
})
export const createControlResource = (data: ControlResourcePayload) =>
request.post<{ code?: number; details?: ControlResource; message?: string }>('/DC-Control/v1/resources', data)
@@ -203,10 +211,9 @@ export const fetchControlResourceTypeOptions = () =>
request.get<{ code?: number; details?: { list: OptionItem[]; count: number }; message?: string }>('/DC-Control/v1/resource-types/options')
export const fetchControlMetricDefinitions = (params?: { page?: number; size?: number; keyword?: string; resource_category?: string }) =>
request.get<{ code?: number; details?: PageResult<ControlMetricDefinition>; message?: string }>(
'/DC-Control/v1/metric-definitions',
{ params }
)
request.get<{ code?: number; details?: PageResult<ControlMetricDefinition>; message?: string }>('/DC-Control/v1/metric-definitions', {
params,
})
export const fetchControlMetricDefinitionOptions = (params?: { resource_category?: string }) =>
request.get<{ code?: number; details?: { list: OptionItem[]; count: number }; message?: string }>(
@@ -218,10 +225,14 @@ export const fetchControlMetricSeries = (params?: MetricSeriesListParams) =>
request.get<{ code?: number; details?: PageResult<ControlMetricSeries>; message?: string }>('/DC-Control/v1/metric-series', { params })
export const fetchControlBusinessSystems = (params?: { page?: number; size?: number; keyword?: string; status?: string }) =>
request.get<{ code?: number; details?: PageResult<ControlBusinessSystem>; message?: string }>('/DC-Control/v1/business-systems', { params })
request.get<{ code?: number; details?: PageResult<ControlBusinessSystem>; message?: string }>('/DC-Control/v1/business-systems', {
params,
})
export const fetchControlBusinessSystemOptions = () =>
request.get<{ code?: number; details?: { list: OptionItem[]; count: number }; message?: string }>('/DC-Control/v1/business-systems/options')
request.get<{ code?: number; details?: { list: OptionItem[]; count: number }; message?: string }>(
'/DC-Control/v1/business-systems/options'
)
export const fetchCollectionStatus = (params?: {
page?: number
@@ -231,7 +242,6 @@ export const fetchCollectionStatus = (params?: {
resource_uid?: string
status?: string
}) =>
request.get<{ code?: number; details?: PageResult<ControlCollectionStatus>; message?: string }>(
'/DC-Control/v1/collection/status',
{ params }
)
request.get<{ code?: number; details?: PageResult<ControlCollectionStatus>; message?: string }>('/DC-Control/v1/collection/status', {
params,
})

View File

@@ -1,5 +1,29 @@
import { request } from '@/api/request'
export interface Floor {
id?: number
name?: string
code?: string
datacenter_id?: number
floor_number?: number
status?: string
area?: number
height?: number
load_bearing?: number
enabled?: boolean
layout_plan?: string
layout_plan_file_id?: string
description?: string
}
export type SaveFloorPayload = Omit<Floor, 'layout_plan'>
interface ApiResponse<T> {
code: number
message: string
details: T
}
/** 获取楼层列表(分页) */
export const fetchFloorList = (data?: { page?: number; page_size?: number; keyword?: string; datacenter_id?: number; status?: string }) => {
return request.post('/Assets/v1/floor/list', data || {})
@@ -16,13 +40,13 @@ export const fetchFloorListByDatacenter = (datacenterId: number, params?: { name
}
/** 创建楼层 */
export const createFloor = (data: any) => {
return request.post('/Assets/v1/floor/create', data)
export const createFloor = (data: SaveFloorPayload) => {
return request.post<ApiResponse<Floor>>('/Assets/v1/floor/create', data)
}
/** 更新楼层 */
export const updateFloor = (data: any) => {
return request.put('/Assets/v1/floor/update', data)
export const updateFloor = (data: SaveFloorPayload) => {
return request.put<ApiResponse<Floor>>('/Assets/v1/floor/update', data)
}
/** 删除楼层 */
@@ -31,7 +55,6 @@ export const deleteFloor = (id: number) => {
}
/** 获取数据中心列表(用于下拉选择) */
export const fetchDatacenterList = (params?: { keyword?: string; name?: string }) => {
const normalizedParams = params?.keyword ? { ...params, name: params.name ?? params.keyword } : params
return request.get('/Assets/v1/datacenter/all', { params: normalizedParams })
export const fetchDatacenterList = (params?: { keyword?: string }) => {
return request.get('/Assets/v1/datacenter/all', { params })
}

48
src/api/ops/floorFile.ts Normal file
View File

@@ -0,0 +1,48 @@
import { request } from '@/api/request'
export interface PendingUpload {
file_id: string
object_key: string
upload: {
method: string
url: string
headers: Record<string, string>
expires_at: string
}
}
interface ApiResponse<T> {
code: number
message: string
details: T
}
/** 初始化楼层布局文件并直传 OSS。 */
export async function uploadFloorLayout(file: File): Promise<PendingUpload> {
const response = await request.post<ApiResponse<PendingUpload>>('/Assets/v1/floor/layout/init', {
filename: file.name,
size: file.size,
content_type: file.type || 'application/octet-stream',
})
if (response.code !== 0) {
throw new Error(response.message || '初始化楼层布局文件上传失败')
}
const pendingUpload = response.details
if (!pendingUpload?.file_id || !pendingUpload.upload?.method || !pendingUpload.upload?.url) {
throw new Error('楼层布局文件上传参数不完整')
}
const uploadResponse = await fetch(pendingUpload.upload.url, {
method: pendingUpload.upload.method,
headers: pendingUpload.upload.headers,
body: file,
credentials: 'omit',
})
if (!uploadResponse.ok) {
throw new Error(`上传楼层布局文件失败: HTTP ${uploadResponse.status}`)
}
return pendingUpload
}

View File

@@ -23,17 +23,6 @@ export interface GoViewProjectData {
createUserId: string
}
export interface GoViewFile {
id: string
fileName: string
fileSize: number
fileSuffix: string
virtualKey: string
relativePath: string
absolutePath: string
createTime: string
}
export interface OssInfo {
BucketName: string
bucketURL: string
@@ -106,15 +95,6 @@ export const publishProject = (data: { identity: string; state: -1 | 1 }) => {
return request.put<GoViewResponse<{ identity: string; state: number }>>('/Visual/v1/project/publish', data)
}
/** 上传文件 */
export const uploadFile = (file: File) => {
const formData = new FormData()
formData.append('object', file)
return request.post<GoViewResponse<GoViewFile>>('/Visual/v1/project/upload', formData, {
headers: { 'Content-Type': 'multipart/form-data' },
})
}
/** 保存项目数据 */
export const saveProjectData = (projectId: string, content: string) => {
const formData = new URLSearchParams()

View File

@@ -34,6 +34,7 @@ export interface HostHardwareDevice {
snmp_port?: number
community?: string
snmp_version?: string
snmp_oids?: string
redfish_base_url?: string
redfish_tls_skip_verify?: boolean
ipmi_timeout_seconds?: number
@@ -65,6 +66,7 @@ export interface HostHardwareDeviceUpsert {
snmp_port?: number
community?: string
snmp_version?: string
snmp_oids?: string
redfish_base_url?: string
redfish_tls_skip_verify?: boolean
location?: string

View File

@@ -48,5 +48,5 @@ export const fetchIncidentAlerts = (id: number) => request.get(`/Alert/v1/incide
export const fetchIncidentTimeline = (id: number) => request.get(`/Alert/v1/incidents/${id}/timeline`)
export const transitionIncident = (id: number, data: { status: string; comment?: string; operator?: string }) =>
export const transitionIncident = (id: number, data: { status: string; comment?: string }) =>
request.post(`/Alert/v1/incidents/${id}/transition`, data)

View File

@@ -1,5 +1,13 @@
import { request } from '@/api/request'
/** IPAM服务统一响应 */
export interface IPAMResponse<T> {
code: number
details: T
message?: string
timeseq?: number
}
/** IP地址运行状态 */
export type IPStatus = 'online' | 'offline' | 'unknown'
@@ -118,7 +126,8 @@ export interface IPGroupListParams {
/** IP分组列表响应 */
export interface IPGroupListResponse {
data: IPGroupItem[]
list: IPGroupItem[]
count: number
}
/** IP分组表单数据 */
@@ -359,92 +368,96 @@ export interface IPAnomalyFormData {
/** ========== 概览 API ========== */
/** 获取IPAM概览统计 */
export const fetchIPAMOverview = () => request.get<IPAMOverview>('/DC-Control/v1/ipam/overview')
export const fetchIPAMOverview = () => request.get<IPAMResponse<IPAMOverview>>('/DC-Control/v1/ipam/overview')
/** ========== IP地址 API ========== */
/** 获取IP地址列表 */
export const fetchIPAddressList = (params?: IPAddressListParams) =>
request.get<IPAddressListResponse>('/DC-Control/v1/ipaddresses', { params })
request.get<IPAMResponse<IPAddressListResponse>>('/DC-Control/v1/ipaddresses', { params })
/** 获取IP地址详情 */
export const fetchIPAddressDetail = (id: number) => request.get<IPAddressItem>(`/DC-Control/v1/ipaddresses/${id}`)
export const fetchIPAddressDetail = (id: number) => request.get<IPAMResponse<IPAddressItem>>(`/DC-Control/v1/ipaddresses/${id}`)
/** 创建IP地址 */
export const createIPAddress = (data: IPAddressFormData) => request.post<IPAddressItem>('/DC-Control/v1/ipaddresses', data)
export const createIPAddress = (data: IPAddressFormData) => request.post<IPAMResponse<IPAddressItem>>('/DC-Control/v1/ipaddresses', data)
/** 更新IP地址 */
export const updateIPAddress = (id: number, data: Partial<IPAddressFormData>) =>
request.put<{ message: string }>(`/DC-Control/v1/ipaddresses/${id}`, data)
request.put<IPAMResponse<{ message: string }>>(`/DC-Control/v1/ipaddresses/${id}`, data)
/** 删除IP地址 */
export const deleteIPAddress = (id: number) => request.delete<{ message: string }>(`/DC-Control/v1/ipaddresses/${id}`)
export const deleteIPAddress = (id: number) => request.delete<IPAMResponse<{ message: string }>>(`/DC-Control/v1/ipaddresses/${id}`)
/** ========== IP分组 API ========== */
/** 获取IP分组列表树形 */
export const fetchIPGroupList = (params?: IPGroupListParams) => request.get<IPGroupListResponse>('/DC-Control/v1/ip-groups', { params })
export const fetchIPGroupList = (params?: IPGroupListParams) =>
request.get<IPAMResponse<IPGroupListResponse>>('/DC-Control/v1/ip-groups', { params })
/** 创建IP分组 */
export const createIPGroup = (data: IPGroupFormData) => request.post<IPGroupItem>('/DC-Control/v1/ip-groups', data)
export const createIPGroup = (data: IPGroupFormData) => request.post<IPAMResponse<IPGroupItem>>('/DC-Control/v1/ip-groups', data)
/** 更新IP分组 */
export const updateIPGroup = (id: number, data: Partial<IPGroupFormData>) =>
request.put<{ message: string }>(`/DC-Control/v1/ip-groups/${id}`, data)
request.put<IPAMResponse<{ message: string }>>(`/DC-Control/v1/ip-groups/${id}`, data)
/** 删除IP分组 */
export const deleteIPGroup = (id: number) => request.delete<{ message: string }>(`/DC-Control/v1/ip-groups/${id}`)
export const deleteIPGroup = (id: number) => request.delete<IPAMResponse<{ message: string }>>(`/DC-Control/v1/ip-groups/${id}`)
/** ========== IP子网 API ========== */
/** 获取IP子网列表 */
export const fetchIPSubnetList = (params?: IPSubnetListParams) => request.get<IPSubnetListResponse>('/DC-Control/v1/ip-subnets', { params })
export const fetchIPSubnetList = (params?: IPSubnetListParams) =>
request.get<IPAMResponse<IPSubnetListResponse>>('/DC-Control/v1/ip-subnets', { params })
/** 获取IP子网详情 */
export const fetchIPSubnetDetail = (id: number) => request.get<IPSubnetItem>(`/DC-Control/v1/ip-subnets/${id}`)
export const fetchIPSubnetDetail = (id: number) => request.get<IPAMResponse<IPSubnetItem>>(`/DC-Control/v1/ip-subnets/${id}`)
/** 创建IP子网 */
export const createIPSubnet = (data: IPSubnetFormData) => request.post<IPSubnetItem>('/DC-Control/v1/ip-subnets', data)
export const createIPSubnet = (data: IPSubnetFormData) => request.post<IPAMResponse<IPSubnetItem>>('/DC-Control/v1/ip-subnets', data)
/** 更新IP子网 */
export const updateIPSubnet = (id: number, data: Partial<IPSubnetFormData>) =>
request.put<{ message: string }>(`/DC-Control/v1/ip-subnets/${id}`, data)
request.put<IPAMResponse<{ message: string }>>(`/DC-Control/v1/ip-subnets/${id}`, data)
/** 删除IP子网 */
export const deleteIPSubnet = (id: number) => request.delete<{ message: string }>(`/DC-Control/v1/ip-subnets/${id}`)
export const deleteIPSubnet = (id: number) => request.delete<IPAMResponse<{ message: string }>>(`/DC-Control/v1/ip-subnets/${id}`)
/** ========== DHCP租约 API ========== */
/** 获取DHCP租约列表 */
export const fetchDHCPLeaseList = (params?: DHCPLeaseListParams) =>
request.get<DHCPLeaseListResponse>('/DC-Control/v1/ipam/dhcp-leases', { params })
request.get<IPAMResponse<DHCPLeaseListResponse>>('/DC-Control/v1/ipam/dhcp-leases', { params })
/** 创建DHCP租约 */
export const createDHCPLease = (data: DHCPLeaseFormData) => request.post<DHCPLeaseItem>('/DC-Control/v1/ipam/dhcp-leases', data)
export const createDHCPLease = (data: DHCPLeaseFormData) =>
request.post<IPAMResponse<DHCPLeaseItem>>('/DC-Control/v1/ipam/dhcp-leases', data)
/** ========== IP冲突 API ========== */
/** 获取IP冲突列表 */
export const fetchIPConflictList = (params?: IPConflictListParams) =>
request.get<IPConflictListResponse>('/DC-Control/v1/ipam/conflicts', { params })
request.get<IPAMResponse<IPConflictListResponse>>('/DC-Control/v1/ipam/conflicts', { params })
/** 创建IP冲突记录 */
export const createIPConflict = (data: IPConflictFormData) => request.post<IPConflictItem>('/DC-Control/v1/ipam/conflicts', data)
export const createIPConflict = (data: IPConflictFormData) =>
request.post<IPAMResponse<IPConflictItem>>('/DC-Control/v1/ipam/conflicts', data)
/** ========== IP变更 API ========== */
/** 获取IP变更列表 */
export const fetchIPChangeList = (params?: IPChangeListParams) =>
request.get<IPChangeListResponse>('/DC-Control/v1/ipam/changes', { params })
request.get<IPAMResponse<IPChangeListResponse>>('/DC-Control/v1/ipam/changes', { params })
/** 创建IP变更记录 */
export const createIPChange = (data: IPChangeFormData) => request.post<IPChangeItem>('/DC-Control/v1/ipam/changes', data)
export const createIPChange = (data: IPChangeFormData) => request.post<IPAMResponse<IPChangeItem>>('/DC-Control/v1/ipam/changes', data)
/** ========== IP异常 API ========== */
/** 获取IP异常列表 */
export const fetchIPAnomalyList = (params?: IPAnomalyListParams) =>
request.get<IPAnomalyListResponse>('/DC-Control/v1/ipam/anomalies', { params })
request.get<IPAMResponse<IPAnomalyListResponse>>('/DC-Control/v1/ipam/anomalies', { params })
/** 创建IP异常记录 */
export const createIPAnomaly = (data: IPAnomalyFormData) => request.post<IPAnomalyItem>('/DC-Control/v1/ipam/anomalies', data)
export const createIPAnomaly = (data: IPAnomalyFormData) => request.post<IPAMResponse<IPAnomalyItem>>('/DC-Control/v1/ipam/anomalies', data)

View File

@@ -76,6 +76,8 @@ export interface SyslogRule {
keyword_regex: string
source_match: string
message_regex: string
recovery_match_regex: string
lifecycle_key: string
alert_name: string
severity_code: string
severity_mapping_json: string
@@ -92,6 +94,8 @@ export interface TrapRule {
priority: number
oid_prefix: string
varbind_match_regex: string
recovery_match_regex: string
lifecycle_key: string
alert_name: string
severity_code: string
policy_id: number

View File

@@ -32,7 +32,7 @@ export const createNoticeChannel = (data: {
email_to?: string[]
email_cc?: string[]
email_bcc?: string[]
email_use_tls?: boolean
email_security_mode?: 'none' | 'starttls' | 'tls'
sms_provider?: string
sms_access_key_id?: string
sms_access_key_secret?: string
@@ -40,6 +40,7 @@ export const createNoticeChannel = (data: {
sms_template_code?: string
sms_phones?: string[]
in_app_targets?: string[]
in_app_aggregation_window?: number
}) => {
return request.post('/Alert/v1/channel/create', data)
}
@@ -67,7 +68,7 @@ export const updateNoticeChannel = (data: {
email_to?: string[]
email_cc?: string[]
email_bcc?: string[]
email_use_tls?: boolean
email_security_mode?: 'none' | 'starttls' | 'tls'
sms_provider?: string
sms_access_key_id?: string
sms_access_key_secret?: string
@@ -75,6 +76,7 @@ export const updateNoticeChannel = (data: {
sms_template_code?: string
sms_phones?: string[]
in_app_targets?: string[]
in_app_aggregation_window?: number
}) => {
return request.post('/Alert/v1/channel/update', data)
}

View File

@@ -21,9 +21,14 @@ export const fetchRackListByDatacenter = (datacenterId: number, params?: { name?
return request.get(`/Assets/v1/rack/datacenter/${datacenterId}`, { params })
}
/** 根据楼层获取机柜列表(下拉,不分页)- 已废弃,请使用 fetchRackListByRoom */
export const fetchRackListByFloor = (floorId: number, params?: { name?: string }) => {
return request.get(`/Assets/v1/rack/floor/${floorId}`, { params })
/** 根据楼层获取机柜列表 */
export const fetchRackListByFloor = (floorId: number, params?: { keyword?: string }) => {
return fetchRackList({
page: 1,
page_size: 100,
floor_id: floorId,
keyword: params?.keyword,
})
}
/** 根据机房获取机柜列表(下拉,不分页) */
@@ -62,13 +67,6 @@ export const fetchSupplierList = () => {
}
/** 获取数据中心列表(用于下拉选择) */
export const fetchDatacenterList = (params?: { keyword?: string; name?: string }) => {
const normalizedParams = params?.keyword ? { ...params, name: params.name ?? params.keyword } : params
return request.get('/Assets/v1/datacenter/all', { params: normalizedParams })
}
/** 获取楼层列表(用于下拉选择) */
export const fetchFloorListForSelect = (params?: { datacenter_id?: number; keyword?: string; name?: string }) => {
const normalizedParams = params?.keyword ? { ...params, name: params.name ?? params.keyword } : params
return request.get('/Assets/v1/floor/all', { params: normalizedParams })
export const fetchDatacenterList = (params?: { keyword?: string }) => {
return request.get('/Assets/v1/datacenter/all', { params })
}

View File

@@ -1,5 +1,5 @@
import { request } from "@/api/request"
import SafeStorage, { AppStorageKey } from "@/utils/safeStorage"
import { request } from '@/api/request'
import SafeStorage, { AppStorageKey } from '@/utils/safeStorage'
// ============ 通用响应类型 ============
@@ -18,6 +18,7 @@ export enum ReportType {
FAULT = 'fault',
SERVER = 'server',
NETWORK_DEVICE = 'network_device',
OPERATIONS = 'operations',
EVIDENCE = 'evidence',
}
@@ -32,7 +33,7 @@ export enum ReportStatus {
export interface ReportRecord {
id: number
/** 列表接口可能含已下线类型字符串,仅六类可再次生成 */
/** 列表接口可能含已下线或自动生成的报表类型 */
report_type: ReportType | string
title: string
description?: string
@@ -43,6 +44,8 @@ export interface ReportRecord {
file_path?: string
file_size?: number
file_mime?: string
started_at?: string
finished_at?: string
created_at: string
updated_at: string
}
@@ -64,6 +67,13 @@ export interface PageResult<T> {
data: T[]
}
export type ReportExportFormat = 'csv' | 'xlsx' | 'pdf'
export interface DownloadedReport {
blob: Blob
fileName: string
}
// ============ 报表生成参数接口 ============
export interface TrafficReportParams {
@@ -127,9 +137,7 @@ export interface NetworkDeviceReportParams {
export interface StatisticsReportParams {
data_source: 'dc-host' | 'dc-network' | 'dc-database' | 'dc-middleware'
/** 与 metric_name 二选一;网络等指标优先用 metric_id */
metric_id?: string
metric_name?: string
metric_id: string
target_identities: string[]
start_time: string
end_time: string
@@ -152,16 +160,13 @@ export interface HistoryReportParams {
export interface TopNReportParams {
data_source: 'dc-host' | 'dc-network' | 'dc-database' | 'dc-middleware' | 'alert'
metric_id?: string
metric_name?: string
metric_id: string
target_identities: string[]
start_time: string
end_time: string
n?: number
rank_aggregate?: 'avg' | 'max' | 'min' | 'last'
order?: 'desc' | 'asc'
metric_type?: 'cpu' | 'disk' | 'io' | 'memory' | 'network'
collector_identity?: string
}
/** 证据导出事件行 */
@@ -230,7 +235,8 @@ export const fetchReportMetricsAvailable = (params: {
identities?: string
keyword?: string
limit?: number
}) => request.get<ApiResponse<{ data_source: string; items: any[]; registry: any[] }>>('/DC-Control/v1/reports/metrics/available', { params })
}) =>
request.get<ApiResponse<{ data_source: string; items: any[]; registry: any[] }>>('/DC-Control/v1/reports/metrics/available', { params })
/** 逻辑指标目录Registry */
export const fetchReportMetricsRegistry = (params: { data_source: string }) =>
@@ -239,8 +245,8 @@ export const fetchReportMetricsRegistry = (params: { data_source: string }) =>
/** 查看报表内容 */
export const fetchReportContent = (id: number) => request.get<ApiResponse<Record<string, any>>>(`/DC-Control/v1/reports/${id}/content`)
/** 原始 ArrayBuffer → 下载用 Blobxlsx 校验 ZIP 魔数 PK */
function exportBufferToBlob(ab: ArrayBuffer, format: 'csv' | 'xlsx', contentType: string | null): Blob {
/** 原始 ArrayBuffer → 下载用 Blobxlsx 校验 ZIP 魔数 PKPDF 校验 %PDF- */
function exportBufferToBlob(ab: ArrayBuffer, format: ReportExportFormat, contentType: string | null): Blob {
const u8 = new Uint8Array(ab)
if (format === 'xlsx') {
const okZip = u8.length >= 4 && u8[0] === 0x50 && u8[1] === 0x4b
@@ -265,16 +271,62 @@ function exportBufferToBlob(ab: ArrayBuffer, format: 'csv' | 'xlsx', contentType
: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
return new Blob([ab], { type: mime })
}
const mime =
contentType && /csv|text|plain/i.test(contentType) ? contentType : 'text/csv;charset=utf-8'
if (format === 'pdf') {
const okPdf = u8.length >= 5 && u8[0] === 0x25 && u8[1] === 0x50 && u8[2] === 0x44 && u8[3] === 0x46 && u8[4] === 0x2d
if (!okPdf) {
throw new Error('导出失败:服务器返回的不是有效的 PDF。')
}
const mime = contentType && /pdf/i.test(contentType) ? contentType : 'application/pdf'
return new Blob([ab], { type: mime })
}
const mime = contentType && /csv|text|plain/i.test(contentType) ? contentType : 'text/csv;charset=utf-8'
return new Blob([ab], { type: mime })
}
function parseContentDispositionFileName(value: string | null): string | undefined {
if (!value) return undefined
const utf8FileName = value.match(/filename\*=UTF-8''([^;]+)/i)
if (utf8FileName) {
try {
return decodeURIComponent(utf8FileName[1])
} catch {
// 编码异常时继续读取普通文件名
}
}
return value.match(/filename="([^"]+)"/i)?.[1]
}
function normalizeReportFileName(fileName: string | undefined, id: number, format: ReportExportFormat): string {
const defaultName = `report_${id}.${format}`
if (!fileName) return defaultName
let name = fileName.split(/[\\/]/).pop() || ''
name = name
.replace(/[\u0000-\u001F\u007F-\u009F\u061C\u200E\u200F\u202A-\u202E\u2066-\u2069]/g, '')
.replace(/[<>:"/\\|?*]/g, '_')
.replace(/[ .]+$/, '')
const extensionIndex = name.lastIndexOf('.')
if (extensionIndex > 0) {
name = name.slice(0, extensionIndex).replace(/[ .]+$/, '')
}
if (!name) return defaultName
const reservedName = /^(con|prn|aux|nul|com[1-9¹²³]|lpt[1-9¹²³])$/i
if (reservedName.test(name.split('.')[0])) {
name = `_${name}`
}
return `${name}.${format}`
}
/**
* 报表文件导出:使用 fetch + arrayBuffer绕过 axios 拦截器与 Blob 中间态,
* 避免网络面板里已是合法 xlsxPK…但落盘文件损坏的情况。
*/
export const exportReport = async (id: number, format: 'csv' | 'xlsx' = 'csv'): Promise<Blob> => {
export const exportReport = async (id: number, format: ReportExportFormat = 'csv'): Promise<DownloadedReport> => {
const base = String(import.meta.env.VITE_API_BASE_URL || '').replace(/\/$/, '')
const url = `${base}/DC-Control/v1/reports/${id}/export?format=${encodeURIComponent(format)}`
const token = SafeStorage.get(AppStorageKey.TOKEN)
@@ -296,7 +348,10 @@ export const exportReport = async (id: number, format: 'csv' | 'xlsx' = 'csv'):
}
throw new Error(msg)
}
return exportBufferToBlob(ab, format, r.headers.get('content-type'))
return {
blob: exportBufferToBlob(ab, format, r.headers.get('content-type')),
fileName: normalizeReportFileName(parseContentDispositionFileName(r.headers.get('content-disposition')), id, format),
}
}
// ============ 监测指标类接口(旧版兼容) ============

View File

@@ -9,7 +9,7 @@ export interface RoomDeviceItem {
service_identity: string
name: string
description: string
room_id: string
room_id: number
device_code?: string
device_category: string
type?: string
@@ -69,7 +69,7 @@ export interface RoomDeviceCreateData {
service_identity?: string
name: string
description?: string
room_id: string
room_id: number
device_category: string
agent_config?: string
collect_method?: 'api' | 'snmp'
@@ -97,7 +97,7 @@ export interface RoomDeviceCreateData {
export interface RoomDeviceUpdateData {
name?: string
description?: string
room_id?: string
room_id?: number
device_category?: string
agent_config?: string
collect_method?: 'api' | 'snmp'

View File

@@ -11,7 +11,9 @@ export interface RoomItem {
description?: string
floor_id: number
datacenter_id: number
status?: string
layout_version: number
status: string
has3D: boolean
floor?: { id: number; name: string }
datacenter?: { id: number; name: string }
}
@@ -25,6 +27,13 @@ export interface RoomListResponse {
}
}
/** 机房详情响应 */
export interface RoomDetailResponse {
code: number
message?: string
details: RoomItem
}
/** 机房列表查询参数 */
export interface RoomListParams {
page?: number
@@ -53,6 +62,12 @@ export interface RoomOptionItem {
sort: number
}
export interface ApiResponse<T> {
code: number
message?: string
details: T
}
/** 机房创建数据 */
export interface RoomCreateData {
name: string
@@ -60,7 +75,7 @@ export interface RoomCreateData {
description?: string
floor_id: number
datacenter_id: number
status?: string
status: string
}
/** 机房更新数据 */
@@ -81,17 +96,17 @@ export const fetchRoomList = (data?: RoomListParams) => {
/** 获取机房详情 */
export const fetchRoomDetail = (id: number) => {
return request.get<RoomItem>(`/Assets/v1/room/detail/${id}`)
return request.get<RoomDetailResponse>(`/Assets/v1/room/detail/${id}`)
}
/** 获取机房列表(下拉,不分页) */
export const fetchRoomOptions = (params?: RoomOptionsParams) => {
return request.get<RoomOptionItem[]>('/Assets/v1/room/all', { params })
return request.get<ApiResponse<RoomOptionItem[]>>('/Assets/v1/room/all', { params })
}
/** 根据楼层获取机房列表(下拉,不分页) */
export const fetchRoomListByFloor = (floorId: number, params?: { name?: string }) => {
return request.get<RoomItem[]>(`/Assets/v1/room/floor/${floorId}`, { params })
return request.get<ApiResponse<RoomItem[]>>(`/Assets/v1/room/floor/${floorId}`, { params })
}
/** 创建机房 */

View File

@@ -67,7 +67,7 @@ export interface StorageListParams {
/** 创建存储设备请求参数 */
export interface StorageCreateData {
service_identity: string
service_identity?: string
name: string
category?: string
type: string

View File

@@ -0,0 +1,370 @@
import { request } from '@/api/request'
/** 3D 机房接口标准响应。 */
interface ThreeMachineRoomResponse<T> {
code: number
message?: string
details: T
}
/** 告警级别。 */
export interface ThreeMachineRoomSeverity {
id?: number
name?: string
code?: string
color?: string
priority?: number
}
/** 三维坐标和旋转。 */
export interface ThreeMachineRoomTransform {
position_x: number
position_y: number
position_z: number
rotation_x?: number
rotation_y?: number
rotation_z?: number
}
/** 独立设备三维尺寸。 */
export interface ThreeMachineRoomSize {
width: number
height: number
depth: number
}
/** 设备绑定的监控资源。 */
export interface ThreeMachineRoomBinding {
resource_uid: string
resource_category?: string
service_identity?: string
display_name?: string
business_system_id?: number | null
}
/** 监控指标。 */
export interface ThreeMachineRoomMetric {
name: string
value?: string | number | null
unit?: string
type?: string
timestamp?: string
}
/** 设备资源运行数据。 */
export interface ThreeMachineRoomResource extends ThreeMachineRoomBinding {
status?: string
metrics_at?: string
metrics?: ThreeMachineRoomMetric[]
}
/** 活动告警。 */
export interface ThreeMachineRoomAlert {
id?: number
alert_name?: string
summary?: string
severity?: ThreeMachineRoomSeverity
status?: string
starts_at?: string
last_seen_at?: string
updated_at?: string
}
/** 按监控资源分组的活动告警。 */
export interface ThreeMachineRoomAlertGroup {
resource_uid: string
active_count?: number
highest_severity?: ThreeMachineRoomSeverity
alerts?: ThreeMachineRoomAlert[]
}
/** 机柜 U 位。 */
export interface ThreeMachineRoomUnit {
id?: number
unit_number: number
status: 'available' | 'occupied' | 'reserved' | 'disabled'
asset_id?: number | null
}
/** 机柜中的资产设备。 */
export interface ThreeMachineRoomDevice {
asset_id: number
asset_code?: string
asset_name?: string
category_id?: number | null
category_code?: string
category_name?: string
placement_type?: 'rack' | 'room' | 'unplaced'
rack_id?: number | null
unit_start?: number | null
unit_end?: number | null
occupied_units?: number
power_consumption?: number
transform?: ThreeMachineRoomTransform
size?: ThreeMachineRoomSize
bindings?: ThreeMachineRoomBinding[]
}
/** 3D 场景机柜。 */
export interface ThreeMachineRoomRack {
id: number
code?: string
name?: string
row?: number
column?: number
height?: number
width_mm?: number
depth_mm?: number
status?: string
transform?: ThreeMachineRoomTransform
utilization_rate?: number
units?: ThreeMachineRoomUnit[]
devices?: ThreeMachineRoomDevice[]
}
/** 3D 机房场景数据。 */
export interface ThreeMachineRoomScene {
room: {
id: number
datacenter_id?: number
floor_id?: number
name?: string
code?: string
scene_length?: number
scene_width?: number
scene_height?: number
layout_version?: number
[key: string]: unknown
}
racks: ThreeMachineRoomRack[]
room_devices?: ThreeMachineRoomDevice[]
summary?: {
rack_count?: number
unit_count?: number
rack_device_count?: number
room_device_count?: number
device_count?: number
}
[key: string]: unknown
}
/** 设备状态信号。 */
export interface ThreeMachineRoomSignal {
asset_id: number
status?: 'normal' | 'warning' | 'abnormal' | string
active_alert_count?: number
highest_alert_severity?: ThreeMachineRoomSeverity
resources?: ThreeMachineRoomResource[]
alerts?: ThreeMachineRoomAlertGroup[]
[key: string]: unknown
}
/** 机房状态信号集合。 */
export interface ThreeMachineRoomSignals {
signals: ThreeMachineRoomSignal[]
[key: string]: unknown
}
/** 设备可观测数据。 */
export interface ThreeMachineRoomObservability {
device?: ThreeMachineRoomDevice
resources?: ThreeMachineRoomResource[]
alerts?: ThreeMachineRoomAlertGroup[]
[key: string]: unknown
}
/** 3D 场景全量导出数据。 */
export interface ThreeMachineRoomExport {
datacenters: Array<{
id: number
name?: string
code?: string
status?: string
latitude?: string
longitude?: string
rooms?: ThreeMachineRoomScene[]
}>
summary?: {
datacenter_count?: number
room_count?: number
rack_count?: number
unit_count?: number
device_count?: number
}
}
/** 机柜布局保存项。 */
export interface ThreeMachineRoomRackLayout {
rack_id: number
row?: number
column?: number
position_x: number
position_y: number
position_z: number
rotation_y: number
}
/** 机房 3D 初始化配置。 */
export interface ThreeMachineRoomConfigData {
expectedVersion: number
sceneLength: number
sceneWidth: number
sceneHeight: number
racks: ThreeMachineRoomRackLayout[]
}
/** 独立设备放置参数。 */
export interface RoomDevicePlacement {
expectedVersion: number
positionX: number
positionY: number
positionZ: number
rotationX: number
rotationY: number
rotationZ: number
sceneWidth: number
sceneHeight: number
sceneDepth: number
}
/** 设备机柜上架参数。 */
export interface RackDevicePlacement {
expectedVersion: number
rackId: number
startUnit: number
occupiedUnits: number
powerConsumption?: number
}
/** 校验响应并返回 3D 机房业务数据。 */
function getResponseDetails<T>(response: ThreeMachineRoomResponse<T>): T {
if (!response || typeof response !== 'object') {
throw new Error('接口返回格式错误')
}
if (response.code !== 0) {
throw new Error(response.message || '接口调用失败')
}
return response.details
}
/** 获取指定机房的 3D 场景。 */
export async function fetchThreeMachineRoomScene(roomId: number): Promise<ThreeMachineRoomScene> {
const response = await request.get<ThreeMachineRoomResponse<ThreeMachineRoomScene>>(
`/Assets/v1/three-d/rooms/${encodeURIComponent(roomId)}/scene`
)
return getResponseDetails(response)
}
/** 获取权限范围内的全部 3D 场景。 */
export async function exportThreeMachineRoomScenes(): Promise<ThreeMachineRoomExport> {
const response = await request.get<ThreeMachineRoomResponse<ThreeMachineRoomExport>>('/Assets/v1/three-d/export')
return getResponseDetails(response)
}
/** 保存机房尺寸和初始机柜布局。 */
export async function saveThreeMachineRoomConfig(
roomId: number,
config: ThreeMachineRoomConfigData
): Promise<{ room_id: number; layout_version: number }> {
const response = await request.put<ThreeMachineRoomResponse<{ room_id: number; layout_version: number }>>(
`/Assets/v1/three-d/rooms/${encodeURIComponent(roomId)}/config`,
{
expected_version: config.expectedVersion,
scene_length: config.sceneLength,
scene_width: config.sceneWidth,
scene_height: config.sceneHeight,
racks: config.racks,
}
)
return getResponseDetails(response)
}
/** 批量保存机柜布局。 */
export async function saveThreeMachineRoomRackLayout(
roomId: number,
expectedVersion: number,
racks: ThreeMachineRoomRackLayout[]
): Promise<{ room_id: number; layout_version: number }> {
const response = await request.put<ThreeMachineRoomResponse<{ room_id: number; layout_version: number }>>(
`/Assets/v1/three-d/rooms/${encodeURIComponent(roomId)}/layout`,
{
expected_version: expectedVersion,
racks,
}
)
return getResponseDetails(response)
}
/** 获取指定机房的设备状态与告警。 */
export async function fetchThreeMachineRoomSignals(roomId: number): Promise<ThreeMachineRoomSignals> {
const response = await request.get<ThreeMachineRoomResponse<ThreeMachineRoomSignals>>(
`/Assets/v1/three-d/rooms/${encodeURIComponent(roomId)}/signals`
)
return getResponseDetails(response)
}
/** 获取设备可观测指标与告警详情。 */
export async function fetchThreeMachineRoomDeviceObservability(assetId: number): Promise<ThreeMachineRoomObservability> {
const response = await request.get<ThreeMachineRoomResponse<ThreeMachineRoomObservability>>(
`/Assets/v1/three-d/devices/${encodeURIComponent(assetId)}/observability`
)
return getResponseDetails(response)
}
/** 保存设备机柜及 U 位。 */
export async function saveThreeMachineRoomRackPlacement(
roomId: number,
assetId: number,
placement: RackDevicePlacement
): Promise<{ layout_version: number }> {
const response = await request.put<ThreeMachineRoomResponse<{ layout_version: number }>>(
`/Assets/v1/three-d/rooms/${encodeURIComponent(roomId)}/devices/${encodeURIComponent(assetId)}/placement`,
{
expected_version: placement.expectedVersion,
placement_type: 'rack',
rack_id: placement.rackId,
start_unit: placement.startUnit,
occupied_units: placement.occupiedUnits,
power_consumption: placement.powerConsumption || 0,
}
)
return getResponseDetails(response)
}
/** 将设备放置在机房场景中。 */
export async function saveThreeMachineRoomDevicePlacement(
roomId: number,
assetId: number,
placement: RoomDevicePlacement
): Promise<{ room_id: number; asset_id: number; placement_type: 'room'; layout_version: number }> {
const response = await request.put<
ThreeMachineRoomResponse<{ room_id: number; asset_id: number; placement_type: 'room'; layout_version: number }>
>(`/Assets/v1/three-d/rooms/${encodeURIComponent(roomId)}/devices/${encodeURIComponent(assetId)}/placement`, {
expected_version: placement.expectedVersion,
placement_type: 'room',
position_x: placement.positionX,
position_y: placement.positionY,
position_z: placement.positionZ,
rotation_x: placement.rotationX,
rotation_y: placement.rotationY,
rotation_z: placement.rotationZ,
scene_width: placement.sceneWidth,
scene_height: placement.sceneHeight,
scene_depth: placement.sceneDepth,
})
return getResponseDetails(response)
}
/** 解除设备在机柜或机房中的位置。 */
export async function removeThreeMachineRoomDevicePlacement(
roomId: number,
assetId: number,
expectedVersion: number
): Promise<{ room_id: number; asset_id: number; placement_type: 'unplaced'; layout_version: number }> {
const response = await request.delete<
ThreeMachineRoomResponse<{ room_id: number; asset_id: number; placement_type: 'unplaced'; layout_version: number }>
>(`/Assets/v1/three-d/rooms/${encodeURIComponent(roomId)}/devices/${encodeURIComponent(assetId)}/placement`, {
data: { expected_version: expectedVersion },
})
return getResponseDetails(response)
}

View File

@@ -1,31 +1,100 @@
import { request } from '@/api/request'
/** U 位状态 */
export type RackUnitStatus = 'available' | 'occupied' | 'reserved' | 'disabled'
/** U 位关联资产摘要 */
export interface RackUnitAssetSummary {
unit_start: number
}
/** U 位公共字段 */
export interface RackUnitBase {
unit_number: number
occupied_units: number
description?: string
}
/** 可用 U 位 */
export interface AvailableRackUnit extends RackUnitBase {
status: 'available'
}
/** 已占用 U 位 */
export interface OccupiedRackUnit extends RackUnitBase {
status: 'occupied'
asset_id: number
asset_name: string
asset_code: string
asset_type: string
power_consumption: number
asset: RackUnitAssetSummary
}
/** 已预留 U 位 */
export interface ReservedRackUnit extends RackUnitBase {
status: 'reserved'
reserved_for: string
reserved_until: string
}
/** 已禁用 U 位 */
export interface DisabledRackUnit extends RackUnitBase {
status: 'disabled'
}
/** 单个 U 位数据 */
export type RackUnitItem = AvailableRackUnit | OccupiedRackUnit | ReservedRackUnit | DisabledRackUnit
/** U 位页面使用的机柜摘要 */
export interface RackUnitRackInfo {
id?: number
room_id: number
height: number
status?: string
used_power?: number
}
/** 获取机柜 U 位状态响应 */
export interface RackUnitListResponse {
code: number
message?: string
details: {
rack: RackUnitRackInfo
units: RackUnitItem[]
}
}
/** 获取机柜U位状态 */
export const fetchRackUnits = (rackId: number) => {
return request.get(`/Assets/v1/unit/rack/${rackId}`)
return request.get<RackUnitListResponse>(`/Assets/v1/unit/rack/${rackId}`)
}
/** 获取U位列表别名 */
export const fetchUnitList = fetchRackUnits
/** 分配U位 */
export const allocateUnit = (data: {
/** 机柜设备上架数据 */
export interface RackPlacementData {
expected_version: number
rack_id: number
start_unit: number
occupied_units: number
asset_id?: number
asset_code?: string
asset_name: string
asset_type: string
power_consumption?: number
description?: string
}) => {
return request.post('/Assets/v1/unit/allocate', data)
power_consumption: number
}
/** 释放U位 */
export const releaseUnit = (data: { rack_id: number; start_unit: number; end_unit: number }) => {
return request.post('/Assets/v1/unit/release', data)
/** 将资产上架到机柜U位 */
export const placeAssetInRack = (roomId: number, assetId: number, data: RackPlacementData) => {
return request.put(`/Assets/v1/three-d/rooms/${roomId}/devices/${assetId}/placement`, {
...data,
placement_type: 'rack',
})
}
/** 取消资产在机房中的放置 */
export const unplaceAsset = (roomId: number, assetId: number, expectedVersion: number) => {
return request.delete(`/Assets/v1/three-d/rooms/${roomId}/devices/${assetId}/placement`, {
data: { expected_version: expectedVersion },
})
}
/** 预留U位 */

View File

@@ -15,6 +15,7 @@ export interface LoginData {
/** 用户信息 */
export interface UserItem {
id?: number
user_id?: number
account?: string
password?: string
confirmPassword?: string

View File

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@@ -1,12 +1,4 @@
<svg width="33" height="33" viewBox="0 0 33 33" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M5.37754 16.9795L12.7498 9.43027C14.7163 7.41663 17.9428 7.37837 19.9564 9.34482C19.9852 9.37297 20.0137 9.40145 20.0418 9.43027L20.1221 9.51243C22.1049 11.5429 22.1049 14.7847 20.1221 16.8152L12.7498 24.3644C10.7834 26.378 7.55686 26.4163 5.54322 24.4498C5.5144 24.4217 5.48592 24.3932 5.45777 24.3644L5.37754 24.2822C3.39468 22.2518 3.39468 19.0099 5.37754 16.9795Z" fill="#12D2AC"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M20.0479 9.43034L27.3399 16.8974C29.3674 18.9735 29.3674 22.2883 27.3399 24.3644C25.3735 26.3781 22.147 26.4163 20.1333 24.4499C20.1045 24.4217 20.076 24.3933 20.0479 24.3644L12.7558 16.8974C10.7284 14.8213 10.7284 11.5065 12.7558 9.43034C14.7223 7.4167 17.9488 7.37844 19.9624 9.34489C19.9912 9.37304 20.0197 9.40152 20.0479 9.43034Z" fill="#307AF2"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M20.1321 9.52163L23.6851 13.1599L16.3931 20.627L9.10103 13.1599L12.6541 9.52163C14.6707 7.45664 17.9794 7.4174 20.0444 9.434C20.074 9.46286 20.1032 9.49207 20.1321 9.52163Z" fill="#0057FE"/>
</g>
<defs>
<clipPath id="clip0">
<rect width="26" height="19" fill="white" transform="translate(3.5 7)"/>
</clipPath>
</defs>
<svg class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" width="33" height="33">
<path d="M0 240.941176a240.941176 240.941176 0 0 1 240.941176-240.941176h542.117648a240.941176 240.941176 0 0 1 240.941176 240.941176v542.117648a240.941176 240.941176 0 0 1-240.941176 240.941176H240.941176a240.941176 240.941176 0 0 1-240.941176-240.941176V240.941176z" fill="#287BF7"></path>
<path d="M340.931765 388.969412a120.621176 120.621176 0 1 0 115.260235 209.799529l70.776471 97.490824a241.091765 241.091765 0 1 1 82.522353-283.587765c15.751529 39.936 14.516706 83.546353 13.763764 110.290824-0.150588 4.879059-0.271059 9.216-0.271059 12.860235 0 22.648471 5.812706 48.519529 18.221177 66.831059 10.24 15.058824 26.654118 28.310588 61.108706 28.310588 31.804235 0 49.152-10.721882 59.331764-22.40753 11.264-12.950588 18.974118-33.370353 18.974118-59.452235 0-18.642824-9.396706-39.574588-27.166118-53.067294-16.022588-12.137412-43.550118-21.594353-86.768941-8.734118l-34.42447-115.440941c75.926588-22.618353 144.112941-9.637647 193.987764 28.190118 48.128 36.502588 74.842353 93.093647 74.842353 149.082353 0 48.489412-14.245647 99.087059-48.609882 138.541176-35.418353 40.719059-87.341176 63.728941-150.166588 63.728941-75.294118 0-128.662588-33.792-160.768-81.106823-29.936941-44.062118-39.032471-95.864471-39.032471-134.475294 0-9.396706 0.150588-17.588706 0.240941-24.847059 0.421647-25.057882 0.632471-38.972235-5.330823-54.091294a120.621176 120.621176 0 0 0-156.491294-67.915294z" fill="#FFFFFF"></path>
</svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -1,11 +1,11 @@
<template>
<a-spin style="display: block" :loading="loading">
<a-spin class="message-box" :class="{ 'is-empty': !renderList.length }" style="display: block" :loading="loading">
<a-tabs v-model:activeKey="messageType" type="rounded" destroy-on-hide>
<a-tab-pane v-for="item in tabList" :key="item.key">
<template #title>
<span>{{ item.title }}{{ formatUnreadLength(item.key) }}</span>
</template>
<a-result v-if="!renderList.length" status="404">
<a-result v-if="!renderList.length" class="message-empty" status="404">
<template #subtitle>{{ $t('messageBox.noContent') }}</template>
</a-result>
<List
@@ -16,11 +16,6 @@
@view-history="openHistory"
/>
</a-tab-pane>
<template #extra>
<a-button type="text" @click="emptyList">
{{ $t('messageBox.tab.button') }}
</a-button>
</template>
</a-tabs>
<a-drawer v-model:visible="historyVisible" width="760px" title="通知投递历史" :footer="false" unmount-on-close @open="fetchHistory">
<a-table :columns="historyColumns" :data="historyList" :loading="historyLoading" :pagination="historyPagination" row-key="id" @page-change="onHistoryPageChange">
@@ -50,7 +45,7 @@ import {
import useLoading from '@/hooks/loading'
import useNotificationSocket from '@/hooks/useNotificationSocket'
import { Message } from '@arco-design/web-vue'
import { computed, reactive, ref, toRefs } from 'vue'
import { computed, reactive, ref, toRefs, watch } from 'vue'
import { useI18n } from 'vue-i18n'
import List from './list.vue'
@@ -59,9 +54,13 @@ interface TabItem {
title: string
avatar?: string
}
const visibleMessageTypes = ['message']
const { loading, setLoading } = useLoading(true)
const messageType = ref('message')
const { t } = useI18n()
const emit = defineEmits<{
(event: 'unread-count-change', count: number): void
}>()
const messageData = reactive<{
renderList: MessageRecord[]
messageList: MessageRecord[]
@@ -92,14 +91,6 @@ const tabList: TabItem[] = [
key: 'message',
title: t('messageBox.tab.title.message'),
},
{
key: 'notice',
title: t('messageBox.tab.title.notice'),
},
{
key: 'todo',
title: t('messageBox.tab.title.todo'),
},
]
async function fetchSourceData() {
setLoading(true)
@@ -176,6 +167,9 @@ const getDeliveryColor = (status: string) => {
const renderList = computed(() => {
return messageData.messageList.filter((item) => messageType.value === item.type)
})
const totalUnreadCount = computed(() => {
return messageData.messageList.filter((item) => visibleMessageTypes.includes(item.type) && !item.status).length
})
const unreadCount = computed(() => {
return renderList.value.filter((item) => !item.status).length
})
@@ -190,9 +184,7 @@ const formatUnreadLength = (type: string) => {
const handleItemClick = (items: MessageListType) => {
if (renderList.value.length) readMessage([...items])
}
const emptyList = () => {
messageData.messageList = []
}
watch(totalUnreadCount, (count) => emit('unread-count-change', count), { immediate: true })
fetchSourceData()
useNotificationSocket<MessageRecord>({
onNotification: upsertMessageToTop,
@@ -201,6 +193,32 @@ useNotificationSocket<MessageRecord>({
</script>
<style scoped lang="less">
.message-box {
width: 400px;
overflow: hidden;
:deep(.arco-spin-children) {
overflow: hidden;
}
:deep(.arco-tabs-content) {
max-height: 360px;
overflow-x: hidden;
overflow-y: auto;
}
&.is-empty {
:deep(.arco-tabs-content) {
max-height: none;
overflow: hidden;
}
:deep(.message-empty) {
padding: 34px 0 22px;
}
}
}
:deep(.arco-popover-popup-content) {
padding: 0;
}

View File

@@ -13,7 +13,8 @@
<a-tag v-if="item.deliveryStatus" :color="getDeliveryColor(item.deliveryStatus)">
{{ getDeliveryLabel(item.deliveryStatus) }}
</a-tag>
<a-tag v-else-if="item.messageType === 0" color="gray">未开始</a-tag>
<a-tag v-if="(item.occurrenceCount || 1) > 1" color="arcoblue">发生 {{ item.occurrenceCount }} </a-tag>
<a-tag v-if="item.messageType === 0" color="gray">未开始</a-tag>
<a-tag v-else-if="item.messageType === 1" color="green">已开通</a-tag>
<a-tag v-else-if="item.messageType === 2" color="blue">进行中</a-tag>
<a-tag v-else-if="item.messageType === 3" color="red">即将到期</a-tag>
@@ -54,7 +55,7 @@
</div>
</a-list-item>
<template #footer>
<a-space fill :size="0" :class="{ 'add-border-top': renderList.length < showMax }">
<a-space fill :size="0" class="message-footer-actions" :class="{ 'add-border-top': renderList.length < showMax }">
<div class="footer-wrap">
<a-link @click="allRead">{{ $t('messageBox.allRead') }}</a-link>
</div>
@@ -147,10 +148,20 @@ const showMax = 3
.arco-list-footer {
padding: 0;
height: 50px;
line-height: 50px;
line-height: normal;
border-top: none;
.arco-space-item {
.message-footer-actions {
display: flex;
width: 100%;
height: 50px;
}
.arco-space-item {
display: flex;
flex: 1;
width: 50%;
height: 50px;
border-right: 1px solid rgb(var(--gray-3));
&:last-child {
border-right: none;
@@ -161,7 +172,20 @@ const showMax = 3
}
}
.footer-wrap {
text-align: center;
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 50px;
:deep(.arco-link) {
line-height: 1;
}
:deep(.arco-link:focus-visible) {
background-color: transparent;
box-shadow: none;
}
}
.arco-typography {
margin-bottom: 0;

View File

@@ -1,8 +1,5 @@
export default {
'messageBox.tab.title.message': 'Message',
'messageBox.tab.title.notice': 'Notice',
'messageBox.tab.title.todo': 'Todo',
'messageBox.tab.button': 'empty',
'messageBox.allRead': 'All Read',
'messageBox.viewMore': 'View More',
'messageBox.noContent': 'No Content',

View File

@@ -1,8 +1,5 @@
export default {
'messageBox.tab.title.message': '消息',
'messageBox.tab.title.notice': '通知',
'messageBox.tab.title.todo': '待办',
'messageBox.tab.button': '清空',
'messageBox.allRead': '全部已读',
'messageBox.viewMore': '查看更多',
'messageBox.noContent': '暂无内容',

View File

@@ -52,7 +52,7 @@
<li>
<a-tooltip :content="$t('settings.navbar.alerts')">
<div class="message-box-trigger">
<a-badge dot>
<a-badge dot :count="notificationUnreadCount">
<a-button class="nav-btn" type="outline" :shape="'circle'" @click="setPopoverVisible">
<icon-notification />
</a-button>
@@ -62,12 +62,12 @@
<a-popover
trigger="click"
:arrow-style="{ display: 'none' }"
:content-style="{ padding: 0, minWidth: '400px' }"
:content-style="{ padding: 0, minWidth: '400px', overflow: 'hidden' }"
content-class="message-popover"
>
<div ref="refBtn" class="ref-btn"></div>
<template #content>
<message-box />
<message-box @unread-count-change="handleMessageBoxUnreadCountChange" />
</template>
</a-popover>
</li>
@@ -93,7 +93,8 @@
<li>
<a-dropdown trigger="click">
<a-avatar :size="32" :style="{ marginRight: '8px' }">
<img alt="avatar" :src="avatar" />
<img v-if="avatar" alt="avatar" :src="avatar" />
<icon-user v-else />
</a-avatar>
<template #content>
<a-doption>
@@ -126,8 +127,10 @@
</template>
<script lang="ts" setup>
import { MessageRecord, queryMessageList } from '@/api/message'
import useLocale from '@/hooks/locale'
import useUser from '@/hooks/user'
import useNotificationSocket from '@/hooks/useNotificationSocket'
import { LOCALE_OPTIONS } from '@/locale'
import { useAppStore, useUserStore } from '@/store'
import { Message } from '@arco-design/web-vue'
@@ -147,7 +150,7 @@ const { changeLocale, currentLocale }: any = useLocale()
const { isFullscreen, toggle: toggleFullScreen } = useFullscreen()
const locales = [...LOCALE_OPTIONS]
const avatar = computed(() => {
return userStore.avatar || '//p3-armor.byteimg.com/tos-cn-i-49unhts6dw/dfdba5317c0c20ce20e64fac803d52bc.svg~tplv-49unhts6dw-image.image'
return userStore.avatar
})
const theme = computed(() => {
return appStore.theme
@@ -173,8 +176,24 @@ const setVisible = () => {
}
const refBtn = ref()
const triggerBtn = ref()
const notificationUnreadCount = ref(0)
const visibleMessageTypes = ['message']
const passwordChangeVisible = ref(false)
const profileChangeVisible = ref(false)
const updateNotificationUnreadCount = (list: MessageRecord[] = []) => {
notificationUnreadCount.value = list.filter((item) => visibleMessageTypes.includes(item.type) && !item.status).length
}
const fetchNotificationUnreadCount = async () => {
try {
const { data } = await queryMessageList()
updateNotificationUnreadCount(data)
} catch (err) {
updateNotificationUnreadCount()
}
}
const handleMessageBoxUnreadCountChange = (count: number) => {
notificationUnreadCount.value = count
}
const setPopoverVisible = () => {
const event = new MouseEvent('click', {
view: window,
@@ -201,6 +220,12 @@ const setDropDownVisible = () => {
triggerBtn.value.dispatchEvent(event)
}
const toggleDrawerMenu = inject('toggleDrawerMenu') as () => void
fetchNotificationUnreadCount()
useNotificationSocket<MessageRecord>({
onNotification: fetchNotificationUnreadCount,
onReconnectAfter: fetchNotificationUnreadCount,
})
</script>
<style scoped lang="less">

View File

@@ -25,6 +25,7 @@
import { ref, watch } from 'vue'
import { Message } from '@arco-design/web-vue'
import { resetUserPassword } from '@/api/module/user'
import type { UserItem } from '@/api/types'
import SafeStorage, { AppStorageKey } from '@/utils/safeStorage'
interface Props {
@@ -79,12 +80,12 @@ const handleSavePassword = async () => {
try {
// 从 SafeStorage 获取登录用户信息
const userInfo = SafeStorage.get(AppStorageKey.USER_INFO) || {}
const userInfo = SafeStorage.get<UserItem>(AppStorageKey.USER_INFO)
const res = await resetUserPassword({
account: userInfo.account || '',
account: userInfo?.account || '',
code: '123456', // 暂时没校验,随便传
password: form.value.newPassword,
phone: userInfo.phone || '13800138000', // 暂时没校验,随便传
phone: userInfo?.phone || '13800138000', // 暂时没校验,随便传
})
if (res.code === 0) {

View File

@@ -29,6 +29,7 @@
import { ref, watch } from 'vue'
import { Message } from '@arco-design/web-vue'
import { modifyUser } from '@/api/module/user'
import type { UserItem } from '@/api/types'
import SafeStorage, { AppStorageKey } from '@/utils/safeStorage'
interface Props {
@@ -56,11 +57,11 @@ watch(
visible.value = val
if (val) {
// 打开时从存储中获取用户信息
const userInfo = SafeStorage.get(AppStorageKey.USER_INFO) || {}
const userInfo = SafeStorage.get<UserItem>(AppStorageKey.USER_INFO)
form.value = {
account: userInfo.account || '',
name: userInfo.name || '',
email: userInfo.email || '',
account: userInfo?.account || '',
name: userInfo?.name || '',
email: userInfo?.email || '',
}
}
}
@@ -76,11 +77,11 @@ const handleSaveProfile = async () => {
loading.value = true
try {
const userInfo = SafeStorage.get(AppStorageKey.USER_INFO) || {}
const userInfo = SafeStorage.get<UserItem>(AppStorageKey.USER_INFO)
const updatedUserInfo = {
...userInfo,
...form.value,
id: userInfo.user_id,
id: userInfo?.user_id ?? userInfo?.id,
}
const res = await modifyUser(updatedUserInfo)

Some files were not shown because too many files have changed in this diff Show More