Files
maintenance_system/src/views/zhinengxunjian/baoxiuguanli.vue
2025-09-22 15:42:13 +08:00

1462 lines
37 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<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="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.leftLineClass">
<div class="task-header">
<div class="task-title">
{{ task.title }}
</div>
<!-- 恢复优先级标签背景色样式 -->
<div class="task-status" :class="task.priorityClass">
{{ task.priority }}
</div>
</div>
<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>
<div class="task-actions">
<el-button type="text" class="action-btn view-btn" @click="handleView(task)"> 详情 </el-button>
<el-button type="primary" :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="900px"
:before-close="handleCancelCreateTask"
custom-class="beautiful-dialog"
center
>
<el-form ref="createTaskFormRef" :model="createTaskForm" :rules="createTaskRules" label-width="120px" 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-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="high" />
<el-option label="中优先级" value="medium" />
<el-option label="低优先级" value="low" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-form-item label="指派维修人*" prop="sendPerson">
<el-select v-model="createTaskForm.sendPerson" placeholder="请选择维修人" :loading="loadingUsers">
<el-option v-for="user in usersList" :key="user.id" :label="user.name" :value="user.id" />
</el-select>
</el-form-item>
<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="上传图片(可选)" width="100%">
<file-upload v-model="createTaskForm.file" :fileType="['png', 'jpg', 'jpeg']">
<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">支持JPGPNG格式最多3张</div>
</div>
</div>
</file-upload>
<!-- <el-upload
class="avatar-uploader"
:file-list="createTaskForm.fileList"
:on-change="handleFileChange"
:before-remove="beforeRemove"
:on-remove="handleRemove"
:auto-upload="false"
multiple
:limit="3"
:on-exceed="handleExceed"
>
</el-upload> -->
<div v-if="createTaskForm.fileList && createTaskForm.fileList.length > 0" class="upload-tip">
<span style="color: #1989fa">已选择{{ createTaskForm.fileList.length }}张图片将在提交时上传</span>
</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>
<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, onMounted } from 'vue';
import router from '@/router';
import TitleComponent from './TitleComponent.vue';
import { baoxiulist, baoxiuDetail, delbaoxiu, updatebaoxiu, addbaoxiu, uploadbaoxiu } from '@/api/zhinengxunjian/baoxiou/index';
import { xunjianUserlist } from '@/api/zhinengxunjian/xunjian';
import { ElMessage } from 'element-plus';
// 激活的选项卡
const activeTab = ref('task');
// 筛选条件
const taskStatus = ref('');
const planType = ref('');
const executor = ref('');
// 任务数据 - 初始为空数组
const tasks = ref([]);
// 分页相关
const currentPage = ref(1);
const pageSize = ref(8);
const total = ref(0);
const loading = ref(false);
// 获取报修任务列表
defineExpose({ getTaskList });
async function getTaskList() {
loading.value = true;
try {
const res = await baoxiulist({
pageNum: currentPage.value,
pageSize: pageSize.value,
status: taskStatus.value,
type: planType.value,
executor: executor.value
});
if (res.code === 200 && res.rows) {
total.value = res.total || 0;
// 将API返回的数据转换为前端显示所需的格式
tasks.value = res.rows.map((item) => ({
id: item.id,
title: item.name || '未命名报修任务',
status: mapStatusToKey(item.status),
statusText: getStatusText(item.status),
leftLineClass: getLeftLineClass(item.status, item.level),
priorityClass: getPriorityClass(item.level),
priority: getPriorityText(item.level),
reportTime: formatDate(item.reportTime),
reporter: item.reportName || '未知报修人',
maintainer: item.sendPerson ? `维修人ID: ${item.sendPerson}` : '未分配',
expectedCompleteTime: getExpectedCompleteTime(item.status),
completeTime: item.completeTime ? formatDate(item.completeTime) : '',
actionText: getActionText(item.status),
actionClass: getActionClass(item.status),
reportInfo: item.reportInfo,
position: item.position,
fileUrl: item.fileUrl
}));
} else {
tasks.value = [];
total.value = 0;
console.error('获取报修任务列表失败:', res.msg);
}
} catch (error) {
console.error('获取报修任务列表异常:', error);
tasks.value = [];
total.value = 0;
} finally {
loading.value = false;
}
}
// 状态映射辅助函数
function mapStatusToKey(status) {
const statusMap = {
'1': 'pending', // 待处理
'2': 'executing', // 处理中
'3': 'completed' // 已完成
};
return statusMap[status] || 'pending';
}
// 获取状态文本
function getStatusText(status) {
const statusMap = {
'1': '待处理',
'2': '处理中',
'3': '已完成'
};
return statusMap[status] || '未知状态';
}
// 获取优先级文本
function getPriorityText(level) {
const levelMap = {
'1': '低优先级',
'2': '中优先级',
'3': '高优先级'
};
return levelMap[level] || '普通优先级';
}
// 获取优先级样式类
function getPriorityClass(level) {
const levelMap = {
'1': 'priority-low',
'2': 'priority-medium',
'3': 'priority-high'
};
return levelMap[level] || 'priority-low';
}
// 获取左侧状态线样式类
function getLeftLineClass(status, level) {
if (status === '3') return 'left-line-completed';
const levelMap = {
'1': 'left-line-low',
'2': 'left-line-medium',
'3': 'left-line-high'
};
return levelMap[level] || 'left-line-low';
}
// 获取操作按钮文本
function getActionText(status) {
const actionMap = {
'1': '分配',
'2': '跟进',
'3': '评价'
};
return actionMap[status] || '查看';
}
// 获取操作按钮样式类
function getActionClass(status) {
const actionMap = {
'1': 'assign-btn',
'2': 'follow-btn',
'3': 'evaluate-btn'
};
return actionMap[status] || 'view-btn';
}
// 获取预计完成时间(根据状态和优先级估算)
function getExpectedCompleteTime(status) {
if (status === '3') return ''; // 已完成任务不需要显示预计时间
const now = new Date();
// 简单估算待处理任务默认当天18:00前完成处理中任务默认2小时内完成
if (status === '1') {
now.setHours(18, 0, 0, 0);
} else {
now.setHours(now.getHours() + 2);
}
return formatDate(now);
}
// 格式化日期时间
function formatDate(date) {
if (!date) return '';
const d = new Date(date);
if (isNaN(d.getTime())) return '';
const year = d.getFullYear();
const month = String(d.getMonth() + 1).padStart(2, '0');
const day = String(d.getDate()).padStart(2, '0');
const hours = String(d.getHours()).padStart(2, '0');
const minutes = String(d.getMinutes()).padStart(2, '0');
return `${year}-${month}-${day} ${hours}:${minutes}`;
}
// 状态排序映射
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; // 重置到第一页
getTaskList(); // 调用接口获取数据
};
// 创建报修任务弹窗相关
const createTaskDialogVisible = ref(false);
const createTaskFormRef = ref(null);
const createTaskForm = ref({
repairName: '',
repairType: '',
priority: '',
detailedDescription: '',
faultLocation: '',
contactPerson: '',
contactPhone: '',
sendPerson: '', // 指派维修人
file: '' // 上传的文件列表
});
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' }],
sendPerson: [{ required: true, message: '请选择维修人', trigger: 'change' }]
};
// 用户列表(维修人)
const usersList = ref([]);
const loadingUsers = ref(false);
// 获取用户列表
const getUsersList = async () => {
loadingUsers.value = true;
try {
const res = await xunjianUserlist();
// 根据接口返回格式成功码是200用户数据在rows数组中
if (res.code === 200 && res.rows && Array.isArray(res.rows)) {
// 映射用户数据使用id作为valueuserName作为显示名称
usersList.value = res.rows.map((user) => ({
id: user.id,
name: user.userName
}));
} else {
usersList.value = [];
console.error('获取用户列表失败:', res.msg || '未知错误');
}
} catch (error) {
console.error('获取用户列表异常:', error);
usersList.value = [];
} finally {
loadingUsers.value = false;
}
};
const isSubmitting = ref(false); // 防止重复提交的状态标记
const isUploading = ref(false); // 防止重复上传的状态标记
// 上传文件方法
const uploadFiles = async (files) => {
// 防止重复上传
if (isUploading.value) {
ElMessage.warning('数据正在处理中,请稍候...');
return [];
}
try {
isUploading.value = true;
const formData = new FormData();
// 关键修复:将字段名从'files'改为'file',匹配后端要求的'file'字段
files.forEach((file) => {
formData.append('file', file.raw); // 这里将'files'改为'file'
});
const res = await uploadbaoxiu(formData);
if (res.code === 200 && res.data && Array.isArray(res.data)) {
return res.data.map((item) => ({
fileId: item.ossId,
fileUrl: item.url
}));
} else {
console.error('文件上传失败:', res.msg);
throw new Error(res.msg || '文件上传失败');
}
} catch (error) {
console.error('文件上传异常:', error);
if (error.message.includes('重复提交')) {
throw new Error('操作过于频繁,请稍后再试');
}
throw error;
} finally {
isUploading.value = false;
}
};
const handleFileChange = (file, fileList) => {
// 只处理刚添加的文件
if (file.status === 'ready') {
// 验证文件格式和大小
const isJPG = file.raw.type === 'image/jpeg' || file.raw.type === 'image/jpg';
const isPNG = file.raw.type === 'image/png';
const isLt2M = file.size / 1024 / 1024 < 2;
if (!isJPG && !isPNG) {
ElMessage.error('上传图片只能是 JPG/JPEG 或 PNG 格式!');
// 从文件列表中移除不符合要求的文件
createTaskForm.value.fileList = fileList.filter((f) => f.uid !== file.uid);
return;
}
if (!isLt2M) {
ElMessage.error('上传图片大小不能超过 2MB!');
// 从文件列表中移除不符合要求的文件
createTaskForm.value.fileList = fileList.filter((f) => f.uid !== file.uid);
return;
}
// 保存验证通过的文件到表单
createTaskForm.value.fileList = fileList;
}
};
const handleExceed = (files, fileList) => {
ElMessage.warning(`当前限制选择 3 张图片,本次选择了 ${files.length} 张,共选择了 ${files.length + fileList.length}`);
};
// 文件移除前的钩子
const beforeRemove = (file, fileList) => {
return window.confirm(`确定移除 ${file.name}`);
};
// 文件移除时的钩子
const handleRemove = (file, fileList) => {
createTaskForm.value.fileList = fileList;
ElMessage.info(`已移除 ${file.name}`);
};
// 创建任务
const handleCreateTask = async () => {
createTaskDialogVisible.value = true;
// 打开弹窗时获取用户列表
await getUsersList();
};
// 保存报修任务
const handleSaveTask = async () => {
console.log(createTaskForm.value.file);
// 防止重复提交
if (!createTaskFormRef.value || isSubmitting.value) return;
try {
// 设置提交状态为true
isSubmitting.value = true;
// 先验证表单
await createTaskFormRef.value.validate();
// 上传选中的文件
let fileIds = [];
let fileUrls = [];
if (createTaskForm.value.fileList && createTaskForm.value.fileList.length > 0) {
ElMessage.info(`开始上传 ${createTaskForm.value.fileList.length} 张图片...`);
const uploadResults = await uploadFiles(createTaskForm.value.fileList);
// 提取上传结果
fileIds = uploadResults.map((result) => result.fileId);
fileUrls = uploadResults.map((result) => result.fileUrl);
ElMessage.success(`图片上传成功,共 ${uploadResults.length}`);
}
// 准备请求数据
const requestData = {
projectId: 1,
name: createTaskForm.value.repairName,
type: mapRepairType(createTaskForm.value.repairType),
status: '1', // 默认为待处理状态
level: mapPriorityLevel(createTaskForm.value.priority),
sendPerson: parseInt(createTaskForm.value.sendPerson),
reportInfo: createTaskForm.value.detailedDescription,
position: createTaskForm.value.faultLocation,
fileId: fileIds.join(','), // 用逗号分隔多个文件ID
fileUrl: fileUrls.join(','), // 用逗号分隔多个文件URL
reportName: createTaskForm.value.contactPerson,
reportPhone: createTaskForm.value.contactPhone
};
// 调用添加报修任务接口
const res = await addbaoxiu(requestData);
// 检查接口返回是否成功使用code=200作为成功标志
if (res.code === 200) {
// 显示成功提示
ElMessage.success('报修任务创建成功');
// 关闭弹窗
createTaskDialogVisible.value = false;
// 重置表单
resetCreateForm();
// 刷新任务列表
await getTaskList();
} else {
ElMessage.error(`创建失败:${res.msg || '未知错误'}`);
}
} catch (error) {
// 错误处理
if (error instanceof Error) {
ElMessage.error(`操作失败:${error.message}`);
} else if (error === false) {
// 表单验证失败,不做处理
} else {
ElMessage.error('操作失败:未知错误');
}
} finally {
// 无论成功失败,都重置提交状态
isSubmitting.value = false;
}
};
// 重置创建表单
function resetCreateForm() {
createTaskForm.value = {
repairName: '',
repairType: '',
priority: '',
detailedDescription: '',
faultLocation: '',
contactPerson: '',
contactPhone: '',
sendPerson: '',
fileList: []
};
if (createTaskFormRef.value) {
createTaskFormRef.value.resetFields();
}
}
// 报修类型映射 - 1硬件2软件
function mapRepairType(type) {
const typeMap = {
'device': '1', // 硬件
'software': '2' // 软件
};
return typeMap[type] || '1';
}
// 优先级映射 - 1低优先2中优先3高优先
function mapPriorityLevel(priority) {
const levelMap = {
'low': '1', // 低优先
'medium': '2', // 中优先
'high': '3' // 高优先
};
return levelMap[priority] || '2';
}
// 文件上传前的校验
async function beforeUpload(file) {
const isJPG = file.type === 'image/jpeg' || file.type === 'image/jpg';
const isPNG = file.type === 'image/png';
const isLt2M = file.size / 1024 / 1024 < 2;
if (!isJPG && !isPNG) {
ElMessage.error('上传图片只能是 JPG/JPEG 或 PNG 格式!');
return false;
}
if (!isLt2M) {
ElMessage.error('上传图片大小不能超过 2MB!');
return false;
}
try {
// 直接上传文件
const uploadResult = await uploadFile(file);
// 存储文件信息到表单
createTaskForm.value.fileList = [
{
...file,
fileId: uploadResult.fileId,
fileUrl: uploadResult.fileUrl
}
];
ElMessage.success('图片上传成功');
} catch (error) {
ElMessage.error('图片上传失败,请重试');
}
// 阻止自动上传
return false;
}
// 文件上传成功处理
function handleUploadSuccess(response, file, fileList) {
// 这个函数实际上不会被调用因为auto-upload=false
// 真正的上传逻辑在beforeUpload函数中处理
}
// 文件上传失败处理
function handleUploadError(err, file, fileList) {
ElMessage.error('上传失败,请稍后重试');
}
// 取消创建报修任务
const handleCancelCreateTask = () => {
createTaskDialogVisible.value = false;
// 重置表单
resetCreateForm();
};
// 分页事件
const handleSizeChange = (val) => {
pageSize.value = val;
currentPage.value = 1;
getTaskList(); // 重新获取数据
};
const handleCurrentChange = (val) => {
currentPage.value = val;
getTaskList(); // 重新获取数据
};
// 查看任务详情
const handleView = (task) => {
console.log('查看任务详情:', task);
};
// 处理选项卡点击
const handleTabClick = () => {
currentPage.value = 1;
};
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');
};
// 组件挂载时获取数据
onMounted(async () => {
// 只获取任务列表,用户列表在弹窗打开时获取
await getTaskList();
});
</script>
<style scoped>
.inspection-tasks {
padding: 20px;
background-color: #f5f7fa;
min-height: 100vh;
}
/* 上传图片区域样式 */
.upload-container {
width: 100%;
}
.upload-box {
border: 2px dashed #dcdfe6;
border-radius: 8px;
padding: 40px 20px;
text-align: center;
cursor: pointer;
background-color: #f8f9fa;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
position: relative;
overflow: hidden;
}
.upload-box::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(64, 158, 255, 0.1), transparent);
transition: all 0.6s ease;
}
.upload-box:hover {
border-color: #409eff;
background-color: #ecf5ff;
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(64, 158, 255, 0.15);
}
.upload-box:hover::before {
left: 100%;
}
.avatar-uploader-icon {
font-size: 40px;
color: #c0c4cc;
margin-bottom: 12px;
transition: color 0.3s;
}
.upload-box:hover .avatar-uploader-icon {
color: #409eff;
}
.upload-text {
font-size: 14px;
color: #606266;
font-weight: 500;
margin-bottom: 6px;
transition: color 0.3s;
}
.upload-box:hover .upload-text {
color: #409eff;
}
.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;
}
.beautiful-dialog .el-dialog__header {
background: linear-gradient(135deg, #409eff, #67c23a);
padding: 20px 24px;
border-bottom: none;
}
.beautiful-dialog .el-dialog__title {
color: #fff;
font-size: 18px;
font-weight: 600;
text-align: center;
margin: 0;
}
.beautiful-dialog .el-dialog__headerbtn {
top: 20px;
}
.beautiful-dialog .el-dialog__headerbtn .el-dialog__close {
color: #fff;
font-size: 18px;
transition: all 0.3s;
}
.beautiful-dialog .el-dialog__headerbtn .el-dialog__close:hover {
color: #fff;
opacity: 0.8;
transform: rotate(90deg);
}
.beautiful-dialog .el-dialog__body {
padding: 30px 32px;
background-color: #fff;
}
.beautiful-dialog .el-dialog__footer {
padding: 20px 32px;
background-color: #f8f9fa;
border-top: 1px solid #f0f2f5;
text-align: center;
}
/* 美化表单样式 */
.elegant-form .el-form-item {
margin-bottom: 24px;
}
.elegant-form .el-form-item__label {
font-weight: 500;
color: #303133;
font-size: 14px;
padding-right: 16px;
}
.elegant-form .el-input__inner,
.elegant-form .el-textarea__inner,
.elegant-form .el-select .el-input__inner {
border-radius: 6px;
border: 1px solid #dcdfe6;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
font-size: 14px;
padding: 0 15px;
height: 38px;
line-height: 38px;
}
.elegant-form .el-textarea__inner {
padding: 10px 15px;
height: auto;
line-height: 1.5;
resize: vertical;
min-height: 100px;
}
.elegant-form .el-input__inner:focus,
.elegant-form .el-textarea__inner:focus,
.elegant-form .el-select .el-input__inner:focus {
border-color: #409eff;
box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.2);
outline: none;
}
/* 美化按钮样式 */
.dialog-footer .el-button {
min-width: 80px;
height: 36px;
border-radius: 6px;
font-size: 14px;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
margin: 0 8px;
}
.dialog-footer .el-button--primary {
background-color: #409eff;
border-color: #409eff;
color: #fff;
font-weight: 500;
}
.dialog-footer .el-button--primary:hover {
background-color: #66b1ff;
border-color: #66b1ff;
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(64, 158, 255, 0.3);
}
.dialog-footer .el-button--default {
background-color: #fff;
border-color: #dcdfe6;
color: #606266;
}
.dialog-footer .el-button--default:hover {
background-color: #f5f7fa;
border-color: #c6e2ff;
color: #409eff;
transform: translateY(-1px);
}
/* 美化下拉选择框 */
.elegant-form .el-select .el-input {
width: 100%;
}
.elegant-form .el-select-dropdown {
border-radius: 6px;
border: 1px solid #e4e7ed;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
overflow: hidden;
}
.elegant-form .el-select-dropdown__item {
padding: 0 20px;
height: 36px;
line-height: 36px;
transition: all 0.3s;
}
.elegant-form .el-select-dropdown__item:hover {
background-color: #ecf5ff;
color: #409eff;
}
.elegant-form .el-select-dropdown__item.selected {
background-color: #ecf5ff;
color: #409eff;
}
/* 添加表单验证反馈样式 */
.elegant-form .el-form-item__error {
color: #f56c6c;
font-size: 12px;
padding-top: 4px;
position: absolute;
bottom: -20px;
left: 0;
}
/* 响应式设计 */
@media screen and (max-width: 768px) {
.beautiful-dialog {
width: 90% !important;
margin: 0 auto;
}
.beautiful-dialog .el-dialog__body {
padding: 20px 24px;
}
.elegant-form .el-form-item {
margin-bottom: 20px;
}
.upload-box {
padding: 30px 15px;
}
}
/* 选项卡样式 */
.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,
.create-btn {
height: 36px;
border-radius: 4px;
}
/* 任务卡片样式 - 恢复优先级标签背景色 */
.task-cards {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(310px, 1fr));
gap: 20px;
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: 260px; /* 增加高度以适应新增的预计完成时间 */
}
.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-actions .el-button {
border-radius: 16px;
padding: 6px 16px;
}
/* 左侧状态线样式 */
.task-card::before {
content: '';
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: 4px;
}
.left-line-high::before {
background-color: #ff4d4f;
}
.left-line-medium::before {
background-color: #fa8c16;
}
.left-line-low::before {
background-color: #1677ff;
}
.left-line-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;
}
.priority-high {
background-color: #fff2f0;
color: #ff4d4f;
border-color: #ffccc7;
}
.priority-medium {
background-color: #fffbe6;
color: #fa8c16;
border-color: #ffe58f;
}
.priority-low {
background-color: #e6f7ff;
color: #1890ff;
border-color: #91d5ff;
}
.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;
}
.task-result {
display: flex;
margin: 10px 0;
font-size: 13px;
padding-top: 8px;
border-top: 1px dashed #f0f2f5;
}
.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;
}
.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;
}
.evaluate-btn {
background-color: #00b42a;
border-color: #00b42a;
}
.evaluate-btn:hover {
background-color: #008718;
border-color: #008718;
}
/* 分页区域样式 */
.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;
}
.avatar-uploader .el-upload-list {
margin-top: 12px;
}
.avatar-uploader .el-upload-list__item {
border-radius: 6px;
margin-bottom: 8px;
transition: all 0.2s;
}
.avatar-uploader .el-upload-list__item:hover {
transform: translateX(4px);
}
.upload-tip {
margin-top: 10px;
font-size: 12px;
}
/* 响应式设计 */
@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>