Fri May 19 08:49:00 UTC 2023 inscode

上级 ca207a2f
run = "npm i && npm run dev"
run = "npm i && cd vite-project && npm run dev"
[env]
PATH = "/root/${PROJECT_DIR}/.config/npm/node_global/bin:/root/${PROJECT_DIR}/node_modules/.bin:${PATH}"
......
console.log("欢迎来到 InsCode");
\ No newline at end of file
console.log("欢迎来到 InsCode");
/**
* 将 URL 中的查询参数转换成一个对象
* @param {string} url 完整的 URL
* @returns {object} 包含查询参数的对象
*/
export function param2Obj(url) {
// 从 URL 中取出查询参数部分,并进行 URL 解码
const search = decodeURIComponent(url.split('?')[1]).replace(/\+/g, ' ')
// 如果没有查询参数,返回一个空对象
if (!search) {
return {}
}
// 初始化一个空对象,用于存储查询参数
const obj = {}
// 将查询参数字符串按 & 符号分割成数组
const searchArr = search.split('&')
// 遍历查询参数数组
searchArr.forEach((v) => {
// 找到等号的位置
const index = v.indexOf('=')
// 如果找到了等号
if (index !== -1) {
// 取出参数名和参数值
const name = v.substring(0, index)
const val = v.substring(index + 1, v.length)
// 将参数名和参数值存入对象中
obj[name] = val
}
})
// 返回包含查询参数的对象
return obj
}
\ No newline at end of file
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
{
"recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"]
}
# 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.
## Recommended IDE Setup
- [VS Code](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin).
## Type Support For `.vue` Imports in TS
TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin) to make the TypeScript language service aware of `.vue` types.
If the standalone TypeScript plugin doesn't feel fast enough to you, Volar has also implemented a [Take Over Mode](https://github.com/johnsoncodehk/volar/discussions/471#discussioncomment-1361669) that is more performant. You can enable it by the following steps:
1. Disable the built-in TypeScript Extension
1. Run `Extensions: Show Built-in Extensions` from VSCode's command palette
2. Find `TypeScript and JavaScript Language Features`, right click and select `Disable (Workspace)`
2. Reload the VSCode window by running `Developer: Reload Window` from the command palette.
<!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>Vite + Vue + TS</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
{
"name": "vite-project",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vue-tsc && vite build",
"preview": "vite preview"
},
"dependencies": {
"@element-plus/icons-vue": "^2.1.0",
"@vueuse/core": "^9.10.0",
"axios": "^0.27.2",
"element-plus": "^2.2.14",
"pinia": "^2.0.20",
"pinia-plugin-persistedstate": "^3.1.0",
"vue-router": "^4.1.3",
"vue": "^3.2.37"
},
"devDependencies": {
"@vitejs/plugin-vue": "^4.1.0",
"typescript": "^5.0.2",
"vite": "^4.3.2",
"vue-tsc": "^1.4.2"
}
}
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
\ No newline at end of file
<template>
<el-config-provider :locale="zhCn">
<router-view />
</el-config-provider>
</template>
<script setup lang="ts">
import { ElConfigProvider } from 'element-plus'
import zhCn from 'element-plus/es/locale/lang/zh-cn'
</script>
\ No newline at end of file
import request from '~/utils/http/axios'
export function login(data: {}) {
return request({
url: '/login',
method: 'post',
data
})
}
export function fetchUser(data: {}) {
return request({
url: '/api/getUser',
method: 'get',
data
})
}
\ No newline at end of file
@import './variables.scss';
.tags-li {
&.active {
background-color: $primary-color;
color: #fff;
.tags-li-title {
color: #fff;
}
}
}
\ No newline at end of file
html.dark {
.main-content {
background-color: black;
}
.tags {
background-color: black;
}
}
\ No newline at end of file
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
}
\ No newline at end of file
@forward 'element-plus/theme-chalk/src/common/var.scss' with ($colors: ('primary': ('base': #0052d9),
'success': ('base': #2ba471),
'danger': ('base': #d54941)),
$menu: (('item-height': 50px)));
// 如果只是按需导入,则可以忽略以下内容。
// 如果你想导入所有样式:
@use 'element-plus/theme-chalk/src/index.scss' as *;
\ No newline at end of file
[class*=" el-icon-lx"],
[class^=el-icon-lx] {
font-family: lx-iconfont !important;
}
\ No newline at end of file
@use './color-light.scss';
@use './icon.css';
@use './main.scss';
@use './element/element.scss';
@use './dark/dark.scss';
@tailwind base;
@tailwind components;
@tailwind utilities;
\ No newline at end of file
@import './variables.scss';
* {
margin: 0;
padding: 0;
}
body {
font-family: 'PingFang SC', 'Helvetica Neue', Helvetica, 'microsoft yahei', arial, STHeiTi,
sans-serif;
font-size: 14px;
svg {
display: inline-block;
}
a {
color: var(--el-color-primary)
}
a:hover {
text-decoration: underline;
}
}
.main-content {
position: relative;
margin-left: 200px;
padding-top: 90px;
min-height: calc(100vh - 60px);
-webkit-transition: left 0.3s ease-in-out;
transition: margin-left 0.3s ease-in-out;
border-left: 1px solid var(ç);
background: url(../images/bg.avif) no-repeat top center;
&.content-collapse {
margin-left: 64px;
}
}
.content {
width: auto;
overflow-y: auto;
&.content-collapse {
left: 64px;
}
}
.container {
padding: 16px;
}
.move-enter-active {
animation: run-scale .1s ease-out 0s;
}
.move-leave-active {
animation: run-scale .1s ease-in 0s reverse;
}
.move-enter-from,
.move-leave-to {
opacity: 0;
}
@keyframes run-scale {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
/*BaseForm*/
.el-time-panel__content::after,
.el-time-panel__content::before {
margin-top: -7px;
}
.el-time-spinner__wrapper .el-scrollbar__wrap:not(.el-scrollbar__wrap--hidden-default) {
padding-bottom: 0;
}
[class*=' el-icon-'],
[class^='el-icon-'] {
speak: none;
font-style: normal;
font-weight: 400;
font-variant: normal;
text-transform: none;
line-height: 1;
vertical-align: baseline;
display: inline-block;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
[hidden] {
display: none !important;
}
\ No newline at end of file
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="37.07" height="36" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 198"><path fill="#41B883" d="M204.8 0H256L128 220.8L0 0h97.92L128 51.2L157.44 0h47.36Z"></path><path fill="#41B883" d="m0 0l128 220.8L256 0h-51.2L128 132.48L50.56 0H0Z"></path><path fill="#35495E" d="M50.56 0L128 133.12L204.8 0h-47.36L128 51.2L97.92 0H50.56Z"></path></svg>
\ No newline at end of file
<script setup lang="ts">
import { ref } from 'vue'
defineProps<{ msg: string }>()
const count = ref(0)
</script>
<template>
<h1>{{ msg }}</h1>
<div class="card">
<button type="button" @click="count++">count is {{ count }}</button>
<p>
Edit
<code>components/HelloWorld.vue</code> to test HMR
</p>
</div>
<p>
Check out
<a href="https://vuejs.org/guide/quick-start.html#local" target="_blank"
>create-vue</a
>, the official Vue + Vite starter
</p>
<p>
Install
<a href="https://github.com/vuejs/language-tools" target="_blank">Volar</a>
in your IDE for a better DX
</p>
<p class="read-the-docs">Click on the Vite and Vue logos to learn more</p>
</template>
<style scoped>
.read-the-docs {
color: #888;
}
</style>
import { DirectiveBinding } from 'vue'
interface ExHTMLElement extends HTMLElement {
resizeListener: EventListener
}
export default {
mounted: (el: ExHTMLElement, binding: DirectiveBinding) => {
el.resizeListener = () => {
setHeight(el, binding)
}
setHeight(el, binding)
window.addEventListener('resize', el.resizeListener)
},
unmounted(el: ExHTMLElement) {
window.removeEventListener('resize', el.resizeListener)
},
updated(el: ExHTMLElement, binding: DirectiveBinding) {
setHeight(el, binding)
}
}
// set el-table height
function setHeight(el: ExHTMLElement, binding: DirectiveBinding) {
const top = el.offsetTop
const bottom = binding?.value?.bottom || 64
const pageHeight = window.innerHeight
console.log(top, bottom, pageHeight)
el.style.height = pageHeight - top - bottom + 'px'
el.style.overflowY = 'auto'
}
\ No newline at end of file
import { DirectiveBinding } from 'vue'
export default {
mounted(el: HTMLElement, binding: DirectiveBinding) {
el.style.color = binding.value
}
}
\ No newline at end of file
import { App } from 'vue'
import color from './color'
import watermark from './watermark'
import adaptive from './adaptive'
import permiss from './permiss'
export default (app: App) => {
app.directive('color', color)
app.directive('watermark', watermark)
app.directive('adaptive', adaptive)
app.directive('permiss', permiss)
}
\ No newline at end of file
import { DirectiveBinding } from 'vue'
export default {
mounted(el: HTMLElement, binding: DirectiveBinding) { }
}
\ No newline at end of file
import { DirectiveBinding } from 'vue'
// 水印
export default {
mounted(el: HTMLElement, binding: DirectiveBinding) {
let txt = 'hello world'
let style = {}
if (binding && binding.value) {
txt = binding.value.txt
style = binding.value.style
}
genWatermark(el, txt, style)
}
}
interface CanvasTextStyle {
font?: string
color?: string
}
function genWatermark(el: HTMLElement, txt: string, style?: CanvasTextStyle) {
const defaultStyle = {
font: '14px arial',
color: 'rgba(0,0,0,0.2)'
}
let canvas = <HTMLCanvasElement>document.createElement('canvas')
let ctx = <CanvasRenderingContext2D>canvas.getContext('2d')
ctx.translate(150, 75)
ctx.rotate((Math.PI / 180) * 25)
ctx.translate(-150, -75)
ctx.font = style?.font || defaultStyle.font
ctx.fillStyle = style?.color || defaultStyle.color
ctx.textAlign = 'center'
ctx.textBaseline = 'middle'
ctx.fillText(txt, canvas.width / 2, canvas.height / 2)
el.style.backgroundImage = `url(${canvas.toDataURL('image/png')})`
}
\ No newline at end of file
<template>
<div class="v-header backdrop-blur-sm bg-white/75 dark:bg-black/75">
<div class="mx-4 text-lg logo">mocha Vue3 Admin</div>
<!-- 折叠按钮 -->
<div class="inline-flex items-center h-full text-xl" @click="collapseChage">
<MoIcon icon-name="ep-expand" class="text-sm" v-if="sidebar.collapse" />
<MoIcon icon-name="ep-fold" class="text-sm" v-else />
</div>
<div class="flex items-center pr-4 ml-auto">
<SiteSearch />
<el-switch
v-model="darkMode"
inline-prompt
:active-icon="Moon"
:inactive-icon="Sunny"
class="mx-2"
@change="toggleDark()"
style="--el-switch-on-color: #0960bd; --el-switch-off-color: #ff6600"
/>
<el-select v-model="tenantId" class="mr-4" placeholder="Select">
<el-option
v-for="item in tenantOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
<div class="flex items-center">
<!-- 消息中心 -->
<!-- <div class="w-8 cursor-pointer" @click="router.push('/system/messageCenter')">
<el-badge :value="message" class="item" :hidden="!message">
<i class="text-xl el-icon-lx-notice"></i>
</el-badge>
</div> -->
<Message />
<!-- 用户头像 -->
<el-avatar class="ml-2" :size="30" :src="imgurl" />
<!-- 用户名下拉菜单 -->
<el-dropdown class="ml-2" trigger="click" @command="handleCommand">
<span class="flex items-center cursor-pointer">
{{ username }}
<el-icon class="el-icon--right">
<MoIcon icon-name="ep-arrow-down" />
</el-icon>
</span>
<template #dropdown>
<el-dropdown-menu>
<a href="https://github.com/lucidity99/mocha-vue3-system" target="_blank">
<el-dropdown-item>项目仓库</el-dropdown-item>
</a>
<el-dropdown-item command="user">个人中心</el-dropdown-item>
<el-dropdown-item divided command="loginout">退出登录</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</div>
<el-dialog></el-dialog>
</div>
</template>
<script setup lang="ts">
import { onMounted, reactive, ref } from 'vue'
import { useSidebarStore } from '~/store/sidebar'
import { useRouter } from 'vue-router'
import { SelectOptionItem } from '~/types/index'
import { useDark, useToggle } from '@vueuse/core'
import { Sunny, Moon } from '@element-plus/icons-vue'
import imgurl from '~/assets/images/img.jpg'
import { useUserStore } from '~/store/user'
import SiteSearch from './SiteSearch.vue'
import Message from './Message.vue'
const isDark = useDark()
let darkMode = ref(false)
darkMode.value = isDark.value
const toggleDark = useToggle(isDark)
const userStore = useUserStore()
const tenantId = ref('mocha')
const tenantOptions: Array<SelectOptionItem> = reactive([{ value: 'mocha', label: 'mocha' }])
const username: string | null = userStore.username
const sidebar = useSidebarStore()
// 侧边栏折叠
const collapseChage = () => {
sidebar.handleCollapse()
}
onMounted(() => {
if (document.body.clientWidth < 1200) {
collapseChage()
}
})
// 用户名下拉菜单选择事件
const router = useRouter()
const handleCommand = (command: string) => {
if (command == 'loginout') {
userStore.userLogout()
router.push('/login')
} else if (command == 'user') {
router.push('/system/profile')
}
}
</script>
<style scoped lang="scss">
.v-header {
position: fixed;
z-index: 10;
display: flex;
align-items: center;
width: 100%;
height: 60px;
border-bottom: 1px solid var(--el-border-color);
.logo {
color: var(--el-color-primary);
}
}
</style>
\ No newline at end of file
import { defineStore } from 'pinia'
import { ref } from 'vue'
import { login } from '~/api/user'
import { UserInfo } from '~/types/index'
export const useUserStore = defineStore(
'user',
() => {
let username = ref('')
let userPermiss = ref([])
// 用户登录
async function userLogin(param: UserInfo) {
const { username: name, permiss } = await login({
username: param.username,
password: param.password
})
console.log(permiss, name)
if (name) {
username.value = name
userPermiss.value = permiss
return true
}
return false
}
// 退出登录
function userLogout() {
username.value = ''
userPermiss.value = []
}
return { username, userPermiss, userLogin, userLogout }
},
{
persist: true
}
)
\ No newline at end of file
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
const pinia = createPinia()
pinia.use(piniaPluginPersistedstate)
import App from './App.vue'
import router from './router'
import './assets/css/index.scss'
import './assets/css/element/index.scss'
import 'element-plus/theme-chalk/dark/css-vars.css'
const app = createApp(App)
app.use(pinia)
app.use(router)
// 在 main.ts文件中设置svg-icon为全局组件
import svgIcon from '~/components/svgIcon/index.vue'
app.component('svg-icon', svgIcon)
import 'iconify-icon'
// 注册指令
import directive from './directive'
directive(app)
// 引入VForm 设计器需全局引入Element Plus
// import ElementPlus from 'element-plus' //引入element-plus库
// import 'element-plus/dist/index.css' //引入element-plus样式
// // 引入并全局注册VForm 3组件
// import VForm3 from 'vform3-builds' //引入VForm 3库
// import 'vform3-builds/dist/designer.style.css' //引入VForm3样式
// app.use(ElementPlus) //全局注册element-plus
// app.use(VForm3)
app.mount('#app')
\ No newline at end of file
import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router'
import { useUserStore } from '~/store/user'
import Home from '~/layout/index.vue'
const modules = import.meta.glob('./modules/**/*.ts', { eager: true })
const routeModuleList: RouteRecordRaw[] = []
Object.values(modules).forEach((key: any) => {
const mod = key.default || []
const modList = Array.isArray(mod) ? [...mod] : [mod]
routeModuleList.push(...modList)
routeModuleList.sort((a, b) => {
const orderA = a?.meta?.order || 0
const orderB = b?.meta?.order || 0
return orderA - orderB
})
})
const routes: RouteRecordRaw[] = [
{
path: '/',
name: 'Home',
component: Home,
redirect: '/dashboard/workbench',
children: [...routeModuleList]
},
{
path: '/login',
name: 'Login',
meta: {
title: '登录'
},
component: () => import(/* webpackChunkName: "login" */ '../views/login/login.vue')
},
{
path: '/403',
name: '403',
meta: {
title: '没有权限'
},
component: () => import(/* webpackChunkName: "403" */ '../views/403.vue')
}
]
const router = createRouter({
history: createWebHashHistory(),
routes
})
router.beforeEach((to, from, next) => {
const userStore = useUserStore()
document.title = `${to.meta.title} | mocha vue3 admin`
const role = userStore.username
if (!role && to.path !== '/login') {
next('/login')
} else if (to.meta.permiss && !userStore.userPermiss.includes(to.meta.permiss)) {
// 如果没有权限,则进入403
next('/403')
} else {
next()
}
})
export default router
\ No newline at end of file
:root {
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
line-height: 1.5;
font-weight: 400;
color-scheme: light dark;
color: rgba(255, 255, 255, 0.87);
background-color: #242424;
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
-webkit-text-size-adjust: 100%;
}
a {
font-weight: 500;
color: #646cff;
text-decoration: inherit;
}
a:hover {
color: #535bf2;
}
body {
margin: 0;
display: flex;
place-items: center;
min-width: 320px;
min-height: 100vh;
}
h1 {
font-size: 3.2em;
line-height: 1.1;
}
button {
border-radius: 8px;
border: 1px solid transparent;
padding: 0.6em 1.2em;
font-size: 1em;
font-weight: 500;
font-family: inherit;
background-color: #1a1a1a;
cursor: pointer;
transition: border-color 0.25s;
}
button:hover {
border-color: #646cff;
}
button:focus,
button:focus-visible {
outline: 4px auto -webkit-focus-ring-color;
}
.card {
padding: 2em;
}
#app {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
text-align: center;
}
@media (prefers-color-scheme: light) {
:root {
color: #213547;
background-color: #ffffff;
}
a:hover {
color: #747bff;
}
button {
background-color: #f9f9f9;
}
}
export interface SelectOptionItem {
value: string
label: string
}
export interface UserInfo {
username: string
password?: string
}
\ No newline at end of file
import axios, { AxiosInstance, AxiosError, AxiosResponse, AxiosRequestConfig } from 'axios'
import { errorHandler, errorMsgHandler } from './errorHandler'
import qs from 'qs'
declare module 'axios' {
export interface AxiosRequestConfig {
isReturnNativeData?: boolean
errorMode?: string
repeatRequest?: boolean
}
}
let pendingMap = new Map()
function getRequestKey(config: AxiosRequestConfig) {
return (config.method || '') + config.url + '?' + qs.stringify(config?.data)
}
function setPendingMap(config: AxiosRequestConfig) {
const controller = new AbortController()
config.signal = controller.signal
const key = getRequestKey(config)
if (pendingMap.has(key)) {
pendingMap.get(key).abort()
pendingMap.delete(key)
} else {
pendingMap.set(key, controller)
}
}
const service: AxiosInstance = axios.create({
timeout: 1000 * 30,
baseURL: import.meta.env.VITE_BASE_URL
})
service.interceptors.request.use(
(config) => {
if (!config.repeatRequest) {
setPendingMap(config)
}
return config
},
(error: AxiosError) => {
console.log(error)
return Promise.reject()
}
)
service.interceptors.response.use((response: AxiosResponse) => {
const config = response.config
const key = getRequestKey(config)
pendingMap.delete(key)
if (response.status === 200) {
if (config?.isReturnNativeData) {
return response.data
} else {
const { result, code, message } = response.data
if (code === 200) {
return result
} else {
errorHandler(message || errorMsgHandler(code), config.errorMode)
}
}
} else {
const errMsg = errorMsgHandler(response.status)
errorHandler(errMsg, config.errorMode)
Promise.reject()
}
})
// 错误处理
service.interceptors.response.use(undefined, (e) => {
errorHandler(e.response.status)
})
export default service
\ No newline at end of file
import { ElMessage, ElNotification } from 'element-plus'
// 根据错误代码,获取对应文字
const errorMsgHandler = (errStatus: number): string => {
if (errStatus === 500) return '服务器内部错误'
if ((errStatus = 400)) return '没有权限'
return '未知错误'
}
// 根据mode,返回错误信息
const errorHandler = (errMsg: string, mode: string = 'modal') => {
const msg = errMsg || '未知错误'
if (mode === 'modal') {
ElMessage(msg + '|' + mode)
}
if (mode === 'toast') {
ElNotification({
title: 'Error',
message: msg,
type: 'error'
})
}
if (mode === 'hiden') {
}
}
export { errorHandler, errorMsgHandler }
\ No newline at end of file
/**
* 将 URL 中的查询参数转换成一个对象
* @param {string} url 完整的 URL
* @returns {object} 包含查询参数的对象
*/
export function param2Obj(url) {
// 从 URL 中取出查询参数部分,并进行 URL 解码
const search = decodeURIComponent(url.split('?')[1]).replace(/\+/g, ' ')
// 如果没有查询参数,返回一个空对象
if (!search) {
return {}
}
// 初始化一个空对象,用于存储查询参数
const obj = {}
// 将查询参数字符串按 & 符号分割成数组
const searchArr = search.split('&')
// 遍历查询参数数组
searchArr.forEach((v) => {
// 找到等号的位置
const index = v.indexOf('=')
// 如果找到了等号
if (index !== -1) {
// 取出参数名和参数值
const name = v.substring(0, index)
const val = v.substring(index + 1, v.length)
// 将参数名和参数值存入对象中
obj[name] = val
}
})
// 返回包含查询参数的对象
return obj
}
\ No newline at end of file
/// <reference types="vite/client" />
{
"compilerOptions": {
"target": "ESNext",
"useDefineForClassFields": true,
"module": "ESNext",
"moduleResolution": "Node",
"strict": true,
"jsx": "preserve",
"sourceMap": true,
"resolveJsonModule": true,
"isolatedModules": true,
"esModuleInterop": true,
"lib": ["ESNext", "DOM"],
"skipLibCheck": true,
"baseUrl": ".",
"paths": { "~/*": ["src/*"] }
},
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.vue"],
"references": [{ "path": "./tsconfig.node.json" }]
}
\ No newline at end of file
{
"compilerOptions": {
"composite": true,
"skipLibCheck": true,
"module": "ESNext",
"moduleResolution": "node",
"allowSyntheticDefaultImports": true
},
"include": ["vite.config.ts"]
}
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
})
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册