where('id', $id) ->where('status', 1) ->first(); } /** * 获取模板 */ public function getTemplate(string $templateCode) { return DB::table('print_templates') ->where('template_code', $templateCode) ->where('status', 1) ->first(); } /** * 获取默认模板 */ public function getDefaultTemplate(string $templateType) { return DB::table('print_templates') ->where('template_type', $templateType) ->where('is_default', 1) ->where('status', 1) ->first(); } /** * 获取模板列表(仅返回基本字段) */ public function getTemplateList(?string $templateType = null) { $query = DB::table('print_templates') ->where('status', 1) ->select([ 'id', 'template_code', 'template_name', 'template_type', 'is_default', 'status', 'created_at', 'updated_at' ]); if ($templateType) { $query->where('template_type', $templateType); } return $query ->orderBy('created_at', 'desc') ->get(); } /** * 渲染模板(使用 LightnCandy) */ public function renderTemplate(string $templateCode, array $data): string { $template = $this->getTemplate($templateCode); if (!$template) { throw new \Exception("模板不存在:{$templateCode}"); } // 编译 Handlebars 模板为 PHP 代码 $phpCode = LightnCandy::compile($template->html_template, [ 'flags' => LightnCandy::FLAG_ERROR_EXCEPTION | LightnCandy::FLAG_HANDLEBARSJS | LightnCandy::FLAG_RUNTIMEPARTIAL | LightnCandy::FLAG_NOESCAPE | LightnCandy::FLAG_BESTPERFORMANCE | LightnCandy::FLAG_SPVARS, 'helpers' => $this->getHelpers(), ]); // 执行编译后的 PHP 代码 $renderer = eval($phpCode); $html = $renderer($data); // 注入 CSS 样式 if ($template->css_style) { $html = $this->injectStyles($html, $template->css_style); } return $html; } /** * 获取自定义 Helper */ private function getHelpers(): array { return [ // 日期格式化 'formatDate' => function($args) { $value = $args[0] ?? ''; $format = $args[1] ?? 'Y-m-d H:i:s'; if (!$value) return ''; return date($format, strtotime($value)); }, // 金额格式化 'formatMoney' => function($args) { return number_format($args[0] ?? 0, 2); }, // 等于 - Block Helper(支持 {{#eq}}...{{else}}...{{/eq}}) 'eq' => function($a, $b, $options) { $isEqual = strval($a) === strval($b); if (is_array($options) && isset($options['fn'])) { // Block helper 模式 return $isEqual ? $options['fn']() : (isset($options['inverse']) ? $options['inverse']() : ''); } // 简单模式(子表达式) return $isEqual ? '1' : ''; }, // 不等于 - Block Helper 'ne' => function($a, $b, $options) { $notEqual = strval($a) !== strval($b); if ($options && isset($options['fn'])) { return $notEqual ? $options['fn']() : (isset($options['inverse']) ? $options['inverse']() : ''); } return $notEqual ? '1' : ''; }, // 大于 - Block Helper 'gt' => function($a, $b, $options) { $result = $a > $b; if ($options && isset($options['fn'])) { return $result ? $options['fn']() : (isset($options['inverse']) ? $options['inverse']() : ''); } return $result ? '1' : ''; }, // 小于 - Block Helper 'lt' => function($a, $b, $options) { $result = $a < $b; if ($options && isset($options['fn'])) { return $result ? $options['fn']() : (isset($options['inverse']) ? $options['inverse']() : ''); } return $result ? '1' : ''; }, // 大于等于 - Block Helper 'gte' => function($a, $b, $options) { $result = $a >= $b; if ($options && isset($options['fn'])) { return $result ? $options['fn']() : (isset($options['inverse']) ? $options['inverse']() : ''); } return $result ? '1' : ''; }, // 小于等于 - Block Helper 'lte' => function($a, $b, $options) { $result = $a <= $b; if ($options && isset($options['fn'])) { return $result ? $options['fn']() : (isset($options['inverse']) ? $options['inverse']() : ''); } return $result ? '1' : ''; }, // 返回值的 helper(用于子表达式 {{#if (eqVal ...)}}) 'eqVal' => function($args) { $a = $args[0] ?? null; $b = $args[1] ?? null; return strval($a) === strval($b); }, 'neVal' => function($args) { $a = $args[0] ?? null; $b = $args[1] ?? null; return strval($a) !== strval($b); }, 'gtVal' => function($args) { return ($args[0] ?? 0) > ($args[1] ?? 0); }, 'ltVal' => function($args) { return ($args[0] ?? 0) < ($args[1] ?? 0); }, 'gteVal' => function($args) { return ($args[0] ?? 0) >= ($args[1] ?? 0); }, 'lteVal' => function($args) { return ($args[0] ?? 0) <= ($args[1] ?? 0); }, // 数组长度 'length' => function($args) { return is_array($args[0]) ? count($args[0]) : 0; }, // 加 'add' => function($args) { return ($args[0] ?? 0) + ($args[1] ?? 0); }, // 减 'sub' => function($args) { return ($args[0] ?? 0) - ($args[1] ?? 0); }, // 默认值 'default' => function($args) { return $args[0] ?? $args[1] ?? ''; }, ]; } /** * 注入样式 */ private function injectStyles(string $html, string $css): string { $styleTag = sprintf('', $css); // 尝试插入到
中 if (preg_match('/]*>/i', $html, $matches)) { return str_replace($matches[0], $matches[0] . $styleTag, $html); } // 如果没有 head 标签,插入到 之后 if (preg_match('/]*>/i', $html, $matches)) { return str_replace($matches[0], $matches[0] . $styleTag, $html); } // 都没有,插入到最前面 return $styleTag . $html; } /** * 替换条形码占位符 */ private function replaceBarcodes(string $html): string { $generator = new \Picqer\Barcode\BarcodeGeneratorHTML(); // 匹配