You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1131 lines
30 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<div class="table-page-container">
<div ref="lxHeader">
<lx-header icon="md-apps" text="维护记录" style="margin-bottom: 10px; border: 0px;">
<slot>
<div style="display: flex;justify-content: flex-start;flex-wrap: wrap;">
<div class="search-item">
<span>仓库:</span>
<Select v-model="select.warehouse" style="width: 120px">
<Option value="">全部</Option>
<Option value="wt">外塘河仓库</Option>
<Option value="xk">胥口仓库</Option>
</Select>
</div>
<div class="search-item">
<span>状态:</span>
<Select v-model="select.status" style="width: 120px">
<Option value="">全部</Option>
<Option value="pending">待处理</Option>
<Option value="completed">已完成</Option>
</Select>
</div>
<div class="search-item">
<span>计划日期:</span>
<DatePicker v-model="select.date_start" type="date" placeholder="开始日期" style="width: 120px"></DatePicker>
<span>-</span>
<DatePicker v-model="select.date_end" type="date" placeholder="结束日期" style="width: 120px"></DatePicker>
</div>
<div class="search-item">
<span>关键词:</span>
<Input v-model="select.keyword" placeholder="维护项目/编号/负责人" style="width: 200px"></Input>
</div>
<Button type="primary" style="margin-left: 8px;" @click="getList">查询</Button>
<Button type="primary" style="margin-left: 8px;" @click="resetSearch">重置</Button>
</div>
</slot>
</lx-header>
</div>
<div class="content-wrapper">
<!-- 统计卡片 -->
<div class="stats-cards">
<div class="card-item total">
<div class="card-label">全部记录</div>
<div class="card-value">{{ stats.total }}</div>
</div>
<div class="card-item pending">
<div class="card-label">未完成</div>
<div class="card-value">{{ stats.pending }}</div>
</div>
<div class="card-item completed">
<div class="card-label">已完成</div>
<div class="card-value">{{ stats.completed }}</div>
</div>
</div>
<div class="table-flex-content">
<el-table
:data="list"
style="width: 100%"
:height="tableHeight"
border
size="medium"
>
<el-table-column
prop="no"
label="记录编号"
min-width="120"
align="center"
/>
<el-table-column
prop="stocks_item.zichanmingcheng"
label="维护物资"
min-width="120"
align="center"
/>
<el-table-column
prop="planned_maintenance_date"
label="计划维护日期"
min-width="120"
align="center"
/>
<el-table-column
label="实际维护日期/状态"
min-width="150"
align="center"
>
<template slot-scope="scope">
<template v-if="scope.row.status === 1">
{{ scope.row.maintenance_date }}
</template>
<template v-else>
<div :class="['status-badge', getBadgeClass(scope.row.planned_maintenance_date)]" style="display: inline-block;">
{{ getBadgeText(scope.row.planned_maintenance_date) }}
</div>
</template>
</template>
</el-table-column>
<el-table-column
prop="responsible_admin.name"
label="负责人"
min-width="100"
align="center"
/>
<el-table-column
label="状态"
min-width="100"
align="center"
>
<template slot-scope="scope">
<el-tag type="success" v-if="scope.row.status===1">{{ getStatusText(scope.row.status) }}</el-tag>
<el-tag v-else>{{ getStatusText(scope.row.status) }}</el-tag>
</template>
</el-table-column>
<el-table-column
label="操作"
align="center"
min-width="200"
header-align="center"
class-name="table-col-action"
>
<template slot-scope="scope">
<div style="display: flex; gap: 8px; justify-content: center;">
<el-button v-if="scope.row.status === 1" type="success" size="small" @click="viewDetail(scope.row)">查看</el-button>
<el-button v-if="scope.row.status === 0" type="primary" size="small" ghost @click="completeMaintenance(scope.row)">完成维护</el-button>
</div>
</template>
</el-table-column>
</el-table>
<div class="pagination-container">
<el-pagination
@size-change="pageSizeChange"
@current-change="pageChange"
:current-page="select.page"
:page-sizes="[10, 20, 50, 100]"
:page-size="select.page_size"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
/>
</div>
</div>
</div>
<!-- 完成维护 Modal -->
<div class="modal" :class="{ show: showModal }">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h3 class="modal-title">完成维护</h3>
<button class="close-button" @click="closeModal">&times;</button>
</div>
<div class="modal-body">
<Form ref="maintenanceForm" :model="form" :rules="formRules" :label-width="120">
<FormItem label="实际维护日期" prop="actual_date">
<DatePicker v-model="form.actual_date" type="date" placeholder="请选择实际维护日期" style="width: 100%"></DatePicker>
</FormItem>
<FormItem label="维护备注" prop="notes">
<Input v-model="form.notes" type="textarea" :rows="4" placeholder="请输入维护备注"></Input>
</FormItem>
<FormItem label="上传图片" prop="photos">
<Upload
ref="upload"
:before-upload="handleBeforeUpload"
:on-success="handleUploadSuccess"
:on-error="handleUploadError"
:on-remove="handleRemove"
:max-size="2048"
multiple
type="drag"
:action="baseUrl + 'api/admin/upload-file'"
>
<div style="padding: 20px 0">
<Icon type="ios-cloud-upload" size="52" style="color: #3399ff"></Icon>
<p>点击或拖拽文件到此处上传</p>
</div>
</Upload>
</FormItem>
<FormItem label="签名" prop="signature">
<div class="signature-pad-wrapper">
<canvas ref="signaturePad" class="signature-canvas"></canvas>
<Button size="small" style="margin-top: 8px;" @click="clearSignature">清除签名</Button>
</div>
</FormItem>
</Form>
</div>
<div class="modal-footer">
<Button @click="closeModal">取消</Button>
<Button type="primary" @click="submitMaintenance">确认完成</Button>
</div>
</div>
</div>
</div>
<!-- 查看详情 Modal -->
<div class="modal" :class="{ show: showViewModal }">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h3 class="modal-title">维护记录详情</h3>
<button class="close-button" @click="closeViewModal">&times;</button>
</div>
<div class="modal-body">
<div class="form-group">
<label>记录编号:</label>
<div class="form-value">{{ currentRecord.no }}</div>
</div>
<div class="form-group">
<label>维护物资:</label>
<div class="form-value">{{ currentRecord.stocks_item_name }}</div>
</div>
<div class="form-group">
<label>计划维护日期:</label>
<div class="form-value">{{ currentRecord.planned_maintenance_date }}</div>
</div>
<div class="form-group">
<label>实际维护日期:</label>
<div class="form-value">{{ currentRecord.maintenance_date || '-' }}</div>
</div>
<div class="form-group">
<label>负责人:</label>
<div class="form-value">{{ currentRecord.responsible_admin_name }}</div>
</div>
<div class="form-group">
<label>状态:</label>
<div class="form-value">{{ getStatusText(currentRecord.status) }}</div>
</div>
<div class="form-group">
<label>维护备注:</label>
<div class="form-value">{{ currentRecord.maintenance_notes || '-' }}</div>
</div>
<div class="form-group">
<label>维护照片:</label>
<div class="photo-gallery" v-if="currentRecord.files && currentRecord.files.length">
<img
v-for="(file, idx) in currentRecord.files"
:key="'file-' + idx"
:src="file.url"
class="photo-preview"
@click="previewImage(file.url)"
style="cursor:pointer;"
alt="维护图片"
/>
</div>
<div class="form-value" v-else>-</div>
</div>
<div class="form-group">
<label>签名照片:</label>
<div class="photo-gallery" v-if="currentRecord.sign && currentRecord.sign.url">
<img
:src="currentRecord.sign.url"
class="sign-preview"
@click="previewImage(currentRecord.sign.url)"
style="cursor:pointer;"
alt="签名图片"
/>
</div>
<div class="form-value" v-else>-</div>
</div>
</div>
<div class="modal-footer" style="justify-content: center;">
<Button @click="closeViewModal">关闭</Button>
</div>
</div>
</div>
</div>
<!-- 预览弹窗 -->
<div v-if="previewUrl" class="image-preview-modal" @click="closePreview">
<img :src="previewUrl" class="image-preview-large" />
</div>
</div>
</template>
<script>
import { Button, Select, Option, DatePicker, Input, Form, FormItem, Upload, Icon } from 'view-design';
import { getOperationList, saveOperation, getOperationDetail } from '@/api/maintenance/maintenance';
export default {
components: {
Button,
Select,
Option,
DatePicker,
Input,
Form,
FormItem,
Upload,
Icon
},
data() {
return {
baseUrl: process.env.VUE_APP_BASE_API || window.location.origin + '/',
select: {
page: 1,
page_size: 10,
warehouse: '',
status: '',
date_start: '',
date_end: '',
keyword: ''
},
stats: {
total: 0,
pending: 0,
completed: 0
},
list: [],
showModal: false,
showViewModal: false,
currentRecord: {},
form: {
actual_date: '',
notes: '',
photos: []
},
formRules: {
actual_date: [
{
required: true,
message: '请选择实际维护日期',
trigger: 'blur',
validator: (rule, value, callback) => {
if (!value) {
callback(new Error('请选择实际维护日期'));
} else {
callback();
}
}
}
],
notes: [],
photos: [
{
required: true,
type: 'array',
min: 1,
message: '请上传维护图片',
trigger: 'change',
validator: (rule, value, callback) => {
if (!value || value.length === 0) {
callback(new Error('请上传维护图片'));
} else {
callback();
}
}
}
],
signature: [
{
required: true,
message: '请签名',
trigger: 'change',
validator: (rule, value, callback) => {
if (!this.getSignatureDataUrl()) {
callback(new Error('请签名'));
} else {
callback();
}
}
}
]
},
currentUser: '张三',
total: 0,
tableHeight: 550,
previewUrl: ''
}
},
created() {
this.getList()
},
mounted() {
this.calcTableHeight();
window.addEventListener('resize', this.calcTableHeight);
this.initSignaturePad();
},
beforeDestroy() {
window.removeEventListener('resize', this.calcTableHeight);
},
methods: {
async getList() {
try {
const params = {
page: this.select.page,
page_size: this.select.page_size,
warehouse: this.select.warehouse,
status: this.select.status,
date_start: this.select.date_start,
date_end: this.select.date_end,
keyword: this.select.keyword
};
const res = await getOperationList(params);
if (res && res.list) {
this.list = res.list.data;
this.total = res.list.total;
this.stats = {
total: res.chart.total,
pending: res.chart.wait,
completed: res.chart.done
};
}
} catch (e) {
this.$message.error('获取维护记录失败');
}
},
resetSearch() {
this.select = {
page: 1,
page_size: 10,
warehouse: '',
status: '',
date_start: '',
date_end: '',
keyword: ''
}
this.getList()
},
pageChange(e) {
this.select.page = e
this.getList()
},
pageSizeChange(e) {
this.select.page_size = e
this.getList()
},
calcTableHeight() {
const header = this.$refs.lxHeader ? this.$refs.lxHeader.offsetHeight : 0;
const windowHeight = window.innerHeight;
const minTableHeight = 600;
this.tableHeight = Math.max(windowHeight - header - 100, minTableHeight);
},
getDateDifferenceInDays(dateString) {
const plannedDate = new Date(dateString);
const today = new Date();
// 设置时间为当天的 00:00:00
today.setHours(0, 0, 0, 0);
plannedDate.setHours(0, 0, 0, 0);
// 计算日期差值(毫秒)
const diffTime = plannedDate - today;
// 转换为天数
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
return diffDays;
},
async viewDetail(item) {
try {
// 1. 调用接口获取详情
const detail = await getOperationDetail({ id: item.id });
// 2. 预处理二级字段为一级
this.currentRecord = {
...detail,
stocks_item_name: detail.stocks_item?.zichanmingcheng || '-',
responsible_admin_name: detail.responsible_admin?.name || '-'
};
// 3. 打开查看窗口
this.showViewModal = true;
} catch (e) {
this.$message.error('获取维护详情失败');
}
},
completeMaintenance(item) {
this.showModal = true;
this.currentRecord = { ...item };
this.form = {
actual_date: new Date().toISOString().split('T')[0],
notes: '',
photos: []
};
this.$nextTick(() => {
if (this.$refs.maintenanceForm) {
this.$refs.maintenanceForm.resetFields();
}
});
},
closeModal() {
this.showModal = false
this.form = {
actual_date: '',
notes: '',
photos: []
}
},
handleBeforeUpload(file) {
// 处理上传前的逻辑
return true; // 返回true表示允许上传
},
handleUploadSuccess(response, file) {
// 假设 response.id 是图片idresponse.url 是图片地址
this.form.photos.push({ id: response.id, url: response.url });
},
handleUploadError(error, file) {
// 处理上传错误后的逻辑
this.$message.error('上传图片失败');
},
handleRemove(file) {
// file.url 是图片地址,找到并移除对应的图片对象
const index = this.form.photos.findIndex(photo => photo.url === file.url);
if (index !== -1) {
this.form.photos.splice(index, 1);
}
},
async submitMaintenance() {
try {
this.$refs.maintenanceForm.validate(async (valid) => {
if (valid) {
// 1. 先将签名canvas转为图片并上传拿到sign_id
const signatureDataUrl = this.getSignatureDataUrl();
let sign_id = '';
if (signatureDataUrl) {
// 将base64转为Blob
const blob = this.dataURLtoBlob(signatureDataUrl);
const formData = new FormData();
formData.append('file', blob, 'signature.png');
// 调用上传接口
const res = await this.$axios.post(this.baseUrl + 'api/admin/upload-file', formData, {
headers: { 'Content-Type': 'multipart/form-data' }
});
sign_id = res.data?.id || res.id;
}
// 2. 组装参数并提交
const params = {
id: this.currentRecord.id,
maintenance_date: this.formatDate(this.form.actual_date),
maintenance_content: this.form.notes,
file_ids: this.form.photos.map(photo => photo.id),
sign_id: sign_id,
status: 1
};
await saveOperation(params);
this.closeModal();
this.getList();
}
});
} catch (e) {
this.$message.error('提交维护记录失败');
}
},
formatDate(date) {
if (!date) return '';
const d = new Date(date);
const year = d.getFullYear();
const month = String(d.getMonth() + 1).padStart(2, '0');
const day = String(d.getDate()).padStart(2, '0');
return `${year}-${month}-${day}`;
},
dataURLtoBlob(dataurl) {
// 将base64图片转为Blob对象
const arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1], bstr = atob(arr[1]);
let n = bstr.length, u8arr = new Uint8Array(n);
while(n--){
u8arr[n] = bstr.charCodeAt(n);
}
return new Blob([u8arr], {type:mime});
},
getBadgeClass(planDate) {
const diffDays = this.getDateDifferenceInDays(planDate);
return diffDays >= 0 ? 'badge-blue' : 'badge-red';
},
getBadgeText(planDate) {
const diffDays = this.getDateDifferenceInDays(planDate);
return diffDays >= 0 ? `还有 ${diffDays}` : `已超期 ${Math.abs(diffDays)}`;
},
getStatusText(status) {
const statusMap = {
'0': '待处理',
'1': '已完成'
};
return statusMap[status] || '未知状态';
},
closeViewModal() {
this.showViewModal = false;
this.currentRecord = {};
},
initSignaturePad() {
const canvas = this.$refs.signaturePad;
if (!canvas) return;
const ctx = canvas.getContext('2d');
// 适配高分屏
const dpr = window.devicePixelRatio || 1;
const container = canvas.parentNode;
const displayWidth = container.offsetWidth || 400;
canvas.width = displayWidth * dpr;
canvas.height = 120 * dpr;
canvas.style.width = displayWidth + 'px';
canvas.style.height = '120px';
ctx.scale(dpr, dpr);
ctx.lineWidth = 2;
ctx.lineCap = 'round';
let drawing = false;
let lastX = 0, lastY = 0;
const getPos = e => {
if (e.touches && e.touches.length) {
const rect = canvas.getBoundingClientRect();
return {
x: e.touches[0].clientX - rect.left,
y: e.touches[0].clientY - rect.top
};
} else {
const rect = canvas.getBoundingClientRect();
return {
x: e.clientX - rect.left,
y: e.clientY - rect.top
};
}
};
const start = e => {
drawing = true;
const pos = getPos(e);
lastX = pos.x;
lastY = pos.y;
};
const move = e => {
if (!drawing) return;
const pos = getPos(e);
ctx.beginPath();
ctx.moveTo(lastX, lastY);
ctx.lineTo(pos.x, pos.y);
ctx.stroke();
lastX = pos.x;
lastY = pos.y;
};
const end = () => { drawing = false; };
canvas.addEventListener('mousedown', start);
canvas.addEventListener('mousemove', move);
canvas.addEventListener('mouseup', end);
canvas.addEventListener('mouseleave', end);
canvas.addEventListener('touchstart', start);
canvas.addEventListener('touchmove', move);
canvas.addEventListener('touchend', end);
this.signaturePadCtx = ctx;
this.signaturePadCanvas = canvas;
},
clearSignature() {
if (this.signaturePadCtx && this.signaturePadCanvas) {
this.signaturePadCtx.clearRect(0, 0, this.signaturePadCanvas.width, this.signaturePadCanvas.height);
}
},
getSignatureDataUrl() {
if (this.signaturePadCanvas) {
return this.signaturePadCanvas.toDataURL('image/png');
}
return '';
},
previewImage(url) {
this.previewUrl = url;
},
closePreview() {
this.previewUrl = '';
}
},
watch: {
showModal(val) {
if (val) {
this.$nextTick(() => {
this.initSignaturePad();
});
}
}
}
}
</script>
<style scoped>
.table-page-container {
padding: 20px;
background-color: #f0f2f5;
min-height: 100vh;
}
.page-header {
margin-bottom: 20px;
padding-bottom: 15px;
border-bottom: 1px solid #e8e8e8;
}
.page-title {
font-size: 15px;
font-weight: 600;
color: #333;
}
.content-wrapper {
background: #f4f6fa !important;
border-radius: 10px;
border: 1.5px solid #e0e3e8 !important;
box-shadow: 0 2px 12px rgba(0,0,0,0.08);
padding: 20px;
margin-top: 10px;
}
.search-item {
display: flex;
align-items: center;
margin-right: 16px;
}
.search-item span {
margin-right: 8px;
white-space: nowrap;
}
.search-item .ivu-select,
.search-item .ivu-date-picker,
.search-item .ivu-input {
margin-right: 8px;
}
.stats-cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
gap: 20px;
margin-bottom: 20px;
}
.card-item {
background-color: #fff;
padding: 15px 20px;
border-radius: 6px;
box-shadow: 0 1px 4px rgba(0,0,0,0.08);
text-align: center;
border-left: 4px solid grey;
}
.card-item.total { border-left-color: #1890ff; }
.card-item.pending { border-left-color: #faad14; }
.card-item.completed { border-left-color: #52c41a; }
.card-label {
font-size: 18px;
color: #888;
margin-bottom: 8px;
}
.card-value {
font-size: 18px;
font-weight: 600;
color: #333;
}
.table-flex-content {
flex: 1 1 0;
display: flex;
flex-direction: column;
min-height: 0;
height: calc(100vh - 280px);
}
.el-table {
border-radius: 10px;
overflow: hidden;
width: 100%;
height: 100%;
margin-bottom: 10px;
}
.el-table__body-wrapper {
height: calc(100% - 40px);
overflow-y: auto;
}
.el-table__header-wrapper {
height: 40px;
}
.el-table td {
font-size: 14px;
padding: 12px 0;
}
.el-table th {
background: #f5f7fa !important;
color: #333;
font-weight: bold;
font-size: 15px;
padding: 12px 0;
}
.status-badge {
display: inline-block;
padding: 4px 8px;
border-radius: 4px;
font-size: 12px;
color: #fff;
}
.badge-blue {
background-color: #2d8cf0;
}
.badge-red {
background-color: #ed4014;
}
.modal {
display: none;
position: fixed;
z-index: 1050;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto;
background-color: rgba(0,0,0,0.5);
align-items: center;
justify-content: center;
}
.modal.show {
display: flex;
}
.modal-dialog {
position: relative;
margin: auto;
pointer-events: none;
max-width: 900px;
width: 90%;
}
.modal-content {
position: relative;
display: flex;
flex-direction: column;
width: 100%;
pointer-events: auto;
background: #f7f9fa !important;
background-clip: padding-box;
border-radius: 12px;
box-shadow: 0 8px 32px rgba(0,0,0,0.15);
border: 2px solid #e0e3e8 !important;
outline: 0;
}
.modal-header {
display: flex;
align-items: flex-start;
justify-content: space-between;
padding: 12px 15px;
background: #f5f7fa;
border-bottom: 1px solid #eaeaea;
border-radius: 12px 12px 0 0;
}
.modal-title {
margin-bottom: 0;
line-height: 1.5;
font-size: 16px;
font-weight: 700;
color: #222;
}
.close-button {
padding: 1rem 1rem;
margin: -1rem -1rem -1rem auto;
background-color: transparent;
border: 0;
font-size: 20px;
font-weight: 700;
line-height: 1;
color: #888;
text-shadow: 0 1px 0 #fff;
opacity: 0.7;
cursor: pointer;
transition: color 0.2s, opacity 0.2s;
}
.close-button:hover {
color: #ed4014;
opacity: 1;
}
.modal-body {
background: #fcfcfd;
padding: 20px 15px 15px 15px;
border-radius: 0 0 12px 12px;
display: grid;
grid-template-columns: 1fr 1fr;
gap: 16px 32px;
}
.form-group {
background: #fff !important;
border: 1.5px solid #e0e3e8 !important;
box-shadow: 0 1px 2px rgba(0,0,0,0.03);
border-radius: 6px;
padding: 12px 16px;
margin-bottom: 0;
display: flex;
flex-direction: column;
min-width: 0;
}
.form-group label {
color: #888;
font-size: 12px;
margin-bottom: 4px;
font-weight: 500;
}
.form-group input[type="date"],
.form-group textarea,
.form-group input[type="file"] {
display: block;
width: 100%;
padding: 5px 7.5px;
font-size: 16px;
font-weight: 400;
line-height: 1.5;
color: #495057;
background-color: #fff !important;
background-clip: padding-box;
border: 1px solid #ced4da;
border-radius: 0.25rem;
transition: border-color .15s ease-in-out,box-shadow .15s ease-in-out;
}
.form-group textarea {
resize: vertical;
}
.form-group input[type="file"] {
padding: 3px 7.5px;
}
.form-text {
font-size: 16px;
color: #6c757d;
}
.signature-area {
width: 100%;
min-width: 0;
}
.signature-pad-wrapper {
width: 100%;
min-width: 0;
display: flex;
flex-direction: column;
align-items: flex-start;
}
.signature-canvas {
border: 1px dashed #ced4da;
background: #fff;
border-radius: 4px;
width: 100%;
height: 120px;
cursor: crosshair;
display: block;
box-sizing: border-box;
margin: 0;
padding: 0;
min-width: 0;
}
.form-value {
color: #222;
font-size: 12px;
font-weight: 500;
background: transparent;
border: none;
min-height: 32px;
padding: 0;
}
.photo-gallery {
display: flex;
flex-wrap: wrap;
gap: 10px;
background: #f8f9fa;
border-radius: 6px;
padding: 8px;
margin-top: 4px;
}
.photo-preview {
width: 64px;
height: 64px;
object-fit: cover;
border-radius: 4px;
border: 1.5px solid #eaeaea;
box-shadow: 0 1px 4px rgba(0,0,0,0.06);
cursor: pointer;
transition: transform 0.15s;
}
.photo-preview:hover {
transform: scale(1.08);
box-shadow: 0 4px 16px rgba(0,0,0,0.13);
}
.pagination-container {
display: flex;
justify-content: flex-end;
margin-top: 15px;
padding-top: 15px;
border-top: 1px solid #f0f0f0;
height: 50px;
}
.el-pagination {
padding: 0;
font-weight: normal;
height: 32px;
line-height: 32px;
.el-pagination__sizes {
margin-right: 16px;
}
.el-pagination__jump {
margin-left: 16px;
}
.el-pagination__total {
margin-right: 16px;
}
.btn-prev,
.btn-next {
background: #fff;
border: 1px solid #dcdfe6;
height: 32px;
line-height: 32px;
&:hover {
color: #409eff;
}
}
.el-pager li {
background: #fff;
border: 1px solid #dcdfe6;
height: 32px;
line-height: 32px;
&:hover {
color: #409eff;
}
&.active {
background-color: #409eff;
color: #fff;
border-color: #409eff;
}
}
}
@media (max-width: 900px) {
.modal-dialog {
max-width: 98vw;
}
.modal-body {
padding: 10px 5px;
grid-template-columns: 1fr;
}
}
.modal-footer {
display: flex;
justify-content: center;
align-items: center;
flex-wrap: wrap;
padding: 7.5px;
border-top: 1px solid #dee2e6;
border-bottom-right-radius: 12px;
border-bottom-left-radius: 12px;
gap: 0.5rem;
}
.image-preview-modal {
position: fixed;
z-index: 2000;
left: 0; top: 0; right: 0; bottom: 0;
background: rgba(0,0,0,0.7);
display: flex;
align-items: center;
justify-content: center;
}
.image-preview-large {
max-width: 90vw;
max-height: 90vh;
border-radius: 8px;
box-shadow: 0 4px 24px rgba(0,0,0,0.3);
background: #fff;
}
.sign-preview {
width: 100%;
max-width: 100%;
height: auto;
object-fit: contain;
border-radius: 4px;
border: 1.5px solid #eaeaea;
box-shadow: 0 1px 4px rgba(0,0,0,0.06);
margin-top: 4px;
cursor: pointer;
background: #fff;
}
/* 分割线 */
hr {
border: none;
border-top: 1.5px solid #e0e3e8;
margin: 16px 0;
}
.ivu-input, .ivu-select, .ivu-date-picker, .ivu-btn, .el-table {
border-color: #bfc8d6 !important;
background: #fff !important;
}
.ivu-btn-primary, .el-button--primary {
background: #2d8cf0 !important;
border-color: #2d8cf0 !important;
color: #fff !important;
}
/* 主色条 */
.modal-header {
display: flex;
align-items: flex-start;
justify-content: space-between;
padding: 1.2rem 1.5rem;
background: #f5f7fa;
border-bottom: 1px solid #eaeaea;
border-radius: 12px 12px 0 0;
}
/* 分割线 */
hr {
border: none;
border-top: 1.5px solid #e0e3e8;
margin: 16px 0;
}
</style>