wip: vite版重构中
This commit is contained in:
246
src/components/Error/404.vue
Normal file
246
src/components/Error/404.vue
Normal file
@@ -0,0 +1,246 @@
|
||||
<template>
|
||||
<div class="wscn-http404-container">
|
||||
<div class="wscn-http404">
|
||||
<div class="pic-404">
|
||||
<img class="pic-404__parent" src="@/assets/img/404.png" alt="404" />
|
||||
<img class="pic-404__child left" src="@/assets/img/404_cloud.png" alt="404" />
|
||||
<img class="pic-404__child mid" src="@/assets/img/404_cloud.png" alt="404" />
|
||||
<img class="pic-404__child right" src="@/assets/img/404_cloud.png" alt="404" />
|
||||
</div>
|
||||
<div class="bullshit">
|
||||
<div class="bullshit__oops"> OOPS! </div>
|
||||
<div class="bullshit__headline">
|
||||
{{ message }}
|
||||
</div>
|
||||
<div class="bullshit__info"> 请检查您输入的网址是否正确,请点击以下按钮返回主页 </div>
|
||||
<router-link to="/">
|
||||
<a href="" class="bullshit__return-home">返回首页</a>
|
||||
</router-link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="404">
|
||||
import { ref } from 'vue'
|
||||
const message = ref<string>('网管说这个页面你不能进......')
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.wscn-http404-container {
|
||||
position: absolute;
|
||||
top: 40%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
.wscn-http404 {
|
||||
position: relative;
|
||||
width: 1200px;
|
||||
padding: 0 50px;
|
||||
overflow: hidden;
|
||||
|
||||
.pic-404 {
|
||||
position: relative;
|
||||
float: left;
|
||||
width: 600px;
|
||||
overflow: hidden;
|
||||
|
||||
&__parent {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&__child {
|
||||
@keyframes cloudLeft {
|
||||
0% {
|
||||
top: 17px;
|
||||
left: 220px;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
20% {
|
||||
top: 33px;
|
||||
left: 188px;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
80% {
|
||||
top: 81px;
|
||||
left: 92px;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
100% {
|
||||
top: 97px;
|
||||
left: 60px;
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
@keyframes cloudMid {
|
||||
0% {
|
||||
top: 10px;
|
||||
left: 420px;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
20% {
|
||||
top: 40px;
|
||||
left: 360px;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
70% {
|
||||
top: 130px;
|
||||
left: 180px;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
100% {
|
||||
top: 160px;
|
||||
left: 120px;
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
@keyframes cloudRight {
|
||||
0% {
|
||||
top: 100px;
|
||||
left: 500px;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
20% {
|
||||
top: 120px;
|
||||
left: 460px;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
80% {
|
||||
top: 180px;
|
||||
left: 340px;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
100% {
|
||||
top: 200px;
|
||||
left: 300px;
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
position: absolute;
|
||||
|
||||
&.left {
|
||||
top: 17px;
|
||||
left: 220px;
|
||||
width: 80px;
|
||||
opacity: 0;
|
||||
animation-delay: 1s;
|
||||
animation-duration: 2s;
|
||||
animation-name: cloudLeft;
|
||||
animation-timing-function: linear;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
|
||||
&.mid {
|
||||
top: 10px;
|
||||
left: 420px;
|
||||
width: 46px;
|
||||
opacity: 0;
|
||||
animation-delay: 1.2s;
|
||||
animation-duration: 2s;
|
||||
animation-name: cloudMid;
|
||||
animation-timing-function: linear;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
|
||||
&.right {
|
||||
top: 100px;
|
||||
left: 500px;
|
||||
width: 62px;
|
||||
opacity: 0;
|
||||
animation-delay: 1s;
|
||||
animation-duration: 2s;
|
||||
animation-name: cloudRight;
|
||||
animation-timing-function: linear;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.bullshit {
|
||||
@keyframes slideUp {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: translateY(60px);
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
position: relative;
|
||||
float: left;
|
||||
width: 300px;
|
||||
padding: 30px 0;
|
||||
overflow: hidden;
|
||||
|
||||
&__oops {
|
||||
margin-bottom: 20px;
|
||||
font-size: 32px;
|
||||
font-weight: bold;
|
||||
line-height: 40px;
|
||||
color: #1482f0;
|
||||
opacity: 0;
|
||||
animation-duration: 0.5s;
|
||||
animation-name: slideUp;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
|
||||
&__headline {
|
||||
margin-bottom: 10px;
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
line-height: 24px;
|
||||
color: #222;
|
||||
opacity: 0;
|
||||
animation-delay: 0.1s;
|
||||
animation-duration: 0.5s;
|
||||
animation-name: slideUp;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
|
||||
&__info {
|
||||
margin-bottom: 30px;
|
||||
font-size: 13px;
|
||||
line-height: 21px;
|
||||
color: grey;
|
||||
opacity: 0;
|
||||
animation-delay: 0.2s;
|
||||
animation-duration: 0.5s;
|
||||
animation-name: slideUp;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
|
||||
&__return-home {
|
||||
display: block;
|
||||
float: left;
|
||||
width: 110px;
|
||||
height: 36px;
|
||||
font-size: 14px;
|
||||
line-height: 36px;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
background: #1482f0;
|
||||
border-radius: 100px;
|
||||
opacity: 0;
|
||||
animation-delay: 0.3s;
|
||||
animation-duration: 0.5s;
|
||||
animation-name: slideUp;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,52 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
|
||||
defineProps<{ msg: string }>()
|
||||
|
||||
const count = ref(0)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<h1>{{ msg }}</h1>
|
||||
|
||||
<p>
|
||||
Recommended IDE setup:
|
||||
<a href="https://code.visualstudio.com/" target="_blank">VSCode</a>
|
||||
+
|
||||
<a href="https://github.com/johnsoncodehk/volar" target="_blank">Volar</a>
|
||||
</p>
|
||||
|
||||
<p>See <code>README.md</code> for more information.</p>
|
||||
|
||||
<p>
|
||||
<a href="https://vitejs.dev/guide/features.html" target="_blank">
|
||||
Vite Docs
|
||||
</a>
|
||||
|
|
||||
<a href="https://v3.vuejs.org/" target="_blank">Vue 3 Docs</a>
|
||||
</p>
|
||||
|
||||
<button type="button" @click="count++">count is: {{ count }}</button>
|
||||
<p>
|
||||
Edit
|
||||
<code>components/HelloWorld.vue</code> to test hot module replacement.
|
||||
</p>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
a {
|
||||
color: #42b983;
|
||||
}
|
||||
|
||||
label {
|
||||
margin: 0 0.5em;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
code {
|
||||
background-color: #eee;
|
||||
padding: 2px 4px;
|
||||
border-radius: 4px;
|
||||
color: #304455;
|
||||
}
|
||||
</style>
|
||||
23
src/components/Message/index.ts
Normal file
23
src/components/Message/index.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { ElMessage } from 'element-plus'
|
||||
|
||||
let messageInstance: Nullable<any> = null
|
||||
|
||||
const resetMessage = (options: any) => {
|
||||
if (messageInstance) {
|
||||
messageInstance.close()
|
||||
}
|
||||
messageInstance = ElMessage(options)
|
||||
}
|
||||
;['error', 'success', 'info', 'warning'].forEach((type: string) => {
|
||||
resetMessage[type] = (options: any) => {
|
||||
if (typeof options === 'string') {
|
||||
options = {
|
||||
message: options
|
||||
}
|
||||
}
|
||||
options.type = type
|
||||
return resetMessage(options)
|
||||
}
|
||||
})
|
||||
|
||||
export const Message = resetMessage as any
|
||||
16
src/components/ParentView/index.vue
Normal file
16
src/components/ParentView/index.vue
Normal file
@@ -0,0 +1,16 @@
|
||||
<template>
|
||||
<div>
|
||||
<router-view>
|
||||
<template #default="{ Component, route }">
|
||||
<keep-alive :include="getCaches">
|
||||
<component :is="Component" :key="route.fullPath" />
|
||||
</keep-alive>
|
||||
</template>
|
||||
</router-view>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts" name="ParentView">
|
||||
import { useCache } from './useCache'
|
||||
|
||||
const { getCaches } = useCache(false)
|
||||
</script>
|
||||
55
src/components/ParentView/useCache.ts
Normal file
55
src/components/ParentView/useCache.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import { computed, ref, unref, ComponentInternalInstance, getCurrentInstance } from 'vue'
|
||||
|
||||
import { useTagsViewStoreWithOut, PAGE_LAYOUT_KEY } from '@/store/modules/tags-view'
|
||||
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
function tryTsxEmit<T extends any = ComponentInternalInstance>(
|
||||
fn: (_instance: T) => Promise<void> | void
|
||||
) {
|
||||
const instance = getCurrentInstance() as any
|
||||
instance && fn.call(null, instance)
|
||||
}
|
||||
|
||||
const ParentLayoutName = 'ParentLayout'
|
||||
export function useCache(isPage: boolean) {
|
||||
const tagsViewStore = useTagsViewStoreWithOut()
|
||||
const name = ref('')
|
||||
const { currentRoute } = useRouter()
|
||||
|
||||
tryTsxEmit((instance) => {
|
||||
const routeName = instance.type.name
|
||||
if (routeName && ![ParentLayoutName].includes(routeName)) {
|
||||
name.value = routeName
|
||||
} else {
|
||||
const matched = currentRoute.value.matched
|
||||
const len = matched.length
|
||||
if (len < 2) return
|
||||
name.value = matched[len - 2].name as string
|
||||
}
|
||||
})
|
||||
|
||||
const getCaches = computed((): string[] => {
|
||||
const cached = tagsViewStore.getCachedViews
|
||||
|
||||
if (isPage) {
|
||||
// page Layout
|
||||
// not parent layout
|
||||
return (cached as any).get(PAGE_LAYOUT_KEY) || []
|
||||
}
|
||||
const cacheSet = new Set<string>()
|
||||
cacheSet.add(unref(name))
|
||||
|
||||
const list = (cached as any).get(unref(name))
|
||||
|
||||
if (!list) {
|
||||
return Array.from(cacheSet)
|
||||
}
|
||||
;(list as string[]).forEach((item: string) => {
|
||||
cacheSet.add(item)
|
||||
})
|
||||
|
||||
return Array.from(cacheSet)
|
||||
})
|
||||
return { getCaches }
|
||||
}
|
||||
16
src/components/Redirect/index.vue
Normal file
16
src/components/Redirect/index.vue
Normal file
@@ -0,0 +1,16 @@
|
||||
<template>
|
||||
<div></div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { unref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
const { currentRoute, replace } = useRouter()
|
||||
const { params, query } = unref(currentRoute)
|
||||
const { path } = params
|
||||
const _path = Array.isArray(path) ? path.join('/') : path
|
||||
replace({
|
||||
path: '/' + _path,
|
||||
query
|
||||
})
|
||||
</script>
|
||||
41
src/components/SvgIcon/index.vue
Normal file
41
src/components/SvgIcon/index.vue
Normal file
@@ -0,0 +1,41 @@
|
||||
<template>
|
||||
<svg :class="svgClass" aria-hidden="true" v-on="$attrs">
|
||||
<use :xlink:href="iconName" />
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="SvgIcon">
|
||||
import { PropType, computed } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
// svg文件名
|
||||
iconClass: {
|
||||
type: String as PropType<string>,
|
||||
required: true
|
||||
},
|
||||
// 自定义类名
|
||||
className: {
|
||||
type: String as PropType<string>,
|
||||
default: ''
|
||||
}
|
||||
})
|
||||
|
||||
const iconName = computed(() => `#icon-${props.iconClass}`)
|
||||
const svgClass = computed(() => {
|
||||
if (props.className) {
|
||||
return `svg-icon ${props.className}`
|
||||
} else {
|
||||
return 'svg-icon'
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.svg-icon {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
overflow: hidden;
|
||||
vertical-align: -0.15em;
|
||||
fill: currentColor;
|
||||
}
|
||||
</style>
|
||||
6
src/components/index.ts
Normal file
6
src/components/index.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import type { App } from 'vue'
|
||||
import SvgIcon from './SvgIcon/index.vue' // svg组件
|
||||
|
||||
export function setupGlobCom(app: App<Element>): void {
|
||||
app.component('SvgIcon', SvgIcon)
|
||||
}
|
||||
Reference in New Issue
Block a user