diff --git a/UpdateLog.md b/UpdateLog.md index 001af29..0174d35 100644 --- a/UpdateLog.md +++ b/UpdateLog.md @@ -1,5 +1,28 @@ # 仓库管理操作端项目更新记录 +## 2024年9月17日 - 代码注释完善 + +**主要更新内容:** +- 为项目核心文件添加了详细的注释文档,包括: + - 视图组件:LayoutView.vue、HomeView.vue、Map.vue、LoginView.vue + - 功能组件:Fence.vue、Filter.vue + - 自定义钩子:useFence.js + - 配置文件:main.js、router/index.js、stores/user.js、config/index.js +- 优化了注释格式和内容组织,提高代码可读性和可维护性 +- 为每个文件添加了文件级注释、类级注释、函数级注释、参数和返回值注释 +- 补充了关键业务逻辑的说明和实现思路 + +**修复问题:** +- 修复了部分文件中的注释格式问题 + +**已知问题:** +- 暂无重大已知问题 + +**后续计划:** +- 继续完善剩余文件的代码注释 +- 实现更多业务功能模块 +- 优化3D地图显示效果和交互体验 + ## 2024年9月16日 - 项目初始化 **主要更新内容:** diff --git a/src/App.vue b/src/App.vue index cd19aa4..52a8540 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,11 +1,27 @@ + + \ No newline at end of file diff --git a/src/components/Fence.vue b/src/components/Fence.vue index 04c1563..43e1118 100644 --- a/src/components/Fence.vue +++ b/src/components/Fence.vue @@ -1,50 +1,104 @@ + + \ No newline at end of file diff --git a/src/components/Filter.vue b/src/components/Filter.vue index 2a5417b..cb76b5c 100644 --- a/src/components/Filter.vue +++ b/src/components/Filter.vue @@ -1,96 +1,202 @@ + + + + 设备类型{{ key }} + \ No newline at end of file diff --git a/src/config/index.js b/src/config/index.js index 2ee319a..87bd3c4 100644 --- a/src/config/index.js +++ b/src/config/index.js @@ -1,74 +1,143 @@ // 系统配置文件 -// 这个文件包含所有全局配置项,可以在整个应用中访问 +/** + * 全局配置文件 + * 包含应用的所有全局配置项,可在整个应用中访问 + * 支持通过外部配置文件(window.APP_CONFIG)覆盖默认配置 + */ -// 优先使用外部配置文件中的配置,如果没有则使用默认值 +/** + * 获取外部配置 + * 优先使用外部配置文件中的配置,如果没有则使用默认值 + * @type {Object} 外部配置对象 + */ const externalConfig = typeof window !== 'undefined' && window.APP_CONFIG || {}; +/** + * 导出全局配置对象 + * 包含应用基本信息、API配置、布局配置、主题配置等 + * @returns {Object} 全局配置对象 + */ export default { - // 应用基本信息 + /** + * 应用基本信息配置 + * 包含应用名称、版本和描述等基本信息 + */ app: { + // 合并外部配置中的app配置 ...externalConfig.app, + // 应用名称(优先使用外部配置,否则使用默认值) name: externalConfig.app?.name || '仓库管理操作端', + // 应用版本号(优先使用外部配置,否则使用默认值) version: externalConfig.app?.version || '1.0.0', + // 应用描述信息(优先使用外部配置,否则使用默认值) description: externalConfig.app?.description || '现代化仓库管理系统前端操作界面' }, - // API配置 + /** + * API配置 + * 包含API请求相关的配置,如基础URL、超时时间等 + */ api: { + // 合并外部配置中的api配置 ...externalConfig.api, - // 优先使用外部配置,其次使用环境变量,最后使用默认值 + // API基础URL(优先使用外部配置,其次使用环境变量,最后使用默认值) baseUrl: externalConfig.api?.baseUrl || import.meta.env.VITE_API_BASE_URL || '/api', + // API请求超时时间(毫秒)(优先使用外部配置,否则使用默认值) timeout: externalConfig.api?.timeout || 30000, // 30秒超时 + // 请求失败重试次数(优先使用外部配置,否则使用默认值) retryCount: externalConfig.api?.retryCount || 3, // 请求失败重试次数 + // 重试间隔时间(毫秒)(优先使用外部配置,否则使用默认值) retryDelay: externalConfig.api?.retryDelay || 1000 // 重试间隔(毫秒) }, - // 布局配置 + /** + * 布局配置 + * 包含应用界面布局相关的配置,如侧边栏宽度、头部高度等 + */ layout: { - sidebarWidth: 240, // 侧边栏宽度 - headerHeight: 60, // 头部高度 - transitionDuration: 300, // 过渡动画时长 - showBreadcrumb: true // 是否显示面包屑 + // 侧边栏宽度(像素) + sidebarWidth: 240, + // 头部导航栏高度(像素) + headerHeight: 60, + // 过渡动画时长(毫秒) + transitionDuration: 300, + // 是否显示面包屑导航 + showBreadcrumb: true }, - // 主题配置 + /** + * 主题配置 + * 包含应用界面主题相关的配置,如颜色、字体大小等 + */ theme: { - primaryColor: '#1890ff', // 主色调 - successColor: '#52c41a', // 成功色 - warningColor: '#faad14', // 警告色 - errorColor: '#f5222d', // 错误色 - fontSize: 14, // 基础字体大小 - darkMode: false // 是否开启暗黑模式 + // 主色调(蓝色) + primaryColor: '#1890ff', + // 成功状态颜色(绿色) + successColor: '#52c41a', + // 警告状态颜色(黄色) + warningColor: '#faad14', + // 错误状态颜色(红色) + errorColor: '#f5222d', + // 基础字体大小(像素) + fontSize: 14, + // 是否开启暗黑模式 + darkMode: false }, - // 缓存配置 + /** + * 缓存配置 + * 包含应用数据缓存相关的配置 + */ cache: { + // 是否启用缓存 enable: true, - defaultExpireTime: 24 * 60 * 60 * 1000 // 默认过期时间(24小时) + // 默认缓存过期时间(毫秒)(24小时) + defaultExpireTime: 24 * 60 * 60 * 1000 }, - // 分页配置 + /** + * 分页配置 + * 包含数据分页显示相关的配置 + */ pagination: { + // 可选的每页记录数 pageSizes: [10, 20, 50, 100], + // 默认每页记录数 defaultPageSize: 20 }, - // 上传配置 + /** + * 上传配置 + * 包含文件上传相关的配置,如最大文件大小、允许的文件类型等 + */ upload: { - maxSize: 10 * 1024 * 1024, // 最大文件大小(10MB) + // 最大文件大小(字节)(10MB) + maxSize: 10 * 1024 * 1024, + // 允许上传的文件类型 allowedTypes: ['jpg', 'jpeg', 'png', 'gif', 'pdf', 'doc', 'docx', 'xls', 'xlsx'] }, - // 日期时间格式 + /** + * 日期时间格式配置 + * 包含日期和时间的格式化配置 + */ date: { + // 日期格式 format: 'YYYY-MM-DD', + // 时间格式 timeFormat: 'HH:mm:ss', + // 日期时间格式 dateTimeFormat: 'YYYY-MM-DD HH:mm:ss' }, - // 权限配置 + /** + * 权限配置 + * 包含应用权限相关的配置,如超级管理员角色、默认角色等 + */ permission: { + // 超级管理员角色名称 superAdminRole: 'admin', + // 默认用户角色名称 defaultRole: 'user' } } \ No newline at end of file diff --git a/src/hooks/useFence.js b/src/hooks/useFence.js index 84f952a..013bd1d 100644 --- a/src/hooks/useFence.js +++ b/src/hooks/useFence.js @@ -1,63 +1,136 @@ +/** + * 围栏生成Hook + * 提供在3D地图上生成围栏的功能 + * 基于THREE.js实现3D围栏的创建、纹理生成和材质配置 + * + * @param {Object} options - 围栏配置选项 + * @param {number} options.height - 围栏高度,默认值为20 + * @param {string} options.color - 围栏颜色,默认值为橙色'#ff7f50' + * @param {boolean} options.isCloseTop - 是否封闭顶部,默认值为false(不封闭) + * @returns {Object} - 包含生成围栏函数的对象 + */ export function useFence({height = 20, isCloseTop = false, color = '#ff7f50'}) { const { THREE } = window.VgoMap - const tex = generateTexture(64) - if(tex) { - tex.wrapT = tex.wrapS = THREE.RepeatWrapping - } + /** + * 生成围栏材质纹理 + * 使用64x64的HTML5 Canvas创建垂直渐变纹理 + * 纹理将用于围栏的侧面材质 + */ +const tex = generateTexture(64) +// 设置纹理的重复模式为REPEAT +if(tex) { + tex.wrapT = tex.wrapS = THREE.RepeatWrapping +} +/** + * 生成围栏函数 + * 创建一个3D围栏网格对象,使用THREE.js的ExtrudeGeometry实现挤出效果 + * + * @param {Array} points - 围栏的顶点数组,按顺时针或逆时针顺序排列 + * @returns {THREE.Mesh} - 生成的围栏3D网格对象 + */ function generateFence(points) { + // 创建THREE.js形状对象 const shape = new THREE.Shape() + // 根据输入的顶点数组设置形状 shape.setFromPoints(points) + + // 创建挤出几何体,将2D形状挤出为3D围栏 const geo = new THREE.ExtrudeGeometry(shape, { - steps: 2, - depth: height, - bevelEnabled: false, + steps: 2, // 挤出的层数 + depth: height, // 挤出的深度(围栏高度) + bevelEnabled: false, // 禁用斜角 }) + + // 创建围栏材质数组 const mater = [ + // 顶部材质 new THREE.MeshBasicMaterial({ color, transparent: true, opacity: 0.1, - visible: isCloseTop, + visible: isCloseTop, // 根据配置决定是否显示顶部 }), + // 侧面材质 new THREE.MeshBasicMaterial({ - map: tex, + map: tex, // 使用渐变纹理 transparent: true, color, opacity: 0.1, - side: 2, + side: 2, // 设置为双面显示(THREE.DoubleSide) }) ] + + // 创建围栏网格对象 const mesh = new THREE.Mesh(geo, mater) + + // 统一设置所有材质的透明度为0.7 mesh.material.forEach(m => m.opacity = 0.7) + return mesh } +/** + * 生成纹理函数 + * 创建一个垂直渐变的HTML5 Canvas纹理,用于围栏侧面的材质 + * + * @param {number} size - 纹理大小,默认值为64 + * @returns {THREE.Texture|null} - 生成的THREE.js纹理对象,如果创建失败则返回null + */ function generateTexture (size = 64) { + // 创建HTML5 Canvas元素 let canvas = document.createElement('canvas') canvas.width = size canvas.height = size + + // 获取Canvas 2D上下文 let ctx = canvas.getContext('2d') - if(!ctx) return + if(!ctx) return null // 如果无法获取上下文,则返回null + + // 创建垂直线性渐变 let linearGradient = ctx.createLinearGradient(0, 0, 0, size) + // 顶部:完全透明 linearGradient.addColorStop(0.2, hexToRgba(color, 0.0)) + // 中部:半透明 linearGradient.addColorStop(0.8, hexToRgba(color, 0.5)) + // 底部:不透明 linearGradient.addColorStop(1.0, hexToRgba(color, 1.0)) + + // 使用渐变填充整个Canvas ctx.fillStyle = linearGradient ctx.fillRect(0, 0, size, size) + // 创建THREE.js纹理对象 let texture = new THREE.Texture(canvas) - texture.needsUpdate = true // 必须 + texture.needsUpdate = true // 必须设置为true,通知THREE.js更新纹理 + return texture } +/** + * 将十六进制颜色转换为RGBA格式 + * 用于在Canvas绘制时设置带透明度的颜色 + * + * @param {string} hex - 十六进制颜色值,格式为'#RRGGBB' + * @param {number} opacity - 透明度,取值范围为0-1,默认值为1(完全不透明) + * @returns {string} - RGBA格式的颜色字符串,格式为'rgba(R,G,B,A)' + */ function hexToRgba (hex, opacity = 1) { - return 'rgba(' + parseInt('0x' + hex.slice(1, 3)) + ',' + parseInt('0x' + hex.slice(3, 5)) + ',' + - parseInt('0x' + hex.slice(5, 7)) + ',' + opacity + ')' + // 提取R、G、B分量并转换为十进制 + const r = parseInt('0x' + hex.slice(1, 3)) + const g = parseInt('0x' + hex.slice(3, 5)) + const b = parseInt('0x' + hex.slice(5, 7)) + + // 构造并返回RGBA格式的颜色字符串 + return `rgba(${r},${g},${b},${opacity})` } +/** + * 返回hook提供的函数 + * 将内部实现的generateFence函数暴露给外部使用 + */ return { - generateFence, + generateFence, // 生成围栏的主要函数 } } \ No newline at end of file diff --git a/src/main.js b/src/main.js index cf249b5..67fdd72 100644 --- a/src/main.js +++ b/src/main.js @@ -1,29 +1,71 @@ +/** +/** + * 应用入口文件 + * 负责创建Vue应用实例、配置插件、注册全局组件和属性 + * 是整个Vue应用的启动点和配置中心 + */ + +// 导入Vue的createApp函数,用于创建应用实例 import { createApp } from 'vue' + +// 导入应用的根组件 import App from './App.vue' + +// 导入路由配置 import router from './router' + +// 导入Element Plus UI组件库 import ElementPlus from 'element-plus' + +// 导入Element Plus的CSS样式 import 'element-plus/dist/index.css' + +// 导入全局自定义样式 import './assets/main.css' + +// 导入Pinia状态管理库 import { createPinia } from 'pinia' + +// 导入Element Plus的所有图标组件 import * as ElementPlusIconsVue from '@element-plus/icons-vue' + // 导入全局配置文件 import config from './config' -const app = createApp(App) -const pinia = createPinia() +/** + * 创建Vue应用实例和状态管理实例 + */ +const app = createApp(App) // 创建Vue应用实例,以App组件为根组件 +const pinia = createPinia() // 创建Pinia状态管理实例 -// 全局注册配置 +/** + * 全局注册配置对象 + * 使配置在整个应用中可访问 + */ +// 为Options API提供全局配置属性 app.config.globalProperties.$config = config -// 为Composition API提供配置 + +// 为Composition API提供全局配置,通过inject('config')获取 app.provide('config', config) -// 全局注册所有图标 +/** + * 全局注册所有Element Plus图标组件 + * 使所有图标组件在应用中无需单独导入即可使用 + */ for (const [key, component] of Object.entries(ElementPlusIconsVue)) { - app.component(key, component) + app.component(key, component) // 注册每个图标组件 } -app.use(ElementPlus) -app.use(router) -app.use(pinia) +/** + * 应用插件配置 + * 依次注册Element Plus、Vue Router和Pinia插件 + */ +app.use(ElementPlus) // 注册Element Plus UI组件库 +app.use(router) // 注册Vue Router路由管理 +app.use(pinia) // 注册Pinia状态管理 -app.mount('#app') \ No newline at end of file +/** + * 将Vue应用实例挂载到DOM元素 + * 使应用在id为'app'的DOM元素中渲染 + */ +app.mount('#app') // 将应用挂载到HTML中的#app元素上 \ No newline at end of file diff --git a/src/router/index.js b/src/router/index.js index dbf650e..49d722d 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -1,37 +1,52 @@ +/** +/** + * 应用路由配置文件 + * 定义应用的路由结构、组件映射和权限控制规则 + */ + +// 导入Vue Router核心函数 import { createRouter, createWebHistory } from 'vue-router' -import LoginView from '../views/LoginView.vue' -import LayoutView from '../views/LayoutView.vue' -import HomeView from '../views/HomeView.vue' -import KeyManager from '../views/Key/KeyManager.vue' + +// 导入页面组件 +import LoginView from '../views/LoginView.vue' // 登录页面组件 +import LayoutView from '../views/LayoutView.vue' // 主布局组件 +import HomeView from '../views/HomeView.vue' // 首页组件 +import KeyManager from '../views/Key/KeyManager.vue' // 钥匙管理组件 + +// 导入用户状态管理 import { useUserStore } from '../stores/user' +/** + * 创建路由实例 + * 使用HTML5 History模式管理路由,需要服务器端配置支持 + */ const router = createRouter({ history: createWebHistory(import.meta.env.BASE_URL), routes: [ { path: '/login', name: 'login', - component: LoginView + component: LoginView // 登录页面,无需身份验证即可访问 }, { path: '/', name: 'layout', - component: LayoutView, - meta: { requiresAuth: true }, + component: LayoutView, // 主布局页面,包含侧边栏、顶栏等公共布局元素 + meta: { requiresAuth: true }, // 标记此路由需要登录权限 children: [ { path: '', name: 'home', components: { - map: HomeView + map: HomeView // 默认视图,渲染首页组件到map视图容器 } }, { path: '/Key/KeyManager', name: 'keyManager', components: { - map: HomeView, - right: KeyManager + map: HomeView, // 左侧显示地图视图 + right: KeyManager // 右侧显示钥匙管理视图 } } // 可以在这里添加更多子路由 @@ -40,18 +55,26 @@ const router = createRouter({ ] }) -// 路由守卫 +/** + * 全局前置路由守卫 + * 用于拦截所有路由跳转请求,实现基于角色的权限控制 + */ router.beforeEach((to, from, next) => { - const userStore = useUserStore() - const isLoggedIn = userStore.isLoggedIn + const userStore = useUserStore() // 获取用户状态管理实例 + const isLoggedIn = userStore.isLoggedIn // 检查用户是否已登录 + // 检查路由是否需要身份验证 if (to.matched.some(record => record.meta.requiresAuth)) { + // 需要身份验证的路由 if (!isLoggedIn) { + // 未登录时重定向到登录页面 next('/login') } else { + // 已登录用户允许访问 next() } } else { + // 不需要身份验证的路由直接放行 next() } }) diff --git a/src/stores/user.js b/src/stores/user.js index cecaa8a..7695acb 100644 --- a/src/stores/user.js +++ b/src/stores/user.js @@ -1,28 +1,63 @@ +/** + * 用户状态管理模块 + * 使用Pinia实现的用户状态管理,负责管理用户的登录状态、认证token和用户信息 + */ + +// 导入Pinia的store定义函数 import { defineStore } from 'pinia' + +// 导入Axios用于API请求 import axios from 'axios' +/** + * 用户状态管理store定义 + * 使用Pinia的defineStore函数创建用户状态管理模块 + * @returns {Object} - 用户store实例,提供用户状态管理的各种方法和属性 + */ export const useUserStore = defineStore('user', { + /** + * 状态定义 + * 初始化用户相关的状态数据 + */ state: () => ({ - token: '', - userInfo: null, - menuList: [] + token: '', // 用户认证令牌,用于API请求认证 + userInfo: null, // 用户详细信息对象 + menuList: [] // 用户可访问的菜单列表 }), + /** + * 计算属性(getters) + * 提供基于state的派生状态,用于获取用户登录状态等信息 + */ getters: { + /** + * 检查用户是否已登录 + * @param {Object} state - store当前状态 + * @returns {boolean} - 用户是否已登录的布尔值 + */ isLoggedIn: (state) => !!state.token }, + /** + * 动作方法(actions) + * 提供修改状态的方法,包括登录、登出和初始化用户信息 + */ actions: { - // 用户登录 + /** + * 用户登录方法 + * 处理用户登录请求,保存登录状态和用户信息 + * @param {Object} credentials - 用户登录凭据,包含用户名和密码等信息 + * @returns {Promise} - 登录结果对象,包含成功状态和错误信息 + */ async login(credentials) { try { // 模拟登录请求 const response = await axios.post('/api/login', credentials) const { token, userInfo, menuList } = response.data - this.token = token - this.userInfo = userInfo - this.menuList = menuList + this.token = token // 保存token到状态 + this.userInfo = userInfo // 保存用户信息 + this.menuList = menuList // 保存菜单列表 // 保存token到localStorage localStorage.setItem('token', token) @@ -34,15 +69,22 @@ export const useUserStore = defineStore('user', { } }, - // 用户登出 + /** + * 用户登出方法 + * 清除用户所有状态信息并移除本地存储的token + */ logout() { - this.token = '' - this.userInfo = null - this.menuList = [] - localStorage.removeItem('token') + this.token = '' // 清除token + this.userInfo = null // 清除用户信息 + this.menuList = [] // 清除菜单列表 + localStorage.removeItem('token') // 移除本地存储的token }, - // 初始化用户信息 + /** + * 初始化用户信息方法 + * 从本地存储恢复用户登录状态并获取最新用户信息 + * @returns {Promise} - 初始化过程的Promise + */ async initUserInfo() { const token = localStorage.getItem('token') if (token) { @@ -50,11 +92,11 @@ export const useUserStore = defineStore('user', { try { // 模拟获取用户信息 const response = await axios.get('/api/user/info') - this.userInfo = response.data.userInfo - this.menuList = response.data.menuList + this.userInfo = response.data.userInfo // 保存用户信息 + this.menuList = response.data.menuList // 保存菜单列表 } catch (error) { console.error('获取用户信息失败:', error) - this.logout() + this.logout() // 失败时自动登出 } } } diff --git a/src/views/HomeView.vue b/src/views/HomeView.vue index e66e511..f499ae5 100644 --- a/src/views/HomeView.vue +++ b/src/views/HomeView.vue @@ -1,18 +1,21 @@ + - + - + - + + 系统状态 - + + 最后更新: {{ currentDate }} - + API地址 @@ -32,10 +35,21 @@ \ No newline at end of file