第一次提交

This commit is contained in:
2026-03-25 15:24:22 +08:00
commit 0f8ac68d4d
156 changed files with 42365 additions and 0 deletions

167
static/css/custom.css Normal file
View File

@@ -0,0 +1,167 @@
/* 自定义样式 */
.main-content {
padding-top: 20px;
}
.card-stats {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}
/* 仪表盘卡片标题字体颜色改为黑色 */
.card-stats .card-title {
color: black !important;
}
.loading {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
z-index: 9999;
}
.spinner {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.sidebar .nav-link.active {
background-color: #495057;
color: #fff;
}
/* 删除确认弹窗样式优化 - 增强特异性 */
.modal-content.custom-modal-content,
div.modal-content.custom-modal-content {
border: none !important;
border-radius: 0.5rem !important;
box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15) !important;
}
.modal-header.custom-modal-header,
div.modal-header.custom-modal-header {
background-color: #f8f9fa !important;
border-bottom: 1px solid #e9ecef !important;
border-radius: 0.5rem 0.5rem 0 0 !important;
padding: 1rem 1.5rem !important;
}
.modal-title.custom-modal-title,
div.modal-title.custom-modal-title {
font-weight: 600 !important;
color: #495057 !important;
font-size: 1.25rem !important;
}
.modal-body.custom-modal-body,
div.modal-body.custom-modal-body {
padding: 1.5rem !important;
font-size: 1rem !important;
line-height: 1.5 !important;
}
.modal-footer.custom-modal-footer,
div.modal-footer.custom-modal-footer {
background-color: #f8f9fa !important;
border-top: 1px solid #e9ecef !important;
border-radius: 0 0 0.5rem 0.5rem !important;
padding: 1rem 1.5rem !important;
}
.btn-close.custom-btn-close,
button.btn-close.custom-btn-close {
box-sizing: content-box !important;
width: 1em !important;
height: 1em !important;
padding: 0.25em 0.25em !important;
color: #000 !important;
background: transparent url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%3e%3cpath d='M.293.293a1 1 0 011.414 0L8 6.586 14.293.293a1 1 0 111.414 1.414L9.414 8l6.293 6.293a1 1 0 01-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 01-1.414-1.414L6.586 8 .293 1.707a1 1 0 010-1.414z'/%3e%3c/svg%3e") center/1em auto no-repeat !important;
border: 0 !important;
border-radius: 0.375rem !important;
opacity: 0.5 !important;
}
.btn-close.custom-btn-close:hover,
button.btn-close.custom-btn-close:hover {
opacity: 0.75 !important;
}
/* 按钮样式 */
.btn-secondary.custom-btn-secondary,
button.btn-secondary.custom-btn-secondary {
background-color: #6c757d !important;
border-color: #6c757d !important;
color: #fff !important;
padding: 0.375rem 0.75rem !important;
font-size: 0.875rem !important;
border-radius: 0.25rem !important;
border: 1px solid transparent !important;
}
.btn-secondary.custom-btn-secondary:hover,
button.btn-secondary.custom-btn-secondary:hover {
background-color: #5a6268 !important;
border-color: #545b62 !important;
}
.btn-danger.custom-btn-danger,
button.btn-danger.custom-btn-danger {
background-color: #dc3545 !important;
border-color: #dc3545 !important;
color: #fff !important;
padding: 0.375rem 0.75rem !important;
font-size: 0.875rem !important;
border-radius: 0.25rem !important;
border: 1px solid transparent !important;
}
.btn-danger.custom-btn-danger:hover,
button.btn-danger.custom-btn-danger:hover {
background-color: #c82333 !important;
border-color: #bd2130 !important;
}
/* 警告消息样式 */
.alert-warning.custom-alert-warning,
div.alert-warning.custom-alert-warning {
background-color: #fff3cd !important;
border-color: #ffeaa7 !important;
color: #856404 !important;
border-radius: 0.375rem !important;
padding: 0.75rem 1.25rem !important;
margin-top: 1rem !important;
margin-bottom: 0 !important;
}
.alert-warning.custom-alert-warning:hover,
div.alert-warning.custom-alert-warning:hover {
background-color: #ffefba !important;
}
.alert-warning.custom-alert-warning::before,
div.alert-warning.custom-alert-warning::before {
content: " " !important;
margin-right: 0.5rem !important;
}
/* 强制删除按钮样式 */
.btn-warning {
background-color: #ffc107 !important;
border-color: #ffc107 !important;
color: #000 !important;
}
.btn-warning:hover {
background-color: #ffb300 !important;
border-color: #ffb300 !important;
}
.icon icon-shape bg-white text-info rounded-circle shadow{
width: 1050px;
}

View File

@@ -0,0 +1 @@
This is a placeholder for the default product image file. Please replace this with an actual image file.

BIN
static/images/taiyiagi.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

View File

@@ -0,0 +1 @@
This is a placeholder for the WeChat QR code image.

70
static/js/custom.js Normal file
View File

@@ -0,0 +1,70 @@
// 自定义JavaScript函数
// 设置前端域名全局变量
if (typeof FRONTEND_DOMAIN !== 'undefined') {
window.FRONTEND_DOMAIN = FRONTEND_DOMAIN;
}
// 显示加载动画
function showLoading() {
const loadingElement = document.getElementById('loading');
if (loadingElement) {
loadingElement.style.display = 'block';
}
}
// 隐藏加载动画
function hideLoading() {
const loadingElement = document.getElementById('loading');
if (loadingElement) {
loadingElement.style.display = 'none';
}
}
// 格式化日期
function formatDate(dateString) {
if (!dateString) return '-';
const date = new Date(dateString);
return date.toLocaleDateString('zh-CN') + ' ' + date.toLocaleTimeString('zh-CN');
}
// 格式化文件大小
function formatFileSize(bytes) {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
}
// 显示通知 - 统一的消息弹窗函数
function showNotification(message, type = 'info') {
// 创建通知元素
const alertDiv = document.createElement('div');
const alertType = type === 'error' ? 'danger' : type;
alertDiv.className = `alert alert-${alertType} alert-dismissible fade show`;
alertDiv.innerHTML = `
${message}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
`;
alertDiv.style.marginBottom = '10px';
// 插入到通知容器中
const container = document.getElementById('notification-container') || document.querySelector('main');
if (container) {
container.insertBefore(alertDiv, container.firstChild);
// 自动隐藏
setTimeout(() => {
if (alertDiv.parentNode) {
alertDiv.remove();
}
}, 5000);
} else {
// 如果找不到容器使用console输出作为备选方案
console.log(`[${type.toUpperCase()}] ${message}`);
}
}
// API请求函数 - 添加认证支持 - REMOVED DUPLICATE
// This function is now defined in base.html to avoid duplication

134
static/js/pagination.js Normal file
View File

@@ -0,0 +1,134 @@
/**
* 公共分页工具函数
*/
// 渲染分页导航
function renderPagination(pagination, pageChangeCallback) {
const paginationEl = document.getElementById('pagination');
if (!paginationEl) return;
// 验证分页数据
if (!pagination || typeof pagination !== 'object' ||
!Number.isFinite(pagination.total_pages) || pagination.total_pages <= 1) {
paginationEl.innerHTML = '';
return;
}
let html = '';
// 上一页
if (pagination.has_prev) {
html += `<li class="page-item">
<a class="page-link" href="#" data-page="${pagination.current_page - 1}">上一页</a>
</li>`;
}
// 页码
const startPage = Math.max(1, pagination.current_page - 2);
const endPage = Math.min(pagination.total_pages, pagination.current_page + 2);
if (startPage > 1) {
html += `<li class="page-item"><a class="page-link" href="#" data-page="1">1</a></li>`;
if (startPage > 2) {
html += `<li class="page-item disabled"><span class="page-link">...</span></li>`;
}
}
for (let i = startPage; i <= endPage; i++) {
html += `<li class="page-item ${i === pagination.current_page ? 'active' : ''}">
<a class="page-link" href="#" data-page="${i}">${i}</a>
</li>`;
}
if (endPage < pagination.total_pages) {
if (endPage < pagination.total_pages - 1) {
html += `<li class="page-item disabled"><span class="page-link">...</span></li>`;
}
html += `<li class="page-item"><a class="page-link" href="#" data-page="${pagination.total_pages}">${pagination.total_pages}</a></li>`;
}
// 下一页
if (pagination.has_next) {
html += `<li class="page-item">
<a class="page-link" href="#" data-page="${pagination.current_page + 1}">下一页</a>
</li>`;
}
paginationEl.innerHTML = html;
// 绑定分页点击事件
paginationEl.querySelectorAll('.page-link').forEach(link => {
link.addEventListener('click', function(e) {
e.preventDefault();
const page = parseInt(this.dataset.page);
if (page && page !== pagination.current_page) {
if (typeof pageChangeCallback === 'function') {
pageChangeCallback(page);
}
}
});
});
}
// 获取URL参数
function getUrlParameter(name) {
const urlParams = new URLSearchParams(window.location.search);
return urlParams.get(name);
}
// 设置URL参数
function setUrlParameter(param, value) {
const urlParams = new URLSearchParams(window.location.search);
if (value === null || value === '') {
urlParams.delete(param);
} else {
urlParams.set(param, value);
}
window.history.replaceState({}, '', `${window.location.pathname}?${urlParams.toString()}`);
}
// 批量更新复选框状态
function setupBatchSelection(selectAllId, itemCheckboxClass, selectedCountId, batchButtons) {
const selectAll = document.getElementById(selectAllId);
if (!selectAll) return;
// 全选/取消全选
selectAll.addEventListener('change', function() {
const checkboxes = document.querySelectorAll(itemCheckboxClass);
checkboxes.forEach(checkbox => {
checkbox.checked = this.checked;
});
updateBatchButtons(selectedCountId, batchButtons);
});
// 单个复选框事件
document.querySelectorAll(itemCheckboxClass).forEach(checkbox => {
checkbox.addEventListener('change', () => {
updateBatchButtons(selectedCountId, batchButtons);
});
});
// 初始状态
updateBatchButtons(selectedCountId, batchButtons);
}
// 更新批量操作按钮状态
function updateBatchButtons(selectedCountId, batchButtons) {
const selectedCount = document.querySelectorAll('.license-checkbox:checked, .product-checkbox:checked, .device-checkbox:checked').length;
if (selectedCountId) {
const countElement = document.getElementById(selectedCountId);
if (countElement) {
countElement.textContent = `已选择 ${selectedCount}`;
}
}
if (batchButtons) {
batchButtons.forEach(buttonId => {
const button = document.getElementById(buttonId);
if (button) {
button.style.display = selectedCount > 0 ? 'inline-block' : 'none';
}
});
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB