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.
450 lines
10 KiB
450 lines
10 KiB
3 weeks ago
|
<template>
|
||
|
<Drawer
|
||
|
v-model:open="visible"
|
||
|
title="修改合同法规"
|
||
|
width="80%"
|
||
|
:destroyOnClose="true"
|
||
|
class="edit-regulation-drawer"
|
||
|
placement="right"
|
||
|
>
|
||
|
<template #extra>
|
||
|
<Space>
|
||
|
<Button @click="handleCancel">取消</Button>
|
||
|
<Button type="primary" @click="handleSubmit" :loading="loading">保存</Button>
|
||
|
</Space>
|
||
|
</template>
|
||
|
|
||
|
<div class="edit-container">
|
||
|
<!-- 左侧PDF预览 -->
|
||
|
<div class="pdf-preview-section">
|
||
|
<div class="section-title">
|
||
|
<FileTextOutlined />
|
||
|
<span>法规文档预览</span>
|
||
|
<Button type="link" size="small" @click="refreshPdf" :loading="pdfLoading">
|
||
|
<ReloadOutlined />
|
||
|
</Button>
|
||
|
</div>
|
||
|
|
||
|
<div class="pdf-container">
|
||
|
<div v-if="pdfLoading" class="loading-container">
|
||
|
<Spin size="large" tip="正在加载文档..." />
|
||
|
</div>
|
||
|
|
||
|
<div v-else-if="pdfError" class="error-container">
|
||
|
<Result
|
||
|
status="error"
|
||
|
:title="pdfError"
|
||
|
sub-title="请检查网络连接或联系管理员"
|
||
|
>
|
||
|
<template #extra>
|
||
|
<Button type="primary" @click="refreshPdf">重新加载</Button>
|
||
|
</template>
|
||
|
</Result>
|
||
|
</div>
|
||
|
|
||
|
<div v-else-if="pdfUrl" class="pdf-embed-container">
|
||
|
<embed
|
||
|
:src="pdfUrl"
|
||
|
type="application/pdf"
|
||
|
width="100%"
|
||
|
height="100%"
|
||
|
style="border: none; display: block;"
|
||
|
/>
|
||
|
</div>
|
||
|
|
||
|
<div v-else class="no-pdf-container">
|
||
|
<Empty description="暂无PDF文档" />
|
||
|
</div>
|
||
|
</div>
|
||
|
</div>
|
||
|
|
||
|
<!-- 右侧修改表单 -->
|
||
|
<div class="edit-form-section">
|
||
|
<div class="section-title">
|
||
|
<EditOutlined />
|
||
|
<span>修改信息</span>
|
||
|
</div>
|
||
|
|
||
|
<div class="form-container">
|
||
|
<Form
|
||
|
ref="formRef"
|
||
|
:model="formData"
|
||
|
:rules="rules"
|
||
|
layout="vertical"
|
||
|
class="edit-form"
|
||
|
>
|
||
|
<!-- 法规名称 -->
|
||
|
<FormItem label="法规名称" name="regulationName" required>
|
||
|
<Input
|
||
|
v-model:value="formData.regulationName"
|
||
|
placeholder="请输入法规名称"
|
||
|
size="large"
|
||
|
/>
|
||
|
</FormItem>
|
||
|
|
||
|
<!-- 法规描述 -->
|
||
|
<FormItem label="法规描述" name="regulationDescription" required>
|
||
|
<TextArea
|
||
|
v-model:value="formData.regulationDescription"
|
||
|
placeholder="请输入法规描述"
|
||
|
:rows="6"
|
||
|
show-count
|
||
|
:maxlength="500"
|
||
|
size="large"
|
||
|
/>
|
||
|
</FormItem>
|
||
|
|
||
|
<!-- 发布日期 -->
|
||
|
<FormItem label="发布日期" name="publishDate" required>
|
||
|
<DatePicker
|
||
|
v-model:value="formData.publishDate"
|
||
|
format="YYYY-MM-DD"
|
||
|
valueFormat="YYYY-MM-DD"
|
||
|
style="width: 100%"
|
||
|
size="large"
|
||
|
/>
|
||
|
</FormItem>
|
||
|
|
||
|
<!-- 是否有效 -->
|
||
|
<FormItem label="是否有效" name="isEffective">
|
||
|
<Select
|
||
|
v-model:value="formData.isEffective"
|
||
|
:options="effectiveOptions"
|
||
|
size="large"
|
||
|
/>
|
||
|
</FormItem>
|
||
|
</Form>
|
||
|
</div>
|
||
|
</div>
|
||
|
</div>
|
||
|
</Drawer>
|
||
|
</template>
|
||
|
|
||
|
<script setup lang="ts">
|
||
|
import { ref, reactive, computed, watch, onUnmounted } from 'vue';
|
||
|
import {
|
||
|
Drawer,
|
||
|
Form,
|
||
|
FormItem,
|
||
|
Input,
|
||
|
DatePicker,
|
||
|
Select,
|
||
|
Button,
|
||
|
Space,
|
||
|
Spin,
|
||
|
Result,
|
||
|
Empty,
|
||
|
message
|
||
|
} from 'ant-design-vue';
|
||
|
import {
|
||
|
FileTextOutlined,
|
||
|
EditOutlined,
|
||
|
ReloadOutlined
|
||
|
} from '@ant-design/icons-vue';
|
||
|
import {
|
||
|
ContractualRegulationNamesInfo,
|
||
|
ContractualRegulationNamesUpdate,
|
||
|
ContractualRegulationNamesViewPdf
|
||
|
} from '@/api/contractReview/ContractualRegulationNames';
|
||
|
import { getDictOptions } from '@/utils/dict';
|
||
|
import dayjs from 'dayjs';
|
||
|
|
||
|
const { TextArea } = Input;
|
||
|
|
||
|
interface Props {
|
||
|
visible: boolean;
|
||
|
regulationId?: number | string;
|
||
|
}
|
||
|
|
||
|
const props = withDefaults(defineProps<Props>(), {
|
||
|
visible: false,
|
||
|
regulationId: undefined
|
||
|
});
|
||
|
|
||
|
const emit = defineEmits<{
|
||
|
'update:visible': [value: boolean];
|
||
|
'success': [];
|
||
|
}>();
|
||
|
|
||
|
// 状态变量
|
||
|
const loading = ref(false);
|
||
|
const pdfLoading = ref(false);
|
||
|
const pdfError = ref('');
|
||
|
const pdfUrl = ref('');
|
||
|
const formRef = ref();
|
||
|
|
||
|
// 表单数据
|
||
|
const formData = reactive({
|
||
|
id: undefined as string | number | undefined,
|
||
|
regulationName: '',
|
||
|
regulationDescription: '',
|
||
|
publishDate: '',
|
||
|
isEffective: 'Y'
|
||
|
});
|
||
|
|
||
|
// 计算属性
|
||
|
const visible = computed({
|
||
|
get: () => props.visible,
|
||
|
set: (value) => emit('update:visible', value)
|
||
|
});
|
||
|
|
||
|
// 表单验证规则
|
||
|
const rules = computed(() => ({
|
||
|
regulationName: [{ required: true, message: '请输入法规名称', trigger: 'blur' }],
|
||
|
regulationDescription: [{ required: true, message: '请输入法规描述', trigger: 'blur' }],
|
||
|
publishDate: [{ required: true, message: '请选择发布日期', trigger: 'change' }],
|
||
|
}));
|
||
|
|
||
|
// 字典选项
|
||
|
const effectiveOptions = getDictOptions('sys_yes_no');
|
||
|
|
||
|
// 监听弹窗显示
|
||
|
watch(() => props.visible, (newVisible) => {
|
||
|
if (newVisible && props.regulationId) {
|
||
|
loadRegulationData();
|
||
|
loadPdf();
|
||
|
} else if (!newVisible) {
|
||
|
resetForm();
|
||
|
cleanupPdfUrl();
|
||
|
}
|
||
|
});
|
||
|
|
||
|
// 加载法规数据
|
||
|
async function loadRegulationData() {
|
||
|
if (!props.regulationId) return;
|
||
|
|
||
|
try {
|
||
|
const result = await ContractualRegulationNamesInfo(props.regulationId);
|
||
|
if (result) {
|
||
|
formData.id = result.id;
|
||
|
formData.regulationName = result.regulationName || '';
|
||
|
formData.regulationDescription = result.regulationDescription || '';
|
||
|
formData.publishDate = result.publishDate || '';
|
||
|
formData.isEffective = result.isEffective || 'Y';
|
||
|
}
|
||
|
} catch (error: any) {
|
||
|
message.error('加载法规信息失败: ' + (error.message || '未知错误'));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// 加载PDF
|
||
|
async function loadPdf() {
|
||
|
if (!props.regulationId) return;
|
||
|
|
||
|
pdfLoading.value = true;
|
||
|
pdfError.value = '';
|
||
|
|
||
|
try {
|
||
|
const pdfResponse = await ContractualRegulationNamesViewPdf(props.regulationId);
|
||
|
|
||
|
if (pdfResponse?.data) {
|
||
|
// 清理之前的URL
|
||
|
cleanupPdfUrl();
|
||
|
|
||
|
// 创建新的Blob URL
|
||
|
const blob = new Blob([pdfResponse.data], { type: 'application/pdf' });
|
||
|
pdfUrl.value = URL.createObjectURL(blob);
|
||
|
} else {
|
||
|
pdfError.value = 'PDF数据格式错误';
|
||
|
}
|
||
|
} catch (err: any) {
|
||
|
pdfError.value = err.message || '加载PDF失败';
|
||
|
console.error('加载PDF失败:', err);
|
||
|
} finally {
|
||
|
pdfLoading.value = false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// 刷新PDF
|
||
|
function refreshPdf() {
|
||
|
loadPdf();
|
||
|
}
|
||
|
|
||
|
// 提交表单
|
||
|
async function handleSubmit() {
|
||
|
try {
|
||
|
await formRef.value?.validate();
|
||
|
} catch (error) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
loading.value = true;
|
||
|
try {
|
||
|
const submitData = {
|
||
|
...formData,
|
||
|
publishDate: formData.publishDate ? dayjs(formData.publishDate).format('YYYY-MM-DD') : ''
|
||
|
};
|
||
|
|
||
|
await ContractualRegulationNamesUpdate(submitData);
|
||
|
message.success('修改成功');
|
||
|
|
||
|
emit('update:visible', false);
|
||
|
emit('success');
|
||
|
} catch (error: any) {
|
||
|
message.error('修改失败: ' + (error.message || '未知错误'));
|
||
|
} finally {
|
||
|
loading.value = false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// 取消
|
||
|
function handleCancel() {
|
||
|
emit('update:visible', false);
|
||
|
}
|
||
|
|
||
|
// 重置表单
|
||
|
function resetForm() {
|
||
|
Object.assign(formData, {
|
||
|
id: undefined,
|
||
|
regulationName: '',
|
||
|
regulationDescription: '',
|
||
|
publishDate: '',
|
||
|
isEffective: 'Y'
|
||
|
});
|
||
|
formRef.value?.clearValidate();
|
||
|
pdfError.value = '';
|
||
|
}
|
||
|
|
||
|
// 清理Blob URL
|
||
|
function cleanupPdfUrl() {
|
||
|
if (pdfUrl.value && pdfUrl.value.startsWith('blob:')) {
|
||
|
URL.revokeObjectURL(pdfUrl.value);
|
||
|
pdfUrl.value = '';
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// 组件销毁时清理
|
||
|
onUnmounted(() => {
|
||
|
cleanupPdfUrl();
|
||
|
});
|
||
|
</script>
|
||
|
|
||
|
<style scoped>
|
||
|
.edit-regulation-drawer {
|
||
|
:deep(.ant-drawer-content) {
|
||
|
height: 100vh;
|
||
|
}
|
||
|
|
||
|
:deep(.ant-drawer-body) {
|
||
|
padding: 0;
|
||
|
height: calc(100vh - 55px);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
.edit-container {
|
||
|
display: flex;
|
||
|
height: 100%;
|
||
|
gap: 1px;
|
||
|
background: #f0f2f5;
|
||
|
}
|
||
|
|
||
|
.pdf-preview-section {
|
||
|
flex: 1;
|
||
|
background: #fff;
|
||
|
display: flex;
|
||
|
flex-direction: column;
|
||
|
}
|
||
|
|
||
|
.edit-form-section {
|
||
|
width: 400px;
|
||
|
background: #fff;
|
||
|
display: flex;
|
||
|
flex-direction: column;
|
||
|
border-left: 1px solid #e8e8e8;
|
||
|
}
|
||
|
|
||
|
.section-title {
|
||
|
padding: 16px 20px;
|
||
|
font-size: 16px;
|
||
|
font-weight: 500;
|
||
|
color: #262626;
|
||
|
border-bottom: 1px solid #e8e8e8;
|
||
|
display: flex;
|
||
|
align-items: center;
|
||
|
gap: 8px;
|
||
|
background: #fafafa;
|
||
|
|
||
|
span {
|
||
|
flex: 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
.pdf-container {
|
||
|
flex: 1;
|
||
|
position: relative;
|
||
|
background: #f5f5f5;
|
||
|
|
||
|
.loading-container {
|
||
|
display: flex;
|
||
|
justify-content: center;
|
||
|
align-items: center;
|
||
|
height: 100%;
|
||
|
}
|
||
|
|
||
|
.error-container {
|
||
|
padding: 40px;
|
||
|
height: 100%;
|
||
|
display: flex;
|
||
|
align-items: center;
|
||
|
justify-content: center;
|
||
|
}
|
||
|
|
||
|
.pdf-embed-container {
|
||
|
height: 100%;
|
||
|
padding: 16px;
|
||
|
|
||
|
embed {
|
||
|
border-radius: 6px;
|
||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
.no-pdf-container {
|
||
|
display: flex;
|
||
|
justify-content: center;
|
||
|
align-items: center;
|
||
|
height: 100%;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
.form-container {
|
||
|
flex: 1;
|
||
|
padding: 20px;
|
||
|
overflow-y: auto;
|
||
|
}
|
||
|
|
||
|
.edit-form {
|
||
|
.ant-form-item {
|
||
|
margin-bottom: 24px;
|
||
|
}
|
||
|
|
||
|
.ant-form-item-label > label {
|
||
|
font-weight: 500;
|
||
|
color: #262626;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* 响应式设计 */
|
||
|
@media (max-width: 1400px) {
|
||
|
.edit-form-section {
|
||
|
width: 380px;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@media (max-width: 1200px) {
|
||
|
.edit-container {
|
||
|
flex-direction: column;
|
||
|
}
|
||
|
|
||
|
.pdf-preview-section {
|
||
|
height: 50%;
|
||
|
}
|
||
|
|
||
|
.edit-form-section {
|
||
|
width: 100%;
|
||
|
height: 50%;
|
||
|
border-left: none;
|
||
|
border-top: 1px solid #e8e8e8;
|
||
|
}
|
||
|
}
|
||
|
</style>
|