materialSelectorForEditor.vue 57 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693
  1. <template>
  2. <div class="table-select">
  3. <span class="title">{{title}}</span>
  4. <div class="close-btn"><i class="iconfont icon-pop-ups_shut-down" @click="$emit('cancle')"></i></div>
  5. <div class="material-tab">
  6. <a v-if="selectableType.includes('image')" class="material-tab-item" @click.prevent="currentMaterialType = 'image'">
  7. <span class="text">图片</span>
  8. <div v-if="currentMaterialType === 'image'" class="bottom-line"></div>
  9. </a>
  10. <a v-if="selectableType.includes('pano')" class="material-tab-item" @click.prevent="currentMaterialType = 'pano'">
  11. <span class="text">全景图</span>
  12. <div v-if="currentMaterialType === 'pano'" class="bottom-line"></div>
  13. </a>
  14. <a v-if="selectableType.includes('audio')" class="material-tab-item" @click.prevent="currentMaterialType = 'audio'">
  15. <span class="text">音频</span>
  16. <div v-if="currentMaterialType === 'audio'" class="bottom-line"></div>
  17. </a>
  18. <a v-if="selectableType.includes('video')" class="material-tab-item" @click.prevent="currentMaterialType = 'video'">
  19. <span class="text">视频</span>
  20. <div v-if="currentMaterialType === 'video'" class="bottom-line"></div>
  21. </a>
  22. <a v-if="selectableType.includes('3D')" class="material-tab-item" @click.prevent="currentMaterialType = '3D'">
  23. <span class="text">三维场景</span>
  24. <div v-if="currentMaterialType === '3D'" class="bottom-line"></div>
  25. </a>
  26. </div>
  27. <div class="filter">
  28. <input type="text" placeholder="输入关键词" v-model="searchKey"/>
  29. <i v-if="!searchKey" class="iconfont icon-editor_search search-icon"/>
  30. <i v-if="searchKey" @click="searchKey=''" class="iconfont icontoast_red clear-icon"></i>
  31. </div>
  32. <div class="table table-image" v-show="currentMaterialType === 'image'">
  33. <div class="table-head-row">
  34. <span class="table-head">1</span>
  35. <span class="table-head" v-for="(item,i) in tableHeadersForImage" :key="i">{{item.name}}</span>
  36. </div>
  37. <div
  38. v-if="imageListRealLength !== 0 || hasMoreImageData"
  39. class="table-body"
  40. v-infinite-scroll="requestMoreImageData"
  41. :infinite-scroll-disabled="!hasMoreImageData || isRequestingMoreImageData"
  42. >
  43. <!-- vuex中的上传中数据 -->
  44. <div class="table-body-row" v-for="(item, i) in uploadStatusListImage" :key="item.uid">
  45. <!-- 如果已经上传成功 -->
  46. <template v-if="item.status === 'SUCCESS'">
  47. <span class="table-data">
  48. <div class="checkbox">
  49. <!-- 负责功能 -->
  50. <input
  51. type="checkbox"
  52. @change="e => selectItem(item.successInfo, e)"
  53. :checked="select.some(i => i[primaryKey] === item.successInfo[primaryKey])"
  54. >
  55. <!-- 负责外观 -->
  56. <span class="for-outer-circle"></span>
  57. <span class="for-inner-circle"></span>
  58. </div>
  59. </span>
  60. <span class="table-data" v-for="(tableItemStructure, idx) in tableHeadersForImage" :key="idx">
  61. <div v-if="tableItemStructure.type=='image'" class="list-img">
  62. <img :src="item.successInfo[tableItemStructure.key] + `?x-oss-process=image/resize,p_10&${Math.random()}`" alt="">
  63. </div>
  64. <span v-else class="ellipsis" v-title="tableItemStructure.key === 'name' ? item.successInfo[tableItemStructure.key] : ''">{{ item.successInfo[tableItemStructure.key] }}</span>
  65. </span>
  66. </template>
  67. <!-- 如果还在上传中 -->
  68. <template v-else-if="item.status = 'LOADING'">
  69. <span class="table-data">
  70. <div class="checkbox">
  71. <span class="for-outer-circle"></span>
  72. <span class="for-inner-circle disabled"></span>
  73. </div>
  74. </span>
  75. <span class="table-data" v-for="(tableItemStructure, idx) in tableHeadersForImage" :key="idx">
  76. <div v-if="tableItemStructure.type=='image'" class="list-img">
  77. <img src="@/assets/images/icons/upload-file-type-icon-image@2x.png" alt="">
  78. </div>
  79. <span v-if="tableItemStructure.key !== 'name' && tableItemStructure.key !== 'dpi'"></span>
  80. <span v-if="tableItemStructure.key === 'dpi'">上传素材 {{item.progress * 100}}%</span>
  81. <span v-if="tableItemStructure.key === 'name'" class="ellipsis" v-title="tableItemStructure.key === 'name' ? item.title : ''">{{ item.title }}</span>
  82. </span>
  83. </template>
  84. <!-- 如果上传失败了 -->
  85. <template v-else-if="item.status = 'FAIL'">
  86. <span class="table-data">
  87. <div class="checkbox">
  88. <span class="for-outer-circle"></span>
  89. <span class="for-inner-circle disabled"></span>
  90. </div>
  91. </span>
  92. <span class="table-data" v-for="(tableItemStructure, idx) in tableHeadersForImage" :key="idx">
  93. <div v-if="tableItemStructure.type=='image'" class="list-img">
  94. <img src="@/assets/images/icons/upload-file-type-icon-image@2x.png" alt="">
  95. </div>
  96. <span v-if="tableItemStructure.key !== 'name' && tableItemStructure.key !== 'dpi'"></span>
  97. <span v-if="tableItemStructure.key === 'dpi'">上传失败</span>
  98. <span v-if="tableItemStructure.key === 'name'" class="ellipsis" v-title="tableItemStructure.key === 'name' ? item.title : ''">{{ item.title }}</span>
  99. </span>
  100. </template>
  101. </div>
  102. <!-- 本组件内的列表数据 -->
  103. <div class="table-body-row" v-for="(item,i) in imageList" :key="i">
  104. <span class="table-data">
  105. <div class="checkbox">
  106. <!-- 负责功能 -->
  107. <input
  108. type="checkbox"
  109. @change="e => selectItem(item, e)"
  110. :checked="select.some(i => i[primaryKey] === item[primaryKey])"
  111. >
  112. <!-- 负责外观 -->
  113. <span class="for-outer-circle"></span>
  114. <span class="for-inner-circle"></span>
  115. </div>
  116. </span>
  117. <span class="table-data" v-for="(sub,idx) in tableHeadersForImage" :key="idx">
  118. <div v-if="sub.type=='image'" class="list-img">
  119. <img :src="item[sub.key] + `?x-oss-process=image/resize,p_10&${Math.random()}`" alt="">
  120. </div>
  121. <span class="ellipsis" v-else v-title="sub.key === 'name' ? item[sub.key] : ''">{{ item[sub.key] }}</span>
  122. </span>
  123. </div>
  124. </div>
  125. <!-- 无数据时的提示 -->
  126. <div v-else class="no-data">
  127. <div v-if="latestUsedSearchKey">
  128. <img :src="require('@/assets/images/default/empty_04_search.png')" alt="">
  129. <span>{{'未搜索到结果~'}}</span>
  130. </div>
  131. <div v-if="!latestUsedSearchKey">
  132. <img :src="require('@/assets/images/default/empty_04.png')" alt="">
  133. <span>{{'暂无素材~'}}</span>
  134. </div>
  135. </div>
  136. </div>
  137. <div class="table table-pano" v-show="currentMaterialType === 'pano'">
  138. <div class="table-head-row">
  139. <span class="table-head">1</span>
  140. <span class="table-head" v-for="(item,i) in tableHeadersForPano" :key="i">{{item.name}}</span>
  141. </div>
  142. <div
  143. v-if="panoList.length !== 0 || hasMorePanoData"
  144. class="table-body"
  145. v-infinite-scroll="requestMorePanoData"
  146. :infinite-scroll-disabled="!hasMorePanoData || isRequestingMorePanoData"
  147. >
  148. <div class="table-body-row" v-for="(item,i) in panoList" :key="i">
  149. <span class="table-data">
  150. <div class="checkbox">
  151. <!-- 负责功能 -->
  152. <input
  153. type="checkbox"
  154. @change="e => selectItem(item, e)"
  155. :checked="select.some(i => i[primaryKey] === item[primaryKey])"
  156. >
  157. <!-- 负责外观 -->
  158. <span class="for-outer-circle"></span>
  159. <span class="for-inner-circle"></span>
  160. </div>
  161. </span>
  162. <span class="table-data" v-for="(sub,idx) in tableHeadersForPano" :key="idx">
  163. <div v-if="sub.type=='image'" class="list-img">
  164. <img :src="item[sub.key] + `?x-oss-process=image/resize,p_10&${Math.random()}`" alt="">
  165. </div>
  166. <span class="ellipsis" v-else v-title="sub.key === 'name' ? item[sub.key] : ''">{{item[sub.key]}}</span>
  167. </span>
  168. </div>
  169. </div>
  170. <!-- 无数据时的提示 -->
  171. <div v-if="panoList.length === 0 && !hasMorePanoData" class="no-data">
  172. <div v-if="latestUsedSearchKey">
  173. <img :src="require('@/assets/images/default/empty_04_search.png')" alt="">
  174. <span>{{'未搜索到结果~'}}</span>
  175. </div>
  176. <div v-if="!latestUsedSearchKey">
  177. <img :src="require('@/assets/images/default/empty_04.png')" alt="">
  178. <span>{{'暂无素材~'}}</span>
  179. </div>
  180. </div>
  181. </div>
  182. <div class="table table-audio" v-show="currentMaterialType === 'audio'">
  183. <div class="table-head-row">
  184. <span class="table-head">1</span>
  185. <span class="table-head" v-for="(item,i) in tableHeadersForAudio" :key="i">{{item.name}}</span>
  186. </div>
  187. <div
  188. v-if="audioListRealLength !== 0 || hasMoreAudioData"
  189. class="table-body"
  190. v-infinite-scroll="requestMoreAudioData"
  191. :infinite-scroll-disabled="!hasMoreAudioData || isRequestingMoreAudioData"
  192. >
  193. <!-- vuex中的上传中数据 -->
  194. <div class="table-body-row" v-for="(item, i) in uploadStatusListAudio" :key="item.uid">
  195. <!-- 如果已经上传成功 -->
  196. <template v-if="item.status === 'SUCCESS'">
  197. <span class="table-data">
  198. <div class="checkbox">
  199. <!-- 负责功能 -->
  200. <input
  201. type="checkbox"
  202. @change="e => selectItem(item.successInfo, e)"
  203. :checked="select.some(i => i[primaryKey] === item.successInfo[primaryKey])"
  204. >
  205. <!-- 负责外观 -->
  206. <span class="for-outer-circle"></span>
  207. <span class="for-inner-circle"></span>
  208. </div>
  209. </span>
  210. <span class="table-data" v-for="(tableItemStructure, idx) in tableHeadersForAudio" :key="idx">
  211. <div v-if="tableItemStructure.type=='audio'" class="list-img">
  212. <!-- <img
  213. :src="require('@/assets/images/icons/upload-file-type-icon-audio@2x.png')"
  214. style="object-fit: contain;"
  215. alt=""
  216. > -->
  217. <AudioIconCanPlay
  218. class="icon"
  219. :vKey="item.successInfo.id"
  220. :idleft="`_${$randomWord(true, 8, 8)}`"
  221. :idright="`_${$randomWord(true, 8, 8)}`"
  222. :myAudioUrl="item.successInfo.ossPath"
  223. ></AudioIconCanPlay>
  224. </div>
  225. <span v-else class="ellipsis" v-title="tableItemStructure.key === 'name' ? item.successInfo[tableItemStructure.key] : ''">{{ item.successInfo[tableItemStructure.key] }}</span>
  226. </span>
  227. </template>
  228. <!-- 如果还在上传中 -->
  229. <template v-else-if="item.status = 'LOADING'">
  230. <span class="table-data">
  231. <div class="checkbox">
  232. <span class="for-outer-circle"></span>
  233. <span class="for-inner-circle disabled"></span>
  234. </div>
  235. </span>
  236. <span class="table-data" v-for="(tableItemStructure, idx) in tableHeadersForAudio" :key="idx">
  237. <div v-if="tableItemStructure.type=='audio'" class="list-img">
  238. <img
  239. :src="require('@/assets/images/icons/upload-file-type-icon-audio@2x.png')"
  240. style="object-fit: contain;"
  241. alt=""
  242. >
  243. </div>
  244. <span v-if="tableItemStructure.key !== 'name' && tableItemStructure.key !== 'fileSize'"></span>
  245. <span v-if="tableItemStructure.key === 'fileSize'">上传素材 {{item.progress * 100}}%</span>
  246. <span v-if="tableItemStructure.key === 'name'" class="ellipsis" v-title="tableItemStructure.key === 'name' ? item.title : ''">{{ item.title }}</span>
  247. </span>
  248. </template>
  249. <!-- 如果上传失败了 -->
  250. <template v-else-if="item.status = 'FAIL'">
  251. <span class="table-data">
  252. <div class="checkbox">
  253. <span class="for-outer-circle"></span>
  254. <span class="for-inner-circle disabled"></span>
  255. </div>
  256. </span>
  257. <span class="table-data" v-for="(tableItemStructure, idx) in tableHeadersForImage" :key="idx">
  258. <div v-if="tableItemStructure.type=='audio'" class="list-img">
  259. <img
  260. :src="require('@/assets/images/icons/upload-file-type-icon-audio@2x.png')"
  261. style="object-fit: contain;"
  262. alt=""
  263. >
  264. </div>
  265. <span v-if="tableItemStructure.key !== 'name' && tableItemStructure.key !== 'fileSize'"></span>
  266. <span v-if="tableItemStructure.key === 'fileSize'">上传失败</span>
  267. <span v-if="tableItemStructure.key === 'name'" class="ellipsis" v-title="tableItemStructure.key === 'name' ? item.title : ''">{{ item.title }}</span>
  268. </span>
  269. </template>
  270. </div>
  271. <!-- 本组件内的列表数据 -->
  272. <div class="table-body-row" v-for="(item,i) in audioList" :key="i">
  273. <span class="table-data">
  274. <div class="checkbox">
  275. <!-- 负责功能 -->
  276. <input
  277. type="checkbox"
  278. @change="e => selectItem(item, e)"
  279. :checked="select.some(i => i[primaryKey] === item[primaryKey])"
  280. >
  281. <!-- 负责外观 -->
  282. <span class="for-outer-circle"></span>
  283. <span class="for-inner-circle"></span>
  284. </div>
  285. </span>
  286. <span class="table-data" v-for="(sub,idx) in tableHeadersForAudio" :key="idx">
  287. <div v-if="sub.type=='audio'" class="list-img">
  288. <!-- <img
  289. :src="require('@/assets/images/icons/upload-file-type-icon-audio@2x.png')"
  290. style="object-fit: contain;"
  291. alt=""
  292. > -->
  293. <AudioIconCanPlay
  294. class="audio-player"
  295. :vKey="item.id"
  296. :idleft="`_${$randomWord(true, 8, 8)}`"
  297. :idright="`_${$randomWord(true, 8, 8)}`"
  298. :myAudioUrl="item.ossPath"
  299. ></AudioIconCanPlay>
  300. </div>
  301. <span class="ellipsis" v-else v-title="sub.key === 'name' ? item[sub.key] : ''">{{item[sub.key]}}</span>
  302. </span>
  303. </div>
  304. </div>
  305. <!-- 无数据时的提示 -->
  306. <div v-if="audioList.length === 0 && !hasMoreAudioData" class="no-data">
  307. <div v-if="latestUsedSearchKey">
  308. <img :src="require('@/assets/images/default/empty_04_search.png')" alt="">
  309. <span>{{'未搜索到结果~'}}</span>
  310. </div>
  311. <div v-if="!latestUsedSearchKey">
  312. <img :src="require('@/assets/images/default/empty_04.png')" alt="">
  313. <span>{{'暂无素材~'}}</span>
  314. </div>
  315. </div>
  316. </div>
  317. <div class="table table-video" v-show="currentMaterialType === 'video'">
  318. <div class="table-head-row">
  319. <span class="table-head">1</span>
  320. <span class="table-head" v-for="(item,i) in tableHeadersForVideo" :key="i">{{item.name}}</span>
  321. </div>
  322. <div
  323. v-if="videoListRealLength !== 0 || hasMoreVideoData"
  324. class="table-body"
  325. v-infinite-scroll="requestMoreVideoData"
  326. :infinite-scroll-disabled="!hasMoreVideoData || isRequestingMoreVideoData"
  327. >
  328. <!-- vuex中的上传中数据 -->
  329. <div class="table-body-row" v-for="(item, i) in uploadStatusListVideo" :key="item.uid">
  330. <!-- 如果已经上传成功 -->
  331. <template v-if="item.status === 'SUCCESS'">
  332. <span class="table-data">
  333. <div class="checkbox">
  334. <!-- 负责功能 -->
  335. <input
  336. type="checkbox"
  337. @change="e => selectItem(item.successInfo, e)"
  338. :checked="select.some(i => i[primaryKey] === item.successInfo[primaryKey])"
  339. >
  340. <!-- 负责外观 -->
  341. <span class="for-outer-circle"></span>
  342. <span class="for-inner-circle"></span>
  343. </div>
  344. </span>
  345. <span class="table-data" v-for="(tableItemStructure, idx) in tableHeadersForVideo" :key="idx">
  346. <div v-if="tableItemStructure.type=='image'" class="list-img">
  347. <img
  348. :src="item.successInfo[tableItemStructure.key]"
  349. alt=""
  350. >
  351. </div>
  352. <span v-else class="ellipsis" v-title="tableItemStructure.key === 'name' ? item.successInfo[tableItemStructure.key] : ''">{{ item.successInfo[tableItemStructure.key] }}</span>
  353. </span>
  354. </template>
  355. <!-- 如果还在上传中 -->
  356. <template v-else-if="item.status = 'LOADING'">
  357. <span class="table-data">
  358. <div class="checkbox">
  359. <span class="for-outer-circle"></span>
  360. <span class="for-inner-circle disabled"></span>
  361. </div>
  362. </span>
  363. <span class="table-data" v-for="(tableItemStructure, idx) in tableHeadersForVideo" :key="idx">
  364. <div v-if="tableItemStructure.type=='image'" class="list-img">
  365. <img
  366. :src="require('@/assets/images/icons/upload-file-type-icon-video@2x.png')"
  367. style="object-fit: contain;"
  368. alt=""
  369. >
  370. </div>
  371. <span v-if="tableItemStructure.key !== 'name' && tableItemStructure.key !== 'fileSize'"></span>
  372. <span v-if="tableItemStructure.key === 'fileSize'">上传素材 {{item.progress * 100}}%</span>
  373. <span v-if="tableItemStructure.key === 'name'" class="ellipsis" v-title="tableItemStructure.key === 'name' ? item.title : ''">{{ item.title }}</span>
  374. </span>
  375. </template>
  376. <!-- 如果上传失败了 -->
  377. <template v-else-if="item.status = 'FAIL'">
  378. <span class="table-data">
  379. <div class="checkbox">
  380. <span class="for-outer-circle"></span>
  381. <span class="for-inner-circle disabled"></span>
  382. </div>
  383. </span>
  384. <span class="table-data" v-for="(tableItemStructure, idx) in tableHeadersForImage" :key="idx">
  385. <div v-if="tableItemStructure.type=='image'" class="list-img">
  386. <img
  387. :src="require('@/assets/images/icons/upload-file-type-icon-video@2x.png')"
  388. style="object-fit: contain;"
  389. alt=""
  390. >
  391. </div>
  392. <span v-if="tableItemStructure.key !== 'name' && tableItemStructure.key !== 'fileSize'"></span>
  393. <span v-if="tableItemStructure.key === 'fileSize'">上传失败</span>
  394. <span v-if="tableItemStructure.key === 'name'" class="ellipsis" v-title="tableItemStructure.key === 'name' ? item.title : ''">{{ item.title }}</span>
  395. </span>
  396. </template>
  397. </div>
  398. <!-- 本组件内的列表数据 -->
  399. <div class="table-body-row" v-for="(item,i) in videoList" :key="i">
  400. <span class="table-data">
  401. <div class="checkbox">
  402. <!-- 负责功能 -->
  403. <input
  404. type="checkbox"
  405. @change="e => selectItem(item, e)"
  406. :checked="select.some(i => i[primaryKey] === item[primaryKey])"
  407. >
  408. <!-- 负责外观 -->
  409. <span class="for-outer-circle"></span>
  410. <span class="for-inner-circle"></span>
  411. </div>
  412. </span>
  413. <span class="table-data" v-for="(sub,idx) in tableHeadersForVideo" :key="idx">
  414. <div v-if="sub.type=='image'" class="list-img">
  415. <img :src="item[sub.key]" alt="">
  416. </div>
  417. <span class="ellipsis" v-else v-title="sub.key === 'name' ? item[sub.key] : ''">{{item[sub.key]}}</span>
  418. </span>
  419. </div>
  420. </div>
  421. <!-- 无数据时的提示 -->
  422. <div v-if="videoList.length === 0 && !hasMoreVideoData" class="no-data">
  423. <div v-if="latestUsedSearchKey">
  424. <img :src="require('@/assets/images/default/empty_04_search.png')" alt="">
  425. <span>{{'未搜索到结果~'}}</span>
  426. </div>
  427. <div v-if="!latestUsedSearchKey">
  428. <img :src="require('@/assets/images/default/empty_04.png')" alt="">
  429. <span>{{'暂无素材~'}}</span>
  430. </div>
  431. </div>
  432. </div>
  433. <div class="table table-3D" v-show="currentMaterialType === '3D'">
  434. <div class="table-head-row">
  435. <span class="table-head">1</span>
  436. <span class="table-head" v-for="(item,i) in tableHeadersFor3D" :key="i">{{item.name}}</span>
  437. </div>
  438. <div
  439. v-if="scene3DList.length !== 0 || hasMore3DData"
  440. class="table-body"
  441. v-infinite-scroll="requestMorePanoData"
  442. :infinite-scroll-disabled="!hasMore3DData || isRequestingMore3DData"
  443. >
  444. <div class="table-body-row" v-for="(item,i) in scene3DList" :key="i">
  445. <span class="table-data">
  446. <div class="checkbox">
  447. <!-- 负责功能 -->
  448. <input
  449. type="checkbox"
  450. @change="e => selectItem(item, e)"
  451. :checked="select.some(i => i[primaryKey] === item[primaryKey])"
  452. >
  453. <!-- 负责外观 -->
  454. <span class="for-outer-circle"></span>
  455. <span class="for-inner-circle"></span>
  456. </div>
  457. </span>
  458. <span class="table-data" v-for="(sub,idx) in tableHeadersFor3D" :key="idx">
  459. <div v-if="sub.type=='image'" class="list-img">
  460. <img :src="item[sub.key] + `?x-oss-process=image/resize,p_10&${Math.random()}`" alt="">
  461. </div>
  462. <span class="ellipsis" v-else v-title="sub.key === 'name' ? item[sub.key] : ''">{{item[sub.key]}}</span>
  463. </span>
  464. </div>
  465. </div>
  466. <!-- 无数据时的提示 -->
  467. <div v-if="scene3DList.length === 0 && !hasMore3DData" class="no-data">
  468. <div v-if="latestUsedSearchKey">
  469. <img :src="require('@/assets/images/default/empty_04_search.png')" alt="">
  470. <span>{{'未搜索到结果~'}}</span>
  471. </div>
  472. <div v-if="!latestUsedSearchKey">
  473. <img :src="require('@/assets/images/default/empty_04.png')" alt="">
  474. <span>{{'暂无素材~'}}</span>
  475. <a href="/#/">
  476. <button class="ui-button">如何拍摄三维场景</button>
  477. </a>
  478. </div>
  479. </div>
  480. </div>
  481. <div class="btns">
  482. <button v-if="currentMaterialType !== '3D'" class="ui-button upload-btn" @click="$refs['file-input'].click()">
  483. <span>上传素材</span>
  484. <i class="iconfont icon-material_prompt tool-tip-for-editor"
  485. v-tooltip="
  486. currentMaterialType === 'image' ? '请上传10MB以内、jpg/png格式的图片' :
  487. currentMaterialType === 'pano' ? '请上传2:1、120MB以内、jpg格式的图片' : ''
  488. ">
  489. </i>
  490. <FileInput
  491. ref="file-input"
  492. :failString="fileInputFailString"
  493. :limitFailStr="fileInputLimitFailStr"
  494. :acceptType="fileInputAcceptType"
  495. :mediaType="currentMaterialType"
  496. :limit="fileInputLimit"
  497. @file-change="onFileInputChange"
  498. ></FileInput>
  499. </button>
  500. <div v-else class="button-placeholder"></div>
  501. <div>
  502. <button class="ui-button deepcancel" @click="$emit('cancle')">取消</button>
  503. <button class="ui-button submit" :class="{disable: !select.length}" @click="$emit('submit', select)">
  504. 确定
  505. </button>
  506. </div>
  507. </div>
  508. </div>
  509. </template>
  510. <script>
  511. import {
  512. getMaterialList,
  513. getSceneList,
  514. uploadMaterial,
  515. editMaterial,
  516. delMaterial,
  517. } from "@/api";
  518. import { changeByteUnit } from '@/utils/file'
  519. import config from "@/config";
  520. import { debounce } from "@/utils/other.js"
  521. import FileInput from "@/components/shared/uploads/UploadMultiple.vue";
  522. import { mapState } from "vuex";
  523. import AudioIconCanPlay from "@/components/audio/indexForEditor.vue";
  524. export default {
  525. props:{
  526. title:{
  527. default:'',
  528. type:String
  529. },
  530. primaryKey: {
  531. default:'id'
  532. },
  533. selectableType: {
  534. type: Array,
  535. default: function() {
  536. return [
  537. 'image',
  538. 'pano',
  539. 'audio',
  540. 'video',
  541. '3D',
  542. ]
  543. },
  544. },
  545. initialMaterialType: {
  546. type: String,
  547. default: 'image',
  548. },
  549. isMultiSelection: {
  550. type: Boolean,
  551. default: false,
  552. }
  553. },
  554. components:{
  555. FileInput,
  556. AudioIconCanPlay,
  557. },
  558. watch:{
  559. searchKey: {
  560. handler: function () {
  561. this.refreshMaterialList(this.currentMaterialType)
  562. },
  563. immediate: false,
  564. },
  565. currentMaterialType: {
  566. handler: function (newVal) {
  567. if (newVal === 'image' && this.imageList.length === 0) {
  568. this.refreshMaterialList('image')
  569. } else if (newVal === 'pano' && this.panoList.length === 0) {
  570. this.refreshMaterialList('pano')
  571. } else if (newVal === 'audio' && this.audioList.length === 0) {
  572. this.refreshMaterialList('audio')
  573. } else if (newVal === 'video' && this.videoList.length === 0) {
  574. this.refreshMaterialList('video')
  575. } else if (newVal === '3D' && this.scene3DList.length === 0) {
  576. this.refreshMaterialList('3D')
  577. }
  578. },
  579. immediate: false,
  580. }
  581. },
  582. computed:{
  583. ...mapState({
  584. uploadStatusListAudio: 'uploadStatusListAudio',
  585. uploadStatusListImage: 'uploadStatusListImage',
  586. uploadStatusListPano: 'uploadStatusListPano',
  587. uploadStatusListVideo: 'uploadStatusListVideo',
  588. }),
  589. tableHeadersForImage() {
  590. return this.$MAPTABLEHEADER['image'].filter(item => {
  591. return ['icon', 'name', 'fileSize', 'dpi'].includes(item.key)
  592. })
  593. },
  594. tableHeadersForPano() {
  595. return this.$MAPTABLEHEADER['pano'].filter(item => {
  596. return ['icon', 'name', 'fileSize'].includes(item.key)
  597. })
  598. },
  599. tableHeadersForAudio() {
  600. return this.$MAPTABLEHEADER['audio'].filter(item => {
  601. return ['ossPath', 'name', 'fileSize'].includes(item.key)
  602. })
  603. },
  604. tableHeadersForVideo() {
  605. return this.$MAPTABLEHEADER['video'].filter(item => {
  606. return ['icon', 'name', 'fileSize'].includes(item.key)
  607. })
  608. },
  609. tableHeadersFor3D() {
  610. return this.$MAPTABLEHEADER['scene'].filter(item => {
  611. return ['thumb', 'sceneName', 'createTime'].includes(item.key)
  612. })
  613. },
  614. fileInputFailString() {
  615. let ret = ''
  616. switch (this.currentMaterialType) {
  617. case 'pano':
  618. ret = '格式错误,请上传2:1、120MB以内、jpg格式的全景图片'
  619. break;
  620. case 'image':
  621. ret = '格式错误,请上传10MB以内、jpg/png格式的图片'
  622. break;
  623. case 'audio':
  624. ret = '格式错误,请上传20MB以内、mp3格式的音频'
  625. break;
  626. case 'video':
  627. ret = '格式错误,请上传200MB以内、mp4格式的视频'
  628. break;
  629. default:
  630. break;
  631. }
  632. return ret
  633. },
  634. fileInputLimitFailStr() {
  635. let ret = ''
  636. switch (this.currentMaterialType) {
  637. case 'pano':
  638. ret = '过大,请上传2:1、120MB以内、jpg格式的全景图片'
  639. break;
  640. case 'image':
  641. ret = '过大,请上传10MB以内、jpg/png格式的图片'
  642. break;
  643. case 'audio':
  644. ret = '过大,请上传20MB以内、mp3格式的音频'
  645. break;
  646. case 'video':
  647. ret = '过大,请上传200MB以内、mp4格式的视频'
  648. break;
  649. default:
  650. break;
  651. }
  652. return ret
  653. },
  654. fileInputAcceptType() {
  655. let ret = ''
  656. switch (this.currentMaterialType) {
  657. case 'pano':
  658. ret = 'image/jpeg'
  659. break;
  660. case 'image':
  661. ret = 'image/png,image/jpeg'
  662. break;
  663. case 'audio':
  664. ret = 'audio/mp3'
  665. break;
  666. case 'video':
  667. ret = 'video/mp4'
  668. break;
  669. default:
  670. break;
  671. }
  672. return ret
  673. },
  674. fileInputmediaType() {
  675. let ret = ''
  676. switch (this.currentMaterialType) {
  677. case 'pano':
  678. ret = 'image'
  679. break;
  680. case 'image':
  681. ret = 'image'
  682. break;
  683. case 'audio':
  684. ret = 'audio'
  685. break;
  686. case 'video':
  687. ret = 'video'
  688. break;
  689. default:
  690. break;
  691. }
  692. return ret
  693. },
  694. fileInputLimit() {
  695. let ret
  696. switch (this.currentMaterialType) {
  697. case 'pano':
  698. ret = 120
  699. break;
  700. case 'image':
  701. ret = 10
  702. break;
  703. case 'audio':
  704. ret = 20
  705. break;
  706. case 'video':
  707. ret = 200
  708. break;
  709. default:
  710. break;
  711. }
  712. return ret
  713. },
  714. imageListRealLength() {
  715. return this.imageList.length + this.uploadStatusListImage.filter((item) => {
  716. return item.status === 'SUCCESS'
  717. }).length
  718. },
  719. audioListRealLength() {
  720. return this.audioList.length + this.uploadStatusListAudio.filter((item) => {
  721. return item.status === 'SUCCESS'
  722. }).length
  723. },
  724. videoListRealLength() {
  725. return this.videoList.length + this.uploadStatusListVideo.filter((item) => {
  726. return item.status === 'SUCCESS'
  727. }).length
  728. },
  729. },
  730. data () {
  731. return {
  732. imageList: [],
  733. panoList: [],
  734. audioList: [],
  735. videoList: [],
  736. scene3DList: [],
  737. select: [],
  738. searchKey:'', // 搜索关键词
  739. latestUsedSearchKey: '',
  740. currentMaterialType: this.initialMaterialType,
  741. isRequestingMoreImageData: false,
  742. isRequestingMorePanoData: false,
  743. isRequestingMoreAudioData: false,
  744. isRequestingMoreVideoData: false,
  745. isRequestingMore3DData: false,
  746. hasMoreImageData: true,
  747. hasMorePanoData: true,
  748. hasMoreAudioData: true,
  749. hasMoreVideoData: true,
  750. hasMore3DData: true,
  751. }
  752. },
  753. methods: {
  754. selectItem(item, e) {
  755. item.materialType = this.currentMaterialType // 三维场景数据没有type字段来表明自己是三维场景。所以统一加一个字段。
  756. if (false) {
  757. // 对于图片,大于600kb的,压缩?
  758. } else {
  759. if (this.isMultiSelection) {
  760. if (e.target.checked) {
  761. this.select.push(item)
  762. } else {
  763. const toDeleteIdx = this.select.findIndex((eachSelect) => {
  764. return eachSelect.id === item.id
  765. })
  766. if (toDeleteIdx >= 0) {
  767. this.select.splice(toDeleteIdx, 1)
  768. }
  769. }
  770. } else {
  771. if (e.target.checked) {
  772. this.select = [item]
  773. } else {
  774. this.select = []
  775. }
  776. }
  777. }
  778. },
  779. requestMoreImageData() {
  780. this.isRequestingMoreImageData = true
  781. const latestUsedSearchKey = this.searchKey
  782. getMaterialList(
  783. {
  784. pageNum: Math.floor(this.imageListRealLength / config.PAGE_SIZE) + 1,
  785. pageSize: config.PAGE_SIZE,
  786. searchKey: this.searchKey,
  787. type: 'image',
  788. },
  789. (data) => {
  790. const newData = data.data.list.map((i) => {
  791. i.fileSize = changeByteUnit(Number(i.fileSize));
  792. i.createTime = i.createTime.substring(0, i.createTime.length - 3)
  793. i.updateTime = i.updateTime.substring(0, i.updateTime.length - 3)
  794. return i;
  795. });
  796. this.imageList = this.imageList.concat(newData)
  797. if (this.imageListRealLength === data.data.total) {
  798. this.hasMoreImageData = false
  799. }
  800. this.isRequestingMoreImageData = false
  801. this.latestUsedSearchKey = latestUsedSearchKey
  802. },
  803. () => {
  804. this.isRequestingMoreImageData = false
  805. this.latestUsedSearchKey = latestUsedSearchKey
  806. }
  807. );
  808. },
  809. requestMorePanoData() {
  810. this.isRequestingMorePanoData = true
  811. const latestUsedSearchKey = this.searchKey
  812. getMaterialList(
  813. {
  814. pageNum: Math.floor(this.panoList.length / config.PAGE_SIZE) + 1,
  815. pageSize: config.PAGE_SIZE,
  816. searchKey: this.searchKey,
  817. type: 'pano',
  818. },
  819. (data) => {
  820. const newData = data.data.list.map((i) => {
  821. i.fileSize = changeByteUnit(Number(i.fileSize));
  822. i.createTime = i.createTime.substring(0, i.createTime.length - 3)
  823. i.updateTime = i.updateTime.substring(0, i.updateTime.length - 3)
  824. return i;
  825. });
  826. this.panoList = this.panoList.concat(newData)
  827. if (this.panoList.length === data.data.total) {
  828. this.hasMorePanoData = false
  829. }
  830. this.isRequestingMorePanoData = false
  831. this.latestUsedSearchKey = latestUsedSearchKey
  832. },
  833. () => {
  834. this.isRequestingMorePanoData = false
  835. this.latestUsedSearchKey = latestUsedSearchKey
  836. }
  837. );
  838. },
  839. requestMoreAudioData() {
  840. this.isRequestingMoreAudioData = true
  841. const latestUsedSearchKey = this.searchKey
  842. getMaterialList(
  843. {
  844. pageNum: Math.floor(this.audioListRealLength / config.PAGE_SIZE) + 1,
  845. pageSize: config.PAGE_SIZE,
  846. searchKey: this.searchKey,
  847. type: 'audio',
  848. },
  849. (data) => {
  850. const newData = data.data.list.map((i) => {
  851. i.fileSize = changeByteUnit(Number(i.fileSize));
  852. i.createTime = i.createTime.substring(0, i.createTime.length - 3)
  853. i.updateTime = i.updateTime.substring(0, i.updateTime.length - 3)
  854. return i;
  855. });
  856. this.audioList = this.audioList.concat(newData)
  857. if (this.audioListRealLength === data.data.total) {
  858. this.hasMoreAudioData = false
  859. }
  860. this.isRequestingMoreAudioData = false
  861. this.latestUsedSearchKey = latestUsedSearchKey
  862. },
  863. () => {
  864. this.isRequestingMoreAudioData = false
  865. this.latestUsedSearchKey = latestUsedSearchKey
  866. }
  867. );
  868. },
  869. requestMoreVideoData() {
  870. this.isRequestingMoreVideoData = true
  871. const latestUsedSearchKey = this.searchKey
  872. getMaterialList(
  873. {
  874. pageNum: Math.floor(this.videoListReallength / config.PAGE_SIZE) + 1,
  875. pageSize: config.PAGE_SIZE,
  876. searchKey: this.searchKey,
  877. type: 'video',
  878. },
  879. (data) => {
  880. const newData = data.data.list.map((i) => {
  881. i.fileSize = changeByteUnit(Number(i.fileSize));
  882. i.icon = i.ossPath + '?x-oss-process=video/snapshot,t_0,f_jpg,w_0,h_0,m_fast,ar_auto';
  883. i.createTime = i.createTime.substring(0, i.createTime.length - 3)
  884. i.updateTime = i.updateTime.substring(0, i.updateTime.length - 3)
  885. return i;
  886. });
  887. this.videoList = this.videoList.concat(newData)
  888. if (this.videoListReallength === data.data.total) {
  889. this.hasMoreVideoData = false
  890. }
  891. this.isRequestingMoreVideoData = false
  892. this.latestUsedSearchKey = latestUsedSearchKey
  893. },
  894. () => {
  895. this.isRequestingMoreVideoData = false
  896. this.latestUsedSearchKey = latestUsedSearchKey
  897. }
  898. );
  899. },
  900. requestMore3DData() {
  901. this.isRequestingMore3DData = true
  902. const latestUsedSearchKey = this.searchKey
  903. getSceneList(
  904. {
  905. pageNum: Math.floor(this.scene3DList.length / config.PAGE_SIZE) + 1,
  906. pageSize: config.PAGE_SIZE,
  907. searchKey: this.searchKey,
  908. },
  909. (data) => {
  910. const newData = data.data.data.list.map((i) => {
  911. return i;
  912. });
  913. this.scene3DList = this.scene3DList.concat(newData)
  914. if (this.scene3DList.length === data.data.data.total) {
  915. this.hasMore3DData = false
  916. }
  917. this.isRequestingMore3DData = false
  918. this.latestUsedSearchKey = latestUsedSearchKey
  919. },
  920. () => {
  921. this.isRequestingMore3DData = false
  922. this.latestUsedSearchKey = latestUsedSearchKey
  923. }
  924. )
  925. },
  926. refreshMaterialList: debounce(function(type) {
  927. if (type === 'image') {
  928. this.isRequestingMoreImageData = false
  929. this.hasMoreImageData = true
  930. this.imageList = []
  931. let filterResult = this.uploadStatusListImage.filter((item) => {
  932. return item.status === 'LOADING'
  933. })
  934. this.$store.commit('setUploadStatusListImage', filterResult)
  935. this.requestMoreImageData()
  936. } else if (type === 'pano') {
  937. this.isRequestingMorePanoData = false
  938. this.hasMorePanoData = true
  939. this.panoList = []
  940. this.requestMorePanoData()
  941. } else if (type === 'audio') {
  942. this.isRequestingMoreAudioData = false
  943. this.hasMoreAudioData = true
  944. this.audioList = []
  945. let filterResult = this.uploadStatusListAudio.filter((item) => {
  946. return item.status === 'LOADING'
  947. })
  948. this.$store.commit('setUploadStatusListAudio', filterResult)
  949. this.requestMoreAudioData()
  950. } else if (type === 'video') {
  951. this.isRequestingMoreVideoData = false
  952. this.hasMoreVideoData = true
  953. this.videoList = []
  954. let filterResult = this.uploadStatusListVideo.filter((item) => {
  955. return item.status === 'LOADING'
  956. })
  957. this.$store.commit('setUploadStatusListVideo', filterResult)
  958. this.requestMoreVideoData()
  959. } else if (type === '3D') {
  960. this.isRequestingMore3DData = false
  961. this.hasMore3DData = true
  962. this.scene3DList = []
  963. this.requestMore3DData()
  964. }
  965. }, 700, false),
  966. onFileInputChange(e) {
  967. switch (this.currentMaterialType) {
  968. case 'pano':
  969. this.onPanoFileInputChange(e)
  970. break;
  971. case 'image':
  972. this.onImageFileInputChange(e)
  973. break;
  974. case 'audio':
  975. this.onAudioFileInputChange(e)
  976. break;
  977. case 'video':
  978. this.onVideoFileInputChange(e)
  979. break;
  980. default:
  981. break;
  982. }
  983. },
  984. onPanoFileInputChange(e) {
  985. e.files.forEach(async (eachFile, i) => {
  986. if (
  987. eachFile.type.indexOf("jpeg") <= -1
  988. ) {
  989. setTimeout(() => {
  990. this.$msg({
  991. message: `“${eachFile.name}”格式错误,请上传2:1、120MB以内、jpg格式的全景图片`,
  992. type: "warning",
  993. });
  994. }, i * 100);
  995. return;
  996. }
  997. if (eachFile.name.substring(0, eachFile.name.lastIndexOf(".")).length > 50) {
  998. setTimeout(() => {
  999. this.$msg({
  1000. message: `“${eachFile.name}”名称过长,请上传标题在50字以内的全景图片`,
  1001. type: "warning",
  1002. });
  1003. }, i * 100);
  1004. return;
  1005. }
  1006. let WHRate = null
  1007. try {
  1008. const {width, height} = await getImgWH(eachFile)
  1009. WHRate = width / height
  1010. } catch(e) {
  1011. console.error('获取图像宽高失败:', e)
  1012. setTimeout(() => {
  1013. this.$msg({
  1014. message: `“${eachFile.name}”格式错误,请上传2:1、120MB以内、jpg格式的全景图片`,
  1015. type: "warning",
  1016. });
  1017. }, i * 100);
  1018. return
  1019. }
  1020. if (WHRate !== 2) {
  1021. setTimeout(() => {
  1022. this.$msg({
  1023. message: `“${eachFile.name}”格式错误,请上传2:1、120MB以内、jpg格式的全景图片`,
  1024. type: "warning",
  1025. });
  1026. }, i * 100);
  1027. return
  1028. }
  1029. let itemInUploadList = {
  1030. title: eachFile.name,
  1031. ifKnowProgress: true,
  1032. progress: 0,
  1033. status: 'LOADING',
  1034. statusText: "正在上传素材",
  1035. uid: `u_${this.$randomWord(true, 8, 8)}`,
  1036. abortHandler: null,
  1037. backendId: '',
  1038. };
  1039. itemInUploadList.abortHandler = uploadMaterial(
  1040. {
  1041. file: eachFile
  1042. },
  1043. {
  1044. type: TYPE,
  1045. uid: itemInUploadList.uid,
  1046. },
  1047. (response) => { // 上传成功
  1048. itemInUploadList.statusText = '正在切图处理'
  1049. itemInUploadList.ifKnowProgress = false
  1050. itemInUploadList.backendId = response.data.id
  1051. },
  1052. (err) => {
  1053. if (err.statusText === 'abort') { // 用户取消了上传任务。
  1054. const index = this.uploadListForUI.findIndex((eachItem) => {
  1055. return eachItem.uid === itemInUploadList.uid
  1056. })
  1057. this.uploadListForUI.splice(index, 1)
  1058. } else {
  1059. itemInUploadList.status = 'FAIL'
  1060. itemInUploadList.statusText = '素材上传失败'
  1061. }
  1062. },
  1063. (progress) => {
  1064. itemInUploadList.progress = progress
  1065. }
  1066. )
  1067. this.uploadListForUI.push(itemInUploadList);
  1068. })
  1069. },
  1070. onImageFileInputChange(e) {
  1071. console.log('tableHeadersForImage: ', this.tableHeadersForImage);
  1072. e.files.forEach((eachFile, i) => {
  1073. if (
  1074. eachFile.type.indexOf("jpeg") <= -1 &&
  1075. eachFile.type.indexOf("png") <= -1
  1076. ) {
  1077. setTimeout(() => {
  1078. this.$msg({
  1079. message: `“${eachFile.name}”格式错误,请上传10MB以内、jpg/png格式的图片`,
  1080. type: "warning",
  1081. });
  1082. }, i * 100);
  1083. return;
  1084. }
  1085. if (eachFile.name.substring(0, eachFile.name.lastIndexOf(".")).length > 50) {
  1086. setTimeout(() => {
  1087. this.$msg({
  1088. message: `“${eachFile.name}”名称过长,请上传标题在50字以内的图片`,
  1089. type: "warning",
  1090. });
  1091. }, i * 100);
  1092. return;
  1093. }
  1094. let itemInUploadList = {
  1095. title: eachFile.name,
  1096. ifKnowProgress: true,
  1097. progress: 0,
  1098. status: 'LOADING',
  1099. statusText: "正在上传素材",
  1100. uid: `u_${this.$randomWord(true, 8, 8)}`,
  1101. abortHandler: null,
  1102. successInfo: null,
  1103. };
  1104. itemInUploadList.abortHandler = uploadMaterial(
  1105. {
  1106. file: eachFile
  1107. },
  1108. {
  1109. type: 'image',
  1110. uid: itemInUploadList.uid,
  1111. },
  1112. (result) => { // 上传成功
  1113. const index = this.uploadStatusListImage.findIndex((eachItem) => {
  1114. return eachItem.uid === itemInUploadList.uid
  1115. })
  1116. result.data.fileSize = changeByteUnit(Number(result.data.fileSize));
  1117. result.data.createTime = result.data.createTime.substring(0, result.data.createTime.length - 3)
  1118. result.data.updateTime = result.data.updateTime.substring(0, result.data.updateTime.length - 3)
  1119. this.uploadStatusListImage[index].status = 'SUCCESS'
  1120. this.uploadStatusListImage[index].successInfo = result.data
  1121. return i;
  1122. },
  1123. (err) => {
  1124. if (err.statusText === 'abort') { // 用户取消了上传任务。
  1125. console.log('用户取消了任务!');
  1126. const index = this.uploadStatusListImage.findIndex((eachItem) => {
  1127. return eachItem.uid === itemInUploadList.uid
  1128. })
  1129. this.uploadStatusListImage.splice(index, 1)
  1130. } else {
  1131. console.log('失败!');
  1132. itemInUploadList.status = 'FAIL'
  1133. itemInUploadList.statusText = '素材上传失败'
  1134. }
  1135. },
  1136. (progress) => {
  1137. console.log('进度:', progress);
  1138. itemInUploadList.progress = progress
  1139. }
  1140. );
  1141. this.uploadStatusListImage.unshift(itemInUploadList);
  1142. });
  1143. },
  1144. onAudioFileInputChange(e) {
  1145. e.files.forEach((eachFile, i) => {
  1146. if (eachFile.name.toLowerCase().indexOf("mp3") <= -1) {
  1147. setTimeout(() => {
  1148. this.$msg({
  1149. message: `“${eachFile.name}”格式错误,请上传20MB以内、mp3格式的音频`,
  1150. type: "warning",
  1151. });
  1152. }, i * 100);
  1153. return;
  1154. }
  1155. if (eachFile.name.substring(0, eachFile.name.lastIndexOf(".")).length > 50) {
  1156. setTimeout(() => {
  1157. this.$msg({
  1158. message: `“${eachFile.name}”名称过长,请上传标题在50字以内的音频`,
  1159. type: "warning",
  1160. });
  1161. }, i * 100);
  1162. return;
  1163. }
  1164. let itemInUploadList = {
  1165. title: eachFile.name,
  1166. ifKnowProgress: true,
  1167. progress: 0,
  1168. status: 'LOADING',
  1169. statusText: "正在上传素材",
  1170. uid: `u_${this.$randomWord(true, 8, 8)}`,
  1171. abortHandler: null,
  1172. };
  1173. itemInUploadList.abortHandler = uploadMaterial(
  1174. {
  1175. file: eachFile
  1176. },
  1177. {
  1178. type: 'audio',
  1179. uid: itemInUploadList.uid,
  1180. },
  1181. (result) => { // 上传成功
  1182. console.log('success');
  1183. const index = this.uploadStatusListAudio.findIndex((eachItem) => {
  1184. return eachItem.uid === itemInUploadList.uid
  1185. })
  1186. result.data.fileSize = changeByteUnit(Number(result.data.fileSize));
  1187. result.data.createTime = result.data.createTime.substring(0, result.data.createTime.length - 3)
  1188. result.data.updateTime = result.data.updateTime.substring(0, result.data.updateTime.length - 3)
  1189. this.uploadStatusListAudio[index].status = 'SUCCESS'
  1190. this.uploadStatusListAudio[index].successInfo = result.data
  1191. },
  1192. (err) => {
  1193. console.log('error');
  1194. if (err.statusText === 'abort') { // 用户取消了上传任务。
  1195. console.log('用户取消了');
  1196. const index = this.uploadStatusListAudio.findIndex((eachItem) => {
  1197. return eachItem.uid === itemInUploadList.uid
  1198. })
  1199. this.uploadStatusListAudio.splice(index, 1)
  1200. } else {
  1201. console.log('上传失败');
  1202. itemInUploadList.status = 'FAIL'
  1203. itemInUploadList.statusText = '素材上传失败'
  1204. }
  1205. },
  1206. (progress) => {
  1207. console.log('progress');
  1208. itemInUploadList.progress = progress
  1209. }
  1210. );
  1211. this.uploadStatusListAudio.unshift(itemInUploadList);
  1212. });
  1213. },
  1214. onVideoFileInputChange(e) {
  1215. e.files.forEach((eachFile, i) => {
  1216. if (eachFile.name.toLowerCase().indexOf("mp4") <= -1) {
  1217. setTimeout(() => {
  1218. this.$msg({
  1219. message: `“${eachFile.name}”格式错误,请上传200MB以内、mp4格式的视频`,
  1220. type: "warning",
  1221. });
  1222. }, i * 100);
  1223. return;
  1224. }
  1225. if (eachFile.name.substring(0, eachFile.name.lastIndexOf(".")).length > 50) {
  1226. setTimeout(() => {
  1227. this.$msg({
  1228. message: `“${eachFile.name}”名称过长,请上传标题在50字以内的视频`,
  1229. type: "warning",
  1230. });
  1231. }, i * 100);
  1232. return;
  1233. }
  1234. let itemInUploadList = {
  1235. title: eachFile.name,
  1236. ifKnowProgress: true,
  1237. progress: 0,
  1238. status: 'LOADING',
  1239. statusText: "正在上传素材",
  1240. uid: `u_${this.$randomWord(true, 8, 8)}`,
  1241. abortHandler: null,
  1242. };
  1243. itemInUploadList.abortHandler = uploadMaterial(
  1244. {
  1245. file: eachFile
  1246. },
  1247. {
  1248. type: 'video',
  1249. uid: itemInUploadList.uid,
  1250. },
  1251. (result) => { // 上传成功
  1252. console.log('成功!');
  1253. const index = this.uploadStatusListVideo.findIndex((eachItem) => {
  1254. return eachItem.uid === itemInUploadList.uid
  1255. })
  1256. result.data.fileSize = changeByteUnit(Number(result.data.fileSize));
  1257. result.data.createTime = result.data.createTime.substring(0, result.data.createTime.length - 3)
  1258. result.data.updateTime = result.data.updateTime.substring(0, result.data.updateTime.length - 3)
  1259. result.data.icon = result.data.ossPath + '?x-oss-process=video/snapshot,t_0,f_jpg,w_0,h_0,m_fast,ar_auto';
  1260. this.uploadStatusListVideo[index].status = 'SUCCESS'
  1261. this.uploadStatusListVideo[index].successInfo = result.data
  1262. },
  1263. (err) => {
  1264. if (err.statusText === 'abort') { // 用户取消了上传任务。
  1265. const index = this.uploadStatusListVideo.findIndex((eachItem) => {
  1266. return eachItem.uid === itemInUploadList.uid
  1267. })
  1268. this.uploadStatusListVideo.splice(index, 1)
  1269. } else {
  1270. itemInUploadList.status = 'FAIL'
  1271. itemInUploadList.statusText = '素材上传失败'
  1272. }
  1273. },
  1274. (progress) => {
  1275. console.log('进度:', progress);
  1276. itemInUploadList.progress = progress
  1277. }
  1278. );
  1279. this.uploadStatusListVideo.unshift(itemInUploadList);
  1280. });
  1281. },
  1282. },
  1283. mounted() {
  1284. console.log('tableHeadersForAudio: ', this.tableHeadersForAudio);
  1285. console.log('tableHeadersForVideo: ', this.tableHeadersForVideo);
  1286. },
  1287. unmounted() {
  1288. this.$store.commit('setUploadStatusListImage', this.uploadStatusListImage.filter((item) => {
  1289. return item.status === 'LOADING'
  1290. }))
  1291. this.$store.commit('setUploadStatusListAudio', this.uploadStatusListAudio.filter((item) => {
  1292. return item.status === 'LOADING'
  1293. }))
  1294. this.$store.commit('setUploadStatusListVideo', this.uploadStatusListVideo.filter((item) => {
  1295. return item.status === 'LOADING'
  1296. }))
  1297. }
  1298. }
  1299. </script>
  1300. <style lang="less" scoped>
  1301. .ellipsis{
  1302. text-overflow: ellipsis;
  1303. overflow: hidden;
  1304. white-space: nowrap;
  1305. width: 100%;
  1306. display: inline-block;
  1307. }
  1308. .table-select {
  1309. position: absolute;
  1310. z-index: 3;
  1311. left: 50%;
  1312. top: 50%;
  1313. transform: translateX(-50%) translateY(-50%);
  1314. width: 600px;
  1315. height: 730px;
  1316. background: #1A1B1D;
  1317. border-radius: 4px;
  1318. border: 1px solid #404040;
  1319. padding: 26px;
  1320. }
  1321. .title {
  1322. font-size: 18px;
  1323. color: rgba(255, 255, 255, 0.6);
  1324. }
  1325. .close-btn {
  1326. display: inline-block;
  1327. position: absolute;
  1328. top: 26px;
  1329. right: 20px;
  1330. font-size: 12px;
  1331. color: #969799;
  1332. cursor: pointer;
  1333. padding: 6px;
  1334. }
  1335. .material-tab {
  1336. margin-top: 35px;
  1337. > .material-tab-item {
  1338. display: inline-block;
  1339. margin-right: 20px;
  1340. position: relative;
  1341. cursor: pointer;
  1342. > .text {
  1343. font-size: 14px;
  1344. font-family: MicrosoftYaHei;
  1345. color: rgba(255, 255, 255, 0.6);
  1346. }
  1347. > .bottom-line {
  1348. position: absolute;
  1349. left: 50%;
  1350. transform: translateX(-50%);
  1351. bottom: -4px;
  1352. width: 16px;
  1353. height: 2px;
  1354. background: #0076F6;
  1355. border-radius: 1px;
  1356. }
  1357. }
  1358. }
  1359. .filter {
  1360. margin-top: 28px;
  1361. width: 100%;
  1362. height: 36px;
  1363. background: #252526;
  1364. border-radius: 2px;
  1365. border: 1px solid #404040;
  1366. position: relative;
  1367. > input {
  1368. box-sizing: border-box;
  1369. width: calc(100% - 42px);
  1370. height: 100%;
  1371. border: none;
  1372. padding-left: 16px;
  1373. background: transparent;
  1374. color: #fff;
  1375. outline: none;
  1376. }
  1377. > .search-icon {
  1378. position: absolute;
  1379. top: 50%;
  1380. transform: translateY(-50%);
  1381. right: 18px;
  1382. color: #404040;
  1383. font-size: 20px;
  1384. }
  1385. > .clear-icon {
  1386. position: absolute;
  1387. top: 50%;
  1388. transform: translateY(-50%);
  1389. right: 18px;
  1390. color: #404040;
  1391. font-size: 20px;
  1392. cursor: pointer;
  1393. }
  1394. }
  1395. @table-height: 440px;
  1396. @table-head-row-height: 40px;
  1397. @table-border-size: 1px;
  1398. .table {
  1399. margin-top: 20px;
  1400. border: @table-border-size solid #404040;
  1401. background: #1A1B1D;
  1402. width: 100%;
  1403. height: @table-height;
  1404. > .table-head-row {
  1405. width: 100%;
  1406. height: @table-head-row-height;
  1407. background: #252526;
  1408. color: rgba(255, 255, 255, 0.6);
  1409. .table-head {
  1410. font-size: 16px;
  1411. line-height: @table-head-row-height;
  1412. height: 100%;
  1413. display: inline-block;
  1414. }
  1415. }
  1416. > .table-body {
  1417. height: calc(@table-height - @table-head-row-height - @table-border-size - @table-border-size);
  1418. overflow: auto;
  1419. display: inline-block;
  1420. width: 100%;
  1421. > .table-body-row {
  1422. height: 50px;
  1423. border-bottom: 1px solid #404040;
  1424. display: flex;
  1425. align-items: center;
  1426. > .table-data {
  1427. font-size:14px;
  1428. line-height:50px;
  1429. height: 100%;
  1430. color: #fff;
  1431. > .list-img {
  1432. position: relative;
  1433. height: 100%;
  1434. display: inline-block;
  1435. width: 100%;
  1436. > img, .audio-player {
  1437. position: absolute;
  1438. top: 50%;
  1439. transform: translateY(-50%);
  1440. width: 40px;
  1441. height: 40px;
  1442. object-fit: cover;
  1443. }
  1444. }
  1445. }
  1446. }
  1447. }
  1448. > .no-data {
  1449. height: calc(@table-height - @table-head-row-height - @table-border-size - @table-border-size);
  1450. width: 100%;
  1451. position: relative;
  1452. > div {
  1453. position: absolute;
  1454. top: 50%;
  1455. left: 50%;
  1456. transform: translate(-50%, -50%);
  1457. text-align: center;
  1458. > img {
  1459. width: 116px;
  1460. }
  1461. > span {
  1462. margin-top: 20px;
  1463. display: block;
  1464. font-size: 14px;
  1465. color: rgba(255, 255, 255, 0.6);
  1466. }
  1467. > a {
  1468. > button {
  1469. margin-top: 20px;
  1470. }
  1471. }
  1472. }
  1473. }
  1474. }
  1475. .table-image .table-head,
  1476. .table-image .table-data {
  1477. &:nth-of-type(1) {
  1478. width: 50px;
  1479. color: transparent;
  1480. }
  1481. &:nth-of-type(2) {
  1482. width: calc(116px - 50px);
  1483. }
  1484. &:nth-of-type(3) {
  1485. width: calc(316px - 116px);
  1486. padding-right: 30px;
  1487. }
  1488. &:nth-of-type(4) {
  1489. width: calc(416px - 316px);
  1490. }
  1491. &:nth-of-type(5) {
  1492. width: calc(100% - 416px);
  1493. }
  1494. }
  1495. .table-pano .table-head,
  1496. .table-pano .table-data {
  1497. &:nth-of-type(1) {
  1498. width: 50px;
  1499. color: transparent;
  1500. }
  1501. &:nth-of-type(2) {
  1502. width: calc(116px - 50px);
  1503. }
  1504. &:nth-of-type(3) {
  1505. width: calc(416px - 116px);
  1506. padding-right: 30px;
  1507. }
  1508. &:nth-of-type(4) {
  1509. width: calc(100% - 416px);
  1510. }
  1511. }
  1512. .table-audio .table-head,
  1513. .table-audio .table-data {
  1514. &:nth-of-type(1) {
  1515. width: 50px;
  1516. color: transparent;
  1517. }
  1518. &:nth-of-type(2) {
  1519. width: calc(116px - 50px);
  1520. }
  1521. &:nth-of-type(3) {
  1522. width: calc(416px - 116px);
  1523. padding-right: 30px;
  1524. }
  1525. &:nth-of-type(4) {
  1526. width: calc(100% - 416px);
  1527. }
  1528. }
  1529. .table-video .table-head,
  1530. .table-video .table-data {
  1531. &:nth-of-type(1) {
  1532. width: 50px;
  1533. color: transparent;
  1534. }
  1535. &:nth-of-type(2) {
  1536. width: calc(116px - 50px);
  1537. }
  1538. &:nth-of-type(3) {
  1539. width: calc(416px - 116px);
  1540. padding-right: 30px;
  1541. }
  1542. &:nth-of-type(4) {
  1543. width: calc(100% - 416px);
  1544. }
  1545. }
  1546. .table-3D .table-head,
  1547. .table-3D .table-data {
  1548. &:nth-of-type(1) {
  1549. width: 50px;
  1550. color: transparent;
  1551. }
  1552. &:nth-of-type(2) {
  1553. width: calc(116px - 50px);
  1554. }
  1555. &:nth-of-type(3) {
  1556. width: calc(416px - 116px);
  1557. padding-right: 30px;
  1558. }
  1559. &:nth-of-type(4) {
  1560. width: calc(100% - 416px);
  1561. }
  1562. }
  1563. .checkbox {
  1564. position: relative;
  1565. width: 100%;
  1566. height: 100%;
  1567. input {
  1568. width: 20px;
  1569. height: 20px;
  1570. position: absolute;
  1571. left: 50%;
  1572. top: 50%;
  1573. transform: translate(-50%, -50%);
  1574. cursor: pointer;
  1575. opacity: 0;
  1576. }
  1577. .for-outer-circle {
  1578. width: 16px;
  1579. height: 16px;
  1580. position: absolute;
  1581. left: 50%;
  1582. top: 50%;
  1583. transform: translate(-50%, -50%);
  1584. border-radius: 50%;
  1585. border: 1px solid #404040;
  1586. pointer-events: none;
  1587. }
  1588. .for-inner-circle {
  1589. width: 8px;
  1590. height: 8px;
  1591. position: absolute;
  1592. left: 50%;
  1593. top: 50%;
  1594. transform: translate(-50%, -50%);
  1595. border-radius: 50%;
  1596. background: #0076F6;
  1597. pointer-events: none;
  1598. opacity: 0;
  1599. &.disabled {
  1600. width: 14px;
  1601. height: 14px;
  1602. background: rgba(255, 255, 255, 0.0800);
  1603. opacity: 1;
  1604. }
  1605. }
  1606. }
  1607. .checkbox > input:checked ~ .for-outer-circle {
  1608. border: 1px solid #0076F6;
  1609. }
  1610. .checkbox > input:checked ~ .for-inner-circle {
  1611. opacity: 1;
  1612. }
  1613. .checkbox > input:disabled {
  1614. cursor: not-allowed;
  1615. }
  1616. // .checkbox > input:disabled + span {
  1617. // }
  1618. .btns {
  1619. display: flex;
  1620. justify-content: space-between;
  1621. margin-top: 40px;
  1622. .upload-btn {
  1623. display: flex;
  1624. align-items: center;
  1625. > span {
  1626. display: inline-block;
  1627. margin-right: 4px;
  1628. }
  1629. i.tool-tip-for-editor {
  1630. font-size: 12px;
  1631. transform: scale(0.923) translateY(1px);
  1632. cursor: default;
  1633. }
  1634. }
  1635. > div {
  1636. .deepcancel {
  1637. margin-right: 16px;
  1638. }
  1639. }
  1640. }
  1641. </style>