Compare commits
34 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bdc45b8890 | ||
|
|
2705bedc71 | ||
|
|
ae970d048c | ||
|
|
29f57c80ef | ||
|
|
80072bf7e0 | ||
|
|
8d377dfed7 | ||
|
|
633b6ffd29 | ||
|
|
cf3141198b | ||
|
|
c05d329614 | ||
|
|
ee08263e4a | ||
| 19ea08d5e0 | |||
|
|
f9809197e8 | ||
|
|
0090a922c6 | ||
|
|
0b26de20b9 | ||
|
|
1202f64bfc | ||
|
|
ce1738daf0 | ||
|
|
a41d824ca3 | ||
|
|
ac5a8450e8 | ||
|
|
01e817a5d6 | ||
|
|
01bf07fc42 | ||
|
|
633e914c9a | ||
|
|
37e69e7bda | ||
|
|
19fb90432a | ||
|
|
4a3c81a792 | ||
|
|
12d3073241 | ||
|
|
72838462ad | ||
|
|
327addf625 | ||
|
|
7fd3b6fdff | ||
| 4bfab6518e | |||
|
|
4655259153 | ||
|
|
cdb23726f8 | ||
|
|
68a1c9d28d | ||
|
|
30e815c027 | ||
|
|
ce10f91b5b |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -9,3 +9,4 @@ public/electron/
|
|||||||
pnpm-lock.yaml
|
pnpm-lock.yaml
|
||||||
CLAUDE.md
|
CLAUDE.md
|
||||||
/public/dist/
|
/public/dist/
|
||||||
|
/docs/
|
||||||
|
|||||||
@@ -33,9 +33,9 @@ mybatis-plus:
|
|||||||
#驼峰命名
|
#驼峰命名
|
||||||
map-underscore-to-camel-case: true
|
map-underscore-to-camel-case: true
|
||||||
#配置sql日志输出
|
#配置sql日志输出
|
||||||
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
|
# log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
|
||||||
#关闭日志输出
|
#关闭日志输出
|
||||||
# log-impl: org.apache.ibatis.logging.nologging.NoLoggingImpl
|
log-impl: org.apache.ibatis.logging.nologging.NoLoggingImpl
|
||||||
global-config:
|
global-config:
|
||||||
db-config:
|
db-config:
|
||||||
#指定主键生成策略
|
#指定主键生成策略
|
||||||
@@ -56,29 +56,31 @@ webSocket:
|
|||||||
#源参数下发,暂态数据默认值
|
#源参数下发,暂态数据默认值
|
||||||
Dip:
|
Dip:
|
||||||
# 暂态前时间(s)
|
# 暂态前时间(s)
|
||||||
fPreTime: 2f
|
# fPreTime: 2f
|
||||||
#写入时间(s)
|
#写入时间(s)
|
||||||
fRampIn: 0.001f
|
fRampIn: 0.001f
|
||||||
#写出时间(s)
|
#写出时间(s)
|
||||||
fRampOut: 0.001f
|
fRampOut: 0.001f
|
||||||
# 暂态后时间(s)
|
# 暂态后时间(s)
|
||||||
fAfterTime: 3f
|
# fAfterTime: 3f
|
||||||
|
|
||||||
|
|
||||||
Flicker:
|
#Flicker:
|
||||||
waveFluType: CPM
|
# waveFluType: CPM
|
||||||
waveType: SQU
|
# waveType: SQU
|
||||||
fDutyCycle: 50f
|
# fDutyCycle: 50f
|
||||||
|
|
||||||
log:
|
#log:
|
||||||
homeDir: {{APP_DATA_PATH}}\logs
|
# homeDir: D:\logs
|
||||||
commonLevel: info
|
# commonLevel: info
|
||||||
report:
|
report:
|
||||||
template: {{APP_DATA_PATH}}\template
|
# template: D:\template
|
||||||
reportDir: {{APP_DATA_PATH}}\report
|
# reportDir: D:\report
|
||||||
dateFormat: yyyy年MM月dd日
|
dateFormat: yyyy年MM月dd日
|
||||||
data:
|
#data:
|
||||||
homeDir: {{APP_DATA_PATH}}\data
|
# homeDir: D:\data
|
||||||
|
#resource:
|
||||||
|
# videoDir: ${data.homeDir}\resources\videos
|
||||||
qr:
|
qr:
|
||||||
cloud: http://pqmcc.com:18082/api/file
|
cloud: http://pqmcc.com:18082/api/file
|
||||||
dev:
|
dev:
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
build/extraResources/mysql/data/#innodb_redo/#ib_redo75_tmp
Normal file
BIN
build/extraResources/mysql/data/#innodb_redo/#ib_redo75_tmp
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1 +1 @@
|
|||||||
74476
|
116212
|
||||||
|
|||||||
Binary file not shown.
BIN
build/extraResources/mysql/data/binlog.000032
Normal file
BIN
build/extraResources/mysql/data/binlog.000032
Normal file
Binary file not shown.
BIN
build/extraResources/mysql/data/binlog.000033
Normal file
BIN
build/extraResources/mysql/data/binlog.000033
Normal file
Binary file not shown.
BIN
build/extraResources/mysql/data/binlog.000034
Normal file
BIN
build/extraResources/mysql/data/binlog.000034
Normal file
Binary file not shown.
BIN
build/extraResources/mysql/data/binlog.000035
Normal file
BIN
build/extraResources/mysql/data/binlog.000035
Normal file
Binary file not shown.
BIN
build/extraResources/mysql/data/binlog.000036
Normal file
BIN
build/extraResources/mysql/data/binlog.000036
Normal file
Binary file not shown.
BIN
build/extraResources/mysql/data/binlog.000037
Normal file
BIN
build/extraResources/mysql/data/binlog.000037
Normal file
Binary file not shown.
BIN
build/extraResources/mysql/data/binlog.000038
Normal file
BIN
build/extraResources/mysql/data/binlog.000038
Normal file
Binary file not shown.
BIN
build/extraResources/mysql/data/binlog.000039
Normal file
BIN
build/extraResources/mysql/data/binlog.000039
Normal file
Binary file not shown.
BIN
build/extraResources/mysql/data/binlog.000040
Normal file
BIN
build/extraResources/mysql/data/binlog.000040
Normal file
Binary file not shown.
@@ -7,3 +7,12 @@
|
|||||||
.\binlog.000029
|
.\binlog.000029
|
||||||
.\binlog.000030
|
.\binlog.000030
|
||||||
.\binlog.000031
|
.\binlog.000031
|
||||||
|
.\binlog.000032
|
||||||
|
.\binlog.000033
|
||||||
|
.\binlog.000034
|
||||||
|
.\binlog.000035
|
||||||
|
.\binlog.000036
|
||||||
|
.\binlog.000037
|
||||||
|
.\binlog.000038
|
||||||
|
.\binlog.000039
|
||||||
|
.\binlog.000040
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
build/extraResources/mysql/data/pqs9100/sys_resource_manage.ibd
Normal file
BIN
build/extraResources/mysql/data/pqs9100/sys_resource_manage.ibd
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -23,5 +23,6 @@ VITE_PROXY=[["/api","http://127.0.0.1:18092/"]]
|
|||||||
#VITE_PROXY=[["/api","http://192.168.1.124:18092/"]]
|
#VITE_PROXY=[["/api","http://192.168.1.124:18092/"]]
|
||||||
#VITE_PROXY=[["/api","http://192.168.2.125:18092/"]]
|
#VITE_PROXY=[["/api","http://192.168.2.125:18092/"]]
|
||||||
# VITE_PROXY=[["/api","http://192.168.1.138:8080/"]]张文
|
# VITE_PROXY=[["/api","http://192.168.1.138:8080/"]]张文
|
||||||
|
VITE_IS_SHOW_RAW_DATA=true
|
||||||
# 开启激活验证
|
# 开启激活验证
|
||||||
VITE_ACTIVATE_OPEN=false
|
VITE_ACTIVATE_OPEN=false
|
||||||
@@ -24,5 +24,6 @@ VITE_PWA=true
|
|||||||
# 线上环境接口地址
|
# 线上环境接口地址
|
||||||
#VITE_API_URL="/api" # 打包时用
|
#VITE_API_URL="/api" # 打包时用
|
||||||
VITE_API_URL="http://127.0.0.1:18092/"
|
VITE_API_URL="http://127.0.0.1:18092/"
|
||||||
|
VITE_IS_SHOW_RAW_DATA=true
|
||||||
# 开启激活验证
|
# 开启激活验证
|
||||||
VITE_ACTIVATE_OPEN=false
|
VITE_ACTIVATE_OPEN=false
|
||||||
@@ -4,7 +4,8 @@
|
|||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no, maximum-scale=1.0, minimum-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no, maximum-scale=1.0, minimum-scale=1.0" />
|
||||||
<title></title>
|
<link rel="icon" type="image/x-icon" href="/favicon.ico">
|
||||||
|
<title>NPQS-9100</title>
|
||||||
<!-- 优化:vue渲染未完成之前,先加一个css动画 -->
|
<!-- 优化:vue渲染未完成之前,先加一个css动画 -->
|
||||||
<style>
|
<style>
|
||||||
#loadingPage {
|
#loadingPage {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "frontend",
|
"name": "frontend",
|
||||||
"version": "0.0.1",
|
"version": "2.0.1",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
BIN
frontend/public/favicon.ico
Normal file
BIN
frontend/public/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 21 KiB |
15
frontend/src/api/detection/lock.ts
Normal file
15
frontend/src/api/detection/lock.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import http from '@/api'
|
||||||
|
import type { DetectionLockHolder } from '@/stores/modules/detectionLock'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询当前检测锁持有状态
|
||||||
|
* - data 为 null → 锁空闲
|
||||||
|
* - data 非 null → 锁被某账号持有
|
||||||
|
*
|
||||||
|
* 本接口只读,不抢锁、不会返回 DETECTION_BUSY
|
||||||
|
*/
|
||||||
|
export const getCurrentLock = () => {
|
||||||
|
return http.get<DetectionLockHolder | null>('/detection/lock/current', undefined, {
|
||||||
|
loading: false
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
|
|
||||||
import type { controlSource } from '@/api/device/interface/controlSource'
|
import type { controlSource } from '@/api/device/interface/controlSource'
|
||||||
import http from '@/api'
|
import http from '@/api'
|
||||||
|
import { useDetectionLockStore } from '@/stores/modules/detectionLock'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @name 程控源管理模块
|
* @name 程控源管理模块
|
||||||
@@ -17,8 +18,11 @@ export const startSimulateTest = (params: controlSource.ResControl) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//停止
|
//停止
|
||||||
export const closeSimulateTest = (params: controlSource.ResControl) => {
|
export const closeSimulateTest = async (params: controlSource.ResControl) => {
|
||||||
return http.post(`/prepare/closeSimulateTest`,params,{loading:false})
|
const result = await http.post(`/prepare/closeSimulateTest`,params,{loading:false})
|
||||||
|
// 主动终止 → 释放本地持锁标记
|
||||||
|
useDetectionLockStore().clearHolder()
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -2,43 +2,44 @@ import type { ReqPage, ResPage } from '@/api/interface'
|
|||||||
|
|
||||||
// 检测源模块
|
// 检测源模块
|
||||||
export namespace TestSource {
|
export namespace TestSource {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 检测脚本表格分页查询参数
|
* 检测源表格分页查询参数
|
||||||
*/
|
*/
|
||||||
export interface ReqTestSourceParams extends ReqPage {
|
export interface ReqTestSourceParams extends ReqPage {
|
||||||
id: string; // 装置序号id 必填
|
id: string
|
||||||
name: string;
|
name: string
|
||||||
pattern: string;
|
pattern: string
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检测源接口
|
// 检测源接口
|
||||||
export interface ResTestSource {
|
export interface ResTestSource {
|
||||||
id: string; //检测源ID
|
id: string
|
||||||
name?: string; //检测源名称(检测源类型 + 设备类型 + 数字自动生成)
|
name?: string
|
||||||
pattern: string;//检测源模式(字典表Code字段,数字、模拟、比对)
|
pattern: string
|
||||||
type: string; //检测源类型(字典表Code字段,标准源、高精度设备)
|
type: string
|
||||||
devType: string;//检测源设备类型(字典表Code字段)
|
devType: string
|
||||||
parameter?: string;//源参数(JSON字符串)
|
maxVoltage?: number
|
||||||
state:number;//
|
maxCurrent?: number
|
||||||
createBy?: string;
|
parameter?: string
|
||||||
createTime?: string;
|
state: number
|
||||||
updateBy?: string;
|
createBy?: string
|
||||||
updateTime?: string;
|
createTime?: string
|
||||||
|
updateBy?: string
|
||||||
|
updateTime?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 检测脚本查询分页返回的对象;
|
/*
|
||||||
|
* 检测源查询分页返回的对象
|
||||||
*/
|
*/
|
||||||
export interface ResTestSourcePage extends ResPage<ResTestSource> {
|
export interface ResTestSourcePage extends ResPage<ResTestSource> {}
|
||||||
|
|
||||||
}
|
|
||||||
export interface ParameterType {
|
export interface ParameterType {
|
||||||
id:string;
|
id: string
|
||||||
type:string;
|
type: string
|
||||||
desc:string;
|
desc: string
|
||||||
value:string|null;
|
value: string | null
|
||||||
sort:number;
|
sort: number
|
||||||
pId:string;
|
pId: string
|
||||||
children?:ParameterType[];
|
children?: ParameterType[]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -12,6 +12,12 @@ import { type ResultData } from '@/api/interface'
|
|||||||
import { ResultEnum } from '@/enums/httpEnum'
|
import { ResultEnum } from '@/enums/httpEnum'
|
||||||
import { checkStatus } from './helper/checkStatus'
|
import { checkStatus } from './helper/checkStatus'
|
||||||
import { useUserStore } from '@/stores/modules/user'
|
import { useUserStore } from '@/stores/modules/user'
|
||||||
|
import { useDetectionLockStore, type DetectionLockHolder } from '@/stores/modules/detectionLock'
|
||||||
|
import {
|
||||||
|
showForceReleasedDialog,
|
||||||
|
showLockBusyDialog,
|
||||||
|
showLockNotStartedToast
|
||||||
|
} from '@/utils/detectionLockDialog'
|
||||||
import router from '@/routers'
|
import router from '@/routers'
|
||||||
import { refreshToken } from '@/api/user/login'
|
import { refreshToken } from '@/api/user/login'
|
||||||
import { EventSourcePolyfill } from 'event-source-polyfill'
|
import { EventSourcePolyfill } from 'event-source-polyfill'
|
||||||
@@ -107,6 +113,32 @@ class RequestHttp {
|
|||||||
}
|
}
|
||||||
return Promise.reject(data)
|
return Promise.reject(data)
|
||||||
}
|
}
|
||||||
|
// 单用户检测互斥:命中 DETECTION_BUSY 时根据 data 和本地持锁状态分发到 4 种文案
|
||||||
|
if (data.code === ResultEnum.DETECTION_BUSY) {
|
||||||
|
const lockStore = useDetectionLockStore()
|
||||||
|
const holder = (data.data ?? null) as DetectionLockHolder | null
|
||||||
|
const currentUserId = userStore.userInfo?.id
|
||||||
|
const localDetecting = lockStore.iAmHolder
|
||||||
|
|
||||||
|
if (!localDetecting && holder) {
|
||||||
|
// S1:他人持锁
|
||||||
|
showLockBusyDialog(holder)
|
||||||
|
} else if (!localDetecting && !holder) {
|
||||||
|
// S2:未开始检测就调中间接口
|
||||||
|
showLockNotStartedToast()
|
||||||
|
} else if (localDetecting && holder && holder.holderUserId !== currentUserId) {
|
||||||
|
// S4-a:被强释 + 别人接手
|
||||||
|
showForceReleasedDialog(holder)
|
||||||
|
lockStore.clearHolder()
|
||||||
|
} else if (localDetecting && !holder) {
|
||||||
|
// S4-b:被强释,无人接手
|
||||||
|
showForceReleasedDialog(null)
|
||||||
|
lockStore.clearHolder()
|
||||||
|
}
|
||||||
|
// 阻断默认错误提示,不再走下面的 ElMessage.error
|
||||||
|
return Promise.reject(data)
|
||||||
|
}
|
||||||
|
|
||||||
// 全局错误信息拦截(防止下载文件的时候返回数据流,没有 code 直接报错)
|
// 全局错误信息拦截(防止下载文件的时候返回数据流,没有 code 直接报错)
|
||||||
if (data.code && data.code !== ResultEnum.SUCCESS) {
|
if (data.code && data.code !== ResultEnum.SUCCESS) {
|
||||||
if (data.message.includes('&')) {
|
if (data.message.includes('&')) {
|
||||||
|
|||||||
@@ -69,5 +69,37 @@ export namespace Plan {
|
|||||||
maxTime: number;
|
maxTime: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface PlanStatisticsItem {
|
||||||
|
itemId: string;
|
||||||
|
itemName: string;
|
||||||
|
unqualifiedCount: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PlanStatisticsOption {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PlanStatistics {
|
||||||
|
planId: string;
|
||||||
|
planName: string;
|
||||||
|
totalCheckCount: number;
|
||||||
|
checkedDeviceCount: number;
|
||||||
|
uncheckedDeviceCount: number;
|
||||||
|
firstQualifiedDeviceCount: number;
|
||||||
|
secondQualifiedDeviceCount: number;
|
||||||
|
thirdOrMoreQualifiedDeviceCount: number;
|
||||||
|
qualifiedDeviceCount: number;
|
||||||
|
unqualifiedDeviceCount: number;
|
||||||
|
unqualifiedItemCount: number;
|
||||||
|
firstPassRate: number;
|
||||||
|
secondPassRate: number;
|
||||||
|
thirdOrMorePassRate: number;
|
||||||
|
unqualifiedRate: number;
|
||||||
|
itemDistributions: PlanStatisticsItem[];
|
||||||
|
manufacturerOptions: PlanStatisticsOption[];
|
||||||
|
devTypeOptions: PlanStatisticsOption[];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -94,6 +94,10 @@ export const staticsAnalyse = (params: { id: string[] }) => {
|
|||||||
return http.download('/adPlan/analyse', params)
|
return http.download('/adPlan/analyse', params)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const getPlanStatistics = (params: { planId: string; manufacturer?: string; devType?: string }) => {
|
||||||
|
return http.post<Plan.PlanStatistics>(`/adPlan/statistics`, params)
|
||||||
|
}
|
||||||
|
|
||||||
//根据计划id分页查询被检设
|
//根据计划id分页查询被检设
|
||||||
export const getDevListByPlanId = (params: any) => {
|
export const getDevListByPlanId = (params: any) => {
|
||||||
return http.post(`/adPlan/listDevByPlanId`, params)
|
return http.post(`/adPlan/listDevByPlanId`, params)
|
||||||
|
|||||||
18
frontend/src/api/resourceManage/index.ts
Normal file
18
frontend/src/api/resourceManage/index.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import http from '@/api'
|
||||||
|
import type { ResourceManage } from '@/api/resourceManage/interface'
|
||||||
|
|
||||||
|
export const getResourceManageList = (params: ResourceManage.ReqResourceManageParams) => {
|
||||||
|
return http.post<ResourceManage.ResResourceManagePage>('/resourceManage/list', params)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const addResourceManage = (params: FormData) => {
|
||||||
|
return http.upload('/resourceManage/add', params)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const updateResourceManage = (params: ResourceManage.ReqUpdateResourceManage) => {
|
||||||
|
return http.post('/resourceManage/update', params)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getResourceManagePlayUrl = (id: string) => {
|
||||||
|
return http.get<ResourceManage.PlayVO>(`/resourceManage/play?id=${id}`)
|
||||||
|
}
|
||||||
34
frontend/src/api/resourceManage/interface/index.ts
Normal file
34
frontend/src/api/resourceManage/interface/index.ts
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import type { ReqPage, ResPage } from '@/api/interface'
|
||||||
|
|
||||||
|
export namespace ResourceManage {
|
||||||
|
export interface ReqResourceManageParams extends ReqPage {
|
||||||
|
name?: string
|
||||||
|
fileName?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ResResourceManage {
|
||||||
|
id: string
|
||||||
|
name: string
|
||||||
|
fileName: string
|
||||||
|
fileSize: number
|
||||||
|
relativePath: string
|
||||||
|
remark: string
|
||||||
|
state: number
|
||||||
|
createBy?: string | null
|
||||||
|
createTime?: string | null
|
||||||
|
updateBy?: string | null
|
||||||
|
updateTime?: string | null
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ResResourceManagePage extends ResPage<ResResourceManage> {}
|
||||||
|
|
||||||
|
export interface ReqUpdateResourceManage {
|
||||||
|
id: string
|
||||||
|
name: string
|
||||||
|
remark: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PlayVO {
|
||||||
|
url: string
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,12 @@
|
|||||||
import http from '@/api'
|
import http from '@/api'
|
||||||
|
import { useDetectionLockStore } from '@/stores/modules/detectionLock'
|
||||||
|
|
||||||
|
|
||||||
export const startPreTest = (params) => {
|
export const startPreTest = async (params) => {
|
||||||
return http.post(`/prepare/startPreTest`, params, {loading: false})
|
const result = await http.post(`/prepare/startPreTest`, params, {loading: false})
|
||||||
|
// 抢锁成功 → 标记本地为持锁者
|
||||||
|
useDetectionLockStore().setAsHolder()
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
export const closePreTest = (params) => {
|
export const closePreTest = (params) => {
|
||||||
@@ -37,8 +41,11 @@ export const resumeTest = (params) => {
|
|||||||
* 比对式通道配对
|
* 比对式通道配对
|
||||||
* @param params
|
* @param params
|
||||||
*/
|
*/
|
||||||
export const contrastTest = (params: any) => {
|
export const contrastTest = async (params: any) => {
|
||||||
return http.post(`/prepare/startContrastTest`,params)
|
const result = await http.post(`/prepare/startContrastTest`, params)
|
||||||
|
// 抢锁成功 → 标记本地为持锁者
|
||||||
|
useDetectionLockStore().setAsHolder()
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
export const exportAlignData= () => {
|
export const exportAlignData= () => {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ export enum ResultEnum {
|
|||||||
ERROR = 500,
|
ERROR = 500,
|
||||||
ACCESSTOKEN_EXPIRED = "A0024",
|
ACCESSTOKEN_EXPIRED = "A0024",
|
||||||
OVERDUE = "A0025",
|
OVERDUE = "A0025",
|
||||||
|
DETECTION_BUSY = "A020042",
|
||||||
TIMEOUT = 30000,
|
TIMEOUT = 30000,
|
||||||
TYPE = "success"
|
TYPE = "success"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,3 +19,6 @@ export const DICT_STORE_KEY = "cn-dictData";
|
|||||||
|
|
||||||
export const CHECK_STORE_KEY = "cn-check";
|
export const CHECK_STORE_KEY = "cn-check";
|
||||||
|
|
||||||
|
// pinia中detectionLock store的key
|
||||||
|
export const DETECTION_LOCK_STORE_KEY = "cn-detectionLock";
|
||||||
|
|
||||||
|
|||||||
33
frontend/src/stores/modules/detectionLock.ts
Normal file
33
frontend/src/stores/modules/detectionLock.ts
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import { defineStore } from 'pinia'
|
||||||
|
import { DETECTION_LOCK_STORE_KEY } from '@/stores/constant'
|
||||||
|
|
||||||
|
export interface DetectionLockHolder {
|
||||||
|
holderUserId: string
|
||||||
|
holderUserName: string
|
||||||
|
acquireTime: string
|
||||||
|
expireAt: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useDetectionLockStore = defineStore(DETECTION_LOCK_STORE_KEY, {
|
||||||
|
state: () => ({
|
||||||
|
iAmHolder: false,
|
||||||
|
holder: null as DetectionLockHolder | null
|
||||||
|
}),
|
||||||
|
actions: {
|
||||||
|
setAsHolder(holder?: Partial<DetectionLockHolder> | null) {
|
||||||
|
this.iAmHolder = true
|
||||||
|
if (holder) {
|
||||||
|
this.holder = {
|
||||||
|
holderUserId: holder.holderUserId ?? '',
|
||||||
|
holderUserName: holder.holderUserName ?? '',
|
||||||
|
acquireTime: holder.acquireTime ?? '',
|
||||||
|
expireAt: holder.expireAt ?? ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
clearHolder() {
|
||||||
|
this.iAmHolder = false
|
||||||
|
this.holder = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
85
frontend/src/utils/detectionLockDialog.ts
Normal file
85
frontend/src/utils/detectionLockDialog.ts
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
|
import router from '@/routers'
|
||||||
|
import type { DetectionLockHolder } from '@/stores/modules/detectionLock'
|
||||||
|
import mittBus, { STOP_DETECTION_TIMER_EVENT } from '@/utils/mittBus'
|
||||||
|
import { requestResourceManageAutoplayFirst } from '@/utils/resourceManageAutoplay'
|
||||||
|
|
||||||
|
const stopDetectionTimer = () => {
|
||||||
|
mittBus.emit(STOP_DETECTION_TIMER_EVENT)
|
||||||
|
}
|
||||||
|
|
||||||
|
const goResourceManage = async () => {
|
||||||
|
requestResourceManageAutoplayFirst()
|
||||||
|
if (router.hasRoute('resourceManage')) {
|
||||||
|
await router.push({ name: 'resourceManage' })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
await router.push('/resourceManage')
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* S1: 他人正在做检测, 自己抢锁被挡
|
||||||
|
*/
|
||||||
|
export const showLockBusyDialog = (holder: DetectionLockHolder) => {
|
||||||
|
stopDetectionTimer()
|
||||||
|
|
||||||
|
ElMessageBox.confirm(`「${holder.holderUserName}」正在做检测,请稍后。`, '检测进行中', {
|
||||||
|
confirmButtonText: '观看检测视频教学',
|
||||||
|
cancelButtonText: '我知道了',
|
||||||
|
type: 'warning',
|
||||||
|
distinguishCancelAndClose: true,
|
||||||
|
customClass: 'detection-lock-busy-dialog'
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
return goResourceManage()
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
// 用户点了"我知道了"或关闭,什么都不做
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* S2: 未开始检测就调中间接口
|
||||||
|
*/
|
||||||
|
export const showLockNotStartedToast = () => {
|
||||||
|
ElMessage.warning('请先点击"开始检测"按钮启动本轮检测')
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* S3: 自己暂停超 10 分钟, 被 WS 推 STOP_TIMEOUT 强制结束
|
||||||
|
*/
|
||||||
|
export const showPauseTimeoutDialog = () => {
|
||||||
|
stopDetectionTimer()
|
||||||
|
|
||||||
|
ElMessageBox.alert('暂停超过 10 分钟未恢复,系统已自动结束本次检测。\n\n如需继续,请重新发起检测。', '本次检测已结束', {
|
||||||
|
confirmButtonText: '我知道了',
|
||||||
|
type: 'warning'
|
||||||
|
}).catch(() => {})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* S4: 被管理员强制释放
|
||||||
|
* - holder 为 null: 强释后无人接手
|
||||||
|
* - holder 非 null: 强释后被别人立刻抢占
|
||||||
|
*/
|
||||||
|
export const showForceReleasedDialog = (holder: DetectionLockHolder | null) => {
|
||||||
|
stopDetectionTimer()
|
||||||
|
|
||||||
|
if (holder) {
|
||||||
|
ElMessageBox.confirm(`当前「${holder.holderUserName}」正在做检测,您无法继续检测,请稍后。`, '检测已被中止', {
|
||||||
|
confirmButtonText: '观看检测视频教学',
|
||||||
|
cancelButtonText: '我知道了',
|
||||||
|
type: 'warning',
|
||||||
|
distinguishCancelAndClose: true
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
return goResourceManage()
|
||||||
|
})
|
||||||
|
.catch(() => {})
|
||||||
|
} else {
|
||||||
|
ElMessageBox.alert('您的检测已被管理员强制结束。\n如需继续,请重新发起检测。', '检测已被中止', {
|
||||||
|
confirmButtonText: '我知道了',
|
||||||
|
type: 'warning'
|
||||||
|
}).catch(() => {})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,11 @@
|
|||||||
import mitt from "mitt";
|
import mitt from "mitt";
|
||||||
|
|
||||||
const mittBus = mitt();
|
export const STOP_DETECTION_TIMER_EVENT = "stopDetectionTimer";
|
||||||
|
|
||||||
|
type MittBusEvents = {
|
||||||
|
openThemeDrawer: undefined;
|
||||||
|
[STOP_DETECTION_TIMER_EVENT]: undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
const mittBus = mitt<MittBusEvents>();
|
||||||
export default mittBus;
|
export default mittBus;
|
||||||
|
|||||||
13
frontend/src/utils/resourceManageAutoplay.ts
Normal file
13
frontend/src/utils/resourceManageAutoplay.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
let shouldAutoplayFirstVideo = false
|
||||||
|
|
||||||
|
export const requestResourceManageAutoplayFirst = () => {
|
||||||
|
shouldAutoplayFirstVideo = true
|
||||||
|
}
|
||||||
|
|
||||||
|
export const hasPendingResourceManageAutoplayFirst = () => shouldAutoplayFirstVideo
|
||||||
|
|
||||||
|
export const consumeResourceManageAutoplayFirst = () => {
|
||||||
|
if (!shouldAutoplayFirstVideo) return false
|
||||||
|
shouldAutoplayFirstVideo = false
|
||||||
|
return true
|
||||||
|
}
|
||||||
@@ -9,6 +9,8 @@
|
|||||||
|
|
||||||
import { ElMessage } from "element-plus";
|
import { ElMessage } from "element-plus";
|
||||||
import { jwtUtil } from "./jwtUtil";
|
import { jwtUtil } from "./jwtUtil";
|
||||||
|
import { useDetectionLockStore } from "@/stores/modules/detectionLock";
|
||||||
|
import { showPauseTimeoutDialog } from "@/utils/detectionLockDialog";
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// 类型定义 (Types & Interfaces)
|
// 类型定义 (Types & Interfaces)
|
||||||
@@ -190,8 +192,8 @@ export default class SocketService {
|
|||||||
* WebSocket连接配置
|
* WebSocket连接配置
|
||||||
*/
|
*/
|
||||||
private config: SocketConfig = {
|
private config: SocketConfig = {
|
||||||
|
// url: 'ws://127.0.0.1:7778/hello',
|
||||||
url: 'ws://127.0.0.1:7778/hello',
|
url: 'ws://127.0.0.1:7778/hello',
|
||||||
//url: 'ws://192.168.1.124:7777/hello',
|
|
||||||
heartbeatInterval: 9000, // 9秒心跳间隔
|
heartbeatInterval: 9000, // 9秒心跳间隔
|
||||||
reconnectDelay: 5000, // 5秒重连延迟
|
reconnectDelay: 5000, // 5秒重连延迟
|
||||||
maxReconnectAttempts: 5, // 最多重连5次
|
maxReconnectAttempts: 5, // 最多重连5次
|
||||||
@@ -546,6 +548,18 @@ export default class SocketService {
|
|||||||
// 检查是否为JSON格式
|
// 检查是否为JSON格式
|
||||||
if (typeof event.data === 'string' && (event.data.startsWith('{') || event.data.startsWith('['))) {
|
if (typeof event.data === 'string' && (event.data.startsWith('{') || event.data.startsWith('['))) {
|
||||||
const message: WebSocketMessage = JSON.parse(event.data);
|
const message: WebSocketMessage = JSON.parse(event.data);
|
||||||
|
|
||||||
|
// 全局拦截:暂停 10 分钟超时,后端推 STOP_TIMEOUT 表示锁已被释放
|
||||||
|
// 不管页面是否注册了对应回调,都要做"清本地持锁标记 + 弹窗告知"
|
||||||
|
if (message?.operateCode === 'STOP_TIMEOUT') {
|
||||||
|
try {
|
||||||
|
useDetectionLockStore().clearHolder();
|
||||||
|
showPauseTimeoutDialog();
|
||||||
|
} catch (e) {
|
||||||
|
console.error('STOP_TIMEOUT 全局处理失败:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (message?.type && this.callBackMapping[message.type]) {
|
if (message?.type && this.callBackMapping[message.type]) {
|
||||||
this.callBackMapping[message.type](message);
|
this.callBackMapping[message.type](message);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -122,7 +122,8 @@
|
|||||||
<script lang="tsx" setup name="test">
|
<script lang="tsx" setup name="test">
|
||||||
import {InfoFilled, Loading} from '@element-plus/icons-vue'
|
import {InfoFilled, Loading} from '@element-plus/icons-vue'
|
||||||
import CompareDataCheckSingleChannelSingleTestPopup from './compareDataCheckSingleChannelSingleTestPopup.vue'
|
import CompareDataCheckSingleChannelSingleTestPopup from './compareDataCheckSingleChannelSingleTestPopup.vue'
|
||||||
import {computed, ComputedRef, nextTick, onBeforeMount, onMounted, reactive, ref, toRef, watch} from 'vue'
|
import {computed, nextTick, onBeforeMount, onBeforeUnmount, onMounted, reactive, ref, toRef, watch} from 'vue'
|
||||||
|
import type { ComputedRef } from 'vue'
|
||||||
import {dialogBig} from '@/utils/elementBind'
|
import {dialogBig} from '@/utils/elementBind'
|
||||||
import {CheckData} from '@/api/check/interface'
|
import {CheckData} from '@/api/check/interface'
|
||||||
import {useCheckStore} from '@/stores/modules/check'
|
import {useCheckStore} from '@/stores/modules/check'
|
||||||
@@ -132,6 +133,7 @@ import {getAutoGenerate, getCanCoefficient, startCoefficient} from '@/api/user/l
|
|||||||
import { generateDevReport } from '@/api/plan/plan'
|
import { generateDevReport } from '@/api/plan/plan'
|
||||||
import {useModeStore} from '@/stores/modules/mode' // 引入模式 store
|
import {useModeStore} from '@/stores/modules/mode' // 引入模式 store
|
||||||
import {useDictStore} from '@/stores/modules/dict'
|
import {useDictStore} from '@/stores/modules/dict'
|
||||||
|
import mittBus, { STOP_DETECTION_TIMER_EVENT } from '@/utils/mittBus'
|
||||||
const checkStore = useCheckStore()
|
const checkStore = useCheckStore()
|
||||||
const modeStore = useModeStore()
|
const modeStore = useModeStore()
|
||||||
const dictStore = useDictStore()
|
const dictStore = useDictStore()
|
||||||
@@ -740,6 +742,10 @@ const stopTimeCount = (type: number) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleStopDetectionTimer = () => {
|
||||||
|
stopTimeCount(1)
|
||||||
|
}
|
||||||
|
|
||||||
// 将秒数转换为 HH:MM:SS 格式
|
// 将秒数转换为 HH:MM:SS 格式
|
||||||
const secondToTime = (second: number) => {
|
const secondToTime = (second: number) => {
|
||||||
let h: string | number = Math.floor(second / 3600) // 小时
|
let h: string | number = Math.floor(second / 3600) // 小时
|
||||||
@@ -898,6 +904,7 @@ const initializeParameters = async () => {
|
|||||||
|
|
||||||
//
|
//
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
mittBus.on(STOP_DETECTION_TIMER_EVENT, handleStopDetectionTimer)
|
||||||
|
|
||||||
if (!checkStore.selectTestItems.preTest) {
|
if (!checkStore.selectTestItems.preTest) {
|
||||||
// 判断是否预检测
|
// 判断是否预检测
|
||||||
@@ -905,6 +912,10 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
mittBus.off(STOP_DETECTION_TIMER_EVENT, handleStopDetectionTimer)
|
||||||
|
})
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
initializeParameters,
|
initializeParameters,
|
||||||
handlePause,
|
handlePause,
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user