后台管理新增图片和附件管理页面及功能

This commit is contained in:
zmister 2021-07-01 14:56:51 +08:00
parent 712387ebbb
commit 0be750850c
11 changed files with 1472 additions and 1 deletions

View File

@ -24,5 +24,13 @@ urlpatterns = [
path('admin_center/',views.admin_center,name="admin_center"), # 后台管理
path('admin/center_menu/',views.admin_center_menu,name="admin_center_menu"), # 后台管理菜单数据
path('admin_overview/',views.admin_overview,name="admin_overview"), # 后台管理仪表盘
# 图片管理及接口
path('manage/image/', views.admin_image, name="image_manage"), # 图片管理页面
path('api/imgs/', views.AdminImageList.as_view(), name="api_admin_imgs"), # 图片列表接口
path('api/img/<int:id>/', views.AdminImageDetail.as_view(), name="api_admin_img"), # 图片详情接口
# 附件管理及接口
path('manage/attachment/', views.admin_attachment, name="attachment_manage"), # 附件管理页面
path('api/attachments/', views.AdminAttachmentList.as_view(), name="api_admin_attachments"), # 附件列表接口
path('api/attachment/<int:id>/', views.AdminAttachmentDetail.as_view(), name="api_admin_attachment"), # 附件详情接口
]

View File

@ -26,6 +26,7 @@ from loguru import logger
import re
import datetime
import requests
import os
# 返回验证码图片
@ -719,6 +720,146 @@ def admin_doctemp(request):
return render(request,'app_admin/admin_doctemp.html',locals())
# 后台管理 - 图片管理页面
@superuser_only
def admin_image(request):
return render(request,'app_admin/admin_image.html',locals())
# 图片列表接口
class AdminImageList(APIView):
authentication_classes = [SessionAuthentication,AppMustAuth]
permission_classes = [SuperUserPermission]
# 获取图片列表
def get(self, request):
kw = request.query_params.get('kw', '')
username = request.query_params.get('username', '')
page_num = request.query_params.get('page', 1)
limit = request.query_params.get('limit', 10)
if kw == '' and username == '':
img_data = Image.objects.all().order_by('-create_time')
elif kw != '':
img_data = Image.objects.filter(file_name__icontains=kw).order_by('-create_time')
elif username != '':
user = User.objects.get(id=username)
img_data = Image.objects.filter(user=user).order_by('-create_time')
page = PageNumberPagination() # 实例化一个分页器
page.page_size = limit
page_imgs = page.paginate_queryset(img_data, request, view=self) # 进行分页查询
serializer = ImageSerializer(page_imgs, many=True) # 对分页后的结果进行序列化处理
resp = {
'code': 0,
'data': serializer.data,
'count': img_data.count()
}
return Response(resp)
# 批量删除图片
def delete(self,request):
ids = request.data.get('id','').split(',')
try:
image = Image.objects.filter(id__in=ids) # 查询附件
for a in image: # 遍历附件
file_path = settings.BASE_DIR + a.file_path
is_exist = os.path.exists(file_path)
if is_exist:
os.remove(file_path)
image.delete() # 删除数据库记录
return JsonResponse({'code': 0, 'data': _('删除成功')})
except Exception as e:
logger.exception("删除图片异常")
return JsonResponse({'code': 4, 'data': _('删除异常')})
# 图片详情接口
class AdminImageDetail(APIView):
authentication_classes = [SessionAuthentication,AppMustAuth]
permission_classes = [SuperUserPermission]
# 删除图片
def delete(self,request,id):
try:
image = Image.objects.filter(id=id) # 查询附件
for a in image: # 遍历附件
file_path = settings.BASE_DIR + a.file_path
is_exist = os.path.exists(file_path)
if is_exist:
os.remove(file_path)
image.delete() # 删除数据库记录
return JsonResponse({'code': 0, 'data': _('删除成功')})
except Exception as e:
logger.exception("删除图片异常")
return JsonResponse({'code': 4, 'data': _('删除异常')})
@superuser_only
# 后台管理 - 附件管理页面
def admin_attachment(request):
return render(request,'app_admin/admin_attachment.html',locals())
# 附件列表接口
class AdminAttachmentList(APIView):
authentication_classes = [SessionAuthentication,AppMustAuth]
permission_classes = [SuperUserPermission]
# 获取附件列表
def get(self, request):
kw = request.query_params.get('kw', '')
username = request.query_params.get('username', '')
page_num = request.query_params.get('page', 1)
limit = request.query_params.get('limit', 10)
if kw == '' and username == '':
attachment_data = Attachment.objects.all().order_by('-create_time')
elif kw != '':
attachment_data = Attachment.objects.filter(file_name__icontains=kw).order_by('-create_time')
elif username != '':
user = User.objects.get(id=username)
attachment_data = Attachment.objects.filter(user=user).order_by('-create_time')
page = PageNumberPagination() # 实例化一个分页器
page.page_size = limit
page_attachments = page.paginate_queryset(attachment_data, request, view=self) # 进行分页查询
serializer = AttachmentSerializer(page_attachments, many=True) # 对分页后的结果进行序列化处理
resp = {
'code': 0,
'data': serializer.data,
'count': attachment_data.count()
}
return Response(resp)
# 批量删除附件
def delete(self,request):
ids = request.data.get('id','').split(',')
try:
attachment = Attachment.objects.filter(id__in=ids) # 查询附件
for a in attachment: # 遍历附件
a.file_path.delete() # 删除文件
attachment.delete() # 删除数据库记录
return JsonResponse({'code': 0, 'data': _('删除成功')})
except Exception as e:
logger.exception("删除附件异常")
return JsonResponse({'code': 4, 'data': _('删除异常')})
# 附件详情接口
class AdminAttachmentDetail(APIView):
authentication_classes = [SessionAuthentication,AppMustAuth]
permission_classes = [SuperUserPermission]
# 删除图片
def delete(self,request,id):
try:
attachment = Attachment.objects.filter(id=id) # 查询附件
for a in attachment: # 遍历附件
a.file_path.delete() # 删除文件
attachment.delete() # 删除数据库记录
return JsonResponse({'code': 0, 'data': _('删除成功')})
except Exception as e:
logger.exception("删除图片异常")
return JsonResponse({'code': 4, 'data': _('删除异常')})
# 后台管理 - 注册邀请码管理
@superuser_only
@logger.catch()
@ -1086,6 +1227,31 @@ def admin_center_menu(request):
"icon": "layui-icon layui-icon-templeate-1",
"href": reverse('doctemp_manage'),
},
{
"id": "my_fodder",
"title": _("素材管理"),
"icon": "layui-icon layui-icon-upload-drag",
"type": 0,
"href": "",
"children": [
{
"id": "my_img",
"title": _("图片管理"),
"icon": "layui-icon layui-icon-face-smile",
"type": 1,
"openType": "_iframe",
"href": reverse("image_manage")
},
{
"id": "my_attachment",
"title": _("附件管理"),
"icon": "layui-icon layui-icon-face-cry",
"type": 1,
"openType": "_iframe",
"href": reverse("attachment_manage")
},
]
},
{
"id": 5,
"title": _("注册码管理"),

View File

@ -48,10 +48,14 @@ class DocTempSerializer(ModelSerializer):
# 图片序列化器
class ImageSerializer(ModelSerializer):
username = serializers.SerializerMethodField(label="用户名")
class Meta:
model = Image
fields = ('__all__')
def get_username(self,obj):
return obj.user.username
# 图片分组序列化器
class ImageGroupSerializer(ModelSerializer):
class Meta:

View File

@ -67,4 +67,5 @@ urlpatterns = [
path('my_collect/',views.my_collect,name="my_collect"), # 我的收藏
path('manage_collect/',views.manage_collect,name="manage_collect"), # 收藏管理
path('get_version/',views.get_version,name="get_version"), # 获取当前版本
path('api/usergroups/userlist', views.UserGroupUserList.as_view(), name="api_usergroups_userlist"), # 用户分组的用户列表接口
]

View File

@ -11,6 +11,10 @@ from django.core.exceptions import PermissionDenied,ObjectDoesNotExist
from django.core.serializers import serialize
from app_doc.models import Project,Doc,DocTemp
from django.contrib.auth.models import User
from rest_framework.views import APIView # 视图
from rest_framework.response import Response # 响应
from rest_framework.pagination import PageNumberPagination # 分页
from rest_framework.authentication import SessionAuthentication # 认证
from django.db.models import Q
from django.db import transaction
from django.utils.html import strip_tags
@ -19,6 +23,7 @@ from loguru import logger
from app_doc.report_utils import *
from app_admin.models import UserOptions,SysSetting
from app_admin.decorators import check_headers,allow_report_file
from app_api.auth_app import AppAuth,AppMustAuth # 自定义认证
import datetime
import traceback
import re
@ -3212,4 +3217,29 @@ def get_version(request):
'status':False,
'data':_('异常')
}
return JsonResponse(data)
return JsonResponse(data)
# 用户分组用户列表接口
class UserGroupUserList(APIView):
authentication_classes = [SessionAuthentication, AppMustAuth]
def get(self,request):
user_data = User.objects.filter(is_active=True).values(
'id', 'username', 'first_name'
)
user_list = []
for user in user_data:
item = {
'name':user['username'],
'value':user['id']
}
user_list.append(item)
# serializer = UserSerializer(user_data, many=True) # 对结果进行序列化处理
resp = {
'code': 0,
'data': user_list,
'count': user_data.count()
}
return Response(resp)

View File

@ -0,0 +1,490 @@
## 更新日志
### 1.2.2
*2021-01-19*
#### 新增
- 新增配置 `model.type: fixed`, 切换为`fixed`布局模式 [体验传送门](https://maplemei.gitee.io/xm-select/#/senior/table)
- 新增实例方法`calcPosition`, fixed布局模式下重新计算位置
#### Bug fixes
- 修改直接设置父节点无法选中的问题
- 修改非严格模式下设置父节点, 子节点受影响
- 修复渲染失败页面监听错误的问题
- 修改数据重复时分组错乱的问题
### 1.2.1
*2020-11-27*
#### 新增
- 新增配置`enableKeyboard`, 用于控制是否使用键盘操作, 用于取消因为键盘事件带来的性能影响
- tree模式新增配置`clickExpand`, 是否点击节点即展开节点
- tree模式新增配置`clickCheck`, 是否点击节点即选中节点
#### Bug fixes
- [紧急修复]tree模式下节点超过2层, 父节点半选状态异常
### 1.2.0
*2020-11-26*
#### 新增
- 图标自定义
- 新增实例方法`getTreeValue`, 用于获取树结构下的父节点和半选节点
- 新增实例方法`changeExpandedKeys`, 用于操作树结构的节点展开状态
- 新增实例方法`enable`, `disable`, 动态操作选项的启用禁用
- 新增配置`layReqText`, 表单验证, 同`layui`的`lay-reqText`
- 新增全局方法`arr2tree`, 用于把列表数据转化为树状结构
#### Bug fixes
- 修复分页模式下的上一页下一页没有国际化
- 修复远程分页时搜索过滤数据异常
- 修改`update`方法无法更新远程
- 修复tree模式下非严格模式搜索异常
- 修复tree模式下工具条操作异常
- 修复tree模式下`initValue`赋值数据错乱
- 修复tree模式下`append`和`delete`方法不更新父节点状态
### 1.1.9
*2020-07-20*
- 更新一下目录结构说明
- 增加③群`1145047250`
### 1.1.9
*2020-05-04*
#### 新增
- tree新增配置`simple`, 代表极简模式, 子级全部被选中后只会显示父级
#### Bug fixes
- 设置远程模式`totalSize`默认为1
- 修复普通多选模式下设置`max`配置后, 工具条的全选和反选 选中数据错误
- 修复`getValue`方法获取到的部分数据中携带`__node`参数无法进行序列化
- 修复同时开启远程搜索和远程分页的时候会出发两次`remoteMethod`
- 优化`remoteMethod`的内部回调机制
### 1.1.8
*2020-02-10*
#### 新增
- 新增级联模式(第一版, 欢迎测试Bug)
#### Bug fixes
- 修改class .hidden为.xm-hidden 避免冲突
- 修改tree模式下只有一个子节点是的虚线样式错误
- 修改tree非严格模式下的工具条操作数据错误
### 1.1.7
*2020-01-02*
#### 新增
- tree模式下可以使用工具条
- tree模式新增远程搜索
- tree模式新增搜索结束回调
#### Bug fixes
- 修复搜索模式下, 有分页的情况, 二次搜索没有回到第一页的问题
- 修复数据过多时的滚动样式问题
#### 预告
- 级联正在路上
### 1.1.6
*2019-12-18*
#### 新增
- 新增显示模式, 可以始终显示下拉内容
- tree模式新增配置, 可以直接展开所有节点
#### Bug fixes
- 修复动态开启动态搜索, 搜索无反应
- 修复radio模式下setValue能设置多值的问题
### 1.1.5
*2019-12-12*
#### Bug fixes
- 修复el为dom模式时, 点击页面其他位置无法关闭下拉的问题
- 修复tree模式的虚线显示问题
- 修复tree默认选中半选状态不完全的问题
### 1.1.4
*2019-12-09*
#### 新增
- 支持表单的重置按钮
#### Bug fixes
- 调整表单验证时, 如果不满足条件, 边框变色
- 修复radio模式下, 工具条操作全选的问题
### 1.1.3
*2019-12-04*
#### Bug fixes
- 调整使用dom渲染get等方法的错误
- 调整宽度过窄时分页的样式
- 修复选项过宽时, label的展示溢出
- 修复setValue自定义数据时 操作报错
- 修复create创建新数据时 操作报错
### 1.1.2
*2019-12-02*
#### Bug fixes
- 修复tree模式下 第一级为叶子节点的排版问题
- 修复tree模式鼠标hover无背景
- 修复鼠标hover时使用隐藏图标的方式选中状态下背景色异常
- 修改IE下的bug
### 1.1.1
*2019-11-26*
#### 新增
- 键盘操作up(上)down(下)Left(上一页)Right(下一页)enter(选中、取消)
#### Bug fixes
- 修改分组模式下第一级数据中children为空数组报错
- 修改tree模式+radio模式无法选中父节点的bug
### 1.1.0
*2019-11-25*
- 经过了将近一周的测试, 树形结构也趋向于完善阶段, 当然现有的功能并不能满足所有的需求, xm-select将会继续前行
#### 新增
- 树形组件
- 懒加载
- 严格/非严格父子结构
- 搜索模式
- 新增参数`layVerify`和`layVerType`, 用于表单验证
- `el`配置可以指定dom对象
- 解决`on`监听时无法使用`setValue`, 增加return处理
- 新增远程分页配置`pageRemote`
- label也可以自定义渲染
- label新增title提示
#### 调整
- 移除分组中的optgroup模式
- 调整代码文件夹结构
- 调整preact版本
### 1.1.0.Beta
#### 2019-11-25
#### 新增
- 树形组件
- [新增]搜索模式
#### 2019-11-23
#### 新增
- 新增参数`layVerify`和`layVerType`, 用于表单验证
- `el`配置可以指定dom对象
#### Bug fixes
- 树形组件
- [修改]修复懒加载模式下回调无数据还展示三角图标
- 解决`on`监听时无法使用`setValue`, 增加return处理
- 修复IE下无`Object.values`的问题
#### 2019-11-22
#### 新增
- 新增远程分页配置`pageRemote`
#### Bug fixes
- 树形组件
- [新增]`strict`严格父子结构
- [新增]`lazy`懒加载模式
- 修改搜索模式下输入中文的bug
#### 2019-11-21
#### 新增
- label也可以自定义渲染
- label新增title提示
#### Bug fixes
- 树形组件
- [修改]树状结构使用`setValue`数据错误
- [修改]树状结构中`children`属性为空数组时无法操作节点的问题
- [修改]半选状态下如无可选子项则变更操作为取消
- 修改`initValue`失效的问题
- 修改`getValue()`方法无法序列化的问题
- 调整拓展中心下拉日期多选的样式
#### 2019-11-19
- 历时半个月, 也算是一次大的版本更新, 此版本仅为测试版本, 升级需谨慎
#### 新增
- 树形组件
#### 调整
- 移除分组中的optgroup模式
- 调整代码文件夹结构
- 调整preact版本
### 1.0.13
*2019-11-07*
#### 新增
- `get`方法新增`single`参数, 可以获取单实例
#### Bug fixes
- 修复`reset`方法报错
- 修复IE下工具条全选数据错误
- 修改文档简单兼容IE
### 1.0.12
*2019-10-24*
#### 新增
- 新增配置`pageEmptyShow`, 调整分页+搜索模式下, 如果无数据是否展示分页
- 新增`create`创建条目时的回调参数`data`, 当前下拉的数据
- 工具条新增反选`REVERSE`
#### Bug fixes
- 修复`create`创建条目时, 工具条不能操作的创建条目的问题
- 修复`create`创建条目时, 分页页码显示异常的问题
- 修复`create`创建条目时, 搜索不存在的回调逻辑
- 修复多语言下工具条一直显示中文的问题
- 调整分页模式下无数据显示页码 0 -> 1
### 1.0.11
*2019-10-23*
#### 新增
- 新增`disabled`配置, 可以禁用多选
- 新增`create`配置, 可以创建条目, 具体见 [创建条目](https://maplemei.gitee.io/xm-select/#/basic/create)
- 方法`warning`新增参数`sustain`, 可以配置是否持续显示
- 新增全局`get`方法, 可以获取多选渲染后的对象
- 新增全局`batch`方法, 可以批量给渲染后的多选执行方法
#### Bug fixes
- 修复`update`方法, 会使已选中数据丢失的问题
- 修复`Safari`浏览器下, 搜索框无法聚焦的问题
### 1.0.10
*2019-10-20*
#### 新增
- 新增`content`配置, 可自定义下拉框HTML, 具体见 [下拉自定义](https://maplemei.gitee.io/xm-select/#/plugin/customer)
- 方法`setValue`新增参数`listenOn`, 可以设置是否通过`on`监听
#### Bug fixes
- 修复初始化渲染也会被`on`监听的bug
- 修复分组模式下, 搜索后分组显示错误
- 调整分组模式下也可以使用分页, 选项控制
### 1.0.9
*2019-10-17*
#### 新增
- 新增`size`尺寸设置, `large`, `medium`, `small`, `mini`
- 新增`warning`方法, 可以闪烁边框提示
- 新增搜索完成回调参数, `list`:当前过滤后的数据
#### Bug fixes
- 修复远程搜索模式下 搜索完成回调错误
### 1.0.8
*2019-10-16*
#### 兼容提示
- 此版本的on方法结构调整, 升级请注意
#### 新增
- 新增分组单击事件 click, 可选值 `SELECT`, `CLEAR`, `AUTO`, `自定义`
- 新增`append`方法追加赋值, `delete`方法删除赋值
- 新增搜索完成回调`filterDone`
#### Bug fixes
- 修复全选和请空不走on监听的问题
- 修复`autoRow`模式下, 无选项时的css样式错误
- 修复`update`后, 下拉框显示状态被重置为隐藏
- 优化`setValue`方法, 可自行判断下拉框的显示状态
- 修复文档错误, 实例没有`render`方法
### 1.0.7
*2019-10-16*
#### 新增
- 新增`autoRow`配置, 可以开启自动换行
- 新增是否显示icon配置, 隐藏图标后将变换背景色显示
### 1.0.6
*2019-10-14*
#### 新增
- 新增`showCount`配置, 可以控制选项的数量, 提高渲染性能
- 新增分组模式, 可以在选项中使用`optgroup`或`children`来开启, 分组时不建议开启分页模式
- 远程搜索中新增`show`参数, 可以查看当前下拉框是否显示
#### Bug fixes
- 修复工具条中`全选`和`清空`还可以操作禁用选项的问题
- 修复远程搜索中`selected`不回显的问题
### 1.0.5
*2019-10-10*
#### Bug fixes
- 修复本地搜索模式下多次重复触发过滤方法, 优化搜索性能
- 修复选项过多时, 可以使用鼠标进行横向滚动
### 1.0.4
*2019-09-27*
#### 新增
- 新增多选上限设置, 可以设置多选选中上限
- 新增工具条, 可以全选, 清空, 自定义
- 新增name设置, 可以表单提交, 隐藏input实现, 值为value逗号分隔
- 新增getValue参数, 可以获取不同类型的值
#### Bug fixes
- 修复搜索模式下输入中文不显示的问题
- 修改render不能及时渲染, 需要二次渲染的问题
- 修改IE下输入循环触发input事件的问题, IE 慎入
### 1.0.3
*2019-09-25*
#### Bug fixes
- 借鉴[ElementUI](https://element.eleme.cn/#/zh-CN)的文档编写方式, 重新编辑使用文档
- 修改on监听时已选中数据不对的问题
- 修改显示模式也支持html方式
- 存在layui时, 同样也能直接使用xmSelect, 不用必须layui.xmSelect
### 1.0.2
*2019-09-23*
#### Bug fixes
- 搜索时输入中文延迟后才进行回显
- 远程搜索时, loading状态也能进行输入的问题
- 单选模式下, 前面的图标变成圆形
- 修正Windows下的一些样式错乱, 兼容IE10以上
- 启动分页, 当搜索时, 如果搜索总页码为0, 再次搜索有页码时, 当前页面为0的问题
- 当底部空间不足时, 再次判断顶部空间是否充足, 优化展开方向
### 1.0.1
*2019-09-22*
#### 新增
- 物理分页配置
- 自定义搜索模式(远程搜索)
- 下拉选高度配置
#### Bug fixes
- 调整布局为flex布局
- 展开下拉选时, 自动聚焦搜索框

201
static/xm-select/LICENSE Normal file
View File

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [2019] [maplemei@aliyun.com]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

8
static/xm-select/dist/xm-select.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,276 @@
{% extends 'app_admin/admin_base.html' %}
{% load static %}
{% load i18n %}
{% block title %}附件管理{% endblock %}
{% block custom_element %}
<link href="{% static 'viewerjs/viewer.css' %}?version={{mrdoc_version}}" rel="stylesheet">
{% endblock %}
{% block content %}
<div class="layui-card">
<div class="layui-card-body">
<div class="layui-card-header" style="margin-bottom: 10px;">
<div class="layui-row">
<span style="font-size:18px;">附件管理
</span>
</div>
</div>
<div class="layui-form">
<div class="layui-form-item">
<label class="layui-form-label">关键词</label>
<div class="layui-input-inline">
<input type="text" name="kw" placeholder="请输入附件名称" autocomplete="off" class="layui-input">
</div>
<button class="pear-btn pear-btn-primary pear-btn-sm" id="search">搜索</button>
</div>
<div class="layui-form-item">
<label class="layui-form-label">用户</label>
<div class="layui-input-inline">
<div id="select-user"></div>
</div>
<button class="pear-btn pear-btn-primary pear-btn-sm" id="searchUser">筛选</button>
</div>
</div>
<div class="layui-row" id="img-div">
<table class="layui-table" id="attachment-list" lay-filter="attachment-table"></table>
</div>
</div>
</div>
<!-- 表格工具栏 -->
<script type="text/html" id="img-toolbar">
<button class="pear-btn pear-btn-danger pear-btn-sm" lay-event="batchRemove">
<i class="layui-icon layui-icon-delete"></i> 批量删除
</button>
</script>
<!-- 附件名 -->
<script type="text/html" id="fileName">
{% verbatim %}
{{#if (d.file_path.startsWith('attachment')) { }}
<a href="/media/{{d.file_path}}" target="_blank">{{d.file_name}}</a>
{{# }else{ }}
<a href="{{decodeURI(d.file_path.replace('/media/',''))}}" target="_blank" >{{ d.file_name }} </a>
{{# } }}
{% endverbatim %}
</script>
<!-- 上传时间 -->
<script type="text/html" id="registerTime">
{% verbatim %}
{{layui.util.toDateString(d.date_joined, "yyyy-MM-dd HH:mm:ss")}}
{% endverbatim %}
</script>
<!-- 最后登录时间 -->
<script type="text/html" id="lastLoginTime">
{% verbatim %}
{{layui.util.toDateString(d.last_login, "yyyy-MM-dd HH:mm:ss")}}
{% endverbatim %}
</script>
<!-- 用户操作模板 -->
<script type="text/html" id="userOpera">
{% verbatim %}
<button class="pear-btn pear-btn-danger pear-btn-sm" lay-event="remove"><i class="layui-icon layui-icon-delete"></i></button>
{% endverbatim %}
</script>
{% endblock %}
{% block custom_script %}
<script src="{% static 'viewerjs/viewer.min.js' %}"></script>
<script>
layui.use(['table','jquery','form','layer','element','table'], function() {
let table = layui.table;
let form = layui.form;
let $ = layui.jquery;
let layer = layui.layer;
let element = layui.element;
$.ajaxSetup({
data: {csrfmiddlewaretoken: '{{ csrf_token }}' },
headers: {"X-CSRFToken":'{{ csrf_token }}'},
});
// 获取用户列表
getUserList = function(usergroup_list){
$.ajax({
url:"{% url 'api_usergroups_userlist' %}",
type:'get',
success:function(r){
if(r.code === 0){
select_user.update({
data:r.data,
autoRow: true,
})
}else{
layer.msg("用户列表获取出错")
}
},
error:function(){
layer.msg("用户列表获取异常")
}
})
};
//获取附件列表,执行表格渲染
table.render({
elem: '#attachment-list',
url: "{% url 'api_admin_attachments' %}",
method:'get',
where:{
'username':$("#username").val(),
},
page:true,
toolbar: '#img-toolbar',
cols: [[
{type: 'checkbox',width:20},
{field:'file_name',title:'附件名称',templet:"#fileName"},
{field:'file_path',title:'附件路径',},
{field:'file_size',title:'附件大小',},
{field:'username',title:'用户',},
{field:'create_time',title:'上传时间',width:160,templet:'#registerTime'},
{field:'oprate',title:'操作',width:180,templet:'#userOpera'},
]],
skin: 'line',
});
// [文集权限指定用户]渲染用户多选下拉框
var select_user = xmSelect.render({
el:'#select-user',
filterable: true,
radio: true,
paging: true,
pageSize: 20,
theme: {
color: '#333333',
},
data:[]
});
getUserList();
// 删除附件
delImg = function(id){
layer.open({
type:1,
title:'删除文档',
area:'300px;',
id:'delPro',//配置ID
content:'<div style="margin-left:10px;">警告:操作将<span style="font-weight:700;color:red;">删除此附件且不可恢复!</span></div>',
btn:['确定','取消'], //添加按钮
btnAlign:'c', //按钮居中
yes:function (index,layero) {
$.ajax({
url:'/admin/api/attachment/'+id + '/',
type:'delete',
success:function(r){
layer.closeAll()
if(r.code == 0){
layer.msg("删除成功",{icon:1},function(){
table.reload('attachment-list',{page:{curr:1}});
})
}else{
layer.msg(r.data,{icon:2})
}
},
error:function(){
layer.closeAll()
layer.mag("删除异常",{icon:2,})
}
})
},
})
};
// 批量删除
batchRemove = function(obj) {
let data = table.checkStatus(obj.config.id).data;
if (data.length === 0) {
layer.msg("{% trans '未选中任何附件' %}", {
icon: 3,
time: 1000
});
return false;
}
let pro_id = "";
for (let i = 0; i < data.length; i++) {
pro_id += data[i].id + ",";
}
pro_id = pro_id.substr(0, pro_id.length - 1);
// console.log(pro_id)
layer.open({
type:1,
title:'{% trans "删除附件" %}',
area:'300px;',
id:'delPro',//配置ID
content:'<div style="margin-left:10px;">你正在批量删除附件!<br><br>警告!此操作将<span style="font-weight:700;color:red;">删除所选附件!</span>并且<span style="font-weight:700;color:red;">不可恢复!</span><br><br>请输入“DELETE”以再次确认删除。<br><input class="layui-input" style="width:95%;" id="confirm_batch_delete_project" placeholder="DELETE"></div>',
btn:['{% trans "批量删除" %}','{% trans "取消" %}'], //添加按钮
btnAlign:'c', //按钮居中
yes:function (index,layero) {
var confirm_delete = $("#confirm_batch_delete_project").val()
if(confirm_delete === 'DELETE'){
let loading = layer.load();
$.ajax({
url: "{% url 'api_admin_attachments' %}",
dataType: 'json',
type: 'delete',
data:{'id':pro_id},
success: function(r) {
layer.closeAll();
if (r.code == 0) {
layer.msg("{% trans '删除成功' %}", {
icon: 1,
time: 1000
}, function() {
table.reload('attachment-list',{page:{curr:1}});
});
} else {
layer.msg(r.data, {
icon: 2,
time: 1000
});
}
}
})
}else{
layer.msg("输入错误!")
}
}
});
};
// 侦听文档删除按钮
table.on("tool(attachment-table)",function(obj){
if (obj.event === 'remove') {
// console.log(obj)
delImg(obj.data.id)
}
});
// 侦听表格工具栏
table.on("toolbar(attachment-table)",function(obj){
if(obj.event === 'batchRemove'){ // 批量删除附件
batchRemove(obj)
}else if(obj.event === 'search'){ // 搜索附件
table.reload('attachment-list',{
where:{'kw':$('input[name="project-search-kw"]').val()
},
})
}
});
// 监听搜索按钮
$("#search").click(function(){
table.reload('attachment-list',{
where:{
'kw':$("input[name=kw]").val(),
},
})
});
// 监听用户筛选按钮
$("#searchUser").click(function(){
table.reload('attachment-list',{
where:{
'username':select_user.getValue('valueStr'),
},
})
});
})
</script>
{% endblock %}

View File

@ -27,6 +27,7 @@
{% endblock %}
<script src="{% static 'PearAdminLayui/component/layui/layui.js' %}?version={{mrdoc_version}}"></script>
<script src="{% static 'PearAdminLayui/component/pear/pear.js' %}?version={{mrdoc_version}}"></script>
<script src="{% static 'xm-select/dist/xm-select.js' %}"></script>
<script async>
layui.use(['jquery','layer'], function() {
let $ = layui.jquery;

View File

@ -0,0 +1,286 @@
{% extends 'app_admin/admin_base.html' %}
{% load static %}
{% load i18n %}
{% block title %}图片管理{% endblock %}
{% block custom_element %}
<link href="{% static 'viewerjs/viewer.css' %}?version={{mrdoc_version}}" rel="stylesheet">
{% endblock %}
{% block content %}
<div class="layui-card">
<div class="layui-card-body">
<div class="layui-card-header" style="margin-bottom: 10px;">
<div class="layui-row">
<span style="font-size:18px;">图片管理
</span>
</div>
</div>
<div class="layui-form">
<div class="layui-form-item">
<label class="layui-form-label">关键词</label>
<div class="layui-input-inline">
<input type="text" name="kw" placeholder="请输入图片名称" autocomplete="off" class="layui-input">
</div>
<button class="pear-btn pear-btn-primary pear-btn-sm" id="search">搜索</button>
</div>
<div class="layui-form-item">
<label class="layui-form-label">用户</label>
<div class="layui-input-inline">
<div id="select-user"></div>
</div>
<button class="pear-btn pear-btn-primary pear-btn-sm" id="searchUser">筛选</button>
</div>
</div>
<div class="layui-row" id="img-div">
<table class="layui-table" id="img-list" lay-filter="img-table"></table>
</div>
</div>
</div>
<!-- 表格工具栏 -->
<script type="text/html" id="img-toolbar">
<button class="pear-btn pear-btn-danger pear-btn-sm" lay-event="batchRemove">
<i class="layui-icon layui-icon-delete"></i> 批量删除
</button>
</script>
<!-- 图片缩略图模板 -->
<script type="text/html" id="imgShow">
{% verbatim %}
<img src="{{d.file_path}}" style="width:20px;height:20px" />
{% endverbatim %}
</script>
<!-- 上传时间 -->
<script type="text/html" id="registerTime">
{% verbatim %}
{{layui.util.toDateString(d.date_joined, "yyyy-MM-dd HH:mm:ss")}}
{% endverbatim %}
</script>
<!-- 最后登录时间 -->
<script type="text/html" id="lastLoginTime">
{% verbatim %}
{{layui.util.toDateString(d.last_login, "yyyy-MM-dd HH:mm:ss")}}
{% endverbatim %}
</script>
<!-- 用户操作模板 -->
<script type="text/html" id="userOpera">
{% verbatim %}
<button class="pear-btn pear-btn-danger pear-btn-sm" lay-event="remove"><i class="layui-icon layui-icon-delete"></i></button>
{% endverbatim %}
</script>
{% endblock %}
{% block custom_script %}
<script src="{% static 'viewerjs/viewer.min.js' %}"></script>
<script>
layui.use(['table','jquery','form','layer','element','table'], function() {
let table = layui.table;
let form = layui.form;
let $ = layui.jquery;
let layer = layui.layer;
let element = layui.element;
$.ajaxSetup({
data: {csrfmiddlewaretoken: '{{ csrf_token }}' },
headers: {"X-CSRFToken":'{{ csrf_token }}'},
});
// 获取用户列表
getUserList = function(usergroup_list){
$.ajax({
url:"{% url 'api_usergroups_userlist' %}",
type:'get',
success:function(r){
if(r.code === 0){
select_user.update({
data:r.data,
autoRow: true,
})
}else{
layer.msg("用户列表获取出错")
}
},
error:function(){
layer.msg("用户列表获取异常")
}
})
};
//获取图片列表,执行表格渲染
table.render({
elem: '#img-list',
url: "{% url 'api_admin_imgs' %}",
method:'get',
where:{
'username':$("#username").val(),
},
page:true,
toolbar: '#img-toolbar',
cols: [[
{type: 'checkbox',width:20},
{field:'file_path',width:60,title:"",templet:'#imgShow'},
{field:'file_name',title:'图片名称',},
{field:'file_path',title:'图片路径',},
{field:'remark',title:'备注',},
{field:'username',title:'用户',},
{field:'create_time',title:'上传时间',width:160,templet:'#registerTime'},
{field:'oprate',title:'操作',width:180,templet:'#userOpera'},
]],
skin: 'line',
done:function(){
//查看图片
var options = {
//inline: true,
url: 'data-original',
fullscreen:false,//全屏
rotatable:false,//旋转
scalable:false,//翻转
//zoomable:false,//缩放
button:false,//关闭按钮
};
var viewer = new Viewer(document.getElementById('img-div'), options);
}
});
// [文集权限指定用户]渲染用户多选下拉框
var select_user = xmSelect.render({
el:'#select-user',
filterable: true,
radio: true,
paging: true,
pageSize: 20,
theme: {
color: '#333333',
},
data:[]
});
getUserList();
// 删除图片
delImg = function(id){
layer.open({
type:1,
title:'删除文档',
area:'300px;',
id:'delPro',//配置ID
content:'<div style="margin-left:10px;">警告:操作将<span style="font-weight:700;color:red;">删除此图片且不可恢复!</span></div>',
btn:['确定','取消'], //添加按钮
btnAlign:'c', //按钮居中
yes:function (index,layero) {
$.ajax({
url:'/admin/api/img/'+id + '/',
type:'delete',
success:function(r){
layer.closeAll()
if(r.code == 0){
layer.msg("删除成功",{icon:1},function(){
table.reload('img-list',{page:{curr:1}});
})
}else{
layer.msg(r.data,{icon:2})
}
},
error:function(){
layer.closeAll()
layer.mag("删除异常",{icon:2,})
}
})
},
})
};
// 批量删除图片
batchRemove = function(obj) {
let data = table.checkStatus(obj.config.id).data;
if (data.length === 0) {
layer.msg("{% trans '未选中任何图片' %}", {
icon: 3,
time: 1000
});
return false;
}
let pro_id = "";
for (let i = 0; i < data.length; i++) {
pro_id += data[i].id + ",";
}
pro_id = pro_id.substr(0, pro_id.length - 1);
// console.log(pro_id)
layer.open({
type:1,
title:'{% trans "删除图片" %}',
area:'300px;',
id:'delPro',//配置ID
content:'<div style="margin-left:10px;">你正在批量删除图片!<br><br>警告!此操作将<span style="font-weight:700;color:red;">删除所选图片!</span>并且<span style="font-weight:700;color:red;">不可恢复!</span><br><br>请输入“DELETE”以再次确认删除。<br><input class="layui-input" style="width:95%;" id="confirm_batch_delete_project" placeholder="DELETE"></div>',
btn:['{% trans "批量删除" %}','{% trans "取消" %}'], //添加按钮
btnAlign:'c', //按钮居中
yes:function (index,layero) {
var confirm_delete = $("#confirm_batch_delete_project").val()
if(confirm_delete === 'DELETE'){
let loading = layer.load();
$.ajax({
url: "{% url 'api_admin_imgs' %}",
dataType: 'json',
type: 'delete',
data:{'id':pro_id},
success: function(r) {
layer.closeAll();
if (r.code == 0) {
layer.msg("{% trans '删除成功' %}", {
icon: 1,
time: 1000
}, function() {
table.reload('img-list',{page:{curr:1}});
});
} else {
layer.msg(r.data, {
icon: 2,
time: 1000
});
}
}
})
}else{
layer.msg("输入错误!")
}
}
});
};
// 侦听文档删除按钮
table.on("tool(img-table)",function(obj){
if (obj.event === 'remove') {
// console.log(obj)
delImg(obj.data.id)
}
});
// 侦听表格工具栏
table.on("toolbar(img-table)",function(obj){
if(obj.event === 'batchRemove'){ // 批量删除图片
batchRemove(obj)
}else if(obj.event === 'search'){ // 搜索图片
table.reload('img-list',{
where:{'kw':$('input[name="project-search-kw"]').val()
},
})
}
});
// 监听搜索按钮
$("#search").click(function(){
table.reload('img-list',{
where:{
'kw':$("input[name=kw]").val(),
},
})
});
// 监听用户筛选按钮
$("#searchUser").click(function(){
table.reload('img-list',{
where:{
'username':select_user.getValue('valueStr'),
},
})
});
})
</script>
{% endblock %}