vue3 脚手架初始化项目生成文件的介绍
文章目录
- 一、介绍
- 二、举例说明
- 1.src/http/index.js
- 2.src/router/index.js
- 3.src/router/routes.js
- 4.src/stores/index.js
- 5.src/App.vue
- 6.src/main.js
- 7.babel.config.js
- 8.jsconfig.json
- 9.vue.config.js
- 10. .env
- 11.src/mock/index.js
- 12.src/mock/mock-i18n.js
- 13.src/locales/en.json
- 14.src/locales/zh.json
- 15.src/locales/index.js
- 16.src/views/pages/system/system.js
- 17. .gitignore
- 18.package.json
一、介绍
- node_modules文件夹:项目依赖文件夹
- public文件夹:一般放置一些静态资源(图片),需要注意,放在public文件夹中的静态资源,在webpack打包时,会原封不动的打包到dist文件夹中。
- src文件夹(程序员源代码文件夹):
- assets文件夹:一般也是放置静态资源(一般放置多个组件共用的静态资源),需要注意,放在assets文件夹里的静态资源,在webpack打包时,会把此静态资源当作一个模块,打包到JS文件中。
- components文件夹:一般放置非路由组件(全局组件)。
- http文件夹:
- index.js:集中管理 HTTP 请求的配置和处理逻辑,使得在应用中发送 HTTP 请求变得更加简单和一致。通过使用 Axios 实例和拦截器,可以有效地管理请求和响应,处理身份验证和错误情况,确保用户体验的流畅性。
- App.vue文件:唯一的根组件。
- main.js文件:程序的入口文件,也是整个程序当中最先执行的文件。
- pages|views文件夹:路由组件。
- router文件夹:路由相关配置。
- index.js:配置路由
- routes.js:路由规则定义(该文件夹可有可无,可全部放在上面index.js中)
- store文件夹:
- index.js:vuex相关配置。
- utils文件夹:该文件夹里面经常放一些常用的功能模块,比如正则、临时身份UUID等等。
- locales:
- en.json:英文词条
- zh.json:中文词条
- index.js:国际化相关配置
- mock文件夹:模拟json文件及相关接口调用
- index.js:是一个用于模拟 API 响应的模块,通常在前端开发中使用。它利用 mockjs 库来创建虚拟的 API 接口,以便在开发和测试过程中不依赖于后端服务。以下是这个文件的主要功能和组成部分。
- mock-i18n.js:模拟的词条文件。
- babel.config.js文件:babel配置文件(bable相关)。
- package.json文件:相当于项目的“身份证”,记录了项目的相关信息(如名字、依赖、运行方式等等)。
- package-lock.json文件:又叫“缓存性文件”,跟踪被安装的每个软件包的确切版本,以便产品可以以相同的方式被 100% 复制(即使软件包的维护者更新了软件包)。即为什么刚开始启动慢,而后面启动就很快?因为缓存性文件都记录下来相关信息了,所以后续启动很快。
- README.md文件:项目的说明性文件
- vue.config.js文件:用于关闭ESLINT校验工具+配置代理服务器解决跨域
- jsconfig.json文件:给src文件夹简写方法,配置别名,方便引入资源
二、举例说明
1.src/http/index.js
/**
* @Name:
* @Author:贾志博
* @description:
*/
import axios, { AxiosInstance, AxiosResponse } from "axios";
import {useStore} from "@/stores";
// 创建http请求的实例对象
const $http = axios.create({
timeout: 30000,
withCredentials: false,
headers: {
'Content-Type': 'application/json;charset=UTF-8',
'Access-Control-Allow-Origin': '*',
'X-Requested-With': 'XMLHttpRequest'
}
});
// 添加请求拦截器
// $http.interceptors.request.use((config) => {
// const token = ''
// if (token) {
// config.headers.Authorization = token;
// }
// return config;
// }, (error) => {
// return Promise.reject(error)
// });
// 添加响应拦截器
$http.interceptors.response.use(
response=> {
const { data } = response
if (response.status === 200) {
return data
}
},
error => {
if (error.status === 401) {
sessionStorage.removeItem('principal');
localStorage.removeItem('mldn-session-id')
// 路由跳转到登录界面
window.location.href = `${window.location.origin}/#/`
} else if (error.status == 502) {
sessionStorage.removeItem('principal');
localStorage.removeItem('mldn-session-id')
window.location.href = `${window.location.origin}/#/`
}
return Promise.resolve({code: error.status})
}
);
const Method = {
GET: 'get',
POST: 'post',
DELETE: 'delete',
PUT: 'put',
}
export { $http, Method };
2.src/router/index.js
import { createRouter, createWebHashHistory } from 'vue-router'
import { baseRoutes, routes } from "./routes";
import { useStore } from "@/stores";
import {queryUserMenu} from "@/views/pages/system/system";
import { commonResponse } from "@/views/pages/_common";
import { getSystemType } from "@/views/login/_request";
const router = createRouter({
history: createWebHashHistory(process.env.BASE_URL),
routes: baseRoutes
})
let permissionRoutes = []
let permissionMap = new Map()
router.beforeEach((to, from, next) => {
var principal = localStorage.getItem('mldn-session-id');
var session_prncipal = sessionStorage.getItem('principal');
const userName = localStorage.getItem("xnms-user-name");
if (session_prncipal == null && principal == null) {
principal = null;
}
if (principal == null) {
if (to.name === 'login') {
useStore().hasAuth = false;
next()
} else {
useStore().hasAuth = false;
next({
name: 'login'
})
}
} else if (to.name === 'login') {
if(permissionRoutes.length > 0) {
const initRouterPush = "/pages/" + permissionRoutes[0].path + "/" + permissionRoutes[0].children[0].path;
useStore().setSelectedMenuKeyFunction(permissionRoutes[0].path)
useStore().setSelectedMenuItemKeyFunction([permissionRoutes[0].path + "_" + permissionRoutes[0].children[0].path]);
useStore().setOpenMenuItemFunction([permissionRoutes[0].path]);
next({path: initRouterPush, replace: true});
} else {
hasAuthHandle(to, next, userName);
}
} else {
hasAuthHandle(to, next, userName);
}
});
const hasAuthHandle = (to, next, userName) => {
if(!useStore().hasAuth) {
getSystemType().then(resp => {
localStorage.setItem('system-type', resp.data)
useStore().setMode(resp.data)
queryUserMenu(userName).then(response => {
commonResponse({
response,
onSuccess: () => {
permissionRoutes = []
permissionMap = new Map()
try {
const deepRoutes = JSON.parse(JSON.stringify(routes));
fillPermissionMap(permissionMap, response)
permissionRoutes = handleRoutes(deepRoutes, permissionMap)
if (permissionRoutes.length > 0) {
useStore().routes.value = permissionRoutes
permissionRoutes.forEach(item => {
router.addRoute('pages', item)
})
}
} catch (error) {
console.error("Error during deep copy:", error);
}
useStore().hasAuth = true
// 检查 to 是否在动态加载的路由里
const isRouteExists = permissionRoutes.some(route => {
if (route.name === to.name || route.path === to.path) {
return true;
}
if (route.children) {
return route.children.some(childRoute => {
return childRoute.name === to.name || childRoute.path === to.path;
});
}
return false;
});
if (isRouteExists) {
next({...to, replace: true})
} else {
// 如果 to 不在动态路由里,可以导航到默认页面
if (permissionRoutes.length > 0) {
const initRouterPush = "/pages/" + permissionRoutes[0].path + "/" + permissionRoutes[0].children[0].path;
useStore().setSelectedMenuKeyFunction(permissionRoutes[0].path)
useStore().setSelectedMenuItemKeyFunction([permissionRoutes[0].path + "_" + permissionRoutes[0].children[0].path]);
useStore().setOpenMenuItemFunction([permissionRoutes[0].path]);
next({path: initRouterPush, replace: true});
}
}
}
})
})
})
} else {
next()
}
}
const fillPermissionMap = (permissionMap, response) => {
const userName = localStorage.getItem('xnms-user-name')
if (userName == "Admin") {
response.data?.forEach(item => {
const { code, canLook, canEdit } = item;
permissionMap.set(code, {
view: canLook == true ? 1 : 0,
edit: canEdit == true ? 1 : 0
})
})
} else {
response.data?.forEach(item => {
const { code, canLook, canEdit } = item;
if (code.length === 4) {
permissionMap.set(code, {
view: canLook == true ? 1 : 0,
edit: canEdit == true ? 1 : 0
})
}
})
response.data?.forEach(item => {
const { code} = item;
if (code.length === 2) {
const children = response.data.filter(child => child.pid === code);
const hasViewVisibleChild = children.some(child => child.canLook === 1);
const hasEditVisibleChild = children.some(child => child.canEdit === 1);
permissionMap.set(code, {
view: hasViewVisibleChild ? 1 : 0,
edit: hasEditVisibleChild ? 1 : 0
});
}
})
}
}
const handleRoutes = (routes, permissionMap) => {
for (let i = 0; i < routes.length; i++) {
const item = routes[i]
if (permissionMap?.get(routes[i].meta.key)?.view) {
routes[i].meta.edit = permissionMap?.get(routes[i].meta.key)?.edit
if (item.children?.length) {
handleRoutes(item.children, permissionMap)
} else {
if (!item.component) {
let fileExistCommon = true
let pathStr = ''
try {
const path = item.name.split('_')
pathStr = path.reduce((pre, cur, index) => {
return `${pre}/${index === path.length - 1 ? cur.charAt(0).toUpperCase() + cur.slice(1) : cur}`
})
require(`@/views/pages/${pathStr}.vue`)
} catch (e) {
fileExistCommon = false
}
let fileExistDiff = true
const modeName = useStore().getMode() ? 'XPT' : 'NOR'
try {
require(`@/views/pages/${pathStr}-${modeName}.vue`)
} catch (e) {
fileExistDiff = false
}
if (fileExistDiff) {
item.component = () => import(`@/views/pages/${pathStr}-${modeName}.vue`)
} else if (fileExistCommon) {
item.component = () => import(`@/views/pages/${pathStr}.vue`)
} else {
item.component = () => import('@/views/pages/_error/404.vue')
}
}
}
} else {
routes.splice(i, 1)
i--
}
}
return routes
}
export default router
3.src/router/routes.js
import Login from "@/views/login/index.vue";
import Index from "@/views/pages/index.vue";
export const baseRoutes = [
{
path: '/',
name: 'login',
component: Login,
},
{
path: '/pages',
name: 'pages',
component: Index,
children: []
}
]
export const routes = [
{
path: 'topology',
name: 'topology',
meta: {
key: '00'
},
children: [
{
path: 'topologyView',
name: 'topology_topologyView',
breadcrumb: 'menu_topoView',
meta: {
key: '0000'
},
},
{
path: 'electronicMap',
name: 'topology_electronicMap',
breadcrumb: 'menu_electronicMap',
meta: {
key: '0001'
},
},
]
}
]
4.src/stores/index.js
import {defineStore} from "pinia";
import {ref} from 'vue'
export const useStore = defineStore('main', () => {
const mode = ref(0)
const setMode = (modeVal) => {
mode.value = modeVal
}
const getMode = () => {
return mode.value
}
const openMenuItem = ref([]);
const setOpenMenuItemFunction = (modeVal) => {
openMenuItem.value = modeVal
}
const getOpenMenuItemFunction = () => {
return openMenuItem
}
const selectedMenuItemKey = ref(null);
const setSelectedMenuItemKeyFunction = (modeVal) => {
selectedMenuItemKey.value = modeVal
}
const getSelectedMenuItemKeyFunction = () => {
return selectedMenuItemKey
}
const selectedMenuKey = ref("");
const setSelectedMenuKeyFunction = (modeVal) => {
selectedMenuKey.value = modeVal
}
const getSelectedMenuKeyFunction = () => {
return selectedMenuKey
}
const hasAuth = ref(false)
const routes = ref([])
const popoverVisible = ref(false);
const popoverPosition = ref({ top: 0, left: 0 });
const selectTopoNode = ref(null)
const websocketRepeaterList = ref([])
return {
getMode,
setMode,
setSelectedMenuKeyFunction,
getSelectedMenuKeyFunction,
setSelectedMenuItemKeyFunction,
getSelectedMenuItemKeyFunction,
setOpenMenuItemFunction,
getOpenMenuItemFunction,
hasAuth,
routes,
popoverVisible,
popoverPosition,
selectTopoNode,
websocketRepeaterList,
}
})
使用方式:在其他xx.vue页面中
import { useStore } from "@/stores";
useStore().hasAuth = false;
5.src/App.vue
<template>
<router-view/>
</template>
<script setup>
import {reactive, provide} from "vue";
import {useI18n} from "vue-i18n";
import {useStore} from "@/stores";
import {getSystemLanguage} from "@/views/pages/system/system";
const userInfo = reactive({
userId: '',
token: ''
})
provide('t', useI18n().t)
provide('userInfo', userInfo)
const getSystemMode = () => {
getSystemLanguage().then(response => {
const mode = response.data
if (mode) {
localStorage.setItem('xnms-mode', mode)
useStore().setMode(parseInt(mode))
}
})
}
getSystemMode()
</script>
<style>
</style>
6.src/main.js
import { createApp } from 'vue';
import App from './App.vue';
import { createPinia } from "pinia";
// import getRouter from '@/router';
import router from '@/router'
import getI18n from '@/locales';
import SelfComponents from "@/views/pages/_common/selfComponents/index";
import ArcoVue, {Message} from '@arco-design/web-vue';
import ArcoVueIcon from '@arco-design/web-vue/es/icon';
import '@arco-design/web-vue/dist/arco.css';
import '@/assets/index.less';
window.Message = Message
if (process.env.NODE_ENV === 'development') {
// import('@/mock')
}
const store = createPinia()
const promise = Promise.all([getI18n()])
const _beforeMount = await promise
// window.i18n = _beforeMount[0]
const app = createApp(App)
app
.use(_beforeMount[0])
.use(router)
.use(ArcoVue)
.use(ArcoVueIcon)
.use(store)
.use(SelfComponents)
app.mount('#app')
7.babel.config.js
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset'
]
}
8.jsconfig.json
{
"compilerOptions": {
"target": "es5",
"module": "esnext",
"baseUrl": "./",
"moduleResolution": "node",
"paths": {
"@/*": [
"src/*"
]
},
"lib": [
"esnext",
"dom",
"dom.iterable",
"scripthost"
]
}
}
9.vue.config.js
const { defineConfig } = require('@vue/cli-service')
const path = require('path')
// const target = 'http://127.0.0.1:61000/'
// const target = 'http://10.110.24.117:62000/'
const target = 'http://10.110.24.62:61000/'
module.exports = defineConfig({
// publicPath: process.env.NODE_ENV === 'development' ? '' : '/XNMS',
transpileDependencies: true,
lintOnSave: false,
assetsDir: 'assets',
devServer: {
proxy: {
'/api': {
target,
changeOrigin: true,
},
}
},
pluginOptions: {
i18n: {
locale: 'en',
fallbackLocale: 'en',
localeDir: 'locales',
enableLegacy: false,
runtimeOnly: false,
compositionOnly: false,
fullInstall: true
}
},
chainWebpack: config => {
config.module.rule('svg')
.exclude.add(path.resolve('src/assets/svg'))
config.module.rule('icons')
.test(/\.svg$/)
.include.add(path.resolve('src/assets/svg')).end()
.use('svg-sprite-loader')
.loader('svg-sprite-loader')
.options({ symbolId: 'icon-[name]' })
}
})
10. .env
VUE_APP_I18N_LOCALE=en
VUE_APP_I18N_FALLBACK_LOCALE=en
11.src/mock/index.js
import Mock from 'mockjs'
import {i18nList} from './mock-i18n'
Mock.mock('/menu/list', 'get', {
status: 0,
dataList: [
{
path: 'topology',
children: [
{
path: 'topologyView',
name: 'topology_topologyView',
},
{
path: 'electronicMap',
name: 'topology_electronicMap',
},
]
}
]
})
Mock.mock('/api/in', 'get', {
code: 200,
data: 1,
msg: "成功",
pagination: null
})
Mock.mock(/^\/api\/in\/[\S]*$/, 'get', {
code: 200,
msg: '',
data: {
...i18nList()
}
})
Mock.mock('/api/site_statistic', 'post', {
"code": 200,
"data": [
{
"businessData": {
"datas": {
"additionalProp1": 1,
"additionalProp2": 2,
"additionalProp3": 3,
"additionalProp4": 4,
"additionalProp5": 5
},
},
"siteData": {
"datas": {
"additionalProp1": 1,
"additionalProp2": 2,
"additionalProp3": 3,
"additionalProp4": 4,
"additionalProp5": 5
},
},
"siteID": 1,
"siteAlias": "站点1",
},
{
"businessData": {
"datas": {
"additionalProp1": 1,
"additionalProp2": 2,
"additionalProp3": 3,
"additionalProp4": 4,
"additionalProp5": 5
},
},
"siteData": {
"datas": {
"additionalProp1": 1,
"additionalProp2": 2,
"additionalProp3": 3,
"additionalProp4": 4,
"additionalProp5": 5
},
},
"siteID": 2,
"siteAlias": "站点2",
}
],
"msg": "string",
"pagination": {
"currentPage": 0,
"pageSize": 0,
"totalPages": 0,
"totalRecords": 0
}
})
Mock.mock('/api/getTransferBusinessData', 'post', {
code: 200,
data: [
{
id: '1',
name: '站点1',
byDateLine: [{
date: '2021-01-01',
value1: 1,
value2: 2
}, {
date: '2021-01-02',
value1: 3,
value2: 4
}, {
date: '2021-01-04',
value1: 3,
value2: 6
}],
byBusinessTimePie: {
1: 2,
2: 4,
3: 6,
4: 1,
5: 3
},
byTimeLine: [{
date: '2021-01-01',
value1: 1,
value2: 2
}, {
date: '2021-01-02',
value1: 3,
value2: 4
}, {
date: '2021-01-04',
value1: 3,
value2: 6
}],
},
{
id: '2',
name: '站点2',
byDateLine: [{
date: '2021-01-01',
value1: 1,
value2: 2
}, {
date: '2021-01-02',
value1: 3,
value2: 4
}, {
date: '2021-01-04',
],
}
]
})
12.src/mock/mock-i18n.js
export const i18nList = () => {
return {
"Title": "XNMS客户端",
"XPTTitle": "XNMS",
"Login": "登 录",
"LoginCancel": "取消登录",
"LoginCheckPassword": "验证中…",
}
}
13.src/locales/en.json
{
"menu_topology": "Topology View",
"menu_alarm": "Monitoring Alarm",
"menu_device": "Equipment Parameters",
"menu_data": "Data Query",
"menu_business": "Business Statistics",
}
14.src/locales/zh.json
{
"menu_topology": "拓扑展示",
"menu_alarm": "监控告警",
"menu_device": "设备参数",
"menu_data": "数据查询",
"menu_business": "业务统计",
}
15.src/locales/index.js
import { createI18n } from 'vue-i18n'
import {getI18nLanguagePkg, getNowLanguageType} from "@/views/login/_request";
const getNowLanguage = async () => {
return new Promise((resolve, reject) => {
getNowLanguageType().then(response => {
if (response.code === 200) {
resolve(response.data)
} else {
window.Message.warning(response.msg)
}
})
})
}
const loadRemoteMessages = async (language) => {
return new Promise((resolve, reject) => {
getI18nLanguagePkg(language).then(response => {
if (response.code === 200) {
const messages = {}
messages[languageEnum[language]] = response.data
resolve(messages)
} else {
window.Message.warning(response.msg)
}
})
})
}
const getI18n = async () => {
const language = await getNowLanguage()
localStorage.setItem('xnms-language', language)
const remoteMessages = await loadRemoteMessages(language)
const i18n = createI18n({
legacy: false,
locale: languageEnum[language],
fallbackLocale: 'zh',
messages: remoteMessages,
globalInjection: true
})
return new Promise((resolve) => {
resolve(i18n)
})
}
const languageEnum = {
0: 'en',
1: 'zh',
2: 'ru'
}
export default getI18n
16.src/views/pages/system/system.js
import { $http, Method } from "@/http";
export const getDeviceManageList = (data) => {
return $http({
url: `/api/deviceManage/queryDeviceList`,
method: Method.POST,
data
})
}
export const getSystemLanguage = (data) => {
return $http({
url: `/api/config`,
method: Method.GET,
data
})
}
export const deleteDevice = (data) => {
return $http({
url: `/api/deviceManage/deleteRepeater`,
method: Method.DELETE,
data
})
}
export const updateDevive = (data) => {
return $http({
url: `/api/deviceManage/updateDeviceSnmpV3`,
method: Method.PUT,
data
})
}
17. .gitignore
.DS_Store
node_modules
/dist
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
18.package.json
{
"name": "xnms",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint",
"i18n:report": "vue-cli-service i18n:report --src \"./src/**/*.?(js|vue)\" --locales \"./src/locales/**/*.json\""
},
"dependencies": {
"axios": "^1.7.7",
"core-js": "^3.8.3",
"echarts": "^5.5.1",
"html2canvas": "^1.4.1",
"jspdf": "^3.0.0",
"leaflet": "^1.9.4",
"leaflet-polylinedecorator": "^1.6.0",
"less": "^4.2.0",
"less-loader": "^12.2.0",
"mockjs": "^1.1.0",
"moment": "^2.30.1",
"pinia": "^2.2.2",
"svg-sprite-loader": "^6.0.11",
"vue": "^3.2.13",
"vue-i18n": "^9.1.0",
"vue-router": "^4.0.3"
},
"devDependencies": {
"@arco-design/web-vue": "^2.56.2",
"@babel/core": "^7.12.16",
"@babel/eslint-parser": "^7.12.16",
"@intlify/vue-i18n-loader": "^3.0.0",
"@vue/cli-plugin-babel": "~5.0.0",
"@vue/cli-plugin-eslint": "~5.0.0",
"@vue/cli-plugin-router": "~5.0.0",
"@vue/cli-service": "~5.0.0",
"eslint": "^7.32.0",
"eslint-plugin-vue": "^8.0.3",
"vue-cli-plugin-i18n": "~2.3.2",
"vue-cli-plugin-mock": "~1.0.3"
},
"eslintConfig": {
"root": true,
"env": {
"node": true
},
"extends": [
"plugin:vue/vue3-essential",
"eslint:recommended"
],
"parserOptions": {
"parser": "@babel/eslint-parser"
},
"rules": {}
},
"browserslist": [
"> 1%",
"last 2 versions",
"not dead",
"not ie 11"
]
}