You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
79 lines
2.0 KiB
79 lines
2.0 KiB
/**
|
|
* 视图引擎中间件 - 纯粹的模板渲染职责
|
|
*
|
|
* 职责:
|
|
* 1. 提供模板渲染能力
|
|
* 2. 处理基础的渲染上下文
|
|
* 3. 错误处理和日志记录
|
|
*
|
|
* 不负责:
|
|
* - 业务数据获取
|
|
* - 复杂的上下文构建
|
|
* - 服务层调用
|
|
*/
|
|
import consolidate from 'consolidate'
|
|
import { resolve } from 'path'
|
|
import { logger } from '@/logger'
|
|
|
|
export default function viewsMiddleware(viewPath, options = {}) {
|
|
const {
|
|
extension = 'pug',
|
|
options: renderOptions = {}
|
|
} = options
|
|
|
|
return async function views(ctx, next) {
|
|
if (ctx.render) return await next()
|
|
|
|
/**
|
|
* 渲染模板
|
|
* @param {string} templatePath - 模板路径
|
|
* @param {object} locals - 本地变量
|
|
* @param {object} options - 渲染选项
|
|
*/
|
|
ctx.response.render = ctx.render = async function(templatePath, locals = {}, options = {}) {
|
|
const fullPath = resolve(viewPath, `${templatePath}.${extension}`)
|
|
|
|
// 基础上下文数据(只包含框架级别的数据)
|
|
const baseContext = {
|
|
currentPath: ctx.path,
|
|
isLogin: !!(ctx.state && ctx.state.user),
|
|
}
|
|
|
|
// 合并上下文:基础上下文 + locals + ctx.state + 渲染选项
|
|
const renderContext = Object.assign(
|
|
{},
|
|
baseContext,
|
|
locals,
|
|
renderOptions,
|
|
ctx.state || {},
|
|
options
|
|
)
|
|
|
|
// 添加 partials 支持
|
|
renderContext.partials = Object.assign({}, renderOptions.partials || {})
|
|
|
|
ctx.type = 'text/html'
|
|
|
|
const render = consolidate[extension]
|
|
if (!render) {
|
|
throw new Error(`Template engine not found for ".${extension}" files`)
|
|
}
|
|
|
|
try {
|
|
const html = await render(fullPath, renderContext)
|
|
ctx.body = html
|
|
return html
|
|
} catch (err) {
|
|
logger.error('View rendering error:', {
|
|
template: templatePath,
|
|
fullPath,
|
|
error: err.message,
|
|
stack: err.stack
|
|
})
|
|
throw err
|
|
}
|
|
}
|
|
|
|
return await next()
|
|
}
|
|
}
|