fix(ms): 材料导入模板重建 + IIS子目录路径修复 + 隐藏分类数据列

- 按用户参考模板重建 imft.xlsx,保持表头/行结构一致
- fetch路径加 /iapip-ms/ 前缀适配 IIS 子目录部署
- 动态写入的分类数据列设为 hidden,用户下载时不可见

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T71oHHhfhLAWAL9EuoFuYh
This commit is contained in:
zty 2026-07-29 02:40:22 -04:00
parent 8ee0784f20
commit b26546d459
3 changed files with 273 additions and 2 deletions

View File

@ -191,8 +191,7 @@ const ImportMaterialsModal: React.FC<{
onClick={async () => {
setDownloading(true)
try {
const file = await fetch('/fts/imft.xlsx')
// const file = await fetch('/iapip-ms/fts/imft.xlsx')
const file = await fetch('/iapip-ms/fts/imft.xlsx')
const wb = new ExcelJS.Workbook()
await wb.xlsx.load(await file.arrayBuffer())
const ws = wb.getWorksheet(1)
@ -200,9 +199,11 @@ const ImportMaterialsModal: React.FC<{
const categories = await api.config.getMtrlCatList()
categories.forEach((fc, i) => {
ws.getCell(1 + i, 43).value = fc.first_category
ws.getColumn(43).hidden = true
ws.getColumn(44 + i).values = fc.second_categories.map(
(sc) => sc.second_category
)
ws.getColumn(44 + i).hidden = true
})
}
await downloadXLSX(

View File

@ -0,0 +1,270 @@
import { ExcelValidator } from '@/common/excel-validator'
import { AnyObj, FROM_TYPE } from '@/common/types'
import { downloadXLSX, toNum } from '@/common/utils'
import api from '@/services/api'
import { ModalForm } from '@ant-design/pro-components'
import { Alert, Button, Divider, Tag, Upload, message, theme } from 'antd'
import { RcFile } from 'antd/es/upload'
import * as ExcelJS from 'exceljs'
import React, { useState } from 'react'
import ImportFile from '../apply-request-modal/import-file'
const ImportTemplatesModal: React.FC<{
title: string
trigger: JSX.Element
}> = ({ title, trigger }) => {
const { token } = theme.useToken()
const [open, setOpen] = useState(false)
const [downloading, setDownloading] = useState(false)
const [uploading, setUploading] = useState(false)
const [errMsg, setErrMsg] = useState<string>()
const [parsedResult, setParseResult] = useState<AnyObj[]>()
const [uploadFile, setUploadFile] = useState<RcFile>()
const beforeUpload = async (file: RcFile) => {
setUploading(true)
const msgKey = 'UPLOADING_TEMPLATES'
try {
message.open({
key: msgKey,
type: 'loading',
content: '正在解析'
})
const wb = new ExcelJS.Workbook()
await wb.xlsx.load(await file.arrayBuffer())
const ws = wb.getWorksheet(1)
if (ws) {
const templates: AnyObj[] = []
ws.getRows(6, 10)?.forEach((row, idx) => {
const id = row.getCell(1).value?.toString()
const name = row.getCell(2).value?.toString()
const category = row.getCell(3).value?.toString()
const province = row.getCell(4).value?.toString()
const city = row.getCell(5).value?.toString()
const constr_area = toNum(row.getCell(6).value?.toString())
if (id && name && category && province && city && constr_area) {
templates.push({
id,
idx: 6 + idx,
name,
category,
province,
city,
constr_area,
display: 1,
from: FROM_TYPE.IMPORT,
spaces: [] as AnyObj
})
}
})
ws.getRows(20, 500)?.forEach((row, idx) => {
const temp_id = row.getCell(2).value?.toString()
const name = row.getCell(5).value?.toString()
const category = row.getCell(6).value?.toString()
const area = toNum(row.getCell(7).value?.toString())
const volume = toNum(row.getCell(8).value?.toString())
const env_temp = toNum(row.getCell(9).value?.toString())
const env_hum = toNum(row.getCell(10).value?.toString())
const env_vent_rate = toNum(row.getCell(11).value?.toString())
const apc_lmt_std_id = row.getCell(12).value?.toString()
if (temp_id && name && category && area && volume && apc_lmt_std_id) {
templates.find((temp) => {
if (temp.id === temp_id) {
temp.spaces.push({
idx: 20 + idx,
name,
category,
layout: 0,
area,
volume,
env_temp,
env_hum,
env_vent_rate,
apc_lmt_std_id
})
return true
}
return false
})
}
})
const excelValidator = new ExcelValidator()
if (await excelValidator.importTemplates(templates)) {
message.open({
key: msgKey,
type: 'success',
content: '上传成功',
duration: 2
})
setErrMsg(undefined)
setParseResult(templates)
setUploadFile(file)
setUploading(false)
return file
}
setErrMsg(excelValidator.errorMessage)
}
} catch (err) {}
message.destroy(msgKey)
setUploading(false)
return false
}
return (
<ModalForm
open={open}
title={title}
width={690}
modalProps={{
maskClosable: false,
destroyOnClose: true,
styles: {
content: {
paddingTop: 20,
paddingBottom: 16
}
},
onCancel: () => setOpen(false)
}}
onOpenChange={(v) => {
if (!v) {
setParseResult(undefined)
setUploadFile(undefined)
}
}}
submitter={{
render: () => (
<div className="w-full flex pt-1 justify-center space-x-4">
{!parsedResult ? (
<>
<Button
style={{ width: 120 }}
loading={downloading}
onClick={async () => {
setDownloading(true)
try {
const file = await fetch('/iapip-ms/fts/itft.xlsx')
const wb = new ExcelJS.Workbook()
await wb.xlsx.load(await file.arrayBuffer())
const ws = wb.getWorksheet(1)
if (ws) {
const categories = await api.config.getProjSpaceCatList()
categories.forEach((pc, i) => {
ws.getCell(1 + i, 19).value = pc.project_category
ws.getColumn(20 + i).values = pc.space_categories.map(
(sc) => sc.space_category
)
})
const cities = await api.config.getCityInfoList()
cities.forEach((p, i) => {
ws.getCell(1 + i, 56).value = p.province
ws.getColumn(57 + i).values = p.cities.map((c) => c.city)
})
}
await downloadXLSX(
await wb.xlsx.writeBuffer(),
'批量导入模板(文件模板).xlsx'
)
} catch (err) {}
setDownloading(false)
}}
>
{downloading ? '正在下载' : '下载文件模板'}
</Button>
<Upload accept=".xlsx" showUploadList={false} beforeUpload={beforeUpload}>
<Button type="primary" style={{ width: 120 }} disabled={uploading}>
</Button>
</Upload>
</>
) : (
<>
<Upload accept=".xlsx" showUploadList={false} beforeUpload={beforeUpload}>
<Button style={{ width: 120 }} disabled={uploading}>
</Button>
</Upload>
<ImportFile
title="导入公共模板申请"
trigger={
<Button type="primary" style={{ width: 120 }}>
</Button>
}
onFinish={async (data) => {
if (uploadFile) {
if (parsedResult.length) {
try {
await api.audit.importTemplates({
file: uploadFile,
data: parsedResult,
qty: parsedResult.length,
desc: data.desc
})
message.success('申请成功')
setOpen(false)
return true
} catch (err) {}
} else {
message.error('至少包含一条解析数据')
}
} else {
message.error('找不到上传文件,请重新上传')
}
return false
}}
/>
</>
)}
</div>
)
}}
trigger={React.cloneElement(trigger, { onClick: () => setOpen(true) })}
>
<Divider style={{ marginTop: 16, marginBottom: 12 }} />
{errMsg ? <Alert message={errMsg} type="error" showIcon style={{ marginBottom: 12 }} /> : ''}
<div className="px-6 pt-3 pb-6 space-y-2">
<label></label>
<div style={{ color: token.colorTextSecondary }}>
1. ,
</div>
<div style={{ color: token.colorTextSecondary }}>
2. , ,
</div>
</div>
{parsedResult ? (
<>
<Divider style={{ margin: 0 }} />
<div className="p-6 space-y-2">
<div>
<label className="mr-4">:</label>
<Button
type="link"
style={{ padding: 0 }}
onClick={async () => {
if (uploadFile) {
await downloadXLSX(await uploadFile.arrayBuffer(), uploadFile.name)
}
}}
>
{uploadFile?.name ?? '下载'}
</Button>
</div>
<div>
<label className="mr-4">:</label>
,
</div>
<div>
<label className="mr-4">:</label>
<Tag style={{ marginInline: 5 }}>{parsedResult.length}</Tag>
</div>
</div>
</>
) : (
''
)}
<Divider style={{ margin: 0 }} />
</ModalForm>
)
}
export default ImportTemplatesModal