lxui-uni 0.0.8 → 0.1.0
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 +11 -6
- package/components/lx-header/lx-header.vue +250 -250
- package/components/lx-hello/lx-hello.vue +25 -25
- package/components/lx-image/index.ts +2 -2
- package/components/lx-image/lx-image.vue +101 -101
- package/components/lx-list/lx-list.vue +103 -163
- package/components/lx-list-state/lx-list-state.vue +92 -92
- package/components/lx-operate-bottom/lx-operate-bottom.vue +116 -116
- package/components/lx-submit-btn/lx-submit-btn.vue +61 -61
- package/components/lx-tabbar/lx-tabbar.vue +99 -99
- package/components/lx-upload/lx-upload.vue +186 -186
- package/index.ts +46 -46
- package/libs/config/index.ts +21 -21
- package/libs/hooks/useListLoadClass/index.ts +145 -143
- package/libs/util/index.ts +294 -294
- package/package.json +13 -13
- package/theme.scss +232 -232
- package/types/components.d.ts +23 -23
- package/types/index.d.ts +25 -25
|
@@ -1,99 +1,99 @@
|
|
|
1
|
-
<script lang="ts" setup>
|
|
2
|
-
import { ref, watchEffect } from 'vue';
|
|
3
|
-
|
|
4
|
-
// tabbar选中项name
|
|
5
|
-
const selectTabbarName = ref('home')
|
|
6
|
-
const url = ref('/static/tabbar/')
|
|
7
|
-
|
|
8
|
-
interface menuListInt {
|
|
9
|
-
name: string
|
|
10
|
-
text: string
|
|
11
|
-
pagePath: string
|
|
12
|
-
}
|
|
13
|
-
const props = withDefaults(defineProps<{ menuList: menuListInt[]; current: string; activeColor?: string; defaultColor?: string }>(), {
|
|
14
|
-
menuList: () => [],
|
|
15
|
-
current: '',
|
|
16
|
-
activeColor: '#5c616f',
|
|
17
|
-
defaultColor: '#7d7e80'
|
|
18
|
-
})
|
|
19
|
-
|
|
20
|
-
// tabbar点击事件
|
|
21
|
-
function clickTabbar({ name, pagePath }: menuListInt) {
|
|
22
|
-
if (name === selectTabbarName.value) return
|
|
23
|
-
selectTabbarName.value = name
|
|
24
|
-
uni.goToPage({ url: pagePath || 'pages/index/index', mode: 'reLaunch' })
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const getImageUrl = (name: string) => `${url.value}${name}${selectTabbarName.value !== name ? '' : '-act'}.png`
|
|
28
|
-
|
|
29
|
-
watchEffect(() => {
|
|
30
|
-
if (props.current !== '') {
|
|
31
|
-
selectTabbarName.value = props.current
|
|
32
|
-
}
|
|
33
|
-
})
|
|
34
|
-
</script>
|
|
35
|
-
<template>
|
|
36
|
-
<view class="main_tabbar flex-center-between">
|
|
37
|
-
<view class="flex column tabbar_item" v-for="item in menuList" :key="item.text" @click="clickTabbar(item)">
|
|
38
|
-
<view :class="{ icon_box: true }">
|
|
39
|
-
<image :src="getImageUrl(item.name)" mode="aspectFit" />
|
|
40
|
-
</view>
|
|
41
|
-
<view class="name" :style="{ color: selectTabbarName === item.name && activeColor ? activeColor : defaultColor }">{{ item.text }}</view>
|
|
42
|
-
</view>
|
|
43
|
-
</view>
|
|
44
|
-
<view class="tabbar_mark" />
|
|
45
|
-
</template>
|
|
46
|
-
|
|
47
|
-
<style scoped lang="scss">
|
|
48
|
-
// 填充高度
|
|
49
|
-
.tabbar_mark {
|
|
50
|
-
/* 兼容 iOS < 11.2 */
|
|
51
|
-
padding-bottom: constant(safe-area-inset-bottom);
|
|
52
|
-
/* 兼容 iOS >= 11.2 */
|
|
53
|
-
padding-bottom: env(safe-area-inset-bottom);
|
|
54
|
-
padding-top: 10rpx;
|
|
55
|
-
height: 100rpx;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
.main_tabbar {
|
|
59
|
-
width: 100%;
|
|
60
|
-
box-sizing: border-box;
|
|
61
|
-
padding: 10rpx 40rpx 0;
|
|
62
|
-
background-color: #fff;
|
|
63
|
-
box-shadow: 0 0 16rpx #ddd;
|
|
64
|
-
border-radius: 40rpx 40rpx 0 0;
|
|
65
|
-
/* 兼容 iOS < 11.2 */
|
|
66
|
-
padding-bottom: constant(safe-area-inset-bottom);
|
|
67
|
-
/* 兼容 iOS >= 11.2 */
|
|
68
|
-
padding-bottom: env(safe-area-inset-bottom);
|
|
69
|
-
position: fixed;
|
|
70
|
-
left: 0;
|
|
71
|
-
bottom: 0;
|
|
72
|
-
z-index: 999;
|
|
73
|
-
|
|
74
|
-
.tabbar_item {
|
|
75
|
-
height: 100rpx;
|
|
76
|
-
|
|
77
|
-
@if env(safe-area-inset-bottom) {
|
|
78
|
-
padding-bottom: 0;
|
|
79
|
-
} @else {
|
|
80
|
-
padding-bottom: 10rpx;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
.icon_box {
|
|
84
|
-
width: 40rpx;
|
|
85
|
-
height: 40rpx;
|
|
86
|
-
|
|
87
|
-
image {
|
|
88
|
-
width: 100%;
|
|
89
|
-
height: 100%;
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
.name {
|
|
94
|
-
margin-top: 6rpx;
|
|
95
|
-
font-size: 26rpx;
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
</style>
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import { ref, watchEffect } from 'vue';
|
|
3
|
+
|
|
4
|
+
// tabbar选中项name
|
|
5
|
+
const selectTabbarName = ref('home')
|
|
6
|
+
const url = ref('/static/tabbar/')
|
|
7
|
+
|
|
8
|
+
interface menuListInt {
|
|
9
|
+
name: string
|
|
10
|
+
text: string
|
|
11
|
+
pagePath: string
|
|
12
|
+
}
|
|
13
|
+
const props = withDefaults(defineProps<{ menuList: menuListInt[]; current: string; activeColor?: string; defaultColor?: string }>(), {
|
|
14
|
+
menuList: () => [],
|
|
15
|
+
current: '',
|
|
16
|
+
activeColor: '#5c616f',
|
|
17
|
+
defaultColor: '#7d7e80'
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
// tabbar点击事件
|
|
21
|
+
function clickTabbar({ name, pagePath }: menuListInt) {
|
|
22
|
+
if (name === selectTabbarName.value) return
|
|
23
|
+
selectTabbarName.value = name
|
|
24
|
+
uni.goToPage({ url: pagePath || 'pages/index/index', mode: 'reLaunch' })
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const getImageUrl = (name: string) => `${url.value}${name}${selectTabbarName.value !== name ? '' : '-act'}.png`
|
|
28
|
+
|
|
29
|
+
watchEffect(() => {
|
|
30
|
+
if (props.current !== '') {
|
|
31
|
+
selectTabbarName.value = props.current
|
|
32
|
+
}
|
|
33
|
+
})
|
|
34
|
+
</script>
|
|
35
|
+
<template>
|
|
36
|
+
<view class="main_tabbar flex-center-between">
|
|
37
|
+
<view class="flex column tabbar_item" v-for="item in menuList" :key="item.text" @click="clickTabbar(item)">
|
|
38
|
+
<view :class="{ icon_box: true }">
|
|
39
|
+
<image :src="getImageUrl(item.name)" mode="aspectFit" />
|
|
40
|
+
</view>
|
|
41
|
+
<view class="name" :style="{ color: selectTabbarName === item.name && activeColor ? activeColor : defaultColor }">{{ item.text }}</view>
|
|
42
|
+
</view>
|
|
43
|
+
</view>
|
|
44
|
+
<view class="tabbar_mark" />
|
|
45
|
+
</template>
|
|
46
|
+
|
|
47
|
+
<style scoped lang="scss">
|
|
48
|
+
// 填充高度
|
|
49
|
+
.tabbar_mark {
|
|
50
|
+
/* 兼容 iOS < 11.2 */
|
|
51
|
+
padding-bottom: constant(safe-area-inset-bottom);
|
|
52
|
+
/* 兼容 iOS >= 11.2 */
|
|
53
|
+
padding-bottom: env(safe-area-inset-bottom);
|
|
54
|
+
padding-top: 10rpx;
|
|
55
|
+
height: 100rpx;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.main_tabbar {
|
|
59
|
+
width: 100%;
|
|
60
|
+
box-sizing: border-box;
|
|
61
|
+
padding: 10rpx 40rpx 0;
|
|
62
|
+
background-color: #fff;
|
|
63
|
+
box-shadow: 0 0 16rpx #ddd;
|
|
64
|
+
border-radius: 40rpx 40rpx 0 0;
|
|
65
|
+
/* 兼容 iOS < 11.2 */
|
|
66
|
+
padding-bottom: constant(safe-area-inset-bottom);
|
|
67
|
+
/* 兼容 iOS >= 11.2 */
|
|
68
|
+
padding-bottom: env(safe-area-inset-bottom);
|
|
69
|
+
position: fixed;
|
|
70
|
+
left: 0;
|
|
71
|
+
bottom: 0;
|
|
72
|
+
z-index: 999;
|
|
73
|
+
|
|
74
|
+
.tabbar_item {
|
|
75
|
+
height: 100rpx;
|
|
76
|
+
|
|
77
|
+
@if env(safe-area-inset-bottom) {
|
|
78
|
+
padding-bottom: 0;
|
|
79
|
+
} @else {
|
|
80
|
+
padding-bottom: 10rpx;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.icon_box {
|
|
84
|
+
width: 40rpx;
|
|
85
|
+
height: 40rpx;
|
|
86
|
+
|
|
87
|
+
image {
|
|
88
|
+
width: 100%;
|
|
89
|
+
height: 100%;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.name {
|
|
94
|
+
margin-top: 6rpx;
|
|
95
|
+
font-size: 26rpx;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
</style>
|
|
@@ -1,186 +1,186 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
/*
|
|
3
|
-
* @description: 上传组件
|
|
4
|
-
* @fileName: lx-upload.vue
|
|
5
|
-
* @params
|
|
6
|
-
* @author: lxx
|
|
7
|
-
* @date: 2024-03-08 14:08:27"
|
|
8
|
-
* @version: V1.0.0
|
|
9
|
-
*/
|
|
10
|
-
import { uploadUrl, uploadBaseUrl } from '../../libs/config'
|
|
11
|
-
import { getCurrentInstance, onMounted, ref } from 'vue'
|
|
12
|
-
const baseUrl = uploadBaseUrl ? uploadBaseUrl : import.meta.env.VITE_APP_BASE_URL
|
|
13
|
-
console.log(baseUrl, 'baseUrl');
|
|
14
|
-
|
|
15
|
-
const delIcon = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACoAAAAqBAMAAAA37dRoAAAAD1BMVEVHcEz8/Pz7+/v7+/v8/PxqU4qsAAAABHRSTlMAv39Ao+r7HgAAAOxJREFUeJx9U9sRhCAM5GEB55AC0LEAnbkCFOm/phOS5cDX/jism80DohTDDn3sx1k1WChm+IaMQEWbGG9oqtjtGp+wS/rYggvpTuzn4lqcTTzDw2CXgAX5ppxBS34Si3QK4j4rzR8jTppDLVfRwYlypR2zX1Su8S+JptImzezzZ5PYlo42dMYz0bFleSYEth6gxuGBpfpm6OQbbrMdUlPEQXpLUvIQg81EgHjlv3KGWNgPH4OM2/NAMXXPIjh5pCEJyemCFDiw7dPNX15J02bB+vL6pD3Avb7qhw2oaFcvkdCbaxfOjscWOmzhD3EZlfmDEVdKAAAAAElFTkSuQmCC'
|
|
16
|
-
const addIcon = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADYAAAAwBAMAAACoMpTzAAAAD1BMVEVHcEzDw8PFxcXExMTExMTLnAMjAAAABHRSTlMAQH+/MHAgfgAAAQlJREFUeJyNlNsZgzAIhYl2AKMOEK0D2OoAGtl/pnqLQDB+5Qn9BeFAAhCsxMM+oG082aJRjsGcYvXFfDLlTdIM8S6psavVjM3bC7uhDu/Ny2TSHNRJ5ll96BtrB2T1kt/slVX0AlTllWakRqeY0zIAD2uHpmCBwMK6U8wsYtfn8+pJxgRyV9KTTTTA1X0J1tMAKcXJCjBMBMmifUmyXrHXxSbJloiNqTidM/uLudAPRHXzXkmXjnTJYz1z+l0dz8GMIWWQls02C46R89uS7hv2ZUdR7IspxXED1nFqz3Ap1Cmlln28uowdkYYQZ4hD++aPgkX2yMYkWpJXwbb9T3fB0x0CYG9tbfYHtAj7ZE5t1ToAAAAASUVORK5CYII='
|
|
17
|
-
interface propsInt {
|
|
18
|
-
modelValue: string[] | string // 上传的图片
|
|
19
|
-
width?: string // 盒子宽度
|
|
20
|
-
radius?: string // 盒子圆角
|
|
21
|
-
spacing?: number // 间隔
|
|
22
|
-
rowNum?: number // 一行的盒子数
|
|
23
|
-
limit?: number // 最多上传个数
|
|
24
|
-
readOnly?: boolean // 是否只读
|
|
25
|
-
}
|
|
26
|
-
const props = withDefaults(defineProps<propsInt>(), {
|
|
27
|
-
radius: '16rpx',
|
|
28
|
-
spacing: 10,
|
|
29
|
-
rowNum: 3,
|
|
30
|
-
limit: 3,
|
|
31
|
-
modelValue: () => [],
|
|
32
|
-
readOnly: false
|
|
33
|
-
})
|
|
34
|
-
const emit = defineEmits(['update:modelValue'])
|
|
35
|
-
// 上传
|
|
36
|
-
const uploadAdd = () => {
|
|
37
|
-
uni.chooseImage({
|
|
38
|
-
count: 1, //默认9
|
|
39
|
-
// sizeType: ['original', 'compressed'], //可以指定是原图还是压缩图,默认二者都有
|
|
40
|
-
// sourceType: ['album'], //从相册选择
|
|
41
|
-
success: (chooseImageRes) => {
|
|
42
|
-
const tempFilePaths = chooseImageRes.tempFilePaths
|
|
43
|
-
uni.uploadFile({
|
|
44
|
-
url: baseUrl + uploadUrl,
|
|
45
|
-
filePath: tempFilePaths[0],
|
|
46
|
-
name: 'file',
|
|
47
|
-
success: (uploadFileRes: any) => {
|
|
48
|
-
const { data, code } = JSON.parse(uploadFileRes.data)
|
|
49
|
-
if (code === 200) {
|
|
50
|
-
// 如果是一张图片传入数据可以是字符串
|
|
51
|
-
if (props.limit === 1) {
|
|
52
|
-
emit('update:modelValue', data)
|
|
53
|
-
} else {
|
|
54
|
-
// eslint-disable-next-line prettier/prettier
|
|
55
|
-
(props.modelValue as string[]).push(data)
|
|
56
|
-
emit('update:modelValue', props.modelValue)
|
|
57
|
-
}
|
|
58
|
-
} else {
|
|
59
|
-
uni.showToast({
|
|
60
|
-
title: '上传失败',
|
|
61
|
-
icon: 'none',
|
|
62
|
-
mask: true
|
|
63
|
-
})
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
})
|
|
67
|
-
}
|
|
68
|
-
})
|
|
69
|
-
}
|
|
70
|
-
// 删除
|
|
71
|
-
const uploadDel = (index?: number) => {
|
|
72
|
-
if (props.limit === 1) {
|
|
73
|
-
emit('update:modelValue', '')
|
|
74
|
-
} else {
|
|
75
|
-
// eslint-disable-next-line prettier/prettier, no-extra-semi
|
|
76
|
-
; (props.modelValue as string[]).splice(index as number, 1)
|
|
77
|
-
emit('update:modelValue', props.modelValue)
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
// 盒子宽度
|
|
81
|
-
const boxWidth = ref('')
|
|
82
|
-
onMounted(() => {
|
|
83
|
-
const query = uni.createSelectorQuery().in(getCurrentInstance())
|
|
84
|
-
query
|
|
85
|
-
.select('.lx_upload_box')
|
|
86
|
-
.boundingClientRect((data: any) => {
|
|
87
|
-
// console.log('得到布局位置信息' + data)
|
|
88
|
-
// console.log('节点离页面顶部的距离为' + data.width, Math.floor(data.width))
|
|
89
|
-
// 没有传大小就计算大小
|
|
90
|
-
if (!props.width) {
|
|
91
|
-
boxWidth.value = Math.floor((data.width - props.spacing * (props.rowNum - 1)) / props.rowNum) + 'px'
|
|
92
|
-
console.log(boxWidth.value, ' boxWidth.value')
|
|
93
|
-
} else {
|
|
94
|
-
boxWidth.value = props.width
|
|
95
|
-
}
|
|
96
|
-
})
|
|
97
|
-
.exec()
|
|
98
|
-
})
|
|
99
|
-
|
|
100
|
-
const previewImage = (urlsList: string[], index?: number) => {
|
|
101
|
-
// 预览图片
|
|
102
|
-
const urls = urlsList.map((item: string) => {
|
|
103
|
-
if (item && item.indexOf('http') === -1) {
|
|
104
|
-
return baseUrl + item
|
|
105
|
-
} else {
|
|
106
|
-
return item
|
|
107
|
-
}
|
|
108
|
-
})
|
|
109
|
-
const uniqueValues = new Set(urls)
|
|
110
|
-
// 如果预览的图片地址相同,则直接预览,预览索引设置无效
|
|
111
|
-
if (uniqueValues.size === 1) {
|
|
112
|
-
uni.previewImage({
|
|
113
|
-
urls
|
|
114
|
-
})
|
|
115
|
-
} else {
|
|
116
|
-
uni.previewImage({
|
|
117
|
-
current: index,
|
|
118
|
-
urls
|
|
119
|
-
})
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
</script>
|
|
123
|
-
<template>
|
|
124
|
-
<view class="lx_upload_box flex-center-start wrap">
|
|
125
|
-
<view v-if="limit === 1 && modelValue" class="upload_item_box" :style="{
|
|
126
|
-
width: boxWidth,
|
|
127
|
-
height: boxWidth,
|
|
128
|
-
borderRadius: radius
|
|
129
|
-
}">
|
|
130
|
-
<image :src="baseUrl + modelValue" mode="aspectFill" @click="previewImage([modelValue as string])"></image>
|
|
131
|
-
<view class="upload_close_icon flex" v-if="!readOnly" @click="uploadDel()">
|
|
132
|
-
<image :src="delIcon" mode="aspectFit" />
|
|
133
|
-
</view>
|
|
134
|
-
</view>
|
|
135
|
-
<view v-else class="upload_item_box" :style="{
|
|
136
|
-
width: boxWidth,
|
|
137
|
-
height: boxWidth,
|
|
138
|
-
borderRadius: radius,
|
|
139
|
-
marginRight: (index + 1) % props.rowNum === 0 ? 0 : spacing + 'px',
|
|
140
|
-
marginBottom: spacing + 'px'
|
|
141
|
-
}" v-for="(item, index) in modelValue" :key="index">
|
|
142
|
-
<image :src="baseUrl + item" mode="aspectFill" @click="previewImage(modelValue as string[], index)"></image>
|
|
143
|
-
<view class="upload_close_icon flex" v-if="!readOnly" @click="uploadDel(index)">
|
|
144
|
-
<image :src="delIcon" mode="aspectFit" />
|
|
145
|
-
</view>
|
|
146
|
-
</view>
|
|
147
|
-
|
|
148
|
-
<view v-if="modelValue.length < limit && !readOnly" class="upload_add_box flex" @click="uploadAdd"
|
|
149
|
-
:style="{ width: boxWidth, height: boxWidth, borderRadius: radius, marginBottom: spacing + 'px' }">
|
|
150
|
-
<view class="upload_add">
|
|
151
|
-
<image :src="addIcon" mode="aspectFit"></image>
|
|
152
|
-
</view>
|
|
153
|
-
</view>
|
|
154
|
-
</view>
|
|
155
|
-
</template>
|
|
156
|
-
<style lang="scss" scoped>
|
|
157
|
-
.lx_upload_box {
|
|
158
|
-
.upload_add_box {
|
|
159
|
-
background: rgba(240, 240, 240, 1);
|
|
160
|
-
|
|
161
|
-
.upload_add {
|
|
162
|
-
width: 36rpx;
|
|
163
|
-
height: 36rpx;
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// 上传的图片
|
|
168
|
-
.upload_item_box {
|
|
169
|
-
overflow: hidden;
|
|
170
|
-
box-sizing: border-box;
|
|
171
|
-
position: relative;
|
|
172
|
-
|
|
173
|
-
.upload_close_icon {
|
|
174
|
-
background-color: rgba($color: #000000, $alpha: 0.3);
|
|
175
|
-
box-shadow: 0 0 8rpx 0 rgba(0, 0, 0, 0.5);
|
|
176
|
-
border-radius: 50%;
|
|
177
|
-
width: 28rpx;
|
|
178
|
-
height: 28rpx;
|
|
179
|
-
position: absolute;
|
|
180
|
-
top: 8rpx;
|
|
181
|
-
right: 8rpx;
|
|
182
|
-
z-index: 5;
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
</style>
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/*
|
|
3
|
+
* @description: 上传组件
|
|
4
|
+
* @fileName: lx-upload.vue
|
|
5
|
+
* @params
|
|
6
|
+
* @author: lxx
|
|
7
|
+
* @date: 2024-03-08 14:08:27"
|
|
8
|
+
* @version: V1.0.0
|
|
9
|
+
*/
|
|
10
|
+
import { uploadUrl, uploadBaseUrl } from '../../libs/config'
|
|
11
|
+
import { getCurrentInstance, onMounted, ref } from 'vue'
|
|
12
|
+
const baseUrl = uploadBaseUrl ? uploadBaseUrl : import.meta.env.VITE_APP_BASE_URL
|
|
13
|
+
console.log(baseUrl, 'baseUrl');
|
|
14
|
+
|
|
15
|
+
const delIcon = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACoAAAAqBAMAAAA37dRoAAAAD1BMVEVHcEz8/Pz7+/v7+/v8/PxqU4qsAAAABHRSTlMAv39Ao+r7HgAAAOxJREFUeJx9U9sRhCAM5GEB55AC0LEAnbkCFOm/phOS5cDX/jism80DohTDDn3sx1k1WChm+IaMQEWbGG9oqtjtGp+wS/rYggvpTuzn4lqcTTzDw2CXgAX5ppxBS34Si3QK4j4rzR8jTppDLVfRwYlypR2zX1Su8S+JptImzezzZ5PYlo42dMYz0bFleSYEth6gxuGBpfpm6OQbbrMdUlPEQXpLUvIQg81EgHjlv3KGWNgPH4OM2/NAMXXPIjh5pCEJyemCFDiw7dPNX15J02bB+vL6pD3Avb7qhw2oaFcvkdCbaxfOjscWOmzhD3EZlfmDEVdKAAAAAElFTkSuQmCC'
|
|
16
|
+
const addIcon = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADYAAAAwBAMAAACoMpTzAAAAD1BMVEVHcEzDw8PFxcXExMTExMTLnAMjAAAABHRSTlMAQH+/MHAgfgAAAQlJREFUeJyNlNsZgzAIhYl2AKMOEK0D2OoAGtl/pnqLQDB+5Qn9BeFAAhCsxMM+oG082aJRjsGcYvXFfDLlTdIM8S6psavVjM3bC7uhDu/Ny2TSHNRJ5ll96BtrB2T1kt/slVX0AlTllWakRqeY0zIAD2uHpmCBwMK6U8wsYtfn8+pJxgRyV9KTTTTA1X0J1tMAKcXJCjBMBMmifUmyXrHXxSbJloiNqTidM/uLudAPRHXzXkmXjnTJYz1z+l0dz8GMIWWQls02C46R89uS7hv2ZUdR7IspxXED1nFqz3Ap1Cmlln28uowdkYYQZ4hD++aPgkX2yMYkWpJXwbb9T3fB0x0CYG9tbfYHtAj7ZE5t1ToAAAAASUVORK5CYII='
|
|
17
|
+
interface propsInt {
|
|
18
|
+
modelValue: string[] | string // 上传的图片
|
|
19
|
+
width?: string // 盒子宽度
|
|
20
|
+
radius?: string // 盒子圆角
|
|
21
|
+
spacing?: number // 间隔
|
|
22
|
+
rowNum?: number // 一行的盒子数
|
|
23
|
+
limit?: number // 最多上传个数
|
|
24
|
+
readOnly?: boolean // 是否只读
|
|
25
|
+
}
|
|
26
|
+
const props = withDefaults(defineProps<propsInt>(), {
|
|
27
|
+
radius: '16rpx',
|
|
28
|
+
spacing: 10,
|
|
29
|
+
rowNum: 3,
|
|
30
|
+
limit: 3,
|
|
31
|
+
modelValue: () => [],
|
|
32
|
+
readOnly: false
|
|
33
|
+
})
|
|
34
|
+
const emit = defineEmits(['update:modelValue'])
|
|
35
|
+
// 上传
|
|
36
|
+
const uploadAdd = () => {
|
|
37
|
+
uni.chooseImage({
|
|
38
|
+
count: 1, //默认9
|
|
39
|
+
// sizeType: ['original', 'compressed'], //可以指定是原图还是压缩图,默认二者都有
|
|
40
|
+
// sourceType: ['album'], //从相册选择
|
|
41
|
+
success: (chooseImageRes) => {
|
|
42
|
+
const tempFilePaths = chooseImageRes.tempFilePaths
|
|
43
|
+
uni.uploadFile({
|
|
44
|
+
url: baseUrl + uploadUrl,
|
|
45
|
+
filePath: tempFilePaths[0],
|
|
46
|
+
name: 'file',
|
|
47
|
+
success: (uploadFileRes: any) => {
|
|
48
|
+
const { data, code } = JSON.parse(uploadFileRes.data)
|
|
49
|
+
if (code === 200) {
|
|
50
|
+
// 如果是一张图片传入数据可以是字符串
|
|
51
|
+
if (props.limit === 1) {
|
|
52
|
+
emit('update:modelValue', data)
|
|
53
|
+
} else {
|
|
54
|
+
// eslint-disable-next-line prettier/prettier
|
|
55
|
+
(props.modelValue as string[]).push(data)
|
|
56
|
+
emit('update:modelValue', props.modelValue)
|
|
57
|
+
}
|
|
58
|
+
} else {
|
|
59
|
+
uni.showToast({
|
|
60
|
+
title: '上传失败',
|
|
61
|
+
icon: 'none',
|
|
62
|
+
mask: true
|
|
63
|
+
})
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
})
|
|
67
|
+
}
|
|
68
|
+
})
|
|
69
|
+
}
|
|
70
|
+
// 删除
|
|
71
|
+
const uploadDel = (index?: number) => {
|
|
72
|
+
if (props.limit === 1) {
|
|
73
|
+
emit('update:modelValue', '')
|
|
74
|
+
} else {
|
|
75
|
+
// eslint-disable-next-line prettier/prettier, no-extra-semi
|
|
76
|
+
; (props.modelValue as string[]).splice(index as number, 1)
|
|
77
|
+
emit('update:modelValue', props.modelValue)
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
// 盒子宽度
|
|
81
|
+
const boxWidth = ref('')
|
|
82
|
+
onMounted(() => {
|
|
83
|
+
const query = uni.createSelectorQuery().in(getCurrentInstance())
|
|
84
|
+
query
|
|
85
|
+
.select('.lx_upload_box')
|
|
86
|
+
.boundingClientRect((data: any) => {
|
|
87
|
+
// console.log('得到布局位置信息' + data)
|
|
88
|
+
// console.log('节点离页面顶部的距离为' + data.width, Math.floor(data.width))
|
|
89
|
+
// 没有传大小就计算大小
|
|
90
|
+
if (!props.width) {
|
|
91
|
+
boxWidth.value = Math.floor((data.width - props.spacing * (props.rowNum - 1)) / props.rowNum) + 'px'
|
|
92
|
+
console.log(boxWidth.value, ' boxWidth.value')
|
|
93
|
+
} else {
|
|
94
|
+
boxWidth.value = props.width
|
|
95
|
+
}
|
|
96
|
+
})
|
|
97
|
+
.exec()
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
const previewImage = (urlsList: string[], index?: number) => {
|
|
101
|
+
// 预览图片
|
|
102
|
+
const urls = urlsList.map((item: string) => {
|
|
103
|
+
if (item && item.indexOf('http') === -1) {
|
|
104
|
+
return baseUrl + item
|
|
105
|
+
} else {
|
|
106
|
+
return item
|
|
107
|
+
}
|
|
108
|
+
})
|
|
109
|
+
const uniqueValues = new Set(urls)
|
|
110
|
+
// 如果预览的图片地址相同,则直接预览,预览索引设置无效
|
|
111
|
+
if (uniqueValues.size === 1) {
|
|
112
|
+
uni.previewImage({
|
|
113
|
+
urls
|
|
114
|
+
})
|
|
115
|
+
} else {
|
|
116
|
+
uni.previewImage({
|
|
117
|
+
current: index,
|
|
118
|
+
urls
|
|
119
|
+
})
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
</script>
|
|
123
|
+
<template>
|
|
124
|
+
<view class="lx_upload_box flex-center-start wrap">
|
|
125
|
+
<view v-if="limit === 1 && modelValue" class="upload_item_box" :style="{
|
|
126
|
+
width: boxWidth,
|
|
127
|
+
height: boxWidth,
|
|
128
|
+
borderRadius: radius
|
|
129
|
+
}">
|
|
130
|
+
<image :src="baseUrl + modelValue" mode="aspectFill" @click="previewImage([modelValue as string])"></image>
|
|
131
|
+
<view class="upload_close_icon flex" v-if="!readOnly" @click="uploadDel()">
|
|
132
|
+
<image :src="delIcon" mode="aspectFit" />
|
|
133
|
+
</view>
|
|
134
|
+
</view>
|
|
135
|
+
<view v-else class="upload_item_box" :style="{
|
|
136
|
+
width: boxWidth,
|
|
137
|
+
height: boxWidth,
|
|
138
|
+
borderRadius: radius,
|
|
139
|
+
marginRight: (index + 1) % props.rowNum === 0 ? 0 : spacing + 'px',
|
|
140
|
+
marginBottom: spacing + 'px'
|
|
141
|
+
}" v-for="(item, index) in modelValue" :key="index">
|
|
142
|
+
<image :src="baseUrl + item" mode="aspectFill" @click="previewImage(modelValue as string[], index)"></image>
|
|
143
|
+
<view class="upload_close_icon flex" v-if="!readOnly" @click="uploadDel(index)">
|
|
144
|
+
<image :src="delIcon" mode="aspectFit" />
|
|
145
|
+
</view>
|
|
146
|
+
</view>
|
|
147
|
+
|
|
148
|
+
<view v-if="modelValue.length < limit && !readOnly" class="upload_add_box flex" @click="uploadAdd"
|
|
149
|
+
:style="{ width: boxWidth, height: boxWidth, borderRadius: radius, marginBottom: spacing + 'px' }">
|
|
150
|
+
<view class="upload_add">
|
|
151
|
+
<image :src="addIcon" mode="aspectFit"></image>
|
|
152
|
+
</view>
|
|
153
|
+
</view>
|
|
154
|
+
</view>
|
|
155
|
+
</template>
|
|
156
|
+
<style lang="scss" scoped>
|
|
157
|
+
.lx_upload_box {
|
|
158
|
+
.upload_add_box {
|
|
159
|
+
background: rgba(240, 240, 240, 1);
|
|
160
|
+
|
|
161
|
+
.upload_add {
|
|
162
|
+
width: 36rpx;
|
|
163
|
+
height: 36rpx;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// 上传的图片
|
|
168
|
+
.upload_item_box {
|
|
169
|
+
overflow: hidden;
|
|
170
|
+
box-sizing: border-box;
|
|
171
|
+
position: relative;
|
|
172
|
+
|
|
173
|
+
.upload_close_icon {
|
|
174
|
+
background-color: rgba($color: #000000, $alpha: 0.3);
|
|
175
|
+
box-shadow: 0 0 8rpx 0 rgba(0, 0, 0, 0.5);
|
|
176
|
+
border-radius: 50%;
|
|
177
|
+
width: 28rpx;
|
|
178
|
+
height: 28rpx;
|
|
179
|
+
position: absolute;
|
|
180
|
+
top: 8rpx;
|
|
181
|
+
right: 8rpx;
|
|
182
|
+
z-index: 5;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
</style>
|
package/index.ts
CHANGED
|
@@ -1,47 +1,47 @@
|
|
|
1
|
-
import {
|
|
2
|
-
goToPage,
|
|
3
|
-
formatTime,
|
|
4
|
-
debounce,
|
|
5
|
-
throttle,
|
|
6
|
-
saveImgData,
|
|
7
|
-
setClipboardData
|
|
8
|
-
} from './libs/util'
|
|
9
|
-
import config from './libs/config'
|
|
10
|
-
// import lxHeader from './components/lx-header/lx-header.vue'
|
|
11
|
-
// import lxHello from './components/lx-hello/lx-hello.vue'
|
|
12
|
-
// import lxImage from './components/lx-image/lx-image.vue'
|
|
13
|
-
// import lxList from './components/lx-list/lx-list.vue'
|
|
14
|
-
// import lxListState from './components/lx-list-state/lx-list-state.vue'
|
|
15
|
-
// import lxOperateBottom from './components/lx-operate-bottom/lx-operate-bottom.vue'
|
|
16
|
-
// import lxSubmitBtn from './components/lx-submit-btn/lx-submit-btn.vue'
|
|
17
|
-
// import lxUpload from './components/lx-upload/lx-upload.vue'
|
|
18
|
-
// export { lxHello, lxImage, lxHeader, lxList, lxListState, lxOperateBottom, lxSubmitBtn, lxUpload }
|
|
19
|
-
// const coms = [lxHello, lxImage, lxHeader, lxList, lxListState, lxOperateBottom, lxSubmitBtn, lxUpload]
|
|
20
|
-
// export default {
|
|
21
|
-
// install(app: App) {
|
|
22
|
-
// coms.forEach((commponent) => {
|
|
23
|
-
// // 将lxHello开头的组件都变为lx-hello将lx后边的组件名都转为小写
|
|
24
|
-
// commponent.name = commponent.name.replace(/^lx/, 'lx-').toLowerCase()
|
|
25
|
-
// app.component(commponent.name as string, commponent)
|
|
26
|
-
// })
|
|
27
|
-
// }
|
|
28
|
-
// }
|
|
29
|
-
|
|
30
|
-
const $util = {
|
|
31
|
-
goToPage,
|
|
32
|
-
formatTime,
|
|
33
|
-
debounce,
|
|
34
|
-
throttle,
|
|
35
|
-
saveImgData,
|
|
36
|
-
setClipboardData,
|
|
37
|
-
config
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
uni.goToPage = goToPage
|
|
41
|
-
uni.$util = $util
|
|
42
|
-
|
|
43
|
-
const install = (Vue: any) => {
|
|
44
|
-
Vue.config.globalProperties.$util = $util
|
|
45
|
-
Vue.config.globalProperties.goToPage = goToPage
|
|
46
|
-
}
|
|
1
|
+
import {
|
|
2
|
+
goToPage,
|
|
3
|
+
formatTime,
|
|
4
|
+
debounce,
|
|
5
|
+
throttle,
|
|
6
|
+
saveImgData,
|
|
7
|
+
setClipboardData
|
|
8
|
+
} from './libs/util'
|
|
9
|
+
import config from './libs/config'
|
|
10
|
+
// import lxHeader from './components/lx-header/lx-header.vue'
|
|
11
|
+
// import lxHello from './components/lx-hello/lx-hello.vue'
|
|
12
|
+
// import lxImage from './components/lx-image/lx-image.vue'
|
|
13
|
+
// import lxList from './components/lx-list/lx-list.vue'
|
|
14
|
+
// import lxListState from './components/lx-list-state/lx-list-state.vue'
|
|
15
|
+
// import lxOperateBottom from './components/lx-operate-bottom/lx-operate-bottom.vue'
|
|
16
|
+
// import lxSubmitBtn from './components/lx-submit-btn/lx-submit-btn.vue'
|
|
17
|
+
// import lxUpload from './components/lx-upload/lx-upload.vue'
|
|
18
|
+
// export { lxHello, lxImage, lxHeader, lxList, lxListState, lxOperateBottom, lxSubmitBtn, lxUpload }
|
|
19
|
+
// const coms = [lxHello, lxImage, lxHeader, lxList, lxListState, lxOperateBottom, lxSubmitBtn, lxUpload]
|
|
20
|
+
// export default {
|
|
21
|
+
// install(app: App) {
|
|
22
|
+
// coms.forEach((commponent) => {
|
|
23
|
+
// // 将lxHello开头的组件都变为lx-hello将lx后边的组件名都转为小写
|
|
24
|
+
// commponent.name = commponent.name.replace(/^lx/, 'lx-').toLowerCase()
|
|
25
|
+
// app.component(commponent.name as string, commponent)
|
|
26
|
+
// })
|
|
27
|
+
// }
|
|
28
|
+
// }
|
|
29
|
+
|
|
30
|
+
const $util = {
|
|
31
|
+
goToPage,
|
|
32
|
+
formatTime,
|
|
33
|
+
debounce,
|
|
34
|
+
throttle,
|
|
35
|
+
saveImgData,
|
|
36
|
+
setClipboardData,
|
|
37
|
+
config
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
uni.goToPage = goToPage
|
|
41
|
+
uni.$util = $util
|
|
42
|
+
|
|
43
|
+
const install = (Vue: any) => {
|
|
44
|
+
Vue.config.globalProperties.$util = $util
|
|
45
|
+
Vue.config.globalProperties.goToPage = goToPage
|
|
46
|
+
}
|
|
47
47
|
export default { install }
|