Skip to content

Commit 7acf323

Browse files
committed
feat(site): add option to save URL only on failure
- Add 'fail_switch' field to Site struct - Implement logic to save URL only if title retrieval fails - Update batch create service to use new fail switch option - Modify list view to highlight rows where URL and title are the same - Update site add form to include fail switch checkbox - Upgrade lancet dependency to v2.3.4
1 parent 705ccb4 commit 7acf323

File tree

7 files changed

+53
-36
lines changed

7 files changed

+53
-36
lines changed

api/v1/site.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import (
1313

1414
type Site struct {
1515
Id int `json:"id"` // ID
16-
Thumb string `json:"thumb"` // 网站 logo
16+
Icon string `json:"icon"` // 网站 logo
1717
Title string `json:"title"` // 名称简介
1818
Url string `json:"url"` // 链接
1919
Category string `json:"category"` // 分类
@@ -60,6 +60,7 @@ type (
6060
CategoryID int `form:"category_id"` // 类别ID
6161
Url string `form:"url"` // 网址地址
6262
IsUsed bool `form:"is_used"` // 是否启用
63+
FailSwitch bool `form:"fail_switch"` // 失败开关
6364
}
6465

6566
SiteCreateResp struct {

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ go 1.22
55
require (
66
github.com/Pacific73/gorm-cache v1.1.1
77
github.com/disintegration/imaging v1.6.2
8-
github.com/duke-git/lancet/v2 v2.3.1
8+
github.com/duke-git/lancet/v2 v2.3.4
99
github.com/dustin/go-humanize v1.0.1
1010
github.com/gin-gonic/gin v1.10.0
1111
github.com/glebarez/sqlite v1.11.0

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/r
3838
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
3939
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
4040
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
41-
github.com/duke-git/lancet/v2 v2.3.1 h1:cYZHQp57CZKP41EFkV/7TGbUrmhjaPMI5vi3Q+9KJNo=
42-
github.com/duke-git/lancet/v2 v2.3.1/go.mod h1:zGa2R4xswg6EG9I6WnyubDbFO/+A/RROxIbXcwryTsc=
41+
github.com/duke-git/lancet/v2 v2.3.4 h1:8XGI7P9w+/GqmEBEXYaH/XuNiM0f4/90Ioti0IvYJls=
42+
github.com/duke-git/lancet/v2 v2.3.4/go.mod h1:zGa2R4xswg6EG9I6WnyubDbFO/+A/RROxIbXcwryTsc=
4343
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
4444
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
4545
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=

internal/service/site/batchcreate.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"context"
1010
"strings"
1111

12+
"github.com/duke-git/lancet/v2/condition"
1213
"golang.org/x/sync/errgroup"
1314

1415
v1 "github.com/ch3nnn/webstack-go/api/v1"
@@ -53,12 +54,14 @@ func (s *service) BatchCreate(ctx context.Context, req *v1.SiteCreateReq) (*v1.S
5354
})
5455

5556
if err := g.Wait(); err != nil {
56-
failURLs = append(failURLs, url)
57-
return
57+
if !req.FailSwitch {
58+
failURLs = append(failURLs, url)
59+
return
60+
}
5861
}
5962

6063
_, err := s.siteRepository.WithContext(ctx).Create(&model.StSite{
61-
Title: title,
64+
Title: condition.Ternary(title != "", title, url),
6265
Icon: icon,
6366
Description: desc,
6467
URL: url,

internal/service/site/list.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ func (s *service) List(ctx context.Context, req *v1.SiteListReq) (resp *v1.SiteL
3939
for i, siteCategory := range siteCategories {
4040
list[i] = v1.Site{
4141
Id: siteCategory.StSite.ID,
42-
Thumb: siteCategory.StSite.Icon,
42+
Icon: siteCategory.StSite.Icon,
4343
Title: siteCategory.StSite.Title,
4444
Url: siteCategory.StSite.URL,
4545
Category: siteCategory.StCategory.Title,

web/templates/site/site_add.html

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,7 @@
1919
<div class="card-title">新增网站</div>
2020
</div>
2121
<div class="card-body">
22-
<div class="alert alert-info" role="alert">注📢: 新增完成后根据网址自动获取标题、Logo、网站描述, 对内容不满可以点击编辑修改呦!
23-
</div>
22+
<div class="alert alert-info" role="alert">注📢: 新增完成后根据网址自动获取标题、Logo、网站描述, 对内容不满可以点击编辑修改呦!</div>
2423
<form>
2524
<div class="input-group mb-3">
2625
<div class="input-group-prepend">
@@ -31,14 +30,16 @@
3130
<div class="form-group">
3231
<label>网站地址</label>
3332
<small class="help-block">(支持多 URL 添加)</small>
34-
<textarea type="text" class="form-control" id="url" placeholder="必须包含 http:// 或 https://且至少含有1个路径。例如: http://www.test.com/test.html 一行一个呦!"></textarea>
33+
<textarea type="text" class="form-control" id="url"
34+
placeholder="必须包含 http:// 或 https://且至少含有1个路径。例如: http://www.test.com/test.html 一行一个呦!"></textarea>
3535
</div>
3636
<div class="form-group">
3737
<label>网站启用</label>
3838
<small class="help-block">(网站关闭后将不能展示!)</small>
3939
<div class="clearfix">
4040
<div class="custom-control custom-radio custom-control-inline">
41-
<input type="radio" id="statusOne" name="customRadioInline" class="custom-control-input" value="0" checked=""/>
41+
<input type="radio" id="statusOne" name="customRadioInline" class="custom-control-input" value="0"
42+
checked=""/>
4243
<label class="custom-control-label" for="statusOne">禁用</label>
4344
</div>
4445
<div class="custom-control custom-radio custom-control-inline">
@@ -48,6 +49,14 @@
4849
</div>
4950
</div>
5051

52+
<div class="form-group">
53+
<label>拉取失败时仅保存网址</label>
54+
<div class="custom-control custom-switch">
55+
<input type="checkbox" class="custom-control-input" id="allowSaveUrlOnFailure">
56+
<label class="custom-control-label" for="allowSaveUrlOnFailure">启用</label>
57+
</div>
58+
</div>
59+
5160
<button type="button" id="btnOk" class="btn btn-primary">确认</button>
5261
<button type="button" id="btnLoading" class="btn btn-primary" disabled style="display: none">
5362
<span class="spinner-grow spinner-grow-sm" role="status" aria-hidden="true"></span>
@@ -74,7 +83,8 @@
7483
"GET",
7584
"/api/admin/category",
7685
"",
77-
function () {},
86+
function () {
87+
},
7888
function (data) {
7989
// 将扁平化数据转换为树形结构
8090
const treeData = buildTree(data.list);
@@ -106,6 +116,7 @@
106116
});
107117
return false;
108118
}
119+
const failSwitch = $("#allowSaveUrlOnFailure").is(":checked");
109120
// 获取所有 name 为 customRadioInline 的单选按钮
110121
const radios = document.querySelectorAll('input[name="customRadioInline"]');
111122
// 遍历单选按钮,找到选中的值
@@ -120,6 +131,7 @@
120131
category_id: $("#category_id").val(),
121132
url: url,
122133
is_used: selectedValue === "1",
134+
fail_switch: failSwitch,
123135
};
124136

125137
AjaxForm(

web/templates/site/site_list.html

Lines changed: 24 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,9 @@
3737
</div>
3838
</div>
3939
<div class="card-body">
40+
<div class="alert alert-info" role="alert">注📢: 勾选"拉取失败时仅保存网址"功能对应行背景色高亮, 记得点击编辑修改呦!</div>
4041
<div class="table-responsive">
41-
<table class="table table-bordered">
42+
<table class="table table-bordered" id="siteTable">
4243
<thead>
4344
<tr>
4445
<th>ID</th>
@@ -56,9 +57,7 @@
5657
<tbody class="tbody"></tbody>
5758
</table>
5859
</div>
59-
<ul class="pagination">
60-
<ul class="pagination" id="paginationDiv"></ul>
61-
</ul>
60+
<ul class="pagination" id="paginationDiv"></ul>
6261
</div>
6362
</div>
6463
</div>
@@ -80,14 +79,12 @@
8079
$(document).ready(function () {
8180
// 加载分类选项
8281
loadCategories();
83-
8482
// 监听分类选择器的变化
8583
$(document).on('change', '#category-filter', function () {
8684
const categoryId = $(this).val(); // 获取选中的分类ID
8785
const searchKeyword = $("#search-keyword").val(); // 获取搜索关键字
8886
getPageListData(1, 10, searchKeyword, categoryId); // 重新加载数据
8987
});
90-
9188
// 加载列表页数据
9289
getPageListData();
9390

@@ -126,7 +123,8 @@
126123
"GET",
127124
url,
128125
{page: page, page_size: page_size, search: search, category_id: category_id},
129-
function () {},
126+
function () {
127+
},
130128
function (data) {
131129
if (data.list.length > 0) {
132130
var totalNum = data.pagination.total; // 总条数
@@ -150,18 +148,24 @@
150148
});
151149
$(".tbody").html("");
152150
$.each(data.list, function (index, value) {
153-
var showUsedBadge = "";
154-
var optionUsedName = "";
151+
let showUsedBadge;
152+
let optionUsedName;
155153
if (value.is_used) {
156154
optionUsedName = '禁用';
157155
showUsedBadge = '<span class="badge badge-success">启用</span></td>'
158156
} else {
159157
optionUsedName = '启用';
160158
showUsedBadge = '<span class="badge badge-danger">禁用</span></td>'
161159
}
162-
const tr = '<tr>' +
160+
161+
let trHtml = '<tr>';
162+
if (value.url === value.title) { // 如果url和title相同,则显示警告样式
163+
trHtml = '<tr class="table-warning">'
164+
}
165+
166+
const tr = trHtml +
163167
'<td>' + value.id + '</td>' +
164-
'<td><img class="lozad img-circle" width="30" src="data:image/png;base64,' + value.thumb + '" data-loaded="true"></td>' +
168+
'<td><img class="lozad img-circle" width="30" src="data:image/png;base64,' + value.icon + '" data-loaded="true"></td>' +
165169
'<td>' + value.title + '</td>' +
166170
'<td><a href="' + value.url + '" target="_blank">' + value.url + '</a></td>' +
167171
'<td>' + value.category + '</td>' +
@@ -212,6 +216,7 @@
212216
}
213217
);
214218
}
219+
215220
// 一键同步
216221
$(document).on('click', '.btn-sync', function () {
217222
const id = $(this).attr('data-id');
@@ -232,7 +237,8 @@
232237
"GET",
233238
'/api/admin/site/sync/' + id,
234239
"",
235-
function () {},
240+
function () {
241+
},
236242
function (data) {
237243
$.alert({
238244
title: '操作成功',
@@ -434,7 +440,7 @@
434440
'</div>' +
435441
'<div>' +
436442
' <label>排序</label>' +
437-
' <small class="help-block">(相同分类下网站排序)</small>'+
443+
' <small class="help-block">(相同分类下网站排序)</small>' +
438444
' <input autofocus="" type="number" placeholder="请输入排序值" class="form-control" id="sort_order" value="' + sort + '">' + <!-- 新增排序输入框 -->
439445
'</div>',
440446
onOpen: function () {
@@ -454,7 +460,8 @@
454460
"GET",
455461
"/api/admin/category",
456462
"",
457-
function () {},
463+
function () {
464+
},
458465
function (data) {
459466
const treeData = buildTree(data.list);// 将扁平化数据转换为树形结构
460467
const selectElement = document.getElementById('category_id');// 获取 <select> 元素
@@ -554,16 +561,12 @@
554561
</script>
555562
<script>
556563
$(document).ready(function () {
557-
// 导出按钮点击事件
558564
$(document).on('click', '#exportBtn', function () {
559-
// 获取搜索关键字
560565
const searchKeyword = $("#search-keyword").val();
561-
// 发起导出请求
562-
exportData(searchKeyword);
566+
exportData(searchKeyword); // 发起导出请求
563567
});
564-
// 导出数据函数
568+
565569
function exportData(searchKeyword) {
566-
// 构造请求 URL
567570
let url = "/api/admin/site/export";
568571
if (searchKeyword) {
569572
url += "?search=" + encodeURIComponent(searchKeyword);
@@ -576,13 +579,11 @@
576579
responseType: 'blob' // 重要:告诉 jQuery 返回的是一个二进制文件
577580
},
578581
success: function (data, status, xhr) {
579-
// 获取文件名(从响应头中提取)
580-
const contentDisposition = xhr.getResponseHeader('Content-Disposition');
582+
const contentDisposition = xhr.getResponseHeader('Content-Disposition');// 获取文件名(从响应头中提取)
581583
let fileName = "sites.xlsx"; // 默认文件名
582584
if (contentDisposition && contentDisposition.includes('filename=')) {
583585
fileName = contentDisposition.split('filename=')[1].trim();
584586
}
585-
// 创建一个链接元素
586587
const link = document.createElement('a');
587588
link.href = window.URL.createObjectURL(data); // 将文件流转换为 URL
588589
link.download = fileName; // 设置下载文件名

0 commit comments

Comments
 (0)