文件管理 完成
parent
29b426ec1c
commit
5de0213cf5
|
@ -2,3 +2,4 @@ from ..models.config_settings import ConfigSettings
|
|||
from ..models.dict_data import DictData
|
||||
from ..models.dict_details import DictDetails
|
||||
from ..models.web_set import WebSet
|
||||
from ..models.save_file import SaveFile
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
import os
|
||||
import uuid
|
||||
|
||||
from django.db.models import CharField, FileField
|
||||
from django.utils import timezone
|
||||
|
||||
from apps.op_drf.models import CoreModel
|
||||
|
||||
|
||||
def files_path(instance, filename):
|
||||
return '/'.join(['system', timezone.now().strftime("%Y-%m-%d"), str(uuid.uuid4()) + os.path.splitext(filename)[-1]])
|
||||
|
||||
|
||||
class SaveFile(CoreModel):
|
||||
name = CharField(max_length=128, verbose_name="文件名称", null=True, blank=True)
|
||||
type = CharField(max_length=32, verbose_name="文件类型", null=True, blank=True)
|
||||
size = CharField(max_length=64, verbose_name="文件大小", null=True, blank=True)
|
||||
address = CharField(max_length=16, verbose_name="存储位置", null=True, blank=True) # 本地、阿里云、腾讯云..
|
||||
oss_url = CharField(max_length=200, verbose_name="OSS地址", null=True, blank=True)
|
||||
file = FileField(verbose_name="文件URL", upload_to=files_path, )
|
||||
|
||||
class Meta:
|
||||
verbose_name = '文件管理'
|
||||
verbose_name_plural = verbose_name
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.name}"
|
|
@ -1,7 +1,7 @@
|
|||
from rest_framework import serializers
|
||||
|
||||
from apps.op_drf.serializers import CustomModelSerializer
|
||||
from apps.system.models import DictData, DictDetails, ConfigSettings
|
||||
from apps.system.models import DictData, DictDetails, ConfigSettings, SaveFile
|
||||
|
||||
|
||||
# ================================================= #
|
||||
|
@ -89,3 +89,39 @@ class ConfigSettingsCreateUpdateSerializer(CustomModelSerializer):
|
|||
model = ConfigSettings
|
||||
exclude = ('description', 'creator', 'modifier')
|
||||
read_only_fields = ('update_datetime', 'create_datetime', 'creator', 'modifier')
|
||||
|
||||
|
||||
# ================================================= #
|
||||
# ************** 参数设置 序列化器 ************** #
|
||||
# ================================================= #
|
||||
|
||||
class SaveFileSerializer(CustomModelSerializer):
|
||||
"""
|
||||
文件管理 简单序列化器
|
||||
"""
|
||||
file_url = serializers.CharField(source='file.url', read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = SaveFile
|
||||
exclude = ('description',)
|
||||
|
||||
|
||||
class SaveFileCreateUpdateSerializer(CustomModelSerializer):
|
||||
"""
|
||||
字典详情 创建/更新时的列化器
|
||||
"""
|
||||
file_url = serializers.CharField(source='file.url', read_only=True)
|
||||
|
||||
def save(self, **kwargs):
|
||||
files = self.context.get('request').FILES.get('file')
|
||||
self.validated_data['name'] = files.name
|
||||
self.validated_data['size'] = files.size
|
||||
self.validated_data['type'] = files.content_type
|
||||
self.validated_data['address'] = '本地存储'
|
||||
instance = super().save(**kwargs)
|
||||
# 进行判断是否需要OSS上传
|
||||
return instance
|
||||
|
||||
class Meta:
|
||||
model = SaveFile
|
||||
exclude = ('description', 'creator', 'modifier')
|
||||
|
|
|
@ -2,14 +2,17 @@ from django.urls import re_path
|
|||
from rest_framework.routers import DefaultRouter
|
||||
|
||||
from apps.system.views import DictDataModelViewSet, DictDetailsModelViewSet, \
|
||||
ConfigSettingsModelViewSet
|
||||
ConfigSettingsModelViewSet, SaveFileModelViewSet
|
||||
|
||||
router = DefaultRouter()
|
||||
router.register(r'dict/type', DictDataModelViewSet)
|
||||
router.register(r'dict/data', DictDetailsModelViewSet)
|
||||
router.register(r'config', ConfigSettingsModelViewSet)
|
||||
router.register(r'savefile', SaveFileModelViewSet)
|
||||
urlpatterns = [
|
||||
re_path('dict/get/type/(?P<pk>.*)/', DictDetailsModelViewSet.as_view({'get': 'dict_details_list'})),
|
||||
re_path('config/configKey/(?P<pk>.*)/', ConfigSettingsModelViewSet.as_view({'get': 'get_config_key'})),
|
||||
# 下载文件
|
||||
re_path('savefile/(?P<pk>.*)/', SaveFileModelViewSet.as_view({'get': 'download_file'})),
|
||||
]
|
||||
urlpatterns += router.urls
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
import os
|
||||
|
||||
from django.conf import settings
|
||||
from django.http import HttpResponse
|
||||
from rest_framework.request import Request
|
||||
|
||||
from apps.op_drf.viewsets import CustomModelViewSet
|
||||
from apps.system.filters import DictDetailsFilter, DictDataFilter, ConfigSettingsFilter
|
||||
from apps.system.models import DictData, DictDetails, ConfigSettings
|
||||
from apps.system.models import DictData, DictDetails, ConfigSettings, SaveFile
|
||||
from apps.system.serializers import DictDataSerializer, DictDataCreateUpdateSerializer, DictDetailsSerializer, \
|
||||
DictDetailsCreateUpdateSerializer, DictDetailsListSerializer, ConfigSettingsSerializer, \
|
||||
ConfigSettingsCreateUpdateSerializer
|
||||
ConfigSettingsCreateUpdateSerializer, SaveFileSerializer, SaveFileCreateUpdateSerializer
|
||||
from utils.response import SuccessResponse
|
||||
|
||||
|
||||
|
@ -84,3 +88,32 @@ class ConfigSettingsModelViewSet(CustomModelViewSet):
|
|||
# if hasattr(self, 'handle_logging'):
|
||||
# self.handle_logging(request, *args, **kwargs)
|
||||
return SuccessResponse(msg=queryset.configValue if queryset else '')
|
||||
|
||||
|
||||
class SaveFileModelViewSet(CustomModelViewSet):
|
||||
"""
|
||||
参数设置 模型的CRUD视图
|
||||
"""
|
||||
queryset = SaveFile.objects.all()
|
||||
serializer_class = SaveFileSerializer
|
||||
create_serializer_class = SaveFileCreateUpdateSerializer
|
||||
update_serializer_class = SaveFileCreateUpdateSerializer
|
||||
# filter_class = ConfigSettingsFilter
|
||||
search_fields = ('configName',)
|
||||
ordering = 'id' # 默认排序
|
||||
|
||||
def download_file(self, request: Request, *args, **kwargs):
|
||||
"""
|
||||
下载文件
|
||||
:param request:
|
||||
:param args:
|
||||
:param kwargs:
|
||||
:return:
|
||||
"""
|
||||
instance = self.get_object()
|
||||
file_path = os.path.join(settings.MEDIA_ROOT,str(instance.file))
|
||||
with open(file_path, "rb") as f:
|
||||
res = HttpResponse(f)
|
||||
res["Content-Type"] = instance.type # 注意格式
|
||||
res["Content-Disposition"] = 'filename="{}"'.format(instance.name)
|
||||
return res
|
||||
|
|
|
@ -8,6 +8,6 @@ def getSql(filename):
|
|||
:return:
|
||||
"""
|
||||
pwd = os.path.join(os.getcwd(), 'scripts', filename)
|
||||
with open(pwd) as fp:
|
||||
content = fp.read()
|
||||
with open(pwd,'rb') as fp:
|
||||
content = fp.read().decode('utf8')
|
||||
return [ele for ele in content.split('\n') if not ele.startswith('--') and ele]
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 查询文件列表
|
||||
export function listSaveFile(query) {
|
||||
return request({
|
||||
url: '/system/savefile/',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 新增文件
|
||||
export function addSaveFile(data) {
|
||||
return request({
|
||||
url: '/system/savefile/',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// 删除文件
|
||||
export function delSaveFile(menuId) {
|
||||
return request({
|
||||
url: '/system/savefile/' + menuId + '/',
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 清理废弃文件
|
||||
export function clearSaveFile() {
|
||||
return request({
|
||||
url: '/system/clearsavefile/',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
|
@ -61,9 +61,10 @@ export default {
|
|||
default: true
|
||||
}
|
||||
},
|
||||
name:'FileUpload',
|
||||
data() {
|
||||
return {
|
||||
uploadFileUrl: process.env.VUE_APP_BASE_API + "/common/upload", // 上传的图片服务器地址
|
||||
uploadFileUrl: process.env.VUE_APP_BASE_API + "/system/savefile/", // 上传的图片服务器地址
|
||||
headers: {
|
||||
Authorization: "Bearer " + getToken(),
|
||||
},
|
||||
|
@ -99,7 +100,7 @@ export default {
|
|||
// 上传前校检格式和大小
|
||||
handleBeforeUpload(file) {
|
||||
// 校检文件类型
|
||||
if (this.fileType) {
|
||||
if (this.fileType && this.fileType[0] !== 'ALL') {
|
||||
let fileExtension = "";
|
||||
if (file.name.lastIndexOf(".") > -1) {
|
||||
fileExtension = file.name.slice(file.name.lastIndexOf(".") + 1);
|
||||
|
|
|
@ -179,8 +179,8 @@
|
|||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" v-if="form.menuType != '2'">
|
||||
<el-form-item :label="form.isFrame !== '0'?'组件路径':'跳转路由'" prop="component_path">
|
||||
<el-input v-model="form.component_path" :placeholder="form.isFrame !== '0'?'请输入前端组件路径':'请输入前端跳转路由'"/>
|
||||
<el-form-item label="组件路径" prop="component_path">
|
||||
<el-input v-model="form.component_path" placeholder="请输入前端组件路径" @change="ComponentPathChange"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
|
@ -325,6 +325,10 @@
|
|||
let res = this.form.interface_path + ":" + this.form.interface_method
|
||||
this.form.perms = res.toLocaleLowerCase().replace(/(\/)/g,':').replace(/(::)/g,':').replace(/(^:)|(:$)/g, "")
|
||||
},
|
||||
/** 组件路径变化,替换斜杠开头 */
|
||||
ComponentPathChange(){
|
||||
this.form.component_path = this.form.component_path.replace(/(^\/)/g,'')
|
||||
},
|
||||
/** 查询菜单列表 */
|
||||
getList() {
|
||||
this.loading = true;
|
||||
|
|
|
@ -405,7 +405,7 @@ export default {
|
|||
// 设置上传的请求头部
|
||||
headers: { Authorization: "Bearer " + getToken() },
|
||||
// 上传的地址
|
||||
url: process.env.VUE_APP_BASE_API + "/permission/user/importData"
|
||||
url: process.env.VUE_APP_BASE_API + "/system/savefile/"
|
||||
},
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
|
|
|
@ -0,0 +1,277 @@
|
|||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
|
||||
<el-form-item label="文件名称" prop="name">
|
||||
<el-input
|
||||
v-model="queryParams.name"
|
||||
placeholder="请输入文件名称"
|
||||
clearable
|
||||
size="small"
|
||||
style="width: 240px"
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="文件类型" prop="type">
|
||||
<el-input
|
||||
v-model="queryParams.type"
|
||||
placeholder="请输入文件类型(待完善)"
|
||||
clearable
|
||||
size="small"
|
||||
style="width: 240px"
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
icon="el-icon-upload"
|
||||
size="mini"
|
||||
@click="handleAdd"
|
||||
v-hasPermi="['system:config:add']"
|
||||
>文件上传
|
||||
</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="danger"
|
||||
plain
|
||||
icon="el-icon-delete"
|
||||
size="mini"
|
||||
:disabled="multiple"
|
||||
@click="handleDelete"
|
||||
v-hasPermi="['system:config:remove']"
|
||||
>批量删除
|
||||
</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="warning"
|
||||
plain
|
||||
icon="el-icon-download"
|
||||
size="mini"
|
||||
@click="handleClear"
|
||||
v-hasPermi="['system:post:export']"
|
||||
>清理废弃文件
|
||||
</el-button>
|
||||
</el-col>
|
||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
|
||||
<el-table v-loading="loading" :data="fileList" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center"/>
|
||||
<el-table-column label="文件编号" width="90" align="center" prop="id"/>
|
||||
<el-table-column label="文件名称" width="200" align="center" prop="name" :show-overflow-tooltip="true"/>
|
||||
<el-table-column label="文件类型" width="150" align="center" prop="type"/>
|
||||
<el-table-column label="文件大小" width="90" align="center" prop="size"/>
|
||||
<el-table-column label="存储位置" align="center" prop="address"/>
|
||||
<el-table-column label="文件本地地址" align="center" prop="file" :show-overflow-tooltip="true"/>
|
||||
<el-table-column label="OSS地址" align="center" prop="oss_url" :show-overflow-tooltip="true"/>
|
||||
<el-table-column label="创建时间" align="center" prop="create_datetime" width="160">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.create_datetime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="130">
|
||||
<template slot-scope="scope">
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-download"
|
||||
@click="handleDownload(scope.row)"
|
||||
v-hasPermi="['system:post:edit']"
|
||||
>下载
|
||||
</el-button>
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-delete"
|
||||
@click="handleDelete(scope.row)"
|
||||
v-hasPermi="['system:post:remove']"
|
||||
>删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<pagination
|
||||
v-show="total>0"
|
||||
:total="total"
|
||||
:page.sync="queryParams.pageNum"
|
||||
:limit.sync="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
<!-- 对话框 -->
|
||||
<el-dialog :title="title" :visible.sync="open" width="400px" append-to-body @close="submitForm">
|
||||
<FileUpload ref="saveFile" :file-type="['ALL']" @input="submitForm"></FileUpload>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="primary" @click="submitForm">完 成</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {clearSaveFile, listSaveFile} from "@/api/system/savefile";
|
||||
import {delSaveFile} from "../../../api/system/savefile";
|
||||
import FileUpload from "../../../components/FileUpload/index";
|
||||
|
||||
/**
|
||||
* 保存
|
||||
* @param {Blob} blob
|
||||
* @param {String} filename 想要保存的文件名称
|
||||
*/
|
||||
function saveAs(blob, filename) {
|
||||
if (window.navigator.msSaveOrOpenBlob) {
|
||||
navigator.msSaveBlob(blob, filename);
|
||||
} else {
|
||||
var link = document.createElement('a');
|
||||
var body = document.querySelector('body');
|
||||
|
||||
link.href = window.URL.createObjectURL(blob);
|
||||
link.download = filename;
|
||||
|
||||
// fix Firefox
|
||||
link.style.display = 'none';
|
||||
body.appendChild(link);
|
||||
|
||||
link.click();
|
||||
body.removeChild(link);
|
||||
|
||||
window.URL.revokeObjectURL(link.href);
|
||||
}
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 blob
|
||||
* @param {String} url 目标文件地址
|
||||
* @return {cb}
|
||||
*/
|
||||
function getBlob(url, cb) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', url, true);
|
||||
xhr.responseType = 'blob';
|
||||
xhr.onload = function () {
|
||||
if (xhr.status === 200) {
|
||||
cb(xhr.response);
|
||||
}
|
||||
};
|
||||
xhr.send();
|
||||
}
|
||||
|
||||
export default {
|
||||
name: "Savefile",
|
||||
components: {FileUpload},
|
||||
data() {
|
||||
return {
|
||||
// 遮罩层
|
||||
loading: true,
|
||||
// 显示搜索条件
|
||||
showSearch: true,
|
||||
// 非单个禁用
|
||||
single: true,
|
||||
// 非多个禁用
|
||||
multiple: true,
|
||||
// 弹出层标题
|
||||
title: "",
|
||||
// 是否显示弹出层
|
||||
open: false,
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
name: undefined,
|
||||
visible: undefined,
|
||||
},
|
||||
// 总条数
|
||||
total: 0,
|
||||
// 数据列表
|
||||
fileList: [],
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getList();
|
||||
},
|
||||
mounted() {
|
||||
},
|
||||
methods: {
|
||||
/** 查询列表 */
|
||||
getList() {
|
||||
this.loading = true;
|
||||
listSaveFile(this.queryParams).then(response => {
|
||||
this.fileList = response.data.results;
|
||||
this.total = response.data.count;
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
/** 搜索按钮操作 */
|
||||
handleQuery() {
|
||||
this.getList();
|
||||
},
|
||||
/** 重置按钮操作 */
|
||||
resetQuery() {
|
||||
this.resetForm("queryForm");
|
||||
this.handleQuery();
|
||||
},
|
||||
/** 上传按钮操作 */
|
||||
handleAdd(row) {
|
||||
this.open = true;
|
||||
this.title = "上传文件";
|
||||
},
|
||||
/** 文件下载 **/
|
||||
handleDownload(row) {
|
||||
getBlob(process.env.VUE_APP_BASE_API + row.file_url, function (blob) {
|
||||
saveAs(blob, row.name);
|
||||
});
|
||||
},
|
||||
/** 删除按钮操作 */
|
||||
handleDelete(row) {
|
||||
this.$confirm('是否确认删除名称为"' + row.name + '"的数据项?', "警告", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning"
|
||||
}).then(function () {
|
||||
return delSaveFile(row.id);
|
||||
}).then(() => {
|
||||
this.getList();
|
||||
this.msgSuccess("删除成功");
|
||||
})
|
||||
},
|
||||
// 多选框选中数据
|
||||
handleSelectionChange(selection) {
|
||||
this.ids = selection.map(item => item.id)
|
||||
this.single = selection.length !== 1
|
||||
this.multiple = !selection.length
|
||||
},
|
||||
/** 清理废弃文件 */
|
||||
handleClear() {
|
||||
this.$confirm('此项操作会把所有数据库中不存在的文件删除,包括OSS中的文件,是否确认要清理废弃文件?', "警告", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning"
|
||||
}).then(function () {
|
||||
return clearSaveFile();
|
||||
}).then(() => {
|
||||
this.getList();
|
||||
this.msgSuccess("删除成功");
|
||||
})
|
||||
},
|
||||
/** 完成按钮 */
|
||||
submitForm: function () {
|
||||
this.getList();
|
||||
this.open = false;
|
||||
this.$refs.saveFile.fileList = []
|
||||
},
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
Loading…
Reference in New Issue