feat(scada): 新增物联组态
添加组态图片相关页面、路由及API,包含图片展示、设备监控和布局图功能
44
src/api/scada/picture.js
Normal file
@@ -0,0 +1,44 @@
|
||||
import request from '@/utils/request';
|
||||
|
||||
// 查询组态列表
|
||||
export function listCenter(query) {
|
||||
return request({
|
||||
url: '/scada/picture/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
}
|
||||
|
||||
// 查询组态详情
|
||||
export function getCenter(id) {
|
||||
return request({
|
||||
url: '/scada/picture/' + id,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
// 新增组态
|
||||
export function addCenter(data) {
|
||||
return request({
|
||||
url: '/scada/picture',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
// 修改组态
|
||||
export function updateCenter(data) {
|
||||
return request({
|
||||
url: '/scada/picture',
|
||||
method: 'put',
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
// 删除组态
|
||||
export function delCenter(id) {
|
||||
return request({
|
||||
url: '/scada/picture/' + id,
|
||||
method: 'delete'
|
||||
});
|
||||
}
|
||||
3931
src/data/screen.json
Normal file
@@ -124,6 +124,21 @@ export const constantRoutes = [
|
||||
// component: () => import('@/views/scada/topo/share'),
|
||||
// },
|
||||
//以上组态特有
|
||||
{
|
||||
path: '/scada/picture',
|
||||
component: () => import('@/views/scada/picture/index'),
|
||||
hidden: true,
|
||||
},
|
||||
{
|
||||
path: '/scada/picture/monitor',
|
||||
component: () => import('@/views/scada/picture/monitor'),
|
||||
hidden: true,
|
||||
},
|
||||
{
|
||||
path: '/scada/picture/screen',
|
||||
component: () => import('@/views/scada/picture/screen'),
|
||||
hidden: true,
|
||||
},
|
||||
];
|
||||
|
||||
// 动态路由,基于用户权限动态去加载
|
||||
|
||||
|
After Width: | Height: | Size: 302 B |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 102 B |
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 493 KiB |
|
After Width: | Height: | Size: 9.1 KiB |
BIN
src/views/scada/picture/icon/beijing.png
Normal file
|
After Width: | Height: | Size: 493 KiB |
|
After Width: | Height: | Size: 610 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 966 B |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 3.5 KiB |
158
src/views/scada/picture/index.vue
Normal file
@@ -0,0 +1,158 @@
|
||||
<template>
|
||||
<div class="center-wrap">
|
||||
<div v-if="showType == 'card'">
|
||||
<el-row :gutter="15">
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="6" style="margin-top: 7.5px; margin-bottom: 7.5px"
|
||||
v-for="item in centerList" :key="item.id">
|
||||
<el-card class="card-wrap" :body-style="{ padding: '10px' }">
|
||||
<div class="img-wrap" @click="goToDetail(item)">
|
||||
<el-image style="width: 100%; height: 100%; border-radius: 5px" lazy
|
||||
:src="baseApi + item.pageImage" fit="cover"></el-image>
|
||||
</div>
|
||||
<div class="tag-wrap">
|
||||
<span>{{ item.pageResolution ? item.pageResolution : '未知' }}</span>
|
||||
</div>
|
||||
<div class="name-wrap">
|
||||
<span>{{ item.pageName }}</span>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-empty description="暂无数据" v-if="total == 0"></el-empty>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Center',
|
||||
data() {
|
||||
return {
|
||||
loading: false, // 遮罩层
|
||||
baseApi: process.env.VUE_APP_BASE_API,
|
||||
// 固定数据
|
||||
centerList: [
|
||||
{
|
||||
"createBy": "1",
|
||||
"createTime": "2026-03-23 17:31:51",
|
||||
"updateBy": null,
|
||||
"updateTime": "2026-03-23 17:31:51",
|
||||
"remark": null,
|
||||
"pageNum": null,
|
||||
"pageSize": null,
|
||||
"id": 5,
|
||||
"guid": "2b5759ba-8ee7-43e8-bb72-234986e52715",
|
||||
"scadaData": null,
|
||||
"serialNumbers": null,
|
||||
"deviceName": null,
|
||||
"isMainPage": null,
|
||||
"pageName": "国瑞药业冻干三物联组态",
|
||||
"pageResolution": null,
|
||||
"isShare": null,
|
||||
"shareUrl": null,
|
||||
"sharePass": null,
|
||||
"pageImage": "/profile/upload/2026/03/23/11(新)冻干3_1 (1)_20260323173148A001.png",
|
||||
"tenantId": null,
|
||||
"tenantName": null,
|
||||
"delFlag": 0,
|
||||
"base64": null,
|
||||
"bindDeviceList": null
|
||||
}
|
||||
],
|
||||
total: 1, // 总条数
|
||||
showType: 'card', // 展示方式
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
// 不再调用接口
|
||||
},
|
||||
methods: {
|
||||
// 跳转到screen.vue组件
|
||||
goToDetail(row) {
|
||||
// 方法1: 使用路由跳转
|
||||
// this.$router.push({
|
||||
// path: '/scada/picture/screen',
|
||||
// query: {
|
||||
// id: row.id,
|
||||
// guid: row.guid,
|
||||
// },
|
||||
// });
|
||||
|
||||
// 方法2: 在新标签页打开
|
||||
const routeData = this.$router.resolve({
|
||||
path: '/scada/picture/screen',
|
||||
query: {
|
||||
id: row.id,
|
||||
guid: row.guid,
|
||||
},
|
||||
});
|
||||
window.open(routeData.href, '_blank');
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.center-wrap {
|
||||
padding: 20px;
|
||||
|
||||
.card-wrap {
|
||||
position: relative;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.img-wrap {
|
||||
height: 200px;
|
||||
width: 100%;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.tag-wrap {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
background-color: #1890ff;
|
||||
border-top-left-radius: 5px;
|
||||
border-bottom-right-radius: 5px;
|
||||
padding: 5px 15px;
|
||||
font-size: 12px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.name-wrap {
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
margin-top: 10px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.tools-wrap {
|
||||
margin-top: 10px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.right-wrap {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.disable {
|
||||
::v-deep .el-upload--picture-card {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
416
src/views/scada/picture/indieScada.vue
Normal file
@@ -0,0 +1,416 @@
|
||||
<template>
|
||||
<div class="center-wrap">
|
||||
<el-form @submit.native.prevent :model="queryParams" ref="queryForm" size="small" :inline="true"
|
||||
v-show="showSearch" label-width="48px">
|
||||
<el-form-item label="名称" prop="pageName">
|
||||
<el-input v-model="queryParams.pageName" placeholder="请输入页面名称" clearable
|
||||
@keyup.enter.native="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" size="mini" @click="handleResetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-row :gutter="10" class="mb10">
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
|
||||
v-hasPermi="['scada:center:add']">新增</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="success" plain icon="el-icon-edit" size="mini" :disabled="single" @click="handleUpdate"
|
||||
v-hasPermi="['scada:center:edit']">修改</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="danger" plain icon="el-icon-delete" size="mini" :disabled="multiple"
|
||||
@click="handleDelete" v-hasPermi="['scada:center:remove']">删除</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport"
|
||||
v-hasPermi="['scada:center:export']">导出</el-button>
|
||||
</el-col>
|
||||
<template>
|
||||
<el-tooltip effect="dark" content="切换卡片/列表" placement="top" style="float: right;">
|
||||
<el-button size="mini" circle icon="el-icon-s-grid" @click="handleChangeShowType" />
|
||||
</el-tooltip>
|
||||
</template>
|
||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList">
|
||||
</right-toolbar>
|
||||
</el-row>
|
||||
|
||||
<div v-if="showType == 'card'">
|
||||
<el-row :gutter="15" v-loading="loading">
|
||||
<el-checkbox-group v-model="ids" @change="checkboxChange">
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="6" style="margin-top: 7.5px; margin-bottom: 7.5px"
|
||||
v-for="item in centerList" :key="item.id">
|
||||
<el-card class="card-wrap" :body-style="{ padding: '10px' }">
|
||||
<div class="img-wrap">
|
||||
<el-image style="width: 100%; height: 100%; border-radius: 5px" lazy
|
||||
:src="baseApi + item.pageImage" fit="cover" v-hasPermi="['scada:center:query']"
|
||||
@click="goToDetail(item)"></el-image>
|
||||
</div>
|
||||
<div class="tag-wrap">
|
||||
<span>{{ item.pageResolution ? item.pageResolution : '未知' }}</span>
|
||||
</div>
|
||||
<div class="name-wrap">
|
||||
<span>{{ item.pageName }}</span>
|
||||
</div>
|
||||
<div class="tools-wrap">
|
||||
<el-checkbox class="checkbox" :label="item.id" :key="item.id"><span
|
||||
v-show="false">占位符</span></el-checkbox>
|
||||
<div class="right-wrap">
|
||||
<!-- 二期开发 -->
|
||||
<!-- <i class="el-icon-share" style="color: #e6a23c" @click="handleShare(item)"></i> -->
|
||||
<el-dropdown style="margin-left: 8px">
|
||||
<span class="el-dropdown-link"
|
||||
v-hasPermi="['scada:center:preview', 'scada:center:remove', 'scada:center:edit']">
|
||||
<svg-icon style="fill: #1890ff" icon-class="more-vertical"></svg-icon>
|
||||
</span>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item command="edit" v-hasPermi="['scada:center:query']">
|
||||
<el-button size="mini" type="text" icon="el-icon-view"
|
||||
@click="goToDetail(item)">详情</el-button>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item command="preview" v-hasPermi="['scada:center:preview']">
|
||||
<el-button style="color: #e6a23c" type="text" size="mini"
|
||||
icon="el-icon-view" @click="handlePreview(item)">预览</el-button>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item command="remove" v-hasPermi="['scada:center:remove']">
|
||||
<el-button style="color: #f56c6c" size="mini" type="text"
|
||||
icon="el-icon-delete" @click="handleDelete(item)">删除</el-button>
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-checkbox-group>
|
||||
</el-row>
|
||||
<el-empty description="暂无数据" v-if="total == 0"></el-empty>
|
||||
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum"
|
||||
:limit.sync="queryParams.pageSize" @pagination="getList" />
|
||||
</div>
|
||||
<div v-if="showType == 'list'">
|
||||
<el-table v-loading="loading" :data="centerList" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column label="id" align="center" prop="id" width="100" />
|
||||
<el-table-column label="名称" align="center" prop="pageName" />
|
||||
<el-table-column label="分辨率" align="center" prop="pageResolution" width="120">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ scope.row.pageResolution ? scope.row.pageResolution : '未知' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="封面" align="center" prop="pageImage" width="120">
|
||||
<template slot-scope="scope">
|
||||
<image-preview :src="scope.row.pageImage" :width="50" :height="50" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- 二期开发 -->
|
||||
<!-- <el-table-column label="分享" align="center" prop="share" width="120">
|
||||
<template slot-scope="scope">
|
||||
<el-image style="width: 50px; height: 50px" :src="scope.row.share" fit="fit" @click="handleShare(scope.row)"></el-image>
|
||||
</template>
|
||||
</el-table-column> -->
|
||||
<el-table-column label="更新时间" align="center" prop="updateTime" width="180" />
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="180">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-view" @click="goToDetail(scope.row)"
|
||||
v-hasPermi="['scada:center:query']">详情</el-button>
|
||||
<el-button style="color: #e6a23c" size="mini" type="text" icon="el-icon-view"
|
||||
@click="handlePreview(item)" v-hasPermi="['scada:center:preview']">预览</el-button>
|
||||
<el-button style="color: #f56c6c" size="mini" type="text" icon="el-icon-delete"
|
||||
@click="handleDelete(scope.row)" v-hasPermi="['scada:center:remove']">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum"
|
||||
:limit.sync="queryParams.pageSize" @pagination="getList" />
|
||||
</div>
|
||||
|
||||
<!-- 添加或修改组态信息对话框 -->
|
||||
<el-dialog :title="dialog.title" :visible.sync="dialog.open" width="400px" append-to-body>
|
||||
<div class="el-divider el-divider--horizontal" style="margin-top: -25px"></div>
|
||||
<el-form ref="dialogForm" :model="dialog.form" :rules="dialog.rules" label-width="65px">
|
||||
<el-form-item label="封面" prop="pageImage">
|
||||
<image-upload v-model="dialog.form.pageImage" :multiple="false"
|
||||
:class="{ disable: uploadDisabled }" />
|
||||
</el-form-item>
|
||||
<el-form-item label="名称" prop="pageName">
|
||||
<el-input v-model="dialog.form.pageName" placeholder="请输入名称" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="描述" prop="remark">
|
||||
<el-input v-model="dialog.form.remark" placeholder="请输入描述" clearable />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="primary" @click="handleDialogSubmit">确 定</el-button>
|
||||
<el-button @click="handleDialogCancel">取 消</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listCenter, getCenter, delCenter, addCenter, updateCenter } from '@/api/scada/center';
|
||||
|
||||
export default {
|
||||
name: 'Center',
|
||||
dicts: ['sys_page_size'],
|
||||
computed: {
|
||||
uploadDisabled: function () {
|
||||
return this.dialog.form.pageImage !== '';
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: true, // 遮罩层
|
||||
baseApi: process.env.VUE_APP_BASE_API,
|
||||
single: true, // 非单个禁用
|
||||
multiple: true, // 非多个禁用
|
||||
showSearch: true, // 显示搜索条件
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
pageName: null,
|
||||
},
|
||||
ids: [], // 选中数组
|
||||
centerList: [], // 组态中心表格数据
|
||||
total: 0, // 总条数
|
||||
showType: 'card', // 展示方式
|
||||
dialog: {
|
||||
open: false, // 弹出层标题
|
||||
title: '', // 对话框标题
|
||||
// 表单参数
|
||||
form: {
|
||||
pageImage: '',
|
||||
pageName: '',
|
||||
remark: '',
|
||||
},
|
||||
// 表单校验
|
||||
rules: {
|
||||
pageName: [{ required: true, message: '请输入名称', trigger: 'change' }],
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.$busEvent.$on('updateCenter', () => {
|
||||
this.getList();
|
||||
});
|
||||
this.getList();
|
||||
},
|
||||
methods: {
|
||||
// 查询组态中心列表
|
||||
getList() {
|
||||
this.loading = true;
|
||||
listCenter(this.queryParams).then((response) => {
|
||||
if (response.code === 200) {
|
||||
this.centerList = response.rows;
|
||||
this.total = response.total;
|
||||
}
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
// 搜索按钮操作
|
||||
handleQuery() {
|
||||
this.ids = [];
|
||||
this.queryParams.pageNum = 1;
|
||||
this.getList();
|
||||
},
|
||||
// 重置按钮操作
|
||||
handleResetQuery() {
|
||||
this.ids = [];
|
||||
this.resetForm('queryForm');
|
||||
this.handleQuery();
|
||||
},
|
||||
// 新增按钮操作
|
||||
handleAdd() {
|
||||
this.reset();
|
||||
this.dialog.open = true;
|
||||
this.dialog.title = '添加组态信息';
|
||||
},
|
||||
// 表单重置
|
||||
reset() {
|
||||
this.dialog.form = {
|
||||
pageImage: '',
|
||||
pageName: '',
|
||||
remark: '',
|
||||
};
|
||||
this.resetForm('dialogForm');
|
||||
},
|
||||
// 修改按钮操作
|
||||
handleUpdate(row) {
|
||||
this.dialog.title = '修改组态信息';
|
||||
const id = row.id || this.ids;
|
||||
getCenter(id).then((res) => {
|
||||
if (res.code === 200) {
|
||||
this.dialog.form = res.data;
|
||||
this.dialog.open = true;
|
||||
}
|
||||
});
|
||||
},
|
||||
// 提交按钮
|
||||
handleDialogSubmit() {
|
||||
this.$refs['dialogForm'].validate((valid) => {
|
||||
if (valid) {
|
||||
if (this.dialog.form.id != null) {
|
||||
updateCenter(this.dialog.form).then((res) => {
|
||||
if (res.code === 200) {
|
||||
this.$modal.msgSuccess('修改成功');
|
||||
this.dialog.open = false;
|
||||
this.getList();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
addCenter(this.dialog.form).then((res) => {
|
||||
if (res.code === 200) {
|
||||
this.$modal.msgSuccess('新增成功');
|
||||
this.dialog.open = false;
|
||||
this.getList();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
// 取消按钮
|
||||
handleDialogCancel() {
|
||||
this.dialog.open = false;
|
||||
},
|
||||
// 删除按钮操作
|
||||
handleDelete(row) {
|
||||
const ids = row.id || this.ids;
|
||||
this.$modal
|
||||
.confirm('是否确认删除组态编号为"' + ids + '"的数据项?')
|
||||
.then(() => {
|
||||
this.loading = true;
|
||||
return delCenter(ids);
|
||||
})
|
||||
.then(() => {
|
||||
this.loading = true;
|
||||
this.getList();
|
||||
this.$modal.msgSuccess('删除成功');
|
||||
})
|
||||
.catch(() => { });
|
||||
},
|
||||
// 跳转组态详情
|
||||
goToDetail(row) {
|
||||
this.$router.push({
|
||||
path: '/scada/topo/editor',
|
||||
query: {
|
||||
id: row.id,
|
||||
guid: row.guid,
|
||||
},
|
||||
});
|
||||
},
|
||||
// 分享
|
||||
handleShare(row) {
|
||||
this.$alert('二期开发', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
});
|
||||
},
|
||||
// 跳转预览详情
|
||||
handlePreview(row) {
|
||||
const routeUrl = this.$router.resolve({
|
||||
path: '/topo/fullscreen',
|
||||
query: {
|
||||
id: row.id,
|
||||
guid: row.guid,
|
||||
},
|
||||
});
|
||||
window.open(routeUrl.href, '_blank');
|
||||
},
|
||||
// 多选框选中数据
|
||||
handleSelectionChange(selection) {
|
||||
this.ids = selection.map((item) => item.id);
|
||||
this.single = selection.length !== 1;
|
||||
this.multiple = !selection.length;
|
||||
},
|
||||
// 切换显示方式
|
||||
handleChangeShowType() {
|
||||
this.ids = [];
|
||||
this.showType = this.showType == 'card' ? 'list' : 'card';
|
||||
},
|
||||
// 导出按钮操作
|
||||
handleExport() {
|
||||
this.download(
|
||||
'scada/center/export',
|
||||
{
|
||||
...this.queryParams,
|
||||
},
|
||||
`组态${new Date().getTime()}.xlsx`
|
||||
);
|
||||
},
|
||||
// 卡片选择
|
||||
checkboxChange(selection) {
|
||||
this.single = selection.length != 1;
|
||||
this.multiple = !selection.length;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.center-wrap {
|
||||
padding: 20px;
|
||||
|
||||
.card-wrap {
|
||||
position: relative;
|
||||
border-radius: 5px;
|
||||
|
||||
.img-wrap {
|
||||
height: 200px;
|
||||
width: 100%;
|
||||
// transition: transform 0.3s ease;
|
||||
|
||||
// &:hover {
|
||||
// cursor: pointer;
|
||||
// transform: scale(1.1);
|
||||
// }
|
||||
}
|
||||
|
||||
.tag-wrap {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
background-color: #1890ff;
|
||||
border-top-left-radius: 5px;
|
||||
border-bottom-right-radius: 5px;
|
||||
padding: 5px 15px;
|
||||
font-size: 12px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.name-wrap {
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
margin-top: 10px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.tools-wrap {
|
||||
margin-top: 10px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.right-wrap {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.disable {
|
||||
::v-deep .el-upload--picture-card {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
670
src/views/scada/picture/monitor.vue
Normal file
@@ -0,0 +1,670 @@
|
||||
<template>
|
||||
<div class="app">
|
||||
<div class="header">国瑞药业冻干三物联组态</div>
|
||||
|
||||
<!-- 上部分 -->
|
||||
<div class="top-section">
|
||||
<!-- 左侧设备列表 -->
|
||||
<div class="device-sidebar">
|
||||
<div class="sidebar-title">设备列表</div>
|
||||
<div class="table-container">
|
||||
<table class="device-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>设备名称</th>
|
||||
<th>设备状态</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="device in filteredDevices" :key="device.id">
|
||||
<td>{{ device.name }}</td>
|
||||
<td>
|
||||
<span
|
||||
class="status-badge"
|
||||
:class="getStatusClass(device.status)"
|
||||
>
|
||||
{{ getStatusText(device.status) }}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="pagination">
|
||||
<button
|
||||
class="page-btn arrow-btn"
|
||||
@click="goToPreviousDevicePage"
|
||||
:disabled="deviceCurrentPage === 1"
|
||||
>
|
||||
<
|
||||
</button>
|
||||
<button
|
||||
v-for="page in deviceTotalPages"
|
||||
:key="page"
|
||||
class="page-btn"
|
||||
:class="{ active: deviceCurrentPage === page }"
|
||||
@click="deviceCurrentPage = page"
|
||||
>
|
||||
{{ page }}
|
||||
</button>
|
||||
<button
|
||||
class="page-btn arrow-btn"
|
||||
@click="goToNextDevicePage"
|
||||
:disabled="deviceCurrentPage === deviceTotalPages"
|
||||
>
|
||||
>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 右侧维修保养模块 -->
|
||||
<div class="maintenance-panel">
|
||||
<!-- 左侧维修统计故障类型 -->
|
||||
<div class="chart-container">
|
||||
<div class="chart-title">维修统计故障类型</div>
|
||||
<div id="maintenanceChart" class="chart"></div>
|
||||
</div>
|
||||
|
||||
<!-- 右侧表格区域 -->
|
||||
<div class="table-section">
|
||||
<!-- 上部分:切换按钮 -->
|
||||
<div class="table-controls">
|
||||
<button
|
||||
class="control-btn"
|
||||
:class="{ active: activeTable === '维修完成情况' }"
|
||||
@click="activeTable = '维修完成情况'"
|
||||
>
|
||||
维修完成情况
|
||||
</button>
|
||||
<button
|
||||
class="control-btn"
|
||||
:class="{ active: activeTable === '保养统计' }"
|
||||
@click="activeTable = '保养统计'"
|
||||
>
|
||||
保养统计
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- 下部分:表格 -->
|
||||
<div class="table-content">
|
||||
<!-- 维修完成情况表 -->
|
||||
<table class="device-table" v-if="activeTable === '维修完成情况'">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>工单编号</th>
|
||||
<th>工单名称</th>
|
||||
<th>设备名称</th>
|
||||
<th>优先级</th>
|
||||
<th>状态</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(item, index) in filteredMaintenanceData" :key="index">
|
||||
<td>{{ item.workOrderId }}</td>
|
||||
<td>{{ item.workOrderName }}</td>
|
||||
<td>{{ item.deviceName }}</td>
|
||||
<td>{{ item.priority }}</td>
|
||||
<td>
|
||||
<span
|
||||
class="status-badge"
|
||||
:class="getStatusClass(item.status)"
|
||||
>
|
||||
{{ getStatusText(item.status) }}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<!-- 保养统计表 -->
|
||||
<table class="device-table" v-if="activeTable === '保养统计'">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>保养编号</th>
|
||||
<th>保养名称</th>
|
||||
<th>设备名称</th>
|
||||
<th>保养周期</th>
|
||||
<th>状态</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(item, index) in filteredMaintenanceData" :key="index">
|
||||
<td>{{ item.maintenanceId }}</td>
|
||||
<td>{{ item.maintenanceName }}</td>
|
||||
<td>{{ item.deviceName }}</td>
|
||||
<td>{{ item.maintenanceCycle }}</td>
|
||||
<td>
|
||||
<span
|
||||
class="status-badge"
|
||||
:class="{
|
||||
'status-running': item.status === '已完成',
|
||||
'status-warning': item.status === '待开始',
|
||||
'status-error': item.status === '故障'
|
||||
}"
|
||||
>
|
||||
{{ item.status }}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<!-- 分页 -->
|
||||
<div class="pagination">
|
||||
<button
|
||||
class="page-btn arrow-btn"
|
||||
@click="goToPreviousPage"
|
||||
:disabled="maintenanceCurrentPage === 1"
|
||||
>
|
||||
<
|
||||
</button>
|
||||
<button
|
||||
v-for="page in maintenanceTotalPages"
|
||||
:key="page"
|
||||
class="page-btn"
|
||||
:class="{ active: maintenanceCurrentPage === page }"
|
||||
@click="maintenanceCurrentPage = page"
|
||||
>
|
||||
{{ page }}
|
||||
</button>
|
||||
<button
|
||||
class="page-btn arrow-btn"
|
||||
@click="goToNextPage"
|
||||
:disabled="maintenanceCurrentPage === maintenanceTotalPages"
|
||||
>
|
||||
>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 下半部分设备图 -->
|
||||
<div class="bottom-section">
|
||||
<div class="layout-container">
|
||||
<img
|
||||
src="./icon/c354ee887a2f66c387ef3cd49be905b3.png"
|
||||
alt="设备布局"
|
||||
class="layout-image"
|
||||
/>
|
||||
<!-- 流动线 -->
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
// 设备列表数据(模拟2页,每页8个)
|
||||
devices: [
|
||||
{ id: 1, name: '全自动贴标机', status: 'running' },
|
||||
{ id: 2, name: '蒸汽喷射式灭菌器', status: 'running' },
|
||||
{ id: 3, name: '压片机', status: 'warning' },
|
||||
{ id: 4, name: '冻干机自动进出料系统', status: 'running' },
|
||||
{ id: 5, name: '热风循环灭菌烘箱', status: 'error' },
|
||||
{ id: 6, name: '包装机', status: 'running' },
|
||||
{ id: 7, name: '配料系统', status: 'running' },
|
||||
{ id: 8, name: '全自动胶囊充填机', status: 'warning' },
|
||||
{ id: 9, name: '颗粒包装机', status: 'running' },
|
||||
{ id: 10, name: '粉末包装机', status: 'running' },
|
||||
{ id: 11, name: '液体灌装机', status: 'running' },
|
||||
{ id: 12, name: '西林瓶灌装机', status: 'warning' },
|
||||
{ id: 13, name: '安瓿瓶灌装机', status: 'running' },
|
||||
{ id: 14, name: '口服液灌装机', status: 'running' },
|
||||
{ id: 15, name: '软膏灌装机', status: 'error' },
|
||||
{ id: 16, name: '栓剂成型机', status: 'running' },
|
||||
{ id: 17, name: '软胶囊机', status: 'running' },
|
||||
{ id: 18, name: '硬胶囊机', status: 'warning' },
|
||||
{ id: 19, name: '丸剂成型机', status: 'running' },
|
||||
{ id: 20, name: '片剂包衣机', status: 'running' }
|
||||
],
|
||||
selectedDevice: 1,
|
||||
deviceCurrentPage: 1,
|
||||
deviceItemsPerPage: 8,
|
||||
|
||||
// 表格数据
|
||||
activeTable: '维修完成情况',
|
||||
maintenanceCurrentPage: 1,
|
||||
maintenanceItemsPerPage: 8,
|
||||
maintenanceData: [
|
||||
// 维修完成情况数据(20条)
|
||||
{ workOrderId: 'GZ20241106001', workOrderName: '片剂生产', deviceName: '压片机', priority: '高', status: 'running' },
|
||||
{ workOrderId: 'GZ20241106002', workOrderName: '胶囊填充', deviceName: '胶囊充填机', priority: '中', status: 'warning' },
|
||||
{ workOrderId: 'GZ20241106003', workOrderName: '药品包装', deviceName: '包装机', priority: '高', status: 'running' },
|
||||
{ workOrderId: 'GZ20241106004', workOrderName: '原料灭菌', deviceName: '灭菌器', priority: '高', status: 'running' },
|
||||
{ workOrderId: 'GZ20241106005', workOrderName: '冻干处理', deviceName: '冻干机', priority: '高', status: 'running' },
|
||||
{ workOrderId: 'GZ20241106006', workOrderName: '配料准备', deviceName: '配料系统', priority: '中', status: 'running' },
|
||||
{ workOrderId: 'GZ20241106007', workOrderName: '贴标处理', deviceName: '贴标机', priority: '低', status: 'running' },
|
||||
{ workOrderId: 'GZ20241106008', workOrderName: '烘干处理', deviceName: '灭菌烘箱', priority: '高', status: 'error' },
|
||||
{ workOrderId: 'GZ20241106009', workOrderName: '颗粒包装', deviceName: '颗粒包装机', priority: '高', status: 'running' },
|
||||
{ workOrderId: 'GZ20241106010', workOrderName: '粉末包装', deviceName: '粉末包装机', priority: '中', status: 'running' },
|
||||
{ workOrderId: 'GZ20241106011', workOrderName: '液体灌装', deviceName: '液体灌装机', priority: '高', status: 'running' },
|
||||
{ workOrderId: 'GZ20241106012', workOrderName: '西林瓶灌装', deviceName: '西林瓶灌装机', priority: '中', status: 'warning' },
|
||||
{ workOrderId: 'GZ20241106013', workOrderName: '安瓿瓶灌装', deviceName: '安瓿瓶灌装机', priority: '高', status: 'running' },
|
||||
{ workOrderId: 'GZ20241106014', workOrderName: '口服液灌装', deviceName: '口服液灌装机', priority: '高', status: 'running' },
|
||||
{ workOrderId: 'GZ20241106015', workOrderName: '软膏灌装', deviceName: '软膏灌装机', priority: '高', status: 'error' },
|
||||
{ workOrderId: 'GZ20241106016', workOrderName: '栓剂成型', deviceName: '栓剂成型机', priority: '中', status: 'running' },
|
||||
{ workOrderId: 'GZ20241106017', workOrderName: '软胶囊生产', deviceName: '软胶囊机', priority: '高', status: 'running' },
|
||||
{ workOrderId: 'GZ20241106018', workOrderName: '硬胶囊生产', deviceName: '硬胶囊机', priority: '中', status: 'warning' },
|
||||
{ workOrderId: 'GZ20241106019', workOrderName: '丸剂成型', deviceName: '丸剂成型机', priority: '高', status: 'running' },
|
||||
{ workOrderId: 'GZ20241106020', workOrderName: '片剂包衣', deviceName: '片剂包衣机', priority: '高', status: 'running' }
|
||||
],
|
||||
maintenanceStats: [
|
||||
// 保养统计数据(20条)
|
||||
{ maintenanceId: 'BY20241106001', maintenanceName: '季度保养', deviceName: '压片机', maintenanceCycle: '3个月', status: '已完成' },
|
||||
{ maintenanceId: 'BY20241106002', maintenanceName: '月度保养', deviceName: '胶囊充填机', maintenanceCycle: '1个月', status: '待开始' },
|
||||
{ maintenanceId: 'BY20241106003', maintenanceName: '年度保养', deviceName: '包装机', maintenanceCycle: '12个月', status: '已完成' },
|
||||
{ maintenanceId: 'BY20241106004', maintenanceName: '季度保养', deviceName: '灭菌器', maintenanceCycle: '3个月', status: '已完成' },
|
||||
{ maintenanceId: 'BY20241106005', maintenanceName: '月度保养', deviceName: '冻干机', maintenanceCycle: '1个月', status: '已完成' },
|
||||
{ maintenanceId: 'BY20241106006', maintenanceName: '季度保养', deviceName: '配料系统', maintenanceCycle: '3个月', status: '已完成' },
|
||||
{ maintenanceId: 'BY20241106007', maintenanceName: '月度保养', deviceName: '贴标机', maintenanceCycle: '1个月', status: '已完成' },
|
||||
{ maintenanceId: 'BY20241106008', maintenanceName: '年度保养', deviceName: '灭菌烘箱', maintenanceCycle: '12个月', status: '故障' },
|
||||
{ maintenanceId: 'BY20241106009', maintenanceName: '季度保养', deviceName: '颗粒包装机', maintenanceCycle: '3个月', status: '已完成' },
|
||||
{ maintenanceId: 'BY20241106010', maintenanceName: '月度保养', deviceName: '粉末包装机', maintenanceCycle: '1个月', status: '待开始' },
|
||||
{ maintenanceId: 'BY20241106011', maintenanceName: '年度保养', deviceName: '液体灌装机', maintenanceCycle: '12个月', status: '已完成' },
|
||||
{ maintenanceId: 'BY20241106012', maintenanceName: '季度保养', deviceName: '西林瓶灌装机', maintenanceCycle: '3个月', status: '待开始' },
|
||||
{ maintenanceId: 'BY20241106013', maintenanceName: '月度保养', deviceName: '安瓿瓶灌装机', maintenanceCycle: '1个月', status: '已完成' },
|
||||
{ maintenanceId: 'BY20241106014', maintenanceName: '年度保养', deviceName: '口服液灌装机', maintenanceCycle: '12个月', status: '已完成' },
|
||||
{ maintenanceId: 'BY20241106015', maintenanceName: '季度保养', deviceName: '软膏灌装机', maintenanceCycle: '3个月', status: '故障' },
|
||||
{ maintenanceId: 'BY20241106016', maintenanceName: '月度保养', deviceName: '栓剂成型机', maintenanceCycle: '1个月', status: '已完成' },
|
||||
{ maintenanceId: 'BY20241106017', maintenanceName: '年度保养', deviceName: '软胶囊机', maintenanceCycle: '12个月', status: '已完成' },
|
||||
{ maintenanceId: 'BY20241106018', maintenanceName: '季度保养', deviceName: '硬胶囊机', maintenanceCycle: '3个月', status: '待开始' },
|
||||
{ maintenanceId: 'BY20241106019', maintenanceName: '月度保养', deviceName: '丸剂成型机', maintenanceCycle: '1个月', status: '已完成' },
|
||||
{ maintenanceId: 'BY20241106020', maintenanceName: '年度保养', deviceName: '片剂包衣机', maintenanceCycle: '12个月', status: '已完成' }
|
||||
]
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
deviceTotalPages() {
|
||||
return Math.ceil(this.devices.length / this.deviceItemsPerPage);
|
||||
},
|
||||
filteredDevices() {
|
||||
const start = (this.deviceCurrentPage - 1) * this.deviceItemsPerPage;
|
||||
return this.devices.slice(start, start + this.deviceItemsPerPage);
|
||||
},
|
||||
maintenanceTotalPages() {
|
||||
const data = this.activeTable === '维修完成情况' ? this.maintenanceData : this.maintenanceStats;
|
||||
return Math.ceil(data.length / this.maintenanceItemsPerPage);
|
||||
},
|
||||
filteredMaintenanceData() {
|
||||
const data = this.activeTable === '维修完成情况' ? this.maintenanceData : this.maintenanceStats;
|
||||
const start = (this.maintenanceCurrentPage - 1) * this.maintenanceItemsPerPage;
|
||||
return data.slice(start, start + this.maintenanceItemsPerPage);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
selectDevice(deviceId) {
|
||||
this.selectedDevice = deviceId;
|
||||
},
|
||||
getStatusClass(status) {
|
||||
switch(status) {
|
||||
case 'running': return 'status-running';
|
||||
case 'warning': return 'status-warning';
|
||||
case 'error': return 'status-error';
|
||||
default: return '';
|
||||
}
|
||||
},
|
||||
getStatusText(status) {
|
||||
switch(status) {
|
||||
case 'running': return '运行中';
|
||||
case 'warning': return '预警';
|
||||
case 'error': return '故障';
|
||||
default: return '未知';
|
||||
}
|
||||
},
|
||||
goToPreviousPage() {
|
||||
if (this.maintenanceCurrentPage > 1) {
|
||||
this.maintenanceCurrentPage--;
|
||||
}
|
||||
},
|
||||
goToNextPage() {
|
||||
if (this.maintenanceCurrentPage < this.maintenanceTotalPages) {
|
||||
this.maintenanceCurrentPage++;
|
||||
}
|
||||
},
|
||||
goToPreviousDevicePage() {
|
||||
if (this.deviceCurrentPage > 1) {
|
||||
this.deviceCurrentPage--;
|
||||
}
|
||||
},
|
||||
goToNextDevicePage() {
|
||||
if (this.deviceCurrentPage < this.deviceTotalPages) {
|
||||
this.deviceCurrentPage++;
|
||||
}
|
||||
},
|
||||
initChart() {
|
||||
const chartDom = document.getElementById('maintenanceChart');
|
||||
const myChart = echarts.init(chartDom);
|
||||
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: '{a} <br/>{b}: {c} ({d}%)'
|
||||
},
|
||||
legend: {
|
||||
orient: 'vertical',
|
||||
left: 'left',
|
||||
textStyle: {
|
||||
color: '#ffffff'
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '故障类型',
|
||||
type: 'pie',
|
||||
radius: ['40%', '70%'],
|
||||
avoidLabelOverlap: false,
|
||||
itemStyle: {
|
||||
borderRadius: 10,
|
||||
borderColor: '#0a0e27',
|
||||
borderWidth: 2
|
||||
},
|
||||
label: {
|
||||
show: false,
|
||||
position: 'center'
|
||||
},
|
||||
emphasis: {
|
||||
label: {
|
||||
show: true,
|
||||
fontSize: '18',
|
||||
fontWeight: 'bold',
|
||||
color: '#ffffff'
|
||||
}
|
||||
},
|
||||
labelLine: {
|
||||
show: false
|
||||
},
|
||||
data: [
|
||||
{ value: 4, name: '机械故障', itemStyle: { color: '#ff6b6b' } },
|
||||
{ value: 3, name: '电气故障', itemStyle: { color: '#4ecdc4' } },
|
||||
{ value: 2, name: '软件故障', itemStyle: { color: '#ffe66d' } },
|
||||
{ value: 1, name: '其他故障', itemStyle: { color: '#1a535c' } }
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
myChart.setOption(option);
|
||||
|
||||
window.addEventListener('resize', () => {
|
||||
myChart.resize();
|
||||
});
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.initChart();
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.app {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
font-family: Arial, sans-serif;
|
||||
background: url('./icon/b4875ec42c53f353804aad374e932828.png') no-repeat center !important;
|
||||
background-size: cover !important;
|
||||
color: #ffffff;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
#app {
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
.header {
|
||||
background: transparent;
|
||||
color: #00ffff;
|
||||
padding: 10px 20px;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
text-shadow: 0 0 20px rgba(0, 255, 255, 0.5);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.top-section {
|
||||
display: grid;
|
||||
grid-template-columns: 40% 60%;
|
||||
gap: 50px;
|
||||
flex: 1;
|
||||
padding: 20px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.device-sidebar {
|
||||
background: url('./icon/c73de8bc60f90ef1a6e63178f6d8f862.png') no-repeat center;
|
||||
background-size: cover;
|
||||
padding: 15px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.sidebar-title {
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
color: #00ffff;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.table-container {
|
||||
padding: 15px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.table-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.table-content {
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.table-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.table-title {
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
color: #00ffff;
|
||||
}
|
||||
|
||||
.table-controls {
|
||||
display: flex;
|
||||
gap: 0;
|
||||
}
|
||||
|
||||
.control-btn {
|
||||
padding: 6px 12px;
|
||||
background: #0066cc;
|
||||
border: 1px solid #0066cc;
|
||||
color: #ffffff;
|
||||
cursor: pointer;
|
||||
font-size: 11px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.control-btn:first-child {
|
||||
border-radius: 4px 0 0 4px;
|
||||
}
|
||||
|
||||
.control-btn:last-child {
|
||||
border-radius: 0 4px 4px 0;
|
||||
}
|
||||
|
||||
.control-btn:hover {
|
||||
background: #0088ff;
|
||||
}
|
||||
|
||||
.control-btn.active {
|
||||
background: #0088ff;
|
||||
}
|
||||
|
||||
.device-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: 11px;
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.device-table th,
|
||||
.device-table td {
|
||||
padding: 8px 10px;
|
||||
text-align: left;
|
||||
border-bottom: 1px solid rgba(0, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.device-table th {
|
||||
background: rgba(0, 255, 255, 0.1);
|
||||
color: #00ffff;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.device-table tbody tr:hover {
|
||||
background: rgba(0, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.pagination {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-top: 15px;
|
||||
gap: 5px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.page-btn {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: #ffffff;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 12px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.page-btn.arrow-btn {
|
||||
background: rgba(0, 255, 255, 0.1);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.page-btn:hover {
|
||||
background: rgba(0, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.page-btn.active {
|
||||
background: #00ffff;
|
||||
color: #000000;
|
||||
border-radius: 4px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.status-badge {
|
||||
padding: 4px 8px;
|
||||
border-radius: 12px;
|
||||
font-size: 10px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.status-running {
|
||||
background: rgba(0, 255, 0, 0.2);
|
||||
color: #00ff00;
|
||||
}
|
||||
|
||||
.status-warning {
|
||||
background: rgba(255, 255, 0, 0.2);
|
||||
color: #ffff00;
|
||||
}
|
||||
|
||||
.status-error {
|
||||
background: rgba(255, 0, 0, 0.2);
|
||||
color: #ff0000;
|
||||
}
|
||||
|
||||
.bottom-section {
|
||||
flex: 1;
|
||||
padding: 20px;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.layout-image {
|
||||
width: 95%;
|
||||
height: 95%;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.maintenance-panel {
|
||||
background: url('./icon/f0f06ffbc9509f233fe5b4e670416c31.png') no-repeat center;
|
||||
background-size: cover;
|
||||
padding: 15px;
|
||||
display: grid;
|
||||
grid-template-columns: 2fr 3fr;
|
||||
gap: 10px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.chart-container {
|
||||
padding: 15px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.chart-title {
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
color: #00ffff;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.chart {
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
}
|
||||
|
||||
.layout-container {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
1012
src/views/scada/picture/screen.vue
Normal file
787
src/views/scada/picture/screenTwo.vue
Normal file
@@ -0,0 +1,787 @@
|
||||
<template>
|
||||
<div class="container">
|
||||
<div class="header">国瑞药业冻干三物联组态</div>
|
||||
|
||||
<!-- 上部分 -->
|
||||
<div class="top-section">
|
||||
<!-- 左侧设备列表 -->
|
||||
<div class="device-sidebar">
|
||||
<div class="sidebar-title">设备列表</div>
|
||||
<div class="table-container">
|
||||
<table class="device-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>设备名称</th>
|
||||
<th>设备状态</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="device in filteredDevices" :key="device.id">
|
||||
<td>{{ device.name }}</td>
|
||||
<td>
|
||||
<span
|
||||
class="status-badge"
|
||||
:class="getStatusClass(device.status)"
|
||||
>
|
||||
{{ getStatusText(device.status) }}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="pagination">
|
||||
<button
|
||||
class="page-btn arrow-btn"
|
||||
@click="goToPreviousDevicePage"
|
||||
:disabled="deviceCurrentPage === 1"
|
||||
>
|
||||
<
|
||||
</button>
|
||||
<button
|
||||
v-for="page in deviceTotalPages"
|
||||
:key="page"
|
||||
class="page-btn"
|
||||
:class="{ active: deviceCurrentPage === page }"
|
||||
@click="deviceCurrentPage = page"
|
||||
>
|
||||
{{ page }}
|
||||
</button>
|
||||
<button
|
||||
class="page-btn arrow-btn"
|
||||
@click="goToNextDevicePage"
|
||||
:disabled="deviceCurrentPage === deviceTotalPages"
|
||||
>
|
||||
>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 右侧维修保养模块 -->
|
||||
<div class="maintenance-panel">
|
||||
<!-- 左侧维修统计故障类型 -->
|
||||
<div class="chart-container">
|
||||
<div class="chart-title">维修统计故障类型</div>
|
||||
<div id="maintenanceChart" class="chart"></div>
|
||||
</div>
|
||||
|
||||
<!-- 右侧表格区域 -->
|
||||
<div class="table-section">
|
||||
<!-- 上部分:切换按钮 -->
|
||||
<div class="table-controls">
|
||||
<button
|
||||
class="control-btn"
|
||||
:class="{ active: activeTable === '维修完成情况' }"
|
||||
@click="activeTable = '维修完成情况'"
|
||||
>
|
||||
维修完成情况
|
||||
</button>
|
||||
<button
|
||||
class="control-btn"
|
||||
:class="{ active: activeTable === '保养统计' }"
|
||||
@click="activeTable = '保养统计'"
|
||||
>
|
||||
保养统计
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- 下部分:表格 -->
|
||||
<div class="table-content">
|
||||
<!-- 维修完成情况表 -->
|
||||
<table class="device-table" v-if="activeTable === '维修完成情况'">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>工单编号</th>
|
||||
<th>工单名称</th>
|
||||
<th>设备名称</th>
|
||||
<th>优先级</th>
|
||||
<th>状态</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(item, index) in filteredMaintenanceData" :key="index">
|
||||
<td>{{ item.workOrderId }}</td>
|
||||
<td>{{ item.workOrderName }}</td>
|
||||
<td>{{ item.deviceName }}</td>
|
||||
<td>{{ item.priority }}</td>
|
||||
<td>
|
||||
<span
|
||||
class="status-badge"
|
||||
:class="getStatusClass(item.status)"
|
||||
>
|
||||
{{ getStatusText(item.status) }}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<!-- 保养统计表 -->
|
||||
<table class="device-table" v-if="activeTable === '保养统计'">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>保养编号</th>
|
||||
<th>保养名称</th>
|
||||
<th>设备名称</th>
|
||||
<th>保养周期</th>
|
||||
<th>状态</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(item, index) in filteredMaintenanceData" :key="index">
|
||||
<td>{{ item.maintenanceId }}</td>
|
||||
<td>{{ item.maintenanceName }}</td>
|
||||
<td>{{ item.deviceName }}</td>
|
||||
<td>{{ item.maintenanceCycle }}</td>
|
||||
<td>
|
||||
<span
|
||||
class="status-badge"
|
||||
:class="{
|
||||
'status-running': item.status === '已完成',
|
||||
'status-warning': item.status === '待开始',
|
||||
'status-error': item.status === '故障'
|
||||
}"
|
||||
>
|
||||
{{ item.status }}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<!-- 分页 -->
|
||||
<div class="pagination">
|
||||
<button
|
||||
class="page-btn arrow-btn"
|
||||
@click="goToPreviousPage"
|
||||
:disabled="maintenanceCurrentPage === 1"
|
||||
>
|
||||
<
|
||||
</button>
|
||||
<button
|
||||
v-for="page in maintenanceTotalPages"
|
||||
:key="page"
|
||||
class="page-btn"
|
||||
:class="{ active: maintenanceCurrentPage === page }"
|
||||
@click="maintenanceCurrentPage = page"
|
||||
>
|
||||
{{ page }}
|
||||
</button>
|
||||
<button
|
||||
class="page-btn arrow-btn"
|
||||
@click="goToNextPage"
|
||||
:disabled="maintenanceCurrentPage === maintenanceTotalPages"
|
||||
>
|
||||
>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 下半部分设备图 -->
|
||||
<div class="bottom-section">
|
||||
<div class="layout-container">
|
||||
<img
|
||||
src="./icon/c354ee887a2f66c387ef3cd49be905b3.png"
|
||||
alt="设备布局"
|
||||
class="layout-image"
|
||||
>
|
||||
<!-- 标记点 -->
|
||||
<div class="marker marker-1" @click="handleMarkerClick(1)"></div>
|
||||
<div class="marker marker-2" @click="handleMarkerClick(2)"></div>
|
||||
<div class="marker marker-3" @click="handleMarkerClick(3)"></div>
|
||||
<div class="marker marker-4" @click="handleMarkerClick(4)"></div>
|
||||
<div class="marker marker-5" @click="handleMarkerClick(5)"></div>
|
||||
<div class="marker marker-6" @click="handleMarkerClick(6)"></div>
|
||||
<div class="marker marker-7" @click="handleMarkerClick(7)"></div>
|
||||
<div class="marker marker-8" @click="handleMarkerClick(8)"></div>
|
||||
<div class="marker marker-9" @click="handleMarkerClick(98)"></div>
|
||||
<div class="marker marker-16" @click="handleMarkerClick(16)"></div>
|
||||
<div class="marker marker-15" @click="handleMarkerClick(15)"></div>
|
||||
<div class="marker marker-17" @click="handleMarkerClick(17)"></div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
// URL参数
|
||||
id: '',
|
||||
guid: '',
|
||||
// 设备列表数据(模拟2页,每页10个)
|
||||
devices: [
|
||||
{ id: 1, name: '全自动贴标机', status: 'running' },
|
||||
{ id: 2, name: '蒸汽喷射式灭菌器', status: 'running' },
|
||||
{ id: 3, name: '压片机', status: 'warning' },
|
||||
{ id: 4, name: '冻干机自动进出料系统', status: 'running' },
|
||||
{ id: 5, name: '热风循环灭菌烘箱', status: 'error' },
|
||||
{ id: 6, name: '包装机', status: 'running' },
|
||||
{ id: 7, name: '配料系统', status: 'running' },
|
||||
{ id: 8, name: '全自动胶囊充填机', status: 'warning' },
|
||||
{ id: 9, name: '颗粒包装机', status: 'running' },
|
||||
{ id: 10, name: '粉末包装机', status: 'running' },
|
||||
{ id: 11, name: '液体灌装机', status: 'running' },
|
||||
{ id: 12, name: '西林瓶灌装机', status: 'warning' },
|
||||
{ id: 13, name: '安瓿瓶灌装机', status: 'running' },
|
||||
{ id: 14, name: '口服液灌装机', status: 'running' },
|
||||
{ id: 15, name: '软膏灌装机', status: 'error' },
|
||||
{ id: 16, name: '栓剂成型机', status: 'running' },
|
||||
{ id: 17, name: '软胶囊机', status: 'running' },
|
||||
{ id: 18, name: '硬胶囊机', status: 'warning' },
|
||||
{ id: 19, name: '丸剂成型机', status: 'running' },
|
||||
{ id: 20, name: '片剂包衣机', status: 'running' }
|
||||
],
|
||||
selectedDevice: 1,
|
||||
deviceCurrentPage: 1,
|
||||
deviceItemsPerPage: 5,
|
||||
|
||||
// 表格数据
|
||||
activeTable: '维修完成情况',
|
||||
maintenanceCurrentPage: 1,
|
||||
maintenanceItemsPerPage: 5,
|
||||
maintenanceData: [
|
||||
// 维修完成情况数据(20条)
|
||||
{ workOrderId: 'GZ20241106001', workOrderName: '片剂生产', deviceName: '压片机', priority: '高', status: 'running' },
|
||||
{ workOrderId: 'GZ20241106002', workOrderName: '胶囊填充', deviceName: '胶囊充填机', priority: '中', status: 'warning' },
|
||||
{ workOrderId: 'GZ20241106003', workOrderName: '药品包装', deviceName: '包装机', priority: '高', status: 'running' },
|
||||
{ workOrderId: 'GZ20241106004', workOrderName: '原料灭菌', deviceName: '灭菌器', priority: '高', status: 'running' },
|
||||
{ workOrderId: 'GZ20241106005', workOrderName: '冻干处理', deviceName: '冻干机', priority: '高', status: 'running' },
|
||||
{ workOrderId: 'GZ20241106006', workOrderName: '配料准备', deviceName: '配料系统', priority: '中', status: 'running' },
|
||||
{ workOrderId: 'GZ20241106007', workOrderName: '贴标处理', deviceName: '贴标机', priority: '低', status: 'running' },
|
||||
{ workOrderId: 'GZ20241106008', workOrderName: '烘干处理', deviceName: '灭菌烘箱', priority: '高', status: 'error' },
|
||||
{ workOrderId: 'GZ20241106009', workOrderName: '颗粒包装', deviceName: '颗粒包装机', priority: '高', status: 'running' },
|
||||
{ workOrderId: 'GZ20241106010', workOrderName: '粉末包装', deviceName: '粉末包装机', priority: '中', status: 'running' },
|
||||
{ workOrderId: 'GZ20241106011', workOrderName: '液体灌装', deviceName: '液体灌装机', priority: '高', status: 'running' },
|
||||
{ workOrderId: 'GZ20241106012', workOrderName: '西林瓶灌装', deviceName: '西林瓶灌装机', priority: '中', status: 'warning' },
|
||||
{ workOrderId: 'GZ20241106013', workOrderName: '安瓿瓶灌装', deviceName: '安瓿瓶灌装机', priority: '高', status: 'running' },
|
||||
{ workOrderId: 'GZ20241106014', workOrderName: '口服液灌装', deviceName: '口服液灌装机', priority: '高', status: 'running' },
|
||||
{ workOrderId: 'GZ20241106015', workOrderName: '软膏灌装', deviceName: '软膏灌装机', priority: '高', status: 'error' },
|
||||
{ workOrderId: 'GZ20241106016', workOrderName: '栓剂成型', deviceName: '栓剂成型机', priority: '中', status: 'running' },
|
||||
{ workOrderId: 'GZ20241106017', workOrderName: '软胶囊生产', deviceName: '软胶囊机', priority: '高', status: 'running' },
|
||||
{ workOrderId: 'GZ20241106018', workOrderName: '硬胶囊生产', deviceName: '硬胶囊机', priority: '中', status: 'warning' },
|
||||
{ workOrderId: 'GZ20241106019', workOrderName: '丸剂成型', deviceName: '丸剂成型机', priority: '高', status: 'running' },
|
||||
{ workOrderId: 'GZ20241106020', workOrderName: '片剂包衣', deviceName: '片剂包衣机', priority: '高', status: 'running' }
|
||||
],
|
||||
maintenanceStats: [
|
||||
// 保养统计数据(20条)
|
||||
{ maintenanceId: 'BY20241106001', maintenanceName: '季度保养', deviceName: '压片机', maintenanceCycle: '3个月', status: '已完成' },
|
||||
{ maintenanceId: 'BY20241106002', maintenanceName: '月度保养', deviceName: '胶囊充填机', maintenanceCycle: '1个月', status: '待开始' },
|
||||
{ maintenanceId: 'BY20241106003', maintenanceName: '年度保养', deviceName: '包装机', maintenanceCycle: '12个月', status: '已完成' },
|
||||
{ maintenanceId: 'BY20241106004', maintenanceName: '季度保养', deviceName: '灭菌器', maintenanceCycle: '3个月', status: '已完成' },
|
||||
{ maintenanceId: 'BY20241106005', maintenanceName: '月度保养', deviceName: '冻干机', maintenanceCycle: '1个月', status: '已完成' },
|
||||
{ maintenanceId: 'BY20241106006', maintenanceName: '季度保养', deviceName: '配料系统', maintenanceCycle: '3个月', status: '已完成' },
|
||||
{ maintenanceId: 'BY20241106007', maintenanceName: '月度保养', deviceName: '贴标机', maintenanceCycle: '1个月', status: '已完成' },
|
||||
{ maintenanceId: 'BY20241106008', maintenanceName: '年度保养', deviceName: '灭菌烘箱', maintenanceCycle: '12个月', status: '故障' },
|
||||
{ maintenanceId: 'BY20241106009', maintenanceName: '季度保养', deviceName: '颗粒包装机', maintenanceCycle: '3个月', status: '已完成' },
|
||||
{ maintenanceId: 'BY20241106010', maintenanceName: '月度保养', deviceName: '粉末包装机', maintenanceCycle: '1个月', status: '待开始' },
|
||||
{ maintenanceId: 'BY20241106011', maintenanceName: '年度保养', deviceName: '液体灌装机', maintenanceCycle: '12个月', status: '已完成' },
|
||||
{ maintenanceId: 'BY20241106012', maintenanceName: '季度保养', deviceName: '西林瓶灌装机', maintenanceCycle: '3个月', status: '待开始' },
|
||||
{ maintenanceId: 'BY20241106013', maintenanceName: '月度保养', deviceName: '安瓿瓶灌装机', maintenanceCycle: '1个月', status: '已完成' },
|
||||
{ maintenanceId: 'BY20241106014', maintenanceName: '年度保养', deviceName: '口服液灌装机', maintenanceCycle: '12个月', status: '已完成' },
|
||||
{ maintenanceId: 'BY20241106015', maintenanceName: '季度保养', deviceName: '软膏灌装机', maintenanceCycle: '3个月', status: '故障' },
|
||||
{ maintenanceId: 'BY20241106016', maintenanceName: '月度保养', deviceName: '栓剂成型机', maintenanceCycle: '1个月', status: '已完成' },
|
||||
{ maintenanceId: 'BY20241106017', maintenanceName: '年度保养', deviceName: '软胶囊机', maintenanceCycle: '12个月', status: '已完成' },
|
||||
{ maintenanceId: 'BY20241106018', maintenanceName: '季度保养', deviceName: '硬胶囊机', maintenanceCycle: '3个月', status: '待开始' },
|
||||
{ maintenanceId: 'BY20241106019', maintenanceName: '月度保养', deviceName: '丸剂成型机', maintenanceCycle: '1个月', status: '已完成' },
|
||||
{ maintenanceId: 'BY20241106020', maintenanceName: '年度保养', deviceName: '片剂包衣机', maintenanceCycle: '12个月', status: '已完成' }
|
||||
]
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
deviceTotalPages() {
|
||||
return Math.ceil(this.devices.length / this.deviceItemsPerPage);
|
||||
},
|
||||
filteredDevices() {
|
||||
const start = (this.deviceCurrentPage - 1) * this.deviceItemsPerPage;
|
||||
return this.devices.slice(start, start + this.deviceItemsPerPage);
|
||||
},
|
||||
maintenanceTotalPages() {
|
||||
const data = this.activeTable === '维修完成情况' ? this.maintenanceData : this.maintenanceStats;
|
||||
return Math.ceil(data.length / this.maintenanceItemsPerPage);
|
||||
},
|
||||
filteredMaintenanceData() {
|
||||
const data = this.activeTable === '维修完成情况' ? this.maintenanceData : this.maintenanceStats;
|
||||
const start = (this.maintenanceCurrentPage - 1) * this.maintenanceItemsPerPage;
|
||||
return data.slice(start, start + this.maintenanceItemsPerPage);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 获取URL参数
|
||||
getUrlParams() {
|
||||
this.id = this.$route.query.id || '';
|
||||
this.guid = this.$route.query.guid || '';
|
||||
console.log('URL参数:', { id: this.id, guid: this.guid });
|
||||
},
|
||||
selectDevice(deviceId) {
|
||||
this.selectedDevice = deviceId;
|
||||
},
|
||||
getStatusClass(status) {
|
||||
switch(status) {
|
||||
case 'running': return 'status-running';
|
||||
case 'warning': return 'status-warning';
|
||||
case 'error': return 'status-error';
|
||||
default: return '';
|
||||
}
|
||||
},
|
||||
getStatusText(status) {
|
||||
switch(status) {
|
||||
case 'running': return '运行中';
|
||||
case 'warning': return '预警';
|
||||
case 'error': return '故障';
|
||||
default: return '未知';
|
||||
}
|
||||
},
|
||||
goToPreviousPage() {
|
||||
if (this.maintenanceCurrentPage > 1) {
|
||||
this.maintenanceCurrentPage--;
|
||||
}
|
||||
},
|
||||
goToNextPage() {
|
||||
if (this.maintenanceCurrentPage < this.maintenanceTotalPages) {
|
||||
this.maintenanceCurrentPage++;
|
||||
}
|
||||
},
|
||||
goToPreviousDevicePage() {
|
||||
if (this.deviceCurrentPage > 1) {
|
||||
this.deviceCurrentPage--;
|
||||
}
|
||||
},
|
||||
goToNextDevicePage() {
|
||||
if (this.deviceCurrentPage < this.deviceTotalPages) {
|
||||
this.deviceCurrentPage++;
|
||||
}
|
||||
},
|
||||
// 标记点点击事件处理函数(预留)
|
||||
handleMarkerClick(markerId) {
|
||||
console.log('Marker clicked:', markerId);
|
||||
// 后续可以根据markerId执行不同的操作
|
||||
// 例如:显示设备详情、跳转到设备监控页面等
|
||||
},
|
||||
initChart() {
|
||||
const chartDom = document.getElementById('maintenanceChart');
|
||||
const myChart = this.$echarts.init(chartDom);
|
||||
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: '{b}: {c} ({d}%)',
|
||||
textStyle: {
|
||||
color: '#ffffff'
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
orient: 'vertical',
|
||||
left: 'left',
|
||||
textStyle: {
|
||||
color: '#ffffff',
|
||||
fontSize: 12
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '故障类型',
|
||||
type: 'pie',
|
||||
radius: ['40%', '70%'],
|
||||
avoidLabelOverlap: false,
|
||||
itemStyle: {
|
||||
borderRadius: 0,
|
||||
borderColor: '#001a33',
|
||||
borderWidth: 2
|
||||
},
|
||||
label: {
|
||||
show: true,
|
||||
position: 'outside',
|
||||
formatter: '{b}\n{d}%',
|
||||
color: '#ffffff',
|
||||
fontSize: 12
|
||||
},
|
||||
labelLine: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
color: '#ffffff'
|
||||
}
|
||||
},
|
||||
data: [
|
||||
{ value: 14, name: '机械故障', itemStyle: { color: '#ff8c00' } },
|
||||
{ value: 5, name: '电气故障', itemStyle: { color: '#9370db' } },
|
||||
{ value: 4, name: '系统故障', itemStyle: { color: '#4682b4' } },
|
||||
{ value: 4, name: '其他', itemStyle: { color: '#ffd700' } }
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
myChart.setOption(option);
|
||||
|
||||
window.addEventListener('resize', () => {
|
||||
myChart.resize();
|
||||
});
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.getUrlParams();
|
||||
this.initChart();
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.container {
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
font-family: Arial, sans-serif;
|
||||
background: url('./icon/b4875ec42c53f353804aad374e932828.png') no-repeat center center;
|
||||
background-size: 100% 100%;
|
||||
color: #ffffff;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.header {
|
||||
text-align: center;
|
||||
padding: 10px 0;
|
||||
font-size: 28px;
|
||||
font-weight: bold;
|
||||
color: #ffffff;
|
||||
text-shadow: 0 0 20px rgba(0, 255, 255, 0.5);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.top-section {
|
||||
display: grid;
|
||||
grid-template-columns: 30% 68%;
|
||||
gap: 30px;
|
||||
flex: 1;
|
||||
padding: 40px;
|
||||
padding-bottom: 20px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.device-sidebar {
|
||||
background: url('./icon/c73de8bc60f90ef1a6e63178f6d8f862.png') no-repeat center;
|
||||
background-size: 100% 100%;
|
||||
padding: 15px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.sidebar-title {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #ffffff;
|
||||
margin-bottom: 10px;
|
||||
padding-bottom: 8px;
|
||||
border-bottom: 1px solid #00ffff;
|
||||
flex-shrink: 0;
|
||||
padding-left:20px;
|
||||
}
|
||||
|
||||
.table-container {
|
||||
padding: 15px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.table-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.table-content {
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.table-controls {
|
||||
display: flex;
|
||||
gap: 0;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.control-btn {
|
||||
padding: 6px 12px;
|
||||
background: #0066cc;
|
||||
border: 1px solid #0066cc;
|
||||
color: #ffffff;
|
||||
cursor: pointer;
|
||||
font-size: 11px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.control-btn:first-child {
|
||||
border-radius: 4px 0 0 4px;
|
||||
}
|
||||
|
||||
.control-btn:last-child {
|
||||
border-radius: 0 4px 4px 0;
|
||||
}
|
||||
|
||||
.control-btn:hover {
|
||||
background: #0088ff;
|
||||
}
|
||||
|
||||
.control-btn.active {
|
||||
background: #0088ff;
|
||||
}
|
||||
|
||||
.device-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: 11px;
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.device-table th,
|
||||
.device-table td {
|
||||
padding: 8px 10px;
|
||||
text-align: left;
|
||||
border-bottom: 1px solid rgba(0, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.device-table th {
|
||||
background: rgba(0, 255, 255, 0.1);
|
||||
color: #ffffff;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.device-table tbody tr:hover {
|
||||
background: rgba(0, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.pagination {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-top: 15px;
|
||||
gap: 5px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.page-btn {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: #ffffff;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 12px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.page-btn.arrow-btn {
|
||||
background: rgba(0, 255, 255, 0.1);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.page-btn:hover {
|
||||
background: rgba(0, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.page-btn.active {
|
||||
background: #00ffff;
|
||||
color: #000000;
|
||||
border-radius: 4px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.status-badge {
|
||||
padding: 4px 8px;
|
||||
border-radius: 12px;
|
||||
font-size: 10px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.status-running {
|
||||
background: rgba(0, 255, 0, 0.2);
|
||||
color: #00ff00;
|
||||
}
|
||||
|
||||
.status-warning {
|
||||
background: rgba(255, 255, 0, 0.2);
|
||||
color: #ffff00;
|
||||
}
|
||||
|
||||
.status-error {
|
||||
background: rgba(255, 0, 0, 0.2);
|
||||
color: #ff0000;
|
||||
}
|
||||
|
||||
.bottom-section {
|
||||
flex: 1;
|
||||
padding: 20px;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.layout-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.layout-container {
|
||||
position: relative;
|
||||
width: 90%;
|
||||
height: 90%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.marker {
|
||||
position: absolute;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
transform: translate(-50%, -50%);
|
||||
cursor: pointer;
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.marker:hover {
|
||||
transform: translate(-50%, -50%) scale(1.2);
|
||||
}
|
||||
|
||||
.marker-1 {
|
||||
transform: scale(1.5);
|
||||
top: 45%;
|
||||
left: 245px;
|
||||
background-image: url('./icon/371b06b22391dea6aa6adf7fb36c7d2b.png');
|
||||
}
|
||||
|
||||
.marker-2 {
|
||||
transform: scale(1.5);
|
||||
top: 45%;
|
||||
left: 375px;
|
||||
background-image: url('./icon/371b06b22391dea6aa6adf7fb36c7d2b.png');
|
||||
}
|
||||
|
||||
.marker-3 {
|
||||
transform: scale(1.5);
|
||||
top: 45%;
|
||||
left: 510px;
|
||||
background-image: url('./icon/371b06b22391dea6aa6adf7fb36c7d2b.png');
|
||||
}
|
||||
|
||||
.marker-4 {
|
||||
transform: scale(1.5);
|
||||
top: 42%;
|
||||
left: 653px;
|
||||
background-image: url('./icon/371b06b22391dea6aa6adf7fb36c7d2b.png');
|
||||
}
|
||||
|
||||
.marker-5 {
|
||||
transform: scale(1.5);
|
||||
top: 55%;
|
||||
left: 409px;
|
||||
background-image: url('./icon/633bec62b4c6d35098f06c189591135d.png');
|
||||
}
|
||||
|
||||
.marker-6 {
|
||||
transform: scale(1.5);
|
||||
top: 55%;
|
||||
left: 546px;
|
||||
background-image: url('./icon/633bec62b4c6d35098f06c189591135d.png');
|
||||
}
|
||||
.marker-15 {
|
||||
transform: scale(1.5);
|
||||
top: 55%;
|
||||
left: 686px;
|
||||
background-image: url('./icon/633bec62b4c6d35098f06c189591135d.png');
|
||||
}
|
||||
|
||||
.marker-16 {
|
||||
transform: scale(1.5);
|
||||
top: 55%;
|
||||
left: 272px;
|
||||
background-image: url('./icon/633bec62b4c6d35098f06c189591135d.png');
|
||||
}
|
||||
|
||||
.marker-7 {
|
||||
transform: scale(1.5);
|
||||
top: 10%;
|
||||
left: 985px;
|
||||
background-image: url('./icon/b257f08f180fba833e8302c54f815ba8.png');
|
||||
}
|
||||
|
||||
.marker-8 {
|
||||
transform: scale(1.5);
|
||||
top: 24%;
|
||||
left: 992px;
|
||||
background-image: url('./icon/b257f08f180fba833e8302c54f815ba8.png');
|
||||
}
|
||||
|
||||
.marker-9 {
|
||||
transform: scale(1.5);
|
||||
top: 39%;
|
||||
left: 1003px;
|
||||
background-image: url('./icon/b257f08f180fba833e8302c54f815ba8.png');
|
||||
}
|
||||
.marker-17 {
|
||||
transform: scale(1.5);
|
||||
top: 69%;
|
||||
left: 230px;
|
||||
background-image: url('./icon/2729bbc4b984dec97dbb2d97ceb39ae6.png');
|
||||
}
|
||||
.chart-container {
|
||||
padding: 15px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.chart-title {
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
color: #ffffff;
|
||||
margin-bottom: 10px;
|
||||
flex-shrink: 0;
|
||||
margin-top: -10px;
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
.chart {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.maintenance-panel {
|
||||
background: url('./icon/f0f06ffbc9509f233fe5b4e670416c31.png') no-repeat center;
|
||||
background-size: 100% 100%;
|
||||
padding: 15px;
|
||||
display: grid;
|
||||
grid-template-columns: 2fr 3fr;
|
||||
gap: 10px;
|
||||
overflow: hidden;
|
||||
|
||||
}
|
||||
</style>
|
||||