diff --git a/application/admin/controller/Cars.php b/application/admin/controller/Cars.php index e80ad06..c5e20f7 100644 --- a/application/admin/controller/Cars.php +++ b/application/admin/controller/Cars.php @@ -2,10 +2,13 @@ namespace app\admin\controller; +use app\admin\model\Admin; +use app\admin\model\car\AttributeValue; use app\common\controller\Backend; use think\Db; use think\exception\PDOException; use think\exception\ValidateException; +use function Symfony\Component\Clock\now; /** * @@ -20,6 +23,7 @@ class Cars extends Backend * @var \app\admin\model\Cars */ protected $model = null; + protected $series,$brands; public function _initialize() { @@ -28,6 +32,10 @@ class Cars extends Backend $series = Db::query("SELECT a.id,brand_id as pid, a.name FROM series a LEFT JOIN brands b ON a.brand_id = b.id;"); $brands = Db::query("SELECT id, 0 as pid, `name` FROM brands;"); + + $this->series = $series; + $this->brands = $brands; + $data = array_merge($brands,$series); $tree = $this->buildTree($data); @@ -42,6 +50,37 @@ class Cars extends Backend } + public function index() + { + //设置过滤方法 + $this->request->filter(['strip_tags', 'trim']); + if (false === $this->request->isAjax()) { + return $this->view->fetch(); + } + //如果发送的来源是 Selectpage,则转发到 Selectpage + if ($this->request->request('keyField')) { + return $this->selectpage(); + } + [$where, $sort, $order, $offset, $limit] = $this->buildparams(); + $list = $this->model + ->where($where) + ->order($sort, $order) + ->with([ + 'contact' =>function ($q) { + $q->field('id,nickname,mobile'); + }, + 'brand' =>function ($q) { + $q->field('id,name'); + }, + 'series' =>function ($q) { + $q->field('id,name'); + }, + ] + ) + ->paginate($limit); + $result = ['total' => $list->total(), 'rows' => $list->items()]; + return json($result); + } /** @@ -73,7 +112,11 @@ class Cars extends Backend $validate = is_bool($this->modelValidate) ? ($this->modelSceneValidate ? $name . '.add' : $name) : $this->modelValidate; $this->model->validateFailException()->validate($validate); } - $result = $this->model->allowField(true)->save($params); + $data = $this->getModelData($params); + $data['created_at'] = now()->format('Y-m-d H:i:s'); + $data['updated_at'] = now()->format('Y-m-d H:i:s'); + $result = $this->model->allowField(true)->save($data); + $this->dealExtend($params,$this->model->id); Db::commit(); } catch (ValidateException|PDOException|\Exception $e) { Db::rollback(); @@ -86,4 +129,124 @@ class Cars extends Backend } + public function edit($ids = null) + { + $row = $this->model->get($ids); + if (!$row) { + $this->error(__('No Results were found')); + } + $adminIds = $this->getDataLimitAdminIds(); + if (is_array($adminIds) && !in_array($row[$this->dataLimitField], $adminIds)) { + $this->error(__('You have no permission')); + } + if (false === $this->request->isPost()) { + $series_map = array_column($this->series,'name','id'); + $series = $series_map[$row->series_id] ?? ''; + $brand_map = array_column($this->brands,'name','id'); + $brand = $brand_map[$row->brand_id] ?? ''; + $series_name = $brand . ' / ' . $series; + $row->series_name = $series_name; + $this->view->assign('row', $row); + return $this->view->fetch(); + } + $params = $this->request->post('row/a'); + if (empty($params)) { + $this->error(__('Parameter %s can not be empty', '')); + } + $params = $this->preExcludeFields($params); + $result = false; + Db::startTrans(); + try { + //是否采用模型验证 + if ($this->modelValidate) { + $name = str_replace("\\model\\", "\\validate\\", get_class($this->model)); + $validate = is_bool($this->modelValidate) ? ($this->modelSceneValidate ? $name . '.edit' : $name) : $this->modelValidate; + $row->validateFailException()->validate($validate); + } + + $data = $this->getModelData($params); + $data['updated_at'] = now()->format('Y-m-d H:i:s'); + $result = $row->allowField(true)->save($data); + + $this->dealExtend($params,$row->id); + + Db::commit(); + } catch (ValidateException|PDOException|Exception $e) { + Db::rollback(); + $this->error($e->getMessage()); + } + if (false === $result) { + $this->error(__('No rows were updated')); + } + $this->success(); + } + + public function extend(){ + $data = \app\admin\model\car\Attributes::order('sort_order')->select(); + $id = request()->get('id'); + $value_map = []; + if ($id){ + $values = AttributeValue::where('car_id',$id)->select(); +// dd($values); + foreach ($values as $value){ + $value_map[$value->attribute_id] = $value->value; + } + } + $res = []; + foreach ($data as $datum){ + $datum->options = json_decode($datum->options); + $re = $datum->toArray(); + $out = [ + 'id' => $re['id'], + 'label' => $re['name'], + 'type' => $re['input_type'], + 'options' => $re['options'], + 'name' => $re['field_key'], + 'value' => $value_map[$re['id']] ?? null, + ]; + $res [] =$out; + } + $this->success(data:$res); + } + + private function dealExtend(array $params,$car_id) + { + $data = \app\admin\model\car\Attributes::order('sort_order')->field('id,field_key')->select(); + $insert = []; +// dd($data); + foreach ($data as $datum){ + if ($params[$datum['field_key']]){ + $insert [] = [ + 'car_id' => $car_id, + 'attribute_id' => $datum->id, + 'value' => $params[$datum['field_key']], + ]; + } + } +// dd($insert); + AttributeValue::where('car_id',$car_id)->delete(); + AttributeValue::insertAll($insert); + } + + private function getModelData(array $params) + { + $res = [ + 'title' => $params['title'], + 'price' => $params['price'], + 'cover_image' => $params['cover_image'], + 'contact_id' => $params['contact_id'] ?? $this->auth->id, + ]; + $series_id = $params['series_id']; + + $series = \app\admin\model\Series::where('id',$series_id)->find(); + + $res ['series_id'] = $series_id; + $res ['brand_id'] = $series->brand_id; + if (!$res['contact_id'] || $res['contact_id'] == -1){ + $res['contact_id'] = $this->auth->id; + } + return $res; + } + + } diff --git a/application/admin/lang/zh-cn.php b/application/admin/lang/zh-cn.php index f0e1bc4..d8bab24 100755 --- a/application/admin/lang/zh-cn.php +++ b/application/admin/lang/zh-cn.php @@ -221,4 +221,6 @@ return [ 'User Module' => '会员模块', 'Register' => '注册', 'User Center' => '会员中心', + 'Created_at' => '创建时间', + 'Updated_at' => '更新时间', ]; diff --git a/application/admin/lang/zh-cn/cars.php b/application/admin/lang/zh-cn/cars.php index c638691..3f807ce 100644 --- a/application/admin/lang/zh-cn/cars.php +++ b/application/admin/lang/zh-cn/cars.php @@ -8,5 +8,7 @@ return [ 'Cover_image' => '图片', 'Location' => '城市名', 'Contact_id' => '联系人', + 'Contact_name' => '联系人', + 'Contact_phone' => '联系电话', 'Is_active' => '是否上架' ]; diff --git a/application/admin/model/Cars.php b/application/admin/model/Cars.php index caa9e79..78d09e6 100644 --- a/application/admin/model/Cars.php +++ b/application/admin/model/Cars.php @@ -29,6 +29,20 @@ class Cars extends Model ]; + public function contact() + { + return $this->belongsTo(Admin::class,'contact_id'); + } + + public function brand() + { + return $this->belongsTo(Brands::class,'brand_id'); + } + + public function series() + { + return $this->belongsTo(Series::class,'series_id'); + } diff --git a/application/admin/model/car/AttributeValue.php b/application/admin/model/car/AttributeValue.php new file mode 100644 index 0000000..7d8a249 --- /dev/null +++ b/application/admin/model/car/AttributeValue.php @@ -0,0 +1,49 @@ + __('Text'), 'select' => __('Select'), 'range' => __('Range'), 'checkbox' => __('Checkbox')]; + } + + + public function getInputTypeTextAttr($value, $data) + { + $value = $value ?: ($data['input_type'] ?? ''); + $list = $this->getInputTypeList(); + return $list[$value] ?? ''; + } + + + + +} diff --git a/application/admin/view/cars/add.html b/application/admin/view/cars/add.html index e262d88..9b38ee2 100644 --- a/application/admin/view/cars/add.html +++ b/application/admin/view/cars/add.html @@ -12,7 +12,8 @@
- + +
@@ -39,8 +40,8 @@
- + {foreach $users as $item} {/foreach} @@ -57,12 +58,6 @@
-
diff --git a/application/admin/view/cars/edit.html b/application/admin/view/cars/edit.html index 3d69db2..f2faac1 100644 --- a/application/admin/view/cars/edit.html +++ b/application/admin/view/cars/edit.html @@ -1,95 +1,67 @@ -
+ + +
+ + -
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
- -
-
- -
- - -
- +
+ +
+
-
    -
    -
    - -
    - +
    + +
    + + +
    -
    -
    - -
    - +
    + +
    + +
    -
    -
    - -
    - +
    + +
    +
    + +
    + + +
    + +
    +
      +
      -
      -
      - -
      - + +
      + +
      + +
      -
      -
      - -
      - + +
      + +
      + +
      -
      -
      - -
      - -
      -
      -
      - -
      - -
      -
      -
      - -
      - -
      -
      - - + +
      + + diff --git a/composer.json b/composer.json index 1d77667..3888800 100755 --- a/composer.json +++ b/composer.json @@ -29,7 +29,8 @@ "ext-json": "*", "ext-curl": "*", "ext-pdo": "*", - "ext-bcmath": "*" + "ext-bcmath": "*", + "nesbot/carbon": "^3.10" }, "config": { "preferred-install": "dist", diff --git a/public/assets/css/car/index.css b/public/assets/css/car/index.css index 519a41c..457b11f 100644 --- a/public/assets/css/car/index.css +++ b/public/assets/css/car/index.css @@ -5,4 +5,5 @@ padding-left:15px; padding-right:15px; padding-top:10px; + overflow-y:auto; } \ No newline at end of file diff --git a/public/assets/js/backend/cars.js b/public/assets/js/backend/cars.js index b154f50..b0d6dda 100644 --- a/public/assets/js/backend/cars.js +++ b/public/assets/js/backend/cars.js @@ -1,4 +1,4 @@ -define(['jquery', 'bootstrap', 'backend', 'table', 'form','cascader'], function ($, undefined, Backend, Table, Form) { +define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'cascader'], function ($, undefined, Backend, Table, Form) { var Controller = { index: function () { @@ -28,20 +28,53 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form','cascader'], function [ {checkbox: true}, {field: 'id', title: __('Id')}, - {field: 'title', title: __('Title'), operate: 'LIKE', table: table, class: 'autocontent', formatter: Table.api.formatter.content}, - {field: 'brand_id', title: __('Brand_id')}, - {field: 'series_id', title: __('Series_id')}, - {field: 'price', title: __('Price'), operate:'BETWEEN'}, - {field: 'cover_image', title: __('Cover_image'), operate: false, events: Table.api.events.image, formatter: Table.api.formatter.image}, - {field: 'location', title: __('Location'), operate: 'LIKE'}, - {field: 'contact_name', title: __('Contact_name'), operate: 'LIKE'}, - {field: 'contact_id', title: __('Contact_id')}, - {field: 'contact_phone', title: __('Contact_phone'), operate: 'LIKE'}, - {field: 'wechat_qrcode', title: __('Wechat_qrcode'), operate: 'LIKE', table: table, class: 'autocontent', formatter: Table.api.formatter.content}, - {field: 'created_at', title: __('Created_at'), operate:'RANGE', addclass:'datetimerange', autocomplete:false}, - {field: 'updated_at', title: __('Updated_at'), operate:'RANGE', addclass:'datetimerange', autocomplete:false}, - {field: 'is_active', title: __('Is_active')}, - {field: 'operate', title: __('Operate'), table: table, events: Table.api.events.operate, formatter: Table.api.formatter.operate} + { + field: 'title', + title: __('Title'), + operate: 'LIKE', + table: table, + class: 'autocontent', + formatter: Table.api.formatter.content + }, + {field: 'brand.name', title: __('Brand_id')}, + {field: 'series.name', title: __('Series_id')}, + {field: 'price', title: __('Price'), operate: 'BETWEEN'}, + { + field: 'cover_image', + title: __('Cover_image'), + operate: false, + events: Table.api.events.image, + formatter: Table.api.formatter.image + }, + {field: 'contact.nickname', title: __('Contact_name'), operate: 'LIKE'}, + {field: 'contact.mobile', title: __('Contact_phone'), operate: 'LIKE'}, + { + field: 'created_at', + title: __('Created_at'), + operate: 'RANGE', + addclass: 'datetimerange', + autocomplete: false + }, + { + field: 'updated_at', + title: __('Updated_at'), + operate: 'RANGE', + addclass: 'datetimerange', + autocomplete: false + }, + {field: 'is_active', title: __('Is_active'), + searchList: { + "0": '下架', + "1": '上架', + }, + formatter: Table.api.formatter.status}, + { + field: 'operate', + title: __('Operate'), + table: table, + events: Table.api.events.operate, + formatter: Table.api.formatter.operate + } ] ] }); @@ -52,25 +85,91 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form','cascader'], function add: function () { Controller.api.bindevent(); Controller.api.series(); + Controller.api.extend(); }, edit: function () { Controller.api.bindevent(); Controller.api.series(); + Controller.api.extend(); }, api: { bindevent: function () { Form.api.bindevent($("form[role=form]")); }, - series:function (){ + series: function () { var _data = series; $('#c-series_id').zdCascader({ data: _data, onChange: function ($this, data, allPathData) { // console.log(data,allPathData); - $('#item_id_value').val(data.value); + $('#c-series_id_value').val(data.value); } }); $('#c-series_id').val($('#c-series_id').data('value')); + }, + extend: function () { + const id = $('input[name="row[id]"]').val(); + let url = "cars/extend"; + if (id) { + url += '&id=' + id; + } + Fast.api.ajax({ + url: url, // 你的 API 地址 + type: "get", + contentType: 'application/json', + dataType: "json" + }, + function (data) { + var container = $('#add-form'); + + data.forEach(function (field) { + let html = ` +
      + +
      `; + + // ✅ 单选框(radio,替代 checkbox) + if (field.type === 'checkbox' || field.type === 'radio') { + field.options.forEach(function (opt) { + // 是否选中 + const checked = String(field.value) === String(opt.value) ? 'checked' : ''; + html += ` + `; + }); + } + + // ✅ 数值范围输入(单 input 版本) + else if (field.type === 'range') { + const opts = field.options || {}; + const val = field.value ?? ''; // 可能是字符串、数字或空 + const min = opts.start ?? ''; + const max = opts.end ?? ''; + const unit = opts.unit ?? ''; + html += ` +
      + + ${unit} +
      `; + } + + html += `
      `; + container.append(html); + }); + + // ✅ 添加提交按钮区域 + container.append(` +
      + +
      + +
      +
      `); + return false; + }) } } };