Toolhub / Views /Tag /Index.cshtml
unifare
Initial commit: ToolHub ASP.NET Core app
5fc700d
@model List<ToolHub.Models.Tag>
@{
ViewData["Title"] = "标签管理";
Layout = "_AdminLayout";
var currentPage = ViewBag.CurrentPage as int? ?? 1;
var totalPages = ViewBag.TotalPages as int? ?? 1;
var totalCount = ViewBag.TotalCount as int? ?? 0;
var pageSize = ViewBag.PageSize as int? ?? 20;
}
<!-- 页面头部 -->
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 2rem;">
<div>
<h2 style="font-size: 1.5rem; font-weight: 700; margin: 0; color: var(--dark);">标签管理</h2>
<p style="color: var(--dark-2); margin: 0.5rem 0 0;">管理工具标签,为工具添加分类标识</p>
</div>
<button class="btn btn-primary" onclick="openTagModal()">
<i class="fas fa-plus"></i>
添加标签
</button>
</div>
<!-- 标签列表 -->
<div class="data-table">
<div style="padding: 1rem 1.5rem; border-bottom: 1px solid var(--light-2); display: flex; justify-content: space-between; align-items: center;">
<span style="font-weight: 600;">标签列表 (@totalCount)</span>
</div>
<table>
<thead>
<tr>
<th>标签名称</th>
<th>颜色</th>
<th>使用次数</th>
<th>创建时间</th>
<th>状态</th>
<th>操作</th>
</tr>
</thead>
<tbody>
@foreach (var tag in Model)
{
<tr>
<td>
<div style="display: flex; align-items: center;">
<span class="badge" style="background: @(tag.Color ?? "var(--primary)"); color: white; margin-right: 0.75rem;">
@tag.Name
</span>
<strong>@tag.Name</strong>
</div>
</td>
<td>
@if (!string.IsNullOrEmpty(tag.Color))
{
<div style="display: flex; align-items: center; gap: 0.5rem;">
<div style="width: 20px; height: 20px; background: @tag.Color; border-radius: 4px; border: 1px solid var(--light-2);"></div>
<code style="font-size: 0.75rem;">@tag.Color</code>
</div>
}
else
{
<span style="color: var(--dark-2);">默认</span>
}
</td>
<td>
<span class="badge badge-primary">@tag.ToolTags?.Count</span>
</td>
<td style="color: var(--dark-2); font-size: 0.875rem;">
@tag.CreatedAt.ToString("yyyy-MM-dd")
</td>
<td>
@if (tag.IsActive)
{
<span class="badge badge-success">启用</span>
}
else
{
<span class="badge badge-secondary">禁用</span>
}
</td>
<td>
<div style="display: flex; gap: 0.5rem;">
<button class="btn btn-outline btn-sm" onclick="editTag(@tag.Id, '@tag.Name', '@tag.Color')">
<i class="fas fa-edit"></i>
</button>
<button class="btn btn-outline btn-sm" onclick="toggleTagStatus(@tag.Id, @tag.IsActive.ToString().ToLower())" style="color: @(tag.IsActive ? "var(--warning)" : "var(--success)");">
<i class="fas fa-@(tag.IsActive ? "pause" : "play")"></i>
</button>
<button class="btn btn-outline btn-sm" onclick="deleteTag(@tag.Id, '@tag.Name')" style="color: var(--danger);">
<i class="fas fa-trash"></i>
</button>
</div>
</td>
</tr>
}
</tbody>
</table>
@if (!Model.Any())
{
<div style="padding: 3rem; text-align: center; color: var(--dark-2);">
<i class="fas fa-tags" style="font-size: 3rem; margin-bottom: 1rem; display: block; opacity: 0.3;"></i>
<h3 style="margin-bottom: 0.5rem;">暂无标签</h3>
<p style="margin-bottom: 1.5rem;">还没有创建任何标签</p>
<button class="btn btn-primary" onclick="openTagModal()">
<i class="fas fa-plus"></i>
创建第一个标签
</button>
</div>
}
<!-- 分页控件 -->
@if (totalPages > 1)
{
<div style="padding: 1.5rem; border-top: 1px solid var(--light-2); display: flex; justify-content: center; align-items: center; gap: 0.5rem;">
<button class="btn btn-outline btn-sm" onclick="changePage(1)" @(currentPage == 1 ? "disabled" : "")>
<i class="fas fa-angle-double-left"></i>
</button>
<button class="btn btn-outline btn-sm" onclick="changePage(@(currentPage - 1))" @(currentPage == 1 ? "disabled" : "")>
<i class="fas fa-angle-left"></i>
</button>
@{
var startPage = Math.Max(1, currentPage - 2);
var endPage = Math.Min(totalPages, currentPage + 2);
}
@for (int i = startPage; i <= endPage; i++)
{
<button class="btn @(i == currentPage ? "btn-primary" : "btn-outline") btn-sm" onclick="changePage(@i)">
@i
</button>
}
<button class="btn btn-outline btn-sm" onclick="changePage(@(currentPage + 1))" @(currentPage == totalPages ? "disabled" : "")>
<i class="fas fa-angle-right"></i>
</button>
<button class="btn btn-outline btn-sm" onclick="changePage(@totalPages)" @(currentPage == totalPages ? "disabled" : "")>
<i class="fas fa-angle-double-right"></i>
</button>
</div>
}
</div>
<!-- 标签模态框 -->
<div id="tagModal" class="modal" style="display: none;">
<div class="modal-backdrop" onclick="closeTagModal()"></div>
<div class="modal-content" style="max-width: 400px;">
<div class="modal-header">
<h3 id="modalTitle">添加标签</h3>
<button onclick="closeTagModal()" style="background: none; border: none; font-size: 1.5rem; cursor: pointer;">×</button>
</div>
<form id="tagForm" onsubmit="submitTag(event)">
<input type="hidden" id="tagId" name="id" value="0" />
<div class="form-group" style="margin-bottom: 1.5rem;">
<label style="display: block; font-weight: 600; margin-bottom: 0.5rem;">标签名称 *</label>
<input type="text" id="tagName" name="name" required
style="width: 100%; padding: 0.75rem; border: 1px solid var(--light-2); border-radius: var(--border-radius);"
placeholder="请输入标签名称" />
</div>
<div class="form-group" style="margin-bottom: 1.5rem;">
<label style="display: block; font-weight: 600; margin-bottom: 0.5rem;">标签颜色</label>
<input type="color" id="tagColor" name="color" value="#165DFF"
style="width: 100%; padding: 0.5rem; border: 1px solid var(--light-2); border-radius: var(--border-radius); height: 3rem;" />
<small style="color: var(--dark-2); font-size: 0.75rem;">选择标签的显示颜色</small>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline" onclick="closeTagModal()">取消</button>
<button type="submit" class="btn btn-primary">
<span id="submitText">保存</span>
</button>
</div>
</form>
</div>
</div>
<style>
.modal {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 10000;
display: flex;
align-items: center;
justify-content: center;
}
.modal-backdrop {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
}
.modal-content {
background: white;
border-radius: var(--border-radius);
position: relative;
z-index: 2;
max-height: 90vh;
overflow-y: auto;
width: 90%;
max-width: 600px;
}
.modal-header {
padding: 1.5rem;
border-bottom: 1px solid var(--light-2);
display: flex;
justify-content: space-between;
align-items: center;
}
.modal-header h3 {
margin: 0;
font-size: 1.25rem;
font-weight: 600;
}
.modal-footer {
padding: 1.5rem;
border-top: 1px solid var(--light-2);
display: flex;
justify-content: flex-end;
gap: 1rem;
}
.form-group input:focus {
border-color: var(--primary);
outline: none;
box-shadow: 0 0 0 3px rgba(22, 93, 255, 0.1);
}
</style>
<script>
let isEditing = false;
function changePage(page) {
window.location.href = `@Url.Action("Index", "Tag")?page=${page}`;
}
function openTagModal(editMode = false) {
document.getElementById('tagModal').style.display = 'flex';
document.getElementById('modalTitle').textContent = editMode ? '编辑标签' : '添加标签';
document.getElementById('submitText').textContent = editMode ? '更新' : '保存';
isEditing = editMode;
if (!editMode) {
document.getElementById('tagForm').reset();
document.getElementById('tagId').value = '0';
document.getElementById('tagColor').value = '#165DFF';
}
}
function closeTagModal() {
document.getElementById('tagModal').style.display = 'none';
}
function editTag(id, name, color) {
openTagModal(true);
document.getElementById('tagId').value = id;
document.getElementById('tagName').value = name;
document.getElementById('tagColor').value = color || '#165DFF';
}
async function submitTag(event) {
event.preventDefault();
const formData = new FormData(event.target);
const data = Object.fromEntries(formData.entries());
try {
const response = await fetch('/Tag/Save', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data)
});
if (response.ok) {
toolHub.showToast(isEditing ? '标签更新成功' : '标签添加成功', 'success');
closeTagModal();
setTimeout(() => location.reload(), 1000);
} else {
toolHub.showToast('操作失败,请重试', 'error');
}
} catch (error) {
console.error('提交失败:', error);
toolHub.showToast('操作失败,请重试', 'error');
}
}
async function toggleTagStatus(id, currentStatus) {
try {
const response = await fetch('/Tag/ToggleStatus', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ id: id, isActive: !currentStatus })
});
if (response.ok) {
toolHub.showToast('状态更新成功', 'success');
setTimeout(() => location.reload(), 1000);
} else {
toolHub.showToast('操作失败,请重试', 'error');
}
} catch (error) {
console.error('操作失败:', error);
toolHub.showToast('操作失败,请重试', 'error');
}
}
async function deleteTag(id, name) {
if (!confirm(`确定要删除标签"${name}"吗?\n注意:删除标签会影响使用该标签的工具。`)) {
return;
}
try {
const response = await fetch('/Tag/Delete', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ id: id })
});
if (response.ok) {
toolHub.showToast('标签删除成功', 'success');
setTimeout(() => location.reload(), 1000);
} else {
toolHub.showToast('删除失败,可能该标签下还有工具', 'error');
}
} catch (error) {
console.error('删除失败:', error);
toolHub.showToast('删除失败,请重试', 'error');
}
}
</script>