first commit
This commit is contained in:
commit
d5a1b4ed47
3
.env
Normal file
3
.env
Normal file
@ -0,0 +1,3 @@
|
||||
ENV_APP_TITLE=运输控制系统
|
||||
ENV_HTTP_BASE=/api
|
||||
ENV_WEBSOCKET_BASE=/ws
|
1
.env.development
Normal file
1
.env.development
Normal file
@ -0,0 +1 @@
|
||||
ENV_APP_TITLE=运输控制系统(开发)
|
0
.env.production
Normal file
0
.env.production
Normal file
25
.gitignore
vendored
Normal file
25
.gitignore
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
components.d.ts
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
13
.prettierrc.js
Normal file
13
.prettierrc.js
Normal file
@ -0,0 +1,13 @@
|
||||
export default {
|
||||
arrowParens: 'always',
|
||||
bracketSpacing: true,
|
||||
singleAttributePerLine: false,
|
||||
bracketSameLine: false,
|
||||
jsxSingleQuote: true,
|
||||
printWidth: 120,
|
||||
semi: true,
|
||||
singleQuote: true,
|
||||
tabWidth: 2,
|
||||
useTabs: false,
|
||||
endOfLine: 'auto',
|
||||
};
|
13
.stylelintrc.json
Normal file
13
.stylelintrc.json
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"plugins": ["stylelint-order"],
|
||||
"extends": [
|
||||
"stylelint-config-standard",
|
||||
"stylelint-config-standard-scss",
|
||||
"stylelint-config-recommended-vue",
|
||||
"stylelint-config-recess-order"
|
||||
],
|
||||
"rules": {
|
||||
"font-family-name-quotes": "always-where-required",
|
||||
"function-url-quotes": "never"
|
||||
}
|
||||
}
|
3
.vscode/extensions.json
vendored
Normal file
3
.vscode/extensions.json
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"recommendations": ["Vue.volar"]
|
||||
}
|
5
README.md
Normal file
5
README.md
Normal file
@ -0,0 +1,5 @@
|
||||
# Vue 3 + TypeScript + Vite
|
||||
|
||||
This template should help get you started developing with Vue 3 and TypeScript in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
|
||||
|
||||
Learn more about the recommended Project Setup and IDE Support in the [Vue Docs TypeScript Guide](https://vuejs.org/guide/typescript/overview.html#project-setup).
|
50
eslint.config.js
Normal file
50
eslint.config.js
Normal file
@ -0,0 +1,50 @@
|
||||
import pluginJs from '@eslint/js';
|
||||
import configPrettier from 'eslint-config-prettier';
|
||||
import pluginSimpleImportSort from 'eslint-plugin-simple-import-sort';
|
||||
import pluginVue from 'eslint-plugin-vue';
|
||||
import globals from 'globals';
|
||||
import tseslint from 'typescript-eslint';
|
||||
|
||||
/** @type {import('eslint').Linter.Config[]} */
|
||||
export default [
|
||||
{ files: ['**/*.{js,mjs,cjs,ts,vue}'] },
|
||||
{
|
||||
languageOptions: {
|
||||
globals: {
|
||||
...globals.browser,
|
||||
NodeJS: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
pluginJs.configs.recommended,
|
||||
...tseslint.configs.recommended,
|
||||
...pluginVue.configs['flat/essential'],
|
||||
configPrettier,
|
||||
{
|
||||
files: ['**/*.vue'],
|
||||
languageOptions: { parserOptions: { parser: tseslint.parser } },
|
||||
rules: {
|
||||
'vue/multi-word-component-names': ['warn', { ignores: ['index'] }],
|
||||
},
|
||||
},
|
||||
{
|
||||
plugins: { 'simple-import-sort': pluginSimpleImportSort },
|
||||
rules: {
|
||||
'simple-import-sort/imports': 'warn',
|
||||
'simple-import-sort/exports': 'warn',
|
||||
},
|
||||
},
|
||||
{
|
||||
rules: {
|
||||
indent: [
|
||||
'error',
|
||||
2,
|
||||
{ SwitchCase: 1, CallExpression: { arguments: 'off' }, ignoredNodes: ['ConditionalExpression *'] },
|
||||
],
|
||||
quotes: ['error', 'single'],
|
||||
semi: ['error', 'always'],
|
||||
'max-len': ['warn', { code: 120, ignoreComments: true, ignoreUrls: true, ignoreTemplateLiterals: true }],
|
||||
'max-lines': ['warn', { max: 1000, skipBlankLines: true, skipComments: true }],
|
||||
},
|
||||
},
|
||||
];
|
13
index.html
Normal file
13
index.html
Normal file
@ -0,0 +1,13 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>%ENV_APP_TITLE%</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
6436
package-lock.json
generated
Normal file
6436
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
45
package.json
Normal file
45
package.json
Normal file
@ -0,0 +1,45 @@
|
||||
{
|
||||
"name": "arm_system",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"start": "vite",
|
||||
"build": "vue-tsc -b && vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@meta2d/core": "^1.0.78",
|
||||
"ant-design-vue": "^4.2.6",
|
||||
"axios": "^1.8.4",
|
||||
"lodash-es": "^4.17.21",
|
||||
"vue": "^3.5.13",
|
||||
"vue-i18n": "^11.1.3",
|
||||
"vue-router": "^4.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.25.0",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"@types/node": "^22.14.1",
|
||||
"@vitejs/plugin-vue": "^5.2.2",
|
||||
"@vue/tsconfig": "^0.7.0",
|
||||
"eslint": "^9.25.0",
|
||||
"eslint-config-prettier": "^10.1.2",
|
||||
"eslint-plugin-simple-import-sort": "^12.1.1",
|
||||
"eslint-plugin-vue": "^10.0.0",
|
||||
"globals": "^16.0.0",
|
||||
"prettier": "^3.5.3",
|
||||
"sass-embedded": "^1.86.3",
|
||||
"stylelint": "^16.18.0",
|
||||
"stylelint-config-recess-order": "^6.0.0",
|
||||
"stylelint-config-recommended-vue": "^1.6.0",
|
||||
"stylelint-config-standard": "^38.0.0",
|
||||
"stylelint-config-standard-scss": "^14.0.0",
|
||||
"stylelint-order": "^7.0.0",
|
||||
"typescript": "~5.7.2",
|
||||
"typescript-eslint": "^8.30.1",
|
||||
"unplugin-vue-components": "^28.5.0",
|
||||
"vite": "^6.3.1",
|
||||
"vue-tsc": "^2.2.8"
|
||||
}
|
||||
}
|
BIN
public/favicon.ico
Normal file
BIN
public/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 362 B |
BIN
public/point/1.png
Normal file
BIN
public/point/1.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 902 B |
12
src/App.vue
Normal file
12
src/App.vue
Normal file
@ -0,0 +1,12 @@
|
||||
<script setup lang="ts">
|
||||
import sLocale from '@core/locale.service';
|
||||
import sTheme from '@core/theme.service';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<a-config-provider :locale="sLocale.ant" :theme="sTheme.ant" :autoInsertSpaceInButton="false">
|
||||
<a-app>
|
||||
<router-view />
|
||||
</a-app>
|
||||
</a-config-provider>
|
||||
</template>
|
4
src/ant.scss
Normal file
4
src/ant.scss
Normal file
@ -0,0 +1,4 @@
|
||||
.ant-app {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
0
src/apis/map/api.ts
Normal file
0
src/apis/map/api.ts
Normal file
63
src/apis/map/constant.ts
Normal file
63
src/apis/map/constant.ts
Normal file
@ -0,0 +1,63 @@
|
||||
import { KeydownType, type Options } from '@meta2d/core';
|
||||
|
||||
//#region 点位
|
||||
export enum MapPointType {
|
||||
普通点 = 1,
|
||||
等待点,
|
||||
避让点,
|
||||
临时避让点,
|
||||
|
||||
电梯点 = 11,
|
||||
自动门点,
|
||||
充电点,
|
||||
停靠点,
|
||||
动作点,
|
||||
|
||||
障碍点 = 99,
|
||||
}
|
||||
export const MAP_POINT_TYPES = Object.freeze(
|
||||
<[string, MapPointType][]>Object.entries(MapPointType).filter(([, v]) => typeof v === 'number'),
|
||||
);
|
||||
//#endregion
|
||||
|
||||
//#region 线路
|
||||
export enum MapRouteType {
|
||||
直线 = 'line',
|
||||
三阶贝塞尔曲线 = 'bezier3',
|
||||
}
|
||||
export const MAP_ROUTE_TYPES = Object.freeze(<[string, MapRouteType][]>Object.entries(MapRouteType));
|
||||
//#endregion
|
||||
|
||||
//#region 区域
|
||||
export enum MapAreaType {
|
||||
库区 = 1,
|
||||
|
||||
互斥区 = 11,
|
||||
非互斥区,
|
||||
}
|
||||
export const MAP_AREA_TYPES = Object.freeze(
|
||||
<[string, MapAreaType][]>Object.entries(MapAreaType).filter(([, v]) => typeof v === 'number'),
|
||||
);
|
||||
//#endregion
|
||||
|
||||
export const EDITOR_CONFIG: Options = {
|
||||
keydown: KeydownType.None,
|
||||
strictScope: true,
|
||||
moveConnectedLine: false,
|
||||
textRotate: false,
|
||||
textFlip: false,
|
||||
disableInput: true,
|
||||
disableRotate: true,
|
||||
disableSize: true,
|
||||
disableAnchor: true,
|
||||
disableEmptyLine: true,
|
||||
disableRepeatLine: true,
|
||||
minScale: 0.19,
|
||||
maxScale: 2.01,
|
||||
scaleOff: 0.01,
|
||||
defaultAnchors: [],
|
||||
fontSize: 14,
|
||||
lineHeight: 1.5,
|
||||
textAlign: 'center',
|
||||
textBaseline: 'top',
|
||||
};
|
3
src/apis/map/index.ts
Normal file
3
src/apis/map/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export * from './api';
|
||||
export * from './constant';
|
||||
export type * from './type';
|
37
src/apis/map/type.ts
Normal file
37
src/apis/map/type.ts
Normal file
@ -0,0 +1,37 @@
|
||||
import type { RobotInfo } from '@api/robot';
|
||||
import type { Pen } from '@meta2d/core';
|
||||
|
||||
import type { MapAreaType, MapPointType, MapRouteType } from './constant';
|
||||
|
||||
export interface MapPen extends Pen {
|
||||
desc?: string; // 描述
|
||||
|
||||
point?: MapPointInfo; // 点位信息
|
||||
route?: MapRouteInfo; // 线路信息
|
||||
area?: MapAreaInfo; // 区域信息
|
||||
|
||||
attrs?: Record<string, unknown>; // 额外属性
|
||||
activeAttrs?: string[]; // 已激活的额外属性
|
||||
}
|
||||
|
||||
//#region 点位
|
||||
export interface MapPointInfo {
|
||||
type: MapPointType; // 点位类型
|
||||
robots?: RobotInfo['id'][]; // 绑定机器人id集合
|
||||
}
|
||||
//#endregion
|
||||
|
||||
//#region 线路
|
||||
export interface MapRouteInfo {
|
||||
type: MapRouteType; // 线路类型
|
||||
direction?: -1 | 1; // 线路方向
|
||||
}
|
||||
//#endregion
|
||||
|
||||
//#region 区域
|
||||
export interface MapAreaInfo {
|
||||
type: MapAreaType; // 区域类型
|
||||
points?: string[]; // 绑定点位id集合
|
||||
routes?: string[]; // 绑定线路id集合
|
||||
}
|
||||
//#endregion
|
0
src/apis/robot/api.ts
Normal file
0
src/apis/robot/api.ts
Normal file
3
src/apis/robot/constant.ts
Normal file
3
src/apis/robot/constant.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export enum RobotType {
|
||||
叉车 = 1,
|
||||
}
|
3
src/apis/robot/index.ts
Normal file
3
src/apis/robot/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export * from './api';
|
||||
export * from './constant';
|
||||
export type * from './type';
|
8
src/apis/robot/type.ts
Normal file
8
src/apis/robot/type.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import type { RobotType } from './constant';
|
||||
|
||||
export interface RobotInfo {
|
||||
id: number; // 机器人id
|
||||
name: string; // 机器人名称
|
||||
brand?: string; // 机器人品牌
|
||||
type: RobotType; // 机器人类型
|
||||
}
|
0
src/apis/scene/api.ts
Normal file
0
src/apis/scene/api.ts
Normal file
0
src/apis/scene/constant.ts
Normal file
0
src/apis/scene/constant.ts
Normal file
3
src/apis/scene/index.ts
Normal file
3
src/apis/scene/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export * from './api';
|
||||
export * from './constant';
|
||||
export type * from './type';
|
0
src/apis/scene/type.ts
Normal file
0
src/apis/scene/type.ts
Normal file
3
src/assets/locales/en-US.json
Normal file
3
src/assets/locales/en-US.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"查询": "123"
|
||||
}
|
0
src/assets/themes/_dark.scss
Normal file
0
src/assets/themes/_dark.scss
Normal file
0
src/assets/themes/_light.scss
Normal file
0
src/assets/themes/_light.scss
Normal file
12
src/assets/themes/editor.json
Normal file
12
src/assets/themes/editor.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"light": {
|
||||
"background": "",
|
||||
"color": "#000000"
|
||||
},
|
||||
"dark": {
|
||||
"background": "",
|
||||
"color": "#8C8C8C",
|
||||
"activeColor": "#FCC947",
|
||||
"textColor": "#BFBFBF"
|
||||
}
|
||||
}
|
9
src/main.ts
Normal file
9
src/main.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import './style.scss';
|
||||
|
||||
import { i18n } from '@core/locale.service';
|
||||
import { router } from '@core/router';
|
||||
import { createApp } from 'vue';
|
||||
|
||||
import App from './App.vue';
|
||||
|
||||
createApp(App).use(router).use(i18n).mount('#app');
|
29
src/pages/home.vue
Normal file
29
src/pages/home.vue
Normal file
@ -0,0 +1,29 @@
|
||||
<script setup lang="ts">
|
||||
import { EditorService } from '@core/editor.service';
|
||||
import { onMounted, shallowRef } from 'vue';
|
||||
|
||||
const elContainer = shallowRef<HTMLDivElement>();
|
||||
const editor = shallowRef<EditorService>();
|
||||
onMounted(() => {
|
||||
editor.value = new EditorService(elContainer.value!);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div ref="elContainer" class="full"></div>
|
||||
|
||||
<div class="test">
|
||||
<a-button @click="editor?.addPoint(50, 50)">点</a-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.test {
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
left: 20px;
|
||||
z-index: 2333;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
</style>
|
90
src/services/editor.service.ts
Normal file
90
src/services/editor.service.ts
Normal file
@ -0,0 +1,90 @@
|
||||
import { EDITOR_CONFIG, type MapPen, MapPointType } from '@api/map';
|
||||
import sTheme from '@core/theme.service';
|
||||
import { LockState, Meta2d } from '@meta2d/core';
|
||||
import THEME from 'asset/themes/editor.json';
|
||||
import { watch } from 'vue';
|
||||
|
||||
export class EditorService {
|
||||
readonly #editor: Meta2d;
|
||||
|
||||
public destroy() {
|
||||
this.#editor.destroy();
|
||||
}
|
||||
|
||||
public resize(): void {
|
||||
this.#editor.resize();
|
||||
this.#editor.render();
|
||||
}
|
||||
|
||||
public open(map?: string, readonly = false): void {
|
||||
const data = map ? JSON.parse(map) : undefined;
|
||||
this.#editor.open(data);
|
||||
this.#editor.lock(readonly ? LockState.Disable : LockState.None);
|
||||
}
|
||||
public save(): string {
|
||||
const data = this.#editor.data();
|
||||
const map = JSON.stringify(data);
|
||||
return map;
|
||||
}
|
||||
public export(): string {
|
||||
const png = this.#editor.toPng(10);
|
||||
return png;
|
||||
}
|
||||
|
||||
//#region 点位
|
||||
public async addPoint(x: number = 0, y: number = 0, type = MapPointType.普通点): Promise<void> {
|
||||
const pen: MapPen = {
|
||||
name: 'point',
|
||||
x,
|
||||
y,
|
||||
width: 24,
|
||||
height: 24,
|
||||
point: { type },
|
||||
};
|
||||
await this.#editor.addPen(pen);
|
||||
}
|
||||
//#endregion
|
||||
|
||||
//#region 线路
|
||||
//#endregion
|
||||
|
||||
//#region 区域
|
||||
//#endregion
|
||||
|
||||
constructor(container: HTMLDivElement) {
|
||||
this.#editor = new Meta2d(container, EDITOR_CONFIG);
|
||||
|
||||
(<HTMLDivElement>container.children.item(5)).ondrop = null;
|
||||
this.#editor.on('*', (e, v) => this.#listen(e, v));
|
||||
this.#register();
|
||||
|
||||
watch(
|
||||
() => sTheme.theme,
|
||||
(v) => this.#editor.setTheme(v),
|
||||
{ immediate: true },
|
||||
);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
#listen(e: unknown, v: any) {
|
||||
switch (e) {
|
||||
default:
|
||||
// console.log(e, v);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#register() {
|
||||
this.#editor.store.theme = THEME;
|
||||
this.#editor.registerCanvasDraw({ point: drawPoint });
|
||||
}
|
||||
}
|
||||
|
||||
//#region 绘制函数
|
||||
function drawPoint(ctx: CanvasRenderingContext2D, pen: MapPen): void {
|
||||
const { x = 0, y = 0, width = 0, height = 0 } = pen.calculative?.worldRect ?? {};
|
||||
const { type } = pen.point ?? {};
|
||||
ctx.rect(x, y, width, height);
|
||||
ctx.stroke();
|
||||
}
|
||||
//#endregion
|
40
src/services/http.ts
Normal file
40
src/services/http.ts
Normal file
@ -0,0 +1,40 @@
|
||||
import { message } from 'ant-design-vue';
|
||||
import axios, { type AxiosInstance, type AxiosRequestConfig } from 'axios';
|
||||
|
||||
// 创建HTTP实例
|
||||
const http: HttpInstance = axios.create({
|
||||
baseURL: import.meta.env.ENV_HTTP_BASE,
|
||||
adapter: 'fetch',
|
||||
timeout: 30_000,
|
||||
});
|
||||
export default http;
|
||||
|
||||
// 添加请求拦截器
|
||||
http.interceptors.request.use(
|
||||
(config) => config,
|
||||
(error) => Promise.reject(error.message),
|
||||
);
|
||||
|
||||
// 添加响应拦截器
|
||||
http.interceptors.response.use(
|
||||
(response) => {
|
||||
const res = <CommonRes>response.data;
|
||||
if (res?.success) return <never>res.data;
|
||||
const hint = res?.message ?? '未知异常';
|
||||
message.error(hint);
|
||||
return Promise.reject(hint);
|
||||
},
|
||||
(error) => Promise.reject(error.message),
|
||||
);
|
||||
|
||||
type HttpInstance = Omit<AxiosInstance, 'get' | 'post'> & {
|
||||
get: <R = void>(url: string, config?: AxiosRequestConfig) => Promise<R | undefined>;
|
||||
post: <R = void, D = unknown>(url: string, data?: D, config?: AxiosRequestConfig<D>) => Promise<R | undefined>;
|
||||
};
|
||||
|
||||
type CommonRes<T = void> = {
|
||||
code: number;
|
||||
success: boolean;
|
||||
data: T | undefined;
|
||||
message: string;
|
||||
};
|
73
src/services/locale.service.ts
Normal file
73
src/services/locale.service.ts
Normal file
@ -0,0 +1,73 @@
|
||||
import 'dayjs/locale/zh-cn';
|
||||
import 'dayjs/locale/en';
|
||||
|
||||
import type { Locale as AntdLocale } from 'ant-design-vue/es/locale';
|
||||
import enUS from 'ant-design-vue/es/locale/en_US';
|
||||
import zhCN from 'ant-design-vue/es/locale/zh_CN';
|
||||
import dayjs from 'dayjs';
|
||||
import { chain } from 'lodash-es';
|
||||
import { ref, watch } from 'vue';
|
||||
import { createI18n } from 'vue-i18n';
|
||||
|
||||
const LOCALE_FILES = import.meta.glob('asset/locales/*.json', { eager: true, import: 'default' });
|
||||
const LOCAL_MAP = chain(LOCALE_FILES)
|
||||
.mapKeys((_, k) => k.match(/^.*[\\|\\/](.+?)\.[^\\.]+$/)?.[1])
|
||||
.mapValues((v) => <Record<string, string>>v)
|
||||
.value();
|
||||
|
||||
enum Locale {
|
||||
简体中文 = 'zh-CN',
|
||||
English = 'en-US',
|
||||
}
|
||||
export const LOCALES = Object.freeze<[string, Locale][]>(Object.entries(Locale));
|
||||
|
||||
export const i18n = createI18n({
|
||||
legacy: true,
|
||||
silentTranslationWarn: true,
|
||||
locale: Locale.简体中文,
|
||||
messages: chain(Locale)
|
||||
.invert()
|
||||
.mapValues((_, k) => LOCAL_MAP[k] ?? {})
|
||||
.value(),
|
||||
});
|
||||
|
||||
const LOCALE_STORAGE_KEY = 'locale';
|
||||
|
||||
class LocaleService {
|
||||
#locale = ref<Locale>(<Locale>localStorage.getItem(LOCALE_STORAGE_KEY) || Locale.简体中文);
|
||||
public get locale(): Locale {
|
||||
return this.#locale.value;
|
||||
}
|
||||
public set locale(v: Locale) {
|
||||
this.#locale.value = v;
|
||||
localStorage.setItem(LOCALE_STORAGE_KEY, v);
|
||||
}
|
||||
|
||||
public get ant(): AntdLocale {
|
||||
switch (this.#locale.value) {
|
||||
case Locale.English:
|
||||
return enUS;
|
||||
case Locale.简体中文:
|
||||
default:
|
||||
return zhCN;
|
||||
}
|
||||
}
|
||||
|
||||
constructor() {
|
||||
watch(this.#locale, (v) => this.#load(v), { immediate: true });
|
||||
}
|
||||
|
||||
#load(locale: Locale): void {
|
||||
i18n.global.locale = locale;
|
||||
switch (locale) {
|
||||
case Locale.English:
|
||||
dayjs.locale('en');
|
||||
break;
|
||||
case Locale.简体中文:
|
||||
default:
|
||||
dayjs.locale('zh-cn');
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
export default new LocaleService();
|
54
src/services/router.ts
Normal file
54
src/services/router.ts
Normal file
@ -0,0 +1,54 @@
|
||||
import { createRouter, createWebHistory, type RouteRecordRaw } from 'vue-router';
|
||||
|
||||
export const ROUTES = Object.freeze<RouteRecordRaw[]>([
|
||||
{ path: '/:pathMatch(.*)*', redirect: '/home' },
|
||||
{ path: '/home', component: () => import('@/home.vue') },
|
||||
// {
|
||||
// path: '/',
|
||||
// component: () => import('@layout/main.vue'),
|
||||
// children: [
|
||||
// { path: '', redirect: 'home' },
|
||||
// { name: '首页', path: 'home', component: () => import('@/home.vue') },
|
||||
// { name: '任务管理', path: 'task-management', component: () => import('@/home.vue') },
|
||||
// { name: '车辆管理', path: 'vehicle-management', component: () => import('@/home.vue') },
|
||||
// {
|
||||
// name: '地图管理',
|
||||
// path: 'map-management',
|
||||
// meta: { standalone: true },
|
||||
// component: () => import('@/map-management/scene-edit.vue'),
|
||||
// },
|
||||
// { name: '参数设置', path: 'parameter-setting', component: import('@/parameter-setting/index.vue') },
|
||||
// {
|
||||
// name: '调度仿真',
|
||||
// path: 'scheduling-emulation',
|
||||
// children: [
|
||||
// {
|
||||
// name: '调度模拟',
|
||||
// path: 'scheduling-simulation',
|
||||
// components: {
|
||||
// default: () => import('@/scheduling-emulation/scheduling-simulation.vue'),
|
||||
// toolbar: () => import('@common/map-switcher.vue'),
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// name: '车辆调试',
|
||||
// path: 'vehicle-commissioning',
|
||||
// component: () => import('@/scheduling-emulation/vehicle-commissioning.vue'),
|
||||
// },
|
||||
// {
|
||||
// name: '场景测试',
|
||||
// path: 'scenario-testing',
|
||||
// component: () => import('@/scheduling-emulation/scenario-testing.vue'),
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
// { name: '告警管理', path: 'alarm-management', component: () => import('@/home.vue') },
|
||||
// { name: '日志管理', path: 'log-management', component: () => import('@/home.vue') },
|
||||
// ],
|
||||
// },
|
||||
]);
|
||||
|
||||
export const router = createRouter({
|
||||
history: createWebHistory(import.meta.env.BASE_URL),
|
||||
routes: ROUTES,
|
||||
});
|
40
src/services/theme.service.ts
Normal file
40
src/services/theme.service.ts
Normal file
@ -0,0 +1,40 @@
|
||||
import { theme, type TokenType as AntdTheme } from 'ant-design-vue';
|
||||
import { ref, watch } from 'vue';
|
||||
|
||||
enum Theme {
|
||||
Light = 'light',
|
||||
Dark = 'dark',
|
||||
}
|
||||
export const THEMES = Object.freeze<[string, Theme][]>(Object.entries(Theme));
|
||||
|
||||
const THEME_STORAGE_KEY = 'locale';
|
||||
|
||||
class ThemeService {
|
||||
#theme = ref<Theme>(<Theme>localStorage.getItem(THEME_STORAGE_KEY) || Theme.Dark);
|
||||
public get theme(): Theme {
|
||||
return this.#theme.value;
|
||||
}
|
||||
public set theme(v: Theme) {
|
||||
this.#theme.value = v;
|
||||
localStorage.setItem(THEME_STORAGE_KEY, v);
|
||||
}
|
||||
|
||||
public get ant(): AntdTheme {
|
||||
switch (this.#theme.value) {
|
||||
case Theme.Dark:
|
||||
return { algorithm: theme.darkAlgorithm };
|
||||
case Theme.Light:
|
||||
default:
|
||||
return { algorithm: theme.defaultAlgorithm };
|
||||
}
|
||||
}
|
||||
|
||||
constructor() {
|
||||
watch(this.#theme, (v) => this.#load(v), { immediate: true });
|
||||
}
|
||||
|
||||
#load(theme: Theme): void {
|
||||
document.documentElement.setAttribute('theme', theme);
|
||||
}
|
||||
}
|
||||
export default new ThemeService();
|
19
src/style.scss
Normal file
19
src/style.scss
Normal file
@ -0,0 +1,19 @@
|
||||
@use './ant';
|
||||
|
||||
html,
|
||||
body {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#app {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.full {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
7
src/vite-env.d.ts
vendored
Normal file
7
src/vite-env.d.ts
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
/// <reference types="vite/client" />
|
||||
|
||||
declare module '*.vue' {
|
||||
import type { DefineComponent } from 'vue';
|
||||
const vueComponent: DefineComponent<object, object, unknown>;
|
||||
export default vueComponent;
|
||||
}
|
24
tsconfig.app.json
Normal file
24
tsconfig.app.json
Normal file
@ -0,0 +1,24 @@
|
||||
{
|
||||
"extends": "@vue/tsconfig/tsconfig.dom.json",
|
||||
"compilerOptions": {
|
||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
||||
|
||||
/* Linting */
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noUncheckedSideEffectImports": true,
|
||||
|
||||
"baseUrl": "./",
|
||||
"paths": {
|
||||
"asset/*": ["src/assets/*"],
|
||||
"@/*": ["src/pages/*"],
|
||||
"@api/*": ["src/apis/*"],
|
||||
"@common/*": ["src/components/*"],
|
||||
"@core/*": ["src/services/*"],
|
||||
"@layout/*": ["src/layouts/*"]
|
||||
}
|
||||
},
|
||||
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"]
|
||||
}
|
7
tsconfig.json
Normal file
7
tsconfig.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"files": [],
|
||||
"references": [
|
||||
{ "path": "./tsconfig.app.json" },
|
||||
{ "path": "./tsconfig.node.json" }
|
||||
]
|
||||
}
|
24
tsconfig.node.json
Normal file
24
tsconfig.node.json
Normal file
@ -0,0 +1,24 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
||||
"target": "ES2022",
|
||||
"lib": ["ES2023"],
|
||||
"module": "ESNext",
|
||||
"skipLibCheck": true,
|
||||
|
||||
/* Bundler mode */
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"isolatedModules": true,
|
||||
"moduleDetection": "force",
|
||||
"noEmit": true,
|
||||
|
||||
/* Linting */
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noUncheckedSideEffectImports": true
|
||||
},
|
||||
"include": ["vite.config.ts"]
|
||||
}
|
54
vite.config.ts
Normal file
54
vite.config.ts
Normal file
@ -0,0 +1,54 @@
|
||||
import vue from '@vitejs/plugin-vue';
|
||||
import { resolve } from 'path';
|
||||
import { AntDesignVueResolver } from 'unplugin-vue-components/resolvers';
|
||||
import Components from 'unplugin-vue-components/vite';
|
||||
import { defineConfig } from 'vite';
|
||||
|
||||
export default ({ mode }: Record<string, unknown>) =>
|
||||
defineConfig({
|
||||
plugins: [vue(), Components({ resolvers: [AntDesignVueResolver({ importStyle: false, resolveIcons: true })] })],
|
||||
base: '/',
|
||||
envPrefix: 'ENV_',
|
||||
resolve: {
|
||||
alias: {
|
||||
asset: resolve(__dirname, 'src/assets/'),
|
||||
'@': resolve(__dirname, 'src/pages/'),
|
||||
'@api': resolve(__dirname, 'src/apis/'),
|
||||
'@common': resolve(__dirname, 'src/components/'),
|
||||
'@core': resolve(__dirname, 'src/services/'),
|
||||
'@layout': resolve(__dirname, 'src/layouts/'),
|
||||
},
|
||||
},
|
||||
css: {
|
||||
preprocessorOptions: {
|
||||
scss: { api: 'modern-compiler' },
|
||||
},
|
||||
},
|
||||
build: {
|
||||
target: 'es2020',
|
||||
outDir: 'dist',
|
||||
sourcemap: false,
|
||||
minify: 'esbuild',
|
||||
chunkSizeWarningLimit: 2000,
|
||||
},
|
||||
esbuild: {
|
||||
drop: mode === 'production' ? ['console'] : [],
|
||||
},
|
||||
server: {
|
||||
port: 8888,
|
||||
host: true,
|
||||
proxy: {
|
||||
// '/api/': {
|
||||
// target: 'http://82.157.33.186:26981/jeecg-boot',
|
||||
// rewrite: (path) => path.replace(/^\/api/, ''),
|
||||
// changeOrigin: true,
|
||||
// },
|
||||
// '/ws/': {
|
||||
// target: 'ws://82.157.33.186:26981/jeecg-boot',
|
||||
// rewrite: (path) => path.replace(/^\/ws/, ''),
|
||||
// changeOrigin: true,
|
||||
// ws: true,
|
||||
// },
|
||||
},
|
||||
},
|
||||
});
|
Loading…
x
Reference in New Issue
Block a user