feat: 项目新建

master
JINGYJ 3 days ago
parent 13661fda26
commit eaa15cddc5

@ -0,0 +1,12 @@
###
# @Author: donghao donghao@supervision.ltd
# @Date: 2025-03-12 15:26:57
# @LastEditors: donghao donghao@supervision.ltd
# @LastEditTime: 2025-03-13 09:17:19
# @FilePath: \5G-Loading-Bay-Web\.env.development
# @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
###
# .env.development
NODE_ENV = development
VITE_APP_ENV = development
VITE_APP_BASE_API = http://192.168.10.14:8000

@ -0,0 +1,4 @@
# .env.production
NODE_ENV = production
VITE_APP_ENV = production
VITE_APP_BASE_API = http://127.0.0.1:8000

@ -0,0 +1,74 @@
{
"globals": {
"Component": true,
"ComponentPublicInstance": true,
"ComputedRef": true,
"DirectiveBinding": true,
"EffectScope": true,
"ExtractDefaultPropTypes": true,
"ExtractPropTypes": true,
"ExtractPublicPropTypes": true,
"InjectionKey": true,
"MaybeRef": true,
"MaybeRefOrGetter": true,
"PropType": true,
"Ref": true,
"VNode": true,
"WritableComputedRef": true,
"computed": true,
"createApp": true,
"customRef": true,
"defineAsyncComponent": true,
"defineComponent": true,
"effectScope": true,
"getCurrentInstance": true,
"getCurrentScope": true,
"h": true,
"inject": true,
"isProxy": true,
"isReactive": true,
"isReadonly": true,
"isRef": true,
"markRaw": true,
"nextTick": true,
"onActivated": true,
"onBeforeMount": true,
"onBeforeUnmount": true,
"onBeforeUpdate": true,
"onDeactivated": true,
"onErrorCaptured": true,
"onMounted": true,
"onRenderTracked": true,
"onRenderTriggered": true,
"onScopeDispose": true,
"onServerPrefetch": true,
"onUnmounted": true,
"onUpdated": true,
"onWatcherCleanup": true,
"provide": true,
"reactive": true,
"readonly": true,
"ref": true,
"resolveComponent": true,
"shallowReactive": true,
"shallowReadonly": true,
"shallowRef": true,
"toRaw": true,
"toRef": true,
"toRefs": true,
"toValue": true,
"triggerRef": true,
"unref": true,
"useAttrs": true,
"useCssModule": true,
"useCssVars": true,
"useId": true,
"useModel": true,
"useSlots": true,
"useTemplateRef": true,
"watch": true,
"watchEffect": true,
"watchPostEffect": true,
"watchSyncEffect": true
}
}

24
.gitignore vendored

@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

@ -0,0 +1,3 @@
{
"recommendations": ["Vue.volar"]
}

@ -0,0 +1 @@
5G-Loading-Bay-Web

337
api.md

@ -0,0 +1,337 @@
<!--
* @Author: donghao donghao@supervision.ltd
* @Date: 2025-03-11 14:18:06
* @LastEditors: donghao donghao@supervision.ltd
* @LastEditTime: 2025-03-11 14:35:45
* @FilePath: \5G-Loading-Bay-Web\api.md
* @Description: 接口备注文档
-->
# 系统接口文档
## 一、登录模块Login
### 1.1 用户登录接口
- **接口地址**/api/login
- **请求方式**POST
- **请求参数**
| 参数名 | 类型 | 是否必填 | 说明 |
|----------|--------|----------|--------------|
| username | string | 是 | 用户账号 |
| password | string | 是 | 登录密码 |
- **响应示例**
```json
{
"code": 200,
"success": true,
"data": {
"refresh": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbl90eXBlIjoicmVmcmVzaCIsImV4cCI6MTc0MzA2ODU4NiwiaWF0IjoxNzQxNzcyNTg2LCJqdGkiOiI2MjNhNmUxM2Q3YjM0NmE4YTMzMGQ3ZGY5MTQ2YTliMCIsInVzZXJfaWQiOjF9.nhUnJuMDsQbKG96ma08bH17tWj-4PbQNUYx4vRzpbIQ",
"access": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNzQyMzc3Mzg2LCJpYXQiOjE3NDE3NzI1ODYsImp0aSI6ImMzMmU0ZWQ5ZWU5MTQ3Nzc5MmFkNjQ1ZmM3NmZlYmE4IiwidXNlcl9pZCI6MX0.2IFz-zWaQF7HeL-SrjNR4o1A-9BebPx3KG7_OU1Y4yg",
"status": "ok"
},
"errorMessage": ""
}
```
### 1.2 用户登录接口
- **接口地址**/api/logout
- **请求方式**POST
- **请求参数**
| 参数名 | 类型 | 是否必填 | 说明 |
|----------|--------|----------|--------------|
| token | string | 是 | 登录成功返回的 token |
- **响应示例**
```json
{
"code": 200,
"success": true,
"data": {},
"errorMessage": ""
}
```
![alt text](localFiles/微信图片_20250306134728.png)
# 检测总量汇总接口文档
## 1. 接口基本信息
- **接口名称**:获取检测总量汇总数据
- **请求方法**`GET`
- **接口路径**`/api/v1/system/get_record_stats/`
- **功能描述**:根据传入的时间类型(月/周),获取检测时间、车体检测数量、撑杆检测数量的汇总数据,用于展示柱状图。
## 2. 请求参数
| 参数名 | 类型 | 是否必填 | 描述 | 示例值 |
|-----------|--------|----------|-----------------------|-------|
| dateType | string | 是 | 时间类型,取值`month`或`week` | month |
## 3. 响应参数
| 参数名 | 类型 | 描述 |
|-------------------|---------|--------------------------|
| code | number | 状态码,`200`表示成功 |
| message | string | 提示信息,如成功或失败描述 |
| data | object | 响应数据主体 |
| ├─ dateArr | array | 所有时间的汇总数组 |
| ├─ vehicleDetection| array | 所有车辆检测数量汇总数组 |
| ├─ poleDetection | array | 所有撑杆检测数量汇总数组 |
## 4. 响应示例
```json
{
"code": 200,
"success": true,
"data": {
"dateArr": [
1,
2,
3,
4,
5,
6,
7
],
"pole": [
0,
8,
0,
0,
0,
0,
0
],
"appearance": [
0,
5,
0,
0,
0,
0,
0
]
},
"errorMessage": ""
}
```
![alt text](localFiles/1741680039157.png)
# 设备信息接口文档
## 1. 接口基本信息
- **接口名称**:获取设备信息数据
- **请求方法**`GET`
- **接口路径**`/api/v1/system/get_device_stats/`
- **功能描述**:获取设备总数,以及车体检测、撑杆检测、钩机检测设备的总量,同时包含各类设备的在线、离线、故障数量状态。
## 2. 请求参数
| 参数名 | 类型 | 是否必填 | 描述 |
|--------|--------|----------|--------------|
| 无 | 无 | 无 | 无请求参数 |
## 3. 响应参数
| 参数分类 | 参数名 | 类型 | 描述 |
|----------------------|---------------------------|---------|--------------------------|
| **设备总数** | deviceTotal | number | 设备总数 |
| **车体检测设备** | vehicleDetectionDevice | number | 车体检测设备总量 |
| **车体检测状态** | vehicleStatus | object | 车体检测设备状态信息 |
| ├─ 在线数量 | ├─ onlineCount | number | 车体检测设备在线数量 |
| ├─ 离线数量 | ├─ outlineCount | number | 车体检测设备离线数量 |
| ├─ 故障数量 | ├─ errorCount | number | 车体检测设备故障数量 |
| **撑杆检测设备** | poleDetectionDevice | number | 撑杆检测设备总量 |
| **撑杆检测状态** | poleStatus | object | 撑杆检测设备状态信息 |
| ├─ 在线数量 | ├─ onlineCount | number | 撑杆检测设备在线数量 |
| ├─ 离线数量 | ├─ outlineCount | number | 撑杆检测设备离线数量 |
| ├─ 故障数量 | ├─ errorCount | number | 撑杆检测设备故障数量 |
| **钩机检测设备** | machineDetectionDevice | number | 钩机检测设备总量 |
| **钩机检测状态** | machineStatus | object | 钩机检测设备状态信息 |
| ├─ 在线数量 | ├─ onlineCount | number | 钩机检测设备在线数量 |
| ├─ 离线数量 | ├─ outlineCount | number | 钩机检测设备离线数量 |
| ├─ 故障数量 | ├─ errorCount | number | 钩机检测设备故障数量 |
## 4. 响应示例
```json
{
"code": 200,
"success": true,
"data": {
"deviceTotal": 36,
"pole": {
"total": 12,
"onlineCount": 8,
"outlineCount": 2,
"errorCount": 2
},"appearance": {
"total": 12,
"onlineCount": 8,
"outlineCount": 2,
"errorCount": 2
},
"excavator": {
"total": 12,
"onlineCount": 8,
"outlineCount": 2,
"errorCount": 2
}
},
"errorMessage": ""
}
```
![alt text](localFiles/1741680066982.png)
# 车体检测问题分布接口文档
## 1. 接口基本信息
- **接口名称**:获取车体检测问题分布数据
- **请求方法**`GET`
- **接口路径**`/api/v1/system/get_record_fault_stats/`
- **功能描述**:根据传入的时间类型,获取车体检测的问题类型及对应占比数据,用于展示饼图。
## 2. 请求参数
| 参数名 | 类型 | 是否必填 | 描述 | 示例值 |
|----------|--------|----------|-------------------|----|
| dateType | string | 是 | 时间类型如week或month | week |
| value | int | 是 | 时间值 | 1 |
| type | string | 是 | 固定为appearance | appearance |
## 3. 响应参数
| 参数名 | 类型 | 描述 |
|----------|---------|--------------------------|
| code | number | 状态码,`200`表示成功 |
| message | string | 提示信息(成功或失败描述) |
| data | array | 车体检测问题分布数据数组 |
| ├─ name | string | 问题类型(如搭扣未搭) |
| ├─ value | number | 问题占比数值(非百分制) |
## 4. 响应示例
```json
{
"code": 200,
"success": true,
"data": [
{
"name": "rise",
"value": 1
},
{
"name": "material",
"value": 1
},
{
"name": "indeed",
"value": 1
},
{
"name": "economy",
"value": 1
},
{
"name": "daughter",
"value": 1
}
],
"errorMessage": ""
}
```
# 撑杆检测问题分布接口文档
## 1. 接口基本信息
- **接口名称**:获取撑杆检测问题分布数据
- **请求方法**`GET`
- **接口路径**`/api/v1/system/get_record_fault_stats/`
- **功能描述**:根据传入的时间类型,获取撑杆检测的问题类型及对应占比数据,用于展示饼图。
## 2. 请求参数
| 参数名 | 类型 | 是否必填 | 描述 | 示例值 |
|----------|--------|----------|-------------------|------|
| dateType | string | 是 | 时间类型如week或month | week |
| value | int | 是 | 时间值 | 1 |
| type | string | 是 | 固定为pole | pole |
## 3. 响应参数
| 参数名 | 类型 | 描述 |
|----------|---------|--------------------------|
| code | number | 状态码,`200`表示成功 |
| message | string | 提示信息(成功或失败描述) |
| data | array | 撑杆检测问题分布数据数组 |
| ├─ name | string | 问题类型(如撑杆断折) |
| ├─ value | number | 问题占比数值(非百分制) |
## 4. 响应示例
```json
{
"code": 200,
"success": true,
"data": [
{
"name": "rise",
"value": 1
},
{
"name": "material",
"value": 1
},
{
"name": "indeed",
"value": 1
},
{
"name": "economy",
"value": 1
},
{
"name": "daughter",
"value": 1
}
],
"errorMessage": ""
}
```
![alt text](localFiles/1741680094060.png)
# 实时监控画面接口文档
## 1. 接口基本信息
- **接口名称**:获取实时监控画面
- **请求方法**`GET`
- **接口路径**`/api/v1/record/get_latest_second_records/`
- **功能描述**:获取实时监控画面数据,展示设备状态及问题(如搭扣未搭、撑杆断裂等)。
## 2. 请求参数
| 参数名 | 类型 | 是否必填 | 描述 | 示例值 |
|--------------|--------|----------|--------------------------|--------------|
## 3. 响应参数
| 参数名 | 类型 | 描述 |
|----------------|---------|--------------------------|
| code | number | 状态码,`200`表示成功 |
| message | string | 提示信息(如成功或失败) |
| data | array | 监控画面数据数组 |
| ├─ imageUrl | string | 监控画面图片/视频URL |
| ├─ problemDesc | string | 问题描述(如搭扣未搭) |
## 4. 响应示例
```json
{
"code": 200,
"success": true,
"data": [
{
"url": "http://192.168.10.14:8123/ftp/1.jpg",
"fault_type": "撑杆弯曲"
},
{
"url": "http://192.168.10.14:8123/ftp/1.jpg",
"fault_type": "门折页座脱落"
}
],
"errorMessage": ""
}
```
![alt text](localFiles/1741680112113.png)

71
auto-imports.d.ts vendored

@ -0,0 +1,71 @@
/* eslint-disable */
/* prettier-ignore */
// @ts-nocheck
// noinspection JSUnusedGlobalSymbols
// Generated by unplugin-auto-import
// biome-ignore lint: disable
export {}
declare global {
const EffectScope: typeof import('vue')['EffectScope']
const computed: typeof import('vue')['computed']
const createApp: typeof import('vue')['createApp']
const customRef: typeof import('vue')['customRef']
const defineAsyncComponent: typeof import('vue')['defineAsyncComponent']
const defineComponent: typeof import('vue')['defineComponent']
const effectScope: typeof import('vue')['effectScope']
const getCurrentInstance: typeof import('vue')['getCurrentInstance']
const getCurrentScope: typeof import('vue')['getCurrentScope']
const h: typeof import('vue')['h']
const inject: typeof import('vue')['inject']
const isProxy: typeof import('vue')['isProxy']
const isReactive: typeof import('vue')['isReactive']
const isReadonly: typeof import('vue')['isReadonly']
const isRef: typeof import('vue')['isRef']
const markRaw: typeof import('vue')['markRaw']
const nextTick: typeof import('vue')['nextTick']
const onActivated: typeof import('vue')['onActivated']
const onBeforeMount: typeof import('vue')['onBeforeMount']
const onBeforeUnmount: typeof import('vue')['onBeforeUnmount']
const onBeforeUpdate: typeof import('vue')['onBeforeUpdate']
const onDeactivated: typeof import('vue')['onDeactivated']
const onErrorCaptured: typeof import('vue')['onErrorCaptured']
const onMounted: typeof import('vue')['onMounted']
const onRenderTracked: typeof import('vue')['onRenderTracked']
const onRenderTriggered: typeof import('vue')['onRenderTriggered']
const onScopeDispose: typeof import('vue')['onScopeDispose']
const onServerPrefetch: typeof import('vue')['onServerPrefetch']
const onUnmounted: typeof import('vue')['onUnmounted']
const onUpdated: typeof import('vue')['onUpdated']
const onWatcherCleanup: typeof import('vue')['onWatcherCleanup']
const provide: typeof import('vue')['provide']
const reactive: typeof import('vue')['reactive']
const readonly: typeof import('vue')['readonly']
const ref: typeof import('vue')['ref']
const resolveComponent: typeof import('vue')['resolveComponent']
const shallowReactive: typeof import('vue')['shallowReactive']
const shallowReadonly: typeof import('vue')['shallowReadonly']
const shallowRef: typeof import('vue')['shallowRef']
const toRaw: typeof import('vue')['toRaw']
const toRef: typeof import('vue')['toRef']
const toRefs: typeof import('vue')['toRefs']
const toValue: typeof import('vue')['toValue']
const triggerRef: typeof import('vue')['triggerRef']
const unref: typeof import('vue')['unref']
const useAttrs: typeof import('vue')['useAttrs']
const useCssModule: typeof import('vue')['useCssModule']
const useCssVars: typeof import('vue')['useCssVars']
const useId: typeof import('vue')['useId']
const useModel: typeof import('vue')['useModel']
const useSlots: typeof import('vue')['useSlots']
const useTemplateRef: typeof import('vue')['useTemplateRef']
const watch: typeof import('vue')['watch']
const watchEffect: typeof import('vue')['watchEffect']
const watchPostEffect: typeof import('vue')['watchPostEffect']
const watchSyncEffect: typeof import('vue')['watchSyncEffect']
}
// for type re-export
declare global {
// @ts-ignore
export type { Component, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue'
import('vue')
}

@ -0,0 +1,475 @@
<!--
* @Author: donghao donghao@supervision.ltd
* @Date: 2025-03-11 14:38:18
* @LastEditors: donghao donghao@supervision.ltd
* @LastEditTime: 2025-03-11 15:21:43
* @FilePath: \5G-Loading-Bay-Web\dashboard.md
* @Description: dashboard接口文档
-->
# 仪表盘模块Dashboard接口文档
## 一、设备状态分页列表接口
### 1.1 接口基本信息
- **接口地址**/api/v1/device/device/
- **请求方式**GET
- **功能描述**:获取设备状态分页列表,支持条件查询
### 1.2 请求参数
| 参数名 | 类型 | 是否必填 | 说明 | 示例值 |
|---------------|--------|----------|--------------------------|----------------|
| device_name | string | 否 | 设备名称(模糊查询) | "设备A" |
| device_number | string | 否 | 设备ID精确查询 | "DEV-001" |
| current | int | 是 | 分页页码 | 1 |
| pageSize | int | 是 | 每页数量 | 10 |
### 1.3 响应参数
| 参数名 | 类型 | 说明 |
|---------------------------|----------|----------------------|
| code | int | 响应码200为成功 |
| errorMessage | string | 异常信息 |
| data | object | 数据体 |
| ├─ total | int | 总记录数 |
| ├─ data | array | 设备状态列表 |
| └─ ├─ device_name | string | 设备名称 |
| └─ ├─ device_id | string | 设备ID |
| └─ ├─ device_location | string | 设备位置 |
| └─ ├─ device_status | int | 设备状态0=在线1=离线2=故障) |
| └─ └─ real_time_video_url | string | 实时视频路径 |
### 1.4 响应示例
```json
{
"code": 200,
"success": true,
"data": {
"total": 21,
"data": [
{
"id": 21,
"key": "21",
"device_number": "DEV-020",
"device_name": "Say Device",
"device_position": "Floor 6, Room 101",
"device_status": "online",
"url": "https://www.johnson-malone.com/",
"created_at": "2025-03-12 14:52:21",
"updated_at": "2025-03-12 14:52:21"
},
{
"id": 20,
"key": "20",
"device_number": "DEV-019",
"device_name": "Reality Device",
"device_position": "Floor 10, Room 105",
"device_status": "offline",
"url": "https://www.lewis.com/",
"created_at": "2025-03-12 14:52:21",
"updated_at": "2025-03-12 14:52:21"
}
],
"current": 1,
"success": true,
"pageSize": 2
},
"errorMessage": ""
}
```
![alt text](localFiles/image-1.png)
## 二、设备历史视频列表接口
### 2.1 接口基本信息
- **接口地址**/api/v1/device/device_history/
- **请求方式**GET
- **功能描述**:根据设备 ID 查询历史视频列表,支持日期时间过滤
### 2.2 请求参数
| 参数名 | 类型 | 是否必填 | 说明 | 示例值 |
|------------|--------|----------|-----------------------------|--------------|
| device_id | int | 是 | 设备 ID关联设备详情 | 1 |
| start_time | string | 否 | 开始时间格式YYYY-MM-DD | "2025-03-01" |
| end_time | string | 否 | 结束时间格式YYYY-MM-DD | "2025-03-31" |
### 2.3 响应参数
| 参数名 | 类型 | 说明 |
|--------------|----------|-----------------------------------|
| code | int | 响应码200 为成功) |
| message | string | 响应信息 |
| data | object | 数据体 |
| ├─ total | int | 总记录数 |
| ├─ list | array | 历史视频列表 |
| └─ ├─ video_url | string | 历史视频路径 |
| └─ └─ record_time | string | 视频记录时间格式YYYvY-MM-DD HH:MM:SS |
### 2.4 响应示例
```json
{
"code": 200,
"success": true,
"data": {
"total": 10,
"data": [
{
"id": 10,
"key": "10",
"video_url": "ftp://192.168.10.38/1.mp4",
"created_at": "2025-03-12 14:53:36",
"updated_at": "2025-03-12 14:53:36",
"device": 1
},
{
"id": 9,
"key": "9",
"video_url": "ftp://192.168.10.38/1.mp4",
"created_at": "2025-03-12 14:53:36",
"updated_at": "2025-03-12 14:53:36",
"device": 1
}
],
"current": 1,
"success": true,
"pageSize": 2
},
"errorMessage": ""
}
```
![alt text](localFiles/image-2.png)
# 撑杆监测模块Pole Monitor接口文档
## 一、撑杆监测分页列表接口
### 1.1 接口基本信息
- **接口地址**/api/v1/record/record/
- **请求方式**GET
- **功能描述**:获取撑杆监测分页列表,支持多条件筛选
### 1.2 请求参数
| 参数名 | 类型 | 是否必填 | 说明 | 示例值 |
|-----------------------|---------|----------|--------------------------|--------------|
| train_number | string | 否 | 车号(精确/模糊查询) | "JZ20250301" |
| train_model | string | 否 | 车型 | "货车" |
| train_carriage_number | string | 否 | 车厢号(精确查询) | "C001" |
| alarm_type | string | 否 | 告警类型(如:倾斜、断裂) | "倾斜" |
| fault_type | string | 否 | 故障类型(如:机械故障、电气故障) | "机械故障" |
| level | int | 否 | 等级1-3数值越大越严重 | 2 |
| is_reviewed | boolean | 否 | 复核状态true=已复核false=未复核) | true |
| created_at | string | 否 | 监测日期格式YYYY-MM-DD | "2025-03-10" |
| current | int | 是 | 分页页码 | 1 |
| pageSize | int | 是 | 每页数量 | 10 |
| type | string | 是 | 固定为 pole | pole |
### 1.3 响应参数
| 参数名 | 类型 | 说明 |
|------------------|----------|--------------------------|
| code | int | 响应码200为成功 |
| message | string | 响应信息 |
| data | object | 数据体 |
| ├─ total | int | 总记录数 |
| ├─ list | array | 撑杆监测列表 |
| └─ ├─ id | string | 唯一标识 |
| └─ ├─ carNo | string | 车号 |
| └─ ├─ carType | string | 车型 |
| └─ ├─ carriageNo | string | 车厢号 |
| └─ ├─ warnType | string | 告警类型 |
| └─ ├─ faultType | string | 故障类型 |
| └─ ├─ level | int | 等级1-3 |
| └─ ├─ review | boolean | 复核状态 |
| └─ └─ date | string | 监测时间格式YYYY-MM-DD HH:MM:SS |
### 1.4 响应示例
```json
{
"code": 200,
"success": true,
"data": {
"total": 8,
"data": [
{
"id": 19,
"key": "19",
"train_number": "pS399",
"train_model": "home",
"train_carriage_number": "C16",
"alarm_type": "candidate",
"fault_type": "several",
"level": 2,
"is_reviewed": false,
"created_at": "2025-03-12 14:46:55",
"updated_at": "2025-03-12 14:46:55",
"type": "pole"
},
{
"id": 16,
"key": "16",
"train_number": "vO272",
"train_model": "enough",
"train_carriage_number": "C49",
"alarm_type": "sing",
"fault_type": "scientist",
"level": 5,
"is_reviewed": true,
"created_at": "2025-03-12 14:46:54",
"updated_at": "2025-03-12 14:46:54",
"type": "pole"
}
],
"current": 1,
"success": true,
"pageSize": 2
},
"errorMessage": ""
}
```
![alt text](localFiles/image-3.png)
## 二、撑杆监测详情接口
### 2.1 接口基本信息
- **接口地址**/api/v1/record/record_detail_list/
- **请求方式**GET
- **功能描述**:根据监测数据 ID 获取详细信息,包含图文列表及监测数据
### 2.2 请求参数
| 参数名 | 类型 | 是否必填 | 说明 | 示例值 |
|--------|-----|----------|--------------------------|-----|
| id | int | 是 | 监测数据唯一标识(关联分页列表 ID | 1 |
| current | int | 是 | 分页页码 | 1 |
| pageSize | int | 是 | 每页数量 | 10 |
### 2.3 响应参数
| 参数名 | 类型 | 说明 |
|----------------------|----------|--------------------------|
| code | int | 响应码200为成功 |
| message | string | 响应信息 |
| data | object | 数据体 |
| ├─ id | string | 监测数据 ID |
| ├─ carNo | string | 车号 |
| ├─ carType | string | 车型 |
| ├─ carriageNo | string | 车厢号 |
| ├─ warnType | string | 告警类型 |
| ├─ faultType | string | 故障类型 |
| ├─ level | int | 等级1-3 |
| ├─ review | boolean | 复核状态 |
| ├─ date | string | 监测时间格式YYYY-MM-DD HH:MM:SS |
| ├─ image_list | array | 监测图文列表 |
| └─ ├─ image_name | string | 图片名称 |
| └─ ├─ image_url | string | 图片路径 |
| └─ ├─ capture_time | string | 图片拍摄时间格式YYYY-MM-DD HH:MM:SS |
| └─ ├─ length | float | 撑杆长度(单位:米) |
| └─ ├─ width | float | 撑杆宽度(单位:米) |
| └─ ├─ height | float | 撑杆高度(单位:米) |
| └─ └─ weight | float | 撑杆重量(单位:千克) |
### 2.4 响应示例
```json
{
"code": 200,
"success": true,
"data": {
"total": 10,
"data": [
{
"id": 1,
"key": "1",
"name": "Christopher Lynch",
"video_url": "ftp://192.168.10.38/1.mp4",
"image_url": "https://picsum.photos/963/650",
"created_at": "2025-03-12 14:51:12",
"updated_at": "2025-03-12 14:51:12",
"length": 6.08,
"width": 0.81,
"height": 0.67,
"weight": 14.14,
"record": 1
},
{
"id": 2,
"key": "2",
"name": "Jon Williams",
"video_url": "ftp://192.168.10.38/1.mp4",
"image_url": "https://placekitten.com/603/345",
"created_at": "2025-03-12 14:51:12",
"updated_at": "2025-03-12 14:51:12",
"length": 6.57,
"width": 1.63,
"height": 2.04,
"weight": 26.9,
"record": 1
}
],
"current": 1,
"success": true,
"pageSize": 2
},
"errorMessage": ""
}
```
![alt text](localFiles/image-4.png)
# 外观监测模块Appearance Monitor接口文档
## 一、外观监测分页列表接口
### 1.1 接口基本信息
- **接口地址**/api/v1/record/record/
- **请求方式**GET
- **功能描述**:获取外观监测分页列表,支持多条件筛选
### 1.2 请求参数
| 参数名 | 类型 | 是否必填 | 说明 | 示例值 |
|-----------------------|---------|----------|--------------------------|--------------|
| train_number | string | 否 | 车号(精确/模糊查询) | "JZ20250301" |
| train_model | string | 否 | 车型 | "货车" |
| train_carriage_number | string | 否 | 车厢号(精确查询) | "C001" |
| alarm_type | string | 否 | 告警类型(如:倾斜、断裂) | "倾斜" |
| fault_type | string | 否 | 故障类型(如:机械故障、电气故障) | "机械故障" |
| level | int | 否 | 等级1-3数值越大越严重 | 2 |
| is_reviewed | boolean | 否 | 复核状态true=已复核false=未复核) | true |
| created_at | string | 否 | 监测日期格式YYYY-MM-DD | "2025-03-10" |
| current | int | 是 | 分页页码 | 1 |
| pageSize | int | 是 | 每页数量 | 10 |
| type | string | 是 | 固定为 appearance | appearance |
### 1.3 响应参数
| 参数名 | 类型 | 说明 |
|------------------|----------|--------------------------|
| code | int | 响应码200为成功 |
| message | string | 响应信息 |
| data | object | 数据体 |
| ├─ total | int | 总记录数 |
| ├─ list | array | 外观监测列表 |
| └─ ├─ id | string | 唯一标识 |
| └─ ├─ carNo | string | 车号 |
| └─ ├─ carType | string | 车型 |
| └─ ├─ carriageNo | string | 车厢号 |
| └─ ├─ warnType | string | 告警类型 |
| └─ ├─ faultType | string | 故障类型 |
| └─ ├─ level | int | 等级1-3 |
| └─ ├─ review | boolean | 复核状态 |
| └─ └─ date | string | 监测时间格式YYYY-MM-DD HH:MM:SS |
### 1.4 响应示例
```json
{
"code": 200,
"success": true,
"data": {
"total": 5,
"data": [
{
"id": 17,
"key": "17",
"train_number": "zE074",
"train_model": "create",
"train_carriage_number": "C28",
"alarm_type": "decision",
"fault_type": "material",
"level": 4,
"is_reviewed": false,
"created_at": "2025-03-12 14:46:55",
"updated_at": "2025-03-12 14:46:55",
"type": "appearance"
},
{
"id": 14,
"key": "14",
"train_number": "Bp417",
"train_model": "ground",
"train_carriage_number": "C43",
"alarm_type": "market",
"fault_type": "rise",
"level": 2,
"is_reviewed": true,
"created_at": "2025-03-12 14:46:54",
"updated_at": "2025-03-12 14:46:54",
"type": "appearance"
}
],
"current": 1,
"success": true,
"pageSize": 2
},
"errorMessage": ""
}
```
![alt text](localFiles/image-5.png)
# 外观监测模块Appearance Monitor接口文档
## 一、外观监测详情接口
### 1.1 接口基本信息
- **接口地址**/api/v1/record/record_detail_list/
- **请求方式**GET
- **功能描述**:根据监测数据 ID 获取详细信息,包含视频列表及监测数据
### 1.2 请求参数
| 参数名 | 类型 | 是否必填 | 说明 | 示例值 |
|--------|-----|----------|--------------------------|-----|
| id | int | 是 | 监测数据唯一标识(关联列表 ID | 1 |
| current | int | 是 | 分页页码 | 1 |
| pageSize | int | 是 | 每页数量 | 10 |
### 1.3 响应参数
| 参数名 | 类型 | 说明 |
|----------------------|----------|--------------------------|
| code | int | 响应码200为成功 |
| message | string | 响应信息 |
| data | object | 数据体 |
| ├─ id | string | 监测数据 ID |
| ├─ carNo | string | 车号 |
| ├─ carType | string | 车型 |
| ├─ carriageNo | string | 车厢号 |
| ├─ warnType | string | 告警类型 |
| ├─ faultType | string | 故障类型 |
| ├─ level | int | 等级1-3 |
| ├─ review | boolean | 复核状态 |
| ├─ date | string | 监测时间格式YYYY-MM-DD HH:MM:SS |
| ├─ video_list | array | 监测视频列表 |
| └─ ├─ video_name | string | 视频名称 |
| └─ ├─ video_url | string | 视频路径 |
| └─ ├─ capture_time | string | 视频拍摄时间格式YYYY-MM-DD HH:MM:SS |
| └─ ├─ length | float | 外观尺寸长度(单位:米) |
| └─ ├─ width | float | 外观尺寸宽度(单位:米) |
| └─ ├─ height | float | 外观尺寸高度(单位:米) |
| └─ ├─ volume | float | 体积(单位:立方米) |
| └─ └─ weight | float | 重量(单位:千克) |
### 1.4 响应示例
```json
{
"code": 200,
"success": true,
"data": {
"total": 10,
"data": [
{
"id": 1,
"key": "1",
"name": "Christopher Lynch",
"video_url": "ftp://192.168.10.38/1.mp4",
"image_url": "https://picsum.photos/963/650",
"created_at": "2025-03-12 14:51:12",
"updated_at": "2025-03-12 14:51:12",
"length": 6.08,
"width": 0.81,
"height": 0.67,
"weight": 14.14,
"record": 1
},
{
"id": 2,
"key": "2",
"name": "Jon Williams",
"video_url": "ftp://192.168.10.38/1.mp4",
"image_url": "https://placekitten.com/603/345",
"created_at": "2025-03-12 14:51:12",
"updated_at": "2025-03-12 14:51:12",
"length": 6.57,
"width": 1.63,
"height": 2.04,
"weight": 26.9,
"record": 1
}
],
"current": 1,
"success": true,
"pageSize": 2
},
"errorMessage": ""
}
```
![alt text](localFiles/image-6.png)

@ -0,0 +1,3 @@
VITE_PORT=5050
VITE_API_BASE=/api
VITE_MOCK_ENABLED=true

@ -0,0 +1,10 @@
###
# @Author: donghao donghao@supervision.ltd
# @Date: 2025-03-06 11:36:56
# @LastEditors: donghao donghao@supervision.ltd
# @LastEditTime: 2025-03-06 11:37:31
# @FilePath: \vite-ai\data-dashboard\env\.env.production
# @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
###
VITE_API_BASE=http://test-api.example.com
VITE_MOCK_ENABLED=false

10
env/.env.test vendored

@ -0,0 +1,10 @@
###
# @Author: donghao donghao@supervision.ltd
# @Date: 2025-03-06 11:36:48
# @LastEditors: donghao donghao@supervision.ltd
# @LastEditTime: 2025-03-06 11:37:50
# @FilePath: \vite-ai\data-dashboard\env\.env.test
# @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
###
VITE_API_BASE=http://api.example.com
VITE_MOCK_ENABLED=false

@ -0,0 +1,24 @@
<!--
* @Author: donghao donghao@supervision.ltd
* @Date: 2025-03-06 17:57:05
* @LastEditors: donghao donghao@supervision.ltd
* @LastEditTime: 2025-03-18 11:08:34
* @FilePath: \5G-Loading-Bay-Web\index.html
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
-->
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>监控平台</title>
</head>
<body>
<script type="module" src="/adapter.min.js"></script>
<script type="module" src="/webrtcstreamer.js"></script>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 551 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 478 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 591 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 699 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 991 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 398 KiB

@ -0,0 +1,40 @@
/*
* @Author: donghao donghao@supervision.ltd
* @Date: 2025-03-07 14:57:20
* @LastEditors: donghao donghao@supervision.ltd
* @LastEditTime: 2025-03-14 15:11:04
* @FilePath: \5G-Loading-Bay-Web\mock\deviceStatus.ts
* @Description:
*/
import { MockMethod } from "vite-plugin-mock";
import { deviceStatusListData, deviceHistoryListData } from "./pools/deviceStatusData";
import { fetchCurrPageByList, fetchMockSuccessFullByOther } from "./utils/apiMock";
export default [
{
url: "/api/v1/device/device/",
method: "post",
response: req => {
const { page, pageSize } = req.body;
// console.log(req);
return {
...fetchCurrPageByList({
...deviceStatusListData,
data: {
...deviceStatusListData.data,
page,
pageSize: pageSize || 10
}
})
};
}
},
{
url: "/api/v1/device/device_history/",
method: "post",
response: req => {
// console.log(req);
return {...fetchMockSuccessFullByOther(deviceHistoryListData)}
}
}
] as MockMethod[];

@ -0,0 +1,40 @@
/*
* @Author: donghao donghao@supervision.ltd
* @Date: 2025-03-11 11:29:02
* @LastEditors: donghao donghao@supervision.ltd
* @LastEditTime: 2025-03-14 15:10:47
* @FilePath: \5G-Loading-Bay-Web\mock\poleMonitor.ts
* @Description:
*/
import { MockMethod } from "vite-plugin-mock";
import { poleMonitorListData, fileListData } from "./pools/poleMonitorData";
import { fetchCurrPageByList, fetchMockSuccessFullByOther } from "./utils/apiMock";
export default [
{
url: "/api/getPoleMonitorList",
method: "post",
response: req => {
const { page, pageSize } = req.body;
// console.log(req);
return {
...fetchCurrPageByList({
...poleMonitorListData,
data: {
...poleMonitorListData.data,
page,
pageSize: pageSize || 10
}
})
};
}
},
{
url: "/api/v1/record/record_detail_list/",
method: "post",
response: req => {
// console.log(req);
return {...fetchMockSuccessFullByOther(fileListData)}
}
}
] as MockMethod[];

@ -0,0 +1 @@
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFtdLAogICJzb3VyY2VzQ29udGVudCI6IFtdLAogICJtYXBwaW5ncyI6ICIiLAogICJuYW1lcyI6IFtdCn0K

@ -0,0 +1,115 @@
/*
* @Author: donghao donghao@supervision.ltd
* @Date: 2024-02-22 13:38:04
* @LastEditors: donghao donghao@supervision.ltd
* @LastEditTime: 2025-03-14 14:03:18
* @FilePath: \General-AI-Platform-Web-Client\mock\pools\deviceStatusData.ts
* @Description:
*/
import { generateRandomDateTimeByYear } from "../utils/mockMoment";
import Mock from "mockjs";
// 定义一些公开的可测试的视频链接
const videoUrls = [
"https://www.learningcontainer.com/wp-content/uploads/2020/05/sample-mp4-file.mp4",
"https://www.sample-videos.com/video123/mp4/1080/big_buck_bunny_1080p_100mb.mp4",
"https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4",
"https://www.sample-videos.com/video123/mp4/360/big_buck_bunny_360p_5mb.mp4",
"https://media.w3.org/2010/05/video/movie_300.mp4",
"https://www.w3schools.com/html/mov_bbb.mp4",
"https://media.w3.org/2010/05/sintel/trailer.mp4",
"https://test-videos.co.uk/vids/bigbuckbunny/mp4/h264/360/Big_Buck_Bunny_360_10s_1MB.mp4",
"https://archive.org/download/Popeye_forPresident/Popeye_forPresident_512kb.mp4",
"https://archive.org/download/Sita_Sings_the_Blues/Sita_Sings_the_Blues_small.mp4",
];
function fetchList(): Record<string, any>[] {
const currList: Record<string, any>[] = [];
const nameArr = [
"摄像模组表面缺陷检测设备",
"镜片表面缺陷检测设备",
"中板表面缺陷检测设备",
"Logo表面缺陷检测设备",
"手机电池表面缺陷检测设备",
"部件表面缺陷检测设备",
"边距缺陷检测设备",
"成品组装缺陷检测设备",
"金属工件表面缺陷检测设备",
"管材表面缺陷检测设备",
];
const codeArr = ["MSRF", "RL0F", "TLOC", "E1AIS", "CRM"];
const deviceGroupArr = ["立杆", "东西货区", "送料区"];
for (let i = 0; i < 35; i++) {
currList.push({
id: i,
createTime: generateRandomDateTimeByYear(2023),
updateTime: "2023-10-17T02:35:41.14308Z",
name: nameArr[Math.floor(Math.random() * 3)],
code: codeArr[Math.floor(Math.random() * 4)] + "-" + i,
deviceGroup:
deviceGroupArr[Math.floor(Math.random() * 3)] +
(Math.floor(Math.random() * 3) + 1),
status: Math.floor(Math.random() * 3) + 1,
remark: "",
});
}
return currList;
}
// 缺陷从EXL里选几个就好了
// 告警代码MSRF-0 RL0F HTFIF-02 TLOC-1 E1AIS-05
// 设备组核心检测组001 无尘总装组005 送料监测线02
const mockHistroyData = Mock.mock({
[`list|${videoUrls.length}`]: [
{
"id|+1": 10,
key: function () {
return this.id.toString();
},
video_url: function () {
// 依次取出视频链接
return videoUrls[this.id - 10];
},
created_at: function () {
// 定义起始和结束时间戳
const startDate = new Date("2025-01-01").getTime();
const endDate = new Date("2025-03-14 23:59:59").getTime();
// 生成随机时间戳
const randomTimestamp =
startDate + Math.random() * (endDate - startDate);
// 根据随机时间戳创建日期对象
const randomDate = new Date(randomTimestamp);
// 格式化日期为 yyyy-MM-dd HH:mm:ss 格式
const year = randomDate.getFullYear();
const month = String(randomDate.getMonth() + 1).padStart(2, "0");
const day = String(randomDate.getDate()).padStart(2, "0");
const hours = String(randomDate.getHours()).padStart(2, "0");
const minutes = String(randomDate.getMinutes()).padStart(2, "0");
const seconds = String(randomDate.getSeconds()).padStart(2, "0");
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
},
device: 1,
}
],
});
const currentData = fetchList();
const currentHistroyData = mockHistroyData.list;
//
export const deviceStatusListData = {
data: {
data: currentData,
total: currentData.length,
current: 1,
pageSize: 10,
},
};
export const deviceHistoryListData = {
data: currentHistroyData,
};

@ -0,0 +1,102 @@
/*
* @Author: donghao donghao@supervision.ltd
* @Date: 2025-03-11 11:30:09
* @LastEditors: donghao donghao@supervision.ltd
* @LastEditTime: 2025-03-17 15:24:42
* @FilePath: \5G-Loading-Bay-Web\mock\pools\poleMonitorData.ts
* @Description: ,`customMade`, koroFileHeader : https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
import Mock from "mockjs";
import { isImage } from "../utils/is";
const videoUrls = [
"https://www.learningcontainer.com/wp-content/uploads/2020/05/sample-mp4-file.mp4",
"http://192.168.10.14:8123/ftp/1.jpg",
"https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4",
"https://www.sample-videos.com/video123/mp4/360/big_buck_bunny_360p_5mb.mp4",
"https://media.w3.org/2010/05/video/movie_300.mp4",
"https://www.w3schools.com/html/mov_bbb.mp4",
"https://media.w3.org/2010/05/sintel/trailer.mp4",
"https://test-videos.co.uk/vids/bigbuckbunny/mp4/h264/360/Big_Buck_Bunny_360_10s_1MB.mp4",
"https://archive.org/download/Popeye_forPresident/Popeye_forPresident_512kb.mp4",
"https://archive.org/download/Sita_Sings_the_Blues/Sita_Sings_the_Blues_small.mp4",
];
const mockListData = Mock.mock({
// 生成 10 条数据,可以根据需要调整数量
"data|140": [
{
// 车号,生成随机的 4 位字母和数字组合
train_number: /[A-Z0-9]{10}/,
// 车型,从预定义的数组中随机选择一个
train_model: () => Mock.Random.pick(["轿车", "SUV", "客车", "货车"]),
// 车厢号,生成 1 到 10 的随机整数
"train_carriage_number|1-10": 1,
// 告警类型,从预定义的数组中随机选择一个
alarm_type: () =>
Mock.Random.pick(["超速告警", "碰撞告警", "低电量告警"]),
// 故障类型,从预定义的数组中随机选择一个
faultType: () => Mock.Random.pick(["撑杆弯曲", "撑杆断折"]),
// 等级,生成 1 到 3 的随机整数
"level|1-3": 1,
// 复核,随机生成 '是' 或 '否'
is_reviewed: () => Mock.Random.pick([true, false]),
// 时间,生成过去一个月内的随机日期和时间
created_at: () =>
Mock.Random.date("yyyy-MM-dd") + " " + Mock.Random.time("HH:mm:ss"),
},
],
});
const mockFilesData = Mock.mock({
[`list|${videoUrls.length}`]: [
{
"id|+1": 10,
key: "@id",
name: "@animal",
video_url: function () {
// 依次取出视频链接
const currFile = videoUrls[this.id - 10];
if (!isImage(currFile)) {
return videoUrls[this.id - 10];
}
return null;
},
image_url: function () {
// 依次取出视频链接
const currFile = videoUrls[this.id - 10];
if (isImage(currFile)) {
return videoUrls[this.id - 10];
}
return null;
},
created_at: '@datetime("yyyy-MM-dd HH:mm:ss")',
updated_at: '@datetime("yyyy-MM-dd HH:mm:ss")',
length: "@float(0.1, 10, 2, 2)",
width: "@float(0.1, 10, 2, 2)",
height: "@float(0.1, 10, 2, 2)",
weight: "@float(0.1, 1000, 1, 2)",
volume: function () {
return (this.length * this.width * this.height).toFixed(2);
},
record: 1,
},
],
});
// console.log(mockListData, 'mockListData');
const currentData = mockListData.data;
const currentFilesData = mockFilesData.list;
export const poleMonitorListData = {
data: {
list: currentData,
total: currentData.length,
page: 1,
pageSize: 10,
},
};
export const fileListData = {
data: {
data: currentFilesData,
},
};

@ -0,0 +1,17 @@
/**成功返回数据结构 */
export interface successMockApiProps {
code: number; // 0 成功
success: boolean; // true 成功
data: any; // mock业务层数据
msg: string | undefined; // 成功提示
isMock: boolean; // true 标识当前是模拟数据
}
/**失败返回数据结构 */
export interface failMockApiProps {
code?: number; // 7 失败
success: boolean; // false 失败
data: any; // mock业务层数据
msg: string | undefined; // 成功提示
isMock: boolean; // true 标识当前是模拟数据
}

@ -0,0 +1,28 @@
/*
* @Author: donghao donghao@supervision.ltd
* @Date: 2025-03-06 11:37:14
* @LastEditors: donghao donghao@supervision.ltd
* @LastEditTime: 2025-03-06 11:38:11
* @FilePath: \vite-ai\data-dashboard\mock\user.ts
* @Description:
*/
export default [
{
url: '/api/login',
method: 'post',
response: ({ body }) => {
if (body.username === 'admin' && body.password === 'admin123') {
return {
code: 200,
data: {
token: 'MOCK_TOKEN_' + Date.now()
}
}
}
return {
code: 401,
message: '用户名或密码错误'
}
}
}
]

@ -0,0 +1,49 @@
// mock/utils/apiMock.ts
function fetchMockSuccessFullByOther({
data,
msg
}) {
const result = {
code: 200,
// 200 成功
success: true,
// true 成功
data: data || null,
// mock业务层数据
msg,
// 成功提示
isMock: true
// true 标识当前是模拟数据
};
return result;
}
function fetchMockFailFullByOther({ data, msg }) {
const result = {
code: 599,
// 200 成功
success: false,
// true 成功
data: data || null,
// mock业务层数据
msg,
// 成功提示
isMock: true
// true 标识当前是模拟数据
};
return result;
}
function fetchCurrPageByList({ data }) {
const { current, pageSize } = data;
const prevPage = current - 1;
const currPageData = {
...data,
data: data.data.slice(prevPage * pageSize, current * pageSize)
};
return fetchMockSuccessFullByOther({ data: currPageData });
}
export {
fetchCurrPageByList,
fetchMockFailFullByOther,
fetchMockSuccessFullByOther
};
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsibW9jay91dGlscy9hcGlNb2NrLnRzIl0sCiAgInNvdXJjZXNDb250ZW50IjogWyJjb25zdCBfX2luamVjdGVkX2ZpbGVuYW1lX18gPSBcIkU6XFxcXHlheGluX3dlYlxcXFw1Ry1Mb2FkaW5nLUJheS1XZWJcXFxcbW9ja1xcXFx1dGlsc1xcXFxhcGlNb2NrLnRzXCI7Y29uc3QgX19pbmplY3RlZF9kaXJuYW1lX18gPSBcIkU6XFxcXHlheGluX3dlYlxcXFw1Ry1Mb2FkaW5nLUJheS1XZWJcXFxcbW9ja1xcXFx1dGlsc1wiO2NvbnN0IF9faW5qZWN0ZWRfaW1wb3J0X21ldGFfdXJsX18gPSBcImZpbGU6Ly8vRToveWF4aW5fd2ViLzVHLUxvYWRpbmctQmF5LVdlYi9tb2NrL3V0aWxzL2FwaU1vY2sudHNcIjsvKlxyXG4gKiBAQXV0aG9yOiBkb25naGFvIGRvbmdoYW9Ac3VwZXJ2aXNpb24ubHRkXHJcbiAqIEBEYXRlOiAyMDI1LTAzLTA3IDE0OjU4OjM5XHJcbiAqIEBMYXN0RWRpdG9yczogZG9uZ2hhbyBkb25naGFvQHN1cGVydmlzaW9uLmx0ZFxyXG4gKiBATGFzdEVkaXRUaW1lOiAyMDI1LTAzLTEzIDE0OjI5OjI2XHJcbiAqIEBGaWxlUGF0aDogXFw1Ry1Mb2FkaW5nLUJheS1XZWJcXG1vY2tcXHV0aWxzXFxhcGlNb2NrLnRzXHJcbiAqIEBEZXNjcmlwdGlvbjogXHU4RkQ5XHU2NjJGXHU5RUQ4XHU4QkE0XHU4QkJFXHU3RjZFLFx1OEJGN1x1OEJCRVx1N0Y2RWBjdXN0b21NYWRlYCwgXHU2MjUzXHU1RjAwa29yb0ZpbGVIZWFkZXJcdTY3RTVcdTc3MEJcdTkxNERcdTdGNkUgXHU4RkRCXHU4ODRDXHU4QkJFXHU3RjZFOiBodHRwczovL2dpdGh1Yi5jb20vT0JLb3JvMS9rb3JvMUZpbGVIZWFkZXIvd2lraS8lRTklODUlOEQlRTclQkQlQUVcclxuICovXHJcbmltcG9ydCB7IGZhaWxNb2NrQXBpUHJvcHMsIHN1Y2Nlc3NNb2NrQXBpUHJvcHMgfSBmcm9tIFwiLi4vdHlwaW5nXCI7XHJcbmV4cG9ydCBmdW5jdGlvbiBmZXRjaE1vY2tTdWNjZXNzRnVsbEJ5T3RoZXIoe1xyXG4gIGRhdGEsXHJcbiAgbXNnLFxyXG59KTogc3VjY2Vzc01vY2tBcGlQcm9wcyB7XHJcbiAgLy8gcmV0dXJuIHtcclxuICAvLyAgIGNvZGU6IDIwMCwgLy8gMjAwIFx1NjIxMFx1NTI5RlxyXG4gIC8vICAgc3VjY2VzczogdHJ1ZSwgLy8gdHJ1ZSBcdTYyMTBcdTUyOUZcclxuICAvLyAgIGRhdGE6IGRhdGEgfHwgbnVsbCwgLy8gbW9ja1x1NEUxQVx1NTJBMVx1NUM0Mlx1NjU3MFx1NjM2RVxyXG4gIC8vICAgbXNnOiBtc2cgfCBcIm9rXCIsIC8vIFx1NjIxMFx1NTI5Rlx1NjNEMFx1NzkzQVxyXG4gIC8vICAgaXNNb2NrOiB0cnVlIC8vIHRydWUgXHU2ODA3XHU4QkM2XHU1RjUzXHU1MjREXHU2NjJGXHU2QTIxXHU2MkRGXHU2NTcwXHU2MzZFXHJcbiAgLy8gfSBhcyBzdWNjZXNzTW9ja0FwaVByb3BzO1xyXG4gIGNvbnN0IHJlc3VsdDogc3VjY2Vzc01vY2tBcGlQcm9wcyA9IHtcclxuICAgIGNvZGU6IDIwMCwgLy8gMjAwIFx1NjIxMFx1NTI5RlxyXG4gICAgc3VjY2VzczogdHJ1ZSwgLy8gdHJ1ZSBcdTYyMTBcdTUyOUZcclxuICAgIGRhdGE6IGRhdGEgfHwgbnVsbCwgLy8gbW9ja1x1NEUxQVx1NTJBMVx1NUM0Mlx1NjU3MFx1NjM2RVxyXG4gICAgbXNnOiBtc2cgYXMgc3RyaW5nIHwgXCJva1wiLCAvLyBcdTYyMTBcdTUyOUZcdTYzRDBcdTc5M0FcclxuICAgIGlzTW9jazogdHJ1ZSwgLy8gdHJ1ZSBcdTY4MDdcdThCQzZcdTVGNTNcdTUyNERcdTY2MkZcdTZBMjFcdTYyREZcdTY1NzBcdTYzNkVcclxuICB9O1xyXG4gIHJldHVybiByZXN1bHQ7XHJcbn1cclxuXHJcbmV4cG9ydCBmdW5jdGlvbiBmZXRjaE1vY2tGYWlsRnVsbEJ5T3RoZXIoeyBkYXRhLCBtc2cgfSk6IGZhaWxNb2NrQXBpUHJvcHMge1xyXG4gIC8vIHJldHVybiB7XHJcbiAgLy8gICBjb2RlOiA1OTksIC8vIDIwMCBcdTYyMTBcdTUyOUZcclxuICAvLyAgIHN1Y2Nlc3M6IHRydWUsIC8vIHRydWUgXHU2MjEwXHU1MjlGXHJcbiAgLy8gICBkYXRhOiBkYXRhIHx8IG51bGwsIC8vIG1vY2tcdTRFMUFcdTUyQTFcdTVDNDJcdTY1NzBcdTYzNkVcclxuICAvLyAgIG1zZzogbXNnIHwgXCJmYWlsXCIsIC8vIFx1NjIxMFx1NTI5Rlx1NjNEMFx1NzkzQVxyXG4gIC8vICAgaXNNb2NrOiB0cnVlIC8vIHRydWUgXHU2ODA3XHU4QkM2XHU1RjUzXHU1MjREXHU2NjJGXHU2QTIxXHU2MkRGXHU2NTcwXHU2MzZFXHJcbiAgLy8gfSBhcyBmYWlsTW9ja0FwaVByb3BzO1xyXG4gIGNvbnN0IHJlc3VsdDogZmFpbE1vY2tBcGlQcm9wcyA9IHtcclxuICAgIGNvZGU6IDU5OSwgLy8gMjAwIFx1NjIxMFx1NTI5RlxyXG4gICAgc3VjY2VzczogZmFsc2UsIC8vIHRydWUgXHU2MjEwXHU1MjlGXHJcbiAgICBkYXRhOiBkYXRhIHx8IG51bGwsIC8vIG1vY2tcdTRFMUFcdTUyQTFcdTVDNDJcdTY1NzBcdTYzNkVcclxuICAgIG1zZzogbXNnIGFzIHN0cmluZyB8IFwiZmFpbFwiLCAvLyBcdTYyMTBcdTUyOUZcdTYzRDBcdTc5M0FcclxuICAgIGlzTW9jazogdHJ1ZSwgLy8gdHJ1ZSBcdTY4MDdcdThCQzZcdTVGNTNcdTUyNERcdTY2MkZcdTZBMjFcdTYyREZcdTY1NzBcdTYzNkVcclxuICB9O1xyXG4gIHJldHVybiByZXN1bHQ7XHJcbn1cclxuXHJcbi8vIFx1NTIwNlx1OTg3NVx1NUM1NVx1NzkzQVxyXG5leHBvcnQgZnVuY3Rpb24gZmV0Y2hDdXJyUGFnZUJ5TGlzdCh7IGRhdGEgfSk6IHN1Y2Nlc3NNb2NrQXBpUHJvcHMge1xyXG4gIC8vIGNvbnNvbGUubG9nKFwiZmV0Y2hDdXJyUGFnZUJ5TGlzdF9kYXRhXCIsIGRhdGEpO1xyXG4gIGNvbnN0IHsgY3VycmVudCwgcGFnZVNpemUgfSA9IGRhdGE7XHJcbiAgY29uc3QgcHJldlBhZ2UgPSBjdXJyZW50IC0gMTtcclxuICBjb25zdCBjdXJyUGFnZURhdGEgPSB7XHJcbiAgICAuLi5kYXRhLFxyXG4gICAgZGF0YTogZGF0YS5kYXRhLnNsaWNlKHByZXZQYWdlICogcGFnZVNpemUsIGN1cnJlbnQgKiBwYWdlU2l6ZSksXHJcbiAgfTtcclxuICByZXR1cm4gZmV0Y2hNb2NrU3VjY2Vzc0Z1bGxCeU90aGVyKHsgZGF0YTogY3VyclBhZ2VEYXRhIH0pO1xyXG59XHJcbiJdLAogICJtYXBwaW5ncyI6ICI7QUFTTyxTQUFTLDRCQUE0QjtBQUFBLEVBQzFDO0FBQUEsRUFDQTtBQUNGLEdBQXdCO0FBUXRCLFFBQU0sU0FBOEI7QUFBQSxJQUNsQyxNQUFNO0FBQUE7QUFBQSxJQUNOLFNBQVM7QUFBQTtBQUFBLElBQ1QsTUFBTSxRQUFRO0FBQUE7QUFBQSxJQUNkO0FBQUE7QUFBQSxJQUNBLFFBQVE7QUFBQTtBQUFBLEVBQ1Y7QUFDQSxTQUFPO0FBQ1Q7QUFFTyxTQUFTLHlCQUF5QixFQUFFLE1BQU0sSUFBSSxHQUFxQjtBQVF4RSxRQUFNLFNBQTJCO0FBQUEsSUFDL0IsTUFBTTtBQUFBO0FBQUEsSUFDTixTQUFTO0FBQUE7QUFBQSxJQUNULE1BQU0sUUFBUTtBQUFBO0FBQUEsSUFDZDtBQUFBO0FBQUEsSUFDQSxRQUFRO0FBQUE7QUFBQSxFQUNWO0FBQ0EsU0FBTztBQUNUO0FBR08sU0FBUyxvQkFBb0IsRUFBRSxLQUFLLEdBQXdCO0FBRWpFLFFBQU0sRUFBRSxTQUFTLFNBQVMsSUFBSTtBQUM5QixRQUFNLFdBQVcsVUFBVTtBQUMzQixRQUFNLGVBQWU7QUFBQSxJQUNuQixHQUFHO0FBQUEsSUFDSCxNQUFNLEtBQUssS0FBSyxNQUFNLFdBQVcsVUFBVSxVQUFVLFFBQVE7QUFBQSxFQUMvRDtBQUNBLFNBQU8sNEJBQTRCLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFDM0Q7IiwKICAibmFtZXMiOiBbXQp9Cg==

@ -0,0 +1,27 @@
// mock/utils/is.ts
var IMAGE_EXTENSIONS = /* @__PURE__ */ new Set([
"jpg",
"jpeg",
"png",
"gif",
"bmp",
"svg",
"webp",
"tiff",
"psd",
"ico",
"jfif",
"apng",
"avif"
]);
function isImage(filename) {
const baseName = filename.split("/").pop().split("\\").pop();
if (!baseName) return false;
const ext = baseName.split(".").pop()?.toLowerCase();
if (!ext || ext.length < 2) return false;
return IMAGE_EXTENSIONS.has(ext);
}
export {
isImage
};
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsibW9jay91dGlscy9pcy50cyJdLAogICJzb3VyY2VzQ29udGVudCI6IFsiY29uc3QgX19pbmplY3RlZF9maWxlbmFtZV9fID0gXCJFOlxcXFx5YXhpbl93ZWJcXFxcNUctTG9hZGluZy1CYXktV2ViXFxcXG1vY2tcXFxcdXRpbHNcXFxcaXMudHNcIjtjb25zdCBfX2luamVjdGVkX2Rpcm5hbWVfXyA9IFwiRTpcXFxceWF4aW5fd2ViXFxcXDVHLUxvYWRpbmctQmF5LVdlYlxcXFxtb2NrXFxcXHV0aWxzXCI7Y29uc3QgX19pbmplY3RlZF9pbXBvcnRfbWV0YV91cmxfXyA9IFwiZmlsZTovLy9FOi95YXhpbl93ZWIvNUctTG9hZGluZy1CYXktV2ViL21vY2svdXRpbHMvaXMudHNcIjsvKipcclxuICogXHU1MjI0XHU2NUFEXHU2NTg3XHU0RUY2XHU1NDBEXHU2NjJGXHU1NDI2XHU0RTNBXHU1NkZFXHU3MjQ3XHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBmaWxlbmFtZSBcdTVCOENcdTY1NzRcdTY1ODdcdTRFRjZcdTU0MERcdUZGMDhcdTU0MkJcdThERUZcdTVGODRcdTU0OENcdTYyNjlcdTVDNTVcdTU0MERcdUZGMDlcclxuICogQHJldHVybnMge2Jvb2xlYW59IFx1NjYyRlx1NTQyNlx1NEUzQVx1NTZGRVx1NzI0N1xyXG4gKi9cclxuXHJcbmNvbnN0IElNQUdFX0VYVEVOU0lPTlMgPSBuZXcgU2V0KFtcclxuICBcImpwZ1wiLFxyXG4gIFwianBlZ1wiLFxyXG4gIFwicG5nXCIsXHJcbiAgXCJnaWZcIixcclxuICBcImJtcFwiLFxyXG4gIFwic3ZnXCIsXHJcbiAgXCJ3ZWJwXCIsXHJcbiAgXCJ0aWZmXCIsXHJcbiAgXCJwc2RcIixcclxuICBcImljb1wiLFxyXG4gIFwiamZpZlwiLFxyXG4gIFwiYXBuZ1wiLFxyXG4gIFwiYXZpZlwiLFxyXG5dKTtcclxuZXhwb3J0IGZ1bmN0aW9uIGlzSW1hZ2UoZmlsZW5hbWUpIHtcclxuICAvLyAxLiBcdTUzQkJcdTk2NjRcdThERUZcdTVGODRcdUZGMENcdTUzRUFcdTRGRERcdTc1NTlcdTY1ODdcdTRFRjZcdTU0MERcclxuICBjb25zdCBiYXNlTmFtZSA9IGZpbGVuYW1lLnNwbGl0KFwiL1wiKS5wb3AoKS5zcGxpdChcIlxcXFxcIikucG9wKCk7XHJcbiAgaWYgKCFiYXNlTmFtZSkgcmV0dXJuIGZhbHNlO1xyXG5cclxuICAvLyAyLiBcdTYzRDBcdTUzRDZcdTYyNjlcdTVDNTVcdTU0MERcdUZGMDhcdTU5MDRcdTc0MDZcdTU5MUFcdTYyNjlcdTVDNTVcdTU0MERcdUZGMENcdTUzRDZcdTY3MDBcdTU0MEVcdTRFMDBcdTRFMkFcdUZGMDlcclxuICBjb25zdCBleHQgPSBiYXNlTmFtZS5zcGxpdChcIi5cIikucG9wKCk/LnRvTG93ZXJDYXNlKCk7XHJcbiAgaWYgKCFleHQgfHwgZXh0Lmxlbmd0aCA8IDIpIHJldHVybiBmYWxzZTsgLy8gXHU2MjY5XHU1QzU1XHU1NDBEXHU5NTdGXHU1RUE2XHU4MUYzXHU1QzExMlx1NEY0RFx1RkYwOFx1NTk4Mi5qcGdcdUZGMDlcclxuXHJcbiAgLy8gMy4gXHU2OEMwXHU2N0U1XHU2NjJGXHU1NDI2XHU1NzI4XHU1NkZFXHU3MjQ3XHU2MjY5XHU1QzU1XHU1NDBEXHU3NjdEXHU1NDBEXHU1MzU1XHJcbiAgcmV0dXJuIElNQUdFX0VYVEVOU0lPTlMuaGFzKGV4dCk7XHJcbn1cclxuXHJcbi8vIFx1NzkzQVx1NEY4Qlx1NkQ0Qlx1OEJENVxyXG4vLyAgIGNvbnNvbGUubG9nKGlzSW1hZ2UoJ3Bob3RvLmpwZycpKTsgICAgIC8vIHRydWVcclxuLy8gICBjb25zb2xlLmxvZyhpc0ltYWdlKCdpbWFnZS5wbmcnKSk7ICAgIC8vIHRydWVcclxuLy8gICBjb25zb2xlLmxvZyhpc0ltYWdlKCdsb2dvLnN2ZycpKTsgICAgIC8vIHRydWVcclxuLy8gICBjb25zb2xlLmxvZyhpc0ltYWdlKCdjb3Zlci50YXIuZ3onKSk7IC8vIGZhbHNlXHVGRjA4XHU5NzVFXHU1NkZFXHU3MjQ3XHU2MjY5XHU1QzU1XHU1NDBEXHVGRjA5XHJcbi8vICAgY29uc29sZS5sb2coaXNJbWFnZSgnZmlsZScpKTsgICAgICAgICAvLyBmYWxzZVx1RkYwOFx1NjVFMFx1NjI2OVx1NUM1NVx1NTQwRFx1RkYwOVxyXG4vLyAgIGNvbnNvbGUubG9nKGlzSW1hZ2UoJy5oaWRkZW4ucG5nJykpOyAgLy8gdHJ1ZVx1RkYwOFx1OTY5MFx1ODVDRlx1NjU4N1x1NEVGNlx1RkYwOVxyXG4vLyAgIGNvbnNvbGUubG9nKGlzSW1hZ2UoJ2ljb24uSlBFRycpKTsgICAgLy8gdHJ1ZVx1RkYwOFx1NTkyN1x1NUMwRlx1NTE5OVx1NEUwRFx1NjU0Rlx1NjExRlx1RkYwOVxyXG4vLyAgIGNvbnNvbGUubG9nKGlzSW1hZ2UoJ3ZpZGVvLm1wNCcpKTsgICAgLy8gZmFsc2VcdUZGMDhcdTg5QzZcdTk4OTFcdUZGMDlcclxuIl0sCiAgIm1hcHBpbmdzIjogIjtBQU1BLElBQU0sbUJBQW1CLG9CQUFJLElBQUk7QUFBQSxFQUMvQjtBQUFBLEVBQ0E7QUFBQSxFQUNBO0FBQUEsRUFDQTtBQUFBLEVBQ0E7QUFBQSxFQUNBO0FBQUEsRUFDQTtBQUFBLEVBQ0E7QUFBQSxFQUNBO0FBQUEsRUFDQTtBQUFBLEVBQ0E7QUFBQSxFQUNBO0FBQUEsRUFDQTtBQUNGLENBQUM7QUFDTSxTQUFTLFFBQVEsVUFBVTtBQUVoQyxRQUFNLFdBQVcsU0FBUyxNQUFNLEdBQUcsRUFBRSxJQUFJLEVBQUUsTUFBTSxJQUFJLEVBQUUsSUFBSTtBQUMzRCxNQUFJLENBQUMsU0FBVSxRQUFPO0FBR3RCLFFBQU0sTUFBTSxTQUFTLE1BQU0sR0FBRyxFQUFFLElBQUksR0FBRyxZQUFZO0FBQ25ELE1BQUksQ0FBQyxPQUFPLElBQUksU0FBUyxFQUFHLFFBQU87QUFHbkMsU0FBTyxpQkFBaUIsSUFBSSxHQUFHO0FBQ2pDOyIsCiAgIm5hbWVzIjogW10KfQo=

@ -0,0 +1,20 @@
// mock/utils/mockMoment.ts
function generateRandomDateTimeByYear(year) {
const month = Math.floor(Math.random() * 12) + 1;
const day = Math.floor(Math.random() * 31) + 1;
const hour = Math.floor(Math.random() * 24);
const minute = Math.floor(Math.random() * 60);
const second = Math.floor(Math.random() * 60);
return `${year}-${month < 10 ? "0" : ""}${month}-${day < 10 ? "0" : ""}${day} ${hour < 10 ? "0" : ""}${hour}:${minute < 10 ? "0" : ""}${minute}:${second < 10 ? "0" : ""}${second}`;
}
function generateRandomMoment(date = /* @__PURE__ */ new Date(), type = "HH:mm:ss") {
const hour = Math.floor(Math.random() * 24);
const minute = Math.floor(Math.random() * 60);
const second = Math.floor(Math.random() * 60);
return `${hour < 10 ? "0" : ""}${hour}:${minute < 10 ? "0" : ""}${minute}:${second < 10 ? "0" : ""}${second}`;
}
export {
generateRandomDateTimeByYear,
generateRandomMoment
};
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsibW9jay91dGlscy9tb2NrTW9tZW50LnRzIl0sCiAgInNvdXJjZXNDb250ZW50IjogWyJjb25zdCBfX2luamVjdGVkX2ZpbGVuYW1lX18gPSBcIkU6XFxcXHlheGluX3dlYlxcXFw1Ry1Mb2FkaW5nLUJheS1XZWJcXFxcbW9ja1xcXFx1dGlsc1xcXFxtb2NrTW9tZW50LnRzXCI7Y29uc3QgX19pbmplY3RlZF9kaXJuYW1lX18gPSBcIkU6XFxcXHlheGluX3dlYlxcXFw1Ry1Mb2FkaW5nLUJheS1XZWJcXFxcbW9ja1xcXFx1dGlsc1wiO2NvbnN0IF9faW5qZWN0ZWRfaW1wb3J0X21ldGFfdXJsX18gPSBcImZpbGU6Ly8vRToveWF4aW5fd2ViLzVHLUxvYWRpbmctQmF5LVdlYi9tb2NrL3V0aWxzL21vY2tNb21lbnQudHNcIjtleHBvcnQgZnVuY3Rpb24gZ2VuZXJhdGVSYW5kb21EYXRlVGltZUJ5WWVhcih5ZWFyKSB7XHJcbiAgLy8gXHU3NTFGXHU2MjEwXHU5NjhGXHU2NzNBXHU2NzA4XHU0RUZEXHVGRjA4MS0xMlx1RkYwOVxyXG4gIGNvbnN0IG1vbnRoID0gTWF0aC5mbG9vcihNYXRoLnJhbmRvbSgpICogMTIpICsgMTtcclxuXHJcbiAgLy8gXHU3NTFGXHU2MjEwXHU5NjhGXHU2NzNBXHU2NUU1XHU2NzFGXHVGRjA4MS0zMVx1RkYwOVxyXG4gIGNvbnN0IGRheSA9IE1hdGguZmxvb3IoTWF0aC5yYW5kb20oKSAqIDMxKSArIDE7XHJcblxyXG4gIC8vIFx1NzUxRlx1NjIxMFx1OTY4Rlx1NjczQVx1NUMwRlx1NjVGNlx1RkYwODAtMjNcdUZGMDlcclxuICBjb25zdCBob3VyID0gTWF0aC5mbG9vcihNYXRoLnJhbmRvbSgpICogMjQpO1xyXG5cclxuICAvLyBcdTc1MUZcdTYyMTBcdTk2OEZcdTY3M0FcdTUyMDZcdTk0OUZcdUZGMDgwLTU5XHVGRjA5XHJcbiAgY29uc3QgbWludXRlID0gTWF0aC5mbG9vcihNYXRoLnJhbmRvbSgpICogNjApO1xyXG5cclxuICAvLyBcdTc1MUZcdTYyMTBcdTk2OEZcdTY3M0FcdTc5RDJcdTk0OUZcdUZGMDgwLTU5XHVGRjA5XHJcbiAgY29uc3Qgc2Vjb25kID0gTWF0aC5mbG9vcihNYXRoLnJhbmRvbSgpICogNjApO1xyXG5cclxuICAvLyBcdThGRDRcdTU2REVcdTk2OEZcdTY3M0FcdTY1RTVcdTY3MUZcdTU0OENcdTY1RjZcdTk1RjRcdTc2ODRcdTVCNTdcdTdCMjZcdTRFMzJcclxuICByZXR1cm4gYCR7eWVhcn0tJHttb250aCA8IDEwID8gXCIwXCIgOiBcIlwifSR7bW9udGh9LSR7XHJcbiAgICBkYXkgPCAxMCA/IFwiMFwiIDogXCJcIlxyXG4gIH0ke2RheX0gJHtob3VyIDwgMTAgPyBcIjBcIiA6IFwiXCJ9JHtob3VyfToke21pbnV0ZSA8IDEwID8gXCIwXCIgOiBcIlwifSR7bWludXRlfToke1xyXG4gICAgc2Vjb25kIDwgMTAgPyBcIjBcIiA6IFwiXCJcclxuICB9JHtzZWNvbmR9YDtcclxufVxyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIGdlbmVyYXRlUmFuZG9tTW9tZW50KGRhdGUgPSBuZXcgRGF0ZSgpLCB0eXBlID0gXCJISDptbTpzc1wiKSB7XHJcbiAgLy8gXHU3NTFGXHU2MjEwXHU5NjhGXHU2NzNBXHU1QzBGXHU2NUY2XHVGRjA4MC0yM1x1RkYwOVxyXG4gIGNvbnN0IGhvdXIgPSBNYXRoLmZsb29yKE1hdGgucmFuZG9tKCkgKiAyNCk7XHJcblxyXG4gIC8vIFx1NzUxRlx1NjIxMFx1OTY4Rlx1NjczQVx1NTIwNlx1OTQ5Rlx1RkYwODAtNTlcdUZGMDlcclxuICBjb25zdCBtaW51dGUgPSBNYXRoLmZsb29yKE1hdGgucmFuZG9tKCkgKiA2MCk7XHJcblxyXG4gIC8vIFx1NzUxRlx1NjIxMFx1OTY4Rlx1NjczQVx1NzlEMlx1OTQ5Rlx1RkYwODAtNTlcdUZGMDlcclxuICBjb25zdCBzZWNvbmQgPSBNYXRoLmZsb29yKE1hdGgucmFuZG9tKCkgKiA2MCk7XHJcbiAgLy8gXHU4RkQ0XHU1NkRFXHU5NjhGXHU2NzNBXHU2NUU1XHU2NzFGXHU1NDhDXHU2NUY2XHU5NUY0XHU3Njg0XHU1QjU3XHU3QjI2XHU0RTMyXHJcbiAgcmV0dXJuIGAke2hvdXIgPCAxMCA/IFwiMFwiIDogXCJcIn0ke2hvdXJ9OiR7bWludXRlIDwgMTAgPyBcIjBcIiA6IFwiXCJ9JHttaW51dGV9OiR7XHJcbiAgICBzZWNvbmQgPCAxMCA/IFwiMFwiIDogXCJcIlxyXG4gIH0ke3NlY29uZH1gO1xyXG59XHJcbiJdLAogICJtYXBwaW5ncyI6ICI7QUFBMlIsU0FBUyw2QkFBNkIsTUFBTTtBQUVyVSxRQUFNLFFBQVEsS0FBSyxNQUFNLEtBQUssT0FBTyxJQUFJLEVBQUUsSUFBSTtBQUcvQyxRQUFNLE1BQU0sS0FBSyxNQUFNLEtBQUssT0FBTyxJQUFJLEVBQUUsSUFBSTtBQUc3QyxRQUFNLE9BQU8sS0FBSyxNQUFNLEtBQUssT0FBTyxJQUFJLEVBQUU7QUFHMUMsUUFBTSxTQUFTLEtBQUssTUFBTSxLQUFLLE9BQU8sSUFBSSxFQUFFO0FBRzVDLFFBQU0sU0FBUyxLQUFLLE1BQU0sS0FBSyxPQUFPLElBQUksRUFBRTtBQUc1QyxTQUFPLEdBQUcsSUFBSSxJQUFJLFFBQVEsS0FBSyxNQUFNLEVBQUUsR0FBRyxLQUFLLElBQzdDLE1BQU0sS0FBSyxNQUFNLEVBQ25CLEdBQUcsR0FBRyxJQUFJLE9BQU8sS0FBSyxNQUFNLEVBQUUsR0FBRyxJQUFJLElBQUksU0FBUyxLQUFLLE1BQU0sRUFBRSxHQUFHLE1BQU0sSUFDdEUsU0FBUyxLQUFLLE1BQU0sRUFDdEIsR0FBRyxNQUFNO0FBQ1g7QUFFTyxTQUFTLHFCQUFxQixPQUFPLG9CQUFJLEtBQUssR0FBRyxPQUFPLFlBQVk7QUFFekUsUUFBTSxPQUFPLEtBQUssTUFBTSxLQUFLLE9BQU8sSUFBSSxFQUFFO0FBRzFDLFFBQU0sU0FBUyxLQUFLLE1BQU0sS0FBSyxPQUFPLElBQUksRUFBRTtBQUc1QyxRQUFNLFNBQVMsS0FBSyxNQUFNLEtBQUssT0FBTyxJQUFJLEVBQUU7QUFFNUMsU0FBTyxHQUFHLE9BQU8sS0FBSyxNQUFNLEVBQUUsR0FBRyxJQUFJLElBQUksU0FBUyxLQUFLLE1BQU0sRUFBRSxHQUFHLE1BQU0sSUFDdEUsU0FBUyxLQUFLLE1BQU0sRUFDdEIsR0FBRyxNQUFNO0FBQ1g7IiwKICAibmFtZXMiOiBbXQp9Cg==

@ -0,0 +1,59 @@
/*
* @Author: donghao donghao@supervision.ltd
* @Date: 2025-03-07 14:58:39
* @LastEditors: donghao donghao@supervision.ltd
* @LastEditTime: 2025-03-13 14:29:26
* @FilePath: \5G-Loading-Bay-Web\mock\utils\apiMock.ts
* @Description: ,`customMade`, koroFileHeader : https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
import { failMockApiProps, successMockApiProps } from "../typing";
export function fetchMockSuccessFullByOther({
data,
msg,
}): successMockApiProps {
// return {
// code: 200, // 200 成功
// success: true, // true 成功
// data: data || null, // mock业务层数据
// msg: msg | "ok", // 成功提示
// isMock: true // true 标识当前是模拟数据
// } as successMockApiProps;
const result: successMockApiProps = {
code: 200, // 200 成功
success: true, // true 成功
data: data || null, // mock业务层数据
msg: msg as string | "ok", // 成功提示
isMock: true, // true 标识当前是模拟数据
};
return result;
}
export function fetchMockFailFullByOther({ data, msg }): failMockApiProps {
// return {
// code: 599, // 200 成功
// success: true, // true 成功
// data: data || null, // mock业务层数据
// msg: msg | "fail", // 成功提示
// isMock: true // true 标识当前是模拟数据
// } as failMockApiProps;
const result: failMockApiProps = {
code: 599, // 200 成功
success: false, // true 成功
data: data || null, // mock业务层数据
msg: msg as string | "fail", // 成功提示
isMock: true, // true 标识当前是模拟数据
};
return result;
}
// 分页展示
export function fetchCurrPageByList({ data }): successMockApiProps {
// console.log("fetchCurrPageByList_data", data);
const { current, pageSize } = data;
const prevPage = current - 1;
const currPageData = {
...data,
data: data.data.slice(prevPage * pageSize, current * pageSize),
};
return fetchMockSuccessFullByOther({ data: currPageData });
}

@ -0,0 +1,43 @@
/**
*
* @param {string} filename
* @returns {boolean}
*/
const IMAGE_EXTENSIONS = new Set([
"jpg",
"jpeg",
"png",
"gif",
"bmp",
"svg",
"webp",
"tiff",
"psd",
"ico",
"jfif",
"apng",
"avif",
]);
export function isImage(filename) {
// 1. 去除路径,只保留文件名
const baseName = filename.split("/").pop().split("\\").pop();
if (!baseName) return false;
// 2. 提取扩展名(处理多扩展名,取最后一个)
const ext = baseName.split(".").pop()?.toLowerCase();
if (!ext || ext.length < 2) return false; // 扩展名长度至少2位如.jpg
// 3. 检查是否在图片扩展名白名单
return IMAGE_EXTENSIONS.has(ext);
}
// 示例测试
// console.log(isImage('photo.jpg')); // true
// console.log(isImage('image.png')); // true
// console.log(isImage('logo.svg')); // true
// console.log(isImage('cover.tar.gz')); // false非图片扩展名
// console.log(isImage('file')); // false无扩展名
// console.log(isImage('.hidden.png')); // true隐藏文件
// console.log(isImage('icon.JPEG')); // true大小写不敏感
// console.log(isImage('video.mp4')); // false视频

@ -0,0 +1,38 @@
export function generateRandomDateTimeByYear(year) {
// 生成随机月份1-12
const month = Math.floor(Math.random() * 12) + 1;
// 生成随机日期1-31
const day = Math.floor(Math.random() * 31) + 1;
// 生成随机小时0-23
const hour = Math.floor(Math.random() * 24);
// 生成随机分钟0-59
const minute = Math.floor(Math.random() * 60);
// 生成随机秒钟0-59
const second = Math.floor(Math.random() * 60);
// 返回随机日期和时间的字符串
return `${year}-${month < 10 ? "0" : ""}${month}-${
day < 10 ? "0" : ""
}${day} ${hour < 10 ? "0" : ""}${hour}:${minute < 10 ? "0" : ""}${minute}:${
second < 10 ? "0" : ""
}${second}`;
}
export function generateRandomMoment(date = new Date(), type = "HH:mm:ss") {
// 生成随机小时0-23
const hour = Math.floor(Math.random() * 24);
// 生成随机分钟0-59
const minute = Math.floor(Math.random() * 60);
// 生成随机秒钟0-59
const second = Math.floor(Math.random() * 60);
// 返回随机日期和时间的字符串
return `${hour < 10 ? "0" : ""}${hour}:${minute < 10 ? "0" : ""}${minute}:${
second < 10 ? "0" : ""
}${second}`;
}

5584
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -0,0 +1,51 @@
{
"name": "data-dashboard",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "cross-env NODE_ENV=development vite",
"prod": "vite --mode production",
"build": "cross-env NODE_ENV=production vite build",
"build:dev": "vite build --mode development",
"build:prod": "vite build --mode production",
"preview": "vite preview --port 5050",
"preview:prod": "vite preview --port 6060 --mode production"
},
"dependencies": {
"@types/three": "^0.175.0",
"axios": "^1.8.3",
"echarts": "^5.6.0",
"exceljs": "^4.4.0",
"lodash": "^4.17.21",
"moment": "^2.30.1",
"postcss-scss": "^4.0.9",
"sass": "^1.85.1",
"swiper": "^11.2.5",
"three": "^0.175.0",
"unplugin-auto-import": "^19.1.1",
"video.js": "^8.22.0",
"vue": "^3.5.13"
},
"devDependencies": {
"@element-plus/icons-vue": "^2.3.1",
"@tailwindcss/forms": "^0.5.10",
"@tailwindcss/postcss": "^4.0.10",
"@types/echarts": "^5.0.0",
"@vitejs/plugin-vue": "^5.2.1",
"@vitejs/plugin-vue-jsx": "^4.1.1",
"@vue/tsconfig": "^0.7.0",
"autoprefixer": "^10.4.20",
"cross-env": "^7.0.3",
"element-plus": "^2.9.5",
"mockjs": "^1.1.0",
"pinia": "^3.0.1",
"postcss": "^8.5.3",
"tailwindcss": "^3.4.17",
"typescript": "~5.7.2",
"vite": "^6.2.0",
"vite-plugin-mock": "^3.0.2",
"vue-router": "^4.5.0",
"vue-tsc": "^2.2.4"
}
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,16 @@
/*
* @Author: donghao donghao@supervision.ltd
* @Date: 2025-03-06 16:22:19
* @LastEditors: donghao donghao@supervision.ltd
* @LastEditTime: 2025-03-06 17:07:24
* @FilePath: \vite-ai\data-dashboard\postcss.config.js
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
import autoprefixer from 'autoprefixer';
import tailwindcss from 'tailwindcss';
export default {
plugins: [
tailwindcss(),
autoprefixer()
]
};

3514
public/adapter.min.js vendored

File diff suppressed because it is too large Load Diff

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

@ -0,0 +1,381 @@
var WebRtcStreamer = (function () {
/**
* Interface with WebRTC-streamer API
* @constructor
* @param {string} videoElement - id of the video element tag
* @param {string} srvurl - url of webrtc-streamer (default is current location)
*/
var WebRtcStreamer = function WebRtcStreamer(videoElement, srvurl) {
if (typeof videoElement === "string") {
this.videoElement = document.getElementById(videoElement);
} else {
this.videoElement = videoElement;
}
this.srvurl = srvurl || location.protocol + "//" + window.location.hostname + ":" + window.location.port;
this.pc = null;
this.mediaConstraints = {
offerToReceiveAudio: true,
offerToReceiveVideo: true
};
this.iceServers = null;
this.earlyCandidates = [];
}
WebRtcStreamer.prototype._handleHttpErrors = function (response) {
if (!response.ok) {
throw Error(response.statusText);
}
return response;
}
/**
* Connect a WebRTC Stream to videoElement
* @param {string} videourl - id of WebRTC video stream
* @param {string} audiourl - id of WebRTC audio stream
* @param {string} options - options of WebRTC call
* @param {string} stream - local stream to send
* @param {string} prefmime - prefered mime
*/
WebRtcStreamer.prototype.connect = function (videourl, audiourl, options, localstream, prefmime) {
this.disconnect();
// getIceServers is not already received
if (!this.iceServers) {
console.log("Get IceServers");
fetch(this.srvurl + "/api/getIceServers")
.then(this._handleHttpErrors)
.then((response) => (response.json()))
.then((response) => this.onReceiveGetIceServers(response, videourl, audiourl, options, localstream, prefmime))
.catch((error) => this.onError("getIceServers " + error))
} else {
this.onReceiveGetIceServers(this.iceServers, videourl, audiourl, options, localstream, prefmime);
}
}
/**
* Disconnect a WebRTC Stream and clear videoElement source
*/
WebRtcStreamer.prototype.disconnect = function () {
if (this.videoElement?.srcObject) {
this.videoElement.srcObject.getTracks().forEach(track => {
track.stop()
this.videoElement.srcObject.removeTrack(track);
});
}
if (this.pc) {
fetch(this.srvurl + "/api/hangup?peerid=" + this.pc.peerid)
.then(this._handleHttpErrors)
.catch((error) => this.onError("hangup " + error))
try {
this.pc.close();
} catch (e) {
console.log("Failure close peer connection:" + e);
}
this.pc = null;
}
}
WebRtcStreamer.prototype.filterPreferredCodec = function (sdp, prefmime) {
const lines = sdp.split('\n');
const [prefkind, prefcodec] = prefmime.toLowerCase().split('/');
let currentMediaType = null;
let sdpSections = [];
let currentSection = [];
// Group lines into sections
lines.forEach(line => {
if (line.startsWith('m=')) {
if (currentSection.length) {
sdpSections.push(currentSection);
}
currentSection = [line];
} else {
currentSection.push(line);
}
});
sdpSections.push(currentSection);
// Process each section
const processedSections = sdpSections.map(section => {
const firstLine = section[0];
if (!firstLine.startsWith('m=' + prefkind)) {
return section.join('\n');
}
// Get payload types for preferred codec
const rtpLines = section.filter(line => line.startsWith('a=rtpmap:'));
const preferredPayloads = rtpLines
.filter(line => line.toLowerCase().includes(prefcodec))
.map(line => line.split(':')[1].split(' ')[0]);
if (preferredPayloads.length === 0) {
return section.join('\n');
}
// Modify m= line to only include preferred payloads
const mLine = firstLine.split(' ');
const newMLine = [...mLine.slice(0, 3), ...preferredPayloads].join(' ');
// Filter related attributes
const filteredLines = section.filter(line => {
if (line === firstLine) return false;
if (line.startsWith('a=rtpmap:')) {
return preferredPayloads.some(payload => line.startsWith(`a=rtpmap:${payload}`));
}
if (line.startsWith('a=fmtp:') || line.startsWith('a=rtcp-fb:')) {
return preferredPayloads.some(payload => line.startsWith(`a=${line.split(':')[0].split('a=')[1]}:${payload}`));
}
return true;
});
return [newMLine, ...filteredLines].join('\n');
});
return processedSections.join('\n');
}
/*
* GetIceServers callback
*/
WebRtcStreamer.prototype.onReceiveGetIceServers = function (iceServers, videourl, audiourl, options, stream, prefmime) {
this.iceServers = iceServers;
this.pcConfig = iceServers || {
"iceServers": []
};
try {
this.createPeerConnection();
let callurl = this.srvurl + "/api/call?peerid=" + this.pc.peerid + "&url=" + encodeURIComponent(videourl);
if (audiourl) {
callurl += "&audiourl=" + encodeURIComponent(audiourl);
}
if (options) {
callurl += "&options=" + encodeURIComponent(options);
}
if (stream) {
this.pc.addStream(stream);
}
// clear early candidates
this.earlyCandidates.length = 0;
// create Offer
this.pc.createOffer(this.mediaConstraints).then((sessionDescription) => {
console.log("Create offer:" + JSON.stringify(sessionDescription));
console.log(`video codecs:${Array.from(new Set(RTCRtpReceiver.getCapabilities("video")?.codecs?.map(codec => codec.mimeType)))}`)
console.log(`audio codecs:${Array.from(new Set(RTCRtpReceiver.getCapabilities("audio")?.codecs?.map(codec => codec.mimeType)))}`)
if (prefmime != undefined) {
//set prefered codec
let [prefkind] = prefmime.split('/');
if (prefkind != "video" && prefkind != "audio") {
prefkind = "video";
prefmime = prefkind + "/" + prefmime;
}
console.log("sdp:" + sessionDescription.sdp);
sessionDescription.sdp = this.filterPreferredCodec(sessionDescription.sdp, prefmime);
console.log("sdp:" + sessionDescription.sdp);
}
this.pc.setLocalDescription(sessionDescription)
.then(() => {
fetch(callurl, {
method: "POST",
body: JSON.stringify(sessionDescription)
})
.then(this._handleHttpErrors)
.then((response) => (response.json()))
.catch((error) => this.onError("call " + error))
.then((response) => this.onReceiveCall(response))
.catch((error) => this.onError("call " + error))
}, (error) => {
console.log("setLocalDescription error:" + JSON.stringify(error));
});
}, (error) => {
alert("Create offer error:" + JSON.stringify(error));
});
} catch (e) {
this.disconnect();
alert("connect error: " + e);
}
}
WebRtcStreamer.prototype.getIceCandidate = function () {
fetch(this.srvurl + "/api/getIceCandidate?peerid=" + this.pc.peerid)
.then(this._handleHttpErrors)
.then((response) => (response.json()))
.then((response) => this.onReceiveCandidate(response))
.catch((error) => this.onError("getIceCandidate " + error))
}
/*
* create RTCPeerConnection
*/
WebRtcStreamer.prototype.createPeerConnection = function () {
console.log("createPeerConnection config: " + JSON.stringify(this.pcConfig));
this.pc = new RTCPeerConnection(this.pcConfig);
let pc = this.pc;
pc.peerid = Math.random();
pc.onicecandidate = (evt) => this.onIceCandidate(evt);
pc.onaddstream = (evt) => this.onAddStream(evt);
pc.oniceconnectionstatechange = (evt) => {
console.log("oniceconnectionstatechange state: " + pc.iceConnectionState);
if (this.videoElement) {
if (pc.iceConnectionState === "connected") {
this.videoElement.style.opacity = "1.0";
} else if (pc.iceConnectionState === "disconnected") {
this.videoElement.style.opacity = "0.25";
} else if ((pc.iceConnectionState === "failed") || (pc.iceConnectionState === "closed")) {
this.videoElement.style.opacity = "0.5";
} else if (pc.iceConnectionState === "new") {
this.getIceCandidate();
}
}
}
pc.ondatachannel = function (evt) {
console.log("remote datachannel created:" + JSON.stringify(evt));
evt.channel.onopen = function () {
console.log("remote datachannel open");
this.send("remote channel openned");
}
evt.channel.onmessage = function (event) {
console.log("remote datachannel recv:" + JSON.stringify(event.data));
}
}
try {
let dataChannel = pc.createDataChannel("ClientDataChannel");
dataChannel.onopen = function () {
console.log("local datachannel open");
this.send("local channel openned");
}
dataChannel.onmessage = function (evt) {
console.log("local datachannel recv:" + JSON.stringify(evt.data));
}
} catch (e) {
console.log("Cannor create datachannel error: " + e);
}
console.log("Created RTCPeerConnnection with config: " + JSON.stringify(this.pcConfig));
return pc;
}
/*
* RTCPeerConnection IceCandidate callback
*/
WebRtcStreamer.prototype.onIceCandidate = function (event) {
if (event.candidate) {
if (this.pc.currentRemoteDescription) {
this.addIceCandidate(this.pc.peerid, event.candidate);
} else {
this.earlyCandidates.push(event.candidate);
}
} else {
console.log("End of candidates.");
}
}
WebRtcStreamer.prototype.addIceCandidate = function (peerid, candidate) {
fetch(this.srvurl + "/api/addIceCandidate?peerid=" + peerid, {
method: "POST",
body: JSON.stringify(candidate)
})
.then(this._handleHttpErrors)
.then((response) => (response.json()))
.then((response) => {
console.log("addIceCandidate ok:" + response)
})
.catch((error) => this.onError("addIceCandidate " + error))
}
/*
* RTCPeerConnection AddTrack callback
*/
WebRtcStreamer.prototype.onAddStream = function (event) {
console.log("Remote track added:" + JSON.stringify(event));
this.videoElement.srcObject = event.stream;
let promise = this.videoElement.play();
if (promise !== undefined) {
promise.catch((error) => {
console.warn("error:" + error);
this.videoElement.setAttribute("controls", true);
});
}
}
/*
* AJAX /call callback
*/
WebRtcStreamer.prototype.onReceiveCall = function (dataJson) {
console.log("offer: " + JSON.stringify(dataJson));
let descr = new RTCSessionDescription(dataJson);
this.pc.setRemoteDescription(descr).then(() => {
console.log("setRemoteDescription ok");
while (this.earlyCandidates.length) {
let candidate = this.earlyCandidates.shift();
this.addIceCandidate(this.pc.peerid, candidate);
}
this.getIceCandidate()
}, (error) => {
console.log("setRemoteDescription error:" + JSON.stringify(error));
});
}
/*
* AJAX /getIceCandidate callback
*/
WebRtcStreamer.prototype.onReceiveCandidate = function (dataJson) {
console.log("candidate: " + JSON.stringify(dataJson));
if (dataJson) {
for (let i = 0; i < dataJson.length; i++) {
let candidate = new RTCIceCandidate(dataJson[i]);
console.log("Adding ICE candidate :" + JSON.stringify(candidate));
this.pc.addIceCandidate(candidate).then(() => {
console.log("addIceCandidate OK");
}, (error) => {
console.log("addIceCandidate error:" + JSON.stringify(error));
});
}
this.pc.addIceCandidate();
}
}
/*
* AJAX callback for Error
*/
WebRtcStreamer.prototype.onError = function (status) {
console.log("onError:" + status);
}
return WebRtcStreamer;
})();
if (typeof window !== 'undefined' && typeof window.document !== 'undefined') {
window.WebRtcStreamer = WebRtcStreamer;
}
if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
module.exports = WebRtcStreamer;
}

@ -0,0 +1,145 @@
<template>
<!-- 全局容器 -->
<div class="app-container">
<!-- 路由视图容器 -->
<router-view v-slot="{ Component, route }">
<!-- 智能缓存仅缓存带有 meta.keepAlive 的路由 -->
<!-- <keep-alive :include="cachedRoutes"> -->
<component
:is="Component"
:key="route.fullPath"
class="router-view-container"
:class="{ 'has-header': showHeader }"
/>
<!-- </keep-alive> -->
</router-view>
<!-- 大屏适配容器用于缩放内容 -->
<div
v-if="isDashboardPage"
ref="scaleContainer"
class="scale-container"
:style="scaleStyle"
>
<!-- 实际内容会被挂载到这里 -->
</div>
</div>
</template>
<script setup lang="ts">
import { computed, ref, onMounted, onUnmounted, watch } from 'vue'
import { useRoute, useRouter } from 'vue-router'
const route = useRoute()
const router = useRouter()
const scaleContainer = ref<HTMLElement>()
const scaleValue = ref(1)
//
const cachedRoutes = ref<string[]>([])
//
const showHeader = computed(() => route.meta.showHeader ?? true)
//
const isDashboardPage = computed(() => route.meta.isDashboard ?? false)
//
const scaleStyle = computed(() => ({
transform: `scale(${scaleValue.value}) translate(-50%, -50%)`,
width: `${(1 / scaleValue.value) * 100}%`,
height: `${(1 / scaleValue.value) * 100}%`
}))
//
const calculateScale = () => {
if (!isDashboardPage.value || !scaleContainer.value) return
const baseWidth = 1920 // 稿
const baseHeight = 1080 // 稿
const currentWidth = window.innerWidth
const currentHeight = window.innerHeight
//
const widthRatio = currentWidth / baseWidth
const heightRatio = currentHeight / baseHeight
scaleValue.value = Math.min(widthRatio, heightRatio)
}
//
watch(
() => route.path,
(newVal) => {
//
if (route.meta.keepAlive && route.name) {
cachedRoutes.value = [...new Set([...cachedRoutes.value, route.name as string])]
}
//
if (isDashboardPage.value) {
nextTick(() => {
//
const view = document.querySelector('.router-view-container')
if (view && scaleContainer.value) {
scaleContainer.value.appendChild(view)
}
calculateScale()
})
}
}
)
// resize
let resizeTimer: number
const onResize = () => {
clearTimeout(resizeTimer)
resizeTimer = setTimeout(calculateScale, 300)
}
onMounted(() => {
window.addEventListener('resize', onResize)
calculateScale()
})
onUnmounted(() => {
window.removeEventListener('resize', onResize)
})
</script>
<style lang="scss">
/* 全局样式重置 */
html, body, #app {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
overflow: hidden; /* 禁用全局滚动条 */
}
.app-container {
position: relative;
width: 100%;
height: 100%;
/* 路由视图基础样式 */
.router-view-container {
width: 100%;
height: 100%;
}
/* 大屏适配容器样式 */
.scale-container {
position: fixed;
top: 50%;
left: 50%;
transform-origin: 0 0;
transition: transform 0.3s;
/* 大屏页面特殊处理 */
.router-view-container {
background-color: #08104C; /* 深色背景 */
color: #fff;
}
}
}
</style>

@ -0,0 +1,96 @@
/*
* @Author: donghao donghao@supervision.ltd
* @Date: 2025-03-07 15:09:18
* @LastEditors: donghao donghao@supervision.ltd
* @LastEditTime: 2025-03-14 11:14:15
* @FilePath: \5G-Loading-Bay-Web\src\api\dashboard.ts
* @Description: ,`customMade`, koroFileHeader : https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
// 接口层
import request from "@/utils/request/instance";
import { config } from "@/config";
//TODO 定义响应类型
export const getDeviceStatusApi = (params: any) => {
return request.get(`/api/v1/device/device/`, params);
};
// 撑杆检测
export const getAppearanceMonitorApi = (params: any) => {
return request.get(`/api/v1/record/record/`, params);
};
// 删除检测
export const deleteAppearanceMonitorApi = (data: any) => {
return request.delete(`/api/v1/record/record/${data.id}/`, data);
};
// 撑杆检测详情
export const getAppearanceMonitorDetailApi = (params: any) => {
return request.get(`/api/v1/record/record_detail_list/`, params);
};
// 检测总量
export const getDataOverviewApi = (params: any) => {
return request.get(`/api/v1/system/get_record_stats/`, params);
};
// 设备信息数据
export const getDeviceInfowApi = () => {
return request.get(`/api/v1/system/get_device_stats/`);
};
// 检测问题分布数据
export const getRecordFaultApi = (params: any) => {
return request.get(`/api/v1/system/get_record_fault_stats/`, params);
};
// 获取实时监控
export const getRealTimeApi = () => {
return request.get(`/api/v1/record/get_latest_second_records/`);
};
// 设备状态打开实时监控
export const playRtspApi = (data: { url: string }) => {
return request.post<LoginRes>(`/api/v1/common/play_rtsp/`, data, {
showLoading: false, // 单独关闭loading
});
};
// {
// "code": 200,
// "success": true,
// "data": {
// "process_id": 23432,
// "host": "192.168.10.14:7001"
// },
// "errorMessage": ""
// }
// 设备状态关闭实时监控
export const stopRtspApi = (data: { process_id: string | number }) => {
return request.post<LoginRes>(`/api/v1/common/stop_rtsp/`, data, {
showLoading: false, // 单独关闭loading
});
};
// 根据设备 ID 查询历史视频列表,支持日期时间过滤
export const getDeviceHistoryDetailApi = (params: {
device_id: string | number;
start_time: string;
end_time: string;
}) => {
return request.get(`/api/v1/device/device_history/`, params);
};
// 车辆管理
export const getVehiclManagementApi = (params: any) => {
return request.get(`/api/v1/record/train_record/`, params);
};
// 外观检测故障前详情
export const getBeforeMonitorDetailApi = (params: any) => {
return request.get(`/api/v1/record/before_arrive_record_list/`, params);
};

@ -0,0 +1,34 @@
/*
* @Author: donghao donghao@supervision.ltd
* @Date: 2025-03-12 15:13:38
* @LastEditors: donghao donghao@supervision.ltd
* @LastEditTime: 2025-03-13 09:29:20
* @FilePath: \5G-Loading-Bay-Web\src\api\user.ts
* @Description: ,`customMade`, koroFileHeader : https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
import request from '@/utils/request/instance'
import { config } from '@/config'
// 定义响应类型
interface LoginRes {
token: string
userInfo: {
id: number
username: string
}
}
export const loginApi = (data: { username: string; password: string }) => {
return request.post<LoginRes>(`/api/v1/user/login/`, data, {
showLoading: false // 单独关闭loading
})
}
export const loginOutApi = () => {
return request.post(`/api/v1/user/logout/`, {}, {
showLoading: false // 单独关闭loading
})
}
// export const getUserInfo = (userId: number) => {
// return request.get(`/user/info/${userId}`)
// }

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 342 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 800 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 225 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 194 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 400 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 352 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 317 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 713 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 248 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 855 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 488 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 915 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 711 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 876 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 922 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 955 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 420 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 468 B

@ -0,0 +1,26 @@
<svg width="42" height="34" viewBox="0 0 42 34" fill="none" xmlns="http://www.w3.org/2000/svg">
<g filter="url(#filter0_ii_9562_48037)">
<path d="M38.582 32.8229H3.93728L1.25977 30.6782V24.4139L2.7611 23.2867L2.78615 10.4887L1.25977 9.26178V3.0673L3.93728 0.932617H38.582L41.2595 3.0673V30.6782L38.582 32.8229Z" fill="#009DFF" fill-opacity="0.1"/>
</g>
<path opacity="0.6" d="M38.3222 33H3.67751L1 30.848V24.562L2.50134 23.431L2.52638 10.589L1 9.35782V3.14202L3.67751 1H38.3222L40.9997 3.14202V30.848L38.3222 33Z" stroke="#009DFF" stroke-width="0.58" stroke-miterlimit="10"/>
<path d="M21 15C22.933 15 24.5 13.433 24.5 11.5C24.5 9.56701 22.933 8 21 8C19.067 8 17.5 9.56701 17.5 11.5C17.5 13.433 19.067 15 21 15Z" fill="#009DFF" stroke="#009DFF" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M12 25.4V26H30V25.4C30 23.1598 30 22.0397 29.5641 21.184C29.1806 20.4314 28.5686 19.8195 27.816 19.436C26.9603 19 25.8402 19 23.6 19H18.4C16.1598 19 15.0397 19 14.1841 19.436C13.4314 19.8195 12.8195 20.4314 12.436 21.184C12 22.0397 12 23.1598 12 25.4Z" fill="#009DFF" stroke="#009DFF" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<defs>
<filter id="filter0_ii_9562_48037" x="1.25977" y="-1.06738" width="39.9995" height="35.8904" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="2"/>
<feGaussianBlur stdDeviation="2"/>
<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.0361115 0 0 0 0 0.4795 0 0 0 0 1 0 0 0 0.4 0"/>
<feBlend mode="normal" in2="shape" result="effect1_innerShadow_9562_48037"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="-2"/>
<feGaussianBlur stdDeviation="2"/>
<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.0352941 0 0 0 0 0.478431 0 0 0 0 1 0 0 0 0.4 0"/>
<feBlend mode="normal" in2="effect1_innerShadow_9562_48037" result="effect2_innerShadow_9562_48037"/>
</filter>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 563 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 208 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 189 KiB

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

Loading…
Cancel
Save