muzhiyu-ui 1.0.1 → 1.0.3
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/README.md +10 -7
- package/components/muzhiyu/mu-button/mu-button.vue +5 -1
- package/components/muzhiyu/mu-image/mu-image.vue +265 -0
- package/index.d.ts +1 -0
- package/index.js +2 -1
- package/package.json +2 -2
- package/utils/index.js +2 -0
package/README.md
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
# MuzhiyuUI 极奢全端组件库
|
|
2
2
|
|
|
3
|
-
> **MuzhiyuUI** 是一套专为 **UniApp + Vue 3** 打造的全端极奢 UI 组件库。包含 **
|
|
3
|
+
> **MuzhiyuUI** 是一套专为 **UniApp + Vue 3** 打造的全端极奢 UI 组件库。包含 **90+ 生产级高频组件**、**Axios 级拦截器 HTTP 客户端**、**格式化与正则校验工具箱**、**TypeScript 全量智能提示** 以及 **全平台暗黑模式原生适配**。
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
## 🌟 核心特性 (Key Features)
|
|
8
8
|
|
|
9
9
|
- ⚡ **Vue 3 + `<script setup>` 驱动**:全量采用 Vue 3 Composition API 重构,逻辑清晰,性能卓越;
|
|
10
|
+
- 📘 **TypeScript 原生类型支持**:内置 `index.d.ts` 声明文件与 `@vue/runtime-core` 全局组件模板智能补全,提供极其流畅的 IDE 编码体验;
|
|
10
11
|
- 🎨 **极奢流光美学设计**:拒绝大众平庸调色,采用现代微渐变、毛玻璃浮岛、Bento 悬浮卡片与动效细节;
|
|
11
12
|
- 📱 **100% 微信小程序 + H5 + App 全端兼容**:重构虚拟节点 `virtualHost: true`,彻底消除小程序自定义组件壳导致的 Flex 错位与样式压缩问题;
|
|
12
13
|
- 🌙 **原生暗黑模式 (Dark Mode)**:全量组件支持 `prefers-color-scheme: dark` 自动跟从系统深色切换;
|
|
13
14
|
- 📦 **支持 `easycom` 按需加载**:无需手动 `import` 组件,随用随写,打包体积小巧;
|
|
14
|
-
- 🛠️
|
|
15
|
+
- 🛠️ **完整工具链集结 (`$mu`)**:自带网络请求拦截器 (`http`)、表单正则校验 (`validate`)、脱敏/格式化 (`format`) 与颜色计算器 (`color`)。
|
|
15
16
|
|
|
16
17
|
---
|
|
17
18
|
|
|
@@ -19,12 +20,13 @@
|
|
|
19
20
|
|
|
20
21
|
```text
|
|
21
22
|
uni_modules/muzhiyu-ui/
|
|
22
|
-
├── components/ <--
|
|
23
|
+
├── components/ <-- 90+ 核心 UI 组件
|
|
23
24
|
├── utils/ <-- 统一网络请求与工具函数
|
|
24
25
|
│ ├── request.js <-- HTTP 客户端拦截器
|
|
25
26
|
│ └── index.js <-- 正则/格式化/脱敏工具箱
|
|
26
|
-
├── index.js <--
|
|
27
|
-
├──
|
|
27
|
+
├── index.js <-- Vue 3 插件注册入口
|
|
28
|
+
├── index.d.ts <-- TypeScript 全量类型声明文件
|
|
29
|
+
├── theme.scss <-- 全局 SCSS 主题变量
|
|
28
30
|
└── package.json <-- 插件市场配置文件
|
|
29
31
|
```
|
|
30
32
|
|
|
@@ -110,12 +112,13 @@ console.log(uni.$mu.validate.isEmail('test@example.com')) // true
|
|
|
110
112
|
|
|
111
113
|
---
|
|
112
114
|
|
|
113
|
-
## 📚
|
|
115
|
+
## 📚 90+ 核心组件全景清单
|
|
114
116
|
|
|
115
117
|
### 1. 基础组件 (Basic)
|
|
116
118
|
| 组件名 | 说明 | 示例标签 |
|
|
117
119
|
| :--- | :--- | :--- |
|
|
118
120
|
| **mu-button** | 极奢按钮(支持流光、圆角、加载、禁用与各种尺寸) | `<mu-button type="primary">确认提交</mu-button>` |
|
|
121
|
+
| **mu-image** | 极奢图片(支持骨架屏占位、失败降级、圆角/头像、全屏预览、淡入) | `<mu-image src="..." width="200" height="200" preview />` |
|
|
119
122
|
| **mu-icon** | 矢量图标(内置 100+ 高频极简图标库) | `<mu-icon name="heart" size="24" color="#F43F5E" />` |
|
|
120
123
|
| **mu-cell** | 单元格组(支持图标、箭头、副标题与右侧插槽) | `<mu-cell title="个人资料" is-link />` |
|
|
121
124
|
| **mu-tag** | 标签(支持渐变、镂空、胶囊与点状) | `<mu-tag type="success">已完成</mu-tag>` |
|
|
@@ -199,4 +202,4 @@ const gridList = [
|
|
|
199
202
|
|
|
200
203
|
## 📄 开源协议 (License)
|
|
201
204
|
|
|
202
|
-
[MIT License](
|
|
205
|
+
[MIT License](https://opensource.org/licenses/MIT)
|
|
@@ -161,7 +161,6 @@ const computedIconSize = computed(() => {
|
|
|
161
161
|
|
|
162
162
|
const defaultIconColor = computed(() => {
|
|
163
163
|
if (props.iconColor) return props.iconColor
|
|
164
|
-
if (props.color) return props.color
|
|
165
164
|
if (props.plain) {
|
|
166
165
|
switch (props.type) {
|
|
167
166
|
case 'primary': return config?.primaryColor?.value || '#171717'
|
|
@@ -171,6 +170,11 @@ const defaultIconColor = computed(() => {
|
|
|
171
170
|
default: return '#64748B'
|
|
172
171
|
}
|
|
173
172
|
}
|
|
173
|
+
// 实色按钮 (primary, danger, success, warning) 图标默认必须为纯白
|
|
174
|
+
if (props.type === 'primary' || props.type === 'danger' || props.type === 'success' || props.type === 'warning') {
|
|
175
|
+
return '#ffffff'
|
|
176
|
+
}
|
|
177
|
+
if (props.color) return props.color
|
|
174
178
|
if (props.type === 'default' || props.type === 'text' || props.type === 'link') return '#475569'
|
|
175
179
|
return '#ffffff'
|
|
176
180
|
})
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<view
|
|
3
|
+
class="mu-image"
|
|
4
|
+
:class="[
|
|
5
|
+
round && 'mu-image--round',
|
|
6
|
+
loading && 'mu-image--loading',
|
|
7
|
+
isError && 'mu-image--error'
|
|
8
|
+
]"
|
|
9
|
+
:style="wrapStyle"
|
|
10
|
+
@tap="handleClick"
|
|
11
|
+
@click="handleClick"
|
|
12
|
+
>
|
|
13
|
+
<!-- 主图片内容 -->
|
|
14
|
+
<image
|
|
15
|
+
v-if="!isError"
|
|
16
|
+
class="mu-image__inner"
|
|
17
|
+
:class="[fade && loaded && 'mu-image__inner--fade']"
|
|
18
|
+
:src="src"
|
|
19
|
+
:mode="mode"
|
|
20
|
+
:lazy-load="lazyLoad"
|
|
21
|
+
:show-menu-by-longpress="showMenuByLongpress"
|
|
22
|
+
:style="imageStyle"
|
|
23
|
+
@load="handleLoad"
|
|
24
|
+
@error="handleError"
|
|
25
|
+
/>
|
|
26
|
+
|
|
27
|
+
<!-- 加载状态/骨架屏占位 -->
|
|
28
|
+
<view
|
|
29
|
+
v-if="loading && showLoading"
|
|
30
|
+
class="mu-image__placeholder mu-image__placeholder--loading"
|
|
31
|
+
:style="placeholderStyle"
|
|
32
|
+
>
|
|
33
|
+
<slot name="loading">
|
|
34
|
+
<view v-if="loadingIcon === 'loading'" class="mu-image__spinner"></view>
|
|
35
|
+
<mu-icon v-else :name="loadingIcon" size="36" color="#94A3B8" />
|
|
36
|
+
</slot>
|
|
37
|
+
</view>
|
|
38
|
+
|
|
39
|
+
<!-- 加载失败占位 -->
|
|
40
|
+
<view
|
|
41
|
+
v-if="isError && showError"
|
|
42
|
+
class="mu-image__placeholder mu-image__placeholder--error"
|
|
43
|
+
:style="placeholderStyle"
|
|
44
|
+
>
|
|
45
|
+
<slot name="error">
|
|
46
|
+
<view class="mu-image__error-box">
|
|
47
|
+
<mu-icon :name="errorIcon" size="44" color="#CBD5E1" />
|
|
48
|
+
<text v-if="errorText" class="mu-image__error-text">{{ errorText }}</text>
|
|
49
|
+
</view>
|
|
50
|
+
</slot>
|
|
51
|
+
</view>
|
|
52
|
+
</view>
|
|
53
|
+
</template>
|
|
54
|
+
|
|
55
|
+
<script setup>
|
|
56
|
+
import { ref, computed, watch } from 'vue'
|
|
57
|
+
|
|
58
|
+
defineOptions({
|
|
59
|
+
name: 'MuImage',
|
|
60
|
+
options: {
|
|
61
|
+
virtualHost: true,
|
|
62
|
+
styleIsolation: 'shared'
|
|
63
|
+
}
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
const props = defineProps({
|
|
67
|
+
src: { type: String, default: '' },
|
|
68
|
+
mode: { type: String, default: 'aspectFill' },
|
|
69
|
+
width: { type: [Number, String], default: '100%' },
|
|
70
|
+
height: { type: [Number, String], default: '100%' },
|
|
71
|
+
radius: { type: [Number, String], default: 0 },
|
|
72
|
+
round: { type: Boolean, default: false },
|
|
73
|
+
lazyLoad: { type: Boolean, default: true },
|
|
74
|
+
showLoading: { type: Boolean, default: true },
|
|
75
|
+
showError: { type: Boolean, default: true },
|
|
76
|
+
loadingIcon: { type: String, default: 'loading' },
|
|
77
|
+
errorIcon: { type: String, default: 'image' },
|
|
78
|
+
errorText: { type: String, default: '' },
|
|
79
|
+
preview: { type: Boolean, default: false },
|
|
80
|
+
fade: { type: Boolean, default: true },
|
|
81
|
+
duration: { type: [Number, String], default: 300 },
|
|
82
|
+
bgColor: { type: String, default: '' },
|
|
83
|
+
showMenuByLongpress: { type: Boolean, default: false }
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
const emit = defineEmits(['click', 'load', 'error'])
|
|
87
|
+
|
|
88
|
+
const loading = ref(true)
|
|
89
|
+
const isError = ref(false)
|
|
90
|
+
const loaded = ref(false)
|
|
91
|
+
|
|
92
|
+
watch(
|
|
93
|
+
() => props.src,
|
|
94
|
+
(val) => {
|
|
95
|
+
if (val) {
|
|
96
|
+
loading.value = true
|
|
97
|
+
isError.value = false
|
|
98
|
+
loaded.value = false
|
|
99
|
+
} else {
|
|
100
|
+
loading.value = false
|
|
101
|
+
isError.value = true
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
{ immediate: true }
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
const formatUnit = (val) => {
|
|
108
|
+
if (typeof val === 'number') return `${val}rpx`
|
|
109
|
+
if (!val) return 'auto'
|
|
110
|
+
return /^\d+$/.test(val) ? `${val}rpx` : val
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const wrapStyle = computed(() => {
|
|
114
|
+
const style = {
|
|
115
|
+
width: formatUnit(props.width)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// 宽度适应模式下,高度自动按比例自适应
|
|
119
|
+
if (props.mode === 'widthFix') {
|
|
120
|
+
style.height = 'auto'
|
|
121
|
+
} else if (props.mode === 'heightFix') {
|
|
122
|
+
style.width = 'auto'
|
|
123
|
+
style.height = formatUnit(props.height)
|
|
124
|
+
} else {
|
|
125
|
+
style.height = formatUnit(props.height)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (props.round) {
|
|
129
|
+
style.borderRadius = '50%'
|
|
130
|
+
} else if (props.radius) {
|
|
131
|
+
style.borderRadius = formatUnit(props.radius)
|
|
132
|
+
}
|
|
133
|
+
return style
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
const placeholderStyle = computed(() => {
|
|
137
|
+
const style = {}
|
|
138
|
+
if (props.bgColor) {
|
|
139
|
+
style.backgroundColor = props.bgColor
|
|
140
|
+
}
|
|
141
|
+
return style
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
const imageStyle = computed(() => {
|
|
145
|
+
const style = {}
|
|
146
|
+
if (props.fade && !loaded.value) {
|
|
147
|
+
style.opacity = 0
|
|
148
|
+
} else {
|
|
149
|
+
style.opacity = 1
|
|
150
|
+
}
|
|
151
|
+
style.transition = `opacity ${props.duration}ms ease`
|
|
152
|
+
return style
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
function handleLoad(e) {
|
|
156
|
+
loading.value = false
|
|
157
|
+
isError.value = false
|
|
158
|
+
loaded.value = true
|
|
159
|
+
emit('load', e)
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function handleError(e) {
|
|
163
|
+
loading.value = false
|
|
164
|
+
isError.value = true
|
|
165
|
+
loaded.value = false
|
|
166
|
+
emit('error', e)
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function handleClick(e) {
|
|
170
|
+
emit('click', e)
|
|
171
|
+
if (props.preview && props.src && !isError.value) {
|
|
172
|
+
uni.previewImage({
|
|
173
|
+
urls: [props.src],
|
|
174
|
+
current: props.src
|
|
175
|
+
})
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
</script>
|
|
179
|
+
|
|
180
|
+
<style lang="scss">
|
|
181
|
+
.mu-image {
|
|
182
|
+
position: relative;
|
|
183
|
+
display: inline-block;
|
|
184
|
+
overflow: hidden;
|
|
185
|
+
box-sizing: border-box;
|
|
186
|
+
vertical-align: middle;
|
|
187
|
+
|
|
188
|
+
&--round {
|
|
189
|
+
border-radius: 50% !important;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
&__inner {
|
|
193
|
+
width: 100%;
|
|
194
|
+
height: 100%;
|
|
195
|
+
display: block;
|
|
196
|
+
will-change: opacity;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
&__placeholder {
|
|
200
|
+
position: absolute;
|
|
201
|
+
top: 0;
|
|
202
|
+
left: 0;
|
|
203
|
+
right: 0;
|
|
204
|
+
bottom: 0;
|
|
205
|
+
display: flex;
|
|
206
|
+
align-items: center;
|
|
207
|
+
justify-content: center;
|
|
208
|
+
z-index: 1;
|
|
209
|
+
background-color: var(--mu-bg-gray, rgba(241, 245, 249, 0.7));
|
|
210
|
+
transition: background-color 0.3s ease;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
&__spinner {
|
|
214
|
+
width: 36rpx;
|
|
215
|
+
height: 36rpx;
|
|
216
|
+
border: 4rpx solid rgba(148, 163, 184, 0.2);
|
|
217
|
+
border-top-color: #94A3B8;
|
|
218
|
+
border-radius: 50%;
|
|
219
|
+
animation: mu-image-spin 0.8s linear infinite;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
&__error-box {
|
|
223
|
+
display: flex;
|
|
224
|
+
flex-direction: column;
|
|
225
|
+
align-items: center;
|
|
226
|
+
justify-content: center;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
&__error-text {
|
|
230
|
+
font-size: 22rpx;
|
|
231
|
+
color: #94A3B8;
|
|
232
|
+
margin-top: 8rpx;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
@keyframes mu-image-spin {
|
|
237
|
+
0% {
|
|
238
|
+
transform: rotate(0deg);
|
|
239
|
+
}
|
|
240
|
+
100% {
|
|
241
|
+
transform: rotate(360deg);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/* 暗黑模式自适应跟从 */
|
|
246
|
+
.mu-dark {
|
|
247
|
+
.mu-image__placeholder {
|
|
248
|
+
background-color: rgba(30, 30, 44, 0.8) !important;
|
|
249
|
+
}
|
|
250
|
+
.mu-image__spinner {
|
|
251
|
+
border-color: rgba(255, 255, 255, 0.15);
|
|
252
|
+
border-top-color: #FFFFFF;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
@media (prefers-color-scheme: dark) {
|
|
257
|
+
.mu-image__placeholder {
|
|
258
|
+
background-color: rgba(30, 30, 44, 0.8);
|
|
259
|
+
}
|
|
260
|
+
.mu-image__spinner {
|
|
261
|
+
border-color: rgba(255, 255, 255, 0.15);
|
|
262
|
+
border-top-color: #FFFFFF;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
</style>
|
package/index.d.ts
CHANGED
|
@@ -162,6 +162,7 @@ declare module '@vue/runtime-core' {
|
|
|
162
162
|
MuGridItem: typeof import('./components/muzhiyu/mu-grid-item/mu-grid-item.vue')['default']
|
|
163
163
|
MuHtml: typeof import('./components/muzhiyu/mu-html/mu-html.vue')['default']
|
|
164
164
|
MuIcon: typeof import('./components/muzhiyu/mu-icon/mu-icon.vue')['default']
|
|
165
|
+
MuImage: typeof import('./components/muzhiyu/mu-image/mu-image.vue')['default']
|
|
165
166
|
MuImageGrid: typeof import('./components/muzhiyu/mu-image-grid/mu-image-grid.vue')['default']
|
|
166
167
|
MuImagePreview: typeof import('./components/muzhiyu/mu-image-preview/mu-image-preview.vue')['default']
|
|
167
168
|
MuIndexList: typeof import('./components/muzhiyu/mu-index-list/mu-index-list.vue')['default']
|
package/index.js
CHANGED
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
* @version 1.0.0
|
|
6
6
|
*/
|
|
7
7
|
import http from './utils/request.js'
|
|
8
|
-
import { util, validate,
|
|
8
|
+
import { util, validate, validator, color } from './utils/index.js'
|
|
9
|
+
const format = util // 别名挂载
|
|
9
10
|
|
|
10
11
|
// Toast 快捷 JS 弹窗函数
|
|
11
12
|
const toast = (title, type, options = {}) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "muzhiyu-ui",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "基于 UniApp Vue3 + SCSS 打造的全端极奢组件库",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"uni_modules": {
|
|
30
30
|
"id": "muzhiyu-ui",
|
|
31
31
|
"name": "muzhiyu-ui 极奢组件库",
|
|
32
|
-
"version": "1.0.
|
|
32
|
+
"version": "1.0.3",
|
|
33
33
|
"description": "基于 UniApp Vue3 + SCSS 打造的全端极奢 UI 组件库",
|
|
34
34
|
"site": "",
|
|
35
35
|
"displayName": "MuzhiyuUI 极奢全端组件库"
|
package/utils/index.js
CHANGED
|
@@ -529,6 +529,8 @@ util.notify.close = () => uni.$emit('mu-notify-close')
|
|
|
529
529
|
util.loadingPage.hide = () => uni.$emit('mu-loading-page-hide')
|
|
530
530
|
util.loadingPage.close = () => uni.$emit('mu-loading-page-hide')
|
|
531
531
|
|
|
532
|
+
export const format = util
|
|
533
|
+
|
|
532
534
|
/**
|
|
533
535
|
* 全局统一导出核心入口对象 (mu)
|
|
534
536
|
*/
|