提交 09ed5c22 authored 作者: yanyalin's avatar yanyalin

页面调整

上级 96121ad7
{
"globals": {
"Component": true,
"ComponentPublicInstance": true,
"ComputedRef": true,
"EffectScope": true,
"ExtractDefaultPropTypes": true,
"ExtractPropTypes": true,
"ExtractPublicPropTypes": true,
"InjectionKey": true,
"PropType": true,
"Ref": true,
"VNode": true,
"WritableComputedRef": true,
"acceptHMRUpdate": true,
"computed": true,
"createApp": true,
"createPinia": true,
"customRef": true,
"defineAsyncComponent": true,
"defineComponent": true,
"defineStore": true,
"effectScope": true,
"getActivePinia": true,
"getCurrentInstance": true,
"getCurrentScope": true,
"h": true,
"inject": true,
"isProxy": true,
"isReactive": true,
"isReadonly": true,
"isRef": true,
"mapActions": true,
"mapGetters": true,
"mapState": true,
"mapStores": true,
"mapWritableState": true,
"markRaw": true,
"nextTick": true,
"onActivated": true,
"onBeforeMount": true,
"onBeforeRouteLeave": true,
"onBeforeRouteUpdate": true,
"onBeforeUnmount": true,
"onBeforeUpdate": true,
"onDeactivated": true,
"onErrorCaptured": true,
"onMounted": true,
"onRenderTracked": true,
"onRenderTriggered": true,
"onScopeDispose": true,
"onServerPrefetch": true,
"onUnmounted": true,
"onUpdated": true,
"provide": true,
"reactive": true,
"readonly": true,
"ref": true,
"resolveComponent": true,
"setActivePinia": true,
"setMapStoreSuffix": true,
"shallowReactive": true,
"shallowReadonly": true,
"shallowRef": true,
"storeToRefs": true,
"toRaw": true,
"toRef": true,
"toRefs": true,
"toValue": true,
"triggerRef": true,
"unref": true,
"useAttrs": true,
"useCssModule": true,
"useCssVars": true,
"useLink": true,
"useMouse": true,
"useMyFetch": true,
"useRoute": true,
"useRouter": true,
"useSlots": true,
"watch": true,
"watchEffect": true,
"watchPostEffect": true,
"watchSyncEffect": true
}
}
...@@ -7,7 +7,8 @@ module.exports = { ...@@ -7,7 +7,8 @@ module.exports = {
'plugin:vue/vue3-essential', 'plugin:vue/vue3-essential',
'eslint:recommended', 'eslint:recommended',
'@vue/eslint-config-typescript', '@vue/eslint-config-typescript',
'@vue/eslint-config-prettier/skip-formatting' '@vue/eslint-config-prettier/skip-formatting',
"./.eslintrc-auto-import.json",
], ],
parserOptions: { parserOptions: {
ecmaVersion: 'latest' ecmaVersion: 'latest'
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
"@logicflow/core": "~1.2.28", "@logicflow/core": "~1.2.28",
"@logicflow/extension": "~1.2.28", "@logicflow/extension": "~1.2.28",
"@vueuse/core": "^11.0.3", "@vueuse/core": "^11.0.3",
"ant-design-vue": "^4.2.3",
"axios": "^1.7.7", "axios": "^1.7.7",
"codemirror": "^6.0.1", "codemirror": "^6.0.1",
"cropperjs": "^1.6.2", "cropperjs": "^1.6.2",
...@@ -37,6 +38,7 @@ ...@@ -37,6 +38,7 @@
"nprogress": "^0.2.0", "nprogress": "^0.2.0",
"pinia": "^2.1.6", "pinia": "^2.1.6",
"pinyin-pro": "^3.18.2", "pinyin-pro": "^3.18.2",
"qs": "^6.13.0",
"screenfull": "^6.0.2", "screenfull": "^6.0.2",
"use-element-plus-theme": "^0.0.5", "use-element-plus-theme": "^0.0.5",
"vue": "^3.5.4", "vue": "^3.5.4",
...@@ -53,17 +55,24 @@ ...@@ -53,17 +55,24 @@
"@types/lodash": "^4.17.7", "@types/lodash": "^4.17.7",
"@types/node": "^18.17.5", "@types/node": "^18.17.5",
"@types/nprogress": "^0.2.0", "@types/nprogress": "^0.2.0",
"@vitejs/plugin-vue": "^4.3.1", "@types/qs": "^6.9.15",
"@vitejs/plugin-vue": "^5.1.3",
"@vitejs/plugin-vue-jsx": "^4.0.1",
"@vue/eslint-config-prettier": "^8.0.0", "@vue/eslint-config-prettier": "^8.0.0",
"@vue/eslint-config-typescript": "^11.0.3", "@vue/eslint-config-typescript": "^11.0.3",
"@vue/test-utils": "^2.4.1", "@vue/test-utils": "^2.4.1",
"@vue/tsconfig": "^0.4.0", "@vue/tsconfig": "^0.4.0",
"autoprefixer": "^10.4.20",
"eslint": "~8.57.0", "eslint": "~8.57.0",
"eslint-plugin-vue": "^9.16.1", "eslint-plugin-vue": "^9.16.1",
"jsdom": "^22.1.0", "jsdom": "^22.1.0",
"postcss": "^8.4.45",
"prettier": "^3.0.0", "prettier": "^3.0.0",
"sass": "^1.66.1", "sass": "^1.66.1",
"tailwindcss": "^3.4.11",
"typescript": "~5.6.2", "typescript": "~5.6.2",
"unplugin-auto-import": "^0.18.3",
"unplugin-vue-components": "^0.27.4",
"vite": "^5.4.4", "vite": "^5.4.4",
"vite-plugin-vue-devtools": "^7.4.5", "vite-plugin-vue-devtools": "^7.4.5",
"vitest": "^0.34.2", "vitest": "^0.34.2",
......
差异被折叠。
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
<template> <template>
<router-view /> <router-view />
</template> </template>
\ No newline at end of file <script setup lang="ts">
import { useFavicon, useTitle } from "@vueuse/core";
import { getConfigInfo } from '@/utils/api'
const title = useTitle();
getConfigInfo("PRODUCT-NAME").then(res => {
title.value = res;
});
const icon = useFavicon();
getConfigInfo("ICON").then(res => {
icon.value = "data:image/x-icon;base64," + res;
});
const logoImage = ref<string>("");
getConfigInfo("LOGO").then(res => {
logoImage.value = "data:image/jpg;base64," + res;
});
provide("logoImage", logoImage);
</script>
<style>
@tailwind base;
@tailwind components;
@tailwind utilities;
</style>
\ No newline at end of file
...@@ -80,15 +80,15 @@ interface ResetCurrentUserPasswordRequest { ...@@ -80,15 +80,15 @@ interface ResetCurrentUserPasswordRequest {
/** /**
* 验证码 * 验证码
*/ */
code: string password: string
/** /**
*密码 *密码
*/ */
password: string new_password: string
/** /**
* 确认密码 * 确认密码
*/ */
re_password: string repeat_new_password: string
} }
interface ResetPasswordRequest { interface ResetPasswordRequest {
......
<template> <template>
<div class="login-form-container"> <div class="login-form-container">
<div class="login-title"> <div class="text-[28px] text-center mb-8">
<div class="logo text-center"> {{ subTitle }}
<LogoFull height="45px" />
</div>
<div class="sub-title text-center" v-if="subTitle">
<el-text type="info">{{ subTitle }}</el-text>
</div>
</div> </div>
<el-card class="login-card"> <el-card class="login-card">
<slot></slot> <slot></slot>
...@@ -18,18 +13,11 @@ defineOptions({ name: 'LoginContainer' }) ...@@ -18,18 +13,11 @@ defineOptions({ name: 'LoginContainer' })
defineProps({ defineProps({
title: String, title: String,
subTitle: String subTitle: String
}) });
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.login-form-container { .login-form-container {
width: 480px; width: 480px;
.login-title {
margin-bottom: 32px;
.sub-title {
font-size: 16px;
}
}
.login-card { .login-card {
border-radius: 8px; border-radius: 8px;
padding: 18px; padding: 18px;
......
<template> <template>
<div class="login-warp flex-center"> <div class="login-warp flex-center">
<div class="login-container w-full h-full"> <div class="login-container w-full h-full">
<el-row class="container w-full h-full"> <el-row class="w-full h-full">
<el-col :xs="0" :sm="0" :md="10" :lg="10" :xl="10" class="left-container"> <el-col :xs="0" :sm="0" :md="10" :lg="10" :xl="14" class="left-container">
<div class="login-image" :style="{ backgroundImage: `url(${loginImage})` }"></div> <div class="login-image"><img class="w-full h-full block" src="@/assets/login.png" alt="" /></div>
</el-col> </el-col>
<el-col :xs="24" :sm="24" :md="14" :lg="14" :xl="14" class="right-container flex-center"> <el-col :xs="24" :sm="24" :md="14" :lg="14" :xl="10" class="right-container flex-center">
<slot></slot> <slot></slot>
</el-col> </el-col>
</el-row> </el-row>
...@@ -13,42 +13,13 @@ ...@@ -13,42 +13,13 @@
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed } from 'vue'
import { getThemeImg } from '@/utils/theme'
import useStore from '@/stores'
import { request } from '@/request'
defineOptions({ name: 'LoginLayout' }) defineOptions({ name: 'LoginLayout' })
const { user } = useStore()
const fileURL = computed(() => {
if (user.themeInfo?.loginImage) {
if (typeof user.themeInfo?.loginImage === 'string') {
return user.themeInfo?.loginImage
} else {
return URL.createObjectURL(user.themeInfo?.loginImage)
}
} else {
return ''
}
})
const loginImage = computed(() => {
if (user.themeInfo?.loginImage) {
return `${fileURL.value}`
} else {
return new URL(`../../assets/theme/${getThemeImg(user.themeInfo?.theme)}.jpg`, import.meta.url)
.href
}
})
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.login-warp { .login-warp {
height: 100vh; height: 100vh;
.login-image { .login-image {
background-repeat: no-repeat;
background-position: center;
background-size: cover;
width: 100%; width: 100%;
height: 100%; height: 100%;
} }
......
<template> <template>
<el-dialog v-model="resetPasswordDialog" :title="$t('layout.topbar.avatar.resetPassword')"> <el-dialog v-model="resetPasswordDialog" title="修改密码">
<el-form <el-form ref="resetPasswordFormRef" :model="resetPasswordForm" :rules="rules">
class="reset-password-form mb-24" <el-form-item prop="password">
ref="resetPasswordFormRef"
:model="resetPasswordForm"
:rules="rules"
>
<p class="mb-8 lighter">{{ $t("layout.topbar.avatar.dialog.newPassword") }}</p>
<el-form-item prop="password" style="margin-bottom: 8px">
<el-input <el-input
type="password" type="password"
class="input-item" class="input-item"
v-model="resetPasswordForm.password" v-model="resetPasswordForm.password"
:placeholder="$t('layout.topbar.avatar.dialog.enterPassword')" placeholder="请输入密码"
show-password show-password
> />
</el-input>
</el-form-item> </el-form-item>
<el-form-item prop="re_password"> <el-form-item prop="new_password">
<el-input <el-input
type="password" type="password"
class="input-item" class="input-item"
v-model="resetPasswordForm.re_password" v-model="resetPasswordForm.new_password"
:placeholder="$t('layout.topbar.avatar.dialog.confirmPassword')" placeholder="请输入密码"
show-password show-password
> >
</el-input> </el-input>
</el-form-item> </el-form-item>
<p class="mb-8 lighter">{{ $t("layout.topbar.avatar.dialog.useEmail") }}</p> <el-form-item prop="repeat_new_password">
<el-form-item style="margin-bottom: 8px">
<el-input <el-input
type="password"
class="input-item" class="input-item"
:disabled="true" v-model="resetPasswordForm.repeat_new_password"
v-bind:modelValue="user.userInfo?.email" placeholder="请输入确认密码"
:placeholder="$t('layout.topbar.avatar.dialog.enterEmail')" show-password
> >
</el-input> </el-input>
</el-form-item> </el-form-item>
<el-form-item prop="code">
<div class="flex-between w-full">
<el-input class="code-input" v-model="resetPasswordForm.code" :placeholder="$t('layout.topbar.avatar.dialog.enterVerificationCode')">
</el-input>
<el-button
:disabled="isDisabled"
class="send-email-button ml-8"
@click="sendEmail"
:loading="loading"
>
{{ isDisabled ? $t('layout.topbar.avatar.dialog.resend', { time }) : $t('layout.topbar.avatar.dialog.getVerificationCode') }}
</el-button>
</div>
</el-form-item>
</el-form> </el-form>
<template #footer> <template #footer>
<div class="dialog-footer"> <div class="dialog-footer">
<el-button @click="resetPasswordDialog = false">{{ $t('layout.topbar.avatar.dialog.cancel') }}</el-button> <el-button @click="resetPasswordDialog = false">取消</el-button>
<el-button type="primary" @click="resetPassword"> {{ $t('layout.topbar.avatar.dialog.save') }} </el-button> <el-button type="primary" @click="resetPassword">保存</el-button>
</div> </div>
</template> </template>
</el-dialog> </el-dialog>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue'
import type { ResetCurrentUserPasswordRequest } from '@/api/type/user' import type { ResetCurrentUserPasswordRequest } from '@/api/type/user'
import type { FormInstance, FormRules } from 'element-plus' import type { FormInstance, FormRules } from 'element-plus'
import { MsgSuccess } from '@/utils/message' import { editPassword } from "@/utils/api";
import UserApi from '@/api/user'
import useStore from '@/stores'
import { useRouter } from 'vue-router'
import { t } from '@/locales'
const router = useRouter() const router = useRouter()
const { user } = useStore()
const resetPasswordDialog = ref<boolean>(false) const resetPasswordDialog = ref<boolean>(false)
const resetPasswordForm = ref<ResetCurrentUserPasswordRequest>({ const resetPasswordForm = ref<ResetCurrentUserPasswordRequest>({
code: '',
password: '', password: '',
re_password: '' new_password: '',
repeat_new_password: ''
}) })
const resetPasswordFormRef = ref<FormInstance>() const resetPasswordFormRef = ref<FormInstance>()
const loading = ref<boolean>(false)
const isDisabled = ref<boolean>(false)
const time = ref<number>(60)
const rules = ref<FormRules<ResetCurrentUserPasswordRequest>>({ const rules = ref<FormRules<ResetCurrentUserPasswordRequest>>({
// @ts-ignore password: [{ required: true, message: "请输入旧密码", trigger: 'blur' }],
code: [{ required: true, message: t('layout.topbar.avatar.dialog.enterVerificationCode'), trigger: 'blur' }], new_password: [
password: [
{ {
required: true, required: true,
message: t('layout.topbar.avatar.dialog.enterPassword'), message: '请输入新密码',
trigger: 'blur' trigger: 'blur'
}, },
{ {
min: 6, min: 6,
max: 20, max: 20,
message: t('layout.topbar.avatar.dialog.passwordLength'), message: '长度在6~20',
trigger: 'blur' trigger: 'blur'
} }
], ],
re_password: [ repeat_new_password: [
{ {
required: true, required: true,
message: t('layout.topbar.avatar.dialog.confirmPassword'), message: '请确认新密码',
trigger: 'blur' trigger: 'blur'
}, },
{ {
min: 6, min: 6,
max: 20, max: 20,
message: t('layout.topbar.avatar.dialog.passwordLength'), message: '长度在6~20',
trigger: 'blur' trigger: 'blur'
}, },
{ {
validator: (rule, value, callback) => { validator: (rule, value, callback) => {
if (resetPasswordForm.value.password != resetPasswordForm.value.re_password) { if (resetPasswordForm.value.new_password != resetPasswordForm.value.repeat_new_password) {
callback(new Error(t('layout.topbar.avatar.dialog.passwordMismatch'))) callback(new Error('两次密码不一致'))
} else { } else {
callback() callback()
} }
...@@ -126,49 +95,24 @@ const rules = ref<FormRules<ResetCurrentUserPasswordRequest>>({ ...@@ -126,49 +95,24 @@ const rules = ref<FormRules<ResetCurrentUserPasswordRequest>>({
} }
] ]
}) })
/**
* 发送验证码
*/
const sendEmail = () => {
UserApi.sendEmailToCurrent(loading).then(() => {
MsgSuccess(t('verificationCodeSentSuccess'))
isDisabled.value = true
handleTimeChange()
})
}
const handleTimeChange = () => {
if (time.value <= 0) {
isDisabled.value = false
time.value = 60
} else {
setTimeout(() => {
time.value--
handleTimeChange()
}, 1000)
}
}
const open = () => { const open = () => {
resetPasswordForm.value = { resetPasswordForm.value = {
code: '',
password: '', password: '',
re_password: '' new_password: '',
repeat_new_password: ''
} }
resetPasswordDialog.value = true resetPasswordDialog.value = true
resetPasswordFormRef.value?.resetFields() resetPasswordFormRef.value?.resetFields()
} }
const resetPassword = () => { const resetPassword = () => {
resetPasswordFormRef.value resetPasswordFormRef.value?.validate().then(() => {
?.validate() editPassword(resetPasswordForm.value).then(res => {
.then(() => { if (res) {
return UserApi.resetCurrentUserPassword(resetPasswordForm.value) sessionStorage.clear();
}) router.push("/login")
.then(() => { }
return user.logout() })
})
.then(() => {
router.push({ name: 'login' })
}) })
} }
const close = () => { const close = () => {
...@@ -176,7 +120,4 @@ const close = () => { ...@@ -176,7 +120,4 @@ const close = () => {
} }
defineExpose({ open, close }) defineExpose({ open, close })
</script> </script>
<style lang="scss" scoped></style>
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
<AppAvatar> <AppAvatar>
<img src="@/assets/user-icon.svg" style="width: 54%" alt=""/> <img src="@/assets/user-icon.svg" style="width: 54%" alt=""/>
</AppAvatar> </AppAvatar>
<span class="ml-8">{{ user.userInfo?.username }}</span> <span class="ml-4">{{ user.name }}</span>
<el-icon class="el-icon--right"> <el-icon class="el-icon--right">
<CaretBottom/> <CaretBottom/>
</el-icon> </el-icon>
...@@ -12,88 +12,32 @@ ...@@ -12,88 +12,32 @@
<template #dropdown> <template #dropdown>
<el-dropdown-menu class="avatar-dropdown"> <el-dropdown-menu class="avatar-dropdown">
<div class="userInfo"> <el-dropdown-item @click="openResetPassword">
<p class="bold mb-4" style="font-size: 14px">{{ user.userInfo?.username }}</p> 重置密码
<p>
<el-text type="info">
{{ user.userInfo?.email }}
</el-text>
</p>
</div>
<el-dropdown-item class="border-t p-8" @click="openResetPassword">
{{ $t('layout.topbar.avatar.resetPassword') }}
</el-dropdown-item>
<div v-hasPermission="new ComplexPermission([], ['x-pack'], 'OR')">
<el-dropdown-item class="border-t p-8" @click="openAPIKeyDialog">
{{ $t('layout.topbar.avatar.apiKey') }}
</el-dropdown-item>
</div>
<el-dropdown-item class="border-t" @click="openAbout">
{{ $t('layout.topbar.avatar.about') }}
</el-dropdown-item> </el-dropdown-item>
<el-dropdown-item class="border-t" @click="logout"> <el-dropdown-item class="border-t" @click="logout">
{{ $t('layout.topbar.avatar.logout') }} 退出
</el-dropdown-item> </el-dropdown-item>
</el-dropdown-menu> </el-dropdown-menu>
</template> </template>
</el-dropdown> </el-dropdown>
<ResetPassword ref="resetPasswordRef"></ResetPassword> <ResetPassword ref="resetPasswordRef"></ResetPassword>
<AboutDialog ref="AboutDialogRef"></AboutDialog>
<APIKeyDialog :user-id="user.userInfo?.id" ref="APIKeyDialogRef"/>
<UserPwdDialog ref="UserPwdDialogRef"/>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import {ref, onMounted} from 'vue'
import useStore from '@/stores'
import {useRouter} from 'vue-router'
import ResetPassword from './ResetPassword.vue' import ResetPassword from './ResetPassword.vue'
import AboutDialog from './AboutDialog.vue' import { layout } from "@/utils/api";
import UserPwdDialog from '@/views/user-manage/component/UserPwdDialog.vue'
import APIKeyDialog from './APIKeyDialog.vue'
import {ComplexPermission} from '@/utils/permission/type'
const {user} = useStore()
const router = useRouter() const router = useRouter()
const UserPwdDialogRef = ref()
const AboutDialogRef = ref()
const APIKeyDialogRef = ref()
const resetPasswordRef = ref<InstanceType<typeof ResetPassword>>() const resetPasswordRef = ref<InstanceType<typeof ResetPassword>>()
const openAbout = () => {
AboutDialogRef.value?.open()
}
function openAPIKeyDialog() {
APIKeyDialogRef.value.open()
}
const openResetPassword = () => { const openResetPassword = () => {
resetPasswordRef.value?.open() resetPasswordRef.value?.open()
} }
const logout = () => { const logout = () => {
user.logout().then(() => { layout().then(() => {
router.push({name: 'login'}) router.push({name: 'login'})
}) })
} }
const user = JSON.parse(sessionStorage.getItem("userInfo"));
onMounted(() => {
if (user.userInfo?.is_edit_password) {
UserPwdDialogRef.value.open(user.userInfo)
}
})
</script> </script>
<style lang="scss" scoped>
.avatar-dropdown {
min-width: 210px;
.userInfo {
padding: 12px 11px;
}
:deep(.el-dropdown-menu__item) {
padding: 12px 11px;
}
}
</style>
...@@ -3,47 +3,11 @@ ...@@ -3,47 +3,11 @@
<div class="top-bar-container border-b flex-between"> <div class="top-bar-container border-b flex-between">
<div class="flex-center h-full"> <div class="flex-center h-full">
<div class="app-title-container cursor" @click="router.push('/')"> <div class="app-title-container cursor" @click="router.push('/')">
<div class="logo flex-center"> <img :src="logo" class="w-[180px] mt-[8px]" alt="" />
<LogoFull />
</div>
</div> </div>
<TopMenu></TopMenu> <TopMenu></TopMenu>
</div> </div>
<div class="flex-center avatar"> <div class="flex-center avatar">
<el-button
v-if="!user.isEnterprise()"
link
type="primary"
@click="toUrl('https://maxkb.cn/pricing.html')"
class="mr-8"
>
<AppIcon iconName="app-pricing" class="mr-8" style="font-size: 20px"></AppIcon>
购买专业版
</el-button>
<el-tooltip effect="dark" :content="$t('layout.topbar.github')" placement="top">
<AppIcon
iconName="app-github"
class="cursor color-secondary mr-8 ml-8"
style="font-size: 20px"
@click="toUrl('https://github.com/1Panel-dev/MaxKB')"
></AppIcon>
</el-tooltip>
<el-tooltip effect="dark" :content="$t('layout.topbar.wiki')" placement="top">
<AppIcon
iconName="app-reading"
class="cursor color-secondary mr-8 ml-8"
style="font-size: 20px"
@click="toUrl('https://maxkb.cn/docs/')"
></AppIcon>
</el-tooltip>
<el-tooltip effect="dark" :content="$t('layout.topbar.forum')" placement="top">
<AppIcon
iconName="app-help"
class="cursor color-secondary mr-16 ml-8"
style="font-size: 20px"
@click="toUrl('https://bbs.fit2cloud.com/c/mk/11')"
></AppIcon>
</el-tooltip>
<el-dropdown v-if="false" trigger="click" type="primary"> <el-dropdown v-if="false" trigger="click" type="primary">
<template #dropdown> <template #dropdown>
<el-dropdown-menu> <el-dropdown-menu>
...@@ -71,22 +35,19 @@ ...@@ -71,22 +35,19 @@
import TopMenu from './top-menu/index.vue' import TopMenu from './top-menu/index.vue'
import Avatar from './avatar/index.vue' import Avatar from './avatar/index.vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { langList } from '@/locales/index' import { langList } from '@/locales'
import { useLocale } from '@/locales/useLocale' import { useLocale } from '@/locales/useLocale'
import useStore from '@/stores'
const { user } = useStore()
const router = useRouter() const router = useRouter()
const { changeLocale } = useLocale() const { changeLocale } = useLocale()
const changeLang = (lang: string) => { const changeLang = (lang: string) => {
changeLocale(lang) changeLocale(lang)
} }
function toUrl(url: string) {
window.open(url, '_blank') const logo: string = inject("logoImage");
}
</script> </script>
<style lang="scss"> <style scoped lang="scss">
.top-bar-container { .top-bar-container {
height: var(--app-header-height); height: var(--app-header-height);
box-sizing: border-box; box-sizing: border-box;
......
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
<div class="top-menu-container flex align-center h-full"> <div class="top-menu-container flex align-center h-full">
<MenuItem <MenuItem
:menu="menu" :menu="menu"
v-hasPermission="menu.meta?.permission"
v-for="(menu, index) in topMenuList" v-for="(menu, index) in topMenuList"
:key="index" :key="index"
> >
...@@ -10,14 +9,10 @@ ...@@ -10,14 +9,10 @@
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed } from 'vue' import { getChildRouteListByPathAndName } from '@/router'
import { getChildRouteListByPathAndName } from '@/router/index'
import MenuItem from './MenuItem.vue' import MenuItem from './MenuItem.vue'
const topMenuList = computed(() => { const topMenuList = computed(() => {
return getChildRouteListByPathAndName('/', 'home') return getChildRouteListByPathAndName('/', 'home')
}) });
</script> </script>
<style lang="scss" scoped> \ No newline at end of file
</style>
...@@ -58,5 +58,4 @@ app.use(ElementPlus, { ...@@ -58,5 +58,4 @@ app.use(ElementPlus, {
app.use(router) app.use(router)
app.use(i18n) app.use(i18n)
app.use(Components) app.use(Components)
app.mount('#app') app.mount('#app');
export { app } \ No newline at end of file
...@@ -14,6 +14,7 @@ const router = createRouter({ ...@@ -14,6 +14,7 @@ const router = createRouter({
}) })
// 路由前置拦截器 // 路由前置拦截器
/*
router.beforeEach( router.beforeEach(
async (to: RouteLocationNormalized, from: RouteLocationNormalized, next: NavigationGuardNext) => { async (to: RouteLocationNormalized, from: RouteLocationNormalized, next: NavigationGuardNext) => {
if (to.name === '404') { if (to.name === '404') {
...@@ -41,6 +42,7 @@ router.beforeEach( ...@@ -41,6 +42,7 @@ router.beforeEach(
next() next()
} }
) )
*/
export const getChildRouteListByPathAndName = (path: any, name?: RouteRecordName | any) => { export const getChildRouteListByPathAndName = (path: any, name?: RouteRecordName | any) => {
return getChildRouteList(routes, path, name) return getChildRouteList(routes, path, name)
......
import Layout from '@/layout/layout-template/DetailLayout.vue' import Layout from '@/layout/layout-template/DetailLayout.vue'
const applicationRouter = { const applicationRouter = {
path: '/application', path: '/',
name: 'application', name: 'app',
meta: { title: '应用', permission: 'APPLICATION:READ' }, meta: { title: '应用' },
redirect: '/application', redirect: '/application',
component: () => import('@/layout/layout-template/AppLayout.vue'), component: () => import('@/layout/layout-template/AppLayout.vue'),
children: [ children: [
......
import type { RouteRecordRaw } from 'vue-router' import type { RouteRecordRaw } from 'vue-router';
import { Role } from '@/utils/permission/type'
const modules: any = import.meta.glob('./modules/*.ts', { eager: true }) const modules: any = import.meta.glob('./modules/*.ts', { eager: true })
const rolesRoutes: RouteRecordRaw[] = [...Object.keys(modules).map((key) => modules[key].default)] const rolesRoutes: RouteRecordRaw[] = [...Object.keys(modules).map((key) => modules[key].default)];
export const routes: Array<RouteRecordRaw> = [ export const routes: Array<RouteRecordRaw> = [
{ {
path: '/', path: '/',
name: 'home', name: 'home',
redirect: '/application', redirect: '/application',
children: [...rolesRoutes] children: [rolesRoutes[0]]
}, },
// 高级编排 // 高级编排
{ {
path: '/application/:id/workflow', path: '/application/:id/workflow',
...@@ -32,16 +29,6 @@ export const routes: Array<RouteRecordRaw> = [ ...@@ -32,16 +29,6 @@ export const routes: Array<RouteRecordRaw> = [
component: () => import('@/views/login/index.vue') component: () => import('@/views/login/index.vue')
}, },
{ {
path: '/register',
name: 'register',
component: () => import('@/views/login/register/index.vue')
},
{
path: '/forgot_password',
name: 'forgot_password',
component: () => import('@/views/login/forgot-password/index.vue')
},
{
path: '/reset_password/:code/:email', path: '/reset_password/:code/:email',
name: 'reset_password', name: 'reset_password',
component: () => import('@/views/login/reset-password/index.vue') component: () => import('@/views/login/reset-password/index.vue')
......
...@@ -117,8 +117,8 @@ const useUserStore = defineStore({ ...@@ -117,8 +117,8 @@ const useUserStore = defineStore({
}) })
}, },
async login(username: string, password: string) { async login(email: string, password: string) {
return UserApi.login({ username, password }).then((ok) => { return UserApi.login({ email, password }).then((ok) => {
this.token = ok.data this.token = ok.data
localStorage.setItem('token', ok.data) localStorage.setItem('token', ok.data)
return this.profile() return this.profile()
......
差异被折叠。
import axios from "axios";
import type { AxiosInstance, AxiosRequestConfig, AxiosResponse, InternalAxiosRequestConfig } from "axios";
import { ElNotification } from 'element-plus';
import router from "@/router";
let bool = false;
function layout(text?: string) {
!bool && ElNotification.error({
title: "通知",
message: text || "登录超时",
});
!bool && router.push("/login");
bool = true
const timout = setTimeout(() => {
bool = false
clearTimeout(timout);
}, 4500)
}
const regex = /^2([0-9]{2})$/; //判断数字大于等于200,小于300
interface ISettings {
prompt?: boolean; //报错是否全局提示
}
type jsonData = Record<string, any>;
class Config {
private instance: AxiosInstance;
private settings: ISettings;
constructor(
requeseConfig,
settings = { prompt: true },
) {
this.instance = axios.create(requeseConfig);
this.settings = settings;
// 全局请求拦截
this.instance.interceptors.request.use(
(config: InternalAxiosRequestConfig) => {
if (config.baseURL === "/console/api") {
config.headers["Authorization"] = "Bearer " + sessionStorage.getItem("token");
} else if (config.baseURL === "/api") {
config.headers["Authorization"] = "Bearer " + sessionStorage.getItem("console-token");
}
return config;
},
(error) => {
return error;
},
);
// 全局响应拦截
this.instance.interceptors.response.use(
(res) => {
if (res.data.status === 401) {
layout();
return;
}
return res;
},
(error) => {
if (error?.response) {
switch (error.response.status) {
case 400:
this.settings?.prompt &&
ElNotification.error({
title: "通知",
message: error.response.data.message,
});
break;
case 401:
layout(error.response.data.message);
break;
case 403:
this.settings?.prompt &&
ElNotification.error({
title: "通知",
message: error.response.data.message,
});
break;
case 404:
this.settings?.prompt &&
ElNotification.error({
title: "通知",
message: error.response.data.message,
});
break;
case 405:
this.settings?.prompt &&
ElNotification.error({
title: "通知",
message:
"请求方法未允许 " + error.response.data.message,
});
break;
case 408:
this.settings?.prompt &&
ElNotification.error({
title: "通知",
message: error.response.data.message,
});
break;
case 413:
this.settings?.prompt &&
ElNotification.error({
title: "通知",
message: error.response.data.message,
});
break;
case 409:
this.settings?.prompt &&
ElNotification.error({
title: "通知",
message: error.response.data.message,
});
break;
case 500:
this.settings?.prompt &&
ElNotification.error({
title: "通知",
message: error.response.data.message || error.response.data.statusText,
});
break;
case 501:
this.settings?.prompt &&
ElNotification.error({
title: "通知",
message: "网络未实现",
});
break;
case 502:
this.settings?.prompt &&
ElNotification.error({
title: "服务异常",
message: "服务重启中...",
});
break;
case 503:
this.settings?.prompt &&
ElNotification.error({
title: "服务异常",
message: "服务重启中...",
});
break;
case 504:
this.settings?.prompt &&
ElNotification.error({
title: "通知",
message: "网络超时",
});
break;
case 505:
this.settings?.prompt &&
ElNotification.error({
title: "通知",
message: "http版本不支持该请求",
});
break;
default:
this.settings?.prompt &&
ElNotification.error({
title: "通知",
message: `连接错误`,
});
}
}
return error?.response?.data || error;
},
);
}
get(url: string, params?: jsonData, headers?: jsonData) {
return new Promise<any>((resolve, reject) => {
this.instance
.get(url, { params, headers })
.then((res) => {
if (regex.test(res.status as any as string)) {
resolve(res.data);
} else {
reject(res)
}
})
.catch((err) => {
reject(err);
});
});
}
post(url: string, data?: any, params?: jsonData, headers?: jsonData) {
return new Promise<any>((resolve, reject) => {
this.instance
.post(url, data, { params, headers })
.then((res) => {
if (regex.test(res.status as any as string)) {
resolve(res.data);
} else {
reject(res)
}
})
.catch((err) => {
reject(err);
});
});
}
delete(url: string, config?: AxiosRequestConfig) {
return new Promise<any>((resolve, reject) => {
this.instance
.delete(url, config)
.then((res) => {
if (regex.test(res.status as any as string)) {
resolve(res.data);
} else {
reject(res)
}
})
.catch((err) => {
reject(err);
});
});
}
put(url: string, data: any, params?: jsonData, headers?: jsonData) {
return new Promise<any>((resolve, reject) => {
this.instance
.put(url, data, { params, headers })
.then((res) => {
if (regex.test(res.status as any as string)) {
resolve(res.data);
} else {
reject(res)
}
})
.catch((err) => {
reject(err);
});
});
}
patch(url: string, data?: any, params?: jsonData, headers?: jsonData) {
return new Promise<any>((resolve, reject) => {
this.instance
.patch(url, data, { params, headers })
.then((res) => {
if (regex.test(res.status as any as string)) {
resolve(res.data);
} else {
reject(res)
}
})
.catch((err) => {
reject(err);
});
});
}
requestConfig(config: AxiosRequestConfig) {
return new Promise<any>((resolve, reject) => {
this.instance
.request(config)
.then((res) => {
if (regex.test(res.status as any as string)) {
resolve(res.data);
} else {
reject(res)
}
})
.catch((err) => {
reject(err);
});
});
}
postForm(url: string, data: jsonData, params?: jsonData, headers?: jsonData) {
return new Promise<any>((resolve, reject) => {
this.instance
.postForm(url, data, { params, headers })
.then((res) => {
if (regex.test(res.status as any as string)) {
resolve(res.data);
} else {
reject(res)
}
})
.catch((err) => {
reject(err);
});
});
}
}
export default Config;
import Config from "./config";
export const request = new Config({baseURL: "/console/api"});
export const api = new Config({baseURL: "/api"});
export const noPrompt = new Config({baseURL: "/console/api"}, { prompt: false });
<template>
<login-layout>
<LoginContainer subTitle="欢迎使用 MaxKB 智能知识库">
<h2 class="mb-24">忘记密码</h2>
<el-form
class="register-form"
ref="resetPasswordFormRef"
:model="CheckEmailForm"
:rules="rules"
>
<div class="mb-24">
<el-form-item prop="email">
<el-input
size="large"
class="input-item"
v-model="CheckEmailForm.email"
placeholder="请输入邮箱"
>
</el-input>
</el-form-item>
</div>
<div class="mb-24">
<el-form-item prop="code">
<div class="flex-between w-full">
<el-input
size="large"
class="code-input"
v-model="CheckEmailForm.code"
placeholder="请输入验证码"
>
</el-input>
<el-button
:disabled="isDisabled"
size="large"
class="send-email-button ml-12"
@click="sendEmail"
:loading="loading"
>
{{ isDisabled ? `重新发送(${time}s)` : '获取验证码' }}</el-button
>
</div>
</el-form-item>
</div>
</el-form>
<el-button size="large" type="primary" class="w-full" @click="checkCode">立即验证</el-button>
<div class="operate-container mt-12">
<el-button
class="register"
@click="router.push('/login')"
link
type="primary"
icon="ArrowLeft"
>
返回登录
</el-button>
</div>
</LoginContainer>
</login-layout>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { CheckCodeRequest } from '@/api/type/user'
import { useRouter } from 'vue-router'
import type { FormInstance, FormRules } from 'element-plus'
import UserApi from '@/api/user'
import { MsgSuccess } from '@/utils/message'
const router = useRouter()
const CheckEmailForm = ref<CheckCodeRequest>({
email: '',
code: '',
type: 'reset_password'
})
const resetPasswordFormRef = ref<FormInstance>()
const rules = ref<FormRules<CheckCodeRequest>>({
email: [
{ required: true, message: '请输入邮箱', trigger: 'blur' },
{
validator: (rule, value, callback) => {
const emailRegExp = /^[a-zA-Z0-9_.-]+@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*\.[a-zA-Z0-9]{2,6}$/
if (!emailRegExp.test(value) && value != '') {
callback(new Error('请输入有效邮箱格式!'))
} else {
callback()
}
},
trigger: 'blur'
}
],
code: [{ required: true, message: '请输入验证码' }]
})
const loading = ref<boolean>(false)
const isDisabled = ref<boolean>(false)
const time = ref<number>(60)
const checkCode = () => {
resetPasswordFormRef.value
?.validate()
.then(() => UserApi.checkCode(CheckEmailForm.value, loading))
.then(() => router.push({ name: 'reset_password', params: CheckEmailForm.value }))
}
/**
* 发送验证码
*/
const sendEmail = () => {
resetPasswordFormRef.value?.validateField('email', (v: boolean) => {
if (v) {
UserApi.sendEmit(CheckEmailForm.value.email, 'reset_password', loading).then(() => {
MsgSuccess('发送验证码成功')
isDisabled.value = true
handleTimeChange()
})
}
})
}
const handleTimeChange = () => {
if (time.value <= 0) {
isDisabled.value = false
time.value = 60
} else {
setTimeout(() => {
time.value--
handleTimeChange()
}, 1000)
}
}
</script>
<style lang="scss" scoped></style>
<template> <template>
<login-layout v-if="user.isEnterprise() ? user.themeInfo : true" v-loading="loading"> <login-layout v-if="user.isEnterprise() ? user.themeInfo : true" v-loading="loading">
<LoginContainer :subTitle="user.themeInfo?.slogan || '欢迎使用 MaxKB 智能知识库'"> <LoginContainer subTitle="欢迎使用 快际新云 智能知识库">
<el-form <el-form
class="login-form" class="login-form"
:rules="rules" :rules="rules"
...@@ -8,45 +8,28 @@ ...@@ -8,45 +8,28 @@
ref="loginFormRef" ref="loginFormRef"
@keyup.enter="login" @keyup.enter="login"
> >
<div class="mb-24"> <el-form-item prop="username" class="mb-[30px]">
<el-form-item prop="username"> <el-input
<el-input size="large"
size="large" class="input-item"
class="input-item" v-model="loginForm.email"
v-model="loginForm.email" placeholder="请输入用户名"
placeholder="请输入用户名" >
> </el-input>
</el-input> </el-form-item>
</el-form-item> <el-form-item prop="password" class="mb-[30px]">
</div> <el-input
<div class="mb-24"> type="password"
<el-form-item prop="password"> size="large"
<el-input class="input-item"
type="password" v-model="loginForm.password"
size="large" placeholder="请输入密码"
class="input-item" show-password
v-model="loginForm.password" >
placeholder="请输入密码" </el-input>
show-password </el-form-item>
>
</el-input>
</el-form-item>
</div>
</el-form> </el-form>
<el-button size="large" type="primary" class="w-full" @click="login">登录</el-button> <el-button size="large" type="primary" class="w-full" @click="login">登录</el-button>
<div class="operate-container flex-between mt-12">
<!-- <el-button class="register" @click="router.push('/register')" link type="primary">
注册
</el-button> -->
<el-button
class="forgot-password"
@click="router.push('/forgot_password')"
link
type="primary"
>
忘记密码?
</el-button>
</div>
</LoginContainer> </LoginContainer>
</login-layout> </login-layout>
</template> </template>
...@@ -56,6 +39,7 @@ import type {LoginRequest} from '@/api/type/user' ...@@ -56,6 +39,7 @@ import type {LoginRequest} from '@/api/type/user'
import {useRouter} from 'vue-router' import {useRouter} from 'vue-router'
import type {FormInstance, FormRules} from 'element-plus' import type {FormInstance, FormRules} from 'element-plus'
import useStore from '@/stores' import useStore from '@/stores'
import { loginPort, userInfo } from '@/utils/api'
const loading = ref<boolean>(false) const loading = ref<boolean>(false)
const {user} = useStore() const {user} = useStore()
...@@ -86,12 +70,18 @@ const loginFormRef = ref<FormInstance>() ...@@ -86,12 +70,18 @@ const loginFormRef = ref<FormInstance>()
const login = () => { const login = () => {
loginFormRef.value?.validate().then(() => { loginFormRef.value?.validate().then(() => {
loading.value = true loading.value = true
user loginPort(loginForm.value).then((res) => {
.login(loginForm.value.email, loginForm.value.password) res && userInfo().then(res => {
.then(() => { sessionStorage.setItem("userInfo", JSON.stringify(res));
router.push({name: 'home'}) const timer = setTimeout(() => {
}) router.push({name: 'home'});
.finally(() => (loading.value = false)) loading.value = false;
clearTimeout(timer);
}, 300)
})
}).catch(() => {
loading.value = false;
})
}) })
} }
......
<template>
<login-layout>
<LoginContainer subTitle="欢迎使用 MaxKB 智能知识库">
<h2 class="mb-24">用户注册</h2>
<el-form class="register-form" :model="registerForm" :rules="rules" ref="registerFormRef">
<div class="mb-24">
<el-form-item prop="username">
<el-input
size="large"
class="input-item"
v-model="registerForm.username"
placeholder="请输入用户名"
>
</el-input>
</el-form-item>
</div>
<div class="mb-24">
<el-form-item prop="password">
<el-input
type="password"
size="large"
class="input-item"
v-model="registerForm.password"
placeholder="请输入密码"
show-password
>
</el-input>
</el-form-item>
</div>
<div class="mb-24">
<el-form-item prop="re_password">
<el-input
type="password"
size="large"
class="input-item"
v-model="registerForm.re_password"
placeholder="请输入确认密码"
show-password
>
</el-input>
</el-form-item>
</div>
<div class="mb-24">
<el-form-item prop="email">
<el-input
size="large"
class="input-item"
v-model="registerForm.email"
placeholder="请输入邮箱"
>
</el-input>
</el-form-item>
</div>
<div class="mb-24">
<el-form-item prop="code">
<div class="flex-between w-full">
<el-input
size="large"
class="code-input"
v-model="registerForm.code"
placeholder="请输入验证码"
>
</el-input>
<el-button
:disabled="isDisabled"
size="large"
class="send-email-button ml-12"
@click="sendEmail"
:loading="sendEmailLoading"
>
{{ isDisabled ? `重新发送(${time}s)` : '获取验证码' }}</el-button
>
</div>
</el-form-item>
</div>
</el-form>
<el-button size="large" type="primary" class="w-full" @click="register">注册</el-button>
<div class="operate-container mt-12">
<el-button
class="register"
@click="router.push('/login')"
link
type="primary"
icon="ArrowLeft"
>
返回登录
</el-button>
</div>
</LoginContainer>
</login-layout>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { RegisterRequest } from '@/api/type/user'
import { useRouter } from 'vue-router'
import UserApi from '@/api/user'
import { MsgSuccess } from '@/utils/message'
import type { FormInstance, FormRules } from 'element-plus'
const router = useRouter()
const registerForm = ref<RegisterRequest>({
username: '',
password: '',
re_password: '',
email: '',
code: ''
})
const rules = ref<FormRules<RegisterRequest>>({
username: [
{
required: true,
message: '请输入用户名',
trigger: 'blur'
},
{
min: 6,
max: 20,
message: '长度在 6 到 20 个字符',
trigger: 'blur'
}
],
password: [
{
required: true,
message: '请输入密码',
trigger: 'blur'
},
{
min: 6,
max: 20,
message: '长度在 6 到 20 个字符',
trigger: 'blur'
}
],
re_password: [
{
required: true,
message: '请输入确认密码',
trigger: 'blur'
},
{
min: 6,
max: 20,
message: '长度在 6 到 20 个字符',
trigger: 'blur'
},
{
validator: (rule, value, callback) => {
if (registerForm.value.password != registerForm.value.re_password) {
callback(new Error('密码不一致'))
} else {
callback()
}
},
trigger: 'blur'
}
],
email: [
{ required: true, message: '请输入邮箱', trigger: 'blur' },
{
validator: (rule, value, callback) => {
const emailRegExp = /^[a-zA-Z0-9_.-]+@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*\.[a-zA-Z0-9]{2,6}$/
if (!emailRegExp.test(value) && value != '') {
callback(new Error('请输入有效邮箱格式!'))
} else {
callback()
}
},
trigger: 'blur'
}
],
code: [{ required: true, message: '请输入验证码' }]
})
const registerFormRef = ref<FormInstance>()
const register = () => {
registerFormRef.value
?.validate()
.then(() => {
return UserApi.register(registerForm.value)
})
.then(() => {
router.push('login')
})
}
const sendEmailLoading = ref<boolean>(false)
const isDisabled = ref<boolean>(false)
const time = ref<number>(60)
/**
* 发送验证码
*/
const sendEmail = () => {
registerFormRef.value?.validateField('email', (v: boolean) => {
if (v) {
UserApi.sendEmit(registerForm.value.email, 'register', sendEmailLoading).then(() => {
MsgSuccess('发送验证码成功')
isDisabled.value = true
handleTimeChange()
})
}
})
}
const handleTimeChange = () => {
if (time.value <= 0) {
isDisabled.value = false
time.value = 60
} else {
setTimeout(() => {
time.value--
handleTimeChange()
}, 1000)
}
}
</script>
<style lang="scss" scoped></style>
/** @type {import('tailwindcss').Config} */
export default {
content: ["./index.html", "./src/**/*.{vue,js,jsx,tsx}"],
corePlugins: {
preflight: false,
},
theme: {
screens: {
sm: "640px",
md: "768px",
lg: "1024px",
xl: "1280px",
"2xl": "1536px",
},
colors: {
primary: "#0c599d",
danger: "#ff0000",
white: "#ffffff",
success: "#67C23A",
warning: "#E6A23C",
info: "#909399",
blue: "#409EFF"
},
extend: {},
},
plugins: [],
}
{ {
"extends": "@vue/tsconfig/tsconfig.dom.json", "include": ["env.d.ts", "src/**/*", "src/**/*.vue", "./auto-imports.d.ts"],
"include": ["env.d.ts", "src/**/*", "src/**/*.vue"], "exclude": ["src/**/__tests__/*", "node_modules", "public"],
"exclude": ["src/**/__tests__/*"],
"compilerOptions": { "compilerOptions": {
"composite": true, "composite": true,
"moduleResolution": "node", "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "bundler",
"strict": false,
"jsx": "preserve",
"importHelpers": true,
"experimentalDecorators": true,
"strictFunctionTypes": false,
"skipLibCheck": true,
"esModuleInterop": true,
"isolatedModules": true,
"allowSyntheticDefaultImports": true,
"forceConsistentCasingInFileNames": true,
"sourceMap": true,
"allowJs": false,
"resolveJsonModule": true,
"lib": [
"ESNext",
"DOM"
],
"baseUrl": ".", "baseUrl": ".",
"target": "esnext", // 使用ES最新语法
"module": "esnext", // 使用ES模块语法
"paths": { "paths": {
"@/*": ["./src/*"] "@/*": ["./src/*"],
} }
} }
} }
...@@ -2,27 +2,77 @@ import { fileURLToPath, URL } from 'node:url' ...@@ -2,27 +2,77 @@ import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite' import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue' import vue from '@vitejs/plugin-vue'
import vueJsx from "@vitejs/plugin-vue-jsx";
import vueDevTools from "vite-plugin-vue-devtools"; import vueDevTools from "vite-plugin-vue-devtools";
import AutoImport from "unplugin-auto-import/vite";
import Components from "unplugin-vue-components/vite";
// https://vitejs.dev/config/ // https://vitejs.dev/config/
export default defineConfig(({ mode }) => { export default defineConfig(({ mode }) => {
return { return {
plugins: [vue(), vueDevTools()], plugins: [
vue(),
vueDevTools(),
vueJsx(),
AutoImport({
include: [
/\.[j]sx?$/, // .ts, .tsx, .js, .jsx
/\.vue$/,
/\.vue\?vue/, // .vue
/\.md$/, // .md
],
// 全局引入插件
imports: [
// presets
"vue",
"vue-router",
"pinia",
// custom
{
"@vueuse/core": [
// named imports
"useMouse", // import { useMouse } from '@vueuse/core',
// alias
["useFetch", "useMyFetch"], // import { useFetch as useMyFetch } from '@vueuse/core',
],
},
],
eslintrc: {
enabled: false,
filepath: './.eslintrc-auto-import.json',
globalsPropValue: true,
},
resolvers: [],
}),
Components({
resolvers: [],
}),
],
server: { server: {
port: 8917, port: 8917,
cors: true, cors: true,
proxy: { proxy: {
"/console": {
target: "https://klm-service-dev.apps.iytcloud.com/",
changeOrigin: true,
secure: false,
},
"/api": { "/api": {
target: "http://192.168.121.203:18080", target: "https://klm-service-dev.apps.iytcloud.com/",
changeOrigin: true, changeOrigin: true,
secure: false, secure: false,
}, },
"/workspaces": {
target: "https://klm-service-dev.apps.iytcloud.com/",
changeOrigin: true,
secure: false,
}
}, },
}, },
resolve: { resolve: {
extensions: ['.vue', '.ts', '.js', '.json', '.tsx', '.jsx'], extensions: ['.vue', '.ts', '.js', '.json', '.tsx', '.jsx'],
alias: { alias: {
'@': fileURLToPath(new URL('./src', import.meta.url)) "@": fileURLToPath(new URL("./src", import.meta.url)),
} }
}, },
esbuild: { esbuild: {
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论