|
@@ -2,6 +2,12 @@ import { cloneDeep } from 'lodash'
|
|
|
import { exportWordDocx, getBase64Sync } from './exportWord'
|
|
|
import { baseURL } from './http'
|
|
|
import { resJiLianFu } from './history'
|
|
|
+import {
|
|
|
+ arrangeImages,
|
|
|
+ calculateRowCharLines,
|
|
|
+ numberToChinese,
|
|
|
+ removeHtmlTags
|
|
|
+} from './exportWordUtils'
|
|
|
|
|
|
export enum EXPORT_WORD_ENUM {
|
|
|
/** 借用藏品点交凭证 */
|
|
@@ -20,13 +26,6 @@ export enum EXPORT_WORD_ENUM {
|
|
|
COLLECTION_CARD = 7
|
|
|
}
|
|
|
|
|
|
-type WallItem = {
|
|
|
- url: string
|
|
|
- width: number
|
|
|
- height: number
|
|
|
- img: string
|
|
|
-}
|
|
|
-
|
|
|
const WORD_FILE_NAME_MAP = {
|
|
|
[EXPORT_WORD_ENUM.BORROW]: {
|
|
|
fileName: '义乌市博物馆借用藏品点交凭证',
|
|
@@ -44,11 +43,11 @@ const WORD_FILE_NAME_MAP = {
|
|
|
},
|
|
|
row: {
|
|
|
// 首页最大行数
|
|
|
- maxRowFirstPage: 1,
|
|
|
+ maxRowFirstPage: 3,
|
|
|
// 尾页最大行数
|
|
|
- maxRowLastPage: 1,
|
|
|
+ maxRowLastPage: 6,
|
|
|
// 每页最大行数
|
|
|
- maxRowPage: 1,
|
|
|
+ maxRowPage: 8,
|
|
|
// 首页单行最大字符行数
|
|
|
maxFirstCharLine: 20,
|
|
|
// 尾页单行最大字符行数
|
|
@@ -56,7 +55,7 @@ const WORD_FILE_NAME_MAP = {
|
|
|
// 每页最大字符行数
|
|
|
maxCharLine: 26,
|
|
|
// 表格每行最小字符行数
|
|
|
- minRowCharLine: 2
|
|
|
+ minRowCharLine: 3
|
|
|
}
|
|
|
},
|
|
|
[EXPORT_WORD_ENUM.SUB_PUT_BACK]: {
|
|
@@ -81,172 +80,6 @@ const WORD_FILE_NAME_MAP = {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-export const numberToChinese = (num: number) => {
|
|
|
- if (isNaN(num)) {
|
|
|
- throw new Error('输入必须是一个有效的数字')
|
|
|
- }
|
|
|
- if (num < 0 || num > 9999) {
|
|
|
- throw new Error('输入数字超出范围 (0-9999)')
|
|
|
- }
|
|
|
- if (num === 0) {
|
|
|
- return '零'
|
|
|
- }
|
|
|
-
|
|
|
- const chineseNumbers = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖']
|
|
|
- const chineseUnits = ['', '拾', '佰', '仟']
|
|
|
-
|
|
|
- let result = ''
|
|
|
- const numStr = num.toString()
|
|
|
- const length = numStr.length
|
|
|
-
|
|
|
- for (let i = 0; i < length; i++) {
|
|
|
- const digit = parseInt(numStr[i])
|
|
|
- const unit = length - i - 1
|
|
|
-
|
|
|
- if (digit !== 0) {
|
|
|
- result += chineseNumbers[digit] + chineseUnits[unit]
|
|
|
- } else {
|
|
|
- // 处理连续的零,只保留一个
|
|
|
- if (i < length - 1 && numStr[i + 1] !== '0') {
|
|
|
- result += chineseNumbers[digit]
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // 处理10-19的情况,去掉开头的"壹"
|
|
|
- if (num >= 10 && num < 20) {
|
|
|
- result = result.replace('壹拾', '拾')
|
|
|
- }
|
|
|
-
|
|
|
- return result
|
|
|
-}
|
|
|
-
|
|
|
-const getImageDimensions = (url: string): Promise<{ width: number; height: number }> => {
|
|
|
- return new Promise(resolve => {
|
|
|
- const img = new Image()
|
|
|
- img.onload = () => {
|
|
|
- resolve({ width: img.width, height: img.height })
|
|
|
- }
|
|
|
- img.onerror = () => {
|
|
|
- resolve({ width: 100, height: 100 })
|
|
|
- }
|
|
|
- img.src = url
|
|
|
- })
|
|
|
-}
|
|
|
-
|
|
|
-/**
|
|
|
- * 将一维数组根据图片宽高比转成三维数组
|
|
|
- */
|
|
|
-const arrangeImages = async (images: { thumb: string }[]) => {
|
|
|
- const MAX_WALL_WIDTH = 520
|
|
|
- const MAX_WALL_HEIGHT = 750
|
|
|
- const MAX_ROWS_PER_WALL = 3
|
|
|
-
|
|
|
- const walls: Array<Array<Array<WallItem>>> = []
|
|
|
-
|
|
|
- let currentWall: Array<Array<WallItem>> = []
|
|
|
- let currentRow: Array<WallItem> = []
|
|
|
-
|
|
|
- let currentRowHeight = 0
|
|
|
- let currentX = 0
|
|
|
-
|
|
|
- const imagesWithDimensions = await Promise.all(
|
|
|
- images.map(async img => {
|
|
|
- const url = baseURL + img.thumb
|
|
|
- const dimensions = await getImageDimensions(url)
|
|
|
- return {
|
|
|
- url,
|
|
|
- originalWidth: dimensions.width,
|
|
|
- originalHeight: dimensions.height
|
|
|
- }
|
|
|
- })
|
|
|
- )
|
|
|
-
|
|
|
- for (const img of imagesWithDimensions) {
|
|
|
- const maxRowHeight = MAX_WALL_HEIGHT / MAX_ROWS_PER_WALL
|
|
|
- const aspectRatio = img.originalWidth / img.originalHeight
|
|
|
-
|
|
|
- let scaledWidth, scaledHeight
|
|
|
-
|
|
|
- scaledHeight = maxRowHeight
|
|
|
- scaledWidth = maxRowHeight * aspectRatio
|
|
|
-
|
|
|
- if (scaledWidth > MAX_WALL_WIDTH) {
|
|
|
- scaledWidth = MAX_WALL_WIDTH
|
|
|
- scaledHeight = MAX_WALL_WIDTH / aspectRatio
|
|
|
- }
|
|
|
-
|
|
|
- // TOFIX: 暂时无法解决图片并列渲染问题
|
|
|
- if (currentX + scaledWidth <= MAX_WALL_WIDTH && false) {
|
|
|
- currentRow.push({
|
|
|
- url: img.url,
|
|
|
- width: scaledWidth,
|
|
|
- height: scaledHeight,
|
|
|
- img: await getBase64Sync(img.url)
|
|
|
- })
|
|
|
- currentX += scaledWidth
|
|
|
- currentRowHeight = Math.max(currentRowHeight, scaledHeight)
|
|
|
- } else {
|
|
|
- if (currentRow.length > 0) {
|
|
|
- // eslint-disable-next-line no-loop-func
|
|
|
- currentRow.forEach(item => {
|
|
|
- item.height = currentRowHeight
|
|
|
- })
|
|
|
- currentWall.push(currentRow)
|
|
|
- }
|
|
|
-
|
|
|
- if (currentWall.length >= MAX_ROWS_PER_WALL) {
|
|
|
- walls.push(currentWall)
|
|
|
- currentWall = []
|
|
|
- }
|
|
|
-
|
|
|
- currentRow = [
|
|
|
- {
|
|
|
- url: img.url,
|
|
|
- width: scaledWidth,
|
|
|
- height: scaledHeight,
|
|
|
- img: await getBase64Sync(img.url)
|
|
|
- }
|
|
|
- ]
|
|
|
- currentX = scaledWidth
|
|
|
- currentRowHeight = scaledHeight
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (currentRow.length > 0) {
|
|
|
- currentWall.push(currentRow)
|
|
|
- }
|
|
|
- if (currentWall.length > 0) {
|
|
|
- walls.push(currentWall)
|
|
|
- }
|
|
|
-
|
|
|
- return walls
|
|
|
-}
|
|
|
-
|
|
|
-const getEffectiveLength = (str: string) => {
|
|
|
- let length = 0
|
|
|
- for (const char of str) {
|
|
|
- // 全角字符(包括中文、全角符号等)的 Unicode 范围判断
|
|
|
- if (char.match(/[\u4e00-\u9fa5\u3000-\u303f\uff00-\uffef]/)) {
|
|
|
- length += 2 // 全角算2字符
|
|
|
- } else {
|
|
|
- length += 1 // 半角算1字符
|
|
|
- }
|
|
|
- }
|
|
|
- return length
|
|
|
-}
|
|
|
-
|
|
|
-const calculateRowCharLines = (row: Record<string, string>, perLine: Record<string, number>) => {
|
|
|
- let maxCharLines = 0
|
|
|
- for (const [field, text] of Object.entries(row)) {
|
|
|
- if (!perLine[field]) continue
|
|
|
- const fieldLength = getEffectiveLength(text)
|
|
|
- const fieldCharLines = Math.ceil(fieldLength / perLine[field])
|
|
|
- maxCharLines = Math.max(maxCharLines, fieldCharLines)
|
|
|
- }
|
|
|
- return maxCharLines || 1
|
|
|
-}
|
|
|
-
|
|
|
/**
|
|
|
* 计算表格数据的分页情况
|
|
|
*/
|
|
@@ -263,7 +96,7 @@ const calcTablePages = (rows: any[], config: (typeof WORD_FILE_NAME_MAP)[2]) =>
|
|
|
const row = rows[i]
|
|
|
const rowCharLines = calculateRowCharLines(row, config.perLine)
|
|
|
|
|
|
- currentPageCharLines += rowCharLines
|
|
|
+ currentPageCharLines += Math.max(rowCharLines, config.row.minRowCharLine)
|
|
|
currentPageRows.push(row)
|
|
|
|
|
|
// 判断是否超出首页
|
|
@@ -326,7 +159,7 @@ export const exportWordHandler = async (type: EXPORT_WORD_ENUM, data: Record<any
|
|
|
for (let i = 0; i < temp.goods.length; i++) {
|
|
|
const good = temp.goods[i]
|
|
|
good.index = i + 1
|
|
|
- good.rtf && (good.rtf = JSON.parse(good.rtf).txtArr[0].txt)
|
|
|
+ good.rtf && (good.rtf = removeHtmlTags(JSON.parse(good.rtf).txtArr[0].txt))
|
|
|
good.thumb && (good.thumb = await getBase64Sync(baseURL + good.thumb))
|
|
|
good.dictAge && (good.dictAge = resJiLianFu(good.dictAge))
|
|
|
good.pcsUnit && (good.pcsUnit = resJiLianFu(good.pcsUnit))
|
|
@@ -342,6 +175,7 @@ export const exportWordHandler = async (type: EXPORT_WORD_ENUM, data: Record<any
|
|
|
if (item.perLine) {
|
|
|
// @ts-ignore
|
|
|
page = calcTablePages(temp.goods, item)
|
|
|
+ console.log(page)
|
|
|
}
|
|
|
|
|
|
switch (type) {
|