本次提交内容包括: 1. 替换13个二级分类为真实商业实拍图,同步更新本地与线上数据库 2. 新增会员拨打额度耗尽引导开通会员功能 3. 优化发布页分类选择逻辑,增加会员权益展示 4. 修复分类名称显示问题,完善相关交互逻辑 5. 新增图片同步工具脚本,确保线上线下数据一致性
231 行
7.6 KiB
PHP
231 行
7.6 KiB
PHP
<?php
|
||
|
||
namespace app\admin\controller\fc;
|
||
|
||
use app\common\controller\Backend;
|
||
use think\Db;
|
||
|
||
/**
|
||
* 同城-分类管理(支持一级主分类与各二级子品类)
|
||
*
|
||
* @icon fa fa-circle-o
|
||
*/
|
||
class Category extends Backend
|
||
{
|
||
|
||
/**
|
||
* FcCategory模型对象
|
||
* @var \app\admin\model\FcCategory
|
||
*/
|
||
protected $model = null;
|
||
|
||
protected $noNeedRight = ['selectpage'];
|
||
|
||
/**
|
||
* 初始化分类控制器并注入一级分类下拉选项
|
||
*
|
||
* @return void
|
||
*/
|
||
public function _initialize()
|
||
{
|
||
parent::_initialize();
|
||
$this->model = new \app\admin\model\FcCategory;
|
||
|
||
$parentList = [0 => '顶级分类(一级大类)'];
|
||
$topRows = Db::name('fc_category')
|
||
->where('pid', 0)
|
||
->order('sort asc, id asc')
|
||
->select();
|
||
foreach ($topRows as $r) {
|
||
$parentList[(int)$r['id']] = (string)$r['name'] . ((int)$r['enabled'] ? '' : ' (已停用)');
|
||
}
|
||
$this->view->assign('parentList', $parentList);
|
||
}
|
||
|
||
/**
|
||
* 分类列表(按一级分类 + 二级分类层级分组展示,支持附带父分类名称)
|
||
*
|
||
* @return string|\think\response\Json
|
||
*/
|
||
public function index()
|
||
{
|
||
$this->request->filter(['strip_tags', 'trim']);
|
||
if ($this->request->isAjax()) {
|
||
if ($this->request->request('keyField')) {
|
||
return $this->selectpage();
|
||
}
|
||
list($where, $sort, $order, $offset, $limit) = $this->buildparams();
|
||
|
||
// 分类总量有限:全量取回后按「父在前、子随父」的树形顺序在内存中重排,
|
||
// 再切片分页,避免数据库层先 limit 导致翻页后父子分组被打散。
|
||
$rows = $this->model->where($where)
|
||
->order('pid asc, sort asc, id asc')
|
||
->select();
|
||
$total = count($rows);
|
||
|
||
$rows = collection($rows)->toArray();
|
||
$parentMap = Db::name('fc_category')->where('pid', 0)->column('name', 'id');
|
||
|
||
// 树形顺序排列(一级分类紧跟其下属二级分类)
|
||
$byParent = [];
|
||
$tops = [];
|
||
$others = [];
|
||
foreach ($rows as &$r) {
|
||
$pid = isset($r['pid']) ? (int)$r['pid'] : 0;
|
||
$r['parent_name'] = $pid > 0 && isset($parentMap[$pid]) ? (string)$parentMap[$pid] : '顶级分类';
|
||
if (empty($r['image']) && !empty($r['icon_key'])) {
|
||
$r['image'] = $this->resolveImageFromKey($r['icon_key']);
|
||
}
|
||
if ($pid === 0) {
|
||
$tops[] = $r;
|
||
} else {
|
||
$byParent[$pid][] = $r;
|
||
$others[] = $r;
|
||
}
|
||
}
|
||
unset($r);
|
||
|
||
$ordered = [];
|
||
$usedIds = [];
|
||
foreach ($tops as $top) {
|
||
$ordered[] = $top;
|
||
$usedIds[$top['id']] = true;
|
||
$tid = (int)$top['id'];
|
||
if (!empty($byParent[$tid])) {
|
||
foreach ($byParent[$tid] as $child) {
|
||
$ordered[] = $child;
|
||
$usedIds[$child['id']] = true;
|
||
}
|
||
}
|
||
}
|
||
foreach ($others as $o) {
|
||
if (!isset($usedIds[$o['id']])) {
|
||
$ordered[] = $o;
|
||
}
|
||
}
|
||
|
||
return json(['total' => $total, 'rows' => array_slice($ordered, $offset, $limit)]);
|
||
}
|
||
return $this->view->fetch();
|
||
}
|
||
|
||
/**
|
||
* 新增分类(自动同步 image 与 icon_key)
|
||
*
|
||
* @return string
|
||
*/
|
||
public function add()
|
||
{
|
||
if ($this->request->isPost()) {
|
||
$params = $this->request->post('row/a', []);
|
||
$params = $this->normalizeCategoryParams($params);
|
||
$this->request->post(['row' => $params]);
|
||
}
|
||
return parent::add();
|
||
}
|
||
|
||
/**
|
||
* 编辑分类(自动补全图片路径并同步 image 与 icon_key)
|
||
*
|
||
* @param mixed $ids
|
||
* @return string
|
||
*/
|
||
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 ($this->request->isPost()) {
|
||
$params = $this->request->post('row/a', []);
|
||
$params = $this->normalizeCategoryParams($params, (int)$ids);
|
||
$this->request->post(['row' => $params]);
|
||
return parent::edit($ids);
|
||
}
|
||
if (empty($row['image']) && !empty($row['icon_key'])) {
|
||
$row['image'] = $this->resolveImageFromKey($row['icon_key']);
|
||
}
|
||
$this->view->assign("row", $row);
|
||
return $this->view->fetch();
|
||
}
|
||
|
||
/**
|
||
* 规范化分类参数:同步实拍图 image 与 icon_key,防止将自身设为上级分类
|
||
*
|
||
* @param array $params
|
||
* @param int $currentId
|
||
* @return array
|
||
*/
|
||
protected function normalizeCategoryParams(array $params, $currentId = 0)
|
||
{
|
||
$pid = isset($params['pid']) ? max(0, (int)$params['pid']) : 0;
|
||
if ($currentId > 0 && $pid === $currentId) {
|
||
$pid = 0;
|
||
}
|
||
$params['pid'] = $pid;
|
||
|
||
// 分类类型:farm=农产品(发布页显示产地规格字段),general=通用信息(招聘求职等)
|
||
// 二级分类强制继承上级类型;编辑一级分类时级联同步其下属二级分类
|
||
$kind = isset($params['kind']) ? trim((string)$params['kind']) : '';
|
||
if (!in_array($kind, ['farm', 'general'], true)) {
|
||
$kind = 'general';
|
||
}
|
||
if ($pid > 0) {
|
||
$parentKind = Db::name('fc_category')->where('id', $pid)->value('kind');
|
||
if ($parentKind === 'farm' || $parentKind === 'general') {
|
||
$kind = $parentKind;
|
||
}
|
||
} elseif ($currentId > 0) {
|
||
Db::name('fc_category')->where('pid', $currentId)->update(['kind' => $kind, 'updatetime' => time()]);
|
||
}
|
||
$params['kind'] = $kind;
|
||
|
||
$iconKey = isset($params['icon_key']) ? trim((string)$params['icon_key']) : '';
|
||
$image = isset($params['image']) ? trim((string)$params['image']) : '';
|
||
if ($image !== '' && $iconKey === '') {
|
||
$params['icon_key'] = $image;
|
||
} elseif ($iconKey !== '' && $image === '') {
|
||
$params['image'] = $this->resolveImageFromKey($iconKey);
|
||
} elseif ($iconKey !== '' && preg_match('/^(\/uploads\/|\/static\/|https?:\/\/)/i', $iconKey)) {
|
||
$params['image'] = $iconKey;
|
||
}
|
||
return $params;
|
||
}
|
||
|
||
/**
|
||
* 根据 icon_key 推导分类图片路径
|
||
*
|
||
* @param string $iconKey
|
||
* @return string
|
||
*/
|
||
protected function resolveImageFromKey($iconKey)
|
||
{
|
||
$iconKey = trim((string)$iconKey);
|
||
if ($iconKey === '') {
|
||
return '/static/icons/cat-service.png';
|
||
}
|
||
if (preg_match('/^(\/uploads\/|\/static\/|https?:\/\/)/i', $iconKey)) {
|
||
return $iconKey;
|
||
}
|
||
$file = strpos($iconKey, '.') !== false ? $iconKey : ($iconKey . '.png');
|
||
if (is_file(ROOT_PATH . 'public/static/categories/' . $file)) {
|
||
return '/static/categories/' . $file;
|
||
}
|
||
return '/static/icons/' . $file;
|
||
}
|
||
|
||
/**
|
||
* SelectPage搜索(供信息/草稿表单选择同城分类)
|
||
*
|
||
* @internal
|
||
*/
|
||
public function selectpage()
|
||
{
|
||
return parent::selectpage();
|
||
}
|
||
}
|