自动检测/手动检测页面
This commit is contained in:
234
frontend/src/views/plan/preTest/components/scrollTable.vue
Normal file
234
frontend/src/views/plan/preTest/components/scrollTable.vue
Normal file
@@ -0,0 +1,234 @@
|
||||
<template>
|
||||
{{ dataSource.length }}
|
||||
<div
|
||||
class="scrollContainer"
|
||||
:key="currentTime"
|
||||
:style="{ height: `${height}px` }"
|
||||
>
|
||||
<div
|
||||
class="scrollHead"
|
||||
:style="{
|
||||
height: headerHeight + 'px',
|
||||
}"
|
||||
>
|
||||
<div v-for="l in columns" :key="l.key" :style="{ width: `${l.width}px` }">
|
||||
{{ l.title }}
|
||||
</div>
|
||||
</div>
|
||||
<ul
|
||||
class="scrollBody"
|
||||
ref="wrapperDom"
|
||||
:style="{ height: `${height - headerHeight}px` }"
|
||||
>
|
||||
<ul ref="childDom1" @mouseenter="handleEnter" @mouseleave="handleLeave">
|
||||
<li
|
||||
v-for="(l, i) in dataSource"
|
||||
:data-key="rowKey ? l[rowKey] : `list${i}`"
|
||||
:key="rowKey ? l[rowKey] : `list${i}`"
|
||||
:style="{ height: `${rowHeight}px` }"
|
||||
>
|
||||
<div
|
||||
v-for="(p, c) in columns"
|
||||
:key="`p${c}`"
|
||||
:style="getStyle(p, l)"
|
||||
@click="
|
||||
(e) => {
|
||||
e.stopPropagation();
|
||||
onCellClick(l, p);
|
||||
onRowClick?.(l);
|
||||
}
|
||||
"
|
||||
>
|
||||
{{ p?.render?.(i, l, l[p.key]) || l[p.key] }}
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<ul ref="childDom2"></ul>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import {
|
||||
onMounted,
|
||||
watch,
|
||||
ref,
|
||||
onBeforeUnmount,
|
||||
computed,
|
||||
nextTick,
|
||||
defineProps,
|
||||
} from "vue";
|
||||
|
||||
const props = defineProps([
|
||||
"height",
|
||||
"dataSource",
|
||||
"columns",
|
||||
"headerHeight",
|
||||
"rowHeight",
|
||||
"onRowClick",
|
||||
"rowKey",
|
||||
"scroll",
|
||||
]);
|
||||
export interface TableColumn {
|
||||
key: string;
|
||||
title: string;
|
||||
width: number;
|
||||
render?: (index: number, data: Record<string, any>, text: any) => any;
|
||||
onClick?: (data: Record<string, any>) => void;
|
||||
}
|
||||
|
||||
// const props = defineProps<ViewProps>()
|
||||
const { height, columns, rowHeight = 27.5, headerHeight = 36, rowKey } = props;
|
||||
|
||||
const wrapperDom = ref<any>();
|
||||
const childDom1 = ref<any>();
|
||||
const childDom2 = ref<any>();
|
||||
const currentTime = ref(new Date().getTime());
|
||||
let count = 0;
|
||||
let reqAnimation: number;
|
||||
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
reqAnimation = window.requestAnimationFrame(taskStart);
|
||||
});
|
||||
});
|
||||
onBeforeUnmount(() => {
|
||||
handleEnter();
|
||||
});
|
||||
const dataSource = computed(() => {
|
||||
console.log("dataSource", dataSource);
|
||||
return props.dataSource;
|
||||
});
|
||||
watch(
|
||||
() => props.dataSource,
|
||||
() => {
|
||||
currentTime.value = new Date().getTime();
|
||||
}
|
||||
);
|
||||
|
||||
const getStyle = (p, l) => {
|
||||
let pStyle = { width: `${p.width}px` };
|
||||
if (l.lineColor) {
|
||||
pStyle["color"] = l.lineColor;
|
||||
}
|
||||
return pStyle;
|
||||
};
|
||||
|
||||
//使用滚动表格,自动触发加载数据
|
||||
const taskStart = () => {
|
||||
// if (
|
||||
// childDom1.value?.clientHeight >= wrapperDom.value?.clientHeight &&
|
||||
// childDom2.value?.clientHeight < 10
|
||||
// ) {
|
||||
// childDom2.value.innerHTML = childDom1.value.innerHTML;
|
||||
// }
|
||||
// if (wrapperDom.value?.scrollTop >= childDom1.value?.scrollHeight) {
|
||||
// // wrapperDom.value.scrollTop = 0;
|
||||
// count = 0;
|
||||
// }
|
||||
// else {
|
||||
count += 1;
|
||||
wrapperDom.value.scrollTop = count;
|
||||
// }
|
||||
if (props.scroll) {
|
||||
reqAnimation = window.requestAnimationFrame(taskStart);
|
||||
}
|
||||
};
|
||||
|
||||
const handleEnter = () => {
|
||||
window.cancelAnimationFrame(reqAnimation);
|
||||
};
|
||||
const handleLeave = () => {
|
||||
reqAnimation = window.requestAnimationFrame(taskStart);
|
||||
};
|
||||
const onCellClick = (l: Record<string, any>, p: TableColumn) => {
|
||||
p?.onClick?.(l);
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.scrollContainer {
|
||||
width: 100%;
|
||||
|
||||
div {
|
||||
text-align: center;
|
||||
display: inline-block;
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
font-weight: normal;
|
||||
font-stretch: normal;
|
||||
letter-spacing: 0;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.scrollHead {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
// background-color: rgba(33, 60, 93, 0.55);
|
||||
background-color: #3F9EFF;
|
||||
|
||||
div {
|
||||
font-size: 14px;
|
||||
font-stretch: normal;
|
||||
letter-spacing: 0;
|
||||
font-family: MicrosoftYaHei, sans-serif;
|
||||
font-weight: bold;
|
||||
color: #ffffff;
|
||||
opacity: 0.47;
|
||||
flex:1;
|
||||
border-left: 1px solid #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.scrollBody {
|
||||
overflow-y: scroll;
|
||||
width: 100%;
|
||||
scrollbar-width: none;
|
||||
-ms-overflow-style: none;
|
||||
|
||||
ul {
|
||||
height: auto;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
li {
|
||||
list-style: none;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
height: 36px;
|
||||
color: #fff;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
li div {
|
||||
line-height: 36px;
|
||||
color: #24acef;
|
||||
white-space: nowrap; /* 文本不换行 */
|
||||
overflow: hidden; /* 溢出部分隐藏 */
|
||||
text-overflow: ellipsis; /* 溢出部分用"..."代替 */
|
||||
flex:1;
|
||||
border-left: 1px solid #eee;
|
||||
}
|
||||
|
||||
li:hover {
|
||||
background: rgba(43, 143, 171, 0.52);
|
||||
> div {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
li:nth-child(even) {
|
||||
background-color: rgba(43, 143, 171, 0.13);
|
||||
}
|
||||
|
||||
li:nth-child(even):hover {
|
||||
background: rgba(43, 143, 171, 0.52);
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
141
frontend/src/views/plan/preTest/components/timeLine.vue
Normal file
141
frontend/src/views/plan/preTest/components/timeLine.vue
Normal file
@@ -0,0 +1,141 @@
|
||||
<template>
|
||||
<div class="time">
|
||||
<!-- 自定义预检测图表页面 -->
|
||||
<div class="time_device" v-for="(item, index) in data" :key="index">
|
||||
<p>
|
||||
{{ item.name }}
|
||||
</p>
|
||||
<!-- 按顺序执行 -->
|
||||
<div class="device_children" v-for="(vv, vvs) in item.children">
|
||||
<div class="item_children" :key="vvs">
|
||||
<p>正在进行: {{ vv.name }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 分割线 -->
|
||||
<div class="time_split_line"></div>
|
||||
<div class="time_end" v-if="item.children.length == 4">预校验完毕!</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted, defineExpose } from "vue";
|
||||
import { MoreFilled } from "@element-plus/icons-vue";
|
||||
|
||||
const activities = [
|
||||
{
|
||||
content: "Custom icon",
|
||||
timestamp: "2018-04-12 20:46",
|
||||
size: "large",
|
||||
type: "primary",
|
||||
icon: MoreFilled,
|
||||
},
|
||||
{
|
||||
content: "Custom color",
|
||||
timestamp: "2018-04-03 20:46",
|
||||
color: "#0bbd87",
|
||||
},
|
||||
{
|
||||
content: "Custom size",
|
||||
timestamp: "2018-04-03 20:46",
|
||||
size: "large",
|
||||
},
|
||||
{
|
||||
content: "Custom hollow",
|
||||
timestamp: "2018-04-03 20:46",
|
||||
type: "primary",
|
||||
hollow: true,
|
||||
},
|
||||
{
|
||||
content: "Default node",
|
||||
timestamp: "2018-04-03 20:46",
|
||||
},
|
||||
];
|
||||
const data = ref([]);
|
||||
const open = (list: any) => {
|
||||
data.value = list;
|
||||
console.log(data.value, "99999999");
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
console.log();
|
||||
});
|
||||
defineExpose({ open });
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.time {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
height: calc(100vh - 260px);
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
//设备层
|
||||
.time_device {
|
||||
flex: none;
|
||||
width: 200px;
|
||||
min-height: 40px;
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin: 0 20px;
|
||||
position: relative;
|
||||
background: #fff;
|
||||
|
||||
//分割线
|
||||
.time_split_line {
|
||||
width: 2px;
|
||||
// min-height: 50px;
|
||||
height: 100%;
|
||||
background-color: #0a47a1;
|
||||
position: absolute;
|
||||
left: 49%;
|
||||
top: 0;
|
||||
z-index: 0;
|
||||
}
|
||||
> p {
|
||||
text-align: center;
|
||||
flex: none;
|
||||
width: 100px;
|
||||
line-height: 30px;
|
||||
border: 2px solid blue;
|
||||
z-index: 999;
|
||||
background: #fff;
|
||||
}
|
||||
//具体执行情况(按顺序执行)
|
||||
.device_children {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background: #fff;
|
||||
z-index: 999;
|
||||
.item_children {
|
||||
width: 200px;
|
||||
min-height: 120px;
|
||||
background: #fff;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
border: 1px solid green;
|
||||
border-radius: 6px;
|
||||
z-index: 999;
|
||||
p {
|
||||
flex: none;
|
||||
width: 200px;
|
||||
text-align: left;
|
||||
z-index: 999;
|
||||
}
|
||||
}
|
||||
}
|
||||
.time_end {
|
||||
width: 100px;
|
||||
line-height: 30px;
|
||||
border: 2px solid blue;
|
||||
z-index: 999;
|
||||
background: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
272
frontend/src/views/plan/preTest/index.vue
Normal file
272
frontend/src/views/plan/preTest/index.vue
Normal file
@@ -0,0 +1,272 @@
|
||||
<!-- 计划管理-预检测页面 -->
|
||||
<template>
|
||||
<div class="test">
|
||||
<!-- 顶部筛选条件&返回按钮 -->
|
||||
<!-- {{ printText }} -->
|
||||
<div class="test_top">
|
||||
<el-checkbox
|
||||
v-for="(item, index) in detectionOptions"
|
||||
:model-value="item.id"
|
||||
:true-value="item.id"
|
||||
:key="index"
|
||||
style="pointer-events: none"
|
||||
>{{ item.name }}</el-checkbox
|
||||
>
|
||||
<el-button type="primary" @click="handlePreTest">启动预检测</el-button>
|
||||
<el-button type="primary" @click="handleBackDeviceList"
|
||||
>返回设备列表</el-button
|
||||
>
|
||||
<el-button type="primary" @click="handleAutoTest">进入检测流程</el-button>
|
||||
</div>
|
||||
<div class="test_bot">
|
||||
<div class="bot_left">
|
||||
<p v-for="(item, index) in leftDeviceData" :key="index">
|
||||
{{ item.name }}{{ item.status === 0 ? "异常" : "通过" }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="bot_right">
|
||||
<div class="right_top">
|
||||
<p>校验信息</p>
|
||||
</div>
|
||||
<div class="right_container">
|
||||
<div
|
||||
v-for="(item, index) in leftDeviceData"
|
||||
:key="index"
|
||||
class="text_item"
|
||||
>
|
||||
<el-divider content-position="left">{{ item.name }}</el-divider>
|
||||
<Text :text="item.name"></Text>
|
||||
</div>
|
||||
</div>
|
||||
<div class="right_bot"><p>预检测结束</p></div>
|
||||
<!-- <el-card style="height: 100%">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>校验信息</span>
|
||||
</div>
|
||||
</template>
|
||||
<div>
|
||||
</div>
|
||||
<template #footer></template>
|
||||
</el-card> -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted, nextTick } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
import Text from "./text.vue";
|
||||
const router = useRouter();
|
||||
const printText = ref(""); // 要显示的文字
|
||||
const speed = 100; // 打字速度,单位:毫秒
|
||||
let index = 0;
|
||||
const str = "这是一段文字这是一段文字这是一段\n文字这是一段文字这是一段文字...";
|
||||
const typeWriter = () => {
|
||||
if (index < str.length) {
|
||||
printText.value += str.charAt(index);
|
||||
index++;
|
||||
setTimeout(typeWriter, speed);
|
||||
}
|
||||
if (index == str.length) {
|
||||
printText.value = "";
|
||||
index = 0;
|
||||
}
|
||||
};
|
||||
const handlePrintText = (text: any, val: any) => {
|
||||
if (index < text.length) {
|
||||
leftDeviceData.value[val].name = text.charAt(index);
|
||||
index++;
|
||||
setTimeout(typeWriter, speed);
|
||||
}
|
||||
// if (index == text.length) {
|
||||
// printText.value = "";
|
||||
// index = 0;
|
||||
// }
|
||||
};
|
||||
const leftDeviceData = ref<any>([
|
||||
// {
|
||||
// id: 0,
|
||||
// name: "设备1-预检测",
|
||||
// status: 0,
|
||||
// },
|
||||
// {
|
||||
// id: 1,
|
||||
// name: "设备2-预检测",
|
||||
// status: 1,
|
||||
// },
|
||||
// {
|
||||
// id: 2,
|
||||
// name: "设备3-预检测",
|
||||
// status: 1,
|
||||
// },
|
||||
// {
|
||||
// id: 3,
|
||||
// name: "设备4-预检测",
|
||||
// status: 0,
|
||||
// },
|
||||
// {
|
||||
// id: 4,
|
||||
// name: "设备5-预检测",
|
||||
// status: 1,
|
||||
// },
|
||||
// {
|
||||
// id: 5,
|
||||
// name: "设备6-预检测",
|
||||
// status: 0,
|
||||
// },
|
||||
]);
|
||||
const initLeftDeviceData = () => {
|
||||
leftDeviceData.value.map((item, index) => {
|
||||
// handlePrintText(item.name, index);
|
||||
});
|
||||
};
|
||||
|
||||
//定义与预检测配置数组
|
||||
const detectionOptions = [
|
||||
{
|
||||
id: 0,
|
||||
name: "标准源通讯检测",
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
name: "设备通讯检测",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: "协议校验",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: "数据校对",
|
||||
},
|
||||
];
|
||||
|
||||
//启动预检测
|
||||
let timer: any = null;
|
||||
const handlePreTest = () => {
|
||||
ElMessage.success("启动预检测");
|
||||
let count = 0;
|
||||
if (timer) {
|
||||
clearInterval(timer);
|
||||
count = 0;
|
||||
leftDeviceData.value = [];
|
||||
}
|
||||
if (count == 5) {
|
||||
count = 0;
|
||||
} else {
|
||||
timer = setInterval(async () => {
|
||||
count++;
|
||||
if (count > 5) return;
|
||||
await nextTick(() => {
|
||||
leftDeviceData.value.push({
|
||||
id: count,
|
||||
name: "设备" + count + "预检测",
|
||||
status: count % 2 == 0 ? 0 : 1,
|
||||
});
|
||||
});
|
||||
}, 2000);
|
||||
}
|
||||
};
|
||||
//进入检测流程
|
||||
const handleAutoTest = () => {
|
||||
router.push({
|
||||
path: "/plan/autoTest",
|
||||
});
|
||||
};
|
||||
//返回设备列表
|
||||
const handleBackDeviceList = () => {
|
||||
router.push({
|
||||
path: "/plan/singlePlanList",
|
||||
});
|
||||
};
|
||||
//左侧数据
|
||||
leftDeviceData.value.map((item: any) => {});
|
||||
onMounted(() => {
|
||||
handlePreTest();
|
||||
typeWriter();
|
||||
initLeftDeviceData();
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.test {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.test_top {
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
display: flex;
|
||||
background-color: #fff;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
border-radius: 4px;
|
||||
padding: 0 10px;
|
||||
box-sizing: border-box;
|
||||
.el-button {
|
||||
margin-left: 20px;
|
||||
}
|
||||
}
|
||||
.test_bot {
|
||||
flex: 1;
|
||||
margin-top: 10px;
|
||||
display: flex;
|
||||
.bot_left {
|
||||
width: 200px;
|
||||
height: 100%;
|
||||
background-color: #fff;
|
||||
border-radius: 6px;
|
||||
padding: 10px;
|
||||
box-sizing: border-box;
|
||||
p {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
.bot_right {
|
||||
flex: 1;
|
||||
background-color: #fff;
|
||||
margin-left: 10px;
|
||||
max-height: calc(100vh - 240px);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
.right_top {
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
padding: 0 20px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.right_container {
|
||||
flex: 1;
|
||||
border: 2px solid #eee;
|
||||
border-left: 0;
|
||||
border-right: 0;
|
||||
padding: 0 20px;
|
||||
box-sizing: border-box;
|
||||
overflow: auto;
|
||||
}
|
||||
.right_bot {
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
padding: 0 20px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
// .el-card {
|
||||
// width: 100%;
|
||||
// height: 100%;
|
||||
// display: flex;
|
||||
// flex-direction: column;
|
||||
// cursor: pointer;
|
||||
// div {
|
||||
// width: 100%;
|
||||
// height: 100px;
|
||||
// border: 2px solid red;
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
35
frontend/src/views/plan/preTest/text.vue
Normal file
35
frontend/src/views/plan/preTest/text.vue
Normal file
@@ -0,0 +1,35 @@
|
||||
<template>
|
||||
<div>
|
||||
<p>{{ typedText }}</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, onUnmounted } from "vue";
|
||||
|
||||
const props = defineProps({
|
||||
text: String,
|
||||
delay: {
|
||||
type: Number,
|
||||
default: 1000,
|
||||
},
|
||||
});
|
||||
|
||||
const typedText = ref("");
|
||||
let timerId;
|
||||
|
||||
function typeText() {
|
||||
if (props.text.length > typedText.value.length) {
|
||||
typedText.value = props.text.substring(0, typedText.value.length + 1);
|
||||
timerId = setTimeout(typeText, props.delay);
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
typeText();
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
clearTimeout(timerId);
|
||||
});
|
||||
</script>
|
||||
Reference in New Issue
Block a user