Browse Source

创建项目

master
LiLongLong 6 months ago
parent
commit
00a6f144f2
  1. 3
      .env
  2. 35
      .gitignore
  3. 32
      README.md
  4. 1
      env.d.ts
  5. 13
      index.html
  6. 4876
      package-lock.json
  7. 41
      package.json
  8. 8
      postcss.config.js
  9. BIN
      public/favicon.ico
  10. 51
      src/App.vue
  11. 31
      src/assets/base.css
  12. 1
      src/assets/logo.svg
  13. 7
      src/assets/main.css
  14. 46
      src/components/HelloWorld.vue
  15. 20
      src/main.ts
  16. 23
      src/router/index.ts
  17. 58
      src/services/axios.ts
  18. 108
      src/services/socket.ts
  19. 12
      src/stores/counter.ts
  20. 3
      src/style.css
  21. 9
      src/utils/index.ts
  22. 9
      src/views/AboutView.vue
  23. 9
      src/views/HomeView.vue
  24. 12
      tailwind.config.js
  25. 12
      tsconfig.app.json
  26. 11
      tsconfig.json
  27. 18
      tsconfig.node.json
  28. 20
      vite.config.ts

3
.env

@ -0,0 +1,3 @@
VITE_API_HOST=window.location.hostname
VITE_API_PORT=80
VITE_WS_PATH=/api/v1/app/ws/state

35
.gitignore

@ -1,11 +1,30 @@
# ---> Vue
# gitignore template for Vue.js projects
#
# Recommended template: Node.gitignore
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
# TODO: where does this rule come from?
docs/_book
node_modules
.DS_Store
dist
dist-ssr
coverage
*.local
# TODO: where does this rule come from?
test/
/cypress/videos/
/cypress/screenshots/
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
*.tsbuildinfo

32
README.md

@ -1,3 +1,31 @@
# auxiliary_sample
# A1800
辅助加样
## Project Setup
```sh
npm install
```
### Compile and Hot-Reload for Development
```sh
npm run dev
```
### Type-Check, Compile and Minify for Production
```sh
npm run build
```
## 约定
目录:
views: 主要页面
components: 子组件
services: API接口
assets: 资源(图片)
utils: 实用工具
.env 环境变量(如IP,Port)
.env.local 本地环境变量

1
env.d.ts

@ -0,0 +1 @@
/// <reference types="vite/client" />

13
index.html

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="UTF-8">
<link rel="icon" href="/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>Vite App</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

4876
package-lock.json
File diff suppressed because it is too large
View File

41
package.json

@ -0,0 +1,41 @@
{
"name": "a1800",
"version": "0.0.0",
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build": "run-p type-check \"build-only {@}\" --",
"preview": "vite preview",
"build-only": "vite build",
"type-check": "vue-tsc --build"
},
"dependencies": {
"axios": "^1.7.9",
"dayjs": "^1.11.13",
"pinia": "^2.3.0",
"ramda": "^0.30.1",
"rxjs": "^7.8.1",
"vant": "^4.9.16",
"vue": "^3.5.13",
"vue-router": "^4.5.0"
},
"devDependencies": {
"@tsconfig/node22": "^22.0.0",
"@types/node": "^22.10.2",
"@types/ramda": "^0.30.2",
"@types/rx": "^4.1.4",
"@vitejs/plugin-vue": "^5.2.1",
"@vitejs/plugin-vue-jsx": "^4.1.1",
"@vue/tsconfig": "^0.7.0",
"autoprefixer": "^10.4.20",
"npm-run-all2": "^7.0.2",
"postcss": "^8.5.1",
"postcss-import": "^16.1.0",
"tailwindcss": "^3.4.17",
"typescript": "~5.6.3",
"vite": "^6.0.5",
"vite-plugin-vue-devtools": "^7.6.8",
"vue-tsc": "^2.1.10"
}
}

8
postcss.config.js

@ -0,0 +1,8 @@
export default {
plugins: {
'postcss-import': {},
'tailwindcss/nesting': {},
tailwindcss: {},
autoprefixer: {},
},
}

BIN
public/favicon.ico

51
src/App.vue

@ -0,0 +1,51 @@
<script setup lang="ts">
import { RouterLink, RouterView } from "vue-router";
import HelloWorld from "./components/HelloWorld.vue";
</script>
<template>
<header>
<img alt="Vue logo" class="logo" src="@/assets/logo.svg" width="125" height="125" />
<div class="wrapper">
<HelloWorld msg="You did it!" />
<h1 class="text-3xl font-bold underline">Hello world!</h1>
<nav>
<RouterLink to="/">Home</RouterLink>
<RouterLink to="/about">About</RouterLink>
</nav>
</div>
</header>
<RouterView />
</template>
<style scoped>
header {
line-height: 1.5;
max-height: 100vh;
.logo {
display: block;
margin: 0 auto 2rem;
}
nav {
width: 100%;
font-size: 12px;
text-align: center;
margin-top: 2rem;
a {
display: inline-block;
padding: 0 1rem;
border-left: 1px solid var(--color-border);
&:first-of-type {
border: 0;
}
&.router-link-exact-active {
color: green;
}
}
}
}
</style>

31
src/assets/base.css

@ -0,0 +1,31 @@
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
font-weight: normal;
}
body {
min-height: 100vh;
font-family:
Inter,
-apple-system,
BlinkMacSystemFont,
'Segoe UI',
Roboto,
Oxygen,
Ubuntu,
Cantarell,
'Fira Sans',
'Droid Sans',
'Helvetica Neue',
sans-serif;
font-size: 16px;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
user-select: none;
}

1
src/assets/logo.svg

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 261.76 226.69"><path d="M161.096.001l-30.225 52.351L100.647.001H-.005l130.877 226.688L261.749.001z" fill="#41b883"/><path d="M161.096.001l-30.225 52.351L100.647.001H52.346l78.526 136.01L209.398.001z" fill="#34495e"/></svg>

7
src/assets/main.css

@ -0,0 +1,7 @@
@import './base.css';
#app {
max-width: 1280px;
max-height: 800px;
overflow: hidden;
}

46
src/components/HelloWorld.vue

@ -0,0 +1,46 @@
<script setup lang="ts">
import { ref } from "vue";
import { showToast } from "vant";
defineProps<{
msg: string;
}>();
const value = ref<number>(50);
const onChange = (value: number) => showToast("当前值:" + value);
</script>
<template>
<div class="greetings">
<h1 class="green">{{ msg }}</h1>
<h3>
Youve successfully created a project with
<a href="https://vite.dev/" target="_blank" rel="noopener">Vite</a> +
<a href="https://vuejs.org/" target="_blank" rel="noopener">Vue 3</a>. What's next?
</h3>
<span class="text-lg font-medium">{{ value }}</span>
<van-slider class="my-8" :step="0.5" v-model="value" @change="onChange" />
</div>
</template>
<style scoped>
.greetings {
--van-slider-bar-height: 6px;
--van-slider-button-width: 32px;
--van-slider-button-height: 32px;
}
h1 {
font-weight: 500;
font-size: 2.6rem;
position: relative;
top: -10px;
}
h3 {
font-size: 1.2rem;
}
.greetings h1,
.greetings h3 {
text-align: center;
}
</style>

20
src/main.ts

@ -0,0 +1,20 @@
import "./style.css";
import "./assets/main.css";
import { createApp } from "vue";
import { createPinia } from "pinia";
import Vant from "vant";
import App from "./App.vue";
import router from "./router";
import "vant/lib/index.css";
const app = createApp(App);
app.use(createPinia());
app.use(router);
app.use(Vant);
app.mount("#app");
console.log("API HOST:", import.meta.env.VITE_API_HOST);
console.log("API PORT:", import.meta.env.VITE_API_PORT);

23
src/router/index.ts

@ -0,0 +1,23 @@
import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/',
name: 'home',
component: HomeView,
},
{
path: '/about',
name: 'about',
// route level code-splitting
// this generates a separate chunk (About.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import('../views/AboutView.vue'),
},
],
})
export default router

58
src/services/axios.ts

@ -0,0 +1,58 @@
import axios from "axios";
const url = `${window.location.protocol}://${import.meta.env.VITE_API_HOST}:${import.meta.env.VITE_API_PORT}`;
const apiClient = axios.create({
baseURL: url, // 设置请求的根路径
timeout: 60000,
headers: {
"Content-Type": "application/json",
},
});
// 请求拦截器
// apiClient.interceptors.request.use(
// (config) => {
// const token = sessionStorage.getItem("token");
// if (!config.headers) {
// config.headers = AxiosHeaders.from({}); // 确保 config.headers 是 AxiosHeaders 类型
// }
// if (token) {
// config.headers.set("Authorization", `Bearer ${encodeURIComponent(token)}`); // 使用 set 方法设置 Authorization
// }
// return config;
// },
// error => {
// return Promise.reject(error);
// }
// );
// 响应拦截器
apiClient.interceptors.response.use(
(response) => {
if (response.data && response.data.dataType === "ZAppPromopt") {
if (response.data.ecode === "USR_NOT_EXIT") {
return Promise.resolve(response.data);
} else if (response.data.ecode === "USR_PASSWORD_ERROR") {
return Promise.resolve(response.data);
} else {
console.log("接口出错", response.data);
// eventBus.emit("show-error-modal", response.data.data);
return Promise.reject(response.data);
}
}
return response;
},
error => {
// eventBus.emit("show-error-modal", {
// messageLevel: "Error",
// title: "网络请求失败",
// info: error.message,
// });
return Promise.reject(error);
}
);
export default apiClient;

108
src/services/socket.ts

@ -0,0 +1,108 @@
import { Subject } from "rxjs";
export type SocketState = 'open' | 'close' | 'error'
class WebSocketClient {
private ws: WebSocket | null = null;
private url: string;
private reconnectAttempts: number = -1;
private maxReconnectAttempts: number = 5;
private reconnectInterval: number = 3000;
readonly dataOb = new Subject()
readonly stateOb = new Subject<SocketState>()
constructor(url: string) {
this.url = url;
}
// 连接 WebSocket
connect(): void {
try {
// WebSocket.CONNECTING (0) WebSocket.OPEN (1)
if (this.ws && this.ws.readyState <= 1) {
// 已连接
console.log(`${this.url} 正在连接或已连接,无需重复连接`);
} else {
this.ws = new WebSocket(this.url);
this.bindEvents();
}
} catch (error) {
console.error("WebSocket 连接失败:", error);
this.reconnect();
}
}
// 绑定事件
private bindEvents(): void {
if (!this.ws) return;
// 连接建立时的处理
this.ws.onopen = () => {
console.log("WebSocket 连接已建立");
this.reconnectAttempts = -1; // 重置重连次数
this.stateOb.next('open')
};
// 接收消息的处理
this.ws.onmessage = (event: MessageEvent) => {
try {
const data = JSON.parse(event.data);
// console.log('🚀 ~ WebSocketClient ~ bindEvents ~ data:', data)
this.dataOb.next(data)
} catch (error) {
console.error("消息解析错误:", error);
}
};
this.ws.onclose = () => {
this.stateOb.next('close')
console.log("WebSocket 连接已关闭");
this.reconnect();
};
this.ws.onerror = error => {
this.stateOb.next('error')
console.error("WebSocket 错误:", error);
};
}
// 重连机制
private reconnect(): void {
if (this.reconnectAttempts === -1) {
this.reconnectAttempts = 0;
}
if (this.reconnectAttempts >= this.maxReconnectAttempts) {
console.log("达到最大重连次数,停止重连");
this.reconnectAttempts = -1;
return;
}
setTimeout(() => {
console.log(`尝试第 ${this.reconnectAttempts + 1} 次重连...`);
this.reconnectAttempts++;
this.connect();
}, this.reconnectInterval);
}
// 关闭连接
disconnect(): void {
if (this.ws) {
this.ws.close();
this.ws = null;
}
}
}
const urlSocketMap = new Map<string, WebSocketClient>();
// 导出 WebSocket 客户端
export const createWebSocket = (url: string): WebSocketClient => {
if (urlSocketMap.has(url)) {
return urlSocketMap.get(url)!;
} else {
const client = new WebSocketClient(url);
urlSocketMap.set(url, client);
return client;
}
};

12
src/stores/counter.ts

@ -0,0 +1,12 @@
import { ref, computed } from 'vue'
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', () => {
const count = ref(0)
const doubleCount = computed(() => count.value * 2)
function increment() {
count.value++
}
return { count, doubleCount, increment }
})

3
src/style.css

@ -0,0 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

9
src/utils/index.ts

@ -0,0 +1,9 @@
export function isValidIPv4(ip: string) {
const ipv4Regex = /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
return ipv4Regex.test(ip);
}
export function formatRemainTime(seconds: number) {
const min = Math.floor(seconds / 60).toFixed();
const sec = (seconds % 60).toFixed();
return min.padStart(2, "0") + ":" + sec.padStart(2, "0");
}

9
src/views/AboutView.vue

@ -0,0 +1,9 @@
<template>
<div class="about">
<h1>This is an about page</h1>
</div>
</template>
<style>
</style>

9
src/views/HomeView.vue

@ -0,0 +1,9 @@
<script setup lang="ts">
</script>
<template>
<main>
<div>Home Sub View</div>
</main>
</template>

12
tailwind.config.js

@ -0,0 +1,12 @@
/** @type {import('tailwindcss').Config} */
export default {
content: [
"./index.html",
"./src/**/*.{vue,js,ts,jsx,tsx}",
],
theme: {
extend: {},
},
plugins: [],
}

12
tsconfig.app.json

@ -0,0 +1,12 @@
{
"extends": "@vue/tsconfig/tsconfig.dom.json",
"include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
"exclude": ["src/**/__tests__/*"],
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
"paths": {
"@/*": ["./src/*"]
}
}
}

11
tsconfig.json

@ -0,0 +1,11 @@
{
"files": [],
"references": [
{
"path": "./tsconfig.node.json"
},
{
"path": "./tsconfig.app.json"
}
]
}

18
tsconfig.node.json

@ -0,0 +1,18 @@
{
"extends": "@tsconfig/node22/tsconfig.json",
"include": [
"vite.config.*",
"vitest.config.*",
"cypress.config.*",
"nightwatch.conf.*",
"playwright.config.*"
],
"compilerOptions": {
"noEmit": true,
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
"module": "ESNext",
"moduleResolution": "Bundler",
"types": ["node"]
}
}

20
vite.config.ts

@ -0,0 +1,20 @@
import { fileURLToPath, URL } from "node:url";
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import vueJsx from "@vitejs/plugin-vue-jsx";
import vueDevTools from "vite-plugin-vue-devtools";
// https://vite.dev/config/
export default defineConfig({
plugins: [vue(), vueJsx(), vueDevTools()],
resolve: {
alias: {
"@": fileURLToPath(new URL("./src", import.meta.url)),
},
},
server: {
host: "0.0.0.0",
port: 5174,
},
});
Loading…
Cancel
Save