feat: 页面结构

This commit is contained in:
wangdan-fit2cloud 2023-10-13 16:11:54 +08:00
parent 5511769673
commit 439400d023
26 changed files with 467 additions and 448 deletions

View File

@ -1,17 +1,41 @@
<template> <template>
<component :is="Object.keys(iconMap).includes(iconName) ? iconMap[iconName].iconReader() : iconMap['404'].iconReader()"> <component
v-if="isIconfont"
:is="
Object.keys(iconMap).includes(iconName)
? iconMap[iconName].iconReader()
: iconMap['404'].iconReader()
"
class="app-icon"
>
</component> </component>
<el-icon v-else-if="iconName">
<component :is="iconName" />
</el-icon>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { iconMap } from "@/components/icons/index" import { computed } from 'vue'
defineOptions({ name: 'AppIcon' }); import { iconMap } from '@/components/icons/index'
withDefaults(defineProps<{ defineOptions({ name: 'AppIcon' })
iconName?: string;
}>(), {
iconName: '404'
});
const props = withDefaults(
defineProps<{
iconName?: string
}>(),
{
iconName: '404'
}
)
const isIconfont = computed(() => props.iconName?.includes('app-'))
</script> </script>
<style lang="scss" scoped></style>
<style lang="scss" scoped>
.app-icon {
line-height: 1em;
svg {
height: 1em;
width: 1em;
}
}
</style>

View File

@ -1,8 +1,8 @@
import { h } from 'vue' import { h } from 'vue'
export const iconMap: any = { export const iconMap: any = {
'404': { 'app-404': {
iconReader: () => { iconReader: () => {
return h('el-icon', { style: 'display:flex' }, [ return h('i', [
h( h(
'svg', 'svg',
{ {
@ -33,131 +33,62 @@ export const iconMap: any = {
]) ])
} }
}, },
home: {
'app-dataset': {
iconReader: () => { iconReader: () => {
return h('el-icon', { style: 'display:flex' }, [ return h('i', [
h( h(
'svg', 'svg',
{ {
viewBox: '0 0 1024 1024', viewBox: '0 0 1024 1024',
version: '1.1', version: '1.1',
style: 'height:14px;width:14px',
xmlns: 'http://www.w3.org/2000/svg'
},
[
h('path', {
d: 'M362.666667 895.914667V639.850667c0-36.266667 33.109333-63.850667 72.533333-63.850667h153.6c39.253333 0 72.533333 27.648 72.533333 63.850667v256.064h59.904c61.269333 0 110.762667-47.957333 110.762667-106.730667V414.165333L557.162667 139.328a63.808 63.808 0 0 0-90.325334 0L192 414.165333v375.018667c0 58.88 49.386667 106.730667 110.762667 106.730667H362.666667z m42.666666 0h213.333334V639.850667c0-10.709333-12.586667-21.184-29.866667-21.184h-153.6c-17.408 0-29.866667 10.389333-29.866667 21.184v256.064z m469.333334-439.082667v332.352c0 82.645333-68.885333 149.397333-153.429334 149.397333H302.762667C218.133333 938.581333 149.333333 871.936 149.333333 789.184V456.832l-27.584 27.584a21.333333 21.333333 0 1 1-30.165333-30.165333L436.672 109.162667a106.474667 106.474667 0 0 1 150.656 0l345.088 345.088a21.333333 21.333333 0 0 1-30.165333 30.165333L874.666667 456.832z',
fill: '#666666'
})
]
)
])
}
},
app: {
iconReader: () => {
return h('el-icon', { style: 'display:flex' }, [
h(
'svg',
{
viewBox: '0 0 1024 1024',
version: '1.1',
style: 'height:14px;width:14px',
xmlns: 'http://www.w3.org/2000/svg'
},
[
h('path', {
d: 'M906.62890625 212.8203125C906.62890625 161.58007812 865.05664063 120.0078125 813.81640625 120.0078125H645.2421875C594.00195313 120.0078125 552.4296875 161.58007812 552.4296875 212.8203125v168.57421875c0 51.24023438 41.57226563 92.8125 92.8125 92.8125h168.57421875c51.24023438 0 92.8125-41.57226563 92.8125-92.8125V212.8203125z m-56.25 173.93554688c0 17.05078125-28.125 31.20117188-45.26367188 31.20117187H640.3203125c-17.05078125 0-30.76171875-14.0625-30.76171875-31.20117188V207.81054687c0-17.05078125 13.7109375-30.67382813 30.76171875-30.67382812h178.9453125c17.05078125 0 31.02539063 13.62304688 31.02539063 30.67382813v178.94531249z m56.25 251.45507812c0-51.24023438-41.57226563-92.8125-92.8125-92.8125H645.2421875C594.00195313 545.3984375 552.4296875 586.97070313 552.4296875 638.2109375v168.57421875c0 51.24023438 41.57226563 92.8125 92.8125 92.8125h168.57421875c51.24023438 0 92.8125-41.57226563 92.8125-92.8125V638.2109375z m-56.25 173.3203125c0 17.05078125-13.88671875 30.9375-30.9375 30.9375H640.49609375c-17.05078125 0-30.9375-13.88671875-30.9375-30.9375V632.5859375c0-17.05078125 13.88671875-30.9375 30.9375-30.9375h178.9453125c17.05078125 0 30.9375 13.88671875 30.9375 30.9375v178.9453125zM468.0546875 638.2109375c0-51.24023438-41.57226563-92.8125-92.8125-92.8125H206.66796875C155.42773437 545.3984375 113.85546875 586.97070313 113.85546875 638.2109375v168.57421875C113.85546875 858.02539063 155.42773437 899.59765625 206.66796875 899.59765625h168.57421875c51.24023438 0 92.8125-41.57226563 92.8125-92.8125V638.2109375z m-57.12890625 173.3203125c0 17.05078125-13.88671875 30.9375-30.9375 30.9375H201.04296875c-17.05078125 0-30.9375-13.88671875-30.9375-30.9375V632.5859375c0-17.05078125 13.88671875-30.9375 30.9375-30.9375h178.9453125c17.05078125 0 30.9375 13.88671875 30.9375 30.9375v178.9453125z m57.12890625-598.7109375C468.0546875 161.58007812 426.48242187 120.0078125 375.2421875 120.0078125H206.66796875C155.42773437 120.0078125 113.85546875 161.58007812 113.85546875 212.8203125v168.57421875C113.85546875 432.63476562 155.42773437 474.20703125 206.66796875 474.20703125h168.57421875c51.24023438 0 92.8125-41.57226563 92.8125-92.8125V212.8203125z m-57.12890625 174.19921875c0 17.05078125-13.88671875 30.9375-30.9375 30.9375H201.04296875c-17.05078125 0-30.9375-13.88671875-30.9375-30.9375V208.07421875c0-17.05078125 13.88671875-30.9375 30.9375-30.9375h178.9453125c17.05078125 0 30.9375 13.88671875 30.9375 30.9375v178.9453125z',
fill: '#768696'
})
]
)
])
}
},
dataset: {
iconReader: () => {
return h('el-icon', { style: 'display:flex' }, [
h(
'svg',
{
viewBox: '0 0 1024 1024',
version: '1.1',
style: 'height:14px;width:14px',
xmlns: 'http://www.w3.org/2000/svg' xmlns: 'http://www.w3.org/2000/svg'
}, },
[ [
h('path', { h('path', {
d: 'M859.5 193H446.939c-1.851-53.25-45.747-96-99.439-96h-183C109.635 97 65 141.635 65 196.5v632c0 54.864 44.635 99.5 99.5 99.5h695c54.864 0 99.5-44.636 99.5-99.5v-536c0-54.865-44.636-99.5-99.5-99.5z m-695-33h183c20.126 0 36.5 16.374 36.5 36.5v28c0 17.397 14.103 31.5 31.5 31.5h444c20.126 0 36.5 16.374 36.5 36.5V321H128V196.5c0-20.126 16.374-36.5 36.5-36.5z m695 705h-695c-20.126 0-36.5-16.374-36.5-36.5V384h768v444.5c0 20.126-16.374 36.5-36.5 36.5z', d: 'M859.5 193H446.939c-1.851-53.25-45.747-96-99.439-96h-183C109.635 97 65 141.635 65 196.5v632c0 54.864 44.635 99.5 99.5 99.5h695c54.864 0 99.5-44.636 99.5-99.5v-536c0-54.865-44.636-99.5-99.5-99.5z m-695-33h183c20.126 0 36.5 16.374 36.5 36.5v28c0 17.397 14.103 31.5 31.5 31.5h444c20.126 0 36.5 16.374 36.5 36.5V321H128V196.5c0-20.126 16.374-36.5 36.5-36.5z m695 705h-695c-20.126 0-36.5-16.374-36.5-36.5V384h768v444.5c0 20.126-16.374 36.5-36.5 36.5z',
fill: '#070102' fill: 'currentColor'
}) })
] ]
) )
]) ])
} }
}, },
setting: { 'app-applicaiton': {
iconReader: () => { iconReader: () => {
return h('el-icon', { style: 'display:flex' }, [ return h('i', [
h( h(
'svg', 'svg',
{ {
viewBox: '0 0 1024 1024', viewBox: '0 0 1024 1024',
version: '1.1', version: '1.1',
style: 'height:14px;width:14px',
xmlns: 'http://www.w3.org/2000/svg' xmlns: 'http://www.w3.org/2000/svg'
}, },
[ [
h('path', { h('path', {
d: 'M512 328c-100.8 0-184 83.2-184 184S411.2 696 512 696 696 612.8 696 512 612.8 328 512 328z m0 320c-75.2 0-136-60.8-136-136s60.8-136 136-136 136 60.8 136 136-60.8 136-136 136z', d: 'M951.901 244.015l-413.3-238.57a33.606 33.606 0 0 0-33.909 0L91.3 244.016c-10.426 6.12-16.99 17.221-16.99 29.346v477.184c0 12.149 6.447 23.343 16.99 29.37l413.3 238.662c5.213 2.933 11.101 4.515 16.99 4.515 5.794 0 11.775-1.582 16.988-4.515l413.3-238.661c10.427-6.121 16.99-17.222 16.99-29.37V273.36a33.908 33.908 0 0 0-16.966-29.346zM892.23 726.016l-370.618 213.97-370.642-213.97v-427.87L521.588 84.178l370.642 213.97v427.869z m8.797 5.073M285.207 348.393a34.095 34.095 0 0 0-46.336 12.567 33.908 33.908 0 0 0 12.474 46.36l235.94 136.215v269.498a33.745 33.745 0 0 0 33.884 33.885 33.745 33.745 0 0 0 33.886-33.885V543.977L791.9 407.227a34.025 34.025 0 0 0 12.451-46.36 34.025 34.025 0 0 0-46.336-12.474l-236.404 136.54-236.405-136.54z m0 0',
fill: '#070102' fill: 'currentColor'
}),
h('path', {
d: 'M857.6 572.8c-20.8-12.8-33.6-35.2-33.6-60.8s12.8-46.4 33.6-60.8c14.4-9.6 20.8-27.2 16-44.8-8-27.2-19.2-52.8-32-76.8-8-14.4-25.6-24-43.2-19.2-24 4.8-48-1.6-65.6-19.2-17.6-17.6-24-41.6-19.2-65.6 3.2-16-4.8-33.6-19.2-43.2-24-14.4-51.2-24-76.8-32-16-4.8-35.2 1.6-44.8 16-12.8 20.8-35.2 33.6-60.8 33.6s-46.4-12.8-60.8-33.6c-9.6-14.4-27.2-20.8-44.8-16-27.2 8-52.8 19.2-76.8 32-14.4 8-24 25.6-19.2 43.2 4.8 24-1.6 49.6-19.2 65.6-17.6 17.6-41.6 24-65.6 19.2-16-3.2-33.6 4.8-43.2 19.2-14.4 24-24 51.2-32 76.8-4.8 16 1.6 35.2 16 44.8 20.8 12.8 33.6 35.2 33.6 60.8s-12.8 46.4-33.6 60.8c-14.4 9.6-20.8 27.2-16 44.8 8 27.2 19.2 52.8 32 76.8 8 14.4 25.6 22.4 43.2 19.2 24-4.8 49.6 1.6 65.6 19.2 17.6 17.6 24 41.6 19.2 65.6-3.2 16 4.8 33.6 19.2 43.2 24 14.4 51.2 24 76.8 32 16 4.8 35.2-1.6 44.8-16 12.8-20.8 35.2-33.6 60.8-33.6s46.4 12.8 60.8 33.6c8 11.2 20.8 17.6 33.6 17.6 3.2 0 8 0 11.2-1.6 27.2-8 52.8-19.2 76.8-32 14.4-8 24-25.6 19.2-43.2-4.8-24 1.6-49.6 19.2-65.6 17.6-17.6 41.6-24 65.6-19.2 16 3.2 33.6-4.8 43.2-19.2 14.4-24 24-51.2 32-76.8 4.8-17.6-1.6-35.2-16-44.8z m-56 92.8c-38.4-6.4-76.8 6.4-104 33.6-27.2 27.2-40 65.6-33.6 104-17.6 9.6-36.8 17.6-56 24-22.4-30.4-57.6-49.6-97.6-49.6-38.4 0-73.6 17.6-97.6 49.6-19.2-6.4-38.4-14.4-56-24 6.4-38.4-6.4-76.8-33.6-104-27.2-27.2-65.6-40-104-33.6-9.6-17.6-17.6-36.8-24-56 30.4-22.4 49.6-57.6 49.6-97.6 0-38.4-17.6-73.6-49.6-97.6 6.4-19.2 14.4-38.4 24-56 38.4 6.4 76.8-6.4 104-33.6 27.2-27.2 40-65.6 33.6-104 17.6-9.6 36.8-17.6 56-24 22.4 30.4 57.6 49.6 97.6 49.6 38.4 0 73.6-17.6 97.6-49.6 19.2 6.4 38.4 14.4 56 24-6.4 38.4 6.4 76.8 33.6 104 27.2 27.2 65.6 40 104 33.6 9.6 17.6 17.6 36.8 24 56-30.4 22.4-49.6 57.6-49.6 97.6 0 38.4 17.6 73.6 49.6 97.6-6.4 19.2-14.4 38.4-24 56z',
fill: '#070102'
}) })
] ]
) )
]) ])
} }
}, },
password: {
'app-exit': {
iconReader: () => { iconReader: () => {
return h('el-icon', { style: 'display:flex' }, [ return h('i', [
h( h(
'svg', 'svg',
{ {
viewBox: '0 0 1024 1024', viewBox: '0 0 1024 1024',
version: '1.1', version: '1.1',
style: 'height:14px;width:14px',
xmlns: 'http://www.w3.org/2000/svg' xmlns: 'http://www.w3.org/2000/svg'
}, },
[ [
h('path', { h('path', {
d: 'M807.28626 393.9647l-59.057047 0 0-78.729086c0-130.193201-105.96438-236.099253-236.229213-236.099253S275.770787 185.042413 275.770787 315.235614l0 78.729086-59.057047 0c-32.616862 0-59.057047 26.425859-59.057047 59.025325L157.656693 885.838314c0 32.598442 26.441209 59.025325 59.057047 59.025325l590.57252 0c32.616862 0 59.057047-26.425859 59.057047-59.025325L866.343307 452.989001C866.343307 420.390559 839.903122 393.9647 807.28626 393.9647zM334.827835 315.235614c0-97.644901 79.473029-177.074951 177.172165-177.074951s177.172165 79.43005 177.172165 177.074951l0 78.729086L334.827835 393.9647 334.827835 315.235614zM807.28626 885.838314 216.71374 885.838314 216.71374 452.989001l590.57252 0L807.28626 885.838314z' d: 'M874.666667 855.744a19.093333 19.093333 0 0 1-19.136 18.922667H168.469333A19.2 19.2 0 0 1 149.333333 855.530667V168.469333A19.2 19.2 0 0 1 168.469333 149.333333h687.061334c10.581333 0 19.136 8.533333 19.136 18.922667V320h42.666666V168.256A61.717333 61.717333 0 0 0 855.530667 106.666667H168.469333A61.866667 61.866667 0 0 0 106.666667 168.469333v687.061334A61.866667 61.866667 0 0 0 168.469333 917.333333h687.061334A61.76 61.76 0 0 0 917.333333 855.744V704h-42.666666v151.744zM851.84 533.333333l-131.797333 131.754667a21.141333 21.141333 0 0 0 0.213333 29.973333 21.141333 21.141333 0 0 0 29.973333 0.192l165.589334-165.589333a20.821333 20.821333 0 0 0 6.122666-14.976 21.44 21.44 0 0 0-6.314666-14.997333l-168.533334-168.533334a21.141333 21.141333 0 0 0-29.952-0.213333 21.141333 21.141333 0 0 0 0.213334 29.973333L847.296 490.666667H469.333333v42.666666h382.506667z',
}), fill: 'currentColor'
h('path', {
d: 'M512 777.635963c16.302291 0 29.528524-13.219069 29.528524-29.512151L541.528524 590.723969c0-16.293081-13.226233-29.512151-29.528524-29.512151s-29.528524 13.219069-29.528524 29.512151l0 157.399843C482.471476 764.416893 495.697709 777.635963 512 777.635963z'
})
]
)
])
}
},
exit: {
iconReader: () => {
return h('el-icon', { style: 'display:flex' }, [
h(
'svg',
{
viewBox: '0 0 1024 1024',
version: '1.1',
style: 'height:14px;width:14px',
xmlns: 'http://www.w3.org/2000/svg'
},
[
h('path', {
d: 'M874.666667 855.744a19.093333 19.093333 0 0 1-19.136 18.922667H168.469333A19.2 19.2 0 0 1 149.333333 855.530667V168.469333A19.2 19.2 0 0 1 168.469333 149.333333h687.061334c10.581333 0 19.136 8.533333 19.136 18.922667V320h42.666666V168.256A61.717333 61.717333 0 0 0 855.530667 106.666667H168.469333A61.866667 61.866667 0 0 0 106.666667 168.469333v687.061334A61.866667 61.866667 0 0 0 168.469333 917.333333h687.061334A61.76 61.76 0 0 0 917.333333 855.744V704h-42.666666v151.744zM851.84 533.333333l-131.797333 131.754667a21.141333 21.141333 0 0 0 0.213333 29.973333 21.141333 21.141333 0 0 0 29.973333 0.192l165.589334-165.589333a20.821333 20.821333 0 0 0 6.122666-14.976 21.44 21.44 0 0 0-6.314666-14.997333l-168.533334-168.533334a21.141333 21.141333 0 0 0-29.952-0.213333 21.141333 21.141333 0 0 0 0.213334 29.973333L847.296 490.666667H469.333333v42.666666h382.506667z'
}) })
] ]
) )

View File

@ -0,0 +1,39 @@
<script setup lang="ts">
import { ref, onBeforeUpdate } from 'vue'
import { useRoute } from 'vue-router'
import TopBar from '@/components/layout/top-bar/index.vue'
const route = useRoute()
const cachedViews: any = ref([])
onBeforeUpdate(() => {
let isCached = route.meta?.cache
let name = route.name
if (isCached && name && !cachedViews.value.includes(name)) {
cachedViews.value.push(name)
}
})
</script>
<template>
<div class="app-layout">
<div class="app-header">
<TopBar></TopBar>
</div>
<div class="app-main">
<router-view v-slot="{ Component }">
<transition appear name="fade-transform" mode="out-in">
<keep-alive :include="cachedViews">
<component :is="Component" />
</keep-alive>
</transition>
</router-view>
</div>
</div>
</template>
<style lang="scss" scoped>
.app-main {
height: calc(100vh - var(--app-header-height));
padding: 0 !important;
}
</style>

View File

@ -1,32 +0,0 @@
<script setup lang="ts">
import TopBar from "@/components/layout/top-bar/index.vue"
</script>
<template>
<div class="common-layout">
<el-container>
<el-header>
<TopBar></TopBar>
</el-header>
<el-main>
<RouterView></RouterView>
</el-main>
</el-container>
</div>
</template>
<style lang="scss" scoped>
.el-header {
--el-header-padding: 0;
--el-header-height: 56px;
padding: var(--el-header-padding);
box-sizing: border-box;
flex-shrink: 0;
height: var(--el-header-height);
}
.el-main {
--el-main-padding: 0;
width: 100vw;
height: calc(100vh - var(--el-header-height, 60px));
}
</style>

View File

@ -1,155 +1,176 @@
<template > <template>
<el-dialog v-model="resetPasswordDialog" title="修改密码"> <el-dialog v-model="resetPasswordDialog" title="修改密码">
<el-form class="reset-password-form" ref="resetPasswordFormRef" :model="resetPasswordForm" :rules="rules"> <el-form
class="reset-password-form"
<el-form-item prop="password"> ref="resetPasswordFormRef"
<el-input type="password" size="large" class="input-item" v-model="resetPasswordForm.password" :model="resetPasswordForm"
placeholder="请输入密码"> :rules="rules"
>
<template #prepend> <el-form-item prop="password">
<el-button :icon="Lock" /> <el-input
</template> type="password"
</el-input> size="large"
</el-form-item> class="input-item"
<el-form-item prop="re_password"> v-model="resetPasswordForm.password"
<el-input type="password" size="large" class="input-item" v-model="resetPasswordForm.re_password" placeholder="请输入密码"
placeholder="请输入确认密码"> >
<template #prepend> <template #prepend>
<el-button :icon="Lock" /> <el-button icon="Lock" />
</template> </template>
</el-input> </el-input>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item prop="re_password">
<el-input size="large" class="input-item" :disabled="true" v-bind:modelValue="userStore.userInfo?.email" <el-input
@change="() => { }" placeholder="请输入邮箱"> type="password"
size="large"
<template #prepend> class="input-item"
<el-button :icon="UserFilled" /> v-model="resetPasswordForm.re_password"
</template> placeholder="请输入确认密码"
</el-input> >
</el-form-item> <template #prepend>
<el-form-item prop="code"> <el-button icon="Lock" />
<el-input size="large" class="code-input" v-model="resetPasswordForm.code" placeholder="请输入验证码"> </template>
<template #prepend> </el-input>
<el-button :icon="Key" /> </el-form-item>
</template> <el-form-item>
</el-input> <el-input
<el-button size="large" class="send-email-button" @click="sendEmail" :loading="loading">获取验证码</el-button> size="large"
</el-form-item> class="input-item"
</el-form> :disabled="true"
<template #footer> v-bind:modelValue="userStore.userInfo?.email"
<span class="dialog-footer"> @change="() => {}"
<el-button type="primary" @click="resetPassword"> placeholder="请输入邮箱"
修改密码 >
</el-button> <template #prepend>
</span> <el-button icon="UserFilled" />
</template> </template>
</el-dialog> </el-input>
</el-form-item>
<el-form-item prop="code">
<div class="flex-between w-full">
<el-input
size="large"
class="code-input"
v-model="resetPasswordForm.code"
placeholder="请输入验证码"
>
<template #prepend>
<el-button icon="Key" />
</template>
</el-input>
<el-button
size="large"
class="send-email-button ml-1"
@click="sendEmail"
:loading="loading"
>获取验证码</el-button
>
</div>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button type="primary" @click="resetPassword"> 修改密码 </el-button>
</span>
</template>
</el-dialog>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref } from "vue"; import { ref } from 'vue'
import type { ResetCurrentUserPasswordRequest } from "@/api/user/type"; import type { ResetCurrentUserPasswordRequest } from '@/api/user/type'
import type { FormInstance, FormRules } from 'element-plus' import type { FormInstance, FormRules } from 'element-plus'
import { ElMessage } from "element-plus" import { MsgSuccess } from '@/utils/message'
import UserApi from "@/api/user" import UserApi from '@/api/user'
import { useUserStore } from '@/stores/user'; import { useUserStore } from '@/stores/user'
import { Lock, UserFilled, Key } from '@element-plus/icons-vue' import { useRouter } from 'vue-router'
import { useRouter } from "vue-router" const router = useRouter()
const router = useRouter();
const userStore = useUserStore() const userStore = useUserStore()
const resetPasswordDialog = ref<boolean>(false); const resetPasswordDialog = ref<boolean>(false)
const resetPasswordForm = ref<ResetCurrentUserPasswordRequest>({ const resetPasswordForm = ref<ResetCurrentUserPasswordRequest>({
code: "", code: '',
password: "", password: '',
re_password: "" re_password: ''
}); })
const resetPasswordFormRef = ref<FormInstance>(); const resetPasswordFormRef = ref<FormInstance>()
const loading = ref<boolean>(false); const loading = ref<boolean>(false)
const rules = ref<FormRules<ResetCurrentUserPasswordRequest>>({ const rules = ref<FormRules<ResetCurrentUserPasswordRequest>>({
code: [{ required: true, message: '请输入验证码' }],
code: [ password: [
{ required: true, message: '请输入验证码' } {
], required: true,
password: [ message: '请输入密码',
{ trigger: 'blur'
required: true,
message: "请输入密码",
trigger: "blur",
},
{
min: 6,
max: 30,
message: "长度在 6 到 30 个字符",
trigger: "blur",
},
],
re_password: [{
required: true,
message: '请输入确认密码',
trigger: 'blur'
}, },
{ {
min: 6, min: 6,
max: 30, max: 30,
message: "长度在 6 到 30 个字符", message: '长度在 6 到 30 个字符',
trigger: "blur", trigger: 'blur'
}
],
re_password: [
{
required: true,
message: '请输入确认密码',
trigger: 'blur'
}, },
{ {
validator: (rule, value, callback) => { min: 6,
if (resetPasswordForm.value.password != resetPasswordForm.value.re_password) { max: 30,
callback(new Error('密码不一致')); message: '长度在 6 到 30 个字符',
} else { trigger: 'blur'
callback(); },
} {
}, validator: (rule, value, callback) => {
trigger: 'blur' if (resetPasswordForm.value.password != resetPasswordForm.value.re_password) {
}] callback(new Error('密码不一致'))
} else {
callback()
}
},
trigger: 'blur'
}
]
}) })
/** /**
* 发送验证码 * 发送验证码
*/ */
const sendEmail = () => { const sendEmail = () => {
UserApi.sendEmailToCurrent(loading) UserApi.sendEmailToCurrent(loading).then(() => {
.then(() => { MsgSuccess('发送验证码成功')
ElMessage.success("发送验证码成功") })
})
} }
const open = () => { const open = () => {
resetPasswordForm.value = { resetPasswordForm.value = {
code: "", code: '',
password: "", password: '',
re_password: "" re_password: ''
} }
resetPasswordDialog.value = true resetPasswordDialog.value = true
} }
const resetPassword = () => { const resetPassword = () => {
resetPasswordFormRef.value?.validate().then(() => { resetPasswordFormRef.value
return UserApi.resetCurrentUserPassword(resetPasswordForm.value) ?.validate()
}).then(() => { .then(() => {
return userStore.logout() return UserApi.resetCurrentUserPassword(resetPasswordForm.value)
}).then(() => { })
router.push({ name: 'login' }) .then(() => {
return userStore.logout()
})
.then(() => {
router.push({ name: 'login' })
}) })
} }
const close = () => { resetPasswordDialog.value = false } const close = () => {
resetPasswordDialog.value = false
}
defineExpose({ open, close }) defineExpose({ open, close })
</script> </script>
<style lang="scss" scope> <style lang="scss" scope></style>
.code-input {
width: 250px;
}
.send-email-button {
margin-left: 12px;
width: 158px;
}
</style>

View File

@ -1,50 +1,39 @@
<template > <template>
<el-dropdown trigger="click" size="small" type="primary"> <el-dropdown trigger="click" type="primary">
<el-avatar> {{ firstUserName }} </el-avatar> <el-avatar :size="30"> {{ firstUserName }} </el-avatar>
<template #dropdown> <template #dropdown>
<el-dropdown-menu> <el-dropdown-menu>
<el-dropdown-item @click="openResetPassword"> <el-dropdown-item @click="openResetPassword">
<AppIcon iconName="password"></AppIcon><span style="margin-left:5px">修改密码</span> <AppIcon iconName="Lock"></AppIcon><span style="margin-left: 5px">修改密码</span>
</el-dropdown-item> </el-dropdown-item>
<el-dropdown-item @click="logout"> <el-dropdown-item @click="logout">
<AppIcon iconName="exit"></AppIcon><span style="margin-left:5px">退出</span> <AppIcon iconName="app-exit"></AppIcon><span style="margin-left: 5px">退出</span>
</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>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed, ref } from "vue"; import { computed, ref } from 'vue'
import { useUserStore } from '@/stores/user'; import { useUserStore } from '@/stores/user'
import { useRouter } from "vue-router"; import { useRouter } from 'vue-router'
import AppIcon from "@/components/icons/AppIcon.vue" import ResetPassword from '@/components/layout/top-bar/components/avatar/ResetPasssword.vue'
import ResetPassword from "@/components/layout/top-bar/components/avatar/ResetPasssword.vue"
const userStore = useUserStore() const userStore = useUserStore()
const router = useRouter() const router = useRouter()
const firstUserName = computed(() => { const firstUserName = computed(() => {
return userStore.userInfo?.username?.substring(0, 1) return userStore.userInfo?.username?.substring(0, 1)
}) })
const resetPasswordRef = ref<InstanceType<typeof ResetPassword>>(); const resetPasswordRef = ref<InstanceType<typeof ResetPassword>>()
const openResetPassword = () => { const openResetPassword = () => {
resetPasswordRef.value?.open() resetPasswordRef.value?.open()
} }
const logout = () => { const logout = () => {
userStore.logout().then(() => { userStore.logout().then(() => {
router.push({ name: "login" }) router.push({ name: 'login' })
}) })
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped></style>
.el-avatar {
--el-avatar-size: 30px;
--el-avatar-bg-color: var(--app-base-action-text-color);
cursor: pointer;
}
.el-dropdown-menu--small {
padding: 10px 0;
}
</style>

View File

@ -1,40 +1,46 @@
<template> <template>
<div class="menu-item-container" :class="isActive ? 'active' : ''" @click="router.push({ name: menu.name })"> <div
<div class="icon"> class="menu-item-container flex-center h-full"
<AppIcon :iconName="menu.meta ? menu.meta.icon as string : '404'"></AppIcon> :class="isActive ? 'active' : ''"
</div> @click="router.push({ name: menu.name })"
<div class="title">{{ menu.meta?.title }} </div> >
<div class="icon">
<AppIcon :iconName="menu.meta ? (menu.meta.icon as string) : '404'" />
</div> </div>
<div class="title">{{ menu.meta?.title }}</div>
</div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { useRouter, useRoute, type RouteRecordRaw } from 'vue-router' import { useRouter, useRoute, type RouteRecordRaw } from 'vue-router'
import { computed } from "vue"; import { computed } from 'vue'
import AppIcon from "@/components/icons/AppIcon.vue" const router = useRouter()
const router = useRouter(); const route = useRoute()
const route = useRoute();
const props = defineProps<{ const props = defineProps<{
menu: RouteRecordRaw menu: RouteRecordRaw
}>() }>()
const isActive = computed(() => { const isActive = computed(() => {
return route.name == props.menu.name && route.path == props.menu.path return route.name == props.menu.name && route.path == props.menu.path
}) })
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.menu-item-container { .menu-item-container {
padding: 0 20px; padding: 0 20px;
display: flex; cursor: pointer;
justify-content: center; .icon {
align-items: center; font-size: 15px;
cursor: pointer; margin-right: 5px;
height: 100%; margin-top: 2px;
}
&:hover {
color: var(--el-color-primary);
}
} }
.active { .active {
background-color: var(--app-base-text-hover-bg-color); font-weight: 600;
border-bottom: 3px solid var(--app-base-text-hover-color); color: var(--el-color-primary);
height: calc(100% - 3px); background-color: var(--el-color-primary-light-9);
border-bottom: 3px solid var(--el-color-primary);
} }
</style> </style>

View File

@ -1,22 +1,25 @@
<template > <template>
<div class="top-menu-container"> <div class="top-menu-container flex h-full">
<MenuItem :menu="menu" v-hasPermission="menu.meta?.permission" v-for="(menu, index) in topMenuList" :key="index"> <MenuItem
</MenuItem> :menu="menu"
</div> v-hasPermission="menu.meta?.permission"
v-for="(menu, index) in topMenuList"
:key="index"
>
</MenuItem>
</div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed } from "vue"; import { computed } from 'vue'
import { getChildRouteListByPathAndName } from "@/router/index" import { getChildRouteListByPathAndName } from '@/router/index'
import MenuItem from "@/components/layout/top-bar/components/top-menu/MenuItem.vue" import MenuItem from '@/components/layout/top-bar/components/top-menu/MenuItem.vue'
const topMenuList = computed(() => { const topMenuList = computed(() => {
return getChildRouteListByPathAndName("/", "home") return getChildRouteListByPathAndName('/', 'home')
}) })
</script> </script>
<style lang="scss" scope> <style lang="scss" scope>
.top-menu-container { .top-menu-container {
display: flex; align-items: center;
align-items: center; margin-bottom: -1px;
height: 100%;
} }
</style> </style>

View File

@ -1,14 +1,13 @@
<template> <template>
<div class="top-bar-container"> <div class="top-bar-container flex-between">
<div class="app-title-container"> <div class="flex-center h-full">
<div class="app-title-icon"></div> <div class="app-title-container flex-center">
<div class="app-title-text">{{ defaultTitle }}</div> <div class="app-title-icon"></div>
<div class="line"></div> <div class="app-title-text ml-1">{{ defaultTitle }}</div>
</div> </div>
<div class="app-top-menu-container"> <el-divider direction="vertical" class="line" />
<TopMenu></TopMenu> <TopMenu></TopMenu>
</div> </div>
<div class="flex-auto"></div>
<div class="avatar"> <div class="avatar">
<Avatar></Avatar> <Avatar></Avatar>
</div> </div>
@ -22,31 +21,12 @@ const defaultTitle = import.meta.env.VITE_APP_TITLE
<style lang="scss"> <style lang="scss">
.top-bar-container { .top-bar-container {
border-bottom: 1px solid rgba(229, 229, 229, 1); border-bottom: 1px solid rgba(229, 229, 229, 1);
height: calc(100% - 1px); height: var(--app-header-height);
background-color: var(--app-header-background-color, #fff); box-sizing: border-box;
width: 100vw; padding: var(--app-header-padding);
display: flex;
.flex-auto {
flex: 1 1 auto;
}
.avatar {
height: 100%;
width: 40px;
display: flex;
justify-content: center;
align-items: center;
margin-right: 10px;
}
.app-title-container { .app-title-container {
width: 200px; margin-right: 30px;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
.app-title-icon { .app-title-icon {
background-image: url('@/assets/logo.png'); background-image: url('@/assets/logo.png');
background-size: 100% 100%; background-size: 100% 100%;
@ -55,18 +35,13 @@ const defaultTitle = import.meta.env.VITE_APP_TITLE
} }
.app-title-text { .app-title-text {
color: var(--app-base-action-text-color); color: var(--el-color-primary);
font-size: 20px; font-size: 20px;
font-weight: 600; font-weight: 600;
align-items: center;
}
.line {
height: 60%;
width: 1px;
margin-left: 20px;
background-color: rgba(229, 229, 229, 1);
} }
} }
.line {
height: 2em;
}
} }
</style> </style>

View File

@ -1,5 +1,5 @@
import type { App } from 'vue' import type { App } from 'vue'
import { hasPermission } from '@/common/permission' import { hasPermission } from '@/utils/permission'
const display = async (el: any, binding: any) => { const display = async (el: any, binding: any) => {
const has = hasPermission( const has = hasPermission(

View File

@ -1,5 +1,5 @@
import axios, { type AxiosRequestConfig } from 'axios' import axios, { type AxiosRequestConfig } from 'axios'
import { ElMessage } from 'element-plus' import { MsgError } from '@/utils/message'
import type { NProgress } from 'nprogress' import type { NProgress } from 'nprogress'
import type { Ref } from 'vue' import type { Ref } from 'vue'
import type { Result } from '@/request/Result' import type { Result } from '@/request/Result'
@ -41,7 +41,7 @@ instance.interceptors.response.use(
(response: any) => { (response: any) => {
if (response.data) { if (response.data) {
if (response.data.code !== 200 && !(response.data instanceof Blob)) { if (response.data.code !== 200 && !(response.data instanceof Blob)) {
ElMessage.error(response.data.message) MsgError(response.data.message)
} }
} }
if (response.headers['content-type'] === 'application/octet-stream') { if (response.headers['content-type'] === 'application/octet-stream') {
@ -51,7 +51,7 @@ instance.interceptors.response.use(
}, },
(err: any) => { (err: any) => {
if (err.code === 'ECONNABORTED') { if (err.code === 'ECONNABORTED') {
ElMessage.error(err.message) MsgError(err.message)
console.error(err) console.error(err)
} }
if (err.response?.status === 401) { if (err.response?.status === 401) {
@ -59,7 +59,7 @@ instance.interceptors.response.use(
} }
if (err.response?.status === 403) { if (err.response?.status === 403) {
ElMessage.error( MsgError(
err.response.data && err.response.data.message ? err.response.data.message : '没有权限访问' err.response.data && err.response.data.message ? err.response.data.message : '没有权限访问'
) )
} }

View File

@ -1,4 +1,4 @@
import { hasPermission } from '@/common/permission/index' import { hasPermission } from '@/utils/permission/index'
import { import {
createRouter, createRouter,
createWebHistory, createWebHistory,
@ -8,7 +8,7 @@ import {
} from 'vue-router' } from 'vue-router'
import { useUserStore } from '@/stores/user' import { useUserStore } from '@/stores/user'
import { store } from '@/stores' import { store } from '@/stores'
import { routes } from '@/router/data' import { routes } from '@/router/routes'
const router = createRouter({ const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL), history: createWebHistory(import.meta.env.BASE_URL),
routes: routes routes: routes

View File

@ -0,0 +1,8 @@
const applicationRouter = {
path: '/app',
name: 'app',
meta: { icon: 'app-applicaiton', title: '应用', permission: 'APPLICATION:READ' },
component: () => import('@/views/app/index.vue')
}
export default applicationRouter

View File

@ -0,0 +1,8 @@
const datasetRouter = {
path: '/dataset',
name: 'dataset',
meta: { icon: 'app-dataset', title: '数据集', permission: 'DATASET:READ' },
component: () => import('@/views/dataset/index.vue')
}
export default datasetRouter

View File

@ -0,0 +1,8 @@
const settingRouter = {
path: '/setting',
name: 'setting',
meta: { icon: 'Setting', title: '系统设置', permission: 'SETTING:READ' },
component: () => import('@/views/setting/index.vue')
}
export default settingRouter

View File

@ -1,35 +1,22 @@
import type { RouteRecordRaw } from 'vue-router' import type { RouteRecordRaw } from 'vue-router'
import { Role } from '@/common/permission/type' import { Role } from '@/utils/permission/type'
const modules: any = import.meta.glob('./modules/*.ts', { eager: true })
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',
component: () => import('@/components/layout/home-layout/index.vue'), component: () => import('@/components/layout/app-layout/index.vue'),
children: [ children: [
{ {
path: '/first', path: '/first',
name: 'first', name: 'first',
meta: { icon: 'app', title: '首页' }, meta: { icon: 'House', title: '首页' },
component: () => import('@/views/first/index.vue') component: () => import('@/views/first/index.vue')
}, },
{ ...rolesRoutes
path: '/app',
name: 'app',
meta: { icon: 'app', title: '应用', permission: 'APPLICATION:READ' },
component: () => import('@/views/app/index.vue')
},
{
path: '/dataset',
name: 'dataset',
meta: { icon: 'dataset', title: '数据集', permission: 'DATASET:READ' },
component: () => import('@/views/dataset/index.vue')
},
{
path: '/setting',
name: 'setting',
meta: { icon: 'setting', title: '系统设置', permission: 'SETTING:READ' },
component: () => import('@/views/setting/index.vue')
}
] ]
}, },
{ {

View File

@ -9,15 +9,11 @@ html {
} }
body { body {
font-family:
Helvetica,
PingFang SC,
Arial,
sans-serif;
font-size: 14px; font-size: 14px;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
text-rendering: optimizeLegibility; -webkit-font-smoothing: antialiased;
font-family: 'Helvetica Neue', Helvetica, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei',
'微软雅黑', Arial, sans-serif;
height: 100%; height: 100%;
margin: 0; margin: 0;
padding: 0; padding: 0;
@ -95,6 +91,10 @@ ul {
margin-bottom: 20px; margin-bottom: 20px;
} }
.flex {
display: flex;
}
.flex-center { .flex-center {
display: flex; display: flex;
align-items: center; align-items: center;

View File

@ -1,3 +1,8 @@
.el-avatar {
--el-avatar-bg-color: var(--el-color-primary);
--el-avatar-size-small: 33px;
cursor: pointer;
}
.el-popper { .el-popper {
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
} }
@ -5,24 +10,22 @@
.el-form { .el-form {
--el-form-inline-content-width: 100%; --el-form-inline-content-width: 100%;
} }
// 抽屉样式整体修改 // 抽屉样式整体修改
.el-drawer{ .el-drawer {
.el-drawer__header {
.el-drawer__header{ padding: 0;
padding: 0; margin: 0 24px;
margin: 0 24px; height: 56px;
height: 56px; border-bottom: 1px solid #d5d6d8;
border-bottom: 1px solid #D5D6D8; .el-drawer__title {
.el-drawer__title {
color: #1f2329; color: #1f2329;
font-weight: 500; font-weight: 500;
font-size: 16px; font-size: 16px;
line-height: 24px; line-height: 24px;
} }
} }
.el-drawer__body{ .el-drawer__body {
--el-drawer-padding-primary:24px --el-drawer-padding-primary: 24px;
} }
} }

View File

@ -1,11 +1,14 @@
:root{ :root {
--app-base-text-color:rgba(31, 35, 41, 1); --el-color-primary: rgba(51, 112, 255, 1);
--app-base-text-font-size:14px; --app-base-text-color: rgba(31, 35, 41, 1);
--app-base-text-hover-color:rgba(51, 112, 255, 1); --app-base-text-font-size: 14px;
--app-base-text-hover-bg-color:rgba(51, 112, 255, 0.1); --app-base-text-hover-color: rgba(51, 112, 255, 1);
--app-base-action-text-color:var(--app-base-text-hover-color ); --app-base-text-hover-bg-color: rgba(51, 112, 255, 0.1);
/** header 组件 */ --app-base-action-text-color: var(--app-base-text-hover-color);
--app-header-height: 56px; /** header 组件 */
--app-header-padding: 0 20px; --app-header-height: 56px;
--app-header-bg-color: #252b3c; --app-header-padding: 0 20px;
--app-header-bg-color: #252b3c;
/** top-bar 组件 */
} }

45
ui/src/utils/message.ts Normal file
View File

@ -0,0 +1,45 @@
import { ElMessageBox, ElMessage } from 'element-plus'
export const MsgSuccess = (message: string) => {
ElMessage.success({
message: message,
type: 'success',
showClose: true,
duration: 1500
})
}
export const MsgInfo = (message: string) => {
ElMessage.info({
message: message,
type: 'info',
showClose: true,
duration: 1500
})
}
export const MsgWarning = (message: string) => {
ElMessage.warning({
message: message,
type: 'warning',
showClose: true,
duration: 1500
})
}
export const MsgError = (message: string) => {
ElMessage.error({
message: message,
type: 'error',
showClose: true,
duration: 1500
})
}
export const MsgConfirm = (message: string, options = {}) => {
const defaultOptions: Object = {
type: 'warning',
...options
}
return ElMessageBox.confirm(message, '确认', defaultOptions)
}

View File

@ -1,6 +1,6 @@
import { store } from '@/stores' import { store } from '@/stores'
import { useUserStore } from '@/stores/user' import { useUserStore } from '@/stores/user'
import { Role, Permission, ComplexPermission } from '@/common/permission/type' import { Role, Permission, ComplexPermission } from '@/utils/permission/type'
/** /**
* *
* @param permission * @param permission

View File

@ -1,16 +1,19 @@
<template > <template>
<div> <div>
<el-button v-hasPermission="'USER:DELETE'">用户删除权限</el-button> <el-button v-hasPermission="'USER:DELETE'">用户删除权限</el-button>
<el-button v-hasPermission="'USER:READ'">用户只读权限</el-button> <el-button v-hasPermission="'USER:READ'">用户只读权限</el-button>
<el-button v-hasPermission="new Role('USER')">普通用户角色</el-button> <el-button v-hasPermission="new Role('USER')">普通用户角色</el-button>
<el-button v-hasPermission="[new Role('ADMIN'), new Role('USER')]">普通用户或者管理员</el-button> <el-button v-hasPermission="[new Role('ADMIN'), new Role('USER')]"
<el-button >普通用户或者管理员</el-button
v-hasPermission="{ permission: ['USER:READ', new Role('USER')], compare: 'AND' }">普通角色并且用户只读权限</el-button> >
<el-button v-hasPermission="{ permission: ['USER:READ', new Role('USER')], compare: 'AND' }"
>普通角色并且用户只读权限</el-button
>
首页 首页
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { Role } from "@/common/permission/type" import { Role } from '@/utils/permission/type'
</script> </script>
<style lang="scss" scoped></style> <style lang="scss" scoped></style>

View File

@ -16,7 +16,7 @@
placeholder="请输入邮箱" placeholder="请输入邮箱"
> >
<template #prepend> <template #prepend>
<el-button :icon="UserFilled" /> <el-button icon="UserFilled" />
</template> </template>
</el-input> </el-input>
</el-form-item> </el-form-item>
@ -29,7 +29,7 @@
placeholder="请输入验证码" placeholder="请输入验证码"
> >
<template #prepend> <template #prepend>
<el-button :icon="Key" /> <el-button icon="Key" />
</template> </template>
</el-input> </el-input>
<el-button <el-button
@ -61,12 +61,11 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue' import { ref } from 'vue'
import { UserFilled, Key } from '@element-plus/icons-vue'
import type { CheckCodeRequest } from '@/api/user/type' import type { CheckCodeRequest } from '@/api/user/type'
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 UserApi from '@/api/user/index' import UserApi from '@/api/user/index'
import { ElMessage } from 'element-plus' import { MsgSuccess } from '@/utils/message'
const router = useRouter() const router = useRouter()
const CheckEmailForm = ref<CheckCodeRequest>({ const CheckEmailForm = ref<CheckCodeRequest>({
@ -108,7 +107,7 @@ const sendEmail = () => {
resetPasswordFormRef.value?.validateField('email', (v: boolean) => { resetPasswordFormRef.value?.validateField('email', (v: boolean) => {
if (v) { if (v) {
UserApi.sendEmit(CheckEmailForm.value.email, 'reset_password', loading).then(() => { UserApi.sendEmit(CheckEmailForm.value.email, 'reset_password', loading).then(() => {
ElMessage.success('发送验证码成功') MsgSuccess('发送验证码成功')
}) })
} }
}) })

View File

@ -100,7 +100,7 @@ import type { RegisterRequest } from '@/api/user/type'
import { UserFilled, Lock, Message, Key } from '@element-plus/icons-vue' import { UserFilled, Lock, Message, Key } from '@element-plus/icons-vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import UserApi from '@/api/user/index' import UserApi from '@/api/user/index'
import { ElMessage } from 'element-plus' import { MsgSuccess } from '@/utils/message'
import type { FormInstance, FormRules } from 'element-plus' import type { FormInstance, FormRules } from 'element-plus'
const router = useRouter() const router = useRouter()
@ -192,7 +192,7 @@ const sendEmail = () => {
registerFormRef.value?.validateField('email', (v: boolean) => { registerFormRef.value?.validateField('email', (v: boolean) => {
if (v) { if (v) {
UserApi.sendEmit(registerForm.value.email, 'register', sendEmailLoading).then(() => { UserApi.sendEmit(registerForm.value.email, 'register', sendEmailLoading).then(() => {
ElMessage.success('发送验证码成功') MsgSuccess('发送验证码成功')
}) })
} }
}) })

View File

@ -18,7 +18,7 @@
show-password show-password
> >
<template #prepend> <template #prepend>
<el-button :icon="Lock" /> <el-button icon="Lock" />
</template> </template>
</el-input> </el-input>
</el-form-item> </el-form-item>
@ -32,7 +32,7 @@
show-password show-password
> >
<template #prepend> <template #prepend>
<el-button :icon="Lock" /> <el-button icon="Lock" />
</template> </template>
</el-input> </el-input>
</el-form-item> </el-form-item>
@ -57,9 +57,8 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted } from 'vue' import { ref, onMounted } from 'vue'
import type { ResetPasswordRequest } from '@/api/user/type' import type { ResetPasswordRequest } from '@/api/user/type'
import { Lock } from '@element-plus/icons-vue'
import { useRouter, useRoute } from 'vue-router' import { useRouter, useRoute } from 'vue-router'
import { ElMessage } from 'element-plus' import { MsgSuccess } from '@/utils/message'
import type { FormInstance, FormRules } from 'element-plus' import type { FormInstance, FormRules } from 'element-plus'
import UserApi from '@/api/user/index' import UserApi from '@/api/user/index'
const router = useRouter() const router = useRouter()
@ -127,7 +126,7 @@ const resetPassword = () => {
?.validate() ?.validate()
.then(() => UserApi.resetPassword(resetPasswordForm.value, loading)) .then(() => UserApi.resetPassword(resetPasswordForm.value, loading))
.then(() => { .then(() => {
ElMessage.success('修改密码成功') MsgSuccess('修改密码成功')
router.push({ name: 'login' }) router.push({ name: 'login' })
}) })
} }