vome-core 0.0.24 → 0.0.26

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 (51) hide show
  1. package/dist/admin/components/vm-action-btn.vue +109 -0
  2. package/dist/admin/components/vm-aside-list-item.vue +124 -0
  3. package/dist/admin/components/vm-card-grid.vue +36 -0
  4. package/dist/admin/components/vm-card.vue +100 -0
  5. package/dist/admin/components/vm-perm-section.vue +57 -0
  6. package/dist/admin/components/vm-split-layout.vue +85 -0
  7. package/dist/admin/config/plugin-dev.js +1 -1
  8. package/dist/admin/crud/components/vm-avatar.vue +103 -0
  9. package/dist/admin/crud/components/vm-copy-btn.vue +101 -0
  10. package/dist/admin/crud/components/vm-datetime-picker.vue +9 -0
  11. package/dist/admin/crud/components/vm-ellipsis-text.vue +34 -0
  12. package/dist/admin/crud/components/vm-form-hint.vue +63 -0
  13. package/dist/admin/crud/components/vm-json-code.vue +40 -0
  14. package/dist/admin/crud/components/vm-json-editor.vue +128 -0
  15. package/dist/admin/crud/components/vm-json-viewer.vue +147 -0
  16. package/dist/admin/crud/components/vm-preview-viewer.vue +39 -6
  17. package/dist/admin/crud/components/vm-status-tag.vue +157 -0
  18. package/dist/admin/crud/components/vm-tag-list.vue +82 -0
  19. package/dist/admin/crud/components/vm-text-link.vue +51 -0
  20. package/dist/admin/crud/config.js +1 -1
  21. package/dist/admin/crud/confirm.js +1 -1
  22. package/dist/admin/crud/dict.js +1 -1
  23. package/dist/admin/crud/index.d.ts +11 -1
  24. package/dist/admin/crud/index.js +1 -1
  25. package/dist/admin/crud/key.js +1 -1
  26. package/dist/admin/crud/mitt.js +1 -1
  27. package/dist/admin/crud/plugins.js +1 -1
  28. package/dist/admin/crud/registry.js +1 -0
  29. package/dist/admin/crud/span.js +1 -1
  30. package/dist/admin/crud/style.js +1 -1
  31. package/dist/admin/crud/validate.js +1 -1
  32. package/dist/admin/directives/perm.js +1 -1
  33. package/dist/admin/hooks/useUpload.js +1 -1
  34. package/dist/admin/lib/browser.js +1 -1
  35. package/dist/admin/lib/cn.js +1 -1
  36. package/dist/admin/lib/dialog-float.js +1 -1
  37. package/dist/admin/lib/export-excel.js +1 -1
  38. package/dist/admin/lib/file-preview.js +1 -1
  39. package/dist/admin/lib/json.d.ts +12 -0
  40. package/dist/admin/lib/json.js +1 -0
  41. package/dist/admin/lib/menu.js +1 -1
  42. package/dist/admin/lib/tree.js +1 -1
  43. package/dist/admin/lib/upload.js +1 -1
  44. package/dist/admin/lib/video-frame.js +1 -1
  45. package/dist/index.js +1 -1
  46. package/dist/server/index.js +1 -1
  47. package/dist/shared/excel.js +1 -1
  48. package/dist/shared/index.js +1 -1
  49. package/dist/shared/tree.js +1 -1
  50. package/package.json +1 -1
  51. package/dist/admin/crud/components/vm-audio.vue +0 -217
@@ -0,0 +1,109 @@
1
+ <template>
2
+ <button
3
+ type="button"
4
+ class="vm-action-btn"
5
+ :class="[`is-${variant}`]"
6
+ :disabled="disabled || loading"
7
+ @click="emit('click', $event)"
8
+ >
9
+ <i v-if="icon" :class="[icon, { 'is-spin': loading }]" aria-hidden="true" />
10
+ <span>{{ loading && loadingText ? loadingText : label }}</span>
11
+ </button>
12
+ </template>
13
+
14
+ <script setup lang="ts">
15
+ defineOptions({ name: 'vm-action-btn' })
16
+
17
+ withDefaults(
18
+ defineProps<{
19
+ label?: string
20
+ loadingText?: string
21
+ icon?: string
22
+ variant?: 'default' | 'primary' | 'success' | 'brand' | 'danger'
23
+ loading?: boolean
24
+ disabled?: boolean
25
+ }>(),
26
+ {
27
+ label: '',
28
+ variant: 'default',
29
+ loading: false,
30
+ disabled: false,
31
+ },
32
+ )
33
+
34
+ const emit = defineEmits<{
35
+ click: [e: MouseEvent]
36
+ }>()
37
+ </script>
38
+
39
+ <style lang="scss" scoped>
40
+ .vm-action-btn {
41
+ display: inline-flex;
42
+ height: 36px;
43
+ align-items: center;
44
+ gap: 4px;
45
+ padding: 0 14px;
46
+ border: none;
47
+ border-radius: 12px;
48
+ background: var(--muted);
49
+ color: var(--foreground);
50
+ font-size: 13px;
51
+ font-weight: 600;
52
+ cursor: pointer;
53
+
54
+ &:hover:not(:disabled) {
55
+ background: color-mix(in srgb, var(--foreground) 8%, var(--muted));
56
+ }
57
+
58
+ &.is-primary {
59
+ background: var(--brand);
60
+ color: #fff;
61
+
62
+ &:hover:not(:disabled) {
63
+ background: var(--brand-deep);
64
+ }
65
+ }
66
+
67
+ &.is-success {
68
+ background: color-mix(in srgb, var(--success) 14%, var(--card));
69
+ color: var(--success);
70
+
71
+ &:hover:not(:disabled) {
72
+ background: color-mix(in srgb, var(--success) 22%, var(--card));
73
+ }
74
+ }
75
+
76
+ &.is-brand {
77
+ background: color-mix(in srgb, var(--brand) 12%, var(--card));
78
+ color: var(--brand);
79
+
80
+ &:hover:not(:disabled) {
81
+ background: color-mix(in srgb, var(--brand) 20%, var(--card));
82
+ }
83
+ }
84
+
85
+ &.is-danger {
86
+ background: var(--danger-soft);
87
+ color: var(--danger);
88
+
89
+ &:hover:not(:disabled) {
90
+ background: color-mix(in srgb, var(--danger) 16%, var(--card));
91
+ }
92
+ }
93
+
94
+ &:disabled {
95
+ opacity: 0.5;
96
+ cursor: not-allowed;
97
+ }
98
+
99
+ i.is-spin {
100
+ animation: vm-action-spin 0.8s linear infinite;
101
+ }
102
+ }
103
+
104
+ @keyframes vm-action-spin {
105
+ to {
106
+ transform: rotate(360deg);
107
+ }
108
+ }
109
+ </style>
@@ -0,0 +1,124 @@
1
+ <template>
2
+ <button
3
+ type="button"
4
+ class="vm-aside-list-item"
5
+ :class="{ 'is-active': active }"
6
+ @click="emit('click', $event)"
7
+ >
8
+ <span class="vm-aside-list-item__body">
9
+ <span class="vm-aside-list-item__title">
10
+ <slot />
11
+ </span>
12
+ <span v-if="subtitle || $slots.subtitle" class="vm-aside-list-item__sub">
13
+ <slot name="subtitle">{{ subtitle }}</slot>
14
+ </span>
15
+ </span>
16
+ <span v-if="$slots.ops" class="vm-aside-list-item__ops" @click.stop>
17
+ <slot name="ops" />
18
+ </span>
19
+ <i v-if="active && showArrow" class="ri-arrow-right-s-line vm-aside-list-item__arrow" />
20
+ </button>
21
+ </template>
22
+
23
+ <script setup lang="ts">
24
+ defineOptions({ name: 'vm-aside-list-item' })
25
+
26
+ withDefaults(
27
+ defineProps<{
28
+ active?: boolean
29
+ subtitle?: string
30
+ showArrow?: boolean
31
+ }>(),
32
+ {
33
+ active: false,
34
+ showArrow: true,
35
+ },
36
+ )
37
+
38
+ const emit = defineEmits<{
39
+ click: [e: MouseEvent]
40
+ }>()
41
+ </script>
42
+
43
+ <style lang="scss" scoped>
44
+ .vm-aside-list-item {
45
+ display: flex;
46
+ width: 100%;
47
+ align-items: center;
48
+ gap: 6px;
49
+ margin-bottom: 4px;
50
+ padding: 10px 10px 10px 12px;
51
+ border: none;
52
+ border-radius: 12px;
53
+ background: transparent;
54
+ text-align: left;
55
+ cursor: pointer;
56
+ transition: background 0.15s ease;
57
+
58
+ &:hover {
59
+ background: var(--muted);
60
+
61
+ .vm-aside-list-item__ops {
62
+ opacity: 1;
63
+ }
64
+ }
65
+
66
+ &.is-active {
67
+ background: var(--brand);
68
+ color: #fff;
69
+
70
+ .vm-aside-list-item__sub {
71
+ color: color-mix(in srgb, #fff 75%, transparent);
72
+ }
73
+
74
+ :deep(.vm-aside__icon-btn) {
75
+ color: #fff;
76
+
77
+ &:hover:not(:disabled) {
78
+ background: color-mix(in srgb, #fff 18%, transparent);
79
+ }
80
+
81
+ &.is-danger:hover:not(:disabled) {
82
+ background: color-mix(in srgb, #fff 22%, transparent);
83
+ }
84
+ }
85
+ }
86
+ }
87
+
88
+ .vm-aside-list-item__body {
89
+ flex: 1;
90
+ min-width: 0;
91
+ }
92
+
93
+ .vm-aside-list-item__title {
94
+ display: block;
95
+ font-size: 13px;
96
+ font-weight: 650;
97
+ overflow: hidden;
98
+ text-overflow: ellipsis;
99
+ white-space: nowrap;
100
+ }
101
+
102
+ .vm-aside-list-item__sub {
103
+ display: block;
104
+ margin-top: 2px;
105
+ font-size: 11px;
106
+ font-style: normal;
107
+ color: var(--muted-foreground);
108
+ overflow: hidden;
109
+ text-overflow: ellipsis;
110
+ white-space: nowrap;
111
+ }
112
+
113
+ .vm-aside-list-item__ops {
114
+ display: inline-flex;
115
+ gap: 2px;
116
+ opacity: 0;
117
+ transition: opacity 0.15s ease;
118
+ }
119
+
120
+ .vm-aside-list-item__arrow {
121
+ flex-shrink: 0;
122
+ font-size: 16px;
123
+ }
124
+ </style>
@@ -0,0 +1,36 @@
1
+ <template>
2
+ <div class="vm-card-grid" :style="gridStyle">
3
+ <slot />
4
+ </div>
5
+ </template>
6
+
7
+ <script setup lang="ts">
8
+ import { computed } from 'vue'
9
+
10
+ defineOptions({ name: 'vm-card-grid' })
11
+
12
+ const props = withDefaults(
13
+ defineProps<{
14
+ minWidth?: string
15
+ gap?: string
16
+ }>(),
17
+ {
18
+ minWidth: '280px',
19
+ gap: '16px',
20
+ },
21
+ )
22
+
23
+ const gridStyle = computed(() => ({
24
+ '--vm-card-min': props.minWidth,
25
+ '--vm-card-gap': props.gap,
26
+ }))
27
+ </script>
28
+
29
+ <style lang="scss" scoped>
30
+ .vm-card-grid {
31
+ display: grid;
32
+ grid-template-columns: repeat(auto-fill, minmax(var(--vm-card-min), 1fr));
33
+ gap: var(--vm-card-gap);
34
+ align-content: start;
35
+ }
36
+ </style>
@@ -0,0 +1,100 @@
1
+ <template>
2
+ <article class="vm-card" :class="{ 'is-add': add }">
3
+ <div v-if="$slots.toolbar" class="vm-card__toolbar">
4
+ <slot name="toolbar" />
5
+ </div>
6
+ <component
7
+ :is="add ? 'button' : 'div'"
8
+ class="vm-card__inner"
9
+ :type="add ? 'button' : undefined"
10
+ @click="add ? emit('click', $event) : undefined"
11
+ >
12
+ <slot />
13
+ </component>
14
+ <footer v-if="$slots.footer" class="vm-card__foot">
15
+ <slot name="footer" />
16
+ </footer>
17
+ </article>
18
+ </template>
19
+
20
+ <script setup lang="ts">
21
+ defineOptions({ name: 'vm-card' })
22
+
23
+ withDefaults(
24
+ defineProps<{
25
+ add?: boolean
26
+ }>(),
27
+ {
28
+ add: false,
29
+ },
30
+ )
31
+
32
+ const emit = defineEmits<{
33
+ click: [e: MouseEvent]
34
+ }>()
35
+ </script>
36
+
37
+ <style lang="scss" scoped>
38
+ .vm-card {
39
+ position: relative;
40
+ display: flex;
41
+ min-height: 168px;
42
+ flex-direction: column;
43
+ border-radius: 16px;
44
+ background: var(--card);
45
+ box-shadow: var(--shadow-soft);
46
+ overflow: hidden;
47
+
48
+ &.is-add {
49
+ border: 1px dashed color-mix(in srgb, var(--brand) 36%, transparent);
50
+ background: color-mix(in srgb, var(--brand) 4%, var(--card));
51
+ box-shadow: none;
52
+ }
53
+ }
54
+
55
+ .vm-card__toolbar {
56
+ position: absolute;
57
+ top: 10px;
58
+ right: 10px;
59
+ z-index: 1;
60
+ }
61
+
62
+ .vm-card__inner {
63
+ display: flex;
64
+ flex: 1;
65
+ flex-direction: column;
66
+ min-height: 0;
67
+ padding: 16px;
68
+ border: none;
69
+ background: transparent;
70
+ text-align: left;
71
+ color: inherit;
72
+
73
+ .is-add & {
74
+ align-items: center;
75
+ justify-content: center;
76
+ gap: 8px;
77
+ cursor: pointer;
78
+ color: var(--brand);
79
+ font-size: 14px;
80
+ font-weight: 650;
81
+
82
+ &:hover {
83
+ background: color-mix(in srgb, var(--brand) 6%, transparent);
84
+ }
85
+
86
+ i {
87
+ font-size: 28px;
88
+ }
89
+ }
90
+ }
91
+
92
+ .vm-card__foot {
93
+ display: flex;
94
+ align-items: center;
95
+ justify-content: space-between;
96
+ gap: 8px;
97
+ padding: 10px 16px 14px;
98
+ border-top: 1px solid color-mix(in srgb, var(--foreground) 6%, transparent);
99
+ }
100
+ </style>
@@ -0,0 +1,57 @@
1
+ <template>
2
+ <div class="vm-perm-section">
3
+ <div v-if="title || $slots.extra" class="vm-perm-section__head">
4
+ <div v-if="title" class="vm-perm-section__title">{{ title }}</div>
5
+ <div v-if="$slots.extra" class="vm-perm-section__extra">
6
+ <slot name="extra" />
7
+ </div>
8
+ </div>
9
+ <div class="vm-perm-section__body">
10
+ <slot />
11
+ </div>
12
+ </div>
13
+ </template>
14
+
15
+ <script setup lang="ts">
16
+ defineOptions({ name: 'vm-perm-section' })
17
+
18
+ defineProps<{
19
+ title?: string
20
+ }>()
21
+ </script>
22
+
23
+ <style lang="scss" scoped>
24
+ .vm-perm-section {
25
+ grid-column: span 12;
26
+ display: grid;
27
+ gap: 8px;
28
+ min-width: 0;
29
+ min-height: 0;
30
+ }
31
+
32
+ .vm-perm-section__head {
33
+ display: flex;
34
+ align-items: center;
35
+ justify-content: space-between;
36
+ gap: 12px;
37
+ }
38
+
39
+ .vm-perm-section__title {
40
+ font-size: 13px;
41
+ font-weight: 650;
42
+ color: var(--foreground);
43
+ }
44
+
45
+ .vm-perm-section__extra {
46
+ display: inline-flex;
47
+ align-items: center;
48
+ gap: 8px;
49
+ font-size: 12px;
50
+ color: var(--muted-foreground);
51
+ }
52
+
53
+ .vm-perm-section__body {
54
+ min-width: 0;
55
+ min-height: 0;
56
+ }
57
+ </style>
@@ -0,0 +1,85 @@
1
+ <template>
2
+ <div class="vm-split-layout" :style="gridStyle">
3
+ <div v-if="$slots.toolbar" class="vm-split-layout__toolbar">
4
+ <slot name="toolbar" />
5
+ </div>
6
+ <aside v-if="$slots.aside" class="vm-split-layout__aside">
7
+ <slot name="aside" />
8
+ </aside>
9
+ <div class="vm-split-layout__main">
10
+ <slot />
11
+ </div>
12
+ </div>
13
+ </template>
14
+
15
+ <script setup lang="ts">
16
+ import { computed } from 'vue'
17
+
18
+ defineOptions({ name: 'vm-split-layout' })
19
+
20
+ const props = withDefaults(
21
+ defineProps<{
22
+ asideWidth?: string
23
+ }>(),
24
+ {
25
+ asideWidth: '240px',
26
+ },
27
+ )
28
+
29
+ const gridStyle = computed(() => ({
30
+ '--vm-split-aside': props.asideWidth,
31
+ }))
32
+ </script>
33
+
34
+ <style lang="scss" scoped>
35
+ .vm-split-layout {
36
+ display: grid;
37
+ height: 100%;
38
+ min-height: 0;
39
+ overflow: hidden;
40
+ border-radius: var(--radius);
41
+ background: var(--card);
42
+ box-shadow: var(--shadow-soft);
43
+ grid-template-columns: var(--vm-split-aside) minmax(0, 1fr);
44
+ grid-template-rows: minmax(0, 1fr);
45
+
46
+ &:has(.vm-split-layout__toolbar) {
47
+ grid-template-rows: auto minmax(0, 1fr);
48
+ }
49
+ }
50
+
51
+ .vm-split-layout__toolbar {
52
+ grid-column: 1 / -1;
53
+ display: flex;
54
+ flex-wrap: wrap;
55
+ align-items: center;
56
+ justify-content: space-between;
57
+ gap: 8px;
58
+ padding: 12px 16px;
59
+ border-bottom: 1px solid color-mix(in srgb, var(--foreground) 8%, transparent);
60
+ }
61
+
62
+ .vm-split-layout__aside {
63
+ grid-row: 2;
64
+ grid-column: 1;
65
+ min-height: 0;
66
+ overflow: hidden;
67
+ border-right: 1px solid color-mix(in srgb, var(--foreground) 8%, transparent);
68
+ }
69
+
70
+ .vm-split-layout__main {
71
+ grid-row: 2;
72
+ grid-column: 2;
73
+ display: grid;
74
+ min-width: 0;
75
+ min-height: 0;
76
+ overflow: hidden;
77
+
78
+ :deep(.vm-crud) {
79
+ height: 100%;
80
+ min-height: 0;
81
+ border-radius: 0;
82
+ box-shadow: none;
83
+ }
84
+ }
85
+ </style>
@@ -1 +1 @@
1
- const _0x3d9fe8=_0x1b49;function _0x1b49(_0x579a70,_0x327aea){_0x579a70=_0x579a70-0x10b;const _0x5a64ce=_0x5a64();let _0x1b49b6=_0x5a64ce[_0x579a70];if(_0x1b49['dqCHox']===undefined){var _0x54b374=function(_0x54603c){const _0xd03005='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';let _0x462cc1='',_0x39bce8='';for(let _0x53db7e=0x0,_0x4cd6cd,_0x564f9c,_0xf7dab9=0x0;_0x564f9c=_0x54603c['charAt'](_0xf7dab9++);~_0x564f9c&&(_0x4cd6cd=_0x53db7e%0x4?_0x4cd6cd*0x40+_0x564f9c:_0x564f9c,_0x53db7e++%0x4)?_0x462cc1+=String['fromCharCode'](0xff&_0x4cd6cd>>(-0x2*_0x53db7e&0x6)):0x0){_0x564f9c=_0xd03005['indexOf'](_0x564f9c);}for(let _0x302c0d=0x0,_0x20cf9a=_0x462cc1['length'];_0x302c0d<_0x20cf9a;_0x302c0d++){_0x39bce8+='%'+('00'+_0x462cc1['charCodeAt'](_0x302c0d)['toString'](0x10))['slice'](-0x2);}return decodeURIComponent(_0x39bce8);};_0x1b49['DMjbmo']=_0x54b374,_0x1b49['nRWgpx']={},_0x1b49['dqCHox']=!![];}const _0x121504=_0x5a64ce[0x0],_0x11d4aa=_0x579a70+_0x121504,_0x58a383=_0x1b49['nRWgpx'][_0x11d4aa];return!_0x58a383?(_0x1b49b6=_0x1b49['DMjbmo'](_0x1b49b6),_0x1b49['nRWgpx'][_0x11d4aa]=_0x1b49b6):_0x1b49b6=_0x58a383,_0x1b49b6;}(function(_0x421898,_0x2f6271){const _0x3fdab8=_0x1b49,_0x2b2610=_0x421898();while(!![]){try{const _0x5c315f=parseInt(_0x3fdab8(0x118))/0x1+parseInt(_0x3fdab8(0x119))/0x2*(parseInt(_0x3fdab8(0x11a))/0x3)+parseInt(_0x3fdab8(0x10c))/0x4+parseInt(_0x3fdab8(0x10b))/0x5*(parseInt(_0x3fdab8(0x115))/0x6)+-parseInt(_0x3fdab8(0x113))/0x7+parseInt(_0x3fdab8(0x11c))/0x8+-parseInt(_0x3fdab8(0x110))/0x9;if(_0x5c315f===_0x2f6271)break;else _0x2b2610['push'](_0x2b2610['shift']());}catch(_0x59545d){_0x2b2610['push'](_0x2b2610['shift']());}}}(_0x5a64,0xba175));export const pluginDev={'docsUrl':_0x3d9fe8(0x10f),'uploadUrl':_0x3d9fe8(0x117),'scaffolds':{'full':{'key':_0x3d9fe8(0x111),'title':_0x3d9fe8(0x10e),'desc':_0x3d9fe8(0x116),'downloadUrl':'https://gitee.com/YOUR_ORG/YOUR_REPO/raw/master/plugin-full.zip'},'backend':{'key':'backend','title':_0x3d9fe8(0x112),'desc':_0x3d9fe8(0x10d),'downloadUrl':_0x3d9fe8(0x11e)},'frontend':{'key':'frontend','title':_0x3d9fe8(0x114),'desc':_0x3d9fe8(0x11d),'downloadUrl':_0x3d9fe8(0x11b)}}};function _0x5a64(){const _0x356456=['mty4mtyXndLUuxrmEeS','zNvSBa','57QV5zco56UV','nZiWnJi2ou1WuLjeyq','57QV5yMn56UV','mJq2otC4nML4u3frta','5A6m5Pw05lIA5yQH5QIH5z2x6isA5OMl5P6277YilNzVBwxVVjRLKkSGC2vYDMvYios4JIb3zwlVViNVViZMNkZLNldLVidLJ5hMIzpLJixLKi7LJ5hLUipJGii','Ahr0Chm6lY92B21LlMrLDI9WBhvNAw5Zl3vWBg9Hza','mZiXmZe1ywjNB0rI','nMPbAKvZsq','mte1nJq0m2r5tMLtEG','Ahr0Chm6lY9NAxrLzs5JB20Vwu9vuL9puKCVwu9vuL9srvbpl3jHDY9Tyxn0zxiVCgX1z2LUlwzYB250zw5KlNPPCa','mtiZnJK2qwXVs2vN','5zco5y+W5B6U5BQu55sO6isA5OMl5P6277YilNzVBwxVVjRLKkSGD2vIl++8IE+8Jos7Psb3DwPPzsdMJilOVB3NI6ZNQ4VLIy3NQ6/POBxPNAlJGii','Ahr0Chm6lY9NAxrLzs5JB20Vwu9vuL9puKCVwu9vuL9srvbpl3jHDY9Tyxn0zxiVCgX1z2LUlwjHy2TLBMqUEMLW','mtvhDKDesLG','mZCYode3nKr2zu1cyW','6zkP5A2qic8G5PYn5yQH56UV5O+s5lU26isA5OMl5P6277YilNzVBwxVVjRKU4uGC2vYDMvY77Yj77Ym6ycc5zci5lIk5lYG44cb55+T5l+H44cb5PsV5lUy562j6io95yQB5OMP5Bgv44cc','5yMn56UVicSG5zco56UV','Ahr0Chm6lY92B21LlMrLDI9KB2nZl3bSDwDPBG'];_0x5a64=function(){return _0x356456;};return _0x5a64();}
1
+ const _0x1675a6=_0xb6be;(function(_0x4fa867,_0x1358cd){const _0x3f26b4=_0xb6be,_0x285beb=_0x4fa867();while(!![]){try{const _0x5da2c1=-parseInt(_0x3f26b4(0x1a6))/0x1+parseInt(_0x3f26b4(0x19c))/0x2+parseInt(_0x3f26b4(0x1a4))/0x3*(-parseInt(_0x3f26b4(0x1ae))/0x4)+parseInt(_0x3f26b4(0x1aa))/0x5*(parseInt(_0x3f26b4(0x1a1))/0x6)+parseInt(_0x3f26b4(0x19b))/0x7*(parseInt(_0x3f26b4(0x1ac))/0x8)+-parseInt(_0x3f26b4(0x1ab))/0x9+parseInt(_0x3f26b4(0x19f))/0xa*(parseInt(_0x3f26b4(0x1a9))/0xb);if(_0x5da2c1===_0x1358cd)break;else _0x285beb['push'](_0x285beb['shift']());}catch(_0xe41ab3){_0x285beb['push'](_0x285beb['shift']());}}}(_0x2484,0x1c41a));export const pluginDev={'docsUrl':_0x1675a6(0x1a8),'uploadUrl':_0x1675a6(0x1a7),'scaffolds':{'full':{'key':'full','title':_0x1675a6(0x1ad),'desc':_0x1675a6(0x19a),'downloadUrl':_0x1675a6(0x1a3)},'backend':{'key':_0x1675a6(0x1a0),'title':_0x1675a6(0x19d),'desc':'钩子\x20/\x20服务端插件脚手架(.vome:仅\x20server),适合上传、短信、支付等能力扩展。','downloadUrl':_0x1675a6(0x1a2)},'frontend':{'key':_0x1675a6(0x1a5),'title':'纯前端','desc':'后台微应用脚手架(.vome:含\x20web/),以\x20wujie\x20挂载独立前端页面。','downloadUrl':_0x1675a6(0x19e)}}};function _0xb6be(_0x101dc3,_0x353e36){_0x101dc3=_0x101dc3-0x19a;const _0x24840a=_0x2484();let _0xb6be52=_0x24840a[_0x101dc3];if(_0xb6be['mpgVUA']===undefined){var _0x425eef=function(_0x19c0b8){const _0x2e2946='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';let _0x1ad588='',_0x844e3e='';for(let _0x5d7a3e=0x0,_0x209ace,_0x4a4f35,_0x3c0179=0x0;_0x4a4f35=_0x19c0b8['charAt'](_0x3c0179++);~_0x4a4f35&&(_0x209ace=_0x5d7a3e%0x4?_0x209ace*0x40+_0x4a4f35:_0x4a4f35,_0x5d7a3e++%0x4)?_0x1ad588+=String['fromCharCode'](0xff&_0x209ace>>(-0x2*_0x5d7a3e&0x6)):0x0){_0x4a4f35=_0x2e2946['indexOf'](_0x4a4f35);}for(let _0x1a1519=0x0,_0x1ed1b6=_0x1ad588['length'];_0x1a1519<_0x1ed1b6;_0x1a1519++){_0x844e3e+='%'+('00'+_0x1ad588['charCodeAt'](_0x1a1519)['toString'](0x10))['slice'](-0x2);}return decodeURIComponent(_0x844e3e);};_0xb6be['XxwhQN']=_0x425eef,_0xb6be['kWevRc']={},_0xb6be['mpgVUA']=!![];}const _0x124d25=_0x24840a[0x0],_0x3cac21=_0x101dc3+_0x124d25,_0x3a09ca=_0xb6be['kWevRc'][_0x3cac21];return!_0x3a09ca?(_0xb6be52=_0xb6be['XxwhQN'](_0xb6be52),_0xb6be['kWevRc'][_0x3cac21]=_0xb6be52):_0xb6be52=_0x3a09ca,_0xb6be52;}function _0x2484(){const _0x5e218b=['Ahr0Chm6lY92B21LlMrLDI9WBhvNAw5Zl3vWBg9Hza','Ahr0Chm6lY92B21LlMrLDI9KB2nZl3bSDwDPBG','mJjcv3rvwei','nu1oCuLVBW','otq0mdu1rfrgy3L3','mtu5ndy0wfH6sezb','5yMn56UVicSG5zco56UV','ofrWse9zBa','5A6m5Pw05lIA5yQH5QIH5z2x6isA5OMl5P6277YilNzVBwxVVjRLKkSGC2vYDMvYios4JIb3zwlVViNVViZMNkZLNldLVidLJ5hMIzpLJixLKi7LJ5hLUipJGii','mJfvC3PLzhm','ndq0nJaYBLr5Exz3','57QV5zco56UV','Ahr0Chm6lY9NAxrLzs5JB20Vwu9vuL9puKCVwu9vuL9srvbpl3jHDY9Tyxn0zxiVCgX1z2LUlwzYB250zw5KlNPPCa','mZy5ndKWDKDPu0Hz','yMfJA2vUza','nZC1nduYufHPufjd','Ahr0Chm6lY9NAxrLzs5JB20Vwu9vuL9puKCVwu9vuL9srvbpl3jHDY9Tyxn0zxiVCgX1z2LUlwjHy2TLBMqUEMLW','Ahr0Chm6lY9NAxrLzs5JB20Vwu9vuL9puKCVwu9vuL9srvbpl3jHDY9Tyxn0zxiVCgX1z2LUlwz1BgWUEMLW','mta5odq1r3rAreX3','zNjVBNrLBMq','mtKXmZC3Cvn0B21n'];_0x2484=function(){return _0x5e218b;};return _0x2484();}
@@ -0,0 +1,103 @@
1
+ <template>
2
+ <button
3
+ v-if="preview && src"
4
+ type="button"
5
+ class="vm-avatar"
6
+ :style="boxStyle"
7
+ :title="title"
8
+ @click="open = true"
9
+ >
10
+ <img v-if="src" class="vm-avatar__img" :src="src" alt="" />
11
+ <span v-else class="vm-avatar__fallback">{{ fallbackText }}</span>
12
+ </button>
13
+ <span v-else class="vm-avatar" :style="boxStyle">
14
+ <img v-if="src" class="vm-avatar__img" :src="src" alt="" />
15
+ <span v-else class="vm-avatar__fallback">{{ fallbackText }}</span>
16
+ </span>
17
+
18
+ <vm-preview-viewer
19
+ v-if="preview && src"
20
+ v-model:open="open"
21
+ embedded
22
+ :src="src"
23
+ :name="displayName"
24
+ />
25
+ </template>
26
+
27
+ <script setup lang="ts">
28
+ import { computed, ref } from 'vue'
29
+ import VmPreviewViewer from './vm-preview-viewer.vue'
30
+
31
+ defineOptions({ name: 'vm-avatar' })
32
+
33
+ const props = withDefaults(
34
+ defineProps<{
35
+ modelValue?: string | null
36
+ name?: string
37
+ /** 表格列从 scope 取昵称 */
38
+ nameProp?: string
39
+ scope?: Record<string, unknown>
40
+ size?: number
41
+ preview?: boolean
42
+ }>(),
43
+ {
44
+ size: 32,
45
+ preview: false,
46
+ },
47
+ )
48
+
49
+ const open = ref(false)
50
+ const src = computed(() => (props.modelValue ? String(props.modelValue) : ''))
51
+
52
+ const displayName = computed(() => {
53
+ if (props.name) return props.name
54
+ if (props.nameProp && props.scope) {
55
+ return String(props.scope[props.nameProp] || '?')
56
+ }
57
+ return '?'
58
+ })
59
+
60
+ const fallbackText = computed(() => displayName.value.slice(0, 1).toUpperCase())
61
+
62
+ const title = computed(() => displayName.value || '头像')
63
+ const boxStyle = computed(() => ({
64
+ width: `${props.size}px`,
65
+ height: `${props.size}px`,
66
+ }))
67
+ </script>
68
+
69
+ <style lang="scss" scoped>
70
+ .vm-avatar {
71
+ display: inline-flex;
72
+ flex-shrink: 0;
73
+ align-items: center;
74
+ justify-content: center;
75
+ overflow: hidden;
76
+ padding: 0;
77
+ border: none;
78
+ border-radius: 50%;
79
+ background: color-mix(in srgb, var(--brand) 12%, var(--card));
80
+ vertical-align: middle;
81
+ cursor: default;
82
+
83
+ &:is(button) {
84
+ cursor: pointer;
85
+
86
+ &:hover {
87
+ box-shadow: 0 0 0 2px color-mix(in srgb, var(--brand) 24%, transparent);
88
+ }
89
+ }
90
+ }
91
+
92
+ .vm-avatar__img {
93
+ width: 100%;
94
+ height: 100%;
95
+ object-fit: cover;
96
+ }
97
+
98
+ .vm-avatar__fallback {
99
+ font-size: 12px;
100
+ font-weight: 700;
101
+ color: var(--brand);
102
+ }
103
+ </style>