20 Commits

Author SHA1 Message Date
caozehui
cf3141198b 微调 2026-06-01 09:26:01 +08:00
caozehui
c05d329614 补充观看教学视频路由跳转功能,检测页面微调 2026-05-29 10:39:12 +08:00
caozehui
ee08263e4a 检测计划统计弹窗下拉框内容调整 2026-05-29 09:58:24 +08:00
19ea08d5e0 feat(detection): 添加检测锁机制防止多用户同时操作
- 新增 detectionLock store 管理检测锁状态
- 实现检测锁相关的弹窗提示功能
- 添加 DETECTION_BUSY 错误码处理多人竞争逻辑
- 在 websocket 中集成检测锁超时处理
- 修改程序源控制接口以同步锁状态
- 更新项目标题和图标配置
- 添加 docs 目录到忽略列表
2026-05-28 20:44:53 +08:00
caozehui
f9809197e8 微调 2026-05-28 16:33:07 +08:00
caozehui
0090a922c6 资源管理页面微调 2026-05-28 14:37:40 +08:00
caozehui
0b26de20b9 资源管理 2026-05-28 13:26:35 +08:00
caozehui
1202f64bfc 检测计划统计功能 2026-05-28 08:44:15 +08:00
caozehui
ce1738daf0 微调 2026-05-27 11:20:12 +08:00
caozehui
a41d824ca3 归档 2026-05-26 15:45:08 +08:00
caozehui
ac5a8450e8 微调 2026-05-26 14:23:59 +08:00
caozehui
01e817a5d6 微调 2026-05-26 13:45:23 +08:00
caozehui
01bf07fc42 检测计划统计功能 2026-05-26 09:22:38 +08:00
caozehui
633e914c9a Revert "下拉多选报告模版"
This reverts commit 37e69e7bda.
2026-05-25 18:38:46 +08:00
caozehui
37e69e7bda 下拉多选报告模版 2026-05-25 14:25:57 +08:00
caozehui
19fb90432a 归档 2026-05-25 09:51:42 +08:00
caozehui
4a3c81a792 归档 2026-05-25 09:50:16 +08:00
caozehui
12d3073241 统一sourceId 2026-05-13 09:47:10 +08:00
caozehui
72838462ad 微调 2026-04-22 10:02:21 +08:00
caozehui
327addf625 微调 2026-04-22 09:58:03 +08:00
105 changed files with 1569 additions and 229 deletions

1
.gitignore vendored
View File

@@ -9,3 +9,4 @@ public/electron/
pnpm-lock.yaml
CLAUDE.md
/public/dist/
/docs/

View File

@@ -33,9 +33,9 @@ mybatis-plus:
#驼峰命名
map-underscore-to-camel-case: true
#配置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:
db-config:
#指定主键生成策略
@@ -55,30 +55,32 @@ webSocket:
#源参数下发,暂态数据默认值
Dip:
#暂态前时间s
fPreTime: 2f
# 暂态前时间s
# fPreTime: 2f
#写入时间s
fRampIn: 0.001f
#写出时间s
fRampOut: 0.001f
#暂态后时间s
fAfterTime: 3f
# 暂态后时间s
# fAfterTime: 3f
Flicker:
waveFluType: CPM
waveType: SQU
fDutyCycle: 50f
#Flicker:
# waveFluType: CPM
# waveType: SQU
# fDutyCycle: 50f
log:
homeDir: {{APP_DATA_PATH}}\logs
commonLevel: info
#log:
# homeDir: D:\logs
# commonLevel: info
report:
template: {{APP_DATA_PATH}}\template
reportDir: {{APP_DATA_PATH}}\report
# template: D:\template
# reportDir: D:\report
dateFormat: yyyy年MM月dd日
data:
homeDir: {{APP_DATA_PATH}}\data
#data:
# homeDir: D:\data
#resource:
# videoDir: ${data.homeDir}\resources\videos
qr:
cloud: http://pqmcc.com:18082/api/file
dev:

View File

@@ -1 +1 @@
11900
95428

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -11,3 +11,6 @@
.\binlog.000033
.\binlog.000034
.\binlog.000035
.\binlog.000036
.\binlog.000037
.\binlog.000038

View File

@@ -24,4 +24,4 @@ VITE_PROXY=[["/api","http://127.0.0.1:18093/"]]
#VITE_PROXY=[["/api","http://192.168.2.125:18092/"]]
# VITE_PROXY=[["/api","http://192.168.1.138:8080/"]]张文
# 开启激活验证
VITE_ACTIVATE_OPEN=true
VITE_ACTIVATE_OPEN=false

View File

@@ -25,4 +25,4 @@ VITE_PWA=true
#VITE_API_URL="/api" # 打包时用
VITE_API_URL="http://127.0.0.1:18093/"
# 开启激活验证
VITE_ACTIVATE_OPEN=true
VITE_ACTIVATE_OPEN=false

View File

@@ -4,7 +4,8 @@
<meta charset="utf-8">
<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" />
<title></title>
<link rel="icon" type="image/x-icon" href="/favicon.ico">
<title>NPQS-9100</title>
<!-- 优化vue渲染未完成之前先加一个css动画 -->
<style>
#loadingPage {

BIN
frontend/public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

View 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
})
}

View File

@@ -1,6 +1,7 @@
import type { controlSource } from '@/api/device/interface/controlSource'
import http from '@/api'
import { useDetectionLockStore } from '@/stores/modules/detectionLock'
/**
* @name 程控源管理模块
@@ -17,8 +18,11 @@ export const startSimulateTest = (params: controlSource.ResControl) => {
}
//停止
export const closeSimulateTest = (params: controlSource.ResControl) => {
return http.post(`/prepare/closeSimulateTest`,params,{loading:false})
export const closeSimulateTest = async (params: controlSource.ResControl) => {
const result = await http.post(`/prepare/closeSimulateTest`,params,{loading:false})
// 主动终止 → 释放本地持锁标记
useDetectionLockStore().clearHolder()
return result
}

View File

@@ -2,43 +2,44 @@ import type { ReqPage, ResPage } from '@/api/interface'
// 检测源模块
export namespace TestSource {
/**
* 检测脚本表格分页查询参数
*/
export interface ReqTestSourceParams extends ReqPage{
id: string; // 装置序号id 必填
name: string;
pattern: string;
}
/**
* 检测源表格分页查询参数
*/
export interface ReqTestSourceParams extends ReqPage {
id: string
name: string
pattern: string
}
// 检测源接口
export interface ResTestSource {
id: string; //检测源ID
name?: string; //检测源名称(检测源类型 + 设备类型 + 数字自动生成)
pattern: string;//检测源模式(字典表Code字段数字、模拟、比对)
type: string; //检测源类型(字典表Code字段标准源、高精度设备)
devType: string;//检测源设备类型(字典表Code字段)
parameter?: string;//源参数JSON字符串
state:number;//
createBy?: string;
createTime?: string;
updateBy?: string;
updateTime?: string;
id: string
name?: string
pattern: string
type: string
devType: string
maxVoltage?: number
maxCurrent?: number
parameter?: string
state: number
createBy?: string
createTime?: string
updateBy?: string
updateTime?: string
}
/* 检测脚本查询分页返回的对象;
*/
export interface ResTestSourcePage extends ResPage<ResTestSource> {
/*
* 检测源查询分页返回的对象
*/
export interface ResTestSourcePage extends ResPage<ResTestSource> {}
}
export interface ParameterType{
id:string;
type:string;
desc:string;
value:string|null;
sort:number;
pId:string;
children?:ParameterType[];
export interface ParameterType {
id: string
type: string
desc: string
value: string | null
sort: number
pId: string
children?: ParameterType[]
}
}
}

View File

@@ -12,6 +12,12 @@ import { type ResultData } from '@/api/interface'
import { ResultEnum } from '@/enums/httpEnum'
import { checkStatus } from './helper/checkStatus'
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 { refreshToken } from '@/api/user/login'
import { EventSourcePolyfill } from 'event-source-polyfill'
@@ -107,6 +113,32 @@ class RequestHttp {
}
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 直接报错)
if (data.code && data.code !== ResultEnum.SUCCESS) {
if (data.message.includes('&')) {

View File

@@ -69,5 +69,37 @@ export namespace Plan {
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[];
}
}

View File

@@ -94,6 +94,10 @@ export const staticsAnalyse = (params: { id: string[] }) => {
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分页查询被检设
export const getDevListByPlanId = (params: any) => {
return http.post(`/adPlan/listDevByPlanId`, params)

View 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}`)
}

View 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
}
}

View File

@@ -1,8 +1,12 @@
import http from '@/api'
import { useDetectionLockStore } from '@/stores/modules/detectionLock'
export const startPreTest = (params) => {
return http.post(`/prepare/startPreTest`, params, {loading: false})
export const startPreTest = async (params) => {
const result = await http.post(`/prepare/startPreTest`, params, {loading: false})
// 抢锁成功 → 标记本地为持锁者
useDetectionLockStore().setAsHolder()
return result
}
export const closePreTest = (params) => {
@@ -37,8 +41,11 @@ export const resumeTest = (params) => {
* 比对式通道配对
* @param params
*/
export const contrastTest = (params: any) => {
return http.post(`/prepare/startContrastTest`,params)
export const contrastTest = async (params: any) => {
const result = await http.post(`/prepare/startContrastTest`, params)
// 抢锁成功 → 标记本地为持锁者
useDetectionLockStore().setAsHolder()
return result
}
export const exportAlignData= () => {

View File

@@ -6,6 +6,7 @@ export enum ResultEnum {
ERROR = 500,
ACCESSTOKEN_EXPIRED = "A0024",
OVERDUE = "A0025",
DETECTION_BUSY = "A020042",
TIMEOUT = 30000,
TYPE = "success"
}

View File

@@ -19,3 +19,6 @@ export const DICT_STORE_KEY = "cn-dictData";
export const CHECK_STORE_KEY = "cn-check";
// pinia中detectionLock store的key
export const DETECTION_LOCK_STORE_KEY = "cn-detectionLock";

View 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
}
}
})

View File

@@ -0,0 +1,83 @@
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'
const stopDetectionTimer = () => {
mittBus.emit(STOP_DETECTION_TIMER_EVENT)
}
const goResourceManage = async () => {
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(() => {})
}
}

View File

@@ -1,4 +1,11 @@
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;

View File

@@ -9,6 +9,8 @@
import { ElMessage } from "element-plus";
import { jwtUtil } from "./jwtUtil";
import { useDetectionLockStore } from "@/stores/modules/detectionLock";
import { showPauseTimeoutDialog } from "@/utils/detectionLockDialog";
// ============================================================================
// 类型定义 (Types & Interfaces)
@@ -190,8 +192,8 @@ export default class SocketService {
* WebSocket连接配置
*/
private config: SocketConfig = {
url: 'ws://127.0.0.1:7778/hello',
//url: 'ws://192.168.1.124:7777/hello',
// url: 'ws://127.0.0.1:7778/hello',
url: 'ws://127.0.0.1:7777/hello',
heartbeatInterval: 9000, // 9秒心跳间隔
reconnectDelay: 5000, // 5秒重连延迟
maxReconnectAttempts: 5, // 最多重连5次
@@ -546,6 +548,18 @@ export default class SocketService {
// 检查是否为JSON格式
if (typeof event.data === 'string' && (event.data.startsWith('{') || event.data.startsWith('['))) {
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]) {
this.callBackMapping[message.type](message);
} else {

View File

@@ -122,7 +122,8 @@
<script lang="tsx" setup name="test">
import {InfoFilled, Loading} from '@element-plus/icons-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 {CheckData} from '@/api/check/interface'
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 {useModeStore} from '@/stores/modules/mode' // 引入模式 store
import {useDictStore} from '@/stores/modules/dict'
import mittBus, { STOP_DETECTION_TIMER_EVENT } from '@/utils/mittBus'
const checkStore = useCheckStore()
const modeStore = useModeStore()
const dictStore = useDictStore()
@@ -740,6 +742,10 @@ const stopTimeCount = (type: number) => {
}
}
const handleStopDetectionTimer = () => {
stopTimeCount(1)
}
// 将秒数转换为 HH:MM:SS 格式
const secondToTime = (second: number) => {
let h: string | number = Math.floor(second / 3600) // 小时
@@ -898,6 +904,7 @@ const initializeParameters = async () => {
//
onMounted(() => {
mittBus.on(STOP_DETECTION_TIMER_EVENT, handleStopDetectionTimer)
if (!checkStore.selectTestItems.preTest) {
// 判断是否预检测
@@ -905,6 +912,10 @@ onMounted(() => {
}
})
onBeforeUnmount(() => {
mittBus.off(STOP_DETECTION_TIMER_EVENT, handleStopDetectionTimer)
})
defineExpose({
initializeParameters,
handlePause,

View File

@@ -148,6 +148,7 @@ import { useCheckStore } from '@/stores/modules/check'
import { contrastTest, pauseTest, resumeTest, startPreTest } from '@/api/socket/socket'
import { useUserStore } from '@/stores/modules/user'
import { JwtUtil } from '@/utils/jwtUtil'
import mittBus, { STOP_DETECTION_TIMER_EVENT } from '@/utils/mittBus'
const userStore = useUserStore()
const checkStore = useCheckStore()
@@ -165,6 +166,14 @@ const preTestStatus = ref('waiting') //预检测执行状态
const TestStatus = ref('waiting') //正式检测执行状态
const webMsgSend = ref() //webSocket推送的数据
const hideInitializingButton = () => {
if (TestStatus.value === 'test_init') {
TestStatus.value = 'waiting'
}
}
mittBus.on(STOP_DETECTION_TIMER_EVENT, hideInitializingButton)
const dialogTitle = ref('')
const showComponent = ref(true)
const preTestRef = ref<InstanceType<typeof ComparePreTest> | null>(null)
@@ -187,6 +196,7 @@ onMounted(() => {
})
onUnmounted(() => {
mittBus.off(STOP_DETECTION_TIMER_EVENT, hideInitializingButton)
window.removeEventListener('resize', handleResize)
})

View File

@@ -206,6 +206,20 @@ function handleWarningError(stepRef: any, logRef: any, message: string) {
watch(webMsgSend, function (newValue, oldValue) {
if (testStatus.value !== 'waiting') {
switch (newValue.requestId) {
case 'overloadTest':
if (newValue.code === 1) {
handleFatalError(step1, step1InitLog, '电压过载!')
}
if (newValue.code === 2) {
handleFatalError(step1, step1InitLog, '电流过载!')
}
if (newValue.code === 3) {
handleFatalError(step1, step1InitLog, '电压和电流过载!')
}
if (newValue.code === 4) {
handleFatalError(step1, step1InitLog, '过载检测成功!')
}
break;
case 'yjc_ytxjy':
switch (newValue.operateCode) {
case 'INIT_GATHER':

View File

@@ -91,7 +91,7 @@
type="primary"
icon="Clock"
@click="handleTest('手动检测')"
v-if="form.activeTabs === 0 && modeStore.currentMode == '模拟式'"
v-if="form.activeTabs === 0 && modeStore.currentMode != '比对式'"
>
手动检测
</el-button>
@@ -483,7 +483,7 @@ const columns = reactive<ColumnProps<Device.ResPqDev>[]>([
sortable: true,
isShow: checkStateShow,
render: scope => {
return scope.row.checkState === 0 ? '未检' : scope.row.checkState === 1 ? '检测中' : '检测完成'
return scope.row.checkState === 0 ? '未检' : scope.row.checkState === 1 ? '检测中' : scope.row.checkState === 2 ? '检测完成':'归档'
}
},
{
@@ -494,10 +494,12 @@ const columns = reactive<ColumnProps<Device.ResPqDev>[]>([
render: scope => {
if (scope.row.checkResult === 0) {
return <el-tag type="danger">不符合</el-tag>
} else if (scope.row.checkResult === 0) {
return '不符合'
} else if (scope.row.checkResult === 1) {
return '符合'
} else if (scope.row.checkResult === 2) {
return '未检'
}else if(scope.row.checkResult === 2) {
return '未检'
}
return ''
}
@@ -539,7 +541,6 @@ const columns = reactive<ColumnProps<Device.ResPqDev>[]>([
{ prop: 'operation', label: '操作', fixed: 'right', minWidth :200,isShow: operationShow }
])
let testType = 'test' // 检测类型:'test'-检测 'reTest'-复检
let qualifiedCount = 0 //合格数量
//比对单个报告生成
@@ -575,8 +576,6 @@ const handleSelectionChange = (selection: any[]) => {
} else {
testType = 'reTest'
}
qualifiedCount=selection.filter(item => item.checkResult == 1).length
let devices: CheckData.Device[] = selection.map((item: any) => {
return {
deviceId: item.id,
@@ -599,6 +598,19 @@ const handleSelectionChange = (selection: any[]) => {
}
}
const isUncheckedDevice = (device: Device.ResPqDev) => Number(device.checkState) === 0 || Number(device.checkResult) === 2
const hasCheckedSelectedDevice = () => channelsSelection.value.some(device => !isUncheckedDevice(device))
const hasUncheckedSelectedDevice = () => channelsSelection.value.some(device => isUncheckedDevice(device))
const hasCheckedUnqualifiedSelectedDevice = () =>
channelsSelection.value.some(device => !isUncheckedDevice(device) && Number(device.checkResult) === 0)
const shouldShowRecheckModeDialog = () => hasCheckedSelectedDevice()
const canUseUnqualifiedItemRecheck = () => hasCheckedUnqualifiedSelectedDevice() && !hasUncheckedSelectedDevice()
//查询
const handleSearch = () => {
proTable.value?.getTableList()
@@ -923,12 +935,12 @@ const handleTest = async (val: string) => {
dialogTitle.value = val
if (val === '手动检测') {
checkStore.setShowDetailType(2)
if (testType === 'reTest') {
if (shouldShowRecheckModeDialog()) {
ElMessageBox.confirm('请选择复检检测方式', '设备复检', {
distinguishCancelAndClose: true,
confirmButtonText: '不合格项复检',
cancelButtonText: '全部复检',
showConfirmButton:qualifiedCount<=0,
showConfirmButton: canUseUnqualifiedItemRecheck(),
type: 'warning'
})
.then(() => {
@@ -963,11 +975,12 @@ const handleTest = async (val: string) => {
checkStore.setCheckType(1)
checkStore.initSelectTestItems()
// 一键检测
if (testType === 'reTest' && modeStore.currentMode != '比对式') {
if (shouldShowRecheckModeDialog() && modeStore.currentMode != '比对式') {
ElMessageBox.confirm('请选择复检检测方式', '设备复检', {
distinguishCancelAndClose: true,
confirmButtonText: '不合格项复检',
cancelButtonText: '全部复检',
showConfirmButton: canUseUnqualifiedItemRecheck(),
type: 'warning'
})
.then(() => {
@@ -1087,7 +1100,7 @@ const openDrawer = async (title: string, row: any) => {
if (title === '检测数据查询') {
checkStore.setShowDetailType(0)
if (modeStore.currentMode == '模拟式') {
if (modeStore.currentMode == '模拟式'||modeStore.currentMode == '数字式') {
dataCheckPopupRef.value?.open(row.id, '-1', null)
} else if (modeStore.currentMode == '比对式') {
dataCheckSingleChannelSingleTestPopupRef.value?.open(row, null, row.id, 2)
@@ -1095,7 +1108,7 @@ const openDrawer = async (title: string, row: any) => {
}
if (title === '误差体系更换') {
checkStore.setShowDetailType(1)
if (modeStore.currentMode == '模拟式') {
if (modeStore.currentMode == '模拟式'||modeStore.currentMode == '数字式') {
dataCheckPopupRef.value?.open(row.id, '-1', null)
} else if (modeStore.currentMode == '比对式') {
dataCheckSingleChannelSingleTestPopupRef.value?.open(row, null, row.id, 2)

View File

@@ -105,7 +105,7 @@ import { InfoFilled, Loading } from '@element-plus/icons-vue'
// 单通道单测试项详情弹窗组件
import dataCheckSingleChannelSingleTestPopup from './dataCheckSingleChannelSingleTestPopup.vue'
// Vue 3 Composition API
import { computed, reactive, ref, toRef, watch } from 'vue'
import { computed, onBeforeUnmount, onMounted, reactive, ref, toRef, watch } from 'vue'
// 对话框大小绑定工具
import { dialogBig } from '@/utils/elementBind'
// 检测数据类型定义
@@ -120,6 +120,7 @@ import { getAutoGenerate } from '@/api/user/login'
import { generateDevReport } from '@/api/plan/plan'
import { useModeStore } from '@/stores/modules/mode' // 引入模式 store
import { useDictStore } from '@/stores/modules/dict'
import mittBus, { STOP_DETECTION_TIMER_EVENT } from '@/utils/mittBus'
// 获取检测状态管理实例
const checkStore = useCheckStore()
@@ -1176,6 +1177,10 @@ const stopTimeCount = () => {
}
}
const handleStopDetectionTimer = () => {
stopTimeCount()
}
// 恢复计时(用于暂停后继续)
const resumeTimeCount = () => {
@@ -1199,8 +1204,14 @@ const secondToTime = (second: number) => {
return h + ':' + m + ':' + s
}
onMounted(() => {
mittBus.on(STOP_DETECTION_TIMER_EVENT, handleStopDetectionTimer)
})
// 组件卸载前清理定时器和响应式引用
onBeforeUnmount(() => {
mittBus.off(STOP_DETECTION_TIMER_EVENT, handleStopDetectionTimer)
// 清理定时器
if (timer) {
clearInterval(timer)

View File

@@ -172,6 +172,7 @@ import { useCheckStore } from '@/stores/modules/check'
import { pauseTest, resumeTest, startPreTest } from '@/api/socket/socket'
import { useUserStore } from '@/stores/modules/user'
import { JwtUtil } from '@/utils/jwtUtil'
import mittBus, { STOP_DETECTION_TIMER_EVENT } from '@/utils/mittBus'
// ====================== 状态管理 ======================
const userStore = useUserStore()
@@ -200,6 +201,14 @@ const channelsTestStatus = ref('waiting') // 通道系数校准执行状态
const TestStatus = ref('waiting') // 正式检测执行状态
const webMsgSend = ref() // webSocket推送的数据用于组件间通信
const hideInitializingButton = () => {
if (TestStatus.value === 'test_init') {
TestStatus.value = 'waiting'
}
}
mittBus.on(STOP_DETECTION_TIMER_EVENT, hideInitializingButton)
// ====================== WebSocket 相关 ======================
const dataSocket = reactive<{
socketServe: typeof socketClient.Instance | null
@@ -705,6 +714,7 @@ const handleClose = () => {
* 确保路由切换或组件销毁时正确关闭WebSocket连接
*/
onBeforeUnmount(() => {
mittBus.off(STOP_DETECTION_TIMER_EVENT, hideInitializingButton)
closeWebSocket() // 组件销毁前关闭WebSocket连接
})

View File

@@ -29,7 +29,7 @@
@node-click="handleNodeClick"
>
<template #default="{ node, data }">
<span class="custom-tree-node" style="display: flex; align-items: center;">
<span class="custom-tree-node">
<!-- 父节点图标 -->
<Platform
v-if="!data.pid"
@@ -39,50 +39,52 @@
}"
/>
<!-- 节点名称 -->
<span>{{ node.label }}</span>
<!-- 子节点右侧图标 + tooltip -->
<el-tooltip
v-if="
node.label != '未检' &&
node.label != '检测中' &&
node.label != '检测完成' &&
hasChildrenInPlanTable(node.data)
"
placement="top"
:manual="true"
content="子计划信息"
>
<List
@click.stop="childDetail(node.data)"
style="
width: 16px;
height: 16px;
margin-left: 8px;
cursor: pointer;
color: var(--el-color-primary);
"
<span class="node-label">{{ node.label }}</span>
<span class="node-actions">
<PieChart
v-if="isCompletedPlanNode(node.data)"
class="node-action-icon"
@click.stop="openStatistics(node.data)"
style="margin-right: 8px"
/>
</el-tooltip>
<!-- 子节点右侧图标 + tooltip -->
<el-tooltip
v-if="
node.label != '未检' &&
node.label != '检测中' &&
node.label != '检测完成' &&
hasChildrenInPlanTable(node.data)
"
placement="top"
:manual="true"
content="子计划信息"
>
<List class="node-action-icon" @click.stop="childDetail(node.data)" />
</el-tooltip>
</span>
</span>
</template>
</el-tree>
</div>
</div>
<SourceOpen ref="openSourceView" :width="width" :height="height + 175"></SourceOpen>
<PlanStatisticsPopup ref="planStatisticsPopupRef" />
</template>
<script lang="ts" setup>
import { type Plan } from '@/api/plan/interface'
import { List, Menu, Platform } from '@element-plus/icons-vue'
import { List, Menu, PieChart, Platform } from '@element-plus/icons-vue'
import { nextTick, onMounted, ref, watch } from 'vue'
import { useRouter } from 'vue-router'
import { useCheckStore } from '@/stores/modules/check'
import { ElTooltip } from 'element-plus'
import SourceOpen from '@/views/plan/planList/components/childrenPlan.vue'
import PlanStatisticsPopup from '@/views/plan/planList/components/planStatisticsPopup.vue'
import { getPlanList } from '@/api/plan/plan.ts'
import { useModeStore } from '@/stores/modules/mode' // 引入模式 store
import { useDictStore } from '@/stores/modules/dict'
const openSourceView = ref()
const planStatisticsPopupRef = ref<InstanceType<typeof PlanStatisticsPopup> | null>(null)
const router = useRouter()
const checkStore = useCheckStore()
const filterText = ref('')
@@ -211,6 +213,14 @@ const childDetail = (data: Plan.ResPlan) => {
}
}
const isCompletedPlanNode = (data: Partial<Plan.ResPlan>) => {
return [1, 2].includes(Number(data.testState))
}
const openStatistics = (data: Partial<Plan.ResPlan>) => {
planStatisticsPopupRef.value?.open(data)
}
function buildTree(flatList: any[]): any[] {
const map = new Map()
const tree: any[] = []
@@ -293,6 +303,40 @@ defineExpose({ getTreeData, clickTableToTree })
margin-top: 12px;
}
:deep(.el-tree-node__content) {
padding-right: 6px;
}
.custom-tree-node {
display: flex;
align-items: center;
width: 100%;
min-width: 0;
}
.node-label {
flex: 1;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.node-actions {
flex: none;
display: inline-flex;
align-items: center;
gap: 8px;
margin-left: 8px;
}
.node-action-icon {
width: 16px;
height: 16px;
cursor: pointer;
color: var(--el-color-primary);
}
//.filter-tree span {
// font-size: 16px;
// display:block;

View File

@@ -228,7 +228,7 @@ const unit = [
},
{
label: '功率',
unit: 'W'
unit: props.valueCode == 'Absolute' ? 'W' : '%Un*In'
},
{
label: '电压偏差',

View File

@@ -10,7 +10,7 @@
class="form-three"
>
<el-form-item label="设备类型" prop="devType">
<el-select v-model="formContent.devType" placeholder="请选择源型">
<el-select v-model="formContent.devType" placeholder="请选择源设备类型">
<el-option
v-for="item in dictStore.getDictData(dictTypeCode)"
:key="item.id"
@@ -29,6 +29,24 @@
/>
</el-select>
</el-form-item>
<el-form-item label="最大电压" prop="maxVoltage">
<el-input-number
v-model="formContent.maxVoltage"
:min="0"
:precision="2"
:step="0.1"
style="width: 100%"
/>
</el-form-item>
<el-form-item label="最大电流" prop="maxCurrent">
<el-input-number
v-model="formContent.maxCurrent"
:min="0"
:precision="2"
:step="0.1"
style="width: 100%"
/>
</el-form-item>
</el-form>
</div>
@@ -40,7 +58,7 @@
/>
<template #footer>
<div>
<el-button :disabled="tableIsDisable" @click="close()"> </el-button>
<el-button :disabled="tableIsDisable" @click="close()">取消</el-button>
<el-button :disabled="tableIsDisable" type="primary" @click="save()">保存</el-button>
</div>
</template>
@@ -49,12 +67,12 @@
<script lang="ts" setup name="ErrorSystemDialog">
import { ElMessage, type FormItemRule } from 'element-plus'
import { computed, Ref, ref } from 'vue'
import { computed, ref, type Ref } from 'vue'
import { dialogBig } from '@/utils/elementBind'
import { addTestSource, getTestSourceById, updateTestSource } from '@/api/device/testSource/index'
import { useDictStore } from '@/stores/modules/dict'
import { type TestSource } from '@/api/device/interface/testSource'
// 定义弹出组件元信息
import { useDictStore } from '@/stores/modules/dict'
const dialogFormRef = ref()
const dictStore = useDictStore()
const mode = ref()
@@ -71,13 +89,15 @@ function useMetaInfo() {
parameter: '',
type: '',
devType: '',
maxVoltage: undefined,
maxCurrent: undefined,
state: 1
})
return { dialogVisible, titleType, formContent }
}
const { dialogVisible, titleType, formContent } = useMetaInfo()
// 清空formContent
const resetFormContent = () => {
formContent.value = {
id: '',
@@ -85,11 +105,13 @@ const resetFormContent = () => {
parameter: '',
type: '',
devType: '',
maxVoltage: undefined,
maxCurrent: undefined,
state: 1
}
}
let dialogTitle = computed(() => {
const dialogTitle = computed(() => {
switch (titleType.value) {
case 'add':
tableIsDisable.value = false
@@ -101,45 +123,52 @@ let dialogTitle = computed(() => {
tableIsDisable.value = true
return '查看检测源'
default:
return '' // 默认情况,可选
return ''
}
})
let dictTypeCode = computed(() => {
const dictTypeCode = computed(() => {
return 'S_Dev_Type_' + dictStore.getDictData('Pattern').find(item => item.id === modeId.value)?.code
})
// 定义规则
const validateNonNegative = (label: string) => {
return (_rule: FormItemRule, value: number | undefined, callback: (error?: Error) => void) => {
if (value != null && value < 0) {
callback(new Error(`${label} cannot be negative`))
return
}
callback()
}
}
const rules: Ref<Record<string, Array<FormItemRule>>> = ref({
name: [{ required: true, message: '检测源名称必填', trigger: 'blur' }],
name: [{ required: true, message: '检测源名称必填', trigger: 'blur' }],
devType: [{ required: true, message: '请选择一项设备类型', trigger: 'change' }],
type: [{ required: true, message: '请选择一项检测源类型', trigger: 'change ' }]
type: [{ required: true, message: '请选择一项检测源类型', trigger: 'change' }],
maxVoltage: [{ validator: validateNonNegative('最大电压'), trigger: 'change' }],
maxCurrent: [{ validator: validateNonNegative('最大电流'), trigger: 'change' }]
})
// 关闭弹窗
const close = () => {
dialogVisible.value = false
// 清空dialogForm中的值
resetFormContent()
// 重置表单
dialogFormRef.value?.resetFields()
parameterTable.value?.clearData()
}
// 保存数据
const save = () => {
try {
dialogFormRef.value?.validate(async (valid: boolean) => {
if (valid) {
if (formContent.value.id) {
await updateTestSource(formContent.value)
ElMessage.success({ message: `${dialogTitle.value}成功` })
ElMessage.success({ message: `${dialogTitle.value}成功` })
} else {
await addTestSource(formContent.value)
ElMessage.success({ message: `${dialogTitle.value}成功` })
ElMessage.success({ message: `${dialogTitle.value}成功` })
}
close()
// 刷新表格
await props.refreshTable!()
await props.refreshTable?.()
}
})
} catch (err) {
@@ -147,7 +176,6 @@ const save = () => {
}
}
// 打开弹窗,可能是新增,也可能是编辑
const open = async (sign: string, data: TestSource.ResTestSource, currentMode: string) => {
titleType.value = sign
dialogVisible.value = true
@@ -155,13 +183,25 @@ const open = async (sign: string, data: TestSource.ResTestSource, currentMode: s
modeId.value = dictStore.getDictData('Pattern').find(item => item.name === currentMode)?.id
if (data.id) {
const result = await getTestSourceById(data)
if (result && result.data) {
formContent.value = result.data as TestSource.ResTestSource
const sourceData = (result?.data ?? {}) as Partial<TestSource.ResTestSource>
formContent.value = {
id: sourceData.id ?? data.id,
pattern: sourceData.pattern ?? modeId.value,
parameter: sourceData.parameter ?? '',
type: sourceData.type ?? '',
devType: sourceData.devType ?? '',
maxVoltage: sourceData.maxVoltage ?? undefined,
maxCurrent: sourceData.maxCurrent ?? undefined,
state: sourceData.state ?? 1,
name: sourceData.name,
createBy: sourceData.createBy,
createTime: sourceData.createTime,
updateBy: sourceData.updateBy,
updateTime: sourceData.updateTime
}
} else {
resetFormContent()
}
// 重置表单
dialogFormRef.value?.resetFields()
}
@@ -169,7 +209,6 @@ const changeParameter = (parameterArr: any) => {
formContent.value.parameter = JSON.stringify(parameterArr)
}
// 对外映射
defineExpose({ open })
const props = defineProps<{
refreshTable: (() => Promise<void>) | undefined

View File

@@ -1,115 +1,123 @@
<template>
<div class='table-box'>
<ProTable
ref='proTable'
:columns='columns'
:request-api="getTableList"
>
<!-- :data='testSourceData' 如果要显示静态数据就切换该配置-->
<!-- 表格 header 按钮 -->
<template #tableHeader='scope'>
<el-button v-auth.testSource="'add'" type='primary' :icon='CirclePlus' @click="openDialog('add')">新增</el-button>
<el-button v-auth.testSource="'delete'" type='danger' :icon='Delete'
plain :disabled='!scope.isSelected' @click='batchDelete(scope.selectedListIds)'>
删除
</el-button>
</template>
<!-- 表格操作 -->
<template #operation='scope'>
<el-button v-auth.testSource="'view'" type='primary' link :icon='View' @click="openDialog('view', scope.row)">查看</el-button>
<el-button v-auth.testSource="'edit'" type='primary' link :icon='EditPen' @click="openDialog('edit', scope.row)">编辑</el-button>
<el-button v-auth.testSource="'delete'" type='primary' link :icon='Delete' @click='handleDelete(scope.row)'>删除</el-button>
</template>
</ProTable>
<div class="table-box">
<ProTable ref="proTable" :columns="columns" :request-api="getTableList">
<template #tableHeader="scope">
<el-button v-auth.testSource="'add'" type="primary" :icon="CirclePlus" @click="openDialog('add')">
新增
</el-button>
<el-button
v-auth.testSource="'delete'"
type="danger"
:icon="Delete"
plain
:disabled="!scope.isSelected"
@click="batchDelete(scope.selectedListIds)"
>
删除
</el-button>
</template>
<template #operation="scope">
<el-button v-auth.testSource="'view'" type="primary" link :icon="View" @click="openDialog('view', scope.row)">
查看
</el-button>
<el-button v-auth.testSource="'edit'" type="primary" link :icon="EditPen" @click="openDialog('edit', scope.row)">
编辑
</el-button>
<el-button
v-auth.testSource="'delete'"
type="primary"
link
:icon="Delete"
@click="handleDelete(scope.row)"
>
删除
</el-button>
</template>
</ProTable>
</div>
<TestSourcePopup :refresh-table='proTable?.getTableList' ref='testSourcePopup' />
<TestSourcePopup :refresh-table="proTable?.getTableList" ref="testSourcePopup" />
</template>
</template>
<script setup lang="tsx" name="useRole">
import { type TestSource } from '@/api/device/interface/testSource'
import { useHandleData } from '@/hooks/useHandleData'
import ProTable from '@/components/ProTable/index.vue'
import type { ProTableInstance, ColumnProps } from '@/components/ProTable/interface'
import { CirclePlus, Delete, EditPen, View } from '@element-plus/icons-vue'
import { useDictStore } from '@/stores/modules/dict'
import TestSourcePopup from './components/testSourcePopup.vue'
import { getTestSourceList, deleteTestSource } from '@/api/device/testSource/index'
import { reactive, ref } from 'vue'
import { useModeStore } from '@/stores/modules/mode'
<script setup lang='tsx' name='useRole'>
import { type TestSource } from '@/api/device/interface/testSource'
import { useHandleData } from '@/hooks/useHandleData'
import { useDownload } from '@/hooks/useDownload'
import { useAuthButtons } from '@/hooks/useAuthButtons'
import ProTable from '@/components/ProTable/index.vue'
import ImportExcel from '@/components/ImportExcel/index.vue'
import type{ ProTableInstance, ColumnProps } from '@/components/ProTable/interface'
import { CirclePlus, Delete, EditPen, Share, Download, Upload, View, Refresh } from '@element-plus/icons-vue'
import { useDictStore } from '@/stores/modules/dict'
import TestSourcePopup from './components/testSourcePopup.vue'
import {
getTestSourceList,deleteTestSource,
} from '@/api/device/testSource/index'
import { reactive, ref } from 'vue'
import { useModeStore } from '@/stores/modules/mode'; // 引入模式 store
defineOptions({
name: 'testSource'
})
const testSourcePopup = ref()
const dictStore = useDictStore()
const modeStore = useModeStore();
// ProTable 实例
const proTable = ref<ProTableInstance>()
defineOptions({
name: 'testSource'
})
const getTableList = (params: any) => {
const testSourcePopup = ref()
const dictStore = useDictStore()
const modeStore = useModeStore()
const proTable = ref<ProTableInstance>()
let newParams = JSON.parse(JSON.stringify(params))
const patternId = dictStore.getDictData('Pattern').find(item=>item.name=== modeStore.currentMode)?.id//获取数据字典中对应的id
newParams.pattern = patternId
return getTestSourceList(newParams)
const getTableList = (params: any) => {
const newParams = JSON.parse(JSON.stringify(params))
const patternId = dictStore.getDictData('Pattern').find(item => item.name === modeStore.currentMode)?.id
newParams.pattern = patternId
return getTestSourceList(newParams)
}
// 表格配置项
const columns = reactive<ColumnProps<TestSource.ResTestSource>[]>([
{ type: 'selection', fixed: 'left', width: 70 ,
},
const columns = reactive<ColumnProps<TestSource.ResTestSource>[]>([
{ type: 'selection', fixed: 'left', width: 70 },
{ type: 'index', fixed: 'left', width: 70, label: '序号' },
{
prop: 'name',
label: '名称',
search: { el: 'input' },
minWidth: 300,
prop: 'name',
label: '名称',
search: { el: 'input' },
minWidth: 300
},
{
prop: 'devType',
label: '设备类型',
enum: dictStore.getDictData('S_Dev_Type_'+dictStore.getDictData('Pattern').find(item=>item.name=== modeStore.currentMode)?.code),
fieldNames: { label: 'name', value: 'id' },
search: { el: 'select' },
minWidth: 250,
prop: 'devType',
label: '设备类型',
enum: dictStore.getDictData(
'S_Dev_Type_' + dictStore.getDictData('Pattern').find(item => item.name === modeStore.currentMode)?.code
),
fieldNames: { label: 'name', value: 'id' },
search: { el: 'select' },
minWidth: 250
},
{
prop: 'type',
label: '检测源类型',
enum: dictStore.getDictData('Pq_Source_Type'),
fieldNames: { label: 'name', value: 'id' },
search: { el: 'select' },
minWidth: 150,
prop: 'type',
label: '检测源类型',
enum: dictStore.getDictData('Pq_Source_Type'),
fieldNames: { label: 'name', value: 'id' },
search: { el: 'select' },
minWidth: 150
},
{ prop: 'operation', label: '操作', fixed: 'right', width: 250 },
])
{
prop: 'maxVoltage',
label: '最大电压(V)',
minWidth: 140
},
{
prop: 'maxCurrent',
label: '最大电流(A)',
minWidth: 140
},
{ prop: 'operation', label: '操作', fixed: 'right', width: 250 }
])
// 打开 drawer(新增、编辑)
const openDialog = (titleType: string, row: Partial<TestSource.ResTestSource> = {}) => {
testSourcePopup.value?.open(titleType, row,modeStore.currentMode)
testSourcePopup.value?.open(titleType, row, modeStore.currentMode)
}
// 批量删除设备
const batchDelete = async (id: string[]) => {
await useHandleData(deleteTestSource, id, '删除所选检测源')
proTable.value?.clearSelection()
proTable.value?.getTableList()
await useHandleData(deleteTestSource, id, '删除所选检测源')
proTable.value?.clearSelection()
proTable.value?.getTableList()
}
// 删除设备
const handleDelete = async (params: TestSource.ResTestSource) => {
await useHandleData(deleteTestSource, [params.id], `删除【${params.name}】检测源`)
proTable.value?.getTableList()
await useHandleData(deleteTestSource, [params.id], `删除【${params.name}】检测源`)
proTable.value?.getTableList()
}
</script>
</script>

View File

@@ -933,7 +933,7 @@ const open = async (sign: string, data: Plan.ReqPlan, currentMode: string, plan:
const datasourceDicts = dictStore.getDictData('Datasource')
formContent.datasourceIds = datasourceDicts
.filter(item => ['real', 'wave_data'].includes(item.code))
.filter(item => ['real'].includes(item.code))
.map(item => item.code)
realTimeSetting.value = true

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