index.php 51 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236
  1. <?php
  2. ?>
  3. <style scoped>
  4. .com-attachment-list {
  5. /* padding: 5px; */
  6. min-width: 860px;
  7. }
  8. @media screen and (min-width:960px) {
  9. .com-attachment-list {
  10. min-width: 860px;
  11. }
  12. }
  13. @media screen and (min-width:1024px) {
  14. .com-attachment-list {
  15. min-width: 960px;
  16. }
  17. }
  18. @media screen and (min-width:1580px) {
  19. .com-attachment-list {
  20. min-width: 1160px;
  21. }
  22. }
  23. @media screen and (min-width: 1680px) {
  24. .com-attachment-list {
  25. min-width: 1360px;
  26. }
  27. }
  28. .com-attachment-list * {
  29. box-sizing: border-box;
  30. }
  31. .com-attachment-list:after {
  32. clear: both;
  33. display: block;
  34. content: " ";
  35. }
  36. .com-attachment-item {
  37. display: inline-block;
  38. /*box-shadow: 0 0 0 1px rgba(0, 0, 0, .15);*/
  39. cursor: pointer;
  40. position: relative;
  41. width: 120px;
  42. height: 140px;
  43. margin: 7.5px;
  44. text-align: center;
  45. padding: 5px 5px 0;
  46. }
  47. .com-attachment-active-icon {
  48. position: absolute;
  49. right: 5px;
  50. top: 5px;
  51. font-size: 28px;
  52. color: #409EFF;
  53. text-shadow: 0 0 1px rgba(255, 255, 255, 0.75);
  54. opacity: 0;
  55. }
  56. .com-attachment-item.checked,
  57. .com-attachment-item.selected {
  58. box-shadow: 0 0 0 1px #1ed0ff;
  59. background: #daf5ff;
  60. border-radius: 5px;
  61. }
  62. .com-attachment-item.checked .com-attachment-active-icon,
  63. .com-attachment-item.selected .com-attachment-active-icon {
  64. opacity: 1;
  65. }
  66. .com-attachment-item .com-attachment-img {
  67. display: block;
  68. }
  69. .com-attachment-item .file-type-icon {
  70. width: 30px;
  71. height: 30px;
  72. border-radius: 30px;
  73. background: #666;
  74. color: #fff;
  75. text-align: center;
  76. line-height: 30px;
  77. font-size: 24px;
  78. }
  79. .com-attachment-upload {
  80. box-shadow: none;
  81. border: 1px dashed #b2b6bd;
  82. height: 110px;
  83. width: 110px;
  84. margin: 12px;
  85. padding: 0;
  86. }
  87. .com-attachment-upload i {
  88. font-size: 30px;
  89. color: #909399;
  90. }
  91. .com-attachment-upload:hover {
  92. box-shadow: none;
  93. border: 1px dashed #409EFF;
  94. }
  95. .com-attachment-upload:hover i {
  96. color: #409EFF;
  97. }
  98. .com-attachment-upload:active {
  99. border: 1px dashed #20669c;
  100. }
  101. .com-attachment-upload:active i {
  102. color: #20669c;
  103. }
  104. .com-attachment-dialog .group-menu {
  105. border-right: none;
  106. width: 230px;
  107. }
  108. .com-attachment-dialog .group-menu .el-menu-item {
  109. padding-left: 10px !important;
  110. padding-right: 10px;
  111. }
  112. .com-attachment-dialog .group-menu .el-menu-item .el-button {
  113. padding: 3px 0;
  114. }
  115. .del-com-attachment-dialog .group-menu .el-menu-item .el-button:hover {
  116. background: #e2e2e2;
  117. }
  118. .com-attachment-dialog .group-menu .el-menu-item .el-button i {
  119. margin-right: 0;
  120. }
  121. .com-attachment-simple-upload {
  122. width: 100% !important;
  123. height: 120px;
  124. border: 1px dashed #e3e3e3;
  125. cursor: pointer;
  126. }
  127. .com-attachment-simple-upload:hover {
  128. background: rgba(0, 0, 0, .05);
  129. }
  130. .com-attachment-simple-upload i {
  131. font-size: 32px;
  132. }
  133. .com-attachment-video-cover {
  134. background-size: cover;
  135. background-position: center;
  136. width: 100%;
  137. height: 100%;
  138. }
  139. .com-attachment-video-info {
  140. background: rgba(0, 0, 0, .35);
  141. color: #fff;
  142. position: absolute;
  143. left: 0;
  144. bottom: 0;
  145. padding: 1px 3px;
  146. font-size: 14px;
  147. }
  148. .com-attachment-video-name {
  149. position: absolute;
  150. top: 100%;
  151. left: 0;
  152. width: 100%;
  153. font-size: 12px;
  154. white-space: nowrap;
  155. overflow: hidden;
  156. text-overflow: ellipsis;
  157. }
  158. .com-attachment-dialog .com-attachment-name {
  159. color: #333333;
  160. margin-top: 5px;
  161. font-size: 13px;
  162. word-break: break-all;
  163. text-overflow: ellipsis;
  164. display: -webkit-box;
  165. -webkit-box-orient: vertical;
  166. -webkit-line-clamp: 1;
  167. overflow: hidden;
  168. }
  169. .com-attachment-left {
  170. text-align: left;
  171. }
  172. .comattachment-copy {
  173. text-align: right;
  174. }
  175. .com-attachment-dialog .search {
  176. margin-top: 10px;
  177. margin-left: 20px;
  178. width: 300px;
  179. }
  180. .com-attachment-dialog .search .el-input__inner {
  181. border-right: 0;
  182. }
  183. .com-attachment-dialog .search .el-input__inner:hover {
  184. border: 1px solid #dcdfe6;
  185. border-right: 0;
  186. outline: 0;
  187. }
  188. .com-attachment-dialog .search .el-input__inner:focus {
  189. border: 1px solid #dcdfe6;
  190. border-right: 0;
  191. outline: 0;
  192. }
  193. .com-attachment-dialog .search .el-input-group__append {
  194. background-color: #fff;
  195. border-left: 0;
  196. width: 10%;
  197. padding: 0;
  198. }
  199. .com-attachment-dialog .search .el-input-group__append {
  200. background-color: #fff;
  201. border-left: 0;
  202. width: 10%;
  203. padding: 0;
  204. }
  205. .com-attachment-dialog .search .el-input-group__append .el-button {
  206. padding: 0;
  207. }
  208. .com-attachment-dialog .search .el-input-group__append .el-button {
  209. margin: 0;
  210. }
  211. .com-attachment-dialog .scrollbar {
  212. height: 100%;
  213. }
  214. .com-attachment-dialog .scrollbar .el-scrollbar__wrap {
  215. overflow-y: hidden;
  216. }
  217. .features_tree {
  218. display: flex;
  219. justify-content: center;
  220. padding: 0 1px;
  221. }
  222. .features_tree button {
  223. flex: 1;
  224. }
  225. .el-tree-node {
  226. margin-top: 10px;
  227. }
  228. </style>
  229. <div id="app" v-cloak>
  230. <div class="com-attachment">
  231. <div class="com-attachment-dialog">
  232. <el-tabs type="border-card" v-model="type">
  233. <el-tab-pane label="图片" name="images"></el-tab-pane>
  234. <el-tab-pane label="普通视频" name="videos"></el-tab-pane>
  235. <el-tab-pane label="大视频" name="bigVideos" v-if="videoConfigInfo.is_show_long_vide === 1"></el-tab-pane>
  236. <!-- <el-dialog class="com-attachment-dialog" :title="title ? title : '选择文件'" :visible.sync="dialogVisible" @opened="dialogOpened" :close-on-click-modal="false" :width="simple?'25%':'65%'" top="10vh" append-to-body> -->
  237. <template v-if="simple">
  238. <com-upload-oss v-if="type == 'bigVideos'" class="com-attachment-simple-upload" :config="videoConfigInfo" v-loading="uploading" :disabled="uploading" @start="handleStart" @success="handleSuccessBig" :multiple="true" :max="1" :params="uploadParams" :fields="uploadFields" :accept="accept" flex="main:center cross:center">
  239. <div v-if="uploading">{{uploadCompleteFilesNum}}/{{uploadFilesNum}}</div>
  240. <i v-else class="el-icon-upload"></i>
  241. </com-upload-oss>
  242. <com-upload v-else class="com-attachment-simple-upload" v-loading="uploading" :disabled="uploading" @start="handleStart" @success="handleSuccess" @complete="handleComplete" :multiple="true" :max="10" :params="uploadParams" :fields="uploadFields" :accept="accept" flex="main:center cross:center">
  243. <div v-if="uploading">{{uploadCompleteFilesNum}}/{{uploadFilesNum}}</div>
  244. <i v-else class="el-icon-upload"></i>
  245. </com-upload>
  246. </template>
  247. <template v-else>
  248. <div style="height: 0;overflow: hidden;">
  249. <canvas id="com-attachment-canvas" style="border: 1px solid #ccc;visibility: hidden;"></canvas>
  250. </div>
  251. <div flex="cross:center box:last" style="margin-bottom: 12px;">
  252. <div></div>
  253. <div flex="cross:center">
  254. <el-button v-if="!showEditBlock" size="small" @click="showEditBlock=true">开启编辑</el-button>
  255. <template v-if="showEditBlock">
  256. <el-button size="small" @click="showEditBlock=false" style="margin-right: 12px">退出编辑模式</el-button>
  257. <el-checkbox size="small" border v-model="selectAll" @change="selectAllChange" label="全选" style="margin-right: 12px;margin-bottom: 0"></el-checkbox>
  258. <el-button size="small" :loading="deleteLoading" @click="deleteItems" type="danger" plain style="margin-right: 12px">删除
  259. </el-button>
  260. <el-dropdown size="small" v-loading="moveLoading" trigger="click" :split-button="true" @command="moveItems">
  261. <span>移动至</span>
  262. <el-dropdown-menu slot="dropdown" style="max-height: 500px;overflow-y: auto">
  263. <el-dropdown-item v-for="(item, index) in groupList" :command="index" :key="index">
  264. {{item.name}}
  265. </el-dropdown-item>
  266. </el-dropdown-menu>
  267. </el-dropdown>
  268. </template>
  269. </div>
  270. </div>
  271. <div flex="box:first" style="border: 1px solid #e3e3e3;margin-bottom: 10px;min-height: 300px">
  272. <div style="border-right: 1px solid #e3e3e3;background-color:#fff">
  273. <el-menu class="group-menu" mode="vertical" v-loading="groupListLoading">
  274. <el-scrollbar style="height:450px;width:100%">
  275. <el-menu-item index="all" @click="switchGroup(-1)">
  276. <i class="el-icon-tickets"></i>
  277. <span>全部</span>
  278. </el-menu-item>
  279. <div class="features_tree">
  280. <!-- @click="addNode" -->
  281. <el-button type="text" v-if="!is_recycle" @click="addNode"><i class="el-icon-circle-plus"></i>添加</el-button>
  282. <el-button type="text" v-if="!is_recycle" @click="editNode"><i class="el-icon-edit"></i>编辑</el-button>
  283. <el-button type="text" v-if="is_recycle" @click="deleteGroups(is_recycle,2)"><i class="el-icon-refresh"></i>还原</el-button>
  284. <el-button type="text" @click="deleteGroups(is_recycle,is_recycle?3:1)"><i class="el-icon-delete"></i>删除</el-button>
  285. </div>
  286. <el-tree :data="data2" highlight-current @node-click="handleNodeClick" :props="defaultProps">
  287. <span slot-scope="{ node, data }">
  288. <i class="el-icon-tickets"></i>
  289. <span>{{ node.label }}</span>
  290. </span>
  291. </el-tree>
  292. </el-scrollbar>
  293. </el-menu>
  294. </div>
  295. <div v-loading="loading" flex="dir:top">
  296. <el-scrollbar class="scrollbar">
  297. <div class="search">
  298. <el-input size="small" placeholder="请输入名称搜索" v-model="p_keyword" @keyup.enter.native="picSearch" class="input-with-select">
  299. <el-button @click="picSearch" slot="append" icon="el-icon-search"></el-button>
  300. </el-input>
  301. </div>
  302. <div class="com-attachment-list">
  303. <div class="com-attachment-item com-attachment-upload" :style="{'width': (type == 'videos' || type === 'bigVideos') ? '180px' : ''}">
  304. <com-upload-oss v-if="type == 'bigVideos'" :config="videoConfigInfo" v-loading="uploading" :disabled="uploading" @start="handleStart" @success="handleSuccessBig" :multiple="true" :max="1" :params="uploadParams" :fields="uploadFields" :accept="accept" flex="main:center cross:center" style="width: 100%;height: 100px">
  305. <div v-if="uploading">{{uploadCompleteFilesNum}}/{{uploadFilesNum}}</div>
  306. <i v-else class="el-icon-upload"></i>
  307. </com-upload-oss>
  308. <com-upload v-else v-loading="uploading" :disabled="uploading" @start="handleStart" @success="handleSuccess" @complete="handleComplete" :multiple="true" :max="10" :params="uploadParams" :fields="uploadFields" :accept="accept" flex="main:center cross:center" style="width: 100%;height: 100px">
  309. <div v-if="uploading">{{uploadCompleteFilesNum}}/{{uploadFilesNum}}</div>
  310. <i v-else class="el-icon-upload"></i>
  311. </com-upload>
  312. </div>
  313. <template v-for="(item, index) in attachments">
  314. <el-tooltip class="item" effect="dark" :content="String(item.old_name)" placement="top" :open-delay="1">
  315. <div :key="index" :class="'com-attachment-item'+((item.checked&&!showEditBlock)?' checked':'')+(item.selected&&showEditBlock?' selected':'')" :style="{'width': (item.upload_type == 'videos' || item.upload_type == 'bigVideos') ? '190px' : ''}" @click="handleClick(item)">
  316. <!-- 图片 -->
  317. <img v-if="item.upload_type == 'images'" class="com-attachment-img" :src="item.url" style="width: 110px;height: 110px;">
  318. <!-- 视频 -->
  319. <div v-if="item.upload_type == 'videos' || item.upload_type == 'bigVideos'" class="com-attachment-img" style="width: 180px;height: 76px;position: relative" @click="playVideo(item)">
  320. <div v-if="item.cover_pic_src" class="com-attachment-video-cover" :style="'background-image: url('+item.cover_pic_src+');'"></div>
  321. <video style="width: 100%; height: 100%; object-fit: cover;" error="预览图加载失败" :id="'app_attachment_'+ _uid + '_' + index" :src="item.url">
  322. </video>
  323. <!--
  324. <div class="com-attachment-video-info" style="margin-left: 24px;">
  325. <i class="el-icon-video-play"></i>
  326. <span>{{item.duration?item.duration:'--:--'}}</span>
  327. </div>-->
  328. </div>
  329. <!-- 音频 -->
  330. <div v-if="item.upload_type == 'voices'" class="com-attachment-img" style="width: 100px;height: 100px;position: relative">
  331. <!-- <div v-if="item.cover_pic_src"
  332. class="com-attachment-video-cover"
  333. :style="'background-image: url('+item.cover_pic_src+');'">
  334. </div> -->
  335. <span>{{item.old_name}}</span>
  336. </div>
  337. <!-- 文件 -->
  338. <div v-if="item.upload_type == 'files'" class="com-attachment-img" style="width: 100px;height: 100px;line-height: 100px;text-align: center">
  339. <i class="file-type-icon el-icon-document"></i>
  340. </div>
  341. <div v-if="item.upload_type != 'voices'" class="com-attachment-name" :class="[item.upload_type == 'images' ? '' : 'com-attachment-left']">{{item.old_name}}</div>
  342. <i v-if="false" class="com-attachment-active-icon el-icon-circle-check"></i>
  343. <div v-if="item.upload_type == 'videos' || item.upload_type == 'bigVideos'">
  344. <div style="fontSize: 12px; color: #666666; display: flex; justify-content: space-between;">
  345. <span>{{ item.created_at|dateTimeFormat('Y-m-d H:i:s') }}</span>
  346. <span>{{ (item.size/(1024*1024)).toFixed(2) }}M</span>
  347. </div>
  348. <div class="comattachment-copy">
  349. <el-link class="copy_btn" style="fontSize: 12px;" type="primary" :underline="false" @click="copyLink(item.url)">复制链接</el-link>
  350. </div>
  351. </div>
  352. </div>
  353. </el-tooltip>
  354. </template>
  355. </div>
  356. </el-scrollbar>
  357. <div style="padding: 5px;text-align: right;margin-top:auto">
  358. <el-pagination v-if="pagination" background @size-change="handleLoadMore" @current-change="handleLoadMore" :current-page.sync="page" :page-size="pagination.pageSize" layout="prev, pager, next, jumper" :total="pagination.total_count">
  359. </el-pagination>
  360. </div>
  361. </div>
  362. </div>
  363. <div style="text-align: right">
  364. <span v-if="showEditBlock" style="color: #909399">请先退出编辑模式</span>
  365. <!-- <el-button @click="confirm" type="primary" :disabled="showEditBlock">选定</el-button> -->
  366. </div>
  367. </template>
  368. </el-tabs>
  369. <!-- </el-dialog> -->
  370. </div>
  371. <el-dialog append-to-body title="分组管理" :visible.sync="addGroupVisible" :close-on-click-modal="false" width="25%">
  372. <el-form @submit.native.prevent label-width="80px" ref="groupForm" :model="groupForm" :rules="groupFormRule">
  373. <el-form-item label="分组名称" prop="name" style="margin-bottom: 22px;">
  374. <el-input v-model="groupForm.name" maxlength="8" show-word-limit></el-input>
  375. </el-form-item>
  376. <el-form-item style="text-align: right">
  377. <el-button size="small" type="primary" @click="groupFormSubmit('groupForm')" :loading="groupFormLoading">保存
  378. </el-button>
  379. </el-form-item>
  380. </el-form>
  381. </el-dialog>
  382. <el-dialog append-to-body title="添加目录" :visible.sync="dialogFormVisible" :close-on-click-modal="false">
  383. <el-form :model="form_dir" :rules="form_dir_rule">
  384. <el-form-item label="父级目录" :label-width="formLabelWidth">
  385. <el-cascader expand-trigger="hover" :options="data2" v-model="selectedOptions2" :props="{ checkStrictly: true, value: 'id' }" clearable @change="handleChange">
  386. </el-cascader>
  387. </el-form-item>
  388. <el-form-item label="目录名称" :label-width="formLabelWidth">
  389. <el-input v-model="form_dir.name" auto-complete="off"></el-input>
  390. </el-form-item>
  391. </el-form>
  392. <div slot="footer" class="dialog-footer">
  393. <el-button size="small" @click="dialogFormVisible = false">取 消</el-button>
  394. <el-button size="small" type="primary" @click="postNode('add')">确 定</el-button>
  395. </div>
  396. </el-dialog>
  397. <el-dialog append-to-body title="编辑目录" :visible.sync="dialogFormVisibleEdit" :close-on-click-modal="false">
  398. <el-form :model="form_dir_s" :rules="form_dir_rules">
  399. <el-form-item label="父级目录" :label-width="formLabelWidth">
  400. <el-cascader expand-trigger="hover" :options="getOptions()" v-model="selectedOptions3" :props="{ checkStrictly: true, value: 'id' }" clearable @change="handleChange2">
  401. </el-cascader>
  402. </el-form-item>
  403. <el-form-item label="目录名称" :label-width="formLabelWidth">
  404. <el-input v-model="form_dir_s.name" auto-complete="off"></el-input>
  405. </el-form-item>
  406. </el-form>
  407. <div slot="footer" class="dialog-footer">
  408. <el-button size="small" @click="dialogFormVisibleEdit = false">取 消</el-button>
  409. <el-button size="small" type="primary" @click="postNode('edit')">确 定</el-button>
  410. </div>
  411. </el-dialog>
  412. <el-dialog :visible.sync="videoPlayVisible" title="" @close="videoPlayVisible = false" width="42%">
  413. <video width="100%" height="460" :src="videoPlayUrl" autoplay controls></video>
  414. </el-dialog>
  415. <div style="line-height: normal;" @click="dialogVisible = !dialogVisible" :style="'display:'+(display?display:'inline-block')">
  416. <slot></slot>
  417. </div>
  418. </div>
  419. </div>
  420. <script>
  421. new Vue({
  422. el: '#app',
  423. data() {
  424. return {
  425. canvas: null,
  426. uploading: false,
  427. dialogVisible: true,
  428. loading: true,
  429. loadingMore: false,
  430. noMore: false,
  431. attachments: [],
  432. checkedAttachments: [],
  433. uploadParams: {},
  434. uploadFields: {
  435. upload_type: this.type || 'images'
  436. },
  437. uploadCompleteFilesNum: 0,
  438. uploadFilesNum: 0,
  439. page: 0,
  440. addGroupVisible: false,
  441. noMall: true,
  442. groupList: [],
  443. groupItem: [],
  444. groupListLoading: false,
  445. groupForm: {
  446. id: null,
  447. name: '',
  448. },
  449. groupFormRule: {
  450. name: [{
  451. required: true,
  452. message: '请填写分组名称。',
  453. }],
  454. },
  455. groupFormLoading: false,
  456. showEditBlock: false,
  457. selectAll: false,
  458. deleteLoading: false,
  459. moveLoading: false,
  460. currentAttachmentGroupId: null,
  461. video: null,
  462. keyword: '',
  463. pagination: null,
  464. p_keyword: '',
  465. data2: [],
  466. defaultProps: {
  467. children: 'children',
  468. label: 'label'
  469. },
  470. dialogFormVisible: false,
  471. dialogFormVisibleEdit: false,
  472. videoPlayVisible: false,
  473. videoPlayUrl: '',
  474. form_dir: {
  475. name: '',
  476. parent_id: '',
  477. },
  478. form_dir_rule: {
  479. name: [{
  480. required: true,
  481. message: '请填写名称',
  482. }],
  483. },
  484. form_dir_s: {
  485. id: '',
  486. parent_id: '',
  487. name: '',
  488. },
  489. form_dir_rules: {
  490. name: [{
  491. required: true,
  492. message: '请填写名称',
  493. }],
  494. },
  495. formLabelWidth: '120px',
  496. selectedOptions: [],
  497. selectedOptions2: [],
  498. select_name: '全部',
  499. is_recycle: 0,
  500. pppid: [],
  501. selectedOptions3: [],
  502. type: 'images',
  503. videoConfigInfo: {},
  504. }
  505. },
  506. mounted() {
  507. this.dialogOpened()
  508. this.uploadConfig()
  509. },
  510. watch: {
  511. type(newVal, oldVal) {
  512. this.type = newVal;
  513. this.uploadFields.upload_type = this.type;
  514. this.dialogOpened()
  515. },
  516. openDialog(newVal, oldVal) {
  517. this.dialogVisible = newVal;
  518. },
  519. dialogVisible(newVal, oldVal) {
  520. if (!newVal) {
  521. this.$emit("closed");
  522. }
  523. },
  524. keyword(newVal, oldVal) {
  525. const groupList = this.groupList;
  526. let arr = [];
  527. groupList.map(v => {
  528. if (v.label.indexOf(newVal) !== -1) {
  529. arr.push(v);
  530. }
  531. });
  532. this.groupItem = arr;
  533. }
  534. },
  535. computed: {
  536. accept: {
  537. get() {
  538. if (this.type === 'images') {
  539. return 'image/*';
  540. }
  541. if (this.type === 'videos' || this.type === 'bigVideos') {
  542. return 'video/*';
  543. }
  544. return '*/*';
  545. },
  546. },
  547. },
  548. props: {
  549. display: String,
  550. title: String,
  551. multiple: Boolean,
  552. max: Number,
  553. params: Object,
  554. simple: {
  555. type: Boolean,
  556. value: false,
  557. },
  558. value: {
  559. type: String,
  560. default: '',
  561. },
  562. openDialog: {
  563. type: Boolean,
  564. default: false,
  565. },
  566. },
  567. methods: {
  568. getOptions() {
  569. let data = JSON.parse(JSON.stringify(this.data2))
  570. let id = this.form_dir_s.id
  571. let pid = this.form_dir_s.parent_id
  572. let self = this
  573. var dismap = function(v, i) {
  574. if (v.children && v.children.length > 0) {
  575. v.children.map(dismap)
  576. }
  577. if (v.id == id) {
  578. v.disabled = true
  579. }
  580. if (v.id == pid) {
  581. v.disabled = true
  582. }
  583. if (self.isChildrenId(v.id)) {
  584. v.disabled = true
  585. }
  586. return v
  587. }
  588. return data.map(dismap)
  589. },
  590. /**
  591. * 作用是不能把当前元素选择到当前元素的子元素下面
  592. */
  593. isChildrenId(cid) {
  594. let id = this.form_dir_s.id
  595. let child = []
  596. let cids = []
  597. var getChildren = function(arr) {
  598. arr.forEach(item => {
  599. if (item.id == id) {
  600. child = item.children
  601. }
  602. if (item.children && item.children.length > 0) {
  603. getChildren(item.children)
  604. }
  605. })
  606. }
  607. var getChildrenId = function(arr) {
  608. arr.forEach(item => {
  609. if (item.id) {
  610. cids.push(item.id)
  611. }
  612. if (item.children && item.children.length > 0) {
  613. getChildrenId(item.children)
  614. }
  615. })
  616. }
  617. getChildren(this.data2)
  618. getChildrenId(child)
  619. return cids.find(function(ccc) {
  620. return ccc == cid
  621. })
  622. },
  623. picSearch() {
  624. this.page = 1;
  625. this.loading = true;
  626. this.loadGroups();
  627. this.loadList({});
  628. },
  629. dialogOpened() {
  630. if (this.simple) {
  631. return;
  632. }
  633. this.page = 1;
  634. this.loading = true;
  635. this.loadGroups();
  636. this.loadList({});
  637. },
  638. deleteItems() {
  639. const itemIds = [];
  640. for (let i in this.attachments) {
  641. if (this.attachments[i].selected) {
  642. itemIds.push(this.attachments[i].id);
  643. }
  644. }
  645. if (!itemIds.length) {
  646. this.$message.warning('请先选择需要删除的图片。');
  647. return;
  648. }
  649. this.$confirm('确认删除所选的' + itemIds.length + '张图片?', '提示', {
  650. type: 'warning'
  651. }).then(() => {
  652. this.deleteLoading = true;
  653. this.$request({
  654. params: {
  655. r: 'common/file/delete'
  656. },
  657. method: 'post',
  658. data: {
  659. ids: itemIds,
  660. },
  661. }).then(e => {
  662. this.deleteLoading = false;
  663. if (e.data.code === 0) {
  664. this.$message.success(e.data.msg);
  665. for (let i in itemIds) {
  666. for (let j in this.attachments) {
  667. if (this.attachments[j].id == itemIds[i]) {
  668. this.attachments.splice(j, 1);
  669. break;
  670. }
  671. }
  672. }
  673. } else {
  674. this.$message.error(e.data.msg);
  675. }
  676. }).catch(e => {
  677. this.deleteLoading = false;
  678. });
  679. }).catch(() => {});
  680. },
  681. selectAllChange(value) {
  682. for (let i in this.attachments) {
  683. this.attachments[i].selected = value;
  684. }
  685. },
  686. selectItem(item) {
  687. item.selected = item.selected ? false : true;
  688. },
  689. moveItems(index) {
  690. const itemIds = [];
  691. for (let i in this.attachments) {
  692. if (this.attachments[i].selected) {
  693. itemIds.push(this.attachments[i].id);
  694. }
  695. }
  696. let itemIdsText = '请先选择需要移动的'+(this.type == 'images' ? '图片': '视频')+'。'
  697. if (!itemIds.length) {
  698. this.$message.warning(itemIdsText);
  699. return;
  700. }
  701. this.$confirm('确认移动所选的' + itemIds.length + ''+(this.type == 'images' ? '张图片': '个视频')+'?', '提示', {
  702. type: 'warning'
  703. }).then(() => {
  704. this.moveLoading = true;
  705. this.$request({
  706. params: {
  707. r: 'common/file/move'
  708. },
  709. method: 'post',
  710. data: {
  711. ids: itemIds,
  712. attachment_group_id: this.groupItem[index].id,
  713. },
  714. }).then(e => {
  715. this.moveLoading = false;
  716. if (e.data.code === 0) {
  717. this.$message.success(e.data.msg);
  718. this.switchGroup(index);
  719. } else {
  720. this.$message.error(e.data.msg);
  721. }
  722. }).catch(e => {
  723. this.moveLoading = false;
  724. });
  725. }).catch(() => {});
  726. },
  727. loadGroups() {
  728. this.groupListLoading = true;
  729. this.$request({
  730. params: {
  731. r: 'common/file/group-list',
  732. is_recycle: 0,
  733. type: this.type,
  734. },
  735. }).then(e => {
  736. this.groupListLoading = false;
  737. if (e.data.code === 0) {
  738. this.noMall = e.data.data.no_mall ? e.data.data.no_mall : false;
  739. this.groupItem = e.data.data.list;
  740. this.groupList = e.data.data.list;
  741. this.data2 = e.data.data.arr;
  742. } else {
  743. this.$message.error(e.data.msg);
  744. }
  745. }).catch(e => {
  746. this.groupListLoading = false;
  747. });
  748. },
  749. groupFormSubmit(formName) {
  750. this.$refs[formName].validate(valid => {
  751. if (valid) {
  752. this.groupFormLoading = true;
  753. this.$request({
  754. params: {
  755. r: 'common/attachment/group-update',
  756. },
  757. method: 'post',
  758. data: Object.assign({}, this.groupForm, {
  759. 'type': this.type
  760. }),
  761. }).then(e => {
  762. this.groupFormLoading = false;
  763. if (e.data.code === 0) {
  764. this.$message.success(e.data.msg);
  765. this.addGroupVisible = false;
  766. if (this.groupForm.edit_index > -1) {
  767. this.groupItem[this.groupForm.edit_index] = e.data.data;
  768. } else {
  769. this.groupItem.push(e.data.data);
  770. }
  771. } else {
  772. this.$message.error(e.data.msg);
  773. }
  774. }).catch(e => {
  775. this.groupFormLoading = false;
  776. });
  777. }
  778. })
  779. },
  780. switchGroup(index) {
  781. this.attachments = [];
  782. this.page = 0;
  783. this.noMore = false;
  784. this.loading = true;
  785. this.uploadParams = {
  786. attachment_group_id: index > -1 ? this.groupItem[index].id : null,
  787. };
  788. this.currentAttachmentGroupId = index > -1 ? this.groupItem[index].id : null;
  789. this.loadList({});
  790. },
  791. loadList(params) {
  792. this.noMore = false;
  793. this.selectAll = false;
  794. params['r'] = 'common/file/selector';
  795. params['page'] = this.page;
  796. params['attachment_group_id'] = this.currentAttachmentGroupId;
  797. params['upload_type'] = this.type;
  798. params['is_recycle'] = 0;
  799. params['keyword'] = this.p_keyword;
  800. this.$request({
  801. params: params,
  802. }).then(e => {
  803. if (e.data.code === 0) {
  804. if (!e.data.data.list.length) {
  805. this.noMore = true;
  806. }
  807. for (let i in e.data.data.list) {
  808. e.data.data.list[i].checked = false;
  809. e.data.data.list[i].selected = false;
  810. e.data.data.list[i].duration = null;
  811. }
  812. //this.attachments = this.attachments.concat(e.data.data.list);
  813. this.attachments = e.data.data.list;
  814. this.pagination = e.data.data.pagination;
  815. this.pagination.pageSize = e.data.data.page_size;
  816. this.pagination.total_count = e.data.data.count;
  817. this.checkedAttachments = [];
  818. this.loading = false;
  819. this.loadingMore = false;
  820. this.updateVideo();
  821. } else {
  822. this.$message.error(e.data.msg);
  823. this.dialogVisible = false;
  824. }
  825. }).catch(e => {});
  826. },
  827. handleClick(item) {
  828. if (this.showEditBlock) {
  829. this.selectItem(item);
  830. return;
  831. }
  832. if (item.checked) {
  833. item.checked = false;
  834. for (let i in this.checkedAttachments) {
  835. if (item.id === this.checkedAttachments[i].id) this.checkedAttachments.splice(i, 1);
  836. }
  837. return;
  838. }
  839. if (this.multiple) {
  840. let checkedCount = 0;
  841. for (let i in this.attachments)
  842. if (this.attachments[i].checked) checkedCount++;
  843. if (this.max && !item.checked && checkedCount >= this.max) return;
  844. item.checked = true;
  845. this.checkedAttachments.push(item);
  846. } else {
  847. for (let i in this.attachments) this.attachments[i].checked = false;
  848. item.checked = true;
  849. this.checkedAttachments = [item];
  850. }
  851. },
  852. confirm() {
  853. this.$emit('selected', this.checkedAttachments, this.params);
  854. this.dialogVisible = false;
  855. const urls = [];
  856. for (let i in this.checkedAttachments) {
  857. urls.push(this.checkedAttachments[i].url);
  858. }
  859. for (let i in this.attachments) {
  860. this.attachments[i].checked = false;
  861. }
  862. this.checkedAttachments = [];
  863. if (!urls.length) {
  864. return;
  865. }
  866. if (this.multiple) {
  867. this.$emit('input', urls);
  868. } else {
  869. this.$emit('input', urls[0]);
  870. }
  871. },
  872. // 上传开始
  873. handleStart(files) {
  874. this.uploading = true;
  875. this.uploadFilesNum = files.length;
  876. this.uploadCompleteFilesNum = 0;
  877. },
  878. // 成功上传
  879. handleSuccess(file) {
  880. console.log(file, 'file')
  881. if (file.response && file.response.data && file.response.data.code === 0) {
  882. const newItem = {
  883. checked: false,
  884. selected: false,
  885. created_at: file.response.data.data.created_at,
  886. deleted_at: file.response.data.data.deleted_at,
  887. id: `${file.response.data.data.id}`,
  888. is_delete: file.response.data.data.is_delete,
  889. mall_id: file.response.data.data.mall_id,
  890. name: file.response.data.data.name,
  891. old_name: file.response.data.data.old_name,
  892. size: file.response.data.data.size,
  893. storage_id: file.response.data.data.storage_id,
  894. thumb_url: file.response.data.data.thumb_url,
  895. type: file.response.data.data.type,
  896. updated_at: file.response.data.data.updated_at,
  897. url: file.response.data.data.url,
  898. user_id: file.response.data.data.user_id,
  899. duration: null,
  900. cover_pic_src: null,
  901. upload_type: file.response.data.data.upload_type
  902. };
  903. this.attachments.unshift(newItem);
  904. this.uploadCompleteFilesNum++;
  905. this.updateVideo();
  906. }
  907. },
  908. handleSuccessBig() {
  909. this.loadList({})
  910. this.uploading = false;
  911. },
  912. // 上传完毕
  913. handleComplete(files) {
  914. this.uploading = false;
  915. if (this.simple) {
  916. let urls = [];
  917. let attachments = [];
  918. for (let i in files) {
  919. if (files[i].response.data && files[i].response.data.code === 0) {
  920. urls.push(files[i].response.data.data.url);
  921. attachments.push(files[i].response.data.data);
  922. }
  923. }
  924. if (!urls.length) {
  925. return;
  926. }
  927. this.dialogVisible = false;
  928. this.$emit('selected', attachments, this.params);
  929. if (this.multiple) {
  930. this.$emit('input', urls);
  931. } else {
  932. this.$emit('input', urls[0]);
  933. }
  934. }
  935. },
  936. handleLoadMore(currentPage) {
  937. if (this.noMore) {
  938. return;
  939. }
  940. this.page = currentPage;
  941. this.loading = true;
  942. this.loadingMore = true;
  943. this.loadList({});
  944. },
  945. updateVideo() {
  946. if (!this.canvas) {
  947. this.canvas = document.getElementById('com-attachment-canvas');
  948. }
  949. for (let i in this.attachments) {
  950. if (this.attachments[i].type == 2) {
  951. if (this.attachments[i].duration) {
  952. continue;
  953. }
  954. let times = 0;
  955. let video = null;
  956. const maxRetry = 10;
  957. const id = 'app_attachment_' + this._uid + '_' + i;
  958. const timer = setInterval(() => {
  959. times++;
  960. if (times >= maxRetry) {
  961. clearInterval(timer);
  962. }
  963. if (!video) {
  964. video = document.getElementById(id);
  965. }
  966. if (!video) {
  967. return;
  968. }
  969. try {
  970. const zoom = 0.15;
  971. this.canvas.width = video.videoWidth * zoom;
  972. this.canvas.height = video.videoHeight * zoom;
  973. this.canvas.getContext('2d').drawImage(video, 0, 0, this.canvas.width, this.canvas.height);
  974. this.attachments[i].cover_pic_src = this.canvas.toDataURL('image/jpg');
  975. } catch (e) {
  976. console.warn('获取视频封面异常: ', e);
  977. }
  978. if (video.duration && !isNaN(video.duration)) {
  979. let m = Math.trunc(video.duration / 60);
  980. let s = Math.trunc(video.duration) % 60;
  981. m = m < 10 ? `0${m}` : `${m}`;
  982. s = s < 10 ? `0${s}` : `${s}`;
  983. this.attachments[i].duration = `${m}:${s}`;
  984. clearInterval(timer);
  985. }
  986. }, 500);
  987. }
  988. }
  989. },
  990. editNode() {
  991. if (this.form_dir_s.id == '') {
  992. this.$message.error('请选择目录');
  993. return false;
  994. }
  995. // 编辑时候自动选中当前的父级
  996. let data = JSON.parse(JSON.stringify(this.data2))
  997. let pid = 0
  998. let id = this.form_dir_s.id
  999. let pids = []
  1000. let getPid = function(arr, id) {
  1001. arr.forEach(item => {
  1002. if (item.id == id) {
  1003. pid = item.parent_id
  1004. if (pid > 0) {
  1005. pids.push(pid)
  1006. getPid(data, pid)
  1007. }
  1008. }
  1009. if (item.children && item.children.length > 0) {
  1010. getPid(item.children, id)
  1011. }
  1012. })
  1013. }
  1014. getPid(data, id)
  1015. pids.reverse()
  1016. this.selectedOptions3 = pids
  1017. this.dialogFormVisibleEdit = true;
  1018. },
  1019. handleChange(val) {
  1020. this.form_dir.parent_id = val;
  1021. },
  1022. handleChange2(val) {
  1023. this.form_dir_s.parent_id = val
  1024. },
  1025. postNode(type) {
  1026. let parent_id = this.form_dir.parent_id[this.form_dir.parent_id.length - 1]
  1027. let formData = {
  1028. name: type == 'add' ? this.form_dir.name : this.form_dir_s.name
  1029. }
  1030. if (type == 'add') {
  1031. formData.parent_id = parent_id
  1032. } else {
  1033. formData.id = this.form_dir_s.id
  1034. formData.parent_id = this.form_dir_s.parent_id[this.form_dir_s.parent_id.length - 1]
  1035. }
  1036. Object.assign(formData, {type: 0})
  1037. if(this.type == 'videos'){
  1038. formData.type = 1
  1039. }
  1040. this.$request({
  1041. params: {
  1042. r: 'common/file/save-group',
  1043. },
  1044. method: 'post',
  1045. data: formData,
  1046. }).then(e => {
  1047. this.groupListLoading = false;
  1048. if (e.data.code === 0) {
  1049. this.dialogFormVisible = false;
  1050. this.dialogFormVisibleEdit = false;
  1051. this.groupListLoading = true;
  1052. } else {
  1053. this.$message.error(e.data.msg);
  1054. }
  1055. this.dialogOpened();
  1056. }).catch(e => {
  1057. this.groupListLoading = false;
  1058. });
  1059. },
  1060. handleNodeClick(data) {
  1061. console.log(data);
  1062. this.uploadParams = {
  1063. attachment_group_id: data.id,
  1064. }
  1065. this.currentAttachmentGroupId = data.id;
  1066. this.form_dir_s.id = data.id;
  1067. this.form_dir_s.name = data.label;
  1068. this.select_name = data.label;
  1069. this.loading = true;
  1070. this.loadList({});
  1071. },
  1072. deleteGroups(is_recycle, type) { // 删除分组
  1073. let title;
  1074. let msg;
  1075. switch (type) {
  1076. case 1:
  1077. // title = '是否确认将选中素材放入回收站中?删除的素材可通过回收站还原';
  1078. title = '目录删除后将无法恢复,您确认要彻底删除所选目录吗?';
  1079. msg = '请选择要删除的目录';
  1080. break;
  1081. case 2:
  1082. title = '确认还原选择素材?';
  1083. msg = '请选择要还原的目录';
  1084. break;
  1085. case 3:
  1086. title = '素材删除后将无法恢复,您确认要彻底删除所选素材吗?';
  1087. msg = '请选择要删除的目录';
  1088. break;
  1089. default:
  1090. title = '';
  1091. msg = '';
  1092. break;
  1093. }
  1094. if (this.form_dir_s.id == '') {
  1095. this.$message.error(msg);
  1096. return false;
  1097. }
  1098. this.$confirm(title, '提示', {
  1099. confirmButtonText: '确定',
  1100. cancelButtonText: '取消',
  1101. type: 'warning'
  1102. }).then(() => {
  1103. this.deleteLoading = true;
  1104. this.$request({
  1105. params: {
  1106. r: 'common/file/del-group'
  1107. },
  1108. method: 'POST',
  1109. data: {
  1110. id: this.form_dir_s,
  1111. //type 1回收 2还原 3删除
  1112. // type:type,
  1113. }
  1114. }).then(e => {
  1115. this.deleteLoading = false;
  1116. if (e.data.code === 0) {
  1117. this.$message.success(e.data.msg);
  1118. this.dialogOpened();
  1119. } else {
  1120. this.$message.error(e.data.msg);
  1121. }
  1122. }).catch(e => {
  1123. this.deleteLoading = false;
  1124. });
  1125. }).catch(() => {});
  1126. },
  1127. addNode() {
  1128. this.form_dir_s = {
  1129. id: '',
  1130. parent_id: '',
  1131. name: '',
  1132. }
  1133. this.dialogFormVisible = true
  1134. },
  1135. /* 获取上传大视频配置 */
  1136. uploadConfig() {
  1137. const params = {};
  1138. params['r'] = 'mall/attachment/get-oss-config';
  1139. this.$request({
  1140. headers: {'Content-Type': 'multipart/form-data'},
  1141. params: params,
  1142. method: 'get',
  1143. }).then(e => {
  1144. if (e.data.code === 1) {
  1145. this.$message.error(e.data.msg);
  1146. }
  1147. this.videoConfigInfo = e.data.data
  1148. }).catch(e => {
  1149. console.log(e)
  1150. });
  1151. },
  1152. playVideo(item){
  1153. console.log(item.url)
  1154. this.videoPlayUrl = item.url
  1155. this.videoPlayVisible = true
  1156. },
  1157. copyLink(url) { //复制链接
  1158. var clipboard = new ClipboardJS('.copy_btn', {
  1159. text: function() {
  1160. return url;
  1161. }
  1162. });
  1163. clipboard.on('success', (e) => {
  1164. this.$message.success('复制成功!');
  1165. clipboard.destroy()
  1166. });
  1167. clipboard.on('error', (e) => {
  1168. his.$message.error('复制失败!');
  1169. clipboard.destroy()
  1170. });
  1171. }
  1172. },
  1173. })
  1174. </script>