add
This commit is contained in:
@@ -10,4 +10,17 @@
|
||||
5.3、菜单组件:支持多级菜单、折叠展开、激活状态。
|
||||
5.4、页面管理:统一的页面加载机制,支持错误处理和重试功能。
|
||||
6、扩展能力:尽量多的配置操作。
|
||||
7、样式需求:该项目为金融性质项目,页面风格样式需要偏向暗夜模式。
|
||||
7、样式需求:该项目为金融性质项目,页面风格样式需要偏向暗夜模式。
|
||||
---
|
||||
假设你是一位经验丰富的 flutter 开发人员,精通项目的架构设计和搭建,协助我使用 flutter 开发 Web 项目,项目类型为资产管理性质的的管理系统,先完成以下需求:
|
||||
1、设计一个项目架构。
|
||||
2、登录页面,输入账号密码登录,只有登录业务,没有注册和找回密码。
|
||||
3、主页分为侧边栏、顶部导航、右边内容区。
|
||||
4、常用组件:
|
||||
4.1、加载动画:全局加载状态提示。
|
||||
4.2、消息提示:支持成功 / 错误 / 信息三种类型,自动消失。
|
||||
4.3、菜单组件:支持多级菜单、折叠展开、激活状态。
|
||||
5、扩展能力:尽量多的配置操作。
|
||||
6、样式需求:该项目为金融性质项目,页面风格样式需要偏向暗夜模式。
|
||||
7、项目名为:asset-assistant-system
|
||||
---
|
||||
@@ -1,14 +0,0 @@
|
||||
/* 通用样式 */
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
font-family: 'Arial', sans-serif;
|
||||
}
|
||||
|
||||
.container {
|
||||
width: 100%;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 0 20px;
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
/* 基础变量定义 */
|
||||
:root {
|
||||
--primary-color: #1E88E5; /* 主色调(金融蓝) */
|
||||
--secondary-color: #263238; /* 次要色调 */
|
||||
--bg-color: #121212; /* 背景色 */
|
||||
--bg-light-color: #1E1E1E; /* 浅色背景 */
|
||||
--text-primary: #E0E0E0; /* 主要文字色 */
|
||||
--text-secondary: #9E9E9E; /* 次要文字色 */
|
||||
--border-color: #333333; /* 边框色 */
|
||||
--hover-color: #2D2D2D; /* hover色 */
|
||||
--active-color: #1976D2; /* 激活色 */
|
||||
--success-color: #4CAF50; /* 成功色 */
|
||||
--error-color: #F44336; /* 错误色 */
|
||||
--info-color: #2196F3; /* 信息色 */
|
||||
}
|
||||
|
||||
/* 全局样式重置 */
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body.dark-theme {
|
||||
background-color: var(--bg-color);
|
||||
color: var(--text-primary);
|
||||
font-family: 'Microsoft YaHei', 'Helvetica Neue', Arial, sans-serif;
|
||||
}
|
||||
|
||||
/* 通用组件样式 */
|
||||
.container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* 按钮样式 */
|
||||
.btn {
|
||||
background-color: var(--primary-color);
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 8px 16px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
background-color: var(--active-color);
|
||||
}
|
||||
|
||||
.btn-success {
|
||||
background-color: var(--success-color);
|
||||
}
|
||||
|
||||
.btn-error {
|
||||
background-color: var(--error-color);
|
||||
}
|
||||
|
||||
.btn-info {
|
||||
background-color: var(--info-color);
|
||||
}
|
||||
|
||||
/* 输入框样式 */
|
||||
.input {
|
||||
background-color: var(--bg-light-color);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
padding: 8px 12px;
|
||||
border-radius: 4px;
|
||||
width: 100%;
|
||||
transition: border 0.3s ease;
|
||||
}
|
||||
|
||||
.input:focus {
|
||||
outline: none;
|
||||
border-color: var(--primary-color);
|
||||
}
|
||||
|
||||
/* 卡片样式 */
|
||||
.card {
|
||||
background-color: var(--bg-light-color);
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
|
||||
padding: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
/* 应用入口 */
|
||||
import { renderHeader } from './components/header.js';
|
||||
import { renderSidebar } from './components/sidebar.js';
|
||||
import { Auth } from './core/auth.js';
|
||||
import { Router } from './core/router.js';
|
||||
import { themeConfig } from './config/theme.js';
|
||||
|
||||
// 初始化应用
|
||||
function initApp() {
|
||||
// 检查登录状态
|
||||
if (!Auth.isLogin() && window.location.pathname.indexOf('login.html') === -1) {
|
||||
Router.push('/login.html');
|
||||
return;
|
||||
}
|
||||
|
||||
// 初始化主题
|
||||
initTheme();
|
||||
|
||||
// 渲染公共组件(非登录页)
|
||||
if (window.location.pathname.indexOf('login.html') === -1) {
|
||||
renderCommonComponents();
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化主题
|
||||
function initTheme() {
|
||||
const savedTheme = localStorage.getItem(themeConfig.themeStorageKey) || themeConfig.defaultTheme;
|
||||
if (savedTheme === 'dark') {
|
||||
document.body.classList.add(themeConfig.darkThemeClass);
|
||||
}
|
||||
}
|
||||
|
||||
// 渲染公共组件(头部和侧边栏)
|
||||
function renderCommonComponents() {
|
||||
const header = renderHeader();
|
||||
const sidebar = renderSidebar();
|
||||
|
||||
document.body.appendChild(header);
|
||||
document.body.appendChild(sidebar);
|
||||
|
||||
// 创建主内容区域
|
||||
const mainContent = document.createElement('div');
|
||||
mainContent.className = 'main-content';
|
||||
document.body.appendChild(mainContent);
|
||||
}
|
||||
|
||||
// 页面加载完成后初始化
|
||||
window.addEventListener('DOMContentLoaded', initApp);
|
||||
@@ -1,31 +0,0 @@
|
||||
/* 顶部导航组件 */
|
||||
import { systemConfig } from '../config/system.js';
|
||||
import { Auth } from '../core/auth.js';
|
||||
import { themeConfig } from '../config/theme.js';
|
||||
|
||||
export function renderHeader() {
|
||||
const header = document.createElement('div');
|
||||
header.className = 'header';
|
||||
|
||||
header.innerHTML = `
|
||||
<div class="app-name">${systemConfig.appName}</div>
|
||||
<div class="header-actions">
|
||||
<button onclick="toggleTheme()">切换主题</button>
|
||||
<button onclick="Auth.logout()">退出登录</button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
// 主题切换函数
|
||||
function toggleTheme() {
|
||||
const body = document.body;
|
||||
if (body.classList.contains(themeConfig.darkThemeClass)) {
|
||||
body.classList.remove(themeConfig.darkThemeClass);
|
||||
localStorage.setItem(themeConfig.themeStorageKey, 'light');
|
||||
} else {
|
||||
body.classList.add(themeConfig.darkThemeClass);
|
||||
localStorage.setItem(themeConfig.themeStorageKey, 'dark');
|
||||
}
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
/**
|
||||
* 全局加载动画组件
|
||||
*/
|
||||
window.Loading = {
|
||||
/**
|
||||
* 初始化加载容器
|
||||
*/
|
||||
init() {
|
||||
let container = $('#loading-container');
|
||||
if (!container.length) {
|
||||
container = $('<div id="loading-container" class="loading-container"></div>');
|
||||
const loadingHtml = `
|
||||
<div class="loading-mask"></div>
|
||||
<div class="loading-spinner">
|
||||
<div class="spinner"></div>
|
||||
<div class="loading-text">加载中...</div>
|
||||
</div>
|
||||
`;
|
||||
container.html(loadingHtml);
|
||||
$('body').append(container);
|
||||
}
|
||||
return container;
|
||||
},
|
||||
|
||||
/**
|
||||
* 显示加载动画
|
||||
* @param {string} text - 加载提示文本
|
||||
*/
|
||||
show(text = '加载中...') {
|
||||
const container = this.init();
|
||||
container.find('.loading-text').text(text);
|
||||
container.css('display', 'flex');
|
||||
},
|
||||
|
||||
/**
|
||||
* 隐藏加载动画
|
||||
*/
|
||||
hide() {
|
||||
const container = $('#loading-container');
|
||||
if (container.length) {
|
||||
container.css('display', 'none');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 添加加载动画样式
|
||||
$('head').append(`
|
||||
<style>
|
||||
.loading-container {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: none;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
z-index: 99999;
|
||||
}
|
||||
.loading-mask {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
backdrop-filter: blur(2px);
|
||||
}
|
||||
.loading-spinner {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
z-index: 1;
|
||||
}
|
||||
.spinner {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border: 4px solid rgba(30, 136, 229, 0.3);
|
||||
border-top: 4px solid var(--primary-color);
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.loading-text {
|
||||
color: var(--text-primary);
|
||||
font-size: 16px;
|
||||
}
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
</style>
|
||||
`);
|
||||
@@ -1,177 +0,0 @@
|
||||
/**
|
||||
* 侧边栏菜单组件
|
||||
*/
|
||||
window.Sidebar = {
|
||||
/**
|
||||
* 初始化侧边栏
|
||||
* @param {jQuery} container - 容器元素
|
||||
*/
|
||||
init(container) {
|
||||
this.container = container;
|
||||
this.collapse = localStorage.getItem(SystemConfig.menuCollapseKey) === 'true';
|
||||
this.renderMenu();
|
||||
this.bindEvents();
|
||||
this.updateCollapseState();
|
||||
},
|
||||
|
||||
/**
|
||||
* 渲染菜单
|
||||
*/
|
||||
renderMenu() {
|
||||
const menuHtml = this.generateMenuHtml(MenuConfig);
|
||||
this.container.html(menuHtml);
|
||||
},
|
||||
|
||||
/**
|
||||
* 生成菜单HTML(递归处理多级菜单)
|
||||
* @param {array} menuList - 菜单列表
|
||||
* @returns {string} - 菜单HTML
|
||||
*/
|
||||
generateMenuHtml(menuList) {
|
||||
let html = '<ul class="sidebar-menu">';
|
||||
|
||||
menuList.forEach(menu => {
|
||||
const hasChildren = menu.children && menu.children.length > 0;
|
||||
const isActive = window.location.pathname.includes(menu.url);
|
||||
|
||||
html += `<li class="menu-item ${hasChildren ? 'has-children' : ''} ${isActive ? 'active' : ''}" data-id="${menu.id}">`;
|
||||
|
||||
// 菜单标题
|
||||
html += `<div class="menu-title">`;
|
||||
html += `<span class="menu-icon">${menu.icon}</span>`;
|
||||
html += `<span class="menu-text">${menu.title}</span>`;
|
||||
if (hasChildren) {
|
||||
html += `<span class="menu-toggle ${this.collapse ? 'collapsed' : ''}">${this.collapse ? '+' : '-'}</span>`;
|
||||
}
|
||||
html += `</div>`;
|
||||
|
||||
// 子菜单
|
||||
if (hasChildren) {
|
||||
html += `<ul class="sub-menu ${this.collapse ? 'hidden' : ''}">`;
|
||||
html += this.generateMenuHtml(menu.children);
|
||||
html += `</ul>`;
|
||||
} else if (menu.url) {
|
||||
// 菜单项链接
|
||||
html += `<a href="${menu.url}" class="menu-link"></a>`;
|
||||
}
|
||||
|
||||
html += `</li>`;
|
||||
});
|
||||
|
||||
html += '</ul>';
|
||||
return html;
|
||||
},
|
||||
|
||||
/**
|
||||
* 绑定事件
|
||||
*/
|
||||
bindEvents() {
|
||||
// 菜单折叠/展开
|
||||
this.container.on('click', '.menu-toggle', (e) => {
|
||||
const $toggle = $(e.currentTarget);
|
||||
const $subMenu = $toggle.closest('.menu-item').find('.sub-menu');
|
||||
|
||||
this.collapse = !this.collapse;
|
||||
localStorage.setItem(SystemConfig.menuCollapseKey, this.collapse);
|
||||
|
||||
$toggle.text(this.collapse ? '+' : '-');
|
||||
$subMenu.toggleClass('hidden', this.collapse);
|
||||
this.updateCollapseState();
|
||||
});
|
||||
|
||||
// 菜单项点击(无链接时展开子菜单)
|
||||
this.container.on('click', '.menu-title:not(.has-children)', (e) => {
|
||||
const $title = $(e.currentTarget);
|
||||
const $link = $title.siblings('.menu-link');
|
||||
if ($link.length) {
|
||||
$link[0].click();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 更新折叠状态样式
|
||||
*/
|
||||
updateCollapseState() {
|
||||
$('.sidebar').toggleClass('collapsed', this.collapse);
|
||||
$('.main-content').toggleClass('sidebar-collapsed', this.collapse);
|
||||
}
|
||||
};
|
||||
|
||||
// 添加侧边栏样式
|
||||
$('head').append(`
|
||||
<style>
|
||||
.sidebar {
|
||||
width: 240px;
|
||||
height: 100%;
|
||||
background-color: var(--bg-light-color);
|
||||
border-right: 1px solid var(--border-color);
|
||||
transition: width 0.3s ease;
|
||||
overflow: hidden;
|
||||
}
|
||||
.sidebar.collapsed {
|
||||
width: 60px;
|
||||
}
|
||||
.sidebar-menu {
|
||||
list-style: none;
|
||||
}
|
||||
.menu-item {
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
.menu-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 12px 16px;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.3s ease;
|
||||
}
|
||||
.menu-title:hover {
|
||||
background-color: var(--hover-color);
|
||||
}
|
||||
.menu-item.active .menu-title {
|
||||
background-color: var(--active-color);
|
||||
}
|
||||
.menu-icon {
|
||||
margin-right: 12px;
|
||||
font-size: 18px;
|
||||
}
|
||||
.menu-text {
|
||||
flex: 1;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
.sidebar.collapsed .menu-text {
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
margin: 0;
|
||||
}
|
||||
.menu-toggle {
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
.sub-menu {
|
||||
list-style: none;
|
||||
background-color: var(--bg-color);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
.sub-menu.hidden {
|
||||
display: none;
|
||||
}
|
||||
.menu-link {
|
||||
display: block;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
.main-content {
|
||||
flex: 1;
|
||||
padding: 20px;
|
||||
transition: margin-left 0.3s ease;
|
||||
}
|
||||
.main-content.sidebar-collapsed {
|
||||
margin-left: -180px;
|
||||
}
|
||||
</style>
|
||||
`);
|
||||
@@ -1,19 +0,0 @@
|
||||
/**
|
||||
* 系统菜单配置(支持多级菜单)
|
||||
*/
|
||||
window.MenuConfig = [
|
||||
{
|
||||
id: 'settings',
|
||||
icon: '⚙️',
|
||||
title: '系统设置',
|
||||
url: 'modules/settings.html',
|
||||
children: [
|
||||
{
|
||||
id: 'country',
|
||||
icon: '🌎',
|
||||
title: '国家管理',
|
||||
url: 'country.html'
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
@@ -1,24 +0,0 @@
|
||||
/**
|
||||
* 系统核心配置
|
||||
*/
|
||||
window.SystemConfig = {
|
||||
// 接口基础路径(实际项目替换为真实接口地址)
|
||||
baseApi: 'https://api.asset-management.com',
|
||||
// Token存储键名
|
||||
tokenKey: 'asset_management_token',
|
||||
// Token过期时间(单位:小时)
|
||||
tokenExpire: 24,
|
||||
// 页面加载失败重试次数
|
||||
retryCount: 2,
|
||||
// 消息提示默认时长(单位:毫秒)
|
||||
messageDuration: 3000,
|
||||
// 菜单折叠状态存储键名
|
||||
menuCollapseKey: 'asset_menu_collapse',
|
||||
// 是否开启调试模式
|
||||
debug: true
|
||||
};
|
||||
|
||||
// 打印调试信息
|
||||
if (SystemConfig.debug) {
|
||||
console.log('系统配置初始化完成:', SystemConfig);
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
/* 主题配置 */
|
||||
export const themeConfig = {
|
||||
defaultTheme: 'light',
|
||||
darkThemeClass: 'dark-theme',
|
||||
themeStorageKey: 'asset_system_theme'
|
||||
};
|
||||
@@ -1,87 +0,0 @@
|
||||
/**
|
||||
* 权限验证核心模块
|
||||
*/
|
||||
window.Auth = {
|
||||
/**
|
||||
* 获取Token
|
||||
*/
|
||||
getToken() {
|
||||
return localStorage.getItem(SystemConfig.tokenKey);
|
||||
},
|
||||
|
||||
/**
|
||||
* 设置Token
|
||||
* @param {string} token - 认证令牌
|
||||
*/
|
||||
setToken(token) {
|
||||
localStorage.setItem(SystemConfig.tokenKey, token);
|
||||
// 设置过期时间(可选)
|
||||
const expireTime = new Date().getTime() + SystemConfig.tokenExpire * 60 * 60 * 1000;
|
||||
localStorage.setItem(`${SystemConfig.tokenKey}_expire`, expireTime);
|
||||
},
|
||||
|
||||
/**
|
||||
* 移除Token
|
||||
*/
|
||||
removeToken() {
|
||||
localStorage.removeItem(SystemConfig.tokenKey);
|
||||
localStorage.removeItem(`${SystemConfig.tokenKey}_expire`);
|
||||
},
|
||||
|
||||
/**
|
||||
* 验证Token是否有效
|
||||
*/
|
||||
isValidToken() {
|
||||
const token = this.getToken();
|
||||
if (!token) return false;
|
||||
|
||||
// 验证过期时间
|
||||
const expireTime = localStorage.getItem(`${SystemConfig.tokenKey}_expire`);
|
||||
if (expireTime && new Date().getTime() > expireTime) {
|
||||
this.removeToken();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* 登录验证拦截
|
||||
* 未登录自动跳转到登录页
|
||||
*/
|
||||
checkLogin() {
|
||||
if (!this.isValidToken()) {
|
||||
window.location.href = '../pages/login.html';
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 登录请求
|
||||
* @param {string} username - 用户名
|
||||
* @param {string} password - 密码
|
||||
* @returns {Promise} - 登录结果
|
||||
*/
|
||||
login(username, password) {
|
||||
return new Promise((resolve, reject) => {
|
||||
// 模拟接口请求(实际项目替换为真实接口)
|
||||
setTimeout(() => {
|
||||
// 简单验证(实际项目需要后端验证)
|
||||
if (username && password) {
|
||||
const token = `TOKEN_${new Date().getTime()}_${username}`;
|
||||
this.setToken(token);
|
||||
resolve({ success: true, token, message: '登录成功' });
|
||||
} else {
|
||||
reject({ success: false, message: '账号或密码不能为空' });
|
||||
}
|
||||
}, 800);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 退出登录
|
||||
*/
|
||||
logout() {
|
||||
this.removeToken();
|
||||
window.location.href = '../pages/login.html';
|
||||
}
|
||||
};
|
||||
@@ -1,25 +0,0 @@
|
||||
/* 加载器 */
|
||||
export const Loader = {
|
||||
show() {
|
||||
const loader = document.createElement('div');
|
||||
loader.id = 'app-loader';
|
||||
loader.style.cssText = `
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(255,255,255,0.8);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 9999;
|
||||
`;
|
||||
loader.innerHTML = '<div>加载中...</div>';
|
||||
document.body.appendChild(loader);
|
||||
},
|
||||
hide() {
|
||||
const loader = document.getElementById('app-loader');
|
||||
if (loader) loader.remove();
|
||||
}
|
||||
};
|
||||
@@ -1,121 +0,0 @@
|
||||
/**
|
||||
* 全局消息提示组件
|
||||
*/
|
||||
window.Message = {
|
||||
/**
|
||||
* 消息容器初始化
|
||||
*/
|
||||
init() {
|
||||
let container = $('#message-container');
|
||||
if (!container.length) {
|
||||
container = $('<div id="message-container" class="message-container"></div>');
|
||||
$('body').append(container);
|
||||
}
|
||||
return container;
|
||||
},
|
||||
|
||||
/**
|
||||
* 创建消息元素(修复 CSS 变量引用问题)
|
||||
* @param {string} content - 消息内容
|
||||
* @param {string} type - 消息类型(success/error/info)
|
||||
* @returns {jQuery} - 消息元素
|
||||
*/
|
||||
createMessage(content, type) {
|
||||
// 存储 CSS 变量名,而非直接使用 var()
|
||||
const typeMap = {
|
||||
success: { icon: '✓', cssVar: '--success-color' },
|
||||
error: { icon: '✗', cssVar: '--error-color' },
|
||||
info: { icon: 'i', cssVar: '--info-color' }
|
||||
};
|
||||
const config = typeMap[type] || typeMap.info;
|
||||
|
||||
// 关键:通过 JS 获取 CSS 变量的实际值
|
||||
const rootElement = document.documentElement;
|
||||
const computedStyle = getComputedStyle(rootElement);
|
||||
const themeColor = computedStyle.getPropertyValue(config.cssVar).trim(); // 解析 CSS 变量
|
||||
|
||||
const message = $(`
|
||||
<div class="message message-${type}">
|
||||
<span class="message-icon">${config.icon}</span>
|
||||
<span class="message-content">${content}</span>
|
||||
</div>
|
||||
`);
|
||||
|
||||
// 设置样式(使用解析后的 CSS 变量值)
|
||||
message.css({
|
||||
backgroundColor: computedStyle.getPropertyValue('--bg-light-color').trim(),
|
||||
borderLeft: `4px solid ${themeColor}`,
|
||||
color: computedStyle.getPropertyValue('--text-primary').trim(),
|
||||
padding: '12px 16px',
|
||||
borderRadius: '4px',
|
||||
boxShadow: '0 2px 8px rgba(0,0,0,0.3)',
|
||||
marginBottom: '8px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
animation: 'messageFadeIn 0.3s ease'
|
||||
});
|
||||
|
||||
message.find('.message-icon').css({
|
||||
color: themeColor,
|
||||
marginRight: '8px',
|
||||
fontWeight: 'bold'
|
||||
});
|
||||
|
||||
return message;
|
||||
},
|
||||
|
||||
/**
|
||||
* 显示消息
|
||||
* @param {string} content - 消息内容
|
||||
* @param {string} type - 消息类型(success/error/info)
|
||||
* @param {number} duration - 显示时长(毫秒)
|
||||
*/
|
||||
show(content, type = 'info', duration = SystemConfig.messageDuration) {
|
||||
const container = this.init();
|
||||
const message = this.createMessage(content, type);
|
||||
|
||||
container.append(message);
|
||||
|
||||
// 自动关闭
|
||||
setTimeout(() => {
|
||||
message.css({ animation: 'messageFadeOut 0.3s ease' });
|
||||
setTimeout(() => {
|
||||
message.remove();
|
||||
}, 300);
|
||||
}, duration);
|
||||
},
|
||||
|
||||
// 快捷方法
|
||||
success(content, duration) {
|
||||
this.show(content, 'success', duration);
|
||||
},
|
||||
|
||||
error(content, duration) {
|
||||
this.show(content, 'error', duration);
|
||||
},
|
||||
|
||||
info(content, duration) {
|
||||
this.show(content, 'info', duration);
|
||||
}
|
||||
};
|
||||
|
||||
// 添加动画样式
|
||||
$('head').append(`
|
||||
<style>
|
||||
@keyframes messageFadeIn {
|
||||
from { opacity: 0; transform: translateY(-10px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
@keyframes messageFadeOut {
|
||||
from { opacity: 1; transform: translateY(0); }
|
||||
to { opacity: 0; transform: translateY(-10px); }
|
||||
}
|
||||
.message-container {
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
z-index: 9999;
|
||||
max-width: 300px;
|
||||
}
|
||||
</style>
|
||||
`);
|
||||
@@ -1,19 +0,0 @@
|
||||
/* 路由管理 */
|
||||
import { Auth } from './auth.js';
|
||||
import { systemConfig } from '../config/system.js';
|
||||
|
||||
export const Router = {
|
||||
// 跳转页面
|
||||
push(path) {
|
||||
// 验证权限
|
||||
if (path !== '/login' && !Auth.isLogin()) {
|
||||
window.location.href = `${systemConfig.baseUrl}login.html`;
|
||||
return;
|
||||
}
|
||||
window.location.href = `${systemConfig.baseUrl}${path.startsWith('/') ? path.slice(1) : path}`;
|
||||
},
|
||||
// 获取当前路径
|
||||
getCurrentPath() {
|
||||
return window.location.pathname.replace(systemConfig.baseUrl, '');
|
||||
}
|
||||
};
|
||||
@@ -1,63 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail # 更严格的错误检查:未定义变量报错、管道错误传递
|
||||
|
||||
# 定义日志函数(带时间戳和级别)
|
||||
log_info() {
|
||||
echo "[$(date +'%Y-%m-%d %H:%M:%S')] [INFO] $1"
|
||||
}
|
||||
|
||||
log_warn() {
|
||||
echo "[$(date +'%Y-%m-%d %H:%M:%S')] [WARN] $1" >&2
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo "[$(date +'%Y-%m-%d %H:%M:%S')] [ERROR] $1" >&2
|
||||
}
|
||||
|
||||
# 定义配置常量(等号两侧无空格!集中管理,便于修改)
|
||||
IMAGE_NAME="asset-assistant-frontend-system"
|
||||
IMAGE_TAG="1.0.0"
|
||||
FULL_IMAGE="${IMAGE_NAME}:${IMAGE_TAG}"
|
||||
DOCKERFILE_PATH="./Dockerfile"
|
||||
SERVICE_NAME="asset-assistant-frontend" # 服务容器名称,便于管理
|
||||
HOST_PORT=8080 # 主机映射端口
|
||||
CONTAINER_PORT=80 # 容器内部端口
|
||||
|
||||
log_info "===== 开始执行构建脚本 ====="
|
||||
|
||||
# 步骤1:停止并删除现有容器(忽略不存在的情况)
|
||||
log_info "检查并停止现有容器: ${SERVICE_NAME}"
|
||||
if sudo docker rm -f "${SERVICE_NAME}" >/dev/null 2>&1; then
|
||||
log_info "容器 ${SERVICE_NAME} 已停止并删除"
|
||||
else
|
||||
log_warn "容器 ${SERVICE_NAME} 不存在,跳过删除步骤"
|
||||
fi
|
||||
|
||||
# 步骤2:删除现有镜像(忽略不存在的情况)
|
||||
log_info "尝试删除现有镜像: ${FULL_IMAGE}"
|
||||
if sudo docker rmi -f "${FULL_IMAGE}" >/dev/null 2>&1; then
|
||||
log_info "镜像 ${FULL_IMAGE} 删除成功"
|
||||
else
|
||||
log_warn "镜像 ${FULL_IMAGE} 不存在或无法删除,跳过删除步骤"
|
||||
fi
|
||||
|
||||
# 步骤3:构建新镜像
|
||||
log_info "开始构建新镜像: ${FULL_IMAGE}(Dockerfile位于${DOCKERFILE_PATH})"
|
||||
if sudo docker build -t "${FULL_IMAGE}" -f Dockerfile .; then
|
||||
log_info "镜像 ${FULL_IMAGE} 构建成功"
|
||||
else
|
||||
log_error "镜像 ${FULL_IMAGE} 构建失败"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 步骤4:启动服务
|
||||
log_info "启动新容器: ${SERVICE_NAME}(${HOST_PORT}:${CONTAINER_PORT})"
|
||||
# 启动容器(后台运行、端口映射、命名容器)
|
||||
if sudo docker run -d -p "${HOST_PORT}:${CONTAINER_PORT}" --name "${SERVICE_NAME}" "${FULL_IMAGE}"; then
|
||||
log_info "容器 ${SERVICE_NAME} 启动成功,访问地址: http://localhost:${HOST_PORT}"
|
||||
else
|
||||
log_error "容器 ${SERVICE_NAME} 启动失败"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_info "===== 构建脚本执行完成 ====="
|
||||
@@ -1,189 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>资产管理系统 - 主页</title>
|
||||
<!-- 引入外部依赖 -->
|
||||
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
|
||||
<!-- 引入自定义配置和组件 -->
|
||||
<script src="../assets/js/config/system.js"></script>
|
||||
<script src="../assets/js/config/menu.js"></script>
|
||||
<script src="../assets/js/core/auth.js"></script>
|
||||
<script src="../assets/js/core/message.js"></script>
|
||||
<script src="../assets/js/core/loading.js"></script>
|
||||
<script src="../assets/js/components/sidebar.js"></script>
|
||||
<!-- 引入样式 -->
|
||||
<link rel="stylesheet" href="../assets/css/dark-theme.css">
|
||||
<link rel="stylesheet" href="../assets/css/common.css">
|
||||
<style>
|
||||
.app-container {
|
||||
display: flex;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
}
|
||||
.header {
|
||||
height: 60px;
|
||||
background-color: var(--bg-light-color);
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0 20px;
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
}
|
||||
.logo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.logo-icon {
|
||||
font-size: 24px;
|
||||
color: var(--primary-color);
|
||||
margin-right: 10px;
|
||||
}
|
||||
.logo-text {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
.user-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.username {
|
||||
margin-right: 15px;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
.logout-btn {
|
||||
background-color: var(--error-color);
|
||||
}
|
||||
.logout-btn:hover {
|
||||
background-color: #D32F2F;
|
||||
}
|
||||
.content-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
.main-content {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 20px;
|
||||
}
|
||||
.page-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="dark-theme">
|
||||
<div class="app-container">
|
||||
<!-- 侧边栏 -->
|
||||
<div class="sidebar" id="sidebar"></div>
|
||||
|
||||
<!-- 主内容区 -->
|
||||
<div class="content-wrapper">
|
||||
<!-- 顶部导航 -->
|
||||
<div class="header">
|
||||
<div class="logo">
|
||||
<span class="logo-icon">💎</span>
|
||||
<span class="logo-text">资产管理系统</span>
|
||||
</div>
|
||||
<div class="user-info">
|
||||
<span class="username" id="username">管理员</span>
|
||||
<button class="btn logout-btn" id="logout-btn">退出登录</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 页面内容容器 -->
|
||||
<div class="main-content">
|
||||
<div class="page-container" id="page-container">
|
||||
<!-- 页面内容将通过路由加载 -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// 初始化前验证登录状态
|
||||
Auth.checkLogin();
|
||||
|
||||
$(document).ready(() => {
|
||||
// 初始化侧边栏
|
||||
Sidebar.init($('#sidebar'));
|
||||
|
||||
// 初始化页面加载(默认加载数据概览)
|
||||
const defaultUrl = 'modules/dashboard.html';
|
||||
loadPage(defaultUrl);
|
||||
|
||||
// 退出登录事件
|
||||
$('#logout-btn').on('click', () => {
|
||||
if (confirm('确定要退出登录吗?')) {
|
||||
Auth.logout();
|
||||
}
|
||||
});
|
||||
|
||||
// 菜单链接点击事件(拦截默认跳转,实现AJAX加载)
|
||||
$('#sidebar').on('click', '.menu-link', (e) => {
|
||||
e.preventDefault();
|
||||
const url = $(e.currentTarget).attr('href');
|
||||
if (url) {
|
||||
loadPage(url);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* 加载页面内容
|
||||
* @param {string} url - 页面URL
|
||||
*/
|
||||
function loadPage(url) {
|
||||
const container = $('#page-container');
|
||||
|
||||
try {
|
||||
Loading.show('加载页面中...');
|
||||
|
||||
// 加载页面内容
|
||||
container.load(url, (response, status, xhr) => {
|
||||
Loading.hide();
|
||||
|
||||
if (status === 'success') {
|
||||
// 更新浏览器历史记录
|
||||
history.pushState({ url }, '', url);
|
||||
Message.success('页面加载成功');
|
||||
} else {
|
||||
// 错误处理和重试
|
||||
container.html(`
|
||||
<div class="error-page card">
|
||||
<h3 style="color: var(--error-color); margin-bottom: 15px;">页面加载失败</h3>
|
||||
<p>错误信息: ${xhr.statusText}</p>
|
||||
<button class="btn btn-info" id="retry-btn" data-url="${url}">重试</button>
|
||||
</div>
|
||||
`);
|
||||
Message.error(`页面加载失败: ${xhr.statusText}`);
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
Loading.hide();
|
||||
Message.error('页面加载异常,请重试');
|
||||
console.error('页面加载错误:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// 重试按钮事件
|
||||
$(document).on('click', '#retry-btn', (e) => {
|
||||
const url = $(e.currentTarget).data('url');
|
||||
loadPage(url);
|
||||
});
|
||||
|
||||
// 监听浏览器前进后退事件
|
||||
window.addEventListener('popstate', (e) => {
|
||||
if (e.state && e.state.url) {
|
||||
loadPage(e.state.url);
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,117 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>资产管理系统 - 登录</title>
|
||||
<!-- 引入外部依赖 -->
|
||||
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
|
||||
<!-- 引入自定义配置和组件 -->
|
||||
<script src="../assets/js/config/system.js"></script>
|
||||
<script src="../assets/js/core/auth.js"></script>
|
||||
<script src="../assets/js/core/message.js"></script>
|
||||
<script src="../assets/js/core/loading.js"></script>
|
||||
<!-- 新增:引入Loading组件 -->
|
||||
<script src="../assets/js/components/loading.js"></script>
|
||||
<!-- 引入样式 -->
|
||||
<link rel="stylesheet" href="../assets/css/dark-theme.css">
|
||||
<link rel="stylesheet" href="../assets/css/common.css">
|
||||
<style>
|
||||
.login-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100vh;
|
||||
padding: 20px;
|
||||
}
|
||||
.login-card {
|
||||
width: 100%;
|
||||
max-width: 400px;
|
||||
background-color: var(--bg-light-color);
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5);
|
||||
padding: 30px;
|
||||
}
|
||||
.login-header {
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
.login-title {
|
||||
font-size: 24px;
|
||||
color: var(--primary-color);
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.login-desc {
|
||||
color: var(--text-secondary);
|
||||
font-size: 14px;
|
||||
}
|
||||
.form-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.form-label {
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
color: var(--text-primary);
|
||||
font-weight: 500;
|
||||
}
|
||||
.login-btn {
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
font-size: 16px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="dark-theme">
|
||||
<div class="login-container">
|
||||
<div class="login-card">
|
||||
<div class="login-header">
|
||||
<h2 class="login-title">资产管理系统</h2>
|
||||
<p class="login-desc">金融资产一站式管理平台</p>
|
||||
</div>
|
||||
<form id="login-form">
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="username">用户名</label>
|
||||
<input type="text" id="username" class="input" placeholder="请输入用户名" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="password">密码</label>
|
||||
<input type="password" id="password" class="input" placeholder="请输入密码" required>
|
||||
</div>
|
||||
<button type="submit" class="btn login-btn">登录</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// 等待DOM完全加载后再执行
|
||||
$(document).ready(() => {
|
||||
// 登录表单提交事件
|
||||
$('#login-form').on('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
const username = $('#username').val().trim();
|
||||
const password = $('#password').val().trim();
|
||||
|
||||
if (!username || !password) {
|
||||
Message.error('账号和密码不能为空');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
Loading.show('正在登录...');
|
||||
const result = await Auth.login(username, password);
|
||||
Message.success(result.message);
|
||||
setTimeout(() => {
|
||||
window.location.href = 'index.html';
|
||||
}, 1000);
|
||||
} catch (error) {
|
||||
Message.error(error.message || '登录失败,请重试');
|
||||
} finally {
|
||||
Loading.hide();
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1 +0,0 @@
|
||||
<!--国家逻辑-->>
|
||||
@@ -1,93 +0,0 @@
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
# 定义项目根目录(脚本所在目录下的 asset-assistant-system)
|
||||
PROJECT_ROOT = Path(__file__).parent / "asset-assistant-system"
|
||||
|
||||
# 定义需要创建的目录列表
|
||||
DIRECTORIES = [
|
||||
"dist",
|
||||
"src/assets/css",
|
||||
"src/assets/css/components",
|
||||
"src/assets/js/config",
|
||||
"src/assets/js/core",
|
||||
"src/assets/js/components",
|
||||
"src/assets/icons",
|
||||
"src/pages/modules",
|
||||
"src/template"
|
||||
]
|
||||
|
||||
# 定义需要创建的文件列表(路径: 文件初始内容)
|
||||
FILES = {
|
||||
# CSS 文件
|
||||
"src/assets/css/dark-theme.css": "/* 暗夜模式主题样式 */\nbody.dark-theme {\n background-color: #1e1e1e;\n color: #f5f5f5;\n}\n",
|
||||
"src/assets/css/common.css": "/* 通用样式 */\n* {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n font-family: 'Arial', sans-serif;\n}\n\n.container {\n width: 100%;\n max-width: 1200px;\n margin: 0 auto;\n padding: 0 20px;\n}\n",
|
||||
|
||||
# JS 配置文件
|
||||
"src/assets/js/config/menu.js": "/* 菜单配置 */\nexport const menuConfig = [\n {\n path: '/dashboard',\n name: '数据概览',\n icon: 'dashboard',\n auth: true\n },\n {\n path: '/asset-list',\n name: '资产列表',\n icon: 'assets',\n auth: true\n }\n];\n",
|
||||
"src/assets/js/config/theme.js": "/* 主题配置 */\nexport const themeConfig = {\n defaultTheme: 'light',\n darkThemeClass: 'dark-theme',\n themeStorageKey: 'asset_system_theme'\n};\n",
|
||||
"src/assets/js/config/system.js": "/* 系统配置 */\nexport const systemConfig = {\n appName: '资产辅助系统',\n version: '1.0.0',\n baseUrl: '/',\n timeout: 5000\n};\n",
|
||||
|
||||
# JS 核心功能
|
||||
"src/assets/js/core/auth.js": "/* 权限验证 */\nimport { systemConfig } from '../config/system.js';\n\nexport const Auth = {\n // 检查是否登录\n isLogin() {\n return localStorage.getItem('token') !== null;\n },\n // 登录\n login(token) {\n localStorage.setItem('token', token);\n },\n // 退出登录\n logout() {\n localStorage.removeItem('token');\n window.location.href = `${systemConfig.baseUrl}login.html`;\n }\n};\n",
|
||||
"src/assets/js/core/loader.js": "/* 加载器 */\nexport const Loader = {\n show() {\n const loader = document.createElement('div');\n loader.id = 'app-loader';\n loader.style.cssText = `\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n background: rgba(255,255,255,0.8);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 9999;\n `;\n loader.innerHTML = '<div>加载中...</div>';\n document.body.appendChild(loader);\n },\n hide() {\n const loader = document.getElementById('app-loader');\n if (loader) loader.remove();\n }\n};\n",
|
||||
"src/assets/js/core/message.js": "/* 消息提示 */\nexport const Message = {\n success(content) {\n this.showMessage(content, 'success');\n },\n error(content) {\n this.showMessage(content, 'error');\n },\n showMessage(content, type) {\n const message = document.createElement('div');\n message.style.cssText = `\n position: fixed;\n top: 20px;\n right: 20px;\n padding: 10px 20px;\n border-radius: 4px;\n color: white;\n z-index: 9998;\n transition: all 0.3s;\n `;\n message.style.backgroundColor = type === 'success' ? '#4CAF50' : '#f44336';\n message.textContent = content;\n document.body.appendChild(message);\n setTimeout(() => message.remove(), 3000);\n }\n};\n",
|
||||
"src/assets/js/core/router.js": "/* 路由管理 */\nimport { Auth } from './auth.js';\nimport { systemConfig } from '../config/system.js';\n\nexport const Router = {\n // 跳转页面\n push(path) {\n // 验证权限\n if (path !== '/login' && !Auth.isLogin()) {\n window.location.href = `${systemConfig.baseUrl}login.html`;\n return;\n }\n window.location.href = `${systemConfig.baseUrl}${path.startsWith('/') ? path.slice(1) : path}`;\n },\n // 获取当前路径\n getCurrentPath() {\n return window.location.pathname.replace(systemConfig.baseUrl, '');\n }\n};\n",
|
||||
|
||||
# JS 组件
|
||||
"src/assets/js/components/sidebar.js": "/* 侧边栏组件 */\nimport { menuConfig } from '../config/menu.js';\nimport { Router } from '../core/router.js';\n\nexport function renderSidebar() {\n const sidebar = document.createElement('div');\n sidebar.className = 'sidebar';\n \n // 渲染菜单\n const menuHtml = menuConfig.map(item => `\n <div class=\"menu-item\" onclick=\"Router.push('${item.path}')\">\n <i class=\"icon-${item.icon}\"></i>\n <span>${item.name}</span>\n </div>\n `).join('');\n \n sidebar.innerHTML = menuHtml;\n return sidebar;\n}\n",
|
||||
"src/assets/js/components/header.js": "/* 顶部导航组件 */\nimport { systemConfig } from '../config/system.js';\nimport { Auth } from '../core/auth.js';\nimport { themeConfig } from '../config/theme.js';\n\nexport function renderHeader() {\n const header = document.createElement('div');\n header.className = 'header';\n \n header.innerHTML = `\n <div class=\"app-name\">${systemConfig.appName}</div>\n <div class=\"header-actions\">\n <button onclick=\"toggleTheme()\">切换主题</button>\n <button onclick=\"Auth.logout()\">退出登录</button>\n </div>\n `;\n \n return header;\n}\n\n// 主题切换函数\nfunction toggleTheme() {\n const body = document.body;\n if (body.classList.contains(themeConfig.darkThemeClass)) {\n body.classList.remove(themeConfig.darkThemeClass);\n localStorage.setItem(themeConfig.themeStorageKey, 'light');\n } else {\n body.classList.add(themeConfig.darkThemeClass);\n localStorage.setItem(themeConfig.themeStorageKey, 'dark');\n }\n}\n",
|
||||
"src/assets/js/components/loading.js": "/* 加载动画组件 */\nexport function renderLoading() {\n const loading = document.createElement('div');\n loading.className = 'loading';\n loading.innerHTML = `\n <div class=\"spinner\"></div>\n <div class=\"loading-text\">加载中...</div>\n `;\n return loading;\n}\n",
|
||||
|
||||
# 应用入口
|
||||
"src/assets/js/app.js": "/* 应用入口 */\nimport { renderHeader } from './components/header.js';\nimport { renderSidebar } from './components/sidebar.js';\nimport { Auth } from './core/auth.js';\nimport { Router } from './core/router.js';\nimport { themeConfig } from './config/theme.js';\n\n// 初始化应用\nfunction initApp() {\n // 检查登录状态\n if (!Auth.isLogin() && window.location.pathname.indexOf('login.html') === -1) {\n Router.push('/login.html');\n return;\n }\n \n // 初始化主题\n initTheme();\n \n // 渲染公共组件(非登录页)\n if (window.location.pathname.indexOf('login.html') === -1) {\n renderCommonComponents();\n }\n}\n\n// 初始化主题\nfunction initTheme() {\n const savedTheme = localStorage.getItem(themeConfig.themeStorageKey) || themeConfig.defaultTheme;\n if (savedTheme === 'dark') {\n document.body.classList.add(themeConfig.darkThemeClass);\n }\n}\n\n// 渲染公共组件(头部和侧边栏)\nfunction renderCommonComponents() {\n const header = renderHeader();\n const sidebar = renderSidebar();\n \n document.body.appendChild(header);\n document.body.appendChild(sidebar);\n \n // 创建主内容区域\n const mainContent = document.createElement('div');\n mainContent.className = 'main-content';\n document.body.appendChild(mainContent);\n}\n\n// 页面加载完成后初始化\nwindow.addEventListener('DOMContentLoaded', initApp);\n",
|
||||
|
||||
# HTML 页面
|
||||
"src/pages/login.html": "<!DOCTYPE html>\n<html lang=\"zh-CN\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>资产辅助系统 - 登录</title>\n <link rel=\"stylesheet\" href=\"../assets/css/common.css\">\n <link rel=\"stylesheet\" href=\"../assets/css/dark-theme.css\">\n</head>\n<body>\n <div class=\"container\">\n <div class=\"login-form\">\n <h2>资产辅助系统</h2>\n <div class=\"form-group\">\n <label>用户名</label>\n <input type=\"text\" id=\"username\" placeholder=\"请输入用户名\">\n </div>\n <div class=\"form-group\">\n <label>密码</label>\n <input type=\"password\" id=\"password\" placeholder=\"请输入密码\">\n </div>\n <button onclick=\"handleLogin()\">登录</button>\n </div>\n </div>\n\n <script type=\"module\">\n import { Auth } from '../assets/js/core/auth.js';\n import { Router } from '../assets/js/core/router.js';\n import { Message } from '../assets/js/core/message.js';\n\n function handleLogin() {\n const username = document.getElementById('username').value;\n const password = document.getElementById('password').value;\n\n if (!username || !password) {\n Message.error('用户名和密码不能为空');\n return;\n }\n\n // 模拟登录验证\n if (username === 'admin' && password === '123456') {\n Auth.login('mock-token-123456');\n Message.success('登录成功');\n setTimeout(() => Router.push('index.html'), 1000);\n } else {\n Message.error('用户名或密码错误');\n }\n }\n </script>\n</body>\n</html>",
|
||||
"src/pages/index.html": "<!DOCTYPE html>\n<html lang=\"zh-CN\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>资产辅助系统 - 主页</title>\n <link rel=\"stylesheet\" href=\"../assets/css/common.css\">\n <link rel=\"stylesheet\" href=\"../assets/css/dark-theme.css\">\n <style>\n .header { height: 60px; background: #2c3e50; color: white; display: flex; align-items: center; justify-content: space-between; padding: 0 20px; }\n .sidebar { width: 200px; height: calc(100vh - 60px); background: #34495e; position: fixed; left: 0; top: 60px; }\n .menu-item { color: white; padding: 15px 20px; cursor: pointer; transition: background 0.3s; }\n .menu-item:hover { background: #2c3e50; }\n .main-content { margin-left: 200px; padding: 20px; min-height: calc(100vh - 60px); }\n </style>\n</head>\n<body>\n <!-- 公共组件会通过 app.js 自动渲染 -->\n <div class=\"main-content\">\n <h1>欢迎使用资产辅助系统</h1>\n <p>请选择左侧菜单进行操作</p>\n </div>\n\n <script type=\"module\" src=\"../assets/js/app.js\"></script>\n</body>\n</html>",
|
||||
"src/pages/modules/dashboard.html": "<!DOCTYPE html>\n<html lang=\"zh-CN\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>数据概览 - 资产辅助系统</title>\n <link rel=\"stylesheet\" href=\"../../assets/css/common.css\">\n <link rel=\"stylesheet\" href=\"../../assets/css/dark-theme.css\">\n <style>\n .dashboard-cards { display: flex; gap: 20px; margin-top: 20px; }\n .card { flex: 1; padding: 20px; background: white; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }\n .dark-theme .card { background: #2c3e50; color: white; }\n </style>\n</head>\n<body>\n <div class=\"main-content\">\n <h2>数据概览</h2>\n <div class=\"dashboard-cards\">\n <div class=\"card\">\n <h3>总资产数</h3>\n <p>1,286</p>\n </div>\n <div class=\"card\">\n <h3>在线设备</h3>\n <p>952</p>\n </div>\n <div class=\"card\">\n <h3>待处理工单</h3>\n <p>36</p>\n </div>\n </div>\n </div>\n\n <script type=\"module\" src=\"../../assets/js/app.js\"></script>\n</body>\n</html>",
|
||||
"src/pages/modules/asset-list.html": "<!DOCTYPE html>\n<html lang=\"zh-CN\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>资产列表 - 资产辅助系统</title>\n <link rel=\"stylesheet\" href=\"../../assets/css/common.css\">\n <link rel=\"stylesheet\" href=\"../../assets/css/dark-theme.css\">\n <style>\n .asset-table { width: 100%; border-collapse: collapse; margin-top: 20px; }\n .asset-table th, .asset-table td { padding: 12px; text-align: left; border-bottom: 1px solid #eee; }\n .dark-theme .asset-table th, .dark-theme .asset-table td { border-bottom-color: #34495e; }\n .asset-table th { background: #f5f5f5; }\n .dark-theme .asset-table th { background: #34495e; }\n .btn { padding: 6px 12px; border: none; border-radius: 4px; cursor: pointer; }\n .btn-detail { background: #3498db; color: white; }\n </style>\n</head>\n<body>\n <div class=\"main-content\">\n <h2>资产列表</h2>\n <table class=\"asset-table\">\n <thead>\n <tr>\n <th>资产ID</th>\n <th>资产名称</th>\n <th>资产类型</th>\n <th>状态</th>\n <th>操作</th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <td>ASSET-001</td>\n <td>服务器-Web01</td>\n <td>服务器</td>\n <td>运行中</td>\n <td><button class=\"btn btn-detail\" onclick=\"goToDetail('ASSET-001')\">查看详情</button></td>\n </tr>\n <tr>\n <td>ASSET-002</td>\n <td>交换机-SW02</td>\n <td>网络设备</td>\n <td>运行中</td>\n <td><button class=\"btn btn-detail\" onclick=\"goToDetail('ASSET-002')\">查看详情</button></td>\n </tr>\n </tbody>\n </table>\n </div>\n\n <script type=\"module\">\n import { Router } from '../../assets/js/core/router.js';\n window.goToDetail = function(assetId) {\n Router.push(`asset-detail.html?id=${assetId}`);\n }\n </script>\n <script type=\"module\" src=\"../../assets/js/app.js\"></script>\n</body>\n</html>",
|
||||
"src/pages/modules/asset-detail.html": "<!DOCTYPE html>\n<html lang=\"zh-CN\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>资产详情 - 资产辅助系统</title>\n <link rel=\"stylesheet\" href=\"../../assets/css/common.css\">\n <link rel=\"stylesheet\" href=\"../../assets/css/dark-theme.css\">\n <style>\n .asset-detail { margin-top: 20px; background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }\n .dark-theme .asset-detail { background: #2c3e50; }\n .detail-item { margin-bottom: 15px; display: flex; }\n .detail-label { width: 120px; font-weight: bold; }\n .btn-back { margin-top: 20px; padding: 8px 16px; background: #3498db; color: white; border: none; border-radius: 4px; cursor: pointer; }\n </style>\n</head>\n<body>\n <div class=\"main-content\">\n <h2>资产详情</h2>\n <div class=\"asset-detail\">\n <div class=\"detail-item\">\n <div class=\"detail-label\">资产ID:</div>\n <div id=\"asset-id\">--</div>\n </div>\n <div class=\"detail-item\">\n <div class=\"detail-label\">资产名称:</div>\n <div id=\"asset-name\">--</div>\n </div>\n <div class=\"detail-item\">\n <div class=\"detail-label\">资产类型:</div>\n <div id=\"asset-type\">--</div>\n </div>\n <div class=\"detail-item\">\n <div class=\"detail-label\">购买日期:</div>\n <div>2024-01-15</div>\n </div>\n <div class=\"detail-item\">\n <div class=\"detail-label\">状态:</div>\n <div style=\"color: #27ae60;\">运行中</div>\n </div>\n </div>\n <button class=\"btn-back\" onclick=\"goBack()\">返回列表</button>\n </div>\n\n <script type=\"module\">\n import { Router } from '../../assets/js/core/router.js';\n\n // 获取资产ID参数\n function getAssetId() {\n const params = new URLSearchParams(window.location.search);\n return params.get('id') || '';\n }\n\n // 初始化详情数据\n function initDetail() {\n const assetId = getAssetId();\n if (!assetId) return;\n\n document.getElementById('asset-id').textContent = assetId;\n \n // 模拟根据ID获取资产名称和类型\n const assetMap = {\n 'ASSET-001': { name: '服务器-Web01', type: '服务器' },\n 'ASSET-002': { name: '交换机-SW02', type: '网络设备' }\n };\n\n const asset = assetMap[assetId] || { name: '未知资产', type: '未知类型' };\n document.getElementById('asset-name').textContent = asset.name;\n document.getElementById('asset-type').textContent = asset.type;\n }\n\n function goBack() {\n Router.push('asset-list.html');\n }\n\n // 初始化\n initDetail();\n </script>\n <script type=\"module\" src=\"../../assets/js/app.js\"></script>\n</body>\n</html>",
|
||||
|
||||
# 根目录文件
|
||||
".gitignore": "# 依赖\nnode_modules/\n.pnp/\n.pnp.js\n\n# 构建产物\ndist/\n\n# 环境变量\n.env\n.env.local\n.env.development.local\n.env.test.local\n.env.production.local\n\n# 日志\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n\n# 编辑器配置\n.idea/\n.vscode/\n*.swp\n*.swo\n\n# 操作系统文件\n.DS_Store\nThumbs.db",
|
||||
"index.html": "<!DOCTYPE html>\n<html lang=\"zh-CN\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>资产辅助系统</title>\n <!-- 自动跳转到登录页 -->\n <meta http-equiv=\"refresh\" content=\"0;url=src/pages/login.html\">\n</head>\n<body>\n <p>正在跳转到登录页... 如果没有自动跳转,请<a href=\"src/pages/login.html\">点击这里</a></p>\n</body>\n</html>",
|
||||
"package.json": "{\n \"name\": \"asset-assistant-system\",\n \"version\": \"1.0.0\",\n \"description\": \"资产辅助管理系统\",\n \"main\": \"index.html\",\n \"scripts\": {\n \"dev\": \"serve\",\n \"build\": \"echo '构建脚本待实现'\",\n \"test\": \"echo '测试脚本待实现'\"\n },\n \"keywords\": [\"asset\", \"management\", \"system\"],\n \"author\": \"\",\n \"license\": \"MIT\",\n \"devDependencies\": {\n \"serve\": \"^14.2.3\"\n }\n}"
|
||||
}
|
||||
|
||||
def create_project_structure():
|
||||
"""创建项目目录结构和文件"""
|
||||
try:
|
||||
# 1. 创建根目录
|
||||
PROJECT_ROOT.mkdir(exist_ok=True)
|
||||
print(f"✅ 创建项目根目录: {PROJECT_ROOT}")
|
||||
|
||||
# 2. 创建所有子目录
|
||||
for dir_path in DIRECTORIES:
|
||||
full_dir_path = PROJECT_ROOT / dir_path
|
||||
full_dir_path.mkdir(parents=True, exist_ok=True)
|
||||
print(f"✅ 创建目录: {full_dir_path}")
|
||||
|
||||
# 3. 创建所有文件并写入初始内容
|
||||
for file_path, content in FILES.items():
|
||||
full_file_path = PROJECT_ROOT / file_path
|
||||
# 确保文件所在目录存在(防止遗漏)
|
||||
full_file_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
# 写入文件内容
|
||||
with open(full_file_path, 'w', encoding='utf-8') as f:
|
||||
f.write(content)
|
||||
print(f"✅ 创建文件: {full_file_path}")
|
||||
|
||||
print("\n🎉 项目结构创建完成!")
|
||||
print(f"📁 项目路径: {PROJECT_ROOT}")
|
||||
print("\n💡 后续操作建议:")
|
||||
print(" 1. 进入项目目录: cd asset-assistant-system")
|
||||
print(" 2. 安装依赖: npm install")
|
||||
print(" 3. 启动开发服务器: npm run dev")
|
||||
print(" 4. 访问: http://localhost:3000")
|
||||
|
||||
except Exception as e:
|
||||
print(f"\n❌ 创建项目结构时出错: {e}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
create_project_structure()
|
||||
Reference in New Issue
Block a user