| @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> |
|
|