Merge branch '0.8.X' of http://49.232.154.205:10100/DeXun/klp-oa into 0.8.X
This commit is contained in:
@@ -1,98 +1,78 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<!-- 左右分栏布局 -->
|
||||
<div class="file-layout">
|
||||
<!-- 左侧边栏 -->
|
||||
<div class="file-sidebar">
|
||||
<!-- 统计卡片 -->
|
||||
<el-card class="summary-card" shadow="hover" :body-style="{ padding: '10px' }">
|
||||
<el-row :gutter="6">
|
||||
<el-col :span="12">
|
||||
<div class="summary-item">
|
||||
<span class="item-label">文件总数</span>
|
||||
<span class="item-value">{{ stats.totalFiles }}</span>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<div class="summary-item">
|
||||
<span class="item-label">公开文件</span>
|
||||
<span class="item-value">{{ stats.publicFiles }}</span>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="6" style="margin-top: 4px;">
|
||||
<el-col :span="12">
|
||||
<div class="summary-item">
|
||||
<span class="item-label">私有文件</span>
|
||||
<span class="item-value">{{ stats.privateFiles }}</span>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<div class="summary-item">
|
||||
<span class="item-label">我上传的</span>
|
||||
<span class="item-value">{{ stats.myFiles }}</span>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<!-- 顶部:统计卡片 + 工具栏 + 筛选标签 -->
|
||||
<div class="page-header">
|
||||
<div class="stats-area">
|
||||
<el-card class="stats-card" shadow="hover" :body-style="{ padding: '0' }">
|
||||
<div class="stats-grid">
|
||||
<div class="stats-item">
|
||||
<span class="stats-label">文件总数</span>
|
||||
<span class="stats-value">{{ stats.totalFiles }}</span>
|
||||
</div>
|
||||
<div class="stats-item">
|
||||
<span class="stats-label">公开文件</span>
|
||||
<span class="stats-value">{{ stats.publicFiles }}</span>
|
||||
</div>
|
||||
<div class="stats-item">
|
||||
<span class="stats-label">私有文件</span>
|
||||
<span class="stats-value">{{ stats.privateFiles }}</span>
|
||||
</div>
|
||||
<div class="stats-item">
|
||||
<span class="stats-label">我上传的</span>
|
||||
<span class="stats-value">{{ stats.myFiles }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
|
||||
<!-- 搜索框 -->
|
||||
<div class="sidebar-section">
|
||||
<div class="header-main">
|
||||
<div class="filter-row">
|
||||
<div class="type-tags">
|
||||
<span
|
||||
v-for="opt in fileTypeOptions"
|
||||
:key="opt.value"
|
||||
class="type-tag"
|
||||
:class="{ active: (queryParams.fileType || '') === opt.value }"
|
||||
@click="handleTypeTabClick(opt.value)"
|
||||
>{{ opt.label }}</span>
|
||||
</div>
|
||||
<div class="filter-extra">
|
||||
<el-select v-model="queryParams.scopeType" placeholder="可见范围" clearable size="mini" @change="handleQuery" style="width:110px;">
|
||||
<el-option label="公开" :value="1" />
|
||||
<el-option label="私有" :value="2" />
|
||||
</el-select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="toolbar-row">
|
||||
<el-input
|
||||
v-model="queryParams.fileName"
|
||||
prefix-icon="el-icon-search"
|
||||
placeholder="按名称搜索"
|
||||
clearable
|
||||
size="mini"
|
||||
style="width:200px;"
|
||||
@change="handleQuery"
|
||||
@clear="handleQuery"
|
||||
></el-input>
|
||||
</div>
|
||||
|
||||
<!-- 文件类型列表 -->
|
||||
<div class="sidebar-section sidebar-file-types">
|
||||
<div class="sidebar-label">文件类型</div>
|
||||
<div class="file-type-list">
|
||||
<div
|
||||
v-for="opt in fileTypeOptions"
|
||||
:key="opt.value"
|
||||
class="type-list-item"
|
||||
:class="{ active: (queryParams.fileType || '') === opt.value }"
|
||||
@click="handleTypeTabClick(opt.value)"
|
||||
>{{ opt.label }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- 右侧主区域 -->
|
||||
<div class="file-main">
|
||||
<!-- 高级搜索 + 操作按钮 -->
|
||||
<div class="main-toolbar">
|
||||
<el-button icon="el-icon-sort" size="mini" @click="toggleQuery">高级搜索</el-button>
|
||||
/>
|
||||
<el-button type="primary" icon="el-icon-plus" size="mini" @click="handleAdd">上传文件</el-button>
|
||||
<el-button type="danger" icon="el-icon-delete" size="mini" :disabled="multiple" @click="handleDelete">删除</el-button>
|
||||
<span v-if="selectedCardIds.length > 0" class="selected-count">已选 {{ selectedCardIds.length }} 项</span>
|
||||
</div>
|
||||
|
||||
<div v-show="showQuery" class="advanced-search">
|
||||
<div class="advanced-search">
|
||||
<el-form :model="queryParams" inline size="mini" class="advanced-search-form">
|
||||
<el-form-item label="订单编号">
|
||||
<el-input v-model="queryParams.orderNo" placeholder="订单编号" clearable @change="handleQuery" style="width:140px;"></el-input>
|
||||
<el-input v-model="queryParams.orderNo" placeholder="订单编号" clearable @change="handleQuery" style="width:140px;" />
|
||||
</el-form-item>
|
||||
<el-form-item label="所属部门">
|
||||
<el-input v-model="queryParams.dept" placeholder="所属部门" clearable @change="handleQuery" style="width:140px;"></el-input>
|
||||
<el-input v-model="queryParams.dept" placeholder="所属部门" clearable @change="handleQuery" style="width:140px;" />
|
||||
</el-form-item>
|
||||
<el-form-item label="文件类型">
|
||||
<el-select v-model="queryParams.fileType" placeholder="文件类型" clearable @change="handleQuery" style="width:120px;">
|
||||
<el-option v-for="item in dict.type.sys_file_type" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="可见范围">
|
||||
<el-select v-model="queryParams.scopeType" placeholder="可见范围" clearable @change="handleQuery" style="width:120px;">
|
||||
<el-option label="公开" :value="1" />
|
||||
<el-option label="私有" :value="2" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="上传时间">
|
||||
<el-date-picker
|
||||
v-model="dateRange"
|
||||
@@ -105,71 +85,56 @@
|
||||
clearable
|
||||
style="width:240px;"
|
||||
@change="handleQuery"
|
||||
></el-date-picker>
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<!-- 文件表格 -->
|
||||
<div class="file-table-wrapper">
|
||||
<KLPTable v-loading="loading" :data="fileList" @selection-change="handleSelectionChange" highlight-current-row @row-click="handleRowClick">
|
||||
<el-table-column type="selection" width="50" align="center" />
|
||||
<el-table-column label="文件名称" align="center" prop="fileName" :show-overflow-tooltip="true" min-width="160">
|
||||
<template slot-scope="scope">
|
||||
<el-link type="primary" @click="handleShowInfo(scope.row)">{{ scope.row.fileName }}</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="上传人" align="center" prop="createByName" width="120" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="所属部门" align="center" prop="dept" width="120" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="订单编号" align="center" prop="orderNo" width="130" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="文件类型" align="center" width="100">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :options="dict.type.sys_file_type" :value="scope.row.fileType"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="文件大小" align="center" width="90">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ formatFileSize(scope.row.fileSize) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="可见范围" align="center" width="80">
|
||||
<template slot-scope="scope">
|
||||
<el-tag :type="scope.row.scopeType === 1 ? 'success' : 'warning'" size="small">
|
||||
{{ scope.row.scopeType === 1 ? '公开' : '私有' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="上传时间" align="center" width="155">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="浏览次数" align="center" width="75">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ scope.row.viewCount != null ? scope.row.viewCount : 0 }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" width="140" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<div class="action-cell">
|
||||
<el-button type="text" size="mini" icon="el-icon-view" @click.stop="handleShowInfo(scope.row)">详情</el-button>
|
||||
<el-button v-if="canEdit(scope.row)" type="text" size="mini" icon="el-icon-edit" @click.stop="handleUpdate(scope.row)">编辑</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</KLPTable>
|
||||
|
||||
<pagination
|
||||
v-show="total>0"
|
||||
:total="total"
|
||||
:page.sync="queryParams.pageNum"
|
||||
:limit.sync="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 文件卡片网格 -->
|
||||
<div class="card-container" v-loading="loading">
|
||||
<div v-if="fileList.length > 0" class="card-grid">
|
||||
<div
|
||||
v-for="file in fileList"
|
||||
:key="file.fileId"
|
||||
class="file-card"
|
||||
:class="{ selected: isSelected(file.fileId) }"
|
||||
@click="handleCardClick(file)"
|
||||
>
|
||||
<div class="card-checkbox" @click.stop="toggleSelect(file)">
|
||||
<i v-if="isSelected(file.fileId)" class="el-icon-check"></i>
|
||||
</div>
|
||||
<div class="card-icon" :class="getFileIconClass(file)">
|
||||
<span class="card-ext">{{ getFileExt(file) }}</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="card-name" :title="file.fileName">{{ file.fileName }}</div>
|
||||
<div class="card-uploader">{{ file.createByName || file.createBy }}</div>
|
||||
<div class="card-meta">
|
||||
<span>{{ formatFileSize(file.fileSize) }}</span>
|
||||
<span class="meta-sep">·</span>
|
||||
<span>{{ parseTime(file.createTime, '{mm}-{dd}') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-actions">
|
||||
<el-button type="text" size="mini" icon="el-icon-view" @click.stop="handleShowInfo(file)">详情</el-button>
|
||||
<el-button type="text" size="mini" icon="el-icon-download" @click.stop="downloadFile(file)">下载</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<el-empty v-else-if="!loading" description="暂无文件" />
|
||||
</div>
|
||||
|
||||
<!-- 分页 -->
|
||||
<pagination
|
||||
v-show="total>0"
|
||||
:total="total"
|
||||
:page.sync="queryParams.pageNum"
|
||||
:limit.sync="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
|
||||
<!-- 上传/编辑对话框 -->
|
||||
<el-dialog :title="dialogTitle" :visible.sync="open" width="650px" append-to-body @close="handleDialogClose">
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="100px">
|
||||
@@ -225,66 +190,68 @@
|
||||
</el-dialog>
|
||||
|
||||
<!-- 文件详情弹窗(含预览) -->
|
||||
<el-dialog :title="infoTitle" :visible.sync="infoVisible" width="1100px" append-to-body top="5vh" @close="handleInfoClose">
|
||||
<el-dialog :title="infoTitle" :visible.sync="infoVisible" width="1200px" append-to-body top="3vh" @close="handleInfoClose">
|
||||
<div v-if="infoFile" class="info-dialog-body">
|
||||
<div class="info-preview">
|
||||
<ImagePreview v-if="infoFileCategory === 'image'" :src="infoFile.filePath" />
|
||||
<PdfPreview v-else-if="infoFileCategory === 'pdf'" :src="infoFile.filePath" />
|
||||
<DocxPreview v-else-if="infoFileCategory === 'docx'" :src="infoFile.filePath" />
|
||||
<XlsxPreview v-else-if="infoFileCategory === 'xlsx'" :src="infoFile.filePath" />
|
||||
<XlsPreview v-else-if="infoFileCategory === 'xls'" :src="infoFile.filePath" />
|
||||
<div v-else class="info-preview-empty">
|
||||
<el-empty description="暂不支持预览此文件类型" />
|
||||
<div class="info-main-row">
|
||||
<div class="info-preview">
|
||||
<ImagePreview v-if="infoFileCategory === 'image'" :src="infoFile.filePath" />
|
||||
<PdfPreview v-else-if="infoFileCategory === 'pdf'" :src="infoFile.filePath" />
|
||||
<DocxPreview v-else-if="infoFileCategory === 'docx'" :src="infoFile.filePath" />
|
||||
<XlsxPreview v-else-if="infoFileCategory === 'xlsx'" :src="infoFile.filePath" />
|
||||
<XlsPreview v-else-if="infoFileCategory === 'xls'" :src="infoFile.filePath" />
|
||||
<div v-else class="info-preview-empty">
|
||||
<el-empty description="暂不支持预览此文件类型" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="info-meta">
|
||||
<el-descriptions :column="1" border size="small">
|
||||
<el-descriptions-item label="文件名称">{{ infoFile.fileName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="文件类型">
|
||||
<dict-tag :options="dict.type.sys_file_type" :value="infoFile.fileType"/>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="文件大小">{{ formatFileSize(infoFile.fileSize) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="文件后缀">{{ infoFile.suffix || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="浏览次数">{{ infoFile.viewCount != null ? infoFile.viewCount : 0 }} 次</el-descriptions-item>
|
||||
<el-descriptions-item label="订单编号">{{ infoFile.orderNo || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="所属部门">{{ infoFile.dept || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="可见范围">
|
||||
<el-tag :type="infoFile.scopeType === 1 ? 'success' : 'warning'" size="small">
|
||||
{{ infoFile.scopeType === 1 ? '公开' : '私有' }}
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="上传人">{{ infoFile.createByName || infoFile.createBy }}</el-descriptions-item>
|
||||
<el-descriptions-item label="上传时间">{{ parseTime(infoFile.createTime) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="备注">{{ infoFile.remark || '-' }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<div style="margin-top:12px;text-align:right;">
|
||||
<el-button type="warning" plain size="mini" icon="el-icon-download" @click="handleExportInfo">导出</el-button>
|
||||
<el-button type="primary" size="mini" icon="el-icon-download" @click="downloadFile(infoFile)">下载</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="info-meta">
|
||||
<el-descriptions :column="1" border size="small">
|
||||
<el-descriptions-item label="文件名称">{{ infoFile.fileName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="文件类型">
|
||||
<dict-tag :options="dict.type.sys_file_type" :value="infoFile.fileType"/>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="文件大小">{{ formatFileSize(infoFile.fileSize) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="文件后缀">{{ infoFile.suffix || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="浏览次数">{{ infoFile.viewCount != null ? infoFile.viewCount : 0 }} 次</el-descriptions-item>
|
||||
<el-descriptions-item label="订单编号">{{ infoFile.orderNo || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="所属部门">{{ infoFile.dept || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="可见范围">
|
||||
<el-tag :type="infoFile.scopeType === 1 ? 'success' : 'warning'" size="small">
|
||||
{{ infoFile.scopeType === 1 ? '公开' : '私有' }}
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="上传人">{{ infoFile.createByName || infoFile.createBy }}</el-descriptions-item>
|
||||
<el-descriptions-item label="上传时间">{{ parseTime(infoFile.createTime) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="备注">{{ infoFile.remark || '-' }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<!-- 评论区域 -->
|
||||
<div class="info-comment">
|
||||
<div class="comment-bar" @click="commentExpanded = !commentExpanded">
|
||||
<span><i class="el-icon-chat-dot-round"></i> 评论 ({{ comments.length }})</span>
|
||||
<span class="comment-toggle">{{ commentExpanded ? '收起' : '展开' }} <i :class="commentExpanded ? 'el-icon-arrow-up' : 'el-icon-arrow-down'"></i></span>
|
||||
</div>
|
||||
<div v-show="commentExpanded" class="comment-body">
|
||||
<div v-if="commentLoading" class="comment-hint"><i class="el-icon-loading"></i> 加载中...</div>
|
||||
<div v-else-if="comments.length === 0" class="comment-hint comment-empty">暂无评论</div>
|
||||
<div v-else class="comment-list">
|
||||
<div v-for="item in comments" :key="item.commentId" class="comment-item">
|
||||
<div class="comment-meta">
|
||||
<span class="comment-dept">{{ item.dept }}</span>
|
||||
<span class="comment-user">{{ item.createBy }}</span>
|
||||
<span class="comment-time">{{ parseTime(item.createTime) }}</span>
|
||||
</div>
|
||||
<div class="comment-text">{{ item.content }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="comment-input-area">
|
||||
<el-input v-model="commentInput" type="textarea" :rows="2" placeholder="输入评论..." resize="none" />
|
||||
<el-button type="primary" size="mini" @click="handleAddComment" :disabled="!commentInput.trim()" style="margin-top:6px;">发送</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 评论区域单独放在下面 -->
|
||||
<div class="info-comment">
|
||||
<div class="comment-bar" @click="commentExpanded = !commentExpanded">
|
||||
<span><i class="el-icon-chat-dot-round"></i> 评论 ({{ comments.length }})</span>
|
||||
<span class="comment-toggle">{{ commentExpanded ? '收起' : '展开' }} <i :class="commentExpanded ? 'el-icon-arrow-up' : 'el-icon-arrow-down'"></i></span>
|
||||
</div>
|
||||
<div style="margin-top:12px;text-align:right;">
|
||||
<el-button type="warning" plain size="mini" icon="el-icon-download" @click="handleExportInfo">导出</el-button>
|
||||
<el-button type="primary" size="mini" icon="el-icon-download" @click="downloadFile(infoFile)">下载</el-button>
|
||||
<div v-show="commentExpanded" class="comment-body">
|
||||
<div v-if="commentLoading" class="comment-hint"><i class="el-icon-loading"></i> 加载中...</div>
|
||||
<div v-else-if="comments.length === 0" class="comment-hint comment-empty">暂无评论</div>
|
||||
<div v-else class="comment-list">
|
||||
<div v-for="item in comments" :key="item.commentId" class="comment-item">
|
||||
<div class="comment-meta">
|
||||
<span class="comment-dept">{{ item.dept }}</span>
|
||||
<span class="comment-user">{{ item.createBy }}</span>
|
||||
<span class="comment-time">{{ parseTime(item.createTime) }}</span>
|
||||
</div>
|
||||
<div class="comment-text">{{ item.content }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="comment-input-area">
|
||||
<el-input v-model="commentInput" type="textarea" :rows="2" placeholder="输入评论..." resize="none" />
|
||||
<el-button type="primary" size="mini" @click="handleAddComment" :disabled="!commentInput.trim()" style="margin-top:6px;">发送</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -320,12 +287,11 @@ export default {
|
||||
loading: false,
|
||||
// 选中数组
|
||||
ids: [],
|
||||
selectedCardIds: [],
|
||||
// 非单个禁用
|
||||
single: true,
|
||||
// 非多个禁用
|
||||
multiple: true,
|
||||
// 显示高级搜索
|
||||
showQuery: false,
|
||||
// 总条数
|
||||
total: 0,
|
||||
// 文件列表
|
||||
@@ -433,6 +399,9 @@ export default {
|
||||
/** 查询文件列表 */
|
||||
getList() {
|
||||
this.loading = true
|
||||
this.selectedCardIds = []
|
||||
this.ids = []
|
||||
this.multiple = true
|
||||
const params = this.addDateRange(this.queryParams, this.dateRange)
|
||||
listFile(params).then(response => {
|
||||
this.fileList = response.rows
|
||||
@@ -450,17 +419,57 @@ export default {
|
||||
listFile({ pageNum: 1, pageSize: 1, createBy: this.$store.getters.name }).then(r => { this.stats.myFiles = r.total })
|
||||
listFile({ pageNum: 1, pageSize: 1 }).then(r => { this.stats.totalFiles = r.total })
|
||||
},
|
||||
/** 获取文件扩展名(大写,用于图标显示) */
|
||||
getFileExt(file) {
|
||||
const raw = file.suffix || file.fileName || ''
|
||||
const ext = raw.includes('.') ? raw.split('.').pop() : raw
|
||||
return ext.slice(0, 4).toUpperCase()
|
||||
},
|
||||
/** 文件图标颜色类 */
|
||||
getFileIconClass(file) {
|
||||
const ext = (file.suffix || file.fileName || '').toLowerCase()
|
||||
if (['doc', 'docx'].includes(ext)) return 'icon-word'
|
||||
if (['xls', 'xlsx'].includes(ext)) return 'icon-excel'
|
||||
if (['ppt', 'pptx'].includes(ext)) return 'icon-ppt'
|
||||
if (ext === 'pdf') return 'icon-pdf'
|
||||
if (['png', 'jpg', 'jpeg', 'gif', 'bmp', 'webp', 'svg', 'ico'].includes(ext)) return 'icon-image'
|
||||
if (['zip', 'rar', '7z', 'tar', 'gz'].includes(ext)) return 'icon-archive'
|
||||
if (['mp4', 'avi', 'mov', 'mkv', 'wmv', 'flv'].includes(ext)) return 'icon-video'
|
||||
if (['mp3', 'wav', 'flac', 'aac', 'ogg'].includes(ext)) return 'icon-audio'
|
||||
if (['txt', 'log', 'md'].includes(ext)) return 'icon-text'
|
||||
return 'icon-other'
|
||||
},
|
||||
/** 格式化文件大小 */
|
||||
formatFileSize(size) {
|
||||
if (!size) return '0 B'
|
||||
if (size < 1024) return size + ' B'
|
||||
if (size < 1024 * 1024) return (size / 1024).toFixed(2) + ' KB'
|
||||
return (size / 1024 / 1024).toFixed(2) + ' MB'
|
||||
if (size < 1024 * 1024) return (size / 1024).toFixed(1) + ' KB'
|
||||
return (size / 1024 / 1024).toFixed(1) + ' MB'
|
||||
},
|
||||
/** 是否可编辑 */
|
||||
canEdit(row) {
|
||||
return row.createBy === this.$store.getters.name
|
||||
},
|
||||
/** 卡片选中判断 */
|
||||
isSelected(fileId) {
|
||||
return this.selectedCardIds.includes(fileId)
|
||||
},
|
||||
/** 切换卡片选中 */
|
||||
toggleSelect(file) {
|
||||
const idx = this.selectedCardIds.indexOf(file.fileId)
|
||||
if (idx > -1) {
|
||||
this.selectedCardIds.splice(idx, 1)
|
||||
} else {
|
||||
this.selectedCardIds.push(file.fileId)
|
||||
}
|
||||
this.ids = [...this.selectedCardIds]
|
||||
this.single = this.selectedCardIds.length !== 1
|
||||
this.multiple = this.selectedCardIds.length === 0
|
||||
},
|
||||
/** 点击卡片(打开详情) */
|
||||
handleCardClick(file) {
|
||||
this.handleShowInfo(file)
|
||||
},
|
||||
/** 取消按钮 */
|
||||
cancel() {
|
||||
this.open = false
|
||||
@@ -501,10 +510,6 @@ export default {
|
||||
this.queryParams.pageNum = 1
|
||||
this.getList()
|
||||
},
|
||||
/** 切换高级搜索 */
|
||||
toggleQuery() {
|
||||
this.showQuery = !this.showQuery
|
||||
},
|
||||
/** 文件类型筛选切换 */
|
||||
handleTypeTabClick(value) {
|
||||
this.queryParams.fileType = value || undefined
|
||||
@@ -525,7 +530,7 @@ export default {
|
||||
}
|
||||
this.getList()
|
||||
},
|
||||
/** 多选框 */
|
||||
/** 多选框(兼容保留) */
|
||||
handleSelectionChange(selection) {
|
||||
this.ids = selection.map(item => item.fileId)
|
||||
this.single = selection.length !== 1
|
||||
@@ -719,126 +724,128 @@ export default {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 左右分栏 */
|
||||
.file-layout {
|
||||
/* ========== 页面顶部:统计 + 工具栏 + 筛选标签 ========== */
|
||||
.page-header {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
gap: 0;
|
||||
height: calc(100vh - 100px);
|
||||
gap: 12px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
/* 左侧边栏 */
|
||||
.file-sidebar {
|
||||
/* 统计卡片区域 */
|
||||
.stats-area {
|
||||
width: 220px;
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding-right: 12px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.sidebar-section {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.sidebar-label {
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
color: #606266;
|
||||
margin-bottom: 6px;
|
||||
padding-left: 2px;
|
||||
}
|
||||
|
||||
/* 文件类型列表(纵向) */
|
||||
.sidebar-file-types {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.file-type-list {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
border: 1px solid #ebeef5;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.type-list-item {
|
||||
padding: 8px 12px;
|
||||
font-size: 13px;
|
||||
color: #606266;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
border-bottom: 1px solid #f2f2f2;
|
||||
transition: all 0.15s;
|
||||
}
|
||||
|
||||
.type-list-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.type-list-item:hover {
|
||||
color: #5F7BA0;
|
||||
background: #f5f7fa;
|
||||
}
|
||||
|
||||
.type-list-item.active {
|
||||
color: #fff;
|
||||
background: #5F7BA0;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* 操作列按钮对齐 */
|
||||
.action-cell {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 2px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* 统计卡片 */
|
||||
.summary-card {
|
||||
.stats-card {
|
||||
height: 100%;
|
||||
border: none;
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
.summary-item {
|
||||
.stats-card /deep/ .el-card__body {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.stats-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 4px;
|
||||
width: 100%;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.stats-item {
|
||||
text-align: center;
|
||||
padding: 6px 4px;
|
||||
padding: 2px 4px;
|
||||
border: 1px solid #ebeef5;
|
||||
border-radius: 2px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.item-label {
|
||||
.stats-label {
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
font-size: 11px;
|
||||
color: #909399;
|
||||
margin-bottom: 2px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.item-value {
|
||||
.stats-value {
|
||||
display: block;
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: #1f2d3d;
|
||||
line-height: 1.2;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
/* 右侧主区域 */
|
||||
.file-main {
|
||||
.header-main {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.main-toolbar {
|
||||
/* 工具栏行 */
|
||||
.toolbar-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 8px;
|
||||
padding: 2px 0;
|
||||
}
|
||||
|
||||
.selected-count {
|
||||
font-size: 12px;
|
||||
color: #409eff;
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
/* 筛选标签行 */
|
||||
.filter-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.type-tags {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.type-tag {
|
||||
padding: 3px 14px;
|
||||
font-size: 12px;
|
||||
border-radius: 14px;
|
||||
cursor: pointer;
|
||||
color: #606266;
|
||||
background: #f5f7fa;
|
||||
border: 1px solid #e4e7ed;
|
||||
transition: all 0.2s;
|
||||
user-select: none;
|
||||
line-height: 22px;
|
||||
}
|
||||
|
||||
.type-tag:hover {
|
||||
color: #5F7BA0;
|
||||
border-color: #5F7BA0;
|
||||
background: #f0f4f8;
|
||||
}
|
||||
|
||||
.type-tag.active {
|
||||
color: #fff;
|
||||
background: #5F7BA0;
|
||||
border-color: #5F7BA0;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.filter-extra {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* 高级搜索 */
|
||||
@@ -847,7 +854,7 @@ export default {
|
||||
border: 1px solid #ebeef5;
|
||||
border-radius: 4px;
|
||||
padding: 10px 16px;
|
||||
margin-bottom: 8px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.advanced-search-form {
|
||||
@@ -860,27 +867,191 @@ export default {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
/* 表格区域 */
|
||||
.file-table-wrapper {
|
||||
flex: 1;
|
||||
/* ========== 文件卡片网格 ========== */
|
||||
.card-container {
|
||||
min-height: 300px;
|
||||
}
|
||||
|
||||
.card-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(185px, 1fr));
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
.file-card {
|
||||
position: relative;
|
||||
background: #fff;
|
||||
border: 1px solid #ebeef5;
|
||||
border-radius: 8px;
|
||||
padding: 20px 14px 14px;
|
||||
cursor: pointer;
|
||||
transition: all 0.25s;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border: 1px solid #ebeef5;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.file-table-wrapper .KLPTable {
|
||||
flex: 1;
|
||||
.file-card:hover {
|
||||
border-color: #c0c4cc;
|
||||
box-shadow: 0 4px 16px 0 rgba(0, 0, 0, 0.08);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
/* 上传区域 */
|
||||
.upload-demo {
|
||||
.file-card.selected {
|
||||
border-color: #409eff;
|
||||
box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.15), 0 4px 16px 0 rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
/* 卡片选中复选框 */
|
||||
.card-checkbox {
|
||||
position: absolute;
|
||||
top: 6px;
|
||||
right: 6px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border: 1px solid #d0d5dd;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: #fff;
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.file-card:hover .card-checkbox {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.file-card.selected .card-checkbox {
|
||||
opacity: 1;
|
||||
background: #409eff;
|
||||
border-color: #409eff;
|
||||
}
|
||||
|
||||
.card-checkbox .el-icon-check {
|
||||
font-size: 12px;
|
||||
color: #fff;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
/* 文件类型图标 */
|
||||
.card-icon {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
border-radius: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 10px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.card-ext {
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
color: #fff;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
/* 各文件类型颜色梯度 */
|
||||
.icon-word {
|
||||
background: linear-gradient(135deg, #4a90d9, #357abd);
|
||||
}
|
||||
.icon-excel {
|
||||
background: linear-gradient(135deg, #52b86a, #3d9e55);
|
||||
}
|
||||
.icon-ppt {
|
||||
background: linear-gradient(135deg, #f09b4a, #d9822e);
|
||||
}
|
||||
.icon-pdf {
|
||||
background: linear-gradient(135deg, #e06060, #cc4444);
|
||||
}
|
||||
.icon-image {
|
||||
background: linear-gradient(135deg, #a877d9, #8e5fc4);
|
||||
}
|
||||
.icon-archive {
|
||||
background: linear-gradient(135deg, #8a9bb0, #6d7e93);
|
||||
}
|
||||
.icon-video {
|
||||
background: linear-gradient(135deg, #4db8a8, #389e8e);
|
||||
}
|
||||
.icon-audio {
|
||||
background: linear-gradient(135deg, #e8924a, #d47d35);
|
||||
}
|
||||
.icon-text {
|
||||
background: linear-gradient(135deg, #90a4ae, #78909c);
|
||||
}
|
||||
.icon-other {
|
||||
background: linear-gradient(135deg, #b0bec5, #90a4ae);
|
||||
}
|
||||
|
||||
/* 卡片内容 */
|
||||
.card-body {
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
/* 详情弹窗主体 */
|
||||
.card-name {
|
||||
font-size: 13px;
|
||||
color: #303133;
|
||||
font-weight: 500;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.card-uploader {
|
||||
font-size: 11px;
|
||||
color: #aaa;
|
||||
margin-top: 2px;
|
||||
line-height: 1.3;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.card-meta {
|
||||
font-size: 11px;
|
||||
color: #909399;
|
||||
margin-top: 4px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.meta-sep {
|
||||
margin: 0 4px;
|
||||
}
|
||||
|
||||
/* 卡片悬停操作按钮 */
|
||||
.card-actions {
|
||||
margin-top: 10px;
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s;
|
||||
}
|
||||
|
||||
.file-card:hover .card-actions {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.card-actions .el-button {
|
||||
padding: 2px 8px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
/* ========== 详情弹窗 ========== */
|
||||
.info-dialog-body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.info-main-row {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
min-height: 400px;
|
||||
@@ -908,14 +1079,13 @@ export default {
|
||||
}
|
||||
|
||||
.info-meta {
|
||||
width: 320px;
|
||||
width: 340px;
|
||||
flex-shrink: 0;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
/* 详情弹窗评论区 */
|
||||
/* 评论区 */
|
||||
.info-comment {
|
||||
margin-top: 12px;
|
||||
border: 1px solid #ebeef5;
|
||||
border-radius: 4px;
|
||||
}
|
||||
@@ -999,10 +1169,3 @@ export default {
|
||||
border-top: 1px solid #f2f2f2;
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- 表格选中行高亮 -->
|
||||
<style>
|
||||
.file-main .el-table .current-row > td {
|
||||
background-color: #ecf5ff !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user