refactor: 优化路由结构

This commit is contained in:
Bunny 2025-02-28 20:14:37 +08:00
parent dec7d73d8f
commit c447018152
36 changed files with 526 additions and 203 deletions

15
.env
View File

@ -1,5 +1,5 @@
# 应用名称
VITE_APP_TITLE="车辆监控中心"
VITE_APP_TITLE="Vite 模板"
# 平台本地运行端口号
VITE_PORT=7000
@ -13,11 +13,20 @@ VITE_APP_URL=http://localhost:8801
# 如果端口被占用会直接退出,而不是尝试下一个端口
VITE_STRICT_PORT=false
# 是否启用屏幕转vw适配
VITE_POST_CSS_PX_TO_VIEWPORT8_PLUGIN=true
# 是否启用屏幕转vw适配,可以选择 postcss-px-to-viewport-8-plugin || autofit
VITE_POST_CSS_PX_TO_VIEWPORT8_PLUGIN="autofit"
# 是否在打包时使用cdn替换本地库 替换 true 不替换 false
VITE_CDN=false
# 是否使用Mock
VITE_MOCK_DEV_SERVER=true
# mock地址
VITE_MOCK_BASE_API=/mock
# 基础请求路径
VITE_APP_BASE_API=/api
# 是否启用gzip压缩
VITE_COMPRESSION=gzip

View File

@ -7,11 +7,20 @@ VITE_PUBLIC_PATH=/
# 跨域代理地址
VITE_APP_URL=http://localhost:8801
# 基础请求路径
VITE_APP_BASE_API=/api
# mock地址
VITE_MOCK_BASE_API=/mock
# 是否使用Mock
VITE_MOCK_DEV_SERVER=true
# 如果端口被占用会直接退出,而不是尝试下一个端口
VITE_STRICT_PORT=false
# 是否启用屏幕转vw适配
VITE_POST_CSS_PX_TO_VIEWPORT8_PLUGIN=true
# 是否启用屏幕转vw适配,可以选择 postcss-px-to-viewport-8-plugin || autofit
VITE_POST_CSS_PX_TO_VIEWPORT8_PLUGIN="autofit"
# 是否在打包时使用cdn替换本地库 替换 true 不替换 false
VITE_CDN=false

View File

@ -1,5 +1,5 @@
# 平台本地运行端口号
VITE_PORT=8800
VITE_PORT=7000
# 开发环境读取配置文件路径
VITE_PUBLIC_PATH=/
@ -7,11 +7,20 @@ VITE_PUBLIC_PATH=/
# 跨域代理地址
VITE_APP_URL=http://localhost:8801
# 基础请求路径
VITE_APP_BASE_API=/api
# 是否使用Mock
VITE_MOCK_DEV_SERVER=true
# mock地址
VITE_MOCK_BASE_API=/mock
# 如果端口被占用会直接退出,而不是尝试下一个端口
VITE_STRICT_PORT=false
# 是否启用屏幕转vw适配
VITE_POST_CSS_PX_TO_VIEWPORT8_PLUGIN=true
# 是否启用屏幕转vw适配,可以选择 postcss-px-to-viewport-8-plugin || autofit
VITE_POST_CSS_PX_TO_VIEWPORT8_PLUGIN="autofit"
# 是否在打包时使用cdn替换本地库 替换 true 不替换 false
VITE_CDN=false

View File

@ -1,14 +1,14 @@
export default {
// (x)=>{},单个参数箭头函数是否显示小括号。(always:始终显示;avoid:省略括号。默认:always)
arrowParens: 'always',
arrowParens: "always",
// 开始标签的右尖括号是否跟随在最后一行属性末尾默认false
bracketSameLine: false,
// 对象字面量的括号之间打印空格 (true - Example: { foo: bar } ; false - Example: {foo:bar})
bracketSpacing: true,
// 是否格式化一些文件中被嵌入的代码片段的风格(auto|off;默认auto)
embeddedLanguageFormatting: 'auto',
embeddedLanguageFormatting: "auto",
// 指定 HTML 文件的空格敏感度 (css|strict|ignore;默认css)
htmlWhitespaceSensitivity: 'ignore',
htmlWhitespaceSensitivity: "ignore",
// 当文件已经被 Prettier 格式化之后,是否会在文件顶部插入一个特殊的 @format 标记默认false
insertPragma: false,
// 在 JSX 中使用单引号替代双引号默认false
@ -16,9 +16,9 @@ export default {
// 每行最多字符数量,超出换行(默认100)
printWidth: 100,
// 超出打印宽度 (always | never | preserve )
proseWrap: 'preserve',
proseWrap: "preserve",
// 对象属性是否使用引号(as-needed | consistent | preserve;默认as-needed:对象的属性需要加引号才添加;)
quoteProps: 'as-needed',
quoteProps: "as-needed",
// 是否只格式化在文件顶部包含特定注释(@prettier| @format)的文件默认false
requirePragma: false,
// 结尾添加分号
@ -28,18 +28,18 @@ export default {
// 缩进空格数默认2个空格
tabWidth: 2,
// 元素末尾是否加逗号默认es5: ES5中的 objects, arrays 等会添加逗号TypeScript 中的 type 后不加逗号
trailingComma: 'es5',
trailingComma: "es5",
// 指定缩进方式空格或tab默认false即使用空格
useTabs: false,
// vue 文件中是否缩进 <styles> 和 <script> 标签,默认 false
// vue 文件中是否缩进 <style> 和 <script> 标签,默认 false
vueIndentScriptAndStyle: false,
endOfLine: 'auto',
endOfLine: "auto",
overrides: [
{
files: '*.html',
files: "*.html",
options: {
parser: 'html',
parser: "html",
},
},
],

View File

@ -7,9 +7,6 @@ export const css = (mode): CSSOptions => {
const plugins: Plugin[] = [usePostCssPxToViewport8plugin(mode)];
return {
preprocessorOptions: {
scss: { additionalData: `@use "@/assets/styles/global.scss";` },
},
postcss: {
plugins: plugins.filter(Boolean),
},
@ -20,23 +17,26 @@ export const css = (mode): CSSOptions => {
const usePostCssPxToViewport8plugin = (mode): Plugin => {
const { VITE_POST_CSS_PX_TO_VIEWPORT8_PLUGIN } = wrapperEnv(mode, 'VITE');
return VITE_POST_CSS_PX_TO_VIEWPORT8_PLUGIN
? postCssPxToViewport8plugin({
unitToConvert: 'px',
viewportWidth: 1920, // 设计稿的宽度
unitPrecision: 5, // 单位转换后保留的精度
propList: ['*'], // 能转化为vw的属性列表
viewportUnit: 'vw', // 希望使用的视口单位
fontViewportUnit: 'vw', // 字体使用的视口单位
selectorBlackList: [], // 需要忽略的CSS选择器不会转为视口单位使用原有的px等单位。
minPixelValue: 1, // 设置最小的转换数值如果为1的话只有大于1的值会被转换
mediaQuery: true, // 媒体查询里的单位是否需要转换单位
replace: true, // 是否直接更换属性值,而不添加备用属性
exclude: [/node_modules/], // 忽略某些文件夹下的文件或特定文件,例如 'node_modules' 下的文件
include: [], // 如果设置了include那将只有匹配到的文件才会被转换
landscape: false, // 是否添加根据 landscapeWidth 生成的媒体查询条件 @media (orientation: landscape)
landscapeUnit: 'vw', // 横屏时使用的单位
landscapeWidth: 1024, // 横屏时使用的视口宽度
})
: null;
const cssPxToVw = postCssPxToViewport8plugin({
unitToConvert: 'px',
viewportWidth: 1920, // 设计稿的宽度
unitPrecision: 5, // 单位转换后保留的精度
propList: ['*'], // 能转化为vw的属性列表
viewportUnit: 'vw', // 希望使用的视口单位
fontViewportUnit: 'vw', // 字体使用的视口单位
selectorBlackList: [], // 需要忽略的CSS选择器不会转为视口单位使用原有的px等单位。
minPixelValue: 1, // 设置最小的转换数值如果为1的话只有大于1的值会被转换
mediaQuery: true, // 媒体查询里的单位是否需要转换单位
replace: true, // 是否直接更换属性值,而不添加备用属性
exclude: [/node_modules/], // 忽略某些文件夹下的文件或特定文件,例如 'node_modules' 下的文件
include: [], // 如果设置了include那将只有匹配到的文件才会被转换
landscape: false, // 是否添加根据 landscapeWidth 生成的媒体查询条件 @media (orientation: landscape)
landscapeUnit: 'vw', // 横屏时使用的单位
landscapeWidth: 1024, // 横屏时使用的视口宽度
});
switch (VITE_POST_CSS_PX_TO_VIEWPORT8_PLUGIN) {
case 'postcss-px-to-viewport-8-plugin':
return cssPxToVw;
}
};

View File

@ -4,12 +4,13 @@ import vueJsx from '@vitejs/plugin-vue-jsx';
import { presetIcons, presetUno } from 'unocss';
import UnoCSS from 'unocss/vite';
import type { PluginOption } from 'vite';
import { vitePluginFakeServer } from 'vite-plugin-fake-server';
import removeConsole from 'vite-plugin-remove-console';
import Inspector from 'vite-plugin-vue-inspector';
import { useCDN } from './cdn';
import { viteConsoleLog } from './info';
import { compressPack, report } from './utils';
import { compressPack, report, wrapperEnv } from './utils';
export const plugins = (mode): PluginOption[] => {
return [
@ -40,5 +41,20 @@ export const plugins = (mode): PluginOption[] => {
],
}),
compressPack(mode),
useMock(mode),
];
};
/** MOCK 服务 */
const useMock = (mode) => {
const { VITE_MOCK_DEV_SERVER } = wrapperEnv(mode);
return VITE_MOCK_DEV_SERVER
? vitePluginFakeServer({
logger: true,
include: 'mock',
infixName: false,
enableProd: true, // 线上支持mock
})
: null;
};

View File

@ -5,6 +5,7 @@ import { wrapperEnv } from './utils';
/* 开发服务配置 */
export const server = (mode) => {
const { VITE_PORT, VITE_APP_URL, VITE_STRICT_PORT } = wrapperEnv(mode);
const options: ServerOptions = {
strictPort: VITE_STRICT_PORT,
port: VITE_PORT,

View File

@ -1,10 +1,10 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link href="/vite.svg" rel="icon" type="image/svg+xml" />
<meta content="width=device-width, initial-scale=1.0" name="viewport" />
<title>%VITE_APP_TITLE%</title>
<meta charset="UTF-8"/>
<link href="/vite.svg" rel="icon" type="image/svg+xml"/>
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
<title>%VITE_APP_TITLE%</title>
</head>
<body>
<div id="app"></div>

19
mock/user.ts Normal file
View File

@ -0,0 +1,19 @@
import { defineFakeRoute } from 'vite-plugin-fake-server/client';
export default defineFakeRoute([
{
url: '/mock/users',
methods: 'get',
response: () => ({
code: 200,
data: {
userId: 2,
nickname: '系统管理员',
username: 'admin',
avatar:
'https://foruda.gitee.com/images/1723603502796844527/03cdca2a_716974.gif?imageView2/1/w/80/h/80',
},
message: '操作成功',
}),
},
]);

View File

@ -1,5 +1,5 @@
{
"name": "dashboard-template",
"name": "vehicle-monitor",
"private": true,
"version": "1.0.0",
"type": "module",
@ -19,6 +19,7 @@
"@unocss/reset": "^66.0.0",
"@vitejs/plugin-vue-jsx": "^4.1.1",
"animate.css": "^4.1.1",
"autofit.js": "^3.2.4",
"axios": "^1.7.9",
"boxen": "^8.0.1",
"dayjs": "^1.11.13",
@ -36,6 +37,7 @@
"pinia-plugin-persistedstate": "^3.2.3",
"postcss-px-to-viewport-8-plugin": "^1.2.5",
"prettier": "^3.3.3",
"qs": "^6.14.0",
"rimraf": "^5.0.10",
"rollup-plugin-visualizer": "^5.14.0",
"sass": "^1.77.8",
@ -47,10 +49,10 @@
"terser": "^5.39.0",
"unocss": "^66.0.0",
"vite-plugin-cdn-import": "^1.0.1",
"vite-plugin-fake-server": "^2.2.0",
"vite-plugin-remove-console": "^2.2.0",
"vite-plugin-vue-inspector": "^5.3.1",
"vue": "^3.5.13",
"vue-echarts": "^7.0.3",
"vue-eslint-parser": "^9.4.3",
"vue-router": "^4.4.3",
"vue-types": "^6.0.0"

View File

@ -29,6 +29,9 @@ importers:
animate.css:
specifier: ^4.1.1
version: 4.1.1
autofit.js:
specifier: ^3.2.4
version: 3.2.4
axios:
specifier: ^1.7.9
version: 1.7.9
@ -80,6 +83,9 @@ importers:
prettier:
specifier: ^3.3.3
version: 3.5.2
qs:
specifier: ^6.14.0
version: 6.14.0
rimraf:
specifier: ^5.0.10
version: 5.0.10
@ -113,6 +119,9 @@ importers:
vite-plugin-cdn-import:
specifier: ^1.0.1
version: 1.0.1(rollup@4.34.8)(vite@6.1.1(jiti@2.4.2)(sass@1.85.0)(terser@5.39.0))
vite-plugin-fake-server:
specifier: ^2.2.0
version: 2.2.0
vite-plugin-remove-console:
specifier: ^2.2.0
version: 2.2.0
@ -122,9 +131,6 @@ importers:
vue:
specifier: ^3.5.13
version: 3.5.13(typescript@5.7.3)
vue-echarts:
specifier: ^7.0.3
version: 7.0.3(@vue/runtime-core@3.5.13)(echarts@5.6.0)(vue@3.5.13(typescript@5.7.3))
vue-eslint-parser:
specifier: ^9.4.3
version: 9.4.3(eslint@9.21.0(jiti@2.4.2))
@ -1091,6 +1097,9 @@ packages:
asynckit@0.4.0:
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
autofit.js@3.2.4:
resolution: {integrity: sha512-THNaA9w55nVNzhsajMgQjtg01VZmHG0jq+HtzDOK7SXCtCiuzDu6tzbGPmt0EbuIM+7oSlKUnfRnXKKGYv8/PA==}
axios@1.7.9:
resolution: {integrity: sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==}
@ -1135,6 +1144,9 @@ packages:
buffer@6.0.3:
resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==}
bundle-import@0.0.2:
resolution: {integrity: sha512-XB3T6xlgqJHThyr2luo3pNAVhfN/Y2qFEsblrzUO5QZLpJtesget8jmGDImSairScy80ZKBDVcRdFzTzWv3v8A==}
cac@6.7.14:
resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==}
engines: {node: '>=8'}
@ -1146,6 +1158,10 @@ packages:
resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==}
engines: {node: '>= 0.4'}
call-bound@1.0.3:
resolution: {integrity: sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==}
engines: {node: '>= 0.4'}
callsites@3.1.0:
resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
engines: {node: '>=6'}
@ -1564,6 +1580,9 @@ packages:
resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==}
engines: {node: '>= 0.4'}
get-tsconfig@4.10.0:
resolution: {integrity: sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A==}
glob-parent@5.1.2:
resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
engines: {node: '>= 6'}
@ -1673,6 +1692,12 @@ packages:
resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==}
engines: {node: '>=6'}
import-from-string@0.0.5:
resolution: {integrity: sha512-z59WIHImWhnGVswc0JoyI10Qn4A8xQw7OKrCFRQHvzGZhhEixX13OtXP9ud3Xjpn16CUoYfh5mTu3tnNODiSAw==}
import-meta-resolve@4.1.0:
resolution: {integrity: sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw==}
imurmurhash@0.1.4:
resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
engines: {node: '>=0.8.19'}
@ -1914,6 +1939,10 @@ packages:
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
engines: {node: '>=0.10.0'}
object-inspect@1.13.4:
resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==}
engines: {node: '>= 0.4'}
ofetch@1.4.1:
resolution: {integrity: sha512-QZj2DfGplQAr2oj9KzceK9Hwz6Whxazmn85yYeVuS3u9XTMOGMRx0kO95MQ+vLsj/S/NwBDMMLU5hpxvI6Tklw==}
@ -1962,6 +1991,10 @@ packages:
resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==}
engines: {node: '>=16 || 14 >=14.18'}
path-to-regexp@8.2.0:
resolution: {integrity: sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==}
engines: {node: '>=16'}
path-type@4.0.0:
resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==}
engines: {node: '>=8'}
@ -2074,6 +2107,10 @@ packages:
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
engines: {node: '>=6'}
qs@6.14.0:
resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==}
engines: {node: '>=0.6'}
queue-microtask@1.2.3:
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
@ -2101,6 +2138,9 @@ packages:
resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==}
engines: {node: '>=8'}
resolve-pkg-maps@1.0.0:
resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==}
reusify@1.0.4:
resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==}
engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
@ -2157,6 +2197,22 @@ packages:
resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
engines: {node: '>=8'}
side-channel-list@1.0.0:
resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==}
engines: {node: '>= 0.4'}
side-channel-map@1.0.1:
resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==}
engines: {node: '>= 0.4'}
side-channel-weakmap@1.0.2:
resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==}
engines: {node: '>= 0.4'}
side-channel@1.1.0:
resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==}
engines: {node: '>= 0.4'}
signal-exit@4.1.0:
resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
engines: {node: '>=14'}
@ -2416,6 +2472,9 @@ packages:
peerDependencies:
vite: '>=2.0.0'
vite-plugin-fake-server@2.2.0:
resolution: {integrity: sha512-RP691997Q207nenNMhg7cvYyBXZyjqXwApTBa+a9kHmILgcAU2w4TMRDiAhIU0HPLAAR5MHlWTEpxA9Tbf+v8g==}
vite-plugin-remove-console@2.2.0:
resolution: {integrity: sha512-qgjh5pz75MdE9Kzs8J0kBwaCfifHV0ezRbB9rpGsIOxam+ilcGV7WOk91vFJXquzRmiKrFh3Hxlh0JJWAmXTbQ==}
@ -2467,17 +2526,6 @@ packages:
vscode-uri@3.1.0:
resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==}
vue-demi@0.13.11:
resolution: {integrity: sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==}
engines: {node: '>=12'}
hasBin: true
peerDependencies:
'@vue/composition-api': ^1.0.0-rc.1
vue: ^3.0.0-0 || ^2.6.0
peerDependenciesMeta:
'@vue/composition-api':
optional: true
vue-demi@0.14.10:
resolution: {integrity: sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==}
engines: {node: '>=12'}
@ -2489,16 +2537,6 @@ packages:
'@vue/composition-api':
optional: true
vue-echarts@7.0.3:
resolution: {integrity: sha512-/jSxNwOsw5+dYAUcwSfkLwKPuzTQ0Cepz1LxCOpj2QcHrrmUa/Ql0eQqMmc1rTPQVrh2JQ29n2dhq75ZcHvRDw==}
peerDependencies:
'@vue/runtime-core': ^3.0.0
echarts: ^5.5.1
vue: ^2.7.0 || ^3.1.1
peerDependenciesMeta:
'@vue/runtime-core':
optional: true
vue-eslint-parser@9.4.3:
resolution: {integrity: sha512-2rYRLWlIpaiN8xbPiDyXZXRgLGOtWxERV7ND5fFAv5qo1D2N9Fu9MNajBNc6o13lZ+24DAWCkQCvj4klgmcITg==}
engines: {node: ^14.17.0 || >=16.0.0}
@ -3562,6 +3600,8 @@ snapshots:
asynckit@0.4.0: {}
autofit.js@3.2.4: {}
axios@1.7.9:
dependencies:
follow-redirects: 1.15.9
@ -3618,6 +3658,11 @@ snapshots:
base64-js: 1.5.1
ieee754: 1.2.1
bundle-import@0.0.2:
dependencies:
get-tsconfig: 4.10.0
import-from-string: 0.0.5
cac@6.7.14: {}
cacheable@1.8.8:
@ -3630,6 +3675,11 @@ snapshots:
es-errors: 1.3.0
function-bind: 1.1.2
call-bound@1.0.3:
dependencies:
call-bind-apply-helpers: 1.0.2
get-intrinsic: 1.3.0
callsites@3.1.0: {}
camelcase@8.0.0: {}
@ -4065,6 +4115,10 @@ snapshots:
dunder-proto: 1.0.1
es-object-atoms: 1.1.1
get-tsconfig@4.10.0:
dependencies:
resolve-pkg-maps: 1.0.0
glob-parent@5.1.2:
dependencies:
is-glob: 4.0.3
@ -4166,6 +4220,13 @@ snapshots:
parent-module: 1.0.1
resolve-from: 4.0.0
import-from-string@0.0.5:
dependencies:
esbuild: 0.24.2
import-meta-resolve: 4.1.0
import-meta-resolve@4.1.0: {}
imurmurhash@0.1.4: {}
ini@1.3.8: {}
@ -4357,6 +4418,8 @@ snapshots:
object-assign@4.1.1: {}
object-inspect@1.13.4: {}
ofetch@1.4.1:
dependencies:
destr: 2.0.3
@ -4412,6 +4475,8 @@ snapshots:
lru-cache: 10.4.3
minipass: 7.1.2
path-to-regexp@8.2.0: {}
path-type@4.0.0: {}
pathe@1.1.2: {}
@ -4507,6 +4572,10 @@ snapshots:
punycode@2.3.1: {}
qs@6.14.0:
dependencies:
side-channel: 1.1.0
queue-microtask@1.2.3: {}
readdirp@3.6.0:
@ -4523,6 +4592,8 @@ snapshots:
resolve-from@5.0.0: {}
resolve-pkg-maps@1.0.0: {}
reusify@1.0.4: {}
rimraf@5.0.10:
@ -4593,6 +4664,34 @@ snapshots:
shebang-regex@3.0.0: {}
side-channel-list@1.0.0:
dependencies:
es-errors: 1.3.0
object-inspect: 1.13.4
side-channel-map@1.0.1:
dependencies:
call-bound: 1.0.3
es-errors: 1.3.0
get-intrinsic: 1.3.0
object-inspect: 1.13.4
side-channel-weakmap@1.0.2:
dependencies:
call-bound: 1.0.3
es-errors: 1.3.0
get-intrinsic: 1.3.0
object-inspect: 1.13.4
side-channel-map: 1.0.1
side-channel@1.1.0:
dependencies:
es-errors: 1.3.0
object-inspect: 1.13.4
side-channel-list: 1.0.0
side-channel-map: 1.0.1
side-channel-weakmap: 1.0.2
signal-exit@4.1.0: {}
sirv@3.0.1:
@ -4910,6 +5009,14 @@ snapshots:
magic-string: 0.25.9
vite: 6.1.1(jiti@2.4.2)(sass@1.85.0)(terser@5.39.0)
vite-plugin-fake-server@2.2.0:
dependencies:
bundle-import: 0.0.2
chokidar: 4.0.3
path-to-regexp: 8.2.0
picocolors: 1.1.1
tinyglobby: 0.2.12
vite-plugin-remove-console@2.2.0: {}
vite-plugin-vue-inspector@5.3.1(vite@6.1.1(jiti@2.4.2)(sass@1.85.0)(terser@5.39.0)):
@ -4940,24 +5047,10 @@ snapshots:
vscode-uri@3.1.0: {}
vue-demi@0.13.11(vue@3.5.13(typescript@5.7.3)):
dependencies:
vue: 3.5.13(typescript@5.7.3)
vue-demi@0.14.10(vue@3.5.13(typescript@5.7.3)):
dependencies:
vue: 3.5.13(typescript@5.7.3)
vue-echarts@7.0.3(@vue/runtime-core@3.5.13)(echarts@5.6.0)(vue@3.5.13(typescript@5.7.3)):
dependencies:
echarts: 5.6.0
vue: 3.5.13(typescript@5.7.3)
vue-demi: 0.13.11(vue@3.5.13(typescript@5.7.3))
optionalDependencies:
'@vue/runtime-core': 3.5.13
transitivePeerDependencies:
- '@vue/composition-api'
vue-eslint-parser@9.4.3(eslint@9.21.0(jiti@2.4.2)):
dependencies:
debug: 4.4.0

View File

@ -5,6 +5,6 @@
<style>
#app {
width: 100%;
height: 1080px;
height: 100%;
}
</style>

View File

@ -2,8 +2,6 @@ import axios, { type AxiosResponse, type InternalAxiosRequestConfig } from 'axio
import qs from 'qs';
import { TOKEN_KEY } from '@/enums/CacheEnum';
import { ResultEnum } from '@/enums/ResultEnum';
import { useUserStoreHook } from '@/store/modules/user';
// 创建 axios 实例
const service = axios.create({
@ -36,33 +34,31 @@ service.interceptors.response.use(
if (response.config.responseType === 'blob' || response.config.responseType === 'arraybuffer') {
return response;
}
// const { code, data, msg } = response.data;
// if (code === ResultEnum.SUCCESS) {
// return data;
// }
const { code, data, msg } = response.data;
if (code === ResultEnum.SUCCESS) {
return data;
if (response.status === 200) {
return response.data;
}
ElMessage.error(msg || '系统出错');
return Promise.reject(new Error(msg || 'Error'));
// ElMessage.error(msg || '系统出错');
return Promise.reject(msg || 'Error');
},
(error: any) => {
// 异常处理
if (error.response.data) {
const { code, msg } = error.response.data;
if (code === ResultEnum.TOKEN_INVALID) {
ElNotification({
title: '提示',
message: '您的会话已过期,请重新登录',
type: 'info',
});
useUserStoreHook()
.resetToken()
.then(() => {
location.reload();
});
} else {
ElMessage.error(msg || '系统出错');
}
// const { code, msg } = error.response.data;
// if (code === ResultEnum.TOKEN_INVALID) {
// ElNotification({
// title: '提示',
// message: '您的会话已过期,请重新登录',
// type: 'info',
// });
// } else {
// ElMessage.error(msg || '系统出错');
// }
}
return Promise.reject(error.message);
}

View File

@ -0,0 +1,68 @@
import axios, { type AxiosResponse, type InternalAxiosRequestConfig } from 'axios';
import qs from 'qs';
import { TOKEN_KEY } from '@/enums/CacheEnum';
// 创建 axios 实例
const service = axios.create({
baseURL: import.meta.env.VITE_MOCK_BASE_API,
timeout: 50000,
headers: { 'Content-Type': 'application/json;charset=utf-8' },
paramsSerializer: (params) => {
return qs.stringify(params);
},
});
// 请求拦截器
service.interceptors.request.use(
(config: InternalAxiosRequestConfig) => {
const accessToken = localStorage.getItem(TOKEN_KEY);
if (accessToken) {
config.headers.Authorization = accessToken;
}
return config;
},
(error: any) => {
return Promise.reject(error);
}
);
// 响应拦截器
service.interceptors.response.use(
(response: AxiosResponse) => {
// 检查配置的响应类型是否为二进制类型('blob' 或 'arraybuffer', 如果是,直接返回响应对象
if (response.config.responseType === 'blob' || response.config.responseType === 'arraybuffer') {
return response;
}
// const { code, data, msg } = response.data;
// if (code === ResultEnum.SUCCESS) {
// return data;
// }
if (response.status === 200) {
return response.data;
}
// ElMessage.error(msg || '系统出错');
return Promise.reject(msg || 'Error');
},
(error: any) => {
// 异常处理
if (error.response.data) {
// const { code, msg } = error.response.data;
// if (code === ResultEnum.TOKEN_INVALID) {
// ElNotification({
// title: '提示',
// message: '您的会话已过期,请重新登录',
// type: 'info',
// });
// } else {
// ElMessage.error(msg || '系统出错');
// }
}
return Promise.reject(error.message);
}
);
// 导出 axios 实例
export default service;

6
src/api/test.ts Normal file
View File

@ -0,0 +1,6 @@
import request from '@/api/server/requestMock';
import type { BaseResult } from '@/types/request';
export const user = () => {
return request<any, BaseResult<any>>({ url: '/users', method: 'get' });
};

4
src/enums/CacheEnum.ts Normal file
View File

@ -0,0 +1,4 @@
/**
* Key
*/
export const TOKEN_KEY = 'accessToken';

9
src/enums/ResultEnum.ts Normal file
View File

@ -0,0 +1,9 @@
/** 响应码枚举 */
export const enum ResultEnum {
// 成功
SUCCESS = '200',
// 错误
ERROR = 'B0001',
// 令牌无效或过期
TOKEN_INVALID = '209',
}

View File

@ -1,39 +1,5 @@
<script lang="ts" setup>
import AppMain from '@/layout/components/AppMain/index.vue';
import Footer from '@/layout/components/Footer/index.vue';
import NavBar from '@/layout/components/NavBar/index.vue';
</script>
<script lang="ts" setup></script>
<template>
<div class="layout">
<div class="arrow left-[38px]">
<img alt="左箭头" src="@/assets/images/arrow/arrow-left.png" />
</div>
<NavBar />
<AppMain />
<Footer />
<div class="arrow right-[38px]">
<img alt="左箭头" src="@/assets/images/arrow/arrow-right.png" />
</div>
</div>
</template>
<template>111</template>
<style scoped>
.layout {
position: relative;
width: 100%;
height: 100%;
background: url('@/assets/images/bg/bg.png') no-repeat center;
background-size: cover;
}
.arrow {
position: absolute;
top: 50%;
transform: translateY(-50%);
img {
width: 65px;
}
}
</style>
<style lang="scss" scoped></style>

18
src/plugins/autofit.ts Normal file
View File

@ -0,0 +1,18 @@
import autofit from 'autofit.js';
/** 使用autoFit 做大屏*/
export const autoFit = () => {
const hasAutoFit = import.meta.env.VITE_POST_CSS_PX_TO_VIEWPORT8_PLUGIN;
if (hasAutoFit !== 'autofit') return null;
autofit.init({
dh: 1080,
dw: 1920,
el: 'body',
resize: true,
transition: 0.49,
limit: 0.1,
allowScroll: false,
});
};

View File

@ -1,6 +1,7 @@
import type { App } from 'vue';
import { setupDirective } from '@/directive';
import { autoFit } from '@/plugins/autofit';
import { useEcharts } from '@/plugins/echarts';
import { setUpRouter } from '@/router';
import { setupStore } from '@/store';
@ -13,7 +14,8 @@ export default {
setupStore(app);
// 设置指令
setupDirective(app);
// 按需引入echarts
// 根据需求引入echarts
useEcharts(app);
autoFit();
},
};

View File

@ -1,48 +1,12 @@
import type { App } from 'vue';
import { createRouter, createWebHashHistory, type RouteRecordRaw } from 'vue-router';
export const Layout = () => import('@/layout/index.vue');
import error from '@/router/modules/error';
import home from '@/router/modules/home';
import remaining from '@/router/modules/remaining';
// 静态路由
const routes: RouteRecordRaw[] = [
{
path: '/redirect',
component: Layout,
meta: { hidden: true },
children: [
{
path: '/redirect/:path(.*)',
component: () => import('@/views/redirect/index.vue'),
},
],
},
{
path: '/',
name: '/',
component: Layout,
// redirect: '/dashboard',
children: [
// {
// path: 'dashboard',
// component: () => import('@/views/index.vue'),
// // 用于 keep-alive 功能,需要与 SFC 中自动推导或显式声明的组件名称一致
// // 参考文档: https://cn.vuejs.org/guide/built-ins/keep-alive.html#include-exclude
// name: 'Dashboard',
// meta: {
// title: 'dashboard',
// icon: 'homepage',
// affix: true,
// keepAlive: true,
// },
// },
],
},
{
path: '/error',
component: () => import('@/views/error-page/404.vue'),
meta: { hidden: true },
},
];
const routes: RouteRecordRaw[] = [...remaining, ...home, ...error];
const router = createRouter({
history: createWebHashHistory(),
routes,

View File

@ -0,0 +1,11 @@
import type { RouteRecordRaw } from 'vue-router';
const routes: RouteRecordRaw[] = [
{
path: '/error',
component: () => import('@/views/error-page/404.vue'),
meta: { hidden: true },
},
];
export default routes;

View File

@ -0,0 +1,34 @@
import type { RouteRecordRaw } from 'vue-router';
import Layout from '@/layout/index.vue';
const routes: RouteRecordRaw[] = [
{
path: '/',
name: '/',
component: Layout,
// redirect: '/dashboard',
children: [
// {
// path: 'dashboard',
// component: () => import('@/views/index.vue'),
// // 用于 keep-alive 功能,需要与 SFC 中自动推导或显式声明的组件名称一致
// // 参考文档: https://cn.vuejs.org/guide/built-ins/keep-alive.html#include-exclude
// name: 'Dashboard',
// meta: {
// title: 'dashboard',
// icon: 'homepage',
// affix: true,
// keepAlive: true,
// },
// },
],
},
{
path: '/smart-parking',
name: 'smartParking',
component: () => import('@/views/smart-parking/index.vue'),
},
];
export default routes;

View File

@ -0,0 +1,19 @@
import type { RouteRecordRaw } from 'vue-router';
import Layout from '@/layout/index.vue';
const routes: RouteRecordRaw[] = [
{
path: '/redirect',
component: Layout,
meta: { hidden: true },
children: [
{
path: '/redirect/:path(.*)',
component: () => import('@/views/redirect/index.vue'),
},
],
},
];
export default routes;

21
src/store/modules/user.ts Normal file
View File

@ -0,0 +1,21 @@
import { defineStore } from 'pinia';
import { user } from '@/api/test';
/** 用户信息 */
export const useUserStore = defineStore('userStore', {
state() {
return {
userinfo: {},
};
},
getters: {},
actions: {
async getUserInfo() {
const response = await user();
if (response.code == 200) {
this.userinfo = response.data;
}
},
},
});

View File

@ -5,8 +5,11 @@ declare global {
VITE_PORT: number;
VITE_PUBLIC_PATH: string;
VITE_APP_URL: string;
VITE_APP_BASE_API: string;
VITE_STRICT_PORT: boolean;
VITE_POST_CSS_PX_TO_VIEWPORT8_PLUGIN: boolean;
VITE_MOCK_DEV_SERVER: boolean;
VITE_MOCK_BASE_API: string;
VITE_CDN: boolean;
VITE_COMPRESSION: string;
}

6
src/types/request/index.d.ts vendored Normal file
View File

@ -0,0 +1,6 @@
// 基础后端返回内容
export interface BaseResult<T> {
code: number;
data: T;
message: string;
}

View File

@ -4,7 +4,7 @@
<footer>
<ul class="flex-x-around">
<li v-for="index in new Array(5)" :key="index" class="rectangle flex-y-center">
<img alt="车辆管理" src="@/assets/images/layout/footer-39.png" />
<img alt="车辆管理" src="../../../../assets/images/layout/footer-39.png" />
<span class="text-white">车辆管理</span>
</li>
</ul>

View File

@ -1,16 +1,14 @@
<script lang="ts" setup>
import MainCenter from '@/layout/components/AppMain/MainCenter.vue';
import MainLeft from '@/layout/components/AppMain/MainLeft.vue';
import MainRight from '@/layout/components/AppMain/MainRight.vue';
import MainCenter from '@/views/smart-parking/components/main/main-center.vue';
import RoadCondition from '@/views/smart-parking/components/main/road-condition.vue';
import TrafficOverview from '@/views/smart-parking/components/main/traffic-overview.vue';
</script>
<template>
<main class="mt-[78px] mx-auto flex-center w-[1620px] h-[650px]">
<MainLeft />
<MainCenter />
<MainRight />
<road-condition />
<main-center />
<traffic-overview />
</main>
</template>

View File

@ -11,22 +11,22 @@
<!-- 汽车列表 -->
<ul class="mt-[32px]">
<li class="car-item flex-x-around">
<img alt="car-1" src="@/assets/images/car/car-1.png" />
<img alt="car-1" src="../../../../assets/images/car/car-1.png" />
<p class="c-white">入卡口西北门</p>
<span class="dashed-circle c-primary-secondary border-b-primary-secondary">畅通</span>
</li>
<li class="car-item flex-x-around">
<img alt="car-1" src="@/assets/images/car/car-1.png" />
<img alt="car-1" src="../../../../assets/images/car/car-1.png" />
<p class="c-white">入卡口东北门</p>
<span class="dashed-circle c-primary-secondary border-b-primary-secondary">畅通</span>
</li>
<li class="car-item flex-x-around">
<img alt="car-1" src="@/assets/images/car/car-2.png" />
<img alt="car-1" src="../../../../assets/images/car/car-2.png" />
<p class="c-white">入卡口东北门</p>
<span class="dashed-circle c-warning border-b-warning">拥堵</span>
</li>
<li class="car-item flex-x-around">
<img alt="car-1" src="@/assets/images/car/car-1.png" />
<img alt="car-1" src="../../../../assets/images/car/car-1.png" />
<p class="c-white">入卡口东南门</p>
<span class="dashed-circle c-primary-secondary border-b-primary-secondary">畅通</span>
</li>

View File

@ -1,7 +1,7 @@
<script lang="ts" setup>
import { onMounted, ref } from 'vue';
import { renderEcharts } from '@/layout/components/AppMain/data';
import { renderEcharts } from '@/views/smart-parking/components/main/data';
const weekDataChart = ref<HTMLDivElement>();

View File

@ -11,9 +11,9 @@
<div class="bar-op">
<ul class="flex-x-around">
<li><img alt="icon-1" src="@/assets/images/layout/icon-1.png" /></li>
<li><img alt="icon-2" src="@/assets/images/layout/icon-2.png" /></li>
<li><img alt="icon-3" src="@/assets/images/layout/icon-3.png" /></li>
<li><img alt="icon-1" src="../../../../assets/images/layout/icon-1.png" /></li>
<li><img alt="icon-2" src="../../../../assets/images/layout/icon-2.png" /></li>
<li><img alt="icon-3" src="../../../../assets/images/layout/icon-3.png" /></li>
</ul>
<span class="c-primary">王菠萝</span>
</div>

View File

@ -0,0 +1,39 @@
<script lang="ts" setup>
import Footer from '@/views/smart-parking/components/footer/index.vue';
import AppMain from '@/views/smart-parking/components/main/index.vue';
import NavBar from '@/views/smart-parking/components/nav-bar/index.vue';
</script>
<template>
<div class="layout">
<div class="arrow left-[38px]">
<img alt="左箭头" src="../../assets/images/arrow/arrow-left.png" />
</div>
<NavBar />
<AppMain />
<Footer />
<div class="arrow right-[38px]">
<img alt="左箭头" src="../../assets/images/arrow/arrow-right.png" />
</div>
</div>
</template>
<style scoped>
.layout {
position: relative;
width: 100%;
height: 100%;
background: url('@/assets/images/bg/bg.png') no-repeat center;
background-size: cover;
}
.arrow {
position: absolute;
top: 50%;
transform: translateY(-50%);
img {
width: 65px;
}
}
</style>

View File

@ -17,6 +17,7 @@ export default defineConfig({
'flex-y-center': 'flex flex-col flex-wrap justify-center items-center',
'flex-y-between': 'flex flex-col flex-wrap justify-between items-center',
'flex-y-around': 'flex flex-col flex-wrap justify-around items-center',
'wh-full': 'w-full h-full',
},
theme: {
colors: {