|
|
@@ -0,0 +1,462 @@
|
|
|
+<template>
|
|
|
+ <div class="mobile-file-center">
|
|
|
+ <div class="mobile-top-bar">
|
|
|
+ <div class="search-row">
|
|
|
+ <el-input
|
|
|
+ v-model="inputData"
|
|
|
+ placeholder="搜索我的文件"
|
|
|
+ prefix-icon="el-icon-search"
|
|
|
+ size="small"
|
|
|
+ class="mobile-search-input"
|
|
|
+ clearable
|
|
|
+ @keyup.enter.native="onSearchFile"
|
|
|
+ />
|
|
|
+ <i class="el-icon-folder-add quick-action-icon" @click="onCreateFolder" />
|
|
|
+ <i class="el-icon-upload2 quick-action-icon" @click="onClickUpload" />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="mobile-breadcrumb-wrapper">
|
|
|
+ <div class="breadcrumb-inner-scroll">
|
|
|
+ <span
|
|
|
+ v-for="(item, index) in pathList"
|
|
|
+ :key="index"
|
|
|
+ class="crumb-item"
|
|
|
+ @click="jumpToPath(item.path)"
|
|
|
+ >
|
|
|
+ {{ item.name }}
|
|
|
+ <i v-if="index < pathList.length - 1" class="el-icon-arrow-right arrow" />
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div
|
|
|
+ v-infinite-scroll="loadMore"
|
|
|
+ class="mobile-list-wrapper"
|
|
|
+ :infinite-scroll-disabled="infiniteDisabled"
|
|
|
+ infinite-scroll-distance="30"
|
|
|
+ >
|
|
|
+ <div class="file-list">
|
|
|
+ <div
|
|
|
+ v-for="item in dataList"
|
|
|
+ :key="item.fileId"
|
|
|
+ class="mobile-file-row"
|
|
|
+ :class="{ 'is-checked': isSelected(item) }"
|
|
|
+ @click="onRowClick(item)"
|
|
|
+ >
|
|
|
+ <div class="row-left-checkbox" @click.stop="toggleItemSelection(item)">
|
|
|
+ <div class="custom-checkbox" :class="{ 'checked': isSelected(item) }">
|
|
|
+ <i v-if="isSelected(item)" class="el-icon-check" />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="row-middle-content">
|
|
|
+ <div class="file-icon-box">
|
|
|
+ <i :class="getFileIcon(item.fileType)" :style="getIconStyle(item.fileType)" />
|
|
|
+ </div>
|
|
|
+ <div class="file-info-text">
|
|
|
+ <span class="filename-title">{{ item.filename }}</span>
|
|
|
+ <div class="file-meta-sub">
|
|
|
+ <span class="m-size">{{ item.size || '-' }}</span>
|
|
|
+ <span class="m-dot">•</span>
|
|
|
+ <span class="m-time">{{ formatSimpleTime(item.updateTime) }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="row-right-more" @click.stop="showSingleActionMenu(item)">
|
|
|
+ <i class="el-icon-more" />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="load-status">
|
|
|
+ <p v-if="loading"><i class="el-icon-loading" /> 载入中...</p>
|
|
|
+ <p v-if="noMore && dataList.length > 0">已加载全部文件</p>
|
|
|
+ <el-empty v-if="dataList.length === 0 && !loading" :image-size="80" description="空空如也" />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <transition name="slide-up">
|
|
|
+ <div v-if="selectedTable.length > 0" class="mobile-batch-bar">
|
|
|
+ <span class="batch-count">已选 {{ selectedTable.length }} 项</span>
|
|
|
+ <div class="batch-buttons">
|
|
|
+ <div class="b-btn" @click="addToAlbum"><i class="el-icon-collection" /><span>存合集</span></div>
|
|
|
+ <div class="b-btn" @click="moveToFolder"><i class="el-icon-rank" /><span>移动</span></div>
|
|
|
+ <div class="b-btn text-danger" @click="onDeleteFile"><i class="el-icon-delete" /><span>删除</span></div>
|
|
|
+ <div class="b-btn text-muted" @click="clearSelections"><span>取消</span></div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </transition>
|
|
|
+
|
|
|
+ <el-dialog
|
|
|
+ :visible.sync="showPreviewDialog"
|
|
|
+ :before-close="handlePreviewClose"
|
|
|
+ width="100%"
|
|
|
+ fullscreen
|
|
|
+ custom-class="mobile-fullscreen-preview"
|
|
|
+ append-to-body
|
|
|
+ >
|
|
|
+ <div slot="title" class="mobile-preview-header">
|
|
|
+ <i class="el-icon-arrow-left back-icon" @click="handlePreviewClose" />
|
|
|
+ <span class="p-title">{{ fileDetail ? fileDetail.filename : '预览' }}</span>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div v-if="fileDetail" class="mobile-preview-body">
|
|
|
+ <template v-if="fileType === 1001">
|
|
|
+ <el-image :src="fileDetail.url" fit="contain" class="m-img" :preview-src-list="[fileDetail.url]" />
|
|
|
+ </template>
|
|
|
+ <template v-else-if="fileType === 1002">
|
|
|
+ <video ref="videoPlayer" :src="fileDetail.url" class="m-video" controls autoplay playsinline />
|
|
|
+ </template>
|
|
|
+ <template v-else-if="fileType === 1003">
|
|
|
+ <div class="m-audio-box">
|
|
|
+ <i class="el-icon-headset" />
|
|
|
+ <audio :src="fileDetail.url" controls autoplay style="width:90%" />
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ <template v-else-if="fileType === 1006">
|
|
|
+ <iframe :src="getPdfUrl(fileDetail.url)" width="100%" height="100%" frameborder="0" />
|
|
|
+ </template>
|
|
|
+ <template v-else>
|
|
|
+ <el-empty description="无法直接在手机查看">
|
|
|
+ <el-button type="primary" size="small" round @click="handleDownload(fileDetail)">下载到本地</el-button>
|
|
|
+ </el-empty>
|
|
|
+ </template>
|
|
|
+ </div>
|
|
|
+ </el-dialog>
|
|
|
+
|
|
|
+ <el-dialog title="新建文件夹" :visible.sync="showCreateFolderDialog" width="85%" custom-class="mobile-center-dialog" append-to-body>
|
|
|
+ <el-form ref="folderForm" :model="createFolderForm" @submit.native.prevent="createFolder">
|
|
|
+ <el-input v-model="createFolderForm.folderName" placeholder="文件夹名称" clearable />
|
|
|
+ </el-form>
|
|
|
+ <div slot="footer" class="m-dialog-footer">
|
|
|
+ <el-button size="small" @click="showCreateFolderDialog = false">取消</el-button>
|
|
|
+ <el-button size="small" type="primary" :loading="loading" @click="createFolder">确定</el-button>
|
|
|
+ </div>
|
|
|
+ </el-dialog>
|
|
|
+
|
|
|
+ <el-dialog title="上传文件" :visible.sync="showUploadDialog" width="90%" custom-class="mobile-center-dialog" append-to-body>
|
|
|
+ <uploader v-if="options" :options="options" :auto-start="true" @file-added="onFileAdded" @file-success="onFileSuccess" @complete="onUploadComplete">
|
|
|
+ <uploader-drop class="m-upload-drop">
|
|
|
+ <i class="el-icon-cloudy" />
|
|
|
+ <p><uploader-btn :attrs="attrs" class="m-select-link">点击选择手机文件上传</uploader-btn></p>
|
|
|
+ </uploader-drop>
|
|
|
+ <uploader-list class="m-upload-list" />
|
|
|
+ </uploader>
|
|
|
+ </el-dialog>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+import { addFile, createFolder, deleteFile, getDiskChannelInfo, getDiskFile, getFileDetail } from '@/api/disk'
|
|
|
+import { hashFile } from '@/utils/functions'
|
|
|
+
|
|
|
+export default {
|
|
|
+ name: 'DiskFileMobile',
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ dataList: [],
|
|
|
+ selectedTable: [], // 存储选中的行对象
|
|
|
+ pathList: [],
|
|
|
+ inputData: '',
|
|
|
+ showPreviewDialog: false,
|
|
|
+ fileDetail: null,
|
|
|
+ fileType: 0,
|
|
|
+ currentPid: '0',
|
|
|
+ loading: false,
|
|
|
+ noMore: false,
|
|
|
+ queryForm: { pn: 1, path: '/', fileType: null },
|
|
|
+ createFolderForm: { pid: '0', folderName: '' },
|
|
|
+ showCreateFolderDialog: false,
|
|
|
+ showUploadDialog: false,
|
|
|
+ options: null,
|
|
|
+ attrs: { accept: '*' },
|
|
|
+ uploadForm: { channelCode: null, pid: null, uploadId: null }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ computed: {
|
|
|
+ infiniteDisabled() { return this.loading || this.noMore }
|
|
|
+ },
|
|
|
+ created() {
|
|
|
+ if (this.$route.query.path) this.queryForm.path = this.$route.query.path
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ loadMore() {
|
|
|
+ if (this.infiniteDisabled) return
|
|
|
+ this.loading = true
|
|
|
+ getDiskFile(this.queryForm).then(resp => {
|
|
|
+ if (resp.code === 0) {
|
|
|
+ const { list, hasNext } = resp.data.pageList
|
|
|
+ this.dataList = [...this.dataList, ...list]
|
|
|
+ if (this.queryForm.pn === 1) this.updatePathInfo(resp.data)
|
|
|
+ if (!hasNext) this.noMore = true; else this.queryForm.pn++
|
|
|
+ } else {
|
|
|
+ this.noMore = true
|
|
|
+ this.$message.error(resp.msg)
|
|
|
+ }
|
|
|
+ }).finally(() => { this.loading = false })
|
|
|
+ },
|
|
|
+ updatePathInfo(respData) {
|
|
|
+ this.currentPid = respData.currentPid
|
|
|
+ const { namePathList } = respData
|
|
|
+ this.pathList = []
|
|
|
+ if (!namePathList || namePathList.length === 0) {
|
|
|
+ this.pathList.push({ path: '/', name: '全部文件' })
|
|
|
+ } else {
|
|
|
+ namePathList.forEach(item => {
|
|
|
+ this.pathList.push({ path: item.path, name: item.filename })
|
|
|
+ })
|
|
|
+ }
|
|
|
+ },
|
|
|
+ changeDirectory(newPath) {
|
|
|
+ this.queryForm.path = newPath
|
|
|
+ this.queryForm.pn = 1
|
|
|
+ this.dataList = []
|
|
|
+ this.noMore = false
|
|
|
+ this.loadMore()
|
|
|
+ },
|
|
|
+ jumpToPath(pathStr) {
|
|
|
+ this.$router.push({ query: { path: pathStr }})
|
|
|
+ this.changeDirectory(pathStr)
|
|
|
+ },
|
|
|
+ onRowClick(item) {
|
|
|
+ if (this.selectedTable.length > 0) {
|
|
|
+ this.toggleItemSelection(item)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ this.fileType = item.fileType
|
|
|
+ if (this.fileType === 1000) {
|
|
|
+ const newPath = this.queryForm.path === '/' ? `/${item.filename}` : `${this.queryForm.path}/${item.filename}`
|
|
|
+ this.jumpToPath(newPath)
|
|
|
+ } else {
|
|
|
+ getFileDetail(item.fileId).then(resp => {
|
|
|
+ if (resp.code === 0) {
|
|
|
+ this.fileDetail = resp.data
|
|
|
+ this.showPreviewDialog = true
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+ },
|
|
|
+ isSelected(item) {
|
|
|
+ return this.selectedTable.some(i => i.fileId === item.fileId)
|
|
|
+ },
|
|
|
+ toggleItemSelection(item) {
|
|
|
+ const idx = this.selectedTable.findIndex(i => i.fileId === item.fileId)
|
|
|
+ if (idx > -1) this.selectedTable.splice(idx, 1); else this.selectedTable.push(item)
|
|
|
+ },
|
|
|
+ clearSelections() { this.selectedTable = [] },
|
|
|
+ formatSimpleTime(timeStr) {
|
|
|
+ if (!timeStr) return ''
|
|
|
+ return timeStr.split(' ')[0].substring(5) // 精简只显示 "MM-DD"
|
|
|
+ },
|
|
|
+ getFileIcon(type) {
|
|
|
+ const iconMap = { 1000: 'el-icon-folder', 1001: 'el-icon-picture', 1002: 'el-icon-video-camera', 1003: 'el-icon-headset', 1004: 'el-icon-document' }
|
|
|
+ return iconMap[type] || 'el-icon-files'
|
|
|
+ },
|
|
|
+ getIconStyle(type) {
|
|
|
+ const colorMap = { 1000: '#f59e0b', 1001: '#10b981', 1002: '#ef4444', 1003: '#8b5cf6' }
|
|
|
+ return { color: colorMap[type] || '#64748b', fontSize: '24px' }
|
|
|
+ },
|
|
|
+ getPdfUrl(url) { return '/pdfjs/web/viewer.html?file=' + encodeURIComponent(url) },
|
|
|
+ handlePreviewClose() {
|
|
|
+ if (this.$refs.videoPlayer) this.$refs.videoPlayer.pause()
|
|
|
+ this.showPreviewDialog = false
|
|
|
+ this.fileDetail = null
|
|
|
+ },
|
|
|
+ onCreateFolder() {
|
|
|
+ this.createFolderForm.folderName = ''
|
|
|
+ this.showCreateFolderDialog = true
|
|
|
+ },
|
|
|
+ createFolder() {
|
|
|
+ if(!this.createFolderForm.folderName.trim()) return
|
|
|
+ this.createFolderForm.pid = this.currentPid
|
|
|
+ this.loading = true
|
|
|
+ createFolder(this.createFolderForm).then(resp => {
|
|
|
+ if (resp.code === 0) {
|
|
|
+ this.showCreateFolderDialog = false
|
|
|
+ this.changeDirectory(this.queryForm.path)
|
|
|
+ }
|
|
|
+ }).finally(() => { this.loading = false })
|
|
|
+ },
|
|
|
+ onDeleteFile() {
|
|
|
+ this.$confirm(`确定删除选中的 ${this.selectedTable.length} 个文件吗?`, '提示', { type: 'warning', width: '80%' }).then(() => {
|
|
|
+ const fileIds = this.selectedTable.map(i => i.fileId)
|
|
|
+ deleteFile({ fileIds }).then(resp => {
|
|
|
+ if (resp.code === 0) {
|
|
|
+ this.$message.success('删除成功')
|
|
|
+ this.clearSelections()
|
|
|
+ this.changeDirectory(this.queryForm.path) // 局部重载目录,绝不触发全页刷新 router.go
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }).catch(() => {})
|
|
|
+ },
|
|
|
+ // ***************** Simple Uploader 移动桥接 *****************
|
|
|
+ onClickUpload() {
|
|
|
+ this.uploadForm.pid = this.currentPid
|
|
|
+ getDiskChannelInfo().then(resp => {
|
|
|
+ if (resp.code === 0) {
|
|
|
+ this.uploadForm.channelCode = resp.data.channelCode
|
|
|
+ this.options = {
|
|
|
+ target: resp.data.ossUrl,
|
|
|
+ chunkSize: 1024 * 1024 * 5, // 手机端改为 5M 切片更稳定
|
|
|
+ maxChunkRetries: 2,
|
|
|
+ fileParameterName: 'file',
|
|
|
+ testChunks: true,
|
|
|
+ checkChunkUploadedByResponse: (chunk, msg) => {
|
|
|
+ const res = JSON.parse(msg).data
|
|
|
+ return res.skipUpload || (res.uploaded || []).indexOf(chunk.offset + 1) >= 0
|
|
|
+ },
|
|
|
+ query: () => ({ channelCode: resp.data.channelCode, multiparts: '' }),
|
|
|
+ headers: { Authorization: 'Bearer ' + resp.data.token }
|
|
|
+ }
|
|
|
+ this.showUploadDialog = true
|
|
|
+ }
|
|
|
+ })
|
|
|
+ },
|
|
|
+ onFileAdded(file) {
|
|
|
+ file.pause()
|
|
|
+ hashFile(file.file).then(res => { file.uniqueIdentifier = res.sha256sum; file.resume() })
|
|
|
+ },
|
|
|
+ onFileSuccess(rootFile, file, response) {
|
|
|
+ const resp = JSON.parse(response)
|
|
|
+ if (resp.code === 0) {
|
|
|
+ this.uploadForm.uploadId = resp.data.uploadId
|
|
|
+ addFile(this.uploadForm)
|
|
|
+ }
|
|
|
+ },
|
|
|
+ onUploadComplete() {
|
|
|
+ this.$message.success('上传处理完成')
|
|
|
+ setTimeout(() => { this.showUploadDialog = false; this.changeDirectory(this.queryForm.path) }, 800)
|
|
|
+ },
|
|
|
+ handleDownload(file) { if(file && file.url) window.open(file.url, '_blank') },
|
|
|
+ onSearchFile() { this.$message.info('搜索触发') },
|
|
|
+ addToAlbum() {}, moveToFolder() {} // 后续逻辑类似
|
|
|
+ }
|
|
|
+}
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+.mobile-file-center {
|
|
|
+ background-color: #f6f8fa;
|
|
|
+ min-height: 100vh;
|
|
|
+}
|
|
|
+
|
|
|
+/* 顶部固定栏 */
|
|
|
+.mobile-top-bar {
|
|
|
+ position: sticky;
|
|
|
+ top: 0;
|
|
|
+ background: #fff;
|
|
|
+ padding: 10px 14px 4px 14px;
|
|
|
+ z-index: 90;
|
|
|
+ border-bottom: 1px solid #edf2f7;
|
|
|
+}
|
|
|
+.search-row { display: flex; align-items: center; gap: 12px; }
|
|
|
+.mobile-search-input { flex: 1; }
|
|
|
+::v-deep .mobile-search-input .el-input__inner { border-radius: 18px; background: #f1f5f9; border: none; }
|
|
|
+
|
|
|
+.quick-action-icon { font-size: 22px; color: #1a73e8; padding: 4px; }
|
|
|
+
|
|
|
+/* 核心面包屑优化:打通横向单行滑动 */
|
|
|
+.mobile-breadcrumb-wrapper {
|
|
|
+ margin-top: 8px;
|
|
|
+ overflow-x: auto;
|
|
|
+ white-space: nowrap;
|
|
|
+ padding-bottom: 4px;
|
|
|
+ -webkit-overflow-scrolling: touch;
|
|
|
+}
|
|
|
+.mobile-breadcrumb-wrapper::-webkit-scrollbar { display: none; } /* 藏起滚动条 */
|
|
|
+
|
|
|
+.breadcrumb-inner-scroll { display: flex; align-items: center; gap: 6px; }
|
|
|
+.crumb-item { font-size: 13px; color: #475569; display: flex; align-items: center; }
|
|
|
+.crumb-item:last-child { color: #1a73e8; font-weight: bold; }
|
|
|
+.crumb-item .arrow { font-size: 11px; margin-left: 6px; color: #94a3b8; }
|
|
|
+
|
|
|
+/* 触屏大列表流布局 */
|
|
|
+.mobile-list-wrapper { padding: 8px 12px 100px 12px; }
|
|
|
+.file-list { display: flex; flex-direction: column; gap: 6px; }
|
|
|
+
|
|
|
+.mobile-file-row {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ background: #fff;
|
|
|
+ padding: 12px 10px;
|
|
|
+ border-radius: 12px;
|
|
|
+ box-shadow: 0 2px 6px rgba(0,0,0,0.01);
|
|
|
+ transition: background 0.15s;
|
|
|
+ -webkit-tap-highlight-color: transparent;
|
|
|
+}
|
|
|
+.mobile-file-row:active { background: #f1f5f9; }
|
|
|
+.mobile-file-row.is-checked { background: #f0f7ff; border: 1px solid #bfdbfe; }
|
|
|
+
|
|
|
+/* 独立设计的加大选框热区 */
|
|
|
+.row-left-checkbox { padding: 8px 10px 8px 4px; }
|
|
|
+.custom-checkbox {
|
|
|
+ width: 18px;
|
|
|
+ height: 18px;
|
|
|
+ border-radius: 50%;
|
|
|
+ border: 1.5px solid #cbd5e1;
|
|
|
+ display: flex;
|
|
|
+ justify-content: center;
|
|
|
+ align-items: center;
|
|
|
+}
|
|
|
+.custom-checkbox.checked { background: #1a73e8; border-color: #1a73e8; }
|
|
|
+.custom-checkbox i { color: #fff; font-size: 11px; font-weight: bold; }
|
|
|
+
|
|
|
+.row-middle-content { flex: 1; display: flex; align-items: center; gap: 12px; overflow: hidden; }
|
|
|
+.file-info-text { display: flex; flex-direction: column; flex: 1; overflow: hidden; }
|
|
|
+.filename-title { font-size: 14px; font-weight: 600; color: #1e293b; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
|
+
|
|
|
+.file-meta-sub { font-size: 11px; color: #94a3b8; margin-top: 2px; display: flex; align-items: center; gap: 4px; }
|
|
|
+
|
|
|
+.row-right-more { padding: 8px 6px; color: #94a3b8; font-size: 16px; }
|
|
|
+
|
|
|
+/* 批量底部面板吸底 */
|
|
|
+.mobile-batch-bar {
|
|
|
+ position: fixed;
|
|
|
+ bottom: 0;
|
|
|
+ left: 0;
|
|
|
+ width: 100%;
|
|
|
+ background: #1e293b;
|
|
|
+ color: #fff;
|
|
|
+ padding: 12px 16px env(safe-area-inset-bottom) 16px;
|
|
|
+ box-sizing: border-box;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 10px;
|
|
|
+ z-index: 100;
|
|
|
+ border-top-left-radius: 16px;
|
|
|
+ border-top-right-radius: 16px;
|
|
|
+}
|
|
|
+.batch-count { font-size: 12px; color: #94a3b8; text-align: center; }
|
|
|
+.batch-buttons { display: flex; justify-content: space-around; align-items: center; }
|
|
|
+.b-btn { display: flex; flex-direction: column; align-items: center; font-size: 11px; gap: 4px; color: #f1f5f9; cursor: pointer; }
|
|
|
+.b-btn i { font-size: 20px; }
|
|
|
+.text-danger { color: #f87171; }
|
|
|
+.text-muted { color: #94a3b8; }
|
|
|
+
|
|
|
+/* 全屏预览层重组 */
|
|
|
+::v-deep .mobile-fullscreen-preview { background: #000 !important; margin:0!important; }
|
|
|
+::v-deep .mobile-fullscreen-preview .el-dialog__header { padding: 0; }
|
|
|
+::v-deep .mobile-fullscreen-preview .el-dialog__body { padding: 0; height: calc(100vh - 50px); background: #000; }
|
|
|
+
|
|
|
+.mobile-preview-header { height: 50px; background: #111; display: flex; align-items: center; padding: 0 16px; color: #fff; gap: 14px; }
|
|
|
+.mobile-preview-header .back-icon { font-size: 20px; }
|
|
|
+.mobile-preview-header .p-title { font-size: 14px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
|
+
|
|
|
+.mobile-preview-body { width: 100%; height: 100%; display: flex; justify-content: center; align-items: center; }
|
|
|
+.m-img { max-width: 100%; max-height: 90vh; }
|
|
|
+.m-video { width: 100%; max-height: 80vh; }
|
|
|
+.m-audio-box { text-align: center; color: #1a73e8; font-size: 48px; width: 100%; }
|
|
|
+
|
|
|
+/* 弹窗小改 */
|
|
|
+::v-deep .mobile-center-dialog { border-radius: 14px !important; }
|
|
|
+.m-upload-drop { border: 2px dashed #cbd5e1; padding: 30px 10px; text-align: center; background: #f8fafc; border-radius: 10px; }
|
|
|
+.m-select-link { color: #1a73e8; font-weight: bold; text-decoration: underline; background: none; border: none; }
|
|
|
+.m-upload-list { max-height: 180px; overflow-y: auto; margin-top: 10px; }
|
|
|
+.m-dialog-footer { display: flex; justify-content: flex-end; gap: 10px; }
|
|
|
+.load-status { text-align: center; padding: 16px 0; font-size: 12px; color: #94a3b8; }
|
|
|
+
|
|
|
+.slide-up-enter-active, .slide-up-leave-active { transition: transform 0.25s ease; }
|
|
|
+.slide-up-enter, .slide-up-leave-to { transform: translateY(100%); }
|
|
|
+</style>
|