vue-next-yui 1.0.3 → 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.
Files changed (41) hide show
  1. package/.vscode/extensions.json +3 -0
  2. package/README.md +5 -0
  3. package/example/App.vue +23 -0
  4. package/example/main.js +4 -0
  5. package/index.html +13 -0
  6. package/package.json +21 -38
  7. package/packages/badge/index.js +0 -0
  8. package/packages/badge/index.scss +91 -0
  9. package/packages/badge/index.vue +76 -0
  10. package/packages/index.js +0 -0
  11. package/scripts/index.js +17 -0
  12. package/scripts/publish.cmd +34 -0
  13. package/src/components/imgViewer/directive.js +43 -0
  14. package/src/components/imgViewer/index.js +41 -0
  15. package/src/components/imgViewer/index.scss +79 -0
  16. package/src/components/imgViewer/index.vue +155 -0
  17. package/src/components/index.js +18 -0
  18. package/src/components/loading/directive.js +40 -0
  19. package/src/components/loading/index.js +70 -0
  20. package/src/components/loading/index.scss +63 -0
  21. package/src/components/loading/index.vue +60 -0
  22. package/src/components/mask/directive.js +49 -0
  23. package/src/components/mask/index.vue +93 -0
  24. package/src/components/watermark/directive.js +28 -0
  25. package/src/components/watermark/index.vue +97 -0
  26. package/src/components/watermark/useWatermark.js +45 -0
  27. package/src/directives/index.js +24 -0
  28. package/src/directives/outside.js +16 -0
  29. package/src/index.js +32 -0
  30. package/src/style/index.css +0 -0
  31. package/src/utils/pick.js +49 -0
  32. package/src/utils/types.js +33 -0
  33. package/tsconfig.json +28 -0
  34. package/vite.config.js +55 -0
  35. package/vite.configAll.js +75 -0
  36. package/viteConfig/directives.js +42 -0
  37. package/viteConfig/utils.js +0 -0
  38. package/favicon.ico +0 -0
  39. package/style.css +0 -1
  40. package/vue-next-yui.es.js +0 -248
  41. package/vue-next-yui.umd.js +0 -1
@@ -0,0 +1,3 @@
1
+ {
2
+ "recommendations": ["Vue.volar"]
3
+ }
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).
@@ -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>
@@ -0,0 +1,4 @@
1
+ import { createApp } from 'vue'
2
+ import App from './App.vue'
3
+
4
+ createApp(App).mount('#app')
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
- "version": "1.0.3",
4
- "description": "vue-next-yui",
5
- "type": "module",
6
- "scripts": {
7
- "dev": "vite",
8
- "build": "vite build",
9
- "preview": "vite preview"
10
- },
11
- "main": "vue-next-yui.umd.js",
12
- "module": "vue-next-yui.es.js",
13
- "exports": {
14
- ".": {
15
- "import": "vue-next-yui.es.js",
16
- "require": "vue-next-yui.umd.js"
17
- },
18
- "./style.css": "style.css"
19
- },
20
- "dependencies": {
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"
38
- }
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
+ }
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
@@ -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>