自动检测/手动检测页面

This commit is contained in:
zhujiyan
2024-10-10 17:47:55 +08:00
parent 507ea137e4
commit e060939bc1
30 changed files with 3176 additions and 116 deletions

View File

@@ -79,13 +79,13 @@
>
<el-button
type="primary"
@click="handleDetection"
@click="handleTest"
v-if="form.activeTabs === 0"
>启动自动检测</el-button
>
<el-button
type="primary"
@click="handleDetection"
@click="handleTest"
v-if="form.activeTabs === 1"
>启动手动检测</el-button
>
@@ -509,12 +509,18 @@ const changeActiveTabs = (val: number) => {
form.value.activeTabs = val;
};
//启动自动检测/手动检测
const handleDetection = () => {
const handleTest = () => {
//自动检测
if (form.value.activeTabs === 0) {
ElMessage.success("自动检测");
}else{
router.push({
path: "/plan/autoTest",
});
} else {
ElMessage.warning("手动检测");
router.push({
path: "/plan/preTest",
});
}
};
onMounted(() => {

View File

@@ -1,5 +1,6 @@
<template>
<div class="homeView" style=" height: calc(100vh - 120px) !important;">
<!-- style=" height: calc(100vh - 120px) !important;" -->
<div class="homeView">
<model v-if="!authStore.showMenuFlag"></model>
<dashboard v-if="authStore.showMenuFlag"></dashboard>
</div>
@@ -81,6 +82,7 @@ onMounted(() => {
<style lang="scss" scoped>
.homeView{
width: 100%;
height: calc(100vh - 120px);
// height: calc(100vh - 120px);
height: 100%;
}
</style>

View File

@@ -232,7 +232,8 @@ onMounted(() => {
<style lang="scss" scoped>
.static {
width: 100%;
height: calc(100vh - 165px);
// height: calc(100vh - 165px);
height: 100%;
display: flex;
justify-content: space-between;
// background-color: #eee;

View File

@@ -1,34 +1,31 @@
<!-- 模式切换页面 -->
<template>
<div class="main">
<div
class="main_container"
:style="{
height: authStore.showMenuFlag
? 'calc(100vh - 165px)'
: 'calc(100vh - 125px)',
}"
>
<div class="mode" v-for="(item, index) in modeList" :key="index">
<div class="mode_top">
<div class="mode_name">
<p>
{{ item.name }}
</p>
</div>
<div class="test_button">
<el-button
size="small"
type="primary"
@click="handelOpen(item.isActive)"
:disabled="item.isActive==false">进入检测</el-button
>
</div>
<div class="main_container">
<!-- :style="{
height: authStore?.showMenuFlag
? 'calc(100vh - 165px)'
: 'calc(100vh - 125px)',
}" -->
<div class="mode" v-for="(item, index) in modeList" :key="index">
<div class="mode_top">
<div class="mode_name">
<p>
{{ item.name }}
</p>
</div>
<div class="mode_img">
<img :src="item.img" />
<div class="test_button">
<el-button
size="small"
type="primary"
@click="handelOpen(item.isActive)"
:disabled="item.isActive == false"
>进入检测</el-button
>
</div>
</div>
<div class="mode_img">
<img :src="item.img" />
</div>
</div>
</div>
</template>
@@ -81,7 +78,8 @@ onMounted(() => {
<style lang="scss" scoped>
.main_container {
width: 100%;
height: calc(100vh - 165px);
// height: calc(100vh - 165px);
height: 100%;
// overflow-y: auto;
display: flex;
justify-content: space-between;
@@ -89,19 +87,16 @@ onMounted(() => {
align-items: center;
// padding: 20px 2%;
.mode {
// width: 31.5%;
// height: 100%;
flex: none;
margin-top: 3px;
width: 32.5%;
height: 100% !important;
height: 99.5% !important;
border: 1px solid #eee;
display: flex;
flex-direction: column;
align-items: center;
// justify-content: space-around;
background: #fff;
border-radius: 6px;
// margin-bottom: 20px;
background: linear-gradient(
180deg,
rgba(0, 153, 255, 1) 0%,

View File

@@ -0,0 +1,568 @@
<template>
<div class="table_info">
<ProTable
ref="proTable"
:columns="columns"
:request-api="getTableList"
:init-param="initParam"
:data-callback="dataCallback"
@drag-sort="sortTable"
:height="tableHeight"
:stripe="true"
>
<!-- 表格 header 按钮 -->
<template #tableHeader="scope">
<el-form :model="form" label-width="80px" :inline="true">
<el-form-item label="检测状态" v-if="form.activeTabs != 5">
<el-select v-model="form.checkStatus">
<el-option
v-for="(item, index) in checkStatusList"
:label="item.label"
:value="item.value"
:key="index"
></el-option>
</el-select>
</el-form-item>
<el-form-item label="报告状态" v-if="form.activeTabs != 5">
<el-select v-model="form.checkReportStatus">
<el-option
v-for="(item, index) in checkReportStatusList"
:label="item.label"
:value="item.value"
:key="index"
></el-option>
</el-select>
</el-form-item>
<el-form-item label="检测结果" v-if="form.activeTabs != 5">
<el-select v-model="form.checkResult">
<el-option
v-for="(item, index) in checkResultList"
:label="item.label"
:value="item.value"
:key="index"
></el-option>
</el-select>
</el-form-item>
<el-form-item label="绑定状态" v-if="form.activeTabs == 5">
<el-select v-model="form.deviceBindStatus">
<el-option
v-for="(item, index) in deviceBindStatusList"
:label="item.label"
:value="item.value"
:key="index"
></el-option>
</el-select>
</el-form-item>
<el-form-item label="设备类型" v-if="form.activeTabs == 5">
<el-select v-model="form.deviceType">
<el-option
v-for="(item, index) in deviceTypeList"
:label="item.label"
:value="item.value"
:key="index"
></el-option>
</el-select>
</el-form-item>
<el-form-item label="制造厂商" v-if="form.activeTabs == 5">
<el-select v-model="form.manufacturer">
<el-option
v-for="(item, index) in manufacturerList"
:label="item.label"
:value="item.value"
:key="index"
></el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" :icon="Search" @click="handleSearch"
>查询</el-button
>
<el-button
type="primary"
@click="handleTest"
v-if="form.activeTabs === 0"
>启动自动检测</el-button
>
<el-button
type="primary"
@click="handleTest"
v-if="form.activeTabs === 1"
>启动手动检测</el-button
>
<el-button type="primary" v-if="form.activeTabs === 2"
>报告生成</el-button
>
<el-button type="primary" v-if="form.activeTabs === 5"
>设备导入</el-button
>
</el-form-item>
</el-form>
</template>
<!-- 表格操作 -->
<!-- <template #operation="scope">
<el-button
type="primary"
link
:icon="View"
@click="openDrawer('查看', scope.row)"
>查看</el-button
>
<el-button
type="primary"
link
:icon="EditPen"
@click="openDrawer('编辑', scope.row)"
>导出</el-button
>
<el-button
type="primary"
link
:icon="Delete"
@click="deleteAccount(scope.row)"
>删除</el-button
>
</template> -->
</ProTable>
</div>
</template>
<script setup lang="tsx" name="useProTable">
import { ref, reactive, onMounted } from "vue";
import { useRouter } from "vue-router";
import { User } from "@/api/interface";
import { useHandleData } from "@/hooks/useHandleData";
import { useDownload } from "@/hooks/useDownload";
import { ElMessage, ElMessageBox } from "element-plus";
import ProTable from "@/components/ProTable/index.vue";
import ImportExcel from "@/components/ImportExcel/index.vue";
import {
CirclePlus,
Delete,
EditPen,
Download,
Upload,
View,
Refresh,
Search,
} from "@element-plus/icons-vue";
import { getPlanList } from "@/api/plan/planList";
const router = useRouter();
const value1 = ref("");
const value2 = ref("");
const tableHeight = ref(0);
console.log(window.innerHeight, "+++++++++");
tableHeight.value = window.innerHeight - 630;
//下拉框数据
//检测状态数据
const checkStatusList = [
{
label: "未检测",
value: 0,
},
{
label: "检测中",
value: 1,
},
{
label: "检测完成",
value: 2,
},
{
label: "归档",
value: 3,
},
];
//检测报告状态数据
const checkReportStatusList = [
{
label: "未生成报告",
value: 0,
},
{
label: "已生成报告",
value: 1,
},
];
//检测结果数组
const checkResultList = [
{
label: "/",
value: null,
},
{
label: "不合格",
value: 0,
},
{
label: "合格",
value: 1,
},
];
//绑定状态数组
const deviceBindStatusList = [
{
label: "未绑定",
value: 0,
},
{
label: "已绑定",
value: 1,
},
];
//设备类型数组
const deviceTypeList = [
{
label: "PQS882A",
value: 0,
},
{
label: "PQS882B4",
value: 1,
},
{
label: "PQS882B5",
value: 2,
},
{
label: "PQS882B6",
value: 3,
},
{
label: "PQS882B7",
value: 4,
},
{
label: "PQS882B8",
value: 5,
},
];
//制造厂商数组
const manufacturerList = [
{
label: "南京灿能电力",
value: 0,
},
{
label: "南瑞继保",
value: 1,
},
{
label: "中电",
value: 2,
},
];
//查询条件
const form: any = ref({
activeTabs: 0, //功能选择
checkStatus: 0, //检测状态
checkReportStatus: 0, //检测报告状态
checkResult: 0, //检测结果
deviceBindStatus: 0, //绑定状态
deviceType: 0, //设备类型
manufacturer: 0, //制造厂商
});
const searchForm = ref({
intervalType: 0,
time: ["2024-08-20", "2024-08-27"],
searchBeginTime: "",
searchEndTime: "",
checkStatus: 0,
checkReportStatus: 0,
checkResult: 0,
});
// ProTable 实例
const proTable = ref<ProTableInstance>();
// 如果表格需要初始化请求参数,直接定义传给 ProTable (之后每次请求都会自动带上该参数,此参数更改之后也会一直带上,改变此参数会自动刷新表格数据)
const initParam = reactive({ type: 1, pageNum: 1, pageSize: 10 });
// dataCallback 是对于返回的表格数据做处理,如果你后台返回的数据不是 list && total 这些字段,可以在这里进行处理成这些字段
// 或者直接去 hooks/useTable.ts 文件中把字段改为你后端对应的就行
const tableList = ref([]);
const dataCallback = (data: any) => {
return {
list: data || data.data || data.list,
total: data.length || data.total, //total
};
};
// 如果你想在请求之前对当前请求参数做一些操作可以自定义如下函数params 为当前所有的请求参数(包括分页),最后返回请求列表接口
// 默认不做操作就直接在 ProTable 组件上绑定 :requestApi="getUserList"
const getTableList = (params: any) => {
let newParams = JSON.parse(JSON.stringify(params));
newParams.createTime && (newParams.startTime = newParams.createTime[0]);
newParams.createTime && (newParams.endTime = newParams.createTime[1]);
delete newParams.createTime;
return getPlanList(newParams);
};
// 表格配置项
const columns = reactive<ColumnProps<User.ResUserList>[]>([
{ type: "selection", fixed: "left", width: 70 },
{
prop: "checkMode",
label: "设备序列号",
width: 140,
render: (scope) => {
return scope.row.checkMode == 0
? "设备1"
: scope.row.checkMode == 1
? "设备2"
: scope.row.checkMode == 2
? "设备3"
: scope.row.checkMode;
},
},
{
prop: "checkMode",
label: "设备类型",
width: 140,
render: (scope) => {
return scope.row.checkMode == 0
? "PQS991"
: scope.row.checkMode == 1
? "PQS882"
: scope.row.checkMode == 2
? "PQS6666"
: scope.row.checkMode;
},
},
{
prop: "checkFrom",
label: "制造厂商",
width: 140,
render: (scope) => {
return scope.row.checkFrom == 0
? "南京灿能"
: scope.row.checkFrom == 1
? "南瑞继保"
: scope.row.checkFrom == 2
? "/"
: scope.row.checkFrom;
},
},
{
prop: "numberFromName",
label: "MAC/IP",
render: (scope) => {
return scope.row.numberFromName == 0
? "192.168.0.1"
: scope.row.numberFromName == 1
? "192.168.0.2"
: scope.row.numberFromName == 2
? "192.168.0.3"
: scope.row.numberFromName;
},
},
// {
// prop: "checkExe",
// label: "检测脚本",
// render: (scope) => {
// return scope.row.checkExe == 0
// ? "国网入网检测脚本(单影响量-模拟式)"
// : scope.row.checkExe == 1
// ? "国网入网检测脚本"
// : scope.row.checkExe == 2
// ? "/"
// : scope.row.checkExe;
// },
// },
// {
// prop: "wctx",
// label: "误差体系",
// render: (scope) => {
// return scope.row.wctx == 0
// ? "Q/GDW 1650.2- 2016"
// : scope.row.wctx == 1
// ? "Q/GDW 10650.2 - 2021"
// : scope.row.wctx == 2
// ? "/"
// : scope.row.wctx;
// },
// },
// {
// prop: "checkStatus",
// label: "检测状态",
// width: 120,
// render: (scope) => {
// return scope.row.checkStatus == 1
// ? "未检测"
// : scope.row.checkStatus == 2
// ? "检测中"
// : scope.row.checkStatus == 3
// ? "检测完成"
// : scope.row.checkStatus;
// },
// },
// {
// prop: "checkReport",
// label: "检测报告",
// width: 120,
// render: (scope) => {
// return scope.row.checkReport == 1
// ? "未生成"
// : scope.row.checkReport == 2
// ? "部分生成"
// : scope.row.checkReport == 3
// ? "全部生成"
// : scope.row.checkReport;
// },
// },
// {
// prop: "checkResult",
// label: "检测结果",
// width: 120,
// render: (scope) => {
// return scope.row.checkReport == 1
// ? "/"
// : scope.row.checkReport == 2
// ? "符合"
// : scope.row.checkReport == 3
// ? "不符合"
// : scope.row.checkReport;
// },
// },
// {
// prop: "parentNode",
// label: "父节点",
// width: 90,
// render: (scope) => {
// return scope.row.checkReport == 0
// ? "/"
// : scope.row.checkReport == 1
// ? "检测计划1"
// : scope.row.checkReport == 2
// ? "检测计划2"
// : scope.row.checkReport == 3
// ? "检测计划3"
// : scope.row.checkReport;
// },
// },
// { prop: "operation", label: "操作", fixed: "right", width: 250 },
]);
// 跳转详情页
const toDetail = () => {
router.push(
`/proTable/useProTable/detail/${Math.random().toFixed(3)}?params=detail-page`
);
};
//重置查询条件
const resetSearchForm = () => {
searchForm.value = {
intervalType: 0,
time: ["2024-08-20", "2024-08-27"],
searchBeginTime: "",
searchEndTime: "",
checkStatus: 0,
checkReportStatus: 0,
checkResult: 0,
};
};
//查询
const handleSearch = () => {
proTable.value?.getTableList();
};
//重置
const handleRefresh = () => {
proTable.value?.getTableList();
};
// 表格拖拽排序
const sortTable = ({
newIndex,
oldIndex,
}: {
newIndex?: number;
oldIndex?: number;
}) => {
console.log(newIndex, oldIndex);
console.log(proTable.value?.tableData);
ElMessage.success("修改列表排序成功");
};
// 删除用户信息
const deleteAccount = async (params: User.ResUserList) => {
await useHandleData(
deleteUser,
{ id: [params.id] },
`删除【${params.username}`
);
proTable.value?.getTableList();
};
// 批量删除用户信息
const batchDelete = async (id: string[]) => {
await useHandleData(deleteUser, { id }, "删除所选用户信息");
proTable.value?.clearSelection();
proTable.value?.getTableList();
};
// 重置用户密码
const resetPass = async (params: User.ResUserList) => {
await useHandleData(
resetUserPassWord,
{ id: params.id },
`重置【${params.username}】用户密码`
);
proTable.value?.getTableList();
};
// 切换用户状态
const changeStatus = async (row: User.ResUserList) => {
await useHandleData(
changeUserStatus,
{ id: row.id, status: row.status == 1 ? 0 : 1 },
`切换【${row.username}】用户状态`
);
proTable.value?.getTableList();
};
//顶部功能切换时修改activeTabs
const changeActiveTabs = (val: number) => {
form.value.activeTabs = val;
};
//启动自动检测/手动检测
const handleTest = () => {
//自动检测
if (form.value.activeTabs === 0) {
ElMessage.success("自动检测");
router.push({
path:"/plan/autoTest"
})
}else{
ElMessage.warning("手动检测");
}
};
onMounted(() => {
console.log(proTable.value?.tableData);
});
defineExpose({ changeActiveTabs });
</script>
<style lang="scss" scoped>
/* 当屏幕宽度小于或等于1300像素时 */
@media screen and (max-width: 1300px) {
.el-select {
width: 130px !important;
}
}
@media screen and (min-width: 1300px) {
.el-select {
width: 150px !important;
}
}
// ::v-deep .el-select {
// width: 150px !important;
// }
.el-form {
width: 100%;
display: flex;
flex-wrap: wrap;
.el-form-item {
display: flex;
align-items: center;
justify-content: space-between;
.el-button {
margin: 0 !important;
margin-right: 10px !important;
}
}
}
</style>

View File

@@ -0,0 +1,125 @@
<template>
<div class="plan_tree">
<div class="search_view">
<el-input
placeholder="请输入计划名称"
v-model="searchForm.planName"
></el-input>
</div>
<div class="tree_container">
<el-tree
:data="data"
ref="treeRef"
:filter-node-method="filterNode"
:props="defaultProps"
node-key="id"
default-expand-all
show-checkbox
:default-checked-keys="defaultChecked"
@node-click="handleNodeClick"
@check-change="changeSelect"
>
<template #default="{ node, data }">
<span
class="custom-tree-node"
style="display: flex; align-items: center"
>
<!-- <Platform v-if="!data.pid" style="width:18px;height: 18px;margin-right:8px;" :style="{color:node.label=='未检测'?'#F56C6C':node.label=='检测中'?'#E6A23C':'#67C23A'}"/> -->
<span>{{ node.label }}</span>
</span>
</template>
</el-tree>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, onMounted, defineExpose, watch } from "vue";
import { Menu, Platform } from "@element-plus/icons-vue";
const emit = defineEmits(["jump"]);
const data: any = ref([]);
const defaultProps = {
children: "children",
label: "name",
pid: "pid",
};
const searchForm = ref({
planName: "",
});
const defaultChecked: any = ref([]);
const treeList: any = ref([]);
const getTreeData = (val: any) => {
defaultChecked.value = [];
data.value = val;
defaultChecked.value.push(data.value[0].children[0].children[0].id);
console.log(defaultChecked.value, "+++++++++++++");
console.log(treeList.value);
treeRef.value.setCurrentKey(defaultChecked.value);
};
const filterText = ref("");
const treeRef = ref();
watch(
() => searchForm.value.planName,
(val) => {
treeRef.value!.filter(val);
},
{
deep: true,
}
);
const changeSelect=()=>{
console.log(treeRef.value.getCheckedKeys());
}
const handleNodeClick = (data) => {
console.log(data);
};
const filterNode = (value: string, data) => {
if (!value) return true;
return data.name.includes(value);
};
// 点击详情
const detail = (e: any) => {
emit("jump", e);
};
onMounted(() => {
console.log();
});
defineExpose({ getTreeData });
</script>
<style lang="scss" scoped>
.plan_tree {
// width: 200px;
height: 100%;
display: flex;
flex-direction: column;
padding: 5px;
// height: calc(100% - 70px);
background-color: #fff;
box-sizing: border-box;
.search_view {
width: 100%;
height: auto;
display: flex;
justify-content: space-between;
padding: 0 5px;
box-sizing: border-box;
align-items: center;
.el-input {
margin-top: 6px;
}
}
.el-input {
width: 100%;
// margin: 0 10px 10px 0;
}
.tree_container {
flex: 1;
height: 100%;
overflow-y: auto;
.el-tree {
height: 100%;
}
}
}
</style>

View File

@@ -0,0 +1,554 @@
<template>
<!-- 自动检测页面 -->
<div class="test">
<div class="test_left">
<Tree ref="treeRef"></Tree>
</div>
<div class="test_right">
<el-descriptions
style="
width: 100%;
border-radius: 6px;
margin-bottom: 10px;
background-color: #fff;
padding: 10px;
"
:column="3"
border
>
<template #extra>
<el-progress
style="width: 80%"
:percentage="percentage"
:color="customColors"
/>
<div class="test_button">
<el-button
type="danger"
v-if="!isPause"
:icon="Close"
@click="handlePauseTest"
>暂停检测</el-button
>
<el-button
type="warning"
v-if="isPause"
:icon="Refresh"
@click="handlePauseTest"
>继续检测</el-button
>
<el-button type="primary" :icon="Check" @click="handleFinishTest"
>完成检测</el-button
>
</div>
</template>
<el-descriptions-item width="0px" label="上送数据总数">
{{ num }}
</el-descriptions-item>
<el-descriptions-item width="0px" label="已上送数据数">
{{ num1 }}
</el-descriptions-item>
<el-descriptions-item width="0px" label="待上送数据数">
{{ num2 }}
</el-descriptions-item>
</el-descriptions>
<!-- 右侧列表 -->
<div class="right_table">
<!-- 模拟列表样式 -->
<!-- 表头设备 -->
<div class="table_left">
<p>测试项目</p>
<div v-for="(item, index) in deviceTestList" :key="index">
{{ item.name }}
</div>
</div>
<div class="table_right">
<div class="right_device_title">
<p v-for="(item, index) in deviceData" :key="index">
{{ item.name }}
</p>
</div>
<div class="right_device_status">
<div
class="status_info"
v-for="(item, index) in deviceTestList"
:key="index"
>
<p v-for="(vv, vvs) in item.children" :key="vvs">
{{ vv.status }}
</p>
</div>
</div>
</div>
<!-- <div class="table_body"></div> -->
<!-- </div> -->
<!-- ------------------------ -->
<ProTable
v-if="false"
ref="proTable"
:columns="columns"
:request-api="getTableList"
:init-param="initParam"
:data-callback="dataCallback"
@drag-sort="sortTable"
>
<!-- 表格 header 按钮 -->
<template #tableHeader> </template>
</ProTable>
</div>
<!-- 右侧状态加载 -->
<div class="right_status" ref="statusRef">
<p v-for="(item, index) in statusList" :key="index">
输入{{ item.remark }} -{{
item.status == 0 ? "输出完毕" : "输入中请稍后"
}}<br />
<span v-if="index == statusList.length - 1">...</span>
</p>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, onMounted, reactive, nextTick } from "vue";
import Tree from "./components/tree.vue";
import { data } from "@/api/plan/autoTest.json";
import ProTable from "@/components/ProTable/index.vue";
import { useTransition } from "@vueuse/core";
import { getPlanList } from "@/api/plan/planList";
import { ElMessage, ElMessageBox } from "element-plus";
import {
CirclePlus,
Delete,
EditPen,
Download,
Upload,
View,
Check,
Plus,
Refresh,
Search,
Close,
} from "@element-plus/icons-vue";
const treeRef = ref<any>();
// 表格配置项
const columns = reactive<ColumnProps<User.ResUserList>[]>([
{ type: "selection", fixed: "left", width: 70 },
// { type: "sort", label: "Sort", width: 80 },
// { type: "expand", label: "Expand", width: 85 },
{
prop: "planName",
label: "计划名称",
width: 120,
},
{
prop: "checkMode",
label: "检测模式",
width: 120,
render: (scope: any) => {
return scope.row.checkMode == 0
? "模拟式"
: scope.row.checkMode == 1
? "对比式"
: scope.row.checkMode == 2
? "数字式"
: scope.row.checkMode;
},
},
{
prop: "checkFrom",
label: "检测源",
width: 120,
render: (scope: any) => {
return scope.row.checkFrom == 0
? "标准源-福禄克-6100A"
: scope.row.checkFrom == 1
? "标准源-昂立-PF2"
: scope.row.checkFrom == 2
? "高精度设备-PQS882-1"
: scope.row.checkFrom;
},
},
{
prop: "numberFromName",
label: "源名称",
render: (scope: any) => {
return scope.row.numberFromName == 0
? "分钟统计数据最大值"
: scope.row.numberFromName == 1
? "分钟统计数据最小值"
: scope.row.numberFromName == 2
? "分钟统计数据CP95值"
: scope.row.numberFromName;
},
},
{
prop: "checkExe",
label: "检测脚本",
width: 120,
render: (scope: any) => {
return scope.row.checkExe == 0
? "国网入网检测脚本(单影响量-模拟式)"
: scope.row.checkExe == 1
? "国网入网检测脚本"
: scope.row.checkExe == 2
? "/"
: scope.row.checkExe;
},
},
{
prop: "wctx",
label: "误差体系",
width: 120,
render: (scope: any) => {
return scope.row.wctx == 0
? "Q/GDW 1650.2- 2016"
: scope.row.wctx == 1
? "Q/GDW 10650.2 - 2021"
: scope.row.wctx == 2
? "/"
: scope.row.wctx;
},
},
{
prop: "checkStatus",
label: "检测状态",
width: 120,
render: (scope: any) => {
return scope.row.checkStatus == 1
? "未检测"
: scope.row.checkStatus == 2
? "检测中"
: scope.row.checkStatus == 3
? "检测完成"
: scope.row.checkStatus;
},
},
{
prop: "checkReport",
label: "检测报告",
width: 120,
render: (scope: any) => {
return scope.row.checkReport == 1
? "未生成"
: scope.row.checkReport == 2
? "部分生成"
: scope.row.checkReport == 3
? "全部生成"
: scope.row.checkReport;
},
},
{
prop: "checkResult",
label: "检测结果",
width: 120,
render: (scope: any) => {
return scope.row.checkReport == 1
? "/"
: scope.row.checkReport == 2
? "符合"
: scope.row.checkReport == 3
? "不符合"
: scope.row.checkReport;
},
},
// { prop: "operation", label: "操作", fixed: "right", width: 250 },
]);
const getTreeData = () => {
treeRef.value && treeRef.value.getTreeData(data);
};
// ProTable 实例
const proTable = ref<ProTableInstance>();
// 如果表格需要初始化请求参数,直接定义传给 ProTable (之后每次请求都会自动带上该参数,此参数更改之后也会一直带上,改变此参数会自动刷新表格数据)
const initParam = reactive({ type: 1, pageNum: 1, pageSize: 10 });
// dataCallback 是对于返回的表格数据做处理,如果你后台返回的数据不是 list && total 这些字段,可以在这里进行处理成这些字段
// 或者直接去 hooks/useTable.ts 文件中把字段改为你后端对应的就行
const tableList = ref([]);
const dataCallback = (data: any) => {
return {
list: data || data.data || data.list,
total: data.length || data.total, //total
};
};
console.log(proTable.value, "proTable.value?proTable.value?proTable.value?");
// 如果你想在请求之前对当前请求参数做一些操作可以自定义如下函数params 为当前所有的请求参数(包括分页),最后返回请求列表接口
// 默认不做操作就直接在 ProTable 组件上绑定 :requestApi="getUserList"
const getTableList = (params: any) => {
let newParams = JSON.parse(JSON.stringify(params));
newParams.createTime && (newParams.startTime = newParams.createTime[0]);
newParams.createTime && (newParams.endTime = newParams.createTime[1]);
delete newParams.createTime;
return getPlanList(newParams);
};
const percentage = ref(0);
const customColors = [
{ color: "red", percentage: 0 },
{ color: "red", percentage: 10 },
{ color: "red", percentage: 20 },
{ color: "red", percentage: 30 }, //红
{ color: "red", percentage: 40 },
{ color: "#e6a23c", percentage: 50 },
{ color: "#e6a23c", percentage: 60 },
{ color: "#e6a23c", percentage: 70 }, //黄
{ color: "#e6a23c", percentage: 80 }, //1989fa
{ color: "#e6a23c", percentage: 90 }, //1989fa
{ color: "#5cb87a", percentage: 100 }, //绿
];
//加载进度条
const refreshProgress = () => {
if (percentage.value < 100) {
percentage.value += 10;
num1.value += 1001;
num2.value -= 1001;
} else {
percentage.value = 0;
num1.value = 9999;
num2.value = 162001;
statusList.value = [];
deviceTestList.value = [];
statusId.value = 0;
}
};
let timer: any = ref("");
const statusTimer: any = ref("");
//检测列表数据
const deviceTestList = ref<any>([]);
//检测结果数据
const deviceList = ref<any>([]);
//前一个页面带过来的设备数据
const deviceData = ref([
{
id: 0,
name: "设备1通道1",
status: Math.floor(Math.random() * 4),
},
{
id: 1,
name: "设备2通道2",
status: Math.floor(Math.random() * 4),
},
{
id: 2,
name: "设备3通道3",
status: Math.floor(Math.random() * 4),
},
{
id: 3,
name: "设备4通道4",
status: Math.floor(Math.random() * 4),
},
]);
const interValTest = () => {
timer.value = setInterval(() => {
deviceTestList.value.push({
id: 0,
name: `频率 ${statusId.value}Hz`,
children: deviceData.value,
// status: Math.floor(Math.random() * 4),
});
refreshProgress();
}, 2000);
statusTimer.value = setInterval(() => {
getStatusList();
statusList.value.map((item: any, index: any) => {
if (index == statusList.value.length - 1) {
item.status = 1;
} else {
item.status = 0;
}
});
}, 2000);
};
interValTest();
//暂停检测
const isPause = ref<boolean>(false);
const handlePauseTest = () => {
if (!isPause.value) {
clearInterval(timer.value);
clearInterval(statusTimer.value);
} else {
interValTest();
}
isPause.value = !isPause.value;
};
//完成检测
const handleFinishTest = () => {
ElMessage.success("完成检测");
};
// 表格拖拽排序
const sortTable = ({
newIndex,
oldIndex,
}: {
newIndex?: number;
oldIndex?: number;
}) => {
console.log(newIndex, oldIndex);
console.log(proTable.value?.tableData);
ElMessage.success("修改列表排序成功");
};
const num = ref(0),
num1 = ref(0),
num2 = ref(0);
const allUploadNum = useTransition(num, {
duration: 1500,
});
const isUploadNum = useTransition(num1, {
duration: 1500,
});
const notUploadNum = useTransition(num2, {
duration: 1500,
});
num.value = 172000;
num1.value = 9999;
num2.value = num.value - num1.value;
const statusList: any = ref([]);
let statusId = ref(0);
const statusRef = ref();
const getStatusList = () => {
statusId.value++;
statusList.value.push({
id: statusId.value,
remark: `频率 ${statusId.value}Hz`,
status: 0,
});
// console.log(statusRef.value.offsetHeight);
nextTick(() => {
if (statusRef.value)
statusRef.value.scrollTop = statusRef.value.scrollHeight;
});
};
onMounted(() => {
getTreeData();
});
</script>
<style lang="scss" scoped>
.test {
width: 100%;
height: 100%;
box-sizing: border-box;
display: flex;
justify-content: space-between;
.test_left {
max-width: 300px;
min-width: 200px;
width: 15%;
height: 100%;
overflow: auto;
padding-bottom: 20px;
box-sizing: border-box;
background-color: #fff;
border-radius: 6px;
padding: 5px !important;
box-sizing: border-box;
}
.test_right {
flex: 1;
height: 100%;
margin-left: 10px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-between;
.right_table {
flex: 1;
// width: 100%;
// height: calc(100vh - 330px);
height: 100%;
flex: 1 !important;
height: calc(100vh - 560px);
border-radius: 4px;
background-color: #fff;
width: 100% !important;
display: flex;
padding: 10px;
box-sizing: border-box;
.table_left {
width: 150px;
height: 100%;
overflow: auto;
p {
line-height: 40px;
margin: 0;
width: 100px;
}
}
.table_right {
flex: 1;
display: flex;
overflow: auto;
flex-direction: column;
.right_device_title {
width: 100%;
display: flex;
justify-content: space-between;
// overflow-x: auto !important;
p {
flex: 1;
// max-width: 150px;
width: auto;
padding: 0 10px;
margin: 0;
line-height: 40px;
}
}
.right_device_status {
width: 100%;
flex: 1;
.status_info {
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
p {
flex: 1;
// max-width: 150px;
min-width: auto;
margin: 0;
padding: 0 10px;
text-align: left;
}
}
}
}
}
.right_status {
width: 100%;
height: 150px;
overflow: auto;
background-color: #fff;
border-radius: 4px;
margin-top: 10px;
padding: 10px 0 20px 10px;
box-sizing: border-box;
p {
height: 20px;
font-size: 14px;
margin: 0 !important;
}
:nth-last-child(1) {
margin-bottom: 40px;
}
}
}
}
::v-deep .header-button-lf {
clear: both !important;
}
::v-deep .el-descriptions__extra {
width: 100%;
display: flex;
justify-content: space-between;
.test_button {
width: auto;
display: flex;
align-items: center;
}
}
</style>

View File

@@ -9,7 +9,7 @@
@drag-sort="sortTable"
>
<!-- 表格 header 按钮 -->
<template #tableHeader="scope">
<template #tableHeader>
<el-form :model="searchForm">
<el-form-item label="检测时间">
<el-select
@@ -63,21 +63,28 @@
>查询</el-button
>
<el-button :icon="Refresh" @click="handleRefresh">重置</el-button>
</el-form-item>
<el-form-item>
<el-button type="primary" :icon="Upload" @click="handleRefresh"
>导入</el-button
>
</el-form-item>
<el-form-item>
<el-button type="primary" :icon="Check" @click="handleRefresh"
>合并</el-button
>
</el-form-item>
<el-form-item>
<el-button type="primary" :icon="Plus" @click="handleRefresh"
>新增</el-button
>
</el-form-item>
<el-form-item>
<el-button type="danger" :icon="Delete" @click="handleRefresh"
>删除</el-button
>
</el-form-item>
</el-form>
<!-- <el-button
v-auth="'add'"
type="primary"
@@ -227,13 +234,10 @@ const initParam = reactive({ type: 1, pageNum: 1, pageSize: 10 });
// dataCallback 是对于返回的表格数据做处理,如果你后台返回的数据不是 list && total 这些字段,可以在这里进行处理成这些字段
// 或者直接去 hooks/useTable.ts 文件中把字段改为你后端对应的就行
const tableList = ref([]);
// console.log(data, ">>>>>>>>>>>");
// tableList.value = data;
// console.log(tableList.value, "?????????????????????177");
const dataCallback = (data: any) => {
return {
list: data || data.data || data.list,
total: data.length || data.total, //total
total: data.length || data.total,
};
};
console.log(proTable.value, "proTable.value?proTable.value?proTable.value?");
@@ -247,20 +251,21 @@ const getTableList = (params: any) => {
return getPlanList(newParams);
};
//查看详情
const detailsRef:any = ref();
const detailsRef: any = ref();
const handleDetails = () => {
console.log(detailsRef.value,"+++++++++++++++");
console.log(detailsRef.value, "+++++++++++++++");
detailsRef.value.open("查看计划");
};
// 表格配置项
const columns = reactive<ColumnProps<User.ResUserList>[]>([
{ type: "selection", fixed: "left", width: 70 },
// { type: "sort", label: "Sort", width: 80 },
// { type: "expand", label: "Expand", width: 85 },
{ type: "sort", label: "Sort", width: 80 },
{ type: "expand", label: "Expand", width: 85 },
{
prop: "planName",
label: "计划名称",
width: 120,
},
{
prop: "checkMode",
@@ -306,6 +311,7 @@ const columns = reactive<ColumnProps<User.ResUserList>[]>([
{
prop: "checkExe",
label: "检测脚本",
width: 120,
render: (scope) => {
return scope.row.checkExe == 0
? "国网入网检测脚本(单影响量-模拟式)"
@@ -319,6 +325,7 @@ const columns = reactive<ColumnProps<User.ResUserList>[]>([
{
prop: "wctx",
label: "误差体系",
width: 120,
render: (scope) => {
return scope.row.wctx == 0
? "Q/GDW 1650.2- 2016"
@@ -330,6 +337,7 @@ const columns = reactive<ColumnProps<User.ResUserList>[]>([
},
},
{
type: "tag",
prop: "checkStatus",
label: "检测状态",
width: 120,
@@ -449,7 +457,8 @@ onMounted(() => {
<style lang="scss" scoped>
.planList {
width: 100%;
height: calc(100vh - 165px);
// height: calc(100vh - 165px);
height: 100%;
}
:deep(.el-select) {

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

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

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

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

View File

@@ -0,0 +1,240 @@
<template>
<el-dialog
v-model="dialogVisible"
:title="dialogTitle"
width="50%"
:before-close="handleClose"
:draggable="true"
:destroy-on-close="true"
:append-to-body="true"
>
<div class="container">
<el-form
ref="ruleFormRef"
style="max-width: 600px"
:model="ruleForm"
:rules="rules"
label-width="auto"
class="demo-ruleForm"
:size="formSize"
status-icon
>
<el-form-item label="Activity name" prop="name">
<el-input v-model="ruleForm.name" />
</el-form-item>
<el-form-item label="Activity zone" prop="region">
<el-select v-model="ruleForm.region" placeholder="Activity zone">
<el-option label="Zone one" value="shanghai" />
<el-option label="Zone two" value="beijing" />
</el-select>
</el-form-item>
<el-form-item label="Activity count" prop="count">
<el-select-v2
v-model="ruleForm.count"
placeholder="Activity count"
:options="options"
/>
</el-form-item>
<el-form-item label="Activity time" required>
<el-col :span="11">
<el-form-item prop="date1">
<el-date-picker
v-model="ruleForm.date1"
type="date"
aria-label="Pick a date"
placeholder="Pick a date"
style="width: 100%"
/>
</el-form-item>
</el-col>
<el-col class="text-center" :span="2">
<span class="text-gray-500">-</span>
</el-col>
<el-col :span="11">
<el-form-item prop="date2">
<el-time-picker
v-model="ruleForm.date2"
aria-label="Pick a time"
placeholder="Pick a time"
style="width: 100%"
/>
</el-form-item>
</el-col>
</el-form-item>
<el-form-item label="Instant delivery" prop="delivery">
<el-switch v-model="ruleForm.delivery" />
</el-form-item>
<el-form-item label="Activity location" prop="location">
<el-segmented v-model="ruleForm.location" :options="locationOptions" />
</el-form-item>
<el-form-item label="Activity type" prop="type">
<el-checkbox-group v-model="ruleForm.type">
<el-checkbox value="Online activities" name="type">
Online activities
</el-checkbox>
<el-checkbox value="Promotion activities" name="type">
Promotion activities
</el-checkbox>
<el-checkbox value="Offline activities" name="type">
Offline activities
</el-checkbox>
<el-checkbox value="Simple brand exposure" name="type">
Simple brand exposure
</el-checkbox>
</el-checkbox-group>
</el-form-item>
<el-form-item label="Resources" prop="resource">
<el-radio-group v-model="ruleForm.resource">
<el-radio value="Sponsorship">Sponsorship</el-radio>
<el-radio value="Venue">Venue</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="Activity form" prop="desc">
<el-input v-model="ruleForm.desc" type="textarea" />
</el-form-item>
</el-form>
</div>
<template #footer>
<div class="dialog-footer">
<el-button @click="resetForm(ruleFormRef)">取消</el-button>
<el-button type="primary" @click="submitForm(ruleFormRef)">
确定
</el-button>
</div>
</template>
</el-dialog>
</template>
<script lang="ts" setup>
import { ref,reactive,onMounted, defineExpose } from "vue";
import type { ComponentSize, FormInstance, FormRules } from 'element-plus'
interface RuleForm {
name: string
region: string
count: string
date1: string
date2: string
delivery: boolean
location: string
type: string[]
resource: string
desc: string
}
const formSize = ref<ComponentSize>('default')
const ruleFormRef = ref<FormInstance>()
const ruleForm = reactive<RuleForm>({
name: 'Hello',
region: '',
count: '',
date1: '',
date2: '',
delivery: false,
location: '',
type: [],
resource: '',
desc: '',
})
const locationOptions = ['Home', 'Company', 'School']
const rules = reactive<FormRules<RuleForm>>({
name: [
{ required: true, message: 'Please input Activity name', trigger: 'blur' },
{ min: 3, max: 5, message: 'Length should be 3 to 5', trigger: 'blur' },
],
region: [
{
required: true,
message: 'Please select Activity zone',
trigger: 'change',
},
],
count: [
{
required: true,
message: 'Please select Activity count',
trigger: 'change',
},
],
date1: [
{
type: 'date',
required: true,
message: 'Please pick a date',
trigger: 'change',
},
],
date2: [
{
type: 'date',
required: true,
message: 'Please pick a time',
trigger: 'change',
},
],
location: [
{
required: true,
message: 'Please select a location',
trigger: 'change',
},
],
type: [
{
type: 'array',
required: true,
message: 'Please select at least one activity type',
trigger: 'change',
},
],
resource: [
{
required: true,
message: 'Please select activity resource',
trigger: 'change',
},
],
desc: [
{ required: true, message: 'Please input activity form', trigger: 'blur' },
],
})
const submitForm = async (formEl: FormInstance | undefined) => {
if (!formEl) return
await formEl.validate((valid, fields) => {
if (valid) {
console.log('submit!')
close()
} else {
console.log('error submit!', fields)
}
})
}
const resetForm = (formEl: FormInstance | undefined) => {
close()
if (!formEl) return
formEl.resetFields()
}
const options = Array.from({ length: 10000 }).map((_, idx) => ({
value: `${idx + 1}`,
label: `${idx + 1}`,
}))
onMounted(() => {
console.log();
});
const dialogVisible = ref<boolean>(false);
const dialogTitle = ref<string>("");
const open = (title: string) => {
dialogTitle.value = title;
dialogVisible.value = true;
};
const close=()=>{
dialogVisible.value = false;
}
onMounted(() => {
console.log();
});
defineExpose({ open });
</script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,481 @@
<template>
<div class="table-box planList">
<ProTable
ref="proTable"
:columns="columns"
:request-api="getTableList"
:init-param="initParam"
:data-callback="dataCallback"
@drag-sort="sortTable"
>
<!-- 表格 header 按钮 -->
<template #tableHeader>
<el-form :model="searchForm">
<el-form-item label="检测时间">
<el-select
v-model="searchForm.intervalType"
style="width: 80px !important"
>
<el-option :value="0" label="按周">按周</el-option>
<el-option :value="1" label="按月">按月</el-option>
<el-option :value="2" label="按日">按日</el-option>
</el-select>
</el-form-item>
<el-form-item label="">
<el-date-picker
style="width: 220px"
v-model="searchForm.time"
type="daterange"
unlink-panels
range-separator="-"
start-placeholder="开始日期"
end-placeholder="结束日期"
:shortcuts="shortcuts"
/>
</el-form-item>
<el-form-item label="检测状态">
<el-select v-model="searchForm.checkStatus">
<el-option :value="0" label="全部"></el-option>
<el-option :value="1" label="未检测"></el-option>
<el-option :value="2" label="检测中"></el-option>
<el-option :value="3" label="检测完成"></el-option>
</el-select>
</el-form-item>
<el-form-item label="检测报告状态">
<el-select v-model="searchForm.checkReportStatus">
<el-option :value="0" label="全部"></el-option>
<el-option :value="1" label="未生成"></el-option>
<el-option :value="2" label="部分生成"></el-option>
<el-option :value="3" label="全部生成"></el-option>
</el-select>
</el-form-item>
<el-form-item label="检测结果">
<el-select v-model="searchForm.checkResult">
<el-option :value="0" label="全部"></el-option>
<el-option :value="1" label="/"></el-option>
<el-option :value="2" label="符合"></el-option>
<el-option :value="3" label="不符合"></el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" :icon="Search" @click="handleSearch"
>查询</el-button
>
<el-button :icon="Refresh" @click="handleRefresh">重置</el-button>
</el-form-item>
<el-form-item>
<el-button type="primary" :icon="Upload" @click="handleRefresh"
>导入</el-button
>
</el-form-item>
<el-form-item>
<el-button type="primary" :icon="Check" @click="handleRefresh"
>合并</el-button
>
</el-form-item>
<el-form-item>
<el-button type="primary" :icon="Plus" @click="handleRefresh"
>新增</el-button
>
</el-form-item>
<el-form-item>
<el-button type="danger" :icon="Delete" @click="handleRefresh"
>删除</el-button
>
</el-form-item>
</el-form>
<!-- <el-button
v-auth="'add'"
type="primary"
:icon="CirclePlus"
@click="openDrawer('新增')"
>新增用户</el-button
>
<el-button
v-auth="'batchAdd'"
type="primary"
:icon="Upload"
plain
@click="batchAdd"
>批量添加用户</el-button
>
<el-button
v-auth="'export'"
type="primary"
:icon="Download"
plain
@click="downloadFile"
>导出用户数据</el-button
>
<el-button type="primary" plain @click="toDetail"
>To 子集详情页面</el-button
>
<el-button
type="danger"
:icon="Delete"
plain
:disabled="!scope.isSelected"
@click="batchDelete(scope.selectedListIds)"
>
批量删除用户
</el-button> -->
</template>
<!-- Expand -->
<!-- <template #expand="scope">
{{ scope.row }}
</template> -->
<!-- 表格操作 -->
<template #operation="scope">
<el-button
type="primary"
link
:icon="View"
@click="handleDetails('查看', scope.row)"
>查看</el-button
>
<el-button
type="primary"
link
:icon="EditPen"
@click="openDrawer('编辑', scope.row)"
>导出</el-button
>
<el-button
type="primary"
link
:icon="Delete"
@click="deleteAccount(scope.row)"
>删除</el-button
>
</template>
</ProTable>
</div>
<plan-details ref="detailsRef"></plan-details>
</template>
<script setup lang="tsx" name="useProTable">
import { ref, reactive, onMounted } from "vue";
import { useRouter } from "vue-router";
import { User } from "@/api/interface";
import { useHandleData } from "@/hooks/useHandleData";
import { useDownload } from "@/hooks/useDownload";
import { ElMessage, ElMessageBox } from "element-plus";
import ProTable from "@/components/ProTable/index.vue";
import ImportExcel from "@/components/ImportExcel/index.vue";
import planDetails from "./components/details.vue";
import {
CirclePlus,
Delete,
EditPen,
Download,
Upload,
View,
Check,
Plus,
Refresh,
Search,
} from "@element-plus/icons-vue";
import { getPlanList } from "@/api/plan/planList";
const router = useRouter();
const value1 = ref("");
const value2 = ref("");
const shortcuts = [
{
text: "最近一周",
value: () => {
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 24 * 7);
return [start, end];
},
},
{
text: "最近一个月",
value: () => {
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 24 * 30);
return [start, end];
},
},
{
text: "最近三个月",
value: () => {
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 24 * 90);
return [start, end];
},
},
];
// 跳转详情页
const toDetail = () => {
router.push(
`/proTable/useProTable/detail/${Math.random().toFixed(3)}?params=detail-page`
);
};
const searchForm = ref({
intervalType: 0,
time: ["2024-08-20", "2024-08-27"],
searchBeginTime: "",
searchEndTime: "",
checkStatus: 0,
checkReportStatus: 0,
checkResult: 0,
});
// ProTable 实例
const proTable = ref<ProTableInstance>();
// 如果表格需要初始化请求参数,直接定义传给 ProTable (之后每次请求都会自动带上该参数,此参数更改之后也会一直带上,改变此参数会自动刷新表格数据)
const initParam = reactive({ type: 1, pageNum: 1, pageSize: 10 });
// dataCallback 是对于返回的表格数据做处理,如果你后台返回的数据不是 list && total 这些字段,可以在这里进行处理成这些字段
// 或者直接去 hooks/useTable.ts 文件中把字段改为你后端对应的就行
const tableList = ref([]);
const dataCallback = (data: any) => {
return {
list: data || data.data || data.list,
total: data.length || data.total,
};
};
console.log(proTable.value, "proTable.value?proTable.value?proTable.value?");
// 如果你想在请求之前对当前请求参数做一些操作可以自定义如下函数params 为当前所有的请求参数(包括分页),最后返回请求列表接口
// 默认不做操作就直接在 ProTable 组件上绑定 :requestApi="getUserList"
const getTableList = (params: any) => {
let newParams = JSON.parse(JSON.stringify(params));
newParams.createTime && (newParams.startTime = newParams.createTime[0]);
newParams.createTime && (newParams.endTime = newParams.createTime[1]);
delete newParams.createTime;
return getPlanList(newParams);
};
//查看详情
const detailsRef: any = ref();
const handleDetails = () => {
console.log(detailsRef.value, "+++++++++++++++");
detailsRef.value.open("查看计划");
};
// 表格配置项
const columns = reactive<ColumnProps<User.ResUserList>[]>([
{ type: "selection", fixed: "left", width: 70 },
{ type: "sort", label: "Sort", width: 80 },
{ type: "expand", label: "Expand", width: 85 },
{
prop: "planName",
label: "计划名称",
width: 120,
},
{
prop: "checkMode",
label: "检测模式",
width: 120,
render: (scope) => {
return scope.row.checkMode == 0
? "模拟式"
: scope.row.checkMode == 1
? "对比式"
: scope.row.checkMode == 2
? "数字式"
: scope.row.checkMode;
},
},
{
prop: "checkFrom",
label: "检测源",
width: 120,
render: (scope) => {
return scope.row.checkFrom == 0
? "标准源-福禄克-6100A"
: scope.row.checkFrom == 1
? "标准源-昂立-PF2"
: scope.row.checkFrom == 2
? "高精度设备-PQS882-1"
: scope.row.checkFrom;
},
},
{
prop: "numberFromName",
label: "源名称",
render: (scope) => {
return scope.row.numberFromName == 0
? "分钟统计数据最大值"
: scope.row.numberFromName == 1
? "分钟统计数据最小值"
: scope.row.numberFromName == 2
? "分钟统计数据CP95值"
: scope.row.numberFromName;
},
},
{
prop: "checkExe",
label: "检测脚本",
width: 120,
render: (scope) => {
return scope.row.checkExe == 0
? "国网入网检测脚本(单影响量-模拟式)"
: scope.row.checkExe == 1
? "国网入网检测脚本"
: scope.row.checkExe == 2
? "/"
: scope.row.checkExe;
},
},
{
prop: "wctx",
label: "误差体系",
width: 120,
render: (scope) => {
return scope.row.wctx == 0
? "Q/GDW 1650.2- 2016"
: scope.row.wctx == 1
? "Q/GDW 10650.2 - 2021"
: scope.row.wctx == 2
? "/"
: scope.row.wctx;
},
},
{
type: "tag",
prop: "checkStatus",
label: "检测状态",
width: 120,
render: (scope) => {
return scope.row.checkStatus == 1
? "未检测"
: scope.row.checkStatus == 2
? "检测中"
: scope.row.checkStatus == 3
? "检测完成"
: scope.row.checkStatus;
},
},
{
prop: "checkReport",
label: "检测报告",
width: 120,
render: (scope) => {
return scope.row.checkReport == 1
? "未生成"
: scope.row.checkReport == 2
? "部分生成"
: scope.row.checkReport == 3
? "全部生成"
: scope.row.checkReport;
},
},
{
prop: "checkResult",
label: "检测结果",
width: 120,
render: (scope) => {
return scope.row.checkReport == 1
? "/"
: scope.row.checkReport == 2
? "符合"
: scope.row.checkReport == 3
? "不符合"
: scope.row.checkReport;
},
},
{ prop: "operation", label: "操作", fixed: "right", width: 250 },
]);
//重置查询条件
const resetSearchForm = () => {
searchForm.value = {
intervalType: 0,
time: ["2024-08-20", "2024-08-27"],
searchBeginTime: "",
searchEndTime: "",
checkStatus: 0,
checkReportStatus: 0,
checkResult: 0,
};
};
//查询
const handleSearch = () => {
proTable.value?.getTableList();
};
//重置
const handleRefresh = () => {
proTable.value?.getTableList();
};
// 表格拖拽排序
const sortTable = ({
newIndex,
oldIndex,
}: {
newIndex?: number;
oldIndex?: number;
}) => {
console.log(newIndex, oldIndex);
console.log(proTable.value?.tableData);
ElMessage.success("修改列表排序成功");
};
// 删除用户信息
const deleteAccount = async (params: User.ResUserList) => {
await useHandleData(
deleteUser,
{ id: [params.id] },
`删除【${params.username}`
);
proTable.value?.getTableList();
};
// 批量删除用户信息
const batchDelete = async (id: string[]) => {
await useHandleData(deleteUser, { id }, "删除所选用户信息");
proTable.value?.clearSelection();
proTable.value?.getTableList();
};
// 重置用户密码
const resetPass = async (params: User.ResUserList) => {
await useHandleData(
resetUserPassWord,
{ id: params.id },
`重置【${params.username}】用户密码`
);
proTable.value?.getTableList();
};
// 切换用户状态
const changeStatus = async (row: User.ResUserList) => {
await useHandleData(
changeUserStatus,
{ id: row.id, status: row.status == 1 ? 0 : 1 },
`切换【${row.username}】用户状态`
);
proTable.value?.getTableList();
};
onMounted(() => {
console.log(proTable.value?.tableData);
});
</script>
<style lang="scss" scoped>
.planList {
width: 100%;
// height: calc(100vh - 165px);
height: 100%;
}
::v-deep .el-select {
width: 150px !important;
}
.el-form {
width: 100%;
display: flex;
flex-wrap: wrap;
.el-form-item {
display: flex;
align-items: center;
justify-content: space-between;
.el-button {
margin: 0 !important;
margin-right: 10px !important;
}
}
}
</style>