文件管理 完成

pull/1/head
李强 2021-02-28 03:15:53 +08:00
parent 29b426ec1c
commit 5de0213cf5
11 changed files with 430 additions and 12 deletions

View File

@ -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

View File

@ -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}"

View File

@ -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')

View File

@ -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

View File

@ -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

View File

@ -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]

View File

@ -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'
})
}

View File

@ -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);

View File

@ -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;

View File

@ -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: {

View File

@ -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>