| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149 |
- /**
- * OA PDF 预览页 query 解析(短 fileUrl / 兼容旧 url)
- * 微信 onLoad 可能已 decode 一次;须安全二次解码,并兼容大小写 key。
- */
- const LOG_TAG = '[oa-pdf-preview]'
- export function pdfPreviewLog(...args) {
- try {
- console.log(LOG_TAG, ...args)
- } catch (_) {
- /* ignore */
- }
- }
- export function safeDecodeURIComponent(value) {
- const raw = String(value == null ? '' : value).trim()
- if (!raw) return ''
- try {
- return decodeURIComponent(raw)
- } catch (_) {
- return raw
- }
- }
- /** 按候选 key 取值;忽略大小写;兼容微信偶发改写 */
- export function pickQueryValue(query, keys) {
- const q = query && typeof query === 'object' ? query : {}
- const list = Array.isArray(keys) ? keys : [keys]
- for (let i = 0; i < list.length; i++) {
- const k = list[i]
- if (k == null) continue
- if (Object.prototype.hasOwnProperty.call(q, k) && q[k] != null) {
- const v = String(q[k]).trim()
- if (v) return v
- }
- }
- const lowerMap = {}
- Object.keys(q).forEach((k) => {
- lowerMap[String(k).toLowerCase()] = q[k]
- })
- for (let i = 0; i < list.length; i++) {
- const k = String(list[i] || '').toLowerCase()
- if (!k) continue
- if (lowerMap[k] != null) {
- const v = String(lowerMap[k]).trim()
- if (v) return v
- }
- }
- return ''
- }
- /**
- * @returns {{ fileUrl: string, legacyUrl: string, name: string, rawKeys: string[] }}
- */
- export function parsePdfPreviewQuery(query) {
- const rawKeys = Object.keys(query && typeof query === 'object' ? query : {})
- const fileUrl = safeDecodeURIComponent(
- pickQueryValue(query, ['fileUrl', 'fileurl', 'file_url', 'f'])
- )
- const legacyUrl = safeDecodeURIComponent(
- pickQueryValue(query, ['url', 'downloadUrl', 'download_url'])
- )
- const name =
- safeDecodeURIComponent(pickQueryValue(query, ['name', 'fileName', 'file_name', 'n'])) ||
- 'approval.pdf'
- return { fileUrl, legacyUrl, name, rawKeys }
- }
- /**
- * 后端 MINIO_BASE_URL 常配成同域相对路径(如 /minio/zkxw),预签名改写后也是
- * `/minio/zkxw/...?X-Amz-...`。微信 downloadFile 必须绝对 http(s)。
- *
- * @param {string} url
- * @param {{ apiBaseUrl?: string, minioBaseUrl?: string }} [bases]
- */
- export function toAbsoluteDownloadUrl(url, bases = {}) {
- const s = String(url || '').trim()
- if (!s) return ''
- if (/^https?:\/\//i.test(s)) return s
- if (s.startsWith('//')) return 'https:' + s
- const apiBase = String(bases.apiBaseUrl || '').trim()
- const minioBase = String(bases.minioBaseUrl || '').trim()
- const originFrom = (base) => {
- const m = String(base || '').match(/^(https?:\/\/[^/]+)/i)
- return m ? m[1] : ''
- }
- const origin = originFrom(apiBase) || originFrom(minioBase)
- if (s.startsWith('/')) {
- return origin ? origin + s : ''
- }
- // 相对 object key
- const minio = minioBase.replace(/\/$/, '')
- if (/^https?:\/\//i.test(minio)) {
- return minio + '/' + s.replace(/^\//, '')
- }
- if (minio.startsWith('/') && origin) {
- return origin + minio + '/' + s.replace(/^\//, '')
- }
- return ''
- }
- /**
- * 拼 MinIO 公网直链(无预签名)。
- * 预签名经相对路径改写后微信 downloadFile 常 403;公网直链对本环境可 200。
- */
- export function buildDirectMinioUrl(fileUrl, bases = {}) {
- const raw = String(fileUrl || '').trim()
- if (!raw) return ''
- const minioBase = String(bases.minioBaseUrl || '').replace(/\/$/, '')
- const apiBase = String(bases.apiBaseUrl || '').trim()
- const originMatch = (apiBase || minioBase).match(/^(https?:\/\/[^/]+)/i)
- const origin = originMatch ? originMatch[1] : ''
- // 已是绝对 http(s):去掉 query(签名)后若可识别为同域 MinIO 则规范化,否则原样去 query
- if (/^https?:\/\//i.test(raw)) {
- const noQuery = raw.split('?')[0]
- try {
- const u = new URL(noQuery)
- const minioPath = minioBase.replace(/^https?:\/\/[^/]+/i, '') || ''
- if (minioPath && u.pathname.indexOf(minioPath) === 0) {
- return origin && minioPath.startsWith('/')
- ? origin + u.pathname
- : noQuery
- }
- } catch (_) {
- /* ignore */
- }
- return noQuery
- }
- // 根相对:/minio/zkxw/key.pdf?... → 拼 origin
- if (raw.startsWith('/')) {
- const pathOnly = raw.split('?')[0]
- return origin ? origin + pathOnly : toAbsoluteDownloadUrl(pathOnly, bases)
- }
- // object key:去掉可能误带的 minio 路径前缀
- let key = raw.replace(/^\//, '').split('?')[0]
- const minioPath = minioBase.replace(/^https?:\/\/[^/]+/i, '').replace(/^\//, '')
- if (minioPath && key.indexOf(minioPath + '/') === 0) {
- key = key.slice(minioPath.length + 1)
- }
- return toAbsoluteDownloadUrl(key, bases)
- }
|