import { Router, Response, NextFunction } from 'express' import { authMiddleware, AuthRequest } from '../middleware/auth' import { getAllTemplates, getTemplateById, getTemplatesByCategory, renderTemplate } from '../services/template.service' const router = Router() /** 获取所有模板列表 */ router.get('/', authMiddleware, async (req: AuthRequest, res: Response, next: NextFunction) => { try { const category = req.query.category as string | undefined const templates = category ? getTemplatesByCategory(category) : getAllTemplates() res.json({ success: true, data: templates }) } catch (err) { next(err) } }) /** 获取模板详情 */ router.get('/:id', authMiddleware, async (req: AuthRequest, res: Response, next: NextFunction) => { try { const template = getTemplateById(req.params.id) if (!template) { return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '模板不存在' } }) } res.json({ success: true, data: template }) } catch (err) { next(err) } }) /** 渲染模板 */ router.post('/:id/render', authMiddleware, async (req: AuthRequest, res: Response, next: NextFunction) => { try { const { variables } = req.body as { variables: Record } const content = renderTemplate(req.params.id, variables || {}) if (!content) { return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '模板不存在' } }) } res.json({ success: true, data: { content } }) } catch (err) { next(err) } }) /** 下载模板(Word .doc 格式,支持变量替换) */ router.get('/:id/download', authMiddleware, async (req: AuthRequest, res: Response, next: NextFunction) => { try { const template = getTemplateById(req.params.id) if (!template) { return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '模板不存在' } }) } // 支持通过 query 参数传入变量(如 ?name=张三&idCardNumber=xxx) const variables: Record = {} for (const [key, value] of Object.entries(req.query)) { if (typeof value === 'string' && key !== 'token') variables[key] = value } let content = template.content if (Object.keys(variables).length > 0) { content = renderTemplate(req.params.id, variables) || content } // 将纯文本转换为HTML段落,使Word样式生效 const textToHtml = (text: string): string => { const lines = text.split(/\n/) let html = '' let inTable = false for (const line of lines) { const trimmed = line.trim() if (!trimmed) { if (inTable) { html += ''; inTable = false } html += '

 

' continue } // 标题检测:以"第X条"开头的行作为小标题 if (/^第[一二三四五六七八九十百]+条/.test(trimmed)) { if (inTable) { html += ''; inTable = false } html += `

${trimmed}

` } else if (/^劳动合同书$|^协议书$|^通知书$|^解除劳动合同协议书$/.test(trimmed)) { if (inTable) { html += ''; inTable = false } html += `

${trimmed}

` } else if (/^(甲方|乙方)((盖章|签字))/.test(trimmed) || /^日期[::]/.test(trimmed)) { if (inTable) { html += ''; inTable = false } html += `

${trimmed}

` } else { if (inTable) { html += ''; inTable = false } html += `

${trimmed}

` } } if (inTable) html += '' return html } const htmlContent = ` ${template.name} ${textToHtml(content)}` const encoded = encodeURIComponent(template.name + '.doc') res.setHeader('Content-Type', 'application/msword; charset=utf-8') res.setHeader('Content-Disposition', `attachment; filename="${encoded}"; filename*=UTF-8''${encoded}`) res.send(htmlContent) } catch (err) { next(err) } }) export default router