|
|
@@ -23,28 +23,36 @@
|
|
|
<view v-if="loading" class="loading-container">
|
|
|
<text class="loading-text">加载中...</text>
|
|
|
</view>
|
|
|
- <!-- #ifdef H5 -->
|
|
|
- <view v-else-if="pdfUrl" id="pdf-viewer-container" class="pdf-viewer-container"></view>
|
|
|
- <!-- #endif -->
|
|
|
- <!-- #ifndef H5 -->
|
|
|
- <web-view v-else-if="pdfUrl" :src="pdfUrl" />
|
|
|
- <!-- #endif -->
|
|
|
- <view v-else class="empty-container">
|
|
|
- <text class="empty-text">暂无PDF</text>
|
|
|
+ <!-- PDF 图片预览 -->
|
|
|
+ <view class="pdf-preview" style="display: none; width: 100%; height: 500px">
|
|
|
+ <SpreadPDFViewer
|
|
|
+ ref="spreadPdfViewerRef"
|
|
|
+ :businessConfig="businessConfig"
|
|
|
+ :templateData="templateData"
|
|
|
+ :templateBlob="templateBlob"
|
|
|
+ @data-loaded="handleSpreadDataLoaded"
|
|
|
+ @save-file="handleSpreadSaveFile"
|
|
|
+ />
|
|
|
</view>
|
|
|
+ <PDFViewer ref="pdfViewerRef" :source="pdfSource" />
|
|
|
+ <!-- <view v-else class="empty-container">
|
|
|
+ <text class="empty-text">暂无PDF</text>
|
|
|
+ </view> -->
|
|
|
</view>
|
|
|
</view>
|
|
|
</template>
|
|
|
|
|
|
<script lang="ts" setup>
|
|
|
-import { ref, computed, onMounted, watch, nextTick } from 'vue'
|
|
|
+import { ref, computed, onMounted } from 'vue'
|
|
|
import { onLoad } from '@dcloudio/uni-app'
|
|
|
-import { getSafetyCheckRecordTemplate, getServiceOrderPDF } from '@/api/task'
|
|
|
-import { useUserStore } from '@/store/user'
|
|
|
-import { isH5 } from '@/utils/platform'
|
|
|
-import dayjs from 'dayjs'
|
|
|
+import { getDynamicTbVal } from '@/api/task'
|
|
|
import HeadView from '../components/HeadView.vue'
|
|
|
import NavBar from '@/components/NavBar/NavBar.vue'
|
|
|
+import { getSecurityCheckTemplate } from '@/api/boiler/boilerTaskOrderSecurityCheck'
|
|
|
+import { getStandardTemplate } from '@/api'
|
|
|
+import { buildFileUrl, getEnvBaseUrl } from '@/utils/index'
|
|
|
+import SpreadPDFViewer from '@/components/SpreadDesigner/SpreadPDFViewer.vue'
|
|
|
+import PDFViewer from '@/components/PDFViewer/index.vue'
|
|
|
|
|
|
// 路由参数
|
|
|
const detailItem = ref<any>({})
|
|
|
@@ -53,6 +61,24 @@ const unitContact = ref('')
|
|
|
const unitPhone = ref('')
|
|
|
const receiverEmail = ref('')
|
|
|
const templateId = ref('')
|
|
|
+const spreadPdfViewerRef = ref<any>(null)
|
|
|
+const pdfSource = ref<ArrayBuffer | Blob | null>(null)
|
|
|
+
|
|
|
+const templateBlob = ref<any>(null)
|
|
|
+const businessConfig = ref<any>({
|
|
|
+ businessType: 'AQJC',
|
|
|
+ title: '安全检查记录编辑',
|
|
|
+ disableNavigate: true, // 是否禁用跳转,默认不禁用
|
|
|
+
|
|
|
+ ui: {
|
|
|
+ title: '安全检查记录',
|
|
|
+ saveButtonText: '保存',
|
|
|
+ cancelButtonText: '取消',
|
|
|
+ showAdditionalToolbar: true,
|
|
|
+ customButtons: [],
|
|
|
+ },
|
|
|
+})
|
|
|
+const templateData = ref<any>({})
|
|
|
|
|
|
onLoad((options) => {
|
|
|
orderId.value = options.orderId || ''
|
|
|
@@ -70,13 +96,8 @@ onLoad((options) => {
|
|
|
}
|
|
|
})
|
|
|
|
|
|
-// 用户信息
|
|
|
-const userStore = useUserStore()
|
|
|
-
|
|
|
// 状态
|
|
|
const loading = ref(false)
|
|
|
-const pdfUrl = ref('')
|
|
|
-const templateParams = ref<any>({})
|
|
|
|
|
|
// 是否显示操作按钮 (对应 PJ 中的 useMemo 计算)
|
|
|
const renderCenter = computed(() => {
|
|
|
@@ -95,90 +116,43 @@ const btnArray = computed(() => {
|
|
|
]
|
|
|
})
|
|
|
|
|
|
-// Blob 转 Base64
|
|
|
-const blobToBase64 = (blob: Blob): Promise<string> => {
|
|
|
- return new Promise((resolve, reject) => {
|
|
|
- const reader = new FileReader()
|
|
|
- reader.onloadend = () => {
|
|
|
- const base64data = reader.result as string
|
|
|
- resolve(base64data.split(',')[1])
|
|
|
- }
|
|
|
- reader.onerror = reject
|
|
|
- reader.readAsDataURL(blob)
|
|
|
- })
|
|
|
-}
|
|
|
-
|
|
|
-// 获取模板
|
|
|
-const handleGetTemplate = async () => {
|
|
|
- const { nickname } = userStore.userInfo || {}
|
|
|
-
|
|
|
- const response = await getSafetyCheckRecordTemplate({ orderId: orderId.value })
|
|
|
- const result = response?.data || {}
|
|
|
- const dataStr = result.dataSource ? JSON.parse(result.dataSource) : {}
|
|
|
-
|
|
|
- const temPar = {
|
|
|
- ...result,
|
|
|
- templateUrl: result.recordTemplateUrl,
|
|
|
- dataSource: JSON.stringify({
|
|
|
- ...dataStr,
|
|
|
- signName: nickname,
|
|
|
- signDate: dayjs().format('YYYY年MM月DD日'),
|
|
|
- }),
|
|
|
- }
|
|
|
-
|
|
|
- templateParams.value = temPar
|
|
|
- return temPar
|
|
|
-}
|
|
|
-
|
|
|
-// 获取安全检查记录 PDF
|
|
|
-const getAppServiceOrderPDF = async (tplParams: any) => {
|
|
|
- const { signFilePdf, id } = detailItem.value || {}
|
|
|
- const { dataSource, templateUrl } = tplParams || {}
|
|
|
-
|
|
|
- if (signFilePdf) {
|
|
|
- pdfUrl.value = buildFileUrl(signFilePdf)
|
|
|
- return
|
|
|
- }
|
|
|
-
|
|
|
- const params = {
|
|
|
- dataStr: dataSource,
|
|
|
- templateUrl,
|
|
|
- orderId: orderId.value,
|
|
|
- securityCheckId: id,
|
|
|
- }
|
|
|
-
|
|
|
- const response: any = await getServiceOrderPDF(params)
|
|
|
- const base64Data = await blobToBase64(response)
|
|
|
- const filePath = `data:application/pdf;base64,${base64Data}`
|
|
|
- if (filePath) {
|
|
|
- pdfUrl.value = filePath
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-// 构建文件 URL
|
|
|
-const buildFileUrl = (fileId: string) => {
|
|
|
- // const baseUrl = uni.getStorageSync('BASE_URL') || 'http://192.168.0.113:40080'
|
|
|
- const baseUrl = 'https://youdao.hofo.co/dexdev/'
|
|
|
- return `${baseUrl}${fileId}`
|
|
|
-}
|
|
|
-
|
|
|
-// 签名 (保留但不使用,与 PJ 版本一致)
|
|
|
-const handleToSign = async () => {
|
|
|
- const { signFilePdf, id } = detailItem.value || {}
|
|
|
- if (!id) return
|
|
|
-
|
|
|
- const url = signFilePdf ? '/pages/sign-detail/index' : '/pages/sign/index'
|
|
|
- uni.redirectTo({
|
|
|
- url: `${url}?orderId=${orderId.value}&type=AQJC&securityCheckId=${id}&unitContact=${unitContact.value}&unitPhone=${unitPhone.value}&receiverEmail=${receiverEmail.value}&templateId=${templateId.value}`,
|
|
|
- })
|
|
|
-}
|
|
|
-
|
|
|
// 初始化
|
|
|
const init = async () => {
|
|
|
loading.value = true
|
|
|
try {
|
|
|
- const tplParams = await handleGetTemplate()
|
|
|
- await getAppServiceOrderPDF(tplParams)
|
|
|
+ // const tplParams = await handleGetTemplate()
|
|
|
+ // await getAppServiceOrderPDF(tplParams)
|
|
|
+ const id = detailItem.value?.id || ''
|
|
|
+ if (!id) return
|
|
|
+ const defaultTemplateResp = await getSecurityCheckTemplate({ orderId: orderId.value })
|
|
|
+ const res = await getStandardTemplate({ id: defaultTemplateResp.data?.templateId })
|
|
|
+ const resData = (res as any).data
|
|
|
+ const dataMap: any = {}
|
|
|
+ const dynamicTbValResp = await getDynamicTbVal({ refId: id })
|
|
|
+ const dynamicTb: any = dynamicTbValResp.data
|
|
|
+ for (let i = 0; i < dynamicTb.dynamicTbValRespVOList.length; i++) {
|
|
|
+ const item = dynamicTb.dynamicTbValRespVOList[i]
|
|
|
+ dataMap[item.colCode] = item.valValue
|
|
|
+ }
|
|
|
+ // 组装templateData
|
|
|
+ templateData.value = {
|
|
|
+ schema: resData.bindingPathSchema ? JSON.parse(resData.bindingPathSchema) : {},
|
|
|
+ data: {
|
|
|
+ ...dataMap,
|
|
|
+ templateId,
|
|
|
+ templateUrl: resData.fileUrl,
|
|
|
+ },
|
|
|
+ pathNameMapping: JSON.parse(resData.bindingPathNameJson),
|
|
|
+ template: templateId,
|
|
|
+ templateUrl: resData.fileUrl,
|
|
|
+ }
|
|
|
+
|
|
|
+ console.log(templateData.value)
|
|
|
+ // 获取 template 文件
|
|
|
+ const fileUri = resData.fileUrl
|
|
|
+ const fileUrl = buildFileUrl(fileUri)
|
|
|
+ const fileBase64 = await downloadFileAsBase64(fileUrl)
|
|
|
+ templateBlob.value = fileBase64
|
|
|
} catch (error) {
|
|
|
console.error('[Page] 初始化失败:', error)
|
|
|
uni.showToast({ title: 'PDF 加载失败', icon: 'none' })
|
|
|
@@ -187,93 +161,86 @@ const init = async () => {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-// 返回
|
|
|
-const goBack = () => {
|
|
|
- uni.navigateBack()
|
|
|
+const downloadFileAsBase64 = (fileUrl: string): Promise<string> => {
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
+ uni.request({
|
|
|
+ url: fileUrl,
|
|
|
+ method: 'GET',
|
|
|
+ responseType: 'arraybuffer',
|
|
|
+ success: (res) => {
|
|
|
+ if (res.statusCode === 200) {
|
|
|
+ const arrayBuffer = res.data as ArrayBuffer
|
|
|
+ const uint8Array = new Uint8Array(arrayBuffer)
|
|
|
+ const binaryString = uint8Array.reduce((data, byte) => {
|
|
|
+ return data + String.fromCharCode(byte)
|
|
|
+ }, '')
|
|
|
+ const base64 = btoa(binaryString)
|
|
|
+ resolve(base64) // 成功时 resolve Base64 字符串
|
|
|
+ } else {
|
|
|
+ reject(new Error(`Request failed with status ${res.statusCode}`))
|
|
|
+ }
|
|
|
+ },
|
|
|
+ fail: (err) => {
|
|
|
+ reject(err) // 失败时 reject 错误
|
|
|
+ },
|
|
|
+ })
|
|
|
+ })
|
|
|
}
|
|
|
|
|
|
-// H5 端 PDF 渲染
|
|
|
-const initH5PdfViewer = async () => {
|
|
|
- if (!pdfUrl.value) return
|
|
|
-
|
|
|
- const container = document.getElementById('pdf-viewer-container')
|
|
|
- if (!container) return
|
|
|
-
|
|
|
- container.innerHTML = ''
|
|
|
+const handleSpreadDataLoaded = () => {
|
|
|
+ console.log('spreadDataLoaded......')
|
|
|
+ spreadPdfViewerRef.value.saveExcel()
|
|
|
+}
|
|
|
|
|
|
- const isBase64 = pdfUrl.value.startsWith('data:application/pdf;base64,')
|
|
|
+const handleSpreadSaveFile = async (blob: Blob) => {
|
|
|
+ console.log('spread Save file.....', blob)
|
|
|
+ const fd = new FormData()
|
|
|
+ fd.append('file', blob)
|
|
|
+ console.log('excel file......', Array.from(fd.entries()))
|
|
|
+ const pdfData = await getPDF(fd)
|
|
|
|
|
|
- if (!(window as any).pdfjsLib) {
|
|
|
- const script = document.createElement('script')
|
|
|
- script.src = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.min.js'
|
|
|
- script.onerror = () => uni.showToast({ title: 'PDF.js 加载失败', icon: 'none' })
|
|
|
- document.head.appendChild(script)
|
|
|
- await new Promise<void>((resolve, reject) => {
|
|
|
- script.onload = () => resolve()
|
|
|
- script.onerror = reject
|
|
|
- })
|
|
|
+ if (pdfData) {
|
|
|
+ pdfSource.value = pdfData
|
|
|
}
|
|
|
+}
|
|
|
|
|
|
- try {
|
|
|
- const pdfjsLib = (window as any).pdfjsLib
|
|
|
- pdfjsLib.GlobalWorkerOptions.workerSrc =
|
|
|
- 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.worker.min.js'
|
|
|
-
|
|
|
- let loadingTask
|
|
|
- if (isBase64) {
|
|
|
- const base64Data = pdfUrl.value.replace(/^data:application\/pdf;base64,/, '')
|
|
|
- const binaryString = atob(base64Data)
|
|
|
- const bytes = new Uint8Array(binaryString.length)
|
|
|
- for (let i = 0; i < binaryString.length; i++) {
|
|
|
- bytes[i] = binaryString.charCodeAt(i)
|
|
|
- }
|
|
|
- loadingTask = pdfjsLib.getDocument({ data: bytes })
|
|
|
- } else {
|
|
|
- loadingTask = pdfjsLib.getDocument(pdfUrl.value)
|
|
|
- }
|
|
|
-
|
|
|
- const pdf = await loadingTask.promise
|
|
|
- container.style.overflow = 'auto'
|
|
|
+const getPDF = (formData: FormData): Promise<ArrayBuffer> => {
|
|
|
+ const apiPath = '/pressure2/standard-file/getPdfLocal'
|
|
|
|
|
|
- for (let pageNum = 1; pageNum <= pdf.numPages; pageNum++) {
|
|
|
- const page = await pdf.getPage(pageNum)
|
|
|
- const scale = 1.5
|
|
|
- const viewport = page.getViewport({ scale })
|
|
|
+ // #ifdef H5
|
|
|
+ return getPDFForH5(apiPath, formData)
|
|
|
+ // #endif
|
|
|
+}
|
|
|
|
|
|
- const canvas = document.createElement('canvas')
|
|
|
- canvas.id = `pdf-page-${pageNum}`
|
|
|
- canvas.style.display = 'block'
|
|
|
- canvas.style.marginBottom = '10px'
|
|
|
+const getPDFForH5 = async (url: string, formData: FormData): Promise<ArrayBuffer> => {
|
|
|
+ let requestUrl = url
|
|
|
+ if (JSON.parse(import.meta.env.VITE_APP_PROXY) && import.meta.env.MODE === 'development') {
|
|
|
+ requestUrl = import.meta.env.VITE_APP_PROXY_PREFIX + url
|
|
|
+ } else {
|
|
|
+ requestUrl = getEnvBaseUrl() + url
|
|
|
+ }
|
|
|
|
|
|
- const context = canvas.getContext('2d')
|
|
|
- canvas.height = viewport.height
|
|
|
- canvas.width = viewport.width
|
|
|
+ const token = uni.getStorageSync('ACCESS_TOKEN')
|
|
|
+ const headers: Record<string, string> = {}
|
|
|
+ if (token) {
|
|
|
+ headers.Authorization = `Bearer ${token}`
|
|
|
+ }
|
|
|
|
|
|
- container.appendChild(canvas)
|
|
|
+ const response = await fetch(requestUrl, {
|
|
|
+ method: 'POST',
|
|
|
+ headers,
|
|
|
+ body: formData,
|
|
|
+ })
|
|
|
|
|
|
- await page.render({ canvasContext: context, viewport }).promise
|
|
|
- }
|
|
|
- } catch (error) {
|
|
|
- console.error('[PDF] Error rendering PDF:', error)
|
|
|
- uni.showToast({ title: 'PDF 渲染失败', icon: 'none' })
|
|
|
+ if (!response.ok) {
|
|
|
+ throw new Error(`Request failed with status ${response.status}`)
|
|
|
}
|
|
|
+
|
|
|
+ return await response.arrayBuffer()
|
|
|
}
|
|
|
|
|
|
onMounted(async () => {
|
|
|
await init()
|
|
|
-
|
|
|
- if (isH5) {
|
|
|
- await nextTick()
|
|
|
- if (pdfUrl.value) {
|
|
|
- setTimeout(() => initH5PdfViewer(), 100)
|
|
|
- } else {
|
|
|
- watch(pdfUrl, (val) => {
|
|
|
- if (val) {
|
|
|
- setTimeout(() => initH5PdfViewer(), 100)
|
|
|
- }
|
|
|
- })
|
|
|
- }
|
|
|
- }
|
|
|
})
|
|
|
</script>
|
|
|
|
|
|
@@ -288,12 +255,12 @@ onMounted(async () => {
|
|
|
.navigate-view {
|
|
|
display: flex;
|
|
|
flex-direction: row;
|
|
|
- justify-content: space-between;
|
|
|
align-items: center;
|
|
|
+ justify-content: space-between;
|
|
|
height: 44px;
|
|
|
+ padding: 0 15px;
|
|
|
background-color: #fff;
|
|
|
border-bottom: 1px solid #eee;
|
|
|
- padding: 0 15px;
|
|
|
}
|
|
|
|
|
|
.navigate-left {
|
|
|
@@ -308,8 +275,8 @@ onMounted(async () => {
|
|
|
}
|
|
|
|
|
|
.navigate-center {
|
|
|
- flex: 1;
|
|
|
display: flex;
|
|
|
+ flex: 1;
|
|
|
justify-content: center;
|
|
|
}
|
|
|
|
|
|
@@ -320,9 +287,9 @@ onMounted(async () => {
|
|
|
}
|
|
|
|
|
|
.navigate-right {
|
|
|
- width: 44px;
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
+ width: 44px;
|
|
|
}
|
|
|
|
|
|
.nav-title {
|
|
|
@@ -335,29 +302,28 @@ onMounted(async () => {
|
|
|
flex: 1;
|
|
|
background-color: #f5f5f5;
|
|
|
}
|
|
|
-
|
|
|
/* #ifdef H5 */
|
|
|
.pdf-viewer-container {
|
|
|
+ box-sizing: border-box;
|
|
|
width: 100%;
|
|
|
height: 100%;
|
|
|
- background-color: #fff;
|
|
|
- text-align: center;
|
|
|
padding: 10px;
|
|
|
- box-sizing: border-box;
|
|
|
+ text-align: center;
|
|
|
+ background-color: #fff;
|
|
|
}
|
|
|
/* #endif */
|
|
|
|
|
|
.loading-container,
|
|
|
.empty-container {
|
|
|
display: flex;
|
|
|
- justify-content: center;
|
|
|
align-items: center;
|
|
|
+ justify-content: center;
|
|
|
height: 100%;
|
|
|
}
|
|
|
|
|
|
.loading-text,
|
|
|
.empty-text {
|
|
|
- color: #999;
|
|
|
font-size: 14px;
|
|
|
+ color: #999;
|
|
|
}
|
|
|
</style>
|