吉安感知网项目-前端
罗广辉
9 hours ago 5f694550ed7bf0f6147c66feaa4406034ae62e54
packages/utils/common/index.js
@@ -12,9 +12,40 @@
   return rules
}
const isContactPhone = value => {
   const mobile = /^1[3-9]\d{9}$/
   const landline = /^0\d{2,3}-?\d{7,8}$/
   return mobile.test(value) || landline.test(value)
}
export function getDictLabel(value, dictList) {
   return dictList.find(item => item.dictKey === value)?.dictValue || value || '-'
export const contactPhoneRules = (required = true, max = 50) => {
   const validateContactPhone = (rule, value, callback) => {
      const nextValue = String(value || '').trim()
      if (!nextValue) return callback()
      if (isContactPhone(nextValue)) return callback()
      return callback(new Error('联系电话格式不正确'))
   }
   return [...fieldRules(required, max), { validator: validateContactPhone, trigger: ['blur', 'change'] }]
}
export function getDictLabel(value, dictList = []) {
   if (value === null || value === undefined || value === '') return '';
   const values = String(value).split(',');
   return dictList
      .filter(item => values.includes(String(item.dictKey)))
      .map(item => item.dictValue)
      .join(',');
}
// id转label xt
export function getDictLabelXT(value, dictList = []) {
   if (value === null || value === undefined || value === '') return '';
   const values = String(value).split(',');
   return dictList
      .filter(item => values.includes(String(item.dictValue)))
      .map(item => item.dictLabel)
      .join(',');
}
export function blobDownload(res) {
@@ -30,3 +61,120 @@
   elink.click()
   document.body.removeChild(elink)
}
export function geomAnalysis(str) {
   if (!str) return []
   return str
      .replace('POLYGON((', '')
      .replace('))', '')
      .split(',')
      .map(pair => {
         const [longitude, latitude] = pair.trim().split(' ').map(Number)
         return { longitude, latitude }
      })
}
// 替换图片请求代理地址
export function replaceWithProxy(url) {
   if (!url) return ''
   return url.replace(
      /^https?:\/\/[^/]+/,
      'https://wrj.shuixiongit.com/ja-proxy'
   )
}
// 加载图片并获取原始尺寸
function loadImage(url) {
   return new Promise((resolve, reject) => {
      const image = new Image()
      image.crossOrigin = 'anonymous'
      image.onload = () => {
         if (!image.naturalWidth || !image.naturalHeight) {
            reject(new Error('图片尺寸无效'))
            return
         }
         resolve(image)
      }
      image.onerror = () => reject(new Error('图片加载失败'))
      image.src = url
   })
}
// 在图片上绘制算法框
function renderAiFrame(image, aiFrame, scaleX = 1, scaleY = 1) {
   const canvas = document.createElement('canvas')
   const ctx = canvas.getContext('2d')
   if (!ctx) return ''
   canvas.width = image.naturalWidth
   canvas.height = image.naturalHeight
   ctx.drawImage(image, 0, 0, canvas.width, canvas.height)
   aiFrame.forEach(item => {
      const { x_cen, y_cen, width, height } = item.bbox || {}
      if ([x_cen, y_cen, width, height].some(value => typeof value !== 'number')) return
      const scaledWidth = width * scaleX
      const scaledHeight = height * scaleY
      const x = x_cen * scaleX - scaledWidth / 2
      const y = y_cen * scaleY - scaledHeight / 2
      const label = item.class_name || ''
      const fontSize = Math.max(18, Math.round(canvas.width / 80))
      const labelHeight = fontSize + 10
      const labelY = y - labelHeight >= 0 ? y - labelHeight : y
      ctx.strokeStyle = '#FF3B30'
      ctx.lineWidth = Math.max(3, Math.round(canvas.width / 640))
      ctx.strokeRect(x, y, scaledWidth, scaledHeight)
      if (label) {
         ctx.font = `${fontSize}px Arial`
         const labelWidth = ctx.measureText(label).width + 16
         ctx.fillStyle = '#FF3B30'
         ctx.fillRect(x, labelY, labelWidth, labelHeight)
         ctx.fillStyle = '#FFFFFF'
         ctx.textBaseline = 'middle'
         ctx.fillText(label, x + 8, labelY + labelHeight / 2)
      }
   })
   try {
      return canvas.toDataURL('image/jpeg', 0.92)
   } catch {
      return ''
   }
}
// 异步生成算法成果图片
export async function appendAiImages(item) {
   try {
      const aiFrame = JSON.parse(item.geojson)
      const resultImagePromise = loadImage(item.resultUrl)
      const thumbnailImagePromise = loadImage(item.thumbnail).catch(() => null)
      const resultImage = await resultImagePromise
      item.resultUrl = renderAiFrame(resultImage, aiFrame)
      const thumbnailImage = await thumbnailImagePromise
      if (!thumbnailImage) {
         item.thumbnail = item.resultUrl
         return
      }
      const scaleX = thumbnailImage.naturalWidth / resultImage.naturalWidth
      const scaleY = thumbnailImage.naturalHeight / resultImage.naturalHeight
      item.thumbnail = renderAiFrame(thumbnailImage, aiFrame, scaleX, scaleY)
   } catch {
      item.thumbnail = item.resultUrl
   }
}
// 图片转带ai框的图片
export async function drawAiFrame(url, aiFrameSource) {
   if (!url) return url
   if (!aiFrameSource) return url
   try {
      const aiFrame = JSON.parse(aiFrameSource)
      const image = await loadImage(url)
      return renderAiFrame(image, aiFrame)
   } catch {
      return ''
   }
}