feat: 新增电话拨打功能及相关配置,完善农产品分类与地区数据
- 新增商家/信息详情页电话拨打接口,支持每日额度限制与拨打日志记录 - 新增平台免责声明内容及协议接口,完善用户服务协议相关配置 - 新增农产品分类图标与上市角标展示,优化分类管理界面样式 - 新增全国行政区划工具类与城市选择组件,重构地区数据结构 - 新增SSH自检与部署脚本,完善项目部署流程
@@ -1,19 +1,26 @@
|
||||
define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'backend/format'], function ($, undefined, Backend, Table, Form, Format) {
|
||||
|
||||
/**
|
||||
* 解析分类图标地址
|
||||
* 解析分类图标/实拍图地址
|
||||
* @param {string} iconKey 标识或路径
|
||||
* @param {number} id 分类ID
|
||||
* @param {string} image 实拍图路径(可选)
|
||||
* @returns {string} 完整的静态图片URL
|
||||
*/
|
||||
function resolveIconUrl(iconKey, id) {
|
||||
function resolveIconUrl(iconKey, id, image) {
|
||||
if (image && /^(https?:\/\/|\/uploads\/|\/static\/)/i.test(image)) {
|
||||
return image;
|
||||
}
|
||||
if (!iconKey) {
|
||||
iconKey = 'cat-job';
|
||||
iconKey = 'cat-veg';
|
||||
}
|
||||
if (/^(https?:\/\/|\/uploads\/|\/static\/)/i.test(iconKey)) {
|
||||
return iconKey;
|
||||
}
|
||||
var file = (iconKey.indexOf('.') !== -1) ? iconKey : iconKey + '.png';
|
||||
if (iconKey.indexOf('sub-') === 0 || iconKey === 'cat-veg' || iconKey === 'cat-fruit') {
|
||||
return '/static/categories/' + file + '?v=2';
|
||||
}
|
||||
return '/static/icons/' + file + '?v=2';
|
||||
}
|
||||
|
||||
@@ -56,23 +63,26 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'backend/format'], fu
|
||||
formatter: Format.serial
|
||||
},
|
||||
|
||||
// 1. 图标预览(微质感微卡片)
|
||||
// 1. 实拍图预览(大图圆角卡,object-fit:cover 保证「看图识物」效果)
|
||||
{
|
||||
field: 'icon_preview',
|
||||
title: __('Icon'),
|
||||
field: 'image',
|
||||
title: '实拍图',
|
||||
operate: false,
|
||||
align: 'center',
|
||||
width: '80px',
|
||||
width: '90px',
|
||||
formatter: function (value, row, index) {
|
||||
var url = resolveIconUrl(row.icon_key, row.id);
|
||||
var url = resolveIconUrl(row.icon_key, row.id, row.image);
|
||||
var color = row.color || '#00E070';
|
||||
return '<div style="display:inline-flex;align-items:center;justify-content:center;width:40px;height:40px;border-radius:10px;background:' + color + '18;border:1px solid ' + color + '40;padding:2px;">' +
|
||||
'<img src="' + url + '" style="width:28px;height:28px;object-fit:contain;" onerror="this.src=\'/static/icons/cat-job.png\'" />' +
|
||||
return '<div class="fc-img-wrap">' +
|
||||
'<div style="display:inline-block;width:60px;height:60px;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-veg.png\'" />' +
|
||||
'</div>' +
|
||||
'<div class="fc-img-zoom"><img src="' + url + '" onerror="this.src=\'/static/categories/cat-veg.png\'" /></div>' +
|
||||
'</div>';
|
||||
}
|
||||
},
|
||||
|
||||
// 2. 分类名称(留足宽度,带主题色小圆点)
|
||||
// 2. 分类名称(含层级缩进,带主题色小圆点)
|
||||
{
|
||||
field: 'name',
|
||||
title: __('Name'),
|
||||
@@ -81,21 +91,42 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'backend/format'], fu
|
||||
width: '200px',
|
||||
formatter: function (value, row, index) {
|
||||
var color = row.color || '#00E070';
|
||||
var dot = '<span style="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>';
|
||||
return '<div style="display:flex;align-items:center;">' + dot + '<span style="font-weight:600;font-size:14px;color:#2c3e50;">' + value + '</span></div>';
|
||||
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>';
|
||||
}
|
||||
},
|
||||
|
||||
// 3. 主题色
|
||||
// 2.5 所属大类
|
||||
{
|
||||
field: 'color',
|
||||
title: __('Color'),
|
||||
operate: 'LIKE',
|
||||
field: 'parent_name',
|
||||
title: '上级分类',
|
||||
operate: false,
|
||||
align: 'center',
|
||||
width: '130px',
|
||||
width: '120px',
|
||||
formatter: function (value, row, index) {
|
||||
var color = value || '#00E070';
|
||||
return '<span class="badge" style="background:' + color + ';color:#ffffff;font-size:12px;font-family:monospace;padding:4px 10px;border-radius:6px;text-shadow:0 1px 2px rgba(0,0,0,0.3);">' + color + '</span>';
|
||||
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;">' + (value || ('#' + row.pid)) + '</span>';
|
||||
}
|
||||
},
|
||||
|
||||
// 2.6 分类类型(决定发布页是否显示产地规格字段)
|
||||
{
|
||||
field: 'kind',
|
||||
title: '类型',
|
||||
operate: false,
|
||||
align: 'center',
|
||||
width: '100px',
|
||||
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>';
|
||||
}
|
||||
},
|
||||
|
||||
@@ -188,6 +219,7 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'backend/format'], fu
|
||||
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');
|
||||
@@ -198,7 +230,7 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'backend/format'], fu
|
||||
// 刷新界面预览元素(纯视觉图形,无死板业务文字)
|
||||
function updateLivePreview() {
|
||||
var name = $nameInput.val() || '未命名分类';
|
||||
var key = $iconInput.val() || 'cat-job';
|
||||
var key = $iconInput.val() || 'cat-veg';
|
||||
var color = $colorInput.val() || '#00E070';
|
||||
var iconUrl = resolveIconUrl(key);
|
||||
|
||||
@@ -213,6 +245,7 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'backend/format'], fu
|
||||
});
|
||||
}
|
||||
if ($colorAddon.length) $colorAddon.css('background', color);
|
||||
if ($colorNative.length) $colorNative.val(color);
|
||||
}
|
||||
|
||||
// 确保初始状态完全隐藏
|
||||
@@ -263,6 +296,11 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'backend/format'], fu
|
||||
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');
|
||||
@@ -270,7 +308,7 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'backend/format'], fu
|
||||
});
|
||||
|
||||
// 初始化回显
|
||||
var initKey = $iconInput.val() || 'cat-job';
|
||||
var initKey = $iconInput.val() || 'cat-veg';
|
||||
$('.fc-icon-chip[data-key="' + initKey + '"]').addClass('active');
|
||||
updateLivePreview();
|
||||
}
|
||||
|
||||
|
之后 宽度: | 高度: | 大小: 5.9 KiB |
二进制
public/static/categories/cat-veg.png
普通文件
|
之后 宽度: | 高度: | 大小: 6.6 KiB |
|
之后 宽度: | 高度: | 大小: 3.4 KiB |
|
之后 宽度: | 高度: | 大小: 4.0 KiB |
|
之后 宽度: | 高度: | 大小: 6.7 KiB |
|
之后 宽度: | 高度: | 大小: 4.1 KiB |
|
之后 宽度: | 高度: | 大小: 4.3 KiB |
|
之后 宽度: | 高度: | 大小: 4.5 KiB |
|
之后 宽度: | 高度: | 大小: 3.4 KiB |
|
之后 宽度: | 高度: | 大小: 4.0 KiB |
|
之后 宽度: | 高度: | 大小: 4.0 KiB |
|
之后 宽度: | 高度: | 大小: 4.4 KiB |
|
之后 宽度: | 高度: | 大小: 3.9 KiB |
|
之后 宽度: | 高度: | 大小: 4.4 KiB |
|
之后 宽度: | 高度: | 大小: 4.9 KiB |
|
之后 宽度: | 高度: | 大小: 3.4 KiB |
|
之后 宽度: | 高度: | 大小: 4.7 KiB |
二进制
public/static/icons/cat-fruit.png
普通文件
|
之后 宽度: | 高度: | 大小: 5.9 KiB |
二进制
public/static/icons/cat-veg.png
普通文件
|
之后 宽度: | 高度: | 大小: 6.6 KiB |