文档管理功能,实现文档的增、删、改、查和图片上传到服务器功能功能的实现
一、分析
1. 业务流程
1.文档展示
-
业务流程
- 根据提供信息进行文档查找(参照文章管理)
- 从后端查取数据返回到前端,进行页面填充
-
请求方式、地址、参数
-
请求方式:GET
-
请求地址:/admin/doc/
-
权限:view_doc
-
请求参数:
参数名 类型 是否必传 备注 start_time datatime 不是必传 url路径参数 end_time datatime 不是必传 form表单参数 title char 不是必传 form表单参数 author char 不是必传 form表单参数
-
2. 文档删除功能
-
业务流程
- 从前端传递一个带有文档id的delete请求
- 判断数据库中是否有该数据
- 逻辑删除该文档,并返回数据到前端
-
请求方式、地址、参数
-
请求方式:DELETE
-
请求地址:/admin/doc/edit/<int:doc_id>/
-
权限:delete_doc
-
请求参数:
参数名 类型 是否必传 备注 doc_id int 必传 路径参数
-
3. 文档修改功能
-
业务流程
- 判断文档标题、文档描述、文档封面图、文档url是否为空
-
判断数据库中是否存在id=doc_id的数据
- 进行表单校验
-
成功后将数据保存进数据库
- 将数据返回到前端
-
请求方式、地址、参数
-
请求方式:PUT
-
请求地址:/admin/doc/edit/<int:doc_id>/
-
权限:change_doc
-
请求参数:
参数名 类型 是否必传 备注 doc_id int 必传 路径参数 title char 必传 请求体里 digest char 必传 请求体里 image_url url 必传 请求体里 file_url url 必传 请求体里
-
-
前端功能实
-
判断文档标题、文档描述、文档封面图、文档url是否为空
-
发起ajax PUT请求
- 跳转到文档管理页面
-
-
后端逻辑处理
- 接收前端传来的数据并转化为字典
- 通过form表单校验上面数据
- 校验成功后将数据保存进数据库
- 返回数据到前端
4. 文档发布功能
-
业务流程
- 判断文档标题、文档描述、文档封面图、文档url是否为空
- 进行表单校验
- 成功后将数据保存进数据库
- 将数据返回到前端
-
请求方式、地址、参数
-
请求方式:POST
-
请求地址:/admin/doc/pub/
-
权限:add_docs
-
请求参数:
参数名 类型 是否必传 备注 title char 必传 请求体里 digest char 必传 请求体里 image_url url 必传 请求体里 file_url url 必传 请求体里
-
-
前端功能实
-
判断文档标题、文档描述、文档封面图、文档url是否为空
-
发起ajax POST请求
- 跳转到文档管理页面
-
-
后端逻辑处理
- 接收前端传来的数据并转化为字典
- 通过form表单校验上面数据
- 校验成功后将数据保存进数据库
- 返回数据到前端
5. 上传文档到服务器
-
业务流程
-
携带文档发送ajax POST请求到后端
-
成功后将新的文档url放进对应的元素中
-
判断文档文件是否存在
-
判断文档格式是否正确
-
判段文档大小是否满足要求
-
通过fastdfs进行文档上传到服务器
-
返回数据到前端
-
-
请求方式、地址、参数
-
请求方式:POST
-
请求地址:/admin/upload/doc/
-
请求参数:
参数名 类型 是否必传 备注 doc_file file 必传 请求体里
-
二、文档展示功能实现
1. urls配置
from django.urls import path
from admin import views
app_name = "admin"
urlpatterns = [
path("doc/", views.DocIndexView.as_view(), name="doc"),
path("doc/edit/<int:doc_id>/", views.DocEditView.as_view(), name="doc_edit"),
path("doc/pub/", views.DocPubView.as_view(), name="doc_pub"),
path("upload/doc/", views.UploadDocServerView.as_view(), name="upload_doc"),
]
2. views视图逻辑处理
import json
from datetime import datetime
from urllib.parse import urlencode
from django.views import View
from django.shortcuts import render
from django.core.paginator import Paginator, EmptyPage
from django.contrib.auth.mixins import PermissionRequiredMixin, LoginRequiredMixin
from news import models
from admin import contains
from docs.models import Docs
from settings import FDFS_URL
from utils.fast.fdfs import client
from admin.forms import ArticleEditForm, DocEditForm, CourseEditForm
from utils.page.per_page import get_page_data
from utils.res_code.res_code import Code, error_map
from utils.res_code.json_function import to_json_data
# 文档主页显示
class DocIndexView(PermissionRequiredMixin, View):
"""
doc show index
route:/admin/doc/
permissions:view_docs
"""
permission_required = ("docs.view_docs",)
raise_exception = True
def get(self, request):
# 1. 从数据库中获取到文档的数据
docs = Docs.objects.only("id", "title", "create_time", "author__username").filter(is_delete=False)
# 2. 获取前端传来的数据:判断1个获取一个:start_time、end_time、doc_title、doc_author
# 判断起始时间start_time
try:
start_time = request.GET.get("start_time", "").strip()
start_time = datetime.strptime(start_time, "Y%m%d%")
except Exception as e:
# logger.info("起始时间格式错误:{}".format(e))
start_time = ""
# 判断结束时间end_time
try:
end_time = request.GET.get("end_time", "").strip()
end_time = datetime.strptime(end_time, "Y%m%d%")
except Exception as e:
# logger.info("起始时间格式错误:{}".format(e))
end_time = ""
# 判断起始时间和结束时间输入的三种情况:1.起始时间有、结束无;2.起始时间无、结束时间有;3.起始时间大于结束时间
# 起始时间有、结束无
if start_time and not end_time:
docs = docs.filter(update_time__lte=start_time)
# 起始时间无、结束时间有
if end_time and not start_time:
docs = docs.filter(update_time__gte=end_time)
# 起始时间大于结束时间
if start_time and end_time:
docs = docs.filter(update_time__range=(start_time, end_time))
if not docs:
return to_json_data(errno=Code.PARAMERR, errmsg=error_map[Code.PARAMERR])
# 3. 对文章标题进行判断/模糊查询
title = request.GET.get("title", "").strip()
if title:
docs = docs.filter(is_delete=False, title__icontains=title)
# 4. 对文章作者进行判断模糊查询
author = request.GET.get("author", "").strip()
if author:
docs = docs.filter(is_delete=False, author__username__icontains=author)
# 5. 进行分页处理
try:
page_num = int(request.GET.get("page", 1))
except Exception as e:
logger.info("页码格式错误:{}".format(e))
page_num = 1
page_obj = Paginator(docs, contains.PER_PAGE_NUMBER)
try:
doc_info = page_obj.page(page_num)
except EmptyPage: # 页码为空
doc_info = page_obj.page(page_obj.num_pages)
pages_data = get_page_data(page_obj, doc_info)
# 将时间转化为字符串
start_time = start_time.strftime("%Y%m%d") if start_time else ""
end_time = end_time.strftime("%Y%m%d") if end_time else ""
# 6. 将数据传递给前端
data = {
'doc_info': doc_info,
'paginator': page_obj,
'start_time': start_time,
'end_time': end_time,
'title': title,
'author': author,
'other_param': urlencode({
'start_time': start_time,
'end_time': end_time,
'title': title,
'author': author,
})
}
data.update(pages_data)
return render(request, 'admin/doc/doc_index.html', context=data)
3. 前端逻辑处理
$(function () {
let $startTime = $("input[name=start_time]");
let $endTime = $("input[name=end_time]");
const config = {
autoclose: true,// 自动关闭
format: 'yyyy/mm/dd',// 日期格式
language: 'zh-CN',// 选择语言为中文
showButtonPanel: true,// 优化样式
todayHighlight: true, // 高亮今天
calendarWeeks: true,// 是否在周行的左侧显示周数
clearBtn: true,// 清除
startDate: new Date(1900, 10, 1),// 0 ~11 网站上线的时候
endDate: new Date(), // 今天
};
$startTime.datepicker(config);
$endTime.datepicker(config);
//文档删除
let $delBtn = $(".btn-del");
$delBtn.click(function () {
let _this = this;
let sDocId = $(this).data("doc-id");
fAlert.alertConfirm({
title: "确定删除该文档吗?",
type: "error",
confirmButtonText: "确认删除",
cancelButtonText: "取消",
confirmCallback: function confirmCallback() {
$.ajax({
url: "/admin/doc/edit/" + sDocId + '/',
type: "DELETE",
dataType: "json",
})
.done(function (res) {
if (res.errno === "200") {
message.showSuccess("成功删除文档!");
$(_this).parents("tr").remove()
} else {
swal.showInputError("删除失败:" + res.errmsg);
}
})
.fail(function () {
alert("服务器超时,请重试!")
})
}
})
});
// 获取cookie
function getCookie(name) {
let cookieValue = null;
if (document.cookie && document.cookie !== '') {
let cookies = document.cookie.split(';');
for (let i = 0; i < cookies.length; i++) {
let cookie = jQuery.trim(cookies[i]);
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
// 添加token到request中
$.ajaxSetup({
beforeSend: function (xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
}
}
});
});
4. html页面填充
{% extends 'admin/base/base.html' %}
{% load staticfiles %}
{% block title %}
文档管理页
{% endblock %}
{% block css %}