Skip to content

Commit

Permalink
feat(category): Increase the classification activation status and opt…
Browse files Browse the repository at this point in the history
…imize the classification management function.

- Realize the display and selection function of classification tree structure-Optimize the layout of classification adding and editing interface
- Added category enable/disable actions
  • Loading branch information
ch3nnn committed Jan 6, 2025
1 parent f0d4cf2 commit 5033fb0
Show file tree
Hide file tree
Showing 9 changed files with 171 additions and 52 deletions.
11 changes: 7 additions & 4 deletions api/v1/category.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ type (
Level int32 `form:"level"` // 分类等级 1 一级分类 2 二级分类
Name string `form:"name"` // 菜单名称
Icon string `form:"icon"` // 图标
IsUsed bool `form:"is_used"` // 是否启用 1:是 0:否

}

CategoryCreateResp struct {
Expand Down Expand Up @@ -65,10 +67,11 @@ type (
}

CategoryDetailResp struct {
Id int `json:"id"` // 主键ID
Pid int `json:"pid"` // 父类ID
Name string `json:"name"` // 分类名称
Icon string `json:"icon"` // 图标
Id int `json:"id"` // 主键ID
Pid int `json:"pid"` // 父类ID
Name string `json:"name"` // 分类名称
Icon string `json:"icon"` // 图标
IsAdd bool `json:"is_add"` // 是否新增子分类
}
)

Expand Down
1 change: 1 addition & 0 deletions api/v1/site.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ type (
SiteCreateReq struct {
CategoryID int `form:"category_id"` // 类别ID
Url string `form:"url"` // 网址地址
IsUsed bool `form:"is_used"` // 是否启用
}

SiteCreateResp struct {
Expand Down
1 change: 1 addition & 0 deletions internal/dal/repository/st_site.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ func (d *customStSiteDao) FindSiteCategoryWithPage(page, pageSize int, result an
query.StCategory,
query.StCategory.ID.EqCol(query.StSite.CategoryID),
).
Order(query.StSite.CreatedAt.Desc()).
Scopes(whereFunc...).
ScanByPage(result, (page-1)*pageSize, pageSize)
}
2 changes: 1 addition & 1 deletion internal/service/category/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ func (s *service) Create(ctx context.Context, req *v1.CategoryCreateReq) (*v1.Ca
Title: req.Name,
Icon: req.Icon,
Level: req.Level,
IsUsed: false,
IsUsed: req.IsUsed,
})
if err != nil {
return nil, err
Expand Down
9 changes: 5 additions & 4 deletions internal/service/category/detail.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,10 @@ func (s *service) Detail(ctx context.Context, req *v1.CategoryDetailReq) (*v1.Ca
}

return &v1.CategoryDetailResp{
Id: category.ID,
Pid: category.ParentID,
Name: category.Title,
Icon: category.Icon,
Id: category.ID,
Pid: category.ParentID,
Name: category.Title,
Icon: category.Icon,
IsAdd: category.ParentID == 0,
}, err
}
1 change: 1 addition & 0 deletions internal/service/site/batchcreate.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ func (s *service) BatchCreate(ctx context.Context, req *v1.SiteCreateReq) (*v1.S
Description: desc,
URL: url,
CategoryID: req.CategoryID,
IsUsed: req.IsUsed,
})
if err != nil {
failCnt++
Expand Down
14 changes: 14 additions & 0 deletions web/static/admin/js/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/**
* 将扁平化列表转换为树形结构
* @param {Array} list - 扁平化列表
* @param {number} pid - 父节点 ID,默认为 0(根节点)
* @returns {Array} - 树形结构
*/
function buildTree(list, pid = 0) {
return list
.filter(item => item.pid === pid) // 过滤出当前层级的节点
.map(item => ({
...item,
children: buildTree(list, item.id) // 递归查找子节点
}));
}
94 changes: 68 additions & 26 deletions web/templates/category/category_view.html
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,21 @@
<!-- <input type="text" class="form-control" id="icon" placeholder="请输入分类图标">-->
</div>

<div class="form-group">
<label>是否启用</label>
<div class="clearfix">
<div class="custom-control custom-radio custom-control-inline">
<input type="radio" id="statusOne" name="customRadioInline" class="custom-control-input" value="0"
checked="">
<label class="custom-control-label" for="statusOne">禁用</label>
</div>
<div class="custom-control custom-radio custom-control-inline">
<input type="radio" id="statusTwo" name="customRadioInline" class="custom-control-input" value="1">
<label class="custom-control-label" for="statusTwo">启用</label>
</div>
</div>
</div>

<input type="hidden" id="id">

<button type="button" id="btnOk" class="btn btn-primary">提交</button>
Expand Down Expand Up @@ -89,17 +104,17 @@
// Font Awesome 图标列表
const icons = [
'linecons-music', 'linecons-search', 'linecons-mail', 'linecons-heart',
'linecons-star', 'linecons-user', 'linecons-videocam', 'linecons-camera',
'linecons-photo', 'linecons-attach', 'linecons-lock', 'linecons-eye',
'linecons-tag', 'linecons-thumbs-up', 'linecons-pencil', 'linecons-comment',
'linecons-location', 'linecons-cup', 'linecons-trash', 'linecons-doc',
'linecons-note', 'linecons-cog', 'linecons-params', 'linecons-calendar',
'linecons-sound', 'linecons-clock', 'linecons-lightbulb', 'linecons-tv',
'linecons-desktop', 'linecons-mobile', 'linecons-cd', 'linecons-inbox',
'linecons-globe', 'linecons-cloud', 'linecons-paper-plane', 'linecons-fire',
'linecons-graduation-cap', 'linecons-megaphone', 'linecons-database', 'linecons-key',
'linecons-beaker', 'linecons-truck', 'linecons-money', 'linecons-food', 'linecons-shop',
'linecons-diamond', 'linecons-t-shirt', 'linecons-wallet',
'linecons-star', 'linecons-user', 'linecons-videocam', 'linecons-camera',
'linecons-photo', 'linecons-attach', 'linecons-lock', 'linecons-eye',
'linecons-tag', 'linecons-thumbs-up', 'linecons-pencil', 'linecons-comment',
'linecons-location', 'linecons-cup', 'linecons-trash', 'linecons-doc',
'linecons-note', 'linecons-cog', 'linecons-params', 'linecons-calendar',
'linecons-sound', 'linecons-clock', 'linecons-lightbulb', 'linecons-tv',
'linecons-desktop', 'linecons-mobile', 'linecons-cd', 'linecons-inbox',
'linecons-globe', 'linecons-cloud', 'linecons-paper-plane', 'linecons-fire',
'linecons-graduation-cap', 'linecons-megaphone', 'linecons-database', 'linecons-key',
'linecons-beaker', 'linecons-truck', 'linecons-money', 'linecons-food', 'linecons-shop',
'linecons-diamond', 'linecons-t-shirt', 'linecons-wallet',
// 添加更多图标...
];

Expand All @@ -122,10 +137,8 @@
function (data) {
$("#level").append("<option value='0'>一级目录</option>");
$.each(data.list, function (index, value) {
if (value.level === 1){
if (value.pid === 0) {
$("#level").append("<option value='" + value.id + "'>" + value.name + "</option>");
}else{
$("#level").append("<option value='" + value.id + "'>&nbsp;&nbsp;&nbsp;" + value.name + "</option>");
}
});

Expand Down Expand Up @@ -158,14 +171,14 @@
field: 'sort',
title: '排序',
formatter: function (value, row, index) {
return '<input type="text" value="'+ row.sort +'" data-id="' + row.id + '" data-name="' + row.name + '" class="form-control sort" style="width:60px;">';
return '<input type="text" value="' + row.sort + '" data-id="' + row.id + '" data-name="' + row.name + '" class="form-control sort" style="width:60px;">';
}
},
{
field: 'link',
title: '分类图标',
formatter: function (value, row, index) {
return '<i class="'+ row.icon+ '"></i>'
return '<i class="' + row.icon + '"></i>'
}
},
{
Expand All @@ -179,7 +192,7 @@
} else {
is_checked = '';
}
return '<div class="custom-control custom-switch"><input type="checkbox" class="custom-control-input" id="'+ row.id + '"' + is_checked + '><label class="custom-control-label customSwitch" state="' + value + '"id="' + row.id + '" for="customSwitch' + row.id + ')"></label></div>';
return '<div class="custom-control custom-switch"><input type="checkbox" class="custom-control-input" id="' + row.id + '"' + is_checked + '><label class="custom-control-label customSwitch" state="' + value + '"id="' + row.id + '" for="customSwitch' + row.id + ')"></label></div>';
},
},
{
Expand Down Expand Up @@ -296,6 +309,24 @@
},
function (data) {
if (op === 'add') {
// 二级分类禁止创建
if (!data.is_add) {
$.alert({
title: '操作失败',
icon: 'mdi mdi-alert',
type: 'red',
content: '二级分类不可创建分类!',
buttons: {
okay: {
text: '关闭',
action: function () {
location.reload();
}
}
}
});
}

$('#level').selectpicker('val', data.id);
$("#level").selectpicker('refresh');
$("#level").attr("disabled", "");
Expand Down Expand Up @@ -378,7 +409,7 @@
let state = $(this).attr("state");
const id = $(this).attr("id");

const is_used = (state === 'true') ? false : true ;
const is_used = (state !== 'true');
const is_used_msg = (state === 'true') ? "禁用" : "启用";

$.confirm({
Expand Down Expand Up @@ -435,6 +466,16 @@
const icon = $("#icon").val();
const id = $("#id").val();

// 获取所有 name 为 customRadioInline 的单选按钮
const radios = document.querySelectorAll('input[name="customRadioInline"]');
// 遍历单选按钮,找到选中的值
let selectedValue;
radios.forEach(radio => {
if (radio.checked) {
selectedValue = radio.value;
}
});

if (level === "") {
$.alert({
title: '温馨提示',
Expand All @@ -460,17 +501,18 @@
name: name,
icon: icon[0],
id: id,
is_used: selectedValue === "1",
};


if (id != '' ){
msg = '分类:' + name + ' 修改完成。'
method = "PUT"
url = "/api/admin/category/update"
}else{
msg = '分类:' + name + ' 创建完成。'
method = "POST"
url = "/api/admin/category"
if (id !== '') {
msg = '分类:' + name + ' 修改完成。'
method = "PUT"
url = "/api/admin/category/update"
} else {
msg = '分类:' + name + ' 创建完成。'
method = "POST"
url = "/api/admin/category"
}

AjaxForm(
Expand Down
90 changes: 73 additions & 17 deletions web/templates/site/site_add.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"/>
<link rel="stylesheet" type="text/css" href="/assets/static/plugin/jquery-confirm/jquery-confirm.min.css">
<link rel="stylesheet" type="text/css" href="/assets/static/admin/css/bootstrap.min.css">
<link rel="stylesheet" type="text/css" href="/assets/static/plugin/bootstrap-multitabs/multitabs.min.css" >
<link rel="stylesheet" type="text/css" href="/assets/static/plugin/bootstrap-multitabs/multitabs.min.css">
<link rel="stylesheet" type="text/css" href="/assets/static/plugin/bootstrap-select/bootstrap-select.css">
<link rel="stylesheet" type="text/css" href="/assets/static/admin/css/style.min.css">
</head>
Expand All @@ -19,19 +19,35 @@
<div class="card-title">新增网站</div>
</div>
<div class="card-body">
<div class="alert alert-info" role="alert">注📢: 新增完成后根据网址自动获取标题、Logo、网站描述, 对内容不满可以点击编辑修改呦!</div>
<div class="alert alert-info" role="alert">注📢: 新增完成后根据网址自动获取标题、Logo、网站描述,
对内容不满可以点击编辑修改呦!
</div>
<form>
<div class="input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text">分类</span>
</div>
<select class="form-control select-picker col-lg-3" data-width="auto"
data-live-search="true" id="category_id">
</select>
<div class="input-group-prepend">
<span class="input-group-text">分类</span>
</div>
<select class="form-control selectpicker col-lg-3" id="category_id" tabindex="null"></select>
</div>
<div class="form-group">
<label>网站地址</label>
<textarea type="text" class="form-control" id="url" placeholder="URL 必须包含 http:// 或 https://且至少含有1个路径 例如: http://www.test.com/test.html,一行一个"></textarea>
<textarea type="text" class="form-control" id="url"
placeholder="URL 必须包含 http:// 或 https://且至少含有1个路径 例如: http://www.test.com/test.html,一行一个"></textarea>
</div>
<div class="form-group">
<label>网站启用</label>
<small class="help-block">网站关闭后将不能展示!</small>
<div class="clearfix">
<div class="custom-control custom-radio custom-control-inline">
<input type="radio" id="statusOne" name="customRadioInline" class="custom-control-input" value="0"
checked="">
<label class="custom-control-label" for="statusOne">禁用</label>
</div>
<div class="custom-control custom-radio custom-control-inline">
<input type="radio" id="statusTwo" name="customRadioInline" class="custom-control-input" value="1">
<label class="custom-control-label" for="statusTwo">启用</label>
</div>
</div>
</div>

<button type="button" id="btnOk" class="btn btn-primary">确认</button>
Expand All @@ -53,7 +69,36 @@
<script type="text/javascript" src="/assets/static/plugin/bootstrap-select/i18n/defaults-zh_CN.min.js"></script>
<script type="text/javascript" src="/assets/static/plugin/bootstrap-multitabs/multitabs.min.js"></script>
<script type="text/javascript" src="/assets/static/admin/js/httpclient.js"></script>
<script type="text/javascript" src="/assets/static/admin/js/utils.js"></script>

<script type="text/javascript">
/**
* 递归渲染树形结构到 <select>
* @param {Array} data - 树形结构数据
* @param {HTMLElement} parent - 父元素
* @param {number} level - 当前层级(用于缩进)
*/
function renderTreeToSelect(data, parent, level = 0) {
data.forEach(item => {
if (item.children && item.children.length > 0) {
// 如果是分组,创建 <optgroup>
const optgroup = document.createElement('optgroup');
optgroup.label = item.name;
parent.appendChild(optgroup);

// 递归渲染子节点
renderTreeToSelect(item.children, optgroup, level + 1);
} else {
// 如果是选项,创建 <option>
const option = document.createElement('option');
option.value = item.id;
option.text = ' '.repeat(level * 4) + item.name; // 缩进显示
parent.appendChild(option);
}
});
}


$(document).ready(function () {

AjaxFormNoAsync(
Expand All @@ -63,13 +108,14 @@
function () {
},
function (data) {
$("#category_id").append("<option value='-1'>一级目录</option>");
$.each(data.list, function (index, value) {
$("#category_id").append("<option value='" + value.id + "'>" + value.name + "</option>");
});

$("#category_id option:eq(0)").attr('selected', 'selected'); //选中第一个
$("#category_id").selectpicker('refresh');
// 将扁平化数据转换为树形结构
const treeData = buildTree(data.list);
// 获取 <select> 元素
const selectElement = document.getElementById('category_id');
// 渲染树形结构到 <select>
renderTreeToSelect(treeData, selectElement);
// 初始化 Bootstrap Select
$('#tree-select').selectpicker();
},
function (response) {
AjaxError(response);
Expand All @@ -82,7 +128,6 @@
});

$('#btnOk').on('click', function () {

const url = $("#url").val();
if (url === "") {
$.alert({
Expand All @@ -94,9 +139,20 @@
return false;
}

// 获取所有 name 为 customRadioInline 的单选按钮
const radios = document.querySelectorAll('input[name="customRadioInline"]');
// 遍历单选按钮,找到选中的值
let selectedValue;
radios.forEach(radio => {
if (radio.checked) {
selectedValue = radio.value;
}
});

const postData = {
category_id: $("#category_id").val(),
url: url,
is_used: selectedValue === "1",
};


Expand Down

0 comments on commit 5033fb0

Please sign in to comment.