$line, 'v' => '']; } else { $out[] = ['k' => trim(substr($line, 0, $p)), 'v' => trim(substr($line, $p + 1))]; } } return json_encode($out, JSON_UNESCAPED_UNICODE); } public function index() { $product = new Product(); return $this->view('admin/products', [ 'products' => $product->all(), 'seg' => $this->seg(), ]); } public function create() { return $this->view('admin/product_form', [ 'p' => null, 'cats' => (new Category())->all(), 'mode' => 'fixed', ]); } public function store() { if (!csrf_check()) { $this->redirect('admin/products'); } $product = new Product(); $cover = $this->uploadFile('cover') ?? $this->post('cover_url', ''); $mode = $this->post('mode') === 'builder' ? 'builder' : 'fixed'; $gallery = ($mode === 'fixed') ? $this->uploadFiles('gallery') : []; $description = ($mode === 'fixed') ? $this->post('description', '') : ''; $id = $product->insert([ 'category_id' => (int)$this->post('category_id'), 'name' => $this->post('name'), 'slug' => '', 'cover' => $cover, 'summary' => $this->post('summary'), 'description' => $description, 'price' => (float)$this->post('price', 0), 'specs' => $this->specsToJson($this->post('specs', '')), 'gallery' => json_encode($gallery, JSON_UNESCAPED_UNICODE), 'tags' => $this->post('tags', ''), 'sort_order' => (int)$this->post('sort_order', 0), 'status' => $this->post('status', 1) ? 1 : 0, 'created_at' => date('Y-m-d'), 'layout' => $this->post('layout', ''), 'mode' => $mode, ]); // URL 标识留空时按记录序号顺序生成(短、稳定) $slug = $this->post('slug') ? slugify($this->post('slug')) : (string)$id; $product->update($id, ['slug' => $slug]); // 新建时若选择「可视化编辑」,保存后直接进入可视化编辑器排版,无需再手动点编辑 if ($mode === 'builder') { $this->redirect('admin/products/edit/' . $id); } $this->redirect('admin/products'); } public function edit($id) { $product = new Product(); $p = $product->find($id); if (!$p) { $this->redirect('admin/products'); } $mode = empty($p['mode']) ? 'fixed' : $p['mode']; $specs = $product->specsArray($p); $specText = ''; foreach ($specs as $s) $specText .= ($s['k'] ?? '') . '|' . ($s['v'] ?? '') . "\n"; $cats = (new Category())->all(); if ($mode === 'builder') { $layout = []; if (!empty($p['layout'])) { $dec = json_decode($p['layout'], true); if (is_array($dec)) $layout = $dec; } return $this->view('admin/product_builder', [ 'p' => $p, 'cats' => $cats, 'specText' => $specText, 'layout' => $layout, 'mode' => $mode, ]); } return $this->view('admin/product_form', [ 'p' => $p, 'cats' => $cats, 'specText' => $specText, 'mode' => $mode, ]); } /** 切换编辑模式(固定版面 <-> 可视化编辑),仅更新 mode 字段,其余数据保留 */ public function switchMode($id) { $p = (new Product())->find($id); if (!$p) { $this->redirect('admin/products'); } $target = ($this->get('mode') === 'builder') ? 'builder' : 'fixed'; (new Product())->update($id, ['mode' => $target]); $this->redirect('admin/products/edit/' . $id); } public function update($id) { if (!csrf_check()) { $this->redirect('admin/products'); } $product = new Product(); $p = $product->find($id); if (!$p) { $this->redirect('admin/products'); } $mode = $this->post('mode') === 'builder' ? 'builder' : 'fixed'; $cover = $this->uploadFile('cover'); if (!$cover && $this->post('cover_url')) $cover = $this->post('cover_url'); if (!$cover) $cover = $p['cover'] ?? ''; $data = [ 'category_id' => (int)$this->post('category_id'), 'name' => $this->post('name'), 'slug' => $this->post('slug') ? slugify($this->post('slug')) : (string)$id, 'cover' => $cover, 'summary' => $this->post('summary'), 'price' => (float)$this->post('price', 0), 'specs' => $this->specsToJson($this->post('specs', '')), 'tags' => $this->post('tags', ''), 'sort_order' => (int)$this->post('sort_order', 0), 'status' => $this->post('status', 1) ? 1 : 0, 'mode' => $mode, ]; if ($mode === 'fixed') { // 固定版面:保存图集与详细描述,保留原有 layout 不被覆盖 $newGal = $this->uploadFiles('gallery'); if (!empty($newGal)) { $data['gallery'] = json_encode($newGal, JSON_UNESCAPED_UNICODE); } elseif ($this->post('clear_gallery')) { $data['gallery'] = '[]'; } else { $data['gallery'] = $p['gallery'] ?? '[]'; } $data['description'] = $this->post('description', ''); } else { // 可视化编辑:更新 layout,保留图集/描述原值 $layout = $this->post('layout', ''); if ($layout !== '' && !is_array(json_decode($layout, true))) { $layout = ''; } $data['layout'] = $layout; } $product->update($id, $data); $this->redirect('admin/products'); } public function delete($id) { if (csrf_check()) { (new Product())->delete($id); } $this->redirect('admin/products'); } }