提交 d76f4ede 编写于 作者: L liuyijun17

feat: taro适配

上级 416856a3
......@@ -521,7 +521,7 @@
"desc": "用于将本地的图片或文件上传至服务器。",
"sort": 1,
"show": true,
"taro": false,
"taro": true,
"author": "swag~jun"
},
{
......@@ -614,7 +614,7 @@
"desc": "列表滚动到底部自动加载更多数据。",
"sort": 5,
"show": true,
"taro": false,
"taro": true,
"author": "swag~jun"
},
{
......@@ -685,7 +685,7 @@
"desc": "展示操作或任务的当前进度。",
"sort": 7,
"show": true,
"taro": false,
"taro": true,
"author": "swag~jun"
},
{
......@@ -962,7 +962,7 @@
"desc": "用于话语和词组的轮播展示,适用于视频中或其他类似需求中。",
"sort": 2,
"show": true,
"taro": false,
"taro": true,
"author": "swag~jun"
},
{
......
......@@ -8,7 +8,7 @@
box-sizing: border-box;
background-color: #f7f8fa;
.barrage-item {
width: 100px;
width: 120px;
// max-width: 150px;
display: block;
position: absolute;
......@@ -19,7 +19,12 @@
text-align: center;
white-space: pre;
transform: translateX(100%);
background: linear-gradient(to right, rgba(0, 0, 0, 0.15), rgba(0, 0, 0, 0));
background: linear-gradient(
to right,
rgba(0, 0, 0, 0.15),
rgba(0, 0, 0, 0)
);
box-sizing: border-box;
&.move {
will-change: transform;
animation-name: moving;
......
......@@ -45,7 +45,6 @@ const InternalBarrage: ForwardRefRenderFunction<
}
const barrageBody = useRef<HTMLDivElement>(null)
const barrageContainer = useRef<HTMLDivElement>(null)
const barrageCWidth = useRef(0)
const timer = useRef(0)
const index = useRef(0)
......@@ -60,10 +59,7 @@ const InternalBarrage: ForwardRefRenderFunction<
}))
useEffect(() => {
if (barrageBody.current) {
barrageCWidth.current = barrageBody.current.offsetWidth
run()
}
return () => {
clearInterval(timer.current)
}
......@@ -84,17 +80,15 @@ const InternalBarrage: ForwardRefRenderFunction<
el.classList.add('barrage-item')
;(barrageContainer.current as HTMLDivElement).appendChild(el)
const query = Taro.createSelectorQuery()
Taro.createSelectorQuery()
.select('.barrage-item')
.boundingClientRect((res) => {
const width = res?.width
console.log('res', res)
const height = res?.height
el.classList.add('move')
el.style.animationDuration = `${speeds}ms`
el.style.top = `${(_index % rows) * (height + top) + 20}px`
// el.style.width = `${width}px`
el.style.setProperty('--move-distance', `-${barrageCWidth.current}px`)
el.dataset.index = `${_index}`
el.addEventListener('animationend', () => {
;(barrageContainer.current as HTMLDivElement).removeChild(el)
......
......@@ -88,7 +88,7 @@ const InternalBarrage: ForwardRefRenderFunction<
el.classList.add('move')
el.style.animationDuration = `${speeds}ms`
el.style.top = `${(_index % rows) * (height + top) + 20}px`
el.style.width = `${width + 20}px`
el.style.width = `${width}px`
el.style.setProperty('--move-distance', `-${barrageCWidth.current}px`)
el.dataset.index = `${_index}`
el.addEventListener('animationend', () => {
......
.demo-barrage {
min-height: 100vh;
}
.barrage-demo-wrap,
.barrage-demo {
padding: 20px 0;
......
......@@ -76,7 +76,7 @@ const BarrageDemo = () => {
return (
<>
<div className="demo">
<div className="demo demo-barrage">
<h2>{translated['84aa6bce']}</h2>
<Cell className="barrage-demo-wrap">
<Barrage
......
......@@ -65,7 +65,7 @@ const BarrageDemo = () => {
translated['4d14b3e0'],
translated['448f995e'],
]
console.log(11, translated)
const barrageRef = useRef<barrageRefState>(null)
const addBarrage = () => {
const n = Math.random()
......
.demo-circleprogress {
min-height: 100vh;
}
.nut-circleprogress {
position: relative;
......
import React, { FunctionComponent } from 'react'
import React, { useState, useEffect, FunctionComponent } from 'react'
import classNames from 'classnames'
import { isObject } from '@/utils'
import bem from '@/utils/bem'
......@@ -20,8 +20,8 @@ const defaultProps = {
strokeWidth: 5,
radius: 50,
strokeLinecap: 'round',
circleColor: '',
pathColor: '',
circleColor: '#fa2c19',
pathColor: '#e5e9f2',
clockwise: true,
} as CircleProgressProps
......@@ -44,9 +44,12 @@ export const CircleProgress: FunctionComponent<
...defaultProps,
...props,
}
const [oldValue, setOldValue] = useState(progress)
const b = bem('circleprogress')
const classes = classNames(className, b(''))
const refRandomId = Math.random().toString(36).slice(-8)
let lastTime = 0
const styles: React.CSSProperties = {
height: `${Number(radius) * 2}px`,
......@@ -54,31 +57,45 @@ export const CircleProgress: FunctionComponent<
...style,
}
const pathStyle = {
stroke: pathColor,
useEffect(() => {
let rafId: number | undefined
const startTime = Date.now()
const startRate = Number(oldValue) // 30
const endRate = Number(progress) // 40
const duration = Math.abs(((startRate - endRate) * 1000) / +100) // 100
const animate = () => {
const now = Date.now()
const progress = Math.min((now - startTime) / duration, 1)
const rate = progress * (endRate - startRate) + startRate
setOldValue(Math.min(Math.max(+rate, 0), 100))
if (endRate > startRate ? rate < endRate : rate > endRate) {
rafId = requestAnimationFrame(animate)
}
const hoverStyle = () => {
const perimeter = 283
const offset = (perimeter * Number(progress)) / 100
return {
stroke: isObject(circleColor) ? `url(#${refRandomId})` : circleColor,
strokeDasharray: `${offset}px ${perimeter}px`,
}
if (rafId) {
cancelAnimationFrame(rafId)
}
rafId = requestAnimationFrame(animate)
}, [progress])
const path = () => {
const isWise = clockwise ? 1 : 0
return `M 50 50 m -45 0 a 45 45 0 1 ${isWise} 90 0 a 45 45 0 1 ${isWise} -90 0`
const requestAnimationFrame = function (callback: Function) {
var currTime = new Date().getTime()
var timeToCall = Math.max(0, 16.7 - (currTime - lastTime))
lastTime = currTime + timeToCall
var id = setTimeout(function () {
callback()
}, timeToCall)
lastTime = currTime + timeToCall
return id
}
const hoverColor = () => {
return isObject(circleColor) ? `url(#${refRandomId})` : circleColor
const cancelAnimationFrame = function (id: any) {
clearTimeout(id)
}
const stop = () => {
if (!isObject(circleColor)) {
return
return []
}
const color = circleColor as IColor
const colorArr = Object.keys(color).sort(
......@@ -97,36 +114,50 @@ export const CircleProgress: FunctionComponent<
return stopArr
}
const transColor = (color: string | undefined) => {
return color && color.replace('#', '%23')
}
const format = (progress: string | number) =>
Math.min(Math.max(+progress, 0), 100)
const circleStyle = () => {
let stopArr: Array<object> = stop()
let stopDom: string[] = []
if (stopArr) {
stopArr.map((item: { key?: string; value?: string }) => {
let obj = ''
obj = `%3Cstop offset='${item.key}' stop-color='${transColor(
item.value
)}'/%3E`
stopDom.push(obj)
})
}
let perimeter = 283
let progress = +oldValue
let offset =
(perimeter * Number(format(parseFloat(progress.toFixed(1))))) / 100
const isWise = props.clockwise ? 1 : 0
const color = isObject(circleColor)
? `url(%23${refRandomId})`
: transColor(circleColor)
let d = `M 50 50 m 0 -45 a 45 45 0 1 ${isWise} 0 90 a 45 45 0 1, ${isWise} 0 -90`
const pa = `%3Cdefs%3E%3ClinearGradient id='${refRandomId}' x1='100%25' y1='0%25' x2='0%25' y2='0%25'%3E${stopDom}%3C/linearGradient%3E%3C/defs%3E`
const path = `%3Cpath d='${d}' stroke-width='${strokeWidth}' stroke='${transColor(
props.pathColor
)}' fill='none'/%3E`
const path1 = `%3Cpath d='${d}' stroke-width='${strokeWidth}' stroke-dasharray='${offset},${perimeter}' stroke-linecap='round' stroke='${transColor(
color
)}' fill='none'/%3E`
return {
background: `url("data:image/svg+xml,%3Csvg viewBox='0 0 100 100' xmlns='http://www.w3.org/2000/svg'%3E${pa}${path}${path1}%3C/svg%3E")`,
width: '100%',
height: '100%',
}
}
return (
<div className={classes} style={styles} {...restProps}>
<svg viewBox="0 0 100 100">
<defs>
<linearGradient id={refRandomId} x1="100%" y1="0%" x2="0%" y2="0%">
{stop()?.map((item: any, index) => {
return (
<stop key={index} offset={item.key} stopColor={item.value} />
)
})}
</linearGradient>
</defs>
<path
className="nut-circleprogress-path"
style={pathStyle}
d={path()}
fill="none"
strokeWidth={strokeWidth}
/>
<path
className="nut-circleprogress-hover"
style={hoverStyle()}
d={path()}
fill="none"
stroke={hoverColor()}
strokeLinecap={strokeLinecap}
transform="rotate(90,50,50)"
strokeWidth={strokeWidth}
/>
</svg>
<div style={circleStyle()}></div>
<div className="nut-circleprogress-text">
{children || <div>{progress}%</div>}
</div>
......
......@@ -74,7 +74,7 @@ const CircleProgressDemo = () => {
return (
<>
<div className="demo">
<div className="demo demo-circleprogress">
<h2>{translated['84aa6bce']}</h2>
<div className="demo__piece">
<CircleProgress progress={20} />
......
......@@ -132,29 +132,6 @@ const InfiniteloadingDemo = () => {
</ul>
</Cell>
<h2>{translated.eb4236fe}</h2>
<Cell>
<ul className="infiniteUl" id="refreshScroll">
<Infiniteloading
pullIcon="JD"
containerId="refreshScroll"
useWindow={false}
isOpenRefresh
hasMore={refreshHasMore}
loadMore={refreshLoadMore}
refresh={refresh}
>
{refreshList.map((item, index) => {
return (
<li className="infiniteLi" key={index}>
{item}
</li>
)
})}
</Infiniteloading>
</ul>
</Cell>
<h2>{translated['9ed40460']}</h2>
<Cell>
<ul className="infiniteUl" id="customScroll">
......
# InfiniteLoading 滚动加载
### 介绍
列表滚动到底部自动加载更多数据。
#### 直接使用 Taro 现有 ScrollView 组件开发 [参考文档](https://docs.taro.zone/docs/components/viewContainer/scroll-view)
\ No newline at end of file
......@@ -2,6 +2,8 @@ import React, { useState, useEffect, useRef, FunctionComponent } from 'react'
import classNames from 'classnames'
import bem from '@/utils/bem'
import Icon from '@/packages/icon/index.taro'
import { ScrollView } from '@tarojs/components'
import Taro from '@tarojs/taro'
import { useConfig } from '@/packages/configprovider/configprovider.taro'
import { IComponent, ComponentDefaults } from '@/utils/typings'
......@@ -9,6 +11,7 @@ import { IComponent, ComponentDefaults } from '@/utils/typings'
export interface InfiniteloadingProps extends IComponent {
hasMore: boolean
threshold: number
upperThreshold: number
containerId: string
useWindow: boolean
useCapture: boolean
......@@ -31,6 +34,7 @@ const defaultProps = {
...ComponentDefaults,
hasMore: true,
threshold: 200,
upperThreshold: 40,
containerId: '',
useWindow: true,
useCapture: false,
......@@ -52,6 +56,7 @@ export const Infiniteloading: FunctionComponent<
children,
hasMore,
threshold,
upperThreshold,
containerId,
useWindow,
useCapture,
......@@ -65,7 +70,6 @@ export const Infiniteloading: FunctionComponent<
refresh,
loadMore,
scrollChange,
...restProps
} = {
...defaultProps,
...props,
......@@ -74,8 +78,10 @@ export const Infiniteloading: FunctionComponent<
const scroller = useRef<HTMLDivElement>(null)
const refreshTop = useRef<HTMLDivElement>(null)
const scrollEl = useRef<Window | HTMLElement | (Node & ParentNode)>(window)
const scrollHeight = useRef(0)
const scrollTop = useRef(0)
const direction = useRef('down')
const isTouching = useRef(false)
const beforeScrollTop = useRef(0)
const refreshMaxH = useRef(0)
const y = useRef(0)
const distance = useRef(0)
......@@ -84,27 +90,21 @@ export const Infiniteloading: FunctionComponent<
const classes = classNames(className, b())
useEffect(() => {
const parentElement = getParentElement(
scroller.current as HTMLDivElement
) as Node & ParentNode
scrollEl.current = useWindow ? window : parentElement
scrollEl.current.addEventListener('scroll', handleScroll, useCapture)
return () => {
scrollEl.current.removeEventListener('scroll', handleScroll, useCapture)
}
refreshMaxH.current = upperThreshold
setTimeout(() => {
getScrollHeight()
}, 200)
}, [hasMore, isInfiniting])
useEffect(() => {
const element = scroller.current as HTMLDivElement
element.addEventListener('touchmove', touchMove, { passive: false })
return () => {
element.removeEventListener('touchmove', touchMove, {
passive: false,
} as EventListenerOptions)
/** 获取需要滚动的距离 */
const getScrollHeight = () => {
const parentElement = getParentElement('scroller')
parentElement
.boundingClientRect((rect) => {
scrollHeight.current = rect.height
})
.exec()
}
}, [])
const getStyle = () => {
return {
......@@ -115,20 +115,10 @@ export const Infiniteloading: FunctionComponent<
}
}
const getParentElement = (el: HTMLElement) => {
return containerId
? document.querySelector(`#${containerId}`)
: el && el.parentNode
}
const handleScroll = () => {
requestAniFrame()(() => {
if (!isScrollAtBottom() || !hasMore || isInfiniting) {
return false
}
setIsInfiniting(true)
loadMore && loadMore(infiniteDone)
})
const getParentElement = (el: string) => {
return Taro.createSelectorQuery().select(
!!containerId ? `#${containerId} #${el}` : `#${el}`
)
}
const infiniteDone = () => {
......@@ -143,112 +133,43 @@ export const Infiniteloading: FunctionComponent<
isTouching.current = false
}
const touchStart = (event: React.TouchEvent<HTMLDivElement>) => {
if (beforeScrollTop.current === 0 && !isTouching.current && isOpenRefresh) {
y.current = event.touches[0].pageY
isTouching.current = true
const childHeight = (
(refreshTop.current as HTMLDivElement).firstElementChild as HTMLElement
).offsetHeight
refreshMaxH.current = Math.floor(childHeight * 1 + 10)
}
}
const touchMove = (event: any) => {
distance.current = event.touches[0].pageY - y.current
if (distance.current > 0 && isTouching.current) {
event.preventDefault()
if (distance.current >= refreshMaxH.current) {
distance.current = refreshMaxH.current
;(
refreshTop.current as HTMLDivElement
).style.height = `${distance.current}px`
} else {
;(
refreshTop.current as HTMLDivElement
).style.height = `${distance.current}px`
}
} else {
distance.current = 0
;(
refreshTop.current as HTMLDivElement
).style.height = `${distance.current}px`
isTouching.current = false
const scrollAction = (e: any) => {
if (e.detail.scrollTop <= 0) {
// 滚动到最顶部
e.detail.scrollTop = 0
} else if (e.detail.scrollTop >= scrollHeight.current) {
// 滚动到最底部
e.detail.scrollTop = scrollHeight.current
}
}
const touchEnd = () => {
if (distance.current < refreshMaxH.current) {
distance.current = 0
;(
refreshTop.current as HTMLDivElement
).style.height = `${distance.current}px`
if (
e.detail.scrollTop > scrollTop.current ||
e.detail.scrollTop >= scrollHeight.current
) {
direction.current = 'down'
} else {
refresh && refresh(refreshDone)
}
}
const requestAniFrame = () => {
return (
window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
function (callback) {
window.setTimeout(callback, 1000 / 60)
}
)
direction.current = 'up'
}
const getWindowScrollTop = () => {
return window.pageYOffset !== undefined
? window.pageYOffset
: (document.documentElement || document.body.parentNode || document.body)
.scrollTop
scrollTop.current = e.detail.scrollTop
scrollChange && scrollChange(e.detail.scrollTop)
}
const calculateTopPosition = (el: HTMLElement): number => {
return !el
? 0
: el.offsetTop + calculateTopPosition(el.offsetParent as HTMLElement)
}
const isScrollAtBottom = () => {
let offsetDistance = 0
let resScrollTop = 0
let direction = 'down'
const windowScrollTop = getWindowScrollTop()
if (useWindow) {
if (scroller.current) {
offsetDistance =
calculateTopPosition(scroller.current) +
scroller.current.offsetHeight -
windowScrollTop -
window.innerHeight
}
resScrollTop = windowScrollTop
} else {
const { scrollHeight, clientHeight, scrollTop } =
scrollEl.current as HTMLElement
offsetDistance = scrollHeight - clientHeight - scrollTop
resScrollTop = scrollTop
}
if (beforeScrollTop.current > resScrollTop) {
direction = 'up'
const lower = () => {
if (direction.current == 'up' || !hasMore || isInfiniting) {
return false
} else {
direction = 'down'
setIsInfiniting(true)
loadMore && loadMore(infiniteDone)
}
beforeScrollTop.current = resScrollTop
scrollChange && scrollChange(resScrollTop)
return offsetDistance <= threshold && direction == 'down'
}
return (
<div
<ScrollView
className={classes}
ref={scroller}
onTouchStart={touchStart}
onTouchMove={touchMove}
onTouchEnd={touchEnd}
{...restProps}
scrollY={true}
id="scroller"
style={{ height: '100%' }}
onScroll={scrollAction}
onScrollToLower={lower}
>
<div className="nut-infinite-top" ref={refreshTop} style={getStyle()}>
<div className="top-box">
......@@ -285,7 +206,7 @@ export const Infiniteloading: FunctionComponent<
)
)}
</div>
</div>
</ScrollView>
)
}
......
......@@ -156,6 +156,7 @@ export const Infiniteloading: FunctionComponent<
}
const touchMove = (event: any) => {
console.log('touchMove', event)
distance.current = event.touches[0].pageY - y.current
if (distance.current > 0 && isTouching.current) {
event.preventDefault()
......
.demo-overlay {
min-height: 100vh;
}
.wrapper {
display: flex;
height: 100%;
......
......@@ -60,7 +60,7 @@ const OverlayDemo = () => {
}
return (
<>
<div className="demo">
<div className="demo demo-overlay">
<h2>{translated['84aa6bce']}</h2>
<Cell>
<Button type="primary" onClick={handleToggleShow}>
......
......@@ -62,7 +62,7 @@ const OverlayDemo = () => {
}
return (
<>
<div className="demo">
<div className="demo demo-overlay">
<h2>{translated['84aa6bce']}</h2>
<Cell>
<Button type="primary" onClick={handleToggleShow}>
......
import React, { useRef } from 'react'
import { useTranslate } from '@/sites/assets/locale/taro'
import { Button, Uploader } from '@/packages/nutui.react.taro'
import Taro from '@tarojs/taro'
export type FileItemStatus =
| 'ready'
......@@ -183,7 +184,7 @@ const UploaderDemo = () => {
canvas.toBlob((blob) => resolve(blob), type, quality)
)
}
const onOversize = (files: File[]) => {
const onOversize = (files: Taro.chooseImage.ImageFile[]) => {
console.log(translated['25e04d44'], files)
}
const onStart = () => {
......
......@@ -3,6 +3,8 @@ export class UploadOptions {
name = 'file'
fileType? = 'image'
formData?: FormData
method = 'post'
......@@ -24,6 +26,8 @@ export class UploadOptions {
onSuccess?: Function
onFailure?: Function
beforeXhrUpload?: Function
}
export class Upload {
options: UploadOptions
......@@ -65,4 +69,51 @@ export class Upload {
console.warn('浏览器不支持 XMLHttpRequest')
}
}
uploadTaro(uploadFile: Function, env: string) {
const options = this.options
if (env === 'WEB') {
this.upload()
} else {
if (options.beforeXhrUpload) {
options.beforeXhrUpload(uploadFile, options)
} else {
const uploadTask = uploadFile({
url: options.url,
filePath: options.taroFilePath,
fileType: options.fileType,
header: {
'Content-Type': 'multipart/form-data',
...options.headers,
}, //
formData: options.formData,
name: options.name,
success(response: { errMsg: any; statusCode: number; data: string }) {
if (options.xhrState == response.statusCode) {
options.onSuccess?.(response, options)
} else {
options.onFailure?.(response, options)
}
},
fail(e: any) {
options.onFailure?.(e, options)
},
})
options.onStart?.(options)
uploadTask.progress(
(res: {
progress: any
totalBytesSent: any
totalBytesExpectedToSend: any
}) => {
options.onProgress?.(res, options)
// console.log('上传进度', res.progress);
// console.log('已经上传的数据长度', res.totalBytesSent);
// console.log('预期需要上传的数据总长度', res.totalBytesExpectedToSend);
}
)
// uploadTask.abort(); // 取消上传任务
}
}
}
}
......@@ -6,7 +6,9 @@ import React, {
useEffect,
} from 'react'
import classNames from 'classnames'
import Icon from '@/packages/icon'
import Icon from '@/packages/icon/index.taro'
import Button from '@/packages/button/index.taro'
import Taro from '@tarojs/taro'
import { Upload, UploadOptions } from './upload'
import bem from '@/utils/bem'
import { useConfig } from '@/packages/configprovider/configprovider.taro'
......@@ -20,11 +22,33 @@ export type FileItemStatus =
| 'error'
| 'removed'
/** 图片的尺寸 */
interface sizeType {
/** 原图 */
original: string
/** compressed */
compressed: string
}
/** 图片的来源 */
interface sourceType {
/** 从相册选图 */
album: string
/** 使用相机 */
camera: string
/** 使用前置摄像头(仅H5纯浏览器使用) */
user: string
/** 使用后置摄像头(仅H5纯浏览器) */
environment: string
}
import { IComponent, ComponentDefaults } from '@/utils/typings'
export interface UploaderProps extends IComponent {
url: string
maximum: string | number
sizeType: (keyof sizeType)[]
sourceType: (keyof sourceType)[]
maximize: number
defaultFileList: FileType<string>[]
listType: string
......@@ -41,7 +65,6 @@ export interface UploaderProps extends IComponent {
xhrState: number | string
headers: object
withCredentials: boolean
clearInput: boolean
isPreview: boolean
isDeletable: boolean
capture: boolean
......@@ -63,12 +86,12 @@ export interface UploaderProps extends IComponent {
option: UploadOptions
}) => void
update?: (fileList: any[]) => void
oversize?: (file: File[]) => void
change?: (param: {
fileList: any[]
event: React.ChangeEvent<HTMLInputElement>
}) => void
beforeUpload?: (file: File[]) => Promise<File[]>
oversize?: (file: Taro.chooseImage.ImageFile[]) => void
change?: (param: { fileList: any[] }) => void
beforeUpload?: (file: any[]) => Promise<any[]>
beforeXhrUpload?: (
file: Taro.chooseImage.ImageFile[]
) => Promise<Taro.chooseImage.ImageFile[]>
beforeDelete?: (file: FileItem, files: FileItem[]) => boolean
fileItemClick?: (file: FileItem) => void
}
......@@ -76,7 +99,9 @@ export interface UploaderProps extends IComponent {
const defaultProps = {
...ComponentDefaults,
url: '',
maximum: 1,
maximum: 9,
sizeType: ['original', 'compressed'],
sourceType: ['album', 'camera'],
uploadIcon: 'photograph',
uploadIconSize: '',
listType: 'picture',
......@@ -93,7 +118,6 @@ const defaultProps = {
xhrState: 200,
timeout: 1000 * 30,
withCredentials: false,
clearInput: false,
isPreview: true,
isDeletable: true,
capture: false,
......@@ -115,7 +139,9 @@ export class FileItem {
type?: string
formData: FormData = new FormData()
path?: string
formData: any = {}
}
const InternalUploader: ForwardRefRenderFunction<
unknown,
......@@ -147,7 +173,8 @@ const InternalUploader: ForwardRefRenderFunction<
maximize,
className,
autoUpload,
clearInput,
sizeType,
sourceType,
start,
removeImage,
change,
......@@ -158,6 +185,7 @@ const InternalUploader: ForwardRefRenderFunction<
failure,
oversize,
beforeUpload,
beforeXhrUpload,
beforeDelete,
...restProps
} = { ...defaultProps, ...props }
......@@ -177,7 +205,7 @@ const InternalUploader: ForwardRefRenderFunction<
useImperativeHandle(ref, () => ({
submit: () => {
Promise.all(uploadQueue).then((res) => {
res.forEach((i) => i.upload())
res.forEach((i) => i.uploadTaro(Taro.uploadFile, Taro.getEnv()))
})
},
}))
......@@ -191,22 +219,37 @@ const InternalUploader: ForwardRefRenderFunction<
}
}
const clearInputValue = (el: HTMLInputElement) => {
el.value = ''
const chooseImage = () => {
if (disabled) {
return
}
Taro.chooseImage({
// 选择数量
count: (maximum as number) * 1 - fileList.length,
// 可以指定是原图还是压缩图,默认二者都有
sizeType: sizeType,
sourceType: sourceType,
success: onChange,
})
}
const executeUpload = (fileItem: FileItem, index: number) => {
console.log('executeUpload fileItem', fileItem, index)
const uploadOption = new UploadOptions()
uploadOption.name = name
uploadOption.url = url
for (const [key, value] of Object.entries(data)) {
fileItem.formData.append(key, value)
}
uploadOption.fileType = fileItem.type
uploadOption.formData = fileItem.formData
uploadOption.timeout = timeout * 1
uploadOption.method = method
uploadOption.xhrState = xhrState
uploadOption.headers = headers
uploadOption.withCredentials = withCredentials
uploadOption.taroFilePath = fileItem.path
uploadOption.beforeXhrUpload = beforeXhrUpload
console.log('uploadOption', uploadOption)
uploadOption.onStart = (option: UploadOptions) => {
clearUploadQueue(index)
setFileList((fileList: FileItem[]) => {
......@@ -220,6 +263,7 @@ const InternalUploader: ForwardRefRenderFunction<
})
start && start(option)
}
uploadOption.onProgress = (
e: ProgressEvent<XMLHttpRequestEventTarget>,
option: UploadOptions
......@@ -235,6 +279,7 @@ const InternalUploader: ForwardRefRenderFunction<
})
progress && progress({ e, option })
}
uploadOption.onSuccess = (
responseText: XMLHttpRequest['responseText'],
option: UploadOptions
......@@ -255,6 +300,7 @@ const InternalUploader: ForwardRefRenderFunction<
option,
})
}
uploadOption.onFailure = (
responseText: XMLHttpRequest['responseText'],
option: UploadOptions
......@@ -274,9 +320,10 @@ const InternalUploader: ForwardRefRenderFunction<
option,
})
}
const task = new Upload(uploadOption)
if (props.autoUpload) {
task.upload()
task.uploadTaro(Taro.uploadFile, Taro.getEnv())
} else {
uploadQueue.push(
new Promise((resolve, reject) => {
......@@ -287,38 +334,47 @@ const InternalUploader: ForwardRefRenderFunction<
}
}
const readFile = (files: File[]) => {
files.forEach((file: File, index: number) => {
const formData = new FormData()
formData.append(name, file)
const readFile = (files: Taro.chooseImage.ImageFile[]) => {
const imgReg = /\.(png|jpeg|jpg|webp|gif)$/i
files.forEach((file: Taro.chooseImage.ImageFile, index: number) => {
let fileType = file.type
const fileItem = new FileItem()
fileItem.name = file.name
if (!fileType && imgReg.test(file.path)) {
fileType = 'image'
}
fileItem.path = file.path
fileItem.name = file.path
fileItem.status = 'ready'
fileItem.type = file.type
fileItem.formData = formData
fileItem.uid = file.lastModified + fileItem.uid
fileItem.message = locale.uploader.readyUpload
executeUpload(fileItem, index)
fileItem.type = fileType
if (isPreview && file.type.includes('image')) {
const reader = new FileReader()
reader.onload = (event: ProgressEvent<FileReader>) => {
fileItem.url = (event.target as FileReader).result as string
fileList.push(fileItem)
setFileList([...fileList])
if (Taro.getEnv() == 'WEB') {
const formData = new FormData()
for (const [key, value] of Object.entries(data)) {
formData.append(key, value)
}
reader.readAsDataURL(file)
formData.append(name, file.originalFileObj as Blob)
fileItem.name = file.originalFileObj?.name
fileItem.type = file.originalFileObj?.type
fileItem.formData = formData
} else {
fileItem.formData = data as any
}
if (isPreview) {
fileItem.url = file.path
}
fileList.push(fileItem)
setFileList([...fileList])
}
executeUpload(fileItem, index)
})
}
const filterFiles = (files: File[]) => {
const filterFiles = (files: Taro.chooseImage.ImageFile[]) => {
const maximum = (props.maximum as number) * 1
const oversizes = new Array<File>()
const filterFile = files.filter((file: File) => {
const maximize = (props.maximize as number) * 1
const oversizes = new Array<Taro.chooseImage.ImageFile>()
const filterFile = files.filter((file: Taro.chooseImage.ImageFile) => {
if (file.size > maximize) {
oversizes.push(file)
return false
......@@ -329,14 +385,10 @@ const InternalUploader: ForwardRefRenderFunction<
oversize && oversize(files)
}
if (filterFile.length > maximum) {
filterFile.splice(maximum, filterFile.length - maximum)
}
if (fileList.length !== 0) {
const index = maximum - fileList.length
filterFile.splice(index, filterFile.length - index)
let currentFileLength = filterFile.length + fileList.length
if (currentFileLength > maximum) {
filterFile.splice(filterFile.length - (currentFileLength - maximum))
}
return filterFile
}
......@@ -351,30 +403,20 @@ const InternalUploader: ForwardRefRenderFunction<
}
}
const fileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
if (disabled) {
return
}
const $el = event.target
const { files } = $el
const onChange = (res: Taro.chooseImage.SuccessCallbackResult) => {
// 返回选定照片的本地文件路径列表,tempFilePath可以作为img标签的src属性显示图片
const { tempFiles } = res
if (beforeUpload) {
beforeUpload(new Array<File>().slice.call(files)).then(
(f: Array<File>) => {
const _files: File[] = filterFiles(new Array<File>().slice.call(f))
beforeUpload(tempFiles).then((f: Array<Taro.chooseImage.ImageFile>) => {
const _files: Taro.chooseImage.ImageFile[] = filterFiles(f)
readFile(_files)
}
)
})
} else {
const _files = filterFiles(new Array<File>().slice.call(files))
const _files: Taro.chooseImage.ImageFile[] = filterFiles(tempFiles)
readFile(_files)
}
props.change && props.change({ fileList, event })
if (clearInput) {
clearInputValue($el)
}
props.change && props.change({ fileList })
}
const handleItemClick = (file: FileItem) => {
......@@ -388,30 +430,7 @@ const InternalUploader: ForwardRefRenderFunction<
<>
{children}
{maximum > fileList.length && (
<>
{capture ? (
<input
className="nut-uploader__input"
type="file"
capture="user"
name={name}
accept={accept}
disabled={disabled}
multiple={multiple}
onChange={fileChange}
/>
) : (
<input
className="nut-uploader__input"
type="file"
name={name}
accept={accept}
disabled={disabled}
multiple={multiple}
onChange={fileChange}
/>
)}
</>
<Button className="nut-uploader__input" onClick={chooseImage} />
)}
</>
</div>
......@@ -454,7 +473,7 @@ const InternalUploader: ForwardRefRenderFunction<
color="rgba(0,0,0,0.6)"
className="close"
name="failure"
click={() => onDelete(item, index)}
onClick={() => onDelete(item, index)}
/>
)}
......@@ -518,7 +537,7 @@ const InternalUploader: ForwardRefRenderFunction<
color="#808080"
className="nut-uploader__preview-img__file__del"
name="del"
click={() => onDelete(item, index)}
onClick={() => onDelete(item, index)}
/>
{/* 缺少进度条组件,待更新 */}
</div>
......@@ -536,28 +555,7 @@ const InternalUploader: ForwardRefRenderFunction<
color="#808080"
name={uploadIcon}
/>
{capture ? (
<input
className="nut-uploader__input"
type="file"
capture="user"
name={name}
accept={accept}
disabled={disabled}
multiple={multiple}
onChange={fileChange}
/>
) : (
<input
className="nut-uploader__input"
type="file"
name={name}
accept={accept}
disabled={disabled}
multiple={multiple}
onChange={fileChange}
/>
)}
<Button className="nut-uploader__input" onClick={chooseImage} />
</div>
)}
</div>
......
# Video 视频播放器
### 介绍
原生video实现的视频播放器
#### 直接使用 Taro 现有 video 组件开发 [参考文档](https://taro-docs.jd.com/taro/docs/components/media/video)
\ No newline at end of file
......@@ -39,6 +39,7 @@ const subPackages = [
'pages/picker/index',
'pages/shortpassword/index',
'pages/textarea/index',
'pages/uploader/index',
'pages/searchbar/index',
'pages/numberkeyboard/index',
],
......@@ -47,6 +48,7 @@ const subPackages = [
root: 'feedback',
pages: [
'pages/actionsheet/index',
'pages/infiniteloading/index',
'pages/switch/index',
'pages/pulltorefresh/index',
],
......@@ -54,6 +56,7 @@ const subPackages = [
{
root: 'exhibition',
pages: [
'pages/circleprogress/index',
'pages/noticebar/index',
'pages/steps/index',
'pages/swiper/index',
......@@ -71,7 +74,11 @@ const subPackages = [
},
{
root: 'business',
pages: ['pages/card/index', 'pages/timeselect/index'],
pages: [
'pages/barrage/index',
'pages/card/index',
'pages/timeselect/index',
],
},
]
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册