Skip to content
This repository was archived by the owner on Oct 27, 2021. It is now read-only.

Commit 9c587db

Browse files
author
pengshanglong
committed
feat: add ImagePicker
1 parent 7daa3f2 commit 9c587db

File tree

4 files changed

+213
-0
lines changed

4 files changed

+213
-0
lines changed

src/components/image-picker/index.tsx

+191
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
import Vue, { VNode } from 'vue'
2+
import classNames from 'classnames'
3+
import { uuid } from '../../utils/common'
4+
import Taro from '@tarojs/taro'
5+
6+
interface MatrixFile extends Partial<File> {
7+
type: 'blank' | 'btn';
8+
uuid: string;
9+
url?: string;
10+
}
11+
12+
// 生成 jsx 二维矩阵
13+
const generateMatrix = (files: MatrixFile[], col: number, showAddBtn: boolean) => {
14+
const matrix: Array<MatrixFile>[] = []
15+
const length = showAddBtn ? files.length + 1 : files.length
16+
const row = Math.ceil(length / col)
17+
for (let i = 0; i < row; i++) {
18+
if (i === row - 1) {
19+
// 最后一行数据加上添加按钮
20+
const lastArr = files.slice(i * col)
21+
if (lastArr.length < col) {
22+
if (showAddBtn) {
23+
lastArr.push({ type: 'btn', uuid: uuid() })
24+
}
25+
// 填补剩下的空列
26+
for (let j = lastArr.length; j < col; j++) {
27+
lastArr.push({ type: 'blank', uuid: uuid() })
28+
}
29+
}
30+
matrix.push(lastArr)
31+
} else {
32+
matrix.push(files.slice(i * col, (i + 1) * col))
33+
}
34+
}
35+
return matrix
36+
}
37+
38+
const ENV = Taro.getEnv()
39+
40+
const AtImagePicker = Vue.extend({
41+
name: 'AtImagePicker',
42+
props: {
43+
customStyle: {
44+
type: [Object, String],
45+
default: () => {},
46+
},
47+
className: {
48+
type: [Object, String],
49+
default: () => {},
50+
},
51+
files: {
52+
type: Array,
53+
default: () => [],
54+
},
55+
mode: {
56+
type: String,
57+
default: 'aspectFill',
58+
validator: (val) => {
59+
const modes = [
60+
'scaleToFill',
61+
'aspectFit',
62+
'aspectFill',
63+
'widthFix',
64+
'top',
65+
'bottom',
66+
'center',
67+
'left',
68+
'right',
69+
'top left',
70+
'top right',
71+
'bottom left',
72+
'bottom right',
73+
]
74+
return modes.includes(val)
75+
},
76+
},
77+
showAddBtn: {
78+
type: Boolean,
79+
default: false,
80+
},
81+
multiple: {
82+
type: Boolean,
83+
default: false,
84+
},
85+
length: {
86+
type: Number,
87+
default: 4,
88+
},
89+
onChange: {
90+
type: Function,
91+
default: () => () => {},
92+
},
93+
onImageClick: {
94+
type: Function,
95+
default: () => () => {},
96+
},
97+
onFail: {
98+
type: Function,
99+
default: () => () => {},
100+
},
101+
},
102+
methods: {
103+
chooseFile(): void {
104+
const { files = [], multiple, count, sizeType, sourceType } = this
105+
const filePathName = ENV === Taro.ENV_TYPE.ALIPAY ? 'apFilePaths' : 'tempFiles'
106+
// const count = multiple ? 99 : 1
107+
const params: any = {}
108+
if (multiple) {
109+
params.count = 99
110+
}
111+
if (count) {
112+
params.count = count
113+
}
114+
if (sizeType) {
115+
params.sizeType = sizeType
116+
}
117+
if (sourceType) {
118+
params.sourceType = sourceType
119+
}
120+
Taro.chooseImage(params)
121+
.then((res) => {
122+
const targetFiles = res.tempFilePaths.map((path, i) => ({
123+
url: path,
124+
file: res[filePathName][i],
125+
}))
126+
const newFiles = files.concat(targetFiles)
127+
this.onChange(newFiles, 'add')
128+
})
129+
.catch(this.onFail)
130+
},
131+
132+
handleImageClick(idx: number): void {
133+
this.onImageClick && this.onImageClick(idx, this.files[idx])
134+
},
135+
136+
handleRemoveImg(idx: number): void {
137+
const { files = [] } = this
138+
if (ENV === Taro.ENV_TYPE.WEB) {
139+
window.URL.revokeObjectURL(files[idx].url)
140+
}
141+
const newFiles = files.filter((_, i) => i !== idx)
142+
this.onChange(newFiles, 'remove', idx)
143+
},
144+
},
145+
render(): VNode {
146+
const { className, customStyle, files, mode, length = 4, showAddBtn = true } = this
147+
const rowLength = length <= 0 ? 1 : length
148+
// 行数
149+
const matrix = generateMatrix(files, rowLength, showAddBtn)
150+
const rootCls = classNames('at-image-picker', className)
151+
152+
return (
153+
<view class={rootCls} style={customStyle}>
154+
{matrix.map((row, i) => (
155+
<view class="at-image-picker__flex-box" key={i + 1}>
156+
{row.map((item, j) =>
157+
item.url ? (
158+
<view class="at-image-picker__flex-item" key={i * length + j}>
159+
<view class="at-image-picker__item">
160+
<view
161+
class="at-image-picker__remove-btn"
162+
onTap={this.handleRemoveImg.bind(this, i * length + j)}></view>
163+
<image
164+
class="at-image-picker__preview-img"
165+
mode={mode}
166+
src={item.url}
167+
onTap={this.handleImageClick.bind(this, i * length + j)}
168+
/>
169+
</view>
170+
</view>
171+
) : (
172+
<view class="at-image-picker__flex-item" key={i * length + j}>
173+
{item.type === 'btn' && (
174+
<view
175+
class="at-image-picker__item at-image-picker__choose-btn"
176+
onTap={this.chooseFile}>
177+
<view class="add-bar"></view>
178+
<view class="add-bar"></view>
179+
</view>
180+
)}
181+
</view>
182+
)
183+
)}
184+
</view>
185+
))}
186+
</view>
187+
)
188+
},
189+
})
190+
191+
export default AtImagePicker

src/components/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import AtCheckbox from './checkbox/index'
2121
import AtRate from './rate/index'
2222
import AtTextarea from './textarea/index'
2323
import AtSearchBar from './search-bar/index'
24+
import AtImagePicker from './image-picker/index'
2425

2526
export {
2627
Grid,
@@ -46,4 +47,5 @@ export {
4647
AtRate,
4748
AtTextarea,
4849
AtSearchBar,
50+
AtImagePicker,
4951
}

src/pages/index/index.vue

+4
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22
<view
33
class="index"
44
>
5+
<AtImagePicker
6+
:files="imageFiles"
7+
:on-change="imageChange"
8+
/>
59
<AtSearchBar
610
:value="searchVal"
711
:on-change="searchValChange"

src/pages/index/indexMixins.ts

+16
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ import {
4646
AtRate,
4747
AtTextarea,
4848
AtSearchBar,
49+
AtImagePicker,
4950
} from '../../components/index'
5051

5152
export default {
@@ -97,6 +98,7 @@ export default {
9798
AtRate,
9899
AtTextarea,
99100
AtSearchBar,
101+
AtImagePicker,
100102
},
101103
data() {
102104
return {
@@ -224,6 +226,17 @@ export default {
224226
rateVal: 2,
225227
textareaVal: '',
226228
searchVal: '',
229+
imageFiles: [
230+
{
231+
url: 'https://storage.360buyimg.com/mtd/home/111543234387022.jpg',
232+
},
233+
{
234+
url: 'https://storage.360buyimg.com/mtd/home/221543234387016.jpg',
235+
},
236+
{
237+
type: 'btn',
238+
},
239+
],
227240
}
228241
},
229242
methods: {
@@ -275,5 +288,8 @@ export default {
275288
searchValChange(val) {
276289
this.searchVal = val
277290
},
291+
imageChange(val) {
292+
this.imageFiles = val
293+
},
278294
},
279295
}

0 commit comments

Comments
 (0)