首次提交
|
|
@ -0,0 +1,3 @@
|
||||||
|
VITE_BASE_URL_API = 'http://192.168.2.9:5192'
|
||||||
|
|
||||||
|
VITE_ENV = 'development'
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
VITE_BASE_URL_API = https://meeting-api.23544.com/pc
|
||||||
|
|
||||||
|
VITE_ENV = 'production'
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
VITE_BASE_URL_API = https://meeting-api.23544.com/pc
|
||||||
|
|
||||||
|
VITE_ENV = 'test'
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
# Windows
|
||||||
|
[Dd]esktop.ini
|
||||||
|
Thumbs.db
|
||||||
|
$RECYCLE.BIN/
|
||||||
|
|
||||||
|
# macOS
|
||||||
|
.DS_Store
|
||||||
|
.fseventsd
|
||||||
|
.Spotlight-V100
|
||||||
|
.TemporaryItems
|
||||||
|
.Trashes
|
||||||
|
|
||||||
|
# Node.js
|
||||||
|
node_modules/
|
||||||
|
dist/
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
/* eslint-disable */
|
||||||
|
/* prettier-ignore */
|
||||||
|
// @ts-nocheck
|
||||||
|
// noinspection JSUnusedGlobalSymbols
|
||||||
|
// Generated by unplugin-auto-import
|
||||||
|
export {}
|
||||||
|
declare global {
|
||||||
|
const ElLoading: typeof import('element-plus/es')['ElLoading']
|
||||||
|
const ElMessage: typeof import('element-plus/es')['ElMessage']
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
/* eslint-disable */
|
||||||
|
/* prettier-ignore */
|
||||||
|
// @ts-nocheck
|
||||||
|
// Generated by unplugin-vue-components
|
||||||
|
// Read more: https://github.com/vuejs/core/pull/3399
|
||||||
|
export {}
|
||||||
|
|
||||||
|
declare module 'vue' {
|
||||||
|
export interface GlobalComponents {
|
||||||
|
ElButton: typeof import('element-plus/es')['ElButton']
|
||||||
|
ElEmpty: typeof import('element-plus/es')['ElEmpty']
|
||||||
|
RouterLink: typeof import('vue-router')['RouterLink']
|
||||||
|
RouterView: typeof import('vue-router')['RouterView']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<!-- <link rel="icon" type="image/svg+xml" href="/vite.svg" /> -->
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title></title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
<script type="module" src="/src/main.ts"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
{
|
||||||
|
"name": "vue3-project-template",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.0.0",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"test": "vite --mode test",
|
||||||
|
"prod": "vite --mode production",
|
||||||
|
"build": "vite build",
|
||||||
|
"build:dev": "vite build --mode development",
|
||||||
|
"build:test": "vite build --mode test",
|
||||||
|
"build:prod": "vite build --mode production",
|
||||||
|
"preview": "vite preview",
|
||||||
|
"lint": "eslint . --ext src/**/*.{js,jsx,vue,ts,tsx} --fix"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@element-plus/icons-vue": "^2.1.0",
|
||||||
|
"agora-rtc-sdk-ng": "^4.23.1",
|
||||||
|
"axios": "^1.4.0",
|
||||||
|
"element-plus": "^2.3.7",
|
||||||
|
"js-md5": "^0.7.3",
|
||||||
|
"pinia": "^2.1.4",
|
||||||
|
"vue": "^3.2.47",
|
||||||
|
"vue-router": "^4.2.2"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@iconify-json/ep": "^1.1.11",
|
||||||
|
"@types/node": "^20.3.1",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^6.3.0",
|
||||||
|
"@typescript-eslint/parser": "^6.3.0",
|
||||||
|
"@vitejs/plugin-vue": "^4.1.0",
|
||||||
|
"@vue/cli-plugin-eslint": "^5.0.8",
|
||||||
|
"@vue/eslint-config-prettier": "^8.0.0",
|
||||||
|
"babel-eslint": "^10.1.0",
|
||||||
|
"eslint": "^8.46.0",
|
||||||
|
"eslint-config-airbnb-base": "^15.0.0",
|
||||||
|
"eslint-plugin-import": "^2.28.0",
|
||||||
|
"eslint-plugin-prettier": "^5.0.0",
|
||||||
|
"eslint-plugin-vue": "^9.16.1",
|
||||||
|
"postcss-px-to-viewport": "^1.1.1",
|
||||||
|
"sass": "^1.63.6",
|
||||||
|
"terser": "^5.18.1",
|
||||||
|
"typescript": "^5.0.2",
|
||||||
|
"unplugin-auto-import": "^0.16.4",
|
||||||
|
"unplugin-icons": "^0.16.3",
|
||||||
|
"unplugin-vue-components": "^0.25.1",
|
||||||
|
"vite": "^4.3.9",
|
||||||
|
"vite-plugin-inspect": "^0.7.29",
|
||||||
|
"vue-tsc": "^1.4.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
<template>
|
||||||
|
<router-view />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { onMounted } from "vue";
|
||||||
|
onMounted(() => {
|
||||||
|
init();
|
||||||
|
})
|
||||||
|
const init = async () => {
|
||||||
|
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
import { request } from '@/utils'
|
||||||
|
export const GetRoomRtcToken = (roomNum: string) =>
|
||||||
|
request({
|
||||||
|
url: `/room/tk/rtc?roomNum=${roomNum}`,
|
||||||
|
method: 'get',
|
||||||
|
})
|
||||||
|
|
||||||
|
export const GetAgoraConf = () =>
|
||||||
|
request({
|
||||||
|
url: `/home/agora-conf`,
|
||||||
|
method: 'get',
|
||||||
|
})
|
||||||
|
|
||||||
|
export const GetPolling = (roomNum: string, count: string) =>
|
||||||
|
request({
|
||||||
|
url: `/room/polling?roomNum=${roomNum}&count=${count}`,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
After Width: | Height: | Size: 860 KiB |
|
After Width: | Height: | Size: 1.5 MiB |
|
After Width: | Height: | Size: 297 KiB |
|
After Width: | Height: | Size: 200 KiB |
|
After Width: | Height: | Size: 836 KiB |
|
After Width: | Height: | Size: 456 KiB |
|
After Width: | Height: | Size: 1.4 MiB |
|
After Width: | Height: | Size: 297 KiB |
|
After Width: | Height: | Size: 367 KiB |
|
After Width: | Height: | Size: 336 KiB |
|
After Width: | Height: | Size: 770 KiB |
|
After Width: | Height: | Size: 6.8 KiB |
|
After Width: | Height: | Size: 6.7 KiB |
|
After Width: | Height: | Size: 8.5 KiB |
|
After Width: | Height: | Size: 8.4 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 412 B |
|
After Width: | Height: | Size: 352 B |
|
After Width: | Height: | Size: 159 KiB |
|
|
@ -0,0 +1,17 @@
|
||||||
|
// 常量配置
|
||||||
|
enum constant {
|
||||||
|
CONFIG_TITLE = 'vue3-vite-ts-pinia',
|
||||||
|
CONFIG_REQUEST_TIMEOUT_TIME = 10000, // 请求超时时间 10秒
|
||||||
|
CONFIG_TOKEN = 'token', // token
|
||||||
|
CONFIG_USERINFO = 'USERINFO', // 用户信息
|
||||||
|
CONFIG_STATUS_CODE_SUCCESS = 100, // 自定义代码 100成功、101失败
|
||||||
|
CONFIG_STATUS_CODE_ERROR = 101,
|
||||||
|
CONFIG_USERNAME_KEY = 'USERNAME', // 用户名
|
||||||
|
CONFIG_PASSWORD_KEY = 'PASSWORD', // 密码
|
||||||
|
CONFIG_IS_REMEMBER_KEY = 'REMEMBER', // 是否记住密码
|
||||||
|
CONFIG_CODE_SUCCESS = 200, // 成功码
|
||||||
|
}
|
||||||
|
// 常规配置
|
||||||
|
const config = {}
|
||||||
|
|
||||||
|
export { config, constant }
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
function user() {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
export default user
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
import { createApp } from 'vue'
|
||||||
|
import router from './router'
|
||||||
|
import './styles/index.scss'
|
||||||
|
import App from './App.vue'
|
||||||
|
|
||||||
|
import Store from './store'
|
||||||
|
|
||||||
|
// 清除项目中的console
|
||||||
|
if (import.meta.env.VITE_ENV !== 'development') {
|
||||||
|
console.log = function () { }
|
||||||
|
}
|
||||||
|
|
||||||
|
const app = createApp(App)
|
||||||
|
|
||||||
|
app.use(router).use(Store).mount('#app')
|
||||||
|
|
@ -0,0 +1,192 @@
|
||||||
|
<template>
|
||||||
|
<div class="home">
|
||||||
|
<div class="home-header">
|
||||||
|
<span>距离下次刷新监控人员还剩{{ timeNumber }}秒</span>
|
||||||
|
<el-button type="primary" @click="refreshUserList" style="margin-left: 10px;">刷新</el-button>
|
||||||
|
</div>
|
||||||
|
<div class="home-content" v-if="userList.length">
|
||||||
|
<div v-for="item in userList" class="home-content-item">
|
||||||
|
<div class="home-content-item-card" :id="'player-wrapper-' + item.uid">
|
||||||
|
<div class="home-content-item-card-name">{{ item.userName }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<di class="home-content-item">
|
||||||
|
<div class="home-content-item-card">
|
||||||
|
</div>
|
||||||
|
</di>
|
||||||
|
<di class="home-content-item">
|
||||||
|
<div class="home-content-item-card">
|
||||||
|
</div>
|
||||||
|
</di>
|
||||||
|
<di class="home-content-item">
|
||||||
|
<div class="home-content-item-card">
|
||||||
|
</div>
|
||||||
|
</di>
|
||||||
|
<di class="home-content-item">
|
||||||
|
<div class="home-content-item-card">
|
||||||
|
</div>
|
||||||
|
</di>
|
||||||
|
<div class="home-content-item">
|
||||||
|
<div class="home-content-item-card">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="home-content-item">
|
||||||
|
<div class="home-content-item-card">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="home-content-item">
|
||||||
|
<div class="home-content-item-card">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="home-content-item">
|
||||||
|
<div class="home-content-item-card">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="home-content" v-else style="justify-content: center;align-items: center;align-content: center;">
|
||||||
|
<el-empty description="暂无数据" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { onMounted, ref, onUnmounted } from "vue";
|
||||||
|
import { agora } from "@/utils/package/agora";
|
||||||
|
import { GetAgoraConf, GetPolling, GetRoomRtcToken } from "@/api";
|
||||||
|
import { storage } from '@/utils'
|
||||||
|
const userList = ref<any>([])
|
||||||
|
const channel = ref('')
|
||||||
|
const timer = ref<NodeJS.Timeout>()
|
||||||
|
const timeNumber = ref(30)
|
||||||
|
onMounted(() => {
|
||||||
|
init()
|
||||||
|
startCountdown()
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
clearInterval(timer.value)
|
||||||
|
});
|
||||||
|
|
||||||
|
const startCountdown = () => {
|
||||||
|
clearInterval(timer.value);
|
||||||
|
timeNumber.value = 30;
|
||||||
|
timer.value = setInterval(() => {
|
||||||
|
timeNumber.value--;
|
||||||
|
if (timeNumber.value === 0) {
|
||||||
|
refreshUserList();
|
||||||
|
timeNumber.value = 30;
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
};
|
||||||
|
const init = async () => {
|
||||||
|
const paramsString = location.search.startsWith('?') ? location.search.slice(1) : location.search;
|
||||||
|
const searchParams = new URLSearchParams(paramsString)
|
||||||
|
channel.value = searchParams.get('channel') as string;
|
||||||
|
storage.setItem('token', searchParams.get('token'))
|
||||||
|
agora.setOptions('uid', Number(searchParams.get('uid')))
|
||||||
|
agora.setOptions('channel', channel.value)
|
||||||
|
await getAgoraConf()
|
||||||
|
await getPolling()
|
||||||
|
await getRoomRtcToken()
|
||||||
|
agoraOn()
|
||||||
|
};
|
||||||
|
|
||||||
|
const getAgoraConf = async () => {
|
||||||
|
await GetAgoraConf().then(res => {
|
||||||
|
if (res.code === 200) {
|
||||||
|
agora.init();
|
||||||
|
agora.setOptions('appId', res.data)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
const getPolling = async () => {
|
||||||
|
await GetPolling(channel.value, '9').then((res: any) => {
|
||||||
|
if (res.code === 200) {
|
||||||
|
userList.value = res.data.map((item: any) => {
|
||||||
|
return {
|
||||||
|
...item,
|
||||||
|
newScreenShareId: Number(1 + item.screenShareId)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
};
|
||||||
|
const refreshUserList = async () => {
|
||||||
|
await agora.leave()
|
||||||
|
await getPolling()
|
||||||
|
await getRoomRtcToken()
|
||||||
|
};
|
||||||
|
const getRoomRtcToken = async () => {
|
||||||
|
GetRoomRtcToken(channel.value).then(res => {
|
||||||
|
if (res.code === 200) {
|
||||||
|
agora.setOptions('token', res.data)
|
||||||
|
agora.join()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
};
|
||||||
|
const agoraOn = async () => {
|
||||||
|
agora.on({
|
||||||
|
userPublished: (user: any, mediaType: 'video' | 'audio') => {
|
||||||
|
if (mediaType === 'video') {
|
||||||
|
user.videoTrack.play(document.getElementById(`player-wrapper-${user.uid}`));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.home {
|
||||||
|
background-color: rgb(7, 9, 11);
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 4px 120px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
&-header {
|
||||||
|
flex-shrink: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: white;
|
||||||
|
padding: 0 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-content {
|
||||||
|
flex-grow: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-content: flex-start;
|
||||||
|
|
||||||
|
&-item {
|
||||||
|
height: calc(100% / 3);
|
||||||
|
width: calc(100% / 3);
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
&-card {
|
||||||
|
width: 516px;
|
||||||
|
height: 290px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
position: relative;
|
||||||
|
background-color: #101317;
|
||||||
|
border: 1px solid #EBEBEB;
|
||||||
|
|
||||||
|
&-name {
|
||||||
|
background-color: rgb(253, 194, 41);
|
||||||
|
color: black;
|
||||||
|
font-size: 26px;
|
||||||
|
position: absolute;
|
||||||
|
left: 4px;
|
||||||
|
bottom: 4px;
|
||||||
|
padding: 0px 10px;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
import { RouteRecordRaw, createRouter, createWebHashHistory } from 'vue-router'
|
||||||
|
|
||||||
|
const routes: Array<RouteRecordRaw> = [
|
||||||
|
{
|
||||||
|
path: '/',
|
||||||
|
redirect: '/home' //重定向首页
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/home',
|
||||||
|
name: 'Home',
|
||||||
|
component: () => import("@/pages/Home/index.vue"), //首页
|
||||||
|
meta: {
|
||||||
|
title: '首页',
|
||||||
|
hasMenu: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
const router = createRouter({
|
||||||
|
history: createWebHashHistory(),
|
||||||
|
routes,
|
||||||
|
})
|
||||||
|
|
||||||
|
export default router
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
/* eslint-disable */
|
||||||
|
declare module '*.vue' {
|
||||||
|
const component: DefineComponent<{}, {}, any>
|
||||||
|
export default component
|
||||||
|
}
|
||||||
|
declare module 'js-md5'
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
import { createPinia } from 'pinia'
|
||||||
|
|
||||||
|
const Store = createPinia()
|
||||||
|
|
||||||
|
export default Store
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
import { defineStore } from 'pinia'
|
||||||
|
|
||||||
|
export const userStore = defineStore('USER', {
|
||||||
|
state: () => ({
|
||||||
|
isLogin: false,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
@import './public.scss';
|
||||||
|
@import './element.scss';
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
html,
|
||||||
|
body,
|
||||||
|
h1,
|
||||||
|
h2,
|
||||||
|
h3,
|
||||||
|
h4,
|
||||||
|
h5,
|
||||||
|
h6 {
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
html,
|
||||||
|
body,
|
||||||
|
#app {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
em {
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
import storage from './package/storage'
|
||||||
|
import request from './request'
|
||||||
|
|
||||||
|
export { storage, request }
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
import AgoraRTC, { IAgoraRTCClient, IAgoraRTCRemoteUser } from "agora-rtc-sdk-ng"
|
||||||
|
let client: IAgoraRTCClient;
|
||||||
|
type optionsType = {
|
||||||
|
appId: string;
|
||||||
|
channel: string;
|
||||||
|
token: string;
|
||||||
|
uid: number;
|
||||||
|
};
|
||||||
|
const options: optionsType = {
|
||||||
|
appId: "",
|
||||||
|
channel: "",
|
||||||
|
token: "",
|
||||||
|
uid: 0,
|
||||||
|
};
|
||||||
|
export const agora = {
|
||||||
|
// 初始化
|
||||||
|
init: async () => {
|
||||||
|
client = AgoraRTC.createClient({ mode: "live", codec: "vp8" });
|
||||||
|
},
|
||||||
|
// 设置参数
|
||||||
|
setOptions: <K extends keyof optionsType>(key: K, value: optionsType[K]) => {
|
||||||
|
options[key] = value;
|
||||||
|
},
|
||||||
|
// 加入频道
|
||||||
|
join: async () => {
|
||||||
|
await client.join(
|
||||||
|
options.appId,
|
||||||
|
options.channel,
|
||||||
|
options.token,
|
||||||
|
options.uid
|
||||||
|
);
|
||||||
|
},
|
||||||
|
// 离开频道
|
||||||
|
leave: async () => {
|
||||||
|
await client.leave();
|
||||||
|
},
|
||||||
|
// 监听
|
||||||
|
on: ({ userPublished }: any) => {
|
||||||
|
client.on("user-published", async (user: IAgoraRTCRemoteUser, mediaType: 'video' | 'audio') => {
|
||||||
|
await client.subscribe(user, mediaType)
|
||||||
|
userPublished(user, mediaType)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
class LocalStorage {
|
||||||
|
private constructor() {}
|
||||||
|
|
||||||
|
private static instance: LocalStorage | null = null
|
||||||
|
|
||||||
|
static getInstance() {
|
||||||
|
if (LocalStorage.instance === null) {
|
||||||
|
LocalStorage.instance = new LocalStorage()
|
||||||
|
}
|
||||||
|
return LocalStorage.instance
|
||||||
|
}
|
||||||
|
|
||||||
|
setItem(key: string, value: any) {
|
||||||
|
localStorage.setItem(key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
getItem(key: string) {
|
||||||
|
return localStorage.getItem(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
removeItem(key: string) {
|
||||||
|
localStorage.removeItem(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
removeAll() {
|
||||||
|
localStorage.clear()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default LocalStorage.getInstance()
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
import { AxiosRequestConfig, AxiosResponse } from 'axios'
|
||||||
|
import Request from './request'
|
||||||
|
import { constant } from '@/config'
|
||||||
|
|
||||||
|
// 实例化
|
||||||
|
const req = new Request({
|
||||||
|
baseURL: import.meta.env.VITE_BASE_URL_API,
|
||||||
|
timeout: constant.CONFIG_REQUEST_TIMEOUT_TIME as number,
|
||||||
|
interceptors: {
|
||||||
|
// 请求拦截器
|
||||||
|
requestInterceptors: (config: AxiosRequestConfig) => config,
|
||||||
|
// 响应拦截器 <T = AxiosResponse>(result: T)
|
||||||
|
responseInterceptors: <T = AxiosResponse>(result: T) => result,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const request = (config: any) => {
|
||||||
|
const { method = 'GET' } = config
|
||||||
|
|
||||||
|
if (method === 'get' || method === 'GET') {
|
||||||
|
config.params = config.data
|
||||||
|
}
|
||||||
|
return req.request<any>(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default request
|
||||||
|
|
@ -0,0 +1,96 @@
|
||||||
|
import axios, { AxiosInstance, AxiosResponse } from 'axios'
|
||||||
|
import { RequestConfig, RequestInterceptors } from './types'
|
||||||
|
import { storage } from '@/utils'
|
||||||
|
import { constant } from '@/config'
|
||||||
|
import router from '@/router'
|
||||||
|
class Request {
|
||||||
|
// axios实例
|
||||||
|
instance: AxiosInstance
|
||||||
|
|
||||||
|
// 拦截器对象
|
||||||
|
interceptorsObj?: RequestInterceptors
|
||||||
|
|
||||||
|
constructor(config: RequestConfig) {
|
||||||
|
// 创建实例
|
||||||
|
this.instance = axios.create(config)
|
||||||
|
|
||||||
|
// 类请求拦截器
|
||||||
|
this.instance.interceptors.request.use(
|
||||||
|
(req: any) => {
|
||||||
|
const token = storage.getItem(constant.CONFIG_TOKEN)
|
||||||
|
console.log(token);
|
||||||
|
if (token) {
|
||||||
|
// 如果有token给请求头加上
|
||||||
|
req.headers.Authorization = `Bearer ${token}`
|
||||||
|
req.timeout = constant.CONFIG_REQUEST_TIMEOUT_TIME
|
||||||
|
}
|
||||||
|
return req
|
||||||
|
},
|
||||||
|
(err: any) => {
|
||||||
|
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// 类响应拦截器
|
||||||
|
this.instance.interceptors.response.use(
|
||||||
|
(res: AxiosResponse) => {
|
||||||
|
const { data: resData } = res
|
||||||
|
if (resData.code !== 200) {
|
||||||
|
ElMessage.error(resData.message)
|
||||||
|
}
|
||||||
|
return resData
|
||||||
|
},
|
||||||
|
(err: any) => {
|
||||||
|
// 根据自己业务/接口返回做相应调整
|
||||||
|
const { status } = err.response
|
||||||
|
|
||||||
|
switch (status) {
|
||||||
|
case 401:
|
||||||
|
// @ts-ignore
|
||||||
|
ElMessage.warning('登录信息已失效,请关闭窗口,重新打开!')
|
||||||
|
storage.removeItem('token')
|
||||||
|
storage.removeItem('user')
|
||||||
|
// router.push({ path: '/login' })
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
const { Code } = err.response.data
|
||||||
|
|
||||||
|
switch (Code) {
|
||||||
|
case 401:
|
||||||
|
// @ts-ignore
|
||||||
|
ElMessage.warning('登录信息已失效,请关闭窗口,重新打开!')
|
||||||
|
storage.removeItem('token')
|
||||||
|
storage.removeItem('user')
|
||||||
|
// router.push({ path: '/login' })
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
request<T>(config: RequestConfig): Promise<T> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
if (config.interceptors?.requestInterceptors) {
|
||||||
|
config = config.interceptors.requestInterceptors(config)
|
||||||
|
}
|
||||||
|
this.instance
|
||||||
|
.request<any, T>(config)
|
||||||
|
.then((res: T) => {
|
||||||
|
// 如果我们为单个响应设置拦截器,这里使用单个响应的拦截器
|
||||||
|
if (config.interceptors?.responseInterceptors) {
|
||||||
|
res = config.interceptors.responseInterceptors<T>(res)
|
||||||
|
}
|
||||||
|
resolve(res)
|
||||||
|
})
|
||||||
|
.catch((err: any) => {
|
||||||
|
reject(err)
|
||||||
|
}).finally(() => {
|
||||||
|
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Request
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
import { AxiosRequestConfig, AxiosResponse } from 'axios'
|
||||||
|
|
||||||
|
// 实例拦截器
|
||||||
|
export interface RequestInterceptors {
|
||||||
|
// 请求拦截
|
||||||
|
requestInterceptors?: (config: AxiosRequestConfig) => AxiosRequestConfig
|
||||||
|
requestInterceptorsCatch?: (err: any) => any
|
||||||
|
// 响应拦截
|
||||||
|
responseInterceptors?: <T = AxiosResponse>(config: T) => T
|
||||||
|
responseInterceptorsCatch?: (err: any) => any
|
||||||
|
}
|
||||||
|
|
||||||
|
// 自定义传入的参数
|
||||||
|
export interface RequestConfig extends AxiosRequestConfig {
|
||||||
|
interceptors?: RequestInterceptors
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface useRequestConfig<T> extends RequestConfig {
|
||||||
|
data?: T
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
/// <reference types="vite/client" />
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ESNext",
|
||||||
|
"useDefineForClassFields": true,
|
||||||
|
"module": "ESNext",
|
||||||
|
"lib": [
|
||||||
|
"ESNext",
|
||||||
|
"DOM",
|
||||||
|
"DOM.Iterable"
|
||||||
|
],
|
||||||
|
"skipLibCheck": true,
|
||||||
|
/* Bundler mode */
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"jsx": "preserve",
|
||||||
|
/* Linting */
|
||||||
|
"strict": true,
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"noUnusedParameters": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
"baseUrl": ".",
|
||||||
|
"paths": {
|
||||||
|
"@/*": [
|
||||||
|
"src/*"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"src/**/*.ts",
|
||||||
|
"src/**/*.d.ts",
|
||||||
|
"src/**/*.tsx",
|
||||||
|
"src/**/*.vue",
|
||||||
|
"./auto-imports.d.ts"
|
||||||
|
],
|
||||||
|
"references": [
|
||||||
|
{
|
||||||
|
"path": "./tsconfig.node.json"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"composite": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowSyntheticDefaultImports": true
|
||||||
|
},
|
||||||
|
"include": ["vite.config.ts"]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
import { resolve } from 'path'
|
||||||
|
import { defineConfig } from 'vite'
|
||||||
|
import vue from '@vitejs/plugin-vue'
|
||||||
|
import AutoImport from 'unplugin-auto-import/vite'
|
||||||
|
import Components from 'unplugin-vue-components/vite'
|
||||||
|
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
|
||||||
|
import Icons from 'unplugin-icons/vite'
|
||||||
|
import IconsResolver from 'unplugin-icons/resolver'
|
||||||
|
import Inspect from 'vite-plugin-inspect'
|
||||||
|
import pxtovw from 'postcss-px-to-viewport'
|
||||||
|
|
||||||
|
const loder_pxtovw = pxtovw({
|
||||||
|
// 这里是设计稿宽度 自己修改
|
||||||
|
viewportWidth: 1920,
|
||||||
|
viewportUnit: 'vw',
|
||||||
|
selectorBlackList: ['.prism-player'],
|
||||||
|
})
|
||||||
|
|
||||||
|
// https://vitejs.dev/config/
|
||||||
|
export default defineConfig(() => ({
|
||||||
|
css: {
|
||||||
|
postcss: {
|
||||||
|
plugins: [loder_pxtovw]
|
||||||
|
},
|
||||||
|
},
|
||||||
|
server: {
|
||||||
|
port: 8080,
|
||||||
|
host: '0.0.0.0',
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
alias: [
|
||||||
|
{
|
||||||
|
find: '@',
|
||||||
|
replacement: resolve(__dirname, 'src'),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
build: {
|
||||||
|
minify: 'terser',
|
||||||
|
terserOptions: {
|
||||||
|
compress: {
|
||||||
|
drop_console: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
vue(),
|
||||||
|
AutoImport({
|
||||||
|
resolvers: [
|
||||||
|
ElementPlusResolver(),
|
||||||
|
IconsResolver({
|
||||||
|
prefix: 'Icon',
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
Components({
|
||||||
|
resolvers: [
|
||||||
|
ElementPlusResolver(),
|
||||||
|
IconsResolver({
|
||||||
|
enabledCollections: ['ep'],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
Icons({
|
||||||
|
autoInstall: true,
|
||||||
|
}),
|
||||||
|
Inspect(),
|
||||||
|
],
|
||||||
|
}))
|
||||||