Commit 78f35887 authored by Cai Wei's avatar Cai Wei

feat(*): 补充遗漏测试标识点

parent 95a47813
Pipeline #1317 failed
# Cypress Studio 启动器使用说明
## 概述
DC-TOM 项目提供了跨平台的 Cypress Studio 启动器,自动处理开发服务器启动和 Cypress Studio 的启动顺序。
## 功能特性
**自动启动开发服务器** - 确保测试环境就绪
**智能端口检测** - 自动检测端口占用情况
**跨平台支持** - Windows/macOS/Linux 全平台兼容
**进程管理** - 自动清理和停止相关进程
**错误处理** - 详细的错误提示和解决方案
## 快速开始
### 通用方法 (推荐)
```bash
# 启动 Cypress Studio (自动启动开发服务器)
npm run studio
# 查看帮助信息
npm run studio:help
# 使用原有命令
npm run cy:open:studio
```
### Windows 专用方法
```cmd
# 方法1: 使用 npm 脚本
npm run studio:win
# 方法2: 直接运行批处理文件
start-studio.bat
# 方法3: 双击 start-studio.bat 文件
```
### macOS/Linux 方法
```bash
# 使用 Node.js 脚本
node start-studio.cjs
# 如果仍有 bash 脚本 (需要可执行权限)
./start-studio.sh
```
## 启动流程
1. **环境检查** - 验证 Node.js、npm、npx 是否可用
2. **端口检测** - 检查 localhost:3000 是否被占用
3. **启动开发服务器** - 如果端口空闲,运行 `npm run dev`
4. **等待就绪** - 等待开发服务器完全启动并响应
5. **启动 Cypress Studio** - 开发环境就绪后启动 Studio
## 故障排除
### 常见问题
**问题**: Windows 下无法执行 `.sh` 脚本
**解决**: 使用 `npm run studio``start-studio.bat`
**问题**: 端口 3000 被占用
**解决**:
- 启动器会自动跳过开发服务器启动
- 手动停止占用端口的进程
- 或修改项目配置使用其他端口
**问题**: Node.js 或 npm 未找到
**解决**:
- 安装 Node.js: https://nodejs.org
- 确保 Node.js 在系统 PATH 中
- 重新打开终端/命令提示符
**问题**: Cypress 启动失败
**解决**:
- 运行 `npm install` 安装项目依赖
- 检查 Cypress 是否正确安装
- 尝试单独运行 `npx cypress open`
### 手动清理
如果启动器意外退出,可能需要手动清理进程:
```bash
# macOS/Linux
pkill -f "vite"
pkill -f "cypress"
# Windows (命令提示符)
taskkill /f /im node.exe
taskkill /f /im cypress.exe
```
## 文件说明
| 文件名 | 用途 | 平台支持 |
|--------|------|----------|
| `start-studio.cjs` | Node.js 启动脚本 | 全平台 |
| `start-studio.bat` | Windows 批处理文件 | Windows |
| `start-studio.sh` | Bash 脚本 (如存在) | macOS/Linux |
## 自定义配置
### 修改端口
如果需要修改开发服务器端口,编辑 `vite.config.js`:
```javascript
export default defineConfig({
server: {
port: 3001 // 修改为其他端口
}
})
```
同时更新启动器脚本中的端口检测逻辑。
### 添加启动参数
`package.json` 中自定义脚本:
```json
{
"scripts": {
"studio:custom": "node start-studio.cjs --custom-arg"
}
}
```
## 技术细节
- **进程管理**: 使用 Node.js `child_process` 模块
- **端口检测**: HTTP 服务器监听测试
- **跨平台**: 自动检测操作系统并调整命令
- **错误处理**: 完整的错误捕获和用户友好提示
## 支持
如果遇到问题,请:
1. 检查 Node.js 版本 (推荐 18+)
2. 确保项目依赖已安装 (`npm install`)
3. 查看启动器输出的错误信息
4. 参考本文档的故障排除部分
---
**注意**: 该启动器专为 DC-TOM 项目设计,但可以适配其他 Vue + Vite + Cypress 项目。
\ No newline at end of file
......@@ -7,8 +7,11 @@
"dev": "vite --mode development",
"build": "vite build",
"preview": "vite preview",
"cy:open": "vite --mode development && cypress open",
"cy:open:studio": "./start-studio.sh",
"cy:open": "cypress open",
"cy:open:studio": "node start-studio.cjs",
"studio": "node start-studio.cjs",
"studio:help": "node start-studio.cjs --help",
"studio:win": "start-studio.bat",
"cy:run": "cypress run",
"cy:run:studio": "cypress run --spec 'cypress/e2e/studio-generated/*.cy.js'",
"cy:run:ci": "cypress run --browser chrome --headless",
......
<template >
<div class="hamburger" @click="toggleClick">
<div class="hamburger" @click="toggleClick" data-testid="hamburger-menu">
<svg
:class="{'is-active':isActive}"
class="hamburger-icon"
......
<template>
<div class="layout-box">
<div class="left" :class="{ colWidth: sidebar }">
<div class="layout-box" data-testid="layout-container">
<div class="left" :class="{ colWidth: sidebar }" data-testid="layout-sidebar">
<menuCom></menuCom>
</div>
<div class="right" :class="{ extend: sidebar }">
<div class="header">
<div class="right" :class="{ extend: sidebar }" data-testid="layout-main">
<div class="header" data-testid="layout-header">
<hamburger
class="hamburger-container"
@toggleClick="toggleSideBar"
:is-active="sidebar"
/>
<h3>E-Core / DC-TOM 管理平台</h3>
<div class="go-tom" @click="goTom" v-if="!currentDomain.includes('screen.bmetech.com')">EcoTOM 三流合一</div>
<div class="right-block">
<div class="go-tom" @click="goTom" v-if="!currentDomain.includes('screen.bmetech.com')" data-testid="go-tom-button">EcoTOM 三流合一</div>
<div class="right-block" data-testid="header-right-block">
<el-dropdown class="right-menu-item" trigger="click">
<div class="user-info">
<div class="user-info" data-testid="user-info">
<svg
color="red"
data-icon-name="user-circle"
......@@ -54,8 +54,8 @@
</el-dropdown>
</div>
</div>
<div class="content">
<div class="breadcrumb-container">
<div class="content" data-testid="layout-content">
<div class="breadcrumb-container" data-testid="breadcrumb-container">
<el-breadcrumb separator="/">
<el-breadcrumb-item
v-for="(item, index) in breadcrumbList"
......
<template>
<div class="page-container collectorList all-select-btn">
<div class="content-box">
<div class="search">
<el-form :inline="true" :model="formInline" class="demo-form-inline">
<div class="page-container collectorList all-select-btn" data-testid="about-view-container">
<div class="content-box" data-testid="content-box">
<div class="search" data-testid="search-area">
<el-form :inline="true" :model="formInline" class="demo-form-inline" data-testid="search-form">
<el-form-item label="事件名称">
<el-input
v-model="formInline.eventName"
placeholder="请输入事件名称"
style="width: 200px"
clearable
data-testid="event-name-input"
/>
</el-form-item>
<el-form-item label="发生位置">
......@@ -17,6 +18,7 @@
placeholder="请输入发生位置"
style="width: 200px"
clearable
data-testid="location-input"
/>
</el-form-item>
<el-form-item label="除尘器名称">
......@@ -25,15 +27,17 @@
placeholder="请输入除尘器名称"
style="width: 200px"
clearable
data-testid="duster-name-input"
/>
</el-form-item>
<el-form-item label="设备类型">
<el-select v-model="formInline.deviceType" style="width: 200px">
<el-select v-model="formInline.deviceType" style="width: 200px" data-testid="device-type-select">
<el-option
v-for="i in typeList.list"
:key="i"
:label="`${i.desc}`"
:value="i.code"
:data-testid="`device-type-option-${i.code}`"
/>
</el-select>
</el-form-item>
......@@ -46,13 +50,14 @@
end-placeholder="结束时间"
popper-class="date-picker-popper"
:teleported="false"
data-testid="alarm-date-picker"
/>
</el-form-item>
<el-form-item>
<el-button type="default" class="reset-btn-balck-theme" @click="onReset"
<el-button type="default" class="reset-btn-balck-theme" @click="onReset" data-testid="reset-button"
>重置</el-button
>
<el-button type="default" class="search-btn-balck-theme" @click="onSubmit"
<el-button type="default" class="search-btn-balck-theme" @click="onSubmit" data-testid="search-button"
>查询</el-button
>
<el-button
......@@ -60,20 +65,21 @@
style="width: 140px"
class="export-btn-balck-theme"
@click="openRound()"
data-testid="suspend-device-button"
>挂起设备</el-button
>
</el-form-item>
<br />
<el-form-item>
<el-radio-group v-model="formInline.suspendFlag" @change="onSubmit">
<el-radio value="1">挂起期间告警</el-radio>
<el-radio value="0">非挂起期间告警</el-radio>
<el-radio value="2">全部告警</el-radio>
<el-radio-group v-model="formInline.suspendFlag" @change="onSubmit" data-testid="suspend-flag-radio-group">
<el-radio value="1" data-testid="suspend-flag-radio-during">挂起期间告警</el-radio>
<el-radio value="0" data-testid="suspend-flag-radio-normal">非挂起期间告警</el-radio>
<el-radio value="2" data-testid="suspend-flag-radio-all">全部告警</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
</div>
<div class="table-box">
<div class="table-box" data-testid="table-container">
<common-table
ref="pageRef"
:data="tableData.list"
......@@ -88,12 +94,13 @@
next: '后一页',
jumper: '跳至',
}"
data-testid="alarm-table"
>
<template #index="{ $index }">
{{ getIndex($index) }}
</template>
<template #operate="{ row }">
<span class="health-score green-color" @click="openDialog(row)">暂挂起</span>
<span class="health-score green-color" @click="openDialog(row)" data-testid="suspend-action-link">暂挂起</span>
</template>
</common-table>
</div>
......@@ -106,22 +113,24 @@
width="500px"
:close-on-click-modal="false"
:close-on-press-escape="false"
data-testid="suspend-dialog"
>
<div class="equd_body">
<div class="equd_body" data-testid="dialog-body">
<el-form
ref="ruleFormRef"
:model="equSubmitInfo"
:rules="rules"
class="equd_form"
data-testid="suspend-form"
>
<span
>是否要对<span class="bold">{{ clickItem.name }}</span
>不再告警:</span
>
<el-form-item>
<el-radio-group v-model="equSubmitInfo.noAlarm" @change="onSubmit">
<el-radio value="1"></el-radio>
<el-radio value="0"></el-radio>
<el-radio-group v-model="equSubmitInfo.noAlarm" @change="onSubmit" data-testid="no-alarm-radio-group">
<el-radio value="1" data-testid="no-alarm-radio-yes"></el-radio>
<el-radio value="0" data-testid="no-alarm-radio-no"></el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="开始时间" prop="startTime">
......@@ -130,6 +139,7 @@
type="datetime"
placeholder="选择开始时间"
popper-class="date-picker-popper"
data-testid="start-time-picker"
/>
</el-form-item>
<el-form-item label="结束时间" prop="endTime">
......@@ -138,23 +148,24 @@
type="datetime"
placeholder="选择结束时间"
popper-class="date-picker-popper"
data-testid="end-time-picker"
/>
</el-form-item>
<el-form-item label="原因分类" prop="reasonCategory">
<el-select v-model="equSubmitInfo.reasonCategory" style="width: 220px;">
<el-option v-for="item in reasonCategoryList" :key="item.key" :value="item.key" :label="item.value"></el-option>
<el-select v-model="equSubmitInfo.reasonCategory" style="width: 220px;" data-testid="reason-category-select">
<el-option v-for="item in reasonCategoryList" :key="item.key" :value="item.key" :label="item.value" :data-testid="`reason-category-option-${item.key}`"></el-option>
</el-select>
</el-form-item>
<el-form-item label="原因描述" prop="reasonDescription" style="padding-left: 10px;">
<el-input v-model="equSubmitInfo.reasonDescription" type="textarea" style="width: 220px;"></el-input>
<el-input v-model="equSubmitInfo.reasonDescription" type="textarea" style="width: 220px;" data-testid="reason-description-textarea"></el-input>
</el-form-item>
</el-form>
</div>
<template #footer>
<span class="dialog-footer ">
<el-button type="default" class="cancel-btn-balck-theme" @click="equDialog = false">取消</el-button>
<el-button type="default" class="search-btn-balck-theme" @click="equPending(ruleFormRef)">
<span class="dialog-footer " data-testid="dialog-footer">
<el-button type="default" class="cancel-btn-balck-theme" @click="equDialog = false" data-testid="cancel-button">取消</el-button>
<el-button type="default" class="search-btn-balck-theme" @click="equPending(ruleFormRef)" data-testid="confirm-button">
确认
</el-button>
</span>
......
......@@ -4,15 +4,16 @@
:title="title"
:width="width"
@close="closeDialog"
data-testid="custom-dialog"
>
<div>
<div data-testid="dialog-content">
<slot name="content"></slot>
<slot></slot>
</div>
<template #footer>
<div class="dialog-footer all-select-btn">
<el-button type="default" @click="closeDialog" class="search-btn-balck-theme">关闭</el-button>
<el-button type="default" v-if="btnGroup.length" v-for="item in btnGroup" class="reset-btn-balck-theme" :key="item.attrKey" @click="handleBtn(item.attrKey)">
<div class="dialog-footer all-select-btn" data-testid="dialog-footer">
<el-button type="default" @click="closeDialog" class="search-btn-balck-theme" data-testid="close-button">关闭</el-button>
<el-button type="default" v-if="btnGroup.length" v-for="item in btnGroup" class="reset-btn-balck-theme" :key="item.attrKey" @click="handleBtn(item.attrKey)" :data-testid="`dialog-button-${item.attrKey}`">
{{item.label}}
</el-button>
</div>
......
......@@ -19,6 +19,7 @@
:key="item.productionLineId"
:label="item.productionLineName"
:value="item.productionLineId"
:data-testid="`production-line-option-${item.productionLineId}`"
/>
</el-select>
</el-form-item>
......@@ -29,12 +30,13 @@
placeholder="请选择"
data-testid="my-agency-device-type-select"
>
<el-option label="全部" value="" />
<el-option label="全部" value="" data-testid="device-type-option-all" />
<el-option
v-for="(item, index) in basicConfiguration.deviceList"
index="item.id"
:label="item.desc"
:value="item.code"
:data-testid="`device-type-option-${item.code}`"
>
</el-option>
</el-select>
......@@ -112,17 +114,19 @@
:btn-group="doneDialog.btnGroup"
@handleBtn="saveDeal"
@close:showDialog="doneDialog.show = false"
data-testid="done-dialog"
>
<div class="form-box">
<el-form ref="form" :model="formModel">
<div class="form-box" data-testid="done-dialog-form-box">
<el-form ref="form" :model="formModel" data-testid="done-dialog-form">
<el-row :gutter="24">
<el-col :span="24">
<div class="label-title">闭环控制分派</div>
<el-form-item label="已分派人:">
<div class="label-title" data-testid="loop-control-dispatch-title">闭环控制分派</div>
<el-form-item label="已分派人:" data-testid="assigned-person-form-item">
<el-tag
v-for="(ele, eIndex) in personTag"
:key="ele.name"
:type="ele.type"
:data-testid="`assigned-person-tag-${eIndex}`"
>
{{ ele.name }}
</el-tag>
......@@ -131,17 +135,17 @@
</el-row>
<el-row :gutter="24">
<el-col :span="24">
<div class="label-title">闭环控制信息</div>
<div class="alarm-content">
<div class="content-item">
<div class="label-title" data-testid="loop-control-info-title">闭环控制信息</div>
<div class="alarm-content" data-testid="loop-control-info-content">
<div class="content-item" data-testid="ticket-number-item">
<span class="name">编号:</span>
<span class="value">{{ workSheetDetail?.workTicketNo }}</span>
</div>
<div class="content-item">
<div class="content-item" data-testid="create-time-item">
<span class="name">创建时间:</span>
<span class="value">{{ workSheetDetail?.createTime }}</span>
</div>
<div class="content-item">
<div class="content-item" data-testid="loop-control-status-item">
<span class="name">闭环控制状态:</span>
<span class="value work-status">{{
workSheetDetail?.desc
......@@ -152,8 +156,8 @@
</el-row>
<el-row :gutter="24" v-if="workSheetDetail?.warnType !== 5">
<el-col :span="24">
<div class="label-title">告警信息</div>
<div class="alarm-content">
<div class="label-title" data-testid="alarm-info-title">告警信息</div>
<div class="alarm-content" data-testid="alarm-info-content">
<div class="content-item">
<span class="name">所属工序:</span>
<span class="value">{{
......@@ -287,8 +291,8 @@
</el-row>
<el-row :gutter="24" v-if="workSheetDetail?.relationAnalysis">
<el-col :span="24">
<div class="label-title">数据异常定位分析</div>
<div class="alarm-content">
<div class="label-title" data-testid="data-exception-analysis-title">数据异常定位分析</div>
<div class="alarm-content" data-testid="data-exception-analysis-content">
<div class="content-item">
<span class="name">离线原因:</span>
<span class="value">{{
......@@ -301,6 +305,7 @@
style="margin-left: 80px"
class="led-btn"
@click="openPage(workSheetDetail)"
data-testid="related-ledger-button"
>关联台账</el-button
>
</div>
......@@ -309,8 +314,8 @@
</el-row>
<el-row :gutter="24" v-if="workSheetDetail?.warnType === 5">
<el-col :span="24">
<div class="label-title">道路信息</div>
<div class="alarm-content">
<div class="label-title" data-testid="road-info-title">道路信息</div>
<div class="alarm-content" data-testid="road-info-content">
<div class="content-item">
<span class="name">发生位置:</span>
<span class="value">
......@@ -404,8 +409,8 @@
<el-row :gutter="24" v-if="workSheetDetail?.alarmImags">
<el-col>
<div class="label-title">告警图片</div>
<div class="demo-image__preview">
<div class="label-title" data-testid="alarm-images-title">告警图片</div>
<div class="demo-image__preview" data-testid="alarm-images-preview">
<div
class="image-preview"
v-for="(item, index) in alarmImg"
......@@ -558,20 +563,21 @@
<el-row :gutter="24">
<el-col :span="24">
<div class="label-title">原因分析</div>
<div class="label-title" data-testid="reason-analysis-title">原因分析</div>
<el-input
class="input-style"
v-model="dialogReason"
type="textarea"
:rows="3"
placeholder="请输入内容"
data-testid="reason-analysis-textarea"
/>
</el-col>
</el-row>
<el-row :gutter="24">
<el-col :span="24">
<div class="label-title">处理措施</div>
<div class="label-title" data-testid="handle-measures-title">处理措施</div>
<div>
<el-input
class="input-style"
......@@ -579,6 +585,7 @@
type="textarea"
:rows="3"
placeholder="请输入内容"
data-testid="handle-measures-textarea"
/>
</div>
</el-col>
......@@ -593,37 +600,42 @@
:width="assignDialog.width"
@handleBtn="saveAssign"
@close:showDialog="assignDialog.show = false"
data-testid="assign-dialog"
>
<el-form>
<el-form-item label="分派人:">
<el-form data-testid="assign-dialog-form">
<el-form-item label="分派人:" data-testid="assign-person-form-item">
<div class="inline-box">
<el-select
v-model="handlerPeopleId"
filterable
placeholder="请选择分派人"
data-testid="assign-person-select"
>
<el-option
v-for="(item, i) in handlerPeople"
:key="i"
:label="item.name"
:value="item.id"
:data-testid="`assign-person-option-${item.id}`"
></el-option>
</el-select>
<el-button
type="default"
class="addBtn search-btn-balck-theme"
@click="addPerson"
data-testid="add-person-button"
>新增</el-button
>
</div>
</el-form-item>
<el-form-item label="已分派人:">
<el-form-item label="已分派人:" data-testid="assigned-person-list-form-item">
<el-tag
v-for="(ele, eIndex) in personTag"
:key="ele.name"
closable
@close="delPerson(eIndex)"
:type="ele.type"
:data-testid="`assigned-person-list-tag-${eIndex}`"
>
{{ ele.name }}
</el-tag>
......@@ -637,30 +649,34 @@
:width="bagDialog.width"
@handleBtn="saveBag"
@close:showDialog="bagDialog.show = false"
data-testid="bag-dialog"
>
<el-form>
<el-form-item label="第几行:">
<el-form data-testid="bag-dialog-form">
<el-form-item label="第几行:" data-testid="bag-row-form-item">
<el-select
v-model="bagRow"
placeholder="请选择第几行"
@change="getCol"
data-testid="bag-row-select"
>
<el-option
v-for="item in rowList"
:key="item"
:label="item"
:value="item"
:data-testid="`bag-row-option-${item}`"
>
</el-option>
</el-select>
</el-form-item>
<el-form-item label="第几列:">
<el-select v-model="bagCol" placeholder="请选择第几列">
<el-form-item label="第几列:" data-testid="bag-col-form-item">
<el-select v-model="bagCol" placeholder="请选择第几列" data-testid="bag-col-select">
<el-option
v-for="item in colList"
:key="item"
:label="item"
:value="item"
:data-testid="`bag-col-option-${item}`"
>
</el-option>
</el-select>
......
This diff is collapsed.
......@@ -92,23 +92,26 @@
width="1000px"
:close-on-click-modal="false"
:close-on-press-escape="false"
data-testid="dust-line-dialog"
>
<div class="input-group">
<div class="input-group" data-testid="dust-selector-group">
<span>除尘器名称:</span>
<el-select
v-model="selectDustNo"
@change="changeDust"
style="width: 120px"
data-testid="dust-name-select"
>
<el-option
v-for="i in dustList.list"
:key="i"
:label="`${i.dusterName}`"
:value="i.dusterNo"
:data-testid="`dust-option-${i.dusterNo}`"
/>
</el-select>
</div>
<div class="echartBox">
<div class="echartBox" data-testid="chart-container">
<chartLine :dustLineInfo="dustLineInfo.list"></chartLine>
</div>
<!-- <template #footer>
......
<template>
<div ref="chartRef" class="chart-line"></div>
<div ref="chartRef" class="chart-line" data-testid="chart-line-container"></div>
</template>
<script setup>
......
<template>
<div class="svg-box">
<div class="svg-main">
<div class="svg-box" data-testid="map-svg-container">
<div class="svg-main" data-testid="svg-main">
<img src="@/assets/map.png" alt="" />
<div class="spot-box">
<div class="spot-box" data-testid="spot-box">
<div
v-for="(spot, index) in spots"
:key="index"
......@@ -15,11 +15,12 @@
left: spot.x + '%',
top: spot.y + '%',
}"
:data-testid="`map-spot-${index}`"
>
<div class="pulse"></div>
<div class="pulse" data-testid="spot-pulse"></div>
<!-- 连接线和详情窗 -->
<transition name="line">
<div class="connector-line" v-if="activeSpot === index"></div>
<div class="connector-line" v-if="activeSpot === index" data-testid="connector-line"></div>
</transition>
<transition
name="detail"
......@@ -30,10 +31,11 @@
<div
class="detail-window"
v-if="activeSpot === index && showDetail"
data-testid="detail-window"
>
<div class="detail-title">{{ spot.name }}</div>
<div class="detail-content">
<div class="detail-item">
<div class="detail-title" data-testid="detail-title">{{ spot.name }}</div>
<div class="detail-content" data-testid="detail-content">
<div class="detail-item" data-testid="detail-status-item">
<span class="label">状态:</span>
<span class="value" :class="`text-status-${spot.status}`">{{
getStatusText(spot.status)
......@@ -43,9 +45,9 @@
<span class="label">数值:</span>
<span class="value">{{ spot.value }}</span>
</div> -->
<div class="detail-item">
<div class="detail-item" data-testid="detail-description-item">
<span class="label">描述:</span>
<div class="value">
<div class="value" data-testid="description-list">
<span class="value-item" v-for="(item, index) in spot.description" :key="index">{{ item }}</span>
<span class="value-item" v-if="spot.description.length === 0">暂无</span>
</div>
......
......@@ -3,15 +3,17 @@
class="message-list"
@mouseenter="pauseScroll"
@mouseleave="resumeScroll"
data-testid="message-list-container"
>
<div class="message-wrapper" ref="messageWrapper">
<div class="message-wrapper" ref="messageWrapper" data-testid="message-wrapper">
<div
class="message-item"
v-for="(message, index) in extendedList"
:key="index"
:data-testid="`message-item-${index}`"
>
<span class="time">{{ message.date }}</span>
<div class="content">
<div class="content" data-testid="message-content">
<span class="title">{{ message.dusterName }}</span
>{{ message.message }}
</div>
......
<template>
<div class="page-container bag-monitoring-container">
<div class="header">
<div class="select-container">
<div class="page-container bag-monitoring-container" data-testid="bag-monitoring-container">
<div class="header" data-testid="bag-monitoring-header">
<div class="select-container" data-testid="device-select-container">
<span class="select-label">检测仪器</span>
<el-select v-model="selectedDevice" placeholder="1#布袋检测仪" @change="handleDeviceChange">
<el-select v-model="selectedDevice" placeholder="1#布袋检测仪" @change="handleDeviceChange" data-testid="device-select">
<el-option
v-for="item in deviceList"
:key="item.deviceNo"
:label="item.deviceName"
:value="item.deviceNo"
:data-testid="`device-option-${item.deviceNo}`"
></el-option>
</el-select>
</div>
<div class="title">BME布袋监测 - {{ dusterName }}</div>
<div class="title" data-testid="monitoring-title">BME布袋监测 - {{ dusterName }}</div>
</div>
<div class="chart-container">
<div class="chart-wrapper" ref="chartRef"></div>
<div class="chart-wrapper no-data" v-if="chartData.xData.length === 0">
<div class="no-data-text">暂无数据</div>
<div class="chart-container" data-testid="chart-container">
<div class="chart-wrapper" ref="chartRef" data-testid="chart-wrapper"></div>
<div class="chart-wrapper no-data" v-if="chartData.xData.length === 0" data-testid="no-data-wrapper">
<div class="no-data-text" data-testid="no-data-text">暂无数据</div>
</div>
</div>
<div class="data-panel" :class="{ collapsed: !isPanelOpen }">
<div class="data-panel" :class="{ collapsed: !isPanelOpen }" data-testid="data-panel">
<div
class="panel-toggle"
:class="{
......@@ -30,33 +31,34 @@
'expanded-icon': isPanelOpen,
}"
@click="togglePanel"
data-testid="panel-toggle"
>
<el-icon v-if="isPanelOpen"><ArrowRightBold /></el-icon>
<el-icon v-if="!isPanelOpen"><ArrowLeftBold /></el-icon>
</div>
<div class="data-box">
<div class="data-item">
<div class="data-box" data-testid="data-box">
<div class="data-item" data-testid="data-item-1">
<div>时间: {{ currentData.time }}</div>
<div>仓室: {{ currentData.compartNo }}</div>
<div>反吹: {{ currentData.blowBack }}</div>
</div>
<div class="data-item">
<div class="data-item" data-testid="data-item-2">
<div>DI3: {{ currentData.di3 }}</div>
<div>排: {{ currentData.row }}</div>
<div>实时值: {{ currentData.realData }}</div>
</div>
<div class="data-item">
<div class="data-item" data-testid="data-item-3">
<div>基线值: {{ currentData.baseline }}</div>
<div>Delay: {{ currentData.delay }}</div>
<div>totaltime: {{ currentData.totalTime }}</div>
</div>
<div class="data-item">
<div class="data-item" data-testid="data-item-4">
<div>next:{{ currentData.next }}</div>
<div>valve:{{ currentData.valveNo }}</div>
<div>DI_TIME={{ currentData.diTime }}</div>
</div>
<div class="data-item">
<div class="data-item" data-testid="data-item-5">
<div>peak={{ currentData.peak }}</div>
</div>
</div>
......@@ -66,76 +68,78 @@
</div> -->
</div>
<div class="time-controls">
<div class="time-desc">
<div class="time-controls" data-testid="time-controls">
<div class="time-desc" data-testid="time-desc">
<span class="icon"></span>
<span class="time-desc-text">当前实时信号:</span>
<span class="time-desc-value">{{ currentData.compartNo }}仓 / {{ currentData.row }}</span>
</div>
<div>
<el-button @click="navigateBackward">
<div data-testid="navigation-controls">
<el-button @click="navigateBackward" data-testid="navigate-backward-button">
<el-icon><ArrowLeftBold />向前</el-icon>
</el-button>
<el-button @click="navigateForward">
<el-button @click="navigateForward" data-testid="navigate-forward-button">
<el-icon><ArrowRightBold />向后</el-icon>
</el-button>
<span class="time-label">间隔</span>
<el-input v-model="timeInterval" class="time-input"></el-input>
<span class="time-unit">分钟</span>
<span class="time-label" data-testid="time-label">间隔</span>
<el-input v-model="timeInterval" class="time-input" data-testid="time-interval-input"></el-input>
<span class="time-unit" data-testid="time-unit">分钟</span>
<el-button @click="reset">重置</el-button>
<el-button @click="reset" data-testid="reset-button">重置</el-button>
</div>
</div>
<div class="data-table">
<div class="data-table" data-testid="data-table">
<table>
<thead>
<tr>
<th>名称</th>
<th v-for="(value, index) in tableData" :key="index">
<table data-testid="monitoring-table">
<thead data-testid="table-head">
<tr data-testid="table-header-row">
<th data-testid="table-header-name">名称</th>
<th v-for="(value, index) in tableData" :key="index" :data-testid="`table-header-${index}`">
{{ value.compartName }}
</th>
</tr>
</thead>
<tbody>
<tr>
<td>峰值{{ currentData.row }}排_R</td>
<tbody data-testid="table-body">
<tr data-testid="peak-value-r-row">
<td data-testid="peak-value-r-label">峰值{{ currentData.row }}排_R</td>
<td
v-for="(value, index) in tableData"
:key="'r' + index"
:class="{
'online-style': value.compartNo === currentData.compartNo,
}"
:data-testid="`peak-value-r-${index}`"
>
{{ value.peakValueR }}
</td>
</tr>
<tr>
<td>峰值_H</td>
<td v-for="(value, index) in tableData" :key="'h' + index">
<tr data-testid="peak-value-h-row">
<td data-testid="peak-value-h-label">峰值_H</td>
<td v-for="(value, index) in tableData" :key="'h' + index" :data-testid="`peak-value-h-${index}`">
{{ value.peakValueH }}
</td>
</tr>
<tr>
<td>状态</td>
<tr data-testid="status-row">
<td data-testid="status-label">状态</td>
<td
v-for="(value, index) in tableData"
:key="'s' + index"
:class="{ error: value.status === 1 }"
:data-testid="`status-${index}`"
>
{{ value.status === 1 ? "故障" : "正常" }}
</td>
</tr>
<tr>
<td>反吹中</td>
<td v-for="(value, index) in tableData" :key="'b' + index">
<tr data-testid="blow-back-row">
<td data-testid="blow-back-label">反吹中</td>
<td v-for="(value, index) in tableData" :key="'b' + index" :data-testid="`blow-back-${index}`">
{{ value.blowBack }}
</td>
</tr>
<tr>
<td>谷值</td>
<td v-for="(value, index) in tableData" :key="'v' + index">
<tr data-testid="valley-value-row">
<td data-testid="valley-value-label">谷值</td>
<td v-for="(value, index) in tableData" :key="'v' + index" :data-testid="`valley-value-${index}`">
{{ value.valleyValue}}
</td>
</tr>
......
<template>
<div class="gsap-number-container">
<div :class="['value', colorClass]" ref="numberElement">{{ isStringMode ? stringValue : displayValue }}</div>
<div class="gsap-number-container" data-testid="gsap-number-container">
<div :class="['value', colorClass]" ref="numberElement" data-testid="number-value">{{ isStringMode ? stringValue : displayValue }}</div>
</div>
</template>
......
<template>
<!-- 方形进度条 -->
<div class="healthy-progress">
<div class="healthy-progress" data-testid="healthy-progress-container">
<div
class="h-8 rounded overflow-hidden border healthy-progress-bar"
:class="borderColor"
data-testid="healthy-progress-bar"
>
<div
id="square-progress-bar"
class="healthy-progress-bar h-full transition-all duration-500 ease-out bg-gradient-to-r from-secondary to-green-400 diagonal-pattern-animation"
:class="diagonalPatternColor"
data-testid="square-progress-bar"
></div>
<div class="justify-between items-center mb-2 healthy-text-position">
<div class="justify-between items-center mb-2 healthy-text-position" data-testid="healthy-text-position">
<span class="font-semibold">健康度:</span>
<span id="square-progress-value" class="text-lg font-bold"> 45%</span>
</div>
......
<template>
<div class="title layout1">
<div class="title layout1" data-testid="warn-title-container">
<span class="warn-title">{{ title }}</span>
<span class="jump-icon" @click="toDetail">>></span>
</div>
<div class="content">
<div class="item" v-for="item in listInfo" :key="item">
<div class="content" data-testid="warn-content-container">
<div class="item" v-for="item in listInfo" :key="item" data-testid="warn-item">
<span class="msg-icon"></span>
{{ item }}
</div>
......
......@@ -20,6 +20,7 @@
:key="item.dusterNo"
:label="item.dusterName"
:value="item.dusterNo"
:data-testid="`duster-option-${item.dusterNo}`"
></el-option>
</el-select>
</el-form-item>
......@@ -43,7 +44,7 @@
<div class="left-box" data-testid="dust-monitoring-charts-section">
<div class="part1 layout3" data-testid="dust-monitoring-charts-container">
<div class="chart-box" v-for="(item, index) in chartData" :key="item" :data-testid="`dust-monitoring-chart-${index}`">
<div :id="'chart' + index" class="chart-item"></div>
<div :id="'chart' + index" class="chart-item" :data-testid="`chart-item-${index}`"></div>
</div>
</div>
<div class="warn-info" data-testid="dust-monitoring-warnings">
......@@ -63,7 +64,7 @@
<div class="part2" data-testid="dust-monitoring-details-section">
<div class="dust-title" data-testid="dust-monitoring-title">{{ dusterName }}</div>
<div class="dust-info" data-testid="dust-monitoring-info-grid">
<div class="info-item" v-for="item in dustInfo" :key="item.label">
<div class="info-item" v-for="item in dustInfo" :key="item.label" data-testid="dust-info-item">
<span class="label">{{ item.label }}</span>
<span class="value">
<gsapNumber :value="item.value" :animationType="'count'" :animationInterval="10000" />
......@@ -73,17 +74,17 @@
<div></div>
</div>
<div class="indicator-box" data-testid="dust-monitoring-gauges">
<div class="indicator-item" id="indicatorOne"></div>
<div class="indicator-item" id="indicatorTwo"></div>
<div class="indicator-item" id="indicatorOne" data-testid="indicator-one"></div>
<div class="indicator-item" id="indicatorTwo" data-testid="indicator-two"></div>
</div>
<div class="position-info" data-testid="dust-monitoring-status-matrix">
<div class="left" v-if="detailObj.compartHealthList.length > 0">
<div class="part" v-for="(list, index) in detailObj.compartHealthList" :key="index">
<div class="left" v-if="detailObj.compartHealthList.length > 0" data-testid="compartment-health-matrix">
<div class="part" v-for="(list, index) in detailObj.compartHealthList" :key="index" data-testid="compartment-row">
<div class="point" :class="{
'status-normal': item.healthStatus === 1,
'status-error': item.healthStatus === 2,
'status-warning': item.healthStatus === 3,
}" v-for="item in detailObj.compartHealthList[index]" :key="item" @click="handleStatusDotClick()">
}" v-for="item in detailObj.compartHealthList[index]" :key="item" @click="handleStatusDotClick()" data-testid="compartment-status-dot">
</div>
</div>
</div>
......@@ -97,15 +98,15 @@
</div> -->
</div>
<div class="other-info" data-testid="dust-monitoring-compartment-info">
<div class="other-info-item">
<div class="other-info-item" data-testid="backflush-compartment-info">
<p class="label">反吹仓室:</p>
<p>{{ detailObj.bachflushCompart }}</p>
</div>
<div class="other-info-item">
<div class="other-info-item" data-testid="leakage-compartment-info">
<p class="label">泄露仓室:</p>
<p>{{ detailObj.leakageCompart }}</p>
</div>
<div class="other-info-item">
<div class="other-info-item" data-testid="fault-compartment-info">
<p class="label">故障仓室:</p>
<p>{{ detailObj.faultCompart }}</p>
</div>
......
<template>
<el-dialog :model-value="modelValue" :title="editData ? '编辑除尘器' : '新增除尘器'" width="500px" :close-on-click-modal="false"
:close-on-press-escape="false" @update:model-value="cancel">
<div class="add-dust-form all-select-btn">
<div class="form-item">
<div class="selector-wrap">
:close-on-press-escape="false" @update:model-value="cancel" data-testid="add-dust-collector-dialog">
<div class="add-dust-form all-select-btn" data-testid="add-dust-form">
<div class="form-item" data-testid="dust-type-selector">
<div class="selector-wrap" data-testid="selector-wrap">
<el-select v-model="formData.dustType" placeholder="除尘器选择" style="width: 238px" filterable remote
:remote-method="handleSearch" :loading="loading">
:remote-method="handleSearch" :loading="loading" data-testid="dust-type-select">
<el-option v-for="item in dustTypeOptions" :key="item.deviceNo" :label="item.deviceName"
:value="item.deviceNo" />
:value="item.deviceNo" :data-testid="`dust-type-option-${item.deviceNo}`" />
</el-select>
<el-button type="deafault" size="default" class="search-btn-balck-theme" @click="selectDustType">选择</el-button>
<el-button type="deafault" size="default" class="search-btn-balck-theme" @click="selectDustType" data-testid="select-dust-type-button">选择</el-button>
</div>
</div>
<div class="form-content">
<el-form ref="formRef" :model="formData" label-width="120px" :rules="rules">
<el-form-item label="除尘器名称" prop="name">
<el-input v-model="formData.name" placeholder="请输入" />
<div class="form-content" data-testid="form-content">
<el-form ref="formRef" :model="formData" label-width="120px" :rules="rules" data-testid="dust-collector-form">
<el-form-item label="除尘器名称" prop="name" data-testid="dust-name-form-item">
<el-input v-model="formData.name" placeholder="请输入" data-testid="dust-name-input" />
</el-form-item>
<el-form-item label="除尘器编号" prop="code">
<el-input v-model="formData.code" placeholder="请输入" />
<el-form-item label="除尘器编号" prop="code" data-testid="dust-code-form-item">
<el-input v-model="formData.code" placeholder="请输入" data-testid="dust-code-input" />
</el-form-item>
<el-form-item label="所属工序" prop="process">
<el-select v-model="formData.process" placeholder="工序选择">
<el-form-item label="所属工序" prop="process" data-testid="process-form-item">
<el-select v-model="formData.process" placeholder="工序选择" data-testid="process-select">
<el-option v-for="item in processOptions" :key="item.productionLineId" :label="item.productionLineName"
:value="item.productionLineId" />
:value="item.productionLineId" :data-testid="`process-option-${item.productionLineId}`" />
</el-select>
</el-form-item>
<el-form-item label="仓室数量" prop="chamberCount">
<el-input v-model.number="formData.chamberCount" placeholder="请输入" />
<el-form-item label="仓室数量" prop="chamberCount" data-testid="chamber-count-form-item">
<el-input v-model.number="formData.chamberCount" placeholder="请输入" data-testid="chamber-count-input" />
</el-form-item>
<el-form-item label="电磁阀数量" prop="valveCount">
<el-input v-model.number="formData.valveCount" placeholder="请输入" />
<el-form-item label="电磁阀数量" prop="valveCount" data-testid="valve-count-form-item">
<el-input v-model.number="formData.valveCount" placeholder="请输入" data-testid="valve-count-input" />
</el-form-item>
<el-form-item label="合理压差范围" required>
<div class="range-input">
<el-form-item prop="pressureMin" class="coordinate-item">
<el-input v-model.number="formData.pressureMin" placeholder="请输入" />
<el-form-item label="合理压差范围" required data-testid="pressure-range-form-item">
<div class="range-input" data-testid="pressure-range-input">
<el-form-item prop="pressureMin" class="coordinate-item" data-testid="pressure-min-form-item">
<el-input v-model.number="formData.pressureMin" placeholder="请输入" data-testid="pressure-min-input" />
</el-form-item>
<span class="separator"></span>
<el-form-item prop="pressureMax" class="coordinate-item">
<el-input v-model.number="formData.pressureMax" placeholder="请输入" />
<span class="separator" data-testid="pressure-separator"></span>
<el-form-item prop="pressureMax" class="coordinate-item" data-testid="pressure-max-form-item">
<el-input v-model.number="formData.pressureMax" placeholder="请输入" data-testid="pressure-max-input" />
</el-form-item>
</div>
</el-form-item>
<el-form-item label="服务器IP" prop="serverIp" required>
<el-input v-model="formData.serverIp" placeholder="请输入" />
<el-form-item label="服务器IP" prop="serverIp" required data-testid="server-ip-form-item">
<el-input v-model="formData.serverIp" placeholder="请输入" data-testid="server-ip-input" />
</el-form-item>
<el-form-item label="二维图坐标" required>
<div class="coordinate-inputs">
<el-form-item prop="coordinateX" class="coordinate-item">
<el-input v-model.number="formData.coordinateX" placeholder="X坐标">
<el-form-item label="二维图坐标" required data-testid="coordinate-form-item">
<div class="coordinate-inputs" data-testid="coordinate-inputs">
<el-form-item prop="coordinateX" class="coordinate-item" data-testid="coordinate-x-form-item">
<el-input v-model.number="formData.coordinateX" placeholder="X坐标" data-testid="coordinate-x-input">
<template #prefix>X:</template>
</el-input>
</el-form-item>
<el-form-item prop="coordinateY" class="coordinate-item">
<el-input v-model.number="formData.coordinateY" placeholder="Y坐标">
<el-form-item prop="coordinateY" class="coordinate-item" data-testid="coordinate-y-form-item">
<el-input v-model.number="formData.coordinateY" placeholder="Y坐标" data-testid="coordinate-y-input">
<template #prefix>Y:</template>
</el-input>
</el-form-item>
......@@ -71,10 +71,10 @@
</el-form>
</div>
<div class="form-footer">
<div class="form-footer" data-testid="form-footer">
<el-button @click="cancel" class="cancel-btn-balck-theme">取消</el-button>
<el-button type="default" class="search-btn-balck-theme" @click="submitForm">确认</el-button>
<el-button @click="cancel" class="cancel-btn-balck-theme" data-testid="cancel-button">取消</el-button>
<el-button type="default" class="search-btn-balck-theme" @click="submitForm" data-testid="confirm-button">确认</el-button>
</div>
</div>
</el-dialog>
......
......@@ -7,30 +7,33 @@
:close-on-press-escape="false"
@update:model-value="$emit('update:modelValue', $event)"
@open="initializeState"
data-testid="room-setting-dialog"
>
<el-form :model="roomForm" label-width="120px">
<el-form-item label="仓室数量:">
<el-form :model="roomForm" label-width="120px" data-testid="room-form">
<el-form-item label="仓室数量:" data-testid="total-rooms-form-item">
<el-input
v-model="roomForm.totalRooms"
controls-position="right"
style="width: 150px"
disabled
data-testid="total-rooms-input"
/>
</el-form-item>
<el-form-item label="仓室分几排:">
<el-form-item label="仓室分几排:" data-testid="rows-form-item">
<el-input-number
v-model="roomForm.rows"
:min="0"
:max="20"
controls-position="right"
@change="handleRowsChange"
data-testid="rows-input-number"
/>
</el-form-item>
</el-form>
<!-- 分布表格 -->
<div class="distribution-table">
<div class="table-title">
<div class="distribution-table" data-testid="distribution-table">
<div class="table-title" data-testid="table-title">
仓室数量分布
<div
v-if="distributionDiff > 0"
......@@ -38,6 +41,7 @@
'distribution-warning',
distributionDiff > 0 ? 'warning-less' : 'warning-less',
]"
data-testid="distribution-warning"
>
当前仓室总数{{ distributionDiff > 0 ? "大于" : "小于" }}默认仓室数量
{{ Math.abs(distributionDiff) }} 个,请修改。
......@@ -49,13 +53,14 @@
size="small"
border
:resizable="false"
data-testid="distribution-data-table"
>
<el-table-column prop="compartPositionRow" label="排" width="180">
<el-table-column prop="compartPositionRow" label="排" width="180" data-testid="row-column">
<template #default="{ row }">
<span>{{ row.compartPositionRow }}</span>
<span data-testid="row-label">{{ row.compartPositionRow }}</span>
</template>
</el-table-column>
<el-table-column prop="compartPositionColumnNum" label="仓数量">
<el-table-column prop="compartPositionColumnNum" label="仓数量" data-testid="count-column">
<template #default="{ row }">
<el-input-number
v-model="row.compartPositionColumnNum"
......@@ -64,6 +69,7 @@
controls-position="right"
size="small"
@change="handleDistributionChange"
:data-testid="`room-count-input-${row.compartPositionRow}`"
/>
</template>
</el-table-column>
......@@ -71,13 +77,14 @@
</div>
<template #footer>
<span class="dialog-footer all-select-btn">
<el-button @click="$emit('update:modelValue', false)" class="cancel-btn-balck-theme">取消</el-button>
<span class="dialog-footer all-select-btn" data-testid="dialog-footer">
<el-button @click="$emit('update:modelValue', false)" class="cancel-btn-balck-theme" data-testid="cancel-button">取消</el-button>
<el-button
type="default"
class="search-btn-balck-theme"
@click="handleConfirm"
:disabled="distributionDiff > 0"
data-testid="confirm-button"
>
确认
</el-button>
......
......@@ -6,32 +6,34 @@
:close-on-click-modal="false"
:close-on-press-escape="false"
@update:model-value="cancel"
data-testid="valve-setting-dialog"
>
<div class="valve-setting all-select-btn">
<div class="setting-row">
<div class="input-group">
<span>电磁阀总数量:</span>
<el-input v-model="valveForm.total" style="width: 120px" disabled />
<div class="valve-setting all-select-btn" data-testid="valve-setting-container">
<div class="setting-row" data-testid="valve-setting-controls">
<div class="input-group" data-testid="total-valves-input-group">
<span data-testid="total-valves-label">电磁阀总数量:</span>
<el-input v-model="valveForm.total" style="width: 120px" disabled data-testid="total-valves-input" />
</div>
<el-button type="default" class="search-btn-balck-theme" @click="handleAverageDistribute"
<el-button type="default" class="search-btn-balck-theme" @click="handleAverageDistribute" data-testid="average-distribute-button"
>平均分布生成</el-button
>
</div>
<div class="table-title">仓室脉冲阀数量分布</div>
<div class="setting-row">
<div class="input-group">
<span>仓室编号:</span>
<el-select v-model="valveForm.frontCompartNo" style="width: 120px" @change="handleRoomChange">
<div class="table-title" data-testid="valve-table-title">仓室脉冲阀数量分布</div>
<div class="setting-row" data-testid="setting-row-controls">
<div class="input-group" data-testid="compartment-number-select">
<span data-testid="compartment-number-label">仓室编号:</span>
<el-select v-model="valveForm.frontCompartNo" style="width: 120px" @change="handleRoomChange" data-testid="compartment-select">
<el-option
v-for="cell in statusData"
:key="cell.frontCompartNo"
:label="`${cell.frontCompartNo}仓`"
:value="cell.frontCompartNo"
:data-testid="`compartment-option-${cell.frontCompartNo}`"
/>
</el-select>
</div>
<div class="input-group">
<span>电磁阀分布数量:</span>
<div class="input-group" data-testid="valve-distribution-input">
<span data-testid="valve-distribution-label">电磁阀分布数量:</span>
<el-input-number
v-model="valveForm.valveNum"
:min="0"
......@@ -39,23 +41,25 @@
style="width: 120px"
@change="handleValveNumChange"
:class="{ 'is-error': errorMessage }"
data-testid="valve-distribution-number-input"
/>
</div>
<div class="input-group">
<span>布袋数量:</span>
<div class="input-group" data-testid="bag-quantity-input">
<span data-testid="bag-quantity-label">布袋数量:</span>
<el-input-number
v-model="valveForm.bagNum"
:min="0"
controls-position="right"
style="width: 120px"
@change="handleBagNumChange"
data-testid="bag-quantity-number-input"
/>
</div>
</div>
<!-- 错误提示 -->
<div v-if="errorMessage" class="error-message">
<i class="el-icon-warning"></i>
<div v-if="errorMessage" class="error-message" data-testid="error-message">
<i class="el-icon-warning" data-testid="error-icon"></i>
{{ errorMessage }}
</div>
......@@ -65,38 +69,40 @@
class="save-btn"
@click="handleSave"
:disabled="!!errorMessage || valveForm.valveNum === null || valveForm.bagNum === null"
data-testid="set-button"
>设置</el-button>
</div> -->
</div>
<!-- 分布表格 -->
<div class="bagNum-table">
<div class="table-title">
<div class="bagNum-table" data-testid="valve-distribution-table">
<div class="table-title" data-testid="distribution-table-title">
仓室脉冲阀数量分布
<span class="valve-valveNum-info">
<span class="valve-valveNum-info" data-testid="valve-count-info">
(当前分配: {{ currentTotalValves }}/{{ valveForm.total }})
</span>
</div>
<div class="valve-bagNum-grid">
<div class="valve-row">
<div class="valve-bagNum-grid" data-testid="valve-grid">
<div class="valve-row" data-testid="valve-row">
<div
v-for="(cell, colIndex) in statusData"
:key="colIndex"
class="valve-cell"
:class="{ 'active-cell': cell.frontCompartNo === valveForm.frontCompartNo }"
@click="valveForm.frontCompartNo = cell.frontCompartNo; handleRoomChange(cell.frontCompartNo)"
:data-testid="`valve-cell-${cell.frontCompartNo}`"
>
<div class="cell-index">{{ cell.frontCompartNo }}</div>
<div class="cell-value">{{ cell.valveNum }} <span>({{ cell.bagNum }})</span></div>
<div class="cell-index" data-testid="cell-index">{{ cell.frontCompartNo }}</div>
<div class="cell-value" data-testid="cell-value">{{ cell.valveNum }} <span data-testid="cell-bag-count">({{ cell.bagNum }})</span></div>
</div>
</div>
</div>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="cancel" class="cancel-btn-balck-theme">取消</el-button>
<el-button type="default" class="search-btn-balck-theme" @click="handleConfirm" :disabled="!!errorMessage">
<span class="dialog-footer" data-testid="valve-dialog-footer">
<el-button @click="cancel" class="cancel-btn-balck-theme" data-testid="cancel-button">取消</el-button>
<el-button type="default" class="search-btn-balck-theme" @click="handleConfirm" :disabled="!!errorMessage" data-testid="save-button">
保存
</el-button>
</span>
......
......@@ -38,12 +38,13 @@
:filter-method="filterProductionLine"
data-testid="dust-production-line-select"
>
<el-option key="all" label="全部" value="all" />
<el-option key="all" label="全部" value="all" data-testid="production-line-option-all" />
<el-option
v-for="item in productionLineFiltered"
:key="item.productionLineId"
:label="item.productionLineName"
:value="item.productionLineId"
:data-testid="`production-line-option-${item.productionLineId}`"
/>
</el-select>
</el-form-item>
......
<template>
<div class="container el-self-container all-select-btn" v-if="dialogVisible">
<div class="close-box" @click="cancelFun">
<div class="container el-self-container all-select-btn" v-if="dialogVisible" data-testid="params-settings-container">
<div class="close-box" @click="cancelFun" data-testid="close-button">
<el-icon size="25"><Close /></el-icon>
</div>
<h1 class="title">sensor设置信息</h1>
<div class="content-box">
<div class="grid-container">
<div class="content-box" data-testid="content-box">
<div class="grid-container" data-testid="grid-container">
<!-- 左侧参数设置 -->
<div class="setting-group">
<div class="setting-group" data-testid="left-settings">
<div
class="setting-row"
v-for="item in leftInfoEnum"
......@@ -22,29 +22,33 @@
style="margin-right: 10px"
v-if="item.type2 === 'checkbox'"
v-model="infoObj[item.propKey2]"
:data-testid="`left-checkbox-${item.propKey2}`"
></el-checkbox>
{{ item.label }}
</span>
<el-input
v-if="item.type === 'input'"
v-model="infoObj[item.propKey]"
:data-testid="`left-input-${item.propKey}`"
></el-input>
<el-select
v-model="infoObj[item.propKey]"
v-if="item.type === 'select'"
:data-testid="`left-select-${item.propKey}`"
>
<el-option
v-for="option in item.options"
:key="option.code"
:label="option.name"
:value="option.code"
:data-testid="`left-select-option-${option.code}`"
></el-option>
</el-select>
</div>
</div>
<!-- 右侧参数设置 -->
<div class="setting-group">
<div class="setting-group" data-testid="right-settings">
<div
class="setting-row"
v-for="item in rightInfoEnum"
......@@ -54,24 +58,25 @@
<el-input
v-if="item.type === 'input'"
v-model="infoObj[item.propKey]"
:data-testid="`right-input-${item.propKey}`"
></el-input>
</div>
</div>
</div>
<!-- 复选框区域 -->
<div class="checkbox-group">
<div class="checkbox-group" data-testid="checkbox-group-1">
<label class="custom-checkbox">
<input type="checkbox" v-model="infoObj.onlineBackblow" /> 在线反吹
<input type="checkbox" v-model="infoObj.onlineBackblow" data-testid="online-backblow-checkbox" /> 在线反吹
</label>
<label class="custom-checkbox">
<input type="checkbox" v-model="infoObj.fastBlow" /> 快/慢
<input type="checkbox" v-model="infoObj.fastBlow" data-testid="fast-blow-checkbox" /> 快/慢
</label>
<label class="custom-checkbox">
<input type="checkbox" v-model="infoObj.d01Alarm" /> DO1报警
<input type="checkbox" v-model="infoObj.d01Alarm" data-testid="d01-alarm-checkbox" /> DO1报警
</label>
<label class="custom-checkbox">
<input type="checkbox" v-model="infoObj.d13Alarm" /> DI3有效
<input type="checkbox" v-model="infoObj.d13Alarm" data-testid="d13-alarm-checkbox" /> DI3有效
</label>
</div>
......@@ -81,59 +86,62 @@
type="textarea"
:rows="4"
v-model="infoObj.compartSort"
data-testid="compart-sort-textarea"
></el-input>
<!-- 复选框区域 -->
<div class="checkbox-group mgr10">
<div class="checkbox-group mgr10" data-testid="checkbox-group-2">
<label class="custom-checkbox">
<input type="checkbox" v-model="infoObj.diInput" /> DI接入Y/N
<input type="checkbox" v-model="infoObj.diInput" data-testid="di-input-checkbox" /> DI接入Y/N
</label>
<label class="custom-checkbox">
<input type="checkbox" v-model="infoObj.showEarlyWarn" /> 显示预警
<input type="checkbox" v-model="infoObj.showEarlyWarn" data-testid="show-early-warn-checkbox" /> 显示预警
</label>
<div class="setting-row">
<div class="setting-row" data-testid="device-relation-setting">
<span class="device-label">关联设备</span>
<el-select v-model="infoObj.relateDevicesNum" placeholder="请选择">
<el-select v-model="infoObj.relateDevicesNum" placeholder="请选择" data-testid="relate-devices-select">
<el-option
v-for="(item, index) in 21"
:key="item"
:value="index"
:label="index"
:data-testid="`relate-device-option-${index}`"
></el-option>
</el-select>
</div>
</div>
<!-- 复选框区域 -->
<div class="checkbox-group mgr10">
<div class="select-input">
<div class="checkbox-group mgr10" data-testid="checkbox-group-3">
<div class="select-input" data-testid="valve-device-setting">
<label class="custom-checkbox">
<input
type="checkbox"
checked
v-model="infoObj.hasLiftValveCommunicationIp"
data-testid="has-lift-valve-checkbox"
/>
脉冲阀设备Y/N 通信IP
</label>
<div style="margin-left: 10px">
<el-input v-model="infoObj.liftValveCommunicationIp"></el-input>
<div style="margin-left: 10px" data-testid="ip-input-container">
<el-input v-model="infoObj.liftValveCommunicationIp" data-testid="lift-valve-ip-input"></el-input>
</div>
</div>
<div>
<div data-testid="pulse-blow-setting">
<label class="custom-checkbox">
<input type="checkbox" v-model="infoObj.indoorPulseBlow"/> 室内脉冲轮流喷吹
<input type="checkbox" v-model="infoObj.indoorPulseBlow" data-testid="indoor-pulse-blow-checkbox"/> 室内脉冲轮流喷吹
</label>
</div>
</div>
</div>
<!-- 操作按钮 -->
<div class="button-group">
<button class="btn search-btn-balck-theme" @click="confirmFun">确认</button>
<button class="btn cancel-btn-balck-theme" @click="cancelFun">取消</button>
<div class="button-group" data-testid="button-group">
<button class="btn search-btn-balck-theme" @click="confirmFun" data-testid="confirm-button">确认</button>
<button class="btn cancel-btn-balck-theme" @click="cancelFun" data-testid="cancel-button">取消</button>
</div>
</div>
<div class="mask" v-if="dialogVisible" @click="cancelFun"></div>
<div class="mask" v-if="dialogVisible" @click="cancelFun" data-testid="dialog-mask"></div>
</template>
<script setup>
import { ref, reactive, computed, defineEmits, onMounted, watch } from "vue";
......
......@@ -14,6 +14,7 @@
:key="item.productionLineId"
:label="item.productionLineName"
:value="item.productionLineId"
:data-testid="`equipment-production-line-option-${item.productionLineId}`"
/>
</el-select>
</el-form-item>
......@@ -47,6 +48,7 @@
:key="item.deviceTypeId"
:label="item.deviceType"
:value="item.deviceTypeId"
:data-testid="`equipment-device-type-option-${item.deviceTypeId}`"
/>
</el-select>
</el-form-item>
......
<template>
<div class="page-container collectorList all-select-btn">
<div class="content-box">
<div class="search">
<el-form :inline="true" :model="formInline" class="demo-form-inline">
<div class="page-container collectorList all-select-btn" data-testid="suspend-management-container">
<div class="content-box" data-testid="content-box">
<div class="search" data-testid="search-area">
<el-form :inline="true" :model="formInline" class="demo-form-inline" data-testid="search-form">
<el-form-item label="项目名称">
<el-select v-model="formInline.projectId" style="width: 200px">
<el-select v-model="formInline.projectId" style="width: 200px" data-testid="project-select">
<el-option
v-for="i in nameList.list"
:key="i"
:label="`${i.name}`"
:value="i.id"
:data-testid="`project-option-${i.id}`"
/>
</el-select>
</el-form-item>
<el-form-item label="设备类型">
<el-select v-model="formInline.deviceType" style="width: 200px">
<el-select v-model="formInline.deviceType" style="width: 200px" data-testid="device-type-select">
<el-option
v-for="i in typeList.list"
:key="i"
:label="`${i.desc}`"
:value="i.code"
:data-testid="`device-type-option-${i.code}`"
/>
</el-select>
</el-form-item>
<el-form-item label="原因分类">
<el-select v-model="formInline.reasonCategory" style="width: 200px">
<el-select v-model="formInline.reasonCategory" style="width: 200px" data-testid="reason-category-select">
<el-option
v-for="i in reasonList.list"
:key="i"
:label="`${i.value}`"
:value="i.key"
:data-testid="`reason-category-option-${i.key}`"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="default" class="reset-btn-balck-theme" @click="onReset"
<el-button type="default" class="reset-btn-balck-theme" @click="onReset" data-testid="reset-button"
>重置</el-button
>
<el-button type="default" class="search-btn-balck-theme" @click="onSubmit"
<el-button type="default" class="search-btn-balck-theme" @click="onSubmit" data-testid="search-button"
>查询</el-button
>
</el-form-item>
</el-form>
</div>
<div class="table-box">
<div class="table-box" data-testid="table-container">
<common-table
ref="pageRef"
:data="tableData.list"
......@@ -59,12 +62,13 @@
next: '后一页',
jumper: '跳至',
}"
data-testid="suspend-management-table"
>
<template #index="{ $index }">
{{ getIndex($index) }}
</template>
<template #operate="{ row }">
<span class="health-score green-color" @click="linkTo(row)">挂起期间告警</span>
<span class="health-score green-color" @click="linkTo(row)" data-testid="suspend-alarm-link">挂起期间告警</span>
</template>
<!-- <template #operation="{ row }">
<span class="view-btn" @click="handleView(row)">详情</span>
......
@echo off
REM DC-TOM Cypress Studio 启动器 - Windows 批处理版本
REM 功能: 先启动开发服务器,然后启动 Cypress Studio
echo.
echo [INFO] DC-TOM Cypress Studio 启动器 - Windows 版本
echo [INFO] 运行平台: Windows
echo.
REM 检查 Node.js 是否安装
node --version >nul 2>&1
if errorlevel 1 (
echo [ERROR] Node.js 未安装或不在 PATH 中
echo 请先安装 Node.js: https://nodejs.org
pause
exit /b 1
)
REM 检查 npm 是否可用
npm --version >nul 2>&1
if errorlevel 1 (
echo [ERROR] npm 未安装或不在 PATH 中
pause
exit /b 1
)
REM 运行 Node.js 脚本
echo [INFO] 启动 Cypress Studio 启动器...
node start-studio.cjs %*
REM 检查脚本执行结果
if errorlevel 1 (
echo.
echo [ERROR] 启动器执行失败
echo 可能的解决方案:
echo 1. 确保项目依赖已安装: npm install
echo 2. 检查 Cypress 是否正确安装
echo 3. 确保端口 3000 未被占用
pause
exit /b 1
)
echo.
echo [INFO] 启动器执行完成
pause
\ No newline at end of file
#!/usr/bin/env node
/**
* DC-TOM Cypress Studio 启动器
* 跨平台支持 (Windows/macOS/Linux)
* 功能: 先启动开发服务器,然后启动 Cypress Studio
*/
const { spawn, exec } = require('child_process');
const http = require('http');
const os = require('os');
// 颜色定义 (Windows CMD 支持的 ANSI 颜色)
const colors = {
reset: '\x1b[0m',
red: '\x1b[31m',
green: '\x1b[32m',
yellow: '\x1b[33m',
blue: '\x1b[34m',
cyan: '\x1b[36m'
};
// 日志函数
const log = {
info: (msg) => console.log(`${colors.blue}ℹ️ ${msg}${colors.reset}`),
success: (msg) => console.log(`${colors.green}✅ ${msg}${colors.reset}`),
warning: (msg) => console.log(`${colors.yellow}⚠️ ${msg}${colors.reset}`),
error: (msg) => console.log(`${colors.red}❌ ${msg}${colors.reset}`),
plain: (msg) => console.log(msg)
};
// 全局变量
let devServerProcess = null;
let isWindows = os.platform() === 'win32';
// 检查端口是否被占用
function checkPort(port) {
return new Promise((resolve) => {
const server = http.createServer();
server.listen(port, () => {
server.close(() => resolve(false)); // 端口未被占用
});
server.on('error', () => resolve(true)); // 端口被占用
});
}
// 等待开发服务器启动
function waitForServer(url, maxAttempts = 30) {
return new Promise((resolve) => {
let attempts = 0;
const checkServer = () => {
attempts++;
const request = http.get(url, (res) => {
if (res.statusCode === 200) {
resolve(true);
} else if (attempts < maxAttempts) {
setTimeout(checkServer, 1000);
} else {
resolve(false);
}
});
request.on('error', () => {
if (attempts < maxAttempts) {
process.stdout.write('.');
setTimeout(checkServer, 1000);
} else {
resolve(false);
}
});
request.setTimeout(2000, () => {
request.destroy();
if (attempts < maxAttempts) {
process.stdout.write('.');
setTimeout(checkServer, 1000);
} else {
resolve(false);
}
});
};
checkServer();
});
}
// 启动开发服务器
async function startDevServer() {
const portOccupied = await checkPort(3000);
if (portOccupied) {
log.warning('端口 3000 已被占用,跳过开发服务器启动');
log.info('如果需要重新启动,请先停止现有的服务');
return true;
}
log.info('启动开发服务器...');
return new Promise((resolve) => {
// Windows 和 Unix 系统使用不同的命令
const npmCommand = isWindows ? 'npm.cmd' : 'npm';
devServerProcess = spawn(npmCommand, ['run', 'dev'], {
cwd: process.cwd(),
stdio: ['pipe', 'pipe', 'pipe'],
shell: isWindows
});
devServerProcess.stdout.on('data', (data) => {
const output = data.toString();
if (output.includes('Local:') && output.includes('localhost:3000')) {
log.success(`开发服务器启动成功 (PID: ${devServerProcess.pid})`);
resolve(true);
}
});
devServerProcess.stderr.on('data', (data) => {
// Vite 的一些输出会到 stderr,但不一定是错误
const output = data.toString();
if (output.includes('Local:') && output.includes('localhost:3000')) {
log.success(`开发服务器启动成功 (PID: ${devServerProcess.pid})`);
resolve(true);
}
});
devServerProcess.on('error', (error) => {
log.error(`开发服务器启动失败: ${error.message}`);
resolve(false);
});
devServerProcess.on('exit', (code) => {
if (code !== 0) {
log.error(`开发服务器异常退出,代码: ${code}`);
resolve(false);
}
});
// 设置超时
setTimeout(() => {
log.info('等待开发服务器启动...');
waitForServer('http://localhost:3000').then((serverReady) => {
if (serverReady) {
log.success('开发服务器已就绪');
resolve(true);
} else {
log.error('开发服务器启动超时');
resolve(false);
}
});
}, 3000);
});
}
// 启动 Cypress Studio
function startCypressStudio() {
log.info('启动 Cypress Studio...');
log.info('Cypress Studio 将在新窗口中打开');
log.info('您可以在 Studio 中录制和编辑测试用例');
const npxCommand = isWindows ? 'npx.cmd' : 'npx';
const cypressProcess = spawn(npxCommand, ['cypress', 'open', '--config', 'experimentalStudio=true'], {
cwd: process.cwd(),
stdio: 'inherit',
shell: isWindows
});
cypressProcess.on('error', (error) => {
log.error(`Cypress Studio 启动失败: ${error.message}`);
cleanup();
process.exit(1);
});
cypressProcess.on('exit', (code) => {
log.info('Cypress Studio 已关闭');
cleanup();
process.exit(code || 0);
});
}
// 清理函数
function cleanup() {
log.info('正在清理进程...');
if (devServerProcess && !devServerProcess.killed) {
log.info(`停止开发服务器 (PID: ${devServerProcess.pid})`);
if (isWindows) {
// Windows 需要强制终止整个进程树
exec(`taskkill /pid ${devServerProcess.pid} /T /F`, (error) => {
if (error) {
log.warning('无法自动停止开发服务器,请手动终止');
}
});
} else {
devServerProcess.kill('SIGTERM');
}
}
}
// 显示使用说明
function showUsage() {
log.plain('用法: node start-studio.js [选项]');
log.plain('');
log.plain('选项:');
log.plain(' --help, -h 显示此帮助信息');
log.plain('');
log.plain('功能:');
log.plain(' 1. 自动启动开发服务器 (localhost:3000)');
log.plain(' 2. 等待服务器就绪');
log.plain(' 3. 启动 Cypress Studio');
log.plain('');
log.plain('注意:');
log.plain(' - 支持 Windows/macOS/Linux');
log.plain(' - 如果端口 3000 已被占用,将跳过开发服务器启动');
log.plain(' - 关闭 Cypress Studio 时,开发服务器也会自动停止');
log.plain(' - 使用 Ctrl+C 可以同时停止两个服务');
}
// 主函数
async function main() {
// 处理命令行参数
if (process.argv.includes('--help') || process.argv.includes('-h')) {
showUsage();
return;
}
console.log();
log.info('🚀 DC-TOM Cypress Studio 启动器');
log.info(`📦 运行平台: ${os.platform()} ${os.arch()}`);
console.log();
// 启动开发服务器
const serverStarted = await startDevServer();
if (serverStarted) {
console.log();
log.success('开发环境准备就绪');
console.log();
// 启动 Cypress Studio
startCypressStudio();
} else {
log.error('无法启动开发服务器,Cypress Studio 启动已取消');
process.exit(1);
}
}
// 设置退出处理
process.on('SIGINT', () => {
log.info('\n收到中断信号,正在退出...');
cleanup();
process.exit(0);
});
process.on('SIGTERM', () => {
log.info('\n收到终止信号,正在退出...');
cleanup();
process.exit(0);
});
process.on('exit', () => {
cleanup();
});
// 运行主函数
main().catch((error) => {
log.error(`启动器发生错误: ${error.message}`);
cleanup();
process.exit(1);
});
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment