优化generateTemplate方法,修复下载a链接无效,修复边界问题,修复跨空间引用变量

This commit is contained in:
wintsa 2026-03-19 17:22:50 +08:00
parent 241a6c4003
commit 68357babda
3 changed files with 246 additions and 26 deletions

View File

@ -62,6 +62,14 @@ type XmInfoLike = {
reviewedBy?: unknown
preparedDate?: unknown
projectIndustry?: unknown
preparedCompany?: unknown
overview?: unknown
desc?: unknown
}
type HtBaseInfoLike = {
quality?: unknown
duration?: unknown
}
interface ScaleRowLike {
@ -89,12 +97,26 @@ interface ZxFwRowLike {
id: string
process?: unknown
subtotal?: unknown
finalFee?: unknown
investScale?: unknown
landScale?: unknown
workload?: unknown
hourly?: unknown
}
interface WorkContentRowLike {
id?: unknown
content?: unknown
checked?: unknown
custom?: unknown
serviceGroup?: unknown
isAddTrigger?: unknown
}
interface WorkContentStateLike {
detailRows?: WorkContentRowLike[]
}
interface ZxFwStorageLike {
selectedIds?: string[]
selectedCodes?: string[]
@ -175,6 +197,7 @@ interface ExportScaleRow {
}
interface ExportMethod1Detail {
proNum: number
major: number
cost: number
basicFee: number
@ -191,6 +214,7 @@ interface ExportMethod1Detail {
}
interface ExportMethod1 {
proAmount: number
cost: number
basicFee: number
basicFee_basic: number
@ -200,6 +224,7 @@ interface ExportMethod1 {
}
interface ExportMethod2Detail {
proNum: number
major: number
area: number
basicFee: number
@ -216,6 +241,7 @@ interface ExportMethod2Detail {
}
interface ExportMethod2 {
proAmount: number
area: number
basicFee: number
basicFee_basic: number
@ -259,13 +285,20 @@ interface ExportMethod4 {
interface ExportService {
id: number
fee: number
finalFee: number
process: number
tasks: ExportTaskGroup[]
method1?: ExportMethod1
method2?: ExportMethod2
method3?: ExportMethod3
method4?: ExportMethod4
}
interface ExportTaskGroup {
serviceid?: number
text: string[]
}
interface ExportServiceCoe {
serviceid: number
coe: number
@ -284,6 +317,8 @@ interface ExportContract {
addtionalFee: number
reserveFee: number
fee: number
quality: string
duration: string
scale: ExportScaleRow[]
serviceCoes: ExportServiceCoe[]
majorCoes: ExportMajorCoe[]
@ -314,10 +349,11 @@ interface ExportMethod5 {
}
interface ExportAdditionalDetail {
id: number
id: number | string
code?: unknown
name: string
fee: number
tasks: ExportTaskGroup[]
m0?: ExportMethod0
m4?: ExportMethod4
m5?: ExportMethod5
@ -334,6 +370,7 @@ interface ExportReserve {
code?: unknown
name: string
fee: number
tasks: ExportTaskGroup[]
m0?: ExportMethod0
m4?: ExportMethod4
m5?: ExportMethod5
@ -343,10 +380,13 @@ interface ExportReportPayload {
name: string
writer: string
reviewer: string
company: string
date: string
industry: number
fee: number
scaleCost: number
overview: string
desc: string
scale: ExportScaleRow[]
serviceCoes: ExportServiceCoe[]
majorCoes: ExportMajorCoe[]
@ -1032,6 +1072,83 @@ const mapIndustryCodeToExportIndustry = (value: unknown): number => {
return 0
}
const parseScaleScopedRowId = (rowId: unknown) => {
const raw = String(rowId || '').trim()
const scopedMatch = /^(\d+)::(.+)$/.exec(raw)
if (scopedMatch) {
return {
proNum: toSafeInteger(scopedMatch[1]) ?? 1,
majorPart: String(scopedMatch[2] || '').trim()
}
}
return {
proNum: 1,
majorPart: raw
}
}
const toScaleMajorId = (row: ScaleMethodRowLike): number | null => {
const direct = toSafeInteger((row as { majorDictId?: unknown }).majorDictId)
if (direct != null) return direct
const parsed = parseScaleScopedRowId(row.id)
return toSafeInteger(parsed.majorPart)
}
const toScaleProNum = (row: ScaleMethodRowLike): number => {
const parsed = parseScaleScopedRowId(row.id)
return parsed.proNum > 0 ? parsed.proNum : 1
}
const normalizeTaskText = (value: unknown): string => String(value || '').trim()
const groupWorkContentTasks = (
rows: WorkContentRowLike[] | undefined,
options?: { forceUngroup?: boolean; serviceLabelToId?: Map<string, number> }
): ExportTaskGroup[] => {
const source = Array.isArray(rows) ? rows : []
const selected = source.filter(item => {
if (item && item.isAddTrigger === true) return false
const isCustom = Boolean(item?.custom)
const isChecked = Boolean(item?.checked)
return isCustom || isChecked
})
if (selected.length === 0) return []
const hasGroup = !options?.forceUngroup && selected.some(item => normalizeTaskText(item?.serviceGroup).length > 0)
if (!hasGroup) {
const text = selected
.map(item => normalizeTaskText(item?.content))
.filter(Boolean)
return text.length > 0 ? [{ text }] : []
}
const grouped = new Map<string, string[]>()
const orderedGroupKeys: string[] = []
for (const item of selected) {
const groupName = normalizeTaskText(item?.serviceGroup)
const key = groupName || '__ungrouped__'
if (!grouped.has(key)) {
grouped.set(key, [])
orderedGroupKeys.push(key)
}
const content = normalizeTaskText(item?.content)
if (!content) continue
grouped.get(key)?.push(content)
}
const byLabel = options?.serviceLabelToId || new Map<string, number>()
return orderedGroupKeys
.map(groupName => {
const text = grouped.get(groupName) || []
if (text.length === 0) return null
const entry: ExportTaskGroup = { text }
const resolvedServiceId = byLabel.get(groupName)
if (resolvedServiceId != null) entry.serviceid = resolvedServiceId
return entry
})
.filter((item): item is ExportTaskGroup => Boolean(item))
}
const buildProjectServiceCoes = (rows: FactorRowLike[] | undefined): ExportServiceCoe[] => {
if (!Array.isArray(rows)) return []
return rows
@ -1084,10 +1201,13 @@ const toExportScaleRows = (rows: ScaleRowLike[] | undefined): ExportScaleRow[] =
const buildMethod1 = (rows: ScaleMethodRowLike[] | undefined): ExportMethod1 | null => {
if (!Array.isArray(rows)) return null
let hasTotalValue = false
const proSet = new Set<number>()
const det = rows
.map(row => {
const major = toSafeInteger(row.id)
const major = toScaleMajorId(row)
if (major == null || row.budgetFee == null) return null
const proNum = toScaleProNum(row)
proSet.add(proNum)
const cost = toFiniteNumber(row.amount)
const basicFee = toFiniteNumber(row.budgetFee)
if (basicFee != null) hasTotalValue = true
@ -1102,6 +1222,7 @@ const buildMethod1 = (rows: ScaleMethodRowLike[] | undefined): ExportMethod1 | n
isNonEmptyString(remark)
if (!hasValue) return null
return {
proNum,
major,
cost: cost ?? 0,
basicFee: basicFee ?? 0,
@ -1121,6 +1242,7 @@ const buildMethod1 = (rows: ScaleMethodRowLike[] | undefined): ExportMethod1 | n
if (det.length === 0 || !hasTotalValue) return null
return {
proAmount: proSet.size > 0 ? proSet.size : 1,
cost: sumNumbers(det.map(item => item.cost)),
basicFee: sumNumbers(det.map(item => item.basicFee)),
basicFee_basic: sumNumbers(det.map(item => item.basicFee_basic)),
@ -1133,10 +1255,13 @@ const buildMethod1 = (rows: ScaleMethodRowLike[] | undefined): ExportMethod1 | n
const buildMethod2 = (rows: ScaleMethodRowLike[] | undefined): ExportMethod2 | null => {
if (!Array.isArray(rows)) return null
let hasTotalValue = false
const proSet = new Set<number>()
const det = rows
.map(row => {
const major = toSafeInteger(row.id)
const major = toScaleMajorId(row)
if (major == null || row.budgetFee == null) return null
const proNum = toScaleProNum(row)
proSet.add(proNum)
const area = toFiniteNumber(row.landArea)
const basicFee = toFiniteNumber(row.budgetFee)
if (basicFee != null) hasTotalValue = true
@ -1151,6 +1276,7 @@ const buildMethod2 = (rows: ScaleMethodRowLike[] | undefined): ExportMethod2 | n
isNonEmptyString(remark)
if (!hasValue) return null
return {
proNum,
major,
area: area ?? 0,
basicFee: basicFee ?? 0,
@ -1170,6 +1296,7 @@ const buildMethod2 = (rows: ScaleMethodRowLike[] | undefined): ExportMethod2 | n
if (det.length === 0 || !hasTotalValue) return null
return {
proAmount: proSet.size > 0 ? proSet.size : 1,
area: sumNumbers(det.map(item => item.area)),
basicFee: sumNumbers(det.map(item => item.basicFee)),
basicFee_basic: sumNumbers(det.map(item => item.basicFee_basic)),
@ -1353,6 +1480,50 @@ const loadHtFeeMethodsByRow = async (mainStorageKey: string, rowId: string) => {
}
}
const buildServiceGroupLabelToIdMap = (serviceIds: string[]): Map<string, number> => {
const map = new Map<string, number>()
for (const serviceId of serviceIds) {
const item = (serviceList as Record<string, any>)[serviceId]
if (!item) continue
const id = toSafeInteger(serviceId)
if (id == null) continue
const label = `${String(item.code || '').trim()} ${String(item.name || '').trim()}`.trim()
if (!label) continue
map.set(label, id)
}
return map
}
const buildServiceTasks = async (
contractId: string,
serviceId: string,
serviceLabelToId: Map<string, number>
): Promise<ExportTaskGroup[]> => {
const taskState = await zxFwPricingStore.loadKeyState<WorkContentStateLike>(`work-content-${contractId}-${serviceId}`)
return groupWorkContentTasks(taskState?.detailRows, { serviceLabelToId })
}
const buildAdditionalRowTasks = async (contractId: string, rowId: string): Promise<ExportTaskGroup[]> => {
const taskStorageKey = `work-content-htExtraFee-${contractId}-additional-work-${rowId}`
const taskState = await zxFwPricingStore.loadKeyState<WorkContentStateLike>(taskStorageKey)
return groupWorkContentTasks(taskState?.detailRows, { forceUngroup: true })
}
const buildServiceFinalFee = (
row: ZxFwRowLike | null | undefined,
method1: ExportMethod1 | null,
method2: ExportMethod2 | null,
method3: ExportMethod3 | null,
method4: ExportMethod4 | null
) => {
const finalFee = toFiniteNumber(row?.finalFee)
if (finalFee != null) return finalFee
const subtotal = toFiniteNumber(row?.subtotal)
if (subtotal != null) return subtotal
const methodSum = sumNumbers([method1?.fee, method2?.fee, method3?.fee, method4?.fee])
return methodSum
}
const buildAdditionalExport = async (contractId: string): Promise<ExportAdditional | null> => {
@ -1363,15 +1534,17 @@ const buildAdditionalExport = async (contractId: string): Promise<ExportAddition
const det = (
await Promise.all(
rows.map(async (row, index) => {
rows.map(async row => {
const methodPayload = await loadHtFeeMethodsByRow(storageKey, row.id)
if (!methodPayload) return null
const tasks = await buildAdditionalRowTasks(contractId, row.id)
const item: ExportAdditionalDetail = {
id: index,
id: row.id,
code: { richText: [{ font: { charset: 134, color: { theme: 1 }, italic: true, name: '宋体', size: 10 }, text: 'C' }, { font: { charset: 134, color: { theme: 1 }, italic: true, name: 'Calibri', size: 10, vertAlign: 'subscript' }, text: 'F' }] },
name: row.name,
fee: methodPayload.fee
fee: methodPayload.fee,
tasks
}
if (methodPayload.m0) item.m0 = methodPayload.m0
if (methodPayload.m4) item.m4 = methodPayload.m4
@ -1402,7 +1575,8 @@ const buildReserveExport = async (contractId: string): Promise<ExportReserve | n
if (!methodPayload) continue
const reserve: ExportReserve = {
name: row.name || '预备费',
fee: methodPayload.fee
fee: methodPayload.fee,
tasks: []
}
if (methodPayload.m0) reserve.m0 = methodPayload.m0
if (methodPayload.m4) reserve.m4 = methodPayload.m4
@ -1436,8 +1610,11 @@ const buildExportReportPayload = async (): Promise<ExportReportPayload> => {
const projectName = isNonEmptyString(projectInfo.projectName) ? projectInfo.projectName.trim() : '造价项目'
const writer = isNonEmptyString(projectInfo.preparedBy) ? projectInfo.preparedBy.trim() : ''
const reviewer = isNonEmptyString(projectInfo.reviewedBy) ? projectInfo.reviewedBy.trim() : ''
const company = isNonEmptyString(projectInfo.preparedCompany) ? projectInfo.preparedCompany.trim() : ''
const date = isNonEmptyString(projectInfo.preparedDate) ? projectInfo.preparedDate.trim() : ''
const industry = mapIndustryCodeToExportIndustry(projectInfo.projectIndustry)
const overview = isNonEmptyString(projectInfo.overview) ? projectInfo.overview.trim() : ''
const desc = isNonEmptyString(projectInfo.desc) ? projectInfo.desc.trim() : ''
const contractCards = (Array.isArray(contractCardsRaw) ? contractCardsRaw : [])
.filter(item => item && typeof item.id === 'string')
@ -1449,11 +1626,12 @@ const buildExportReportPayload = async (): Promise<ExportReportPayload> => {
const contract = contractCards[index]
const contractId = contract.id
await zxFwPricingStore.loadContract(contractId)
const [htInfoRaw, zxFwRaw, htConsultCategoryFactorRaw, htMajorFactorRaw] = await Promise.all([
const [htInfoRaw, zxFwRaw, htConsultCategoryFactorRaw, htMajorFactorRaw, htBaseInfoRaw] = await Promise.all([
kvStore.getItem<DetailRowsStorageLike<ScaleRowLike>>(`ht-info-v3-${contractId}`),
kvStore.getItem<ZxFwStorageLike>(`zxFW-${contractId}`),
kvStore.getItem<DetailRowsStorageLike<FactorRowLike>>(`ht-consult-category-factor-v1-${contractId}`),
kvStore.getItem<DetailRowsStorageLike<FactorRowLike>>(`ht-major-factor-v1-${contractId}`)
kvStore.getItem<DetailRowsStorageLike<FactorRowLike>>(`ht-major-factor-v1-${contractId}`),
kvStore.getItem<HtBaseInfoLike>(`ht-base-info-${contractId}`)
])
const contractState = zxFwPricingStore.getContractState(contractId)
@ -1462,6 +1640,7 @@ const buildExportReportPayload = async (): Promise<ExportReportPayload> => {
id: String(row.id || ''),
process: row.process,
subtotal: row.subtotal,
finalFee: row.finalFee,
investScale: row.investScale,
landScale: row.landScale,
workload: row.workload,
@ -1494,6 +1673,7 @@ const buildExportReportPayload = async (): Promise<ExportReportPayload> => {
const serviceIdTexts = sortServiceIdsByDict(
(selectedIds.length > 0 ? selectedIds : fallbackServiceIds).filter(hasServiceId)
)
const serviceLabelToId = buildServiceGroupLabelToIdMap(serviceIdTexts)
const services = (
await Promise.all(
@ -1518,11 +1698,15 @@ const buildExportReportPayload = async (): Promise<ExportReportPayload> => {
const method3 = buildMethod3(method3Raw?.detailRows)
const method4 = buildMethod4(method4Raw?.detailRows)
const fee = buildServiceFee(sourceRow, method1, method2, method3, method4)
const finalFee = buildServiceFinalFee(sourceRow, method1, method2, method3, method4)
const tasks = await buildServiceTasks(contractId, serviceIdText, serviceLabelToId)
const process = Number(sourceRow?.process) === 1 ? 1 : 0
const service: ExportService = {
id: serviceId,
process,
fee
fee,
finalFee,
tasks
}
if (method1) service.method1 = method1
if (method2) service.method2 = method2
@ -1564,6 +1748,8 @@ const buildExportReportPayload = async (): Promise<ExportReportPayload> => {
addtionalFee,
reserveFee,
fee: contractFee,
quality: isNonEmptyString(htBaseInfoRaw?.quality) ? htBaseInfoRaw.quality.trim() : '',
duration: isNonEmptyString(htBaseInfoRaw?.duration) ? htBaseInfoRaw.duration.trim() : '',
scale: contractScale,
serviceCoes: contractServiceCoesRaw,
majorCoes: contractMajorCoesRaw,
@ -1577,10 +1763,13 @@ const buildExportReportPayload = async (): Promise<ExportReportPayload> => {
name: projectName,
writer,
reviewer,
company,
date,
industry,
fee: sumNumbers(contracts.map(item => item.fee)),
scaleCost: projectScaleCost,
overview,
desc,
scale: projectScale,
serviceCoes: projectServiceCoes,
majorCoes: projectMajorCoes,

View File

@ -9,7 +9,7 @@ import {
PinnedRowModule,
RowAutoHeightModule,
TextEditorModule,
TooltipModule,
TooltipModule,ClientSideRowModelApiModule ,
UndoRedoEditModule,RenderApiModule ,ColumnApiModule ,CellSpanModule ,RowStyleModule ,RowSelectionModule
} from 'ag-grid-community'
@ -39,7 +39,7 @@ const AG_GRID_MODULES = [
RowAutoHeightModule,ContextMenuModule,
LargeTextEditorModule,
UndoRedoEditModule,
CellStyleModule,
CellStyleModule,ClientSideRowModelApiModule ,
PinnedRowModule,RenderApiModule ,ColumnApiModule ,
TooltipModule,
TreeDataModule,

View File

@ -11,6 +11,34 @@ const toFiniteNumber = (value: unknown) => {
const num = Number(value)
return Number.isFinite(num) ? num : 0
}
// 兼容导出 tasks 对象结构:[{ text: [] }, { serviceid, text: [] }]
const normalizeTaskTexts = (tasks: unknown): string[] => {
if (!Array.isArray(tasks)) return [];
const result: string[] = [];
const seen = new Set<string>();
const pushText = (value: unknown) => {
const text = String(value ?? '').trim();
if (!text) return;
if (seen.has(text)) return;
seen.add(text);
result.push(text);
};
tasks.forEach(item => {
if (typeof item === 'string') {
pushText(item);
return;
}
if (!item || typeof item !== 'object') return;
const textField = item.text;
if (Array.isArray(textField)) {
textField.forEach(pushText);
return;
}
pushText(textField);
});
return result;
}
export type WorkType = '基本工作' | '可选工作' | '日常顾问' | '专项顾问' | '附加工作' | '自定义'
export const TYPE_LABEL_MAP: Record<number, WorkType> = {
@ -797,7 +825,7 @@ export async function exportFile(fileName: string, data: any | (() => Promise<an
const blob = new Blob([buffer], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
const url1 = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.hcode = url1;
a.href = url1;
a.download = `${fileName}.xlsx`;
a.click();
URL.revokeObjectURL(url1);
@ -812,7 +840,6 @@ export async function exportFile(fileName: string, data: any | (() => Promise<an
async function generateTemplate(data) {
// data.contracts[0].services[0].tasks = [{ text: '依据本项目在招标阶段确认的设计图纸工程量,结合工程量清单计量支付规则,对设计工程量进行复核,包括构件工程量、明细表工程量和汇总表工程量,同时与相关方核对工程量', '依据核对后确认的设计图纸数量,细化与合并招标工程量清单或合同工程量清单,建立各维度清单间的数据链接,与相关单位完成核对与确认', '依据确定的招标工程量清单或合同工程量清单,拆解与合并相应的清单费用,与相关单位完成核对与确认', '现场勘查与测量现场实施工程量'}];
// const downTextTmp = { richText: [{ font: { charset: 134, color: { theme: 1 }, italic: true, name: '宋体', size: 10 }, text: '常规' }, { font: { charset: 134, color: { theme: 1 }, italic: true, name: 'Calibri', size: 10, vertAlign: 'subscript' }, text: '下标' }] };
console.log(data)
// 编制说明 → 工作内容的前后默认项
let prefixIDs = [6, 7, 8, 9, 10, 11, 12, 13, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27];
let suffixIDs = [6, 7, 8, 9, 10, 11, 12, 13, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27];
@ -2220,8 +2247,9 @@ async function generateTemplate(data) {
ciTastNum++;
});
});
if (si.tasks?.length) {
si.tasks.forEach((sti, stindex) => {
const serviceTaskTexts = normalizeTaskTexts(si.tasks);
if (serviceTaskTexts.length) {
serviceTaskTexts.forEach((sti, stindex) => {
let stiTextArr = paragraphLineBreakFor1112(` ${stindex + 1}${sti}`, ctx);
stiTextArr.forEach(ti => {
cusInsertRowFunc(descRowNum, [descSheet.getRow(descRowNum - 1)], descSheet, (targetRow) => {
@ -2244,8 +2272,9 @@ async function generateTemplate(data) {
targetRow.getCell(1).value = ` ${ciTastNum}${ai.id == 1 ? '负责协调工作,具体工作内容包括:' : '其他附加工作,具体工作内容包括:'}`;
ciTastNum++;
});
if (ai.tasks?.length) {
ai.tasks.forEach((ati, atindex) => {
const additionalTaskTexts = normalizeTaskTexts(ai.tasks);
if (additionalTaskTexts.length) {
additionalTaskTexts.forEach((ati, atindex) => {
let atiTextArr = paragraphLineBreakFor1112(` ${atindex + 1}${ati}`, ctx);
atiTextArr.forEach(ti => {
cusInsertRowFunc(descRowNum, [descSheet.getRow(descRowNum - 1)], descSheet, (targetRow) => {
@ -2287,7 +2316,6 @@ async function generateTemplate(data) {
let descRowNum1 = descRowNum + 1;
let descRowNum2 = descRowNum + 2;
let descRowNum3 = descRowNum + 2;
console.log( data.contracts)
data.contracts.forEach((ci, cindex) => {
descRowNum3 = descRowNum3 - descRowNum2;
descRowNum2 = descRowNum2 - descRowNum1 - 1;
@ -2369,8 +2397,9 @@ async function generateTemplate(data) {
ciTastNum++;
});
});
if (si.tasks?.length) {
si.tasks.forEach((sti, stindex) => {
const serviceTaskTexts = normalizeTaskTexts(si.tasks);
if (serviceTaskTexts.length) {
serviceTaskTexts.forEach((sti, stindex) => {
let stiTextArr = paragraphLineBreakFor1112(` ${stindex + 1}${sti}`, ctx);
stiTextArr.forEach(ti => {
cusInsertRowFunc(descRowNum3, [descSheet.getRow(descRowNum3 - 1)], descSheet, (targetRow) => {
@ -2393,8 +2422,9 @@ async function generateTemplate(data) {
targetRow.getCell(1).value = ` ${ciTastNum}${ai.id == 1 ? '负责协调工作,具体工作内容包括:' : '其他附加工作,具体工作内容包括:'}`;
ciTastNum++;
});
if (ai.tasks?.length) {
ai.tasks.forEach((ati, atindex) => {
const additionalTaskTexts = normalizeTaskTexts(ai.tasks);
if (additionalTaskTexts.length) {
additionalTaskTexts.forEach((ati, atindex) => {
let atiTextArr = paragraphLineBreakFor1112(` ${atindex + 1}${ati}`, ctx);
atiTextArr.forEach(ti => {
cusInsertRowFunc(descRowNum3, [descSheet.getRow(descRowNum3 - 1)], descSheet, (targetRow) => {
@ -2490,9 +2520,10 @@ async function generateTemplate(data) {
});
}
}
if (data.desc) {
if (String(data.desc || '').trim()) {
descRowNum++;
let otherDesc = paragraphLineBreakFor1112(` ${data.desc}${/$/.test(ci.duration) ? '' : '。'}`, ctx);
const otherDescText = String(data.desc || '').trim();
let otherDesc = paragraphLineBreakFor1112(` ${otherDescText}${/$/.test(otherDescText) ? '' : '。'}`, ctx);
descSheet.getRow(descRowNum).getCell(1).value = otherDesc[0];
descRowNum++;
if (otherDesc.length > 1) {
@ -2742,4 +2773,4 @@ function ArabicToChinese(Arabic_numerals) {
return chineseNumerals[parseInt(match)];
});
return mixNumerals;
}
}