Files
tms-erp-web/src/views/transit/exception-disposal.vue
T

1029 lines
33 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>
<basic-container class="exception-disposal-page">
<section class="exception-disposal-page__search">
<el-form :model="query" label-position="right" label-width="88px" @submit.prevent>
<div class="exception-disposal-page__search-grid">
<el-form-item v-for="field in visibleSearchFields" :key="field.prop" :label="field.label">
<el-date-picker
v-if="field.type === 'daterange'"
v-model="query[field.prop]"
type="daterange"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
start-placeholder="请选择"
end-placeholder="请选择"
range-separator="-"
/>
<el-cascader
v-else-if="field.prop === 'exceptionType'"
v-model="query[field.prop]"
:options="field.options"
clearable
filterable
placeholder="全部"
:props="{ emitPath: false }"
/>
<el-select
v-else-if="field.type === 'select'"
v-model="query[field.prop]"
clearable
placeholder="全部"
>
<el-option
v-for="item in field.options"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
<el-input v-else v-model="query[field.prop]" clearable placeholder="请输入" />
</el-form-item>
<div class="exception-disposal-page__search-actions">
<el-button type="primary" @click="handleSearch">查询</el-button>
<el-button @click="handleReset">重置</el-button>
<el-button text :icon="searchExpanded ? ArrowUp : ArrowDown" @click="toggleSearch">
{{ searchExpanded ? '收起' : '展开' }}
</el-button>
</div>
</div>
</el-form>
</section>
<section class="exception-disposal-page__table-panel">
<div class="exception-disposal-page__toolbar">
<div>
<el-button
v-if="permission.exception_disposal_batch_complete"
type="primary"
plain
@click="handleBatchComplete"
>
批量完成
</el-button>
</div>
<div class="exception-disposal-page__toolbar-right">
<el-tooltip content="刷新" placement="top">
<el-button :icon="Refresh" text @click="loadTable" />
</el-tooltip>
<el-tooltip content="表格密度" placement="top">
<el-button :icon="Rank" text @click="noop" />
</el-tooltip>
<el-tooltip content="列设置" placement="top">
<el-button :icon="Setting" text @click="noop" />
</el-tooltip>
</div>
</div>
<el-table
v-loading="loading"
:data="rows"
border
class="exception-disposal-page__table"
@selection-change="handleSelectionChange"
>
<el-table-column type="selection" width="52" fixed="left" align="center" />
<el-table-column type="index" label="序号" width="72" fixed="left" align="center" />
<el-table-column
v-for="column in tableColumns"
:key="column.prop"
:prop="column.prop"
:label="column.label"
:min-width="column.minWidth"
align="center"
show-overflow-tooltip
>
<template #default="{ row }">
<el-link
v-if="column.link && row[column.prop]"
type="primary"
@click="openWaybill(row)"
>
{{ row[column.prop] }}
</el-link>
<el-link
v-else-if="column.prop === 'disposalStatusName'"
:type="statusLinkType(row.disposalStatus)"
@click="openDisposal(row, row.disposalStatus === 'completed' ? 'detail' : 'follow')"
>
{{ row[column.prop] || '-' }}
</el-link>
<span v-else>{{ row[column.prop] || '-' }}</span>
</template>
</el-table-column>
<el-table-column label="操作" width="220" fixed="right" align="center">
<template #default="{ row }">
<div class="exception-disposal-page__actions">
<template v-if="row.disposalStatus !== 'completed'">
<el-link
v-if="permission.exception_disposal_follow"
type="primary"
@click="openDisposal(row, 'follow')"
>
跟进
</el-link>
<el-link
v-if="permission.exception_disposal_edit"
type="primary"
@click="openDisposal(row, 'edit')"
>
编辑
</el-link>
<el-link
v-if="permission.exception_disposal_complete"
type="primary"
@click="completeRow(row)"
>
完成
</el-link>
</template>
<template v-else>
<el-link
v-if="permission.exception_disposal_view"
type="primary"
@click="openDisposal(row, 'detail')"
>
详情
</el-link>
<el-link
v-if="permission.exception_disposal_edit"
type="primary"
@click="openDisposal(row, 'edit')"
>
编辑
</el-link>
</template>
</div>
</template>
</el-table-column>
</el-table>
<div class="exception-disposal-page__pagination">
<el-pagination
v-model:current-page="page.current"
v-model:page-size="page.size"
:total="page.total"
:page-sizes="[10, 20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper"
@current-change="loadTable"
@size-change="handleSizeChange"
/>
</div>
</section>
<el-dialog
v-model="dialog.visible"
:title="dialogTitle"
:width="dialog.mode === 'edit' ? '1080px' : '960px'"
top="6vh"
append-to-body
destroy-on-close
>
<div v-loading="dialog.loading" class="exception-disposal-page__dialog">
<!-- 编辑态:只呈现运单信息与可编辑项,不再重复详情页的只读信息 -->
<el-form
v-if="dialog.mode === 'edit'"
ref="editFormRef"
:model="editForm"
:rules="editRules"
label-position="right"
label-width="96px"
>
<section-card title="运单信息">
<el-descriptions :column="3" border size="small">
<el-descriptions-item
v-for="field in editWaybillFields"
:key="field.prop"
:label="field.label"
>
{{ detailValue(field.prop) }}
</el-descriptions-item>
</el-descriptions>
</section-card>
<section-card title="异常信息">
<el-row :gutter="18">
<el-col :span="12">
<el-form-item label="上报人">
<span class="exception-disposal-page__readonly">{{
detailValue('reporterName')
}}</span>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="上报时间">
<span class="exception-disposal-page__readonly">{{
detailValue('reportTime')
}}</span>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="异常节点">
<span class="exception-disposal-page__readonly">{{
detailValue('nodeName')
}}</span>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="异常类型" prop="exceptionType">
<el-select
v-model="editForm.exceptionType"
placeholder="请选择"
@change="handleEditTypeChange"
>
<el-option
v-for="item in exceptionTypeOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="异常原因" prop="exceptionReason">
<el-select
v-model="editForm.exceptionReason"
clearable
placeholder="请选择"
:disabled="!editForm.exceptionType"
>
<el-option
v-for="item in editReasonOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="上报说明" prop="reportDescription">
<el-input
v-model="editForm.reportDescription"
type="textarea"
maxlength="500"
show-word-limit
:rows="3"
placeholder="请输入"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="现场照片">
<div class="exception-disposal-page__edit-photos">
<div
v-for="(url, index) in editForm.scenePhotoList"
:key="url"
class="exception-disposal-page__edit-photo"
>
<el-image
:src="url"
:preview-src-list="editForm.scenePhotoList"
:initial-index="index"
fit="cover"
/>
<el-link type="danger" :underline="false" @click="removeEditPhoto(index)"
>删除</el-link
>
</div>
<el-upload
action="/api/blade-resource/oss/endpoint/put-file"
name="file"
:headers="uploadHeaders"
:show-file-list="false"
:before-upload="beforePhotoUpload"
:on-success="handlePhotoSuccess"
:on-error="handlePhotoError"
>
<div class="exception-disposal-page__edit-photo-add">+ 添加</div>
</el-upload>
</div>
</el-form-item>
</el-col>
</el-row>
</section-card>
</el-form>
<template v-else>
<section-card title="运单信息">
<el-descriptions :column="3" border size="small">
<el-descriptions-item
v-for="field in detailWaybillFields"
:key="field.prop"
:label="field.label"
>
{{ detailValue(field.prop) }}
</el-descriptions-item>
</el-descriptions>
</section-card>
<section-card title="异常信息">
<el-descriptions :column="2" border size="small">
<el-descriptions-item
v-for="field in detailExceptionFields"
:key="field.prop"
:label="field.label"
:span="field.span === 2 ? 2 : 1"
>
{{ detailValue(field.prop) }}
</el-descriptions-item>
</el-descriptions>
</section-card>
<section-card title="现场照片">
<div class="exception-disposal-page__photos">
<el-image
v-for="(url, index) in photoUrls"
:key="url"
:src="url"
:preview-src-list="photoUrls"
:initial-index="index"
fit="cover"
/>
<el-empty v-if="!photoUrls.length" description="暂无现场照片" :image-size="56" />
</div>
</section-card>
<section-card v-if="dialog.mode === 'follow'" title="跟进">
<el-form
ref="followFormRef"
:model="followForm"
:rules="followRules"
label-position="right"
label-width="96px"
>
<el-row :gutter="18">
<el-col :span="24">
<el-form-item label="跟进说明" prop="followContent">
<el-input
v-model="followForm.followContent"
type="textarea"
maxlength="500"
show-word-limit
:rows="3"
placeholder="请输入"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="跟进附件">
<vehicle-attachment-upload
v-model="followForm.attachments"
:limit="20"
:max-size="500"
button-text="上传附件"
:show-tip="false"
/>
</el-form-item>
</el-col>
</el-row>
</el-form>
</section-card>
<section-card title="历史跟进">
<el-table :data="detail.followRecords || []" border>
<el-table-column type="index" label="序号" width="70" align="center" />
<el-table-column
v-for="column in followColumns"
:key="column.prop"
:prop="column.prop"
:label="column.label"
:min-width="column.minWidth"
align="center"
show-overflow-tooltip
/>
<el-table-column label="附件" min-width="120" align="center">
<template #default="{ row }">
<template v-if="parseAttachments(row.attachmentsJson).length">
<span
v-for="(file, index) in parseAttachments(row.attachmentsJson)"
:key="index"
class="attachment-file-name exception-disposal-page__attachment-link"
role="button"
tabindex="0"
@click="previewAttachment(file, parseAttachments(row.attachmentsJson))"
@keydown.enter="previewAttachment(file, parseAttachments(row.attachmentsJson))"
@keydown.space.prevent="
previewAttachment(file, parseAttachments(row.attachmentsJson))
"
>{{ file.name || '附件' + (index + 1) }}</span>
</template>
<span v-else>-</span>
</template>
</el-table-column>
</el-table>
</section-card>
</template>
</div>
<template #footer>
<el-button
v-if="dialog.mode === 'edit' && permission.exception_disposal_edit"
type="primary"
:loading="dialog.submitting"
@click="submitEdit"
>
提交
</el-button>
<el-button
v-if="dialog.mode === 'follow' && permission.exception_disposal_follow"
type="primary"
:loading="dialog.submitting"
@click="submitFollow"
>
提交
</el-button>
<el-button
v-if="dialog.mode === 'follow' && permission.exception_disposal_complete"
type="primary"
plain
@click="completeFromDialog"
>
完成
</el-button>
<el-button @click="dialog.visible = false">取消</el-button>
</template>
</el-dialog>
<el-dialog
v-model="attachmentDocumentPreviewVisible"
:title="attachmentPreviewFile.name || '附件预览'"
append-to-body
destroy-on-close
width="90%"
top="4vh"
>
<pdf-preview
v-if="
attachmentDocumentPreviewVisible &&
attachmentPreviewFile.url &&
attachmentPreviewFile.isPdf
"
:source="attachmentPreviewFile.url"
@error="handleAttachmentPreviewError"
/>
<open-file-viewer
v-else-if="attachmentDocumentPreviewVisible && attachmentPreviewFile.url"
:file="attachmentPreviewFile.url"
:file-name="attachmentPreviewFile.name"
:mime-type="attachmentPreviewFile.mimeType"
width="100%"
height="72vh"
fit="contain"
theme="auto"
locale="zh-CN"
:toolbar="attachmentViewerToolbar"
:plugins="attachmentViewerPlugins"
@unsupported="handleAttachmentPreviewUnsupported"
@error="handleAttachmentPreviewError"
/>
</el-dialog>
<el-image-viewer
v-if="attachmentImagePreviewVisible"
:url-list="attachmentImagePreviewUrls"
:initial-index="attachmentImagePreviewIndex"
@close="attachmentImagePreviewVisible = false"
/>
</basic-container>
</template>
<script>
import { ArrowDown, ArrowUp, Rank, Refresh, Setting } from '@element-plus/icons-vue';
import { mapGetters } from 'vuex';
import {
detailExceptionFields,
detailWaybillFields,
disposalStatusOptions,
editWaybillFields,
exceptionCategoryOptions,
exceptionReasonOptions,
exceptionTypeOptions,
followColumns,
searchFields,
tableColumns,
} from '@/option/transit/exception-disposal';
import { getUploadHeaders } from '@/utils/upload';
import * as api from '@/api/transit/exception-disposal';
import PdfPreview from '@/components/pdf-preview/main.vue';
import { ElImageViewer } from 'element-plus';
import { OpenFileViewer } from '@open-file-viewer/vue';
import { fallbackPlugin, imagePlugin, officePlugin, textPlugin } from '@open-file-viewer/core';
import '@open-file-viewer/core/style.css';
import pdfWorkerSrc from 'pdfjs-dist/build/pdf.worker.mjs?url';
const attachmentName = row => row?.originalName || row?.name || row?.fileName || '附件';
const attachmentUrl = row =>
row?.url || row?.link || row?.fileUrl || row?.downloadUrl || row?.domain || '';
const attachmentExtension = row => {
const source = String(attachmentName(row) || attachmentUrl(row)).split('?')[0];
const index = source.lastIndexOf('.');
return index > -1 ? source.slice(index + 1).toLowerCase() : '';
};
const isAttachmentImage = row =>
['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp'].includes(attachmentExtension(row));
const isAttachmentPdf = row => {
const mimeType = String(row.mimeType || row.contentType || '').toLowerCase();
return mimeType.includes('application/pdf') || attachmentExtension(row) === 'pdf';
};
const attachmentViewerPlugins = [
imagePlugin(),
officePlugin({ pdf: { workerSrc: pdfWorkerSrc, useFetchData: true } }),
textPlugin(),
fallbackPlugin(),
];
export default {
components: { ElImageViewer, OpenFileViewer, PdfPreview },
data() {
return {
Rank,
Refresh,
Setting,
ArrowDown,
ArrowUp,
searchFields,
tableColumns,
detailExceptionFields,
detailWaybillFields,
followColumns,
exceptionTypeOptions,
editWaybillFields,
loading: false,
query: {},
rows: [],
selection: [],
searchExpanded: false,
page: { current: 1, size: 10, total: 0 },
dialog: { visible: false, mode: 'follow', loading: false, submitting: false },
detail: {},
followForm: { followContent: '', attachments: [] },
followRules: {
followContent: [{ max: 500, message: '跟进说明不能超过500个字符', trigger: 'blur' }],
},
editForm: {
exceptionType: '',
exceptionReason: '',
reportDescription: '',
scenePhotoList: [],
},
editRules: {
exceptionType: [{ required: true, message: '请选择异常类型', trigger: 'change' }],
reportDescription: [
{ required: true, message: '请输入上报说明', trigger: 'blur' },
{ max: 500, message: '上报说明不能超过500个字符', trigger: 'blur' },
],
},
attachmentImagePreviewVisible: false,
attachmentImagePreviewUrls: [],
attachmentImagePreviewIndex: 0,
attachmentDocumentPreviewVisible: false,
attachmentPreviewFile: {},
attachmentViewerPlugins,
attachmentViewerToolbar: {
download: true,
fullscreen: true,
print: true,
rotate: true,
zoom: true,
},
};
},
computed: {
...mapGetters(['permission']),
dialogTitle() {
return (
{ detail: '异常详情', follow: '异常跟进', edit: '编辑异常' }[this.dialog.mode] || '异常详情'
);
},
editReasonOptions() {
return exceptionReasonOptions(this.editForm.exceptionType);
},
uploadHeaders() {
return getUploadHeaders();
},
visibleSearchFields() {
return this.searchExpanded ? this.searchFields : this.searchFields.slice(0, 4);
},
photoUrls() {
return this.parsePhotos(this.detail.scenePhotos);
},
},
mounted() {
this.loadTable();
},
methods: {
async loadTable() {
this.loading = true;
try {
const res = await api.getList(this.page.current, this.page.size, this.buildQuery());
const data = this.unwrapPage(res);
this.rows = (data.records || []).map(this.decorateRow);
this.page.total = data.total || 0;
} finally {
this.loading = false;
}
},
buildQuery() {
const params = { ...this.query };
if (Array.isArray(params.reportTimeRange)) {
params.reportStartTime = params.reportTimeRange[0];
params.reportEndTime = params.reportTimeRange[1];
}
delete params.reportTimeRange;
return params;
},
handleSearch() {
this.page.current = 1;
this.loadTable();
},
handleReset() {
this.query = {};
this.handleSearch();
},
toggleSearch() {
this.searchExpanded = !this.searchExpanded;
},
handleSizeChange() {
this.page.current = 1;
this.loadTable();
},
handleSelectionChange(selection) {
this.selection = selection;
},
async openDisposal(row, mode) {
this.dialog = { visible: true, mode, loading: true, submitting: false };
this.followForm = { followContent: '', attachments: [] };
try {
const res = await api.getDetail(row.id);
this.detail = this.decorateRow(res.data?.data || res.data || res || {});
if (mode === 'edit') {
this.editForm = {
exceptionType: this.detail.exceptionType || '',
exceptionReason: this.detail.exceptionReason || '',
reportDescription: this.detail.reportDescription || '',
scenePhotoList: this.parsePhotos(this.detail.scenePhotos),
};
}
} finally {
this.dialog.loading = false;
}
},
detailValue(prop) {
const value = this.detail[prop];
return value === null || value === undefined || value === '' ? '-' : value;
},
handleEditTypeChange() {
// 一级类型变了,旧二级可能不属于新类型
const stillValid = this.editReasonOptions.some(
item => item.value === this.editForm.exceptionReason
);
if (!stillValid) {
this.editForm.exceptionReason = '';
}
},
beforePhotoUpload(file) {
const validType = ['image/jpeg', 'image/png', 'image/jpg', 'image/bmp'].includes(file.type);
if (!validType) {
this.$message.error('仅支持 JPG、PNG、BMP 图片');
return false;
}
if (file.size / 1024 / 1024 >= 5) {
this.$message.error('图片大小不能超过 5MB');
return false;
}
return true;
},
handlePhotoSuccess(res) {
if (res.code === 200 && res.data) {
const url = res.data.link || res.data.url || res.data.domain || '';
if (url) {
this.editForm.scenePhotoList = [...this.editForm.scenePhotoList, url];
}
} else {
this.$message.error(res.msg || '上传失败');
}
},
handlePhotoError() {
this.$message.error('上传失败');
},
removeEditPhoto(index) {
const list = [...this.editForm.scenePhotoList];
list.splice(index, 1);
this.editForm.scenePhotoList = list;
},
async submitEdit() {
await this.$refs.editFormRef.validate();
this.dialog.submitting = true;
try {
await api.update({
id: this.detail.id,
exceptionType: this.editForm.exceptionType,
exceptionReason: this.editForm.exceptionReason,
reportDescription: this.editForm.reportDescription,
scenePhotos: this.editForm.scenePhotoList.join(','),
});
this.$message.success('编辑成功');
this.dialog.visible = false;
this.loadTable();
} finally {
this.dialog.submitting = false;
}
},
async submitFollow() {
await this.$refs.followFormRef.validate();
const followContent = (this.followForm.followContent || '').trim();
const attachmentsJson = this.followForm.attachments.length
? JSON.stringify(this.followForm.attachments)
: '';
// 后端要求二者至少有其一
if (!followContent && !attachmentsJson) {
this.$message.warning('请填写跟进说明或上传附件');
return;
}
this.dialog.submitting = true;
try {
await api.follow({ id: this.detail.id, followContent, attachmentsJson });
this.$message.success('跟进成功');
this.dialog.visible = false;
this.loadTable();
} finally {
this.dialog.submitting = false;
}
},
parseAttachments(value) {
if (!value) return [];
if (Array.isArray(value)) return value;
try {
const parsed = JSON.parse(value);
return Array.isArray(parsed) ? parsed.filter(item => item && item.url) : [];
} catch (error) {
return [];
}
},
previewAttachment(row, rows = []) {
const url = attachmentUrl(row);
if (!url) {
this.$message.warning('附件地址为空,无法预览');
return;
}
if (isAttachmentImage(row)) {
this.attachmentImagePreviewUrls = (rows || [])
.filter(item => isAttachmentImage(item) && attachmentUrl(item))
.map(item => attachmentUrl(item));
this.attachmentImagePreviewIndex = Math.max(
this.attachmentImagePreviewUrls.indexOf(url),
0
);
this.attachmentImagePreviewVisible = true;
return;
}
const isPdf = isAttachmentPdf(row);
this.attachmentPreviewFile = {
name: attachmentName(row),
url,
mimeType: isPdf ? 'application/pdf' : row.mimeType || row.contentType || '',
isPdf,
};
this.attachmentDocumentPreviewVisible = true;
},
handleAttachmentPreviewUnsupported() {
this.$message.warning('当前文件暂不支持在线预览');
},
handleAttachmentPreviewError() {
this.$message.error('附件预览失败');
},
async completeRow(row) {
await this.$confirm('确认完成该异常处置?', '提示', { type: 'warning' });
await api.complete({ id: row.id });
this.$message.success('完成成功');
this.loadTable();
},
async completeFromDialog() {
await this.$confirm('确认完成该异常处置?', '提示', { type: 'warning' });
await api.complete({ id: this.detail.id });
this.$message.success('完成成功');
this.dialog.visible = false;
this.loadTable();
},
async handleBatchComplete() {
if (!this.selection.length) {
this.$message.warning('请选择需要完成的异常');
return;
}
await this.$confirm('确认批量完成已选择的异常处置?', '提示', { type: 'warning' });
await api.batchComplete(this.selection.map(item => item.id).join(','));
this.$message.success('批量完成成功');
this.loadTable();
},
openWaybill(row) {
if (row.waybillId) {
this.$router.push({
path: '/business/waybill-manage/detail',
query: { id: row.waybillId },
});
}
},
decorateRow(row) {
return {
...row,
exceptionTypeName: this.exceptionName(row.exceptionType),
disposalStatusName:
disposalStatusOptions.find(item => item.value === row.disposalStatus)?.label ||
row.disposalStatus ||
'-',
};
},
exceptionName(value) {
if (!value) return '-';
// 一级存中文大类名(如「交通异常」),二级存子类名(如「货损」),两级都查
const group = exceptionCategoryOptions.find(item => item.value === value);
if (group) return group.label;
for (const item of exceptionCategoryOptions) {
const match = item.children.find(child => child.value === value);
if (match) return match.label;
}
return value;
},
statusLinkType(status) {
return status === 'completed' ? 'info' : 'primary';
},
parsePhotos(value) {
if (!value) return [];
try {
const parsed = JSON.parse(value);
if (Array.isArray(parsed)) return parsed.filter(Boolean);
} catch (error) {
return String(value)
.split(',')
.map(item => item.trim())
.filter(Boolean);
}
return [];
},
unwrapPage(res) {
const data = res.data?.data || res.data || res || {};
return data.records ? data : { records: [], total: 0 };
},
noop() {},
},
};
</script>
<style scoped lang="scss">
.exception-disposal-page {
color: #303133;
}
.exception-disposal-page__search {
margin-bottom: 8px;
padding: 12px 12px 4px;
background: #fff;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
:deep(.el-form-item__label) {
min-width: 88px;
white-space: nowrap;
}
}
.exception-disposal-page__search-grid {
display: grid;
grid-template-columns: repeat(4, minmax(220px, 1fr));
gap: 8px 24px;
:deep(.el-form-item) {
margin-bottom: 8px;
}
:deep(.el-input),
:deep(.el-select),
:deep(.el-cascader),
:deep(.el-date-editor) {
width: 100%;
}
}
.exception-disposal-page__search-actions {
grid-column: 1 / -1;
display: flex;
justify-content: flex-end;
gap: 8px;
margin-bottom: 8px;
}
.exception-disposal-page__table-panel {
//background: #fff;
}
.exception-disposal-page__toolbar {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px 0;
}
.exception-disposal-page__toolbar-right,
.exception-disposal-page__actions {
display: flex;
align-items: center;
gap: 8px;
}
.exception-disposal-page__table {
--el-table-border-color: #eff1f7;
:deep(th.el-table__cell) {
background: #f5f7fa;
}
:deep(.el-table__row:nth-child(even) td.el-table__cell) {
background: #fafafa;
}
}
.exception-disposal-page__pagination {
display: flex;
justify-content: flex-end;
padding: 16px 0;
}
.exception-disposal-page__dialog {
padding: 4px 0 0;
}
.exception-disposal-page__attachment-link {
display: block;
margin-bottom: 2px;
}
.exception-disposal-page__readonly {
color: #606266;
word-break: break-all;
}
.exception-disposal-page__photos {
display: flex;
flex-wrap: wrap;
gap: 12px;
min-height: 80px;
:deep(.el-image) {
width: 96px;
height: 96px;
border: 1px solid #eff1f7;
}
}
.exception-disposal-page__dialog :deep(.section-card + .section-card) {
margin-top: 16px;
}
.exception-disposal-page__edit-photos {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 12px;
}
.exception-disposal-page__edit-photo {
display: flex;
flex-direction: column;
align-items: center;
gap: 4px;
:deep(.el-image) {
width: 96px;
height: 96px;
border: 1px solid #eff1f7;
}
}
.exception-disposal-page__edit-photo-add {
display: flex;
align-items: center;
justify-content: center;
width: 96px;
height: 96px;
border: 1px dashed #dcdfe6;
border-radius: 4px;
color: #909399;
cursor: pointer;
&:hover {
border-color: #409eff;
color: #409eff;
}
}
@media screen and (max-width: 1200px) {
.exception-disposal-page__search-grid {
grid-template-columns: repeat(2, minmax(220px, 1fr));
}
}
@media screen and (max-width: 760px) {
.exception-disposal-page__search-grid {
grid-template-columns: 1fr;
}
}
</style>