'头图介绍', 'stats' => '宣传数据', 'steps' => '操作流程', 'features' => '功能卡片', 'cta' => '底部行动', 'custom' => '自定义图文', ]; } /** * @return array */ public static function published() { try { $rows = Db::name('ps_home_block')->where('status', 'normal')->order('weigh desc,id asc')->select(); } catch (\Exception $e) { return self::defaults(); } $out = []; foreach ($rows as $row) { $out[] = self::formatRow($row); } return $out; } /** * @param array $row * @return array */ public static function formatRow(array $row) { $content = []; if (!empty($row['content'])) { $decoded = json_decode($row['content'], true); if (is_array($decoded)) { $content = $decoded; } } return [ 'id' => (int)$row['id'], 'type' => (string)$row['type'], 'name' => (string)$row['name'], 'kicker' => (string)$row['kicker'], 'title' => (string)$row['title'], 'subtitle' => (string)$row['subtitle'], 'weigh' => (int)$row['weigh'], 'content' => $content, ]; } /** * @return array */ public static function defaults() { $now = time(); $rows = [ [ 'id' => 0, 'type' => 'hero', 'name' => '头图', 'kicker' => '', 'title' => '智印流程单', 'subtitle' => '把工单数据,准确地打在已印好的纸上', 'weigh' => 100, 'content' => [ 'eyebrow' => '给车间用的智能套打', 'brand' => '智印流程单', 'lead' => '文件导入就能识别产品与工序,选好模板、填好数量,即可批量套打。模板可自制,也可在商城里选用,每次打印都留得下记录。', 'btn_primary_text' => '开始套打', 'btn_primary_path' => '/workorders/edit', 'btn_secondary_text' => '了解流程', 'btn_secondary_path' => '#home-flow', 'panel_title' => '今天的车间', 'notes' => [ '导入文件,AI 帮你认出编号和工序', '数量只在套打时填写,一版一张', '按原纸格子对齐,字打在该打的地方', ], ], ], [ 'id' => 0, 'type' => 'stats', 'name' => '宣传数据', 'kicker' => '', 'title' => '', 'subtitle' => '', 'weigh' => 90, 'content' => [ 'items' => [ ['label' => '入驻厂家', 'key' => 'factory_count', 'value' => ''], ['label' => '今日打印量', 'key' => 'print_today', 'value' => ''], ['label' => '累计套打张数', 'key' => 'print_total', 'value' => ''], ['label' => '可用模板', 'key' => 'template_count', 'value' => ''], ], ], ], [ 'id' => 0, 'type' => 'steps', 'name' => '操作流程', 'kicker' => '怎么用', 'title' => '四步走完,就能出单', 'subtitle' => '从建档到打印,车间按这个顺序做就行,不用先学一堆设置。', 'weigh' => 80, 'content' => [ 'items' => [ ['title' => '建档或导入', 'desc' => '手工新建产品 / 工单,或用文件导入让 AI 识别编号、名称和工序。'], ['title' => '准备模板', 'desc' => '在本厂模板中选用已识版样式,也可到模板商城补充官方或市场模板。'], ['title' => '套打出单', 'desc' => '填写本次总数量和每版数量,预览拆张后确认打印,只印字段不印底图。'], ['title' => '记录与分析', 'desc' => '在打印日志里查看流水和补打记录,首页看板同步展示打印量与工厂数据。'], ], ], ], [ 'id' => 0, 'type' => 'features', 'name' => '功能介绍', 'kicker' => '能做什么', 'title' => '日常出单用得到的能力', 'subtitle' => '识别、套打、模板、商城、日志和分析都在这一处,点卡片就能进去。', 'weigh' => 70, 'content' => [ 'items' => [ ['tag' => '识别', 'title' => '文件导入 AI 识别', 'desc' => '上传 Excel、PDF 或图片,自动提取编号、名称与工序,核对后写入本厂档案。', 'path' => '/workorders'], ['tag' => '套打', 'title' => '批量打印', 'desc' => '按总数量和每版数量自动拆张,一次确认即可连续套打多张流程单。', 'path' => '/templates'], ['tag' => '模板', 'title' => '自定义模板', 'desc' => '上传车间流程单识版,格子坐标与字段对齐后,按原纸套打不跑偏。', 'path' => '/templates'], ['tag' => '商城', 'title' => '模板商城', 'desc' => '选用官方或市场模板,单次或买断授权,快速补齐本厂可套打样式。', 'path' => '/market'], ['tag' => '日志', 'title' => '打印日志', 'desc' => '每次套打留流水号、模板与张数,便于车间对账、补打和追溯。', 'path' => '/print/history'], ['tag' => '分析', 'title' => '数据智能分析', 'desc' => '汇总入驻工厂、日打印量、本厂工单与套打次数,看清车间打印节奏。', 'path' => '/print/history'], ], ], ], [ 'id' => 0, 'type' => 'cta', 'name' => '底部行动', 'kicker' => '', 'title' => '准备好就开打', 'subtitle' => '选一张本厂模板,填上本次数量,流程单就能按原纸套出来。', 'weigh' => 60, 'content' => [ 'btn_primary_text' => '选择模板套打', 'btn_primary_path' => '/templates', 'btn_secondary_text' => '进入工单', 'btn_secondary_path' => '/workorders', ], ], ]; foreach ($rows as &$row) { $row['createtime'] = $now; $row['updatetime'] = $now; $row['status'] = 'normal'; } unset($row); return $rows; } /** * @param array $params * @return array */ public static function normalize(array $params) { $type = isset($params['type']) ? (string)$params['type'] : 'custom'; if (!isset(self::typeList()[$type])) { $type = 'custom'; } $params['type'] = $type; $params['name'] = mb_substr(trim((string)($params['name'] ?? '')), 0, 64); $params['kicker'] = mb_substr(trim((string)($params['kicker'] ?? '')), 0, 64); $params['title'] = mb_substr(trim((string)($params['title'] ?? '')), 0, 120); $params['subtitle'] = mb_substr(trim((string)($params['subtitle'] ?? '')), 0, 255); $params['weigh'] = (int)($params['weigh'] ?? 0); $params['status'] = ($params['status'] ?? '') === 'hidden' ? 'hidden' : 'normal'; $raw = $params['content_json'] ?? $params['content'] ?? []; if (is_string($raw)) { $decoded = json_decode($raw, true); $raw = is_array($decoded) ? $decoded : []; } if (!is_array($raw)) { $raw = []; } $params['content'] = json_encode(self::cleanContent($type, $raw), JSON_UNESCAPED_UNICODE); unset($params['content_json'], $params['items'], $params['notes']); return $params; } /** * @param string $type * @param array $raw * @return array */ public static function cleanContent($type, array $raw) { $str = function ($key, $max = 255) use ($raw) { return mb_substr(trim((string)($raw[$key] ?? '')), 0, $max); }; if ($type === 'hero') { $notes = $raw['notes'] ?? []; if (is_string($notes)) { $notes = preg_split('/\r\n|\n/', $notes); } $cleanNotes = []; foreach ((array)$notes as $note) { $note = trim((string)$note); if ($note !== '') { $cleanNotes[] = mb_substr($note, 0, 120); } } return [ 'eyebrow' => $str('eyebrow', 40), 'brand' => $str('brand', 40), 'lead' => $str('lead', 500), 'btn_primary_text' => $str('btn_primary_text', 20), 'btn_primary_path' => $str('btn_primary_path', 120), 'btn_secondary_text' => $str('btn_secondary_text', 20), 'btn_secondary_path' => $str('btn_secondary_path', 120), 'panel_title' => $str('panel_title', 40), 'notes' => $cleanNotes, ]; } if ($type === 'stats' || $type === 'steps' || $type === 'features') { $items = []; foreach ((array)($raw['items'] ?? []) as $item) { if (!is_array($item)) { continue; } if ($type === 'stats') { $label = mb_substr(trim((string)($item['label'] ?? '')), 0, 40); if ($label === '') { continue; } $items[] = [ 'label' => $label, 'key' => mb_substr(trim((string)($item['key'] ?? '')), 0, 32), 'value' => mb_substr(trim((string)($item['value'] ?? '')), 0, 32), ]; } elseif ($type === 'steps') { $title = mb_substr(trim((string)($item['title'] ?? '')), 0, 40); if ($title === '') { continue; } $items[] = [ 'title' => $title, 'desc' => mb_substr(trim((string)($item['desc'] ?? '')), 0, 200), ]; } else { $title = mb_substr(trim((string)($item['title'] ?? '')), 0, 40); if ($title === '') { continue; } $items[] = [ 'tag' => mb_substr(trim((string)($item['tag'] ?? '')), 0, 12), 'title' => $title, 'desc' => mb_substr(trim((string)($item['desc'] ?? '')), 0, 200), 'path' => mb_substr(trim((string)($item['path'] ?? '')), 0, 120), ]; } } return ['items' => $items]; } if ($type === 'cta') { return [ 'btn_primary_text' => $str('btn_primary_text', 20), 'btn_primary_path' => $str('btn_primary_path', 120), 'btn_secondary_text' => $str('btn_secondary_text', 20), 'btn_secondary_path' => $str('btn_secondary_path', 120), ]; } return [ 'body' => $str('body', 2000), 'btn_text' => $str('btn_text', 20), 'btn_path' => $str('btn_path', 120), ]; } }