Skip to content

Commit cedd21e

Browse files
committed
feat(directive): ✨ 增加loading指令
1 parent a6ca2b2 commit cedd21e

13 files changed

Lines changed: 173 additions & 14 deletions

File tree

eslint.config.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,9 @@ export default defineConfig([
7474
// TS未使用变量警告
7575
'@typescript-eslint/no-unused-vars': 'error',
7676
// Vue未使用组件(质量规则)
77-
'vue/no-unused-components': 'warn'
77+
'vue/no-unused-components': 'warn',
78+
// 关闭从vue导出部分包,例如:@vue/shared
79+
'vue/prefer-import-from-vue': 'off'
7880
}
7981
},
8082
// 6. 排除文件(全局生效),全局忽略规则(同时作用于ESLint和Prettier),可以不用单独配置.prettierignore和.eslintignore文件

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
},
6464
"dependencies": {
6565
"@nuxt/ui": "^4.5.1",
66+
"@vue/shared": "^3.5.29",
6667
"@vueuse/core": "^14.2.1",
6768
"alova": "^3.5.1",
6869
"tailwindcss": "^4.2.1",

pnpm-lock.yaml

Lines changed: 6 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/assets/styles/index.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
@import 'tailwindcss' prefix(anyfork);
1+
@import 'tailwindcss';
22
@import '@nuxt/ui';

src/directive/index.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import type { App, Directive } from 'vue'
2+
import focus from './modules/focus'
3+
import loading from './modules/loading/index'
4+
const directivesList: Record<string, Directive> = {
5+
// focus指令
6+
focus,
7+
//loading指令
8+
loading
9+
}
10+
const directives = {
11+
install: function (app: App<Element>) {
12+
Object.keys(directivesList).forEach((key) => {
13+
// 注册自定义指令
14+
app.directive(key, directivesList[key])
15+
})
16+
}
17+
}
18+
export default directives

src/directive/modules/focus.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import type { Directive } from 'vue'
2+
3+
/**
4+
* 自定义focus指令
5+
*/
6+
const directive: Directive = {
7+
mounted(el: HTMLElement) {
8+
el.focus()
9+
}
10+
}
11+
export default directive
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<template>
2+
<div v-if="loading" class="bg-elevated/90 absolute top-0 left-0 z-100000000 flex h-full max-h-screen w-full items-center justify-center">
3+
<div class="flex w-40 flex-col items-center justify-center">
4+
<div class="text-primary flex items-center">
5+
<svg xmlns="http://www.w3.org/2000/svg" width="2em" height="2em" viewBox="0 0 24 24">
6+
<path fill="currentColor" d="M12 2A10 10 0 1 0 22 12A10 10 0 0 0 12 2Zm0 18a8 8 0 1 1 8-8A8 8 0 0 1 12 20Z" opacity="0.5" />
7+
<path fill="currentColor" d="M20 12h2A10 10 0 0 0 12 2V4A8 8 0 0 1 20 12Z"><animateTransform attributeName="transform" dur="1s" from="0 12 12" repeatCount="indefinite" to="360 12 12" type="rotate" /></path>
8+
</svg>
9+
</div>
10+
<div class="text-primary text-[16px]">{{ loadingText }}</div>
11+
</div>
12+
</div>
13+
</template>
14+
15+
<script setup lang="ts">
16+
import type { LoadingProps } from '../types'
17+
const { loadingText, loading } = defineProps<Partial<LoadingProps>>()
18+
</script>
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { createVNode, render } from 'vue'
2+
import Loading from '../component/Loading.vue'
3+
import type { LoadingProps } from '../types/index'
4+
5+
/**
6+
* 创建、更新和销毁 v-loading 的实例
7+
*/
8+
export const createLoading = (props: Partial<LoadingProps>, target: HTMLElement) => {
9+
const data = reactive({
10+
...props
11+
})
12+
const Comp = defineComponent({
13+
render: () => h(Loading, data)
14+
})
15+
const vm = createVNode(Comp)
16+
const container = document.createElement('div')
17+
// 将虚拟节点挂载到容器上
18+
render(vm, container)
19+
target.appendChild(vm.el as HTMLElement)
20+
if (props.fullscreen) {
21+
target.classList.add('overflow-y-hidden')
22+
} else {
23+
target.classList.remove('overflow-y-hidden')
24+
}
25+
return {
26+
update: (newProps: Partial<LoadingProps>) => Object.assign(data, newProps),
27+
destroy: () => render(null, container)
28+
}
29+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import { hyphenate } from '@vue/shared'
2+
import { type Directive, type DirectiveBinding } from 'vue'
3+
import { createLoading } from './hooks/createLoading'
4+
5+
const INSTANCE_KEY = Symbol('loading')
6+
7+
const getAttributeName = (name: string) => {
8+
return `loading-${hyphenate(name)}`
9+
}
10+
/**
11+
* 定义挂着对象类型
12+
*/
13+
export interface loading extends HTMLElement {
14+
[INSTANCE_KEY]: ReturnType<typeof createLoading>
15+
}
16+
17+
// 获取指令传递的参数
18+
const getProps = (el: loading, binding: DirectiveBinding<boolean>) => {
19+
const loadingText = el.getAttribute(getAttributeName('text'))
20+
const fullscreen = binding.modifiers.fullscreen
21+
return {
22+
loadingText: loadingText ?? 'loading',
23+
loading: binding.value,
24+
fullscreen
25+
}
26+
}
27+
28+
/**
29+
* 自定义loading指令
30+
*/
31+
const directive: Directive<loading, boolean> = {
32+
mounted(el, binding) {
33+
if (binding.value) {
34+
const props = getProps(el, binding)
35+
console.log(props)
36+
if (!props.fullscreen) {
37+
el.classList.add('relative')
38+
}
39+
el[INSTANCE_KEY] = createLoading(props, props.fullscreen ? document.body : el)
40+
}
41+
},
42+
updated(el, binding) {
43+
if (binding.oldValue !== binding.value) {
44+
if (binding.value && !binding.oldValue) {
45+
const props = getProps(el, binding)
46+
if (!props.fullscreen) {
47+
el.classList.add('relative')
48+
}
49+
el[INSTANCE_KEY] = createLoading(props, props.fullscreen ? document.body : el)
50+
} else if (!binding.value && binding.oldValue) {
51+
el[INSTANCE_KEY]?.update(getProps(el, binding))
52+
}
53+
if (binding.value) {
54+
el.classList.remove('overflow-y-auto')
55+
el.classList.add('overflow-y-hidden')
56+
} else {
57+
el.classList.remove('overflow-y-hidden')
58+
el.classList.add('overflow-y-auto')
59+
}
60+
}
61+
},
62+
unmounted(el) {
63+
el[INSTANCE_KEY]?.destroy()
64+
}
65+
}
66+
export default directive
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
export interface LoadingProps {
2+
/**
3+
* 指令值
4+
*/
5+
loading: boolean
6+
/**
7+
* loading文本
8+
*/
9+
loadingText?: string
10+
/**
11+
* 是否全屏
12+
*/
13+
fullscreen?: boolean
14+
}

0 commit comments

Comments
 (0)