index.vue 9.0 KB

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