index.vue 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408
  1. <template>
  2. <view class="uploadFile_wrap">
  3. <!-- 上传按钮 -->
  4. <view class="upload_btn_wrap" @click="handleUpload">
  5. <view class="upload_btn_content">
  6. <view class="upload_icon">
  7. <u-icon name="plus-circle" size="24" color="#fff"></u-icon>
  8. </view>
  9. <text class="upload_text">上传文件</text>
  10. </view>
  11. </view>
  12. <!-- 文件列表分类展示 -->
  13. <view class="fileList_wrap" v-if="!loading && dataList.length > 0">
  14. <view class="file_item_wrap" v-if="imgList.length > 0">
  15. <view class="item_title">图片文件:</view>
  16. <mk-upload
  17. :imgList="imgList"
  18. @onDelete="handleDelFile"
  19. :controlShow="false"
  20. type="image"
  21. @onPreviewTake="handlePreviewImage"
  22. ></mk-upload>
  23. </view>
  24. <view class="file_item_wrap" v-if="recordList.length > 0">
  25. <view class="item_title">录音文件:</view>
  26. <mk-upload
  27. :imgList="recordList"
  28. @onDelete="handleDelFile"
  29. :controlShow="false"
  30. type="record"
  31. @onPreviewTake="handlePreviewRecord"
  32. ></mk-upload>
  33. </view>
  34. <view class="file_item_wrap" v-if="videoList.length > 0">
  35. <view class="item_title">视频文件:</view>
  36. <mk-upload
  37. :imgList="videoList"
  38. @onDelete="handleDelFile"
  39. :controlShow="false"
  40. type="video"
  41. @onPreviewTake="handlePreviewVideo"
  42. ></mk-upload>
  43. </view>
  44. <view class="file_item_wrap" v-if="otherList.length > 0">
  45. <view class="item_title">其他文件:</view>
  46. <mk-upload
  47. :imgList="otherList"
  48. @onDelete="handleDelFile"
  49. :controlShow="false"
  50. type="file"
  51. @onPreviewTake="handlePreviewFile"
  52. ></mk-upload>
  53. </view>
  54. </view>
  55. <!-- 加载状态 -->
  56. <view v-if="loading" class="loading_wrap">
  57. <u-loading-icon text="加载中" textSize="18"></u-loading-icon>
  58. </view>
  59. <!-- 空状态 -->
  60. <view v-else-if="dataList.length === 0" class="empty_wrap">
  61. <u-empty text="暂无文件数据"></u-empty>
  62. </view>
  63. <!-- 图片预览 -->
  64. <u-image
  65. ref="previewImg"
  66. :src="previewImageSrc"
  67. mode="widthFix"
  68. v-if="previewImageSrc"
  69. @click="previewImageSrc = ''"
  70. ></u-image>
  71. <!-- 文件预览模态框 -->
  72. <u-modal :show="showFileModal" :title="currentFile.fileName" @confirm="handleFileConfirm">
  73. <view v-if="currentFile && currentFile.fileUrl" class="currentFile_wrap">
  74. <audio
  75. style="text-align: left"
  76. :src="currentFile.fileUrl"
  77. :name="'点击播放'"
  78. controls
  79. v-if="['wav','mp3','aac','wma'].includes(currentFile.fileSuffix)"
  80. ></audio>
  81. <video
  82. :src="currentFile.fileUrl"
  83. mode=""
  84. class="currentFile_image"
  85. v-else-if="showFileModal"
  86. show-fullscreen-btn
  87. ></video>
  88. </view>
  89. <view v-else>
  90. 文件异常
  91. </view>
  92. </u-modal>
  93. <!-- 编辑对话框 -->
  94. <u-popup v-model="dialogVisible" mode="center" width="500rpx" border-radius="16">
  95. <view class="dialog_header">{{ dialogTitle }}</view>
  96. <view class="dialog_body">
  97. <u-form :model="fileForm" ref="fileFormRef" label-width="100px">
  98. <u-form-item label="文件名称" prop="fileName">
  99. <u-input v-model="fileForm.fileName" placeholder="请输入文件名称" />
  100. </u-form-item>
  101. <u-form-item label="备注" prop="remark">
  102. <u-input v-model="fileForm.remark" type="textarea" placeholder="请输入备注信息" />
  103. </u-form-item>
  104. </u-form>
  105. </view>
  106. <view class="dialog_footer">
  107. <u-button @click="handleClose">取消</u-button>
  108. <u-button type="primary" @click="handleEditSubmit">确定</u-button>
  109. </view>
  110. </u-popup>
  111. </view>
  112. </template>
  113. <script>
  114. import mkUpload from "@/components/mk-upload/mk-upload.vue"
  115. import upload from '@/mixins/upload';
  116. export default {
  117. name: 'UploadFile',
  118. mixins : [upload],
  119. components: {
  120. mkUpload
  121. },
  122. props: {
  123. clueId: { type: [String, Number], required: true },
  124. sourceId: { type: [String, Number], default: '' },
  125. type: { type: String, required: true },
  126. orderFileType: { type: String, default: '' },
  127. isDuplicate: { type: String, default: '2' }
  128. },
  129. computed: {
  130. imgList() {
  131. return this.dataList.filter(v => ['png','gif','bmp','jpg','jpeg','webp'].includes((v.fileSuffix || '').toLowerCase()));
  132. },
  133. recordList() {
  134. return this.dataList.filter(v => ['wav','mp3','aac','wma'].includes((v.fileSuffix || '').toLowerCase()));
  135. },
  136. videoList() {
  137. return this.dataList.filter(v => ['avi','mkv','mov','wmv','mp4'].includes((v.fileSuffix || '').toLowerCase()));
  138. },
  139. otherList() {
  140. const list = this.imgList.concat(this.recordList).concat(this.videoList);
  141. return this.dataList.filter(v => !list.some(j => j.id === v.id));
  142. }
  143. },
  144. data() {
  145. return {
  146. loading: false,
  147. dataList: [],
  148. previewImageSrc: '',
  149. showFileModal: false,
  150. currentFile: {},
  151. // 编辑对话框相关
  152. dialogVisible: false,
  153. dialogTitle: '编辑文件',
  154. fileForm: {
  155. id: null,
  156. fileName: '',
  157. remark: ''
  158. }
  159. }
  160. },
  161. methods: {
  162. async handleUploadSuccess(){
  163. await uni.$u.api.saveClueFile({
  164. clueId : this.clueId,
  165. list : this.uploadList,
  166. sourceId: this.sourceId,
  167. type : this.type,
  168. orderFileType : this.orderFileType
  169. });
  170. uni.$u.toast("上传成功");
  171. // 清空
  172. this.uploadList = [];
  173. this.getList();
  174. },
  175. // 获取文件列表
  176. async getList() {
  177. // 检查必要参数是否存在
  178. if (!this.clueId) {
  179. return;
  180. }
  181. this.loading = true
  182. try {
  183. const params = {
  184. clueId: this.clueId,
  185. sourceId: this.sourceId,
  186. type: this.type,
  187. orderFileType: this.orderFileType,
  188. isDuplicate: this.isDuplicate,
  189. pageNum: 1,
  190. pageSize: 1000 // 设置一个较大的值以获取所有数据
  191. }
  192. const response = await uni.$u.api.selectClueFileByDto(params)
  193. this.dataList = response.rows || []
  194. } catch (error) {
  195. uni.$u.toast(`获取列表失败:${error.message}`)
  196. this.dataList = []
  197. } finally {
  198. this.loading = false
  199. }
  200. },
  201. // 删除文件
  202. async handleDelFile(item) {
  203. try {
  204. await uni.$u.api.deleteClueFile([item.id])
  205. uni.$u.toast('删除成功')
  206. this.getList()
  207. } catch (error) {
  208. uni.$u.toast(`删除失败:${error.message}`)
  209. }
  210. },
  211. // 预览图片
  212. handlePreviewImage(item) {
  213. const imgList = this.imgList.map(v => v.fileUrl);
  214. const currentIndex = imgList.findIndex(url => url === item.fileUrl);
  215. uni.previewImage({
  216. current: currentIndex >= 0 ? currentIndex : 0,
  217. urls: imgList
  218. });
  219. },
  220. // 预览录音
  221. handlePreviewRecord(item) {
  222. this.currentFile = item;
  223. this.showFileModal = true;
  224. },
  225. // 预览视频
  226. handlePreviewVideo(item) {
  227. this.currentFile = item;
  228. this.showFileModal = true;
  229. },
  230. // 预览其他文件
  231. handlePreviewFile(item) {
  232. uni.$u.toast("其他文件不支持预览,可前往网页端查看");
  233. },
  234. // 关闭文件预览
  235. handleFileConfirm() {
  236. this.showFileModal = false;
  237. },
  238. // 编辑文件
  239. handleEdit(row) {
  240. this.dialogTitle = '编辑文件'
  241. this.fileForm = {
  242. id: row.id,
  243. fileName: row.fileName,
  244. remark: row.remark
  245. }
  246. this.dialogVisible = true
  247. },
  248. // 提交编辑表单
  249. async handleEditSubmit() {
  250. try {
  251. await uni.$u.api.updateClueFile(this.fileForm)
  252. uni.$u.toast('编辑成功')
  253. this.dialogVisible = false
  254. this.getList()
  255. } catch (error) {
  256. uni.$u.toast(`编辑失败:${error.message}`)
  257. }
  258. },
  259. // 关闭对话框
  260. handleClose() {
  261. this.dialogVisible = false
  262. this.fileForm = {
  263. id: null,
  264. fileName: '',
  265. remark: ''
  266. }
  267. }
  268. },
  269. created() {
  270. this.getList();
  271. }
  272. }
  273. </script>
  274. <style lang="scss" scoped>
  275. .uploadFile_wrap {
  276. padding: 0 20px 20px;
  277. }
  278. .upload_btn_wrap {
  279. margin-top: 10px;
  280. margin-bottom: 10px;
  281. background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  282. border-radius: 12px;
  283. padding: 16px 20px;
  284. box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
  285. transition: all 0.3s ease;
  286. position: relative;
  287. overflow: hidden;
  288. }
  289. .upload_btn_wrap::before {
  290. content: '';
  291. position: absolute;
  292. top: 0;
  293. left: -100%;
  294. width: 100%;
  295. height: 100%;
  296. background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
  297. transition: left 0.5s;
  298. }
  299. .upload_btn_wrap:active::before {
  300. left: 100%;
  301. }
  302. .upload_btn_wrap:active {
  303. transform: scale(0.98);
  304. box-shadow: 0 2px 8px rgba(102, 126, 234, 0.4);
  305. }
  306. .upload_btn_content {
  307. display: flex;
  308. align-items: center;
  309. justify-content: center;
  310. gap: 8px;
  311. position: relative;
  312. z-index: 1;
  313. }
  314. .upload_icon {
  315. display: flex;
  316. align-items: center;
  317. justify-content: center;
  318. width: 32px;
  319. height: 32px;
  320. background: rgba(255, 255, 255, 0.2);
  321. border-radius: 50%;
  322. backdrop-filter: blur(10px);
  323. }
  324. .upload_text {
  325. color: #fff;
  326. font-size: 16px;
  327. font-weight: 600;
  328. letter-spacing: 0.5px;
  329. }
  330. .fileList_wrap {
  331. padding: 0;
  332. color: #666666;
  333. font-size: 24rpx;
  334. .file_item_wrap {
  335. margin-bottom: 20px;
  336. .item_title {
  337. margin-bottom: 10px;
  338. font-weight: bold;
  339. font-size: 16px;
  340. color: #202020;
  341. }
  342. }
  343. }
  344. .currentFile_wrap {
  345. .file_audio {
  346. height: 60px;
  347. width: 100%;
  348. }
  349. .currentFile_image {
  350. height: 42vh;
  351. object-fit: contain;
  352. }
  353. }
  354. .loading_wrap,
  355. .empty_wrap {
  356. padding: 40px 20px;
  357. text-align: center;
  358. }
  359. .dialog_header {
  360. padding: 20px;
  361. font-size: 16px;
  362. font-weight: bold;
  363. text-align: center;
  364. border-bottom: 1px solid #e9ecef;
  365. }
  366. .dialog_body {
  367. padding: 20px;
  368. }
  369. .dialog_footer {
  370. padding: 20px;
  371. display: flex;
  372. justify-content: space-around;
  373. border-top: 1px solid #e9ecef;
  374. }
  375. </style>