合并
This commit is contained in:
BIN
public/assets/demo/czzl.png
Normal file
BIN
public/assets/demo/czzl.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
BIN
public/assets/demo/dayImg.png
Normal file
BIN
public/assets/demo/dayImg.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 136 KiB |
BIN
public/assets/demo/glsc.png
Normal file
BIN
public/assets/demo/glsc.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.2 KiB |
BIN
public/assets/demo/sjjk.png
Normal file
BIN
public/assets/demo/sjjk.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.0 KiB |
BIN
public/xx.xlsx
BIN
public/xx.xlsx
Binary file not shown.
BIN
public/供应商导入模板(1).xlsx
Normal file
BIN
public/供应商导入模板(1).xlsx
Normal file
Binary file not shown.
@ -64,3 +64,26 @@ export const delSafetyInspection = (id: string | number | Array<string | number>
|
||||
data: { ids: id }
|
||||
});
|
||||
};
|
||||
/**
|
||||
*
|
||||
* 新增安全巡检工单复查
|
||||
* @param data
|
||||
*/
|
||||
export const addSafetyInspectionReview = (data: any) => {
|
||||
return request({
|
||||
url: '/safety/safetyInspection/review',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
}
|
||||
/**
|
||||
* 新增安全巡检工单整改
|
||||
* @param data
|
||||
*/
|
||||
export const addSafetyInspectionRectification = (data: any) => {
|
||||
return request({
|
||||
url: '/safety/safetyInspection/rectification',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
@ -1,4 +1,9 @@
|
||||
export interface SafetyInspectionVO {
|
||||
|
||||
/**
|
||||
* 整改单位名称
|
||||
*/
|
||||
rectificationName: string;
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
|
||||
@ -66,3 +66,18 @@ export const delTeamMeeting = (id: string | number | Array<string | number>) =>
|
||||
}
|
||||
});
|
||||
};
|
||||
// 查询项目班组下的成员列表
|
||||
export const listProjectTeamMember = (data: any) => {
|
||||
return request({
|
||||
url: '/project/projectTeamMember/list',
|
||||
method: 'get',
|
||||
data
|
||||
});
|
||||
};
|
||||
// 根据 id 查询项目以及项目下的分包公司、班组列表
|
||||
export const getProjectContractorTeamList = (id: string | number) => {
|
||||
return request({
|
||||
url: '/project/project/contractorTeamList/' + id,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
@ -58,6 +58,10 @@ export interface TeamMeetingVO {
|
||||
}
|
||||
|
||||
export interface TeamMeetingForm extends BaseEntity {
|
||||
/**
|
||||
* 班会主题
|
||||
*/
|
||||
meetingTheme?: string;
|
||||
/**
|
||||
* 主键id
|
||||
*/
|
||||
|
||||
@ -10,7 +10,6 @@ import CryptoJS from 'crypto-js';
|
||||
*/
|
||||
|
||||
const BASE_GO_URL = import.meta.env.VITE_APP_BASE_API_GO;
|
||||
const token = $cache.local.get('goToken');
|
||||
|
||||
interface RequestGo extends Function {
|
||||
(config: any): Promise<any>;
|
||||
@ -70,12 +69,12 @@ const exceptionStr = '/api/v1/test/'; // /api/v1/test/*接口拦截
|
||||
service.interceptors.request.use(
|
||||
(config: any) => {
|
||||
// 在发送请求之前做些什么 token
|
||||
|
||||
const token = $cache.local.get('goToken');
|
||||
if (token) {
|
||||
config.headers = config.headers || {};
|
||||
(config.headers as any)['Authorization'] = `Bearer ${token}`;
|
||||
console.log('🚀 ~ config.headers:', config.headers);
|
||||
}
|
||||
|
||||
const stores = useUserStore();
|
||||
if (!whiteUrl.includes(config.url) && !config.url.includes(exceptionStr)) {
|
||||
if (config.params && Reflect.has(config.params, 'projectId')) {
|
||||
|
||||
@ -1,10 +1,8 @@
|
||||
<template>
|
||||
<el-row>
|
||||
<el-row style="padding: 0 20px;">
|
||||
<el-col>
|
||||
<div
|
||||
style="color: rgba(0, 30, 59, 1); font-family: 'Alibaba-PuHuiTi-Bold'; margin: 10px 0 0 0"
|
||||
:style="{ fontSize: fontLevelMap[props.fontLevel] }"
|
||||
>
|
||||
<div style="color: rgba(0, 30, 59, 1); font-family: 'Alibaba-PuHuiTi-Bold'; margin: 10px 0 0 0"
|
||||
:style="{ fontSize: fontLevelMap[props.fontLevel] }">
|
||||
{{ props.title }}
|
||||
</div>
|
||||
</el-col>
|
||||
|
||||
38
src/views/demo1/components/RightTitle.vue
Normal file
38
src/views/demo1/components/RightTitle.vue
Normal file
@ -0,0 +1,38 @@
|
||||
<template>
|
||||
<div style="display: flex;align-items: center;">
|
||||
<div class="icon">
|
||||
<img :src="`/assets/demo/${icon}.png`" alt="图片" />
|
||||
</div>
|
||||
<div class="subtitle">
|
||||
{{ subtitle }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
const { subtitle, icon } = defineProps({
|
||||
subtitle: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
icon: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<style scoped>
|
||||
.subtitle {
|
||||
font-family: Alibaba-PuHuiTi-Bold;
|
||||
font-size: 18px;
|
||||
font-weight: 400;
|
||||
letter-spacing: 0px;
|
||||
line-height: 21.6px;
|
||||
color: rgba(0, 30, 59, 1);
|
||||
text-align: left;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 30px;
|
||||
}
|
||||
</style>
|
||||
226
src/views/demo1/components/czzljl.vue
Normal file
226
src/views/demo1/components/czzljl.vue
Normal file
@ -0,0 +1,226 @@
|
||||
<template>
|
||||
<div class="operation-log-container">
|
||||
<!-- 页面标题 -->
|
||||
<div class="page-title">
|
||||
<el-icon class="title-icon">
|
||||
<DocumentChecked />
|
||||
</el-icon>
|
||||
<h1>操作指令记录</h1>
|
||||
</div>
|
||||
|
||||
<!-- 搜索和操作区域 -->
|
||||
<div class="search-bar">
|
||||
<el-select v-model="selectedCommandType" placeholder="输入操作指令..." class="search-select" clearable>
|
||||
<el-option v-for="type in commandTypes" :key="type.value" :label="type.label" :value="type.value" />
|
||||
</el-select>
|
||||
|
||||
<el-button type="primary" class="send-btn">
|
||||
<el-icon>
|
||||
<Paperclip />
|
||||
</el-icon>
|
||||
发送
|
||||
</el-button>
|
||||
|
||||
<el-button type="primary" :plain="true" class="export-btn">
|
||||
<el-icon>
|
||||
<Download />
|
||||
</el-icon>
|
||||
导出
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<!-- 表格区域 -->
|
||||
<el-table :data="tableData" border stripe class="log-table">
|
||||
<el-table-column type="selection" width="50" />
|
||||
|
||||
<el-table-column prop="commandType" label="指令类型" width="120" />
|
||||
|
||||
<el-table-column prop="operationContent" label="操作内容" min-width="200" />
|
||||
|
||||
<el-table-column prop="device" label="设备" width="100" />
|
||||
|
||||
<el-table-column prop="status" label="状态" width="120" align="center">
|
||||
<template #default="scope">
|
||||
<el-tag :type="scope.row.status === 'SUCCESS' ? 'success' : 'danger'" effect="light"
|
||||
class="status-tag">
|
||||
<el-icon :class="scope.row.status === 'SUCCESS' ? 'success-icon' : 'error-icon'">
|
||||
<Check v-if="scope.row.status === 'SUCCESS'" />
|
||||
<Close v-else />
|
||||
</el-icon>
|
||||
{{ scope.row.status }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column prop="time" label="时间" width="140" />
|
||||
</el-table>
|
||||
|
||||
<!-- 分页区域 -->
|
||||
<div class="pagination-container">
|
||||
<div class="record-info">
|
||||
显示第1到7条,共54条记录
|
||||
</div>
|
||||
|
||||
<el-pagination v-model:current-page="currentPage" :page-size="pageSize" :page-count="totalPages"
|
||||
layout="prev, pager, next" @current-change="handlePageChange" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import {
|
||||
DocumentChecked,
|
||||
Paperclip,
|
||||
Download,
|
||||
Check,
|
||||
Close
|
||||
} from '@element-plus/icons-vue';
|
||||
|
||||
// 指令类型选项
|
||||
const commandTypes = [
|
||||
{ label: '系统自检', value: 'system_check' },
|
||||
{ label: '闭合馈线开关', value: 'close_feeder_switch' },
|
||||
{ label: '闭合保护开关', value: 'close_protection_switch' },
|
||||
{ label: '断开馈线开关', value: 'open_feeder_switch' },
|
||||
];
|
||||
|
||||
// 选中的指令类型
|
||||
const selectedCommandType = ref('');
|
||||
|
||||
// 表格数据
|
||||
const tableData = ref([
|
||||
{
|
||||
commandType: '系统自检',
|
||||
operationContent: '系统进入正常供电模式.',
|
||||
device: '主系统',
|
||||
status: 'SUCCESS',
|
||||
time: '09:51:24'
|
||||
},
|
||||
{
|
||||
commandType: '闭合馈线开关',
|
||||
operationContent: '馈线开关A已闭合,负载A.',
|
||||
device: '主系统',
|
||||
status: 'SUCCESS',
|
||||
time: '09:41:20'
|
||||
},
|
||||
{
|
||||
commandType: '闭合保护开关',
|
||||
operationContent: '闭合母线开关',
|
||||
device: '主系统',
|
||||
status: 'ERROR',
|
||||
time: '09:21:24'
|
||||
},
|
||||
{
|
||||
commandType: '系统自检',
|
||||
operationContent: '母线电压稳定在110KV',
|
||||
device: '主系统',
|
||||
status: 'SUCCESS',
|
||||
time: '09:11:24'
|
||||
},
|
||||
{
|
||||
commandType: '断开馈线开关',
|
||||
operationContent: '闭合馈线开关',
|
||||
device: '主系统',
|
||||
status: 'SUCCESS',
|
||||
time: '09:02:24'
|
||||
},
|
||||
{
|
||||
commandType: '断开馈线开关',
|
||||
operationContent: '闭合馈线开关',
|
||||
device: '主系统',
|
||||
status: 'SUCCESS',
|
||||
time: '09:02:24'
|
||||
},
|
||||
{
|
||||
commandType: '断开馈线开关',
|
||||
operationContent: '闭合馈线开关',
|
||||
device: '主系统',
|
||||
status: 'SUCCESS',
|
||||
time: '09:02:24'
|
||||
}
|
||||
]);
|
||||
|
||||
// 分页相关
|
||||
const currentPage = ref(3);
|
||||
const pageSize = ref(7);
|
||||
const totalPages = ref(20);
|
||||
|
||||
// 处理页码变化
|
||||
const handlePageChange = (page) => {
|
||||
currentPage.value = page;
|
||||
// 实际应用中这里会根据页码加载对应的数据
|
||||
console.log(`切换到第${page}页`);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.operation-log-container {
|
||||
max-width: 1200px;
|
||||
margin: 20px auto;
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.title-icon {
|
||||
margin-right: 8px;
|
||||
color: #409eff;
|
||||
}
|
||||
|
||||
.search-bar {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
margin-bottom: 20px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.search-select {
|
||||
flex: 0 1 300px;
|
||||
}
|
||||
|
||||
.send-btn,
|
||||
.export-btn {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.log-table {
|
||||
width: 100%;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.status-tag {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.success-icon {
|
||||
color: #67c23a;
|
||||
}
|
||||
|
||||
.error-icon {
|
||||
color: #f56c6c;
|
||||
}
|
||||
|
||||
.pagination-container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
color: #606266;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.record-info {
|
||||
color: #606266;
|
||||
}
|
||||
|
||||
.el-pagination {
|
||||
--el-pagination-active-color: #409eff;
|
||||
}
|
||||
</style>
|
||||
349
src/views/demo1/components/dzt.vue
Normal file
349
src/views/demo1/components/dzt.vue
Normal file
@ -0,0 +1,349 @@
|
||||
<template>
|
||||
<el-card style="width: 100%;">
|
||||
<div class="circuit-container">
|
||||
<!-- 状态指示器 -->
|
||||
<div class="status-indicators">
|
||||
<div class="status-item">
|
||||
<span class="status-dot closed"></span>
|
||||
<span>闭合</span>
|
||||
</div>
|
||||
<div class="status-item">
|
||||
<span class="status-dot open"></span>
|
||||
<span>断开</span>
|
||||
</div>
|
||||
<div class="status-item">
|
||||
<span class="status-dot running"></span>
|
||||
<span>运行中</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 控制按钮 -->
|
||||
<div class="control-buttons">
|
||||
<button v-for="(device, index) in devices" :key="index" @click="toggleDevice(device.id)"
|
||||
:class="{ 'btn-active': device.status === 'closed' }">
|
||||
{{ device.name }}: {{ device.statusText }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- 电路图 SVG -->
|
||||
<svg class="circuit-svg" viewBox="0 0 800 400" xmlns="http://www.w3.org/2000/svg">
|
||||
<!-- 背景网格 -->
|
||||
<pattern id="grid" width="50" height="50" patternUnits="userSpaceOnUse">
|
||||
<path d="M 50 0 L 0 0 0 50" fill="none" stroke="#f0f0f0" stroke-width="1" />
|
||||
</pattern>
|
||||
<rect width="100%" height="100%" fill="url(#grid)" />
|
||||
|
||||
<!-- 主线路径 -->
|
||||
<!-- 发电站到主开关 -->
|
||||
<line x1="100" y1="200" x2="180" y2="200" stroke="#22c55e" stroke-width="4" />
|
||||
|
||||
<!-- 主开关到变压器 -->
|
||||
<line x1="220" y1="200" x2="280" y2="200" stroke="#22c55e" stroke-width="4" />
|
||||
|
||||
<!-- 变压器到母线开关 -->
|
||||
<line x1="360" y1="200" x2="420" y2="200" stroke="#22c55e" stroke-width="4" />
|
||||
|
||||
<!-- 母线开关到分支点 -->
|
||||
<line x1="460" y1="200" x2="500" y2="200" stroke="#22c55e" stroke-width="4" />
|
||||
|
||||
<!-- 分支点到馈线开关A -->
|
||||
<line x1="500" y1="200" x2="500" y2="100" stroke="#22c55e" stroke-width="4" />
|
||||
<line x1="500" y1="100" x2="580" y2="100" stroke="#22c55e" stroke-width="4" />
|
||||
|
||||
<!-- 分支点到馈线开关B -->
|
||||
<line x1="500" y1="200" x2="500" y2="300" stroke="#ef4444" stroke-width="4" stroke-dasharray="5,5" />
|
||||
<line x1="500" y1="300" x2="580" y2="300" stroke="#ef4444" stroke-width="4" stroke-dasharray="5,5" />
|
||||
|
||||
<!-- 馈线开关B到保护开关 -->
|
||||
<line x1="620" y1="300" x2="700" y2="300" stroke="#ef4444" stroke-width="4" stroke-dasharray="5,5" />
|
||||
|
||||
<!-- 发电站 -->
|
||||
<circle cx="100" cy="200" r="30" fill="#3b82f6" stroke="#1e40af" stroke-width="2" />
|
||||
<text x="100" y="205" text-anchor="middle" fill="white" font-weight="bold">发电站</text>
|
||||
|
||||
<!-- 主开关 -->
|
||||
<g :class="{ 'switch-closed': devices[0].status === 'closed' }">
|
||||
<circle cx="200" cy="200" r="15" :fill="devices[0].status === 'closed' ? '#10b981' : '#ef4444'" />
|
||||
<text x="200" y="250" text-anchor="middle" font-size="12">主开关</text>
|
||||
<text x="200" y="270" text-anchor="middle" font-size="10" fill="#666">220kV</text>
|
||||
<text x="200" y="285" text-anchor="middle" font-size="10"
|
||||
:fill="devices[0].status === 'closed' ? '#10b981' : '#ef4444'">
|
||||
{{ devices[0].statusText }}
|
||||
</text>
|
||||
</g>
|
||||
|
||||
<!-- 变压器 -->
|
||||
<g>
|
||||
<rect x="300" y="170" width="60" height="60" fill="white" stroke="#666" stroke-width="2" />
|
||||
<line x1="315" y1="190" x2="345" y2="190" stroke="#666" stroke-width="2" />
|
||||
<line x1="315" y1="210" x2="345" y2="210" stroke="#666" stroke-width="2" />
|
||||
<line x1="315" y1="230" x2="345" y2="230" stroke="#666" stroke-width="2" />
|
||||
<text x="330" y="285" text-anchor="middle" font-size="10" fill="#10b981">已完成</text>
|
||||
</g>
|
||||
|
||||
<!-- 母线开关 -->
|
||||
<g :class="{ 'switch-closed': devices[1].status === 'closed' }">
|
||||
<circle cx="440" cy="200" r="15" :fill="devices[1].status === 'closed' ? '#10b981' : '#ef4444'" />
|
||||
<text x="440" y="250" text-anchor="middle" font-size="12">母线开关</text>
|
||||
<text x="440" y="270" text-anchor="middle" font-size="10" fill="#666">110kV</text>
|
||||
<text x="440" y="285" text-anchor="middle" font-size="10"
|
||||
:fill="devices[1].status === 'closed' ? '#10b981' : '#ef4444'">
|
||||
{{ devices[1].statusText }}
|
||||
</text>
|
||||
</g>
|
||||
|
||||
<!-- 分支点 -->
|
||||
<circle cx="500" cy="200" r="8" fill="#666" />
|
||||
|
||||
<!-- 馈线开关A -->
|
||||
<g :class="{ 'switch-closed': devices[2].status === 'closed' }">
|
||||
<circle cx="600" cy="100" r="15" :fill="devices[2].status === 'closed' ? '#10b981' : '#ef4444'" />
|
||||
<text x="600" y="60" text-anchor="middle" font-size="12">馈线开关A</text>
|
||||
<text x="600" y="80" text-anchor="middle" font-size="10"
|
||||
:fill="devices[2].status === 'closed' ? '#10b981' : '#ef4444'">
|
||||
{{ devices[2].statusText }}
|
||||
</text>
|
||||
</g>
|
||||
|
||||
<!-- 负载A -->
|
||||
<circle cx="680" cy="100" r="20" fill="#3b82f6" stroke="#1e40af" stroke-width="2" />
|
||||
<text x="680" y="105" text-anchor="middle" fill="white" font-size="12">负载A</text>
|
||||
<text x="680" y="135" text-anchor="middle" font-size="10" fill="#10b981">已完成</text>
|
||||
|
||||
<!-- 馈线开关B -->
|
||||
<g :class="{ 'switch-closed': devices[3].status === 'closed' }">
|
||||
<circle cx="600" cy="300" r="15" :fill="devices[3].status === 'closed' ? '#10b981' : '#ef4444'" />
|
||||
<text x="600" y="340" text-anchor="middle" font-size="12">馈线开关B</text>
|
||||
<text x="600" y="360" text-anchor="middle" font-size="10"
|
||||
:fill="devices[3].status === 'closed' ? '#10b981' : '#ef4444'">
|
||||
{{ devices[3].statusText }}
|
||||
</text>
|
||||
</g>
|
||||
|
||||
<!-- 保护开关 -->
|
||||
<g :class="{ 'switch-closed': devices[4].status === 'closed' }">
|
||||
<circle cx="720" cy="300" r="15" :fill="devices[4].status === 'closed' ? '#10b981' : '#ef4444'" />
|
||||
<text x="720" y="340" text-anchor="middle" font-size="12">保护开关</text>
|
||||
<text x="720" y="360" text-anchor="middle" font-size="10"
|
||||
:fill="devices[4].status === 'closed' ? '#10b981' : '#ef4444'">
|
||||
{{ devices[4].statusText }}
|
||||
</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
<!-- WebSocket 连接状态 -->
|
||||
<div class="connection-status">
|
||||
<span>WebSocket 连接状态:</span>
|
||||
<span :class="wsConnected ? 'connected' : 'disconnected'">
|
||||
{{ wsConnected ? '已连接' : '未连接' }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, onUnmounted } from 'vue';
|
||||
|
||||
// 设备状态数据
|
||||
const devices = ref([
|
||||
{ id: 'main-switch', name: '主开关', status: 'closed', statusText: '已完成' },
|
||||
{ id: 'bus-switch', name: '母线开关', status: 'closed', statusText: '已完成' },
|
||||
{ id: 'feeder-a', name: '馈线开关A', status: 'closed', statusText: '已完成' },
|
||||
{ id: 'feeder-b', name: '馈线开关B', status: 'open', statusText: '未完成' },
|
||||
{ id: 'protection-switch', name: '保护开关', status: 'open', statusText: '未完成' }
|
||||
]);
|
||||
|
||||
// WebSocket 连接状态
|
||||
const wsConnected = ref(false);
|
||||
let ws = null;
|
||||
|
||||
// 切换设备状态
|
||||
const toggleDevice = (deviceId) => {
|
||||
const device = devices.value.find(d => d.id === deviceId);
|
||||
if (device) {
|
||||
// 切换状态
|
||||
device.status = device.status === 'closed' ? 'open' : 'closed';
|
||||
device.statusText = device.status === 'closed' ? '已完成' : '未完成';
|
||||
|
||||
// 发送状态到服务器
|
||||
if (ws && ws.readyState === WebSocket.OPEN) {
|
||||
ws.send(JSON.stringify({
|
||||
type: 'status-update',
|
||||
deviceId,
|
||||
status: device.status
|
||||
}));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 连接 WebSocket
|
||||
const connectWebSocket = () => {
|
||||
// 实际应用中替换为真实的 WebSocket 服务器地址
|
||||
const wsUrl = 'ws://localhost:8080/ws/circuit';
|
||||
|
||||
// 关闭现有连接
|
||||
if (ws) {
|
||||
ws.close();
|
||||
}
|
||||
|
||||
// 创建新连接
|
||||
ws = new WebSocket(wsUrl);
|
||||
|
||||
ws.onopen = () => {
|
||||
console.log('WebSocket 连接已建立');
|
||||
wsConnected.value = true;
|
||||
|
||||
// 发送当前状态
|
||||
ws.send(JSON.stringify({
|
||||
type: 'initial-status',
|
||||
status: devices.value.reduce((acc, device) => {
|
||||
acc[device.id] = device.status;
|
||||
return acc;
|
||||
}, {})
|
||||
}));
|
||||
};
|
||||
|
||||
ws.onmessage = (event) => {
|
||||
try {
|
||||
const data = JSON.parse(event.data);
|
||||
console.log('收到消息:', data);
|
||||
|
||||
// 处理状态更新
|
||||
if (data.type === 'status-update' && data.deviceId && data.status) {
|
||||
const device = devices.value.find(d => d.id === data.deviceId);
|
||||
if (device) {
|
||||
device.status = data.status;
|
||||
device.statusText = data.status === 'closed' ? '已完成' : '未完成';
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('解析 WebSocket 消息失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
ws.onclose = () => {
|
||||
console.log('WebSocket 连接已关闭');
|
||||
wsConnected.value = false;
|
||||
|
||||
// 5秒后尝试重连
|
||||
setTimeout(connectWebSocket, 5000);
|
||||
};
|
||||
|
||||
ws.onerror = (error) => {
|
||||
console.error('WebSocket 错误:', error);
|
||||
};
|
||||
};
|
||||
|
||||
// 组件挂载时连接 WebSocket
|
||||
onMounted(() => {
|
||||
connectWebSocket();
|
||||
});
|
||||
|
||||
// 组件卸载时关闭 WebSocket 连接
|
||||
onUnmounted(() => {
|
||||
if (ws) {
|
||||
ws.close();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.circuit-container {
|
||||
font-family: Arial, sans-serif;
|
||||
padding: 20px;
|
||||
max-width: 1000px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.status-indicators {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
margin-bottom: 15px;
|
||||
padding: 10px;
|
||||
background-color: #f9f9f9;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.status-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.status-dot {
|
||||
display: inline-block;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.status-dot.closed {
|
||||
background-color: #10b981;
|
||||
}
|
||||
|
||||
.status-dot.open {
|
||||
background-color: #ef4444;
|
||||
}
|
||||
|
||||
.status-dot.running {
|
||||
background-color: #eab308;
|
||||
}
|
||||
|
||||
.control-buttons {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.control-buttons button {
|
||||
padding: 8px 16px;
|
||||
background-color: #f0f0f0;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.control-buttons button.btn-active {
|
||||
background-color: #10b981;
|
||||
color: white;
|
||||
border-color: #059669;
|
||||
}
|
||||
|
||||
.control-buttons button:hover {
|
||||
background-color: #e0e0e0;
|
||||
}
|
||||
|
||||
.control-buttons button.btn-active:hover {
|
||||
background-color: #059669;
|
||||
}
|
||||
|
||||
.circuit-svg {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 5px;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.connection-status {
|
||||
margin-top: 15px;
|
||||
padding: 10px;
|
||||
background-color: #f9f9f9;
|
||||
border-radius: 5px;
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.connection-status .connected {
|
||||
color: #10b981;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.connection-status .disconnected {
|
||||
color: #ef4444;
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
69
src/views/demo1/components/glsc.vue
Normal file
69
src/views/demo1/components/glsc.vue
Normal file
@ -0,0 +1,69 @@
|
||||
<template>
|
||||
<div>
|
||||
<RightTitle subtitle="功率输出趋势" icon="glsc" />
|
||||
</div>
|
||||
<div ref="chartRef" style="width: 100%; height: 400px;"></div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import * as echarts from 'echarts';
|
||||
import { ref, onMounted } from 'vue';
|
||||
import RightTitle from './RightTitle.vue';
|
||||
// 创建对 chart DOM 的引用
|
||||
const chartRef = ref(null);
|
||||
|
||||
// 初始化图表并设置配置
|
||||
onMounted(() => {
|
||||
const chart = echarts.init(chartRef.value); // 初始化图表
|
||||
|
||||
const option = {
|
||||
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
},
|
||||
legend: {
|
||||
data: ['发电机1', '发电机2'],
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
boundaryGap: false,
|
||||
data: ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10'],
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
min: 10,
|
||||
max: 20,
|
||||
interval: 2,
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '发电机1',
|
||||
type: 'line',
|
||||
data: [14, 16, 14, 15, 16, 14, 16, 15, 16, 17, 16],
|
||||
smooth: true,
|
||||
color: '#3366FF',
|
||||
areaStyle: {
|
||||
opacity: 0.2,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: '发电机2',
|
||||
type: 'line',
|
||||
data: [16, 17, 15, 16, 18, 16, 17, 17, 18, 19, 18],
|
||||
smooth: true,
|
||||
color: '#FFCC00',
|
||||
areaStyle: {
|
||||
opacity: 0.2,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
// 设置图表的配置
|
||||
chart.setOption(option);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* Optional styling */
|
||||
</style>
|
||||
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<el-card>
|
||||
<el-card style="width: 100%;">
|
||||
<div class="container">
|
||||
<div class="item">
|
||||
<div class="top">
|
||||
@ -31,7 +31,7 @@
|
||||
<img src="/assets/demo/sb2.png" alt="">
|
||||
</div>
|
||||
<div class="content">
|
||||
<p class="t1">128小时</p>
|
||||
<p class="t1" style="font-size: 21px;">128小时</p>
|
||||
<p class="t2">运行时长</p>
|
||||
</div>
|
||||
</div>
|
||||
@ -99,9 +99,8 @@
|
||||
<span class="t2">(30 days)</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="back">
|
||||
<span>34522天</span>
|
||||
<span>已安全运行</span>
|
||||
<div class="item day">
|
||||
<span class="text">34522天</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
@ -113,14 +112,40 @@
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.day {
|
||||
flex: 1.5 !important;
|
||||
margin: 0 !important;
|
||||
padding: 0 !important;
|
||||
background-color: transparent !important;
|
||||
background-image: url('/assets/demo/dayImg.png');
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
background-position: 100% 50%;
|
||||
position: relative;
|
||||
width: 120px;
|
||||
|
||||
.text {
|
||||
position: absolute;
|
||||
bottom: 60px;
|
||||
color: rgba(24, 109, 245, 1);
|
||||
font-weight: bold;
|
||||
font-size: 25px;
|
||||
margin-left: 10px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
}
|
||||
|
||||
.item {
|
||||
background-color: rgba(240, 249, 255, 1);
|
||||
padding: 20px 40px 20px 20px;
|
||||
padding: 30px 15px;
|
||||
border-radius: 20px;
|
||||
text-align: center;
|
||||
flex: 1;
|
||||
margin: 75px 0;
|
||||
|
||||
.top {
|
||||
display: flex;
|
||||
@ -177,8 +202,16 @@
|
||||
|
||||
}
|
||||
|
||||
.back {
|
||||
position: relative;
|
||||
|
||||
|
||||
|
||||
:deep(.el-card__body) {
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.box {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
<script setup>
|
||||
|
||||
136
src/views/demo1/components/sjjk.vue
Normal file
136
src/views/demo1/components/sjjk.vue
Normal file
@ -0,0 +1,136 @@
|
||||
<template>
|
||||
<RightTitle subtitle="实时数据监控" icon="sjjk" />
|
||||
|
||||
<div class="sjjk-container">
|
||||
<div class="chart-container">
|
||||
<div id="chart" class="echarts-wrapper" style="width: 800px; height: 200px;"></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import RightTitle from './RightTitle.vue';
|
||||
import * as echarts from 'echarts';
|
||||
import { onMounted } from 'vue';
|
||||
|
||||
onMounted(() => {
|
||||
// 初始化图表
|
||||
const myChart = echarts.init(document.getElementById('chart'));
|
||||
|
||||
// 图表配置项
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'shadow',
|
||||
},
|
||||
},
|
||||
legend: {
|
||||
data: ['电压(kv)', '电流(A)', '功率(MW)'],
|
||||
},
|
||||
grid: {
|
||||
top: '15%',
|
||||
left: '12%',
|
||||
right: '12%',
|
||||
bottom: '20%',
|
||||
containLabel: true,
|
||||
show: false
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: ['发电机1', '发电机2', '发电机3', '发电机4', '发电机5', '发电机6'],
|
||||
axisLabel: {
|
||||
interval: 0,
|
||||
rotate: 45
|
||||
}
|
||||
},
|
||||
yAxis: [
|
||||
{
|
||||
type: 'value',
|
||||
name: '电压(kv)/电流(A)',
|
||||
splitLine: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'value',
|
||||
name: '功率(MW)',
|
||||
splitLine: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
],
|
||||
series: [
|
||||
{
|
||||
name: '电压(kv)',
|
||||
type: 'bar',
|
||||
data: [80, 70, 55, 80, 80, 80],
|
||||
},
|
||||
{
|
||||
name: '电流(A)',
|
||||
type: 'bar',
|
||||
data: [65, 80, 65, 65, 65, 65],
|
||||
},
|
||||
{
|
||||
name: '功率(MW)',
|
||||
type: 'bar',
|
||||
yAxisIndex: 1,
|
||||
data: [58, 80, 80, 58, 58, 58],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
// 渲染图表
|
||||
myChart.setOption(option);
|
||||
|
||||
// 窗口大小变化时,图表自适应
|
||||
window.addEventListener('resize', () => {
|
||||
myChart.resize();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
:deep(.el-card__body) {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.sjjk-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: #ffffff;
|
||||
border-radius: 8px;
|
||||
/* padding: 20px; */
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.chart-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
width: 100%;
|
||||
max-width: 800px;
|
||||
}
|
||||
|
||||
.chart-container {
|
||||
width: 100%;
|
||||
max-width: 800px;
|
||||
height: 400px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.echarts-wrapper {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-height: 400px;
|
||||
border-radius: 4px;
|
||||
/* box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); */
|
||||
}
|
||||
</style>
|
||||
@ -2,10 +2,26 @@
|
||||
<div style="padding: 20px;">
|
||||
<el-row>
|
||||
<el-col :span="15">
|
||||
<TitleComponent title="设备情况" subtitle="电站一次监控数据" />
|
||||
<sbqk />
|
||||
<el-row>
|
||||
<TitleComponent title="设备情况" subtitle="电站一次监控数据" />
|
||||
<sbqk />
|
||||
</el-row>
|
||||
<el-row>
|
||||
<TitleComponent title="电站一次系统图" subtitle="电站一次监控数据" />
|
||||
<dzt />
|
||||
</el-row>
|
||||
<el-row>
|
||||
<TitleComponent title="运行步骤日志" subtitle="实时更新中" />
|
||||
<!-- <sbqk /> -->
|
||||
</el-row>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-col :span="8" :push="1">
|
||||
<TitleComponent title="运行步骤日志" subtitle="实时更新中" />
|
||||
<el-card style="padding: 0;">
|
||||
<sjjk />
|
||||
<glsc />
|
||||
<czzljl />
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
@ -19,5 +35,8 @@
|
||||
<script setup>
|
||||
import TitleComponent from '@/views/demo/components/TitleComponent.vue';
|
||||
import sbqk from './components/sbqk.vue';
|
||||
|
||||
</script>
|
||||
import sjjk from './components/sjjk.vue';
|
||||
import glsc from './components/glsc.vue';
|
||||
import czzljl from './components/czzljl.vue';
|
||||
import dzt from './components/dzt.vue';
|
||||
</script>
|
||||
|
||||
0
src/views/demo3/bar1.vue
Normal file
0
src/views/demo3/bar1.vue
Normal file
351
src/views/demo3/components/box1/qushi.vue
Normal file
351
src/views/demo3/components/box1/qushi.vue
Normal file
@ -0,0 +1,351 @@
|
||||
<template>
|
||||
<div class="chart-container">
|
||||
<!-- 图表标题和时间范围选择器 -->
|
||||
<div class="chart-header">
|
||||
<h2>发电量趋势 (kWh)</h2>
|
||||
<div class="chart-actions">
|
||||
<button @click="timeRange = 'day'" :class="{ active: timeRange === 'day' }">日发电</button>
|
||||
<button @click="timeRange = 'week'" :class="{ active: timeRange === 'week' }">周发电</button>
|
||||
<button @click="timeRange = 'month'" :class="{ active: timeRange === 'month' }">月发电</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 图表内容区域 -->
|
||||
<div ref="chartRef" class="chart-content"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, watch } from 'vue';
|
||||
import * as echarts from 'echarts';
|
||||
|
||||
// 图表DOM引用
|
||||
const chartRef = ref(null);
|
||||
// 时间范围状态
|
||||
const timeRange = ref('day');
|
||||
// 图表实例
|
||||
let chartInstance = null;
|
||||
|
||||
// 定义颜色常量
|
||||
const ACTUAL_COLOR = '#2A82E4';
|
||||
const THEORETICAL_COLOR = '#67C23A';
|
||||
|
||||
// 根据时间范围返回对应的数据
|
||||
const getChartData = () => {
|
||||
if (timeRange.value === 'day') {
|
||||
return {
|
||||
xAxis: ['03-09', '03-10', '03-11', '03-12', '03-13', '03-14', '03-15', '03-16'],
|
||||
actualData: [35, 65, 60, 55, 45, 50, 45, 30],
|
||||
theoreticalData: [30, 70, 85, 80, 65, 60, 65, 95]
|
||||
};
|
||||
} else if (timeRange.value === 'week') {
|
||||
return {
|
||||
xAxis: ['第1周', '第2周', '第3周', '第4周'],
|
||||
actualData: [280, 360, 320, 400],
|
||||
theoreticalData: [300, 400, 350, 450]
|
||||
};
|
||||
} else {
|
||||
// 月数据
|
||||
return {
|
||||
xAxis: ['1月', '2月', '3月', '4月', '5月', '6月'],
|
||||
actualData: [1250, 1420, 1380, 1650, 1520, 1780],
|
||||
theoreticalData: [1350, 1500, 1450, 1700, 1600, 1850]
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
// 初始化图表
|
||||
const initChart = () => {
|
||||
if (chartRef.value && !chartInstance) {
|
||||
chartInstance = echarts.init(chartRef.value);
|
||||
}
|
||||
|
||||
const data = getChartData();
|
||||
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
// 悬停线相关设置
|
||||
axisPointer: {
|
||||
type: 'cross',
|
||||
lineStyle: {
|
||||
color: '#2A82E4',
|
||||
width: 2
|
||||
}
|
||||
},
|
||||
backgroundColor: 'rgba(255, 255, 255, 0.9)',
|
||||
borderColor: '#e8e8e8',
|
||||
borderWidth:2 ,
|
||||
textStyle: {
|
||||
color: '#333'
|
||||
},
|
||||
formatter: function (params) {
|
||||
const date = params[0].axisValue;
|
||||
const actualValue = params[0].value;
|
||||
const theoreticalValue = params[1].value;
|
||||
|
||||
return `
|
||||
<div style="font-weight: bold;">${date}</div>
|
||||
<div style="color: ${ACTUAL_COLOR};">实际发电量: ${actualValue}</div>
|
||||
<div style="color: ${THEORETICAL_COLOR};">理论发电量: ${theoreticalValue}</div>
|
||||
`;
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
right: 'center',
|
||||
data: ['实际发电', '理论发电'],
|
||||
top: 0,
|
||||
itemWidth: 10,
|
||||
itemHeight: 10,
|
||||
icon: 'stack',
|
||||
textStyle: {
|
||||
fontSize: 12,
|
||||
color: '#666'
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '10%',
|
||||
top: '25%',
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
boundaryGap: false,
|
||||
data: data.xAxis,
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: '#dcdfe6'
|
||||
}
|
||||
},
|
||||
axisLabel: {
|
||||
color: '#909399',
|
||||
fontSize: 12
|
||||
},
|
||||
axisTick: {
|
||||
show: false // 去除刻度线
|
||||
},
|
||||
splitLine: {
|
||||
show: true, //显示分割线
|
||||
lineStyle: {
|
||||
type: 'dashed' // 设置分割线为虚线
|
||||
}
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: '#dcdfe6'
|
||||
}
|
||||
},
|
||||
axisLabel: {
|
||||
color: '#909399',
|
||||
fontSize: 12
|
||||
},
|
||||
axisTick: {
|
||||
show: false // 去除刻度线
|
||||
},
|
||||
splitLine: {
|
||||
show: true, //显示分割线
|
||||
lineStyle: {
|
||||
type: 'dashed' // 设置分割线为虚线
|
||||
}
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '实际发电',
|
||||
type: 'line',
|
||||
data: data.actualData,
|
||||
smooth: true,
|
||||
symbol: 'circle',
|
||||
symbolSize: 6,
|
||||
// 开始不显示拐点, 鼠标经过显示
|
||||
showSymbol: false,
|
||||
// 设置拐点颜色以及边框
|
||||
itemStyle: {
|
||||
color: ACTUAL_COLOR
|
||||
},
|
||||
lineStyle: {
|
||||
color: ACTUAL_COLOR,
|
||||
width: 3
|
||||
},
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
color: '#2A82E4',
|
||||
borderColor: '#fff',
|
||||
borderWidth: 3
|
||||
}
|
||||
},
|
||||
areaStyle: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{
|
||||
offset: 0,
|
||||
color: `rgba(${parseInt(ACTUAL_COLOR.slice(1, 3), 16)}, ${parseInt(ACTUAL_COLOR.slice(3, 5), 16)}, ${parseInt(ACTUAL_COLOR.slice(5, 7), 16)}, 0.2)`
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: `rgba(${parseInt(ACTUAL_COLOR.slice(1, 3), 16)}, ${parseInt(ACTUAL_COLOR.slice(3, 5), 16)}, ${parseInt(ACTUAL_COLOR.slice(5, 7), 16)}, 0.05)`
|
||||
}
|
||||
])
|
||||
}
|
||||
},
|
||||
{
|
||||
name: '理论发电',
|
||||
type: 'line',
|
||||
data: data.theoreticalData,
|
||||
smooth: true,
|
||||
symbol: 'circle',
|
||||
symbolSize: 6,
|
||||
itemStyle: {
|
||||
color: THEORETICAL_COLOR
|
||||
},
|
||||
lineStyle: {
|
||||
color: THEORETICAL_COLOR,
|
||||
width: 3
|
||||
},
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
color: '#67C23A',
|
||||
borderColor: '#fff',
|
||||
borderWidth: 3
|
||||
}
|
||||
},
|
||||
// 开始不显示拐点, 鼠标经过显示
|
||||
showSymbol: false,
|
||||
areaStyle: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{
|
||||
offset: 0,
|
||||
color: `rgba(${parseInt(THEORETICAL_COLOR.slice(1, 3), 16)}, ${parseInt(THEORETICAL_COLOR.slice(3, 5), 16)}, ${parseInt(THEORETICAL_COLOR.slice(5, 7), 16)}, 0.2)`
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: `rgba(${parseInt(THEORETICAL_COLOR.slice(1, 3), 16)}, ${parseInt(THEORETICAL_COLOR.slice(3, 5), 16)}, ${parseInt(THEORETICAL_COLOR.slice(5, 7), 16)}, 0.05)`
|
||||
}
|
||||
])
|
||||
}
|
||||
},
|
||||
]
|
||||
};
|
||||
|
||||
chartInstance.setOption(option);
|
||||
};
|
||||
|
||||
// 响应窗口大小变化
|
||||
const handleResize = () => {
|
||||
if (chartInstance) {
|
||||
chartInstance.resize();
|
||||
}
|
||||
};
|
||||
|
||||
// 生命周期钩子
|
||||
onMounted(() => {
|
||||
initChart();
|
||||
window.addEventListener('resize', handleResize);
|
||||
|
||||
// 清理函数
|
||||
return () => {
|
||||
window.removeEventListener('resize', handleResize);
|
||||
if (chartInstance) {
|
||||
chartInstance.dispose();
|
||||
chartInstance = null;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
// 监听时间范围变化,更新图表
|
||||
watch(timeRange, () => {
|
||||
initChart();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.chart-container {
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
height: 400px;
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.chart-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 15px 20px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.chart-header h2 {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.chart-actions button {
|
||||
background: none;
|
||||
border: 1px solid #e0e0e0;
|
||||
padding: 5px 12px;
|
||||
border-radius: 4px;
|
||||
margin-left: 8px;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.chart-actions button.active {
|
||||
background-color: #1890ff;
|
||||
color: white;
|
||||
border-color: #1890ff;
|
||||
}
|
||||
|
||||
.chart-content {
|
||||
width: 100%;
|
||||
height: calc(100% - 54px);
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.chart-container {
|
||||
height: 350px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.chart-container {
|
||||
height: 300px;
|
||||
}
|
||||
|
||||
.chart-header {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.chart-actions {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.chart-actions button {
|
||||
margin: 0;
|
||||
flex: 1;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.chart-actions button:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.model {
|
||||
padding: 20px;
|
||||
background-color: rgba(242, 248, 252, 1);
|
||||
}
|
||||
</style>
|
||||
225
src/views/demo3/components/box1/statusBox.vue
Normal file
225
src/views/demo3/components/box1/statusBox.vue
Normal file
File diff suppressed because one or more lines are too long
205
src/views/demo3/components/box2/bar.vue
Normal file
205
src/views/demo3/components/box2/bar.vue
Normal file
@ -0,0 +1,205 @@
|
||||
<template>
|
||||
<div class="chart-container">
|
||||
<div class="chart-header">
|
||||
<h2>发电量预测对比</h2>
|
||||
<p>实际发电量与预测值偏差在u±5%以内,处于合理范围,预测模型准确度较高。</p>
|
||||
</div>
|
||||
<div class="chart-content" ref="chartRef"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onUnmounted, nextTick } from 'vue';
|
||||
import * as echarts from 'echarts';
|
||||
|
||||
const chartRef = ref<HTMLDivElement>();
|
||||
let chartInstance: echarts.ECharts | null = null;
|
||||
|
||||
// 初始化图表
|
||||
const initChart = async () => {
|
||||
await nextTick();
|
||||
if (!chartRef.value) return;
|
||||
|
||||
// 销毁已存在的图表实例
|
||||
if (chartInstance) {
|
||||
chartInstance.dispose();
|
||||
}
|
||||
|
||||
// 创建新的图表实例
|
||||
chartInstance = echarts.init(chartRef.value);
|
||||
|
||||
// 图表配置
|
||||
const option = {
|
||||
tooltip: {
|
||||
backgroundColor: 'rgba(13, 64, 71, 0.50)',
|
||||
borderColor: 'rgba(143, 225, 252, 0.60)',
|
||||
textStyle: {
|
||||
color: '#fff'
|
||||
},
|
||||
padding: 10,
|
||||
borderRadius: 4,
|
||||
},
|
||||
legend: {
|
||||
data: ['实际发电量', '预测发电量'],
|
||||
itemWidth: 14,
|
||||
itemHeight: 10,
|
||||
itemGap: 4,
|
||||
textStyle: {
|
||||
fontSize: 12,
|
||||
color: '#666'
|
||||
}
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: ['上周', '本周'],
|
||||
axisPointer: {
|
||||
type: 'shadow'
|
||||
},
|
||||
axisLabel: {
|
||||
textStyle: {
|
||||
color: '#333',
|
||||
fontSize: 12
|
||||
}
|
||||
},
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: '#ccc'
|
||||
}
|
||||
},
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
min: 0,
|
||||
max: 10000,
|
||||
interval: 5000,
|
||||
axisLine: {
|
||||
show: false
|
||||
},
|
||||
axisTick: {
|
||||
show: false
|
||||
},
|
||||
splitLine: {
|
||||
lineStyle: {
|
||||
color: '#f0f0f0',
|
||||
type: 'dashed'
|
||||
}
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '实际发电量',
|
||||
type: 'bar',
|
||||
data: [4899, 5200],
|
||||
itemStyle: {
|
||||
color: '#1a77dd',
|
||||
borderRadius: [8, 8, 0, 0] // 设置圆角,上左、上右、下右、下左
|
||||
},
|
||||
barWidth: 60
|
||||
},
|
||||
{
|
||||
name: '预测发电量',
|
||||
type: 'bar',
|
||||
data: [5000, 5300],
|
||||
itemStyle: {
|
||||
color: '#7c3aed',
|
||||
borderRadius: [8, 8, 0, 0] // 设置圆角,上左、上右、下右、下左
|
||||
},
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
color: '#6d28d9' // 悬停时的颜色
|
||||
}
|
||||
},
|
||||
barWidth: 60
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
// 设置图表配置
|
||||
chartInstance.setOption(option);
|
||||
};
|
||||
|
||||
// 窗口大小改变时重新调整图表
|
||||
const handleResize = () => {
|
||||
if (chartInstance) {
|
||||
chartInstance.resize();
|
||||
}
|
||||
};
|
||||
|
||||
// 生命周期钩子
|
||||
onMounted(() => {
|
||||
initChart();
|
||||
window.addEventListener('resize', handleResize);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('resize', handleResize);
|
||||
if (chartInstance) {
|
||||
chartInstance.dispose();
|
||||
chartInstance = null;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.chart-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 16px;
|
||||
box-sizing: border-box;
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.chart-header {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.chart-header h2 {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 8px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.chart-header p {
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.chart-content {
|
||||
width: 100%;
|
||||
height: calc(100% - 80px);
|
||||
min-height: 200px;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.chart-container {
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.chart-content {
|
||||
height: calc(100% - 70px);
|
||||
min-height: 280px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.chart-container {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.chart-header h2 {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.chart-header p {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.chart-content {
|
||||
height: calc(100% - 65px);
|
||||
min-height: 250px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
130
src/views/demo3/components/box2/pie.vue
Normal file
130
src/views/demo3/components/box2/pie.vue
Normal file
@ -0,0 +1,130 @@
|
||||
<template>
|
||||
<div class="performance-pie-container">
|
||||
<h2>性能比分析</h2>
|
||||
<p>当前系统性能比为82.5%,高于同行水平(78%),表明系统运行状态良好。</p>
|
||||
<div id="performancePieChart" ref="chartRef"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted, onUnmounted } from 'vue';
|
||||
import * as echarts from 'echarts';
|
||||
|
||||
const chartRef = ref<HTMLElement>();
|
||||
let chartInstance: echarts.ECharts | null = null;
|
||||
|
||||
const initChart = () => {
|
||||
if (chartRef.value) {
|
||||
chartInstance = echarts.init(chartRef.value);
|
||||
|
||||
const option = {
|
||||
series: [
|
||||
{
|
||||
type: 'gauge',
|
||||
startAngle: 180,
|
||||
endAngle: 0,
|
||||
min: 0,
|
||||
max: 100,
|
||||
splitNumber: 10,
|
||||
radius: '100%',
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
width: 20,
|
||||
color: [
|
||||
[0.78, '#2f4554'], // 同行水平78%
|
||||
[0.825, '#1890ff'], // 当前性能比82.5%
|
||||
[1, '#52c41a']
|
||||
]
|
||||
}
|
||||
},
|
||||
pointer: {
|
||||
show: false
|
||||
},
|
||||
axisTick: {
|
||||
show: false
|
||||
},
|
||||
splitLine: {
|
||||
show: false
|
||||
},
|
||||
axisLabel: {
|
||||
show: false
|
||||
},
|
||||
detail: {
|
||||
valueAnimation: true,
|
||||
formatter: '{value}%',
|
||||
color: '#000',
|
||||
fontSize: 30,
|
||||
fontWeight: 'bold',
|
||||
offsetCenter: [0, '-5%']
|
||||
},
|
||||
data: [
|
||||
{
|
||||
value: 82.5,
|
||||
name: '性能比'
|
||||
}
|
||||
],
|
||||
// 添加内圆环效果
|
||||
anchor: {
|
||||
show: true,
|
||||
showAbove: true,
|
||||
size: 10,
|
||||
itemStyle: {
|
||||
borderWidth: 10,
|
||||
borderColor: '#fff'
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
chartInstance.setOption(option);
|
||||
}
|
||||
};
|
||||
|
||||
const handleResize = () => {
|
||||
if (chartInstance) {
|
||||
chartInstance.resize();
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
initChart();
|
||||
window.addEventListener('resize', handleResize);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('resize', handleResize);
|
||||
if (chartInstance) {
|
||||
chartInstance.dispose();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.performance-pie-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 10px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.performance-pie-container h2 {
|
||||
margin: 0 0 8px 0;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.performance-pie-container p {
|
||||
margin: 0 0 12px 0;
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
#performancePieChart {
|
||||
width: 100%;
|
||||
height: calc(100% - 60px);
|
||||
min-height: 200px;
|
||||
}
|
||||
</style>
|
||||
316
src/views/demo3/components/line1.vue
Normal file
316
src/views/demo3/components/line1.vue
Normal file
@ -0,0 +1,316 @@
|
||||
<template>
|
||||
<div class="chart-container">
|
||||
<!-- 图表内容区域 -->
|
||||
<div ref="chartRef" class="chart-content"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, watch } from 'vue';
|
||||
import * as echarts from 'echarts';
|
||||
|
||||
// 图表DOM引用
|
||||
const chartRef = ref(null);
|
||||
// 图表实例
|
||||
let chartInstance = null;
|
||||
|
||||
// 定义颜色常量
|
||||
const ACTUAL_COLOR = '#31FBFB';
|
||||
const THEORETICAL_COLOR = '#5158FA';
|
||||
|
||||
|
||||
const dataX = ['00:00', '02:00', '04:00', '06:00', '08:00', '10:00', '12:00', '14:00', '16:00', '18:00', '20:00', '22:00'];
|
||||
const y = [40, 58, 40, 44, 61, 58, 77, 60, 78, 53, 70, 53];
|
||||
const y1 = [50, 48, 44, 62, 41, 78, 57, 70, 68, 93, 60, 73];
|
||||
// 初始化图表
|
||||
const initChart = () => {
|
||||
if (chartRef.value && !chartInstance) {
|
||||
chartInstance = echarts.init(chartRef.value);
|
||||
}
|
||||
|
||||
const option = {
|
||||
title: {
|
||||
text: '逆变器输出与汇流箱电流',
|
||||
top: 5,
|
||||
textStyle: {
|
||||
color: '#111111', // 标题颜色
|
||||
fontWeight: 'bold',
|
||||
fontSize: 16,
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
backgroundColor: 'rgba(33,56,77,1)',
|
||||
borderColor: 'rgba(33,56,77,1)',
|
||||
textStyle: {
|
||||
color: '#fff',
|
||||
fontSize: 14
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
right: 'center',
|
||||
top: 4,
|
||||
itemWidth: 10,
|
||||
itemHeight: 10,
|
||||
itemGap: 25,
|
||||
icon: 'stack',
|
||||
textStyle: {
|
||||
color: '#E1E5E6'
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
top: '30%',
|
||||
right: '4%',
|
||||
bottom: '4%',
|
||||
left: '6%',
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: {
|
||||
data: dataX,
|
||||
type: 'category',
|
||||
boundaryGap: true,
|
||||
axisLabel: {
|
||||
textStyle: {
|
||||
color: '#B5C5D4',
|
||||
fontSize: 12
|
||||
}
|
||||
},
|
||||
axisTick: {
|
||||
show: false // 去除刻度线
|
||||
},
|
||||
},
|
||||
yAxis: [
|
||||
{
|
||||
name: '逆变器输出:KW',
|
||||
position: 'left',
|
||||
type: 'value',
|
||||
axisLabel: {
|
||||
textStyle: {
|
||||
color: '#B5C5D4',
|
||||
fontSize: 12
|
||||
}
|
||||
},
|
||||
axisTick: {
|
||||
show: false // 去除刻度线
|
||||
},
|
||||
splitLine: {
|
||||
show: false,// y轴分割线
|
||||
}
|
||||
},
|
||||
{
|
||||
name: '汇流箱电流',
|
||||
position: 'right',
|
||||
type: 'value',
|
||||
axisLabel: {
|
||||
textStyle: {
|
||||
color: '#B5C5D4',
|
||||
fontSize: 12
|
||||
}
|
||||
},
|
||||
|
||||
axisTick: {
|
||||
show: false // 去除刻度线
|
||||
},
|
||||
|
||||
splitLine: {
|
||||
show: false,// y轴分割线
|
||||
}
|
||||
},
|
||||
],
|
||||
|
||||
|
||||
series: [
|
||||
{
|
||||
name: '总流量',
|
||||
smooth: true,
|
||||
type: 'line',
|
||||
yAxisIndex: 1, // 关联到右侧y轴
|
||||
lineStyle: {
|
||||
width: 4 // 线条宽度,单位是 px
|
||||
},
|
||||
// 填充颜色设置
|
||||
areaStyle: {
|
||||
color: new echarts.graphic.LinearGradient(
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
[
|
||||
{
|
||||
offset: 0,
|
||||
color: 'rgba(253, 190, 93,.4)' // 渐变色的起始颜色
|
||||
},
|
||||
{
|
||||
offset: 0.9,
|
||||
color: 'rgba(253, 190, 93,0)' // 渐变线的结束颜色
|
||||
}
|
||||
],
|
||||
false
|
||||
),
|
||||
shadowColor: 'rgba(0, 0, 0, 0.1)'
|
||||
},
|
||||
// 拐点
|
||||
showSymbol: true,
|
||||
symbolSize: 10,
|
||||
// 设置拐点颜色以及边框
|
||||
itemStyle: {
|
||||
color: 'rgba(253, 190, 93, 1)'
|
||||
},
|
||||
data: y1
|
||||
},
|
||||
{
|
||||
name: '进园',
|
||||
smooth: true,
|
||||
type: 'line',
|
||||
lineStyle: {
|
||||
width: 4 // 线条宽度,单位是 px
|
||||
},
|
||||
// 填充颜色设置
|
||||
areaStyle: {
|
||||
color: new echarts.graphic.LinearGradient(
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
[
|
||||
{
|
||||
offset: 0,
|
||||
color: 'rgba(26, 119, 221,.4)' // 渐变色的起始颜色
|
||||
},
|
||||
{
|
||||
offset: 0.9,
|
||||
color: 'rgba(26, 119, 221,0)' // 渐变线的结束颜色
|
||||
}
|
||||
],
|
||||
false
|
||||
),
|
||||
shadowColor: 'rgba(0, 0, 0, 0.1)'
|
||||
},
|
||||
// 拐点
|
||||
showSymbol: true,
|
||||
symbolSize: 10,
|
||||
// 设置拐点颜色以及边框
|
||||
itemStyle: {
|
||||
color: 'rgba(26, 119, 221, 1)'
|
||||
},
|
||||
data: y
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
chartInstance.setOption(option);
|
||||
};
|
||||
|
||||
// 响应窗口大小变化
|
||||
const handleResize = () => {
|
||||
if (chartInstance) {
|
||||
chartInstance.resize();
|
||||
}
|
||||
};
|
||||
|
||||
// 生命周期钩子
|
||||
onMounted(() => {
|
||||
initChart();
|
||||
window.addEventListener('resize', handleResize);
|
||||
|
||||
// 清理函数
|
||||
return () => {
|
||||
window.removeEventListener('resize', handleResize);
|
||||
if (chartInstance) {
|
||||
chartInstance.dispose();
|
||||
chartInstance = null;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.chart-container {
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
height: 400px;
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.chart-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 15px 20px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.chart-header h2 {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.chart-actions button {
|
||||
background: none;
|
||||
border: 1px solid #e0e0e0;
|
||||
padding: 5px 12px;
|
||||
border-radius: 4px;
|
||||
margin-left: 8px;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.chart-actions button.active {
|
||||
background-color: #1890ff;
|
||||
color: white;
|
||||
border-color: #1890ff;
|
||||
}
|
||||
|
||||
.chart-content {
|
||||
width: 100%;
|
||||
height: calc(100% - 54px);
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.chart-container {
|
||||
height: 350px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.chart-container {
|
||||
height: 300px;
|
||||
}
|
||||
|
||||
.chart-header {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.chart-actions {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.chart-actions button {
|
||||
margin: 0;
|
||||
flex: 1;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.chart-actions button:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.model {
|
||||
padding: 20px;
|
||||
background-color: rgba(242, 248, 252, 1);
|
||||
}
|
||||
</style>
|
||||
0
src/views/demo3/components/line2.vue
Normal file
0
src/views/demo3/components/line2.vue
Normal file
78
src/views/demo3/index.vue
Normal file
78
src/views/demo3/index.vue
Normal file
@ -0,0 +1,78 @@
|
||||
<template>
|
||||
<div class="model">
|
||||
<!-- 标题栏 -->
|
||||
<el-row :gutter="24">
|
||||
<el-col :span="12">
|
||||
<TitleComponent title="运行参数曲线分析" subtitle="实时监控光伏系统关键运行参数,分析性能趋势" />
|
||||
</el-col>
|
||||
<!-- 外层col:控制整体宽度并右对齐,同时作为flex容器 -->
|
||||
<el-col :span="12" style="display: flex; justify-content: flex-end; align-items: center;">
|
||||
<!-- 子col1:输入框容器 -->
|
||||
<el-col :span="4">
|
||||
<el-select placeholder="请选择电站">
|
||||
<el-option label="所有电站" value="all"></el-option>
|
||||
</el-select>
|
||||
</el-col>
|
||||
<!-- 子col2:下拉框容器 -->
|
||||
<el-col :span="4">
|
||||
<el-select placeholder="请选择月份">
|
||||
<el-option label="所有月份" value="all"></el-option>
|
||||
</el-select>
|
||||
</el-col>
|
||||
<el-col :span="4">
|
||||
<el-button type="primary">
|
||||
导出数据
|
||||
<el-icon class="el-icon--right">
|
||||
<UploadFilled />
|
||||
</el-icon>
|
||||
</el-button>
|
||||
</el-col>
|
||||
</el-col>
|
||||
|
||||
|
||||
</el-row>
|
||||
<!-- 图示区域 -->
|
||||
<el-row :gutter="24">
|
||||
<el-col :span="16">
|
||||
<el-card>
|
||||
<statusBox></statusBox>
|
||||
<qushi class="line"></qushi>
|
||||
</el-card>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="8">
|
||||
<el-card>
|
||||
<bar></bar>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="24">
|
||||
<el-col :span="16">
|
||||
<el-card>
|
||||
<line1 class="line"></line1>
|
||||
</el-card>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="8">
|
||||
<el-card>
|
||||
<bar></bar>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import TitleComponent from '../demo/components/TitleComponent.vue';
|
||||
import StatusBox from './components/box1/statusBox.vue'
|
||||
import Qushi from './components/box1/qushi.vue'
|
||||
import Bar from './components/box2/bar.vue'
|
||||
import line1 from './components/line1.vue';
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.model {
|
||||
padding: 20p15px;
|
||||
}
|
||||
</style>
|
||||
@ -163,9 +163,6 @@ const handleTabChange = (tab) => {
|
||||
getVersionNums(false);
|
||||
}
|
||||
};
|
||||
onMounted(async () => {
|
||||
await getVersionNums();
|
||||
});
|
||||
|
||||
// 获取版本号
|
||||
async function getVersionNums(isSheet = true) {
|
||||
@ -173,6 +170,7 @@ async function getVersionNums(isSheet = true) {
|
||||
state.loading.versions = true;
|
||||
state.error = null;
|
||||
|
||||
console.log('🚀 ~ handleSheetName ~ currentProject.value?.id:', currentProject.value?.id);
|
||||
const res = await obtainAllVersionNumbers({
|
||||
projectId: currentProject.value?.id,
|
||||
workOrderType: state.work_order_type,
|
||||
@ -378,6 +376,32 @@ const downloadTemplate = (type) => {
|
||||
alert('下载失败,请重试');
|
||||
}
|
||||
};
|
||||
const resetState = () => {
|
||||
state.tableData = [];
|
||||
state.options = [];
|
||||
state.sheets = [];
|
||||
state.queryForm.versions = '';
|
||||
state.queryForm.sheet = '';
|
||||
state.versionsData = {};
|
||||
state.error = null;
|
||||
isExpandAll.value = true;
|
||||
};
|
||||
onMounted(async () => {
|
||||
await getVersionNums();
|
||||
});
|
||||
//监听项目id刷新数据
|
||||
const listeningProject = watch(
|
||||
() => currentProject.value?.id,
|
||||
(nid, oid) => {
|
||||
resetState();
|
||||
state.queryForm.projectId = nid;
|
||||
getVersionNums();
|
||||
}
|
||||
);
|
||||
|
||||
onUnmounted(() => {
|
||||
listeningProject();
|
||||
});
|
||||
</script>
|
||||
<style>
|
||||
.billof-quantities {
|
||||
|
||||
@ -4,10 +4,11 @@
|
||||
<div class="navigation-tabs">
|
||||
<div class="nav-tab" @click="handleInspection1">待办事项</div>
|
||||
<div class="nav-tab active" @click="handleInspection2">巡检管理</div>
|
||||
<div class="nav-tab">试验管理</div>
|
||||
<div class="nav-tab">报修管理</div>
|
||||
<div class="nav-tab">抢修管理</div>
|
||||
<div class="nav-tab">工单管理</div>
|
||||
<div class="nav-tab" @click="handleInspection3">试验管理</div>
|
||||
<div class="nav-tab" @click="handleInspection4">报修管理</div>
|
||||
<div class="nav-tab" @click="handleInspection5">抢修管理</div>
|
||||
<div class="nav-tab" @click="handleInspection6">工单管理</div>
|
||||
<div class="nav-tab" @click="handleInspection7">运维组织</div>
|
||||
</div>
|
||||
<TitleComponent title="运维巡检管理" subtitle="制定巡检计划,安排巡检任务,跟进巡检记录"></TitleComponent>
|
||||
|
||||
@ -486,6 +487,21 @@ const handleInspection1 = () => {
|
||||
const handleInspection2 = () => {
|
||||
router.push('/rili/InspectionManagement');
|
||||
};
|
||||
const handleInspection3 = () => {
|
||||
router.push('/rili/shiyanguanli');
|
||||
};
|
||||
const handleInspection4 = () => {
|
||||
router.push('/rili/baoxiuguanli');
|
||||
};
|
||||
const handleInspection5 = () => {
|
||||
router.push('/rili/qiangxiuguanli');
|
||||
};
|
||||
const handleInspection6 = () => {
|
||||
router.push('/rili/gongdanliebiao');
|
||||
};
|
||||
const handleInspection7 = () => {
|
||||
router.push('/rili/renyuanzhuangtai');
|
||||
};
|
||||
const handleInspectionManagement1 = () => {
|
||||
router.push('/rili/InspectionManagement');
|
||||
};
|
||||
|
||||
1031
src/views/dhr_demo/banzhuzhuangtai.vue
Normal file
1031
src/views/dhr_demo/banzhuzhuangtai.vue
Normal file
File diff suppressed because it is too large
Load Diff
1165
src/views/dhr_demo/baoxiuguanli.vue
Normal file
1165
src/views/dhr_demo/baoxiuguanli.vue
Normal file
File diff suppressed because it is too large
Load Diff
720
src/views/dhr_demo/baoxiujilu.vue
Normal file
720
src/views/dhr_demo/baoxiujilu.vue
Normal file
@ -0,0 +1,720 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="inspection-tasks">
|
||||
<!-- 导航栏 -->
|
||||
<div class="navigation-tabs">
|
||||
<div class="nav-tab" @click="handleInspection1">待办事项</div>
|
||||
<div class="nav-tab" @click="handleInspection2">巡检管理</div>
|
||||
<div class="nav-tab" @click="handleInspection3">试验管理</div>
|
||||
<div class="nav-tab active" @click="handleInspection4">报修管理</div>
|
||||
<div class="nav-tab" @click="handleInspection5">抢修管理</div>
|
||||
<div class="nav-tab" @click="handleInspection6">工单管理</div>
|
||||
<div class="nav-tab" @click="handleInspection7">运维组织</div>
|
||||
</div>
|
||||
|
||||
<!-- 页面标题 -->
|
||||
<TitleComponent title="报修管理模块" subtitle="创建报修任务,跟进报修记录,管理维修进度"></TitleComponent>
|
||||
|
||||
<!-- 选项卡 -->
|
||||
<div class="tabs-wrapper">
|
||||
<div style="display: flex; align-items: center; gap: 10px">
|
||||
<el-button type="primary" @click="handleInspectionManagement1">报修任务</el-button>
|
||||
<el-button type="primary" @click="handleInspectionManagement2">报修记录</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 筛选栏 -->
|
||||
<div class="filter-bar">
|
||||
<div class="filter-container">
|
||||
<div class="filter-item">
|
||||
<el-select v-model="taskStatus" placeholder="任务状态">
|
||||
<el-option label="待执行" value="pending"></el-option>
|
||||
<el-option label="处理中" value="processing"></el-option>
|
||||
<el-option label="已完成" value="completed"></el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<el-select v-model="priority" placeholder="优先级">
|
||||
<el-option label="高优先级" value="high"></el-option>
|
||||
<el-option label="中优先级" value="medium"></el-option>
|
||||
<el-option label="低优先级" value="low"></el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<el-select v-model="executor" placeholder="处理人员">
|
||||
<el-option label="全部人员" value="all"></el-option>
|
||||
<el-option label="李阳" value="liyang"></el-option>
|
||||
<el-option label="张明" value="zhangming"></el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<el-date-picker
|
||||
v-model="dateRange"
|
||||
type="datetimerange"
|
||||
start-placeholder="开始时间"
|
||||
end-placeholder="结束时间"
|
||||
format="YYYY-MM-DD HH:mm"
|
||||
value-format="YYYY-MM-DD HH:mm"
|
||||
/>
|
||||
</div>
|
||||
<div class="filter-actions">
|
||||
<el-button type="primary" class="search-btn" @click="handleSearch"> 搜索 </el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 统计卡片区域 -->
|
||||
<div class="statistics-container">
|
||||
<div class="stat-card">
|
||||
<div class="stat-info">
|
||||
<p class="stat-label">本月报修数</p>
|
||||
<p class="stat-value">24</p>
|
||||
<p class="stat-trend up">较上月 +4.2%</p>
|
||||
</div>
|
||||
<div class="stat-icon">
|
||||
<i class="el-icon-file-text"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="stat-card">
|
||||
<div class="stat-info">
|
||||
<p class="stat-label">平均处理时长</p>
|
||||
<p class="stat-value">3.5小时</p>
|
||||
<p class="stat-trend down">较上月 -0.6小时</p>
|
||||
</div>
|
||||
<div class="stat-icon">
|
||||
<i class="el-icon-clock"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="stat-card">
|
||||
<div class="stat-info">
|
||||
<p class="stat-label">待处理报修</p>
|
||||
<p class="stat-value">15</p>
|
||||
<p class="stat-trend warning">需及时处理</p>
|
||||
</div>
|
||||
<div class="stat-icon warning">
|
||||
<i class="el-icon-alarm-clock"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="stat-card">
|
||||
<div class="stat-info">
|
||||
<p class="stat-label">完成率</p>
|
||||
<p class="stat-value">92%</p>
|
||||
<p class="stat-trend up">较上月 +2.1%</p>
|
||||
</div>
|
||||
<div class="stat-icon success">
|
||||
<i class="el-icon-check-circle"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 报修记录表格 -->
|
||||
<div class="table-container">
|
||||
<el-table :data="filteredRecords" border style="width: 100%" class="record-table">
|
||||
<el-table-column prop="reportNo" label="报修单号" width="140"></el-table-column>
|
||||
<el-table-column prop="content" label="报修内容"></el-table-column>
|
||||
<el-table-column prop="reporter" label="报修人" width="100"></el-table-column>
|
||||
<el-table-column prop="reportTime" label="报修时间" width="160"></el-table-column>
|
||||
<el-table-column prop="handler" label="处理人员" width="100"></el-table-column>
|
||||
<el-table-column prop="status" label="处理状态" width="100">
|
||||
<template #default="scope">
|
||||
<span :class="`status-tag ${scope.row.statusClass}`">{{ scope.row.status }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="handleTime" label="处理时间" width="160"></el-table-column>
|
||||
<el-table-column prop="result" label="维修结果"></el-table-column>
|
||||
<el-table-column label="操作" width="160">
|
||||
<template #default="scope">
|
||||
<el-button type="text" size="small" class="detail-btn" @click="handleDetail(scope.row)"> 详情 </el-button>
|
||||
<el-button type="text" size="small" :class="scope.row.actionClass" @click="handleAction(scope.row)">
|
||||
{{ scope.row.actionText }}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<!-- 分页区域 -->
|
||||
<div class="pagination-section">
|
||||
<div class="pagination-controls">
|
||||
<el-pagination
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
:current-page="currentPage"
|
||||
:page-sizes="[7, 15, 20, 30]"
|
||||
:page-size="pageSize"
|
||||
layout="prev, pager, next, jumper"
|
||||
:total="total"
|
||||
background
|
||||
>
|
||||
</el-pagination>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue';
|
||||
import router from '@/router';
|
||||
import TitleComponent from '@/views/demo/components/TitleComponent.vue';
|
||||
|
||||
// 筛选条件
|
||||
const taskStatus = ref('');
|
||||
const priority = ref('');
|
||||
const executor = ref('');
|
||||
const dateRange = ref([]);
|
||||
|
||||
// 分页相关
|
||||
const currentPage = ref(3);
|
||||
const pageSize = ref(7);
|
||||
|
||||
// 报修记录数据
|
||||
const repairRecords = ref([
|
||||
{
|
||||
reportNo: 'R-2025-0620-001',
|
||||
content: '服务器A1电源故障',
|
||||
reporter: '张明',
|
||||
reportTime: '2025-06-20 08:30',
|
||||
handler: '李阳',
|
||||
status: '处理中',
|
||||
statusClass: 'processing',
|
||||
handleTime: '--',
|
||||
result: '--',
|
||||
actionText: '跟进',
|
||||
actionClass: 'follow-btn'
|
||||
},
|
||||
{
|
||||
reportNo: 'R-2025-0620-002',
|
||||
content: '测试软件授权过期',
|
||||
reporter: '张明',
|
||||
reportTime: '2025-06-20 09:15',
|
||||
handler: '李阳',
|
||||
status: '已完成',
|
||||
statusClass: 'completed',
|
||||
handleTime: '2025-06-21 14:30',
|
||||
result: '已重新授权',
|
||||
actionText: '评价',
|
||||
actionClass: 'evaluate-btn'
|
||||
},
|
||||
{
|
||||
reportNo: 'R-2025-0620-003',
|
||||
content: '打印机卡纸故障',
|
||||
reporter: '张明',
|
||||
reportTime: '2025-06-20 10:45',
|
||||
handler: '李阳',
|
||||
status: '已完成',
|
||||
statusClass: 'completed',
|
||||
handleTime: '2025-06-21 10:15',
|
||||
result: '已清除卡纸,设备正常',
|
||||
actionText: '评价',
|
||||
actionClass: 'evaluate-btn'
|
||||
},
|
||||
{
|
||||
reportNo: 'R-2025-0620-004',
|
||||
content: '网络交换机端口故障',
|
||||
reporter: '张明',
|
||||
reportTime: '2025-06-20 13:20',
|
||||
handler: '李阳',
|
||||
status: '已完成',
|
||||
statusClass: 'completed',
|
||||
handleTime: '2025-06-20 15:40',
|
||||
result: '已更换端口',
|
||||
actionText: '评价',
|
||||
actionClass: 'evaluate-btn'
|
||||
},
|
||||
{
|
||||
reportNo: 'R-2025-0620-005',
|
||||
content: '服务器A1电源故障',
|
||||
reporter: '张明',
|
||||
reportTime: '2025-06-20 14:50',
|
||||
handler: '李阳',
|
||||
status: '已完成',
|
||||
statusClass: 'completed',
|
||||
handleTime: '2025-06-21 09:30',
|
||||
result: '已重新授权',
|
||||
actionText: '评价',
|
||||
actionClass: 'evaluate-btn'
|
||||
},
|
||||
{
|
||||
reportNo: 'R-2025-0620-006',
|
||||
content: '机房空调温度异常',
|
||||
reporter: '张明',
|
||||
reportTime: '2025-06-20 16:10',
|
||||
handler: '李阳',
|
||||
status: '已完成',
|
||||
statusClass: 'completed',
|
||||
handleTime: '2025-06-20 17:25',
|
||||
result: '已修复,温度正常',
|
||||
actionText: '评价',
|
||||
actionClass: 'evaluate-btn'
|
||||
},
|
||||
{
|
||||
reportNo: 'R-2025-0620-007',
|
||||
content: '监控系统无法连接',
|
||||
reporter: '张明',
|
||||
reportTime: '2025-06-20 18:05',
|
||||
handler: '李阳',
|
||||
status: '已完成',
|
||||
statusClass: 'completed',
|
||||
handleTime: '2025-06-21 08:45',
|
||||
result: '网络配置已重置',
|
||||
actionText: '评价',
|
||||
actionClass: 'evaluate-btn'
|
||||
}
|
||||
]);
|
||||
|
||||
// 总记录数
|
||||
const total = ref(54);
|
||||
|
||||
// 筛选后的记录
|
||||
const filteredRecords = computed(() => {
|
||||
// 实际应用中这里会根据筛选条件过滤数据
|
||||
return repairRecords.value;
|
||||
});
|
||||
|
||||
// 搜索处理
|
||||
const handleSearch = () => {
|
||||
currentPage.value = 1; // 重置到第一页
|
||||
// 实际应用中这里会根据筛选条件过滤数据
|
||||
};
|
||||
|
||||
// 创建任务弹窗相关
|
||||
const createTaskDialogVisible = ref(false);
|
||||
const createTaskForm = ref({
|
||||
taskName: '',
|
||||
repairContent: '',
|
||||
timeRange: [],
|
||||
priority: '',
|
||||
handler: ''
|
||||
});
|
||||
|
||||
const createTaskRules = {
|
||||
taskName: [{ required: true, message: '请输入任务名称', trigger: 'blur' }],
|
||||
repairContent: [{ required: true, message: '请输入报修内容', trigger: 'blur' }],
|
||||
timeRange: [{ required: true, message: '请选择时间范围', trigger: 'change' }]
|
||||
};
|
||||
|
||||
// 保存任务
|
||||
const handleSaveTask = () => {
|
||||
// 模拟保存任务逻辑
|
||||
console.log('保存任务:', createTaskForm.value);
|
||||
// 关闭弹窗
|
||||
createTaskDialogVisible.value = false;
|
||||
// 重置表单
|
||||
createTaskForm.value = {
|
||||
taskName: '',
|
||||
repairContent: '',
|
||||
timeRange: [],
|
||||
priority: '',
|
||||
handler: ''
|
||||
};
|
||||
// 这里可以添加成功提示和刷新任务列表的逻辑
|
||||
};
|
||||
|
||||
// 取消创建任务
|
||||
const handleCancelCreateTask = () => {
|
||||
createTaskDialogVisible.value = false;
|
||||
// 重置表单
|
||||
createTaskForm.value = {
|
||||
taskName: '',
|
||||
repairContent: '',
|
||||
timeRange: [],
|
||||
priority: '',
|
||||
handler: ''
|
||||
};
|
||||
};
|
||||
|
||||
// 分页事件
|
||||
const handleSizeChange = (val) => {
|
||||
pageSize.value = val;
|
||||
currentPage.value = 1;
|
||||
};
|
||||
|
||||
const handleCurrentChange = (val) => {
|
||||
currentPage.value = val;
|
||||
};
|
||||
|
||||
// 查看详情
|
||||
const handleDetail = (record) => {
|
||||
console.log('查看详情:', record);
|
||||
};
|
||||
|
||||
// 处理操作
|
||||
const handleAction = (record) => {
|
||||
console.log('执行操作:', record.actionText, record);
|
||||
};
|
||||
|
||||
// 导航事件
|
||||
const handleInspectionManagement1 = () => {
|
||||
router.push('/rili/baoxiuguanli');
|
||||
};
|
||||
|
||||
const handleInspectionManagement2 = () => {
|
||||
router.push('/rili/baoxiujilu');
|
||||
};
|
||||
|
||||
const handleInspection1 = () => {
|
||||
router.push('/rili/rili');
|
||||
};
|
||||
|
||||
const handleInspection2 = () => {
|
||||
router.push('/rili/InspectionManagement');
|
||||
};
|
||||
|
||||
const handleInspection3 = () => {
|
||||
router.push('/rili/shiyanguanli');
|
||||
};
|
||||
|
||||
const handleInspection4 = () => {
|
||||
router.push('/rili/baoxiuguanli');
|
||||
};
|
||||
const handleInspection5 = () => {
|
||||
router.push('/rili/qiangxiuguanli');
|
||||
};
|
||||
const handleInspection6 = () => {
|
||||
router.push('/rili/gongdanliebiao');
|
||||
};
|
||||
const handleInspection7 = () => {
|
||||
router.push('/rili/renyuanzhuangtai');
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.inspection-tasks {
|
||||
padding: 20px;
|
||||
background-color: #f5f7fa;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
/* 选项卡样式 */
|
||||
.tabs-wrapper {
|
||||
background-color: #fff;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 16px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
/* 筛选栏样式 */
|
||||
.filter-bar {
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 24px;
|
||||
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05);
|
||||
padding: 16px 24px;
|
||||
}
|
||||
|
||||
.filter-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 16px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.filter-item {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.filter-bar .el-select,
|
||||
.filter-bar .el-date-picker {
|
||||
width: 180px;
|
||||
height: 36px;
|
||||
}
|
||||
|
||||
.filter-bar .el-select .el-input__inner,
|
||||
.filter-bar .el-date-picker .el-input__inner {
|
||||
border-radius: 4px;
|
||||
border-color: #dcdfe6;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.filter-bar .el-select .el-input__inner:focus,
|
||||
.filter-bar .el-date-picker .el-input__inner:focus {
|
||||
border-color: #165dff;
|
||||
box-shadow: 0 0 0 2px rgba(22, 93, 255, 0.1);
|
||||
}
|
||||
|
||||
.filter-actions {
|
||||
margin-left: auto;
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.search-btn {
|
||||
background-color: #f2f3f5;
|
||||
color: #303133;
|
||||
border-color: #f2f3f5;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.search-btn:hover {
|
||||
background-color: #e5e6eb;
|
||||
color: #303133;
|
||||
border-color: #e5e6eb;
|
||||
}
|
||||
|
||||
.create-btn {
|
||||
background-color: #165dff;
|
||||
border-color: #165dff;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.create-btn:hover {
|
||||
background-color: #0e42d2;
|
||||
border-color: #0e42d2;
|
||||
}
|
||||
|
||||
/* 统计卡片样式 */
|
||||
.statistics-container {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
|
||||
gap: 20px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||||
padding: 20px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.stat-card:hover {
|
||||
transform: translateY(-3px);
|
||||
}
|
||||
|
||||
.stat-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 14px;
|
||||
color: #86909c;
|
||||
margin: 0 0 8px 0;
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
color: #1d2129;
|
||||
margin: 0 0 4px 0;
|
||||
}
|
||||
|
||||
.stat-trend {
|
||||
font-size: 12px;
|
||||
margin: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.stat-trend.up {
|
||||
color: #00b42a;
|
||||
}
|
||||
|
||||
.stat-trend.up::before {
|
||||
content: '↑';
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.stat-trend.down {
|
||||
color: #ff4d4f;
|
||||
}
|
||||
|
||||
.stat-trend.down::before {
|
||||
content: '↓';
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.stat-trend.warning {
|
||||
color: #fa8c16;
|
||||
}
|
||||
|
||||
.stat-icon {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 50%;
|
||||
background-color: #e8f3ff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #165dff;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.stat-icon.warning {
|
||||
background-color: #fff7e6;
|
||||
color: #fa8c16;
|
||||
}
|
||||
|
||||
.stat-icon.success {
|
||||
background-color: #f6ffed;
|
||||
color: #52c41a;
|
||||
}
|
||||
|
||||
/* 表格样式 */
|
||||
.table-container {
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05);
|
||||
padding: 16px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.record-table {
|
||||
border-collapse: separate;
|
||||
border-spacing: 0;
|
||||
}
|
||||
|
||||
.record-table th {
|
||||
background-color: #f7f8fa;
|
||||
color: #4e5969;
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.record-table td {
|
||||
color: #1d2129;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.status-tag {
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.status-tag.processing {
|
||||
background-color: #fffbe6;
|
||||
color: #faad14;
|
||||
border: 1px solid #fff1b8;
|
||||
}
|
||||
|
||||
.status-tag.completed {
|
||||
background-color: #f0f9eb;
|
||||
color: #52c41a;
|
||||
border: 1px solid #e1f3d8;
|
||||
}
|
||||
|
||||
.detail-btn {
|
||||
color: #165dff;
|
||||
}
|
||||
|
||||
.follow-btn {
|
||||
color: #fa8c16;
|
||||
}
|
||||
|
||||
.evaluate-btn {
|
||||
color: #52c41a;
|
||||
}
|
||||
|
||||
/* 分页区域样式 */
|
||||
.pagination-section {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.pagination-controls .el-pagination {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.pagination-controls .el-pagination button,
|
||||
.pagination-controls .el-pagination .el-pager li {
|
||||
min-width: 36px;
|
||||
height: 36px;
|
||||
line-height: 36px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.pagination-controls .el-pagination .el-pager li.active {
|
||||
background-color: #165dff;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* 导航栏样式 */
|
||||
.navigation-tabs {
|
||||
display: flex;
|
||||
margin-bottom: 20px;
|
||||
background-color: #fff;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08);
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.nav-tab {
|
||||
padding: 12px 24px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
color: #606266;
|
||||
border-right: 1px solid #f0f0f0;
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.nav-tab:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.nav-tab:hover {
|
||||
color: #409eff;
|
||||
background-color: #ecf5ff;
|
||||
}
|
||||
|
||||
.nav-tab.active {
|
||||
background-color: #409eff;
|
||||
color: #fff;
|
||||
box-shadow: 0 2px 4px rgba(64, 158, 255, 0.3);
|
||||
}
|
||||
|
||||
.nav-tab {
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 1200px) {
|
||||
.statistics-container {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.inspection-tasks {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.filter-container {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.filter-actions {
|
||||
margin-left: 0;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.filter-bar .el-select,
|
||||
.filter-bar .el-date-picker {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.statistics-container {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.table-container {
|
||||
overflow-x: auto;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
700
src/views/dhr_demo/cheliangzhuangtai.vue
Normal file
700
src/views/dhr_demo/cheliangzhuangtai.vue
Normal file
@ -0,0 +1,700 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="execution-records">
|
||||
<!-- 顶部导航栏 -->
|
||||
<div class="navigation-tabs">
|
||||
<div class="nav-tab" @click="handleInspection1">待办事项</div>
|
||||
<div class="nav-tab" @click="handleInspection2">巡检管理</div>
|
||||
<div class="nav-tab" @click="handleInspection3">试验管理</div>
|
||||
<div class="nav-tab" @click="handleInspection4">报修管理</div>
|
||||
<div class="nav-tab" @click="handleInspection5">抢修管理</div>
|
||||
<div class="nav-tab" @click="handleInspection6">工单管理</div>
|
||||
<div class="nav-tab active" @click="handleInspection7">运维组织</div>
|
||||
</div>
|
||||
|
||||
<!-- 页面标题 -->
|
||||
<TitleComponent title="运维组织模块" subtitle="实时监控人员状态、车辆状态和班组状态"></TitleComponent>
|
||||
|
||||
<!-- 选项卡 -->
|
||||
<div class="tabs-wrapper">
|
||||
<div style="display: flex; align-items: center; gap: 10px">
|
||||
<el-button type="primary" @click="handleInspectionManagement1">人员状态</el-button>
|
||||
<el-button type="primary" @click="handleInspectionManagement2">车辆状态</el-button>
|
||||
<el-button type="primary" @click="handleInspectionManagement3">班组状态</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 搜索和筛选区 -->
|
||||
<div class="search-filter">
|
||||
<div class="search-container">
|
||||
<!-- 左侧统计数据和车辆图片组合 -->
|
||||
<div class="stats-and-image">
|
||||
<div class="vehicle-stats">
|
||||
<div class="stat-item total">
|
||||
<div class="stat-value">{{ totalVehicles }}</div>
|
||||
<div class="stat-label">总车辆数</div>
|
||||
<div class="stat-desc">所有运维车辆总数</div>
|
||||
</div>
|
||||
<div class="stat-item available">
|
||||
<div class="stat-value">{{ availableVehicles }}</div>
|
||||
<div class="stat-label">可用车辆</div>
|
||||
<div class="stat-desc">可立即调度</div>
|
||||
</div>
|
||||
<div class="stat-item in-use">
|
||||
<div class="stat-value">{{ inUseVehicles }}</div>
|
||||
<div class="stat-label">使用中</div>
|
||||
<div class="stat-desc">执行任务中</div>
|
||||
</div>
|
||||
<div class="stat-item maintenance">
|
||||
<div class="stat-value">{{ maintenanceVehicles }}</div>
|
||||
<div class="stat-label">维修/保养</div>
|
||||
<div class="stat-desc">预计2天后可使用</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 右侧搜索区域 -->
|
||||
<div class="search-integrated">
|
||||
<div class="search-box">
|
||||
<div class="search-label">搜索车牌号</div>
|
||||
<div class="license-plate-input">
|
||||
<span class="plate-prefix">京</span>
|
||||
<span class="plate-dot">·</span>
|
||||
<el-input v-model="plateNumber1" maxlength="1" class="plate-char"></el-input>
|
||||
<el-input v-model="plateNumber2" maxlength="1" class="plate-char"></el-input>
|
||||
<el-input v-model="plateNumber3" maxlength="1" class="plate-char"></el-input>
|
||||
<el-input v-model="plateNumber4" maxlength="1" class="plate-char"></el-input>
|
||||
<el-input v-model="plateNumber5" maxlength="1" class="plate-char"></el-input>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 车辆列表表格 -->
|
||||
<div class="table-wrapper">
|
||||
<el-table :data="pagedTableData" stripe style="width: 100%" highlight-current-row class="custom-table">
|
||||
<el-table-column align="center" prop="vehicleId" label="车辆编号" min-width="120"></el-table-column>
|
||||
<el-table-column align="center" prop="licensePlate" label="车牌号" min-width="120"></el-table-column>
|
||||
<el-table-column align="center" prop="vehicleType" label="车辆类型" min-width="120"></el-table-column>
|
||||
<el-table-column align="center" prop="team" label="所属班组" min-width="120"></el-table-column>
|
||||
<el-table-column align="center" prop="responsiblePerson" label="负责人" min-width="120"></el-table-column>
|
||||
<el-table-column align="center" prop="currentTask" label="当前任务" min-width="150"></el-table-column>
|
||||
<el-table-column align="center" prop="status" label="当前状态" min-width="120">
|
||||
<template #default="scope">
|
||||
<el-tag :type="getStatusTagType(scope.row.status)" class="status-tag">
|
||||
{{ scope.row.status }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" prop="nextMaintenance" label="下次保养" min-width="120"></el-table-column>
|
||||
<el-table-column align="center" label="操作" min-width="180" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-button type="text" @click="handleViewDetails(scope.row)" size="small" class="action-btn">详情</el-button>
|
||||
<el-button
|
||||
type="text"
|
||||
@click="handleDispatch(scope.row)"
|
||||
size="small"
|
||||
class="action-btn dispatch-btn"
|
||||
v-if="scope.row.status === '可用'"
|
||||
>
|
||||
调度
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<!-- 分页区域 -->
|
||||
<div class="pagination-section">
|
||||
<div class="pagination-info">
|
||||
显示第{{ (currentPage - 1) * pageSize + 1 }}到{{ Math.min(currentPage * pageSize, total) }}条,共{{ total }}条记录
|
||||
</div>
|
||||
<div class="pagination-controls">
|
||||
<el-pagination
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
:current-page="currentPage"
|
||||
:page-sizes="[10, 20, 30, 40]"
|
||||
:page-size="pageSize"
|
||||
layout="prev, pager, next, jumper"
|
||||
:total="total"
|
||||
background
|
||||
>
|
||||
</el-pagination>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue';
|
||||
import router from '@/router';
|
||||
import TitleComponent from '@/views/demo/components/TitleComponent.vue';
|
||||
|
||||
// 搜索和筛选条件
|
||||
const searchKeyword = ref('');
|
||||
const plateNumber1 = ref('');
|
||||
const plateNumber2 = ref('');
|
||||
const plateNumber3 = ref('');
|
||||
const plateNumber4 = ref('');
|
||||
const plateNumber5 = ref('');
|
||||
|
||||
// 车辆数据
|
||||
const rawTableData = ref([
|
||||
{
|
||||
vehicleId: 'CAR-001',
|
||||
licensePlate: '京A·12345',
|
||||
vehicleType: '工具车',
|
||||
team: '第一运维组',
|
||||
responsiblePerson: '赵工',
|
||||
currentTask: '无',
|
||||
status: '可用',
|
||||
nextMaintenance: '2023-07-15'
|
||||
},
|
||||
{
|
||||
vehicleId: 'CAR-003',
|
||||
licensePlate: '京B·67890',
|
||||
vehicleType: '小型货车',
|
||||
team: '第二运维组',
|
||||
responsiblePerson: '赵工',
|
||||
currentTask: 'WO-2023-0619-055',
|
||||
status: '使用中',
|
||||
nextMaintenance: '2023-08-20'
|
||||
},
|
||||
{
|
||||
vehicleId: 'CAR-005',
|
||||
licensePlate: '京C·24680',
|
||||
vehicleType: '工程车',
|
||||
team: '第一运维组',
|
||||
responsiblePerson: '赵工',
|
||||
currentTask: 'WO-2023-0618-054',
|
||||
status: '使用中',
|
||||
nextMaintenance: '2023-07-15'
|
||||
},
|
||||
{
|
||||
vehicleId: 'CAR-007',
|
||||
licensePlate: '京D·13579',
|
||||
vehicleType: '面包车',
|
||||
team: '第三运维组',
|
||||
responsiblePerson: '赵工',
|
||||
currentTask: '无',
|
||||
status: '维修中',
|
||||
nextMaintenance: '2023-07-15'
|
||||
},
|
||||
{
|
||||
vehicleId: 'CAR-009',
|
||||
licensePlate: '京E·12345',
|
||||
vehicleType: '工具车',
|
||||
team: '第一运维组',
|
||||
responsiblePerson: '赵工',
|
||||
currentTask: '无',
|
||||
status: '可用',
|
||||
nextMaintenance: '2023-07-15'
|
||||
},
|
||||
{
|
||||
vehicleId: 'CAR-011',
|
||||
licensePlate: '京F·12345',
|
||||
vehicleType: '工具车',
|
||||
team: '第一运维组',
|
||||
responsiblePerson: '赵工',
|
||||
currentTask: '无',
|
||||
status: '可用',
|
||||
nextMaintenance: '2023-07-15'
|
||||
},
|
||||
{
|
||||
vehicleId: 'CAR-013',
|
||||
licensePlate: '京G·12345',
|
||||
vehicleType: '工具车',
|
||||
team: '第一运维组',
|
||||
responsiblePerson: '赵工',
|
||||
currentTask: '无',
|
||||
status: '可用',
|
||||
nextMaintenance: '2023-07-15'
|
||||
}
|
||||
]);
|
||||
|
||||
// 分页相关
|
||||
const currentPage = ref(1);
|
||||
const pageSize = ref(10);
|
||||
const total = ref(rawTableData.value.length);
|
||||
|
||||
// 统计数据
|
||||
const totalVehicles = computed(() => rawTableData.value.length);
|
||||
const availableVehicles = computed(() => rawTableData.value.filter((v) => v.status === '可用').length);
|
||||
const inUseVehicles = computed(() => rawTableData.value.filter((v) => v.status === '使用中').length);
|
||||
const maintenanceVehicles = computed(() => rawTableData.value.filter((v) => v.status === '维修中').length);
|
||||
|
||||
// 分页处理后的数据
|
||||
const pagedTableData = computed(() => {
|
||||
// 筛选逻辑
|
||||
let filteredData = [...rawTableData.value];
|
||||
|
||||
// 搜索关键词筛选
|
||||
if (searchKeyword.value) {
|
||||
const keyword = searchKeyword.value.toLowerCase();
|
||||
filteredData = filteredData.filter(
|
||||
(item) =>
|
||||
item.vehicleId.toLowerCase().includes(keyword) ||
|
||||
item.licensePlate.toLowerCase().includes(keyword) ||
|
||||
item.vehicleType.toLowerCase().includes(keyword)
|
||||
);
|
||||
}
|
||||
|
||||
// 车牌号筛选
|
||||
const plateNumber = plateNumber1.value + plateNumber2.value + plateNumber3.value + plateNumber4.value + plateNumber5.value;
|
||||
if (plateNumber) {
|
||||
filteredData = filteredData.filter((item) => item.licensePlate.replace(/[·\s]/g, '').includes(plateNumber));
|
||||
}
|
||||
|
||||
// 更新总条数
|
||||
total.value = filteredData.length;
|
||||
|
||||
// 分页处理
|
||||
const startIndex = (currentPage.value - 1) * pageSize.value;
|
||||
const endIndex = startIndex + pageSize.value;
|
||||
return filteredData.slice(startIndex, endIndex);
|
||||
});
|
||||
|
||||
// 获取状态标签样式
|
||||
const getStatusTagType = (status) => {
|
||||
const statusMap = {
|
||||
'可用': 'success',
|
||||
'使用中': 'primary',
|
||||
'维修中': 'warning'
|
||||
};
|
||||
return statusMap[status] || 'default';
|
||||
};
|
||||
|
||||
// 搜索处理
|
||||
const handleSearch = () => {
|
||||
currentPage.value = 1; // 重置到第一页
|
||||
};
|
||||
|
||||
// 分页事件
|
||||
const handleSizeChange = (val) => {
|
||||
pageSize.value = val;
|
||||
currentPage.value = 1;
|
||||
};
|
||||
|
||||
const handleCurrentChange = (val) => {
|
||||
currentPage.value = val;
|
||||
};
|
||||
|
||||
// 操作按钮事件
|
||||
const handleViewDetails = (row) => {
|
||||
console.log('查看详情:', row);
|
||||
// 实际应用中这里会跳转到详情页
|
||||
};
|
||||
|
||||
const handleDispatch = (row) => {
|
||||
console.log('调度车辆:', row);
|
||||
// 实际应用中这里会打开调度表单
|
||||
};
|
||||
|
||||
// 导航路由跳转
|
||||
const handleInspection1 = () => {
|
||||
router.push('/rili/rili');
|
||||
};
|
||||
const handleInspection2 = () => {
|
||||
router.push('/rili/InspectionManagement');
|
||||
};
|
||||
const handleInspection3 = () => {
|
||||
router.push('/rili/shiyanguanli');
|
||||
};
|
||||
const handleInspection4 = () => {
|
||||
router.push('/rili/baoxiuguanli');
|
||||
};
|
||||
const handleInspection5 = () => {
|
||||
router.push('/rili/qiangxiuguanli');
|
||||
};
|
||||
const handleInspection6 = () => {
|
||||
router.push('/rili/gongdanliebiao');
|
||||
};
|
||||
const handleInspection7 = () => {
|
||||
router.push('/rili/renyuanzhuangtai');
|
||||
};
|
||||
const handleInspectionManagement1 = () => {
|
||||
router.push('/rili/renyuanzhuangtai');
|
||||
};
|
||||
const handleInspectionManagement2 = () => {
|
||||
router.push('/rili/cheliangzhuangtai');
|
||||
};
|
||||
const handleInspectionManagement3 = () => {
|
||||
router.push('/rili/banzhuzhuangtai');
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.execution-records {
|
||||
padding: 20px;
|
||||
background-color: #f5f7fa;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
/* 选项卡样式 */
|
||||
.tabs-wrapper {
|
||||
background-color: #fff;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 16px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
/* 搜索和筛选区样式 */
|
||||
.search-filter {
|
||||
border-radius: 12px;
|
||||
margin-bottom: 24px;
|
||||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
|
||||
overflow: hidden;
|
||||
transition: box-shadow 0.3s ease;
|
||||
background: linear-gradient(135deg, #f0f7ff 0%, #f5f7fa 100%);
|
||||
}
|
||||
|
||||
.search-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 24px 32px;
|
||||
min-height: 140px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* 统计数据和车辆图片组合容器 */
|
||||
.stats-and-image {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 40px;
|
||||
flex: 1;
|
||||
max-width: calc(100% - 350px);
|
||||
}
|
||||
|
||||
/* 车辆统计区域 */
|
||||
.vehicle-stats {
|
||||
display: flex;
|
||||
gap: 28px;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
/* 搜索区域 */
|
||||
.search-integrated {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
width: 320px;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.search-box {
|
||||
background-color: rgba(255, 255, 255, 0.95);
|
||||
border-radius: 12px;
|
||||
padding: 20px 24px;
|
||||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
|
||||
backdrop-filter: blur(10px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
.search-label {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #2c3e50;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.stat-item {
|
||||
background-color: rgba(255, 255, 255, 0.95);
|
||||
border-radius: 12px;
|
||||
padding: 20px 28px;
|
||||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
min-width: 160px;
|
||||
backdrop-filter: blur(10px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
.stat-item:hover {
|
||||
transform: translateY(-5px) scale(1.02);
|
||||
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
|
||||
border-color: rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
font-size: 32px;
|
||||
font-weight: 700;
|
||||
margin-bottom: 6px;
|
||||
line-height: 1.2;
|
||||
letter-spacing: -0.5px;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 4px;
|
||||
color: #2c3e50;
|
||||
}
|
||||
|
||||
.stat-desc {
|
||||
font-size: 13px;
|
||||
color: #606266;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.stat-item.total .stat-value {
|
||||
color: #303133;
|
||||
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.stat-item.available .stat-value {
|
||||
color: #52c41a;
|
||||
text-shadow: 0 2px 4px rgba(82, 196, 26, 0.2);
|
||||
}
|
||||
|
||||
.stat-item.in-use .stat-value {
|
||||
color: #fa8c16;
|
||||
text-shadow: 0 2px 4px rgba(250, 140, 22, 0.2);
|
||||
}
|
||||
|
||||
.stat-item.maintenance .stat-value {
|
||||
color: #faad14;
|
||||
text-shadow: 0 2px 4px rgba(250, 173, 20, 0.2);
|
||||
}
|
||||
|
||||
/* 车牌号输入样式 */
|
||||
.license-plate-input {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background-color: #fff;
|
||||
border: 2px solid #e6e6e6;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.license-plate-input:hover {
|
||||
border-color: #c0c4cc;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.plate-prefix {
|
||||
background-color: #f5f7fa;
|
||||
padding: 0 12px;
|
||||
height: 42px;
|
||||
line-height: 42px;
|
||||
border-right: 1px solid #dcdfe6;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.plate-dot {
|
||||
padding: 0 6px;
|
||||
height: 42px;
|
||||
line-height: 42px;
|
||||
color: #606266;
|
||||
font-size: 24px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.plate-char {
|
||||
width: 32px !important;
|
||||
height: 42px !important;
|
||||
text-align: center;
|
||||
border: none;
|
||||
border-right: 1px solid #f0f0f0;
|
||||
background-color: #fff;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.plate-char:focus {
|
||||
background-color: #f0f7ff;
|
||||
color: #409eff;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.plate-char:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
/* 表格样式优化 */
|
||||
.table-wrapper {
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 24px;
|
||||
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* 状态标签样式 */
|
||||
.status-tag {
|
||||
padding: 4px 10px;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* 操作按钮样式优化 */
|
||||
.action-btn {
|
||||
color: #165dff;
|
||||
font-size: 13px;
|
||||
padding: 4px 10px;
|
||||
margin: 0 3px;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.action-btn:hover {
|
||||
color: #094ab2;
|
||||
background-color: #e6f7ff;
|
||||
}
|
||||
|
||||
.dispatch-btn {
|
||||
color: #52c41a;
|
||||
}
|
||||
|
||||
.dispatch-btn:hover {
|
||||
color: #389e0d;
|
||||
background-color: #f6ffed;
|
||||
}
|
||||
|
||||
/* 分页区域样式 */
|
||||
.pagination-section {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
background-color: #fff;
|
||||
padding: 16px 24px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.pagination-info {
|
||||
font-size: 14px;
|
||||
color: #606266;
|
||||
}
|
||||
|
||||
.pagination-controls .el-pagination {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.pagination-controls .el-pagination button,
|
||||
.pagination-controls .el-pagination .el-pager li {
|
||||
min-width: 36px;
|
||||
height: 36px;
|
||||
line-height: 36px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.pagination-controls .el-pagination .el-pager li.active {
|
||||
background-color: #165dff;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* 导航栏样式 */
|
||||
.navigation-tabs {
|
||||
display: flex;
|
||||
margin-bottom: 20px;
|
||||
background-color: #fff;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08);
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.nav-tab {
|
||||
padding: 12px 24px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
color: #606266;
|
||||
border-right: 1px solid #f0f0f0;
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.nav-tab:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.nav-tab:hover {
|
||||
color: #409eff;
|
||||
background-color: #ecf5ff;
|
||||
}
|
||||
|
||||
.nav-tab.active {
|
||||
background-color: #409eff;
|
||||
color: #fff;
|
||||
box-shadow: 0 2px 4px rgba(64, 158, 255, 0.3);
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 1200px) {
|
||||
.search-container {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.stats-and-image {
|
||||
max-width: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.search-integrated {
|
||||
width: 100%;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 992px) {
|
||||
.stats-and-image {
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.vehicle-image-container {
|
||||
width: 100%;
|
||||
max-width: 300px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.execution-records {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.navigation-tabs {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.nav-tab {
|
||||
flex: 1 0 33%;
|
||||
padding: 10px 0;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.vehicle-stats {
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.stat-item {
|
||||
flex: 1 0 40%;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.pagination-section {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 10px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
1095
src/views/dhr_demo/gongdanliebiao.vue
Normal file
1095
src/views/dhr_demo/gongdanliebiao.vue
Normal file
File diff suppressed because it is too large
Load Diff
@ -4,10 +4,11 @@
|
||||
<div class="navigation-tabs">
|
||||
<div class="nav-tab active" @click="handleInspection1">待办事项</div>
|
||||
<div class="nav-tab" @click="handleInspection2">巡检管理</div>
|
||||
<div class="nav-tab">试验管理</div>
|
||||
<div class="nav-tab">报修管理</div>
|
||||
<div class="nav-tab">抢修管理</div>
|
||||
<div class="nav-tab">工单管理</div>
|
||||
<div class="nav-tab" @click="handleInspection3">试验管理</div>
|
||||
<div class="nav-tab" @click="handleInspection4">报修管理</div>
|
||||
<div class="nav-tab" @click="handleInspection5">抢修管理</div>
|
||||
<div class="nav-tab" @click="handleInspection6">工单管理</div>
|
||||
<div class="nav-tab" @click="handleInspection7">运维组织</div>
|
||||
</div>
|
||||
|
||||
<!-- 标题栏 -->
|
||||
@ -310,6 +311,21 @@ const handleInspection1 = () => {
|
||||
const handleInspection2 = () => {
|
||||
router.push('/rili/InspectionManagement');
|
||||
};
|
||||
const handleInspection3 = () => {
|
||||
router.push('/rili/shiyanguanli');
|
||||
};
|
||||
const handleInspection4 = () => {
|
||||
router.push('/rili/baoxiuguanli');
|
||||
};
|
||||
const handleInspection5 = () => {
|
||||
router.push('/rili/qiangxiuguanli');
|
||||
};
|
||||
const handleInspection6 = () => {
|
||||
router.push('/rili/gongdanliebiao');
|
||||
};
|
||||
const handleInspection7 = () => {
|
||||
router.push('/rili/renyuanzhuangtai');
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
814
src/views/dhr_demo/paidanjilu.vue
Normal file
814
src/views/dhr_demo/paidanjilu.vue
Normal file
@ -0,0 +1,814 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="dispatch-records">
|
||||
<!-- 顶部导航栏 -->
|
||||
<div class="navigation-tabs">
|
||||
<div class="nav-tab" @click="handleInspection1">待办事项</div>
|
||||
<div class="nav-tab" @click="handleInspection2">巡检管理</div>
|
||||
<div class="nav-tab" @click="handleInspection3">试验管理</div>
|
||||
<div class="nav-tab" @click="handleInspection4">报修管理</div>
|
||||
<div class="nav-tab" @click="handleInspection5">抢修管理</div>
|
||||
<div class="nav-tab active" @click="handleInspection6">工单管理</div>
|
||||
<div class="nav-tab" @click="handleInspection7">运维组织</div>
|
||||
</div>
|
||||
|
||||
<!-- 页面标题 -->
|
||||
<TitleComponent title="工单管理模块" subtitle="发起工单任务,跟踪流程记录和执行情况"></TitleComponent>
|
||||
|
||||
<!-- 选项卡 -->
|
||||
<div class="tabs-wrapper">
|
||||
<div style="display: flex; align-items: center; gap: 10px">
|
||||
<el-button type="primary" @click="handleInspectionManagement1">工单列表</el-button>
|
||||
<el-button type="primary" @click="handleInspectionManagement2">派单计划</el-button>
|
||||
<el-button type="primary" @click="handleInspectionManagement3">执行计划</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 筛选栏 -->
|
||||
<div class="filter-bar">
|
||||
<div class="filter-container">
|
||||
<div class="filter-item">
|
||||
<el-select v-model="dispatchStatus" placeholder="派单状态" clearable>
|
||||
<el-option label="全部状态" value="all"></el-option>
|
||||
<el-option label="已接收" value="received"></el-option>
|
||||
<el-option label="处理中" value="processing"></el-option>
|
||||
<el-option label="已完成" value="completed"></el-option>
|
||||
<el-option label="已拒绝" value="rejected"></el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<el-select v-model="executor" placeholder="执行人" clearable>
|
||||
<el-option label="全部人员" value="all"></el-option>
|
||||
<el-option label="张明" value="zhangming"></el-option>
|
||||
<el-option label="李华" value="lihua"></el-option>
|
||||
<el-option label="王强" value="wangqiang"></el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<el-date-picker v-model="dispatchDate" type="date" placeholder="派单日期" format="yyyy/MM/dd" value-format="yyyy/MM/dd"></el-date-picker>
|
||||
</div>
|
||||
<div class="filter-actions">
|
||||
<el-button type="primary" class="search-btn" @click="handleSearch">搜索</el-button>
|
||||
<el-button type="primary" class="create-btn" @click="handleExport">导出记录</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 统计卡片 -->
|
||||
<div class="statistics-cards">
|
||||
<div class="stat-card">
|
||||
<div class="stat-value">{{ totalDispatches }}</div>
|
||||
<div class="stat-label">本月派单总数</div>
|
||||
<div class="stat-trend">较上月 <span class="trend-up">↑ 12%</span></div>
|
||||
<div class="stat-icon">
|
||||
<i class="el-icon-document"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-value">{{ avgResponseTime }}</div>
|
||||
<div class="stat-label">平均响应时间</div>
|
||||
<div class="stat-trend">较上月 <span class="trend-down">↓ 5分钟</span></div>
|
||||
<div class="stat-icon">
|
||||
<i class="el-icon-clock"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-value">{{ pendingDispatches }}</div>
|
||||
<div class="stat-label">待接收工单</div>
|
||||
<div class="stat-trend">较昨日 <span class="trend-up">↑ 2</span></div>
|
||||
<div class="stat-icon warning">
|
||||
<i class="el-icon-warning"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-value">{{ completionRate }}</div>
|
||||
<div class="stat-label">按时完成率</div>
|
||||
<div class="stat-trend">较上月 <span class="trend-up">↑ 3%</span></div>
|
||||
<div class="stat-icon success">
|
||||
<i class="el-icon-check-circle"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 派单记录表格 -->
|
||||
<div class="table-wrapper">
|
||||
<el-table :data="pagedTableData" stripe style="width: 100%" highlight-current-row class="custom-table">
|
||||
<el-table-column align="center" prop="dispatchNo" label="派单号" min-width="120"></el-table-column>
|
||||
<el-table-column align="center" prop="orderNo" label="工单编号" min-width="120"></el-table-column>
|
||||
<el-table-column align="center" prop="taskType" label="工单类型" min-width="100"></el-table-column>
|
||||
<el-table-column align="center" prop="dispatcher" label="派单人" min-width="100"></el-table-column>
|
||||
<el-table-column align="center" prop="executor" label="接收人" min-width="100"></el-table-column>
|
||||
<el-table-column align="center" prop="dispatchTime" label="派单时间" min-width="140"></el-table-column>
|
||||
<el-table-column align="center" prop="status" label="派单状态" min-width="100">
|
||||
<template #default="scope">
|
||||
<el-tag :type="getStatusTagType(scope.row.status)" class="status-tag">
|
||||
{{ scope.row.status }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" prop="completionStatus" label="完成状态" min-width="100">
|
||||
<template #default="scope">
|
||||
<el-tag :type="getCompletionTagType(scope.row.completionStatus)" class="completion-tag">
|
||||
{{ scope.row.completionStatus }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="操作" min-width="180" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-button type="text" @click="handleViewDetails(scope.row)" size="small" class="action-btn">详情</el-button>
|
||||
<el-button
|
||||
type="text"
|
||||
@click="handleViewReport(scope.row)"
|
||||
size="small"
|
||||
class="action-btn report-btn"
|
||||
v-if="scope.row.completionStatus === '已完成'"
|
||||
>
|
||||
查看报告
|
||||
</el-button>
|
||||
<el-button
|
||||
type="text"
|
||||
@click="handleViewProgress(scope.row)"
|
||||
size="small"
|
||||
class="action-btn progress-btn"
|
||||
v-else-if="scope.row.completionStatus === '进行中'"
|
||||
>
|
||||
查看进度
|
||||
</el-button>
|
||||
<el-button type="text" @click="handleTrack(scope.row)" size="small" class="action-btn track-btn" v-else> 跟踪 </el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<!-- 分页区域 -->
|
||||
<div class="pagination-section">
|
||||
<div class="pagination-info">
|
||||
显示第{{ (currentPage - 1) * pageSize + 1 }}到{{ Math.min(currentPage * pageSize, total) }}条,共{{ total }}条记录
|
||||
</div>
|
||||
<div class="pagination-controls">
|
||||
<el-pagination
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
:current-page="currentPage"
|
||||
:page-sizes="[10, 20, 30, 40]"
|
||||
:page-size="pageSize"
|
||||
layout="prev, pager, next, jumper"
|
||||
:total="total"
|
||||
background
|
||||
>
|
||||
</el-pagination>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, reactive } from 'vue';
|
||||
import router from '@/router';
|
||||
import TitleComponent from '@/views/demo/components/TitleComponent.vue';
|
||||
|
||||
// 激活的选项卡,默认显示派单记录
|
||||
const activeTab = ref('dispatch');
|
||||
|
||||
// 筛选条件
|
||||
const dispatchStatus = ref('all');
|
||||
const executor = ref('all');
|
||||
const dispatchDate = ref('');
|
||||
|
||||
// 统计数据
|
||||
const totalDispatches = ref(56);
|
||||
const avgResponseTime = ref('42分钟');
|
||||
const pendingDispatches = ref(7);
|
||||
const completionRate = ref('92%');
|
||||
|
||||
// 派单记录数据
|
||||
const rawTableData = ref([
|
||||
{
|
||||
dispatchNo: 'DP-2023-0619-089',
|
||||
orderNo: 'WO-2023-0619-055',
|
||||
taskType: '安全设备检查检测',
|
||||
dispatcher: '张明',
|
||||
executor: '李阳',
|
||||
dispatchTime: '2023-06-19 13:50',
|
||||
status: '已接收',
|
||||
completionStatus: '未完成'
|
||||
},
|
||||
{
|
||||
dispatchNo: 'DP-2023-0619-088',
|
||||
orderNo: 'WO-2023-0618-054',
|
||||
taskType: '新设备安装调试',
|
||||
dispatcher: '张明',
|
||||
executor: '李阳',
|
||||
dispatchTime: '2023-06-19 13:30',
|
||||
status: '已接收',
|
||||
completionStatus: '进行中'
|
||||
},
|
||||
{
|
||||
dispatchNo: 'DP-2023-0619-087',
|
||||
orderNo: 'WO-2023-0617-053',
|
||||
taskType: '系统升级改造',
|
||||
dispatcher: '张明',
|
||||
executor: '李阳',
|
||||
dispatchTime: '2023-06-19 10:15',
|
||||
status: '已接收',
|
||||
completionStatus: '已完成'
|
||||
},
|
||||
{
|
||||
dispatchNo: 'DP-2023-0619-086',
|
||||
orderNo: 'WO-2023-0616-052',
|
||||
taskType: '安全设备检查检测',
|
||||
dispatcher: '张明',
|
||||
executor: '李阳',
|
||||
dispatchTime: '2023-06-19 09:40',
|
||||
status: '已接收',
|
||||
completionStatus: '未完成'
|
||||
},
|
||||
{
|
||||
dispatchNo: 'DP-2023-0619-085',
|
||||
orderNo: 'WO-2023-0615-051',
|
||||
taskType: '安全设备检查检测',
|
||||
dispatcher: '张明',
|
||||
executor: '李阳',
|
||||
dispatchTime: '2023-06-19 09:20',
|
||||
status: '已接收',
|
||||
completionStatus: '未完成'
|
||||
},
|
||||
{
|
||||
dispatchNo: 'DP-2023-0619-084',
|
||||
orderNo: 'WO-2023-0614-050',
|
||||
taskType: '安全设备检查检测',
|
||||
dispatcher: '张明',
|
||||
executor: '李阳',
|
||||
dispatchTime: '2023-06-19 09:10',
|
||||
status: '已接收',
|
||||
completionStatus: '未完成'
|
||||
},
|
||||
{
|
||||
dispatchNo: 'DP-2023-0619-083',
|
||||
orderNo: 'WO-2023-0613-049',
|
||||
taskType: '安全设备检查检测',
|
||||
dispatcher: '张明',
|
||||
executor: '李阳',
|
||||
dispatchTime: '2023-06-19 08:50',
|
||||
status: '已接收',
|
||||
completionStatus: '未完成'
|
||||
}
|
||||
]);
|
||||
|
||||
// 分页相关
|
||||
const currentPage = ref(1);
|
||||
const pageSize = ref(10);
|
||||
const total = ref(rawTableData.value.length);
|
||||
|
||||
// 分页处理后的数据
|
||||
const pagedTableData = computed(() => {
|
||||
// 筛选逻辑
|
||||
let filteredData = [...rawTableData.value];
|
||||
|
||||
if (dispatchStatus.value !== 'all') {
|
||||
filteredData = filteredData.filter((item) => item.status === dispatchStatus.value);
|
||||
}
|
||||
|
||||
if (executor.value !== 'all') {
|
||||
// 这里假设执行人的值与选项值对应
|
||||
const executorMap = {
|
||||
'zhangming': '张明',
|
||||
'lihua': '李华',
|
||||
'wangqiang': '王强'
|
||||
};
|
||||
filteredData = filteredData.filter((item) => item.executor === executorMap[executor.value]);
|
||||
}
|
||||
|
||||
if (dispatchDate.value) {
|
||||
filteredData = filteredData.filter((item) => item.dispatchTime.includes(dispatchDate.value));
|
||||
}
|
||||
|
||||
// 更新总条数
|
||||
total.value = filteredData.length;
|
||||
|
||||
// 分页处理
|
||||
const startIndex = (currentPage.value - 1) * pageSize.value;
|
||||
const endIndex = startIndex + pageSize.value;
|
||||
return filteredData.slice(startIndex, endIndex);
|
||||
});
|
||||
|
||||
// 获取派单状态标签样式
|
||||
const getStatusTagType = (status) => {
|
||||
const statusMap = {
|
||||
'已接收': 'primary',
|
||||
'处理中': 'success',
|
||||
'已完成': 'info',
|
||||
'已拒绝': 'danger'
|
||||
};
|
||||
return statusMap[status] || 'default';
|
||||
};
|
||||
|
||||
// 获取完成状态标签样式
|
||||
const getCompletionTagType = (status) => {
|
||||
const statusMap = {
|
||||
'未完成': 'warning',
|
||||
'进行中': 'primary',
|
||||
'已完成': 'success',
|
||||
'已逾期': 'danger'
|
||||
};
|
||||
return statusMap[status] || 'default';
|
||||
};
|
||||
|
||||
// 搜索处理
|
||||
const handleSearch = () => {
|
||||
currentPage.value = 1; // 重置到第一页
|
||||
};
|
||||
|
||||
// 导出记录
|
||||
const handleExport = () => {
|
||||
console.log('导出派单记录');
|
||||
// 实际应用中这里会调用导出API
|
||||
};
|
||||
|
||||
// 分页事件
|
||||
const handleSizeChange = (val) => {
|
||||
pageSize.value = val;
|
||||
currentPage.value = 1;
|
||||
};
|
||||
|
||||
const handleCurrentChange = (val) => {
|
||||
currentPage.value = val;
|
||||
};
|
||||
|
||||
// 选项卡点击
|
||||
const handleTabClick = (tab) => {
|
||||
console.log('切换到选项卡:', tab.name);
|
||||
// 重置筛选条件和分页
|
||||
dispatchStatus.value = 'all';
|
||||
executor.value = 'all';
|
||||
dispatchDate.value = '';
|
||||
currentPage.value = 1;
|
||||
|
||||
// 如果切换到其他选项卡,导航到相应页面
|
||||
if (tab.name === 'list') {
|
||||
router.push('/rili/gongdanguanli');
|
||||
} else if (tab.name === 'execution') {
|
||||
router.push('/rili/execution-records');
|
||||
}
|
||||
};
|
||||
|
||||
// 操作按钮事件
|
||||
const handleViewDetails = (row) => {
|
||||
console.log('查看派单详情:', row);
|
||||
// 实际应用中这里会跳转到详情页
|
||||
};
|
||||
|
||||
const handleViewReport = (row) => {
|
||||
console.log('查看报告:', row);
|
||||
// 实际应用中这里会跳转到报告页面
|
||||
};
|
||||
|
||||
const handleViewProgress = (row) => {
|
||||
console.log('查看进度:', row);
|
||||
// 实际应用中这里会跳转到进度页面
|
||||
};
|
||||
|
||||
const handleTrack = (row) => {
|
||||
console.log('跟踪工单:', row);
|
||||
// 实际应用中这里会跳转到跟踪页面或打开跟踪弹窗
|
||||
};
|
||||
|
||||
// 导航路由跳转
|
||||
const handleInspection1 = () => {
|
||||
router.push('/rili/rili');
|
||||
};
|
||||
const handleInspection2 = () => {
|
||||
router.push('/rili/InspectionManagement');
|
||||
};
|
||||
const handleInspection3 = () => {
|
||||
router.push('/rili/shiyanguanli');
|
||||
};
|
||||
const handleInspection4 = () => {
|
||||
router.push('/rili/baoxiuguanli');
|
||||
};
|
||||
const handleInspection5 = () => {
|
||||
router.push('/rili/qiangxiuguanli');
|
||||
};
|
||||
const handleInspection6 = () => {
|
||||
router.push('/rili/gongdanguanli');
|
||||
};
|
||||
const handleInspection7 = () => {
|
||||
router.push('/rili/renyuanzhuangtai');
|
||||
};
|
||||
const handleInspectionManagement1 = () => {
|
||||
router.push('/rili/gongdanliebiao');
|
||||
};
|
||||
|
||||
const handleInspectionManagement2 = () => {
|
||||
router.push('/rili/paidanjilu');
|
||||
};
|
||||
const handleInspectionManagement3 = () => {
|
||||
router.push('/rili/zhixingjilu');
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.dispatch-records {
|
||||
padding: 20px;
|
||||
background-color: #f5f7fa;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
/* 选项卡样式 */
|
||||
.tabs-wrapper {
|
||||
background-color: #fff;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 16px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.custom-tabs {
|
||||
padding-top: 1px;
|
||||
}
|
||||
|
||||
.custom-tabs .el-tabs__header {
|
||||
margin: 0 -20px;
|
||||
padding: 0 20px;
|
||||
border-bottom: 1px solid #e4e7ed;
|
||||
}
|
||||
|
||||
.custom-tabs .el-tabs__nav-wrap::after {
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.custom-tabs .el-tabs__item {
|
||||
font-size: 14px;
|
||||
color: #606266;
|
||||
padding: 14px 20px;
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
.custom-tabs .el-tabs__item.is-active {
|
||||
color: #165dff;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.custom-tabs .el-tabs__item:hover {
|
||||
color: #165dff;
|
||||
}
|
||||
|
||||
/* 筛选栏样式 */
|
||||
.filter-bar {
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 24px;
|
||||
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05);
|
||||
padding: 16px 24px;
|
||||
}
|
||||
|
||||
.filter-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 16px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.filter-item {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.filter-bar .el-select,
|
||||
.filter-bar .el-date-picker {
|
||||
width: 180px;
|
||||
height: 36px;
|
||||
}
|
||||
|
||||
.filter-bar .el-select .el-input__inner,
|
||||
.filter-bar .el-date-picker .el-input__inner {
|
||||
border-radius: 4px;
|
||||
border-color: #dcdfe6;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.filter-bar .el-select .el-input__inner:focus,
|
||||
.filter-bar .el-date-picker .el-input__inner:focus {
|
||||
border-color: #165dff;
|
||||
box-shadow: 0 0 0 2px rgba(22, 93, 255, 0.1);
|
||||
}
|
||||
|
||||
.filter-actions {
|
||||
margin-left: auto;
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.search-btn {
|
||||
background-color: #f2f3f5;
|
||||
color: #303133;
|
||||
border-color: #f2f3f5;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.search-btn:hover {
|
||||
background-color: #e5e6eb;
|
||||
color: #303133;
|
||||
border-color: #e5e6eb;
|
||||
}
|
||||
|
||||
.export-btn {
|
||||
color: #165dff;
|
||||
border: 1px solid #165dff;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.export-btn:hover {
|
||||
background-color: #e8f3ff;
|
||||
color: #0e42d2;
|
||||
border-color: #0e42d2;
|
||||
}
|
||||
|
||||
/* 统计卡片样式 */
|
||||
.statistics-cards {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
gap: 16px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
color: #1d2129;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 14px;
|
||||
color: #86909c;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.stat-trend {
|
||||
font-size: 12px;
|
||||
color: #86909c;
|
||||
}
|
||||
|
||||
.trend-up {
|
||||
color: #52c41a;
|
||||
}
|
||||
|
||||
.trend-down {
|
||||
color: #ff4d4f;
|
||||
}
|
||||
|
||||
.stat-icon {
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 8px;
|
||||
background-color: #f0f7ff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #165dff;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.stat-icon.warning {
|
||||
background-color: #fff7e6;
|
||||
color: #fa8c16;
|
||||
}
|
||||
|
||||
.stat-icon.success {
|
||||
background-color: #f6ffed;
|
||||
color: #52c41a;
|
||||
}
|
||||
|
||||
/* 表格样式 */
|
||||
.table-wrapper {
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 24px;
|
||||
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.custom-table {
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.custom-table th {
|
||||
background-color: #f5f7fa;
|
||||
font-weight: 500;
|
||||
color: #606266;
|
||||
text-align: center;
|
||||
padding: 12px 8px;
|
||||
border-bottom: 1px solid #e4e7ed;
|
||||
}
|
||||
|
||||
.custom-table td {
|
||||
padding: 12px 8px;
|
||||
border-bottom: 1px solid #e4e7ed;
|
||||
color: #303133;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.custom-table tr:hover {
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
|
||||
.custom-table tr.current-row {
|
||||
background-color: #ecf5ff;
|
||||
}
|
||||
|
||||
.status-tag,
|
||||
.completion-tag {
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
/* 操作按钮样式 */
|
||||
.action-btn {
|
||||
font-size: 12px;
|
||||
padding: 4px 8px;
|
||||
margin: 0 2px;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.action-btn:hover {
|
||||
background-color: #e8f3ff;
|
||||
}
|
||||
|
||||
.report-btn {
|
||||
color: #52c41a;
|
||||
}
|
||||
|
||||
.report-btn:hover {
|
||||
color: #389e0d;
|
||||
background-color: #f6ffed;
|
||||
}
|
||||
|
||||
.progress-btn {
|
||||
color: #165dff;
|
||||
}
|
||||
|
||||
.progress-btn:hover {
|
||||
color: #0e42d2;
|
||||
background-color: #e8f3ff;
|
||||
}
|
||||
|
||||
.track-btn {
|
||||
color: #fa8c16;
|
||||
}
|
||||
|
||||
.track-btn:hover {
|
||||
color: #e67700;
|
||||
background-color: #fff7e6;
|
||||
}
|
||||
|
||||
/* 分页区域样式 */
|
||||
.pagination-section {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
background-color: #fff;
|
||||
padding: 16px 24px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.pagination-info {
|
||||
font-size: 14px;
|
||||
color: #606266;
|
||||
}
|
||||
|
||||
.pagination-controls .el-pagination {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.pagination-controls .el-pagination button,
|
||||
.pagination-controls .el-pagination .el-pager li {
|
||||
min-width: 36px;
|
||||
height: 36px;
|
||||
line-height: 36px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.pagination-controls .el-pagination .el-pager li.active {
|
||||
background-color: #165dff;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* 导航栏样式 */
|
||||
.navigation-tabs {
|
||||
display: flex;
|
||||
margin-bottom: 20px;
|
||||
background-color: #fff;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08);
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.nav-tab {
|
||||
padding: 12px 24px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
color: #606266;
|
||||
border-right: 1px solid #f0f0f0;
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.nav-tab:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.nav-tab:hover {
|
||||
color: #409eff;
|
||||
background-color: #ecf5ff;
|
||||
}
|
||||
|
||||
.nav-tab.active {
|
||||
background-color: #409eff;
|
||||
color: #fff;
|
||||
box-shadow: 0 2px 4px rgba(64, 158, 255, 0.3);
|
||||
}
|
||||
|
||||
.nav-tab {
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 1200px) {
|
||||
.filter-container {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.filter-actions {
|
||||
margin-left: 0;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.filter-bar .el-select,
|
||||
.filter-bar .el-date-picker {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
.create-btn {
|
||||
background-color: #165dff;
|
||||
border-color: #165dff;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.create-btn:hover {
|
||||
background-color: #0e42d2;
|
||||
border-color: #0e42d2;
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
.dispatch-records {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.navigation-tabs {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.nav-tab {
|
||||
flex: 1 0 33%;
|
||||
padding: 10px 0;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.statistics-cards {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
|
||||
.pagination-section {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.statistics-cards {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
912
src/views/dhr_demo/qiangxiuguanli.vue
Normal file
912
src/views/dhr_demo/qiangxiuguanli.vue
Normal file
@ -0,0 +1,912 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="inspection-tasks">
|
||||
<div class="navigation-tabs">
|
||||
<div class="nav-tab" @click="handleInspection1">待办事项</div>
|
||||
<div class="nav-tab" @click="handleInspection2">巡检管理</div>
|
||||
<div class="nav-tab" @click="handleInspection3">试验管理</div>
|
||||
<div class="nav-tab" @click="handleInspection4">报修管理</div>
|
||||
<div class="nav-tab active" @click="handleInspection5">抢修管理</div>
|
||||
<div class="nav-tab" @click="handleInspection6">工单管理</div>
|
||||
<div class="nav-tab" @click="handleInspection7">运维组织</div>
|
||||
</div>
|
||||
<!-- 页面标题 -->
|
||||
<TitleComponent title="抢修管理模块" subtitle="处理紧急抢修任务,跟踪抢修进度和记录"></TitleComponent>
|
||||
|
||||
<!-- 选项卡 -->
|
||||
<div class="tabs-wrapper">
|
||||
<div style="display: flex; align-items: center; gap: 10px">
|
||||
<el-button type="primary" @click="handleInspectionManagement1">抢修任务</el-button>
|
||||
<el-button type="primary" @click="handleInspectionManagement2">抢修记录</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 筛选栏 -->
|
||||
<div class="filter-bar">
|
||||
<div class="filter-container">
|
||||
<div class="filter-item">
|
||||
<el-select v-model="taskStatus" placeholder="任务状态">
|
||||
<el-option label="待执行" value="pending"></el-option>
|
||||
<el-option label="执行中" value="executing"></el-option>
|
||||
<el-option label="已延期" value="delayed"></el-option>
|
||||
<el-option label="已完成" value="completed"></el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<el-select v-model="planType" placeholder="全部计划">
|
||||
<el-option label="全部计划" value="all"></el-option>
|
||||
<el-option label="每日巡检计划" value="daily"></el-option>
|
||||
<el-option label="每周巡检计划" value="weekly"></el-option>
|
||||
<el-option label="每月巡检计划" value="monthly"></el-option>
|
||||
<el-option label="每季度巡检计划" value="quarterly"></el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<el-select v-model="executor" placeholder="执行人">
|
||||
<el-option label="全部人员" value="all"></el-option>
|
||||
<el-option label="张明" value="zhangming"></el-option>
|
||||
<el-option label="李华" value="lihua"></el-option>
|
||||
<el-option label="王强" value="wangqiang"></el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
<div class="filter-actions">
|
||||
<el-button type="primary" class="search-btn" @click="handleSearch"> 搜索 </el-button>
|
||||
<el-button type="primary" icon="el-icon-plus" class="create-btn" @click="handleCreateTask"> 手动创建任务 </el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 任务卡片列表 -->
|
||||
<div class="task-cards">
|
||||
<div class="task-card" v-for="(task, index) in pagedTasks" :key="index" :class="task.statusClass">
|
||||
<!-- 顶部信息区域 -->
|
||||
<div class="task-header">
|
||||
<div class="task-title">
|
||||
{{ task.title }}
|
||||
</div>
|
||||
<div class="task-type-tag" :class="getFaultTypeClass(task.faultType)">
|
||||
{{ task.faultType }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 新增滚动内容容器 -->
|
||||
<div class="task-content-scroll">
|
||||
<div class="task-details">
|
||||
<div class="detail-item">
|
||||
<span class="detail-label">报修时间</span>
|
||||
<span class="detail-value">{{ task.reportTime }}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<span class="detail-label">报修人</span>
|
||||
<span class="detail-value">{{ task.reporter }}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<span class="detail-label">维修人</span>
|
||||
<span class="detail-value">{{ task.maintainer }}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<span class="detail-label">预计完成</span>
|
||||
<span class="detail-value">{{ task.expectedCompleteTime }}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<span class="detail-label">状态</span>
|
||||
<span class="detail-value">{{ task.statusText }}</span>
|
||||
</div>
|
||||
|
||||
<!-- 已完成状态的额外信息 -->
|
||||
<div v-if="task.status === 'completed'" class="task-result">
|
||||
<span class="detail-label">完成时间</span>
|
||||
<span class="detail-value">{{ task.completeTime }}</span>
|
||||
</div>
|
||||
|
||||
<!-- 增加一些示例数据来演示滚动效果 -->
|
||||
<div v-if="task.status === 'executing'" class="detail-item">
|
||||
<span class="detail-label">开始时间</span>
|
||||
<span class="detail-value">{{ task.startTime || '30分钟前' }}</span>
|
||||
</div>
|
||||
<div v-if="task.status === 'executing'" class="detail-item">
|
||||
<span class="detail-label">进度</span>
|
||||
<span class="detail-value">{{ task.progress || '60%' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="task-actions">
|
||||
<el-button type="text" size="small" class="action-btn view-btn" @click="handleView(task)"> 详情 </el-button>
|
||||
<el-button type="primary" size="small" :class="task.actionClass" @click="handleAction(task)">
|
||||
{{ task.actionText }}
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 分页区域 -->
|
||||
<div class="pagination-section">
|
||||
<div class="pagination-controls">
|
||||
<el-pagination
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
:current-page="currentPage"
|
||||
:page-sizes="[8, 12, 16, 20]"
|
||||
:page-size="pageSize"
|
||||
layout="prev, pager, next, jumper"
|
||||
:total="total"
|
||||
background
|
||||
>
|
||||
</el-pagination>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 新建紧急抢修任务弹窗 -->
|
||||
<el-dialog
|
||||
v-model="createTaskDialogVisible"
|
||||
title="创建紧急抢修任务"
|
||||
width="800px"
|
||||
:before-close="handleCancelCreateTask"
|
||||
custom-class="beautiful-dialog"
|
||||
center
|
||||
>
|
||||
<el-form ref="createTaskFormRef" :model="createTaskForm" :rules="createTaskRules" label-width="100px" class="elegant-form">
|
||||
<el-form-item label="抢修名称*" prop="repairName">
|
||||
<el-input v-model="createTaskForm.repairName" placeholder="简要描述抢修内容" />
|
||||
</el-form-item>
|
||||
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="抢修类型*" prop="repairType">
|
||||
<el-select v-model="createTaskForm.repairType" placeholder="请选择类型">
|
||||
<el-option label="设备故障" value="device" />
|
||||
<el-option label="软件故障" value="software" />
|
||||
<el-option label="网络故障" value="network" />
|
||||
<el-option label="环境问题" value="environment" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="紧急程度*" prop="priority">
|
||||
<el-select v-model="createTaskForm.priority" placeholder="请选择紧急程度">
|
||||
<el-option label="致命" value="fatal" />
|
||||
<el-option label="紧急" value="urgent" />
|
||||
<el-option label="较危险" value="dangerous" />
|
||||
<el-option label="一般" value="normal" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-form-item label="故障描述*" prop="detailedDescription">
|
||||
<el-input
|
||||
v-model="createTaskForm.detailedDescription"
|
||||
type="textarea"
|
||||
:rows="4"
|
||||
placeholder="详细描述故障现象、影响范围、潜在威胁等信息"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="故障位置*" prop="faultLocation">
|
||||
<el-input v-model="createTaskForm.faultLocation" placeholder="例如:A区102" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="上传图片(可选)*">
|
||||
<div class="upload-container">
|
||||
<div class="upload-box">
|
||||
<i class="el-icon-plus avatar-uploader-icon"></i>
|
||||
<div class="upload-text">点击或拖拽图片至此处上传</div>
|
||||
<div class="upload-hint">支持JPG、PNG格式,最多3张</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="联系人*" prop="contactPerson">
|
||||
<el-input v-model="createTaskForm.contactPerson" placeholder="您的姓名" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="联系电话*" prop="contactPhone">
|
||||
<el-input v-model="createTaskForm.contactPhone" placeholder="您的联系电话" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-form-item label="是否需要立即现场支持*">
|
||||
<div>
|
||||
<el-radio v-model="createTaskForm.needSupport" label="yes">是,需要立即派人现场</el-radio>
|
||||
<el-radio v-model="createTaskForm.needSupport" label="no">否,可远程指导或延后处理</el-radio>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="handleCancelCreateTask">取消</el-button>
|
||||
<el-button type="primary" @click="handleSaveTask">提交抢修计划</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue';
|
||||
import router from '@/router';
|
||||
import TitleComponent from '@/views/demo/components/TitleComponent.vue';
|
||||
|
||||
// 激活的选项卡
|
||||
const activeTab = ref('task');
|
||||
|
||||
// 根据故障类型获取对应的CSS类
|
||||
const getFaultTypeClass = (faultType) => {
|
||||
const typeMap = {
|
||||
// 电力,设备故障为红色
|
||||
'电力故障': 'electric',
|
||||
'设备故障': 'equipment',
|
||||
|
||||
// 供水,设备损坏为黄色
|
||||
'供水问题': 'water',
|
||||
'设备损坏': 'damage',
|
||||
|
||||
// 其余默认使用基本样式(绿色)
|
||||
'网络中断': '',
|
||||
'制冷系统故障': '',
|
||||
'安全问题': ''
|
||||
};
|
||||
return typeMap[faultType] || '';
|
||||
};
|
||||
|
||||
// 筛选条件
|
||||
const repairType = ref('all');
|
||||
const taskStatus = ref('all');
|
||||
const emergencyLevel = ref('all');
|
||||
const planType = ref('all');
|
||||
const executor = ref('all');
|
||||
|
||||
// 任务数据 - 添加了更多字段以展示滚动效果
|
||||
const tasks = ref([
|
||||
{
|
||||
title: '主配电室短路故障',
|
||||
status: 'executing',
|
||||
statusText: '抢修中',
|
||||
statusClass: 'status-high',
|
||||
priority: '致命',
|
||||
reportTime: '10分钟前',
|
||||
reporter: '李阳',
|
||||
maintainer: '张明',
|
||||
expectedCompleteTime: '30分钟内',
|
||||
faultType: '电力故障',
|
||||
faultLocation: '地下一层主配电室',
|
||||
startTime: '10分钟前',
|
||||
progress: '40%',
|
||||
remarks: '已切断该区域电源,正在排查短路点',
|
||||
actionText: '实时跟进',
|
||||
actionClass: 'follow-btn'
|
||||
},
|
||||
{
|
||||
title: '主配电室短路故障',
|
||||
status: 'executing',
|
||||
statusText: '抢修中',
|
||||
statusClass: 'status-medium',
|
||||
priority: '紧急',
|
||||
reportTime: '45分钟前',
|
||||
reporter: '李阳',
|
||||
maintainer: '张明',
|
||||
expectedCompleteTime: '1小时内',
|
||||
faultType: '供水问题',
|
||||
faultLocation: '三楼东侧卫生间',
|
||||
startTime: '40分钟前',
|
||||
progress: '70%',
|
||||
remarks: '已找到漏水点,正在进行修复',
|
||||
actionText: '实时跟进',
|
||||
actionClass: 'follow-btn'
|
||||
},
|
||||
{
|
||||
title: '网络主干交换机故障',
|
||||
status: 'completed',
|
||||
statusText: '已完成',
|
||||
statusClass: 'status-completed',
|
||||
priority: '较危险',
|
||||
reportTime: '1小时前',
|
||||
reporter: '李阳',
|
||||
maintainer: '张明',
|
||||
expectedCompleteTime: '1小时内',
|
||||
faultType: '网络中断',
|
||||
faultLocation: '二楼机房',
|
||||
completeTime: '2小时前',
|
||||
remarks: '交换机电源模块故障,已更换备用模块',
|
||||
actionText: '查看报告',
|
||||
actionClass: 'view-btn'
|
||||
},
|
||||
{
|
||||
title: '安全出口指示牌损坏',
|
||||
status: 'pending',
|
||||
statusText: '待处理',
|
||||
statusClass: 'status-medium',
|
||||
priority: '较危险',
|
||||
reportTime: '3小时前',
|
||||
reporter: '李阳',
|
||||
maintainer: '未分配',
|
||||
expectedCompleteTime: '今天以内',
|
||||
faultType: '设备损坏',
|
||||
faultLocation: '一楼东侧安全通道',
|
||||
remarks: '指示牌不亮,可能是线路问题',
|
||||
actionText: '分配任务',
|
||||
actionClass: 'assign-btn'
|
||||
},
|
||||
{
|
||||
title: '制冷系统主压缩机故障',
|
||||
status: 'executing',
|
||||
statusText: '抢修中',
|
||||
statusClass: 'status-high',
|
||||
priority: '致命',
|
||||
reportTime: '1小时前',
|
||||
reporter: '李阳',
|
||||
maintainer: '张明',
|
||||
expectedCompleteTime: '45分钟内',
|
||||
faultType: '设备故障',
|
||||
faultLocation: '楼顶制冷机房',
|
||||
startTime: '50分钟前',
|
||||
progress: '30%',
|
||||
remarks: '压缩机无法启动,正在检查电路和 refrigerant 压力',
|
||||
actionText: '实时跟进',
|
||||
actionClass: 'follow-btn'
|
||||
},
|
||||
{
|
||||
title: '主配电室短路故障',
|
||||
status: 'executing',
|
||||
statusText: '抢修中',
|
||||
statusClass: 'status-medium',
|
||||
priority: '紧急',
|
||||
reportTime: '45分钟前',
|
||||
reporter: '李阳',
|
||||
maintainer: '张明',
|
||||
expectedCompleteTime: '1小时内',
|
||||
faultType: '供水问题',
|
||||
faultLocation: '地下室水泵房',
|
||||
startTime: '40分钟前',
|
||||
progress: '60%',
|
||||
remarks: '水泵压力不足,正在更换滤网和检查管道',
|
||||
actionText: '实时跟进',
|
||||
actionClass: 'follow-btn'
|
||||
}
|
||||
]);
|
||||
|
||||
// 分页相关
|
||||
const currentPage = ref(1);
|
||||
const pageSize = ref(20);
|
||||
const total = ref(6);
|
||||
|
||||
// 状态排序映射
|
||||
const statusOrder = {
|
||||
pending: 0, // 待处理
|
||||
executing: 1, // 处理中
|
||||
completed: 2 // 已完成
|
||||
};
|
||||
|
||||
// 分页处理后的数据(含排序)
|
||||
const pagedTasks = computed(() => {
|
||||
// 先按状态排序
|
||||
const sortedTasks = [...tasks.value].sort((a, b) => {
|
||||
return statusOrder[a.status] - statusOrder[b.status];
|
||||
});
|
||||
|
||||
// 再进行分页
|
||||
const startIndex = (currentPage.value - 1) * pageSize.value;
|
||||
const endIndex = startIndex + pageSize.value;
|
||||
return sortedTasks.slice(startIndex, endIndex);
|
||||
});
|
||||
|
||||
// 搜索处理
|
||||
const handleSearch = () => {
|
||||
currentPage.value = 1; // 重置到第一页
|
||||
// 实际应用中这里会根据筛选条件过滤数据
|
||||
};
|
||||
|
||||
// 创建紧急抢修任务弹窗相关
|
||||
const createTaskDialogVisible = ref(false);
|
||||
const createTaskForm = ref({
|
||||
repairName: '',
|
||||
repairType: '',
|
||||
priority: '',
|
||||
detailedDescription: '',
|
||||
faultLocation: '',
|
||||
contactPerson: '',
|
||||
contactPhone: '',
|
||||
needSupport: 'yes'
|
||||
});
|
||||
|
||||
const createTaskRules = {
|
||||
repairName: [{ required: true, message: '请输入抢修名称', trigger: 'blur' }],
|
||||
repairType: [{ required: true, message: '请选择抢修类型', trigger: 'change' }],
|
||||
priority: [{ required: true, message: '请选择紧急程度', trigger: 'change' }],
|
||||
detailedDescription: [{ required: true, message: '请输入故障描述', trigger: 'blur' }],
|
||||
faultLocation: [{ required: true, message: '请输入故障位置', trigger: 'blur' }],
|
||||
contactPerson: [{ required: true, message: '请输入联系人', trigger: 'blur' }],
|
||||
contactPhone: [{ required: true, message: '请输入联系电话', trigger: 'blur' }]
|
||||
};
|
||||
|
||||
// 创建任务
|
||||
const handleCreateTask = () => {
|
||||
createTaskDialogVisible.value = true;
|
||||
};
|
||||
|
||||
// 提交抢修计划
|
||||
const handleSaveTask = () => {
|
||||
// 模拟提交抢修计划逻辑
|
||||
console.log('提交抢修计划:', createTaskForm.value);
|
||||
// 关闭弹窗
|
||||
createTaskDialogVisible.value = false;
|
||||
// 重置表单
|
||||
createTaskForm.value = {
|
||||
repairName: '',
|
||||
repairType: '',
|
||||
priority: '',
|
||||
detailedDescription: '',
|
||||
faultLocation: '',
|
||||
contactPerson: '',
|
||||
contactPhone: '',
|
||||
needSupport: 'yes'
|
||||
};
|
||||
// 这里可以添加成功提示和刷新任务列表的逻辑
|
||||
};
|
||||
|
||||
// 取消创建紧急抢修任务
|
||||
const handleCancelCreateTask = () => {
|
||||
createTaskDialogVisible.value = false;
|
||||
// 重置表单
|
||||
createTaskForm.value = {
|
||||
repairName: '',
|
||||
repairType: '',
|
||||
priority: '',
|
||||
detailedDescription: '',
|
||||
faultLocation: '',
|
||||
contactPerson: '',
|
||||
contactPhone: '',
|
||||
needSupport: 'yes'
|
||||
};
|
||||
};
|
||||
|
||||
// 分页事件
|
||||
const handleSizeChange = (val) => {
|
||||
pageSize.value = val;
|
||||
currentPage.value = 1;
|
||||
};
|
||||
|
||||
const handleCurrentChange = (val) => {
|
||||
currentPage.value = val;
|
||||
};
|
||||
|
||||
// 查看任务详情
|
||||
const handleView = (task) => {
|
||||
console.log('查看任务详情:', task);
|
||||
};
|
||||
|
||||
// 处理选项卡点击
|
||||
const handleTabClick = () => {
|
||||
currentPage.value = 1;
|
||||
};
|
||||
|
||||
const handleInspectionManagement1 = () => {
|
||||
router.push('/rili/qiangxiuguanli');
|
||||
};
|
||||
|
||||
const handleInspectionManagement2 = () => {
|
||||
router.push('/rili/qiangxiujilu');
|
||||
};
|
||||
const handleInspection1 = () => {
|
||||
router.push('/rili/rili');
|
||||
};
|
||||
const handleInspection2 = () => {
|
||||
router.push('/rili/InspectionManagement');
|
||||
};
|
||||
const handleInspection3 = () => {
|
||||
router.push('/rili/shiyanguanli');
|
||||
};
|
||||
const handleInspection4 = () => {
|
||||
router.push('/rili/baoxiuguanli');
|
||||
};
|
||||
const handleInspection5 = () => {
|
||||
router.push('/rili/qiangxiuguanli');
|
||||
};
|
||||
const handleInspection6 = () => {
|
||||
router.push('/rili/gongdanliebiao');
|
||||
};
|
||||
const handleInspection7 = () => {
|
||||
router.push('/rili/renyuanzhuangtai');
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.inspection-tasks {
|
||||
padding: 20px;
|
||||
background-color: #f5f7fa;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
/* 任务卡片样式修改 - 增加滚动功能 */
|
||||
.task-cards {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
||||
gap: 16px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.task-card {
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||||
padding: 16px;
|
||||
transition: all 0.3s ease;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
/* 固定卡片高度 */
|
||||
min-height: 280px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* 顶部装饰条样式 */
|
||||
.task-card.status-high::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 4px;
|
||||
}
|
||||
|
||||
.task-card.status-medium::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 4px;
|
||||
}
|
||||
|
||||
.task-card.status-low::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 4px;
|
||||
}
|
||||
|
||||
.task-card.status-completed::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 4px;
|
||||
}
|
||||
|
||||
/* 添加滚动内容容器 */
|
||||
.task-content-scroll {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding-right: 8px; /* 为滚动条预留空间 */
|
||||
margin-bottom: 10px;
|
||||
/* 限制最大高度,超出则显示滚动条 */
|
||||
max-height: 180px;
|
||||
}
|
||||
|
||||
/* 滚动条样式保持不变 */
|
||||
.task-content-scroll::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
.task-content-scroll::-webkit-scrollbar-track {
|
||||
background: #f1f1f1;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.task-content-scroll::-webkit-scrollbar-thumb {
|
||||
background: #c1c1c1;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.task-content-scroll::-webkit-scrollbar-thumb:hover {
|
||||
background: #a8a8a8;
|
||||
}
|
||||
|
||||
.task-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 16px;
|
||||
padding-bottom: 12px;
|
||||
border-bottom: 1px solid #f0f2f5;
|
||||
}
|
||||
|
||||
.task-title {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #1d2129;
|
||||
line-height: 1.4;
|
||||
flex: 1;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.task-type-tag {
|
||||
padding: 4px 10px;
|
||||
border-radius: 6px;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
/* 不同故障类型的颜色 */
|
||||
/* 电力,设备故障为红色 */
|
||||
.task-type-tag.electric,
|
||||
.task-type-tag.equipment {
|
||||
background-color: #fff2f0;
|
||||
color: #ff4d4f;
|
||||
border-color: #ffccc7;
|
||||
}
|
||||
|
||||
/* 供水,设备损坏为黄色 */
|
||||
.task-type-tag.water,
|
||||
.task-type-tag.damage {
|
||||
background-color: #fffbe6;
|
||||
color: #fa8c16;
|
||||
border-color: #ffe58f;
|
||||
}
|
||||
|
||||
/* 其余为绿色 */
|
||||
.task-type-tag {
|
||||
background-color: #f6ffed;
|
||||
color: #52c41a;
|
||||
border-color: #b7eb8f;
|
||||
}
|
||||
|
||||
.task-card:hover {
|
||||
transform: translateY(-3px);
|
||||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
.task-details {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.detail-item {
|
||||
display: flex;
|
||||
margin-bottom: 8px;
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.detail-label {
|
||||
flex: 0 0 70px;
|
||||
color: #86909c;
|
||||
}
|
||||
|
||||
.detail-value {
|
||||
flex: 1;
|
||||
color: #4e5969;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.task-result {
|
||||
display: flex;
|
||||
margin: 8px 0;
|
||||
font-size: 12px;
|
||||
padding-top: 8px;
|
||||
border-top: 1px dashed #f0f2f5;
|
||||
}
|
||||
|
||||
.task-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
padding-top: 16px;
|
||||
border-top: 1px solid #f0f2f5;
|
||||
margin-top: auto; /* 自动推到最底部 */
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
font-size: 12px;
|
||||
padding: 4px 12px;
|
||||
}
|
||||
|
||||
.view-btn {
|
||||
color: #165dff;
|
||||
}
|
||||
|
||||
.view-btn:hover {
|
||||
color: #0e42d2;
|
||||
background-color: #e8f3ff;
|
||||
}
|
||||
|
||||
.follow-btn {
|
||||
background-color: #165dff;
|
||||
border-color: #165dff;
|
||||
}
|
||||
|
||||
.follow-btn:hover {
|
||||
background-color: #0e42d2;
|
||||
border-color: #0e42d2;
|
||||
}
|
||||
|
||||
.assign-btn {
|
||||
background-color: #ff7d00;
|
||||
border-color: #ff7d00;
|
||||
}
|
||||
|
||||
.assign-btn:hover {
|
||||
background-color: #e86a00;
|
||||
border-color: #e86a00;
|
||||
}
|
||||
|
||||
/* 其他样式保持不变 */
|
||||
.tabs-wrapper {
|
||||
background-color: #fff;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 16px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.filter-bar {
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 24px;
|
||||
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05);
|
||||
padding: 16px 24px;
|
||||
}
|
||||
|
||||
.filter-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 16px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.filter-item {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.filter-bar .el-select {
|
||||
width: 180px;
|
||||
height: 36px;
|
||||
}
|
||||
|
||||
.filter-actions {
|
||||
margin-left: auto;
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.search-btn {
|
||||
background-color: #f2f3f5;
|
||||
color: #303133;
|
||||
border-color: #f2f3f5;
|
||||
}
|
||||
|
||||
.create-btn {
|
||||
background-color: #165dff;
|
||||
border-color: #165dff;
|
||||
}
|
||||
|
||||
.pagination-section {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.navigation-tabs {
|
||||
display: flex;
|
||||
margin-bottom: 20px;
|
||||
background-color: #fff;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08);
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.nav-tab {
|
||||
padding: 12px 24px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
color: #606266;
|
||||
border-right: 1px solid #f0f0f0;
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.nav-tab:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.nav-tab:hover {
|
||||
color: #409eff;
|
||||
background-color: #ecf5ff;
|
||||
}
|
||||
|
||||
.nav-tab.active {
|
||||
background-color: #409eff;
|
||||
color: #fff;
|
||||
box-shadow: 0 2px 4px rgba(64, 158, 255, 0.3);
|
||||
}
|
||||
|
||||
/* 上传图片区域样式 */
|
||||
.upload-container {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.upload-box {
|
||||
border: 2px dashed #dcdfe6;
|
||||
border-radius: 8px;
|
||||
padding: 40px 20px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
|
||||
.avatar-uploader-icon {
|
||||
font-size: 40px;
|
||||
color: #c0c4cc;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.upload-text {
|
||||
font-size: 14px;
|
||||
color: #606266;
|
||||
font-weight: 500;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.upload-hint {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
/* 弹窗和表单样式保持不变 */
|
||||
.beautiful-dialog {
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.15);
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.elegant-form .el-form-item {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 1200px) {
|
||||
.task-cards {
|
||||
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.inspection-tasks {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.filter-container {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.filter-actions {
|
||||
margin-left: 0;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.filter-bar .el-select {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.task-cards {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
741
src/views/dhr_demo/qiangxiujilu.vue
Normal file
741
src/views/dhr_demo/qiangxiujilu.vue
Normal file
@ -0,0 +1,741 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="inspection-tasks">
|
||||
<!-- 导航栏 -->
|
||||
<div class="navigation-tabs">
|
||||
<div class="nav-tab" @click="handleInspection1">待办事项</div>
|
||||
<div class="nav-tab" @click="handleInspection2">巡检管理</div>
|
||||
<div class="nav-tab" @click="handleInspection3">试验管理</div>
|
||||
<div class="nav-tab" @click="handleInspection4">报修管理</div>
|
||||
<div class="nav-tab active" @click="handleInspection5">抢修管理</div>
|
||||
<div class="nav-tab" @click="handleInspection6">工单管理</div>
|
||||
<div class="nav-tab" @click="handleInspection7">运维组织</div>
|
||||
</div>
|
||||
|
||||
<!-- 页面标题和操作区 -->
|
||||
<div class="header-section">
|
||||
<TitleComponent title="抢修管理模块" subtitle="处理紧急抢修任务,跟踪抢修进度和记录"></TitleComponent>
|
||||
<div class="header-actions">
|
||||
<el-button class="filter-btn" @click="showFilter = !showFilter">
|
||||
筛选
|
||||
<i class="el-icon-arrow-down ml-1"></i>
|
||||
</el-button>
|
||||
<el-button type="primary" class="export-btn" @click="handleExport"> 导出数据 </el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tabs-wrapper">
|
||||
<div style="display: flex; align-items: center; gap: 10px">
|
||||
<el-button type="primary" @click="handleInspectionManagement1">抢修任务</el-button>
|
||||
<el-button type="primary" @click="handleInspectionManagement2">抢修记录</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 筛选栏 (默认隐藏) -->
|
||||
<div class="filter-bar" v-if="showFilter">
|
||||
<div class="filter-container">
|
||||
<div class="filter-item">
|
||||
<el-select v-model="taskStatus" placeholder="任务状态">
|
||||
<el-option label="待执行" value="pending"></el-option>
|
||||
<el-option label="处理中" value="processing"></el-option>
|
||||
<el-option label="已完成" value="completed"></el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<el-select v-model="priority" placeholder="紧急程度">
|
||||
<el-option label="紧急" value="urgent"></el-option>
|
||||
<el-option label="常规" value="normal"></el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<el-select v-model="executor" placeholder="抢修人员">
|
||||
<el-option label="全部人员" value="all"></el-option>
|
||||
<el-option label="李明" value="liming"></el-option>
|
||||
<el-option label="王伟" value="wangwei"></el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<el-date-picker
|
||||
v-model="dateRange"
|
||||
type="datetimerange"
|
||||
start-placeholder="开始时间"
|
||||
end-placeholder="结束时间"
|
||||
format="YYYY-MM-DD HH:mm"
|
||||
value-format="YYYY-MM-DD HH:mm"
|
||||
/>
|
||||
</div>
|
||||
<div class="filter-actions">
|
||||
<el-button type="primary" class="search-btn" @click="handleSearch"> 搜索 </el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 统计卡片区域 -->
|
||||
<div class="statistics-container">
|
||||
<div class="stat-card">
|
||||
<div class="stat-info">
|
||||
<p class="stat-label">本月抢修总数</p>
|
||||
<p class="stat-value">18</p>
|
||||
<p class="stat-trend up">较上月 +8.7%</p>
|
||||
</div>
|
||||
<div class="stat-icon">
|
||||
<i class="el-icon-warning-outline"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="stat-card">
|
||||
<div class="stat-info">
|
||||
<p class="stat-label">平均抢修时长</p>
|
||||
<p class="stat-value">58分钟</p>
|
||||
<p class="stat-trend down">较上月 -2.5分钟</p>
|
||||
</div>
|
||||
<div class="stat-icon">
|
||||
<i class="el-icon-clock"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="stat-card">
|
||||
<div class="stat-info">
|
||||
<p class="stat-label">待处理抢修</p>
|
||||
<p class="stat-value">3</p>
|
||||
<p class="stat-trend warning">需要尽快处理</p>
|
||||
</div>
|
||||
<div class="stat-icon warning">
|
||||
<i class="el-icon-alarm"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="stat-card">
|
||||
<div class="stat-info">
|
||||
<p class="stat-label">按时完成率</p>
|
||||
<p class="stat-value">92%</p>
|
||||
<p class="stat-trend up">较上月 +2.4%</p>
|
||||
</div>
|
||||
<div class="stat-icon success">
|
||||
<i class="el-icon-check-circle"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 抢修记录表格 -->
|
||||
<div class="table-container">
|
||||
<el-table :data="repairRecords" border style="width: 100%" class="record-table">
|
||||
<el-table-column prop="reportNo" label="抢修单号" width="140"></el-table-column>
|
||||
<el-table-column prop="content" label="抢修内容"></el-table-column>
|
||||
<el-table-column prop="reporter" label="报修人" width="100"></el-table-column>
|
||||
<el-table-column prop="reportTime" label="报修时间" width="160"></el-table-column>
|
||||
<el-table-column prop="handler" label="抢修人员" width="100"></el-table-column>
|
||||
<el-table-column prop="priority" label="紧急程度" width="100">
|
||||
<template #default="scope">
|
||||
<span :class="`priority-tag ${scope.row.priorityClass}`">{{ scope.row.priority }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="status" label="处理状态" width="100">
|
||||
<template #default="scope">
|
||||
<span :class="`status-tag ${scope.row.statusClass}`">{{ scope.row.status }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="duration" label="处理时长" width="100"></el-table-column>
|
||||
<el-table-column label="操作" width="140">
|
||||
<template #default="scope">
|
||||
<el-button type="text" size="small" class="detail-btn" @click="handleDetail(scope.row)"> 详情 </el-button>
|
||||
<el-button type="text" size="small" :class="scope.row.actionClass" @click="handleAction(scope.row)">
|
||||
{{ scope.row.actionText }}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<!-- 分页区域 -->
|
||||
<div class="pagination-section">
|
||||
<div class="pagination-controls">
|
||||
<el-pagination
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
:current-page="currentPage"
|
||||
:page-sizes="[10, 20, 30, 50]"
|
||||
:page-size="pageSize"
|
||||
layout="prev, pager, next, jumper"
|
||||
:total="total"
|
||||
background
|
||||
>
|
||||
</el-pagination>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue';
|
||||
import router from '@/router';
|
||||
import TitleComponent from '@/views/demo/components/TitleComponent.vue';
|
||||
|
||||
// 筛选条件
|
||||
const taskStatus = ref('');
|
||||
const priority = ref('');
|
||||
const executor = ref('');
|
||||
const dateRange = ref([]);
|
||||
const showFilter = ref(false);
|
||||
|
||||
// 分页相关
|
||||
const currentPage = ref(3);
|
||||
const pageSize = ref(10);
|
||||
const total = ref(187);
|
||||
|
||||
// 抢修记录数据
|
||||
const repairRecords = ref([
|
||||
{
|
||||
reportNo: 'R-2025-0620-001',
|
||||
content: '网络主干交换机故障AAAAAA',
|
||||
reporter: '陈明',
|
||||
reportTime: '2025-06-20 08:30',
|
||||
handler: '李明',
|
||||
priority: '紧急',
|
||||
priorityClass: 'urgent',
|
||||
status: '已完成',
|
||||
statusClass: 'completed',
|
||||
duration: '10分钟',
|
||||
actionText: '详情',
|
||||
actionClass: 'detail-btn'
|
||||
},
|
||||
{
|
||||
reportNo: 'R-2025-0620-002',
|
||||
content: '实验室水管爆裂',
|
||||
reporter: '陈明',
|
||||
reportTime: '2025-06-20 08:30',
|
||||
handler: '李明',
|
||||
priority: '紧急',
|
||||
priorityClass: 'urgent',
|
||||
status: '抢修中',
|
||||
statusClass: 'processing',
|
||||
duration: '4分钟',
|
||||
actionText: '查看',
|
||||
actionClass: 'follow-btn'
|
||||
},
|
||||
{
|
||||
reportNo: 'R-2025-0620-003',
|
||||
content: '主配电室线路故障',
|
||||
reporter: '陈明',
|
||||
reportTime: '2025-06-20 08:30',
|
||||
handler: '李明',
|
||||
priority: '紧急',
|
||||
priorityClass: 'urgent',
|
||||
status: '已完成',
|
||||
statusClass: 'completed',
|
||||
duration: '10分钟',
|
||||
actionText: '详情',
|
||||
actionClass: 'detail-btn'
|
||||
},
|
||||
{
|
||||
reportNo: 'R-2025-0620-004',
|
||||
content: '网络主干交换机故障AAAAAA',
|
||||
reporter: '陈明',
|
||||
reportTime: '2025-06-20 08:30',
|
||||
handler: '李明',
|
||||
priority: '常规',
|
||||
priorityClass: 'normal',
|
||||
status: '已完成',
|
||||
statusClass: 'completed',
|
||||
duration: '10分钟',
|
||||
actionText: '详情',
|
||||
actionClass: 'detail-btn'
|
||||
},
|
||||
{
|
||||
reportNo: 'R-2025-0620-005',
|
||||
content: '网络主干交换机故障AAAAAA',
|
||||
reporter: '陈明',
|
||||
reportTime: '2025-06-20 08:30',
|
||||
handler: '李明',
|
||||
priority: '紧急',
|
||||
priorityClass: 'urgent',
|
||||
status: '已完成',
|
||||
statusClass: 'completed',
|
||||
duration: '10分钟',
|
||||
actionText: '详情',
|
||||
actionClass: 'detail-btn'
|
||||
},
|
||||
{
|
||||
reportNo: 'R-2025-0620-006',
|
||||
content: '网络主干交换机故障AAAAAA',
|
||||
reporter: '陈明',
|
||||
reportTime: '2025-06-20 08:30',
|
||||
handler: '李明',
|
||||
priority: '紧急',
|
||||
priorityClass: 'urgent',
|
||||
status: '已完成',
|
||||
statusClass: 'completed',
|
||||
duration: '10分钟',
|
||||
actionText: '详情',
|
||||
actionClass: 'detail-btn'
|
||||
},
|
||||
{
|
||||
reportNo: 'R-2025-0620-007',
|
||||
content: '网络主干交换机故障AAAAAA',
|
||||
reporter: '陈明',
|
||||
reportTime: '2025-06-20 08:30',
|
||||
handler: '李明',
|
||||
priority: '紧急',
|
||||
priorityClass: 'urgent',
|
||||
status: '已完成',
|
||||
statusClass: 'completed',
|
||||
duration: '10分钟',
|
||||
actionText: '详情',
|
||||
actionClass: 'detail-btn'
|
||||
}
|
||||
]);
|
||||
|
||||
// 搜索处理
|
||||
const handleSearch = () => {
|
||||
currentPage.value = 1; // 重置到第一页
|
||||
// 实际应用中这里会根据筛选条件过滤数据
|
||||
};
|
||||
|
||||
// 导出数据
|
||||
const handleExport = () => {
|
||||
console.log('导出抢修记录数据');
|
||||
// 实际应用中添加导出逻辑
|
||||
};
|
||||
|
||||
// 分页事件
|
||||
const handleSizeChange = (val) => {
|
||||
pageSize.value = val;
|
||||
currentPage.value = 1;
|
||||
};
|
||||
|
||||
const handleCurrentChange = (val) => {
|
||||
currentPage.value = val;
|
||||
};
|
||||
|
||||
// 查看详情
|
||||
const handleDetail = (record) => {
|
||||
console.log('查看详情:', record);
|
||||
};
|
||||
|
||||
// 处理操作
|
||||
const handleAction = (record) => {
|
||||
console.log('执行操作:', record.actionText, record);
|
||||
};
|
||||
|
||||
// 选项卡切换
|
||||
const handleTaskTab = () => {
|
||||
// 抢修任务选项卡逻辑
|
||||
};
|
||||
|
||||
const handleRecordTab = () => {
|
||||
router.push('/rili/qiangxiujiilu');
|
||||
};
|
||||
|
||||
// 导航事件
|
||||
const handleInspection1 = () => {
|
||||
router.push('/rili/rili');
|
||||
};
|
||||
|
||||
const handleInspection2 = () => {
|
||||
router.push('/rili/InspectionManagement');
|
||||
};
|
||||
|
||||
const handleInspection3 = () => {
|
||||
router.push('/rili/shiyanguanli');
|
||||
};
|
||||
|
||||
const handleInspection4 = () => {
|
||||
router.push('/rili/baoxiuguanli');
|
||||
};
|
||||
|
||||
const handleInspection5 = () => {
|
||||
router.push('/rili/qiangxiuguanli');
|
||||
};
|
||||
|
||||
const handleInspection6 = () => {
|
||||
router.push('/rili/gongdanliebiao');
|
||||
};
|
||||
const handleInspection7 = () => {
|
||||
router.push('/rili/renyuanzhuangtai');
|
||||
};
|
||||
|
||||
const handleInspectionManagement1 = () => {
|
||||
router.push('/rili/qiangxiuguanli');
|
||||
};
|
||||
|
||||
const handleInspectionManagement2 = () => {
|
||||
router.push('/rili/qiangxiujilu');
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.inspection-tasks {
|
||||
padding: 20px;
|
||||
background-color: #f5f7fa;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
/* 头部标题和操作区 */
|
||||
.header-section {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.header-actions {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.filter-btn {
|
||||
background-color: #fff;
|
||||
color: #303133;
|
||||
border-color: #dcdfe6;
|
||||
}
|
||||
|
||||
.export-btn {
|
||||
background-color: #165dff;
|
||||
border-color: #165dff;
|
||||
}
|
||||
|
||||
/* 选项卡样式 */
|
||||
.tabs-wrapper {
|
||||
background-color: #fff;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 16px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
/* 筛选栏样式 */
|
||||
.filter-bar {
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 24px;
|
||||
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05);
|
||||
padding: 16px 24px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.filter-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 16px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.filter-item {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.filter-bar .el-select,
|
||||
.filter-bar .el-date-picker {
|
||||
width: 180px;
|
||||
height: 36px;
|
||||
}
|
||||
|
||||
.filter-bar .el-select .el-input__inner,
|
||||
.filter-bar .el-date-picker .el-input__inner {
|
||||
border-radius: 4px;
|
||||
border-color: #dcdfe6;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.filter-bar .el-select .el-input__inner:focus,
|
||||
.filter-bar .el-date-picker .el-input__inner:focus {
|
||||
border-color: #165dff;
|
||||
box-shadow: 0 0 0 2px rgba(22, 93, 255, 0.1);
|
||||
}
|
||||
|
||||
.filter-actions {
|
||||
margin-left: auto;
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.search-btn {
|
||||
background-color: #f2f3f5;
|
||||
color: #303133;
|
||||
border-color: #f2f3f5;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.search-btn:hover {
|
||||
background-color: #e5e6eb;
|
||||
color: #303133;
|
||||
border-color: #e5e6eb;
|
||||
}
|
||||
|
||||
/* 统计卡片样式 */
|
||||
.statistics-container {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
|
||||
gap: 20px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||||
padding: 20px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.stat-card:hover {
|
||||
transform: translateY(-3px);
|
||||
}
|
||||
|
||||
.stat-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 14px;
|
||||
color: #86909c;
|
||||
margin: 0 0 8px 0;
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
color: #1d2129;
|
||||
margin: 0 0 4px 0;
|
||||
}
|
||||
|
||||
.stat-trend {
|
||||
font-size: 12px;
|
||||
margin: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.stat-trend.up {
|
||||
color: #00b42a;
|
||||
}
|
||||
|
||||
.stat-trend.up::before {
|
||||
content: '↑';
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.stat-trend.down {
|
||||
color: #ff4d4f;
|
||||
}
|
||||
|
||||
.stat-trend.down::before {
|
||||
content: '↓';
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.stat-trend.warning {
|
||||
color: #fa8c16;
|
||||
}
|
||||
|
||||
.stat-icon {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 50%;
|
||||
background-color: #ffebe6;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #ff4d4f;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.stat-icon.warning {
|
||||
background-color: #fff7e6;
|
||||
color: #fa8c16;
|
||||
}
|
||||
|
||||
.stat-icon.success {
|
||||
background-color: #f6ffed;
|
||||
color: #52c41a;
|
||||
}
|
||||
|
||||
/* 表格样式 */
|
||||
.table-container {
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05);
|
||||
padding: 16px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.record-table {
|
||||
border-collapse: separate;
|
||||
border-spacing: 0;
|
||||
}
|
||||
|
||||
.record-table th {
|
||||
background-color: #f7f8fa;
|
||||
color: #4e5969;
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.record-table td {
|
||||
color: #1d2129;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.status-tag {
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.status-tag.processing {
|
||||
background-color: #fffbe6;
|
||||
color: #faad14;
|
||||
border: 1px solid #fff1b8;
|
||||
}
|
||||
|
||||
.status-tag.completed {
|
||||
background-color: #f0f9eb;
|
||||
color: #52c41a;
|
||||
border: 1px solid #e1f3d8;
|
||||
}
|
||||
|
||||
.priority-tag {
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.priority-tag.urgent {
|
||||
background-color: #ffebe6;
|
||||
color: #ff4d4f;
|
||||
border: 1px solid #ffccc7;
|
||||
}
|
||||
|
||||
.priority-tag.normal {
|
||||
background-color: #e6f7ff;
|
||||
color: #1890ff;
|
||||
border: 1px solid #b3d8ff;
|
||||
}
|
||||
|
||||
.detail-btn {
|
||||
color: #165dff;
|
||||
}
|
||||
|
||||
.follow-btn {
|
||||
color: #fa8c16;
|
||||
}
|
||||
|
||||
/* 分页区域样式 */
|
||||
.pagination-section {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.pagination-controls .el-pagination {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.pagination-controls .el-pagination button,
|
||||
.pagination-controls .el-pagination .el-pager li {
|
||||
min-width: 36px;
|
||||
height: 36px;
|
||||
line-height: 36px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.pagination-controls .el-pagination .el-pager li.active {
|
||||
background-color: #165dff;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* 导航栏样式 */
|
||||
.navigation-tabs {
|
||||
display: flex;
|
||||
margin-bottom: 20px;
|
||||
background-color: #fff;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08);
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.nav-tab {
|
||||
padding: 12px 24px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
color: #606266;
|
||||
border-right: 1px solid #f0f0f0;
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.nav-tab:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.nav-tab:hover {
|
||||
color: #409eff;
|
||||
background-color: #ecf5ff;
|
||||
}
|
||||
|
||||
.nav-tab.active {
|
||||
background-color: #409eff;
|
||||
color: #fff;
|
||||
box-shadow: 0 2px 4px rgba(64, 158, 255, 0.3);
|
||||
}
|
||||
|
||||
.nav-tab {
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 1200px) {
|
||||
.statistics-container {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.inspection-tasks {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.header-section {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.header-actions {
|
||||
width: 100%;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.filter-container {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.filter-actions {
|
||||
margin-left: 0;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.filter-bar .el-select,
|
||||
.filter-bar .el-date-picker {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.statistics-container {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.table-container {
|
||||
overflow-x: auto;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
866
src/views/dhr_demo/renyuanzhuangtai.vue
Normal file
866
src/views/dhr_demo/renyuanzhuangtai.vue
Normal file
@ -0,0 +1,866 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="operation-organization">
|
||||
<!-- 顶部导航栏 -->
|
||||
<div class="navigation-tabs">
|
||||
<div class="nav-tab" @click="handleInspection1">待办事项</div>
|
||||
<div class="nav-tab" @click="handleInspection2">巡检管理</div>
|
||||
<div class="nav-tab" @click="handleInspection3">试验管理</div>
|
||||
<div class="nav-tab" @click="handleInspection4">报修管理</div>
|
||||
<div class="nav-tab" @click="handleInspection5">抢修管理</div>
|
||||
<div class="nav-tab" @click="handleInspection6">工单管理</div>
|
||||
<div class="nav-tab active" @click="handleInspection7">运维组织</div>
|
||||
</div>
|
||||
|
||||
<!-- 页面标题 -->
|
||||
<TitleComponent title="运维组织模块" subtitle="实时监控人员状态、车辆状态和班组状态"></TitleComponent>
|
||||
|
||||
<!-- 选项卡 -->
|
||||
<!-- 选项卡和按钮组合 -->
|
||||
<div class="tabs-wrapper">
|
||||
<div style="display: flex; align-items: center; gap: 10px">
|
||||
<el-button type="primary" @click="handleInspectionManagement1">人员状态</el-button>
|
||||
<el-button type="primary" @click="handleInspectionManagement2">车辆状态</el-button>
|
||||
<el-button type="primary" @click="handleInspectionManagement3">班组状态</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 内容区域 -->
|
||||
<div class="content-container">
|
||||
<!-- 左侧数据概览区域 -->
|
||||
<div class="sidebar">
|
||||
<div class="stats-card">
|
||||
<h3 class="stats-title">人员数据总览</h3>
|
||||
<div class="chart-container">
|
||||
<div class="gauge-chart">
|
||||
<div class="doughnut-chart">
|
||||
<!-- 动态镂空环形图 -->
|
||||
<svg width="200" height="200" viewBox="0 0 200 200">
|
||||
<!-- 环形背景 -->
|
||||
<circle cx="100" cy="100" r="70" fill="none" stroke="#f0f2f5" stroke-width="12" class="background-circle" />
|
||||
<!-- 在线可用部分 - 动态显示 -->
|
||||
<circle
|
||||
cx="100"
|
||||
cy="100"
|
||||
r="70"
|
||||
fill="none"
|
||||
stroke="#165dff"
|
||||
stroke-width="12"
|
||||
stroke-linecap="round"
|
||||
:stroke-dasharray="circumference"
|
||||
:stroke-dashoffset="onlineOffset"
|
||||
transform="rotate(-90 100 100)"
|
||||
class="progress-circle online-progress"
|
||||
/>
|
||||
<!-- 离线休息部分 - 动态显示 -->
|
||||
<circle
|
||||
cx="100"
|
||||
cy="100"
|
||||
r="70"
|
||||
fill="none"
|
||||
stroke="#40c9c6"
|
||||
stroke-width="12"
|
||||
stroke-linecap="round"
|
||||
:stroke-dasharray="circumference"
|
||||
:stroke-dashoffset="offlineOffset"
|
||||
transform="rotate(-90 100 100)"
|
||||
class="progress-circle offline-progress"
|
||||
/>
|
||||
</svg>
|
||||
<div class="gauge-text">
|
||||
<div class="gauge-percentage">{{ onlineRate }}%</div>
|
||||
<div class="gauge-label">在线可用率</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="chart-legend">
|
||||
<div class="legend-item">
|
||||
<span class="legend-color online"></span>
|
||||
<span class="legend-text">在线可用人数 ({{ availablePersonnel }})</span>
|
||||
</div>
|
||||
<div class="legend-item">
|
||||
<span class="legend-color offline"></span>
|
||||
<span class="legend-text">离线休息人数 ({{ restingPersonnel }})</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="stats-grid">
|
||||
<div class="stat-item">
|
||||
<div class="stat-value">{{ totalPersonnel }}</div>
|
||||
<div class="stat-label">总人数</div>
|
||||
<div class="stat-change">+2人</div>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<div class="stat-value">{{ availablePersonnel }}</div>
|
||||
<div class="stat-label">在线可用</div>
|
||||
<div class="stat-change">+2人</div>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<div class="stat-value">{{ workingPersonnel }}</div>
|
||||
<div class="stat-label">工作中</div>
|
||||
<div class="stat-change">+1人</div>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<div class="stat-value">{{ restingPersonnel }}</div>
|
||||
<div class="stat-label">离线休息</div>
|
||||
<div class="stat-change">-1人</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 右侧人员列表区域,带滚动条 -->
|
||||
<div class="main-content">
|
||||
<div class="scrollable-content">
|
||||
<div class="personnel-grid">
|
||||
<div v-for="(person, index) in personnelList" :key="index" class="person-card">
|
||||
<div class="person-header">
|
||||
<div class="avatar">
|
||||
<img :src="person.avatar" alt="头像" class="avatar-img" />
|
||||
<div class="status-indicator" :class="person.statusClass"></div>
|
||||
</div>
|
||||
<div class="person-info">
|
||||
<div class="person-name">{{ person.name }}</div>
|
||||
<div class="person-status">{{ person.statusText }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="person-details">
|
||||
<div class="detail-item">工号: {{ person.id }}</div>
|
||||
<div class="detail-item">岗位: {{ person.position }}</div>
|
||||
<div class="detail-item">班组: {{ person.team }}</div>
|
||||
<div class="detail-item">今日完成: {{ person.completedTasks }}单</div>
|
||||
<div class="detail-item" v-if="person.currentTask">当前任务: {{ person.currentTask }}</div>
|
||||
<div class="detail-item" v-else>当前任务: 无</div>
|
||||
</div>
|
||||
<div class="person-actions">
|
||||
<el-button type="text" @click="viewDetails(person)" size="small" class="detail-btn">详情</el-button>
|
||||
<el-button type="text" @click="assignTask(person)" size="small" class="assign-btn">指派</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch } from 'vue';
|
||||
import router from '@/router';
|
||||
import TitleComponent from '@/views/demo/components/TitleComponent.vue';
|
||||
|
||||
// 激活的选项卡
|
||||
const activeTab = ref('personnel');
|
||||
|
||||
// 统计数据
|
||||
const totalPersonnel = ref(36);
|
||||
const availablePersonnel = ref(18);
|
||||
const workingPersonnel = ref(12);
|
||||
const restingPersonnel = ref(6);
|
||||
const onlineRate = ref(82);
|
||||
|
||||
// 环形图相关计算
|
||||
const radius = 70;
|
||||
const circumference = 2 * Math.PI * radius;
|
||||
const onlineOffset = ref(circumference - (onlineRate.value / 100) * circumference);
|
||||
const offlineOffset = ref(circumference - ((100 - onlineRate.value) / 100) * circumference);
|
||||
|
||||
// 监听onlineRate变化,更新环形图
|
||||
watch(onlineRate, (newRate) => {
|
||||
onlineOffset.value = circumference - (newRate / 100) * circumference;
|
||||
offlineOffset.value = circumference - ((100 - newRate) / 100) * circumference;
|
||||
});
|
||||
|
||||
// 人员列表数据
|
||||
const personnelList = ref([
|
||||
{
|
||||
id: 'EMP-2023-001',
|
||||
name: '张工',
|
||||
position: '设备维护工程师',
|
||||
team: '第一运维组',
|
||||
statusText: '在线可用',
|
||||
statusClass: 'online',
|
||||
completedTasks: 2,
|
||||
currentTask: '',
|
||||
avatar: 'https://p9-flow-imagex-sign.byteimg.com/tos-cn-i-a9rns2rl98/937facf77da3466fafaf9ff8f0223333.png~tplv-a9rns2rl98-24:720:720.png'
|
||||
},
|
||||
{
|
||||
id: 'EMP-2023-006',
|
||||
name: '李工',
|
||||
position: '系统工程师',
|
||||
team: '第三运维组',
|
||||
statusText: '工作中',
|
||||
statusClass: 'working',
|
||||
completedTasks: 1,
|
||||
currentTask: 'WO-2023-0619-055',
|
||||
avatar: 'https://p3-flow-imagex-sign.byteimg.com/tos-cn-i-a9rns2rl98/63a989286b91488ca0c4a0141041ea41.png~tplv-a9rns2rl98-24:720:720.png'
|
||||
},
|
||||
{
|
||||
id: 'EMP-2023-015',
|
||||
name: '刘工',
|
||||
position: '安全检查工程师',
|
||||
team: '第一运维组',
|
||||
statusText: '工作中',
|
||||
statusClass: 'working',
|
||||
completedTasks: 0,
|
||||
currentTask: 'WO-2023-0618-054',
|
||||
avatar: 'https://p3-flow-imagex-sign.byteimg.com/tos-cn-i-a9rns2rl98/0a6cf54a4a1c4623b8365939c8d61adc.png~tplv-a9rns2rl98-24:720:720.png'
|
||||
},
|
||||
{
|
||||
id: 'EMP-2023-022',
|
||||
name: '孙工',
|
||||
position: '安装调试工程师',
|
||||
team: '第三运维组',
|
||||
statusText: '离线休息',
|
||||
statusClass: 'offline',
|
||||
completedTasks: 2,
|
||||
currentTask: '',
|
||||
avatar: 'https://p3-flow-imagex-sign.byteimg.com/tos-cn-i-a9rns2rl98/f3e766fffb5d4573945ef7501894c461.png~tplv-a9rns2rl98-24:720:720.png'
|
||||
},
|
||||
{
|
||||
id: 'EMP-2023-008',
|
||||
name: '李工',
|
||||
position: '系统工程师',
|
||||
team: '第三运维组',
|
||||
statusText: '工作中',
|
||||
statusClass: 'working',
|
||||
completedTasks: 1,
|
||||
currentTask: 'WO-2023-0619-055',
|
||||
avatar: 'https://p3-flow-imagex-sign.byteimg.com/tos-cn-i-a9rns2rl98/d315aa56eb894980bf090804594ccf13.png~tplv-a9rns2rl98-24:720:720.png'
|
||||
},
|
||||
{
|
||||
id: 'EMP-2023-016',
|
||||
name: '刘工',
|
||||
position: '安全检查工程师',
|
||||
team: '第一运维组',
|
||||
statusText: '工作中',
|
||||
statusClass: 'working',
|
||||
completedTasks: 0,
|
||||
currentTask: 'WO-2023-0618-054',
|
||||
avatar: 'https://p9-flow-imagex-sign.byteimg.com/tos-cn-i-a9rns2rl98/937facf77da3466fafaf9ff8f0223333.png~tplv-a9rns2rl98-24:720:720.png'
|
||||
},
|
||||
{
|
||||
id: 'EMP-2023-002',
|
||||
name: '张工',
|
||||
position: '设备维护工程师',
|
||||
team: '第一运维组',
|
||||
statusText: '在线可用',
|
||||
statusClass: 'online',
|
||||
completedTasks: 2,
|
||||
currentTask: '',
|
||||
avatar: 'https://p3-flow-imagex-sign.byteimg.com/tos-cn-i-a9rns2rl98/63a989286b91488ca0c4a0141041ea41.png~tplv-a9rns2rl98-24:720:720.png'
|
||||
},
|
||||
{
|
||||
id: 'EMP-2023-009',
|
||||
name: '李工',
|
||||
position: '系统工程师',
|
||||
team: '第三运维组',
|
||||
statusText: '工作中',
|
||||
statusClass: 'working',
|
||||
completedTasks: 1,
|
||||
currentTask: 'WO-2023-0619-055',
|
||||
avatar: 'https://p3-flow-imagex-sign.byteimg.com/tos-cn-i-a9rns2rl98/0a6cf54a4a1c4623b8365939c8d61adc.png~tplv-a9rns2rl98-24:720:720.png'
|
||||
},
|
||||
{
|
||||
id: 'EMP-2023-017',
|
||||
name: '刘工',
|
||||
position: '安全检查工程师',
|
||||
team: '第一运维组',
|
||||
statusText: '工作中',
|
||||
statusClass: 'working',
|
||||
completedTasks: 0,
|
||||
currentTask: 'WO-2023-0618-054',
|
||||
avatar: 'https://p3-flow-imagex-sign.byteimg.com/tos-cn-i-a9rns2rl98/f3e766fffb5d4573945ef7501894c461.png~tplv-a9rns2rl98-24:720:720.png'
|
||||
}
|
||||
]);
|
||||
|
||||
// 选项卡点击事件
|
||||
const handleTabClick = (tab) => {
|
||||
console.log('切换到选项卡:', tab.name);
|
||||
// 根据选择的选项卡加载不同数据
|
||||
if (tab.name === 'vehicles') {
|
||||
// 加载车辆状态数据
|
||||
} else if (tab.name === 'teams') {
|
||||
// 加载班组状态数据
|
||||
} else {
|
||||
// 加载人员状态数据
|
||||
}
|
||||
};
|
||||
|
||||
// 人员卡片操作
|
||||
const viewDetails = (person) => {
|
||||
console.log('查看详情:', person);
|
||||
// 跳转到人员详情页
|
||||
};
|
||||
|
||||
const assignTask = (person) => {
|
||||
console.log('指派任务给:', person);
|
||||
// 打开任务指派对话框
|
||||
};
|
||||
|
||||
// 导航路由跳转
|
||||
const handleInspection1 = () => {
|
||||
router.push('/rili/rili');
|
||||
};
|
||||
const handleInspection2 = () => {
|
||||
router.push('/rili/InspectionManagement');
|
||||
};
|
||||
const handleInspection3 = () => {
|
||||
router.push('/rili/shiyanguanli');
|
||||
};
|
||||
const handleInspection4 = () => {
|
||||
router.push('/rili/baoxiuguanli');
|
||||
};
|
||||
const handleInspection5 = () => {
|
||||
router.push('/rili/qiangxiuguanli');
|
||||
};
|
||||
const handleInspection6 = () => {
|
||||
router.push('/rili/gongdanliebiao');
|
||||
};
|
||||
const handleInspection7 = () => {
|
||||
router.push('/rili/renyuanzhuangtai');
|
||||
};
|
||||
const handleInspectionManagement1 = () => {
|
||||
router.push('/rili/renyuanzhuangtai');
|
||||
};
|
||||
const handleInspectionManagement2 = () => {
|
||||
router.push('/rili/cheliangzhuangtai');
|
||||
};
|
||||
const handleInspectionManagement3 = () => {
|
||||
router.push('/rili/banzhuzhuangtai');
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.operation-organization {
|
||||
padding: 20px;
|
||||
background-color: #f5f7fa;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
/* 选项卡样式 */
|
||||
.tabs-wrapper {
|
||||
background-color: #fff;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 16px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.custom-tabs {
|
||||
padding-top: 1px;
|
||||
}
|
||||
|
||||
.custom-tabs .el-tabs__header {
|
||||
margin: 0 -20px;
|
||||
padding: 0 20px;
|
||||
border-bottom: 1px solid #e4e7ed;
|
||||
}
|
||||
|
||||
.custom-tabs .el-tabs__nav-wrap::after {
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.custom-tabs .el-tabs__item {
|
||||
font-size: 14px;
|
||||
color: #606266;
|
||||
padding: 16px 20px;
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
.custom-tabs .el-tabs__item.is-active {
|
||||
color: #165dff;
|
||||
font-weight: 500;
|
||||
border-bottom: 2px solid #165dff;
|
||||
}
|
||||
|
||||
.custom-tabs .el-tabs__item:hover {
|
||||
color: #165dff;
|
||||
}
|
||||
|
||||
/* 内容容器样式 */
|
||||
.content-container {
|
||||
display: flex;
|
||||
gap: 24px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* 左侧边栏样式 */
|
||||
.sidebar {
|
||||
width: 320px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.stats-card {
|
||||
background-color: #fff;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.06);
|
||||
padding: 24px;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.stats-title {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: #2c3e50;
|
||||
margin: 0 0 24px 0;
|
||||
padding-bottom: 16px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
/* 图表容器 */
|
||||
.chart-container {
|
||||
margin-bottom: 32px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
.gauge-chart {
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.doughnut-chart {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.gauge-text {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.gauge-percentage {
|
||||
font-size: 36px;
|
||||
font-weight: 700;
|
||||
color: #165dff;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.gauge-label {
|
||||
font-size: 15px;
|
||||
color: #606266;
|
||||
margin-top: 6px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* 环形图动画效果 */
|
||||
.background-circle {
|
||||
stroke: #f0f2f5;
|
||||
}
|
||||
|
||||
.progress-circle {
|
||||
transition: stroke-dashoffset 1s ease-in-out;
|
||||
}
|
||||
|
||||
.online-progress {
|
||||
stroke: #165dff;
|
||||
filter: drop-shadow(0 0 6px rgba(22, 93, 255, 0.2));
|
||||
}
|
||||
|
||||
.offline-progress {
|
||||
stroke: #40c9c6;
|
||||
filter: drop-shadow(0 0 6px rgba(64, 201, 198, 0.2));
|
||||
}
|
||||
|
||||
/* 图例样式 */
|
||||
.chart-legend {
|
||||
margin-top: 16px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.legend-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
.legend-color {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 4px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.legend-color.online {
|
||||
background-color: #165dff;
|
||||
box-shadow: 0 2px 4px rgba(22, 93, 255, 0.3);
|
||||
}
|
||||
|
||||
.legend-color.offline {
|
||||
background-color: #40c9c6;
|
||||
box-shadow: 0 2px 4px rgba(64, 201, 198, 0.3);
|
||||
}
|
||||
|
||||
.legend-text {
|
||||
font-size: 13px;
|
||||
color: #606266;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* 统计网格 */
|
||||
.stats-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.stat-item {
|
||||
background-color: #f8fafc;
|
||||
border-radius: 10px;
|
||||
padding: 16px 12px;
|
||||
text-align: center;
|
||||
border: 1px solid #f0f0f0;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.stat-item:hover {
|
||||
background-color: #ffffff;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
font-size: 24px;
|
||||
font-weight: 700;
|
||||
color: #2c3e50;
|
||||
margin-bottom: 6px;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 13px;
|
||||
color: #606266;
|
||||
margin-bottom: 4px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.stat-change {
|
||||
font-size: 12px;
|
||||
color: #52c41a;
|
||||
font-weight: 500;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.stat-change::before {
|
||||
content: '↗';
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.stat-change {
|
||||
font-size: 12px;
|
||||
color: #52c41a;
|
||||
}
|
||||
|
||||
/* 主内容区域 */
|
||||
.main-content {
|
||||
flex: 1;
|
||||
background-color: #fff;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.06);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.scrollable-content {
|
||||
max-height: calc(100vh - 340px);
|
||||
overflow-y: auto;
|
||||
padding: 28px;
|
||||
}
|
||||
|
||||
/* 增强内容区域标题样式 */
|
||||
.main-content-title {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: #2c3e50;
|
||||
margin-bottom: 24px;
|
||||
padding-bottom: 12px;
|
||||
border-bottom: 2px solid #f0f0f0;
|
||||
}
|
||||
|
||||
/* 人员网格 */
|
||||
.personnel-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.person-card {
|
||||
border: 1px solid #f0f0f0;
|
||||
border-radius: 12px;
|
||||
padding: 20px;
|
||||
transition: all 0.3s ease;
|
||||
background-color: #ffffff;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
.person-card:hover {
|
||||
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.08);
|
||||
border-color: #e6f7ff;
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.person-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
position: relative;
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
margin-right: 16px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.avatar-img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
border: 2px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.status-indicator {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
border-radius: 50%;
|
||||
border: 3px solid #fff;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.status-indicator.online {
|
||||
background-color: #52c41a;
|
||||
}
|
||||
|
||||
.status-indicator.working {
|
||||
background-color: #165dff;
|
||||
}
|
||||
|
||||
.status-indicator.offline {
|
||||
background-color: #909399;
|
||||
}
|
||||
|
||||
.person-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.person-name {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: #2c3e50;
|
||||
margin-bottom: 6px;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.person-status {
|
||||
font-size: 13px;
|
||||
padding: 3px 10px;
|
||||
border-radius: 15px;
|
||||
display: inline-block;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.person-status.online {
|
||||
background-color: #f6ffed;
|
||||
color: #52c41a;
|
||||
}
|
||||
|
||||
.person-status.working {
|
||||
background-color: #e8f3ff;
|
||||
color: #165dff;
|
||||
}
|
||||
|
||||
.person-status.offline {
|
||||
background-color: #f5f5f5;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
.person-details {
|
||||
margin-bottom: 20px;
|
||||
border-top: 1px dashed #f0f0f0;
|
||||
padding-top: 16px;
|
||||
}
|
||||
|
||||
.detail-item {
|
||||
font-size: 13px;
|
||||
color: #606266;
|
||||
margin-bottom: 10px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.detail-item:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.person-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 12px;
|
||||
border-top: 1px dashed #f0f0f0;
|
||||
padding-top: 16px;
|
||||
}
|
||||
|
||||
/* 美化按钮样式 */
|
||||
.detail-btn,
|
||||
.assign-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 6px 16px;
|
||||
border-radius: 6px;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
transition: all 0.3s ease;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
.detail-btn {
|
||||
color: #165dff;
|
||||
background-color: #f0f7ff;
|
||||
border-color: #d6e4ff;
|
||||
}
|
||||
|
||||
.detail-btn:hover {
|
||||
color: #094ab2;
|
||||
background-color: #e6f7ff;
|
||||
border-color: #91bfff;
|
||||
}
|
||||
|
||||
.assign-btn {
|
||||
color: #fa8c16;
|
||||
background-color: #fff9f0;
|
||||
border-color: #ffe7ba;
|
||||
}
|
||||
|
||||
.assign-btn:hover {
|
||||
color: #e67700;
|
||||
background-color: #fff7e6;
|
||||
border-color: #ffd591;
|
||||
}
|
||||
|
||||
/* 导航栏样式 */
|
||||
.navigation-tabs {
|
||||
display: flex;
|
||||
margin-bottom: 24px;
|
||||
background-color: #fff;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
||||
padding: 4px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.nav-tab {
|
||||
padding: 14px 24px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #606266;
|
||||
border-right: 1px solid #f0f0f0;
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.nav-tab:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.nav-tab:hover {
|
||||
color: #165dff;
|
||||
background-color: #f0f7ff;
|
||||
}
|
||||
|
||||
.nav-tab.active {
|
||||
background: linear-gradient(135deg, #165dff 0%, #4080ff 100%);
|
||||
color: #fff;
|
||||
box-shadow: 0 4px 12px rgba(22, 93, 255, 0.3);
|
||||
border-right-color: transparent;
|
||||
}
|
||||
|
||||
.nav-tab.active:hover {
|
||||
background: linear-gradient(135deg, #094ab2 0%, #3366cc 100%);
|
||||
}
|
||||
|
||||
.nav-tab {
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
/* 滚动条样式 */
|
||||
.scrollable-content::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
}
|
||||
|
||||
.scrollable-content::-webkit-scrollbar-track {
|
||||
background: #f5f7fa;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.scrollable-content::-webkit-scrollbar-thumb {
|
||||
background: #c0c4cc;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.scrollable-content::-webkit-scrollbar-thumb:hover {
|
||||
background: #909399;
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 1200px) {
|
||||
.content-container {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
width: 100%;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.scrollable-content {
|
||||
max-height: 600px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.operation-organization {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.navigation-tabs {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.nav-tab {
|
||||
flex: 1 0 33%;
|
||||
padding: 10px 0;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.personnel-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
1587
src/views/dhr_demo/shiyanguanli.vue
Normal file
1587
src/views/dhr_demo/shiyanguanli.vue
Normal file
File diff suppressed because it is too large
Load Diff
969
src/views/dhr_demo/shiyanjilu.vue
Normal file
969
src/views/dhr_demo/shiyanjilu.vue
Normal file
@ -0,0 +1,969 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="operation-inspection">
|
||||
<div class="navigation-tabs">
|
||||
<div class="nav-tab" @click="handleInspection1">待办事项</div>
|
||||
<div class="nav-tab" @click="handleInspection2">巡检管理</div>
|
||||
<div class="nav-tab active" @click="handleInspection3">试验管理</div>
|
||||
<div class="nav-tab" @click="handleInspection4">报修管理</div>
|
||||
<div class="nav-tab" @click="handleInspection5">抢修管理</div>
|
||||
<div class="nav-tab" @click="handleInspection6">工单管理</div>
|
||||
<div class="nav-tab" @click="handleInspection7">运维组织</div>
|
||||
</div>
|
||||
|
||||
<div class="header-container">
|
||||
<TitleComponent title="实验管理系统" subtitle="制定巡检计划,安排巡检任务,跟进巡检记录"></TitleComponent>
|
||||
<div class="header-actions">
|
||||
<el-button type="primary" class="export-btn">筛选</el-button>
|
||||
<el-button type="primary" class="create-btn">导入数据</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 选项卡和按钮组合 -->
|
||||
<div class="tabs-wrapper">
|
||||
<div style="display: flex; align-items: center; gap: 10px">
|
||||
<el-button type="primary" @click="handleInspectionManagement1">实验计划</el-button>
|
||||
<el-button type="primary" @click="handleInspectionManagement2">实验任务</el-button>
|
||||
<el-button type="primary" @click="handleInspectionManagement3">实验记录</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 4. 筛选和操作区域 -->
|
||||
<div class="filter-and-actions">
|
||||
<div class="filters">
|
||||
<el-select v-model="filterStatus" placeholder="巡检状态" clearable>
|
||||
<el-option label="全部状态" value="all"></el-option>
|
||||
<el-option label="正常" value="normal"></el-option>
|
||||
<el-option label="需关注" value="attention"></el-option>
|
||||
<el-option label="有问题" value="problem"></el-option>
|
||||
</el-select>
|
||||
|
||||
<el-select v-model="filterType" placeholder="巡检类型" clearable>
|
||||
<el-option label="全部类型" value="all"></el-option>
|
||||
<el-option label="数据库" value="database"></el-option>
|
||||
<el-option label="服务器" value="server"></el-option>
|
||||
<el-option label="网络设备" value="network"></el-option>
|
||||
</el-select>
|
||||
|
||||
<el-date-picker
|
||||
v-model="dateRange"
|
||||
type="daterange"
|
||||
range-separator="至"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
value-format="YYYY-MM-DD"
|
||||
class="date-picker"
|
||||
></el-date-picker>
|
||||
|
||||
<el-button type="primary" class="search-btn"> 搜索 </el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 5. 主内容区 -->
|
||||
<div class="content-container">
|
||||
<!-- 5.3 巡检记录(根据图片调整) -->
|
||||
<div v-if="activeTab === 'record'" class="record-container">
|
||||
<h2 class="section-title">试验记录与报告</h2>
|
||||
<p class="section-subtitle">截止至 {{ currentDate }}</p>
|
||||
|
||||
<!-- 统计卡片 -->
|
||||
<div class="stat-grid">
|
||||
<div class="stat-card">
|
||||
<p class="stat-label">本月完成试验</p>
|
||||
<p class="stat-value">{{ statData.completed }}<span class="stat-change up">较上月 ↑2.4%</span></p>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<p class="stat-label">试验通过率</p>
|
||||
<p class="stat-value">{{ statData.passRate }}%<span class="stat-change up">较上月 ↑5.6%</span></p>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<p class="stat-label">待分析记录</p>
|
||||
<p class="stat-value">{{ statData.pendingAnalysis }}<span class="stat-change warning">需要及时处理</span></p>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<p class="stat-label">平均试验时长</p>
|
||||
<p class="stat-value">{{ statData.avgDuration }}<span class="stat-change down">较上月 ↓9.4分钟</span></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 试验记录列表 -->
|
||||
<div class="test-records">
|
||||
<!-- 数据库性能巡检记录 -->
|
||||
<div class="test-record-card passed">
|
||||
<div class="record-header">
|
||||
<h3 class="record-title">数据库性能巡检</h3>
|
||||
<p class="record-date">
|
||||
{{ testRecords[0].date }} <span class="record-time">耗时: {{ testRecords[0].duration }}</span>
|
||||
</p>
|
||||
<span class="status-tag status-passed">通过</span>
|
||||
</div>
|
||||
|
||||
<!-- 试验进度 -->
|
||||
<div class="test-progress">
|
||||
<div class="progress-step active">
|
||||
<div class="step-number">1</div>
|
||||
<div class="step-name">准备环境</div>
|
||||
</div>
|
||||
<div class="progress-line active"></div>
|
||||
<div class="progress-step active">
|
||||
<div class="step-number">2</div>
|
||||
<div class="step-name">50%负载</div>
|
||||
</div>
|
||||
<div class="progress-line active"></div>
|
||||
<div class="progress-step active">
|
||||
<div class="step-number">3</div>
|
||||
<div class="step-name">80%负载</div>
|
||||
</div>
|
||||
<div class="progress-line active"></div>
|
||||
<div class="progress-step active">
|
||||
<div class="step-number">4</div>
|
||||
<div class="step-name">100%负载</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 试验结果 -->
|
||||
<div class="test-result">
|
||||
<h4 class="result-title">试验结果</h4>
|
||||
<p class="result-content">系统在100%负载下稳定运行1小时,CPU平均使用率92%,内存使用率88%,无崩溃或异常重启现象。</p>
|
||||
<p class="result-details">
|
||||
平均响应时间: {{ testRecords[0].responseTime }} | 错误率: {{ testRecords[0].errorRate }} | 温度值: {{ testRecords[0].temperature }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="record-actions">
|
||||
<button class="operate-btn view-btn">查看详情</button>
|
||||
<button class="operate-btn report-btn">生成报告</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 1000用户并发测试记录 -->
|
||||
<div class="test-record-card failed">
|
||||
<div class="record-header">
|
||||
<h3 class="record-title">1000用户并发测试</h3>
|
||||
<p class="record-date">
|
||||
{{ testRecords[1].date }} <span class="record-time">耗时: {{ testRecords[1].duration }}</span>
|
||||
</p>
|
||||
<span class="status-tag status-failed">失败</span>
|
||||
</div>
|
||||
|
||||
<!-- 试验进度 -->
|
||||
<div class="test-progress">
|
||||
<div class="progress-step active">
|
||||
<div class="step-number">1</div>
|
||||
<div class="step-name">准备环境</div>
|
||||
</div>
|
||||
<div class="progress-line active"></div>
|
||||
<div class="progress-step active">
|
||||
<div class="step-number">2</div>
|
||||
<div class="step-name">300用户</div>
|
||||
</div>
|
||||
<div class="progress-line active"></div>
|
||||
<div class="progress-step active">
|
||||
<div class="step-number">3</div>
|
||||
<div class="step-name">500用户</div>
|
||||
</div>
|
||||
<div class="progress-line failed"></div>
|
||||
<div class="progress-step failed">
|
||||
<div class="step-number">4</div>
|
||||
<div class="step-name">800用户</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 失败原因分析 -->
|
||||
<div class="test-result failure-analysis">
|
||||
<h4 class="result-title">失败原因分析</h4>
|
||||
<p class="result-content">当并发用户数达到780人时,数据库连接耗尽,新用户无法建立数据库连接,导致系统响应超时。</p>
|
||||
|
||||
<!-- 改进建议 -->
|
||||
<div class="improvement-suggestion">
|
||||
<i class="fas fa-lightbulb"></i>
|
||||
<p>建议: 增加数据库连接池最大连接数,优化长连接超时时间,增加连接复用机制分析评估。</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="record-actions">
|
||||
<button class="operate-btn view-btn">查看详情</button>
|
||||
<button class="operate-btn report-btn">生成报告</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 5.1 巡检计划表格 -->
|
||||
<div v-if="activeTab === 'plan'" class="table-container">
|
||||
<el-table :data="planTableData" border>
|
||||
<el-table-column prop="name" label="计划名称" width="220">
|
||||
<template #default="scope">
|
||||
<div class="plan-name">{{ scope.row.name }}</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="type" label="巡检类型" width="120"></el-table-column>
|
||||
<el-table-column prop="cycle" label="巡检周期" width="120"></el-table-column>
|
||||
<el-table-column prop="dateRange" label="执行时间范围"></el-table-column>
|
||||
<el-table-column prop="progress" label="完成进度" width="120">
|
||||
<template #default="scope">
|
||||
<div class="progress-bar">
|
||||
<div class="progress-fill" :style="{ width: scope.row.progress + '%', backgroundColor: getProgressColor(scope.row.status) }"></div>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="status" label="状态" width="100">
|
||||
<template #default="scope">
|
||||
<span :class="['status-tag', `status-${scope.row.status}`]">
|
||||
{{ getStatusText(scope.row.status) }}
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="responsible" label="负责人" width="120"></el-table-column>
|
||||
<el-table-column label="操作" width="220">
|
||||
<template #default="scope">
|
||||
<div class="operation-buttons">
|
||||
<button class="operate-btn edit-btn" v-if="['drafted', 'paused'].includes(scope.row.status)">编辑</button>
|
||||
<button class="operate-btn execute-btn" v-if="scope.row.status === 'drafted'">执行</button>
|
||||
<button class="operate-btn pause-btn" v-if="scope.row.status === 'in-progress'">暂停</button>
|
||||
<button class="operate-btn resume-btn" v-if="scope.row.status === 'paused'">恢复</button>
|
||||
<button class="operate-btn view-btn">查看详情</button>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<!-- 5.2 巡检任务表格 -->
|
||||
<div v-if="activeTab === 'task'" class="table-container">
|
||||
<el-table :data="taskTableData" border>
|
||||
<el-table-column prop="name" label="任务名称" width="220"></el-table-column>
|
||||
<el-table-column prop="planName" label="所属计划" width="180"></el-table-column>
|
||||
<el-table-column prop="type" label="巡检类型" width="120"></el-table-column>
|
||||
<el-table-column prop="target" label="巡检对象" width="150"></el-table-column>
|
||||
<el-table-column prop="deadline" label="截止时间" width="160"></el-table-column>
|
||||
<el-table-column prop="status" label="状态" width="100">
|
||||
<template #default="scope">
|
||||
<span :class="['status-tag', `status-${scope.row.status}`]">
|
||||
{{ getTaskStatusText(scope.row.status) }}
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="executor" label="执行人" width="120"></el-table-column>
|
||||
<el-table-column label="操作" width="180">
|
||||
<template #default="scope">
|
||||
<div class="operation-buttons">
|
||||
<button class="operate-btn accept-btn" v-if="scope.row.status === 'pending'">接受</button>
|
||||
<button class="operate-btn complete-btn" v-if="scope.row.status === 'accepted'">完成</button>
|
||||
<button class="operate-btn view-btn">查看详情</button>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 6. 分页 -->
|
||||
<div class="pagination" v-if="activeTab !== 'record'">
|
||||
<p class="total-records">显示1到{{ pageSize }}条,共{{ totalRecords }}条记录</p>
|
||||
<el-pagination
|
||||
layout="prev, pager, next, jumper, sizes"
|
||||
:total="totalRecords"
|
||||
v-model:current-page="currentPage"
|
||||
v-model:page-size="pageSize"
|
||||
:page-sizes="[20, 50, 100]"
|
||||
@current-change="handlePageChange"
|
||||
@size-change="handleSizeChange"
|
||||
></el-pagination>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue';
|
||||
import router from '@/router';
|
||||
import TitleComponent from '@/views/demo/components/TitleComponent.vue';
|
||||
// 1. 选项卡状态管理
|
||||
const activeTab = ref('record'); // 默认显示"巡检记录"
|
||||
const showFilter = ref(false);
|
||||
|
||||
// 2. 筛选条件
|
||||
const filterStatus = ref('all');
|
||||
const filterType = ref('all');
|
||||
const dateRange = ref([]);
|
||||
|
||||
// 4. 当前日期
|
||||
const currentDate = computed(() => {
|
||||
const date = new Date();
|
||||
return `${date.getFullYear()}/${String(date.getMonth() + 1).padStart(2, '0')}/${String(date.getDate()).padStart(2, '0')} ${String(
|
||||
date.getHours()
|
||||
).padStart(2, '0')}:${String(date.getMinutes()).padStart(2, '0')}`;
|
||||
});
|
||||
|
||||
// 5. 统计数据
|
||||
const statData = ref({
|
||||
completed: 12,
|
||||
passRate: 83,
|
||||
pendingAnalysis: 3,
|
||||
avgDuration: '42分钟'
|
||||
});
|
||||
|
||||
// 6. 试验记录数据
|
||||
const testRecords = ref([
|
||||
{
|
||||
date: '2025-06-15',
|
||||
duration: '1小时45分钟',
|
||||
responseTime: '1.2s',
|
||||
errorRate: '0%',
|
||||
temperature: '72°C'
|
||||
},
|
||||
{
|
||||
date: '2025-06-12',
|
||||
duration: '2小时10分钟'
|
||||
}
|
||||
]);
|
||||
|
||||
// 9. 方法:切换顶部导航
|
||||
const handleInspection1 = () => {
|
||||
router.push('/rili/rili');
|
||||
};
|
||||
const handleInspection2 = () => {
|
||||
router.push('/rili/InspectionManagement');
|
||||
};
|
||||
const handleInspection3 = () => {
|
||||
router.push('/rili/shiyanguanli');
|
||||
};
|
||||
const handleInspection4 = () => {
|
||||
router.push('/rili/baoxiuguanli');
|
||||
};
|
||||
const handleInspection5 = () => {
|
||||
router.push('/rili/qiangxiuguanli');
|
||||
};
|
||||
const handleInspection6 = () => {
|
||||
router.push('/rili/gongdanliebiao');
|
||||
};
|
||||
const handleInspection7 = () => {
|
||||
router.push('/rili/renyuanzhuangtai');
|
||||
};
|
||||
const handleInspectionManagement1 = () => {
|
||||
router.push('/rili/shiyanguanli');
|
||||
};
|
||||
const handleInspectionManagement2 = () => {
|
||||
router.push('/rili/shiyanrenwu');
|
||||
};
|
||||
const handleInspectionManagement3 = () => {
|
||||
router.push('/rili/shiyanjilu');
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 1. 基础容器样式 */
|
||||
.operation-inspection {
|
||||
padding: 20px;
|
||||
background-color: #f9fbfd;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
/* 2. 顶部导航选项卡 */
|
||||
.navigation-tabs {
|
||||
display: flex;
|
||||
background-color: #fff;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08);
|
||||
margin-bottom: 20px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.nav-tab {
|
||||
padding: 12px 24px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
font-size: 14px;
|
||||
color: #6b7280;
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
border-right: 1px solid #f0f0f0;
|
||||
}
|
||||
.nav-tab:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
.nav-tab:hover:not(.active) {
|
||||
background-color: #f3f4f6;
|
||||
}
|
||||
.nav-tab.active {
|
||||
background-color: #165dff;
|
||||
color: #fff;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* 选项卡样式 */
|
||||
.tabs-wrapper {
|
||||
background-color: #fff;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 16px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
/* 筛选栏样式 */
|
||||
.filter-bar {
|
||||
background-color: #fff;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 16px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.filter-item {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.filter-bar .el-select,
|
||||
.filter-bar .el-date-picker {
|
||||
width: 150px;
|
||||
height: 36px;
|
||||
}
|
||||
|
||||
.filter-actions {
|
||||
margin-left: auto;
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
/* 5. 筛选和操作区域 */
|
||||
.filter-and-actions {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 16px;
|
||||
background-color: #fff;
|
||||
border: 1px solid #e5e7eb;
|
||||
border-radius: 0 0 4px 4px;
|
||||
margin-bottom: 16px;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
}
|
||||
.filters {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
}
|
||||
.el-select,
|
||||
.date-picker {
|
||||
width: 160px;
|
||||
}
|
||||
.search-btn,
|
||||
.export-btn {
|
||||
background-color: #165dff;
|
||||
border-color: #165dff;
|
||||
}
|
||||
.filter-btn {
|
||||
background-color: #f3f4f6;
|
||||
color: #6b7280;
|
||||
border-color: #e5e7eb;
|
||||
}
|
||||
|
||||
/* 6. 表格容器 */
|
||||
.table-container {
|
||||
background-color: #fff;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #e5e7eb;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.el-table {
|
||||
width: 100%;
|
||||
}
|
||||
.el-table th {
|
||||
background-color: #f9fafb;
|
||||
font-weight: 500;
|
||||
color: #4b5563;
|
||||
}
|
||||
.plan-name {
|
||||
white-space: pre-line;
|
||||
}
|
||||
|
||||
/* 7. 进度条样式 */
|
||||
.progress-bar {
|
||||
height: 8px;
|
||||
background-color: #f3f4f6;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.progress-fill {
|
||||
height: 100%;
|
||||
transition: width 0.3s ease;
|
||||
}
|
||||
|
||||
/* 8. 状态标签样式 */
|
||||
.status-tag {
|
||||
display: inline-block;
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
}
|
||||
.status-drafted {
|
||||
background-color: #e0efff;
|
||||
color: #165dff;
|
||||
}
|
||||
.status-in-progress {
|
||||
background-color: #e0f2fe;
|
||||
color: #0284c7;
|
||||
}
|
||||
.status-completed {
|
||||
background-color: #e6ffed;
|
||||
color: #00b42a;
|
||||
}
|
||||
.status-paused {
|
||||
background-color: #f2f3f5;
|
||||
color: #86909c;
|
||||
}
|
||||
.status-pending {
|
||||
background-color: #f9fafb;
|
||||
color: #6b7280;
|
||||
}
|
||||
.status-accepted {
|
||||
background-color: #eff6ff;
|
||||
color: #2563eb;
|
||||
}
|
||||
.status-rejected {
|
||||
background-color: #fee2e2;
|
||||
color: #dc2626;
|
||||
}
|
||||
.status-normal {
|
||||
background-color: #e6ffed;
|
||||
color: #00b42a;
|
||||
}
|
||||
.status-attention {
|
||||
background-color: #fff7e0;
|
||||
color: #ff7d00;
|
||||
}
|
||||
.status-problem {
|
||||
background-color: #fff2f0;
|
||||
color: #f5222d;
|
||||
}
|
||||
.status-passed {
|
||||
background-color: #e6ffed;
|
||||
color: #00b42a;
|
||||
}
|
||||
.status-failed {
|
||||
background-color: #fee2e2;
|
||||
color: #dc2626;
|
||||
}
|
||||
|
||||
/* 9. 操作按钮样式 */
|
||||
.operation-buttons {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.operate-btn {
|
||||
padding: 2px 8px;
|
||||
font-size: 12px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
background: none;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
.edit-btn {
|
||||
color: #165dff;
|
||||
}
|
||||
.edit-btn:hover {
|
||||
background-color: #e8f3ff;
|
||||
}
|
||||
.execute-btn {
|
||||
color: #00b42a;
|
||||
}
|
||||
.execute-btn:hover {
|
||||
background-color: #e6ffed;
|
||||
}
|
||||
.pause-btn {
|
||||
color: #ff7d00;
|
||||
}
|
||||
.pause-btn:hover {
|
||||
background-color: #fff7e0;
|
||||
}
|
||||
.resume-btn {
|
||||
color: #722ed1;
|
||||
}
|
||||
.resume-btn:hover {
|
||||
background-color: #f3e8ff;
|
||||
}
|
||||
.view-btn {
|
||||
color: #165dff;
|
||||
}
|
||||
.view-btn:hover {
|
||||
background-color: #e8f3ff;
|
||||
}
|
||||
.complete-btn {
|
||||
color: #00b42a;
|
||||
}
|
||||
.complete-btn:hover {
|
||||
background-color: #e6ffed;
|
||||
}
|
||||
.accept-btn {
|
||||
color: #2563eb;
|
||||
}
|
||||
.accept-btn:hover {
|
||||
background-color: #eff6ff;
|
||||
}
|
||||
.report-btn {
|
||||
color: #ff7d00;
|
||||
}
|
||||
.report-btn:hover {
|
||||
background-color: #fff7e0;
|
||||
}
|
||||
|
||||
/* 10. 分页样式 */
|
||||
.pagination {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 12px;
|
||||
background-color: #fff;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #e5e7eb;
|
||||
}
|
||||
.total-records {
|
||||
font-size: 14px;
|
||||
color: #6b7280;
|
||||
margin: 0;
|
||||
}
|
||||
.el-pagination {
|
||||
--el-pagination-item-active-bg-color: #165dff;
|
||||
}
|
||||
|
||||
/* 11. 记录页面样式 */
|
||||
.record-container {
|
||||
background-color: #fff;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #e5e7eb;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: #1f2329;
|
||||
margin: 0 0 10px 0;
|
||||
}
|
||||
|
||||
.section-subtitle {
|
||||
font-size: 14px;
|
||||
color: #6b7280;
|
||||
margin: 0 0 20px 0;
|
||||
text-align: right;
|
||||
}
|
||||
/* 头部容器 - 替换了固定gap的flex布局 */
|
||||
.header-container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.header-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
/* 12. 统计卡片样式 */
|
||||
.stat-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 16px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
.stat-card {
|
||||
background-color: #f0f7ff;
|
||||
border-radius: 8px;
|
||||
padding: 16px;
|
||||
border-left: 4px solid #165dff;
|
||||
}
|
||||
.stat-label {
|
||||
font-size: 14px;
|
||||
color: #6b7280;
|
||||
margin: 0 0 8px 0;
|
||||
}
|
||||
.stat-value {
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
color: #1f2329;
|
||||
margin: 0;
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: 8px;
|
||||
}
|
||||
.stat-change {
|
||||
font-size: 12px;
|
||||
padding: 1px 6px;
|
||||
border-radius: 4px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.stat-change.up {
|
||||
background-color: #e6ffed;
|
||||
color: #00b42a;
|
||||
}
|
||||
.stat-change.down {
|
||||
background-color: #fff1f0;
|
||||
color: #f5222d;
|
||||
}
|
||||
.stat-change.warning {
|
||||
background-color: #fff7e0;
|
||||
color: #ff7d00;
|
||||
}
|
||||
|
||||
/* 13. 试验记录样式 */
|
||||
.test-records {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.test-record-card {
|
||||
border: 1px solid #e5e7eb;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
transition: box-shadow 0.2s;
|
||||
}
|
||||
|
||||
.test-record-card:hover {
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.test-record-card.passed {
|
||||
border-left: 4px solid #00b42a;
|
||||
}
|
||||
|
||||
.test-record-card.failed {
|
||||
border-left: 4px solid #dc2626;
|
||||
}
|
||||
|
||||
.record-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 16px;
|
||||
background-color: #f9fafb;
|
||||
border-bottom: 1px solid #e5e7eb;
|
||||
}
|
||||
|
||||
.record-title {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: #1f2329;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.record-date {
|
||||
font-size: 14px;
|
||||
color: #6b7280;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.record-time {
|
||||
font-size: 12px;
|
||||
color: #9ca3af;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
/* 14. 试验进度样式 */
|
||||
.test-progress {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 20px;
|
||||
background-color: #fff;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.progress-step {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.step-number {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background-color: #e5e7eb;
|
||||
color: #6b7280;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: 600;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.step-name {
|
||||
font-size: 12px;
|
||||
color: #6b7280;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.progress-line {
|
||||
flex: 1;
|
||||
height: 2px;
|
||||
background-color: #e5e7eb;
|
||||
}
|
||||
|
||||
.progress-step.active .step-number {
|
||||
background-color: #165dff;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.progress-step.active .step-name {
|
||||
color: #165dff;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.progress-line.active {
|
||||
background-color: #165dff;
|
||||
}
|
||||
|
||||
.progress-step.failed .step-number {
|
||||
background-color: #dc2626;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.progress-step.failed .step-name {
|
||||
color: #dc2626;
|
||||
}
|
||||
|
||||
.progress-line.failed {
|
||||
background-color: #dc2626;
|
||||
}
|
||||
|
||||
/* 15. 试验结果样式 */
|
||||
.test-result {
|
||||
padding: 16px 20px;
|
||||
border-top: 1px solid #e5e7eb;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.result-title {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #1f2329;
|
||||
margin: 0 0 12px 0;
|
||||
}
|
||||
|
||||
.result-content {
|
||||
font-size: 14px;
|
||||
color: #4b5563;
|
||||
line-height: 1.5;
|
||||
margin: 0 0 12px 0;
|
||||
}
|
||||
|
||||
.result-details {
|
||||
font-size: 13px;
|
||||
color: #6b7280;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.failure-analysis {
|
||||
background-color: #fff5f5;
|
||||
}
|
||||
|
||||
.improvement-suggestion {
|
||||
margin-top: 12px;
|
||||
padding: 10px 12px;
|
||||
background-color: #fff8e6;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.improvement-suggestion i {
|
||||
color: #ff7d00;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.improvement-suggestion p {
|
||||
font-size: 13px;
|
||||
color: #6b46c1;
|
||||
margin: 0;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
/* 16. 记录操作按钮 */
|
||||
.record-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 10px;
|
||||
padding: 16px 20px;
|
||||
background-color: #f9fafb;
|
||||
border-top: 1px solid #e5e7eb;
|
||||
}
|
||||
|
||||
/* 17. 响应式适配 */
|
||||
@media (max-width: 1200px) {
|
||||
.stat-grid {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.stat-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.test-progress {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 16px;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.progress-step {
|
||||
flex-direction: row;
|
||||
width: 100%;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.step-number {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.progress-line {
|
||||
width: 2px;
|
||||
height: 30px;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.filters {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.el-select,
|
||||
.date-picker {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
width: 100%;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.navigation-tabs {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.nav-tab {
|
||||
flex: 1 1 auto;
|
||||
min-width: 100px;
|
||||
}
|
||||
|
||||
.record-header {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 8px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
907
src/views/dhr_demo/shiyanrenwu.vue
Normal file
907
src/views/dhr_demo/shiyanrenwu.vue
Normal file
@ -0,0 +1,907 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="inspection-tasks">
|
||||
<div class="navigation-tabs">
|
||||
<div class="nav-tab" @click="handleInspection1">待办事项</div>
|
||||
<div class="nav-tab" @click="handleInspection2">巡检管理</div>
|
||||
<div class="nav-tab active" @click="handleInspection3">试验管理</div>
|
||||
<div class="nav-tab" @click="handleInspection4">报修管理</div>
|
||||
<div class="nav-tab" @click="handleInspection5">抢修管理</div>
|
||||
<div class="nav-tab" @click="handleInspection6">工单管理</div>
|
||||
<div class="nav-tab" @click="handleInspection7">运维组织</div>
|
||||
</div>
|
||||
<!-- 页面标题 -->
|
||||
<TitleComponent title="实验管理系统" subtitle="制定巡检计划,安排巡检任务,跟进巡检记录"></TitleComponent>
|
||||
|
||||
<!-- 选项卡 -->
|
||||
<div class="tabs-wrapper">
|
||||
<div style="display: flex; align-items: center; gap: 10px">
|
||||
<el-button type="primary" @click="handleInspectionManagement1">实验计划</el-button>
|
||||
<el-button type="primary" @click="handleInspectionManagement2">实验任务</el-button>
|
||||
<el-button type="primary" @click="handleInspectionManagement3">实验记录</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 筛选栏 -->
|
||||
<div class="filter-bar">
|
||||
<div class="filter-container">
|
||||
<div class="filter-item">
|
||||
<el-select v-model="taskStatus" placeholder="任务状态">
|
||||
<el-option label="待执行" value="pending"></el-option>
|
||||
<el-option label="执行中" value="executing"></el-option>
|
||||
<el-option label="已延期" value="delayed"></el-option>
|
||||
<el-option label="已完成" value="completed"></el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<el-select v-model="planType" placeholder="全部计划">
|
||||
<el-option label="每日巡检计划" value="daily"></el-option>
|
||||
<el-option label="每周巡检计划" value="weekly"></el-option>
|
||||
<el-option label="每月巡检计划" value="monthly"></el-option>
|
||||
<el-option label="每季度巡检计划" value="quarterly"></el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<el-select v-model="executor" placeholder="执行人">
|
||||
<el-option label="全部人员" value="all"></el-option>
|
||||
<el-option label="张明" value="zhangming"></el-option>
|
||||
<el-option label="李华" value="lihua"></el-option>
|
||||
<el-option label="王强" value="wangqiang"></el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
<div class="filter-actions">
|
||||
<el-button type="primary" class="search-btn" @click="handleSearch"> 搜索 </el-button>
|
||||
<el-button type="primary" icon="el-icon-plus" class="create-btn" @click="handleCreateTask"> 手动创建任务 </el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 任务卡片列表 -->
|
||||
<div class="task-cards">
|
||||
<div class="task-card" v-for="(task, index) in pagedTasks" :key="index">
|
||||
<div class="task-header">
|
||||
<div class="task-title">
|
||||
{{ task.title }}
|
||||
</div>
|
||||
<div class="task-status" :class="task.statusClass">
|
||||
{{ task.statusText }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="task-details">
|
||||
<div class="detail-item">
|
||||
<span class="detail-label">计划时间</span>
|
||||
<span class="detail-value">{{ task.planTime }}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<span class="detail-label">巡检对象</span>
|
||||
<span class="detail-value">{{ task.target }}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<span class="detail-label">执行人</span>
|
||||
<span class="detail-value">{{ task.executor }}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<span class="detail-label">关联计划</span>
|
||||
<span class="detail-value">{{ task.relatedPlan }}</span>
|
||||
</div>
|
||||
|
||||
<!-- 特定状态的额外信息 -->
|
||||
<div v-if="task.status === 'delayed'" class="delay-reason">
|
||||
<span class="detail-label">延期原因</span>
|
||||
<span class="detail-value">{{ task.delayReason }}</span>
|
||||
</div>
|
||||
|
||||
<div v-if="task.status === 'executing'" class="progress-container">
|
||||
<span class="detail-label">完成进度</span>
|
||||
<div class="progress-bar">
|
||||
<el-progress :percentage="task.progress" stroke-width="6" :stroke-color="task.progressColor"></el-progress>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="task.status === 'completed'" class="task-result">
|
||||
<span class="detail-label">结果</span>
|
||||
<span class="detail-value" :class="task.resultClass">{{ task.result }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="task-actions">
|
||||
<el-button type="text" size="small" class="action-btn view-btn" @click="handleView(task)"> 详情 </el-button>
|
||||
<el-button type="primary" size="small" :class="task.actionClass" @click="handleAction(task)">
|
||||
{{ task.actionText }}
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 分页区域 -->
|
||||
<div class="pagination-section">
|
||||
<div class="pagination-controls">
|
||||
<el-pagination
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
:current-page="currentPage"
|
||||
:page-sizes="[8, 12, 16, 20]"
|
||||
:page-size="pageSize"
|
||||
layout="prev, pager, next, jumper"
|
||||
:total="total"
|
||||
background
|
||||
>
|
||||
</el-pagination>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 添加新任务弹窗 -->
|
||||
<el-dialog v-model="createTaskDialogVisible" title="添加新任务" width="500px" :before-close="handleCancelCreateTask">
|
||||
<el-form ref="createTaskFormRef" :model="createTaskForm" :rules="createTaskRules" label-width="80px">
|
||||
<el-form-item label="任务名称" prop="taskName">
|
||||
<el-input v-model="createTaskForm.taskName" placeholder="输入任务名称" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="巡检对象" prop="inspectionTarget">
|
||||
<el-input v-model="createTaskForm.inspectionTarget" placeholder="输入巡检内容" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="时间" prop="timeRange">
|
||||
<el-date-picker
|
||||
v-model="createTaskForm.timeRange"
|
||||
type="datetimerange"
|
||||
start-placeholder="开始时间"
|
||||
end-placeholder="结束时间"
|
||||
format="YYYY-MM-DD HH:mm"
|
||||
value-format="YYYY-MM-DD HH:mm"
|
||||
placeholder="选择时间范围"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="关联计划">
|
||||
<el-select v-model="createTaskForm.relatedPlan" placeholder="选择关联计划">
|
||||
<el-option label="全部计划" value="all" />
|
||||
<el-option label="每日巡检计划" value="daily" />
|
||||
<el-option label="每周巡检计划" value="weekly" />
|
||||
<el-option label="每月巡检计划" value="monthly" />
|
||||
<el-option label="每季度巡检计划" value="quarterly" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="执行人">
|
||||
<el-select v-model="createTaskForm.executor" placeholder="选择执行人">
|
||||
<el-option label="全部人员" value="all" />
|
||||
<el-option label="张明" value="zhangming" />
|
||||
<el-option label="李华" value="lihua" />
|
||||
<el-option label="王强" value="wangqiang" />
|
||||
<el-option label="赵伟" value="zhaowei" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="handleCancelCreateTask">取消</el-button>
|
||||
<el-button type="primary" @click="handleSaveTask">保存任务</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue';
|
||||
import router from '@/router';
|
||||
import TitleComponent from '@/views/demo/components/TitleComponent.vue';
|
||||
|
||||
// 激活的选项卡
|
||||
const activeTab = ref('task');
|
||||
|
||||
// 筛选条件
|
||||
const taskStatus = ref('');
|
||||
const planType = ref('');
|
||||
const executor = ref('');
|
||||
|
||||
// 任务数据
|
||||
const tasks = ref([
|
||||
{
|
||||
title: '生产服务器日常巡检',
|
||||
status: 'pending',
|
||||
statusText: '待执行',
|
||||
statusClass: 'status-pending',
|
||||
planTime: '2025-06-16 08:30',
|
||||
target: '生产服务器集群(12台)',
|
||||
executor: '张明',
|
||||
relatedPlan: '每日巡检计划',
|
||||
actionText: '开始执行',
|
||||
actionClass: 'start-btn'
|
||||
},
|
||||
{
|
||||
title: '机房环境检查',
|
||||
status: 'delayed',
|
||||
statusText: '已延期',
|
||||
statusClass: 'status-delayed',
|
||||
planTime: '2025-06-16 08:30',
|
||||
target: '机房温度、湿度、电源',
|
||||
executor: '李华',
|
||||
relatedPlan: '每周巡检计划',
|
||||
delayReason: '设备维修处理中',
|
||||
actionText: '重新安排',
|
||||
actionClass: 'reschedule-btn'
|
||||
},
|
||||
{
|
||||
title: '网络设备安全巡检',
|
||||
status: 'executing',
|
||||
statusText: '执行中',
|
||||
statusClass: 'status-executing',
|
||||
planTime: '2025-06-16 09:30',
|
||||
target: '核心交换机、防火墙',
|
||||
executor: '王强',
|
||||
relatedPlan: '每周巡检计划',
|
||||
progress: 60,
|
||||
progressColor: '#FF7D00',
|
||||
actionText: '完成',
|
||||
actionClass: 'complete-btn'
|
||||
},
|
||||
{
|
||||
title: '数据库性能巡检',
|
||||
status: 'completed',
|
||||
statusText: '已完成',
|
||||
statusClass: 'status-completed',
|
||||
planTime: '2025-06-16 10:30',
|
||||
target: '生产数据库集群(2组)',
|
||||
executor: '赵伟',
|
||||
relatedPlan: '每月巡检计划',
|
||||
result: '正常',
|
||||
resultClass: 'result-normal',
|
||||
actionText: '查看报告',
|
||||
actionClass: 'report-btn'
|
||||
},
|
||||
{
|
||||
title: '生产服务器日常巡检',
|
||||
status: 'pending',
|
||||
statusText: '待执行',
|
||||
statusClass: 'status-pending',
|
||||
planTime: '2025-06-16 13:30',
|
||||
target: '生产服务器集群(12台)',
|
||||
executor: '张明',
|
||||
relatedPlan: '每日巡检计划',
|
||||
actionText: '开始执行',
|
||||
actionClass: 'start-btn'
|
||||
},
|
||||
{
|
||||
title: '机房环境检查',
|
||||
status: 'delayed',
|
||||
statusText: '已延期',
|
||||
statusClass: 'status-delayed',
|
||||
planTime: '2025-06-16 14:00',
|
||||
target: '机房温度、湿度、电源',
|
||||
executor: '李华',
|
||||
relatedPlan: '每周巡检计划',
|
||||
delayReason: '设备维修处理中',
|
||||
actionText: '重新安排',
|
||||
actionClass: 'reschedule-btn'
|
||||
},
|
||||
{
|
||||
title: '网络设备安全巡检',
|
||||
status: 'executing',
|
||||
statusText: '执行中',
|
||||
statusClass: 'status-executing',
|
||||
planTime: '2025-06-16 15:00',
|
||||
target: '核心交换机、防火墙',
|
||||
executor: '王强',
|
||||
relatedPlan: '每周巡检计划',
|
||||
progress: 35,
|
||||
progressColor: '#FF7D00',
|
||||
actionText: '完成',
|
||||
actionClass: 'complete-btn'
|
||||
},
|
||||
{
|
||||
title: '数据库性能巡检',
|
||||
status: 'completed',
|
||||
statusText: '已完成',
|
||||
statusClass: 'status-completed',
|
||||
planTime: '2025-06-16 16:00',
|
||||
target: '生产数据库集群(2组)',
|
||||
executor: '赵伟',
|
||||
relatedPlan: '每月巡检计划',
|
||||
result: '正常',
|
||||
resultClass: 'result-normal',
|
||||
actionText: '查看报告',
|
||||
actionClass: 'report-btn'
|
||||
}
|
||||
]);
|
||||
|
||||
// 分页相关
|
||||
const currentPage = ref(1);
|
||||
const pageSize = ref(8);
|
||||
const total = ref(tasks.value.length);
|
||||
|
||||
// 状态排序映射
|
||||
const statusOrder = {
|
||||
pending: 0, // 待完成
|
||||
delayed: 1, // 已延期
|
||||
executing: 2, // 执行中
|
||||
completed: 3 // 已完成
|
||||
};
|
||||
|
||||
// 分页处理后的数据(含排序)
|
||||
const pagedTasks = computed(() => {
|
||||
// 先按状态排序
|
||||
const sortedTasks = [...tasks.value].sort((a, b) => {
|
||||
return statusOrder[a.status] - statusOrder[b.status];
|
||||
});
|
||||
|
||||
// 再进行分页
|
||||
const startIndex = (currentPage.value - 1) * pageSize.value;
|
||||
const endIndex = startIndex + pageSize.value;
|
||||
return sortedTasks.slice(startIndex, endIndex);
|
||||
});
|
||||
|
||||
// 搜索处理
|
||||
const handleSearch = () => {
|
||||
currentPage.value = 1; // 重置到第一页
|
||||
// 实际应用中这里会根据筛选条件过滤数据
|
||||
};
|
||||
|
||||
// 创建任务弹窗相关
|
||||
const createTaskDialogVisible = ref(false);
|
||||
const createTaskForm = ref({
|
||||
taskName: '',
|
||||
inspectionTarget: '',
|
||||
timeRange: [],
|
||||
relatedPlan: 'all',
|
||||
executor: 'all'
|
||||
});
|
||||
|
||||
const createTaskRules = {
|
||||
taskName: [{ required: true, message: '请输入任务名称', trigger: 'blur' }],
|
||||
inspectionTarget: [{ required: true, message: '请输入巡检对象', trigger: 'blur' }],
|
||||
timeRange: [{ required: true, message: '请选择时间范围', trigger: 'change' }]
|
||||
};
|
||||
|
||||
// 创建任务
|
||||
const handleCreateTask = () => {
|
||||
createTaskDialogVisible.value = true;
|
||||
};
|
||||
|
||||
// 保存任务
|
||||
const handleSaveTask = () => {
|
||||
// 模拟保存任务逻辑
|
||||
console.log('保存任务:', createTaskForm.value);
|
||||
// 关闭弹窗
|
||||
createTaskDialogVisible.value = false;
|
||||
// 重置表单
|
||||
createTaskForm.value = {
|
||||
taskName: '',
|
||||
inspectionTarget: '',
|
||||
timeRange: [],
|
||||
relatedPlan: 'all',
|
||||
executor: 'all'
|
||||
};
|
||||
// 这里可以添加成功提示和刷新任务列表的逻辑
|
||||
};
|
||||
|
||||
// 取消创建任务
|
||||
const handleCancelCreateTask = () => {
|
||||
createTaskDialogVisible.value = false;
|
||||
// 重置表单
|
||||
createTaskForm.value = {
|
||||
taskName: '',
|
||||
inspectionTarget: '',
|
||||
timeRange: [],
|
||||
relatedPlan: 'all',
|
||||
executor: 'all'
|
||||
};
|
||||
};
|
||||
|
||||
// 分页事件
|
||||
const handleSizeChange = (val) => {
|
||||
pageSize.value = val;
|
||||
currentPage.value = 1;
|
||||
};
|
||||
|
||||
const handleCurrentChange = (val) => {
|
||||
currentPage.value = val;
|
||||
};
|
||||
|
||||
// 查看任务详情
|
||||
const handleView = (task) => {
|
||||
console.log('查看任务详情:', task);
|
||||
};
|
||||
|
||||
const handleInspectionManagement1 = () => {
|
||||
router.push('/rili/shiyanguanli');
|
||||
};
|
||||
const handleInspectionManagement2 = () => {
|
||||
router.push('/rili/shiyanrenwu');
|
||||
};
|
||||
const handleInspectionManagement3 = () => {
|
||||
router.push('/rili/shiyanjilu');
|
||||
};
|
||||
const handleInspection1 = () => {
|
||||
router.push('/rili/rili');
|
||||
};
|
||||
const handleInspection2 = () => {
|
||||
router.push('/rili/InspectionManagement');
|
||||
};
|
||||
const handleInspection3 = () => {
|
||||
router.push('/rili/shiyanguanli');
|
||||
};
|
||||
const handleInspection4 = () => {
|
||||
router.push('/rili/baoxiuguanli');
|
||||
};
|
||||
const handleInspection5 = () => {
|
||||
router.push('/rili/qiangxiuguanli');
|
||||
};
|
||||
const handleInspection6 = () => {
|
||||
router.push('/rili/gongdanliebiao');
|
||||
};
|
||||
const handleInspection7 = () => {
|
||||
router.push('/rili/renyuanzhuangtai');
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.inspection-tasks {
|
||||
padding: 20px;
|
||||
background-color: #f5f7fa;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
/* 选项卡样式 */
|
||||
.tabs-wrapper {
|
||||
background-color: #fff;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 16px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.custom-tabs {
|
||||
border-bottom: none;
|
||||
padding-top: 20px;
|
||||
}
|
||||
|
||||
.custom-tabs .el-tabs__header {
|
||||
margin: 0 -20px 0 -20px;
|
||||
padding: 0 20px;
|
||||
border-bottom: 1px solid #e4e7ed;
|
||||
}
|
||||
|
||||
.custom-tabs .el-tabs__nav-wrap::after {
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.custom-tabs .el-tabs__item {
|
||||
font-size: 14px;
|
||||
color: #606266;
|
||||
padding: 12px 20px;
|
||||
margin-right: 20px;
|
||||
border-bottom: 2px solid transparent;
|
||||
}
|
||||
|
||||
.custom-tabs .el-tabs__item.is-active {
|
||||
color: #409eff;
|
||||
border-bottom-color: #409eff;
|
||||
}
|
||||
|
||||
.custom-tabs .el-tabs__item:hover {
|
||||
color: #409eff;
|
||||
}
|
||||
/* 筛选栏样式 */
|
||||
.filter-bar {
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 24px;
|
||||
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05);
|
||||
padding: 16px 24px;
|
||||
}
|
||||
|
||||
.filter-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 16px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.filter-item {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.filter-bar .el-select {
|
||||
width: 180px;
|
||||
height: 36px;
|
||||
}
|
||||
|
||||
.filter-bar .el-select .el-input__inner {
|
||||
border-radius: 4px;
|
||||
border-color: #dcdfe6;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.filter-bar .el-select .el-input__inner:focus {
|
||||
border-color: #165dff;
|
||||
box-shadow: 0 0 0 2px rgba(22, 93, 255, 0.1);
|
||||
}
|
||||
|
||||
.filter-actions {
|
||||
margin-left: auto;
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.search-btn {
|
||||
background-color: #f2f3f5;
|
||||
color: #303133;
|
||||
border-color: #f2f3f5;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.search-btn:hover {
|
||||
background-color: #e5e6eb;
|
||||
color: #303133;
|
||||
border-color: #e5e6eb;
|
||||
}
|
||||
|
||||
.create-btn {
|
||||
background-color: #165dff;
|
||||
border-color: #165dff;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.create-btn:hover {
|
||||
background-color: #0e42d2;
|
||||
border-color: #0e42d2;
|
||||
}
|
||||
|
||||
/* 任务卡片样式 */
|
||||
.task-cards {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||||
gap: 20px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.task-card {
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05);
|
||||
padding: 16px 16px 60px 16px; /* 底部留出更多空间给按钮 */
|
||||
transition: box-shadow 0.2s ease;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
min-height: 280px; /* 确保有足够高度显示所有内容 */
|
||||
}
|
||||
|
||||
.task-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
padding-top: 12px;
|
||||
border-top: 1px solid #f0f2f5;
|
||||
position: absolute;
|
||||
bottom: 16px;
|
||||
right: 16px;
|
||||
left: 16px;
|
||||
background-color: #fff;
|
||||
padding: 12px 0 0 0;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.task-card::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 4px;
|
||||
}
|
||||
|
||||
.task-card.status-pending::before {
|
||||
background-color: #1677ff;
|
||||
}
|
||||
|
||||
.task-card.status-delayed::before {
|
||||
background-color: #ff4d4f;
|
||||
}
|
||||
|
||||
.task-card.status-executing::before {
|
||||
background-color: #fa8c16;
|
||||
}
|
||||
|
||||
.task-card.status-completed::before {
|
||||
background-color: #52c41a;
|
||||
}
|
||||
|
||||
.task-card:hover {
|
||||
transform: translateY(-3px);
|
||||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
.task-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 16px;
|
||||
padding-bottom: 12px;
|
||||
border-bottom: 1px solid #f0f2f5;
|
||||
}
|
||||
|
||||
.task-title {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: #1d2129;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.task-status {
|
||||
padding: 4px 10px;
|
||||
border-radius: 6px;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
/* 待执行状态 - 蓝色 */
|
||||
.status-pending {
|
||||
background-color: #e6f7ff;
|
||||
color: #1677ff;
|
||||
border-color: #91d5ff;
|
||||
}
|
||||
|
||||
/* 已延期状态 - 红色 */
|
||||
.status-delayed {
|
||||
background-color: #fff2f0;
|
||||
color: #ff4d4f;
|
||||
border-color: #ffccc7;
|
||||
}
|
||||
|
||||
/* 执行中状态 - 黄色 */
|
||||
.status-executing {
|
||||
background-color: #fffbe6;
|
||||
color: #fa8c16;
|
||||
border-color: #ffe58f;
|
||||
}
|
||||
|
||||
/* 已完成状态 - 绿色 */
|
||||
.status-completed {
|
||||
background-color: #f6ffed;
|
||||
color: #52c41a;
|
||||
border-color: #b7eb8f;
|
||||
}
|
||||
|
||||
.task-details {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.detail-item {
|
||||
display: flex;
|
||||
margin-bottom: 10px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.detail-label {
|
||||
flex: 0 0 80px;
|
||||
color: #86909c;
|
||||
}
|
||||
|
||||
.detail-value {
|
||||
flex: 1;
|
||||
color: #4e5969;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.delay-reason {
|
||||
display: flex;
|
||||
margin: 10px 0;
|
||||
font-size: 13px;
|
||||
padding-top: 8px;
|
||||
border-top: 1px dashed #f0f2f5;
|
||||
}
|
||||
|
||||
.progress-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: 10px 0;
|
||||
padding-top: 8px;
|
||||
border-top: 1px dashed #f0f2f5;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
flex: 1;
|
||||
padding-left: 80px;
|
||||
margin-top: 4px;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.progress-text {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
font-size: 12px;
|
||||
color: #86909c;
|
||||
}
|
||||
|
||||
.task-result {
|
||||
display: flex;
|
||||
margin: 10px 0;
|
||||
font-size: 13px;
|
||||
padding-top: 8px;
|
||||
border-top: 1px dashed #f0f2f5;
|
||||
}
|
||||
|
||||
.result-normal {
|
||||
color: #00b42a;
|
||||
}
|
||||
|
||||
.result-abnormal {
|
||||
color: #f53f3f;
|
||||
}
|
||||
|
||||
.task-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
padding-top: 12px;
|
||||
border-top: 1px solid #f0f2f5;
|
||||
position: absolute;
|
||||
bottom: 16px;
|
||||
right: 16px;
|
||||
left: 16px;
|
||||
background-color: #fff;
|
||||
padding: 12px 0 0 0;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
font-size: 13px;
|
||||
padding: 4px 10px;
|
||||
}
|
||||
|
||||
.view-btn {
|
||||
color: #165dff;
|
||||
}
|
||||
|
||||
.view-btn:hover {
|
||||
color: #0e42d2;
|
||||
background-color: #e8f3ff;
|
||||
}
|
||||
|
||||
.start-btn {
|
||||
background-color: #165dff;
|
||||
border-color: #165dff;
|
||||
}
|
||||
|
||||
.start-btn:hover {
|
||||
background-color: #0e42d2;
|
||||
border-color: #0e42d2;
|
||||
}
|
||||
|
||||
.reschedule-btn {
|
||||
background-color: #ff7d00;
|
||||
border-color: #ff7d00;
|
||||
}
|
||||
|
||||
.reschedule-btn:hover {
|
||||
background-color: #e86a00;
|
||||
border-color: #e86a00;
|
||||
}
|
||||
|
||||
.complete-btn {
|
||||
background-color: #00b42a;
|
||||
border-color: #00b42a;
|
||||
}
|
||||
|
||||
.complete-btn:hover {
|
||||
background-color: #008718;
|
||||
border-color: #008718;
|
||||
}
|
||||
|
||||
.report-btn {
|
||||
background-color: #86909c;
|
||||
border-color: #86909c;
|
||||
}
|
||||
|
||||
.report-btn:hover {
|
||||
background-color: #6b7785;
|
||||
border-color: #6b7785;
|
||||
}
|
||||
|
||||
/* 分页区域样式 */
|
||||
.pagination-section {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.pagination-controls .el-pagination {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.pagination-controls .el-pagination button,
|
||||
.pagination-controls .el-pagination .el-pager li {
|
||||
min-width: 36px;
|
||||
height: 36px;
|
||||
line-height: 36px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.pagination-controls .el-pagination .el-pager li.active {
|
||||
background-color: #165dff;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 1200px) {
|
||||
.task-cards {
|
||||
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.inspection-tasks {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.filter-container {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.filter-actions {
|
||||
margin-left: 0;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.filter-bar .el-select {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.task-cards {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
/* 导航栏样式 */
|
||||
.navigation-tabs {
|
||||
display: flex;
|
||||
margin-bottom: 20px;
|
||||
background-color: #fff;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08);
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.nav-tab {
|
||||
padding: 12px 24px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
color: #606266;
|
||||
border-right: 1px solid #f0f0f0;
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.nav-tab:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.nav-tab:hover {
|
||||
color: #409eff;
|
||||
background-color: #ecf5ff;
|
||||
}
|
||||
|
||||
.nav-tab.active {
|
||||
background-color: #409eff;
|
||||
color: #fff;
|
||||
box-shadow: 0 2px 4px rgba(64, 158, 255, 0.3);
|
||||
}
|
||||
|
||||
.nav-tab {
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
</style>
|
||||
@ -3,14 +3,15 @@
|
||||
<div class="navigation-tabs">
|
||||
<div class="nav-tab" @click="handleInspection1">待办事项</div>
|
||||
<div class="nav-tab active" @click="handleInspection2">巡检管理</div>
|
||||
<div class="nav-tab">试验管理</div>
|
||||
<div class="nav-tab">报修管理</div>
|
||||
<div class="nav-tab">抢修管理</div>
|
||||
<div class="nav-tab">工单管理</div>
|
||||
<div class="nav-tab" @click="handleInspection3">试验管理</div>
|
||||
<div class="nav-tab" @click="handleInspection4">报修管理</div>
|
||||
<div class="nav-tab" @click="handleInspection5">抢修管理</div>
|
||||
<div class="nav-tab" @click="handleInspection6">工单管理</div>
|
||||
<div class="nav-tab" @click="handleInspection7">运维组织</div>
|
||||
</div>
|
||||
<div style="display: flex; align-items: center; gap: 1200px">
|
||||
<div class="header-container">
|
||||
<TitleComponent title="运维巡检管理" subtitle="制定巡检计划,安排巡检任务,跟进巡检记录"></TitleComponent>
|
||||
<div style="display: flex; align-items: center; gap: 10px">
|
||||
<div class="header-actions">
|
||||
<el-button type="primary" class="export-btn">筛选</el-button>
|
||||
<el-button type="primary" class="create-btn">导入数据</el-button>
|
||||
</div>
|
||||
@ -58,10 +59,10 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 主内容区 -->
|
||||
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6" style="grid-auto-rows: 1fr">
|
||||
<!-- 主内容区 - 使用flex确保等高 -->
|
||||
<div class="main-content-container">
|
||||
<!-- 左侧和中间内容区 -->
|
||||
<div class="lg:col-span-2 space-y-6">
|
||||
<div class="left-content">
|
||||
<!-- 巡检记录与报告卡片 -->
|
||||
<div class="content-card">
|
||||
<div class="card-header">
|
||||
@ -260,14 +261,14 @@
|
||||
</div>
|
||||
|
||||
<!-- 右侧最近巡检结果 -->
|
||||
<div class="lg:col-span-1" style="display: flex; flex-direction: column; height: 100%">
|
||||
<div class="content-card" style="flex: 1; display: flex; flex-direction: column">
|
||||
<div class="right-content">
|
||||
<div class="content-card h-full flex flex-col">
|
||||
<div class="card-header">
|
||||
<h2 class="card-title">最近巡检结果</h2>
|
||||
</div>
|
||||
|
||||
<!-- 巡检结果列表 -->
|
||||
<div class="card-body" style="flex: 1; overflow-y: auto">
|
||||
<!-- 巡检结果列表 - 添加滚动样式 -->
|
||||
<div class="card-body flex-1 overflow-y-auto scrollbar-thin">
|
||||
<div class="inspection-results space-y-4">
|
||||
<!-- 结果1:正常 -->
|
||||
<div class="inspection-card bg-white border border-gray-200 rounded-lg p-4 shadow-sm">
|
||||
@ -281,7 +282,7 @@
|
||||
<div class="flex flex-col items-center">
|
||||
<i class="fas fa-check-circle text-green-500 text-lg mb-1"></i>
|
||||
<span class="text-xs text-gray-600 mb-1">系统连接量</span>
|
||||
<span class="text-sm font-medium">128<500</span>
|
||||
<span class="text-sm font-medium">128</span>
|
||||
</div>
|
||||
<div class="flex flex-col items-center">
|
||||
<i class="fas fa-check-circle text-green-500 text-lg mb-1"></i>
|
||||
@ -421,6 +422,21 @@ const handleInspection1 = () => {
|
||||
const handleInspection2 = () => {
|
||||
router.push('/rili/InspectionManagement');
|
||||
};
|
||||
const handleInspection3 = () => {
|
||||
router.push('/rili/shiyanguanli');
|
||||
};
|
||||
const handleInspection4 = () => {
|
||||
router.push('/rili/baoxiuguanli');
|
||||
};
|
||||
const handleInspection5 = () => {
|
||||
router.push('/rili/qiangxiuguanli');
|
||||
};
|
||||
const handleInspection6 = () => {
|
||||
router.push('/rili/gongdanliebiao');
|
||||
};
|
||||
const handleInspection7 = () => {
|
||||
router.push('/rili/renyuanzhuangtai');
|
||||
};
|
||||
const handleInspectionManagement1 = () => {
|
||||
router.push('/rili/InspectionManagement');
|
||||
};
|
||||
@ -440,6 +456,19 @@ const handleInspectionManagement3 = () => {
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
/* 头部容器 - 替换了固定gap的flex布局 */
|
||||
.header-container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.header-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
/* 导航栏样式 */
|
||||
.navigation-tabs {
|
||||
display: flex;
|
||||
@ -527,12 +556,35 @@ const handleInspectionManagement3 = () => {
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
/* 主内容区 - 使用flex确保等高 */
|
||||
.main-content-container {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
height: calc(100% - 20px);
|
||||
min-height: 500px; /* 确保有最小高度 */
|
||||
}
|
||||
|
||||
.left-content {
|
||||
flex: 2; /* 左侧占2/3宽度 */
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.right-content {
|
||||
flex: 1; /* 右侧占1/3宽度 */
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* 内容卡片样式 */
|
||||
.content-card {
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
|
||||
overflow: hidden;
|
||||
height: 100%; /* 卡片高度占满容器 */
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
@ -552,6 +604,7 @@ const handleInspectionManagement3 = () => {
|
||||
|
||||
.card-body {
|
||||
padding: 0 20px;
|
||||
flex: 1; /* 内容区域占满剩余空间 */
|
||||
}
|
||||
|
||||
/* 统计卡片样式 */
|
||||
@ -657,8 +710,36 @@ const handleInspectionManagement3 = () => {
|
||||
background-color: #ecf5ff;
|
||||
}
|
||||
|
||||
/* 滚动条样式优化 */
|
||||
.scrollbar-thin {
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: #d1d5db #f3f4f6;
|
||||
}
|
||||
|
||||
.scrollbar-thin::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
.scrollbar-thin::-webkit-scrollbar-track {
|
||||
background: #f3f4f6;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.scrollbar-thin::-webkit-scrollbar-thumb {
|
||||
background-color: #d1d5db;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.scrollbar-thin::-webkit-scrollbar-thumb:hover {
|
||||
background-color: #9ca3af;
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 1200px) {
|
||||
.main-content-container {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.filter-bar {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
|
||||
@ -4,10 +4,11 @@
|
||||
<div class="navigation-tabs">
|
||||
<div class="nav-tab" @click="handleInspection1">待办事项</div>
|
||||
<div class="nav-tab active" @click="handleInspection2">巡检管理</div>
|
||||
<div class="nav-tab">试验管理</div>
|
||||
<div class="nav-tab">报修管理</div>
|
||||
<div class="nav-tab">抢修管理</div>
|
||||
<div class="nav-tab">工单管理</div>
|
||||
<div class="nav-tab" @click="handleInspection3">试验管理</div>
|
||||
<div class="nav-tab" @click="handleInspection4">报修管理</div>
|
||||
<div class="nav-tab" @click="handleInspection5">抢修管理</div>
|
||||
<div class="nav-tab" @click="handleInspection6">工单管理</div>
|
||||
<div class="nav-tab" @click="handleInspection7">运维组织</div>
|
||||
</div>
|
||||
<!-- 页面标题 -->
|
||||
<TitleComponent title="运维巡检管理" subtitle="制定巡检计划,安排巡检任务,跟进巡检记录"></TitleComponent>
|
||||
@ -49,7 +50,7 @@
|
||||
</el-select>
|
||||
</div>
|
||||
<div class="filter-actions">
|
||||
<el-button type="primary" class="search-btn" @click="handleSearch"> <i class="el-icon-search mr-1"></i>搜索 </el-button>
|
||||
<el-button type="primary" class="search-btn">搜索</el-button>
|
||||
<el-button type="primary" icon="el-icon-plus" class="create-btn" @click="handleCreateTask"> 手动创建任务 </el-button>
|
||||
</div>
|
||||
</div>
|
||||
@ -422,6 +423,21 @@ const handleInspection1 = () => {
|
||||
const handleInspection2 = () => {
|
||||
router.push('/rili/InspectionManagement');
|
||||
};
|
||||
const handleInspection3 = () => {
|
||||
router.push('/rili/shiyanguanli');
|
||||
};
|
||||
const handleInspection4 = () => {
|
||||
router.push('/rili/baoxiuguanli');
|
||||
};
|
||||
const handleInspection5 = () => {
|
||||
router.push('/rili/qiangxiuguanli');
|
||||
};
|
||||
const handleInspection6 = () => {
|
||||
router.push('/rili/gongdanliebiao');
|
||||
};
|
||||
const handleInspection7 = () => {
|
||||
router.push('/rili/运维组织');
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@ -511,8 +527,7 @@ const handleInspection2 = () => {
|
||||
.filter-actions {
|
||||
margin-left: auto;
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
flex-shrink: 0;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.search-btn {
|
||||
|
||||
1112
src/views/dhr_demo/zhixingjilu.vue
Normal file
1112
src/views/dhr_demo/zhixingjilu.vue
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,7 +1,6 @@
|
||||
<template>
|
||||
<div class="p-2">
|
||||
<el-row :gutter="20">
|
||||
<!-- 流程分类树 -->
|
||||
<el-col style="" :span="5">
|
||||
<el-card shadow="hover">
|
||||
<template #header>
|
||||
@ -73,6 +72,7 @@
|
||||
</template>
|
||||
|
||||
<el-table v-loading="loading" :data="cailiaoshebeiList" @selection-change="handleSelectionChange">
|
||||
<el-table-column label="所属上级" align="center" prop="suppliespricePname" />
|
||||
<el-table-column label="物资名称" align="center" prop="name" />
|
||||
<el-table-column label="质量标准" align="center" prop="qs" />
|
||||
<el-table-column label="规格型号" align="center" prop="specification" />
|
||||
@ -91,8 +91,6 @@
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 添加或修改物资-材料设备对话框 -->
|
||||
<el-dialog :close-on-click-modal="false" draggable :title="dialog.title" v-model="dialog.visible" width="1500px" append-to-body>
|
||||
<el-form :model="form" ref="cailiaoshebeiFormRef" :rules="rules" label-width="80px" :inline="false">
|
||||
<el-divider>基础信息</el-divider>
|
||||
@ -115,9 +113,7 @@
|
||||
</el-row>
|
||||
|
||||
<el-divider>主要信息</el-divider>
|
||||
<!-- 表格添加border属性,优化视觉体验 -->
|
||||
<el-table :data="form.planList" border>
|
||||
<!-- 版本号列 -->
|
||||
<el-table-column prop="batchNumber" align="center" label="版本号 " width="200">
|
||||
<template #default="scope">
|
||||
<el-select v-model="scope.row.batchNumber" placeholder="请选择" @change="(val) => selectNameVersion(val, scope.row, scope.$index)">
|
||||
@ -125,41 +121,41 @@
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- 物资名称列(核心修改:动态过滤已选选项) -->
|
||||
<el-table-column prop="name" align="center" label="物资名称" width="160">
|
||||
<template #default="scope">
|
||||
<el-input v-model="scope.row.name" v-if="scope.row.mrpBaseId" placeholder="请输入物资名称" disabled />
|
||||
<el-select
|
||||
<el-cascader
|
||||
v-else
|
||||
:disabled="!scope.row.batchNumber"
|
||||
v-model="scope.row.suppliespriceId"
|
||||
:show-all-levels="false"
|
||||
:options="getAvailableNameList(scope.$index)"
|
||||
placeholder="请选择"
|
||||
@change="(val) => selectName(val, scope.row, scope.$index)"
|
||||
>
|
||||
<!-- 动态过滤:排除当前行外已选中的物资ID -->
|
||||
<el-option v-for="item in getAvailableNameList(scope.$index)" :key="item.id" :label="item.name" :value="item.id" />
|
||||
</el-select>
|
||||
:props="{
|
||||
label: 'name',
|
||||
value: 'id',
|
||||
children: 'children',
|
||||
emitPath: false
|
||||
}"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- 剩余量列 -->
|
||||
<el-table-column align="center" label="剩余量" width="80">
|
||||
<template #default="scope">
|
||||
<span>{{ scope.row.Remaining }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- 规格型号列 -->
|
||||
<el-table-column prop="specification" align="center" label="规格型号" width="100">
|
||||
<template #default="scope">
|
||||
<span>{{ scope.row.specification }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- 单位列 -->
|
||||
<el-table-column prop="unit" align="center" label="单位" width="80">
|
||||
<template #default="scope">
|
||||
<span>{{ scope.row.unit }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- 数量列 -->
|
||||
<el-table-column prop="demandQuantity" align="center" label="数量" width="140">
|
||||
<template #default="scope">
|
||||
<el-input
|
||||
@ -171,31 +167,26 @@
|
||||
type="number"
|
||||
min="0"
|
||||
/>
|
||||
<!-- 数量错误提示 -->
|
||||
<div v-if="scope.row.quantityError" class="text-red-500 text-xs mt-1">{{ scope.row.quantityError }}</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- 质量标准列 -->
|
||||
<el-table-column prop="qs" align="center" label="质量标准" width="140">
|
||||
<template #header> <span class="text-red-500">*</span> 质量标准 </template>
|
||||
<template #default="scope">
|
||||
<el-input v-model="scope.row.qs" placeholder="请输入质量标准" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- 需求到货时间列 -->
|
||||
<el-table-column prop="arrivalTime" align="center" label="需求到货时间" width="180">
|
||||
<template #header> <span class="text-red-500">*</span> 需求到货时间 </template>
|
||||
<template #default="scope">
|
||||
<el-date-picker v-model="scope.row.arrivalTime" type="date" value-format="YYYY-MM-DD" placeholder="请选择" style="width: 140px" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- 备注列 -->
|
||||
<el-table-column prop="remark" align="center" label="备注">
|
||||
<template #default="scope">
|
||||
<el-input v-model="scope.row.remark" placeholder="请输入备注" disabled />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- 操作列 -->
|
||||
<el-table-column prop="remark" align="center" label="操作" width="150">
|
||||
<template #default="scope">
|
||||
<el-button @click="addRow" type="primary" icon="Plus" size="small" />
|
||||
@ -407,7 +398,15 @@ const validateDemandQuantity = (row: PlanListItem, index: number) => {
|
||||
const getMrpBaseRemaining = async (suppliespriceId: number, row: PlanListItem) => {
|
||||
try {
|
||||
const res = await mrpBaseRemaining({ suppliespriceId });
|
||||
row.Remaining = res.data || 0;
|
||||
const selected = res.data;
|
||||
console.log('🚀 ~ getMrpBaseRemaining ~ selected:', selected);
|
||||
row.Remaining = selected.remainingQuantity || 0;
|
||||
row.name = selected.name;
|
||||
row.specification = selected.specification;
|
||||
row.unit = selected.unit;
|
||||
row.qs = selected.qs || '';
|
||||
row.remark = selected.remark || '';
|
||||
row.arrivalTime = selected.arrivalTime || '';
|
||||
// 剩余量更新后,重新校验当前数量
|
||||
validateDemandQuantity(row, 0);
|
||||
} catch (error) {
|
||||
@ -431,17 +430,7 @@ const getAvailableNameList = (currentIndex: number) => {
|
||||
/** 选择物资名称触发 */
|
||||
const selectName = (val: number, row: PlanListItem, index: number) => {
|
||||
// 1. 获取剩余量并更新基础信息
|
||||
getMrpBaseRemaining(val, row).then(() => {
|
||||
const selected = nameList.value.find((item: any) => item.id === val);
|
||||
if (selected) {
|
||||
row.name = selected.name;
|
||||
row.specification = selected.specification;
|
||||
row.unit = selected.unit;
|
||||
row.qs = selected.qs || '';
|
||||
row.remark = selected.remark || '';
|
||||
row.arrivalTime = selected.arrivalTime || '';
|
||||
}
|
||||
});
|
||||
getMrpBaseRemaining(val, row);
|
||||
};
|
||||
|
||||
/** 节点单击事件 */
|
||||
|
||||
@ -421,6 +421,7 @@ const handleDetail = async (row?: PurchaseDocVO) => {
|
||||
/** 表单重置 */
|
||||
const reset = () => {
|
||||
form.value = { ...initFormData };
|
||||
planList.value = [];
|
||||
purchaseDocFormRef.value?.resetFields();
|
||||
form.value.projectId = currentProject.value?.id;
|
||||
};
|
||||
@ -494,7 +495,7 @@ const submitForm = () => {
|
||||
purchaseDocFormRef.value?.validate(async (valid: boolean) => {
|
||||
if (valid) {
|
||||
buttonLoading.value = true;
|
||||
form.value.associationList = selectPlanList.value;
|
||||
form.value.associationList = JSON.parse(JSON.stringify(selectPlanList.value));
|
||||
form.value.associationList.forEach((item: any) => {
|
||||
item.planId = item.id;
|
||||
delete item.id;
|
||||
|
||||
@ -58,7 +58,7 @@
|
||||
<el-table-column label="操作" align="center" fixed="right" min-width="200" fixed-width>
|
||||
<template #default="scope">
|
||||
<el-tooltip content="修改" placement="top">
|
||||
<el-button link type="primary" icon="View" @click="handleDetails(scope.row)" v-hasPermi="['mechanical:mechanicalrewriting:edit']"
|
||||
<el-button link type="primary" icon="View" @click="handleDetails(scope.row)" v-hasPermi="['mechanical:mechanicalrewriting:query']"
|
||||
>详情</el-button
|
||||
>
|
||||
</el-tooltip>
|
||||
|
||||
@ -121,7 +121,7 @@
|
||||
</el-dialog>
|
||||
<!-- 上传退场记录 -->
|
||||
<el-dialog title="员工离场" v-model="memberStatus" width="30%">
|
||||
<el-form :model="memberForm" ref="memberFormRef" :rules="memberRules" label-width="100px" :inline="false">
|
||||
<el-form :model="memberForm" ref="memberFormRef" :rules="memberRules" label-width="120px" :inline="false">
|
||||
<el-form-item label="用户名">
|
||||
<el-input v-model="memberForm.userName" disabled></el-input>
|
||||
</el-form-item>
|
||||
@ -202,9 +202,10 @@ const memberStatus = ref(false);
|
||||
interface Props {
|
||||
projectTeamVo: ProjectTeamVO;
|
||||
}
|
||||
const memberForm = reactive<ConstructionUserMembeForm>({
|
||||
const memberForm = reactive<any>({
|
||||
id: undefined,
|
||||
filePath: undefined,
|
||||
salaryConfirmationFile: undefined,
|
||||
salaryVoucherFile: undefined,
|
||||
remark: undefined,
|
||||
userName: undefined,
|
||||
wxOrPc: 1
|
||||
@ -391,6 +392,8 @@ const handleExit = async (row?: ProjectTeamMemberVO) => {
|
||||
memberForm.userName = row?.userName;
|
||||
console.log('🚀 ~ handleDelete ~ row:', row);
|
||||
memberForm.id = row?.id;
|
||||
memberForm.salaryVoucherFile = '';
|
||||
memberForm.salaryConfirmationFile = '';
|
||||
memberStatus.value = true;
|
||||
};
|
||||
|
||||
|
||||
@ -271,6 +271,7 @@ const listeningProject = watch(
|
||||
queryParams.value.projectId = nid;
|
||||
form.value.projectId = nid;
|
||||
getList();
|
||||
getClockIn();
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@ -62,11 +62,11 @@
|
||||
</el-descriptions>
|
||||
<el-descriptions :column="2" border label-width="160px" size="large">
|
||||
<el-descriptions-item label-align="center" label="整改措施及完成情况" :span="2" label-class-name="white">
|
||||
{{ safetyInspectionDetail?.verificationResult }}
|
||||
{{ safetyInspectionDetail?.rectificationResult }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label-align="center" label="整改附件" :span="2" label-class-name="white">
|
||||
<el-space wrap>
|
||||
<div v-for="item in rectificationFileList" :key="item.ossId">
|
||||
<div v-for="item in safetyInspectionDetail?.rectificationImgFileList" :key="item.ossId">
|
||||
<span v-if="['.png', '.jpg', '.jpeg'].includes(item.fileSuffix)">
|
||||
<image-preview :src="item.url" width="200px" />
|
||||
</span>
|
||||
@ -136,14 +136,14 @@ const get = async () => {
|
||||
if (res.data && res.code === 0) {
|
||||
safetyInspectionDetail.value = res.data.details;
|
||||
|
||||
if (res.data.rectificationFileList) {
|
||||
if (res.data.inspectionFile) {
|
||||
const checkFileRes = await listByIds(res.data.inspectionFile.split(','));
|
||||
checkFileList.value = checkFileRes.data;
|
||||
}
|
||||
if (res.data.rectificationFile) {
|
||||
const rectificationFileRes = await listByIds(res.data.rectificationFile.split(','));
|
||||
rectificationFileList.value = rectificationFileRes.data;
|
||||
}
|
||||
// if (res.data.rectificationImgFile) {
|
||||
// const rectificationFileRes = await listByIds(res.data.rectificationFile.split(','));
|
||||
// rectificationFileList.value = rectificationFileRes.data;
|
||||
// }
|
||||
}
|
||||
};
|
||||
|
||||
@ -219,6 +219,7 @@ watch(
|
||||
z-index: 10;
|
||||
width: 105px;
|
||||
height: 105px;
|
||||
|
||||
img {
|
||||
width: 105px;
|
||||
}
|
||||
|
||||
@ -22,7 +22,10 @@
|
||||
>{{ safetyInspectionDetail?.abarbeitung }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label-align="center" label="要求整改期限" label-class-name="white">
|
||||
{{ dayjs(safetyInspectionDetail?.rectificationDeadline).format('YYYY 年 MM 月 DD 日') }}
|
||||
<span v-if="safetyInspectionDetail?.rectificationDeadline">{{
|
||||
dayjs(safetyInspectionDetail?.rectificationDeadline).format('YYYY 年 MM 月 DD 日')
|
||||
}}</span>
|
||||
<span v-else></span>
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<el-descriptions border direction="vertical" size="large">
|
||||
@ -69,7 +72,7 @@
|
||||
</el-descriptions>
|
||||
<el-descriptions :column="2" border label-width="160px" size="large">
|
||||
<el-descriptions-item label-align="center" label="班组" label-class-name="white"
|
||||
>{{ safetyInspectionDetail?.teamName }}
|
||||
>{{ safetyInspectionDetail?.rectificationName }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label-align="center" label="整改日期" label-class-name="white"
|
||||
>{{ safetyInspectionDetail?.rectificationTime }}
|
||||
@ -240,6 +243,7 @@ watch(
|
||||
z-index: 10;
|
||||
width: 105px;
|
||||
height: 105px;
|
||||
|
||||
img {
|
||||
width: 105px;
|
||||
}
|
||||
|
||||
@ -20,7 +20,7 @@
|
||||
<template #header>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['safety:safetyLog:add']">新增 </el-button>
|
||||
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['safety:safetyLog:add']">新1增 </el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="Upload" @click="handleAdd('file')" v-hasPermi="['safety:safetyLog:add']">上传附件 </el-button>
|
||||
@ -259,6 +259,7 @@ const data = reactive<PageData<SafetyLogForm, SafetyLogQuery>>({
|
||||
otherCondition: undefined,
|
||||
remark: undefined,
|
||||
creatorName: undefined,
|
||||
|
||||
params: {}
|
||||
},
|
||||
rules: {
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
<el-descriptions-item label="开会时间">{{ dayjs(teamMeetingDetail?.meetingDate).format('YYYY 年 MM 月 DD 日') }}</el-descriptions-item>
|
||||
<el-descriptions-item label="上传时间">{{ teamMeetingDetail?.createdAt }}</el-descriptions-item>
|
||||
<el-descriptions-item :span="2" label="班会内容">{{ teamMeetingDetail?.content }}</el-descriptions-item>
|
||||
<el-descriptions-item :span="2" label="班会图片">
|
||||
<el-descriptions-item :span="2" label="班会图片" v-if="teamMeetingDetail?.pictureUrlList">
|
||||
<el-space wrap>
|
||||
<span :key="item" v-for="item in teamMeetingDetail?.pictureUrlList">
|
||||
<image-preview :src="'http://58.17.134.85:8920' + item" width="200px" />
|
||||
|
||||
@ -56,10 +56,33 @@
|
||||
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
|
||||
</el-card>
|
||||
<!-- 添加或修改站班会对话框 -->
|
||||
<el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
|
||||
<el-dialog :title="dialog.title" v-model="dialog.visible" width="50%" append-to-body>
|
||||
<el-form ref="teamMeetingFormRef" :model="form" :rules="rules" label-width="80px">
|
||||
<el-form-item label="开会时间" prop="meetingDate">
|
||||
<el-date-picker clearable v-model="form.meetingDate" type="date" value-format="YYYY-MM-DD" placeholder="请选择开会时间" />
|
||||
<el-date-picker clearable v-model="form.meetingDate" type="date" value-format="YYYY-MM-DD HH:mm:ss" placeholder="请选择开会时间" />
|
||||
</el-form-item>
|
||||
<el-form-item label="班组名称" prop="teamId">
|
||||
<el-select v-model="form.teamId" placeholder="请选择班组">
|
||||
<el-option v-for="item in teamList" :key="item.id" :label="item.name" :value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="施工单位" prop="contractorId">
|
||||
<el-select v-model="form.contractorId" placeholder="请选择施工单位">
|
||||
<el-option v-for="item in contractorList" :key="item.id" :label="item.name" :value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="宣讲人" prop="compereId">
|
||||
<el-select v-model="form.compereId" placeholder="请选择宣讲人">
|
||||
<el-option v-for="item in compereList" :key="item.memberId" :label="item.memberName" :value="item.memberId" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="参与人" prop="participantIdList">
|
||||
<el-select v-model="form.participantIdList" placeholder="请选择参与人" multiple>
|
||||
<el-option v-for="item in participantList" :key="item.memberId" :label="item.memberName" :value="item.memberId" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="班会主题" prop="meetingTheme">
|
||||
<el-input v-model="form.meetingTheme" placeholder="请输入班会主题" />
|
||||
</el-form-item>
|
||||
<el-form-item label="班会内容">
|
||||
<editor v-model="form.content" :min-height="192" />
|
||||
@ -104,6 +127,10 @@ const ids = ref<Array<string | number>>([]);
|
||||
const single = ref(true);
|
||||
const multiple = ref(true);
|
||||
const total = ref(0);
|
||||
const teamList = ref<any>([]);
|
||||
const contractorList = ref<any>([]);
|
||||
const compereList = ref<any>([]);
|
||||
const participantList = ref<any>([]);
|
||||
|
||||
const queryFormRef = ref<ElFormInstance>();
|
||||
const teamMeetingFormRef = ref<ElFormInstance>();
|
||||
@ -123,7 +150,8 @@ const initFormData: TeamMeetingForm = {
|
||||
participantIdList: undefined,
|
||||
content: undefined,
|
||||
pictureList: undefined,
|
||||
remark: undefined
|
||||
remark: undefined,
|
||||
meetingTheme: undefined
|
||||
};
|
||||
const data = reactive<PageData<TeamMeetingForm, TeamMeetingQuery>>({
|
||||
form: { ...initFormData },
|
||||
@ -240,9 +268,30 @@ onUnmounted(() => {
|
||||
listeningProject();
|
||||
});
|
||||
console.log(11);
|
||||
const getTeamAndContractor = async () => {
|
||||
const projectId = currentProject.value?.id;
|
||||
const {
|
||||
data: { teamList: teamListRes, contractorList: contractorListRes }
|
||||
} = await getProjectContractorTeamList(projectId);
|
||||
console.log(teamListRes, contractorListRes);
|
||||
|
||||
teamList.value = teamListRes;
|
||||
contractorList.value = contractorListRes;
|
||||
};
|
||||
const getUsers = async () => {
|
||||
const { rows } = await listProjectTeamMember({
|
||||
projectId: currentProject.value?.id
|
||||
});
|
||||
compereList.value = rows.filter((item) => item.postId == 1);
|
||||
participantList.value = rows.filter((item) => item.postId == 0);
|
||||
};
|
||||
const getData = async () => {
|
||||
await getTeamAndContractor();
|
||||
await getUsers();
|
||||
};
|
||||
onMounted(() => {
|
||||
console.log(122);
|
||||
getList();
|
||||
getData();
|
||||
});
|
||||
</script>
|
||||
|
||||
@ -7,15 +7,23 @@
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="中标文件" prop="name">
|
||||
<file-upload v-model="form.costEstimationFile" :fileSize="100" :auto-upload="false"
|
||||
uploadUrl="/tender/biddingPlan/uploadBiddingDocuments" method="put" ref="fileUploadRef" :data="{
|
||||
<file-upload
|
||||
v-model="form.costEstimationFile"
|
||||
:fileSize="100"
|
||||
:auto-upload="false"
|
||||
uploadUrl="/tender/biddingPlan/uploadBiddingDocuments"
|
||||
method="put"
|
||||
ref="fileUploadRef"
|
||||
:data="{
|
||||
projectId: currentProject?.id,
|
||||
type: planType,
|
||||
fileType: '0',
|
||||
bidStatus: '0',
|
||||
id: row.id,
|
||||
winningBidderId: form.winningBidder
|
||||
}" showFileList />
|
||||
}"
|
||||
showFileList
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
@ -75,7 +83,7 @@ const submitForm = () => {
|
||||
fileUploadRef.value.submitUpload().then((res) => {
|
||||
if (res == 'noFile') {
|
||||
ElMessage({
|
||||
message: '请上传招标文件',
|
||||
message: '请上传中标文件',
|
||||
type: 'warning'
|
||||
});
|
||||
return;
|
||||
|
||||
@ -113,15 +113,7 @@
|
||||
<!-- <el-button type="primary" link icon="Edit" @click="handleSave(scope.row)" v-hasPermi="['tender:segmentedIndicatorPlanning:edit']"
|
||||
>信息
|
||||
</el-button> -->
|
||||
<el-button
|
||||
type="primary"
|
||||
link
|
||||
icon="View"
|
||||
@click="handleDetail(scope.row)"
|
||||
:disabled="scope.row.bidStatus == 1"
|
||||
v-hasPermi="['tender:biddingPlan:getMore']"
|
||||
>详情</el-button
|
||||
>
|
||||
<el-button type="primary" link icon="View" @click="handleDetail(scope.row)" v-hasPermi="['tender:biddingPlan:getMore']">详情</el-button>
|
||||
|
||||
<el-button
|
||||
type="primary"
|
||||
|
||||
@ -549,6 +549,8 @@ const splitBackEndStrToForm = (resData: any) => {
|
||||
};
|
||||
/** 审核过程按钮操作 */
|
||||
const handleAudit = async (row) => {
|
||||
if(!row.fileId)return proxy?.$modal.msgError('用户资料未上传');
|
||||
|
||||
proxy.$tab.closePage(proxy.$route);
|
||||
proxy.$router.push({
|
||||
path: `/approval/supplierInput/indexEdit`,
|
||||
|
||||
Reference in New Issue
Block a user