Files
department-api/routes/docs.js

167 lines
6.2 KiB
JavaScript
Raw Normal View History

2025-12-08 18:26:55 +08:00
const express = require('express');
const router = express.Router();
const path = require('path');
const fs = require('fs');
const sanitizeSegment = (seg) => String(seg || '').replace(/[^a-zA-Z0-9_\-]/g, '');
router.get('/list', (req, res) => {
try {
const series = sanitizeSegment(req.query.series);
const sub = sanitizeSegment(req.query.sub);
if (!series || !sub) {
return res.status(400).json({ error: '缺少必要参数 series 或 sub' });
}
const modelsDocsDir = path.join(__dirname, '..', 'uploads', 'models', series, sub, 'docs');
const legacyDocsDir = path.join(__dirname, '..', 'uploads', 'docs', series, sub);
const docsDir = fs.existsSync(modelsDocsDir) ? modelsDocsDir : legacyDocsDir;
if (!fs.existsSync(docsDir)) {
return res.json({ success: true, data: [] });
}
const entries = fs.readdirSync(docsDir, { withFileTypes: true });
const files = entries
.filter((e) => e.isFile())
.filter((e) => /\.(pdf|doc|docx|xls|xlsx|ppt|pptx)$/i.test(e.name))
.map((e) => {
const fullPath = path.join(docsDir, e.name);
let size = 0;
try {
size = fs.statSync(fullPath).size;
} catch {}
const base = docsDir === modelsDocsDir ? `/uploads/models/${series}/${sub}/docs` : `/uploads/docs/${series}/${sub}`;
const url = `${base}/${e.name}`;
return { name: e.name, url, size };
});
res.json({ success: true, data: files });
} catch (error) {
console.error('获取资料列表错误:', error);
res.status(500).json({ error: '服务器内部错误' });
}
});
// 图片列表(支持每机型统一文件夹结构)
router.get('/images', (req, res) => {
try {
const series = sanitizeSegment(req.query.series);
const sub = sanitizeSegment(req.query.sub);
if (!series || !sub) {
return res.status(400).json({ error: '缺少必要参数 series 或 sub' });
}
const candidates = [
path.join(__dirname, '..', 'uploads', 'models', series, sub, 'image'),
path.join(__dirname, '..', 'uploads', 'models', series, sub, 'images'),
path.join(__dirname, '..', 'uploads', 'images', series, sub),
path.join(__dirname, '..', 'uploads', 'image', series, sub)
];
const imgDir = candidates.find((p) => fs.existsSync(p));
if (!fs.existsSync(imgDir)) {
return res.json({ success: true, data: [] });
}
const entries = fs.readdirSync(imgDir, { withFileTypes: true });
const files = entries
.filter((e) => e.isFile())
.filter((e) => /\.(png|jpg|jpeg|gif|webp|svg)$/i.test(e.name))
.map((e) => {
const fullPath = path.join(imgDir, e.name);
let size = 0;
try { size = fs.statSync(fullPath).size; } catch {}
let base = `/uploads/images/${series}/${sub}`;
if (imgDir.includes(path.join('uploads', 'models'))) {
if (imgDir.endsWith(path.join(series, sub, 'image'))) {
base = `/uploads/models/${series}/${sub}/image`;
} else if (imgDir.endsWith(path.join(series, sub, 'images'))) {
base = `/uploads/models/${series}/${sub}/images`;
}
} else if (imgDir.includes(path.join('uploads', 'image'))) {
base = `/uploads/image/${series}/${sub}`;
}
const url = `${base}/${e.name}`;
return { name: e.name, url, size };
});
res.json({ success: true, data: files });
} catch (error) {
console.error('获取图片列表错误:', error);
res.status(500).json({ error: '服务器内部错误' });
}
});
module.exports = router;
// 使用相同 router 添加规格读取接口
router.get('/specs', (req, res) => {
try {
const series = sanitizeSegment(req.query.series);
const sub = sanitizeSegment(req.query.sub);
if (!series || !sub) {
return res.status(400).json({ error: '缺少必要参数 series 或 sub' });
}
const dirCandidates = [
path.join(__dirname, '..', 'uploads', 'models', series, sub, 'specs'),
path.join(__dirname, '..', 'uploads', 'specs', series, sub)
];
const parseCsvFile = (filePath) => {
const raw = fs.readFileSync(filePath, 'utf8');
const lines = raw.replace(/^\uFEFF/, '').split(/\r?\n/).filter((l) => l.trim().length);
const normalize = (s) => String(s || '').trim().replace(/^"(.*)"$/, '$1').replace(/^'(.*)'$/, '$1');
const parseLine = (l) => {
const replaced = l.replace(//g, ';').replace(//g, ',').replace(/\uff0c/g, ',').replace(/\uff1b/g, ';');
let tokens = replaced.match(/"([^"]*)"|[^,;\t]+/g) || [];
if (tokens.length < 2) {
const parts = replaced.split(/:|/);
if (parts.length >= 2) {
tokens = [parts[0], parts.slice(1).join(':')];
}
}
const key = normalize(tokens[0] || '');
const val = normalize(tokens[1] || '');
return { key, value: val };
};
return lines.map(parseLine).filter((r) => r.key.length);
};
const dirPath = dirCandidates.find((p) => fs.existsSync(p) && fs.statSync(p).isDirectory());
if (dirPath) {
const entries = fs.readdirSync(dirPath, { withFileTypes: true });
const csvFiles = entries.filter((e) => e.isFile() && /\.csv$/i.test(String(e.name).trim()));
if (csvFiles.length) {
const sections = csvFiles.map((e) => {
const fp = path.join(dirPath, e.name);
const title = e.name.replace(/\.csv$/i, '');
return { title, rows: parseCsvFile(fp) };
});
return res.json({ success: true, data: sections });
}
}
const fileCandidates = [
path.join(__dirname, '..', 'uploads', 'models', series, sub, 'specs', 'specs.csv'),
path.join(__dirname, '..', 'uploads', 'models', series, sub, 'specs.csv'),
path.join(__dirname, '..', 'uploads', 'specs', series, `${sub}.csv`),
path.join(__dirname, '..', 'uploads', 'specs', series, sub, 'specs.csv')
];
const filePath = fileCandidates.find((p) => fs.existsSync(p));
if (!filePath) {
return res.json({ success: true, data: [] });
}
const specs = parseCsvFile(filePath);
res.json({ success: true, data: specs });
} catch (error) {
console.error('读取规格CSV错误:', error);
res.status(500).json({ error: '服务器内部错误' });
}
});