Compare commits

95 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
cc6ea1acc6 fix snmp链路测试 2026-07-05 21:21:53 +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
zxr
1ad0323715 fix 大屏管理 2026-07-03 15:48:23 +08:00
zxr
b01a0f0979 fix 报表数据无法查看与下载 2026-07-03 11:09:16 +08:00
zxr
8f3dd3e43e 新增 业务拓扑相关 2026-06-29 21:47:46 +08:00
zxr
2107f77081 修复 : 数据为空的时候,创建按钮不显示 2026-06-29 10:42:36 +08:00
zxr
fcaad4b3ae 修复 空数据时页面组件不显示 2026-06-29 10:09:28 +08:00
zxr
cd5e8b5f2d 任务执行1-19 2026-06-26 12:51:19 +08:00
zxr
18531bfcac fix 2026-06-24 15:12:18 +08:00
zxr
d104dd8df9 fix 2026-05-02 17:21:08 +08:00
zxr
bd61f722a2 Merge branch 'main' of https://git.apinb.com/ops/front 2026-05-02 17:20:20 +08:00
zxr
27e1f335a6 fix 2026-05-02 17:08:10 +08:00
026cff306a feat 2026-05-02 09:59:29 +08:00
1af4075e9b feat 2026-05-02 09:59:06 +08:00
zxr
ea3e60c17c fix 2026-04-30 18:55:32 +08:00
zxr
ea9531313e fix 2026-04-28 17:24:36 +08:00
zxr
f7b34c5d60 fix 2026-04-28 17:22:06 +08:00
zxr
39150be726 Merge branch 'main' of https://git.apinb.com/ops/front 2026-04-27 19:26:48 +08:00
zxr
25e8eafc69 fix 2026-04-27 19:26:44 +08:00
e569571462 feat 2026-04-19 21:44:17 +08:00
7f209b5fef feat 2026-04-19 20:57:48 +08:00
zxr
d9a0470ecf fix 2026-04-19 14:24:03 +08:00
zxr
39d3254b8d Merge branch 'main' of https://git.apinb.com/ops/front 2026-04-18 16:26:28 +08:00
zxr
0a12f54132 fix 2026-04-18 16:26:24 +08:00
9afaa4b775 feat 2026-04-18 15:07:05 +08:00
878cbea9a5 feat 2026-04-18 14:23:06 +08:00
d5df671ff6 feat 2026-04-16 22:38:21 +08:00
ba933457d4 feat 2026-04-16 22:18:26 +08:00
zxr
01139f2874 fix 2026-04-16 18:59:33 +08:00
zxr
cb8bd05ff7 fix 2026-04-15 21:41:47 +08:00
zxr
55170bceb0 fix 2026-04-14 17:42:31 +08:00
zxr
8db158c390 fix 2026-04-14 16:17:31 +08:00
b8a7ba1cbb feat 2026-04-13 23:27:09 +08:00
e84cb75dda feat 2026-04-13 23:18:15 +08:00
zxr
f030f9c5c9 fix 2026-04-13 20:57:41 +08:00
zxr
003c552238 fix 2026-04-12 16:40:33 +08:00
zxr
1dcab7af96 fix 2026-04-12 15:01:46 +08:00
5f4111aeb1 feat 2026-04-11 22:22:07 +08:00
c72af0bfa7 feat 2026-04-11 21:23:10 +08:00
68a320b6c2 Merge branch 'main' of https://git.apinb.com/ops/front 2026-04-11 21:08:39 +08:00
a0ca86d98d feat 2026-04-11 21:08:34 +08:00
zxr
dc31084d87 Merge branch 'main' of https://git.apinb.com/ops/front 2026-04-11 20:03:10 +08:00
zxr
8b3bd98e68 fix 2026-04-11 20:03:06 +08:00
431 changed files with 3011332 additions and 22039 deletions

View File

@@ -6,11 +6,18 @@ VITE_APP_WORKSPACE=ops
# 应用标题
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 同源代理访问 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

View File

@@ -5,6 +5,7 @@ VITE_APP_WORKSPACE=ops
VITE_APP_TITLE=标准管理系统
VITE_APP_DESCRIPTION="default standard template"
VITE_USE_MOCK=false
# API 基础URL
VITE_API_BASE_URL=https://ops-api.apinb.com

115
.kilo/package-lock.json generated Normal file
View File

@@ -0,0 +1,115 @@
{
"name": ".kilo",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"dependencies": {
"@kilocode/plugin": "7.2.14"
}
},
"node_modules/@kilocode/plugin": {
"version": "7.2.14",
"resolved": "https://registry.npmjs.org/@kilocode/plugin/-/plugin-7.2.14.tgz",
"integrity": "sha512-mS+WA9HZIBH2qQ9ARA+v0q4MdQTSdfOvKbe4AOSkjP+P5hVA70OM/UVM9DVcvmjSOxU+wuUxmOy+j/EQIrgFmw==",
"license": "MIT",
"dependencies": {
"@kilocode/sdk": "7.2.14",
"zod": "4.1.8"
},
"peerDependencies": {
"@opentui/core": ">=0.1.97",
"@opentui/solid": ">=0.1.97"
},
"peerDependenciesMeta": {
"@opentui/core": {
"optional": true
},
"@opentui/solid": {
"optional": true
}
}
},
"node_modules/@kilocode/sdk": {
"version": "7.2.14",
"resolved": "https://registry.npmjs.org/@kilocode/sdk/-/sdk-7.2.14.tgz",
"integrity": "sha512-Naz83lFrsbavuDp6UwxRuglOaSNvRBsZfcRNvb7RpWYAwbuJP0dBdhpXj6uO3ta5qxeQ2JzxKNC9Ffz+LCLLDg==",
"license": "MIT",
"dependencies": {
"cross-spawn": "7.0.6"
}
},
"node_modules/cross-spawn": {
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
"integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
"license": "MIT",
"dependencies": {
"path-key": "^3.1.0",
"shebang-command": "^2.0.0",
"which": "^2.0.1"
},
"engines": {
"node": ">= 8"
}
},
"node_modules/isexe": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
"license": "ISC"
},
"node_modules/path-key": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/shebang-command": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
"license": "MIT",
"dependencies": {
"shebang-regex": "^3.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/shebang-regex": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
"license": "ISC",
"dependencies": {
"isexe": "^2.0.0"
},
"bin": {
"node-which": "bin/node-which"
},
"engines": {
"node": ">= 8"
}
},
"node_modules/zod": {
"version": "4.1.8",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}
}
}
}

115
.kilocode/package-lock.json generated Normal file
View File

@@ -0,0 +1,115 @@
{
"name": ".kilocode",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"dependencies": {
"@kilocode/plugin": "7.2.14"
}
},
"node_modules/@kilocode/plugin": {
"version": "7.2.14",
"resolved": "https://registry.npmjs.org/@kilocode/plugin/-/plugin-7.2.14.tgz",
"integrity": "sha512-mS+WA9HZIBH2qQ9ARA+v0q4MdQTSdfOvKbe4AOSkjP+P5hVA70OM/UVM9DVcvmjSOxU+wuUxmOy+j/EQIrgFmw==",
"license": "MIT",
"dependencies": {
"@kilocode/sdk": "7.2.14",
"zod": "4.1.8"
},
"peerDependencies": {
"@opentui/core": ">=0.1.97",
"@opentui/solid": ">=0.1.97"
},
"peerDependenciesMeta": {
"@opentui/core": {
"optional": true
},
"@opentui/solid": {
"optional": true
}
}
},
"node_modules/@kilocode/sdk": {
"version": "7.2.14",
"resolved": "https://registry.npmjs.org/@kilocode/sdk/-/sdk-7.2.14.tgz",
"integrity": "sha512-Naz83lFrsbavuDp6UwxRuglOaSNvRBsZfcRNvb7RpWYAwbuJP0dBdhpXj6uO3ta5qxeQ2JzxKNC9Ffz+LCLLDg==",
"license": "MIT",
"dependencies": {
"cross-spawn": "7.0.6"
}
},
"node_modules/cross-spawn": {
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
"integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
"license": "MIT",
"dependencies": {
"path-key": "^3.1.0",
"shebang-command": "^2.0.0",
"which": "^2.0.1"
},
"engines": {
"node": ">= 8"
}
},
"node_modules/isexe": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
"license": "ISC"
},
"node_modules/path-key": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/shebang-command": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
"license": "MIT",
"dependencies": {
"shebang-regex": "^3.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/shebang-regex": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
"license": "ISC",
"dependencies": {
"isexe": "^2.0.0"
},
"bin": {
"node-which": "bin/node-which"
},
"engines": {
"node": ">= 8"
}
},
"node_modules/zod": {
"version": "4.1.8",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}
}
}
}

View File

@@ -1,25 +1,34 @@
# Architect Mode Rules (Non-Obvious Only)
## Development Constraints
- Code is not deployed yet; do not spend effort preserving backward compatibility with old code.
## Architecture Overview
- Vue 3 SPA with dynamic route loading from server
- Pinia stores: `app`, `user`, `tab-bar` (see [`src/store/`](src/store/))
- Two-layer route guard: user login check + permission check
## Dynamic Route System
- Server menu fetched in [`app store`](src/store/modules/app/index.ts) via `fetchServerMenuConfig()`
- Routes registered dynamically in [`permission.ts`](src/router/guard/permission.ts)
- Uses `isMenuLoading`/`isMenuLoaded` flags to prevent duplicate loads
## API Architecture
- Two axios instances for different auth patterns:
- [`request.ts`](src/api/request.ts): Workspace header + custom token format
- [`interceptor.ts`](src/api/interceptor.ts): Bearer token + code 20000 validation
- Choose based on backend API requirements
## State Management
- [`SafeStorage`](src/utils/safeStorage.ts) required for all localStorage access
- Supports TTL expiry and type-safe keys via enum
## Component Patterns
- Global components registered in [`components/index.ts`](src/components/index.ts)
- ECharts modules manually imported for bundle size optimization
- ECharts modules manually imported for bundle size optimization

View File

@@ -1,19 +1,23 @@
# Ask Mode Rules (Non-Obvious Only)
## Project Structure
- Vue 3 + Arco Design admin template with Pinia state management
- Vite config files located in `config/` directory (not root)
## Key Directories
- `src/api/` - API layer with two axios instances
- `src/views/ops/` - Main business modules (kb, netarch, asset, etc.)
- `src/router/guard/` - Route guards including dynamic menu loading
- `src/store/modules/app/` - App store with server menu fetching
## Documentation References
- Arco Design Vue: https://arco.design/vue
- Vue Flow (for topology): https://vueflow.dev/
## API Patterns
- Use [`request.ts`](src/api/request.ts) for workspace-aware requests
- Use [`interceptor.ts`](src/api/interceptor.ts) for standard Bearer token auth
- Use [`interceptor.ts`](src/api/interceptor.ts) for standard Bearer token auth

View File

@@ -1,13 +1,21 @@
# Code Mode Rules (Non-Obvious Only)
## Development Constraints
- Code is not deployed yet; do not spend effort preserving backward compatibility with old code.
## API Layer
- Two axios instances exist: [`request.ts`](src/api/request.ts) (custom with workspace header) and [`interceptor.ts`](src/api/interceptor.ts) (global with Bearer token). Choose based on whether you need workspace support.
## Storage
- Always use [`SafeStorage`](src/utils/safeStorage.ts) with `AppStorageKey` enum - never use localStorage directly. Supports TTL via third parameter.
## useRequest Hook
- [`useRequest()`](src/hooks/request.ts) invokes API immediately - does NOT work in async functions. Pass params via `.bind(null, params)` pattern.
## Dynamic Routes
- Routes loaded from server in [`permission.ts`](src/router/guard/permission.ts) using `isMenuLoading`/`isMenuLoaded` flags. Don't modify these flags manually.
- Routes loaded from server in [`permission.ts`](src/router/guard/permission.ts) using `isMenuLoading`/`isMenuLoaded` flags. Don't modify these flags manually.

View File

@@ -1,17 +1,21 @@
# Debug Mode Rules (Non-Obvious Only)
## API Response Codes
- [`interceptor.ts`](src/api/interceptor.ts) expects `code: 20000` for success. Other codes trigger error messages.
- Token expiry codes: 50008, 50012, 50014 trigger logout modal.
## Token Storage
- Tokens stored via [`SafeStorage`](src/utils/safeStorage.ts) with key `AppStorageKey.TOKEN`.
- Token expiry redirects to `/auth/login` (not `/login`).
## Route Loading Issues
- If routes not loading, check `isMenuLoading`/`isMenuLoaded` flags in [`permission.ts`](src/router/guard/permission.ts).
- Server menu fetched via [`fetchServerMenuConfig()`](src/store/modules/app/index.ts).
## Environment
- Dev config: `.env.development`, Prod config: `.env.production`
- API base URL: `VITE_API_BASE_URL`, Workspace: `VITE_APP_WORKSPACE`
- API base URL: `VITE_API_BASE_URL`, Workspace: `VITE_APP_WORKSPACE`

File diff suppressed because it is too large Load Diff

View File

@@ -13,6 +13,10 @@ This file provides guidance to agents when working with code in this repository.
## Critical Architecture Notes
### Development Constraints
- Code is not deployed yet; do not spend effort preserving backward compatibility with old code.
### Vite Config Location
Config files are in `config/` directory, NOT root. All vite commands reference `./config/vite.config.*.ts`.

View File

@@ -1,196 +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,86 +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,47 @@
# Front - OPS 前端
`front` 是 OPS 平台的统一前端入口,基于 Vue 3、Vite、Arco Design Vue、Pinia 和 Vue Router。页面以动态菜单为主RBAC 菜单接口不可用时使用本地菜单数据兜底。
## 当前代码入口
| 类型 | 路径 |
| --- | --- |
| 应用入口 | `front/src/main.ts` |
| 路由入口 | `front/src/router/index.ts` |
| 静态路由 | `front/src/router/routes/modules/` |
| 动态菜单转换 | `front/src/router/menu-data.ts` |
| 本地菜单兜底 | `front/src/router/local-menu-items.ts``front/src/router/local-menu-flat.ts` |
| 页面目录 | `front/src/views/` |
| API 模块 | `front/src/api/module/` |
| 构建配置 | `front/config/` |
## 主要页面域
| 页面域 | 目录 |
| --- | --- |
| 首页与概览 | `front/src/views/home``front/src/views/ops/pages/overview` |
| 数据中心与资源 | `front/src/views/ops/pages/dc``datacenter``resource-context` |
| 告警与治理 | `front/src/views/ops/pages/alert``governance` |
| 工单闭环 | `front/src/views/ops/pages/feedback` |
| 知识库 | `front/src/views/ops/pages/kb` |
| 报表与自动化 | `front/src/views/ops/pages/report``automation` |
| 业务系统与拓扑 | `front/src/views/ops/pages/business-system``business-topology` |
| 大屏 | `front/src/views/ops/pages/big-screen``front/src/views/visualization` |
| 日志管理 | `front/src/views/ops/pages/log-mgmt` |
| 系统设置 | `front/src/views/ops/pages/system-settings` |
## 常用命令
```powershell
cd D:\work\ops\front
pnpm.cmd install
pnpm.cmd dev
pnpm.cmd build
```
## 相关文档
- `front/PAGES_README.md`:页面清单和路由说明。
- `front/QUICK_START.md`:本地启动说明。
- `front/docs/`:页面设计、适配验收和部分接口对接文档。
- `docs/模块文档索引.md`:全工作区模块边界与文档入口。

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,24 +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'
export default mergeConfig(
{
mode: 'development',
server: {
open: true,
host: '0.0.0.0',
fs: {
strict: true,
},
const HIKVISION_PROXY_TIMEOUT = 8000
const proxyTarget = (port: number) => ({
target: `http://127.0.0.1:${port}`,
changeOrigin: true,
})
/** 读取海康 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",

3351
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

5
pnpm-workspace.yaml Normal file
View File

@@ -0,0 +1,5 @@
allowBuilds:
esbuild: true
less: true
unrs-resolver: true
vue-demi: true

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,26 +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(
`/fts/v1/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 { 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;
icon: string;
color: string;
parent_id: number;
level: number;
path: string;
sort_order: number;
status: string;
creator_id: number;
creator_name: string;
doc_count: number;
faq_count: number;
metadata: string | null;
remarks: string;
id: number
created_at: string
updated_at: string
name: string
description: string
type: 'general'
icon: string
color: string
parent_id: number
level: 1 | 2 | 3
path: string
sort_order: number
status: CategoryStatus
creator_id: number
creator_name: string
doc_count: number
faq_count: number
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;
name: string
description: string
icon: string
color: string
parent_id: number
sort_order: number
status: CategoryStatus
remarks: string
}
/** 更新分类请求参数 */
export interface UpdateCategoryParams {
id: number;
name?: string;
description?: string;
icon?: string;
color?: string;
sort_order?: number;
status?: string;
remarks?: string;
/** 更新分类参数 */
export interface UpdateCategoryParams extends CreateCategoryParams {
id: number
}
/** 获取分类列表参数 */
/** 分类列表参数 */
export interface FetchCategoryListParams {
type?: string;
parent_id?: number;
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,218 +1,118 @@
import { request } from "@/api/request";
import { request } from '@/api/request'
import type { KbReply } from './types'
/** 文档状态 */
export type DocumentStatus = 'draft' | 'published' | 'reviewed' | 'rejected';
/** 文档状态 */
export type DocumentStatus = 'draft' | 'published' | 'reviewed' | 'rejected'
/** 文档类型。 */
export type DocumentType = 'common' | 'guide' | 'solution' | 'troubleshoot' | 'process' | 'technical'
/** 文档列表范围。 */
export type DocumentScope = 'my' | 'all'
/** 文档类型 */
export type DocumentType = 'common' | 'guide' | 'solution' | 'troubleshoot' | 'process' | 'technical';
/** 文档接口类型 */
/** 文档资源。 */
export interface Document {
id: number;
created_at: string;
updated_at: string;
doc_no: string;
title: string;
description: string;
content: string;
type: DocumentType;
status: DocumentStatus;
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;
is_favorited?: boolean;
id: number
created_at: string
updated_at: string
doc_no: string
title: string
description: string
content: string
type: DocumentType
status: DocumentStatus
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
related_docs: string
detection_point_ids: string
metadata: string
keywords: string
remarks: string
is_favorited: boolean
}
/** API响应包装类型 */
export interface ApiResponse<T = any> {
code: number;
message: string;
data: T;
/** 文档分页结果。 */
export interface DocumentPage {
total: number
page: number
page_size: number
data: Document[]
}
/** 分页响应类型 */
export interface PaginatedResponse<T> {
total: number;
page: number;
page_size: number;
data: T[];
}
/** 创建文档请求参数 */
/** 文档创建字段。 */
export interface CreateDocumentParams {
title: string;
description?: string;
content: string;
type?: DocumentType;
category_id?: number;
sub_category?: string;
keywords?: string;
tags?: string;
remarks?: string;
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 UpdateDocumentParams {
id: number;
title?: string;
description?: string;
content?: string;
type?: DocumentType;
category_id?: number;
sub_category?: string;
keywords?: string;
tags?: string;
remarks?: string;
/** 文档编辑字段。 */
export interface UpdateDocumentParams extends CreateDocumentParams {
id: number
}
/** 获取文档列表参数 */
/** 文档列表参数 */
export interface FetchDocumentListParams {
page?: number;
page_size?: number;
keyword?: string;
type?: DocumentType;
status?: DocumentStatus;
category_id?: number;
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' },
{ label: '故障排查', value: 'troubleshoot' },
{ label: '流程规范', value: 'process' },
{ 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,86 +1,49 @@
import { request } from '@/api/request';
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 type ResourceType = 'document' | 'faq'
/** 收藏记录接口 */
/** 收藏记录 */
export interface Favorite {
id: number;
created_at: string;
resource_type: ResourceType;
resource_id: number;
resource_name: string;
remarks: string;
is_deleted: boolean;
resource_data?: any;
id: number
created_at: string
resource_type: ResourceType
resource_id: number
resource_name: string
remarks: string
is_deleted: boolean
resource_data?: Document | Faq
}
/** 收藏列表响应 */
export interface FavoriteListResponse {
total: number;
page: number;
page_size: number;
data: Favorite[];
export interface FavoritePage {
total: number
page: number
page_size: number
data: Favorite[]
}
/** 获取收藏列表参数 */
export interface FetchFavoriteListParams {
page?: number;
page_size?: number;
resource_type?: ResourceType;
page?: number
page_size?: number
resource_type?: ResourceType
}
/** 收藏请求参数 */
export interface CollectParams {
resource_type: ResourceType;
resource_id: number;
remarks?: string;
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)

68
src/api/kb/knowledge.ts Normal file
View File

@@ -0,0 +1,68 @@
import { request } from '@/api/request'
export interface DetectionPoint {
id: number
resource_category: string
metric_code: string
alert_rule_id: number
fault_type: string
knowledge_type: 'document' | 'faq'
knowledge_id: number
knowledge_title: string
enabled: boolean
remarks: string
}
export interface KnowledgeRecommendation {
knowledge_type: 'document' | 'faq'
knowledge_id: number
title: string
status: string
resource_category: string
metric_code: string
alert_rule_id: number
fault_type: string
detection_point_id: number
score: number
reason: string
}
export interface IncidentKnowledgeRelation {
id: number
incident_id: number
ticket_id: number
knowledge_type: 'document' | 'faq'
knowledge_id: number
knowledge_title: string
relation_type: string
source: string
score: number
reason: string
created_at: string
}
export const fetchKnowledgeRecommendations = (params: {
resource_category?: string
metric_code?: string
alert_rule_id?: number
fault_type?: string
limit?: number
}) => request.get('/Kb/v1/knowledge/recommend', { params })
export const fetchIncidentKnowledge = (incidentId: number) => request.get(`/Kb/v1/knowledge/incidents/${incidentId}/relations`)
export const fetchTicketKnowledge = (ticketId: number) => request.get('/Kb/v1/knowledge/relations', { params: { ticket_id: ticketId } })
export const relateIncidentKnowledge = (data: {
incident_id?: number
ticket_id?: number
knowledge_type: 'document' | 'faq'
knowledge_id: number
knowledge_title?: string
relation_type?: string
source?: string
score?: number
reason?: string
}) => request.post('/Kb/v1/knowledge/relations', data)
export const createPostmortemDraft = (data: Record<string, any>) => request.post('/Kb/v1/knowledge/postmortem/draft', data)

View File

@@ -1,173 +1,71 @@
import { request } from "@/api/request";
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;
type: 'document' | 'faq'
resource: Document | Faq
}
/** 分页响应类型 */
export interface PaginatedResponse<T> {
total: number;
page: number;
page_size: number;
data: T[];
/** 审核分页结果。 */
export interface ReviewPage {
total: number
page: number
page_size: number
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;
page?: number
page_size?: number
resource_type?: ReviewResourceType
}
/** 审核通过参数 */
export interface ApproveParams {
resource_type: "document" | "faq";
id: number;
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,88 +1,53 @@
import { request } from "@/api/request";
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_id: number;
resource_name: string;
deleted_by: number;
deleted_name: string;
deleted_time: string;
delete_reason: string;
original_data: string;
remarks: string;
id: number
created_at: string
updated_at: string
resource_type: TrashResourceType
resource_id: number
resource_name: string
deleted_by: number
deleted_name: string
deleted_time: string
delete_reason: string
original_data: string
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';
page?: number
page_size?: number
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

@@ -1,4 +1,5 @@
import axios from 'axios'
import { request } from '@/api/request'
export interface MessageRecord {
id: number
@@ -10,19 +11,93 @@ export interface MessageRecord {
time: string
status: 0 | 1
messageType?: number
deliveryStatus?: string
channelType?: string
attemptCount?: number
lastError?: string
incidentId?: number
alertRecordId?: number
nextRetryAt?: string
occurrenceCount?: number
firstOccurredAt?: string
lastOccurredAt?: string
lastDeliveryId?: number
}
export type MessageListType = MessageRecord[]
export function queryMessageList() {
return axios.post<MessageListType>('/api/message/list')
interface ApiResponse<T> {
code?: number
message?: string
data?: T
details?: T
}
/** 通知投递历史 */
export interface NotificationDeliveryRecord {
id: number
incident_id: number
alert_record_id: number
channel_id: number
channel_type: string
target: string
subject: string
content: string
status: string
attempt_count: number
next_retry_at?: string
last_error?: string
trace_id?: string
delivered_at?: string
created_at?: string
updated_at?: string
}
/** 投递历史查询参数 */
export interface NotificationDeliveryQuery {
page?: number
page_size?: number
keyword?: string
incident_id?: number
status?: string
channel_type?: string
}
/** 分页结果 */
export interface PageResult<T> {
total: number
page: number
page_size: number
data: T[]
}
function unwrapData<T>(response: ApiResponse<T> | T): T {
const wrapped = response as ApiResponse<T>
return (wrapped.details ?? wrapped.data ?? response) as T
}
/** 查询站内消息列表 */
export async function queryMessageList() {
const response = await request.get<ApiResponse<MessageListType>>('/Alert/v1/message/list')
return { data: unwrapData<MessageListType>(response) || [] }
}
interface MessageStatus {
ids: number[]
}
/** 标记站内消息已读 */
export function setMessageStatus(data: MessageStatus) {
return axios.post<MessageListType>('/api/message/read', data)
return request.post<ApiResponse<boolean>>('/Alert/v1/message/read', data)
}
/** 查询通知投递历史 */
export function queryNotificationDeliveryHistory(params?: NotificationDeliveryQuery) {
return request.get<ApiResponse<PageResult<NotificationDeliveryRecord>>>('/Alert/v1/notification/deliveries', { params: params || {} })
}
/** 人工补发通知 */
export function resendNotificationDelivery(id: number) {
return request.post<ApiResponse<{ delivery_id: number; status: string }>>(`/Alert/v1/notification/deliveries/${id}/resend`)
}
export interface ChatRecord {

View File

@@ -1,35 +1,35 @@
import { request } from "@/api/request";
import { request } from '@/api/request'
/** 批次列表 */
export const fetchLoanBatch = (data: { page: number, size: number }) => request.post("/factor/v1/fetch/loan_batch", data);
export const fetchLoanBatch = (data: { page: number; size: number }) => request.post('/factor/v1/fetch/loan_batch', data)
/** 上报列表 */
export const fetchTransferWatch = (data: { page: number, size: number }) => request.post("/factor/v1/fetch/transfer_watch", data);
export const fetchTransferWatch = (data: { page: number; size: number }) => request.post('/factor/v1/fetch/transfer_watch', data)
/** 回盘列表 */
export const fetchTransferDown = (data: { page: number, size: number }) => request.post("/factor/v1/fetch/transfer_down", data);
export const fetchTransferDown = (data: { page: number; size: number }) => request.post('/factor/v1/fetch/transfer_down', data)
/** 放款数据列表 */
export const fetchDataFile = (data: { page: number, size: number }) => request.post("/factor/v1/fetch/data_file", data);
export const fetchDataFile = (data: { page: number; size: number }) => request.post('/factor/v1/fetch/data_file', data)
/** 回盘数据列表 */
export const fetchDataReply = (data: { page: number, size: number }) => request.post("/factor/v1/fetch/data_reply", data);
export const fetchDataReply = (data: { page: number; size: number }) => request.post('/factor/v1/fetch/data_reply', data)
/** 贷款支付数据列表 */
export const fetchLoanPayment = (data: { page: number, size: number, status?: number }) => request.post("/factor/v1/fetch/loan_payment", data);
export const fetchLoanPayment = (data: { page: number; size: number; status?: number }) =>
request.post('/factor/v1/fetch/loan_payment', data)
/** 获取日报数据 */
export const fetchOverviewReports = (data: { page: number, size: number }) => request.post("/factor/v1/reports/overview", data);
export const fetchOverviewReports = (data: { page: number; size: number }) => request.post('/factor/v1/reports/overview', data)
/** 获取日报数据 */
export const fetchDailyReports = (data: { page: number, size: number }) => request.post("/factor/v1/reports/days", data);
export const fetchDailyReports = (data: { page: number; size: number }) => request.post('/factor/v1/reports/days', data)
/** 获取周报数据 */
export const fetchWeeklyReports = (data: { page: number, size: number }) => request.post("/factor/v1/reports/weeks", data);
export const fetchWeeklyReports = (data: { page: number; size: number }) => request.post('/factor/v1/reports/weeks', data)
/** 获取月报数据 */
export const fetchMonthlyReports = (data: { page: number, size: number }) => request.post("/factor/v1/reports/months", data);
export const fetchMonthlyReports = (data: { page: number; size: number }) => request.post('/factor/v1/reports/months', data)
/** 获取批次详情 */
export const getLoanBatchDetail = (data: { batch_id: number }) => request.post(`/factor/v1/get/batch`,data);
export const getLoanBatchDetail = (data: { batch_id: number }) => request.post(`/factor/v1/get/batch`, data)

View File

@@ -1,43 +1,44 @@
import { request } from "@/api/request";
import { request } from '@/api/request'
export interface MenuItem {
id?: number;
identity?: string;
code?: string;
description?: string;
menu_icon?: string;
menu_path?: string;
parent_id?: number | null;
title?: string;
title_en?: string;
type?: number;
sort_key?: number;
order?: number; // 排序字段(用于本地排序)
app_id?: number;
created_at?: string;
children?: MenuItem[];
is_web_page?: boolean; // 是否为网页
web_url?: string; // 嵌入的网页URL
component?: string; // 路由文件地址
is_new_tab?: boolean; // 是否在新窗口打开
is_full?: boolean; // 单独的页面,不包含菜单
hide_menu?: boolean; // 隐藏菜单栏的菜单
id?: number
identity?: string
code?: string
description?: string
menu_icon?: string
menu_path?: string
parent_id?: number | null
title?: string
title_en?: string
type?: number
sort_key?: number
order?: number // 排序字段(用于本地排序)
app_id?: number
created_at?: string
children?: MenuItem[]
is_web_page?: boolean // 是否为网页
web_url?: string // 嵌入的网页URL
component?: string // 路由文件地址
is_new_tab?: boolean // 是否在新窗口打开
is_full?: boolean // 单独的页面,不包含菜单
hide_menu?: boolean // 隐藏菜单栏的菜单
}
/** 获取菜单 */
export const fetchMenu = (data: { page: number, size: number }) => request.post("/rbac2/v1/pmn/fetch", data, { needWorkspace: true });
export const fetchMenu = (data: { page: number; size: number }) => request.post('/rbac2/v1/pmn/fetch', data, { needWorkspace: true })
/** 创建菜单 */
export const createMenu = (data: MenuItem) => request.post("/rbac2/v1/pmn/create", data, { needWorkspace: true });
export const createMenu = (data: MenuItem) => request.post('/rbac2/v1/pmn/create', data, { needWorkspace: true })
/** 修改菜单 */
export const modifyMenu = (data: MenuItem) => request.post("/rbac2/v1/pmn/modify", data, { needWorkspace: true });
export const modifyMenu = (data: MenuItem) => request.post('/rbac2/v1/pmn/modify', data, { needWorkspace: true })
/** 删除菜单 */
export const deleteMenu = (data: { id: MenuItem['id'] }) => request.post("/rbac2/v1/pmn/del", data, { needWorkspace: true });
export const deleteMenu = (data: { id: MenuItem['id'] }) => request.post('/rbac2/v1/pmn/del', data, { needWorkspace: true })
/** 更新菜单排序 */
export const updateMenuOrder = (data: { pmn_id: number, sort_key: number }[]) =>
request.post("/rbac2/v1/pmn/sort", data);
export const updateMenuOrder = (data: { pmn_id: number; sort_key: number }[]) => request.post('/rbac2/v1/pmn/sort', data)
/** 用户-给指定用户设置权限 */
export const userPmn = (data: { code: string; list: { id: number, pmn_id: number }[] }) => request.post("/rbac2/v1/pmn/user_pmn", data, { needWorkspace: true });
export const userPmn = (data: { code: string; list: { id: number; pmn_id: number }[] }) =>
request.post('/rbac2/v1/pmn/user_pmn', data, { needWorkspace: true })
/** 用户-给指定用户设置权限 */
export const userSetPmn = (data: { list: { id: number, pmn_id: number }[] }) => request.post("/rbac2/v1/pmn/user/set_pmn", data, { needWorkspace: true });
export const userSetPmn = (data: { list: { id: number; pmn_id: number }[] }) =>
request.post('/rbac2/v1/pmn/user/set_pmn', data, { needWorkspace: true })

View File

@@ -1,35 +1,37 @@
import { request } from "@/api/request";
import { request } from '@/api/request'
/** 获取报表 */
export const fetchReports = (data: { page: number, size: number, keyword?: string }) => request.get("/api/v1/report/basics", { params: data });
export const fetchReports = (data: { page: number; size: number; keyword?: string }) =>
request.get('/api/v1/report/basics', { params: data })
/** 创建报表任务 */
export const fetchReportNewTask = (keyName:string,data: any) => request.post("/api/v1/report/task/new/"+keyName, data);
export const fetchReportNewTask = (keyName: string, data: any) => request.post('/api/v1/report/task/new/' + keyName, data)
/** 获取报表基础信息详情 */
export const fetchReportBasicInfo = (id: string) => request.get(`/api/v1/report/basics/${id}`);
export const fetchReportBasicInfo = (id: string) => request.get(`/api/v1/report/basics/${id}`)
/** 获取指定报表的调度列表 */
export const fetchReportSchedules = (report_basic_id: string) => request.get(`/api/v1/report/schedules/${report_basic_id}`);
export const fetchReportSchedules = (report_basic_id: string) => request.get(`/api/v1/report/schedules/${report_basic_id}`)
/** 批量创建报表调度 */
export const fetchReportSchedulesBatch = (data: any) => request.post("/api/v1/report/schedules/batch", data);
export const fetchReportSchedulesBatch = (data: any) => request.post('/api/v1/report/schedules/batch', data)
/** 获取指定报表的历史记录列表 */
export const fetchReportHistories = (report_basic_id: string) => request.get(`/api/v1/report/histories/${report_basic_id}?status=2`);
export const fetchReportHistories = (report_basic_id: string) => request.get(`/api/v1/report/histories/${report_basic_id}?status=2`)
/** 获取报表历史记录列表 */
export const fetchReportHistoriesList = (data: { page: number, size: number, keyword?: string }) => request.get("/api/v1/report/histories", { params: data });
export const fetchReportHistoriesList = (data: { page: number; size: number; keyword?: string }) =>
request.get('/api/v1/report/histories', { params: data })
/** 获取邮件推送记录列表 */
export enum PushStatus {
/** 发送成功 */
Success = 1,
/** 发送失败 */
Failed = 2
Failed = 2,
}
export const fetchReportPushesList = (data: { page: number, size: number, keyword?: string, status?: PushStatus }) => request.get("/api/v1/report/pushes", { params: data });
export const fetchReportPushesList = (data: { page: number; size: number; keyword?: string; status?: PushStatus }) =>
request.get('/api/v1/report/pushes', { params: data })
/** 重新发送邮件 */
export const fetchReportPushesResend = (id: number) => request.post(`/api/v1/report/pushes/${id}`);
export const fetchReportPushesResend = (id: number) => request.post(`/api/v1/report/pushes/${id}`)

View File

@@ -1,49 +1,52 @@
import { request } from "@/api/request";
import { LoginRequest, UserItem } from "../types";
import { request } from '@/api/request'
import { LoginRequest, UserItem } from '../types'
/** 登录 */
export const login = (data: LoginRequest) => request.post("/rbac2/v1/login", data);
export const login = (data: LoginRequest) => request.post('/rbac2/v1/login', data)
/** 登出 */
export const logout = () => request.post("/rbac2/v1/logout");
export const logout = () => request.post('/rbac2/v1/logout')
/** 获取菜单列表 */
export const getMenuList = () => request.post("/rbac2/v1/user/menu");
export const getMenuList = () => request.post('/rbac2/v1/user/menu')
/** 创建用户 */
export const createUser = (data: UserItem) => request.post("/rbac2/v1/user/create", data, { needWorkspace: true });
export const createUser = (data: UserItem) => request.post('/rbac2/v1/user/create', data, { needWorkspace: true })
/** 获取用户详情 */
export const getUserDetail = (data: UserItem) => request.post("/rbac2/v1/user/detail", data);
export const getUserDetail = (data: UserItem) => request.post('/rbac2/v1/user/detail', data)
/** 获取用户列表 */
export const fetchUserList = (data: { page: number, size: number, keyword?: string }) => request.post("/rbac2/v1/user/fetch", data);
export const fetchUserList = (data: { page: number; size: number; keyword?: string }) => request.post('/rbac2/v1/user/fetch', data)
/** 修改用户 */
export const modifyUser = (data: UserItem) => request.post("/rbac2/v1/user/modify", data);
export const modifyUser = (data: UserItem) => request.post('/rbac2/v1/user/modify', data)
/** 删除用户 */
export const deleteUser = (data: { id: UserItem['id'] }) => request.post("/rbac2/v1/user/del", data);
export const deleteUser = (data: { id: UserItem['id'] }) => request.post('/rbac2/v1/user/del', data)
/** 获取所有权限【树形】 */
export const getUserPmnTree = (data: { id: UserItem['id'], workspace: string }) => request.post("/rbac2/v1/user/pmn_tree", data);
export const getUserPmnTree = (data: { id: UserItem['id']; workspace: string }) => request.post('/rbac2/v1/user/pmn_tree', data)
/** 用户-获取所有权限【平面】 */
export const userPmn = (data: { id: UserItem['id'], workspace: string }) => request.post("/rbac2/v1/user/pmn", data, { needWorkspace: true });
export const userPmn = (data: { id: UserItem['id']; workspace: string }) =>
request.post('/rbac2/v1/user/pmn', data, { needWorkspace: true })
/** 用户-给指定用户设置权限 */
export const userSetPmn = (data: { list: { id: number, pmn_id: number }[] }) => request.post("/rbac2/v1/user/set_pmn", data, { needWorkspace: true });
export const userSetPmn = (data: { list: { id: number; pmn_id: number }[] }) =>
request.post('/rbac2/v1/user/set_pmn', data, { needWorkspace: true })
/** 用户-给指定用户编辑权限 */
export const userModifyPmn = (data: { id: UserItem['id'], list: number[] }) => request.post("/rbac2/v1/user/modify_pmn", data, { needWorkspace: true });
export const userModifyPmn = (data: { id: UserItem['id']; list: number[] }) =>
request.post('/rbac2/v1/user/modify_pmn', data, { needWorkspace: true })
/** 重置密码(短信) */
export const resetUserPassword = (data: {
/** 账号 */
account: string;
account: string
/** 验证码 */
code: string;
code: string
/** 密码 */
password: string;
password: string
/** 手机号 */
phone: string;
}) => request.post("/rbac2/v1/reset", data);
phone: string
}) => request.post('/rbac2/v1/reset', data)
/** 管理员重置用户密码 */
export const adminResetUserPassword = (data: {
/** 用户ID */
id: number;
id: number
/** 新密码 */
password: string;
}) => request.post("/rbac2/v1/user/reset_password", data);
password: string
}) => request.post('/rbac2/v1/user/reset_password', data)

17
src/api/ops/alertEvent.ts Normal file
View File

@@ -0,0 +1,17 @@
import { request } from '@/api/request'
export const fetchAlertEvents = (data: {
page?: number
page_size?: number
alert_record_id?: number
event_type?: string
source?: string
severity?: string
start_time?: string
end_time?: string
keyword?: string
sort?: string
order?: string
}) => request.get('/Alert/v1/event/list', { params: data })
export const fetchAlertEventDetail = (id: number) => request.get(`/Alert/v1/event/get/${id}`)

View File

@@ -1,56 +1,53 @@
import { request } from "@/api/request";
import { request } from '@/api/request'
/** 获取 告警记录列表 */
export const fetchHistories = (data: {
page?: number,
page_size?: number,
size?: number,
policy_id?: number,
rule_id?: number,
status?: string,
severity_id?: number,
starts_at?: string,
ends_at?: string,
export const fetchHistories = (data: {
page?: number
page_size?: number
size?: number
policy_id?: number
rule_id?: number
status?: string
severity_id?: number
starts_at?: string
ends_at?: string
keyword?: string
}) => {
// 兼容 size 参数,转换为 page_size
const params: any = { ...data };
const params: any = { ...data }
if (params.size !== undefined && params.page_size === undefined) {
params.page_size = params.size;
delete params.size;
params.page_size = params.size
delete params.size
}
return request.get("/Alert/v1/record/list", { params });
};
return request.get('/Alert/v1/record/list', { params })
}
/** 获取 告警记录详情 */
export const fetchHistoryDetail = (id: number) => request.get(`/Alert/v1/record/get/${id}`);
export const fetchHistoryDetail = (id: number) => request.get(`/Alert/v1/record/get/${id}`)
/** 获取 告警统计 */
export const fetchAlertStatistics = (data: {
policy_id?: number,
starts_at?: string,
ends_at?: string
}) => request.get("/Alert/v1/record/statistics", { params: data });
export const fetchAlertStatistics = (data: { policy_id?: number; starts_at?: string; ends_at?: string }) =>
request.get('/Alert/v1/record/statistics', { params: data })
/** 获取 告警计数 */
export const fetchAlertCount = () => request.get("/Alert/v1/record/count");
export const fetchAlertCount = () => request.get('/Alert/v1/record/count')
/** 获取 告警处理记录列表 */
export const fetchAlertProcessList = (data: {
alert_record_id?: number,
action?: string,
page?: number,
page_size?: number,
keyword?: string,
sort?: string,
alert_record_id?: number
action?: string
page?: number
page_size?: number
keyword?: string
sort?: string
order?: string
}) => request.get("/Alert/v1/process/list", { params: data });
}) => request.get('/Alert/v1/process/list', { params: data })
/** 获取 告警趋势 */
export const fetchAlertTrend = (data: {
granularity?: string, // day / month
start_time?: string,
end_time?: string,
policy_id?: number,
granularity?: string // day / month
start_time?: string
end_time?: string
policy_id?: number
severity_id?: number
}) => request.get("/Alert/v1/record/trend", { params: data });
}) => request.get('/Alert/v1/record/trend', { params: data })

View File

@@ -1,21 +1,13 @@
import { request } from "@/api/request";
import { request } from '@/api/request'
/** 获取告警级别列表(分页) */
export const fetchAlertLevelList = (data?: {
page?: number;
page_size?: number;
keyword?: string;
enabled?: string;
}) => {
return request.get("/Alert/v1/severity/list", { params: data || {} });
};
export const fetchAlertLevelList = (data?: { page?: number; page_size?: number; keyword?: string; enabled?: string }) => {
return request.get('/Alert/v1/severity/list', { params: data || {} })
}
/** 获取告警级别下拉选项(不分页),默认仅返回 enabled=true */
export const fetchAlertLevelOptions = (data?: {
keyword?: string;
enabled?: 'true' | 'false';
}) => {
return request.get("/Alert/v1/severity/options", {
export const fetchAlertLevelOptions = (data?: { keyword?: string; enabled?: 'true' | 'false' }) => {
return request.get('/Alert/v1/severity/options', {
params: {
keyword: data?.keyword || undefined,
enabled: data?.enabled ?? 'true',
@@ -25,39 +17,39 @@ export const fetchAlertLevelOptions = (data?: {
/** 获取告警级别详情 */
export const fetchAlertLevelDetail = (id: number) => {
return request.get(`/Alert/v1/severity/get/${id}`);
};
return request.get(`/Alert/v1/severity/get/${id}`)
}
/** 创建告警级别 */
export const createAlertLevel = (data: {
name: string;
code: string;
description?: string;
color: string;
icon?: string;
priority?: number;
enabled?: boolean;
config?: string;
name: string
code: string
description?: string
color: string
icon?: string
priority?: number
enabled?: boolean
config?: string
}) => {
return request.post("/Alert/v1/severity/create", data);
};
return request.post('/Alert/v1/severity/create', data)
}
/** 更新告警级别 */
export const updateAlertLevel = (data: {
id: number;
name?: string;
code?: string;
description?: string;
color?: string;
icon?: string;
priority?: number;
enabled?: boolean;
config?: string;
id: number
name?: string
code?: string
description?: string
color?: string
icon?: string
priority?: number
enabled?: boolean
config?: string
}) => {
return request.post("/Alert/v1/severity/update", data);
};
return request.post('/Alert/v1/severity/update', data)
}
/** 删除告警级别 */
export const deleteAlertLevel = (id: number) => {
return request.delete(`/Alert/v1/severity/delete/${id}`);
};
return request.delete(`/Alert/v1/severity/delete/${id}`)
}

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

@@ -1,49 +1,53 @@
import { request } from "@/api/request";
import { request } from '@/api/request'
/** 获取 当前告警记录列表 */
export const fetchAlertRecords = (data: {
page?: number,
page_size?: number,
policy_id?: number,
rule_id?: number,
status?: string,
severity_id?: number,
start_time?: string,
end_time?: string,
export const fetchAlertRecords = (data: {
page?: number
page_size?: number
policy_id?: number
rule_id?: number
status?: string
severity_id?: number
start_time?: string
end_time?: string
keyword?: string
sort?: string
order?: string
binding_id?: number
resource_category?: string
server_identity?: string
ip?: string
}) => {
return request.get("/Alert/v1/record/list", { params: data });
};
return request.get('/Alert/v1/record/list', { params: data })
}
/** 获取 告警记录详情 */
export const fetchAlertRecordDetail = (id: number) => request.get(`/Alert/v1/record/get/${id}`);
export const fetchAlertRecordDetail = (id: number) => request.get(`/Alert/v1/record/get/${id}`)
/** 创建处理记录(执行确认/解决/屏蔽等操作) */
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;
silence_until?: string;
silence_reason?: string;
escalate_level?: string;
escalate_to?: string;
root_cause?: string;
solution?: string;
alert_record_id: number
action: string // ack / resolve / silence / comment / assign / escalate / close
comment?: string
assign_to?: string
assign_to_id?: string
silence_until?: string
silence_reason?: string
escalate_level?: string
escalate_to?: string
root_cause?: string
solution?: string
// 按后端约定metadata 为 JSON 字符串,例如 "{}"
metadata?: string;
}) => request.post("/Alert/v1/process/create", data);
metadata?: string
}) => request.post('/Alert/v1/process/create', data)
/** 获取 告警处理记录列表 */
export const fetchAlertProcessList = (data: {
alert_record_id?: number,
action?: string,
page?: number,
page_size?: number,
keyword?: string,
sort?: string,
alert_record_id?: number
action?: string
page?: number
page_size?: number
keyword?: string
sort?: string
order?: string
}) => request.get("/Alert/v1/process/list", { params: data });
}) => request.get('/Alert/v1/process/list', { params: data })

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,19 +127,12 @@ export const COMPARE_OPERATORS = [
export const RULE_TYPES = [
{ value: 'static', label: '静态阈值' },
{ value: 'dynamic', label: '动态阈值' },
{ value: 'promql', label: 'PromQL 表达式' },
]
// ==================== 告警模板接口 ====================
/** 获取告警模板列表 */
export const fetchTemplateList = (params?: {
page?: number
page_size?: number
name?: string
category?: string
enabled?: boolean
}) => {
export const fetchTemplateList = (params?: { page?: number; page_size?: number; name?: string; category?: string; enabled?: boolean }) => {
return request.get('/Alert/v1/template/list', { params })
}
@@ -195,10 +188,7 @@ export const fetchSeverityList = () => {
}
/** 获取告警级别下拉选项(不分页),支持 keyword 模糊搜索与 enabled=true 过滤 */
export const fetchSeverityOptions = (params?: {
keyword?: string
enabled?: 'true' | 'false'
}) => {
export const fetchSeverityOptions = (params?: { keyword?: string; enabled?: 'true' | 'false' }) => {
return request.get('/Alert/v1/severity/options', {
params: {
keyword: params?.keyword || undefined,
@@ -215,11 +205,6 @@ export const fetchSeverityByCode = (code: string) => {
// ==================== 指标元数据接口 ====================
/** 获取指标元数据 */
export const fetchMetricsMeta = (params: {
data_source: string
server_identity?: string
keyword?: string
limit?: number
}) => {
export const fetchMetricsMeta = (params: { data_source: string; server_identity?: string; keyword?: string; limit?: number }) => {
return request.get('/DC-Control/v1/services/metrics/meta', { params })
}
}

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
@@ -105,6 +96,11 @@ export const createAsset = (data: AssetForm) => {
return request.post('/Assets/v1/asset/create', data)
}
/** 批量导入资产 */
export const bulkImportAssets = (data: { dry_run?: boolean; items: any[] }) => {
return request.post('/Assets/v1/asset/bulk_import', data)
}
/** 更新资产 */
export const updateAsset = (data: AssetForm) => {
return request.put('/Assets/v1/asset/update', data)
@@ -132,17 +128,24 @@ export const fetchSupplierOptions = () => {
return request.get('/Assets/v1/supplier/all')
}
/** 获取数据中心列表(下拉) */
export const fetchDatacenterOptions = () => {
return request.get('/Assets/v1/datacenter/list')
/** 绑定资产和监控资源 */
export const linkAssetResource = (data: {
asset_id: number
resource_uid: string
display_name?: string
business_system_id?: number
health_status?: string
alert_status?: string
}) => {
return request.post('/Assets/v1/asset/resource/link', data)
}
/** 根据数据中心获取楼层列表 */
export const fetchFloorOptions = (datacenterId: number) => {
return request.get(`/Assets/v1/datacenter/${datacenterId}`)
/** 查询资产绑定的监控资源 */
export const fetchAssetResourceBindings = (assetId: number) => {
return request.get(`/Assets/v1/asset/resource/${assetId}`)
}
/** 获取机柜列表(下拉) */
export const fetchRackOptions = (params?: { datacenter_id?: number; floor_id?: number }) => {
return request.post('/Assets/v1/rack/list', params || {})
/** 解除资产和监控资源绑定 */
export const unlinkAssetResource = (assetId: number, resourceUid: string) => {
return request.delete(`/Assets/v1/asset/resource/${assetId}`, { params: { resource_uid: resourceUid } })
}

87
src/api/ops/automation.ts Normal file
View File

@@ -0,0 +1,87 @@
import { request } from '@/api/request'
export interface AutomationScript {
id: number
name: string
resource_category: string
command_template: string
approval_level: string
rollback_command?: string
rollback_required: boolean
description?: string
status: string
created_at?: string
updated_at?: string
}
export interface AutomationExecution {
id: number
script_id: number
mode: string
status: string
resource_uid: string
rendered_command: string
incident_id?: number
ticket_id?: number
approval_level: string
approved_by?: string
approved_at?: string
operator?: string
started_at?: string
finished_at?: string
timeout_sec?: number
stdout?: string
stderr?: string
exit_code?: number
error?: string
audit_trail?: string
created_at?: string
updated_at?: string
}
export interface AutomationRunRequest {
resource_uid: string
incident_id?: number
ticket_id?: number
operator?: string
approver?: string
approved?: boolean
mode?: 'execute' | 'rollback'
variables?: Record<string, string>
}
export interface PageResponse<T> {
total: number
page: number
page_size: number
data: T[]
}
export const fetchAutomationScripts = (params?: Record<string, any>) =>
request.get<{ details: PageResponse<AutomationScript> }>('/DC-Control/v1/automation/scripts', { params })
export const createAutomationScript = (data: Partial<AutomationScript>) =>
request.post<{ details: AutomationScript }>('/DC-Control/v1/automation/scripts', data)
export const updateAutomationScript = (id: number, data: Partial<AutomationScript>) =>
request.put(`/DC-Control/v1/automation/scripts/${id}`, data)
export const deleteAutomationScript = (id: number) => request.delete(`/DC-Control/v1/automation/scripts/${id}`)
export const dryRunAutomationScript = (id: number, data: AutomationRunRequest) =>
request.post<{ details: AutomationExecution }>(`/DC-Control/v1/automation/scripts/${id}/dry-run`, data)
export const requestAutomationExecution = (id: number, data: AutomationRunRequest) =>
request.post<{ details: AutomationExecution }>(`/DC-Control/v1/automation/scripts/${id}/executions`, data)
export const executeAutomationScript = (id: number, data: AutomationRunRequest) =>
request.post<{ details: AutomationExecution }>(`/DC-Control/v1/automation/scripts/${id}/execute`, data)
export const rollbackAutomationScript = (id: number, data: AutomationRunRequest) =>
request.post<{ details: AutomationExecution }>(`/DC-Control/v1/automation/scripts/${id}/rollback`, data)
export const fetchAutomationExecutions = (params?: Record<string, any>) =>
request.get<{ details: PageResponse<AutomationExecution> }>('/DC-Control/v1/automation/executions', { params })
export const approveAutomationExecution = (id: number, data: { approver: string; operator?: string }) =>
request.post<{ details: AutomationExecution }>(`/DC-Control/v1/automation/executions/${id}/approve`, data)

104
src/api/ops/bigScreen.ts Normal file
View File

@@ -0,0 +1,104 @@
import { request } from '@/api/request'
export interface ApiResponse<T = any> {
code: number
data?: T
details?: T
message?: string
}
export interface BigScreen {
id: number
name: string
description?: string
thumbnail?: string
group_id?: number
goview_project_id?: string
width: number
height: number
status: number
publish_status: 'draft' | 'published' | 'offline' | string
is_public?: boolean
creator_name?: string
created_at?: string
updated_at?: string
}
export interface BigScreenGroup {
id: number
name: string
description?: string
sort_order: number
status: number
}
export interface BigScreenPublish {
id: number
screen_id: number
goview_project_id: string
version: string
publish_type: number
status: number
publisher_name?: string
created_at?: string
}
export interface BigScreenCarousel {
id: number
name: string
screen_ids: Record<string, any>
interval_seconds: number
enabled: boolean
}
export interface BigScreenSnapshot {
id: number
screen_id: number
publish_id?: number
file_name: string
file_path: string
file_size: number
checksum?: string
exporter_name?: string
created_at?: string
}
export interface PageResult<T> {
total: number
page: number
page_size: number
data: T[]
}
export const fetchBigScreens = (params?: { page?: number; page_size?: number; keyword?: string }) =>
request.get<ApiResponse<PageResult<BigScreen>>>('/Mgt/v1/screen', { params })
export const createBigScreen = (data: Partial<BigScreen>) =>
request.post<ApiResponse<BigScreen>>('/Mgt/v1/screen', data)
export const updateBigScreen = (id: number, data: Partial<BigScreen>) =>
request.put<ApiResponse<BigScreen>>(`/Mgt/v1/screen/${id}`, data)
export const publishBigScreen = (data: { screen_id: number; publish_type: 1 | 2; change_log?: string }) =>
request.post<ApiResponse<BigScreenPublish>>('/Mgt/v1/publish', data)
export const fetchBigScreenPublishes = (params: { screen_id: number; page?: number; page_size?: number }) =>
request.get<ApiResponse<PageResult<BigScreenPublish>>>('/Mgt/v1/publish', { params })
export const fetchBigScreenGroups = () =>
request.get<ApiResponse<BigScreenGroup[]>>('/Mgt/v1/big-screen/groups')
export const createBigScreenGroup = (data: Partial<BigScreenGroup>) =>
request.post<ApiResponse<BigScreenGroup>>('/Mgt/v1/big-screen/groups', data)
export const fetchBigScreenCarousels = () =>
request.get<ApiResponse<BigScreenCarousel[]>>('/Mgt/v1/big-screen/carousels')
export const createBigScreenCarousel = (data: Partial<BigScreenCarousel>) =>
request.post<ApiResponse<BigScreenCarousel>>('/Mgt/v1/big-screen/carousels', data)
export const fetchBigScreenSnapshots = (params?: { screen_id?: number }) =>
request.get<ApiResponse<BigScreenSnapshot[]>>('/Mgt/v1/big-screen/snapshots', { params })
export const createBigScreenSnapshot = (data: Partial<BigScreenSnapshot>) =>
request.post<ApiResponse<BigScreenSnapshot>>('/Mgt/v1/big-screen/snapshots', data)

View File

@@ -0,0 +1,237 @@
import { request } from '@/api/request'
/** 业务系统基础信息 */
export interface BusinessSystemItem {
id: number
code: string
name: string
system_type: string
owner_user_id?: number
department_id?: number
description?: string
status: string
is_builtin: boolean
health_score?: number
health_level?: string
resource_count?: number
active_incident_count?: number
created_at?: string
updated_at?: string
}
/** 分页响应 */
export interface PageResult<T> {
total: number
page: number
page_size: number
data: T[]
}
/** 业务系统列表查询参数 */
export interface BusinessSystemListParams {
page?: number
size?: number
keyword?: string
status?: string
}
/** 健康分扣分明细 */
export interface BusinessHealthFactor {
key: string
label: string
score_delta: number
status: string
description: string
}
/** 业务健康摘要 */
export interface BusinessHealth {
business_system_id: number
health: {
score: number
level: string
deductions: Record<string, number>
}
signal_counts: {
resources: number
url_probes: number
active_incidents: number
sla_status: string
}
}
/** 业务系统资源成员 */
export interface BusinessResourceMember {
id: number
resource_uid: string
resource_id?: number
role: string
criticality: string
weight: number
description?: string
}
/** 业务拓扑资源 */
export interface BusinessTopologyResource {
id: number
resource_uid: string
resource_category: string
service_identity: string
display_name: string
description?: string
status: string
business_system_id?: number
owner_user_id?: number
department_id?: number
asset_id?: number
tags?: string
source_service?: string
source_table?: string
source_id?: number
created_at?: string
updated_at?: string
}
/** 业务拓扑节点 */
export interface BusinessTopologyNode {
id?: number
node_id: string
resource_uid: string
label: string
type: string
status: string
}
/** 业务拓扑边 */
export interface BusinessTopologyEdge {
id?: number
source_node_id: string
target_node_id: string
topology_id?: number
}
/** 业务系统依赖关系 */
export interface BusinessDependency {
id: number
business_system_id: number
depends_on_business_system_id: number
dependency_type: string
criticality: string
description?: string
}
/** 业务拓扑 */
export interface BusinessTopology {
business_system_id: number
resources: BusinessTopologyResource[] | null
dependencies: BusinessDependency[] | null
nodes: BusinessTopologyNode[] | null
links: BusinessTopologyEdge[] | null
}
/** 业务时间线项 */
export interface BusinessTimelineItem {
source: 'raw_event' | 'alert' | 'incident' | 'ticket' | 'note' | 'document'
source_id: string
occurred_at: string
title: string
message: string
severity?: string
status?: string
metadata?: Record<string, any>
}
/** 业务笔记 */
export interface BusinessNote {
id: number
business_system_id: number
title?: string
content: string
author_user_id?: number
author_name?: string
visibility: string
occurred_at: string
created_at: string
}
/** 关联知识或文档 */
export interface BusinessDocumentLink {
id: number
business_system_id: number
title: string
url: string
link_type: string
description?: string
created_at: string
}
/** 列表响应 */
export interface ListResult<T> {
list: T[] | null
count: number
}
/** 获取业务系统列表 */
export const fetchBusinessSystems = (params?: BusinessSystemListParams) =>
request.get<{ code?: number; details?: PageResult<BusinessSystemItem>; message?: string }>('/DC-Control/v1/business-systems', { params })
/** 获取业务系统详情 */
export const fetchBusinessSystemDetail = (id: number) =>
request.get<{ code?: number; details?: BusinessSystemItem; message?: string }>(`/DC-Control/v1/business-systems/${id}`)
/** 获取业务健康摘要 */
export const fetchBusinessHealth = (id: number) =>
request.get<{ code?: number; details?: BusinessHealth; message?: string }>(`/DC-Control/v1/business-systems/${id}/health`)
/** 获取业务资源成员 */
export const fetchBusinessMembers = (id: number) =>
request.get<{ code?: number; details?: ListResult<BusinessResourceMember>; message?: string }>(`/DC-Control/v1/business-systems/${id}/resources`)
export interface BusinessResourceMemberPayload {
resource_uid?: string
resource_id?: number
role?: string
criticality?: string
weight?: number
description?: string
}
export const createBusinessMember = (id: number, data: BusinessResourceMemberPayload) =>
request.post<{ code?: number; details?: BusinessResourceMember; message?: string }>(`/DC-Control/v1/business-systems/${id}/resources`, data)
export const deleteBusinessMember = (id: number, memberId: number) =>
request.delete<{ code?: number; details?: { message: string }; message?: string }>(`/DC-Control/v1/business-systems/${id}/resources/${memberId}`)
/** 获取业务拓扑 */
export const fetchBusinessTopology = (id: number) =>
request.get<{ code?: number; details?: BusinessTopology; message?: string }>(`/DC-Control/v1/business-systems/${id}/topology`)
/** 获取业务系统依赖 */
export const fetchBusinessDependencies = (id: number) =>
request.get<{ code?: number; details?: ListResult<BusinessDependency>; message?: string }>(`/DC-Control/v1/business-systems/${id}/dependencies`)
/** 创建业务系统依赖 */
export const createBusinessDependency = (id: number, data: Partial<BusinessDependency>) =>
request.post<{ code?: number; details?: BusinessDependency; message?: string }>(`/DC-Control/v1/business-systems/${id}/dependencies`, data)
/** 获取业务时间线 */
export const fetchBusinessTimeline = (id: number, params?: { limit?: number }) =>
request.get<{ code?: number; details?: ListResult<BusinessTimelineItem>; message?: string }>(`/DC-Control/v1/business-systems/${id}/timeline`, {
params,
})
/** 获取业务笔记 */
export const fetchBusinessNotes = (id: number) =>
request.get<{ code?: number; details?: ListResult<BusinessNote>; message?: string }>(`/DC-Control/v1/business-systems/${id}/notes`)
/** 创建业务笔记 */
export const createBusinessNote = (id: number, data: { title: string; content: string }) =>
request.post<{ code?: number; details?: BusinessNote; message?: string }>(`/DC-Control/v1/business-systems/${id}/notes`, data)
/** 获取业务文档链接 */
export const fetchBusinessDocumentLinks = (id: number) =>
request.get<{ code?: number; details?: ListResult<BusinessDocumentLink>; message?: string }>(`/DC-Control/v1/business-systems/${id}/documents`)
/** 新增业务文档链接 */
export const createBusinessDocumentLink = (id: number, data: Partial<BusinessDocumentLink>) =>
request.post<{ code?: number; details?: BusinessDocumentLink; message?: string }>(`/DC-Control/v1/business-systems/${id}/documents`, data)

View File

@@ -152,22 +152,19 @@ export interface MetricMetaQueryParams {
* 获取数据库服务列表
* @param params 查询参数
*/
export const fetchDatabaseList = (params: DatabaseQueryParams) =>
request.get<DatabaseListResponse>('/DC-Control/v1/database', { params })
export const fetchDatabaseList = (params: DatabaseQueryParams) => request.get<DatabaseListResponse>('/DC-Control/v1/database', { params })
/**
* 获取数据库服务详情
* @param id 服务ID
*/
export const fetchDatabaseDetail = (id: number) =>
request.get<DatabaseService>(`/DC-Control/v1/database/${id}`)
export const fetchDatabaseDetail = (id: number) => request.get<DatabaseService>(`/DC-Control/v1/database/${id}`)
/**
* 创建数据库服务
* @param data 创建参数
*/
export const createDatabase = (data: CreateDatabaseParams) =>
request.post<{ message: string; id: number }>('/DC-Control/v1/database', data)
export const createDatabase = (data: CreateDatabaseParams) => request.post<{ message: string; id: number }>('/DC-Control/v1/database', data)
/**
* 更新数据库服务
@@ -181,8 +178,7 @@ export const updateDatabase = (id: number, data: UpdateDatabaseParams) =>
* 删除数据库服务(软删除)
* @param id 服务ID
*/
export const deleteDatabase = (id: number) =>
request.delete<{ message: string }>(`/DC-Control/v1/database/${id}`)
export const deleteDatabase = (id: number) => request.delete<{ message: string }>(`/DC-Control/v1/database/${id}`)
/**
* 按服务主键采集指标
@@ -196,4 +192,4 @@ export const collectDatabaseMetrics = (data: CollectMetricsParams) =>
* @param params 查询参数
*/
export const fetchMetricMeta = (params: MetricMetaQueryParams) =>
request.get<MetricMetaResponse>('/DC-Control/v1/services/metrics/meta', { params })
request.get<MetricMetaResponse>('/DC-Control/v1/services/metrics/meta', { params })

View File

@@ -1,41 +1,46 @@
import { request } from "@/api/request";
import { request } from '@/api/request'
/** 获取所有已启用数据中心(下拉,不分页) */
export const fetchDatacenterAll = (params?: { keyword?: string }) => {
return request.get("/Assets/v1/datacenter/all", { params });
};
return request.get('/Assets/v1/datacenter/all', { params })
}
/** 获取数据中心列表(分页) */
export const fetchDatacenterList = (data?: {
page?: number;
page_size?: number;
keyword?: string;
province_id?: number;
city_id?: number;
status?: string;
page?: number
page_size?: number
keyword?: string
province_id?: number
city_id?: number
status?: string
}) => {
return request.post("/Assets/v1/datacenter/list", data || {});
};
return request.post('/Assets/v1/datacenter/list', data || {})
}
/** 获取数据中心详情 */
export const fetchDatacenterDetail = (id: number) => {
return request.get(`/Assets/v1/datacenter/detail/${id}`);
};
return request.get(`/Assets/v1/datacenter/detail/${id}`)
}
/** 创建数据中心 */
export const createDatacenter = (data: any) => {
return request.post("/Assets/v1/datacenter/create", data);
};
return request.post('/Assets/v1/datacenter/create', data)
}
/** 批量导入数据中心 */
export const bulkImportDatacenters = (data: { dry_run?: boolean; items: any[] }) => {
return request.post('/Assets/v1/datacenter/bulk_import', data)
}
/** 更新数据中心 */
export const updateDatacenter = (data: any) => {
return request.put("/Assets/v1/datacenter/update", data);
};
return request.put('/Assets/v1/datacenter/update', data)
}
/** 删除数据中心 */
export const deleteDatacenter = (id: number) => {
return request.delete(`/Assets/v1/datacenter/delete/${id}`);
};
return request.delete(`/Assets/v1/datacenter/delete/${id}`)
}
/** 获取省份列表(用于下拉选择) */
export const fetchProvinceList = (params?: { keyword?: string }) => {
@@ -49,10 +54,10 @@ export const fetchCityList = (provinceId: number, params?: { keyword?: string })
/** 根据城市获取数据中心列表 */
export const fetchDatacenterByCity = (cityId: number) => {
return request.get(`/Assets/v1/datacenter/city/${cityId}`);
};
return request.get(`/Assets/v1/datacenter/city/${cityId}`)
}
/** 获取数据中心树形结构 */
export const fetchDatacenterTree = () => {
return request.get("/Assets/v1/datacenter/tree");
};
return request.get('/Assets/v1/datacenter/tree')
}

View File

@@ -1,42 +1,247 @@
import { request } from "@/api/request";
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
}
/** 获取 采集器 */
export const fetchCollectors = (data: { page: number, size: number, keyword?: string }) => request.get("/DC-Control/v1/collectors", { params: data });
export const fetchCollectors = (data: { page: number; size: number; keyword?: string }) =>
request.get('/DC-Control/v1/collectors', { params: data })
/** 新增 采集器 */
export const createCollector = (data: any) => request.post("/DC-Control/v1/collectors", data);
export const createCollector = (data: any) => request.post('/DC-Control/v1/collectors', data)
/** 删除 采集器 */
export const deleteCollector = (id: number) => request.delete(`/DC-Control/v1/collectors/${id}`);
export const deleteCollector = (id: number) => request.delete(`/DC-Control/v1/collectors/${id}`)
/** 获取 采集器详情 */
export const fetchCollectorDetail = (id: number) => request.get(`/DC-Control/v1/collectors/${id}`);
export const fetchCollectorDetail = (id: number) => request.get(`/DC-Control/v1/collectors/${id}`)
/** 更新 采集器 */
export const updateCollector = (data: any) => request.put(`/DC-Control/v1/collectors/${data.id}`, data);
export const updateCollector = (data: any) => request.put(`/DC-Control/v1/collectors/${data.id}`, data)
/** 获取 采集器统计数据 */
export const fetchCollectorStatistics = () => request.get("/DC-Control/v1/statistics");
export const fetchCollectorStatistics = () => request.get('/DC-Control/v1/statistics')
/** 获取 许可证信息 */
export const fetchLicenseInfo = () =>
request.get<{ code?: number; data?: LicenceConfig; message?: string }>("/DC-Control/v1/license");
request.get<{ code: number; details: LicenceInfo; message: string; timeseq: number }>('/DC-Control/v1/license')
export interface PageResult<T> {
total: number
page: number
page_size: number
data: T[]
}
export interface OptionItem {
id?: number
label: string
value: string
}
export interface ControlResource {
id: number
resource_uid: string
resource_category: string
service_identity: string
display_name: string
description?: string
status: string
business_system_id?: number
owner_user_id?: number
department_id?: number
asset_id?: number
datacenter_id?: number
room_id?: number
rack_id?: number
unit_start?: number
unit_end?: number
location_ref?: string
tags?: string
source_service?: string
source_table?: string
source_id?: number
created_at?: string
updated_at?: string
}
export interface ControlResourcePayload {
resource_category?: string
service_identity?: string
display_name?: string
description?: string
status?: string
business_system_id?: number | null
owner_user_id?: number | null
department_id?: number | null
asset_id?: number | null
datacenter_id?: number | null
room_id?: number | null
rack_id?: number | null
unit_start?: number | null
unit_end?: number | null
location_ref?: string | null
tags?: string
}
export interface ControlResourceType {
id: number
resource_category: string
display_name: string
metric_template?: string
supported_collect_method?: string
description?: string
enabled: boolean
}
export interface ControlMetricDefinition {
id: number
metric_code: string
metric_name: string
resource_category: string
unit?: string
threshold_suggestion?: string
collect_method?: string
description?: string
enabled: boolean
}
export interface ControlMetricSeries {
id: number
resource_uid: string
metric_code: string
collection_task_id?: string
storage_driver: string
database_name?: string
table_name?: string
series_key?: string
retention_policy?: string
enabled: boolean
}
export interface ControlBusinessSystem {
id: number
code: string
name: string
system_type: string
owner_user_id?: number
department_id?: number
description?: string
status: string
is_builtin: boolean
}
export interface ControlCollectionStatus {
id: number
resource_uid: string
resource_category: string
service_identity: string
collect_method: string
schedule_interval: number
retry_policy?: string
last_status: string
last_error?: string
last_sample_time?: string
trace_id?: string
source_service?: string
created_at?: string
updated_at?: string
}
export interface ResourceListParams {
page?: number
size?: number
keyword?: string
resource_category?: string
business_system_id?: number
}
export interface MetricSeriesListParams {
page?: number
size?: number
keyword?: string
resource_uid?: string
}
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,
})
export const createControlResource = (data: ControlResourcePayload) =>
request.post<{ code?: number; details?: ControlResource; message?: string }>('/DC-Control/v1/resources', data)
export const updateControlResource = (id: number, data: ControlResourcePayload) =>
request.put<{ code?: number; details?: ControlResource; message?: string }>(`/DC-Control/v1/resources/${id}`, data)
export const deleteControlResource = (id: number) =>
request.delete<{ code?: number; details?: { message: string }; message?: string }>(`/DC-Control/v1/resources/${id}`)
export const backfillControlResources = () =>
request.post<{ code?: number; details?: { message: string }; message?: string }>('/DC-Control/v1/resources/backfill')
export const fetchControlResourceTypes = (params?: { page?: number; size?: number; keyword?: string }) =>
request.get<{ code?: number; details?: PageResult<ControlResourceType>; message?: string }>('/DC-Control/v1/resource-types', { params })
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,
})
export const fetchControlMetricDefinitionOptions = (params?: { resource_category?: string }) =>
request.get<{ code?: number; details?: { list: OptionItem[]; count: number }; message?: string }>(
'/DC-Control/v1/metric-definitions/options',
{ params }
)
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,
})
export const fetchControlBusinessSystemOptions = () =>
request.get<{ code?: number; details?: { list: OptionItem[]; count: number }; message?: string }>(
'/DC-Control/v1/business-systems/options'
)
export const fetchCollectionStatus = (params?: {
page?: number
size?: number
keyword?: string
resource_category?: string
resource_uid?: string
status?: string
}) =>
request.get<{ code?: number; details?: PageResult<ControlCollectionStatus>; message?: string }>('/DC-Control/v1/collection/status', {
params,
})

View File

@@ -1,91 +1,89 @@
import { request } from '@/api/request'
/** 与后端 discovery/summary 对齐 */
export interface DiscoverySummary {
device_total_current: number
online_current: number
offline_current: number
other_status_current: number
primary_scan_ids: number[]
last_run?: DiscoveryScanRun | null
}
export interface DiscoveryScanRun {
id: number
scan_id: number
run_type: string
status: string
started_at: string
finished_at?: string | null
device_total: number
online_count: number
offline_count: number
new_count: number
error_message?: string
trigger_by: string
}
export interface DiscoveryDeviceRow {
id: number
ip_address: string
scan_id?: number
run_id?: number
status: string
mac_address?: string
hostname?: string
device_type?: string
manufacturer?: string
discovery_methods?: string
last_seen_time?: string
last_scan_time?: string
first_seen_time?: string
}
export interface DiscoveryDevicesResponse {
scope: string
scan_id?: number
primary_scan_ids?: number[]
total: number
page: number
page_size: number
data: DiscoveryDeviceRow[]
}
export interface DiscoveryScanRunsResponse {
total: number
page: number
page_size: number
data: DiscoveryScanRun[]
}
export interface DiscoveryConfig {
id: number
primary_scan_ids: number[]
primary_scan_ids_raw?: string
}
export const fetchDiscoverySummary = () =>
request.get<{ code: number; details?: DiscoverySummary }>('/DC-Control/v1/discovery/summary')
export const fetchDiscoveryDevices = (params?: {
scope?: 'current' | 'raw'
scan_id?: number
page?: number
size?: number
status?: string
keyword?: string
}) =>
request.get<{ code: number; details?: DiscoveryDevicesResponse }>('/DC-Control/v1/discovery/devices', {
params,
})
export const fetchDiscoveryScanRuns = (params?: { scan_id?: number; page?: number; size?: number }) =>
request.get<{ code: number; details?: DiscoveryScanRunsResponse }>('/DC-Control/v1/discovery/scan-runs', {
params,
})
export const fetchDiscoveryConfig = () =>
request.get<{ code: number; details?: DiscoveryConfig }>('/DC-Control/v1/discovery/config')
export const updateDiscoveryConfig = (data: { primary_scan_ids: number[] }) =>
request.put<{ code: number; details?: DiscoveryConfig }>('/DC-Control/v1/discovery/config', data)
import { request } from '@/api/request'
/** 与后端 discovery/summary 对齐 */
export interface DiscoverySummary {
device_total_current: number
online_current: number
offline_current: number
other_status_current: number
primary_scan_ids: number[]
last_run?: DiscoveryScanRun | null
}
export interface DiscoveryScanRun {
id: number
scan_id: number
run_type: string
status: string
started_at: string
finished_at?: string | null
device_total: number
online_count: number
offline_count: number
new_count: number
error_message?: string
trigger_by: string
}
export interface DiscoveryDeviceRow {
id: number
ip_address: string
scan_id?: number
run_id?: number
status: string
mac_address?: string
hostname?: string
device_type?: string
manufacturer?: string
discovery_methods?: string
last_seen_time?: string
last_scan_time?: string
first_seen_time?: string
}
export interface DiscoveryDevicesResponse {
scope: string
scan_id?: number
primary_scan_ids?: number[]
total: number
page: number
page_size: number
data: DiscoveryDeviceRow[]
}
export interface DiscoveryScanRunsResponse {
total: number
page: number
page_size: number
data: DiscoveryScanRun[]
}
export interface DiscoveryConfig {
id: number
primary_scan_ids: number[]
primary_scan_ids_raw?: string
}
export const fetchDiscoverySummary = () => request.get<{ code: number; details?: DiscoverySummary }>('/DC-Control/v1/discovery/summary')
export const fetchDiscoveryDevices = (params?: {
scope?: 'current' | 'raw'
scan_id?: number
page?: number
size?: number
status?: string
keyword?: string
}) =>
request.get<{ code: number; details?: DiscoveryDevicesResponse }>('/DC-Control/v1/discovery/devices', {
params,
})
export const fetchDiscoveryScanRuns = (params?: { scan_id?: number; page?: number; size?: number }) =>
request.get<{ code: number; details?: DiscoveryScanRunsResponse }>('/DC-Control/v1/discovery/scan-runs', {
params,
})
export const fetchDiscoveryConfig = () => request.get<{ code: number; details?: DiscoveryConfig }>('/DC-Control/v1/discovery/config')
export const updateDiscoveryConfig = (data: { primary_scan_ids: number[] }) =>
request.put<{ code: number; details?: DiscoveryConfig }>('/DC-Control/v1/discovery/config', data)

View File

@@ -15,7 +15,12 @@ function normalizeTicketListStatusParam(params: Record<string, any>) {
delete params.status
return
}
arr = s.includes(',') ? s.split(',').map((x) => x.trim()).filter(Boolean) : [s]
arr = s.includes(',')
? s
.split(',')
.map((x) => x.trim())
.filter(Boolean)
: [s]
}
if (arr.length === 0) delete params.status
else params.status = arr
@@ -62,6 +67,9 @@ export const fetchFeedbackTickets = (data?: {
creator_id?: number
assignee_id?: number
category_id?: number
incident_id?: number
resource_uid?: string
business_system_id?: number
keyword?: string
/** 格式 YYYY-MM-DD HH:MM:SS */
start_time?: string
@@ -88,55 +96,60 @@ export const fetchFeedbackTickets = (data?: {
}
/** 创建 工单 */
export const createFeedbackTicket = (data: any) => request.post("/Feedback/v1/tickets", data);
export const createFeedbackTicket = (data: any) => request.post('/Feedback/v1/tickets', data)
/** 更新 工单 */
export const updateFeedbackTicket = (id: number, data: any) => request.put(`/Feedback/v1/tickets/${id}`, data);
export const updateFeedbackTicket = (id: number, data: any) => request.put(`/Feedback/v1/tickets/${id}`, data)
/** 删除 工单 */
export const deleteFeedbackTicket = (id: number) => request.delete(`/Feedback/v1/tickets/${id}`);
export const deleteFeedbackTicket = (id: number) => request.delete(`/Feedback/v1/tickets/${id}`)
/** 获取 工单详情 */
export const fetchFeedbackTicketDetail = (id: number) => request.get(`/Feedback/v1/tickets/${id}`);
export const fetchFeedbackTicketDetail = (id: number) => request.get(`/Feedback/v1/tickets/${id}`)
/** 根据工单编号获取详情 */
export const fetchFeedbackTicketByNo = (no: string) => request.get(`/Feedback/v1/tickets/no/${no}`);
export const fetchFeedbackTicketByNo = (no: string) => request.get(`/Feedback/v1/tickets/no/${no}`)
/** 接单 */
export const acceptFeedbackTicket = (id: number) => request.post(`/Feedback/v1/tickets/${id}/accept`);
export const acceptFeedbackTicket = (id: number) => request.post(`/Feedback/v1/tickets/${id}/accept`)
/** 转交 */
export const transferFeedbackTicket = (id: number, data: { assignee_id: number, assignee_name: string, reason?: string }) => request.post(`/Feedback/v1/tickets/${id}/transfer`, data);
export const transferFeedbackTicket = (id: number, data: { assignee_id: number; assignee_name: string; reason?: string }) =>
request.post(`/Feedback/v1/tickets/${id}/transfer`, data)
/** 撤回 */
export const cancelFeedbackTicket = (id: number) => request.post(`/Feedback/v1/tickets/${id}/cancel`);
export const cancelFeedbackTicket = (id: number) => request.post(`/Feedback/v1/tickets/${id}/cancel`)
/** 挂起 */
export const suspendFeedbackTicket = (id: number, data: { content: string }) => request.post(`/Feedback/v1/tickets/${id}/suspend`, data);
export const suspendFeedbackTicket = (id: number, data: { content: string }) => request.post(`/Feedback/v1/tickets/${id}/suspend`, data)
/** 重启 */
export const resumeFeedbackTicket = (id: number) => request.post(`/Feedback/v1/tickets/${id}/resume`);
export const resumeFeedbackTicket = (id: number) => request.post(`/Feedback/v1/tickets/${id}/resume`)
/** 解决 */
export const resolveFeedbackTicket = (id: number, data: { content: string }) => request.post(`/Feedback/v1/tickets/${id}/resolve`, data);
export const resolveFeedbackTicket = (id: number, data: { content: string }) => request.post(`/Feedback/v1/tickets/${id}/resolve`, data)
/** 关闭 */
export const closeFeedbackTicket = (id: number, data: { remarks?: string }) => request.post(`/Feedback/v1/tickets/${id}/close`, data);
export const closeFeedbackTicket = (id: number, data: { remarks?: string }) => request.post(`/Feedback/v1/tickets/${id}/close`, data)
/** 添加评论 */
export const commentFeedbackTicket = (id: number, data: { content: string }) => request.post(`/Feedback/v1/tickets/${id}/comment`, data);
export const commentFeedbackTicket = (id: number, data: { content: string }) => request.post(`/Feedback/v1/tickets/${id}/comment`, data)
/** 获取操作日志 */
export const fetchFeedbackTicketLogs = (id: number) => request.get(`/Feedback/v1/tickets/${id}/logs`);
export const fetchFeedbackTicketLogs = (id: number) => request.get(`/Feedback/v1/tickets/${id}/logs`)
/** 获取关联关系 */
export const fetchFeedbackTicketRelations = (id: number) => request.get(`/Feedback/v1/tickets/${id}/relations`);
export const fetchFeedbackTicketRelations = (id: number) => request.get(`/Feedback/v1/tickets/${id}/relations`)
/** 创建关联 */
export const createFeedbackTicketRelation = (id: number, data: { target_ticket_id: number, relation_type: string, description?: string }) => request.post(`/Feedback/v1/tickets/${id}/relations`, data);
export const createFeedbackTicketRelation = (id: number, data: { target_ticket_id: number; relation_type: string; description?: string }) =>
request.post(`/Feedback/v1/tickets/${id}/relations`, data)
/** 删除关联 */
export const deleteFeedbackTicketRelation = (relationId: number) => request.delete(`/Feedback/v1/tickets/relations/${relationId}`);
export const deleteFeedbackTicketRelation = (relationId: number) => request.delete(`/Feedback/v1/tickets/relations/${relationId}`)
/** 获取 工单统计数据 */
export const fetchFeedbackTicketStatistics = () => request.get("/Feedback/v1/tickets/statistics");
export const fetchFeedbackTicketStatistics = () => request.get('/Feedback/v1/tickets/statistics')
/** 获取 工单 SLA/SLO 统计数据 */
export const fetchFeedbackTicketSLAStatistics = () => request.get('/Feedback/v1/tickets/sla/statistics')

View File

@@ -1,48 +1,60 @@
import { request } from "@/api/request";
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 || {});
};
export const fetchFloorList = (data?: { page?: number; page_size?: number; keyword?: string; datacenter_id?: number; status?: string }) => {
return request.post('/Assets/v1/floor/list', data || {})
}
/** 获取楼层详情 */
export const fetchFloorDetail = (id: number) => {
return request.get(`/Assets/v1/floor/detail/${id}`);
};
return request.get(`/Assets/v1/floor/detail/${id}`)
}
/** 根据数据中心获取楼层列表(下拉,不分页) */
export const fetchFloorListByDatacenter = (
datacenterId: number,
params?: { name?: string }
) => {
return request.get(`/Assets/v1/floor/datacenter/${datacenterId}`, { params });
};
export const fetchFloorListByDatacenter = (datacenterId: number, params?: { name?: string }) => {
return request.get(`/Assets/v1/floor/datacenter/${datacenterId}`, { params })
}
/** 创建楼层 */
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)
}
/** 删除楼层 */
export const deleteFloor = (id: number) => {
return request.delete(`/Assets/v1/floor/delete/${id}`);
};
return request.delete(`/Assets/v1/floor/delete/${id}`)
}
/** 获取数据中心列表(用于下拉选择) */
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 })
}

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