| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575 |
- <!--
- * @Author: zhuMingXi
- * @Date: 2025/07/26 14:35:00
- * @LastEditors: zhuMingXi
- * @LastEditTime: 2025/07/26 14:35:00
- * @Description: 编辑检验方案弹窗
- * @FilePath: D:\yiqun\yudao-admin\yudao-ui-admin-vue3\src\views\pressure\taskorder\components\AddInspectionplanDetail.vue
- -->
- <template>
- <ContentWrap
- v-show="!showInlineEditRecord"
- title="检验方案"
- class="check-record-wrapper"
- v-loading="pdfLoading"
- >
- <div class="designer-inner" ref="wrapperContainerRef">
- <!-- <SpreadViewer :initData="initData" ref="spreadRef" />-->
- <SpreadViewer :initData="editData" ref="editSpreadRecordRef" @saveSuccess="saveSuccessRecord"/>
- </div>
- <div class="operation-inner relative">
- <div class="btn-list">
- <template v-if="['add', 'edit'].includes(isEdit)">
- <el-button type="primary" @click="handleSubmitBefore">提交审核</el-button>
- <!-- <el-button type="primary" plain @click="handleEditSpreadRecord">编辑封面</el-button>-->
- </template>
- <el-button type="default" plain @click="handleCloseModal">取 消</el-button>
- </div>
- <div class="operation-item" style="max-height: calc(100% / 2); overflow-y: auto">
- <div class="item-header"> 方案上传 </div>
- <div class="item-content">
- <el-upload
- ref="uploadRef"
- action="#"
- v-model:file-list="fileList"
- :http-request="handlePostFile"
- :auto-upload="false"
- :disabled="isEdit === 'view'"
- :limit="1"
- :on-change="handleChange"
- :on-error="handleUploadError"
- :on-exceed="handleExceed"
- :accept="accept"
- drag
- >
- <Icon icon="ep:upload" />
- <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
- <template #tip>
- <div class="el-upload__tip text-center">
- <span>{{ `仅允许导入${accept} 格式文件。` }}</span>
- </div>
- </template>
- </el-upload>
- </div>
- </div>
- <div class="operation-item">
- <div class="item-header"> 退回原因 </div>
- <div class="item-content">
- <el-input
- v-model="returnReason"
- :autosize="{ minRows: 10, maxRows: 50 }"
- type="textarea"
- readonly
- />
- </div>
- </div>
- </div>
- </ContentWrap>
- <!-- 检验录入-模板 -->
- <CustomDialog v-model="showInlineEditRecord" title="检验录入" :show-footer="false" fullscreen>
- <!-- <SpreadViewer :initData="editData" ref="editSpreadRecordRef" @saveSuccess="saveSuccessRecord"/>-->
- </CustomDialog>
- <CustomDialog
- ref="customUnitDialogRef"
- title="选择审核人"
- v-model="approvalUserVisible"
- width="650px"
- :dialogAttrs="{ zIndex: 10006 }"
- >
- <el-form ref="formRef" :model="formData" :rules="formRules" label-width="135px">
- <el-form-item label="检验方案审核人" prop="inspectionAuditId">
- <el-select v-model="formData.inspectionAuditId" clearable placeholder="请选择">
- <el-option
- v-for="item in optionList.schemeReviewerList"
- :key="item.id"
- :label="item.nickname"
- :value="item.id"
- />
- </el-select>
- </el-form-item>
- <!-- <el-form-item label="检验方案批准人" prop="inspectionApproveId">-->
- <!-- <el-select v-model="formData.inspectionApproveId" clearable placeholder="请选择">-->
- <!-- <el-option-->
- <!-- v-for="item in optionList.schemeApproveList"-->
- <!-- :key="item.id"-->
- <!-- :label="item.nickname"-->
- <!-- :value="item.id"-->
- <!-- />-->
- <!-- </el-select>-->
- <!-- </el-form-item>-->
- </el-form>
- <template #footer>
- <el-button @click="approvalUserVisible = false">取消</el-button>
- <el-button type="primary" @click="handleSubmitUser">确定</el-button>
- </template>
- </CustomDialog>
- </template>
- <script lang="tsx" setup>
- import { BoilerTaskOrderApi } from '@/api/pressure2/boilertaskorder'
- import {defineModel, PropType, ref} from 'vue'
- import VuePdfEmbed from 'vue-pdf-embed'
- import EditInspectionplanReport from './EditInspectionplanReport.vue'
- import { getPressureReportTemplatePDF } from '@/api/pressure/reportTemplate'
- import { ElForm, UploadFile, UploadProps, UploadRequestOptions } from 'element-plus'
- import { uploadFile } from '@/api/common'
- import VueOfficeDocx from '@vue-office/docx/lib/v3/vue-office-docx.mjs'
- import '@vue-office/docx/lib/v3/index.css'
- import * as UserGroupApi from '@/api/bpm/userGroup'
- import * as UserApi from '@/api/system/user'
- import { debounce } from 'lodash-es'
- import { PressureReportType } from '@/utils/constants'
- import SpreadDesigner from "@/components/SpreadDesigner/index.vue";
- import {DynamicTbValApi} from "@/api/pressure2/dynamictbval";
- import * as GC from "@grapecity-software/spread-sheets";
- import {getPDF, getStandardTemplateInfo} from "@/api/laboratory/standard/template";
- import {buildFileUrl} from "@/utils";
- import axios from "axios";
- import {editReport} from "@/utils/reportUtil";
- import {DynamicTbColApi} from "@/api/pressure2/dynamictbcol";
- import {SpreadViewer} from "@/components/DynamicReport";
- import {InitParams} from "@/components/DynamicReport/SpreadInterface";
- const props = defineProps({
- taskOrderDetail: {
- type: Object,
- default: () => ({})
- },
- inspectionplanDetail: {
- type: Object,
- default: () => ({})
- },
- isEdit: {
- type: String as PropType<'add' | 'edit' | 'view'>,
- required: true
- },
- editInspectionplanParams: {
- type: Object,
- default: () => ({})
- }
- })
- const emits = defineEmits(['success', 'refresh'])
- const visible = defineModel('visible', { type: Boolean, default: false })
- const returnReason = ref(props.inspectionplanDetail?.returnReason)
- // 选择审核人
- const formRef = ref<InstanceType<typeof ElForm>>()
- const formData = ref<Recordable>({})
- const formRules = reactive({
- inspectionAuditId: [{ required: true, message: '请选择审核人', trigger: 'change' }],
- inspectionApproveId: [{ required: true, message: '请选择批准人', trigger: 'change' }]
- })
- const optionList = reactive<{
- schemeReviewerList: Recordable[]
- schemeApproveList: Recordable[]
- }>({
- schemeReviewerList: [],
- schemeApproveList: []
- })
- const approvalUserVisible = ref(false)
- // 获取审核人信息
- const getSchemeReviewerList = async () => {
- try {
- const { inspectionAuditId, inspectionApproveId } = await UserApi.getApprovalDetail({})
- formData.value = {
- inspectionAuditId,
- inspectionApproveId
- }
- optionList.schemeReviewerList = await UserGroupApi.getUserGroupUserList({
- category: 600,
- status: 0
- })
- optionList.schemeApproveList = await UserGroupApi.getUserGroupUserList({
- category: 700,
- status: 0
- })
- approvalUserVisible.value = true
- } catch (error) {}
- }
- const handleSubmitUser = () => {
- formRef.value?.validate(async (valid) => {
- if (valid) {
- handleSubmitConfirm()
- }
- })
- }
- // PDF相关状态
- const pdfLoading = ref(false)
- const showInlineEditRecord = ref(false)
- const handleEditSpreadRecord = () => {
- editPreview()
- showInlineEditRecord.value = true
- }
- const handleSuccess = (info) => {
- // 更新相关的信息,reportId prepareJson 等
- console.log('handleSuccess-info:', info )
- emits('success', info)
- initPreview()
- }
- const uploadRef = ref()
- const fileList = ref<UploadFile[]>([]) // 文件列表
- const accept = '.docx,.pdf'
- const fileLoading = ref(false) // 表单的加载中
- // 获取文件类型
- const getFileExtension = (fileName: string) => {
- return fileName.split('.').pop()?.toLowerCase()
- }
- const previewUrl = ref()
- const getPreviewFileUrl = (file: File) => {
- let fileReader = new FileReader()
- fileReader.onload = () => {
- previewUrl.value = fileReader.result
- }
- if (getFileExtension(file.name) === 'docx') fileReader.readAsArrayBuffer(file)
- else {
- const pdfBlob = new Blob([file], { type: 'application/pdf' })
- previewUrl.value = URL.createObjectURL(pdfBlob)
- }
- }
- // 文件类型校验
- const handleChange: UploadProps['onChange'] = (file, uploadFiles) => {
- console.log(file, uploadFiles)
- const allowedExtensions = accept.toLowerCase().split(',')
- const fileExt = '.' + (getFileExtension(file.name) || '')
- if (!allowedExtensions.includes(fileExt)) {
- ElMessage.error(`不支持的文件类型,仅允许上传: ${allowedExtensions.join(',')}类型文件`)
- uploadFiles.splice(uploadFiles.indexOf(file), 1)
- return false // 阻止上传
- }
- previewUrl.value = null
- file.raw && getPreviewFileUrl(file.raw as File)
- }
- /** 文件数超出提示 */
- const handleExceed = (): void => {
- ElMessage.error('最多只能上传一个文件!')
- }
- /** 上传错误处理 */
- const handleUploadError = (): void => {
- fileLoading.value = false
- }
- // 文件上传方法
- const handlePostFile = async (options: UploadRequestOptions) => {
- const { file, onProgress, onSuccess, onError } = options
- fileLoading.value = true
- try {
- // 上传文件
- const formData = new FormData()
- formData.append('file', file)
- const manualUrl = await uploadFile(formData)
- // 更新文件列表
- fileList.value = [
- {
- ...file,
- response: manualUrl,
- name: file.name,
- status: 'success',
- url: manualUrl,
- uid: file.uid || Date.now() + Math.random()
- }
- ]
- onSuccess(manualUrl)
- handleSubmitOperationReport(manualUrl)
- } catch (error: any) {
- onError(error)
- ElMessage.error('上传失败,请您重新上传!')
- } finally {
- fileLoading.value = false
- }
- }
- // 提交审核
- const handleSubmitOperationReport = async (manualUrl = '') => {
- // 弹窗选择审批人
- const { reportId, templateId, prepareJson } = props.editInspectionplanParams
- const params: Recordable = {
- id: reportId,
- prepareJson: prepareJson,
- auditUserIds: [formData.value.inspectionAuditId],
- approveUserIds: [formData.value.inspectionApproveId],
- reportType: PressureReportType['INSPECTIONPLAN']
- // 检验方案,
- // reportUrl: uploadUrl,
- }
- if(manualUrl) params.manualUrl = manualUrl
- const submitRes = await BoilerTaskOrderApi.submitOpinionNoticeApproval(params)
- if (submitRes) {
- ElMessage.success('提交审核成功')
- handleCloseModal()
- }
- }
- const handleSubmitConfirm = () => {
- approvalUserVisible.value = false
- // if(props.isEdit === 'add'){
- // fileList.value[0]?.url ? handleSubmitOperationReport(fileList.value[0].url) : uploadRef.value.submit()
- // } else {
- // handleSubmitOperationReport()
- // }
- fileList.value[0]?.url ? handleSubmitOperationReport(fileList.value[0].url) : uploadRef.value.submit()
- }
- const handleSubmitBefore = async () => {
- try {
- await editSpreadRecordRef.value?.handleSave()
- } catch (error) {
- console.error('保存数据失败:', error)
- ElMessage.error('保存数据失败,请重试')
- return
- }
- // console.log(props.editInspectionplanParams, 'props.editInspectionplanParams')
- // if (props.isEdit === 'add' && !fileList.value.length) return ElMessage.error('请先上传方案文件')
- if (!fileList.value.length) return ElMessage.error('请先上传方案文件')
- getSchemeReviewerList()
- }
- // 关闭弹窗
- const handleCloseModal = () => {
- visible.value = false
- emits('refresh')
- }
- const pdfContentWidth = ref(0)
- const wrapperContainerRef = ref<HTMLDivElement | null>(null)
- const handleWindowResize = debounce(() => {
- if(wrapperContainerRef.value){
- console.log('wrapperContainerRef.value:log', wrapperContainerRef.value)
- const currentWidth = wrapperContainerRef.value?.clientWidth
- pdfContentWidth.value = currentWidth > 950 ? 950 : currentWidth - 20
- }
- }, 100)
- const initData=ref<InitParams>(
- {
- templateId: '',
- refId: '',
- refName:'',
- insId:'',
- opType: 1, // 0:excel,1: pdf
- manualUrl: '',
- });
- const editData=ref<InitParams>(
- {
- templateId: '',
- refId: '',
- refName:'',
- insId:'',
- opType: 0, // 0:excel,1: pdf
- });
- const spreadRef=ref();
- const editSpreadRecordRef=ref();
- const saveSuccessRecord = async (data)=>{
- handleSuccess()
- showInlineEditRecord.value = false
- }
- const editPreview=()=>{
- editData.value.templateId = props.editInspectionplanParams.templateId;
- editData.value.refId = props.editInspectionplanParams.reportId;
- editData.value.opType = 0;
- setTimeout(()=>{
- editSpreadRecordRef.value?.reloadView();
- },50)
- console.log('editDataPreview', editData.value)
- }
- const initPreview=()=>{
- initData.value.templateId = props.editInspectionplanParams.templateId;
- initData.value.refId = props.editInspectionplanParams.reportId;
- initData.value.opType = 1;
- initData.value.manualUrl = props.inspectionplanDetail?.manualUrl;
- setTimeout(()=>{
- spreadRef.value?.reloadView();
- },50)
- console.log('initPreview', initData.value)
- }
- onMounted(()=>{
- handleWindowResize()
- // console.log('wrapperContainerHeight.value ', wrapperContainerHeight.value)
- window.addEventListener('resize', handleWindowResize)
- // initPreview()
- editPreview()
- })
- onUnmounted(() => {
- window.removeEventListener('resize', handleWindowResize)
- })
- </script>
- <style lang="scss" scoped>
- :deep(.app-container) {
- position: relative;
- }
- .check-record-wrapper {
- position: absolute;
- left: 0;
- right: 0;
- top: 0;
- bottom: 0;
- z-index: 2000;
- :deep(.el-card__body) {
- display: flex;
- justify-content: space-between;
- align-items: stretch;
- height: calc(100% - 58px);
- }
- :deep(.spread-designer-container) {
- height: calc(
- 100vh - var(--top-tool-height) - var(--tags-view-height) - var(--app-footer-height) - 64px
- );
- padding: 0;
- border: 1px solid var(--el-border-color);
- border-right-width: 0px;
- }
- .designer-inner {
- width: calc(100% - 440px);
- flex: 1;
- position: relative;
- }
- .operation-inner {
- display: flex;
- flex-direction: column;
- gap: 10px;
- height: 100%;
- flex-basis: 440px;
- padding: 7px 10px 20px 10px;
- border: 1px solid var(--el-border-color);
- background-color: #fff;
- box-sizing: border-box;
- overflow: hidden;
- .btn-list {
- padding: 5px 0 2px;
- }
- .version-box {
- padding: 8px 0 12px;
- }
- .operation-item {
- position: relative;
- // max-height: calc(100% / 3);
- overflow: hidden;
- .item-header {
- position: sticky;
- left: 0;
- top: 0;
- z-index: 1000;
- width: 100%;
- height: 28px;
- padding-left: 20px;
- font-size: 16px;
- line-height: 28px;
- color: #fff;
- background: #fff url(@/assets/imgs/pressure/my-task-detail-operation-bg.png) no-repeat left
- top;
- background-size: 100% 28px;
- }
- .item-content {
- // height: calc(100% - 48px);
- height: auto;
- padding: 10px 0;
- font-size: 14px;
- overflow-y: auto;
- overflow-x: hidden;
- box-sizing: content-box;
- .el-empty {
- width: 100%;
- height: 150px;
- padding: 0;
- box-sizing: border-box;
- }
- }
- &:last-child {
- // max-height: unset;
- // flex-grow: 1;
- flex: 1;
- .item-content {
- height: calc(100% - 48px);
- }
- }
- :deep(.smart-table-inner .toolbar) {
- margin: 0 !important;
- }
- .error-item {
- display: flex;
- align-items: center;
- padding: 7px 10px;
- color: #41475c;
- border: 1px solid var(--el-border-color);
- border-width: 1px 0 1px 0;
- background-color: #f5f5fa;
- .el-icon {
- margin-right: 6px;
- }
- &:nth-child(n + 1) {
- margin-top: -1px;
- }
- &:nth-child(even) {
- background-color: #fff;
- }
- }
- }
- }
- }
- .history-version-table {
- height: 100%;
- display: flex;
- flex-direction: column;
- overflow: hidden;
- :deep(.smart-table-inner) {
- flex: 1;
- display: flex;
- flex-direction: column;
- // border: 1px solid red;
- overflow: hidden;
- .smart-table-table {
- flex: 1;
- overflow: hidden;
- .el-table {
- height: calc(100% - 52px);
- }
- }
- }
- }
- </style>
- <style>
- .docx-wrapper {
- background-color: #fff !important;
- }
- .docx-wrapper > section.docx {
- box-shadow: none !important;
- }
- </style>
|