文件
fenleixinxi/public/assets/js/backend/fc/category.js
MiAn d812e54f8e build: 更新正式环境后端域名并删除无用公钥文件
删除了不再使用的公钥文件 pub_key.pem,同时将生产环境的后端域名从原地址更新为新的正式域名,确保项目配置与线上环境匹配。
2026-10-30 15:23:27 +08:00

510 行
24 KiB
JavaScript

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

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

define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'backend/format'], function ($, undefined, Backend, Table, Form, Format) {
/**
* categories 目录下的专属图标键名列表
*/
var CATEGORY_DIR_KEYS = [
'cat-fruit', 'cat-poultry', 'cat-veg', 'cat-job',
'fruit-apple', 'fruit-grape', 'fruit-kiwi', 'fruit-orange', 'fruit-passion', 'fruit-peach', 'fruit-strawberry', 'fruit-watermelon',
'veg-cabbage', 'veg-chili', 'veg-green-cabbage', 'veg-greens', 'veg-purple-cabbage', 'veg-radish', 'veg-tomato'
];
/**
* 解析分类图标/实拍图地址
* @param {string} iconKey 标识或路径
* @param {number} id 分类ID
* @param {string} image 实拍图路径(可选)
* @returns {string} 完整的静态图片URL
*/
function resolveIconUrl(iconKey, id, image) {
if (image && /^(https?:\/\/|\/uploads\/|\/static\/)/i.test(image)) {
return image;
}
if (!iconKey) {
iconKey = 'cat-job';
}
if (/^(https?:\/\/|\/uploads\/|\/static\/)/i.test(iconKey)) {
return iconKey;
}
var baseKey = iconKey.replace(/\.(png|jpg|jpeg|gif|svg)$/i, '');
var file = (iconKey.indexOf('.') !== -1) ? iconKey : iconKey + '.png';
if (iconKey.indexOf('sub-') === 0 || CATEGORY_DIR_KEYS.indexOf(baseKey) !== -1 || /^(veg-|fruit-|cat-veg|cat-fruit|cat-job|cat-poultry)/i.test(baseKey)) {
return '/static/categories/' + file + '?v=2';
}
return '/static/icons/' + file + '?v=2';
}
var Controller = {
index: function () {
// 初始化表格参数配置
Table.api.init({
extend: {
index_url: 'fc/category/index' + location.search,
add_url: 'fc/category/add',
edit_url: 'fc/category/edit',
del_url: 'fc/category/del',
multi_url: 'fc/category/multi',
import_url: 'fc/category/import',
table: 'fc_category',
}
});
var table = $("#table");
// 初始化表格
table.bootstrapTable({
url: $.fn.bootstrapTable.defaults.extend.index_url,
pk: 'id',
sortName: 'sort',
sortOrder: 'asc',
escape: false,
columns: [
[
{checkbox: true, width: '40px'},
{
field: 'id',
title: __('SerialNo'),
operate: false,
sortable: false,
align: 'center',
width: '60px',
class: 'fc-serial',
table: table,
formatter: Format.serial
},
// 1. 图标/实拍图预览(大图圆角卡,object-fit:cover)
{
field: 'image',
title: '图标/图片',
operate: false,
align: 'center',
width: '90px',
formatter: function (value, row, index) {
var url = resolveIconUrl(row.icon_key, row.id, row.image);
var color = row.color || '#00E070';
return '<div class="fc-img-wrap">' +
'<div style="display:inline-block;width:56px;height:56px;border-radius:14px;overflow:hidden;background:' + color + '15;border:1px solid ' + color + '40;box-shadow:0 2px 8px rgba(4,48,30,0.08);cursor:zoom-in;">' +
'<img src="' + url + '" style="width:100%;height:100%;object-fit:cover;display:block;" onerror="this.src=\'/static/categories/cat-job.png\'" />' +
'</div>' +
'<div class="fc-img-zoom"><img src="' + url + '" onerror="this.src=\'/static/categories/cat-job.png\'" /></div>' +
'</div>';
}
},
// 2. 分类名称(含层级缩进,带主题色小圆点)
{
field: 'name',
title: __('Name'),
operate: 'LIKE',
align: 'left',
width: '200px',
formatter: function (value, row, index) {
var color = row.color || '#00E070';
var isSub = Number(row.pid || 0) > 0;
var dot = '<span style="flex:0 0 auto;display:inline-block;width:10px;height:10px;border-radius:50%;background:' + color + ';margin-right:10px;vertical-align:middle;box-shadow:0 0 6px ' + color + '99;"></span>';
var nameStyle = isSub
? 'font-weight:500;font-size:13.5px;color:#475569;'
: 'font-weight:700;font-size:14px;color:#1e293b;';
return '<div class="' + (isSub ? 'fc-tree-sub' : '') + '" style="display:flex;align-items:center;">' + dot + '<span style="' + nameStyle + '">' + value + '</span></div>';
}
},
// 2.5 所属大类(关联实际 pid 字段,支持普通搜索按大类下拉过滤)
{
field: 'pid',
title: '所属分类',
align: 'center',
width: '130px',
searchList: (Config && Config.parentList) ? Config.parentList : {},
formatter: function (value, row, index) {
if (!row.pid || Number(row.pid) === 0) {
return '<span class="label" style="background:#ecfdf5;color:#059669;border:1px solid #a7f3d0;border-radius:6px;padding:3px 8px;font-weight:600;">一级大类</span>';
}
return '<span class="label" style="background:#f1f5f9;color:#475569;border-radius:6px;padding:3px 8px;">' + (row.parent_name || ('#' + row.pid)) + '</span>';
}
},
// 2.6 分类类型(决定发布页是否显示产地规格字段,支持下拉筛选)
{
field: 'kind',
title: '类型',
align: 'center',
width: '100px',
searchList: {'farm': '农产品', 'general': '通用信息'},
formatter: function (value, row, index) {
if (value === 'farm') {
return '<span class="label" style="background:#ecfdf5;color:#059669;border:1px solid #a7f3d0;border-radius:6px;padding:3px 8px;font-weight:600;">农产品</span>';
}
return '<span class="label" style="background:#eff6ff;color:#2563eb;border:1px solid #bfdbfe;border-radius:6px;padding:3px 8px;">通用信息</span>';
}
},
// 4. 排序权重
{
field: 'sort',
title: __('Sort'),
align: 'center',
width: '110px',
sortable: true,
formatter: function (value, row, index) {
return '<span class="badge" style="background:#eef7f2;color:#04874a;font-weight:700;font-size:13px;padding:4px 10px;border-radius:8px;border:1px solid #bcead1;">' + value + '</span>';
}
},
// 5. 发布帖子数
{
field: 'post_count',
title: __('Post_count'),
align: 'center',
width: '120px',
sortable: true,
formatter: function (value, row, index) {
var count = Number(value || 0);
var bg = count > 0 ? '#f0fdf4' : '#f8f9fa';
var text = count > 0 ? '#15803d' : '#888';
return '<span class="label" style="background:' + bg + ';color:' + text + ';border-radius:8px;padding:4px 10px;font-size:12px;font-weight:600;display:inline-block;">' + count + ' 篇</span>';
}
},
// 6. 启用状态(原生点击切换开关)
{
field: 'enabled',
title: __('Enabled'),
align: 'center',
width: '100px',
searchList: {0: '已禁用', 1: '已启用'},
formatter: Table.api.formatter.toggle
},
// 6.5 首页金刚区推荐(原生点击切换开关,最多8个二级品类)
{
field: 'is_home',
title: '首页金刚区',
align: 'center',
width: '120px',
searchList: {0: '未推荐', 1: '已推荐'},
formatter: function (value, row, index) {
if (!row.pid || Number(row.pid) === 0) {
return '<span class="text-muted" style="color:#94a3b8;font-size:12px;">— 一级不展示 —</span>';
}
return Table.api.formatter.toggle.call(this, value, row, index);
}
},
// 7. 更新时间
{
field: 'updatetime',
title: __('Updatetime'),
operate: 'RANGE',
addclass: 'datetimerange',
autocomplete: false,
align: 'center',
width: '180px',
formatter: Table.api.formatter.datetime
},
// 8. 操作栏
{
field: 'operate',
title: __('Operate'),
table: table,
align: 'center',
width: '120px',
events: Table.api.events.operate,
formatter: Table.api.formatter.operate
}
]
]
});
// 为表格绑定事件
Table.api.bindevent(table);
/**
* 快捷层级与状态筛选(全部/一级大类/二级品类/首页金刚区推荐)
* @param {Object} filterObj 过滤参数键值对
* @param {Object} opObj 操作符参数键值对
* @param {HTMLElement} activeBtn 触发按钮
*/
function applyQuickFilter(filterObj, opObj, activeBtn) {
$('.fc-quick-filters .btn').removeClass('active');
if (activeBtn) {
$(activeBtn).addClass('active');
}
var options = table.bootstrapTable('getOptions');
var queryParams = options.queryParams;
options.pageNumber = 1;
options.queryParams = function (params) {
var p = typeof queryParams === 'function' ? queryParams(params) : params;
var filter = p.filter ? JSON.parse(p.filter) : {};
var op = p.op ? JSON.parse(p.op) : {};
// 清理快捷筛选旧字段,避免状态叠加冲突
delete filter.pid;
delete op.pid;
delete filter.is_home;
delete op.is_home;
// 合并新条件
$.extend(filter, filterObj);
$.extend(op, opObj);
p.filter = JSON.stringify(filter);
p.op = JSON.stringify(op);
return p;
};
table.bootstrapTable('refresh', {});
}
// 全部
$(document).on("click", ".btn-filter-all", function () {
applyQuickFilter({}, {}, this);
});
// 仅看一级大类 (pid = 0)
$(document).on("click", ".btn-filter-top", function () {
applyQuickFilter({pid: 0}, {pid: '='}, this);
});
// 仅看二级品类 (pid > 0)
$(document).on("click", ".btn-filter-sub", function () {
applyQuickFilter({pid: 0}, {pid: '>'}, this);
});
// 快捷筛选:只看首页金刚区推荐分类 (is_home = 1)
$(document).on("click", ".btn-filter-home", function () {
applyQuickFilter({is_home: 1}, {is_home: '='}, this);
});
},
add: function () {
Controller.api.bindFormInteractions();
Controller.api.bindevent();
},
edit: function () {
Controller.api.bindFormInteractions();
Controller.api.bindevent();
},
api: {
bindevent: function () {
Form.api.bindevent($("form[role=form]"));
},
/**
* 绑定新增与编辑表单的高级可视化交互
*/
bindFormInteractions: function () {
var $iconInput = $('#c-icon_key');
var $nameInput = $('#c-name');
var $colorInput = $('#c-color');
var $colorNative = $('#c-color-native');
var $colorAddon = $('#color-preview-addon');
var $previewBadge = $('#preview-badge');
var $previewIcon = $('#preview-icon');
var $previewName = $('#preview-name');
var $previewKind = $('#preview-kind');
var $currentIconImg = $('#current-icon-img');
var $currentIconLabel = $('#current-icon-label');
var $iconModal = $('#fc-icon-modal');
var $iconSearch = $('#fc-icon-search');
var $iconTabs = $('.fc-icon-tab-btn');
// 刷新界面预览元素
function updateLivePreview() {
var name = $nameInput.val() || '未命名分类';
var key = $iconInput.val() || 'cat-job';
var color = $colorInput.val() || '#00E070';
var iconUrl = resolveIconUrl(key);
var kind = $('input[name="row[kind]"]:checked').val() || 'general';
var $matchedChip = $('.fc-icon-chip[data-key="' + key + '"]');
if ($matchedChip.length) {
var chipImg = $matchedChip.find('img').attr('src');
if (chipImg) {
iconUrl = chipImg;
}
}
if ($previewName.length) $previewName.text(name);
if ($previewIcon.length) $previewIcon.attr('src', iconUrl);
if ($currentIconImg.length) $currentIconImg.attr('src', iconUrl);
if ($currentIconLabel.length) {
if ($matchedChip.length) {
$currentIconLabel.text($matchedChip.data('name') || key);
} else if (/^(\/uploads\/|https?:\/\/)/i.test(key)) {
$currentIconLabel.text('自定义上传图片');
} else {
$currentIconLabel.text(key);
}
}
if ($previewKind.length) {
if (kind === 'farm') {
$previewKind.text('农产品类目').removeClass('general').addClass('farm');
} else {
$previewKind.text('通用便民类目').removeClass('farm').addClass('general');
}
}
if ($previewBadge.length) {
$previewBadge.css({
'background': color + '1a',
'border-color': color + '4d',
'box-shadow': '0 6px 16px ' + color + '26'
});
}
if ($colorAddon.length) $colorAddon.css('background', color);
if ($colorNative.length) $colorNative.val(color);
}
// 确保初始状态完全隐藏
$iconModal.removeClass('open').attr('style', 'display:none !important;');
// 1. 打开插画挑选面板
$(document).on('click', '#btn-open-icon-picker', function (e) {
e.preventDefault();
e.stopPropagation();
$iconModal.removeAttr('style').addClass('open').fadeIn(150);
// 打开时定位高亮当前图标
var curKey = $iconInput.val();
var $activeChip = $('.fc-icon-chip[data-key="' + curKey + '"]');
$('.fc-icon-chip').removeClass('active');
if ($activeChip.length) {
$activeChip.addClass('active');
}
if ($iconSearch.length) {
$iconSearch.focus();
}
});
// 关闭插画挑选面板
function closeIconModal() {
$iconModal.removeClass('open').fadeOut(120, function () {
$(this).attr('style', 'display:none !important;');
});
}
$(document).on('click', '#btn-close-icon-modal, .fc-modal-overlay', function (e) {
e.preventDefault();
closeIconModal();
});
// 2. 点选图标卡片
$(document).on('click', '.fc-icon-chip', function (e) {
e.preventDefault();
var key = $(this).data('key');
var iconName = $(this).data('name') || '';
var iconColor = $(this).data('color') || '';
$iconInput.val(key).trigger('change');
$('.fc-icon-chip').removeClass('active');
$(this).addClass('active');
// 若名称尚未输入,自动以图标名称填充
if ($nameInput.length && !$nameInput.val().trim() && iconName) {
$nameInput.val(iconName).trigger('input');
}
// 若当前主题色为默认春绿且图标带有推荐色,可智能联动
if (iconColor && ($colorInput.val() === '#00E070' || !$colorInput.val())) {
$colorInput.val(iconColor).trigger('change');
}
updateLivePreview();
closeIconModal();
});
// 2.1 图标分类 Tab 切换
$(document).on('click', '.fc-icon-tab-btn', function (e) {
e.preventDefault();
$('.fc-icon-tab-btn').removeClass('active');
$(this).addClass('active');
var targetGroup = $(this).data('group');
filterChips();
});
// 2.2 图标搜索过滤
if ($iconSearch.length) {
$iconSearch.on('input', function () {
filterChips();
});
}
// 过滤卡片执行逻辑
function filterChips() {
var group = $('.fc-icon-tab-btn.active').data('group') || 'all';
var keyword = ($iconSearch.val() || '').trim().toLowerCase();
var visibleCount = 0;
$('.fc-icon-chip').each(function () {
var chipGroup = $(this).data('group') || '';
var chipKey = ($(this).data('key') || '').toLowerCase();
var chipName = ($(this).data('name') || '').toLowerCase();
var matchGroup = (group === 'all' || chipGroup === group);
var matchKeyword = (!keyword || chipName.indexOf(keyword) !== -1 || chipKey.indexOf(keyword) !== -1);
if (matchGroup && matchKeyword) {
$(this).show();
visibleCount++;
} else {
$(this).hide();
}
});
var $emptyTip = $('#fc-icon-empty');
if (visibleCount === 0) {
if (!$emptyTip.length) {
$('.fc-modal-grid').append('<div id="fc-icon-empty" style="width:100%;text-align:center;padding:32px 10px;color:#94a3b8;font-size:13px;"><i class="fa fa-folder-open-o" style="font-size:24px;display:block;margin-bottom:8px;color:#cbd5e1;"></i>未找到匹配的预设图标,您可直接使用「上传自定义图标」</div>');
} else {
$emptyTip.show();
}
} else if ($emptyTip.length) {
$emptyTip.hide();
}
}
// 3. 自定义上传图片监听
$iconInput.on('input change', function () {
updateLivePreview();
});
// 4. 监听分类名称输入
$nameInput.on('input', function () {
updateLivePreview();
});
// 4.1 监听分类类型单选变更
$('input[name="row[kind]"]').on('change', function () {
updateLivePreview();
});
// 5. 监听颜色输入与推荐调色板点选
$colorInput.on('input change', function () {
updateLivePreview();
});
// 5.1 原生取色器 → 十六进制输入框
$colorNative.on('input change', function () {
$colorInput.val($(this).val()).trigger('change');
});
$(document).on('click', '.fc-color-presets .fc-color-dot', function () {
var color = $(this).data('color');
$colorInput.val(color).trigger('change');
updateLivePreview();
});
// 初始化回显
var initKey = $iconInput.val() || 'cat-service';
$('.fc-icon-chip[data-key="' + initKey + '"]').addClass('active');
updateLivePreview();
}
}
};
return Controller;
});