AudioUploader.js 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. import Button from './Button'
  2. import Input from './Input'
  3. import Label from './Label'
  4. import React from 'react'
  5. import Select from './Select'
  6. import Tag from './Tag'
  7. import Upload from './Upload'
  8. const style = {
  9. paramsConfig: {
  10. paddingBottom: '10px',
  11. borderBottom: '1px solid rgb(217, 217, 217)',
  12. display: 'flex',
  13. flexWrap: 'wrap',
  14. },
  15. insertTitle: {
  16. fontSize: '14px',
  17. paddingRight: '10px',
  18. color: 'rgba(0, 0, 0, 0.65)',
  19. },
  20. sourceList: {
  21. margin: '10px 10px 10px 0',
  22. border: '1px dashed rgb(217, 217, 217)',
  23. borderRadius: '4px',
  24. },
  25. configTitle: {
  26. display: 'block',
  27. fontSize: '14px',
  28. margin: '10px 0',
  29. paddingRight: '10px',
  30. color: 'rgba(0, 0, 0, 0.65)',
  31. },
  32. warnInfo: {
  33. display: 'inline-block',
  34. width: '100%',
  35. margin: '5px',
  36. textAlign: 'center',
  37. fontSize: '12px',
  38. color: '#f04134',
  39. },
  40. }
  41. const linkRegx = /^https?:\/\/(([a-zA-Z0-9_-])+(\.)?)*(:\d+)?(\/((\.)?(\?)?=?&?[a-zA-Z0-9,_-](\?)?)*)*$/i
  42. let timeoutInstance = null
  43. class UploadModal extends React.PureComponent {
  44. state = {
  45. sources: [],
  46. currentSource: '',
  47. width: 400,
  48. height: 400,
  49. controls: 'true',
  50. autoplay: 'false',
  51. muted: 'false',
  52. loop: 'false',
  53. poster: '',
  54. name: '',
  55. author: '',
  56. errorMsg: '',
  57. errorMsgVisible: false,
  58. }
  59. updateCurrentSource = e => {
  60. this.setState({currentSource: e.target.value})
  61. }
  62. addSource = () => {
  63. let {sources, currentSource} = this.state
  64. let newsources = sources.concat([currentSource])
  65. if (currentSource === '') {
  66. this.showErrorMsg('链接不能为空')
  67. } else if (!linkRegx.test(currentSource)) {
  68. this.showErrorMsg('非法的链接')
  69. } else if (sources.indexOf(currentSource) !== -1) {
  70. this.showErrorMsg('链接已存在')
  71. } else {
  72. this.setState({
  73. sources: newsources,
  74. currentSource: '',
  75. }, () => {
  76. this.props.onChange && this.props.onChange(this.generateHtml())
  77. })
  78. }
  79. }
  80. removeSource = index => {
  81. let sourcesCopy = this.state.sources.concat([])
  82. sourcesCopy.splice(index, 1)
  83. this.setState({sources: sourcesCopy})
  84. }
  85. upload = e => {
  86. let {upload} = this.props
  87. if (!upload) return
  88. upload(e).then(url => {
  89. this.setState({currentSource: url})
  90. }).catch(e => {
  91. e.constructor === Error ? this.showErrorMsg(e.message) : this.showErrorMsg(e)
  92. })
  93. }
  94. showErrorMsg = msg => {
  95. this.setState({errorMsg: msg, errorMsgVisible: true})
  96. clearTimeout(timeoutInstance)
  97. timeoutInstance = setTimeout(() => {
  98. this.setState({errorMsg: '', errorMsgVisible: false})
  99. }, 4000)
  100. }
  101. getFileType = (fileUrl, mediaType) => {
  102. let type = fileUrl.match(/\.(\w+)$/, 'i')
  103. return type ? type[1].toLowerCase() : 'mp3'
  104. }
  105. generateHtml = () => {
  106. let {sources, controls, autoplay, loop, poster, name, author} = this.state
  107. let dataExtra = JSON.stringify({'poster': poster, 'name': name, 'author': author})
  108. let len = sources.length
  109. if (len > 0) {
  110. let html = ''
  111. let attr = ''
  112. attr += controls === 'false' ? '' : ' controls="true" '
  113. attr += autoplay === 'false' ? '' : ' autoplay="true" '
  114. attr += loop === 'false' ? '' : ' loop="true" '
  115. if (len === 1) {
  116. html = `<audio src="${sources[0]}" ${attr} data-extra='${dataExtra}'>你的浏览器不支持 audio 标签</audio>`
  117. } else {
  118. html = `<audio ${attr} data-extra='${dataExtra}'>`
  119. sources.forEach(source => {
  120. html += `<source src=${source} type="audio/${this.getFileType(source, 'audio')}">`
  121. })
  122. html += '你的浏览器不支持 audio 标签</audio>'
  123. }
  124. return html + '<p></p>'
  125. }
  126. }
  127. closeModal = () => {
  128. this.props.closeModal()
  129. }
  130. changeConfig = (e, type) => {
  131. let value = e.target.value
  132. let boolType = ['controls', 'autoplay', 'muted', 'loop']
  133. if (type === 'width' || type === 'height') {
  134. if (isNaN(parseInt(value))) {
  135. value = parseInt(value)
  136. }
  137. } else if (boolType.indexOf(type) !== -1) {
  138. value = !!value
  139. }
  140. this.setState({[type]: value}, () => {
  141. this.props.onChange && this.props.onChange(this.generateHtml())
  142. })
  143. }
  144. renderSourceList = () => {
  145. let {sources} = this.state
  146. if (sources.length > 0) {
  147. let list = sources.map((source, index) => {
  148. return <Tag value={source} key={source} index={index} onRemove={this.removeSource} />
  149. })
  150. return list
  151. } else {
  152. return <span style={style.warnInfo}>至少添加一个链接</span>
  153. }
  154. }
  155. renderAudioConfig = () => {
  156. let {controls, autoplay, loop, poster, name, author} = this.state
  157. return (
  158. <form style={style.paramsConfig}>
  159. <Label name='controls'>
  160. <Select defaultValue={controls} onChange={e => { this.changeConfig(e, 'controls') }}>
  161. <option value='true'>true</option>
  162. <option value='false'>false</option>
  163. </Select>
  164. </Label>
  165. <Label name='autoplay'>
  166. <Select defaultValue={autoplay} onChange={e => { this.changeConfig(e, 'autoplay') }}>
  167. <option value='true'>true</option>
  168. <option value='false'>false</option>
  169. </Select>
  170. </Label>
  171. <Label name='loop'>
  172. <Select defaultValue={loop} onChange={e => { this.changeConfig(e, 'loop') }}>
  173. <option value='true'>true</option>
  174. <option value='false'>false</option>
  175. </Select>
  176. </Label>
  177. <Label name='poster'>
  178. <Input type='text' defaultValue={poster} onChange={e => { this.changeConfig(e, 'poster') }} />
  179. </Label>
  180. <Label name='name'>
  181. <Input type='text' defaultValue={name} onChange={e => { this.changeConfig(e, 'name') }} />
  182. </Label>
  183. <Label name='author'>
  184. <Input type='text' defaultValue={author} onChange={e => { this.changeConfig(e, 'author') }} />
  185. </Label>
  186. </form>
  187. )
  188. }
  189. render() {
  190. let {currentSource, errorMsg, errorMsgVisible} = this.state
  191. let {progress} = this.props
  192. return (
  193. <div>
  194. <div>
  195. <span style={style.insertTitle}>插入链接</span>
  196. <Input style={{width: '300px'}} type='text' value={currentSource} onChange={this.updateCurrentSource} />
  197. <Button onClick={this.addSource}>添加</Button>
  198. <Upload onChange={this.upload} />
  199. </div>
  200. <div>
  201. <span style={{...style.warnInfo, display: progress && progress !== -1 ? 'block' : 'none'}}>
  202. {progress}%
  203. </span>
  204. <span style={{...style.warnInfo, display: errorMsgVisible ? 'block' : 'none'}}>{errorMsg}</span>
  205. </div>
  206. <div style={style.sourceList}>
  207. {this.renderSourceList()}
  208. </div>
  209. <span style={style.configTitle}>参数配置</span>
  210. {this.renderAudioConfig()}
  211. <div style={{textAlign: 'center', padding: '20px 10px 0 10px'}}>
  212. {
  213. <audio src={currentSource} controls='controls' style={{width: '400px'}}>
  214. 你的浏览器不支持 audio 标签
  215. </audio>
  216. }
  217. </div>
  218. </div>
  219. )
  220. }
  221. }
  222. export default UploadModal