文件
fenleixinxi/application/admin/controller/fc/Category.php
MiAn f13fb6cdab feat: 完成二级分类真实实拍图替换与功能优化
本次提交内容包括:
1. 替换13个二级分类为真实商业实拍图,同步更新本地与线上数据库
2. 新增会员拨打额度耗尽引导开通会员功能
3. 优化发布页分类选择逻辑,增加会员权益展示
4. 修复分类名称显示问题,完善相关交互逻辑
5. 新增图片同步工具脚本,确保线上线下数据一致性
2026-10-28 14:13:32 +08:00

231 行
7.6 KiB
PHP

此文件含有模棱两可的 Unicode 字符

此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。

<?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();
}
}