vue-next-yui 1.0.4 → 1.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.vscode/extensions.json +3 -0
- package/README.md +5 -0
- package/example/App.vue +23 -0
- package/example/main.js +4 -0
- package/index.html +13 -0
- package/package.json +20 -37
- package/packages/badge/index.js +0 -0
- package/packages/badge/index.scss +91 -0
- package/packages/badge/index.vue +76 -0
- package/packages/index.js +0 -0
- package/scripts/index.js +17 -0
- package/scripts/publish.cmd +34 -0
- package/src/components/imgViewer/directive.js +43 -0
- package/src/components/imgViewer/index.js +41 -0
- package/src/components/imgViewer/index.scss +79 -0
- package/src/components/imgViewer/index.vue +155 -0
- package/src/components/index.js +18 -0
- package/src/components/loading/directive.js +40 -0
- package/src/components/loading/index.js +70 -0
- package/src/components/loading/index.scss +63 -0
- package/src/components/loading/index.vue +60 -0
- package/src/components/mask/directive.js +49 -0
- package/src/components/mask/index.vue +93 -0
- package/src/components/watermark/directive.js +28 -0
- package/src/components/watermark/index.vue +97 -0
- package/src/components/watermark/useWatermark.js +45 -0
- package/src/directives/index.js +24 -0
- package/src/directives/outside.js +16 -0
- package/src/index.js +32 -0
- package/src/style/index.css +0 -0
- package/src/utils/pick.js +49 -0
- package/src/utils/types.js +33 -0
- package/tsconfig.json +28 -0
- package/vite.config.js +55 -0
- package/vite.configAll.js +75 -0
- package/viteConfig/directives.js +42 -0
- package/viteConfig/utils.js +0 -0
- package/favicon.ico +0 -0
- package/style.css +0 -1
- package/vue-next-yui.es.js +0 -248
- package/vue-next-yui.umd.js +0 -1
package/README.md
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
# Vue 3 + Vite
|
|
2
|
+
|
|
3
|
+
This template should help get you started developing with Vue 3 in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
|
|
4
|
+
|
|
5
|
+
Learn more about IDE Support for Vue in the [Vue Docs Scaling up Guide](https://vuejs.org/guide/scaling-up/tooling.html#ide-support).
|
package/example/App.vue
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import Watermark from "../src/components/watermark/index.vue"
|
|
3
|
+
// import {Watermark} from "../dist/components/watermark"
|
|
4
|
+
import {reactive} from "vue";
|
|
5
|
+
|
|
6
|
+
const options = reactive({
|
|
7
|
+
content:'QUI组件库',
|
|
8
|
+
angle:45,
|
|
9
|
+
fontSize:24,
|
|
10
|
+
fontColor:'#3565ee'
|
|
11
|
+
});
|
|
12
|
+
</script>
|
|
13
|
+
|
|
14
|
+
<template>
|
|
15
|
+
<div>测试页面</div>
|
|
16
|
+
<Watermark content="Watermark" :fontSize="30" :width="240" :height="240">
|
|
17
|
+
<div style="height: 400px;"></div>
|
|
18
|
+
</Watermark>
|
|
19
|
+
</template>
|
|
20
|
+
|
|
21
|
+
<style scoped>
|
|
22
|
+
|
|
23
|
+
</style>
|
package/example/main.js
ADDED
package/index.html
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
|
+
<title>VUE-NEXT-YUI</title>
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<div id="app"></div>
|
|
11
|
+
<script type="module" src="/example/main.js"></script>
|
|
12
|
+
</body>
|
|
13
|
+
</html>
|
package/package.json
CHANGED
|
@@ -1,38 +1,21 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "vue-next-yui",
|
|
3
|
-
"
|
|
4
|
-
"
|
|
5
|
-
"type": "module",
|
|
6
|
-
"scripts": {
|
|
7
|
-
"dev": "vite",
|
|
8
|
-
"build": "
|
|
9
|
-
"preview": "vite preview"
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
"
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
"
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
"vue": "^3.4.29",
|
|
22
|
-
"vue-router": "^4.3.3"
|
|
23
|
-
},
|
|
24
|
-
"devDependencies": {
|
|
25
|
-
"@vitejs/plugin-vue": "^5.0.5",
|
|
26
|
-
"sass": "^1.77.8",
|
|
27
|
-
"vite": "^5.3.1"
|
|
28
|
-
},
|
|
29
|
-
"keywords": [
|
|
30
|
-
"vue-next-yui",
|
|
31
|
-
"npm",
|
|
32
|
-
"package",
|
|
33
|
-
"vue",
|
|
34
|
-
"vue3"
|
|
35
|
-
],
|
|
36
|
-
"author": "shawyu",
|
|
37
|
-
"license": "MIT"
|
|
1
|
+
{
|
|
2
|
+
"name": "vue-next-yui",
|
|
3
|
+
"private": false,
|
|
4
|
+
"version": "1.0.5",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "vite",
|
|
8
|
+
"build": "node scripts/index.js",
|
|
9
|
+
"preview": "vite preview",
|
|
10
|
+
"publish": "call ./scripts/build-publish.cmd"
|
|
11
|
+
},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"vue": "^3.5.13"
|
|
14
|
+
},
|
|
15
|
+
"devDependencies": {
|
|
16
|
+
"@vitejs/plugin-vue": "^6.0.3",
|
|
17
|
+
"sass": "^1.97.0",
|
|
18
|
+
"vite": "7.3.0",
|
|
19
|
+
"vite-plugin-dts": "^4.5.4"
|
|
20
|
+
}
|
|
38
21
|
}
|
|
File without changes
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
.yu-badge-wrapper{
|
|
2
|
+
position: relative;
|
|
3
|
+
display: inline-block;
|
|
4
|
+
vertical-align: middle;
|
|
5
|
+
width: fit-content;
|
|
6
|
+
.yu-badge_content{
|
|
7
|
+
position: relative;
|
|
8
|
+
z-index: 1;
|
|
9
|
+
}
|
|
10
|
+
.yu-badge_value{
|
|
11
|
+
position: absolute;
|
|
12
|
+
top: 0;
|
|
13
|
+
right: 0;
|
|
14
|
+
z-index: 2;
|
|
15
|
+
transform: translate(50%,-50%);
|
|
16
|
+
line-height: 0;
|
|
17
|
+
width: fit-content;
|
|
18
|
+
&.yu-badge_danger{
|
|
19
|
+
.badge_dot{
|
|
20
|
+
background-color: var(--qui-color-danger,#ff6b6b);
|
|
21
|
+
}
|
|
22
|
+
.badge_value{
|
|
23
|
+
background-color: var(--qui-color-danger,#ff6b6b);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
&.yu-badge_primary{
|
|
27
|
+
.badge_dot{
|
|
28
|
+
background-color: var(--qui-color-primary, #3565ee);
|
|
29
|
+
}
|
|
30
|
+
.badge_value{
|
|
31
|
+
background-color: var(--qui-color-primary, #3565ee);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
&.yu-badge_assist{
|
|
35
|
+
.badge_dot{
|
|
36
|
+
background-color: var(--qui-color-assist,#5c3af6);
|
|
37
|
+
}
|
|
38
|
+
.badge_value{
|
|
39
|
+
background-color: var(--qui-color-assist,#5c3af6);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
&.yu-badge_success{
|
|
43
|
+
.badge_dot{
|
|
44
|
+
background-color: var(--qui-color-success,#31b05d);
|
|
45
|
+
}
|
|
46
|
+
.badge_value{
|
|
47
|
+
background-color: var(--qui-color-success,#31b05d);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
&.yu-badge_warning{
|
|
51
|
+
.badge_dot{
|
|
52
|
+
background-color: var(--qui-color-warning,#f1a510);
|
|
53
|
+
}
|
|
54
|
+
.badge_value{
|
|
55
|
+
background-color: var(--qui-color-warning,#f1a510);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
.badge_dot{
|
|
59
|
+
background-color: var(--qui-color-danger,#ff6b6b);
|
|
60
|
+
border: 1px solid var(--qui-main-bg, #ffffff);
|
|
61
|
+
border-radius: 50px;
|
|
62
|
+
width: 8px;
|
|
63
|
+
height: 8px;
|
|
64
|
+
}
|
|
65
|
+
.badge_value{
|
|
66
|
+
background-color: var(--qui-color-danger,#ff6b6b);
|
|
67
|
+
border-radius: var(--qui-border-radius-round, 100px);
|
|
68
|
+
color: var(--qui-color-white, #ffffff);
|
|
69
|
+
display: inline-flex;
|
|
70
|
+
justify-content: center;
|
|
71
|
+
align-items: center;
|
|
72
|
+
font-size: 11px;
|
|
73
|
+
height: 18px;
|
|
74
|
+
padding: 0 6px;
|
|
75
|
+
white-space: nowrap;
|
|
76
|
+
border: 1px solid var(--qui-main-bg, #ffffff);
|
|
77
|
+
box-sizing: border-box;
|
|
78
|
+
}
|
|
79
|
+
.badge_animation{
|
|
80
|
+
animation: shake 0.3s infinite alternate;
|
|
81
|
+
}
|
|
82
|
+
@keyframes shake {
|
|
83
|
+
from {
|
|
84
|
+
transform: scale(1) translateY(-2px);
|
|
85
|
+
}
|
|
86
|
+
to {
|
|
87
|
+
transform: scale(0.95) translateY(0);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import {computed} from "vue";
|
|
3
|
+
|
|
4
|
+
const props = defineProps({
|
|
5
|
+
value: {
|
|
6
|
+
type: [String,Number],
|
|
7
|
+
default: function () {
|
|
8
|
+
return 0
|
|
9
|
+
}
|
|
10
|
+
},
|
|
11
|
+
max: {
|
|
12
|
+
type: Number,
|
|
13
|
+
default: function () {
|
|
14
|
+
return 99
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
type: {
|
|
18
|
+
type: String,
|
|
19
|
+
default: 'danger',
|
|
20
|
+
validator: value => ['primary', 'assist', 'success', 'warning', 'danger'].includes(value)
|
|
21
|
+
},
|
|
22
|
+
offset: {
|
|
23
|
+
type: Array,
|
|
24
|
+
default: function () {
|
|
25
|
+
return [0,0]
|
|
26
|
+
},
|
|
27
|
+
validator: function (value) {
|
|
28
|
+
return (typeof(value) === 'object' && Array.isArray(value) && value.length === 2 && typeof(value[0]) === 'number' && typeof(value[1]) === 'number');
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
dot: {
|
|
32
|
+
type: Boolean,
|
|
33
|
+
default: function () {
|
|
34
|
+
return false
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
showZero: {
|
|
38
|
+
type: Boolean,
|
|
39
|
+
default: function () {
|
|
40
|
+
return false
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
hidden: {
|
|
44
|
+
type: Boolean,
|
|
45
|
+
default: () => {
|
|
46
|
+
return false
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
animation: {
|
|
50
|
+
type: Boolean,
|
|
51
|
+
default: () => {
|
|
52
|
+
return false
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
</script>
|
|
58
|
+
|
|
59
|
+
<template>
|
|
60
|
+
<div class="yu-badge-wrapper">
|
|
61
|
+
<div class="yu-badge_content">
|
|
62
|
+
<slot></slot>
|
|
63
|
+
</div>
|
|
64
|
+
<div class="yu-badge_value" :class="`yu-badge_${type}`" :style="{right:`-${offset[0]}px` || 0,top:`${offset[1]}px` || 0}" v-if="!hidden">
|
|
65
|
+
<div class="badge_dot" v-if="dot"></div>
|
|
66
|
+
<template v-else>
|
|
67
|
+
<div class="badge_value" :class="{'badge_animation':animation}" v-if="showZero && !value">0</div>
|
|
68
|
+
<div class="badge_value" :class="{'badge_animation':animation}" v-text="value > max?`${max}+`:value" v-else-if="value"></div>
|
|
69
|
+
<div class="badge_value" :class="{'badge_animation':animation}" v-else-if="$slots.badge">
|
|
70
|
+
<slot name="badge"></slot>
|
|
71
|
+
</div>
|
|
72
|
+
</template>
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
75
|
+
</template>
|
|
76
|
+
<style lang="scss" src="./index.scss" scoped></style>
|
|
File without changes
|
package/scripts/index.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { execSync } from 'child_process'
|
|
2
|
+
import { existsSync, rmSync } from 'fs'
|
|
3
|
+
import { join } from 'path'
|
|
4
|
+
|
|
5
|
+
// 清理 dist 目录
|
|
6
|
+
const distDir = join(process.cwd(), 'dist')
|
|
7
|
+
if (existsSync(distDir)) {
|
|
8
|
+
rmSync(distDir, { recursive: true, force: true })
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
console.log('开始构建主包...')
|
|
12
|
+
execSync('vite build --config vite.config.js', { stdio: 'inherit' })
|
|
13
|
+
|
|
14
|
+
console.log('开始构建指令包...')
|
|
15
|
+
execSync('vite build --config ./viteConfig/directives.js', { stdio: 'inherit' })
|
|
16
|
+
|
|
17
|
+
console.log('构建完成!')
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
@echo off
|
|
2
|
+
|
|
3
|
+
cd /d "%~dp0..\"
|
|
4
|
+
|
|
5
|
+
@REM call pnpm version major
|
|
6
|
+
@REM call pnpm version minor
|
|
7
|
+
|
|
8
|
+
call pnpm version patch --no-commit-hooks --no-git-tag-version
|
|
9
|
+
|
|
10
|
+
call pnpm build
|
|
11
|
+
if %errorlevel% equ 0 (
|
|
12
|
+
echo =========================================
|
|
13
|
+
echo Build completed successfully.
|
|
14
|
+
echo =========================================
|
|
15
|
+
) else (
|
|
16
|
+
echo =========================================
|
|
17
|
+
echo Error: Build failed
|
|
18
|
+
echo =========================================
|
|
19
|
+
exit /b 1
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
@REM call pnpm publish --force
|
|
23
|
+
call pnpm publish
|
|
24
|
+
if %errorlevel% neq 0 (
|
|
25
|
+
echo =========================================
|
|
26
|
+
echo Error: Publish failed.
|
|
27
|
+
echo =========================================
|
|
28
|
+
exit /b 1
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
echo =========================================
|
|
32
|
+
echo Build and publish process completed successfully.
|
|
33
|
+
echo =========================================
|
|
34
|
+
exit /b 0
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import {createApp} from 'vue'
|
|
2
|
+
import ImgViewerComponent from './index.vue'
|
|
3
|
+
// 创建 viewer 指令
|
|
4
|
+
const vViewer = {
|
|
5
|
+
mounted(el) {
|
|
6
|
+
el.clickShowImage = (e)=>{
|
|
7
|
+
try {
|
|
8
|
+
if (e.target.tagName !== 'IMG' || !e.target.src) return
|
|
9
|
+
if (el.instance) {
|
|
10
|
+
el.instance.show()
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
const app = createApp(ImgViewerComponent,{src: e.target.src})
|
|
14
|
+
// 创建挂载点
|
|
15
|
+
const viewerWrapper = document.createElement('div')
|
|
16
|
+
// 挂载 imgViewer 组件
|
|
17
|
+
const instance = app.mount(viewerWrapper)
|
|
18
|
+
el.instance = instance;
|
|
19
|
+
el.app = app;
|
|
20
|
+
document.body.appendChild(instance.$el)
|
|
21
|
+
instance.show()
|
|
22
|
+
} catch (error) {
|
|
23
|
+
console.error('Image viewer error:', error);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
el.addEventListener('click', el.clickShowImage, { passive: true });
|
|
27
|
+
},
|
|
28
|
+
beforeUnmount(el) {
|
|
29
|
+
try {
|
|
30
|
+
el.removeEventListener('click', el.clickShowImage, { passive: true });
|
|
31
|
+
if(!el.instance) return
|
|
32
|
+
el.instance.hide()
|
|
33
|
+
setTimeout(() => {
|
|
34
|
+
el.app.unmount()
|
|
35
|
+
el.instance.$el.remove()
|
|
36
|
+
el.instance = null
|
|
37
|
+
},200)
|
|
38
|
+
}catch (error){
|
|
39
|
+
console.log('Image viewer error:', error)
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
export default vViewer
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import {createApp, h, ref} from 'vue'
|
|
2
|
+
import ImgViewerComponent from './index.vue'
|
|
3
|
+
|
|
4
|
+
export function useImgViewer() {
|
|
5
|
+
let instance = null;
|
|
6
|
+
const show = (params) => {
|
|
7
|
+
let options = null;
|
|
8
|
+
const visible = ref(false)
|
|
9
|
+
if (typeof params === 'object' && params !== null) {
|
|
10
|
+
options = {...params}
|
|
11
|
+
} else if (typeof params === 'string') {
|
|
12
|
+
options = {
|
|
13
|
+
src: params
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
visible.value = true
|
|
17
|
+
const app = createApp({
|
|
18
|
+
setup() {
|
|
19
|
+
visible.value = true;
|
|
20
|
+
return () =>
|
|
21
|
+
h(ImgViewerComponent, {
|
|
22
|
+
...options,
|
|
23
|
+
modelValue: visible.value,
|
|
24
|
+
onClose: close
|
|
25
|
+
})
|
|
26
|
+
}
|
|
27
|
+
})
|
|
28
|
+
const close = () => {
|
|
29
|
+
if (!instance) return
|
|
30
|
+
visible.value = false
|
|
31
|
+
setTimeout(() => {
|
|
32
|
+
app.unmount();
|
|
33
|
+
instance = null;
|
|
34
|
+
}, 200)
|
|
35
|
+
}
|
|
36
|
+
const rootNode = document.createElement('div')
|
|
37
|
+
instance = app.mount(rootNode)
|
|
38
|
+
document.body.appendChild(instance.$el)
|
|
39
|
+
}
|
|
40
|
+
return (params) => show(params)
|
|
41
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
.yu-img-viewer-wrapper{
|
|
2
|
+
position: fixed;
|
|
3
|
+
top: 0;
|
|
4
|
+
bottom: 0;
|
|
5
|
+
left: 0;
|
|
6
|
+
right: 0;
|
|
7
|
+
z-index: 998;
|
|
8
|
+
background-color: rgba(0, 0, 0, 0.4);
|
|
9
|
+
display: -webkit-box;
|
|
10
|
+
display: -webkit-flex;
|
|
11
|
+
display: -ms-flexbox;
|
|
12
|
+
display: flex;
|
|
13
|
+
align-items: center;
|
|
14
|
+
justify-content: center;
|
|
15
|
+
overflow: hidden;
|
|
16
|
+
user-select: none;
|
|
17
|
+
will-change: transform, opacity;
|
|
18
|
+
.yu-img-viewer{
|
|
19
|
+
position: fixed;
|
|
20
|
+
bottom: 40px;
|
|
21
|
+
left: 50%;
|
|
22
|
+
z-index: 1;
|
|
23
|
+
transform: translateX(-50%);
|
|
24
|
+
font-size: 0;
|
|
25
|
+
display: -webkit-box;
|
|
26
|
+
display: -webkit-flex;
|
|
27
|
+
display: -ms-flexbox;
|
|
28
|
+
display: flex;
|
|
29
|
+
align-items: center;
|
|
30
|
+
gap: 10px;
|
|
31
|
+
background-color: rgba(0, 0, 0, 0.5);
|
|
32
|
+
padding: 10px 15px;
|
|
33
|
+
border-radius: 50px;
|
|
34
|
+
.icon{
|
|
35
|
+
width: 24px;
|
|
36
|
+
height: 24px;
|
|
37
|
+
padding: 5px;
|
|
38
|
+
box-sizing:content-box;
|
|
39
|
+
max-width: none;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
.yu-img-viewer-enter-active,
|
|
44
|
+
.yu-img-viewer-leave-active{
|
|
45
|
+
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1),
|
|
46
|
+
opacity 0.3s linear;
|
|
47
|
+
}
|
|
48
|
+
.yu-img-viewer-enter-from,
|
|
49
|
+
.yu-img-viewer-leave-to{
|
|
50
|
+
transform: translateY(-10px);
|
|
51
|
+
opacity: 0;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
@media screen and (min-width: 768px) {
|
|
55
|
+
.yu-img-viewer-wrapper{
|
|
56
|
+
.yu-img-viewer{
|
|
57
|
+
.icon{
|
|
58
|
+
cursor: pointer;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
@media screen and (max-width: 768px) {
|
|
64
|
+
.yu-img-viewer-wrapper{
|
|
65
|
+
.yu-img-viewer{
|
|
66
|
+
bottom: 20px;
|
|
67
|
+
left: 15px;
|
|
68
|
+
right: 15px;
|
|
69
|
+
z-index: 1;
|
|
70
|
+
transform: translateX(0);
|
|
71
|
+
justify-content: space-between;
|
|
72
|
+
padding: 10px 15px;
|
|
73
|
+
border-radius: 10px;
|
|
74
|
+
.icon{
|
|
75
|
+
padding: 6px;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import {ref, watch} from "vue";
|
|
3
|
+
const modelValue = defineModel({type: Boolean, default: false});
|
|
4
|
+
const props = defineProps({
|
|
5
|
+
src: {
|
|
6
|
+
type: String,
|
|
7
|
+
default() {
|
|
8
|
+
return ''
|
|
9
|
+
}
|
|
10
|
+
},
|
|
11
|
+
maskClose: {
|
|
12
|
+
type: Boolean,
|
|
13
|
+
default() {
|
|
14
|
+
return true
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
const emits = defineEmits(['close'])
|
|
20
|
+
const maxRatio = 0.95; //显示占用屏幕的最大比例
|
|
21
|
+
const imgScale = ref(1); //原始比例最适合屏幕的比例
|
|
22
|
+
const scale = ref(1); //缩放倍率
|
|
23
|
+
const rotate = ref(0); //旋转角度
|
|
24
|
+
const imgWidth = ref(0); //图片原始宽
|
|
25
|
+
const imgHeight = ref(0); //图片原始高
|
|
26
|
+
const isNatural = ref(true) //是否展示实际尺寸的icon
|
|
27
|
+
const onMask = () => {
|
|
28
|
+
props.maskClose && onClose()
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const onImgLoad = (e) => {
|
|
32
|
+
imgWidth.value = e.target.naturalWidth;
|
|
33
|
+
imgHeight.value = e.target.naturalHeight;
|
|
34
|
+
setImgScale();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const setImgScale = () => {
|
|
38
|
+
const windowWidth = window.innerWidth;
|
|
39
|
+
const windowHeight = window.innerHeight;
|
|
40
|
+
const windowRatio = windowHeight / windowWidth;
|
|
41
|
+
const imgRatio = imgHeight.value / imgWidth.value;
|
|
42
|
+
let scaleValue = 1;
|
|
43
|
+
if (imgRatio > windowRatio) {
|
|
44
|
+
// 图片比窗口更高
|
|
45
|
+
scaleValue = (windowHeight * maxRatio) / imgHeight.value;
|
|
46
|
+
} else {
|
|
47
|
+
// 图片比窗口更宽或比例相同
|
|
48
|
+
scaleValue = (windowWidth * maxRatio) / imgWidth.value;
|
|
49
|
+
}
|
|
50
|
+
const val = Math.round(scaleValue * 10) / 10;
|
|
51
|
+
imgScale.value = Math.max(val, 1); // 设置最小缩放比例为1
|
|
52
|
+
if (!isNatural.value) {
|
|
53
|
+
scale.value = imgScale.value;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const onWheel = (e) => {
|
|
58
|
+
e.preventDefault()
|
|
59
|
+
if (e.wheelDelta > 0) {
|
|
60
|
+
onZoomOut();
|
|
61
|
+
} else {
|
|
62
|
+
onZoomIn();
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const onZoomIn = (step = 0.1) => {
|
|
67
|
+
const newScale = scale.value - step;
|
|
68
|
+
scale.value = Math.max(newScale, 0.1) // 设置最小缩放比例为10%
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const onZoomOut = (step = 0.1) => {
|
|
72
|
+
const newScale = scale.value + step
|
|
73
|
+
scale.value = Math.min(newScale, 2) // 设置最大缩放比例为200%
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const onToggleFit = () => {
|
|
77
|
+
isNatural.value = !isNatural.value;
|
|
78
|
+
scale.value = isNatural.value ? 1 : imgScale.value
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const onRotate = (angle) => {
|
|
82
|
+
rotate.value += angle
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const show = () => {
|
|
86
|
+
modelValue.value = true;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const hide = () => {
|
|
90
|
+
modelValue.value = false;
|
|
91
|
+
onResetParams()
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const onClose = () => {
|
|
95
|
+
hide();
|
|
96
|
+
emits("close")
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const onResetParams = () => {
|
|
100
|
+
imgScale.value = 1;
|
|
101
|
+
scale.value = 1;
|
|
102
|
+
rotate.value = 0;
|
|
103
|
+
imgWidth.value = 0;
|
|
104
|
+
imgHeight.value = 0;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const onStopPropagation = (e) => {
|
|
108
|
+
e.stopPropagation();
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const onWindowResize = () => {
|
|
112
|
+
if(modelValue.value && !isNatural.value){
|
|
113
|
+
setImgScale()
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
const onEscKeyDown =(e) => {
|
|
117
|
+
if (e.key === 'Escape') {
|
|
118
|
+
onClose()
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
watch(modelValue, (newVal) => {
|
|
123
|
+
if (newVal) {
|
|
124
|
+
document.body.style.overflow = 'hidden';
|
|
125
|
+
window.addEventListener('keydown', onEscKeyDown)
|
|
126
|
+
window.addEventListener('resize', onWindowResize)
|
|
127
|
+
} else {
|
|
128
|
+
document.body.style.overflow = "";
|
|
129
|
+
window.removeEventListener('keydown', onEscKeyDown)
|
|
130
|
+
window.removeEventListener('resize', onWindowResize)
|
|
131
|
+
}
|
|
132
|
+
}, {immediate: true})
|
|
133
|
+
|
|
134
|
+
defineExpose({
|
|
135
|
+
show,
|
|
136
|
+
hide
|
|
137
|
+
})
|
|
138
|
+
</script>
|
|
139
|
+
<template>
|
|
140
|
+
<Transition name="yu-img-viewer" appear>
|
|
141
|
+
<div class="yu-img-viewer-wrapper" v-if="modelValue" @click="onMask" @wheel="onWheel">
|
|
142
|
+
<img :src="src" class="img-show" @click="onStopPropagation" @load="onImgLoad" :style="`max-width:${maxRatio * 100}%;max-height:${maxRatio * 100}%;transform: scale(${scale}) rotate(${rotate}deg);transition: all 0.3s ease;`" alt="">
|
|
143
|
+
<div class="yu-img-viewer" @click="onStopPropagation">
|
|
144
|
+
<img @click="onZoomIn()" title="缩小" class="icon" alt="" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEgAAABICAMAAABiM0N1AAAAmVBMVEUAAAD////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////VHQRUAAAAMnRSTlMA9+7gJPncJwX06aV0ShHkmo4+GrFIRbdvU3tsamZhNDDPxbaflU7WvzIgHg2tgzmHWNLgL/wAAAJTSURBVFjD7Zdrd7IwDMfDTRAE8QJDuaiIt6nb8/T7f7gR2TkeWEtT5975e5mUP23StCm8eMHHzsOjMWRsaJRhZj+qctqNWYvx7hPU+b9hHA65oyazPzABxh7oBFfWoM9HsR04TmDHo7n+bdyeqTrTJjaaF7Xtkac18j5NJ7FuMpPZT9ds0kgtKTr/GPJm8r3mG0N2cp2P23RSEJJqJKUMR7kr6GHlElY3xfgMTOjFHKDSvjfvmK/xO0h4x2F63y7wMD4nkLLWMB9if8xqMkAooVwI3Yfbf0hssVpEdbfAhZk0IRMXVwicWO8TIDLB9AoiiBOaUYVmOKWp8B8ekMEMh1yPUXsiulCEJcANH24yUADPpzXHntf2uYrQvP4g4djD2j5SERrVH1Qc+7G2+ypCfv1BybFjSdsqQjZubkHsLq0jxWAdjNa+CQTZwZEtQ1enOwGnNlh/KTTsLi3+oTQgLc1QDragbMtH0n/k2CvlDSmo2uSREkn5x5F60XIvCvdJxwiE6gdbxb9lFY9acZYHTzr8oVC7jsS7xTGwsaNfkO4Fem5IllN0chyZiP3Yjg3XhCYCK3zjgJCzTm9rrFVvf83IjVYiaSGprd8GJISyZjTTGK1F3snbY6LSkskadoRyevl6I+XFbXvsfctcoyGTxhs5b++PGr951Pj3R41eAEwbpRRkLAZMgFXhklGJVgZOceDJaJUNDXGjVICcz9DtTOaYBXd3pBGVkFMalvg41selt4wvnZxYN/UF/Jq9JdniZBao5MKTlK7wDFYfSQAvXgB8AbQmiYamqSF8AAAAAElFTkSuQmCC">
|
|
145
|
+
<img @click="onZoomOut()" title="放大" class="icon" alt="" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEgAAABICAMAAABiM0N1AAAAilBMVEUAAAD////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////2N2iNAAAALXRSTlMA+ODk70Uk9belj3QnBdxKRzHonfyYVDUfGhFv7LF8bGpmYT3PxK9O1r4NhhWkhma1AAACU0lEQVRYw+2X2ZqaQBCFD4swCAgD4oYr7mbq/V8vFoxfPkw3VE/Mnf9lqae71i7x5o2aaFdcbZ/It4/5NsIP+Rw61MIppjBnciAFl10MI84X0mCfIefrRA3uKhxEizheRINw5VLDagYhe4cYK0jRIg0sYpYJRJTjWmY4x1/Mh43UGgJ+EfPhQYn3QcxQqGNtoGVTX6pAD1u6k3XWyzQTeLfn+Iw8dOKNWOncmXfOl9Ob3pnDuev6WsDx+RR0j8X5gJYB3dmCkYRyAh2X+hwRXAV2rOtTdsyDCI+dq6DmUFeakCGnVxNBvtBcETnHGSi6ha+0154RKOw2ka3JcA6of0Cpwk53FOaUW0AZPp4/kAuB55Oq5nY8s0yEVkRUKuwFEYUmQqEmSFciSkyEEiI6Kuzc0pGJUMTFrYndrV0/9ES7nhY8AiQH29Tm+QIxF/D/FPL7XbPTZ9dcVWEbB1vTtsefpP+qsOfGBal5lUrTFuEFYaMaR69qWmTmY2QJFfmrBtteO2rtVDNqEygZvWj4ozJ9jkKoiW2zBzK7oeOFpB0E7OhO2X2OL1kiuMMPMbTMlvK1ZjxFB2cSL1plzwopXf0O6KHoW0a3FslW5EKyHjMhelgT46sXdp9IrpQsv/9CDJ6aLvj2KkhdYsre9D7u757CpPlTk4Qn92GsgL1PzAZ9TEakYZx/AbWSrA3i6kIK/Dx6+NkoVehnWmTUwrpuF38+Tq36ghUkfG7yo+3T2HWOwXpwe8pJozTBP3O2ekpczISVMrxCaUx0wiuYrssF3rwBfgPQ0YapPYGLOQAAAABJRU5ErkJggg==">
|
|
146
|
+
<img v-if="isNatural" @click="onToggleFit" title="适应窗口" class="icon" alt="" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEgAAABIBAMAAACnw650AAAALVBMVEUAAAD///////////////////////////////////////////////////////+hSKubAAAADnRSTlMA7+b59OdySknVttYPDGmoZHwAAACySURBVEjHYxgFQx5k2r0DgkcNEB6HHoj3eBqqGq53EKAA4TJBuQtQFOVBRQNQFT1DUTTvnaEgEDgVQLjsISCe8LvXKIrsHl/AdCev3WMU/ruH2Hwj925UETmKnmBT5IeqSO8plIFPtFQdykAXHQWjABU0aRAhqvcIysAQJZx8h0C+G6qK7B4fwFTDY/cYa2EPq1tUEIU9ZrWhgK/a4MJetyzAWpXB6pY4cFW2jWEUDHUAANSUoGLnF/1TAAAAAElFTkSuQmCC">
|
|
147
|
+
<img v-else @click="onToggleFit" title="实际尺寸" class="icon" alt="" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEgAAABIBAMAAACnw650AAAAJ1BMVEUAAAD///////////////////////////////////////////////+uPUo5AAAADHRSTlMA7vD5hXLVtg3W1HNKpeIJAAAAkUlEQVRIx2MYBaMABqJszmABh1OR1bCfwQEKkBTF4FJ0FElRzhlDQSxA+MxJJEU2hxuwuZTD5jAS78wh7P7ROTOqCIsiZxMgAWXhVAQOYCgLp6IzIDEoC78iZxNCimA2EVZEHetAYPApGorhhJZUCCe6wZIRhp4iAsUh4YKVcBF9mtTCniEMe7UxlWEUjAIoAACLZA9Ayw+XDwAAAABJRU5ErkJggg==">
|
|
148
|
+
<img @click="onRotate(-90)" title="向左旋转90°" class="icon" alt="" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEgAAABICAMAAABiM0N1AAAAn1BMVEUAAAD///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////8Kd3m4AAAANHRSTlMA+u1wMJv0EAzjysVFGwb81aJVCdu4aFrSzqiNijsV96+fkWFALSgj59dNNRizfFFJhe++/7y+OwAAAjhJREFUWMPt19dy4jAUBuBfLjHggsEB06vpJeyu3v/Z1kdyJmSwZMFwkZnwXWUic+RzJKvg5eU3mrRHndhifFr3XdvBgzzbb/JrljvG/TbulN+K03vfZsh4uTjDHfZ1LjQbo7YzieBtAntmcalVg6khF97tENcuAyb/ry97rfb5R4eTXbukcHNR/rdAl82ULWV5GvTs9ByhzKFPrawNpXfOt8glDf3bRwsRaQ+Vv3kz/XpGz/3zoNZtUnYrKJzyVhs4U5xOAp02Rdqpxs6mAHBoXBoJ9LrUm6todOh9RYG2R7P5ofpeaL4N6IEAlSLqsK9opCCkBQMOlelDnTilF1KPwXCmfzGXaolSaxlogXA5e6OpEkIjZGK6lKrLzPq8MIbOXHRaqsW/8aGV5Y/UUWrJv/SG+hIVCazL0+bStpVWzySZQBelZvmYdewVzCzVszv6yCIYO+jraK5Gqw2egeWVwDPQx/msQOxZqVk/qtiPD79+Qj5urv5EkPYGHgzJj1a9WI1gaCyWEfUaaSUw46oWNtITaRs50jHsoNv46onxyacBlWSbN59hYMWK7UizbzMH1XzNBkkiqlJcPQX+VO4yY9pBT6iQUhzXoK8BtC5MHmv0xAHypHsqZfKgVcHrUaT+BiojamcZKk1icZawFePuF4dRA54vN9s9boQLJnq5wEgy50JsT3AtaCkO7GqpdXuFGCiuEHoTl/FyuwD3Wc+Z/pplLrT736NYboYHHYurqCWvoi8vv85/WZV1EethCRYAAAAASUVORK5CYII=">
|
|
149
|
+
<img @click="onRotate(90)" title="向右旋转90°" class="icon" alt="" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEgAAABICAMAAABiM0N1AAAAn1BMVEUAAAD///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////8Kd3m4AAAANHRSTlMA+uLt1gYw/JxwDvbzysWhR7ORi2hVCtHPuamGWk9ANSIRCGFCLSgdGBXo2jquo5l1Gn1bVjAitwAAAkhJREFUWMPtl9ly6jAMhu1sZCMrgRASlrCGFgqt3//ZTmUfhsMc23HaXHSmfDeZiTW/LVmWZfTkye/DcuxA84iv5XGaNV9VeX3Tyb8MAmeFulMa5H88e9lRZmMQPjh1kTpuSBj6yYmWK+QuF1k6HBCKNlcP8QubvIgefu/fNRarmammE/nUPOSEI2MeT2/ujccSnQyDbb7gDpoXD0aHbPvW2KuEOhXVSUy530O6lCMhLyK7rQdujZCY1RSUYtCE7RBYjQ3QyZCMmiq9I3ShgnxssBkhOeMh7KmF4s+PwzeZ0/igNg5HCFMNu2vxLXIwMBUyBCYsxCGqIEAWUuCW+gV/GHx/ky/llG5gxVfYXGE4FxDDvTQ4kGT+ab1HCRPacc1gMJSXKPKX/MwOMN8MzqT8aJsBeeDMtdqxKeRE6YTcWXNtRtSzdg4f4ZEw+AG16RRqbJ2pJzwf4P8CKWPOK1Mcaxf1gE8IRn0AR+dnCfXgWs/BZtvfAywhewCOiN2HEDu0qrjFpJRF+1VVKCUEXwVjSQffap3VR2Gp9Q7qAZ0gQFT8E7UFadKLtILqv1URmn1aHmvxOJTRQKUV8+63tfjKnrVvPbQahtmW3eSjrTbGcCNLE4W1NTiSCxUq6976oFTK5oppG9neYWM6nzAAyxzGJwr1hjWjgSALHB9GjUa9PcbJnpNo7JoNVt0a9vPm4XfjGIQS1t2fEAV9QpiNlc1uTwi97OVRM7CbPp5ZONyh7szZw+9Ofrl+6ymqE6wb07Q8oCdPfiF/ANQydloYw+eCAAAAAElFTkSuQmCC">
|
|
150
|
+
<img @click="onClose" title="关闭" class="icon" alt="" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEgAAABICAMAAABiM0N1AAAAbFBMVEUAAAD///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////8+T+BWAAAAI3RSTlMAsoyKtI+5mK+spJ/RvKecjnMGyb+pkw3AtoN6V0cb6js4LTOnxLwAAAFGSURBVFjD7dbLbsIwEIXhScCEBsilBcq1t3n/d6zTNjpIjjzOzKqS/w0L0KfRAQSUy+VyQ2vmmhK6MG9fI8/f2VfITsm+PvKCD06SHEsQNTy0TriHt88UaylLC2bGRIL0JN3jHakiLjk4adKBJut46AX7yFIzuU+6g50ao4Ob6mBnOGoJO5/SFUib8J79HAU7tQ/74B6NVKnvQcUoYZ8dkV464nO40iiQnN/H4mCnvodjuGnsTMgiLclYxXjzTF1/oavV6fiv0uYcMbazO23789DpDHwvivHztLDd4x2jVMEhOusXr/BTiZuc1cFNagetNDu1oxNK5VwHP9uQ5u60gTMldVYHO6kdtEuX6tAJpTLVORASFpcd9U3Yp6Foe3mnG+6JdeKhe+wPPe4RbhJOent/+Csj7fRFMam/UVKX+pNyudx/7hvtEkCDo9yvRAAAAABJRU5ErkJggg==">
|
|
151
|
+
</div>
|
|
152
|
+
</div>
|
|
153
|
+
</Transition>
|
|
154
|
+
</template>
|
|
155
|
+
<style lang="scss" src="./index.scss" scoped></style>
|